You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

387 lines
18 KiB

// TOOD: `pub(super) for most of these?`
use std::path::PathBuf;
use std::io::{Read};
use std::fs::File;
use thiserror::Error;
//use crate::ship::ship::ShipEvent;
use crate::area::MapArea;
use crate::Holiday;
use crate::enemy::{MapEnemy, RawMapEnemy, RareMonsterAppearTable};
use crate::monster::MonsterType;
use crate::variant::{MapVariant, MapVariantMode};
use crate::object::{MapObject, RawMapObject};
use crate::room::{Episode, RoomMode, PlayerMode};
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, *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).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![Some(monster)];
match monster.monster {
MonsterType::Monest => {
for _ in 0..30 {
monsters.push(Some(MapEnemy::new(MonsterType::Mothmant, 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::PofuillySlime => {
for _ in 0..5 {
monsters.push(Some(MapEnemy::new(MonsterType::PofuillySlime, monster.map_area)));
}
},
MonsterType::PouillySlime => {
for _ in 0..5 {
monsters.push(Some(MapEnemy::new(MonsterType::PofuillySlime, 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)
}
pub fn default_map_variants(episode: Episode, player_mode: PlayerMode) -> Vec<MapVariant> {
match (episode, player_mode) {
(Episode::One, PlayerMode::Multi) => {
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, PlayerMode::Single) => {
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, PlayerMode::Multi) => {
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, PlayerMode::Single) => {
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, PlayerMode::Multi) => {
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),
]
},
(Episode::Four, PlayerMode::Single) => {
vec![MapVariant::new(MapArea::Pioneer2Ep4, MapVariantMode::Offline),
MapVariant::new(MapArea::CraterEast, MapVariantMode::Offline),
MapVariant::new(MapArea::CraterWest, MapVariantMode::Offline),
MapVariant::new(MapArea::CraterSouth, MapVariantMode::Offline),
MapVariant::new(MapArea::CraterNorth, MapVariantMode::Offline),
MapVariant::new(MapArea::CraterInterior, MapVariantMode::Offline),
MapVariant::new(MapArea::SubDesert1, MapVariantMode::Offline),
MapVariant::new(MapArea::SubDesert2, MapVariantMode::Offline),
MapVariant::new(MapArea::SubDesert3, MapVariantMode::Offline),
MapVariant::new(MapArea::SaintMillion, MapVariantMode::Offline),
]
},
}
}
#[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(map_variants: Vec<MapVariant>, enemy_data: Vec<Option<MapEnemy>>, object_data: Vec<Option<MapObject>>) -> Maps {
Maps {
map_variants,
enemy_data,
object_data,
}
}
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>>,
rare_monster_table: &RareMonsterAppearTable,
event: Holiday)
{
self.enemy_data = enemies
.into_iter()
.map(|enemy| enemy.map(|enemy| rare_monster_table.apply(enemy, event)))
.collect();
self.object_data = objects;
}
pub fn get_rare_monster_list(&self) -> Vec<u16> {
let mut rare_monsters = vec![0xFFFF; 16];
let shiny: Vec<(usize, &Option<MapEnemy>)> = self.enemy_data.iter()
.enumerate()
.filter(|(_,m)| {
match m {
Some(m) => {
m.shiny
},
None => false,
}
})
.collect();
for monster in &shiny {
if let Some(j) = rare_monsters.iter().position(|&x| x == 0xFFFF) {
rare_monsters[j] = monster.0 as u16;
} else {
break
}
}
rare_monsters
}
}
pub fn generate_free_roam_maps(room_mode: RoomMode, event: Holiday) -> Maps {
let rare_monster_table = RareMonsterAppearTable::new(room_mode.episode());
let map_variants = default_map_variants(room_mode.episode(), room_mode.player_mode());
Maps {
enemy_data: map_variants.iter()
.flat_map(|map_variant| {
enemy_data_from_map_data(map_variant, &room_mode.episode())
})
.map(|enemy| enemy.map(|enemy| rare_monster_table.apply(enemy, event)))
.collect(),
object_data: map_variants.iter()
.flat_map(|map_variant| {
objects_from_map_data(map_variant.obj_file().into(), &room_mode.episode(), &map_variant.map)
}).collect(),
map_variants,
}
}
pub fn null_free_roam_maps(_room_mode: RoomMode, _event: Holiday) -> Maps {
Maps {
enemy_data: Default::default(),
object_data: Default::default(),
map_variants: Default::default(),
}
}