Buffs from auras no longer need to be mutably accessed every tick in aura system to prevent applying a buff every tick and ensure duration only starts after leaving aura

This commit is contained in:
Sam 2023-03-09 20:27:54 -05:00
parent 9efac9957d
commit 8a6a60d5bb
3 changed files with 104 additions and 56 deletions

View File

@ -1,5 +1,9 @@
#![allow(clippy::nonstandard_macro_braces)] //tmp as of false positive !?
use crate::{comp::Stats, resources::Time, uid::Uid};
use crate::{
comp::{aura::AuraKey, Stats},
resources::Time,
uid::Uid,
};
use core::cmp::Ordering;
#[cfg(not(target_arch = "wasm32"))]
use hashbrown::HashMap;
@ -168,7 +172,7 @@ pub enum BuffCategory {
Magical,
Divine,
PersistOnDeath,
FromAura(bool), // bool used to check if buff recently set by aura
FromActiveAura(Uid, AuraKey),
}
#[derive(Clone, Debug, Serialize, Deserialize)]
@ -285,7 +289,7 @@ impl Buff {
},
BuffKind::Potion => {
vec![BuffEffect::HealthChangeOverTime {
rate: data.strength * dbg!(stats.map_or(1.0, |s| s.heal_multiplier)),
rate: data.strength * stats.map_or(1.0, |s| s.heal_multiplier),
kind: ModifierKind::Additive,
instance,
}]
@ -366,12 +370,20 @@ impl Buff {
BuffKind::PotionSickness => vec![BuffEffect::HealReduction(data.strength)],
};
let start_time = Time(time.0 + data.delay.unwrap_or(0.0));
let end_time = if cat_ids
.iter()
.any(|cat_id| matches!(cat_id, BuffCategory::FromActiveAura(..)))
{
None
} else {
data.duration.map(|dur| Time(start_time.0 + dur))
};
Buff {
kind,
data,
cat_ids,
start_time,
end_time: data.duration.map(|dur| Time(start_time.0 + dur)),
end_time,
effects,
source,
}

View File

@ -33,45 +33,32 @@ pub struct ReadData<'a> {
groups: ReadStorage<'a, Group>,
uids: ReadStorage<'a, Uid>,
stats: ReadStorage<'a, Stats>,
buffs: ReadStorage<'a, Buffs>,
}
#[derive(Default)]
pub struct Sys;
impl<'a> System<'a> for Sys {
type SystemData = (
ReadData<'a>,
WriteStorage<'a, Auras>,
WriteStorage<'a, Buffs>,
);
type SystemData = (ReadData<'a>, WriteStorage<'a, Auras>);
const NAME: &'static str = "aura";
const ORIGIN: Origin = Origin::Common;
const PHASE: Phase = Phase::Create;
fn run(_job: &mut Job<Self>, (read_data, mut auras, mut buffs): Self::SystemData) {
fn run(_job: &mut Job<Self>, (read_data, mut auras): Self::SystemData) {
let mut server_emitter = read_data.server_bus.emitter();
let dt = read_data.dt.0;
auras.set_event_emission(false);
buffs.set_event_emission(false);
// Iterate through all buffs, on any buffs that are from an aura, sets the check
// 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);
}
}
}
// Iterate through all entities with an aura
for (entity, pos, mut auras_comp) in
(&read_data.entities, &read_data.positions, &mut auras).join()
for (entity, pos, mut auras_comp, uid) in (
&read_data.entities,
&read_data.positions,
&mut auras,
&read_data.uids,
)
.join()
{
let mut expired_auras = Vec::<AuraKey>::new();
// Iterate through the auras attached to this entity
@ -108,7 +95,7 @@ impl<'a> System<'a> for Sys {
})
});
target_iter.for_each(|(target, target_pos, health, target_uid, stats)| {
let mut target_buffs = match buffs.get_mut(target) {
let target_buffs = match read_data.buffs.get(target) {
Some(buff) => buff,
None => return,
};
@ -135,10 +122,12 @@ impl<'a> System<'a> for Sys {
if is_target {
activate_aura(
key,
aura,
*uid,
target,
health,
&mut target_buffs,
target_buffs,
stats,
&read_data,
&mut server_emitter,
@ -155,17 +144,18 @@ impl<'a> System<'a> for Sys {
}
}
auras.set_event_emission(true);
buffs.set_event_emission(true);
}
}
#[warn(clippy::pedantic)]
//#[warn(clippy::nursery)]
fn activate_aura(
key: AuraKey,
aura: &Aura,
applier: Uid,
target: EcsEntity,
health: &Health,
target_buffs: &mut Buffs,
target_buffs: &Buffs,
stats: Option<&Stats>,
read_data: &ReadData,
server_emitter: &mut Emitter<ServerEvent>,
@ -247,13 +237,9 @@ fn activate_aura(
let emit_buff = !target_buffs.buffs.iter().any(|(_, buff)| {
buff.cat_ids
.iter()
.any(|cat_id| matches!(cat_id, BuffCategory::FromAura(_)))
.any(|cat_id| matches!(cat_id, BuffCategory::FromActiveAura(uid, aura_key) if *aura_key == key && *uid == applier))
&& buff.kind == kind
&& buff.data.strength >= data.strength
&& buff.end_time.map_or(true, |end| {
data.duration
.map_or(false, |dur| end.0 >= read_data.time.0 + dur)
})
});
if emit_buff {
server_emitter.emit(ServerEvent::Buff {
@ -261,31 +247,13 @@ fn activate_aura(
buff_change: BuffChange::Add(Buff::new(
kind,
data,
vec![category, BuffCategory::FromAura(true)],
vec![category, BuffCategory::FromActiveAura(applier, key)],
source,
*read_data.time,
stats,
)),
});
}
// Finds all buffs on target that are from an aura, are of
// the same buff kind, and are of at most the same strength.
// 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);
}
}
},
}
}

View File

@ -1,6 +1,7 @@
use common::{
combat::DamageContributor,
comp::{
aura::Auras,
body::{object, Body},
buff::{
Buff, BuffCategory, BuffChange, BuffData, BuffEffect, BuffId, BuffKind, BuffSource,
@ -9,7 +10,7 @@ use common::{
fluid_dynamics::{Fluid, LiquidKind},
item::MaterialStatManifest,
Energy, Group, Health, HealthChange, Inventory, LightEmitter, ModifierKind, PhysicsState,
Stats,
Pos, Stats,
},
event::{Emitter, EventBus, ServerEvent},
resources::{DeltaTime, Time},
@ -39,6 +40,8 @@ pub struct ReadData<'a> {
time: Read<'a, Time>,
msm: ReadExpect<'a, MaterialStatManifest>,
buffs: ReadStorage<'a, Buffs>,
auras: ReadStorage<'a, Auras>,
positions: ReadStorage<'a, Pos>,
}
#[derive(Default)]
@ -233,6 +236,71 @@ impl<'a> System<'a> for Sys {
}
let mut expired_buffs = Vec::<BuffId>::new();
// Replace buffs from an active aura with a normal buff when out of range of the
// aura
buff_comp
.buffs
.iter()
.filter_map(|(id, buff)| {
if let Some((uid, aura_key)) = buff.cat_ids.iter().find_map(|cat_id| {
if let BuffCategory::FromActiveAura(uid, aura_key) = cat_id {
Some((uid, aura_key))
} else {
None
}
}) {
Some((id, buff, uid, aura_key))
} else {
None
}
})
.for_each(|(buff_id, buff, uid, aura_key)| {
let replace = if let Some(aura_entity) = read_data
.uid_allocator
.retrieve_entity_internal((*uid).into())
{
if let Some(aura) = read_data
.auras
.get(aura_entity)
.and_then(|auras| auras.auras.get(*aura_key))
{
if let (Some(pos), Some(aura_pos)) = (
read_data.positions.get(entity),
read_data.positions.get(aura_entity),
) {
pos.0.distance_squared(aura_pos.0) > aura.radius.powi(2)
} else {
true
}
} else {
true
}
} else {
true
};
if replace {
expired_buffs.push(*buff_id);
server_emitter.emit(ServerEvent::Buff {
entity,
buff_change: BuffChange::Add(Buff::new(
buff.kind,
buff.data,
buff.cat_ids
.iter()
.copied()
.filter(|cat_id| {
!matches!(cat_id, BuffCategory::FromActiveAura(..))
})
.collect::<Vec<_>>(),
buff.source,
*read_data.time,
Some(&stat),
)),
});
}
});
buff_comp.buffs.iter().for_each(|(id, buff)| {
if buff.end_time.map_or(false, |end| end.0 < read_data.time.0) {
expired_buffs.push(*id)