Claim set · data/claims/sa2.yaml

Sonic Advance 2 (the SAT-R decompilation, SAT-R/sa2)

Game: Sonic Advance 2.

In Sonic Advance 2 the ground acceleration rises with the ring count. At the start of each frame Player_HandleBoostThreshold picks a bracket from the rings and Player_ApplyBoostPhysics sets the acceleration from playerBoostPhysicsTable: 8 subpixels (1/256 px/frame) a frame with 10 rings or fewer, 12 with 11 to 49, 14 with 50 to 99, 16 with 100 to 149, and 18 with 150 or more. The deceleration, taken when the d-pad is held against the motion, is 64 in every bracket, and the speed-up item doubles both. With neither Left nor Right held, or while unk2A counts down (a 30-frame timer set when the player slips off a steep slope, in the atlas's reading), Player_HandleGroundMovement takes a fixed 8 a frame off the speed toward 0. Sonic 1's rates are $C (12) to accelerate, the same $C for the friction, and $80 (128) against the motion. sa2/ground-rates

About: Sonic Advance 2 Running

Value
{
 "accel": [
  8,
  12,
  14,
  16,
  18
 ],
 "decel": 64,
 "friction": 8
}
Codesa2: a decompilation of Sonic Advance 1 and 2 (SAT-R/sa2) · src/game/shared/stage/player.c#L4899-L4902#if (GAME == GAME_SA2) Player_HandleBoostThreshold(p); Player_HandleBoostState(p); Player_ApplyBoostPhysics(p);sa2: a decompilation of Sonic Advance 1 and 2 (SAT-R/sa2) · src/game/shared/stage/player.c#L9107-L9120void Player_HandleBoostThreshold(Player *p) { if (gRingCount >= 150) { p->boostThreshold = 4; } else if (gRingCount >= 100) { p->boostThreshold = 3; } else if (gRingCount >= 50) { p->boostThreshold = 2; } else if (gRingCount > 10) { p->boostThreshold = 1; } else { p->boostThreshold = 0;sa2: a decompilation of Sonic Advance 1 and 2 (SAT-R/sa2) · src/game/shared/stage/player.c#L552-L553static const s16 playerBoostPhysicsTable[5][2] = { { 8, 64 }, { 12, 64 }, { 14, 64 }, { 16, 64 }, { 18, 64 },sa2: a decompilation of Sonic Advance 1 and 2 (SAT-R/sa2) · src/game/shared/stage/player.c#L9926-L9927p->acceleration = playerBoostPhysicsTable[boostThreshold][0]; p->deceleration = playerBoostPhysicsTable[boostThreshold][1];sa2: a decompilation of Sonic Advance 1 and 2 (SAT-R/sa2) · src/game/shared/stage/player.c#L4907-L4910 — Player_HandleWater runs after Player_ApplyBoostPhysicsPlayer_HandleInputs(p); … Player_HandleWater(p);sa2: a decompilation of Sonic Advance 1 and 2 (SAT-R/sa2) · src/game/shared/stage/player.c#L4429-L4437if (p->itemEffect & PLAYER_ITEM_EFFECT__SPEED_UP) { … #else p->acceleration = p->acceleration * 2; p->deceleration = p->deceleration * 2;sa2: a decompilation of Sonic Advance 1 and 2 (SAT-R/sa2) · src/game/shared/stage/player.c#L6652-L6658 — Player_Idle, the decompilers' name for the ground state that walking and running useif (!Player_TryTaunt(p) && !Player_TryCrouchOrSpinAttack(p) && !Player_TryJump(p) … Player_HandleGroundMovement(p);sa2: a decompilation of Sonic Advance 1 and 2 (SAT-R/sa2) · src/game/shared/stage/player.c#L8965-L8975void Player_HandleGroundMovement(Player *p) { s8 charState = CHARSTATE_INVALID; s32 acc = p->acceleration; s32 dec = p->deceleration; if ((p->SA2_LABEL(unk2A) == 0) && p->heldInput & (DPAD_LEFT | DPAD_RIGHT)) { if (p->qSpeedGround > 0) { if (p->heldInput & DPAD_RIGHT) { if (p->qSpeedGround < p->topSpeed) { p->qSpeedGround += acc;sa2: a decompilation of Sonic Advance 1 and 2 (SAT-R/sa2) · src/game/shared/stage/player.c#L8984-L8994} else if (p->qSpeedGround >= Q(2.0)) { … p->qSpeedGround -= dec;sa2: a decompilation of Sonic Advance 1 and 2 (SAT-R/sa2) · src/game/shared/stage/player.c#L9063-L9074} else { s32 grndSpeed = p->qSpeedGround; if (grndSpeed > 0) { s16 val = grndSpeed - Q(8.0 / 256.0); if (val <= 0) { val = 0;sa2: a decompilation of Sonic Advance 1 and 2 (SAT-R/sa2) · src/game/shared/stage/player.c#L118-L127#define PLAYERFN_UPDATE_UNK2A(player) \ … if (player->SA2_LABEL(unk2A) != 0) { \ player->SA2_LABEL(unk2A) -= 1; \ } else if ((player->rotation + Q(0.125)) & 0xC0) { \ if (ABS(player->qSpeedGround) < Q(1.875)) { \ … player->SA2_LABEL(unk2A) = GBA_FRAMES_PER_SECOND / 2; \s1disasm: Sonic the Hedgehog (16-bit) disassembly (sonicretro/s1disasm) · _incObj/01 Sonic.asm#L5-L7son_maxspeed: equ $600 ; Sonic's max speed son_acceleration: equ $C ; Sonic's acceleration son_deceleration: equ $80 ; Sonic's decelerations1disasm: Sonic the Hedgehog (16-bit) disassembly (sonicretro/s1disasm) · _incObj/01 Sonic.asm#L499-L506Sonic_CheckDpadLetGo: … andi.b #btnL+btnR,d0 ; is left or right held? … sub.w d5,d0 ; reduce current rightward speed by accelerationChecked by claude, 2026-10-22 — read Task_PlayerMain, Player_HandleBoostThreshold, Player_ApplyBoostPhysics, Player_HandleWater's speed-up branch, Player_Idle, and Player_HandleGroundMovement at the pinned commit, with their #if branches; and Sonic 1's equates and Sonic_CheckDpadLetGo

The left-moving branches mirror the right (L9009-L9019, L9075-L9084). The table's speeds are in the same 1/256 px units as Sonic 1's subpixels (Q(n) is n × 256). d5 in Sonic 1's friction holds the acceleration (s1disasm/ground-friction). The ring count is gRingCount; the atlas hasn't read how multiplayer modes count rings.

Cited by: Running, Sonic Advance 2

Outside boost mode Player_ApplyBoostPhysics sets topSpeed, the speed holding forward builds to in Player_HandleGroundMovement, to 6 px/frame (Q(6.0)), Sonic 1's son_maxspeed ($600). A faster speed, from a slope, is cut only at maxSpeed, by sub_8023260, which the ground state (Player_Idle) calls after adding the slope term: 9 px/frame, or 15 while MOVESTATE_SPIN_ATTACK is set, as a spin dash sets it. sa2/top-speed

About: Sonic Advance 2 Running

Value
{
 "top": 1536,
 "cap": 2304,
 "capSpinAttack": 3840
}
Codesa2: a decompilation of Sonic Advance 1 and 2 (SAT-R/sa2) · src/game/shared/stage/player.c#L9912-L9924void Player_ApplyBoostPhysics(Player *p) { u32 boostThreshold = p->boostThreshold; if (p->isBoosting) { p->topSpeed = Q(12.0); p->maxSpeed = Q(15.0); } else if (p->moveState & MOVESTATE_SPIN_ATTACK) { p->topSpeed = Q(6.0); p->maxSpeed = Q(15.0); } else { p->topSpeed = Q(6.0); p->maxSpeed = Q(9.0);sa2: a decompilation of Sonic Advance 1 and 2 (SAT-R/sa2) · src/game/shared/stage/player.c#L8974-L8979if (p->qSpeedGround < p->topSpeed) { p->qSpeedGround += acc; if (p->qSpeedGround > p->topSpeed) { p->qSpeedGround = p->topSpeed;sa2: a decompilation of Sonic Advance 1 and 2 (SAT-R/sa2) · src/game/shared/stage/player.c#L6658-L6669Player_HandleGroundMovement(p); if (((p->rotation + Q(0.375)) & 0xFF) < 0xC0) { s32 acceleration = GET_ROTATED_ACCEL(p->rotation); … p->qSpeedGround += acceleration; … SA2_LABEL(sub_8023260)(p);sa2: a decompilation of Sonic Advance 1 and 2 (SAT-R/sa2) · src/game/shared/stage/player.c#L3383-L3388void SA2_LABEL(sub_8023260)(Player *p) { s32 maxSpeed = p->maxSpeed; if (p->qSpeedGround > (s16)maxSpeed) { p->qSpeedGround = +maxSpeed;sa2: a decompilation of Sonic Advance 1 and 2 (SAT-R/sa2) · src/game/shared/stage/player.c#L7190p->moveState |= (MOVESTATE_SPINDASH | MOVESTATE_SPIN_ATTACK);s1disasm: Sonic the Hedgehog (16-bit) disassembly (sonicretro/s1disasm) · _incObj/01 Sonic.asm#L5son_maxspeed: equ $600 ; Sonic's max speedChecked by claude, 2026-10-22 — read Player_ApplyBoostPhysics, the forward branch of Player_HandleGroundMovement, Player_Idle, and sub_8023260 at the pinned commit; and Sonic 1's equate

Q(n) is n × 256 (include/global.h), so 6, 9, and 15 px/frame are 1536, 2304, and 3840. The slope term is GET_ROTATED_ACCEL, the sine of the angle × 3 / 32.

Cited by: Running, Sonic Advance 2

Boost mode starts from a run held at the top speed. Holding forward on the ground at 6 px/frame or faster, Player_HandleGroundMovement leaves the speed as it is and adds the frame's acceleration to boostSpeed, in the branch where Sonic 2's Sonic_MoveRight keeps a speed above the top and Sonic 1's cuts it back to the top. At the start of each frame Player_HandleBoostState turns boost mode on once boostSpeed reaches the ring bracket's entry in playerBoostThresholdTable: Q(8) (2048) with 10 rings or fewer, 2040 with 11 to 49, 1680 with 50 to 99, 1440 with 100 to 149, and 1080 with 150 or more. In the air or below the top speed it sets boostSpeed back to 0. With each bracket's acceleration that is 256, 170, 120, 90, and 60 frames of holding forward at the top speed, and half as many with the speed-up item (computed). sa2/boost-trigger

About: Sonic Advance 2 Running

Value
{
 "threshold": [
  2048,
  2040,
  1680,
  1440,
  1080
 ],
 "frames": [
  256,
  170,
  120,
  90,
  60
 ]
}
Codesa2: a decompilation of Sonic Advance 1 and 2 (SAT-R/sa2) · src/game/shared/stage/player.c#L8972-L8982if (p->qSpeedGround > 0) { if (p->heldInput & DPAD_RIGHT) { if (p->qSpeedGround < p->topSpeed) { … } else { p->boostSpeed += acc; }sa2: a decompilation of Sonic Advance 1 and 2 (SAT-R/sa2) · src/game/shared/stage/player.c#L9009-L9019} else if (p->qSpeedGround < 0) { if (p->heldInput & DPAD_LEFT) { if (p->qSpeedGround > -p->topSpeed) { … } else { p->boostSpeed += acc; }sa2: a decompilation of Sonic Advance 1 and 2 (SAT-R/sa2) · src/game/shared/stage/player.c#L556static const s16 playerBoostThresholdTable[5] = { Q(8), Q(7.96875), Q(6.5625), Q(5.625), Q(4.21875) };sa2: a decompilation of Sonic Advance 1 and 2 (SAT-R/sa2) · src/game/shared/stage/player.c#L4899-L4902#if (GAME == GAME_SA2) Player_HandleBoostThreshold(p); Player_HandleBoostState(p); Player_ApplyBoostPhysics(p);sa2: a decompilation of Sonic Advance 1 and 2 (SAT-R/sa2) · src/game/shared/stage/player.c#L9122-L9150void Player_HandleBoostState(Player *p) { bool32 isBoostActive = p->isBoosting; if (isBoostActive) { … } else { if ((!(p->moveState & MOVESTATE_IN_AIR)) && ((ABS(p->qSpeedGround) >= p->topSpeed))) { if (p->boostSpeed >= playerBoostThresholdTable[p->boostThreshold]) { p->isBoosting = TRUE; … } else { #ifndef NON_MATCHING …sa2: a decompilation of Sonic Advance 1 and 2 (SAT-R/sa2) · src/game/shared/stage/player.c#L552-L553static const s16 playerBoostPhysicsTable[5][2] = { { 8, 64 }, { 12, 64 }, { 14, 64 }, { 16, 64 }, { 18, 64 },sa2: a decompilation of Sonic Advance 1 and 2 (SAT-R/sa2) · src/game/shared/stage/player.c#L4429-L4437if (p->itemEffect & PLAYER_ITEM_EFFECT__SPEED_UP) { … p->acceleration = p->acceleration * 2;s2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L36998-L37004add.w d5,d0 ; add acceleration to the right cmp.w d6,d0 ; compare new speed with top speed blt.s + ; if new speed is less than the maximum, branch sub.w d5,d0 ; remove this frame's acceleration change cmp.w d6,d0 ; compare speed with top speed bge.s + ; if speed was already greater than the maximum, branch move.w d6,d0 ; limit speed on ground going rights1disasm: Sonic the Hedgehog (16-bit) disassembly (sonicretro/s1disasm) · _incObj/01 Sonic.asm#L716-L719add.w d5,d0 ; add acceleration to current ground speed cmp.w d6,d0 ; is new speed above max speed? blt.s .nocap ; if not, branch move.w d6,d0 ; cap Sonic's ground speedChecked by claude, 2026-10-22 — read Player_HandleGroundMovement's forward branches, Player_HandleBoostState, both tables, Task_PlayerMain's call order, and the speed-up branch at the pinned commit; and Sonic_MoveRight in Sonic 1 and Sonic 2

The reset writes isBoostActive, which is false on that branch, in the matching build (NON_MATCHING is the ports' build and writes 0). The frame counts are 2048 / 8, 2040 / 12, 1680 / 14, 1440 / 16, and 1080 / 18, all whole. They count frames at the top speed: the frame the run reaches it adds nothing, and boost mode starts at the beginning of the frame after the last addition. A run slowed below the top speed by an uphill slope loses the count.

Cited by: Sonic Advance 2

In boost mode Player_ApplyBoostPhysics raises topSpeed, the speed holding forward builds to, from 6 to 12 px/frame, twice Sonic 1's single top speed (son_maxspeed, $600; computed), and maxSpeed, the ground cap, to 15; the acceleration still follows the ring count. Player_HandleBoostState looks for the end of boost mode only on the ground, where a ground speed below 4.5 px/frame ends it. Among the other ends are a hit (Player_InitHurt), the start of Tails's or Cream's flight, a multiplayer finish, the special stage transition, and InitializePlayer. sa2/boost-mode

About: Sonic Advance 2 Running

Value
{
 "top": 3072,
 "cap": 3840,
 "end": 1152
}
Codesa2: a decompilation of Sonic Advance 1 and 2 (SAT-R/sa2) · src/game/shared/stage/player.c#L9912-L9927void Player_ApplyBoostPhysics(Player *p) { u32 boostThreshold = p->boostThreshold; if (p->isBoosting) { p->topSpeed = Q(12.0); p->maxSpeed = Q(15.0); … p->acceleration = playerBoostPhysicsTable[boostThreshold][0];sa2: a decompilation of Sonic Advance 1 and 2 (SAT-R/sa2) · src/game/shared/stage/player.c#L9122-L9133void Player_HandleBoostState(Player *p) { bool32 isBoostActive = p->isBoosting; if (isBoostActive) { if (!(p->moveState & MOVESTATE_IN_AIR)) { p->boostSpeed = playerBoostThresholdTable[p->boostThreshold]; if (ABS(p->qSpeedGround) < Q(4.5)) { p->isBoosting = FALSE; p->boostSpeed = 0;sa2: a decompilation of Sonic Advance 1 and 2 (SAT-R/sa2) · src/game/shared/stage/player.c#L7856-L7859void Player_InitHurt(Player *p) { p->timerInvulnerability = 0x78; p->isBoosting = FALSE;sa2: a decompilation of Sonic Advance 1 and 2 (SAT-R/sa2) · src/game/sa2/stage/player_callbacks.c#L893-L905void Player_Tails_InitFlying(Player *p) … p->isBoosting = FALSE;sa2: a decompilation of Sonic Advance 1 and 2 (SAT-R/sa2) · src/game/sa2/stage/player_callbacks.c#L632-L644void Player_Cream_InitFlying(Player *p) … p->isBoosting = FALSE;sa2: a decompilation of Sonic Advance 1 and 2 (SAT-R/sa2) · src/game/shared/stage/player.c#L8193-L8230void Player_HandleMultiplayerFinish(Player *p) … p->isBoosting = FALSE;sa2: a decompilation of Sonic Advance 1 and 2 (SAT-R/sa2) · src/game/shared/stage/player.c#L10050-L10057void Player_InitSpecialStageTransition(Player *p) … p->isBoosting = FALSE;sa2: a decompilation of Sonic Advance 1 and 2 (SAT-R/sa2) · src/game/shared/stage/player.c#L976-L1063void InitializePlayer(Player *p) … #if (GAME == GAME_SA2) p->transition = 0; p->unk6E = 0; p->prevTransition = 0; p->isBoosting = FALSE;s1disasm: Sonic the Hedgehog (16-bit) disassembly (sonicretro/s1disasm) · _incObj/01 Sonic.asm#L5son_maxspeed: equ $600 ; Sonic's max speedChecked by claude, 2026-10-22 — read Player_ApplyBoostPhysics, Player_HandleBoostState, Player_InitHurt, and the Tails and Cream flight starts, and the other isBoosting = FALSE writes in player.c at the pinned commit; and Sonic 1's equate

The list of ends isn't complete: player_callbacks.c also clears isBoosting in Player_SonicAmy_InitStopNSlam (L286) and Player_Knuckles_InitGlide (L1811), and player_super_sonic.c in SuperSonicInitPlayer (L97); the atlas hasn't read those paths' conditions. Boost mode also starts boost particles and a sound (CreateBoostModeParticles, SE_221, L9137-L9141), and some objects and Sonic's and Amy's skid attack test isBoosting; the atlas hasn't read those. A ground speed between 4.5 and 12 px/frame keeps boost mode, so a boosted run can slow on a slope or a brake without losing it until it drops below 4.5.

Cited by: Running, Sonic Advance 2

Player_InitJump, which doesn't test the character, adds PLAYER_JUMP_HEIGHT, 4.875 px/frame (2.625 in water), to the speed along the ground's normal (rotation - 64). Sonic 1's Sonic_Jump adds son_jumpspeed, $680 (6.5 px/frame), along the normal the same way, so Sonic Advance 2's take-off is three quarters of it (computed). sa2/jump-speed

About: Sonic Advance 2 Jump

Value: 1248 1/256 px/frame

Codesa2: a decompilation of Sonic Advance 1 and 2 (SAT-R/sa2) · include/game/shared/parameters/characters.h#L23-L24#define PLAYER_JUMP_HEIGHT (4.875) #define PLAYER_JUMP_HEIGHT_UNDER_WATER (2.625)sa2: a decompilation of Sonic Advance 1 and 2 (SAT-R/sa2) · src/game/shared/stage/player.c#L6939-L7004void Player_InitJump(Player *p) … jumpHeight = (p->moveState & MOVESTATE_IN_WATER) ? Q(PLAYER_JUMP_HEIGHT_UNDER_WATER) : Q(PLAYER_JUMP_HEIGHT); … rot = p->rotation - 64; accelX = I(COS_24_8(rot * 4) * jumpHeight); p->qSpeedAirX += accelX; accelY = I(SIN_24_8(rot * 4) * jumpHeight); p->qSpeedAirY += accelY;s1disasm: Sonic the Hedgehog (16-bit) disassembly (sonicretro/s1disasm) · _incObj/01 Sonic.asm#L8son_jumpspeed: equ $680 ; Sonic's jump speeds1disasm: Sonic the Hedgehog (16-bit) disassembly (sonicretro/s1disasm) · _incObj/01 Sonic.asm#L1218move.w #son_jumpspeed,d2 ; set initial jump forces1disasm: Sonic the Hedgehog (16-bit) disassembly (sonicretro/s1disasm) · _incObj/01 Sonic.asm#L1224-L1233subi.b #$40,d0 ; rotate it perpendicularly for jump trajectory jsr (CalcSine).l ; find the direction Sonic should jumpChecked by claude, 2026-10-22 — read Player_InitJump whole and characters.h at the pinned commit; and Sonic 1's equate and Sonic_Jump

4.875 × 256 = 1248, and 1248 / 1664 = 0.75 exactly. rotation is a byte angle, 64 to a quarter turn, as Sonic 1's $40 is. Player_InitJump adds to the speed the player already has, and on a standing object in a boss stage it also takes off the camera's motion (L6993-L6997).

Cited by: Jump, Sonic Advance 2

Each frame of a jump, button held or not, Player_Jumping adds PLAYER_GRAVITY, 42/256 px/frame² (12/256 in water), to the vertical speed through PLAYERFN_UPDATE_AIR_FALL_SPEED, except on a frame where Sonic's forward thrust or a mid-air move starts (Player_Sonic_TryForwardThrust, Player_TryMidAirAction): Player_Jumping returns before the gravity then. That is three quarters of the gravity Sonic 1's airborne-rolling mode, Sonic_MdJump2, adds through ObjectFall, $38 (56/256) a frame (computed). sa2/gravity

About: Sonic Advance 2 Gravity

Value: 42 1/256 px/frame²

Codesa2: a decompilation of Sonic Advance 1 and 2 (SAT-R/sa2) · include/game/shared/parameters/characters.h#L18-L19#define PLAYER_GRAVITY (42.0 / 256.0) #define PLAYER_GRAVITY_UNDER_WATER (12.0 / 256.0)sa2: a decompilation of Sonic Advance 1 and 2 (SAT-R/sa2) · src/game/shared/stage/player.c#L200-L207#elif (GAME == GAME_SA2) #ifndef COLLECT_RINGS_ROM #define PLAYERFN_UPDATE_AIR_FALL_SPEED(player) \ if (player->moveState & MOVESTATE_IN_WATER) { \ player->qSpeedAirY += Q(PLAYER_GRAVITY_UNDER_WATER); \ } else { \ player->qSpeedAirY += Q(PLAYER_GRAVITY); \ }sa2: a decompilation of Sonic Advance 1 and 2 (SAT-R/sa2) · src/game/shared/stage/player.c#L7017-L7021if (p->moveState & MOVESTATE_100) { #ifndef COLLECT_RINGS_ROM if (gGameMode != GAME_MODE_MULTI_PLAYER_COLLECT_RINGS) if (Player_Sonic_TryForwardThrust(p) || Player_TryMidAirAction(p)) return;sa2: a decompilation of Sonic Advance 1 and 2 (SAT-R/sa2) · src/game/shared/stage/player.c#L7039-L7042SA2_LABEL(sub_80232D0)(p); PLAYERFN_UPDATE_AIR_FALL_SPEED(p); PLAYERFN_UPDATE_POSITION(p);s1disasm: Sonic the Hedgehog (16-bit) disassembly (sonicretro/s1disasm) · _incObj/sub ObjectFall & SpeedToPos.asm#L5gravity: equ $38 ; gravity constant used by many objectss1disasm: Sonic the Hedgehog (16-bit) disassembly (sonicretro/s1disasm) · _incObj/01 Sonic.asm#L359-L363Sonic_MdJump2: ; While Sonic is in the air and rolling (usually, but not limited to, jumping) … jsr (ObjectFall).l ; apply gravity and update Sonic's position based on his current velocitiesChecked by claude, 2026-10-22 — read characters.h, the SA2 branch of the gravity macro, and Player_Jumping (its early return included) at the pinned commit; and Sonic 1's gravity equate and Sonic_MdJump2

42 / 56 = 0.75 exactly. The decompilation has a second macro, PLAYERFN_UPDATE_AIR_FALL_SPEED_B, that halves the gravity while the player rises; Player_DefaultTrick and two other states use it, the jump doesn't.

Cited by: Gravity, Sonic Advance 2

In a jump (MOVESTATE_100, which Player_InitJump sets), on any frame the jump button isn't held, Player_Jumping cuts a rise faster than 3 px/frame to exactly −3 (PLAYER_MAX_NOT_HELD_JUMP_FORCE; −1.5 in water). Outside the Collect Rings multiplayer mode it first tries Sonic's forward thrust and the mid-air moves (Player_Sonic_TryForwardThrust, Player_TryMidAirAction), and a frame on which one starts skips the cap. Sonic 1's Sonic_JumpHeight cuts to 4 px/frame (-$400), so this cap too is three quarters of Sonic 1's (computed). sa2/release-cap

About: Sonic Advance 2 Variable jump height

Value: -768 1/256 px/frame

Codesa2: a decompilation of Sonic Advance 1 and 2 (SAT-R/sa2) · include/game/shared/parameters/characters.h#L28-L29#define PLAYER_MAX_NOT_HELD_JUMP_FORCE (3.0) #define PLAYER_MAX_NOT_HELD_JUMP_FORCE_UNDER_WATER (1.5)sa2: a decompilation of Sonic Advance 1 and 2 (SAT-R/sa2) · src/game/shared/stage/player.c#L6965p->moveState |= (MOVESTATE_100 | MOVESTATE_IN_AIR);sa2: a decompilation of Sonic Advance 1 and 2 (SAT-R/sa2) · src/game/shared/stage/player.c#L7006-L7027void Player_Jumping(Player *p) { s16 maxJumpSpeed = -Q(PLAYER_MAX_NOT_HELD_JUMP_FORCE); … if (p->moveState & MOVESTATE_IN_WATER) { maxJumpSpeed = -Q(PLAYER_MAX_NOT_HELD_JUMP_FORCE_UNDER_WATER); } … if (p->moveState & MOVESTATE_100) { … if (gGameMode != GAME_MODE_MULTI_PLAYER_COLLECT_RINGS) …s1disasm: Sonic the Hedgehog (16-bit) disassembly (sonicretro/s1disasm) · _incObj/01 Sonic.asm#L1273-L1288Sonic_JumpHeight: … move.w #-$400,d1 ; set max jump height … cmp.w obVelY(a0),d1 ; get current y speed ble.s .return ; is Sonic moving up slower than the cap speed? if yes, branch move.b (v_jpadhold2).w,d0 ; get currently held buttons andi.b #btnABC,d0 ; is A, B or C still held after jumping? bne.s .return ; if yes, branch move.w d1,obVelY(a0) ; otherwise, cap Sonic's maximum jump heightChecked by claude, 2026-10-22 — read characters.h, Player_InitJump, and Player_Jumping at the pinned commit; and Sonic 1's Sonic_JumpHeight

−768 / −1024 = 0.75 exactly. As in Sonic 1 (s1disasm/release-order), the cap comes before the frame's gravity (L7041). The comment on the cap is the decompilers'.

Cited by: Sonic Advance 2, Variable jump height

While the spin dash charges, each jump press adds Q(2.0) (512) to qSpindashAccel and then raises it to at least Q(8.0) (2048) with MAX, so one press is a full charge, the starting press included: Player_TryInitSpindash runs Player_InitSpindash and then Player_Spindash in the same frame. Each frame Down stays held first takes 1/32 off the charge. Releasing Down sets the ground speed from sSpinDashSpeeds at the charge's whole part, capped at 8: 6 px/frame plus 3/8 a step, up to 9, so a release on the frame after a press always gives 9 px/frame (computed). In Sonic 2 the starting press sets spindash_counter to 0, and each later press adds $200 up to a cap of $800, so a full charge takes five presses in all (computed); its release speeds run from 8 to 12 px/frame. sa2/spindash

About: Sonic Advance 2 Spin dash

Value
[
 1536,
 1632,
 1728,
 1824,
 1920,
 2016,
 2112,
 2208,
 2304
]
Codesa2: a decompilation of Sonic Advance 1 and 2 (SAT-R/sa2) · src/game/shared/stage/player.c#L9898-L9904bool32 Player_TryInitSpindash(Player *p) { u32 r6 = 0; if (p->charState == CHARSTATE_CROUCH) { if (p->frameInput & gPlayerControls.jump) { PLAYERFN_SET_AND_CALL(Player_InitSpindash, p);sa2: a decompilation of Sonic Advance 1 and 2 (SAT-R/sa2) · src/game/shared/stage/player.c#L7186-L7203void Player_InitSpindash(Player *p) … p->qSpindashAccel = 0; … PLAYERFN_SET_AND_CALL(Player_Spindash, p);sa2: a decompilation of Sonic Advance 1 and 2 (SAT-R/sa2) · src/game/shared/stage/player.c#L7212-L7225if (!(p->heldInput & DPAD_DOWN)) { … index = I(p->qSpindashAccel); if (index > 8) index = 8; speed = sSpinDashSpeeds[index]; … p->qSpeedGround = speed;sa2: a decompilation of Sonic Advance 1 and 2 (SAT-R/sa2) · src/game/shared/stage/player.c#L7230-L7249} else { s16 pitch = p->qSpindashAccel; s16 pitch2 = pitch; if (pitch2 != 0) { pitch = pitch2 - (I(pitch << 3)); … if (p->frameInput & gPlayerControls.jump) { … pitch += Q(2.0); pitch = MAX(pitch, Q(8.0));sa2: a decompilation of Sonic Advance 1 and 2 (SAT-R/sa2) · src/game/shared/stage/player.c#L558-L567static const s16 sSpinDashSpeeds[9] = { Q_8_8(6.000 + 0 * (3. / 8.)), // … Q_8_8(6.000 + 8 * (3. / 8.)), //s2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L37605-L37613move.b (Ctrl_1_Press_Logical).w,d0 andi.b #button_B_mask|button_C_mask|button_A_mask,d0 beq.w return_1AC8C … move.b #1,spindash_flag(a0) move.w #0,spindash_counter(a0)s2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L37726-L37729addi.w #$200,spindash_counter(a0) cmpi.w #$800,spindash_counter(a0) blo.s Obj01_Spindash_ResetScr move.w #$800,spindash_counter(a0)s2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L37688-L37697SpindashSpeeds: dc.w $800 ; 0 … dc.w $C00 ; 8Checked by claude, 2026-10-22 — read Player_TryInitSpindash (the main game's, not the Collect Rings copy at L8935), Player_InitSpindash, Player_Spindash, sSpinDashSpeeds, and MAX at the pinned commit; and Sonic 2's Sonic_CheckSpindash start, Sonic_ChargingSpindash, and SpindashSpeeds

MAX is ((a) > (b)) ? (a) : (b) (include/global.h), a floor where Sonic 2 has a ceiling; the decompilers call the charge pitch because it also sets the rev sound's pitch. I(pitch << 3) is pitch × 8 / 256, a 32nd. From 2048, one held frame without a press leaves 1984, whose whole part is 7 (8.625 px/frame). Player_Spindash is matching code, so the floor is the game's, not a decompilation slip.

Cited by: Sonic Advance 2