Pokémon Platinum
A Generation IV Pokémon game for Nintendo DS, read through the pret/pokeplatinum decompilation.
On this page
Pokémon Platinum is a Generation IV Pokémon game for Nintendo DS. The code read here is pret’s pokeplatinum decompilation, a work in progress whose battle engine is C and which builds the US release.
Where Pokémon Emerald’s damage formula lets a move’s type pick its stats, Platinum’s reads the move itself: each move carries its own physical or special class, whatever its type (the physical/special split)1. Critical hits keep Emerald’s stage table2, and capture keeps Emerald’s formula and shake checks3.
Mechanics
| Mechanic | Kind | How |
|---|---|---|
| Damage formula | Attack over defense | each move's own class picks Attack or Special Attack, not its type |
| Physical/special split | By move | each move's class, physical, special, or status, whatever its type |
| Critical hit | From stages | Emerald's stages, plus Super Luck; Lucky Chant blocks it, and Sniper makes it ×3 |
| Capture | Four shake checks | Emerald's formula and four shake checks, with the Dusk and Quick Balls added |
| Ability | Technician, Super Luck, Sniper, and Mold Breaker | |
| Held item | the critical-rate items, as in Emerald |
Every claim about this game, by concept (6)
References
- ^In Platinum the damage formula reads each move's own class (
MOVE_DATA(move).class:CLASS_PHYSICAL,CLASS_SPECIAL, orCLASS_STATUS) and uses Attack against Defense for a physical move, Special Attack against Special Defense for a special one, whatever the move's type. Emerald picks the pair by type, below or aboveTYPE_MYSTERYin the type list, so Thunder Punch (Electric) is special in Emerald and physical in Platinum, and Shadow Ball (Ghost) is physical in Emerald and special in Platinum.Codepret/pokeplatinum · pret/pokeemeraldHow we know
12typedef struct MoveTable { 13 u16 effect; 14 u8 class; 15 u8 power;
include/move_table.h lines 12–15 at 49f78f3 · upstream ↗ 1CLASS_PHYSICAL 2CLASS_SPECIAL 3CLASS_STATUS
generated/move_classes.txt lines 1–3 at 49f78f3 · upstream ↗ 6635 attackStat = BattleMon_Get(battleCtx, attacker, BATTLEMON_ATTACK, NULL); 6636 defenseStat = BattleMon_Get(battleCtx, defender, BATTLEMON_DEFENSE, NULL); 6637 spAttackStat = BattleMon_Get(battleCtx, attacker, BATTLEMON_SP_ATTACK, NULL); 6638 spDefenseStat = BattleMon_Get(battleCtx, defender, BATTLEMON_SP_DEFENSE, NULL);
src/battle/battle_lib.c lines 6635–6638 at 49f78f3 · upstream ↗ 6704 moveClass = MOVE_DATA(move).class;src/battle/battle_lib.c line 6704 at 49f78f3 · upstream ↗ 6947 if (moveClass == CLASS_PHYSICAL) { ⋮ 6950 damage = attackStat * sStatStageBoosts[attackStage].numerator; ⋮ 6956 damage = attackStat * sStatStageBoosts[attackStage].numerator; ⋮ 6965 stageDivisor = defenseStat * sStatStageBoosts[defenseStage].numerator; ⋮ 6971 stageDivisor = defenseStat * sStatStageBoosts[defenseStage].numerator; ⋮ 6992 } else if (moveClass == CLASS_SPECIAL) {
src/battle/battle_lib.c lines 6947–6992 at 49f78f3 · upstream ↗ 10 "class": "CLASS_PHYSICAL", 11 "type": "TYPE_ELECTRIC",
res/moves/thunder_punch/data.json lines 10–11 at 49f78f3 · upstream ↗ 10 "class": "CLASS_SPECIAL", 11 "type": "TYPE_GHOST",
res/moves/shadow_ball/data.json lines 10–11 at 49f78f3 · upstream ↗ 466#define IS_TYPE_PHYSICAL(moveType) (moveType < TYPE_MYSTERY) 467#define IS_TYPE_SPECIAL(moveType) (moveType > TYPE_MYSTERY)
include/battle.h lines 466–467 at 731ad5b · upstream ↗ 13#define TYPE_GHOST 7 ⋮ 15#define TYPE_MYSTERY 9 ⋮ 19#define TYPE_ELECTRIC 13
include/constants/pokemon.h lines 13–19 at 731ad5b · upstream ↗ 120 [MOVE_THUNDER_PUNCH] = ⋮ 124 .type = TYPE_ELECTRIC,
src/data/battle_moves.h lines 120–124 at 731ad5b · upstream ↗ 3214 [MOVE_SHADOW_BALL] = ⋮ 3218 .type = TYPE_GHOST,
src/data/battle_moves.h lines 3214–3218 at 731ad5b · upstream ↗ 3232 if (IS_TYPE_PHYSICAL(type))src/pokemon.c line 3232 at 731ad5b · upstream ↗ 3287 if (IS_TYPE_SPECIAL(type))src/pokemon.c line 3287 at 731ad5b · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-22.
Checked by a second AI model, 2026-10-22.
- ^
BattleSystem_CalcCriticalMultikeeps Emerald's stage table, 1 in 16, 8, 4, 3, or 2, capped at stage 4, and its sum: +2 for Focus Energy, +1 for a held item withHOLD_EFFECT_CRITRATE_UP, +2 for Chansey or Farfetch'd holding an item whose hold effect names that species, plus the move's boosts, which in Platinum come from a battle-script variable (criticalBoosts) rather than a list of move effects. It adds +1 for the ability Super Luck, which Emerald's sum doesn't read. Besides Battle Armor and Shell Armor, which an attacker with Mold Breaker ignores, Lucky Chant on the target's side blocks a critical, and so does aMOVE_EFFECT_NO_CRITICALflag that a comment in the header says is checked but never set. Platinum draws its 16-bit roll before those tests, where Emerald draws it only after its other tests pass.Codepret/pokeplatinum · pret/pokeemeraldrevisedHow we know
7096// each value here is implicitly 1 / N 7097static const u8 sCriticalStageRates[] = { 7098 16, // neutral 7099 8, // +1 7100 4, // +2 7101 3, // +3 7102 2, // +4
src/battle/battle_lib.c lines 7096–7103 at 49f78f3 · upstream ↗ 7123 effectiveCritStage = (((attackerVolStatus & VOLATILE_CONDITION_FOCUS_ENERGY) != FALSE) * 2) 7124 + (itemEffect == HOLD_EFFECT_CRITRATE_UP) 7125 + criticalStage 7126 + (attackerAbility == ABILITY_SUPER_LUCK) 7127 + (2 * (itemEffect == HOLD_EFFECT_CHANSEY_CRITRATE_UP && attackerSpecies == SPECIES_CHANSEY)) 7128 + (2 * (itemEffect == HOLD_EFFECT_FARFETCHD_CRITRATE_UP && attackerSpecies == SPECIES_FARFETCHD)); ⋮ 7131 effectiveCritStage = 4; ⋮ 7134 if (BattleSystem_RandNext(battleSys) % sCriticalStageRates[effectiveCritStage] == 0 7135 && Battler_IgnorableAbility(battleCtx, attacker, defender, ABILITY_BATTLE_ARMOR) == FALSE 7136 && Battler_IgnorableAbility(battleCtx, attacker, defender, ABILITY_SHELL_ARMOR) == FALSE 7137 && (sideConditions & SIDE_CONDITION_LUCKY_CHANT) == FALSE 7138 && (defenderMoveEffects & MOVE_EFFECT_NO_CRITICAL) == FALSE) { ⋮
src/battle/battle_lib.c lines 7123–7139 at 49f78f3 · upstream ↗ 3112 if (Battler_Ability(battleCtx, attacker) != ABILITY_MOLD_BREAKER) { 3113 if (Battler_Ability(battleCtx, defender) == ability) { 3114 result = TRUE;
src/battle/battle_lib.c lines 3112–3115 at 49f78f3 · upstream ↗ 2158 battleCtx->criticalMul = BattleSystem_CalcCriticalMulti(battleSys, ⋮ 2162 battleCtx->criticalBoosts,
src/battle/battle_script.c lines 2158–2162 at 49f78f3 · upstream ↗ 9531 case BTLVAR_CRITICAL_BOOSTS: 9532 return &battleCtx->criticalBoosts;
src/battle/battle_script.c lines 9531–9532 at 49f78f3 · upstream ↗ 23#define MOVE_EFFECT_NO_CRITICAL (1 << 15) // checked for, but never setinclude/constants/battle/moves.h line 23 at 49f78f3 · upstream ↗ 1308u16 BattleSystem_RandNext(BattleSystem *battleSys) ⋮ 1311 return (u16)(battleSys->seedRandNext / LCRNG_DIVISOR);
src/battle/battle_system.c lines 1308–1312 at 49f78f3 · upstream ↗ 605// The chance is 1/N for each stage. 606static const u16 sCriticalHitChance[] = {16, 8, 4, 3, 2};
src/battle_script_commands.c lines 605–606 at 731ad5b · upstream ↗ 1267 critChance = 2 * ((gBattleMons[gBattlerAttacker].status2 & STATUS2_FOCUS_ENERGY) != 0) 1268 + (gBattleMoves[gCurrentMove].effect == EFFECT_HIGH_CRITICAL) 1269 + (gBattleMoves[gCurrentMove].effect == EFFECT_SKY_ATTACK) 1270 + (gBattleMoves[gCurrentMove].effect == EFFECT_BLAZE_KICK) 1271 + (gBattleMoves[gCurrentMove].effect == EFFECT_POISON_TAIL) 1272 + (holdEffect == HOLD_EFFECT_SCOPE_LENS) ⋮ 1279 if ((gBattleMons[gBattlerTarget].ability != ABILITY_BATTLE_ARMOR && gBattleMons[gBattlerTarget].ability != ABILITY_SHELL_ARMOR) ⋮ 1282 && !(Random() % sCriticalHitChance[critChance])) 1283 gCritMultiplier = 2;
src/battle_script_commands.c lines 1267–1283 at 731ad5b · 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.
- ^Platinum's
BattleScript_CalcCatchShakeskeeps Emerald's capture math: the same , the same status bonuses (×2 for sleep or freeze; ×15/10 for poison, bad poison, burn, or paralysis), a catch at once when is 255 or more, and otherwise the threshold with up to four 16-bit rolls, each below a shake, and the Master Ball's shakes set to four after the draw. The two square roots run on the DS hardware square-root unit (CP_SetSqrt32) instead of Emerald's GBA BIOS call, and both games roll with the same generator, multiplier 1,103,515,245 and increment 24,691, keeping the top 16 bits. A Pal Park battle or the catching tutorial always catches.Codepret/pokeplatinum · pret/pokeemeraldrevisedHow we know
10904static int BattleScript_CalcCatchShakes(BattleSystem *battleSys, BattleContext *battleCtx) ⋮ 10906 if (BattleSystem_GetBattleType(battleSys) & BATTLE_TYPE_ALWAYS_CATCH) { 10907 return BALL_3_SHAKES_SUCCESS;
src/battle/battle_script.c lines 10904–10907 at 49f78f3 · upstream ↗ 56#define BATTLE_TYPE_ALWAYS_CATCH (BATTLE_TYPE_PAL_PARK | BATTLE_TYPE_CATCH_TUTORIAL)include/constants/battle.h line 56 at 49f78f3 · upstream ↗ 10978 u32 catchRate = (speciesMod * ballMod / 10) 10979 * (battleCtx->battleMons[battleCtx->defender].maxHP * 3 - battleCtx->battleMons[battleCtx->defender].curHP * 2) 10980 / (battleCtx->battleMons[battleCtx->defender].maxHP * 3); ⋮ 10982 if (battleCtx->battleMons[battleCtx->defender].status & (MON_CONDITION_SLEEP | MON_CONDITION_FREEZE)) { 10983 catchRate *= 2; ⋮ 10986 if (battleCtx->battleMons[battleCtx->defender].status & (MON_CONDITION_POISON | MON_CONDITION_BURN | MON_CONDITION_PARALYSIS | MON_CONDITION_TOXIC)) { 10987 catchRate = catchRate * 15 / 10;
src/battle/battle_script.c lines 10978–10988 at 49f78f3 · upstream ↗ 10990 int shakes; 10991 if (catchRate >= 255) { 10992 shakes = BALL_3_SHAKES_SUCCESS; 10993 } else { 10994 u32 sqrtRate = (0xFF << 16) / catchRate; 10995 CP_SetSqrt32(sqrtRate); 10996 CP_WaitSqrt(); ⋮ 10998 sqrtRate = CP_GetSqrtResult32(); 10999 CP_SetSqrt32(sqrtRate); 11000 CP_WaitSqrt(); ⋮ 11002 catchRate = CP_GetSqrtResult32(); 11003 catchRate = (0xFFFF << 4) / catchRate; ⋮
src/battle/battle_script.c lines 10990–11013 at 49f78f3 · upstream ↗ 8#define BALL_3_SHAKES_SUCCESS 4include/constants/battle/battle_script.h line 8 at 49f78f3 · upstream ↗ 1308u16 BattleSystem_RandNext(BattleSystem *battleSys) ⋮ 1310 battleSys->seedRandNext = battleSys->seedRandNext * LCRNG_MULTIPLIER + LCRNG_INCREMENT; 1311 return (u16)(battleSys->seedRandNext / LCRNG_DIVISOR);
src/battle/battle_system.c lines 1308–1312 at 49f78f3 · upstream ↗ 8#define LCRNG_MULTIPLIER 1103515245L 9#define LCRNG_INCREMENT 24691 10#define LCRNG_DIVISOR 65536L
include/math_util.h lines 8–10 at 49f78f3 · upstream ↗ 9987 odds = (catchRate * ballMultiplier / 10) 9988 * (gBattleMons[gBattlerTarget].maxHP * 3 - gBattleMons[gBattlerTarget].hp * 2) 9989 / (3 * gBattleMons[gBattlerTarget].maxHP); ⋮ 9991 if (gBattleMons[gBattlerTarget].status1 & (STATUS1_SLEEP | STATUS1_FREEZE)) 9992 odds *= 2; 9993 if (gBattleMons[gBattlerTarget].status1 & (STATUS1_POISON | STATUS1_BURN | STATUS1_PARALYSIS | STATUS1_TOXIC_POISON)) 9994 odds = (odds * 15) / 10; ⋮ 9998 if (gLastUsedItem == ITEM_MASTER_BALL) ⋮ 10009 if (odds > 254) // mon caught ⋮ 10025 odds = Sqrt(Sqrt(16711680 / odds)); 10026 odds = 1048560 / odds; ⋮ 10028 for (shakes = 0; shakes < BALL_3_SHAKES_SUCCESS && Random() < odds; shakes++); ⋮
src/battle_script_commands.c lines 9987–10031 at 731ad5b · upstream ↗ 20u16 Sqrt(u32 num);include/gba/syscall.h line 20 at 731ad5b · upstream ↗ 72 .ifdef L_Sqrt 73 thumb_func_start Sqrt 74Sqrt: 75 svc #8
libagbsyscall/libagbsyscall.s lines 72–78 at 731ad5b · upstream ↗ 16#define ISO_RANDOMIZE1(val) (1103515245 * (val) + 24691)include/random.h line 16 at 731ad5b · upstream ↗ 11u16 Random(void) ⋮ 13 gRngValue = ISO_RANDOMIZE1(gRngValue); ⋮ 15 return gRngValue >> 16;
src/random.c lines 11–16 at 731ad5b · 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
- pret contributors. 2026. “pokeplatinum: decompilation of Pokémon Platinum.” pret. Commit 49f78f3. pret/pokeplatinum @ 49f78f3 · licence, all uses
- pret contributors. 2026. “pokeemerald: decompilation of Pokémon Emerald.” pret. Commit 731ad5b. pret/pokeemerald @ 731ad5b · licence, all uses