Logo

Damage Formula & Integer Rounding - How Polished Crystal Calculates Damage

A technical reference for Polished Crystal's exact damage formula, its integer rounding order, and how it differs from Gen 9 / Pokémon Showdown's damage calculation.

Author: Cammy
Last updated: July 9, 2026

Polished Crystal runs on the Game Boy Color and does all of its damage math with integer arithmetic, flooring at specific points in a specific order. If you implement the formula with floating-point math (or with Gen 9's rounding rules), your results will drift from the real game by a point or two in many matchups - enough to get a KO range wrong.

This page documents the exact pipeline as implemented in the game source (engine/battle/effect_commands.asm, engine/battle/abilities.asm, home/math.asm), and then summarizes how it differs from Gen 9 / Showdown. It is written for anyone re-implementing the formula (e.g. Polished Showdown); the damage calculator tool on this site follows this pipeline.

The one primitive: MultiplyAndDivide

Almost every modifier in Polished Crystal goes through a single routine, MultiplyAndDivide (home/math.asm). It takes a fraction packed into one byte ($xy = multiply by x, divide by y) and applies it to the current running damage value:

damage = floor((damage * x) / y)

Two things matter here:

  1. Each modifier floors once, immediately after its own division. Modifiers are not combined into one big multiplier first.
  2. Because the nibbles only go up to 15, every modifier is a small rational: 1.5× is 3/2, 1.3× is 13/10, 2.25× is 9/4, 2/3 is 2/3, and so on.

So a chain of modifiers is floor(floor(floor(d·a/b)·c/d)·e/f)…, applied in the fixed order below.

The full pipeline, in order

Step 1 - Stats (16-bit) and the 8-bit truncation

BattleCommand_damagestats loads the raw attack and defense stats (16-bit), then applies stat-side modifiers:

  • Weather defense boosts (defender): Hail gives Ice-types 1.5× Defense, Sandstorm gives Rock-types 1.5× Sp.Def - computed as def + floor(def/2).
  • Screens: Reflect/Light Screen double the raw defense stat (def * 2). Skipped if the attack is a crit, the attacker has Infiltrator, or the move is Brick Break.
  • Items: Thick Club / Light Ball double attack (atk * 2); Metal Powder (Ditto) and Eviolite (unevolved) give 1.5× defense (def + floor(def/2)).

Then comes the most exotic step, TruncateHL_BC: the Game Boy passes attack and defense to the core formula in 8-bit registers, so while either stat is above 255, both stats are halved together (floor), with a minimum of 1. A 400 Atk vs 300 Def matchup actually computes as 200 vs 150. This preserves the ratio approximately, but introduces its own rounding - and it means big stats lose precision that Gen 9 never loses.

Stat stages are not applied here. See Step 3.

Step 2 - Base damage passes 1–2

d = floor(2 * Level / 5) + 2
d = d * Power
d = d * Attack        (the truncated 8-bit attack)

No division has happened yet, so d is at its largest here. The game deliberately applies most modifiers at this point, before dividing by Defense and 50, so that flooring costs almost nothing.

Step 3 - Stat stages (applied to damage, not stats!)

Attack and Defense stages are applied to the running damage value via MultiplyAndDivide, using the standard stage fractions:

  • Attack stage +n(2+n)/2; -n2/(2+n)
  • Defense stage is applied inverted: a defender at +2 Def multiplies your damage by 2/4.

Rules layered on top:

  • Crits skip the attacker's negative stages and the defender's positive stages (positive attack stages and negative defense stages still count).
  • Unaware (defender) skips the attacker's attack stages; Unaware (attacker) skips the defender's defense stages.

Step 4 - First ability pass, burn, Flash Fire, crit, items

Still before any division, each of these applies via MultiplyAndDivide in this order (BattleCommand_damagecalc / ApplyDamageAbilities):

  1. Offensive abilities: Technician 3/2 (BP ≤ 60), Huge Power 2/1 (physical), Hustle & Gorilla Tactics 3/2 (physical), Overgrow/Blaze/Torrent/Swarm 3/2 (≤⅓ HP, matching type), Rivalry 5/4 or 3/4, Sheer Force 13/10, Analytic 13/10, Solar Power 3/2, Iron Fist 6/5, Tough Claws 13/10, Mega Launcher 3/2, Sand Force 13/10, Reckless 6/5, Guts 3/2, -ate abilities 6/5, Steely Spirit 3/2, Sharpness 3/2.
  2. Defensive abilities: Multiscale 1/2, Marvel Scale 2/3, Thick Fat 1/2, Dry Skin 5/4 (Fire), Fur Coat 1/2, Fluffy (2/1 for Fire, 1/2 for contact
    • both apply to a Fire contact move). Note: "physical defense" mods (Marvel Scale, Fur Coat) also apply against Psystrike.
  3. Burn: 1/2 for physical moves (skipped with Guts or Facade).
  4. Flash Fire: 3/2 when boosted and using a Fire move.
  5. Critical hit: 3/2, or 9/4 with Sniper.
  6. Attacker's item: type-boost items 6/5, Muscle Band / Wise Glasses 11/10, Choice Band/Specs 3/2, Metronome (5+n)/5, Punching Glove 11/10, Life Orb 13/10.
  7. Defender's item: Assault Vest 2/3 vs special moves.

Step 5 - Base damage passes 3–4

d = floor(d / Defense)   (truncated 8-bit defense, screens/items included, NO stages)
d = floor(d / 50)
d = d + 2                (capped at 65535)

Step 6 - Type effectiveness, STAB, and weather: the ×16 byte

This is the part that surprises people most. Effectiveness, STAB, and the sun/rain move boost are all folded into a single one-byte fixed-point multiplier, wTypeMatchup, where $10 (16) = 1.0×:

  • Type effectiveness sets it: 4 (¼×), 8 (½×), 16 (1×), 32 (2×), 64 (4×). 0 means immune → damage is zeroed and the move fails.
  • STAB: matchup += floor(matchup / 2); Adaptability doubles instead.
  • Weather (sun/rain move type boost/penalty): + floor(matchup/2) or floor(matchup / 2).

Damage is multiplied by this byte, and then - while still at the ×16 scale - these apply via MultiplyAndDivide:

  1. Parental Bond second hit: 1/4
  2. Tinted Lens: 2/1 (when not very effective); Filter / Solid Rock: 3/4 (when super effective)
  3. Expert Belt: 6/5 (when super effective)

Only after all of those does the game do a single floor(d / 16). Applying these at the raised scale means they round less than they would post-division. If the result is 0 (and the target isn't immune), it becomes 1.

Step 7 - Damage roll

If damage is ≥ 2: d = floor(d * R / 100) where R is uniform in 85–100 inclusive (16 possible rolls). Damage of 0 or 1 skips the roll entirely.

How this differs from Gen 9 or Showdown

AspectPolished CrystalGen 9 (Showdown)
Modifier arithmetic
Small rationals (x/y, nibbles), floor after each modifier
4096-based fixed point, chained mods use pokeRound (round half down), applied once
Stat stages
Applied to the running damage as (2+n)/2 fractions, mid-formula
Applied to the stat itself before the base formula
Ability/item boosts
Applied before /Def, /50, +2 (on the huge intermediate product)
Mostly applied to stats, base power, or the final damage after the base formula
Stat size limit
Attack & Defense truncated together to 8 bits (halved until ≤ 255)
No truncation; full precision
Screens
Double the defense stat (skipped on crit/Infiltrator/Brick Break)
Final damage ×0.5 (2732/4096)
Type/STAB/weather
One shared ×16 fixed-point byte, single /16 at the end
Separate multipliers; STAB is a 6144/4096 final mod, effectiveness directly multiplies with flooring between steps
Crit timing
Mid-formula, before /Def and /50
1.5× applied to base damage after the core formula, before random factor
Random factor
85–100 (16 rolls), applied last, skipped if damage < 2
85–100 (16 rolls), applied before STAB/effectiveness/burn/final mods
Burn
×1/2 mid-formula (before /50)
×0.5 near the end of the modifier chain
Minimum damage
Forced to 1 after the /16 type step
Forced to 1 at the end

The practical consequences:

  • Order matters more than the fractions. Gen 9 applies the random roll early and effectiveness/STAB late; Polished applies every modifier before its divisions where possible and rolls last. Two implementations using the "same" multipliers will disagree by 1–3 points constantly if the order and floor placement differ.
  • High-stat matchups diverge extra because of the 8-bit truncation - a mechanic with no Gen 9 equivalent at all.
  • pokeRound vs floor: Showdown's chained modifiers round half down at 4096 scale; Polished floors every intermediate. Even a single 1.5× modifier can differ by 1 between the two schemes.

Worked example

Level 50, 80 BP physical move, 120 Atk vs 100 Def, STAB, neutral effectiveness, no other modifiers:

floor(2*50/5) + 2         = 22
22 * 80 * 120             = 211200
floor(211200 / 100)       = 2112
floor(2112 / 50)          = 42
42 + 2                    = 44
type byte: 16 + floor(16/2) = 24 (STAB)
floor(44 * 24 / 16)       = 66
rolls: floor(66 * 85/100) … floor(66 * 100/100) = 56 … 66

A float implementation of "44 × 1.5 = 66" happens to agree here, but shift any input and the floors start to bite - e.g. 43 + 2 = 45 → floor(45·24/16) = 67, where 45 × 1.5 = 67.5 rounds to 68 under half-up rounding.

Source references: BattleCommand_damagestats, TruncateHL_BC, BattleCommand_damagecalc, DamagePass1–4, BattleCommand_stab, BattleCommand_damagevariation in engine/battle/effect_commands.asm; ApplyDamageAbilities in engine/battle/abilities.asm; MultiplyAndDivide in home/math.asm; DoWeatherModifiers in engine/battle/misc.asm.

Was this useful?

Loading votes...