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.melee_attack.is_none_or(|melee| melee.hit_count == 0);
90                    if let CharacterState::LeapRanged(c) = &mut update.character {
91                        c.timer = Duration::default();
92                        c.stage_section = if early_exit {
93                            StageSection::Recover
94                        } else {
95                            StageSection::Movement
96                        };
97                    }
98                }
99            },
100            StageSection::Movement => {
101                if self.timer < self.static_data.movement_duration {
102                    let frac = {
103                        let raw = self.timer.as_secs_f32()
104                            / self.static_data.movement_duration.as_secs_f32();
105                        raw.clamp(0.0, 1.0)
106                    };
107                    let progress = 1.0 - frac;
108                    handle_forced_movement(data, &mut update, ForcedMovement::Leap {
109                        vertical: self.static_data.vert_leap_strength,
110                        forward: self.static_data.horiz_leap_strength,
111                        progress,
112                        direction: MovementDirection::AntiLook,
113                    });
114
115                    if !self.ranged_done && frac > self.static_data.movement_ranged_timing {
116                        let precision_mult =
117                            combat::compute_precision_mult(data.inventory, data.msm);
118
119                        let projectile = self.static_data.projectile.clone().create_projectile(
120                            Some(*data.uid),
121                            precision_mult,
122                            Some(self.static_data.ability_info),
123                        );
124
125                        let body_offsets = data.body.projectile_offsets(
126                            update.ori.look_vec(),
127                            data.scale.map_or(1.0, |s| s.0),
128                        );
129                        let pos = Pos(data.pos.0 + body_offsets);
130                        output_events.emit_server(ShootEvent {
131                            entity: Some(data.entity),
132                            source_vel: Some(*data.vel),
133                            pos,
134                            dir: data.inputs.look_dir,
135                            body: self.static_data.projectile_body,
136                            projectile,
137                            light: self.static_data.projectile_light,
138                            speed: self.static_data.projectile_speed,
139                            object: None,
140                            marker: None,
141                        });
142
143                        if let CharacterState::LeapRanged(c) = &mut update.character {
144                            c.ranged_done = true;
145                        }
146                    }
147
148                    if let CharacterState::LeapRanged(c) = &mut update.character {
149                        c.timer = tick_attack_or_default(data, self.timer, None);
150                    }
151                } else if data.physics.on_ground.is_some()
152                    | data.physics.in_liquid().is_some()
153                    | (self.timer
154                        > (self.static_data.movement_duration + self.static_data.land_timeout))
155                {
156                    if let CharacterState::LeapRanged(c) = &mut update.character {
157                        c.timer = Duration::default();
158                        c.stage_section = StageSection::Recover;
159                    }
160                } else if let CharacterState::LeapRanged(c) = &mut update.character {
161                    c.timer = tick_attack_or_default(data, self.timer, None);
162                }
163            },
164            StageSection::Recover => {
165                if self.timer < self.static_data.recover_duration {
166                    if let CharacterState::LeapRanged(c) = &mut update.character {
167                        c.timer = tick_attack_or_default(
168                            data,
169                            self.timer,
170                            Some(data.stats.recovery_speed_modifier),
171                        );
172                    }
173                } else {
174                    end_melee_ability(data, &mut update);
175                }
176            },
177            _ => {
178                end_melee_ability(data, &mut update);
179            },
180        }
181
182        // At end of state logic so an interrupt isn't overwritten
183        handle_interrupts(data, &mut update, output_events);
184
185        update
186    }
187}