diff --git a/autogen/convert_constants.py b/autogen/convert_constants.py
index 6048b8d63..f036c9b28 100644
--- a/autogen/convert_constants.py
+++ b/autogen/convert_constants.py
@@ -16,6 +16,7 @@ in_files = [
"src/game/characters.h",
"src/pc/network/network_player.h",
"include/PR/os_cont.h",
+ "src/game/interaction.c",
]
exclude_constants = [
@@ -23,6 +24,8 @@ exclude_constants = [
'^LEVEL_.*',
'^AREA_.*',
'^CONT_ERR.*',
+ '^READ_MASK$',
+ '^SIGN_RANGE$',
]
pretend_find = [
diff --git a/autogen/lua_constants/built-in.lua b/autogen/lua_constants/built-in.lua
index 322130b60..7a05988a1 100644
--- a/autogen/lua_constants/built-in.lua
+++ b/autogen/lua_constants/built-in.lua
@@ -130,6 +130,21 @@ function approach_f32(current, target, inc, dec)
return current;
end
+function approach_s32(current, target, inc, dec)
+ if current < target then
+ current = current + inc
+ if current > target then
+ current = target
+ end
+ else
+ current = current - dec
+ if current < target then
+ current = target
+ end
+ end
+ return current;
+end
+
function SOUND_ARG_LOAD(bank, playFlags, soundID, priority, flags2)
return ((bank << 28) | (playFlags << 24) | (soundID << 16) | (priority << 8) | (flags2 << 4) | 1)
end
diff --git a/docs/lua/constants.md b/docs/lua/constants.md
index 2daf1126e..a81c4fe4d 100644
--- a/docs/lua/constants.md
+++ b/docs/lua/constants.md
@@ -6,6 +6,8 @@
- [characters.h](#characters.h)
- [CharacterSound](#CharacterSound)
- [CharacterType](#CharacterType)
+- [interaction.c](#interaction.c)
+ - [InteractionFlag](#InteractionFlag)
- [mario_animation_ids.h](#mario_animation_ids.h)
- [MarioAnimID](#MarioAnimID)
- [network_player.h](#network_player.h)
@@ -740,6 +742,26 @@
+## [interaction.c](#interaction.c)
+- INT_ANY_ATTACK
+- INT_ATTACK_NOT_FROM_BELOW
+- INT_ATTACK_NOT_WEAK_FROM_ABOVE
+- INT_ATTACK_SLIDE
+
+### [enum InteractionFlag](#InteractionFlag)
+| Identifier | Value |
+| :--------- | :---- |
+| INT_GROUND_POUND_OR_TWIRL | (1 << 0) |
+| INT_PUNCH | (1 << 1) |
+| INT_KICK | (1 << 2) |
+| INT_TRIP | (1 << 3) |
+| INT_SLIDE_KICK | (1 << 4) |
+| INT_FAST_ATTACK_OR_SHELL | (1 << 5) |
+| INT_HIT_FROM_ABOVE | (1 << 6) |
+| INT_HIT_FROM_BELOW | (1 << 7) |
+
+
+
## [mario_animation_ids.h](#mario_animation_ids.h)
### [enum MarioAnimID](#MarioAnimID)
diff --git a/docs/lua/hooks.md b/docs/lua/hooks.md
index 85cb941f5..cf597c311 100644
--- a/docs/lua/hooks.md
+++ b/docs/lua/hooks.md
@@ -84,6 +84,7 @@ hook_event(HOOK_MARIO_UPDATE, mario_update)
| ----- | ---- |
| action_id | integer |
| func | Lua Function |
+| interaction_type | [enum InteractionFlag](constants.md#InteractionFlag) |
### Lua Example
diff --git a/mods/character-movesets.lua b/mods/character-movesets.lua
index f890a2869..a7ed97803 100644
--- a/mods/character-movesets.lua
+++ b/mods/character-movesets.lua
@@ -2,11 +2,22 @@
-- incompatible: moveset
-- description: Gives each character unique abilities and stats.
+ANGLE_QUEUE_SIZE = 9
+SPIN_TIMER_SUCCESSFUL_INPUT = 4
+
+gEventTable = {}
+
gStateExtras = {}
for i=0,(MAX_PLAYERS-1) do
gStateExtras[i] = {}
local m = gMarioStates[i]
local e = gStateExtras[i]
+ e.prevPos = {}
+ e.prevPos.x = 0
+ e.prevPos.y = 0
+ e.prevPos.z = 0
+ e.angleDeltaQueue = {}
+ for j=0,(ANGLE_QUEUE_SIZE-1) do e.angleDeltaQueue[j] = 0 end
e.lastAction = m.action
e.animFrame = 0
e.scuttle = 0
@@ -14,18 +25,20 @@ for i=0,(MAX_PLAYERS-1) do
e.boostTimer = 0
e.rotAngle = 0
e.lastHurtCounter = 0
+ e.stickLastAngle = 0
+ e.spinDirection = 0
+ e.spinBufferTimer = 0
+ e.spinInput = 0
+ e.lastIntendedMag = 0
end
-gEventTable = {}
-
-
-ACT_SPIN_POUND_LAND = (0x037 | ACT_FLAG_STATIONARY | ACT_FLAG_ATTACKING)
-ACT_SPIN_POUND = (0x08F | ACT_FLAG_AIR | ACT_FLAG_ATTACKING)
-
-----------
-- luigi --
-----------
+ACT_SPIN_POUND_LAND = (0x037 | ACT_FLAG_STATIONARY | ACT_FLAG_ATTACKING)
+ACT_SPIN_POUND = (0x08F | ACT_FLAG_AIR | ACT_FLAG_ATTACKING)
+
function act_spin_pound(m)
local e = gStateExtras[m.playerIndex]
if m.actionTimer == 0 then
@@ -303,6 +316,8 @@ end
function toad_update(m)
local e = gStateExtras[m.playerIndex]
+
+ -- track average forward velocity
if e.averageForwardVel > m.forwardVel then
e.averageForwardVel = e.averageForwardVel * 0.93 + m.forwardVel * 0.07
else
@@ -331,9 +346,9 @@ gEventTable[CT_TOAD] = {
update = toad_update,
}
------------
+-------------
-- waluigi --
------------
+-------------
ACT_WALL_SLIDE = (0x0BF | ACT_FLAG_AIR | ACT_FLAG_MOVING | ACT_FLAG_ALLOW_VERTICAL_WIND_ACTION)
@@ -453,6 +468,439 @@ gEventTable[CT_WALUIGI] = {
update = waluigi_update,
}
+-----------
+-- wario --
+-----------
+
+ACT_WARIO_DASH = (0x05B | ACT_FLAG_MOVING | ACT_FLAG_ATTACKING)
+ACT_WARIO_AIR_DASH = (0x05B | ACT_FLAG_AIR | ACT_FLAG_ATTACKING)
+ACT_CORKSCREW_CONK = (0x05D | ACT_FLAG_AIR | ACT_FLAG_ATTACKING | ACT_FLAG_ALLOW_VERTICAL_WIND_ACTION)
+ACT_WARIO_SPINNING_OBJ = (0x05B | ACT_FLAG_STATIONARY)
+
+function act_corkscrew_conk(m)
+ local e = gStateExtras[m.playerIndex]
+ m.particleFlags = m.particleFlags | PARTICLE_DUST
+
+ common_air_action_step(m, ACT_JUMP_LAND, MARIO_ANIM_FORWARD_SPINNING, AIR_STEP_NONE)
+ set_anim_to_frame(m, e.animFrame)
+
+ if e.animFrame >= m.marioObj.header.gfx.animInfo.curAnim.loopEnd then
+ e.animFrame = e.animFrame - m.marioObj.header.gfx.animInfo.curAnim.loopEnd
+ end
+
+ if (m.input & INPUT_Z_PRESSED) ~= 0 then
+ local rc = set_mario_action(m, ACT_GROUND_POUND, 0)
+ m.actionTimer = 5
+ return rc
+ end
+
+ m.actionTimer = m.actionTimer + 1
+ e.animFrame = e.animFrame + 1
+
+ return 0
+end
+
+function act_wario_dash(m)
+ local e = gStateExtras[m.playerIndex]
+
+ -- when hitting wall, stay dashing for an extra frame
+ if m.actionArg == 99 then
+ m.actionTimer = m.actionTimer + 1
+ if m.actionTimer > 2 then
+ return set_mario_action(m, ACT_WALKING, 0)
+ end
+ return 0
+ end
+
+ -- make sound
+ if m.actionTimer == 0 then
+ m.actionState = m.actionArg
+ play_character_sound(m, CHAR_SOUND_YAHOO)
+ end
+
+ -- walk once dash is up
+ if m.actionTimer > 15 then
+ return set_mario_action(m, ACT_WALKING, 0)
+ end
+
+ -- slide and set animation
+ common_slide_action(m, ACT_DIVE, ACT_MOVE_PUNCHING, MARIO_ANIM_FIRST_PUNCH)
+ set_anim_to_frame(m, 25)
+
+ -- set dash speed
+ local speed = 100
+ if m.actionTimer > 8 then
+ speed = speed - (m.actionTimer - 8) * 11
+ end
+ mario_set_forward_vel(m, speed)
+
+ -- corkscrew conk
+ if (m.input & INPUT_A_PRESSED) ~= 0 then
+ set_jumping_action(m, ACT_CORKSCREW_CONK, 0)
+ play_character_sound(m, CHAR_SOUND_YAHOO)
+ end
+
+ -- slide kick
+ if (m.input & INPUT_Z_PRESSED) ~= 0 then
+ return set_mario_action(m, ACT_SLIDE_KICK, 0)
+ end
+
+ m.actionTimer = m.actionTimer + 1
+ return 0
+end
+
+function act_wario_air_dash(m)
+ local e = gStateExtras[m.playerIndex]
+
+ -- walk once dash is up
+ if m.actionTimer > 15 then
+ return set_mario_action(m, ACT_JUMP_LAND, 0)
+ end
+
+ -- slide and set animation
+ common_air_action_step(m, ACT_JUMP_LAND, MARIO_ANIM_FIRST_PUNCH, AIR_STEP_NONE)
+ set_anim_to_frame(m, 25)
+
+ -- set dash speed
+ local speed = 100
+ if m.actionTimer > 8 then
+ speed = speed - (m.actionTimer - 8) * 11
+ end
+ mario_set_forward_vel(m, speed)
+
+ -- corkscrew conk
+ if (m.input & INPUT_A_PRESSED) ~= 0 then
+ set_jumping_action(m, ACT_CORKSCREW_CONK, 0)
+ play_character_sound(m, CHAR_SOUND_YAHOO)
+ end
+
+ -- slide kick
+ if (m.input & INPUT_Z_PRESSED) ~= 0 then
+ return set_mario_action(m, ACT_SLIDE_KICK, 0)
+ end
+
+ m.actionTimer = m.actionTimer + 1
+ return 0
+end
+
+function act_wario_spinning_obj(m)
+ local spin = 0
+
+ -- throw object
+ if m.playerIndex == 0 and (m.input & INPUT_B_PRESSED) ~= 0 then
+ play_character_sound_if_no_flag(m, CHAR_SOUND_WAH2, MARIO_MARIO_SOUND_PLAYED)
+ play_sound_if_no_flag(m, SOUND_ACTION_THROW, MARIO_ACTION_SOUND_PLAYED)
+ return set_mario_action(m, ACT_RELEASING_BOWSER, 0)
+ end
+
+ -- set animation
+ if m.playerIndex == 0 and m.angleVel.y == 0 then
+ m.actionTimer = m.actionTimer + 1
+ if m.actionTimer > 120 then
+ return set_mario_action(m, ACT_RELEASING_BOWSER, 1)
+ end
+
+ set_mario_animation(m, MARIO_ANIM_HOLDING_BOWSER)
+ else
+ m.actionTimer = 0
+ set_mario_animation(m, MARIO_ANIM_SWINGING_BOWSER)
+ end
+
+ -- spin
+ if m.intendedMag > 20.0 then
+ -- spin = acceleration
+ spin = (m.intendedYaw - m.twirlYaw) / 0x20
+
+ if spin < -0x80 then
+ spin = -0x80
+ end
+ if spin > 0x80 then
+ spin = 0x80
+ end
+
+ m.twirlYaw = m.intendedYaw
+ m.angleVel.y = m.angleVel.y + spin
+
+ if m.angleVel.y > 0x1000 then
+ m.angleVel.y = 0x1000
+ end
+ if m.angleVel.y < -0x1000 then
+ m.angleVel.y = -0x1000
+ end
+ elseif m.angleVel.y > -0x750 and m.angleVel.y < 0x750 then
+ -- go back to walking
+ return set_mario_action(m, ACT_HOLD_IDLE, 0)
+ else
+ -- slow down spin
+ m.angleVel.y = approach_s32(m.angleVel.y, 0, 128, 128);
+ end
+
+ -- apply spin
+ spin = m.faceAngle.y
+ m.faceAngle.y = m.faceAngle.y + m.angleVel.y
+
+ -- play sound on overflow
+ if m.angleVel.y <= -0x100 and spin < m.faceAngle.y then
+ queue_rumble_data_mario(m, 4, 20)
+ play_sound(SOUND_OBJ_BOWSER_SPINNING, m.marioObj.header.gfx.cameraToObject)
+ end
+ if m.angleVel.y >= 0x100 and spin > m.faceAngle.y then
+ queue_rumble_data_mario(m, 4, 20)
+ play_sound(SOUND_OBJ_BOWSER_SPINNING, m.marioObj.header.gfx.cameraToObject)
+ end
+
+ stationary_ground_step(m)
+
+ if m.angleVel.y >= 0 then
+ m.marioObj.header.gfx.angle.x = -m.angleVel.y
+ else
+ m.marioObj.header.gfx.angle.x = m.angleVel.y
+ end
+
+ return false
+end
+
+function wario_update_spin_input(m)
+ local e = gStateExtras[m.playerIndex]
+ local rawAngle = atan2s(-m.controller.stickY, m.controller.stickX)
+ e.spinInput = 0
+
+ -- prevent issues due to the frame going out of the dead zone registering the last angle as 0
+ if e.lastIntendedMag > 0.5 and m.intendedMag > 0.5 then
+ local angleOverFrames = 0
+ local thisFrameDelta = 0
+ local i = 0
+
+ local newDirection = e.spinDirection
+ local signedOverflow = 0
+
+ if rawAngle < e.stickLastAngle then
+ if (e.stickLastAngle - rawAngle) > 0x8000 then
+ signedOverflow = 1
+ end
+ if signedOverflow ~= 0 then
+ newDirection = 1
+ else
+ newDirection = -1
+ end
+ elseif rawAngle > e.stickLastAngle then
+ if (rawAngle - e.stickLastAngle) > 0x8000 then
+ signedOverflow = 1
+ end
+ if signedOverflow ~= 0 then
+ newDirection = -1
+ else
+ newDirection = 1
+ end
+ end
+
+ if e.spinDirection ~= newDirection then
+ for i=0,(ANGLE_QUEUE_SIZE-1) do
+ e.angleDeltaQueue[i] = 0
+ end
+ e.spinDirection = newDirection
+ else
+ for i=(ANGLE_QUEUE_SIZE-1),1,-1 do
+ e.angleDeltaQueue[i] = e.angleDeltaQueue[i-1]
+ angleOverFrames = angleOverFrames + e.angleDeltaQueue[i]
+ end
+ end
+
+ if e.spinDirection < 0 then
+ if signedOverflow ~= 0 then
+ thisFrameDelta = math.floor((1.0*e.stickLastAngle + 0x10000) - rawAngle)
+ else
+ thisFrameDelta = e.stickLastAngle - rawAngle
+ end
+ elseif e.spinDirection > 0 then
+ if signedOverflow ~= 0 then
+ thisFrameDelta = math.floor(1.0*rawAngle + 0x10000 - e.stickLastAngle)
+ else
+ thisFrameDelta = rawAngle - e.stickLastAngle
+ end
+ end
+
+ e.angleDeltaQueue[0] = thisFrameDelta
+ angleOverFrames = angleOverFrames + thisFrameDelta
+
+ if angleOverFrames >= 0xA000 then
+ e.spinBufferTimer = SPIN_TIMER_SUCCESSFUL_INPUT
+ end
+
+
+ -- allow a buffer after a successful input so that you can switch directions
+ if e.spinBufferTimer > 0 then
+ e.spinInput = 1
+ e.spinBufferTimer = e.spinBufferTimer - 1
+ end
+ else
+ e.spinDirection = 0
+ e.spinBufferTimer = 0
+ end
+
+ e.stickLastAngle = rawAngle
+ e.lastIntendedMag = m.intendedMag
+end
+
+function wario_before_phys_step(m)
+ local hScale = 1.0
+
+ -- slower on ground
+ if (m.action & ACT_FLAG_MOVING) ~= 0 then
+ hScale = hScale * 0.9
+ end
+
+ -- make wario sink
+ if (m.action & ACT_FLAG_SWIMMING) ~= 0 then
+ if m.action ~= ACT_WATER_PLUNGE then
+ m.vel.y = m.vel.y - 3
+ end
+ end
+
+ -- faster holding item
+ if m.heldObj ~= nil then
+ m.vel.y = m.vel.y - 1
+ hScale = hScale * 1.3
+ if (m.action & ACT_FLAG_AIR) ~= 0 then
+ hScale = hScale * 1.3
+ end
+ end
+
+ m.vel.x = m.vel.x * hScale
+ m.vel.z = m.vel.z * hScale
+end
+
+function wario_on_set_action(m)
+ local e = gStateExtras[m.playerIndex]
+
+ -- air dash
+ if m.action == ACT_MOVE_PUNCHING and m.prevAction == ACT_WARIO_DASH then
+ local actionTimer = m.actionTimer
+ set_mario_action(m, ACT_WARIO_AIR_DASH, 0)
+ m.actionTimer = actionTimer
+ m.vel.x = 0
+ m.vel.y = 0
+ m.vel.z = 0
+ return
+ end
+
+ -- slow down when dash/conk ends
+ if (m.prevAction == ACT_WARIO_DASH) or (m.prevAction == ACT_WARIO_AIR_DASH) or (m.prevAction == ACT_CORKSCREW_CONK) then
+ if m.action == ACT_CORKSCREW_CONK then
+ mario_set_forward_vel(m, 60)
+ m.vel.x = 0
+ m.vel.y = 70.0
+ m.vel.z = 0
+ elseif (m.action == ACT_SLIDE_KICK) then
+ mario_set_forward_vel(m, 70)
+ m.vel.x = 0
+ m.vel.y = 30.0
+ m.vel.z = 0
+ elseif m.forwardVel > 20 then
+ mario_set_forward_vel(m, 20)
+ end
+ end
+
+ -- when hitting a wall which dashing, have one more single frame of dash
+ if m.action == ACT_GROUND_BONK and m.prevAction == ACT_WARIO_DASH then
+ set_mario_action(m, ACT_WARIO_DASH, 99)
+ mario_set_forward_vel(m, 1)
+ m.vel.x = 0
+ m.vel.y = 0
+ m.vel.z = 0
+ end
+
+ -- more height on triple jump
+ if m.action == ACT_TRIPLE_JUMP or m.action == ACT_SPECIAL_TRIPLE_JUMP then
+ m.vel.y = m.vel.y * 1.15
+ end
+
+ -- less height on other jumps
+ if m.action == ACT_JUMP or m.action == ACT_DOUBLE_JUMP or m.action == ACT_STEEP_JUMP or m.action == ACT_RIDING_SHELL_JUMP or m.action == ACT_BACKFLIP or m.action == ACT_LONG_JUMP then
+ m.vel.y = m.vel.y * 0.8
+
+ -- prevent from getting stuck on platform
+ if m.marioObj.platform ~= nil then
+ m.pos.y = m.pos.y + 10
+ end
+ elseif m.action == ACT_SIDE_FLIP then
+ m.vel.y = m.vel.y * 0.86
+
+ -- prevent from getting stuck on platform
+ if m.marioObj.platform ~= nil then
+ m.pos.y = m.pos.y + 10
+ end
+ end
+ e.lastAction = action
+end
+
+function wario_update(m)
+ local hScale = 1.0
+ local e = gStateExtras[m.playerIndex]
+
+ wario_update_spin_input(m)
+
+ -- spin around objects
+ if m.action == ACT_HOLD_IDLE or m.action == ACT_HOLD_WALKING then
+ if e.spinInput ~= 0 then
+ m.twirlYaw = m.intendedYaw
+ if e.spinDirection == 1 then
+ m.angleVel.y = 1500
+ else
+ m.angleVel.y = -1500
+ end
+ m.intendedMag = 21
+ return set_mario_action(m, ACT_WARIO_SPINNING_OBJ, 1)
+ end
+ end
+
+ -- turn heavy objects into light
+ if m.action == ACT_HOLD_HEAVY_IDLE then
+ return set_mario_action(m, ACT_HOLD_IDLE, 0)
+ end
+
+ -- turn dive into dash
+ if m.action == ACT_DIVE and m.prevAction == ACT_WALKING then
+ if (m.controller.buttonPressed & B_BUTTON) ~= 0 then
+ m.actionTimer = 0
+ return set_mario_action(m, ACT_WARIO_DASH, 0)
+ end
+ end
+
+ -- shake cmaera
+ if m.action == ACT_GROUND_POUND_LAND then
+ set_camera_shake_from_hit(SHAKE_MED_DAMAGE)
+ end
+
+ -- faster ground pound
+ if m.action == ACT_GROUND_POUND then
+ m.vel.y = m.vel.y * 1.3
+ end
+
+ -- more gravity
+ if (m.action & ACT_FLAG_AIR) ~= 0 then
+ m.vel.y = m.vel.y - 1.15
+ end
+
+ -- takes less damage
+ if m.action ~= ACT_LAVA_BOOST then
+ m.hurtCounter = m.hurtCounter * 0.5
+ end
+
+ m.vel.x = m.vel.x * hScale
+ m.vel.z = m.vel.z * hScale
+
+ e.prevPos.x = m.pos.x
+ e.prevPos.y = m.pos.y
+ e.prevPos.z = m.pos.z
+end
+
+gEventTable[CT_WARIO] = {
+ before_phys_step = wario_before_phys_step,
+ on_set_action = wario_on_set_action,
+ update = wario_update,
+}
+
----------
-- main --
----------
@@ -462,9 +910,10 @@ function mario_before_phys_step(m)
return
end
- if gEventTable[m.character.type] == nil then
+ if gEventTable[m.character.type] == nil or gEventTable[m.character.type].before_phys_step == nil then
return
end
+
gEventTable[m.character.type].before_phys_step(m)
end
@@ -473,20 +922,34 @@ function mario_on_set_action(m)
return
end
- if gEventTable[m.character.type] == nil then
+ if gEventTable[m.character.type] == nil or gEventTable[m.character.type].on_set_action == nil then
return
end
+
gEventTable[m.character.type].on_set_action(m)
end
+function mario_before_update(m)
+ if m.action == ACT_BUBBLED then
+ return
+ end
+
+ if gEventTable[m.character.type] == nil or gEventTable[m.character.type].before_update == nil then
+ return
+ end
+
+ gEventTable[m.character.type].before_update(m)
+end
+
function mario_update(m)
if m.action == ACT_BUBBLED then
return
end
- if gEventTable[m.character.type] == nil then
+ if gEventTable[m.character.type] == nil or gEventTable[m.character.type].update == nil then
return
end
+
gEventTable[m.character.type].update(m)
end
@@ -494,10 +957,15 @@ end
-- hooks --
-----------
+hook_event(HOOK_BEFORE_MARIO_UPDATE, mario_before_update)
hook_event(HOOK_MARIO_UPDATE, mario_update)
hook_event(HOOK_ON_SET_MARIO_ACTION, mario_on_set_action)
hook_event(HOOK_BEFORE_PHYS_STEP, mario_before_phys_step)
hook_mario_action(ACT_WALL_SLIDE, act_wall_slide)
-hook_mario_action(ACT_SPIN_POUND, act_spin_pound)
-hook_mario_action(ACT_SPIN_POUND_LAND, act_spin_pound_land)
+hook_mario_action(ACT_SPIN_POUND, act_spin_pound, INT_GROUND_POUND_OR_TWIRL)
+hook_mario_action(ACT_SPIN_POUND_LAND, act_spin_pound_land, INT_GROUND_POUND_OR_TWIRL)
+hook_mario_action(ACT_WARIO_DASH, act_wario_dash, INT_PUNCH)
+hook_mario_action(ACT_WARIO_AIR_DASH, act_wario_air_dash, INT_PUNCH)
+hook_mario_action(ACT_CORKSCREW_CONK, act_corkscrew_conk, INT_FAST_ATTACK_OR_SHELL)
+hook_mario_action(ACT_WARIO_SPINNING_OBJ, act_wario_spinning_obj)
diff --git a/src/game/interaction.c b/src/game/interaction.c
index 1eea2092b..e4994cd23 100644
--- a/src/game/interaction.c
+++ b/src/game/interaction.c
@@ -29,14 +29,16 @@
#include "pc/network/network.h"
#include "pc/lua/smlua_hooks.h"
-#define INT_GROUND_POUND_OR_TWIRL (1 << 0) // 0x01
-#define INT_PUNCH (1 << 1) // 0x02
-#define INT_KICK (1 << 2) // 0x04
-#define INT_TRIP (1 << 3) // 0x08
-#define INT_SLIDE_KICK (1 << 4) // 0x10
-#define INT_FAST_ATTACK_OR_SHELL (1 << 5) // 0x20
-#define INT_HIT_FROM_ABOVE (1 << 6) // 0x40
-#define INT_HIT_FROM_BELOW (1 << 7) // 0x80
+enum InteractionFlag {
+ INT_GROUND_POUND_OR_TWIRL = (1 << 0), // 0x01
+ INT_PUNCH = (1 << 1), // 0x02
+ INT_KICK = (1 << 2), // 0x04
+ INT_TRIP = (1 << 3), // 0x08
+ INT_SLIDE_KICK = (1 << 4), // 0x10
+ INT_FAST_ATTACK_OR_SHELL = (1 << 5), // 0x20
+ INT_HIT_FROM_ABOVE = (1 << 6), // 0x40
+ INT_HIT_FROM_BELOW = (1 << 7), // 0x80
+};
#define INT_ATTACK_NOT_FROM_BELOW \
(INT_GROUND_POUND_OR_TWIRL | INT_PUNCH | INT_KICK | INT_TRIP | INT_SLIDE_KICK \
@@ -192,15 +194,17 @@ u32 determine_interaction(struct MarioState *m, struct Object *o) {
u32 interaction = 0;
u32 action = m->action;
+ interaction = smlua_get_action_interaction_type(m);
+
// hack: make water punch actually do something
- if (m->action == ACT_WATER_PUNCH && o->oInteractType & INTERACT_PLAYER) {
+ if (interaction == 0 && m->action == ACT_WATER_PUNCH && o->oInteractType & INTERACT_PLAYER) {
s16 dYawToObject = mario_obj_angle_to_object(m, o) - m->faceAngle[1];
if (-0x2AAA <= dYawToObject && dYawToObject <= 0x2AAA) {
- return INT_PUNCH;
+ interaction = INT_PUNCH;
}
}
- if (action & ACT_FLAG_ATTACKING) {
+ if (interaction == 0 && action & ACT_FLAG_ATTACKING) {
if (action == ACT_PUNCHING || action == ACT_MOVE_PUNCHING || action == ACT_JUMP_KICK) {
s16 dYawToObject = mario_obj_angle_to_object(m, o) - m->faceAngle[1];
@@ -1347,6 +1351,7 @@ u32 interact_player(struct MarioState* m, UNUSED u32 interactType, struct Object
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);
+
if ((interaction & INT_ANY_ATTACK) && !(interaction & INT_HIT_FROM_ABOVE) && !isInvulnerable && !isIgnoredAttack && !isAttackerInvulnerable) {
// determine if slide attack should be ignored
diff --git a/src/pc/lua/smlua_constants_autogen.c b/src/pc/lua/smlua_constants_autogen.c
index 4fcfdca0c..e7e58bbc8 100644
--- a/src/pc/lua/smlua_constants_autogen.c
+++ b/src/pc/lua/smlua_constants_autogen.c
@@ -115,6 +115,20 @@ char gSmluaConstants[] = ""
" end\n"
" return current;\n"
"end\n"
+"function approach_s32(current, target, inc, dec)\n"
+" if current < target then\n"
+" current = current + inc\n"
+" if current > target then\n"
+" current = target\n"
+" end\n"
+" else\n"
+" current = current - dec\n"
+" if current < target then\n"
+" current = target\n"
+" end\n"
+" end\n"
+" return current;\n"
+"end\n"
"function SOUND_ARG_LOAD(bank, playFlags, soundID, priority, flags2)\n"
" return ((bank << 28) | (playFlags << 24) | (soundID << 16) | (priority << 8) | (flags2 << 4) | 1)\n"
"end\n"
@@ -818,6 +832,18 @@ char gSmluaConstants[] = ""
"CHAR_SOUND_SO_LONGA_BOWSER = 40\n"
"CHAR_SOUND_IMA_TIRED = 41\n"
"CHAR_SOUND_MAX = 42\n"
+"INT_GROUND_POUND_OR_TWIRL = (1 << 0)\n"
+"INT_PUNCH = (1 << 1)\n"
+"INT_KICK = (1 << 2)\n"
+"INT_TRIP = (1 << 3)\n"
+"INT_SLIDE_KICK = (1 << 4)\n"
+"INT_FAST_ATTACK_OR_SHELL = (1 << 5)\n"
+"INT_HIT_FROM_ABOVE = (1 << 6)\n"
+"INT_HIT_FROM_BELOW = (1 << 7)\n"
+"INT_ATTACK_NOT_FROM_BELOW = (INT_GROUND_POUND_OR_TWIRL | INT_PUNCH | INT_KICK | INT_TRIP | INT_SLIDE_KICK | INT_FAST_ATTACK_OR_SHELL | INT_HIT_FROM_ABOVE)\n"
+"INT_ANY_ATTACK = (INT_GROUND_POUND_OR_TWIRL | INT_PUNCH | INT_KICK | INT_TRIP | INT_SLIDE_KICK | INT_FAST_ATTACK_OR_SHELL | INT_HIT_FROM_ABOVE | INT_HIT_FROM_BELOW)\n"
+"INT_ATTACK_NOT_WEAK_FROM_ABOVE = (INT_GROUND_POUND_OR_TWIRL | INT_PUNCH | INT_KICK | INT_TRIP | INT_HIT_FROM_BELOW)\n"
+"INT_ATTACK_SLIDE = (INT_SLIDE_KICK | INT_FAST_ATTACK_OR_SHELL)\n"
"MARIO_ANIM_SLOW_LEDGE_GRAB = 0\n"
"MARIO_ANIM_FALL_OVER_BACKWARDS = 1\n"
"MARIO_ANIM_BACKWARD_AIR_KB = 2\n"
diff --git a/src/pc/lua/smlua_functions.c b/src/pc/lua/smlua_functions.c
index 4801aa080..01eea5c40 100644
--- a/src/pc/lua/smlua_functions.c
+++ b/src/pc/lua/smlua_functions.c
@@ -19,6 +19,16 @@ bool smlua_functions_valid_param_count(lua_State* L, int expected) {
return true;
}
+bool smlua_functions_valid_param_range(lua_State* L, int min, int max) {
+ int top = lua_gettop(L);
+ if (top < min || top > max) {
+ LOG_LUA("improper param count: expected (%u - %u), received %u", min, max, top);
+ smlua_logline();
+ return false;
+ }
+ return true;
+}
+
//////////
// misc //
//////////
diff --git a/src/pc/lua/smlua_functions.h b/src/pc/lua/smlua_functions.h
index 60f0d69d5..291168591 100644
--- a/src/pc/lua/smlua_functions.h
+++ b/src/pc/lua/smlua_functions.h
@@ -2,6 +2,7 @@
#define SMLUA_FUNCTIONS_H
bool smlua_functions_valid_param_count(lua_State* L, int expected);
+bool smlua_functions_valid_param_range(lua_State* L, int min, int max);
void smlua_bind_functions(void);
#endif
\ No newline at end of file
diff --git a/src/pc/lua/smlua_hooks.c b/src/pc/lua/smlua_hooks.c
index 83dfc025e..652a4263b 100644
--- a/src/pc/lua/smlua_hooks.c
+++ b/src/pc/lua/smlua_hooks.c
@@ -12,6 +12,8 @@ static struct LuaHookedEvent sHookedEvents[HOOK_MAX] = { 0 };
int smlua_hook_event(lua_State* L) {
if (L == NULL) { return 0; }
+ if (!smlua_functions_valid_param_count(L, 2)) { return 0; }
+
u16 hookType = smlua_to_integer(L, -2);
if (!gSmLuaConvertSuccess) { return 0; }
@@ -139,6 +141,7 @@ void smlua_call_event_hooks_network_player_param(enum LuaHookedEventType hookTyp
struct LuaHookedMarioAction {
u32 action;
+ u32 interactionType;
int reference;
};
@@ -149,6 +152,9 @@ static int sHookedMarioActionsCount = 0;
int smlua_hook_mario_action(lua_State* L) {
if (L == NULL) { return 0; }
+ if (!smlua_functions_valid_param_range(L, 2, 3)) { return 0; }
+ int paramCount = lua_gettop(L);
+
if (sHookedMarioActionsCount >= MAX_HOOKED_ACTIONS) {
LOG_LUA("Hooked mario actions exceeded maximum references!");
smlua_logline();
@@ -162,6 +168,7 @@ int smlua_hook_mario_action(lua_State* L) {
return 0;
}
+ lua_pushvalue(L, 2);
int ref = luaL_ref(L, LUA_REGISTRYINDEX);
if (ref == -1) {
@@ -170,8 +177,19 @@ int smlua_hook_mario_action(lua_State* L) {
return 0;
}
+ lua_Integer interactionType = 0;
+ if (paramCount >= 3) {
+ interactionType = smlua_to_integer(L, 3);
+ if (interactionType == 0 || !gSmLuaConvertSuccess) {
+ LOG_LUA("Hook Action: tried to hook invalid interactionType: %lld, %u", interactionType, gSmLuaConvertSuccess);
+ smlua_logline();
+ return 0;
+ }
+ }
+
struct LuaHookedMarioAction* hooked = &sHookedMarioActions[sHookedMarioActionsCount];
hooked->action = action;
+ hooked->interactionType = interactionType;
hooked->reference = ref;
if (!gSmLuaConvertSuccess) { return 0; }
@@ -213,6 +231,18 @@ bool smlua_call_action_hook(struct MarioState* m, s32* returnValue) {
return false;
}
+u32 smlua_get_action_interaction_type(struct MarioState* m) {
+ u32 interactionType = 0;
+ lua_State* L = gLuaState;
+ if (L == NULL) { return false; }
+ for (int i = 0; i < sHookedMarioActionsCount; i++) {
+ if (sHookedMarioActions[i].action == m->action) {
+ interactionType |= sHookedMarioActions[i].interactionType;
+ }
+ }
+ return interactionType;
+}
+
/////////////////////////
// hooked chat command //
/////////////////////////
@@ -230,6 +260,8 @@ static int sHookedChatCommandsCount = 0;
int smlua_hook_chat_command(lua_State* L) {
if (L == NULL) { return 0; }
+ if (!smlua_functions_valid_param_count(L, 3)) { return 0; }
+
if (sHookedChatCommandsCount >= MAX_HOOKED_CHAT_COMMANDS) {
LOG_LUA("Hooked chat command exceeded maximum references!");
smlua_logline();
diff --git a/src/pc/lua/smlua_hooks.h b/src/pc/lua/smlua_hooks.h
index 0d459f7d8..87824e799 100644
--- a/src/pc/lua/smlua_hooks.h
+++ b/src/pc/lua/smlua_hooks.h
@@ -32,6 +32,7 @@ void smlua_call_event_hooks_mario_param(enum LuaHookedEventType hookType, struct
void smlua_call_event_hooks_mario_params(enum LuaHookedEventType hookType, struct MarioState* m1, struct MarioState* m2);
bool smlua_call_action_hook(struct MarioState* m, s32* returnValue);
+u32 smlua_get_action_interaction_type(struct MarioState* m);
bool smlua_call_chat_command_hook(char* command);
void smlua_display_chat_commands(void);