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.action;
81 c.ori_modifier = self.static_data.ori_modifier.action;
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
98 data.updater.insert(
99 data.entity,
100 self.static_data
101 .melee_constructor
102 .clone()
103 .create_melee(precision_mult, self.static_data.ability_info)
104 .with_block_breaking(
105 data.inputs
106 .break_block_pos
107 .map(|p| {
108 (
109 p.map(|e| e.floor() as i32),
110 self.static_data.ability_info.tool,
111 )
112 })
113 .filter(|(_, tool)| {
114 matches!(tool, Some(ToolKind::Pick | ToolKind::Shovel))
115 }),
116 ),
117 );
118 if self.static_data.ability_info.tool == Some(ToolKind::Shovel) {
120 output_events.emit_local(LocalEvent::CreateOutcome(Outcome::GroundDig {
121 pos: data.pos.0 + *data.ori.look_dir() * (data.body.max_radius()),
122 }));
123 }
124 } else if self.timer < self.static_data.swing_duration {
125 if let CharacterState::BasicMelee(c) = &mut update.character {
127 c.timer = tick_attack_or_default(data, self.timer, None);
128 }
129 } else {
130 if let CharacterState::BasicMelee(c) = &mut update.character {
132 c.timer = Duration::default();
133 c.stage_section = StageSection::Recover;
134 c.movement_modifier = self.static_data.movement_modifier.recover;
135 c.ori_modifier = self.static_data.ori_modifier.recover;
136 }
137 }
138 },
139 StageSection::Recover => {
140 if self.timer < self.static_data.recover_duration {
141 if let CharacterState::BasicMelee(c) = &mut update.character {
143 c.timer = tick_attack_or_default(data, self.timer, None);
144 c.movement_modifier = self.static_data.movement_modifier.recover;
145 c.ori_modifier = self.static_data.ori_modifier.recover;
146 }
147 } else {
148 if input_is_pressed(data, self.static_data.ability_info.input) {
150 reset_state(self, data, output_events, &mut update);
151 } else {
152 end_melee_ability(data, &mut update);
153 }
154 }
155 },
156 _ => {
157 end_melee_ability(data, &mut update);
159 },
160 }
161
162 handle_interrupts(data, &mut update, output_events);
164
165 update
166 }
167}
168
169fn reset_state(
170 data: &Data,
171 join: &JoinData,
172 output_events: &mut OutputEvents,
173 update: &mut StateUpdate,
174) {
175 handle_input(
176 join,
177 output_events,
178 update,
179 data.static_data.ability_info.input,
180 );
181}
182
183#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
184pub enum FrontendSpecifier {
185 FlameTornado,
186 FireGigasWhirlwind,
187}