Fix bug: prevent multiple job assignments to same dorf in one frame
- Add assigned_this_frame HashSet to track dorfs receiving jobs - Filter out already-assigned dorfs from subsequent job assignments - Add debug logging for spawn position (z-level) - Add debug logging for is_standable failures - Add debug logging for falling/snapping movement behavior This fixes the critical bug where one dorf could be assigned multiple jobs in the same frame, causing earlier assignments to be lost due to queue.clear(). Each dorf now receives at most one job per frame.
This commit is contained in:
@@ -99,12 +99,24 @@ pub fn spawn_dorfs(
|
|||||||
let seed = hasher.finish();
|
let seed = hasher.finish();
|
||||||
let mut rng = WyRand::seed_from_u64(seed);
|
let mut rng = WyRand::seed_from_u64(seed);
|
||||||
|
|
||||||
|
use crate::world::chunks::Z_ABOVE;
|
||||||
|
|
||||||
for _ in 0..config.spawn_counts.dorfs {
|
for _ in 0..config.spawn_counts.dorfs {
|
||||||
let raw_x = rng.random_range(-512.0f32..512.0f32);
|
let raw_x = rng.random_range(-512.0f32..512.0f32);
|
||||||
let raw_y = rng.random_range(-512.0f32..512.0f32);
|
let raw_y = rng.random_range(-512.0f32..512.0f32);
|
||||||
let grid_x = (raw_x / TILE_SIZE).round() * TILE_SIZE;
|
let grid_x = (raw_x / TILE_SIZE).round() * TILE_SIZE;
|
||||||
let grid_y = (raw_y / TILE_SIZE).round() * TILE_SIZE;
|
let grid_y = (raw_y / TILE_SIZE).round() * TILE_SIZE;
|
||||||
let grid_z = 35.0 * TILE_SIZE;
|
// Spawn at top of valid world range (Z_ABOVE * TILE_SIZE)
|
||||||
|
// The movement system will snap/fall them to surface level
|
||||||
|
let grid_z = Z_ABOVE * TILE_SIZE;
|
||||||
|
|
||||||
|
info!(
|
||||||
|
"[SPAWN] Dorf spawning at ({}, {}, {}) z_level={}",
|
||||||
|
grid_x,
|
||||||
|
grid_y,
|
||||||
|
grid_z,
|
||||||
|
grid_z / TILE_SIZE
|
||||||
|
);
|
||||||
|
|
||||||
let cit = commands
|
let cit = commands
|
||||||
.spawn(Dorf::new(&asset_server, Vec3::new(grid_x, grid_y, grid_z)))
|
.spawn(Dorf::new(&asset_server, Vec3::new(grid_x, grid_y, grid_z)))
|
||||||
|
|||||||
@@ -618,13 +618,13 @@ pub fn update_wandering_targets(
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn movement(
|
pub fn movement(
|
||||||
mut query: Query<(&mut Ambulatory, &mut Transform)>,
|
mut query: Query<(Entity, &mut Ambulatory, &mut Transform)>,
|
||||||
tilemap: Res<TileMap>,
|
tilemap: Res<TileMap>,
|
||||||
occupancy: Res<TileOccupancy>,
|
occupancy: Res<TileOccupancy>,
|
||||||
) {
|
) {
|
||||||
query
|
query
|
||||||
.par_iter_mut()
|
.par_iter_mut()
|
||||||
.for_each(|(mut ambulatory, mut transform)| {
|
.for_each(|(entity, mut ambulatory, mut transform)| {
|
||||||
debug!(
|
debug!(
|
||||||
"[PATH] Moving: target={:?} current={:?}",
|
"[PATH] Moving: target={:?} current={:?}",
|
||||||
ambulatory.target, transform.translation
|
ambulatory.target, transform.translation
|
||||||
@@ -635,8 +635,20 @@ pub fn movement(
|
|||||||
// Snap them to the top instead of falling through unloaded z-space one
|
// Snap them to the top instead of falling through unloaded z-space one
|
||||||
// tile per tick.
|
// tile per tick.
|
||||||
if transform.translation.z > Z_ABOVE as f32 * TILE_SIZE {
|
if transform.translation.z > Z_ABOVE as f32 * TILE_SIZE {
|
||||||
|
info!(
|
||||||
|
"[MOVEMENT] Entity {:?} above world at z={}, snapping to z={}",
|
||||||
|
entity,
|
||||||
|
transform.translation.z,
|
||||||
|
Z_ABOVE as f32 * TILE_SIZE
|
||||||
|
);
|
||||||
transform.translation.z = Z_ABOVE as f32 * TILE_SIZE;
|
transform.translation.z = Z_ABOVE as f32 * TILE_SIZE;
|
||||||
} else if transform.translation.z > -(Z_BELOW as f32) * TILE_SIZE {
|
} else if transform.translation.z > -(Z_BELOW as f32) * TILE_SIZE {
|
||||||
|
info!(
|
||||||
|
"[MOVEMENT] Entity {:?} falling at z={}, z_level={}",
|
||||||
|
entity,
|
||||||
|
transform.translation.z,
|
||||||
|
transform.translation.z / TILE_SIZE
|
||||||
|
);
|
||||||
transform.translation.z -= TILE_SIZE;
|
transform.translation.z -= TILE_SIZE;
|
||||||
}
|
}
|
||||||
ambulatory.current_path = None;
|
ambulatory.current_path = None;
|
||||||
|
|||||||
@@ -45,6 +45,10 @@ pub fn job_pathfinding_system(
|
|||||||
info!("[PATHFIND] Locked dorfs: {:?}", locked_dorfs.len());
|
info!("[PATHFIND] Locked dorfs: {:?}", locked_dorfs.len());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Track dorfs assigned jobs in this frame to prevent multiple assignments
|
||||||
|
let mut assigned_this_frame: std::collections::HashSet<Entity> =
|
||||||
|
std::collections::HashSet::new();
|
||||||
|
|
||||||
for job_idx in unclaimed.into_iter().take(MAX_JOBS_PER_TICK) {
|
for job_idx in unclaimed.into_iter().take(MAX_JOBS_PER_TICK) {
|
||||||
let (kind, state, _) = match job_queue.get_job_at(job_idx) {
|
let (kind, state, _) = match job_queue.get_job_at(job_idx) {
|
||||||
Some(k) => k,
|
Some(k) => k,
|
||||||
@@ -80,7 +84,7 @@ pub fn job_pathfinding_system(
|
|||||||
.filter_map(|(entity, queue, state, transform, _)| {
|
.filter_map(|(entity, queue, state, transform, _)| {
|
||||||
total_dorfs += 1;
|
total_dorfs += 1;
|
||||||
|
|
||||||
if locked_dorfs.contains(&entity) {
|
if locked_dorfs.contains(&entity) || assigned_this_frame.contains(&entity) {
|
||||||
locked_count += 1;
|
locked_count += 1;
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
@@ -88,6 +92,12 @@ pub fn job_pathfinding_system(
|
|||||||
let pos = transform.translation.as_ivec3();
|
let pos = transform.translation.as_ivec3();
|
||||||
if !tilemap.is_standable(pos) {
|
if !tilemap.is_standable(pos) {
|
||||||
not_standable_count += 1;
|
not_standable_count += 1;
|
||||||
|
info!(
|
||||||
|
"[PATHFIND] Dorf {:?} NOT STANDABLE at pos={:?} z_level={}",
|
||||||
|
entity,
|
||||||
|
pos,
|
||||||
|
pos.z / crate::constants::ITILE_SIZE
|
||||||
|
);
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -296,6 +306,7 @@ pub fn job_pathfinding_system(
|
|||||||
"[PATHFIND] SUCCESS: Assigned job {:?} to dorf {:?} with approach {:?}",
|
"[PATHFIND] SUCCESS: Assigned job {:?} to dorf {:?} with approach {:?}",
|
||||||
job_id, dorf_entity, approach_target
|
job_id, dorf_entity, approach_target
|
||||||
);
|
);
|
||||||
|
assigned_this_frame.insert(dorf_entity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
Reference in New Issue
Block a user