Anatomy of a hit: between the d20 and the damage number

From the game's own decompiled rules code — logic/damage-model.json (the RuleDealDamage → RulePrepareDamage → RuleCalculateDamage pipeline, step-attributed), logic/core-mechanics.json, and logic/damage-dice-summary.json (3,571 dice formulas) — build 20212986, patch 2.7.0x.

You roll a 17, the UI says "hit", and a number pops. Between those two moments the code runs a seven-step pipeline that players never see. Here is what it actually does.

Building the attack bonus

Attack bonus is assembled, not stored (core-mechanics.json#attackBonus):

attack = (BAB − iterative penalty) + attack-stat mod + stacked bonuses (feats/buffs/size/morale) + weapon enhancement + situational

Three details worth knowing:

  • The attack stat is per-weapon. Each weapon blueprint names its AttackBonusStat — melee defaults STR, ranged DEX, and replacement effects (Weapon Finesse, Zen Archery) swap it contextually.
  • Enhancement doesn't stack with itself: the weapon's enhancement bonus applies only where it exceeds an existing enhancement-class modifier.
  • Iteratives fall out of BAB arithmetic (RuleCalculateAttacksCount.cs): extra attacks at BAB 6/11/16, swinging at −5/−10/−15. Secondary natural attacks take −5 (−2 with the right feat; a Demon mythic power removes the penalty entirely).

The damage pipeline, verbatim from the decompile

  1. Bail checks — untargetable or friendly-green target? Stop.
  2. Sneak damage (RulePrepareDamage.cs) — if the initiator has a SneakAttack stat and the target is flanked or flat-footed, add SneakAttack×d6 precision damage. Knife Master's hidden blade rolls d8s with knives and d4s with everything else; surprise spells sneak too.
  3. Per damage component (RuleCalculateDamage.cs):
    • Crits multiply dice only — flat bonuses are never multiplied. Crit immunity forces the multiplier to 1; on player-faction targets, the difficulty preset gates enemy crits entirely (and non-Normal presets make enemy crits dice-only).
    • Maximized/Minimized are exact: rolls×sides, or just rolls.
    • Empower multiplies the rolled value including flat bonuses — measurably stronger than tabletop's dice-only reading.
    • Halving chains: explicit half flags halve, save-for-half multiplies ×0.5 — except Azata Favorable Magic, which pays ×0.75.
    • Difficulty "decline" options cut damage ×0.75 / ×0.5; everything floors at 1.
    • Final value = floor(rolled × (1+BonusPercent/100) × Vulnerability × Durability × tactical factor × (1+ModifierBonus)). Full immunity zeroes the component.
  4. Aggregate per component; MinHPAfterDamage clamps results so a hit can't drop the target below a floor.
  5. Difficulty multiplier — enemy damage dealt to your party is multiplied by the preset's DamageToParty (0.2 on Story, 2.0 on Unfair), minimum 1 damage.
  6. Redirection shunts a percentage to a protection target.
  7. Temporary HP absorbs first — damage eats your THP before it ever touches real hit points.

In tactical (army) combat the same code swaps in squad math: dice rolls scale with unit count, and the hit factor is max(1, d20 + attackBonus − targetAC) × 0.05, halved by concealment or mirror image.

What the dice census says

3,571 damage/heal dice formulas ship in the client. D6 owns more than half of them (1,803), ahead of D8 (487), D4 (295), D10 (157), and D12 (65). Ownership: 2,040 belong to abilities, 917 to buffs, 300 to area effects, 256 to features, and just 56 to weapon enchantments — the game's damage economy lives in spells and statuses, not in weapons.

Browse per-spell and per-class numbers in our Classes and Spells sections.

Sources

  • logic/damage-model.json — the seven-step pipeline, each step pinned to its decompiled method.
  • logic/core-mechanics.json — attack assembly, iteratives, sneak triggers, attacks of opportunity (RuleAttackRoll.cs, RuleAttackWithWeapon.cs).
  • logic/damage-dice-summary.json — the 3,571-formula census.
  • Build: 20212986 (patch 2.7.0x).

Related database section: Classes — e.g. Dragon.