veloren_common/comp/
stats.rs1use common_i18n::Content;
2use serde::{Deserialize, Serialize};
3use specs::{Component, DerefFlaggedStorage};
4use std::{error::Error, fmt};
5
6use crate::combat::{AttackEffect, DamagedEffect, DeathEffect};
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 precision_multiplier_override: Option<f32>,
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<DamagedEffect>,
98 pub effects_on_death: Vec<DeathEffect>,
100 pub disable_auxiliary_abilities: bool,
101 pub crowd_control_resistance: f32,
102 pub item_effect_reduction: f32,
103}
104
105impl Stats {
106 pub fn new(name: Content, body: Body) -> Self {
107 Self {
108 name,
109 original_body: body,
110 damage_reduction: StatsSplit::default(),
111 poise_reduction: StatsSplit::default(),
112 max_health_modifiers: StatsModifier::default(),
113 move_speed_modifier: 1.0,
114 jump_modifier: 1.0,
115 attack_speed_modifier: 1.0,
116 recovery_speed_modifier: 1.0,
117 friction_modifier: 1.0,
118 max_energy_modifiers: StatsModifier::default(),
119 poise_damage_modifier: 1.0,
120 attack_damage_modifier: 1.0,
121 precision_multiplier_override: None,
122 precision_vulnerability_multiplier_override: None,
123 swim_speed_modifier: 1.0,
124 effects_on_attack: Vec::new(),
125 mitigations_penetration: 0.0,
126 energy_reward_modifier: 1.0,
127 effects_on_damaged: Vec::new(),
128 effects_on_death: Vec::new(),
129 disable_auxiliary_abilities: false,
130 crowd_control_resistance: 0.0,
131 item_effect_reduction: 1.0,
132 }
133 }
134
135 pub fn empty(body: Body) -> Self { Self::new(Content::dummy(), body) }
138
139 pub fn reset_temp_modifiers(&mut self) {
141 let name = std::mem::replace(&mut self.name, Content::dummy());
143 let body = self.original_body;
144
145 *self = Self::new(name, body);
146 }
147}
148
149impl Component for Stats {
150 type Storage = DerefFlaggedStorage<Self, specs::VecStorage<Self>>;
151}