diff --git a/src/reskit/level/system.rs b/src/reskit/level/system.rs index 22edc2f..dc69e5e 100644 --- a/src/reskit/level/system.rs +++ b/src/reskit/level/system.rs @@ -1,8 +1,8 @@ -use std::{error::Error, convert::TryInto, cmp::max}; +use std::{error::Error, convert::TryInto, cmp::max, num::ParseIntError}; use image::GenericImageView; 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}; +use super::{converter::{TiledTilemap, Layer, SystemPlane}, ecs::Component}; /** * Output the .bin and .pal file (using `tileset` tool to build it) containing each of the tiles @@ -241,7 +241,7 @@ pub fn get_ecs( tilemap: &TiledTilemap ) -> Result, Box> { // Assign this unique combination of components a type id // by inserting it into `types` - types.insert( components ); + types.insert_if_absent( components ); } let largest_type_size: u16 = types.iter() @@ -300,7 +300,84 @@ pub fn get_ecs( tilemap: &TiledTilemap ) -> Result, Box> { result.push( 0xFF ); } - // TODO: Component Attribute Table + // Component Attribute Table + // Index all attribute values that do not parse to u16 + let mut attribute_value_ids: LinkedHashSet = LinkedHashSet::new(); + for entity in &tilemap.ecs { + for ( _, component ) in &entity.components { + for ( _, attribute_value ) in &component.attributes { + // Handling various types from Tiled Editor + // * If it is "true" or "false", leave it alone. It will be articulated as 1 or 0. + // * If it can be parsed as a u16, leave it alone. + // * In all other cases, add to attribute_value_ids to create an index. + if attribute_value != "true" && attribute_value != "false" { + let try_as_u16: Result = attribute_value.parse(); + if let Err( _ ) = try_as_u16 { + attribute_value_ids.insert_if_absent( attribute_value.clone() ); + print_info( &format!( "Attribute Value ID {}: \"{}\"", attribute_value_ids.len() - 1, attribute_value ) ); + } + } + } + } + } + + // Find the largest size of a combination of attributes (the component attribute union size) + let largest_type_attributes_size = tilemap.ecs.iter() + .map( | entity | { + let mut total_attributes_for_entity = 0; + + for ( _, component ) in &entity.components { + total_attributes_for_entity += component.attributes.len() + } + + total_attributes_for_entity + } ) + .reduce( | prev, current | max( prev, current ) ) + .ok_or( "internal error: no largest type attributes size" )? as u16; + + // 2 bytes: Component Attribute Union Size + result.extend( largest_type_attributes_size.to_be_bytes() ); + + // For (Object Table Size): + for entity in &tilemap.ecs { + // (Component Attribute Union Size) bytes: Component attribute settings + // Sort components in this entity alphabetically, ascending + let mut components: Vec<(&String, &Component)> = entity.components.iter().collect(); + components.sort_by( | a, b | a.0.partial_cmp( b.0 ).expect( "internal error: unsortable" ) ); + + let mut written_attributes = 0; + for ( _, component ) in &components { + // Sort attributes in each component alphabetically + let mut attributes: Vec<(&String, &String)> = component.attributes.iter().collect(); + attributes.sort_by( | a, b | a.0.partial_cmp( b.0 ).expect( "internal error: unsortable" ) ); + + for ( _, attribute_value ) in attributes { + written_attributes += 1; + match attribute_value.as_str() { + "true" => result.extend( ( 1 as u16 ).to_be_bytes() ), + "false" => result.extend( ( 0 as u16 ).to_be_bytes() ), + string => { + let try_as_u16: Result = string.parse(); + if let Ok( numeric ) = try_as_u16 { + result.extend( numeric.to_be_bytes() ); + } else { + let id: u16 = attribute_value_ids.iter() + .position( | attribute_value | attribute_value == &string ) + .ok_or( "internal error: no type id" )? as u16; + + result.extend( id.to_be_bytes() ); + } + } + } + } + } + + // Fill the remainder of the union with 0xFF, if needed + let remainder = largest_type_attributes_size - written_attributes; + for _ in 0..remainder { + result.extend( ( 0xFFFF as u16 ).to_be_bytes() ); + } + } // Output IDs to terminal let component_ids: Vec = component_ids.into_iter().collect();