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