itemstatcost.txt is the stat dictionary that every other table consults when it needs to read or write a value on a unit or item. When an affix in magicprefix.txt says Code = strength, Min = 5, Max = 10, the meaning of "strength" — its display routine, its save-bit budget, whether it stacks on the unit or stays on the item, how it groups with other stats in tooltips — comes from the corresponding row here. Skill effects, item modifiers, character save serialization, and the network protocol all consult these rows.
368 rows, 52 columns. Every stat the engine can manipulate has exactly one row. The columns split into four conceptual groups: storage and serialization (how many bits, signed or unsigned, saved to disk or session-only), behavior (what to do when the stat changes — auras, callbacks, op functions), display (how the stat renders in tooltips and the advanced-stats UI), and gold-cost (how the stat affects an item's buy/sell/repair price).
What's in a row
The 52 columns split into nine functional groups.
Identity
| Column | Meaning |
|---|
Stat | Unique pointer name (strength, maxhp, damageresist, item_singleskill). This is what every other table references to bind to this stat. |
*ID | Numeric index — the row's position in the file. Reference-only (the * prefix marks comment-only fields the engine ignores), but external systems and other rows may reference stats by this ID. |
Network and runtime serialization
These columns control how the stat travels between the server, the game client, and the save format.
| Column | Meaning |
|---|
Send Other | 1 = the stat is broadcast to other clients (visible to other players). |
Signed | 1 = treated as a signed integer (can be negative); 0 = unsigned. Affects how Send Bits is interpreted. |
Send Bits | Bit width allocated for the stat's value when sending to clients. Caps the maximum value: n bits means 0..2^n − 1 unsigned, −2^(n−1)..2^(n−1) − 1 signed. |
Send Param Bits | Bit width for the stat's parameter value (used by stats that carry a secondary value, e.g. a skill ID). Always treated as a signed integer. |
UpdateAnimRate | 1 = the stat triggers an animation-speed recalc when it changes. Only consulted for stats with State bits or for skill server functions 30, 61, 71. |
| Column | Meaning |
|---|
Saved | 1 = the stat persists in the character save file. 16 of 368 stats are persisted (the core attributes, max-life/mana/stamina, and a handful of other engine-tracked counters). |
CSvSigned | When Saved=1, controls whether the saved value is signed (1) or unsigned (0). |
CSvBits | Bit width allocated for the stat's value in the save file. |
CSvParam | Bit width for the stat's parameter in the save file. |
1.09-Save Bits / 1.09-Save Add | Pre-LoD save-format equivalents of Save Bits / Save Add, only used when reading items from a Patch 1.09d-or-older character save. Preserved for backward compatibility. |
Save Bits | Bit width for the stat's value when saved on an item (not a character). |
Save Add | A value added to the stat before bit-packing it into the item save (lets the engine pack negative or off-by-N values into unsigned bit fields). |
Save Param Bits | Bit width for the stat's parameter in item saves. Always unsigned. |
keepzero | 1 = the stat stays on the unit's stat-change list even when its value is 0 (otherwise the engine drops zero-valued stats from broadcast). |
Behavior and op-function chaining
| Column | Meaning |
|---|
fCallback | 1 = changes to this stat invoke the engine's callback to update related state, item events, or skills. 38 stats have this enabled. |
fMin | 1 = the stat has a hard floor at the value in MinAccr. |
MinAccr | The minimum value if fMin=1. |
direct | 1 = when this stat updates via skill server functions 65, 66, 81, 82, it scales against its maxstat cap (used so current life can't exceed max life, etc.). |
maxstat | The pointer to a related "max" stat. Used to pair hitpoints with maxhp, mana with maxmana, stamina with maxstamina, durability with max durability. |
damagerelated | 1 = the stat is exclusive to the item it sits on and isn't aggregated onto the unit (typical for weapon damage stats — prevents dual-wielded weapons from stacking each other's damage onto attacks). |
op | Operator function ID (0–13). Lets a stat modify another stat instead of just adding to its own bucket. The 13 documented operators include percent-modifier (op=1), by-level scaling (op=2 / 3 / 4 / 5), by-time-of-day adjustments (op=6 / 7), Energy → Mana scaling (op=8 — used by energy itself to drive maxmana), Vitality → Life/Stamina scaling (op=9), and player/item percent operators (op=11 / 13). 85 of 368 stats use a non-zero op. |
op param, op base | Numeric parameters for the op function (typically a divisor, level threshold, or shift amount). |
op stat1, op stat2, op stat3 | Up to three target stats that the op function modifies. For energy, op stat1 = maxmana — that's how each point of Energy translates into Mana. |
itemevent1, itemeventfunc1, itemevent2, itemeventfunc2 | Hook a stat to in-game events. The itemevent# value references an event from Events.txt (on-attack, on-hit, on-kill, on-damaged, etc.); the itemeventfunc# value selects an event-handler function. 21 stats use itemevent1; 11 of those also use itemevent2 to catch a parallel event with the same handler. The pattern is consistently melee/missile pairing — when a proc-stat needs to fire on both the melee and ranged variants of the same trigger, slot 1 catches one and slot 2 catches the other (e.g. item_skillonattack runs func 20 on domeleeattack AND domissileattack; item_knockback runs func 7 on domeleedamage AND domissiledamage). |
Item gold-cost (Encode)
| Column | Meaning |
|---|
Encode | Function ID controlling how the stat affects the buy / sell / repair cost of an item carrying it. Five behaviors: 0 (or empty) — flat Add + multiplied stat value; 1 — uses the stat's parameter as a Skill ID and pulls cost mult / cost add from Skills.txt; 2 — same idea but the parameter encodes both Skill ID and skill level; 3 — same as 2; 4 — averages the stat's by-time min/max into a baseline, then applies Add and Multiply. 46 of 368 stats declare an Encode. |
Add | Flat additive modifier applied after Multiply for Encode 0 and Encode 4. |
Multiply | Multiplicative modifier (interpreted in 1024ths — divide by 1024 to get the real factor). |
ValShift | Bit shift applied to the stat's value before the cost calculation runs, used when the stored value's bit-precision differs from its display value. |
descfunc is the dispatch table that controls how the stat renders in item tooltips. The engine supports 29 numbered display functions; the shipped data uses 16 of them. The full enumeration is documented in the engine, but the most-used in shipped data are:
descfunc | Behavior | Use count |
|---|
| 19 | Sprintf Num — generic numeric substitution into a localization string. The default for almost all stats. | 165 |
| 18 | Same as 17 (Plus/Minus By Time variant). | 18 |
| 17 | Plus/Minus By Time — value depends on game time-of-day. | 17 |
| 15 | Proc Skill — formats "X% chance to cast level N skill on Y." | 6 |
| 12 | Plus Sub One — +value if value > 1, otherwise value or −value. | 2 |
| 22 | Versus Monster Percent — uses the strplur field from MonType.txt to format "X% damage to demons / undead / etc." | 2 |
| 1, 5, 11, 13, 14, 16, 23, 24, 27, 28, 29 | Specialized one-offs (Plus/Minus, Percent128, Repair, Add Class Skill, Add Tab Skill, Aura, Reanimate, Charges, Single Skill, Non-Class Skill, Sprintf Num Positive). | 1 each |
The remaining 13 documented descfunc values exist in the engine but no shipped stat uses them.
| Column | Meaning |
|---|
descpriority | Sort order in tooltips. Higher values render higher in the stat list. Stats with the same priority render in row-order from the file. |
descfunc | Display-function dispatch (see above). |
descval | Where to place the value in the rendered string: 0 = hide the value, 1 = at the start, 2 = at the end. |
descstrpos | Localization-string key used when the value is positive. |
descstrneg | Localization-string key used when the value is negative. |
descstr2 | Optional secondary string appended to the rendered output. Used by per-level and other multi-string functions. |
Grouped display (dgrp)
| Column | Meaning |
|---|
dgrp | Group ID. When all stats sharing the same dgrp value are simultaneously present on the unit, the engine collapses them into one combined line instead of rendering each individually. 8 stats use dgrp in shipped data, in 2 groups: dgrp=1 collapses strength + energy + dexterity + vitality into "+N to All Attributes"; dgrp=2 collapses the four resistances into "+N% to All Resistances." |
dgrpfunc | Display function for the combined line (same dispatch as descfunc). |
dgrpval | Value placement, same semantics as descval. |
dgrpstrpos / dgrpstrneg / dgrpstr2 | Localization-string keys for the combined line. |
Misc
| Column | Meaning |
|---|
stuff | Universal bit-shift value (must be 1–8; defaults to 6 if out of range) controlling how skill IDs and skill levels are packed into a stat's value for charge-based items. The row this appears on doesn't matter — it's a global setting. |
advdisplay | Controls visibility on the Advanced Stats UI: 0 (or empty) = never shown, 1 = always shown, 2 = shown only when value is positive. |
Row terminator
| Column | Meaning |
|---|
*eol | End-of-row marker, always 0. Reference-only. |
Worked example: strength
Strength is the simplest stat in the file and shows several conventions at once.
| Column | Value | Notes |
|---|
Stat | strength | The pointer every other table uses. |
*ID | 0 | First row in the file. |
Send Other | 1 | Visible to other clients. |
Send Bits | 11 | 11-bit unsigned = max 2,047 — well above any plausible character total. |
Saved | 1 | Persisted in the character save. |
CSvBits | 10 | 10 bits in the save = max 1,023. |
Save Bits / Save Add | 8 / 32 | When written on an item: 8 bits with a +32 baseline shift — the engine packs ±32 to +223 efficiently. |
fMin / MinAccr | 1 / 1 | Hard floor at 1 (no zero-strength characters). |
Encode / Add / Multiply | (blank) / 125 / 55 | Default Encode 0: each point of Strength on an item adds (value × 55 / 1024) + 125 gold to buy/sell/repair cost. |
descpriority / descfunc / descstrpos | 67 / 19 / ModStr1a | Renders near the top of the tooltip via descfunc=19 (Sprintf Num) into the ModStr1a localization string ("+N to Strength"). |
dgrp / dgrpfunc / dgrpstrpos | 1 / 19 / Moditem2allattrib | Member of the all-attributes group: when an item carries strength + energy + dexterity + vitality simultaneously (e.g. an Annihilus), they collapse into the single "+N to All Attributes" line via the Moditem2allattrib string. |
For comparison, energy is structurally identical except for one critical addition: op = 8 with op stat1 = maxmana. That's the engine's "Energy Operator" — for every stat point of Energy, the unit's maxmana is increased by Energy × ManaPerMagic from CharStats.txt, scaled by the Mana bit shift. This is why characters get more Mana per Energy point at higher levels and how the per-class Mana-per-Energy ratios live in CharStats.txt rather than being hard-coded.
And item_singleskill (the +N-to-a-specific-skill mod, e.g. "+3 to Fireball") shows the skill-cost integration: Encode = 1 tells the engine to pull cost mult / cost add from the Skills.txt row referenced by the stat's parameter (the skill ID), so the gold cost of an item carrying the mod scales with the value of the specific skill being granted.
Cross-references
Properties.txt — the affix/cube/skill-mod system. Each Properties.txt row references a Stat from this table to declare what stat its mod actually modifies.
magicprefix.txt, magicsuffix.txt, automagic.txt — affix mod blocks reference Properties.txt codes, which in turn resolve to stats here.
cubemain.txt — recipe mod 1–mod 5 blocks reference Properties.txt, which resolves here.
Skills.txt — Encode = 1/2/3 stats pull cost mult / cost add from a Skills row keyed by the stat's parameter.
CharStats.txt — op = 8 (Energy → Mana) and op = 9 (Vitality → Life/Stamina) read ManaPerMagic, LifePerVitality, and StaminaPerVitality from this table.
Events.txt — itemevent1 / itemevent2 reference event codes here.
MonType.txt — descfunc = 22 (Versus Monster Percent) reads the strplur field here.
States.txt — stats with State bits interact with the state system; UpdateAnimRate checks state membership.
SkillDesc.txt — descfunc = 27 / 28 (Single Skill / Non Class Skill) read the str name field here.