veloren_common/states/
leap_ranged.rs

1use crate::{
2    combat,
3    comp::{
4        Body, CharacterState, LightEmitter, MeleeConstructor, Pos, ProjectileConstructor,
5        StateUpdate, character_state::OutputEvents,
6    },
7    event::ShootEvent,
8    states::{
9        behavior::{CharacterBehavior, JoinData},
10        utils::{StageSection, *},
11    },
12};
13use serde::{Deserialize, Serialize};
14use std::time::Duration;
15
16/// Separated out to condense update portions of character state
17#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
18pub struct StaticData {
19    pub buildup_duration: Duration,
20    pub buildup_melee_timing: f32,
21    pub movement_duration: Duration,
22    pub movement_ranged_timing: f32,
23    pub land_timeout: Duration,
24    pub recover_duration: Duration,
25    pub melee: Option<MeleeConstructor>,
26    pub melee_required: bool,
27    pub projectile: ProjectileConstructor,
28    pub projectile_body: Body,
29    pub projectile_light: Option<LightEmitter>,
30    pub projectile_speed: f32,
31    pub horiz_leap_strength: f32,
32    pub vert_leap_strength: f32,
33    pub ability_info: AbilityInfo,
34}
35
36#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
37pub struct Data {
38    pub static_data: StaticData,
39    pub timer: Duration,
40    pub stage_section: StageSection,
41    pub melee_done: bool,
42    pub ranged_done: bool,
43}
44
45impl CharacterBehavior for Data {
46    fn behavior(&self, data: &JoinData, output_events: &mut OutputEvents) -> StateUpdate {
47        let mut update = StateUpdate::from(data);
48
49        handle_orientation(data, &mut update, 1.0, None);
50        handle_move(data, &mut update, 0.3);
51
52        match self.stage_section {
53            StageSection::Buildup => {
54                if self.timer < self.static_data.buildup_duration {
55                    let frac = {
56                        let raw = self.timer.as_secs_f32()
57                            / self.static_data.buildup_duration.as_secs_f32();
58                        raw.clamp(0.0, 1.0)
59                    };
60                    if self.static_data.melee.is_some()
61                        && !self.melee_done
62                        && frac > self.static_data.buildup_melee_timing
63                    {
64                        let precision_mult =
65                            combat::compute_precision_mult(data.inventory, data.msm);
66                        let tool_stats = get_tool_stats(data, self.static_data.ability_info);
67
68                        if let Some(melee) = &self.static_data.melee {
69                            data.updater.insert(
70                                data.entity,
71                                melee.clone().create_melee(
72                                    precision_mult,
73                                    tool_stats,
74                                    self.static_data.ability_info,
75                                ),
76                            );
77                        }
78
79                        if let CharacterState::LeapRanged(c) = &mut update.character {
80                            c.melee_done = true;
81                        }
82                    }
83
84                    if let CharacterState::LeapRanged(c) = &mut update.character {
85                        c.timer = tick_attack_or_default(data, self.timer, None);
86                    }
87                } else {
88                    let early_exit = self.static_data.melee_required
89                        && data
90                            .melee_attack
91                            .is_none_or(|melee| melee.hit_entities.is_empty());
92                    if let CharacterState::LeapRanged(c) = &mut update.character {
93                        c.timer = Duration::default();
94                        c.stage_section = if early_exit {
95                            StageSection::Recover
96                        } else {
97                            StageSection::Movement
98                        };
99                    }
100                }
101            },
102            StageSection::Movement => {
103                if self.timer < self.static_data.movement_duration {
104                    let frac = {
105                        let raw = self.timer.as_secs_f32()
106                            / self.static_data.movement_duration.as_secs_f32();
107                        raw.clamp(0.0, 1.0)
108                    };
109                    let progress = 1.0 - frac;
110                    handle_forced_movement(data, &mut update, ForcedMovement::Leap {
111                        vertical: self.static_data.vert_leap_strength,
112                        forward: self.static_data.horiz_leap_strength,
113                        progress,
114                        direction: MovementDirection::AntiLook,
115                    });
116
117                    if !self.ranged_done && frac > self.static_data.movement_ranged_timing {
118                        let precision_mult =
119                            combat::compute_precision_mult(data.inventory, data.msm);
120
121                        let projectile = self.static_data.projectile.clone().create_projectile(
122                            Some(*data.uid),
123                            precision_mult,
124                            Some(self.static_data.ability_info),
125                        );
126
127                        let body_offsets = data.body.projectile_offsets(
128                            update.ori.look_vec(),
129                            data.scale.map_or(1.0, |s| s.0),
130                        );
131                        let pos = Pos(data.pos.0 + body_offsets);
132                        output_events.emit_server(ShootEvent {
133                            entity: Some(data.entity),
134                            source_vel: Some(*data.vel),
135                            pos,
136                            dir: data.inputs.look_dir,
137                            body: self.static_data.projectile_body,
138                            projectile,
139                            light: self.static_data.projectile_light,
140                            speed: self.static_data.projectile_speed,
141                            object: None,
142                            marker: None,
143                        });
144
145                        if let CharacterState::LeapRanged(c) = &mut update.character {
146                            c.ranged_done = true;
147                        }
148                    }
149
150                    if let CharacterState::LeapRanged(c) = &mut update.character {
151                        c.timer = tick_attack_or_default(data, self.timer, None);
152                    }
153                } else if data.physics.on_ground.is_some()
154                    | data.physics.in_liquid().is_some()
155                    | (self.timer
156                        > (self.static_data.movement_duration + self.static_data.land_timeout))
157                {
158                    if let CharacterState::LeapRanged(c) = &mut update.character {
159                        c.timer = Duration::default();
160                        c.stage_section = StageSection::Recover;
161                    }
162                } else if let CharacterState::LeapRanged(c) = &mut update.character {
163                    c.timer = tick_attack_or_default(data, self.timer, None);
164                }
165            },
166            StageSection::Recover => {
167                if self.timer < self.static_data.recover_duration {
168                    if let CharacterState::LeapRanged(c) = &mut update.character {
169                        c.timer = tick_attack_or_default(
170                            data,
171                            self.timer,
172                            Some(data.stats.recovery_speed_modifier),
173                        );
174                    }
175                } else {
176                    end_melee_ability(data, &mut update);
177                }
178            },
179            _ => {
180                end_melee_ability(data, &mut update);
181            },
182        }
183
184        // At end of state logic so an interrupt isn't overwritten
185        handle_interrupts(data, &mut update, output_events);
186
187        update
188    }
189}