This commit is contained in:
xLuigiGamerx 2026-01-28 23:04:18 +03:00
commit a6cf3f202a
669 changed files with 33720 additions and 2336 deletions

View file

@ -97,11 +97,6 @@ gServerSettings = {}
--- Struct containing the settings for Nametags
gNametagsSettings = {}
--- @type Camera
--- Struct contaning camera fields
--- - This camera is the same as `gMarioStates[i].area.camera` or `gCurrentArea.camera`
gCamera = {}
-----------
-- hooks --
-----------

View file

@ -0,0 +1,93 @@
--- Don't add any functional code to this file ---
--- @meta
--- @class LuigiState
--- @class ToadState
--- @class WarioState
--- @class WaluigiState
--- @class ToadetteState
--- @field public averageForwardVel number
--- @class PeachState
--- @class DaisyState
--- @class YoshiState
--- @class BirdoState
--- @field public spitTimer integer
--- @field public framesSinceShoot integer
--- @field public flameCharge integer
--- @class SpikeState
--- @class PaulineState
--- @class RosalinaState
--- @field public canSpin boolean
--- @field public orbitObjActive boolean
--- @field public orbitObjDist number
--- @field public orbitObjAngle integer
--- @class WapeachState
--- @class DonkeyKongState
--- @class SonicState
--- @field public spinCharge integer
--- @field public groundYVel integer
--- @field public prevForwardVel integer
--- @field public peakHeight integer
--- @field public actionADone boolean
--- @field public actionBDone boolean
--- @field public bounced boolean
--- @field public spindashState integer
--- @field public instashieldTimer integer
--- @field public oxygen integer
--- @field public prevVelY number
--- @field public prevHeight number
--- @field public physTimer integer
--- @field public lastforwardPos Vec3f
--- @field public realFVel number
--- @class CharacterState
--- @field public mario MarioState
--- @field public luigi LuigiState
--- @field public toad ToadState
--- @field public wario WarioState
--- @field public waluigi WaluigiState
--- @field public toadette ToadetteState
--- @field public peach PeachState
--- @field public daisy DaisyState
--- @field public yoshi YoshiState
--- @field public birdo BirdoState
--- @field public spike SpikeState
--- @field public pauline PaulineState
--- @field public rosalina RosalinaState
--- @field public wapeach WapeachState
--- @field public donkeyKong DonkeyKongState
--- @field public sonic SonicState
--- @alias SonicMouthGSCId
--- | `SONIC_MOUTH_NORMAL`
--- | `SONIC_MOUTH_FROWN`
--- | `SONIC_MOUTH_GRIMACING`
--- | `SONIC_MOUTH_HAPPY`
--- | `SONIC_MOUTH_GRIN`
--- | `SONIC_MOUTH_ATTACKED`
--- | `SONIC_MOUTH_SHOCKED`
--- | `SONIC_MOUTH_SURPRISED`
--- | `SONIC_MOUTH_NEUTRAL`
--- @alias SonicMouthSideGSCId
--- | `SONIC_MOUTH_LEFT`
--- | `SONIC_MOUTH_RIGHT`
--- @alias HandParam
--- | `SONIC_HAND_RIGHT`
--- | `SONIC_HAND_LEFT`
--- | `WAPEACH_HAND_AXE`

View file

@ -0,0 +1,10 @@
-- Environment inclusions --
--[[
We only need to include character select rn
]]
charSelect = charSelect
if not charSelect then return end
_ENV = setmetatable(_G, { __index = charSelect })

View file

@ -0,0 +1,31 @@
--- Misc Functions ---
--- @param m MarioState
--- @param name string
--- @param accel? number
--- Plays a custom animation for MarioState `m`
function play_custom_anim(m, name, accel)
accel = accel or 0x10000
m.marioObj.header.gfx.animInfo.animAccel = accel
if (smlua_anim_util_get_current_animation_name(m.marioObj) ~= name or m.marioObj.header.gfx.animInfo.animID ~= -1) then
m.marioObj.header.gfx.animInfo.animID = -1
set_anim_to_frame(m, 0)
end
smlua_anim_util_set_animation(m.marioObj, name)
end
--- @param str string
--- @param splitAt? string
function string.split(str, splitAt)
if splitAt == nil then
splitAt = " "
end
local result = {}
for match in str:gmatch(string.format("[^%s]+", splitAt)) do
table.insert(result, match)
end
return result
end

View file

@ -0,0 +1,148 @@
local colObjLists = { OBJ_LIST_GENACTOR, OBJ_LIST_PUSHABLE, OBJ_LIST_SURFACE, OBJ_LIST_DESTRUCTIVE }
local bhvBlacklist = {
[id_bhvBowser] = true,
[id_bhvDoor] = true,
[id_bhvDoorWarp] = true,
[id_bhvStarDoor] = true,
[id_bhvUnlockDoorStar] = true,
[id_bhvToadMessage] = true,
[id_bhvFireSpitter] = true,
[id_bhvExplosion] = true
}
---@param o Object
---@param o2 Object
local function attack_bounce(o, o2)
o2.oVelY = 15.0
play_sound(SOUND_ACTION_BONK, o2.header.gfx.cameraToObject)
end
---@param o Object
---@param o2 Object
local function attack_bully(o, o2)
o2.oBullyLastNetworkPlayerIndex = o.globalPlayerIndex
o2.oMoveAngleYaw = o.oMoveAngleYaw
o2.oForwardVel = 30.0
o2.oInteractStatus = o2.oInteractStatus | ATTACK_FAST_ATTACK | INT_STATUS_WAS_ATTACKED | INT_STATUS_INTERACTED
end
---@param o Object
---@param o2 Object
local function attack_bully_strong(o, o2)
o2.oBullyLastNetworkPlayerIndex = o.globalPlayerIndex
o2.oMoveAngleYaw = o.oMoveAngleYaw
o2.oForwardVel = 50.0
o2.oVelY = 30.0
o2.oInteractStatus = o2.oInteractStatus | ATTACK_FAST_ATTACK | INT_STATUS_WAS_ATTACKED | INT_STATUS_INTERACTED
end
---@param o Object
---@param o2 Object
local function attack_mrblizzard(o, o2)
if o2.prevObj then
o2.prevObj.oAction = 2
o2.prevObj = nil
o2.oMrBlizzardHeldObj = nil
end
o2.oAction = MR_BLIZZARD_ACT_DEATH
end
---@param o Object
---@param o2 Object
local function attack_bullet_bill(o, o2)
spawn_mist_particles_with_sound(SOUND_GENERAL2_BOBOMB_EXPLOSION)
o2.oAction = 4
o2.oTimer = 0
end
---@param o Object
---@param o2 Object
local function attack_chuckya(o, o2)
o2.oAction = 2
o2.oVelY = 30
o2.oMoveAngleYaw = o.oMoveAngleYaw
o2.oForwardVel = 25
end
---@param o Object
---@param o2 Object
local function attack_whomp(o, o2)
o2.oAction = 8
end
---@param o Object
---@param o2 Object
local function attack_kingbobomb(o, o2)
if o2.oFlags & OBJ_FLAG_HOLDABLE ~= 0 and o2.oAction ~= 8 then
o2.oVelY = 30
o2.oForwardVel = 30
o2.oMoveAngleYaw = o.oMoveAngleYaw
o2.oMoveFlags = 0
o2.oAction = 4
end
end
---@param o Object
---@param o2 Object
local function attack_wooden_post(o, o2)
o2.oWoodenPostMarioPounding = 1
o2.oWoodenPostSpeedY = -100.0
cur_obj_play_sound_2(SOUND_GENERAL_POUND_WOOD_POST)
end
-- lists for edge case interactions
bhvWapeachAxeAttacks = {
[id_bhvSmallBully] = attack_bully_strong,
[id_bhvBigBully] = attack_bully_strong,
[id_bhvBigBullyWithMinions] = attack_bully_strong,
[id_bhvSmallChillBully] = attack_bully_strong,
[id_bhvBigChillBully] = attack_bully_strong,
[id_bhvMrBlizzard] = attack_mrblizzard,
[id_bhvBulletBill] = attack_bullet_bill,
[id_bhvSmallWhomp] = attack_whomp,
[id_bhvChuckya] = attack_chuckya,
[id_bhvWoodenPost] = attack_wooden_post,
}
---@param o Object
---@param spAttacksList table<BehaviorId,function>
---@param getTarget? boolean
function obj_process_attacks(o, spAttacksList, getTarget)
-- players
if o.oInteractType == 0 then
local m = nearest_mario_state_to_object(o)
if m and m.playerIndex == 0 and m.marioObj.globalPlayerIndex ~= o.globalPlayerIndex
and m.action & (ACT_FLAG_INVULNERABLE | ACT_FLAG_INTANGIBLE) == 0 and m.invincTimer == 0
and obj_check_hitbox_overlap(m.marioObj, o) then
if spAttacksList[id_bhvMario] then
spAttacksList[id_bhvMario](o, m)
else
take_damage_and_knock_back(m, o)
end
if getTarget then return m.marioObj end
end
end
-- other objects
for i, list in ipairs(colObjLists) do
local o2 = obj_get_first(list)
while o2 do
if o ~= o2 and o2.oInteractStatus & INT_STATUS_INTERACTED == 0 and o2.oIntangibleTimer == 0 and obj_check_hitbox_overlap(o, o2) then
local bhv = get_id_from_behavior(o2.behavior)
if not bhvBlacklist[bhv] then
if spAttacksList[bhv] then
spAttacksList[bhv](o, o2)
else
o2.oInteractStatus = o2.oInteractStatus | ATTACK_FAST_ATTACK | INT_STATUS_WAS_ATTACKED |
INT_STATUS_INTERACTED
end
if getTarget then return o2 end
end
end
o2 = obj_get_next(o2)
end
end
end

View file

@ -0,0 +1,52 @@
--- Vars that all movesets use --
--- @type CharacterState[]
gCharacterStates = {}
for i = 0, (MAX_PLAYERS - 1) do
gCharacterStates[i] = {}
local m = gMarioStates[i]
local e = gCharacterStates[i]
e.mario = m
e.luigi = {}
e.toad = {}
e.wario = {}
e.waluigi = {}
e.toadette = {}
e.peach = {}
e.daisy = {}
e.yoshi = {}
e.birdo = {}
e.spike = {}
e.pauline = {}
e.rosalina = {}
e.wapeach = {}
e.donkeyKong = {}
e.sonic = {}
e.toadette.averageForwardVel = 0
e.birdo.spitTimer = 0
e.birdo.framesSinceShoot = 255
e.birdo.flameCharge = 0
e.rosalina.canSpin = true
e.rosalina.orbitObjActive = false
e.rosalina.orbitObjDist = 0
e.rosalina.orbitObjAngle = 0
e.sonic.spinCharge = 0
e.sonic.groundYVel = 0
e.sonic.prevForwardVel = 0
e.sonic.peakHeight = 0
e.sonic.actionADone = false
e.sonic.actionBDone = false
e.sonic.bounced = false
e.sonic.spindashState = 0
e.sonic.instashieldTimer = 0
e.sonic.oxygen = 900 -- 30 seconds
e.sonic.prevVelY = 0
e.sonic.prevHeight = 0
e.sonic.physTimer = 0
e.sonic.lastforwardPos = gVec3fZero()
e.sonic.realFVel = 0
end

