1use common_i18n::Content;
2use serde::{Deserialize, Serialize};
3use specs::{Component, DerefFlaggedStorage};
4use std::{error::Error, fmt};
5
6use crate::{
7 combat::{AttackEffect, AttackedModification, CombatRequirement, StatEffect},
8 comp::projectile::ProjectileConstructorEffect,
9 uid::Uid,
10};
11
12use super::Body;
13
14#[derive(Debug)]
15#[expect(dead_code)] pub enum StatChangeError {
17 Underflow,
18 Overflow,
19}
20
21#[derive(Debug, Clone, Copy, Deserialize, Serialize)]
22pub struct StatsModifier {
23 pub add_mod: f32,
24 pub mult_mod: f32,
25}
26
27impl Default for StatsModifier {
28 fn default() -> Self {
29 Self {
30 add_mod: 0.0,
31 mult_mod: 1.0,
32 }
33 }
34}
35
36#[derive(Debug, Clone, Copy, Deserialize, Serialize)]
37pub struct StatsSplit {
38 pub pos_mod: f32,
39 pub neg_mod: f32,
40}
41
42impl Default for StatsSplit {
43 fn default() -> Self {
44 Self {
45 pos_mod: 0.0,
46 neg_mod: 0.0,
47 }
48 }
49}
50
51impl StatsSplit {
52 pub fn modifier(&self) -> f32 { self.pos_mod + self.neg_mod }
53}
54
55impl StatsModifier {
56 pub fn compute_maximum(&self, base_value: f32) -> f32 {
57 base_value * self.mult_mod + self.add_mod
58 }
59
60 pub fn update_maximum(&self) -> bool {
62 self.add_mod.abs() > f32::EPSILON || (self.mult_mod - 1.0).abs() > f32::EPSILON
63 }
64}
65
66impl fmt::Display for StatChangeError {
67 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
68 write!(f, "{}", match self {
69 Self::Underflow => "insufficient stat quantity",
70 Self::Overflow => "stat quantity would overflow",
71 })
72 }
73}
74impl Error for StatChangeError {}
75
76#[derive(Clone, Debug, Serialize, Deserialize)]
77pub struct Stats {
78 pub name: Content,
79 pub original_body: Body,
80 pub damage_reduction: StatsSplit,
81 pub poise_reduction: StatsSplit,
82 pub max_health_modifiers: StatsModifier,
83 pub move_speed_modifier: f32,
84 pub charge_move_speed_modifier: f32,
85 pub buildup_move_speed_modifier: f32,
86 pub jump_modifier: f32,
87 pub attack_speed_modifier: f32,
88 pub charge_speed_modifier: f32,
89 pub buildup_speed_modifier: f32,
90 pub recovery_speed_modifier: f32,
91 pub friction_modifier: f32,
92 pub max_energy_modifiers: StatsModifier,
93 pub poise_damage_modifier: f32,
94 pub attack_damage_modifier: f32,
95 pub conditional_precision_modifiers: Vec<(Option<CombatRequirement>, f32, bool)>,
96 pub precision_vulnerability_multiplier_override: Option<f32>,
97 pub swim_speed_modifier: f32,
98 pub effects_on_attack: Vec<AttackEffect>,
100 pub mitigations_penetration: f32,
103 pub energy_reward_modifier: f32,
104 pub energy_efficiency_modifier: f32,
105 pub effects_on_damaged: Vec<StatEffect>,
107 pub effects_on_death: Vec<StatEffect>,
109 pub disable_auxiliary_abilities: bool,
110 pub crowd_control_resistance: f32,
111 pub item_effect_reduction: f32,
112 pub attacked_modifications: Vec<AttackedModification>,
114 pub precision_power_mult: f32,
115 pub knockback_mult: f32,
116 pub projectile_speed_mult: f32,
117 pub projectile_constructor_effects: Vec<ProjectileConstructorEffect>,
118 pub marked_entities: Vec<Uid>,
122}
123
124impl Stats {
125 pub fn new(name: Content, body: Body) -> Self {
126 Self {
127 name,
128 original_body: body,
129 damage_reduction: StatsSplit::default(),
130 poise_reduction: StatsSplit::default(),
131 max_health_modifiers: StatsModifier::default(),
132 move_speed_modifier: 1.0,
133 charge_move_speed_modifier: 1.0,
134 buildup_move_speed_modifier: 1.0,
135 jump_modifier: 1.0,
136 attack_speed_modifier: 1.0,
137 recovery_speed_modifier: 1.0,
138 charge_speed_modifier: 1.0,
139 buildup_speed_modifier: 1.0,
140 friction_modifier: 1.0,
141 max_energy_modifiers: StatsModifier::default(),
142 poise_damage_modifier: 1.0,
143 attack_damage_modifier: 1.0,
144 conditional_precision_modifiers: Vec::new(),
145 precision_vulnerability_multiplier_override: None,
146 swim_speed_modifier: 1.0,
147 effects_on_attack: Vec::new(),
148 mitigations_penetration: 0.0,
149 energy_reward_modifier: 1.0,
150 energy_efficiency_modifier: 1.0,
151 effects_on_damaged: Vec::new(),
152 effects_on_death: Vec::new(),
153 disable_auxiliary_abilities: false,
154 crowd_control_resistance: 0.0,
155 item_effect_reduction: 1.0,
156 attacked_modifications: Vec::new(),
157 precision_power_mult: 1.0,
158 knockback_mult: 1.0,
159 projectile_speed_mult: 1.0,
160 projectile_constructor_effects: Vec::new(),
161 marked_entities: Vec::new(),
162 }
163 }
164
165 pub fn empty(body: Body) -> Self { Self::new(Content::dummy(), body) }
168
169 pub fn reset_temp_modifiers(&mut self) {
171 let name = std::mem::replace(&mut self.name, Content::dummy());
173 let body = self.original_body;
174
175 *self = Self::new(name, body);
176 }
177}
178
179impl Component for Stats {
180 type Storage = DerefFlaggedStorage<Self, specs::VecStorage<Self>>;
181}