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.
This commit is contained in:
@@ -11,10 +11,15 @@ use crate::game::{Command, CreatureRef, Direction, Game};
|
||||
pub enum Screen {
|
||||
MainMenu,
|
||||
Explore,
|
||||
/// Menue-Kampf (Charter): Faehigkeitsliste, Zielwahl mit den Zifferntasten.
|
||||
/// Menue-Kampf (Charter): Faehigkeitsliste links, Gegnerliste rechts.
|
||||
Combat {
|
||||
/// Cursor in der Faehigkeitsliste.
|
||||
pick: usize,
|
||||
/// Cursor in der Gegnerliste.
|
||||
target: usize,
|
||||
/// `true` = die Gegnerliste ist aktiv. Ein `bool` und Tab statt zweier
|
||||
/// gleichzeitig sichtbarer Cursor — dieselbe Bedienung wie im Laden.
|
||||
targeting: bool,
|
||||
},
|
||||
/// Zwei Bereiche: Inventar mit Cursor, Ausrüstung als Registry-Schleife.
|
||||
Inventory {
|
||||
@@ -98,37 +103,54 @@ impl Screen {
|
||||
_ => Response::None,
|
||||
},
|
||||
|
||||
Screen::Combat { pick } => {
|
||||
Screen::Combat { pick, target, targeting } => {
|
||||
let abilities = game.abilities_of(CreatureRef::Player, reg);
|
||||
if abilities.is_empty() {
|
||||
return Response::None;
|
||||
}
|
||||
*pick = (*pick).min(abilities.len() - 1);
|
||||
*target = (*target).min(game.enemies.len().saturating_sub(1));
|
||||
|
||||
// Der aktive Cursor und seine Laenge — sonst stuende dieselbe
|
||||
// Klemmung viermal da.
|
||||
let (cursor, len) = if *targeting {
|
||||
(&mut *target, game.enemies.len())
|
||||
} else {
|
||||
(&mut *pick, abilities.len())
|
||||
};
|
||||
|
||||
match key {
|
||||
KeyCode::Tab | KeyCode::BackTab => {
|
||||
*targeting = !*targeting;
|
||||
Response::None
|
||||
}
|
||||
KeyCode::Up | KeyCode::Char('k') => {
|
||||
*pick = pick.saturating_sub(1);
|
||||
*cursor = cursor.saturating_sub(1);
|
||||
Response::None
|
||||
}
|
||||
KeyCode::Down | KeyCode::Char('j') => {
|
||||
*pick = (*pick + 1).min(abilities.len() - 1);
|
||||
*cursor = (*cursor + 1).min(len.saturating_sub(1));
|
||||
Response::None
|
||||
}
|
||||
// Ziel ueber die Zifferntasten; ohne Gegner (Flucht,
|
||||
// Selbstheilung) zaehlt der Held selbst als Ziel.
|
||||
KeyCode::Enter | KeyCode::Char('1'..='9') => {
|
||||
let index = match key {
|
||||
KeyCode::Char(c @ '1'..='9') => (c as u8 - b'1') as usize,
|
||||
_ => 0,
|
||||
};
|
||||
let target = if game.enemies.is_empty() {
|
||||
CreatureRef::Player
|
||||
} else {
|
||||
CreatureRef::Enemy(index.min(game.enemies.len() - 1))
|
||||
// Die Ziffern springen aufs Ziel, sie feuern nicht — sonst
|
||||
// haette dieselbe Taste je nach Seite zwei Bedeutungen.
|
||||
KeyCode::Char(c @ '1'..='9') => {
|
||||
let i = (c as u8 - b'1') as usize;
|
||||
if i < game.enemies.len() {
|
||||
*target = i;
|
||||
}
|
||||
Response::None
|
||||
}
|
||||
// Ohne Gegner (Flucht, Selbstheilung) zaehlt der Held
|
||||
// selbst als Ziel.
|
||||
KeyCode::Enter => {
|
||||
let ziel = match game.enemies.is_empty() {
|
||||
true => CreatureRef::Player,
|
||||
false => CreatureRef::Enemy(*target),
|
||||
};
|
||||
Response::Command(Command::UseAbility {
|
||||
ability: abilities[*pick].clone(),
|
||||
target,
|
||||
target: ziel,
|
||||
})
|
||||
}
|
||||
KeyCode::Char('.') => Response::Command(Command::Wait),
|
||||
|
||||
Reference in New Issue
Block a user