diff --git a/src/ship/map.rs b/src/ship/map.rs index 7fe41ae..dce0903 100644 --- a/src/ship/map.rs +++ b/src/ship/map.rs @@ -6,6 +6,7 @@ use std::io::{Read}; use std::fs::File; use byteorder::{LittleEndian, ReadBytesExt}; +use rand::Rng; use crate::ship::monster::MonsterType; use crate::ship::room::Episode; @@ -64,32 +65,6 @@ impl RawMapEnemy { } - -struct MapObjects { - -} - - - - -struct MapEnemies { - -} - -impl MapEnemies { - fn new(path: PathBuf) -> MapEnemies { - MapEnemies { - - } - } - - fn read_next_enemy(&mut self) -> Option { - None - } -} - - - #[derive(Debug)] enum MapEnemyError { UnknownEnemyId(u32), @@ -104,7 +79,6 @@ pub struct MapEnemy { dead: bool, } - impl MapEnemy { fn from_raw(enemy: RawMapEnemy, episode: &Episode /*, battleparam */) -> Result { let monster = match (enemy, episode) { @@ -191,8 +165,6 @@ impl MapEnemy { (RawMapEnemy {id: 281, skin: 0, ..}, _) => MonsterType::SaintMillion, (RawMapEnemy {id: 281, skin: 1, ..}, _) => MonsterType::Shambertin, _ => return Err(MapEnemyError::UnknownEnemyId(enemy.id)) - - //_ => panic!("trying to load unknown monster: {:?}", enemy) }; Ok(MapEnemy { @@ -211,50 +183,245 @@ impl MapEnemy { } } -//impl From for MapEnemy +#[derive(Debug)] +enum MapVariantMode { + Online, + Offline, +} +#[derive(Debug)] +enum MapVariantType { + Pioneer2Ep1, + Forest1, + Forest2, + Caves1, + Caves2, + Caves3, + Mines1, + Mines2, + Ruins1, + Ruins2, + Ruins3, + Dragon, + DeRolLe, + VolOpt, + DarkFalz, +} #[derive(Debug)] -pub struct Maps { - //map_indexes: - enemy_data: Vec - +struct MapVariant { + map: MapVariantType, + mode: MapVariantMode, + major: u8, + minor: u8, +} + +impl MapVariant { + fn new(map: MapVariantType, mode: MapVariantMode) -> MapVariant { + let major = match map { + MapVariantType::Pioneer2Ep1 => 0, + MapVariantType::Forest1 | MapVariantType::Forest2 => 0, + MapVariantType::Caves1 | MapVariantType::Caves2 | MapVariantType::Caves3 => rand::thread_rng().gen_range(0, 3), + MapVariantType::Mines1 | MapVariantType::Mines2 => rand::thread_rng().gen_range(0, 3), + MapVariantType::Ruins1 | MapVariantType::Ruins2 | MapVariantType::Ruins3 => rand::thread_rng().gen_range(0, 3), + MapVariantType::Dragon | MapVariantType::DeRolLe | MapVariantType::VolOpt | MapVariantType::DarkFalz => 0, + }; + + let minor = match map { + MapVariantType::Pioneer2Ep1 => 0, + MapVariantType::Forest1 => rand::thread_rng().gen_range(0, 6), + MapVariantType::Forest2 => rand::thread_rng().gen_range(0, 5), + MapVariantType::Caves1 | MapVariantType::Caves2 | MapVariantType::Caves3 => rand::thread_rng().gen_range(0, 2), + MapVariantType::Mines1 | MapVariantType::Mines2 => rand::thread_rng().gen_range(0, 2), + MapVariantType::Ruins1 | MapVariantType::Ruins2 | MapVariantType::Ruins3 => rand::thread_rng().gen_range(0, 2), + MapVariantType::Dragon | MapVariantType::DeRolLe | MapVariantType::VolOpt | MapVariantType::DarkFalz => 0, + }; + + MapVariant { + map: map, + mode: mode, + major: major, + minor: minor, + } + } + + fn dat_file(&self) -> String { + match self.map { + MapVariantType::Pioneer2Ep1 => "data/maps/map_city00_00e.dat".into(), + MapVariantType::Forest1 => format!("data/maps/map_forest01_0{}e.dat", self.minor), + MapVariantType::Forest2 => format!("data/maps/map_forest02_0{}e.dat", self.minor), + MapVariantType::Caves1 => format!("data/maps/map_cave01_0{}_0{}e.dat", self.major, self.minor), + MapVariantType::Caves2 => format!("data/maps/map_cave02_0{}_0{}e.dat", self.major, self.minor), + MapVariantType::Caves3 => format!("data/maps/map_cave03_0{}_0{}e.dat", self.major, self.minor), + MapVariantType::Mines1 => format!("data/maps/map_machine01_0{}_0{}e.dat", self.major, self.minor), + MapVariantType::Mines2 => format!("data/maps/map_machine02_0{}_0{}e.dat", self.major, self.minor), + MapVariantType::Ruins1 => format!("data/maps/map_ancient01_0{}_0{}e.dat", self.major, self.minor), + MapVariantType::Ruins2 => format!("data/maps/map_ancient02_0{}_0{}e.dat", self.major, self.minor), + MapVariantType::Ruins3 => format!("data/maps/map_ancient03_0{}_0{}e.dat", self.major, self.minor), + MapVariantType::Dragon => "data/maps/map_boss01e.dat".into(), + MapVariantType::DeRolLe => "data/maps/map_boss02e.dat".into(), + MapVariantType::VolOpt => "data/maps/map_boss03e.dat".into(), + MapVariantType::DarkFalz => "data/maps/map_boss04e.dat".into(), + } + } + + fn pkt_header(&self) -> [u8; 2] { + [self.major, self.minor] + } } +fn enemy_data_from_map_data(path: PathBuf, episode: &Episode) -> Vec> { + let mut cursor = File::open(path).unwrap(); + let mut enemy_data = Vec::new(); + while let Ok(enemy) = RawMapEnemy::from_byte_stream(&mut cursor) { + let new_enemy = MapEnemy::from_raw(enemy, episode); + enemy_data.append(&mut new_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))); + } + }, + MonsterType::PofuillySlime => { + for _ in 0..4 { + monsters.push(Some(MapEnemy::new(MonsterType::PofuillySlime))); + } + }, + MonsterType::PanArms => { + monsters.push(Some(MapEnemy::new(MonsterType::Hidoom))); + monsters.push(Some(MapEnemy::new(MonsterType::Migium))); + }, + MonsterType::SinowBeat => { + for _ in 0..4 { + monsters.push(Some(MapEnemy::new(MonsterType::SinowBeat))); + } + }, + MonsterType::SinowGold => { + for _ in 0..4 { + monsters.push(Some(MapEnemy::new(MonsterType::SinowGold))); + } + }, + MonsterType::Canane => { + for _ in 0..8 { + monsters.push(Some(MapEnemy::new(MonsterType::RingCanadine))); + } + }, + MonsterType::ChaosSorcerer => { + monsters.push(Some(MapEnemy::new(MonsterType::BeeR))); + monsters.push(Some(MapEnemy::new(MonsterType::BeeL))); + }, + MonsterType::Bulclaw => { + for _ in 0..4 { + monsters.push(Some(MapEnemy::new(MonsterType::Claw))); + } + }, + MonsterType::DeRolLe => { + for _ in 0..10 { + monsters.push(Some(MapEnemy::new(MonsterType::DeRolLeBody))); + } + for _ in 0..9 { + monsters.push(Some(MapEnemy::new(MonsterType::DeRolLeMine))); + } + }, + MonsterType::VolOptPartA => { + for _ in 0..6 { + monsters.push(Some(MapEnemy::new(MonsterType::VolOptPillar))); + } + for _ in 0..24 { + monsters.push(Some(MapEnemy::new(MonsterType::VolOptMonitor))); + } + for _ in 0..2 { + monsters.push(Some(MapEnemy::new(MonsterType::VolOptUnused))); + } + monsters.push(Some(MapEnemy::new(MonsterType::VolOptAmp))); + monsters.push(Some(MapEnemy::new(MonsterType::VolOptCore))); + monsters.push(Some(MapEnemy::new(MonsterType::VolOptUnused))); + }, + // TOOD: this cares about difficulty (theres an ult-specific darvant?) + MonsterType::DarkFalz => { + for _ in 0..509 { + monsters.push(Some(MapEnemy::new(MonsterType::Darvant))); + } + monsters.push(Some(MapEnemy::new(MonsterType::DarkFalz3))); + monsters.push(Some(MapEnemy::new(MonsterType::DarkFalz2))); + monsters.push(Some(MapEnemy::new(MonsterType::DarkFalz1))); + }, + _ => { + warn!("children: {}", enemy.children); + for _ in 0..enemy.children { + monsters.push(Some(MapEnemy::new(monster.monster))); + } + } + } + monsters + })); + } + enemy_data +} + + +#[derive(Debug)] +pub struct Maps { + map_variants: [MapVariant; 15], + enemy_data: Vec> +} impl Maps { - pub fn new() -> Maps { + pub fn new(episode: Episode) -> Maps { + warn!("new maps ep: {:?}", episode); + let map_variants = match episode { + Episode::One => { + [MapVariant::new(MapVariantType::Pioneer2Ep1, MapVariantMode::Online), + MapVariant::new(MapVariantType::Forest1, MapVariantMode::Online), + MapVariant::new(MapVariantType::Forest2, MapVariantMode::Online), + MapVariant::new(MapVariantType::Caves1, MapVariantMode::Online), + MapVariant::new(MapVariantType::Caves2, MapVariantMode::Online), + MapVariant::new(MapVariantType::Caves3, MapVariantMode::Online), + MapVariant::new(MapVariantType::Mines1, MapVariantMode::Online), + MapVariant::new(MapVariantType::Mines2, MapVariantMode::Online), + MapVariant::new(MapVariantType::Ruins1, MapVariantMode::Online), + MapVariant::new(MapVariantType::Ruins2, MapVariantMode::Online), + MapVariant::new(MapVariantType::Ruins3, MapVariantMode::Online), + MapVariant::new(MapVariantType::Dragon, MapVariantMode::Online), + MapVariant::new(MapVariantType::DeRolLe, MapVariantMode::Online), + MapVariant::new(MapVariantType::VolOpt, MapVariantMode::Online), + MapVariant::new(MapVariantType::DarkFalz, MapVariantMode::Online), + ] + }, + _ => panic!() + }; + let mut maps = Maps { - enemy_data: Vec::new(), + enemy_data: map_variants.iter().fold(Vec::new(), |mut enemy_data, map_variant| { + enemy_data.append(&mut enemy_data_from_map_data(map_variant.dat_file().into(), &episode)); + enemy_data + }), + map_variants: map_variants, }; - maps.add_map("data/maps/map_city00_00e.dat".into()); - maps.add_map("data/maps/map_forest01_00e.dat".into()); - maps.add_map("data/maps/map_cave01_00_00e.dat".into()); - warn!("len {}", maps.enemy_data.len()); maps } - fn add_map(&mut self, path: PathBuf) { - let mut cursor = File::open(path).unwrap(); - while let Ok(enemy) = RawMapEnemy::from_byte_stream(&mut cursor) { - let new_enemy = MapEnemy::from_raw(enemy); - match new_enemy.monster { - MonsterType::Monest => { - self.enemy_data.push(new_enemy); - for _ in 0..30 { - self.enemy_data.push(MapEnemy::new(MonsterType::Mothmant)); - } - }, - _ => self.enemy_data.push(new_enemy) - } - } + pub fn enemy_by_id(&self, id: usize) -> MapEnemy { + self.enemy_data[id].unwrap() } - pub fn enemy_by_id(&self, id: usize) -> MapEnemy { - self.enemy_data[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 + }) } } diff --git a/src/ship/room.rs b/src/ship/room.rs index a4fba17..c69c0b6 100644 --- a/src/ship/room.rs +++ b/src/ship/room.rs @@ -1,5 +1,7 @@ use std::convert::{From, Into, TryFrom, TryInto}; +use crate::ship::map::Maps; + #[derive(Debug)] pub enum RoomCreationError { InvalidMode, @@ -117,7 +119,8 @@ pub struct RoomState { mode: RoomMode, pub name: String, password: [u16; 16], - pub maps: [u32; 0x20], + //pub maps: [u32; 0x20], + pub maps: Maps, // drop_table // items on ground // enemy info @@ -158,12 +161,18 @@ impl RoomState { difficulty: create_room.difficulty.try_into()?, } }; - + + let ep = room_mode.episode(); Ok(RoomState { mode: room_mode, name: String::from_utf16_lossy(&create_room.name).trim_matches(char::from(0)).into(), password: create_room.password, - maps: [0; 0x20], + //maps: [0; 0x20], + maps: Maps::new(ep), }) } + + pub fn map_headers(&self) -> [u32; 0x20] { + self.maps.map_headers() + } } diff --git a/src/ship/ship.rs b/src/ship/ship.rs index 1bf140d..fa98bdb 100644 --- a/src/ship/ship.rs +++ b/src/ship/ship.rs @@ -369,7 +369,7 @@ impl ShipServerState { let join_room = JoinRoom { flag: 1, - maps: room.maps, + maps: room.maps.map_headers(), players: players, client_id: 0, leader_id: 0,