# Aeshnidae.LootTables

ACE's code-side loot tables, editable at runtime - the per-tier tables that are C# in
`ACE.Server.Factories.Tables`, not rows in the world database: workmanship, spell level,
armour and weapon type, gem class, cantrip count and level, the treasure-profile item kinds,
the per-tier item lists (`Wcids.*`), item value by tier, and so on. 420 tables on ACE as of
2026-09-24. The staff site edits them: https://aelrynth.com/staff/loot/code (Developer+).

The database loot tables (profiles, wielded gear, gems, materials) are a different thing,
edited on the other tabs of /staff/loot and reloaded by DevKit's `/loot-reload`.

## How

| File | What |
| --- | --- |
| `Tables.cs` | Finds every static `ChanceTable<T>` / `List<float>` / `List<int>` in the Tables namespace (plus `LootGenerationFactory`, `MissileMagicDefense`) by reflection, snapshots retail, applies overrides, writes the catalogue. |
| `Notes.cs` | What each class of table is, in words, and which tables the game never reads (marked NOT USED). |
| `Mod.cs` | Load: discover, apply `overrides.json`, write `catalog.json`. Unload: retail back. |
| `Commands.cs` | `/loottables` (status) and `/loottables-apply` (re-read and apply; console-safe). Developer. |

- **Edited in place.** Tier lists captured the table objects at startup, so a table is
  `Clear()`ed and refilled, never replaced. `ChanceTable`'s `verified` flag is reset.
- **On the world thread.** Loot rolls on several landblock threads; an apply is queued with
  `WorldManager.EnqueueAction` and runs between ticks (or at once while the world is closed).
- **All or nothing per table.** A table is refused whole if a value does not parse, a
  chance is outside 0-1, its chances do not add up to 1 (±0.001; every retail table does),
  a per-tier list changes length, or an object-valued table (`GemResult`) changes count.
  Refusals are logged and listed in the catalogue; every other table still applies.
- **Retail is the default.** A table not in `overrides.json` is retail. Every apply first
  puts every table back to retail, so removing an entry and applying restores it.
- CantripChance's scaled copies are rebuilt after an apply.

## Files beside the dll

- `overrides.json` - `{ "Tables": { "WorkmanshipChance.T3_Chances": [ {"Value":"4","Chance":0.2}, ... ] } }`.
  Written by the staff site; hand edits work too (then `/loottables-apply`).
- `catalog.json` - every table, retail and current, class notes, the last apply's
  refusals. Written after each apply for the staff site.
- `Settings.json` - the two file names.

The staff site keeps every change (table before and after, the difference, a revert) in
`~/loot-code-changes` and posts it to #content. A save the mod refuses is taken back.
