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.
This commit is contained in:
@@ -741,7 +741,7 @@ impl Game {
|
||||
Command::PickUp => self.pick_up(reg),
|
||||
Command::UseItem(i) => self.use_item(*i, reg),
|
||||
Command::Equip(i) => self.equip(*i, reg),
|
||||
Command::Unequip(slot) => self.unequip(&slot.clone()),
|
||||
Command::Unequip(slot) => self.unequip(&slot.clone(), reg),
|
||||
Command::Talk => self.talk(reg),
|
||||
Command::Choose(i) => self.choose(*i, reg),
|
||||
Command::Buy(i) => self.buy(*i, reg),
|
||||
@@ -828,25 +828,33 @@ impl Game {
|
||||
fn sell(&mut self, index: usize, reg: &Registries) {
|
||||
// Ein Stapel wird stueckweise verkauft — sonst gaebe es keinen Weg,
|
||||
// fuenf von fuenfzig Traenken loszuwerden.
|
||||
let inst = ItemInstance { count: 1, ..self.player.inventory[index].clone() };
|
||||
let inst = self.take_at(index);
|
||||
let preis = self.sell_price(&inst, reg);
|
||||
let name = self.item_label(&inst, reg);
|
||||
|
||||
// Ueber den Index, nicht ueber `take`: Zwei Traenke mit verschiedenen
|
||||
// Affixen teilen sich die `def`, und `take` naehme den erstbesten.
|
||||
let slot = &mut self.player.inventory[index];
|
||||
slot.count -= 1;
|
||||
if slot.count == 0 {
|
||||
self.player.inventory.remove(index);
|
||||
}
|
||||
self.give(ItemInstance::stack("core:gold", preis), reg);
|
||||
// Der Haendler hat kein Gold-Konto: Die Spanne, nicht ein Vorrat,
|
||||
// begrenzt den Handel.
|
||||
self.log(format!("{name} verkauft fuer {preis} Gold"));
|
||||
}
|
||||
|
||||
/// Nimmt **ein Stueck** aus dem Eintrag an `index` und gibt es zurueck.
|
||||
/// Ueber den Index, nicht ueber die `def`: Zwei Traenke mit verschiedenen
|
||||
/// Affixen teilen sich die `def`, und `take` naehme den erstbesten.
|
||||
fn take_at(&mut self, index: usize) -> ItemInstance {
|
||||
let slot = &mut self.player.inventory[index];
|
||||
slot.count -= 1;
|
||||
let leer = slot.count == 0;
|
||||
let einzeln = ItemInstance { count: 1, ..slot.clone() };
|
||||
if leer {
|
||||
self.player.inventory.remove(index);
|
||||
}
|
||||
einzeln
|
||||
}
|
||||
|
||||
fn use_item(&mut self, index: usize, reg: &Registries) {
|
||||
let inst = self.player.inventory[index].clone();
|
||||
// Ein Stapel wird stueckweise benutzt — `count` wegzuwerfen hiesse,
|
||||
// mit einem Trank alle zu trinken.
|
||||
let inst = ItemInstance { count: 1, ..self.player.inventory[index].clone() };
|
||||
let Some(def) = reg.items.get(&inst.def) else { return };
|
||||
|
||||
let name = self.item_label(&inst, reg);
|
||||
@@ -860,29 +868,30 @@ impl Game {
|
||||
}
|
||||
|
||||
if def.consumed_on_use {
|
||||
self.player.inventory.remove(index);
|
||||
self.take_at(index);
|
||||
}
|
||||
self.log(format!("{name} benutzt"));
|
||||
self.events.push_back(Event::new(EventKind::ItemUsed, CreatureRef::Player));
|
||||
}
|
||||
|
||||
fn equip(&mut self, index: usize, reg: &Registries) {
|
||||
let inst = self.player.inventory[index].clone();
|
||||
let Some(slot) = reg.items.get(&inst.def).and_then(|d| d.slot.clone()) else { return };
|
||||
let def = self.player.inventory[index].def.clone();
|
||||
let Some(slot) = reg.items.get(&def).and_then(|d| d.slot.clone()) else { return };
|
||||
|
||||
let inst = self.take_at(index);
|
||||
// Was im Slot lag, wandert zurueck ins Inventar — nicht ins Nichts.
|
||||
if let Some(alt) = self.player.equipment.remove(&slot) {
|
||||
self.player.inventory.push(alt);
|
||||
self.give(alt, reg);
|
||||
}
|
||||
self.player.inventory.remove(index);
|
||||
let name = self.item_label(&inst, reg);
|
||||
self.player.equipment.insert(slot, inst);
|
||||
self.log(format!("{name} angelegt"));
|
||||
}
|
||||
|
||||
fn unequip(&mut self, slot: &str) {
|
||||
fn unequip(&mut self, slot: &str, reg: &Registries) {
|
||||
if let Some(inst) = self.player.equipment.remove(slot) {
|
||||
self.player.inventory.push(inst);
|
||||
// Ueber `give`, damit ein abgelegter Stapel wieder verschmilzt.
|
||||
self.give(inst, reg);
|
||||
self.log(String::from("Abgelegt"));
|
||||
}
|
||||
}
|
||||
@@ -1655,4 +1664,28 @@ mod tests {
|
||||
assert_eq!(game.player.health, vorher, "ein gestoerter Gegner richtet nichts an");
|
||||
assert!(game.enemies[0].intent.is_some(), "und er sagt fuer den naechsten Zug neu an");
|
||||
}
|
||||
|
||||
/// Gefunden im Spiel: Zwei Heiltraenke lagen als **ein** Stapel im
|
||||
/// Inventar, und `use_item` entfernte den Eintrag — ein Schluck, beide weg.
|
||||
/// Der Stapel war in M4 dazugekommen, seine Verbraucher nicht mitgezogen.
|
||||
#[test]
|
||||
fn ein_stapel_wird_stueckweise_verbraucht() {
|
||||
let (mut game, reg) = zwergenkrieger();
|
||||
game.give(ItemInstance::stack("core:health_potion", 2), ®);
|
||||
let platz = game.player.inventory.len() - 1;
|
||||
game.player.health = 10.0;
|
||||
|
||||
assert!(matches!(game.execute(Command::UseItem(platz), ®), Outcome::TurnPassed));
|
||||
assert_eq!(game.count_of("core:health_potion"), 1, "nur einer darf weg sein");
|
||||
assert!(game.player.health > 10.0, "und er muss auch wirken");
|
||||
|
||||
// Der zweite raeumt den Eintrag ab, statt einen Stapel der Groesse 0
|
||||
// stehen zu lassen.
|
||||
assert!(matches!(game.execute(Command::UseItem(platz), ®), Outcome::TurnPassed));
|
||||
assert_eq!(game.count_of("core:health_potion"), 0);
|
||||
assert!(
|
||||
!game.player.inventory.iter().any(|i| i.def == "core:health_potion"),
|
||||
"der leere Eintrag muss verschwinden"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user