mod.rs 5.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. pub mod tile;
  2. use crate::map::tile::Tile;
  3. use ggez::GameError;
  4. use ggez::GameResult;
  5. use std::collections::HashMap;
  6. use std::fs::File;
  7. use std::io::BufReader;
  8. use std::path::Path;
  9. use tiled::{
  10. parse_with_path, Image as TiledImage, Image, Layer, LayerData, Map as TiledMap, Orientation,
  11. PropertyValue, Tile as TiledTile, TiledError, Tileset,
  12. };
  13. fn get_tile_from_terrain_tileset_with_id(
  14. terrain_tileset: &Tileset,
  15. id: u32,
  16. terrain_image_width: u32,
  17. terrain_image_height: u32,
  18. ) -> GameResult<Tile> {
  19. for tile in terrain_tileset.tiles.iter() {
  20. if tile.id == id - terrain_tileset.first_gid {
  21. let str_id = match tile.properties.get("ID") {
  22. None => {
  23. return GameResult::Err(GameError::ResourceLoadError(format!(
  24. "Tile {} have no ID property",
  25. id
  26. )))
  27. }
  28. Some(property_value) => match property_value {
  29. PropertyValue::StringValue(str_id) => str_id.clone(),
  30. _ => {
  31. return GameResult::Err(GameError::ResourceLoadError(format!(
  32. "Tile {} must have String ID property value",
  33. id
  34. )))
  35. }
  36. },
  37. };
  38. let tile_width = terrain_tileset.tile_width;
  39. let tile_height = terrain_tileset.tile_height;
  40. let relative_tile_width = tile_width as f32 / terrain_image_width as f32;
  41. let relative_tile_height = tile_height as f32 / terrain_image_height as f32;
  42. let len_by_width = terrain_image_width / tile_width;
  43. let tile_y = tile.id / len_by_width;
  44. let tile_x = tile.id - (tile_y * len_by_width);
  45. return GameResult::Ok(Tile::from_str_id(
  46. &str_id,
  47. tile_width,
  48. tile_height,
  49. relative_tile_width,
  50. relative_tile_height,
  51. tile_x,
  52. tile_y,
  53. ));
  54. }
  55. }
  56. return GameResult::Err(GameError::ResourceLoadError(format!(
  57. "No tile with {} found",
  58. id
  59. )));
  60. }
  61. pub struct Map {
  62. pub tiled_map: TiledMap,
  63. pub background_image: TiledImage,
  64. pub terrain_image: TiledImage,
  65. pub tiles: HashMap<(u32, u32), Tile>,
  66. }
  67. impl Map {
  68. pub fn new(map_file_path: &Path) -> GameResult<Self> {
  69. let map_file = File::open(map_file_path)?;
  70. let map_file_reader = BufReader::new(map_file);
  71. let tiled_map = match parse_with_path(map_file_reader, map_file_path) {
  72. Ok(map) => map,
  73. Err(e) => {
  74. return GameResult::Err(GameError::ResourceLoadError(format!(
  75. "Fail to parse map: {:?}",
  76. e
  77. )))
  78. }
  79. };
  80. if &tiled_map.orientation != &Orientation::Orthogonal {
  81. return GameResult::Err(GameError::ResourceLoadError(
  82. "Map must be orthogonal orientation".to_string(),
  83. ));
  84. }
  85. // FIXME BS NOW: manage correctly error
  86. let background_image = match &(tiled_map.image_layers.first().unwrap()).image.as_ref() {
  87. None => {
  88. return GameResult::Err(GameError::ResourceLoadError(
  89. "No image layer found in map ".to_string(),
  90. ))
  91. }
  92. Some(image) => image.clone(),
  93. };
  94. let terrain_tileset: Tileset = match tiled_map
  95. .tilesets
  96. .clone()
  97. .into_iter()
  98. .filter(|t| t.name == "terrain")
  99. .collect::<Vec<Tileset>>()
  100. .first()
  101. {
  102. None => {
  103. return GameResult::Err(GameError::ResourceLoadError(
  104. "No terrain tileset found in map ".to_string(),
  105. ))
  106. }
  107. Some(tileset) => tileset.clone(),
  108. };
  109. let terrain_image = {
  110. match terrain_tileset.images.first() {
  111. None => {
  112. return GameResult::Err(GameError::ResourceLoadError(
  113. "No terrain image found in terrain tileset".to_string(),
  114. ))
  115. }
  116. Some(terrain_image) => terrain_image.clone(),
  117. }
  118. };
  119. let terrain_layer: Layer = match tiled_map
  120. .layers
  121. .clone()
  122. .into_iter()
  123. .filter(|l| l.name == "terrain")
  124. .collect::<Vec<Layer>>()
  125. .first()
  126. {
  127. None => {
  128. return GameResult::Err(GameError::ResourceLoadError(
  129. "No terrain layer found in map ".to_string(),
  130. ))
  131. }
  132. Some(layer) => layer.clone(),
  133. };
  134. let mut tiles: HashMap<(u32, u32), Tile> = HashMap::new();
  135. match terrain_layer.tiles {
  136. LayerData::Finite(layer_tiles) => {
  137. for (x, tiles_row) in layer_tiles.iter().enumerate() {
  138. for (y, layer_tile) in tiles_row.iter().enumerate() {
  139. let tile = get_tile_from_terrain_tileset_with_id(
  140. &terrain_tileset,
  141. layer_tile.gid,
  142. terrain_image.width as u32,
  143. terrain_image.height as u32,
  144. )?;
  145. tiles.insert((y as u32, x as u32), tile);
  146. }
  147. }
  148. }
  149. LayerData::Infinite(_) => {
  150. return GameResult::Err(GameError::ResourceLoadError(
  151. "Terrain layer must be finite".to_string(),
  152. ))
  153. }
  154. }
  155. GameResult::Ok(Map {
  156. tiled_map: tiled_map.clone(),
  157. background_image: background_image.clone(),
  158. terrain_image,
  159. tiles,
  160. })
  161. }
  162. }