Task Infrastructure — Task Enum, Queue, Events, Executor
This commit is contained in:
@@ -4,5 +4,6 @@ pub mod livestock;
|
|||||||
pub mod sentient;
|
pub mod sentient;
|
||||||
pub mod shared_components;
|
pub mod shared_components;
|
||||||
pub mod shared_systems;
|
pub mod shared_systems;
|
||||||
|
pub mod tasks;
|
||||||
|
|
||||||
pub use shared_systems::*;
|
pub use shared_systems::*;
|
||||||
|
|||||||
@@ -3,11 +3,13 @@ use crate::constants::TILE_SIZE;
|
|||||||
use crate::constants::*;
|
use crate::constants::*;
|
||||||
use crate::entities::cargo::{CarryVisualState, HaulSlot};
|
use crate::entities::cargo::{CarryVisualState, HaulSlot};
|
||||||
use crate::entities::shared_components::Ambulatory;
|
use crate::entities::shared_components::Ambulatory;
|
||||||
|
use crate::entities::tasks::{Task, TaskQueue, TaskState};
|
||||||
use crate::game::SpawnDelay;
|
use crate::game::SpawnDelay;
|
||||||
use crate::world::VisibleGameEntity;
|
use crate::world::VisibleGameEntity;
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
use bevy_rand::prelude::*;
|
use bevy_rand::prelude::*;
|
||||||
use rand::RngExt;
|
use rand::RngExt;
|
||||||
|
use smallvec::smallvec;
|
||||||
|
|
||||||
#[derive(Bundle)]
|
#[derive(Bundle)]
|
||||||
pub struct Dorf {
|
pub struct Dorf {
|
||||||
@@ -17,6 +19,8 @@ pub struct Dorf {
|
|||||||
visibility: Visibility,
|
visibility: Visibility,
|
||||||
haul_slot: HaulSlot,
|
haul_slot: HaulSlot,
|
||||||
carry_visual: CarryVisualState,
|
carry_visual: CarryVisualState,
|
||||||
|
task_queue: TaskQueue,
|
||||||
|
task_state: TaskState,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Dorf {
|
impl Dorf {
|
||||||
@@ -25,6 +29,7 @@ impl Dorf {
|
|||||||
let run_speed = 6.;
|
let run_speed = 6.;
|
||||||
let normal_sprite = asset_server.load("dorf.png");
|
let normal_sprite = asset_server.load("dorf.png");
|
||||||
let carry_sprite = asset_server.load("dorf.png");
|
let carry_sprite = asset_server.load("dorf.png");
|
||||||
|
let origin = (position / TILE_SIZE).as_ivec3();
|
||||||
|
|
||||||
Dorf {
|
Dorf {
|
||||||
ambulatory: Ambulatory {
|
ambulatory: Ambulatory {
|
||||||
@@ -47,6 +52,14 @@ impl Dorf {
|
|||||||
visibility: Visibility::Hidden,
|
visibility: Visibility::Hidden,
|
||||||
haul_slot: HaulSlot::default(),
|
haul_slot: HaulSlot::default(),
|
||||||
carry_visual: CarryVisualState::new(normal_sprite, carry_sprite),
|
carry_visual: CarryVisualState::new(normal_sprite, carry_sprite),
|
||||||
|
task_queue: TaskQueue {
|
||||||
|
tasks: smallvec![Task::Idle {
|
||||||
|
target: origin,
|
||||||
|
wander_radius: 10,
|
||||||
|
origin,
|
||||||
|
}],
|
||||||
|
},
|
||||||
|
task_state: TaskState::Pending,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -71,6 +71,7 @@ use crate::entities::shared_systems::constants::{
|
|||||||
WALK_SPEED_DIVISOR,
|
WALK_SPEED_DIVISOR,
|
||||||
};
|
};
|
||||||
use crate::entities::shared_systems::occupancy::{rebuild_tile_occupancy, TileOccupancy};
|
use crate::entities::shared_systems::occupancy::{rebuild_tile_occupancy, TileOccupancy};
|
||||||
|
use crate::entities::tasks::task_executor_system;
|
||||||
use crate::world::tiles::tile_changed::{PathfindingDirtyChunks, TileChangedEvent};
|
use crate::world::tiles::tile_changed::{PathfindingDirtyChunks, TileChangedEvent};
|
||||||
use crate::world::tiles::TileMap;
|
use crate::world::tiles::TileMap;
|
||||||
use crate::world::{
|
use crate::world::{
|
||||||
@@ -236,8 +237,10 @@ impl Plugin for PathfindingPlugin {
|
|||||||
collect_pathfinding_dirty_chunks,
|
collect_pathfinding_dirty_chunks,
|
||||||
invalidate_paths_on_tile_change,
|
invalidate_paths_on_tile_change,
|
||||||
prepare_paths,
|
prepare_paths,
|
||||||
|
// DEPRECATED: Wandering now handled by Task::Idle in task executor
|
||||||
update_wandering_targets,
|
update_wandering_targets,
|
||||||
movement,
|
movement,
|
||||||
|
task_executor_system,
|
||||||
)
|
)
|
||||||
.chain(),
|
.chain(),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -0,0 +1,153 @@
|
|||||||
|
//! Task system components — the data backbone for entity behaviour.
|
||||||
|
//!
|
||||||
|
//! # Design
|
||||||
|
//! - Tasks are enum variants with associated data, not trait objects — zero-cost dispatch.
|
||||||
|
//! - TaskQueue holds a VecDeque of pending tasks; front() is current active task.
|
||||||
|
//! - TaskState tracks execution phase: Pending → Active → Completed/Failed.
|
||||||
|
//! - All task data is owned — no references to world state — for cache locality.
|
||||||
|
//!
|
||||||
|
//! # Performance
|
||||||
|
//! - SmallVec<[Task; 4]> for queue — avoids heap alloc for typical short queues.
|
||||||
|
//! - Task enum is #[repr(u8)] for compact storage and fast matching.
|
||||||
|
//! - No dynamic dispatch — match on Task variant directly in executor.
|
||||||
|
|
||||||
|
use bevy::prelude::*;
|
||||||
|
use smallvec::SmallVec;
|
||||||
|
|
||||||
|
/// A single task an entity can execute.
|
||||||
|
///
|
||||||
|
/// Tasks are self-contained: they carry all data needed for execution.
|
||||||
|
/// The executor matches on the variant and drives behaviour accordingly.
|
||||||
|
///
|
||||||
|
/// # Variant lifecycle
|
||||||
|
/// - Created by task-generating systems (e.g., "chop that tree")
|
||||||
|
/// - Pushed to TaskQueue
|
||||||
|
/// - Popped to Active by executor when ready
|
||||||
|
/// - Completed/Failed → removed, events fired
|
||||||
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
#[repr(u8)]
|
||||||
|
pub enum Task {
|
||||||
|
/// Idle/wandering behaviour. Refactored from existing wandering system.
|
||||||
|
/// Target is a standable tile within wander_radius; entity moves toward it.
|
||||||
|
/// When reached, picks new random target. Never "completes" — cycles forever
|
||||||
|
/// until replaced by a higher-priority task.
|
||||||
|
Idle {
|
||||||
|
/// Current wander target tile (world position).
|
||||||
|
target: IVec3,
|
||||||
|
/// Max distance from origin to wander (Chebyshev).
|
||||||
|
wander_radius: i32,
|
||||||
|
/// Origin tile for wander bounds.
|
||||||
|
origin: IVec3,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// Move to a tile within "close enough" threshold.
|
||||||
|
/// Completes when entity is within `threshold_tiles` of target.
|
||||||
|
/// Uses existing pathfinding infrastructure.
|
||||||
|
GoTo {
|
||||||
|
target: IVec3,
|
||||||
|
/// Completion threshold in tiles (Chebyshev distance).
|
||||||
|
threshold_tiles: i32,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// Placeholder for Stage 3 implementations.
|
||||||
|
/// Executor should match and log "unimplemented" for now.
|
||||||
|
ChopTree {
|
||||||
|
target_fixture: IVec3,
|
||||||
|
},
|
||||||
|
HaulObject {
|
||||||
|
target_cargo_tile: IVec3,
|
||||||
|
},
|
||||||
|
DropHauled {
|
||||||
|
drop_tile: IVec3,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Task {
|
||||||
|
/// Returns true if this task is "terminal" — i.e., should be popped from
|
||||||
|
/// the queue when done, rather than cycled (like Idle).
|
||||||
|
#[inline]
|
||||||
|
pub fn is_terminal(&self) -> bool {
|
||||||
|
!matches!(self, Task::Idle { .. })
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Human-readable name for debug logging.
|
||||||
|
#[inline]
|
||||||
|
pub fn name(&self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
Task::Idle { .. } => "Idle",
|
||||||
|
Task::GoTo { .. } => "GoTo",
|
||||||
|
Task::ChopTree { .. } => "ChopTree",
|
||||||
|
Task::HaulObject { .. } => "HaulObject",
|
||||||
|
Task::DropHauled { .. } => "DropHauled",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Queue of tasks for an entity. Front of queue is currently executing task.
|
||||||
|
///
|
||||||
|
/// Tasks are pushed to the back; executor pops from front when complete.
|
||||||
|
/// Higher-priority tasks can be inserted at front via `push_front`.
|
||||||
|
///
|
||||||
|
/// Uses SmallVec to avoid heap allocation for typical queue lengths (<4 tasks).
|
||||||
|
#[derive(Component, Debug, Default)]
|
||||||
|
pub struct TaskQueue {
|
||||||
|
pub tasks: SmallVec<[Task; 4]>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TaskQueue {
|
||||||
|
#[inline]
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.tasks.is_empty()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn current(&self) -> Option<&Task> {
|
||||||
|
self.tasks.first()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn current_mut(&mut self) -> Option<&mut Task> {
|
||||||
|
self.tasks.first_mut()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn push(&mut self, task: Task) {
|
||||||
|
self.tasks.push(task);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn push_front(&mut self, task: Task) {
|
||||||
|
self.tasks.insert(0, task);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn pop(&mut self) -> Option<Task> {
|
||||||
|
if self.tasks.is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(self.tasks.remove(0))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn clear(&mut self) {
|
||||||
|
self.tasks.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Tracks execution state of the current task.
|
||||||
|
///
|
||||||
|
/// Allows systems to react to task phase changes without polling TaskQueue.
|
||||||
|
/// Updated by executor system each tick.
|
||||||
|
#[derive(Component, Debug, Default, PartialEq, Eq)]
|
||||||
|
pub enum TaskState {
|
||||||
|
/// Task is queued but not yet started.
|
||||||
|
#[default]
|
||||||
|
Pending,
|
||||||
|
/// Task is actively being executed this frame.
|
||||||
|
Active,
|
||||||
|
/// Task completed successfully — will be popped next tick.
|
||||||
|
Completed,
|
||||||
|
/// Task failed or was interrupted — will be popped next tick.
|
||||||
|
Failed,
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
//! Task lifecycle events — for decoupled reaction to task state changes.
|
||||||
|
//!
|
||||||
|
//! Events are fired by the executor when a task transitions state.
|
||||||
|
//! Systems can listen for these to trigger UI updates, logging, or
|
||||||
|
//! downstream task generation (e.g., "ChopTree completed" → spawn Cargo).
|
||||||
|
|
||||||
|
use crate::entities::tasks::components::Task;
|
||||||
|
use bevy::prelude::*;
|
||||||
|
|
||||||
|
#[derive(Message, Clone)]
|
||||||
|
pub struct TaskClaimed {
|
||||||
|
pub entity: Entity,
|
||||||
|
pub task: Task,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Message, Clone)]
|
||||||
|
pub struct TaskCompleted {
|
||||||
|
pub entity: Entity,
|
||||||
|
pub task: Task,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Message, Clone)]
|
||||||
|
pub struct TaskFailed {
|
||||||
|
pub entity: Entity,
|
||||||
|
pub task: Task,
|
||||||
|
pub reason: &'static str,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Message, Clone)]
|
||||||
|
pub struct TaskDropped {
|
||||||
|
pub entity: Entity,
|
||||||
|
pub task: Task,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Message, Clone)]
|
||||||
|
pub struct TaskBlocked {
|
||||||
|
pub entity: Entity,
|
||||||
|
pub task: Task,
|
||||||
|
pub blocker: &'static str,
|
||||||
|
}
|
||||||
@@ -0,0 +1,141 @@
|
|||||||
|
//! Task executor system — the dispatcher that drives entity behaviour.
|
||||||
|
//!
|
||||||
|
//! Runs in FixedUpdate. For each entity with TaskQueue:
|
||||||
|
//! 1. If no active task, pop front of queue → set TaskState::Active
|
||||||
|
//! 2. Match on active Task variant, execute corresponding logic
|
||||||
|
//! 3. On completion/failure, update TaskState, fire event, pop task
|
||||||
|
//! 4. If queue empty after pop, assign default Task::Idle
|
||||||
|
//!
|
||||||
|
//! Uses Changed<TaskQueue> + Changed<TaskState> to minimize queries.
|
||||||
|
|
||||||
|
use crate::constants::ITILE_SIZE;
|
||||||
|
use crate::entities::shared_components::Ambulatory;
|
||||||
|
use crate::entities::tasks::components::{Task, TaskQueue, TaskState};
|
||||||
|
use crate::entities::tasks::events::{TaskClaimed, TaskCompleted, TaskFailed};
|
||||||
|
use crate::entities::tasks::idle::execute_idle;
|
||||||
|
use crate::world::tiles::tilemap::TileMap;
|
||||||
|
use bevy::prelude::*;
|
||||||
|
use bevy_rand::prelude::*;
|
||||||
|
|
||||||
|
/// Main task executor. Runs in FixedUpdate.
|
||||||
|
pub fn task_executor_system(
|
||||||
|
mut commands: Commands,
|
||||||
|
tilemap: Res<TileMap>,
|
||||||
|
mut rng_q: Query<&mut WyRand, With<GlobalRng>>,
|
||||||
|
mut query: Query<(
|
||||||
|
Entity,
|
||||||
|
&mut TaskQueue,
|
||||||
|
&mut TaskState,
|
||||||
|
&mut Ambulatory,
|
||||||
|
&Transform,
|
||||||
|
)>,
|
||||||
|
mut claimed_writer: MessageWriter<TaskClaimed>,
|
||||||
|
mut completed_writer: MessageWriter<TaskCompleted>,
|
||||||
|
mut failed_writer: MessageWriter<TaskFailed>,
|
||||||
|
) {
|
||||||
|
let Ok(mut rng) = rng_q.single_mut() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
for (entity, mut queue, mut state, mut ambulatory, transform) in query.iter_mut() {
|
||||||
|
// If queue empty, assign default Idle task
|
||||||
|
if queue.is_empty() {
|
||||||
|
let origin = (transform.translation / ITILE_SIZE as f32).as_ivec3();
|
||||||
|
queue.push(Task::Idle {
|
||||||
|
target: origin,
|
||||||
|
wander_radius: 10,
|
||||||
|
origin,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Promote Pending → Active if needed
|
||||||
|
if *state == TaskState::Pending && !queue.is_empty() {
|
||||||
|
*state = TaskState::Active;
|
||||||
|
if let Some(task) = queue.current() {
|
||||||
|
claimed_writer.write(TaskClaimed {
|
||||||
|
entity,
|
||||||
|
task: task.clone(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute current task if Active
|
||||||
|
if *state == TaskState::Active {
|
||||||
|
if let Some(current_task) = queue.current_mut() {
|
||||||
|
match current_task {
|
||||||
|
Task::Idle { .. } => {
|
||||||
|
execute_idle(
|
||||||
|
entity,
|
||||||
|
current_task,
|
||||||
|
transform,
|
||||||
|
&mut ambulatory,
|
||||||
|
&tilemap,
|
||||||
|
&mut rng,
|
||||||
|
);
|
||||||
|
// Idle never completes - it cycles forever
|
||||||
|
}
|
||||||
|
Task::GoTo {
|
||||||
|
target,
|
||||||
|
threshold_tiles,
|
||||||
|
} => {
|
||||||
|
let entity_pos = (transform.translation / ITILE_SIZE as f32).as_ivec3();
|
||||||
|
// Chebyshev distance (max of absolute differences)
|
||||||
|
let distance = (entity_pos.x - target.x)
|
||||||
|
.abs()
|
||||||
|
.max((entity_pos.y - target.y).abs());
|
||||||
|
|
||||||
|
if distance <= *threshold_tiles || !tilemap.is_standable(*target) {
|
||||||
|
*state = TaskState::Completed;
|
||||||
|
} else {
|
||||||
|
// Set target - pathfinding handles the rest
|
||||||
|
ambulatory.target = Some(Vec3::new(
|
||||||
|
target.x as f32,
|
||||||
|
target.y as f32,
|
||||||
|
transform.translation.z,
|
||||||
|
));
|
||||||
|
ambulatory.current_path = None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Stage 3 placeholders - log and fail for now
|
||||||
|
Task::ChopTree { .. } | Task::HaulObject { .. } | Task::DropHauled { .. } => {
|
||||||
|
debug!(
|
||||||
|
"Task {} unimplemented for entity {:?}",
|
||||||
|
current_task.name(),
|
||||||
|
entity
|
||||||
|
);
|
||||||
|
*state = TaskState::Failed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle task completion/failure
|
||||||
|
if *state == TaskState::Completed || *state == TaskState::Failed {
|
||||||
|
if let Some(completed_task) = queue.pop() {
|
||||||
|
if *state == TaskState::Completed {
|
||||||
|
completed_writer.write(TaskCompleted {
|
||||||
|
entity,
|
||||||
|
task: completed_task.clone(),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
failed_writer.write(TaskFailed {
|
||||||
|
entity,
|
||||||
|
task: completed_task.clone(),
|
||||||
|
reason: "unimplemented",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// If terminal task completed, ensure Idle is queued
|
||||||
|
if completed_task.is_terminal() && queue.is_empty() {
|
||||||
|
let origin = (transform.translation / ITILE_SIZE as f32).as_ivec3();
|
||||||
|
queue.push(Task::Idle {
|
||||||
|
target: origin,
|
||||||
|
wander_radius: 10,
|
||||||
|
origin,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*state = TaskState::Pending;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
//! 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();
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set ambulatory target - pathfinding system will compute path
|
||||||
|
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.
|
||||||
|
fn pick_wander_target(
|
||||||
|
origin: &IVec3,
|
||||||
|
radius: i32,
|
||||||
|
tilemap: &TileMap,
|
||||||
|
rng: &mut WyRand,
|
||||||
|
) -> Option<IVec3> {
|
||||||
|
let mut candidates = smallvec::SmallVec::<[IVec3; 16]>::new();
|
||||||
|
|
||||||
|
// Collect valid tiles within radius
|
||||||
|
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) {
|
||||||
|
candidates.push(candidate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if candidates.is_empty() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let idx = rng.random_range(0..candidates.len());
|
||||||
|
Some(candidates[idx])
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
pub mod components;
|
||||||
|
pub mod events;
|
||||||
|
pub mod executor;
|
||||||
|
pub mod idle;
|
||||||
|
|
||||||
|
pub use components::{Task, TaskQueue, TaskState};
|
||||||
|
pub use events::{TaskBlocked, TaskClaimed, TaskCompleted, TaskDropped, TaskFailed};
|
||||||
|
pub use executor::task_executor_system;
|
||||||
@@ -6,6 +6,7 @@ use crate::entities::{
|
|||||||
cargo::any_hauling,
|
cargo::any_hauling,
|
||||||
item::{initialize_item_rotation_state, item_tile_management_system, ItemRotationTimer},
|
item::{initialize_item_rotation_state, item_tile_management_system, ItemRotationTimer},
|
||||||
shared_systems::digging::dig_system,
|
shared_systems::digging::dig_system,
|
||||||
|
tasks::{TaskClaimed, TaskCompleted, TaskFailed, TaskDropped, TaskBlocked},
|
||||||
};
|
};
|
||||||
use crate::game::SpawnDelay;
|
use crate::game::SpawnDelay;
|
||||||
|
|
||||||
@@ -47,6 +48,11 @@ fn main() {
|
|||||||
.insert_resource(ClearColor(Color::srgb(0., 0., 0.)))
|
.insert_resource(ClearColor(Color::srgb(0., 0., 0.)))
|
||||||
.add_plugins(world::WorldPlugin)
|
.add_plugins(world::WorldPlugin)
|
||||||
.add_plugins(entities::pathfinding::PathfindingPlugin)
|
.add_plugins(entities::pathfinding::PathfindingPlugin)
|
||||||
|
.add_message::<TaskClaimed>()
|
||||||
|
.add_message::<TaskCompleted>()
|
||||||
|
.add_message::<TaskFailed>()
|
||||||
|
.add_message::<TaskDropped>()
|
||||||
|
.add_message::<TaskBlocked>()
|
||||||
.add_systems(
|
.add_systems(
|
||||||
Startup,
|
Startup,
|
||||||
(camera::spawn_panning_camera, cursor::setup_cursor),
|
(camera::spawn_panning_camera, cursor::setup_cursor),
|
||||||
|
|||||||
Reference in New Issue
Block a user