|
@ -6,6 +6,7 @@ use std::io::{Read}; |
|
|
use std::fs::File;
|
|
|
use std::fs::File;
|
|
|
|
|
|
|
|
|
use byteorder::{LittleEndian, ReadBytesExt};
|
|
|
use byteorder::{LittleEndian, ReadBytesExt};
|
|
|
|
|
|
use rand::Rng;
|
|
|
|
|
|
|
|
|
use crate::ship::monster::MonsterType;
|
|
|
use crate::ship::monster::MonsterType;
|
|
|
use crate::ship::room::Episode;
|
|
|
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<MapEnemy> {
|
|
|
|
|
|
None
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
#[derive(Debug)]
|
|
|
enum MapEnemyError {
|
|
|
enum MapEnemyError {
|
|
|
UnknownEnemyId(u32),
|
|
|
UnknownEnemyId(u32),
|
|
@ -104,7 +79,6 @@ pub struct MapEnemy { |
|
|
dead: bool,
|
|
|
dead: bool,
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
impl MapEnemy {
|
|
|
impl MapEnemy {
|
|
|
fn from_raw(enemy: RawMapEnemy, episode: &Episode /*, battleparam */) -> Result<MapEnemy, MapEnemyError> {
|
|
|
fn from_raw(enemy: RawMapEnemy, episode: &Episode /*, battleparam */) -> Result<MapEnemy, MapEnemyError> {
|
|
|
let monster = match (enemy, episode) {
|
|
|
let monster = match (enemy, episode) {
|
|
@ -191,8 +165,6 @@ impl MapEnemy { |
|
|
(RawMapEnemy {id: 281, skin: 0, ..}, _) => MonsterType::SaintMillion,
|
|
|
(RawMapEnemy {id: 281, skin: 0, ..}, _) => MonsterType::SaintMillion,
|
|
|
(RawMapEnemy {id: 281, skin: 1, ..}, _) => MonsterType::Shambertin,
|
|
|
(RawMapEnemy {id: 281, skin: 1, ..}, _) => MonsterType::Shambertin,
|
|
|
_ => return Err(MapEnemyError::UnknownEnemyId(enemy.id))
|
|
|
_ => return Err(MapEnemyError::UnknownEnemyId(enemy.id))
|
|
|
|
|
|
|
|
|
//_ => panic!("trying to load unknown monster: {:?}", enemy)
|
|
|
|
|
|
};
|
|
|
};
|
|
|
|
|
|
|
|
|
Ok(MapEnemy {
|
|
|
Ok(MapEnemy {
|
|
@ -211,50 +183,245 @@ impl MapEnemy { |
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
//impl From<RawMapEnemy> 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)]
|
|
|
#[derive(Debug)]
|
|
|
pub struct Maps {
|
|
|
|
|
|
//map_indexes:
|
|
|
|
|
|
enemy_data: Vec<MapEnemy>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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<Option<MapEnemy>> {
|
|
|
|
|
|
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<Option<MapEnemy>>
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
impl Maps {
|
|
|
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 {
|
|
|
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
|
|
|
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
|
|
|
|
|
|
})
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|