From 05f4c42f81f00e9d6cc1bd2fe9e3e6c88aea72d0 Mon Sep 17 00:00:00 2001 From: Isaac0-dev <62234577+Isaac0-dev@users.noreply.github.com> Date: Thu, 1 Jun 2023 14:10:06 +1000 Subject: [PATCH] add cancels to HOOK_BEFORE_PHYS_STEP allowing for custom step systems (#400) * add cancels to HOOK_BEFORE_PHYS_STEP allowing for custom step systems * give lua mods access to stepArg and nextPos from hanging and air step; fix a crash when m.floor is null --- autogen/lua_definitions/constants.lua | 12 +++++ docs/lua/constants.md | 4 ++ docs/lua/guides/hooks.md | 2 +- include/sm64.h | 5 ++ src/game/mario.c | 10 ++-- src/game/mario_actions_automatic.c | 5 +- src/game/mario_actions_submerged.c | 15 +++--- src/game/mario_step.c | 6 ++- src/pc/lua/smlua_constants_autogen.c | 4 ++ src/pc/lua/smlua_hooks.c | 77 ++++++++++++++++++++++++++- src/pc/lua/smlua_hooks.h | 2 + 11 files changed, 124 insertions(+), 18 deletions(-) diff --git a/autogen/lua_definitions/constants.lua b/autogen/lua_definitions/constants.lua index 15db1032b..81f0ac59b 100644 --- a/autogen/lua_definitions/constants.lua +++ b/autogen/lua_definitions/constants.lua @@ -8126,6 +8126,18 @@ PARTICLE_WATER_SPLASH = (1 << 6) --- @type integer PARTICLE_WAVE_TRAIL = (1 << 10) +--- @type integer +STEP_TYPE_AIR = 2 + +--- @type integer +STEP_TYPE_GROUND = 1 + +--- @type integer +STEP_TYPE_HANG = 4 + +--- @type integer +STEP_TYPE_WATER = 3 + --- @type integer VALID_BUTTONS = (A_BUTTON | B_BUTTON | Z_TRIG | START_BUTTON | U_JPAD | D_JPAD | L_JPAD | R_JPAD | L_TRIG | R_TRIG | X_BUTTON | Y_BUTTON | U_CBUTTONS | D_CBUTTONS | L_CBUTTONS | R_CBUTTONS ) diff --git a/docs/lua/constants.md b/docs/lua/constants.md index 3600db432..4df4e9802 100644 --- a/docs/lua/constants.md +++ b/docs/lua/constants.md @@ -2894,6 +2894,10 @@ - PARTICLE_VERTICAL_STAR - PARTICLE_WATER_SPLASH - PARTICLE_WAVE_TRAIL +- STEP_TYPE_AIR +- STEP_TYPE_GROUND +- STEP_TYPE_HANG +- STEP_TYPE_WATER - VALID_BUTTONS - WATER_STEP_CANCELLED - WATER_STEP_HIT_CEILING diff --git a/docs/lua/guides/hooks.md b/docs/lua/guides/hooks.md index af584645a..7f8eca33a 100644 --- a/docs/lua/guides/hooks.md +++ b/docs/lua/guides/hooks.md @@ -91,7 +91,7 @@ The lua functions sent to `hook_event()` will be automatically called by SM64 wh | HOOK_MARIO_UPDATE | Called once per player per frame at the end of a mario update | [MarioState](structs.md#MarioState) mario | | HOOK_BEFORE_MARIO_UPDATE | Called once per player per frame at the beginning of a mario update | [MarioState](structs.md#MarioState) mario | | HOOK_ON_SET_MARIO_ACTION | Called every time a player's current action is changed | [MarioState](structs.md#MarioState) mario | -| HOOK_BEFORE_PHYS_STEP | Called once per player per frame before physics code is run | [MarioState](structs.md#MarioState) mario | +| HOOK_BEFORE_PHYS_STEP | Called once per player per frame before physics code is run, return an integer to cancel it with your own step result | [MarioState](structs.md#MarioState) mario, `integer` stepType | | HOOK_ALLOW_PVP_ATTACK | Called when one player attacks another, return `true` to allow the attack | [MarioState](structs.md#MarioState) attacker, [MarioState](structs.md#MarioState) victim | | HOOK_ON_PVP_ATTACK | Called when one player attacks another | [MarioState](structs.md#MarioState) attacker, [MarioState](structs.md#MarioState) victim | | HOOK_ON_PLAYER_CONNECTED | Called when a player connects | [MarioState](structs.md#MarioState) connector | diff --git a/include/sm64.h b/include/sm64.h index ea0348964..02e1490f0 100644 --- a/include/sm64.h +++ b/include/sm64.h @@ -88,6 +88,11 @@ #define WATER_STEP_CANCELLED 3 #define WATER_STEP_HIT_WALL 4 +#define STEP_TYPE_GROUND 1 +#define STEP_TYPE_AIR 2 +#define STEP_TYPE_WATER 3 +#define STEP_TYPE_HANG 4 + #define PARTICLE_DUST /* 0x00000001 */ (1 << 0) #define PARTICLE_VERTICAL_STAR /* 0x00000002 */ (1 << 1) #define PARTICLE_2 /* 0x00000004 */ (1 << 2) diff --git a/src/game/mario.c b/src/game/mario.c index d896664d7..1a09b8b12 100644 --- a/src/game/mario.c +++ b/src/game/mario.c @@ -631,7 +631,7 @@ struct Surface *resolve_and_return_wall_collisions(Vec3f pos, f32 offset, f32 ra if (find_wall_collisions(&collisionData)) { wall = collisionData.walls[collisionData.numWalls - 1]; } - + // I'm not sure if this code is actually ever used or not. pos[0] = collisionData.x; pos[1] = collisionData.y; @@ -1143,7 +1143,7 @@ u32 set_mario_action(struct MarioState *m, u32 action, u32 actionArg) { u32 returnValue = 0; smlua_call_event_hooks_mario_action_params_ret_int(HOOK_BEFORE_SET_MARIO_ACTION, m, action, &returnValue); if (returnValue == 1) { return TRUE; } else if (returnValue) { action = returnValue; } - + switch (action & ACT_GROUP_MASK) { case ACT_GROUP_MOVING: action = set_mario_action_moving(m, action, actionArg); @@ -2187,14 +2187,14 @@ s32 execute_mario_action(UNUSED struct Object *o) { // Both of the wind handling portions play wind audio only in // non-Japanese releases. extern bool gDjuiInMainMenu; - if (gMarioState->floor->type == SURFACE_HORIZONTAL_WIND && !gDjuiInMainMenu) { + if (gMarioState->floor && gMarioState->floor->type == SURFACE_HORIZONTAL_WIND && !gDjuiInMainMenu) { spawn_wind_particles(0, (gMarioState->floor->force << 8)); #ifndef VERSION_JP play_sound(SOUND_ENV_WIND2, gMarioState->marioObj->header.gfx.cameraToObject); #endif } - if (gMarioState->floor->type == SURFACE_VERTICAL_WIND) { + if (gMarioState->floor && gMarioState->floor->type == SURFACE_VERTICAL_WIND) { spawn_wind_particles(1, 0); #ifndef VERSION_JP play_sound(SOUND_ENV_WIND2, gMarioState->marioObj->header.gfx.cameraToObject); @@ -2240,7 +2240,7 @@ void init_single_mario(struct MarioState* m) { m->invincTimer = 0; m->visibleToEnemies = TRUE; - + if (m->cap & (SAVE_FLAG_CAP_ON_GROUND | SAVE_FLAG_CAP_ON_KLEPTO | SAVE_FLAG_CAP_ON_UKIKI | SAVE_FLAG_CAP_ON_MR_BLIZZARD)) { m->flags = 0; } else { diff --git a/src/game/mario_actions_automatic.c b/src/game/mario_actions_automatic.c index 8f884b917..b571a03f5 100644 --- a/src/game/mario_actions_automatic.c +++ b/src/game/mario_actions_automatic.c @@ -69,7 +69,7 @@ void play_climbing_sounds(struct MarioState *m, s32 b) { s32 set_pole_position(struct MarioState *m, f32 offsetY) { if (!m) { return 0; } if (m->usedObj == NULL) { m->usedObj = cur_obj_find_nearest_pole(); } - + // This is here so if somehow a pole despawns while you are on it. // You will just drop from it. if (m->usedObj == NULL) { @@ -340,7 +340,8 @@ s32 perform_hanging_step(struct MarioState *m, Vec3f nextPos) { f32 floorHeight; f32 ceilOffset; - smlua_call_event_hooks_mario_param(HOOK_BEFORE_PHYS_STEP, m); + s32 returnValue = 0; + if (smlua_call_event_hooks_mario_param_and_int_ret_int(HOOK_BEFORE_PHYS_STEP, m, STEP_TYPE_HANG, &returnValue)) return returnValue; if (gServerSettings.enableCheats && gCheats.superSpeed && m->playerIndex == 0) { m->vel[0] *= SUPER_SPEED_MULTIPLIER; diff --git a/src/game/mario_actions_submerged.c b/src/game/mario_actions_submerged.c index e7dd1df4f..e14390c2d 100644 --- a/src/game/mario_actions_submerged.c +++ b/src/game/mario_actions_submerged.c @@ -26,9 +26,9 @@ #define MIN_SWIM_STRENGTH 160 #define MIN_SWIM_SPEED 16.0f -static s16 sWasAtSurface[MAX_PLAYERS] = { FALSE, FALSE, FALSE, FALSE, - FALSE, FALSE, FALSE, FALSE, - FALSE, FALSE, FALSE, FALSE, +static s16 sWasAtSurface[MAX_PLAYERS] = { FALSE, FALSE, FALSE, FALSE, + FALSE, FALSE, FALSE, FALSE, + FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE }; static s16 sSwimStrength[MAX_PLAYERS] = { MIN_SWIM_STRENGTH, MIN_SWIM_STRENGTH, MIN_SWIM_STRENGTH, MIN_SWIM_STRENGTH, MIN_SWIM_STRENGTH, MIN_SWIM_STRENGTH, MIN_SWIM_STRENGTH, MIN_SWIM_STRENGTH, @@ -192,7 +192,8 @@ u32 perform_water_step(struct MarioState *m) { Vec3f step; struct Object *marioObj = m->marioObj; - smlua_call_event_hooks_mario_param(HOOK_BEFORE_PHYS_STEP, m); + s32 returnValue = 0; + if (smlua_call_event_hooks_mario_param_and_int_ret_int(HOOK_BEFORE_PHYS_STEP, m, STEP_TYPE_WATER, &returnValue)) return (u32) returnValue; if (gServerSettings.enableCheats && gCheats.superSpeed && m->playerIndex == 0) { m->vel[0] *= SUPER_SPEED_MULTIPLIER; @@ -552,7 +553,7 @@ static s32 check_water_jump(struct MarioState *m) { static s32 act_breaststroke(struct MarioState *m) { if (!m) { return 0; } u16 pIndex = m->playerIndex; - + if (m->actionArg == 0) { sSwimStrength[pIndex] = MIN_SWIM_STRENGTH; } @@ -613,7 +614,7 @@ static s32 act_breaststroke(struct MarioState *m) { static s32 act_swimming_end(struct MarioState *m) { if (!m) { return 0; } u16 pIndex = m->playerIndex; - + if (m->flags & MARIO_METAL_CAP) { return set_mario_action(m, ACT_METAL_WATER_FALLING, 1); } @@ -653,7 +654,7 @@ static s32 act_swimming_end(struct MarioState *m) { static s32 act_flutter_kick(struct MarioState *m) { if (!m) { return 0; } u16 pIndex = m->playerIndex; - + if (m->flags & MARIO_METAL_CAP) { return set_mario_action(m, ACT_METAL_WATER_FALLING, 1); } diff --git a/src/game/mario_step.c b/src/game/mario_step.c index f3aff044d..9daa54b57 100644 --- a/src/game/mario_step.c +++ b/src/game/mario_step.c @@ -350,7 +350,8 @@ s32 perform_ground_step(struct MarioState *m) { u32 stepResult; Vec3f intendedPos; - smlua_call_event_hooks_mario_param(HOOK_BEFORE_PHYS_STEP, m); + s32 returnValue = 0; + if (smlua_call_event_hooks_mario_param_and_int_ret_int(HOOK_BEFORE_PHYS_STEP, m, STEP_TYPE_GROUND, &returnValue)) return returnValue; if (gServerSettings.enableCheats && gCheats.superSpeed && m->playerIndex == 0 && m->action != ACT_BUBBLED) { m->vel[0] *= SUPER_SPEED_MULTIPLIER; @@ -708,7 +709,8 @@ s32 perform_air_step(struct MarioState *m, u32 stepArg) { s32 quarterStepResult; s32 stepResult = AIR_STEP_NONE; - smlua_call_event_hooks_mario_param(HOOK_BEFORE_PHYS_STEP, m); + s32 returnValue = 0; + if (smlua_call_event_hooks_mario_param_and_int_and_int_ret_int(HOOK_BEFORE_PHYS_STEP, m, STEP_TYPE_AIR, stepArg, &returnValue)) return returnValue; if (gServerSettings.enableCheats && gCheats.superSpeed && m->playerIndex == 0 && m->action != ACT_BUBBLED) { m->vel[0] *= SUPER_SPEED_MULTIPLIER; diff --git a/src/pc/lua/smlua_constants_autogen.c b/src/pc/lua/smlua_constants_autogen.c index 0cbd54207..bd1e6de3a 100644 --- a/src/pc/lua/smlua_constants_autogen.c +++ b/src/pc/lua/smlua_constants_autogen.c @@ -2582,6 +2582,10 @@ char gSmluaConstants[] = "" "WATER_STEP_HIT_CEILING = 2\n" "WATER_STEP_CANCELLED = 3\n" "WATER_STEP_HIT_WALL = 4\n" +"STEP_TYPE_GROUND = 1\n" +"STEP_TYPE_AIR = 2\n" +"STEP_TYPE_WATER = 3\n" +"STEP_TYPE_HANG = 4\n" "PARTICLE_DUST = (1 << 0)\n" "PARTICLE_VERTICAL_STAR = (1 << 1)\n" "PARTICLE_2 = (1 << 2)\n" diff --git a/src/pc/lua/smlua_hooks.c b/src/pc/lua/smlua_hooks.c index a039fba4c..a253a0790 100644 --- a/src/pc/lua/smlua_hooks.c +++ b/src/pc/lua/smlua_hooks.c @@ -677,7 +677,7 @@ bool smlua_call_event_hooks_mario_character_sound_param_ret_int(enum LuaHookedEv lua_pushinteger(L, m->playerIndex); lua_gettable(L, -2); lua_remove(L, -2); - + // push character sound lua_pushinteger(L, characterSound); @@ -765,6 +765,81 @@ void smlua_call_event_hooks_mario_param_and_int_ret_bool(enum LuaHookedEventType } } +bool smlua_call_event_hooks_mario_param_and_int_ret_int(enum LuaHookedEventType hookType, struct MarioState* m, s32 param, s32* returnValue) { + lua_State* L = gLuaState; + if (L == NULL) { return false; } + struct LuaHookedEvent* hook = &sHookedEvents[hookType]; + for (int i = 0; i < hook->count; i++) { + s32 prevTop = lua_gettop(L); + + // push the callback onto the stack + lua_rawgeti(L, LUA_REGISTRYINDEX, hook->reference[i]); + + // push mario state + lua_getglobal(L, "gMarioStates"); + lua_pushinteger(L, m->playerIndex); + lua_gettable(L, -2); + lua_remove(L, -2); + + // push param + lua_pushinteger(L, param); + + // call the callback + if (0 != smlua_call_hook(L, 2, 1, 0, hook->mod[i])) { + LOG_LUA("Failed to call the callback: %u", hookType); + continue; + } + + // output the return value + if (lua_type(L, -1) == LUA_TNUMBER) { + *returnValue = smlua_to_integer(L, -1); + lua_settop(L, prevTop); + return true; + } + lua_settop(L, prevTop); + } + return false; +} + +bool smlua_call_event_hooks_mario_param_and_int_and_int_ret_int(enum LuaHookedEventType hookType, struct MarioState* m, s32 param, u32 args, s32* returnValue) { + lua_State* L = gLuaState; + if (L == NULL) { return false; } + struct LuaHookedEvent* hook = &sHookedEvents[hookType]; + for (int i = 0; i < hook->count; i++) { + s32 prevTop = lua_gettop(L); + + // push the callback onto the stack + lua_rawgeti(L, LUA_REGISTRYINDEX, hook->reference[i]); + + // push mario state + lua_getglobal(L, "gMarioStates"); + lua_pushinteger(L, m->playerIndex); + lua_gettable(L, -2); + lua_remove(L, -2); + + // push param + lua_pushinteger(L, param); + + // push args + lua_pushinteger(L, args); + + // call the callback + if (0 != smlua_call_hook(L, 3, 1, 0, hook->mod[i])) { + LOG_LUA("Failed to call the callback: %u", hookType); + continue; + } + + // output the return value + if (lua_type(L, -1) == LUA_TNUMBER) { + *returnValue = smlua_to_integer(L, -1); + lua_settop(L, prevTop); + return true; + } + lua_settop(L, prevTop); + } + return false; +} + //////////////////// // hooked actions // //////////////////// diff --git a/src/pc/lua/smlua_hooks.h b/src/pc/lua/smlua_hooks.h index a0696a682..f44f9af25 100644 --- a/src/pc/lua/smlua_hooks.h +++ b/src/pc/lua/smlua_hooks.h @@ -122,6 +122,8 @@ void smlua_call_event_hooks_on_chat_message(enum LuaHookedEventType hookType, st bool smlua_call_event_hooks_mario_character_sound_param_ret_int(enum LuaHookedEventType hookType, struct MarioState* m, enum CharacterSound characterSound, s32* returnValue); void smlua_call_event_hooks_mario_action_params_ret_int(enum LuaHookedEventType hookType, struct MarioState *m, u32 action, u32* returnValue); void smlua_call_event_hooks_mario_param_and_int_ret_bool(enum LuaHookedEventType hookType, struct MarioState* m, s32 param, bool* returnValue); +bool smlua_call_event_hooks_mario_param_and_int_ret_int(enum LuaHookedEventType hookType, struct MarioState* m, s32 param, s32* returnValue); +bool smlua_call_event_hooks_mario_param_and_int_and_int_ret_int(enum LuaHookedEventType hookType, struct MarioState* m, s32 param, u32 args, s32* returnValue); enum BehaviorId smlua_get_original_behavior_id(const BehaviorScript* behavior); const BehaviorScript* smlua_override_behavior(const BehaviorScript* behavior);