Add ECS parsing

stinkhead7ds
Ashley N. 2023-09-10 14:21:57 -04:00
parent 0543d9adfc
commit dc10213f75
5 changed files with 120 additions and 4 deletions

View File

@ -18,3 +18,4 @@ linked_hash_set = "0.1.4"
linked-hash-map = "0.5.6" linked-hash-map = "0.5.6"
convert_case = "0.6.0" convert_case = "0.6.0"
roxmltree = "0.18.0" roxmltree = "0.18.0"
regex = "1.9.5"

View File

@ -1,6 +1,6 @@
use std::{error::Error, fs::File, io::Write, path::Path}; use std::{error::Error, fs::File, io::Write, path::Path};
use clap::Parser; use clap::Parser;
use crate::reskit::{tileset, soundtrack::{formats::dmf::DmfModule, engines::echo::engine::{EchoFormat, EchoArtifact}}, utility::{print_good, print_error}, level::converter::get_tiled_tilemap}; 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};
use super::settings::{Args, Tools, TileOutputFormat, TileOrder}; use super::settings::{Args, Tools, TileOutputFormat, TileOrder};
pub fn run_command() -> Result<(), Box<dyn Error>> { pub fn run_command() -> Result<(), Box<dyn Error>> {
@ -67,7 +67,7 @@ pub fn run_command() -> Result<(), Box<dyn Error>> {
} }
Tools::Level { input_file, output_directory, console, tile_size } => { Tools::Level { input_file, output_directory, console, tile_size } => {
let tiled_file = get_tiled_tilemap( &input_file )?; let tiled_file = get_tiled_tilemap( &input_file )?;
print_info( &format!( "{:#?}", tiled_file ) );
print_good( "file opened without errors" ); print_good( "file opened without errors" );
print_error( "unimplemented" ); print_error( "unimplemented" );
} }

View File

@ -1,27 +1,32 @@
use std::{error::Error, fs::read_to_string}; use std::{error::Error, fs::read_to_string};
use image::DynamicImage; use image::DynamicImage;
use roxmltree::Node; use roxmltree::Node;
use crate::reskit::utility::print_warning; use crate::reskit::utility::print_warning;
use super::ecs;
#[derive(Debug)]
pub struct TiledTilemap { pub struct TiledTilemap {
tileset: TiledTileset, tileset: TiledTileset,
layers: Vec<Layer>, layers: Vec<Layer>,
ecs: Vec<ecs::Entity>,
width: usize, width: usize,
height: usize, height: usize,
tile_width: usize, tile_width: usize,
tile_height: usize tile_height: usize
} }
#[derive(Debug)]
pub struct TiledTileset { pub struct TiledTileset {
image: DynamicImage image: DynamicImage
} }
#[derive(Debug)]
pub enum SystemPlane { pub enum SystemPlane {
MdPlaneA, MdPlaneA,
MdPlaneB MdPlaneB
} }
#[derive(Debug)]
pub enum Layer { pub enum Layer {
Tile { Tile {
system_plane: SystemPlane, system_plane: SystemPlane,
@ -165,10 +170,19 @@ pub fn get_tiled_tilemap( path: &str ) -> Result<TiledTilemap, Box<dyn Error>> {
print_warning( "tile map has no \"collision\" layer: this probably is not what you want" ); print_warning( "tile map has no \"collision\" layer: this probably is not what you want" );
} }
// Get the entity-component system
let object_group = map.descendants().find( | node | node.tag_name() == "objectgroup".into() );
let ecs = if let Some( object_group ) = object_group {
ecs::get_ecs( object_group )?
} else {
Vec::new()
};
Ok( Ok(
TiledTilemap { TiledTilemap {
tileset: TiledTileset { image }, tileset: TiledTileset { image },
layers, layers,
ecs,
width, width,
height, height,
tile_width, tile_width,

100
src/reskit/level/ecs.rs Normal file
View File

@ -0,0 +1,100 @@
use std::{error::Error, collections::HashMap};
use regex::Regex;
use roxmltree::Node;
use crate::reskit::utility::print_warning;
#[derive(Debug)]
pub struct Entity {
pub components: HashMap<String, Component>
}
#[derive(Debug)]
pub struct Component {
pub attributes: HashMap<String, String>
}
pub fn get_ecs( object_group: Node ) -> Result<Vec<Entity>, Box<dyn Error>> {
let mut entities: Vec<Entity> = Vec::new();
let objects: Vec<Node> = object_group.descendants().filter( | node | node.tag_name() == "object".into() ).collect();
for object in objects {
let object_id = object.attribute( "id" ).ok_or( "invalid file: no id attribute set on object" )?;
let object_name = object.attribute( "name" ).unwrap_or( "<no name>" );
let object_name = format!( "({}, ID: {})", object_id, object_name );
let mut entity: Entity = Entity {
components: HashMap::new()
};
// Get attributes for implicit `position` component
let x = object.attribute( "x" ).ok_or( "invalid file: position property not present on object" )?;
let y = object.attribute( "y" ).ok_or( "invalid file: position property not present on object" )?;
let width = object.attribute( "width" ).ok_or( "invalid file: position property not present on object" )?;
let height = object.attribute( "height" ).ok_or( "invalid file: position property not present on object" )?;
entity.components.insert(
format!( "position" ),
Component {
attributes: HashMap::from( [
( format!( "x" ), x.to_owned() ),
( format!( "y" ), y.to_owned() ),
( format!( "width" ), width.to_owned() ),
( format!( "height" ), height.to_owned() )
] )
}
);
let properties = object.descendants().find( | node | node.tag_name() == "properties".into() ).ok_or( "invalid file: no properties in object" )?;
let properties = properties.descendants().filter( | node | node.tag_name() == "property".into() && node.attribute( "name" ).unwrap_or( "" ).starts_with( "reskit-component" ) );
for component_property in properties {
let name = component_property.attribute( "name" ).ok_or( "internal error: name attribute expected" )?;
let prop_type = component_property.attribute( "type" ).unwrap_or( "string" );
let value = component_property.attribute( "value" );
if let Some( value ) = value {
// Set up regex to remove individual parts
let regex = Regex::new( r#"reskit-component\[([a-z0-9_]+)\](\.[a-z0-9_]*)?"# )?;
if let Some( captures ) = regex.captures( name ) {
let component_name = captures.get( 1 ).ok_or( "internal error: regex did not match properly" )?.as_str();
let attribute_name = captures.get( 2 );
if let Some( attribute_name ) = attribute_name {
// Setting is a component attribute setting ("value" is the setting)
let attribute_name = attribute_name.as_str().replace( ".", "" );
if let Some( component ) = entity.components.get_mut( component_name ) {
component.attributes.insert( attribute_name, value.to_owned() );
} else {
return Err( format!( "in object {}: undefined component in reskit-component definition \"{}\"", object_name, name ) )?;
}
} else {
// Setting is a component definition
if prop_type != "bool" {
print_warning( &format!( "in object {}: non-bool type reskit-component definition \"{}\". ignoring...", object_name, name ) );
} else {
if value == "true" {
if entity.components.contains_key( component_name ) {
print_warning( &format!( "in object {}: duplicate reskit-component definition \"{}\". ignoring...", object_name, name ) );
} else {
entity.components.insert(
component_name.to_owned(),
Component { attributes: HashMap::new() }
);
}
} else {
print_warning( &format!( "in object {}: reskit-component definition \"{}\" is set to false. ignoring...", object_name, name ) );
}
}
}
} else {
print_warning( &format!( "in object {}: invalid format for reskit-component attribute \"{}\". ignoring...", object_name, name ) );
}
} else {
print_warning( &format!( "in object {}: no value for reskit-component attribute \"{}\". ignoring...", object_name, name ) );
}
}
entities.push( entity );
}
Ok( entities )
}

View File

@ -1 +1,2 @@
pub mod converter; pub mod converter;
pub mod ecs;