|
@ -8,12 +8,13 @@ use thiserror::Error; |
|
|
|
|
|
|
|
|
use crate::ship::ship::ShipEvent;
|
|
|
use crate::ship::ship::ShipEvent;
|
|
|
use crate::ship::monster::MonsterType;
|
|
|
use crate::ship::monster::MonsterType;
|
|
|
use crate::ship::room::{Episode, RoomMode};
|
|
|
|
|
|
|
|
|
use crate::ship::room::{Episode, RoomMode, PlayerMode};
|
|
|
|
|
|
|
|
|
// TODO: don't use *
|
|
|
// TODO: don't use *
|
|
|
use crate::ship::map::*;
|
|
|
use crate::ship::map::*;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pub fn objects_from_stream(cursor: &mut impl Read, episode: &Episode, map_area: &MapArea) -> Vec<Option<MapObject>> {
|
|
|
pub fn objects_from_stream(cursor: &mut impl Read, episode: &Episode, map_area: &MapArea) -> Vec<Option<MapObject>> {
|
|
|
let mut object_data = Vec::new();
|
|
|
let mut object_data = Vec::new();
|
|
|
|
|
|
|
|
@ -172,24 +173,9 @@ fn enemy_data_from_map_data(map_variant: &MapVariant, episode: &Episode) -> Vec< |
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#[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, rare_monster_table: &enemy::RareMonsterAppearTable, event: ShipEvent) -> Maps {
|
|
|
|
|
|
let map_variants = match (room_mode.episode(), room_mode.single_player()) {
|
|
|
|
|
|
(Episode::One, 0) => {
|
|
|
|
|
|
|
|
|
fn map_variants(episode: Episode, player_mode: PlayerMode) -> Vec<MapVariant> {
|
|
|
|
|
|
match (episode, player_mode) {
|
|
|
|
|
|
(Episode::One, PlayerMode::Multi) => {
|
|
|
vec![MapVariant::new(MapArea::Pioneer2Ep1, MapVariantMode::Online),
|
|
|
vec![MapVariant::new(MapArea::Pioneer2Ep1, MapVariantMode::Online),
|
|
|
MapVariant::new(MapArea::Forest1, MapVariantMode::Online),
|
|
|
MapVariant::new(MapArea::Forest1, MapVariantMode::Online),
|
|
|
MapVariant::new(MapArea::Forest2, MapVariantMode::Online),
|
|
|
MapVariant::new(MapArea::Forest2, MapVariantMode::Online),
|
|
@ -207,7 +193,7 @@ impl Maps { |
|
|
MapVariant::new(MapArea::DarkFalz, MapVariantMode::Online),
|
|
|
MapVariant::new(MapArea::DarkFalz, MapVariantMode::Online),
|
|
|
]
|
|
|
]
|
|
|
},
|
|
|
},
|
|
|
(Episode::One, 1) => {
|
|
|
|
|
|
|
|
|
(Episode::One, PlayerMode::Single) => {
|
|
|
vec![MapVariant::new(MapArea::Pioneer2Ep1, MapVariantMode::Offline),
|
|
|
vec![MapVariant::new(MapArea::Pioneer2Ep1, MapVariantMode::Offline),
|
|
|
MapVariant::new(MapArea::Forest1, MapVariantMode::Offline),
|
|
|
MapVariant::new(MapArea::Forest1, MapVariantMode::Offline),
|
|
|
MapVariant::new(MapArea::Forest2, MapVariantMode::Offline),
|
|
|
MapVariant::new(MapArea::Forest2, MapVariantMode::Offline),
|
|
@ -225,7 +211,7 @@ impl Maps { |
|
|
MapVariant::new(MapArea::DarkFalz, MapVariantMode::Offline),
|
|
|
MapVariant::new(MapArea::DarkFalz, MapVariantMode::Offline),
|
|
|
]
|
|
|
]
|
|
|
},
|
|
|
},
|
|
|
(Episode::Two, 0) => {
|
|
|
|
|
|
|
|
|
(Episode::Two, PlayerMode::Multi) => {
|
|
|
vec![MapVariant::new(MapArea::Pioneer2Ep2, MapVariantMode::Online),
|
|
|
vec![MapVariant::new(MapArea::Pioneer2Ep2, MapVariantMode::Online),
|
|
|
MapVariant::new(MapArea::VrTempleAlpha, MapVariantMode::Online),
|
|
|
MapVariant::new(MapArea::VrTempleAlpha, MapVariantMode::Online),
|
|
|
MapVariant::new(MapArea::VrTempleBeta, MapVariantMode::Online),
|
|
|
MapVariant::new(MapArea::VrTempleBeta, MapVariantMode::Online),
|
|
@ -244,7 +230,7 @@ impl Maps { |
|
|
MapVariant::new(MapArea::GolDragon, MapVariantMode::Online),
|
|
|
MapVariant::new(MapArea::GolDragon, MapVariantMode::Online),
|
|
|
]
|
|
|
]
|
|
|
},
|
|
|
},
|
|
|
(Episode::Two, 1) => {
|
|
|
|
|
|
|
|
|
(Episode::Two, PlayerMode::Single) => {
|
|
|
vec![MapVariant::new(MapArea::Pioneer2Ep2, MapVariantMode::Offline),
|
|
|
vec![MapVariant::new(MapArea::Pioneer2Ep2, MapVariantMode::Offline),
|
|
|
MapVariant::new(MapArea::VrTempleAlpha, MapVariantMode::Offline),
|
|
|
MapVariant::new(MapArea::VrTempleAlpha, MapVariantMode::Offline),
|
|
|
MapVariant::new(MapArea::VrTempleBeta, MapVariantMode::Offline),
|
|
|
MapVariant::new(MapArea::VrTempleBeta, MapVariantMode::Offline),
|
|
@ -263,7 +249,7 @@ impl Maps { |
|
|
MapVariant::new(MapArea::GolDragon, MapVariantMode::Offline),
|
|
|
MapVariant::new(MapArea::GolDragon, MapVariantMode::Offline),
|
|
|
]
|
|
|
]
|
|
|
},
|
|
|
},
|
|
|
(Episode::Four, _) => {
|
|
|
|
|
|
|
|
|
(Episode::Four, PlayerMode::Multi) => {
|
|
|
vec![MapVariant::new(MapArea::Pioneer2Ep4, MapVariantMode::Online),
|
|
|
vec![MapVariant::new(MapArea::Pioneer2Ep4, MapVariantMode::Online),
|
|
|
MapVariant::new(MapArea::CraterEast, MapVariantMode::Online),
|
|
|
MapVariant::new(MapArea::CraterEast, MapVariantMode::Online),
|
|
|
MapVariant::new(MapArea::CraterWest, MapVariantMode::Online),
|
|
|
MapVariant::new(MapArea::CraterWest, MapVariantMode::Online),
|
|
@ -276,21 +262,42 @@ impl Maps { |
|
|
MapVariant::new(MapArea::SaintMillion, MapVariantMode::Online),
|
|
|
MapVariant::new(MapArea::SaintMillion, MapVariantMode::Online),
|
|
|
]
|
|
|
]
|
|
|
},
|
|
|
},
|
|
|
_ => unreachable!()
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
(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 {
|
|
|
Maps {
|
|
|
enemy_data: map_variants.iter()
|
|
|
|
|
|
.flat_map(|map_variant| {
|
|
|
|
|
|
enemy_data_from_map_data(map_variant, &room_mode.episode())
|
|
|
|
|
|
})
|
|
|
|
|
|
.map(|enemy| apply_rare_enemy(enemy, rare_monster_table, 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,
|
|
|
map_variants,
|
|
|
|
|
|
enemy_data,
|
|
|
|
|
|
object_data,
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
@ -322,7 +329,7 @@ impl Maps { |
|
|
{
|
|
|
{
|
|
|
self.enemy_data = enemies
|
|
|
self.enemy_data = enemies
|
|
|
.into_iter()
|
|
|
.into_iter()
|
|
|
.map(|enemy| apply_rare_enemy(enemy, rare_monster_table, event))
|
|
|
|
|
|
|
|
|
.map(|enemy| enemy.map(|enemy| rare_monster_table.apply(enemy, event)))
|
|
|
.collect();
|
|
|
.collect();
|
|
|
self.object_data = objects;
|
|
|
self.object_data = objects;
|
|
|
}
|
|
|
}
|
|
@ -351,13 +358,37 @@ impl Maps { |
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
fn apply_rare_enemy(enemy: Option<MapEnemy>, rare_enemy_table: &RareMonsterAppearTable, event: ShipEvent) -> Option<MapEnemy> {
|
|
|
|
|
|
enemy.map(|enemy| {
|
|
|
|
|
|
if enemy.can_be_rare() && rare_enemy_table.roll_is_rare(&enemy.monster) {
|
|
|
|
|
|
enemy.into_rare(event)
|
|
|
|
|
|
|
|
|
pub trait MapBuilder: Send + Sync {
|
|
|
|
|
|
fn generate_maps(&self, room_mode: RoomMode, event: ShipEvent) -> Maps;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[derive(Clone)]
|
|
|
|
|
|
pub struct FreeRoamMapBuilder {
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
impl FreeRoamMapBuilder {
|
|
|
|
|
|
pub fn new() -> FreeRoamMapBuilder {
|
|
|
|
|
|
FreeRoamMapBuilder {
|
|
|
}
|
|
|
}
|
|
|
else {
|
|
|
|
|
|
enemy
|
|
|
|
|
|
}
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
impl MapBuilder for FreeRoamMapBuilder {
|
|
|
|
|
|
fn generate_maps(&self, room_mode: RoomMode, event: ShipEvent) -> Maps {
|
|
|
|
|
|
let rare_monster_table = RareMonsterAppearTable::new(room_mode.episode());
|
|
|
|
|
|
let map_variants = 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,
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
}
|
|
|
}
|