1use crate::{
2 combat,
3 comp::{CharacterState, MeleeConstructor, StateUpdate, character_state::OutputEvents},
4 states::{
5 behavior::{CharacterBehavior, JoinData},
6 utils::*,
7 },
8};
9use serde::{Deserialize, Serialize};
10use std::time::Duration;
11
12#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
14pub struct StaticData {
15 pub energy_drain: f32,
17 pub forward_speed: f32,
19 pub buildup_duration: Duration,
21 pub charge_duration: Duration,
23 pub swing_duration: Duration,
25 pub recover_duration: Duration,
27 pub melee_constructor: MeleeConstructor,
29 pub ori_modifier: f32,
31 pub auto_charge: bool,
33 pub ability_info: AbilityInfo,
35}
36
37#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
38pub struct Data {
39 pub static_data: StaticData,
42 pub auto_charge: bool,
45 pub timer: Duration,
47 pub stage_section: StageSection,
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_move(data, &mut update, 0.1);
56
57 let create_melee = |charge_frac: f32| {
58 let precision_mult = combat::compute_precision_mult(data.inventory, data.msm);
59 let tool_stats = get_tool_stats(data, self.static_data.ability_info);
60 self.static_data
61 .melee_constructor
62 .handle_scaling(charge_frac)
63 .create_melee(precision_mult, tool_stats)
64 };
65
66 match self.stage_section {
67 StageSection::Buildup => {
68 if self.timer < self.static_data.buildup_duration {
69 handle_orientation(data, &mut update, 1.0, None);
70 update.character = CharacterState::DashMelee(Data {
72 timer: tick_attack_or_default(data, self.timer, None),
73 ..*self
74 });
75 } else {
76 update.character = CharacterState::DashMelee(Data {
78 auto_charge: !input_is_pressed(data, self.static_data.ability_info.input)
79 || self.static_data.auto_charge,
80 timer: Duration::default(),
81 stage_section: StageSection::Charge,
82 ..*self
83 });
84 }
85 },
86 StageSection::Charge => {
87 if self.timer < self.static_data.charge_duration
88 && (input_is_pressed(data, self.static_data.ability_info.input)
89 || self.auto_charge)
90 && update.energy.current() >= 0.0
91 {
92 let charge_frac = (self.timer.as_secs_f32()
94 / self.static_data.charge_duration.as_secs_f32())
95 .min(1.0);
96
97 handle_orientation(data, &mut update, self.static_data.ori_modifier, None);
98 handle_forced_movement(
99 data,
100 &mut update,
101 ForcedMovement::Forward(
102 self.static_data.forward_speed * charge_frac.sqrt(),
103 ),
104 );
105
106 if let Some(melee) = data.melee_attack {
109 if !melee.applied {
110 update.character = CharacterState::DashMelee(Data {
112 timer: tick_attack_or_default(data, self.timer, None),
113 ..*self
114 });
115 } else if melee.hit_count == 0 {
116 data.updater.insert(data.entity, create_melee(charge_frac));
118 update.character = CharacterState::DashMelee(Data {
119 timer: tick_attack_or_default(data, self.timer, None),
120 ..*self
121 });
122 } else {
123 update.character = CharacterState::DashMelee(Data {
125 timer: Duration::default(),
126 stage_section: StageSection::Action,
127 ..*self
128 });
129 }
130 } else {
131 data.updater.insert(data.entity, create_melee(charge_frac));
133
134 update.character = CharacterState::DashMelee(Data {
135 timer: tick_attack_or_default(data, self.timer, None),
136 ..*self
137 });
138 }
139
140 update
142 .energy
143 .change_by(-self.static_data.energy_drain * data.dt.0);
144 } else {
145 update.character = CharacterState::DashMelee(Data {
147 timer: Duration::default(),
148 stage_section: StageSection::Action,
149 ..*self
150 });
151 }
152 },
153 StageSection::Action => {
154 if self.timer < self.static_data.swing_duration {
155 update.character = CharacterState::DashMelee(Data {
157 timer: tick_attack_or_default(data, self.timer, None),
158 ..*self
159 });
160 } else {
161 update.character = CharacterState::DashMelee(Data {
163 timer: Duration::default(),
164 stage_section: StageSection::Recover,
165 ..*self
166 });
167 }
168 },
169 StageSection::Recover => {
170 if self.timer < self.static_data.recover_duration {
171 update.character = CharacterState::DashMelee(Data {
173 timer: tick_attack_or_default(
174 data,
175 self.timer,
176 Some(data.stats.recovery_speed_modifier),
177 ),
178 ..*self
179 });
180 } else {
181 end_melee_ability(data, &mut update);
183 }
184 },
185 _ => {
186 end_melee_ability(data, &mut update);
188 },
189 }
190
191 handle_interrupts(data, &mut update, output_events);
193
194 update
195 }
196}