Path: blob/master/src/game/mario_actions_submerged.c
7858 views
#include <PR/ultratypes.h>12#include "sm64.h"3#include "level_update.h"4#include "memory.h"5#include "engine/math_util.h"6#include "area.h"7#include "save_file.h"8#include "sound_init.h"9#include "engine/surface_collision.h"10#include "interaction.h"11#include "mario.h"12#include "mario_step.h"13#include "camera.h"14#include "audio/external.h"15#include "behavior_data.h"16#include "level_table.h"17#include "rumble_init.h"1819#include "settings.h"2021#define MIN_SWIM_STRENGTH 16022#define MIN_SWIM_SPEED 16.0f2324static s16 sWasAtSurface = FALSE;25static s16 sSwimStrength = MIN_SWIM_STRENGTH;26static s16 sWaterCurrentSpeeds[] = { 28, 12, 8, 4 };2728static s16 sBobTimer;29static s16 sBobIncrement;30static f32 sBobHeight;3132static void set_swimming_at_surface_particles(struct MarioState *m, u32 particleFlag) {33s16 atSurface = m->pos[1] >= m->waterLevel - 130;3435if (atSurface) {36m->particleFlags |= particleFlag;37if (atSurface ^ sWasAtSurface) {38play_sound(SOUND_ACTION_UNKNOWN431, m->marioObj->header.gfx.cameraToObject);39}40}4142sWasAtSurface = atSurface;43}4445static s32 swimming_near_surface(struct MarioState *m) {46if (m->flags & MARIO_METAL_CAP) {47return FALSE;48}4950return (m->waterLevel - 80) - m->pos[1] < 400.0f;51}5253static f32 get_buoyancy(struct MarioState *m) {54f32 buoyancy = 0.0f;5556if (m->flags & MARIO_METAL_CAP) {57if (m->action & ACT_FLAG_INVULNERABLE) {58buoyancy = -2.0f;59} else {60buoyancy = -18.0f;61}62} else if (swimming_near_surface(m)) {63if (configImprovedSwimming) {64buoyancy = 2.0f;65}66else {67buoyancy = 1.25f;68}69} else if (!(m->action & ACT_FLAG_MOVING)) {70if (configImprovedSwimming) {71buoyancy = -0.5f;72}73else {74buoyancy = -2.0f;75}76}7778return buoyancy;79}8081static u32 perform_water_full_step(struct MarioState *m, Vec3f nextPos) {82struct Surface *wall;83struct Surface *ceil;84struct Surface *floor;85f32 ceilHeight;86f32 floorHeight;8788wall = resolve_and_return_wall_collisions(nextPos, 10.0f, 110.0f);89floorHeight = find_floor(nextPos[0], nextPos[1], nextPos[2], &floor);9091if (configApplyBugFixes > 1)92ceilHeight = vec3f_find_ceil(nextPos, nextPos[1], &ceil);93else94ceilHeight = vec3f_find_ceil(nextPos, floorHeight, &ceil);9596if (floor == NULL) {97return WATER_STEP_CANCELLED;98}99100if (nextPos[1] >= floorHeight) {101if (ceilHeight - nextPos[1] >= 160.0f) {102vec3f_copy(m->pos, nextPos);103m->floor = floor;104m->floorHeight = floorHeight;105106if (wall != NULL) {107return WATER_STEP_HIT_WALL;108} else {109return WATER_STEP_NONE;110}111}112113if (ceilHeight - floorHeight < 160.0f) {114return WATER_STEP_CANCELLED;115}116117//! Water ceiling downwarp118vec3f_set(m->pos, nextPos[0], ceilHeight - 160.0f, nextPos[2]);119m->floor = floor;120m->floorHeight = floorHeight;121return WATER_STEP_HIT_CEILING;122} else {123if (ceilHeight - floorHeight < 160.0f) {124return WATER_STEP_CANCELLED;125}126127vec3f_set(m->pos, nextPos[0], floorHeight, nextPos[2]);128m->floor = floor;129m->floorHeight = floorHeight;130return WATER_STEP_HIT_FLOOR;131}132}133134static void apply_water_current(struct MarioState *m, Vec3f step) {135s32 i;136f32 whirlpoolRadius = 2000.0f;137138if (m->floor->type == SURFACE_FLOWING_WATER) {139s16 currentAngle = m->floor->force << 8;140f32 currentSpeed = sWaterCurrentSpeeds[m->floor->force >> 8];141142step[0] += currentSpeed * sins(currentAngle);143step[2] += currentSpeed * coss(currentAngle);144}145146for (i = 0; i < 2; i++) {147struct Whirlpool *whirlpool = gCurrentArea->whirlpools[i];148if (whirlpool != NULL) {149f32 strength = 0.0f;150151f32 dx = whirlpool->pos[0] - m->pos[0];152f32 dy = whirlpool->pos[1] - m->pos[1];153f32 dz = whirlpool->pos[2] - m->pos[2];154155f32 lateralDist = sqrtf(dx * dx + dz * dz);156f32 distance = sqrtf(lateralDist * lateralDist + dy * dy);157158s16 pitchToWhirlpool = atan2s(lateralDist, dy);159s16 yawToWhirlpool = atan2s(dz, dx);160161yawToWhirlpool -= (s16)(0x2000 * 1000.0f / (distance + 1000.0f));162163if (whirlpool->strength >= 0) {164if (gCurrLevelNum == LEVEL_DDD && gCurrAreaIndex == 2) {165whirlpoolRadius = 4000.0f;166}167168if (distance >= 26.0f && distance < whirlpoolRadius) {169strength = whirlpool->strength * (1.0f - distance / whirlpoolRadius);170}171} else if (distance < 2000.0f) {172strength = whirlpool->strength * (1.0f - distance / 2000.0f);173}174175step[0] += strength * coss(pitchToWhirlpool) * sins(yawToWhirlpool);176step[1] += strength * sins(pitchToWhirlpool);177step[2] += strength * coss(pitchToWhirlpool) * coss(yawToWhirlpool);178}179}180}181182static u32 perform_water_step(struct MarioState *m) {183UNUSED u32 unused;184u32 stepResult;185Vec3f nextPos;186Vec3f step;187struct Object *marioObj = m->marioObj;188189vec3f_copy(step, m->vel);190191if (m->action & ACT_FLAG_SWIMMING) {192apply_water_current(m, step);193}194195nextPos[0] = m->pos[0] + step[0];196nextPos[1] = m->pos[1] + step[1];197nextPos[2] = m->pos[2] + step[2];198199if (nextPos[1] > m->waterLevel - 80) {200nextPos[1] = m->waterLevel - 80;201m->vel[1] = 0.0f;202}203204stepResult = perform_water_full_step(m, nextPos);205206vec3f_copy(marioObj->header.gfx.pos, m->pos);207vec3s_set(marioObj->header.gfx.angle, -m->faceAngle[0], m->faceAngle[1], m->faceAngle[2]);208209return stepResult;210}211212static BAD_RETURN(u32) update_water_pitch(struct MarioState *m) {213struct Object *marioObj = m->marioObj;214215if (marioObj->header.gfx.angle[0] > 0) {216marioObj->header.gfx.pos[1] +=21760.0f * sins(marioObj->header.gfx.angle[0]) * sins(marioObj->header.gfx.angle[0]);218}219220if (marioObj->header.gfx.angle[0] < 0) {221marioObj->header.gfx.angle[0] = marioObj->header.gfx.angle[0] * 6 / 10;222}223224if (marioObj->header.gfx.angle[0] > 0) {225marioObj->header.gfx.angle[0] = marioObj->header.gfx.angle[0] * 10 / 8;226}227}228229static void stationary_slow_down(struct MarioState *m) {230f32 buoyancy = get_buoyancy(m);231232m->angleVel[0] = 0;233m->angleVel[1] = 0;234235m->forwardVel = approach_f32(m->forwardVel, 0.0f, 1.0f, 1.0f);236m->vel[1] = approach_f32(m->vel[1], buoyancy, 2.0f, 1.0f);237238m->faceAngle[0] = approach_s32(m->faceAngle[0], 0, 0x200, 0x200);239m->faceAngle[2] = approach_s32(m->faceAngle[2], 0, 0x100, 0x100);240241m->vel[0] = m->forwardVel * coss(m->faceAngle[0]) * sins(m->faceAngle[1]);242m->vel[2] = m->forwardVel * coss(m->faceAngle[0]) * coss(m->faceAngle[1]);243}244245static void update_swimming_speed(struct MarioState *m, f32 decelThreshold) {246f32 buoyancy = get_buoyancy(m);247f32 maxSpeed = 28.0f;248249if (m->action & ACT_FLAG_STATIONARY) {250m->forwardVel -= 2.0f;251}252253if (m->forwardVel < 0.0f) {254m->forwardVel = 0.0f;255}256257if (m->forwardVel > maxSpeed) {258m->forwardVel = maxSpeed;259}260261if (m->forwardVel > decelThreshold) {262m->forwardVel -= 0.5f;263}264265m->vel[0] = m->forwardVel * coss(m->faceAngle[0]) * sins(m->faceAngle[1]);266m->vel[1] = m->forwardVel * sins(m->faceAngle[0]) + buoyancy;267m->vel[2] = m->forwardVel * coss(m->faceAngle[0]) * coss(m->faceAngle[1]);268}269270static void update_swimming_yaw(struct MarioState *m) {271s16 targetYawVel = -(s16)(10.0f * m->controller->stickX);272273if (targetYawVel > 0) {274if (m->angleVel[1] < 0) {275m->angleVel[1] += 0x40;276if (m->angleVel[1] > 0x10) {277m->angleVel[1] = 0x10;278}279} else {280m->angleVel[1] = approach_s32(m->angleVel[1], targetYawVel, 0x10, 0x20);281}282} else if (targetYawVel < 0) {283if (m->angleVel[1] > 0) {284m->angleVel[1] -= 0x40;285if (m->angleVel[1] < -0x10) {286m->angleVel[1] = -0x10;287}288} else {289m->angleVel[1] = approach_s32(m->angleVel[1], targetYawVel, 0x20, 0x10);290}291} else {292m->angleVel[1] = approach_s32(m->angleVel[1], 0, 0x40, 0x40);293}294295m->faceAngle[1] += m->angleVel[1];296m->faceAngle[2] = -m->angleVel[1] * 8;297}298299static void update_swimming_pitch(struct MarioState *m) {300s16 targetPitch = -(s16)(252.0f * m->controller->stickY);301302s16 pitchVel;303if (m->faceAngle[0] < 0) {304pitchVel = 0x100;305} else {306pitchVel = 0x200;307}308309if (m->faceAngle[0] < targetPitch) {310if ((m->faceAngle[0] += pitchVel) > targetPitch) {311m->faceAngle[0] = targetPitch;312}313} else if (m->faceAngle[0] > targetPitch) {314if ((m->faceAngle[0] -= pitchVel) < targetPitch) {315m->faceAngle[0] = targetPitch;316}317}318}319320static void common_idle_step(struct MarioState *m, s32 animation, s32 arg) {321s16 *val = &m->marioBodyState->headAngle[0];322323update_swimming_yaw(m);324update_swimming_pitch(m);325update_swimming_speed(m, MIN_SWIM_SPEED);326perform_water_step(m);327update_water_pitch(m);328329if (m->faceAngle[0] > 0) {330*val = approach_s32(*val, m->faceAngle[0] / 2, 0x80, 0x200);331} else {332*val = approach_s32(*val, 0, 0x200, 0x200);333}334335if (arg == 0) {336set_mario_animation(m, animation);337} else {338set_mario_anim_with_accel(m, animation, arg);339}340341set_swimming_at_surface_particles(m, PARTICLE_IDLE_WATER_WAVE);342}343344static s32 act_water_idle(struct MarioState *m) {345u32 val = 0x10000;346347if (m->flags & MARIO_METAL_CAP) {348return set_mario_action(m, ACT_METAL_WATER_FALLING, 1);349}350351if (m->input & INPUT_B_PRESSED) {352return set_mario_action(m, ACT_WATER_PUNCH, 0);353}354355if (m->input & INPUT_A_PRESSED) {356return set_mario_action(m, ACT_BREASTSTROKE, 0);357}358359if (m->faceAngle[0] < -0x1000) {360val = 0x30000;361}362363common_idle_step(m, MARIO_ANIM_WATER_IDLE, val);364return FALSE;365}366367static s32 act_hold_water_idle(struct MarioState *m) {368if (m->flags & MARIO_METAL_CAP) {369return set_mario_action(m, ACT_HOLD_METAL_WATER_FALLING, 0);370}371372if (m->marioObj->oInteractStatus & INT_STATUS_MARIO_DROP_OBJECT) {373return drop_and_set_mario_action(m, ACT_WATER_IDLE, 0);374}375376if (m->input & INPUT_B_PRESSED) {377return set_mario_action(m, ACT_WATER_THROW, 0);378}379380if (m->input & INPUT_A_PRESSED) {381return set_mario_action(m, ACT_HOLD_BREASTSTROKE, 0);382}383384common_idle_step(m, MARIO_ANIM_WATER_IDLE_WITH_OBJ, 0);385return FALSE;386}387388static s32 act_water_action_end(struct MarioState *m) {389if (m->flags & MARIO_METAL_CAP) {390return set_mario_action(m, ACT_METAL_WATER_FALLING, 1);391}392393if (m->input & INPUT_B_PRESSED) {394return set_mario_action(m, ACT_WATER_PUNCH, 0);395}396397if (m->input & INPUT_A_PRESSED) {398return set_mario_action(m, ACT_BREASTSTROKE, 0);399}400401common_idle_step(m, MARIO_ANIM_WATER_ACTION_END, 0);402if (is_anim_at_end(m)) {403set_mario_action(m, ACT_WATER_IDLE, 0);404}405return FALSE;406}407408static s32 act_hold_water_action_end(struct MarioState *m) {409if (m->flags & MARIO_METAL_CAP) {410return set_mario_action(m, ACT_HOLD_METAL_WATER_FALLING, 0);411}412413if (m->marioObj->oInteractStatus & INT_STATUS_MARIO_DROP_OBJECT) {414return drop_and_set_mario_action(m, ACT_WATER_IDLE, 0);415}416417if (m->input & INPUT_B_PRESSED) {418return set_mario_action(m, ACT_WATER_THROW, 0);419}420421if (m->input & INPUT_A_PRESSED) {422return set_mario_action(m, ACT_HOLD_BREASTSTROKE, 0);423}424425common_idle_step(426m, m->actionArg == 0 ? MARIO_ANIM_WATER_ACTION_END_WITH_OBJ : MARIO_ANIM_STOP_GRAB_OBJ_WATER,4270);428if (is_anim_at_end(m)) {429set_mario_action(m, ACT_HOLD_WATER_IDLE, 0);430}431return FALSE;432}433434static void reset_bob_variables(struct MarioState *m) {435sBobTimer = 0;436sBobIncrement = 0x800;437sBobHeight = m->faceAngle[0] / 256.0f + 20.0f;438}439440/**441* Controls the bobbing that happens when you swim near the surface.442*/443static void surface_swim_bob(struct MarioState *m) {444if (sBobIncrement != 0 && m->pos[1] > m->waterLevel - 85 && m->faceAngle[0] >= 0) {445if ((sBobTimer += sBobIncrement) >= 0) {446m->marioObj->header.gfx.pos[1] += sBobHeight * sins(sBobTimer);447return;448}449}450451sBobIncrement = 0;452}453454static void common_swimming_step(struct MarioState *m, s16 swimStrength) {455s16 floorPitch;456UNUSED struct Object *marioObj = m->marioObj;457458update_swimming_yaw(m);459update_swimming_pitch(m);460update_swimming_speed(m, swimStrength / 10.0f);461462switch (perform_water_step(m)) {463case WATER_STEP_HIT_FLOOR:464floorPitch = -find_floor_slope(m, -0x8000);465if (m->faceAngle[0] < floorPitch) {466if (configImprovedSwimming)467m->faceAngle[0] = approach_s32(m->faceAngle[0], floorPitch, 0x400, 0x400);468else469m->faceAngle[0] = floorPitch;470}471break;472473case WATER_STEP_HIT_CEILING:474if (m->faceAngle[0] > -0x3000) {475m->faceAngle[0] -= 0x100;476}477break;478479case WATER_STEP_HIT_WALL:480if (m->controller->stickY == 0.0f) {481if (m->faceAngle[0] > 0.0f) {482m->faceAngle[0] += 0x200;483if (m->faceAngle[0] > 0x3F00) {484m->faceAngle[0] = 0x3F00;485}486} else {487m->faceAngle[0] -= 0x200;488if (m->faceAngle[0] < -0x3F00) {489m->faceAngle[0] = -0x3F00;490}491}492}493break;494}495496update_water_pitch(m);497m->marioBodyState->headAngle[0] = approach_s32(m->marioBodyState->headAngle[0], 0, 0x200, 0x200);498499surface_swim_bob(m);500set_swimming_at_surface_particles(m, PARTICLE_WAVE_TRAIL);501}502503static void play_swimming_noise(struct MarioState *m) {504s16 animFrame = m->marioObj->header.gfx.animInfo.animFrame;505506// This must be one line to match on -O2507if (animFrame == 0 || animFrame == 12) play_sound(SOUND_ACTION_UNKNOWN434, m->marioObj->header.gfx.cameraToObject);508}509510static s32 check_water_jump(struct MarioState *m) {511s32 probe = (s32)(m->pos[1] + 1.5f);512513if (m->input & INPUT_A_PRESSED) {514if (probe >= m->waterLevel - 80 && m->faceAngle[0] >= 0 && m->controller->stickY < -60.0f) {515vec3s_set(m->angleVel, 0, 0, 0);516517if (configImprovedSwimming) {518m->vel[1] = 64.0f;519}520else {521m->vel[1] = 62.0f;522}523524if (m->heldObj == NULL) {525return set_mario_action(m, ACT_WATER_JUMP, 0);526} else {527return set_mario_action(m, ACT_HOLD_WATER_JUMP, 0);528}529}530}531532return FALSE;533}534535static s32 act_breaststroke(struct MarioState *m) {536if (m->actionArg == 0) {537sSwimStrength = MIN_SWIM_STRENGTH;538}539540if (m->flags & MARIO_METAL_CAP) {541return set_mario_action(m, ACT_METAL_WATER_FALLING, 1);542}543544if (m->input & INPUT_B_PRESSED) {545return set_mario_action(m, ACT_WATER_PUNCH, 0);546}547548if (++m->actionTimer == 14) {549return set_mario_action(m, ACT_FLUTTER_KICK, 0);550}551552if (check_water_jump(m)) {553return TRUE;554}555556if (m->actionTimer < 6) {557if (configImprovedSwimming) {558m->forwardVel += 1.0f;559}560else {561m->forwardVel += 0.5f;562}563}564565if (m->actionTimer >= 9) {566if (configImprovedSwimming) {567m->forwardVel += 3.0f;568}569else {570m->forwardVel += 1.5f;571}572}573574if (m->actionTimer >= 2) {575if (m->actionTimer < 6 && (m->input & INPUT_A_PRESSED)) {576m->actionState = 1;577}578579if (m->actionTimer == 9 && m->actionState == 1) {580set_anim_to_frame(m, 0);581m->actionState = 0;582m->actionTimer = 1;583sSwimStrength = MIN_SWIM_STRENGTH;584}585}586587if (m->actionTimer == 1) {588play_sound(sSwimStrength == MIN_SWIM_STRENGTH ? SOUND_ACTION_SWIM : SOUND_ACTION_SWIM_FAST,589m->marioObj->header.gfx.cameraToObject);590reset_bob_variables(m);591}592593#if ENABLE_RUMBLE594if (m->actionTimer < 6) {595func_sh_8024CA04();596}597#endif598599set_mario_animation(m, MARIO_ANIM_SWIM_PART1);600common_swimming_step(m, sSwimStrength);601602return FALSE;603}604605static s32 act_swimming_end(struct MarioState *m) {606if (m->flags & MARIO_METAL_CAP) {607return set_mario_action(m, ACT_METAL_WATER_FALLING, 1);608}609610if (m->input & INPUT_B_PRESSED) {611return set_mario_action(m, ACT_WATER_PUNCH, 0);612}613614if (m->actionTimer >= 15) {615return set_mario_action(m, ACT_WATER_ACTION_END, 0);616}617618if (check_water_jump(m)) {619return TRUE;620}621622if ((m->input & INPUT_A_DOWN) && m->actionTimer >= 7) {623if (m->actionTimer == 7 && sSwimStrength < 280) {624sSwimStrength += 10;625}626return set_mario_action(m, ACT_BREASTSTROKE, 1);627}628629if (m->actionTimer >= 7) {630sSwimStrength = MIN_SWIM_STRENGTH;631}632633m->actionTimer++;634635m->forwardVel -= 0.25f;636set_mario_animation(m, MARIO_ANIM_SWIM_PART2);637common_swimming_step(m, sSwimStrength);638639return FALSE;640}641642static s32 act_flutter_kick(struct MarioState *m) {643if (m->flags & MARIO_METAL_CAP) {644return set_mario_action(m, ACT_METAL_WATER_FALLING, 1);645}646647if (m->input & INPUT_B_PRESSED) {648return set_mario_action(m, ACT_WATER_PUNCH, 0);649}650651if (!(m->input & INPUT_A_DOWN)) {652if (m->actionTimer == 0 && sSwimStrength < 280) {653sSwimStrength += 10;654}655return set_mario_action(m, ACT_SWIMMING_END, 0);656}657658m->forwardVel = approach_f32(m->forwardVel, 12.0f, 0.1f, 0.15f);659m->actionTimer = 1;660sSwimStrength = MIN_SWIM_STRENGTH;661662if (m->forwardVel < 14.0f) {663play_swimming_noise(m);664set_mario_animation(m, MARIO_ANIM_FLUTTERKICK);665}666667common_swimming_step(m, sSwimStrength);668return FALSE;669}670671static s32 act_hold_breaststroke(struct MarioState *m) {672if (m->flags & MARIO_METAL_CAP) {673return set_mario_action(m, ACT_HOLD_METAL_WATER_FALLING, 0);674}675676if (m->marioObj->oInteractStatus & INT_STATUS_MARIO_DROP_OBJECT) {677return drop_and_set_mario_action(m, ACT_WATER_IDLE, 0);678}679680if (++m->actionTimer == 17) {681return set_mario_action(m, ACT_HOLD_FLUTTER_KICK, 0);682}683684if (m->input & INPUT_B_PRESSED) {685return set_mario_action(m, ACT_WATER_THROW, 0);686}687688if (check_water_jump(m)) {689return TRUE;690}691692if (m->actionTimer < 6) {693m->forwardVel += 0.5f;694}695696if (m->actionTimer >= 9) {697m->forwardVel += 1.5f;698}699700if (m->actionTimer >= 2) {701if (m->actionTimer < 6 && (m->input & INPUT_A_PRESSED)) {702m->actionState = 1;703}704705if (m->actionTimer == 9 && m->actionState == 1) {706set_anim_to_frame(m, 0);707m->actionState = 0;708m->actionTimer = 1;709}710}711712if (m->actionTimer == 1) {713play_sound(SOUND_ACTION_SWIM, m->marioObj->header.gfx.cameraToObject);714reset_bob_variables(m);715}716717set_mario_animation(m, MARIO_ANIM_SWIM_WITH_OBJ_PART1);718common_swimming_step(m, 0x00A0);719return FALSE;720}721722static s32 act_hold_swimming_end(struct MarioState *m) {723if (m->flags & MARIO_METAL_CAP) {724return set_mario_action(m, ACT_HOLD_METAL_WATER_FALLING, 0);725}726727if (m->marioObj->oInteractStatus & INT_STATUS_MARIO_DROP_OBJECT) {728return drop_and_set_mario_action(m, ACT_WATER_IDLE, 0);729}730731if (m->actionTimer >= 15) {732return set_mario_action(m, ACT_HOLD_WATER_ACTION_END, 0);733}734735if (m->input & INPUT_B_PRESSED) {736return set_mario_action(m, ACT_WATER_THROW, 0);737}738739if (check_water_jump(m)) {740return TRUE;741}742743if ((m->input & INPUT_A_DOWN) && m->actionTimer >= 7) {744return set_mario_action(m, ACT_HOLD_BREASTSTROKE, 0);745}746747m->actionTimer++;748749m->forwardVel -= 0.25f;750set_mario_animation(m, MARIO_ANIM_SWIM_WITH_OBJ_PART2);751common_swimming_step(m, 0x00A0);752return FALSE;753}754755static s32 act_hold_flutter_kick(struct MarioState *m) {756if (m->flags & MARIO_METAL_CAP) {757return set_mario_action(m, ACT_HOLD_METAL_WATER_FALLING, 0);758}759760if (m->marioObj->oInteractStatus & INT_STATUS_MARIO_DROP_OBJECT) {761return drop_and_set_mario_action(m, ACT_WATER_IDLE, 0);762}763764if (m->input & INPUT_B_PRESSED) {765return set_mario_action(m, ACT_WATER_THROW, 0);766}767768if (!(m->input & INPUT_A_DOWN)) {769return set_mario_action(m, ACT_HOLD_SWIMMING_END, 0);770}771772m->forwardVel = approach_f32(m->forwardVel, 12.0f, 0.1f, 0.15f);773if (m->forwardVel < 14.0f) {774play_swimming_noise(m);775set_mario_animation(m, MARIO_ANIM_FLUTTERKICK_WITH_OBJ);776}777common_swimming_step(m, 0x00A0);778return FALSE;779}780781static s32 act_water_shell_swimming(struct MarioState *m) {782if (m->marioObj->oInteractStatus & INT_STATUS_MARIO_DROP_OBJECT) {783return drop_and_set_mario_action(m, ACT_WATER_IDLE, 0);784}785786if (m->input & INPUT_B_PRESSED) {787return set_mario_action(m, ACT_WATER_THROW, 0);788}789790if (m->actionTimer++ == (configBetterPowerups ? 480 : 240)) {791m->heldObj->oInteractStatus = INT_STATUS_STOP_RIDING;792m->heldObj = NULL;793stop_shell_music();794set_mario_action(m, ACT_FLUTTER_KICK, 0);795}796797m->forwardVel = approach_f32(m->forwardVel, 30.0f, 2.0f, 1.0f);798799play_swimming_noise(m);800set_mario_animation(m, MARIO_ANIM_FLUTTERKICK_WITH_OBJ);801common_swimming_step(m, 0x012C);802803return FALSE;804}805806static s32 check_water_grab(struct MarioState *m) {807//! Heave hos have the grabbable interaction type but are not normally808// grabbable. Since water grabbing doesn't check the appropriate input flag,809// you can use water grab to pick up heave ho.810if (m->marioObj->collidedObjInteractTypes & INTERACT_GRABBABLE) {811struct Object *object = mario_get_collided_object(m, INTERACT_GRABBABLE);812f32 dx = object->oPosX - m->pos[0];813f32 dz = object->oPosZ - m->pos[2];814s16 dAngleToObject = atan2s(dz, dx) - m->faceAngle[1];815816if (dAngleToObject >= -0x2AAA && dAngleToObject <= 0x2AAA) {817m->usedObj = object;818mario_grab_used_object(m);819m->marioBodyState->grabPos = GRAB_POS_LIGHT_OBJ;820return TRUE;821}822}823824return FALSE;825}826827static s32 act_water_throw(struct MarioState *m) {828update_swimming_yaw(m);829update_swimming_pitch(m);830update_swimming_speed(m, MIN_SWIM_SPEED);831perform_water_step(m);832update_water_pitch(m);833834set_mario_animation(m, MARIO_ANIM_WATER_THROW_OBJ);835play_sound_if_no_flag(m, SOUND_ACTION_SWIM, MARIO_ACTION_SOUND_PLAYED);836837m->marioBodyState->headAngle[0] = approach_s32(m->marioBodyState->headAngle[0], 0, 0x200, 0x200);838839if (m->actionTimer++ == 5) {840mario_throw_held_object(m);841#if ENABLE_RUMBLE842queue_rumble_data(3, 50);843#endif844}845846if (is_anim_at_end(m)) {847set_mario_action(m, ACT_WATER_IDLE, 0);848}849850return FALSE;851}852853static s32 act_water_punch(struct MarioState *m) {854if (m->forwardVel < 7.0f) {855m->forwardVel += 1.0f;856}857858update_swimming_yaw(m);859update_swimming_pitch(m);860update_swimming_speed(m, MIN_SWIM_SPEED);861perform_water_step(m);862update_water_pitch(m);863864m->marioBodyState->headAngle[0] = approach_s32(m->marioBodyState->headAngle[0], 0, 0x200, 0x200);865866play_sound_if_no_flag(m, SOUND_ACTION_SWIM, MARIO_ACTION_SOUND_PLAYED);867868switch (m->actionState) {869case 0:870set_mario_animation(m, MARIO_ANIM_WATER_GRAB_OBJ_PART1);871if (is_anim_at_end(m)) {872m->actionState = check_water_grab(m) + 1;873}874break;875876case 1:877set_mario_animation(m, MARIO_ANIM_WATER_GRAB_OBJ_PART2);878if (is_anim_at_end(m)) {879set_mario_action(m, ACT_WATER_ACTION_END, 0);880}881break;882883case 2:884set_mario_animation(m, MARIO_ANIM_WATER_PICK_UP_OBJ);885if (is_anim_at_end(m)) {886if (m->heldObj->behavior == segmented_to_virtual(bhvKoopaShellUnderwater)) {887play_shell_music();888set_mario_action(m, ACT_WATER_SHELL_SWIMMING, 0);889} else {890set_mario_action(m, ACT_HOLD_WATER_ACTION_END, 1);891}892}893break;894}895896return FALSE;897}898899static void common_water_knockback_step(struct MarioState *m, s32 animation, u32 endAction, s32 arg3) {900stationary_slow_down(m);901perform_water_step(m);902set_mario_animation(m, animation);903904m->marioBodyState->headAngle[0] = 0;905906if (is_anim_at_end(m)) {907if (arg3 > 0) {908m->invincTimer = 30;909}910911set_mario_action(m, m->health >= 0x100 ? endAction : ACT_WATER_DEATH, 0);912}913}914915static s32 act_backward_water_kb(struct MarioState *m) {916common_water_knockback_step(m, MARIO_ANIM_BACKWARDS_WATER_KB, ACT_WATER_IDLE, m->actionArg);917return FALSE;918}919920static s32 act_forward_water_kb(struct MarioState *m) {921common_water_knockback_step(m, MARIO_ANIM_WATER_FORWARD_KB, ACT_WATER_IDLE, m->actionArg);922return FALSE;923}924925static s32 act_water_shocked(struct MarioState *m) {926play_sound_if_no_flag(m, SOUND_MARIO_WAAAOOOW, MARIO_ACTION_SOUND_PLAYED);927play_sound(SOUND_MOVING_SHOCKED, m->marioObj->header.gfx.cameraToObject);928set_camera_shake_from_hit(SHAKE_SHOCK);929930if (set_mario_animation(m, MARIO_ANIM_SHOCKED) == 0) {931m->actionTimer++;932m->flags |= MARIO_METAL_SHOCK;933}934935if (m->actionTimer >= 6) {936m->invincTimer = 30;937set_mario_action(m, m->health < 0x100 ? ACT_WATER_DEATH : ACT_WATER_IDLE, 0);938}939940stationary_slow_down(m);941perform_water_step(m);942m->marioBodyState->headAngle[0] = 0;943return FALSE;944}945946static s32 act_drowning(struct MarioState *m) {947switch (m->actionState) {948case 0:949set_mario_animation(m, MARIO_ANIM_DROWNING_PART1);950m->marioBodyState->eyeState = MARIO_EYES_HALF_CLOSED;951if (is_anim_at_end(m)) {952m->actionState = 1;953}954break;955956case 1:957set_mario_animation(m, MARIO_ANIM_DROWNING_PART2);958m->marioBodyState->eyeState = MARIO_EYES_DEAD;959if (m->marioObj->header.gfx.animInfo.animFrame == 30) {960level_trigger_warp(m, WARP_OP_DEATH);961}962break;963}964965play_sound_if_no_flag(m, SOUND_MARIO_DROWNING, MARIO_ACTION_SOUND_PLAYED);966stationary_slow_down(m);967perform_water_step(m);968969return FALSE;970}971972static s32 act_water_death(struct MarioState *m) {973stationary_slow_down(m);974perform_water_step(m);975976m->marioBodyState->eyeState = MARIO_EYES_DEAD;977978set_mario_animation(m, MARIO_ANIM_WATER_DYING);979if (set_mario_animation(m, MARIO_ANIM_WATER_DYING) == 35) {980level_trigger_warp(m, WARP_OP_DEATH);981}982983return FALSE;984}985986static s32 act_water_plunge(struct MarioState *m) {987u32 stepResult;988s32 stateFlags = m->heldObj != NULL;989990f32 endVSpeed;991if (swimming_near_surface(m)) {992endVSpeed = 0.0f;993} else {994endVSpeed = -5.0f;995}996997if (m->flags & MARIO_METAL_CAP) {998stateFlags |= 4;999} else if ((m->prevAction & ACT_FLAG_DIVING) || (m->input & INPUT_A_DOWN)) {1000stateFlags |= 2;1001}10021003m->actionTimer++;10041005stationary_slow_down(m);10061007stepResult = perform_water_step(m);10081009if (m->actionState == 0) {1010play_sound(SOUND_ACTION_UNKNOWN430, m->marioObj->header.gfx.cameraToObject);1011if (m->peakHeight - m->pos[1] > 1150.0f) {1012play_sound(SOUND_MARIO_HAHA_2, m->marioObj->header.gfx.cameraToObject);1013}10141015m->particleFlags |= PARTICLE_WATER_SPLASH;1016m->actionState = 1;1017#if ENABLE_RUMBLE1018if (m->prevAction & ACT_FLAG_AIR) {1019queue_rumble_data(5, 80);1020}1021#endif1022}10231024if (stepResult == WATER_STEP_HIT_FLOOR || m->vel[1] >= endVSpeed || m->actionTimer > 20) {1025switch (stateFlags) {1026case 0:1027set_mario_action(m, ACT_WATER_ACTION_END, 0);1028break;1029case 1:1030set_mario_action(m, ACT_HOLD_WATER_ACTION_END, 0);1031break;1032case 2:1033set_mario_action(m, ACT_FLUTTER_KICK, 0);1034break;1035case 3:1036set_mario_action(m, ACT_HOLD_FLUTTER_KICK, 0);1037break;1038case 4:1039set_mario_action(m, ACT_METAL_WATER_FALLING, 0);1040break;1041case 5:1042set_mario_action(m, ACT_HOLD_METAL_WATER_FALLING, 0);1043break;1044}1045sBobIncrement = 0;1046}10471048switch (stateFlags) {1049case 0:1050set_mario_animation(m, MARIO_ANIM_WATER_ACTION_END);1051break;1052case 1:1053set_mario_animation(m, MARIO_ANIM_WATER_ACTION_END_WITH_OBJ);1054break;1055case 2:1056set_mario_animation(m, MARIO_ANIM_FLUTTERKICK);1057break;1058case 3:1059set_mario_animation(m, MARIO_ANIM_FLUTTERKICK_WITH_OBJ);1060break;1061case 4:1062set_mario_animation(m, MARIO_ANIM_GENERAL_FALL);1063break;1064case 5:1065set_mario_animation(m, MARIO_ANIM_FALL_WITH_LIGHT_OBJ);1066break;1067}10681069m->particleFlags |= PARTICLE_PLUNGE_BUBBLE;1070return FALSE;1071}10721073static s32 act_caught_in_whirlpool(struct MarioState *m) {1074f32 sinAngleChange;1075f32 cosAngleChange;1076f32 newDistance;1077s16 angleChange;10781079struct Object *marioObj = m->marioObj;1080struct Object *whirlpool = m->usedObj;10811082f32 dx = m->pos[0] - whirlpool->oPosX;1083f32 dz = m->pos[2] - whirlpool->oPosZ;1084f32 distance = sqrtf(dx * dx + dz * dz);10851086if ((marioObj->oMarioWhirlpoolPosY += m->vel[1]) < 0.0f) {1087marioObj->oMarioWhirlpoolPosY = 0.0f;1088if (distance < 16.1f && m->actionTimer++ == 16) {1089level_trigger_warp(m, WARP_OP_DEATH);1090}1091}10921093if (distance <= 28.0f) {1094newDistance = 16.0f;1095angleChange = 0x1800;1096} else if (distance < 256.0f) {1097newDistance = distance - (12.0f - distance / 32.0f);1098angleChange = (s16)(0x1C00 - distance * 20.0f);1099} else {1100newDistance = distance - 4.0f;1101angleChange = 0x800;1102}11031104m->vel[1] = -640.0f / (newDistance + 16.0f);11051106sinAngleChange = sins(angleChange);1107cosAngleChange = coss(angleChange);11081109if (distance < 1.0f) {1110dx = newDistance * sins(m->faceAngle[1]);1111dz = newDistance * coss(m->faceAngle[1]);1112} else {1113dx *= newDistance / distance;1114dz *= newDistance / distance;1115}11161117m->pos[0] = whirlpool->oPosX + dx * cosAngleChange + dz * sinAngleChange;1118m->pos[2] = whirlpool->oPosZ - dx * sinAngleChange + dz * cosAngleChange;1119m->pos[1] = whirlpool->oPosY + marioObj->oMarioWhirlpoolPosY;11201121m->faceAngle[1] = atan2s(dz, dx) + 0x8000;11221123set_mario_animation(m, MARIO_ANIM_GENERAL_FALL);1124vec3f_copy(m->marioObj->header.gfx.pos, m->pos);1125vec3s_set(m->marioObj->header.gfx.angle, 0, m->faceAngle[1], 0);1126#if ENABLE_RUMBLE1127reset_rumble_timers();1128#endif11291130return FALSE;1131}11321133static void play_metal_water_jumping_sound(struct MarioState *m, u32 landing) {1134if (!(m->flags & MARIO_ACTION_SOUND_PLAYED)) {1135m->particleFlags |= PARTICLE_MIST_CIRCLE;1136}11371138play_sound_if_no_flag(m, landing ? SOUND_ACTION_METAL_LAND_WATER : SOUND_ACTION_METAL_JUMP_WATER,1139MARIO_ACTION_SOUND_PLAYED);1140}11411142static void play_metal_water_walking_sound(struct MarioState *m) {1143if (is_anim_past_frame(m, 10) || is_anim_past_frame(m, 49)) {1144play_sound(SOUND_ACTION_METAL_STEP_WATER, m->marioObj->header.gfx.cameraToObject);1145m->particleFlags |= PARTICLE_DUST;1146}1147}11481149static void update_metal_water_walking_speed(struct MarioState *m) {1150f32 val = m->intendedMag / 1.5f;11511152if (m->forwardVel <= 0.0f) {1153m->forwardVel += 1.1f;1154} else if (m->forwardVel <= val) {1155m->forwardVel += 1.1f - m->forwardVel / 43.0f;1156} else if (m->floor->normal.y >= 0.95f) {1157m->forwardVel -= 1.0f;1158}11591160if (m->forwardVel > 32.0f) {1161m->forwardVel = 32.0f;1162}11631164m->faceAngle[1] =1165m->intendedYaw - approach_s32((s16)(m->intendedYaw - m->faceAngle[1]), 0, 0x800, 0x800);11661167m->slideVelX = m->forwardVel * sins(m->faceAngle[1]);1168m->slideVelZ = m->forwardVel * coss(m->faceAngle[1]);11691170m->vel[0] = m->slideVelX;1171m->vel[1] = 0.0f;1172m->vel[2] = m->slideVelZ;1173}11741175static s32 update_metal_water_jump_speed(struct MarioState *m) {1176UNUSED f32 nextY = m->pos[1] + m->vel[1];1177f32 waterSurface = m->waterLevel - 100;11781179if (m->vel[1] > 0.0f && m->pos[1] > waterSurface) {1180return TRUE;1181}11821183if (m->input & INPUT_NONZERO_ANALOG) {1184s16 intendedDYaw = m->intendedYaw - m->faceAngle[1];1185m->forwardVel += 0.8f * coss(intendedDYaw);1186m->faceAngle[1] += 0x200 * sins(intendedDYaw);1187} else {1188m->forwardVel = approach_f32(m->forwardVel, 0.0f, 0.25f, 0.25f);1189}11901191if (m->forwardVel > 16.0f) {1192m->forwardVel -= 1.0f;1193}11941195if (m->forwardVel < 0.0f) {1196m->forwardVel += 2.0f;1197}11981199m->vel[0] = m->slideVelX = m->forwardVel * sins(m->faceAngle[1]);1200m->vel[2] = m->slideVelZ = m->forwardVel * coss(m->faceAngle[1]);1201return FALSE;1202}12031204static s32 act_metal_water_standing(struct MarioState *m) {1205if (!(m->flags & MARIO_METAL_CAP)) {1206return set_mario_action(m, ACT_WATER_IDLE, 0);1207}12081209if (m->input & INPUT_A_PRESSED) {1210return set_mario_action(m, ACT_METAL_WATER_JUMP, 0);1211}12121213if (m->input & INPUT_NONZERO_ANALOG) {1214return set_mario_action(m, ACT_METAL_WATER_WALKING, 0);1215}12161217switch (m->actionState) {1218case 0:1219set_mario_animation(m, MARIO_ANIM_IDLE_HEAD_LEFT);1220break;1221case 1:1222set_mario_animation(m, MARIO_ANIM_IDLE_HEAD_RIGHT);1223break;1224case 2:1225set_mario_animation(m, MARIO_ANIM_IDLE_HEAD_CENTER);1226break;1227}12281229if (is_anim_at_end(m) && ++m->actionState == 3) {1230m->actionState = 0;1231}12321233stop_and_set_height_to_floor(m);1234if (m->pos[1] >= m->waterLevel - 150) {1235m->particleFlags |= PARTICLE_IDLE_WATER_WAVE;1236}12371238return FALSE;1239}12401241static s32 act_hold_metal_water_standing(struct MarioState *m) {1242if (m->marioObj->oInteractStatus & INT_STATUS_MARIO_DROP_OBJECT) {1243return drop_and_set_mario_action(m, ACT_METAL_WATER_STANDING, 0);1244}12451246if (!(m->flags & MARIO_METAL_CAP)) {1247return set_mario_action(m, ACT_HOLD_WATER_IDLE, 0);1248}12491250if (m->input & INPUT_A_PRESSED) {1251return set_mario_action(m, ACT_HOLD_METAL_WATER_JUMP, 0);1252}12531254if (m->input & INPUT_NONZERO_ANALOG) {1255return set_mario_action(m, ACT_HOLD_METAL_WATER_WALKING, 0);1256}12571258stop_and_set_height_to_floor(m);1259set_mario_animation(m, MARIO_ANIM_IDLE_WITH_LIGHT_OBJ);1260return FALSE;1261}12621263static s32 act_metal_water_walking(struct MarioState *m) {1264s32 val04;12651266if (!(m->flags & MARIO_METAL_CAP)) {1267return set_mario_action(m, ACT_WATER_IDLE, 0);1268}12691270if (m->input & INPUT_FIRST_PERSON) {1271return set_mario_action(m, ACT_METAL_WATER_STANDING, 0);1272}12731274if (m->input & INPUT_A_PRESSED) {1275return set_mario_action(m, ACT_METAL_WATER_JUMP, 0);1276}12771278if (m->input & INPUT_UNKNOWN_5) {1279return set_mario_action(m, ACT_METAL_WATER_STANDING, 0);1280}12811282if ((val04 = (s32)(m->forwardVel / 4.0f * 0x10000)) < 0x1000) {1283val04 = 0x1000;1284}12851286set_mario_anim_with_accel(m, MARIO_ANIM_WALKING, val04);1287play_metal_water_walking_sound(m);1288update_metal_water_walking_speed(m);12891290switch (perform_ground_step(m)) {1291case GROUND_STEP_LEFT_GROUND:1292set_mario_action(m, ACT_METAL_WATER_FALLING, 1);1293break;12941295case GROUND_STEP_HIT_WALL:1296m->forwardVel = 0.0f;1297break;1298}12991300return FALSE;1301}13021303static s32 act_hold_metal_water_walking(struct MarioState *m) {1304s32 val04;13051306if (m->marioObj->oInteractStatus & INT_STATUS_MARIO_DROP_OBJECT) {1307return drop_and_set_mario_action(m, ACT_METAL_WATER_WALKING, 0);1308}13091310if (!(m->flags & MARIO_METAL_CAP)) {1311return set_mario_action(m, ACT_HOLD_WATER_IDLE, 0);1312}13131314if (m->input & INPUT_A_PRESSED) {1315return set_mario_action(m, ACT_HOLD_METAL_WATER_JUMP, 0);1316}13171318if (m->input & INPUT_UNKNOWN_5) {1319return set_mario_action(m, ACT_HOLD_METAL_WATER_STANDING, 0);1320}13211322m->intendedMag *= 0.4f;13231324if ((val04 = (s32)(m->forwardVel / 2.0f * 0x10000)) < 0x1000) {1325val04 = 0x1000;1326}13271328set_mario_anim_with_accel(m, MARIO_ANIM_RUN_WITH_LIGHT_OBJ, val04);1329play_metal_water_walking_sound(m);1330update_metal_water_walking_speed(m);13311332switch (perform_ground_step(m)) {1333case GROUND_STEP_LEFT_GROUND:1334set_mario_action(m, ACT_HOLD_METAL_WATER_FALLING, 1);1335break;13361337case GROUND_STEP_HIT_WALL:1338m->forwardVel = 0.0f;1339break;1340}13411342return FALSE;1343}13441345static s32 act_metal_water_jump(struct MarioState *m) {1346if (!(m->flags & MARIO_METAL_CAP)) {1347return set_mario_action(m, ACT_WATER_IDLE, 0);1348}13491350if (update_metal_water_jump_speed(m)) {1351return set_mario_action(m, ACT_WATER_JUMP, 1);1352}13531354play_metal_water_jumping_sound(m, FALSE);1355set_mario_animation(m, MARIO_ANIM_SINGLE_JUMP);13561357switch (perform_air_step(m, 0)) {1358case AIR_STEP_LANDED:1359set_mario_action(m, ACT_METAL_WATER_JUMP_LAND, 0);1360break;13611362case AIR_STEP_HIT_WALL:1363m->forwardVel = 0.0f;1364break;1365}13661367return FALSE;1368}13691370static s32 act_hold_metal_water_jump(struct MarioState *m) {1371if (m->marioObj->oInteractStatus & INT_STATUS_MARIO_DROP_OBJECT) {1372return drop_and_set_mario_action(m, ACT_METAL_WATER_FALLING, 0);1373}13741375if (!(m->flags & MARIO_METAL_CAP)) {1376return set_mario_action(m, ACT_HOLD_WATER_IDLE, 0);1377}13781379if (update_metal_water_jump_speed(m)) {1380return set_mario_action(m, ACT_HOLD_WATER_JUMP, 1);1381}13821383play_metal_water_jumping_sound(m, FALSE);1384set_mario_animation(m, MARIO_ANIM_JUMP_WITH_LIGHT_OBJ);13851386switch (perform_air_step(m, 0)) {1387case AIR_STEP_LANDED:1388set_mario_action(m, ACT_HOLD_METAL_WATER_JUMP_LAND, 0);1389break;13901391case AIR_STEP_HIT_WALL:1392m->forwardVel = 0.0f;1393break;1394}13951396return FALSE;1397}13981399static s32 act_metal_water_falling(struct MarioState *m) {1400if (!(m->flags & MARIO_METAL_CAP)) {1401return set_mario_action(m, ACT_WATER_IDLE, 0);1402}14031404if (m->input & INPUT_NONZERO_ANALOG) {1405m->faceAngle[1] += 0x400 * sins(m->intendedYaw - m->faceAngle[1]);1406}14071408set_mario_animation(m, m->actionArg == 0 ? MARIO_ANIM_GENERAL_FALL : MARIO_ANIM_FALL_FROM_WATER);1409stationary_slow_down(m);14101411if (perform_water_step(m) & WATER_STEP_HIT_FLOOR) { // hit floor or cancelled1412set_mario_action(m, ACT_METAL_WATER_FALL_LAND, 0);1413}14141415return FALSE;1416}14171418static s32 act_hold_metal_water_falling(struct MarioState *m) {1419if (m->marioObj->oInteractStatus & INT_STATUS_MARIO_DROP_OBJECT) {1420return drop_and_set_mario_action(m, ACT_METAL_WATER_FALLING, 0);1421}14221423if (!(m->flags & MARIO_METAL_CAP)) {1424return set_mario_action(m, ACT_HOLD_WATER_IDLE, 0);1425}14261427if (m->input & INPUT_NONZERO_ANALOG) {1428m->faceAngle[1] += 0x400 * sins(m->intendedYaw - m->faceAngle[1]);1429}14301431set_mario_animation(m, MARIO_ANIM_FALL_WITH_LIGHT_OBJ);1432stationary_slow_down(m);14331434if (perform_water_step(m) & WATER_STEP_HIT_FLOOR) { // hit floor or cancelled1435set_mario_action(m, ACT_HOLD_METAL_WATER_FALL_LAND, 0);1436}14371438return FALSE;1439}14401441static s32 act_metal_water_jump_land(struct MarioState *m) {1442play_metal_water_jumping_sound(m, TRUE);14431444if (!(m->flags & MARIO_METAL_CAP)) {1445return set_mario_action(m, ACT_WATER_IDLE, 0);1446}14471448if (m->input & INPUT_NONZERO_ANALOG) {1449return set_mario_action(m, ACT_METAL_WATER_WALKING, 0);1450}14511452stop_and_set_height_to_floor(m);1453set_mario_animation(m, MARIO_ANIM_LAND_FROM_SINGLE_JUMP);14541455if (is_anim_at_end(m)) {1456return set_mario_action(m, ACT_METAL_WATER_STANDING, 0);1457}14581459return FALSE;1460}14611462static s32 act_hold_metal_water_jump_land(struct MarioState *m) {1463play_metal_water_jumping_sound(m, TRUE);14641465if (m->marioObj->oInteractStatus & INT_STATUS_MARIO_DROP_OBJECT) {1466return drop_and_set_mario_action(m, ACT_METAL_WATER_STANDING, 0);1467}14681469if (!(m->flags & MARIO_METAL_CAP)) {1470return set_mario_action(m, ACT_HOLD_WATER_IDLE, 0);1471}14721473if (m->input & INPUT_NONZERO_ANALOG) {1474return set_mario_action(m, ACT_HOLD_METAL_WATER_WALKING, 0);1475}14761477stop_and_set_height_to_floor(m);1478set_mario_animation(m, MARIO_ANIM_JUMP_LAND_WITH_LIGHT_OBJ);14791480if (is_anim_at_end(m)) {1481return set_mario_action(m, ACT_HOLD_METAL_WATER_STANDING, 0);1482}14831484return FALSE;1485}14861487static s32 act_metal_water_fall_land(struct MarioState *m) {1488play_metal_water_jumping_sound(m, TRUE);14891490if (!(m->flags & MARIO_METAL_CAP)) {1491return set_mario_action(m, ACT_WATER_IDLE, 0);1492}14931494if (m->input & INPUT_NONZERO_ANALOG) {1495return set_mario_action(m, ACT_METAL_WATER_WALKING, 0);1496}14971498stop_and_set_height_to_floor(m);1499set_mario_animation(m, MARIO_ANIM_GENERAL_LAND);15001501if (is_anim_at_end(m)) {1502return set_mario_action(m, ACT_METAL_WATER_STANDING, 0);1503}15041505return FALSE;1506}15071508static s32 act_hold_metal_water_fall_land(struct MarioState *m) {1509play_metal_water_jumping_sound(m, TRUE);15101511if (m->marioObj->oInteractStatus & INT_STATUS_MARIO_DROP_OBJECT) {1512return drop_and_set_mario_action(m, ACT_METAL_WATER_STANDING, 0);1513}15141515if (!(m->flags & MARIO_METAL_CAP)) {1516return set_mario_action(m, ACT_HOLD_WATER_IDLE, 0);1517}15181519if (m->input & INPUT_NONZERO_ANALOG) {1520return set_mario_action(m, ACT_HOLD_METAL_WATER_WALKING, 0);1521}15221523stop_and_set_height_to_floor(m);1524set_mario_animation(m, MARIO_ANIM_FALL_LAND_WITH_LIGHT_OBJ);15251526if (is_anim_at_end(m)) {1527return set_mario_action(m, ACT_HOLD_METAL_WATER_STANDING, 0);1528}15291530return FALSE;1531}15321533static s32 check_common_submerged_cancels(struct MarioState *m) {1534if (m->pos[1] > m->waterLevel - 80) {1535if (m->waterLevel - 80 > m->floorHeight) {1536m->pos[1] = m->waterLevel - 80;1537} else {1538//! If you press B to throw the shell, there is a ~5 frame window1539// where your held object is the shell, but you are not in the1540// water shell swimming action. This allows you to hold the water1541// shell on land (used for cloning in DDD).1542if (m->action == ACT_WATER_SHELL_SWIMMING && m->heldObj != NULL) {1543m->heldObj->oInteractStatus = INT_STATUS_STOP_RIDING;1544m->heldObj = NULL;1545stop_shell_music();1546}15471548return transition_submerged_to_walking(m);1549}1550}15511552if (m->health < 0x100 && !(m->action & (ACT_FLAG_INTANGIBLE | ACT_FLAG_INVULNERABLE))) {1553set_mario_action(m, ACT_DROWNING, 0);1554}15551556return FALSE;1557}15581559s32 mario_execute_submerged_action(struct MarioState *m) {1560s32 cancel;15611562if (check_common_submerged_cancels(m)) {1563return TRUE;1564}15651566m->quicksandDepth = 0.0f;15671568m->marioBodyState->headAngle[1] = 0;1569m->marioBodyState->headAngle[2] = 0;15701571/* clang-format off */1572switch (m->action) {1573case ACT_WATER_IDLE: cancel = act_water_idle(m); break;1574case ACT_HOLD_WATER_IDLE: cancel = act_hold_water_idle(m); break;1575case ACT_WATER_ACTION_END: cancel = act_water_action_end(m); break;1576case ACT_HOLD_WATER_ACTION_END: cancel = act_hold_water_action_end(m); break;1577case ACT_DROWNING: cancel = act_drowning(m); break;1578case ACT_BACKWARD_WATER_KB: cancel = act_backward_water_kb(m); break;1579case ACT_FORWARD_WATER_KB: cancel = act_forward_water_kb(m); break;1580case ACT_WATER_DEATH: cancel = act_water_death(m); break;1581case ACT_WATER_SHOCKED: cancel = act_water_shocked(m); break;1582case ACT_BREASTSTROKE: cancel = act_breaststroke(m); break;1583case ACT_SWIMMING_END: cancel = act_swimming_end(m); break;1584case ACT_FLUTTER_KICK: cancel = act_flutter_kick(m); break;1585case ACT_HOLD_BREASTSTROKE: cancel = act_hold_breaststroke(m); break;1586case ACT_HOLD_SWIMMING_END: cancel = act_hold_swimming_end(m); break;1587case ACT_HOLD_FLUTTER_KICK: cancel = act_hold_flutter_kick(m); break;1588case ACT_WATER_SHELL_SWIMMING: cancel = act_water_shell_swimming(m); break;1589case ACT_WATER_THROW: cancel = act_water_throw(m); break;1590case ACT_WATER_PUNCH: cancel = act_water_punch(m); break;1591case ACT_WATER_PLUNGE: cancel = act_water_plunge(m); break;1592case ACT_CAUGHT_IN_WHIRLPOOL: cancel = act_caught_in_whirlpool(m); break;1593case ACT_METAL_WATER_STANDING: cancel = act_metal_water_standing(m); break;1594case ACT_METAL_WATER_WALKING: cancel = act_metal_water_walking(m); break;1595case ACT_METAL_WATER_FALLING: cancel = act_metal_water_falling(m); break;1596case ACT_METAL_WATER_FALL_LAND: cancel = act_metal_water_fall_land(m); break;1597case ACT_METAL_WATER_JUMP: cancel = act_metal_water_jump(m); break;1598case ACT_METAL_WATER_JUMP_LAND: cancel = act_metal_water_jump_land(m); break;1599case ACT_HOLD_METAL_WATER_STANDING: cancel = act_hold_metal_water_standing(m); break;1600case ACT_HOLD_METAL_WATER_WALKING: cancel = act_hold_metal_water_walking(m); break;1601case ACT_HOLD_METAL_WATER_FALLING: cancel = act_hold_metal_water_falling(m); break;1602case ACT_HOLD_METAL_WATER_FALL_LAND: cancel = act_hold_metal_water_fall_land(m); break;1603case ACT_HOLD_METAL_WATER_JUMP: cancel = act_hold_metal_water_jump(m); break;1604case ACT_HOLD_METAL_WATER_JUMP_LAND: cancel = act_hold_metal_water_jump_land(m); break;1605}1606/* clang-format on */16071608return cancel;1609}161016111612