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
- Bail checks — untargetable or friendly-green target? Stop.
- 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. - 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.
- Aggregate per component;
MinHPAfterDamageclamps results so a hit can't drop the target below a floor. - 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. - Redirection shunts a percentage to a protection target.
- 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).