// TOOD: `pub(super) for most of these?` use std::io::{Read}; use byteorder::{LittleEndian, ReadBytesExt}; use thiserror::Error; use crate::ship::monster::MonsterType; use crate::ship::room::Episode; use crate::ship::map::*; #[derive(Debug, Copy, Clone)] pub struct RawMapEnemy { id: u32, _unknown1: u16, pub children: u16, map_area: u16, _unknown4: u16, section: u16, wave_idd: u16, wave_id: u32, x: f32, y: f32, z: f32, xrot: u32, yrot: u32, zrot: u32, _field1: u32, field2: u32, _field3: u32, _field4: u32, _field5: u32, skin: u32, _field6: u32 } impl RawMapEnemy { pub fn from_byte_stream(cursor: &mut R) -> Result { Ok(RawMapEnemy { id: cursor.read_u32::()?, _unknown1: cursor.read_u16::()?, children: cursor.read_u16::()?, map_area: cursor.read_u16::()?, _unknown4: cursor.read_u16::()?, section: cursor.read_u16::()?, wave_idd: cursor.read_u16::()?, wave_id: cursor.read_u32::()?, x: cursor.read_f32::()?, y: cursor.read_f32::()?, z: cursor.read_f32::()?, xrot: cursor.read_u32::()?, yrot: cursor.read_u32::()?, zrot: cursor.read_u32::()?, _field1: cursor.read_u32::()?, field2: cursor.read_u32::()?, _field3: cursor.read_u32::()?, _field4: cursor.read_u32::()?, _field5: cursor.read_u32::()?, skin: cursor.read_u32::()?, _field6: cursor.read_u32::()?, }) } } #[derive(Error, Debug)] #[error("")] pub enum MapEnemyError { UnknownEnemyId(u32), MapAreaError(#[from] MapAreaError), } #[derive(Debug, Copy, Clone)] pub struct MapEnemy { pub monster: MonsterType, pub map_area: MapArea, hp: u32, // TODO: other stats from battleparam pub player_hit: [bool; 4], pub dropped_item: bool, pub gave_exp: bool, } impl MapEnemy { pub fn from_raw(enemy: RawMapEnemy, episode: &Episode, map_area: &MapArea /*, battleparam */) -> Result { // TODO: rare enemies ep1-4, tower lilys, event rappies, ult variants? let monster = match map_area { MapArea::Forest1 | MapArea::Forest2 | MapArea::Dragon | MapArea::Caves1 | MapArea::Caves2 | MapArea::Caves3 | MapArea::DeRolLe | MapArea::Mines1 | MapArea::Mines2 | MapArea::VolOpt | MapArea::Ruins1 | MapArea::Ruins2 | MapArea::Ruins3 | MapArea::DarkFalz => { match (enemy, episode) { (RawMapEnemy {id: 64, ..}, _) => MonsterType::Hildebear, // (RawMapEnemy {id: 64, ..}, _) => MonsterType::Hildeblue, (RawMapEnemy {id: 65, ..}, _) => MonsterType::RagRappy, // (RawMapEnemy {id: 65, ..}, _) => MonsterType::AlRappy, (RawMapEnemy {id: 66, ..}, _) => MonsterType::Monest, (RawMapEnemy {id: 67, field2: 0, ..}, _) => MonsterType::SavageWolf, (RawMapEnemy {id: 67, ..}, _) => MonsterType::BarbarousWolf, (RawMapEnemy {id: 68, skin: 0, ..}, _) => MonsterType::Booma, (RawMapEnemy {id: 68, skin: 1, ..}, _) => MonsterType::Gobooma, (RawMapEnemy {id: 68, skin: 2, ..}, _) => MonsterType::Gigobooma, (RawMapEnemy {id: 96, ..}, _) => MonsterType::GrassAssassin, (RawMapEnemy {id: 97, ..}, _) => MonsterType::PoisonLily, // (RawMapEnemy {id: 97, ..}, _) => MonsterType::NarLily, (RawMapEnemy {id: 98, ..}, _) => MonsterType::NanoDragon, (RawMapEnemy {id: 99, skin: 0, ..}, _) => MonsterType::EvilShark, (RawMapEnemy {id: 99, skin: 1, ..}, _) => MonsterType::PalShark, (RawMapEnemy {id: 99, skin: 2, ..}, _) => MonsterType::GuilShark, (RawMapEnemy {id: 100, ..}, _) => MonsterType::PofuillySlime, // (RawMapEnemy {id: 100, ..}, _) => MonsterType::PouillySlime, (RawMapEnemy {id: 101, ..}, _) => MonsterType::PanArms, (RawMapEnemy {id: 128, skin: 0, ..}, _) => MonsterType::Dubchic, (RawMapEnemy {id: 128, skin: 1, ..}, _) => MonsterType::Gillchic, (RawMapEnemy {id: 129, ..}, _) => MonsterType::Garanz, (RawMapEnemy {id: 130, field2: 0, ..}, _) => MonsterType::SinowBeat, (RawMapEnemy {id: 130, ..}, _) => MonsterType::SinowGold, (RawMapEnemy {id: 131, ..}, _) => MonsterType::Canadine, (RawMapEnemy {id: 132, ..}, _) => MonsterType::Canane, (RawMapEnemy {id: 133, ..}, _) => MonsterType::Dubwitch, (RawMapEnemy {id: 160, ..}, _) => MonsterType::Delsaber, (RawMapEnemy {id: 161, ..}, _) => MonsterType::ChaosSorcerer, (RawMapEnemy {id: 162, ..}, _) => MonsterType::DarkGunner, (RawMapEnemy {id: 164, ..}, _) => MonsterType::ChaosBringer, (RawMapEnemy {id: 165, ..}, _) => MonsterType::DarkBelra, (RawMapEnemy {id: 166, skin: 0, ..}, _) => MonsterType::Dimenian, (RawMapEnemy {id: 166, skin: 1, ..}, _) => MonsterType::LaDimenian, (RawMapEnemy {id: 166, skin: 2, ..}, _) => MonsterType::SoDimenian, (RawMapEnemy {id: 167, ..}, _) => MonsterType::Bulclaw, (RawMapEnemy {id: 168, ..}, _) => MonsterType::Claw, (RawMapEnemy {id: 192, ..}, Episode::One) => MonsterType::Dragon, (RawMapEnemy {id: 193, ..}, _) => MonsterType::DeRolLe, (RawMapEnemy {id: 194, ..}, _) => MonsterType::VolOptPartA, (RawMapEnemy {id: 197, ..}, _) => MonsterType::VolOpt, (RawMapEnemy {id: 200, ..}, _) => MonsterType::DarkFalz, _ => return Err(MapEnemyError::UnknownEnemyId(enemy.id)) } }, MapArea::VrTempleAlpha | MapArea::VrTempleBeta | MapArea::BarbaRay | MapArea::VrSpaceshipAlpha | MapArea::VrSpaceshipBeta | MapArea::GolDragon | MapArea::JungleAreaNorth | MapArea::JungleAreaEast | MapArea::Mountain | MapArea::Seaside | MapArea::SeasideNight | MapArea::Cca | MapArea::GalGryphon | MapArea::SeabedUpper | MapArea::SeabedLower | MapArea::OlgaFlow => { match (enemy, episode) { (RawMapEnemy {id: 64, ..}, _) => MonsterType::Hildebear, // (RawMapEnemy {id: 64, ..}, _) => MonsterType::Hildeblue, (RawMapEnemy {id: 65, ..}, _) => MonsterType::RagRappy, // (RawMapEnemy {id: 65, ..}, _) => MonsterType::EventRappy, (RawMapEnemy {id: 66, ..}, _) => MonsterType::Monest, (RawMapEnemy {id: 67, field2: 0, ..}, _) => MonsterType::SavageWolf, (RawMapEnemy {id: 67, ..}, _) => MonsterType::BarbarousWolf, (RawMapEnemy {id: 96, ..}, _) => MonsterType::GrassAssassin, (RawMapEnemy {id: 97, ..}, _) => MonsterType::PoisonLily, // (RawMapEnemy {id: 97, ..}, _) => MonsterType::NarLily, // (RawMapEnemy {id: 97, ..}, _) => MonsterType::DelLily, (RawMapEnemy {id: 101, ..}, _) => MonsterType::PanArms, (RawMapEnemy {id: 128, skin: 0, ..}, _) => MonsterType::Dubchic, (RawMapEnemy {id: 128, skin: 1, ..}, _) => MonsterType::Gillchic, (RawMapEnemy {id: 129, ..}, _) => MonsterType::Garanz, (RawMapEnemy {id: 133, ..}, _) => MonsterType::Dubwitch, (RawMapEnemy {id: 160, ..}, _) => MonsterType::Delsaber, (RawMapEnemy {id: 161, ..}, _) => MonsterType::ChaosSorcerer, (RawMapEnemy {id: 165, ..}, _) => MonsterType::DarkBelra, (RawMapEnemy {id: 166, skin: 0, ..}, _) => MonsterType::Dimenian, (RawMapEnemy {id: 166, skin: 1, ..}, _) => MonsterType::LaDimenian, (RawMapEnemy {id: 166, skin: 2, ..}, _) => MonsterType::SoDimenian, (RawMapEnemy {id: 192, ..}, Episode::Two) => MonsterType::GalGryphon, (RawMapEnemy {id: 202, ..}, _) => MonsterType::OlgaFlow, (RawMapEnemy {id: 203, ..}, _) => MonsterType::BarbaRay, (RawMapEnemy {id: 204, ..}, _) => MonsterType::GolDragon, (RawMapEnemy {id: 212, skin: 0, ..}, _) => MonsterType::SinowBerill, (RawMapEnemy {id: 212, skin: 1, ..}, _) => MonsterType::SinowSpigell, (RawMapEnemy {id: 213, skin: 0, ..}, _) => MonsterType::Merillia, (RawMapEnemy {id: 213, skin: 1, ..}, _) => MonsterType::Meriltas, (RawMapEnemy {id: 214, skin: 0, ..}, _) => MonsterType::Mericarol, (RawMapEnemy {id: 214, skin: 1, ..}, _) => MonsterType::Merikle, (RawMapEnemy {id: 214, skin: 2, ..}, _) => MonsterType::Mericus, (RawMapEnemy {id: 215, skin: 0, ..}, _) => MonsterType::UlGibbon, (RawMapEnemy {id: 215, skin: 1, ..}, _) => MonsterType::ZolGibbon, (RawMapEnemy {id: 216, ..}, _) => MonsterType::Gibbles, (RawMapEnemy {id: 217, ..}, _) => MonsterType::Gee, (RawMapEnemy {id: 218, ..}, _) => MonsterType::GiGue, (RawMapEnemy {id: 219, ..}, _) => MonsterType::Deldepth, (RawMapEnemy {id: 220, ..}, _) => MonsterType::Delbiter, (RawMapEnemy {id: 221, skin: 0, ..}, _) => MonsterType::Dolmolm, (RawMapEnemy {id: 221, skin: 1, ..}, _) => MonsterType::Dolmdarl, (RawMapEnemy {id: 222, ..}, _) => MonsterType::Morfos, (RawMapEnemy {id: 223, ..}, _) => MonsterType::Recobox, (RawMapEnemy {id: 224, skin: 0, ..}, _) => MonsterType::SinowZoa, (RawMapEnemy {id: 224, skin: 1, ..}, _) => MonsterType::SinowZele, (RawMapEnemy {id: 224, ..}, _) => MonsterType::Epsilon, (RawMapEnemy {id: 225, ..}, _) => MonsterType::IllGill, _ => return Err(MapEnemyError::UnknownEnemyId(enemy.id)) } }, MapArea::Tower => { match (enemy, episode) { (RawMapEnemy {id: 97, ..}, _) => MonsterType::DelLily, (RawMapEnemy {id: 214, skin: 0, ..}, _) => MonsterType::Mericarol, (RawMapEnemy {id: 214, skin: 1, ..}, _) => MonsterType::Merikle, (RawMapEnemy {id: 214, skin: 2, ..}, _) => MonsterType::Mericus, (RawMapEnemy {id: 216, ..}, _) => MonsterType::Gibbles, (RawMapEnemy {id: 218, ..}, _) => MonsterType::GiGue, (RawMapEnemy {id: 220, ..}, _) => MonsterType::Delbiter, (RawMapEnemy {id: 223, ..}, _) => MonsterType::Recobox, (RawMapEnemy {id: 224, ..}, _) => MonsterType::Epsilon, (RawMapEnemy {id: 225, ..}, _) => MonsterType::IllGill, _ => return Err(MapEnemyError::UnknownEnemyId(enemy.id)) } }, MapArea::CraterEast | MapArea::CraterWest | MapArea::CraterSouth | MapArea::CraterNorth | MapArea::CraterInterior => { match (enemy, episode) { (RawMapEnemy {id: 65, ..}, Episode::Four) => MonsterType::SandRappyCrater, // (RawMapEnemy {id: 65, ..}, Episode::Four) => MonsterType::DelRappyCrater, (RawMapEnemy {id: 272, ..}, _) => MonsterType::Astark, (RawMapEnemy {id: 273, field2: 0, ..}, _) => MonsterType::SatelliteLizardCrater, (RawMapEnemy {id: 273, ..}, _) => MonsterType::YowieCrater, (RawMapEnemy {id: 276, ..}, _) => MonsterType::ZuCrater, // (RawMapEnemy {id: 276, ..}, _) => MonsterType::PazuzuCrater, (RawMapEnemy {id: 277, skin: 0, ..}, _) => MonsterType::Boota, (RawMapEnemy {id: 277, skin: 1, ..}, _) => MonsterType::ZeBoota, (RawMapEnemy {id: 277, skin: 2, ..}, _) => MonsterType::BaBoota, (RawMapEnemy {id: 278, ..}, _) => MonsterType::Dorphon, // (RawMapEnemy {id: 278, ..}, _) => MonsterType::DorphonEclair, _ => return Err(MapEnemyError::UnknownEnemyId(enemy.id)) } }, MapArea::SubDesert1 | MapArea::SubDesert2 | MapArea::SubDesert3 | MapArea::SaintMillion => { match (enemy, episode) { (RawMapEnemy {id: 65, ..}, Episode::Four) => MonsterType::SandRappyDesert, // (RawMapEnemy {id: 65, ..}, Episode::Four) => MonsterType::DelRappyDesert, (RawMapEnemy {id: 273, field2: 0, ..}, _) => MonsterType::SatelliteLizardDesert, (RawMapEnemy {id: 273, ..}, _) => MonsterType::YowieDesert, (RawMapEnemy {id: 274, ..}, _) => MonsterType::MerissaA, // (RawMapEnemy {id: 274, ..}, _) => MonsterType::MerissaAA, (RawMapEnemy {id: 275, ..}, _) => MonsterType::Girtablulu, (RawMapEnemy {id: 276, ..}, _) => MonsterType::ZuDesert, // (RawMapEnemy {id: 276, ..}, _) => MonsterType::PazuzuDesert, (RawMapEnemy {id: 279, skin: 0, ..}, _) => MonsterType::Goran, (RawMapEnemy {id: 279, skin: 1, ..}, _) => MonsterType::PyroGoran, (RawMapEnemy {id: 279, skin: 2, ..}, _) => MonsterType::GoranDetonator, (RawMapEnemy {id: 281, skin: 0, ..}, _) => MonsterType::SaintMillion, (RawMapEnemy {id: 281, skin: 1, ..}, _) => MonsterType::Shambertin, // (RawMapEnemy {id: 281, skin: 1, ..}, _) => MonsterType::Kondrieu, _ => return Err(MapEnemyError::UnknownEnemyId(enemy.id)) } }, _ => return Err(MapEnemyError::UnknownEnemyId(enemy.id)) }; Ok(MapEnemy { monster, map_area: *map_area, hp: 0, dropped_item: false, gave_exp: false, player_hit: [false; 4], }) } pub fn new(monster: MonsterType, map_area: MapArea) -> MapEnemy { MapEnemy { monster, map_area, hp: 0, dropped_item: false, gave_exp: false, player_hit: [false; 4], } } }