veloren_common/states/
self_buff.rs

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