Commit Graph

44 Commits

Author SHA1 Message Date
a8fea8b526 fix(balance): Startausruestung wird angelegt, Krit wirkt
Gemessen statt geschaetzt — es waren nicht die Zahlen:

- on_create konnte nur give_item, die Startwaffe lag also im Beutel.
  Jeder Held schlug mit blossen Haenden zu: Schaden 2 statt 7,7, zehn
  Zuege pro Ratte statt zweieinhalb. Neue Aktion equip_item.
- Der Dieb bekam gar keine Waffe. Jetzt ein Dolch (+3 Schaden, +5 Krit).
- critical_chance percent = 15 auf einer Basis von 0 ist ein No-op — die
  Signaturzahl des Diebs tat nichts. Basis jetzt 5, Dieb traegt flat 10.

Die Prozent-auf-Null-Falle steht im Glossar unter Modifikator: Sie folgt
aus der Stapelregel aus Ticket 01 und schnappt sonst wieder zu.
2026-08-13 08:24:06 +02:00
44cef14960 Merge branch 'feat/m8-charakterbau' 2026-08-13 08:07:40 +02:00
69efa72d68 feat(charakterbau): Rasse und Klasse werden gewaehlt
Der Held war seit M1 als Zwergenkrieger verdrahtet, waehrend zwei Rassen
und zwei Klassen ungenutzt in der Registry lagen. Der Charakterbau ist
eine Schleife ueber categories_ordered() und options_of() — ein Mod, der
"Talent" mit pick = 2 ergaenzt, bekommt eine dritte Spalte ohne Code.

- CharacterOption bekommt abilities und on_create (Ticket 02, TODO seit M3)
- abilities_of liest jetzt Rasse und Klasse: Krieger traegt Wuchtschlag,
  Dieb Hinterhalt, Zwerg Steinsinn
- on_create gibt die Startausruestung als gewoehnliche Aktionen —
  starting_hero() schrumpft auf Creature::new(name)
- MainMenu trennt "neuer Held" von "Lauf fortsetzen"

Befund: Das Feld abilities fehlte, obwohl Ticket 15 sein ganzes Argument
darauf stuetzt — eine Klasse konnte nur flache Modifikatoren geben.
2026-08-13 08:03:19 +02:00
b721947a65 Merge branch 'feat/m7-quests' 2026-08-13 07:49:39 +02:00
37b5820c5b feat(quests): Quests als Trigger mit Zaehler
Eine Quest IST ein Trigger mit count = N — kein zweiter Mechanismus.
Gezaehlt wird erst ab start_quest, erledigte bleiben mit done = true
stehen, und die Belohnung darf die naechste starten: das ist die Kette.

- fuenfzehnte Registry, Trigger per serde(flatten) eingebettet
- give_item als Aktion, damit eine Belohnung mehr als Erfahrung zahlt
- QuestLog auf q, Fortschritt im Speicherstand, unbekannte IDs verworfen
- eine Funktion entscheidet fuer alle Traeger, wann ein Trigger feuert

Befund: Die Ordnungszahlen im Aktionskatalog widersprechen sich — vier
Tickets zaehlten je vom eigenen Stand. Sie sind raus, CONTEXT.md fuehrt
die Liste.
2026-08-13 07:43:46 +02:00
090d9ae136 Merge branch 'feat/m6-speicherstand' 2026-08-12 22:01:57 +02:00
d09fef7069 feat(save): Speicherstand als Game-Baum
Autosave beim Betreten einer Etage und beim Beenden, ein Slot, bei Tod
geloescht. Der Save ist der Baum selbst — keine eigenen Save-Structs, die
man zweimal pflegt.

- Kacheln als ASCII-Zeilen wie ein Prefab, nicht 3200 Enum-Eintraege
- atomar geschrieben: tmp plus rename, der einzige Stand ueberlebt einen
  Abbruch
