sm64

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

interaction.c (61913B)


      1 #include <PR/ultratypes.h>
      2 
      3 #include "area.h"
      4 #include "actors/common1.h"
      5 #include "audio/external.h"
      6 #include "behavior_actions.h"
      7 #include "behavior_data.h"
      8 #include "camera.h"
      9 #include "course_table.h"
     10 #include "dialog_ids.h"
     11 #include "engine/math_util.h"
     12 #include "engine/surface_collision.h"
     13 #include "game_init.h"
     14 #include "interaction.h"
     15 #include "level_update.h"
     16 #include "mario.h"
     17 #include "mario_step.h"
     18 #include "memory.h"
     19 #include "obj_behaviors.h"
     20 #include "object_helpers.h"
     21 #include "save_file.h"
     22 #include "seq_ids.h"
     23 #include "sm64.h"
     24 #include "sound_init.h"
     25 #include "rumble_init.h"
     26 
     27 #define INT_GROUND_POUND_OR_TWIRL (1 << 0) // 0x01
     28 #define INT_PUNCH                 (1 << 1) // 0x02
     29 #define INT_KICK                  (1 << 2) // 0x04
     30 #define INT_TRIP                  (1 << 3) // 0x08
     31 #define INT_SLIDE_KICK            (1 << 4) // 0x10
     32 #define INT_FAST_ATTACK_OR_SHELL  (1 << 5) // 0x20
     33 #define INT_HIT_FROM_ABOVE        (1 << 6) // 0x40
     34 #define INT_HIT_FROM_BELOW        (1 << 7) // 0x80
     35 
     36 #define INT_ATTACK_NOT_FROM_BELOW                                                 \
     37     (INT_GROUND_POUND_OR_TWIRL | INT_PUNCH | INT_KICK | INT_TRIP | INT_SLIDE_KICK \
     38      | INT_FAST_ATTACK_OR_SHELL | INT_HIT_FROM_ABOVE)
     39 
     40 #define INT_ANY_ATTACK                                                            \
     41     (INT_GROUND_POUND_OR_TWIRL | INT_PUNCH | INT_KICK | INT_TRIP | INT_SLIDE_KICK \
     42      | INT_FAST_ATTACK_OR_SHELL | INT_HIT_FROM_ABOVE | INT_HIT_FROM_BELOW)
     43 
     44 #define INT_ATTACK_NOT_WEAK_FROM_ABOVE                                                \
     45     (INT_GROUND_POUND_OR_TWIRL | INT_PUNCH | INT_KICK | INT_TRIP | INT_HIT_FROM_BELOW)
     46 
     47 u8 sDelayInvincTimer;
     48 s16 sInvulnerable;
     49 u32 interact_coin(struct MarioState *, u32, struct Object *);
     50 u32 interact_water_ring(struct MarioState *, u32, struct Object *);
     51 u32 interact_star_or_key(struct MarioState *, u32, struct Object *);
     52 u32 interact_bbh_entrance(struct MarioState *, u32, struct Object *);
     53 u32 interact_warp(struct MarioState *, u32, struct Object *);
     54 u32 interact_warp_door(struct MarioState *, u32, struct Object *);
     55 u32 interact_door(struct MarioState *, u32, struct Object *);
     56 u32 interact_cannon_base(struct MarioState *, u32, struct Object *);
     57 u32 interact_igloo_barrier(struct MarioState *, u32, struct Object *);
     58 u32 interact_tornado(struct MarioState *, u32, struct Object *);
     59 u32 interact_whirlpool(struct MarioState *, u32, struct Object *);
     60 u32 interact_strong_wind(struct MarioState *, u32, struct Object *);
     61 u32 interact_flame(struct MarioState *, u32, struct Object *);
     62 u32 interact_snufit_bullet(struct MarioState *, u32, struct Object *);
     63 u32 interact_clam_or_bubba(struct MarioState *, u32, struct Object *);
     64 u32 interact_bully(struct MarioState *, u32, struct Object *);
     65 u32 interact_shock(struct MarioState *, u32, struct Object *);
     66 u32 interact_mr_blizzard(struct MarioState *, u32, struct Object *);
     67 u32 interact_hit_from_below(struct MarioState *, u32, struct Object *);
     68 u32 interact_bounce_top(struct MarioState *, u32, struct Object *);
     69 u32 interact_unknown_08(struct MarioState *, u32, struct Object *);
     70 u32 interact_damage(struct MarioState *, u32, struct Object *);
     71 u32 interact_breakable(struct MarioState *, u32, struct Object *);
     72 u32 interact_koopa_shell(struct MarioState *, u32, struct Object *);
     73 u32 interact_pole(struct MarioState *, u32, struct Object *);
     74 u32 interact_hoot(struct MarioState *, u32, struct Object *);
     75 u32 interact_cap(struct MarioState *, u32, struct Object *);
     76 u32 interact_grabbable(struct MarioState *, u32, struct Object *);
     77 u32 interact_text(struct MarioState *, u32, struct Object *);
     78 
     79 struct InteractionHandler {
     80     u32 interactType;
     81     u32 (*handler)(struct MarioState *, u32, struct Object *);
     82 };
     83 
     84 static struct InteractionHandler sInteractionHandlers[] = {
     85     { INTERACT_COIN,           interact_coin },
     86     { INTERACT_WATER_RING,     interact_water_ring },
     87     { INTERACT_STAR_OR_KEY,    interact_star_or_key },
     88     { INTERACT_BBH_ENTRANCE,   interact_bbh_entrance },
     89     { INTERACT_WARP,           interact_warp },
     90     { INTERACT_WARP_DOOR,      interact_warp_door },
     91     { INTERACT_DOOR,           interact_door },
     92     { INTERACT_CANNON_BASE,    interact_cannon_base },
     93     { INTERACT_IGLOO_BARRIER,  interact_igloo_barrier },
     94     { INTERACT_TORNADO,        interact_tornado },
     95     { INTERACT_WHIRLPOOL,      interact_whirlpool },
     96     { INTERACT_STRONG_WIND,    interact_strong_wind },
     97     { INTERACT_FLAME,          interact_flame },
     98     { INTERACT_SNUFIT_BULLET,  interact_snufit_bullet },
     99     { INTERACT_CLAM_OR_BUBBA,  interact_clam_or_bubba },
    100     { INTERACT_BULLY,          interact_bully },
    101     { INTERACT_SHOCK,          interact_shock },
    102     { INTERACT_BOUNCE_TOP2,    interact_bounce_top },
    103     { INTERACT_MR_BLIZZARD,    interact_mr_blizzard },
    104     { INTERACT_HIT_FROM_BELOW, interact_hit_from_below },
    105     { INTERACT_BOUNCE_TOP,     interact_bounce_top },
    106     { INTERACT_DAMAGE,         interact_damage },
    107     { INTERACT_POLE,           interact_pole },
    108     { INTERACT_HOOT,           interact_hoot },
    109     { INTERACT_BREAKABLE,      interact_breakable },
    110     { INTERACT_KOOPA,          interact_bounce_top },
    111     { INTERACT_KOOPA_SHELL,    interact_koopa_shell },
    112     { INTERACT_UNKNOWN_08,     interact_unknown_08 },
    113     { INTERACT_CAP,            interact_cap },
    114     { INTERACT_GRABBABLE,      interact_grabbable },
    115     { INTERACT_TEXT,           interact_text },
    116 };
    117 
    118 static u32 sForwardKnockbackActions[][3] = {
    119     { ACT_SOFT_FORWARD_GROUND_KB, ACT_FORWARD_GROUND_KB, ACT_HARD_FORWARD_GROUND_KB },
    120     { ACT_FORWARD_AIR_KB,         ACT_FORWARD_AIR_KB,    ACT_HARD_FORWARD_AIR_KB },
    121     { ACT_FORWARD_WATER_KB,       ACT_FORWARD_WATER_KB,  ACT_FORWARD_WATER_KB },
    122 };
    123 
    124 static u32 sBackwardKnockbackActions[][3] = {
    125     { ACT_SOFT_BACKWARD_GROUND_KB, ACT_BACKWARD_GROUND_KB, ACT_HARD_BACKWARD_GROUND_KB },
    126     { ACT_BACKWARD_AIR_KB,         ACT_BACKWARD_AIR_KB,    ACT_HARD_BACKWARD_AIR_KB },
    127     { ACT_BACKWARD_WATER_KB,       ACT_BACKWARD_WATER_KB,  ACT_BACKWARD_WATER_KB },
    128 };
    129 
    130 static u8 sDisplayingDoorText = FALSE;
    131 static u8 sJustTeleported = FALSE;
    132 static u8 sPSSSlideStarted = FALSE;
    133 
    134 /**
    135  * Returns the type of cap Mario is wearing.
    136  */
    137 u32 get_mario_cap_flag(struct Object *capObject) {
    138     const BehaviorScript *script = virtual_to_segmented(0x13, capObject->behavior);
    139 
    140     if (script == bhvNormalCap) {
    141         return MARIO_NORMAL_CAP;
    142     } else if (script == bhvMetalCap) {
    143         return MARIO_METAL_CAP;
    144     } else if (script == bhvWingCap) {
    145         return MARIO_WING_CAP;
    146     } else if (script == bhvVanishCap) {
    147         return MARIO_VANISH_CAP;
    148     }
    149 
    150     return 0;
    151 }
    152 
    153 /**
    154  * Returns true if the passed in object has a moving angle yaw
    155  * in the angular range given towards Mario.
    156  */
    157 u32 object_facing_mario(struct MarioState *m, struct Object *o, s16 angleRange) {
    158     f32 dx = m->pos[0] - o->oPosX;
    159     f32 dz = m->pos[2] - o->oPosZ;
    160 
    161     s16 angleToMario = atan2s(dz, dx);
    162     s16 dAngle = angleToMario - o->oMoveAngleYaw;
    163 
    164     if (-angleRange <= dAngle && dAngle <= angleRange) {
    165         return TRUE;
    166     }
    167 
    168     return FALSE;
    169 }
    170 
    171 s16 mario_obj_angle_to_object(struct MarioState *m, struct Object *o) {
    172     f32 dx = o->oPosX - m->pos[0];
    173     f32 dz = o->oPosZ - m->pos[2];
    174 
    175     return atan2s(dz, dx);
    176 }
    177 
    178 /**
    179  * Determines Mario's interaction with a given object depending on their proximity,
    180  * action, speed, and position.
    181  */
    182 u32 determine_interaction(struct MarioState *m, struct Object *o) {
    183     u32 interaction = 0;
    184     u32 action = m->action;
    185 
    186     if (action & ACT_FLAG_ATTACKING) {
    187         if (action == ACT_PUNCHING || action == ACT_MOVE_PUNCHING || action == ACT_JUMP_KICK) {
    188             s16 dYawToObject = mario_obj_angle_to_object(m, o) - m->faceAngle[1];
    189 
    190             if (m->flags & MARIO_PUNCHING) {
    191                 // 120 degrees total, or 60 each way
    192                 if (-0x2AAA <= dYawToObject && dYawToObject <= 0x2AAA) {
    193                     interaction = INT_PUNCH;
    194                 }
    195             }
    196             if (m->flags & MARIO_KICKING) {
    197                 // 120 degrees total, or 60 each way
    198                 if (-0x2AAA <= dYawToObject && dYawToObject <= 0x2AAA) {
    199                     interaction = INT_KICK;
    200                 }
    201             }
    202             if (m->flags & MARIO_TRIPPING) {
    203                 // 180 degrees total, or 90 each way
    204                 if (-0x4000 <= dYawToObject && dYawToObject <= 0x4000) {
    205                     interaction = INT_TRIP;
    206                 }
    207             }
    208         } else if (action == ACT_GROUND_POUND || action == ACT_TWIRLING) {
    209             if (m->vel[1] < 0.0f) {
    210                 interaction = INT_GROUND_POUND_OR_TWIRL;
    211             }
    212         } else if (action == ACT_GROUND_POUND_LAND || action == ACT_TWIRL_LAND) {
    213             // Neither ground pounding nor twirling change Mario's vertical speed on landing.,
    214             // so the speed check is nearly always true (perhaps not if you land while going upwards?)
    215             // Additionally, actionState it set on each first thing in their action, so this is
    216             // only true prior to the very first frame (i.e. active 1 frame prior to it run).
    217             if (m->vel[1] < 0.0f && m->actionState == 0) {
    218                 interaction = INT_GROUND_POUND_OR_TWIRL;
    219             }
    220         } else if (action == ACT_SLIDE_KICK || action == ACT_SLIDE_KICK_SLIDE) {
    221             interaction = INT_SLIDE_KICK;
    222         } else if (action & ACT_FLAG_RIDING_SHELL) {
    223             interaction = INT_FAST_ATTACK_OR_SHELL;
    224         } else if (m->forwardVel <= -26.0f || 26.0f <= m->forwardVel) {
    225             interaction = INT_FAST_ATTACK_OR_SHELL;
    226         }
    227     }
    228 
    229     // Prior to this, the interaction type could be overwritten. This requires, however,
    230     // that the interaction not be set prior. This specifically overrides turning a ground
    231     // pound into just a bounce.
    232     if (interaction == 0 && (action & ACT_FLAG_AIR)) {
    233         if (m->vel[1] < 0.0f) {
    234             if (m->pos[1] > o->oPosY) {
    235                 interaction = INT_HIT_FROM_ABOVE;
    236             }
    237         } else {
    238             if (m->pos[1] < o->oPosY) {
    239                 interaction = INT_HIT_FROM_BELOW;
    240             }
    241         }
    242     }
    243 
    244     return interaction;
    245 }
    246 
    247 /**
    248  * Sets the interaction types for INT_STATUS_INTERACTED, INT_STATUS_WAS_ATTACKED
    249  */
    250 u32 attack_object(struct Object *o, s32 interaction) {
    251     u32 attackType = 0;
    252 
    253     switch (interaction) {
    254         case INT_GROUND_POUND_OR_TWIRL:
    255             attackType = ATTACK_GROUND_POUND_OR_TWIRL;
    256             break;
    257         case INT_PUNCH:
    258             attackType = ATTACK_PUNCH;
    259             break;
    260         case INT_KICK:
    261         case INT_TRIP:
    262             attackType = ATTACK_KICK_OR_TRIP;
    263             break;
    264         case INT_SLIDE_KICK:
    265         case INT_FAST_ATTACK_OR_SHELL:
    266             attackType = ATTACK_FAST_ATTACK;
    267             break;
    268         case INT_HIT_FROM_ABOVE:
    269             attackType = ATTACK_FROM_ABOVE;
    270             break;
    271         case INT_HIT_FROM_BELOW:
    272             attackType = ATTACK_FROM_BELOW;
    273             break;
    274     }
    275 
    276     o->oInteractStatus = attackType + (INT_STATUS_INTERACTED | INT_STATUS_WAS_ATTACKED);
    277     return attackType;
    278 }
    279 
    280 void mario_stop_riding_object(struct MarioState *m) {
    281     if (m->riddenObj != NULL) {
    282         m->riddenObj->oInteractStatus = INT_STATUS_STOP_RIDING;
    283         stop_shell_music();
    284         m->riddenObj = NULL;
    285     }
    286 }
    287 
    288 void mario_grab_used_object(struct MarioState *m) {
    289     if (m->heldObj == NULL) {
    290         m->heldObj = m->usedObj;
    291         obj_set_held_state(m->heldObj, bhvCarrySomething3);
    292     }
    293 }
    294 
    295 void mario_drop_held_object(struct MarioState *m) {
    296     if (m->heldObj != NULL) {
    297         if (m->heldObj->behavior == segmented_to_virtual(bhvKoopaShellUnderwater)) {
    298             stop_shell_music();
    299         }
    300 
    301         obj_set_held_state(m->heldObj, bhvCarrySomething4);
    302 
    303         // ! When dropping an object instead of throwing it, it will be put at Mario's
    304         // y-positon instead of the HOLP's y-position. This fact is often exploited when
    305         // cloning objects.
    306         m->heldObj->oPosX = m->marioBodyState->heldObjLastPosition[0];
    307         m->heldObj->oPosY = m->pos[1];
    308         m->heldObj->oPosZ = m->marioBodyState->heldObjLastPosition[2];
    309 
    310         m->heldObj->oMoveAngleYaw = m->faceAngle[1];
    311 
    312         m->heldObj = NULL;
    313     }
    314 }
    315 
    316 void mario_throw_held_object(struct MarioState *m) {
    317     if (m->heldObj != NULL) {
    318         if (m->heldObj->behavior == segmented_to_virtual(bhvKoopaShellUnderwater)) {
    319             stop_shell_music();
    320         }
    321 
    322         obj_set_held_state(m->heldObj, bhvCarrySomething5);
    323 
    324         m->heldObj->oPosX = m->marioBodyState->heldObjLastPosition[0] + 32.0f * sins(m->faceAngle[1]);
    325         m->heldObj->oPosY = m->marioBodyState->heldObjLastPosition[1];
    326         m->heldObj->oPosZ = m->marioBodyState->heldObjLastPosition[2] + 32.0f * coss(m->faceAngle[1]);
    327 
    328         m->heldObj->oMoveAngleYaw = m->faceAngle[1];
    329 
    330         m->heldObj = NULL;
    331     }
    332 }
    333 
    334 void mario_stop_riding_and_holding(struct MarioState *m) {
    335     mario_drop_held_object(m);
    336     mario_stop_riding_object(m);
    337 
    338     if (m->action == ACT_RIDING_HOOT) {
    339         m->usedObj->oInteractStatus = FALSE;
    340         m->usedObj->oHootMarioReleaseTime = gGlobalTimer;
    341     }
    342 }
    343 
    344 u32 does_mario_have_normal_cap_on_head(struct MarioState *m) {
    345     return (m->flags & (MARIO_CAPS | MARIO_CAP_ON_HEAD)) == (MARIO_NORMAL_CAP | MARIO_CAP_ON_HEAD);
    346 }
    347 
    348 void mario_blow_off_cap(struct MarioState *m, f32 capSpeed) {
    349     struct Object *capObject;
    350 
    351     if (does_mario_have_normal_cap_on_head(m)) {
    352         save_file_set_cap_pos(m->pos[0], m->pos[1], m->pos[2]);
    353 
    354         m->flags &= ~(MARIO_NORMAL_CAP | MARIO_CAP_ON_HEAD);
    355 
    356         capObject = spawn_object(m->marioObj, MODEL_MARIOS_CAP, bhvNormalCap);
    357 
    358         capObject->oPosY += (m->action & ACT_FLAG_SHORT_HITBOX) ? 120.0f : 180.0f;
    359         capObject->oForwardVel = capSpeed;
    360         capObject->oMoveAngleYaw = (s16)(m->faceAngle[1] + 0x400);
    361 
    362         if (m->forwardVel < 0.0f) {
    363             capObject->oMoveAngleYaw = (s16)(capObject->oMoveAngleYaw + 0x8000);
    364         }
    365     }
    366 }
    367 
    368 u32 mario_lose_cap_to_enemy(u32 arg) {
    369     u32 wasWearingCap = FALSE;
    370 
    371     if (does_mario_have_normal_cap_on_head(gMarioState)) {
    372         save_file_set_flags(arg == 1 ? SAVE_FLAG_CAP_ON_KLEPTO : SAVE_FLAG_CAP_ON_UKIKI);
    373         gMarioState->flags &= ~(MARIO_NORMAL_CAP | MARIO_CAP_ON_HEAD);
    374         wasWearingCap = TRUE;
    375     }
    376 
    377     return wasWearingCap;
    378 }
    379 
    380 void mario_retrieve_cap(void) {
    381     mario_drop_held_object(gMarioState);
    382     save_file_clear_flags(SAVE_FLAG_CAP_ON_KLEPTO | SAVE_FLAG_CAP_ON_UKIKI);
    383     gMarioState->flags &= ~MARIO_CAP_ON_HEAD;
    384     gMarioState->flags |= MARIO_NORMAL_CAP | MARIO_CAP_IN_HAND;
    385 }
    386 
    387 u32 able_to_grab_object(struct MarioState *m, UNUSED struct Object *o) {
    388     u32 action = m->action;
    389 
    390     if (action == ACT_DIVE_SLIDE || action == ACT_DIVE) {
    391         if (!(o->oInteractionSubtype & INT_SUBTYPE_GRABS_MARIO)) {
    392             return TRUE;
    393         }
    394     } else if (action == ACT_PUNCHING || action == ACT_MOVE_PUNCHING) {
    395         if (m->actionArg < 2) {
    396             return TRUE;
    397         }
    398     }
    399 
    400     return FALSE;
    401 }
    402 
    403 struct Object *mario_get_collided_object(struct MarioState *m, u32 interactType) {
    404     s32 i;
    405     struct Object *object;
    406 
    407     for (i = 0; i < m->marioObj->numCollidedObjs; i++) {
    408         object = m->marioObj->collidedObjs[i];
    409 
    410         if (object->oInteractType == interactType) {
    411             return object;
    412         }
    413     }
    414 
    415     return NULL;
    416 }
    417 
    418 u32 mario_check_object_grab(struct MarioState *m) {
    419     u32 result = FALSE;
    420     const BehaviorScript *script;
    421 
    422     if (m->input & INPUT_INTERACT_OBJ_GRABBABLE) {
    423         script = virtual_to_segmented(0x13, m->interactObj->behavior);
    424 
    425         if (script == bhvBowser) {
    426             s16 facingDYaw = m->faceAngle[1] - m->interactObj->oMoveAngleYaw;
    427             if (facingDYaw >= -0x5555 && facingDYaw <= 0x5555) {
    428                 m->faceAngle[1] = m->interactObj->oMoveAngleYaw;
    429                 m->usedObj = m->interactObj;
    430                 result = set_mario_action(m, ACT_PICKING_UP_BOWSER, 0);
    431             }
    432         } else {
    433             s16 facingDYaw = mario_obj_angle_to_object(m, m->interactObj) - m->faceAngle[1];
    434             if (facingDYaw >= -0x2AAA && facingDYaw <= 0x2AAA) {
    435                 m->usedObj = m->interactObj;
    436 
    437                 if (!(m->action & ACT_FLAG_AIR)) {
    438                     set_mario_action(
    439                         m, (m->action & ACT_FLAG_DIVING) ? ACT_DIVE_PICKING_UP : ACT_PICKING_UP, 0);
    440                 }
    441 
    442                 result = TRUE;
    443             }
    444         }
    445     }
    446 
    447     return result;
    448 }
    449 
    450 u32 bully_knock_back_mario(struct MarioState *mario) {
    451     struct BullyCollisionData marioData;
    452     struct BullyCollisionData bullyData;
    453     s16 newMarioYaw;
    454     s16 newBullyYaw;
    455     s16 marioDYaw;
    456     UNUSED s16 bullyDYaw;
    457 
    458     u32 bonkAction = 0;
    459 
    460     struct Object *bully = mario->interactObj;
    461 
    462     //! Conversion ratios multiply to more than 1 (could allow unbounded speed
    463     // with bonk cancel - but this isn't important for regular bully battery)
    464     f32 bullyToMarioRatio = bully->hitboxRadius * 3 / 53;
    465     f32 marioToBullyRatio = 53.0f / bully->hitboxRadius;
    466 
    467     init_bully_collision_data(&marioData, mario->pos[0], mario->pos[2], mario->forwardVel,
    468                               mario->faceAngle[1], bullyToMarioRatio, 52.0f);
    469 
    470     init_bully_collision_data(&bullyData, bully->oPosX, bully->oPosZ, bully->oForwardVel,
    471                               bully->oMoveAngleYaw, marioToBullyRatio, bully->hitboxRadius + 2.0f);
    472 
    473     if (mario->forwardVel != 0.0f) {
    474         transfer_bully_speed(&marioData, &bullyData);
    475     } else {
    476         transfer_bully_speed(&bullyData, &marioData);
    477     }
    478 
    479     newMarioYaw = atan2s(marioData.velZ, marioData.velX);
    480     newBullyYaw = atan2s(bullyData.velZ, bullyData.velX);
    481 
    482     marioDYaw = newMarioYaw - mario->faceAngle[1];
    483     bullyDYaw = newBullyYaw - bully->oMoveAngleYaw;
    484 
    485     mario->faceAngle[1] = newMarioYaw;
    486     mario->forwardVel = sqrtf(marioData.velX * marioData.velX + marioData.velZ * marioData.velZ);
    487     mario->pos[0] = marioData.posX;
    488     mario->pos[2] = marioData.posZ;
    489 
    490     bully->oMoveAngleYaw = newBullyYaw;
    491     bully->oForwardVel = sqrtf(bullyData.velX * bullyData.velX + bullyData.velZ * bullyData.velZ);
    492     bully->oPosX = bullyData.posX;
    493     bully->oPosZ = bullyData.posZ;
    494 
    495     if (marioDYaw < -0x4000 || marioDYaw > 0x4000) {
    496         mario->faceAngle[1] += 0x8000;
    497         mario->forwardVel *= -1.0f;
    498 
    499         if (mario->action & ACT_FLAG_AIR) {
    500             bonkAction = ACT_BACKWARD_AIR_KB;
    501         } else {
    502             bonkAction = ACT_SOFT_BACKWARD_GROUND_KB;
    503         }
    504     } else {
    505         if (mario->action & ACT_FLAG_AIR) {
    506             bonkAction = ACT_FORWARD_AIR_KB;
    507         } else {
    508             bonkAction = ACT_SOFT_FORWARD_GROUND_KB;
    509         }
    510     }
    511 
    512     return bonkAction;
    513 }
    514 
    515 void bounce_off_object(struct MarioState *m, struct Object *o, f32 velY) {
    516     m->pos[1] = o->oPosY + o->hitboxHeight;
    517     m->vel[1] = velY;
    518 
    519     m->flags &= ~MARIO_UNKNOWN_08;
    520 
    521     play_sound(SOUND_ACTION_BOUNCE_OFF_OBJECT, m->marioObj->header.gfx.cameraToObject);
    522 }
    523 
    524 void hit_object_from_below(struct MarioState *m, UNUSED struct Object *o) {
    525     m->vel[1] = 0.0f;
    526     set_camera_shake_from_hit(SHAKE_HIT_FROM_BELOW);
    527 }
    528 
    529 UNUSED static u32 unused_determine_knockback_action(struct MarioState *m) {
    530     u32 bonkAction;
    531     s16 angleToObject = mario_obj_angle_to_object(m, m->interactObj);
    532     s16 facingDYaw = angleToObject - m->faceAngle[1];
    533 
    534     if (m->forwardVel < 16.0f) {
    535         m->forwardVel = 16.0f;
    536     }
    537 
    538     m->faceAngle[1] = angleToObject;
    539 
    540     if (facingDYaw >= -0x4000 && facingDYaw <= 0x4000) {
    541         m->forwardVel *= -1.0f;
    542         if (m->action & (ACT_FLAG_AIR | ACT_FLAG_ON_POLE | ACT_FLAG_HANGING)) {
    543             bonkAction = ACT_BACKWARD_AIR_KB;
    544         } else {
    545             bonkAction = ACT_SOFT_BACKWARD_GROUND_KB;
    546         }
    547     } else {
    548         m->faceAngle[1] += 0x8000;
    549         if (m->action & (ACT_FLAG_AIR | ACT_FLAG_ON_POLE | ACT_FLAG_HANGING)) {
    550             bonkAction = ACT_FORWARD_AIR_KB;
    551         } else {
    552             bonkAction = ACT_SOFT_FORWARD_GROUND_KB;
    553         }
    554     }
    555 
    556     return bonkAction;
    557 }
    558 
    559 u32 determine_knockback_action(struct MarioState *m, UNUSED s32 arg) {
    560     u32 bonkAction;
    561 
    562     s16 terrainIndex = 0; // 1 = air, 2 = water, 0 = default
    563     s16 strengthIndex = 0;
    564 
    565     s16 angleToObject = mario_obj_angle_to_object(m, m->interactObj);
    566     s16 facingDYaw = angleToObject - m->faceAngle[1];
    567     s16 remainingHealth = m->health - 0x40 * m->hurtCounter;
    568 
    569     if (m->action & (ACT_FLAG_SWIMMING | ACT_FLAG_METAL_WATER)) {
    570         terrainIndex = 2;
    571     } else if (m->action & (ACT_FLAG_AIR | ACT_FLAG_ON_POLE | ACT_FLAG_HANGING)) {
    572         terrainIndex = 1;
    573     }
    574 
    575     if (remainingHealth < 0x100) {
    576         strengthIndex = 2;
    577     } else if (m->interactObj->oDamageOrCoinValue >= 4) {
    578         strengthIndex = 2;
    579     } else if (m->interactObj->oDamageOrCoinValue >= 2) {
    580         strengthIndex = 1;
    581     }
    582 
    583     m->faceAngle[1] = angleToObject;
    584 
    585     if (terrainIndex == 2) {
    586         if (m->forwardVel < 28.0f) {
    587             mario_set_forward_vel(m, 28.0f);
    588         }
    589 
    590         if (m->pos[1] >= m->interactObj->oPosY) {
    591             if (m->vel[1] < 20.0f) {
    592                 m->vel[1] = 20.0f;
    593             }
    594         } else {
    595             if (m->vel[1] > 0.0f) {
    596                 m->vel[1] = 0.0f;
    597             }
    598         }
    599     } else {
    600         if (m->forwardVel < 16.0f) {
    601             mario_set_forward_vel(m, 16.0f);
    602         }
    603     }
    604 
    605     if (-0x4000 <= facingDYaw && facingDYaw <= 0x4000) {
    606         m->forwardVel *= -1.0f;
    607         bonkAction = sBackwardKnockbackActions[terrainIndex][strengthIndex];
    608     } else {
    609         m->faceAngle[1] += 0x8000;
    610         bonkAction = sForwardKnockbackActions[terrainIndex][strengthIndex];
    611     }
    612 
    613     return bonkAction;
    614 }
    615 
    616 void push_mario_out_of_object(struct MarioState *m, struct Object *o, f32 padding) {
    617     f32 minDistance = o->hitboxRadius + m->marioObj->hitboxRadius + padding;
    618 
    619     f32 offsetX = m->pos[0] - o->oPosX;
    620     f32 offsetZ = m->pos[2] - o->oPosZ;
    621     f32 distance = sqrtf(offsetX * offsetX + offsetZ * offsetZ);
    622 
    623     if (distance < minDistance) {
    624         struct Surface *floor;
    625         s16 pushAngle;
    626         f32 newMarioX;
    627         f32 newMarioZ;
    628 
    629         if (distance == 0.0f) {
    630             pushAngle = m->faceAngle[1];
    631         } else {
    632             pushAngle = atan2s(offsetZ, offsetX);
    633         }
    634 
    635         newMarioX = o->oPosX + minDistance * sins(pushAngle);
    636         newMarioZ = o->oPosZ + minDistance * coss(pushAngle);
    637 
    638         f32_find_wall_collision(&newMarioX, &m->pos[1], &newMarioZ, 60.0f, 50.0f);
    639 
    640         find_floor(newMarioX, m->pos[1], newMarioZ, &floor);
    641         if (floor != NULL) {
    642             //! Doesn't update Mario's referenced floor (allows oob death when
    643             // an object pushes you into a steep slope while in a ground action)
    644             m->pos[0] = newMarioX;
    645             m->pos[2] = newMarioZ;
    646         }
    647     }
    648 }
    649 
    650 void bounce_back_from_attack(struct MarioState *m, u32 interaction) {
    651     if (interaction & (INT_PUNCH | INT_KICK | INT_TRIP)) {
    652         if (m->action == ACT_PUNCHING) {
    653             m->action = ACT_MOVE_PUNCHING;
    654         }
    655 
    656         if (m->action & ACT_FLAG_AIR) {
    657             mario_set_forward_vel(m, -16.0f);
    658         } else {
    659             mario_set_forward_vel(m, -48.0f);
    660         }
    661 
    662         set_camera_shake_from_hit(SHAKE_ATTACK);
    663         m->particleFlags |= PARTICLE_TRIANGLE;
    664     }
    665 
    666     if (interaction & (INT_PUNCH | INT_KICK | INT_TRIP | INT_FAST_ATTACK_OR_SHELL)) {
    667         play_sound(SOUND_ACTION_HIT_2, m->marioObj->header.gfx.cameraToObject);
    668     }
    669 }
    670 
    671 u32 should_push_or_pull_door(struct MarioState *m, struct Object *o) {
    672     f32 dx = o->oPosX - m->pos[0];
    673     f32 dz = o->oPosZ - m->pos[2];
    674 
    675     s16 dYaw = o->oMoveAngleYaw - atan2s(dz, dx);
    676 
    677     return (dYaw >= -0x4000 && dYaw <= 0x4000) ? 0x00000001 : 0x00000002;
    678 }
    679 
    680 u32 take_damage_from_interact_object(struct MarioState *m) {
    681     s32 shake;
    682     s32 damage = m->interactObj->oDamageOrCoinValue;
    683 
    684     if (damage >= 4) {
    685         shake = SHAKE_LARGE_DAMAGE;
    686     } else if (damage >= 2) {
    687         shake = SHAKE_MED_DAMAGE;
    688     } else {
    689         shake = SHAKE_SMALL_DAMAGE;
    690     }
    691 
    692     if (!(m->flags & MARIO_CAP_ON_HEAD)) {
    693         damage += (damage + 1) / 2;
    694     }
    695 
    696     if (m->flags & MARIO_METAL_CAP) {
    697         damage = 0;
    698     }
    699 
    700     m->hurtCounter += 4 * damage;
    701 
    702 #if ENABLE_RUMBLE
    703     queue_rumble_data(5, 80);
    704 #endif
    705     set_camera_shake_from_hit(shake);
    706 
    707     return damage;
    708 }
    709 
    710 u32 take_damage_and_knock_back(struct MarioState *m, struct Object *o) {
    711     u32 damage;
    712 
    713     if (!sInvulnerable && !(m->flags & MARIO_VANISH_CAP)
    714         && !(o->oInteractionSubtype & INT_SUBTYPE_DELAY_INVINCIBILITY)) {
    715         o->oInteractStatus = INT_STATUS_INTERACTED | INT_STATUS_ATTACKED_MARIO;
    716         m->interactObj = o;
    717 
    718         damage = take_damage_from_interact_object(m);
    719 
    720         if (o->oInteractionSubtype & INT_SUBTYPE_BIG_KNOCKBACK) {
    721             m->forwardVel = 40.0f;
    722         }
    723 
    724         if (o->oDamageOrCoinValue > 0) {
    725             play_sound(SOUND_MARIO_ATTACKED, m->marioObj->header.gfx.cameraToObject);
    726         }
    727 
    728         update_mario_sound_and_camera(m);
    729         return drop_and_set_mario_action(m, determine_knockback_action(m, o->oDamageOrCoinValue),
    730                                          damage);
    731     }
    732 
    733     return FALSE;
    734 }
    735 
    736 void reset_mario_pitch(struct MarioState *m) {
    737     if (m->action == ACT_WATER_JUMP || m->action == ACT_SHOT_FROM_CANNON || m->action == ACT_FLYING) {
    738         set_camera_mode(m->area->camera, m->area->camera->defMode, 1);
    739         m->faceAngle[0] = 0;
    740     }
    741 }
    742 
    743 u32 interact_coin(struct MarioState *m, UNUSED u32 interactType, struct Object *o) {
    744     m->numCoins += o->oDamageOrCoinValue;
    745     m->healCounter += 4 * o->oDamageOrCoinValue;
    746 
    747     o->oInteractStatus = INT_STATUS_INTERACTED;
    748 
    749     if (COURSE_IS_MAIN_COURSE(gCurrCourseNum)
    750         && m->numCoins - o->oDamageOrCoinValue < 100 && m->numCoins >= 100) {
    751         bhv_spawn_star_no_level_exit(STAR_INDEX_100_COINS);
    752     }
    753 
    754 #if ENABLE_RUMBLE
    755     if (o->oDamageOrCoinValue >= 2) {
    756         queue_rumble_data(5, 80);
    757     }
    758 #endif
    759 
    760     return FALSE;
    761 }
    762 
    763 u32 interact_water_ring(struct MarioState *m, UNUSED u32 interactType, struct Object *o) {
    764     m->healCounter += 4 * o->oDamageOrCoinValue;
    765     o->oInteractStatus = INT_STATUS_INTERACTED;
    766     return FALSE;
    767 }
    768 
    769 u32 interact_star_or_key(struct MarioState *m, UNUSED u32 interactType, struct Object *o) {
    770     u32 starIndex;
    771     u32 starGrabAction = ACT_STAR_DANCE_EXIT;
    772     u32 noExit = (o->oInteractionSubtype & INT_SUBTYPE_NO_EXIT) != 0;
    773     u32 grandStar = (o->oInteractionSubtype & INT_SUBTYPE_GRAND_STAR) != 0;
    774 
    775     if (m->health >= 0x100) {
    776         mario_stop_riding_and_holding(m);
    777 #if ENABLE_RUMBLE
    778         queue_rumble_data(5, 80);
    779 #endif
    780 
    781         if (!noExit) {
    782             m->hurtCounter = 0;
    783             m->healCounter = 0;
    784             if (m->capTimer > 1) {
    785                 m->capTimer = 1;
    786             }
    787         }
    788 
    789         if (noExit) {
    790             starGrabAction = ACT_STAR_DANCE_NO_EXIT;
    791         }
    792 
    793         if (m->action & ACT_FLAG_SWIMMING) {
    794             starGrabAction = ACT_STAR_DANCE_WATER;
    795         }
    796 
    797         if (m->action & ACT_FLAG_METAL_WATER) {
    798             starGrabAction = ACT_STAR_DANCE_WATER;
    799         }
    800 
    801         if (m->action & ACT_FLAG_AIR) {
    802             starGrabAction = ACT_FALL_AFTER_STAR_GRAB;
    803         }
    804 
    805         spawn_object(o, MODEL_NONE, bhvStarKeyCollectionPuffSpawner);
    806 
    807         o->oInteractStatus = INT_STATUS_INTERACTED;
    808         m->interactObj = o;
    809         m->usedObj = o;
    810 
    811         starIndex = (o->oBhvParams >> 24) & 0x1F;
    812         save_file_collect_star_or_key(m->numCoins, starIndex);
    813 
    814         m->numStars =
    815             save_file_get_total_star_count(gCurrSaveFileNum - 1, COURSE_MIN - 1, COURSE_MAX - 1);
    816 
    817         if (!noExit) {
    818             drop_queued_background_music();
    819             fadeout_level_music(126);
    820         }
    821 
    822         play_sound(SOUND_MENU_STAR_SOUND, m->marioObj->header.gfx.cameraToObject);
    823 #ifndef VERSION_JP
    824         update_mario_sound_and_camera(m);
    825 #endif
    826 
    827         if (grandStar) {
    828             return set_mario_action(m, ACT_JUMBO_STAR_CUTSCENE, 0);
    829         }
    830 
    831         return set_mario_action(m, starGrabAction, noExit + 2 * grandStar);
    832     }
    833 
    834     return FALSE;
    835 }
    836 
    837 u32 interact_bbh_entrance(struct MarioState *m, UNUSED u32 interactType, struct Object *o) {
    838     if (m->action != ACT_BBH_ENTER_SPIN && m->action != ACT_BBH_ENTER_JUMP) {
    839         mario_stop_riding_and_holding(m);
    840 
    841         o->oInteractStatus = INT_STATUS_INTERACTED;
    842         m->interactObj = o;
    843         m->usedObj = o;
    844 
    845         if (m->action & ACT_FLAG_AIR) {
    846             return set_mario_action(m, ACT_BBH_ENTER_SPIN, 0);
    847         }
    848 
    849         return set_mario_action(m, ACT_BBH_ENTER_JUMP, 0);
    850     }
    851 
    852     return FALSE;
    853 }
    854 
    855 u32 interact_warp(struct MarioState *m, UNUSED u32 interactType, struct Object *o) {
    856     u32 action;
    857 
    858     if (o->oInteractionSubtype & INT_SUBTYPE_FADING_WARP) {
    859         action = m->action;
    860 
    861         if (action == ACT_TELEPORT_FADE_IN) {
    862             sJustTeleported = TRUE;
    863 
    864         } else if (!sJustTeleported) {
    865             if (action == ACT_IDLE || action == ACT_PANTING || action == ACT_STANDING_AGAINST_WALL
    866                 || action == ACT_CROUCHING) {
    867                 m->interactObj = o;
    868                 m->usedObj = o;
    869 
    870                 sJustTeleported = TRUE;
    871                 return set_mario_action(m, ACT_TELEPORT_FADE_OUT, 0);
    872             }
    873         }
    874     } else {
    875         if (m->action != ACT_EMERGE_FROM_PIPE) {
    876             o->oInteractStatus = INT_STATUS_INTERACTED;
    877             m->interactObj = o;
    878             m->usedObj = o;
    879 
    880 #if ENABLE_RUMBLE
    881             if (o->collisionData == segmented_to_virtual(warp_pipe_seg3_collision_03009AC8)) {
    882                 play_sound(SOUND_MENU_ENTER_PIPE, m->marioObj->header.gfx.cameraToObject);
    883                 queue_rumble_data(15, 80);
    884             } else {
    885                 play_sound(SOUND_MENU_ENTER_HOLE, m->marioObj->header.gfx.cameraToObject);
    886                 queue_rumble_data(12, 80);
    887             }
    888 #else
    889             play_sound(o->collisionData == segmented_to_virtual(warp_pipe_seg3_collision_03009AC8)
    890                            ? SOUND_MENU_ENTER_PIPE
    891                            : SOUND_MENU_ENTER_HOLE,
    892                        m->marioObj->header.gfx.cameraToObject);
    893 #endif
    894 
    895             mario_stop_riding_object(m);
    896             return set_mario_action(m, ACT_DISAPPEARED, (WARP_OP_WARP_OBJECT << 16) + 2);
    897         }
    898     }
    899 
    900     return FALSE;
    901 }
    902 
    903 u32 interact_warp_door(struct MarioState *m, UNUSED u32 interactType, struct Object *o) {
    904     u32 doorAction = 0;
    905     u32 saveFlags = save_file_get_flags();
    906     s16 warpDoorId = o->oBhvParams >> 24;
    907     u32 actionArg;
    908 
    909     if (m->action == ACT_WALKING || m->action == ACT_DECELERATING) {
    910         if (warpDoorId == 1 && !(saveFlags & SAVE_FLAG_UNLOCKED_UPSTAIRS_DOOR)) {
    911             if (!(saveFlags & SAVE_FLAG_HAVE_KEY_2)) {
    912                 if (!sDisplayingDoorText) {
    913                     set_mario_action(m, ACT_READING_AUTOMATIC_DIALOG,
    914                                      (saveFlags & SAVE_FLAG_HAVE_KEY_1) ? DIALOG_023 : DIALOG_022);
    915                 }
    916                 sDisplayingDoorText = TRUE;
    917 
    918                 return FALSE;
    919             }
    920 
    921             doorAction = ACT_UNLOCKING_KEY_DOOR;
    922         }
    923 
    924         if (warpDoorId == 2 && !(saveFlags & SAVE_FLAG_UNLOCKED_BASEMENT_DOOR)) {
    925             if (!(saveFlags & SAVE_FLAG_HAVE_KEY_1)) {
    926                 if (!sDisplayingDoorText) {
    927                     // Moat door skip was intended confirmed
    928                     set_mario_action(m, ACT_READING_AUTOMATIC_DIALOG,
    929                                      (saveFlags & SAVE_FLAG_HAVE_KEY_2) ? DIALOG_023 : DIALOG_022);
    930                 }
    931                 sDisplayingDoorText = TRUE;
    932 
    933                 return FALSE;
    934             }
    935 
    936             doorAction = ACT_UNLOCKING_KEY_DOOR;
    937         }
    938 
    939         if (m->action == ACT_WALKING || m->action == ACT_DECELERATING) {
    940             actionArg = should_push_or_pull_door(m, o) + 0x00000004;
    941 
    942             if (doorAction == 0) {
    943                 if (actionArg & 0x00000001) {
    944                     doorAction = ACT_PULLING_DOOR;
    945                 } else {
    946                     doorAction = ACT_PUSHING_DOOR;
    947                 }
    948             }
    949 
    950             m->interactObj = o;
    951             m->usedObj = o;
    952             return set_mario_action(m, doorAction, actionArg);
    953         }
    954     }
    955 
    956     return FALSE;
    957 }
    958 
    959 u32 get_door_save_file_flag(struct Object *door) {
    960     u32 saveFileFlag = 0;
    961     s16 requiredNumStars = door->oBhvParams >> 24;
    962 
    963     s16 isCCMDoor = door->oPosX < 0.0f;
    964     s16 isPSSDoor = door->oPosY > 500.0f;
    965 
    966     switch (requiredNumStars) {
    967         case 1:
    968             if (isPSSDoor) {
    969                 saveFileFlag = SAVE_FLAG_UNLOCKED_PSS_DOOR;
    970             } else {
    971                 saveFileFlag = SAVE_FLAG_UNLOCKED_WF_DOOR;
    972             }
    973             break;
    974 
    975         case 3:
    976             if (isCCMDoor) {
    977                 saveFileFlag = SAVE_FLAG_UNLOCKED_CCM_DOOR;
    978             } else {
    979                 saveFileFlag = SAVE_FLAG_UNLOCKED_JRB_DOOR;
    980             }
    981             break;
    982 
    983         case 8:
    984             saveFileFlag = SAVE_FLAG_UNLOCKED_BITDW_DOOR;
    985             break;
    986 
    987         case 30:
    988             saveFileFlag = SAVE_FLAG_UNLOCKED_BITFS_DOOR;
    989             break;
    990 
    991         case 50:
    992             saveFileFlag = SAVE_FLAG_UNLOCKED_50_STAR_DOOR;
    993             break;
    994     }
    995 
    996     return saveFileFlag;
    997 }
    998 
    999 u32 interact_door(struct MarioState *m, UNUSED u32 interactType, struct Object *o) {
   1000     s16 requiredNumStars = o->oBhvParams >> 24;
   1001     s16 numStars = save_file_get_total_star_count(gCurrSaveFileNum - 1, COURSE_MIN - 1, COURSE_MAX - 1);
   1002 
   1003     if (m->action == ACT_WALKING || m->action == ACT_DECELERATING) {
   1004         if (numStars >= requiredNumStars) {
   1005             u32 actionArg = should_push_or_pull_door(m, o);
   1006             u32 enterDoorAction;
   1007             u32 doorSaveFileFlag;
   1008 
   1009             if (actionArg & 0x00000001) {
   1010                 enterDoorAction = ACT_PULLING_DOOR;
   1011             } else {
   1012                 enterDoorAction = ACT_PUSHING_DOOR;
   1013             }
   1014 
   1015             doorSaveFileFlag = get_door_save_file_flag(o);
   1016             m->interactObj = o;
   1017             m->usedObj = o;
   1018 
   1019             if (o->oInteractionSubtype & INT_SUBTYPE_STAR_DOOR) {
   1020                 enterDoorAction = ACT_ENTERING_STAR_DOOR;
   1021             }
   1022 
   1023             if (doorSaveFileFlag != 0 && !(save_file_get_flags() & doorSaveFileFlag)) {
   1024                 enterDoorAction = ACT_UNLOCKING_STAR_DOOR;
   1025             }
   1026 
   1027             return set_mario_action(m, enterDoorAction, actionArg);
   1028         } else if (!sDisplayingDoorText) {
   1029             u32 text = DIALOG_022 << 16;
   1030 
   1031             switch (requiredNumStars) {
   1032                 case 1:
   1033                     text = DIALOG_024 << 16;
   1034                     break;
   1035                 case 3:
   1036                     text = DIALOG_025 << 16;
   1037                     break;
   1038                 case 8:
   1039                     text = DIALOG_026 << 16;
   1040                     break;
   1041                 case 30:
   1042                     text = DIALOG_027 << 16;
   1043                     break;
   1044                 case 50:
   1045                     text = DIALOG_028 << 16;
   1046                     break;
   1047                 case 70:
   1048                     text = DIALOG_029 << 16;
   1049                     break;
   1050             }
   1051 
   1052             text += requiredNumStars - numStars;
   1053 
   1054             sDisplayingDoorText = TRUE;
   1055             return set_mario_action(m, ACT_READING_AUTOMATIC_DIALOG, text);
   1056         }
   1057     } else if (m->action == ACT_IDLE && sDisplayingDoorText == TRUE && requiredNumStars == 70) {
   1058         m->interactObj = o;
   1059         m->usedObj = o;
   1060         return set_mario_action(m, ACT_ENTERING_STAR_DOOR, should_push_or_pull_door(m, o));
   1061     }
   1062 
   1063     return FALSE;
   1064 }
   1065 
   1066 u32 interact_cannon_base(struct MarioState *m, UNUSED u32 interactType, struct Object *o) {
   1067     if (m->action != ACT_IN_CANNON) {
   1068         mario_stop_riding_and_holding(m);
   1069         o->oInteractStatus = INT_STATUS_INTERACTED;
   1070         m->interactObj = o;
   1071         m->usedObj = o;
   1072         return set_mario_action(m, ACT_IN_CANNON, 0);
   1073     }
   1074 
   1075     return FALSE;
   1076 }
   1077 
   1078 u32 interact_igloo_barrier(struct MarioState *m, UNUSED u32 interactType, struct Object *o) {
   1079     //! Sets used object without changing action (LOTS of interesting glitches,
   1080     // but unfortunately the igloo barrier is the only object with this interaction
   1081     // type)
   1082     m->interactObj = o;
   1083     m->usedObj = o;
   1084     push_mario_out_of_object(m, o, 5.0f);
   1085     return FALSE;
   1086 }
   1087 
   1088 u32 interact_tornado(struct MarioState *m, UNUSED u32 interactType, struct Object *o) {
   1089     struct Object *marioObj = m->marioObj;
   1090 
   1091     if (m->action != ACT_TORNADO_TWIRLING && m->action != ACT_SQUISHED) {
   1092         mario_stop_riding_and_holding(m);
   1093         mario_set_forward_vel(m, 0.0f);
   1094         update_mario_sound_and_camera(m);
   1095 
   1096         o->oInteractStatus = INT_STATUS_INTERACTED;
   1097         m->interactObj = o;
   1098         m->usedObj = o;
   1099 
   1100         marioObj->oMarioTornadoYawVel = 0x400;
   1101         marioObj->oMarioTornadoPosY = m->pos[1] - o->oPosY;
   1102 
   1103         play_sound(SOUND_MARIO_WAAAOOOW, m->marioObj->header.gfx.cameraToObject);
   1104 #if ENABLE_RUMBLE
   1105         queue_rumble_data(30, 60);
   1106 #endif
   1107         return set_mario_action(m, ACT_TORNADO_TWIRLING, m->action == ACT_TWIRLING);
   1108     }
   1109 
   1110     return FALSE;
   1111 }
   1112 
   1113 u32 interact_whirlpool(struct MarioState *m, UNUSED u32 interactType, struct Object *o) {
   1114     struct Object *marioObj = m->marioObj;
   1115 
   1116     if (m->action != ACT_CAUGHT_IN_WHIRLPOOL) {
   1117         mario_stop_riding_and_holding(m);
   1118         o->oInteractStatus = INT_STATUS_INTERACTED;
   1119         m->interactObj = o;
   1120         m->usedObj = o;
   1121 
   1122         m->forwardVel = 0.0f;
   1123 
   1124         marioObj->oMarioWhirlpoolPosY = m->pos[1] - o->oPosY;
   1125 
   1126         play_sound(SOUND_MARIO_WAAAOOOW, m->marioObj->header.gfx.cameraToObject);
   1127 #if ENABLE_RUMBLE
   1128         queue_rumble_data(30, 60);
   1129 #endif
   1130         return set_mario_action(m, ACT_CAUGHT_IN_WHIRLPOOL, 0);
   1131     }
   1132 
   1133     return FALSE;
   1134 }
   1135 
   1136 u32 interact_strong_wind(struct MarioState *m, UNUSED u32 interactType, struct Object *o) {
   1137     UNUSED struct Object *marioObj = m->marioObj;
   1138 
   1139     if (m->action != ACT_GETTING_BLOWN) {
   1140         mario_stop_riding_and_holding(m);
   1141         o->oInteractStatus = INT_STATUS_INTERACTED;
   1142         m->interactObj = o;
   1143         m->usedObj = o;
   1144 
   1145         m->faceAngle[1] = o->oMoveAngleYaw + 0x8000;
   1146         m->gettingBlownGravity = 0.4f;
   1147         m->forwardVel = -24.0f;
   1148         m->vel[1] = 12.0f;
   1149 
   1150         play_sound(SOUND_MARIO_WAAAOOOW, m->marioObj->header.gfx.cameraToObject);
   1151         update_mario_sound_and_camera(m);
   1152         return set_mario_action(m, ACT_GETTING_BLOWN, 0);
   1153     }
   1154 
   1155     return FALSE;
   1156 }
   1157 
   1158 u32 interact_flame(struct MarioState *m, UNUSED u32 interactType, struct Object *o) {
   1159     u32 burningAction = ACT_BURNING_JUMP;
   1160 
   1161     if (!sInvulnerable && !(m->flags & MARIO_METAL_CAP) && !(m->flags & MARIO_VANISH_CAP)
   1162         && !(o->oInteractionSubtype & INT_SUBTYPE_DELAY_INVINCIBILITY)) {
   1163 #if ENABLE_RUMBLE
   1164         queue_rumble_data(5, 80);
   1165 #endif
   1166         o->oInteractStatus = INT_STATUS_INTERACTED;
   1167         m->interactObj = o;
   1168 
   1169         if ((m->action & (ACT_FLAG_SWIMMING | ACT_FLAG_METAL_WATER))
   1170             || m->waterLevel - m->pos[1] > 50.0f) {
   1171             play_sound(SOUND_GENERAL_FLAME_OUT, m->marioObj->header.gfx.cameraToObject);
   1172         } else {
   1173             m->marioObj->oMarioBurnTimer = 0;
   1174             update_mario_sound_and_camera(m);
   1175             play_sound(SOUND_MARIO_ON_FIRE, m->marioObj->header.gfx.cameraToObject);
   1176 
   1177             if ((m->action & ACT_FLAG_AIR) && m->vel[1] <= 0.0f) {
   1178                 burningAction = ACT_BURNING_FALL;
   1179             }
   1180 
   1181             return drop_and_set_mario_action(m, burningAction, 1);
   1182         }
   1183     }
   1184 
   1185     return FALSE;
   1186 }
   1187 
   1188 u32 interact_snufit_bullet(struct MarioState *m, UNUSED u32 interactType, struct Object *o) {
   1189     if (!sInvulnerable && !(m->flags & MARIO_VANISH_CAP)) {
   1190         if (m->flags & MARIO_METAL_CAP) {
   1191             o->oInteractStatus = INT_STATUS_INTERACTED | INT_STATUS_WAS_ATTACKED;
   1192             play_sound(SOUND_ACTION_UNKNOWN458, m->marioObj->header.gfx.cameraToObject);
   1193         } else {
   1194             o->oInteractStatus = INT_STATUS_INTERACTED | INT_STATUS_ATTACKED_MARIO;
   1195             m->interactObj = o;
   1196             take_damage_from_interact_object(m);
   1197 
   1198             play_sound(SOUND_MARIO_ATTACKED, m->marioObj->header.gfx.cameraToObject);
   1199             update_mario_sound_and_camera(m);
   1200 
   1201             return drop_and_set_mario_action(m, determine_knockback_action(m, o->oDamageOrCoinValue),
   1202                                              o->oDamageOrCoinValue);
   1203         }
   1204     }
   1205 
   1206     if (!(o->oInteractionSubtype & INT_SUBTYPE_DELAY_INVINCIBILITY)) {
   1207         sDelayInvincTimer = TRUE;
   1208     }
   1209 
   1210     return FALSE;
   1211 }
   1212 
   1213 u32 interact_clam_or_bubba(struct MarioState *m, UNUSED u32 interactType, struct Object *o) {
   1214     if (o->oInteractionSubtype & INT_SUBTYPE_EATS_MARIO) {
   1215         o->oInteractStatus = INT_STATUS_INTERACTED;
   1216         m->interactObj = o;
   1217         return set_mario_action(m, ACT_EATEN_BY_BUBBA, 0);
   1218     } else if (take_damage_and_knock_back(m, o)) {
   1219         return TRUE;
   1220     }
   1221 
   1222     if (!(o->oInteractionSubtype & INT_SUBTYPE_DELAY_INVINCIBILITY)) {
   1223         sDelayInvincTimer = TRUE;
   1224     }
   1225 
   1226     return TRUE;
   1227 }
   1228 
   1229 u32 interact_bully(struct MarioState *m, UNUSED u32 interactType, struct Object *o) {
   1230     UNUSED u8 filler[4];
   1231 
   1232     u32 interaction;
   1233     if (m->flags & MARIO_METAL_CAP) {
   1234         interaction = INT_FAST_ATTACK_OR_SHELL;
   1235     } else {
   1236         interaction = determine_interaction(m, o);
   1237     }
   1238 
   1239     m->interactObj = o;
   1240 
   1241     if (interaction & INT_ATTACK_NOT_FROM_BELOW) {
   1242 #if ENABLE_RUMBLE
   1243         queue_rumble_data(5, 80);
   1244 #endif
   1245         push_mario_out_of_object(m, o, 5.0f);
   1246 
   1247         m->forwardVel = -16.0f;
   1248         o->oMoveAngleYaw = m->faceAngle[1];
   1249         o->oForwardVel = 3392.0f / o->hitboxRadius;
   1250 
   1251         attack_object(o, interaction);
   1252         bounce_back_from_attack(m, interaction);
   1253         return TRUE;
   1254     }
   1255 
   1256     else if (!sInvulnerable && !(m->flags & MARIO_VANISH_CAP)
   1257              && !(o->oInteractionSubtype & INT_SUBTYPE_DELAY_INVINCIBILITY)) {
   1258         o->oInteractStatus = INT_STATUS_INTERACTED;
   1259         m->invincTimer = 2;
   1260 
   1261         update_mario_sound_and_camera(m);
   1262         play_sound(SOUND_MARIO_EEUH, m->marioObj->header.gfx.cameraToObject);
   1263         play_sound(SOUND_OBJ_BULLY_METAL, m->marioObj->header.gfx.cameraToObject);
   1264 
   1265         push_mario_out_of_object(m, o, 5.0f);
   1266         drop_and_set_mario_action(m, bully_knock_back_mario(m), 0);
   1267 #if ENABLE_RUMBLE
   1268         queue_rumble_data(5, 80);
   1269 #endif
   1270         return TRUE;
   1271     }
   1272 
   1273     return FALSE;
   1274 }
   1275 
   1276 u32 interact_shock(struct MarioState *m, UNUSED u32 interactType, struct Object *o) {
   1277     if (!sInvulnerable && !(m->flags & MARIO_VANISH_CAP)
   1278         && !(o->oInteractionSubtype & INT_SUBTYPE_DELAY_INVINCIBILITY)) {
   1279         u32 actionArg = (m->action & (ACT_FLAG_AIR | ACT_FLAG_ON_POLE | ACT_FLAG_HANGING)) == 0;
   1280 
   1281         o->oInteractStatus = INT_STATUS_INTERACTED | INT_STATUS_ATTACKED_MARIO;
   1282         m->interactObj = o;
   1283 
   1284         take_damage_from_interact_object(m);
   1285         play_sound(SOUND_MARIO_ATTACKED, m->marioObj->header.gfx.cameraToObject);
   1286 #if ENABLE_RUMBLE
   1287         queue_rumble_data(70, 60);
   1288 #endif
   1289 
   1290         if (m->action & (ACT_FLAG_SWIMMING | ACT_FLAG_METAL_WATER)) {
   1291             return drop_and_set_mario_action(m, ACT_WATER_SHOCKED, 0);
   1292         } else {
   1293             update_mario_sound_and_camera(m);
   1294             return drop_and_set_mario_action(m, ACT_SHOCKED, actionArg);
   1295         }
   1296     }
   1297 
   1298     if (!(o->oInteractionSubtype & INT_SUBTYPE_DELAY_INVINCIBILITY)) {
   1299         sDelayInvincTimer = TRUE;
   1300     }
   1301 
   1302     return FALSE;
   1303 }
   1304 
   1305 UNUSED static u32 interact_stub(UNUSED struct MarioState *m, UNUSED u32 interactType, struct Object *o) {
   1306     if (!(o->oInteractionSubtype & INT_SUBTYPE_DELAY_INVINCIBILITY)) {
   1307         sDelayInvincTimer = TRUE;
   1308     }
   1309     return FALSE;
   1310 }
   1311 
   1312 u32 interact_mr_blizzard(struct MarioState *m, UNUSED u32 interactType, struct Object *o) {
   1313     if (take_damage_and_knock_back(m, o)) {
   1314         return TRUE;
   1315     }
   1316 
   1317     if (!(o->oInteractionSubtype & INT_SUBTYPE_DELAY_INVINCIBILITY)) {
   1318         sDelayInvincTimer = TRUE;
   1319     }
   1320 
   1321     return FALSE;
   1322 }
   1323 
   1324 u32 interact_hit_from_below(struct MarioState *m, UNUSED u32 interactType, struct Object *o) {
   1325     UNUSED u8 filler[4];
   1326 
   1327     u32 interaction;
   1328     if (m->flags & MARIO_METAL_CAP) {
   1329         interaction = INT_FAST_ATTACK_OR_SHELL;
   1330     } else {
   1331         interaction = determine_interaction(m, o);
   1332     }
   1333 
   1334     if (interaction & INT_ANY_ATTACK) {
   1335 #if ENABLE_RUMBLE
   1336         queue_rumble_data(5, 80);
   1337 #endif
   1338         attack_object(o, interaction);
   1339         bounce_back_from_attack(m, interaction);
   1340 
   1341         if (interaction & INT_HIT_FROM_BELOW) {
   1342             hit_object_from_below(m, o);
   1343         }
   1344 
   1345         if (interaction & INT_HIT_FROM_ABOVE) {
   1346             if (o->oInteractionSubtype & INT_SUBTYPE_TWIRL_BOUNCE) {
   1347                 bounce_off_object(m, o, 80.0f);
   1348                 reset_mario_pitch(m);
   1349 #ifndef VERSION_JP
   1350                 play_sound(SOUND_MARIO_TWIRL_BOUNCE, m->marioObj->header.gfx.cameraToObject);
   1351 #endif
   1352                 return drop_and_set_mario_action(m, ACT_TWIRLING, 0);
   1353             } else {
   1354                 bounce_off_object(m, o, 30.0f);
   1355             }
   1356         }
   1357     } else if (take_damage_and_knock_back(m, o)) {
   1358         return TRUE;
   1359     }
   1360 
   1361     if (!(o->oInteractionSubtype & INT_SUBTYPE_DELAY_INVINCIBILITY)) {
   1362         sDelayInvincTimer = TRUE;
   1363     }
   1364 
   1365     return FALSE;
   1366 }
   1367 
   1368 u32 interact_bounce_top(struct MarioState *m, UNUSED u32 interactType, struct Object *o) {
   1369     u32 interaction;
   1370     if (m->flags & MARIO_METAL_CAP) {
   1371         interaction = INT_FAST_ATTACK_OR_SHELL;
   1372     } else {
   1373         interaction = determine_interaction(m, o);
   1374     }
   1375 
   1376     if (interaction & INT_ATTACK_NOT_FROM_BELOW) {
   1377 #if ENABLE_RUMBLE
   1378         queue_rumble_data(5, 80);
   1379 #endif
   1380         attack_object(o, interaction);
   1381         bounce_back_from_attack(m, interaction);
   1382 
   1383         if (interaction & INT_HIT_FROM_ABOVE) {
   1384             if (o->oInteractionSubtype & INT_SUBTYPE_TWIRL_BOUNCE) {
   1385                 bounce_off_object(m, o, 80.0f);
   1386                 reset_mario_pitch(m);
   1387 #ifndef VERSION_JP
   1388                 play_sound(SOUND_MARIO_TWIRL_BOUNCE, m->marioObj->header.gfx.cameraToObject);
   1389 #endif
   1390                 return drop_and_set_mario_action(m, ACT_TWIRLING, 0);
   1391             } else {
   1392                 bounce_off_object(m, o, 30.0f);
   1393             }
   1394         }
   1395     } else if (take_damage_and_knock_back(m, o)) {
   1396         return TRUE;
   1397     }
   1398 
   1399     if (!(o->oInteractionSubtype & INT_SUBTYPE_DELAY_INVINCIBILITY)) {
   1400         sDelayInvincTimer = TRUE;
   1401     }
   1402 
   1403     return FALSE;
   1404 }
   1405 
   1406 u32 interact_unknown_08(struct MarioState *m, UNUSED u32 interactType, struct Object *o) {
   1407     u32 interaction = determine_interaction(m, o);
   1408 
   1409     if (interaction & INT_PUNCH) {
   1410         o->oInteractStatus = INT_STATUS_INTERACTED | INT_STATUS_WAS_ATTACKED | ATTACK_PUNCH;
   1411         bounce_back_from_attack(m, interaction);
   1412     } else if (take_damage_and_knock_back(m, o)) {
   1413         return TRUE;
   1414     }
   1415 
   1416     if (!(o->oInteractionSubtype & INT_SUBTYPE_DELAY_INVINCIBILITY)) {
   1417         sDelayInvincTimer = TRUE;
   1418     }
   1419 
   1420     return FALSE;
   1421 }
   1422 
   1423 u32 interact_damage(struct MarioState *m, UNUSED u32 interactType, struct Object *o) {
   1424     if (take_damage_and_knock_back(m, o)) {
   1425         return TRUE;
   1426     }
   1427 
   1428     if (!(o->oInteractionSubtype & INT_SUBTYPE_DELAY_INVINCIBILITY)) {
   1429         sDelayInvincTimer = TRUE;
   1430     }
   1431 
   1432     return FALSE;
   1433 }
   1434 
   1435 u32 interact_breakable(struct MarioState *m, UNUSED u32 interactType, struct Object *o) {
   1436     u32 interaction = determine_interaction(m, o);
   1437 
   1438     if (interaction & INT_ATTACK_NOT_WEAK_FROM_ABOVE) {
   1439         attack_object(o, interaction);
   1440         bounce_back_from_attack(m, interaction);
   1441 
   1442         m->interactObj = o;
   1443 
   1444         switch (interaction) {
   1445             case INT_HIT_FROM_ABOVE:
   1446                 bounce_off_object(m, o, 30.0f); //! Not in the 0x8F mask
   1447                 break;
   1448 
   1449             case INT_HIT_FROM_BELOW:
   1450                 hit_object_from_below(m, o);
   1451                 break;
   1452         }
   1453 
   1454         return TRUE;
   1455     }
   1456 
   1457     return FALSE;
   1458 }
   1459 
   1460 u32 interact_koopa_shell(struct MarioState *m, UNUSED u32 interactType, struct Object *o) {
   1461     if (!(m->action & ACT_FLAG_RIDING_SHELL)) {
   1462         u32 interaction = determine_interaction(m, o);
   1463 
   1464         if (interaction == INT_HIT_FROM_ABOVE || m->action == ACT_WALKING
   1465             || m->action == ACT_HOLD_WALKING) {
   1466             m->interactObj = o;
   1467             m->usedObj = o;
   1468             m->riddenObj = o;
   1469 
   1470             attack_object(o, interaction);
   1471             update_mario_sound_and_camera(m);
   1472             play_shell_music();
   1473             mario_drop_held_object(m);
   1474 
   1475             //! Puts Mario in ground action even when in air, making it easy to
   1476             // escape air actions into crouch slide (shell cancel)
   1477             return set_mario_action(m, ACT_RIDING_SHELL_GROUND, 0);
   1478         }
   1479 
   1480         push_mario_out_of_object(m, o, 2.0f);
   1481     }
   1482 
   1483     return FALSE;
   1484 }
   1485 
   1486 u32 check_object_grab_mario(struct MarioState *m, UNUSED u32 interactType, struct Object *o) {
   1487     if ((!(m->action & (ACT_FLAG_AIR | ACT_FLAG_INVULNERABLE | ACT_FLAG_ATTACKING)) || !sInvulnerable)
   1488         && (o->oInteractionSubtype & INT_SUBTYPE_GRABS_MARIO)) {
   1489         if (object_facing_mario(m, o, 0x2AAA)) {
   1490             mario_stop_riding_and_holding(m);
   1491             o->oInteractStatus = INT_STATUS_INTERACTED | INT_STATUS_GRABBED_MARIO;
   1492 
   1493             m->faceAngle[1] = o->oMoveAngleYaw;
   1494             m->interactObj = o;
   1495             m->usedObj = o;
   1496 
   1497             update_mario_sound_and_camera(m);
   1498             play_sound(SOUND_MARIO_OOOF, m->marioObj->header.gfx.cameraToObject);
   1499 #if ENABLE_RUMBLE
   1500             queue_rumble_data(5, 80);
   1501 #endif
   1502             return set_mario_action(m, ACT_GRABBED, 0);
   1503         }
   1504     }
   1505 
   1506     push_mario_out_of_object(m, o, -5.0f);
   1507     return FALSE;
   1508 }
   1509 
   1510 u32 interact_pole(struct MarioState *m, UNUSED u32 interactType, struct Object *o) {
   1511     s32 actionId = m->action & ACT_ID_MASK;
   1512     if (actionId >= 0x080 && actionId < 0x0A0) {
   1513         if (!(m->prevAction & ACT_FLAG_ON_POLE) || m->usedObj != o) {
   1514 #if defined(VERSION_SH) || defined(VERSION_CN)
   1515             f32 velConv = m->forwardVel; // conserve the velocity.
   1516             struct Object *marioObj = m->marioObj;
   1517             u32 lowSpeed;
   1518 #else
   1519             u32 lowSpeed = (m->forwardVel <= 10.0f);
   1520             struct Object *marioObj = m->marioObj;
   1521 #endif
   1522 
   1523             mario_stop_riding_and_holding(m);
   1524 
   1525 #if defined(VERSION_SH) || defined(VERSION_CN)
   1526             lowSpeed = (velConv <= 10.0f);
   1527 #endif
   1528 
   1529             m->interactObj = o;
   1530             m->usedObj = o;
   1531             m->vel[1] = 0.0f;
   1532             m->forwardVel = 0.0f;
   1533 
   1534             marioObj->oMarioPoleUnk108 = 0;
   1535             marioObj->oMarioPoleYawVel = 0;
   1536             marioObj->oMarioPolePos = m->pos[1] - o->oPosY;
   1537 
   1538             if (lowSpeed) {
   1539                 return set_mario_action(m, ACT_GRAB_POLE_SLOW, 0);
   1540             }
   1541 
   1542             //! @bug Using m->forwardVel here is assumed to be 0.0f due to the set from earlier.
   1543             //       This is fixed in the Shindou version.
   1544 #if defined(VERSION_SH) || defined(VERSION_CN)
   1545             marioObj->oMarioPoleYawVel = (s32)(velConv * 0x100 + 0x1000);
   1546 #else
   1547             marioObj->oMarioPoleYawVel = (s32)(m->forwardVel * 0x100 + 0x1000);
   1548 #endif
   1549             reset_mario_pitch(m);
   1550 #if ENABLE_RUMBLE
   1551             queue_rumble_data(5, 80);
   1552 #endif
   1553             return set_mario_action(m, ACT_GRAB_POLE_FAST, 0);
   1554         }
   1555     }
   1556 
   1557     return FALSE;
   1558 }
   1559 
   1560 u32 interact_hoot(struct MarioState *m, UNUSED u32 interactType, struct Object *o) {
   1561     s32 actionId = m->action & ACT_ID_MASK;
   1562 
   1563     //! Can pause to advance the global timer without falling too far, allowing
   1564     // you to regrab after letting go.
   1565     if (actionId >= 0x080 && actionId < 0x098
   1566         && (gGlobalTimer - m->usedObj->oHootMarioReleaseTime > 30)) {
   1567         mario_stop_riding_and_holding(m);
   1568 
   1569         o->oInteractStatus = TRUE; //! Note: Not a flag, treated as a TRUE/FALSE statement
   1570         m->interactObj = o;
   1571         m->usedObj = o;
   1572 
   1573 #if ENABLE_RUMBLE
   1574         queue_rumble_data(5, 80);
   1575 #endif
   1576         update_mario_sound_and_camera(m);
   1577         return set_mario_action(m, ACT_RIDING_HOOT, 0);
   1578     }
   1579 
   1580     return FALSE;
   1581 }
   1582 
   1583 u32 interact_cap(struct MarioState *m, UNUSED u32 interactType, struct Object *o) {
   1584     u32 capFlag = get_mario_cap_flag(o);
   1585     u16 capMusic = 0;
   1586     u16 capTime = 0;
   1587 
   1588     if (m->action != ACT_GETTING_BLOWN && capFlag != 0) {
   1589         m->interactObj = o;
   1590         o->oInteractStatus = INT_STATUS_INTERACTED;
   1591 
   1592         m->flags &= ~MARIO_CAP_ON_HEAD & ~MARIO_CAP_IN_HAND;
   1593         m->flags |= capFlag;
   1594 
   1595         switch (capFlag) {
   1596             case MARIO_VANISH_CAP:
   1597                 capTime = 600;
   1598                 capMusic = SEQUENCE_ARGS(4, SEQ_EVENT_POWERUP);
   1599                 break;
   1600 
   1601             case MARIO_METAL_CAP:
   1602                 capTime = 600;
   1603                 capMusic = SEQUENCE_ARGS(4, SEQ_EVENT_METAL_CAP);
   1604                 break;
   1605 
   1606             case MARIO_WING_CAP:
   1607                 capTime = 1800;
   1608                 capMusic = SEQUENCE_ARGS(4, SEQ_EVENT_POWERUP);
   1609                 break;
   1610         }
   1611 
   1612         if (capTime > m->capTimer) {
   1613             m->capTimer = capTime;
   1614         }
   1615 
   1616         if ((m->action & ACT_FLAG_IDLE) || m->action == ACT_WALKING) {
   1617             m->flags |= MARIO_CAP_IN_HAND;
   1618             set_mario_action(m, ACT_PUTTING_ON_CAP, 0);
   1619         } else {
   1620             m->flags |= MARIO_CAP_ON_HEAD;
   1621         }
   1622 
   1623         play_sound(SOUND_MENU_STAR_SOUND, m->marioObj->header.gfx.cameraToObject);
   1624         play_sound(SOUND_MARIO_HERE_WE_GO, m->marioObj->header.gfx.cameraToObject);
   1625 
   1626         if (capMusic != 0) {
   1627             play_cap_music(capMusic);
   1628         }
   1629 
   1630         return TRUE;
   1631     }
   1632 
   1633     return FALSE;
   1634 }
   1635 
   1636 u32 interact_grabbable(struct MarioState *m, u32 interactType, struct Object *o) {
   1637     const BehaviorScript *script = virtual_to_segmented(0x13, o->behavior);
   1638 
   1639     if (o->oInteractionSubtype & INT_SUBTYPE_KICKABLE) {
   1640         u32 interaction = determine_interaction(m, o);
   1641         if (interaction & (INT_KICK | INT_TRIP)) {
   1642             attack_object(o, interaction);
   1643             bounce_back_from_attack(m, interaction);
   1644             return FALSE;
   1645         }
   1646     }
   1647 
   1648     if ((o->oInteractionSubtype & INT_SUBTYPE_GRABS_MARIO)) {
   1649         if (check_object_grab_mario(m, interactType, o)) {
   1650             return TRUE;
   1651         }
   1652     }
   1653 
   1654     if (able_to_grab_object(m, o)) {
   1655         if (!(o->oInteractionSubtype & INT_SUBTYPE_NOT_GRABBABLE)) {
   1656             m->interactObj = o;
   1657             m->input |= INPUT_INTERACT_OBJ_GRABBABLE;
   1658             return TRUE;
   1659         }
   1660     }
   1661 
   1662     if (script != bhvBowser) {
   1663         push_mario_out_of_object(m, o, -5.0f);
   1664     }
   1665 
   1666     return FALSE;
   1667 }
   1668 
   1669 u32 mario_can_talk(struct MarioState *m, u32 arg) {
   1670     s16 val6;
   1671 
   1672     if ((m->action & ACT_FLAG_IDLE) != 0x00000000) {
   1673         return TRUE;
   1674     }
   1675 
   1676     if (m->action == ACT_WALKING) {
   1677         if (arg) {
   1678             return TRUE;
   1679         }
   1680 
   1681         val6 = m->marioObj->header.gfx.animInfo.animID;
   1682 
   1683         if (val6 == 0x0080 || val6 == 0x007F || val6 == 0x006C) {
   1684             return TRUE;
   1685         }
   1686     }
   1687 
   1688     return FALSE;
   1689 }
   1690 
   1691 #ifdef VERSION_JP
   1692 #define READ_MASK (INPUT_B_PRESSED)
   1693 #else
   1694 #define READ_MASK (INPUT_B_PRESSED | INPUT_A_PRESSED)
   1695 #endif
   1696 
   1697 #ifdef VERSION_JP
   1698 #define SIGN_RANGE 0x38E3
   1699 #else
   1700 #define SIGN_RANGE 0x4000
   1701 #endif
   1702 
   1703 u32 check_read_sign(struct MarioState *m, struct Object *o) {
   1704     if ((m->input & READ_MASK) && mario_can_talk(m, 0) && object_facing_mario(m, o, SIGN_RANGE)) {
   1705         s16 facingDYaw = (s16)(o->oMoveAngleYaw + 0x8000) - m->faceAngle[1];
   1706         if (facingDYaw >= -SIGN_RANGE && facingDYaw <= SIGN_RANGE) {
   1707             f32 targetX = o->oPosX + 105.0f * sins(o->oMoveAngleYaw);
   1708             f32 targetZ = o->oPosZ + 105.0f * coss(o->oMoveAngleYaw);
   1709 
   1710             m->marioObj->oMarioReadingSignDYaw = facingDYaw;
   1711             m->marioObj->oMarioReadingSignDPosX = targetX - m->pos[0];
   1712             m->marioObj->oMarioReadingSignDPosZ = targetZ - m->pos[2];
   1713 
   1714             m->interactObj = o;
   1715             m->usedObj = o;
   1716             return set_mario_action(m, ACT_READING_SIGN, 0);
   1717         }
   1718     }
   1719 
   1720     return FALSE;
   1721 }
   1722 
   1723 u32 check_npc_talk(struct MarioState *m, struct Object *o) {
   1724     if ((m->input & READ_MASK) && mario_can_talk(m, 1)) {
   1725         s16 facingDYaw = mario_obj_angle_to_object(m, o) - m->faceAngle[1];
   1726         if (facingDYaw >= -0x4000 && facingDYaw <= 0x4000) {
   1727             o->oInteractStatus = INT_STATUS_INTERACTED;
   1728 
   1729             m->interactObj = o;
   1730             m->usedObj = o;
   1731 
   1732             push_mario_out_of_object(m, o, -10.0f);
   1733             return set_mario_action(m, ACT_WAITING_FOR_DIALOG, 0);
   1734         }
   1735     }
   1736 
   1737     push_mario_out_of_object(m, o, -10.0f);
   1738     return FALSE;
   1739 }
   1740 
   1741 u32 interact_text(struct MarioState *m, UNUSED u32 interactType, struct Object *o) {
   1742     u32 interact = FALSE;
   1743 
   1744     if (o->oInteractionSubtype & INT_SUBTYPE_SIGN) {
   1745         interact = check_read_sign(m, o);
   1746     } else if (o->oInteractionSubtype & INT_SUBTYPE_NPC) {
   1747         interact = check_npc_talk(m, o);
   1748     } else {
   1749         push_mario_out_of_object(m, o, 2.0f);
   1750     }
   1751 
   1752     return interact;
   1753 }
   1754 
   1755 void check_kick_or_punch_wall(struct MarioState *m) {
   1756     if (m->flags & (MARIO_PUNCHING | MARIO_KICKING | MARIO_TRIPPING)) {
   1757         Vec3f detector;
   1758         detector[0] = m->pos[0] + 50.0f * sins(m->faceAngle[1]);
   1759         detector[2] = m->pos[2] + 50.0f * coss(m->faceAngle[1]);
   1760         detector[1] = m->pos[1];
   1761 
   1762         if (resolve_and_return_wall_collisions(detector, 80.0f, 5.0f) != NULL) {
   1763             if (m->action != ACT_MOVE_PUNCHING || m->forwardVel >= 0.0f) {
   1764                 if (m->action == ACT_PUNCHING) {
   1765                     m->action = ACT_MOVE_PUNCHING;
   1766                 }
   1767 
   1768                 mario_set_forward_vel(m, -48.0f);
   1769                 play_sound(SOUND_ACTION_HIT_2, m->marioObj->header.gfx.cameraToObject);
   1770                 m->particleFlags |= PARTICLE_TRIANGLE;
   1771             } else if (m->action & ACT_FLAG_AIR) {
   1772                 mario_set_forward_vel(m, -16.0f);
   1773                 play_sound(SOUND_ACTION_HIT_2, m->marioObj->header.gfx.cameraToObject);
   1774                 m->particleFlags |= PARTICLE_TRIANGLE;
   1775             }
   1776         }
   1777     }
   1778 }
   1779 
   1780 void mario_process_interactions(struct MarioState *m) {
   1781     sDelayInvincTimer = FALSE;
   1782     sInvulnerable = (m->action & ACT_FLAG_INVULNERABLE) || m->invincTimer != 0;
   1783 
   1784     if (!(m->action & ACT_FLAG_INTANGIBLE) && m->collidedObjInteractTypes != 0) {
   1785         s32 i;
   1786         for (i = 0; i < ARRAY_COUNT(sInteractionHandlers); i++) {
   1787             u32 interactType = sInteractionHandlers[i].interactType;
   1788             if (m->collidedObjInteractTypes & interactType) {
   1789                 struct Object *object = mario_get_collided_object(m, interactType);
   1790 
   1791                 m->collidedObjInteractTypes &= ~interactType;
   1792 
   1793                 if (!(object->oInteractStatus & INT_STATUS_INTERACTED)) {
   1794                     if (sInteractionHandlers[i].handler(m, interactType, object)) {
   1795                         break;
   1796                     }
   1797                 }
   1798             }
   1799         }
   1800     }
   1801 
   1802     if (m->invincTimer > 0 && !sDelayInvincTimer) {
   1803         m->invincTimer--;
   1804     }
   1805 
   1806     //! If the kick/punch flags are set and an object collision changes Mario's
   1807     // action, he will get the kick/punch wall speed anyway.
   1808     check_kick_or_punch_wall(m);
   1809     m->flags &= ~MARIO_PUNCHING & ~MARIO_KICKING & ~MARIO_TRIPPING;
   1810 
   1811     if (!(m->marioObj->collidedObjInteractTypes & (INTERACT_WARP_DOOR | INTERACT_DOOR))) {
   1812         sDisplayingDoorText = FALSE;
   1813     }
   1814     if (!(m->marioObj->collidedObjInteractTypes & INTERACT_WARP)) {
   1815         sJustTeleported = FALSE;
   1816     }
   1817 }
   1818 
   1819 void check_death_barrier(struct MarioState *m) {
   1820     if (m->pos[1] < m->floorHeight + 2048.0f) {
   1821         if (level_trigger_warp(m, WARP_OP_WARP_FLOOR) == 20 && !(m->flags & MARIO_UNKNOWN_18)) {
   1822             play_sound(SOUND_MARIO_WAAAOOOW, m->marioObj->header.gfx.cameraToObject);
   1823         }
   1824     }
   1825 }
   1826 
   1827 void check_lava_boost(struct MarioState *m) {
   1828     if (!(m->action & ACT_FLAG_RIDING_SHELL) && m->pos[1] < m->floorHeight + 10.0f) {
   1829         if (!(m->flags & MARIO_METAL_CAP)) {
   1830             m->hurtCounter += (m->flags & MARIO_CAP_ON_HEAD) ? 12 : 18;
   1831         }
   1832 
   1833         update_mario_sound_and_camera(m);
   1834         drop_and_set_mario_action(m, ACT_LAVA_BOOST, 0);
   1835     }
   1836 }
   1837 
   1838 void pss_begin_slide(UNUSED struct MarioState *m) {
   1839     if (!(gHudDisplay.flags & HUD_DISPLAY_FLAG_TIMER)) {
   1840         level_control_timer(TIMER_CONTROL_SHOW);
   1841         level_control_timer(TIMER_CONTROL_START);
   1842         sPSSSlideStarted = TRUE;
   1843     }
   1844 }
   1845 
   1846 void pss_end_slide(struct MarioState *m) {
   1847     //! This flag isn't set on death or level entry, allowing double star spawn
   1848     if (sPSSSlideStarted) {
   1849         u16 slideTime = level_control_timer(TIMER_CONTROL_STOP);
   1850         if (slideTime < 630) {
   1851             m->marioObj->oBhvParams = (STAR_INDEX_ACT_2 << 24);
   1852             spawn_default_star(-6358.0f, -4300.0f, 4700.0f);
   1853         }
   1854         sPSSSlideStarted = FALSE;
   1855     }
   1856 }
   1857 
   1858 void mario_handle_special_floors(struct MarioState *m) {
   1859     if ((m->action & ACT_GROUP_MASK) == ACT_GROUP_CUTSCENE) {
   1860         return;
   1861     }
   1862 
   1863     if (m->floor != NULL) {
   1864         s32 floorType = m->floor->type;
   1865 
   1866         switch (floorType) {
   1867             case SURFACE_DEATH_PLANE:
   1868             case SURFACE_VERTICAL_WIND:
   1869                 check_death_barrier(m);
   1870                 break;
   1871 
   1872             case SURFACE_WARP:
   1873                 level_trigger_warp(m, WARP_OP_WARP_FLOOR);
   1874                 break;
   1875 
   1876             case SURFACE_TIMER_START:
   1877                 pss_begin_slide(m);
   1878                 break;
   1879 
   1880             case SURFACE_TIMER_END:
   1881                 pss_end_slide(m);
   1882                 break;
   1883         }
   1884 
   1885         if (!(m->action & ACT_FLAG_AIR) && !(m->action & ACT_FLAG_SWIMMING)) {
   1886             switch (floorType) {
   1887                 case SURFACE_BURNING:
   1888                     check_lava_boost(m);
   1889                     break;
   1890             }
   1891         }
   1892     }
   1893 }