veloren_common/comp/
stats.rs

1use 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)] // TODO: remove once trade sim hits master
11pub 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    // Note: unused for now
56    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    /// This adds effects to any attacks that the entity makes
90    pub effects_on_attack: Vec<AttackEffect>,
91    /// This is the fraction of damage reduction (from armor and other buffs)
92    /// that gets ignored by attacks from this entity
93    pub mitigations_penetration: f32,
94    pub energy_reward_modifier: f32,
95    /// This creates effects when the entity is damaged
96    pub effects_on_damaged: Vec<DamagedEffect>,
97    /// This creates effects when the entity is killed
98    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    /// Creates an empty `Stats` instance - used during character loading from
135    /// the database
136    pub fn empty(body: Body) -> Self { Self::new("".to_string(), body) }
137
138    /// Resets temporary modifiers to default values
139    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}