Auras now apply a buff only once, instead of applying a buff every second.

Also supports for buffs persisting a period of time after leaving the aura.
Campfire regen slightly buffed.
This commit is contained in:
Sam 2021-02-26 17:35:49 -05:00
parent ded0cd0302
commit 45dd36be07
5 changed files with 78 additions and 29 deletions

View File

@ -77,6 +77,7 @@ pub enum BuffCategory {
Magical, Magical,
Divine, Divine,
PersistOnDeath, PersistOnDeath,
FromAura(bool), // bool used to check freshness of buff being apllied from aura
} }
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]

View File

@ -1,7 +1,7 @@
use common::{ use common::{
comp::{ comp::{
aura::{AuraChange, AuraKey, AuraKind, AuraTarget}, aura::{AuraChange, AuraKey, AuraKind, AuraTarget},
buff, buff::{self, BuffCategory},
group::Group, group::Group,
Auras, BuffKind, Buffs, CharacterState, Health, Pos, Auras, BuffKind, Buffs, CharacterState, Health, Pos,
}, },
@ -25,19 +25,36 @@ pub struct ReadData<'a> {
char_states: ReadStorage<'a, CharacterState>, char_states: ReadStorage<'a, CharacterState>,
healths: ReadStorage<'a, Health>, healths: ReadStorage<'a, Health>,
groups: ReadStorage<'a, Group>, groups: ReadStorage<'a, Group>,
buffs: ReadStorage<'a, Buffs>,
} }
pub struct Sys; pub struct Sys;
impl<'a> System<'a> for Sys { impl<'a> System<'a> for Sys {
type SystemData = (ReadData<'a>, WriteStorage<'a, Auras>); type SystemData = (
ReadData<'a>,
WriteStorage<'a, Auras>,
WriteStorage<'a, Buffs>,
);
fn run(&mut self, (read_data, mut auras): Self::SystemData) { fn run(&mut self, (read_data, mut auras, mut buffs): Self::SystemData) {
let mut server_emitter = read_data.server_bus.emitter(); let mut server_emitter = read_data.server_bus.emitter();
let dt = read_data.dt.0; let dt = read_data.dt.0;
auras.set_event_emission(false); auras.set_event_emission(false);
// Iterate through all buffs, on any buffs that are from an aura, sets the check
// for whether the buff is fresh 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 // Iterate through all entities with an aura
for (entity, pos, mut auras_comp) in for (entity, pos, mut auras_comp) in
(&read_data.entities, &read_data.positions, &mut auras).join() (&read_data.entities, &read_data.positions, &mut auras).join()
@ -56,10 +73,10 @@ impl<'a> System<'a> for Sys {
expired_auras.push(key); expired_auras.push(key);
} }
} }
for (target, target_pos, target_buffs, health) in ( for (target, target_pos, mut target_buffs, health) in (
&read_data.entities, &read_data.entities,
&read_data.positions, &read_data.positions,
&read_data.buffs, &mut buffs,
&read_data.healths, &read_data.healths,
) )
.join() .join()
@ -89,35 +106,58 @@ impl<'a> System<'a> for Sys {
category, category,
source, source,
} => { } => {
// Checks if the buff is not active so it isn't applied let apply_buff = match kind {
// every tick, but rather only once it runs out BuffKind::CampfireHeal => {
// TODO: Check for stronger buff of same kind so it can replace matches!(
// active buff. read_data.char_states.get(target),
if !target_buffs.contains(kind) { Some(CharacterState::Sit)
// Conditions for different buffs are in this match ) && health.current() < health.maximum()
// statement },
let apply_buff = match kind { // Add other specific buff conditions here
BuffKind::CampfireHeal => { _ => true,
matches!( };
read_data.char_states.get(target), if apply_buff {
Some(CharacterState::Sit) // Checks that target is not already receiving a buff from an
) && health.current() < health.maximum() // aura, where the buff is of the same kind, and is of at least
}, // the same strength
// Add other specific buff conditions here // If no such buff is present, adds the buff
_ => true, let emit_buff = !target_buffs.buffs.iter().any(|(_, buff)| {
}; buff.cat_ids.iter().any(|cat_id| {
if apply_buff { matches!(cat_id, BuffCategory::FromAura(_))
}) && buff.kind == kind
&& buff.data.strength >= data.strength
});
if emit_buff {
use buff::*; use buff::*;
server_emitter.emit(ServerEvent::Buff { server_emitter.emit(ServerEvent::Buff {
entity: target, entity: target,
buff_change: BuffChange::Add(Buff::new( buff_change: BuffChange::Add(Buff::new(
kind, kind,
data, data,
vec![category], vec![category, BuffCategory::FromAura(true)],
source, 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
// For any such buffs, sets freshness to fresh
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

@ -99,9 +99,9 @@ impl<'a> System<'a> for Sys {
kind, kind,
} => { } => {
*accumulated += *rate * dt; *accumulated += *rate * dt;
// Apply damage only once a second (with a minimum of 1 damage), or // Apply health change only once a second or
// when a buff is removed // when a buff is removed
if accumulated.abs() > rate.abs().max(10.0) if accumulated.abs() > rate.abs()
|| buff.time.map_or(false, |dur| dur == Duration::default()) || buff.time.map_or(false, |dur| dur == Duration::default())
{ {
let cause = if *accumulated > 0.0 { let cause = if *accumulated > 0.0 {
@ -180,6 +180,14 @@ impl<'a> System<'a> for Sys {
} }
fn tick_buff(id: u64, buff: &mut Buff, dt: f32, mut expire_buff: impl FnMut(u64)) { fn tick_buff(id: u64, buff: &mut Buff, dt: f32, mut expire_buff: impl FnMut(u64)) {
// If a buff is freshly applied from an aura, do not tick duration
if buff
.cat_ids
.iter()
.any(|cat_id| matches!(cat_id, BuffCategory::FromAura(true)))
{
return;
}
if let Some(remaining_time) = &mut buff.time { if let Some(remaining_time) = &mut buff.time {
if let Some(new_duration) = remaining_time.checked_sub(Duration::from_secs_f32(dt)) { if let Some(new_duration) = remaining_time.checked_sub(Duration::from_secs_f32(dt)) {
// The buff still continues. // The buff still continues.

View File

@ -978,7 +978,7 @@ fn handle_spawn_campfire(
.with(comp::Auras::new(Aura::new( .with(comp::Auras::new(Aura::new(
AuraKind::Buff { AuraKind::Buff {
kind: BuffKind::CampfireHeal, kind: BuffKind::CampfireHeal,
data: BuffData::new(0.01, Some(Duration::from_secs(1))), data: BuffData::new(0.02, Some(Duration::from_secs(1))),
category: BuffCategory::Natural, category: BuffCategory::Natural,
source: BuffSource::World, source: BuffSource::World,
}, },

View File

@ -194,7 +194,7 @@ pub fn handle_create_waypoint(server: &mut Server, pos: Vec3<f32>) {
.with(comp::Auras::new(Aura::new( .with(comp::Auras::new(Aura::new(
AuraKind::Buff { AuraKind::Buff {
kind: BuffKind::CampfireHeal, kind: BuffKind::CampfireHeal,
data: BuffData::new(0.01, Some(Duration::from_secs(1))), data: BuffData::new(0.02, Some(Duration::from_secs(1))),
category: BuffCategory::Natural, category: BuffCategory::Natural,
source: BuffSource::World, source: BuffSource::World,
}, },