2020-08-10 22:54:45 +00:00
|
|
|
use crate::{
|
2020-10-02 17:15:10 +00:00
|
|
|
comp::{BuffChange, BuffEffect, Buffs, HealthChange, HealthSource, Stats},
|
2020-10-01 02:32:38 +00:00
|
|
|
event::{EventBus, ServerEvent},
|
2020-08-10 22:54:45 +00:00
|
|
|
state::DeltaTime,
|
2020-10-01 02:32:38 +00:00
|
|
|
sync::Uid,
|
2020-08-10 22:54:45 +00:00
|
|
|
};
|
2020-10-01 02:32:38 +00:00
|
|
|
use specs::{Entities, Join, Read, ReadStorage, System, WriteStorage};
|
2020-08-10 22:54:45 +00:00
|
|
|
use std::time::Duration;
|
|
|
|
|
|
|
|
/// This system modifies entity stats, changing them using buffs
|
|
|
|
/// Currently, the system is VERY, VERY CRUDE and SYNC UN-FRIENDLY.
|
|
|
|
/// It does not use events and uses `Vec`s stored in component.
|
|
|
|
///
|
|
|
|
/// TODO: Make this production-quality system/design
|
|
|
|
pub struct Sys;
|
|
|
|
impl<'a> System<'a> for Sys {
|
|
|
|
#[allow(clippy::type_complexity)]
|
|
|
|
type SystemData = (
|
|
|
|
Entities<'a>,
|
|
|
|
Read<'a, DeltaTime>,
|
2020-10-01 02:32:38 +00:00
|
|
|
Read<'a, EventBus<ServerEvent>>,
|
|
|
|
ReadStorage<'a, Uid>,
|
2020-08-10 22:54:45 +00:00
|
|
|
WriteStorage<'a, Stats>,
|
|
|
|
WriteStorage<'a, Buffs>,
|
|
|
|
);
|
|
|
|
|
2020-10-01 02:32:38 +00:00
|
|
|
fn run(&mut self, (entities, dt, server_bus, uids, mut stats, mut buffs): Self::SystemData) {
|
|
|
|
let mut server_emitter = server_bus.emitter();
|
|
|
|
for (entity, uid, mut buffs) in (&entities, &uids, &mut buffs.restrict_mut()).join() {
|
2020-08-10 22:54:45 +00:00
|
|
|
let buff_comp = buffs.get_mut_unchecked();
|
2020-10-02 17:15:10 +00:00
|
|
|
let (mut active_buff_indices_for_removal, mut inactive_buff_indices_for_removal) =
|
|
|
|
(Vec::<usize>::new(), Vec::<usize>::new());
|
2020-08-10 22:54:45 +00:00
|
|
|
// Tick all de/buffs on a Buffs component.
|
2020-10-02 17:15:10 +00:00
|
|
|
for i in 0..buff_comp.active_buffs.len() {
|
2020-08-10 22:54:45 +00:00
|
|
|
// First, tick the buff and subtract delta from it
|
|
|
|
// and return how much "real" time the buff took (for tick independence).
|
|
|
|
// TODO: handle delta for "indefinite" buffs, i.e. time since they got removed.
|
2020-10-02 17:15:10 +00:00
|
|
|
let buff_delta = if let Some(remaining_time) = &mut buff_comp.active_buffs[i].time {
|
2020-08-10 22:54:45 +00:00
|
|
|
let pre_tick = remaining_time.as_secs_f32();
|
|
|
|
let new_duration = remaining_time.checked_sub(Duration::from_secs_f32(dt.0));
|
|
|
|
let post_tick = if let Some(dur) = new_duration {
|
|
|
|
// The buff still continues.
|
|
|
|
*remaining_time -= Duration::from_secs_f32(dt.0);
|
|
|
|
dur.as_secs_f32()
|
|
|
|
} else {
|
|
|
|
// The buff has expired.
|
2020-10-01 01:35:57 +00:00
|
|
|
// Remove it.
|
2020-10-02 17:15:10 +00:00
|
|
|
active_buff_indices_for_removal.push(i);
|
2020-08-10 22:54:45 +00:00
|
|
|
0.0
|
|
|
|
};
|
|
|
|
pre_tick - post_tick
|
|
|
|
} else {
|
|
|
|
// The buff is indefinite, and it takes full tick (delta).
|
|
|
|
// TODO: Delta for indefinite buffs might be shorter since they can get removed
|
|
|
|
// *during a tick* and this treats it as it always happens on a *tick end*.
|
|
|
|
dt.0
|
|
|
|
};
|
|
|
|
|
|
|
|
// Now, execute the buff, based on it's delta
|
2020-10-02 17:15:10 +00:00
|
|
|
for effect in &mut buff_comp.active_buffs[i].effects {
|
|
|
|
#[allow(clippy::single_match)]
|
|
|
|
// Remove clippy when more effects are added here
|
2020-10-01 00:40:46 +00:00
|
|
|
match effect {
|
|
|
|
// Only add an effect here if it is continuous or it is not immediate
|
2020-10-01 01:35:57 +00:00
|
|
|
BuffEffect::HealthChangeOverTime { rate, accumulated } => {
|
2020-10-01 00:40:46 +00:00
|
|
|
*accumulated += *rate * buff_delta;
|
|
|
|
// Apply only 0.5 or higher damage
|
|
|
|
if accumulated.abs() > 5.0 {
|
|
|
|
if let Some(stats) = stats.get_mut(entity) {
|
|
|
|
let change = HealthChange {
|
|
|
|
amount: *accumulated as i32,
|
|
|
|
cause: HealthSource::Unknown,
|
|
|
|
};
|
|
|
|
stats.health.change_by(change);
|
|
|
|
}
|
|
|
|
*accumulated = 0.0;
|
|
|
|
};
|
|
|
|
},
|
|
|
|
_ => {},
|
|
|
|
};
|
|
|
|
}
|
2020-08-10 22:54:45 +00:00
|
|
|
}
|
2020-10-02 17:15:10 +00:00
|
|
|
for i in 0..buff_comp.inactive_buffs.len() {
|
|
|
|
// First, tick the buff and subtract delta from it
|
|
|
|
// and return how much "real" time the buff took (for tick independence).
|
|
|
|
// TODO: handle delta for "indefinite" buffs, i.e. time since they got removed.
|
|
|
|
if let Some(remaining_time) = &mut buff_comp.inactive_buffs[i].time {
|
|
|
|
let new_duration = remaining_time.checked_sub(Duration::from_secs_f32(dt.0));
|
|
|
|
if new_duration.is_some() {
|
|
|
|
// The buff still continues.
|
|
|
|
*remaining_time -= Duration::from_secs_f32(dt.0);
|
|
|
|
} else {
|
|
|
|
// The buff has expired.
|
|
|
|
// Remove it.
|
|
|
|
inactive_buff_indices_for_removal.push(i);
|
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
2020-10-01 02:32:38 +00:00
|
|
|
server_emitter.emit(ServerEvent::Buff {
|
|
|
|
uid: *uid,
|
2020-10-02 17:15:10 +00:00
|
|
|
buff_change: BuffChange::RemoveByIndex(
|
|
|
|
active_buff_indices_for_removal,
|
|
|
|
inactive_buff_indices_for_removal,
|
|
|
|
),
|
2020-10-01 02:32:38 +00:00
|
|
|
});
|
2020-08-10 22:54:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|