diff --git a/assets/common/abilities/sceptre/targetedheal.ron b/assets/common/abilities/sceptre/targetedheal.ron new file mode 100644 index 0000000000..954d5c8ede --- /dev/null +++ b/assets/common/abilities/sceptre/targetedheal.ron @@ -0,0 +1,6 @@ +TargetedEffect( + buildup_duration: 0.5, + recover_duration: 0.5, + max_range: 25.0, + effect: heal: 100.0, +) \ No newline at end of file diff --git a/common/src/states/blink us.rs b/common/src/states/blink us.rs new file mode 100644 index 0000000000..884b612555 --- /dev/null +++ b/common/src/states/blink us.rs @@ -0,0 +1,98 @@ +use crate::{ + comp::{CharacterState, StateUpdate}, + event::ServerEvent, + states::{ + behavior::{CharacterBehavior, JoinData}, + utils::*, + }, +}; +use serde::{Deserialize, Serialize}; +use std::time::Duration; + +/// Separated out to condense update portions of character state +#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct StaticData { + /// How long the state builds up for + pub buildup_duration: Duration, + /// How long the state recovers for + pub recover_duration: Duration, + /// What the max range of the teleport is + pub max_range: f32, + /// Miscellaneous information about the ability + pub ability_info: AbilityInfo, +} + +#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct Data { + /// Struct containing data that does not change over the course of the + /// character state + pub static_data: StaticData, + /// Timer for each stage + pub timer: Duration, + /// What section the character stage is in + pub stage_section: StageSection, +} + +impl CharacterBehavior for Data { + fn behavior(&self, data: &JoinData) -> StateUpdate { + let mut update = StateUpdate::from(data); + + match self.stage_section { + StageSection::Buildup => { + if self.timer < self.static_data.buildup_duration { + // Build up + update.character = CharacterState::Blink(Data { + timer: self + .timer + .checked_add(Duration::from_secs_f32(data.dt.0)) + .unwrap_or_default(), + ..*self + }); + } else { + // Blinks to target location, defaults to 25 meters in front if no target + // provided + if let Some(input_attr) = self.static_data.ability_info.input_attr { + if let Some(target) = input_attr.target_entity { + update.server_events.push_front(ServerEvent::TeleportTo { + entity: data.entity, + target, + max_range: Some(self.static_data.max_range), + }); + } else if let Some(pos) = input_attr.select_pos { + update.pos.0 = pos; + } else { + update.pos.0 += *data.inputs.look_dir * 25.0; + } + } + // Transitions to recover section of stage + update.character = CharacterState::Blink(Data { + timer: Duration::default(), + stage_section: StageSection::Recover, + ..*self + }); + } + }, + StageSection::Recover => { + if self.timer < self.static_data.recover_duration { + // Recovery + update.character = CharacterState::Blink(Data { + timer: self + .timer + .checked_add(Duration::from_secs_f32(data.dt.0)) + .unwrap_or_default(), + ..*self + }); + } else { + // Done + update.character = CharacterState::Wielding; + } + }, + _ => { + // If it somehow ends up in an incorrect stage section + update.character = CharacterState::Wielding; + }, + } + + update + } +} diff --git a/common/src/states/targeted_effect.rs b/common/src/states/targeted_effect.rs new file mode 100644 index 0000000000..4c5a353d1c --- /dev/null +++ b/common/src/states/targeted_effect.rs @@ -0,0 +1,95 @@ +use crate::{ + comp::{CharacterState, StateUpdate}, + event::ServerEvent, + states::{ + behavior::{CharacterBehavior, JoinData}, + utils::*, + }, +}; +use serde::{Deserialize, Serialize}; +use std::time::Duration; + +/// Separated out to condense update portions of character state +#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct StaticData { + /// How long the state builds up for + pub buildup_duration: Duration, + /// How long the state recovers for + pub recover_duration: Duration, + /// What the max range of the teleport is + pub max_range: f32, + /// Miscellaneous information about the ability + pub ability_info: AbilityInfo, +} + +#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct Data { + /// Struct containing data that does not change over the course of the + /// character state + pub static_data: StaticData, + /// Timer for each stage + pub timer: Duration, + /// What section the character stage is in + pub stage_section: StageSection, +} + +impl CharacterBehavior for Data { + fn behavior(&self, data: &JoinData) -> StateUpdate { + let mut update = StateUpdate::from(data); + + match self.stage_section { + StageSection::Buildup => { + if self.timer < self.static_data.buildup_duration { + // Build up + update.character = CharacterState::TargetedEffect(Data { + timer: self + .timer + .checked_add(Duration::from_secs_f32(data.dt.0)) + .unwrap_or_default(), + ..*self + }); + } else { + // Heals a target + if let Some(input_attr) = self.static_data.ability_info.input_attr { + if let Some(target) = input_attr.target_entity { + update.server_events.push_front(ServerEvent::HealthChange { + entity: target, + change: HealthChange { + amount: self.static_data.heal as i32, + cause: HealthSource::Heal { by: Some(data.uid) }, + })}; + } + } + } + // Transitions to recover section of stage + update.character = CharacterState::TargetedEffect(Data { + timer: Duration::default(), + stage_section: StageSection::Recover, + ..*self + }); + } + }, + StageSection::Recover => { + if self.timer < self.static_data.recover_duration { + // Recovery + update.character = CharacterState::TargetedEffect(Data { + timer: self + .timer + .checked_add(Duration::from_secs_f32(data.dt.0)) + .unwrap_or_default(), + ..*self + }); + } else { + // Done + update.character = CharacterState::Wielding; + } + }, + _ => { + // If it somehow ends up in an incorrect stage section + update.character = CharacterState::Wielding; + }, + } + + update + } +}