How the D2R Item Generator Works

This doc explains the model the d2r-item-generator implements: the full surface of items it builds, the in-game mechanics it mirrors, and the roll-odds math behind the numbers it shows.

Scope of the generator

The tool builds single Diablo II: Resurrected items — not characters, not sets, not runewords, not full inventories. Inside that scope it covers a wide matrix:

  • 11 equipment slots: amulets, body armor, belts, boots, charms, circlets, gloves, helms, rings, shields, weapons.
  • 3 generation modes per slot (where applicable): rare, magic (charms-only), crafted (blood / caster / hit power / safety, with per-slot eligibility from D2's cubemain recipes).
  • Item shape per slot: base type, base subtype where it exists (helms split into plain helms, barbarian helms, druid pelts; shields split into plain shields, paladin shields, necromancer shrunken heads, warlock tomes), ilvl, ethereal flag.
  • Affixes: 0–3 prefixes plus 0–3 suffixes (subject to mode and total-count rules — see below), each with a tier-bound value or multi-axis roll.
  • Special-slot mods: staffmods (per-class skill bonuses on certain bases) and auto-mods (guaranteed mods stamped onto specific subtypes).
  • Roll Odds: per-affix and joint probabilities for every randomly rolled element (currently affixes and craft bonuses; staffmods and auto-mods are not yet covered — see §8).
  • Scoring: PvM/PvP scoring for rings (the only slot with a complete scoring ruleset today).

The rest of this doc breaks each of these down in detail.

The rare model

The rare model is the bulk of the math, and it's what most users build. Rares are generated as a sequence of weighted random draws.

Affix count

A rare item rolls 3–6 total affixes under a 1:2:3:2 ratio:

TotalWeightProbability
3112.5%
4225.0%
5337.5%
6225.0%

Five-affix rares are the most common; three-affix and six-affix rares each occur ~12.5% / 25% of the time. Source: d2r-ring-simulator/affix_simulation.py.

Side split (prefix vs suffix)

For a given total affix count, the prefix/suffix split is binomial (each affix is 50/50 prefix-or-suffix), constrained by the rule that neither side can exceed 3. There is no minimum-per-side rule — a 3-affix rare can roll all-prefix (3,0) or all-suffix (0,3).

Renormalized probabilities for every legal (prefix_count, suffix_count) configuration:

Config (P, S)P(config)
(0, 3)1/64
(1, 2)3/64
(2, 1)3/64
(3, 0)1/64
(1, 3)1/14
(2, 2)3/28
(3, 1)1/14
(2, 3)3/16
(3, 2)3/16
(3, 3)1/4

(Sum = 1.0. The all-one-side configs only appear at total=3; the side cap forces ≥1 of each at total≥4.)

Group selection (with mutual exclusion)

For each prefix slot, the game picks one group from the prefix pool, weighted by frequency. Once a group is picked, it's removed from the pool for the remaining draws on that side — so the same item can never roll two prefixes from the same group (e.g. two cold-resist prefixes).

The same process runs independently for the suffix slots, drawing from the suffix pool with its own group exclusion.

The group is the level above the affix — multiple specific affixes (e.g. Sapphire / Cobalt / Lapis / Azure cold-resist tiers) can share one group, and group exclusion locks out all tiers of that group once any of them is picked.

Tier selection within a group

Once a group is picked, the game selects a specific tier within that group, weighted by tier.frequency. Tiers also gate on item level: only tiers where level ≤ ilvl (and ilvl ≤ maxlevel if set) are eligible.

Tier frequencies are not uniform. Cold-resist on boots, for example, runs Azure (freq 2), Lapis (freq 4), Cobalt (freq 3), Sapphire (freq 3) — so Lapis (the second-lowest tier) is the most common, not the highest. Some community calculators flatten this by treating every integer in the affix's overall min–max range as equally likely; that's only correct when every tier has equal frequency and equal width, which most affixes don't.

Within-tier value roll

Once a tier is picked, the game rolls a value uniformly in the tier's [min, max] integer range. So Lapis cold-resist (range 11–20) delivers any value 11–20 with equal 1/10 probability per integer.

Multi-roll affixes

Some affixes have multiple independent value axes — +min/max damage on weapons, +attack rating / +damage paired prefixes, etc. Each axis rolls independently and uniformly in its own range, after the tier is picked. The tier's frequency weight applies once to the whole multi-roll affix, not per-axis.

Synthetic wildcards

A few affixes appear in the picker as "any class" wildcards (e.g. +1 to All Class Skills, +2 to Class Skills (any class)). These don't exist as a single in-game affix — they aggregate the eight per-class entries (Sorceress, Paladin, etc.) into one row for the user. Their effective frequency is N × per_class_freq where N is the number of classes (typically 8), since the per-class entries are mutually exclusive and disjoint.

ilvl gating

Every tier has a level (the minimum item level at which it can spawn) and an optional maxlevel (the level at which it phases out in favor of higher tiers). The default ilvl in the tool is the highest level value across every eligible tier in the slot's pool, so the picker shows the maximum-tier set out of the box.

The magic model (charms)

Magic items in the tool are charms-only today (small / medium / large grand charms), and they follow a different slot-fill rule than rares.

Slot-fill distribution

A magic charm has at most one prefix and one suffix, and the slot fills follow a 50/25/25 distribution:

OutcomeProbability
Suffix only50%
Prefix only25%
Both prefix + suffix25%

Marginal slot-fill rates fall out: P(prefix slot filled) = 0.5, P(suffix slot filled) = 0.75. But these are not independent: the joint "both filled" probability is 0.25, not 0.5 × 0.75 = 0.375. The two slot-fills are negatively correlated under the renorm.

Same affix pool, different rules

Magic charms draw from the same prefix and suffix pools as rares would, with the same tier-frequency selection and within-tier uniform value rolls. Only the slot-fill rule differs.

Verified against modern calculators

The 50/25/25 slot-fill model matches modern community calculators (maxroll) on charm odds:

  • of Vita 41–45 on a grand charm: 0.75 × 4/100 = 1/33.3.
  • of Balance (FHR) on a grand charm: 0.75 × 4/100 = 1/33.3.
  • Skill tab on a grand charm: 0.50 × 24/247 = 1/20.6.

Older community references (e.g. Arreat Summit) sometimes report different numbers for skill tabs because they were last updated before the most recent expansion grew the skill-tab pool from 21 to 24 tabs (warlock added). Our model uses the current 24-tab pool and matches maxroll's updated figures.

The crafted model (afterthought)

Crafted items use cube recipes that combine a magic base, jewels, and runes to roll an item with fixed recipe mods + a few random affixes. There are four families: blood, caster, hit power, safety — each with its own recipe-fixed mod set.

In practice, very few crafts are made on Battle.net. The realistic build targets are blood rings and caster amulets; everything else (caster boots, hit power gloves, safety helms, etc.) is rare on the trade market. The tool supports the full matrix because the permalink and odds machinery cost nothing extra, but most of it is unused.

How a craft is shaped

  • Recipe-fixed mods with sliding ranges: every craft produces the same set of mods, but most have a min–max range and the actual roll lands somewhere inside that range. The tool surfaces sliding-range bonuses in the Roll Odds list with their own =X / ≥X toggle.
  • Random affixes: the game then rolls 1–4 additional random affixes from the slot's normal prefix/suffix pool, capped at 4 total random affixes (down from the rare 6 cap).
  • The fixed mods do not consume the random affix budget. A craft has its recipe mods + up to 4 random affixes.

Per-slot eligibility

Not every (slot, craft) combination has a recipe. Eligibility comes from D2's cubemain.txt — some slots support all four craft families, others only a subset. The tool's per-slot craftTypes list reflects the cubemain ground truth.

Bases, subtypes, and itype gating

D2's affix pools are gated by item type (itype). A boots prefix that lists armo in its itype1..7 columns spawns on any armor base including boots; a prefix that lists only weap doesn't spawn on boots. This means each slot has its own filtered prefix and suffix pool drawn from the same global affix tables.

Subtypes

Some slots split internally:

  • Helms: plain helms (helm), barbarian helms (phlm), druid pelts (pelt).
  • Shields: plain shields (shie), paladin shields (ashd), necromancer shrunken heads (head), warlock tomes (grim).

Subtype affix pools differ — paladin shields carry their own auto-mod group, druid pelts carry druid-only staffmods, and warlock tomes carry warlock staffmods plus their own auto-mod branches.

Ethereal multiplier

Ethereal armor and weapons spawn at a flat 1-in-20 rate in D2R. The tool multiplies the joint probability by 1/20 when the ethereal flag is set. Rings, amulets, jewels, and charms cannot be ethereal — the flag is hidden in their UI and skipped in the math.

Class restriction

When a base supports a staffmod class (e.g. a barbarian helm carries barbarian-only skill bonuses), picking that base implicitly restricts the item to that class. Class restriction is auto-set from the base selection; users don't toggle it directly.

Special-slot mods: staffmods and auto-mods

These are mods that don't follow the standard prefix/suffix model.

Staffmods

Staffmods are per-class skill bonuses that some bases roll independently of the prefix/suffix rolls:

  • Barbarian helms (phlm) carry +1/+2/+3 to specific barbarian skills.
  • Druid pelts (pelt) carry +N to druid skills.
  • Sorceress orbs (orb) carry +N to sorceress skills.
  • Paladin shields (ashd) carry +N to paladin skills.
  • Necromancer shrunken heads (head) carry +N to necromancer skills.
  • Warlock tomes (grim) carry +N to warlock skills.
  • Some weapons (scepters → paladin, wands → necromancer, knives → warlock) carry class staffmods without locking the base to that class — these are "unlocked staffmod" weapons.

The tool lets users pick 1–3 specific skills per item with values 1/2/3. Roll odds for staffmods are not yet computed — see §8. Combinations are not yet validated — the tool currently allows some combinations that the game wouldn't generate (e.g. duplicate skills, more skills than the base supports). This is a known gap tracked separately.

Auto-mods

Auto-mods are guaranteed mods stamped onto specific subtypes, with a few branches per subtype that are mutually exclusive:

  • Sorceress orbs roll one of: +mana, +life.
  • Paladin shields (ashd) roll one of: all-resist, attack rating
    • enhanced damage paired prefixes.
  • Necromancer shrunken heads (head): single auto-mod (curse-related).
  • Warlock tomes (grim) roll one of: magic skill, fire skill.
  • Amazon bows roll an Amazon skill tab.
  • Amazon javelins / spears roll a different skill tab.

For each subtype with auto-mods, the tool surfaces a branch picker. Once a branch is picked, the user adjusts the value sliders within the branch's roll ranges. Auto-mod roll odds are not yet computed — the values display, but the probability of the chosen branch and roll combination is not surfaced.

Roll Odds — what we compute

The Roll Odds panel reports two numbers per item: per-affix odds and the joint full-item probability.

Per-affix odds (slot-conditional)

For a single selected affix on one side (prefix or suffix):

P(slot rolls this affix at this value) =
    effective_freq(affix, value, ilvl) / pool_side(ilvl)

pool_side(ilvl) is the sum of tier.frequency over every eligible tier of every affix on that side, at the user's ilvl.

effective_freq depends on the value mode:

  • Exact-tier mode (atLeast = false): the tier whose range contains the value contributes its full tier.frequency. Other tiers contribute zero.
  • At-least mode (atLeast = true): every eligible tier contributes tier.frequency × within_tier_qualifying_fraction, where the fraction is (hi − threshold + 1) / (hi − lo + 1) for a tier whose range straddles the threshold, 1.0 for a tier entirely above, 0.0 for a tier entirely below.

This is the tier-frequency-weighted model — the actual game mechanic, where tier.frequency controls tier selection, not the tier's range width. Other community calculators sometimes use a "total range" approximation (treat every integer in the affix's overall min–max range as equally likely), which is only correct for affixes where every tier has equal frequency and equal width. Most affixes don't.

