1use crate::{
2 comp::{
3 CharacterState, StateUpdate,
4 buff::{Buff, BuffCategory, BuffChange, BuffData, BuffKind, BuffSource, DestInfo},
5 character_state::OutputEvents,
6 },
7 event::{BuffEvent, ComboChangeEvent, LocalEvent},
8 outcome::Outcome,
9 resources::Secs,
10 states::{
11 behavior::{CharacterBehavior, JoinData},
12 utils::*,
13 },
14};
15use serde::{Deserialize, Serialize};
16use std::time::Duration;
17
18#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
20pub struct StaticData {
21 pub buildup_duration: Duration,
23 pub cast_duration: Duration,
25 pub recover_duration: Duration,
27 pub buff_kind: BuffKind,
29 pub buff_strength: f32,
31 pub buff_duration: Option<Secs>,
33 pub combo_cost: u32,
36 pub combo_scaling: Option<ScalingKind>,
37 pub combo_on_use: u32,
40 pub enforced_limit: bool,
43 pub ability_info: AbilityInfo,
45 pub specifier: Option<FrontendSpecifier>,
47}
48
49#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
50pub struct Data {
51 pub static_data: StaticData,
54 pub timer: Duration,
56 pub stage_section: StageSection,
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_move(data, &mut update, 0.8);
65 handle_jump(data, output_events, &mut update, 1.0);
66
67 match self.stage_section {
68 StageSection::Buildup => {
69 if self.timer < self.static_data.buildup_duration {
70 update.character = CharacterState::SelfBuff(Data {
72 timer: tick_attack_or_default(data, self.timer, None),
73 ..*self
74 });
75 } else {
76 let combo_consumption = if self.static_data.combo_scaling.is_some() {
78 self.static_data.combo_on_use
79 } else {
80 self.static_data.combo_cost
81 };
82 output_events.emit_server(ComboChangeEvent {
83 entity: data.entity,
84 change: -(combo_consumption as i32),
85 });
86
87 let scaling_factor = self.static_data.combo_scaling.map_or(1.0, |cs| {
88 cs.factor(
89 self.static_data.combo_on_use as f32,
90 self.static_data.combo_cost as f32,
91 )
92 });
93
94 let mut buff_cat_ids = if self
95 .static_data
96 .ability_info
97 .ability
98 .is_some_and(|a| a.ability.is_from_wielded())
99 {
100 vec![BuffCategory::RemoveOnLoadoutChange]
101 } else {
102 Vec::new()
103 };
104
105 if self.static_data.enforced_limit {
107 buff_cat_ids.push(BuffCategory::SelfBuff);
108
109 output_events.emit_server(BuffEvent {
110 entity: data.entity,
111 buff_change: BuffChange::RemoveByCategory {
112 all_required: vec![BuffCategory::SelfBuff],
113 any_required: vec![],
114 none_required: vec![],
115 },
116 });
117 }
118
119 let dest_info = DestInfo {
120 stats: Some(data.stats),
121 mass: Some(data.mass),
122 };
123
124 let buff = Buff::new(
126 self.static_data.buff_kind,
127 BuffData::new(
128 self.static_data.buff_strength * scaling_factor,
129 self.static_data.buff_duration,
130 ),
131 buff_cat_ids,
132 BuffSource::Character { by: *data.uid },
133 *data.time,
134 dest_info,
135 Some(data.mass),
136 );
137 output_events.emit_server(BuffEvent {
138 entity: data.entity,
139 buff_change: BuffChange::Add(buff),
140 });
141 update.character = CharacterState::SelfBuff(Data {
143 timer: Duration::default(),
144 stage_section: StageSection::Action,
145 ..*self
146 });
147 }
148 },
149 StageSection::Action => {
150 if self.timer < self.static_data.cast_duration {
151 update.character = CharacterState::SelfBuff(Data {
153 timer: tick_attack_or_default(data, self.timer, None),
154 ..*self
155 });
156 if let Some(FrontendSpecifier::FromTheAshes) = self.static_data.specifier {
157 output_events.emit_local(LocalEvent::CreateOutcome(
159 Outcome::FromTheAshes { pos: data.pos.0 },
160 ));
161 }
162 } else {
163 update.character = CharacterState::SelfBuff(Data {
164 timer: Duration::default(),
165 stage_section: StageSection::Recover,
166 ..*self
167 });
168 }
169 },
170 StageSection::Recover => {
171 if self.timer < self.static_data.recover_duration {
172 update.character = CharacterState::SelfBuff(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_ability(data, &mut update);
183 }
184 },
185 _ => {
186 end_ability(data, &mut update);
188 },
189 }
190
191 handle_interrupts(data, &mut update, output_events);
193
194 update
195 }
196}
197#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
199pub enum FrontendSpecifier {
200 FromTheAshes,
201}