sm64

A Super Mario 64 decompilation
Log | Files | Refs | README | LICENSE

mario_actions_automatic.c (27065B)


      1 #include <PR/ultratypes.h>
      2 
      3 #include "sm64.h"
      4 #include "behavior_data.h"
      5 #include "mario_actions_automatic.h"
      6 #include "audio/external.h"
      7 #include "area.h"
      8 #include "mario.h"
      9 #include "mario_step.h"
     10 #include "engine/math_util.h"
     11 #include "memory.h"
     12 #include "engine/graph_node.h"
     13 #include "save_file.h"
     14 #include "engine/surface_collision.h"
     15 #include "interaction.h"
     16 #include "camera.h"
     17 #include "level_table.h"
     18 #include "rumble_init.h"
     19 
     20 #define POLE_NONE          0
     21 #define POLE_TOUCHED_FLOOR 1
     22 #define POLE_FELL_OFF      2
     23 
     24 #define HANG_NONE            0
     25 #define HANG_HIT_CEIL_OR_OOB 1
     26 #define HANG_LEFT_CEIL       2
     27 
     28 void add_tree_leaf_particles(struct MarioState *m) {
     29     f32 leafHeight;
     30 
     31     if (m->usedObj->behavior == segmented_to_virtual(bhvTree)) {
     32         // make leaf effect spawn higher on the Shifting Sand Land palm tree
     33         if (gCurrLevelNum == LEVEL_SSL) {
     34             leafHeight = 250.0f;
     35         } else {
     36             leafHeight = 100.0f;
     37         }
     38         if (m->pos[1] - m->floorHeight > leafHeight) {
     39             m->particleFlags |= PARTICLE_LEAF;
     40         }
     41     }
     42 }
     43 
     44 void play_climbing_sounds(struct MarioState *m, s32 b) {
     45     s32 isOnTree = (m->usedObj->behavior == segmented_to_virtual(bhvTree));
     46 
     47     if (b == 1) {
     48         if (is_anim_past_frame(m, 1)) {
     49             play_sound(isOnTree ? SOUND_ACTION_CLIMB_UP_TREE : SOUND_ACTION_CLIMB_UP_POLE,
     50                        m->marioObj->header.gfx.cameraToObject);
     51         }
     52     } else {
     53         play_sound(isOnTree ? SOUND_MOVING_SLIDE_DOWN_TREE : SOUND_MOVING_SLIDE_DOWN_POLE,
     54                    m->marioObj->header.gfx.cameraToObject);
     55     }
     56 }
     57 
     58 s32 set_pole_position(struct MarioState *m, f32 offsetY) {
     59     UNUSED u8 filler[12];
     60     struct Surface *floor;
     61     struct Surface *ceil;
     62     f32 floorHeight;
     63     f32 ceilHeight;
     64     s32 collided;
     65     s32 result = POLE_NONE;
     66     f32 poleTop = m->usedObj->hitboxHeight - 100.0f;
     67     struct Object *marioObj = m->marioObj;
     68 
     69     if (marioObj->oMarioPolePos > poleTop) {
     70         marioObj->oMarioPolePos = poleTop;
     71     }
     72 
     73     m->pos[0] = m->usedObj->oPosX;
     74     m->pos[2] = m->usedObj->oPosZ;
     75     m->pos[1] = m->usedObj->oPosY + marioObj->oMarioPolePos + offsetY;
     76 
     77     collided = f32_find_wall_collision(&m->pos[0], &m->pos[1], &m->pos[2], 60.0f, 50.0f);
     78     collided |= f32_find_wall_collision(&m->pos[0], &m->pos[1], &m->pos[2], 30.0f, 24.0f);
     79 
     80     ceilHeight = vec3f_find_ceil(m->pos, m->pos[1], &ceil);
     81     if (m->pos[1] > ceilHeight - 160.0f) {
     82         m->pos[1] = ceilHeight - 160.0f;
     83         marioObj->oMarioPolePos = m->pos[1] - m->usedObj->oPosY;
     84     }
     85 
     86     floorHeight = find_floor(m->pos[0], m->pos[1], m->pos[2], &floor);
     87     if (m->pos[1] < floorHeight) {
     88         m->pos[1] = floorHeight;
     89         set_mario_action(m, ACT_IDLE, 0);
     90         result = POLE_TOUCHED_FLOOR;
     91     } else if (marioObj->oMarioPolePos < -m->usedObj->hitboxDownOffset) {
     92         m->pos[1] = m->usedObj->oPosY - m->usedObj->hitboxDownOffset;
     93         set_mario_action(m, ACT_FREEFALL, 0);
     94         result = POLE_FELL_OFF;
     95     } else if (collided) {
     96         if (m->pos[1] > floorHeight + 20.0f) {
     97             m->forwardVel = -2.0f;
     98             set_mario_action(m, ACT_SOFT_BONK, 0);
     99             result = POLE_FELL_OFF;
    100         } else {
    101             set_mario_action(m, ACT_IDLE, 0);
    102             result = POLE_TOUCHED_FLOOR;
    103         }
    104     }
    105 
    106     vec3f_copy(m->marioObj->header.gfx.pos, m->pos);
    107     vec3s_set(m->marioObj->header.gfx.angle, m->usedObj->oMoveAnglePitch, m->faceAngle[1],
    108               m->usedObj->oMoveAngleRoll);
    109 
    110     return result;
    111 }
    112 
    113 s32 act_holding_pole(struct MarioState *m) {
    114     struct Object *marioObj = m->marioObj;
    115 
    116 #ifdef VERSION_JP
    117     if (m->input & INPUT_A_PRESSED) {
    118         add_tree_leaf_particles(m);
    119         m->faceAngle[1] += 0x8000;
    120         return set_mario_action(m, ACT_WALL_KICK_AIR, 0);
    121     }
    122 
    123     if (m->input & INPUT_Z_PRESSED) {
    124         add_tree_leaf_particles(m);
    125         m->forwardVel = -2.0f;
    126         return set_mario_action(m, ACT_SOFT_BONK, 0);
    127     }
    128 #else
    129     if ((m->input & INPUT_Z_PRESSED) || m->health < 0x100) {
    130         add_tree_leaf_particles(m);
    131         m->forwardVel = -2.0f;
    132         return set_mario_action(m, ACT_SOFT_BONK, 0);
    133     }
    134 
    135     if (m->input & INPUT_A_PRESSED) {
    136         add_tree_leaf_particles(m);
    137         m->faceAngle[1] += 0x8000;
    138         return set_mario_action(m, ACT_WALL_KICK_AIR, 0);
    139     }
    140 #endif
    141 
    142     if (m->controller->stickY > 16.0f) {
    143         f32 poleTop = m->usedObj->hitboxHeight - 100.0f;
    144         const BehaviorScript *poleBehavior = virtual_to_segmented(0x13, m->usedObj->behavior);
    145 
    146         if (marioObj->oMarioPolePos < poleTop - 0.4f) {
    147             return set_mario_action(m, ACT_CLIMBING_POLE, 0);
    148         }
    149 
    150         if (poleBehavior != bhvGiantPole && m->controller->stickY > 50.0f) {
    151             return set_mario_action(m, ACT_TOP_OF_POLE_TRANSITION, 0);
    152         }
    153     }
    154 
    155     if (m->controller->stickY < -16.0f) {
    156         marioObj->oMarioPoleYawVel -= m->controller->stickY * 2;
    157         if (marioObj->oMarioPoleYawVel > 0x1000) {
    158             marioObj->oMarioPoleYawVel = 0x1000;
    159         }
    160 
    161         m->faceAngle[1] += marioObj->oMarioPoleYawVel;
    162         marioObj->oMarioPolePos -= marioObj->oMarioPoleYawVel / 0x100;
    163 
    164         if (m->usedObj->behavior == segmented_to_virtual(bhvTree)) {
    165             //! The Shifting Sand Land palm tree check is done climbing up in
    166             // add_tree_leaf_particles, but not here, when climbing down.
    167             if (m->pos[1] - m->floorHeight > 100.0f) {
    168                 m->particleFlags |= PARTICLE_LEAF;
    169             }
    170         }
    171         play_climbing_sounds(m, 2);
    172 #if ENABLE_RUMBLE
    173         reset_rumble_timers();
    174 #endif
    175         set_sound_moving_speed(SOUND_BANK_MOVING, marioObj->oMarioPoleYawVel / 0x100 * 2);
    176     } else {
    177         marioObj->oMarioPoleYawVel = 0;
    178         m->faceAngle[1] -= m->controller->stickX * 16.0f;
    179     }
    180 
    181     if (set_pole_position(m, 0.0f) == POLE_NONE) {
    182         set_mario_animation(m, MARIO_ANIM_IDLE_ON_POLE);
    183     }
    184 
    185     return FALSE;
    186 }
    187 
    188 s32 act_climbing_pole(struct MarioState *m) {
    189     s32 sp24;
    190     struct Object *marioObj = m->marioObj;
    191     s16 cameraAngle = m->area->camera->yaw;
    192 
    193 #ifndef VERSION_JP
    194     if (m->health < 0x100) {
    195         add_tree_leaf_particles(m);
    196         m->forwardVel = -2.0f;
    197         return set_mario_action(m, ACT_SOFT_BONK, 0);
    198     }
    199 #endif
    200 
    201     if (m->input & INPUT_A_PRESSED) {
    202         add_tree_leaf_particles(m);
    203         m->faceAngle[1] += 0x8000;
    204         return set_mario_action(m, ACT_WALL_KICK_AIR, 0);
    205     }
    206 
    207     if (m->controller->stickY < 8.0f) {
    208         return set_mario_action(m, ACT_HOLDING_POLE, 0);
    209     }
    210 
    211     marioObj->oMarioPolePos += m->controller->stickY / 8.0f;
    212     marioObj->oMarioPoleYawVel = 0;
    213     m->faceAngle[1] = cameraAngle - approach_s32((s16)(cameraAngle - m->faceAngle[1]), 0, 0x400, 0x400);
    214 
    215     if (set_pole_position(m, 0.0f) == POLE_NONE) {
    216         sp24 = m->controller->stickY / 4.0f * 0x10000;
    217         set_mario_anim_with_accel(m, MARIO_ANIM_CLIMB_UP_POLE, sp24);
    218         add_tree_leaf_particles(m);
    219         play_climbing_sounds(m, 1);
    220     }
    221 
    222     return FALSE;
    223 }
    224 
    225 s32 act_grab_pole_slow(struct MarioState *m) {
    226     play_sound_if_no_flag(m, SOUND_MARIO_WHOA, MARIO_MARIO_SOUND_PLAYED);
    227 
    228     if (set_pole_position(m, 0.0f) == POLE_NONE) {
    229         set_mario_animation(m, MARIO_ANIM_GRAB_POLE_SHORT);
    230         if (is_anim_at_end(m)) {
    231             set_mario_action(m, ACT_HOLDING_POLE, 0);
    232         }
    233         add_tree_leaf_particles(m);
    234     }
    235 
    236     return FALSE;
    237 }
    238 
    239 s32 act_grab_pole_fast(struct MarioState *m) {
    240     struct Object *marioObj = m->marioObj;
    241 
    242     play_sound_if_no_flag(m, SOUND_MARIO_WHOA, MARIO_MARIO_SOUND_PLAYED);
    243     m->faceAngle[1] += marioObj->oMarioPoleYawVel;
    244     marioObj->oMarioPoleYawVel = marioObj->oMarioPoleYawVel * 8 / 10;
    245 
    246     if (set_pole_position(m, 0.0f) == POLE_NONE) {
    247         if (marioObj->oMarioPoleYawVel > 0x800) {
    248             set_mario_animation(m, MARIO_ANIM_GRAB_POLE_SWING_PART1);
    249         } else {
    250             set_mario_animation(m, MARIO_ANIM_GRAB_POLE_SWING_PART2);
    251             if (is_anim_at_end(m)) {
    252                 marioObj->oMarioPoleYawVel = 0;
    253                 set_mario_action(m, ACT_HOLDING_POLE, 0);
    254             }
    255         }
    256         add_tree_leaf_particles(m);
    257     }
    258 
    259     return FALSE;
    260 }
    261 
    262 s32 act_top_of_pole_transition(struct MarioState *m) {
    263     struct Object *marioObj = m->marioObj;
    264 
    265     marioObj->oMarioPoleYawVel = 0;
    266     if (m->actionArg == 0) {
    267         set_mario_animation(m, MARIO_ANIM_START_HANDSTAND);
    268         if (is_anim_at_end(m)) {
    269             return set_mario_action(m, ACT_TOP_OF_POLE, 0);
    270         }
    271     } else {
    272         set_mario_animation(m, MARIO_ANIM_RETURN_FROM_HANDSTAND);
    273         if (m->marioObj->header.gfx.animInfo.animFrame == 0) {
    274             return set_mario_action(m, ACT_HOLDING_POLE, 0);
    275         }
    276     }
    277 
    278     set_pole_position(m, return_mario_anim_y_translation(m));
    279     return FALSE;
    280 }
    281 
    282 s32 act_top_of_pole(struct MarioState *m) {
    283     UNUSED struct Object *marioObj = m->marioObj;
    284 
    285     if (m->input & INPUT_A_PRESSED) {
    286         return set_mario_action(m, ACT_TOP_OF_POLE_JUMP, 0);
    287     }
    288     if (m->controller->stickY < -16.0f) {
    289         return set_mario_action(m, ACT_TOP_OF_POLE_TRANSITION, 1);
    290     }
    291 
    292     m->faceAngle[1] -= m->controller->stickX * 16.0f;
    293 
    294     set_mario_animation(m, MARIO_ANIM_HANDSTAND_IDLE);
    295     set_pole_position(m, return_mario_anim_y_translation(m));
    296     return FALSE;
    297 }
    298 
    299 s32 perform_hanging_step(struct MarioState *m, Vec3f nextPos) {
    300     UNUSED u8 filler[4];
    301     struct Surface *ceil;
    302     struct Surface *floor;
    303     f32 ceilHeight;
    304     f32 floorHeight;
    305     f32 ceilOffset;
    306 
    307     m->wall = resolve_and_return_wall_collisions(nextPos, 50.0f, 50.0f);
    308     floorHeight = find_floor(nextPos[0], nextPos[1], nextPos[2], &floor);
    309     ceilHeight = vec3f_find_ceil(nextPos, floorHeight, &ceil);
    310 
    311     if (floor == NULL) {
    312         return HANG_HIT_CEIL_OR_OOB;
    313     }
    314     if (ceil == NULL) {
    315         return HANG_LEFT_CEIL;
    316     }
    317     if (ceilHeight - floorHeight <= 160.0f) {
    318         return HANG_HIT_CEIL_OR_OOB;
    319     }
    320     if (ceil->type != SURFACE_HANGABLE) {
    321         return HANG_LEFT_CEIL;
    322     }
    323 
    324     ceilOffset = ceilHeight - (nextPos[1] + 160.0f);
    325     if (ceilOffset < -30.0f) {
    326         return HANG_HIT_CEIL_OR_OOB;
    327     }
    328     if (ceilOffset > 30.0f) {
    329         return HANG_LEFT_CEIL;
    330     }
    331 
    332     nextPos[1] = m->ceilHeight - 160.0f;
    333     vec3f_copy(m->pos, nextPos);
    334 
    335     m->floor = floor;
    336     m->floorHeight = floorHeight;
    337     m->ceil = ceil;
    338     m->ceilHeight = ceilHeight;
    339 
    340     return HANG_NONE;
    341 }
    342 
    343 s32 update_hang_moving(struct MarioState *m) {
    344     s32 stepResult;
    345     Vec3f nextPos;
    346     f32 maxSpeed = 4.0f;
    347 
    348     m->forwardVel += 1.0f;
    349     if (m->forwardVel > maxSpeed) {
    350         m->forwardVel = maxSpeed;
    351     }
    352 
    353     m->faceAngle[1] =
    354         m->intendedYaw - approach_s32((s16)(m->intendedYaw - m->faceAngle[1]), 0, 0x800, 0x800);
    355 
    356     m->slideYaw = m->faceAngle[1];
    357     m->slideVelX = m->forwardVel * sins(m->faceAngle[1]);
    358     m->slideVelZ = m->forwardVel * coss(m->faceAngle[1]);
    359 
    360     m->vel[0] = m->slideVelX;
    361     m->vel[1] = 0.0f;
    362     m->vel[2] = m->slideVelZ;
    363 
    364     nextPos[0] = m->pos[0] - m->ceil->normal.y * m->vel[0];
    365     nextPos[2] = m->pos[2] - m->ceil->normal.y * m->vel[2];
    366     nextPos[1] = m->pos[1];
    367 
    368     stepResult = perform_hanging_step(m, nextPos);
    369 
    370     vec3f_copy(m->marioObj->header.gfx.pos, m->pos);
    371     vec3s_set(m->marioObj->header.gfx.angle, 0, m->faceAngle[1], 0);
    372     return stepResult;
    373 }
    374 
    375 void update_hang_stationary(struct MarioState *m) {
    376     m->forwardVel = 0.0f;
    377     m->slideVelX = 0.0f;
    378     m->slideVelZ = 0.0f;
    379 
    380     m->pos[1] = m->ceilHeight - 160.0f;
    381     vec3f_copy(m->vel, gVec3fZero);
    382     vec3f_copy(m->marioObj->header.gfx.pos, m->pos);
    383 }
    384 
    385 s32 act_start_hanging(struct MarioState *m) {
    386 #if ENABLE_RUMBLE
    387     if (m->actionTimer++ == 0) {
    388         queue_rumble_data(5, 80);
    389     }
    390 #else
    391     m->actionTimer++;
    392 #endif
    393 
    394     if ((m->input & INPUT_NONZERO_ANALOG) && m->actionTimer >= 31) {
    395         return set_mario_action(m, ACT_HANGING, 0);
    396     }
    397 
    398     if (!(m->input & INPUT_A_DOWN)) {
    399         return set_mario_action(m, ACT_FREEFALL, 0);
    400     }
    401 
    402     if (m->input & INPUT_Z_PRESSED) {
    403         return set_mario_action(m, ACT_GROUND_POUND, 0);
    404     }
    405 
    406     //! Crash if Mario's referenced ceiling is NULL (same for other hanging actions)
    407     if (m->ceil->type != SURFACE_HANGABLE) {
    408         return set_mario_action(m, ACT_FREEFALL, 0);
    409     }
    410 
    411     set_mario_animation(m, MARIO_ANIM_HANG_ON_CEILING);
    412     play_sound_if_no_flag(m, SOUND_ACTION_HANGING_STEP, MARIO_ACTION_SOUND_PLAYED);
    413     update_hang_stationary(m);
    414 
    415     if (is_anim_at_end(m)) {
    416         set_mario_action(m, ACT_HANGING, 0);
    417     }
    418 
    419     return FALSE;
    420 }
    421 
    422 s32 act_hanging(struct MarioState *m) {
    423     if (m->input & INPUT_NONZERO_ANALOG) {
    424         return set_mario_action(m, ACT_HANG_MOVING, m->actionArg);
    425     }
    426 
    427     if (!(m->input & INPUT_A_DOWN)) {
    428         return set_mario_action(m, ACT_FREEFALL, 0);
    429     }
    430 
    431     if (m->input & INPUT_Z_PRESSED) {
    432         return set_mario_action(m, ACT_GROUND_POUND, 0);
    433     }
    434 
    435     if (m->ceil->type != SURFACE_HANGABLE) {
    436         return set_mario_action(m, ACT_FREEFALL, 0);
    437     }
    438 
    439     if (m->actionArg & 1) {
    440         set_mario_animation(m, MARIO_ANIM_HANDSTAND_LEFT);
    441     } else {
    442         set_mario_animation(m, MARIO_ANIM_HANDSTAND_RIGHT);
    443     }
    444 
    445     update_hang_stationary(m);
    446 
    447     return FALSE;
    448 }
    449 
    450 s32 act_hang_moving(struct MarioState *m) {
    451     if (!(m->input & INPUT_A_DOWN)) {
    452         return set_mario_action(m, ACT_FREEFALL, 0);
    453     }
    454 
    455     if (m->input & INPUT_Z_PRESSED) {
    456         return set_mario_action(m, ACT_GROUND_POUND, 0);
    457     }
    458 
    459     if (m->ceil->type != SURFACE_HANGABLE) {
    460         return set_mario_action(m, ACT_FREEFALL, 0);
    461     }
    462 
    463     if (m->actionArg & 1) {
    464         set_mario_animation(m, MARIO_ANIM_MOVE_ON_WIRE_NET_RIGHT);
    465     } else {
    466         set_mario_animation(m, MARIO_ANIM_MOVE_ON_WIRE_NET_LEFT);
    467     }
    468 
    469     if (m->marioObj->header.gfx.animInfo.animFrame == 12) {
    470         play_sound(SOUND_ACTION_HANGING_STEP, m->marioObj->header.gfx.cameraToObject);
    471 #if ENABLE_RUMBLE
    472         queue_rumble_data(1, 30);
    473 #endif
    474     }
    475 
    476     if (is_anim_past_end(m)) {
    477         m->actionArg ^= 1;
    478         if (m->input & INPUT_UNKNOWN_5) {
    479             return set_mario_action(m, ACT_HANGING, m->actionArg);
    480         }
    481     }
    482 
    483     if (update_hang_moving(m) == HANG_LEFT_CEIL) {
    484         set_mario_action(m, ACT_FREEFALL, 0);
    485     }
    486 
    487     return FALSE;
    488 }
    489 
    490 s32 let_go_of_ledge(struct MarioState *m) {
    491     f32 floorHeight;
    492     struct Surface *floor;
    493 
    494     m->vel[1] = 0.0f;
    495     m->forwardVel = -8.0f;
    496     m->pos[0] -= 60.0f * sins(m->faceAngle[1]);
    497     m->pos[2] -= 60.0f * coss(m->faceAngle[1]);
    498 
    499     floorHeight = find_floor(m->pos[0], m->pos[1], m->pos[2], &floor);
    500     if (floorHeight < m->pos[1] - 100.0f) {
    501         m->pos[1] -= 100.0f;
    502     } else {
    503         m->pos[1] = floorHeight;
    504     }
    505 
    506     return set_mario_action(m, ACT_SOFT_BONK, 0);
    507 }
    508 
    509 void climb_up_ledge(struct MarioState *m) {
    510     set_mario_animation(m, MARIO_ANIM_IDLE_HEAD_LEFT);
    511     m->pos[0] += 14.0f * sins(m->faceAngle[1]);
    512     m->pos[2] += 14.0f * coss(m->faceAngle[1]);
    513     vec3f_copy(m->marioObj->header.gfx.pos, m->pos);
    514 }
    515 
    516 void update_ledge_climb_camera(struct MarioState *m) {
    517     f32 sp4;
    518 
    519     if (m->actionTimer < 14) {
    520         sp4 = m->actionTimer;
    521     } else {
    522         sp4 = 14.0f;
    523     }
    524     m->statusForCamera->pos[0] = m->pos[0] + sp4 * sins(m->faceAngle[1]);
    525     m->statusForCamera->pos[2] = m->pos[2] + sp4 * coss(m->faceAngle[1]);
    526     m->statusForCamera->pos[1] = m->pos[1];
    527     m->actionTimer++;
    528     m->flags |= MARIO_UNKNOWN_25;
    529 }
    530 
    531 void update_ledge_climb(struct MarioState *m, s32 animation, u32 endAction) {
    532     stop_and_set_height_to_floor(m);
    533 
    534     set_mario_animation(m, animation);
    535     if (is_anim_at_end(m)) {
    536         set_mario_action(m, endAction, 0);
    537         if (endAction == ACT_IDLE) {
    538             climb_up_ledge(m);
    539         }
    540     }
    541 }
    542 
    543 s32 act_ledge_grab(struct MarioState *m) {
    544     f32 heightAboveFloor;
    545     s16 intendedDYaw = m->intendedYaw - m->faceAngle[1];
    546     s32 hasSpaceForMario = (m->ceilHeight - m->floorHeight >= 160.0f);
    547 
    548     if (m->actionTimer < 10) {
    549         m->actionTimer++;
    550     }
    551 
    552     if (m->floor->normal.y < 0.9063078f) {
    553         return let_go_of_ledge(m);
    554     }
    555 
    556     if (m->input & (INPUT_Z_PRESSED | INPUT_OFF_FLOOR)) {
    557         return let_go_of_ledge(m);
    558     }
    559 
    560     if ((m->input & INPUT_A_PRESSED) && hasSpaceForMario) {
    561         return set_mario_action(m, ACT_LEDGE_CLIMB_FAST, 0);
    562     }
    563 
    564     if (m->input & INPUT_STOMPED) {
    565         if (m->marioObj->oInteractStatus & INT_STATUS_MARIO_KNOCKBACK_DMG) {
    566             m->hurtCounter += (m->flags & MARIO_CAP_ON_HEAD) ? 12 : 18;
    567         }
    568         return let_go_of_ledge(m);
    569     }
    570     if (m->actionTimer == 10 && (m->input & INPUT_NONZERO_ANALOG)
    571 #ifdef VERSION_EU
    572         // On EU, you can't slow climb up ledges while holding A.
    573         && !(m->input & INPUT_A_DOWN)
    574 #endif
    575     ) {
    576         if (intendedDYaw >= -0x4000 && intendedDYaw <= 0x4000) {
    577             if (hasSpaceForMario) {
    578                 return set_mario_action(m, ACT_LEDGE_CLIMB_SLOW_1, 0);
    579             }
    580         } else {
    581             return let_go_of_ledge(m);
    582         }
    583     }
    584 
    585     heightAboveFloor = m->pos[1] - find_floor_height_relative_polar(m, -0x8000, 30.0f);
    586     if (hasSpaceForMario && heightAboveFloor < 100.0f) {
    587         return set_mario_action(m, ACT_LEDGE_CLIMB_FAST, 0);
    588     }
    589 
    590     if (m->actionArg == 0) {
    591         play_sound_if_no_flag(m, SOUND_MARIO_WHOA, MARIO_MARIO_SOUND_PLAYED);
    592     }
    593 
    594     stop_and_set_height_to_floor(m);
    595     set_mario_animation(m, MARIO_ANIM_IDLE_ON_LEDGE);
    596 
    597     return FALSE;
    598 }
    599 
    600 s32 act_ledge_climb_slow(struct MarioState *m) {
    601     if (m->input & INPUT_OFF_FLOOR) {
    602         return let_go_of_ledge(m);
    603     }
    604 
    605     if (m->actionTimer >= 28
    606         && (m->input
    607             & (INPUT_NONZERO_ANALOG | INPUT_A_PRESSED | INPUT_OFF_FLOOR | INPUT_ABOVE_SLIDE))) {
    608         climb_up_ledge(m);
    609         return check_common_action_exits(m);
    610     }
    611 
    612     if (m->actionTimer == 10) {
    613         play_sound_if_no_flag(m, SOUND_MARIO_EEUH, MARIO_MARIO_SOUND_PLAYED);
    614     }
    615 
    616     update_ledge_climb(m, MARIO_ANIM_SLOW_LEDGE_GRAB, ACT_IDLE);
    617 
    618     update_ledge_climb_camera(m);
    619     if (m->marioObj->header.gfx.animInfo.animFrame == 17) {
    620         m->action = ACT_LEDGE_CLIMB_SLOW_2;
    621     }
    622 
    623     return FALSE;
    624 }
    625 
    626 s32 act_ledge_climb_down(struct MarioState *m) {
    627     if (m->input & INPUT_OFF_FLOOR) {
    628         return let_go_of_ledge(m);
    629     }
    630 
    631     play_sound_if_no_flag(m, SOUND_MARIO_WHOA, MARIO_MARIO_SOUND_PLAYED);
    632 
    633     update_ledge_climb(m, MARIO_ANIM_CLIMB_DOWN_LEDGE, ACT_LEDGE_GRAB);
    634     m->actionArg = 1;
    635 
    636     return FALSE;
    637 }
    638 
    639 s32 act_ledge_climb_fast(struct MarioState *m) {
    640     if (m->input & INPUT_OFF_FLOOR) {
    641         return let_go_of_ledge(m);
    642     }
    643 
    644     play_sound_if_no_flag(m, SOUND_MARIO_UH2, MARIO_MARIO_SOUND_PLAYED);
    645 
    646     update_ledge_climb(m, MARIO_ANIM_FAST_LEDGE_GRAB, ACT_IDLE);
    647 
    648     if (m->marioObj->header.gfx.animInfo.animFrame == 8) {
    649         play_mario_landing_sound(m, SOUND_ACTION_TERRAIN_LANDING);
    650     }
    651     update_ledge_climb_camera(m);
    652 
    653     return FALSE;
    654 }
    655 
    656 s32 act_grabbed(struct MarioState *m) {
    657     if (m->marioObj->oInteractStatus & INT_STATUS_MARIO_UNK2) {
    658         s32 thrown = (m->marioObj->oInteractStatus & INT_STATUS_MARIO_UNK6) == 0;
    659 
    660         m->faceAngle[1] = m->usedObj->oMoveAngleYaw;
    661         vec3f_copy(m->pos, m->marioObj->header.gfx.pos);
    662 #if ENABLE_RUMBLE
    663         queue_rumble_data(5, 60);
    664 #endif
    665 
    666         return set_mario_action(m, (m->forwardVel >= 0.0f) ? ACT_THROWN_FORWARD : ACT_THROWN_BACKWARD,
    667                                 thrown);
    668     }
    669 
    670     set_mario_animation(m, MARIO_ANIM_BEING_GRABBED);
    671     return FALSE;
    672 }
    673 
    674 s32 act_in_cannon(struct MarioState *m) {
    675     struct Object *marioObj = m->marioObj;
    676     s16 startFacePitch = m->faceAngle[0];
    677     s16 startFaceYaw = m->faceAngle[1];
    678 
    679     switch (m->actionState) {
    680         case 0:
    681             m->marioObj->header.gfx.node.flags &= ~GRAPH_RENDER_ACTIVE;
    682             m->usedObj->oInteractStatus = INT_STATUS_INTERACTED;
    683 
    684             m->statusForCamera->cameraEvent = CAM_EVENT_CANNON;
    685             m->statusForCamera->usedObj = m->usedObj;
    686 
    687             vec3f_set(m->vel, 0.0f, 0.0f, 0.0f);
    688 
    689             m->pos[0] = m->usedObj->oPosX;
    690             m->pos[1] = m->usedObj->oPosY + 350.0f;
    691             m->pos[2] = m->usedObj->oPosZ;
    692 
    693             m->forwardVel = 0.0f;
    694 
    695             m->actionState = 1;
    696             break;
    697 
    698         case 1:
    699             if (m->usedObj->oAction == 1) {
    700                 m->faceAngle[0] = m->usedObj->oMoveAnglePitch;
    701                 m->faceAngle[1] = m->usedObj->oMoveAngleYaw;
    702 
    703                 marioObj->oMarioCannonObjectYaw = m->usedObj->oMoveAngleYaw;
    704                 marioObj->oMarioCannonInputYaw = 0;
    705 
    706                 m->actionState = 2;
    707             }
    708             break;
    709 
    710         case 2:
    711             m->faceAngle[0] -= (s16)(m->controller->stickY * 10.0f);
    712             marioObj->oMarioCannonInputYaw -= (s16)(m->controller->stickX * 10.0f);
    713 
    714             if (m->faceAngle[0] > 0x38E3) {
    715                 m->faceAngle[0] = 0x38E3;
    716             }
    717             if (m->faceAngle[0] < 0) {
    718                 m->faceAngle[0] = 0;
    719             }
    720 
    721             if (marioObj->oMarioCannonInputYaw > 0x4000) {
    722                 marioObj->oMarioCannonInputYaw = 0x4000;
    723             }
    724             if (marioObj->oMarioCannonInputYaw < -0x4000) {
    725                 marioObj->oMarioCannonInputYaw = -0x4000;
    726             }
    727 
    728             m->faceAngle[1] = marioObj->oMarioCannonObjectYaw + marioObj->oMarioCannonInputYaw;
    729             if (m->input & INPUT_A_PRESSED) {
    730                 m->forwardVel = 100.0f * coss(m->faceAngle[0]);
    731 
    732                 m->vel[1] = 100.0f * sins(m->faceAngle[0]);
    733 
    734                 m->pos[0] += 120.0f * coss(m->faceAngle[0]) * sins(m->faceAngle[1]);
    735                 m->pos[1] += 120.0f * sins(m->faceAngle[0]);
    736                 m->pos[2] += 120.0f * coss(m->faceAngle[0]) * coss(m->faceAngle[1]);
    737 
    738                 play_sound(SOUND_ACTION_FLYING_FAST, m->marioObj->header.gfx.cameraToObject);
    739                 play_sound(SOUND_OBJ_POUNDING_CANNON, m->marioObj->header.gfx.cameraToObject);
    740 
    741                 m->marioObj->header.gfx.node.flags |= GRAPH_RENDER_ACTIVE;
    742 
    743                 set_mario_action(m, ACT_SHOT_FROM_CANNON, 0);
    744 #if ENABLE_RUMBLE
    745                 queue_rumble_data(60, 70);
    746 #endif
    747                 m->usedObj->oAction = 2;
    748                 return FALSE;
    749             } else if (m->faceAngle[0] != startFacePitch || m->faceAngle[1] != startFaceYaw) {
    750                 play_sound(SOUND_MOVING_AIM_CANNON, m->marioObj->header.gfx.cameraToObject);
    751 #if ENABLE_RUMBLE
    752                 reset_rumble_timers_2(0);
    753 #endif
    754             }
    755     }
    756 
    757     vec3f_copy(m->marioObj->header.gfx.pos, m->pos);
    758     vec3s_set(m->marioObj->header.gfx.angle, 0, m->faceAngle[1], 0);
    759     set_mario_animation(m, MARIO_ANIM_DIVE);
    760 
    761     return FALSE;
    762 }
    763 
    764 s32 act_tornado_twirling(struct MarioState *m) {
    765     struct Surface *floor;
    766     Vec3f nextPos;
    767     f32 sinAngleVel;
    768     f32 cosAngleVel;
    769     f32 floorHeight;
    770     struct Object *marioObj = m->marioObj;
    771     struct Object *usedObj = m->usedObj;
    772     s16 prevTwirlYaw = m->twirlYaw;
    773 
    774     f32 dx = (m->pos[0] - usedObj->oPosX) * 0.95f;
    775     f32 dz = (m->pos[2] - usedObj->oPosZ) * 0.95f;
    776 
    777     if (m->vel[1] < 60.0f) {
    778         m->vel[1] += 1.0f;
    779     }
    780 
    781     if ((marioObj->oMarioTornadoPosY += m->vel[1]) < 0.0f) {
    782         marioObj->oMarioTornadoPosY = 0.0f;
    783     }
    784     if (marioObj->oMarioTornadoPosY > usedObj->hitboxHeight) {
    785         if (m->vel[1] < 20.0f) {
    786             m->vel[1] = 20.0f;
    787         }
    788         return set_mario_action(m, ACT_TWIRLING, 1);
    789     }
    790 
    791     if (m->angleVel[1] < 0x3000) {
    792         m->angleVel[1] += 0x100;
    793     }
    794 
    795     if (marioObj->oMarioTornadoYawVel < 0x1000) {
    796         marioObj->oMarioTornadoYawVel += 0x100;
    797     }
    798 
    799     m->twirlYaw += m->angleVel[1];
    800 
    801     sinAngleVel = sins(marioObj->oMarioTornadoYawVel);
    802     cosAngleVel = coss(marioObj->oMarioTornadoYawVel);
    803 
    804     nextPos[0] = usedObj->oPosX + dx * cosAngleVel + dz * sinAngleVel;
    805     nextPos[2] = usedObj->oPosZ - dx * sinAngleVel + dz * cosAngleVel;
    806     nextPos[1] = usedObj->oPosY + marioObj->oMarioTornadoPosY;
    807 
    808     f32_find_wall_collision(&nextPos[0], &nextPos[1], &nextPos[2], 60.0f, 50.0f);
    809 
    810     floorHeight = find_floor(nextPos[0], nextPos[1], nextPos[2], &floor);
    811     if (floor != NULL) {
    812         m->floor = floor;
    813         m->floorHeight = floorHeight;
    814         vec3f_copy(m->pos, nextPos);
    815     } else {
    816         if (nextPos[1] >= m->floorHeight) {
    817             m->pos[1] = nextPos[1];
    818         } else {
    819             m->pos[1] = m->floorHeight;
    820         }
    821     }
    822 
    823     m->actionTimer++;
    824 
    825     set_mario_animation(m, (m->actionArg == 0) ? MARIO_ANIM_START_TWIRL : MARIO_ANIM_TWIRL);
    826 
    827     if (is_anim_past_end(m)) {
    828         m->actionArg = 1;
    829     }
    830 
    831     // Play sound on angle overflow
    832     if (prevTwirlYaw > m->twirlYaw) {
    833         play_sound(SOUND_ACTION_TWIRL, m->marioObj->header.gfx.cameraToObject);
    834     }
    835 
    836     vec3f_copy(m->marioObj->header.gfx.pos, m->pos);
    837     vec3s_set(m->marioObj->header.gfx.angle, 0, m->faceAngle[1] + m->twirlYaw, 0);
    838 #if ENABLE_RUMBLE
    839     reset_rumble_timers();
    840 #endif
    841 
    842     return FALSE;
    843 }
    844 
    845 s32 check_common_automatic_cancels(struct MarioState *m) {
    846     if (m->pos[1] < m->waterLevel - 100) {
    847         return set_water_plunge_action(m);
    848     }
    849 
    850     return FALSE;
    851 }
    852 
    853 s32 mario_execute_automatic_action(struct MarioState *m) {
    854     s32 cancel;
    855 
    856     if (check_common_automatic_cancels(m)) {
    857         return TRUE;
    858     }
    859 
    860     m->quicksandDepth = 0.0f;
    861 
    862     /* clang-format off */
    863     switch (m->action) {
    864         case ACT_HOLDING_POLE:           cancel = act_holding_pole(m);           break;
    865         case ACT_GRAB_POLE_SLOW:         cancel = act_grab_pole_slow(m);         break;
    866         case ACT_GRAB_POLE_FAST:         cancel = act_grab_pole_fast(m);         break;
    867         case ACT_CLIMBING_POLE:          cancel = act_climbing_pole(m);          break;
    868         case ACT_TOP_OF_POLE_TRANSITION: cancel = act_top_of_pole_transition(m); break;
    869         case ACT_TOP_OF_POLE:            cancel = act_top_of_pole(m);            break;
    870         case ACT_START_HANGING:          cancel = act_start_hanging(m);          break;
    871         case ACT_HANGING:                cancel = act_hanging(m);                break;
    872         case ACT_HANG_MOVING:            cancel = act_hang_moving(m);            break;
    873         case ACT_LEDGE_GRAB:             cancel = act_ledge_grab(m);             break;
    874         case ACT_LEDGE_CLIMB_SLOW_1:     cancel = act_ledge_climb_slow(m);       break;
    875         case ACT_LEDGE_CLIMB_SLOW_2:     cancel = act_ledge_climb_slow(m);       break;
    876         case ACT_LEDGE_CLIMB_DOWN:       cancel = act_ledge_climb_down(m);       break;
    877         case ACT_LEDGE_CLIMB_FAST:       cancel = act_ledge_climb_fast(m);       break;
    878         case ACT_GRABBED:                cancel = act_grabbed(m);                break;
    879         case ACT_IN_CANNON:              cancel = act_in_cannon(m);              break;
    880         case ACT_TORNADO_TWIRLING:       cancel = act_tornado_twirling(m);       break;
    881     }
    882     /* clang-format on */
    883 
    884     return cancel;
    885 }