From a1bbc136fcfaa60fe3f69338e0dcd7a6c831d208 Mon Sep 17 00:00:00 2001 From: Sam Date: Mon, 1 Mar 2021 15:44:29 -0500 Subject: [PATCH 01/18] New abilities for sceptre are done --- .../common/abilities/sceptre/healingbeam.ron | 8 +- .../common/abilities/sceptre/healingbomb.ron | 19 --- .../abilities/sceptre/lifestealbeam.ron | 15 ++ .../common/abilities/sceptre/wardingaura.ron | 14 ++ .../abilities/weapon_ability_manifest.ron | 8 +- common/src/comp/ability.rs | 94 +++++++----- common/src/comp/aura.rs | 45 +++++- common/src/comp/character_state.rs | 4 + common/src/states/cast_aura.rs | 135 ++++++++++++++++++ common/src/states/mod.rs | 1 + common/sys/src/aura.rs | 9 +- common/sys/src/character_behavior.rs | 2 + common/sys/src/stats.rs | 3 +- server/src/state_ext.rs | 2 + 14 files changed, 294 insertions(+), 65 deletions(-) delete mode 100644 assets/common/abilities/sceptre/healingbomb.ron create mode 100644 assets/common/abilities/sceptre/lifestealbeam.ron create mode 100644 assets/common/abilities/sceptre/wardingaura.ron create mode 100644 common/src/states/cast_aura.rs diff --git a/assets/common/abilities/sceptre/healingbeam.ron b/assets/common/abilities/sceptre/healingbeam.ron index 8b6b235b6f..90a23b9b8d 100644 --- a/assets/common/abilities/sceptre/healingbeam.ron +++ b/assets/common/abilities/sceptre/healingbeam.ron @@ -2,13 +2,13 @@ BasicBeam( buildup_duration: 0.25, recover_duration: 0.25, beam_duration: 1.0, - base_hps: 60, - base_dps: 60, + base_hps: 80, + base_dps: 0, tick_rate: 2.0, range: 25.0, max_angle: 1.0, - lifesteal_eff: 0.15, - energy_regen: 25, + lifesteal_eff: 0.0, + energy_regen: 0, energy_cost: 50, energy_drain: 0, orientation_behavior: Normal, diff --git a/assets/common/abilities/sceptre/healingbomb.ron b/assets/common/abilities/sceptre/healingbomb.ron deleted file mode 100644 index a7d0be6de3..0000000000 --- a/assets/common/abilities/sceptre/healingbomb.ron +++ /dev/null @@ -1,19 +0,0 @@ -BasicRanged( - energy_cost: 450, - buildup_duration: 0.8, - recover_duration: 0.05, - projectile: Heal( - heal: 80.0, - damage: 60.0, - poise_damage: 0, - radius: 6.0, - ), - projectile_body: Object(BoltNature), - /*projectile_light: Some(LightEmitter { - col: (0.0, 1.0, 0.0).into(), - ..Default::default() - }),*/ - projectile_gravity: Some(Gravity(0.5)), - projectile_speed: 40.0, - can_continue: false, -) diff --git a/assets/common/abilities/sceptre/lifestealbeam.ron b/assets/common/abilities/sceptre/lifestealbeam.ron new file mode 100644 index 0000000000..5f403d85d9 --- /dev/null +++ b/assets/common/abilities/sceptre/lifestealbeam.ron @@ -0,0 +1,15 @@ +BasicBeam( + buildup_duration: 0.25, + recover_duration: 0.25, + beam_duration: 1.0, + base_hps: 0, + base_dps: 80, + tick_rate: 2.0, + range: 25.0, + max_angle: 1.0, + lifesteal_eff: 0.15, + energy_regen: 25, + energy_cost: 0, + energy_drain: 0, + orientation_behavior: Normal, +) \ No newline at end of file diff --git a/assets/common/abilities/sceptre/wardingaura.ron b/assets/common/abilities/sceptre/wardingaura.ron new file mode 100644 index 0000000000..0fb1815e7c --- /dev/null +++ b/assets/common/abilities/sceptre/wardingaura.ron @@ -0,0 +1,14 @@ +CastAura( + buildup_duration: 0.25, + cast_duration: 1.5, + recover_duration: 0.25, + targets: InGroup, + aura: ( + kind: Invulnerability, + strength: 1.0, + duration: Some(0.5), + category: Magical, + ), + range: 25.0, + energy_cost: 400, +) \ No newline at end of file diff --git a/assets/common/abilities/weapon_ability_manifest.ron b/assets/common/abilities/weapon_ability_manifest.ron index 7d13b63a8f..2c9f072a6c 100644 --- a/assets/common/abilities/weapon_ability_manifest.ron +++ b/assets/common/abilities/weapon_ability_manifest.ron @@ -70,9 +70,11 @@ ], ), Sceptre: ( - primary: "common.abilities.sceptre.healingbeam", - secondary: "common.abilities.sceptre.healingbomb", - abilities: [], + primary: "common.abilities.sceptre.lifestealbeam", + secondary: "common.abilities.sceptre.healingbeam", + abilities: [ + (None, "common.abilities.sceptre.wardingaura"), + ], ), Dagger: ( primary: "common.abilities.dagger.tempbasic", diff --git a/common/src/comp/ability.rs b/common/src/comp/ability.rs index 0100612828..c91a0003a5 100644 --- a/common/src/comp/ability.rs +++ b/common/src/comp/ability.rs @@ -1,7 +1,8 @@ use crate::{ assets::{self, Asset}, + combat, comp::{ - inventory::item::tool::ToolKind, projectile::ProjectileConstructor, skills, Body, + aura, inventory::item::tool::ToolKind, projectile::ProjectileConstructor, skills, Body, CharacterState, EnergySource, Gravity, LightEmitter, StateUpdate, }, states::{ @@ -227,6 +228,15 @@ pub enum CharacterAbility { energy_drain: f32, orientation_behavior: basic_beam::MovementBehavior, }, + CastAura { + buildup_duration: f32, + cast_duration: f32, + recover_duration: f32, + targets: combat::GroupTarget, + aura: aura::AuraBuffConstructor, + range: f32, + energy_cost: f32, + }, } impl Default for CharacterAbility { @@ -264,34 +274,14 @@ impl CharacterAbility { .try_change_by(-(*energy_cost as i32), EnergySource::Ability) .is_ok() }, - CharacterAbility::DashMelee { energy_cost, .. } => update - .energy - .try_change_by(-(*energy_cost as i32), EnergySource::Ability) - .is_ok(), - CharacterAbility::BasicMelee { energy_cost, .. } => update - .energy - .try_change_by(-(*energy_cost as i32), EnergySource::Ability) - .is_ok(), - CharacterAbility::BasicRanged { energy_cost, .. } => update - .energy - .try_change_by(-(*energy_cost as i32), EnergySource::Ability) - .is_ok(), - CharacterAbility::LeapMelee { energy_cost, .. } => { - update.vel.0.z >= 0.0 - && update - .energy - .try_change_by(-(*energy_cost as i32), EnergySource::Ability) - .is_ok() - }, - CharacterAbility::SpinMelee { energy_cost, .. } => update - .energy - .try_change_by(-(*energy_cost as i32), EnergySource::Ability) - .is_ok(), - CharacterAbility::ChargedRanged { energy_cost, .. } => update - .energy - .try_change_by(-(*energy_cost as i32), EnergySource::Ability) - .is_ok(), - CharacterAbility::ChargedMelee { energy_cost, .. } => update + CharacterAbility::DashMelee { energy_cost, .. } + | CharacterAbility::BasicMelee { energy_cost, .. } + | CharacterAbility::BasicRanged { energy_cost, .. } + | CharacterAbility::SpinMelee { energy_cost, .. } + | CharacterAbility::ChargedRanged { energy_cost, .. } + | CharacterAbility::ChargedMelee { energy_cost, .. } + | CharacterAbility::Shockwave { energy_cost, .. } + | CharacterAbility::CastAura { energy_cost, .. } => update .energy .try_change_by(-(*energy_cost as i32), EnergySource::Ability) .is_ok(), @@ -304,10 +294,13 @@ impl CharacterAbility { .try_change_by(-(*energy_cost as i32), EnergySource::Ability) .is_ok() }, - CharacterAbility::Shockwave { energy_cost, .. } => update - .energy - .try_change_by(-(*energy_cost as i32), EnergySource::Ability) - .is_ok(), + CharacterAbility::LeapMelee { energy_cost, .. } => { + update.vel.0.z >= 0.0 + && update + .energy + .try_change_by(-(*energy_cost as i32), EnergySource::Ability) + .is_ok() + }, _ => true, } } @@ -500,6 +493,17 @@ impl CharacterAbility { *base_dps *= power * speed; *tick_rate *= speed; }, + CastAura { + ref mut buildup_duration, + ref mut recover_duration, + // cast_duration explicitly not affected by speed + ref mut aura, + .. + } => { + *buildup_duration /= speed; + *recover_duration /= speed; + aura.strength *= power; + }, } self } @@ -517,7 +521,8 @@ impl CharacterAbility { | ChargedMelee { energy_cost, .. } | ChargedRanged { energy_cost, .. } | Shockwave { energy_cost, .. } - | BasicBeam { energy_cost, .. } => *energy_cost as u32, + | BasicBeam { energy_cost, .. } + | CastAura { energy_cost, .. } => *energy_cost as u32, BasicBlock | Boost { .. } | ComboMelee { .. } => 0, } } @@ -1467,6 +1472,27 @@ impl From<(&CharacterAbility, AbilityInfo)> for CharacterState { stage_section: StageSection::Buildup, offset: Vec3::zero(), }), + CharacterAbility::CastAura { + buildup_duration, + cast_duration, + recover_duration, + targets, + aura, + range, + energy_cost: _, + } => CharacterState::CastAura(cast_aura::Data { + static_data: cast_aura::StaticData { + buildup_duration: Duration::from_secs_f32(*buildup_duration), + cast_duration: Duration::from_secs_f32(*cast_duration), + recover_duration: Duration::from_secs_f32(*recover_duration), + targets: *targets, + aura: *aura, + range: *range, + ability_info, + }, + timer: Duration::default(), + stage_section: StageSection::Buildup, + }), } } } diff --git a/common/src/comp/aura.rs b/common/src/comp/aura.rs index eb82775cdb..e172878d4c 100644 --- a/common/src/comp/aura.rs +++ b/common/src/comp/aura.rs @@ -1,4 +1,5 @@ use crate::{ + combat::GroupTarget, comp::buff::{BuffCategory, BuffData, BuffKind, BuffSource}, uid::Uid, }; @@ -62,11 +63,24 @@ pub enum AuraTarget { /// Targets the group of the entity specified by the `Uid`. This is useful /// for auras which should only affect a player's party. GroupOf(Uid), - + /// Targets everyone not in the group of the entity specified by the `Uid`. + /// This is useful for auras which should only affect a player's + /// enemies. + NotGroupOf(Uid), /// Targets all entities. This is for auras which are global or neutral. All, } +impl From<(Option, Option<&Uid>)> for AuraTarget { + fn from((target, uid): (Option, Option<&Uid>)) -> Self { + match (target, uid) { + (Some(GroupTarget::InGroup), Some(uid)) => Self::GroupOf(*uid), + (Some(GroupTarget::OutOfGroup), Some(uid)) => Self::NotGroupOf(*uid), + _ => Self::All, + } + } +} + impl Aura { /// Creates a new Aura to be assigned to an entity pub fn new( @@ -104,6 +118,35 @@ impl Auras { pub fn remove(&mut self, key: AuraKey) { self.auras.remove(key); } } +#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq)] +pub struct AuraBuffConstructor { + pub kind: BuffKind, + pub strength: f32, + pub duration: Option, + pub category: BuffCategory, +} + +impl AuraBuffConstructor { + pub fn to_aura( + self, + uid: &Uid, + radius: f32, + duration: Option, + target: AuraTarget, + ) -> Aura { + let aura_kind = AuraKind::Buff { + kind: self.kind, + data: BuffData { + strength: self.strength, + duration: self.duration.map(Duration::from_secs_f32), + }, + category: self.category, + source: BuffSource::Character { by: *uid }, + }; + Aura::new(aura_kind, radius, duration, target) + } +} + impl Component for Auras { type Storage = DerefFlaggedStorage>; } diff --git a/common/src/comp/character_state.rs b/common/src/comp/character_state.rs index 74e39f2bbf..27f21f5fb7 100644 --- a/common/src/comp/character_state.rs +++ b/common/src/comp/character_state.rs @@ -81,6 +81,8 @@ pub enum CharacterState { /// A continuous attack that affects all creatures in a cone originating /// from the source BasicBeam(basic_beam::Data), + /// Creates an aura that persists as long as you are actively casting + CastAura(cast_aura::Data), } impl CharacterState { @@ -100,6 +102,7 @@ impl CharacterState { | CharacterState::RepeaterRanged(_) | CharacterState::Shockwave(_) | CharacterState::BasicBeam(_) + | CharacterState::CastAura(_) ) } @@ -121,6 +124,7 @@ impl CharacterState { | CharacterState::RepeaterRanged(_) | CharacterState::Shockwave(_) | CharacterState::BasicBeam(_) + | CharacterState::CastAura(_) ) } diff --git a/common/src/states/cast_aura.rs b/common/src/states/cast_aura.rs new file mode 100644 index 0000000000..4582698e94 --- /dev/null +++ b/common/src/states/cast_aura.rs @@ -0,0 +1,135 @@ +use crate::{ + combat::GroupTarget, + comp::{ + aura::{AuraBuffConstructor, AuraChange, AuraTarget}, + 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 until state should create the aura + pub buildup_duration: Duration, + /// How long the state is creating an aura + pub cast_duration: Duration, + /// How long the state has until exiting + pub recover_duration: Duration, + /// Determines how the aura selects its targets + pub targets: GroupTarget, + /// Has information used to construct the aura + pub aura: AuraBuffConstructor, + /// Radius of aura + pub range: f32, + /// What key is used to press 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); + + handle_move(data, &mut update, 0.6); + handle_jump(data, &mut update); + if !ability_key_is_pressed(data, self.static_data.ability_info.key) { + handle_interrupt(data, &mut update, false); + match update.character { + CharacterState::CastAura(_) => {}, + _ => { + return update; + }, + } + } + + match self.stage_section { + StageSection::Buildup => { + if self.timer < self.static_data.buildup_duration { + // Build up + update.character = CharacterState::CastAura(Data { + timer: self + .timer + .checked_add(Duration::from_secs_f32(data.dt.0)) + .unwrap_or_default(), + ..*self + }); + } else { + // Creates aura + let targets = + AuraTarget::from((Some(self.static_data.targets), Some(data.uid))); + let aura = self.static_data.aura.to_aura( + data.uid, + self.static_data.range, + Some(self.static_data.cast_duration), + targets, + ); + update.server_events.push_front(ServerEvent::Aura { + entity: data.entity, + aura_change: AuraChange::Add(aura), + }); + // Build up + update.character = CharacterState::CastAura(Data { + timer: Duration::default(), + stage_section: StageSection::Cast, + ..*self + }); + } + }, + StageSection::Cast => { + if self.timer < self.static_data.cast_duration { + // Cast + update.character = CharacterState::CastAura(Data { + timer: self + .timer + .checked_add(Duration::from_secs_f32(data.dt.0)) + .unwrap_or_default(), + ..*self + }); + } else { + update.character = CharacterState::CastAura(Data { + timer: Duration::default(), + stage_section: StageSection::Recover, + ..*self + }); + } + }, + StageSection::Recover => { + if self.timer < self.static_data.recover_duration { + update.character = CharacterState::CastAura(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/mod.rs b/common/src/states/mod.rs index bd505fbbe0..60bdb2a477 100644 --- a/common/src/states/mod.rs +++ b/common/src/states/mod.rs @@ -4,6 +4,7 @@ pub mod basic_melee; pub mod basic_ranged; pub mod behavior; pub mod boost; +pub mod cast_aura; pub mod charged_melee; pub mod charged_ranged; pub mod climb; diff --git a/common/sys/src/aura.rs b/common/sys/src/aura.rs index bf6c23875d..95074e9ea9 100644 --- a/common/sys/src/aura.rs +++ b/common/sys/src/aura.rs @@ -7,7 +7,7 @@ use common::{ }, event::{EventBus, ServerEvent}, resources::DeltaTime, - uid::UidAllocator, + uid::{Uid, UidAllocator}, }; use common_ecs::{Job, Origin, Phase, System}; use specs::{ @@ -26,6 +26,7 @@ pub struct ReadData<'a> { char_states: ReadStorage<'a, CharacterState>, healths: ReadStorage<'a, Health>, groups: ReadStorage<'a, Group>, + uids: ReadStorage<'a, Uid>, } #[derive(Default)] @@ -79,11 +80,12 @@ impl<'a> System<'a> for Sys { expired_auras.push(key); } } - for (target, target_pos, mut target_buffs, health) in ( + for (target, target_pos, mut target_buffs, health, target_uid) in ( &read_data.entities, &read_data.positions, &mut buffs, &read_data.healths, + &read_data.uids, ) .join() { @@ -96,7 +98,8 @@ impl<'a> System<'a> for Sys { .and_then(|e| read_data.groups.get(e)) .map_or(false, |owner_group| { Some(owner_group) == read_data.groups.get(target) - }); + }) + || *target_uid == uid; if !same_group { continue; diff --git a/common/sys/src/character_behavior.rs b/common/sys/src/character_behavior.rs index 1dcc7f6b9a..fe00e76512 100644 --- a/common/sys/src/character_behavior.rs +++ b/common/sys/src/character_behavior.rs @@ -303,6 +303,7 @@ impl<'a> System<'a> for Sys { CharacterState::RepeaterRanged(data) => data.handle_event(&j, action), CharacterState::Shockwave(data) => data.handle_event(&j, action), CharacterState::BasicBeam(data) => data.handle_event(&j, action), + CharacterState::CastAura(data) => data.handle_event(&j, action), }; local_emitter.append(&mut state_update.local_events); server_emitter.append(&mut state_update.server_events); @@ -342,6 +343,7 @@ impl<'a> System<'a> for Sys { CharacterState::RepeaterRanged(data) => data.behavior(&j), CharacterState::Shockwave(data) => data.behavior(&j), CharacterState::BasicBeam(data) => data.behavior(&j), + CharacterState::CastAura(data) => data.behavior(&j), }; local_emitter.append(&mut state_update.local_events); diff --git a/common/sys/src/stats.rs b/common/sys/src/stats.rs index bc7fbe02a8..638c15a115 100644 --- a/common/sys/src/stats.rs +++ b/common/sys/src/stats.rs @@ -232,7 +232,8 @@ impl<'a> System<'a> for Sys { | CharacterState::ChargedRanged { .. } | CharacterState::RepeaterRanged { .. } | CharacterState::Shockwave { .. } - | CharacterState::BasicBeam { .. } => { + | CharacterState::BasicBeam { .. } + | CharacterState::CastAura { .. } => { if energy.get_unchecked().regen_rate != 0.0 { energy.get_mut_unchecked().regen_rate = 0.0 } diff --git a/server/src/state_ext.rs b/server/src/state_ext.rs index 6f2c5fc852..379fb920e5 100644 --- a/server/src/state_ext.rs +++ b/server/src/state_ext.rs @@ -184,6 +184,7 @@ impl StateExt for State { .with(inventory) .with(comp::Buffs::default()) .with(comp::Combo::default()) + .with(comp::Auras::default()) } fn create_object(&mut self, pos: comp::Pos, object: comp::object::Body) -> EcsEntityBuilder { @@ -275,6 +276,7 @@ impl StateExt for State { comp::Alignment::Owned(self.read_component_copied(entity).unwrap()), ); self.write_component(entity, comp::Buffs::default()); + self.write_component(entity, comp::Auras::default()); self.write_component(entity, comp::Combo::default()); // Make sure physics components are updated From 43874a4aa5f1b3e976c534c664784ea7bd876f22 Mon Sep 17 00:00:00 2001 From: Sam Date: Tue, 2 Mar 2021 14:26:45 -0500 Subject: [PATCH 02/18] Added healing beam character state. This was done as there was a lot of special casing in basic beam to account for healing. --- .../common/abilities/sceptre/healingbeam.ron | 8 +- .../abilities/sceptre/lifestealbeam.ron | 4 +- .../common/abilities/staff/flamethrower.ron | 6 +- .../abilities/staffsimple/flamethrower.ron | 4 +- .../unique/quadlowbeam/healingbeam.ron | 6 +- .../unique/quadlowbreathe/flamethrower.ron | 4 +- .../abilities/unique/turret/flamethrower.ron | 4 +- common/src/comp/ability.rs | 86 ++++++--- common/src/comp/character_state.rs | 5 + common/src/states/basic_beam.rs | 38 +--- common/src/states/healing_beam.rs | 172 ++++++++++++++++++ common/src/states/mod.rs | 1 + common/sys/src/character_behavior.rs | 2 + common/sys/src/stats.rs | 3 +- 14 files changed, 264 insertions(+), 79 deletions(-) create mode 100644 common/src/states/healing_beam.rs diff --git a/assets/common/abilities/sceptre/healingbeam.ron b/assets/common/abilities/sceptre/healingbeam.ron index 90a23b9b8d..44f321e1c4 100644 --- a/assets/common/abilities/sceptre/healingbeam.ron +++ b/assets/common/abilities/sceptre/healingbeam.ron @@ -1,15 +1,11 @@ -BasicBeam( +HealingBeam( buildup_duration: 0.25, recover_duration: 0.25, beam_duration: 1.0, - base_hps: 80, - base_dps: 0, + heal: 80, tick_rate: 2.0, range: 25.0, max_angle: 1.0, lifesteal_eff: 0.0, - energy_regen: 0, energy_cost: 50, - energy_drain: 0, - orientation_behavior: Normal, ) \ No newline at end of file diff --git a/assets/common/abilities/sceptre/lifestealbeam.ron b/assets/common/abilities/sceptre/lifestealbeam.ron index 5f403d85d9..e010e5a77e 100644 --- a/assets/common/abilities/sceptre/lifestealbeam.ron +++ b/assets/common/abilities/sceptre/lifestealbeam.ron @@ -2,14 +2,12 @@ BasicBeam( buildup_duration: 0.25, recover_duration: 0.25, beam_duration: 1.0, - base_hps: 0, - base_dps: 80, + damage: 80, tick_rate: 2.0, range: 25.0, max_angle: 1.0, lifesteal_eff: 0.15, energy_regen: 25, - energy_cost: 0, energy_drain: 0, orientation_behavior: Normal, ) \ No newline at end of file diff --git a/assets/common/abilities/staff/flamethrower.ron b/assets/common/abilities/staff/flamethrower.ron index 7bb592a9fb..3a0849658e 100644 --- a/assets/common/abilities/staff/flamethrower.ron +++ b/assets/common/abilities/staff/flamethrower.ron @@ -2,14 +2,12 @@ BasicBeam( buildup_duration: 0.25, recover_duration: 0.25, beam_duration: 1.0, - base_hps: 0, - base_dps: 150, + damage: 150, tick_rate: 3.0, range: 20.0, max_angle: 10.0, lifesteal_eff: 0.0, energy_regen: 0, - energy_cost: 1, energy_drain: 350, orientation_behavior: Normal, -) +) \ No newline at end of file diff --git a/assets/common/abilities/staffsimple/flamethrower.ron b/assets/common/abilities/staffsimple/flamethrower.ron index 905372944f..8932d5c031 100644 --- a/assets/common/abilities/staffsimple/flamethrower.ron +++ b/assets/common/abilities/staffsimple/flamethrower.ron @@ -2,14 +2,12 @@ BasicBeam( buildup_duration: 0.5, recover_duration: 0.5, beam_duration: 1.0, - base_hps: 0, - base_dps: 150, + damage: 150, tick_rate: 3.0, range: 20.0, max_angle: 0.1, lifesteal_eff: 0.0, energy_regen: 0, - energy_cost: 1, energy_drain: 350, orientation_behavior: Normal, ) diff --git a/assets/common/abilities/unique/quadlowbeam/healingbeam.ron b/assets/common/abilities/unique/quadlowbeam/healingbeam.ron index 8b6b235b6f..0c1b67dd20 100644 --- a/assets/common/abilities/unique/quadlowbeam/healingbeam.ron +++ b/assets/common/abilities/unique/quadlowbeam/healingbeam.ron @@ -2,14 +2,14 @@ BasicBeam( buildup_duration: 0.25, recover_duration: 0.25, beam_duration: 1.0, - base_hps: 60, - base_dps: 60, + damage: 60, + //base_hps: 60, Don't merge until this comment is removed tick_rate: 2.0, range: 25.0, max_angle: 1.0, lifesteal_eff: 0.15, energy_regen: 25, - energy_cost: 50, + //energy_cost: 50, energy_drain: 0, orientation_behavior: Normal, ) \ No newline at end of file diff --git a/assets/common/abilities/unique/quadlowbreathe/flamethrower.ron b/assets/common/abilities/unique/quadlowbreathe/flamethrower.ron index 3b8117c98b..8a9be0de54 100644 --- a/assets/common/abilities/unique/quadlowbreathe/flamethrower.ron +++ b/assets/common/abilities/unique/quadlowbreathe/flamethrower.ron @@ -2,14 +2,12 @@ BasicBeam( buildup_duration: 0.4, recover_duration: 0.25, beam_duration: 0.5, - base_hps: 0, - base_dps: 150, + damage: 150, tick_rate: 3.0, range: 15.0, max_angle: 22.5, lifesteal_eff: 0.0, energy_regen: 0, - energy_cost: 0, energy_drain: 0, orientation_behavior: Normal, ) \ No newline at end of file diff --git a/assets/common/abilities/unique/turret/flamethrower.ron b/assets/common/abilities/unique/turret/flamethrower.ron index 512104ffdd..e734cb12be 100644 --- a/assets/common/abilities/unique/turret/flamethrower.ron +++ b/assets/common/abilities/unique/turret/flamethrower.ron @@ -2,14 +2,12 @@ BasicBeam( buildup_duration: 0.25, recover_duration: 0.25, beam_duration: 0.5, - base_hps: 0, - base_dps: 9001, + damage: 9001, tick_rate: 3.0, range: 30.0, max_angle: 1.0, lifesteal_eff: 0.0, energy_regen: 0, - energy_cost: 0, energy_drain: 0, orientation_behavior: Turret, ) \ No newline at end of file diff --git a/common/src/comp/ability.rs b/common/src/comp/ability.rs index c91a0003a5..6b2bf66ee7 100644 --- a/common/src/comp/ability.rs +++ b/common/src/comp/ability.rs @@ -14,7 +14,6 @@ use crate::{ }; use serde::{Deserialize, Serialize}; use std::time::Duration; -use vek::Vec3; #[derive(Copy, Clone, Hash, Eq, PartialEq, Debug, Serialize, Deserialize)] pub enum CharacterAbilityType { @@ -217,14 +216,12 @@ pub enum CharacterAbility { buildup_duration: f32, recover_duration: f32, beam_duration: f32, - base_hps: f32, - base_dps: f32, + damage: f32, tick_rate: f32, range: f32, max_angle: f32, lifesteal_eff: f32, energy_regen: f32, - energy_cost: f32, energy_drain: f32, orientation_behavior: basic_beam::MovementBehavior, }, @@ -237,6 +234,16 @@ pub enum CharacterAbility { range: f32, energy_cost: f32, }, + HealingBeam { + buildup_duration: f32, + recover_duration: f32, + beam_duration: f32, + heal: f32, + tick_rate: f32, + range: f32, + max_angle: f32, + energy_cost: f32, + }, } impl Default for CharacterAbility { @@ -481,16 +488,13 @@ impl CharacterAbility { BasicBeam { ref mut buildup_duration, ref mut recover_duration, - ref mut base_hps, - ref mut base_dps, + ref mut damage, ref mut tick_rate, .. } => { *buildup_duration /= speed; *recover_duration /= speed; - // hps and dps adjusted by speed as they are normalized by tick rate already - *base_hps *= power * speed; - *base_dps *= power * speed; + *damage *= power; *tick_rate *= speed; }, CastAura { @@ -504,6 +508,18 @@ impl CharacterAbility { *recover_duration /= speed; aura.strength *= power; }, + HealingBeam { + ref mut buildup_duration, + ref mut recover_duration, + ref mut heal, + ref mut tick_rate, + .. + } => { + *buildup_duration /= speed; + *recover_duration /= speed; + *heal *= power; + *tick_rate *= speed; + }, } self } @@ -521,8 +537,15 @@ impl CharacterAbility { | ChargedMelee { energy_cost, .. } | ChargedRanged { energy_cost, .. } | Shockwave { energy_cost, .. } - | BasicBeam { energy_cost, .. } + | HealingBeam { energy_cost, .. } | CastAura { energy_cost, .. } => *energy_cost as u32, + BasicBeam { energy_drain, .. } => { + if *energy_drain > f32::EPSILON { + 1 + } else { + 0 + } + }, BasicBlock | Boost { .. } | ComboMelee { .. } => 0, } } @@ -922,14 +945,14 @@ impl CharacterAbility { *projectile = projectile.modified_projectile(power, regen, range, 1_f32); }, BasicBeam { - ref mut base_dps, + ref mut damage, ref mut range, ref mut energy_drain, ref mut beam_duration, .. } => { if let Ok(Some(level)) = skillset.skill_level(Staff(FDamage)) { - *base_dps *= 1.3_f32.powi(level.into()); + *damage *= 1.3_f32.powi(level.into()); } if let Ok(Some(level)) = skillset.skill_level(Staff(FRange)) { let range_mod = 1.25_f32.powi(level.into()); @@ -970,8 +993,8 @@ impl CharacterAbility { } }, Some(ToolKind::Sceptre) => { - use skills::SceptreSkill::*; - match self { + //use skills::SceptreSkill::*; + /*match self { BasicBeam { ref mut base_hps, ref mut base_dps, @@ -1036,7 +1059,7 @@ impl CharacterAbility { } }, _ => {}, - } + }*/ }, None => { use skills::RollSkill::*; @@ -1441,14 +1464,12 @@ impl From<(&CharacterAbility, AbilityInfo)> for CharacterState { buildup_duration, recover_duration, beam_duration, - base_hps, - base_dps, + damage, tick_rate, range, max_angle, lifesteal_eff, energy_regen, - energy_cost, energy_drain, orientation_behavior, } => CharacterState::BasicBeam(basic_beam::Data { @@ -1456,21 +1477,18 @@ impl From<(&CharacterAbility, AbilityInfo)> for CharacterState { buildup_duration: Duration::from_secs_f32(*buildup_duration), recover_duration: Duration::from_secs_f32(*recover_duration), beam_duration: Duration::from_secs_f32(*beam_duration), - base_hps: *base_hps, - base_dps: *base_dps, + damage: *damage, tick_rate: *tick_rate, range: *range, max_angle: *max_angle, lifesteal_eff: *lifesteal_eff, energy_regen: *energy_regen, - energy_cost: *energy_cost, energy_drain: *energy_drain, ability_info, orientation_behavior: *orientation_behavior, }, timer: Duration::default(), stage_section: StageSection::Buildup, - offset: Vec3::zero(), }), CharacterAbility::CastAura { buildup_duration, @@ -1493,6 +1511,30 @@ impl From<(&CharacterAbility, AbilityInfo)> for CharacterState { timer: Duration::default(), stage_section: StageSection::Buildup, }), + CharacterAbility::HealingBeam { + buildup_duration, + recover_duration, + beam_duration, + heal, + tick_rate, + range, + max_angle, + energy_cost, + } => CharacterState::HealingBeam(healing_beam::Data { + static_data: healing_beam::StaticData { + buildup_duration: Duration::from_secs_f32(*buildup_duration), + recover_duration: Duration::from_secs_f32(*recover_duration), + beam_duration: Duration::from_secs_f32(*beam_duration), + heal: *heal, + tick_rate: *tick_rate, + range: *range, + max_angle: *max_angle, + energy_cost: *energy_cost, + ability_info, + }, + timer: Duration::default(), + stage_section: StageSection::Buildup, + }), } } } diff --git a/common/src/comp/character_state.rs b/common/src/comp/character_state.rs index 27f21f5fb7..fe439922c6 100644 --- a/common/src/comp/character_state.rs +++ b/common/src/comp/character_state.rs @@ -83,6 +83,8 @@ pub enum CharacterState { BasicBeam(basic_beam::Data), /// Creates an aura that persists as long as you are actively casting CastAura(cast_aura::Data), + /// A directed beam that heals targets in range + HealingBeam(healing_beam::Data), } impl CharacterState { @@ -103,6 +105,7 @@ impl CharacterState { | CharacterState::Shockwave(_) | CharacterState::BasicBeam(_) | CharacterState::CastAura(_) + | CharacterState::HealingBeam(_) ) } @@ -125,6 +128,7 @@ impl CharacterState { | CharacterState::Shockwave(_) | CharacterState::BasicBeam(_) | CharacterState::CastAura(_) + | CharacterState::HealingBeam(_) ) } @@ -145,6 +149,7 @@ impl CharacterState { | CharacterState::Stunned(_) | CharacterState::Wielding | CharacterState::Talk + | CharacterState::HealingBeam(_) ) } diff --git a/common/src/states/basic_beam.rs b/common/src/states/basic_beam.rs index 6479c730bb..c04fe495cf 100644 --- a/common/src/states/basic_beam.rs +++ b/common/src/states/basic_beam.rs @@ -24,11 +24,9 @@ pub struct StaticData { pub recover_duration: Duration, /// How long each beam segment persists for pub beam_duration: Duration, - /// Base healing per second - pub base_hps: f32, - /// Base damage per second - pub base_dps: f32, - /// Ticks of damage/healing per second + /// Base damage per tick + pub damage: f32, + /// Ticks per second pub tick_rate: f32, /// Max range pub range: f32, @@ -37,11 +35,9 @@ pub struct StaticData { /// Lifesteal efficiency (0 gives 0% conversion of damage to health, 1 gives /// 100% conversion of damage to health) pub lifesteal_eff: f32, - /// Energy regened per second for damage ticks + /// Energy regenerated per tick pub energy_regen: f32, - /// Energy consumed per second for heal ticks - pub energy_cost: f32, - /// Energy drained per + /// Energy drained per second pub energy_drain: f32, /// Used to dictate how orientation functions in this state pub orientation_behavior: MovementBehavior, @@ -58,8 +54,6 @@ pub struct Data { pub timer: Duration, /// What section the character stage is in pub stage_section: StageSection, - /// Used to offset beam and particles - pub offset: Vec3, } impl CharacterBehavior for Data { @@ -103,17 +97,10 @@ impl CharacterBehavior for Data { tick_dur: Duration::from_secs_f32(1.0 / self.static_data.tick_rate), timer: Duration::default(), }); - // Gets offsets - let body_offsets = Vec3::new( - (data.body.radius() + 1.0) * data.inputs.look_dir.x, - (data.body.radius() + 1.0) * data.inputs.look_dir.y, - data.body.eye_height(), - ) * 0.55; // Build up update.character = CharacterState::BasicBeam(Data { timer: Duration::default(), stage_section: StageSection::Cast, - offset: body_offsets, ..*self }); } @@ -135,27 +122,17 @@ impl CharacterBehavior for Data { let damage = AttackDamage::new( Damage { source: DamageSource::Energy, - value: self.static_data.base_dps as f32 / self.static_data.tick_rate, + value: self.static_data.damage, }, Some(GroupTarget::OutOfGroup), ) .with_effect(lifesteal); - let heal = AttackEffect::new( - Some(GroupTarget::InGroup), - CombatEffect::Heal( - self.static_data.base_hps as f32 / self.static_data.tick_rate, - ), - ) - .with_requirement(CombatRequirement::SufficientEnergy( - self.static_data.energy_cost, - )); let (crit_chance, crit_mult) = get_crit_data(data, self.static_data.ability_info); let attack = Attack::default() .with_damage(damage) .with_crit(crit_chance, crit_mult) - .with_effect(energy) - .with_effect(heal); + .with_effect(energy); let properties = beam::Properties { attack, @@ -189,7 +166,6 @@ impl CharacterBehavior for Data { .timer .checked_add(Duration::from_secs_f32(data.dt.0)) .unwrap_or_default(), - offset: body_offsets, ..*self }); diff --git a/common/src/states/healing_beam.rs b/common/src/states/healing_beam.rs new file mode 100644 index 0000000000..24ec6c3322 --- /dev/null +++ b/common/src/states/healing_beam.rs @@ -0,0 +1,172 @@ +use crate::{ + combat::{Attack, AttackEffect, CombatEffect, CombatRequirement, GroupTarget}, + comp::{beam, Body, CharacterState, Ori, Pos, StateUpdate}, + event::ServerEvent, + states::{ + behavior::{CharacterBehavior, JoinData}, + utils::*, + }, + uid::Uid, +}; +use serde::{Deserialize, Serialize}; +use std::time::Duration; +use vek::*; + +/// Separated out to condense update portions of character state +#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct StaticData { + /// How long until state should deal damage or heal + pub buildup_duration: Duration, + /// How long the state has until exiting + pub recover_duration: Duration, + /// How long each beam segment persists for + pub beam_duration: Duration, + /// Base healing per tick + pub heal: f32, + /// Ticks of healing per second + pub tick_rate: f32, + /// Max range + pub range: f32, + /// Max angle (45.0 will give you a 90.0 angle window) + pub max_angle: f32, + /// Energy consumed per second for heal ticks + pub energy_cost: f32, + /// What key is used to press 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); + + handle_move(data, &mut update, 0.4); + handle_jump(data, &mut update); + if !ability_key_is_pressed(data, self.static_data.ability_info.key) { + handle_interrupt(data, &mut update, false); + match update.character { + CharacterState::HealingBeam(_) => {}, + _ => { + return update; + }, + } + } + + match self.stage_section { + StageSection::Buildup => { + if self.timer < self.static_data.buildup_duration { + // Build up + update.character = CharacterState::HealingBeam(Data { + timer: self + .timer + .checked_add(Duration::from_secs_f32(data.dt.0)) + .unwrap_or_default(), + ..*self + }); + } 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(), + }); + // Build up + update.character = CharacterState::HealingBeam(Data { + timer: Duration::default(), + stage_section: StageSection::Cast, + ..*self + }); + } + }, + StageSection::Cast => { + if ability_key_is_pressed(data, self.static_data.ability_info.key) { + let speed = + self.static_data.range / self.static_data.beam_duration.as_secs_f32(); + let heal = AttackEffect::new( + Some(GroupTarget::InGroup), + CombatEffect::Heal(self.static_data.heal), + ) + .with_requirement(CombatRequirement::SufficientEnergy( + self.static_data.energy_cost, + )); + let attack = Attack::default().with_effect(heal); + + let properties = beam::Properties { + attack, + angle: self.static_data.max_angle.to_radians(), + speed, + duration: self.static_data.beam_duration, + owner: Some(*data.uid), + }; + // Gets offsets + let body_offsets = match data.body { + Body::Humanoid(_) => Vec3::new( + (data.body.radius() + 2.0) * data.inputs.look_dir.x, + (data.body.radius() + 2.0) * data.inputs.look_dir.y, + data.body.eye_height() * 0.55, + ), + _ => Vec3::new( + (data.body.radius() + 3.0) * data.inputs.look_dir.x, + (data.body.radius() + 3.0) * data.inputs.look_dir.y, + data.body.eye_height() * 0.55, + ), + }; + let pos = Pos(data.pos.0 + body_offsets); + // Create beam segment + update.server_events.push_front(ServerEvent::BeamSegment { + properties, + pos, + ori: Ori::from(data.inputs.look_dir), + }); + update.character = CharacterState::HealingBeam(Data { + timer: self + .timer + .checked_add(Duration::from_secs_f32(data.dt.0)) + .unwrap_or_default(), + ..*self + }); + } else { + update.character = CharacterState::HealingBeam(Data { + timer: Duration::default(), + stage_section: StageSection::Recover, + ..*self + }); + } + }, + StageSection::Recover => { + if self.timer < self.static_data.recover_duration { + update.character = CharacterState::HealingBeam(Data { + timer: self + .timer + .checked_add(Duration::from_secs_f32(data.dt.0)) + .unwrap_or_default(), + ..*self + }); + } 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/mod.rs b/common/src/states/mod.rs index 60bdb2a477..3984eb29b6 100644 --- a/common/src/states/mod.rs +++ b/common/src/states/mod.rs @@ -14,6 +14,7 @@ pub mod dash_melee; pub mod equipping; pub mod glide; pub mod glide_wield; +pub mod healing_beam; pub mod idle; pub mod leap_melee; pub mod repeater_ranged; diff --git a/common/sys/src/character_behavior.rs b/common/sys/src/character_behavior.rs index fe00e76512..ab46d49b07 100644 --- a/common/sys/src/character_behavior.rs +++ b/common/sys/src/character_behavior.rs @@ -304,6 +304,7 @@ impl<'a> System<'a> for Sys { CharacterState::Shockwave(data) => data.handle_event(&j, action), CharacterState::BasicBeam(data) => data.handle_event(&j, action), CharacterState::CastAura(data) => data.handle_event(&j, action), + CharacterState::HealingBeam(data) => data.handle_event(&j, action), }; local_emitter.append(&mut state_update.local_events); server_emitter.append(&mut state_update.server_events); @@ -344,6 +345,7 @@ impl<'a> System<'a> for Sys { CharacterState::Shockwave(data) => data.behavior(&j), CharacterState::BasicBeam(data) => data.behavior(&j), CharacterState::CastAura(data) => data.behavior(&j), + CharacterState::HealingBeam(data) => data.behavior(&j), }; local_emitter.append(&mut state_update.local_events); diff --git a/common/sys/src/stats.rs b/common/sys/src/stats.rs index 638c15a115..5c09d199a7 100644 --- a/common/sys/src/stats.rs +++ b/common/sys/src/stats.rs @@ -233,7 +233,8 @@ impl<'a> System<'a> for Sys { | CharacterState::RepeaterRanged { .. } | CharacterState::Shockwave { .. } | CharacterState::BasicBeam { .. } - | CharacterState::CastAura { .. } => { + | CharacterState::CastAura { .. } + | CharacterState::HealingBeam { .. } => { if energy.get_unchecked().regen_rate != 0.0 { energy.get_mut_unchecked().regen_rate = 0.0 } From 18edc1092bd68955e34b7fd6161e0a186975ffb5 Mon Sep 17 00:00:00 2001 From: Sam Date: Tue, 2 Mar 2021 17:19:38 -0500 Subject: [PATCH 03/18] Basic beam now generates combo. Healing beam now consumes combo, and requires combo to heal. --- .../common/abilities/sceptre/healingbeam.ron | 2 +- .../abilities/sceptre/lifestealbeam.ron | 2 +- .../common/abilities/staff/flamethrower.ron | 2 +- .../abilities/staffsimple/flamethrower.ron | 2 +- .../unique/quadlowbeam/healingbeam.ron | 2 +- .../unique/quadlowbreathe/flamethrower.ron | 2 +- .../abilities/unique/turret/flamethrower.ron | 2 +- common/src/combat.rs | 30 ++++++++++++++++--- common/src/states/basic_beam.rs | 3 +- common/src/states/healing_beam.rs | 5 ++-- common/sys/src/beam.rs | 6 ++-- common/sys/src/melee.rs | 7 ++++- common/sys/src/projectile.rs | 6 ++-- common/sys/src/shockwave.rs | 6 ++-- server/src/events/entity_manipulation.rs | 2 ++ 15 files changed, 57 insertions(+), 22 deletions(-) diff --git a/assets/common/abilities/sceptre/healingbeam.ron b/assets/common/abilities/sceptre/healingbeam.ron index 44f321e1c4..930ad025d4 100644 --- a/assets/common/abilities/sceptre/healingbeam.ron +++ b/assets/common/abilities/sceptre/healingbeam.ron @@ -2,7 +2,7 @@ HealingBeam( buildup_duration: 0.25, recover_duration: 0.25, beam_duration: 1.0, - heal: 80, + heal: 40, tick_rate: 2.0, range: 25.0, max_angle: 1.0, diff --git a/assets/common/abilities/sceptre/lifestealbeam.ron b/assets/common/abilities/sceptre/lifestealbeam.ron index e010e5a77e..7fcdc5932f 100644 --- a/assets/common/abilities/sceptre/lifestealbeam.ron +++ b/assets/common/abilities/sceptre/lifestealbeam.ron @@ -2,7 +2,7 @@ BasicBeam( buildup_duration: 0.25, recover_duration: 0.25, beam_duration: 1.0, - damage: 80, + damage: 40, tick_rate: 2.0, range: 25.0, max_angle: 1.0, diff --git a/assets/common/abilities/staff/flamethrower.ron b/assets/common/abilities/staff/flamethrower.ron index 3a0849658e..968285b222 100644 --- a/assets/common/abilities/staff/flamethrower.ron +++ b/assets/common/abilities/staff/flamethrower.ron @@ -2,7 +2,7 @@ BasicBeam( buildup_duration: 0.25, recover_duration: 0.25, beam_duration: 1.0, - damage: 150, + damage: 50, tick_rate: 3.0, range: 20.0, max_angle: 10.0, diff --git a/assets/common/abilities/staffsimple/flamethrower.ron b/assets/common/abilities/staffsimple/flamethrower.ron index 8932d5c031..e87a65dca1 100644 --- a/assets/common/abilities/staffsimple/flamethrower.ron +++ b/assets/common/abilities/staffsimple/flamethrower.ron @@ -2,7 +2,7 @@ BasicBeam( buildup_duration: 0.5, recover_duration: 0.5, beam_duration: 1.0, - damage: 150, + damage: 50, tick_rate: 3.0, range: 20.0, max_angle: 0.1, diff --git a/assets/common/abilities/unique/quadlowbeam/healingbeam.ron b/assets/common/abilities/unique/quadlowbeam/healingbeam.ron index 0c1b67dd20..089c906bc8 100644 --- a/assets/common/abilities/unique/quadlowbeam/healingbeam.ron +++ b/assets/common/abilities/unique/quadlowbeam/healingbeam.ron @@ -2,7 +2,7 @@ BasicBeam( buildup_duration: 0.25, recover_duration: 0.25, beam_duration: 1.0, - damage: 60, + damage: 30, //base_hps: 60, Don't merge until this comment is removed tick_rate: 2.0, range: 25.0, diff --git a/assets/common/abilities/unique/quadlowbreathe/flamethrower.ron b/assets/common/abilities/unique/quadlowbreathe/flamethrower.ron index 8a9be0de54..2b8313cfea 100644 --- a/assets/common/abilities/unique/quadlowbreathe/flamethrower.ron +++ b/assets/common/abilities/unique/quadlowbreathe/flamethrower.ron @@ -2,7 +2,7 @@ BasicBeam( buildup_duration: 0.4, recover_duration: 0.25, beam_duration: 0.5, - damage: 150, + damage: 50, tick_rate: 3.0, range: 15.0, max_angle: 22.5, diff --git a/assets/common/abilities/unique/turret/flamethrower.ron b/assets/common/abilities/unique/turret/flamethrower.ron index e734cb12be..b06e2cf39e 100644 --- a/assets/common/abilities/unique/turret/flamethrower.ron +++ b/assets/common/abilities/unique/turret/flamethrower.ron @@ -2,7 +2,7 @@ BasicBeam( buildup_duration: 0.25, recover_duration: 0.25, beam_duration: 0.5, - damage: 9001, + damage: 3000, tick_rate: 3.0, range: 30.0, max_angle: 1.0, diff --git a/common/src/combat.rs b/common/src/combat.rs index 474fc06e8c..023b77770f 100644 --- a/common/src/combat.rs +++ b/common/src/combat.rs @@ -13,8 +13,8 @@ use crate::{ }, poise::PoiseChange, skills::{SkillGroupKind, SkillSet}, - Body, Energy, EnergyChange, EnergySource, Health, HealthChange, HealthSource, Inventory, - Stats, + Body, Combo, Energy, EnergyChange, EnergySource, Health, HealthChange, HealthSource, + Inventory, Stats, }, event::ServerEvent, uid::Uid, @@ -45,6 +45,7 @@ pub struct AttackerInfo<'a> { pub entity: EcsEntity, pub uid: Uid, pub energy: Option<&'a Energy>, + pub combo: Option<&'a Combo>, } #[cfg(not(target_arch = "wasm32"))] @@ -229,7 +230,7 @@ impl Attack { { if match &effect.requirement { Some(CombatRequirement::AnyDamage) => accumulated_damage > 0.0, - Some(CombatRequirement::SufficientEnergy(r)) => { + Some(CombatRequirement::Energy(r)) => { if let Some(AttackerInfo { entity, energy: Some(e), @@ -252,6 +253,26 @@ impl Attack { false } }, + Some(CombatRequirement::Combo(r)) => { + if let Some(AttackerInfo { + entity, + combo: Some(c), + .. + }) = attacker + { + let sufficient_combo = c.counter() >= *r; + if sufficient_combo { + emit(ServerEvent::ComboChange { + entity, + change: -(*r as i32), + }); + } + + sufficient_combo + } else { + false + } + }, None => true, } { match effect.effect { @@ -405,7 +426,8 @@ pub enum CombatEffect { #[derive(Clone, Debug, Serialize, Deserialize)] pub enum CombatRequirement { AnyDamage, - SufficientEnergy(f32), + Energy(f32), + Combo(u32), } #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, Serialize, Deserialize)] diff --git a/common/src/states/basic_beam.rs b/common/src/states/basic_beam.rs index c04fe495cf..c39182ef59 100644 --- a/common/src/states/basic_beam.rs +++ b/common/src/states/basic_beam.rs @@ -132,7 +132,8 @@ impl CharacterBehavior for Data { let attack = Attack::default() .with_damage(damage) .with_crit(crit_chance, crit_mult) - .with_effect(energy); + .with_effect(energy) + .with_combo_increment(); let properties = beam::Properties { attack, diff --git a/common/src/states/healing_beam.rs b/common/src/states/healing_beam.rs index 24ec6c3322..94b2163fff 100644 --- a/common/src/states/healing_beam.rs +++ b/common/src/states/healing_beam.rs @@ -96,9 +96,8 @@ impl CharacterBehavior for Data { Some(GroupTarget::InGroup), CombatEffect::Heal(self.static_data.heal), ) - .with_requirement(CombatRequirement::SufficientEnergy( - self.static_data.energy_cost, - )); + .with_requirement(CombatRequirement::Energy(self.static_data.energy_cost)) + .with_requirement(CombatRequirement::Combo(1)); let attack = Attack::default().with_effect(heal); let properties = beam::Properties { diff --git a/common/sys/src/beam.rs b/common/sys/src/beam.rs index ff008a05cd..82766f28ad 100644 --- a/common/sys/src/beam.rs +++ b/common/sys/src/beam.rs @@ -1,8 +1,8 @@ use common::{ combat::{AttackerInfo, TargetInfo}, comp::{ - Beam, BeamSegment, Body, Energy, Group, Health, HealthSource, Inventory, Last, Ori, Pos, - Scale, Stats, + Beam, BeamSegment, Body, Combo, Energy, Group, Health, HealthSource, Inventory, Last, Ori, + Pos, Scale, Stats, }, event::{EventBus, ServerEvent}, resources::{DeltaTime, Time}, @@ -35,6 +35,7 @@ pub struct ReadData<'a> { groups: ReadStorage<'a, Group>, energies: ReadStorage<'a, Energy>, stats: ReadStorage<'a, Stats>, + combos: ReadStorage<'a, Combo>, } /// This system is responsible for handling beams that heal or do damage @@ -162,6 +163,7 @@ impl<'a> System<'a> for Sys { entity, uid, energy: read_data.energies.get(entity), + combo: read_data.combos.get(entity), }); let target_info = TargetInfo { diff --git a/common/sys/src/melee.rs b/common/sys/src/melee.rs index 5890bf5b68..49222486b8 100644 --- a/common/sys/src/melee.rs +++ b/common/sys/src/melee.rs @@ -1,6 +1,9 @@ use common::{ combat::{AttackerInfo, TargetInfo}, - comp::{Body, CharacterState, Energy, Group, Health, Inventory, Melee, Ori, Pos, Scale, Stats}, + comp::{ + Body, CharacterState, Combo, Energy, Group, Health, Inventory, Melee, Ori, Pos, Scale, + Stats, + }, event::{EventBus, ServerEvent}, uid::Uid, util::Dir, @@ -27,6 +30,7 @@ pub struct ReadData<'a> { char_states: ReadStorage<'a, CharacterState>, server_bus: Read<'a, EventBus>, stats: ReadStorage<'a, Stats>, + combos: ReadStorage<'a, Combo>, } /// This system is responsible for handling accepted inputs like moving or @@ -113,6 +117,7 @@ impl<'a> System<'a> for Sys { entity: attacker, uid: *uid, energy: read_data.energies.get(attacker), + combo: read_data.combos.get(attacker), }); let target_info = TargetInfo { diff --git a/common/sys/src/projectile.rs b/common/sys/src/projectile.rs index 0255a884ea..124af72606 100644 --- a/common/sys/src/projectile.rs +++ b/common/sys/src/projectile.rs @@ -1,8 +1,8 @@ use common::{ combat::{AttackerInfo, TargetInfo}, comp::{ - projectile, Energy, Group, HealthSource, Inventory, Ori, PhysicsState, Pos, Projectile, - Stats, Vel, + projectile, Combo, Energy, Group, HealthSource, Inventory, Ori, PhysicsState, Pos, + Projectile, Stats, Vel, }, event::{EventBus, ServerEvent}, resources::DeltaTime, @@ -30,6 +30,7 @@ pub struct ReadData<'a> { groups: ReadStorage<'a, Group>, energies: ReadStorage<'a, Energy>, stats: ReadStorage<'a, Stats>, + combos: ReadStorage<'a, Combo>, } /// This system is responsible for handling projectile effect triggers @@ -110,6 +111,7 @@ impl<'a> System<'a> for Sys { entity, uid, energy: read_data.energies.get(entity), + combo: read_data.combos.get(entity), } }); diff --git a/common/sys/src/shockwave.rs b/common/sys/src/shockwave.rs index d5b1c9846c..e89eee5efe 100644 --- a/common/sys/src/shockwave.rs +++ b/common/sys/src/shockwave.rs @@ -1,8 +1,8 @@ use common::{ combat::{AttackerInfo, TargetInfo}, comp::{ - Body, Energy, Group, Health, HealthSource, Inventory, Last, Ori, PhysicsState, Pos, Scale, - Shockwave, ShockwaveHitEntities, Stats, + Body, Combo, Energy, Group, Health, HealthSource, Inventory, Last, Ori, PhysicsState, Pos, + Scale, Shockwave, ShockwaveHitEntities, Stats, }, event::{EventBus, ServerEvent}, resources::{DeltaTime, Time}, @@ -36,6 +36,7 @@ pub struct ReadData<'a> { physics_states: ReadStorage<'a, PhysicsState>, energies: ReadStorage<'a, Energy>, stats: ReadStorage<'a, Stats>, + combos: ReadStorage<'a, Combo>, } /// This system is responsible for handling accepted inputs like moving or @@ -184,6 +185,7 @@ impl<'a> System<'a> for Sys { entity, uid, energy: read_data.energies.get(entity), + combo: read_data.combos.get(entity), }); let target_info = TargetInfo { diff --git a/server/src/events/entity_manipulation.rs b/server/src/events/entity_manipulation.rs index 2a42c497ab..32d6e564af 100644 --- a/server/src/events/entity_manipulation.rs +++ b/server/src/events/entity_manipulation.rs @@ -676,6 +676,7 @@ pub fn handle_explosion(server: &Server, pos: Vec3, explosion: Explosion, o }, RadiusEffect::Attack(attack) => { let energies = &ecs.read_storage::(); + let combos = &ecs.read_storage::(); for (entity_b, pos_b, _health_b, inventory_b_maybe, stats_b_maybe) in ( &ecs.entities(), &ecs.read_storage::(), @@ -715,6 +716,7 @@ pub fn handle_explosion(server: &Server, pos: Vec3, explosion: Explosion, o entity, uid, energy: energies.get(entity), + combo: combos.get(entity), }); let target_info = combat::TargetInfo { From c6a222340e05a8bf3d64fa37519ebf511a6f4ad5 Mon Sep 17 00:00:00 2001 From: Sam Date: Tue, 2 Mar 2021 23:56:09 -0500 Subject: [PATCH 04/18] Particles for lifesteal beam. Changed how frontend recognized beams. --- .../common/abilities/sceptre/healingbeam.ron | 1 + .../abilities/sceptre/lifestealbeam.ron | 1 + .../common/abilities/staff/flamethrower.ron | 1 + .../abilities/staffsimple/flamethrower.ron | 1 + .../unique/quadlowbeam/healingbeam.ron | 1 + .../unique/quadlowbreathe/flamethrower.ron | 1 + .../abilities/unique/turret/flamethrower.ron | 1 + assets/voxygen/shaders/particle-vert.glsl | 9 + common/src/comp/ability.rs | 10 +- common/src/comp/beam.rs | 8 + common/src/outcome.rs | 4 +- common/src/states/basic_beam.rs | 3 + common/src/states/healing_beam.rs | 3 + server/src/events/entity_creation.rs | 6 +- voxygen/src/audio/sfx/mod.rs | 10 +- voxygen/src/render/pipelines/particle.rs | 1 + voxygen/src/scene/particle.rs | 157 +++++++----------- 17 files changed, 109 insertions(+), 109 deletions(-) diff --git a/assets/common/abilities/sceptre/healingbeam.ron b/assets/common/abilities/sceptre/healingbeam.ron index 930ad025d4..4fb8535507 100644 --- a/assets/common/abilities/sceptre/healingbeam.ron +++ b/assets/common/abilities/sceptre/healingbeam.ron @@ -8,4 +8,5 @@ HealingBeam( max_angle: 1.0, lifesteal_eff: 0.0, energy_cost: 50, + specifier: HealingBeam, ) \ No newline at end of file diff --git a/assets/common/abilities/sceptre/lifestealbeam.ron b/assets/common/abilities/sceptre/lifestealbeam.ron index 7fcdc5932f..3f233fac95 100644 --- a/assets/common/abilities/sceptre/lifestealbeam.ron +++ b/assets/common/abilities/sceptre/lifestealbeam.ron @@ -10,4 +10,5 @@ BasicBeam( energy_regen: 25, energy_drain: 0, orientation_behavior: Normal, + specifier: LifestealBeam ) \ No newline at end of file diff --git a/assets/common/abilities/staff/flamethrower.ron b/assets/common/abilities/staff/flamethrower.ron index 968285b222..060c23f821 100644 --- a/assets/common/abilities/staff/flamethrower.ron +++ b/assets/common/abilities/staff/flamethrower.ron @@ -10,4 +10,5 @@ BasicBeam( energy_regen: 0, energy_drain: 350, orientation_behavior: Normal, + specifier: Flamethrower, ) \ No newline at end of file diff --git a/assets/common/abilities/staffsimple/flamethrower.ron b/assets/common/abilities/staffsimple/flamethrower.ron index e87a65dca1..bbf0ce6783 100644 --- a/assets/common/abilities/staffsimple/flamethrower.ron +++ b/assets/common/abilities/staffsimple/flamethrower.ron @@ -10,4 +10,5 @@ BasicBeam( energy_regen: 0, energy_drain: 350, orientation_behavior: Normal, + specifier: Flamethrower, ) diff --git a/assets/common/abilities/unique/quadlowbeam/healingbeam.ron b/assets/common/abilities/unique/quadlowbeam/healingbeam.ron index 089c906bc8..ffbd29ff05 100644 --- a/assets/common/abilities/unique/quadlowbeam/healingbeam.ron +++ b/assets/common/abilities/unique/quadlowbeam/healingbeam.ron @@ -12,4 +12,5 @@ BasicBeam( //energy_cost: 50, energy_drain: 0, orientation_behavior: Normal, + specifier: HealingBeam, ) \ No newline at end of file diff --git a/assets/common/abilities/unique/quadlowbreathe/flamethrower.ron b/assets/common/abilities/unique/quadlowbreathe/flamethrower.ron index 2b8313cfea..8d81986e27 100644 --- a/assets/common/abilities/unique/quadlowbreathe/flamethrower.ron +++ b/assets/common/abilities/unique/quadlowbreathe/flamethrower.ron @@ -10,4 +10,5 @@ BasicBeam( energy_regen: 0, energy_drain: 0, orientation_behavior: Normal, + specifier: Flamethrower, ) \ No newline at end of file diff --git a/assets/common/abilities/unique/turret/flamethrower.ron b/assets/common/abilities/unique/turret/flamethrower.ron index b06e2cf39e..6b865f8bfa 100644 --- a/assets/common/abilities/unique/turret/flamethrower.ron +++ b/assets/common/abilities/unique/turret/flamethrower.ron @@ -10,4 +10,5 @@ BasicBeam( energy_regen: 0, energy_drain: 0, orientation_behavior: Turret, + specifier: Flamethrower, ) \ No newline at end of file diff --git a/assets/voxygen/shaders/particle-vert.glsl b/assets/voxygen/shaders/particle-vert.glsl index 11bc889e28..c6159ace1a 100644 --- a/assets/voxygen/shaders/particle-vert.glsl +++ b/assets/voxygen/shaders/particle-vert.glsl @@ -60,6 +60,7 @@ const int FIRE_BOWL = 18; const int SNOW = 19; const int EXPLOSION = 20; const int ICE = 21; +const int LIFESTEAL_BEAM = 22; // meters per second squared (acceleration) const float earth_gravity = 9.807; @@ -340,6 +341,14 @@ void main() { vec4(vec3(0.4, 1.6 + 0.3 * sin(tick.x * 10 - lifetime * 3 + 4), 1.0 + 0.15 * sin(tick.x * 5 - lifetime * 5)), 1 /*0.3*/), spin_in_axis(inst_dir, tick.z) ); + } else if (inst_mode == LIFESTEAL_BEAM) { + f_reflect = 0.01; + attr = Attr( + spiral_motion(inst_dir, 0.3 * (floor(2 * rand0 + 0.5) - 0.5) * min(linear_scale(10), 1), lifetime / inst_lifespan, 10.0, inst_time), + vec3((1.7 - 0.7 * abs(floor(2 * rand0 - 0.5) + 0.5)) * (1.5 + 0.5 * sin(tick.x * 10 - lifetime * 4))), + vec4(vec3(1.0 + 0.3 * sin(tick.x + lifetime * 5), 1.25 + 0.2 * sin(tick.x * 10 - lifetime * 3 + 4), 0.7), 1 /*0.3*/), + spin_in_axis(inst_dir, tick.z) + ); } else if (inst_mode == ENERGY_NATURE) { f_reflect = 0.0; attr = Attr( diff --git a/common/src/comp/ability.rs b/common/src/comp/ability.rs index 6b2bf66ee7..5c2441dc75 100644 --- a/common/src/comp/ability.rs +++ b/common/src/comp/ability.rs @@ -2,8 +2,8 @@ use crate::{ assets::{self, Asset}, combat, comp::{ - aura, inventory::item::tool::ToolKind, projectile::ProjectileConstructor, skills, Body, - CharacterState, EnergySource, Gravity, LightEmitter, StateUpdate, + aura, beam, inventory::item::tool::ToolKind, projectile::ProjectileConstructor, skills, + Body, CharacterState, EnergySource, Gravity, LightEmitter, StateUpdate, }, states::{ behavior::JoinData, @@ -224,6 +224,7 @@ pub enum CharacterAbility { energy_regen: f32, energy_drain: f32, orientation_behavior: basic_beam::MovementBehavior, + specifier: beam::FrontendSpecifier, }, CastAura { buildup_duration: f32, @@ -243,6 +244,7 @@ pub enum CharacterAbility { range: f32, max_angle: f32, energy_cost: f32, + specifier: beam::FrontendSpecifier, }, } @@ -1472,6 +1474,7 @@ impl From<(&CharacterAbility, AbilityInfo)> for CharacterState { energy_regen, energy_drain, orientation_behavior, + specifier, } => CharacterState::BasicBeam(basic_beam::Data { static_data: basic_beam::StaticData { buildup_duration: Duration::from_secs_f32(*buildup_duration), @@ -1486,6 +1489,7 @@ impl From<(&CharacterAbility, AbilityInfo)> for CharacterState { energy_drain: *energy_drain, ability_info, orientation_behavior: *orientation_behavior, + specifier: *specifier, }, timer: Duration::default(), stage_section: StageSection::Buildup, @@ -1520,6 +1524,7 @@ impl From<(&CharacterAbility, AbilityInfo)> for CharacterState { range, max_angle, energy_cost, + specifier, } => CharacterState::HealingBeam(healing_beam::Data { static_data: healing_beam::StaticData { buildup_duration: Duration::from_secs_f32(*buildup_duration), @@ -1531,6 +1536,7 @@ impl From<(&CharacterAbility, AbilityInfo)> for CharacterState { max_angle: *max_angle, energy_cost: *energy_cost, ability_info, + specifier: *specifier, }, timer: Duration::default(), stage_section: StageSection::Buildup, diff --git a/common/src/comp/beam.rs b/common/src/comp/beam.rs index 60b722aed6..2ad006672f 100644 --- a/common/src/comp/beam.rs +++ b/common/src/comp/beam.rs @@ -11,6 +11,7 @@ pub struct Properties { pub speed: f32, pub duration: Duration, pub owner: Option, + pub specifier: FrontendSpecifier, } // TODO: Separate components out for cheaper network syncing @@ -44,3 +45,10 @@ pub struct Beam { impl Component for Beam { type Storage = IdvStorage; } + +#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq)] +pub enum FrontendSpecifier { + Flamethrower, + LifestealBeam, + HealingBeam, +} diff --git a/common/src/outcome.rs b/common/src/outcome.rs index 6954c28a43..670936c7f9 100644 --- a/common/src/outcome.rs +++ b/common/src/outcome.rs @@ -1,5 +1,5 @@ use crate::{comp, uid::Uid}; -use comp::item::Reagent; +use comp::{beam, item::Reagent}; use serde::{Deserialize, Serialize}; use vek::*; @@ -24,7 +24,7 @@ pub enum Outcome { }, Beam { pos: Vec3, - heal: bool, + specifier: beam::FrontendSpecifier, }, ExpChange { uid: Uid, diff --git a/common/src/states/basic_beam.rs b/common/src/states/basic_beam.rs index c39182ef59..045bf1a86b 100644 --- a/common/src/states/basic_beam.rs +++ b/common/src/states/basic_beam.rs @@ -43,6 +43,8 @@ pub struct StaticData { pub orientation_behavior: MovementBehavior, /// What key is used to press ability pub ability_info: AbilityInfo, + /// Used to specify the beam to the frontend + pub specifier: beam::FrontendSpecifier, } #[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] @@ -141,6 +143,7 @@ impl CharacterBehavior for Data { speed, duration: self.static_data.beam_duration, owner: Some(*data.uid), + specifier: self.static_data.specifier, }; // Gets offsets let body_offsets = match data.body { diff --git a/common/src/states/healing_beam.rs b/common/src/states/healing_beam.rs index 94b2163fff..e5a6806ac5 100644 --- a/common/src/states/healing_beam.rs +++ b/common/src/states/healing_beam.rs @@ -33,6 +33,8 @@ pub struct StaticData { pub energy_cost: f32, /// What key is used to press ability pub ability_info: AbilityInfo, + /// Used to specify the beam to the frontend + pub specifier: beam::FrontendSpecifier, } #[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] @@ -106,6 +108,7 @@ impl CharacterBehavior for Data { speed, duration: self.static_data.beam_duration, owner: Some(*data.uid), + specifier: self.static_data.specifier, }; // Gets offsets let body_offsets = match data.body { diff --git a/server/src/events/entity_creation.rs b/server/src/events/entity_creation.rs index 2fb80f0bd9..a10b60c9d9 100644 --- a/server/src/events/entity_creation.rs +++ b/server/src/events/entity_creation.rs @@ -1,7 +1,6 @@ use crate::{sys, Server, StateExt}; use common::{ character::CharacterId, - combat, comp::{ self, aura::{Aura, AuraKind, AuraTarget}, @@ -175,10 +174,7 @@ pub fn handle_beam(server: &mut Server, properties: beam::Properties, pos: Pos, let ecs = state.ecs(); ecs.write_resource::>().push(Outcome::Beam { pos: pos.0, - heal: properties - .attack - .effects() - .any(|e| matches!(e.effect(), combat::CombatEffect::Heal(h) if *h > 0.0)), + specifier: properties.specifier, }); state.create_beam(properties, pos, ori).build(); } diff --git a/voxygen/src/audio/sfx/mod.rs b/voxygen/src/audio/sfx/mod.rs index 47183dd40b..e90a3c6cb3 100644 --- a/voxygen/src/audio/sfx/mod.rs +++ b/voxygen/src/audio/sfx/mod.rs @@ -91,6 +91,7 @@ use client::Client; use common::{ assets::{self, AssetExt, AssetHandle}, comp::{ + beam, item::{ItemKind, Reagent, ToolKind}, object, Body, CharacterAbilityType, InventoryUpdateEvent, }, @@ -353,14 +354,15 @@ impl SfxMgr { let file_ref = "voxygen.audio.sfx.character.level_up_sound_-_shorter_wind_up"; audio.play_sfx(file_ref, *pos, None); }, - Outcome::Beam { pos, heal } => { - if *heal { + Outcome::Beam { pos, specifier } => match specifier { + beam::FrontendSpecifier::LifestealBeam | beam::FrontendSpecifier::HealingBeam => { let file_ref = "voxygen.audio.sfx.abilities.staff_channeling"; audio.play_sfx(file_ref, *pos, None); - } else { + }, + beam::FrontendSpecifier::Flamethrower => { let file_ref = "voxygen.audio.sfx.abilities.flame_thrower"; audio.play_sfx(file_ref, *pos, None); - } + }, }, Outcome::ExpChange { .. } => {}, } diff --git a/voxygen/src/render/pipelines/particle.rs b/voxygen/src/render/pipelines/particle.rs index 6100ae60df..69c9900584 100644 --- a/voxygen/src/render/pipelines/particle.rs +++ b/voxygen/src/render/pipelines/particle.rs @@ -118,6 +118,7 @@ pub enum ParticleMode { Snow = 19, Explosion = 20, Ice = 21, + LifestealBeam = 22, } impl ParticleMode { diff --git a/voxygen/src/scene/particle.rs b/voxygen/src/scene/particle.rs index 7161bbf88b..99d73c25a2 100644 --- a/voxygen/src/scene/particle.rs +++ b/voxygen/src/scene/particle.rs @@ -8,8 +8,7 @@ use crate::{ }; use common::{ assets::{AssetExt, DotVoxAsset}, - combat::CombatEffect, - comp::{item::Reagent, object, BeamSegment, Body, CharacterState, Ori, Pos, Shockwave}, + comp::{beam, item::Reagent, object, BeamSegment, Body, CharacterState, Ori, Pos, Shockwave}, figure::Segment, outcome::Outcome, resources::DeltaTime, @@ -401,104 +400,70 @@ impl ParticleMgr { .join() .filter(|(_, _, b)| b.creation.map_or(true, |c| (c + dt as f64) >= time)) { - // + // TODO: Handle this less hackily. Done this way as beam segments are created + // every server tick, which is approximately 33 ms. Heartbeat scheduler used to + // account for clients with less than 30 fps because they start the creation + // time when the segments are received and could receive 2 at once + let beam_tick_count = 33.max(self.scheduler.heartbeats(Duration::from_millis(1))); let range = beam.properties.speed * beam.properties.duration.as_secs_f32(); - if beam - .properties - .attack - .effects() - .any(|e| matches!(e.effect(), CombatEffect::Heal(h) if *h > 0.0)) - { - // Emit a light when using healing - lights.push(Light::new(pos.0, Rgb::new(0.1, 1.0, 0.15), 1.0)); - for i in 0..self.scheduler.heartbeats(Duration::from_millis(1)) { - self.particles.push(Particle::new_directed( - beam.properties.duration, - time + i as f64 / 1000.0, - ParticleMode::HealingBeam, + match beam.properties.specifier { + beam::FrontendSpecifier::Flamethrower => { + let mut rng = thread_rng(); + let (from, to) = (Vec3::::unit_z(), *ori.look_dir()); + let m = Mat3::::rotation_from_to_3d(from, to); + // Emit a light when using flames + lights.push(Light::new( pos.0, - pos.0 + *ori.look_dir() * range, + Rgb::new(1.0, 0.25, 0.05).map(|e| e * rng.gen_range(0.8..1.2)), + 2.0, )); - /* - if let CharacterState::BasicBeam(b) = character_state { - if b.stage_section == StageSection::Cast { - if b.static_data.base_hps > 0.0 {// - // Emit a light when using healing - lights.push(Light::new(pos.0 + b.offset, Rgb::new(0.1, 1.0, 0.15), 1.0)); - for i in 0..self.scheduler.heartbeats(Duration::from_millis(1)) { - self.particles.push(Particle::new_directed( - b.static_data.beam_duration, - time + i as f64 / 1000.0, - ParticleMode::HealingBeam, - pos.0 + particle_ori * 0.5 + b.offset, - pos.0 + particle_ori * b.static_data.range + b.offset, - )); - } - } else { - let mut rng = thread_rng(); - let (from, to) = (Vec3::::unit_z(), particle_ori); - let m = Mat3::::rotation_from_to_3d(from, to); - // Emit a light when using flames - lights.push(Light::new( - pos.0 + b.offset, - Rgb::new(1.0, 0.25, 0.05).map(|e| e * rng.gen_range(0.8..1.2)), - 2.0, - )); - self.particles.resize_with( - self.particles.len() - + 2 * usize::from( - self.scheduler.heartbeats(Duration::from_millis(1)), - ), - || { - let phi: f32 = - rng.gen_range(0.0..b.static_data.max_angle.to_radians()); - let theta: f32 = rng.gen_range(0.0..2.0 * PI); - let offset_z = Vec3::new( - phi.sin() * theta.cos(), - phi.sin() * theta.sin(), - phi.cos(), - ); - let random_ori = offset_z * m * Vec3::new(-1.0, -1.0, 1.0); - Particle::new_directed( - b.static_data.beam_duration, - time, - ParticleMode::FlameThrower, - pos.0 + random_ori * 0.5 + b.offset, - pos.0 + random_ori * b.static_data.range + b.offset, - ) - }, - ); - } - */ - } - } else { - let mut rng = thread_rng(); - let (from, to) = (Vec3::::unit_z(), *ori.look_dir()); - let m = Mat3::::rotation_from_to_3d(from, to); - // Emit a light when using flames - lights.push(Light::new( - pos.0, - Rgb::new(1.0, 0.25, 0.05).map(|e| e * rng.gen_range(0.8..1.2)), - 2.0, - )); - self.particles.resize_with( - self.particles.len() - + 2 * usize::from(self.scheduler.heartbeats(Duration::from_millis(1))), - || { - let phi: f32 = rng.gen_range(0.0..beam.properties.angle); - let theta: f32 = rng.gen_range(0.0..2.0 * PI); - let offset_z = - Vec3::new(phi.sin() * theta.cos(), phi.sin() * theta.sin(), phi.cos()); - let random_ori = offset_z * m * Vec3::new(-1.0, -1.0, 1.0); - Particle::new_directed( + self.particles.resize_with( + self.particles.len() + 2 * usize::from(beam_tick_count), + || { + let phi: f32 = rng.gen_range(0.0..beam.properties.angle); + let theta: f32 = rng.gen_range(0.0..2.0 * PI); + let offset_z = Vec3::new( + phi.sin() * theta.cos(), + phi.sin() * theta.sin(), + phi.cos(), + ); + let random_ori = offset_z * m * Vec3::new(-1.0, -1.0, 1.0); + Particle::new_directed( + beam.properties.duration, + time, + ParticleMode::FlameThrower, + pos.0, + pos.0 + random_ori * range, + ) + }, + ); + }, + beam::FrontendSpecifier::HealingBeam => { + // Emit a light when using healing + lights.push(Light::new(pos.0, Rgb::new(0.1, 1.0, 0.15), 1.0)); + for i in 0..beam_tick_count { + self.particles.push(Particle::new_directed( beam.properties.duration, - time, - ParticleMode::FlameThrower, - pos.0, /* + random_ori */ - pos.0 + random_ori * range, - ) - }, - ); + time + i as f64 / 1000.0, + ParticleMode::HealingBeam, + pos.0, + pos.0 + *ori.look_dir() * range, + )); + } + }, + beam::FrontendSpecifier::LifestealBeam => { + // Emit a light when using lifesteal beam + lights.push(Light::new(pos.0, Rgb::new(0.8, 1.0, 0.5), 1.0)); + for i in 0..beam_tick_count { + self.particles.push(Particle::new_directed( + beam.properties.duration, + time + i as f64 / 1000.0, + ParticleMode::LifestealBeam, + pos.0, + pos.0 + *ori.look_dir() * range, + )); + } + }, } } } From b6f4543a146384d3c47b8ccb4a659cbf803a8617 Mon Sep 17 00:00:00 2001 From: Sam Date: Wed, 3 Mar 2021 00:18:01 -0500 Subject: [PATCH 05/18] Fixed beam offsets, told healing beam to use beam animation. --- common/src/states/basic_beam.rs | 19 ++++++------------- common/src/states/healing_beam.rs | 19 ++++++------------- voxygen/src/scene/figure/mod.rs | 26 ++++++++++++++++++++++++++ 3 files changed, 38 insertions(+), 26 deletions(-) diff --git a/common/src/states/basic_beam.rs b/common/src/states/basic_beam.rs index 045bf1a86b..4b6d5a3fc9 100644 --- a/common/src/states/basic_beam.rs +++ b/common/src/states/basic_beam.rs @@ -3,7 +3,7 @@ use crate::{ Attack, AttackDamage, AttackEffect, CombatEffect, CombatRequirement, Damage, DamageSource, GroupTarget, }, - comp::{beam, Body, CharacterState, EnergyChange, EnergySource, Ori, Pos, StateUpdate}, + comp::{beam, CharacterState, EnergyChange, EnergySource, Ori, Pos, StateUpdate}, event::ServerEvent, states::{ behavior::{CharacterBehavior, JoinData}, @@ -146,18 +146,11 @@ impl CharacterBehavior for Data { specifier: self.static_data.specifier, }; // Gets offsets - let body_offsets = match data.body { - Body::Humanoid(_) => Vec3::new( - (data.body.radius() + 2.0) * data.inputs.look_dir.x, - (data.body.radius() + 2.0) * data.inputs.look_dir.y, - data.body.eye_height() * 0.55, - ), - _ => Vec3::new( - (data.body.radius() + 3.0) * data.inputs.look_dir.x, - (data.body.radius() + 3.0) * data.inputs.look_dir.y, - data.body.eye_height() * 0.55, - ), - }; + let body_offsets = Vec3::new( + (data.body.radius() + 1.0) * data.inputs.look_dir.x, + (data.body.radius() + 1.0) * data.inputs.look_dir.y, + data.body.eye_height() * 0.6, + ); let pos = Pos(data.pos.0 + body_offsets); // Create beam segment update.server_events.push_front(ServerEvent::BeamSegment { diff --git a/common/src/states/healing_beam.rs b/common/src/states/healing_beam.rs index e5a6806ac5..f5c950428e 100644 --- a/common/src/states/healing_beam.rs +++ b/common/src/states/healing_beam.rs @@ -1,6 +1,6 @@ use crate::{ combat::{Attack, AttackEffect, CombatEffect, CombatRequirement, GroupTarget}, - comp::{beam, Body, CharacterState, Ori, Pos, StateUpdate}, + comp::{beam, CharacterState, Ori, Pos, StateUpdate}, event::ServerEvent, states::{ behavior::{CharacterBehavior, JoinData}, @@ -111,18 +111,11 @@ impl CharacterBehavior for Data { specifier: self.static_data.specifier, }; // Gets offsets - let body_offsets = match data.body { - Body::Humanoid(_) => Vec3::new( - (data.body.radius() + 2.0) * data.inputs.look_dir.x, - (data.body.radius() + 2.0) * data.inputs.look_dir.y, - data.body.eye_height() * 0.55, - ), - _ => Vec3::new( - (data.body.radius() + 3.0) * data.inputs.look_dir.x, - (data.body.radius() + 3.0) * data.inputs.look_dir.y, - data.body.eye_height() * 0.55, - ), - }; + let body_offsets = Vec3::new( + (data.body.radius() + 1.0) * data.inputs.look_dir.x, + (data.body.radius() + 1.0) * data.inputs.look_dir.y, + data.body.eye_height() * 0.6, + ); let pos = Pos(data.pos.0 + body_offsets); // Create beam segment update.server_events.push_front(ServerEvent::BeamSegment { diff --git a/voxygen/src/scene/figure/mod.rs b/voxygen/src/scene/figure/mod.rs index e4d0a9af31..f02effb9f1 100644 --- a/voxygen/src/scene/figure/mod.rs +++ b/voxygen/src/scene/figure/mod.rs @@ -1252,6 +1252,32 @@ impl FigureMgr { skeleton_attr, ) }, + CharacterState::HealingBeam(s) => { + let stage_time = s.timer.as_secs_f32(); + let stage_progress = match s.stage_section { + StageSection::Buildup => { + stage_time / s.static_data.buildup_duration.as_secs_f32() + }, + StageSection::Cast => s.timer.as_secs_f32(), + StageSection::Recover => { + stage_time / s.static_data.recover_duration.as_secs_f32() + }, + _ => 0.0, + }; + anim::character::BeamAnimation::update_skeleton( + &target_base, + ( + active_tool_kind, + second_tool_kind, + time, + vel.0.magnitude(), + Some(s.stage_section), + ), + stage_progress, + &mut state_animation_rate, + skeleton_attr, + ) + }, CharacterState::ComboMelee(s) => { let stage_index = (s.stage - 1) as usize; let stage_time = s.timer.as_secs_f32(); From c5f74e528da8392cacdb3d712aaedfa6b4b24cee Mon Sep 17 00:00:00 2001 From: Sam Date: Wed, 3 Mar 2021 15:34:42 -0500 Subject: [PATCH 06/18] Animation for cast aura, combo information passed to skillbar. --- voxygen/anim/src/character/shockwave.rs | 2 +- voxygen/src/hud/mod.rs | 4 ++++ voxygen/src/hud/skillbar.rs | 5 ++++- voxygen/src/scene/figure/mod.rs | 28 +++++++++++++++++++++++++ 4 files changed, 37 insertions(+), 2 deletions(-) diff --git a/voxygen/anim/src/character/shockwave.rs b/voxygen/anim/src/character/shockwave.rs index 04171b4e12..661351857f 100644 --- a/voxygen/anim/src/character/shockwave.rs +++ b/voxygen/anim/src/character/shockwave.rs @@ -36,7 +36,7 @@ impl Animation for ShockwaveAnimation { let (move1, move2, move3) = match stage_section { Some(StageSection::Buildup) => (anim_time, 0.0, 0.0), - Some(StageSection::Swing) => (1.0, anim_time, 0.0), + Some(StageSection::Swing) | Some(StageSection::Cast) => (1.0, anim_time, 0.0), Some(StageSection::Recover) => (1.0, 1.0, anim_time), _ => (0.0, 0.0, 0.0), }; diff --git a/voxygen/src/hud/mod.rs b/voxygen/src/hud/mod.rs index 75c594ec98..9ca59adf55 100644 --- a/voxygen/src/hud/mod.rs +++ b/voxygen/src/hud/mod.rs @@ -2158,6 +2158,7 @@ impl Hud { let controllers = ecs.read_storage::(); let ability_map = ecs.fetch::(); let bodies = ecs.read_storage::(); + let combos = ecs.read_storage::(); if let ( Some(health), @@ -2165,12 +2166,14 @@ impl Hud { Some(energy), Some(_character_state), Some(_controller), + Some(combo), ) = ( healths.get(entity), inventories.get(entity), energies.get(entity), character_states.get(entity), controllers.get(entity).map(|c| &c.inputs), + combos.get(entity), ) { Skillbar::new( global_state, @@ -2190,6 +2193,7 @@ impl Hud { i18n, &ability_map, &msm, + &combo, ) .set(self.ids.skillbar, ui_widgets); } diff --git a/voxygen/src/hud/skillbar.rs b/voxygen/src/hud/skillbar.rs index 4bfeddf724..fde06c7d91 100644 --- a/voxygen/src/hud/skillbar.rs +++ b/voxygen/src/hud/skillbar.rs @@ -21,7 +21,7 @@ use common::comp::{ tool::{AbilityMap, Tool, ToolKind}, Hands, Item, ItemKind, MaterialStatManifest, }, - Energy, Health, Inventory, + Combo, Energy, Health, Inventory, }; use conrod_core::{ color, @@ -141,6 +141,7 @@ pub struct Skillbar<'a> { common: widget::CommonBuilder, ability_map: &'a AbilityMap, msm: &'a MaterialStatManifest, + combo: &'a Combo, } impl<'a> Skillbar<'a> { @@ -163,6 +164,7 @@ impl<'a> Skillbar<'a> { localized_strings: &'a Localization, ability_map: &'a AbilityMap, msm: &'a MaterialStatManifest, + combo: &'a Combo, ) -> Self { Self { global_state, @@ -183,6 +185,7 @@ impl<'a> Skillbar<'a> { localized_strings, ability_map, msm, + combo, } } } diff --git a/voxygen/src/scene/figure/mod.rs b/voxygen/src/scene/figure/mod.rs index f02effb9f1..24ee90bd01 100644 --- a/voxygen/src/scene/figure/mod.rs +++ b/voxygen/src/scene/figure/mod.rs @@ -1099,6 +1099,34 @@ impl FigureMgr { skeleton_attr, ) }, + CharacterState::CastAura(s) => { + let stage_time = s.timer.as_secs_f32(); + let stage_progress = match s.stage_section { + StageSection::Buildup => { + stage_time / s.static_data.buildup_duration.as_secs_f32() + }, + StageSection::Cast => { + stage_time / s.static_data.cast_duration.as_secs_f32() + }, + StageSection::Recover => { + stage_time / s.static_data.recover_duration.as_secs_f32() + }, + _ => 0.0, + }; + anim::character::ShockwaveAnimation::update_skeleton( + &target_base, + ( + active_tool_kind, + second_tool_kind, + time, + vel.0.magnitude(), + Some(s.stage_section), + ), + stage_progress, + &mut state_animation_rate, + skeleton_attr, + ) + }, CharacterState::LeapMelee(s) => { let stage_progress = match active_tool_kind { Some(ToolKind::Axe | ToolKind::Hammer) => { From 53100b6f372f3f74a4f5130675713992fb62f1d2 Mon Sep 17 00:00:00 2001 From: Monty Date: Thu, 4 Mar 2021 02:24:54 +0100 Subject: [PATCH 07/18] combo point display --- voxygen/src/hud/skillbar.rs | 47 +++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/voxygen/src/hud/skillbar.rs b/voxygen/src/hud/skillbar.rs index fde06c7d91..88f79f22e2 100644 --- a/voxygen/src/hud/skillbar.rs +++ b/voxygen/src/hud/skillbar.rs @@ -28,6 +28,7 @@ use conrod_core::{ widget::{self, Button, Image, Rectangle, Text}, widget_ids, Color, Colorable, Positionable, Sizeable, Widget, WidgetCommon, }; +use inline_tweak::*; use vek::*; widget_ids! { @@ -74,6 +75,11 @@ widget_ids! { stamina_txt_alignment, stamina_txt_bg, stamina_txt, + // Combo Counter + combo_icon, + combo_align, + combo_bg, + combo, // Slots m1_slot, m1_slot_bg, @@ -912,6 +918,43 @@ impl<'a> Widget for Skillbar<'a> { .w_h(16.0, 18.0) .mid_bottom_with_margin_on(state.ids.m2_content, -11.0) .set(state.ids.m2_ico, ui); + + // Combo Counter + if self.combo.counter() > 0 { + let combo_txt = format!("{}", self.combo.counter()); + let combo_cnt = self.combo.counter() as f32; + let fnt_col = Color::Rgba( + (1.0 - combo_cnt / (combo_cnt + tweak!(1.0))).max(0.79), + (1.0 - combo_cnt / (combo_cnt + tweak!(40.0))).max(0.19), + (1.0 - combo_cnt / (combo_cnt + tweak!(5.0))).max(0.17), + 1.0, + ); + let fnt_size = + ((14.0 + self.combo.counter() as f32 * tweak!(0.5)).min(tweak!(50.0))) as u32; + + Rectangle::fill_with([10.0, 10.0], color::TRANSPARENT) + .middle_of(ui.window) + .set(state.ids.combo_align, ui); + Text::new(combo_txt.as_str()) + .bottom_right_with_margins_on(state.ids.combo_align, tweak!(-300.0), tweak!(-190.0)) + .font_size(self.fonts.cyri.scale(fnt_size)) + .font_id(self.fonts.cyri.conrod_id) + .color(BLACK) + .set(state.ids.combo_bg, ui); + Text::new(combo_txt.as_str()) + .bottom_right_with_margins_on(state.ids.combo_bg, 1.0, 1.0) + .font_size(self.fonts.cyri.scale(fnt_size)) + .font_id(self.fonts.cyri.conrod_id) + .color(fnt_col) + .set(state.ids.combo, ui); + let icon_size = fnt_size as f64 * tweak!(0.75); + Image::new(self.imgs.combat_rating_ico_shadow) + .wh([icon_size, icon_size]) + .bottom_right_with_margins_on(state.ids.combo, 0.0, tweak!(-5.0) - icon_size) + .color(Some(fnt_col)) + .graphics_for(state.ids.combo) + .set(state.ids.combo_icon, ui); + } } } @@ -939,6 +982,10 @@ fn ability_description(tool: &ToolKind) -> Option<(&str, &str)> { "Possessing Arrow", "\nShoots a poisonous arrow.\nLets you control your target.", )), + ToolKind::Sceptre => Some(( + "Thorn Bulwark", + "\nProtects you and your group with thorns\nfor a short amount of time.", + )), _ => None, } } From 398370ca51779a7e9faf239a23d9fc1b05ffd0b3 Mon Sep 17 00:00:00 2001 From: Sam Date: Wed, 3 Mar 2021 22:43:11 -0500 Subject: [PATCH 08/18] Changed lifesteal beam particles to look better. Warding aura now just provides damage reduction instead of invulnerability. Also with a longer duration and less movespeed penalty. --- .../common/abilities/sceptre/wardingaura.ron | 6 ++--- assets/voxygen/i18n/en/buff.ron | 2 ++ assets/voxygen/shaders/particle-vert.glsl | 2 +- common/src/comp/buff.rs | 27 ++++++++++++------- common/src/states/cast_aura.rs | 2 +- common/sys/src/buff.rs | 4 +-- voxygen/src/hud/mod.rs | 4 +++ 7 files changed, 30 insertions(+), 17 deletions(-) diff --git a/assets/common/abilities/sceptre/wardingaura.ron b/assets/common/abilities/sceptre/wardingaura.ron index 0fb1815e7c..cdebac58e2 100644 --- a/assets/common/abilities/sceptre/wardingaura.ron +++ b/assets/common/abilities/sceptre/wardingaura.ron @@ -4,9 +4,9 @@ CastAura( recover_duration: 0.25, targets: InGroup, aura: ( - kind: Invulnerability, - strength: 1.0, - duration: Some(0.5), + kind: ProtectingWard, + strength: 0.5, + duration: Some(10.0), category: Magical, ), range: 25.0, diff --git a/assets/voxygen/i18n/en/buff.ron b/assets/voxygen/i18n/en/buff.ron index 1c0de6e367..c29a013a71 100644 --- a/assets/voxygen/i18n/en/buff.ron +++ b/assets/voxygen/i18n/en/buff.ron @@ -17,6 +17,8 @@ "buff.desc.campfire_heal": "Resting at a campfire heals 1% per second.", "buff.title.invulnerability": "Invulnerability", "buff.desc.invulnerability": "You cannot be damaged by any attack.", + "buff.title.protectingward": "Protecting Ward", + "buff.desc.protectingward": "You are protected, somewhat, from attacks." // Debuffs "buff.title.bleed": "Bleeding", "buff.desc.bleed": "Inflicts regular damage.", diff --git a/assets/voxygen/shaders/particle-vert.glsl b/assets/voxygen/shaders/particle-vert.glsl index c6159ace1a..c2d6aa3311 100644 --- a/assets/voxygen/shaders/particle-vert.glsl +++ b/assets/voxygen/shaders/particle-vert.glsl @@ -346,7 +346,7 @@ void main() { attr = Attr( spiral_motion(inst_dir, 0.3 * (floor(2 * rand0 + 0.5) - 0.5) * min(linear_scale(10), 1), lifetime / inst_lifespan, 10.0, inst_time), vec3((1.7 - 0.7 * abs(floor(2 * rand0 - 0.5) + 0.5)) * (1.5 + 0.5 * sin(tick.x * 10 - lifetime * 4))), - vec4(vec3(1.0 + 0.3 * sin(tick.x + lifetime * 5), 1.25 + 0.2 * sin(tick.x * 10 - lifetime * 3 + 4), 0.7), 1 /*0.3*/), + vec4(vec3(1.35 + 0.25 * sin(tick.x * 3 - lifetime * 3 + 4), 0.7 + 1.0 * sin(tick.x * 5 + lifetime * 5), 0.8 + 0.25 * sin(lifetime - tick.x * 5)), 1 /*0.3*/), spin_in_axis(inst_dir, tick.z) ); } else if (inst_mode == ENERGY_NATURE) { diff --git a/common/src/comp/buff.rs b/common/src/comp/buff.rs index 7dc9fd0dd0..91a103e918 100644 --- a/common/src/comp/buff.rs +++ b/common/src/comp/buff.rs @@ -33,6 +33,8 @@ pub enum BuffKind { IncreaseMaxHealth, /// Makes you immune to attacks Invulnerability, + /// Reduces incoming damage + ProtectingWard, } #[cfg(not(target_arch = "wasm32"))] @@ -49,6 +51,7 @@ impl BuffKind { BuffKind::IncreaseMaxEnergy => true, BuffKind::IncreaseMaxHealth => true, BuffKind::Invulnerability => true, + BuffKind::ProtectingWard => true, } } @@ -101,16 +104,11 @@ pub enum BuffEffect { kind: ModifierKind, }, /// Changes maximum health by a certain amount - MaxHealthModifier { - value: f32, - kind: ModifierKind, - }, + MaxHealthModifier { value: f32, kind: ModifierKind }, /// Changes maximum stamina by a certain amount - MaxEnergyModifier { - value: f32, - kind: ModifierKind, - }, - ImmuneToAttacks, + MaxEnergyModifier { value: f32, kind: ModifierKind }, + /// Reduces damage after armor is accounted for by this fraction + DamageReduction(f32), } /// Actual de/buff. @@ -213,7 +211,16 @@ impl Buff { }], data.duration, ), - BuffKind::Invulnerability => (vec![BuffEffect::ImmuneToAttacks], data.duration), + BuffKind::Invulnerability => (vec![BuffEffect::DamageReduction(1.0)], data.duration), + BuffKind::ProtectingWard => ( + vec![BuffEffect::DamageReduction( + // Causes non-linearity in effect strength, but necessary to allow for tool + // power and other things to affect the strength. 0.5 also still provides 50% + // damage reduction. + data.strength / (0.5 + data.strength), + )], + data.duration, + ), }; Buff { kind, diff --git a/common/src/states/cast_aura.rs b/common/src/states/cast_aura.rs index 4582698e94..8f419e52fc 100644 --- a/common/src/states/cast_aura.rs +++ b/common/src/states/cast_aura.rs @@ -47,7 +47,7 @@ impl CharacterBehavior for Data { fn behavior(&self, data: &JoinData) -> StateUpdate { let mut update = StateUpdate::from(data); - handle_move(data, &mut update, 0.6); + handle_move(data, &mut update, 0.8); handle_jump(data, &mut update); if !ability_key_is_pressed(data, self.static_data.ability_info.key) { handle_interrupt(data, &mut update, false); diff --git a/common/sys/src/buff.rs b/common/sys/src/buff.rs index 0e71d50533..da0be23308 100644 --- a/common/sys/src/buff.rs +++ b/common/sys/src/buff.rs @@ -165,8 +165,8 @@ impl<'a> System<'a> for Sys { energy.set_maximum(new_max); }, }, - BuffEffect::ImmuneToAttacks => { - stat.damage_reduction = 1.0; + BuffEffect::DamageReduction(dr) => { + stat.damage_reduction = dr.min(1.0); }, }; } diff --git a/voxygen/src/hud/mod.rs b/voxygen/src/hud/mod.rs index 9ca59adf55..f7d54d0a7e 100644 --- a/voxygen/src/hud/mod.rs +++ b/voxygen/src/hud/mod.rs @@ -3282,6 +3282,8 @@ pub fn get_buff_image(buff: BuffKind, imgs: &Imgs) -> conrod_core::image::Id { BuffKind::IncreaseMaxEnergy { .. } => imgs.buff_energyplus_0, BuffKind::IncreaseMaxHealth { .. } => imgs.buff_healthplus_0, BuffKind::Invulnerability => imgs.buff_invincibility_0, + // Do not merge until icon for this buff + BuffKind::ProtectingWard => imgs.buff_invincibility_0, // Debuffs BuffKind::Bleeding { .. } => imgs.debuff_bleed_0, BuffKind::Cursed { .. } => imgs.debuff_skull_0, @@ -3298,6 +3300,7 @@ pub fn get_buff_title(buff: BuffKind, localized_strings: &Localization) -> &str BuffKind::IncreaseMaxHealth { .. } => localized_strings.get("buff.title.IncreaseMaxHealth"), BuffKind::IncreaseMaxEnergy { .. } => localized_strings.get("buff.title.staminaup"), BuffKind::Invulnerability => localized_strings.get("buff.title.invulnerability"), + BuffKind::ProtectingWard => localized_strings.get("buff.title.protectingward"), // Debuffs BuffKind::Bleeding { .. } => localized_strings.get("buff.title.bleed"), BuffKind::Cursed { .. } => localized_strings.get("buff.title.cursed"), @@ -3314,6 +3317,7 @@ pub fn get_buff_desc(buff: BuffKind, localized_strings: &Localization) -> &str { BuffKind::IncreaseMaxHealth { .. } => localized_strings.get("buff.desc.IncreaseMaxHealth"), BuffKind::IncreaseMaxEnergy { .. } => localized_strings.get("buff.desc.IncreaseMaxEnergy"), BuffKind::Invulnerability => localized_strings.get("buff.desc.invulnerability"), + BuffKind::ProtectingWard => localized_strings.get("buff.desc.protectingward"), // Debuffs BuffKind::Bleeding { .. } => localized_strings.get("buff.desc.bleed"), BuffKind::Cursed { .. } => localized_strings.get("buff.desc.cursed"), From c29cb037e7be2da1ab35f9420804bd6149537e21 Mon Sep 17 00:00:00 2001 From: Monty Date: Thu, 4 Mar 2021 20:11:44 +0100 Subject: [PATCH 09/18] better combo indicator animation --- voxygen/src/hud/mod.rs | 8 ++++-- voxygen/src/hud/skillbar.rs | 56 +++++++++++++++++++++++-------------- 2 files changed, 41 insertions(+), 23 deletions(-) diff --git a/voxygen/src/hud/mod.rs b/voxygen/src/hud/mod.rs index f7d54d0a7e..fa8e3dc7c6 100644 --- a/voxygen/src/hud/mod.rs +++ b/voxygen/src/hud/mod.rs @@ -61,7 +61,7 @@ use crate::{ GlobalState, }; use client::Client; - +use common::resources::Time; use common::{ combat, comp::{ @@ -887,7 +887,7 @@ impl Hud { let items = ecs.read_storage::(); let inventories = ecs.read_storage::(); let msm = ecs.read_resource::(); - let entities = ecs.entities(); + let entities = ecs.entities(); let me = client.entity(); if (client.pending_trade().is_some() && !self.show.trade) @@ -2159,6 +2159,7 @@ impl Hud { let ability_map = ecs.fetch::(); let bodies = ecs.read_storage::(); let combos = ecs.read_storage::(); + let time = ecs.read_resource::