From 1a10faf751f319e364b071e0a31d9d151b48280b Mon Sep 17 00:00:00 2001 From: josebaMdeL <60483968+josebaMdeL@users.noreply.github.com> Date: Fri, 5 Aug 2022 12:05:47 +0200 Subject: [PATCH] Added support for WangSets (#226) Closes #112 --- CHANGELOG.md | 4 ++ assets/tiled_csv_wangsets.tmx | 28 ++++++++ assets/tilesheet_wangsets.tsx | 112 ++++++++++++++++++++++++++++++ src/error.rs | 7 ++ src/tileset.rs | 13 ++++ src/tileset/wangset.rs | 101 +++++++++++++++++++++++++++ src/tileset/wangset/wang_color.rs | 63 +++++++++++++++++ src/tileset/wangset/wang_tile.rs | 64 +++++++++++++++++ tests/lib.rs | 22 +++++- 9 files changed, 413 insertions(+), 1 deletion(-) create mode 100644 assets/tiled_csv_wangsets.tmx create mode 100644 assets/tilesheet_wangsets.tsx create mode 100644 src/tileset/wangset.rs create mode 100644 src/tileset/wangset/wang_color.rs create mode 100644 src/tileset/wangset/wang_tile.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index fe131d4..f4e87ef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,10 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [Unreleased] +### Added +- Support for Wang sets. + ## [0.10.2] ### Added - Map-wrapped chunks: `ChunkWrapper`. diff --git a/assets/tiled_csv_wangsets.tmx b/assets/tiled_csv_wangsets.tmx new file mode 100644 index 0000000..91aca7b --- /dev/null +++ b/assets/tiled_csv_wangsets.tmx @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="UTF-8"?> +<map version="1.8" tiledversion="1.8.5" orientation="orthogonal" renderorder="right-down" width="30" height="20" tilewidth="32" tileheight="32" infinite="0" nextlayerid="3" nextobjectid="1"> + <tileset firstgid="1" source="tilesheet_wangsets.tsx"/> + <layer id="1" name="Layer 1" width="30" height="20"> + <data encoding="csv"> +47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47, +47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47, +47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47, +47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47, +47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47, +47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47, +47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47, +47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47, +47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47, +47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47, +47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47, +47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47, +47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47, +47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47, +47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47, +47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47, +47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47, +47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47, +47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47, +47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47 +</data> + </layer> +</map> diff --git a/assets/tilesheet_wangsets.tsx b/assets/tilesheet_wangsets.tsx new file mode 100644 index 0000000..3e6247f --- /dev/null +++ b/assets/tilesheet_wangsets.tsx @@ -0,0 +1,112 @@ +<?xml version="1.0" encoding="UTF-8"?> +<tileset version="1.8" tiledversion="1.8.5" name="tilesheet_wangsets" tilewidth="32" tileheight="32" tilecount="84" columns="14"> + <image source="tilesheet.png" width="448" height="192"/> + <wangsets> + <wangset name="Void" type="mixed" tile="-1"> + <wangcolor name="" color="#ff0000" tile="-1" probability="1"/> + <wangtile tileid="0" wangid="1,1,0,0,0,0,0,1"/> + <wangtile tileid="1" wangid="0,0,0,1,1,1,0,0"/> + <wangtile tileid="14" wangid="0,0,0,0,0,1,1,1"/> + <wangtile tileid="15" wangid="0,1,1,1,0,0,0,0"/> + <wangtile tileid="16" wangid="1,1,1,1,1,1,1,1"/> + <wangtile tileid="17" wangid="1,1,1,1,1,1,1,1"/> + <wangtile tileid="28" wangid="1,1,0,0,0,0,0,1"/> + <wangtile tileid="29" wangid="1,1,0,0,0,0,0,1"/> + <wangtile tileid="56" wangid="1,1,0,0,0,0,0,1"/> + <wangtile tileid="57" wangid="0,0,0,1,1,1,0,0"/> + <wangtile tileid="70" wangid="0,0,0,0,0,1,1,1"/> + <wangtile tileid="71" wangid="0,1,1,1,0,0,0,0"/> + </wangset> + <wangset name="Wall" type="mixed" tile="-1"> + <wangcolor name="Light" color="#00ff00" tile="-1" probability="1"/> + <wangcolor name="Dark" color="#006f00" tile="-1" probability="1"/> + <wangtile tileid="4" wangid="2,2,2,2,2,2,2,2"/> + <wangtile tileid="5" wangid="2,2,0,0,0,2,2,2"/> + <wangtile tileid="6" wangid="2,2,0,0,0,0,0,2"/> + <wangtile tileid="7" wangid="2,2,2,2,0,0,0,2"/> + <wangtile tileid="8" wangid="0,0,0,2,0,0,0,0"/> + <wangtile tileid="9" wangid="0,0,0,0,0,2,0,0"/> + <wangtile tileid="10" wangid="2,2,0,2,0,2,2,2"/> + <wangtile tileid="11" wangid="2,2,2,2,0,2,0,2"/> + <wangtile tileid="12" wangid="2,2,0,2,0,2,0,2"/> + <wangtile tileid="13" wangid="0,2,2,2,0,2,0,2"/> + <wangtile tileid="19" wangid="0,0,0,0,0,2,2,2"/> + <wangtile tileid="21" wangid="0,2,2,2,0,0,0,0"/> + <wangtile tileid="22" wangid="0,2,2,0,0,0,0,0"/> + <wangtile tileid="23" wangid="0,0,0,0,0,0,0,2"/> + <wangtile tileid="24" wangid="0,2,0,2,2,2,2,2"/> + <wangtile tileid="25" wangid="0,2,2,2,2,2,0,2"/> + <wangtile tileid="26" wangid="0,2,0,2,0,2,2,2"/> + <wangtile tileid="27" wangid="0,2,0,2,2,2,0,2"/> + <wangtile tileid="31" wangid="0,0,2,2,2,2,2,0"/> + <wangtile tileid="33" wangid="0,0,0,2,2,2,2,2"/> + <wangtile tileid="34" wangid="0,0,0,2,2,2,0,0"/> + <wangtile tileid="35" wangid="0,2,2,2,1,2,0,0"/> + <wangtile tileid="36" wangid="0,0,0,2,0,2,0,0"/> + <wangtile tileid="37" wangid="0,0,0,0,0,2,0,2"/> + <wangtile tileid="38" wangid="0,2,0,0,0,0,0,2"/> + <wangtile tileid="39" wangid="0,2,0,2,0,0,0,0"/> + <wangtile tileid="40" wangid="1,1,0,1,0,1,0,1"/> + <wangtile tileid="41" wangid="0,1,1,1,0,1,0,1"/> + <wangtile tileid="45" wangid="2,2,2,2,2,2,2,2"/> + <wangtile tileid="46" wangid="1,1,1,1,1,1,1,1"/> + <wangtile tileid="47" wangid="1,1,0,0,0,1,1,1"/> + <wangtile tileid="48" wangid="1,1,0,0,0,0,0,1"/> + <wangtile tileid="49" wangid="1,1,1,1,0,0,0,1"/> + <wangtile tileid="50" wangid="0,0,0,1,0,0,0,0"/> + <wangtile tileid="51" wangid="0,0,0,0,0,1,0,0"/> + <wangtile tileid="52" wangid="1,1,0,1,0,1,1,1"/> + <wangtile tileid="53" wangid="1,1,1,1,0,1,0,1"/> + <wangtile tileid="54" wangid="0,1,0,1,0,1,1,1"/> + <wangtile tileid="55" wangid="0,1,0,1,1,1,0,1"/> + <wangtile tileid="59" wangid="0,0,1,1,1,1,1,0"/> + <wangtile tileid="61" wangid="0,0,0,0,0,1,1,1"/> + <wangtile tileid="63" wangid="0,1,1,1,0,0,0,0"/> + <wangtile tileid="64" wangid="0,1,0,0,0,0,0,0"/> + <wangtile tileid="65" wangid="0,0,0,0,0,0,0,1"/> + <wangtile tileid="66" wangid="0,1,0,1,1,1,1,1"/> + <wangtile tileid="67" wangid="0,1,1,1,1,1,0,1"/> + <wangtile tileid="73" wangid="1,1,2,2,2,2,2,1"/> + <wangtile tileid="75" wangid="0,0,0,1,1,1,1,1"/> + <wangtile tileid="76" wangid="0,0,0,1,1,1,0,0"/> + <wangtile tileid="77" wangid="0,1,1,1,1,1,0,0"/> + <wangtile tileid="78" wangid="0,0,0,1,0,1,0,0"/> + <wangtile tileid="79" wangid="0,0,0,0,0,1,0,1"/> + <wangtile tileid="80" wangid="0,1,0,0,0,0,0,1"/> + <wangtile tileid="81" wangid="0,1,0,1,0,0,0,0"/> + </wangset> + <wangset name="Floor" type="mixed" tile="-1"> + <wangcolor name="" color="#ff0000" tile="-1" probability="1"> + <properties> + <property name="Damage" type="float" value="0"/> + </properties> + </wangcolor> + <wangcolor name="Trap" color="#00ff00" tile="-1" probability="1"> + <properties> + <property name="Damage" type="float" value="32.1"/> + </properties> + </wangcolor> + <wangtile tileid="5" wangid="0,0,1,1,1,0,0,0"/> + <wangtile tileid="6" wangid="0,0,1,1,1,1,1,0"/> + <wangtile tileid="7" wangid="0,0,0,0,1,1,1,0"/> + <wangtile tileid="19" wangid="1,1,1,1,1,0,0,0"/> + <wangtile tileid="20" wangid="1,1,1,1,1,1,1,1"/> + <wangtile tileid="21" wangid="1,0,0,0,1,1,1,1"/> + <wangtile tileid="33" wangid="1,1,1,0,0,0,0,0"/> + <wangtile tileid="34" wangid="1,1,1,0,0,0,1,1"/> + <wangtile tileid="35" wangid="1,0,0,0,0,0,1,1"/> + <wangtile tileid="47" wangid="0,0,1,1,1,0,0,0"/> + <wangtile tileid="48" wangid="0,0,1,1,1,1,1,0"/> + <wangtile tileid="49" wangid="0,0,0,0,1,1,1,0"/> + <wangtile tileid="61" wangid="1,1,1,1,1,0,0,0"/> + <wangtile tileid="62" wangid="2,2,2,2,2,2,2,2"/> + <wangtile tileid="63" wangid="1,0,0,0,1,1,1,1"/> + <wangtile tileid="75" wangid="1,1,1,0,0,0,0,0"/> + <wangtile tileid="76" wangid="1,1,1,0,0,0,1,1"/> + <wangtile tileid="77" wangid="1,0,0,0,0,0,1,1"/> + <properties> + <property name="Movement Cost" type="int" value="1"/> + </properties> + </wangset> + </wangsets> +</tileset> diff --git a/src/error.rs b/src/error.rs index 32b06fa..66c1f5d 100644 --- a/src/error.rs +++ b/src/error.rs @@ -49,6 +49,11 @@ pub enum Error { /// Supported types are `string`, `int`, `float`, `bool`, `color`, `file` and `object`. type_name: String, }, + /// Found a WangId that was not properly formatted. + InvalidWangIdEncoding { + /// Stores the wrongly parsed String. + read_string: String, + }, } /// A result with an error variant of [`crate::Error`]. @@ -93,6 +98,8 @@ impl fmt::Display for Error { write!(fmt, "Invalid property value: {}", description), Error::UnknownPropertyType { type_name } => write!(fmt, "Unknown property value type '{}'", type_name), + Error::InvalidWangIdEncoding{read_string} => + write!(fmt, "\"{}\" is not a valid WangId format", read_string), } } } diff --git a/src/tileset.rs b/src/tileset.rs index 441be44..ed3d06c 100644 --- a/src/tileset.rs +++ b/src/tileset.rs @@ -10,6 +10,9 @@ use crate::properties::{parse_properties, Properties}; use crate::tile::TileData; use crate::{util::*, Gid, Tile, TileId}; +mod wangset; +pub use wangset::{WangColor, WangId, WangSet, WangTile}; + /// A collection of tiles for usage in maps and template objects. /// /// Also see the [TMX docs](https://doc.mapeditor.org/en/stable/reference/tmx-map-format/#tileset). @@ -53,6 +56,9 @@ pub struct Tileset { /// All the tiles present in this tileset, indexed by their local IDs. tiles: HashMap<TileId, TileData>, + /// All the wangsets present in this tileset. + pub wang_sets: Vec<WangSet>, + /// The custom properties of the tileset. pub properties: Properties, } @@ -235,6 +241,7 @@ impl Tileset { let mut image = Option::None; let mut tiles = HashMap::with_capacity(prop.tilecount as usize); let mut properties = HashMap::new(); + let mut wang_sets = Vec::new(); parse_tag!(parser, "tileset", { "image" => |attrs| { @@ -250,6 +257,11 @@ impl Tileset { tiles.insert(id, tile); Ok(()) }, + "wangset" => |attrs| { + let set = WangSet::new(parser, attrs)?; + wang_sets.push(set); + Ok(()) + }, }); // A tileset is considered an image collection tileset if there is no image attribute (because its tiles do). @@ -278,6 +290,7 @@ impl Tileset { tilecount: prop.tilecount, image, tiles, + wang_sets, properties, }) } diff --git a/src/tileset/wangset.rs b/src/tileset/wangset.rs new file mode 100644 index 0000000..6b84f28 --- /dev/null +++ b/src/tileset/wangset.rs @@ -0,0 +1,101 @@ +use std::collections::HashMap; + +use xml::attribute::OwnedAttribute; + +use crate::{ + error::Error, + properties::{parse_properties, Properties}, + util::{get_attrs, parse_tag, XmlEventResult}, + Result, TileId, +}; + +mod wang_color; +pub use wang_color::WangColor; +mod wang_tile; +pub use wang_tile::{WangId, WangTile}; + +/// Wang set's terrain brush connection type. +#[derive(Debug, PartialEq, Clone, Copy)] +pub enum WangSetType { + Corner, + Edge, + Mixed, +} + +impl Default for WangSetType { + fn default() -> Self { + WangSetType::Mixed + } +} + +/// Raw data belonging to a WangSet. +#[derive(Debug, PartialEq, Clone)] +pub struct WangSet { + /// The name of the Wang set. + pub name: String, + /// Type of Wang set. + pub wang_set_type: WangSetType, + /// The tile ID of the tile representing this Wang set. + pub tile: Option<TileId>, + /// The colors color that can be used to define the corner and/or edge of each Wang tile. + pub wang_colors: Vec<WangColor>, + /// All the Wang tiles present in this Wang set, indexed by their local IDs. + pub wang_tiles: HashMap<TileId, WangTile>, + /// The custom properties of this Wang set. + pub properties: Properties, +} + +impl WangSet { + /// Reads data from XML parser to create a WangSet. + pub fn new( + parser: &mut impl Iterator<Item = XmlEventResult>, + attrs: Vec<OwnedAttribute>, + ) -> Result<WangSet> { + // Get common data + let (name, wang_set_type, tile) = get_attrs!( + for v in attrs { + "name" => name ?= v.parse::<String>(), + "type" => wang_set_type ?= v.parse::<String>(), + "tile" => tile ?= v.parse::<i64>(), + } + (name, wang_set_type, tile) + ); + + let wang_set_type = match wang_set_type.as_str() { + "corner" => WangSetType::Corner, + "edge" => WangSetType::Edge, + _ => WangSetType::default(), + }; + let tile = if tile >= 0 { Some(tile as u32) } else { None }; + + // Gather variable data + let mut wang_colors = Vec::new(); + let mut wang_tiles = HashMap::new(); + let mut properties = HashMap::new(); + parse_tag!(parser, "wangset", { + "wangcolor" => |attrs| { + let color = WangColor::new(parser, attrs)?; + wang_colors.push(color); + Ok(()) + }, + "wangtile" => |attrs| { + let (id, t) = WangTile::new(parser, attrs)?; + wang_tiles.insert(id, t); + Ok(()) + }, + "properties" => |_| { + properties = parse_properties(parser)?; + Ok(()) + }, + }); + + Ok(WangSet { + name, + wang_set_type, + tile, + wang_colors, + wang_tiles, + properties, + }) + } +} diff --git a/src/tileset/wangset/wang_color.rs b/src/tileset/wangset/wang_color.rs new file mode 100644 index 0000000..21a274b --- /dev/null +++ b/src/tileset/wangset/wang_color.rs @@ -0,0 +1,63 @@ +use std::collections::HashMap; + +use xml::attribute::OwnedAttribute; + +use crate::{ + error::Error, + properties::{parse_properties, Color, Properties}, + util::{get_attrs, parse_tag, XmlEventResult}, + Result, TileId, +}; + +/// Stores the data of the Wang color. +#[derive(Debug, PartialEq, Clone)] +pub struct WangColor { + /// The name of this color. + pub name: String, + #[allow(missing_docs)] + pub color: Color, + /// The tile ID of the tile representing this color. + pub tile: Option<TileId>, + /// The relative probability that this color is chosen over others in case of multiple options. (defaults to 0) + pub probability: f32, + /// The custom properties of this color. + pub properties: Properties, +} + +impl WangColor { + /// Reads data from XML parser to create a WangColor. + pub fn new( + parser: &mut impl Iterator<Item = XmlEventResult>, + attrs: Vec<OwnedAttribute>, + ) -> Result<WangColor> { + // Get common data + let (name, color, tile, probability) = get_attrs!( + for v in attrs { + "name" => name ?= v.parse::<String>(), + "color" => color ?= v.parse(), + "tile" => tile ?= v.parse::<i64>(), + "probability" => probability ?= v.parse::<f32>(), + } + (name, color, tile, probability) + ); + + let tile = if tile >= 0 { Some(tile as u32) } else { None }; + + // Gather variable data + let mut properties = HashMap::new(); + parse_tag!(parser, "wangcolor", { + "properties" => |_| { + properties = parse_properties(parser)?; + Ok(()) + }, + }); + + Ok(WangColor { + name, + color, + tile, + probability, + properties, + }) + } +} diff --git a/src/tileset/wangset/wang_tile.rs b/src/tileset/wangset/wang_tile.rs new file mode 100644 index 0000000..d0c591f --- /dev/null +++ b/src/tileset/wangset/wang_tile.rs @@ -0,0 +1,64 @@ +use std::str::FromStr; + +use xml::attribute::OwnedAttribute; + +use crate::{ + error::Error, + util::{get_attrs, XmlEventResult}, + Result, TileId, +}; + +/** +The Wang ID, stored as an array of 8 u8 values. +*/ +#[derive(Debug, PartialEq, Clone, Copy)] +pub struct WangId(pub [u8; 8]); + +impl FromStr for WangId { + type Err = Error; + + fn from_str(s: &str) -> std::result::Result<WangId, Error> { + let mut ret = [0u8; 8]; + let values: Vec<&str> = s + .trim_start_matches('[') + .trim_end_matches(']') + .split(',') + .collect(); + if values.len() != 8 { + return Err(Error::InvalidWangIdEncoding { + read_string: s.to_string(), + }); + } + for i in 0..8 { + ret[i] = values[i].parse::<u8>().unwrap_or(0); + } + + Ok(WangId(ret)) + } +} + +/// Stores the Wang ID. +#[derive(Debug, PartialEq, Clone, Copy)] +pub struct WangTile { + #[allow(missing_docs)] + pub wang_id: WangId, +} + +impl WangTile { + /// Reads data from XML parser to create a WangTile. + pub(crate) fn new( + _parser: &mut impl Iterator<Item = XmlEventResult>, + attrs: Vec<OwnedAttribute>, + ) -> Result<(TileId, WangTile)> { + // Get common data + let (tile_id, wang_id) = get_attrs!( + for v in attrs { + "tileid" => tile_id ?= v.parse::<u32>(), + "wangid" => wang_id ?= v.parse(), + } + (tile_id, wang_id) + ); + + Ok((tile_id, WangTile { wang_id })) + } +} diff --git a/tests/lib.rs b/tests/lib.rs index c0faf29..1dac048 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -1,7 +1,7 @@ use std::path::PathBuf; use tiled::{ Color, FiniteTileLayer, GroupLayer, Layer, LayerType, Loader, Map, ObjectLayer, PropertyValue, - ResourceCache, TileLayer, + ResourceCache, TileLayer, WangId, }; fn as_tile_layer<'map>(layer: Layer<'map>) -> TileLayer<'map> { @@ -419,3 +419,23 @@ fn test_group_layers() { layer_tile_3.properties.get("key") ); } + +#[test] +fn test_reading_wang_sets() { + let mut loader = Loader::new(); + let map = loader + .load_tmx_map("assets/tiled_csv_wangsets.tmx") + .unwrap(); + + // We will pick some random data from the wangsets for tessting + let tileset = map.tilesets().get(0).unwrap(); + assert_eq!(tileset.wang_sets.len(), 3); + let wangset_2 = tileset.wang_sets.get(1).unwrap(); + let tile_10 = wangset_2.wang_tiles.get(&10).unwrap(); + assert_eq!(tile_10.wang_id, WangId([2u8, 2, 0, 2, 0, 2, 2, 2])); + let wangset_3 = tileset.wang_sets.get(2).unwrap(); + let color_2 = wangset_3.wang_colors.get(1).unwrap(); + let readed_damage = color_2.properties.get("Damage").unwrap(); + let damage_value = &PropertyValue::FloatValue(32.1); + assert_eq!(readed_damage, damage_value); +} -- GitLab