veloren_common/states/
basic_aura.rs

1use crate::{
2    combat::GroupTarget,
3    comp::{
4        CharacterState, StateUpdate,
5        aura::{AuraBuffConstructor, AuraChange, AuraKind, AuraTarget, Specifier},
6        character_state::OutputEvents,
7    },
8    event::{AuraEvent, ComboChangeEvent},
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(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    /// Determines how the aura selects its targets
28    pub targets: GroupTarget,
29    /// Has information used to construct the auras
30    pub auras: Vec<AuraBuffConstructor>,
31    /// How long aura lasts
32    pub aura_duration: Option<Secs>,
33    /// Radius of aura
34    pub range: f32,
35    /// What key is used to press ability
36    pub ability_info: AbilityInfo,
37    /// Whether the aura's effect scales with the user's current combo
38    pub scales_with_combo: bool,
39    /// Combo at the time the aura is first cast
40    pub combo_at_cast: u32,
41    /// Used to specify aura to the frontend
42    pub specifier: Option<Specifier>,
43}
44
45#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
46pub struct Data {
47    /// Struct containing data that does not change over the course of the
48    /// character state
49    pub static_data: StaticData,
50    /// Timer for each stage
51    pub timer: Duration,
52    /// What section the character stage is in
53    pub stage_section: StageSection,
54}
55
56impl CharacterBehavior for Data {
57    fn behavior(&self, data: &JoinData, output_events: &mut OutputEvents) -> StateUpdate {
58        let mut update = StateUpdate::from(data);
59
60        handle_orientation(data, &mut update, 1.0, None);
61        handle_move(data, &mut update, 0.8);
62        handle_jump(data, output_events, &mut update, 1.0);
63
64        match self.stage_section {
65            StageSection::Buildup => {
66                if self.timer < self.static_data.buildup_duration {
67                    // Build up
68                    update.character = CharacterState::BasicAura(Data {
69                        static_data: self.static_data.clone(),
70                        timer: tick_attack_or_default(data, self.timer, None),
71                        ..*self
72                    });
73                } else {
74                    // Creates aura
75                    let targets =
76                        AuraTarget::from((Some(self.static_data.targets), Some(data.uid)));
77                    for aura_data in &self.static_data.auras {
78                        let mut aura = aura_data.to_aura(
79                            data.uid,
80                            self.static_data.range,
81                            // check for indefinite aura
82                            self.static_data.aura_duration,
83                            targets,
84                            *data.time,
85                        );
86                        if self.static_data.scales_with_combo {
87                            match aura.aura_kind {
88                                AuraKind::Buff {
89                                    kind: _,
90                                    ref mut data,
91                                    category: _,
92                                    source: _,
93                                } => {
94                                    data.strength *=
95                                        (self.static_data.combo_at_cast.max(1) as f32).sqrt();
96                                },
97                                AuraKind::FriendlyFire | AuraKind::ForcePvP => {},
98                            }
99                            output_events.emit_server(ComboChangeEvent {
100                                entity: data.entity,
101                                change: -(self.static_data.combo_at_cast as i32),
102                            });
103                        }
104                        output_events.emit_server(AuraEvent {
105                            entity: data.entity,
106                            aura_change: AuraChange::Add(aura),
107                        });
108                    }
109                    // Build up
110                    update.character = CharacterState::BasicAura(Data {
111                        static_data: self.static_data.clone(),
112                        timer: Duration::default(),
113                        stage_section: StageSection::Action,
114                    });
115                }
116            },
117            StageSection::Action => {
118                if self.timer < self.static_data.cast_duration {
119                    // Cast
120                    update.character = CharacterState::BasicAura(Data {
121                        static_data: self.static_data.clone(),
122                        timer: tick_attack_or_default(data, self.timer, None),
123                        ..*self
124                    });
125                } else {
126                    update.character = CharacterState::BasicAura(Data {
127                        static_data: self.static_data.clone(),
128                        timer: Duration::default(),
129                        stage_section: StageSection::Recover,
130                    });
131                }
132            },
133            StageSection::Recover => {
134                if self.timer < self.static_data.recover_duration {
135                    update.character = CharacterState::BasicAura(Data {
136                        static_data: self.static_data.clone(),
137                        timer: tick_attack_or_default(
138                            data,
139                            self.timer,
140                            Some(data.stats.recovery_speed_modifier),
141                        ),
142                        ..*self
143                    });
144                } else {
145                    // Done
146                    end_ability(data, &mut update);
147                }
148            },
149            _ => {
150                // If it somehow ends up in an incorrect stage section
151                end_ability(data, &mut update);
152            },
153        }
154
155        // At end of state logic so an interrupt isn't overwritten
156        handle_interrupts(data, &mut update, output_events);
157
158        update
159    }
160}