Mechanics

missiles

Every projectile in D2R — velocity, lifetime, collision, sub-missile chains, damage fields, and the function-pointer dispatch that controls server and client behavior.

Missiles.txt defines every projectile type in D2R: arrows, javelins, spell projectiles, sub-missiles spawned in flight or on impact, AoE clouds, and explosion effects. 736 rows across 172 columns (147 carry data; the 25 columns whose names start with * are inline annotation comments). skills references rows here by name (via columns like Missile, Missile2, SrvMissile) to specify which projectile a skill fires; the relationship is one-way — a skill points at a missile, the missile has no back-reference.

This table matters for rigorous "frames-to-damage" analysis. The clock on many caster skills starts when the missile is created, not when the player finishes the casting animation. Range (lifetime in frames) and Vel (pixels per frame) together determine how long before the projectile reaches its target.

Architecture: function pointers + numeric inputs

The missiles table uses a function-pointer dispatch model. Each row selects numbered engine routines for server-side and client-side behavior:

Function-pointer columnRole
pSrvDoFuncPer-frame server behavior while the missile is alive. Movement, sub-missile spawning, homing.
pSrvHitFuncServer behavior on collision — deals damage, creates AoE effects, spawns follow-on missiles.
pSrvDmgFuncServer damage transformation invoked before the unit is damaged (rarely set; used for Holy Bolt's faction modifier, Blessed Hammer's undead/demon multipliers, etc.).
pCltDoFuncPer-frame client behavior — visual particle trails, flight animation, client-side sub-missiles.
pCltHitFuncClient-side collision behavior — visual hit effects and overlays.

Each function code is a small integer that selects a hard-coded engine routine. Numeric inputs to the chosen routine live in paired columns: SrvCalc1 / Param1–5 (server-do), SHitCalc1 / sHitPar1–3 (server-hit), CltCalc1 / CltParam1–5 (client-do), CHitCalc1 / cHitPar1–3 (client-hit), DmgCalc1 / dParam1–2 (server-damage). The 25 *param1 desc columns next to each parameter hold a one-line annotation of what that parameter means for that row — they have no engine effect.

Identity

ColumnMeaning
MissileUnique name. Primary lookup key. Referenced by skills.txt via Missile, Missile2, SrvMissile, and by calc-DSL expressions like miss('abysscenter'.par1). Row order determines each missile's numeric ID; rows must not be reordered.
*IDNumeric missile ID (annotation column, equal to row order). Used by DSL expressions and engine internals.

Velocity, lifetime, visibility

ColumnMeaning
VelLaunch speed in pixels per frame (25 fps).
MaxVelSpeed cap. When Accel = 0, the missile travels at Vel for its full life. With Accel > 0, velocity grows each frame until it reaches MaxVel.
VelLevExtra velocity per caster level beyond level 1.
AccelAcceleration per frame. Positive = speeds up, negative = slows down.
RangeLifetime in frames (25 frames = 1 second). How long the missile lives before expiring if it has not hit anything.
LevRange (file column Radius)The file column in this slot is named Radius (locbones documents it as LevRange). Empirically, values match an AoE hit radius in sub-tiles — sigil missiles use 4 / 6 / 8 (small / medium / large), Warlock's Abyss family uses calc-DSL expressions like par3+(sksrc('Enhanced Entropy'.blvl)/par5). Empty for standard projectiles, whose AoE on impact is set by pSrvHitFunc parameters (sHitPar1) instead.
InitStepsFrames the missile must be alive before becoming visible to the client. Below this count the missile renders as invisible. Used by 274 rows for delayed-emergence effects.
ActivateFrames the missile must be alive before it can collide. Below this, the missile passes through targets. Used by 9 rows.

Collision and hit-class