Binary file not shown.

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,843 @@
-------------------
-- Birdo Moveset --
-------------------
if not charSelect then return end
local SOUND_SPIT = audio_sample_load("z_sfx_birdo_spit.ogg") -- Load audio sample
---------------
-- Birdo Egg --
---------------
_G.ACT_BIRDO_HOLD_WALKING = allocate_mario_action(ACT_FLAG_MOVING | ACT_GROUP_OBJECT)
_G.ACT_SPIT_EGG = allocate_mario_action(ACT_FLAG_STATIONARY | ACT_FLAG_IDLE | ACT_FLAG_ALLOW_FIRST_PERSON | ACT_FLAG_PAUSE_EXIT)
_G.ACT_SPIT_EGG_WALK = allocate_mario_action(ACT_FLAG_MOVING | ACT_FLAG_ALLOW_FIRST_PERSON)
_G.ACT_SPIT_EGG_AIR = allocate_mario_action(ACT_FLAG_AIR | ACT_FLAG_ALLOW_VERTICAL_WIND_ACTION | ACT_FLAG_CONTROL_JUMP_HEIGHT)
--- @param m MarioState
local function act_birdo_hold_walking(m)
local startYaw = m.faceAngle.y
if m.heldObj and m.heldObj.behavior == get_behavior_from_id(id_bhvJumpingBox) then
return set_mario_action(m, ACT_CRAZY_BOX_BOUNCE, 0)
end
if (m.marioObj.oInteractStatus & INT_STATUS_MARIO_DROP_OBJECT) ~= 0 then
return drop_and_set_mario_action(m, ACT_WALKING, 0)
end
if (should_begin_sliding(m)) ~= 0 then
return set_mario_action(m, ACT_HOLD_BEGIN_SLIDING, 0)
end
if (m.input & INPUT_B_PRESSED) ~= 0 then
return set_mario_action(m, ACT_THROWING, 0)
end
if (m.input & INPUT_A_PRESSED) ~= 0 then
return set_jumping_action(m, ACT_HOLD_JUMP, 0)
end
if (m.input & INPUT_ZERO_MOVEMENT) ~= 0 then
return set_mario_action(m, ACT_HOLD_DECELERATING, 0)
end
if (m.input & INPUT_Z_PRESSED) ~= 0 then
return drop_and_set_mario_action(m, ACT_CROUCH_SLIDE, 0)
end
update_walking_speed(m) -- normal walking speed
local result = perform_ground_step(m)
if result == GROUND_STEP_LEFT_GROUND then
set_mario_action(m, ACT_HOLD_FREEFALL, 0)
elseif result == GROUND_STEP_HIT_WALL then
if (m.forwardVel > 16) then
mario_set_forward_vel(m, 16)
end
end
-- for the animation, temporarily read birdo's speed as lower so it looks less goofy
local prevForwardVel = m.forwardVel
local prevMag = m.intendedMag
m.forwardVel = m.forwardVel * 0.6
m.intendedMag = m.intendedMag * 0.6
anim_and_audio_for_hold_walk(m)
m.forwardVel = prevForwardVel
m.intendedMag = prevMag
-- tilt body
local dYaw = m.faceAngle.y - startYaw
local val02 = -(dYaw * m.forwardVel / 12)
local val00 = (m.forwardVel * 170)
val02 = math.clamp(val02, -0x1555, 0x1555)
val00 = math.clamp(val00, 0x0, 0x1555)
m.marioBodyState.allowPartRotation = true
m.marioBodyState.torsoAngle.z = approach_s32(m.marioBodyState.torsoAngle.z, val02, 0x400, 0x400)
m.marioBodyState.torsoAngle.x = approach_s32(m.marioBodyState.torsoAngle.x, val00, 0x400, 0x400)
if (0.4 * m.intendedMag - m.forwardVel > 10) then
set_mario_particle_flags(m, PARTICLE_DUST, 0)
end
return 0
end
--- @param m MarioState
local function act_spit_egg(m)
local e = gCharacterStates[m.playerIndex]
if (m.quicksandDepth > 30) then
return set_mario_action(m, ACT_IN_QUICKSAND, 0)
end
if m.actionState == 0 then
play_custom_anim(m, "BIRDO_ANIM_IDLE_TO_AIM_IDLE")
if is_anim_past_end(m) ~= 0 then
m.actionState = 1
end
elseif e.birdo.flameCharge == 0 and e.birdo.framesSinceShoot > 10 then
play_custom_anim(m, "BIRDO_ANIM_AIM_IDLE_TO_IDLE")
if is_anim_past_end(m) ~= 0 then
return set_mario_action(m, ACT_IDLE, 0)
end
else
play_custom_anim(m, "BIRDO_ANIM_AIM_IDLE")
end
mario_drop_held_object(m)
m.actionTimer = m.actionTimer + 1
local oldActTimer = m.actionTimer
if (m.input & INPUT_NONZERO_ANALOG) ~= 0 then
mario_set_forward_vel(m, 0)
local result = set_mario_action(m, ACT_SPIT_EGG_WALK, 0)
m.actionTimer = oldActTimer
return result
elseif (check_common_idle_cancels(m) ~= 0) then
if m.action & ACT_FLAG_AIR ~= 0 then
mario_set_forward_vel(m, 0)
set_mario_action(m, ACT_SPIT_EGG_AIR, 1)
if m.vel.y <= 0 then
m.actionArg = 0
end
m.actionTimer = oldActTimer
end
return 1
end
mario_set_forward_vel(m, 0)
perform_ground_step(m)
return 0
end
--- @param m MarioState
local function act_spit_egg_walk(m)
local e = gCharacterStates[m.playerIndex]
local mBody = m.marioBodyState
mario_drop_held_object(m)
m.actionTimer = m.actionTimer + 1
if e.birdo.flameCharge == 0 and e.birdo.framesSinceShoot > 10 then
if m.forwardVel < 0 then
m.forwardVel = m.intendedMag
m.faceAngle.y = m.intendedYaw
return set_mario_action(m, ACT_FINISH_TURNING_AROUND, 0)
end
m.forwardVel = m.intendedMag
m.faceAngle.y = m.intendedYaw
return set_mario_action(m, ACT_WALKING, 0)
end
if mario_floor_is_slippery(m) ~= 0 then
return set_mario_action(m, ACT_WALKING, 0)
end
if (should_begin_sliding(m)) ~= 0 then
return set_mario_action(m, ACT_BEGIN_SLIDING, 0)
end
if (m.input & INPUT_FIRST_PERSON) ~= 0 then
m.intendedMag = 0
if m.slideVelX == 0 and m.slideVelZ == 0 then
return begin_braking_action(m)
end
end
if (m.input & INPUT_ZERO_MOVEMENT) ~= 0 and m.slideVelX == 0 and m.slideVelZ == 0 then
local oldActTimer = m.actionTimer
local result = set_mario_action(m, ACT_SPIT_EGG, 0)
m.actionTimer = oldActTimer
return result
end
if (m.input & INPUT_Z_PRESSED) ~= 0 then
return set_mario_action(m, ACT_CROUCH_SLIDE, 0)
end
-- strafe movement
local newVelX = sins(m.intendedYaw) * m.intendedMag
local newVelZ = coss(m.intendedYaw) * m.intendedMag
m.slideVelX = approach_f32(m.slideVelX, newVelX, 4, 4)
m.slideVelZ = approach_f32(m.slideVelZ, newVelZ, 4, 4)
m.vel.x, m.vel.z = m.slideVelX, m.slideVelZ
m.forwardVel = math.sqrt(m.vel.x ^ 2 + m.vel.z ^ 2)
if (m.input & INPUT_A_PRESSED) ~= 0 then
set_mario_y_vel_based_on_fspeed(m, 42, 0.25)
m.slideVelX = m.slideVelX * 0.8
m.slideVelZ = m.slideVelZ * 0.8
m.vel.x, m.vel.z = m.slideVelX, m.slideVelZ
m.forwardVel = m.forwardVel * 0.8
local oldActTimer = m.actionTimer
local result = set_mario_action(m, ACT_SPIT_EGG_AIR, 1)
m.actionTimer = oldActTimer
return result
end
local result = (perform_ground_step(m))
if result == GROUND_STEP_LEFT_GROUND then
m.vel.y = 0
local oldActTimer = m.actionTimer
set_mario_action(m, ACT_SPIT_EGG_AIR, 0)
m.actionTimer = oldActTimer
--set_character_animation(m, CHAR_ANIM_GENERAL_FALL)
elseif result == GROUND_STEP_NONE then
--anim_and_audio_for_walk(m)
play_step_sound(m, 10, 49)
local dYaw = math.s16(m.faceAngle.y - m.intendedYaw)
play_custom_anim(m, "BIRDO_ANIM_AIM_WALK", m.forwardVel / 4 * 0x10000)
mBody.allowPartRotation = true
m.marioObj.header.gfx.angle.y = m.intendedYaw
local marioAnimInfo = m.marioObj.header.gfx.animInfo
if math.abs(dYaw) > 0x4000 then
m.marioObj.header.gfx.angle.y = m.intendedYaw - 0x8000
marioAnimInfo.animAccel = -math.abs(marioAnimInfo.animAccel)
else
marioAnimInfo.animAccel = math.abs(marioAnimInfo.animAccel)
end
-- Handle manually the loop points of the animation if moving backwards
if marioAnimInfo.animAccel < 0 and marioAnimInfo.animFrame <= marioAnimInfo.curAnim.loopStart then
marioAnimInfo.animFrame = marioAnimInfo.curAnim.loopEnd
marioAnimInfo.animFrameAccelAssist = marioAnimInfo.animFrame << 16
end
mBody.torsoAngle.y = math.s16(m.faceAngle.y - m.marioObj.header.gfx.angle.y) * 0.4
mBody.headAngle.y = m.faceAngle.y - m.marioObj.header.gfx.angle.y - mBody.torsoAngle.y
if m.intendedMag - m.forwardVel > 16 then
set_mario_particle_flags(m, PARTICLE_DUST, 0)
end
end
check_ledge_climb_down(m)
return 0
end
---@param m MarioState
local function act_spit_egg_air(m)
local e = gCharacterStates[m.playerIndex]
play_custom_anim(m, "BIRDO_ANIM_AIM_JUMP")
if m.actionArg ~= 1 then
set_anim_to_frame(m, m.marioObj.header.gfx.animInfo.curAnim.loopEnd)
else
play_mario_sound(m, SOUND_ACTION_TERRAIN_JUMP, 0)
end
m.actionTimer = m.actionTimer + 1
if (m.input & INPUT_Z_PRESSED) ~= 0 then
return set_mario_action(m, ACT_GROUND_POUND, 0)
end
-- air strafe
local newVelX = sins(m.intendedYaw) * m.intendedMag
local newVelZ = coss(m.intendedYaw) * m.intendedMag
m.slideVelX = approach_f32(m.slideVelX, newVelX, 1, 1)
m.slideVelZ = approach_f32(m.slideVelZ, newVelZ, 1, 1)
m.vel.x, m.vel.z = m.slideVelX, m.slideVelZ
m.forwardVel = m.slideVelX * sins(m.faceAngle.y) + m.slideVelZ * coss(m.faceAngle.y)
--local absSpeed = math.max(math.abs(m.slideVelX), math.abs(m.slideVelZ))
local result = (perform_air_step(m, 0))
if result == AIR_STEP_LANDED then
if check_fall_damage_or_get_stuck(m, ACT_HARD_BACKWARD_GROUND_KB) ~= 0 then
return 1
elseif e.birdo.flameCharge == 0 and e.birdo.framesSinceShoot > 10 then
set_mario_action(m, ACT_FREEFALL_LAND, 0)
else
local oldActTimer = m.actionTimer
set_mario_action(m, ACT_SPIT_EGG_WALK, 0)
m.actionTimer = oldActTimer
end
return 1
elseif result == AIR_STEP_HIT_WALL then
mario_set_forward_vel(m, 0)
elseif result == AIR_STEP_HIT_LAVA_WALL then
lava_boost_on_wall(m)
end
return 0
end
-- Egg
local eggIntObjLists = {
OBJ_LIST_GENACTOR,
OBJ_LIST_PUSHABLE,
OBJ_LIST_SURFACE,
OBJ_LIST_PLAYER,
}
E_MODEL_EGG = smlua_model_util_get_id("egg_geo")
---@param o Object
function bhv_birdo_egg_init(o)
o.oFlags = (OBJ_FLAG_UPDATE_GFX_POS_AND_ANGLE | OBJ_FLAG_SET_FACE_YAW_TO_MOVE_YAW | OBJ_FLAG_HOLDABLE | OBJ_FLAG_COMPUTE_DIST_TO_MARIO)
o.oFaceAngleRoll = 0
o.oMoveAngleRoll = 0
o.oGravity = 0
o.oBounciness = 0
o.oFriction = 1
o.oDragStrength = 0
o.oBuoyancy = 0
o.oWallHitboxRadius = 60
o.oVelY = 0
o.collisionData = smlua_collision_util_get("egg_collision")
local hitbox = get_temp_object_hitbox()
hitbox.interactType = INTERACT_DAMAGE
hitbox.hurtboxRadius = 60
hitbox.hurtboxHeight = 80
hitbox.downOffset = 80
hitbox.radius = 60
hitbox.height = 80
hitbox.damageOrCoinValue = 1
if o.oBehParams ~= 0 then
-- similar hitbox to fire spitter flames
hitbox.interactType = INTERACT_FLAME
hitbox.radius = 10
hitbox.height = 40
hitbox.hurtboxRadius = 10
hitbox.hurtboxHeight = 40
hitbox.downOffset = 30
obj_set_billboard(o)
o.header.gfx.scale.x = 3
o.header.gfx.scale.y = 3
o.header.gfx.scale.z = 3
end
obj_set_hitbox(o, hitbox)
o.oIntangibleTimer = 10
-- do manual shadow, otherwise the shadow renders on top of itself
o.header.gfx.disableAutomaticShadowPos = true
o.header.gfx.shadowPos.x = o.oPosX
o.header.gfx.shadowPos.y = o.oPosY - 50
o.header.gfx.shadowPos.z = o.oPosZ
network_init_object(o, true, { 'globalPlayerIndex' })
end
---@param o Object
function bhv_birdo_egg_loop(o)
if o.oBehParams ~= 0 then
o.oAnimState = o.oAnimState + 1
end
if o.oHeldState == HELD_FREE then
cur_obj_enable_rendering()
if o.oAction == 0 then
o.oGravity = 0
else
o.oMoveAnglePitch = 0
o.oFaceAnglePitch = 0
o.oGravity = -2
end
cur_obj_update_floor_and_walls()
local oldForwardVel = o.oForwardVel
if o.oAction == 0 then
obj_compute_vel_from_move_pitch(o.oForwardVel)
end
cur_obj_move_standard(60)
o.oForwardVel = oldForwardVel
local defaultVel = 20
if o.oBehParams ~= 0 then
defaultVel = 40
end
if o.oAction == 0 and o.oForwardVel > defaultVel then
o.oForwardVel = approach_f32(o.oForwardVel, defaultVel, 3, 3)
end
-- manual object collision
local dieFromCollision = false
o.numCollidedObjs = obj_attack_collided_from_other_object(o)
if o.numCollidedObjs ~= 0 and o.oBehParams == 0 then
dieFromCollision = true
end
if o.oDistanceToMario < 2000 then
for _, list in ipairs(eggIntObjLists) do
local o2 = obj_get_first(list)
while o2 and o.numCollidedObjs < 4 do
if o ~= o2 then
if list ~= OBJ_LIST_PLAYER and o2.oHeldState == HELD_FREE and detect_object_hitbox_overlap(o, o2) ~= 0 then
o2.numCollidedObjs = o2.numCollidedObjs - 1 -- prevent game crash
local doEggInteract = birdo_egg_interaction(o2, o)
if o.oBehParams == 0 or doEggInteract then
dieFromCollision = true
end
if doEggInteract or o2.oInteractType == INTERACT_BREAKABLE or obj_is_attackable(o2) then
if obj_has_behavior_id(o2, id_bhvBowser) == 0 then
o2.oInteractStatus = o2.oInteractStatus | ATTACK_PUNCH | INT_STATUS_WAS_ATTACKED |
INT_STATUS_INTERACTED | INT_STATUS_TOUCHED_BOB_OMB
end
end
elseif o.oBehParams ~= 0 and birdo_fire_is_targettable(o2, o) and dist_between_objects(o2, o) <= 700 then
local angleToObject = obj_angle_to_object(o, o2)
if abs_angle_diff(o.oMoveAngleYaw, angleToObject) <= 0x4000 then
cur_obj_rotate_yaw_toward(angleToObject, 0x200)
end
end
end
o2 = obj_get_next(o2)
end
if o.numCollidedObjs >= 4 then break end
end
end
-- surface collision
if o.oAction == 0 and o.oBehParams == 0 and o.oMoveFlags & OBJ_MOVE_MASK_IN_WATER == 0 then
local m0 = gMarioStates[0]
load_object_collision_model()
if cur_obj_is_mario_on_platform() ~= 0 then
if (m0.action == ACT_PUNCHING or m0.action == ACT_MOVE_PUNCHING) then
-- pick up egg
m0.heldObj = o
m0.marioBodyState.grabPos = GRAB_POS_LIGHT_OBJ
o.heldByPlayerIndex = 0
o.oHeldState = HELD_HELD
set_mario_action(m0, ACT_HOLD_FREEFALL, 0)
if (o.oSyncID ~= 0) then network_send_object(o, false) end
elseif (m0.prevAction & ACT_FLAG_AIR) ~= 0 then -- prevent falling off of egg easily
m0.pos.x = o.oPosX
m0.pos.z = o.oPosZ
end
end
end
if dieFromCollision or o.oMoveFlags & (OBJ_MOVE_HIT_WALL | OBJ_MOVE_UNDERWATER_ON_GROUND | OBJ_MOVE_MASK_ON_GROUND) ~= 0 or o.oTimer > 120 then
o.numCollidedObjs = 0
spawn_mist_particles()
obj_mark_for_deletion(o)
end
o.oInteractStatus = 0
o.numCollidedObjs = 0
elseif o.oHeldState == HELD_HELD then
o.oFaceAnglePitch = 0
o.oMoveAnglePitch = 0
o.oInteractType = INTERACT_GRABBABLE
cur_obj_disable_rendering_and_become_intangible(o)
elseif o.oHeldState == HELD_THROWN then
o.oFaceAnglePitch = 0
o.oMoveAnglePitch = 0
o.oInteractType = INTERACT_DAMAGE
cur_obj_enable_rendering_and_become_tangible(o)
cur_obj_change_action(1)
local m = gMarioStates[o.heldByPlayerIndex]
o.oForwardVel = math.max(m.forwardVel + 15, 40)
o.oVelY = 10
o.oTimer = 0
o.oHeldState = HELD_FREE
o.oIntangibleTimer = 10
elseif o.oHeldState == HELD_DROPPED then
spawn_mist_particles()
obj_mark_for_deletion(o)
end
-- do manual shadow, otherwise the shadow renders on top of itself
if o.activeFlags ~= ACTIVE_FLAG_DEACTIVATED then
o.header.gfx.disableAutomaticShadowPos = true
o.header.gfx.shadowPos.x = o.oPosX
o.header.gfx.shadowPos.y = o.oPosY - 50
o.header.gfx.shadowPos.z = o.oPosZ
else
o.header.gfx.disableAutomaticShadowPos = false
end
end
-- lua recreation
---@param a Object
---@param b Object
function detect_object_hitbox_overlap(a, b)
if not (a and b) then return 0 end
local sp3C = a.oPosY - a.hitboxDownOffset
local sp38 = b.oPosY - b.hitboxDownOffset
local dx = a.oPosX - b.oPosX
local dz = a.oPosZ - b.oPosZ
local collisionRadius = a.hitboxRadius + b.hitboxRadius
local distance = math.floor(math.sqrt(dx * dx + dz * dz))
-- do not check for player interactions here
if ((a.oInteractType & INTERACT_PLAYER) ~= 0 and (b.oInteractType & INTERACT_PLAYER) ~= 0) then
return 0
end
if (collisionRadius > distance) then
local sp20 = a.hitboxHeight + sp3C
local sp1C = b.hitboxHeight + sp38
if (sp3C > sp1C) then
return 0
end
if (sp20 < sp38) then
return 0
end
if (a.numCollidedObjs >= 4) then
return 0
end
if (b.numCollidedObjs >= 4) then
return 0
end
-- can't reference these fields in lua
--a.collidedObjs[a.numCollidedObjs] = b
--b.collidedObjs[b.numCollidedObjs] = a
a.collidedObjInteractTypes = a.collidedObjInteractTypes | b.oInteractType
b.collidedObjInteractTypes = b.collidedObjInteractTypes | a.oInteractType
a.numCollidedObjs = a.numCollidedObjs + 1
b.numCollidedObjs = b.numCollidedObjs + 1
return 1
end
return 0
end
id_bhvBirdoEgg = hook_behavior(nil, OBJ_LIST_SURFACE, true, bhv_birdo_egg_init, bhv_birdo_egg_loop, "bhvBirdoEgg")
---@param m MarioState
function birdo_update(m)
-- spit egg
local e = gCharacterStates[m.playerIndex]
local inSpitAction = (m.action == ACT_SPIT_EGG or m.action == ACT_SPIT_EGG_WALK or m.action == ACT_SPIT_EGG_AIR or m.action == ACT_FIRST_PERSON or m.action == ACT_WATER_PUNCH or m.action == ACT_FLYING)
local headRot = m.marioBodyState.headAngle
if m.controller.buttonPressed & B_BUTTON ~= 0 and inSpitAction then
-- when mashing B, stay in spit action
e.birdo.framesSinceShoot = 0
if e.birdo.spitTimer == 0 then
e.birdo.flameCharge = 0
end
else
-- handle shooting repeatedly/charging
if e.birdo.framesSinceShoot ~= 255 then
e.birdo.framesSinceShoot = e.birdo.framesSinceShoot + 1
end
if m.controller.buttonDown & B_BUTTON ~= 0 then
if inSpitAction then
e.birdo.flameCharge = e.birdo.flameCharge + 1
end
elseif e.birdo.spitTimer < 25 then
if e.birdo.flameCharge >= 30 then
e.birdo.framesSinceShoot = 0 -- shoot fireball
else
e.birdo.flameCharge = 0
end
end
end
if (e.birdo.framesSinceShoot <= 10 or e.birdo.flameCharge ~= 0) and not m.heldObj and inSpitAction then
local canShoot = true
local eggCount = 0
local gIndex = network_global_index_from_local(m.playerIndex)
local egg = obj_get_first_with_behavior_id(id_bhvBirdoEgg)
while egg do
if egg.oAction == 0 and egg.oHeldState == HELD_FREE and egg.globalPlayerIndex == gIndex then
eggCount = eggCount + 1
if eggCount >= 3 then -- max of 3 eggs/fireballs per player
canShoot = false
break
end
end
egg = obj_get_next_with_same_behavior_id(egg)
end
if e.birdo.spitTimer ~= 0 then
e.birdo.spitTimer = e.birdo.spitTimer - 1
m.marioBodyState.allowPartRotation = true
if e.birdo.spitTimer > 24 then
headRot.x = approach_f32(headRot.x, degrees_to_sm64(-30), degrees_to_sm64(10), degrees_to_sm64(10))
else
headRot.x = approach_f32(headRot.x, degrees_to_sm64(0), degrees_to_sm64(3.5), degrees_to_sm64(3.5))
end
end
if e.birdo.spitTimer == 0 and canShoot and e.birdo.framesSinceShoot <= 10 then
m.actionTimer = 0
m.actionArg = 0
end
local mouthPos = gVec3fZero()
local yaw = m.faceAngle.y
local pitch = 0
if canShoot then
-- when swimming, flying, or in first person, allow shooting in any direction
if m.action == ACT_FIRST_PERSON then
yaw = m.statusForCamera.headRotation.y + yaw
pitch = m.statusForCamera.headRotation.x
mouthPos.x = m.pos.x + sins(yaw) * 60 * coss(pitch)
mouthPos.y = m.pos.y + 120 - sins(pitch) * 120
mouthPos.z = m.pos.z + coss(yaw) * 60 * coss(pitch)
elseif m.action & ACT_FLAG_SWIMMING_OR_FLYING ~= 0 then
pitch = -m.faceAngle.x
if pitch < 0 then
mouthPos.x = m.pos.x + sins(yaw) * 80 * coss(pitch)
mouthPos.y = m.pos.y + 120
mouthPos.z = m.pos.z + coss(yaw) * 80 * coss(pitch)
else
mouthPos.x = m.pos.x + sins(yaw) * 80
mouthPos.y = m.pos.y + 120 - sins(pitch) * 150
mouthPos.z = m.pos.z + coss(yaw) * 80
end
else
mouthPos.x = m.marioBodyState.headPos.x + sins(yaw + m.marioBodyState.headAngle.y) * 60
mouthPos.y = m.marioBodyState.headPos.y + 20
mouthPos.z = m.marioBodyState.headPos.z + coss(yaw + m.marioBodyState.headAngle.y) * 60
end
end
if canShoot and e.birdo.spitTimer == 0 and e.birdo.flameCharge >= 30 and m.action & ACT_FLAG_SWIMMING == 0 then
spawn_non_sync_object(id_bhvKoopaShellFlame, E_MODEL_RED_FLAME,
mouthPos.x,
mouthPos.y,
mouthPos.z,
function(o)
o.oKoopaShellFlameUnkF8 = 2
o.oMoveAngleYaw = math.random(0, 0xFFFF)
o.oVelY = math.random(10)
o.oAnimState = math.random(10)
o.oGravity = -4.0
o.oTimer = 1
o.oForwardVel = math.random(10)
end)
play_sound(SOUND_AIR_BLOW_FIRE, m.marioObj.header.gfx.cameraToObject)
end
if canShoot and e.birdo.spitTimer == 0 and e.birdo.framesSinceShoot <= 10 then
e.birdo.spitTimer = 30
elseif e.birdo.spitTimer == 25 then
local model = E_MODEL_EGG
local isFireball = (e.birdo.flameCharge >= 30)
if isFireball then
model = E_MODEL_RED_FLAME
e.birdo.flameCharge = 0
end
if not isFireball then
audio_sample_play(SOUND_SPIT, m.pos, 1) -- Play audio sample
else
play_sound(SOUND_AIR_BOWSER_SPIT_FIRE, m.marioObj.header.gfx.cameraToObject)
end
if m.playerIndex == 0 then
local eggVel = m.forwardVel * 2 + 25
-- add double floor velocity to prevent being able to platform on eggs forever
if m.floor and m.floor.object and m.floor.object.oForwardVel ~= 0 then
eggVel = eggVel + m.floor.object.oForwardVel * 2
end
spawn_sync_object(id_bhvBirdoEgg, model, mouthPos.x + sins(yaw) * 40 * coss(pitch), mouthPos.y,
mouthPos.z + coss(yaw) * 40 * coss(pitch), function(o)
o.oForwardVel = math.max(eggVel, 40)
o.oMoveAngleYaw = yaw
o.oFaceAnglePitch = pitch
o.oMoveAnglePitch = pitch
o.oIntangibleTimer = 100
o.globalPlayerIndex = gIndex
o.oBehParams = (isFireball and 1) or 0
spawn_mist_particles_variable(20, 120, 5)
end)
end
end
elseif e.birdo.spitTimer ~= 0 then
e.birdo.spitTimer = e.birdo.spitTimer - 1
m.marioBodyState.allowPartRotation = true
if e.birdo.spitTimer > 24 then
headRot.x = approach_f32(headRot.x, degrees_to_sm64(-30), degrees_to_sm64(10), degrees_to_sm64(10))
else
headRot.x = approach_f32(headRot.x, degrees_to_sm64(0), degrees_to_sm64(3.5), degrees_to_sm64(3.5))
end
end
-- throw objects instantly
if m.action == ACT_THROWING then
if m.actionTimer < 6 then
m.actionTimer = 6
set_anim_to_frame(m, 6)
end
elseif m.action == ACT_AIR_THROW or m.action == ACT_AIR_THROW_LAND then
if m.actionTimer < 3 then
m.actionTimer = 3
set_anim_to_frame(m, 3)
end
end
end
function birdo_on_set_action(m)
if m.action ~= ACT_SPIT_EGG and m.action ~= ACT_SPIT_EGG_WALK and m.action ~= ACT_SPIT_EGG_AIR then
gCharacterStates[m.playerIndex].birdo.spitTimer = 0
end
if m.action == ACT_HOLD_WALKING then -- switch to custom hold action
set_mario_action(m, ACT_BIRDO_HOLD_WALKING, 0)
end
end
local shootActs = {
[ACT_PUNCHING] = ACT_SPIT_EGG,
[ACT_MOVE_PUNCHING] = ACT_SPIT_EGG_WALK,
[ACT_JUMP_KICK] = ACT_SPIT_EGG_AIR,
}
function birdo_before_action(m, action, actionArg)
if m.playerIndex ~= 0 then return end
if shootActs[action] and m.controller.buttonDown & A_BUTTON == 0 then
if action == ACT_PUNCHING and actionArg == 9 then return end
local e = gCharacterStates[m.playerIndex]
e.birdo.framesSinceShoot = 0
if e.birdo.spitTimer == 0 then
e.birdo.flameCharge = 0
end
local canShoot = true
local eggCount = 0
local gIndex = network_global_index_from_local(m.playerIndex)
local egg = obj_get_first_with_behavior_id(id_bhvBirdoEgg)
while egg do
if egg.oAction == 0 and egg.oHeldState == HELD_FREE and egg.globalPlayerIndex == gIndex then
eggCount = eggCount + 1
if eggCount >= 3 then -- max of 3 eggs/fireballs per player
canShoot = false
break
end
end
egg = obj_get_next_with_same_behavior_id(egg)
end
if m.action ~= ACT_SPIT_EGG or e.birdo.spitTimer == 0 or canShoot then
m.marioObj.header.gfx.animInfo.animFrame = 0
return shootActs[action]
end
end
end
function birdo_on_interact(m, o, intType)
local e = gCharacterStates[m.playerIndex]
if intType == INTERACT_GRABBABLE and e.birdo.framesSinceShoot == 0 and e.birdo.flameCharge == 0 and (m.action == ACT_SPIT_EGG or m.action == ACT_SPIT_EGG_WALK) and o.oInteractionSubtype & INT_SUBTYPE_NOT_GRABBABLE == 0 then
set_mario_action(m, ACT_MOVE_PUNCHING, 1)
return
end
end
function birdo_before_phys_step(m)
local hScale = 1.0
local vScale = 1.0
-- faster ground movement and slower, floaty air movement
if (m.action & ACT_FLAG_MOVING) ~= 0 and m.action ~= ACT_BUBBLED then
hScale = hScale * 1.12 -- not as fast as toad
elseif m.action & ACT_FLAG_AIR ~= 0 then
hScale = hScale * 0.94
if m.vel.y < 0 then
vScale = vScale * 0.98
end
end
m.vel.x = m.vel.x * hScale
m.vel.y = m.vel.y * vScale
m.vel.z = m.vel.z * hScale
end
-- allow shooting in first person
function birdo_before_update(m)
if m.action == ACT_FIRST_PERSON and m.controller.buttonPressed & B_BUTTON ~= 0 then
local e = gCharacterStates[m.playerIndex]
e.birdo.framesSinceShoot = 0
if e.birdo.spitTimer == 0 then
e.birdo.flameCharge = 0
end
m.controller.buttonPressed = m.controller.buttonPressed & ~B_BUTTON
end
end
-- interactions for birdo's egg/fireball
function birdo_egg_interaction(o, egg)
if egg.oBehParams ~= 0 and obj_has_behavior_id(o, id_bhvMrBlizzard) ~= 0 then
o.oFaceAngleRoll = 0x3000
o.oMrBlizzardHeldObj = nil
o.prevObj = o.oMrBlizzardHeldObj
o.oAction = MR_BLIZZARD_ACT_DEATH
o.oMrBlizzardDizziness = 0
o.oMrBlizzardChangeInDizziness = 0
o.oTimer = 30
return true
end
if egg.oBehParams ~= 0 and obj_has_behavior_id(o, id_bhvBowser) ~= 0 then
if o.oAction ~= 4 and o.oAction ~= 5 and o.oAction ~= 6 and o.oAction ~= 12 and o.oAction ~= 19 and o.oAction ~= 20 and math.abs(o.oVelY) <= 2 then
o.oAction = 1
end
return true
end
if o.oInteractType == INTERACT_BULLY then
o.oBullyLastNetworkPlayerIndex = egg.globalPlayerIndex
o.oForwardVel = (egg.oBehParams ~= 0 and 50) or 25
o.oMoveAngleYaw = egg.oMoveAngleYaw
return true
end
end
-- prevent player interaction with Birdo's egg if player interaction is not pvp (owner still interacts)
---@param m MarioState
---@param o Object
---@param type integer
function player_egg_allow_interact(m, o, type)
if obj_has_behavior_id(o, id_bhvBirdoEgg) ~= 0 then
local m2 = gMarioStates[network_local_index_from_global(o.globalPlayerIndex)]
if m.playerIndex ~= m2.playerIndex and gServerSettings.playerInteractions ~= PLAYER_INTERACTIONS_PVP then
return false
end
end
end
hook_event(HOOK_ALLOW_INTERACT, player_egg_allow_interact)
-- returns true if this object can be hit by birdo's fireball
function birdo_fire_is_targettable(o, egg)
if o.oInteractType == INTERACT_PLAYER then
local m = gMarioStates[o.oBehParams - 1]
if (not m) or is_player_active(m) == 0 then return false end
local gIndex = network_global_index_from_local(m.playerIndex)
return (gServerSettings.playerInteractions == PLAYER_INTERACTIONS_PVP) and (egg.globalPlayerIndex ~= gIndex)
end
return (obj_has_behavior_id(o, id_bhvMrBlizzard) ~= 0 or obj_has_behavior_id(o, id_bhvBowser) ~= 0
or o.oInteractType == INTERACT_BULLY or o.oInteractType == INTERACT_BREAKABLE or obj_is_attackable(o))
end
hook_mario_action(ACT_BIRDO_HOLD_WALKING, act_birdo_hold_walking)
hook_mario_action(ACT_SPIT_EGG, act_spit_egg)
hook_mario_action(ACT_SPIT_EGG_AIR, act_spit_egg_air)
hook_mario_action(ACT_SPIT_EGG_WALK, act_spit_egg_walk)
-- Fix object shadows getting messed up. Base coop bug
---@param o Object
function on_obj_load(o)
o.header.gfx.disableAutomaticShadowPos = false
o.header.gfx.shadowInvisible = false
end
hook_event(HOOK_ON_OBJECT_LOAD, on_obj_load)

