1use crate::{
2 combat::GroupTarget,
3 comp::{
4 CharacterState, StateUpdate,
5 aura::{AuraBuffConstructor, AuraChange, AuraKind, AuraTarget, Specifier},
6 character_state::OutputEvents,
7 },
8 event::{AuraEvent, ComboChangeEvent},
9 resources::Secs,
10 states::{
11 behavior::{CharacterBehavior, JoinData},
12 utils::*,
13 },
14};
15use serde::{Deserialize, Serialize};
16use std::time::Duration;
17
18#[derive(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 targets: GroupTarget,
29 pub auras: Vec<AuraBuffConstructor>,
31 pub aura_duration: Option<Secs>,
33 pub range: f32,
35 pub ability_info: AbilityInfo,
37 pub scales_with_combo: bool,
39 pub combo_at_cast: u32,
41 pub specifier: Option<Specifier>,
43}
44
45#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
46pub struct Data {
47 pub static_data: StaticData,
50 pub timer: Duration,
52 pub stage_section: StageSection,
54}
55
56impl CharacterBehavior for Data {
57 fn behavior(&self, data: &JoinData, output_events: &mut OutputEvents) -> StateUpdate {
58 let mut update = StateUpdate::from(data);
59
60 handle_orientation(data, &mut update, 1.0, None);
61 handle_move(data, &mut update, 0.8);
62 handle_jump(data, output_events, &mut update, 1.0);
63
64 match self.stage_section {
65 StageSection::Buildup => {
66 if self.timer < self.static_data.buildup_duration {
67 update.character = CharacterState::BasicAura(Data {
69 static_data: self.static_data.clone(),
70 timer: tick_attack_or_default(data, self.timer, None),
71 ..*self
72 });
73 } else {
74 let targets =
76 AuraTarget::from((Some(self.static_data.targets), Some(data.uid)));
77 for aura_data in &self.static_data.auras {
78 let mut aura = aura_data.to_aura(
79 data.uid,
80 self.static_data.range,
81 self.static_data.aura_duration,
83 targets,
84 *data.time,
85 );
86 if self.static_data.scales_with_combo {
87 match aura.aura_kind {
88 AuraKind::Buff {
89 kind: _,
90 ref mut data,
91 category: _,
92 source: _,
93 } => {
94 data.strength *=
95 (self.static_data.combo_at_cast.max(1) as f32).sqrt();
96 },
97 AuraKind::FriendlyFire | AuraKind::ForcePvP => {},
98 }
99 output_events.emit_server(ComboChangeEvent {
100 entity: data.entity,
101 change: -(self.static_data.combo_at_cast as i32),
102 });
103 }
104 output_events.emit_server(AuraEvent {
105 entity: data.entity,
106 aura_change: AuraChange::Add(aura),
107 });
108 }
109 update.character = CharacterState::BasicAura(Data {
111 static_data: self.static_data.clone(),
112 timer: Duration::default(),
113 stage_section: StageSection::Action,
114 });
115 }
116 },
117 StageSection::Action => {
118 if self.timer < self.static_data.cast_duration {
119 update.character = CharacterState::BasicAura(Data {
121 static_data: self.static_data.clone(),
122 timer: tick_attack_or_default(data, self.timer, None),
123 ..*self
124 });
125 } else {
126 update.character = CharacterState::BasicAura(Data {
127 static_data: self.static_data.clone(),
128 timer: Duration::default(),
129 stage_section: StageSection::Recover,
130 });
131 }
132 },
133 StageSection::Recover => {
134 if self.timer < self.static_data.recover_duration {
135 update.character = CharacterState::BasicAura(Data {
136 static_data: self.static_data.clone(),
137 timer: tick_attack_or_default(
138 data,
139 self.timer,
140 Some(data.stats.recovery_speed_modifier),
141 ),
142 ..*self
143 });
144 } else {
145 end_ability(data, &mut update);
147 }
148 },
149 _ => {
150 end_ability(data, &mut update);
152 },
153 }
154
155 handle_interrupts(data, &mut update, output_events);
157
158 update
159 }
160}