diff --git a/CHANGELOG.md b/CHANGELOG.md
index fe131d46..f4e87efa 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 00000000..91aca7bb
--- /dev/null
+++ b/assets/tiled_csv_wangsets.tmx
@@ -0,0 +1,28 @@
+
+
diff --git a/assets/tilesheet_wangsets.tsx b/assets/tilesheet_wangsets.tsx
new file mode 100644
index 00000000..3e6247f1
--- /dev/null
+++ b/assets/tilesheet_wangsets.tsx
@@ -0,0 +1,112 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/error.rs b/src/error.rs
index 32b06fab..66c1f5d1 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 441be445..ed3d06c8 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,
+ /// All the wangsets present in this tileset.
+ pub wang_sets: Vec,
+
/// 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 00000000..6b84f288
--- /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,
+ /// The colors color that can be used to define the corner and/or edge of each Wang tile.
+ pub wang_colors: Vec,
+ /// All the Wang tiles present in this Wang set, indexed by their local IDs.
+ pub wang_tiles: HashMap,
+ /// 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- ,
+ attrs: Vec,
+ ) -> Result {
+ // Get common data
+ let (name, wang_set_type, tile) = get_attrs!(
+ for v in attrs {
+ "name" => name ?= v.parse::(),
+ "type" => wang_set_type ?= v.parse::(),
+ "tile" => tile ?= v.parse::(),
+ }
+ (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 00000000..21a274b5
--- /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,
+ /// 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
- ,
+ attrs: Vec,
+ ) -> Result {
+ // Get common data
+ let (name, color, tile, probability) = get_attrs!(
+ for v in attrs {
+ "name" => name ?= v.parse::(),
+ "color" => color ?= v.parse(),
+ "tile" => tile ?= v.parse::(),
+ "probability" => probability ?= v.parse::(),
+ }
+ (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 00000000..d0c591fb
--- /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 {
+ 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::().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
- ,
+ attrs: Vec,
+ ) -> Result<(TileId, WangTile)> {
+ // Get common data
+ let (tile_id, wang_id) = get_attrs!(
+ for v in attrs {
+ "tileid" => tile_id ?= v.parse::(),
+ "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 c0faf29e..1dac048c 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);
+}