veloren_common/states/
transform.rs1use std::time::Duration;
2
3use common_assets::AssetExt;
4use rand::thread_rng;
5use serde::{Deserialize, Serialize};
6use tracing::error;
7
8use crate::{
9 comp::{CharacterState, StateUpdate, item::Reagent},
10 event::TransformEvent,
11 generation::{EntityConfig, EntityInfo},
12 states::utils::{end_ability, tick_attack_or_default},
13};
14
15use super::{
16 behavior::CharacterBehavior,
17 utils::{AbilityInfo, StageSection},
18};
19
20#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
21pub enum FrontendSpecifier {
22 Evolve,
23 Cursekeeper,
24}
25
26#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
27pub struct StaticData {
28 pub buildup_duration: Duration,
30 pub recover_duration: Duration,
32 pub target: String,
34 pub ability_info: AbilityInfo,
35 pub allow_players: bool,
37 pub specifier: Option<FrontendSpecifier>,
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}
51
52impl CharacterBehavior for Data {
53 fn behavior(
54 &self,
55 data: &super::behavior::JoinData,
56 output_events: &mut crate::comp::character_state::OutputEvents,
57 ) -> crate::comp::StateUpdate {
58 let mut update = StateUpdate::from(data);
59 match self.stage_section {
60 StageSection::Buildup => {
61 if self.timer < self.static_data.buildup_duration {
63 update.character = CharacterState::Transform(Data {
64 static_data: self.static_data.clone(),
65 timer: tick_attack_or_default(data, self.timer, None),
66 ..*self
67 });
68 } else {
70 let Ok(entity_config) = EntityConfig::load(&self.static_data.target) else {
71 error!(?self.static_data.target, "Failed to load entity configuration");
72 end_ability(data, &mut update);
73 return update;
74 };
75
76 let entity_info = EntityInfo::at(data.pos.0).with_entity_config(
77 entity_config.read().clone(),
78 Some(&self.static_data.target),
79 &mut thread_rng(),
80 None,
81 );
82
83 if let Some(specifier) = self.static_data.specifier {
85 match specifier {
86 FrontendSpecifier::Evolve => {
87 output_events.emit_local(crate::event::LocalEvent::CreateOutcome(
88 crate::outcome::Outcome::Explosion {
89 pos: data.pos.0,
90 power: 5.0,
91 radius: 2.0,
92 is_attack: false,
93 reagent: Some(Reagent::White),
94 },
95 ))
96 },
97 FrontendSpecifier::Cursekeeper => {
98 output_events.emit_local(crate::event::LocalEvent::CreateOutcome(
99 crate::outcome::Outcome::Explosion {
100 pos: data.pos.0,
101 power: 5.0,
102 radius: 2.0,
103 is_attack: false,
104 reagent: Some(Reagent::Purple),
105 },
106 ))
107 },
108 }
109 }
110
111 output_events.emit_server(TransformEvent {
112 target_entity: *data.uid,
113 entity_info,
114 allow_players: self.static_data.allow_players,
115 delete_on_failure: false,
116 });
117 update.character = CharacterState::Transform(Data {
118 static_data: self.static_data.clone(),
119 timer: Duration::default(),
120 stage_section: StageSection::Recover,
121 });
122 }
123 },
124 StageSection::Recover => {
125 if self.timer < self.static_data.recover_duration {
127 update.character = CharacterState::Transform(Data {
128 static_data: self.static_data.clone(),
129 timer: tick_attack_or_default(
130 data,
131 self.timer,
132 Some(data.stats.recovery_speed_modifier),
133 ),
134 ..*self
135 });
136 } else {
137 end_ability(data, &mut update);
139 }
140 },
141 _ => {
142 end_ability(data, &mut update);
144 },
145 }
146
147 update
148 }
149}