152 lines
5.9 KiB
Rust
152 lines
5.9 KiB
Rust
use std::{error::Error, convert::TryInto};
|
|
use crate::reskit::tileset::image_to_tiles;
|
|
use super::converter::{TiledTilemap, Layer, SystemPlane};
|
|
|
|
/**
|
|
* Output the .bin file (using `tileset` tool to build it) containing each of the tiles
|
|
* in the Tiled Editor tileset.
|
|
*/
|
|
pub fn get_tiles( tilemap: &TiledTilemap ) -> Result<Vec<u8>, Box<dyn Error>> {
|
|
let mut palette: [u16; 16] = [ 0; 16 ];
|
|
let mut all_tiles: Vec<u8> = Vec::new();
|
|
|
|
for tile_y in 0..tilemap.tile_height {
|
|
for tile_x in 0..tilemap.tile_width {
|
|
let tile = tilemap.tileset.image.clone().crop(
|
|
tile_x as u32,
|
|
tile_y as u32,
|
|
tilemap.tile_width as u32,
|
|
tilemap.tile_height as u32
|
|
);
|
|
|
|
let tile_bin = image_to_tiles(
|
|
&tile,
|
|
&mut palette,
|
|
"tile"
|
|
);
|
|
|
|
all_tiles.extend( tile_bin );
|
|
}
|
|
}
|
|
|
|
// Define result and write palette to top of result
|
|
let mut result: Vec<u8> = Vec::new();
|
|
for i in 0..palette.len() {
|
|
let bytes = palette[ i ].to_be_bytes();
|
|
for i in 0..2 {
|
|
result.push( bytes[ i ] );
|
|
}
|
|
}
|
|
|
|
// Extend all tiles onto result
|
|
result.extend( all_tiles );
|
|
|
|
Ok( result )
|
|
}
|
|
|
|
/**
|
|
* Output the .map file defining the hardware tilemap for the given Tiled Tilemap.
|
|
* In `--system md`, this outputs tilemap B and then tilemap A
|
|
*/
|
|
pub fn get_tilemap( tilemap: &TiledTilemap ) -> Result<Vec<u8>, Box<dyn Error>> {
|
|
let layer_b: Option<&Layer> = tilemap.layers.iter().find( | layer | matches!( layer, Layer::Tile { system_plane: SystemPlane::MdPlaneB, tiles: _ } ) );
|
|
let layer_a: Option<&Layer> = tilemap.layers.iter().find( | layer | matches!( layer, Layer::Tile { system_plane: SystemPlane::MdPlaneA, tiles: _ } ) );
|
|
|
|
let md_height_per_tile = tilemap.tile_height / 8;
|
|
let md_width_per_tile = tilemap.tile_width / 8;
|
|
|
|
// Each entry in a `--system md` tilemap is 16 bits
|
|
let mut nametable: Vec<u16> = vec![ 0; ( tilemap.width * md_width_per_tile ) * ( tilemap.height * md_height_per_tile ) ];
|
|
|
|
if let Some( layer_b ) = layer_b {
|
|
let layer_b = match layer_b {
|
|
Layer::Tile { system_plane: _, tiles } => tiles,
|
|
_ => return Err( "internal error: invalid object type" )?
|
|
};
|
|
|
|
for y in 0..tilemap.height {
|
|
for x in 0..tilemap.width {
|
|
let target_tile: u32 = *layer_b.get( ( y * tilemap.width ) + x ).ok_or( "internal error: invalid data in tilemap" )?;
|
|
let target_tile: u16 = target_tile.try_into()?;
|
|
if target_tile > 0 {
|
|
let wide_tile_size = md_height_per_tile as u16 * md_width_per_tile as u16;
|
|
let source_tile = ( target_tile - 1 ) * wide_tile_size;
|
|
let x = x * md_width_per_tile;
|
|
let y = y * md_height_per_tile;
|
|
|
|
// From the starting point of x, y "stamp" the target tile's indices
|
|
let mut revolving_counter = 0;
|
|
for y_stamp in y..( y + md_height_per_tile ) {
|
|
for x_stamp in x..( x + md_width_per_tile ) {
|
|
nametable[ ( y_stamp * ( tilemap.width * md_width_per_tile ) ) + x_stamp ] = source_tile + revolving_counter;
|
|
revolving_counter = ( revolving_counter + 1 ) % wide_tile_size;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Just do the same for layer a
|
|
// Copy pasted because i'm lazy and tired
|
|
if let Some( layer_a ) = layer_a {
|
|
let layer_a = match layer_a {
|
|
Layer::Tile { system_plane: _, tiles } => tiles,
|
|
_ => return Err( "internal error: invalid object type" )?
|
|
};
|
|
|
|
for y in 0..tilemap.height {
|
|
for x in 0..tilemap.width {
|
|
let target_tile: u32 = *layer_a.get( ( y * tilemap.width ) + x ).ok_or( "internal error: invalid data in tilemap" )?;
|
|
let target_tile: u16 = target_tile.try_into()?;
|
|
if target_tile > 0 {
|
|
let wide_tile_size = md_height_per_tile as u16 * md_width_per_tile as u16;
|
|
let source_tile = ( target_tile - 1 ) * wide_tile_size;
|
|
let x = x * md_width_per_tile;
|
|
let y = y * md_height_per_tile;
|
|
|
|
// From the starting point of x, y "stamp" the target tile's indices
|
|
let mut revolving_counter = 0;
|
|
for y_stamp in y..( y + md_height_per_tile ) {
|
|
for x_stamp in x..( x + md_width_per_tile ) {
|
|
nametable[ ( y_stamp * ( tilemap.width * md_width_per_tile ) ) + x_stamp ] = source_tile + revolving_counter;
|
|
revolving_counter = ( revolving_counter + 1 ) % wide_tile_size;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Convert the u16's to a series of u8 data
|
|
let mut result: Vec<u8> = Vec::new();
|
|
for i in 0..nametable.len() {
|
|
let bytes = nametable[ i ].to_be_bytes();
|
|
for i in 0..2 {
|
|
result.push( bytes[ i ] );
|
|
}
|
|
}
|
|
Ok( result )
|
|
}
|
|
|
|
/**
|
|
* Get the .lvc collision map (u8 sized for the map dimensions, collision areas are either 0 or 1)
|
|
*/
|
|
pub fn get_collision_map( tilemap: &TiledTilemap ) -> Result<Vec<u8>, Box<dyn Error>> {
|
|
let mut result: Vec<u8> = Vec::new();
|
|
|
|
let collision: Option<&Layer> = tilemap.layers.iter().find( | layer | matches!( layer, Layer::Collision { tiles: _ } ) );
|
|
if let Some( collision ) = collision {
|
|
let collision = match collision {
|
|
Layer::Collision { tiles } => tiles,
|
|
_ => return Err( "internal error: invalid object type" )?
|
|
};
|
|
|
|
for collision_data in collision {
|
|
let collision_data: u8 = if *collision_data > 0 { 1 } else { 0 };
|
|
result.push( collision_data );
|
|
}
|
|
}
|
|
|
|
Ok( result )
|
|
} |