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".
This commit is contained in:
2026-08-12 20:58:35 +02:00
parent 036f60d463
commit 26d9533a93
6 changed files with 175 additions and 2 deletions

View File

@@ -3,7 +3,7 @@
use ratatui::crossterm::event::KeyCode;
use crate::data::Registries;
use crate::game::{Command, CreatureRef, Direction, Game};
use crate::game::{Cheat, Command, CreatureRef, Direction, Game};
/// Popups sind gewoehnliche Screens, die sich beim Rendern als Overlay
/// ausweisen — ein gemeinsamer `Vec<Screen>` statt `current` plus
@@ -30,6 +30,12 @@ pub enum Screen {
/// Meldungszeile im Hauptmenue: Ein Abstiegs-Roguelike lebt davon, dass der
/// Lauf am Ende *erzaehlt* wird (Ticket 14).
GameOver,
/// Werkbank. Immer mit gebaut statt hinter `cfg(debug_assertions)`: Das
/// waere ein `#[cfg]` an der Variante *und* an jedem `match` darueber, fuer
/// ein Spiel, das niemand ausliefert. Ein `#[cfg]` reicht spaeter nach.
Debug {
pick: usize,
},
/// **Flache Optionsliste**, kein Knotengraph (M4): Was erst nach einer
/// Antwort erscheint, ist eine Option mit einer Bedingung.
Dialog {
@@ -96,6 +102,7 @@ impl Screen {
KeyCode::Char('>') => Response::Command(Command::Descend),
KeyCode::Char('g') => Response::Command(Command::PickUp),
KeyCode::Char('t') => Response::Command(Command::Talk),
KeyCode::F(12) => Response::Push(Screen::Debug { pick: 0 }),
// Screen-Navigation ist reine UI und fasst `Game` nie an.
KeyCode::Char('c') => Response::Push(Screen::CharacterSheet),
KeyCode::Char('i') => Response::Push(Screen::Inventory { pick: 0 }),
@@ -155,6 +162,7 @@ impl Screen {
}
KeyCode::Char('.') => Response::Command(Command::Wait),
KeyCode::Char('c') => Response::Push(Screen::CharacterSheet),
KeyCode::F(12) => Response::Push(Screen::Debug { pick: 0 }),
_ => Response::None,
}
}
@@ -259,6 +267,32 @@ impl Screen {
}
}
Screen::Debug { pick } => {
let items = reg.items_ordered();
if !items.is_empty() {
*pick = (*pick).min(items.len() - 1);
}
match key {
KeyCode::Up | KeyCode::Char('k') => {
*pick = pick.saturating_sub(1);
Response::None
}
KeyCode::Down | KeyCode::Char('j') => {
*pick = (*pick + 1).min(items.len().saturating_sub(1));
Response::None
}
KeyCode::Enter if !items.is_empty() => {
Response::Command(Command::Debug(Cheat::Give(items[*pick].id.clone())))
}
KeyCode::Char('h') => Response::Command(Command::Debug(Cheat::Heal)),
KeyCode::Char('>') => Response::Command(Command::Debug(Cheat::Descend)),
KeyCode::Char('x') => Response::Command(Command::Debug(Cheat::ClearRoom)),
KeyCode::Esc | KeyCode::F(12) | KeyCode::Char('q') => Response::Pop,
_ => Response::None,
}
}
Screen::GameOver => Response::Quit,
}
}