doukutsu-rs/src/stage.rs

281 lines
7.2 KiB
Rust

use std::io::{Cursor, Read};
use std::str::from_utf8;
use byteorder::{LE, ReadBytesExt};
use ggez::{Context, filesystem, GameResult};
use ggez::GameError::ResourceLoadError;
use log::info;
use strum::AsStaticRef;
use crate::map::Map;
#[derive(Debug, EnumIter, AsStaticStr, PartialEq, Eq, Hash, Copy, Clone)]
pub enum NpcType {
#[strum(serialize = "0")]
Zero,
Almo1,
Almo2,
Ballos,
Bllg,
Cemet,
Cent,
Curly,
Dark,
Dr,
Eggs1,
Eggs2,
Frog,
Guest,
Hell,
Heri,
IronH,
Island,
Kings,
Maze,
Miza,
Moon,
Omg,
Plant,
Press,
Priest,
Ravil,
Red,
Regu,
Sand,
Stream,
Sym,
Toro,
TwinD,
Weed,
X,
}
impl NpcType {
pub fn filename(&self) -> String {
["Npc", self.as_static()].join("")
}
}
#[derive(Debug, PartialEq, Eq, Hash)]
pub struct Tileset {
name: String,
}
impl Clone for Tileset {
fn clone(&self) -> Self {
Tileset {
name: self.name.clone(),
}
}
}
impl Tileset {
pub fn new(name: &str) -> Tileset {
Tileset {
name: name.to_owned(),
}
}
pub fn filename(&self) -> String {
["Prt", &self.name].join("")
}
pub fn orig_width(&self) -> usize {
// todo: move to json or something?
if self.name == "Labo" {
return 128;
}
256
}
}
#[derive(Debug, PartialEq, Eq, Hash)]
pub struct Background {
name: String,
}
impl Clone for Background {
fn clone(&self) -> Self {
Background {
name: self.name.clone(),
}
}
}
impl Background {
pub fn new(name: &str) -> Background {
Background {
name: name.to_owned(),
}
}
pub fn filename(&self) -> String {
self.name.to_owned()
}
}
#[derive(Debug, EnumIter, PartialEq, Eq, Hash, Copy, Clone)]
pub enum BackgroundType {
Stationary,
MoveDistant,
MoveNear,
Water,
Black,
Autoscroll,
OutsideWind,
Outside,
}
impl BackgroundType {
pub fn new(id: u8) -> BackgroundType {
match id {
0 => { BackgroundType::Stationary }
1 => { BackgroundType::MoveDistant }
2 => { BackgroundType::MoveNear }
3 => { BackgroundType::Water }
4 => { BackgroundType::Black }
5 => { BackgroundType::Autoscroll }
6 => { BackgroundType::OutsideWind }
7 => { BackgroundType::Outside }
_ => { BackgroundType::Stationary }
}
}
}
#[derive(Debug)]
pub struct StageData {
pub name: String,
pub map: String,
pub boss: String,
pub boss_no: usize,
pub tileset: Tileset,
pub background: Background,
pub background_type: BackgroundType,
pub npc: NpcType,
}
impl Clone for StageData {
fn clone(&self) -> Self {
StageData {
name: self.name.clone(),
map: self.map.clone(),
boss: self.boss.clone(),
boss_no: self.boss_no,
tileset: self.tileset.clone(),
background: self.background.clone(),
background_type: self.background_type,
npc: self.npc,
}
}
}
impl StageData {
pub fn load_stage_table(ctx: &mut Context, root: &str) -> GameResult<Vec<StageData>> {
let stage_tbl_path = [root, "stage.tbl"].join("");
let mrmap_bin_path = [root, "mrmap.bin"].join("");
if filesystem::exists(ctx, &stage_tbl_path) {
let mut stages = Vec::new();
info!("Loading stage table from {}", &stage_tbl_path);
let mut data = Vec::new();
filesystem::open(ctx, stage_tbl_path)?.read_to_end(&mut data)?;
let count = data.len() / 0xe5;
let mut f = Cursor::new(data);
for i in 0..count {}
return Ok(stages);
} else if filesystem::exists(ctx, &mrmap_bin_path) {
let mut stages = Vec::new();
info!("Loading stage table from {}", &mrmap_bin_path);
let mut data = Vec::new();
let mut fh = filesystem::open(ctx, &mrmap_bin_path)?;
let count = fh.read_u32::<LE>()?;
fh.read_to_end(&mut data)?;
if data.len() < count as usize * 0x74 {
return Err(ResourceLoadError("Specified stage table size is bigger than actual number of entries.".to_string()));
}
let mut f = Cursor::new(data);
for _ in 0..count {
let mut map_buf = Box::new(vec![0u8; 0x10]);
let mut boss_buf = Box::new(vec![0u8; 0x10]);
let mut name_buf = Box::new(vec![0u8; 0x22]);
let mut ts_buf = vec![0u8; 0x10];
let mut back_buf = vec![0u8; 0x10];
let mut npc_buf = vec![0u8; 0x10];
f.read_exact(&mut ts_buf)?;
f.read_exact(&mut map_buf)?;
let bg_type = f.read_u8()?;
f.read_exact(&mut back_buf)?;
f.read_exact(&mut npc_buf)?;
f.read_exact(&mut boss_buf)?;
let boss_no = f.read_u8()? as usize;
f.read_exact(&mut name_buf)?;
let tileset = from_utf8(&ts_buf)
.map_err(|_| ResourceLoadError("UTF-8 error in tileset field".to_string()))?
.trim_matches('\0').to_owned();
let map = from_utf8(&map_buf)
.map_err(|_| ResourceLoadError("UTF-8 error in map field".to_string()))?
.trim_matches('\0').to_owned();
let background = from_utf8(&back_buf)
.map_err(|_| ResourceLoadError("UTF-8 error in background field".to_string()))?
.trim_matches('\0').to_owned();
let boss = from_utf8(&boss_buf)
.map_err(|_| ResourceLoadError("UTF-8 error in boss field".to_string()))?
.trim_matches('\0').to_owned();
let name = from_utf8(&name_buf)
.map_err(|_| ResourceLoadError("UTF-8 error in name field".to_string()))?
.trim_matches('\0').to_owned();
let stage = StageData {
name: name.clone(),
map: map.clone(),
boss: boss.clone(),
boss_no,
tileset: Tileset::new(&tileset),
background: Background::new(&background),
background_type: BackgroundType::new(bg_type),
npc: NpcType::Zero,
};
stages.push(stage);
}
return Ok(stages);
}
Err(ResourceLoadError("No stage table found.".to_string()))
}
}
pub struct Stage {
pub map: Map,
pub data: StageData,
}
impl Stage {
pub fn load(ctx: &mut Context, data: &StageData) -> GameResult<Stage> {
let map_file = filesystem::open(ctx, ["/Stage/", &data.map, ".pxm"].join(""))?;
let attrib_file = filesystem::open(ctx, ["/Stage/", &data.tileset.name, ".pxa"].join(""))?;
let map = Map::load_from(map_file, attrib_file)?;
let stage = Stage {
map,
data: data.clone(),
};
Ok(stage)
}
}