tiled_json_rs/
tile_set.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5use crate::{
6    layer::ObjectGroup,
7    parsers::{parse_color, parse_path, parse_property},
8    wangs::WangSet,
9    Color, TiledValue, Vec2,
10};
11use serde::de::Error;
12use serde::{Deserialize, Deserializer};
13use std::collections::HashMap;
14use std::fs::File;
15use std::path::PathBuf;
16
17/// A tileset that associates information with each tile.
18///
19/// A tileset associates information with each tile such as
20/// image path or terrain type, may include a tiles array property.
21/// Each tile in the `tiles` member has a local id property which
22/// specifies the local ID within the tileset.
23///
24/// Tile sets may be internal to the map, or external files.
25#[derive(Debug, PartialEq, Clone)]
26pub struct TileSet {
27    /// The number of tile columns in the tileset. Eg; dividing the
28    /// associated image in to columns where each column is the width
29    /// of the tile.
30    pub columns: u32,
31    /// GID corresponding to the first tile in the set
32    pub first_gid: u32,
33    /// Path to the image used for tiles in this set
34    pub image: PathBuf,
35    pub image_width: u32,
36    pub image_height: u32,
37    /// Buffer between image edge and first tile in pixels
38    pub margin: u32,
39    /// Spacing between adjacent tiles in image in pixels
40    pub spacing: u32,
41    pub name: String,
42    pub properties: HashMap<String, TiledValue>,
43    pub terrains: Option<Vec<Terrain>>,
44    /// The tile count + the first GID enable finding the tile location
45    /// on the image
46    pub tile_count: u32,
47    pub tile_height: u32,
48    pub tile_width: u32,
49    /// used to specify an offset in pixels, to be applied
50    /// when drawing a tile from this tileset
51    pub tile_offset: Option<Vec2<i32>>,
52    /// Holds *extra* information for tiles such as terrain or animation
53    pub tiles: Option<Vec<Tile>>,
54    /// Defaults to 0,0,0,0 (rgba)
55    pub transparent_color: Color,
56    pub wang_sets: Option<Vec<WangSet>>,
57}
58
59impl<'de> Deserialize<'de> for TileSet {
60    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
61    where
62        D: Deserializer<'de>,
63    {
64        #[derive(Deserialize)]
65        #[serde(rename_all = "lowercase")]
66        struct External {
67            #[serde(rename(deserialize = "firstgid"))]
68            first_gid: u32,
69            source: String,
70        }
71
72        #[derive(Deserialize)]
73        struct Internal {
74            columns: u32,
75            #[serde(rename(deserialize = "firstgid"), default)]
76            first_gid: u32,
77            #[serde(deserialize_with = "parse_path")]
78            image: PathBuf,
79            #[serde(rename(deserialize = "imagewidth"))]
80            image_width: u32,
81            #[serde(rename(deserialize = "imageheight"))]
82            image_height: u32,
83            margin: u32,
84            spacing: u32,
85            name: String,
86            #[serde(deserialize_with = "parse_property", default)]
87            properties: HashMap<String, TiledValue>,
88            terrains: Option<Vec<Terrain>>,
89            #[serde(rename(deserialize = "tilecount"))]
90            tile_count: u32,
91            #[serde(rename(deserialize = "tileheight"))]
92            tile_height: u32,
93            #[serde(rename(deserialize = "tilewidth"))]
94            tile_width: u32,
95            #[serde(rename(deserialize = "tileoffset"))]
96            tile_offset: Option<Vec2<i32>>,
97            tiles: Option<Vec<Tile>>,
98            #[serde(
99                rename(deserialize = "transparentcolor"),
100                deserialize_with = "parse_color",
101                default
102            )]
103            transparent_color: Color,
104            #[serde(rename(deserialize = "wangsets"))]
105            wang_sets: Option<Vec<WangSet>>,
106        }
107
108        #[derive(Deserialize)]
109        #[serde(untagged)]
110        enum Helper {
111            Internal(Internal),
112            External(External),
113        }
114
115        let v = serde_json::Value::deserialize(deserializer)?;
116        if let Ok(m) = Helper::deserialize(&v) {
117            let t = match m {
118                Helper::Internal(v) => v,
119                Helper::External(v) => {
120                    let path = PathBuf::from(v.source);
121                    let file = File::open(path)
122                        .map_err(|e| Error::custom(format!("{:?}", e)))?;
123                    let mut set: Internal = serde_json::from_reader(file)
124                        .map_err(|e| Error::custom(format!("{:?}", e)))?;
125                    set.first_gid = v.first_gid;
126                    set
127                }
128            };
129            let tile_set = TileSet {
130                columns: t.columns,
131                first_gid: t.first_gid,
132                image: t.image,
133                image_width: t.image_width,
134                image_height: t.image_height,
135                margin: t.margin,
136                spacing: t.spacing,
137                name: t.name,
138                properties: t.properties,
139                terrains: t.terrains,
140                tile_count: t.tile_count,
141                tile_height: t.tile_height,
142                tile_width: t.tile_width,
143                tile_offset: t.tile_offset,
144                tiles: t.tiles,
145                transparent_color: t.transparent_color,
146                wang_sets: t.wang_sets,
147            };
148            return Ok(tile_set);
149        } else {
150            Err(Error::custom("could not parse tile-set"))
151        }
152    }
153}
154
155/// Contains all possible data for a tile including an optional `ObjectGroup`
156#[derive(Deserialize, Debug, PartialEq, Clone)]
157pub struct Tile {
158    pub animation: Option<Vec<Frame>>,
159    /// Unlike the ID used in the `TileLayer`, this ID is
160    /// local to the `TileSet` only and so starts at 0 (the
161    /// tile layer ID starts a 1 for tiles with 0 being no-tile).
162    pub id: u32,
163    /// Image representing this tile if it uses a separate image
164    pub image: Option<String>,
165    /// Width of the tile image in pixels
166    #[serde(rename(deserialize = "imagewidth"), default)]
167    pub image_width: u32,
168    /// Height of the tile image in pixels
169    #[serde(rename(deserialize = "imageheight"), default)]
170    pub image_height: u32,
171    #[serde(rename(deserialize = "objectgroup"))]
172    pub object_group: Option<ObjectGroup>,
173    #[serde(deserialize_with = "parse_property", default)]
174    pub properties: HashMap<String, TiledValue>,
175    /// The order of indices is: top-left, top-right, bottom-left, bottom-right
176    ///
177    /// Each entry is the index number in to the Terrain array to get the
178    /// specific terrain type for this tile. Typically used in conjunction
179    /// with the tileset structure as the terrain tiles are stored within
180    /// the data there.
181    pub terrain: Option<[i8; 4]>,
182    /// An optional string for describing a type
183    #[serde(rename(deserialize = "type"))]
184    pub tile_type: Option<String>,
185}
186
187/// Data for an individual frame of animation
188#[derive(Deserialize, Debug, PartialEq, Eq, Clone)]
189pub struct Frame {
190    /// Frame duration in milliseconds
191    pub duration: u32,
192    /// Local tile ID representing this frame
193    #[serde(rename(deserialize = "tileid"))]
194    pub tile_id: u32,
195}
196
197#[derive(Deserialize, Debug, PartialEq, Eq, Clone)]
198pub struct Terrain {
199    pub name: String,
200    /// Local ID of the tile for this terrain within the tileset
201    pub tile: u32,
202}