75 percent done ECS exporter

stinkhead7ds
Ashley N. 2023-09-19 23:54:48 -04:00
parent bd6136903d
commit 0360a875f2
3 changed files with 109 additions and 5 deletions

View File

@ -1,6 +1,6 @@
use std::{error::Error, fs::File, io::Write, path::Path};
use clap::Parser;
use crate::reskit::{tileset, soundtrack::{formats::dmf::DmfModule, engines::echo::engine::{EchoFormat, EchoArtifact}}, utility::{print_good, print_error, print_info}, level::{converter::get_tiled_tilemap, system::{get_tiles, get_code, get_tilemap, get_collision_map}}};
use crate::reskit::{tileset, soundtrack::{formats::dmf::DmfModule, engines::echo::engine::{EchoFormat, EchoArtifact}}, utility::{print_good, print_error, print_info}, level::{converter::get_tiled_tilemap, system::{get_tiles, get_code, get_tilemap, get_collision_map, get_ecs}}};
use super::settings::{Args, Tools, TileOutputFormat, TileOrder};
pub fn run_command() -> Result<(), Box<dyn Error>> {
@ -87,6 +87,10 @@ pub fn run_command() -> Result<(), Box<dyn Error>> {
nametables_bin.write_all( &get_collision_map( &tiled_file )? )?;
print_good( "exported collision.lvc" );
let mut nametables_bin = File::create( format!( "{}objects.ecs", output_directory ) )?;
nametables_bin.write_all( &get_ecs( &tiled_file )? )?;
print_good( "exported objects.ecs" );
let mut code_asm = File::create( format!( "{}level.asm", output_directory ) )?;
code_asm.write_all( &get_code( &tiled_file, "testlevel", "levels/" )?.as_bytes() )?;
print_good( "exported level.asm" );

View File

@ -8,7 +8,7 @@ use super::ecs;
pub struct TiledTilemap {
pub tileset: Vec<TiledTileset>,
pub layers: Vec<Layer>,
ecs: Vec<ecs::Entity>,
pub ecs: Vec<ecs::Entity>,
pub width: usize,
pub height: usize
}

View File

@ -1,6 +1,7 @@
use std::{error::Error, convert::TryInto};
use std::{error::Error, convert::TryInto, cmp::max};
use image::GenericImageView;
use crate::reskit::{tileset::image_to_tiles, utility::symbol_to_pascal, cli::settings::TileOrder};
use linked_hash_set::LinkedHashSet;
use crate::reskit::{tileset::image_to_tiles, utility::{symbol_to_pascal, print_info}, cli::settings::TileOrder};
use super::converter::{TiledTilemap, Layer, SystemPlane};
/**
@ -215,6 +216,101 @@ pub fn get_collision_map( tilemap: &TiledTilemap ) -> Result<Vec<u8>, Box<dyn Er
Ok( result )
}
/**
* Get the entity-component system defined. If none is defined return an empty vec.
*/
pub fn get_ecs( tilemap: &TiledTilemap ) -> Result<Vec<u8>, Box<dyn Error>> {
if tilemap.ecs.is_empty() {
return Ok( vec![] )
}
let mut result: Vec<u8> = Vec::new();
// Build a complete set of component IDs and types
let mut component_ids: LinkedHashSet<String> = LinkedHashSet::new();
let mut types: LinkedHashSet<LinkedHashSet<String>> = LinkedHashSet::new();
for entity in &tilemap.ecs {
// Get the list of components attached to this entity
// Sort alphabetically as order matters in LinkedHashSets
let mut components: Vec<String> = entity.components.keys().map( | id | id.to_lowercase() ).collect();
components.sort();
let components: LinkedHashSet<String> = components.into_iter().collect();
// Assign an index to each unique component id by inserting it into `component_ids`
component_ids.extend( components.clone() );
// Assign this unique combination of components a type id
// by inserting it into `types`
types.insert( components );
}
let largest_type_size: u16 = types.iter()
.map( | components | components.len() as u16 )
.reduce( | prev, current | max( prev, current ) )
.ok_or( "internal error: type is empty" )?;
// Type Table
// 2 bytes: Number of Types
result.extend( ( types.len() as u16 ).to_be_bytes() );
// 2 bytes: Type Definition Union Size
result.extend( largest_type_size.to_be_bytes() );
// For (Number of Types):
for ecs_type in &types {
// (Type Definition Union Size) bytes: List of Component IDs
let mut type_component_ids: Vec<u8> = vec![];
for component in ecs_type {
type_component_ids.push(
component_ids.iter().position( | value | component == value ).ok_or( "internal error: no component" )? as u8
);
}
// Fill the remainder of the union with 0xFF, if needed
let remainder = largest_type_size as usize - type_component_ids.len();
for _ in 0..remainder {
type_component_ids.push( 0xFF );
}
result.extend( type_component_ids );
}
// Object Table
// 2 bytes: Object Table Size
result.extend( ( 512 as u16 ).to_be_bytes() );
// For (Object Table Size)
for entity in &tilemap.ecs {
// Do the thing again
let mut components: Vec<String> = entity.components.keys().map( | id | id.to_lowercase() ).collect();
components.sort();
let components: LinkedHashSet<String> = components.into_iter().collect();
// What type id is this?
let type_id: u8 = types.iter().position( | ecs_type | ecs_type == &components ).ok_or( "internal error: no type id" )? as u8;
// Output type id
result.push( type_id );
}
// Fill the remainder of the sparse Object Array with 0xFF, if needed
let remainder = 512 - tilemap.ecs.len();
for _ in 0..remainder {
result.push( 0xFF );
}
// TODO: Component Attribute Table
// Output IDs to terminal
let component_ids: Vec<String> = component_ids.into_iter().collect();
for i in 0..component_ids.len() {
print_info( &format!( "Component ID {}: {}", i, component_ids[ i ] ) );
}
Ok( result )
}
/**
* Get a helper .asm or .c file that ties all the level components together
*/
@ -246,12 +342,16 @@ pub fn get_code( tilemap: &TiledTilemap, level_name: &str, path_prefix: &str ) -
{level_label}Collision:
incbin '{path_prefix}{level_name}/collision.lvc'
{level_label}Objects:
incbin '{path_prefix}{level_name}/objects.ecs'
{level_label}:
dc.w {width}, {height}, {num_tiles}
dc.l {level_label}Tiles
dc.l {level_label}Palettes
dc.l {level_label}Nametables
dc.l {level_label}Collision"# );
dc.l {level_label}Collision
dc.l {level_label}Objects"# );
Ok( file )
}