essences/
character_state.rs

1use crate::ad_usage::AdUsageMap;
2use crate::buffs::ActiveBuff;
3use crate::character_settings::CharacterSettings;
4use crate::class::CharacterClass;
5use crate::cores::CoresState;
6use crate::currency::{CurrencyId, CurrencyUnit};
7use crate::entity::EntityAttributes;
8use crate::talent_tree::TalentLevelsMap;
9
10use crate::plinko::PlinkoPinBonusesMap;
11
12use super::{
13    abilities, ability_stones, bundles, characters, items, pets, skins, statue, users, vassals,
14};
15
16use crate::prelude::*;
17
18#[derive(Clone, PartialEq, Eq, Default, Debug, JsonSchema, Tsify, Serialize, Deserialize)]
19pub struct CharacterState {
20    pub user: users::User,
21    pub character: characters::Character,
22    pub currencies: Vec<CurrencyUnit>,
23    pub inventory: Vec<items::Item>,
24    pub all_abilities: Vec<abilities::Ability>, // Every ability that player have with equipped abilities
25    pub equipped_abilities: abilities::EquippedAbilities, // Only player's equipped abilitie
26    /// Owned ability stones (their own collection — never item or artifact stones).
27    pub ability_stones: Vec<ability_stones::OwnedAbilityStone>,
28    /// Which stone sits in which ability socket.
29    pub ability_stone_sockets: ability_stones::AbilityStoneSockets,
30    pub all_pets: Vec<pets::Pet>,
31    pub equipped_pets: pets::EquippedPets,
32    pub suzerain: Option<vassals::Suzerain>,
33    pub vassals: Vec<vassals::Vassal>,
34    pub vassal_tasks: Vec<vassals::VassalTask>,
35    pub player_attributes: EntityAttributes,
36    pub bundle_step_generic: Vec<bundles::BundleStepGeneric>,
37    pub character_settings: CharacterSettings,
38    pub character_skins: skins::CharacterSkins,
39    pub talent_levels: TalentLevelsMap,
40    pub statue_state: statue::StatueState,
41    /// Постоянные прибавки к статам, накопленные за колышки Plinko.
42    /// Начисляются только сервером в обработчике `PlinkoBallDropped`.
43    pub plinko_pin_bonuses: PlinkoPinBonusesMap,
44    pub ad_usage: AdUsageMap,
45
46    /// BAL-038: hidden per-family daily kill-faucet counters. Never shown to
47    /// the player; kept for telemetry, admin and debug.
48    pub kill_faucet_daily: crate::kill_faucets::KillFaucetDailyMap,
49    pub active_buffs: Vec<ActiveBuff>,
50    pub character_classes: Vec<CharacterClass>,
51    /// Twin cores, the laws slotted into them and the bridges between them.
52    pub cores: CoresState,
53    /// Owned Trigger/Effect Stones. Two collections that never mix, each stone
54    /// carrying its own socket assignment — see [`crate::stones`].
55    pub stones: crate::stones::StoneInventory,
56    /// Owned artifacts, the worn one, and the stones in the artifact's six
57    /// sockets — see [`crate::artifacts`].
58    pub artifacts: crate::artifacts::ArtifactCollection,
59}
60
61impl CharacterState {
62    pub fn get_vassal(&self, vassal_id: uuid::Uuid) -> anyhow::Result<vassals::Vassal> {
63        match self
64            .vassals
65            .iter()
66            .find(|vassal| vassal.character_id == vassal_id)
67        {
68            Some(vassal) => Ok(vassal.clone()),
69            None => anyhow::bail!("No vassal with given id={}", vassal_id),
70        }
71    }
72
73    pub fn has_vassal(&self, vassal_id: uuid::Uuid) -> bool {
74        self.vassals
75            .iter()
76            .any(|vassal| vassal.character_id == vassal_id)
77    }
78
79    pub fn get_currency(&self, currency_id: CurrencyId) -> i64 {
80        if let Some(currency_unity) = self
81            .currencies
82            .iter()
83            .find(|currency| currency.currency_id == currency_id)
84        {
85            return currency_unity.amount;
86        };
87
88        0
89    }
90
91    pub fn get_arena_tickets(&self, currency_id: CurrencyId) -> i64 {
92        if let Some(arena_tickets_unit) = self
93            .currencies
94            .iter()
95            .find(|currency| currency.currency_id == currency_id)
96        {
97            return arena_tickets_unit.amount;
98        };
99
100        0
101    }
102
103    pub fn decrement_arena_tickets(&mut self, currency_id: CurrencyId) -> anyhow::Result<()> {
104        let Some(arena_tickets_unit) = self
105            .currencies
106            .iter_mut()
107            .find(|currency| currency.currency_id == currency_id)
108        else {
109            anyhow::bail!(
110                "Failed to get arena tickets currency unit with id={}",
111                currency_id
112            );
113        };
114
115        if arena_tickets_unit.amount <= 0 {
116            anyhow::bail!("No available arena tickets to decrement",);
117        }
118
119        arena_tickets_unit.amount -= 1;
120
121        Ok(())
122    }
123}
124
125#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, JsonSchema, Tsify)]
126#[tsify(into_wasm_abi, from_wasm_abi)]
127pub struct GetCharacterStateRequest {
128    pub character_id: uuid::Uuid,
129}
130
131#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, JsonSchema, Tsify)]
132#[tsify(into_wasm_abi, from_wasm_abi)]
133pub struct FullClearCharacterRequest {
134    pub username: String,
135}
136
137#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, JsonSchema, Tsify)]
138#[tsify(into_wasm_abi, from_wasm_abi)]
139pub enum GetCharacterStateResponse {
140    Ok {
141        character_state: Box<CharacterState>,
142        /// Durable global equipment side used to render the owner's active loadout.
143        flip_state: crate::flip::FlipState,
144    },
145    Error {
146        code: String,
147        message: String,
148    },
149}