Files
rustkeep/.scratch/rustkeep-1.0/map.md

11 KiB
Raw Blame History

Map: rustkeep 1.0

Gitea-Spiegel: Issue #1. Diese Datei ist die kanonische Quelle — bei Abweichung gilt .scratch/.

Gitea hat keine nativen Parent-Child- oder Blocking-Beziehungen, deshalb steht beides dort als Konvention im Body. Zuordnung Ticket → Gitea-Issue:

Ticket Gitea Ticket Gitea
01 Effekt-Vokabular #2 06 Research ratatui #7
02 Typ-Registry #3 07 Game-State-Baum #8
03 Mod-Ladepipeline #4 08 Event-System #9
04 Fehlerstrategie #5 09 Save-Schema #10
05 Research Script-Engine #6 10 Raum-Prefab-Format #11
11 UI-Gerüst #12 12 Prototyp ratatui #13
13 Script-Engine wählen #14 14 Kampfformeln #15
15 Progression #16 16 Loot-Tabellen #17

Destination

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, Inventar und Ausrüstung, Quests, NPCs, Handel, Speichern/Laden, Mod-Support auf drei Stufen.

Notes

Domain: Rust-Terminal-Roguelike, moddbar. Repo: MDietermann/rustkeep.

Das Glossar liegt in CONTEXT.md im Repo-Wurzelverzeichnis — kanonische Begriffe, keine Implementierung. Jede Session, die einen Begriff schärft, schreibt ihn dort fest.

Jede Session konsultiert: ponytail (dauerhaft aktiv), /grilling und /domain-modeling für Grilling-Tickets, /research für Research-Tickets, /prototype für Prototype-Tickets.

Feststehende Rahmenentscheidungen aus der Charter-Session:

Thema Entscheidung
Motivation Rust lernen und Spiel bauen — Lern-Entscheidungen bekommen eigene Tickets, wo sie echte Architekturfragen sind
Kernschleife Grid-Bewegung zum Erkunden, Kampf im Menü (Dungeon Master / Etrian Odyssey)
Held Solo, kein Party-System
Rendering ratatui + crossterm; print_box fliegt raus, sobald ratatui steht
Etagen Hybrid — Raum-Prefabs in TOML, prozedural zusammengesetzt
Daten Alles TOML, zur Laufzeit geladen. include_str! und enum ItemType fallen
Mod-IDs Namespaced (core:iron_sword), Präfix aus dem Manifest und automatisch vorangestellt. Ladereihenfolge aus einer Spielerliste, nicht aus dem Manifest (03). Das Kernspiel ist selbst ein Mod
Mod-Stufen Daten → Typen → Scripting, in dieser Reihenfolge. Scripting setzt auf der Typ-Registry auf
Effekte Ein generisches Effekt-System mit Wirkung auf Stats. Rasse, Klasse, Items, Statuseffekte, Quest-Belohnungen und später Scripts nutzen dasselbe Vokabular — dieses Vokabular ist die Mod-API
Quests Bedingung + Belohnung als Daten, generisches Event-/Trigger-System
Weltzustand Game-Ownership-Baum, Verweise über String-IDs (keine Indizes, 07). Registries liegt daneben, nicht darin. Kein ECS
Fehler Result statt panic! — kaputte Mod-TOMLs sind der Normalfall, nicht der Ausnahmefall
Save TOML, Mod-Liste im Header, fehlender Mod = Warnung und Verwerfen, kein harter Abbruch

Leitplanke aus Ticket 05: Scripts reden ausschließlich über das Effekt-Vokabular, nie direkt mit dem Game-State. Solange das gilt, ist die Script-Engine austauschbar — Ticket 01 entscheidet damit indirekt auch über Ticket 13.

