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.
This commit is contained in:
2026-08-12 21:03:07 +02:00
parent 26d9533a93
commit b072e5ade3
3 changed files with 30 additions and 5 deletions

View File

@@ -892,10 +892,13 @@ impl Game {
let Some(def) = reg.items.get(&inst.def) else { return }; let Some(def) = reg.items.get(&inst.def) else { return };
let name = self.item_label(&inst, reg); let name = self.item_label(&inst, reg);
let target = if self.enemies.is_empty() { // TODO(M5): Ein Gegenstand trifft immer den ersten Gegner. Seit der
CreatureRef::Player // Kampf-Screen einen Zielcursor hat, ist das eine Luecke —
} else { // sie faellt erst auf, wenn `core` den ersten Wurfgegenstand
CreatureRef::Enemy(0) // bekommt. Dann traegt `UseItem` ein Ziel wie `UseAbility`.
let target = match self.enemies.iter().position(|e| e.is_alive()) {
Some(i) => CreatureRef::Enemy(i),
None => CreatureRef::Player,
}; };
for action in &def.actions { for action in &def.actions {
self.apply(action, CreatureRef::Player, target, reg); self.apply(action, CreatureRef::Player, target, reg);
@@ -1749,4 +1752,23 @@ mod tests {
assert!(game.enemies.is_empty()); assert!(game.enemies.is_empty());
assert!(game.journal.iter().any(|z| z.contains("geräumt")), "{:?}", game.journal); assert!(game.journal.iter().any(|z| z.contains("geräumt")), "{:?}", game.journal);
} }
/// Im Kampf trinken zu koennen ist eine Entscheidung — Zug gegen Leben.
/// Der Spielkern hat es immer erlaubt, nur der Kampf-Screen hatte keine
/// Taste dafuer; der Test haelt fest, dass die Erlaubnis echt ist.
#[test]
fn ein_trank_im_kampf_kostet_einen_zug() {
let (mut game, reg) = zwergenkrieger();
let ratte = reg.creatures.get("core:rat").expect("Riesenratte");
game.enemies = vec![Creature::from_def(ratte)];
game.give(ItemInstance::stack("core:health_potion", 2), &reg);
let platz = game.player.inventory.len() - 1;
game.player.health = 10.0;
let vorher = game.turn;
assert!(matches!(game.execute(Command::UseItem(platz), &reg), Outcome::TurnPassed));
assert!(game.player.health > 10.0, "der Trank wirkt auch im Kampf");
assert_eq!(game.turn, vorher + 1, "und er kostet den Zug");
assert_eq!(game.count_of("core:health_potion"), 1);
}
} }

View File

@@ -483,7 +483,7 @@ fn draw_combat(
zeilen.push(Line::from(namen.join(", ")).style(Style::new().fg(Color::Green))); zeilen.push(Line::from(namen.join(", ")).style(Style::new().fg(Color::Green)));
} }
zeilen.push( zeilen.push(
Line::from("Tab Seite · ↑↓ wählen · 1-9 Ziel · Enter angreifen · . warten · c Bogen") Line::from("Tab Seite · ↑↓ wählen · Enter angreifen · i Inventar · . warten · c Bogen")
.style(Style::new().fg(Color::DarkGray)), .style(Style::new().fg(Color::DarkGray)),
); );

View File

@@ -162,6 +162,9 @@ impl Screen {
} }
KeyCode::Char('.') => Response::Command(Command::Wait), KeyCode::Char('.') => Response::Command(Command::Wait),
KeyCode::Char('c') => Response::Push(Screen::CharacterSheet), KeyCode::Char('c') => Response::Push(Screen::CharacterSheet),
// Derselbe Screen wie draussen. `rejects` verbietet
// `UseItem` im Kampf nicht — es fehlte nur die Tuer.
KeyCode::Char('i') => Response::Push(Screen::Inventory { pick: 0 }),
KeyCode::F(12) => Response::Push(Screen::Debug { pick: 0 }), KeyCode::F(12) => Response::Push(Screen::Debug { pick: 0 }),
_ => Response::None, _ => Response::None,
} }