# 16 — Loot-Tabellen Type: grilling Status: resolved Blocked by: 02 ## Question Wie werden Beutefunde definiert, gewichtet und ausgewürfelt? Die Gegenstands-Registry aus 02 steht, `luck` ist ein Attribut aus 01. Offen ist alles dazwischen: - **Wo hängt Beute?** Am Gegner, am Raum-Prefab, an der Etage, oder an einer benannten Tabelle, die mehrere referenzieren können? - **Wie wird gewichtet?** Gewichte pro Eintrag, Wahrscheinlichkeit in Prozent, oder Seltenheitsstufen mit fester Verteilung? Dürfen Tabellen andere Tabellen referenzieren? - **Wie wirkt `luck`?** Auf die Drop-Chance, auf die Seltenheitsstufe, auf die Menge — oder auf mehreres, und mit welcher Formel? - **Mengen und Garantien**: Gibt es „genau ein Gegenstand", „null bis drei", garantierte Drops für Bosse? Kann ein Eintrag mehrfach gezogen werden? - **Etagenskalierung**: Fallen auf Etage 5 andere Gegenstände als auf Etage 1, und wird das über getrennte Tabellen ausgedrückt oder über eine Bedingung am Eintrag? - **Wie greifen Mods ein?** Nur neue Tabellen hinzufügen, oder auch Einträge in fremde Tabellen einhängen — und wie sieht dieses Einhängen aus, ohne die fremde Datei zu überschreiben? - **Zufall**: derselbe Generator wie im Kampf (Ticket 14), und gehört sein Zustand in den Speicherstand? Ergebnis: Tabellenschema mit TOML-Beispiel, Gewichtungsregel, `luck`-Formel und die Mod-Einhängepunkte. ## Vorgabe aus Ticket 07 Beute ist `ItemInstance { def, affixes }`, nicht bloß eine Gegenstands-ID. Damit kommt eine Frage dazu, die es vor 07 nicht gab: **Wer vergibt Affixe beim Loot, und mit welcher Gewichtung?** Hängen sie an der Tabelle, am Eintrag, an der Etage — und wirkt `luck` auf die Affix-Zahl, auf deren Seltenheit, oder gar nicht? Affixe sind eine eigene Registry (die sechste), also gilt für sie derselbe Mod-Einhängepunkt wie für Gegenstände. ## Answer Begriffe im [Glossar](../../../CONTEXT.md). Der Zufall war bereits entschieden: derselbe gesetzte Generator aus Ticket 14, dessen Seed im Save liegt — Beute würfelt beim Nachladen nicht neu. ### Eine Maschinerie für Gegenstände und Affixe Zwölfte Registry `[[loot_tables]]`: ```toml [[loot_tables]] id = "core:weapon_affixes" rolls = "0-1" entries = [ { affix = "core:flaming", weight = 5 }, { affix = "core:greater_flaming", weight = 1 }, # Stufe, kein Wert { nothing = true, weight = 20 }, ] [[loot_tables]] id = "core:floor_3_table" rolls = "1-3" entries = [ { item = "core:health_potion", weight = 10, count = "1-2" }, { item = "core:iron_sword", weight = 3, affixes = "core:weapon_affixes" }, { table = "core:rare_table", weight = 1 }, { nothing = true, weight = 20 }, { item = "core:boss_blade", guaranteed = true }, ] ``` **Gewichte statt Prozent** — keine Geschmacksfrage, sondern eine Modding-Frage: Prozentwerte müssen sich auf 100 summieren, und ein hinzugefügter Eintrag macht die Summe kaputt, woraufhin jemand alle anderen anpassen muss. Gewichte verdünnen sich anteilig, ohne dass irgendwer rechnet. **Nieten sind gewöhnliche Einträge**, kein eigenes `drop_chance`-Feld. Ein Gegner, der selten etwas fallen lässt, bekommt ein hohes `nothing`-Gewicht — die Engine kennt eine Mechanik statt zweier. **Tabelle-in-Tabelle** ist eine Zeile und liefert Seltenheitsstufen, ohne dass „Seltenheit" ein Begriff werden muss. **`guaranteed = true`** wird immer ausgegeben und nimmt an der Gewichtung nicht teil. Mit Gewichten allein lässt sich „sicher" nicht ausdrücken, und Bosse stehen in der Destination; Questgegenstände sind derselbe Fall. **Mehrfachziehung erlaubt**, kein `unique`-Flag. Drei Heiltränke sind ein normales Ergebnis, und stapelbare Gegenstände (02) tragen das ohne Zutun. Wer Einzelstücke will, gibt ihnen eine eigene Tabelle mit `rolls = "1-1"`. Die Decke: Falls „nie zweimal dasselbe Schwert" nötig wird, ist `unique` ein Flag — jetzt wäre es eine Regel ohne Fall. ### Affix-Stufen statt Affix-Werte Ticket 07 hat festgelegt, dass eine `ItemInstance` **nur Affix-IDs** trägt. „Flammend" und „Stark flammend" sind deshalb zwei Registry-Einträge mit unterschiedlichen Modifikatoren und Preisfaktoren, keine Zahl am Exemplar. Eine Stärke je Exemplar hätte drei Dinge auf einmal gekostet: das Save-Format (ID-Liste), die Preisformel `base_price × Π price_factor` und die Stapelregel — zwei Schwerter „Flammend 3" und „Flammend 4" stapeln nicht mehr, aber wie zeigt man den Unterschied an? Mit Stufen kann der Modder seine Abstufungen **benennen**, statt eine Zahl auszuliefern, die keine Anzeige hat. ### `luck` hat genau eine Regel **Eine Chance auf einen zusätzlichen Zug — auf jeder Tabelle.** ``` p_extra = clamp(luck * LUCK_FACTOR, 0.0, 0.9) ``` Ein Knopf mehr in `[balance]`. Die Regel wirkt dreifach, ohne dreimal geschrieben zu werden: mehr Gegenstände, mehr Affixe, und häufiger die stärkere Stufe — weil die als eigener Eintrag mit kleinem Gewicht danebensteht. Der Grund gegen „Glück verschiebt zur Seltenheit" ist ein Ausschluss: **Es gibt keinen Seltenheitsbegriff.** Das Schema kennt nur Gewichte; Seltenheit erst zu definieren und dann Gewichte umzurechnen wäre ein zweites Konzept für dasselbe Gefühl. ### Der Mod-Einhängepunkt existiert bereits Ticket 03 gilt unverändert: ganzer Eintrag wird ersetzt. Ein neuer Mechanismus wird trotzdem nicht gebraucht — **Tabelle-in-Tabelle ist der Einhängepunkt**, zwei gewöhnliche Überschreibungen und null neue Begriffe: ```toml [[loot_tables]] id = "meinmod:floor_3" entries = [ { table = "core:floor_3_table", weight = 100 }, { item = "meinmod:zauberstab", weight = 5 } ] [[floors]] id = "core:floor_3" overrides = "core:floor_3" loot = "meinmod:floor_3" ``` Decke: Zwei Mods, die dieselbe Etage umhängen, kollidieren — der letzte gewinnt. Das ist derselbe Konflikt wie bei jeder anderen Überschreibung in 03. Falls er häufig wird, ist `[[loot_additions]]` der Nachbau; ihn *vorher* zu bauen hieße, einen zweiten Mechanismus für etwas einzuführen, das der erste schon kann. ### Beute wird beim Aufheben gezogen `Room` aus Ticket 10 bekommt **eine `loot_table` und `loot: Vec`** statt fertiger Instanzen — alle Haufen eines Raums ziehen ohnehin aus derselben Tabelle, die ID je Haufen zu wiederholen wäre Redundanz. Aufgelöst wird erst beim Aufheben; der Save wird dadurch sogar kleiner. Die **Zahl** der Haufen steht bei der Erzeugung fest (die `l`-Zeichen im Prefab), ihr Inhalt nicht. Alle zeigen **dasselbe generische Symbol** — deshalb verrät die Anzeige nichts, und ein Glücksring wirkt auch auf das, was auf dieser Etage bereits liegt. Bei Ziehung zur Erzeugungszeit wäre der Wurf für diese Etage schon gefallen, bevor der Spieler den Ring findet. Fällt der Wurf beim Aufheben auf eine Niete, **verschwindet der Haufen mit einer Meldung**. Kein Neuwürfeln — das hängt sich bei einer reinen Nieten-Tabelle auf und bräuchte dann doch einen Zähler. Keine Ladeprüfung „Tabellen für `l`-Plätze dürfen keine Nieten enthalten": Das wäre die Bevormundung, die Ticket 02 abgeräumt hat. Wer leere Haufen nicht will, lässt Nieten aus der Tabelle weg. ### Etagenskalierung — die Auflage aus 15 ist strukturell erfüllt Eine Tabelle je Etage, wie Ticket 10 es mit dem `loot`-Feld bereits angelegt hat. **Nirgends steht eine Fünf**: Etagen sind Registry-Einträge, jeder bringt seine Tabelle mit, ein Mod mit Etage 6 bringt ihre gleich mit, und der Endlosmodus wiederholt mit der letzten Etage auch deren Tabelle. Bedingungen am Eintrag (`floor at_least 3` auf einer gemeinsamen Tabelle) klingen flexibler, lösen das Endlos-Problem aber gerade *nicht*: Ein Eintrag mit `at_least = 3` ist auf Etage 500 genauso gültig wie auf Etage 3. Echte Tiefenskalierung bräuchte Gewichte als *Funktion* der Etage — eine Formel, keine Daten, und die wird gebaut, wenn der Endlosmodus kommt. Ein Prefab darf über ein **optionales** `loot`-Feld seine eigene Tabelle nennen. Sonst ist ein Schatzkammer-Raum nur ein Raum mit mehr `l`-Zeichen — mehr vom Gleichen statt etwas Besserem. Es nennt dabei eine Tabellen-**ID**, keine Gegenstände, und bleibt damit über alle Tiefen verwendbar; dieselbe Begründung, mit der 10 festgelegt hat, dass ein `e` nicht sagt, *welcher* Gegner. ### Wo Beute hängt An der `creatures`-Registry (14), an der Etage (10) und optional am Prefab. Alle drei nennen schlicht eine Tabellen-ID. ### Zwei Nachträge an geschlossene Tickets | Ticket | Nachtrag | |---|---| | **10** | `Room` hält `loot_table: LootTableId` plus `loot: Vec` statt `Vec<(Pos, ItemInstance)>`; optionales `loot`-Feld am Prefab; generischer Beute-Marker im Grid | | **14** | `LUCK_FACTOR` in `[balance]` |