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(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(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 if let CharacterState::BasicMelee(c) = &mut update.character {
73 c.timer = tick_attack_or_default(data, self.timer, None);
74 }
75 } else {
76 if let CharacterState::BasicMelee(c) = &mut update.character {
78 c.timer = Duration::default();
79 c.stage_section = StageSection::Action;
80 c.movement_modifier = self.static_data.movement_modifier.swing;
81 c.ori_modifier = self.static_data.ori_modifier.swing;
82 }
83 }
84 },
85 StageSection::Action => {
86 if !self.exhausted
87 && self.timer.as_secs_f32()
88 >= self.static_data.swing_duration.as_secs_f32()
89 * self.static_data.hit_timing
90 {
91 if let CharacterState::BasicMelee(c) = &mut update.character {
92 c.timer = tick_attack_or_default(data, self.timer, None);
93 c.exhausted = true;
94 }
95
96 let precision_mult = combat::compute_precision_mult(data.inventory, data.msm);
97 let tool_stats = get_tool_stats(data, self.static_data.ability_info);
98
99 data.updater.insert(
100 data.entity,
101 self.static_data
102 .melee_constructor
103 .clone()
104 .create_melee(precision_mult, tool_stats, self.static_data.ability_info)
105 .with_block_breaking(
106 data.inputs
107 .break_block_pos
108 .map(|p| {
109 (
110 p.map(|e| e.floor() as i32),
111 self.static_data.ability_info.tool,
112 )
113 })
114 .filter(|(_, tool)| {
115 matches!(tool, Some(ToolKind::Pick | ToolKind::Shovel))
116 }),
117 ),
118 );
119 if self.static_data.ability_info.tool == Some(ToolKind::Shovel) {
121 output_events.emit_local(LocalEvent::CreateOutcome(Outcome::GroundDig {
122 pos: data.pos.0 + *data.ori.look_dir() * (data.body.max_radius()),
123 }));
124 }
125 } else if self.timer < self.static_data.swing_duration {
126 if let CharacterState::BasicMelee(c) = &mut update.character {
128 c.timer = tick_attack_or_default(data, self.timer, None);
129 }
130 } else {
131 if let CharacterState::BasicMelee(c) = &mut update.character {
133 c.timer = Duration::default();
134 c.stage_section = StageSection::Recover;
135 c.movement_modifier = self.static_data.movement_modifier.recover;
136 c.ori_modifier = self.static_data.ori_modifier.recover;
137 }
138 }
139 },
140 StageSection::Recover => {
141 if self.timer < self.static_data.recover_duration {
142 if let CharacterState::BasicMelee(c) = &mut update.character {
144 c.timer = tick_attack_or_default(
145 data,
146 self.timer,
147 Some(data.stats.recovery_speed_modifier),
148 );
149 c.movement_modifier = self.static_data.movement_modifier.recover;
150 c.ori_modifier = self.static_data.ori_modifier.recover;
151 }
152 } else {
153 if input_is_pressed(data, self.static_data.ability_info.input) {
155 reset_state(self, data, output_events, &mut update);
156 } else {
157 end_melee_ability(data, &mut update);
158 }
159 }
160 },
161 _ => {
162 end_melee_ability(data, &mut update);
164 },
165 }
166
167 handle_interrupts(data, &mut update, output_events);
169
170 update
171 }
172}
173
174fn reset_state(
175 data: &Data,
176 join: &JoinData,
177 output_events: &mut OutputEvents,
178 update: &mut StateUpdate,
179) {
180 handle_input(
181 join,
182 output_events,
183 update,
184 data.static_data.ability_info.input,
185 );
186}
187
188#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
189pub enum FrontendSpecifier {
190 FlameTornado,
191 FireGigasWhirlwind,
192}