oxygengine_overworld/components/
inventory.rs1use crate::resources::market::*;
2use oxygengine_core::{
3 prefab::{Prefab, PrefabComponent},
4 Scalar,
5};
6use serde::{Deserialize, Serialize};
7use std::collections::HashMap;
8
9#[derive(Debug, Clone)]
10pub enum InventoryError {
11 InventoryFull,
12 EmptyItem(MarketItemId),
13 ItemDoesNotExists(MarketItemId),
14 RemovingTooMuchItems(MarketItemId, usize, usize),
16 CannotGiveItem(MarketItemId, usize),
18 CannotReceiveItem(MarketItemId, usize),
20}
21
22#[derive(Debug, Default, Clone, Serialize, Deserialize)]
23pub struct Inventory {
24 capacity_weight: Option<Scalar>,
25 items: HashMap<MarketItemId, usize>,
26 items_resize: usize,
27}
28
29impl Inventory {
30 pub fn new(capacity_weight: Option<Scalar>) -> Self {
31 Self::with_resize(capacity_weight, 10)
32 }
33
34 pub fn with_resize(capacity_weight: Option<Scalar>, items_resize: usize) -> Self {
35 Self {
36 capacity_weight,
37 items: HashMap::with_capacity(items_resize),
38 items_resize,
39 }
40 }
41
42 pub fn items(&self) -> impl Iterator<Item = (MarketItemId, usize)> + '_ {
43 self.items.iter().map(|(id, count)| (*id, *count))
44 }
45
46 pub fn contains(&self, id: MarketItemId) -> Option<usize> {
47 self.items.get(&id).copied()
48 }
49
50 pub fn can_add<T, V>(
51 &self,
52 id: MarketItemId,
53 count: usize,
54 database: &MarketDatabase<T, V>,
55 ) -> bool
56 where
57 T: std::fmt::Debug + Clone + Send + Sync,
58 V: Currency + std::fmt::Debug + Clone + Send + Sync,
59 {
60 count != 0
61 && database
62 .item(id)
63 .map(|item| {
64 self.capacity_weight
65 .map(|capacity_weight| item.weight * count as Scalar <= capacity_weight)
66 .unwrap_or(true)
67 })
68 .unwrap_or_default()
69 }
70
71 pub fn add<T, V>(
72 &mut self,
73 id: MarketItemId,
74 count: usize,
75 database: &MarketDatabase<T, V>,
76 ) -> Result<(), InventoryError>
77 where
78 T: std::fmt::Debug + Clone + Send + Sync,
79 V: Currency + std::fmt::Debug + Clone + Send + Sync,
80 {
81 if count == 0 {
82 return Err(InventoryError::EmptyItem(id));
83 }
84 let item = match database.item(id) {
85 Some(item) => item,
86 None => return Err(InventoryError::ItemDoesNotExists(id)),
87 };
88 if let Some(capacity_weight) = self.capacity_weight {
89 if item.weight * count as Scalar > capacity_weight {
90 return Err(InventoryError::InventoryFull);
91 }
92 }
93 *self.items.entry(id).or_default() += count;
94 Ok(())
95 }
96
97 pub fn can_remove(&self, id: MarketItemId, count: usize) -> bool {
98 count != 0
99 && self
100 .items
101 .get(&id)
102 .copied()
103 .and_then(|c| c.checked_sub(count))
104 .is_some()
105 }
106
107 pub fn remove(&mut self, id: MarketItemId, count: usize) -> Result<(), InventoryError> {
108 if count == 0 {
109 return Err(InventoryError::EmptyItem(id));
110 }
111 if let Some(c) = self.items.get(&id).copied() {
112 if let Some(c) = c.checked_sub(count) {
113 if c == 0 {
114 self.items.remove(&id);
115 return Ok(());
116 }
117 self.items.insert(id, c);
118 return Ok(());
119 }
120 return Err(InventoryError::RemovingTooMuchItems(id, c, count));
121 }
122 Err(InventoryError::ItemDoesNotExists(id))
123 }
124
125 pub fn transfer<T, V>(
126 &mut self,
127 receiver: &mut Self,
128 id: MarketItemId,
129 count: usize,
130 database: &MarketDatabase<T, V>,
131 ) -> Result<(), InventoryError>
132 where
133 T: std::fmt::Debug + Clone + Send + Sync,
134 V: Currency + std::fmt::Debug + Clone + Send + Sync,
135 {
136 if !self.can_remove(id, count) {
137 return Err(InventoryError::CannotGiveItem(id, count));
138 }
139 if !receiver.can_add(id, count, database) {
140 return Err(InventoryError::CannotReceiveItem(id, count));
141 }
142 self.remove(id, count).unwrap();
143 receiver.add(id, count, database).unwrap();
144 Ok(())
145 }
146}
147
148impl Prefab for Inventory {}
149impl PrefabComponent for Inventory {}