Skip to content

Loot — Abstractions

Folder Overview

This folder contains the contracts that let Loot award things it knows nothing about.

Rolling produces a LootResult describing what was won. Turning that description into items in a container, currency in a wallet, or objects in the world is somebody else's job — and these are the interfaces that somebody implements.


Purpose

The Loot assembly references no other gameplay system. Not Inventory, not Currency, not Pickups. These abstractions are how that stays true while loot still delivers real rewards.

Every implementation lives in Integrations/Loot/, gated on both REV_LOOT_PRESENT and the define for the system it bridges to. Delete Inventory and the inventory adapter compiles out; Loot itself keeps building and keeps rolling.


What Lives Here

Delivery adapters

  • ILootInventoryAdapter — resolves an owner's container to an opaque handle, then adds an exact quantity of one item.
  • ILootCurrencyAdapter — credits an amount of one currency to an owner.
  • ILootPickupSpawner — spawns an award into the world instead of granting it directly.

All three are optional. A project with only Inventory installed rolls currency awards perfectly well; it simply has nowhere to put them, and LootService reports them through Undelivered rather than discarding them quietly.

Modifier seam

  • ILootModifier — adjusts a rolled award for one owner. Luck, quantity bonuses, event multipliers.

Important Notes

Items travel as GUID strings, handles as object

An entry names an item by GUID and a container by an opaque object handle the adapter defines itself. That is the same arrangement ICraftingInventoryAdapter uses, for the same reason: a typed ItemStack in this signature would drag a hard Inventory reference into an assembly that must compile without it.

TryAddExact is all-or-nothing, and has no space-check companion

A false result must leave the container untouched. That guarantee is what lets LootService try delivery first and fall back to spawning the award into the world, with no risk of a half-added stack existing in both places.

There is deliberately no HasSpaceFor alongside it. Crafting has one because it must know an output will fit before it consumes the inputs. Loot consumes nothing, so it can simply try — and adding a space check here would mean walking the container twice per award to learn what the add call already reports.

Modifiers adjust what was won, not what could drop

LootService collects ILootModifier with GetComponentsInParent, so adding the component is the entire wiring step — no registration, no bookkeeping. But modifiers run against the result, never against the table.

That is a deliberate limit rather than an oversight. Luck, magic find and pity counters want to change the odds themselves, and this seam cannot express them. The reason it does not is that a table whose printed weights do not describe its own behaviour is very hard to reason about, and harder still to report a bug against. Odds-level modification is recorded as a future consideration rather than quietly approximated here.