1use crate::{
2 combat::{self, CombatEffect},
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(Copy, 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 pub damage_effect: Option<CombatEffect>,
36}
37
38#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
39pub struct Data {
40 pub static_data: StaticData,
43 pub timer: Duration,
45 pub stage_section: StageSection,
47 pub exhausted: bool,
49}
50
51impl CharacterBehavior for Data {
52 fn behavior(&self, data: &JoinData, output_events: &mut OutputEvents) -> StateUpdate {
53 let mut update = StateUpdate::from(data);
54
55 handle_orientation(data, &mut update, 1.0, None);
56 handle_move(data, &mut update, 0.3);
57 handle_jump(data, output_events, &mut update, 1.0);
58
59 match self.stage_section {
60 StageSection::Buildup => {
62 if self.timer < self.static_data.buildup_duration {
64 update.character = CharacterState::LeapMelee(Data {
65 timer: tick_attack_or_default(data, self.timer, None),
66 ..*self
67 });
68 } else {
69 update.character = CharacterState::LeapMelee(Data {
71 timer: Duration::default(),
72 stage_section: StageSection::Movement,
73 ..*self
74 });
75
76 match self.static_data.specifier {
78 Some(FrontendSpecifier::LeapWhoosh) => {
79 output_events.emit_local(LocalEvent::CreateOutcome(Outcome::Whoosh {
80 pos: data.pos.0 + *data.ori.look_dir() * (data.body.max_radius()),
81 }));
82 },
83 Some(FrontendSpecifier::LeapSwoosh) => {
84 output_events.emit_local(LocalEvent::CreateOutcome(Outcome::Swoosh {
85 pos: data.pos.0 + *data.ori.look_dir() * (data.body.max_radius()),
86 }));
87 },
88 _ => {},
89 };
90 }
91 },
92 StageSection::Movement => {
93 if self.timer < self.static_data.movement_duration {
94 let progress = 1.0
96 - self.timer.as_secs_f32()
97 / self.static_data.movement_duration.as_secs_f32();
98 handle_forced_movement(data, &mut update, ForcedMovement::Leap {
99 vertical: self.static_data.vertical_leap_strength,
100 forward: self.static_data.forward_leap_strength,
101 progress,
102 direction: MovementDirection::Look,
103 });
104
105 update.character = CharacterState::LeapMelee(Data {
110 timer: tick_attack_or_default(data, self.timer, None),
111 ..*self
112 });
113 } else if data.physics.on_ground.is_some() | data.physics.in_liquid().is_some() {
114 update.character = CharacterState::LeapMelee(Data {
116 timer: Duration::default(),
117 stage_section: StageSection::Action,
118 ..*self
119 });
120 }
121 },
122 StageSection::Action => {
123 if self.timer < self.static_data.swing_duration {
124 update.character = CharacterState::LeapMelee(Data {
126 timer: tick_attack_or_default(data, self.timer, None),
127 ..*self
128 });
129 } else {
130 update.character = CharacterState::LeapMelee(Data {
132 timer: Duration::default(),
133 stage_section: StageSection::Recover,
134 ..*self
135 });
136 }
137 },
138 StageSection::Recover => {
139 if !self.exhausted {
140 let precision_mult = combat::compute_precision_mult(data.inventory, data.msm);
141 let tool_stats = get_tool_stats(data, self.static_data.ability_info);
142
143 data.updater.insert(
144 data.entity,
145 self.static_data
146 .melee_constructor
147 .create_melee(precision_mult, tool_stats),
148 );
149
150 update.character = CharacterState::LeapMelee(Data {
151 timer: tick_attack_or_default(
152 data,
153 self.timer,
154 Some(data.stats.recovery_speed_modifier),
155 ),
156 exhausted: true,
157 ..*self
158 });
159 } else if self.timer < self.static_data.recover_duration {
160 update.character = CharacterState::LeapMelee(Data {
162 timer: tick_attack_or_default(
163 data,
164 self.timer,
165 Some(data.stats.recovery_speed_modifier),
166 ),
167 ..*self
168 });
169 } else {
170 end_melee_ability(data, &mut update);
172 }
173 },
174 _ => {
175 end_melee_ability(data, &mut update);
177 },
178 }
179
180 handle_interrupts(data, &mut update, output_events);
182
183 update
184 }
185}
186
187#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
188pub enum FrontendSpecifier {
189 LeapWhoosh,
190 LeapSwoosh,
191}