View file

@ -0,0 +1,55 @@
-------------------
-- Daisy Moveset --
-------------------
if not charSelect then return end
local midairJumpActs = {
[ACT_JUMP] = true,
[ACT_DOUBLE_JUMP] = true,
[ACT_TRIPLE_JUMP] = true,
[ACT_LONG_JUMP] = true,
[ACT_BACKFLIP] = true,
[ACT_SIDE_FLIP] = true,
[ACT_WALL_KICK_AIR] = true,
}
_G.ACT_MIDAIR_JUMP = allocate_mario_action(ACT_GROUP_AIRBORNE | ACT_FLAG_ALLOW_VERTICAL_WIND_ACTION | ACT_FLAG_MOVING)
--- @param m MarioState
local function act_midair_jump(m)
-- apply movement when using action
common_air_action_step(m, ACT_JUMP_LAND, CHAR_ANIM_BEND_KNESS_RIDING_SHELL, AIR_STEP_NONE)
-- setup when action starts (vertical speed and voiceline)
if m.actionTimer == 0 then
m.vel.y = m.forwardVel * 0.3 + 40
m.forwardVel = m.forwardVel * 0.7
play_character_sound(m, CHAR_SOUND_HELLO)
end
set_mario_particle_flags(m, PARTICLE_LEAF, 0)
-- avoid issue with flying and then make the hover end after 2 secs or when stopping holding the button
if m.prevAction ~= ACT_TRIPLE_JUMP and (m.flags & MARIO_WING_CAP) ~= 0 then
if m.actionTimer >= 10 or (m.controller.buttonDown & A_BUTTON) == 0 then
set_mario_action(m, ACT_FREEFALL, 0)
end
else
if m.actionTimer >= 10 or (m.controller.buttonDown & A_BUTTON) == 0 then
set_mario_action(m, ACT_FREEFALL, 0)
end
end
-- increment the action timer to make the hover stop
m.actionTimer = m.actionTimer + 1
end
--- @param m MarioState
function daisy_update(m)
if (m.input & INPUT_A_PRESSED) ~= 0 and m.vel.y < 10 and m.prevAction ~= ACT_MIDAIR_JUMP and midairJumpActs[m.action] then
set_mario_action(m, ACT_MIDAIR_JUMP, 0)
end
end
hook_mario_action(ACT_MIDAIR_JUMP, act_midair_jump)

View file

