1use crate::config::ConfigInterface;
2use crate::data::item_data::ItemData;
3use crate::game::errors::item_event::GameItemEventError;
4use crate::models::item::attributes_container::ItemAttributesContainerInterface;
5use crate::models::item::properties::{ItemProperties, ItemPropertiesType};
6use crate::models::item::properties_container::{
7 ItemPropertiesContainer, ItemPropertiesContainerInterface,
8};
9use crate::traits::model::Model;
10use chrono::{DateTime, Utc};
11use diesel::{AsChangeset, Insertable, Queryable, Selectable};
12use serde::{Deserialize, Serialize};
13use std::collections::{HashMap, HashSet};
14use std::sync::Arc;
15
16pub mod attributes;
17pub mod attributes_container;
18pub mod properties;
19pub mod properties_container;
20
21#[derive(
22 Debug, Default, Serialize, Deserialize, Clone, PartialEq, Queryable, Selectable, AsChangeset,
23)]
24#[diesel(table_name = crate::schema::fish_items)]
25pub struct Item {
26 pub id: i64,
27 pub user_id: i64,
28 pub type_id: i32,
29 pub properties: ItemPropertiesContainer,
30 pub created_at: DateTime<Utc>,
31 pub updated_at: DateTime<Utc>,
32}
33
34impl Model for Item {
35 type Table = crate::schema::fish_items::table;
36 type PrimaryKeyType = i64;
37 type InsertType = NewItem;
38
39 fn table() -> Self::Table {
40 crate::schema::fish_items::table
41 }
42
43 fn id(&self) -> Self::PrimaryKeyType {
44 self.id
45 }
46}
47
48#[derive(Insertable)]
49#[diesel(table_name = crate::schema::fish_items)]
50#[diesel(check_for_backend(diesel::pg::Pg))]
51pub struct NewItem {
52 pub user_id: i64,
53 pub type_id: i32,
54 pub properties: ItemPropertiesContainer,
55}
56
57impl NewItem {
58 pub fn new(user_id: i64, item_data: Arc<ItemData>) -> Self {
59 Self {
60 user_id,
61 type_id: item_data.id,
62 properties: item_data.default_properties.clone(),
63 }
64 }
65}
66
67pub type ItemEventResult = Result<ItemEventSuccess, GameItemEventError>;
68
69#[derive(Debug, Default, Clone, PartialEq)]
70pub struct ItemEventSuccess {
71 pub consume: bool,
72}
73
74impl ItemEventSuccess {
75 pub fn new(consume: bool) -> Self {
76 Self { consume }
77 }
78}
79
80impl Item {
81 pub fn migrate_properties(&mut self, config: Arc<dyn ConfigInterface>) -> ItemEventResult {
82 let item_data = config
83 .get_item_data(self.type_id)
84 .ok_or(GameItemEventError::invalid_item_type(self.type_id))?;
85
86 let required_property_types: HashSet<ItemPropertiesType> =
87 item_data.default_properties.get_properties_types();
88
89 let existing_property_types: HashSet<ItemPropertiesType> =
90 self.properties.get_properties_types();
91
92 let properties_to_add: HashSet<ItemPropertiesType> = required_property_types
93 .difference(&existing_property_types)
94 .copied()
95 .collect();
96
97 let properties_to_remove: HashSet<ItemPropertiesType> = existing_property_types
98 .difference(&required_property_types)
99 .copied()
100 .collect();
101
102 properties_to_add.iter().for_each(|component_type| {
103 let component = component_type.get_default_properties();
104 self.properties.add_properties(component);
105 });
106
107 properties_to_remove
108 .iter()
109 .for_each(|component_type| self.properties.remove_properties(*component_type));
110
111 Ok(ItemEventSuccess::new(self.should_consume()))
112 }
113
114 pub fn use_as_rod(&mut self, config: Arc<dyn ConfigInterface>) -> ItemEventResult {
115 let attributes = self
116 .attributes(config)
117 .ok_or(GameItemEventError::invalid_item_type(self.type_id))?;
118
119 if !attributes.is_rod() {
120 Err(GameItemEventError::not_a_rod(self.type_id))
121 } else {
122 self.properties.on_use(1);
123 Ok(ItemEventSuccess::new(self.should_consume()))
124 }
125 }
126
127 pub fn add(&mut self, amount: u64) -> ItemEventResult {
128 self.on_add(amount);
129 Ok(ItemEventSuccess::new(self.should_consume()))
130 }
131
132 pub fn remove(&mut self, amount: u64) -> ItemEventResult {
133 self.on_remove(amount);
134 Ok(ItemEventSuccess::new(self.should_consume()))
135 }
136}
137
138pub trait ItemInterface: ItemPropertiesContainerInterface {
139 fn attributes(
140 &self,
141 config: Arc<dyn ConfigInterface>,
142 ) -> Option<Arc<dyn ItemAttributesContainerInterface>>;
143}
144
145impl ItemInterface for Item {
146 fn attributes(
147 &self,
148 config: Arc<dyn ConfigInterface>,
149 ) -> Option<Arc<dyn ItemAttributesContainerInterface>> {
150 config
151 .get_item_data(self.type_id)
152 .map(|data| data as Arc<dyn ItemAttributesContainerInterface>)
153 }
154}
155
156impl ItemPropertiesContainerInterface for Item {
157 fn get_properties(&self) -> &HashMap<ItemPropertiesType, ItemProperties> {
158 self.properties.get_properties()
159 }
160
161 fn get_properties_mut(&mut self) -> &mut HashMap<ItemPropertiesType, ItemProperties> {
162 self.properties.get_properties_mut()
163 }
164}