keep units on the map so the pathfinder cannot panic

a collision could shove a unit past the edge of the arena. the next path
search then turned that negative coordinate into a tile index, cast it to
usize and read far off the end of the grid - the panic killed the tokio
task running the battle, snapshots stopped, and the models the client had
already been told about were left frozen and unresolvable. that is the
invisible-unit symptom: the simulation was dead, not the rendering.

the push is clamped to the arena now, and find_path checks that the start
tile is on the map rather than only the goal.
This commit is contained in:
WiseDev 2026-08-23 14:23:22 +03:00
parent 9f3ac3f0b9
commit e9894d9838
2 changed files with 12 additions and 3 deletions

View file

@ -36,7 +36,10 @@ pub fn find_path(
) -> Vec<(i32, i32)> { ) -> Vec<(i32, i32)> {
let start = (tile_of(from.0), tile_of(from.1)); let start = (tile_of(from.0), tile_of(from.1));
let goal = (tile_of(to.0), tile_of(to.1)); let goal = (tile_of(to.0), tile_of(to.1));
if start == goal || !tilemap.is_inside(goal.0, goal.1) { if start == goal
|| !tilemap.is_inside(start.0, start.1)
|| !tilemap.is_inside(goal.0, goal.1)
{
return Vec::new(); return Vec::new();
} }
let width = tilemap.width().max(1) as usize; let width = tilemap.width().max(1) as usize;

View file

@ -2,6 +2,7 @@ use crate::battle::logic_battle::LogicBattle;
use crate::battle::logic_component::{LogicComponent, COMPONENT_COMBAT, COMPONENT_HITPOINT}; use crate::battle::logic_component::{LogicComponent, COMPONENT_COMBAT, COMPONENT_HITPOINT};
use crate::battle::logic_game_object::{LogicGameObjectEntry, LogicObjectBody}; use crate::battle::logic_game_object::{LogicGameObjectEntry, LogicObjectBody};
use crate::battle::logic_battle::BATTLE_TICKS_PER_SECOND; use crate::battle::logic_battle::BATTLE_TICKS_PER_SECOND;
use crate::battle::logic_tilemap::SUBTILE_UNITS;
use crate::data::LogicDataRef; use crate::data::LogicDataRef;
pub const TICK_MILLISECONDS: i32 = 50; pub const TICK_MILLISECONDS: i32 = 50;
pub const MANA_ACCUMULATOR_STEP: i32 = 5000; pub const MANA_ACCUMULATOR_STEP: i32 = 5000;
@ -426,9 +427,14 @@ impl LogicBattle {
if push.2 == 0 { if push.2 == 0 {
continue; continue;
} }
let (limit_x, limit_y) = self
.tilemap
.as_ref()
.map(|map| (map.width() * SUBTILE_UNITS - 1, map.height() * SUBTILE_UNITS - 1))
.unwrap_or((i32::MAX, i32::MAX));
let base = self.objects.objects[index].body.base_mut(); let base = self.objects.objects[index].body.base_mut();
base.position.x += (push.0 / push.2 as i64) as i32; base.position.x = (base.position.x + (push.0 / push.2 as i64) as i32).clamp(0, limit_x);
base.position.y += (push.1 / push.2 as i64) as i32; base.position.y = (base.position.y + (push.1 / push.2 as i64) as i32).clamp(0, limit_y);
} }
} }
fn resolve_attacks(&mut self) { fn resolve_attacks(&mut self) {