- fremdes Schema wird abgelehnt, die Datei bleibt liegen
- fehlende Mods verwerfen Eintraege, nie den Lauf
- Confirm-Screen vor dem Spiel, wenn der Save Eintraege verloren hat

Fuenf Befunde: die Ereigniswarteschlange gehoert nicht in den Save, die
Feldreihenfolge ist Teil des TOML-Formats, Serialize fuer Floor muss von
Hand, Confirm braucht on_no, und die Rueckfrage galt zuerst auch fuer
Mod-Ladewarnungen.
2026-08-12 22:01:12 +02:00
7fdd8bbb00 Merge branch 'feat/m5-progression' 2026-08-12 21:47:50 +02:00
6f91b44e50 fix(bogen): vorgemerkte Punkte beim Abschicken verbrauchen
Nach Enter stand unspent_points auf 0, waehrend der Screen den Punkt noch
vorgemerkt hielt — die Anzeige rechnete 0 - 1 auf u32 und stuerzte ab.

Der Screen leert sein Feld, waehrend er das Kommando uebergibt, dazu
saturating_sub an den zwei Lesestellen: unspent_points kann sich auch
unter dem offenen Screen aendern.

Erster UI-Test des Projekts. handle_key ist ohne Terminal aufrufbar, und
sobald ein Screen Zustand haelt, ist die Naht zum Spielzustand eine
Fehlerquelle wie jede andere.
2026-08-12 21:20:02 +02:00
b19492bb51 feat(progression): Erfahrung, Aufstieg und Punkteverteilung
Toeten gibt Erfahrung (exp_reward stand seit M3 ungelesen an jedem
Gegner), give_exp als zwoelfte Aktion fuer spaetere Quests. Aufstieg in
einer Schleife am Zugende, ein Punkt je Stufe, keine Obergrenze.

Verteilt wird im Charakterbogen — kein zwoelfter Screen. AssignPoints
nimmt alle Punkte auf einmal und kostet damit einen Zug.

luck wirkt jetzt: eine Chance auf einen Zusatzzug auf jeder Beutetabelle.

Drei Befunde:
- strength und constitution las niemand, zwei der fuenf verteilbaren
  Grundwerte waren Fallen. Zwei balance-Knoepfe rechnen sie beim
  Verteilen um.
- Rng::chance(0.0) verbrauchte eine Zahl und verschob den Zufallsstrom.
- Cheat::ClearRoom ging am Todesweg vorbei.
2026-08-12 21:15:25 +02:00
b072e5ade3 feat(kampf): Inventar im Kampf erreichbar
'i' oeffnet denselben Screen wie draussen. `rejects` hat `UseItem` im
Kampf nie verboten — es fehlte nur die Tuer in der UI.

Dabei: Ein Gegenstand traf `Enemy(0)`, auch wenn der schon tot war. Jetzt
der erste lebende. Dass er den Zielcursor ignoriert, steht als TODO — es
faellt erst mit dem ersten Wurfgegenstand auf.
2026-08-12 21:03:07 +02:00
26d9533a93 feat(debug): Werkbank auf F12
Gegenstandsliste mit Cursor, dazu heilen, absteigen und Raum leeren.
Laeuft ueber ein Kommando Debug(Cheat) statt am Spielkern vorbei — die
Zusage aus Ticket 11, dass Response::Command der einzige Weg nach game/
ist, gilt auch fuer Cheats.

ClearRoom toetet die Gegner, statt die Liste zu leeren: So laeuft der
normale Weg samt Todes-Triggern und "Der Raum ist geraeumt".
2026-08-12 20:58:35 +02:00
036f60d463 fix(inventar): ein Stapel wird stueckweise verbraucht
use_item und equip entfernten den ganzen Eintrag — ein Schluck aus zwei
Heiltraenken verbrauchte beide. `count` kam in M4 dazu, die drei aelteren
Verbraucher wurden nicht mitgezogen.

