//! The `Enemy` entity, its corpse snapshot, and lifecycle state. The `EnemyBehavior` trait
//! and on-death dispatch live in `crate::behavior::enemy`. Enemy *kinds* are registry-defined
//! (`crate::registry::EnemyRegistry`), mirroring how weapons are defined — an `EnemyID` is the
//! enemy's "what am I", just like a `WeaponID` defines a projectile.
use std::collections::HashMap;
use macroquad::math::Vec2;
use crate::{
animations::AnimationTypes,
behavior::{enemy::EnemyBehavior, status::StatusBehavior},
context::DamageMap,
registry::{DamageID, EnemyID},
types::Source,
world::EntityId,
};
pub enum EnemyState {
Alive(f32),
Killed(f32),
}
#[derive(Clone)]
pub struct Enemy {
pub id: EntityId,
pub pos: Vec2,
pub vel: Vec2,
pub hp: f32,
pub tier: u32,
pub max_hp: f32,
pub radius: f32,
pub xp: u32,
pub kind: EnemyID,
pub death_anim: AnimationTypes,
pub statuses: Vec<Box<dyn StatusBehavior>>,
pub resists: HashMap<DamageID, f32>,
pub killer: Source,
pub behaviors: Vec<Box<dyn EnemyBehavior>>,
pub draw: Box<fn(&Enemy) -> ()>,
}
impl Enemy {
pub fn apply_damage(&mut self, amount: DamageMap, source: Source) -> EnemyState {
let mut total_damage = 0.0;
for (id, dmg) in amount.damage_map.iter() {
let resist = *self.resists.get(id).unwrap_or(&0.0f32);
let damage_done = dmg.compute() - dmg.compute() * resist;
total_damage += damage_done;
self.hp -= damage_done;
}
if !self.alive() {
self.killer = source;
EnemyState::Killed(total_damage)
} else {
EnemyState::Alive(total_damage)
}
}
pub fn push_status(&mut self, s: Box<dyn StatusBehavior>) {
self.statuses.push(s);
}
pub fn alive(&self) -> bool {
self.hp > 0.0
}
pub fn animate(&self) {
(self.draw)(self)
}
}
#[derive(Clone, Copy)] // Copy — no heap, trivially cheap, serializable
pub struct Corpse {
pub id: EntityId,
pub pos: Vec2,
pub kind: EnemyID, // for drop weights + which VFX/sprite
pub tier: u32,
pub xp: u32,
pub death_anim: AnimationTypes,
pub killer: Source,
pub radius: f32,
pub hp_overkill: f32, // whatever reactions actually want
}
impl Corpse {
pub fn of(e: &Enemy) -> Corpse {
Corpse {
id: e.id,
pos: e.pos,
kind: e.kind,
death_anim: e.death_anim,
killer: e.killer,
radius: e.radius,
hp_overkill: e.hp,
tier: e.tier,
xp: e.xp,
}
}
}