From c46e23af06b7e1aa3a7f772e6dd6f30c46b90920 Mon Sep 17 00:00:00 2001 From: popertots Date: Sun, 22 Mar 2026 15:23:44 +0000 Subject: [PATCH] fix --- src/entities/cargo/log.rs | 2 +- src/entities/tasks/demo.rs | 16 ++++++++++++---- src/entities/tasks/executor.rs | 4 ++-- src/world/tiles/tilemap.rs | 19 ++++++++++++++++--- 4 files changed, 31 insertions(+), 10 deletions(-) diff --git a/src/entities/cargo/log.rs b/src/entities/cargo/log.rs index 67703c5..1d3abb6 100644 --- a/src/entities/cargo/log.rs +++ b/src/entities/cargo/log.rs @@ -57,7 +57,7 @@ pub fn spawn_log_cargo( ); // Find nearest free tile from biased position - let drop_pos = tilemap.find_nearest_free_cargo_tile(biased_pos, 4)?; + let drop_pos = tilemap.find_nearest_free_cargo_tile(biased_pos, 4, &[])?; // TODO: OuchEvent — if a living entity occupies drop_pos, they take impact damage. // Check tilemap occupancy here when the combat/injury system exists. diff --git a/src/entities/tasks/demo.rs b/src/entities/tasks/demo.rs index 2ac17c7..034d67e 100644 --- a/src/entities/tasks/demo.rs +++ b/src/entities/tasks/demo.rs @@ -283,13 +283,21 @@ pub fn demo_system( let idle_dorfs: SmallVec<[Entity; 8]> = idle_dorfs.into_iter().map(|(e, _)| e).collect(); + // Track destinations reserved this tick to avoid assigning the same tile + // to multiple dorfs before any have physically dropped their cargo. + let mut reserved: SmallVec<[IVec3; 8]> = SmallVec::new(); + for dorf_entity in idle_dorfs { - // Compute haul destination fresh for each assignment — - // as logs accumulate near origin, each new assignment correctly - // finds the next free tile. + // Compute haul destination fresh for each assignment, + // excluding tiles already reserved this tick. let dest = tilemap - .find_nearest_free_cargo_tile(IVec3::ZERO, HAUL_DEST_SEARCH_RADIUS) + .find_nearest_free_cargo_tile( + IVec3::ZERO, + HAUL_DEST_SEARCH_RADIUS, + &reserved, + ) .unwrap_or(IVec3::ZERO); + reserved.push(dest); let Some((log_entity, log_pos)) = unassigned.pop_front() else { break; diff --git a/src/entities/tasks/executor.rs b/src/entities/tasks/executor.rs index 8b66854..7540dfb 100644 --- a/src/entities/tasks/executor.rs +++ b/src/entities/tasks/executor.rs @@ -276,7 +276,7 @@ pub fn task_executor_system( if chosen_drop.is_none() { *chosen_drop = Some( tilemap_mut - .find_nearest_free_cargo_tile(*dest, 8) + .find_nearest_free_cargo_tile(*dest, 8, &[]) .unwrap_or(*dest), ); } @@ -331,7 +331,7 @@ pub fn task_executor_system( if chosen_drop.is_none() { *chosen_drop = Some( tilemap_mut - .find_nearest_free_cargo_tile(*pos, 8) + .find_nearest_free_cargo_tile(*pos, 8, &[]) .unwrap_or(*pos), ); } diff --git a/src/world/tiles/tilemap.rs b/src/world/tiles/tilemap.rs index 1f6d631..ee86728 100644 --- a/src/world/tiles/tilemap.rs +++ b/src/world/tiles/tilemap.rs @@ -467,8 +467,18 @@ impl TileMap { /// Uses a spiral outward search — O(radius²) worst case but returns immediately /// on first free tile found. Searches only at origin.z (same z-level). /// Returns None if no free tile found within radius. - pub fn find_nearest_free_cargo_tile(&self, origin: IVec3, max_radius: i32) -> Option { - if !self.cargo_tiles.contains_key(&origin) && self.is_standable(origin) { + /// Excludes any tiles in `exclude` from consideration — useful when + /// reserving multiple destinations in the same tick. + pub fn find_nearest_free_cargo_tile( + &self, + origin: IVec3, + max_radius: i32, + exclude: &[IVec3], + ) -> Option { + if !self.cargo_tiles.contains_key(&origin) + && !exclude.contains(&origin) + && self.is_standable(origin) + { return Some(origin); } @@ -483,7 +493,10 @@ impl TileMap { origin.y + dy * ITILE_SIZE, origin.z, ); - if !self.cargo_tiles.contains_key(&candidate) && self.is_standable(candidate) { + if !self.cargo_tiles.contains_key(&candidate) + && !exclude.contains(&candidate) + && self.is_standable(candidate) + { return Some(candidate); } }