//! 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 { 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 }