Files
rustkeep/.scratch/rustkeep-1.0/issues/16-loot-tabellen.md

8.6 KiB
Raw Blame History

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. 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]]:

[[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:

[[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<Pos> 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<Pos> statt Vec<(Pos, ItemInstance)>; optionales loot-Feld am Prefab; generischer Beute-Marker im Grid
14 LUCK_FACTOR in [balance]