Ein Griff fuer alle: take_at(index) nimmt ein Stueck und raeumt den
Eintrag ab, wenn er leer ist. unequip laeuft jetzt durch give, damit ein
abgelegter Stapel wieder verschmilzt.

Dabei aufgefallen: Der Heiltrank hatte nie `actions` — seit M1
verbrauchbar, ohne zu wirken.
2026-08-12 20:54:07 +02:00
6014471c33 feat(kampf): verbindliche Gegner-Ansage und Zielcursor
Am Zugende legt jeder Gegner fest, was er im naechsten Zug tut; der
Kampf-Screen zeigt es unter dem Lebensbalken. Verbindlich statt Vorschau,
denn nur so entsteht ein Fenster: Wer dem Gegner zwischen Ansage und
Ausfuehrung die Ausdauer nimmt, kostet ihn den Zug.

Zielwahl bekommt einen eigenen Cursor, Tab schaltet zwischen Gegner- und
Faehigkeitsliste um — dieselbe Bedienung wie im Laden. Die Ziffern
springen nur noch aufs Ziel, ausgeloest wird mit Enter.
2026-08-12 20:49:24 +02:00
7963df5236 fix(kampf): geglueckte Flucht bricht die Initiativreihenfolge ab
Die Reihenfolge steht vor dem Zug fest, Flucht leert `enemies` aber
mitten darin — danach zeigten die Indizes ins Leere und ein Gegner, der
nach dem Helden drankam, stuerzte das Spiel ab.

Zwei Zusagen aus Ticket 14, jede fuer sich richtig, nie zusammen geprueft:
alle Kampftests loesten die Flucht am Ende der Reihenfolge aus.
2026-08-12 20:39:54 +02:00
3a6af6affb Merge branch 'feat/m4-handel' 2026-08-12 20:36:23 +02:00
7ed15afaf9 feat(handel): NPC-Registry, Dialog, Laden und Stapelregel
Vierzehnte Registry: NPCs sind keine Wesen — kein Kampfzustand, keine
Initiative, keine Attribute. Ihre Antwortliste ist flach; was erst nach
einer Antwort erscheint, ist eine Option mit einer Bedingung.

- open_shop als elfte Aktion (game/ setzt eine Absicht, die UI schiebt)
- Talk/Choose/Buy/Sell, Talk wirkt auf das eigene Feld wie PickUp
- Preisspanne statt Goldkonto beim Haendler, Charisma in der Preisfunktion
- Warenkorb einmal bei der Etagenerzeugung gezogen
- Stapelregel aus Ticket 07 endlich gebaut: 50-120 Gold war 120 Eintraege
- Dialog- und Shop-Screen, NPC-Symbol im Grid
2026-08-12 20:31:35 +02:00
197f62ce33 feat: m4 teil 1 — beute, inventar und affixe
- data/: [[loot_tables]] und [[affixes]] als Registries; LootEntry mit
  guaranteed, nothing, table-in-table, count und affixes
- game/loot.rs: EINE Ziehmaschinerie fuer Gegenstaende und Affixe,
  Tiefendeckel gegen wechselseitigen Bezug
- PickUp zieht erst beim Aufheben (Ticket 16) — moeglich nur, weil alle
  Haufen dasselbe Symbol zeigen
- UseItem/Equip/Unequip/Drop, Inventar-Screen als Registry-Schleife
- Affixe im Attributpfad, in der Resistenz und in der Trigger-Suche;
  item_label stellt Affixnamen voran, item_price multipliziert Faktoren
- Room haelt eine loot_table (nicht je Haufen eine), Prefab-loot
  ueberschreibt die Etagentabelle

53 Tests. Offen aus M4: Handel.
2026-08-12 17:56:28 +02:00
cdcabcce21 feat: when-filter am trigger schliesst die luecke aus m3
condition fragt Wesen, when filtert das Ereignis. Getrennt, weil dieselbe
Bedingungsgrammatik auch beim Auslesen eines Attributs laeuft — dort gibt es
kein Event, und eine Nutzlast-Pruefung in condition waere im Trigger sinnvoll
und am Modifikator immer falsch, ohne dass der Modder es sieht.

