split map.rs into a module
This commit is contained in:
		
							parent
							
								
									9a0be2eb57
								
							
						
					
					
						commit
						6a8fccd360
					
				
							
								
								
									
										1289
									
								
								src/ship/map.rs
									
									
									
									
									
								
							
							
						
						
									
										1289
									
								
								src/ship/map.rs
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										220
									
								
								src/ship/map/area.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										220
									
								
								src/ship/map/area.rs
									
									
									
									
									
										Normal file
									
								
							| @ -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<MapArea, MapAreaError> { | ||||
|         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<u32> { | ||||
|         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,
 | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										279
									
								
								src/ship/map/enemy.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										279
									
								
								src/ship/map/enemy.rs
									
									
									
									
									
										Normal file
									
								
							| @ -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<R: Read>(cursor: &mut R) -> Result<RawMapEnemy, std::io::Error> { | ||||
|         Ok(RawMapEnemy { | ||||
|             id: cursor.read_u32::<LittleEndian>()?, | ||||
|             _unknown1: cursor.read_u16::<LittleEndian>()?, | ||||
|             children: cursor.read_u16::<LittleEndian>()?, | ||||
|             map_area: cursor.read_u16::<LittleEndian>()?, | ||||
|             _unknown4: cursor.read_u16::<LittleEndian>()?, | ||||
|             section: cursor.read_u16::<LittleEndian>()?, | ||||
|             wave_idd: cursor.read_u16::<LittleEndian>()?, | ||||
|             wave_id: cursor.read_u32::<LittleEndian>()?, | ||||
|             x: cursor.read_f32::<LittleEndian>()?, | ||||
|             y: cursor.read_f32::<LittleEndian>()?, | ||||
|             z: cursor.read_f32::<LittleEndian>()?, | ||||
|             xrot: cursor.read_u32::<LittleEndian>()?, | ||||
|             yrot: cursor.read_u32::<LittleEndian>()?, | ||||
|             zrot: cursor.read_u32::<LittleEndian>()?, | ||||
|             _field1: cursor.read_u32::<LittleEndian>()?, | ||||
|             field2: cursor.read_u32::<LittleEndian>()?, | ||||
|             _field3: cursor.read_u32::<LittleEndian>()?, | ||||
|             _field4: cursor.read_u32::<LittleEndian>()?, | ||||
|             _field5: cursor.read_u32::<LittleEndian>()?, | ||||
|             skin: cursor.read_u32::<LittleEndian>()?, | ||||
|             _field6: cursor.read_u32::<LittleEndian>()?, | ||||
|         }) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| #[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<MapEnemy, MapEnemyError> { | ||||
|         // 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], | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
							
								
								
									
										317
									
								
								src/ship/map/maps.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										317
									
								
								src/ship/map/maps.rs
									
									
									
									
									
										Normal file
									
								
							| @ -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<Option<MapObject>> { | ||||
|     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<Option<MapObject>> { | ||||
|     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<Option<MapEnemy>> { | ||||
|     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<Option<MapEnemy>> { | ||||
|     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<Option<MapEnemy>> { | ||||
|     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<MapVariant>, | ||||
|     enemy_data: Vec<Option<MapEnemy>>, | ||||
|     object_data: Vec<Option<MapObject>>, | ||||
| } | ||||
| 
 | ||||
| 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<MapEnemy, MapsError> { | ||||
|         self.enemy_data[id].ok_or(MapsError::InvalidMonsterId(id)) | ||||
|     } | ||||
| 
 | ||||
|     pub fn object_by_id(&self, id: usize) -> Result<MapObject, MapsError> { | ||||
|         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<Option<MapEnemy>>, objects: Vec<Option<MapObject>>) { | ||||
|         self.enemy_data = enemies; | ||||
|         self.object_data = objects; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
							
								
								
									
										25
									
								
								src/ship/map/mod.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								src/ship/map/mod.rs
									
									
									
									
									
										Normal file
									
								
							| @ -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}; | ||||
| 
 | ||||
							
								
								
									
										174
									
								
								src/ship/map/object.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										174
									
								
								src/ship/map/object.rs
									
									
									
									
									
										Normal file
									
								
							| @ -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<R: Read>(cursor: &mut R) -> Result<RawMapObject, std::io::Error> { | ||||
|         Ok(RawMapObject { | ||||
|             otype: cursor.read_u16::<LittleEndian>()?, | ||||
|             unknown1: cursor.read_u16::<LittleEndian>()?, | ||||
|             unknown2: cursor.read_u32::<LittleEndian>()?, | ||||
|             id: cursor.read_u16::<LittleEndian>()?, | ||||
|             group: cursor.read_u16::<LittleEndian>()?, | ||||
|             section: cursor.read_u16::<LittleEndian>()?, | ||||
|             unknown3: cursor.read_u16::<LittleEndian>()?, | ||||
|             x: cursor.read_f32::<LittleEndian>()?, | ||||
|             y: cursor.read_f32::<LittleEndian>()?, | ||||
|             z: cursor.read_f32::<LittleEndian>()?, | ||||
|             xrot: cursor.read_u32::<LittleEndian>()?, | ||||
|             yrot: cursor.read_u32::<LittleEndian>()?, | ||||
|             zrot: cursor.read_u32::<LittleEndian>()?, | ||||
|             field1: cursor.read_f32::<LittleEndian>()?, | ||||
|             field2: cursor.read_f32::<LittleEndian>()?, | ||||
|             field3: cursor.read_f32::<LittleEndian>()?, | ||||
|             field4: cursor.read_u32::<LittleEndian>()?, | ||||
|             field5: cursor.read_u32::<LittleEndian>()?, | ||||
|             field6: cursor.read_u32::<LittleEndian>()?, | ||||
|             field7: cursor.read_u32::<LittleEndian>()?, | ||||
|         }) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| #[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<MapObject, MapObjectError> { | ||||
|         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, | ||||
|         }) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
							
								
								
									
										369
									
								
								src/ship/map/variant.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										369
									
								
								src/ship/map/variant.rs
									
									
									
									
									
										Normal file
									
								
							| @ -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] | ||||
|     } | ||||
| } | ||||
| 
 | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user