From b7b7af556016021aa4fc46537012af80ddaaa9ef Mon Sep 17 00:00:00 2001 From: popertots Date: Sat, 21 Mar 2026 21:39:52 +0000 Subject: [PATCH] inventory: linter fixes - saturating_add, SmallVec<4>, new_default, drop PIG_SLOTS --- src/entities/item/inventory/constants.rs | 1 - src/entities/item/inventory/container.rs | 2 +- src/entities/item/inventory/ops.rs | 2 +- src/entities/item/inventory/personal.rs | 73 ++---------------------- src/entities/item/inventory/systems.rs | 31 +--------- src/entities/livestock/pig.rs | 1 + 6 files changed, 11 insertions(+), 99 deletions(-) diff --git a/src/entities/item/inventory/constants.rs b/src/entities/item/inventory/constants.rs index 56613fa..971becb 100644 --- a/src/entities/item/inventory/constants.rs +++ b/src/entities/item/inventory/constants.rs @@ -28,4 +28,3 @@ pub const SIZE_HUGE: u8 = 8; // chest, anvil, large crate /// 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 diff --git a/src/entities/item/inventory/container.rs b/src/entities/item/inventory/container.rs index 7677597..8404201 100644 --- a/src/entities/item/inventory/container.rs +++ b/src/entities/item/inventory/container.rs @@ -41,7 +41,7 @@ pub struct Container { 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]>, + pub contents: SmallVec<[Entity; 4]>, /// Cached total weight of contents (updated on add/remove, not per-tick). pub current_weight: u32, } diff --git a/src/entities/item/inventory/ops.rs b/src/entities/item/inventory/ops.rs index 51d10b9..aff7eb9 100644 --- a/src/entities/item/inventory/ops.rs +++ b/src/entities/item/inventory/ops.rs @@ -59,7 +59,7 @@ pub fn can_add( if current_count >= max_count { return AddResult::TooMany; } - if carrier_current_weight + item_weight > carrier_max_weight { + if carrier_current_weight.saturating_add(item_weight) > carrier_max_weight { return AddResult::TooHeavy; } AddResult::Ok diff --git a/src/entities/item/inventory/personal.rs b/src/entities/item/inventory/personal.rs index b2dbc91..c7cec0f 100644 --- a/src/entities/item/inventory/personal.rs +++ b/src/entities/item/inventory/personal.rs @@ -1,32 +1,4 @@ -//! 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(). +//! PersonalInventory — slots, strength-based carry weight, encumbrance tracking. use bevy::prelude::*; use smallvec::SmallVec; @@ -34,7 +6,6 @@ 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, @@ -43,15 +14,10 @@ pub enum LifeStage { } 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::Child => adult_slots / 2, LifeStage::Baby => { if adult_slots >= 2 { 1 @@ -65,32 +31,15 @@ impl LifeStage { #[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 (0–255). 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(), @@ -102,15 +51,16 @@ impl PersonalInventory { } } - /// Maximum carry weight based on strength and held container bonuses. + pub fn new_default(adult_slots: u8, stage: LifeStage) -> Self { + Self::new(adult_slots, stage, DEFAULT_STRENGTH) + } + 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, @@ -134,8 +84,6 @@ impl PersonalInventory { 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); @@ -146,8 +94,6 @@ impl PersonalInventory { } } - /// 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() @@ -161,13 +107,6 @@ impl PersonalInventory { 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; } diff --git a/src/entities/item/inventory/systems.rs b/src/entities/item/inventory/systems.rs index b9275db..3ba678e 100644 --- a/src/entities/item/inventory/systems.rs +++ b/src/entities/item/inventory/systems.rs @@ -1,19 +1,5 @@ -//! 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. +//! Encumbrance system. Fires `InventoryChangedEvent` after any inventory mutation to +//! trigger walk_speed recalculation without per-tick iteration over all carriers. use bevy::prelude::*; use rustc_hash::FxHashSet; @@ -23,26 +9,16 @@ 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, mut carriers: Query<(&mut PersonalInventory, &mut Ambulatory)>, containers: Query<&Container>, ) { - // Deduplicate — multiple adds in one frame should only trigger one recalc let changed: FxHashSet = events.read().map(|e| e.carrier).collect(); if changed.is_empty() { return; @@ -53,7 +29,6 @@ pub fn update_encumbrance( continue; }; - // Rebuild container bonus from direct slots only (not recursive) let bonus: u32 = inventory .slots .iter() @@ -72,8 +47,6 @@ pub fn update_encumbrance( } else { ambulatory.base_walk_speed }; - // TODO: additional encumbrance debuffs (combat penalty, stamina drain, - // job speed modifier) when gameplay systems exist } } } diff --git a/src/entities/livestock/pig.rs b/src/entities/livestock/pig.rs index b3696c5..285d75b 100644 --- a/src/entities/livestock/pig.rs +++ b/src/entities/livestock/pig.rs @@ -14,6 +14,7 @@ pub struct Pig { sprite: Sprite, transform: Transform, visibility: Visibility, + // Pigs have no inventory — PersonalInventory is not attached to this entity. } impl Pig {