Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion LunaDll/LuaMain/LuaProxyFFI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -446,7 +446,7 @@ extern "C" {
typedef struct ExtendedNPCFields_\
{\
bool noblockcollision;\
bool nonpccollision;\
bool nonpcinteraction;\
short fullyInsideSection;\
unsigned int collisionGroup;\
} ExtendedNPCFields;";
Expand Down
1 change: 1 addition & 0 deletions LunaDll/Misc/RuntimeHook.h
Original file line number Diff line number Diff line change
Expand Up @@ -615,6 +615,7 @@ void __stdcall runtimeHookPlayerPlayerInteraction(void);

void __stdcall runtimeHookBlockNPCFilter(void);
void __stdcall runtimeHookNPCCollisionGroup(void);
void __stdcall runtimeHookWalkPastNPCs(void);

void __stdcall runtimeHookLevelPauseCheck(void);

Expand Down
3 changes: 3 additions & 0 deletions LunaDll/Misc/RuntimeHookComponents/RuntimeHookGeneral.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand Down
108 changes: 96 additions & 12 deletions LunaDll/Misc/RuntimeHookComponents/RuntimeHookHooks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand Down Expand Up @@ -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
}
Expand Down Expand Up @@ -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);
Expand Down
26 changes: 26 additions & 0 deletions LunaDll/SMBXInternal/NPCs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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;
}

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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))
{
Expand Down Expand Up @@ -622,6 +642,12 @@ uintptr_t NPC::GetPropertyTableAddress(const std::string& s)
{
return reinterpret_cast<uintptr_t>(npcprop_luahandlesspeed);
}
else if (s == "nonpcinteraction") {
return reinterpret_cast<uintptr_t>(npcprop_nonpcinteraction);
}
else if (s == "walkpastnpcs") {
return reinterpret_cast<uintptr_t>(npcprop_walkpastnpcs);
}
else if (s == "terminalvelocity")
{
return reinterpret_cast<uintptr_t>(npcprop_terminalvelocity);
Expand Down
6 changes: 4 additions & 2 deletions LunaDll/SMBXInternal/NPCs.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -538,7 +538,7 @@ struct ExtendedNPCFields
noblockcollision = false;
fullyInsideSection = -1;
collisionGroup = 0u;
nonpccollision = false;
nonpcinteraction = false;
}
};

Expand Down Expand Up @@ -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);
Expand Down