From 7df6b781f1e867dc8fa2f8565fa15c8c6989177f Mon Sep 17 00:00:00 2001 From: Sam Date: Fri, 25 Sep 2020 15:02:30 -0500 Subject: [PATCH] Added keyframe support to basic beam state. Addressed some comments. --- assets/voxygen/shaders/particle-vert.glsl | 4 +- common/src/comp/ability.rs | 29 +-- common/src/states/basic_beam.rs | 263 ++++++++++------------ common/src/states/dash_melee.rs | 6 + common/src/states/utils.rs | 1 + voxygen/src/anim/src/character/dash.rs | 1 + voxygen/src/scene/figure/mod.rs | 1 + voxygen/src/scene/particle.rs | 16 +- 8 files changed, 156 insertions(+), 165 deletions(-) diff --git a/assets/voxygen/shaders/particle-vert.glsl b/assets/voxygen/shaders/particle-vert.glsl index 83120dfcab..35a60a9840 100644 --- a/assets/voxygen/shaders/particle-vert.glsl +++ b/assets/voxygen/shaders/particle-vert.glsl @@ -125,7 +125,7 @@ vec3 spiral_motion(vec3 line, float radius, float time_function) { return line * time_function + vec3( radius * cos(10 * time_function - inst_time) * axis2.x + radius * sin(10 * time_function - inst_time) * axis3.x, radius * cos(10 * time_function - inst_time) * axis2.y + radius * sin(10 * time_function - inst_time) * axis3.y, - radius * cos(10 * time_function - inst_time) * axis2.z + radius * sin(10 * time_function - inst_time) * axis3.z + 1.0); + radius * cos(10 * time_function - inst_time) * axis2.z + radius * sin(10 * time_function - inst_time) * axis3.z); } void main() { @@ -275,7 +275,7 @@ void main() { ); } else if (inst_mode == HEALING_BEAM) { attr = Attr( - spiral_motion(beam_pos2() - inst_pos, 0.3 * (floor(2 * hash(vec4(inst_time)) + 0.5) - 0.5), lifetime / 1), // The 1 that lifetime is divided by is the duration of the beam. It is currently hardcoded here due to limitations in passing in variables. + spiral_motion(beam_pos2() - inst_pos, 0.3 * (floor(2 * hash(vec4(inst_time)) + 0.5) - 0.5) * min(linear_scale(10), 1), lifetime / 1), // The 1 that lifetime is divided by is the duration of the beam. It is currently hardcoded here due to limitations in passing in variables. vec3((1.7 - 0.7 * abs(floor(2 * hash(vec4(inst_time)) - 0.5) + 0.5)) * (1.5 + 0.5 * sin(tick.x * 10 - lifetime * 4))), vec4(vec3(0.3, 0.7 + 0.4 * sin(tick.x * 8 - lifetime * 3), 0.3 + 0.1 * sin (tick.x * 2)), 0.3), spin_in_axis(vec3(inst_entropy, inst_misc, inst_lifespan), tick.z) diff --git a/common/src/comp/ability.rs b/common/src/comp/ability.rs index 3908772544..345ad6997b 100644 --- a/common/src/comp/ability.rs +++ b/common/src/comp/ability.rs @@ -532,20 +532,23 @@ impl From<&CharacterAbility> for CharacterState { energy_regen, energy_drain, } => CharacterState::BasicBeam(basic_beam::Data { - exhausted: false, + static_data: basic_beam::StaticData { + buildup_duration: *buildup_duration, + recover_duration: *recover_duration, + beam_duration: *beam_duration, + base_hps: *base_hps, + base_dps: *base_dps, + tick_rate: *tick_rate, + range: *range, + max_angle: *max_angle, + lifesteal_eff: *lifesteal_eff, + energy_regen: *energy_regen, + energy_drain: *energy_drain, + }, + timer: Duration::default(), + stage_section: StageSection::Buildup, particle_ori: None::>, - buildup_duration: *buildup_duration, - cooldown_duration: Duration::default(), - recover_duration: *recover_duration, - beam_duration: *beam_duration, - base_hps: *base_hps, - base_dps: *base_dps, - tick_rate: *tick_rate, - range: *range, - max_angle: *max_angle, - lifesteal_eff: *lifesteal_eff, - energy_regen: *energy_regen, - energy_drain: *energy_drain, + offset: 0.0, }), } } diff --git a/common/src/states/basic_beam.rs b/common/src/states/basic_beam.rs index 830babef21..a811648719 100644 --- a/common/src/states/basic_beam.rs +++ b/common/src/states/basic_beam.rs @@ -1,7 +1,7 @@ use crate::{ - comp::{beam, CharacterState, Ori, Pos, StateUpdate}, + comp::{beam, humanoid, Body, CharacterState, Ori, Pos, StateUpdate}, event::ServerEvent, - states::utils::*, + states::utils::{StageSection, *}, sync::Uid, sys::character_behavior::*, }; @@ -9,16 +9,11 @@ use serde::{Deserialize, Serialize}; use std::time::Duration; use vek::Vec3; +/// Separated out to condense update portions of character state #[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] -pub struct Data { - /// Whether the attack can currently deal damage - pub exhausted: bool, - /// Used for particle stuffs - pub particle_ori: Option>, +pub struct StaticData { /// How long until state should deal damage or heal pub buildup_duration: Duration, - /// How long until weapon can deal another tick of damage - pub cooldown_duration: Duration, /// How long the state has until exiting pub recover_duration: Duration, /// How long each beam segment persists for @@ -42,6 +37,21 @@ pub struct Data { pub energy_drain: u32, } +#[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, + /// Used for particle stuffs + pub particle_ori: Option>, + /// Used to offset beam and particles + pub offset: f32, +} + impl CharacterBehavior for Data { fn behavior(&self, data: &JoinData) -> StateUpdate { let mut update = StateUpdate::from(data); @@ -54,139 +64,110 @@ impl CharacterBehavior for Data { return update; } - if self.buildup_duration != Duration::default() { - // Creates beam - data.updater.insert(data.entity, beam::Beam { - hit_entities: Vec::::new(), - tick_dur: Duration::from_secs_f32(1.0 / self.tick_rate), - timer: Duration::default(), - }); - // Build up - update.character = CharacterState::BasicBeam(Data { - exhausted: self.exhausted, - particle_ori: Some(*data.inputs.look_dir), - buildup_duration: self - .buildup_duration - .checked_sub(Duration::from_secs_f32(data.dt.0)) - .unwrap_or_default(), - cooldown_duration: self.cooldown_duration, - recover_duration: self.recover_duration, - beam_duration: self.beam_duration, - base_hps: self.base_hps, - base_dps: self.base_dps, - tick_rate: self.tick_rate, - range: self.range, - max_angle: self.max_angle, - lifesteal_eff: self.lifesteal_eff, - energy_regen: self.energy_regen, - energy_drain: self.energy_drain, - }); - } else if data.inputs.primary.is_pressed() && !self.exhausted { - let damage = (self.base_dps as f32 / self.tick_rate) as u32; - let heal = (self.base_hps as f32 / self.tick_rate) as u32; - let energy_regen = (self.energy_regen as f32 / self.tick_rate) as u32; - let energy_drain = (self.energy_drain as f32 / self.tick_rate) as u32; - let speed = self.range / self.beam_duration.as_secs_f32(); - let properties = beam::Properties { - angle: self.max_angle.to_radians(), - speed, - damage, - heal, - lifesteal_eff: self.lifesteal_eff, - energy_regen, - energy_drain, - duration: self.beam_duration, - owner: Some(*data.uid), - }; - let pos = Pos(data.pos.0 + Vec3::new(0.0, 0.0, 1.0)); - // Create beam segment - update.server_events.push_front(ServerEvent::BeamSegment { - properties, - pos, - ori: Ori(data.inputs.look_dir), - }); + match self.stage_section { + StageSection::Buildup => { + if self.timer < self.static_data.buildup_duration { + // Build up + update.character = CharacterState::BasicBeam(Data { + static_data: self.static_data, + timer: self + .timer + .checked_add(Duration::from_secs_f32(data.dt.0)) + .unwrap_or_default(), + stage_section: self.stage_section, + particle_ori: Some(*data.inputs.look_dir), + offset: self.offset, + }); + } else { + // Creates beam + data.updater.insert(data.entity, beam::Beam { + hit_entities: Vec::::new(), + tick_dur: Duration::from_secs_f32(1.0 / self.static_data.tick_rate), + timer: Duration::default(), + }); + // Gets offset + let eye_height = match data.body { + Body::Humanoid(body) => body.eye_height(), + _ => humanoid::DEFAULT_HUMANOID_EYE_HEIGHT, + }; + // Build up + update.character = CharacterState::BasicBeam(Data { + static_data: self.static_data, + timer: Duration::default(), + stage_section: StageSection::Cast, + particle_ori: Some(*data.inputs.look_dir), + offset: eye_height * 0.9, - update.character = CharacterState::BasicBeam(Data { - exhausted: true, - particle_ori: Some(*data.inputs.look_dir), - buildup_duration: self.buildup_duration, - recover_duration: self.recover_duration, - cooldown_duration: Duration::from_secs_f32(1.0 / self.tick_rate / 10.0), - beam_duration: self.beam_duration, - base_hps: self.base_hps, - base_dps: self.base_dps, - tick_rate: self.tick_rate, - range: self.range, - max_angle: self.max_angle, - lifesteal_eff: self.lifesteal_eff, - energy_regen: self.energy_regen, - energy_drain: self.energy_drain, - }); - } else if data.inputs.primary.is_pressed() && self.cooldown_duration != Duration::default() - { - // Cooldown until next tick of damage - update.character = CharacterState::BasicBeam(Data { - exhausted: self.exhausted, - particle_ori: Some(*data.inputs.look_dir), - buildup_duration: self.buildup_duration, - cooldown_duration: self - .cooldown_duration - .checked_sub(Duration::from_secs_f32(data.dt.0)) - .unwrap_or_default(), - recover_duration: self.recover_duration, - beam_duration: self.beam_duration, - base_hps: self.base_hps, - base_dps: self.base_dps, - tick_rate: self.tick_rate, - range: self.range, - max_angle: self.max_angle, - lifesteal_eff: self.lifesteal_eff, - energy_regen: self.energy_regen, - energy_drain: self.energy_drain, - }); - } else if data.inputs.primary.is_pressed() { - update.character = CharacterState::BasicBeam(Data { - exhausted: false, - particle_ori: Some(*data.inputs.look_dir), - buildup_duration: self.buildup_duration, - recover_duration: self.recover_duration, - cooldown_duration: self.cooldown_duration, - beam_duration: self.beam_duration, - base_hps: self.base_hps, - base_dps: self.base_dps, - tick_rate: self.tick_rate, - range: self.range, - max_angle: self.max_angle, - lifesteal_eff: self.lifesteal_eff, - energy_regen: self.energy_regen, - energy_drain: self.energy_drain, - }); - } else if self.recover_duration != Duration::default() { - // Recovery - update.character = CharacterState::BasicBeam(Data { - exhausted: self.exhausted, - particle_ori: Some(*data.inputs.look_dir), - buildup_duration: self.buildup_duration, - cooldown_duration: self.cooldown_duration, - recover_duration: self - .recover_duration - .checked_sub(Duration::from_secs_f32(data.dt.0)) - .unwrap_or_default(), - beam_duration: self.beam_duration, - base_hps: self.base_hps, - base_dps: self.base_dps, - tick_rate: self.tick_rate, - range: self.range, - max_angle: self.max_angle, - lifesteal_eff: self.lifesteal_eff, - energy_regen: self.energy_regen, - energy_drain: self.energy_drain, - }); - } else { - // Done - update.character = CharacterState::Wielding; - // Make sure beam component is removed - data.updater.remove::(data.entity); + }); + } + }, + StageSection::Cast => { + if data.inputs.primary.is_pressed() { + let damage = (self.static_data.base_dps as f32 / self.static_data.tick_rate) as u32; + let heal = (self.static_data.base_hps as f32 / self.static_data.tick_rate) as u32; + let energy_regen = (self.static_data.energy_regen as f32 / self.static_data.tick_rate) as u32; + let energy_drain = (self.static_data.energy_drain as f32 / self.static_data.tick_rate) as u32; + let speed = self.static_data.range / self.static_data.beam_duration.as_secs_f32(); + let properties = beam::Properties { + angle: self.static_data.max_angle.to_radians(), + speed, + damage, + heal, + lifesteal_eff: self.static_data.lifesteal_eff, + energy_regen, + energy_drain, + duration: self.static_data.beam_duration, + owner: Some(*data.uid), + }; + let pos = Pos(data.pos.0 + Vec3::new(0.0, 0.0, self.offset)); + // Create beam segment + update.server_events.push_front(ServerEvent::BeamSegment { + properties, + pos, + ori: Ori(data.inputs.look_dir), + }); + update.character = CharacterState::BasicBeam(Data { + static_data: self.static_data, + timer: self.timer, + stage_section: self.stage_section, + particle_ori: Some(*data.inputs.look_dir), + offset: self.offset, + }); + } else { + update.character = CharacterState::BasicBeam(Data { + static_data: self.static_data, + timer: Duration::default(), + stage_section: StageSection::Recover, + particle_ori: Some(*data.inputs.look_dir), + offset: self.offset, + }); + } + }, + StageSection::Recover => { + if self.timer < self.static_data.recover_duration { + update.character = CharacterState::BasicBeam(Data { + static_data: self.static_data, + timer: self + .timer + .checked_add(Duration::from_secs_f32(data.dt.0)) + .unwrap_or_default(), + stage_section: self.stage_section, + particle_ori: Some(*data.inputs.look_dir), + offset: self.offset, + }); + } else { + // Done + update.character = CharacterState::Wielding; + // Make sure attack component is removed + data.updater.remove::(data.entity); + } + }, + _ => { + // If it somehow ends up in an incorrect stage section + update.character = CharacterState::Wielding; + // Make sure attack component is removed + data.updater.remove::(data.entity); + }, } update diff --git a/common/src/states/dash_melee.rs b/common/src/states/dash_melee.rs index 556a4a40ea..9e94f3768b 100644 --- a/common/src/states/dash_melee.rs +++ b/common/src/states/dash_melee.rs @@ -252,6 +252,12 @@ impl CharacterBehavior for Data { data.updater.remove::(data.entity); } }, + _ => { + // If it somehow ends up in an incorrect stage section + update.character = CharacterState::Wielding; + // Make sure attack component is removed + data.updater.remove::(data.entity); + }, } update diff --git a/common/src/states/utils.rs b/common/src/states/utils.rs index 475a5cf13f..c970d9cfad 100644 --- a/common/src/states/utils.rs +++ b/common/src/states/utils.rs @@ -363,4 +363,5 @@ pub enum StageSection { Swing, Recover, Charge, + Cast, } diff --git a/voxygen/src/anim/src/character/dash.rs b/voxygen/src/anim/src/character/dash.rs index 9d15e0514c..d2499e590f 100644 --- a/voxygen/src/anim/src/character/dash.rs +++ b/voxygen/src/anim/src/character/dash.rs @@ -180,6 +180,7 @@ impl Animation for DashAnimation { Quaternion::rotation_x(-1.5) * Quaternion::rotation_y(-1.0); next.control.scale = Vec3::one(); }, + _ => {}, } } }, diff --git a/voxygen/src/scene/figure/mod.rs b/voxygen/src/scene/figure/mod.rs index c799e24ebb..70300051d1 100644 --- a/voxygen/src/scene/figure/mod.rs +++ b/voxygen/src/scene/figure/mod.rs @@ -913,6 +913,7 @@ impl FigureMgr { StageSection::Recover => { stage_time / s.static_data.recover_duration.as_secs_f64() }, + _ => 0.0, }; anim::character::DashAnimation::update_skeleton( &target_base, diff --git a/voxygen/src/scene/particle.rs b/voxygen/src/scene/particle.rs index f30027b0e2..8ec43a9689 100644 --- a/voxygen/src/scene/particle.rs +++ b/voxygen/src/scene/particle.rs @@ -14,6 +14,7 @@ use common::{ span, spiral::Spiral2d, state::DeltaTime, + states::utils::StageSection, terrain::TerrainChunk, vol::{RectRasterableVol, SizedVol}, }; @@ -352,17 +353,14 @@ impl ParticleMgr { { if let CharacterState::BasicBeam(b) = character_state { let particle_ori = b.particle_ori.unwrap_or(*ori.vec()); - for i in 0..self.scheduler.heartbeats(Duration::from_millis(1)) { - if b.buildup_duration == Duration::default() { + if b.stage_section == StageSection::Cast { + for i in 0..self.scheduler.heartbeats(Duration::from_millis(1)) { self.particles.push(Particle::new_beam( - b.beam_duration, - time + (i as f64) / 1000.0, + b.static_data.beam_duration, + time + i as f64 / 1000.0, ParticleMode::HealingBeam, - pos.0 - + (i as f32 / b.beam_duration.as_millis() as f32) - * particle_ori - * b.range, - pos.0 + particle_ori * b.range, + pos.0 + particle_ori * 0.5 + Vec3::new(0.0, 0.0, b.offset), + pos.0 + particle_ori * b.static_data.range + Vec3::new(0.0, 0.0, b.offset), )); } }