2020-12-01 00:28:00 +00:00
|
|
|
use common::{
|
2020-10-13 00:48:25 +00:00
|
|
|
comp::{
|
2020-10-31 22:34:08 +00:00
|
|
|
BuffCategory, BuffChange, BuffEffect, BuffId, BuffSource, Buffs, Health, HealthChange,
|
2021-01-08 19:12:09 +00:00
|
|
|
HealthSource, Inventory, ModifierKind,
|
2020-10-13 00:48:25 +00:00
|
|
|
},
|
2020-10-01 02:32:38 +00:00
|
|
|
event::{EventBus, ServerEvent},
|
2020-12-01 00:28:00 +00:00
|
|
|
resources::DeltaTime,
|
2021-01-08 19:12:09 +00:00
|
|
|
Damage, DamageSource,
|
2020-08-10 22:54:45 +00:00
|
|
|
};
|
2020-10-25 01:20:03 +00:00
|
|
|
use specs::{Entities, Join, Read, ReadStorage, System, WriteStorage};
|
2020-08-10 22:54:45 +00:00
|
|
|
use std::time::Duration;
|
|
|
|
|
|
|
|
pub struct Sys;
|
|
|
|
impl<'a> System<'a> for Sys {
|
|
|
|
#[allow(clippy::type_complexity)]
|
|
|
|
type SystemData = (
|
2020-10-25 01:20:03 +00:00
|
|
|
Entities<'a>,
|
2020-08-10 22:54:45 +00:00
|
|
|
Read<'a, DeltaTime>,
|
2020-10-01 02:32:38 +00:00
|
|
|
Read<'a, EventBus<ServerEvent>>,
|
2021-01-08 19:12:09 +00:00
|
|
|
ReadStorage<'a, Inventory>,
|
2020-10-31 22:34:08 +00:00
|
|
|
WriteStorage<'a, Health>,
|
2020-08-10 22:54:45 +00:00
|
|
|
WriteStorage<'a, Buffs>,
|
|
|
|
);
|
|
|
|
|
2020-10-25 18:42:42 +00:00
|
|
|
fn run(
|
|
|
|
&mut self,
|
2021-01-08 19:12:09 +00:00
|
|
|
(entities, dt, server_bus, inventories, mut healths, mut buffs): Self::SystemData,
|
2020-10-25 18:42:42 +00:00
|
|
|
) {
|
2020-10-01 02:32:38 +00:00
|
|
|
let mut server_emitter = server_bus.emitter();
|
2020-10-19 03:00:35 +00:00
|
|
|
// Set to false to avoid spamming server
|
2020-10-25 01:20:03 +00:00
|
|
|
buffs.set_event_emission(false);
|
2020-10-31 22:34:08 +00:00
|
|
|
healths.set_event_emission(false);
|
2021-01-07 20:25:12 +00:00
|
|
|
for (entity, mut buff_comp, mut health) in (&entities, &mut buffs, &mut healths).join() {
|
2020-10-24 20:12:37 +00:00
|
|
|
let mut expired_buffs = Vec::<BuffId>::new();
|
|
|
|
for (id, buff) in buff_comp.buffs.iter_mut() {
|
2020-10-19 03:00:35 +00:00
|
|
|
// Tick the buff and subtract delta from it
|
2020-10-24 20:12:37 +00:00
|
|
|
if let Some(remaining_time) = &mut buff.time {
|
|
|
|
if let Some(new_duration) =
|
|
|
|
remaining_time.checked_sub(Duration::from_secs_f32(dt.0))
|
|
|
|
{
|
2020-08-10 22:54:45 +00:00
|
|
|
// The buff still continues.
|
2020-10-24 20:12:37 +00:00
|
|
|
*remaining_time = new_duration;
|
2020-08-10 22:54:45 +00:00
|
|
|
} else {
|
2020-10-24 23:07:38 +00:00
|
|
|
// checked_sub returns None when remaining time
|
|
|
|
// went below 0, so set to 0
|
|
|
|
*remaining_time = Duration::default();
|
2020-08-10 22:54:45 +00:00
|
|
|
// The buff has expired.
|
2020-10-01 01:35:57 +00:00
|
|
|
// Remove it.
|
2020-10-24 20:12:37 +00:00
|
|
|
expired_buffs.push(*id);
|
|
|
|
}
|
2020-10-19 03:00:35 +00:00
|
|
|
}
|
|
|
|
}
|
2020-08-10 22:54:45 +00:00
|
|
|
|
2021-01-08 19:12:09 +00:00
|
|
|
if let Some(inventory) = inventories.get(entity) {
|
|
|
|
let damage_reduction = Damage::compute_damage_reduction(inventory);
|
2020-10-25 18:42:42 +00:00
|
|
|
if (damage_reduction - 1.0).abs() < f32::EPSILON {
|
|
|
|
for (id, buff) in buff_comp.buffs.iter() {
|
|
|
|
if !buff.kind.is_buff() {
|
|
|
|
expired_buffs.push(*id);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-31 22:34:08 +00:00
|
|
|
// Call to reset health to base values
|
|
|
|
health.reset_max();
|
2020-10-24 23:07:38 +00:00
|
|
|
|
|
|
|
// Iterator over the lists of buffs by kind
|
2021-01-07 20:25:12 +00:00
|
|
|
let buff_comp = &mut *buff_comp;
|
2020-10-24 20:12:37 +00:00
|
|
|
for buff_ids in buff_comp.kinds.values() {
|
2020-10-24 23:07:38 +00:00
|
|
|
// Get the strongest of this buff kind
|
2020-10-24 20:12:37 +00:00
|
|
|
if let Some(buff) = buff_comp.buffs.get_mut(&buff_ids[0]) {
|
2020-10-24 23:07:38 +00:00
|
|
|
// Get buff owner?
|
2020-10-24 20:12:37 +00:00
|
|
|
let buff_owner = if let BuffSource::Character { by: owner } = buff.source {
|
|
|
|
Some(owner)
|
2020-10-19 03:00:35 +00:00
|
|
|
} else {
|
2020-10-24 20:12:37 +00:00
|
|
|
None
|
2020-10-19 03:00:35 +00:00
|
|
|
};
|
2020-10-24 23:07:38 +00:00
|
|
|
|
2020-10-24 20:12:37 +00:00
|
|
|
// Now, execute the buff, based on it's delta
|
|
|
|
for effect in &mut buff.effects {
|
|
|
|
match effect {
|
2020-12-04 22:24:56 +00:00
|
|
|
BuffEffect::HealthChangeOverTime {
|
|
|
|
rate,
|
|
|
|
accumulated,
|
|
|
|
kind,
|
|
|
|
} => {
|
2020-10-24 20:12:37 +00:00
|
|
|
*accumulated += *rate * dt.0;
|
|
|
|
// Apply damage only once a second (with a minimum of 1 damage), or
|
|
|
|
// when a buff is removed
|
|
|
|
if accumulated.abs() > rate.abs().max(10.0)
|
|
|
|
|| buff.time.map_or(false, |dur| dur == Duration::default())
|
|
|
|
{
|
|
|
|
let cause = if *accumulated > 0.0 {
|
2020-11-05 01:21:42 +00:00
|
|
|
HealthSource::Heal { by: buff_owner }
|
2020-10-24 20:12:37 +00:00
|
|
|
} else {
|
2020-11-05 01:21:42 +00:00
|
|
|
HealthSource::Damage {
|
2021-01-18 05:46:53 +00:00
|
|
|
kind: DamageSource::Buff(buff.kind),
|
2020-11-05 01:21:42 +00:00
|
|
|
by: buff_owner,
|
|
|
|
}
|
2020-10-24 20:12:37 +00:00
|
|
|
};
|
2020-12-04 22:24:56 +00:00
|
|
|
let amount = match *kind {
|
|
|
|
ModifierKind::Additive => *accumulated as i32,
|
|
|
|
ModifierKind::Fractional => {
|
|
|
|
(health.maximum() as f32 * *accumulated) as i32
|
|
|
|
},
|
|
|
|
};
|
2020-10-24 20:12:37 +00:00
|
|
|
server_emitter.emit(ServerEvent::Damage {
|
2020-11-02 00:26:01 +00:00
|
|
|
entity,
|
2020-12-04 22:24:56 +00:00
|
|
|
change: HealthChange { amount, cause },
|
2020-10-24 20:12:37 +00:00
|
|
|
});
|
|
|
|
*accumulated = 0.0;
|
|
|
|
};
|
|
|
|
},
|
2020-10-24 23:07:38 +00:00
|
|
|
BuffEffect::MaxHealthModifier { value, kind } => match kind {
|
|
|
|
ModifierKind::Additive => {
|
2021-01-07 20:25:12 +00:00
|
|
|
let health = &mut *health;
|
|
|
|
let buffed_health_max =
|
|
|
|
(health.maximum() as f32 + *value) as u32;
|
|
|
|
health.set_maximum(buffed_health_max);
|
2020-12-04 22:24:56 +00:00
|
|
|
},
|
|
|
|
ModifierKind::Fractional => {
|
2021-01-07 20:25:12 +00:00
|
|
|
let health = &mut *health;
|
2020-12-04 22:24:56 +00:00
|
|
|
health.set_maximum((health.maximum() as f32 * *value) as u32);
|
2020-10-24 23:07:38 +00:00
|
|
|
},
|
|
|
|
},
|
2020-10-24 20:12:37 +00:00
|
|
|
};
|
|
|
|
}
|
2020-10-19 03:00:35 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-24 20:12:37 +00:00
|
|
|
// Remove buffs that expire
|
|
|
|
if !expired_buffs.is_empty() {
|
2020-10-19 03:00:35 +00:00
|
|
|
server_emitter.emit(ServerEvent::Buff {
|
2020-10-25 01:20:03 +00:00
|
|
|
entity,
|
2020-10-24 20:12:37 +00:00
|
|
|
buff_change: BuffChange::RemoveById(expired_buffs),
|
2020-10-19 03:00:35 +00:00
|
|
|
});
|
|
|
|
}
|
2020-10-11 21:39:52 +00:00
|
|
|
|
2020-10-31 22:34:08 +00:00
|
|
|
// Remove buffs that don't persist on death
|
|
|
|
if health.is_dead {
|
2020-10-13 00:48:25 +00:00
|
|
|
server_emitter.emit(ServerEvent::Buff {
|
2020-10-25 01:20:03 +00:00
|
|
|
entity,
|
2020-10-13 00:48:25 +00:00
|
|
|
buff_change: BuffChange::RemoveByCategory {
|
2020-10-24 20:12:37 +00:00
|
|
|
all_required: vec![],
|
|
|
|
any_required: vec![],
|
|
|
|
none_required: vec![BuffCategory::PersistOnDeath],
|
2020-10-13 00:48:25 +00:00
|
|
|
},
|
|
|
|
});
|
|
|
|
}
|
2020-08-10 22:54:45 +00:00
|
|
|
}
|
2020-10-27 01:17:46 +00:00
|
|
|
// Turned back to true
|
2020-10-25 01:20:03 +00:00
|
|
|
buffs.set_event_emission(true);
|
2020-10-31 22:34:08 +00:00
|
|
|
healths.set_event_emission(true);
|
2020-08-10 22:54:45 +00:00
|
|
|
}
|
|
|
|
}
|