veloren_common/states/
transform.rs1use std::time::Duration;
2
3use common_assets::{AssetExt, Ron};
4use rand::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) = Ron::<EntityConfig>::load(&self.static_data.target)
71 else {
72 error!(?self.static_data.target, "Failed to load entity configuration");
73 end_ability(data, &mut update);
74 return update;
75 };
76
77 let entity_info = EntityInfo::at(data.pos.0).with_entity_config(
78 entity_config.read().clone().into_inner(),
79 Some(&self.static_data.target),
80 &mut rng(),
81 None,
82 );
83
84 if let Some(specifier) = self.static_data.specifier {
86 match specifier {
87 FrontendSpecifier::Evolve => {
88 output_events.emit_local(crate::event::LocalEvent::CreateOutcome(
89 crate::outcome::Outcome::Explosion {
90 pos: data.pos.0,
91 power: 5.0,
92 radius: 2.0,
93 is_attack: false,
94 reagent: Some(Reagent::White),
95 },
96 ))
97 },
98 FrontendSpecifier::Cursekeeper => {
99 output_events.emit_local(crate::event::LocalEvent::CreateOutcome(
100 crate::outcome::Outcome::Explosion {
101 pos: data.pos.0,
102 power: 5.0,
103 radius: 2.0,
104 is_attack: false,
105 reagent: Some(Reagent::Purple),
106 },
107 ))
108 },
109 }
110 }
111
112 output_events.emit_server(TransformEvent {
113 target_entity: *data.uid,
114 entity_info,
115 allow_players: self.static_data.allow_players,
116 delete_on_failure: false,
117 });
118 update.character = CharacterState::Transform(Data {
119 static_data: self.static_data.clone(),
120 timer: Duration::default(),
121 stage_section: StageSection::Recover,
122 });
123 }
124 },
125 StageSection::Recover => {
126 if self.timer < self.static_data.recover_duration {
128 update.character = CharacterState::Transform(Data {
129 static_data: self.static_data.clone(),
130 timer: tick_attack_or_default(
131 data,
132 self.timer,
133 Some(data.stats.recovery_speed_modifier),
134 ),
135 ..*self
136 });
137 } else {
138 end_ability(data, &mut update);
140 }
141 },
142 _ => {
143 end_ability(data, &mut update);
145 },
146 }
147
148 update
149 }
150}