#![allow(dead_code)] use std::convert::Into; use std::path::PathBuf; use std::io::{Read}; use std::fs::File; use byteorder::{LittleEndian, ReadBytesExt}; use rand::Rng; use thiserror::Error; use crate::ship::monster::MonsterType; use crate::ship::room::{Episode, RoomMode}; #[derive(Debug, Copy, Clone)] struct RawMapEnemy { id: u32, _unknown1: u16, 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 { 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("")] 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 dropped_item: bool, pub gave_exp: bool, } impl MapEnemy { 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: monster, map_area: map_area.clone(), hp: 0, dropped_item: false, gave_exp: false, }) } fn new(monster: MonsterType, map_area: MapArea) -> MapEnemy { MapEnemy { monster: monster, map_area: map_area, hp: 0, dropped_item: false, gave_exp: false, } } } #[derive(Debug, Copy, Clone)] struct RawMapObject { otype: u16, unknown1: u16, unknown2: u32, id: u16, group: u16, section: u16, unknown3: u16, x: f32, y: f32, z: f32, xrot: u32, yrot: u32, zrot: u32, field1: f32, field2: f32, field3: f32, field4: u32, field5: u32, field6: u32, field7: u32, } impl RawMapObject { fn from_byte_stream(cursor: &mut R) -> Result { Ok(RawMapObject { otype: cursor.read_u16::()?, unknown1: cursor.read_u16::()?, unknown2: cursor.read_u32::()?, id: cursor.read_u16::()?, group: cursor.read_u16::()?, section: cursor.read_u16::()?, unknown3: cursor.read_u16::()?, 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_f32::()?, field2: cursor.read_f32::()?, field3: cursor.read_f32::()?, field4: cursor.read_u32::()?, field5: cursor.read_u32::()?, field6: cursor.read_u32::()?, field7: cursor.read_u32::()?, }) } } #[derive(Debug, Copy, Clone)] pub enum FixedBoxDropType { Weapon, Armor, Tool, Meseta, Random, Specific(u32), // TODO: ItemDropType } impl FixedBoxDropType { fn from_object(field1: f32, field2: f32, field3: f32, field4: u32) -> FixedBoxDropType { match (field1.round() as i32, field2.round() as i32, field3.round() as i32, field4) { (0, 1, 1, 0) => { FixedBoxDropType::Random }, (0, 1, _, 0x4000000) => { FixedBoxDropType::Meseta }, (0, 1, 1, _) => { FixedBoxDropType::Specific(field4) }, (-1, 1, 1, _) => { // ??????? FixedBoxDropType::Specific(field4) }, (0, 1, 0, 0) => { FixedBoxDropType::Weapon }, (0, 1, 0, 0x1000000) => { FixedBoxDropType::Armor }, (0, 1, 0, 0x3000000) => { FixedBoxDropType::Tool }, (1, _, _, _) => { FixedBoxDropType::Random }, _ => { println!("this box state should not occur? {} {} {} {}", field1.round() as i32, field2.round() as i32, field3.round() as i32, field4); FixedBoxDropType::Random } } } } #[derive(Debug, Copy, Clone)] pub enum MapObjectType { Box, FixedBox(FixedBoxDropType), EnemyBox, EnemyFixedBox(FixedBoxDropType), RuinsBox, RuinsFixedBox(FixedBoxDropType), RuinsEnemyBox, RuinsEnemyFixedBox(FixedBoxDropType), CcaBox, CcaFixedBox(FixedBoxDropType), EmptyBox, EmptyFixedBox(FixedBoxDropType), RuinsEmptyBox, RuinsEmptyFixedBox, } #[derive(Debug, Copy, Clone)] pub struct MapObject { pub object: MapObjectType, pub map: MapArea, pub dropped_item: bool, //id: u32, } #[derive(Debug, Copy, Clone)] enum MapObjectError { UnknownObjectType(u16, RawMapObject), } impl MapObject { fn from_raw(raw: RawMapObject, episode: Episode, map_area: &MapArea) -> Result { let object = match (raw, episode) { (RawMapObject {otype: 136, ..}, _) => MapObjectType::Box, (RawMapObject {otype: 145, ..}, _) => MapObjectType::EnemyBox, (RawMapObject {otype: 146, ..}, _) => MapObjectType::FixedBox(FixedBoxDropType::from_object(raw.field1, raw.field2, raw.field3, raw.field4)), (RawMapObject {otype: 147, ..}, _) => MapObjectType::EnemyFixedBox(FixedBoxDropType::from_object(raw.field1, raw.field2, raw.field3, raw.field4)), (RawMapObject {otype: 149, ..}, _) => MapObjectType::EmptyFixedBox(FixedBoxDropType::from_object(raw.field1, raw.field2, raw.field3, raw.field4)), (RawMapObject {otype: 353, ..}, _) => MapObjectType::RuinsFixedBox(FixedBoxDropType::from_object(raw.field1, raw.field2, raw.field3, raw.field4)), (RawMapObject {otype: 354, ..}, _) => MapObjectType::RuinsBox, (RawMapObject {otype: 355, ..}, _) => MapObjectType::RuinsEnemyFixedBox(FixedBoxDropType::from_object(raw.field1, raw.field2, raw.field3, raw.field4)), (RawMapObject {otype: 356, ..}, _) => MapObjectType::RuinsEnemyBox, (RawMapObject {otype: 357, ..}, _) => MapObjectType::RuinsEmptyBox, (RawMapObject {otype: 512, ..}, _) => MapObjectType::CcaBox, (RawMapObject {otype: 515, ..}, _) => MapObjectType::CcaFixedBox(FixedBoxDropType::from_object(raw.field1, raw.field2, raw.field3, raw.field4)), _ => return Err(MapObjectError::UnknownObjectType(raw.otype, raw)) }; Ok(MapObject { object: object, map: map_area.clone(), dropped_item: false, }) } } #[derive(Debug, PartialEq)] enum MapVariantMode { Online, Offline, } #[derive(Debug, Copy, Clone, PartialEq)] pub enum MapArea { Pioneer2Ep1, Forest1, Forest2, Caves1, Caves2, Caves3, Mines1, Mines2, Ruins1, Ruins2, Ruins3, Dragon, DeRolLe, VolOpt, DarkFalz, Pioneer2Ep2, VrTempleAlpha, VrTempleBeta, VrSpaceshipAlpha, VrSpaceshipBeta, Cca, JungleAreaNorth, JungleAreaEast, Mountain, Seaside, SeabedUpper, SeabedLower, GalGryphon, OlgaFlow, BarbaRay, GolDragon, SeasideNight, Tower, Pioneer2Ep4, CraterEast, CraterWest, CraterSouth, CraterNorth, CraterInterior, SubDesert1, SubDesert2, SubDesert3, SaintMillion, // TestMapEp4, } #[derive(Error, Debug)] #[error("")] pub enum MapAreaError { UnknownMapArea(u32), } impl MapArea { pub fn from_value(episode: &Episode, area: u32) -> Result { match (episode, area) { (Episode::One, 0) => Ok(MapArea::Pioneer2Ep1), (Episode::One, 1) => Ok(MapArea::Forest1), (Episode::One, 2) => Ok(MapArea::Forest2), (Episode::One, 3) => Ok(MapArea::Caves1), (Episode::One, 4) => Ok(MapArea::Caves2), (Episode::One, 5) => Ok(MapArea::Caves3), (Episode::One, 6) => Ok(MapArea::Mines1), (Episode::One, 7) => Ok(MapArea::Mines2), (Episode::One, 8) => Ok(MapArea::Ruins1), (Episode::One, 9) => Ok(MapArea::Ruins2), (Episode::One, 10) => Ok(MapArea::Ruins3), (Episode::One, 11) => Ok(MapArea::Dragon), (Episode::One, 12) => Ok(MapArea::DeRolLe), (Episode::One, 13) => Ok(MapArea::VolOpt), (Episode::One, 14) => Ok(MapArea::DarkFalz), (Episode::Two, 0) => Ok(MapArea::Pioneer2Ep2), (Episode::Two, 1) => Ok(MapArea::VrTempleAlpha), (Episode::Two, 2) => Ok(MapArea::VrTempleBeta), (Episode::Two, 3) => Ok(MapArea::VrSpaceshipAlpha), (Episode::Two, 4) => Ok(MapArea::VrSpaceshipBeta), (Episode::Two, 5) => Ok(MapArea::Cca), (Episode::Two, 6) => Ok(MapArea::JungleAreaNorth), (Episode::Two, 7) => Ok(MapArea::JungleAreaEast), (Episode::Two, 8) => Ok(MapArea::Mountain), (Episode::Two, 9) => Ok(MapArea::Seaside), (Episode::Two, 10) => Ok(MapArea::SeabedUpper), (Episode::Two, 11) => Ok(MapArea::SeabedLower), (Episode::Two, 12) => Ok(MapArea::GalGryphon), (Episode::Two, 13) => Ok(MapArea::OlgaFlow), (Episode::Two, 14) => Ok(MapArea::BarbaRay), (Episode::Two, 15) => Ok(MapArea::GolDragon), (Episode::Two, 16) => Ok(MapArea::SeasideNight), (Episode::Two, 17) => Ok(MapArea::Tower), (Episode::Four, 0) => Ok(MapArea::Pioneer2Ep4), (Episode::Four, 1) => Ok(MapArea::CraterEast), (Episode::Four, 2) => Ok(MapArea::CraterWest), (Episode::Four, 3) => Ok(MapArea::CraterSouth), (Episode::Four, 4) => Ok(MapArea::CraterNorth), (Episode::Four, 5) => Ok(MapArea::CraterInterior), (Episode::Four, 6) => Ok(MapArea::SubDesert1), (Episode::Four, 7) => Ok(MapArea::SubDesert2), (Episode::Four, 8) => Ok(MapArea::SubDesert3), (Episode::Four, 9) => Ok(MapArea::SaintMillion), // (Episode::Four, 10) => Ok(MapArea::TestMapEp4), _ => Err(MapAreaError::UnknownMapArea(area)) } } pub fn drop_area_value(&self) -> Option { match self { MapArea::Forest1 => Some(0), MapArea::Forest2 => Some(1), MapArea::Caves1 => Some(2), MapArea::Caves2 => Some(3), MapArea::Caves3 => Some(4), MapArea::Mines1 => Some(5), MapArea::Mines2 => Some(6), MapArea::Ruins1 => Some(7), MapArea::Ruins2 => Some(8), MapArea::Ruins3 => Some(9), MapArea::Dragon => Some(2), MapArea::DeRolLe => Some(5), MapArea::VolOpt => Some(7), MapArea::DarkFalz => Some(9), MapArea::VrTempleAlpha => Some(0), MapArea::VrTempleBeta => Some(1), MapArea::VrSpaceshipAlpha => Some(2), MapArea::VrSpaceshipBeta => Some(3), MapArea::Cca => Some(4), MapArea::JungleAreaNorth => Some(5), MapArea::JungleAreaEast => Some(5), MapArea::Mountain => Some(6), MapArea::Seaside => Some(7), MapArea::SeabedUpper => Some(8), MapArea::SeabedLower => Some(9), MapArea::GalGryphon => Some(8), MapArea::OlgaFlow => Some(9), MapArea::BarbaRay => Some(2), MapArea::GolDragon => Some(5), MapArea::SeasideNight => Some(7), MapArea::Tower => Some(9), MapArea::CraterEast => Some(2), MapArea::CraterWest => Some(3), MapArea::CraterSouth => Some(4), MapArea::CraterNorth => Some(5), MapArea::CraterInterior => Some(6), MapArea::SubDesert1 => Some(7), MapArea::SubDesert2 => Some(8), MapArea::SubDesert3 => Some(9), MapArea::SaintMillion => Some(9), // MapArea::TestMapEp4 => Some(0), _ => None } } pub fn area_value(&self) -> u8 { match self { MapArea::Pioneer2Ep1 => 0, MapArea::Forest1 => 1, MapArea::Forest2 => 2, MapArea::Caves1 => 3, MapArea::Caves2 => 4, MapArea::Caves3 => 5, MapArea::Mines1 => 6, MapArea::Mines2 => 7, MapArea::Ruins1 => 8, MapArea::Ruins2 => 9, MapArea::Ruins3 => 10, MapArea::Dragon => 11, MapArea::DeRolLe => 12, MapArea::VolOpt => 13, MapArea::DarkFalz => 14, MapArea::Pioneer2Ep2 => 0, MapArea::VrTempleAlpha => 1, MapArea::VrTempleBeta => 2, MapArea::VrSpaceshipAlpha => 3, MapArea::VrSpaceshipBeta => 4, MapArea::Cca => 5, MapArea::JungleAreaNorth => 6, MapArea::JungleAreaEast => 7, MapArea::Mountain => 8, MapArea::Seaside => 9, MapArea::SeabedUpper => 10, MapArea::SeabedLower => 11, MapArea::GalGryphon => 12, MapArea::OlgaFlow => 13, MapArea::BarbaRay => 14, MapArea::GolDragon => 15, MapArea::SeasideNight => 16, MapArea::Tower => 17, MapArea::Pioneer2Ep4 => 0, MapArea::CraterEast => 1, MapArea::CraterWest => 2, MapArea::CraterSouth => 3, MapArea::CraterNorth => 4, MapArea::CraterInterior => 5, MapArea::SubDesert1 => 6, MapArea::SubDesert2 => 7, MapArea::SubDesert3 => 8, MapArea::SaintMillion => 9, // MapArea::TestMapEp4 => 10, } } } #[derive(Debug)] struct MapVariant { map: MapArea, mode: MapVariantMode, major: u8, minor: u8, } impl MapVariant { fn new(map: MapArea, mode: MapVariantMode) -> MapVariant { if mode == MapVariantMode::Online { let major = match map { MapArea::Pioneer2Ep1 => 0, MapArea::Forest1 | MapArea::Forest2 => 0, MapArea::Caves1 | MapArea::Caves2 | MapArea::Caves3 => rand::thread_rng().gen_range(0, 3), MapArea::Mines1 | MapArea::Mines2 => rand::thread_rng().gen_range(0, 3), MapArea::Ruins1 | MapArea::Ruins2 | MapArea::Ruins3 => rand::thread_rng().gen_range(0, 3), MapArea::Dragon | MapArea::DeRolLe | MapArea::VolOpt | MapArea::DarkFalz => 0, MapArea::Pioneer2Ep2 => 0, MapArea::VrTempleAlpha | MapArea::VrTempleBeta => rand::thread_rng().gen_range(0, 2), MapArea::VrSpaceshipAlpha | MapArea::VrSpaceshipBeta => rand::thread_rng().gen_range(0, 2), MapArea::Cca => 0, MapArea::JungleAreaNorth | MapArea::JungleAreaEast => 0, MapArea::Mountain => rand::thread_rng().gen_range(0, 2), MapArea::Seaside | MapArea::SeasideNight=> 0, MapArea::SeabedUpper | MapArea::SeabedLower => rand::thread_rng().gen_range(0, 2), MapArea::BarbaRay | MapArea::GolDragon | MapArea::GalGryphon | MapArea::OlgaFlow => 0, MapArea::Tower => 0, MapArea::Pioneer2Ep4 => 0, MapArea::CraterEast | MapArea::CraterWest | MapArea::CraterSouth | MapArea::CraterNorth | MapArea::CraterInterior => 0, MapArea::SubDesert1 | MapArea::SubDesert3 => rand::thread_rng().gen_range(0, 2), MapArea::SubDesert2 => 0, MapArea::SaintMillion => 0, }; let minor = match map { MapArea::Pioneer2Ep1 => 0, MapArea::Forest1 | MapArea::Forest2 => rand::thread_rng().gen_range(0, 5), MapArea::Caves1 | MapArea::Caves2 | MapArea::Caves3 => rand::thread_rng().gen_range(0, 2), MapArea::Mines1 | MapArea::Mines2 => rand::thread_rng().gen_range(0, 2), MapArea::Ruins1 | MapArea::Ruins2 | MapArea::Ruins3 => rand::thread_rng().gen_range(0, 2), MapArea::Dragon | MapArea::DeRolLe | MapArea::VolOpt | MapArea::DarkFalz => 0, MapArea::Pioneer2Ep2 => 0, MapArea::VrTempleAlpha | MapArea::VrTempleBeta => 0, MapArea::VrSpaceshipAlpha | MapArea::VrSpaceshipBeta => 0, MapArea::Cca => rand::thread_rng().gen_range(0, 3), MapArea::JungleAreaNorth | MapArea::JungleAreaEast => rand::thread_rng().gen_range(0, 3), MapArea::Mountain => rand::thread_rng().gen_range(0, 2), MapArea::Seaside | MapArea::SeasideNight => rand::thread_rng().gen_range(0, 3), MapArea::SeabedUpper | MapArea::SeabedLower => rand::thread_rng().gen_range(0, 2), MapArea::BarbaRay | MapArea::GolDragon | MapArea::GalGryphon | MapArea::OlgaFlow => 0, MapArea::Tower => 0, MapArea::Pioneer2Ep4 => 0, MapArea::CraterEast | MapArea::CraterWest | MapArea::CraterSouth | MapArea::CraterNorth | MapArea::CraterInterior => rand::thread_rng().gen_range(0, 3), MapArea::SubDesert1 | MapArea::SubDesert3 => 0, MapArea::SubDesert2 => rand::thread_rng().gen_range(0, 2), MapArea::SaintMillion => 0, }; MapVariant { map: map, mode: mode, major: major, minor: minor, } } else { let major = match map { MapArea::Pioneer2Ep1 => 0, MapArea::Forest1 | MapArea::Forest2 => 0, MapArea::Caves1 | MapArea::Caves2 | MapArea::Caves3 => rand::thread_rng().gen_range(0, 3), MapArea::Mines1 | MapArea::Mines2 => rand::thread_rng().gen_range(0, 3), MapArea::Ruins1 | MapArea::Ruins2 | MapArea::Ruins3 => rand::thread_rng().gen_range(0, 3), MapArea::Dragon | MapArea::DeRolLe | MapArea::VolOpt | MapArea::DarkFalz => 0, MapArea::Pioneer2Ep2 => 0, MapArea::VrTempleAlpha | MapArea::VrTempleBeta => rand::thread_rng().gen_range(0, 2), MapArea::VrSpaceshipAlpha | MapArea::VrSpaceshipBeta => rand::thread_rng().gen_range(0, 2), MapArea::Cca => 0, MapArea::JungleAreaNorth | MapArea::JungleAreaEast => 0, MapArea::Mountain => rand::thread_rng().gen_range(0, 2), MapArea::Seaside | MapArea::SeasideNight => 0, MapArea::SeabedUpper | MapArea::SeabedLower => rand::thread_rng().gen_range(0, 2), MapArea::BarbaRay | MapArea::GolDragon | MapArea::GalGryphon | MapArea::OlgaFlow => 0, MapArea::Tower => 0, MapArea::Pioneer2Ep4 => 0, MapArea::CraterEast | MapArea::CraterWest | MapArea::CraterSouth | MapArea::CraterNorth | MapArea::CraterInterior => 0, MapArea::SubDesert1 | MapArea::SubDesert3 => rand::thread_rng().gen_range(0, 2), MapArea::SubDesert2 => 0, MapArea::SaintMillion => 0, }; let minor = match map { MapArea::Pioneer2Ep1 => 0, MapArea::Forest1 | MapArea::Forest2 => rand::thread_rng().gen_range(0, 3), MapArea::Caves1 | MapArea::Caves2 | MapArea::Caves3 => 0, MapArea::Mines1 | MapArea::Mines2 => rand::thread_rng().gen_range(0, 2), MapArea::Ruins1 | MapArea::Ruins2 | MapArea::Ruins3 => rand::thread_rng().gen_range(0, 2), MapArea::Dragon | MapArea::DeRolLe | MapArea::VolOpt | MapArea::DarkFalz => 0, MapArea::Pioneer2Ep2 => 0, MapArea::VrTempleAlpha | MapArea::VrTempleBeta => 0, MapArea::VrSpaceshipAlpha | MapArea::VrSpaceshipBeta => 0, MapArea::Cca => rand::thread_rng().gen_range(0, 3), MapArea::JungleAreaNorth | MapArea::JungleAreaEast => rand::thread_rng().gen_range(0, 3), MapArea::Mountain => rand::thread_rng().gen_range(0, 2), MapArea::Seaside | MapArea::SeasideNight => rand::thread_rng().gen_range(0, 3), MapArea::SeabedUpper | MapArea::SeabedLower => 0, MapArea::BarbaRay | MapArea::GolDragon | MapArea::GalGryphon | MapArea::OlgaFlow => 0, MapArea::Tower => 0, MapArea::Pioneer2Ep4 => 0, MapArea::CraterEast | MapArea::CraterWest | MapArea::CraterSouth | MapArea::CraterNorth | MapArea::CraterInterior => rand::thread_rng().gen_range(0, 3), MapArea::SubDesert1 | MapArea::SubDesert3 => 0, MapArea::SubDesert2 => rand::thread_rng().gen_range(0, 2), MapArea::SaintMillion => 0, }; MapVariant { map: map, mode: mode, major: major, minor: minor, } } } // TODO: rename to npc_file fn dat_file(&self) -> String { if self.mode == MapVariantMode::Online { match self.map { MapArea::Pioneer2Ep1 => "data/maps/map_city00_00e.dat".into(), MapArea::Forest1 => format!("data/maps/map_forest01_0{}e.dat", self.minor), MapArea::Forest2 => format!("data/maps/map_forest02_0{}e.dat", self.minor), MapArea::Caves1 => format!("data/maps/map_cave01_0{}_0{}e.dat", self.major, self.minor), MapArea::Caves2 => format!("data/maps/map_cave02_0{}_0{}e.dat", self.major, self.minor), MapArea::Caves3 => format!("data/maps/map_cave03_0{}_0{}e.dat", self.major, self.minor), MapArea::Mines1 => format!("data/maps/map_machine01_0{}_0{}e.dat", self.major, self.minor), MapArea::Mines2 => format!("data/maps/map_machine02_0{}_0{}e.dat", self.major, self.minor), MapArea::Ruins1 => format!("data/maps/map_ancient01_0{}_0{}e.dat", self.major, self.minor), MapArea::Ruins2 => format!("data/maps/map_ancient02_0{}_0{}e.dat", self.major, self.minor), MapArea::Ruins3 => format!("data/maps/map_ancient03_0{}_0{}e.dat", self.major, self.minor), MapArea::Dragon => "data/maps/map_boss01e.dat".into(), MapArea::DeRolLe => "data/maps/map_boss02e.dat".into(), MapArea::VolOpt => "data/maps/map_boss03e.dat".into(), MapArea::DarkFalz => "data/maps/map_boss04e.dat".into(), MapArea::Pioneer2Ep2 => "data/maps/map_labo00_00e.dat".into(), MapArea::VrTempleAlpha => format!("data/maps/map_ruins01_0{}_0{}e.dat", self.major, self.minor), MapArea::VrTempleBeta => format!("data/maps/map_ruins02_0{}_0{}e.dat", self.major, self.minor), MapArea::VrSpaceshipAlpha => format!("data/maps/map_space01_0{}_0{}e.dat", self.major, self.minor), MapArea::VrSpaceshipBeta => format!("data/maps/map_space02_0{}_0{}e.dat", self.major, self.minor), MapArea::Cca => format!("data/maps/map_jungle01_0{}e.dat", self.minor), MapArea::JungleAreaNorth => format!("data/maps/map_jungle02_0{}e.dat", self.minor), MapArea::JungleAreaEast => format!("data/maps/map_jungle03_0{}e.dat", self.minor), MapArea::Mountain => format!("data/maps/map_jungle04_0{}_0{}e.dat", self.major, self.minor), MapArea::Seaside => format!("data/maps/map_jungle05_0{}e.dat", self.minor), MapArea::SeabedUpper => format!("data/maps/map_seabed01_0{}_0{}e.dat", self.major, self.minor), MapArea::SeabedLower => format!("data/maps/map_seabed02_0{}_0{}e.dat", self.major, self.minor), MapArea::GalGryphon => "data/maps/map_boss05e.dat".into(), MapArea::OlgaFlow => "data/maps/map_boss06e.dat".into(), MapArea::BarbaRay => "data/maps/map_boss07e.dat".into(), MapArea::GolDragon => "data/maps/map_boss08e.dat".into(), MapArea::Pioneer2Ep4 => "data/maps/map_city02_00_00e.dat".into(), MapArea::CraterEast => format!("data/maps/map_wilds01_00_0{}e.dat", self.minor), MapArea::CraterWest => format!("data/maps/map_wilds01_01_0{}e.dat", self.minor), MapArea::CraterSouth => format!("data/maps/map_wilds01_02_0{}e.dat", self.minor), MapArea::CraterNorth => format!("data/maps/map_wilds01_03_0{}e.dat", self.minor), MapArea::CraterInterior => format!("data/maps/map_crater01_00_0{}e.dat", self.minor), MapArea::SubDesert1 => format!("data/maps/map_desert01_0{}_0{}e.dat", self.major, self.minor), MapArea::SubDesert2 => format!("data/maps/map_desert02_0{}_0{}e.dat", self.major, self.minor), MapArea::SubDesert3 => format!("data/maps/map_desert03_0{}_0{}e.dat", self.major, self.minor), MapArea::SaintMillion => "data/maps/map_boss09_00_00e.dat".into(), _ => unreachable!(), } } else { // Offline match self.map { MapArea::Pioneer2Ep1 => "data/maps/map_city00_00e_s.dat".into(), MapArea::Forest1 => format!("data/maps/map_forest01_0{}_offe.dat", self.minor*2), MapArea::Forest2 => { match self.minor { 0 => format!("data/maps/map_forest02_00_offe.dat"), 1 => format!("data/maps/map_forest02_03_offe.dat"), 2 => format!("data/maps/map_forest02_04_offe.dat"), _ => unreachable!() }}, MapArea::Caves1 => format!("data/maps/map_cave01_0{}_0{}_offe.dat", self.major, self.minor), MapArea::Caves2 => format!("data/maps/map_cave02_0{}_0{}_offe.dat", self.major, self.minor), MapArea::Caves3 => format!("data/maps/map_cave03_0{}_0{}_offe.dat", self.major, self.minor), MapArea::Mines1 => format!("data/maps/map_machine01_0{}_0{}e.dat", self.major, self.minor), MapArea::Mines2 => format!("data/maps/map_machine02_0{}_0{}e.dat", self.major, self.minor), MapArea::Ruins1 => format!("data/maps/map_ancient01_0{}_0{}e.dat", self.major, self.minor), MapArea::Ruins2 => format!("data/maps/map_ancient02_0{}_0{}e.dat", self.major, self.minor), MapArea::Ruins3 => format!("data/maps/map_ancient03_0{}_0{}e.dat", self.major, self.minor), MapArea::Dragon => "data/maps/map_boss01e.dat".into(), MapArea::DeRolLe => "data/maps/map_boss02e.dat".into(), MapArea::VolOpt => "data/maps/map_boss03e.dat".into(), MapArea::DarkFalz => "data/maps/map_boss04e.dat".into(), MapArea::Pioneer2Ep2 => "data/maps/map_labo00_00e_s.dat".into(), MapArea::VrTempleAlpha => format!("data/maps/map_ruins01_0{}_0{}_offe.dat", self.major, self.minor), MapArea::VrTempleBeta => format!("data/maps/map_ruins02_0{}_0{}_offe.dat", self.major, self.minor), MapArea::VrSpaceshipAlpha => format!("data/maps/map_space01_0{}_0{}_offe.dat", self.major, self.minor), MapArea::VrSpaceshipBeta => format!("data/maps/map_space02_0{}_0{}_offe.dat", self.major, self.minor), MapArea::Cca => format!("data/maps/map_jungle01_0{}_offe.dat", self.minor), MapArea::JungleAreaNorth => format!("data/maps/map_jungle02_0{}_offe.dat", self.minor), MapArea::JungleAreaEast => format!("data/maps/map_jungle03_0{}_offe.dat", self.minor), MapArea::Mountain => format!("data/maps/map_jungle04_0{}_0{}_offe.dat", self.major, self.minor), MapArea::Seaside => format!("data/maps/map_jungle05_0{}_offe.dat", self.minor), MapArea::SeabedUpper => format!("data/maps/map_seabed01_0{}_0{}_offe.dat", self.major, self.minor), MapArea::SeabedLower => format!("data/maps/map_seabed02_0{}_0{}_offe.dat", self.major, self.minor), MapArea::GalGryphon => "data/maps/map_boss05e.dat".into(), MapArea::OlgaFlow => "data/maps/map_boss06e.dat".into(), MapArea::BarbaRay => "data/maps/map_boss07e.dat".into(), MapArea::GolDragon => "data/maps/map_boss08e.dat".into(), MapArea::Pioneer2Ep4 => "data/maps/map_city02_00_00e_s.dat".into(), MapArea::CraterEast => format!("data/maps/map_wilds01_00_0{}e.dat", self.minor), MapArea::CraterWest => format!("data/maps/map_wilds01_01_0{}e.dat", self.minor), MapArea::CraterSouth => format!("data/maps/map_wilds01_02_0{}e.dat", self.minor), MapArea::CraterNorth => format!("data/maps/map_wilds01_03_0{}e.dat", self.minor), MapArea::CraterInterior => format!("data/maps/map_crater01_00_0{}e.dat", self.minor), MapArea::SubDesert1 => format!("data/maps/map_desert01_0{}_0{}e.dat", self.major, self.minor), MapArea::SubDesert2 => format!("data/maps/map_desert02_0{}_0{}e.dat", self.major, self.minor), MapArea::SubDesert3 => format!("data/maps/map_desert03_0{}_0{}e.dat", self.major, self.minor), MapArea::SaintMillion => "data/maps/map_boss09_00_00e.dat".into(), _ => unreachable!(), } } } fn obj_file(&self) -> String { if self.mode == MapVariantMode::Online { match self.map { MapArea::Pioneer2Ep1 => "data/maps/map_city00_00o.dat".into(), MapArea::Forest1 => format!("data/maps/map_forest01_0{}o.dat", self.minor), MapArea::Forest2 => format!("data/maps/map_forest02_0{}o.dat", self.minor), MapArea::Caves1 => format!("data/maps/map_cave01_0{}_0{}o.dat", self.major, self.minor), MapArea::Caves2 => format!("data/maps/map_cave02_0{}_0{}o.dat", self.major, self.minor), MapArea::Caves3 => format!("data/maps/map_cave03_0{}_0{}o.dat", self.major, self.minor), MapArea::Mines1 => format!("data/maps/map_machine01_0{}_0{}o.dat", self.major, self.minor), MapArea::Mines2 => format!("data/maps/map_machine02_0{}_0{}o.dat", self.major, self.minor), MapArea::Ruins1 => format!("data/maps/map_ancient01_0{}_0{}o.dat", self.major, self.minor), MapArea::Ruins2 => format!("data/maps/map_ancient02_0{}_0{}o.dat", self.major, self.minor), MapArea::Ruins3 => format!("data/maps/map_ancient03_0{}_0{}o.dat", self.major, self.minor), MapArea::Dragon => "data/maps/map_boss01o.dat".into(), MapArea::DeRolLe => "data/maps/map_boss02o.dat".into(), MapArea::VolOpt => "data/maps/map_boss03o.dat".into(), MapArea::DarkFalz => "data/maps/map_boss04o.dat".into(), MapArea::Pioneer2Ep2 => "data/maps/map_labo00_00o.dat".into(), MapArea::VrTempleAlpha => format!("data/maps/map_ruins01_0{}_0{}o.dat", self.major, self.minor), MapArea::VrTempleBeta => format!("data/maps/map_ruins02_0{}_0{}o.dat", self.major, self.minor), MapArea::VrSpaceshipAlpha => format!("data/maps/map_space01_0{}_0{}o.dat", self.major, self.minor), MapArea::VrSpaceshipBeta => format!("data/maps/map_space02_0{}_0{}o.dat", self.major, self.minor), MapArea::Cca => format!("data/maps/map_jungle01_0{}o.dat", self.minor), MapArea::JungleAreaNorth => format!("data/maps/map_jungle02_0{}o.dat", self.minor), MapArea::JungleAreaEast => format!("data/maps/map_jungle03_0{}o.dat", self.minor), MapArea::Mountain => format!("data/maps/map_jungle04_0{}_0{}o.dat", self.major, self.minor), MapArea::Seaside => format!("data/maps/map_jungle05_0{}o.dat", self.minor), MapArea::SeabedUpper => format!("data/maps/map_seabed01_0{}_0{}o.dat", self.major, self.minor), MapArea::SeabedLower => format!("data/maps/map_seabed02_0{}_0{}o.dat", self.major, self.minor), MapArea::GalGryphon => "data/maps/map_boss05o.dat".into(), MapArea::OlgaFlow => "data/maps/map_boss06o.dat".into(), MapArea::BarbaRay => "data/maps/map_boss07o.dat".into(), MapArea::GolDragon => "data/maps/map_boss08o.dat".into(), MapArea::Pioneer2Ep4 => "data/maps/map_city02_00_00o.dat".into(), MapArea::CraterEast => format!("data/maps/map_wilds01_00_0{}o.dat", self.minor), MapArea::CraterWest => format!("data/maps/map_wilds01_01_0{}o.dat", self.minor), MapArea::CraterSouth => format!("data/maps/map_wilds01_02_0{}o.dat", self.minor), MapArea::CraterNorth => format!("data/maps/map_wilds01_03_0{}o.dat", self.minor), MapArea::CraterInterior => format!("data/maps/map_crater01_00_0{}o.dat", self.minor), MapArea::SubDesert1 => format!("data/maps/map_desert01_0{}_0{}o.dat", self.major, self.minor), MapArea::SubDesert2 => format!("data/maps/map_desert02_0{}_0{}o.dat", self.major, self.minor), MapArea::SubDesert3 => format!("data/maps/map_desert03_0{}_0{}o.dat", self.major, self.minor), MapArea::SaintMillion => "data/maps/map_boss09_00_00o.dat".into(), _ => unreachable!(), } } else { match self.map { MapArea::Pioneer2Ep1 => "data/maps/map_city00_00o_s.dat".into(), MapArea::Forest1 => format!("data/maps/map_forest01_0{}o.dat", self.minor*2), MapArea::Forest2 => { match self.minor { 0 => format!("data/maps/map_forest02_00o.dat"), 1 => format!("data/maps/map_forest02_03o.dat"), 2 => format!("data/maps/map_forest02_04o.dat"), _ => unreachable!() }}, MapArea::Caves1 => format!("data/maps/map_cave01_0{}_0{}o.dat", self.major, self.minor), MapArea::Caves2 => format!("data/maps/map_cave02_0{}_0{}o.dat", self.major, self.minor), MapArea::Caves3 => format!("data/maps/map_cave03_0{}_0{}o.dat", self.major, self.minor), MapArea::Mines1 => format!("data/maps/map_machine01_0{}_0{}o.dat", self.major, self.minor), MapArea::Mines2 => format!("data/maps/map_machine02_0{}_0{}o.dat", self.major, self.minor), MapArea::Ruins1 => format!("data/maps/map_ancient01_0{}_0{}o.dat", self.major, self.minor), MapArea::Ruins2 => format!("data/maps/map_ancient02_0{}_0{}o.dat", self.major, self.minor), MapArea::Ruins3 => format!("data/maps/map_ancient03_0{}_0{}o.dat", self.major, self.minor), MapArea::Dragon => "data/maps/map_boss01o.dat".into(), MapArea::DeRolLe => "data/maps/map_boss02o.dat".into(), MapArea::VolOpt => "data/maps/map_boss03o.dat".into(), MapArea::DarkFalz => "data/maps/map_boss04_offo.dat".into(), MapArea::Pioneer2Ep2 => "data/maps/map_labo00_00o_s.dat".into(), MapArea::VrTempleAlpha => format!("data/maps/map_ruins01_0{}_0{}_offo.dat", self.major, self.minor), MapArea::VrTempleBeta => format!("data/maps/map_ruins02_0{}_0{}_offo.dat", self.major, self.minor), MapArea::VrSpaceshipAlpha => format!("data/maps/map_space01_0{}_0{}_offo.dat", self.major, self.minor), MapArea::VrSpaceshipBeta => format!("data/maps/map_space02_0{}_0{}_offo.dat", self.major, self.minor), MapArea::Cca => format!("data/maps/map_jungle01_0{}_offo.dat", self.minor), MapArea::JungleAreaNorth => format!("data/maps/map_jungle02_0{}_offo.dat", self.minor), MapArea::JungleAreaEast => format!("data/maps/map_jungle03_0{}_offo.dat", self.minor), MapArea::Mountain => format!("data/maps/map_jungle04_0{}_0{}_offo.dat", self.major, self.minor), MapArea::Seaside => format!("data/maps/map_jungle05_0{}_offo.dat", self.minor), MapArea::SeabedUpper => format!("data/maps/map_seabed01_0{}_0{}_offo.dat", self.major, self.minor), MapArea::SeabedLower => format!("data/maps/map_seabed02_0{}_0{}_offo.dat", self.major, self.minor), MapArea::GalGryphon => "data/maps/map_boss05_offo.dat".into(), MapArea::OlgaFlow => "data/maps/map_boss06_offo.dat".into(), MapArea::BarbaRay => "data/maps/map_boss07_offo.dat".into(), MapArea::GolDragon => "data/maps/map_boss08_offo.dat".into(), MapArea::Pioneer2Ep4 => "data/maps/map_city02_00_00o_s.dat".into(), MapArea::CraterEast => format!("data/maps/map_wilds01_00_0{}o.dat", self.minor), MapArea::CraterWest => format!("data/maps/map_wilds01_01_0{}o.dat", self.minor), MapArea::CraterSouth => format!("data/maps/map_wilds01_02_0{}o.dat", self.minor), MapArea::CraterNorth => format!("data/maps/map_wilds01_03_0{}o.dat", self.minor), MapArea::CraterInterior => format!("data/maps/map_crater01_00_0{}o.dat", self.minor), MapArea::SubDesert1 => format!("data/maps/map_desert01_0{}_0{}o.dat", self.major, self.minor), MapArea::SubDesert2 => format!("data/maps/map_desert02_0{}_0{}o.dat", self.major, self.minor), MapArea::SubDesert3 => format!("data/maps/map_desert03_0{}_0{}o.dat", self.major, self.minor), MapArea::SaintMillion => "data/maps/map_boss09_00_00o.dat".into(), _ => unreachable!(), } } } fn pkt_header(&self) -> [u8; 2] { [self.major, self.minor] } } pub fn objects_from_stream(cursor: &mut impl Read, episode: &Episode, map_area: &MapArea) -> Vec> { let mut object_data = Vec::new(); while let Ok(raw_object) = RawMapObject::from_byte_stream(cursor) { let object = MapObject::from_raw(raw_object.clone(), *episode, map_area); object_data.push(object.ok()); } object_data } fn objects_from_map_data(path: PathBuf, episode: &Episode, map_area: &MapArea) -> Vec> { let mut cursor = File::open(path.clone()).unwrap(); objects_from_stream(&mut cursor, episode, map_area) } fn parse_enemy(episode: &Episode, map_area: &MapArea, raw_enemy: RawMapEnemy) -> Vec> { let enemy = MapEnemy::from_raw(raw_enemy, episode, map_area); enemy .map_or(vec![None], |monster| { let mut monsters = Vec::new(); monsters.push(Some(monster)); match monster.monster { MonsterType::Monest => { for _ in 0..30 { monsters.push(Some(MapEnemy::new(MonsterType::Mothmant, monster.map_area))); } }, MonsterType::PofuillySlime => { for _ in 0..4 { monsters.push(Some(MapEnemy::new(MonsterType::PofuillySlime, monster.map_area))); } }, MonsterType::PanArms => { monsters.push(Some(MapEnemy::new(MonsterType::Hidoom, monster.map_area))); monsters.push(Some(MapEnemy::new(MonsterType::Migium, monster.map_area))); }, MonsterType::SinowBeat => { for _ in 0..4 { monsters.push(Some(MapEnemy::new(MonsterType::SinowBeat, monster.map_area))); } }, MonsterType::SinowGold => { for _ in 0..4 { monsters.push(Some(MapEnemy::new(MonsterType::SinowGold, monster.map_area))); } }, MonsterType::Canane => { for _ in 0..8 { monsters.push(Some(MapEnemy::new(MonsterType::RingCanadine, monster.map_area))); } }, MonsterType::ChaosSorcerer => { monsters.push(Some(MapEnemy::new(MonsterType::BeeR, monster.map_area))); monsters.push(Some(MapEnemy::new(MonsterType::BeeL, monster.map_area))); }, MonsterType::Bulclaw => { for _ in 0..4 { monsters.push(Some(MapEnemy::new(MonsterType::Claw, monster.map_area))); } }, MonsterType::DeRolLe => { for _ in 0..10 { monsters.push(Some(MapEnemy::new(MonsterType::DeRolLeBody, monster.map_area))); } for _ in 0..9 { monsters.push(Some(MapEnemy::new(MonsterType::DeRolLeMine, monster.map_area))); } }, MonsterType::VolOptPartA => { for _ in 0..6 { monsters.push(Some(MapEnemy::new(MonsterType::VolOptPillar, monster.map_area))); } for _ in 0..24 { monsters.push(Some(MapEnemy::new(MonsterType::VolOptMonitor, monster.map_area))); } for _ in 0..2 { monsters.push(Some(MapEnemy::new(MonsterType::VolOptUnused, monster.map_area))); } monsters.push(Some(MapEnemy::new(MonsterType::VolOptAmp, monster.map_area))); monsters.push(Some(MapEnemy::new(MonsterType::VolOptCore, monster.map_area))); monsters.push(Some(MapEnemy::new(MonsterType::VolOptUnused, monster.map_area))); }, // TOOD: this cares about difficulty (theres an ult-specific darvant?) MonsterType::DarkFalz => { for _ in 0..509 { monsters.push(Some(MapEnemy::new(MonsterType::Darvant, monster.map_area))); } monsters.push(Some(MapEnemy::new(MonsterType::DarkFalz3, monster.map_area))); monsters.push(Some(MapEnemy::new(MonsterType::DarkFalz2, monster.map_area))); monsters.push(Some(MapEnemy::new(MonsterType::DarkFalz1, monster.map_area))); }, MonsterType::OlgaFlow => { for _ in 0..512 { monsters.push(Some(MapEnemy::new(MonsterType::OlgaFlow, monster.map_area))); } }, MonsterType::BarbaRay => { for _ in 0..47 { monsters.push(Some(MapEnemy::new(MonsterType::PigRay, monster.map_area))); } }, MonsterType::GolDragon => { for _ in 0..5 { monsters.push(Some(MapEnemy::new(MonsterType::GolDragon, monster.map_area))); } }, MonsterType::SinowBerill => { for _ in 0..4 { monsters.push(Some(MapEnemy::new(MonsterType::SinowBerill, monster.map_area))); // unused clones } }, MonsterType::SinowSpigell => { for _ in 0..4 { monsters.push(Some(MapEnemy::new(MonsterType::SinowSpigell, monster.map_area))); // unused clones } }, MonsterType::Recobox => { // + recons for _ in 0..raw_enemy.children { monsters.push(Some(MapEnemy::new(MonsterType::Recon, monster.map_area))); } }, MonsterType::Epsilon => { for _ in 0..4 { monsters.push(Some(MapEnemy::new(MonsterType::Epsiguard, monster.map_area))); } }, _ => { for _ in 0..raw_enemy.children { monsters.push(Some(MapEnemy::new(monster.monster, monster.map_area))); } } } monsters }) } pub fn enemy_data_from_stream(cursor: &mut impl Read, map_area: &MapArea, episode: &Episode) -> Vec> { let mut enemy_data = Vec::new(); while let Ok(enemy) = RawMapEnemy::from_byte_stream(cursor) { enemy_data.append(&mut parse_enemy(episode, map_area, enemy)); } enemy_data } fn enemy_data_from_map_data(map_variant: &MapVariant, episode: &Episode) -> Vec> { let path = map_variant.dat_file(); let mut cursor = File::open(path).unwrap(); enemy_data_from_stream(&mut cursor, &map_variant.map, episode) } #[derive(Error, Debug)] #[error("")] pub enum MapsError { InvalidMonsterId(usize), InvalidObjectId(usize), } #[derive(Debug)] pub struct Maps { map_variants: Vec, enemy_data: Vec>, object_data: Vec>, } impl Maps { pub fn new(room_mode: RoomMode) -> Maps { let map_variants = match (room_mode.episode(), room_mode.single_player()) { (Episode::One, 0) => { vec![MapVariant::new(MapArea::Pioneer2Ep1, MapVariantMode::Online), MapVariant::new(MapArea::Forest1, MapVariantMode::Online), MapVariant::new(MapArea::Forest2, MapVariantMode::Online), MapVariant::new(MapArea::Caves1, MapVariantMode::Online), MapVariant::new(MapArea::Caves2, MapVariantMode::Online), MapVariant::new(MapArea::Caves3, MapVariantMode::Online), MapVariant::new(MapArea::Mines1, MapVariantMode::Online), MapVariant::new(MapArea::Mines2, MapVariantMode::Online), MapVariant::new(MapArea::Ruins1, MapVariantMode::Online), MapVariant::new(MapArea::Ruins2, MapVariantMode::Online), MapVariant::new(MapArea::Ruins3, MapVariantMode::Online), MapVariant::new(MapArea::Dragon, MapVariantMode::Online), MapVariant::new(MapArea::DeRolLe, MapVariantMode::Online), MapVariant::new(MapArea::VolOpt, MapVariantMode::Online), MapVariant::new(MapArea::DarkFalz, MapVariantMode::Online), ] }, (Episode::One, 1) => { vec![MapVariant::new(MapArea::Pioneer2Ep1, MapVariantMode::Offline), MapVariant::new(MapArea::Forest1, MapVariantMode::Offline), MapVariant::new(MapArea::Forest2, MapVariantMode::Offline), MapVariant::new(MapArea::Caves1, MapVariantMode::Offline), MapVariant::new(MapArea::Caves2, MapVariantMode::Offline), MapVariant::new(MapArea::Caves3, MapVariantMode::Offline), MapVariant::new(MapArea::Mines1, MapVariantMode::Offline), MapVariant::new(MapArea::Mines2, MapVariantMode::Offline), MapVariant::new(MapArea::Ruins1, MapVariantMode::Offline), MapVariant::new(MapArea::Ruins2, MapVariantMode::Offline), MapVariant::new(MapArea::Ruins3, MapVariantMode::Offline), MapVariant::new(MapArea::Dragon, MapVariantMode::Offline), MapVariant::new(MapArea::DeRolLe, MapVariantMode::Offline), MapVariant::new(MapArea::VolOpt, MapVariantMode::Offline), MapVariant::new(MapArea::DarkFalz, MapVariantMode::Offline), ] }, (Episode::Two, 0) => { vec![MapVariant::new(MapArea::Pioneer2Ep2, MapVariantMode::Online), MapVariant::new(MapArea::VrTempleAlpha, MapVariantMode::Online), MapVariant::new(MapArea::VrTempleBeta, MapVariantMode::Online), MapVariant::new(MapArea::VrSpaceshipAlpha, MapVariantMode::Online), MapVariant::new(MapArea::VrSpaceshipBeta, MapVariantMode::Online), MapVariant::new(MapArea::Cca, MapVariantMode::Online), MapVariant::new(MapArea::JungleAreaNorth, MapVariantMode::Online), MapVariant::new(MapArea::JungleAreaEast, MapVariantMode::Online), MapVariant::new(MapArea::Mountain, MapVariantMode::Online), MapVariant::new(MapArea::Seaside, MapVariantMode::Online), MapVariant::new(MapArea::SeabedUpper, MapVariantMode::Online), MapVariant::new(MapArea::SeabedLower, MapVariantMode::Online), MapVariant::new(MapArea::GalGryphon, MapVariantMode::Online), MapVariant::new(MapArea::OlgaFlow, MapVariantMode::Online), MapVariant::new(MapArea::BarbaRay, MapVariantMode::Online), MapVariant::new(MapArea::GolDragon, MapVariantMode::Online), ] }, (Episode::Two, 1) => { vec![MapVariant::new(MapArea::Pioneer2Ep2, MapVariantMode::Offline), MapVariant::new(MapArea::VrTempleAlpha, MapVariantMode::Offline), MapVariant::new(MapArea::VrTempleBeta, MapVariantMode::Offline), MapVariant::new(MapArea::VrSpaceshipAlpha, MapVariantMode::Offline), MapVariant::new(MapArea::VrSpaceshipBeta, MapVariantMode::Offline), MapVariant::new(MapArea::Cca, MapVariantMode::Offline), MapVariant::new(MapArea::JungleAreaNorth, MapVariantMode::Offline), MapVariant::new(MapArea::JungleAreaEast, MapVariantMode::Offline), MapVariant::new(MapArea::Mountain, MapVariantMode::Offline), MapVariant::new(MapArea::Seaside, MapVariantMode::Offline), MapVariant::new(MapArea::SeabedUpper, MapVariantMode::Offline), MapVariant::new(MapArea::SeabedLower, MapVariantMode::Offline), MapVariant::new(MapArea::GalGryphon, MapVariantMode::Offline), MapVariant::new(MapArea::OlgaFlow, MapVariantMode::Offline), MapVariant::new(MapArea::BarbaRay, MapVariantMode::Offline), MapVariant::new(MapArea::GolDragon, MapVariantMode::Offline), ] }, (Episode::Four, _) => { vec![MapVariant::new(MapArea::Pioneer2Ep4, MapVariantMode::Online), MapVariant::new(MapArea::CraterEast, MapVariantMode::Online), MapVariant::new(MapArea::CraterWest, MapVariantMode::Online), MapVariant::new(MapArea::CraterSouth, MapVariantMode::Online), MapVariant::new(MapArea::CraterNorth, MapVariantMode::Online), MapVariant::new(MapArea::CraterInterior, MapVariantMode::Online), MapVariant::new(MapArea::SubDesert1, MapVariantMode::Online), MapVariant::new(MapArea::SubDesert2, MapVariantMode::Online), MapVariant::new(MapArea::SubDesert3, MapVariantMode::Online), MapVariant::new(MapArea::SaintMillion, MapVariantMode::Online), ] }, _ => unreachable!() }; let maps = Maps { enemy_data: map_variants.iter().fold(Vec::new(), |mut enemy_data, map_variant| { enemy_data.append(&mut enemy_data_from_map_data(&map_variant, &room_mode.episode())); enemy_data }), object_data: map_variants.iter().map(|map_variant| { objects_from_map_data(map_variant.obj_file().into(), &room_mode.episode(), &map_variant.map) }).flatten().collect(), map_variants: map_variants, }; maps } pub fn enemy_by_id(&self, id: usize) -> Result { self.enemy_data[id].ok_or(MapsError::InvalidMonsterId(id)) } pub fn object_by_id(&self, id: usize) -> Result { self.object_data[id].ok_or(MapsError::InvalidObjectId(id)) } pub fn map_headers(&self) -> [u32; 0x20] { self.map_variants.iter() .enumerate() .fold([0; 0x20], |mut header, (i, map_variant)| { let [major, minor] = map_variant.pkt_header(); header[i*2] = major as u32; header[i*2 + 1] = minor as u32; header }) } pub fn set_quest_data(&mut self, enemies: Vec>, objects: Vec>) { self.enemy_data = enemies; self.object_data = objects; } }