using Theriapolis.Core.Data; using Theriapolis.Core.Items; using Theriapolis.Core.Util; namespace Theriapolis.Core.Loot; /// /// Phase 7 M2 — container-level deterministic loot rolls. Wraps /// with a per-container /// derived from the dungeon's layout seed and the container's slot index, /// so the same (worldSeed, poiId, slotIdx) always rolls the same /// items. /// /// Per Phase 7 plan §4.4 / §5.5: /// lootContainerSeed = dungeonLayoutSeed ^ C.RNG_DUNGEON_LOOT ^ slotIdx /// /// The path is the encounter-drop pipeline (uses /// the encounter's RNG); this path is for static dungeon containers and /// does not advance any encounter-time stream. /// public static class LootGenerator { /// /// Roll a single container's contents. /// /// Loot-table id (e.g. loot_dungeon_imperium_t2). /// /// Per-container seed. Caller is expected to derive this as /// worldSeed ^ C.RNG_DUNGEON_LAYOUT ^ poiId ^ C.RNG_DUNGEON_LOOT ^ slotIdx. /// /// Loot-table dictionary from . /// Item dictionary from . /// An array of ready to drop into an inventory. public static ItemInstance[] RollContainer( string tableId, ulong containerSeed, IReadOnlyDictionary tables, IReadOnlyDictionary items) { var rng = new SeededRng(containerSeed); var drops = LootRoller.Roll(tableId, tables, items, rng); var result = new ItemInstance[drops.Count]; for (int i = 0; i < drops.Count; i++) result[i] = new ItemInstance(drops[i].Def, drops[i].Qty); return result; } /// /// Convenience overload that resolves the per-container seed from the /// dungeon layout seed + slot index per the Phase 7 dice contract. /// public static ItemInstance[] RollContainer( string tableId, ulong dungeonLayoutSeed, int slotIdx, IReadOnlyDictionary tables, IReadOnlyDictionary items) { ulong containerSeed = dungeonLayoutSeed ^ C.RNG_DUNGEON_LOOT ^ (ulong)slotIdx; return RollContainer(tableId, containerSeed, tables, items); } }