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, self.static_data.ability_info.tool),
80                            self.static_data.range,
81                            // check for indefinite aura
82                            self.static_data.aura_duration,
83                            targets,
84                            *data.time,
85                            self.static_data.specifier,
86                        );
87                        if self.static_data.scales_with_combo {
88                            match aura.aura_kind {
89                                AuraKind::Buff {
90                                    kind: _,
91                                    ref mut data,
92                                    category: _,
93                                    source: _,
94                                } => {
95                                    data.strength *=
96                                        (self.static_data.combo_at_cast.max(1) as f32).sqrt();
97                                },
98                                AuraKind::FriendlyFire | AuraKind::ForcePvP => {},
99                            }
100                            output_events.emit_server(ComboChangeEvent {
101                                entity: data.entity,
102                                change: -(self.static_data.combo_at_cast as i32),
103                            });
104                        }
105                        output_events.emit_server(AuraEvent {
106                            entity: data.entity,
107                            aura_change: AuraChange::Add(aura),
108                        });
109                    }
110                    // Build up
111                    update.character = CharacterState::BasicAura(Data {
112                        static_data: self.static_data.clone(),
113                        timer: Duration::default(),
114                        stage_section: StageSection::Action,
115                    });
116                }
117            },
118            StageSection::Action => {
119                if self.timer < self.static_data.cast_duration {
120                    // Cast
121                    update.character = CharacterState::BasicAura(Data {
122                        static_data: self.static_data.clone(),
123                        timer: tick_attack_or_default(data, self.timer, None),
124                        ..*self
125                    });
126                } else {
127                    update.character = CharacterState::BasicAura(Data {
128                        static_data: self.static_data.clone(),
129                        timer: Duration::default(),
130                        stage_section: StageSection::Recover,
131                    });
132                }
133            },
134            StageSection::Recover => {
135                if self.timer < self.static_data.recover_duration {
136                    update.character = CharacterState::BasicAura(Data {
137                        static_data: self.static_data.clone(),
138                        timer: tick_attack_or_default(
139                            data,
140                            self.timer,
141                            Some(data.stats.recovery_speed_modifier),
142                        ),
143                        ..*self
144                    });
145                } else {
146                    // Done
147                    end_ability(data, &mut update);
148                }
149            },
150            _ => {
151                // If it somehow ends up in an incorrect stage section
152                end_ability(data, &mut update);
153            },
154        }
155
156        // At end of state logic so an interrupt isn't overwritten
157        handle_interrupts(data, &mut update, output_events);
158
159        update
160    }
161}