1use crate::{
2 combat::ScalingKind,
3 comp::{
4 CharacterState, StateUpdate,
5 buff::{Buff, BuffCategory, BuffChange, BuffData, BuffKind, BuffSource, DestInfo},
6 character_state::OutputEvents,
7 },
8 event::{BuffEvent, ComboChangeEvent, LocalEvent},
9 outcome::Outcome,
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)]
19pub struct BuffDesc {
20 pub kind: BuffKind,
22 pub data: BuffData,
24}
25
26#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
28pub struct StaticData {
29 pub buildup_duration: Duration,
31 pub cast_duration: Duration,
33 pub recover_duration: Duration,
35 pub buffs: Vec<BuffDesc>,
37 pub buff_cat: Option<BuffCategory>,
38 pub combo_cost: u32,
41 pub combo_scaling: Option<ScalingKind>,
42 pub combo_on_use: u32,
45 pub enforced_limit: bool,
48 pub ability_info: AbilityInfo,
50 pub specifier: Option<FrontendSpecifier>,
52}
53
54#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
55pub struct Data {
56 pub static_data: StaticData,
59 pub timer: Duration,
61 pub stage_section: StageSection,
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_move(data, &mut update, 0.8);
70 handle_jump(data, output_events, &mut update, 1.0);
71
72 match self.stage_section {
73 StageSection::Buildup => {
74 if self.timer < self.static_data.buildup_duration {
75 if let CharacterState::SelfBuff(c) = &mut update.character {
77 c.timer = tick_attack_or_default(data, self.timer, None);
78 }
79 } else {
80 let combo_consumption = if self.static_data.combo_scaling.is_some() {
82 self.static_data.combo_on_use
83 } else {
84 self.static_data.combo_cost
85 };
86 output_events.emit_server(ComboChangeEvent {
87 entity: data.entity,
88 change: -(combo_consumption as i32),
89 });
90
91 let scaling_factor = self.static_data.combo_scaling.map_or(1.0, |cs| {
92 cs.factor(
93 self.static_data.combo_on_use as f32,
94 self.static_data.combo_cost as f32,
95 )
96 });
97
98 let mut buff_cat_ids = self
99 .static_data
100 .buff_cat
101 .clone()
102 .into_iter()
103 .collect::<Vec<_>>();
104
105 if self
106 .static_data
107 .ability_info
108 .ability
109 .is_some_and(|a| a.ability.is_from_wielded())
110 {
111 buff_cat_ids.push(BuffCategory::RemoveOnLoadoutChange);
112 }
113
114 if self.static_data.enforced_limit {
116 buff_cat_ids.push(BuffCategory::SelfBuff);
117
118 output_events.emit_server(BuffEvent {
119 entity: data.entity,
120 buff_change: BuffChange::RemoveByCategory {
121 all_required: vec![BuffCategory::SelfBuff],
122 any_required: vec![],
123 none_required: vec![],
124 },
125 });
126 }
127
128 let dest_info = DestInfo {
129 stats: Some(data.stats),
130 mass: Some(data.mass),
131 };
132
133 for buff_desc in self.static_data.buffs.iter() {
135 let mut buff_data = buff_desc.data;
136 buff_data.strength *= scaling_factor;
137
138 let buff = Buff::new(
139 buff_desc.kind,
140 buff_data,
141 buff_cat_ids.clone(),
142 BuffSource::Character {
143 by: *data.uid,
144 tool_kind: self.static_data.ability_info.tool,
145 },
146 *data.time,
147 dest_info,
148 Some(data.mass),
149 );
150 output_events.emit_server(BuffEvent {
151 entity: data.entity,
152 buff_change: BuffChange::Add(buff),
153 });
154 }
155 if let CharacterState::SelfBuff(c) = &mut update.character {
157 c.timer = Duration::default();
158 c.stage_section = StageSection::Action;
159 }
160 }
161 },
162 StageSection::Action => {
163 if self.timer < self.static_data.cast_duration {
164 if let CharacterState::SelfBuff(c) = &mut update.character {
166 c.timer = tick_attack_or_default(data, self.timer, None);
167 }
168 if let Some(FrontendSpecifier::FromTheAshes) = self.static_data.specifier {
169 output_events.emit_local(LocalEvent::CreateOutcome(
171 Outcome::FromTheAshes { pos: data.pos.0 },
172 ));
173 }
174 } else if let CharacterState::SelfBuff(c) = &mut update.character {
175 c.timer = Duration::default();
176 c.stage_section = StageSection::Recover;
177 }
178 },
179 StageSection::Recover => {
180 if self.timer < self.static_data.recover_duration {
181 if let CharacterState::SelfBuff(c) = &mut update.character {
182 c.timer = tick_attack_or_default(
183 data,
184 self.timer,
185 Some(data.stats.recovery_speed_modifier),
186 );
187 }
188 } else {
189 end_ability(data, &mut update);
191 }
192 },
193 _ => {
194 end_ability(data, &mut update);
196 },
197 }
198
199 handle_interrupts(data, &mut update, output_events);
201
202 update
203 }
204}
205#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
207pub enum FrontendSpecifier {
208 FromTheAshes,
209}