td

enemy.rs at [1c4f2f89eb]
Login

enemy.rs at [1c4f2f89eb]

File src/enemy.rs artifact c5a6ad1825 part of check-in 1c4f2f89eb


//! 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,
        }
    }
}