From d1cc829623824009621b66dd178a3dafdccb744d Mon Sep 17 00:00:00 2001 From: WiseDev <83840010+wisedevik@users.noreply.github.com> Date: Sun, 23 Aug 2026 13:28:23 +0300 Subject: [PATCH] send units at the nearest tower when nothing is in sight units stood still all battle. a unit only moved if it had a combat target, and targets are only found inside SightRange - six thousand units for a knight - while the enemy tower sits twenty thousand away. so nothing ever walked, nothing was ever hit, no tower fell, crowns stayed at nothing and the battle could only end on the clock. a unit with no target now walks at the nearest enemy building, which is the default target the client falls back to. once the tower comes inside sight the ordinary search picks it up and the attack timer takes over. the battle turn log carries the standing tower count per side so damage is visible before it becomes a crown. --- crates/game-service/src/battle_session.rs | 8 ++++++ crates/game-service/src/service.rs | 1 + crates/logic/src/battle/logic_simulation.rs | 28 ++++++++++++++++++--- 3 files changed, 34 insertions(+), 3 deletions(-) diff --git a/crates/game-service/src/battle_session.rs b/crates/game-service/src/battle_session.rs index bd31a79..e961ea3 100644 --- a/crates/game-service/src/battle_session.rs +++ b/crates/game-service/src/battle_session.rs @@ -209,6 +209,12 @@ impl BattleSession { pub fn stars(&self) -> (i32, i32) { (self.mode.battle.stars(0), self.mode.battle.stars(1)) } + pub fn towers_standing(&self) -> (usize, usize) { + ( + self.mode.battle.leader_towers[0].len(), + self.mode.battle.leader_towers[1].len(), + ) + } pub fn owner_of(&self, account: LogicLong) -> Option { self.mode .battle @@ -312,6 +318,7 @@ impl BattleRegistry { finished: session.is_finished(), stars: session.stars(), objects: session.object_count(), + towers: session.towers_standing(), }) } pub async fn play( @@ -348,4 +355,5 @@ pub struct BattleProgress { pub finished: bool, pub stars: (i32, i32), pub objects: usize, + pub towers: (usize, usize), } diff --git a/crates/game-service/src/service.rs b/crates/game-service/src/service.rs index 965ac11..7ab5dcc 100644 --- a/crates/game-service/src/service.rs +++ b/crates/game-service/src/service.rs @@ -373,6 +373,7 @@ impl GameApi for GameService { finished = progress.map(|state| state.finished), stars = ?progress.map(|state| state.stars), objects = progress.map(|state| state.objects), + towers = ?progress.map(|state| state.towers), spawned, commands = ?turn .commands diff --git a/crates/logic/src/battle/logic_simulation.rs b/crates/logic/src/battle/logic_simulation.rs index f6f72d7..00d494a 100644 --- a/crates/logic/src/battle/logic_simulation.rs +++ b/crates/logic/src/battle/logic_simulation.rs @@ -187,6 +187,24 @@ impl LogicBattle { } } } + fn default_target(&self, index: usize) -> Option { + let owner = self.objects.objects[index].owner_index(); + let from = self.objects.objects[index].position(); + let mut best: Option<(i32, usize)> = None; + for (other, entry) in self.objects.objects.iter().enumerate() { + if other == index || entry.owner_index() == owner || !entry.is_alive() { + continue; + } + if !entry.stats().is_building() { + continue; + } + let distance = distance_squared(from, entry.position()); + if best.map(|(closest, _)| distance < closest).unwrap_or(true) { + best = Some((distance, other)); + } + } + best.map(|(_, other)| other) + } fn move_objects(&mut self) { let positions: Vec<(i32, i32)> = self .objects @@ -199,11 +217,15 @@ impl LogicBattle { if stats.speed < 1 || !self.objects.objects[index].is_alive() { continue; } - let Some(target) = self.objects.objects[index] + let target = match self.objects.objects[index] .combat_mut() .and_then(|component| component.target_index) - else { - continue; + { + Some(target) => target, + None => match self.default_target(index) { + Some(target) => target, + None => continue, + }, }; let Some(goal) = positions.get(target).copied() else { continue;