veloren_common/states/
finisher_melee.rs

1use crate::{
2    combat::{self, CombatBuff, CombatEffect, ScalingKind},
3    comp::{CharacterState, MeleeConstructor, StateUpdate, character_state::OutputEvents},
4    states::{
5        behavior::{CharacterBehavior, JoinData},
6        utils::*,
7    },
8};
9use serde::{Deserialize, Serialize};
10use std::time::Duration;
11
12/// Separated out to condense update portions of character state
13#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
14pub struct StaticData {
15    /// How long until state should deal damage
16    pub buildup_duration: Duration,
17    /// How long the state is swinging for
18    pub swing_duration: Duration,
19    /// How long the state has until exiting
20    pub recover_duration: Duration,
21    /// Used to construct the Melee attack
22    pub melee_constructor: MeleeConstructor,
23    /// Used to determine if and how scaling of the melee attack should happen
24    pub scaling: Option<Scaling>,
25    /// Minimum amount of combo needed to activate ability
26    pub minimum_combo: u32,
27    /// Amount of combo when ability was activated
28    pub combo_on_use: u32,
29    pub combo_consumption: ComboConsumption,
30    /// What key is used to press ability
31    pub ability_info: AbilityInfo,
32}
33
34#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
35pub struct Data {
36    /// Struct containing data that does not change over the course of the
37    /// character state
38    pub static_data: StaticData,
39    /// Timer for each stage
40    pub timer: Duration,
41    /// What section the character stage is in
42    pub stage_section: StageSection,
43    /// Whether the attack can deal more damage
44    pub exhausted: bool,
45}
46
47impl CharacterBehavior for Data {
48    fn behavior(&self, data: &JoinData, output_events: &mut OutputEvents) -> StateUpdate {
49        let mut update = StateUpdate::from(data);
50
51        handle_orientation(data, &mut update, 1.0, None);
52        handle_move(data, &mut update, 0.7);
53        handle_jump(data, output_events, &mut update, 1.0);
54        handle_interrupts(data, &mut update, output_events);
55
56        match self.stage_section {
57            StageSection::Buildup => {
58                if self.timer < self.static_data.buildup_duration {
59                    // Build up
60                    if let CharacterState::FinisherMelee(c) = &mut update.character {
61                        c.timer = tick_attack_or_default(data, self.timer, None);
62                    }
63                } else {
64                    // Transitions to swing section of stage
65                    if let CharacterState::FinisherMelee(c) = &mut update.character {
66                        c.timer = Duration::default();
67                        c.stage_section = StageSection::Action;
68                    }
69                }
70            },
71            StageSection::Action => {
72                if !self.exhausted {
73                    if let CharacterState::FinisherMelee(c) = &mut update.character {
74                        c.exhausted = true;
75                    }
76
77                    self.static_data.combo_consumption.consume(
78                        data,
79                        output_events,
80                        self.static_data.minimum_combo,
81                    );
82                    let mut melee_constructor = self.static_data.melee_constructor.clone();
83
84                    if let Some(scaling) = self.static_data.scaling {
85                        let scaling_factor = scaling
86                            .kind
87                            .factor(
88                                self.static_data.combo_on_use as f32,
89                                self.static_data.minimum_combo as f32,
90                            )
91                            .min(scaling.max_factor);
92                        match scaling.target {
93                            ScalingTarget::Attack => {
94                                melee_constructor =
95                                    melee_constructor.handle_scaling(scaling_factor);
96                            },
97                            ScalingTarget::Buff => {
98                                if let Some(CombatEffect::Buff(CombatBuff { strength, .. })) =
99                                    &mut melee_constructor.damage_effect
100                                {
101                                    *strength *= scaling_factor;
102                                }
103                            },
104                        }
105                    }
106
107                    let precision_mult = combat::compute_precision_mult(data.inventory, data.msm);
108                    let tool_stats = get_tool_stats(data, self.static_data.ability_info);
109
110                    data.updater.insert(
111                        data.entity,
112                        melee_constructor.create_melee(
113                            precision_mult,
114                            tool_stats,
115                            self.static_data.ability_info,
116                        ),
117                    );
118                } else if self.timer < self.static_data.swing_duration {
119                    // Swings
120                    if let CharacterState::FinisherMelee(c) = &mut update.character {
121                        c.timer = tick_attack_or_default(data, self.timer, None);
122                    }
123                } else {
124                    // Transitions to recover section of stage
125                    if let CharacterState::FinisherMelee(c) = &mut update.character {
126                        c.timer = Duration::default();
127                        c.stage_section = StageSection::Recover
128                    }
129                }
130            },
131            StageSection::Recover => {
132                if self.timer < self.static_data.recover_duration {
133                    // Recovery
134                    if let CharacterState::FinisherMelee(c) = &mut update.character {
135                        c.timer = tick_attack_or_default(
136                            data,
137                            self.timer,
138                            Some(data.stats.recovery_speed_modifier),
139                        );
140                    }
141                } else {
142                    // Done
143                    end_melee_ability(data, &mut update);
144                }
145            },
146            _ => {
147                // If it somehow ends up in an incorrect stage section
148                end_melee_ability(data, &mut update);
149            },
150        }
151
152        update
153    }
154}
155
156#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
157pub enum ScalingTarget {
158    Attack,
159    Buff,
160}
161
162#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
163pub struct Scaling {
164    pub target: ScalingTarget,
165    pub kind: ScalingKind,
166    pub max_factor: f32,
167}