diff --git a/LunaDll/LuaMain/LuaProxyFFI.cpp b/LunaDll/LuaMain/LuaProxyFFI.cpp index add95bd3..9eebbf32 100644 --- a/LunaDll/LuaMain/LuaProxyFFI.cpp +++ b/LunaDll/LuaMain/LuaProxyFFI.cpp @@ -446,7 +446,7 @@ extern "C" { typedef struct ExtendedNPCFields_\ {\ bool noblockcollision;\ - bool nonpccollision;\ + bool nonpcinteraction;\ short fullyInsideSection;\ unsigned int collisionGroup;\ } ExtendedNPCFields;"; diff --git a/LunaDll/Misc/RuntimeHook.h b/LunaDll/Misc/RuntimeHook.h index 5b583746..997a34a5 100644 --- a/LunaDll/Misc/RuntimeHook.h +++ b/LunaDll/Misc/RuntimeHook.h @@ -615,6 +615,7 @@ void __stdcall runtimeHookPlayerPlayerInteraction(void); void __stdcall runtimeHookBlockNPCFilter(void); void __stdcall runtimeHookNPCCollisionGroup(void); +void __stdcall runtimeHookWalkPastNPCs(void); void __stdcall runtimeHookLevelPauseCheck(void); diff --git a/LunaDll/Misc/RuntimeHookComponents/RuntimeHookGeneral.cpp b/LunaDll/Misc/RuntimeHookComponents/RuntimeHookGeneral.cpp index 5ce03fb2..e404e25e 100644 --- a/LunaDll/Misc/RuntimeHookComponents/RuntimeHookGeneral.cpp +++ b/LunaDll/Misc/RuntimeHookComponents/RuntimeHookGeneral.cpp @@ -2137,6 +2137,9 @@ void TrySkipPatch() // Patch to handle collisionGroup for NPC-to-NPC interactions, and harmlessthrown flag PATCH(0xA181AD).JMP(runtimeHookNPCCollisionGroup).NOP_PAD_TO_SIZE<6>().Apply(); + // Patch to handle walkpastnpcs config for NPCs + PATCH(0xA1B801).JMP(runtimeHookWalkPastNPCs).NOP_PAD_TO_SIZE<724>().Apply(); + // Replace pause button detection code to avoid re-triggering when held PATCH(0x8CA405).JMP(runtimeHookLevelPauseCheck).NOP_PAD_TO_SIZE<6>().Apply(); diff --git a/LunaDll/Misc/RuntimeHookComponents/RuntimeHookHooks.cpp b/LunaDll/Misc/RuntimeHookComponents/RuntimeHookHooks.cpp index 780cd0f8..65d756d2 100644 --- a/LunaDll/Misc/RuntimeHookComponents/RuntimeHookHooks.cpp +++ b/LunaDll/Misc/RuntimeHookComponents/RuntimeHookHooks.cpp @@ -3940,6 +3940,15 @@ static unsigned int __stdcall runtimeHookBlockNPCFilterInternal(unsigned int hit if (!gCollisionMatrix.getIndicesCollide(ext->collisionGroup,ownerExt->collisionGroup)) // Check collision matrix return 0; + + // Check noNPCInteraction + if (ext->nonpcinteraction || ownerExt->nonpcinteraction) + return 0; // Collision cancelled + + NPCMOB* ownerNPC = NPC::GetRaw(npcIdx); + + if (NPC::GetNoNPCInteraction(npc->id) || NPC::GetNoNPCInteraction(ownerNPC->id)) + return 0; // Collision cancelled } else { @@ -3982,27 +3991,30 @@ static unsigned int __stdcall runtimeHookNPCCollisionGroupInternal(int npcAIdx, if (npcAIdx == npcBIdx) // Don't collide if it's the same NPC - this is what the code we're replacing does! return 0; // Collision cancelled + NPCMOB* npcA = NPC::GetRaw(npcAIdx); + NPCMOB* npcB = NPC::GetRaw(npcBIdx); + + if (npcA == nullptr || npcB == nullptr) + return 0; + // Check harmlessthrown flag + if (NPC::GetHarmlessThrown(npcA->id)) // If harmlessthrown is set + return 0; // Collision cancelled - NPCMOB* npc = NPC::GetRaw(npcAIdx); - if (npc != nullptr) - { - if (NPC::GetHarmlessThrown(npc->id)) // If harmlessthrown is set - return 0; // Collision cancelled - } + // Check noNPCInteraction config + if (NPC::GetNoNPCInteraction(npcA->id) || NPC::GetNoNPCInteraction(npcB->id)) + return 0; // Collision cancelled - // Check collision group - + // Check noNPCInteraction field ExtendedNPCFields* extA = NPC::GetRawExtended(npcAIdx); ExtendedNPCFields* extB = NPC::GetRawExtended(npcBIdx); - if (!gCollisionMatrix.getIndicesCollide(extA->collisionGroup,extB->collisionGroup)) // Check collision matrix + if (extA->nonpcinteraction || extB->nonpcinteraction) return 0; // Collision cancelled - // Check noNPCCollision - if (extA->nonpccollision || extB->nonpccollision) { + // Check collision group + if (!gCollisionMatrix.getIndicesCollide(extA->collisionGroup,extB->collisionGroup)) // Check collision matrix return 0; // Collision cancelled - } return -1; // Collision goes ahead } @@ -4036,6 +4048,78 @@ __declspec(naked) void __stdcall runtimeHookNPCCollisionGroup(void) } } +static void __stdcall runtimeHookWalkPastNPCsInternal(int npcAIdx, int npcBIdx) +{ + // C++ side reimplementation of the original turning code + NPCMOB* npcA = NPC::GetRaw(npcAIdx); + NPCMOB* npcB = NPC::GetRaw(npcBIdx); + + int16_t walkPastA = NPC::GetWalkPastNPCs(npcA->id); + int16_t walkPastB = NPC::GetWalkPastNPCs(npcB->id); + + // walkpastnpcs value of 2; ignore the bump entirely + if (walkPastA == 2 || walkPastB == 2) + { + return; + } + + + npcA->npcCollisionFlag = -1; + + if (npcA->directionFaced == npcB->directionFaced) + { + // In cases where the directions of both NPCs matches, only the one with the lower speed should turn around + double relativeSpeedA = npcA->momentum.speedX * npcA->directionFaced; + double relativeSpeedB = npcB->momentum.speedX * npcB->directionFaced; + + if (relativeSpeedA > relativeSpeedB) + { + if (walkPastA == 0) + npcA->bounceOffBlock = -1; + } + else if (relativeSpeedA < relativeSpeedB) + { + if (walkPastB == 0) + npcB->bounceOffBlock = -1; + } + else + { + npcA->bounceOffBlock = -1; + npcB->bounceOffBlock = -1; + } + } + else + { + // If the directions don't match, both should turn around + if (walkPastA == 0) + npcA->bounceOffBlock = -1; + + if (walkPastB == 0) + npcB->bounceOffBlock = -1; + } +} + +__declspec(naked) void __stdcall runtimeHookWalkPastNPCs(void) +{ + // 00A1B801 | 66:81BE E2000000 B300 | cmp word ptr ds:[esi+E2],B3 | + // https://github.com/smbx/smbx-legacy-source/blob/master/modNPC.bas#L2475 + + __asm { + push esi + + mov eax, dword ptr ss:[ebp-0x188] // npcBIdx + push eax + movsx eax, word ptr ss:[ebp-0x180] // npcAIdx + push eax + + call runtimeHookWalkPastNPCsInternal + + pop esi + push 0xA1BAD5 + ret + } +} + static unsigned int __stdcall runtimeHookBlockPlayerFilterInternal(short playerIdx, int blockIdx, int oldSlope) { PlayerMOB* player = Player::Get(playerIdx); diff --git a/LunaDll/SMBXInternal/NPCs.cpp b/LunaDll/SMBXInternal/NPCs.cpp index 212851d7..562e3681 100644 --- a/LunaDll/SMBXInternal/NPCs.cpp +++ b/LunaDll/SMBXInternal/NPCs.cpp @@ -273,6 +273,8 @@ static int16_t npcprop_noshieldfireeffect[NPC::MAX_ID + 1] = { 0 }; static int16_t npcprop_notcointransformable[NPC::MAX_ID + 1] = { 0 }; static int16_t npcprop_staticdirection[NPC::MAX_ID + 1] = { 0 }; static int16_t npcprop_luahandlesspeed[NPC::MAX_ID + 1] = { 0 }; +static int16_t npcprop_nonpcinteraction[NPC::MAX_ID + 1] = { 0 }; +static int16_t npcprop_walkpastnpcs[NPC::MAX_ID + 1] = { 0 }; static double npcprop_terminalvelocity[NPC::MAX_ID + 1] = { 0 }; // Other NPC-related config data, not by ID @@ -293,6 +295,8 @@ void NPC::InitProperties() { npcprop_notcointransformable[i] = 0; npcprop_staticdirection[i] = 0; npcprop_luahandlesspeed[i] = 0; + npcprop_nonpcinteraction[i] = 0; + npcprop_walkpastnpcs[i] = 0; npcprop_terminalvelocity[i] = 0; } @@ -500,6 +504,12 @@ void NPC::InitProperties() { npcprop_staticdirection[181] = -1; npcprop_staticdirection[212] = -1; + // Default walkpastnpcs values + npcprop_walkpastnpcs[13] = 1; + npcprop_walkpastnpcs[17] = 1; + npcprop_walkpastnpcs[265] = 1; + npcprop_walkpastnpcs[179] = 2; + // Default terminal velocity values npcprop_terminalvelocity[259] = -1; npcprop_terminalvelocity[260] = -1; @@ -564,6 +574,16 @@ bool NPC::GetLuaHandlesSpeed(int id) { return (npcprop_luahandlesspeed[id] != 0); } +bool NPC::GetNoNPCInteraction(int id) { + if ((id < 1) || (id > NPC::MAX_ID)) return false; + return (npcprop_nonpcinteraction[id] != 0); +} + +int16_t NPC::GetWalkPastNPCs(int id) { + if ((id < 1) || (id > NPC::MAX_ID)) return 0; + return npcprop_walkpastnpcs[id]; +} + double NPC::GetTerminalVelocity(int id) { if ((id < 1) || (id > NPC::MAX_ID) || (npcprop_terminalvelocity[id] == 0)) { @@ -622,6 +642,12 @@ uintptr_t NPC::GetPropertyTableAddress(const std::string& s) { return reinterpret_cast(npcprop_luahandlesspeed); } + else if (s == "nonpcinteraction") { + return reinterpret_cast(npcprop_nonpcinteraction); + } + else if (s == "walkpastnpcs") { + return reinterpret_cast(npcprop_walkpastnpcs); + } else if (s == "terminalvelocity") { return reinterpret_cast(npcprop_terminalvelocity); diff --git a/LunaDll/SMBXInternal/NPCs.h b/LunaDll/SMBXInternal/NPCs.h index 90f7a0fa..d35c7ae5 100644 --- a/LunaDll/SMBXInternal/NPCs.h +++ b/LunaDll/SMBXInternal/NPCs.h @@ -522,7 +522,7 @@ static_assert(sizeof(NPCMOB) == 0x158, "sizeof(NPCMOB) must be 0x158"); struct ExtendedNPCFields { bool noblockcollision; - bool nonpccollision; + bool nonpcinteraction; short fullyInsideSection; unsigned int collisionGroup; @@ -538,7 +538,7 @@ struct ExtendedNPCFields noblockcollision = false; fullyInsideSection = -1; collisionGroup = 0u; - nonpccollision = false; + nonpcinteraction = false; } }; @@ -585,6 +585,8 @@ namespace NPC { bool GetNotCoinTransformable(int id); bool GetStaticDirection(int id); bool GetLuaHandlesSpeed(int id); + bool GetNoNPCInteraction(int id); + int16_t GetWalkPastNPCs(int id); double GetTerminalVelocity(int id); uintptr_t GetPropertyTableAddress(const std::string& s);