From a970a611928a5c7e421431c6df59eda22ebfd705 Mon Sep 17 00:00:00 2001 From: Joseph Gerardot <indyJAG@gmail.com> Date: Wed, 20 Nov 2019 13:31:36 -0500 Subject: [PATCH 01/12] Add energy comsumption on rolling and charging, and accelerating regeneration when idle. --- common/src/comp/energy.rs | 3 +++ common/src/comp/mod.rs | 2 +- common/src/comp/stats.rs | 5 +---- common/src/sys/stats.rs | 44 +++++++++++++++++++++++++++++++++++---- 4 files changed, 45 insertions(+), 9 deletions(-) diff --git a/common/src/comp/energy.rs b/common/src/comp/energy.rs index 88164813e0..23d4d9535d 100644 --- a/common/src/comp/energy.rs +++ b/common/src/comp/energy.rs @@ -5,6 +5,7 @@ use specs_idvs::IDVStorage; pub struct Energy { current: u32, maximum: u32, + pub regen_rate: i32, pub last_change: Option<(i32, f64, EnergySource)>, } @@ -12,6 +13,7 @@ pub struct Energy { pub enum EnergySource { CastSpell, LevelUp, + Regen, Unknown, } @@ -20,6 +22,7 @@ impl Energy { Energy { current: amount, maximum: amount, + regen_rate: 0, last_change: None, } } diff --git a/common/src/comp/mod.rs b/common/src/comp/mod.rs index 5f2a631c89..21f6114608 100644 --- a/common/src/comp/mod.rs +++ b/common/src/comp/mod.rs @@ -26,7 +26,7 @@ pub use controller::{ ControlEvent, Controller, ControllerInputs, Input, InputState, InventoryManip, MountState, Mounting, }; -pub use energy::Energy; +pub use energy::{Energy, EnergySource}; pub use inputs::CanBuild; pub use inventory::{item, Inventory, InventoryUpdate, Item, ItemKind}; pub use last::Last; diff --git a/common/src/comp/stats.rs b/common/src/comp/stats.rs index 532f6cb8be..117f064f04 100644 --- a/common/src/comp/stats.rs +++ b/common/src/comp/stats.rs @@ -164,10 +164,7 @@ impl Stats { current: 0, maximum: 50, }, - equipment: Equipment { - main: main, - alt: None, - }, + equipment: Equipment { main, alt: None }, is_dead: false, }; diff --git a/common/src/sys/stats.rs b/common/src/sys/stats.rs index f4ac03e576..6e668a98fc 100644 --- a/common/src/sys/stats.rs +++ b/common/src/sys/stats.rs @@ -1,9 +1,13 @@ use crate::{ - comp::{HealthSource, Stats}, + comp::{ActionState, CharacterState, Energy, EnergySource, HealthSource, Stats}, event::{EventBus, ServerEvent}, state::DeltaTime, }; -use specs::{Entities, Join, Read, System, WriteStorage}; +use specs::{Entities, Join, Read, ReadStorage, System, WriteStorage}; + +const ENERGY_REGEN_ACCEL: i32 = 1; +const BLOCK_COST: i32 = 50; +const ROLL_CHARGE_COST: i32 = 200; /// This system kills players /// and handles players levelling up @@ -13,10 +17,15 @@ impl<'a> System<'a> for Sys { Entities<'a>, Read<'a, DeltaTime>, Read<'a, EventBus<ServerEvent>>, + ReadStorage<'a, CharacterState>, WriteStorage<'a, Stats>, + WriteStorage<'a, Energy>, ); - fn run(&mut self, (entities, dt, server_event_bus, mut stats): Self::SystemData) { + fn run( + &mut self, + (entities, dt, server_event_bus, character_states, mut stats,mut energies): Self::SystemData, + ) { let mut server_event_emitter = server_event_bus.emitter(); // Increment last change timer @@ -27,7 +36,14 @@ impl<'a> System<'a> for Sys { stats.set_event_emission(true); // Mutates all stats every tick causing the server to resend this component for every entity every tick - for (entity, mut stats) in (&entities, &mut stats.restrict_mut()).join() { + for (entity, character_state, mut stats, energy) in ( + &entities, + &character_states, + &mut stats.restrict_mut(), + &mut energies, + ) + .join() + { let (set_dead, level_up) = { let stat = stats.get_unchecked(); ( @@ -58,6 +74,26 @@ impl<'a> System<'a> for Sys { stat.health .set_to(stat.health.maximum(), HealthSource::LevelUp) } + + // Recharge energy if not wielding, and accelerate. + match character_state.action { + ActionState::Wield { .. } | ActionState::Attack { .. } => energy.regen_rate = 0, + ActionState::Block { .. } => { + energy.change_by(framerate_dt(-BLOCK_COST, dt.0), EnergySource::CastSpell) + } + ActionState::Roll { .. } | ActionState::Charge { .. } => energy.change_by( + framerate_dt(-ROLL_CHARGE_COST, dt.0), + EnergySource::CastSpell, + ), + ActionState::Idle => { + energy.regen_rate += ENERGY_REGEN_ACCEL; + energy.change_by(energy.regen_rate, EnergySource::Regen); + } + } } } } +/// Convience method to scale an integer by dt +fn framerate_dt(a: i32, dt: f32) -> i32 { + (a as f32 * (dt)) as i32 +} From 715e01f989758d224b9ed04e517418bc5159b934 Mon Sep 17 00:00:00 2001 From: Joseph Gerardot <indyJAG@gmail.com> Date: Thu, 21 Nov 2019 19:53:28 -0500 Subject: [PATCH 02/12] Make charging take a discrete amount of energy and change energy regeneration to use floats so it is smoother and tickrate-independent. --- common/src/comp/energy.rs | 25 +++++++++++++++++++++++-- common/src/comp/stats.rs | 20 ++++++++++++++++++++ common/src/sys/controller.rs | 22 ++++++++++++++++------ common/src/sys/stats.rs | 27 +++++++-------------------- 4 files changed, 66 insertions(+), 28 deletions(-) diff --git a/common/src/comp/energy.rs b/common/src/comp/energy.rs index 23d4d9535d..2fcb180f09 100644 --- a/common/src/comp/energy.rs +++ b/common/src/comp/energy.rs @@ -5,7 +5,7 @@ use specs_idvs::IDVStorage; pub struct Energy { current: u32, maximum: u32, - pub regen_rate: i32, + pub regen_rate: f32, pub last_change: Option<(i32, f64, EnergySource)>, } @@ -17,12 +17,18 @@ pub enum EnergySource { Unknown, } +#[derive(Debug)] +pub enum StatChangeError { + Underflow, + Overflow, +} + impl Energy { pub fn new(amount: u32) -> Energy { Energy { current: amount, maximum: amount, - regen_rate: 0, + regen_rate: 0.0, last_change: None, } } @@ -46,6 +52,21 @@ impl Energy { self.last_change = Some((amount, 0.0, cause)); } + pub fn try_change_by( + &mut self, + amount: i32, + cause: EnergySource, + ) -> Result<(), StatChangeError> { + if self.current as i32 + amount < 0 { + Err(StatChangeError::Underflow) + } else if self.current as i32 + amount > self.maximum as i32 { + Err(StatChangeError::Overflow) + } else { + self.change_by(amount, cause); + Ok(()) + } + } + pub fn set_maximum(&mut self, amount: u32) { self.maximum = amount; self.current = self.current.min(self.maximum); diff --git a/common/src/comp/stats.rs b/common/src/comp/stats.rs index 117f064f04..5d0adba2e6 100644 --- a/common/src/comp/stats.rs +++ b/common/src/comp/stats.rs @@ -76,6 +76,26 @@ impl Health { self.current = self.current.min(self.maximum); } } +#[derive(Debug)] +pub enum StatChangeError { + Underflow, + Overflow, +} +use std::error::Error; +use std::fmt; +impl fmt::Display for StatChangeError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "{}", + match self { + Self::Underflow => "insufficient stat quantity", + Self::Overflow => "stat quantity would overflow", + } + ) + } +} +impl Error for StatChangeError {} impl Exp { pub fn current(&self) -> u32 { diff --git a/common/src/sys/controller.rs b/common/src/sys/controller.rs index f68839f571..b5b48aa2fa 100644 --- a/common/src/sys/controller.rs +++ b/common/src/sys/controller.rs @@ -2,8 +2,8 @@ use super::movement::ROLL_DURATION; use crate::{ comp::{ self, item, projectile, ActionState, ActionState::*, Body, CharacterState, ControlEvent, - Controller, ControllerInputs, HealthChange, HealthSource, ItemKind, Mounting, - MovementState, MovementState::*, PhysicsState, Projectile, Stats, Vel, + Controller, ControllerInputs, Energy, EnergySource, HealthChange, HealthSource, ItemKind, + Mounting, MovementState, MovementState::*, PhysicsState, Projectile, Stats, Vel, }, event::{Emitter, EventBus, LocalEvent, ServerEvent}, state::DeltaTime, @@ -16,6 +16,8 @@ use specs::{ use std::time::Duration; use vek::*; +const CHARGE_COST: i32 = 50; + /// # Controller System /// #### Responsible for validating controller inputs and setting new Character States /// ---- @@ -247,6 +249,7 @@ impl<'a> System<'a> for Sys { WriteStorage<'a, Controller>, WriteStorage<'a, CharacterState>, ReadStorage<'a, Stats>, + WriteStorage<'a, Energy>, ReadStorage<'a, Body>, ReadStorage<'a, Vel>, ReadStorage<'a, PhysicsState>, @@ -264,6 +267,7 @@ impl<'a> System<'a> for Sys { mut controllers, mut character_states, stats, + mut energies, bodies, velocities, physics_states, @@ -273,12 +277,13 @@ impl<'a> System<'a> for Sys { ) { let mut server_emitter = server_bus.emitter(); let mut local_emitter = local_bus.emitter(); - for (entity, uid, controller, mut character, stats, body, vel, physics, mount) in ( + for (entity, uid, controller, mut character, stats, energy, body, vel, physics, mount) in ( &entities, &uids, &mut controllers, &mut character_states, &stats, + &mut energies, &bodies, &velocities, &physics_states, @@ -579,9 +584,14 @@ impl<'a> System<'a> for Sys { // Try to charge if inputs.charge.is_pressed() && !inputs.charge.is_held_down() { - character.action = Charge { - time_left: Duration::from_millis(250), - }; + if energy + .try_change_by(-CHARGE_COST, EnergySource::CastSpell) + .is_ok() + { + character.action = Charge { + time_left: Duration::from_millis(250), + } + } continue; } diff --git a/common/src/sys/stats.rs b/common/src/sys/stats.rs index 6e668a98fc..b877a85759 100644 --- a/common/src/sys/stats.rs +++ b/common/src/sys/stats.rs @@ -5,12 +5,9 @@ use crate::{ }; use specs::{Entities, Join, Read, ReadStorage, System, WriteStorage}; -const ENERGY_REGEN_ACCEL: i32 = 1; -const BLOCK_COST: i32 = 50; -const ROLL_CHARGE_COST: i32 = 200; +const ENERGY_REGEN_ACCEL: f32 = 1.0; -/// This system kills players -/// and handles players levelling up +/// This system kills players, levels them up, and regenerates energy. pub struct Sys; impl<'a> System<'a> for Sys { type SystemData = ( @@ -75,25 +72,15 @@ impl<'a> System<'a> for Sys { .set_to(stat.health.maximum(), HealthSource::LevelUp) } - // Recharge energy if not wielding, and accelerate. + // Accelerate recharging energy if not wielding. match character_state.action { - ActionState::Wield { .. } | ActionState::Attack { .. } => energy.regen_rate = 0, - ActionState::Block { .. } => { - energy.change_by(framerate_dt(-BLOCK_COST, dt.0), EnergySource::CastSpell) - } - ActionState::Roll { .. } | ActionState::Charge { .. } => energy.change_by( - framerate_dt(-ROLL_CHARGE_COST, dt.0), - EnergySource::CastSpell, - ), ActionState::Idle => { - energy.regen_rate += ENERGY_REGEN_ACCEL; - energy.change_by(energy.regen_rate, EnergySource::Regen); + energy.regen_rate += ENERGY_REGEN_ACCEL * dt.0; + energy.change_by(energy.regen_rate as i32, EnergySource::Regen); } + // All other states do not regen and set the rate back to zero. + _ => energy.regen_rate = 0.0, } } } } -/// Convience method to scale an integer by dt -fn framerate_dt(a: i32, dt: f32) -> i32 { - (a as f32 * (dt)) as i32 -} From 263fffb79cd8e05dcae91d2669ee80afb55bbd88 Mon Sep 17 00:00:00 2001 From: timokoesters <timo@koesters.xyz> Date: Fri, 17 Jan 2020 21:08:27 +0100 Subject: [PATCH 03/12] improvement: better movement --- common/src/state.rs | 2 +- common/src/sys/controller.rs | 2 +- common/src/sys/movement.rs | 15 +++++---------- common/src/sys/phys.rs | 5 +++-- common/src/sys/stats.rs | 2 +- server/src/lib.rs | 4 ++-- 6 files changed, 13 insertions(+), 17 deletions(-) diff --git a/common/src/state.rs b/common/src/state.rs index a111a5f1fa..4bbaa5d3b4 100644 --- a/common/src/state.rs +++ b/common/src/state.rs @@ -39,7 +39,7 @@ pub struct DeltaTime(pub f32); /// upper limit. If delta time exceeds this value, the game's physics will begin to produce time /// lag. Ideally, we'd avoid such a situation. const MAX_DELTA_TIME: f32 = 1.0; -const HUMANOID_JUMP_ACCEL: f32 = 16.0; +const HUMANOID_JUMP_ACCEL: f32 = 26.0; #[derive(Default)] pub struct BlockChange { diff --git a/common/src/sys/controller.rs b/common/src/sys/controller.rs index b5b48aa2fa..86e468b45c 100644 --- a/common/src/sys/controller.rs +++ b/common/src/sys/controller.rs @@ -16,7 +16,7 @@ use specs::{ use std::time::Duration; use vek::*; -const CHARGE_COST: i32 = 50; +const CHARGE_COST: i32 = 200; /// # Controller System /// #### Responsible for validating controller inputs and setting new Character States diff --git a/common/src/sys/movement.rs b/common/src/sys/movement.rs index 5a96f68496..e7c93e3bbf 100644 --- a/common/src/sys/movement.rs +++ b/common/src/sys/movement.rs @@ -15,13 +15,13 @@ use vek::*; pub const ROLL_DURATION: Duration = Duration::from_millis(600); -const HUMANOID_ACCEL: f32 = 50.0; +const HUMANOID_ACCEL: f32 = 100.0; const HUMANOID_SPEED: f32 = 120.0; -const HUMANOID_AIR_ACCEL: f32 = 10.0; +const HUMANOID_AIR_ACCEL: f32 = 15.0; const HUMANOID_AIR_SPEED: f32 = 100.0; const HUMANOID_WATER_ACCEL: f32 = 70.0; const HUMANOID_WATER_SPEED: f32 = 120.0; -const HUMANOID_CLIMB_ACCEL: f32 = 5.0; +const HUMANOID_CLIMB_ACCEL: f32 = 10.0; const ROLL_SPEED: f32 = 17.0; const CHARGE_SPEED: f32 = 20.0; const GLIDE_ACCEL: f32 = 15.0; @@ -221,14 +221,9 @@ impl<'a> System<'a> for Sys { if inputs.climb_down.is_pressed() && !inputs.climb.is_pressed() { vel.0 -= dt.0 * vel.0.map(|e| e.abs().powf(1.5) * e.signum() * 6.0); } else if inputs.climb.is_pressed() && !inputs.climb_down.is_pressed() { - vel.0.z = (vel.0.z + dt.0 * GRAVITY * 1.25).min(CLIMB_SPEED); + vel.0.z = (vel.0.z + dt.0 * GRAVITY * 1.25).min(CLIMB_SPEED).max(0.0); } else { - vel.0.z = vel.0.z + dt.0 * GRAVITY * 1.5; - vel.0 = Lerp::lerp( - vel.0, - Vec3::zero(), - 30.0 * dt.0 / (1.0 - vel.0.z.min(0.0) * 5.0), - ); + vel.0.z = (vel.0.z - dt.0 * GRAVITY * 0.01).min(CLIMB_SPEED); } } diff --git a/common/src/sys/phys.rs b/common/src/sys/phys.rs index 955d229c22..e974fca9e4 100644 --- a/common/src/sys/phys.rs +++ b/common/src/sys/phys.rs @@ -11,14 +11,14 @@ use { vek::*, }; -pub const GRAVITY: f32 = 9.81 * 4.0; +pub const GRAVITY: f32 = 9.81 * 7.0; const BOUYANCY: f32 = 0.0; // Friction values used for linear damping. They are unitless quantities. The // value of these quantities must be between zero and one. They represent the // amount an object will slow down within 1/60th of a second. Eg. if the frction // is 0.01, and the speed is 1.0, then after 1/60th of a second the speed will // be 0.99. after 1 second the speed will be 0.54, which is 0.99 ^ 60. -const FRIC_GROUND: f32 = 0.08; +const FRIC_GROUND: f32 = 0.15; const FRIC_AIR: f32 = 0.0125; const FRIC_FLUID: f32 = 0.2; @@ -275,6 +275,7 @@ impl<'a> System<'a> for Sys { { // ...block-hop! pos.0.z = (pos.0.z + 0.1).ceil(); + vel.0.z = 0.0; on_ground = true; break; } else { diff --git a/common/src/sys/stats.rs b/common/src/sys/stats.rs index b877a85759..b013a12171 100644 --- a/common/src/sys/stats.rs +++ b/common/src/sys/stats.rs @@ -5,7 +5,7 @@ use crate::{ }; use specs::{Entities, Join, Read, ReadStorage, System, WriteStorage}; -const ENERGY_REGEN_ACCEL: f32 = 1.0; +const ENERGY_REGEN_ACCEL: f32 = 0.5; /// This system kills players, levels them up, and regenerates energy. pub struct Sys; diff --git a/server/src/lib.rs b/server/src/lib.rs index 6bf0557244..797e0f52f8 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -258,7 +258,7 @@ impl Server { state.write_component(entity, body); state.write_component(entity, comp::Stats::new(name, main)); - state.write_component(entity, comp::Energy::new(200)); + state.write_component(entity, comp::Energy::new(1000)); state.write_component(entity, comp::Controller::default()); state.write_component(entity, comp::Pos(spawn_point)); state.write_component(entity, comp::Vel(Vec3::zero())); @@ -1185,7 +1185,7 @@ impl StateExt for State { .with(comp::Controller::default()) .with(body) .with(stats) - .with(comp::Energy::new(100)) + .with(comp::Energy::new(500)) .with(comp::Gravity(1.0)) .with(comp::CharacterState::default()) } From cd774780c4c7943ee8195d06b0d314d7904e987c Mon Sep 17 00:00:00 2001 From: Pfauenauge90 <44173739+Pfauenauge90@users.noreply.github.com> Date: Fri, 17 Jan 2020 23:16:50 +0100 Subject: [PATCH 04/12] darkened button for unavailabe charge --- voxygen/src/hud/skillbar.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/voxygen/src/hud/skillbar.rs b/voxygen/src/hud/skillbar.rs index 2422c8b3d5..2048ca37cf 100644 --- a/voxygen/src/hud/skillbar.rs +++ b/voxygen/src/hud/skillbar.rs @@ -733,7 +733,11 @@ impl<'a> Widget for Skillbar<'a> { // TODO: Changeable slot image Image::new(self.imgs.charge) .w_h(18.0 * scale, 18.0 * scale) - //.color(Some(BG_COLOR)) + .color(if self.energy.current() as f64 >= 200.0 { + Some(Color::Rgba(1.0, 1.0, 1.0, 1.0)) + } else { + Some(Color::Rgba(0.4, 0.4, 0.4, 1.0)) + }) .middle_of(state.ids.slot1_bg) .set(state.ids.slot1_icon, ui); // Slot 6 @@ -915,8 +919,8 @@ impl<'a> Widget for Skillbar<'a> { .set(state.ids.health_text, ui); let energy_text = format!( "{}/{}", - self.energy.current() as u32, - self.energy.maximum() as u32 + self.energy.current() as u32 / 10, + self.energy.maximum() as u32 / 10 ); Text::new(&energy_text) .mid_top_with_margin_on(state.ids.energybar_bg, 6.0 * scale) From bf3a735e41b2fd155436c710a6416562926b752c Mon Sep 17 00:00:00 2001 From: Pfauenauge90 <44173739+Pfauenauge90@users.noreply.github.com> Date: Fri, 17 Jan 2020 23:47:59 +0100 Subject: [PATCH 05/12] skillbar background fix --- voxygen/src/hud/skillbar.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/voxygen/src/hud/skillbar.rs b/voxygen/src/hud/skillbar.rs index 2048ca37cf..76cf9a7dba 100644 --- a/voxygen/src/hud/skillbar.rs +++ b/voxygen/src/hud/skillbar.rs @@ -550,7 +550,7 @@ impl<'a> Widget for Skillbar<'a> { // M1 Slot Image::new(self.imgs.skillbar_slot_big_bg) - .w_h(36.0 * scale, 36.0 * scale) + .w_h(38.0 * scale, 38.0 * scale) .color(match self.stats.equipment.main.as_ref().map(|i| &i.kind) { Some(ItemKind::Tool { kind, .. }) => match kind { Tool::Bow => Some(BG_COLOR_2), @@ -615,7 +615,7 @@ impl<'a> Widget for Skillbar<'a> { } Image::new(self.imgs.skillbar_slot_big_bg) - .w_h(36.0 * scale, 36.0 * scale) + .w_h(38.0 * scale, 38.0 * scale) .color(match self.stats.equipment.main.as_ref().map(|i| &i.kind) { Some(ItemKind::Tool { kind, .. }) => match kind { Tool::Bow => Some(BG_COLOR_2), @@ -726,7 +726,7 @@ impl<'a> Widget for Skillbar<'a> { } } Image::new(self.imgs.skillbar_slot_bg) - .w_h(19.0 * scale, 19.0 * scale) + .w_h(19.5 * scale, 19.5 * scale) .color(Some(BG_COLOR)) .middle_of(state.ids.slot1) .set(state.ids.slot1_bg, ui); From 7cb69082ec6ab5d3dcf048501da472f2bd7269ce Mon Sep 17 00:00:00 2001 From: Pfauenauge90 <44173739+Pfauenauge90@users.noreply.github.com> Date: Sat, 18 Jan 2020 02:10:12 +0100 Subject: [PATCH 06/12] various fixes --- assets/voxygen/element/icons/staff_m2.vox | Bin 60360 -> 60360 bytes voxygen/src/hud/mod.rs | 7 +- voxygen/src/hud/skillbar.rs | 96 ++++++++++++++++------ 3 files changed, 76 insertions(+), 27 deletions(-) diff --git a/assets/voxygen/element/icons/staff_m2.vox b/assets/voxygen/element/icons/staff_m2.vox index 789fc430596d8798cab6b0e1629138f19c0bde4e..0ee506012a49bec997e422a4beac5cf728c08bff 100644 GIT binary patch delta 4828 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 L{_}sufB*A;#V{b} delta 4828 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 H?|=ReHMSI% diff --git a/voxygen/src/hud/mod.rs b/voxygen/src/hud/mod.rs index e79965efd7..2b5eb02237 100644 --- a/voxygen/src/hud/mod.rs +++ b/voxygen/src/hud/mod.rs @@ -544,7 +544,7 @@ impl Hud { let own_level = stats .get(client.entity()) .map_or(0, |stats| stats.level.level()); - + //self.input = client.read_storage::<comp::ControllerInputs>(); if let Some(stats) = stats.get(me) { // Hurt Frame let hp_percentage = @@ -1653,10 +1653,12 @@ impl Hud { let energy = ecs.read_storage::<comp::Energy>(); let character_state = ecs.read_storage::<comp::CharacterState>(); let entity = client.entity(); - if let (Some(stats), Some(energy), Some(character_state)) = ( + let controller = ecs.read_storage::<comp::Controller>(); + if let (Some(stats), Some(energy), Some(character_state), Some(controller)) = ( stats.get(entity), energy.get(entity), character_state.get(entity), + controller.get(entity).map(|c| &c.inputs), ) { Skillbar::new( global_state, @@ -1666,6 +1668,7 @@ impl Hud { &energy, &character_state, self.pulse, + &controller, ) .set(self.ids.skillbar, ui_widgets); } diff --git a/voxygen/src/hud/skillbar.rs b/voxygen/src/hud/skillbar.rs index 76cf9a7dba..c11c13d236 100644 --- a/voxygen/src/hud/skillbar.rs +++ b/voxygen/src/hud/skillbar.rs @@ -3,7 +3,9 @@ use super::{ /*FOCUS_COLOR, RAGE_COLOR,*/ HP_COLOR, LOW_HP_COLOR, MANA_COLOR, TEXT_COLOR, XP_COLOR, }; use crate::GlobalState; -use common::comp::{item::Debug, item::Tool, ActionState, CharacterState, Energy, ItemKind, Stats}; +use common::comp::{ + item::Debug, item::Tool, ActionState, CharacterState, ControllerInputs, Energy, ItemKind, Stats, +}; use conrod_core::{ color, widget::{self, Button, Image, Rectangle, Text}, @@ -103,6 +105,7 @@ pub struct Skillbar<'a> { stats: &'a Stats, energy: &'a Energy, character_state: &'a CharacterState, + controller: &'a ControllerInputs, pulse: f32, #[conrod(common_builder)] common: widget::CommonBuilder, @@ -118,6 +121,7 @@ impl<'a> Skillbar<'a> { energy: &'a Energy, character_state: &'a CharacterState, pulse: f32, + controller: &'a ControllerInputs, ) -> Self { Self { imgs, @@ -129,6 +133,7 @@ impl<'a> Skillbar<'a> { common: widget::CommonBuilder::default(), character_state, pulse, + controller, } } } @@ -526,17 +531,32 @@ impl<'a> Widget for Skillbar<'a> { match self.character_state.action { ActionState::Attack { .. } => { - let fade_pulse = (self.pulse * 4.0/*speed factor*/).cos() * 0.5 + 0.6; //Animation timer; - Image::new(self.imgs.skillbar_slot_big) - .w_h(40.0 * scale, 40.0 * scale) - .top_left_with_margins_on(state.ids.hotbar_align, -40.0 * scale, 0.0) - .set(state.ids.m1_slot, ui); - Image::new(self.imgs.skillbar_slot_big_act) - .w_h(40.0 * scale, 40.0 * scale) - .middle_of(state.ids.m1_slot) - .color(Some(Color::Rgba(1.0, 1.0, 1.0, fade_pulse))) - .floating(true) - .set(state.ids.m1_slot_act, ui); + if self.controller.primary.is_pressed() { + let fade_pulse = (self.pulse * 4.0/*speed factor*/).cos() * 0.5 + 0.6; //Animation timer; + Image::new(self.imgs.skillbar_slot_big) + .w_h(40.0 * scale, 40.0 * scale) + .top_left_with_margins_on( + state.ids.hotbar_align, + -40.0 * scale, + 0.0, + ) + .set(state.ids.m1_slot, ui); + Image::new(self.imgs.skillbar_slot_big_act) + .w_h(40.0 * scale, 40.0 * scale) + .middle_of(state.ids.m1_slot) + .color(Some(Color::Rgba(1.0, 1.0, 1.0, fade_pulse))) + .floating(true) + .set(state.ids.m1_slot_act, ui); + } else { + Image::new(self.imgs.skillbar_slot_big) + .w_h(40.0 * scale, 40.0 * scale) + .top_left_with_margins_on( + state.ids.hotbar_align, + -40.0 * scale, + 0.0, + ) + .set(state.ids.m1_slot, ui); + } } _ => { Image::new(self.imgs.skillbar_slot_big) @@ -548,7 +568,6 @@ impl<'a> Widget for Skillbar<'a> { } } // M1 Slot - Image::new(self.imgs.skillbar_slot_big_bg) .w_h(38.0 * scale, 38.0 * scale) .color(match self.stats.equipment.main.as_ref().map(|i| &i.kind) { @@ -576,7 +595,7 @@ impl<'a> Widget for Skillbar<'a> { .w(match self.stats.equipment.main.as_ref().map(|i| &i.kind) { Some(ItemKind::Tool { kind, .. }) => match kind { Tool::Bow => 30.0 * scale, - Tool::Staff => 30.0 * scale, + Tool::Staff => 32.0 * scale, _ => 38.0 * scale, }, _ => 38.0 * scale, @@ -584,7 +603,7 @@ impl<'a> Widget for Skillbar<'a> { .h(match self.stats.equipment.main.as_ref().map(|i| &i.kind) { Some(ItemKind::Tool { kind, .. }) => match kind { Tool::Bow => 30.0 * scale, - Tool::Staff => 36.0 * scale, + Tool::Staff => 32.0 * scale, _ => 38.0 * scale, }, _ => 38.0 * scale, @@ -595,16 +614,43 @@ impl<'a> Widget for Skillbar<'a> { match self.character_state.action { ActionState::Block { .. } => { let fade_pulse = (self.pulse * 4.0/*speed factor*/).cos() * 0.5 + 0.6; //Animation timer; - Image::new(self.imgs.skillbar_slot_big) - .w_h(40.0 * scale, 40.0 * scale) - .right_from(state.ids.m1_slot, 0.0) - .set(state.ids.m2_slot, ui); - Image::new(self.imgs.skillbar_slot_big_act) - .w_h(40.0 * scale, 40.0 * scale) - .middle_of(state.ids.m2_slot) - .color(Some(Color::Rgba(1.0, 1.0, 1.0, fade_pulse))) - .floating(true) - .set(state.ids.m2_slot_act, ui); + if self.controller.secondary.is_pressed() { + Image::new(self.imgs.skillbar_slot_big) + .w_h(40.0 * scale, 40.0 * scale) + .right_from(state.ids.m1_slot, 0.0) + .set(state.ids.m2_slot, ui); + Image::new(self.imgs.skillbar_slot_big_act) + .w_h(40.0 * scale, 40.0 * scale) + .middle_of(state.ids.m2_slot) + .color(Some(Color::Rgba(1.0, 1.0, 1.0, fade_pulse))) + .floating(true) + .set(state.ids.m2_slot_act, ui); + } else { + Image::new(self.imgs.skillbar_slot_big) + .w_h(40.0 * scale, 40.0 * scale) + .right_from(state.ids.m1_slot, 0.0) + .set(state.ids.m2_slot, ui); + } + } + ActionState::Attack { .. } => { + let fade_pulse = (self.pulse * 4.0/*speed factor*/).cos() * 0.5 + 0.6; //Animation timer; + if self.controller.secondary.is_pressed() { + Image::new(self.imgs.skillbar_slot_big) + .w_h(40.0 * scale, 40.0 * scale) + .right_from(state.ids.m1_slot, 0.0) + .set(state.ids.m2_slot, ui); + Image::new(self.imgs.skillbar_slot_big_act) + .w_h(40.0 * scale, 40.0 * scale) + .middle_of(state.ids.m2_slot) + .color(Some(Color::Rgba(1.0, 1.0, 1.0, fade_pulse))) + .floating(true) + .set(state.ids.m2_slot_act, ui); + } else { + Image::new(self.imgs.skillbar_slot_big) + .w_h(40.0 * scale, 40.0 * scale) + .right_from(state.ids.m1_slot, 0.0) + .set(state.ids.m2_slot, ui); + } } _ => { Image::new(self.imgs.skillbar_slot_big) From e1c640d5b66f3dbabae3130bd2ed29d3f98957ac Mon Sep 17 00:00:00 2001 From: Pfauenauge90 <44173739+Pfauenauge90@users.noreply.github.com> Date: Sat, 18 Jan 2020 02:26:00 +0100 Subject: [PATCH 07/12] charge icon update --- .../voxygen/element/icons/skill_charge_3.vox | Bin 57509 -> 63217 bytes voxygen/src/hud/img_ids.rs | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/voxygen/element/icons/skill_charge_3.vox b/assets/voxygen/element/icons/skill_charge_3.vox index ddbc3c07177d753f0f71067441011c90137ea232..4f38d39a080247e0d29c6887f63ebd6139946ffe 100644 GIT binary patch literal 63217 zcmds=d7LC?dEcwwx9_|9sH(2+uCBh2Rx7P$_K;RWf)yapp3)x3njOuxyR);iJ3TX* zU7Z#XBtnp9=aOYeY$-C1ZJZDrLXd)u9gH0yj$>jGY?A~`hz%ij5{-`#oa^<sYF4ZB z^YQ=hh<EEfp67SG?^|zG_pHRdci-oK)G&<0ckDmH#rW0_4CDCzlXqr=20w41M6X=$ z8#%fEbIqXFFa`<3$jR86A$yxyt~nW{#8>;Ydz3CEBV+4X8KuM-lj}K|Sn1H;rudW= z#iOK&%Mh0#E=yciCRTG&5T9JhipHQxOoLJnul-FotoJul<fX|=lb0bcBNOBc^d!lT zZ02NYB_rzdY0;R^3U|Ino-6yC4%Zr2o2$b$2~G-}l#GKV(USmApg*~q1t$wmP8#5) zS8~E#$cfs#MXpV5m8-?I!nMk^%(cR`#5DnKQbxg#gDugUSWAPOmPzm<;HSVhzz3I{ zoXlJ<b2Z5=h1dROMl=?(q7G+$kdz*n&CR%MY{q1LGfzxZy0l~TM=5dmBzy_@1bj*O zBJic)GvG_Z*M~0yUk|?QWhrZ$LONh|D6Nfx^ez`=^Rg)$mkYAJQItOQ*5wlY1#-)b z<wK8+%Q1N4@W$ayz?*<K32zeK6uc>T)9|L@&5)acH%o37-ki)`PRSNyKK*U_I_T+c zMu?BdG;`TlsmSI^S$Zoa*;*-*Z%TiqKz<Qj1>&Rd$Ka32I2t4+fewL=<XRH`B>XAx zQZkK>K3o}e1p6|JjvN~Dmvf>ro`*Lls$+t=$cpNCPFSN^VR5z3XN5hM5%zdiIHMWi zjHQJ$UIarj?-7}zM5xEm5~ak^A}NXWI6C4oiJl00Qs^<zlSWUlZ!@cDFw*GA$Q)YG zAq&eHQ5#K*8XWFuO1NVwQ6J6WGbtLhn+ti-Ihqsgqgmk}%ZS$Tr0i@6>1||%cRb4+ zW|+$~b1Kjy(Ie3lMNd>F&?G2HbVbmWT1%iOfu1CKk}`v?9=fvV%AqNLIV%fjDsJRN z>sUfKM`FT0B87D%A?lL};Z7z+Z89yq*{o=u5W?9MqP8hT?XVCoSC?yjGb-FeDfVoV zJ)2<93N#5cNi<0`MbQ*RQw&WpnL?X^wlv!M#AndfBR7k-9J=!8Dxj;loRj8;6!pU~ z;T(>N`XMPA`=jWKiTXh)Z2B69gs?W_!rLDgjROhMd~b|;Of(KgMU%efftY9>jB^jg zSjT914@k5n&?P9z^(dO6Xo{gJCevu^qb-BB;J(hHEw`FNQyNVfSwxqKt`hsG%O2`- z-v-yt(R5hv98C)EL_)OSt{sjG_dtsEONZnB(Tr#xONtib{{EEc97_rRxa6J`(&nCQ za}|^%x`MryLYslMG}`)T%dExG6h~7+=FpW#R{>o`beZTXZRBKmGYv;f)D9-Wjxukl zkh8tNfQAAZ6QXt`gT^fDonwE+MV&cvnXel2>#&|^AubohOP_P0#kt^J7HAUO2NF#Z zO;I#OWd?0Mv}MtjLsxz^iKdh+qRT{A30-A$RhXkTd)YshhBw82O0&;1qB)SlyGM%F zJ$=c#i1s}~bXZ%TJ=jK5i#7HS#zgB#RCw&k_K`SaanU|p;(RJ|c12lRfhNIOBGDw# z6qUV=D4JquipxB@3g{}L%e<UMQ%07TvuMi6>Sj!|4@5=hfD~PLI_$6R!8q51Xt4L| z<4NI;r$mk7a39!{8DUYXlUY%j%!x9kG#O*h$HToJ+ygD{ui*X+?$Zvs`jia1dK*$^ z(UwD39$kgige;=VL{|x2Wpq_GvS`Xli*vHed~}(M4*J@xk<Wf=F$dm>B==%UG)B^* zK9UjcNLJKFa>5yjz!ibZ;!KUm?uHSbvz?7TR|72(&eJMqZH2S8%vmeYBxDwCIdtXG zRX|q}UFJ$sme5s3S7kYarYxFr?9(1teXtDtMey0B-J{*7ZP1S3ze8V_z8-yj`V9Ia zoDXgKJM?$y@6q3<-=IIj-1&@^IQLE2<{1#2cR6(B*P>{O$s)Q;bd}Im=6O;<S9K#3 z&XbKU2kZ{mZLoc?TVQ)&H^FZ3{?P@$Lrj~PKy!;2kC-Mg4PxrNpLB`q5Z5NoC$2@D zM_iM*#%6)>2=`_ryhrnBE1<22Hgh#DOK2;jt%9~H+AOr$%$b9>8YP%3ce4qG2S<yx z4@Vo04jf%LdT?av%g`4gr$KC!SdZ8i{XYF|VmrikiA_^aQIEh;CpJl60v~aBge;)1 zh&~g2CG?e75@<`JEhVkxG}<!cWkYQ)TphUDaQSex;0o5N30Ls`QR6;$;IVn9>cZ6_ zzD>MOe2aLG_$KiUIBmEsVlCdgy2N*gZxinm-{Oq(d7ig8Q-brPNW6*0(rQeW(O5xa z6^#}e?Ts{?8Mw3f$%X6V!4>S22JZ(=-cuTvBkW57k3*@!<-%2ms{vONT^{i*;sqEI zj0pF<Bvue>hj|_H+T{7<waD|xYvMQX9lU>e#G2%k&{;-jWhE}F=(NyjZ=}$eMq>v2 zEciLu*zm|rfRzL@!g(BpN5UuIv8da!v+l_DdQJN4u57K>rMKRIt4Uspm^85wo-Z-( z-zarSpJ3jq<XW<`X3O@PBmK3SY^}M{<9#pKmp$gTu`I}w=#0t=TB{pz_F)2@N$H@q z2DXdVI$9fOZEn<M6Yj=(gfqYn&kPgK-Hn*!9E4ZmL9kvGII6O<YRUGhE&WwTwpMG> zTXkh~tu7mD7H3M8GbO^BCdiMIA0t0XzGN=qyjPULsBq?#!*{#Ja-KC5%#Y;Ei=s7# z);L-d%xMy>DYT~1nn7!p`N{GA6XAWQf{rRWEOglDaL`ecO|&#tOPn1hXGetnpM)<# zJ&ukTI-=;1;d(YXXS%%Kb~p#y==N7EblK>4@KIwPij0eJza*Gj$$Mz<jvTxXdmAF$ zLsc|eXttRz2c8;yE}H9TZlJk|W)IE5y&R>)DS5`bJX_mG6SBQOj;0vnQQ{<PA;>cs zFQKc9t_r%U=(5mdqbo<egSIR=8FC_=k7?pl?2#lM6O6|hOT!&wJOy5qy5uUjPbBwb z6umL@#?hNVZxX$E8CxyL*h-$yb$J=bdyIGQD1CAIBz+0`qN_#P1=<DLc`4TOd}b@s zF3JRRBj`^uKN0X!m-C{=JFGKb5YA$eu_9vy8Cfp`V?3+#GJUzoSV7qH#jsd|BI8BI z3yc>S&+{26FWf;v)CNULN!EGZyL@IbSda8_AsFZLMM0S3MNt|ph|*Y5lt=TTG+q#8 z>Xq|(QKnv@uQFB;)$>J};rz+6_i|uY>9^?1(wE!F%lvXt6nN*)!JnsH;GD`+FQB&w zXMuW=x=FoA-K1WkZc;B%FH<j3FH^6u&gNuZ6ekO!Fj*A&$s+xQpr5rUiX3D1NI_U5 zMNu6o(qEuI&l(lE7P#hFt0LC|*F0-xuvUGF!MgRry34rDc<`C7#xtQ#zss3jryh(s zjMW%(Ih*VByPTbM>cLozF?W!c^^F#NK7Ah7RygJ|AN7r<tgn00<$S2GHKn^|N_V*^ z>&tanUu{U2`>noO=USrQWNnI~I98MO6<4||1yN*8%rS>{jdq1~D-&bWc4${=S7=+b zZQ2&?Ds7X!SE86QN{N#b<D8Gu7iUbOAwgbjqfB3!z7l;U`b_qi$sVhQ=R*{}I5Co# z#72p+l1y%x>@QQMmMvncGRk=x=PZ%LCO8YkTA62iiI|d15o>TSrZ;S2E$&~7*ebDA zVk^W}h%FOa=6$$CY>9VIllLx%c$;{ecuUF+i}<P(<R!_AtXGIH6JI92M7+tlTO-FI z$05h&tg*?l$g#+&%E(%koC-M=a?0eC$T8WYE_pTbYUDZOIpo<=pgBo?gflrszClTo zn;|z#ZjRhM&$<}-QA(VANlB0|C`m9PJY!N|7?j}id4~Kf`8o3Q<QI5O#=wYD;$TQh z0t`V(f)QbjQ{WhsG#D8$vS8%E$b(S;qsa3v22PX`2S-v8;0Q_*oCy0S1(rccgA=^d zX2HpUlLw~&P7xfFeH#NSN{NFdDG9IyB?(r9{g?vJprpadfRzO+2UZ@e0$4?`Ot4Dq z{}^~tN*p{%Nq{FPN$?^ob+BBpT(D|jIbhjfSzuMcs(@7ns|1$G{qBR;0<Q(02c8FB z6TBvP4e%P^)xoQS=Ym%Q&jHT{&jPOsUIn}|cqQ;m&WSddKA1k3EihYPdSH5BHo<Iy z*#NTvW*tly%o>;um^PRem{l+<V3xrwfoXE)bii$c+XmMM*9W%+ZVOxwTo2qPxJ_^y z;MT!)!L5PofNO(mfm;Q)0&W@H61XO3SQqRL*d4IjV7I~c!S=y!f!zYz1KR_;$!C}* z_zm#u;Je`0z<0p6!MDJ#f?ol@41NiGlXI}gxzUBAE8~L>9342?aJ1p@;qc*T!O?=l zgTsTP2}c8tIvg$>H8>nNY&a}9s&G``D8o^L!{kiu<E6*>)8l#AmGVMYCg2nBCE<#! zwBhmL@!@H~(}Kr?rwLC3o;o}(JT-V6cx-qqc&hMJ;3-R+XPv_tRNFA%>f@=;y!G(Z z<JsGVtIK)TfvY1^gEm}kxO}*LxLR;|a5dp-z*UFKg{uaa1D6e#C98uf?=KbJx5{vp zWNq1i$AG6M%*8%)*Mq0W_iJ5vx}1p}cseo-Umv~<Tv@nsaOL3&&V(XdCR`=B$^(b@ zI-B=83!bX9mn+g)E{8l4p2Vgptoc53)&svMQ|n#uyL>0q0l&j@y3KRh2fr=kSVbhq zU6C58iR4LBn4^wJoGgjb`IZplmPn2I#8gFctS(aLU1DmCo1!#g;K6{S&wELqb?L#; zlWDm6aAn}iuC{sh`#kSkJnuce2W#@3Q-kkP>U_?0`OH+~Gl|3dzAbH@R}RmZ+GPVg z1H3+XedfLgUQcGg&4QT&Gr#KdeNl_=usptVX!7~K0bU(E7rYvH4tO?rR)}YSmj<g( z$$*swD+gA7t<CoYKHu@Q_)PBc`M1euxdxv@>fpHG)WC7Tu|u3bI2kaqVC2BauXp&I z+U7I8&*#(@`5yUA@*8}PsY{FV(dMjmIJ0Xj26=tvyvLe$Wr5rxx#pVBXS5cdD?IX= z<Tc2vljo9G3-fa1<;g3MS0v9|Z}XYSC#S_{5l>cEo8&aesgvV|IeBsl<P^y<S@RM( zWpXNOE#f_Ckz@0&;gAz(D-d5K-Xy+6e3|&lx-YB5TWg-Qc~5bOudN!y^@-~d*JaOl z*z;{!CDvMRNqgOs4zV?Ur(qITBCbqag}5ql7JJ)fZ#%@*)(yt{jQ1GtGTvdl&A885 z(BeJHV64wrkFhRe9md*>`QccFu_|N18E7*Wd`}VNROz?qx9NB25Be<nf^*EFFKFAe z9oj)XICp|;ZPOF^#jX$|9U({BB0A!W*hovnM?8@jX^P}XgZrc|(j%_OaKB_n9FZHb zMSjE*g^{W#j#Pv>Qs&+$iSmdkDkD81W_v=;c13i)D`N8<5vL?5$@#WOQPPymyf3l? zU*rakC=6<%Fkc4C6@>*?<mc<6xaf-FLXF~bewe~uY>2{Qo$<O5lP;x3aVT|>UuaMq zA;%pN9dkr%)E4nETO_z9Pue0yNuRVthLWY^@RwWkMDC?#m{**)MPbeo(aEZaQR0&o zk(extBqcRj66r}(WGGqqlH{jPc7<Fli}ZL&6h=!TGj59fc!l|>h|Fk}xv9ca76nRX ztR(W3EHOD^@+T`IOH7Wj{AgKZ8Ow1V1??PqbLXq*w3%m1<j*@IOS^F1rmrH>c+0?1 zJYN%;^QI`!PIGP*Pj)!B%EF{S4R@OJEJb{3yiLC=(!>@=YxI>^t0r^riOfq4u1(gW zDY6SK+8%XJWM8U_%%CCClq_SZL6dp%MP{)j(l50{mU&CDK3T?d3%*FxpIY!la^TWe z7dh57MO<>;Wo><tq@G}Xb8~HxT=YePHO&v&A^~QcHOtc<Uu=umVkaz7V#Ih=q&Rz{ zV+|1<b-C7A+dAvoq}||bZipCXa&*k4-=)7U;wL@Env69WYk*-3IoY7UMocZ-6H#L0 zC+nQ$En;mE8}o?wh;I^aiP(6Pecxc;*NAh8vqT);B=<ygv_-5X5@RjmO;JAYk>iQ@ z`KCy)Mv3u;NS?PulDUtMwm7>y&X=Yv4jR%tU+4UH`QEz5Gt80IWt;EJEY8^~=WK;@ zw#A%y+y_nectaGKN0a$1F>fXAkMh74m4QX6it3;utU;Ml6869p4(&4i<%O~+&s9W; zQdzJ>d9g}vRaDrgmHCROF52{2qBLI>=3Gfs=E`ATiC7b?>bxUL#F+DzD9)Mil!UeD z2y?+kPoL-B{E1^n8e6}@z4~W_n!556)PtY-_Z+*YxK%Y??hCFW7z5+*m9d~5^v5U@ z^HYQA3YCi&X6NQkO)X7aoLjgwacXvE;?#_!Kj7dJCcF$baVbvE&HiqG=S}m+@46?* z4}OC9;OAxXpnm9%kz;O9x$^TG;-E1UFuXb{=zK%e@av=AU?ND_`TD3p(HpRKy*?`V zW%(PTcE3I<n65WO?RkAvFf(t6y6N>%{3ia5Q8x!s$KLafJHp@I6R~&iUiH{xkE!oG zWvIKJ{gUcdeyIMkQc++0;^81v{o?-T)scfkYHIAQ>ahn8s^_2FubzKuN<IIvMRoJ@ z3#z!!Qng86`S0JZwmo-?GVY8i<3d^)A1^86OO`4gHk3UuRAb#x*MHhj$qR<cJz=Oa zZR?YU+I={q4h)Lw<Oj>@$6jcvcRgmPqfZ;^)Grz8k>4fvsH?o$j_N&jox16>`&50Y zqB<+Cy6)#%YWOSHDEodx^*#vpM-6q`7Yt>bfdA2?GM>&W<1-cY<Ta-H#86dz{r0+2 z2iodsw%ZGo-=};x*m{33u73F9N%d#HeXshHFCSNb^y_!4Z+`xe`n~7xQeXY_ooZj- zSGRRMwbyT`J)WzE8;;uHTIxEdqOP&Z>i(N{sQY$ZuTBhKr;grutvY<eThxK=J@xKw z9rey@+G^jeH>%rix?b(wxlQf9@hxg-dskh5t*@@_HC4Y|S2s|9bm8P{%IxmC`m68# ziu&4bKC5oLuA@#Iew#Wsd04&g(wKVcBLnq`pL(DA)laRc7a70xhHYWly{)fy^xEoL zzo|O)nrhfpWtB~}{-K6?e7B>%@mtraZ+&&UdS=Z~=ic{4RVX#oAFFZo;TwKZJ$lo} z)cn4mP^b4lrGDi2x;k|KirRN}S?yVTQmNZ-SKr^gTfMk_yZU^mqkh(P)v9T#hmuJ( z70lmVcd6ZbZ&kM(I<DR}Ij7!v>BH)-k36RiKl3?t{L^1k+q;&!ZD&gzeEYR(`rez> zg#(6qV$4v_T{6@c(9$yVs$Vg~d0cweE$ZIuEEV`)c~4(`cKk-QYu7Hdar1NP#^H~v zw?6r}sy$ItU-=nBeSOqWpS<>3wQburb*}rMI&{}(l<~x)s=8EFFMP~UAHUO3??<cW zdFliGr`6lu`jk4e{9)yc+v=;I>Z=z{*3?tJA9~z1_aQZW?gQ$^Ge4?sn0!!ezkf+> zyKkVbeeY~Ie#;|2u5N$)S#`(qFQ|7v{TX%O*<Vvfe*P<=zkU1msqZiiMv|g_pybyK z_3dBY()VrV=WiMh=IFxbzo9<#<v&!<y!c)9{I~v8{m%FQTz&5^ei+_=!5`eFVg1dn zcfa3Lht`HvVP8Rg^O^D+Vt4P`rrv%eJha|01|EOG=ft7k2rB#pXG-uFe8NNf(j$x0 zNg5MpX3w5|;No;}oJ>44Jv(#mQV=&B96}RI3lGek3l6}c9m8SP#l`8<TkSnzLeQRA zywVe#ffKU}OIyudVRPzI2sae;P0S1heK!p4*t69f-iqA2)p}^Fb#q8`cJ6_P107+; z#N3&su<>d)!Rf7!o4~t1Zh~dj$4#&y^>Gv2*!s8$P9c5V1P8J{Zc<vgNo(aMqm`TB zp3&#N$!X;#ua%pER&Ii?di1$(Os(9Mv~p9{%1!WXr9SsfRVz1^R&H#q+&EgfscGdV z_zjCbXIfn=H^Hy&^jVwW-zC+@ji;5HmR4?jt=zP=a?{buO;;;7J+0jIwQ}<ot=wFr zm78m|a<fe<H`i(9X1i8yuGh-V4O+R`p_Q8(wQ@70m78I$-0alK%`UCn?AFT79<AKm zq?MbSwQ{ppD>t`j<>pqc+`LsQH@9i!=60>zyiF@NZ`aDrKCRr`p_Q9=XyxXeTDf_b zR&MUp%FVmAa`PUo+}x#=oBdk3IiQuBgIc*cq?McCKXReZ-%ktv{~h#kbGKG*?$OH4 zQLWq@)5^_pt=yc@%FTPVa&xa%Zhk~7H}`4fW<)DDC$(}js+F7jwQ@72m78&`+)QZY zW>PCRQ(Cz>rInl0TDdu+m78g;+?>_Q&5TxV&S~XlRx3B>wQ_SoD>rjmxfy8XW?m~d z3tG8Z)XL4@(#p+}R&Fk8<>r!BZXVFe&4XIGc}Oca4{PP-5v|-js+F4`)ymCdTDf_j zR&L&}m75P}<>qf|<>rH0xp`bGH&1Bg=0jS!`8!&<`LI@Q{;pPTKBASIC$)0(V_Lac z*2>Lgt=z0=<z`hYH)~qCS=Y+VhE{H#(#p+`Yvtx?t=#;CR&GA3m79-g<>v2c<>v2e z<>n`~a`RJKxp_t_H_vM2<{xO~<~gn0{IphXKCYFUpV7+AKh(<2&uZo7=d^P3^IEz2 zgjQ~TK`S@^NGmu0SSvT5)XL32(aOy)YUSpav~u$)t=xQCD>wgCD>t9f%FRF1%FSoB za`U`aZeGyJ%|F-5&F8do^UGSf`4z3)d|oRzzp9m+U(?FXztGCf7qoKo>sq<_qE>Ex zLn}AGsg;|5sg;{AY31h2TDkd(R&M^4R&IVvD>wgID>uKbm79N~m7Cwu%FS1`a`SJs za`U@dxp`45H(%4r&A-#i&DXVZ^Ltvk`F*Y2{DD?(zM++yf3KCB|Dctd|EQIlZ))Y{ zTUxpKL#^C=TPrvJNh>$s(aOz#*2>KvY31gRwQ}=at=xQ1D>wf|D>vWQ%FTb(%FUl> z<>pVda`WG`a`WG{a`Qj5a`Tc_ZZ@@Y^JiMQ`E#w@{DoF-{!%M9|5GbBKhVm}|I*6M z54CdhS6aFG-&(o(KU%r@zgoHZe_FZu|5~|GTDdVIhLOclj52ZQk;Um`P&qSu_Ur=} zrxQW_q3PL~bC(D+hT}ndY2ksHbCTN7j^Qxt;^Oq_t@fTUA!tu5Ug?PjDYFYpTg_cz zbLvtEHx%?u%nSv6Hw^9Av(+5lirl-^dT6V4b4YY{?tzB`9bv}A+?l1Yv2^!44s_`a zegx&JlaOnA{=u+guyAJjYWQpzY#4!`*M`r|pA9f3X0PyujSCsNcJA5ZUn$`<OwG^C zO$W()hWtH4A<ER!^i)7Pv}0&X@R`|*7p{`t%t-JPAitLMf%)0LnKZC@LkRF@5VNP| zwjct!;n4Zg!qzMYi9dZMF<9K!a!*|vY_00lBNJz~Oz*zhaUo6o?mhnS(C)x{*gu$^ zer;gT@uo8r49slJ$KE|5>%y*yGvR!^+zRuChqh*Rcz7t#`mQTlgUqX1!(DlHVP@jx zx!S^c)8z>}XRglsn=W<;JU2amF{~^uEu0VI!-+e)@L&M3dryi#C#Dvsmo7c9G`(eH z=bli>skx~$(-ZSy&Z|0J*^o!Bm<Zsnng}arXXgfCW%}X8g#ch{wH}<^QXCFVou0n< zik_LNL9j48ckB$N=e{>X4p-*X+`_^jbTF8Gcxx%Qmh9r`skyDqAKsr+!40~#H-hz@ zxO8r5`r^5Txo}E{1CHCTaJ*)#hPgA`{qtu+y3-4D3rm6dy+eNZzmWRe%eQ(6bj=l@ zU_P%-Lx4JQYUUM?U}VVWpMd+;D=Dwx-kPv8Gt+O96m~r{d*;%WLn)l4u`4J+;#K0X zGP5-MD*ZuMNFA=?#l@-9v-9CXY@Kx>?AFQ;?F>V>r5ER>9tl?`ko))*qG0eU(H7YB zrAvUlYTClpzA7Sw2wT$_d!==1X5#cKk+i15vCw9?l0nX}&p%=E>FK$-F#N9pbof>G delta 2023 zcmXZcyNV>&8Nl)Ht2*~n=TfCWAhfVZLQZ@E2afGPhS6$Qtkx{@G&4*f3~pw6Hrli^ zW9aFN69vxo1p5RE^ahR`=>>#FPcZsxQNypQyUwZer<(5b(+}e}-;UdtpMUxIYY}<+ zm$PS_<Uikwyf{1mWcuX;uXix~dj9h7=V#acm#ZTGR}L!oiUA`GEF3%nA`&tt6jU^H zmF@k^o8|3?<-UksRng0ajv0&SQB(;jAtj`Ql#miqLV60S$B2Zix}Q)Ot7zz$vAjJm z_md1)MTUzi+hNsYSar-Yyb>J<4TJ_l1EGP?aB}cE9D@#rsKY)XV`99ZqM>8P!rUz5 z<s{>($ar4SWL$R{Z)X`dq9dL0e%NvtbvzkZ9ghxP$9+K5@h%}VXF@?mL&uDT`AN)W z7PFefoEKCv>n7%=iy>x+8DfTqGN%zn&C$ZCIc&WeV&*QY*(D}rOem;m=$Nr^QxtoZ z#9m~v=M#$9brpNl#BRD+zClaO60^kY(ZH%bIJJAP_AaR1MaC1d+AT3l%o4N2EHO*W z-YhH-#a$+Gt1OO~TNiORRoteDyY0Leb;KMocN}40)$N_SJCC4_m?P$hIa-dEqvdEh zT3biV9Ts(`ApR<fzewWGv-tHS{<?_YRPnb>JWu9{d19WJC*}`UJuy$r?*d~H2^kX# zDjGWTX6{>f?<HIX2^VoI;XFxLX9?GngiVofTP5&hfmk3Gh=qgEu(ulSoQ9p}dO$=% z#)N{3#+=UFnR^#Ldx@7p;wnm9CyCct;%1Wgsz|)qibg__P&|w<8VN;0kx(QQ2}MGY zP$U!yMM81cnA^E$)_7XD<|JKtNf$xVI!d}ul5Vpk9wyO9G!l)pH=6FOrk&HY<^P+A zBqE7OB9e%tU13sXazod2oVmV0B@@b*Uh+CfzKN1wCC;LmNG6i^qvksUtC>iqk!fTa znMS6OcN619x0y&Lk`G<;X=ZSN%5*=-^mLTznU(3%$+Yq^ZGuev4kkj3V}#LZXE{4~ z1VpYSWS#CN6rJ`JO{ZgLe8vLZN+DD}8>M_|q+D7lD<@^+rSL?B5aSqOVBz2qwD9XL zgbI5^AynA)3ZZgnT27rgGZqX|X;d0@Wu<PMRMxNeTjeldgn@;F*UB%tvM*FNp-P~# z+f@SfP_>>Kb2?@$Z!Pzuv@;{^Z&unrowOICjR$C~-&nt~eq;T{`i=D)f6ulh&<HfP z(?*~jine2ALPN)l1uC7V=ubxJ&y4gJR{C=%{R`1fgfWaTuyF9&iF6vBExXg`he`Xf zFjmoY+lh1{od^rb{A7^%(^2L#D>H%lxrgXXWF|5bna>Po3kQ#&Gu!n{WIkk_j}zAl zDjGUwED&8-Yhf+Eu|IM!uyF7Qh)BqoP*BlydGq6=_soa?`rUsYUjOFb5C8b!^~1|Y jUp@Tw-7g;gwEX$u<MR85cf<P+fB5L}o8Nx$-T!_DZ|RdM diff --git a/voxygen/src/hud/img_ids.rs b/voxygen/src/hud/img_ids.rs index 750534e663..27a19d6ef3 100644 --- a/voxygen/src/hud/img_ids.rs +++ b/voxygen/src/hud/img_ids.rs @@ -120,7 +120,7 @@ image_ids! { flyingrod_m1: "voxygen.element.icons.debug_wand_m1", flyingrod_m2: "voxygen.element.icons.debug_wand_m2", - charge: "voxygen.element.icons.skill_charge_2", + charge: "voxygen.element.icons.skill_charge_3", // Icons From db9a4ac6b94f608d312598a2df65b07069899796 Mon Sep 17 00:00:00 2001 From: timokoesters <timo@koesters.xyz> Date: Sat, 18 Jan 2020 15:00:59 +0100 Subject: [PATCH 08/12] improvement: reset character state and energy on death --- common/src/comp/energy.rs | 1 + server/src/lib.rs | 11 +++++++++++ 2 files changed, 12 insertions(+) diff --git a/common/src/comp/energy.rs b/common/src/comp/energy.rs index 2fcb180f09..32169ddbff 100644 --- a/common/src/comp/energy.rs +++ b/common/src/comp/energy.rs @@ -14,6 +14,7 @@ pub enum EnergySource { CastSpell, LevelUp, Regen, + Revive, Unknown, } diff --git a/server/src/lib.rs b/server/src/lib.rs index 797e0f52f8..eb44cad557 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -434,6 +434,17 @@ impl Server { .map(|err| { error!("Failed to insert ForceUpdate on dead client: {:?}", err) }); + state + .ecs() + .write_storage::<comp::Energy>() + .get_mut(entity) + .map(|energy| { + energy.set_to(energy.maximum(), comp::EnergySource::Revive) + }); + let _ = state + .ecs() + .write_storage::<comp::CharacterState>() + .insert(entity, comp::CharacterState::default()); } else { // If not a player delete the entity if let Err(err) = state.delete_entity_recorded(entity) { From 1912ab0d8b39d8e0d4466776f28023be658cf6e9 Mon Sep 17 00:00:00 2001 From: timokoesters <timo@koesters.xyz> Date: Sun, 19 Jan 2020 20:19:44 +0100 Subject: [PATCH 09/12] refactor: use restrict_mut --- common/src/sys/controller.rs | 16 ++++++++++++++-- common/src/sys/stats.rs | 20 +++++++++++++++----- 2 files changed, 29 insertions(+), 7 deletions(-) diff --git a/common/src/sys/controller.rs b/common/src/sys/controller.rs index 86e468b45c..c63f726055 100644 --- a/common/src/sys/controller.rs +++ b/common/src/sys/controller.rs @@ -277,13 +277,24 @@ impl<'a> System<'a> for Sys { ) { let mut server_emitter = server_bus.emitter(); let mut local_emitter = local_bus.emitter(); - for (entity, uid, controller, mut character, stats, energy, body, vel, physics, mount) in ( + for ( + entity, + uid, + controller, + mut character, + stats, + mut energy, + body, + vel, + physics, + mount, + ) in ( &entities, &uids, &mut controllers, &mut character_states, &stats, - &mut energies, + &mut energies.restrict_mut(), &bodies, &velocities, &physics_states, @@ -585,6 +596,7 @@ impl<'a> System<'a> for Sys { // Try to charge if inputs.charge.is_pressed() && !inputs.charge.is_held_down() { if energy + .get_mut_unchecked() .try_change_by(-CHARGE_COST, EnergySource::CastSpell) .is_ok() { diff --git a/common/src/sys/stats.rs b/common/src/sys/stats.rs index b013a12171..ad5e16e984 100644 --- a/common/src/sys/stats.rs +++ b/common/src/sys/stats.rs @@ -33,11 +33,11 @@ impl<'a> System<'a> for Sys { stats.set_event_emission(true); // Mutates all stats every tick causing the server to resend this component for every entity every tick - for (entity, character_state, mut stats, energy) in ( + for (entity, character_state, mut stats, mut energy) in ( &entities, &character_states, &mut stats.restrict_mut(), - &mut energies, + &mut energies.restrict_mut(), ) .join() { @@ -75,11 +75,21 @@ impl<'a> System<'a> for Sys { // Accelerate recharging energy if not wielding. match character_state.action { ActionState::Idle => { - energy.regen_rate += ENERGY_REGEN_ACCEL * dt.0; - energy.change_by(energy.regen_rate as i32, EnergySource::Regen); + if { + let energy = energy.get_unchecked(); + energy.current() < energy.maximum() + } { + let mut energy = energy.get_mut_unchecked(); + energy.regen_rate += ENERGY_REGEN_ACCEL * dt.0; + energy.change_by(energy.regen_rate as i32, EnergySource::Regen); + } } // All other states do not regen and set the rate back to zero. - _ => energy.regen_rate = 0.0, + _ => { + if energy.get_unchecked().regen_rate != 0.0 { + energy.get_mut_unchecked().regen_rate = 0.0 + } + } } } } From a708ab84d6e8f5ab0e30c0807d54b3b354626471 Mon Sep 17 00:00:00 2001 From: timokoesters <timo@koesters.xyz> Date: Sun, 19 Jan 2020 20:55:07 +0100 Subject: [PATCH 10/12] fix: make fall damage behave correctly again after changing gravity --- common/src/sys/phys.rs | 2 +- server/src/lib.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/common/src/sys/phys.rs b/common/src/sys/phys.rs index e974fca9e4..cf913da4b6 100644 --- a/common/src/sys/phys.rs +++ b/common/src/sys/phys.rs @@ -33,7 +33,7 @@ fn integrate_forces(dt: f32, mut lv: Vec3<f32>, grav: f32, damp: f32) -> Vec3<f3 // must be interpolated accordingly let linear_damp = (1.0 - damp.min(1.0)).powf(dt * 60.0); - lv.z = (lv.z - grav * dt).max(-50.0); + lv.z = (lv.z - grav * dt).max(-80.0); lv * linear_damp } diff --git a/server/src/lib.rs b/server/src/lib.rs index eb44cad557..7a7f85eeed 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -624,11 +624,11 @@ impl Server { } ServerEvent::LandOnGround { entity, vel } => { - if vel.z <= -25.0 { + if vel.z <= -37.0 { if let Some(stats) = state.ecs().write_storage::<comp::Stats>().get_mut(entity) { - let falldmg = (vel.z / 5.0) as i32; + let falldmg = (vel.z / 2.5) as i32; if falldmg < 0 { stats.health.change_by(comp::HealthChange { amount: falldmg, From 01ac937db57d7a4347f5c3daa283e326a8354fe1 Mon Sep 17 00:00:00 2001 From: Joseph Gerardot <indyJAG@gmail.com> Date: Sun, 19 Jan 2020 14:44:44 -0500 Subject: [PATCH 11/12] Fixup energy regen math to properly account for acceleration at any framerate. --- common/src/sys/stats.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/common/src/sys/stats.rs b/common/src/sys/stats.rs index ad5e16e984..6f059dbe42 100644 --- a/common/src/sys/stats.rs +++ b/common/src/sys/stats.rs @@ -5,7 +5,7 @@ use crate::{ }; use specs::{Entities, Join, Read, ReadStorage, System, WriteStorage}; -const ENERGY_REGEN_ACCEL: f32 = 0.5; +const ENERGY_REGEN_ACCEL: f32 = 20.0; /// This system kills players, levels them up, and regenerates energy. pub struct Sys; @@ -80,8 +80,9 @@ impl<'a> System<'a> for Sys { energy.current() < energy.maximum() } { let mut energy = energy.get_mut_unchecked(); + // Have to account for Calc I differential equations due to acceleration + energy.change_by((energy.regen_rate * dt.0 + ENERGY_REGEN_ACCEL * dt.0.powf(2.0) / 2.0) as i32, EnergySource::Regen); energy.regen_rate += ENERGY_REGEN_ACCEL * dt.0; - energy.change_by(energy.regen_rate as i32, EnergySource::Regen); } } // All other states do not regen and set the rate back to zero. From 5f41841522a8d77a3d58f6b9d701d6704fcf9321 Mon Sep 17 00:00:00 2001 From: Pfauenauge90 <44173739+Pfauenauge90@users.noreply.github.com> Date: Sun, 19 Jan 2020 22:39:01 +0100 Subject: [PATCH 12/12] added TODO for energy numbers --- common/src/sys/stats.rs | 6 +++++- voxygen/src/hud/skillbar.rs | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/common/src/sys/stats.rs b/common/src/sys/stats.rs index 6f059dbe42..0564fd8bf2 100644 --- a/common/src/sys/stats.rs +++ b/common/src/sys/stats.rs @@ -81,7 +81,11 @@ impl<'a> System<'a> for Sys { } { let mut energy = energy.get_mut_unchecked(); // Have to account for Calc I differential equations due to acceleration - energy.change_by((energy.regen_rate * dt.0 + ENERGY_REGEN_ACCEL * dt.0.powf(2.0) / 2.0) as i32, EnergySource::Regen); + energy.change_by( + (energy.regen_rate * dt.0 + ENERGY_REGEN_ACCEL * dt.0.powf(2.0) / 2.0) + as i32, + EnergySource::Regen, + ); energy.regen_rate += ENERGY_REGEN_ACCEL * dt.0; } } diff --git a/voxygen/src/hud/skillbar.rs b/voxygen/src/hud/skillbar.rs index c11c13d236..4cf7bb433c 100644 --- a/voxygen/src/hud/skillbar.rs +++ b/voxygen/src/hud/skillbar.rs @@ -965,7 +965,7 @@ impl<'a> Widget for Skillbar<'a> { .set(state.ids.health_text, ui); let energy_text = format!( "{}/{}", - self.energy.current() as u32 / 10, + self.energy.current() as u32 / 10, //TODO Fix regeneration with smaller energy numbers instead of dividing by 10 here self.energy.maximum() as u32 / 10 ); Text::new(&energy_text)