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"
convert_case = "0.6.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 euclid::default::Rect;
use image::{DynamicImage, GenericImageView};
use linked_hash_map::LinkedHashMap;
use roxmltree::Node;
@ -9,6 +10,7 @@ pub struct TiledTilemap {
pub tileset: Vec<TiledTileset>,
pub layers: Vec<Layer>,
pub objects: Vec<Object>,
pub collision: Vec<Rect<u16>>,
pub width: usize,
pub height: usize
}
@ -48,9 +50,6 @@ pub enum Layer {
Tile {
system_plane: SystemPlane,
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,
tiles
} ) ),
"collision" => Ok( Some( Layer::Collision { tiles } ) ),
_ => {
print_warning( &format!( "on layer {}: invalid reskit-layer value {}; ignoring this layer", layer_name, layer_type ) );
Ok( None )
@ -343,16 +341,43 @@ pub fn get_tiled_tilemap( path: &str, object_fields: &Vec<&str> ) -> Result<Tile
.filter_map( | option | option )
.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
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 {
get_objs( &object_group, object_fields )?
} 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()
};
@ -361,6 +386,7 @@ pub fn get_tiled_tilemap( path: &str, object_fields: &Vec<&str> ) -> Result<Tile
tileset,
layers,
objects,
collision,
width,
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>> {
let mut result: Vec<u8> = Vec::new();
let collision: Option<&Layer> = tilemap.layers.iter().find( | layer | matches!( layer, Layer::Collision { tiles: _ } ) );
if let Some( collision ) = collision {
let collision = match collision {
Layer::Collision { tiles } => tiles,
_ => return Err( "internal error: invalid object type" )?
};
// 2 bytes: Number of bounding boxes
result.extend( ( tilemap.collision.len() as u16 ).to_be_bytes() );
for collision_data in collision {
let collision_data: u8 = if *collision_data > 0 { 1 } else { 0 };
result.push( collision_data );
}
// For (Number of bounding boxes):
for bounding_box in &tilemap.collision {
// 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 )