oxygengine_overworld/components/
inventory.rs

1use 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    /// (item id, owned item count, removing item count)
15    RemovingTooMuchItems(MarketItemId, usize, usize),
16    /// (item id, item count)
17    CannotGiveItem(MarketItemId, usize),
18    /// (item id, item count)
19    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 {}