ColumnMeaning
CollideTypeWhat the missile can collide with. 0 = nothing; 1 = players only; 2 = monsters only; 3 = players and monsters; 4 = nothing (duplicate); 5 = monsters (duplicate); 6 = barriers like doors; 7 = other missiles; 8 = players, monsters, barriers, and walls. Empty is treated as 0. The values that actually appear in shipping data are 1, 3, 6, 8, and empty.
CollideKill1 = missile is destroyed on first collision. 0 = missile persists through collisions (piercing missiles, persistent clouds).
CollideFriend1 = missile can hit friendly units, including the caster.
Collision1 = missile has its own placement collision mask in the world (the engine treats it as a movable obstacle).
ClientCol1 = client-side collision check, gated by CollideType.
NextHit / NextDelayWhen NextHit = 1, the missile cannot hit the same unit again for NextDelay frames. Prevents per-frame stacking on rapid-fire missiles.
Explosion1 = missile is classified as an explosion; uses different nearby-unit search handlers when dealing damage.
AlwaysExplode1 = missile always fires the explosion sequence on death (triggering pSrvHitFunc, HitSound, ExplosionMissile), regardless of whether it made a valid collision.
ToHit1 = missile uses the caster's Attack Rating to determine if it lands. 0 = always hits on collision.
Pierce1 = missile is eligible for the Pierce mechanic (Amazon passive, Pierce-affix items).
KnockBackPercentage chance (0–100, not a boolean) that the struck unit is knocked back.
GetHit1 = struck unit enters hit-recovery animation (GH mode).
SizeDiameter in sub-tiles (X and Y axes) that the missile occupies for collision.
HitClassNumeric bitfield controlling which hit sound and hit overlay the engine plays on collision — see the table below. Unrelated to the hitclass lookup table, which is for weapon-class animation tokens.

HitClass bitfield codes

ValueLayer
16Double Layer
32Fire Layer
48Cold Layer
64Lightning Layer
80Poison Layer
96Stun Layer
112Bash Layer
128Thorns Layer
144Sanctuary Layer
160Silent Voice Layer
176Goo Layer

Values are added together for missiles that should fire multiple layers (e.g. fire + stun = 32 + 96 = 128).

Sub-missile chains

ColumnMeaning
SubMissile1/2/3Missiles spawned during flight, by pSrvDoFunc. The exact trigger (per-frame, per-interval, at a specific frame) is controlled by the function code, not this field.
HitSubMissile1/2/3/4Missiles spawned on collision, by pSrvHitFunc. The primary mechanism for "explosion on impact" patterns.
ExplosionMissileClient-side missile spawned when this missile explodes or expires. Visual only.
CltSubMissile1/2/3Client-only counterparts to SubMissile1–3. Visual particle trails — no game-damage effect.
CltHitSubMissile1/2/3/4Client-only counterparts to HitSubMissile1–4. Visual hit effects only.
MissileWeaponVFXD2R-era column. Used by 3 rows (bladewarp, bladewarpexplode, echoingstrike) to attach a weapon visual effect. Empty for everything else.

Damage

Most spell missiles carry 0 in MinDamage/MaxDamage and empty EMin/EMax — their damage flows from the linked skill. Two mechanisms connect missile to skill damage:

  • MissileSkill = 1 — the missile looks up the skill that created it and uses that skill's damage in place of its own.
  • Skill = <skill name> — the missile binds to a specific skill row in skills.txt for damage values regardless of which skill spawned it.
