diff --git a/src/ship/map.rs b/src/ship/map.rs deleted file mode 100644 index b007baf..0000000 --- a/src/ship/map.rs +++ /dev/null @@ -1,1289 +0,0 @@ -#![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(u16), -} - -impl MapArea { - pub fn from_value(episode: &Episode, area: u16) -> 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; - } -} - - diff --git a/src/ship/map/area.rs b/src/ship/map/area.rs new file mode 100644 index 0000000..57ccd0a --- /dev/null +++ b/src/ship/map/area.rs @@ -0,0 +1,220 @@ +// TOOD: `pub(super) for most of these?` + +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}; + +// TODO: don't use * +use crate::ship::map::*; + +#[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(u16), +} + +impl MapArea { + pub fn from_value(episode: &Episode, area: u16) -> 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, + } + } +} diff --git a/src/ship/map/enemy.rs b/src/ship/map/enemy.rs new file mode 100644 index 0000000..ccd06ce --- /dev/null +++ b/src/ship/map/enemy.rs @@ -0,0 +1,279 @@ +// TOOD: `pub(super) for most of these?` +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}; + +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: monster, + map_area: map_area.clone(), + hp: 0, + dropped_item: false, + gave_exp: false, + player_hit: [false; 4], + }) + } + + pub fn new(monster: MonsterType, map_area: MapArea) -> MapEnemy { + MapEnemy { + monster: monster, + map_area: map_area, + hp: 0, + dropped_item: false, + gave_exp: false, + player_hit: [false; 4], + } + } +} + diff --git a/src/ship/map/maps.rs b/src/ship/map/maps.rs new file mode 100644 index 0000000..b28bd71 --- /dev/null +++ b/src/ship/map/maps.rs @@ -0,0 +1,317 @@ +// TOOD: `pub(super) for most of these?` + +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}; + +// TODO: don't use * +use crate::ship::map::*; + + +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; + } +} + diff --git a/src/ship/map/mod.rs b/src/ship/map/mod.rs new file mode 100644 index 0000000..308b7ef --- /dev/null +++ b/src/ship/map/mod.rs @@ -0,0 +1,25 @@ +mod area; +mod enemy; +mod object; +mod variant; +mod maps; + +// TODO: don't just forward everything to the module scope +pub use area::*; +pub use enemy::*; +pub use object::*; +pub use variant::*; +pub use maps::*; + +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}; + diff --git a/src/ship/map/object.rs b/src/ship/map/object.rs new file mode 100644 index 0000000..f8b47fc --- /dev/null +++ b/src/ship/map/object.rs @@ -0,0 +1,174 @@ +// TOOD: `pub(super) for most of these?` + +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}; + +// TODO: don't use * +use crate::ship::map::*; + + +#[derive(Debug, Copy, Clone)] +pub 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 { + pub 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)] +pub enum MapObjectError { + UnknownObjectType(u16, RawMapObject), +} + + +impl MapObject { + pub 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, + }) + } +} + diff --git a/src/ship/map/variant.rs b/src/ship/map/variant.rs new file mode 100644 index 0000000..dee6de2 --- /dev/null +++ b/src/ship/map/variant.rs @@ -0,0 +1,369 @@ +// TOOD: `pub(super) for most of these?` + +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}; + +// TODO: don't use * +use crate::ship::map::*; + +#[derive(Debug, PartialEq)] +pub enum MapVariantMode { + Online, + Offline, +} + +#[derive(Debug)] +pub struct MapVariant { + pub map: MapArea, + mode: MapVariantMode, + major: u8, + minor: u8, +} + +impl MapVariant { + pub 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 + pub 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!(), + } + } + } + + pub 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!(), + } + } + } + + pub fn pkt_header(&self) -> [u8; 2] { + [self.major, self.minor] + } +} +