Add ECS parsing
parent
0543d9adfc
commit
dc10213f75
|
@ -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"
|
|
@ -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" );
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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 )
|
||||||
|
}
|
|
@ -1 +1,2 @@
|
||||||
pub mod converter;
|
pub mod converter;
|
||||||
|
pub mod ecs;
|
Loading…
Reference in New Issue