docs: ticket 16 (loot-tabellen) aufloesen, nachtraege an 10 und 14, loot-tabelle ins glossar, destination erreicht
This commit is contained in:
@@ -160,7 +160,8 @@ struct Room {
|
||||
prefab: PrefabId, // fuer room_entered und Trigger
|
||||
rect: Rect,
|
||||
enemies: Vec<Creature>,
|
||||
loot: Vec<(Pos, ItemInstance)>,
|
||||
loot_table: LootTableId, // Nachtrag 16: Prefab-loot oder Etagentabelle
|
||||
loot: Vec<Pos>, // Haufen, erst beim Aufheben aufgeloest
|
||||
cleared: bool,
|
||||
}
|
||||
|
||||
@@ -178,6 +179,21 @@ Ladeprüfung über die 04-Maschinerie: **`layout`-Zeilen müssen gleich lang sei
|
||||
kein Rechteck und die Platzierung rechnet mit Müll. Ein Zweizeiler, der eine ganze Klasse von
|
||||
Modder-Frust abfängt.
|
||||
|
||||
### Nachtrag aus Ticket 16
|
||||
|
||||
**Bodenbeute wird erst beim Aufheben gezogen**, deshalb hält `Room.loot` nur noch Positionen und der
|
||||
Raum *eine* `loot_table` — alle Haufen eines Raums ziehen ohnehin aus derselben Tabelle (dem
|
||||
Prefab-`loot` oder, wenn es fehlt, der Etagentabelle). Die *Zahl* der Haufen steht weiterhin bei der
|
||||
Erzeugung fest, es sind die `l`-Zeichen; ihr Inhalt nicht. Der Save wird dadurch kleiner.
|
||||
|
||||
Möglich ist das nur, weil **alle Haufen dasselbe generische Symbol zeigen**: Die Anzeige verrät damit
|
||||
nichts, was noch nicht gewürfelt ist. Der Gewinn ist, dass ein Glücksring auch auf Beute wirkt, die auf
|
||||
dieser Etage schon liegt.
|
||||
|
||||
Dazu bekommt ein Prefab ein **optionales `loot`-Feld**, das die Etagentabelle für seine `l`-Plätze
|
||||
ersetzt — sonst wäre ein `core:treasure`-Raum nur ein Raum mit mehr `l`-Zeichen. Es nennt eine
|
||||
Tabellen-ID, keine Gegenstände, und bleibt damit über alle Tiefen verwendbar.
|
||||
|
||||
### Siegbedingung — bisher nirgends definiert
|
||||
|
||||
Kein Ticket hatte je festgelegt, was beim Abstieg von der letzten Etage passiert.
|
||||
|
||||
@@ -213,6 +213,9 @@ fünfzig Zeilen. Dazu bekommt die `creatures`-Registry ein Feld `exp_reward`.
|
||||
Ausdrücklich **nicht** dazugekommen ist `points_per_level`: Ein Aufstieg gibt genau einen Punkt, das
|
||||
ist eine Regel und kein Stellknopf.
|
||||
|
||||
**Nachtrag aus Ticket 16:** `LUCK_FACTOR` kommt dazu — `luck` gewährt eine Chance auf einen
|
||||
zusätzlichen Zug auf jeder Beutetabelle. Damit hat `[balance]` acht Felder.
|
||||
|
||||
### Vier Nachträge an geschlossene Tickets
|
||||
|
||||
| Ticket | Nachtrag |
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# 16 — Loot-Tabellen
|
||||
|
||||
Type: grilling
|
||||
Status: open
|
||||
Status: resolved
|
||||
Blocked by: 02
|
||||
|
||||
## Question
|
||||
@@ -35,3 +35,147 @@ 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<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]` |
|
||||
|
||||
@@ -17,7 +17,13 @@ dort als Konvention im Body. Zuordnung Ticket → Gitea-Issue:
|
||||
| 13 Script-Engine wählen | [#14](https://gitlab.hive-brain.de/MDietermann/rustkeep/issues/14) | 14 Kampfformeln | [#15](https://gitlab.hive-brain.de/MDietermann/rustkeep/issues/15) |
|
||||
| 15 Progression | [#16](https://gitlab.hive-brain.de/MDietermann/rustkeep/issues/16) | 16 Loot-Tabellen | [#17](https://gitlab.hive-brain.de/MDietermann/rustkeep/issues/17) |
|
||||
|
||||
## Destination
|
||||
## Destination — **erreicht am 2026-08-12**
|
||||
|
||||
Alle sechzehn Tickets sind geschlossen. Was jetzt existiert: sechzehn Entscheidungsdokumente, ein
|
||||
Glossar mit den kanonischen Begriffen, und ein 342-Zeilen-Wegwerfprototyp auf dem Branch
|
||||
`prototyp/12-ratatui-durchstich`. Was **nicht** existiert: das Spiel. Der nächste Schritt ist die
|
||||
Implementierung, und sie hat mit `src/` (heute `print_box`, `include_str!`, `enum ItemType`) fast
|
||||
nichts gemein.
|
||||
|
||||
Alle Architektur- und Design-Entscheidungen für rustkeep 1.0 sind getroffen — nicht gebaut, entschieden.
|
||||
1.0 heißt: 5 Etagen mit Boss, Solo-Held mit Rasse und Klasse, Grid-Erkundung mit Menü-Kampf,
|
||||
@@ -78,6 +84,7 @@ auch über Ticket 13.
|
||||
- [13 — Script-Engine wählen](issues/13-script-engine-waehlen.md) ([#14](https://gitlab.hive-brain.de/MDietermann/rustkeep/issues/14)) — **Rhai**, aber die Wahl ist dank der Leitplanke aus 05 nur noch eine *Implementierungs*-Entscheidung: Scripts lesen ueber eine Handvoll Funktionen (gespiegelt an der Bedingungsgrammatik aus 08 — sie sehen nichts, was eine Bedingung nicht auch saehe) und schreiben ausschliesslich ueber Aktionen. Begruendung unbequem, aber ehrlich — **fuer ein unveroeffentlichtes Terminal-Roguelike schreibt niemand Mods**, der Lua-Vorsprung zahlt sich erst mit einer Gemeinde aus. Mods gelten als **vertrauenswuerdig** (wie bei Factorio, Minecraft, Skyrim), gehaertet wird gegen Unfaelle: acht Engine-Schalter, gegen die Doku geprueft; `set_max_operations` ausdruecklich **nicht** in `[balance]`, sonst hebt ein Mod sein eigenes Limit auf. `rand()` laeuft durch den Seed aus 14, sonst wuerfelten ausgerechnet Scripts beim Laden neu. `run_script` als neunte Aktion, Scripts als **elfte Registry**. Risiken beobachtet mit benanntem Ausstiegssignal; `smartstring` ist ein INFO-Advisory, kein Ausstiegsgrund. **Stufe (c) wandert hinter 1.0** — eine Script-Engine zu bauen, bevor jemand an eine Vokabular-Luecke gestossen ist, baut die Antwort vor der Frage.
|
||||
- [14 — Kampfformeln und Rundenablauf](issues/14-kampfformeln-und-rundenablauf.md) ([#15](https://gitlab.hive-brain.de/MDietermann/rustkeep/issues/15)) — **Ein Zug = Spielerhandlung plus alle Gegnerreaktionen**, sonst tickte ein Debuff bei drei Gegnern viermal. Initiative als `i32` **am Wesen** statt als Reihenfolgeliste (die waere eine zweite Indexsammlung mit demselben Ungueltigkeitsproblem); tote Gegner erst am Ende von `execute`, nach dem Leeren der Warteschlange. Trefferformel und Schadenskette mit drei Klemmbereichen — `0.8` bei Resistenz, weil `1.0` Immunitaet und damit ein Spielabbruch waere. **Die Krit-Reihenfolge-Frage loest sich auf**: multiplikativ ist kommutativ. Eigener xorshift, **Seed im Save** — Nachladen wuerfelt nicht neu. Faehigkeiten kosten Ausdauer, Abklingzeit in Zuegen, gesperrt ⇒ `Rejected` ohne Zug. **Achte Registry: Wesen** — die Luecke lag seit 02 offen; `ai` als geordnete Bedingungsliste. `[balance]` verschmilzt **feldweise**, ausdrueckliche Ausnahme zu 03 (festes Schema). Vier Nachtraege: `scale` und `flee` an 01, drei Felder an 07, zwei Events an 08, `GameOver` an 11.
|
||||
- [15 — Progression und Level-up](issues/15-progression-und-levelup.md) ([#16](https://gitlab.hive-brain.de/MDietermann/rustkeep/issues/16)) — Ein Aufstieg gibt **genau einen** frei verteilbaren Punkt auf die fuenf Grundwerte; `AssignPoints` ist **ein** Command mit allen Punkten (drei Stufen waeren sonst drei Zuege im Menue). **Keine Obergrenze** — `clamp(1, 50)` faellt, die Kurve begrenzt sich ueber steigende Kosten (`exp_base`, `exp_exponent` in `[balance]`). Erfahrung aus Toetungen (`exp_reward` am Wesen) und Quests (`give_exp`, zehnte Aktion), **nicht** fuers Betreten einer Etage — das belohnte das Durchrennen. **Die Zuwachs-Regel bleibt eng**: nur Aufstieg und `AssignPoints` heben den Lebensstand mit `max_health`, Ausruestung nie — sonst „heilt ein Ring beim Anlegen" (01). Rasse und Klasse bleiben **flach**, `per_level` entfaellt ersatzlos; was sie traegt, sind Faehigkeiten. `level` darf modifiziert werden, aber Punkte haengen an `base.level` — ein `+1 level`-Ring schenkt keinen. Vier Nachtraege: `give_exp` an 01, `current_exp`/`unspent_points` an **`Game`** statt ans Wesen (07 hatte die Platzierung hierher delegiert), `AssignPoints` an 11, zwei Balance-Knoepfe plus `exp_reward` an 14.
|
||||
- [16 — Loot-Tabellen](issues/16-loot-tabellen.md) ([#17](https://gitlab.hive-brain.de/MDietermann/rustkeep/issues/17)) — Zwoelfte Registry `[[loot_tables]]`, **eine Maschinerie fuer Gegenstaende und Affixe**. **Gewichte statt Prozent**, weil Prozente sich auf 100 summieren muessen und ein hinzugefuegter Mod-Eintrag die Summe kaputt macht. Nieten sind gewoehnliche Eintraege (kein `drop_chance`), Tabelle-in-Tabelle liefert Seltenheitsstufen, ohne dass „Seltenheit" ein Begriff wird. **Affix-Stufen statt Affix-Werte** — eine Staerke je Exemplar haette Save-Format, Preisformel und Stapelregel aus 07 auf einmal gekostet. **`luck` hat genau eine Regel**: Chance auf einen zusaetzlichen Zug auf jeder Tabelle — wirkt dreifach (mehr Gegenstaende, mehr Affixe, haeufiger die staerkere Stufe), ohne dreimal geschrieben zu werden. Der **Mod-Einhaengepunkt existiert schon**: Tabelle-in-Tabelle plus zwei Ueberschreibungen, kein zweiter Mechanismus. Bodenbeute wird **erst beim Aufheben** gezogen (`Room` haelt eine `loot_table` plus Positionen), moeglich nur weil alle Haufen dasselbe Symbol zeigen — dadurch wirkt ein Gluecksring auch auf das, was schon liegt. `guaranteed = true` fuer Bosse, Mehrfachziehung erlaubt. Nachtraege: `Room`-Umbau und optionales Prefab-`loot` an 10, `LUCK_FACTOR` an 14.
|
||||
|
||||
## Not yet specified
|
||||
|
||||
|
||||
Reference in New Issue
Block a user