sm64coopdx/mods/char-select-extra-chars-plus/moveset-donkey-kong.lua
Yuyake 1cb8cb8e5e
Some checks failed
Build coop / build-linux (push) Has been cancelled
Build coop / build-steamos (push) Has been cancelled
Build coop / build-windows-opengl (push) Has been cancelled
Build coop / build-windows-directx (push) Has been cancelled
Build coop / build-macos-arm (push) Has been cancelled
Build coop / build-macos-intel (push) Has been cancelled
Extra Characters PLUS v1.0 (#1076)
* Replaces Extra Characters

* Folder inside another folder fix
2026-01-11 02:20:45 +01:00

955 lines
No EOL
32 KiB
Lua

-------------------------
-- 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})