diff --git a/crates/game-service/tests/walk_to_tower.rs b/crates/game-service/tests/walk_to_tower.rs index 864abf4..2cd8eef 100644 --- a/crates/game-service/tests/walk_to_tower.rs +++ b/crates/game-service/tests/walk_to_tower.rs @@ -122,6 +122,19 @@ fn the_bot_deploys_in_front_of_its_own_towers() { } } session.advance_to(tick); + for (side, leader) in session.mode().battle.leaders.iter().enumerate() { + let alive = session + .mode() + .battle + .objects + .objects + .iter() + .any(|entry| entry.global_id == *leader); + assert!( + alive, + "t={tick}: side {side}'s leader {leader:?} is no longer in the battle" + ); + } if session.is_finished() || session.towers_standing().0 == 0 { println!("battle over at t={tick} after {plays} bot plays"); break; @@ -205,3 +218,39 @@ fn a_bot_troop_walks_at_the_player_towers() { "the bot troop walked away from the player towers to {end:?}" ); } +#[test] +fn both_leaders_resolve_to_a_summoner() { + let root = assets(); + if let Ok(tables) = LogicDataTables::load_from_dir(&root) { + LogicDataTables::install(Arc::new(tables)); + } + let builder = BattleBuilder::new(&root); + let mode = builder + .build( + LogicDataRef::by_name(table::LOCATIONS, "PvP_goblin"), + LogicDataRef::None, + LogicDataRef::by_name(table::ARENAS, "Arena_T"), + vec![avatar(5), avatar(0)], + [None, None], + 1, + ) + .expect("battle"); + println!("leaders: {:?}", mode.battle.leaders); + for (side, leader) in mode.battle.leaders.iter().enumerate() { + assert!( + !leader.is_none(), + "side {side} has no leader; LogicBattle::resetSimulatedManaTimers reads both \ + without checking for null and takes the client down with it" + ); + let found = mode + .battle + .objects + .objects + .iter() + .find(|entry| entry.global_id == *leader); + assert!( + found.is_some(), + "side {side}'s leader {leader:?} matches no object in the battle" + ); + } +} diff --git a/crates/logic/src/battle/logic_simulation.rs b/crates/logic/src/battle/logic_simulation.rs index 2997a1b..9406aca 100644 --- a/crates/logic/src/battle/logic_simulation.rs +++ b/crates/logic/src/battle/logic_simulation.rs @@ -514,17 +514,20 @@ impl LogicBattle { } } fn remove_dead(&mut self) { + let leaders = self.leaders; let dead: Vec = self .objects .objects .iter() - .filter(|entry| !entry.is_alive()) + .filter(|entry| !entry.is_alive() && !leaders.contains(&entry.global_id)) .map(|entry| entry.global_id) .collect(); if dead.is_empty() { return; } - self.objects.objects.retain(LogicGameObjectEntry::is_alive); + self.objects + .objects + .retain(|entry| entry.is_alive() || leaders.contains(&entry.global_id)); for towers in self.leader_towers.iter_mut() { towers.retain(|tower| !dead.contains(tower)); }