Skip to content

Loot — System Boundaries

What Loot deliberately does not do, and why. Each of these is a decision, not a gap waiting to be filled — knowing them is how you avoid designing around something that will never arrive.

It does not validate item or currency ids

LootEntry.itemGuid and LootEntry.currencyId are raw strings, checked against nothing. A typo is not a compile error, not an editor warning and not a runtime exception — it is an entry that never drops.

This is the price of the decoupling that lets the roller compile without Inventory. Nothing today closes the gap; the debugger's sampling is the practical check, since an authored entry missing from a large sample means its id does not resolve.

It does not change the odds

ILootModifier runs after the roll, on what was won. There is no seam that re-weights entries before they are picked, so luck stats, magic find, difficulty scaling, level gating and pity counters cannot be expressed without editing LootRoller.

Deliberate: a table whose printed weights do not describe its behaviour is very hard to reason about. It is also a real ceiling, and the most likely reason a project outgrows this system.

It does not decide when to drop

Nothing in Loot polls, ticks or watches. Something in your game calls Roll or RollAndGrant.

LootDropOnDeath is the one shipped trigger, and it is an integration — it subscribes to Health's Died and calls the service. If you want drops on chest-open, quest-complete or timer-elapsed, you call the service from there. That is the whole integration.

It does not persist anything

There is no Loot save participant. A rolled-but-uncollected pickup on the ground does not survive a save/load cycle unless you save it yourself, and a table's roll history is not recorded anywhere.

This is a known gap rather than a principle — Loot, Pickups and Economy are the three systems with no save participant, and it is on the roadmap rather than ruled out.

It does not guarantee a drop is reproducible across builds

UseDeterministicRng(seed) makes a sequence repeatable within a build, which is what the debugger uses and what makes sampling meaningful. It is not a promise that seed N yields the same drops in a later version: adding an entry, reordering a table or changing a quantity range changes what that sequence produces.

Do not use a loot seed as a save-compatible identifier for "which sword the player got".

It does not retry an undeliverable award

When direct delivery fails and spawning is unavailable or refused, the award is reported through Undelivered and dropped. Nothing queues it, mails it or converts it.

Where an undeliverable award should go is a design decision — some games mail it, some convert it to currency, some simply tell the player their bag is full. A framework guessing would be wrong for most.

It does not enforce that a table is sensible

A table can nest itself indirectly, weight everything at zero, or award nothing at all. LootRoller refuses to recurse past MaxNestingDepth (8) and detects cycles by the path it has walked, so a self-referencing table warns rather than hanging — but a table that is merely useless is authored content, and this system does not second-guess authored content.

It does not own the container

Item awards go to a container named by string, defaulting to the service's DefaultContainer ("Backpack" unless configured). Loot does not create containers, does not know their capacity rules and does not decide what stacking means. All of that is Inventory's, reached through the adapter.

It does not know what an item is

A GUID is the whole of Loot's knowledge about an item. It cannot filter by rarity, level, type or tag, because it cannot see any of those — they live in ItemDefinition, in a system Loot does not reference.

Anything that needs to reason about item properties belongs on your side of an ILootModifier, where you do have Inventory available.

What it does guarantee

  • A roll is a pure function of the table and the RNG sequence.
  • Every award ends up delivered, spawned, or named in Undelivered — never silently dropped.
  • Granted carries what actually landed.
  • Grant never spawns pickups, so a collected pickup cannot spawn another.
  • An absent adapter degrades delivery for that award kind and changes nothing about the roll.
  • Nesting terminates: depth-capped and cycle-detected.