//! `Monster` type. use crate::{bin::OptU16, err::*, fixed::{Fixed, Unit}}; use bitflags::bitflags; use super::{attk, damg}; /// Reads a `MNpx` chunk. pub fn read(b: &[u8]) -> ResultS<(Monster, usize)> { read_data! { endian: BIG, buf: b, size: 156, start: 0, data { let collection = u16[0]; let vitality = u16[2]; let dty_immune = u32[4] flag damg::DamageTypeFlags; let dty_weak = u32[8] flag damg::DamageTypeFlags; let flags = u32[12] flag MonsterFlags; let cls_self = u32[16] flag MonsterClass; let cls_friend = u32[20]; let cls_enemy = u32[24]; let snd_pitch = Fixed[28]; let snd_see_enemy = OptU16[32]; let snd_see_friend = OptU16[34]; let snd_seeclear = OptU16[36]; let snd_kill = OptU16[38]; let snd_apologize = OptU16[40]; let snd_amicide = OptU16[42]; let snd_flaming = OptU16[44]; let snd_active = OptU16[46]; let snd_active_mask = u16[48]; let typ_item = OptU16[50]; let radius = Unit[52]; let height = Unit[54]; let height_hover = Unit[56]; let ledge_min = Unit[58]; let ledge_max = Unit[60]; let ext_vel_scale = Fixed[62]; let fxt_impact = OptU16[66]; let fxt_impact_melee = OptU16[68]; let fxt_trail = OptU16[70]; let half_fov_horz = u16[72]; let half_fov_vert = u16[74]; let view_range = Unit[76]; let view_range_dark = Unit[78]; let intelligence = u16[80]; let speed = u16[82]; let gravity = u16[84]; let vel_terminal = u16[86]; let door_try_mask = u16[88]; let expl_radius = OptU16[90]; let expl_damage = damg::read[92; 12]; let seq_hit = OptU16[104]; let seq_dying_hard = OptU16[106]; let seq_dying_soft = OptU16[108]; let seq_dead_hard = OptU16[110]; let seq_dead_soft = OptU16[112]; let seq_standing = u16[114]; let seq_moving = u16[116]; let seq_tele_in = OptU16[118]; let seq_tele_out = OptU16[120]; let atk_frequency = u16[122]; let atk_melee = attk::read[124; 16]; let atk_range = attk::read[140; 16]; } } // friend and enemy fields MUST truncate because the original source code // used `-1` to represent "all classes" which should be invalid normally let cls_friend = MonsterClass::from_bits_truncate(cls_friend); let cls_enemy = MonsterClass::from_bits_truncate(cls_enemy); Ok((Monster{collection, vitality, dty_immune, dty_weak, flags, cls_self, cls_friend, cls_enemy, snd_pitch, snd_see_enemy, snd_see_friend, snd_seeclear, snd_kill, snd_apologize, snd_amicide, snd_flaming, snd_active, snd_active_mask, typ_item, radius, height, height_hover, ledge_min, ledge_max, ext_vel_scale, fxt_impact, fxt_impact_melee, fxt_trail, half_fov_horz, half_fov_vert, view_range, view_range_dark, intelligence, speed, gravity, vel_terminal, door_try_mask, expl_radius, expl_damage, seq_hit, seq_dying_hard, seq_dying_soft, seq_dead_hard, seq_dead_soft, seq_standing, seq_moving, seq_tele_in, seq_tele_out, atk_frequency, atk_melee, atk_range}, 156)) } /// A monster definition. #[cfg_attr(feature = "serde_obj", derive(serde::Serialize))] #[derive(Debug, Eq, PartialEq)] pub struct Monster { pub collection: u16, pub vitality: u16, pub dty_immune: damg::DamageTypeFlags, pub dty_weak: damg::DamageTypeFlags, pub flags: MonsterFlags, pub cls_self: MonsterClass, pub cls_friend: MonsterClass, pub cls_enemy: MonsterClass, pub snd_pitch: Fixed, pub snd_see_enemy: OptU16, pub snd_see_friend: OptU16, pub snd_seeclear: OptU16, pub snd_kill: OptU16, pub snd_apologize: OptU16, pub snd_amicide: OptU16, pub snd_flaming: OptU16, pub snd_active: OptU16, pub snd_active_mask: u16, pub typ_item: OptU16, pub radius: Unit, pub height: Unit, pub height_hover: Unit, pub ledge_min: Unit, pub ledge_max: Unit, pub ext_vel_scale: Fixed, pub fxt_impact: OptU16, pub fxt_impact_melee: OptU16, pub fxt_trail: OptU16, pub half_fov_horz: u16, pub half_fov_vert: u16, pub view_range: Unit, pub view_range_dark: Unit, pub intelligence: u16, pub speed: u16, pub gravity: u16, pub vel_terminal: u16, pub door_try_mask: u16, pub expl_radius: OptU16, pub expl_damage: damg::Damage, pub seq_hit: OptU16, pub seq_dying_hard: OptU16, pub seq_dying_soft: OptU16, pub seq_dead_hard: OptU16, pub seq_dead_soft: OptU16, pub seq_standing: u16, pub seq_moving: u16, pub seq_tele_in: OptU16, pub seq_tele_out: OptU16, pub atk_frequency: u16, pub atk_melee: attk::Attack, pub atk_range: attk::Attack, } bitflags! { /// Flags for a monster. #[cfg_attr(feature = "serde_obj", derive(serde::Serialize))] pub struct MonsterFlags: u32 { const IGNORE_LOS = 1; const FLYING = 1 << 1; const ALIEN = 1 << 2; const MAJOR = 1 << 3; const MINOR = 1 << 4; const NO_OMIT = 1 << 5; const FLOATS = 1 << 6; const NO_ATTACK = 1 << 7; const SNIPE = 1 << 8; const INVISIBLE = 1 << 9; const SUBTLY_INVISIBLE = 1 << 10; const KAMIKAZE = 1 << 11; const BERSERKER = 1 << 12; const ENLARGED = 1 << 13; const DELAYED_DEATH = 1 << 14; const FIRE_SYMMETRICAL = 1 << 15; const NUCLEAR_DEATH = 1 << 16; const NO_FIRE_BACKWARDS = 1 << 17; const CAN_DIE_IN_FLAMES = 1 << 18; const WAIT_FOR_GOOD_SHOT = 1 << 19; const TINY = 1 << 20; const FAST_ATTACK = 1 << 21; const LIKES_WATER = 1 << 22; const LIKES_SEWAGE = 1 << 23; const LIKES_LAVA = 1 << 24; const LIKES_GOO = 1 << 25; const TELE_UNDER_MEDIA = 1 << 26; const USE_RANDOM_WEAPON = 1 << 27; } } bitflags! { /// The composite type of a monster. #[cfg_attr(feature = "serde_obj", derive(serde::Serialize))] pub struct MonsterClass: u32 { const PLAYER = 1; const CIVILIAN = 1 << 1; const MADD = 1 << 2; const POSSESSED_HUMMER = 1 << 3; const DEFENDER = 1 << 4; const FIGHTER = 1 << 5; const TROOPER = 1 << 6; const HUNTER = 1 << 7; const ENFORCER = 1 << 8; const JUGGERNAUT = 1 << 9; const HUMMER = 1 << 10; const COMPILER = 1 << 11; const CYBORG = 1 << 12; const ASSIMILATED = 1 << 13; const TICK = 1 << 14; const YETI = 1 << 15; } } // EOF