veloren_common/states/
rapid_melee.rs

1use crate::{
2    Explosion, RadiusEffect, combat,
3    combat::{Attack, AttackDamage, Damage, DamageKind::Crushing, DamageSource, GroupTarget},
4    comp::{
5        CharacterState, MeleeConstructor, StateUpdate, character_state::OutputEvents, item::Reagent,
6    },
7    event::{ComboChangeEvent, ExplosionEvent},
8    states::{
9        behavior::{CharacterBehavior, JoinData},
10        utils::*,
11    },
12};
13use serde::{Deserialize, Serialize};
14use std::time::Duration;
15use vek::Vec3;
16
17/// Separated out to condense update portions of character state
18#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
19pub struct StaticData {
20    /// How long until the state attacks
21    pub buildup_duration: Duration,
22    /// How long the state is in the swing duration
23    pub swing_duration: Duration,
24    /// How long until state ends
25    pub recover_duration: Duration,
26    /// Used to construct the Melee attack
27    pub melee_constructor: MeleeConstructor,
28    /// Energy cost per attack
29    pub energy_cost: f32,
30    /// Maximum number of consecutive strikes, if there is a max
31    pub max_strikes: Option<u32>,
32    pub move_modifier: f32,
33    pub ori_modifier: f32,
34    pub minimum_combo: u32,
35    /// Used to indicate to the frontend what ability this is for any special
36    /// effects
37    pub frontend_specifier: Option<FrontendSpecifier>,
38    /// What key is used to press ability
39    pub ability_info: AbilityInfo,
40}
41
42#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
43pub struct Data {
44    /// Struct containing data that does not change over the course of the
45    /// character state
46    pub static_data: StaticData,
47    /// Timer for each stage
48    pub timer: Duration,
49    /// How many spins it has done
50    pub current_strike: u32,
51    /// What section the character stage is in
52    pub stage_section: StageSection,
53    /// Whether the state can deal damage
54    pub exhausted: bool,
55}
56
57impl CharacterBehavior for Data {
58    fn behavior(&self, data: &JoinData, output_events: &mut OutputEvents) -> StateUpdate {
59        let mut update = StateUpdate::from(data);
60
61        handle_orientation(data, &mut update, self.static_data.ori_modifier, None);
62        handle_move(data, &mut update, self.static_data.move_modifier);
63        handle_interrupts(data, &mut update, output_events);
64
65        match self.stage_section {
66            StageSection::Buildup => {
67                if self.timer < self.static_data.buildup_duration {
68                    // Build up
69                    if let CharacterState::RapidMelee(c) = &mut update.character {
70                        c.timer = tick_attack_or_default(data, self.timer, None);
71                    }
72                } else {
73                    // Transitions to swing section of stage
74                    if let CharacterState::RapidMelee(c) = &mut update.character {
75                        c.timer = Duration::default();
76                        c.stage_section = StageSection::Action;
77                    }
78                }
79            },
80            StageSection::Action => {
81                if !self.exhausted {
82                    if let CharacterState::RapidMelee(c) = &mut update.character {
83                        c.timer = Duration::default();
84                        c.exhausted = true;
85                    }
86
87                    let precision_mult = combat::compute_precision_mult(data.inventory, data.msm);
88                    let tool_stats = get_tool_stats(data, self.static_data.ability_info);
89
90                    data.updater.insert(
91                        data.entity,
92                        self.static_data
93                            .melee_constructor
94                            .create_melee(precision_mult, tool_stats),
95                    );
96                } else if self.timer < self.static_data.swing_duration {
97                    // Swings
98                    if let CharacterState::RapidMelee(c) = &mut update.character {
99                        c.timer = tick_attack_or_default(data, self.timer, None);
100                    }
101                } else if match self.static_data.max_strikes {
102                    Some(max) => self.current_strike < max,
103                    None => input_is_pressed(data, self.static_data.ability_info.input),
104                } && update
105                    .energy
106                    .try_change_by(-self.static_data.energy_cost)
107                    .is_ok()
108                {
109                    if self.static_data.frontend_specifier == Some(FrontendSpecifier::CultistVortex)
110                    {
111                        let damage = AttackDamage::new(
112                            Damage {
113                                source: DamageSource::Explosion,
114                                kind: Crushing,
115                                value: 10.0,
116                            },
117                            Some(GroupTarget::OutOfGroup),
118                            rand::random(),
119                        );
120                        let attack = Attack::default().with_damage(damage);
121                        let explosion = Explosion {
122                            effects: vec![RadiusEffect::Attack(attack)],
123                            radius: data.body.max_radius() * 4.0,
124                            reagent: Some(Reagent::Purple),
125                            min_falloff: 0.5,
126                        };
127                        let pos =
128                            data.pos.0 + (*data.ori.look_dir() * (data.body.max_radius() * 3.0));
129                        let explosition =
130                            Vec3::new(pos.x, pos.y, pos.z + (data.body.height() / 4.0));
131                        output_events.emit_server(ExplosionEvent {
132                            pos: explosition,
133                            explosion,
134                            owner: Some(*data.uid),
135                        });
136                    }
137                    if let CharacterState::RapidMelee(c) = &mut update.character {
138                        c.timer = Duration::default();
139                        c.current_strike += 1;
140                        c.exhausted = false;
141                    }
142                } else {
143                    // Transitions to recover section of stage
144                    if let CharacterState::RapidMelee(c) = &mut update.character {
145                        c.timer = Duration::default();
146                        c.stage_section = StageSection::Recover;
147                    }
148                }
149
150                // Consume combo if any was required
151                if self.static_data.minimum_combo > 0 {
152                    output_events.emit_server(ComboChangeEvent {
153                        entity: data.entity,
154                        change: -data.combo.map_or(0, |c| c.counter() as i32),
155                    });
156                }
157            },
158            StageSection::Recover => {
159                if self.timer < self.static_data.recover_duration {
160                    // Recover
161                    if let CharacterState::RapidMelee(c) = &mut update.character {
162                        c.timer = tick_attack_or_default(
163                            data,
164                            self.timer,
165                            Some(data.stats.recovery_speed_modifier),
166                        );
167                    }
168                } else {
169                    // Done
170                    end_melee_ability(data, &mut update);
171                }
172            },
173            _ => {
174                // If it somehow ends up in an incorrect stage section
175                end_melee_ability(data, &mut update);
176            },
177        }
178
179        update
180    }
181}
182
183#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
184pub enum FrontendSpecifier {
185    CultistVortex,
186    Whirlwind,
187}