mirror of
https://github.com/coop-deluxe/sm64coopdx.git
synced 2026-04-06 18:26:28 +00:00
Merge branch 'dev' of https://github.com/coop-deluxe/sm64coopdx into shift
This commit is contained in:
commit
a6cf3f202a
669 changed files with 33720 additions and 2336 deletions
|
|
@ -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 --
|
||||
-----------
|
||||
|
|
|
|||
93
mods/char-select-extra-chars-plus/!class-definitions.lua
Normal file
93
mods/char-select-extra-chars-plus/!class-definitions.lua
Normal 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`
|
||||
10
mods/char-select-extra-chars-plus/!environments.lua
Normal file
10
mods/char-select-extra-chars-plus/!environments.lua
Normal 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 })
|
||||
|
||||
31
mods/char-select-extra-chars-plus/!misc-functions.lua
Normal file
31
mods/char-select-extra-chars-plus/!misc-functions.lua
Normal 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
|
||||
148
mods/char-select-extra-chars-plus/a-hitboxes.lua
Normal file
148
mods/char-select-extra-chars-plus/a-hitboxes.lua
Normal 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
|
||||
52
mods/char-select-extra-chars-plus/a-movesets-vars.lua
Normal file
52
mods/char-select-extra-chars-plus/a-movesets-vars.lua
Normal 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
|
||||
BIN
mods/char-select-extra-chars-plus/actors/dizzy_circle_geo.bin
Normal file
BIN
mods/char-select-extra-chars-plus/actors/dizzy_circle_geo.bin
Normal file
Binary file not shown.
BIN
mods/char-select-extra-chars-plus/actors/dk_cap_geo.bin
Normal file
BIN
mods/char-select-extra-chars-plus/actors/dk_cap_geo.bin
Normal file
Binary file not shown.
BIN
mods/char-select-extra-chars-plus/actors/dk_geo.bin
Normal file
BIN
mods/char-select-extra-chars-plus/actors/dk_geo.bin
Normal file
Binary file not shown.
BIN
mods/char-select-extra-chars-plus/actors/dk_metal_cap_geo.bin
Normal file
BIN
mods/char-select-extra-chars-plus/actors/dk_metal_cap_geo.bin
Normal file
Binary file not shown.
Binary file not shown.
BIN
mods/char-select-extra-chars-plus/actors/dk_wing_cap_geo.bin
Normal file
BIN
mods/char-select-extra-chars-plus/actors/dk_wing_cap_geo.bin
Normal file
Binary file not shown.
BIN
mods/char-select-extra-chars-plus/actors/foreman_spike_geo.bin
Normal file
BIN
mods/char-select-extra-chars-plus/actors/foreman_spike_geo.bin
Normal file
Binary file not shown.
BIN
mods/char-select-extra-chars-plus/actors/segasonic_cap_geo.bin
Normal file
BIN
mods/char-select-extra-chars-plus/actors/segasonic_cap_geo.bin
Normal file
Binary file not shown.
BIN
mods/char-select-extra-chars-plus/actors/segasonic_geo.bin
Normal file
BIN
mods/char-select-extra-chars-plus/actors/segasonic_geo.bin
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
mods/char-select-extra-chars-plus/actors/segasupersonic_geo.bin
Normal file
BIN
mods/char-select-extra-chars-plus/actors/segasupersonic_geo.bin
Normal file
Binary file not shown.
BIN
mods/char-select-extra-chars-plus/actors/spike_bomb_geo.bin
Normal file
BIN
mods/char-select-extra-chars-plus/actors/spike_bomb_geo.bin
Normal file
Binary file not shown.
BIN
mods/char-select-extra-chars-plus/actors/spike_hammer_geo.bin
Normal file
BIN
mods/char-select-extra-chars-plus/actors/spike_hammer_geo.bin
Normal file
Binary file not shown.
BIN
mods/char-select-extra-chars-plus/actors/wapeach_cap_geo.bin
Normal file
BIN
mods/char-select-extra-chars-plus/actors/wapeach_cap_geo.bin
Normal file
Binary file not shown.
BIN
mods/char-select-extra-chars-plus/actors/wapeach_geo.bin
Normal file
BIN
mods/char-select-extra-chars-plus/actors/wapeach_geo.bin
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
18912
mods/char-select-extra-chars-plus/anims-donkey.lua
Normal file
18912
mods/char-select-extra-chars-plus/anims-donkey.lua
Normal file
File diff suppressed because it is too large
Load diff
3032
mods/char-select-extra-chars-plus/custom-anims-sonic.lua
Normal file
3032
mods/char-select-extra-chars-plus/custom-anims-sonic.lua
Normal file
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
1936
mods/char-select-extra-chars-plus/main.lua
Normal file
1936
mods/char-select-extra-chars-plus/main.lua
Normal file
File diff suppressed because it is too large
Load diff
843
mods/char-select-extra-chars-plus/moveset-birdo.lua
Normal file
843
mods/char-select-extra-chars-plus/moveset-birdo.lua
Normal 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)
|
||||
55
mods/char-select-extra-chars-plus/moveset-daisy.lua
Normal file
55
mods/char-select-extra-chars-plus/moveset-daisy.lua
Normal 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)
|
||||
955
mods/char-select-extra-chars-plus/moveset-donkey-kong.lua
Normal file
955
mods/char-select-extra-chars-plus/moveset-donkey-kong.lua
Normal 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})
|
||||
|
|
@ -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
|
||||
59
mods/char-select-extra-chars-plus/moveset-peach.lua
Normal file
59
mods/char-select-extra-chars-plus/moveset-peach.lua
Normal 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)
|
||||
203
mods/char-select-extra-chars-plus/moveset-rosalina.lua
Normal file
203
mods/char-select-extra-chars-plus/moveset-rosalina.lua
Normal 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)
|
||||
1975
mods/char-select-extra-chars-plus/moveset-sonic.lua
Normal file
1975
mods/char-select-extra-chars-plus/moveset-sonic.lua
Normal file
File diff suppressed because it is too large
Load diff
596
mods/char-select-extra-chars-plus/moveset-spike.lua
Normal file
596
mods/char-select-extra-chars-plus/moveset-spike.lua
Normal 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 })
|
||||
90
mods/char-select-extra-chars-plus/moveset-toadette.lua
Normal file
90
mods/char-select-extra-chars-plus/moveset-toadette.lua
Normal 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
|
||||
302
mods/char-select-extra-chars-plus/moveset-wapeach.lua
Normal file
302
mods/char-select-extra-chars-plus/moveset-wapeach.lua
Normal 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)
|
||||
54
mods/char-select-extra-chars-plus/moveset-yoshi.lua
Normal file
54
mods/char-select-extra-chars-plus/moveset-yoshi.lua
Normal 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
Loading…
Add table
Reference in a new issue