Extract buff executor into own function

This commit is contained in:
juliancoffee 2022-09-05 15:48:23 +03:00
parent 7ea720b2ef
commit 54c48c7112

View File

@ -11,10 +11,10 @@ use common::{
Energy, Group, Health, HealthChange, Inventory, LightEmitter, ModifierKind, PhysicsState,
Stats,
},
event::{EventBus, ServerEvent},
event::{Emitter, EventBus, ServerEvent},
resources::{DeltaTime, Time},
terrain::SpriteKind,
uid::UidAllocator,
uid::{Uid, UidAllocator},
Damage, DamageSource,
};
use common_base::prof_span;
@ -22,8 +22,8 @@ use common_ecs::{Job, Origin, ParMode, Phase, System};
use hashbrown::HashMap;
use rayon::iter::ParallelIterator;
use specs::{
saveload::MarkerAllocator, shred::ResourceId, Entities, Join, ParJoin, Read, ReadExpect,
ReadStorage, SystemData, World, WriteStorage,
saveload::MarkerAllocator, shred::ResourceId, Entities, Entity, Join, ParJoin, Read,
ReadExpect, ReadStorage, SystemData, World, WriteStorage,
};
use std::time::Duration;
@ -257,165 +257,19 @@ impl<'a> System<'a> for Sys {
// Now, execute the buff, based on it's delta
for effect in &mut buff.effects {
match effect {
BuffEffect::HealthChangeOverTime {
rate,
accumulated,
kind,
instance,
} => {
*accumulated += *rate * dt;
// Apply health change only once per second, per health, or
// when a buff is removed
if accumulated.abs() > rate.abs().min(1.0)
|| buff.time.map_or(false, |dur| dur == Duration::default())
{
let (cause, by) = if *accumulated != 0.0 {
(Some(DamageSource::Buff(buff.kind)), buff_owner)
} else {
(None, None)
};
let amount = match *kind {
ModifierKind::Additive => *accumulated,
ModifierKind::Fractional => health.maximum() * *accumulated,
};
let damage_contributor = by.and_then(|uid| {
read_data.uid_allocator.retrieve_entity_internal(uid.0).map(
|entity| {
DamageContributor::new(
uid,
read_data.groups.get(entity).cloned(),
)
},
)
});
server_emitter.emit(ServerEvent::HealthChange {
entity,
change: HealthChange {
amount,
by: damage_contributor,
cause,
time: *read_data.time,
crit: false,
instance: *instance,
},
});
*accumulated = 0.0;
};
},
BuffEffect::EnergyChangeOverTime {
rate,
accumulated,
kind,
} => {
*accumulated += *rate * dt;
// Apply energy change only once per second, per energy, or
// when a buff is removed
if accumulated.abs() > rate.abs().min(10.0)
|| buff.time.map_or(false, |dur| dur == Duration::default())
{
let amount = match *kind {
ModifierKind::Additive => *accumulated,
ModifierKind::Fractional => {
energy.maximum() as f32 * *accumulated
},
};
server_emitter.emit(ServerEvent::EnergyChange {
entity,
change: amount,
});
*accumulated = 0.0;
};
},
BuffEffect::MaxHealthModifier { value, kind } => match kind {
ModifierKind::Additive => {
stat.max_health_modifiers.add_mod += *value;
},
ModifierKind::Fractional => {
stat.max_health_modifiers.mult_mod *= *value;
},
},
BuffEffect::MaxEnergyModifier { value, kind } => match kind {
ModifierKind::Additive => {
stat.max_energy_modifiers.add_mod += *value;
},
ModifierKind::Fractional => {
stat.max_energy_modifiers.mult_mod *= *value;
},
},
BuffEffect::DamageReduction(dr) => {
stat.damage_reduction = stat.damage_reduction.max(*dr).min(1.0);
},
BuffEffect::MaxHealthChangeOverTime {
rate,
kind,
target_fraction,
achieved_fraction,
} => {
// Current fraction uses information from last tick, which is
// necessary as buffs from this tick are not guaranteed to have
// finished applying
let current_fraction = health.maximum() / health.base_max();
// If achieved_fraction not initialized, initialize it to health
// fraction
if achieved_fraction.is_none() {
*achieved_fraction = Some(current_fraction)
}
if let Some(achieved_fraction) = achieved_fraction {
// Percentage change that should be applied to max_health
let health_tick = match kind {
ModifierKind::Additive => {
// `rate * dt` is amount of health, dividing by base max
// creates fraction
*rate * dt / health.base_max() as f32
},
ModifierKind::Fractional => {
// `rate * dt` is the fraction
*rate * dt
},
};
let potential_fraction = *achieved_fraction + health_tick;
// Potential progress towards target fraction, if
// target_fraction ~ 1.0 then set progress to 1.0 to avoid
// divide by zero
let progress = if (1.0 - *target_fraction).abs() > f32::EPSILON
{
(1.0 - potential_fraction) / (1.0 - *target_fraction)
} else {
1.0
};
// Change achieved_fraction depending on what other buffs have
// occurred
if progress > 1.0 {
// If potential fraction already beyond target fraction,
// simply multiply max_health_modifier by the target
// fraction, and set achieved fraction to target_fraction
*achieved_fraction = *target_fraction;
} else {
// Else have not achieved target yet, update
// achieved_fraction
*achieved_fraction = potential_fraction;
}
// Apply achieved_fraction to max_health_modifier
stat.max_health_modifiers.mult_mod *= *achieved_fraction;
}
},
BuffEffect::MovementSpeed(speed) => {
stat.move_speed_modifier *= *speed;
},
BuffEffect::AttackSpeed(speed) => {
stat.attack_speed_modifier *= *speed;
},
BuffEffect::GroundFriction(gf) => {
stat.friction_modifier *= *gf;
},
};
execute_effect(
effect,
buff.kind,
buff.time,
&read_data,
&mut *stat,
health,
energy,
entity,
buff_owner,
&mut server_emitter,
dt,
);
}
}
}
@ -446,6 +300,175 @@ impl<'a> System<'a> for Sys {
}
}
fn execute_effect(
effect: &mut BuffEffect,
buff_kind: BuffKind,
buff_time: Option<std::time::Duration>,
read_data: &ReadData,
stat: &mut Stats,
health: &Health,
energy: &Energy,
entity: Entity,
buff_owner: Option<Uid>,
server_emitter: &mut Emitter<ServerEvent>,
dt: f32,
) {
match effect {
BuffEffect::HealthChangeOverTime {
rate,
accumulated,
kind,
instance,
} => {
*accumulated += *rate * dt;
// Apply health change only once per second, per health, or
// when a buff is removed
if accumulated.abs() > rate.abs().min(1.0)
|| buff_time.map_or(false, |dur| dur == Duration::default())
{
let (cause, by) = if *accumulated != 0.0 {
(Some(DamageSource::Buff(buff_kind)), buff_owner)
} else {
(None, None)
};
let amount = match *kind {
ModifierKind::Additive => *accumulated,
ModifierKind::Fractional => health.maximum() * *accumulated,
};
let damage_contributor = by.and_then(|uid| {
read_data
.uid_allocator
.retrieve_entity_internal(uid.0)
.map(|entity| {
DamageContributor::new(uid, read_data.groups.get(entity).cloned())
})
});
server_emitter.emit(ServerEvent::HealthChange {
entity,
change: HealthChange {
amount,
by: damage_contributor,
cause,
time: *read_data.time,
crit: false,
instance: *instance,
},
});
*accumulated = 0.0;
};
},
BuffEffect::EnergyChangeOverTime {
rate,
accumulated,
kind,
} => {
*accumulated += *rate * dt;
// Apply energy change only once per second, per energy, or
// when a buff is removed
if accumulated.abs() > rate.abs().min(10.0)
|| buff_time.map_or(false, |dur| dur == Duration::default())
{
let amount = match *kind {
ModifierKind::Additive => *accumulated,
ModifierKind::Fractional => energy.maximum() as f32 * *accumulated,
};
server_emitter.emit(ServerEvent::EnergyChange {
entity,
change: amount,
});
*accumulated = 0.0;
};
},
BuffEffect::MaxHealthModifier { value, kind } => match kind {
ModifierKind::Additive => {
stat.max_health_modifiers.add_mod += *value;
},
ModifierKind::Fractional => {
stat.max_health_modifiers.mult_mod *= *value;
},
},
BuffEffect::MaxEnergyModifier { value, kind } => match kind {
ModifierKind::Additive => {
stat.max_energy_modifiers.add_mod += *value;
},
ModifierKind::Fractional => {
stat.max_energy_modifiers.mult_mod *= *value;
},
},
BuffEffect::DamageReduction(dr) => {
stat.damage_reduction = stat.damage_reduction.max(*dr).min(1.0);
},
BuffEffect::MaxHealthChangeOverTime {
rate,
kind,
target_fraction,
achieved_fraction,
} => {
// Current fraction uses information from last tick, which is
// necessary as buffs from this tick are not guaranteed to have
// finished applying
let current_fraction = health.maximum() / health.base_max();
// If achieved_fraction not initialized, initialize it to health
// fraction
if achieved_fraction.is_none() {
*achieved_fraction = Some(current_fraction)
}
if let Some(achieved_fraction) = achieved_fraction {
// Percentage change that should be applied to max_health
let health_tick = match kind {
ModifierKind::Additive => {
// `rate * dt` is amount of health, dividing by base max
// creates fraction
*rate * dt / health.base_max() as f32
},
ModifierKind::Fractional => {
// `rate * dt` is the fraction
*rate * dt
},
};
let potential_fraction = *achieved_fraction + health_tick;
// Potential progress towards target fraction, if
// target_fraction ~ 1.0 then set progress to 1.0 to avoid
// divide by zero
let progress = if (1.0 - *target_fraction).abs() > f32::EPSILON {
(1.0 - potential_fraction) / (1.0 - *target_fraction)
} else {
1.0
};
// Change achieved_fraction depending on what other buffs have
// occurred
if progress > 1.0 {
// If potential fraction already beyond target fraction,
// simply multiply max_health_modifier by the target
// fraction, and set achieved fraction to target_fraction
*achieved_fraction = *target_fraction;
} else {
// Else have not achieved target yet, update
// achieved_fraction
*achieved_fraction = potential_fraction;
}
// Apply achieved_fraction to max_health_modifier
stat.max_health_modifiers.mult_mod *= *achieved_fraction;
}
},
BuffEffect::MovementSpeed(speed) => {
stat.move_speed_modifier *= *speed;
},
BuffEffect::AttackSpeed(speed) => {
stat.attack_speed_modifier *= *speed;
},
BuffEffect::GroundFriction(gf) => {
stat.friction_modifier *= *gf;
},
};
}
fn tick_buff(id: u64, buff: &mut Buff, dt: f32, mut expire_buff: impl FnMut(u64)) {
// If a buff is recently applied from an aura, do not tick duration
if buff