veloren_common/states/
self_buff.rs

1use crate::{
2    comp::{
3        CharacterState, StateUpdate,
4        buff::{Buff, BuffCategory, BuffChange, BuffData, BuffKind, BuffSource, DestInfo},
5        character_state::OutputEvents,
6    },
7    event::{BuffEvent, ComboChangeEvent, LocalEvent},
8    outcome::Outcome,
9    resources::Secs,
10    states::{
11        behavior::{CharacterBehavior, JoinData},
12        utils::*,
13    },
14};
15use serde::{Deserialize, Serialize};
16use std::time::Duration;
17
18/// Separated out to condense update portions of character state
19#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
20pub struct StaticData {
21    /// How long until state should create the aura
22    pub buildup_duration: Duration,
23    /// How long the state is creating an aura
24    pub cast_duration: Duration,
25    /// How long the state has until exiting
26    pub recover_duration: Duration,
27    /// What kind of buff is created
28    pub buff_kind: BuffKind,
29    /// Strength of the created buff
30    pub buff_strength: f32,
31    /// How long buff lasts
32    pub buff_duration: Option<Secs>,
33    /// This is the minimum amount of combo required to enter this character
34    /// state
35    pub combo_cost: u32,
36    pub combo_scaling: Option<ScalingKind>,
37    /// This is the amount of combo held by the entity when this character state
38    /// was entered
39    pub combo_on_use: u32,
40    /// Controls whether `SelfBuff`s that were previously applied should be
41    /// removed
42    pub enforced_limit: bool,
43    /// What key is used to press ability
44    pub ability_info: AbilityInfo,
45    /// Used to specify an outcome for the buff
46    pub specifier: Option<FrontendSpecifier>,
47}
48
49#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
50pub struct Data {
51    /// Struct containing data that does not change over the course of the
52    /// character state
53    pub static_data: StaticData,
54    /// Timer for each stage
55    pub timer: Duration,
56    /// What section the character stage is in
57    pub stage_section: StageSection,
58}
59
60impl CharacterBehavior for Data {
61    fn behavior(&self, data: &JoinData, output_events: &mut OutputEvents) -> StateUpdate {
62        let mut update = StateUpdate::from(data);
63
64        handle_move(data, &mut update, 0.8);
65        handle_jump(data, output_events, &mut update, 1.0);
66
67        match self.stage_section {
68            StageSection::Buildup => {
69                if self.timer < self.static_data.buildup_duration {
70                    // Build up
71                    update.character = CharacterState::SelfBuff(Data {
72                        timer: tick_attack_or_default(data, self.timer, None),
73                        ..*self
74                    });
75                } else {
76                    // Consume combo
77                    let combo_consumption = if self.static_data.combo_scaling.is_some() {
78                        self.static_data.combo_on_use
79                    } else {
80                        self.static_data.combo_cost
81                    };
82                    output_events.emit_server(ComboChangeEvent {
83                        entity: data.entity,
84                        change: -(combo_consumption as i32),
85                    });
86
87                    let scaling_factor = self.static_data.combo_scaling.map_or(1.0, |cs| {
88                        cs.factor(
89                            self.static_data.combo_on_use as f32,
90                            self.static_data.combo_cost as f32,
91                        )
92                    });
93
94                    let mut buff_cat_ids = if self
95                        .static_data
96                        .ability_info
97                        .ability
98                        .is_some_and(|a| a.ability.is_from_wielded())
99                    {
100                        vec![BuffCategory::RemoveOnLoadoutChange]
101                    } else {
102                        Vec::new()
103                    };
104
105                    // Remove previous selfbuffs if we should
106                    if self.static_data.enforced_limit {
107                        buff_cat_ids.push(BuffCategory::SelfBuff);
108
109                        output_events.emit_server(BuffEvent {
110                            entity: data.entity,
111                            buff_change: BuffChange::RemoveByCategory {
112                                all_required: vec![BuffCategory::SelfBuff],
113                                any_required: vec![],
114                                none_required: vec![],
115                            },
116                        });
117                    }
118
119                    let dest_info = DestInfo {
120                        stats: Some(data.stats),
121                        mass: Some(data.mass),
122                    };
123
124                    // Creates buff
125                    let buff = Buff::new(
126                        self.static_data.buff_kind,
127                        BuffData::new(
128                            self.static_data.buff_strength * scaling_factor,
129                            self.static_data.buff_duration,
130                        ),
131                        buff_cat_ids,
132                        BuffSource::Character { by: *data.uid },
133                        *data.time,
134                        dest_info,
135                        Some(data.mass),
136                    );
137                    output_events.emit_server(BuffEvent {
138                        entity: data.entity,
139                        buff_change: BuffChange::Add(buff),
140                    });
141                    // Build up
142                    update.character = CharacterState::SelfBuff(Data {
143                        timer: Duration::default(),
144                        stage_section: StageSection::Action,
145                        ..*self
146                    });
147                }
148            },
149            StageSection::Action => {
150                if self.timer < self.static_data.cast_duration {
151                    // Cast
152                    update.character = CharacterState::SelfBuff(Data {
153                        timer: tick_attack_or_default(data, self.timer, None),
154                        ..*self
155                    });
156                    if let Some(FrontendSpecifier::FromTheAshes) = self.static_data.specifier {
157                        // Send local event used for frontend shenanigans
158                        output_events.emit_local(LocalEvent::CreateOutcome(
159                            Outcome::FromTheAshes { pos: data.pos.0 },
160                        ));
161                    }
162                } else {
163                    update.character = CharacterState::SelfBuff(Data {
164                        timer: Duration::default(),
165                        stage_section: StageSection::Recover,
166                        ..*self
167                    });
168                }
169            },
170            StageSection::Recover => {
171                if self.timer < self.static_data.recover_duration {
172                    update.character = CharacterState::SelfBuff(Data {
173                        timer: tick_attack_or_default(
174                            data,
175                            self.timer,
176                            Some(data.stats.recovery_speed_modifier),
177                        ),
178                        ..*self
179                    });
180                } else {
181                    // Done
182                    end_ability(data, &mut update);
183                }
184            },
185            _ => {
186                // If it somehow ends up in an incorrect stage section
187                end_ability(data, &mut update);
188            },
189        }
190
191        // At end of state logic so an interrupt isn't overwritten
192        handle_interrupts(data, &mut update, output_events);
193
194        update
195    }
196}
197/// Used to specify a particular effect for frontend purposes
198#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
199pub enum FrontendSpecifier {
200    FromTheAshes,
201}