summaryrefslogtreecommitdiff
path: root/src/lowlevel/tileset.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/lowlevel/tileset.rs')
-rw-r--r--src/lowlevel/tileset.rs443
1 files changed, 443 insertions, 0 deletions
diff --git a/src/lowlevel/tileset.rs b/src/lowlevel/tileset.rs
new file mode 100644
index 0000000..0e6dc87
--- /dev/null
+++ b/src/lowlevel/tileset.rs
@@ -0,0 +1,443 @@
+use crate::{ensure_element, ensure_tag_name, get_attribute};
+use anyhow::anyhow;
+use rgb::RGB8;
+
+use super::property::Properties;
+
+pub enum ObjectAlignment {
+ Unspecified,
+ TopLeft,
+ Top,
+ TopRight,
+ Left,
+ Center,
+ Right,
+ BottomLeft,
+ Bottom,
+ BottomRight,
+}
+
+impl Default for ObjectAlignment {
+ fn default() -> Self {
+ ObjectAlignment::Unspecified
+ }
+}
+
+pub struct Frame {
+ /// The local ID of a tile within the parent <tileset>.
+ tileid: u32,
+
+ // How long (in milliseconds) this frame should be displayed before advancing to the next frame.
+ duration: u32,
+}
+
+/// Define properties for a specific tile
+pub struct Tile {
+ /// The local tile ID within its tileset.
+ pub id: u32,
+ /// The type of the tile.
+ /// Refers to an object type and is used by tile objects. (optional) (since 1.0)
+ // TODO pub tile_type: Option<>,
+
+ /// Defines the terrain type of each corner of the tile,
+ /// given as comma-separated indexes in the terrain types array
+ /// in the order top-left, top-right, bottom-left, bottom-right.
+ /// Leaving out a value means that corner has no terrain. (optional)
+ // TODO pub terrain: Option<>,
+
+ /// A percentage indicating the probability that this tile is chosen
+ /// when it competes with others while editing with the terrain tool.
+ /// (defaults to 0)
+ pub probability: f64,
+
+ pub properties: super::property::Properties,
+
+ //TODO Can contain at most one: <image> (since 0.9), <objectgroup>, <animation>
+ pub image: Option<super::image::ImageSource>,
+
+ /// Each tile can have exactly one animation associated with it.
+ /// In the future, there could be support for multiple named animations on a tile.
+ pub animation: Option<Vec<Frame>>,
+}
+
+/// This element is used to specify an offset in pixels,
+/// to be applied when drawing a tile from the related tileset.
+/// When not present, no offset is applied.
+#[derive(Eq, PartialEq, Debug)]
+pub struct TileOffset {
+ /// Horizontal offset in pixels. (defaults to 0)
+ x: isize,
+ /// Vertical offset in pixels (positive is down, defaults to 0)
+ y: isize,
+}
+
+impl TileOffset {
+ fn from_xml(node: roxmltree::Node) -> anyhow::Result<TileOffset> {
+ ensure_element!(node);
+ ensure_tag_name!(node, "tileoffset");
+
+ Ok(TileOffset {
+ x: get_attribute!(node, "x")?.parse()?,
+ y: get_attribute!(node, "y")?.parse()?,
+ })
+ }
+}
+
+impl Default for TileOffset {
+ fn default() -> Self {
+ TileOffset { x: 0, y: 0 }
+ }
+}
+
+#[cfg(test)]
+mod test_tile_offset {
+ use crate::lowlevel::tileset::TileOffset;
+
+ #[test]
+ fn default_value() {
+ let default: TileOffset = Default::default();
+ assert_eq!(default.x, 0);
+ assert_eq!(default.y, 0);
+ }
+
+ #[test]
+ fn from_xml() {
+ crate::parse_property_test!(
+ TileOffset,
+ r##"<tileoffset x="32" y="16" />"##,
+ TileOffset { x: 32, y: 16 }
+ );
+ }
+}
+
+/// Orientation of the grid for the tiles in this tileset (orthogonal or isometric, defaults to orthogonal)
+#[derive(Eq, PartialEq, Debug)]
+pub enum GridOrientation {
+ Orthogonal,
+ Isometric,
+}
+
+impl Default for GridOrientation {
+ fn default() -> Self {
+ GridOrientation::Orthogonal
+ }
+}
+
+/// This element is only used in case of isometric orientation,
+/// and determines how tile overlays for terrain and collision information are rendered.
+#[derive(Eq, PartialEq, Debug)]
+pub struct Grid {
+ orientation: GridOrientation,
+ /// Width of a grid cell
+ width: u32,
+ /// Height of a grid cell
+ height: u32,
+}
+
+impl Grid {
+ fn from_xml(node: roxmltree::Node) -> anyhow::Result<Grid> {
+ ensure_element!(node);
+ ensure_tag_name!(node, "grid");
+ let orientation: GridOrientation = match node.attribute("orientation") {
+ Some("orthogonal") => GridOrientation::Orthogonal,
+ Some("isometric") => GridOrientation::Isometric,
+ _ => GridOrientation::default(),
+ };
+
+ Ok(Grid {
+ orientation,
+ width: get_attribute!(node, "width")?.parse()?,
+ height: get_attribute!(node, "height")?.parse()?,
+ })
+ }
+}
+
+#[cfg(test)]
+mod test_grid {
+ use crate::lowlevel::tileset::{Grid, GridOrientation};
+
+ #[test]
+ fn grid_orientation_default_value() {
+ let default: GridOrientation = Default::default();
+ assert_eq!(default, GridOrientation::Orthogonal);
+ }
+
+ #[test]
+ fn from_xml() {
+ crate::parse_property_test!(
+ Grid,
+ r##"<grid orientation="isometric" height="32" width="32" />"##,
+ Grid {
+ orientation: GridOrientation::Isometric,
+ height: 32,
+ width: 32
+ }
+ );
+ crate::parse_property_test!(
+ Grid,
+ r##"<grid orientation="orthogonal" height="32" width="32" />"##,
+ Grid {
+ orientation: GridOrientation::Orthogonal,
+ height: 32,
+ width: 32
+ }
+ );
+ }
+
+ #[test]
+ fn from_xml_without_explicit_orientation() {
+ crate::parse_property_test!(
+ Grid,
+ r##"<grid height="32" width="32" />"##,
+ Grid {
+ orientation: GridOrientation::default(),
+ height: 32,
+ width: 32
+ }
+ );
+ }
+}
+
+#[derive(PartialEq, Debug)]
+pub struct Terrain {
+ /// The name of the terrain type.
+ pub name: String,
+ /// The local tile-id of the tile that represents the terrain visually.
+ pub tile: u32,
+ pub properties: Properties,
+}
+
+impl Terrain {
+ fn from_xml(node: roxmltree::Node) -> anyhow::Result<Terrain> {
+ ensure_element!(node);
+ ensure_tag_name!(node, "terrain");
+
+ let name = get_attribute!(node, "name")?.to_string();
+ let tile = get_attribute!(node, "tile")?.parse()?;
+ let properties: Properties = match node.children().find(|&c| c.has_tag_name("properties")) {
+ Some(node) => Properties::from_xml(node)?,
+ None => Properties::default(),
+ };
+
+ Ok(Terrain {
+ name,
+ tile,
+ properties,
+ })
+ }
+}
+
+/// A color that can be used to define the corner and/or edge of a Wang tile.
+#[derive(PartialEq, Debug)]
+pub struct WangColor {
+ /// The name of this color.
+ pub name: String,
+ /// The color in #RRGGBB format (example: #c17d11).
+ pub color: RGB8,
+ /// The local tile ID of the tile representing this color.
+ pub tile: u32,
+ /// The relative probability that this color is chosen over others in case of multiple options.
+ /// (defaults to 0)
+ pub probability: f64,
+
+ // Can contain at most one: <properties>
+ pub properties: Properties,
+}
+
+/// “The Wang ID, given by a comma-separated list of indexes
+/// (starting from 1, because 0 means _unset_)
+/// referring to the Wang colors in the Wang set in the following order:
+/// top, top right, right, bottom right, bottom, bottom left, left, top left (since Tiled 1.5).
+/// Before Tiled 1.5, the Wang ID was saved as a 32-bit unsigned integer
+/// stored in the format 0xCECECECE (where each C is a corner color and each E is an edge color,
+/// in reverse order).”
+#[derive(PartialEq, Debug)]
+pub struct WangId {}
+
+///Defines a Wang tile, by referring to a tile in the tileset and associating it with a certain Wang ID.
+#[derive(PartialEq, Debug)]
+pub struct WangTile {
+ /// The tile ID.
+ pub tileid: u32,
+ /// The Wang ID
+ pub wangid: WangId,
+ /// Whether the tile is flipped horizontally (removed in Tiled 1.5).
+ pub hflip: bool,
+ /// Whether the tile is flipped vertically (removed in Tiled 1.5).
+ pub vflip: bool,
+ /// Whether the tile is flipped on its diagonal (removed in Tiled 1.5).
+ pub dflip: bool,
+}
+
+/// Defines a list of corner colors and a list of edge colors,
+/// and any number of Wang tiles using these colors.
+#[derive(PartialEq, Debug)]
+pub struct WangSet {
+ ///The name of the Wang set.
+ pub name: String,
+ /// The tile ID of the tile representing this Wang set.
+ pub tile: u32,
+
+ // Can contain at most one: <properties>
+ pub properties: Properties,
+
+ /// Can contain up to 255: <wangcolor> (since Tiled 1.5)
+ pub wangcolor: Vec<WangColor>,
+
+ /// Can contain any number: <wangtile>
+ pub wangtiles: Vec<WangTile>,
+}
+
+/// This element is used to describe which transformations can be applied to the tiles
+/// (e.g. to extend a Wang set by transforming existing tiles).
+#[derive(PartialEq, Debug)]
+pub struct TileSetTransformations {
+ /// Whether the tiles in this set can be flipped horizontally (default 0)
+ pub hflip: bool,
+ /// Whether the tiles in this set can be flipped vertically (default 0)
+ pub vflip: bool,
+ /// Whether the tiles in this set can be rotated in 90 degree increments (default 0)
+ pub rotate: bool,
+ /// Whether untransformed tiles remain preferred, otherwise transformed tiles are used to produce more variations (default 0)
+ pub prefer_untransformed: bool,
+}
+
+impl Default for TileSetTransformations {
+ fn default() -> Self {
+ TileSetTransformations {
+ hflip: false,
+ vflip: false,
+ rotate: false,
+ prefer_untransformed: false,
+ }
+ }
+}
+
+impl TileSetTransformations {
+ pub fn from_xml(node: roxmltree::Node) -> anyhow::Result<TileSetTransformations> {
+ ensure_element!(node);
+ ensure_tag_name!(node, "transformations");
+ macro_rules! num_string_to_bool {
+ ($attribute_name:expr) => {
+ match node.attribute($attribute_name) {
+ Some("1") => Ok(true),
+ Some("0") => Ok(false),
+ Some(val) => Err(anyhow!(
+ "Unexpected value \"{}\" for {}. Only 0->false and 1->true are allowed",
+ val,
+ $attribute_name
+ )),
+ None => Ok(false),
+ }
+ };
+ }
+
+ Ok(TileSetTransformations {
+ hflip: num_string_to_bool!("hflip")?,
+ vflip: num_string_to_bool!("vflip")?,
+ rotate: num_string_to_bool!("rotate")?,
+ prefer_untransformed: num_string_to_bool!("preferuntransformed")?,
+ })
+ }
+}
+
+//TODO TileSetTransformations tests (parse test and test for default)
+
+#[cfg(test)]
+mod test_transformations {
+ use crate::lowlevel::tileset::TileSetTransformations;
+
+ #[test]
+ fn default_value() {
+ let default: TileSetTransformations = Default::default();
+ assert_eq!(
+ default,
+ TileSetTransformations {
+ hflip: false,
+ vflip: false,
+ rotate: false,
+ prefer_untransformed: false,
+ }
+ );
+ }
+
+ #[test]
+ fn from_xml() {
+ crate::parse_property_test!(
+ TileSetTransformations,
+ r##"<transformations hflip="1" vflip="1" rotate="1" preferuntransformed="1" />"##,
+ TileSetTransformations {
+ hflip: true,
+ vflip: true,
+ rotate: true,
+ prefer_untransformed: true,
+ }
+ );
+ crate::parse_property_test!(
+ TileSetTransformations,
+ r##"<transformations hflip="1" vflip="0" rotate="1" preferuntransformed="0" />"##,
+ TileSetTransformations {
+ hflip: true,
+ vflip: false,
+ rotate: true,
+ prefer_untransformed: false,
+ }
+ );
+ }
+}
+
+pub struct TileSet {
+ /// The name of this tileset.
+ pub name: String,
+ /// The (maximum) width of the tiles in this tileset.
+ pub tile_width: usize,
+
+ /// The (maximum) height of the tiles in this tileset.
+ pub tile_height: usize,
+ /// The spacing in pixels between the tiles in this tileset (applies to the tileset image, defaults to 0)
+ pub spacing: usize,
+
+ /// The margin around the tiles in this tileset (applies to the tileset image, defaults to 0)
+ pub margin: usize,
+
+ /// The number of tiles in this tileset (since 0.13)
+ pub tile_count: usize,
+
+ /// The number of tile columns in the tileset.
+ /// For image collection tilesets it is editable and is used when displaying the tileset. (since 0.15)
+ pub columns: usize,
+
+ /// Controls the alignment for tile objects.
+ /// Valid values are unspecified, topleft, top, topright, left, center, right, bottomleft, bottom and bottomright.
+ /// The default value is unspecified, for compatibility reasons.
+ /// When unspecified, tile objects use bottomleft in orthogonal mode and bottom in isometric mode. (since 1.4)
+ pub object_alignment: ObjectAlignment,
+
+ /// Define properties for specific tiles.
+ /// This doesn't necessarily contain all tiles (only tiles that have properties, like animations or image tiles)
+ /// In fact most of the time it doesn't contain tiles.
+ pub tile: Vec<Tile>,
+
+ pub image: Option<super::image::Image>,
+
+ /// this field is optional, if not set it will get {x:0, y:0} (no offset)
+ pub tile_offset: TileOffset,
+
+ /// (since 1.0)
+ pub grid: Option<Grid>,
+
+ pub properties: super::property::Properties,
+
+ pub terrain_types: Option<Vec<Terrain>>,
+
+ /// (since 1.1)
+ pub wangsets: Option<Vec<WangSet>>,
+
+ /// (since 1.5)
+ pub transformations: TileSetTransformations,
+}
+
+pub enum TileSetElement {
+ Embedded { first_gid: usize, tileset: TileSet },
+ External { first_gid: usize, source: String },
+}