Joint odds (the full item)

The joint full-item probability multiplies independent components:

P(item) = P(prefix_set) × P(suffix_set) × P(craft_bonuses) × ethereal_factor × magic_factor
  • P(prefix_set) and P(suffix_set) each come from a sum-over-permutations sequential weighted-without-replacement draw with group exclusion. For a side with k selected affixes, the joint sums over all k! orderings of those affixes; for each ordering, the sequential product is (freq₁/pool) × (freq₂/(pool−g₁)) × ... × (freqₖ/(pool−Σ groups)). This explicitly accounts for the asymmetry that P(A then B) ≠ P(B then A) under group exclusion.
  • P(craft_bonuses) multiplies in the per-bonus odds for any sliding-range craft mod the user has constrained (=X or ≥X). Fixed-range craft mods contribute ×1.
  • ethereal_factor is 1/20 when the ethereal flag is set on an armor or weapon.
  • magic_factor is the slot-fill distribution multiplier for magic items: 0.5 if only the prefix slot is filled, 0.75 if only the suffix slot, 0.25 if both (not 0.375 — see the negatively-correlated-fills note).

What this answers

The joint probability is a slot-conditional answer:

"Given an item with this many prefix slots and this many suffix slots, what's the probability that the slots roll this exact set of affixes / tiers / values?"