@ -0,0 +1,955 @@
-------------------------
-- Donkey Kong Moveset --
-------------------------
if not charSelect then return end
local DONKEY_KONG_ROLL_SPEED = 60
local DONKEY_KONG_ROLL_DECAY_PERCENT = 0.98
local DONKEY_KONG_ROLL_DECAY_TIME = 10
local DONKEY_KONG_ROLL_STARTUP = 4
local DONKEY_KONG_ROLL_END = 25
local DONKEY_KONG_SLIP_TIME = 20
local DONKEY_KONG_SLIDE_TIME = 40
----------------
-- DK Gravity --
----------------
--- @param m MarioState
--- @param wall Surface
--- @param intendedPos Vec3f
--- @param nextPos Vec3f
--- @return integer
--- Checks ledge grab for donkey kong
local function donkey_kong_check_ledge_grab(m, wall, intendedPos, nextPos)
if not m then return 0 end
local ledgeFloor
local ledgePos = gVec3fZero()
local displacementX
local displacementZ
if m.vel.y > 0 then
return 0
end
displacementX = nextPos.x - intendedPos.x
displacementZ = nextPos.z - intendedPos.z
-- Only ledge grab if the wall displaced Mario in the opposite direction of
-- his velocity.
if displacementX * m.vel.x + displacementZ * m.vel.z > 0.0 then
return 0
end
--! Since the search for floors starts at y + m.marioObj.hitboxHeight (160.0f), we will sometimes grab
-- a higher ledge than expected (glitchy ledge grab)
ledgePos.x = nextPos.x - wall.normal.x * 60.0
ledgePos.z = nextPos.z - wall.normal.z * 60.0
ledgePos.y, ledgeFloor = find_floor(ledgePos.x, nextPos.y + m.marioObj.hitboxHeight, ledgePos.z)
if not ledgeFloor then return 0 end
if gLevelValues.fixCollisionBugs ~= 0 and gLevelValues.fixCollisionBugsFalseLedgeGrab ~= 0 then
-- fix false ledge grabs
if (not ledgeFloor or ledgeFloor.normal.y < 0.90630779) then
return 0
end
end
if ledgePos.y - nextPos.y <= 100.0 then
return 0
end
vec3f_copy(m.pos, ledgePos)
m.floor = ledgeFloor
m.floorHeight = ledgePos.y
m.floorAngle = atan2s(ledgeFloor.normal.z, ledgeFloor.normal.x)
m.faceAngle.x = 0
m.faceAngle.y = atan2s(wall.normal.z, wall.normal.x) + 0x8000
return 1
end
--- Turns a WallCollisionData object into a table
--- @param wcd WallCollisionData
--- @return table
local function wcd_to_table(wcd)
return {
x = wcd.x, -- number
y = wcd.y, -- number
z = wcd.z, -- number
offsetY = wcd.offsetY, -- number
radius = wcd.radius, -- number
unused = wcd.unused, -- integer
numWalls = wcd.numWalls, -- integer
walls = {
wcd.walls[1],
wcd.walls[2],
wcd.walls[3],
wcd.walls[4]
} , -- Surface[]
normalAddition = {
x = wcd.normalAddition.x,
y = wcd.normalAddition.y,
z = wcd.normalAddition.z,
}, -- Vec3f
normalCount = wcd.normalCount, -- integer
}
end
--- Fills a WallCollisionData object from a table
--- @param wcd WallCollisionData
--- @param t table
local function table_to_wcd(wcd, t)
wcd.x = t.x
wcd.y = t.y
wcd.z = t.z
wcd.offsetY = t.offsetY
wcd.radius = t.radius
wcd.unused = t.unused
wcd.numWalls = t.numWalls
wcd.walls[1] = t.walls[1]
wcd.walls[2] = t.walls[2]
wcd.walls[3] = t.walls[3]
wcd.walls[4] = t.walls[4]
wcd.normalAddition.x = t.normalAddition.x
wcd.normalAddition.y = t.normalAddition.y
wcd.normalAddition.z = t.normalAddition.z
wcd.normalCount = t.normalCount
end
--- @param m MarioState
--- @param intendedPos Vec3f
--- @param stepArg integer
--- @return integer
--- Performs an air quarter step for donkey kong
local function perform_donkey_kong_air_quarter_step(m, intendedPos, stepArg)
if not m then return 0 end
local wallDYaw
local nextPos = gVec3fZero()
local ceil
local floor
local ceilHeight
local floorHeight
local waterLevel
local tempWcd
vec3f_copy(nextPos, intendedPos)
-- Important note:
-- The WallCollisionData pointer is always the same, meaning it cannot be used for both upperWcd and lowerWcd
-- Fortunately, it's read-write, so we can turn it into a table for the Lua part of the function and
-- turn it back into a WallCollisionData object for the C function calls
tempWcd = collision_get_temp_wall_collision_data()
resolve_and_return_wall_collisions_data(nextPos, 150.0, 50.0, tempWcd)
local upperWcd = wcd_to_table(tempWcd)
tempWcd = collision_get_temp_wall_collision_data()
resolve_and_return_wall_collisions_data(nextPos, 30.0, 50.0, tempWcd)
local lowerWcd = wcd_to_table(tempWcd)
floorHeight, floor = find_floor(nextPos.x, nextPos.y, nextPos.z)
ceilHeight, ceil = vec3f_mario_ceil(nextPos, floorHeight)
waterLevel = find_water_level(nextPos.x, nextPos.z)
m.wall = nil
--! The water pseudo floor is not referenced when your intended qstep is
-- out of bounds, so it won't detect you as landing.
if not floor then
if nextPos.y <= m.floorHeight then
m.pos.y = m.floorHeight
return AIR_STEP_LANDED
end
m.pos.y = nextPos.y
if gServerSettings.bouncyLevelBounds ~= BOUNCY_LEVEL_BOUNDS_OFF then
m.faceAngle.y = m.faceAngle.y + 0x8000
mario_set_forward_vel(m, gServerSettings.bouncyLevelBounds == BOUNCY_LEVEL_BOUNDS_ON_CAP and math.clamp(1.5 * m.forwardVel, -500, 500) or 1.5 * m.forwardVel)
end
return AIR_STEP_HIT_WALL
end
if (m.action & ACT_FLAG_RIDING_SHELL) ~= 0 and floorHeight < waterLevel then
local allowForceAction = TRIPLET_BUTTERFLY_ACT_ACTIVATE
if allowForceAction then
floorHeight = waterLevel
floor = get_water_surface_pseudo_floor()
floor.originOffset = floorHeight
end
end
--! This check uses f32, but findFloor uses short (overflow jumps)
if nextPos.y <= floorHeight then
if ceilHeight - floorHeight > m.marioObj.hitboxHeight then
m.pos.x = nextPos.x
m.pos.z = nextPos.z
m.floor = floor
m.floorHeight = floorHeight
end
--! When ceilHeight - floorHeight <= m->marioObj->hitboxHeight (160.0f), the step result says that
-- Mario landed, but his movement is cancelled and his referenced floor
-- isn't updated (pedro spots)
m.pos.y = floorHeight
return AIR_STEP_LANDED
end
if nextPos.y + m.marioObj.hitboxHeight > ceilHeight then
if m.vel.y >= 0.0 then
m.vel.y = 0.0
--! Uses referenced ceiling instead of ceil (ceiling hang upwarp)
if (stepArg and (stepArg & AIR_STEP_CHECK_HANG) ~= 0) and m.ceil and m.ceil.type == SURFACE_HANGABLE then
return AIR_STEP_GRABBED_CEILING
end
return AIR_STEP_NONE
end
if nextPos.y <= m.floorHeight then
m.pos.y = m.floorHeight
return AIR_STEP_LANDED
end
m.pos.y = nextPos.y
return AIR_STEP_HIT_WALL
end
--! When the wall is not completely vertical or there is a slight wall
-- misalignment, you can activate these conditions in unexpected situations
if (stepArg and (stepArg & AIR_STEP_CHECK_LEDGE_GRAB) ~= 0) and upperWcd.numWalls == 0 and lowerWcd.numWalls > 0 then
for i = 1, lowerWcd.numWalls do
if gLevelValues.fixCollisionBugs == 0 then
i = lowerWcd.numWalls
end
local wall = lowerWcd.walls[i]
if donkey_kong_check_ledge_grab(m, wall, intendedPos, nextPos) ~= 0 then
return AIR_STEP_GRABBED_LEDGE
end
end
vec3f_copy(m.pos, nextPos)
m.floor = floor
m.floorHeight = floorHeight
return AIR_STEP_NONE
end
vec3f_copy(m.pos, nextPos)
m.floor = floor
m.floorHeight = floorHeight
if upperWcd.numWalls > 0 then
table_to_wcd(tempWcd, upperWcd)
mario_update_wall(m, tempWcd)
upperWcd = wcd_to_table(tempWcd)
for i = 1, upperWcd.numWalls do
if gLevelValues.fixCollisionBugs == 0 then
i = upperWcd.numWalls
end
local wall = upperWcd.walls[i]
wallDYaw = atan2s(wall.normal.z, wall.normal.x) - m.faceAngle.y
if wall.type == SURFACE_BURNING then
m.wall = wall
return AIR_STEP_HIT_LAVA_WALL
end
if wallDYaw < -0x6000 or wallDYaw > 0x6000 then
m.wall = wall
m.flags = m.flags | MARIO_UNKNOWN_30
return AIR_STEP_HIT_WALL
end
end
elseif lowerWcd.numWalls > 0 then
table_to_wcd(tempWcd, lowerWcd)
mario_update_wall(m, tempWcd)
lowerWcd = wcd_to_table(tempWcd)
for i = 1, lowerWcd.numWalls do
if gLevelValues.fixCollisionBugs == 0 then
i = lowerWcd.numWalls
end
local wall = lowerWcd.walls[i]
wallDYaw = atan2s(wall.normal.z, wall.normal.x) - m.faceAngle.y
if wall.type == SURFACE_BURNING then
m.wall = wall
return AIR_STEP_HIT_LAVA_WALL
end
if wallDYaw < -0x6000 or wallDYaw > 0x6000 then
m.wall = wall
m.flags = m.flags | MARIO_UNKNOWN_30
return AIR_STEP_HIT_WALL
end
end
end
return AIR_STEP_NONE
end
--- @param m MarioState
--- Applies twirl gravity to donkey kong
local function apply_donkey_kong_twirl_gravity(m)
if not m then return end
local terminalVelocity
local heaviness = 1.0
if m.angleVel.y > 1024 then
heaviness = 1024.0 / m.angleVel.y
end
terminalVelocity = -75.0 * heaviness
m.vel.y = m.vel.y - 4.0 * heaviness
if m.vel.y < terminalVelocity then
m.vel.y = terminalVelocity
end
end
--- @param m MarioState
--- @return integer
--- Checks if gravity should be strengthen for donkey kong jump ascent
local function should_strengthen_gravity_for_donkey_kong_jump_ascent(m)
if not m then return 0 end
if m.flags & MARIO_UNKNOWN_08 == 0 then
return 0
end
if m.action & ACT_FLAG_INTANGIBLE ~= 0 or m.action & ACT_FLAG_INVULNERABLE ~= 0 then
return 0
end
if m.input & INPUT_A_DOWN == 0 and m.vel.y > 20.0 then
return m.action & ACT_FLAG_CONTROL_JUMP_HEIGHT ~= 0 and 1 or 0
end
return 0
end
--- @param m MarioState
--- Applies gravity to donkey kong
local function apply_donkey_kong_gravity(m)
if m.action == ACT_TWIRLING and m.vel.y < 0.0 then
apply_donkey_kong_twirl_gravity(m)
elseif m.action == ACT_SHOT_FROM_CANNON then
m.vel.y = math.max(-75, m.vel.y - 1)
elseif m.action == ACT_LONG_JUMP or m.action == ACT_SLIDE_KICK or m.action == ACT_BBH_ENTER_SPIN then
m.vel.y = math.max(-75, m.vel.y - 3.0)
elseif m.action == ACT_LAVA_BOOST or m.action == ACT_FALL_AFTER_STAR_GRAB then
m.vel.y = math.max(-65, m.vel.y - 4.8)
elseif m.action == ACT_GETTING_BLOWN then
m.vel.y = math.max(-75, m.vel.y - (1.5 * m.unkC4))
elseif should_strengthen_gravity_for_donkey_kong_jump_ascent(m) ~= 0 then
m.vel.y = m.vel.y / 4.0
elseif m.action & ACT_FLAG_METAL_WATER ~= 0 then
m.vel.y = math.max(-16, m.vel.y - 2.4)
elseif m.flags & MARIO_WING_CAP ~= 0 and m.vel.y < 0.0 and m.input & INPUT_A_DOWN ~= 0 then
m.marioBodyState.wingFlutter = 1
m.vel.y = m.vel.y - 3.0
if m.vel.y < -37.5 then
m.vel.y = math.min(-37.5, m.vel.y + 4)
end
else
if m.vel.y < 0 then
m.vel.y = math.max(-75, m.vel.y - 6)
else
m.vel.y = math.max(-75, m.vel.y - 4.25)
end
end
end
---@param m MarioState
--- Applies vertical wind to donkey kong
local function apply_donkey_kong_vertical_wind(m)
if not m then return end
local maxVelY
local offsetY
if m.action ~= ACT_GROUND_POUND then
offsetY = m.pos.y - -1500.0
if m.floor and m.floor.type == SURFACE_VERTICAL_WIND and -3000.0 < offsetY and offsetY < 2000.0 then
if offsetY >= 0.0 then
maxVelY = 10000.0 / (offsetY + 200.0)
else
maxVelY = 50.0
end
if m.vel.y < maxVelY then
m.vel.y = (m.vel.y + maxVelY / 6.0) -- Bit stronger so DK doesn't fall through
if m.vel.y > maxVelY then
m.vel.y = maxVelY
end
end
end
end
end
--- @param m MarioState
--- @param stepArg integer
--- @return integer
--- Performs an air step for donkey kong
local function perform_donkey_kong_air_step(m, stepArg)
local intendedPos = gVec3fZero()
local quarterStepResult
local stepResult = AIR_STEP_NONE
m.wall = nil
for i = 0, 3 do
local step = gVec3fZero()
step = {
x = m.vel.x / 4.0,
y = m.vel.y / 4.0,
z = m.vel.z / 4.0,
}
intendedPos.x = m.pos.x + step.x
intendedPos.y = m.pos.y + step.y
intendedPos.z = m.pos.z + step.z
vec3f_normalize(step)
set_find_wall_direction(step, true, true)
quarterStepResult = perform_donkey_kong_air_quarter_step(m, intendedPos, stepArg)
set_find_wall_direction(step, false, false)
--! On one qf, hit OOB/ceil/wall to store the 2 return value, and continue
-- getting 0s until your last qf. Graze a wall on your last qf, and it will
-- return the stored 2 with a sharply angled reference wall. (some gwks)
if (quarterStepResult ~= AIR_STEP_NONE) then
stepResult = quarterStepResult
end
if (quarterStepResult == AIR_STEP_LANDED or quarterStepResult == AIR_STEP_GRABBED_LEDGE
or quarterStepResult == AIR_STEP_GRABBED_CEILING
or quarterStepResult == AIR_STEP_HIT_LAVA_WALL) then
break
end
end
if (m.vel.y >= 0.0) then
m.peakHeight = m.pos.y
end
m.terrainSoundAddend = mario_get_terrain_sound_addend(m)
-- Start climbing
if m.wall ~= nil and m.action ~= ACT_DONKEY_CLIMB and m.prevAction ~= ACT_DONKEY_CLIMB
and (m.action & ACT_FLAG_INVULNERABLE == 0) and stepResult ~= AIR_STEP_HIT_LAVA_WALL
and m.input & INPUT_A_DOWN ~= 0 and m.heldObj == nil then
local wallangle = atan2s(m.wallNormal.z, m.wallNormal.x) + 0x8000
-- Only grab wall if within certain angle of the wall
if abs_angle_diff(wallangle, m.faceAngle.y) < 0x3000 then
set_mario_action(m, ACT_DONKEY_CLIMB, 0)
if stepResult == AIR_STEP_HIT_WALL then return 0 end
return stepResult
end
end
if (m.action ~= ACT_FLYING and m.action ~= ACT_BUBBLED) then
apply_donkey_kong_gravity(m)
end
apply_donkey_kong_vertical_wind(m)
vec3f_copy(m.marioObj.header.gfx.pos, m.pos)
vec3s_set(m.marioObj.header.gfx.angle, 0, m.faceAngle.y, 0)
return stepResult
end
function donkey_kong_before_phys_step(m, stepType, stepArg)
if stepType == STEP_TYPE_GROUND then
-- return perform_donkey_kong_ground_step(m) -- TBA
elseif stepType == STEP_TYPE_AIR then
return perform_donkey_kong_air_step(m, stepArg)
elseif stepType == STEP_TYPE_WATER then
-- return perform_donkey_kong_water_step(m) -- TBA
elseif stepType == STEP_TYPE_HANG then
-- return perform_donkey_kong_hanging_step(m) -- TBA
end
end
function donkey_kong_before_action(m, action, actionArg)
if (action == ACT_DIVE or action == ACT_MOVE_PUNCHING) and m.action & ACT_FLAG_AIR == 0 and m.input & INPUT_A_DOWN == 0 and m.forwardVel >= 20 then
m.vel.y = 20
m.faceAngle.x = 0
return ACT_DONKEY_KONG_ROLL
elseif (action == ACT_PUNCHING and actionArg == 9) then
return ACT_DONKEY_KONG_POUND
end
end
function donkey_kong_on_interact(m, o, type, value)
-- allow donkey kong to grab objects with the roll
if type == INTERACT_GRABBABLE and m.action == ACT_DONKEY_KONG_ROLL then
if ((o.oInteractionSubtype & INT_SUBTYPE_NOT_GRABBABLE) == 0) then
m.interactObj = o
m.input = m.input | INPUT_INTERACT_OBJ_GRABBABLE
if (o.oSyncID ~= 0) then network_send_object(o, false) end
return 1
end
end
end
function on_attack_object(m, o, interaction)
-- speed up when hitting enemies with roll
if (m.action == ACT_DONKEY_KONG_ROLL or m.action == ACT_DONKEY_KONG_ROLL_AIR) and (interaction & INT_FAST_ATTACK_OR_SHELL ~= 0) then
if o.oInteractType == INTERACT_BULLY then
mario_set_forward_vel(m, -25)
m.actionTimer = DONKEY_KONG_ROLL_DECAY_TIME
m.actionArg = 1
else
local newForwardVel = math.min(m.forwardVel * 1.1, 70)
mario_set_forward_vel(m, newForwardVel)
m.actionTimer = 0
m.actionArg = 0
end
end
-- Bounce code
if (CT_DONKEY_KONG ~= _G.charSelect.character_get_current_number(m.playerIndex)) then return end
if (_G.charSelect.get_options_status(6) ~= 0) then
if (interaction == INT_HIT_FROM_ABOVE and m.framesSinceA < 5) then
m.actionTimer = 0
if (m.action == ACT_DONKEY_KONG_BOUNCE) then
set_mario_action(m, ACT_DONKEY_KONG_BOUNCE, m.actionArg + 1)
else
set_mario_action(m, ACT_DONKEY_KONG_BOUNCE, 1)
end
end
end
end
hook_event(HOOK_ON_ATTACK_OBJECT, on_attack_object)
_G.ACT_DONKEY_KONG_ROLL = allocate_mario_action(ACT_GROUP_MOVING | ACT_FLAG_ATTACKING | ACT_FLAG_MOVING)
_G.ACT_DONKEY_KONG_ROLL_AIR = allocate_mario_action(ACT_GROUP_AIRBORNE | ACT_FLAG_ATTACKING | ACT_FLAG_AIR | ACT_FLAG_ALLOW_VERTICAL_WIND_ACTION)
_G.ACT_DONKEY_KONG_POUND = allocate_mario_action(ACT_GROUP_STATIONARY | ACT_FLAG_ATTACKING)
_G.ACT_DONKEY_KONG_POUND_HIT = allocate_mario_action(ACT_GROUP_STATIONARY | ACT_FLAG_ATTACKING)
_G.ACT_DONKEY_KONG_BOUNCE = (ACT_GROUP_AIRBORNE | ACT_FLAG_MOVING | ACT_FLAG_AIR | ACT_FLAG_ALLOW_VERTICAL_WIND_ACTION)
---------------
-- DK Bounce --
---------------
--- SwagSkeleton95
--- Allows the player to bounce across enemies with well-timed A presses. Spawns coins when chained across multiple enemies. Credit to baconator2558 for the vast majority of this code.
--- action
local bounceSounds = {
audio_sample_load("z_sfx_dk_bounce1.ogg"),
audio_sample_load("z_sfx_dk_bounce2.ogg"),
audio_sample_load("z_sfx_dk_bounce3.ogg")
}
local coinObj = nil
function act_dk_bounce(m)
if (m.actionTimer == 0) then
set_character_animation(m, CHAR_ANIM_FORWARD_SPINNING)
set_anim_to_frame(m, 0)
m.forwardVel = 0
m.vel.x = 0
m.vel.y = 80
play_character_sound(m, CHAR_SOUND_YAHOO_WAHA_YIPPEE)
m.vel.z = 0
m.slideVelX = 0
m.slideVelZ = 0
m.faceAngle.y = m.intendedYaw
if (m.actionArg >= 3) then
coinObj = spawn_non_sync_object(id_bhvBlueCoinJumping, E_MODEL_SPARKLES, m.pos.x, m.pos.y, m.pos.z, nil)
end
audio_sample_play(bounceSounds[math.min(m.actionArg,3)], m.pos, 0.5)
-- plays a random sound from a table ('bounceSounds') of 3 sound files.
-- I didn't include them here because I ripped them straight from DKCR myself
-- and I'm under the impression that this mod mainly uses self-made sound effects
set_mario_particle_flags(m, PARTICLE_HORIZONTAL_STAR, 0)
end
if (m.actionTimer >= 1 and coinObj ~= nil) then
coinObj.oPosX = m.pos.x
coinObj.oPosY = m.pos.y
coinObj.oPosZ = m.pos.z
interact_coin(m, INTERACT_COIN, coinObj)
coinObj = nil
end
if (m.actionTimer > 5 and m.marioObj.header.gfx.animInfo.animID == CHAR_ANIM_FORWARD_SPINNING) then
set_character_animation(m, CHAR_ANIM_TRIPLE_JUMP)
set_anim_to_frame(m, 21)
end
m.forwardVel = math.min(m.forwardVel, 95)
update_air_without_turn(m)
if (m.actionTimer > 20) then
update_air_without_turn(m)
end
if (m.vel.y < 10) then
update_air_without_turn(m)
if (m.vel.y < -10) then
update_air_without_turn(m)
update_air_without_turn(m)
update_air_without_turn(m)
update_air_without_turn(m)
update_air_without_turn(m)
end
end
local stepResult = perform_air_step(m, AIR_STEP_CHECK_HANG | AIR_STEP_CHECK_LEDGE_GRAB)
if (stepResult == AIR_STEP_LANDED) then
set_character_animation(m, CHAR_ANIM_FORWARD_SPINNING)
set_anim_to_frame(m, 0)
return set_mario_action(m, ACT_DOUBLE_JUMP_LAND, 0)
elseif (stepResult == AIR_STEP_GRABBED_LEDGE) then
set_character_animation(m, CHAR_ANIM_IDLE_ON_LEDGE)
return drop_and_set_mario_action(m, ACT_LEDGE_GRAB, 0)
elseif (stepResult == AIR_STEP_GRABBED_CEILING) then
return set_mario_action(m, ACT_START_HANGING, 0)
end
m.faceAngle.y = approach_s16_symmetric(m.faceAngle.y, m.intendedYaw, (abs_angle_diff(m.faceAngle.y, m.intendedYaw) / (25 * m.actionTimer + 1)) + 750)
update_air_without_turn(m)
m.actionTimer = m.actionTimer + 1
if (check_kick_or_dive_in_air(m) ~= 0) then
return 1
end
return 0
end
hook_mario_action(ACT_DONKEY_KONG_BOUNCE, act_dk_bounce, INT_HIT_FROM_ABOVE)
--- Roll
---@param m MarioState
local function act_donkey_kong_roll(m)
if (not m) then return 0 end
local isSliding = (mario_floor_is_slippery(m)) ~= 0
if isSliding then
if update_sliding(m, 4) ~= 0 or m.actionState == 0 then
m.faceAngle.x = 0
return set_mario_action(m, ACT_DECELERATING, 0)
end
end
if mario_check_object_grab(m) ~= 0 then
m.faceAngle.x = 0
set_character_animation(m, CHAR_ANIM_FIRST_PUNCH)
set_anim_to_frame(m, 2)
return 1
end
if (m.input & INPUT_A_PRESSED) ~= 0 then
m.faceAngle.x = 0
m.marioObj.header.gfx.angle.x = m.faceAngle.x
local result = set_jumping_action(m, ACT_JUMP, 0)
if not isSliding then
m.forwardVel = m.forwardVel / 0.8 - 5 -- conserve all jump momentum minus 5
end
return result
end
local doSpinAnim = true
m.actionTimer = m.actionTimer + 1
set_character_animation(m, CHAR_ANIM_START_CROUCHING)
if m.actionState == 0 then
doSpinAnim = false
local newForwardVel = m.forwardVel
newForwardVel = DONKEY_KONG_ROLL_SPEED * (m.actionTimer / DONKEY_KONG_ROLL_STARTUP)
if m.actionTimer >= DONKEY_KONG_ROLL_STARTUP then
newForwardVel = DONKEY_KONG_ROLL_SPEED
m.actionState = 1
end
mario_set_forward_vel(m, newForwardVel)
elseif m.actionTimer >= DONKEY_KONG_ROLL_DECAY_TIME and not isSliding then
-- slow down after a time
local newForwardVel = m.forwardVel
newForwardVel = newForwardVel * DONKEY_KONG_ROLL_DECAY_PERCENT
mario_set_forward_vel(m, newForwardVel)
end
-- influence direction slightly
m.marioObj.oMoveAngleYaw = m.faceAngle.y
cur_obj_rotate_yaw_toward(m.intendedYaw, 0x100)
m.faceAngle.y = m.marioObj.oMoveAngleYaw
local result = perform_ground_step(m)
if result == GROUND_STEP_LEFT_GROUND then
if m.actionState == 0 then
mario_set_forward_vel(m, DONKEY_KONG_ROLL_SPEED)
end
return set_mario_action(m, ACT_DONKEY_KONG_ROLL_AIR, 0)
elseif result == GROUND_STEP_HIT_WALL then
if (m.wall or gServerSettings.bouncyLevelBounds == BOUNCY_LEVEL_BOUNDS_OFF) then
m.faceAngle.x = 0
set_mario_particle_flags(m, PARTICLE_VERTICAL_STAR, 0)
slide_bonk(m, ACT_GROUND_BONK, ACT_WALKING)
return
end
end
if doSpinAnim then
local prevFaceAngleX = m.faceAngle.x
m.faceAngle.x = m.faceAngle.x + 0x60 * m.forwardVel
m.marioObj.header.gfx.angle.x = m.faceAngle.x
m.marioObj.header.gfx.pos.y = m.marioObj.header.gfx.pos.y + 50
if prevFaceAngleX <= 0 and m.faceAngle.x > 0 then
play_sound(SOUND_ACTION_SPIN, m.marioObj.header.gfx.cameraToObject)
end
end
-- end roll
if m.actionTimer > DONKEY_KONG_ROLL_END then
m.faceAngle.x = 0
return set_mario_action(m, ACT_WALKING, 0)
end
return 0
end
hook_mario_action(ACT_DONKEY_KONG_ROLL, { every_frame = act_donkey_kong_roll }, INT_FAST_ATTACK_OR_SHELL)
---@param m MarioState
local function act_donkey_kong_roll_air(m)
if (not m) then return 0 end
if (m.input & INPUT_A_PRESSED) ~= 0 then
m.terrainSoundAddend = 0
m.faceAngle.x = 0
m.marioObj.header.gfx.angle.x = m.faceAngle.x
local result = set_mario_action(m, ACT_JUMP, 0)
m.forwardVel = m.forwardVel / 0.8 - 5 -- conserve all jump momentum minus 5
return result
end
m.actionTimer = m.actionTimer + 1
-- influence direction slightly
m.marioObj.oMoveAngleYaw = m.faceAngle.y
cur_obj_rotate_yaw_toward(m.intendedYaw, 0x100)
m.faceAngle.y = m.marioObj.oMoveAngleYaw
mario_set_forward_vel(m, m.forwardVel)
local result = perform_air_step(m, AIR_STEP_CHECK_LEDGE_GRAB)
if result == AIR_STEP_LANDED then
if (check_fall_damage_or_get_stuck(m, ACT_HARD_BACKWARD_GROUND_KB) == 0) then
set_mario_action(m, ACT_DONKEY_KONG_ROLL, 0)
m.actionState = 1
return 1
end
elseif result == AIR_STEP_HIT_WALL then
if (m.wall or gServerSettings.bouncyLevelBounds == BOUNCY_LEVEL_BOUNDS_OFF) then
mario_bonk_reflection(m, 1)
if (m.vel.y > 0) then m.vel.y = 0 end
set_mario_particle_flags(m, PARTICLE_VERTICAL_STAR, 0)
drop_and_set_mario_action(m, ACT_BACKWARD_AIR_KB, 0)
return 1
end
elseif result == AIR_STEP_HIT_LAVA_WALL then
lava_boost_on_wall(m)
return 1
end
local prevFaceAngleX = m.faceAngle.x
m.faceAngle.x = m.faceAngle.x + 0x60 * m.forwardVel
m.marioObj.header.gfx.angle.x = m.faceAngle.x
m.marioObj.header.gfx.pos.y = m.marioObj.header.gfx.pos.y + 50
if prevFaceAngleX <= 0 and m.faceAngle.x > 0 then
play_sound(SOUND_ACTION_SPIN, m.marioObj.header.gfx.cameraToObject)
end
if m.actionTimer > DONKEY_KONG_ROLL_END then
m.faceAngle.x = 0
return set_mario_action(m, ACT_FREEFALL, 0)
end
return 0
end
hook_mario_action(ACT_DONKEY_KONG_ROLL_AIR, { every_frame = act_donkey_kong_roll_air }, INT_FAST_ATTACK_OR_SHELL)
local function act_donkey_kong_pound(m)
if (not m) then return 0 end
mario_set_forward_vel(m, 0)
if (mario_floor_is_slippery(m)) ~= 0 then
return set_mario_action(m, ACT_BEGIN_SLIDING, 0)
end
if (m.input & INPUT_A_PRESSED) ~= 0 then
local result = set_jumping_action(m, ACT_JUMP, 0)
return result
elseif (m.input & INPUT_B_PRESSED) ~= 0 and m.actionTimer ~= 0 then
m.actionState = 1
end
m.actionTimer = m.actionTimer + 1
if m.actionTimer == 1 then
play_mario_heavy_landing_sound(m, SOUND_ACTION_TERRAIN_HEAVY_LANDING)
-- Spawn particles at hand that hit ground
local pos = {x = 0, y = 0, z = 0}
if m.marioObj.header.gfx.animInfo.animFrame >= 8 then
get_mario_anim_part_pos(m, MARIO_ANIM_PART_RIGHT_HAND, pos)
else
get_mario_anim_part_pos(m, MARIO_ANIM_PART_LEFT_HAND, pos)
end
pos.y = m.pos.y -- always appear on ground
spawn_non_sync_object(id_bhvHorStarParticleSpawner, E_MODEL_NONE, pos.x, pos.y, pos.z, nil)
spawn_non_sync_object(id_bhvMistCircParticleSpawner, E_MODEL_NONE, pos.x, pos.y, pos.z, nil)
m.action = ACT_DONKEY_KONG_POUND_HIT
m.marioObj.hitboxRadius = 100 -- larger hitbox
elseif m.action == ACT_DONKEY_KONG_POUND_HIT then
m.action = ACT_DONKEY_KONG_POUND
m.marioObj.hitboxRadius = 37 -- reset hitbox
elseif m.actionTimer >= 8 then
if m.actionState ~= 0 then
-- pound again
m.actionTimer = 0
m.actionState = 0
elseif m.input & INPUT_Z_DOWN ~= 0 then
set_mario_action(m, ACT_START_CROUCHING, 0)
else
set_mario_action(m, ACT_IDLE, 0)
end
end
--set_character_anim_with_accel(m, CHAR_ANIM_PLACE_LIGHT_OBJ, 0x20000)
-- 28 anim frames in 16 frames
if m.marioObj.header.gfx.animInfo.animFrame > 15 and m.actionTimer == 0 then
--djui_chat_message_create(tostring(m.marioObj.header.gfx.animInfo.animFrame))
set_anim_to_frame(m, 0)
end
play_custom_anim(m, "donkey_ground_slap", 0x10000 * 28 // 16)
--[[set_anim_to_frame(m, m.marioObj.header.gfx.animInfo.animFrame)
if m.controller.buttonPressed & L_TRIG ~= 0 then
set_anim_to_frame(m, m.marioObj.header.gfx.animInfo.animFrame + 1)
end]]
local result = perform_ground_step(m)
if result == GROUND_STEP_LEFT_GROUND then
return set_mario_action(m, ACT_FREEFALL, 0)
end
end
hook_mario_action(ACT_DONKEY_KONG_POUND, { every_frame = act_donkey_kong_pound })
hook_mario_action(ACT_DONKEY_KONG_POUND_HIT, { every_frame = act_donkey_kong_pound }, INT_GROUND_POUND) -- same action but with ground pound interaction
-----------------------
--- Donkey Climbing ---
--- -------------------
DK_ANIM_CLIMBING = 'donkey_custom_climbing'
ACT_DONKEY_CLIMB = allocate_mario_action(ACT_FLAG_AIR | ACT_GROUP_AIRBORNE)
-- Climbing ability action
--- @param m MarioState
function act_donkey_climb(m)
--No wall, no climb
if m.wall == nil then
set_mario_action(m, ACT_TRIPLE_JUMP, 0)
mario_set_forward_vel(m, 10)
return true
--Press A to jump off
elseif (m.input & INPUT_A_PRESSED) ~= 0 then
set_mario_action(m, ACT_JUMP, 0)
m.faceAngle.y = m.faceAngle.y - 0x8000
mario_set_forward_vel(m, 20)
return true
--Press Z to just fall off
elseif (m.input & INPUT_Z_PRESSED) ~= 0 then
m.input = m.input &~ INPUT_Z_PRESSED
play_character_sound(m, CHAR_SOUND_UH)
mario_set_forward_vel(m, -8)
return set_mario_action(m, ACT_FREEFALL, 0)
end
--Woah!
if m.actionTimer == 0 then
play_character_sound(m, CHAR_SOUND_WHOA)
end
local climbAnimSpeed = m.intendedMag
local wallangle = atan2s(m.wallNormal.z, m.wallNormal.x) + 0x8000
local transwall
if m.actionTimer >= 4 then
--Face beside wall and move around it
m.faceAngle.y = wallangle - 0x4000
if m.actionTimer <= DONKEY_KONG_SLIP_TIME then
mario_set_forward_vel(m, m.controller.stickX/3)
m.vel.y = m.controller.stickY/3
elseif m.actionTimer <= DONKEY_KONG_SLIDE_TIME then -- Slip on wall after some time
climbAnimSpeed = 32
m.vel.y = 0
else
climbAnimSpeed = 0
m.vel.y = m.vel.y + 8 -- counteract gravity
end
--Perform air step
local air_step = perform_air_step(m, 0)
transwall = m.wall
if air_step == AIR_STEP_LANDED then
return set_mario_action(m, ACT_FREEFALL_LAND, 0)
end
end
--Face directly towards wall to make sure we're latched on
m.faceAngle.y = wallangle
mario_set_forward_vel(m, 1)
if m.actionTimer <= DONKEY_KONG_SLIP_TIME then
m.vel.y = 0
elseif m.actionTimer <= DONKEY_KONG_SLIDE_TIME then
m.vel.y = 5
end
--Perform air step
air_step = perform_air_step(m, 0)
if air_step == AIR_STEP_LANDED then
return set_mario_action(m, ACT_FREEFALL_LAND, 0)
elseif m.wall == nil then
if transwall == nil then
set_mario_action(m, ACT_TRIPLE_JUMP, 0)
mario_set_forward_vel(m, 10)
return true
else
m.wall = transwall
end
end
--Climbing animation
play_custom_anim(m, "donkey_custom_climbing", climbAnimSpeed * 0x3000)
if m.actionTimer < 8 or climbAnimSpeed == 0 then
set_anim_to_frame(m, 0)
else
m.particleFlags = m.particleFlags | PARTICLE_DUST
m.terrainSoundAddend = SOUND_TERRAIN_SAND << 16
play_step_sound(m, 26, 79)
end
local inward_offset = 25
m.marioObj.header.gfx.pos.x = m.marioObj.header.gfx.pos.x + inward_offset * sins(m.faceAngle.y)
m.marioObj.header.gfx.pos.z = m.marioObj.header.gfx.pos.z + inward_offset * coss(m.faceAngle.y)
m.actionTimer = m.actionTimer + 1
end
hook_mario_action(ACT_DONKEY_CLIMB, {every_frame = act_donkey_climb, gravity = function() end})

View file

@ -1,4 +1,9 @@
if not _G.charSelectExists then return end
---------------------
-- Pauline Moveset --
---------------------
local OmmEnabled = OmmEnabled
if not charSelect then return end
---------------
-- Constants --
@ -74,12 +79,12 @@ local CAPPY_EVENT_UNLOAD = 5
local CAPPY_EVENT_BOUNCE = 6
-- Actions
local ACT_CAPPY_THROW_GROUND = allocate_mario_action(ACT_GROUP_MOVING | ACT_FLAG_MOVING)
local ACT_CAPPY_THROW_AIRBORNE = allocate_mario_action(ACT_GROUP_AIRBORNE | ACT_FLAG_AIR)
local ACT_CAPPY_BOUNCE = allocate_mario_action(ACT_GROUP_AIRBORNE | ACT_FLAG_AIR | ACT_FLAG_ALLOW_VERTICAL_WIND_ACTION)
local ACT_CAPPY_VAULT = allocate_mario_action(ACT_GROUP_AIRBORNE | ACT_FLAG_AIR | ACT_FLAG_ALLOW_VERTICAL_WIND_ACTION)
local ACT_CAPPY_RAINBOW_SPIN = allocate_mario_action(ACT_GROUP_AIRBORNE | ACT_FLAG_AIR | ACT_FLAG_ALLOW_VERTICAL_WIND_ACTION | ACT_FLAG_ATTACKING)
local ACT_CAPPY_THROW_WATER = allocate_mario_action(ACT_GROUP_SUBMERGED | ACT_FLAG_SWIMMING)
_G.ACT_CAPPY_THROW_GROUND = allocate_mario_action(ACT_GROUP_MOVING | ACT_FLAG_MOVING)
_G.ACT_CAPPY_THROW_AIRBORNE = allocate_mario_action(ACT_GROUP_AIRBORNE | ACT_FLAG_AIR)
_G.ACT_CAPPY_BOUNCE = allocate_mario_action(ACT_GROUP_AIRBORNE | ACT_FLAG_AIR | ACT_FLAG_ALLOW_VERTICAL_WIND_ACTION)
_G.ACT_CAPPY_VAULT = allocate_mario_action(ACT_GROUP_AIRBORNE | ACT_FLAG_AIR | ACT_FLAG_ALLOW_VERTICAL_WIND_ACTION)
_G.ACT_CAPPY_RAINBOW_SPIN = allocate_mario_action(ACT_GROUP_AIRBORNE | ACT_FLAG_AIR | ACT_FLAG_ALLOW_VERTICAL_WIND_ACTION | ACT_FLAG_ATTACKING)
_G.ACT_CAPPY_THROW_WATER = allocate_mario_action(ACT_GROUP_SUBMERGED | ACT_FLAG_SWIMMING)
-- Animations
local MARIO_ANIM_PAULINE_CAPPY_VAULT = "anim_pauline_cappy_vault"
@ -135,11 +140,10 @@ local function mario_anim_play_custom(m, animName, animAccel)
m.marioObj.header.gfx.animInfo.animAccel = animAccel or 0x10000
end
-- Hacky way to get Pauline
local function get_pauline()
for i = 1, #extraCharacters do
local extraCharacter = extraCharacters[i]
if extraCharacter.name == "Pauline" then
if extraCharacter.tablePos == CT_PAULINE then
return extraCharacter
end
end
@ -147,7 +151,7 @@ local function get_pauline()
end
local function is_pauline(m)
return _G.charSelect.character_get_current_number(m.playerIndex) == get_pauline().tablePos
return character_get_current_number(m.playerIndex) == CT_PAULINE
end
-----------
@ -172,7 +176,7 @@ local id_bhvCappy = hook_behavior(
true,
function (o) o.oFlags = o.oFlags | OBJ_FLAG_UPDATE_GFX_POS_AND_ANGLE end,
function (o) bhv_cappy_loop(o) end,
"bhvCappy"
"bhvPaulineCappy"
)
local function cappy_get_object(m)
@ -897,7 +901,6 @@ local function cappy_process_mario_interactions(cappy)
if cappy_mario_can_bounce() then
local marioObj = m0.marioObj
obj_set_pos(marioObj, m0.pos.x, m0.pos.y, m0.pos.z)
marioObj.hitboxRadius = 50
local obj = obj_get_first_with_behavior_id(id_bhvCappy)
while obj ~= nil do
if (obj.oSubAction == 1 and -- Cappy is spawned
@ -913,7 +916,12 @@ local function cappy_process_mario_interactions(cappy)
if (marioObj.oIntangibleTimer == 0 or mAction == ACT_BUBBLED) then
-- Check hitbox overlap
if obj_check_hitbox_overlap(marioObj, obj) then
local marioHitboxRadius = marioObj.hitboxRadius
marioObj.hitboxRadius = 50
local hitboxOverlap = obj_check_hitbox_overlap(marioObj, obj)
marioObj.hitboxRadius = marioHitboxRadius
if hitboxOverlap then
local marioGfx = marioObj.header.gfx
-- Pop bubble
@ -1175,7 +1183,7 @@ end
local function cappy_update(m, cappy)
-- Unload Cappy if...
if (_G.OmmEnabled or -- OMM Rebirth is enabled
if (OmmEnabled or -- OMM Rebirth is enabled
not is_pauline(m) or -- Not Pauline
not gNetworkPlayers[m.playerIndex].connected or -- Not connected
not is_player_active(m) -- Not active
@ -1296,7 +1304,6 @@ function pauline_update(m)
end
end
cappy_process_mario_interactions(cappy)
m.marioObj.hitboxRadius = 37
end
-- Process Cappy events

View file

@ -0,0 +1,59 @@
-------------------
-- Peach Moveset --
-------------------
if not charSelect then return end
local floatActs = {
[ACT_JUMP] = true,
[ACT_DOUBLE_JUMP] = true,
[ACT_TRIPLE_JUMP] = true,
[ACT_LONG_JUMP] = true,
[ACT_BACKFLIP] = true,
[ACT_SIDE_FLIP] = true,
[ACT_WALL_KICK_AIR] = true,
}
_G.ACT_FLOAT = allocate_mario_action(ACT_GROUP_AIRBORNE | ACT_FLAG_ALLOW_VERTICAL_WIND_ACTION | ACT_FLAG_MOVING)
--- @param m MarioState
local function act_float(m)
-- apply movement when using action
common_air_action_step(m, ACT_JUMP_LAND, CHAR_ANIM_BEND_KNESS_RIDING_SHELL, AIR_STEP_NONE)
-- setup when action starts (horizontal speed and voiceline)
if m.actionTimer == 0 then
play_character_sound(m, CHAR_SOUND_HELLO)
end
if m.forwardVel > 20 then
m.forwardVel = m.forwardVel - 0.5
end
-- Slowly decend
m.vel.y = -1
set_mario_particle_flags(m, PARTICLE_SPARKLES, 0)
-- avoid issue with flying and then make the hover end after 2 secs or when stopping holding the button
if m.prevAction ~= ACT_TRIPLE_JUMP and (m.flags & MARIO_WING_CAP) ~= 0 then
if m.actionTimer >= 50 or (m.controller.buttonDown & A_BUTTON) == 0 then
set_mario_action(m, ACT_FREEFALL, 0)
end
else
if m.actionTimer >= 50 or (m.controller.buttonDown & A_BUTTON) == 0 then
set_mario_action(m, ACT_FREEFALL, 0)
end
end
-- increment the action timer to make the hover stop
m.actionTimer = m.actionTimer + 1
end
--- @param m MarioState
function peach_update(m)
if (m.input & INPUT_A_DOWN) ~= 0 and m.vel.y < -10 and m.prevAction ~= ACT_FLOAT and floatActs[m.action] then
set_mario_action(m, ACT_FLOAT, 0)
end
end
hook_mario_action(ACT_FLOAT, act_float)

View file

@ -0,0 +1,203 @@
----------------------
-- Rosalina Moveset --
----------------------
if not charSelect then return end
_G.ACT_JUMP_TWIRL = allocate_mario_action(ACT_GROUP_AIRBORNE | ACT_FLAG_AIR | ACT_FLAG_ATTACKING)
E_MODEL_TWIRL_EFFECT = smlua_model_util_get_id("spin_attack_geo")
---@param o Object
local function bhv_spin_attack_init(o)
o.oFlags = OBJ_FLAG_UPDATE_GFX_POS_AND_ANGLE -- Allows you to change the position and angle
end
---@param o Object
local function bhv_spin_attack_loop(o)
-- Retrieves the Mario state corresponding to its global index
local m = gMarioStates[network_local_index_from_global(o.globalPlayerIndex)]
if m == nil or m.marioObj == nil then
obj_mark_for_deletion(o)
return
end
o.parentObj = m.marioObj -- Sets the Mario object as its parent
cur_obj_set_pos_relative_to_parent(0, 20, 0) -- Makes it move to its parent's position
o.oFaceAngleYaw = o.oFaceAngleYaw + 0x2000 -- Rotates it
if m.action ~= ACT_JUMP_TWIRL or o.oTimer > 15 then -- Deletes itself once the action changes
obj_mark_for_deletion(o)
end
end
local id_bhvTwirlEffect = hook_behavior(nil, OBJ_LIST_GENACTOR, true, bhv_spin_attack_init, bhv_spin_attack_loop,
"bhvRosalinaTwirlEffect")
-- Spinable actions, these are actions you can spin out of that don't normally allow a kick/dive
local extraSpinActs = {
[ACT_LONG_JUMP] = true,
[ACT_BACKFLIP] = true,
}
-- Spin overridable actions, these are overriden instantly
local spinOverrides = {
[ACT_PUNCHING] = true,
[ACT_MOVE_PUNCHING] = true,
[ACT_JUMP_KICK] = true,
[ACT_DIVE] = true
}
local ROSALINA_SOUND_SPIN = audio_sample_load("z_sfx_rosalina_spinattack.ogg") -- Load audio sample
---@param m MarioState
function act_jump_twirl(m)
local e = gCharacterStates[m.playerIndex]
if m.actionTimer >= 15 then
return set_mario_action(m, ACT_FREEFALL, 0) -- End the action
end
if m.actionTimer == 0 then
m.marioObj.header.gfx.animInfo.animID = -1
play_character_sound(m, CHAR_SOUND_HELLO) -- Plays the character sound
audio_sample_play(ROSALINA_SOUND_SPIN, m.pos, 1) -- Plays the spin sound sample
m.particleFlags = m.particleFlags | ACTIVE_PARTICLE_SPARKLES -- Spawns sparkle particles
if e.rosalina.canSpin then
m.vel.y = 30 -- Initial upward velocity
e.rosalina.canSpin = false
-- Spawn the spin effect
if m.playerIndex == 0 then
spawn_sync_object(id_bhvTwirlEffect, E_MODEL_TWIRL_EFFECT, m.pos.x, m.pos.y, m.pos.z, function(o)
o.globalPlayerIndex = m.marioObj.globalPlayerIndex
end)
end
else
m.vel.y = math.max(m.vel.y, 0)
end
m.marioObj.hitboxRadius = 100 -- Damage hitbox
else
m.marioObj.hitboxRadius = 37 -- Reset the hitbox after initial hit
end
common_air_action_step(m, ACT_FREEFALL_LAND, CHAR_ANIM_BEND_KNESS_RIDING_SHELL, AIR_STEP_NONE)
m.marioBodyState.handState = MARIO_HAND_PEACE_SIGN -- Hand State
-- Increments the action timer
m.actionTimer = m.actionTimer + 1
end
---@param m MarioState
---@param o Object
---@param intType InteractionType
function rosalina_allow_interact(m, o, intType)
local e = gCharacterStates[m.playerIndex]
if m.action == ACT_JUMP_TWIRL and intType == INTERACT_GRABBABLE and o.oInteractionSubtype & INT_SUBTYPE_NOT_GRABBABLE == 0 then
local angleTo = mario_obj_angle_to_object(m, o)
if (o.oInteractionSubtype & INT_SUBTYPE_GRABS_MARIO ~= 0 or obj_has_behavior_id(o, id_bhvBowser) ~= 0) then -- heavy grab objects
if m.pos.y - m.floorHeight < 100 and abs_angle_diff(m.faceAngle.y, angleTo) < 0x4000 then
m.action = ACT_MOVE_PUNCHING
m.actionArg = 1
end
elseif not e.rosalina.orbitObjActive then -- light grab objects
m.usedObj = o
e.rosalina.orbitObjActive = true
e.rosalina.orbitObjDist = 160 - m.actionTimer * 2
e.rosalina.orbitObjAngle = angleTo
return false
end
end
end
---@param m MarioState
function rosalina_update(m)
local e = gCharacterStates[m.playerIndex]
if m.controller.buttonPressed & B_BUTTON ~= 0 and extraSpinActs[m.action] then
return set_mario_action(m, ACT_JUMP_TWIRL, 0)
end
--if m.action & ACT_FLAG_AIR == 0 and m.playerIndex == 0 then
-- e.rosalina.canSpin = true
--end
if m.action ~= ACT_JUMP_TWIRL and m.marioObj.hitboxRadius ~= 37 then
m.marioObj.hitboxRadius = 37
end
if e.rosalina.orbitObjActive then
local o = m.usedObj
if not o or o.activeFlags == ACTIVE_FLAG_DEACTIVATED then
e.rosalina.orbitObjActive = false
o.oIntangibleTimer = 0
if m.playerIndex == 0 then m.usedObj = nil end
return
end
e.rosalina.orbitObjDist = e.rosalina.orbitObjDist - 6
if e.rosalina.orbitObjDist >= 90 then
e.rosalina.orbitObjAngle = e.rosalina.orbitObjAngle + 0x1800
else
e.rosalina.orbitObjAngle = approach_s16_asymptotic(e.rosalina.orbitObjAngle, m.faceAngle.y, 4)
end
o.oPosX = m.pos.x + sins(e.rosalina.orbitObjAngle) * e.rosalina.orbitObjDist
o.oPosZ = m.pos.z + coss(e.rosalina.orbitObjAngle) * e.rosalina.orbitObjDist
o.oPosY = approach_f32_asymptotic(o.oPosY, m.pos.y + 50, 0.25)
obj_set_vel(o, 0, 0, 0)
o.oForwardVel = 0
o.oIntangibleTimer = -1
if m.playerIndex == 0 and e.rosalina.orbitObjDist <= 80 then
e.rosalina.orbitObjActive = false
o.oIntangibleTimer = 0
if m.action & (ACT_FLAG_INVULNERABLE | ACT_FLAG_INTANGIBLE) ~= 0 or m.action & ACT_GROUP_MASK >= ACT_GROUP_SUBMERGED then
m.usedObj = nil
else
o.oIntangibleTimer = 0
m.interactObj = o
m.usedObj = o
if o.oSyncID ~= 0 then network_send_object(o, true) end
if m.action & ACT_FLAG_AIR == 0 then
set_mario_action(m, ACT_HOLD_IDLE, 0)
mario_grab_used_object(m)
else
set_mario_action(m, ACT_HOLD_FREEFALL, 0)
mario_grab_used_object(m)
end
end
end
end
end
---@param m MarioState
function rosalina_before_action(m, action)
if not action then return end
local e = gCharacterStates[m.playerIndex]
if spinOverrides[action] and m.controller.buttonDown & (Z_TRIG | A_BUTTON) == 0 and m.action ~= ACT_STEEP_JUMP then
return ACT_JUMP_TWIRL
end
if action & ACT_FLAG_AIR == 0 and not e.rosalina.canSpin then
play_sound_with_freq_scale(SOUND_GENERAL_COIN_SPURT_EU, m.marioObj.header.gfx.cameraToObject, 1.6)
if m.playerIndex == 0 then
spawn_sync_object(id_bhvSparkle, E_MODEL_SPARKLES_ANIMATION, m.pos.x, m.pos.y + 200, m.pos.z,
function(o) obj_scale(o, 0.75) end)
end
e.rosalina.canSpin = true
end
end
hook_mario_action(ACT_JUMP_TWIRL, act_jump_twirl, INT_KICK)

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,596 @@
-------------------
-- Spike Moveset --
-------------------
if not charSelect then return end
-----------------
-- Spike Bombs --
-----------------
_G.ACT_SPIKE_PLACE_BOMB = allocate_mario_action(ACT_GROUP_STATIONARY | ACT_FLAG_STATIONARY)
_G.ACT_SPIKE_PLACE_BOMB_AIR = allocate_mario_action(ACT_GROUP_AIRBORNE | ACT_FLAG_AIR | ACT_FLAG_ALLOW_VERTICAL_WIND_ACTION)
_G.ACT_BOMB_JUMP = allocate_mario_action(ACT_GROUP_AIRBORNE | ACT_FLAG_AIR | ACT_FLAG_ALLOW_VERTICAL_WIND_ACTION)
---@param m MarioState
local function act_spike_place_bomb(m)
if (not m) then return 0 end
if (m.input & INPUT_UNKNOWN_10) ~= 0 then
return drop_and_set_mario_action(m, ACT_SHOCKWAVE_BOUNCE, 0)
end
if (m.input & INPUT_OFF_FLOOR) ~= 0 then
return drop_and_set_mario_action(m, ACT_FREEFALL, 0)
end
m.actionTimer = m.actionTimer + 1
if m.playerIndex == 0 and m.actionTimer == 4 then
spike_spawn_bomb(m)
end
play_character_sound_if_no_flag(m, CHAR_SOUND_PUNCH_YAH, MARIO_ACTION_SOUND_PLAYED)
animated_stationary_ground_step(m, CHAR_ANIM_PLACE_LIGHT_OBJ, ACT_IDLE)
set_anim_to_frame(m, m.marioObj.header.gfx.animInfo.animFrame + 2)
return 0
end
---@param m MarioState
local function act_spike_place_bomb_air(m)
if (not m) then return 0 end
if (m.actionState == 0) then
play_character_sound_if_no_flag(m, CHAR_SOUND_PUNCH_YAH, MARIO_ACTION_SOUND_PLAYED)
set_character_animation(m, CHAR_ANIM_PLACE_LIGHT_OBJ)
if is_anim_past_frame(m, 10) ~= 0 then
m.actionState = 1
else
set_anim_to_frame(m, m.marioObj.header.gfx.animInfo.animFrame + 2)
end
else
set_character_animation(m, CHAR_ANIM_GENERAL_FALL)
end
update_air_without_turn(m)
m.actionTimer = m.actionTimer + 1
if m.playerIndex == 0 and m.actionTimer == 4 then
spike_spawn_bomb(m, true)
end
local result = perform_air_step(m, (m.actionState == 1 and AIR_STEP_CHECK_LEDGE_GRAB) or 0)
if result == AIR_STEP_LANDED then
if (check_fall_damage_or_get_stuck(m, ACT_HARD_BACKWARD_GROUND_KB) == 0) then
set_mario_action(m, ACT_FREEFALL_LAND, 0)
end
elseif result == AIR_STEP_HIT_WALL then
if (m.wall or gServerSettings.bouncyLevelBounds == BOUNCY_LEVEL_BOUNDS_OFF) then
mario_set_forward_vel(m, 0)
end
elseif result == AIR_STEP_GRABBED_LEDGE then
set_mario_action(m, ACT_LEDGE_GRAB, 0)
end
return 0
end
---@param m MarioState
function spike_attempt_explode_bomb(m)
local gIndex = network_global_index_from_local(m.playerIndex)
local o = obj_get_first_with_behavior_id(id_bhvSpikeBomb)
while o do
if o.globalPlayerIndex == gIndex then
o.oBreakableWallForce = 1
network_send_object(o, false)
return true
end
o = obj_get_next_with_same_behavior_id(o)
end
-- if invincible, prevent spawning bombs
if m.flags & MARIO_VANISH_CAP ~= 0 or m.invincTimer ~= 0 then
return true
end
return false
end
---@param m MarioState
function spike_spawn_bomb(m, air)
if spike_attempt_explode_bomb(m) then return end
local pos = gVec3fZero()
if air then
pos.x = m.pos.x
pos.y = m.pos.y - 133
pos.z = m.pos.z
else
pos.x = m.pos.x + 80 * sins(m.faceAngle.y)
pos.y = m.pos.y
pos.z = m.pos.z + 80 * coss(m.faceAngle.y)
end
spawn_sync_object(id_bhvSpikeBomb, E_MODEL_SPIKE_BOMB, pos.x, pos.y, pos.z, function(o)
o.oVelY = 0
o.oForwardVel = m.forwardVel
o.globalPlayerIndex = network_global_index_from_local(m.playerIndex)
end)
end
-- note that other players can end up in this action
---@param m MarioState
local function act_bomb_jump(m)
update_air_without_turn(m)
if m.actionState == 0 then
play_character_sound_if_no_flag(m, CHAR_SOUND_YAHOO_WAHA_YIPPEE, MARIO_ACTION_SOUND_PLAYED)
set_character_animation(m, CHAR_ANIM_FORWARD_SPINNING)
if m.health <= 0xFF then
m.actionState = 1
end
else
m.peakHeight = m.pos.y + 10000 -- force falling sound
play_far_fall_sound(m)
set_character_animation(m, CHAR_ANIM_AIRBORNE_ON_STOMACH)
end
local result = perform_air_step(m, (m.actionState == 0 and AIR_STEP_CHECK_HANG | AIR_STEP_CHECK_LEDGE_GRAB) or 0)
if result == AIR_STEP_LANDED then
if m.actionState ~= 0 then
set_mario_action(m, ACT_HARD_FORWARD_GROUND_KB, 0)
elseif (check_fall_damage_or_get_stuck(m, ACT_HARD_FORWARD_GROUND_KB) == 0) then
set_mario_action(m, ACT_TRIPLE_JUMP_LAND, 0)
end
elseif result == AIR_STEP_HIT_WALL then
if (m.wall or gServerSettings.bouncyLevelBounds == BOUNCY_LEVEL_BOUNDS_OFF) then
mario_set_forward_vel(m, 0)
end
elseif result == AIR_STEP_GRABBED_LEDGE then
set_mario_action(m, ACT_LEDGE_GRAB, 0)
elseif result == AIR_STEP_GRABBED_CEILING then
set_mario_action(m, ACT_START_HANGING, 0)
end
if m.actionState ~= 0 then
m.actionTimer = m.actionTimer + 1
m.marioObj.header.gfx.angle.x = m.actionTimer * 0x1000 - 0x4000
m.marioObj.header.gfx.angle.y = m.faceAngle.y + m.actionTimer * 0x800
m.marioObj.header.gfx.angle.z = m.actionTimer * 0x1200
else
m.actionTimer = 0
end
return 0
end
-- Bomb object
local spikeColObjLists = {
OBJ_LIST_GENACTOR,
OBJ_LIST_PUSHABLE,
OBJ_LIST_SURFACE,
OBJ_LIST_DESTRUCTIVE
}
E_MODEL_SPIKE_BOMB = smlua_model_util_get_id("spike_bomb_geo")
---@param o Object
function bhv_spike_bomb_init(o)
o.oFlags = (OBJ_FLAG_UPDATE_GFX_POS_AND_ANGLE | OBJ_FLAG_COMPUTE_DIST_TO_MARIO | OBJ_FLAG_HOLDABLE)
o.oFaceAngleRoll = 0
o.oMoveAngleRoll = 0
o.oGravity = 2.5
--o.oBounciness = 0
o.oFriction = 0
--o.oDragStrength = 0.5
o.oBuoyancy = 1.3
o.oWallHitboxRadius = 60
local hitbox = get_temp_object_hitbox()
hitbox.interactType = INTERACT_GRABBABLE
hitbox.hurtboxRadius = 0
hitbox.hurtboxHeight = 0
hitbox.downOffset = 0
hitbox.radius = 65
hitbox.height = 133
hitbox.damageOrCoinValue = 0
obj_set_hitbox(o, hitbox)
o.oInteractionSubtype = (INT_SUBTYPE_KICKABLE | INT_SUBTYPE_NOT_GRABBABLE)
network_init_object(o, true, { 'globalPlayerIndex', 'oBreakableWallForce' })
end
---@param o Object
function bhv_spike_bomb_loop(o)
local m = gMarioStates[network_local_index_from_global(o.globalPlayerIndex)]
if is_player_active(m) == 0 or ((m.playerIndex == 0 or o.oHeldState ~= HELD_FREE) and (m.action == ACT_SPIKE_PLACE_BOMB or m.action == ACT_SPIKE_PLACE_BOMB_AIR)
and (m.actionTimer <= 1 or m.controller.buttonPressed & B_BUTTON ~= 0)) then
o.oBreakableWallForce = 1
if m.playerIndex == 0 then
network_send_object(o, false)
end
end
-- only allow us to pick up our own bombs
-- (holding another player's bomb makes it appear using our own colors, that's literally the only reason this restriction exists)
if m.playerIndex ~= 0 then
o.oInteractionSubtype = o.oInteractionSubtype | INT_SUBTYPE_NOT_GRABBABLE
else
o.oInteractionSubtype = o.oInteractionSubtype & ~INT_SUBTYPE_NOT_GRABBABLE
end
if o.oHeldState == HELD_FREE then
cur_obj_enable_rendering()
-- become tangible if we were untangible, but not if timer is set
if o.oIntangibleTimer == -1 then o.oIntangibleTimer = 0 end
local colFlags = object_step()
local floor = nil
if colFlags & OBJ_COL_FLAG_GROUNDED ~= 0 then
floor = collision_find_floor(o.oPosX, o.oPosY, o.oPosZ)
if o.oAction == 1 or (floor == nil or floor.type == SURFACE_BURNING or floor.type == SURFACE_DEATH_PLANE or floor.type == SURFACE_VERTICAL_WIND) then
o.oBreakableWallForce = 1
end
elseif colFlags & OBJ_COL_FLAG_HIT_WALL ~= 0 then
if o.oAction == 1 then
o.oBreakableWallForce = 1
end
end
if o.oAction == 0 and o.oTimer == 1 then
cur_obj_play_sound_1(SOUND_AIR_BOBOMB_LIT_FUSE)
end
if ((o.oInteractStatus & INT_STATUS_INTERACTED) ~= 0) then
if ((o.oInteractStatus & INT_STATUS_MARIO_UNK1) ~= 0) then
local player = nearest_player_to_object(o)
if (player) then
o.oMoveAngleYaw = player.header.gfx.angle.y
end
o.oForwardVel = 25.0
o.oVelY = 30.0
o.oFriction = 1
cur_obj_change_action(1)
end
if ((o.oInteractStatus & INT_STATUS_TOUCHED_BOB_OMB) ~= 0) then
o.oBreakableWallForce = 1
end
end
-- object collision
local collide = 0
for i, list in ipairs(spikeColObjLists) do
local o2 = obj_get_first(list)
while o2 do
if o ~= o2 and o2.oInteractStatus & INT_STATUS_INTERACTED == 0 and o2.oHeldState == HELD_FREE and o2.oInteractType ~= INTERACT_TEXT and ((floor and floor.object == o2) or obj_check_hitbox_overlap(o, o2)) then
if floor == nil or floor.object ~= o2 then
collide = 1
end
local didBombInteract = spike_bomb_interaction(o2, o)
if didBombInteract then
collide = 1
elseif (o2.oInteractType == INTERACT_BREAKABLE or o2.oInteractType == INTERACT_GRABBABLE or obj_is_attackable(o2)) then
o2.oInteractStatus = o2.oInteractStatus | ATTACK_FAST_ATTACK | INT_STATUS_WAS_ATTACKED |
INT_STATUS_INTERACTED | INT_STATUS_TOUCHED_BOB_OMB
end
end
o2 = obj_get_next(o2)
end
end
if o.oAction ~= 2 and (o.oBreakableWallForce ~= 0 or collide ~= 0) then
cur_obj_change_action(2)
end
o.oInteractStatus = 0
-- explosion action
if o.oAction == 2 then
local SCALE_TO_BOBOMB = 3 -- explosion is this many times larger than a normal bob-omb
obj_set_billboard(o)
o.oGravity = 0
o.oVelY = 0
o.oForwardVel = 0
if o.oInteractType ~= INTERACT_DAMAGE then
o.oInteractType = INTERACT_DAMAGE
obj_set_model_extended(o, E_MODEL_EXPLOSION)
local hitbox = get_temp_object_hitbox()
hitbox.interactType = INTERACT_DAMAGE
hitbox.hurtboxRadius = 150 * SCALE_TO_BOBOMB
hitbox.hurtboxHeight = 150 * SCALE_TO_BOBOMB
hitbox.downOffset = 150 * SCALE_TO_BOBOMB
hitbox.radius = 150 * SCALE_TO_BOBOMB
hitbox.height = 150 * SCALE_TO_BOBOMB
hitbox.damageOrCoinValue = 0
obj_set_hitbox(o, hitbox)
bhv_explosion_init()
end
if m.playerIndex == 0 or gServerSettings.playerInteractions == PLAYER_INTERACTIONS_PVP then
-- deal damage based on distance to explosion
local dist = lateral_dist_between_objects(o, gMarioStates[0].marioObj)
o.oDamageOrCoinValue = math.ceil(math.clamp(1 - (dist / (200 * SCALE_TO_BOBOMB)), 0, 1) * 4)
else
o.oDamageOrCoinValue = 0 -- deal no damage
end
if o.oTimer == 9 then
bhv_explosion_loop()
end
o.oOpacity = o.oOpacity - 14
cur_obj_scale((o.oTimer / 9.0 + 1) * SCALE_TO_BOBOMB)
o.oAnimState = o.oAnimState + 1
end
elseif o.oHeldState == HELD_HELD then
cur_obj_disable_rendering_and_become_intangible(o)
local heldM = gMarioStates[o.heldByPlayerIndex]
if (heldM.playerIndex == 0 and m.playerIndex ~= 0) or (o.oBreakableWallForce ~= 0 and heldM and heldM.playerIndex == 0) then
mario_drop_held_object(heldM)
o.oPosX, o.oPosY, o.oPosZ = heldM.pos.x, heldM.pos.y, heldM.pos.z
end
elseif o.oHeldState == HELD_DROPPED then
cur_obj_change_action(0)
o.oTimer = 1
o.oHeldState = HELD_FREE
o.oFaceAngleYaw = o.oMoveAngleYaw
elseif o.oHeldState == HELD_THROWN then
o.oForwardVel = 25.0
o.oVelY = 30.0
cur_obj_change_action(1)
o.oHeldState = HELD_FREE
o.oFaceAngleYaw = o.oMoveAngleYaw
end
end
id_bhvSpikeBomb = hook_behavior(nil, OBJ_LIST_DESTRUCTIVE, true, bhv_spike_bomb_init, bhv_spike_bomb_loop, "bhvSpikeBomb")
-------------------
-- Spike Hammers --
-------------------
-- hammer object
E_MODEL_SPIKE_HAMMER = smlua_model_util_get_id("spike_hammer_geo")
---@param o Object
function bhv_spike_hammer_init(o)
o.oFlags = (OBJ_FLAG_UPDATE_GFX_POS_AND_ANGLE | OBJ_FLAG_COMPUTE_DIST_TO_MARIO | OBJ_FLAG_SET_FACE_ANGLE_TO_MOVE_ANGLE)
o.oGravity = -4
o.oBounciness = 0
o.oFriction = 1
o.oDragStrength = 0
o.oBuoyancy = 0
o.oWallHitboxRadius = 60
cur_obj_scale(0.75)
local hitbox = get_temp_object_hitbox()
hitbox.interactType = INTERACT_DAMAGE
hitbox.hurtboxRadius = 0
hitbox.hurtboxHeight = 0
hitbox.downOffset = 100
hitbox.radius = 100
hitbox.height = 200
hitbox.damageOrCoinValue = 2
local prevIntangibleTimer = o.oIntangibleTimer
obj_set_hitbox(o, hitbox)
o.oIntangibleTimer = prevIntangibleTimer
network_init_object(o, true, { 'globalPlayerIndex' })
end
---@param o Object
function bhv_spike_hammer_loop(o)
cur_obj_update_floor_and_walls()
cur_obj_move_standard(60)
if o.oMoveFlags & (OBJ_MOVE_HIT_WALL | OBJ_MOVE_MASK_ON_GROUND | OBJ_MOVE_UNDERWATER_ON_GROUND) ~= 0 then
spawn_mist_particles()
obj_mark_for_deletion(o)
end
if o.oTimer == 1 then
cur_obj_play_sound_1(SOUND_ACTION_SIDE_FLIP_UNK)
end
-- Don't do damage to others unless PVP is on
local m = gMarioStates[network_local_index_from_global(o.globalPlayerIndex)]
if m.playerIndex ~= 0 and gServerSettings.playerInteractions ~= PLAYER_INTERACTIONS_PVP then
o.oDamageOrCoinValue = 0
else
o.oDamageOrCoinValue = 2
end
o.oMoveAnglePitch = o.oMoveAnglePitch + 0x1000
-- object collision
local collide = 0
for i, list in ipairs(spikeColObjLists) do
local o2 = obj_get_first(list)
while o2 do
if o ~= o2 and o2.oInteractStatus & INT_STATUS_INTERACTED == 0 and o2.oHeldState == HELD_FREE and o2.oInteractType ~= INTERACT_TEXT and obj_check_hitbox_overlap(o, o2) then
collide = 1
local didHammerInteract = spike_hammer_interaction(o2, o)
if (not didHammerInteract) and (o2.oInteractType == INTERACT_BREAKABLE or o2.oInteractType == INTERACT_GRABBABLE or obj_is_attackable(o2)) then
o2.oInteractStatus = o2.oInteractStatus | ATTACK_GROUND_POUND_OR_TWIRL | INT_STATUS_WAS_ATTACKED |
INT_STATUS_INTERACTED | INT_STATUS_TOUCHED_BOB_OMB
end
end
o2 = obj_get_next(o2)
end
end
if collide ~= 0 or o.oInteractStatus ~= 0 then
spawn_mist_particles()
obj_mark_for_deletion(o)
end
o.oInteractStatus = 0
end
id_bhvSpikeHammer = hook_behavior(nil, OBJ_LIST_DESTRUCTIVE, true, bhv_spike_hammer_init, bhv_spike_hammer_loop, "bhvSpikeHammer")
---@param m MarioState
function spike_update(m)
if m.controller.buttonPressed & B_BUTTON ~= 0 and (m.action == ACT_GROUND_POUND or (m.action == ACT_LONG_JUMP and m.controller.buttonDown & Z_TRIG ~= 0)) then
m.vel.y = 20
m.forwardVel = 0
if spike_attempt_explode_bomb(m) then
set_mario_action(m, ACT_JUMP_KICK, 0)
else
set_mario_action(m, ACT_SPIKE_PLACE_BOMB_AIR, 0)
end
end
-- throw hammer when punching
if m.playerIndex == 0 and (m.action == ACT_PUNCHING or m.action == ACT_MOVE_PUNCHING)
and m.actionArg == 1 and m.marioObj.header.gfx.animInfo.animFrame == 2 and m.heldObj == nil then
spawn_sync_object(id_bhvSpikeHammer, E_MODEL_SPIKE_HAMMER, m.pos.x, m.pos.y + 80, m.pos.z, function(o)
o.oVelY = 50
o.oForwardVel = 40
o.oMoveAngleYaw = m.faceAngle.y
o.oMoveAnglePitch, o.oMoveAngleRoll = 0, 0
o.oIntangibleTimer = 7
o.globalPlayerIndex = network_global_index_from_local(m.playerIndex)
end)
end
end
---@param m MarioState
---@param action integer
function spike_before_action(m, action)
if (action == ACT_PUNCHING or action == ACT_MOVE_PUNCHING) and m.controller.buttonDown & Z_TRIG ~= 0 then
if not spike_attempt_explode_bomb(m) then
return ACT_SPIKE_PLACE_BOMB
end
end
end
-- handle player interaction with spike's bomb
---@param m MarioState
---@param o Object
---@param type integer
---@param value boolean
function player_bomb_interact(m, o, type, value)
if obj_has_behavior_id(o, id_bhvSpikeBomb) ~= 0 and type == INTERACT_DAMAGE and value then
if m.pos.y > o.oPosY and o.oDamageOrCoinValue >= 4 and m.action & ACT_FLAG_AIR ~= 0 then
m.invincTimer = math.max(m.invincTimer, 3)
m.faceAngle.y = m.intendedYaw
set_mario_action(m, ACT_BOMB_JUMP, 0)
m.vel.y = 69
m.forwardVel = 16
if m.playerIndex == 0 then
o.oIntangibleTimer = 3 -- needed for arena
end
else
if m.action & (ACT_FLAG_AIR | ACT_FLAG_WATER_OR_TEXT | ACT_FLAG_METAL_WATER) == 0 then
if m.action == ACT_FORWARD_GROUND_KB or m.action == ACT_HARD_FORWARD_GROUND_KB or m.action == ACT_SOFT_FORWARD_GROUND_KB then
set_mario_action(m, ACT_HARD_FORWARD_AIR_KB, 0)
else
set_mario_action(m, ACT_HARD_BACKWARD_AIR_KB, 0)
end
end
m.forwardVel = o.oDamageOrCoinValue * 10
m.vel.y = o.oDamageOrCoinValue * 20
end
end
end
hook_event(HOOK_ON_INTERACT, player_bomb_interact)
-- prevent player interaction with Spike's bomb if player interaction is off (owner still interacts)
---@param m MarioState
---@param o Object
---@param type integer
function player_bomb_hammer_allow_interact(m, o, type)
if (obj_has_behavior_id(o, id_bhvSpikeBomb) ~= 0 or obj_has_behavior_id(o, id_bhvSpikeHammer) ~= 0) and type == INTERACT_DAMAGE then
local m2 = gMarioStates[network_local_index_from_global(o.globalPlayerIndex)]
if m.playerIndex ~= m2.playerIndex and gServerSettings.playerInteractions == PLAYER_INTERACTIONS_NONE then
return false
end
end
end
hook_event(HOOK_ALLOW_INTERACT, player_bomb_hammer_allow_interact)
-- handle other object interactions with spike's bomb
---@param o Object
---@param bomb Object
function spike_bomb_interaction(o, bomb)
if obj_has_behavior_id(o, id_bhvSmallWhomp) ~= 0 then
o.oNumLootCoins = 5
obj_spawn_loot_yellow_coins(o, 5, 20)
o.oAction = 8
return true
end
if obj_has_behavior_id(o, id_bhvGoomba) ~= 0 and o.oGoombaSize == 1 then
o.oInteractStatus = o.oInteractStatus | ATTACK_GROUND_POUND_OR_TWIRL | INT_STATUS_WAS_ATTACKED |
INT_STATUS_INTERACTED
return true
end
-- Is this too much?
if obj_has_behavior_id(o, id_bhvThwomp) ~= 0 or obj_has_behavior_id(o, id_bhvThwomp2) ~= 0 then
o.oNumLootCoins = 5
obj_spawn_loot_yellow_coins(o, 5, 20)
spawn_mist_particles_variable(0, 0, 100)
spawn_triangle_break_particles(20, 138, 3, 4)
cur_obj_shake_screen(SHAKE_POS_SMALL)
create_sound_spawner(SOUND_OBJ_THWOMP)
obj_mark_for_deletion(o)
return true
end
if obj_has_behavior_id(o, id_bhvWfBreakableWallLeft) ~= 0
or obj_has_behavior_id(o, id_bhvWfBreakableWallRight) ~= 0 then
o.oBreakableWallForce = 1
return true
end
if obj_has_behavior_id(o, id_bhvChuckya) ~= 0 then
o.oAction = 2
o.oForwardVel = 30
o.oMoveAngleYaw = obj_angle_to_object(o, bomb) + 0x8000
o.oVelY = 10
o.oChuckyaUnk88 = 3 -- auto throw mario
obj_init_animation(o, 2)
return true
end
if obj_has_behavior_id(o, id_bhvChainChomp) ~= 0 then
o.oInteractStatus = o.oInteractStatus | ATTACK_FAST_ATTACK |
INT_STATUS_TOUCHED_BOB_OMB | INT_STATUS_WAS_ATTACKED | INT_STATUS_INTERACTED
return true
end
if o.oInteractType == INTERACT_BULLY then
o.oBullyLastNetworkPlayerIndex = bomb.globalPlayerIndex
o.oForwardVel = 50
o.oMoveAngleYaw = obj_angle_to_object(o, bomb) + 0x8000
o.oVelY = 20
o.oInteractStatus = o.oInteractStatus | ATTACK_FAST_ATTACK |
INT_STATUS_TOUCHED_BOB_OMB | INT_STATUS_WAS_ATTACKED | INT_STATUS_INTERACTED
return true
end
end
-- handle object interaction's with spike's hammer (literally just bullies and spike's objects)
---@param o Object
---@param hammer Object
function spike_hammer_interaction(o, hammer)
if o.oInteractType == INTERACT_BULLY then
o.oBullyLastNetworkPlayerIndex = hammer.globalPlayerIndex
o.oForwardVel = 30
o.oMoveAngleYaw = hammer.oMoveAngleYaw
o.oInteractStatus = o.oInteractStatus | ATTACK_FAST_ATTACK |
INT_STATUS_TOUCHED_BOB_OMB | INT_STATUS_WAS_ATTACKED | INT_STATUS_INTERACTED
return true
end
-- prevent interaction early (since punching is grab, and failing the grab would otherwise explode the bomb)
if obj_has_behavior_id(o, id_bhvSpikeBomb) ~= 0 and hammer.oIntangibleTimer ~= 0 then
return true
end
-- break both hammers
if obj_has_behavior_id(o, id_bhvSpikeHammer) ~= 0 then
o.oInteractStatus = o.oInteractStatus | INT_STATUS_INTERACTED
return true
end
end
hook_mario_action(ACT_SPIKE_PLACE_BOMB, { every_frame = act_spike_place_bomb })
hook_mario_action(ACT_SPIKE_PLACE_BOMB_AIR, { every_frame = act_spike_place_bomb_air })
hook_mario_action(ACT_BOMB_JUMP, { every_frame = act_bomb_jump })

View file

@ -0,0 +1,90 @@
----------------------
-- Toadette Moveset --
----------------------
if not charSelect then return end
function toadette_before_phys_step(m)
local hScale = 1.0
local vScale = 1.0
-- faster ground movement
if (m.action & ACT_FLAG_MOVING) ~= 0 then
hScale = hScale * 1.05
end
-- slower holding item
if m.heldObj then
m.vel.y = m.vel.y - 2.0
hScale = hScale * 0.9
if (m.action & ACT_FLAG_AIR) ~= 0 then
hScale = hScale * 0.9
end
end
m.vel.x = m.vel.x * hScale
m.vel.y = m.vel.y * vScale
m.vel.z = m.vel.z * hScale
end
function toadette_on_set_action(m)
local e = gCharacterStates[m.playerIndex]
-- wall kick height based on how fast toadette is going
if m.action == ACT_WALL_KICK_AIR and m.prevAction ~= ACT_HOLDING_POLE and m.prevAction ~= ACT_CLIMBING_POLE then
m.vel.y = m.vel.y * 0.8
m.vel.y = m.vel.y + e.toadette.averageForwardVel * 0.8
return
end
-- more distance on dive and long jump
if m.action == ACT_DIVE or m.action == ACT_LONG_JUMP then
m.forwardVel = m.forwardVel * 1
end
-- less height on jumps
if m.action == ACT_JUMP or m.action == ACT_DOUBLE_JUMP or m.action == ACT_TRIPLE_JUMP or m.action == ACT_SPECIAL_TRIPLE_JUMP or m.action == ACT_STEEP_JUMP or m.action == ACT_RIDING_SHELL_JUMP or m.action == ACT_BACKFLIP or m.action == ACT_WALL_KICK_AIR or m.action == ACT_LONG_JUMP then
m.vel.y = m.vel.y * 1
-- prevent from getting stuck on platform
if m.marioObj.platform 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 then
m.pos.y = m.pos.y + 10
end
end
end
function toadette_update(m)
local e = gCharacterStates[m.playerIndex]
-- track average forward velocity
if e.toadette.averageForwardVel > m.forwardVel then
e.toadette.averageForwardVel = e.toadette.averageForwardVel * 0.93 + m.forwardVel * 0.07
else
e.toadette.averageForwardVel = m.forwardVel
end
-- keep your momentum for a while
if m.action == ACT_WALKING and m.forwardVel > 30 then
mario_set_forward_vel(m, m.forwardVel + 0.8)
end
-- faster flip during ground pound
if m.action == ACT_GROUND_POUND then
m.marioObj.header.gfx.animInfo.animAccel = 32768 * 4
if m.actionTimer < 10 then
m.actionTimer = m.actionTimer + 1
end
end
-- Floaty
if m.vel.y < 0 and (m.action == ACT_JUMP or m.action == ACT_DOUBLE_JUMP or m.action == ACT_TRIPLE_JUMP or m.action == ACT_HOLD_JUMP) then
m.vel.y = m.vel.y + 0.9
end
end

View file

@ -0,0 +1,302 @@
-------------------------
-- Wapeach Axe Attacks --
-------------------------
if not charSelect then return end
_G.ACT_AXE_CHOP = allocate_mario_action(ACT_GROUP_STATIONARY | ACT_FLAG_STATIONARY)
_G.ACT_AXE_SPIN = allocate_mario_action(ACT_GROUP_MOVING | ACT_FLAG_MOVING | ACT_FLAG_ATTACKING)
_G.ACT_AXE_SPIN_AIR = allocate_mario_action(ACT_FLAG_ATTACKING | ACT_FLAG_AIR | ACT_GROUP_AIRBORNE)
_G.ACT_AXE_SPIN_DIZZY = allocate_mario_action(ACT_GROUP_MOVING | ACT_FLAG_MOVING)
---@param o Object
local function bhv_axe_attack_init(o)
o.oFlags = OBJ_FLAG_UPDATE_GFX_POS_AND_ANGLE | OBJ_FLAG_SET_FACE_ANGLE_TO_MOVE_ANGLE
o.oDamageOrCoinValue = 2
o.oNumLootCoins = 0
o.oHealth = 0
o.hitboxRadius = 60
o.hitboxHeight = 80
o.hurtboxRadius = 60
o.hurtboxHeight = 80
o.hitboxDownOffset = 0
o.oInteractType = 0
cur_obj_scale(1)
cur_obj_become_tangible()
network_init_object(o, true, {})
end
---@param o Object
local function bhv_axe_attack_loop(o)
local m = gMarioStates[network_local_index_from_global(o.globalPlayerIndex)]
local handPos = gVec3fZero()
if not get_mario_anim_part_pos(m, MARIO_ANIM_PART_RIGHT_HAND, handPos) then
vec3f_copy(handPos, m.pos)
end
local dist = 185.0
local axeDir = gVec3fZero()
vec3f_copy(axeDir, handPos)
vec3f_sub(axeDir, m.pos)
vec3f_normalize(axeDir)
vec3f_mul(axeDir, 120)
o.oPosX = handPos.x + sins(m.faceAngle.y) * coss(m.faceAngle.x) * dist
o.oPosY = handPos.y - axeDir.y
o.oPosZ = handPos.z + coss(m.faceAngle.y) * coss(m.faceAngle.x) * dist
obj_process_attacks(o, bhvWapeachAxeAttacks)
if o.oTimer == 15 then
obj_mark_for_deletion(o)
end
end
local id_bhvAxeAttack = hook_behavior(nil, OBJ_LIST_DESTRUCTIVE, true, bhv_axe_attack_init, bhv_axe_attack_loop, "bhvWapeachAxeAttack")
---@param m MarioState
local function act_axe_chop(m)
local slope = -find_floor_slope(m, 0)
m.faceAngle.x = slope
m.marioObj.header.gfx.angle.x = slope
if m.actionTimer == 0 then
set_character_animation(m, CHAR_ANIM_BREAKDANCE)
smlua_anim_util_set_animation(m.marioObj, 'wapeach_axechop')
play_character_sound(m, CHAR_SOUND_YAHOO_WAHA_YIPPEE)
m.forwardVel = 0
end
--if m.actionTimer >= 14 and m.actionTimer <= 40 then m.marioBodyState.handState = 2 end
if m.actionTimer == 17 then
play_sound(SOUND_OBJ_POUNDING_LOUD, m.marioObj.header.gfx.cameraToObject)
if m.playerIndex == 0 then
local handPos = gVec3fZero()
if not get_mario_anim_part_pos(m, MARIO_ANIM_PART_RIGHT_HAND, handPos) then
vec3f_copy(handPos, m.pos)
end
spawn_sync_object(id_bhvAxeAttack, E_MODEL_NONE, handPos.x, handPos.y + 25, handPos.z, function(o)
o.globalPlayerIndex = m.marioObj.globalPlayerIndex
end)
end
-- shakey cam if you are close enough to petey (based on local player's camera)
if vec3f_length(m.marioObj.header.gfx.cameraToObject) < 2000 then
set_camera_shake_from_hit(SHAKE_SMALL_DAMAGE)
end
end
if is_anim_at_end(m) ~= 0 then
set_mario_action(m, ACT_IDLE, 0)
end
mario_set_forward_vel(m, approach_f32_symmetric(m.forwardVel, 0.0, 5.0))
local step = perform_ground_step(m)
if step == GROUND_STEP_LEFT_GROUND then
return set_mario_action(m, ACT_FREEFALL, 0)
end
m.actionTimer = m.actionTimer + 1
end
---@param m MarioState
local function act_axe_spin(m)
if m.actionTimer == 0 then
play_character_sound(m, CHAR_SOUND_YAHOO_WAHA_YIPPEE)
m.forwardVel = math.clamp(m.forwardVel + 21, 0, 850)
end
if m.controller.buttonPressed & B_BUTTON ~= 0 then
m.forwardVel = math.clamp(m.forwardVel + 7, 0, 850)
end
play_custom_anim(m, 'wapeach_axespin', math.clamp(m.forwardVel * 0x500, 0, 0x1F000))
set_mario_particle_flags(m, PARTICLE_DUST, 0)
if is_anim_past_frame(m, 1) ~= 0 then
play_sound(SOUND_ACTION_SPIN, m.marioObj.header.gfx.cameraToObject)
end
apply_slope_accel(m)
if m.intendedMag > 1 then
m.faceAngle.y = approach_s16_symmetric(m.faceAngle.y, m.intendedYaw, 0x200)
end
m.forwardVel = m.forwardVel - 1.0
mario_set_forward_vel(m, m.forwardVel)
local step = perform_ground_step(m)
if m.forwardVel < 20 and m.actionTimer >= 15 then
return set_mario_action(m, ACT_AXE_SPIN_DIZZY, 0)
end
if m.forwardVel >= 100 and m.actionState == 0 then
play_character_sound(m, CHAR_SOUND_TWIRL_BOUNCE)
m.actionState = 1
end
if step == GROUND_STEP_HIT_WALL then
set_mario_particle_flags(m, PARTICLE_TRIANGLE, 0)
mario_bonk_reflection(m, 0)
play_sound(SOUND_ACTION_HIT_3, m.marioObj.header.gfx.cameraToObject)
return set_mario_action(m, ACT_AXE_SPIN_DIZZY, 0)
end
if step == GROUND_STEP_LEFT_GROUND then
return set_mario_action(m, ACT_AXE_SPIN_AIR, 0)
end
local gfx = m.marioObj.header.gfx
local floorAngle = atan2s(m.floor.normal.z, m.floor.normal.x)
local floorSlope = radians_to_sm64(math.acos(m.floor.normal.y))
local speedTilt = math.clamp(m.forwardVel * 0x80, -0x500, 0x500)
gfx.angle.x = floorSlope * coss(floorAngle - m.faceAngle.y) + speedTilt
gfx.angle.z = floorSlope * -sins(floorAngle - m.faceAngle.y)
m.actionTimer = m.actionTimer + 1
end
local function act_axe_spin_air(m)
update_air_with_turn(m)
m.vel.y = m.vel.y + 2
play_custom_anim(m, 'wapeach_axespin', math.clamp(m.forwardVel * 0x500, 0, 0x1F000))
if is_anim_past_frame(m, 1) ~= 0 then
play_sound(SOUND_ACTION_SPIN, m.marioObj.header.gfx.cameraToObject)
end
if m.intendedMag > 1 then
m.faceAngle.y = approach_s16_symmetric(m.faceAngle.y, m.intendedYaw, 0x200)
end
m.forwardVel = math.clamp(m.forwardVel - 0.7, 0, 850)
mario_set_forward_vel(m, m.forwardVel)
local step = perform_air_step(m, 0)
if m.forwardVel < 20 and m.actionTimer >= 10 then
set_mario_action(m, ACT_THROWN_FORWARD, 0)
return
end
if m.forwardVel >= 100 and m.actionState == 0 then
play_character_sound(m, CHAR_SOUND_TWIRL_BOUNCE)
m.actionState = 1
end
if step == AIR_STEP_LANDED then
return set_mario_action(m, ACT_AXE_SPIN, 0)
end
if step == AIR_STEP_HIT_WALL then
mario_bonk_reflection(m, 1)
return set_mario_action(m, ACT_THROWN_BACKWARD, 0)
end
m.actionTimer = m.actionTimer + 1
end
E_MODEL_DIZZYCIRCLE = smlua_model_util_get_id("dizzy_circle_geo")
---@param o Object
local function bhv_dizzycircle_init(o)
o.oFlags = OBJ_FLAG_UPDATE_GFX_POS_AND_ANGLE -- Allows you to change the position and angle
end
---@param o Object
local function bhv_dizzycircle_loop(o)
smlua_anim_util_set_animation(o, 'dizzycircle_idle')
local m = nearest_mario_state_to_object(o)
o.oPosX = m.marioBodyState.headPos.x
o.oPosY = m.marioBodyState.headPos.y + 50
o.oPosZ = m.marioBodyState.headPos.z
--pM = gMarioStates[network_local_index_from_global(o.globalPlayerIndex)]
if o.oTimer > 42 then -- Deletes itself once the action changes
obj_mark_for_deletion(o)
end
end
local id_bhvDizzyCircle = hook_behavior(nil, OBJ_LIST_GENACTOR, true, bhv_dizzycircle_init, bhv_dizzycircle_loop, "bhvWapeachDizzyCircle")
local function act_axe_spin_dizzy(m)
if m.actionTimer == 1 then
play_character_sound(m, CHAR_SOUND_WHOA)
-- Spawn the spin effect
spawn_non_sync_object(id_bhvDizzyCircle, E_MODEL_DIZZYCIRCLE, m.marioBodyState.headPos.x,
m.marioBodyState.headPos.y, m.marioBodyState.headPos.z,
function(o)
o.parentObj = m.marioObj
o.globalPlayerIndex = m.marioObj.globalPlayerIndex
end)
end
if m.actionTimer >= 42 then
m.marioBodyState.eyeState = MARIO_EYES_DEAD
set_character_animation(m, CHAR_ANIM_LAND_ON_STOMACH)
smlua_anim_util_set_animation(m.marioObj, 'wapeach_flop')
if m.actionTimer == 52 then
play_sound(SOUND_ACTION_PAT_BACK, m.marioObj.header.gfx.cameraToObject)
play_character_sound(m, CHAR_SOUND_OOOF2)
end
if m.actionTimer > 52 and m.actionTimer < 111 then
apply_slope_accel(m)
m.forwardVel = m.forwardVel * 0.95
if m.forwardVel <= 0 then
m.forwardVel = 0
else
set_mario_particle_flags(m, PARTICLE_DUST, 0)
play_sound(SOUND_AIR_ROUGH_SLIDE, m.marioObj.header.gfx.cameraToObject)
end
elseif m.actionTimer >= 111 then
m.forwardVel = 0
if m.controller.buttonPressed & B_BUTTON ~= 0 or m.controller.buttonPressed & A_BUTTON ~= 0 then
set_mario_action(m, ACT_FORWARD_ROLLOUT, 0)
end
end
else
apply_slope_accel(m)
m.forwardVel = math.clamp(m.forwardVel, 0, 21)
if is_anim_past_frame(m, 1) ~= 0 then
play_sound(SOUND_ACTION_SPIN, m.marioObj.header.gfx.cameraToObject)
end
set_character_animation(m, CHAR_ANIM_BREAKDANCE)
smlua_anim_util_set_animation(m.marioObj, 'wapeach_dizzy')
end
mario_set_forward_vel(m, m.forwardVel)
local step = perform_ground_step(m)
if step == GROUND_STEP_LEFT_GROUND then
set_mario_action(m, ACT_THROWN_FORWARD, 0)
end
local gfx = m.marioObj.header.gfx
local floorAngle = atan2s(m.floor.normal.z, m.floor.normal.x)
local floorSlope = radians_to_sm64(math.acos(m.floor.normal.y))
gfx.angle.x = floorSlope * coss(floorAngle - m.faceAngle.y)
gfx.angle.z = floorSlope * -sins(floorAngle - m.faceAngle.y)
m.actionTimer = m.actionTimer + 1
end
local axeOverrides = {
[ACT_PUNCHING] = ACT_AXE_CHOP,
[ACT_MOVE_PUNCHING] = ACT_AXE_CHOP,
[ACT_SLIDE_KICK] = ACT_AXE_SPIN,
}
---@param m MarioState
---@param action integer
function wapeach_before_action(m, action, actionArg)
if axeOverrides[action] then
if (action == ACT_PUNCHING or action == ACT_MOVE_PUNCHING) and actionArg ~= 9 then return end
return axeOverrides[action]
end
end
hook_mario_action(ACT_AXE_CHOP, act_axe_chop)
hook_mario_action(ACT_AXE_SPIN, act_axe_spin)
hook_mario_action(ACT_AXE_SPIN_AIR, act_axe_spin_air)
hook_mario_action(ACT_AXE_SPIN_DIZZY, act_axe_spin_dizzy)

View file

@ -0,0 +1,54 @@
-------------------
-- Yoshi Moveset --
-------------------
if not charSelect then return end
-- Flutterable actions, these don't match the DS flutterable actions
local flutterActs = {
[ACT_JUMP] = true,
[ACT_DOUBLE_JUMP] = true,
[ACT_TRIPLE_JUMP] = true,
[ACT_LONG_JUMP] = true,
[ACT_FREEFALL] = true
}
_G.ACT_FLUTTER = allocate_mario_action(ACT_FLAG_AIR | ACT_FLAG_ALLOW_VERTICAL_WIND_ACTION | ACT_GROUP_AIRBORNE)
YOSHI_ANIM_FLUTTER = 'yoshi_flutter_jump'
---@param m MarioState
function act_flutter(m)
-- End flutter after 1 second
if m.actionTimer >= 30 or (m.input & INPUT_A_DOWN) == 0 then
if m.actionTimer < 30 then
play_character_sound(m, CHAR_SOUND_MAX) -- Stop sample after letting go of A
end
return set_mario_action(m, ACT_FREEFALL, 0)
end
local ended = common_air_action_step(m, ACT_JUMP_LAND, CHAR_ANIM_RUNNING_UNUSED, 0) ~= 0 -- Checks if the action ended earlier due to forced actions like bonking or landing
if ended then
play_character_sound(m, CHAR_SOUND_MAX) -- Stop sample after landing
elseif m.actionTimer == 0 then
play_character_sound(m, YOSHI_SOUND_FLUTTER) -- Play audio sample
end
smlua_anim_util_set_animation(m.marioObj, YOSHI_ANIM_FLUTTER) -- Sets the animation
m.marioBodyState.eyeState = MARIO_EYES_CLOSED -- Eye State
m.vel.y = approach_f32(m.vel.y, m.actionTimer / 1.25, 8, 8) -- Height increases faster as the 1 second passes
m.marioObj.header.gfx.animInfo.animAccel = 0x10000 * 2 -- Anim Speed
m.actionTimer = m.actionTimer + 1
return false
end
---@param m MarioState
function yoshi_update(m)
if m.prevAction & ACT_FLAG_AIR == 0 and m.action & ACT_FLAG_AIR ~= 0 and flutterActs[m.action] and m.controller.buttonDown & A_BUTTON ~= 0 and m.vel.y < 0 then
set_mario_action(m, ACT_FLUTTER, 0)
end
end
hook_mario_action(ACT_FLUTTER, act_flutter)

Some files were not shown because too many files have changed in this diff Show more