Super Mario Sunshine
Nintendo's 2002 3D platformer for the GameCube, read through the doldecomp decompilation.
On this page
The atlas reads Super Mario Sunshine through the doldecomp decompilation, C++ that builds five versions, the Japanese one by default. Names and comments quoted here are the decompilers’. Nearly all of Mario’s code and FLUDD’s is decompiled but doesn’t yet match the game’s own, and the claims say so. Most movement constants are named parameters with a default in the code, which a file on the disc can replace1. The atlas hasn’t read those files, so the numbers here are the defaults.
A jump takes off at 42 plus a quarter of Mario’s forward speed, the same take-off as Super Mario 642,3. Letting go of A while he rises faster than 20 multiplies his upward speed by 0.75 on each update, where Super Mario 64 divides it by 4 each frame2. The status numbers carry over too: the jump’s ends in 0x880 in both games, and the bit that lets a release cut the rise is the same2. Each update moves Mario only a quarter of his velocity, so a speed moves him a quarter as far per update as it does per frame in Super Mario 641.
The triple jump follows a landing from a double jump, a backflip, or a spin jump, and takes off by default at 70 plus a quarter of the speed, where Super Mario 64’s is a flat 694. The wall jump keeps Super Mario 64’s take-off of 62 up and 24 forward, but a wall hit holds Mario on the wall until he presses A or reaches the floor, with no 7-frame window5,6.
FLUDD’s hover nozzle eases Mario toward a height above where the hover began, 160 units by default, closing a set fraction of the gap each update. It lasts until the spray’s timer runs out, the pumping stops, or the tank empties7. Banjo-Kazooie’s Feathery Flap slows a fall instead8.
Mechanics
| Mechanic | Kind | How |
|---|---|---|
| Jump | Speed from the run | 42 plus a quarter of the forward speed, Super Mario 64's take-off, with each update moving Mario a quarter of his velocity |
| Gravity | One gravity | a parameter, 0.8 an update by default, down to −75, with a lighter one in a spin jump's descent |
| Variable jump height | Release cut | with A up while Mario rises faster than 20, in a status without bits 0x1000 and 0x20000, each update multiplies the rise by 0.75 in place of the gravity; the triple jump can't be cut |
| Triple jump | Landing chain | a press within 15 updates of landing from a double jump, a backflip, or a spin jump starts it at 20 forward or more, by default at 70 plus a quarter of the speed |
| Wall jump | Wall slide | a wall hit above 16 forward holds Mario on the wall, sliding ever faster after 10 updates, and A at any point kicks him off at 62 up and 24 forward |
| Flight | Held height | FLUDD's hover nozzle eases Mario toward 160 units above where it started, by default, until the spray's timer runs out, the pumping stops, or the tank empties |
Every claim about this game, by concept (5)
References
- ^ abIn the doldecomp decompilation of Super Mario Sunshine, most of Mario's movement constants are named parameters (
TParamRT), each built with a default in the code, in sets that each name a file, such as/Mario/Jump.prm.TParams::load, in a file configure.py marks as matching the game's code, reads the set's file from the scene's/map/paramsfolder if it is there and from theparamsarchive if not, and a record whose key and name match a parameter replaces that parameter's default; the rest keep theirs. The values the game plays with are data on the disc, which the atlas hasn't read, so the numbers in these claims are the code's defaults. The jump set's gravity,mGravity, defaults to 0.8. In the jumping states,jumpProcessin the decompilers' Player code, which doesn't yet match, moves Mario by a quarter of his velocity in one step per update. ThenfallProcesssubtractsmGravityfrom his vertical speed, except in a dive (the dive set's own gravity, with a cap of its own), in a spin jump's descent (mSpinJumpGravity, default 0.6), and when a release cuts the rise (sms/jump); in every case it then caps the fall at −75. Super Mario 64 also moves Mario a quarter of his velocity at a time, but in four steps a frame (sm64/units).Code notesdoldecomp/sms · n64decomp/sm64revisedHow we know
314Matching = True # Object matches and should be linked 315NonMatching = False # Object does not match and should not be linked
configure.py lines 314–315 at d2cc8f5 · upstream ↗ 879 Object(Matching, "System/Params.cpp"),configure.py line 879 at d2cc8f5 · upstream ↗ 950 Object(NonMatching, "Player/MarioPhysics.cpp"),configure.py line 950 at d2cc8f5 · upstream ↗ 964 PCHObject(NonMatching, "Player/MarioInit.cpp"),configure.py line 964 at d2cc8f5 · upstream ↗ 8#define PARAM_INIT(member, defaultValue) \ 9 member(this, defaultValue, JDrama::TNameRef::calcKeyCode(#member), #member)
include/System/ParamInst.hpp lines 8–9 at d2cc8f5 · upstream ↗ 12 if (mHead != nullptr) { 13 s32 length = stream.readS32(); 14 for (int i = 0; i < length; i++) { 15 u16 keyCode = stream.read16b(); ⋮ 21 if (keyCode == param->keyCode && !strcmp(buffer, param->name)) { 22 param->load(stream); 23 break; ⋮ 26 if (param == nullptr) { 27 s32 end = stream.read32b(); 28 stream.skip(end);
src/System/Params.cpp lines 12–29 at d2cc8f5 · upstream ↗ 54 if (mSceneArc != nullptr && mSceneArc->becomeCurrent(SceneParamsDir)) { 55 resource = mSceneArc->getResource(filename); ⋮ 64 if (mArc != nullptr) { 65 mArc->becomeCurrent("/"); 66 void* resource2 = mArc->getResource(filename);
src/System/Params.cpp lines 54–66 at d2cc8f5 · upstream ↗ 5static const char SceneParamsDir[] = "/map/params";src/System/Params.cpp line 5 at d2cc8f5 · upstream ↗ 544TMario::TJumpParams::TJumpParams() 545 : TParams("/Mario/Jump.prm") 546 , PARAM_INIT(mGravity, 0.8f) 547 , PARAM_INIT(mSpinJumpGravity, 0.6f)
src/Player/MarioInit.cpp lines 544–547 at d2cc8f5 · upstream ↗ 498 Vec next; 499 next.x = mPosition.x + 0.25f * mVel.x; 500 next.y = mPosition.y + 0.25f * mVel.y; 501 next.z = mPosition.z + 0.25f * mVel.z; 502 int ret = checkGroundAtJumping(next, param_1); ⋮ 509 fallProcess();
src/Player/MarioPhysics.cpp lines 498–509 at d2cc8f5 · upstream ↗ 464void TMario::fallProcess() ⋮ 466 if (mStatus == MARIO_STATUS_DIVE) { 467 mVel.y -= mDivingParams.mGravity.get(); 468 if (mVel.y < -75.0f) 469 mVel.y = -75.0f; 470 } else { 471 if (isFallCancel()) { 472 mVel.y *= 0.75f; 473 } else if ((mStatus == MARIO_STATUS_LEFT_ROTATE_JUMP 474 || mStatus == MARIO_STATUS_RIGHT_ROTATE_JUMP) 475 && mVel.y < 0.0f) { 476 mVel.y -= mJumpParams.mSpinJumpGravity.get(); ⋮
src/Player/MarioPhysics.cpp lines 464–485 at d2cc8f5 · upstream ↗ 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;
src/game/mario_step.c lines 618–621 at 9921382 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-22.
Revised after a check by a second AI model, 2026-10-22.
- ^ abcIn Super Mario Sunshine, by the decompilers' Player code, which doesn't yet match, a press that starts a jump through
changePlayerJumpingfirst goes to a miss jump ifisJumpMissis true, then to a spin jump ifconsiderRotateJumpStartreads the stick's recent turning as a spin, and otherwise to the status it asks for. For the single jump,MARIO_STATUS_JUMP,setStatusToJumpingsets Mario's vertical speed to 42 plus a quarter of his forward speed and multiplies the forward speed by 0.8, constants in the code rather than parameters, and the same take-off as Super Mario 64's single jump (sm64/jump-speed). On two kinds of floor, which the code tells apart only asisThing2andisThing3, the speed is replaced or added to and the jump becomesMARIO_STATUS_FORCE_JUMP; on Yoshi it is multiplied by the parametermJumpYoshiMult, and whileisSinking()is true both speeds are scaled by a factor from themGraffitoParamsset. When the new jumping status carries the bit the code callsMARIO_STATUS_FLAG_UNK2000000(0x2000000), as the single jump, the double jump, and the wall kick do,setStatusToJumpingsets bit 0x100 ofunk78, and it clears that bit for any other jumping status (other code clears it too). While that bit is set, the status has neither bit 0x1000 nor 0x20000, A isn't held, and Mario rises faster than 20,fallProcessmultiplies his vertical speed by 0.75 on each update in place of the gravity. Super Mario 64 divides it by 4 on each frame when A isn't held and Mario rises faster than 20, if itsMARIO_UNKNOWN_08flag is set, the action is neither intangible nor invulnerable, and the action hasACT_FLAG_CONTROL_JUMP_HEIGHT, the same bit, 0x02000000 (sm64/jump-release). The status numbers line up too: Sunshine's single jump is 0x2000000 | 0x800 | 0x080 (computed 0x2000880), and Super Mario 64'sACT_JUMPis 0x03000880.Code notesdoldecomp/sms · n64decomp/sm64revisedHow we know
949 PCHObject(NonMatching, "Player/MarioMove.cpp"), 950 Object(NonMatching, "Player/MarioPhysics.cpp"),
configure.py lines 949–950 at d2cc8f5 · upstream ↗ 764int TMario::changePlayerJumping(u32 param_1, u32 param_2) ⋮ 766 if (isJumpMiss()) { 767 setMissJumping(); 768 } else { 769 if (considerRotateJumpStart()) 770 return 1; ⋮ 772 changePlayerStatus(param_1, param_2, false);
src/Player/MarioMove.cpp lines 764–772 at d2cc8f5 · upstream ↗ 420void TMario::setPlayerJumpSpeed(f32 speed_mult, f32 force) ⋮ 422 mVel.y = (mForwardVel * speed_mult) + force;
src/Player/MarioMove.cpp lines 420–423 at d2cc8f5 · upstream ↗ 434 switch (status) { 435 case MARIO_STATUS_JUMP: 436 case 0x89C: 437 setPlayerJumpSpeed(0.25f, 42.0f); 438 mForwardVel *= 0.8f; ⋮ 441 if (mGroundPlane->isThing2()) { 442 mVel.y = 0.01f * mGroundPlane->getActiveJumpPower(); 443 nextStatus = MARIO_STATUS_FORCE_JUMP; ⋮ 445 } else if (mGroundPlane->isThing3()) { 446 mVel.y += -unkBC + getGroundJumpPower(); ⋮ 453 nextStatus = MARIO_STATUS_FORCE_JUMP;
src/Player/MarioMove.cpp lines 434–456 at d2cc8f5 · upstream ↗ 647 if (isSinking()) { 648 f32 scale = ((mGraffitoParams.mSinkJumpRateMax.get() ⋮ 653 mVel.y *= scale; 654 mForwardVel *= scale;
src/Player/MarioMove.cpp lines 647–654 at d2cc8f5 · upstream ↗ 663 if (onYoshi()) { 664 mVel.y *= mYoshiParams.mJumpYoshiMult.get(); ⋮ 670 if (nextStatus & MARIO_STATUS_FLAG_UNK2000000) 671 unk78 |= 0x100; 672 else 673 unk78 &= ~0x100;
src/Player/MarioMove.cpp lines 663–673 at d2cc8f5 · upstream ↗ 1482 if (mGamePad->checkMeaning(TMarioGamePad::MEANING_A)) 1483 mInput |= 0x80;
src/Player/MarioMove.cpp lines 1482–1483 at d2cc8f5 · upstream ↗ 450BOOL TMario::isFallCancel() ⋮ 452 if (!(unk78 & 0x100 ? true : false)) 453 return false; ⋮ 455 if (mStatus & 0x21000) 456 return false; ⋮ 458 if (!(mInput & 0x80) && mVel.y > 20.0f) 459 return true;
src/Player/MarioPhysics.cpp lines 450–459 at d2cc8f5 · upstream ↗ 471 if (isFallCancel()) { 472 mVel.y *= 0.75f;
src/Player/MarioPhysics.cpp lines 471–472 at d2cc8f5 · upstream ↗ 7 MARIO_STATUS_FLAG_JUMPING = 0x800,include/Player/MarioStatus.hpp line 7 at d2cc8f5 · upstream ↗ 17 MARIO_STATUS_FLAG_UNK2000000 = 0x2000000,include/Player/MarioStatus.hpp line 17 at d2cc8f5 · upstream ↗ 26 MARIO_STATUS_TYPE_JUMPING = 0x080,include/Player/MarioStatus.hpp line 26 at d2cc8f5 · upstream ↗ 119 MARIO_STATUS_JUMP = MARIO_STATUS_FLAG_UNK2000000 | MARIO_STATUS_FLAG_JUMPING 120 | MARIO_STATUS_TYPE_JUMPING | 0x0, // 0x2000880 121 MARIO_STATUS_SECOND_JUMP = MARIO_STATUS_FLAG_UNK2000000 122 | MARIO_STATUS_FLAG_JUMPING ⋮ 130 MARIO_STATUS_WALL_JUMP = MARIO_STATUS_FLAG_UNK2000000 131 | MARIO_STATUS_FLAG_JUMPING
include/Player/MarioStatus.hpp lines 119–133 at d2cc8f5 · upstream ↗ 821 case ACT_JUMP: 822 case ACT_HOLD_JUMP: ⋮ 824 set_mario_y_vel_based_on_fspeed(m, 42.0f, 0.25f); 825 m->forwardVel *= 0.8f;
src/game/mario.c lines 821–825 at 9921382 · upstream ↗ 763static void set_mario_y_vel_based_on_fspeed(struct MarioState *m, f32 initialVelY, f32 multiplier) { ⋮ 766 m->vel[1] = initialVelY + get_additive_y_vel_for_jumps() + m->forwardVel * multiplier;
src/game/mario.c lines 763–766 at 9921382 · upstream ↗ 519u32 should_strengthen_gravity_for_jump_ascent(struct MarioState *m) { 520 if (!(m->flags & MARIO_UNKNOWN_08)) { 521 return FALSE; ⋮ 524 if (m->action & (ACT_FLAG_INTANGIBLE | ACT_FLAG_INVULNERABLE)) { 525 return FALSE; ⋮ 528 if (!(m->input & INPUT_A_DOWN) && m->vel[1] > 20.0f) { 529 return (m->action & ACT_FLAG_CONTROL_JUMP_HEIGHT) != 0;
src/game/mario_step.c lines 519–529 at 9921382 · upstream ↗ 559 } else if (should_strengthen_gravity_for_jump_ascent(m)) { 560 m->vel[1] /= 4.0f;
src/game/mario_step.c lines 559–560 at 9921382 · upstream ↗ 165#define ACT_FLAG_CONTROL_JUMP_HEIGHT /* 0x02000000 */ (1 << 25)include/sm64.h line 165 at 9921382 · upstream ↗ 256#define ACT_JUMP 0x03000880 // (0x080 | ACT_FLAG_AIR | ACT_FLAG_ALLOW_VERTICAL_WIND_ACTION | ACT_FLAG_CONTROL_JUMP_HEIGHT)include/sm64.h line 256 at 9921382 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-22.
Revised after a check by a second AI model, 2026-10-22.
- ^A single jump sets Mario's vertical speed to 42 plus a quarter of his forward speed, in units per frame, and multiplies the forward speed by 0.8.Coden64decomp/sm64
How we know
763static void set_mario_y_vel_based_on_fspeed(struct MarioState *m, f32 initialVelY, f32 multiplier) { ⋮ 766 m->vel[1] = initialVelY + get_additive_y_vel_for_jumps() + m->forwardVel * multiplier;
src/game/mario.c lines 763–766 at 9921382 · upstream ↗ 821 case ACT_JUMP: 822 case ACT_HOLD_JUMP: ⋮ 824 set_mario_y_vel_based_on_fspeed(m, 42.0f, 0.25f); 825 m->forwardVel *= 0.8f;
src/game/mario.c lines 821–826 at 9921382 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-21.
Checked by a second AI model, 2026-10-21.
- ^In Super Mario Sunshine, by the decompilers' Player code, which doesn't yet match, the double jump, the backflip (
MARIO_STATUS_BACK_JUMP), the spin jump, and the jumps the code callstrampleandwireJumpingall land inMARIO_STATUS_SEC_JUMP_SLIP. ItsjumpSlipEventscounts updates and ends the landing on the 16th, before reading A, so a press on one of the first 15 (computed) goes tochangePlayerJumping, which can still turn it into a miss or spin jump (sms/jump): the triple jump,MARIO_STATUS_ULTRA_JUMP, if the forward speed is at least the parametermTriJumpEnableSp(default 20), and a single jump if not. The single jump and the wall kick land inMARIO_STATUS_JUMP_SLIPinstead, where a press makes a double jump only at a forward speed of at leastmSecJumpEnableSp(default 20). The triple jump takes off atmUltraJumpForceplusmUltraJumpSpeedMulttimes the forward speed (defaults 70 and 0.25) and multiplies the forward speed bymUltraJumpXZMult(default 0.8); its status lacks the bit that lets a release cut the rise. Super Mario 64's triple jump starts from the double jump's 4-frame landing at a forward speed above 20, takes off at 69 whatever the speed, and can't be cut either (sm64/triple-jump, sm64/jump-release).Code notesdoldecomp/sms · n64decomp/sm64revisedHow we know
947 Object(NonMatching, "Player/MarioJump.cpp"), ⋮ 949 PCHObject(NonMatching, "Player/MarioMove.cpp"), ⋮ 952 PCHObject(NonMatching, "Player/MarioRun.cpp"), ⋮ 964 PCHObject(NonMatching, "Player/MarioInit.cpp"),
configure.py lines 947–964 at d2cc8f5 · upstream ↗ 341BOOL TMario::secJumping() ⋮ 357 jumpingBasic(MARIO_STATUS_SEC_JUMP_SLIP, anim, 3);
src/Player/MarioJump.cpp lines 341–357 at d2cc8f5 · upstream ↗ 378BOOL TMario::backJumping() ⋮ 390 jumpingBasic(MARIO_STATUS_SEC_JUMP_SLIP, 0xF7, 0); 391 else 392 jumpingBasic(MARIO_STATUS_SEC_JUMP_SLIP, ANIM_LAND, 0);
src/Player/MarioJump.cpp lines 378–392 at d2cc8f5 · upstream ↗ 773BOOL TMario::trample() ⋮ 778 jumpingBasic(MARIO_STATUS_SEC_JUMP_SLIP, mAnimationId, 3);
src/Player/MarioJump.cpp lines 773–778 at d2cc8f5 · upstream ↗ 997BOOL TMario::rotateJumping() ⋮ 1007 jumpingBasic(MARIO_STATUS_SEC_JUMP_SLIP, mAnimationId, 0);
src/Player/MarioJump.cpp lines 997–1007 at d2cc8f5 · upstream ↗ 1021BOOL TMario::wireJumping() ⋮ 1035 jumpingBasic(MARIO_STATUS_SEC_JUMP_SLIP, mAnimationId, 3);
src/Player/MarioJump.cpp lines 1021–1035 at d2cc8f5 · upstream ↗ 335 jumpingBasic(MARIO_STATUS_JUMP_SLIP, ANIM_JUMP, 3);src/Player/MarioJump.cpp line 335 at d2cc8f5 · upstream ↗ 454 jumpingBasic(MARIO_STATUS_JUMP_SLIP, ANIM_WJMP, 3);src/Player/MarioJump.cpp line 454 at d2cc8f5 · upstream ↗ 1538BOOL TMario::jumpSlipEvents(JumpSlipRecord* rec) ⋮ 1541 return changePlayerStatus(rec->mEndStatus, 0, false); ⋮ 1543 mStatusTimer++; ⋮ 1545 if (mStatusTimer >= rec->mMaxTimer) 1546 return changePlayerStatus(rec->mEndStatus, 0, false); ⋮ 1548 if (mInput & 0x2) { 1549 if (rec->mJumpStatus == MARIO_STATUS_SECOND_JUMP) 1550 if (mForwardVel >= mJumpParams.mSecJumpEnableSp.get()) 1551 return changePlayerJumping(MARIO_STATUS_SECOND_JUMP, 0); ⋮ 1553 if (rec->mJumpStatus == MARIO_STATUS_ULTRA_JUMP) 1554 if (mForwardVel >= mJumpParams.mTriJumpEnableSp.get()) 1555 return changePlayerJumping(MARIO_STATUS_ULTRA_JUMP, 0); ⋮
src/Player/MarioRun.cpp lines 1538–1557 at d2cc8f5 · upstream ↗ 1599BOOL TMario::secJumpSlip() ⋮ 1601 static TMario::JumpSlipRecord param 1602 = { 0x10, MARIO_STATUS_SEC_JUMP_END, MARIO_STATUS_ULTRA_JUMP,
src/Player/MarioRun.cpp lines 1599–1602 at d2cc8f5 · upstream ↗ 471 case MARIO_STATUS_ULTRA_JUMP: 472 setPlayerJumpSpeed(mJumpParams.mUltraJumpSpeedMult.get(), 473 mJumpParams.mUltraJumpForce.get()); 474 mForwardVel *= mJumpParams.mUltraJumpXZMult.get();
src/Player/MarioMove.cpp lines 471–474 at d2cc8f5 · upstream ↗ 572 , PARAM_INIT(mSecJumpEnableSp, 20.0f) ⋮ 576 , PARAM_INIT(mTriJumpEnableSp, 20.0f) 577 , PARAM_INIT(mUltraJumpForce, 70.0f) 578 , PARAM_INIT(mUltraJumpSpeedMult, 0.25f) 579 , PARAM_INIT(mUltraJumpXZMult, 0.8f)
src/Player/MarioInit.cpp lines 572–579 at d2cc8f5 · upstream ↗ 124 MARIO_STATUS_ULTRA_JUMP 125 = MARIO_STATUS_FLAG_JUMPING | MARIO_STATUS_TYPE_JUMPING | 0x2, // 0x882
include/Player/MarioStatus.hpp lines 124–125 at d2cc8f5 · upstream ↗ 1874s32 act_double_jump_land(struct MarioState *m) { 1875 if (common_landing_cancels(m, &sDoubleJumpLandAction, set_triple_jump_action)) {
src/game/mario_actions_moving.c lines 1874–1875 at 9921382 · upstream ↗ 50struct LandingAction sDoubleJumpLandAction = { 51 4, 5, ACT_FREEFALL, ACT_DOUBLE_JUMP_LAND_STOP, ACT_JUMP, ACT_FREEFALL, ACT_BEGIN_SLIDING,
src/game/mario_actions_moving.c lines 50–51 at 9921382 · upstream ↗ 150 } else if (m->forwardVel > 20.0f) { 151 return set_mario_action(m, ACT_TRIPLE_JUMP, 0);
src/game/mario_actions_moving.c lines 150–151 at 9921382 · upstream ↗ 796 case ACT_TRIPLE_JUMP: 797 set_mario_y_vel_based_on_fspeed(m, 69.0f, 0.0f); 798 m->forwardVel *= 0.8f;
src/game/mario.c lines 796–798 at 9921382 · upstream ↗ 258#define ACT_TRIPLE_JUMP 0x01000882 // (0x082 | ACT_FLAG_AIR | ACT_FLAG_ALLOW_VERTICAL_WIND_ACTION)include/sm64.h line 258 at 9921382 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-22.
Revised after a check by a second AI model, 2026-10-22.
- ^In Super Mario Sunshine, by the decompilers' Player code, which doesn't yet match, when
jumpProcessreports a wall tojumpingBasicduring a jump, Mario is knocked back if his forward speed is above the parametermClashSpeed(default 50) outside the wire roll jump, stops on Yoshi, stops on a wall markedisNoWallJump, and catches a fence (isFence). Otherwise, at a forward speed above 16, he turns about (afterplayerRefrection) and, against a wall, holds on inMARIO_STATUS_WALL_SLIDE. TherestayWalladds 1 to the status timer each update, capped at 60, and 1 more while it is below 20, so he hangs still for 10 updates and then slides down at half the timer: −10.5 on the 11th update, then 0.5 faster each update to −30 (computed). An A press on any update on the wall kicks:startJumpWallturns him about and entersMARIO_STATUS_WALL_JUMP, whose take-off insetStatusToJumpingis 62 up and 24 forward, constants in the code; the 52 thatstartJumpWallwrites first is overwritten. Landing or losing the wall ends the hold. Super Mario 64 kicks at the same 62 up and at least 24 forward, and itsact_air_hit_wallalso writes a 52 that the kick's take-off overwrites, but it holds Mario only 2 frames and then bonks him away, with A still working for 5 more: 7 frames in all (sm64/wall-kick). With no wall to hold, Sunshine bonks him the way Super Mario 64 does: back down at 38 or faster, and at −8 if faster than 8.Code notesdoldecomp/sms · n64decomp/sm64revisedHow we know
947 Object(NonMatching, "Player/MarioJump.cpp"), ⋮ 949 PCHObject(NonMatching, "Player/MarioMove.cpp"), 950 Object(NonMatching, "Player/MarioPhysics.cpp"), ⋮ 964 PCHObject(NonMatching, "Player/MarioInit.cpp"),
configure.py lines 947–964 at d2cc8f5 · upstream ↗ 201 case 2: 202 if (mStatus != MARIO_STATUS_WIRE_ROLL_JUMP 203 && mForwardVel > mDeParams.mClashSpeed.get()) { ⋮ 205 changePlayerDropping(MARIO_STATUS_JUMP_SHORT_BACK_DOWN, 0U); ⋮ 209 if (onYoshi()) { 210 setPlayerVelocity(0.0f); ⋮ 213 if (mWallPlane != nullptr) { 214 if (mWallPlane->isNoWallJump()) { ⋮ 216 setPlayerVelocity(0.0f); ⋮ 220 if (mWallPlane != nullptr) { 221 if (mWallPlane->isFence()) { ⋮ 228 return changePlayerStatus(MARIO_STATUS_FENCE_JUMP_CATCH, 0, 0); ⋮
src/Player/MarioJump.cpp lines 201–250 at d2cc8f5 · upstream ↗ 252 if (mForwardVel > 8.0f) 253 setPlayerVelocity(-8.0f); 254 return changePlayerStatus(MARIO_STATUS_LAND_SAFE_DOWN, 0, 0);
src/Player/MarioJump.cpp lines 252–254 at d2cc8f5 · upstream ↗ 510 , PARAM_INIT(mClashSpeed, 50.0f)src/Player/MarioInit.cpp line 510 at d2cc8f5 · upstream ↗ 586BOOL TMario::stayWall() ⋮ 588 mStatusTimer += 1; 589 if (mStatusTimer > 60) 590 mStatusTimer = 60; ⋮ 592 if (mInput & 2) 593 return startJumpWall(); ⋮ 598 if (mStatusTimer < 20) { 599 mStatusTimer += 1; 600 mVel.set(0.0f, 0.0f, 0.0f); 601 } else { 602 mVel.y = -mStatusTimer * 0.5f; ⋮ 610 switch (jumpProcess(0)) { ⋮
src/Player/MarioJump.cpp lines 586–620 at d2cc8f5 · upstream ↗ 16BOOL TMario::startJumpWall() ⋮ 25 mVel.y = 52.0f; 26 mFaceAngle.y += 0x8000; 27 if (mVel.y + (160.0f + mPosition.y) >= mFloorPosition.x) 28 mVel.y = 1.0f; ⋮ 30 return changePlayerStatus(MARIO_STATUS_WALL_JUMP, 0, 0);
src/Player/MarioJump.cpp lines 16–30 at d2cc8f5 · upstream ↗ 517 case MARIO_STATUS_WALL_JUMP: 518 setPlayerJumpSpeed(0.0f, 62.0f); 519 mForwardVel = 24.0f;
src/Player/MarioMove.cpp lines 517–519 at d2cc8f5 · upstream ↗ 733 switch (status & MARIO_STATUS_TYPE_MASK) { ⋮ 737 case MARIO_STATUS_TYPE_JUMPING: 738 status = setStatusToJumping(status, arg);
src/Player/MarioMove.cpp lines 733–738 at d2cc8f5 · upstream ↗ 1479 if (mGamePad->checkFrameMeaning(TMarioGamePad::MEANING_A)) 1480 mInput |= 0x2;
src/Player/MarioMove.cpp lines 1479–1480 at d2cc8f5 · upstream ↗ 1314 if (++(m->actionTimer) <= 2) { 1315 if (m->input & INPUT_A_PRESSED) { 1316 m->vel[1] = 52.0f; 1317 m->faceAngle[1] += 0x8000; 1318 return set_mario_action(m, ACT_WALL_KICK_AIR, 0); ⋮ 1320 } else if (m->forwardVel >= 38.0f) { 1321 m->wallKickTimer = 5; ⋮ 1327 return set_mario_action(m, ACT_BACKWARD_AIR_KB, 0); ⋮ 1329 m->wallKickTimer = 5; ⋮ 1334 if (m->forwardVel > 8.0f) { 1335 mario_set_forward_vel(m, -8.0f); ⋮ 1337 return set_mario_action(m, ACT_SOFT_BONK, 0);
src/game/mario_actions_airborne.c lines 1314–1337 at 9921382 · upstream ↗ 1147s32 check_wall_kick(struct MarioState *m) { 1148 if ((m->input & INPUT_A_PRESSED) && m->wallKickTimer != 0 && m->prevAction == ACT_AIR_HIT_WALL) { 1149 m->faceAngle[1] += 0x8000; 1150 return set_mario_action(m, ACT_WALL_KICK_AIR, 0);
src/game/mario_actions_airborne.c lines 1147–1151 at 9921382 · upstream ↗ 1407 if (m->wallKickTimer > 0) { 1408 m->wallKickTimer--;
src/game/mario.c lines 1407–1409 at 9921382 · upstream ↗ 828 case ACT_WALL_KICK_AIR: 829 case ACT_TOP_OF_POLE_JUMP: 830 set_mario_y_vel_based_on_fspeed(m, 62.0f, 0.0f); 831 if (m->forwardVel < 24.0f) { 832 m->forwardVel = 24.0f;
src/game/mario.c lines 828–833 at 9921382 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-22.
Revised after a check by a second AI model, 2026-10-22.
- ^A jump that hits a wall at a forward speed above 16 turns Mario around and holds him in
ACT_AIR_HIT_WALL. A press on its first 2 frames kicks off the wall. On the 3rd he bonks away,wallKickTimeris set to 5 and counts down once a frame, and A still kicks while it is nonzero: 7 frames in all. The kick takes off at 62 units/frame up and at least 24 forward.Coden64decomp/sm64revisedHow we know
388 case AIR_STEP_HIT_WALL: ⋮ 391 if (m->forwardVel > 16.0f) { ⋮ 396 m->faceAngle[1] += 0x8000; ⋮ 399 set_mario_action(m, ACT_AIR_HIT_WALL, 0);
src/game/mario_actions_airborne.c lines 388–399 at 9921382 · upstream ↗ 1314 if (++(m->actionTimer) <= 2) { 1315 if (m->input & INPUT_A_PRESSED) { ⋮ 1318 return set_mario_action(m, ACT_WALL_KICK_AIR, 0); ⋮ 1320 } else if (m->forwardVel >= 38.0f) { 1321 m->wallKickTimer = 5; ⋮ 1327 return set_mario_action(m, ACT_BACKWARD_AIR_KB, 0); 1328 } else { 1329 m->wallKickTimer = 5; ⋮ 1337 return set_mario_action(m, ACT_SOFT_BONK, 0);
src/game/mario_actions_airborne.c lines 1314–1337 at 9921382 · upstream ↗ 1156s32 act_backward_air_kb(struct MarioState *m) { 1157 if (check_wall_kick(m)) { 1158 return TRUE;
src/game/mario_actions_airborne.c lines 1156–1158 at 9921382 · upstream ↗ 1247s32 act_soft_bonk(struct MarioState *m) { 1248 if (check_wall_kick(m)) { 1249 return TRUE;
src/game/mario_actions_airborne.c lines 1247–1249 at 9921382 · upstream ↗ 1147s32 check_wall_kick(struct MarioState *m) { 1148 if ((m->input & INPUT_A_PRESSED) && m->wallKickTimer != 0 && m->prevAction == ACT_AIR_HIT_WALL) { 1149 m->faceAngle[1] += 0x8000; 1150 return set_mario_action(m, ACT_WALL_KICK_AIR, 0);
src/game/mario_actions_airborne.c lines 1147–1151 at 9921382 · upstream ↗ 1407 if (m->wallKickTimer > 0) { 1408 m->wallKickTimer--;
src/game/mario.c lines 1407–1409 at 9921382 · upstream ↗ 1705 update_mario_inputs(gMarioState); ⋮ 1714 // The function can loop through many action shifts in one frame, ⋮ 1717 while (inLoop) {
src/game/mario.c lines 1705–1717 at 9921382 · upstream ↗ 828 case ACT_WALL_KICK_AIR: 829 case ACT_TOP_OF_POLE_JUMP: 830 set_mario_y_vel_based_on_fspeed(m, 62.0f, 0.0f); 831 if (m->forwardVel < 24.0f) { 832 m->forwardVel = 24.0f;
src/game/mario.c lines 828–834 at 9921382 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-21.
Revised after a check by a second AI model, 2026-10-21.
- ^In Super Mario Sunshine, by the decompilers' Player code, which doesn't yet match, FLUDD's hover nozzle holds Mario up through the status the code calls
MARIO_STATUS_ROCKET.rocketCheck, which the jumps, the wall kick, standing, and running call, enters it, unless Mario is already in it or inMARIO_STATUS_ROCKET_LANDING, when he has FLUDD, the current nozzle'smRocketTypeparameter is 1, his upper body is inUPPER_STATE_PUMPING, and the nozzle is spraying (canSpray, a name a decompilers' comment calls fabricated), and sets a target heightmHoverHeight(default 160) above him; standing or running, the callers set it above the floor instead. The status's entry code has a voice for the hover, rocket, and turbo nozzles, and the height-holding code inrocketingruns only for the hover. Each updaterocketingsets his vertical speed to the gap to the target times the parametermAccelRate(default 0.03) and multiplies his forward speed bymBrake(default 0.95), the stick adding forward speed or turning him, and thenjumpProcessmoves him a quarter of his velocity: with the defaults he closes 0.75% of the gap each update (computed), and any rise or fall he carried in is dropped at once. The hover is a trigger nozzle (TNozzleTrigger). While Mario pumps, and in water only with a full tank, each update adds 150 times R's analog reading tomInsidePressure, and every update takes awaymInsidePressureDec(default 100); when the pressure passesmInsidePressureMax(default 4500) while the spray is idle and the nozzle'smRumbleOnChargeis false, the spray starts with a timer ofmTriggerTimeupdates (withmRumbleOnChargetrue, an A press with R held starts it instead). An empty tank sets the spray back to idle; the timer running out or the pumping stopping sets it to the state the code callsSPRAY_STATE_DEAD, which returns to idle only once R reads 0. Without a spray,rocketingends inMARIO_STATUS_ROCKET_LANDING. On each update it sprays, each of the nozzle's emitters asks the water manager for drops, betweenmNumMinandmNumby the pressure, and the tank, which holdsmAmountMax(default 2100), losesmDecRatefor each drop accepted. Which nozzles havemRocketType1, the timer, and the tank are data in each nozzle's file, NozzleTrgHover.prm for the hover. Banjo-Kazooie's Feathery Flap, a lift of 280 units/s and a fall capped at −399.9 units/s for up to 2.5 s, slows a fall where the hover holds a height (banjo-kazooie/feathery-flap).Code notesdoldecomp/sms · banjo.decomp/banjo-kazooierevisedHow we know
947 Object(NonMatching, "Player/MarioJump.cpp"), ⋮ 949 PCHObject(NonMatching, "Player/MarioMove.cpp"), 950 Object(NonMatching, "Player/MarioPhysics.cpp"), ⋮ 952 PCHObject(NonMatching, "Player/MarioRun.cpp"), ⋮ 956 Object(NonMatching, "Player/MarioWait.cpp"), ⋮ 959 PCHObject(NonMatching, "Player/WaterGun.cpp"), ⋮ 964 PCHObject(NonMatching, "Player/MarioInit.cpp"),
configure.py lines 947–964 at d2cc8f5 · upstream ↗ 880BOOL TMario::rocketCheck() ⋮ 883 if (mStatus == MARIO_STATUS_ROCKET) 884 bVar2 = false; 885 if (mStatus == MARIO_STATUS_ROCKET_LANDING) 886 bVar2 = false; ⋮ 888 if (checkFlag(MARIO_FLAG_HAS_FLUDD)) { 889 if (mWaterGun->getCurrentNozzle()->mRocketType.get() != 1) 890 bVar2 = false; ⋮ 892 if (!isUpperState(UPPER_STATE_PUMPING)) 893 bVar2 = false; ⋮ 895 if (!mWaterGun->canSpray()) 896 bVar2 = false; ⋮
src/Player/MarioJump.cpp lines 880–904 at d2cc8f5 · upstream ↗ 118 if (rocketCheck()) { 119 unk314 120 = mFloorPosition.y + mWaterGun->mWatergunParams.mHoverHeight.get(); 121 return changePlayerStatus(MARIO_STATUS_ROCKET, 0, false);
src/Player/MarioWait.cpp lines 118–121 at d2cc8f5 · upstream ↗ 697 if (rocketCheck()) { 698 unk314 699 = mFloorPosition.y + mWaterGun->mWatergunParams.mHoverHeight.get(); 700 return changePlayerStatus(MARIO_STATUS_ROCKET, 0, false);
src/Player/MarioRun.cpp lines 697–700 at d2cc8f5 · upstream ↗ 451 if (rocketCheck())src/Player/MarioJump.cpp line 451 at d2cc8f5 · upstream ↗ 19 , PARAM_INIT(mHoverHeight, 160.0f)include/Player/WaterGun.hpp line 19 at d2cc8f5 · upstream ↗ 238 // TODO: get rid of this -- it's real name is isEmitting() and it 239 // wasn't stripped in MarioRun.cpp 240 // //Fabricated 241 bool canSpray() const ⋮ 243 if (mCurrentWater == 0) 244 return false; ⋮ 248 if (triggerNozzle->getSprayState() 249 == TNozzleTrigger::SPRAY_STATE_ACTIVE) 250 return true;
include/Player/WaterGun.hpp lines 238–250 at d2cc8f5 · upstream ↗ 573 case MARIO_STATUS_ROCKET: { ⋮ 575 if ((int)mWaterGun->mCurrentNozzle == TWaterGun::Rocket) { ⋮ 579 if ((int)mWaterGun->mCurrentNozzle == TWaterGun::Turbo) ⋮ 581 if ((int)mWaterGun->mCurrentNozzle == TWaterGun::Hover) 582 startVoice(MSD_SE_MV21_JUMP_SMALL_01); 583 setPlayerJumpSpeed(0.0f, 10.0f);
src/Player/MarioMove.cpp lines 573–583 at d2cc8f5 · upstream ↗ 909BOOL TMario::rocketing() ⋮ 911 if (!checkFlag(MARIO_FLAG_HAS_FLUDD)) 912 return changePlayerStatus(MARIO_STATUS_ROCKET_LANDING, 0, 0); ⋮ 914 if (mWaterGun->getCurrentNozzle()->mRocketType.get() != 1) 915 return changePlayerStatus(MARIO_STATUS_ROCKET_LANDING, 0, 0); ⋮ 917 if (!isUpperState(UPPER_STATE_PUMPING) || !mWaterGun->canSpray()) 918 return changePlayerStatus(MARIO_STATUS_ROCKET_LANDING, 0, 0);
src/Player/MarioJump.cpp lines 909–918 at d2cc8f5 · upstream ↗ 920 if (mInput & 1) { 921 switch (mWaterGun->mCurrentNozzle) { 922 case TWaterGun::Hover: ⋮ 942 mForwardVel += mag * JMASCos(angleDiff) 943 * mDivingParams.mAccelControl.get(); ⋮ 952 s16 rotSp = mHoverParams.mRotSp.get(); 953 mFaceAngle.y 954 = mIntendedYaw 955 - IConverge((s16)(mIntendedYaw - mFaceAngle.y), 0, 956 rotSp, rotSp);
src/Player/MarioJump.cpp lines 920–959 at d2cc8f5 · upstream ↗ 971 switch (mWaterGun->getCurrentNozzleType()) { 972 case TWaterGun::Hover: 973 mVel.y = (unk314 - mPosition.y) * mHoverParams.mAccelRate.get(); 974 mForwardVel *= mHoverParams.mBrake.get(); 975 break; ⋮ 978 switch (jumpProcess(2)) {
src/Player/MarioJump.cpp lines 971–978 at d2cc8f5 · upstream ↗ 500 next.y = mPosition.y + 0.25f * mVel.y;src/Player/MarioPhysics.cpp line 500 at d2cc8f5 · upstream ↗ 45 , mHoverParams("/Mario/HHover.prm")src/Player/MarioInit.cpp line 45 at d2cc8f5 · upstream ↗ 916TMario::THHoverParams::THHoverParams(const char* prm) 917 : TParams(prm) 918 , PARAM_INIT(mRotSp, 0x80) 919 , PARAM_INIT(mAccelRate, 0.029999999f) 920 , PARAM_INIT(mBrake, 0.94999999f) ⋮ 922 TParams::load(mPrmPath);
src/Player/MarioInit.cpp lines 916–922 at d2cc8f5 · upstream ↗ 2375 stateMachine(); ⋮ 2387 gunExec();
src/Player/MarioMove.cpp lines 2375–2387 at d2cc8f5 · upstream ↗ 2300 mWaterGun->triggerPressureMovement(*unk108);src/Player/MarioMove.cpp line 2300 at d2cc8f5 · upstream ↗ 2316 } else if (mUpperState == UPPER_STATE_PUMPING) { 2317 mWaterGun->emit();
src/Player/MarioMove.cpp lines 2316–2317 at d2cc8f5 · upstream ↗ 1148 , mNozzleHover("hover_wg", "/Mario/WaterGun/NozzleTrgHover.prm", this)src/Player/WaterGun.cpp line 1148 at d2cc8f5 · upstream ↗ 1163 mNozzleList[Hover] = &mNozzleHover;src/Player/WaterGun.cpp line 1163 at d2cc8f5 · upstream ↗ 197 , PARAM_INIT(mAmountMax, 0x834)src/Player/WaterGun.cpp line 197 at d2cc8f5 · upstream ↗ 222 , PARAM_INIT(mInsidePressureDec, 100.0f) 223 , PARAM_INIT(mInsidePressureMax, 4500.0f) 224 , PARAM_INIT(mTriggerTime, 1)
src/Player/WaterGun.cpp lines 222–224 at d2cc8f5 · upstream ↗ 508void TNozzleTrigger::movement(const TMarioControllerWork& controllerWork) ⋮ 510 if (mFludd->getCurrentWater() <= 0) { 511 mSprayState = TNozzleTrigger::SPRAY_STATE_INACTIVE; ⋮ 517 if (mSprayState == TNozzleTrigger::SPRAY_STATE_ACTIVE) { 518 mSprayTimer -= 1; ⋮ 522 if (!isPumping || mSprayTimer <= 0) { 523 mSprayState = TNozzleTrigger::SPRAY_STATE_DEAD;
src/Player/WaterGun.cpp lines 508–527 at d2cc8f5 · upstream ↗ 529 if ((mRumbleOnCharge == true 530 && (controllerWork.mFrameInput & TMarioControllerWork::A) != 0 531 && (controllerWork.mInput & TMarioControllerWork::R) != 0) 532 && mSprayState == TNozzleTrigger::SPRAY_STATE_INACTIVE) { 533 mSprayState = TNozzleTrigger::SPRAY_STATE_ACTIVE; ⋮ 540 mSprayTimer = mTriggerTime.get();
src/Player/WaterGun.cpp lines 529–540 at d2cc8f5 · upstream ↗ 545 if (!mFludd->getMario()->isUpperState(TMario::UPPER_STATE_PUMPING)) 546 canSpray = false; ⋮ 548 if (mFludd->getMario()->checkFlag(MARIO_FLAG_IN_ANY_WATER) == true 549 && mFludd->getCurrentWater() < mAmountMax.get()) 550 canSpray = false; ⋮ 552 if (canSpray == true) { 553 mInsidePressure += 150.0f * controllerWork.mAnalogR;
src/Player/WaterGun.cpp lines 545–553 at d2cc8f5 · upstream ↗ 570 mInsidePressure -= mInsidePressureDec.get(); ⋮ 575 if (mInsidePressure > mInsidePressureMax.get()) { 576 mInsidePressure = mInsidePressureMax.get(); 577 if (!mRumbleOnCharge 578 && mSprayState == TNozzleTrigger::SPRAY_STATE_INACTIVE) { 579 mSprayState = TNozzleTrigger::SPRAY_STATE_ACTIVE; 580 mSprayTimer = mTriggerTime.get();
src/Player/WaterGun.cpp lines 570–580 at d2cc8f5 · upstream ↗ 595 if (mSprayState == TNozzleTrigger::SPRAY_STATE_DEAD) { 596 mInsidePressure = 0.0f; 597 if (controllerWork.mAnalogR == 0.0f) { 598 mSprayState = TNozzleTrigger::SPRAY_STATE_INACTIVE;
src/Player/WaterGun.cpp lines 595–598 at d2cc8f5 · upstream ↗ 605void TNozzleTrigger::emit(int param_1) ⋮ 607 if (mFludd->mCurrentWater > 0 608 && (u8)mSprayState == TNozzleTrigger::SPRAY_STATE_ACTIVE) { ⋮ 617 f32 pressure = triggerFill / insidePressureMax; ⋮ 619 mEmitNum += pressure * (emitNum - emitNumMin) + emitNumMin; ⋮ 646 u8 emittedWater = gpModelWaterManager->emitRequest(*emitInfo); ⋮ 649 if (emittedWater != 0) { 650 mFludd->depleteWater(emittedWater * mDecRate.get());
src/Player/WaterGun.cpp lines 605–650 at d2cc8f5 · upstream ↗ 1779 for (int i = 0; i < nozzleBmdData.getEmitterCount(currentNozzleType); ++i) 1780 currentNozzle->emit(i);
src/Player/WaterGun.cpp lines 1779–1780 at d2cc8f5 · upstream ↗ 13const f32 D_80364A10 = 280.0f; 14const f32 D_80364A14 = -1100.0f; 15const f32 D_80364A18 = -399.9f;
src/core2/bs/bFlap.c lines 13–15 at e8d31fa · upstream ↗ 126 baphysics_set_vertical_velocity(D_80364A10); 127 baphysics_set_gravity(D_80364A14); 128 baphysics_set_terminal_velocity(D_80364A18);
src/core2/bs/bFlap.c lines 126–128 at e8d31fa · upstream ↗ 180 batimer_decrement(0); 181 if(batimer_isZero(0))
src/core2/bs/bFlap.c lines 180–181 at e8d31fa · upstream ↗ 33 baphysics_set_vertical_velocity(0.0f); 34 baphysics_set_gravity(D_80364A14); 35 modelAppendages_setKazooiesUpperHalfVisibility(TRUE); 36 baflag_set(BA_FLAG_12_HAS_FLAPPED); 37 batimer_set(0, 2.5f);
src/core2/bs/bFlap.c lines 33–37 at e8d31fa · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-22.
Revised after a check by a second AI model, 2026-10-22.
- ^With the Feathery Flap learned, A pressed during a jump or a fall starts it, once per time in the air: the flag it sets clears only on stable ground, while climbing, or in water, and a peck in the air blocks it. It stops Banjo's rise (vertical speed 0, gravity −1100 units/s²); when the opening animation reaches 0.9 of its length, it sets him rising at 280 units/s with his fall capped at −399.9, against the usual cap of −4000. A let go in the first 0.67 s of the flap, counted from its start through the opening animation by
D_8037D308(the atlas's reading of that unnamed variable as elapsed time), does nothing; after that, A up restores the usual gravity and cap. A 2.5-second timer ends the flap in any case. OnceD_8037D301, which counts the frames the flap animation passes 0.9 of its length from the lift on (the atlas reads it as a loop count), reaches 4, holding A scales the target horizontal speed to 0.35 of what the stick gives. Super Mario 64'sact_jumpchecks B and Z but not A; its second jump comes from a press during a landing (sm64/double-jump).Codebanjo.decomp/banjo-kazooie · n64decomp/sm64revisedHow we know
40int bainput_should_feathery_flap(void){ 41 return bakey_pressed(BUTTON_A) && can_feathery_flap();
src/core2/bainput.c lines 40–42 at e8d31fa · upstream ↗ 53bool can_feathery_flap(void){ 54 return baflag_isFalse(BA_FLAG_12_HAS_FLAPPED) 55 && baflag_isFalse(BA_FLAG_5_HAS_PECKED) 56 && ability_hasLearned(ABILITY_7_FEATHERY_FLAP);
src/core2/playerutils.c lines 53–56 at e8d31fa · upstream ↗ 187 if(player_isStable() || bsclimb_inSet(bs_getState())){ 188 baflag_clear(BA_FLAG_5_HAS_PECKED); 189 baflag_clear(BA_FLAG_12_HAS_FLAPPED); ⋮ 192 if(player_inWater()){ 193 baflag_clear(BA_FLAG_5_HAS_PECKED); 194 baflag_clear(BA_FLAG_12_HAS_FLAPPED);
src/core2/code_D9B0.c lines 187–195 at e8d31fa · upstream ↗ 146 if(bainput_should_feathery_flap()) 147 sp34 = BS_10_BFLAP;
src/core2/bs/jump.c lines 146–147 at e8d31fa · upstream ↗ 225 if(bainput_should_feathery_flap() && baflag_isFalse(BA_FLAG_5_HAS_PECKED)) 226 sp2C = BS_10_BFLAP;
src/core2/bs/jump.c lines 225–226 at e8d31fa · upstream ↗ 13const f32 D_80364A10 = 280.0f; 14const f32 D_80364A14 = -1100.0f; 15const f32 D_80364A18 = -399.9f;
src/core2/bs/bFlap.c lines 13–15 at e8d31fa · upstream ↗ 33 baphysics_set_vertical_velocity(0.0f); 34 baphysics_set_gravity(D_80364A14); 35 modelAppendages_setKazooiesUpperHalfVisibility(TRUE); 36 baflag_set(BA_FLAG_12_HAS_FLAPPED); 37 batimer_set(0, 2.5f);
src/core2/bs/bFlap.c lines 33–37 at e8d31fa · upstream ↗ 55void func_802A2810(void) { ⋮ 58 if (anctrl_isAt(plyrMvPtr, 0.9f)) { 59 D_8037D301++;
src/core2/bs/bFlap.c lines 55–60 at e8d31fa · upstream ↗ 86void func_802A2900(void){ 87 D_8037D304 = D_8037D308; 88 D_8037D308 += time_getDelta();
src/core2/bs/bFlap.c lines 86–89 at e8d31fa · upstream ↗ 113 func_802B6FA8(); 114 switch(D_8037D300){ 115 case 0: ⋮ 119 if(anctrl_isAt(sp18, 0.9f)){ ⋮ 126 baphysics_set_vertical_velocity(D_80364A10); 127 baphysics_set_gravity(D_80364A14); 128 baphysics_set_terminal_velocity(D_80364A18); 129 D_8037D300 = 1; ⋮ 138 if(0.67 <= D_8037D308){ 139 D_8037D300 = 2; ⋮ 148 if(D_8037D301 == 4) { 149 D_8037D300 = 3; ⋮
src/core2/bs/bFlap.c lines 113–182 at e8d31fa · upstream ↗ 10f32 baphysics_default_gravity = -2700.0f; //defaultGravity 11f32 baphysics_default_terminal_velocity = -4000.0f;
src/core2/ba/physics.c lines 10–11 at e8d31fa · upstream ↗ 446s32 act_jump(struct MarioState *m) { 447 if (check_kick_or_dive_in_air(m)) { ⋮ 451 if (m->input & INPUT_Z_PRESSED) { 452 return set_mario_action(m, ACT_GROUND_POUND, 0);
src/game/mario_actions_airborne.c lines 446–453 at 9921382 · upstream ↗ 108s32 check_kick_or_dive_in_air(struct MarioState *m) { 109 if (m->input & INPUT_B_PRESSED) { 110 return set_mario_action(m, m->forwardVel > 28.0f ? ACT_DIVE : ACT_JUMP_KICK, 0);
src/game/mario_actions_airborne.c lines 108–110 at 9921382 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-22.
Revised after a check by a second AI model, 2026-10-22.
Sources
Code
- doldecomp. 2026. “sms: a decompilation of Super Mario Sunshine (doldecomp/sms).” Commit d2cc8f5. doldecomp/sms @ d2cc8f5 · licence, all uses
- n64decomp. 2023. “sm64: a decompilation of Super Mario 64 (n64decomp/sm64).” Commit 9921382. n64decomp/sm64 @ 9921382 · licence, all uses
- banjo.decomp. 2026. “banjo-kazooie: a decompilation of Banjo-Kazooie (banjo.decomp/banjo-kazooie).” Commit e8d31fa. banjo.decomp/banjo-kazooie @ e8d31fa · licence, all uses