use crate::{
behavior::{dyn_clone_trait, projectile::BehaviorPriority},
context::{DamageMap, TickContext},
enemy::Enemy,
registry::DamageID,
types::{DamageDelivery, Source},
};
pub trait StatusBehavior: StatusClone + Send + Sync {
fn on_tick(&mut self, _e: &mut Enemy, _c: &mut TickContext) -> bool {
false
}
fn priority(&self) -> i32 {
BehaviorPriority::DEFAULT
}
}
dyn_clone_trait!(StatusClone, StatusBehavior);
#[derive(Clone)]
pub struct PeriodDamage {
kind: DamageID,
amount: f32,
interval: f32,
lifetime: f32,
duration: f32,
// Captured from whoever applied this (e.g. the projectile's `Source::Tower`) at the
// moment of application — NOT read from `TickContext.source` on every tick, since the
// generic per-enemy tick pass (`tick_enemies`) runs with `Source::None`. Using that
// would silently make every DoT invisible to tower-attributed tracking (the DPS meter,
// kill credit) regardless of who actually applied it.
source: Source,
}
impl PeriodDamage {
pub fn make(
kind: DamageID,
amount: f32,
interval: f32,
lifetime: f32,
source: Source,
) -> Box<dyn StatusBehavior> {
Box::new(PeriodDamage {
kind,
amount,
interval,
lifetime,
duration: 0.0,
source,
})
}
}
impl StatusBehavior for PeriodDamage {
fn on_tick(&mut self, e: &mut Enemy, c: &mut TickContext) -> bool {
let dmg = (self.amount / self.interval) * c.dt;
self.duration += c.dt;
let mut dm = DamageMap::default();
dm.flat(self.kind, dmg);
// Queued like everything else instead of mutating `e` directly: a status ticking
// itself into the enemy bypassed the one shared choke point every other damage
// source goes through, which means a future global modifier (e.g. "+30% all
// damage") would need special-cased access to the status system to see it, instead
// of just hooking `Command::Damage` once.
c.cmd.damage(e.id, dm, self.source, DamageDelivery::Dot);
self.duration > self.lifetime
}
}
pub fn dispatch_status_tick(e: &mut Enemy, c: &mut TickContext) {
let mut statuses = std::mem::take(&mut e.statuses);
statuses.retain_mut(|s| !s.on_tick(e, c));
e.statuses = statuses;
}