Where items come from: inside the loot machine

From logic/item-acquisition.jsonl (75,809 provenance edges) and logic/loot-vendor-math.json (decompile-verified: VendorLogic.cs, SharedVendorTables.cs, the BlueprintUnitLoot.GenerateItems() family) — build 20212986, patch 2.7.0x.

Every sword you've ever looted has a paper trail. We dumped all of them — 75,809 edges linking items to whatever can grant them — and read the code that rolls the drops.

The provenance census

| Source | Edges | What it means | |---|---:|---| | Units | 26,995 | carried/dropped by creatures | | Dungeon & area loot tables | 25,556 | containers and placeables | | Shared vendor tables | 9,878 | shop stock | | Trash-loot settings | 3,403 | auto-filled junk | | Etudes / dialog cues / answers | 3,582 | story grants | | Buffs, abilities, crafting | 2,592 | everything else |

Story grants are bigger than they feel: 3,582 items arrive through dialog and story state — quest rewards live in conversations, not in the loot system at all.

How a container rolls its contents

GenerateItems() runs every loot component in order, and there are exactly three kinds (loot-vendor-math.json):

  • Fixed pack — adds exactly m_Count of an item or table. No roll.
  • Variable pack — count is a uniform integer in [CountFrom, CountTo], inclusive. Your two-arrows-vs-eleven-arrows roll.
  • Weighted pick — one roll of uniform[0, sumWeights), walked down cumulative weights; adds exactly 1 of the winner.

The fourth kind isn't a component but a recursion: a loot entry can point at another loot table, once per count — tables compose into trees, which is why a simple crate can be secretly rolling against a dungeon-level table that rolls again.

Trash loot is priced, not listed: FillLootComponent keeps adding uniform-random trash items until the pile reaches its target approximate price. That's why every bandit's pouch is worth about the same.

Vendor math

sellPrice = round0.5(cost × SellModifier × discount × vendor.SellPriceModifier)
buyPrice  = round0.5(cost × discount × vendor.PriceModifier)

Three things the UI never says:

  • Selling outside the trade window pays less — the junk-sale path skips the vendor's own modifier entirely.
  • Discounts are injectable: kingdom buffs and etudes can attach per-vendor, per-item-type discounts through a generic action wrapper.
  • Shop stock generates once and persists. A shared vendor table's GenerateItems() result is saved with your game; restocking only ever adds newly unlocked fixed items (GetLootDifference). Sold out of the unique cloak? It stays sold out.

Trace any specific item's sources from its page in our Items section.

Sources

  • logic/item-acquisition.jsonl — all 75,809 provenance edges.
  • logic/loot-vendor-math.json — drop-roll semantics, trash-fill pricing, and both price formulas, each pinned to its class file.
  • Build: 20212986 (patch 2.7.0x).

Related database section: Items — e.g. Adamantine Full Plate +5.