veloren/common/sys/src/stats.rs

168 lines
6.3 KiB
Rust
Raw Normal View History

use common::{
comp::{CharacterState, Energy, EnergyChange, EnergySource, Health, HealthSource, Stats},
event::{EventBus, ServerEvent},
metrics::SysMetrics,
resources::DeltaTime,
span,
};
use specs::{Entities, Join, Read, ReadExpect, ReadStorage, System, WriteStorage};
const ENERGY_REGEN_ACCEL: f32 = 10.0;
/// This system kills players, levels them up, and regenerates energy.
pub struct Sys;
impl<'a> System<'a> for Sys {
#[allow(clippy::type_complexity)]
type SystemData = (
Entities<'a>,
Read<'a, DeltaTime>,
Read<'a, EventBus<ServerEvent>>,
ReadExpect<'a, SysMetrics>,
ReadStorage<'a, CharacterState>,
WriteStorage<'a, Stats>,
WriteStorage<'a, Health>,
WriteStorage<'a, Energy>,
);
fn run(
&mut self,
(
entities,
dt,
server_event_bus,
sys_metrics,
character_states,
mut stats,
mut healths,
mut energies,
): Self::SystemData,
) {
let start_time = std::time::Instant::now();
span!(_guard, "run", "stats::Sys::run");
let mut server_event_emitter = server_event_bus.emitter();
// Increment last change timer
healths.set_event_emission(false); // avoid unnecessary syncing
for health in (&mut healths).join() {
health.last_change.0 += f64::from(dt.0);
}
healths.set_event_emission(true);
2020-07-05 12:39:28 +00:00
// Update stats
for (entity, mut stats, mut health) in (
&entities,
&mut stats.restrict_mut(),
&mut healths.restrict_mut(),
)
.join()
{
let (set_dead, level_up) = {
let stat = stats.get_unchecked();
let health = health.get_unchecked();
(
health.should_die() && !health.is_dead,
stat.exp.current() >= stat.exp.maximum(),
)
};
if set_dead {
let health = health.get_mut_unchecked();
server_event_emitter.emit(ServerEvent::Destroy {
entity,
cause: health.last_change.1.cause,
});
health.is_dead = true;
}
if level_up {
let stat = stats.get_mut_unchecked();
while stat.exp.current() >= stat.exp.maximum() {
stat.exp.change_by(-(stat.exp.maximum() as i64));
stat.level.change_by(1);
stat.exp.update_maximum(stat.level.level());
2020-06-01 09:21:33 +00:00
server_event_emitter.emit(ServerEvent::LevelUp(entity, stat.level.level()));
}
let health = health.get_mut_unchecked();
health.update_max_hp(Some(stat.body_type), stat.level.level());
health.set_to(health.maximum(), HealthSource::LevelUp);
}
2020-07-05 12:39:28 +00:00
}
2020-07-05 12:39:28 +00:00
// Update energies
2020-07-05 14:06:01 +00:00
for (character_state, mut energy) in
(&character_states, &mut energies.restrict_mut()).join()
2020-07-05 12:39:28 +00:00
{
match character_state {
2020-06-13 11:35:31 +00:00
// Accelerate recharging energy.
CharacterState::Idle { .. }
| CharacterState::Sit { .. }
| CharacterState::Dance { .. }
2020-08-02 05:09:11 +00:00
| CharacterState::Sneak { .. }
| CharacterState::GlideWield { .. }
| CharacterState::Wielding { .. }
| CharacterState::Equipping { .. }
| CharacterState::Boost { .. } => {
let res = {
2020-01-19 19:19:44 +00:00
let energy = energy.get_unchecked();
energy.current() < energy.maximum()
};
if res {
2020-01-19 19:19:44 +00:00
let mut energy = energy.get_mut_unchecked();
// Have to account for Calc I differential equations due to acceleration
2020-10-30 21:49:58 +00:00
energy.change_by(EnergyChange {
amount: (energy.regen_rate * dt.0
+ ENERGY_REGEN_ACCEL * dt.0.powi(2) / 2.0)
2020-01-19 21:39:01 +00:00
as i32,
2020-10-30 21:49:58 +00:00
source: EnergySource::Regen,
});
energy.regen_rate =
(energy.regen_rate + ENERGY_REGEN_ACCEL * dt.0).min(100.0);
2020-01-19 19:19:44 +00:00
}
},
// Ability and glider use does not regen and sets the rate back to zero.
CharacterState::Glide { .. }
| CharacterState::BasicMelee { .. }
| CharacterState::DashMelee { .. }
| CharacterState::LeapMelee { .. }
2020-07-08 19:58:41 +00:00
| CharacterState::SpinMelee { .. }
| CharacterState::ComboMelee { .. }
2020-07-26 03:06:53 +00:00
| CharacterState::BasicRanged { .. }
| CharacterState::ChargedMelee { .. }
2020-08-08 20:53:55 +00:00
| CharacterState::ChargedRanged { .. }
| CharacterState::RepeaterRanged { .. }
| CharacterState::Shockwave { .. }
| CharacterState::BasicBeam { .. } => {
2020-01-19 19:19:44 +00:00
if energy.get_unchecked().regen_rate != 0.0 {
energy.get_mut_unchecked().regen_rate = 0.0
}
},
2020-08-25 12:21:25 +00:00
// recover small amount of passive energy from blocking, and bonus energy from
// blocking attacks?
CharacterState::BasicBlock => {
let res = {
let energy = energy.get_unchecked();
energy.current() < energy.maximum()
};
if res {
2020-10-30 21:49:58 +00:00
energy.get_mut_unchecked().change_by(EnergyChange {
amount: -3,
source: EnergySource::Regen,
});
}
},
2020-06-13 11:35:31 +00:00
// Non-combat abilities that consume energy;
// temporarily stall energy gain, but preserve regen_rate.
CharacterState::Roll { .. } | CharacterState::Climb { .. } => {},
}
}
sys_metrics.stats_ns.store(
start_time.elapsed().as_nanos() as u64,
std::sync::atomic::Ordering::Relaxed,
);
}
}