Your chance to hit a monster, and a monster's chance to hit you, with block, dodge, avoid and evade. Worked out with PD2's own hit roll, read out of the game's code.
Every melee attack and every missile that can miss goes through one roll in ProjectDiablo.dll (0x10271740), called from PD2's own attack resolver (0x10270EB0) in place of the base game's roll. The steps, in order:
The defender's defense (dexterity ÷ 4 plus armor, times the % bonuses), plus defense vs melee or vs missile. Ignore Target's Defense sets it to 0 against normal monsters, champions and minions at any level (there's no level condition); not against uniques, superuniques, bosses (every monster flagged as a boss in the game data: act bosses, ubers, map bosses and more), mercenaries or players. −% target defense is capped at 100 and halved against players, bosses, superuniques and mercenaries; your curses and auras (Conviction, Weaken, Battle Cry, Cloak of Shadows, Attract, Inner Sight) change the monster's defense itself.
"−X to Monster Defense Per Hit" isn't part of the roll. Each hit that lands (at the start of the swing, after the block check) permanently lowers the monster's base defense by X, down to 0 (D2Game 0x6FCFA890, still used by PD2). Later rolls see the lower defense. Enter it with the number of hits already landed.
Your base AR (attack rating + 5 × dexterity − 35 + your class's bonus). +AR vs demons or undead is added here. Then all the % at once: attack rating %, the skill's attack bonus, weapon mastery (melee only) and % vs monster type. The character screen shows your AR with the same % applied, so for most attacks the screen number is the one used.
PD2 multiplies by 111 where the base game used 100, so everyone hits a bit more often.
Below 5 becomes 5. Up to 95 it stays as is. Above 95 the extra ratio only counts at 90/111, and the result stops at 100. So 100% is reachable; the base game stopped at 95.
A number from 0 to 99 from PD2's own random generator; the attack hits when it's below the chance. Every division drops the remainder.
Only after a hit. Shield block first (your character-screen block, capped at 75; PD2 has no penalty for moving). Then weapon block (two-handed swords and two claws, capped at 75), then evade if you're walking or running (if you're moving with no evade, dodge and avoid aren't tried), then dodge for melee or avoid for missiles. On PvP maps these three are halved.
Open any of these to see exactly what it does, when it applies and its limits.
Addresses are virtual addresses in the shipped DLLs: D2Game.dll at 0x6FC20000, D2Common.dll at 0x6FD50000, ProjectDiablo.dll (PD) at 0x10000000. VERIFIED means the real code was run natively on generated inputs and matched this page's code exactly; READ means read from the disassembly; DATA means taken from PD2's .txt tables.
| Melee start (srvstfunc) | D2Game 0x6FC64BE5 | calls the stock resolver 0x6FCFE5A0, which PD2 redirects (31 of 34 call sites, via wrapper PD 0x102EEDA0) |
| Resolver | PD 0x10270EB0 | (defender, attacker, …): melee range PD 0x10270FF0 → hit roll → block chain; returns the hit flags |
| Hit roll | PD 0x10271740 | levels (stat 12), defense (#10672 + stat 33/32), player AR (#10621), target adjustments, AR %, chance, roll |
| Target adjustments | PD 0x10271620 | stat 115 ITD, stat 116 −% target defense; boss test D2Common #10064 (PD 0x10278580), hireling test #11104 (PD 0x102784F0) |
| Weapon mastery | PD 0x102727D0 | stat 342 melee, 345 throwing |
| AR vs monster type | PD 0x10279790 / 0x102EFB10 | stat 179 entries matched against MonType |
| AR % | PD 0x102CE840 | double: bonus = AR × pct ÷ 100, truncated, clamped to int32 |
| Chance | PD 0x10271470 | 111 ratio, level factor, 5 floor, soft cap above 95, 100 cap |
| Random roll | PD 0x102C5D10 | PD2's own generator on the attacker's seed; hit if value % 100 < chance |
| Block / dodge / avoid / evade | PD 0x1026FB60 → 0x1026FCF0 | defender's seed; any success clears the hit bit (PD 0x10270FB9) |
| Damage store | D2Game 0x6FCFDDE0 | runs only with the hit bit set; applies −defense per hit (0x6FCFD450 → 0x6FCFA890) |
Data: PD2 data.zip (MonStats, MonStats2, MonLvl, Levels, MonUMod, MonType, SuperUniques, Skills, Missiles, ItemStatCost). The calculator's code is the same module the tests run (window.PD2Hit).