veloren_common/states/
leap_melee.rs

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