Adjust collision to use drawn bounding boxes

stinkhead7ds
Ashley N. 2023-10-10 19:15:03 -04:00
parent 5f8bd8c87b
commit bb8fc323f5
3 changed files with 53 additions and 21 deletions

View File

@ -18,4 +18,5 @@ 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" regex = "1.9.5"
euclid = "0.22.9"

View File

@ -1,4 +1,5 @@
use std::{error::Error, fs::read_to_string, path::Path, borrow::Cow}; use std::{error::Error, fs::read_to_string, path::Path, borrow::Cow};
use euclid::default::Rect;
use image::{DynamicImage, GenericImageView}; use image::{DynamicImage, GenericImageView};
use linked_hash_map::LinkedHashMap; use linked_hash_map::LinkedHashMap;
use roxmltree::Node; use roxmltree::Node;
@ -9,6 +10,7 @@ pub struct TiledTilemap {
pub tileset: Vec<TiledTileset>, pub tileset: Vec<TiledTileset>,
pub layers: Vec<Layer>, pub layers: Vec<Layer>,
pub objects: Vec<Object>, pub objects: Vec<Object>,
pub collision: Vec<Rect<u16>>,
pub width: usize, pub width: usize,
pub height: usize pub height: usize
} }
@ -48,9 +50,6 @@ pub enum Layer {
Tile { Tile {
system_plane: SystemPlane, system_plane: SystemPlane,
tiles: Vec<u32> tiles: Vec<u32>
},
Collision {
tiles: Vec<u32>
} }
} }
@ -104,7 +103,6 @@ fn get_layer( layer: Node, map_width: usize, map_height: usize ) -> Result<Optio
system_plane: SystemPlane::MdPlaneB, system_plane: SystemPlane::MdPlaneB,
tiles tiles
} ) ), } ) ),
"collision" => Ok( Some( Layer::Collision { tiles } ) ),
_ => { _ => {
print_warning( &format!( "on layer {}: invalid reskit-layer value {}; ignoring this layer", layer_name, layer_type ) ); print_warning( &format!( "on layer {}: invalid reskit-layer value {}; ignoring this layer", layer_name, layer_type ) );
Ok( None ) Ok( None )
@ -343,16 +341,43 @@ pub fn get_tiled_tilemap( path: &str, object_fields: &Vec<&str> ) -> Result<Tile
.filter_map( | option | option ) .filter_map( | option | option )
.collect(); .collect();
// Print warning if there is no collision layer
if let None = layers.iter().find( | layer | matches!( layer, Layer::Collision { tiles: _ } ) ) {
print_warning( "tile map has no \"collision\" layer: this probably is not what you want" );
}
// Get the entity-component system // Get the entity-component system
let object_group = map.descendants().find( | node | node.tag_name() == "objectgroup".into() ); let object_group = map.descendants().find( | node |
node.tag_name() == "objectgroup".into() &&
node.descendants().find( | node | node.attribute( "name" ) == Some( "reskit-layer" ) && node.attribute( "value" ) == Some( "object" ) ).is_some()
);
let objects = if let Some( object_group ) = object_group { let objects = if let Some( object_group ) = object_group {
get_objs( &object_group, object_fields )? get_objs( &object_group, object_fields )?
} else { } else {
print_warning( "no object layer in this file, this is probably not what you want..." );
Vec::new()
};
// Get collision
let collision_group = map.descendants().find( | node |
node.tag_name() == "objectgroup".into() &&
node.descendants().find( | node | node.attribute( "name" ) == Some( "reskit-layer" ) && node.attribute( "value" ) == Some( "collision" ) ).is_some()
);
let collision = if let Some( collision_group ) = collision_group {
let mut result = Vec::new();
let objects = collision_group.descendants().filter( | node | node.tag_name() == "object".into() );
for object in objects {
let x: u16 = object.attribute( "x" ).ok_or( "invalid file: no \"x\" attribute in collision bounding box" )?.parse()?;
let y: u16 = object.attribute( "y" ).ok_or( "invalid file: no \"y\" attribute in collision bounding box" )?.parse()?;
let width: u16 = object.attribute( "width" ).ok_or( "invalid file: no \"width\" attribute in collision bounding box" )?.parse()?;
let height: u16 = object.attribute( "height" ).ok_or( "invalid file: no \"height\" attribute in collision bounding box" )?.parse()?;
result.push( euclid::rect( x, y, width, height ) );
}
if result.is_empty() {
print_warning( "collision layer present but no bounding boxes are defined, this is probably not what you want..." );
}
result
} else {
print_warning( "no collision layer in this file, this is probably not what you want..." );
Vec::new() Vec::new()
}; };
@ -361,6 +386,7 @@ pub fn get_tiled_tilemap( path: &str, object_fields: &Vec<&str> ) -> Result<Tile
tileset, tileset,
layers, layers,
objects, objects,
collision,
width, width,
height height
} }

View File

@ -199,17 +199,22 @@ pub fn get_tilemap( tilemap: &TiledTilemap ) -> Result<Vec<u8>, Box<dyn Error>>
pub fn get_collision_map( tilemap: &TiledTilemap ) -> Result<Vec<u8>, Box<dyn Error>> { pub fn get_collision_map( tilemap: &TiledTilemap ) -> Result<Vec<u8>, Box<dyn Error>> {
let mut result: Vec<u8> = Vec::new(); let mut result: Vec<u8> = Vec::new();
let collision: Option<&Layer> = tilemap.layers.iter().find( | layer | matches!( layer, Layer::Collision { tiles: _ } ) ); // 2 bytes: Number of bounding boxes
if let Some( collision ) = collision { result.extend( ( tilemap.collision.len() as u16 ).to_be_bytes() );
let collision = match collision {
Layer::Collision { tiles } => tiles,
_ => return Err( "internal error: invalid object type" )?
};
for collision_data in collision { // For (Number of bounding boxes):
let collision_data: u8 = if *collision_data > 0 { 1 } else { 0 }; for bounding_box in &tilemap.collision {
result.push( collision_data ); // 2 bytes: X dimension
} result.extend( bounding_box.origin.x.to_be_bytes() );
// 2 bytes: Y dimension
result.extend( bounding_box.origin.y.to_be_bytes() );
// 2 bytes: X2 dimension (x + width)
result.extend( ( bounding_box.origin.x + bounding_box.size.width ).to_be_bytes() );
// 2 bytes: Y2 dimension (y + height)
result.extend( ( bounding_box.origin.y + bounding_box.size.height ).to_be_bytes() );
} }
Ok( result ) Ok( result )