veloren_common/states/
dive_melee.rs

1use crate::{
2    combat,
3    comp::{CharacterState, MeleeConstructor, StateUpdate, character_state::OutputEvents},
4    states::{
5        behavior::{CharacterBehavior, JoinData},
6        utils::{StageSection, *},
7    },
8};
9use serde::{Deserialize, Serialize};
10use std::time::Duration;
11
12/// Separated out to condense update portions of character state
13#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
14pub struct StaticData {
15    /// If Some(_), the state can be activated on the ground
16    pub buildup_duration: Option<Duration>,
17    /// How long the state is moving
18    pub movement_duration: Duration,
19    /// How long the weapon swings
20    pub swing_duration: Duration,
21    /// How long the state has until exiting
22    pub recover_duration: Duration,
23    /// The minimum vertical speed the state needed
24    pub vertical_speed: f32,
25    /// Used to construct the Melee attack
26    pub melee_constructor: MeleeConstructor,
27    /// Caps the amount of scaling the attack will receive from vertical speed
28    pub max_scaling: f32,
29    /// What key is used to press ability
30    pub ability_info: AbilityInfo,
31}
32
33#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
34pub struct Data {
35    /// Struct containing data that does not change over the course of the
36    /// character state
37    pub static_data: StaticData,
38    /// Timer for each stage
39    pub timer: Duration,
40    /// What section the character stage is in
41    pub stage_section: StageSection,
42    /// Whether the attack can deal more damage
43    pub exhausted: bool,
44    /// The maximum negative vertical velocity achieved during the state
45    pub max_vertical_speed: f32,
46}
47
48impl CharacterBehavior for Data {
49    fn behavior(&self, data: &JoinData, _output_events: &mut OutputEvents) -> StateUpdate {
50        let mut update = StateUpdate::from(data);
51
52        if let CharacterState::DiveMelee(c) = &mut update.character {
53            c.max_vertical_speed = c.max_vertical_speed.max(-data.vel.0.z);
54        }
55
56        match self.stage_section {
57            StageSection::Buildup => {
58                handle_orientation(data, &mut update, 0.5, None);
59                handle_move(data, &mut update, 0.3);
60
61                if let Some(buildup_duration) = self.static_data.buildup_duration {
62                    if self.timer < buildup_duration {
63                        if let CharacterState::DiveMelee(c) = &mut update.character {
64                            c.timer = tick_attack_or_default(data, self.timer, None);
65                        }
66                    } else if let CharacterState::DiveMelee(c) = &mut update.character {
67                        c.timer = Duration::default();
68                        c.stage_section = StageSection::Action;
69                    }
70                } else if let CharacterState::DiveMelee(c) = &mut update.character {
71                    c.timer = Duration::default();
72                    c.stage_section = StageSection::Action;
73                }
74            },
75            StageSection::Movement => {
76                handle_move(data, &mut update, 1.0);
77                if data.physics.on_ground.is_some() {
78                    // Transitions to swing portion of state upon hitting ground
79                    if let CharacterState::DiveMelee(c) = &mut update.character {
80                        c.timer = Duration::default();
81                        c.stage_section = StageSection::Action;
82                    }
83                } else if self.timer < self.static_data.movement_duration {
84                    if let CharacterState::DiveMelee(c) = &mut update.character {
85                        c.timer = self
86                            .timer
87                            .checked_add(Duration::from_secs_f32(data.dt.0))
88                            .unwrap_or_default();
89                    }
90                } else {
91                    // In character state for too long, leaving in case somethign went wrong
92                    end_ability(data, &mut update);
93                }
94            },
95            StageSection::Action => {
96                if !self.exhausted {
97                    // Attack
98                    let precision_mult = combat::compute_precision_mult(data.inventory, data.msm);
99                    let tool_stats = get_tool_stats(data, self.static_data.ability_info);
100                    let scaling = self.max_vertical_speed / self.static_data.vertical_speed;
101                    let scaling = scaling.min(self.static_data.max_scaling);
102
103                    data.updater.insert(
104                        data.entity,
105                        self.static_data
106                            .melee_constructor
107                            .handle_scaling(scaling)
108                            .create_melee(precision_mult, tool_stats),
109                    );
110
111                    if let CharacterState::DiveMelee(c) = &mut update.character {
112                        c.timer = tick_attack_or_default(data, self.timer, None);
113                        c.exhausted = true;
114                    }
115                } else if self.timer < self.static_data.swing_duration {
116                    if let CharacterState::DiveMelee(c) = &mut update.character {
117                        c.timer = tick_attack_or_default(data, self.timer, None);
118                    }
119                } else {
120                    // Transitions to recover portion
121                    if let CharacterState::DiveMelee(c) = &mut update.character {
122                        c.timer = Duration::default();
123                        c.stage_section = StageSection::Recover;
124                    }
125                }
126            },
127            StageSection::Recover => {
128                handle_orientation(data, &mut update, 0.5, None);
129                handle_move(data, &mut update, 0.3);
130
131                if self.timer < self.static_data.recover_duration {
132                    // Complete recovery delay before finishing state
133                    if let CharacterState::DiveMelee(c) = &mut update.character {
134                        c.timer = tick_attack_or_default(
135                            data,
136                            self.timer,
137                            Some(data.stats.recovery_speed_modifier),
138                        );
139                    }
140                } else {
141                    // Done
142                    end_melee_ability(data, &mut update);
143                }
144            },
145            _ => {
146                // If it somehow ends up in an incorrect stage section
147                end_melee_ability(data, &mut update);
148            },
149        }
150
151        update
152    }
153}