essences/
autochest.rs

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        // Compare power if enabled and there's an equipped item of the same type
186        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}