veloren_common/states/
basic_block.rs

1use super::utils::*;
2use crate::{
3    combat::AttackSource,
4    comp::{
5        CharacterState, StateUpdate,
6        character_state::{AttackFilters, OutputEvents},
7    },
8    states::behavior::{CharacterBehavior, JoinData},
9};
10use serde::{Deserialize, Serialize};
11use std::time::Duration;
12
13#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
14pub struct ParryWindow {
15    pub buildup: bool,
16    pub recover: bool,
17}
18
19/// Separated out to condense update portions of character state
20#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
21pub struct StaticData {
22    /// How long until state should deal damage
23    pub buildup_duration: Duration,
24    /// How long the state has until exiting
25    pub recover_duration: Duration,
26    /// Max angle (45.0 will give you a 90.0 angle window)
27    pub max_angle: f32,
28    /// Base value that incoming damage is reduced by and converted to poise
29    /// damage
30    pub block_strength: f32,
31    /// What durations are considered a parry
32    pub parry_window: ParryWindow,
33    /// What key is used to press ability
34    pub ability_info: AbilityInfo,
35    /// Energy consumed to initiate the block
36    pub energy_cost: f32,
37    /// Energy recovered upon successful parry
38    pub energy_regen: f32,
39    /// Whether block can be held
40    pub can_hold: bool,
41    /// What kinds of attacks the block applies to
42    pub blocked_attacks: AttackFilters,
43}
44
45#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
46pub struct Data {
47    /// Struct containing data that does not change over the course of the
48    /// character state
49    pub static_data: StaticData,
50    /// Timer for each stage
51    pub timer: Duration,
52    /// What section the character stage is in
53    pub stage_section: StageSection,
54    // Whether there was parry
55    pub is_parry: bool,
56}
57
58impl CharacterBehavior for Data {
59    fn behavior(&self, data: &JoinData, output_events: &mut OutputEvents) -> StateUpdate {
60        let mut update = StateUpdate::from(data);
61
62        handle_orientation(data, &mut update, 1.0, None);
63        handle_move(data, &mut update, 0.4);
64
65        match self.stage_section {
66            StageSection::Buildup => {
67                if self.timer < self.static_data.buildup_duration {
68                    // Build up
69                    update.character = CharacterState::BasicBlock(Data {
70                        timer: tick_attack_or_default(data, self.timer, None),
71                        ..*self
72                    });
73                } else {
74                    // Transitions to swing section of stage
75                    update.character = CharacterState::BasicBlock(Data {
76                        timer: Duration::default(),
77                        stage_section: if self.static_data.can_hold {
78                            StageSection::Action
79                        } else {
80                            StageSection::Recover
81                        },
82                        ..*self
83                    });
84                }
85            },
86            StageSection::Action => {
87                if self.static_data.can_hold
88                    && input_is_pressed(data, self.static_data.ability_info.input)
89                {
90                    // Block
91                    update.character = CharacterState::BasicBlock(Data {
92                        timer: tick_attack_or_default(data, self.timer, None),
93                        ..*self
94                    });
95                } else {
96                    // Transitions to recover section of stage
97                    update.character = CharacterState::BasicBlock(Data {
98                        timer: Duration::default(),
99                        stage_section: StageSection::Recover,
100                        ..*self
101                    });
102                }
103            },
104            StageSection::Recover => {
105                if (self.static_data.parry_window.recover || !self.is_parry)
106                    && self.timer < self.static_data.recover_duration
107                {
108                    // Recovery
109                    update.character = CharacterState::BasicBlock(Data {
110                        timer: tick_attack_or_default(
111                            data,
112                            self.timer,
113                            Some(data.stats.recovery_speed_modifier),
114                        ),
115                        ..*self
116                    });
117                } else {
118                    // Done
119                    end_ability(data, &mut update);
120                }
121            },
122            _ => {
123                // If it somehow ends up in an incorrect stage section
124                end_ability(data, &mut update);
125            },
126        }
127
128        // At end of state logic so an interrupt isn't overwritten
129        handle_interrupts(data, &mut update, output_events);
130
131        update
132    }
133}
134
135impl Data {
136    pub fn is_parry(&self, attack: AttackSource) -> bool {
137        let could_block = self.static_data.blocked_attacks.applies(attack);
138        let timed = match self.stage_section {
139            StageSection::Buildup => self.static_data.parry_window.buildup,
140            StageSection::Recover => self.static_data.parry_window.recover,
141            _ => false,
142        };
143        could_block && timed
144    }
145}