data/claims/pokecrystal-capture.yamlPokémon Crystal's capture formula (pret/pokecrystal)
Game: Pokémon Crystal. Concept: Capture.
A wild Pokémon's catch rate is its species' catch-rate byte, copied into wEnemyMonCatchRate when the battle loads it (45 for Bulbasaur). pokecrystal-capture/catch-rate
engine/battle/core.asm#L6383-L6385ld a, [wBaseCatchRate]
ld [de], a
inc depokecrystal: disassembly of Pokémon Crystal · data/pokemon/base_stats/bulbasaur.asm#L7db 45 ; catch rateChecked by claude, 2026-10-22 — read the end of LoadEnemyMon (L6370-L6386): after the five base stats it writes wBaseCatchRate into the next byte, which ram/wram.asm L2714-L2715 names wEnemyMonCatchRatePokeBallEffect catches outright with a Master Ball or in the catching tutorial. Otherwise a ball's routine changes the catch rate: the Ultra Ball doubles it, and the Great, Safari, and Park Balls add half of it, each capped at 255. The Poké Ball has no routine and leaves it as it is. pokecrystal-capture/ball-modifier
Value
{
"kind": "ball-modifier",
"ultra": "2C",
"great": "C + floor(C/2)",
"safari": "C + floor(C/2)",
"park": "C + floor(C/2)",
"poke": "C",
"cap": 255,
"master": "caught"
}engine/items/item_effects.asm#L242-L270ld a, [wEnemyMonCatchRate]
ld b, a
ld a, [wBattleType]
cp BATTLETYPE_TUTORIAL
jp z, .catch_without_fail
ld a, [wCurItem]
cp MASTER_BALL
jp z, .catch_without_fail
…
ld hl, BallMultiplierFunctionTablepokecrystal: disassembly of Pokémon Crystal · engine/items/item_effects.asm#L722-L754BallMultiplierFunctionTable:
…
dbw ULTRA_BALL, UltraBallMultiplier
dbw GREAT_BALL, GreatBallMultiplier
dbw SAFARI_BALL, SafariBallMultiplier ; Safari Ball, leftover from RBY
…
UltraBallMultiplier:
; multiply catch rate by 2
sla b
ret nc
ld b, $ff
ret
…Checked by claude, 2026-10-22 — read the two outright-catch tests, the table walk (a ball not in the table, such as POKE_BALL, reaches -1 and skips to .skip_or_return_from_ball_fn with b unchanged), and the Ultra and Great routines with their carry capsCited by: Capture
Except with a Level Ball, PokeBallEffect scales the ball-modified rate by HP: for max HP and current HP . When is 256 or more, both and are first divided by 4 (a quartered of 0 becomes 1) and only each low byte is kept, so the formula breaks for max HP over 341. pokecrystal-capture/hp-term
Value
{
"kind": "hp-term",
"maxFactor": 3,
"hpFactor": 2,
"quarterAbove": 255,
"min": 1
}engine/items/item_effects.asm#L272-L338ld a, [wCurItem]
cp LEVEL_BALL
ld a, b
jp z, .skip_hp_calc
…
sla c
rl b
ld h, d
ld l, e
add hl, de
add hl, de
…
…Checked by claude, 2026-10-22 — traced the registers: bc = current HP doubled (sla c / rl b), de = max HP tripled (hl = de + de + de); if d is nonzero both are shifted right twice and a zero c is raised to 1; b = e, then C' × (b - c) / b with a zero quotient raised to 1. The max HP > 341 bug is pret's comment, and 3 × 342 / 4 = 256 overflows a byteCited by: Capture
The Level Ball doubles the catch rate when the player's Pokémon is higher in level than the wild one, quadruples it when half the player's level is still higher, and multiplies it by 8 when a quarter is, capped at 255. It then skips the HP and status steps: its final rate is that multiplied catch rate alone. pokecrystal-capture/level-ball
Value
{
"kind": "ball-modifier",
"levelBall": [
2,
4,
8
],
"cap": 255,
"skipsHP": true
}engine/items/item_effects.asm#L1032-L1058LevelBallMultiplier:
; multiply catch rate by 8 if player mon level / 4 > enemy mon level
; multiply catch rate by 4 if player mon level / 2 > enemy mon level
; multiply catch rate by 2 if player mon level > enemy mon levelpokecrystal: disassembly of Pokémon Crystal · engine/items/item_effects.asm#L272-L276.skip_or_return_from_ball_fn
ld a, [wCurItem]
cp LEVEL_BALL
ld a, b
jp z, .skip_hp_calcChecked by claude, 2026-10-22 — read LevelBallMultiplier (three level compares, each sla b with a carry cap) and the jump: ld a, b leaves the flags from cp LEVEL_BALL, so a Level Ball jumps to .skip_hp_calc with the multiplied rate, past the HP term (L278-L338) and the status bonus (L340-L355)Cited by: Capture
Crystal adds 10 to for a sleeping or frozen Pokémon, capped at 255, and nothing for burn, poison, or paralysis: the test meant to add 5 for them reads a value the sleep-and-freeze mask has already cleared. Gold and Silver have the same code, and their disassembly notes that those three statuses were meant to rank between sleep and none. pokecrystal-capture/status-bonus
Value
{
"kind": "status-add",
"sleepOrFreeze": 10,
"burnParalysisPoison": 0,
"intended": 5,
"cap": 255
}engine/items/item_effects.asm#L339-L355.statuscheck
; BUG: BRN/PSN/PAR do not affect catch rate (see docs/bugs_and_glitches.md)
ld b, a
ld a, [wEnemyMonStatus]
and 1 << FRZ | SLP_MASK
ld c, 10
jr nz, .addstatus
and a
ld c, 5
jr nz, .addstatus
ld c, 0
.addstatus
…pokegold: disassembly of Pokémon Gold and Silver · engine/items/item_effects.asm#L337-L353.statuscheck
; This routine is buggy. It was intended that SLP and FRZ provide a higher
; catch rate than BRN/PSN/PAR, which in turn provide a higher catch rate than
; no status effect at all. But instead, it makes BRN/PSN/PAR provide no
; benefit.
…
and 1 << FRZ | SLP_MASK
ld c, 10
jr nz, .addstatus
; ld a, [wEnemyMonStatus]
and a
ld c, 5
…Checked by claude, 2026-10-22 — read both: after and 1 << FRZ | SLP_MASK falls through (zero), a is already 0, so and a is zero and c becomes 0; Gold's copy is the same instructions with the fixing load commented outCited by: Capture
The final rate goes into wFinalCatchRate, and one random byte at most that rate catches, so the chance is and a final rate of 255 always catches. This one roll decides the throw; the wobbles come after. pokecrystal-capture/roll
Value
{
"kind": "single-roll",
"range": [
0,
255
],
"catchIf": "Random() <= X"
}engine/items/item_effects.asm#L372-L386.skip_hp_calc
ld b, a
ld [wFinalCatchRate], a
call Random
cp b
ld a, 0
jr z, .catch_without_fail
jr nc, .fail_to_catch
.catch_without_fail
ld a, [wEnemyMonSpecies]
.fail_to_catch
ld [wWildMon], aChecked by claude, 2026-10-22 — read the compare: equal jumps to the catch, a larger byte (no carry) fails, a smaller one falls through to the catch; wWildMon then holds the species or 0, and the animation that follows reads itCited by: Capture
Computed, counting every value of the roll as equally likely: a Bulbasaur (catch rate 45) with max HP 100 and no status in a Poké Ball has a final rate of 15 at full HP, so 16 in 256 (6.25%), and 44 at 1 HP, so 45 in 256 (17.6%). pokecrystal-capture/chance
Value
{
"kind": "example",
"catchRate": 45,
"maxHP": 100,
"ball": "poke",
"status": "none",
"fullHP": 0.0625,
"oneHP": 0.1758
}engine/items/item_effects.asm#L289-L338sla c
rl b
…
srl d
rr e
srl d
rr e
srl b
rr c
srl b
rr c
…
…pokecrystal: disassembly of Pokémon Crystal · engine/items/item_effects.asm#L372-L380call Random
cp b
ld a, 0
jr z, .catch_without_fail
jr nc, .fail_to_catchpokecrystal: disassembly of Pokémon Crystal · data/pokemon/base_stats/bulbasaur.asm#L7db 45 ; catch rateChecked by claude, 2026-10-22 — 3M = 300 has a high byte, so it becomes 75; at full HP 2H = 200 becomes 50, X = floor(45 × 25 / 75) = 15; at 1 HP 2H = 2 becomes 0, raised to 1, X = floor(45 × 74 / 75) = 44; the roll passes for X + 1 of 256 bytesThe ball's wobbles are drawn after the throw is decided. A caught Pokémon always gets three. On a failed throw, each of up to three wobbles happens with chance , where is read from the first row of WobbleProbabilities whose catch rate is at least the final rate (63 for a rate of 1, up to 255 for 255). The table's comment gives nLeft/255 = (nRight/255) ** 4, so each is a fourth root, . The atlas reads that as the chance of passing one of four equal checks whose product is the catch chance; the comment doesn't say so. pokecrystal-capture/wobbles
Value
{
"kind": "wobble-table",
"maxWobbles": 3,
"rows": [
[
1,
63
],
[
2,
75
],
[
3,
84
],
[
4,
90
],
[
5,
95
],
[
7,
103
],
[
10,
113
],
[
15,
126
],
[
20,
134
],
[
30,
149
],
[
40,
160
],
[
50,
169
],
[
60,
177
],
[
80,
191
],
[
100,
201
],
[
120,
211
],
[
140,
220
],
[
160,
227
],
[
180,
234
],
[
200,
240
],
[
220,
246
],
[
240,
251
],
[
254,
253
],
[
255,
255
]
]
}engine/battle_anims/pokeball_wobble.asm#L1-L44GetPokeBallWobble:
; Returns whether a Poke Ball will wobble in the catch animation.
; Whether a Pokemon is caught is determined beforehand.
…
; Wobble up to 3 times.
cp 3 + 1
jr z, .finished
ld a, [wWildMon]
and a
ld c, 0 ; next
jr nz, .done
ld hl, WobbleProbabilities
…pokecrystal: disassembly of Pokémon Crystal · data/battle/wobble_probabilities.asm#L1-L27WobbleProbabilities:
; catch rate, chance of wobbling / 255
; nLeft/255 = (nRight/255) ** 4
db 1, 63
…
db 255, 255Checked by claude, 2026-10-22 — read GetPokeBallWobble: a nonzero wWildMon (caught) returns "next" until the count reaches 4 and .finished returns caught; otherwise the table walk stops at the first row with rate ≥ the final rate and a byte below p wobbles. Every row of the table copied into valueCited by: Capture
Gold and Silver run the same capture code as Crystal: from the catch-rate load to the final roll, PokeBallEffect differs only in comments, and the wobble table is identical. pokecrystal-capture/gold-silver-same
About: Pokémon Gold and Silver Capture
Codepokegold: disassembly of Pokémon Gold and Silver ·engine/items/item_effects.asm#L240-L388ld a, [wEnemyMonCatchRate]
ld b, a
…
cp LEVEL_BALL
ld a, b
jp z, .skip_hp_calc
…
; BUG: Catch rate formula breaks for Pokémon with max HP > 341 (see docs/bugs_and_glitches.md)
…
.skip_hp_calc
ld b, a
ld [wFinalCatchRate], a
…pokecrystal: disassembly of Pokémon Crystal · engine/items/item_effects.asm#L242-L382ld a, [wEnemyMonCatchRate]
ld b, a
…
cp LEVEL_BALL
ld a, b
jp z, .skip_hp_calc
…
; BUG: Catch rate formula breaks for Pokémon with max HP > 341 (see docs/bugs_and_glitches.md)
…
.skip_hp_calc
ld b, a
ld [wFinalCatchRate], a
…pokegold: disassembly of Pokémon Gold and Silver · data/battle/wobble_probabilities.asm#L1-L27WobbleProbabilities:
; catch rate, chance of wobbling / 255
; nLeft/255 = (nRight/255) ** 4pokecrystal: disassembly of Pokémon Crystal · data/battle/wobble_probabilities.asm#L1-L27WobbleProbabilities:
; catch rate, chance of wobbling / 255
; nLeft/255 = (nRight/255) ** 4Checked by claude, 2026-10-22 — diffed PokeBallEffect from its label to .catch_without_fail between the pinned pokecrystal and pokegold: the only differences are comment lines (the BUG notes, and Gold's commented-out fixes); diffed the two wobble tables (identical) and the wobble routines (Gold returns directly instead of restoring a WRAM bank)Cited by: Capture