1use crate::{
2 combat,
3 comp::{CharacterState, MeleeConstructor, StateUpdate, character_state::OutputEvents},
4 event::LocalEvent,
5 outcome::Outcome,
6 states::{
7 behavior::{CharacterBehavior, JoinData},
8 utils::{StageSection, *},
9 },
10};
11use serde::{Deserialize, Serialize};
12use std::time::Duration;
13
14#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
16pub struct StaticData {
17 pub movement_duration: Duration,
19 pub buildup_duration: Duration,
21 pub swing_duration: Duration,
23 pub recover_duration: Duration,
25 pub melee_constructor: MeleeConstructor,
27 pub specifier: Option<FrontendSpecifier>,
29 pub forward_leap_strength: f32,
31 pub vertical_leap_strength: f32,
33 pub ability_info: AbilityInfo,
35}
36
37#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
38pub struct Data {
39 pub static_data: StaticData,
42 pub timer: Duration,
44 pub stage_section: StageSection,
46 pub exhausted: bool,
48}
49
50impl CharacterBehavior for Data {
51 fn behavior(&self, data: &JoinData, output_events: &mut OutputEvents) -> StateUpdate {
52 let mut update = StateUpdate::from(data);
53
54 handle_orientation(data, &mut update, 1.0, None);
55 handle_move(data, &mut update, 0.3);
56 handle_jump(data, output_events, &mut update, 1.0);
57
58 match self.stage_section {
59 StageSection::Buildup => {
61 if self.timer < self.static_data.buildup_duration {
63 if let CharacterState::LeapMelee(c) = &mut update.character {
64 c.timer = tick_attack_or_default(data, self.timer, None);
65 }
66 } else {
67 if let CharacterState::LeapMelee(c) = &mut update.character {
69 c.timer = Duration::default();
70 c.stage_section = StageSection::Movement
71 }
72
73 match self.static_data.specifier {
75 Some(FrontendSpecifier::LeapWhoosh) => {
76 output_events.emit_local(LocalEvent::CreateOutcome(Outcome::Whoosh {
77 pos: data.pos.0 + *data.ori.look_dir() * (data.body.max_radius()),
78 }));
79 },
80 Some(FrontendSpecifier::LeapSwoosh) => {
81 output_events.emit_local(LocalEvent::CreateOutcome(Outcome::Swoosh {
82 pos: data.pos.0 + *data.ori.look_dir() * (data.body.max_radius()),
83 }));
84 },
85 _ => {},
86 };
87 }
88 },
89 StageSection::Movement => {
90 if self.timer < self.static_data.movement_duration {
91 let progress = 1.0
93 - self.timer.as_secs_f32()
94 / self.static_data.movement_duration.as_secs_f32();
95 handle_forced_movement(data, &mut update, ForcedMovement::Leap {
96 vertical: self.static_data.vertical_leap_strength,
97 forward: self.static_data.forward_leap_strength,
98 progress,
99 direction: MovementDirection::Look,
100 });
101
102 if let CharacterState::LeapMelee(c) = &mut update.character {
107 c.timer = tick_attack_or_default(data, self.timer, None);
108 }
109 } else if data.physics.on_ground.is_some() | data.physics.in_liquid().is_some() {
110 if let CharacterState::LeapMelee(c) = &mut update.character {
112 c.timer = Duration::default();
113 c.stage_section = StageSection::Action;
114 }
115 }
116 },
117 StageSection::Action => {
118 if self.timer < self.static_data.swing_duration {
119 if let CharacterState::LeapMelee(c) = &mut update.character {
121 c.timer = tick_attack_or_default(data, self.timer, None);
122 }
123 } else {
124 if let CharacterState::LeapMelee(c) = &mut update.character {
126 c.timer = Duration::default();
127 c.stage_section = StageSection::Recover;
128 }
129 }
130 },
131 StageSection::Recover => {
132 if !self.exhausted {
133 let precision_mult = combat::compute_precision_mult(data.inventory, data.msm);
134 let tool_stats = get_tool_stats(data, self.static_data.ability_info);
135
136 data.updater.insert(
137 data.entity,
138 self.static_data.melee_constructor.clone().create_melee(
139 precision_mult,
140 tool_stats,
141 self.static_data.ability_info,
142 ),
143 );
144
145 if let CharacterState::LeapMelee(c) = &mut update.character {
146 c.timer = tick_attack_or_default(
147 data,
148 self.timer,
149 Some(data.stats.recovery_speed_modifier),
150 );
151 c.exhausted = true;
152 }
153 } else if self.timer < self.static_data.recover_duration {
154 if let CharacterState::LeapMelee(c) = &mut update.character {
156 c.timer = tick_attack_or_default(
157 data,
158 self.timer,
159 Some(data.stats.recovery_speed_modifier),
160 );
161 }
162 } else {
163 end_melee_ability(data, &mut update);
165 }
166 },
167 _ => {
168 end_melee_ability(data, &mut update);
170 },
171 }
172
173 handle_interrupts(data, &mut update, output_events);
175
176 update
177 }
178}
179
180#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
181pub enum FrontendSpecifier {
182 LeapWhoosh,
183 LeapSwoosh,
184}