veloren_common/states/
leap_melee.rs

1use crate::{
2    combat::{self, CombatEffect},
3    comp::{CharacterState, MeleeConstructor, StateUpdate, character_state::OutputEvents},
4    event::LocalEvent,
5    outcome::Outcome,
6    states::{
7        behavior::{CharacterBehavior, JoinData},
8        utils::{StageSection, *},
9    },
10};
11use serde::{Deserialize, Serialize};
12use std::time::Duration;
13
14/// Separated out to condense update portions of character state
15#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
16pub struct StaticData {
17    /// How long the state is moving
18    pub movement_duration: Duration,
19    /// How long until state should deal damage
20    pub buildup_duration: Duration,
21    /// How long the weapon swings
22    pub swing_duration: Duration,
23    /// How long the state has until exiting
24    pub recover_duration: Duration,
25    /// Used to construct the Melee attack
26    pub melee_constructor: MeleeConstructor,
27    /// Used to specify the sfx to the frontend
28    pub specifier: Option<FrontendSpecifier>,
29    /// Affects how far forward the player leaps
30    pub forward_leap_strength: f32,
31    /// Affects how high the player leaps
32    pub vertical_leap_strength: f32,
33    /// What key is used to press ability
34    pub ability_info: AbilityInfo,
35    pub damage_effect: Option<CombatEffect>,
36}
37
38#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
39pub struct Data {
40    /// Struct containing data that does not change over the course of the
41    /// character state
42    pub static_data: StaticData,
43    /// Timer for each stage
44    pub timer: Duration,
45    /// What section the character stage is in
46    pub stage_section: StageSection,
47    /// Whether the attack can deal more damage
48    pub exhausted: bool,
49}
50
51impl CharacterBehavior for Data {
52    fn behavior(&self, data: &JoinData, output_events: &mut OutputEvents) -> StateUpdate {
53        let mut update = StateUpdate::from(data);
54
55        handle_orientation(data, &mut update, 1.0, None);
56        handle_move(data, &mut update, 0.3);
57        handle_jump(data, output_events, &mut update, 1.0);
58
59        match self.stage_section {
60            // Delay before leaping into the air
61            StageSection::Buildup => {
62                // Wait for `buildup_duration` to expire
63                if self.timer < self.static_data.buildup_duration {
64                    update.character = CharacterState::LeapMelee(Data {
65                        timer: tick_attack_or_default(data, self.timer, None),
66                        ..*self
67                    });
68                } else {
69                    // Transitions to leap portion of state after buildup delay
70                    update.character = CharacterState::LeapMelee(Data {
71                        timer: Duration::default(),
72                        stage_section: StageSection::Movement,
73                        ..*self
74                    });
75
76                    // Send local event used for frontend shenanigans
77                    match self.static_data.specifier {
78                        Some(FrontendSpecifier::LeapWhoosh) => {
79                            output_events.emit_local(LocalEvent::CreateOutcome(Outcome::Whoosh {
80                                pos: data.pos.0 + *data.ori.look_dir() * (data.body.max_radius()),
81                            }));
82                        },
83                        Some(FrontendSpecifier::LeapSwoosh) => {
84                            output_events.emit_local(LocalEvent::CreateOutcome(Outcome::Swoosh {
85                                pos: data.pos.0 + *data.ori.look_dir() * (data.body.max_radius()),
86                            }));
87                        },
88                        _ => {},
89                    };
90                }
91            },
92            StageSection::Movement => {
93                if self.timer < self.static_data.movement_duration {
94                    // Apply jumping force
95                    let progress = 1.0
96                        - self.timer.as_secs_f32()
97                            / self.static_data.movement_duration.as_secs_f32();
98                    handle_forced_movement(data, &mut update, ForcedMovement::Leap {
99                        vertical: self.static_data.vertical_leap_strength,
100                        forward: self.static_data.forward_leap_strength,
101                        progress,
102                        direction: MovementDirection::Look,
103                    });
104
105                    // Increment duration
106                    // If we were to set a timeout for state, this would be
107                    // outside if block and have else check for > movement
108                    // duration * some multiplier
109                    update.character = CharacterState::LeapMelee(Data {
110                        timer: tick_attack_or_default(data, self.timer, None),
111                        ..*self
112                    });
113                } else if data.physics.on_ground.is_some() | data.physics.in_liquid().is_some() {
114                    // Transitions to swing portion of state upon hitting ground
115                    update.character = CharacterState::LeapMelee(Data {
116                        timer: Duration::default(),
117                        stage_section: StageSection::Action,
118                        ..*self
119                    });
120                }
121            },
122            StageSection::Action => {
123                if self.timer < self.static_data.swing_duration {
124                    // Swings weapons
125                    update.character = CharacterState::LeapMelee(Data {
126                        timer: tick_attack_or_default(data, self.timer, None),
127                        ..*self
128                    });
129                } else {
130                    // Transitions to recover portion
131                    update.character = CharacterState::LeapMelee(Data {
132                        timer: Duration::default(),
133                        stage_section: StageSection::Recover,
134                        ..*self
135                    });
136                }
137            },
138            StageSection::Recover => {
139                if !self.exhausted {
140                    let precision_mult = combat::compute_precision_mult(data.inventory, data.msm);
141                    let tool_stats = get_tool_stats(data, self.static_data.ability_info);
142
143                    data.updater.insert(
144                        data.entity,
145                        self.static_data
146                            .melee_constructor
147                            .create_melee(precision_mult, tool_stats),
148                    );
149
150                    update.character = CharacterState::LeapMelee(Data {
151                        timer: tick_attack_or_default(
152                            data,
153                            self.timer,
154                            Some(data.stats.recovery_speed_modifier),
155                        ),
156                        exhausted: true,
157                        ..*self
158                    });
159                } else if self.timer < self.static_data.recover_duration {
160                    // Complete recovery delay before finishing state
161                    update.character = CharacterState::LeapMelee(Data {
162                        timer: tick_attack_or_default(
163                            data,
164                            self.timer,
165                            Some(data.stats.recovery_speed_modifier),
166                        ),
167                        ..*self
168                    });
169                } else {
170                    // Done
171                    end_melee_ability(data, &mut update);
172                }
173            },
174            _ => {
175                // If it somehow ends up in an incorrect stage section
176                end_melee_ability(data, &mut update);
177            },
178        }
179
180        // At end of state logic so an interrupt isn't overwritten
181        handle_interrupts(data, &mut update, output_events);
182
183        update
184    }
185}
186
187#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
188pub enum FrontendSpecifier {
189    LeapWhoosh,
190    LeapSwoosh,
191}