1use crate::{
2 combat,
3 comp::{
4 CharacterState, MeleeConstructor, StateUpdate, character_state::OutputEvents,
5 melee::CustomCombo,
6 },
7 event::LocalEvent,
8 outcome::Outcome,
9 states::{
10 behavior::{CharacterBehavior, JoinData},
11 utils::{StageSection, *},
12 },
13};
14use serde::{Deserialize, Serialize};
15use std::time::Duration;
16
17#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
18pub struct StaticData {
20 pub energy_drain: f32,
22 pub energy_cost: f32,
24 pub buildup_strike: Option<(Duration, MeleeConstructor)>,
27 pub charge_duration: Duration,
29 pub swing_duration: Duration,
31 pub hit_timing: f32,
33 pub recover_duration: Duration,
35 pub melee_constructor: MeleeConstructor,
37 pub ability_info: AbilityInfo,
39 pub specifier: Option<FrontendSpecifier>,
41 pub custom_combo: CustomCombo,
43 pub movement_modifier: MovementModifier,
45 pub ori_modifier: OrientationModifier,
47}
48
49#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
50pub struct Data {
51 pub static_data: StaticData,
54 pub stage_section: StageSection,
56 pub timer: Duration,
58 pub exhausted: bool,
60 pub charge_amount: f32,
62 pub movement_modifier: Option<f32>,
64 pub ori_modifier: Option<f32>,
66}
67
68impl Data {
69 pub fn charge_frac(&self) -> f32 {
71 if let StageSection::Charge = self.stage_section {
72 (self.timer.as_secs_f32() / self.static_data.charge_duration.as_secs_f32()).min(1.0)
73 } else {
74 0.0
75 }
76 }
77}
78
79impl CharacterBehavior for Data {
80 fn behavior(&self, data: &JoinData, output_events: &mut OutputEvents) -> StateUpdate {
81 let mut update = StateUpdate::from(data);
82
83 handle_orientation(data, &mut update, self.ori_modifier.unwrap_or(1.0), None);
84 handle_move(data, &mut update, self.movement_modifier.unwrap_or(0.7));
85 handle_jump(data, output_events, &mut update, 1.0);
86
87 match self.stage_section {
88 StageSection::Buildup => {
89 if let Some((buildup, strike)) = &self.static_data.buildup_strike {
90 if self.timer < *buildup {
91 if let CharacterState::ChargedMelee(c) = &mut update.character {
92 c.timer = tick_attack_or_default(data, self.timer, None);
93 }
94 } else {
95 let precision_mult =
96 combat::compute_precision_mult(data.inventory, data.msm);
97 data.updater.insert(
98 data.entity,
99 strike
100 .clone()
101 .create_melee(precision_mult, self.static_data.ability_info),
102 );
103
104 if let CharacterState::ChargedMelee(c) = &mut update.character {
105 c.stage_section = StageSection::Charge;
106 c.timer = Duration::default();
107 }
108 }
109 } else if let CharacterState::ChargedMelee(c) = &mut update.character {
110 c.stage_section = StageSection::Charge;
111 c.timer = Duration::default();
112 }
113 },
114 StageSection::Charge => {
115 if input_is_pressed(data, self.static_data.ability_info.input)
116 && (update.energy.current() >= self.static_data.energy_drain * data.dt.0)
117 && self.timer < self.static_data.charge_duration
118 {
119 let charge = (self.timer.as_secs_f32()
120 / self.static_data.charge_duration.as_secs_f32())
121 .min(1.0);
122
123 if let CharacterState::ChargedMelee(c) = &mut update.character {
125 c.timer = tick_attack_or_default(data, self.timer, None);
126 c.charge_amount = charge;
127 }
128
129 update
131 .energy
132 .change_by(-self.static_data.energy_drain * data.dt.0);
133 } else if input_is_pressed(data, self.static_data.ability_info.input)
134 && (update.energy.current() >= self.static_data.energy_drain * data.dt.0)
135 {
136 if let CharacterState::ChargedMelee(c) = &mut update.character {
138 c.timer = tick_attack_or_default(data, self.timer, None);
139 }
140
141 update
143 .energy
144 .change_by(-self.static_data.energy_drain * data.dt.0 / 5.0);
145 } else {
146 if let CharacterState::ChargedMelee(c) = &mut update.character {
148 c.stage_section = StageSection::Action;
149 c.timer = Duration::default();
150 c.movement_modifier = self.static_data.movement_modifier.action;
151 c.ori_modifier = self.static_data.ori_modifier.action;
152 }
153 }
154 },
155 StageSection::Action => {
156 if self.timer.as_millis() as f32
157 > self.static_data.hit_timing
158 * self.static_data.swing_duration.as_millis() as f32
159 && !self.exhausted
160 {
161 if let CharacterState::ChargedMelee(c) = &mut update.character {
163 c.timer = tick_attack_or_default(data, self.timer, None);
164 c.exhausted = true;
165 }
166
167 let precision_mult = combat::compute_precision_mult(data.inventory, data.msm);
168 let custom_combo = CustomCombo {
169 base: self
170 .static_data
171 .custom_combo
172 .base
173 .map(|b| (self.charge_amount * b as f32).round() as i32),
174 conditional: self
175 .static_data
176 .custom_combo
177 .conditional
178 .map(|c| ((self.charge_amount * c.0 as f32).round() as i32, c.1)),
179 };
180
181 data.updater.insert(
182 data.entity,
183 self.static_data
184 .melee_constructor
185 .clone()
186 .custom_combo(custom_combo)
187 .handle_scaling(self.charge_amount)
188 .create_melee(precision_mult, self.static_data.ability_info),
189 );
190
191 if let Some(FrontendSpecifier::GroundCleave) = self.static_data.specifier {
192 output_events.emit_local(LocalEvent::CreateOutcome(Outcome::GroundSlam {
194 pos: data.pos.0
195 + *data.ori.look_dir()
196 * (data.body.max_radius()
197 + self.static_data.melee_constructor.range),
198 }));
199 }
200 } else if self.timer < self.static_data.swing_duration {
201 if let CharacterState::ChargedMelee(c) = &mut update.character {
203 c.timer = tick_attack_or_default(data, self.timer, None);
204 }
205 } else {
206 if let CharacterState::ChargedMelee(c) = &mut update.character {
208 c.stage_section = StageSection::Recover;
209 c.timer = Duration::default();
210 c.movement_modifier = self.static_data.movement_modifier.recover;
211 c.ori_modifier = self.static_data.ori_modifier.recover;
212 }
213 }
214 },
215 StageSection::Recover => {
216 if self.timer < self.static_data.recover_duration {
217 if let CharacterState::ChargedMelee(c) = &mut update.character {
219 c.timer = tick_attack_or_default(data, self.timer, None);
220 }
221 } else {
222 end_melee_ability(data, &mut update);
224 }
225 },
226 _ => {
227 end_melee_ability(data, &mut update);
229 },
230 }
231
232 handle_interrupts(data, &mut update, output_events);
234
235 update
236 }
237}
238
239#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
241pub enum FrontendSpecifier {
242 GroundCleave,
243}