1use crate::{
2 combat::{self, CombatEffect},
3 comp::{
4 Body, CharacterState, LightEmitter, Pos, StateUpdate, character_state::OutputEvents,
5 projectile::ProjectileConstructor,
6 },
7 event::ShootEvent,
8 states::{
9 behavior::{CharacterBehavior, JoinData},
10 utils::*,
11 },
12};
13use serde::{Deserialize, Serialize};
14use std::time::Duration;
15
16#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
18pub struct StaticData {
19 pub buildup_duration: Duration,
21 pub charge_duration: Duration,
23 pub recover_duration: Duration,
25 pub energy_drain: f32,
27 pub projectile: ProjectileConstructor,
29 pub projectile_body: Body,
30 pub projectile_light: Option<LightEmitter>,
31 pub initial_projectile_speed: f32,
32 pub scaled_projectile_speed: f32,
33 pub move_speed: f32,
35 pub ability_info: AbilityInfo,
37 pub damage_effect: Option<CombatEffect>,
39}
40
41#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
42pub struct Data {
43 pub static_data: StaticData,
46 pub timer: Duration,
48 pub stage_section: StageSection,
50 pub exhausted: bool,
52}
53
54impl Data {
55 pub fn charge_frac(&self) -> f32 {
57 if let StageSection::Charge = self.stage_section {
58 (self.timer.as_secs_f32() / self.static_data.charge_duration.as_secs_f32()).min(1.0)
59 } else {
60 0.0
61 }
62 }
63}
64
65impl CharacterBehavior for Data {
66 fn behavior(&self, data: &JoinData, output_events: &mut OutputEvents) -> StateUpdate {
67 let mut update = StateUpdate::from(data);
68
69 handle_orientation(data, &mut update, 1.0, None);
70 handle_move(data, &mut update, self.static_data.move_speed);
71 handle_jump(data, output_events, &mut update, 1.0);
72
73 match self.stage_section {
74 StageSection::Buildup => {
75 if self.timer < self.static_data.buildup_duration {
76 update.character = CharacterState::ChargedRanged(Data {
78 timer: tick_attack_or_default(data, self.timer, None),
79 ..*self
80 });
81 } else {
82 update.character = CharacterState::ChargedRanged(Data {
84 timer: Duration::default(),
85 stage_section: StageSection::Charge,
86 ..*self
87 });
88 }
89 },
90 StageSection::Charge => {
91 if !input_is_pressed(data, self.static_data.ability_info.input) && !self.exhausted {
92 let charge_frac = self.charge_frac();
93 let precision_mult = combat::compute_precision_mult(data.inventory, data.msm);
95 let body_offsets = data
97 .body
98 .projectile_offsets(update.ori.look_vec(), data.scale.map_or(1.0, |s| s.0));
99 let pos = Pos(data.pos.0 + body_offsets);
100 let projectile = self
101 .static_data
102 .projectile
103 .handle_scaling(charge_frac)
104 .create_projectile(
105 Some(*data.uid),
106 precision_mult,
107 self.static_data.damage_effect,
108 );
109 output_events.emit_server(ShootEvent {
110 entity: data.entity,
111 pos,
112 dir: data.inputs.look_dir,
113 body: self.static_data.projectile_body,
114 projectile,
115 light: self.static_data.projectile_light,
116 speed: self.static_data.initial_projectile_speed
117 + charge_frac * self.static_data.scaled_projectile_speed,
118 object: None,
119 });
120
121 update.character = CharacterState::ChargedRanged(Data {
122 timer: Duration::default(),
123 stage_section: StageSection::Recover,
124 exhausted: true,
125 ..*self
126 });
127 } else if self.timer < self.static_data.charge_duration
128 && input_is_pressed(data, self.static_data.ability_info.input)
129 {
130 update.character = CharacterState::ChargedRanged(Data {
132 timer: tick_attack_or_default(data, self.timer, None),
133 ..*self
134 });
135
136 update
138 .energy
139 .change_by(-self.static_data.energy_drain * data.dt.0);
140 } else if input_is_pressed(data, self.static_data.ability_info.input) {
141 update.character = CharacterState::ChargedRanged(Data {
143 timer: tick_attack_or_default(data, self.timer, None),
144 ..*self
145 });
146
147 update
149 .energy
150 .change_by(-self.static_data.energy_drain * data.dt.0 / 5.0);
151 }
152 },
153 StageSection::Recover => {
154 if self.timer < self.static_data.recover_duration {
155 update.character = CharacterState::ChargedRanged(Data {
157 timer: tick_attack_or_default(
158 data,
159 self.timer,
160 Some(data.stats.recovery_speed_modifier),
161 ),
162 ..*self
163 });
164 } else {
165 end_ability(data, &mut update);
167 }
168 },
169 _ => {
170 end_ability(data, &mut update);
172 },
173 }
174
175 handle_interrupts(data, &mut update, output_events);
177
178 update
179 }
180}