1#![allow(clippy::module_name_repetitions)]
2use std::time::Duration;
3
4use chrono::{DateTime, Local};
5use enum_map::{Enum, EnumMap};
6use num_derive::FromPrimitive;
7use strum::{EnumIter, IntoEnumIterator};
8
9use super::{ArrSkip, CCGet, CFPGet, CSTGet, EnumMapGet, SFError, ServerTime};
10
11#[derive(Debug, Default, Clone)]
13#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
14pub struct Underworld {
15 pub buildings: EnumMap<UnderworldBuildingType, UnderworldBuilding>,
18 pub units: EnumMap<UnderworldUnitType, UnderworldUnit>,
20 pub production: EnumMap<UnderworldResourceType, UnderworldProduction>,
22 pub last_collectable_update: Option<DateTime<Local>>,
26
27 pub souls_current: u64,
31 pub souls_limit: u64,
35
36 pub upgrade_building: Option<UnderworldBuildingType>,
38 pub upgrade_finish: Option<DateTime<Local>>,
40 pub upgrade_begin: Option<DateTime<Local>>,
42
43 pub lure_level: u16,
45 pub lured_today: u16,
47 pub lure_suggestion: Option<LureSuggestion>,
50}
51
52#[derive(Debug, Clone, Copy, PartialEq, Eq)]
56#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
57pub struct LureSuggestion(pub(crate) u32);
58
59#[derive(Debug, Default, Clone, Copy)]
62#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
63pub struct UnderworldCost {
64 pub time: Duration,
66 pub silver: u64,
68 pub souls: u64,
70}
71
72impl UnderworldCost {
73 pub(crate) fn parse(data: &[i64]) -> Result<UnderworldCost, SFError> {
74 Ok(UnderworldCost {
75 time: Duration::from_secs(data.csiget(0, "u time cost", 0)?),
76 silver: data.csiget(1, "u silver cost", u64::MAX)?,
78 souls: data.csiget(2, "u sould cost", u64::MAX)?,
79 })
80 }
81}
82
83impl Underworld {
84 pub(crate) fn update_building_prices(
85 &mut self,
86 data: &[i64],
87 ) -> Result<(), SFError> {
88 for (pos, typ) in UnderworldBuildingType::iter().enumerate() {
89 self.buildings.get_mut(typ).upgrade_cost = UnderworldCost::parse(
90 data.skip(pos * 3, "underworld building prices")?,
91 )?;
92 }
93 Ok(())
94 }
95
96 pub(crate) fn update_underworld_unit_prices(
97 &mut self,
98 data: &[i64],
99 ) -> Result<(), SFError> {
100 for (pos, typ) in UnderworldUnitType::iter().enumerate() {
101 self.units.get_mut(typ).upgrade_next_lvl =
102 data.csiget(pos * 3, "uunit next lvl", 0)?;
103 self.units.get_mut(typ).upgrade_cost.silver =
104 data.csiget(1 + pos * 3, "uunit upgrade gold", 0)?;
105 self.units.get_mut(typ).upgrade_cost.souls =
106 data.csiget(2 + pos * 3, "uunit upgrade gold", 0)?;
107 }
108 Ok(())
109 }
110
111 pub(crate) fn update(
112 &mut self,
113 data: &[i64],
114 server_time: ServerTime,
115 ) -> Result<(), SFError> {
116 for (pos, typ) in UnderworldBuildingType::iter().enumerate() {
117 self.buildings.get_mut(typ).level =
118 data.csiget(448 + pos, "building level", 0)?;
119 }
120
121 for (i, typ) in UnderworldUnitType::iter().enumerate() {
122 let start = 146 + i * 148;
123 self.units.get_mut(typ).upgraded_amount =
124 data.csiget(start, "uunit upgrade level", 0)?;
125 self.units.get_mut(typ).count =
126 data.csiget(start + 1, "uunit count", 0)?;
127 self.units.get_mut(typ).total_attributes =
128 data.csiget(start + 2, "uunit atr bonus", 0)?;
129 self.units.get_mut(typ).level =
130 data.csiget(start + 3, "uunit level", 0)?;
131 }
132
133 #[allow(clippy::enum_glob_use)]
134 {
135 use UnderworldResourceType::*;
136 self.production.get_mut(Souls).last_collectable =
137 data.csiget(459, "uu souls in building", 0)?;
138 self.production.get_mut(Souls).limit =
139 data.csiget(460, "uu sould max in building", 0)?;
140 self.souls_limit = data.csiget(461, "uu souls max saved", 0)?;
141 self.production.get_mut(Souls).per_hour =
142 data.csiget(463, "uu souls per hour", 0)?;
143
144 self.production.get_mut(Silver).last_collectable =
145 data.csiget(464, "uu gold in building", 0)?;
146 self.production.get_mut(Silver).limit =
147 data.csiget(465, "uu max gold in building", 0)?;
148 self.production.get_mut(Silver).per_hour =
149 data.csiget(466, "uu gold ", 0)?;
150
151 self.production.get_mut(ThirstForAdventure).last_collectable =
152 data.csiget(473, "uu alu in building", 0)?;
153 self.production.get_mut(ThirstForAdventure).limit =
154 data.csiget(474, "uu max stored alu", 0)?;
155 self.production.get_mut(ThirstForAdventure).per_hour =
156 data.csiget(475, "uu alu per day", 0)?;
157 }
158
159 self.last_collectable_update =
160 data.cstget(467, "uw resource time", server_time)?;
161 self.upgrade_building =
162 data.cfpget(468, "u building upgrade", |x| x - 1)?;
163 self.upgrade_finish = data.cstget(469, "u expand end", server_time)?;
164 self.upgrade_begin =
165 data.cstget(470, "u upgrade begin", server_time)?;
166 self.lure_level = data.csiget(471, "uu lure lvl", 0)?;
167 self.lured_today = data.csiget(472, "u battles today", 0)?;
168 Ok(())
169 }
170}
171
172#[derive(Debug, Clone, Copy, strum::EnumCount, Enum, PartialEq)]
174#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
175#[allow(missing_docs)]
176pub enum UnderworldResourceType {
177 Souls = 0,
178 Silver = 1,
179 #[doc(alias = "ALU")]
180 ThirstForAdventure = 2,
181}
182
183#[derive(Debug, Default, Clone)]
186#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
187pub struct UnderworldProduction {
188 pub last_collectable: u64,
193 pub limit: u64,
196 pub per_hour: u64,
199}
200
201#[derive(
203 Debug,
204 Clone,
205 Copy,
206 FromPrimitive,
207 strum::EnumCount,
208 Enum,
209 EnumIter,
210 PartialEq,
211)]
212#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
213#[allow(missing_docs)]
214pub enum UnderworldBuildingType {
215 HeartOfDarkness = 0,
216 Gate = 1,
217 GoldPit = 2,
218 SoulExtractor = 3,
219 GoblinPit = 4,
220 TortureChamber = 5,
221 GladiatorTrainer = 6,
222 TrollBlock = 7,
223 Adventuromatic = 8,
224 Keeper = 9,
225}
226
227#[derive(Debug, Clone, Copy, strum::EnumCount, Enum, EnumIter, PartialEq)]
229#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
230#[allow(missing_docs)]
231pub enum UnderworldUnitType {
232 Goblin = 0,
233 Troll = 1,
234 Keeper = 2,
235}
236
237#[derive(Debug, Default, Clone, Copy)]
239#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
240pub struct UnderworldBuilding {
241 pub level: u8,
244 pub upgrade_cost: UnderworldCost,
246}
247
248#[derive(Debug, Default, Clone)]
250#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
251pub struct UnderworldUnit {
252 pub level: u16,
254 pub count: u16,
256 pub total_attributes: u32,
258
259 pub upgraded_amount: u16,
261
262 pub upgrade_cost: UnderworldCost,
264 pub upgrade_next_lvl: u16,
266}