data/claims/pokered-capture.yamlPokémon Red and Blue's capture formula (pret/pokered)
Game: Pokémon Red and Blue. Concept: Capture.
A wild Pokémon's catch rate is its species' catch-rate byte, copied into wEnemyMonActualCatchRate when the battle loads it (45 for Bulbasaur). In the Safari Zone, bait halves that copy and a rock doubles it, capped at 255. pokered-capture/catch-rate
engine/battle/core.asm#L6107-L6118ld hl, wMonHBaseStats
ld de, wEnemyMonBaseStats
…
ld hl, wMonHCatchRate
ld a, [hli]
ld [de], apokered: disassembly of Pokémon Red and Blue · data/pokemon/base_stats/bulbasaur.asm#L7db 45 ; catch ratepokered: disassembly of Pokémon Red and Blue · engine/items/item_effects.asm#L1432-L1455ld hl, wEnemyMonActualCatchRate
srl [hl] ; halve catch rate
…
ld hl, wEnemyMonActualCatchRate
ld a, [hl]
add a ; double catch rate
jr nc, .noCarry
ld a, $ffChecked by claude, 2026-10-22 — read LoadEnemyMonData's copy (the five base stats, then wMonHCatchRate into the next byte, which ram/wram.asm L1200-L1201 names wEnemyMonActualCatchRate), Bulbasaur's base stats, and ItemUseBait and ItemUseRockItemUseBall first draws a random byte : any value for a Poké Ball, redrawn until it is at most 200 for a Great Ball and at most 150 for an Ultra or Safari Ball. A Master Ball catches before the range test. pokered-capture/ball-roll
Value
{
"kind": "roll-range",
"poke": [
0,
255
],
"great": [
0,
200
],
"ultra": [
0,
150
],
"safari": [
0,
150
],
"master": "caught"
}engine/items/item_effects.asm#L177-L214; Get the first random number. Let it be called Rand1.
…
.loop
call Random
ld b, a
…
cp MASTER_BALL
jp z, .captured
…
cp POKE_BALL
jr z, .checkForAilments
…
…Checked by claude, 2026-10-22 — read the loop: Random returns a byte, cp 200 / cp 150 jump back to .loop when the byte is larger, and the Master Ball test comes before bothCited by: Capture
A status subtracts from : 25 for sleep or freeze, 12 for any other (burn, paralysis, poison), 0 for none. If is below that value the Pokémon is caught at once; otherwise the ball fails when less the value is greater than the catch rate, and passes on to the HP check when it is not. pokered-capture/status-and-rate
Value
{
"kind": "status-subtract",
"sleepOrFreeze": 25,
"burnParalysisPoison": 12,
"none": 0
}engine/items/item_effects.asm#L216-L239; no status ailment: Status = 0
; Burn/Paralysis/Poison: Status = 12
; Freeze/Sleep: Status = 25
; If Status is greater than Rand1, the Pokémon will be caught for sure.
ld a, [wEnemyMonStatus]
and a
jr z, .skipAilmentValueSubtraction ; no ailments
and (1 << FRZ) | SLP_MASK
ld c, 12
jr z, .notFrozenOrAsleep
ld c, 25
.notFrozenOrAsleep
…pokered: disassembly of Pokémon Red and Blue · engine/items/item_effects.asm#L298-L303pop bc ; b = Rand1 - Status
; If Rand1 - Status > CatchRate, the ball fails to capture the Pokémon.
ld a, [wEnemyMonActualCatchRate]
cp b
jr c, .failedToCaptureChecked by claude, 2026-10-22 — read the subtraction (sub c sets carry, and jumps to .captured, when R1 < c) and the comparison against wEnemyMonActualCatchRate (carry, so failure, when the rate is below R1 - S)Cited by: Capture
For the HP check, ItemUseBall computes from max HP and current HP , where the ball factor is 8 for a Great Ball and 12 for every other ball. A over 255 catches; otherwise a second random byte catches when it is at most . The check runs only after the status and catch-rate test has passed. pokered-capture/hp-check
Value
{
"kind": "hp-check",
"numerator": 255,
"ballFactor": {
"great": 8,
"other": 12
},
"hpDivisor": 4,
"minDivisor": 1,
"cap": 255
}engine/items/item_effects.asm#L241-L316; Calculate MaxHP * 255.
…
; Determine BallFactor. It's 8 for Great Balls and 12 for the others.
ld a, [wCurItem]
cp GREAT_BALL
ld a, 12
jr nz, .skip1
ld a, 8
…
; Divide the enemy's current HP by 4. HP is not supposed to exceed 999 so
; the result should fit in a. If the division results in a quotient of 0,
; change it to 1.
…Checked by claude, 2026-10-22 — read the multiply by 255, the ball factor (12 unless the item is GREAT_BALL), the two-shift quarter of HP with 0 raised to 1, the second divide, the W > 255 branch on the quotient's high byte, and the R2 comparison (carry, so failure, when W < R2)Cited by: Capture
Computed, counting every value of each roll as equally likely: with no status, Red and Blue catch with chance , where is 256, 201, or 151 values of for a Poké, Great, or Ultra Ball. For a Bulbasaur (catch rate 45) with max HP 100 in a Poké Ball, that is about 6.0% at full HP and 46 in 256 (18.0%) at 1 HP. pokered-capture/chance
Value
{
"kind": "example",
"catchRate": 45,
"maxHP": 100,
"ball": "poke",
"status": "none",
"fullHP": 0.0604,
"oneHP": 0.1797
}engine/items/item_effects.asm#L185-L214.loop
call Random
…
ld a, 200
cp b
jr c, .loop
…
ld a, 150
cp b
jr c, .looppokered: disassembly of Pokémon Red and Blue · engine/items/item_effects.asm#L298-L316; If Rand1 - Status > CatchRate, the ball fails to capture the Pokémon.
…
; If W > 255, the ball captures the Pokémon.
…
call Random ; Let this random number be called Rand2.
; If Rand2 > X, the ball fails to capture the Pokémon.pokered: disassembly of Pokémon Red and Blue · data/pokemon/base_stats/bulbasaur.asm#L7db 45 ; catch rateChecked by claude, 2026-10-22 — counted with a script that runs ItemUseBall's integer steps over every R1 and R2: at full HP W = floor(2125 / 25) = 85, so 46/256 × 86/256 ≈ 6.04%; at 1 HP the quarter of HP is 0, raised to 1, so W = 2125 > 255 and only R1 ≤ 45 matters, 46/256Computed: because the Great Ball's ball factor is 8 and the Ultra Ball's is 12, a Great Ball can beat an Ultra Ball. With a catch rate of 200 or more, both first rolls always pass, so the Great Ball never catches less often. For a Bulbasaur (catch rate 45) with max HP 100 at full HP and no status, the Great Ball catches about 11.4% of the time and the Ultra Ball about 10.2%. pokered-capture/great-over-ultra
Value
{
"kind": "example",
"catchRate": 45,
"maxHP": 100,
"hp": 100,
"great": 0.1144,
"ultra": 0.1023
}engine/items/item_effects.asm#L201-L214; If it's a Great/Ultra/Safari Ball and Rand1 is greater than 200, try again.
ld a, 200
cp b
jr c, .loop
…
ld a, [hl]
cp GREAT_BALL
jr z, .checkForAilments
; If it's an Ultra/Safari Ball and Rand1 is greater than 150, try again.
ld a, 150
cp b
jr c, .looppokered: disassembly of Pokémon Red and Blue · engine/items/item_effects.asm#L253-L259; Determine BallFactor. It's 8 for Great Balls and 12 for the others.
ld a, [wCurItem]
cp GREAT_BALL
ld a, 12
jr nz, .skip1
ld a, 8Checked by claude, 2026-10-25 — counted with the same script; Great: 46/201 × 128/256 (W = floor(3187 / 25) = 127) ≈ 11.44%, Ultra: 46/151 × 86/256 ≈ 10.23%; for C ≥ 200 both R1 tests always pass and F = 8 gives a W at least as large as F = 12; read the ball's Rand1 retries at L201-L214 (Great Ball over 200, Ultra over 150)Cited by: Capture
When the ball fails, Red and Blue choose its shakes without a roll: , with 255 for a Poké Ball, 200 for a Great Ball, and 150 otherwise; then , plus 10 for sleep or freeze or 5 for another status. A under 10 is a miss ("You missed the Pokémon!"), under 30 one shake, under 70 two, and 70 or more three ("Shoot! It was so close too!"). pokered-capture/shakes
Value
{
"kind": "shake-display",
"ballDivisor": {
"poke": 255,
"great": 200,
"other": 150
},
"statusAdd": {
"sleepOrFreeze": 10,
"other": 5
},
"thresholds": [
10,
30,
70
]
}engine/items/item_effects.asm#L321-L412.failedToCapture
ldh a, [hQuotient + 3]
ld [wPokeBallCaptureCalcTemp], a ; Save X.
…
; Poké Ball: BallFactor2 = 255
; Great Ball: BallFactor2 = 200
; Ultra/Safari Ball: BallFactor2 = 150
…
; Let Y = (CatchRate * 100) / BallFactor2. Calculate Y.
…
; no status ailment: Status2 = 0
; Burn/Paralysis/Poison: Status2 = 5
…pokered: disassembly of Pokémon Red and Blue · data/text/text_6.asm#L9-L27_ItemUseBallText01::
text "You missed the"
line "#MON!"
…
_ItemUseBallText04::
text "Shoot! It was so"
line "close too!"Checked by claude, 2026-10-22 — read .failedToCapture through the threshold compares (no Random call between them), and the message table at L439-L455 that maps $20 to ItemUseBallText01 and $63 to ItemUseBallText04Cited by: Capture
Before any roll, ItemUseBall makes the ball dodge an unidentified ghost and the Restless Soul Marowak on Pokémon Tower 6F, and catches the old man's demonstration Pokémon outright. pokered-capture/uncatchable
engine/items/item_effects.asm#L149-L175; If the player is fighting an unidentified ghost, set the value that indicates
; the Pokémon can't be caught and skip the capture calculations.
…
; Old Man battle
…
jp .captured
…
cp POKEMON_TOWER_6F
…
cp RESTLESS_SOUL
ld b, $10 ; can't be caught value
jp z, .setAnimDataChecked by claude, 2026-10-22 — read the ghost test, the old man branch, and the Tower 6F Marowak test, all before .loopYellow runs the same capture code as Red and Blue from the first roll to the shake count: the ranges, the status values, the ball factors, and the shake thresholds match line for line. Before the rolls it adds a Pikachu battle (BATTLE_TYPE_PIKACHU), which takes the old man's branch and catches outright. pokered-capture/yellow-same
About: Pokémon Yellow Capture
Codepokeyellow: disassembly of Pokémon Yellow ·engine/items/item_effects.asm#L197-L428.loop
call Random
…
ld a, 200
cp b
jr c, .loop
…
ld c, 12
jr z, .notFrozenOrAsleep
ld c, 25
…
; Determine BallFactor. It's 8 for Great Balls and 12 for the others.
…pokered: disassembly of Pokémon Red and Blue · engine/items/item_effects.asm#L185-L414.loop
call Random
…
ld a, 200
cp b
jr c, .loop
…
ld c, 12
jr z, .notFrozenOrAsleep
ld c, 25
…
; Determine BallFactor. It's 8 for Great Balls and 12 for the others.
…pokeyellow: disassembly of Pokémon Yellow · engine/items/item_effects.asm#L157-L171ld a, [wBattleType]
cp BATTLE_TYPE_OLD_MAN
jr z, .oldManBattle
cp BATTLE_TYPE_PIKACHU
jr z, .oldManBattle ; pikachu battle technically old man battle
…
ld a, [wBattleType]
cp BATTLE_TYPE_OLD_MAN
jp nz, .capturedChecked by claude, 2026-10-22 — diffed ItemUseBall between the pinned pokered and pokeyellow; from .loop to .setAnimData the only differences are two blank lines, and before .loop Yellow adds the BATTLE_TYPE_PIKACHU branchesCited by: Capture