sm64

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

mario_step.c (20919B)


      1 #include <ultra64.h>
      2 
      3 #include "sm64.h"
      4 #include "engine/math_util.h"
      5 #include "engine/surface_collision.h"
      6 #include "mario.h"
      7 #include "audio/external.h"
      8 #include "game_init.h"
      9 #include "interaction.h"
     10 #include "mario_step.h"
     11 
     12 static s16 sMovingSandSpeeds[] = { 12, 8, 4, 0 };
     13 
     14 struct Surface gWaterSurfacePseudoFloor = {
     15     SURFACE_VERY_SLIPPERY, 0,    0,    0, 0, 0, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 },
     16     { 0.0f, 1.0f, 0.0f },  0.0f, NULL,
     17 };
     18 
     19 /**
     20  * Always returns zero. This may have been intended
     21  * to be used for the beta trampoline. Its return value
     22  * is used by set_mario_y_vel_based_on_fspeed as a constant
     23  * addition to Mario's Y velocity. Given the closeness of
     24  * this function to stub_mario_step_2, it is probable that this
     25  * was intended to check whether a trampoline had made itself
     26  * known through stub_mario_step_2 and whether Mario was on it,
     27  * and if so return a higher value than 0.
     28  */
     29 f32 get_additive_y_vel_for_jumps(void) {
     30     return 0.0f;
     31 }
     32 
     33 /**
     34  * Does nothing, but takes in a MarioState. This is only ever
     35  * called by update_mario_inputs, which is called as part of Mario's
     36  * update routine. Due to its proximity to stub_mario_step_2, an
     37  * incomplete trampoline function, and get_additive_y_vel_for_jumps,
     38  * a potentially trampoline-related function, it is plausible that
     39  * this could be used for checking if Mario was on the trampoline.
     40  * It could, for example, make him bounce.
     41  */
     42 void stub_mario_step_1(UNUSED struct MarioState *x) {
     43 }
     44 
     45 /**
     46  * Does nothing. This is only called by the beta trampoline.
     47  * Due to its proximity to get_additive_y_vel_for_jumps, another
     48  * currently-pointless function, it is probable that this was used
     49  * by the trampoline to make itself known to get_additive_y_vel_for_jumps,
     50  * or to set a variable with its intended additive Y vel.
     51  */
     52 void stub_mario_step_2(void) {
     53 }
     54 
     55 void transfer_bully_speed(struct BullyCollisionData *obj1, struct BullyCollisionData *obj2) {
     56     f32 rx = obj2->posX - obj1->posX;
     57     f32 rz = obj2->posZ - obj1->posZ;
     58 
     59     //! Bully NaN crash
     60     f32 projectedV1 = (rx * obj1->velX + rz * obj1->velZ) / (rx * rx + rz * rz);
     61     f32 projectedV2 = (-rx * obj2->velX - rz * obj2->velZ) / (rx * rx + rz * rz);
     62 
     63     // Kill speed along r. Convert one object's speed along r and transfer it to
     64     // the other object.
     65     obj2->velX += obj2->conversionRatio * projectedV1 * rx - projectedV2 * -rx;
     66     obj2->velZ += obj2->conversionRatio * projectedV1 * rz - projectedV2 * -rz;
     67 
     68     obj1->velX += -projectedV1 * rx + obj1->conversionRatio * projectedV2 * -rx;
     69     obj1->velZ += -projectedV1 * rz + obj1->conversionRatio * projectedV2 * -rz;
     70 
     71     //! Bully battery
     72 }
     73 
     74 BAD_RETURN(s32) init_bully_collision_data(struct BullyCollisionData *data, f32 posX, f32 posZ,
     75                                f32 forwardVel, s16 yaw, f32 conversionRatio, f32 radius) {
     76     if (forwardVel < 0.0f) {
     77         forwardVel *= -1.0f;
     78         yaw += 0x8000;
     79     }
     80 
     81     data->radius = radius;
     82     data->conversionRatio = conversionRatio;
     83     data->posX = posX;
     84     data->posZ = posZ;
     85     data->velX = forwardVel * sins(yaw);
     86     data->velZ = forwardVel * coss(yaw);
     87 }
     88 
     89 void mario_bonk_reflection(struct MarioState *m, u32 negateSpeed) {
     90     if (m->wall != NULL) {
     91         s16 wallAngle = atan2s(m->wall->normal.z, m->wall->normal.x);
     92         m->faceAngle[1] = wallAngle - (s16)(m->faceAngle[1] - wallAngle);
     93 
     94         play_sound((m->flags & MARIO_METAL_CAP) ? SOUND_ACTION_METAL_BONK : SOUND_ACTION_BONK,
     95                    m->marioObj->header.gfx.cameraToObject);
     96     } else {
     97         play_sound(SOUND_ACTION_HIT, m->marioObj->header.gfx.cameraToObject);
     98     }
     99 
    100     if (negateSpeed) {
    101         mario_set_forward_vel(m, -m->forwardVel);
    102     } else {
    103         m->faceAngle[1] += 0x8000;
    104     }
    105 }
    106 
    107 u32 mario_update_quicksand(struct MarioState *m, f32 sinkingSpeed) {
    108     if (m->action & ACT_FLAG_RIDING_SHELL) {
    109         m->quicksandDepth = 0.0f;
    110     } else {
    111         if (m->quicksandDepth < 1.1f) {
    112             m->quicksandDepth = 1.1f;
    113         }
    114 
    115         switch (m->floor->type) {
    116             case SURFACE_SHALLOW_QUICKSAND:
    117                 if ((m->quicksandDepth += sinkingSpeed) >= 10.0f) {
    118                     m->quicksandDepth = 10.0f;
    119                 }
    120                 break;
    121 
    122             case SURFACE_SHALLOW_MOVING_QUICKSAND:
    123                 if ((m->quicksandDepth += sinkingSpeed) >= 25.0f) {
    124                     m->quicksandDepth = 25.0f;
    125                 }
    126                 break;
    127 
    128             case SURFACE_QUICKSAND:
    129             case SURFACE_MOVING_QUICKSAND:
    130                 if ((m->quicksandDepth += sinkingSpeed) >= 60.0f) {
    131                     m->quicksandDepth = 60.0f;
    132                 }
    133                 break;
    134 
    135             case SURFACE_DEEP_QUICKSAND:
    136             case SURFACE_DEEP_MOVING_QUICKSAND:
    137                 if ((m->quicksandDepth += sinkingSpeed) >= 160.0f) {
    138                     update_mario_sound_and_camera(m);
    139                     return drop_and_set_mario_action(m, ACT_QUICKSAND_DEATH, 0);
    140                 }
    141                 break;
    142 
    143             case SURFACE_INSTANT_QUICKSAND:
    144             case SURFACE_INSTANT_MOVING_QUICKSAND:
    145                 update_mario_sound_and_camera(m);
    146                 return drop_and_set_mario_action(m, ACT_QUICKSAND_DEATH, 0);
    147                 break;
    148 
    149             default:
    150                 m->quicksandDepth = 0.0f;
    151                 break;
    152         }
    153     }
    154 
    155     return FALSE;
    156 }
    157 
    158 u32 mario_push_off_steep_floor(struct MarioState *m, u32 action, u32 actionArg) {
    159     s16 floorDYaw = m->floorAngle - m->faceAngle[1];
    160 
    161     if (floorDYaw > -0x4000 && floorDYaw < 0x4000) {
    162         m->forwardVel = 16.0f;
    163         m->faceAngle[1] = m->floorAngle;
    164     } else {
    165         m->forwardVel = -16.0f;
    166         m->faceAngle[1] = m->floorAngle + 0x8000;
    167     }
    168 
    169     return set_mario_action(m, action, actionArg);
    170 }
    171 
    172 u32 mario_update_moving_sand(struct MarioState *m) {
    173     struct Surface *floor = m->floor;
    174     s32 floorType = floor->type;
    175 
    176     if (floorType == SURFACE_DEEP_MOVING_QUICKSAND || floorType == SURFACE_SHALLOW_MOVING_QUICKSAND
    177         || floorType == SURFACE_MOVING_QUICKSAND || floorType == SURFACE_INSTANT_MOVING_QUICKSAND) {
    178         s16 pushAngle = floor->force << 8;
    179         f32 pushSpeed = sMovingSandSpeeds[floor->force >> 8];
    180 
    181         m->vel[0] += pushSpeed * sins(pushAngle);
    182         m->vel[2] += pushSpeed * coss(pushAngle);
    183 
    184         return TRUE;
    185     }
    186 
    187     return FALSE;
    188 }
    189 
    190 u32 mario_update_windy_ground(struct MarioState *m) {
    191     struct Surface *floor = m->floor;
    192 
    193     if (floor->type == SURFACE_HORIZONTAL_WIND) {
    194         f32 pushSpeed;
    195         s16 pushAngle = floor->force << 8;
    196 
    197         if (m->action & ACT_FLAG_MOVING) {
    198             s16 pushDYaw = m->faceAngle[1] - pushAngle;
    199 
    200             pushSpeed = m->forwardVel > 0.0f ? -m->forwardVel * 0.5f : -8.0f;
    201 
    202             if (pushDYaw > -0x4000 && pushDYaw < 0x4000) {
    203                 pushSpeed *= -1.0f;
    204             }
    205 
    206             pushSpeed *= coss(pushDYaw);
    207         } else {
    208             pushSpeed = 3.2f + (gGlobalTimer % 4);
    209         }
    210 
    211         m->vel[0] += pushSpeed * sins(pushAngle);
    212         m->vel[2] += pushSpeed * coss(pushAngle);
    213 
    214 #ifdef VERSION_JP
    215         play_sound(SOUND_ENV_WIND2, m->marioObj->header.gfx.cameraToObject);
    216 #endif
    217         return TRUE;
    218     }
    219 
    220     return FALSE;
    221 }
    222 
    223 void stop_and_set_height_to_floor(struct MarioState *m) {
    224     struct Object *marioObj = m->marioObj;
    225 
    226     mario_set_forward_vel(m, 0.0f);
    227     m->vel[1] = 0.0f;
    228 
    229     //! This is responsible for some downwarps.
    230     m->pos[1] = m->floorHeight;
    231 
    232     vec3f_copy(marioObj->header.gfx.pos, m->pos);
    233     vec3s_set(marioObj->header.gfx.angle, 0, m->faceAngle[1], 0);
    234 }
    235 
    236 s32 stationary_ground_step(struct MarioState *m) {
    237     u32 takeStep;
    238     struct Object *marioObj = m->marioObj;
    239     u32 stepResult = GROUND_STEP_NONE;
    240 
    241     mario_set_forward_vel(m, 0.0f);
    242 
    243     takeStep = mario_update_moving_sand(m);
    244     takeStep |= mario_update_windy_ground(m);
    245     if (takeStep) {
    246         stepResult = perform_ground_step(m);
    247     } else {
    248         //! This is responsible for several stationary downwarps.
    249         m->pos[1] = m->floorHeight;
    250 
    251         vec3f_copy(marioObj->header.gfx.pos, m->pos);
    252         vec3s_set(marioObj->header.gfx.angle, 0, m->faceAngle[1], 0);
    253     }
    254 
    255     return stepResult;
    256 }
    257 
    258 static s32 perform_ground_quarter_step(struct MarioState *m, Vec3f nextPos) {
    259     UNUSED struct Surface *lowerWall;
    260     struct Surface *upperWall;
    261     struct Surface *ceil;
    262     struct Surface *floor;
    263     f32 ceilHeight;
    264     f32 floorHeight;
    265     f32 waterLevel;
    266 
    267     lowerWall = resolve_and_return_wall_collisions(nextPos, 30.0f, 24.0f);
    268     upperWall = resolve_and_return_wall_collisions(nextPos, 60.0f, 50.0f);
    269 
    270     floorHeight = find_floor(nextPos[0], nextPos[1], nextPos[2], &floor);
    271     ceilHeight = vec3f_find_ceil(nextPos, floorHeight, &ceil);
    272 
    273     waterLevel = find_water_level(nextPos[0], nextPos[2]);
    274 
    275     m->wall = upperWall;
    276 
    277     if (floor == NULL) {
    278         return GROUND_STEP_HIT_WALL_STOP_QSTEPS;
    279     }
    280 
    281     if ((m->action & ACT_FLAG_RIDING_SHELL) && floorHeight < waterLevel) {
    282         floorHeight = waterLevel;
    283         floor = &gWaterSurfacePseudoFloor;
    284         floor->originOffset = floorHeight; //! Wrong origin offset (no effect)
    285     }
    286 
    287     if (nextPos[1] > floorHeight + 100.0f) {
    288         if (nextPos[1] + 160.0f >= ceilHeight) {
    289             return GROUND_STEP_HIT_WALL_STOP_QSTEPS;
    290         }
    291 
    292         vec3f_copy(m->pos, nextPos);
    293         m->floor = floor;
    294         m->floorHeight = floorHeight;
    295         return GROUND_STEP_LEFT_GROUND;
    296     }
    297 
    298     if (floorHeight + 160.0f >= ceilHeight) {
    299         return GROUND_STEP_HIT_WALL_STOP_QSTEPS;
    300     }
    301 
    302     vec3f_set(m->pos, nextPos[0], floorHeight, nextPos[2]);
    303     m->floor = floor;
    304     m->floorHeight = floorHeight;
    305 
    306     if (upperWall != NULL) {
    307         s16 wallDYaw = atan2s(upperWall->normal.z, upperWall->normal.x) - m->faceAngle[1];
    308 
    309         if (wallDYaw >= 0x2AAA && wallDYaw <= 0x5555) {
    310             return GROUND_STEP_NONE;
    311         }
    312         if (wallDYaw <= -0x2AAA && wallDYaw >= -0x5555) {
    313             return GROUND_STEP_NONE;
    314         }
    315 
    316         return GROUND_STEP_HIT_WALL_CONTINUE_QSTEPS;
    317     }
    318 
    319     return GROUND_STEP_NONE;
    320 }
    321 
    322 s32 perform_ground_step(struct MarioState *m) {
    323     s32 i;
    324     u32 stepResult;
    325     Vec3f intendedPos;
    326 
    327     for (i = 0; i < 4; i++) {
    328         intendedPos[0] = m->pos[0] + m->floor->normal.y * (m->vel[0] / 4.0f);
    329         intendedPos[2] = m->pos[2] + m->floor->normal.y * (m->vel[2] / 4.0f);
    330         intendedPos[1] = m->pos[1];
    331 
    332         stepResult = perform_ground_quarter_step(m, intendedPos);
    333         if (stepResult == GROUND_STEP_LEFT_GROUND || stepResult == GROUND_STEP_HIT_WALL_STOP_QSTEPS) {
    334             break;
    335         }
    336     }
    337 
    338     m->terrainSoundAddend = mario_get_terrain_sound_addend(m);
    339     vec3f_copy(m->marioObj->header.gfx.pos, m->pos);
    340     vec3s_set(m->marioObj->header.gfx.angle, 0, m->faceAngle[1], 0);
    341 
    342     if (stepResult == GROUND_STEP_HIT_WALL_CONTINUE_QSTEPS) {
    343         stepResult = GROUND_STEP_HIT_WALL;
    344     }
    345     return stepResult;
    346 }
    347 
    348 u32 check_ledge_grab(struct MarioState *m, struct Surface *wall, Vec3f intendedPos, Vec3f nextPos) {
    349     struct Surface *ledgeFloor;
    350     Vec3f ledgePos;
    351     f32 displacementX;
    352     f32 displacementZ;
    353 
    354     if (m->vel[1] > 0) {
    355         return FALSE;
    356     }
    357 
    358     displacementX = nextPos[0] - intendedPos[0];
    359     displacementZ = nextPos[2] - intendedPos[2];
    360 
    361     // Only ledge grab if the wall displaced Mario in the opposite direction of
    362     // his velocity.
    363     if (displacementX * m->vel[0] + displacementZ * m->vel[2] > 0.0f) {
    364         return FALSE;
    365     }
    366 
    367     //! Since the search for floors starts at y + 160, we will sometimes grab
    368     // a higher ledge than expected (glitchy ledge grab)
    369     ledgePos[0] = nextPos[0] - wall->normal.x * 60.0f;
    370     ledgePos[2] = nextPos[2] - wall->normal.z * 60.0f;
    371     ledgePos[1] = find_floor(ledgePos[0], nextPos[1] + 160.0f, ledgePos[2], &ledgeFloor);
    372 
    373     if (ledgePos[1] - nextPos[1] <= 100.0f) {
    374         return FALSE;
    375     }
    376 
    377     vec3f_copy(m->pos, ledgePos);
    378     m->floor = ledgeFloor;
    379     m->floorHeight = ledgePos[1];
    380 
    381     m->floorAngle = atan2s(ledgeFloor->normal.z, ledgeFloor->normal.x);
    382 
    383     m->faceAngle[0] = 0;
    384     m->faceAngle[1] = atan2s(wall->normal.z, wall->normal.x) + 0x8000;
    385     return TRUE;
    386 }
    387 
    388 s32 perform_air_quarter_step(struct MarioState *m, Vec3f intendedPos, u32 stepArg) {
    389     s16 wallDYaw;
    390     Vec3f nextPos;
    391     struct Surface *upperWall;
    392     struct Surface *lowerWall;
    393     struct Surface *ceil;
    394     struct Surface *floor;
    395     f32 ceilHeight;
    396     f32 floorHeight;
    397     f32 waterLevel;
    398 
    399     vec3f_copy(nextPos, intendedPos);
    400 
    401     upperWall = resolve_and_return_wall_collisions(nextPos, 150.0f, 50.0f);
    402     lowerWall = resolve_and_return_wall_collisions(nextPos, 30.0f, 50.0f);
    403 
    404     floorHeight = find_floor(nextPos[0], nextPos[1], nextPos[2], &floor);
    405     ceilHeight = vec3f_find_ceil(nextPos, floorHeight, &ceil);
    406 
    407     waterLevel = find_water_level(nextPos[0], nextPos[2]);
    408 
    409     m->wall = NULL;
    410 
    411     //! The water pseudo floor is not referenced when your intended qstep is
    412     // out of bounds, so it won't detect you as landing.
    413 
    414     if (floor == NULL) {
    415         if (nextPos[1] <= m->floorHeight) {
    416             m->pos[1] = m->floorHeight;
    417             return AIR_STEP_LANDED;
    418         }
    419 
    420         m->pos[1] = nextPos[1];
    421         return AIR_STEP_HIT_WALL;
    422     }
    423 
    424     if ((m->action & ACT_FLAG_RIDING_SHELL) && floorHeight < waterLevel) {
    425         floorHeight = waterLevel;
    426         floor = &gWaterSurfacePseudoFloor;
    427         floor->originOffset = floorHeight; //! Incorrect origin offset (no effect)
    428     }
    429 
    430     //! This check uses f32, but findFloor uses short (overflow jumps)
    431     if (nextPos[1] <= floorHeight) {
    432         if (ceilHeight - floorHeight > 160.0f) {
    433             m->pos[0] = nextPos[0];
    434             m->pos[2] = nextPos[2];
    435             m->floor = floor;
    436             m->floorHeight = floorHeight;
    437         }
    438 
    439         //! When ceilHeight - floorHeight <= 160, the step result says that
    440         // Mario landed, but his movement is cancelled and his referenced floor
    441         // isn't updated (pedro spots)
    442         m->pos[1] = floorHeight;
    443         return AIR_STEP_LANDED;
    444     }
    445 
    446     if (nextPos[1] + 160.0f > ceilHeight) {
    447         if (m->vel[1] >= 0.0f) {
    448             m->vel[1] = 0.0f;
    449 
    450             //! Uses referenced ceiling instead of ceil (ceiling hang upwarp)
    451             if ((stepArg & AIR_STEP_CHECK_HANG) && m->ceil != NULL
    452                 && m->ceil->type == SURFACE_HANGABLE) {
    453                 return AIR_STEP_GRABBED_CEILING;
    454             }
    455 
    456             return AIR_STEP_NONE;
    457         }
    458 
    459         //! Potential subframe downwarp->upwarp?
    460         if (nextPos[1] <= m->floorHeight) {
    461             m->pos[1] = m->floorHeight;
    462             return AIR_STEP_LANDED;
    463         }
    464 
    465         m->pos[1] = nextPos[1];
    466         return AIR_STEP_HIT_WALL;
    467     }
    468 
    469     //! When the wall is not completely vertical or there is a slight wall
    470     // misalignment, you can activate these conditions in unexpected situations
    471     if ((stepArg & AIR_STEP_CHECK_LEDGE_GRAB) && upperWall == NULL && lowerWall != NULL) {
    472         if (check_ledge_grab(m, lowerWall, intendedPos, nextPos)) {
    473             return AIR_STEP_GRABBED_LEDGE;
    474         }
    475 
    476         vec3f_copy(m->pos, nextPos);
    477         m->floor = floor;
    478         m->floorHeight = floorHeight;
    479         return AIR_STEP_NONE;
    480     }
    481 
    482     vec3f_copy(m->pos, nextPos);
    483     m->floor = floor;
    484     m->floorHeight = floorHeight;
    485 
    486     if (upperWall != NULL || lowerWall != NULL) {
    487         m->wall = upperWall != NULL ? upperWall : lowerWall;
    488         wallDYaw = atan2s(m->wall->normal.z, m->wall->normal.x) - m->faceAngle[1];
    489 
    490         if (m->wall->type == SURFACE_BURNING) {
    491             return AIR_STEP_HIT_LAVA_WALL;
    492         }
    493 
    494         if (wallDYaw < -0x6000 || wallDYaw > 0x6000) {
    495             m->flags |= MARIO_UNKNOWN_30;
    496             return AIR_STEP_HIT_WALL;
    497         }
    498     }
    499 
    500     return AIR_STEP_NONE;
    501 }
    502 
    503 void apply_twirl_gravity(struct MarioState *m) {
    504     f32 terminalVelocity;
    505     f32 heaviness = 1.0f;
    506 
    507     if (m->angleVel[1] > 1024) {
    508         heaviness = 1024.0f / m->angleVel[1];
    509     }
    510 
    511     terminalVelocity = -75.0f * heaviness;
    512 
    513     m->vel[1] -= 4.0f * heaviness;
    514     if (m->vel[1] < terminalVelocity) {
    515         m->vel[1] = terminalVelocity;
    516     }
    517 }
    518 
    519 u32 should_strengthen_gravity_for_jump_ascent(struct MarioState *m) {
    520     if (!(m->flags & MARIO_UNKNOWN_08)) {
    521         return FALSE;
    522     }
    523 
    524     if (m->action & (ACT_FLAG_INTANGIBLE | ACT_FLAG_INVULNERABLE)) {
    525         return FALSE;
    526     }
    527 
    528     if (!(m->input & INPUT_A_DOWN) && m->vel[1] > 20.0f) {
    529         return (m->action & ACT_FLAG_CONTROL_JUMP_HEIGHT) != 0;
    530     }
    531 
    532     return FALSE;
    533 }
    534 
    535 void apply_gravity(struct MarioState *m) {
    536     if (m->action == ACT_TWIRLING && m->vel[1] < 0.0f) {
    537         apply_twirl_gravity(m);
    538     } else if (m->action == ACT_SHOT_FROM_CANNON) {
    539         m->vel[1] -= 1.0f;
    540         if (m->vel[1] < -75.0f) {
    541             m->vel[1] = -75.0f;
    542         }
    543     } else if (m->action == ACT_LONG_JUMP || m->action == ACT_SLIDE_KICK
    544                || m->action == ACT_BBH_ENTER_SPIN) {
    545         m->vel[1] -= 2.0f;
    546         if (m->vel[1] < -75.0f) {
    547             m->vel[1] = -75.0f;
    548         }
    549     } else if (m->action == ACT_LAVA_BOOST || m->action == ACT_FALL_AFTER_STAR_GRAB) {
    550         m->vel[1] -= 3.2f;
    551         if (m->vel[1] < -65.0f) {
    552             m->vel[1] = -65.0f;
    553         }
    554     } else if (m->action == ACT_GETTING_BLOWN) {
    555         m->vel[1] -= m->gettingBlownGravity;
    556         if (m->vel[1] < -75.0f) {
    557             m->vel[1] = -75.0f;
    558         }
    559     } else if (should_strengthen_gravity_for_jump_ascent(m)) {
    560         m->vel[1] /= 4.0f;
    561     } else if (m->action & ACT_FLAG_METAL_WATER) {
    562         m->vel[1] -= 1.6f;
    563         if (m->vel[1] < -16.0f) {
    564             m->vel[1] = -16.0f;
    565         }
    566     } else if ((m->flags & MARIO_WING_CAP) && m->vel[1] < 0.0f && (m->input & INPUT_A_DOWN)) {
    567         m->marioBodyState->wingFlutter = TRUE;
    568 
    569         m->vel[1] -= 2.0f;
    570         if (m->vel[1] < -37.5f) {
    571             if ((m->vel[1] += 4.0f) > -37.5f) {
    572                 m->vel[1] = -37.5f;
    573             }
    574         }
    575     } else {
    576         m->vel[1] -= 4.0f;
    577         if (m->vel[1] < -75.0f) {
    578             m->vel[1] = -75.0f;
    579         }
    580     }
    581 }
    582 
    583 void apply_vertical_wind(struct MarioState *m) {
    584     f32 maxVelY;
    585     f32 offsetY;
    586 
    587     if (m->action != ACT_GROUND_POUND) {
    588         offsetY = m->pos[1] - -1500.0f;
    589 
    590         if (m->floor->type == SURFACE_VERTICAL_WIND && -3000.0f < offsetY && offsetY < 2000.0f) {
    591             if (offsetY >= 0.0f) {
    592                 maxVelY = 10000.0f / (offsetY + 200.0f);
    593             } else {
    594                 maxVelY = 50.0f;
    595             }
    596 
    597             if (m->vel[1] < maxVelY) {
    598                 if ((m->vel[1] += maxVelY / 8.0f) > maxVelY) {
    599                     m->vel[1] = maxVelY;
    600                 }
    601             }
    602 
    603 #ifdef VERSION_JP
    604             play_sound(SOUND_ENV_WIND2, m->marioObj->header.gfx.cameraToObject);
    605 #endif
    606         }
    607     }
    608 }
    609 
    610 s32 perform_air_step(struct MarioState *m, u32 stepArg) {
    611     Vec3f intendedPos;
    612     s32 i;
    613     s32 quarterStepResult;
    614     s32 stepResult = AIR_STEP_NONE;
    615 
    616     m->wall = NULL;
    617 
    618     for (i = 0; i < 4; i++) {
    619         intendedPos[0] = m->pos[0] + m->vel[0] / 4.0f;
    620         intendedPos[1] = m->pos[1] + m->vel[1] / 4.0f;
    621         intendedPos[2] = m->pos[2] + m->vel[2] / 4.0f;
    622 
    623         quarterStepResult = perform_air_quarter_step(m, intendedPos, stepArg);
    624 
    625         //! On one qf, hit OOB/ceil/wall to store the 2 return value, and continue
    626         // getting 0s until your last qf. Graze a wall on your last qf, and it will
    627         // return the stored 2 with a sharply angled reference wall. (some gwks)
    628 
    629         if (quarterStepResult != AIR_STEP_NONE) {
    630             stepResult = quarterStepResult;
    631         }
    632 
    633         if (quarterStepResult == AIR_STEP_LANDED || quarterStepResult == AIR_STEP_GRABBED_LEDGE
    634             || quarterStepResult == AIR_STEP_GRABBED_CEILING
    635             || quarterStepResult == AIR_STEP_HIT_LAVA_WALL) {
    636             break;
    637         }
    638     }
    639 
    640     if (m->vel[1] >= 0.0f) {
    641         m->peakHeight = m->pos[1];
    642     }
    643 
    644     m->terrainSoundAddend = mario_get_terrain_sound_addend(m);
    645 
    646     if (m->action != ACT_FLYING) {
    647         apply_gravity(m);
    648     }
    649     apply_vertical_wind(m);
    650 
    651     vec3f_copy(m->marioObj->header.gfx.pos, m->pos);
    652     vec3s_set(m->marioObj->header.gfx.angle, 0, m->faceAngle[1], 0);
    653 
    654     return stepResult;
    655 }
    656 
    657 // They had these functions the whole time and never used them? Lol
    658 
    659 void set_vel_from_pitch_and_yaw(struct MarioState *m) {
    660     m->vel[0] = m->forwardVel * coss(m->faceAngle[0]) * sins(m->faceAngle[1]);
    661     m->vel[1] = m->forwardVel * sins(m->faceAngle[0]);
    662     m->vel[2] = m->forwardVel * coss(m->faceAngle[0]) * coss(m->faceAngle[1]);
    663 }
    664 
    665 void set_vel_from_yaw(struct MarioState *m) {
    666     m->vel[0] = m->slideVelX = m->forwardVel * sins(m->faceAngle[1]);
    667     m->vel[1] = 0.0f;
    668     m->vel[2] = m->slideVelZ = m->forwardVel * coss(m->faceAngle[1]);
    669 }