PD2 IAS Calculator: Frames Per Attack
Frames per attack, IAS breakpoints and werewolf / werebear speed, worked out with the same animation data and timing rules the game itself uses.
Loading animation data…
Fetching the animation, weapon and skill tables.Fastest held attacks, per class and form
For every class in human, werewolf and werebear form: the two fastest frame counts a held normal attack can reach, and every weapon setup that reaches them, with the IAS each one needs. The IAS figures assume no speed skills; Fanaticism, Frenzy, Burst of Speed or the Werewolf skill's own bonus lower them (the frame counts stay the same, since they are set by the 175 speed cap). Open a row and add your skill levels to see your own numbers. Click a class, a form or a frame count to open it.
What these numbers assume
- Normal attack (A1), held. Skill attack speed is 0, so auras such as Fanaticism, Frenzy or the Werewolf skill's own bonus would lower the IAS needed.
- Human: the IAS shown is your total from every source. Dual wielding is included for Barbarian and Assassin, using PD2's faster-weapon rule.
- Forms: IAS on the weapon is limited to the most the item data allows on that base: crafted, rare or unique affixes, Shael in sockets, and one corruption. Corruption on a white weapon gives 1–6 sockets and never IAS; on a magic, rare, crafted, set or unique weapon it gives 3–6 sockets for bows, crossbows and two-handers, 2–4 for everything else, or up to 40 IAS. "Other IAS" is what the rest of your gear must add on top.
- Forms for non-Druids need The Beast (the only werebear/werewolf source in PD2's data) and then a swap to the listed weapon. Two things behind this are not yet traced in the code: that the form survives the swap, and that socketed IAS counts as IAS on the weapon.
- Class-only bases (Amazon bows and spears, Assassin claws, Sorceress orbs, the 2H Phase Blade) only appear for their class.
Loading the fastest setups…
How the game turns IAS into frames
Diablo II runs at 25 game frames per second. Every attack is an animation, and the game moves that animation forward by a fixed amount each frame. IAS makes that amount bigger, so the animation finishes in fewer frames. Everything below follows that chain:
Add up your IAS
Take every "Increased Attack Speed" you have: on the weapon, on armor and jewelry, in charms and jewels, and from the Barbarian's Increased Speed passive. In human form it all goes into one pile.
Apply diminishing returns (EIAS)
The game shrinks large amounts of IAS. The result is called effective IAS (EIAS), and it can never reach 120.
EIAS = 120 × IAS ÷ (120 + IAS), rounded down20 IAS → 17 · 40 → 30 · 60 → 40 · 100 → 54 · 150 → 66 · 200 → 75 · 400 → 92
Work out your speed
Start at 100. Add skill speed (Fanaticism, Frenzy, Burst of Speed, the Werewolf bonus; slows like Decrepify count as negative), subtract the weapon's speed modifier (WSM, so a −30 weapon adds 30), and add EIAS. The result is kept between 15 and 175. Skill speed grows quickly at low levels and then flattens: Fanaticism gives 25 at level 5 and 35 at level 20; Werewolf gives 45 at level 5 and 68 at level 20. Set your skill levels in the calculator's Speed skills box. Sequence skills such as Frenzy, Whirlwind or Dragon Claw take another 30 off. Two skills change the speed while they play: Dragon Tail takes 20 off, and Double Swing adds 50 on your client.
speed = 100 + skill speed − WSM + EIAS (15 to 175)Look up the animation
Each class has its own attack animation for each kind of weapon, with a length in frames and a base speed (usually 256). Two-handed swords use the two-handed sword animation for every class, except a Barbarian holding one with a shield or a second weapon. Amazons and Sorceresses skip the first 1 or 2 frames of their normal attack with most melee weapons.
Zeal, Fury, Fend, Strafe and Dragon Talon hit several times in one use. Each hit sends the animation back (to the start for Zeal, Fury and Dragon Talon), so every swing but the last is cut short at its hit frame and the last plays in full. The calculator shows these as swing lengths such as 4/4/8.
Rate per frame
The animation moves forward by "rate" each game frame, where 256 equals one animation frame.
rate = animation speed × speed ÷ 100, rounded downCount the frames
The attack ends once it has moved through the whole animation. When you hold the attack button, your game ends its copy one frame early and sends the next attack straight away, which is where the − 1 comes from.
frames = ⌈(animation frames − start frame) × 256 ÷ rate⌉ − 1
PD2 changes dual wielding. Only the faster of the two weapons counts, using its own WSM and the IAS on it; IAS on the other weapon is ignored. The original game averaged the two WSMs.
Work out your speed, exactly as in human form
All your IAS, weapon and gear alike, goes through diminishing returns and into speed, capped at 175. The Werewolf skill also gives its own attack speed bonus, which adds to speed directly like other skill speed.
speed = 100 + skill speed − WSM + EIAS(all IAS) (15 to 175)Time your normal attack with this weapon
The form does not use the wolf's own animation speed. It compares against how long your character's normal attack takes with the weapon you hold, using only the weapon: its WSM and the IAS on it. Gear IAS is not part of this step.
weapon score = 100 − WSM + IAS on the weapon normal time H = normal frames × 256 ÷ (weapon score × normal speed ÷ 100)With no weapon, H is 19. This uses your own class's animation, so it works for any class using an item that grants the form.
Turn that into the form's base speed
The game divides the length of the animation you were in by H. When you hold attack, you start each swing from standing, and the wolf's standing animation is 9 frames long.
base speed = 9 × 256 ÷ HThe server repeats this with the attack's real length (13 frames) and gets a faster number, but your game keeps the standing value, and your game is what paces held attacks. The first swing straight out of a run uses the run animation (8 frames) instead.
Rate per frame
rate = base speed × speed ÷ 100Count the frames
The wolf's attack is 13 frames long.
frames = ⌈13 × 256 ÷ rate⌉ − 1
IAS on the weapon counts twice: once in the weapon score and once in speed. IAS from gear counts only in speed. That is why weapon IAS is worth much more in form, and why gear IAS stops helping once speed reaches 175.
Work out your speed, exactly as in human form
All IAS goes through diminishing returns into speed, capped at 175. Werebear gives no attack speed bonus of its own.
speed = 100 + skill speed − WSM + EIAS(all IAS) (15 to 175)Time your normal attack with this weapon
Same as the werewolf: only the weapon's WSM and the IAS on it count here.
weapon score = 100 − WSM + IAS on the weapon normal time H = normal frames × 256 ÷ (weapon score × normal speed ÷ 100)With no weapon, H is 19.
Turn that into the form's base speed
The bear's standing animation is 10 frames long, one more than the wolf's.
base speed = 10 × 256 ÷ HThe server uses the attack's real length (12 frames) instead; your game keeps the standing value and paces held attacks. A first swing straight out of a run uses the run animation (8 frames).
Rate per frame
rate = base speed × speed ÷ 100Count the frames
The bear's attack is 12 frames long.
frames = ⌈12 × 256 ÷ rate⌉ − 1
The fastest bear attack is 2 frames, and only an Amazon with a bow reaches it, after taking bear form through The Beast and swapping weapons. A crafted bow needs 171 IAS on the bow; Cliffkiller needs 191. One-handed weapons fall short because a corruption can only add 2 to 4 sockets to a magic, rare, crafted or unique one-hander. Those builds also depend on two things not yet traced in the code: that the form survives the weapon swap, and that socketed IAS counts as IAS on the weapon.
Developer reference
Scope and status
Everything on this page comes from the Project Diablo 2 client install: the stock Diablo II 1.13c DLLs (D2Common.dll, D2Game.dll, D2Client.dll, built 2010-03-08) with PD2's changes applied at runtime by ProjectDiablo.dll, plus PD2's data.zip. No community formulas were used. Addresses are virtual addresses at each DLL's preferred base (D2Common 0x6FD50000, D2Game 0x6FC20000, D2Client 0x6FAB0000).
Each rule is marked by how it was established:
- VERIFIED the game's own machine code was loaded at its base address in a test harness, called directly on the CPU with random or exhaustive inputs, and matched the formula on every case. Where a function needs game systems the harness does not have (inventory pages, the timer queue, item-stat attachment), only those calls are replaced by small stand-ins, and each section says which.
- READ traced from the disassembly but not executed.
- OPEN not settled yet.
PD2's online servers could in principle run different server code. The client computes the same animation rate locally, and the client is what paces held attacks in form.
Integer rules
- All arithmetic is 32-bit integer. Signed division truncates toward zero (
idiv), which matters for negative IAS. - The rate multiply-and-divide is unsigned:
rate = floor(base × speed / 100), capped at0x7FFF. - Animation positions and lengths are 8.8 fixed point: one animation frame is 256.
- IAS of exactly −120 divides by zero inside the game. A rate of 0 means the attack never ends.
Data you need
| Source | What to read |
|---|---|
data/global/AnimData.d2 | 256 hash buckets, each uint32 count followed by count records of 160 bytes: char name[8], uint32 framesPerDirection, uint32 speed, uint8 events[144]. Keys are TOKEN + MODE + WEAPONCLASS, e.g. BAA11HS, 40A1HTH. Player tokens: AM SO NE PA BA DZ AI. Werewolf 40, werebear TG. A missing key falls back to 2048 frames at speed 256. |
Weapons.txt | speed (WSM; the item carries stat 68 attackrate = −speed), wclass, 2handedwclass, 1or2handed, type. |
Skills.txt | anim (A1, A2, S1–S4, TH, KK, SQ…) and seqnum for sequence skills. Skill auras that add attackrate (Fanaticism, Frenzy, Werewolf, Burst of Speed, slows) are skill speed; the passive item_fasterattackrate (Increased Speed) is ordinary IAS. |
| D2Common tables | Start frames 0x6FDE4480, weapon-class index 0x6FDEF028, sequence lengths 0x6FDECF40, diminishing-returns table 0x6FDE4608. Values are listed under Extracted tables. |
Speed VERIFIED
D2Common 0x6FD83110 computes the rate for every mode. For attack modes:
EIAS = tdiv(120 * IAS_total, 120 + IAS_total) // 0x6FD823E0, table 0x6FDE4608 {stat 93, k 120} s = attackrate + EIAS // attackrate = 100 base + weapon stat68 (−WSM) + skill auras if (mode == SQ) s -= 30 // sequence skills, players only s = clamp(s, 15, 175) rate = min(floor(animSpeed * s / 100), 0x7FFF) // stored at unit+0x4C
- Base
attackrateis 100, set when the character loads (D2Game0x6FC760C6). The weapon adds its stat 68, which is −WSM. - PD2 dual wield VERIFIED (D2Common +0x334AC is replaced by a call to PD
0x102EEBF0→0x10268A90; PD2's own code was run on 60,000 weapon pairs): for players holding two weapons, pick the one with the largerstat68 + its own stat93, detach the other weapon's stats, and computes = attackrate + EIASwith only the faster weapon attached. On a tie the second (left-hand) weapon is used. The stock game useds += (w1.stat68 + w2.stat68)/2 − w1.stat68. - The same table drives FHR, FCR, FBR (k = 120) and FRW (k = 150) for other modes.
Speed skills VERIFIED
Skills that change attack speed put a value into the attackrate stat through their aura stats in Skills.txt; it is added straight to s, next to −WSM, without passing through the IAS curve. Values from several skills add up. The Barbarian's Increased Speed is different: it is a passive item_fasterattackrate (stat 93), so it is ordinary IAS and goes through EIAS. In form it is gear IAS, not weapon IAS.
// "dm" calc: D2Common 0x6FD9DC30, args (lvl, min, max). Run natively on 20,792 inputs, 0 mismatches. dm(lvl, a, b) { t = tdiv(110*lvl, lvl+6); v = tdiv(t*(b-a), 100) + a; return v > b ? b : v; } Fanaticism attackrate = dm(lvl, 10, 40) // dm34: Param3 = 10, Param4 = 40 Werewolf attackrate = dm(lvl, 10, 80) // skill "Wearwolf", dm34 Frenzy attackrate = dm(lvl, 0, 50) // dm56: Param5 = 0, Param6 = 50 Burst of Speed attackrate = tdiv((110*blvl)*(60-15), 100*(blvl+6)) + 15 // Assassin skill (internal name Quickness); base level only Increased Speed IAS = blvl * 2 // passive item_fasterattackrate; base level only
lvl includes +skills, blvl is hard points only. The Skills.txt parameters are read from the skill record at +0x148…+0x164 (Param1–8) by the calc dispatcher around 0x6FDBA939. Holy Freeze and Decrepify subtract from attackrate (Holy Freeze −dm(lvl, 25, 60); Decrepify −min(Param6 + lvl + Curse Mastery blvl, 60)); the calculator takes those as a manual negative value. Feral Rage and Hunger only change velocitypercent (movement).
| Level | Fanaticism | Werewolf | Frenzy | Burst of Speed (hard) | Increased Speed IAS (hard) |
|---|---|---|---|---|---|
| 1 | 14 | 20 | 7 | 22 | 2 |
| 5 | 25 | 45 | 25 | 37 | 10 |
| 10 | 30 | 57 | 34 | 45 | 20 |
| 15 | 33 | 64 | 39 | 50 | 30 |
| 20 | 35 | 68 | 42 | 53 | 40 |
| 25 | 36 | 71 | 44 | – | – |
| 30 | 37 | 73 | 45 | – | – |
| 40 | 38 | 76 | 47 | – | – |
Every item in PD2's data that grants one of these skills (UniqueItems, SetItems, Sets, Runes, MagicPrefix/Suffix and CubeMain were all scanned for skill parameters):
| Item | Grants | Who it speeds up |
|---|---|---|
| The Beast (runeword) | Fanaticism aura, level 8–10 | Any class, and the party |
| Wolfhowl (unique helm) | +4–6 Werewolf (oskill) | Any class, in werewolf form |
| Hustle (armor runeword) | equipped-skill Quickness SelfAura (Burst of Speed), level 6 | Any class: +15 (see below) |
| Fury (runeword) | +5 Frenzy | Barbarian only (class skill bonus) |
| Chaos (runeword) | +2 Blade Dance | Assassin only; a sequence skill, not a speed bonus |
| Doomsayer, Ice, Shattered Wall | Holy Freeze aura | Slows enemies, not you |
| Delirium, Dream, Exile's Path, Lawbringer, Fetid Sprinkler, Executioner's Justice, magic "of Confusion/Decrepification" | Confuse / Decrepify procs | Act on enemies, not you |
Hustle. PD2's handler for stat 191 item_skillonequip (PD 0x102C6030) adds the skill with D2Common #10302(unit, skill, 0, …), which sets the skill's base level (skill+0x28) to 0, then starts it as an item aura at level 6 (D2Game 0x6FCCDB30). The calc keyword blvl returns that base level (D2Common 0x6FDA14C6), so ((110·0)·45)/(100·6) + 15 = 15 for every class. The Assassin's own Burst of Speed (internal name Quickness) uses the same quickness state; do-function 18 (0x6FC628F0) replaces the state each time either one is applied, and the aura re-applies every 25 frames, so with Hustle equipped the Assassin keeps +15 rather than her own value. READ
Animation selection VERIFIED
- Weapon class (D2Common
0x6FD93860, run on 6,776 hand combinations). The right-hand item is used if itscomponentis 5 or 6 (a weapon), otherwise the left-hand item; with neither, or in death modes, the class's CharStatsbaseWClass(hth). Assassin with two weapons →ht2. Barbarian with two weapons, by the weapon swinging now (primary) and the other: 1hs + 1ht →1js; 1ht + 1ht →1jt; 1ht + 1hs →1st; anything else1ss. - Grip (
0x6FD6FB80). With one hand occupied, a weapon flagged2handed, or a1or2handedweapon held by a Barbarian, uses2handedwclass. With both hands occupied, a Barbarian holding a1or2handedweapon grips it one-handed (wclass); otherwise any2handeditem still gives2handedwclass. In PD2's data this only matters for the 18 two-handed swords (1hs/2hs): every class swings them with the two-handed animation, except a Barbarian with a shield or second weapon. - Start frame (#10031,
0x6FD82220, run on 7,980 cases): players in A1/A2 (and S3/S4 for Barbarian and Assassin, whose entries are all 0).start = table[row][class], with the row from the weapon's class: bow 1, 1hs 2, 1ht 3, stf 4, 2hs 5, 2ht 6, xbw 7, ht1 12, anything else 0. Only Amazon and Sorceress have non-zero entries. The row comes from the weapon'swclass(item record +0xC0), not the grip. - Sequence skills (anim SQ, setup #10099
0x6FD82820, run for all 23 sequences × 14 weapon classes): the sequence lengthunit+0x34 = frames << 8comes from0x6FDECF40[seqnum], 14 entries of{ptr, frames, frames2}ordered hth 1ht 2ht 1hs 2hs bow xbw stf 1js 1jt 1ss 1st ht1 ht2 (weapon class mapped through0x6FDECFA0). The sequence position starts at 0 andframes2becomes the animation length; in PD2's table the two are always equal. Base speed 256 and the −30 speed penalty.
Wereform override VERIFIED
When the unit is in a transform state and player flag +0xC8 bit 3 is set, D2Common 0x6FD83C20 / 0x6FDA0CE0 replaces the animation speed before the rate is computed:
// 0x6FD767F0, on the right-hand weapon. Uses the item's own stats, not the player total. if (no weapon) H = 19 else if (no A1 anim) H = 45 // 0x6FD768B7 fallback else { h = AnimData[classToken + "A1" + weaponClass] // character's own class (0x6FD93C30) d = tdiv((100 + item.stat68 + item.stat93) * h.speed, 100) // 100 − WSM + weapon IAS H = tdiv(h.frames * 256, d) } base = tdiv(currentLength & ~0xFF, H) // currentLength = unit+0x48 if (base == 0) base = anim.speed // form animation's own speed
Stale length VERIFIED. SetMode (#11090 → 0x6FD835F0) computes the rate before it stores the new animation length at unit+0x48. So currentLength is the length of the previous animation. The server recalculates after SetMode (D2Game 0x6FC987C0 → 0x6FCFFF10) and gets the attack's own length. The client does not (D2Client 0x6FADA750 → 0x6FAFDC70 → 0x6FADA6C0 → SetMode, no later recalculation) READ.
The client ends each local attack by setting neutral (0x6FACC4AC), so a held attack starts from the neutral animation: wolf 40NUHTH = 9 frames, bear TGNUHTH = 10. A first swing out of a run or walk uses RN/WL (8 frames for both forms).
Frame count and pacing
- Server scheduler VERIFIED (D2Game
0x6FCFF7B0, and PD2's replacement0x102CCC30; both run on 20,000 unit setups and produced identical timer lists): when a mode starts at frame F it schedules the animation's events forn = #{k ≥ 1 : start·256 + k·rate < length}steps and the END timer atF + max(n, 1) + 1. PD2 also storesF·256in player data +0x198 instead ofunit+0x44. - Client: the local copy ends when
pos + rate ≥ length, after n advances, and the held-button repeat (0x6FAF46B0) re-issues on the next loop. The client side of this is READ. - Command acceptance VERIFIED (
0x6FC98340→0x6FC97E00, with the real timer lookup0x6FCAE490, 100,000 cases): commands 0, 1, 5 and 17 are always taken. Otherwise by current mode: NU WL RN TN TW S2 S4 always; A1 A2 SC TH whilecur ≤ END + 5(or if the new command is 4 or 9); KK whilecur ≤ END + 5; S1 unless the class is Amazon; S3 unless the class is Druid; DT GH BL DD never. SQ goes through a separate check (0x6FC98100) that PD2 patches. - Held attacks therefore repeat every
n = ⌈(length − start·256) / rate⌉ − 1frames. This last step combines the verified scheduler with the client loop order, which is READ. - In form READ, the server's END sends nothing to the client: the END path (
0x6FC99850→0x6FC982F0) only calls SetMode, and the mode-changed flag atunit+0xC4bit 0 is only read for monsters (0x6FCC2F90). The client packet table (0x6FB8DE60) has no message that would end the local attack. So held attacks in form repeat at the client count, computed with the stale neutral length. The server's own shorter schedule is shown in the calculator for reference.
Skills that change their own speed READ
A few skills' start functions attach a temporary stat list with attackrate (stat 68) and recompute the animation rate (D2Common #10819 = 0x6FD83110). The list is freed when the mode changes.
- Dragon Tail: server srvstfunc 27 (
D2Game 0x6FCB2940→0x6FCBE7B0) and client cltstfunc 9 (D2Client 0x6FAF0400→0x6FB50880) both add Skills.txt Param4, which PD2 sets to −20. - Double Swing: only the client's cltstfunc 27 (
D2Client 0x6FB76290) adds its client calc,par5= +50. Held attacks are paced by the client, so the calculator uses it. - Berserk: only the server's srvstfunc 32 (
D2Game 0x6FC473C0) adds calc3 (ln56); the client doesn't, so the held-attack pace is unchanged.
This matches the Dragon Tail breakpoints in PD2's own Advanced Stats window, which adds the same Param4.
Follow-up swings (Zeal, Fury, Fend, Strafe, Dragon Talon) VERIFIED
These skills are not sequences. They play a normal attack animation (A1, or KK for Dragon Talon), and each time the animation's hit frame is reached the skill's do-function lands a hit and, while hits remain, moves the animation back. The last hit lets the animation play to its end. Skills with srvdofunc 13 (Zeal, Fury, Fend), 12 (Strafe) and 42 (Dragon Talon) do this.
// hit count, set by the start function from calc1 and decremented on each hit Zeal, Fury hits = min(Param5 + lvl - 1, Param6) // 3 with PD2's data Fend hits = 3 Dragon Talon hits = min(lvl/6 + 1, 3) Strafe max = min(Param3 + lvl - 1, Param4); min = min(2 + lvl/4, max); hits = min(max(targets, min), max) // restart value p: Param2 for do-func 13 (Zeal/Fury 100, Fend 60), Param6 for 12 (Strafe 50), 100 for 42 client (D2Client 0x6FB505F0): pos = tdiv((pos >> 8) * (100 - p), 100) << 8 // animation frame index server (D2Game 0x6FD02600, PD2 0x102CD0B0): k0 = tdiv((now - swingStart) * (100 - p), 100); swingStart = now - k0 // game frames reschedule from position k0 * 256, checking frames from k0 - 1; END at now + n + 1 // event rule, client D2Common #10853 (0x6FD82460): each frame j0 = (pos >> 8) + (rate >= 256 ? 1 : 0); pos += rate; hit if j0 <= A <= (pos >> 8) // server first swing (0x6FCFF7B0): frames checked from the start frame; events only while pos < length
Ais the animation's hit frame: the first frame whose AnimData event byte is 1–4. Every player attack animation in PD2's data has exactly one.- A restarted swing lasts until its hit. The last swing lasts
⌈(length − pos)/rate⌉ − 1frames, the same client rule as a normal held attack, and the whole sequence then repeats. - With p = 100 (Zeal, Fury, Dragon Talon) both sides restart at 0 and agree. With p < 100 (Fend, Strafe) the client scales the animation frame and the server scales elapsed game frames, so their hit times can differ; in form the two sides also swing at different rates. The calculator shows the client's swing lengths (they pace the repeat) and the server's hit times, and drops any server hit that would land after your client repeats the attack.
- Verified natively: PD2's follow-up scheduler and the stock one on 40,000 setups (timers and stored start, 0 mismatches); the client step #10853 on 20,000 swings (hit step and end, 0 mismatches); the server's first-swing event timing on 20,000 setups. The client's restart arithmetic (
0x6FB505F0) and the assumption that a target is present for every hit are read, not run.
Reference implementation
A self-contained version of what the calculator does for one attack. A is the AnimData lookup (key → [frames, speed]).
const tdiv = (a, b) => Math.trunc(a / b); // C-style signed division const eias = x => tdiv(120 * x, 120 + x); function heldFrames({ token, form, mode, wclass, weapon, weaponIas, gearIas, skillSpeed, start = 0, seqFrames }) { // weapon = { wsm } or null. form = null | "40" | "TG" const wsm = weapon ? weapon.wsm : 0; let s = 100 + skillSpeed - wsm + eias(weaponIas + gearIas); let len, base; if (mode === "SQ") { s -= 30; len = seqFrames * 256; base = 256; start = 0; } else if (form) { const atk = A[form + mode + "HTH"], neutral = A[form + "NUHTH"]; len = atk[0] * 256; start = 0; let H; if (!weapon) H = 19; else { const h = A[token + "A1" + wclass.toUpperCase()]; if (!h) H = 45; else H = tdiv(h[0] * 256, tdiv((100 - wsm + weaponIas) * h[1], 100)); } base = tdiv((neutral[0] * 256) & ~0xFF, H) || atk[1]; // client: stale neutral length } else { const anim = A[token + mode + wclass.toUpperCase()]; len = anim[0] * 256; base = anim[1]; } s = Math.max(15, Math.min(175, s)); const rate = Math.min(Math.floor(base * s / 100), 0x7FFF); return Math.ceil((len - start * 256) / rate) - 1; }
For the server's own schedule in form, use the attack's length instead of the neutral length in the base line. For dual wielding in PD2, pass the faster weapon's WSM and IAS.
Extracted tables
Loading the extracted tables…
Address map
| What | Where | Status |
|---|---|---|
| Animation rate (all modes) | D2Common 0x6FD83110 | VERIFIED |
| Diminishing-returns helper / table | 0x6FD823E0 / 0x6FDE4608 | VERIFIED |
| PD2 dual-wield replacement | D2Common +0x334AC → PD 0x102EEBF0 → 0x10268A90 | VERIFIED |
| Wereform override | 0x6FD83C20, 0x6FDA0CE0, 0x6FD767F0 | VERIFIED |
| SetMode (stale length) | #11090 → 0x6FD835F0 | VERIFIED |
| Start frame | #10031 0x6FD82220, table 0x6FDE4480 | VERIFIED |
| Weapon class for animation, grip | 0x6FD93860, 0x6FD6FB80, index table 0x6FDEF028 | VERIFIED |
| Sequence setup and lengths | #10099 0x6FD82820, 0x6FDECF40, map 0x6FDECFA0 | VERIFIED |
| Server scheduler | D2Game 0x6FCFF7B0, PD 0x102CCC30 | VERIFIED |
| Skill level curve "dm" | D2Common 0x6FD9DC30 | VERIFIED |
| Follow-up restart (server) | D2Game 0x6FD02600, PD2 0x102CD0B0 (call at 0x6FCC3602 replaced) | VERIFIED |
| Client animation step and events | D2Common #10853 0x6FD82460 | VERIFIED |
| Follow-up restart (client) | D2Client 0x6FB505F0; do-funcs 0x6FAC4FD0, 0x6FAC5820, 0x6FAF0670 | READ |
| Server attack start / recalc | 0x6FC987C0 → 0x6FCFFF10 | READ |
| Server command acceptance | 0x6FC98340 → 0x6FC97E00, timer lookup 0x6FCAE490 | VERIFIED |
| Server end of attack | 0x6FC99850 → 0x6FC982F0 | READ |
| Client attack start | D2Client 0x6FADA750 → 0x6FAFDC70 → 0x6FADA6C0 | READ |
| Client held-attack repeat / loop | 0x6FAF46B0 / 0x6FAF4B50 | READ |
| Client packet table | 0x6FB8DE60 (12-byte entries) | READ |
Known gaps OPEN
- Whether IAS from socketed runes and jewels is part of the weapon's own IAS in the form override. The override reads the item's full stat list, and the item's own affixes are merged into it; socket merging has not been traced yet.
- Whether a form granted by an item survives swapping to a weapon that does not grant it.
- Form skills other than Fury (Maul, Hunger and others) are not modelled yet.
- Follow-up skills assume every hit finds a target. With no target, Zeal-type skills stop restarting and the swing plays out.