1use crate::gatings::AutoChestGatings;
2use crate::prelude::*;
3
4use crate::items::Attribute;
5use crate::items::AttributeId;
6use crate::items::Item;
7use crate::items::ItemRarity;
8use crate::items::ItemRarityId;
9
10#[derive(Clone, Default, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema, Tsify)]
11pub struct AutoChest {
12 pub active_filter: Option<AutoChestFilter>,
13 pub filters: Vec<AutoChestFilter>,
14 pub power_compare_enabled: bool,
15 pub batch_size: i64,
16}
17
18#[declare]
19pub type AutoChestFilterId = Uuid;
20
21#[derive(Clone, Default, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema, Tsify)]
22pub struct AutoChestFilter {
23 pub id: AutoChestFilterId,
24 pub name: String,
25 pub rarity_id: Option<ItemRarityId>,
26 pub guaranteed_attribute_enabled: bool,
27 pub guaranteed_attribute_id: Option<AttributeId>,
28 pub additional_attribute_enabled: bool,
29 pub additional_attribute_ids: Vec<AttributeId>,
30}
31
32impl AutoChestFilter {
33 pub fn validate(
34 &self,
35 cur_chapter_level: i64,
36 attributes: &[Attribute],
37 autochest_gatings: &AutoChestGatings,
38 ) -> anyhow::Result<()> {
39 if cur_chapter_level < autochest_gatings.autochest_button_unlock_chapter {
40 anyhow::bail!(
41 "Auto chest is not enabled on current chapter = {}",
42 cur_chapter_level
43 )
44 }
45
46 if self.rarity_id.is_some() && cur_chapter_level < autochest_gatings.rarity_filter_unlock {
47 anyhow::bail!(
48 "Rarity filter is not available on cur_chapter_level = {}",
49 cur_chapter_level
50 )
51 }
52
53 if let Some(guaranteed_attribute_id) = self.guaranteed_attribute_id {
54 if cur_chapter_level < autochest_gatings.guaranteed_stat_unlock {
55 anyhow::bail!(
56 "Guaranteed stat filter is not available on cur_chapter_level = {}",
57 cur_chapter_level
58 )
59 }
60
61 let Some(guaranteed_attr) = attributes
62 .iter()
63 .find(|attr| attr.id == guaranteed_attribute_id)
64 else {
65 anyhow::bail!(
66 "Tried finding guaranteed_attribute with id = {}, but didn't find it in config",
67 guaranteed_attribute_id
68 )
69 };
70
71 if !guaranteed_attr.is_ui_visible {
72 anyhow::bail!(
73 "Provided guaranteed attribute = {:?}, shouldn't be in auto chest",
74 guaranteed_attr
75 )
76 }
77 }
78
79 if !self.additional_attribute_ids.is_empty() {
80 if self.additional_attribute_ids.len() > 3 {
81 anyhow::bail!(
82 "More additional attributes provided: {}, than allowed: 3",
83 self.additional_attribute_ids.len(),
84 )
85 }
86
87 let required_chapter = match self.additional_attribute_ids.len() {
88 1 => autochest_gatings.first_additional_stat_unlock,
89 2 => autochest_gatings.second_additional_stat_unlock,
90 3 => autochest_gatings.third_additional_stat_unlock,
91 _ => unreachable!(),
92 };
93
94 if cur_chapter_level < required_chapter {
95 anyhow::bail!(
96 "Additional stats filter with {} attribute(s) is not available on cur_chapter_level = {}",
97 self.additional_attribute_ids.len(),
98 cur_chapter_level
99 )
100 }
101
102 for additional_attr_id in &self.additional_attribute_ids {
103 let Some(additional_attr) = attributes
104 .iter()
105 .find(|attr| attr.id == *additional_attr_id)
106 else {
107 anyhow::bail!(
108 "Tried finding additional_attribute with id = {}, but didn't find it in config",
109 additional_attr_id
110 )
111 };
112
113 if !additional_attr.is_ui_visible {
114 anyhow::bail!(
115 "Provided additional attribute = {:?}, shouldn't be in auto chest",
116 additional_attr
117 )
118 }
119 }
120 }
121
122 Ok(())
123 }
124}
125
126pub fn filter_items(
127 items: &[Item],
128 filter: &Option<AutoChestFilter>,
129 power_compare_enabled: bool,
130 item_rarities: &Vec<ItemRarity>,
131 item_powers: &std::collections::HashMap<uuid::Uuid, i64>,
132 equipped_item_powers: &std::collections::HashMap<crate::items::ItemType, i64>,
133) -> anyhow::Result<(Vec<Item>, Vec<Item>)> {
134 let mut items_to_sell = vec![];
135 let mut items_to_equip = vec![];
136
137 let filter_rarity = if let Some(filter) = filter
138 && let Some(rarity_id) = filter.rarity_id
139 {
140 let Some(rarity) = item_rarities.iter().find(|r| r.id == rarity_id) else {
141 anyhow::bail!(
142 "Couldn't find filter rarity_id = {}, in provided item_rarities = {:?}",
143 rarity_id,
144 item_rarities
145 );
146 };
147 Some(rarity)
148 } else {
149 None
150 };
151
152 for item in items {
153 if let Some(filter_rarity) = filter_rarity
154 && item.rarity.order < filter_rarity.order
155 {
156 items_to_sell.push(item.clone());
157 continue;
158 }
159
160 if let Some(filter) = filter {
161 if filter.guaranteed_attribute_enabled
162 && let Some(guaranteed_attribute_id) = filter.guaranteed_attribute_id
163 && !item
164 .attributes
165 .iter()
166 .any(|item_attr| item_attr.attr_id == guaranteed_attribute_id)
167 {
168 items_to_sell.push(item.clone());
169 continue;
170 }
171
172 if filter.additional_attribute_enabled
173 && !filter.additional_attribute_ids.is_empty()
174 && !filter.additional_attribute_ids.iter().any(|id| {
175 item.attributes
176 .iter()
177 .any(|item_attr| *id == item_attr.attr_id)
178 })
179 {
180 items_to_sell.push(item.clone());
181 continue;
182 }
183 }
184
185 if power_compare_enabled
187 && let Some(&equipped_power) = equipped_item_powers.get(&item.item_type)
188 && let Some(&item_power) = item_powers.get(&item.id)
189 && item_power < equipped_power
190 {
191 items_to_sell.push(item.clone());
192 continue;
193 }
194
195 items_to_equip.push(item.clone());
196 }
197
198 Ok((items_to_sell, items_to_equip))
199}