1use std::collections::HashMap;
2
3use configs::game_config::GameConfig;
4use essences::abilities::{Ability, AbilityTemplate};
5use essences::arena::Arena;
6use essences::autochest::AutoChest;
7use essences::bundles::{BundleElement, BundleId};
8use essences::character_state::CharacterState;
9use essences::dungeons::Dungeons;
10use essences::entity::Entity;
11use essences::fighting::ActiveFight;
12use essences::gift::Gift;
13use essences::items::Item;
14use essences::mail::Mail;
15use essences::offers::OffersInfo;
16use essences::prelude::*;
17use essences::progress_pass::ProgressPassState;
18use essences::pvp::PVPState;
19use essences::quest::QuestsGroups;
20use essences::referrals::Patron;
21
22use event_system::state::State;
23
24use crate::BehaviorRegistry;
25use crate::bundles::{bundle_raw_afk_step_to_element, bundle_raw_step_to_element};
26use crate::game_config_helpers::GameConfigLookup;
27use crate::party::Party;
28use schemars::JsonSchema;
29use strum::{EnumIter, IntoEnumIterator};
30
31#[derive(
33 Clone, Copy, PartialEq, Eq, Debug, Tsify, Serialize, Deserialize, JsonSchema, EnumIter,
34)]
35#[serde(rename_all = "lowercase")]
36pub enum UserStatus {
37 Admin,
38 Banned,
39}
40
41impl UserStatus {
42 pub fn as_str(self) -> &'static str {
44 match self {
45 UserStatus::Admin => "admin",
46 UserStatus::Banned => "banned",
47 }
48 }
49}
50
51#[derive(Clone, PartialEq, Eq, Debug, Tsify, Serialize, Deserialize, JsonSchema, EnumIter)]
54pub enum UserPermissions {
55 CheatsAccess,
56 LogOutAccess,
57}
58
59impl UserPermissions {
60 pub fn all() -> Vec<Self> {
62 Self::iter().collect()
63 }
64}
65
66#[derive(Clone, PartialEq, Eq, Default, Debug, Tsify, Serialize, Deserialize, JsonSchema)]
67#[tsify(into_wasm_abi)]
68pub struct OverlordState {
69 pub character_state: CharacterState,
70 pub blocked_character_ids: Vec<uuid::Uuid>,
71 pub active_fight: Option<ActiveFight>,
72 pub pvp_state: Option<PVPState>,
73 pub connection_store: HashMap<String, i64>,
74 pub quest_groups: QuestsGroups,
75 pub incoming_gifts: Vec<Gift>,
76 pub incoming_mails: Vec<Mail>,
77 pub patron: Option<Patron>,
78 pub referral_daily_reward_claimed: Option<bool>,
79 pub auto_chest: AutoChest,
80 pub arena: Arena,
81 pub dungeons: Dungeons,
82 pub offers_info: OffersInfo,
83 pub permissions: Vec<UserPermissions>,
84 pub party: Party,
85 pub progress_pass: ProgressPassState,
86}
87
88impl State for OverlordState {
89 fn cmp_db_updated(&self, db_updated: &Self) -> bool {
91 self.character_state.character == db_updated.character_state.character
92 && self.character_state.inventory == db_updated.character_state.inventory
93 }
94
95 fn is_ticker_paused(&self) -> bool {
96 self.active_fight.as_ref().is_some_and(|fight| fight.paused)
97 }
98}
99
100impl OverlordState {
101 pub fn claim_vassal_reward(
102 &mut self,
103 vassal_id: uuid::Uuid,
104 claim_time: chrono::DateTime<chrono::Utc>,
105 ) -> anyhow::Result<()> {
106 match self
107 .character_state
108 .vassals
109 .iter_mut()
110 .find(|vassal| vassal.character_id == vassal_id)
111 {
112 Some(vassal) => {
113 vassal.claim_reward(claim_time);
114 Ok(())
115 }
116 None => anyhow::bail!("No vassal with given id={}", vassal_id),
117 }
118 }
119
120 pub fn claim_suzerain_reward(
121 &mut self,
122 claim_time: chrono::DateTime<chrono::Utc>,
123 ) -> anyhow::Result<()> {
124 let Some(suzerain) = self.character_state.suzerain.as_mut() else {
125 anyhow::bail!("No suzerain to update")
126 };
127 suzerain.claim_reward(claim_time);
128 Ok(())
129 }
130
131 pub fn compute_description_values_for_ability(
132 &self,
133 ability: &Ability,
134 behaviors: &BehaviorRegistry,
135 config: &GameConfig,
136 ) -> Vec<f64> {
137 let Some(template) = config.ability_template(ability.template_id) else {
138 tracing::error!(
139 "Failed to get template for ability template_id={}",
140 ability.template_id
141 );
142 return vec![];
143 };
144
145 let Some(ref description_script) = template.description_values_script else {
146 return vec![];
147 };
148
149 match crate::behaviors::ui_values::description_values(
150 &crate::behaviors::ui_values::DescriptionValuesCtx {
151 ability_level: ability.level,
152 ability_template_id: ability.template_id,
153 script: description_script,
154 config,
155 lookups: behaviors.lookups(),
156 },
157 ) {
158 Ok(values) => values,
159 Err(err) => {
160 tracing::error!("Error computing description values: {err}");
161 vec![]
162 }
163 }
164 }
165
166 pub fn compute_description_values_for_talent(
167 &self,
168 talent_template: &essences::talent_tree::TalentTemplate,
169 level: i64,
170 ) -> Vec<f64> {
171 let Some(ref description_script) = talent_template.description_values_script else {
172 return vec![];
173 };
174
175 match crate::behaviors::ui_values::talent_description_values(
176 &crate::behaviors::ui_values::TalentDescriptionValuesCtx {
177 talent_level: level,
178 script: description_script,
179 },
180 ) {
181 Ok(values) => values,
182 Err(err) => {
183 tracing::error!("Error computing talent description values: {err}");
184 vec![]
185 }
186 }
187 }
188
189 pub fn compute_description_values_template(
190 &self,
191 ability_template: &AbilityTemplate,
192 behaviors: &BehaviorRegistry,
193 config: &GameConfig,
194 ) -> Vec<f64> {
195 let ability = Ability::from_template(ability_template, None, None);
196
197 self.compute_description_values_for_ability(&ability, behaviors, config)
198 }
199
200 pub fn compute_afk_reward(
201 &self,
202 game_config: &GameConfig,
203 behaviors: &BehaviorRegistry,
204 ) -> Vec<BundleElement> {
205 let mut elements = vec![];
206
207 let Some(bundle) = game_config.bundle(game_config.afk_rewards_settings.bundle_id) else {
208 tracing::error!(
209 "Couldn't find afk bundle with id = {}",
210 game_config.afk_rewards_settings.bundle_id
211 );
212 return vec![];
213 };
214
215 let now = ::time::utc_now();
216
217 for step in &bundle.steps {
218 elements.push(bundle_raw_afk_step_to_element(
219 step,
220 &self.character_state,
221 now,
222 behaviors,
223 game_config,
224 ));
225 }
226
227 elements
228 }
229
230 pub fn compute_afk_instant_reward(
231 &self,
232 game_config: &GameConfig,
233 behaviors: &BehaviorRegistry,
234 ) -> Vec<BundleElement> {
235 let mut elements = vec![];
236
237 let Some(bundle) = game_config.bundle(game_config.afk_rewards_settings.bundle_id) else {
238 tracing::error!(
239 "Couldn't find afk bundle with id = {}",
240 game_config.afk_rewards_settings.bundle_id
241 );
242 return vec![];
243 };
244
245 let afk_settings = &game_config.afk_rewards_settings;
246 let duration_sec = afk_settings
247 .instant_reward_duration_sec
248 .min(afk_settings.max_possible_time_sec);
249 let fake_now = self.character_state.character.last_afk_reward_claimed_at
250 + chrono::Duration::seconds(duration_sec as i64);
251
252 for step in &bundle.steps {
253 elements.push(bundle_raw_afk_step_to_element(
254 step,
255 &self.character_state,
256 fake_now,
257 behaviors,
258 game_config,
259 ));
260 }
261
262 elements
263 }
264
265 pub fn compute_bundle_reward(
266 &self,
267 game_config: &GameConfig,
268 behaviors: &BehaviorRegistry,
269 bundle_id: BundleId,
270 ) -> Vec<BundleElement> {
271 let mut elements = vec![];
272
273 let Some(bundle) = game_config.bundle(bundle_id) else {
274 tracing::error!(
275 "Couldn't find bundle with id = {}",
276 game_config.afk_rewards_settings.bundle_id
277 );
278 return vec![];
279 };
280
281 for step in &bundle.steps {
282 elements.push(bundle_raw_step_to_element(
283 step,
284 &self.character_state,
285 behaviors,
286 game_config,
287 ));
288 }
289
290 elements
291 }
292
293 pub fn calculate_item_power(
294 &self,
295 item: Item,
296 game_config: &GameConfig,
297 behaviors: &BehaviorRegistry,
298 ) -> anyhow::Result<i64> {
299 crate::behaviors::power::item_power(&crate::behaviors::power::ItemPowerCtx {
300 character: &self.character_state,
301 item: &item,
302 config: game_config,
303 lookups: behaviors.lookups(),
304 })
305 }
306
307 pub fn get_active_fight_player(&self) -> Option<&Entity> {
308 self.active_fight
309 .as_ref()
310 .and_then(|fight| fight.get_player())
311 }
312
313 pub fn get_active_fight_player_mut(&mut self) -> Option<&mut Entity> {
314 self.active_fight
315 .as_mut()
316 .and_then(|fight| fight.get_player_mut())
317 }
318}