diff --git a/autogen/lua_definitions/constants.lua b/autogen/lua_definitions/constants.lua index 7959a9857..31c4efc7d 100644 --- a/autogen/lua_definitions/constants.lua +++ b/autogen/lua_definitions/constants.lua @@ -6682,22 +6682,25 @@ HOOK_ON_SET_MARIO_ACTION = 3 HOOK_BEFORE_PHYS_STEP = 4 --- @type LuaHookedEventType -HOOK_ON_PVP_ATTACK = 5 +HOOK_ALLOW_PVP_ATTACK = 5 --- @type LuaHookedEventType -HOOK_ON_PLAYER_CONNECTED = 6 +HOOK_ON_PVP_ATTACK = 6 --- @type LuaHookedEventType -HOOK_ON_PLAYER_DISCONNECTED = 7 +HOOK_ON_PLAYER_CONNECTED = 7 --- @type LuaHookedEventType -HOOK_ON_HUD_RENDER = 8 +HOOK_ON_PLAYER_DISCONNECTED = 8 --- @type LuaHookedEventType -HOOK_ON_INTERACT = 9 +HOOK_ON_HUD_RENDER = 9 --- @type LuaHookedEventType -HOOK_MAX = 10 +HOOK_ON_INTERACT = 10 + +--- @type LuaHookedEventType +HOOK_MAX = 11 --- @class ModelExtendedId diff --git a/docs/lua/constants.md b/docs/lua/constants.md index 00c242e5e..9f112baa8 100644 --- a/docs/lua/constants.md +++ b/docs/lua/constants.md @@ -2331,12 +2331,13 @@ | HOOK_BEFORE_MARIO_UPDATE | 2 | | HOOK_ON_SET_MARIO_ACTION | 3 | | HOOK_BEFORE_PHYS_STEP | 4 | -| HOOK_ON_PVP_ATTACK | 5 | -| HOOK_ON_PLAYER_CONNECTED | 6 | -| HOOK_ON_PLAYER_DISCONNECTED | 7 | -| HOOK_ON_HUD_RENDER | 8 | -| HOOK_ON_INTERACT | 9 | -| HOOK_MAX | 10 | +| HOOK_ALLOW_PVP_ATTACK | 5 | +| HOOK_ON_PVP_ATTACK | 6 | +| HOOK_ON_PLAYER_CONNECTED | 7 | +| HOOK_ON_PLAYER_DISCONNECTED | 8 | +| HOOK_ON_HUD_RENDER | 9 | +| HOOK_ON_INTERACT | 10 | +| HOOK_MAX | 11 | [:arrow_up_small:](#) diff --git a/docs/lua/hooks.md b/docs/lua/hooks.md index 90a738a64..4b102665f 100644 --- a/docs/lua/hooks.md +++ b/docs/lua/hooks.md @@ -91,6 +91,7 @@ The lua functions sent to `hook_event()` will be automatically called by SM64 wh | 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_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 | | HOOK_ON_PLAYER_DISCONNECTED | Called when a player disconnects | [MarioState](structs.md#MarioState) disconnector | diff --git a/mods/football.lua b/mods/football.lua index 48b1915e7..022f48a7e 100644 --- a/mods/football.lua +++ b/mods/football.lua @@ -1067,6 +1067,16 @@ function on_player_connected(m) s.team = selectTeam end +--- @param m1 MarioState +--- @param m2 MarioState +function allow_pvp_attack(m1, m2) + local s1 = gPlayerSyncTable[m1.playerIndex] + local s2 = gPlayerSyncTable[m2.playerIndex] + if s1.team == s2.team then + return false + end + return true +end function hud_score_render() djui_hud_set_font(FONT_HUD) @@ -1300,6 +1310,7 @@ hook_event(HOOK_MARIO_UPDATE, mario_update) hook_event(HOOK_UPDATE, update) hook_event(HOOK_ON_HUD_RENDER, on_hud_render) hook_event(HOOK_ON_PLAYER_CONNECTED, on_player_connected) +hook_event(HOOK_ALLOW_PVP_ATTACK, allow_pvp_attack) hook_chat_command('football-reset', "[game|ball] resets the game or ball", on_football_reset_command) for i=0,(MAX_PLAYERS-1) do diff --git a/src/game/interaction.c b/src/game/interaction.c index 92e5a94d4..061e87574 100644 --- a/src/game/interaction.c +++ b/src/game/interaction.c @@ -1356,6 +1356,12 @@ u32 interact_player(struct MarioState* m, UNUSED u32 interactType, struct Object u8 isIgnoredAttack = (m->action == ACT_JUMP || m->action == ACT_DOUBLE_JUMP || m->action == ACT_LONG_JUMP || m->action == ACT_SIDE_FLIP); if ((interaction & INT_ANY_ATTACK) && !(interaction & INT_HIT_FROM_ABOVE) && !isInvulnerable && !isIgnoredAttack && !isAttackerInvulnerable) { + bool allow = true; + smlua_call_event_hooks_mario_params_ret_bool(HOOK_ALLOW_PVP_ATTACK, m, m2, &allow); + if (!allow) { + // Lua blocked the interaction + return false; + } // determine if slide attack should be ignored if ((interaction & INT_ATTACK_SLIDE) && player_is_sliding(m2)) { diff --git a/src/pc/lua/smlua_constants_autogen.c b/src/pc/lua/smlua_constants_autogen.c index 19fe0a5cc..dd6e6f8bb 100644 --- a/src/pc/lua/smlua_constants_autogen.c +++ b/src/pc/lua/smlua_constants_autogen.c @@ -2366,12 +2366,13 @@ char gSmluaConstants[] = "" "HOOK_BEFORE_MARIO_UPDATE = 2\n" "HOOK_ON_SET_MARIO_ACTION = 3\n" "HOOK_BEFORE_PHYS_STEP = 4\n" -"HOOK_ON_PVP_ATTACK = 5\n" -"HOOK_ON_PLAYER_CONNECTED = 6\n" -"HOOK_ON_PLAYER_DISCONNECTED = 7\n" -"HOOK_ON_HUD_RENDER = 8\n" -"HOOK_ON_INTERACT = 9\n" -"HOOK_MAX = 10\n" +"HOOK_ALLOW_PVP_ATTACK = 5\n" +"HOOK_ON_PVP_ATTACK = 6\n" +"HOOK_ON_PLAYER_CONNECTED = 7\n" +"HOOK_ON_PLAYER_DISCONNECTED = 8\n" +"HOOK_ON_HUD_RENDER = 9\n" +"HOOK_ON_INTERACT = 10\n" +"HOOK_MAX = 11\n" "E_MODEL_NONE = 0\n" "E_MODEL_MARIO = 1\n" "E_MODEL_SMOKE = 2\n" diff --git a/src/pc/lua/smlua_hooks.c b/src/pc/lua/smlua_hooks.c index e7312a4cb..e4eb61d98 100644 --- a/src/pc/lua/smlua_hooks.c +++ b/src/pc/lua/smlua_hooks.c @@ -127,6 +127,39 @@ void smlua_call_event_hooks_mario_params(enum LuaHookedEventType hookType, struc } } +void smlua_call_event_hooks_mario_params_ret_bool(enum LuaHookedEventType hookType, struct MarioState* m1, struct MarioState* m2, bool* returnValue) { + lua_State* L = gLuaState; + if (L == NULL) { return; } + struct LuaHookedEvent* hook = &sHookedEvents[hookType]; + for (int i = 0; i < hook->count; i++) { + // push the callback onto the stack + lua_rawgeti(L, LUA_REGISTRYINDEX, hook->reference[i]); + + // push mario state + lua_getglobal(L, "gMarioStates"); + lua_pushinteger(L, m1->playerIndex); + lua_gettable(L, -2); + lua_remove(L, -2); + + // push mario state + lua_getglobal(L, "gMarioStates"); + lua_pushinteger(L, m2->playerIndex); + lua_gettable(L, -2); + lua_remove(L, -2); + + // call the callback + if (0 != smlua_call_hook(L, 2, 1, 0, hook->entry[i])) { + LOG_LUA("Failed to call the callback: %u, %s", hookType, lua_tostring(L, -1)); + smlua_logline(); + continue; + } + + // output the return value + *returnValue = smlua_to_boolean(L, -1); + lua_pop(L, 1); + } +} + void smlua_call_event_hooks_interact_params(enum LuaHookedEventType hookType, struct MarioState* m, struct Object* obj, u32 interactType, bool interactValue) { lua_State* L = gLuaState; if (L == NULL) { return; } diff --git a/src/pc/lua/smlua_hooks.h b/src/pc/lua/smlua_hooks.h index b4282008d..e5d4c77e8 100644 --- a/src/pc/lua/smlua_hooks.h +++ b/src/pc/lua/smlua_hooks.h @@ -10,6 +10,7 @@ enum LuaHookedEventType { HOOK_BEFORE_MARIO_UPDATE, HOOK_ON_SET_MARIO_ACTION, HOOK_BEFORE_PHYS_STEP, + HOOK_ALLOW_PVP_ATTACK, HOOK_ON_PVP_ATTACK, HOOK_ON_PLAYER_CONNECTED, HOOK_ON_PLAYER_DISCONNECTED, @@ -24,6 +25,7 @@ static char* LuaHookedEventTypeName[] = { "HOOK_BEFORE_MARIO_UPDATE", "HOOK_ON_SET_MARIO_ACTION", "HOOK_BEFORE_PHYS_STEP", + "HOOK_ALLOW_PVP_ATTACK", "HOOK_ON_PVP_ATTACK", "HOOK_ON_PLAYER_CONNECTED", "HOOK_ON_PLAYER_DISCONNECTED", @@ -35,6 +37,7 @@ static char* LuaHookedEventTypeName[] = { void smlua_call_event_hooks(enum LuaHookedEventType hookType); void smlua_call_event_hooks_mario_param(enum LuaHookedEventType hookType, struct MarioState* m); void smlua_call_event_hooks_mario_params(enum LuaHookedEventType hookType, struct MarioState* m1, struct MarioState* m2); +void smlua_call_event_hooks_mario_params_ret_bool(enum LuaHookedEventType hookType, struct MarioState* m1, struct MarioState* m2, bool* returnValue); void smlua_call_event_hooks_interact_params(enum LuaHookedEventType hookType, struct MarioState* m, struct Object* obj, u32 interactType, bool interactValue); const BehaviorScript* smlua_override_behavior(const BehaviorScript* behavior);