Decisions so far

  • 01 — Effekt-Vokabular (#2) — Zwei Begriffe statt einem: Modifikator (rechnet ein Attribut um, solange er existiert) und Aktion (ändert Zustand einmalig), getrennte Maschinen und getrennte TOML-Arrays. Fester Attributkatalog ohne Namensraum; max_health ist Attribut, der Lebensstand ist Zustand. Sechs Aktionen, damage eigenständig wegen der Minderungskette. Stapeln (Basis + Σ flach) × (1 + Σ prozentual). Eine Uhr: der Zug. Sieben feste Schadenstypen, ein Minderungsmechanismus (resistance), armor nur Kurzschreibweise. Auren wohnen beim Träger. Bedingungsvokabular bewusst an Ticket 08 delegiert. Begriffe im Glossar.
  • 02 — Typ-Registry statt Enums (#3) — Es gibt keinen Item-Typ mehr: enum ItemType vermischte Slot, Verhalten und Kategorie und entfällt ersatzlos zugunsten der Felder slot, stackable, consumed_on_use. ItemWeapon und ItemArmor fallen weg, ein Gegenstand ist einheitlich. Fähigkeit kommt als dritter Begriff neben Modifikator und Aktion dazu (Angebot statt Wirkung). Fünf moddbare Registries; Slots und sogar Charakteroption-Kategorien sind Inhalt, mit pick = N. Startausrüstung ist on_create als gewöhnliche Aktionsliste. value → base_price, shop_price → price_modifier.
  • 03 — Mod-Ladepipeline und Manifest (#4) — Zwei Suchpfade: mods/ neben der Binary (enthält core/) und data_dir()/rustkeep/mods über die Crate directories; bei gleicher ID gewinnt das Profil, mit Warnzeile. core liegt als echter Mod-Ordner auf der Platte, include_str! entfällt. manifest.toml mit vier Feldern (id, name, version, depends) — TOML statt YAML, weil serde_yaml und der Fork serde_yml beide archiviert sind. Ladereihenfolge aus einer selbstheilenden Spielerliste load_order.toml; depends sortiert nicht, sondern prüft, und eine Verletzung ist harter Startfehler. Jede *.toml im Mod wird eingelesen, jede darf jede Registry-Tabelle enthalten. Präfix wird automatisch vorangestellt, Überschreiben über overrides = "core:iron_sword" am Eintrag — der landet nur unter der überschriebenen ID. Drei Phasen (Manifeste → Einlesen → Auflösen), dadurch keine Vorwärtsreferenzen; Fehlereinheit ist der Eintrag. Kein Nachladen zur Laufzeit.
  • 04 — Fehlerstrategie für kaputte Mods (#5) — Syntax ist ein Datei-Fehler, alles Semantische ein Eintrags-Fehler; damit 03s Versprechen hält, wird zweistufig deserialisiert (Vec<Spanned<toml::Value>>, dann Eintrag für Eintrag), sonst reißt ein Tippfehler die ganze items.toml mit. Genau drei harte Fehler: depends fehlend, depends verletzt, core nicht gefunden — stderr und exit(1). core ist sonst kein Sonderfall; abgesichert wird es durch einen #[test], der mods/core/ mit null Warnungen lädt. Warnungen sammeln sich in Vec<String> und landen in logs/load-<zeitstempel>.log (letzte zehn), plus eine Zeile auf dem Startbildschirm — kein Report-Screen. Meldung meinmod/items.toml:42: core:iron_sword — <message>, Mod-ID vorne, keine Spalte. Rust: handgeschriebenes enum LoadError mit impl Display statt thiserror/anyhow, load_mods() -> Result<(Registries, Vec<String>), LoadError>, Kette endet vor main mit eprintln! und exit(1). Panic-Grenze ist die Datenherkunft: TOML ist Result, eigener Code darf panicken.
  • 05 — Research: Script-Engine (#6) — Empfehlung Rhai wegen Fehlermeldungen mit Zeile und Spalte, billiger Typ-Brücke, AST-Hot-Reload und Build ohne cc. Gemessen: mlua liest unter StdLib::ALL_SAFE per io.open echte Dateien — „safe" heißt dort speichersicher, nicht fähigkeitsbeschränkt. Lua gewinnt bei der Modder-Vertrautheit; WASM scheidet für 1.0 aus. Kernfolge: reden Scripts nur über das Effekt-Vokabular, ist die Engine-Wahl umkehrbar. Entscheidung offen in Ticket 13.
  • 06 — Research: ratatui-App-Architektur (#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.
  • 07 — Game-State-Baum (#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.

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.
  • 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 — Slots, Gewicht oder Kapazität, Sortierung, Vergleichsansicht. Wartet jetzt auf das Screen-Modell (11), nicht mehr auf 06.
  • Mod-Verteilung — durch 03 auf „Ordner ins Profil entpacken" geschrumpft, ohne Versionsprüfung. Offen ist nur noch, ob 1.0 mehr braucht.

Out of scope

  • Story-Modul — kommt später als eigenes Modul, linear statt prozedural, ebenfalls moddbar. Eigene Map, wenn 1.0 steht.
  • Party-System — bewusst gegen Solo entschieden; ein Wechsel wäre ein Umbau der Kampfarchitektur, keine Erweiterung.
  • Multiplayer
  • Grafik jenseits des Terminals