data/claims/mf.yamlMetroid Fusion (metroidret's decompilation of the US release)
Game: Metroid Fusion.
The mf decompilation's README names mf_us.gba, the North American release, as its default and only supported build and gives its SHA-1 as ca33f4348c2c05dd330d37b97e2c5a69531dfe87, which is the SHA-1 of the No-Intro file Metroid Fusion (USA).gba. The README counts 376 functions not yet decompiled. The atlas hasn't built it. mf/build
README.md#L53231/3607 functions decompiled (89.57%, 376 left)mf: a decompilation of Metroid Fusion (metroidret/mf) · README.md#L13-L17This repository can be used to build multiple ROMs. The ROMs are named like this: `mf_{region}.gba` (or `mf_{region}_debug.gba`)
…
- us: USA, North America `sha1: ca33f4348c2c05dd330d37b97e2c5a69531dfe87`
- eu: Europe **(Not yet supported)**mf: a decompilation of Metroid Fusion (metroidret/mf) · README.md#L26The default built ROM is mf_us.gbamf: a decompilation of Metroid Fusion (metroidret/mf) · mf_us.sha1#L1CA33F4348C2C05DD330D37B97E2C5A69531DFE87 mf_us.gbamf: a decompilation of Metroid Fusion (metroidret/mf) · Makefile#L4-L11REGION ?= us
…
ifeq ($(REGION),us)
TARGET = mf_us
…
CPPFLAGS += -DREGION_USChecked by claude-agent, 2026-10-22 — read the README, mf_us.sha1, and the Makefile's region block, and ran sha1sum on /data/roms/gba/Metroid Fusion (USA).gba, which gives the same hash. The undecompiled functions are the asm/disasm_*.s files (not fetched); none of the C files cited in mf claims carries a NONMATCHING markerThe README warns that the decomp "is not shiftable", so it rebuilds the US ROM byte for byte but isn't yet a base for modified builds.
Cited by: Metroid Fusion
When SamusInit runs with no file loading (gIsLoadingFile clear) and an unnamed flag, gUnk_3000be3, clear, it copies sEquipment_Empty into gEquipment: 99 energy of 99, 10 missiles of 10, 10 power bombs of 10, and no weapon flags (MBF_NONE). The atlas reads this as a new game's start. Zero Mission starts with the same 99 energy, but with every ammunition count at 0. mf/start-energy
About: Metroid Fusion Health upgrade
Value: 99 energy
Codemf: a decompilation of Metroid Fusion (metroidret/mf) ·src/samus.c#L7523-L7526if (gUnk_3000be3 || gIsLoadingFile)
return;
…
gEquipment = sEquipment_Empty;mf: a decompilation of Metroid Fusion (metroidret/mf) · src/data/samus/samus_animation_pointers.c#L47-L55const struct Equipment sEquipment_Empty = {
.currentEnergy = 99,
.maxEnergy = 99,
.currentMissiles = 10,
.maxMissiles = 10,
.currentPowerBombs = 10,
.maxPowerBombs = 10,
.beamStatus = BF_NONE,
.weaponsStatus = MBF_NONE,mf: a decompilation of Metroid Fusion (metroidret/mf) · include/constants/samus.h#L236-L239// --- Energy and ammo ---
#define STARTING_ENERGY 99
#define STARTING_MISSILES 10
#define STARTING_POWER_BOMBS 10mzm: a decompilation of Metroid: Zero Mission (metroidret/mzm) · src/samus.c#L8071-L8088if (!gIsLoadingFile)
{
// Zero out most of samus's data
DMA3_FILL_32(0, &gSamusData, sizeof(gSamusData));
DMA3_FILL_32(0, &gEquipment, sizeof(gEquipment));
…
gEquipment.maxEnergy = 99;
gEquipment.currentEnergy = 99;Checked by claude-agent, 2026-10-22 — read SamusInit to its end and sEquipment_Empty. gUnk_3000be3 is cleared when the main loop enters the in-game mode from another mode (agbmain.c) and set to 1 at the end of the in-game init (in_game.c line 499), so the copy happens only on the first init after entering play. Zero Mission's side is mzm/start-energyThe 10 missiles and 10 power bombs sit in the counters before Samus can use them: the data downloads OR each ability's weapon flag into weaponsStatus (src/event.c line 128), and the pause screen and ending count tanks from these starts, (maxMissiles - STARTING_MISSILES) / MISSILE_INCREASE_AMOUNT (src/menus/pause_screen_map.c lines 540-544).
Cited by: Health upgrade, Metroid Fusion
An energy tank adds 100 to Samus's maximum energy and sets her energy to the new maximum, unless the new maximum would pass MAX_ENERGY_AMOUNT, 20 tanks plus the starting 99, or 2099. The tank code reads no difficulty. Zero Mission's tank also adds 100 and refills, or 50 on Hard, and stops at 12 tanks: 1299 on Easy and Normal and 699 on Hard (computed). mf/energy-tank
About: Metroid Fusion Health upgrade
Value: 100 energy per tank
Codemf: a decompilation of Metroid Fusion (metroidret/mf) ·src/bg_clip.c#L267-L274else if (i == TANK_TYPE_ENERGY)
{
if (gEquipment.maxEnergy + ENERGY_INCREASE_AMOUNT <= MAX_ENERGY_AMOUNT)
{
gEquipment.maxEnergy += ENERGY_INCREASE_AMOUNT;
gEquipment.currentEnergy = gEquipment.maxEnergy;
}
}mf: a decompilation of Metroid Fusion (metroidret/mf) · include/constants/samus.h#L241-L247#ifndef REGION_JP
#define ENERGY_INCREASE_AMOUNT 100
…
// Default 2099
#define MAX_ENERGY_AMOUNT (20 * ENERGY_INCREASE_AMOUNT + STARTING_ENERGY)mzm: a decompilation of Metroid: Zero Mission (metroidret/mzm) · src/bg_clip.c#L531-L537else if (i == ITEM_TYPE_ENERGY)
{
if (sNumberOfTanksPerArea[MAX_AMOUNT_OF_AREAS - 1].energy * sTankIncreaseAmount[gDifficulty].energy + sStartingHealthAmmo.energy >= gEquipment.maxEnergy + sTankIncreaseAmount[gDifficulty].energy)
{
gEquipment.maxEnergy += sTankIncreaseAmount[gDifficulty].energy;
gEquipment.currentEnergy = gEquipment.maxEnergy;mzm: a decompilation of Metroid: Zero Mission (metroidret/mzm) · src/data/block_data.c#L52-L53[MAX_AMOUNT_OF_AREAS - 1] = {
.energy = 12,mzm: a decompilation of Metroid: Zero Mission (metroidret/mzm) · src/data/block_data.c#L60-L81const struct TankList sStartingHealthAmmo = {
.energy = 99,
…
const struct TankList sTankIncreaseAmount[DIFF_COUNT] = {
[DIFF_EASY] = {
.energy = 100,
…
[DIFF_NORMAL] = {
.energy = 100,
…
[DIFF_HARD] = {
.energy = 50,Checked by claude-agent, 2026-10-22 — read the tank branch of the block-touch code in bg_clip.c and the constants; the US build defines REGION_US (mf/build), so the #ifndef REGION_JP block applies. 20 x 100 + 99 = 2099; the maximum rises from 99 in steps of 100, so the 20th tank reaches 2099 exactly and a 21st fails. Zero Mission's side is mzm/energy-tankThe test caps tanks by arithmetic alone; how many energy tanks the map holds is in room data the atlas hasn't fetched. The HUD draws the maximum with four digits (src/hud.c).
Cited by: Health upgrade, Metroid Fusion
Samus has one missile count and one power bomb count, each with a maximum. A missile tank adds 5 to both the missile count and its maximum, unless the maximum would pass 255; a power bomb tank adds 2 to both, up to 254. The missile types the downloads add (super, ice, and diffusion missiles, flags in weaponsStatus) draw on that one count. Zero Mission keeps separate counts for missiles, super missiles, and power bombs, and its missile tank also adds 5, or 2 on Hard. mf/ammo-tanks
About: Metroid Fusion Ammunition
Value
{
"missile": 5,
"powerBomb": 2
}src/bg_clip.c#L258-L282// Apply tank
if (i == TANK_TYPE_MISSILE)
{
if (gEquipment.maxMissiles + MISSILE_INCREASE_AMOUNT <= MAX_MISSILE_AMOUNT)
{
gEquipment.maxMissiles += MISSILE_INCREASE_AMOUNT;
gEquipment.currentMissiles += MISSILE_INCREASE_AMOUNT;
…
else if (i == TANK_TYPE_POWER_BOMB)
{
if (gEquipment.maxPowerBombs + POWER_BOMB_INCREASE_AMOUNT <= MAX_POWER_BOMB_AMOUNT)
{
…mf: a decompilation of Metroid Fusion (metroidret/mf) · include/constants/samus.h#L241-L251#ifndef REGION_JP
#define ENERGY_INCREASE_AMOUNT 100
#define MISSILE_INCREASE_AMOUNT 5
#define POWER_BOMB_INCREASE_AMOUNT 2
…
// Default 255
#define MAX_MISSILE_AMOUNT (51 * MISSILE_INCREASE_AMOUNT)
// Default 254
#define MAX_POWER_BOMB_AMOUNT (127 * POWER_BOMB_INCREASE_AMOUNT)mf: a decompilation of Metroid Fusion (metroidret/mf) · include/structs/samus.h#L42-L50struct Equipment {
u16 currentEnergy;
u16 maxEnergy;
u16 currentMissiles;
u16 maxMissiles;
u8 currentPowerBombs;
u8 maxPowerBombs;
u8 beamStatus;
u8 weaponsStatus;mf: a decompilation of Metroid Fusion (metroidret/mf) · include/constants/samus.h#L273-L280#define MBF_NONE 0
#define MBF_MISSILES (1 << 0)
#define MBF_SUPER_MISSILES (1 << 1)
#define MBF_ICE_MISSILES (1 << 2)
#define MBF_DIFFUSION_MISSILES (1 << 3)
…
#define MBF_ANY_MISSILE (MBF_MISSILES | MBF_SUPER_MISSILES | MBF_ICE_MISSILES | MBF_DIFFUSION_MISSILES)mzm: a decompilation of Metroid: Zero Mission (metroidret/mzm) · include/structs/samus.h#L16-L24struct Equipment {
u16 maxEnergy;
u16 maxMissiles;
u8 maxSuperMissiles;
u8 maxPowerBombs;
u16 currentEnergy;
u16 currentMissiles;
u8 currentSuperMissiles;
u8 currentPowerBombs;mzm: a decompilation of Metroid: Zero Mission (metroidret/mzm) · src/data/block_data.c#L67-L84const struct TankList sTankIncreaseAmount[DIFF_COUNT] = {
[DIFF_EASY] = {
.energy = 100,
.missile = 5,
…
[DIFF_NORMAL] = {
.energy = 100,
.missile = 5,
…
[DIFF_HARD] = {
.energy = 50,
.missile = 2,Checked by claude-agent, 2026-10-22 — read the missile and power bomb branches in bg_clip.c, the constants (51 x 5 = 255, 127 x 2 = 254), the Equipment struct, which has no super missile count, and the weapon flags. That the missile types share the count is the struct's reading: there is no other missile counter in it. Zero Mission's side is mzm/ammo-tanksFrom the start of 10 (mf/start-energy), 49 missile tanks reach 255 (computed). The tank adds to the count even before the missile download sets MBF_MISSILES. SpriteUtilRefillSamus adds missiles only when some missile flag is set (src/sprite_util.c lines 2693-2699).
Cited by: Ammunition, Metroid Fusion
A jump sets Samus's upward yVelocity to 170 (SAMUS_LOW_JUMP_VELOCITY), or 220 with the High Jump, with no separate suitless speed. The velocity counts eighths of a subpixel and a pixel is 4 subpixels, so these are 5.3125 and 6.875 px/frame (computed). Movement caps the velocity at 128 (16 subpixels, 4 px), so a jump's first 8 frames each rise 4 px, and a High Jump's first 16 (computed). Zero Mission's jump takes off at 192, or 232 with the High Jump, under a cap of 192: 6 px/frame from the first frame. mf/jump-speed
About: Metroid Fusion Jump
Value
[ 5.3125, 6.875 ]
src/samus.c#L4784-L4793case SPOSE_RUNNING:
if (gSamusDataCopy.forcedMovement == FORCED_MOVEMENT_MID_AIR_JUMP)
{
gSamusData.pose = SPOSE_STARTING_SPIN_JUMP;
…
if (gEquipment.suitMiscStatus & SMF_HIGH_JUMP)
gSamusData.yVelocity = SAMUS_HIGH_JUMP_VELOCITY;
else
gSamusData.yVelocity = SAMUS_LOW_JUMP_VELOCITY;mf: a decompilation of Metroid Fusion (metroidret/mf) · src/samus.c#L4929-L4942default:
gSamusData.pose = SPOSE_MID_AIR;
…
if (gSamusDataCopy.forcedMovement == FORCED_MOVEMENT_MID_AIR_JUMP)
{
…
if (gEquipment.suitMiscStatus & SMF_HIGH_JUMP)
gSamusData.yVelocity = SAMUS_HIGH_JUMP_VELOCITY;
else
gSamusData.yVelocity = SAMUS_LOW_JUMP_VELOCITY;mf: a decompilation of Metroid Fusion (metroidret/mf) · include/constants/samus.h#L321-L327#define SAMUS_HIGH_JUMP_VELOCITY (SUB_PIXEL_TO_VELOCITY(QUARTER_BLOCK_SIZE + EIGHTH_BLOCK_SIZE + PIXEL_SIZE - ONE_SUB_PIXEL * .5f))
#define SAMUS_LOW_JUMP_VELOCITY (SUB_PIXEL_TO_VELOCITY(QUARTER_BLOCK_SIZE + PIXEL_SIZE + ONE_SUB_PIXEL + ONE_SUB_PIXEL * .25f))
…
#define SAMUS_Y_VELOCITY_CAP (SUB_PIXEL_TO_VELOCITY(QUARTER_BLOCK_SIZE))mf: a decompilation of Metroid Fusion (metroidret/mf) · include/macros.h#L192-L193#define VELOCITY_TO_SUB_PIXEL(velocity) (DIV_SHIFT((velocity), 8))
#define SUB_PIXEL_TO_VELOCITY(velocity) ((s32)((velocity) * 8))mf: a decompilation of Metroid Fusion (metroidret/mf) · include/types.h#L43-L59// The amount of sub pixels a single visible pixel contains
#define SUB_PIXEL_RATIO 4
// The amount of visible pixels a block contains
#define PIXEL_PER_BLOCK 16
…
#define BLOCK_SIZE (PIXEL_PER_BLOCK * SUB_PIXEL_RATIO)
…
#define QUARTER_BLOCK_SIZE (BLOCK_SIZE / 4)
…
#define EIGHTH_BLOCK_SIZE (BLOCK_SIZE / 8)
…
#define PIXEL_SIZE (BLOCK_SIZE / PIXEL_PER_BLOCK)
…mf: a decompilation of Metroid Fusion (metroidret/mf) · src/samus.c#L5526-L5538// Check is within the bounds
if (gSamusData.yVelocity > gSamusPhysics.yVelocityCap)
velocity = VELOCITY_TO_SUB_PIXEL(gSamusPhysics.yVelocityCap);
else if (gSamusData.yVelocity < -gSamusPhysics.yVelocityCap)
velocity = VELOCITY_TO_SUB_PIXEL(-gSamusPhysics.yVelocityCap);
else
velocity = VELOCITY_TO_SUB_PIXEL(gSamusData.yVelocity);
…
gSamusData.yPosition -= velocity;mzm: a decompilation of Metroid: Zero Mission (metroidret/mzm) · include/constants/samus.h#L336-L338#define SAMUS_HIGH_JUMP_VELOCITY (SUB_PIXEL_TO_VELOCITY(QUARTER_BLOCK_SIZE + PIXEL_SIZE * 3 + ONE_SUB_PIXEL))
#define SAMUS_LOW_JUMP_VELOCITY (SUB_PIXEL_TO_VELOCITY(QUARTER_BLOCK_SIZE + EIGHTH_BLOCK_SIZE))
#define SAMUS_SUITLESS_JUMP_VELOCITY (SUB_PIXEL_TO_VELOCITY(QUARTER_BLOCK_SIZE + EIGHTH_BLOCK_SIZE + PIXEL_SIZE / 2 + ONE_SUB_PIXEL / 2.f))mzm: a decompilation of Metroid: Zero Mission (metroidret/mzm) · include/constants/samus.h#L325#define SAMUS_Y_POSITIVE_VELOCITY_CAP 192Checked by claude-agent, 2026-10-22 — read SamusSetMidAirPose's running and default cases and the constants. BLOCK_SIZE is 64 subpixels, so the low jump is (16 + 4 + 1 + 0.25) x 8 = 170, the High Jump (16 + 8 + 4 - 0.5) x 8 = 220, and the cap 16 x 8 = 128; dividing by 8 x 4 = 32 gives px/frame. SamusUpdateVelocityPosition moves Samus by the capped value shifted right 3 and then lowers yVelocity by the acceleration, 6 out of liquid (mf/gravity): 170 down to 128 is 8 frames at the cap, 220 down to 130 is 16. The cap is yVelocityCap, set to SAMUS_Y_VELOCITY_CAP out of liquid by SamusUpdatePhysics (line 175). Zero Mission's side is mzm/jump-speedThe wall jump takes the low jump speed even with the High Jump, where Zero Mission's takes either (mzm/wall-jump); in liquid without the Gravity Suit it can take 48 instead (src/samus.c lines 4894-4900). The Space Jump resets the velocity to the same 170 or 220 when A is pressed while falling at a velocity of 78 or more, about 2.4 px/frame (src/samus.c lines 2529-2541).
Cited by: Jump, Metroid Fusion
Out of liquid, SamusUpdatePhysics sets the vertical acceleration to 6 and the vertical speed cap to 128, and SamusUpdateVelocityPosition takes 6 from yVelocity on every frame of the airborne poses while the velocity is above -220, whether A is held or not: 0.1875 px/frame², with rises and falls both capped at 4 px/frame (computed). In water, lava, or acid without the Gravity Suit or the flag the decompilers name SMF_SA_X_SUIT, the acceleration drops to 4 and the cap to 48, 0.125 px/frame² and 1.5 px/frame. Zero Mission's gravity is 10, 0.3125 px/frame², with the same 4 px/frame fall cap (computed). mf/gravity
About: Metroid Fusion Gravity
Value: 0.1875 px/frame²
Codemf: a decompilation of Metroid Fusion (metroidret/mf) ·include/constants/samus.h#L326-L335#define SAMUS_Y_ACCELERATION (SUB_PIXEL_TO_VELOCITY(ONE_SUB_PIXEL * .75f))
#define SAMUS_Y_VELOCITY_CAP (SUB_PIXEL_TO_VELOCITY(QUARTER_BLOCK_SIZE))
…
#define SAMUS_TERMINAL_VELOCITY (SUB_PIXEL_TO_VELOCITY(HALF_BLOCK_SIZE - PIXEL_SIZE - ONE_SUB_PIXEL / 2.f))mf: a decompilation of Metroid Fusion (metroidret/mf) · src/samus.c#L137-L175switch (currentHazard)
{
case HAZARD_WATER:
gSamusUnderwaterFlag++;
…
case HAZARD_LAVA:
case HAZARD_ACID:
if (!(gEquipment.suitMiscStatus & (SMF_GRAVITY_SUIT | SMF_SA_X_SUIT)))
slowed++;
break;
…
gSamusPhysics.yAcceleration = SAMUS_Y_ACCELERATION * 2.f / 3;
…mf: a decompilation of Metroid Fusion (metroidret/mf) · src/samus.c#L5526-L5538// Check is within the bounds
if (gSamusData.yVelocity > gSamusPhysics.yVelocityCap)
…
else if (gSamusData.yVelocity < -gSamusPhysics.yVelocityCap)
velocity = VELOCITY_TO_SUB_PIXEL(-gSamusPhysics.yVelocityCap);
…
// Apply acceleration
if (gSamusData.yVelocity > -SAMUS_TERMINAL_VELOCITY)
gSamusData.yVelocity -= gSamusPhysics.yAcceleration;
…
gSamusData.yPosition -= velocity;mzm: a decompilation of Metroid: Zero Mission (metroidret/mzm) · include/constants/samus.h#L324-L327#define SAMUS_Y_ACCELERATION 10
#define SAMUS_Y_POSITIVE_VELOCITY_CAP 192
#define SAMUS_Y_NEGATIVE_VELOCITY_CAP 128
#define SAMUS_Y_SLOWED_VELOCITY_CAP 40Checked by claude-agent, 2026-10-22 — read SamusUpdatePhysics's hazard switch and both branches, and the airborne poses' branch of SamusUpdateVelocityPosition. 0.75 x 8 = 6, 6 x 2 / 3 = 4, 16 x 8 = 128, 128 x 0.375 = 48, and the terminal velocity (32 - 4 - 0.5) x 8 = 220; dividing by 32 gives px. The water case falls through to the lava and acid case, so all three test the suits. Zero Mission's side is mzm/gravitySamusUpdatePhysics sets SMF_SA_X_SUIT whenever the Ice Beam bit is set in beamStatus and clears it otherwise (src/samus.c lines 192-196). Out of liquid, a slowed value of 0x80 also slows Samus; the decompilers' comment calls it "nightmare related".
Cited by: Gravity, Metroid Fusion
Stepping the code's arithmetic with A held, out of liquid, a jump from the ground peaks about 71 px up (283 subpixels) on its 28th frame, and a High Jump 104 px (416 subpixels) on its 36th (computed: each frame moves yVelocity capped at 128 and shifted right 3, then takes 6 from it). The same stepping gives Zero Mission about 59 px on the 19th frame and 83 px on the 23rd (mzm/jump-height), so Fusion's jump is higher and slower. mf/jump-height
About: Metroid Fusion Jump
Value
[ 70.75, 104 ]
include/constants/samus.h#L321-L327#define SAMUS_HIGH_JUMP_VELOCITY (SUB_PIXEL_TO_VELOCITY(QUARTER_BLOCK_SIZE + EIGHTH_BLOCK_SIZE + PIXEL_SIZE - ONE_SUB_PIXEL * .5f))
#define SAMUS_LOW_JUMP_VELOCITY (SUB_PIXEL_TO_VELOCITY(QUARTER_BLOCK_SIZE + PIXEL_SIZE + ONE_SUB_PIXEL + ONE_SUB_PIXEL * .25f))
…
#define SAMUS_Y_ACCELERATION (SUB_PIXEL_TO_VELOCITY(ONE_SUB_PIXEL * .75f))
#define SAMUS_Y_VELOCITY_CAP (SUB_PIXEL_TO_VELOCITY(QUARTER_BLOCK_SIZE))mf: a decompilation of Metroid Fusion (metroidret/mf) · src/samus.c#L5526-L5538// Check is within the bounds
if (gSamusData.yVelocity > gSamusPhysics.yVelocityCap)
velocity = VELOCITY_TO_SUB_PIXEL(gSamusPhysics.yVelocityCap);
…
else
velocity = VELOCITY_TO_SUB_PIXEL(gSamusData.yVelocity);
…
if (gSamusData.yVelocity > -SAMUS_TERMINAL_VELOCITY)
gSamusData.yVelocity -= gSamusPhysics.yAcceleration;
…
gSamusData.yPosition -= velocity;mzm: a decompilation of Metroid: Zero Mission (metroidret/mzm) · include/constants/samus.h#L324-L337#define SAMUS_Y_ACCELERATION 10
#define SAMUS_Y_POSITIVE_VELOCITY_CAP 192
…
#define SAMUS_HIGH_JUMP_VELOCITY (SUB_PIXEL_TO_VELOCITY(QUARTER_BLOCK_SIZE + PIXEL_SIZE * 3 + ONE_SUB_PIXEL))
#define SAMUS_LOW_JUMP_VELOCITY (SUB_PIXEL_TO_VELOCITY(QUARTER_BLOCK_SIZE + EIGHTH_BLOCK_SIZE))mzm: a decompilation of Metroid: Zero Mission (metroidret/mzm) · src/samus.c#L6758-L6776if (pData->yVelocity > gSamusPhysics.positiveYVelocityCap)
{
velocity = DIV_SHIFT(gSamusPhysics.positiveYVelocityCap, 8);
}
…
else
{
velocity = DIV_SHIFT(pData->yVelocity, 8);
}
…
if (pData->yVelocity >= -231)
{
…Checked by claude-agent, 2026-10-22 — the same Python loop as mzm/jump-height, over the frames from take-off with velocity v from 170 (or 220): move by min(v, 128) >> 3 subpixels, then v -= 6, until the move is 0 or less; the rise sums to 283 and 416 subpixels, divided by 4 for pixels. It assumes nothing else touches yVelocity on the way up (no release, ceiling, or liquid). With Zero Mission's 192 and 232, cap 192, and step 10, the loop gives 235 and 331Not yet measured in the ROM.
Cited by: Metroid Fusion
On any frame Samus rises (yVelocity above 0) without A held, the mid-air handlers set yVelocity to 0, at any height: SamusMidAir, SamusTurningAroundMidAir, SamusSpinning (which runs the spin jump, the Space Jump, and the Screw Attack), and SamusWallJumping, which also drops a wall jump into a spin. The test is each handler's last step but the wall jump's, so a frame whose earlier branch returns skips it. Zero Mission's first three handlers end with the same test. mf/release-cap
About: Metroid Fusion Variable jump height
Value: 0 velocity after release
Codemf: a decompilation of Metroid Fusion (metroidret/mf) ·src/samus.c#L2124-L2130// Check turning
if (turning)
return SPOSE_TURNING_AROUND_MID_AIR;
…
// Check terminate Y velocity if not pressing A and going up
if (!(gButtonInput & KEY_A) && gSamusData.yVelocity > 0)
gSamusData.yVelocity = 0;mf: a decompilation of Metroid Fusion (metroidret/mf) · src/samus.c#L2195-L2196if (!(gButtonInput & KEY_A) && gSamusData.yVelocity > 0)
gSamusData.yVelocity = 0;mf: a decompilation of Metroid Fusion (metroidret/mf) · src/samus.c#L2596-L2597if (!(gButtonInput & KEY_A) && gSamusData.yVelocity > 0)
gSamusData.yVelocity = 0;mf: a decompilation of Metroid Fusion (metroidret/mf) · src/samus.c#L3025-L3032if (!(gButtonInput & KEY_A))
{
// Stop upwards Y velocity if released A
if (gSamusData.yVelocity > 0)
gSamusData.yVelocity = 0;
…
// Set spinning
gSamusData.pose = SPOSE_SPINNING;mf: a decompilation of Metroid Fusion (metroidret/mf) · src/data/samus_data.c#L162-L189[SPOSE_MID_AIR] = SamusMidAir,
[SPOSE_TURNING_AROUND_MID_AIR] = SamusTurningAroundMidAir,
…
[SPOSE_STARTING_SPIN_JUMP] = SamusSpinning,
[SPOSE_SPINNING] = SamusSpinning,
…
[SPOSE_WALL_JUMPING] = SamusWallJumping,
…
[SPOSE_SPACE_JUMPING] = SamusSpinning,
[SPOSE_SCREW_ATTACKING] = SamusSpinning,mzm: a decompilation of Metroid: Zero Mission (metroidret/mzm) · src/samus.c#L4572-L4578// Check turning
if (turning)
return SPOSE_TURNING_AROUND_MIDAIR;
…
// Check terminate Y velocity if not pressing A and going up
if (!(gButtonInput & KEY_A) && pData->yVelocity > 0)
pData->yVelocity = 0;mzm: a decompilation of Metroid: Zero Mission (metroidret/mzm) · src/samus.c#L4645-L4647// Check terminate Y velocity if not pressing A and going up
if (!(gButtonInput & KEY_A) && pData->yVelocity > 0)
pData->yVelocity = 0;mzm: a decompilation of Metroid: Zero Mission (metroidret/mzm) · src/samus.c#L4774-L4776// Check terminate Y velocity if not pressing A and going up
if (!(gButtonInput & KEY_A) && pData->yVelocity > 0)
pData->yVelocity = 0;Checked by claude-agent, 2026-10-22 — read SamusMidAir, SamusTurningAroundMidAir, and SamusSpinning to their ends, the release branch of SamusWallJumping, and the pose-handler table. Zero Mission's three lines are the ones mzm/release-cap cites in SamusMidAir, SamusTurningAroundMidAir, and SamusSpinningSamusMorphBallMidAir has the same test, only while forcedMovement is 0 (src/samus.c lines 2870-2875). SamusMidAir and SamusTurningAroundMidAir turn a new A press into a spin when up and down aren't held, and return before the test.
Cited by: Metroid Fusion, Variable jump height
Touching an absorbable X parasite runs SamusAbsorbX, which refills by its color: a red X (SSC_RED_X_PARASITE) 500 energy, 100 missiles, and 10 power bombs; a green X 2 missiles; any other (yellow) X 10 energy. SpriteUtilRefillSamus adds missiles only once a missile flag is set and power bombs only once the power bomb flag is, and stops each at its maximum. mf/x-absorb
About: Metroid Fusion Item drop table
Value
{
"yellow": {
"energy": 10
},
"green": {
"missiles": 2
},
"red": {
"energy": 500,
"missiles": 100,
"powerBombs": 10
}
}src/x_parasite_util.c#L482-L497void XParasiteGettingAbsorbedInit(void)
…
SamusAbsorbX();mf: a decompilation of Metroid Fusion (metroidret/mf) · src/x_parasite_util.c#L433-L470void SamusAbsorbX(void)
…
case SSP_X_ABSORBABLE_BY_SAMUS:
if (gCurrentSprite.samusCollision == SSC_RED_X_PARASITE)
{
energy = 500;
missiles = 100;
powerBombs = 10;
…
else if (gCurrentSprite.samusCollision == SSC_GREEN_X_PARASITE)
{
energy = 0;
…mf: a decompilation of Metroid Fusion (metroidret/mf) · src/sprite_util.c#L2686-L2707void SpriteUtilRefillSamus(u16 energy, u16 missiles, u16 powerBombs)
{
gEquipment.currentEnergy += energy;
…
if (gEquipment.currentEnergy > gEquipment.maxEnergy)
gEquipment.currentEnergy = gEquipment.maxEnergy;
…
if (gEquipment.weaponsStatus & MBF_ANY_MISSILE)
{
gEquipment.currentMissiles += missiles;
…
if (gEquipment.currentMissiles > gEquipment.maxMissiles)
…Checked by claude-agent, 2026-10-22 — read SamusAbsorbX, its caller XParasiteGettingAbsorbedInit, and SpriteUtilRefillSamus. The color names follow the decompilers' collision ids and OAM names (sXParasiteOam_Yellow, _Green, _Red); the else branch takes every absorbable X that isn't red or green, which XParasiteDetermineColor makes yellow (mf/x-color)An X marked SSP_X_UNABSORBABLE_BY_SAMUS gives nothing. A core X refills 400 energy, 50 missiles, and 10 power bombs through the same function (src/sprites_AI/core_x.c line 615).
Cited by: Ammunition, Item drop table, Metroid Fusion
When a dying sprite's countdown ends, SpriteDying sets its pose to 0x5B (the decompilers' SPRITE_POSE_TURNING_INTO_X) unless it is a secondary sprite, and an enemy's AI (Choot's, for one) runs XParasiteInit in that pose. The X it becomes takes its color from a number from 1 to 1024, made from the low 2 bits of gSpriteRandomNumber and the 8-bit frame counter, against three weights in that sprite's row of sPrimarySpriteStats: the top red-weight values give red and the next green-weight values green, each only when its weight is above 0, and everything left below the green bound gives yellow, whatever the yellow weight is, unless it is 0, which deletes the X instead. A would-be green X turns yellow when missiles are full and energy isn't, or Samus has no missiles yet; a would-be yellow X turns green when energy is full and missiles aren't. An X from a core X is green when gSpriteRandomNumber is below 5 or that energy test holds, and otherwise yellow; an X placed in a room is yellow. All of this applies only to an X whose sprite property is SSP_X_ABSORBABLE_BY_SAMUS: one marked SSP_X_UNABSORBABLE_BY_SAMUS, or the unnamed SSP_UNKNOWN_40, is always yellow, and any other property deletes the X. mf/x-color
About: Metroid Fusion Item drop table
Codemf: a decompilation of Metroid Fusion (metroidret/mf) ·src/x_parasite_util.c#L561-L572case SSP_X_ABSORBABLE_BY_SAMUS:
if (type == X_PARASITE_TYPE_FROM_CORE_X)
{
if (gSpriteRandomNumber < 5 || SpriteUtilCheckEnergyFullAndMissilesNotFull())
{
gCurrentSprite.pOam = sXParasiteOam_Green;
gCurrentSprite.samusCollision = SSC_GREEN_X_PARASITE;
}
else
{
gCurrentSprite.pOam = sXParasiteOam_Yellow;mf: a decompilation of Metroid Fusion (metroidret/mf) · src/x_parasite_util.c#L574-L655else if (type == X_PARASITE_TYPE_FROM_SPRITE)
{
// Calculate random number between 1 and 1024 inclusive
randomNumber = gSpriteRandomNumber & 3;
randomNumber <<= 8;
randomNumber |= gFrameCounter8Bit;
randomNumber++;
…
yellowXThreshold = sPrimarySpriteStats[spriteId][4];
greenXThreshold = sPrimarySpriteStats[spriteId][5];
redXThreshold = sPrimarySpriteStats[spriteId][6];
…
…mf: a decompilation of Metroid Fusion (metroidret/mf) · src/x_parasite_util.c#L657-L669case SSP_X_UNABSORBABLE_BY_SAMUS:
gCurrentSprite.invincibilityStunFlashTimer &= 0x7f;
gCurrentSprite.pOam = sXParasiteOam_Yellow;
…
case SSP_UNKNOWN_40:
gCurrentSprite.pOam = sXParasiteOam_Yellow;
gCurrentSprite.samusCollision = SSC_YELLOW_X_PARASITE;
break;
…
default:
gCurrentSprite.status = 0;mf: a decompilation of Metroid Fusion (metroidret/mf) · src/x_parasite_util.c#L509-L530void SpriteDying(void)
…
if (--gCurrentSprite.work1 == 0)
{
if (gCurrentSprite.properties & SP_SECONDARY_SPRITE)
{
gCurrentSprite.status = 0;
}
else
{
gCurrentSprite.pose = 0x5b;mf: a decompilation of Metroid Fusion (metroidret/mf) · src/sprites_AI/choot.c#L328-L342case 0x57:
SpriteDyingInit();
case 0x58:
SpriteDying();
break;
…
case 0x5b:
XParasiteInit();
break;mf: a decompilation of Metroid Fusion (metroidret/mf) · include/constants/sprite.h#L477SPRITE_POSE_TURNING_INTO_X = 0x5B,mf: a decompilation of Metroid Fusion (metroidret/mf) · src/sprite_util.c#L2746-L2770u8 SpriteUtilCheckEnergyFullAndMissilesNotFull(void)
{
if (gEquipment.currentEnergy == gEquipment.maxEnergy)
{
if (gEquipment.weaponsStatus & MBF_ANY_MISSILE && gEquipment.currentMissiles < gEquipment.maxMissiles)
return TRUE;
…
u8 SpriteUtilCheckMissilesFullAndEnergyNotFull(void)
{
if (!(gEquipment.weaponsStatus & MBF_ANY_MISSILE))
return TRUE;
…
…mf: a decompilation of Metroid Fusion (metroidret/mf) · src/x_parasite_util.c#L697-L737case PSPRITE_X_PARASITE_IN_ROOM:
case PSPRITE_X_PARASITE_IN_ROOM_PROXIMITY:
…
XParasiteDetermineColor(X_PARASITE_TYPE_IN_ROOM, 0);
…
XParasiteDetermineColor(X_PARASITE_TYPE_FROM_CORE_X, 0);
…
case PSPRITE_X_PARASITE:
case PSPRITE_X_PARASITE_AQUA_ZEBESIAN:
XParasiteSpawningInit();
XParasiteDetermineColor(X_PARASITE_TYPE_FROM_SPRITE, gCurrentSprite.roomSlot);
break;
…Checked by claude-agent, 2026-10-22 — read XParasiteDetermineColor to its end, the two Samus checks, and XParasiteInit's cases. The red range is (1024 - red, 1024], green (red bound - green, red bound], yellow [1, green bound]; a zero red or green weight skips its range and passes its bound down; the yellow weight is only tested for zero. "Weights" is the atlas's reading: the code reads columns 4, 5, and 6 of the row for the sprite's id (or its roomSlot for PSPRITE_X_PARASITE), and the default case turns the sprite into PSPRITE_X_PARASITE. SpriteDyingInit plays SOUND_SPRITE_TRANSFORMING_INTO_X; the atlas hasn't checked every enemy's AI for the 0x5B caseWhen the three ranges don't cover the number, the code deletes the X (gCurrentSprite.status = 0); the decompilers' comment says "This point shouldn't be reached if drop chances sum to 1024". The weights themselves are in sPrimarySpriteStats, in src/data, which the atlas hasn't fetched.
Cited by: Item drop table, Metroid Fusion