- 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.
127 lines
4.0 KiB
Rust
127 lines
4.0 KiB
Rust
use crate::config::GameConfig;
|
|
use crate::constants::TILE_SIZE;
|
|
use crate::constants::*;
|
|
use crate::entities::behaviour::{EntityBehaviourRegistry, EntityType};
|
|
use crate::entities::cargo::{CarryVisualState, HaulSlot};
|
|
use crate::entities::shared_components::Ambulatory;
|
|
use crate::entities::tasks::job_queue::JobId;
|
|
use crate::entities::tasks::{IdleState, Task, TaskQueue, TaskState};
|
|
use crate::game::SpawnDelay;
|
|
use crate::world::VisibleGameEntity;
|
|
use bevy::prelude::*;
|
|
use bevy_rand::prelude::*;
|
|
use rand::{RngExt, SeedableRng};
|
|
use std::collections::VecDeque;
|
|
|
|
#[derive(Bundle)]
|
|
pub struct Dorf {
|
|
ambulatory: Ambulatory,
|
|
sprite: Sprite,
|
|
transform: Transform,
|
|
visibility: Visibility,
|
|
haul_slot: HaulSlot,
|
|
carry_visual: CarryVisualState,
|
|
task_queue: TaskQueue,
|
|
task_state: TaskState,
|
|
entity_type: EntityType,
|
|
}
|
|
|
|
impl Dorf {
|
|
pub fn new(asset_server: &Res<AssetServer>, position: Vec3) -> Self {
|
|
let walk_speed = 5.;
|
|
let run_speed = 6.;
|
|
let normal_sprite = asset_server.load("dorf.png");
|
|
let carry_sprite = asset_server.load("dorf.png");
|
|
let origin = position.as_ivec3();
|
|
let behaviour = EntityBehaviourRegistry::global_get("dorf");
|
|
|
|
Dorf {
|
|
ambulatory: Ambulatory {
|
|
walk_speed,
|
|
base_walk_speed: walk_speed,
|
|
run_speed,
|
|
target: None,
|
|
current_path: None,
|
|
path_index: 0,
|
|
step_recovery: 0,
|
|
validation_cooldown: 0,
|
|
move_direction: Vec2::ZERO,
|
|
step_history: [0i16; 4],
|
|
},
|
|
sprite: Sprite {
|
|
image: normal_sprite.clone(),
|
|
..Default::default()
|
|
},
|
|
transform: Transform::from_translation(position).with_scale(Vec3::splat(PIXEL_RATIO)),
|
|
visibility: Visibility::Hidden,
|
|
haul_slot: HaulSlot::default(),
|
|
carry_visual: CarryVisualState::new(normal_sprite, carry_sprite),
|
|
task_queue: TaskQueue {
|
|
tasks: VecDeque::from([Task::Idle {
|
|
job_id: JobId::default(),
|
|
origin,
|
|
sigma_world: behaviour.idle.sigma_world,
|
|
state: IdleState::Picking {
|
|
retry_after_tick: 0,
|
|
retry_count: 0,
|
|
},
|
|
}]),
|
|
},
|
|
task_state: TaskState::Pending,
|
|
entity_type: EntityType("dorf"),
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn spawn_dorfs(
|
|
mut commands: Commands,
|
|
asset_server: Res<AssetServer>,
|
|
config: Res<GameConfig>,
|
|
mut delay: ResMut<SpawnDelay>,
|
|
mut has_spawned: Local<bool>,
|
|
) {
|
|
if *has_spawned {
|
|
return;
|
|
}
|
|
|
|
if delay.0 < 60 {
|
|
delay.0 += 1;
|
|
return;
|
|
}
|
|
|
|
*has_spawned = true;
|
|
|
|
// Create deterministic RNG from SEED - same pattern as forestry.rs
|
|
use std::collections::hash_map::DefaultHasher;
|
|
use std::hash::{Hash, Hasher};
|
|
let mut hasher = DefaultHasher::new();
|
|
crate::constants::SEED.hash(&mut hasher);
|
|
let seed = hasher.finish();
|
|
let mut rng = WyRand::seed_from_u64(seed);
|
|
|
|
use crate::world::chunks::Z_ABOVE;
|
|
|
|
for _ in 0..config.spawn_counts.dorfs {
|
|
let raw_x = 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_y = (raw_y / TILE_SIZE).round() * 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
|
|
.spawn(Dorf::new(&asset_server, Vec3::new(grid_x, grid_y, grid_z)))
|
|
.id();
|
|
commands.entity(cit).insert(VisibleGameEntity);
|
|
}
|
|
}
|