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