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 -}