Nutzlast zurueck: damage_type und prefab. critical und amount bleiben draussen,
bis jemand sie fordert.

Dazu Item.triggers/actions/abilities und Trigger-Suche in der Ausruestung —
mods/core/items.toml belegt es mit einem Dornenpanzer, der NUR Feuer
reflektiert.

45 Tests.
2026-08-12 17:44:26 +02:00
b25dd1c49a docs: m3-befunde in tickets 01, 08 und 14 zurueckgetragen, dreizehnte registry ins glossar 2026-08-12 17:08:27 +02:00
217a7acbbc feat: m3 kampf, events und trigger
Der Dungeon hat jetzt Gegner, und man kann sterben.

- game/action.rs: sieben Schadenstypen, fuenf der zehn Aktionen, Target
- game/condition.rs: Bedingungsgrammatik (Liste = UND, not je Eintrag)
- game/event.rs: zwoelf Events, Trigger als vierte Wirkungsform
- game/combat.rs: Treffer- und Schadensformel mit drei Klemmbereichen
- Game::apply, drain_events (FIFO), Initiative, Rundenablauf aus Ticket 14
- data/: abilities-, creatures- und modifiers-Registry, [balance] feldweise
- ui/: Combat-Screen mit Faehigkeitsmenue und Sperrgrund

Drei Befunde:
- Die Trefferformel war toter Code — jeder Angriff traf. Nur der Compiler
  hat es gemeldet ('hit_chance is never used'). Test haelt sie am Leben.
- Die Warteschlange war ein Stack (pop statt pop_front), obwohl Ticket 08
  ausdruecklich 'haengt es hinten an' sagt. Jetzt VecDeque.
- Benannte Modifikatoren (core:poisoned) hatten keine Registry — Ticket 01
  laesst apply_modifier per ID referenzieren, aber keine der zwoelf
  definierte sie. Das ist die dreizehnte.

43 Tests.
2026-08-12 17:03:00 +02:00
8bcd53efa2 docs: m2-befunde in tickets 10 und 11 zurueckgetragen, verification erweitert 2026-08-12 16:19:16 +02:00
f998e55210 feat: m2 etagengenerierung — prefabs, floors, korridore, abstieg und sieg
Der handgebaute Testraum ist weg; Etagen entstehen aus Daten.

- game/rng.rs: xorshift64*, Seed im Game (Ticket 14)
- game/generate.rs: freie Platzierung, Pflichtraeume zuerst, Kette
  i->i-1 fuer strukturelle Erreichbarkeit, acht Orientierungen
- data/: [[prefabs]] und [[floors]] als neunte und zehnte Registry,
  Ladepruefungen fuer rechteckige Layouts und Pflichtraum-Flaeche
- Descend plus Siegbedingung: keine [[floors]] mit der naechsten Nummer
  heisst gewonnen — die Zahl 5 steht nirgends im Code
- GameOver-Screen, mitlaufender Bildausschnitt im Grid

Befund: Korridore stanzten 2340 Loecher in gezeichnete Waende, weil sie
AUF der Tuer starteten. Erst Schritt nach aussen, dann L-Form, sonst
Wegsuche ueber den freien Raum — die kann eine Raumwand nicht beruehren.
Der Test zaehlt jetzt null.

21 Tests.
2026-08-12 16:14:23 +02:00
3714bad313 docs: m1-befunde in tickets 03, 04 und 11 zurueckgetragen, verification-abschnitt in der map 2026-08-12 15:42:33 +02:00
b39491916f feat: m1 vertikaler durchstich — mod-lader, registries, attributberechnung, screen-stack
Ersetzt src/ vollstaendig: print_box, include_str! und enum ItemType fallen,
wie Ticket 01-03 es vorgezeichnet haben.

