1use crate::{
2 combat,
3 comp::{
4 CharacterState, MeleeConstructor, StateUpdate, character_state::OutputEvents,
5 tool::ToolKind,
6 },
7 event::LocalEvent,
8 outcome::Outcome,
9 states::{
10 behavior::{CharacterBehavior, JoinData},
11 utils::*,
12 },
13};
14use serde::{Deserialize, Serialize};
15use std::time::Duration;
16
17#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
19pub struct StaticData {
20 pub buildup_duration: Duration,
22 pub swing_duration: Duration,
24 pub recover_duration: Duration,
26 pub hit_timing: f32,
28 pub melee_constructor: MeleeConstructor,
30 #[serde(default)]
32 pub movement_modifier: MovementModifier,
33 #[serde(default)]
35 pub ori_modifier: OrientationModifier,
36 pub frontend_specifier: Option<FrontendSpecifier>,
39 pub ability_info: AbilityInfo,
41}
42
43#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
44pub struct Data {
45 pub static_data: StaticData,
48 pub timer: Duration,
50 pub stage_section: StageSection,
52 pub exhausted: bool,
54 pub movement_modifier: Option<f32>,
56 pub ori_modifier: Option<f32>,
58}
59
60impl CharacterBehavior for Data {
61 fn behavior(&self, data: &JoinData, output_events: &mut OutputEvents) -> StateUpdate {
62 let mut update = StateUpdate::from(data);
63
64 handle_orientation(data, &mut update, self.ori_modifier.unwrap_or(1.0), None);
65 handle_move(data, &mut update, self.movement_modifier.unwrap_or(0.7));
66 handle_jump(data, output_events, &mut update, 1.0);
67
68 match self.stage_section {
69 StageSection::Buildup => {
70 if self.timer < self.static_data.buildup_duration {
71 update.character = CharacterState::BasicMelee(Data {
73 timer: tick_attack_or_default(data, self.timer, None),
74 ..*self
75 });
76 } else {
77 update.character = CharacterState::BasicMelee(Data {
79 timer: Duration::default(),
80 stage_section: StageSection::Action,
81 movement_modifier: self.static_data.movement_modifier.swing,
82 ori_modifier: self.static_data.ori_modifier.swing,
83 ..*self
84 });
85 }
86 },
87 StageSection::Action => {
88 if !self.exhausted
89 && self.timer.as_secs_f32()
90 >= self.static_data.swing_duration.as_secs_f32()
91 * self.static_data.hit_timing
92 {
93 update.character = CharacterState::BasicMelee(Data {
94 timer: tick_attack_or_default(data, self.timer, None),
95 exhausted: true,
96 ..*self
97 });
98
99 let precision_mult = combat::compute_precision_mult(data.inventory, data.msm);
100 let tool_stats = get_tool_stats(data, self.static_data.ability_info);
101
102 data.updater.insert(
103 data.entity,
104 self.static_data
105 .melee_constructor
106 .create_melee(precision_mult, tool_stats)
107 .with_block_breaking(
108 data.inputs
109 .break_block_pos
110 .map(|p| {
111 (
112 p.map(|e| e.floor() as i32),
113 self.static_data.ability_info.tool,
114 )
115 })
116 .filter(|(_, tool)| {
117 matches!(tool, Some(ToolKind::Pick | ToolKind::Shovel))
118 }),
119 ),
120 );
121 if self.static_data.ability_info.tool == Some(ToolKind::Shovel) {
123 output_events.emit_local(LocalEvent::CreateOutcome(Outcome::GroundDig {
124 pos: data.pos.0 + *data.ori.look_dir() * (data.body.max_radius()),
125 }));
126 }
127 } else if self.timer < self.static_data.swing_duration {
128 update.character = CharacterState::BasicMelee(Data {
130 timer: tick_attack_or_default(data, self.timer, None),
131 ..*self
132 });
133 } else {
134 update.character = CharacterState::BasicMelee(Data {
136 timer: Duration::default(),
137 stage_section: StageSection::Recover,
138 movement_modifier: self.static_data.movement_modifier.recover,
139 ori_modifier: self.static_data.ori_modifier.recover,
140 ..*self
141 });
142 }
143 },
144 StageSection::Recover => {
145 if self.timer < self.static_data.recover_duration {
146 update.character = CharacterState::BasicMelee(Data {
148 timer: tick_attack_or_default(
149 data,
150 self.timer,
151 Some(data.stats.recovery_speed_modifier),
152 ),
153 movement_modifier: self.static_data.movement_modifier.recover,
154 ori_modifier: self.static_data.ori_modifier.recover,
155 ..*self
156 });
157 } else {
158 if input_is_pressed(data, self.static_data.ability_info.input) {
160 reset_state(self, data, output_events, &mut update);
161 } else {
162 end_melee_ability(data, &mut update);
163 }
164 }
165 },
166 _ => {
167 end_melee_ability(data, &mut update);
169 },
170 }
171
172 handle_interrupts(data, &mut update, output_events);
174
175 update
176 }
177}
178
179fn reset_state(
180 data: &Data,
181 join: &JoinData,
182 output_events: &mut OutputEvents,
183 update: &mut StateUpdate,
184) {
185 handle_input(
186 join,
187 output_events,
188 update,
189 data.static_data.ability_info.input,
190 );
191}
192
193#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
194pub enum FrontendSpecifier {
195 FlameTornado,
196}