Files
dorf/src/entities/tasks/idle.rs
T
2026-03-22 01:24:08 +00:00

86 lines
2.5 KiB
Rust

//! Idle task implementation — wandering behaviour refactored here.
//!
//! This module contains the logic that was previously in the wandering
//! system. Now it's called by the task executor when Task::Idle is active.
use crate::constants::ITILE_SIZE;
use crate::entities::shared_components::Ambulatory;
use crate::entities::tasks::components::Task;
use crate::world::tiles::tilemap::TileMap;
use bevy::prelude::*;
use bevy_rand::prelude::*;
use rand::RngExt;
/// Execute Idle task: set target for wandering, let pathfinding handle movement.
pub(super) fn execute_idle(
entity: Entity,
task: &mut Task,
transform: &Transform,
ambulatory: &mut Ambulatory,
tilemap: &TileMap,
rng: &mut WyRand,
) {
let Task::Idle {
target,
wander_radius,
origin,
} = task
else {
return;
};
let entity_pos = (transform.translation / ITILE_SIZE as f32).as_ivec3();
// Track if target actually changed this tick
let mut target_changed = false;
// If reached target or target is no longer standable, pick new target
if entity_pos == *target || !tilemap.is_standable(*target) {
if let Some(new_target) = pick_wander_target(origin, *wander_radius, tilemap, rng) {
*target = new_target;
target_changed = true;
}
}
// Only reset path if target changed or no path exists
if target_changed || ambulatory.current_path.is_none() {
ambulatory.target = Some(Vec3::new(
target.x as f32,
target.y as f32,
transform.translation.z,
));
ambulatory.current_path = None;
ambulatory.path_index = 0;
}
}
/// Pick a random standable tile within wander radius of origin.
/// Uses reservoir sampling to avoid heap allocation.
fn pick_wander_target(
origin: &IVec3,
radius: i32,
tilemap: &TileMap,
rng: &mut WyRand,
) -> Option<IVec3> {
let mut chosen = None;
let mut count = 0u32;
for dx in -radius..=radius {
for dy in -radius..=radius {
let candidate = IVec3::new(
origin.x + dx * ITILE_SIZE,
origin.y + dy * ITILE_SIZE,
origin.z,
);
if tilemap.is_standable(candidate) {
count += 1;
// Reservoir sampling: 1/count chance to replace chosen
if rng.random_range(0..count) == 0 {
chosen = Some(candidate);
}
}
}
}
chosen
}