diff --git a/autogen/lua_definitions/constants.lua b/autogen/lua_definitions/constants.lua
index 934edf9e..5a708ed9 100644
--- a/autogen/lua_definitions/constants.lua
+++ b/autogen/lua_definitions/constants.lua
@@ -2484,7 +2484,7 @@ SHAKE_SHOCK = 10
SHAKE_SMALL_DAMAGE = 3
--- @type integer
-PALETTE_MAX = 30
+PALETTE_MAX = 32
--- @class CharacterSound
diff --git a/autogen/lua_definitions/functions.lua b/autogen/lua_definitions/functions.lua
index 44b33687..b478326b 100644
--- a/autogen/lua_definitions/functions.lua
+++ b/autogen/lua_definitions/functions.lua
@@ -3830,6 +3830,13 @@ function mario_throw_held_object(m)
-- ...
end
+--- @param attacker MarioState
+--- @param victim MarioState
+--- @return integer
+function passes_pvp_interaction_checks(attacker, victim)
+ -- ...
+end
+
--- @param courseNum integer
--- @param levelNum integer
--- @param areaIndex integer
diff --git a/autogen/lua_definitions/structs.lua b/autogen/lua_definitions/structs.lua
index 0e06bdaa..dd5e185f 100644
--- a/autogen/lua_definitions/structs.lua
+++ b/autogen/lua_definitions/structs.lua
@@ -849,6 +849,7 @@
--- @field public oBubbaUnkFC integer
--- @field public oBulletBillInitialMoveYaw integer
--- @field public oBullyKBTimerAndMinionKOCounter integer
+--- @field public oBullyLastNetworkPlayerIndex integer
--- @field public oBullyMarioCollisionAngle integer
--- @field public oBullyPrevX number
--- @field public oBullyPrevY number
diff --git a/docs/lua/functions.md b/docs/lua/functions.md
index 4df2b360..dadf234d 100644
--- a/docs/lua/functions.md
+++ b/docs/lua/functions.md
@@ -743,6 +743,7 @@
- [mario_stop_riding_and_holding](#mario_stop_riding_and_holding)
- [mario_stop_riding_object](#mario_stop_riding_object)
- [mario_throw_held_object](#mario_throw_held_object)
+ - [passes_pvp_interaction_checks](#passes_pvp_interaction_checks)
@@ -14630,6 +14631,27 @@ The `reliable` field will ensure that the packet arrives, but should be used spa
+## [passes_pvp_interaction_checks](#passes_pvp_interaction_checks)
+
+### Lua Example
+`local integerValue = passes_pvp_interaction_checks(attacker, victim)`
+
+### Parameters
+| Field | Type |
+| ----- | ---- |
+| attacker | [MarioState](structs.md#MarioState) |
+| victim | [MarioState](structs.md#MarioState) |
+
+### Returns
+- `integer`
+
+### C Prototype
+`u8 passes_pvp_interaction_checks(struct MarioState* attacker, struct MarioState* victim);`
+
+[:arrow_up_small:](#)
+
+
+
---
# functions from level_info.h
diff --git a/docs/lua/structs.md b/docs/lua/structs.md
index 91ac4aed..eec212df 100644
--- a/docs/lua/structs.md
+++ b/docs/lua/structs.md
@@ -1260,6 +1260,7 @@
| oBullyPrevZ | `number` | |
| oBullyKBTimerAndMinionKOCounter | `integer` | |
| oBullyMarioCollisionAngle | `integer` | |
+| oBullyLastNetworkPlayerIndex | `integer` | |
| oButterflyYPhase | `integer` | |
| oTripletButterflyScale | `number` | |
| oTripletButterflySpeed | `number` | |
diff --git a/src/game/interaction.c b/src/game/interaction.c
index f224dc1d..1304d0aa 100644
--- a/src/game/interaction.c
+++ b/src/game/interaction.c
@@ -1325,6 +1325,21 @@ u8 player_is_sliding(struct MarioState* m) {
return FALSE;
}
+u8 passes_pvp_interaction_checks(struct MarioState* attacker, struct MarioState* victim) {
+ // attacked
+ u8 isInCutscene = ((attacker->action & ACT_GROUP_MASK) == ACT_GROUP_CUTSCENE) || ((victim->action & ACT_GROUP_MASK) == ACT_GROUP_CUTSCENE);
+ isInCutscene = isInCutscene || (attacker->action == ACT_IN_CANNON) || (victim->action == ACT_IN_CANNON);
+ u8 isAttackerInvulnerable = (attacker->action & ACT_FLAG_INVULNERABLE) || attacker->invincTimer != 0 || attacker->hurtCounter != 0;
+ u8 isInvulnerable = (victim->action & ACT_FLAG_INVULNERABLE) || victim->invincTimer != 0 || victim->hurtCounter != 0 || isInCutscene;
+ u8 isIgnoredAttack = (attacker->action == ACT_JUMP || attacker->action == ACT_DOUBLE_JUMP || attacker->action == ACT_LONG_JUMP || attacker->action == ACT_SIDE_FLIP);
+
+ if (victim->knockbackTimer > 0) {
+ return false;
+ }
+
+ return (!isInvulnerable && !isIgnoredAttack && !isAttackerInvulnerable);
+}
+
u32 interact_player(struct MarioState* m, UNUSED u32 interactType, struct Object* o) {
if (!is_player_active(m)) { return FALSE; }
if (gServerSettings.playerInteractions == PLAYER_INTERACTIONS_NONE) { return FALSE; }
@@ -1362,14 +1377,7 @@ u32 interact_player(struct MarioState* m, UNUSED u32 interactType, struct Object
u32 interaction = determine_interaction(m, o);
- // attacked
- u8 isInCutscene = ((m->action & ACT_GROUP_MASK) == ACT_GROUP_CUTSCENE) || ((m2->action & ACT_GROUP_MASK) == ACT_GROUP_CUTSCENE);
- isInCutscene = isInCutscene || (m->action == ACT_IN_CANNON) || (m2->action == ACT_IN_CANNON);
- u8 isAttackerInvulnerable = (m->action & ACT_FLAG_INVULNERABLE) || m->invincTimer != 0 || m->hurtCounter != 0;
- u8 isInvulnerable = (m2->action & ACT_FLAG_INVULNERABLE) || m2->invincTimer != 0 || m2->hurtCounter != 0 || isInCutscene;
- 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) {
+ if ((interaction & INT_ANY_ATTACK) && !(interaction & INT_HIT_FROM_ABOVE) && passes_pvp_interaction_checks(m, m2)) {
bool allow = true;
smlua_call_event_hooks_mario_params_ret_bool(HOOK_ALLOW_PVP_ATTACK, m, m2, &allow);
if (!allow) {
@@ -1377,10 +1385,6 @@ u32 interact_player(struct MarioState* m, UNUSED u32 interactType, struct Object
return false;
}
- if (m2->knockbackTimer > 0) {
- return false;
- }
-
// determine if slide attack should be ignored
if ((interaction & INT_ATTACK_SLIDE) || player_is_sliding(m2)) {
// determine the difference in velocities
diff --git a/src/game/interaction.h b/src/game/interaction.h
index 3069adea..58d53556 100644
--- a/src/game/interaction.h
+++ b/src/game/interaction.h
@@ -115,5 +115,6 @@ u32 mario_check_object_grab(struct MarioState *m);
u32 get_door_save_file_flag(struct Object *door);
void mario_process_interactions(struct MarioState *m);
void mario_handle_special_floors(struct MarioState *m);
+u8 passes_pvp_interaction_checks(struct MarioState* attacker, struct MarioState* victim);
#endif // INTERACTION_H
diff --git a/src/pc/lua/smlua_cobject_autogen.c b/src/pc/lua/smlua_cobject_autogen.c
index 8db814e6..987a27f2 100644
--- a/src/pc/lua/smlua_cobject_autogen.c
+++ b/src/pc/lua/smlua_cobject_autogen.c
@@ -831,7 +831,7 @@ static struct LuaObjectField sNetworkPlayerFields[LUA_NETWORK_PLAYER_FIELD_COUNT
{ "type", LVT_U8, offsetof(struct NetworkPlayer, type), true, LOT_NONE },
};
-#define LUA_OBJECT_FIELD_COUNT 753
+#define LUA_OBJECT_FIELD_COUNT 754
static struct LuaObjectField sObjectFields[LUA_OBJECT_FIELD_COUNT] = {
{ "activeFlags", LVT_S16, offsetof(struct Object, activeFlags), false, LOT_NONE },
{ "areaTimer", LVT_U32, offsetof(struct Object, areaTimer), false, LOT_NONE },
@@ -971,6 +971,7 @@ static struct LuaObjectField sObjectFields[LUA_OBJECT_FIELD_COUNT] = {
{ "oBubbaUnkFC", LVT_S32, offsetof(struct Object, oBubbaUnkFC), false, LOT_NONE },
{ "oBulletBillInitialMoveYaw", LVT_S32, offsetof(struct Object, oBulletBillInitialMoveYaw), false, LOT_NONE },
{ "oBullyKBTimerAndMinionKOCounter", LVT_S32, offsetof(struct Object, oBullyKBTimerAndMinionKOCounter), false, LOT_NONE },
+ { "oBullyLastNetworkPlayerIndex", LVT_S16, offsetof(struct Object, oBullyLastNetworkPlayerIndex), false, LOT_NONE },
{ "oBullyMarioCollisionAngle", LVT_S32, offsetof(struct Object, oBullyMarioCollisionAngle), false, LOT_NONE },
{ "oBullyPrevX", LVT_F32, offsetof(struct Object, oBullyPrevX), false, LOT_NONE },
{ "oBullyPrevY", LVT_F32, offsetof(struct Object, oBullyPrevY), false, LOT_NONE },
diff --git a/src/pc/lua/smlua_functions_autogen.c b/src/pc/lua/smlua_functions_autogen.c
index aae414dd..c060f316 100644
--- a/src/pc/lua/smlua_functions_autogen.c
+++ b/src/pc/lua/smlua_functions_autogen.c
@@ -7822,6 +7822,19 @@ int smlua_func_mario_throw_held_object(lua_State* L) {
return 1;
}
+int smlua_func_passes_pvp_interaction_checks(lua_State* L) {
+ if(!smlua_functions_valid_param_count(L, 2)) { return 0; }
+
+ struct MarioState* attacker = (struct MarioState*)smlua_to_cobject(L, 1, LOT_MARIOSTATE);
+ if (!gSmLuaConvertSuccess) { return 0; }
+ struct MarioState* victim = (struct MarioState*)smlua_to_cobject(L, 2, LOT_MARIOSTATE);
+ if (!gSmLuaConvertSuccess) { return 0; }
+
+ lua_pushinteger(L, passes_pvp_interaction_checks(attacker, victim));
+
+ return 1;
+}
+
//////////////////
// level_info.h //
//////////////////
@@ -16490,6 +16503,7 @@ void smlua_bind_functions_autogen(void) {
smlua_bind_function(L, "mario_stop_riding_and_holding", smlua_func_mario_stop_riding_and_holding);
smlua_bind_function(L, "mario_stop_riding_object", smlua_func_mario_stop_riding_object);
smlua_bind_function(L, "mario_throw_held_object", smlua_func_mario_throw_held_object);
+ smlua_bind_function(L, "passes_pvp_interaction_checks", smlua_func_passes_pvp_interaction_checks);
// level_info.h
smlua_bind_function(L, "get_level_name", smlua_func_get_level_name);