It is not "P(this rare item drops)" in an item-level marginal sense. Other community calculators compute the marginal form, which folds in the affix-count distribution as an additional factor. The tool intentionally answers the slot-conditional question because that's the build-design view — given the item shape you're targeting, how rare is the roll inside that shape — rather than "how many drops until one of these appears."

What Roll Odds do NOT yet cover

Several elements of the generated item are not yet factored into the joint probability:

  • Affix-count distribution — the joint is conditional on the user's selected slot configuration. P(item rolls 3p+3s vs 5 affixes vs 4 affixes) is not multiplied in. This is the largest divergence from maxroll's interpretation.
  • Staffmod selection — entire staffmod section is informational. P(specific skill picked) and P(specific value rolled per skill) are not computed.
  • Auto-mod branch + values — the chosen branch (sorc orb mana vs life, ashd resist vs attack-rating prefixes) and its rolled values don't contribute to the joint.
  • Base-level requirement gates — the tool applies the user's ilvl to filter eligible tiers, but it doesn't model the relationship between base item type and minimum drop level (some bases drop only on higher difficulties / area levels).

Known limitations and roadmap

A list of gaps that future sessions can pick up. Each is also tracked in code comments where relevant.

  • Slot-conditional vs item-level marginal — the tool's joint answers "P(roll matches | this item shape)" rather than "P(this drops)". Adding the marginal form would require folding in the affix-count distribution and summing over compatible item-shape configurations.
  • Staffmod combination validation — the tool currently allows some combinations the game would never generate. Need to enforce the per-base staffmod count limit and the at-most-one-skill-per-axis rule.
  • Staffmod and auto-mod roll odds — both surfaces show the user the values but don't compute probabilities. Significant feature work to add cleanly.

Source pointers

  • Affix odds engine: src/lib/drop-odds.ts.
  • Per-slot data files: src/lib/{ring,amulet,helm,...}-data.ts.
  • Affix-count distribution: d2r-ring-simulator/affix_simulation.py (the authoritative model).
  • Affix data source (game tables): d2r-ring-simulator/data/{magicprefix,magicsuffix,itemtypes,armor,weapons}.txt — pinkufairy/D2R-Excel mirror of D2R's data files.
  • Rare validation rule: src/components/ItemGenerator.tsx (isIncompleteRare).