- data/: Mod-Lader mit zwei Suchpfaden, selbstheilender load_order.toml,
  drei Phasen und zweistufiger Deserialisierung (03/04)
- game/: Attributkatalog, Creature, Game::attribute ueber mehrere Quellen,
  Command mit Outcome (01/07/11/12)
- ui/: Screen-Stack mit Response, Grid-Widget, Press-Filter (06/11/12)
- mods/core/: echte TOML-Dateien auf der Platte statt include_str!

10 Tests, darunter der Borrow-Beweis fuer Registries-neben-Game, den
Ticket 12 nicht fuehren konnte.
2026-08-12 15:39:17 +02:00
9c5efab331 docs: ticket 16 (loot-tabellen) aufloesen, nachtraege an 10 und 14, loot-tabelle ins glossar, destination erreicht 2026-08-12 14:18:44 +02:00
5faa5d6165 docs: ticket 15 (progression) aufloesen, nachtraege an 01 07 11 14, erfahrung ins glossar 2026-08-12 13:45:41 +02:00
380400611e docs: ticket 13 (script-engine) aufloesen, rhai gewaehlt, stufe c hinter 1.0, nachtrag an 01 2026-08-12 13:00:50 +02:00
bed9ef5762 docs: ticket 09 (save-schema) aufloesen, nachtraege an 02 und 11, speicherstand ins glossar 2026-08-12 12:34:29 +02:00
6442e104ab docs: ticket 10 (raum-prefab-format) aufloesen, nachtraege an 08 und 14, prefab und etage ins glossar 2026-08-12 12:11:42 +02:00
d51ebb26a1 docs: ticket 14 (kampfformeln) aufloesen, nachtraege an 01 07 08 11, registry-liste und initiative ins glossar 2026-08-12 11:51:25 +02:00
88195d3c9a docs: ticket 12 (durchstich) aufloesen, korrekturen an 11 und 06, zug im glossar geschaerft 2026-08-12 11:27:19 +02:00
2c19cb2aaf docs: ticket 11 (ui-geruest) aufloesen, nachtrag an 01, command ins glossar 2026-08-12 10:54:09 +02:00
d5a6feb091 docs: ticket 08 (event-trigger-system) aufloesen, nachtraege an 01 und 07, trigger bedingung und quest ins glossar 2026-08-12 10:34:12 +02:00
f25f393880 docs: ticket 07 (game-state-baum) aufloesen, wesen und affix ins glossar, vorgaben an tickets 08 09 10 14 16 2026-08-12 10:08:57 +02:00
145b82bc08 docs: ticket 04 (fehlerstrategie) aufloesen, vorgaben an ticket 11 2026-08-12 09:45:55 +02:00
d2d5ae6d8c docs: ticket 03 (mod-ladepipeline) aufloesen, mod ins glossar, vorgaben an tickets 04 und 10 2026-08-12 09:21:45 +02:00
5c4543da1a docs: ticket 02 (typ-registry) aufloesen, faehigkeit ins glossar, ticket 16 graduieren 2026-08-11 16:28:04 +02:00
4e86a37d2f docs: ticket 01 (effekt-vokabular) aufloesen, glossar anlegen, tickets 14 und 15 graduieren 2026-08-11 16:08:00 +02:00
c8cde87db1 docs: ticket 05 (script-engine-research) aufloesen, ticket 13 graduieren 2026-08-11 15:45:38 +02:00
9f9279d926 docs: ticket 06 (ratatui-research) aufloesen, tickets 11 und 12 graduieren 2026-08-11 15:38:22 +02:00
8ad5da82c1 docs: wayfinder-map fuer rustkeep 1.0 anlegen 2026-08-11 15:13:49 +02:00
290d985bc6 refactor: getter-traits durch pub-felder ersetzen, print_box wiederverwenden 2026-08-11 14:39:59 +02:00
eacf0c1a01 feat: initial commit - item catalog, player and stats 2026-08-11 14:36:51 +02:00