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