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 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)]
18pub struct BuffDesc {
19 pub kind: BuffKind,
21 pub data: BuffData,
23}
24
25#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
27pub struct StaticData {
28 pub buildup_duration: Duration,
30 pub cast_duration: Duration,
32 pub recover_duration: Duration,
34 pub buffs: Vec<BuffDesc>,
36 pub combo_cost: u32,
39 pub combo_scaling: Option<ScalingKind>,
40 pub combo_on_use: u32,
43 pub enforced_limit: bool,
46 pub ability_info: AbilityInfo,
48 pub specifier: Option<FrontendSpecifier>,
50}
51
52#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
53pub struct Data {
54 pub static_data: StaticData,
57 pub timer: Duration,
59 pub stage_section: StageSection,
61}
62
63impl CharacterBehavior for Data {
64 fn behavior(&self, data: &JoinData, output_events: &mut OutputEvents) -> StateUpdate {
65 let mut update = StateUpdate::from(data);
66
67 handle_move(data, &mut update, 0.8);
68 handle_jump(data, output_events, &mut update, 1.0);
69
70 match self.stage_section {
71 StageSection::Buildup => {
72 if self.timer < self.static_data.buildup_duration {
73 if let CharacterState::SelfBuff(c) = &mut update.character {
75 c.timer = tick_attack_or_default(data, self.timer, None);
76 }
77 } else {
78 let combo_consumption = if self.static_data.combo_scaling.is_some() {
80 self.static_data.combo_on_use
81 } else {
82 self.static_data.combo_cost
83 };
84 output_events.emit_server(ComboChangeEvent {
85 entity: data.entity,
86 change: -(combo_consumption as i32),
87 });
88
89 let scaling_factor = self.static_data.combo_scaling.map_or(1.0, |cs| {
90 cs.factor(
91 self.static_data.combo_on_use as f32,
92 self.static_data.combo_cost as f32,
93 )
94 });
95
96 let mut buff_cat_ids = if self
97 .static_data
98 .ability_info
99 .ability
100 .is_some_and(|a| a.ability.is_from_wielded())
101 {
102 vec![BuffCategory::RemoveOnLoadoutChange]
103 } else {
104 Vec::new()
105 };
106
107 if self.static_data.enforced_limit {
109 buff_cat_ids.push(BuffCategory::SelfBuff);
110
111 output_events.emit_server(BuffEvent {
112 entity: data.entity,
113 buff_change: BuffChange::RemoveByCategory {
114 all_required: vec![BuffCategory::SelfBuff],
115 any_required: vec![],
116 none_required: vec![],
117 },
118 });
119 }
120
121 let dest_info = DestInfo {
122 stats: Some(data.stats),
123 mass: Some(data.mass),
124 };
125
126 for buff_desc in self.static_data.buffs.iter() {
128 let mut buff_data = buff_desc.data;
129 buff_data.strength *= scaling_factor;
130
131 let buff = Buff::new(
132 buff_desc.kind,
133 buff_data,
134 buff_cat_ids.clone(),
135 BuffSource::Character { by: *data.uid },
136 *data.time,
137 dest_info,
138 Some(data.mass),
139 );
140 output_events.emit_server(BuffEvent {
141 entity: data.entity,
142 buff_change: BuffChange::Add(buff),
143 });
144 }
145 if let CharacterState::SelfBuff(c) = &mut update.character {
147 c.timer = Duration::default();
148 c.stage_section = StageSection::Action;
149 }
150 }
151 },
152 StageSection::Action => {
153 if self.timer < self.static_data.cast_duration {
154 if let CharacterState::SelfBuff(c) = &mut update.character {
156 c.timer = tick_attack_or_default(data, self.timer, None);
157 }
158 if let Some(FrontendSpecifier::FromTheAshes) = self.static_data.specifier {
159 output_events.emit_local(LocalEvent::CreateOutcome(
161 Outcome::FromTheAshes { pos: data.pos.0 },
162 ));
163 }
164 } else if let CharacterState::SelfBuff(c) = &mut update.character {
165 c.timer = Duration::default();
166 c.stage_section = StageSection::Recover;
167 }
168 },
169 StageSection::Recover => {
170 if self.timer < self.static_data.recover_duration {
171 if let CharacterState::SelfBuff(c) = &mut update.character {
172 c.timer = tick_attack_or_default(
173 data,
174 self.timer,
175 Some(data.stats.recovery_speed_modifier),
176 );
177 }
178 } else {
179 end_ability(data, &mut update);
181 }
182 },
183 _ => {
184 end_ability(data, &mut update);
186 },
187 }
188
189 handle_interrupts(data, &mut update, output_events);
191
192 update
193 }
194}
195#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
197pub enum FrontendSpecifier {
198 FromTheAshes,
199}