docs: ticket 10 (raum-prefab-format) aufloesen, nachtraege an 08 und 14, prefab und etage ins glossar
This commit is contained in:
@@ -102,6 +102,20 @@ Stufe (c) kann niemand ein eigenes Event *auslösen* — ein namespaced Event w
|
||||
Kommt Scripting (Ticket 13), wird `emit_event` eine gewöhnliche Aktion und die Liste bekommt eine
|
||||
`Custom(String)`-Variante. Ein Zusatz, kein Umbau.
|
||||
|
||||
### Nachtrag aus Ticket 10: zwölf Events
|
||||
|
||||
`room_entered { prefab_id }` kommt dazu. `floor_entered` deckt nur die Etage ab — „die Tür fällt hinter
|
||||
dir zu", ein Hinterhalt oder eine Quest „finde die Bibliothek" brauchen den einzelnen Raum. Prefabs
|
||||
tragen Trigger wie jeder andere Registry-Inhalt, neue Maschinerie kostet es also nicht.
|
||||
|
||||
Das Event trägt zugleich den Kampfbeginn: Ein `room_entered` in einen Raum mit lebenden Gegnern
|
||||
verschiebt sie nach `Game.enemies`.
|
||||
|
||||
**Beobachtung zur Liste selbst:** Sie wurde hier als „fest und geschlossen" festgelegt, und seither hat
|
||||
*jedes* Ticket, das echte Spielsituationen beschreibt, einen Eintrag gebraucht — 14 zwei, 10 einen. Das
|
||||
spricht nicht gegen die Entscheidung (namespaced Mod-Events wären ohne Scripting weiterhin sinnlos),
|
||||
aber wer 15 oder 16 bearbeitet, sollte mit einem weiteren rechnen.
|
||||
|
||||
### Nachtrag aus Ticket 14: elf Events statt neun
|
||||
|
||||
`combat_started` und `combat_ended { outcome: Victory | Fled }` kommen dazu. Ohne sie ist „gewinne drei
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# 10 — Raum-Prefab-Format
|
||||
|
||||
Type: grilling
|
||||
Status: open
|
||||
Status: resolved
|
||||
Blocked by: 03
|
||||
|
||||
## Question
|
||||
@@ -35,3 +35,170 @@ Prefabs sind eine **weitere Registry über dieselbe Ladepipeline**, kein eigener
|
||||
beantwortet die letzte Frage oben halb: Hinzufügen ist der Normalfall, Ersetzen geht über `overrides`.
|
||||
Offen bleibt, ob **Generator-Parameter** (Etagen-Tabellen, Gewichtungen, Pflichträume) ebenfalls
|
||||
Registry-Einträge sind — dann mit derselben Maschine moddbar — oder ein Sonderfall.
|
||||
|
||||
## Answer
|
||||
|
||||
Begriffe im [Glossar](../../../CONTEXT.md).
|
||||
|
||||
### Etagenaufbau: freie Platzierung mit Korridoren
|
||||
|
||||
Prefabs werden nacheinander an zufälliger Position gesetzt, Überlappung (plus ein Feld Rand)
|
||||
verworfen, nach N Fehlversuchen aufgegeben — der klassische Rogue-Ansatz.
|
||||
|
||||
Verbunden wird **Raum `i` mit Raum `i−1`** in Platzierungsreihenfolge, danach ein paar zufällige
|
||||
Zusatzkorridore für Schleifen. Korridore als L-Form, erst horizontal, dann vertikal.
|
||||
|
||||
Die Kette statt eines minimalen Spannbaums, weil sie **Erreichbarkeit strukturell garantiert**: Hängt
|
||||
jeder Raum am vorigen, ist jeder Raum von jedem erreichbar — ohne Flutfüllung und ohne
|
||||
Reparaturschleife. Das beantwortet die Garantiefrage aus dem Ticketkörper nebenbei mit. Prim wären
|
||||
zwanzig Zeilen für dasselbe Ergebnis mit hübscheren Korridorlängen.
|
||||
|
||||
**Rotation und Spiegelung sind erlaubt** — acht Varianten je Prefab, und das Drehen eines ASCII-Blocks
|
||||
ist eine Transposition. Achtmal so viel Inhalt aus derselben Handarbeit.
|
||||
|
||||
Kein Opt-out-Feld: 1.0 hat kein Prefab, dessen Ausrichtung etwas bedeutet. Taucht eines auf, ist
|
||||
`rotate = false` eine Zeile — vorher wäre es ein Schalter, den niemand stellt.
|
||||
|
||||
### Ein Prefab
|
||||
|
||||
```toml
|
||||
[[prefabs]]
|
||||
id = "core:kreuzung"
|
||||
tags = ["core:normal"]
|
||||
layout = """
|
||||
####+####
|
||||
#.......#
|
||||
#..e...e#
|
||||
+.......+
|
||||
#..l....#
|
||||
#.......#
|
||||
####+####
|
||||
"""
|
||||
```
|
||||
|
||||
| Zeichen | Bedeutung | | Zeichen | Bedeutung |
|
||||
|---|---|---|---|---|
|
||||
| `#` | Wand | | `l` | Beute-Platz |
|
||||
| `.` | Boden | | `>` | Treppe |
|
||||
| `+` | Tür | | `<` | Eingang |
|
||||
| `e` | Gegner-Platz | | `n` | NPC-Platz |
|
||||
|
||||
Kein eigener Händler-Marker: **Ein Händler ist ein NPC.** Ein zweites Zeichen für denselben Platz wäre
|
||||
dieselbe Sorte Vermischung, die Ticket 02 beim `ItemType` abgeräumt hat.
|
||||
|
||||
Marker stehen **im Block**, nicht als Koordinatenliste daneben. Der ganze Sinn von ASCII ist, dass ein
|
||||
Modder den Raum *sieht*; Koordinaten daneben zu pflegen wäre die schlechtere Hälfte von beidem.
|
||||
|
||||
**Türen sind explizit.** Ohne sie stanzt der Generator Löcher in eine von Hand gezeichnete Wand — wer
|
||||
Prefabs baut, will bestimmen, wo man hineinkommt, sonst hätte man auch würfeln können. Korridore
|
||||
dürfen nur an `+` andocken.
|
||||
|
||||
**Ein `e` sagt „hier steht ein Gegner", nicht *welcher*.** Was einzieht, entscheidet die Etage. Sonst
|
||||
wäre jedes Prefab an eine Schwierigkeitsstufe gebunden und derselbe hübsche Raum ließe sich nicht auf
|
||||
Etage 1 *und* auf Etage 5 verwenden — genau die Wiederverwendung, für die man Prefabs baut.
|
||||
|
||||
Bei `n` ist es **umgekehrt**: Den NPC nennt das Prefab selbst (`npcs = ["core:blacksmith"]`), nicht
|
||||
die Etage. NPCs skalieren nicht mit der Tiefe — ein Schmied auf Etage 5 ist derselbe Schmied wie auf
|
||||
Etage 1, und ein `core:merchant`-Raum, dessen Händler ausgewürfelt würde, wäre kein Händlerraum. Das
|
||||
Feld selbst gehört dem NPC-Ticket; hier steht nur, auf welcher Seite die Entscheidung liegt.
|
||||
|
||||
### Die Etage
|
||||
|
||||
```toml
|
||||
[[floors]]
|
||||
id = "core:floor_3"
|
||||
number = 3
|
||||
size = [80, 40] # Felder
|
||||
rooms = 9 # Zielzahl inklusive Pflichtraeume
|
||||
required = ["core:entrance", "core:stairs", "core:merchant"] # Tags
|
||||
prefabs = [ { tag = "core:normal", weight = 10 }, { tag = "core:treasure", weight = 2 } ]
|
||||
creatures = [ { id = "core:skeleton", weight = 5 }, { id = "core:ghoul", weight = 2 } ]
|
||||
loot = "core:floor_3_table"
|
||||
```
|
||||
|
||||
Ablauf: **Pflichträume zuerst** auf die leere Fläche, danach gewichtet auffüllen bis `rooms` erreicht
|
||||
oder die Versuche erschöpft sind.
|
||||
|
||||
Ausgewählt wird durchgehend über **Tags**, auch bei den Pflichträumen. IDs würden bedeuten, dass ein
|
||||
Mod keinen eigenen Eingangsraum beisteuern kann, ohne die ganze Etagen-Tabelle zu überschreiben.
|
||||
|
||||
Dass Pflichträume zuerst kommen, *ist* die Garantie — auf einer leeren Fläche passen sie praktisch
|
||||
immer. Optionale Räume, die nicht mehr passen, entfallen einfach; das ist der Normalfall, kein Fehler.
|
||||
Passt die Flächensumme der Pflicht-Tags **grundsätzlich** nicht in `size`, ist das ein Datenfehler und
|
||||
beim Laden erkennbar: Warnzeile über die Maschinerie aus 04. Zur Laufzeit dann kein
|
||||
Wiederholungszähler und kein Rückfallpfad — die Prüfung sitzt dort, wo der Fehler gemacht wurde.
|
||||
|
||||
`core:merchant` steht unter `required` und nicht unter `prefabs`: Ticket 07 hat Backtracking
|
||||
ausgeschlossen, der Händler muss also garantiert auftauchen statt gewichtet.
|
||||
|
||||
Damit beantwortet sich auch die Frage aus der 03-Vorgabe: **Generator-Parameter sind Daten.**
|
||||
`prefabs` und `floors` sind zwei gewöhnliche Registries — ein Mod ergänzt Etage 6 oder ersetzt Etage 3
|
||||
über `overrides`, ohne dass hier etwas erfunden wird. Die Gesamtzahl steigt damit auf **zehn**.
|
||||
|
||||
### Kampf beginnt beim Betreten eines Raums
|
||||
|
||||
Die Bestellung aus 14, wer `Game.enemies` füllt: **Gegner gehören zum Raum, nicht zur Etage.** Ein
|
||||
`room_entered` in einen Raum mit lebenden Gegnern verschiebt sie nach `Game.enemies` und startet den
|
||||
Kampf; nach dem Sieg gilt der Raum als geräumt und ein erneutes Betreten startet nichts mehr.
|
||||
|
||||
Der Grund ist, was dadurch **nicht** gebaut werden muss: Frei umherlaufende Gegner bräuchten Pfadsuche,
|
||||
Sichtlinien, Verfolgung und ein Bewegungsbudget pro Zug — ein eigenes System, das niemand bestellt hat
|
||||
und das die Charter mit „Kampf im Menü" auch nicht nahelegt. Die `e`-Plätze aus dem Prefab sind
|
||||
schlicht die Gegnerliste des Raums.
|
||||
|
||||
### `Floor` — der Innenbau aus Ticket 07
|
||||
|
||||
```rust
|
||||
struct Floor {
|
||||
number: u8,
|
||||
tiles: Vec<Tile>, width: u16, height: u16,
|
||||
rooms: Vec<Room>,
|
||||
player_room: Option<usize>,
|
||||
}
|
||||
|
||||
struct Room {
|
||||
prefab: PrefabId, // fuer room_entered und Trigger
|
||||
rect: Rect,
|
||||
enemies: Vec<Creature>,
|
||||
loot: Vec<(Pos, ItemInstance)>,
|
||||
cleared: bool,
|
||||
}
|
||||
|
||||
enum Tile { Wall, Floor, Door, Stairs, Entrance }
|
||||
```
|
||||
|
||||
Korridore gehören zu **keinem** Raum — sie sind schlicht `Tile::Floor` außerhalb aller `rect`. Dadurch
|
||||
feuert `room_entered` sauber beim Übergang Korridor → Raum, ohne Sonderfall, und `player_room: None`
|
||||
ist ein echter Zustand statt eines Fehlers.
|
||||
|
||||
Gegner werden bei Kampfbeginn **verschoben, nicht kopiert** — sonst gäbe es zwei Wahrheiten über
|
||||
denselben Gegner.
|
||||
|
||||
Ladeprüfung über die 04-Maschinerie: **`layout`-Zeilen müssen gleich lang sein**, sonst ist der Block
|
||||
kein Rechteck und die Platzierung rechnet mit Müll. Ein Zweizeiler, der eine ganze Klasse von
|
||||
Modder-Frust abfängt.
|
||||
|
||||
### Siegbedingung — bisher nirgends definiert
|
||||
|
||||
Kein Ticket hatte je festgelegt, was beim Abstieg von der letzten Etage passiert.
|
||||
|
||||
**`Descend` auf der letzten definierten Etage gewinnt den Lauf.** Es gibt keine Zahl „5" im Code: Die
|
||||
Engine sucht `[[floors]]` mit `number = aktuell + 1` und findet sie keine, ist Schluss. Ein Mod, der
|
||||
Etage 6 ergänzt, verlängert das Spiel ohne eine Zeile Engine-Code — dieselbe Bewegung wie bei
|
||||
`core:attack` in Ticket 11: Was wie Engine-Logik aussieht, ist Inhalt.
|
||||
|
||||
### Zwei Nachträge an geschlossene Tickets
|
||||
|
||||
| Ticket | Nachtrag |
|
||||
|---|---|
|
||||
| **08** | `room_entered { prefab_id }` als **zwölftes** Event. `floor_entered` deckt nur die Etage ab; „die Tür fällt hinter dir zu", ein Hinterhalt oder eine Quest „finde die Bibliothek" brauchen den einzelnen Raum. Neue Maschinerie kostet es nicht — Prefabs sind gewöhnlicher Registry-Inhalt und dürfen Trigger tragen wie alles andere |
|
||||
| **14** | `GameOver { outcome: Died \| Won }` statt nur `GameOver`. Der Bildschirm zeigt in beiden Fällen dasselbe — Etage, Zug, Gold, Verlauf —, nur die Überschrift unterscheidet sich. Zwei Screens für zwei Texte wären Verdopplung |
|
||||
|
||||
### Auflagen an andere Tickets
|
||||
|
||||
- **09 Save-Schema**: Layout oder Seed? Ticket 14 hat einen seedbaren Generator geliefert, Regenerieren
|
||||
*wäre* also möglich — aber der Raumzustand (`cleared`, gefallene Gegner, genommene Beute) lässt sich
|
||||
nicht regenerieren und muss ohnehin mit. 09 entscheidet, ob sich der Seed dann noch lohnt.
|
||||
- **16 Loot-Tabellen**: `loot` an der Etage verweist auf eine Tabelle, die `l`-Plätze ziehen daraus.
|
||||
- **Boss-Design**: Ein Boss ist derzeit ein Gegner in einem Prefab mit `core:boss`-Tag. Phasen fehlen
|
||||
weiterhin.
|
||||
|
||||
@@ -120,7 +120,9 @@ Trank.
|
||||
`MainMenu` (trägt die Ladewarnungs-Zeile aus 04) · `CharacterCreation` (Registry-Schleife aus 02) ·
|
||||
`Explore` · `Combat` · `Inventory` · `CharacterSheet` · `QuestLog` · `Shop` · `Dialog` · `Confirm`
|
||||
|
||||
**Nachtrag aus Ticket 14: `GameOver` als elfter Screen**, Vollbild. Die lazy Variante — zurück ins
|
||||
**Nachtrag aus Ticket 14: `GameOver { outcome: Died | Won }` als elfter Screen**, Vollbild. Das
|
||||
`outcome` kam aus Ticket 10 dazu — der Abstieg von der letzten definierten Etage gewinnt den Lauf, und
|
||||
Sieg wie Tod zeigen dieselben Zahlen unter einer anderen Überschrift. Die lazy Variante — zurück ins
|
||||
`MainMenu` mit einer Meldungszeile — wurde bewusst verworfen: Ein Abstiegs-Roguelike lebt davon, dass
|
||||
der Lauf am Ende *erzählt* wird (Etage, Zug, Gold, Todesursache). Das ist der Moment, für den man fünf
|
||||
Etagen gespielt hat, und kein anderer Screen kann ihn übernehmen.
|
||||
|
||||
@@ -213,7 +213,7 @@ Zeile lang.
|
||||
| **01** | `damage`-Aktion: `menge = amount + scale * damage-Attribut(Verursacher)`, beide Felder optional (Standard 0). Deckt Basisangriff (`scale = 1.0`), Feuerball (`amount = 20`) und Wuchtschlag (`scale = 1.5`) mit *einer* Formel. Dazu `flee` als **achte Aktion** |
|
||||
| **07** | `initiative: i32` und `cooldowns: HashMap<AbilityId, u32>` am `Creature`; RNG-Seed in `Game`; **`enemies` ist der laufende Kampf**, nicht die Etage |
|
||||
| **08** | `combat_started` und `combat_ended { outcome: Victory \| Fled }` — die Eventliste wächst auf **elf**. Der Tod des Helden braucht keine dritte Variante, `creature_died` mit `source = Player` deckt ihn ab |
|
||||
| **11** | **`GameOver`** als elfter Screen, Vollbild. Bewusst nicht die lazy Variante mit einer Meldungszeile im Hauptmenü: Ein Abstiegs-Roguelike lebt davon, dass der Lauf am Ende erzählt wird — Etage, Zug, Gold, Todesursache |
|
||||
| **11** | **`GameOver`** als elfter Screen, Vollbild. Bewusst nicht die lazy Variante mit einer Meldungszeile im Hauptmenü: Ein Abstiegs-Roguelike lebt davon, dass der Lauf am Ende erzählt wird — Etage, Zug, Gold, Todesursache. *Ticket 10 hat daraus `GameOver { outcome: Died \| Won }` gemacht, weil der Abstieg von der letzten Etage den Lauf gewinnt* |
|
||||
|
||||
### Auflagen an andere Tickets
|
||||
|
||||
|
||||
@@ -67,6 +67,7 @@ auch über Ticket 13.
|
||||
- [06 — Research: ratatui-App-Architektur](issues/06-research-ratatui-architektur.md) ([#7](https://gitlab.hive-brain.de/MDietermann/rustkeep/issues/7)) — Screen-Stack über zentralem `Game`-State statt Elm oder Component-Trait; `game/` bleibt ratatui-frei; Popups per `Clear` auf Sub-`Rect` statt Widget-Stack; blockierendes `event::read()` ohne Tick; Dungeon-Grid ist Eigenbau, es gibt kein Widget dafür; ratatui 0.30.2 mit MSRV 1.88; `KeyEventKind::Press`-Filter unter Windows Pflicht. Voller Bericht in [research/06](research/06-ratatui-architektur.md).
|
||||
- [07 — Game-State-Baum](issues/07-game-state-baum.md) ([#8](https://gitlab.hive-brain.de/MDietermann/rustkeep/issues/8)) — `Registries` lebt **neben** `Game`, nicht darin: als Feld kollidierte `&mut game.player` mit `&game.registries` bei jedem Methodenaufruf. Verweise per `String`-ID, keine Indizes (die hängen an der Ladereihenfolge). Ein `Creature` für Held und Gegner, aber **zwei Felder** `player` und `enemies` statt einer `Vec` — Solo-Held heißt, „Gegner greift Gegner an" existiert nicht, und zwei Felder sind ein disjunkter Borrow. Ein `Floor`, kein Backtracking; Händler müssen deshalb auf jeder Etage vorkommen können. **Instanzzustand kommt**: `ItemInstance { def, affixes }`, und **Affixe sind die sechste Registry** — dadurch bleibt der Save eine ID-Liste und der Preis `base_price × Π price_factor`. Stapeln bei gleicher `def` und gleicher `affixes`. `enum CreatureRef`, dazu je ein Eingang `Game::apply` und `Game::attribute` — Letzteres auf `Game`, weil Auren beim Träger wohnen und ein `Creature` seine Attribute allein gar nicht ausrechnen kann. Kein Attribut-Cache.
|
||||
- [08 — Event- und Trigger-System](issues/08-event-trigger-system.md) ([#9](https://gitlab.hive-brain.de/MDietermann/rustkeep/issues/9)) — **Der Trigger ist die vierte Wirkungsform** neben Modifikator, Aktion und Fähigkeit: `{ on, condition, actions }` darf überall stehen, wo `modifiers` und `actions` stehen — sonst entstünde dieselbe Maschine ein zweites Mal in 14 als `on_hit`. Eine Bedingungsgrammatik für Trigger *und* Modifikatoren: getaggtes Enum, Liste ist UND, `not` je Eintrag, **kein ODER** (zwei Einträge oder ein gemeinsames Tag). Neun feste Events ohne Namensraum, jedes mit `source`/`target`; `attack_hit` und `damage_taken` bleiben getrennt, weil der eine beim Angreifer und der andere beim Getroffenen hängt. Im Trigger ist `self` immer der Träger. **Warteschlange statt synchron** — synchrones Feuern wäre ein Borrow-Fehler (über `self.quests` iterieren, während `self.apply` läuft) — mit Zähler bei 100 Events pro Zug statt Zyklengraph. Trigger werden gesucht, nicht indiziert. **Quest = Trigger mit `count`**, siebte Registry, erledigte bleiben markiert stehen. Zwei Nachträge an geschlossene Tickets: `start_quest` als siebte Aktion in 01, und `against: Option<CreatureRef>` plus `events: Vec<Event>` in 07.
|
||||
- [10 — Raum-Prefab-Format](issues/10-raum-prefab-format.md) ([#11](https://gitlab.hive-brain.de/MDietermann/rustkeep/issues/11)) — Freie Platzierung mit L-Korridoren; verbunden wird **Raum `i` mit Raum `i−1`**, weil eine Kette Erreichbarkeit *strukturell* garantiert statt sie nachtraeglich zu pruefen. Prefab als ASCII-Block mit Markern im Block, **Tueren explizit** (sonst stanzt der Generator Loecher in gezeichnete Waende); Rotation und Spiegelung erlaubt, `rotate = false` als Opt-out. **Ein `e` sagt „hier steht ein Gegner", nicht welcher** — das entscheidet die Etage, sonst waere jedes Prefab an eine Schwierigkeitsstufe gebunden. `[[prefabs]]` und `[[floors]]` sind die neunte und zehnte Registry; Auswahl durchgehend ueber **Tags**, Pflichtraeume zuerst platziert (das *ist* die Garantie), Flaechenpruefung beim Laden. **Kampf beginnt bei `room_entered`**, Gegner gehoeren zum Raum — das erspart Pfadsuche, Sichtlinien und Verfolgung. `Floor` mit `rooms`, `player_room: Option<usize>` (Korridor ist ein echter Zustand). **Siegbedingung erstmals definiert**: kein `[[floors]]` mit der naechsten Nummer = gewonnen, die Zahl „5" steht nirgends im Code. Nachtraege: `room_entered` an 08 (zwoelf Events), `GameOver { outcome }` an 14.
|
||||
- [11 — UI-Gerüst und Screen-Modell](issues/11-ui-geruest-screen-modell.md) ([#12](https://gitlab.hive-brain.de/MDietermann/rustkeep/issues/12)) — **„Aktion" hiess zwei Dinge**: `Action` bleibt das TOML-Effektvokabular aus 01, `Command` wird das UI-Kommando, Eingang `Game::execute(cmd, reg)` — die eine Stelle, an der ein Zug endet (Modifikatoren ticken, Event-Warteschlange leerarbeiten). Zwölf Commands, **alle kosten einen Zug** (ein `costs_turn`-Flag waere eine Ausnahmeliste); Angriff und Flucht sind keine Varianten, sondern `UseAbility("core:attack")` und `UseAbility("core:flee")` — damit moddbar. Ein gemeinsamer `Vec<Screen>` fuer Screens und Popups; Screens **geben `Response` zurueck statt selbst zu schieben**, sonst haelt ein Screen ein `&mut` auf die `Vec`, in der er liegt. Ein `Response::Command` schliesst alle Overlays — dasselbe Flag, das schon fuers Rendern existiert. Zehn Screens, `ListState` in der Variante (der Stack erhaelt den Cursor gratis, solange der Screen liegt). Nur `KeyEventKind::Press`: bei `Repeat` bestimmte die Tastaturwiederholrate die Spielzeit; gegen zaehes Laufen hilft ein Renn-Kommando in `game/`. Tastenbelegung fest, Pfeile und `hjkl`. ratatui-Freiheit von `game/` prueft ein `#[test]` statt eines Workspace. Nachtrag an 01: `symbol` traegt das Zeichen und einen Farbnamen selbst — `ui/theme.rs` ist Code und damit fuer Modder unerreichbar.
|
||||
- [12 — Prototyp: ratatui-Durchstich](issues/12-prototyp-ratatui-durchstich.md) ([#13](https://gitlab.hive-brain.de/MDietermann/rustkeep/issues/13)) — **Das Modell aus 11 traegt.** Primaerquelle: Branch `prototyp/12-ratatui-durchstich`, `cargo run --example durchstich`. Bestaetigt: `mod game` bleibt ratatui-frei ohne Disziplin, Grid als eigenes `impl Widget` ueber `buf[(x,y)].set_symbol(…)`, `Vec<Screen>` mit Overlay-Flag fuer Rendern *und* Auto-Schliessen, `Response` statt Stack-Mutation (kein Borrow-Aerger). **Der Windows-Fallstrick ist jetzt eine Zahl**: `Tasten 14 → Press 7 → Zuege 7` — ohne Filter kostete jeder Schritt zwei Zuege und damit doppelte Spielzeit. Zwei Korrekturen: 11 sagte „alle Commands kosten einen Zug", richtig ist **alle ausgefuehrten** (`Outcome { TurnPassed, Rejected(&str) }`, Grund reist mit zurueck); und `Command` muss `Clone` sein, damit `Confirm` es herausgeben kann. Ungetestet blieb `Registries` neben `Game` — der Durchstich laedt keine.
|
||||
- [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.
|
||||
@@ -74,7 +75,7 @@ auch über Ticket 13.
|
||||
## Not yet specified
|
||||
|
||||
- **Balancing** — konkrete Zahlen für Attribute, Schaden, Preise und Erfahrung über fünf Etagen. Wartet auf Kampfformeln (14), Progression (15) und Loot (16); vorher gibt es nichts zu balancieren.
|
||||
- **Boss-Design** — ob Bosse nur Gegner mit besseren Werten sind oder eigene Mechanik brauchen (Phasen, Skripte). Berührt Stufe (c) des Mod-Supports.
|
||||
- **Boss-Design** — ein Boss ist derzeit ein Wesen mit `ai`-Liste (14) in einem Prefab mit `core:boss`-Tag (10). Was fehlt, sind **Phasen**: „ab 50 % Leben ein anderes Verhalten" wäre mit der `ai`-Bedingungsliste sogar ausdrückbar, „ab 50 % ändern sich die Werte" nicht. Berührt Stufe (c) des Mod-Supports.
|
||||
- **NPC- und Handelsmodell** — Warenbestand, Dialogdaten. Wartet auf das Event-System (08). Die Preisformel steht bereits in 07; weil 07 Backtracking ausgeschlossen hat, muss ein Händler auf **jeder** Etage erreichbar sein.
|
||||
- **Inventar-UX** — Gewicht oder Kapazität (`carry_capacity` ist seit 01 ein Attribut ohne Regel), Sortierung, Vergleichsansicht. 11 hat nur den Screen und die Commands festgelegt, nicht was darin steht.
|
||||
- **Mod-Verteilung** — durch 03 auf „Ordner ins Profil entpacken" geschrumpft, ohne Versionsprüfung. Offen ist nur noch, ob 1.0 mehr braucht.
|
||||
|
||||
28
CONTEXT.md
28
CONTEXT.md
@@ -165,6 +165,28 @@ keine Zeit.
|
||||
Es gibt keine getrennte Kampfrunden-Uhr: Befristete [Modifikatoren](#modifikator) ticken auch beim
|
||||
Erkunden, damit „ich warte den Debuff draußen ab" eine Entscheidung mit Kosten bleibt.
|
||||
|
||||
## Prefab
|
||||
|
||||
Ein von Hand gezeichneter Raum als ASCII-Block, moddbarer namespaced Inhalt. Wände, Boden und Türen
|
||||
stehen als Zeichen darin, dazu Plätze für Gegner, Beute, Treppe, Eingang und NPCs.
|
||||
|
||||
Ein Gegner-Platz sagt „hier steht ein Gegner", **nicht welcher** — das entscheidet die [Etage](#etage).
|
||||
Nur so lässt sich derselbe Raum auf Etage 1 und Etage 5 verwenden. Bei NPCs ist es umgekehrt: Die nennt
|
||||
das Prefab selbst, denn ein Schmied ist auf jeder Etage derselbe Schmied.
|
||||
|
||||
Türen sind ausdrücklich markiert, damit der Generator keine Löcher in eine gezeichnete Wand stanzt.
|
||||
Prefabs dürfen gedreht und gespiegelt werden.
|
||||
|
||||
## Etage
|
||||
|
||||
Eine Ebene des Dungeons und zugleich ihr Bauplan: Fläche, Raumzahl, Pflichträume, gewichtete
|
||||
[Prefab](#prefab)- und [Wesen](#wesen)-Auswahl, Beutetabelle. Ausgewählt wird über [Tags](#tag), nie
|
||||
über IDs — sonst könnte ein Mod keinen eigenen Eingangsraum beisteuern.
|
||||
|
||||
Es existiert immer nur **eine** Etage; beim Abstieg wird die vorige verworfen, es gibt kein
|
||||
Zurückgehen. Gibt es keine Etage mit der nächsten Nummer, ist der Lauf **gewonnen** — die Zahl der
|
||||
Etagen steht nirgends im Code.
|
||||
|
||||
## Initiative
|
||||
|
||||
Ein Zahlenwert am [Wesen](#wesen), einmal beim Betreten eines Kampfes aus `dexterity` plus Wurf
|
||||
@@ -176,8 +198,8 @@ Sammlung von Verweisen, die beim Tod eines Gegners ungültig würde.
|
||||
|
||||
## Registry
|
||||
|
||||
Eine benannte Sammlung moddbarer Definitionen, geschlüsselt nach namespaced ID. Es gibt **acht**, und
|
||||
das hier ist die kanonische Liste — sie ist über vier Tickets gewachsen und stand sonst nirgends
|
||||
Eine benannte Sammlung moddbarer Definitionen, geschlüsselt nach namespaced ID. Es gibt **zehn**, und
|
||||
das hier ist die kanonische Liste — sie ist über fünf Tickets gewachsen und stand sonst nirgends
|
||||
vollständig:
|
||||
|
||||
| Registry | TOML-Tabelle |
|
||||
@@ -190,6 +212,8 @@ vollständig:
|
||||
| [Affixe](#affix) | `[[affixes]]` |
|
||||
| [Quests](#quest) | `[[quests]]` |
|
||||
| [Wesen](#wesen) | `[[creatures]]` |
|
||||
| [Prefabs](#prefab) | `[[prefabs]]` |
|
||||
| [Etagen](#etage) | `[[floors]]` |
|
||||
|
||||
Jede dieser Tabellen darf in jeder beliebigen `*.toml` eines [Mods](#mod) stehen.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user