ColumnMeaning
MinDamage / MaxDamageBase physical min/max damage.
MinLevDam1–5 / MaxLevDam1–5Per-level physical damage increments across five level bands (levels 2–8, 9–16, 17–22, 23–28, 29+).
HitShiftDamage modifier in 256ths, capped 0–8. The values are exponential, not linear: 8 = 256/256 (100%), 7 = 128/256 (50%), 6 = 64/256 (25%), 5 = 32/256 (12.5%), 4 = 16/256 (6.25%), 3 = 8/256 (3.13%), 2 = 4/256 (1.56%), 1 = 2/256 (0.78%), 0 = 1/256 (0.39%). Most spell missiles use 8.
ETypeElemental damage type. Values seen: fire, cold, ltng, pois, mag, burn (open wounds-style burn), frze (cold-aura freeze). Empty = physical only.
EMin / EMaxBase elemental min/max damage.
MinELev1–5 / MaxELev1–5Per-level elemental damage increments (five-band, same thresholds as physical).
ELenBase elemental duration in frames. Relevant for poison (damage tick window), cold (chill/freeze length).
ELevLen1–3Per-level duration increments across three bands (levels 2–8, 9–16, 17+).
SrcDamageFraction of the caster's weapon damage added to the missile, in 128ths (128 = 100%). -1 or 0 = no weapon damage contribution.
Half2HSrc1 = if SrcDamage is set and the caster wields a 2-handed weapon, halve the contribution.
SrcMissDmgFraction (in 128ths) of the parent missile's damage carried over to a sub-missile.
ApplyMastery1 = elemental mastery bonuses (Sorceress Fire/Cold/Lightning Mastery) apply to this missile's elemental damage.

Special flags

ColumnMeaning
HolyBitfield restricting which monster types this missile can damage. 0 = all units; 1 = undead only; 2 = demons only; 4 = beasts only.
CanSlow1 = missile is affected by the slowmissiles state.
Town1 = missile can exist in town areas. 0 = destroyed immediately when entering town.
NoUniqueMod1 = missile does not pick up bonuses from the caster monster's unique modifiers.
NoMultiShot1 = missile is not duplicated by the Multi-Shot monster mod.

D2R-era function-code extensions

Locbones documents pSrvDoFunc 0–37, pCltDoFunc 0–68, pSrvHitFunc 0–59, and pCltHitFunc 0–64. The shipping D2R data uses values beyond those ranges:

Columnlocbones maxData max
pSrvDoFunc3745
pCltDoFunc6876
pSrvHitFunc5960
pCltHitFunc6465

The high codes back D2R-era content (Reign of the Warlock skills, post-LoD additions). Treat them as opaque engine routines — the shape of their parameter columns is documented case-by-case via the *param1 desc annotations on the rows that use them.

Cross-references

  • skills — links missile names via Missile, Missile2, SrvMissile. The skill's calc fields and elemental damage flow into the missile when MissileSkill = 1 or the missile's Skill field names that skill.
  • skilldescdescmissile1/2/3 references missiles as inputs to tooltip calc formulas (miss('abysscenter'.par1)).
  • hitclassunrelated to missiles.HitClass. That table is for weapon-class animation tokens; the bitfield documented above is the elemental-layer system.

Worked example: Poison Javelin

Poison Javelin (an Amazon javelin skill) shows the missile-and-cloud pattern that almost every "trail" caster uses.

The skill fires poisonjav (the visible javelin shot). While in flight, the javelin spawns one poisonjavcloud per frame as a SubMissile1. The cloud sits stationary for its Range lifetime and damages anything that walks into it.

Where does the damage come from? Neither row carries elemental fields directly:

Fieldpoisonjavpoisonjavcloud
pSrvDoFunc2 (MissileDoPoisonJavelin)3 (StillMissileKludge — stationary)
Vel24 px/frameempty (stationary)
Range25 (1 second)60 (2.4 seconds)
SubMissile1poisonjavcloud(empty)
EType(empty)(empty)
EMin / EMax(empty)(empty)
Skill(empty)Poison Javelin

The cloud's Skill = Poison Javelin field binds it back to the skills row, and the per-tick poison damage is read from there. This is the standard skill-bound carrier pattern: the missile rows control timing and shape, but damage values live in skills.txt.

-- Inspect the javelin → cloud chain
SELECT Missile, pSrvDoFunc, Vel, Range, SubMissile1, Skill
FROM missiles
WHERE Missile IN ('poisonjav', 'poisonjavcloud')
ORDER BY Missile;
-- Find every missile that binds back to a skill (carrier-pattern missiles)
SELECT Missile, pSrvDoFunc, Range, Skill
FROM missiles
WHERE Skill != ''
ORDER BY Skill, Missile
LIMIT 25;