inv update

This commit is contained in:
2026-03-21 21:27:26 +00:00
parent 40b5d8ce95
commit 2f9360c8bc
13 changed files with 567 additions and 58 deletions
-49
View File
@@ -1,49 +0,0 @@
use bevy::prelude::*;
#[derive(Component, Debug)]
pub struct Container {
pub max_weight: u32,
pub max_items: Option<u32>,
pub current_weight: u32,
pub contents: Vec<Entity>, // TODO - move away from Entity
pub is_open: bool,
}
impl Container {
pub fn new(max_weight: u32, max_items: Option<u32>) -> Self {
Self {
max_weight,
max_items,
current_weight: 0,
contents: Vec::new(),
is_open: true,
}
}
pub fn can_fit(&self, weight: u32) -> bool {
self.current_weight + weight <= self.max_weight
&& self
.max_items
.map_or(true, |max| self.contents.len() < max as usize)
}
pub fn add_item(&mut self, item_entity: Entity, weight: u32) -> bool {
if self.can_fit(weight) {
self.contents.push(item_entity);
self.current_weight += weight;
true
} else {
false
}
}
pub fn remove_item(&mut self, item_entity: Entity, weight: u32) -> bool {
if let Some(pos) = self.contents.iter().position(|&e| e == item_entity) {
self.contents.remove(pos);
self.current_weight = self.current_weight.saturating_sub(weight);
true
} else {
false
}
}
}
+31
View File
@@ -0,0 +1,31 @@
/// Baseline carry weight for an entity with strength 0 (kg, arbitrary units).
/// Actual max = BASE_CARRY_WEIGHT + strength * STRENGTH_CARRY_MULTIPLIER.
pub const BASE_CARRY_WEIGHT: u32 = 20;
/// Carry weight added per point of strength.
pub const STRENGTH_CARRY_MULTIPLIER: u32 = 5;
/// Default strength value for a newly spawned entity.
/// Tune per entity type by setting PersonalInventory.strength at spawn.
pub const DEFAULT_STRENGTH: u8 = 5;
/// walk_speed multiplier applied when carry weight exceeds max carry weight.
/// 0.5 = half speed when over-encumbered (Skyrim-style).
pub const ENCUMBERED_SPEED_MULTIPLIER: f32 = 0.5;
/// Item size values. Every item has a size: u8.
/// Containers use max_item_size: u8 to gate what they accept.
pub const SIZE_TINY: u8 = 1; // coin, seed, ring, dice
pub const SIZE_SMALL: u8 = 2; // apple, knife, pouch, small book
pub const SIZE_MEDIUM: u8 = 4; // sword, boot, helmet, loaf of bread
pub const SIZE_LARGE: u8 = 6; // shield, backpack (empty), large tool
pub const SIZE_HUGE: u8 = 8; // chest, anvil, large crate
/// Personal inventory slot counts per life stage, applied at spawn.
/// Baby: max(0, 1) unless species adult_slots == 1, then 0.
/// Child: floor(adult_slots / 2), minimum 0.
/// Adult: species-defined value.
/// See PersonalInventory::slots_for_age.
pub const DORF_ADULT_SLOTS: u8 = 8;
pub const RABBIT_ADULT_SLOTS: u8 = 1;
pub const PIG_ADULT_SLOTS: u8 = 0; // pigs carry nothing
+143
View File
@@ -0,0 +1,143 @@
//! Container — a carryable or world-placed storage object.
//!
//! # Design
//! Containers enforce count and size limits. Weight is enforced at the carrier
//! (PersonalInventory) level, not here — a container does not know who carries it.
//!
//! # carry_weight_bonus
//! When a Container is directly held in a PersonalInventory slot (not nested inside
//! another container), its carry_weight_bonus is added to the carrier's effective
//! max weight. Only direct-hold containers contribute — nested containers do not.
//! The encumbrance system queries for this during weight recalculation.
//!
//! # Nesting
//! Containers can be placed inside other containers (contents: Vec<Entity> may
//! contain entities that themselves have Container components). Nesting depth is
//! physically bounded by size — a coin purse (SIZE_SMALL) cannot hold a chest (SIZE_HUGE).
//!
//! # TODO
//! contents currently stores Entity. Once a stable non-generational ID scheme
//! exists, migrate to that. Tracked in container.rs TODO comment.
use bevy::prelude::*;
use smallvec::SmallVec;
use super::ops::AddResult;
use crate::entities::item::inventory::ops::can_add;
#[derive(Component, Debug)]
pub struct Container {
/// Maximum number of items this container can hold.
pub max_count: u32,
/// Maximum size of a single item this container accepts.
/// Prevents putting a chest inside a coin purse.
pub max_item_size: u8,
/// This container's own size as an item (for fitting into other containers).
pub size: u8,
/// Bonus carry weight (kg) added to the direct carrier when this container
/// is held in a PersonalInventory slot. Does NOT stack when nested.
pub carry_weight_bonus: u32,
/// Whether this container can be opened/closed. Closed containers reject adds.
pub is_open: bool,
/// Items held. May include entities that themselves have Container components.
/// TODO: migrate away from Entity when stable ID scheme exists.
pub contents: SmallVec<[Entity; 8]>,
/// Cached total weight of contents (updated on add/remove, not per-tick).
pub current_weight: u32,
}
impl Container {
/// Construct a new container.
///
/// # Parameters
/// - `max_count`: item slot limit
/// - `max_item_size`: largest item size accepted (use SIZE_* constants)
/// - `size`: this container's own size as an item
/// - `carry_weight_bonus`: kg added to direct carrier's max weight
pub fn new(max_count: u32, max_item_size: u8, size: u8, carry_weight_bonus: u32) -> Self {
Self {
max_count,
max_item_size,
size,
carry_weight_bonus,
is_open: true,
contents: SmallVec::new(),
current_weight: 0,
}
}
/// Attempt to add an item.
///
/// `carrier_current_weight` and `carrier_max_weight` are the top-level
/// PersonalInventory's values — weight is enforced at the carrier level.
/// Pass u32::MAX for both if this container is a world object (chest on floor)
/// with no carrier weight limit.
pub fn try_add(
&mut self,
item_entity: Entity,
item_weight: u32,
item_size: u8,
carrier_current_weight: u32,
carrier_max_weight: u32,
) -> AddResult {
let result = can_add(
self.is_open,
self.contents.len(),
self.max_count as usize,
item_size,
self.max_item_size,
carrier_current_weight,
carrier_max_weight,
item_weight,
);
if result.is_ok() {
self.contents.push(item_entity);
self.current_weight = self.current_weight.saturating_add(item_weight);
}
result
}
pub fn remove(&mut self, item_entity: Entity, item_weight: u32) -> bool {
if let Some(pos) = self.contents.iter().position(|&e| e == item_entity) {
self.contents.swap_remove(pos);
self.current_weight = self.current_weight.saturating_sub(item_weight);
true
} else {
false
}
}
pub fn contains(&self, item_entity: Entity) -> bool {
self.contents.contains(&item_entity)
}
pub fn count(&self) -> usize {
self.contents.len()
}
pub fn is_full(&self) -> bool {
self.contents.len() >= self.max_count as usize
}
}
/// Common container presets. Use these rather than constructing manually
/// to keep sizes and bonuses consistent across the codebase.
impl Container {
/// Small coin purse — coins, seeds, dice, rings only.
pub fn coin_purse() -> Self {
use crate::entities::item::inventory::constants::*;
Self::new(20, SIZE_TINY, SIZE_SMALL, 0)
}
/// Leather backpack — most items fit, carried on back.
pub fn backpack() -> Self {
use crate::entities::item::inventory::constants::*;
Self::new(12, SIZE_LARGE, SIZE_LARGE, 20)
}
/// Wooden chest — large capacity, large items fit, significant weight bonus.
pub fn wooden_chest() -> Self {
use crate::entities::item::inventory::constants::*;
Self::new(40, SIZE_HUGE, SIZE_HUGE, 40)
}
}
+11
View File
@@ -0,0 +1,11 @@
pub mod constants;
pub mod container;
pub mod ops;
pub mod personal;
pub mod systems;
pub use constants::*;
pub use container::Container;
pub use ops::{best_slot, can_add, AddResult, SwapOp};
pub use personal::{LifeStage, PersonalInventory};
pub use systems::{update_encumbrance, InventoryChangedEvent};
+103
View File
@@ -0,0 +1,103 @@
use bevy::prelude::*;
/// Result of an attempt to add an item to an inventory slot.
/// The entity/player sees only the variant name — no internal details exposed.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AddResult {
/// Item added successfully.
Ok,
/// Total weight would exceed the carrier's max. Item not added.
TooHeavy,
/// No free slots remain. Item not added.
TooMany,
/// Item's size exceeds this container's max_item_size. Item not added.
TooBig,
/// Container is closed. Item not added.
Closed,
}
impl AddResult {
pub fn is_ok(self) -> bool {
self == AddResult::Ok
}
/// Human-readable reason for UI/log use. Returns None on Ok.
pub fn reason(self) -> Option<&'static str> {
match self {
AddResult::Ok => None,
AddResult::TooHeavy => Some("too heavy to carry"),
AddResult::TooMany => Some("not enough space"),
AddResult::TooBig => Some("item too large to fit"),
AddResult::Closed => Some("container is closed"),
}
}
}
/// Check whether an item can be added without modifying any state.
/// Used by both PersonalInventory and Container before committing.
///
/// `carrier_current_weight` — total weight currently carried by the top-level carrier.
/// `carrier_max_weight` — max weight the carrier can hold.
/// Both are passed in so this fn has no ECS access and is testable in isolation.
#[inline]
pub fn can_add(
is_open: bool,
current_count: usize,
max_count: usize,
item_size: u8,
max_item_size: u8,
carrier_current_weight: u32,
carrier_max_weight: u32,
item_weight: u32,
) -> AddResult {
if !is_open {
return AddResult::Closed;
}
if item_size > max_item_size {
return AddResult::TooBig;
}
if current_count >= max_count {
return AddResult::TooMany;
}
if carrier_current_weight + item_weight > carrier_max_weight {
return AddResult::TooHeavy;
}
AddResult::Ok
}
/// Represents a single item movement needed during reorganisation.
/// Used by the future reorganise task — defined now so the interface is stable.
///
/// The task system executes these one at a time with a per-swap delay based on
/// the entity's intelligence stat (smarter entity = shorter delay per swap).
#[derive(Debug, Clone)]
pub struct SwapOp {
/// Entity being moved.
pub item: Entity,
/// Source container/inventory entity (None = ground).
pub from: Option<Entity>,
/// Destination container/inventory entity (None = ground/drop).
pub to: Option<Entity>,
}
/// Find the best destination entity (container or personal inventory) for an item
/// given a list of candidate containers sorted by preference.
///
/// "Best" = smallest max_item_size that still fits the item, i.e. most specific
/// container. A coin goes into the coin purse before the backpack.
///
/// Returns None if no candidate can accept the item.
///
/// # Parameters
/// - `candidates`: slice of (container_entity, max_item_size, current_count,
/// max_count, is_open) tuples, already filtered for weight.
/// - `item_size`: size of the item to place.
pub fn best_slot(candidates: &[(Entity, u8, usize, usize, bool)], item_size: u8) -> Option<Entity> {
candidates
.iter()
.filter(|(_, max_size, count, max_count, open)| {
*open && item_size <= *max_size && count < max_count
})
.min_by_key(|(_, max_size, _, _, _)| *max_size)
.map(|(entity, _, _, _, _)| *entity)
}
+174
View File
@@ -0,0 +1,174 @@
//! PersonalInventory — the inventory of a living entity.
//!
//! # Design
//! Every entity that can carry items gets a PersonalInventory. Slot count is
//! species- and age-defined. Weight limit is strength-based.
//!
//! # Weight limit
//! max_carry_weight = BASE_CARRY_WEIGHT + strength * STRENGTH_CARRY_MULTIPLIER
//! + sum(carry_weight_bonus for each Container directly in slots)
//!
//! The bonus from held containers is re-evaluated whenever the slot contents
//! change. It is NOT recursive — containers inside containers do not contribute
//! their bonus to the carrier.
//!
//! # Encumbrance
//! When current_weight > max_carry_weight, the entity is encumbered.
//! The update_encumbrance system sets Ambulatory.walk_speed to
//! base_walk_speed * ENCUMBERED_SPEED_MULTIPLIER when over limit.
//!
//! # Item placement
//! Items are always placed in the best available slot at time of pickup —
//! the smallest container whose max_item_size >= item.size with a free slot.
//! No automatic reorganisation occurs. Reorganisation is an explicit task.
//!
//! # Life stage slot counts
//! Adult: species value (e.g. DORF_ADULT_SLOTS = 8)
//! Child: floor(adult / 2), minimum 0
//! Baby: 1 if adult >= 2, else 0
//! See slots_for_age().
use bevy::prelude::*;
use smallvec::SmallVec;
use super::constants::*;
use super::ops::{can_add, AddResult};
/// Life stage of a living entity. Determines slot count at spawn.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum LifeStage {
Baby,
Child,
Adult,
}
impl LifeStage {
/// Compute slot count for this life stage given the species adult slot count.
///
/// Baby: 1 if adult_slots >= 2, else 0
/// Child: floor(adult_slots / 2), minimum 0
/// Adult: adult_slots
pub fn slots_for_age(self, adult_slots: u8) -> u8 {
match self {
LifeStage::Adult => adult_slots,
LifeStage::Child => adult_slots / 2, // integer floor
LifeStage::Baby => {
if adult_slots >= 2 {
1
} else {
0
}
}
}
}
}
#[derive(Component, Debug)]
pub struct PersonalInventory {
/// Items directly held (body slots). May include Container entities.
/// SmallVec<[Entity; 8]> — zero heap allocation for typical dorf (8 slots).
pub slots: SmallVec<[Entity; 8]>,
/// Maximum number of directly-held items (hands, pockets, body slots).
pub max_slots: u8,
/// Strength stat (0255). Determines carry weight capacity.
/// Set at spawn per species/individual. Increase when stats system arrives.
pub strength: u8,
/// Cached total weight of all directly-held items + their contents.
/// Updated on add/remove. NOT recomputed per tick.
pub current_weight: u32,
/// Cached carry weight bonus from Container items in direct slots.
/// Updated whenever slots change. NOT recursive.
pub held_container_bonus: u32,
/// Whether this entity is currently over their weight limit.
/// Set by update_encumbrance system. Read by Ambulatory system.
pub is_encumbered: bool,
}
impl PersonalInventory {
/// Construct for a specific life stage and species.
pub fn new(adult_slots: u8, stage: LifeStage, strength: u8) -> Self {
Self {
slots: SmallVec::new(),
max_slots: stage.slots_for_age(adult_slots),
strength,
current_weight: 0,
held_container_bonus: 0,
is_encumbered: false,
}
}
/// Maximum carry weight based on strength and held container bonuses.
pub fn max_carry_weight(&self) -> u32 {
BASE_CARRY_WEIGHT
+ self.strength as u32 * STRENGTH_CARRY_MULTIPLIER
+ self.held_container_bonus
}
/// Attempt to add an item directly to a personal slot.
/// Use this when no container is available or preferred.
pub fn try_add_direct(
&mut self,
item_entity: Entity,
item_weight: u32,
item_size: u8,
) -> AddResult {
let result = can_add(
true,
self.slots.len(),
self.max_slots as usize,
item_size,
u8::MAX,
self.current_weight,
self.max_carry_weight(),
item_weight,
);
if result.is_ok() {
self.slots.push(item_entity);
self.current_weight = self.current_weight.saturating_add(item_weight);
}
result
}
/// Remove an item from direct slots. Does NOT recurse into containers.
/// Returns true if found and removed.
pub fn remove_direct(&mut self, item_entity: Entity, item_weight: u32) -> bool {
if let Some(pos) = self.slots.iter().position(|&e| e == item_entity) {
self.slots.swap_remove(pos);
self.current_weight = self.current_weight.saturating_sub(item_weight);
true
} else {
false
}
}
/// Whether the entity can accept an item at all (ignoring containers).
/// Quick check before attempting full placement.
pub fn can_accept(&self, item_weight: u32) -> bool {
self.slots.len() < self.max_slots as usize
&& self.current_weight + item_weight <= self.max_carry_weight()
}
pub fn is_full(&self) -> bool {
self.slots.len() >= self.max_slots as usize
}
pub fn slot_count(&self) -> usize {
self.slots.len()
}
/// Rebuild held_container_bonus from current direct slots.
/// Call after any slot change that might add/remove a Container.
/// Caller is responsible for triggering this — not automatic.
///
/// Requires a query to check which slot entities have Container components.
/// Called by the encumbrance system, not inline in add/remove, to avoid
/// needing World/query access at the point of inventory mutation.
pub fn rebuild_container_bonus(&mut self, bonus: u32) {
self.held_container_bonus = bonus;
}
}
+79
View File
@@ -0,0 +1,79 @@
//! Encumbrance system — updates walk_speed based on carry weight.
//!
//! # Event-driven design
//! Rather than iterating all carriers every tick, encumbrance recalculates only
//! for entities that fired an InventoryChangedEvent. At 5000 entities with rare
//! inventory changes, this reduces per-tick work from O(entities) to O(changes).
//!
//! Any code that mutates a PersonalInventory or a Container held by an entity
//! MUST fire InventoryChangedEvent { carrier } after the mutation. The carrier
//! is always the top-level entity with PersonalInventory — not a nested container.
//!
//! # base_walk_speed
//! Ambulatory gains base_walk_speed: f32 — the unmodified speed. Encumbrance
//! writes walk_speed = base_walk_speed * ENCUMBERED_SPEED_MULTIPLIER.
//! Restoring writes walk_speed = base_walk_speed. base_walk_speed is never
//! modified by encumbrance.
use bevy::prelude::*;
use rustc_hash::FxHashSet;
use crate::entities::item::inventory::constants::ENCUMBERED_SPEED_MULTIPLIER;
use crate::entities::item::inventory::personal::PersonalInventory;
use crate::entities::item::Container;
use crate::entities::shared_components::Ambulatory;
/// Fire this event whenever a PersonalInventory or a directly-held Container
/// changes contents or weight. Always use the top-level carrier entity.
///
/// Multiple events for the same carrier in one frame are deduplicated —
/// only one encumbrance recalculation occurs per carrier per frame.
#[derive(Message)]
pub struct InventoryChangedEvent {
pub carrier: Entity,
}
/// Recalculate encumbrance only for carriers that had inventory changes this frame.
///
/// Deduplicates multiple events for the same entity via FxHashSet before querying.
/// Cost: O(unique_changed_carriers) instead of O(all_carriers).
pub fn update_encumbrance(
mut events: MessageReader<InventoryChangedEvent>,
mut carriers: Query<(&mut PersonalInventory, &mut Ambulatory)>,
containers: Query<&Container>,
) {
// Deduplicate — multiple adds in one frame should only trigger one recalc
let changed: FxHashSet<Entity> = events.read().map(|e| e.carrier).collect();
if changed.is_empty() {
return;
}
for carrier_entity in changed {
let Ok((mut inventory, mut ambulatory)) = carriers.get_mut(carrier_entity) else {
continue;
};
// Rebuild container bonus from direct slots only (not recursive)
let bonus: u32 = inventory
.slots
.iter()
.filter_map(|&e| containers.get(e).ok())
.map(|c| c.carry_weight_bonus)
.sum();
inventory.rebuild_container_bonus(bonus);
let was_encumbered = inventory.is_encumbered;
let now_encumbered = inventory.current_weight > inventory.max_carry_weight();
inventory.is_encumbered = now_encumbered;
if now_encumbered != was_encumbered {
ambulatory.walk_speed = if now_encumbered {
ambulatory.base_walk_speed * ENCUMBERED_SPEED_MULTIPLIER
} else {
ambulatory.base_walk_speed
};
// TODO: additional encumbrance debuffs (combat penalty, stamina drain,
// job speed modifier) when gameplay systems exist
}
}
}
+5 -2
View File
@@ -1,8 +1,8 @@
pub mod constants; pub mod constants;
pub mod container;
pub mod core; pub mod core;
pub mod decorations; pub mod decorations;
pub mod drop_table; pub mod drop_table;
pub mod inventory;
pub mod material; pub mod material;
pub mod perishable; pub mod perishable;
pub mod prefabs; pub mod prefabs;
@@ -10,10 +10,13 @@ pub mod quality;
pub mod systems; pub mod systems;
pub mod types; pub mod types;
pub use container::*;
pub use core::*; pub use core::*;
pub use decorations::*; pub use decorations::*;
pub use drop_table::*; pub use drop_table::*;
pub use inventory::{
best_slot, can_add, update_encumbrance, AddResult, Container, InventoryChangedEvent, LifeStage,
PersonalInventory, SwapOp,
};
pub use material::*; pub use material::*;
pub use perishable::*; pub use perishable::*;
pub use prefabs::*; pub use prefabs::*;
+5 -2
View File
@@ -18,10 +18,13 @@ pub struct Pig {
impl Pig { impl Pig {
pub fn new(asset_server: &Res<AssetServer>, position: Vec3) -> Self { pub fn new(asset_server: &Res<AssetServer>, position: Vec3) -> Self {
let walk_speed = 25.;
let run_speed = 6.;
Pig { Pig {
ambulatory: Ambulatory { ambulatory: Ambulatory {
walk_speed: 25., walk_speed,
run_speed: 6., base_walk_speed: walk_speed,
run_speed,
target: None, target: None,
current_path: None, current_path: None,
path_index: 0, path_index: 0,
+5 -2
View File
@@ -20,10 +20,13 @@ pub struct Rabbit {
impl Rabbit { impl Rabbit {
pub fn new(asset_server: &Res<AssetServer>, position: Vec3) -> Self { pub fn new(asset_server: &Res<AssetServer>, position: Vec3) -> Self {
let walk_speed = 1.;
let run_speed = 6.;
Rabbit { Rabbit {
ambulatory: Ambulatory { ambulatory: Ambulatory {
walk_speed: 1., walk_speed,
run_speed: 6., base_walk_speed: walk_speed,
run_speed,
target: None, target: None,
current_path: None, current_path: None,
path_index: 0, path_index: 0,
+5 -2
View File
@@ -18,10 +18,13 @@ pub struct Dorf {
impl Dorf { impl Dorf {
pub fn new(asset_server: &Res<AssetServer>, position: Vec3) -> Self { pub fn new(asset_server: &Res<AssetServer>, position: Vec3) -> Self {
let walk_speed = 5.;
let run_speed = 6.;
Dorf { Dorf {
ambulatory: Ambulatory { ambulatory: Ambulatory {
walk_speed: 5., walk_speed,
run_speed: 6., base_walk_speed: walk_speed,
run_speed,
target: None, target: None,
current_path: None, current_path: None,
path_index: 0, path_index: 0,
@@ -4,6 +4,7 @@ use bevy::tasks::Task;
#[derive(Component)] #[derive(Component)]
pub struct Ambulatory { pub struct Ambulatory {
pub walk_speed: f32, pub walk_speed: f32,
pub base_walk_speed: f32,
pub run_speed: f32, pub run_speed: f32,
pub current_path: Option<Vec<Vec3>>, pub current_path: Option<Vec<Vec3>>,
pub path_index: usize, pub path_index: usize,
@@ -18,6 +19,7 @@ impl Default for Ambulatory {
fn default() -> Self { fn default() -> Self {
Self { Self {
walk_speed: 1.0, walk_speed: 1.0,
base_walk_speed: 1.0,
run_speed: 2.0, run_speed: 2.0,
current_path: None, current_path: None,
path_index: 0, path_index: 0,
+4 -1
View File
@@ -63,6 +63,7 @@ use crate::constants::{
ITILE_SIZE, PATHFINDER_HIERARCHICAL_THRESHOLD_CHUNKS, PATHFINDER_MAX_NODES, ITILE_SIZE, PATHFINDER_HIERARCHICAL_THRESHOLD_CHUNKS, PATHFINDER_MAX_NODES,
PATHFINDER_PROVISIONAL_NODE_LIMIT, PIXEL_RATIO, TILE_SIZE, PATHFINDER_PROVISIONAL_NODE_LIMIT, PIXEL_RATIO, TILE_SIZE,
}; };
use crate::entities::item::inventory::{update_encumbrance, InventoryChangedEvent};
use crate::entities::shared_systems::constants::{ use crate::entities::shared_systems::constants::{
CONVOY_DOT_THRESHOLD, ES_DIRECTION_THRESHOLD, HEAD_ON_DOT_THRESHOLD, OCCUPANCY_CROWD_THRESHOLD, CONVOY_DOT_THRESHOLD, ES_DIRECTION_THRESHOLD, HEAD_ON_DOT_THRESHOLD, OCCUPANCY_CROWD_THRESHOLD,
PATHFINDER_DIRTY_LOOKAHEAD, PATHFINDER_VALIDATE_STEPS, PATHFINDER_VALIDATION_COOLDOWN, PATHFINDER_DIRTY_LOOKAHEAD, PATHFINDER_VALIDATE_STEPS, PATHFINDER_VALIDATION_COOLDOWN,
@@ -218,7 +219,8 @@ pub struct PathfindingPlugin;
impl Plugin for PathfindingPlugin { impl Plugin for PathfindingPlugin {
fn build(&self, app: &mut App) { fn build(&self, app: &mut App) {
app.insert_resource(PathfindingBenchmark::new(100)) app.add_message::<InventoryChangedEvent>()
.insert_resource(PathfindingBenchmark::new(100))
.insert_resource(crate::entities::shared_components::CompletedPaths::default()) .insert_resource(crate::entities::shared_components::CompletedPaths::default())
.insert_resource(crate::entities::shared_components::PathRequestCounter::default()) .insert_resource(crate::entities::shared_components::PathRequestCounter::default())
.insert_resource(PathRequestQueue::default()) .insert_resource(PathRequestQueue::default())
@@ -228,6 +230,7 @@ impl Plugin for PathfindingPlugin {
FixedUpdate, FixedUpdate,
( (
rebuild_tile_occupancy, rebuild_tile_occupancy,
update_encumbrance,
collect_pathfinding_dirty_chunks, collect_pathfinding_dirty_chunks,
invalidate_paths_on_tile_change, invalidate_paths_on_tile_change,
prepare_paths, prepare_paths,