From d9319bfd5ca398b527cf2445669fe1541e6466c9 Mon Sep 17 00:00:00 2001 From: hheik <4469778+hheik@users.noreply.github.com> Date: Tue, 8 Apr 2025 23:41:13 +0300 Subject: [PATCH] Working gather-pickup-store work system --- Cargo.lock | 1 + Cargo.toml | 1 + src/debug.rs | 2 +- src/game.rs | 16 +- src/game/item.rs | 149 ++++++++++++++--- src/game/prefab.rs | 2 +- src/game/systems.rs | 2 + src/game/systems/creature.rs | 24 ++- src/game/systems/item.rs | 27 +++ src/game/systems/worker.rs | 316 ++++++++++++++++++++++++++++------- src/game/work.rs | 26 ++- src/util.rs | 6 +- src/util/types.rs | 1 + 13 files changed, 471 insertions(+), 102 deletions(-) create mode 100644 src/game/systems/item.rs diff --git a/Cargo.lock b/Cargo.lock index 128a118..7ac3dd7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2424,6 +2424,7 @@ dependencies = [ "bevy_mod_debugdump", "bevy_prototype_lyon", "bevy_rapier2d", + "fastrand", "num-traits", ] diff --git a/Cargo.toml b/Cargo.toml index 40bf765..3bff232 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,6 +11,7 @@ bevy-inspector-egui = "0.28.1" bevy_mod_debugdump = "0.12.1" bevy_prototype_lyon = "0.13.0" bevy_rapier2d = "0.28.0" +fastrand = "2.3.0" num-traits = "0.2.19" # Enable a small amount of optimization in debug mode diff --git a/src/debug.rs b/src/debug.rs index 98a3fb2..396d2f6 100644 --- a/src/debug.rs +++ b/src/debug.rs @@ -19,7 +19,7 @@ impl Plugin for DebugPlugin { app.add_plugins(( bevy_inspector_egui::quick::WorldInspectorPlugin::new().run_if(is_debug_enabled), - bevy_rapier2d::prelude::RapierDebugRenderPlugin::default(), + // bevy_rapier2d::prelude::RapierDebugRenderPlugin::default(), )); app.register_type::() diff --git a/src/game.rs b/src/game.rs index 4a1842a..10aa043 100644 --- a/src/game.rs +++ b/src/game.rs @@ -26,14 +26,22 @@ pub fn init(app: &mut App) { .register_type::() .register_type::() .register_type::() + .add_event::() + .add_event::() .add_systems(Startup, systems::setup_2d) .add_systems( Update, ( - systems::demo_2d, - systems::work_select, - systems::worker_movement, - ), + ( + systems::demo_2d, + systems::work_select, + systems::worker_movement, + systems::spawn_items, + ), + (systems::do_work,), + (systems::apply_task_result,), + ) + .chain(), ) .add_systems(PostUpdate, item::update_item_sprite) .add_systems(Update, (systems::draw_job_targets).in_set(debug::DebugSet)); diff --git a/src/game/item.rs b/src/game/item.rs index 96ac0a4..208f87a 100644 --- a/src/game/item.rs +++ b/src/game/item.rs @@ -1,5 +1,7 @@ use bevy::prelude::*; +use crate::util::Kilograms; + #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Component, Reflect)] #[reflect(Component)] #[require(Sprite)] @@ -7,44 +9,78 @@ pub enum Item { Wood, } +impl Item { + // TODO: implement + fn stack_max_size(&self) -> Option { + match self { + _ => Some(100), + } + } + + // TODO: implement + fn weight(&self) -> Kilograms { + match self { + Self::Wood => 1.0, // Paper birch, height 30cm, diameter 18cm ~= 6 kg, chopped in 6 pieces ~= 1kg + } + } +} + #[derive(Copy, Clone, Debug, Reflect)] pub struct ItemStack { pub item: Item, pub count: u32, } -impl Default for ItemStack { - fn default() -> Self { - Self { - item: Item::Wood, - count: 1, - } +impl From for ItemStack { + fn from(item: Item) -> Self { + Self { item, count: 1 } } } #[derive(Clone, Debug, Default, Component, Reflect)] #[reflect(Component)] -pub struct ItemSource(pub ItemStack); +pub struct ItemSource { + /// What items are spawned when gathered + pub drops: Vec, + /// How many times this entity can be gathered before it despawns + pub gather_limit: Option, +} #[derive(Clone, Debug, Default, Component, Reflect)] #[reflect(Component)] pub struct Inventory { pub capacity: Option, + // TODO: Create a "Reserved" item stack type, that indicates that someone has reserved a slot + // for some new item type in the inventory pub items: Vec, } -pub enum InsertResult { - /// Combine to an existing stack. Contains (`index`, `new_count`) - Combine(usize, u32), - /// No existing stackable stacks, push a new one. - Push(ItemStack), +#[derive(Clone, Debug)] +pub enum InventoryError { + IndexNotFound { + index: usize, + inventory: Inventory, + info: Option, + }, } +/// Result for trying to insert an item to inventory. Should be used and discarded immediately, as +/// it might not be valid after mutating inventory. In future, It could contain a hash value for +/// the valid inventory state that it aplies to! +#[derive(Debug)] +pub enum InsertResult { + /// Combine to an existing stack. Contains (`index`, `new_count`) + Combine { index: usize, new_count: u32 }, + /// No existing stackable stacks, push a new one. + Push { stack: ItemStack }, +} + +#[derive(Debug)] pub enum RemoveResult { /// The stack will get depleted, so it can be removed. Contains the `index` of empty stack. - Empty(usize), + Empty { index: usize }, /// The stack will get only partially depleted. Contains (`index`, `new_count`) - Partial(usize, u32), + Partial { index: usize, new_count: u32 }, } impl Inventory { @@ -55,26 +91,30 @@ impl Inventory { } } - pub fn is_empty(&self) -> bool { - self.items.iter().all(|stack| stack.count == 0) + pub fn weight(&self) -> Kilograms { + self.items + .iter() + .map(|stack| stack.item.weight() * stack.count as f32) + .sum() } - pub fn try_insert(&self, item_stack: &ItemStack) -> Option { + pub fn try_insert(&self, item_stack: ItemStack) -> Option { match self .items .iter() .enumerate() .find(|(_, stack)| stack.item == item_stack.item) { - Some((index, stack)) => { - Some(InsertResult::Combine(index, stack.count + item_stack.count)) - } + Some((index, stack)) => Some(InsertResult::Combine { + index, + new_count: stack.count + item_stack.count, + }), None => { if self .capacity .map_or(true, |capacity| self.items.len() < capacity) { - Some(InsertResult::Push(*item_stack)) + Some(InsertResult::Push { stack: item_stack }) } else { None } @@ -82,7 +122,7 @@ impl Inventory { } } - pub fn try_remove(&self, item_stack: &ItemStack) -> Option { + pub fn try_remove(&self, item_stack: ItemStack) -> Option { match self .items .iter() @@ -92,9 +132,9 @@ impl Inventory { Some((index, stack)) => match stack.count.checked_sub(item_stack.count) { Some(new_count) => { if new_count == 0 { - Some(RemoveResult::Empty(index)) + Some(RemoveResult::Empty { index }) } else { - Some(RemoveResult::Partial(index, new_count)) + Some(RemoveResult::Partial { index, new_count }) } } // Not enough items @@ -108,7 +148,7 @@ impl Inventory { pub fn try_transfer( &self, to: &Self, - item_stack: &ItemStack, + item_stack: ItemStack, ) -> Option<(RemoveResult, InsertResult)> { match (self.try_remove(item_stack), to.try_insert(item_stack)) { (Some(remove_result), Some(insert_result)) => Some((remove_result, insert_result)), @@ -116,6 +156,58 @@ impl Inventory { } } + pub fn apply_remove(&mut self, remove_result: RemoveResult) -> Result<(), InventoryError> { + match remove_result { + RemoveResult::Empty { index } => { + if index >= self.items.len() { + Err(InventoryError::IndexNotFound { + index, + inventory: self.clone(), + info: Some("RemoveResult::Empty".to_string()), + }) + } else { + self.items.remove(index); + self.sanitize(); + Ok(()) + } + } + RemoveResult::Partial { index, new_count } => match self.items.get_mut(index) { + Some(stack) => { + stack.count = new_count; + self.sanitize(); + Ok(()) + } + None => Err(InventoryError::IndexNotFound { + index, + inventory: self.clone(), + info: Some("RemoveResult::Partial".to_string()), + }), + }, + } + } + + pub fn apply_insert(&mut self, insert_result: InsertResult) -> Result<(), InventoryError> { + match insert_result { + InsertResult::Push { stack } => { + self.items.push(stack); + self.sanitize(); + Ok(()) + } + InsertResult::Combine { index, new_count } => match self.items.get_mut(index) { + Some(stack) => { + stack.count = new_count; + self.sanitize(); + Ok(()) + } + None => Err(InventoryError::IndexNotFound { + index, + inventory: self.clone(), + info: Some("InsertResult::Combine".to_string()), + }), + }, + } + } + pub fn sanitize(&mut self) { if let Some(capacity) = self.capacity { self.items.truncate(capacity); @@ -139,3 +231,10 @@ pub fn update_item_sprite( }); } } + +#[derive(Event, Clone, Debug)] +pub struct SpawnItem { + pub item: Item, + pub to: Vec2, + pub velocity: Vec2, +} diff --git a/src/game/prefab.rs b/src/game/prefab.rs index ab1bad4..d60e977 100644 --- a/src/game/prefab.rs +++ b/src/game/prefab.rs @@ -32,7 +32,7 @@ pub struct Glorb; ..default() }), SpriteLoader(|| SpriteLoader::from("sprites/tree.png")), - ItemSource(|| ItemSource(ItemStack { item: Item::Wood, count: 1 })), + ItemSource(|| ItemSource { drops: vec![ItemStack { item: Item::Wood, count: 1 }], gather_limit: None }), WorkDuration(|| WorkDuration(5.0)) )] pub struct Tree; diff --git a/src/game/systems.rs b/src/game/systems.rs index 2946519..d347879 100644 --- a/src/game/systems.rs +++ b/src/game/systems.rs @@ -1,7 +1,9 @@ mod creature; mod demo; +mod item; mod worker; pub use creature::*; pub use demo::*; +pub use item::*; pub use worker::*; diff --git a/src/game/systems/creature.rs b/src/game/systems/creature.rs index 47cfcc8..8753873 100644 --- a/src/game/systems/creature.rs +++ b/src/game/systems/creature.rs @@ -1,21 +1,35 @@ use bevy::prelude::*; -use crate::game::{creature::Mover, work::Worker}; +use crate::{ + game::{creature::Mover, item::Inventory, work::Worker}, + util::{inverse_lerp, lerp}, +}; pub fn worker_movement( - mut worker_query: Query<(Entity, &Mover, &Worker, &mut Transform)>, + mut worker_query: Query<(Entity, &Mover, &Worker, &mut Transform, Option<&Inventory>)>, global_query: Query<&GlobalTransform>, time: Res