//! Structures used by Marathon's Physics format. use crate::{durandal::{bin::*, err::*, fixed::*}}; use bitflags::bitflags; /// Reads a `PXpx` chunk. pub fn read_pxpx(b: &[u8]) -> ResultS<(Physics, usize)> { read_data! { endian: BIG, buf: b, size: 104, start: 0, data { let vel_fwd = Fixed[0]; let vel_bkw = Fixed[4]; let vel_prp = Fixed[8]; let acc_nrm = Fixed[12]; let dec_nrm = Fixed[16]; let dec_air = Fixed[20]; let acc_grv = Fixed[24]; let acc_cli = Fixed[28]; let vel_trm = Fixed[32]; let dec_ext = Fixed[36]; let acc_ang = Fixed[40]; let dec_ang = Fixed[44]; let vel_ang = Fixed[48]; let vel_rec = Fixed[52]; let fng_vel = Fixed[56]; let fng_max = Fixed[60]; let ele_max = Fixed[64]; let dec_xng = Fixed[68]; let stp_dta = Fixed[72]; let stp_amp = Fixed[76]; let ply_rad = Fixed[80]; let ply_hei = Fixed[84]; let ply_dhi = Fixed[88]; let ply_cam = Fixed[92]; let ply_spl = Fixed[96]; let ply_hcm = Fixed[100]; } } Ok((Physics{acc_ang, acc_cli, acc_grv, acc_nrm, dec_air, dec_ang, dec_ext, dec_nrm, dec_xng, ele_max, fng_max, fng_vel, ply_cam, ply_dhi, ply_hcm, ply_hei, ply_rad, ply_spl, stp_amp, stp_dta, vel_ang, vel_bkw, vel_fwd, vel_prp, vel_rec, vel_trm}, 104)) } /// Reads a `FXpx` chunk. pub fn read_fxpx(b: &[u8]) -> ResultS<(Effect, usize)> { read_data! { endian: BIG, buf: b, size: 14, start: 0, data { let collection = u16[0]; let shape = u16[2]; let pitch = Fixed[4]; let flags = u16[8] flag EffectFlags; let delay = OptU16[10]; let delay_snd = OptU16[12]; } } Ok((Effect{collection, shape, pitch, flags, delay, delay_snd}, 14)) } /// Reads a `WPpx` chunk. pub fn read_wppx(b: &[u8]) -> ResultS<(Weapon, usize)> { read_data! { endian: BIG, buf: b, size: 134, start: 0, data { let typ_item = u16[0]; let typ_powerup = OptU16[2]; let typ_weapon = u16[4] enum WeaponType; let flags = u16[6] flag WeaponFlags; let lit_value = Fixed[8]; let lit_decay = u16[12]; let hei_idle = Fixed[14]; let amp_bob = Fixed[18]; let hei_kick = Fixed[22]; let hei_reload = Fixed[26]; let wid_idle = Fixed[30]; let amp_horz = Fixed[34]; let collection = u16[38]; let frm_idle = u16[40]; let frm_firing = u16[42]; let frm_reload = OptU16[44]; let frm_charge = OptU16[48]; let frm_charged = OptU16[50]; let tic_ready = u16[52]; let tic_load_beg = u16[54]; let tic_load_mid = u16[56]; let tic_load_end = u16[58]; let tic_powerup = u16[60]; let trig_pri = read_trigger[62; 36]; let trig_sec = read_trigger[98; 36]; } } Ok((Weapon{amp_bob, amp_horz, collection, flags, frm_charge, frm_charged, frm_firing, frm_idle, frm_reload, hei_idle, hei_kick, hei_reload, lit_decay, lit_value, tic_load_beg, tic_load_end, tic_load_mid, tic_powerup, tic_ready, trig_pri, trig_sec, typ_item, typ_powerup, typ_weapon, wid_idle}, 134)) } /// Reads a `PRpx` chunk. pub fn read_prpx(b: &[u8]) -> ResultS<(Projectile, usize)> { read_data! { endian: BIG, buf: b, size: 48, start: 0, data { let collection = OptU16[0]; let shape = u16[2]; let fxt_explode = OptU16[4]; let fxt_exp_media = OptU16[6]; let fxt_trail = OptU16[8]; let tic_trail = u16[10]; let max_trail = OptU16[12]; let typ_media = OptU16[14]; let radius = Unit[16]; let dmg_rad = Unit[18]; let dmg_def = read_damage[20; 12]; let flags = u32[32] flag ProjectileFlags; let speed = Unit[36]; let range = Unit[38]; let snd_pitch = Fixed[40]; let snd_fly = OptU16[44]; let snd_bounce = OptU16[46]; } } Ok((Projectile{collection, shape, fxt_explode, fxt_exp_media, fxt_trail, tic_trail, max_trail, typ_media, radius, dmg_rad, dmg_def, flags, speed, range, snd_pitch, snd_fly, snd_bounce}, 48)) } /// Reads a `MNpx` chunk. pub fn read_mnpx(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 DamageTypeFlags; let dty_weak = u32[8] flag 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 = read_damage[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 = read_attack[124; 16]; let atk_range = read_attack[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)) } /// Reads a `Trigger` object. fn read_trigger(b: &[u8]) -> ResultS { read_data! { endian: BIG, buf: b, size: 36, start: 0, data { let magazine = u16[0]; let typ_ammo = OptU16[2]; let tic_round = OptU16[4]; let tic_recover = u16[6]; let tic_charge = u16[8]; let recoil = Unit[10]; let snd_fire = OptU16[12]; let snd_click = OptU16[14]; let snd_charge = OptU16[16]; let snd_casing = OptU16[18]; let snd_reload = OptU16[20]; let snd_charged = OptU16[22]; let typ_proj = u16[24]; let theta = u16[26]; let dx = i16[28]; let dz = i16[30]; let typ_casing = u16[32] enum CasingType; let burst = u16[34]; } } Ok(Trigger{burst, dx, dz, magazine, recoil, snd_casing, snd_charge, snd_charged, snd_click, snd_fire, snd_reload, theta, tic_charge, tic_recover, tic_round, typ_ammo, typ_casing, typ_proj}) } /// Reads a `Damage` object. fn read_damage(b: &[u8]) -> ResultS { read_data! { endian: BIG, buf: b, size: 12, start: 0, data { let dtype = u16[0] enum DamageType; let flags = u16[2]; let dmg_base = u16[4]; let dmg_rand = u16[6]; let scale = Fixed[8]; } } let alien = flags != 0; Ok(Damage{dtype, alien, dmg_base, dmg_rand, scale}) } /// Reads an `Attack` object. fn read_attack(b: &[u8]) -> ResultS { read_data! { endian: BIG, buf: b, size: 16, start: 0, data { let ptype = OptU16[0]; let rep = u16[2]; let error = Angle[4]; let range = Unit[6]; let shape = u16[8]; let ofs_x = Unit[10]; let ofs_y = Unit[12]; let ofs_z = Unit[14]; } } Ok(Attack{ptype, rep, error, range, shape, ofs_x, ofs_y, ofs_z}) } /// Static physics information. #[cfg_attr(feature = "serde_obj", derive(serde::Serialize))] #[derive(Debug, Eq, PartialEq)] pub struct Physics { pub acc_ang: Fixed, pub acc_cli: Fixed, pub acc_grv: Fixed, pub acc_nrm: Fixed, pub dec_air: Fixed, pub dec_ang: Fixed, pub dec_ext: Fixed, pub dec_nrm: Fixed, pub dec_xng: Fixed, pub ele_max: Fixed, pub fng_max: Fixed, pub fng_vel: Fixed, pub ply_cam: Fixed, pub ply_dhi: Fixed, pub ply_hcm: Fixed, pub ply_hei: Fixed, pub ply_rad: Fixed, pub ply_spl: Fixed, pub stp_amp: Fixed, pub stp_dta: Fixed, pub vel_ang: Fixed, pub vel_bkw: Fixed, pub vel_fwd: Fixed, pub vel_prp: Fixed, pub vel_rec: Fixed, pub vel_trm: Fixed, } /// An effect definition. #[cfg_attr(feature = "serde_obj", derive(serde::Serialize))] #[derive(Debug, Eq, PartialEq)] pub struct Effect { pub collection: u16, pub shape: u16, pub pitch: Fixed, pub flags: EffectFlags, pub delay: OptU16, pub delay_snd: OptU16, } /// A weapon definition. #[cfg_attr(feature = "serde_obj", derive(serde::Serialize))] #[derive(Debug, Eq, PartialEq)] pub struct Weapon { pub amp_bob: Fixed, pub amp_horz: Fixed, pub collection: u16, pub flags: WeaponFlags, pub frm_charge: OptU16, pub frm_charged: OptU16, pub frm_firing: u16, pub frm_idle: u16, pub frm_reload: OptU16, pub hei_idle: Fixed, pub hei_kick: Fixed, pub hei_reload: Fixed, pub lit_decay: u16, pub lit_value: Fixed, pub tic_load_beg: u16, pub tic_load_end: u16, pub tic_load_mid: u16, pub tic_powerup: u16, pub tic_ready: u16, pub trig_pri: Trigger, pub trig_sec: Trigger, pub typ_item: u16, pub typ_powerup: OptU16, pub typ_weapon: WeaponType, pub wid_idle: Fixed, } /// The definition of one of two triggers for a weapon. #[cfg_attr(feature = "serde_obj", derive(serde::Serialize))] #[derive(Debug, Eq, PartialEq)] pub struct Trigger { pub burst: u16, pub dx: i16, pub dz: i16, pub magazine: u16, pub recoil: Unit, pub snd_casing: OptU16, pub snd_charge: OptU16, pub snd_charged: OptU16, pub snd_click: OptU16, pub snd_fire: OptU16, pub snd_reload: OptU16, pub theta: u16, pub tic_charge: u16, pub tic_recover: u16, pub tic_round: OptU16, pub typ_ammo: OptU16, pub typ_casing: CasingType, pub typ_proj: u16, } /// A projectile definition. #[cfg_attr(feature = "serde_obj", derive(serde::Serialize))] #[derive(Debug, Eq, PartialEq)] pub struct Projectile { pub collection: OptU16, pub shape: u16, pub fxt_explode: OptU16, pub fxt_exp_media: OptU16, pub fxt_trail: OptU16, pub tic_trail: u16, pub max_trail: OptU16, pub typ_media: OptU16, pub radius: Unit, pub dmg_rad: Unit, pub dmg_def: Damage, pub flags: ProjectileFlags, pub speed: Unit, pub range: Unit, pub snd_pitch: Fixed, pub snd_fly: OptU16, pub snd_bounce: OptU16, } /// 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: DamageTypeFlags, pub dty_weak: 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: 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: Attack, pub atk_range: Attack, } /// A damage definition. #[cfg_attr(feature = "serde_obj", derive(serde::Serialize))] #[derive(Debug, Eq, PartialEq)] pub struct Damage { pub dtype: DamageType, pub alien: bool, pub dmg_base: u16, pub dmg_rand: u16, pub scale: Fixed, } /// The definition of a monster's attack. #[cfg_attr(feature = "serde_obj", derive(serde::Serialize))] #[derive(Debug, Eq, PartialEq)] pub struct Attack { pub ptype: OptU16, pub rep: u16, pub error: Angle, pub range: Unit, pub shape: u16, pub ofs_x: Unit, pub ofs_y: Unit, pub ofs_z: Unit, } bitflags! { /// Flags for an effect. #[cfg_attr(feature = "serde_obj", derive(serde::Serialize))] pub struct EffectFlags: u16 { const END_ON_LOOP = 1; const END_ON_XFER_LOOP = 1 << 1; const SOUND_ONLY = 1 << 2; const MAKE_TWIN_VISIBLE = 1 << 3; const MEDIA_EFFECT = 1 << 4; } } bitflags! { /// Flags for a weapon. #[cfg_attr(feature = "serde_obj", derive(serde::Serialize))] pub struct WeaponFlags: u16 { const AUTOMATIC = 1; const REMOVE_AFTER_USE = 1 << 1; const INSTANT_CASING = 1 << 2; const OVERLOADS = 1 << 3; const RANDOM_AMMO = 1 << 4; const TEMPORARY_POWER = 1 << 5; const RELOAD_ONE_HAND = 1 << 6; const FIRE_OUT_OF_PHASE = 1 << 7; const FIRE_UNDER_MEDIA = 1 << 8; const TRIGGER_SAME_AMMO = 1 << 9; const SECONDARY_FLIP = 1 << 10; } } bitflags! { /// Flags for a projectile. #[cfg_attr(feature = "serde_obj", derive(serde::Serialize))] pub struct ProjectileFlags: u32 { const GUIDED = 1; const STOP_ON_LOOP = 1 << 1; const PERSISTENT = 1 << 2; const ALIEN = 1 << 3; const GRAVITY = 1 << 4; const NO_HORZ_ERROR = 1 << 5; const NO_VERT_ERROR = 1 << 6; const TOGGLE_PANELS = 1 << 7; const POS_VERT_ERROR = 1 << 8; const MELEE = 1 << 9; const RIPPER = 1 << 10; const PASS_TRANS_RANDOM = 1 << 11; const PASS_TRANS_MORE = 1 << 12; const DOUBLE_GRAVITY = 1 << 13; const REBOUND_FLOOR = 1 << 14; const THROUGH_MEDIA = 1 << 15; const BECOME_ITEM = 1 << 16; const BLOODY = 1 << 17; const WANDER_HORZ = 1 << 18; const USE_LOW_GRAV = 1 << 19; const PASS_MEDIA = 1 << 20; } } 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; } } bitflags! { /// The composite type of damage taken by something. #[cfg_attr(feature = "serde_obj", derive(serde::Serialize))] pub struct DamageTypeFlags: u32 { const EXPLOSION = 1; const ELECTRICAL_STAFF = 1 << 1; const PROJECTILE = 1 << 2; const ABSORBED = 1 << 3; const FLAME = 1 << 4; const HOUND_CLAWS = 1 << 5; const ALIEN_PROJECTILE = 1 << 6; const HULK_SLAP = 1 << 7; const COMPILER_BOLT = 1 << 8; const FUSION_BOLT = 1 << 9; const HUNTER_BOLT = 1 << 10; const FIST = 1 << 11; const TELEPORTER = 1 << 12; const DEFENDER = 1 << 13; const YETI_CLAWS = 1 << 14; const YETI_PROJECTILE = 1 << 15; const CRUSHING = 1 << 16; const LAVA = 1 << 17; const SUFFOCATION = 1 << 18; const GOO = 1 << 19; const ENERGY_DRAIN = 1 << 20; const OXYGEN_DRAIN = 1 << 21; const HUMMER_BOLT = 1 << 22; const SHOTGUN_PROJECTILE = 1 << 23; } } c_enum! { /// A bullet shell casing emitted by a weapon. #[cfg_attr(feature = "serde_obj", derive(serde::Serialize))] #[derive(Debug)] pub enum CasingType: u16 { Rifle = 0, Pistol = 1, PistolLeft = 2, PistolRight = 3, SMG = 4, None = 0xFFFF, } } c_enum! { /// The type of functionality a weapon provides. #[cfg_attr(feature = "serde_obj", derive(serde::Serialize))] #[derive(Debug)] pub enum WeaponType: u16 { Melee = 0, Normal = 1, DualFunc = 2, DualPistol = 3, Multipurpose = 4, } } c_enum! { /// A named type of damage taken by something. #[cfg_attr(feature = "serde_obj", derive(serde::Serialize))] #[derive(Debug)] pub enum DamageType: u16 { Explosion = 0, ElectricalStaff = 1, Projectile = 2, Absorbed = 3, Flame = 4, HoundClaws = 5, AlienProjectile = 6, HulkSlap = 7, CompilerBolt = 8, FusionBolt = 9, HunterBolt = 10, Fist = 11, Teleporter = 12, Defender = 13, YetiClaws = 14, YetiProjectile = 15, Crushing = 16, Lava = 17, Suffocation = 18, Goo = 19, EnergyDrain = 20, OxygenDrain = 21, HummerBolt = 22, ShotgunProjectile = 23, None = 0xFFFF, } } // EOF