veloren/common/sys/src/aura.rs

191 lines
8.6 KiB
Rust
Raw Normal View History

2020-12-04 22:24:56 +00:00
use common::{
comp::{
2021-01-18 22:58:56 +00:00
aura::{AuraChange, AuraKey, AuraKind, AuraTarget},
buff::{self, BuffCategory},
2021-01-18 22:58:56 +00:00
group::Group,
Auras, BuffKind, Buffs, CharacterState, Health, Pos,
2020-12-04 22:24:56 +00:00
},
event::{EventBus, ServerEvent},
resources::DeltaTime,
2021-03-01 20:44:29 +00:00
uid::{Uid, UidAllocator},
2020-12-04 22:24:56 +00:00
};
use common_ecs::{Job, Origin, Phase, System};
use specs::{
saveload::MarkerAllocator, shred::ResourceId, Entities, Join, Read, ReadStorage, SystemData,
World, WriteStorage,
};
2020-12-04 22:24:56 +00:00
use std::time::Duration;
#[derive(SystemData)]
pub struct ReadData<'a> {
entities: Entities<'a>,
dt: Read<'a, DeltaTime>,
server_bus: Read<'a, EventBus<ServerEvent>>,
uid_allocator: Read<'a, UidAllocator>,
positions: ReadStorage<'a, Pos>,
char_states: ReadStorage<'a, CharacterState>,
healths: ReadStorage<'a, Health>,
groups: ReadStorage<'a, Group>,
2021-03-01 20:44:29 +00:00
uids: ReadStorage<'a, Uid>,
}
#[derive(Default)]
2020-12-04 22:24:56 +00:00
pub struct Sys;
2021-03-08 11:13:59 +00:00
impl<'a> System<'a> for Sys {
type SystemData = (
ReadData<'a>,
WriteStorage<'a, Auras>,
WriteStorage<'a, Buffs>,
);
2020-12-04 22:24:56 +00:00
const NAME: &'static str = "aura";
const ORIGIN: Origin = Origin::Common;
const PHASE: Phase = Phase::Create;
2021-03-08 11:13:59 +00:00
fn run(_job: &mut Job<Self>, (read_data, mut auras, mut buffs): Self::SystemData) {
let mut server_emitter = read_data.server_bus.emitter();
let dt = read_data.dt.0;
2020-12-04 22:24:56 +00:00
auras.set_event_emission(false);
2021-03-15 21:35:53 +00:00
buffs.set_event_emission(false);
2020-12-04 22:24:56 +00:00
// Iterate through all buffs, on any buffs that are from an aura, sets the check
2021-02-26 23:15:24 +00:00
// for whether the buff recently set by aura to false
for (_, mut buffs_comp) in (&read_data.entities, &mut buffs).join() {
for (_, buff) in buffs_comp.buffs.iter_mut() {
if let Some(cat_id) = buff
.cat_ids
.iter_mut()
.find(|cat_id| matches!(cat_id, BuffCategory::FromAura(true)))
{
*cat_id = BuffCategory::FromAura(false);
}
}
}
2020-12-04 22:24:56 +00:00
// Iterate through all entities with an aura
for (entity, pos, mut auras_comp) in
(&read_data.entities, &read_data.positions, &mut auras).join()
{
2020-12-04 22:24:56 +00:00
let mut expired_auras = Vec::<AuraKey>::new();
// Iterate through the auras attached to this entity
for (key, aura) in auras_comp.auras.iter_mut() {
// Tick the aura and subtract dt from it
if let Some(remaining_time) = &mut aura.duration {
if let Some(new_duration) =
remaining_time.checked_sub(Duration::from_secs_f32(dt))
2020-12-04 22:24:56 +00:00
{
*remaining_time = new_duration;
} else {
*remaining_time = Duration::default();
expired_auras.push(key);
}
}
2021-03-01 20:44:29 +00:00
for (target, target_pos, mut target_buffs, health, target_uid) in (
&read_data.entities,
&read_data.positions,
&mut buffs,
&read_data.healths,
2021-03-01 20:44:29 +00:00
&read_data.uids,
2021-01-14 12:21:55 +00:00
)
.join()
2020-12-04 22:24:56 +00:00
{
// Ensure entity is within the aura radius
if target_pos.0.distance_squared(pos.0) < aura.radius.powi(2) {
2021-01-18 22:58:56 +00:00
if let AuraTarget::GroupOf(uid) = aura.target {
let same_group = read_data
.uid_allocator
2021-01-18 22:58:56 +00:00
.retrieve_entity_internal(uid.into())
.and_then(|e| read_data.groups.get(e))
2021-01-18 22:58:56 +00:00
.map_or(false, |owner_group| {
Some(owner_group) == read_data.groups.get(target)
2021-03-01 20:44:29 +00:00
})
|| *target_uid == uid;
2021-01-18 22:58:56 +00:00
if !same_group {
continue;
}
}
2020-12-04 22:24:56 +00:00
// TODO: When more aura kinds (besides Buff) are
// implemented, match on them here
match aura.aura_kind {
AuraKind::Buff {
kind,
data,
category,
source,
} => {
let apply_buff = match kind {
BuffKind::CampfireHeal => {
matches!(
read_data.char_states.get(target),
Some(CharacterState::Sit)
) && health.current() < health.maximum()
},
// Add other specific buff conditions here
_ => true,
};
if apply_buff {
// Checks that target is not already receiving a buff from an
// aura, where the buff is of the same kind, and is of at least
// the same strength and of at least the same duration
// If no such buff is present, adds the buff
let emit_buff = !target_buffs.buffs.iter().any(|(_, buff)| {
buff.cat_ids.iter().any(|cat_id| {
matches!(cat_id, BuffCategory::FromAura(_))
}) && buff.kind == kind
&& buff.data.strength >= data.strength
&& buff.time.map_or(true, |dur| {
data.duration.map_or(false, |dur_2| dur >= dur_2)
})
});
if emit_buff {
2020-12-04 22:24:56 +00:00
use buff::*;
server_emitter.emit(ServerEvent::Buff {
entity: target,
2020-12-04 22:24:56 +00:00
buff_change: BuffChange::Add(Buff::new(
kind,
data,
vec![category, BuffCategory::FromAura(true)],
2020-12-04 22:24:56 +00:00
source,
)),
});
}
// Finds all buffs on target that are from an aura, are of the
// same buff kind, and are of at most the same strength
2021-02-26 23:15:24 +00:00
// For any such buffs, marks it as recently applied
for (_, buff) in
target_buffs.buffs.iter_mut().filter(|(_, buff)| {
buff.cat_ids.iter().any(|cat_id| {
matches!(cat_id, BuffCategory::FromAura(_))
}) && buff.kind == kind
&& buff.data.strength <= data.strength
})
{
if let Some(cat_id) =
buff.cat_ids.iter_mut().find(|cat_id| {
matches!(cat_id, BuffCategory::FromAura(false))
})
{
*cat_id = BuffCategory::FromAura(true);
}
}
2020-12-04 22:24:56 +00:00
}
},
}
}
}
}
if !expired_auras.is_empty() {
server_emitter.emit(ServerEvent::Aura {
entity,
aura_change: AuraChange::RemoveByKey(expired_auras),
});
}
}
auras.set_event_emission(true);
2021-03-15 21:35:53 +00:00
buffs.set_event_emission(true);
2020-12-04 22:24:56 +00:00
}
}