Make charging take a discrete amount of energy and change energy

regeneration to use floats so it is smoother and tickrate-independent.
This commit is contained in:
Joseph Gerardot 2019-11-21 19:53:28 -05:00 committed by Pfauenauge90
parent b4337e57aa
commit c10c31043c
4 changed files with 66 additions and 28 deletions

View File

@ -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);

View File

@ -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 {

View File

@ -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() {
if energy
.try_change_by(-CHARGE_COST, EnergySource::CastSpell)
.is_ok()
{
character.action = Charge {
time_left: Duration::from_millis(250),
};
}
}
continue;
}

View File

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