veloren_common/states/
roll.rs

1use crate::{
2    comp::{
3        CharacterState, StateUpdate,
4        buff::{BuffChange, BuffKind},
5        character_state::{AttackFilters, OutputEvents},
6    },
7    event::BuffEvent,
8    states::{
9        behavior::{CharacterBehavior, JoinData},
10        utils::*,
11    },
12    util::Dir,
13};
14use serde::{Deserialize, Serialize};
15use std::time::Duration;
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 state should roll
21    pub buildup_duration: Duration,
22    /// How long state is rolling for
23    pub movement_duration: Duration,
24    /// How long it takes to recover from roll
25    pub recover_duration: Duration,
26    /// Affects the speed and distance of the roll
27    pub roll_strength: f32,
28    /// Affects whether you are immune to various attacks while rolling
29    pub attack_immunities: AttackFilters,
30    /// Information about the ability
31    pub ability_info: AbilityInfo,
32    /// Whether the roll cancelled another ability
33    pub was_cancel: bool,
34}
35
36#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
37pub struct Data {
38    /// Struct containing data that does not change over the course of the
39    /// character state
40    pub static_data: StaticData,
41    /// Timer for each stage
42    pub timer: Duration,
43    /// What section the character stage is in
44    pub stage_section: StageSection,
45    /// Had weapon
46    pub was_wielded: bool,
47    /// What direction were we previously aiming in?
48    pub prev_aimed_dir: Option<Dir>,
49    /// Is sneaking, true if previous state was also considered sneaking
50    pub is_sneaking: bool,
51}
52
53impl CharacterBehavior for Data {
54    fn behavior(&self, data: &JoinData, output_events: &mut OutputEvents) -> StateUpdate {
55        let mut update = StateUpdate::from(data);
56
57        // You should not be able to strafe while rolling
58        update.should_strafe = false;
59
60        // Smooth orientation
61        handle_orientation(data, &mut update, 2.5, None);
62
63        match self.stage_section {
64            StageSection::Buildup => {
65                handle_move(data, &mut update, 0.3);
66                if self.timer < self.static_data.buildup_duration {
67                    // Build up
68                    update.character = CharacterState::Roll(Data {
69                        timer: tick_attack_or_default(data, self.timer, None),
70                        ..*self
71                    });
72                } else {
73                    // Remove burning effect if active
74                    output_events.emit_server(BuffEvent {
75                        entity: data.entity,
76                        buff_change: BuffChange::RemoveByKind(BuffKind::Burning),
77                    });
78                    // Transitions to movement section of stage
79                    update.character = CharacterState::Roll(Data {
80                        timer: Duration::default(),
81                        stage_section: StageSection::Movement,
82                        ..*self
83                    });
84                }
85            },
86            StageSection::Movement => {
87                // Update velocity
88                handle_forced_movement(
89                    data,
90                    &mut update,
91                    ForcedMovement::Forward(
92                        self.static_data.roll_strength
93                            * ((1.0
94                                - self.timer.as_secs_f32()
95                                    / self.static_data.movement_duration.as_secs_f32())
96                                / 2.0
97                                + 0.25),
98                    ),
99                );
100
101                if self.timer < self.static_data.movement_duration {
102                    // Movement
103                    update.character = CharacterState::Roll(Data {
104                        timer: tick_attack_or_default(data, self.timer, None),
105                        ..*self
106                    });
107                } else {
108                    // Transition to recover section of stage
109                    update.character = CharacterState::Roll(Data {
110                        timer: Duration::default(),
111                        stage_section: StageSection::Recover,
112                        ..*self
113                    });
114                }
115            },
116            StageSection::Recover => {
117                handle_move(data, &mut update, 1.0);
118                // Allows for jumps to interrupt recovery in roll
119                if self.timer < self.static_data.recover_duration
120                    && !handle_jump(data, output_events, &mut update, 1.5)
121                {
122                    // Recover
123                    update.character = CharacterState::Roll(Data {
124                        timer: tick_attack_or_default(
125                            data,
126                            self.timer,
127                            Some(data.stats.recovery_speed_modifier),
128                        ),
129                        ..*self
130                    });
131                } else {
132                    // Done
133                    end_ability(data, &mut update);
134                }
135            },
136            _ => {
137                // If it somehow ends up in an incorrect stage section
138                end_ability(data, &mut update);
139            },
140        }
141
142        update
143    }
144}