1use nanoserde::DeJson;
2
3use macroquad::prelude::*;
4
5use std::collections::HashMap;
6
7mod error;
8mod tiled;
9
10pub use error::Error;
11pub use tiled::layer::Property;
12
13#[derive(Debug, Clone)]
14pub struct Object {
15 pub gid: Option<u32>,
17
18 pub world_x: f32,
19 pub world_y: f32,
20 pub world_w: f32,
21 pub world_h: f32,
22
23 pub tile_x: u32,
24 pub tile_y: u32,
25 pub tile_w: u32,
26 pub tile_h: u32,
27
28 pub name: String,
29
30 pub properties: HashMap<String, String>,
31}
32
33#[derive(Debug)]
34pub struct Tile {
35 pub id: u32,
37 pub tileset: String,
39 pub attrs: String,
41}
42
43#[derive(Debug, Default)]
44pub struct Layer {
45 pub objects: Vec<Object>,
46 pub width: u32,
47 pub height: u32,
48 pub data: Vec<Option<Tile>>,
49 pub opacity: f32,
51 pub image: Option<Texture2D>,
52 pub offsetx: Option<f32>,
53 pub offsety: Option<f32>,
54}
55
56#[derive(Debug)]
57pub struct TileSet {
58 pub texture: Texture2D,
59
60 pub tilewidth: i32,
61 pub tileheight: i32,
62 pub columns: u32,
63 pub spacing: i32,
64 pub margin: i32,
65}
66
67impl TileSet {
68 fn sprite_rect(&self, ix: u32) -> Rect {
69 let sw = self.tilewidth as f32;
70 let sh = self.tileheight as f32;
71 let sx = (ix % self.columns) as f32 * (sw + self.spacing as f32) + self.margin as f32;
72 let sy = (ix / self.columns) as f32 * (sh + self.spacing as f32) + self.margin as f32;
73
74 Rect::new(sx + 1.1, sy + 1.1, sw - 2.2, sh - 2.2)
76 }
77}
78
79#[derive(Debug)]
80pub struct Map {
81 pub layers: HashMap<String, Layer>,
82 pub tilesets: HashMap<String, TileSet>,
83
84 pub raw_tiled_map: tiled::Map,
86}
87
88impl Map {
89 pub fn spr(&self, tileset: &str, sprite: u32, dest: Rect) {
90 if self.tilesets.contains_key(tileset) == false {
91 panic!(
92 "No such tileset: {}, tilesets available: {:?}",
93 tileset,
94 self.tilesets.keys()
95 )
96 }
97 let tileset = &self.tilesets[tileset];
98 let spr_rect = tileset.sprite_rect(sprite);
99
100 draw_texture_ex(
101 &tileset.texture,
102 dest.x,
103 dest.y,
104 WHITE,
105 DrawTextureParams {
106 dest_size: Some(vec2(dest.w, dest.h)),
107 source: Some(Rect::new(
108 spr_rect.x - 1.0,
109 spr_rect.y - 1.0,
110 spr_rect.w + 2.0,
111 spr_rect.h + 2.0,
112 )),
113 ..Default::default()
114 },
115 );
116 }
117
118 pub fn spr_ex(&self, tileset: &str, source: Rect, dest: Rect) {
119 let tileset = &self.tilesets[tileset];
120
121 draw_texture_ex(
122 &tileset.texture,
123 dest.x,
124 dest.y,
125 WHITE,
126 DrawTextureParams {
127 dest_size: Some(vec2(dest.w, dest.h)),
128 source: Some(source),
129 ..Default::default()
130 },
131 );
132 }
133
134 pub fn contains_layer(&self, layer: &str) -> bool {
135 self.layers.contains_key(layer)
136 }
137
138 pub fn draw_tiles(&self, layer: &str, dest: Rect, source: impl Into<Option<Rect>>) {
139 assert!(self.layers.contains_key(layer), "No such layer: {}", layer);
140
141 let source = source.into().unwrap_or(Rect::new(
142 0.,
143 0.,
144 self.raw_tiled_map.width as f32,
145 self.raw_tiled_map.height as f32,
146 ));
147 let layer = &self.layers[layer];
148
149 let spr_width = dest.w / source.w;
150 let spr_height = dest.h / source.h;
151
152 let mut separated_by_ts: HashMap<&str, Vec<(&Tile, Rect)>> = HashMap::new();
153
154 for y in source.y as u32..source.y as u32 + source.h as u32 {
155 for x in source.x as u32..source.x as u32 + source.w as u32 {
156 if let Some(tile) = &layer
157 .data
158 .get((y * layer.width + x) as usize)
159 .unwrap_or(&None)
160 {
161 if !separated_by_ts.contains_key(tile.tileset.as_str()) {
162 separated_by_ts.insert(&tile.tileset, vec![]);
163 }
164
165 let pos = vec2(
166 (x - source.x as u32) as f32 / source.w * dest.w + dest.x,
167 (y - source.y as u32) as f32 / source.h * dest.h + dest.y,
168 );
169 separated_by_ts
170 .get_mut(tile.tileset.as_str())
171 .unwrap()
172 .push((&tile, Rect::new(pos.x, pos.y, spr_width, spr_height)));
173 }
174 }
175 }
176
177 for (tileset, tileset_layer) in &separated_by_ts {
178 for (tile, rect) in tileset_layer {
179 self.spr(tileset, tile.id, *rect);
180 }
181 }
182 }
183
184 pub fn draw_imglayer(&self, layer: &str, dest: Rect, source: Option<Rect>) {
185 assert!(self.layers.contains_key(layer), "No such layer: {}", layer);
186 let layer = &self.layers[layer];
187 assert!(layer.image.is_some(), "No texture found.");
188 let img_texture = layer.image.clone().unwrap();
189 let dest_width_frac =
190 img_texture.width() / (self.raw_tiled_map.width * self.raw_tiled_map.tilewidth) as f32;
191 let dest_height_frac =
192 img_texture.height() / (self.raw_tiled_map.height * self.raw_tiled_map.height) as f32;
193
194 let source = source.unwrap_or(Rect::new(0., 0., img_texture.width(), img_texture.height()));
195 draw_texture_ex(
196 &img_texture,
197 (layer.offsetx.unwrap() - source.x) / source.w * dest.w + dest.x,
198 (layer.offsety.unwrap() - source.y) / source.h * dest.h + dest.y,
199 Color {
200 r: 255.,
201 g: 255.,
202 b: 255.,
203 a: layer.opacity,
204 },
205 DrawTextureParams {
206 dest_size: Some(vec2(dest.w * dest_width_frac, dest.h * dest_height_frac)),
207 source: Some(source),
208 ..Default::default()
209 },
210 );
211 }
212
213 pub fn tiles(&self, layer: &str, rect: impl Into<Option<Rect>>) -> TilesIterator {
214 assert!(self.layers.contains_key(layer), "No such layer: {}", layer);
215
216 let rect = rect.into().unwrap_or(Rect::new(
217 0.,
218 0.,
219 self.raw_tiled_map.width as f32,
220 self.raw_tiled_map.height as f32,
221 ));
222 TilesIterator::new(&self.layers[layer], rect)
223 }
224
225 pub fn get_tile(&self, layer: &str, x: u32, y: u32) -> &Option<Tile> {
226 assert!(self.layers.contains_key(layer), "No such layer: {}", layer);
227
228 let layer = &self.layers[layer];
229
230 if x >= layer.width || y >= layer.height {
231 return &None;
232 }
233
234 &layer.data[(y * layer.width + x) as usize]
235 }
236}
237
238pub struct TilesIterator<'a> {
239 rect: Rect,
240 current: (u32, u32),
241 layer: &'a Layer,
242}
243
244impl<'a> TilesIterator<'a> {
245 fn new(layer: &'a Layer, rect: Rect) -> Self {
246 let current = (rect.x as u32, rect.y as u32);
247
248 TilesIterator {
249 layer,
250 rect,
251 current,
252 }
253 }
254}
255
256impl<'a> Iterator for TilesIterator<'a> {
257 type Item = (u32, u32, &'a Option<Tile>);
258
259 fn next(&mut self) -> Option<Self::Item> {
260 let next_x;
261 let next_y;
262
263 if self.current.0 + 1 >= self.rect.x as u32 + self.rect.w as u32 {
264 next_x = self.rect.x as u32;
265 next_y = self.current.1 + 1;
266 } else {
267 next_x = self.current.0 + 1;
268 next_y = self.current.1;
269 }
270
271 if next_y >= self.rect.y as u32 + self.rect.h as u32 {
272 return None;
273 }
274
275 let res = Some((
276 self.current.0,
277 self.current.1,
278 &self.layer.data[(self.current.1 * self.layer.width + self.current.0) as usize],
279 ));
280 self.current = (next_x, next_y);
281 res
282 }
283}
284
285pub fn load_map(
291 data: &str,
292 textures: &[(&str, Texture2D)],
293 external_tilesets: &[(&str, &str)],
294) -> Result<Map, error::Error> {
295 let map: tiled::Map = DeJson::deserialize_json(data)?;
296
297 let mut layers = HashMap::new();
298 let mut tilesets = HashMap::new();
299 let mut map_tilesets = vec![];
300
301 for tileset in &map.tilesets {
302 let tileset = if tileset.source.is_empty() {
303 tileset.clone()
304 } else {
305 let tileset_data = external_tilesets
306 .iter()
307 .find(|(name, _)| *name == &tileset.source)
308 .unwrap();
309 let mut map_tileset: tiled::Tileset = DeJson::deserialize_json(&tileset_data.1)?;
310 map_tileset.firstgid = tileset.firstgid;
311 map_tileset
312 };
313
314 let texture = &textures
315 .iter()
316 .find(|(name, _)| *name == tileset.image)
317 .ok_or(error::Error::TextureNotFound {
318 texture: tileset.image.clone(),
319 })?
320 .1;
321
322 tilesets.insert(
323 tileset.name.clone(),
324 TileSet {
325 texture: texture.clone(),
326 columns: tileset.columns as _,
327 margin: tileset.margin,
328 spacing: tileset.spacing,
329 tilewidth: tileset.tilewidth,
330 tileheight: tileset.tileheight,
331 },
332 );
333
334 map_tilesets.push(tileset);
335 }
336
337 for layer in &map.layers {
338 if layers.contains_key(&layer.name) {
339 return Err(error::Error::NonUniqueLayerName {
340 layer: layer.name.clone(),
341 });
342 }
343
344 let tile_width = map.tilewidth as f32;
345 let tile_height = map.tileheight as f32;
346
347 let mut objects = vec![];
348 for object in &layer.objects {
349 objects.push(Object {
350 gid: object.gid,
351 world_x: object.x,
352 world_y: object.y,
353 world_w: object.width,
354 world_h: object.height,
355
356 tile_x: (object.x / tile_width) as u32,
357 tile_y: (object.y / tile_height) as u32,
358 tile_w: (object.width / tile_width) as u32,
359 tile_h: (object.height / tile_height) as u32,
360 name: object.name.clone(),
361 properties: object
362 .properties
363 .iter()
364 .map(|property| (property.name.to_string(), property.value.to_string()))
365 .collect(),
366 });
367 }
368
369 let find_tileset = |tile: u32| {
370 map_tilesets.iter().find(|tileset| {
371 tile >= tileset.firstgid && tile < tileset.firstgid + tileset.tilecount
372 })
373 };
374
375 layers.insert(
376 layer.name.clone(),
377 match layer.ty.as_str() {
378 "tilelayer" | "objectlayer" => Layer {
379 objects,
380 width: layer.width,
381 height: layer.height,
382 data: layer
383 .data
384 .iter()
385 .map(|tile| {
386 find_tileset(*tile).map(|tileset| {
387 let attrs = tileset
388 .tiles
389 .iter()
390 .find(|t| t.id as u32 == *tile - tileset.firstgid)
391 .and_then(|tile| tile.ty.clone())
392 .unwrap_or("".to_owned());
393
394 Tile {
395 id: *tile - tileset.firstgid,
396 tileset: tileset.name.clone(),
397 attrs,
398 }
399 })
400 })
401 .collect::<Vec<_>>(),
402 opacity: layer.opacity,
403 ..Default::default()
404 },
405 "imagelayer" => {
406 let img_name = layer.image.clone().unwrap();
407 if img_name == "" {
408 continue;
409 }
410 let offsetx = match layer.offsetx {
411 Some(x) => Some(x as f32),
412 None => Some(0f32),
413 };
414 let offsety = match layer.offsety {
415 Some(y) => Some(y as f32),
416 None => Some(0f32),
417 };
418 let img_texture = &textures
419 .iter()
420 .find(|(name, _)| *name == img_name)
421 .ok_or(error::Error::TextureNotFound { texture: img_name })?
422 .1;
423
424 Layer {
425 image: Some(img_texture.clone()),
426 opacity: layer.opacity,
427 offsetx,
428 offsety,
429 ..Default::default()
430 }
431 }
432 layer_type => {
433 return Err(error::Error::LayerTypeNotFound {
434 layer_type: layer_type.to_string(),
435 })
436 }
437 },
438 );
439 }
440
441 Ok(Map {
442 layers,
443 tilesets,
444 raw_tiled_map: map,
445 })
446}