mod.rs 5.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  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, Layer, LayerData, Map as TiledMap, Orientation,
  11. PropertyValue, 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 terrain_tileset: Tileset,
  66. pub tiles: HashMap<(u32, u32), Tile>,
  67. }
  68. impl Map {
  69. pub fn new(map_file_path: &Path) -> GameResult<Self> {
  70. let map_file = File::open(map_file_path)?;
  71. let map_file_reader = BufReader::new(map_file);
  72. let tiled_map = match parse_with_path(map_file_reader, map_file_path) {
  73. Ok(map) => map,
  74. Err(e) => {
  75. return GameResult::Err(GameError::ResourceLoadError(format!(
  76. "Fail to parse map: {:?}",
  77. e
  78. )))
  79. }
  80. };
  81. if &tiled_map.orientation != &Orientation::Orthogonal {
  82. return GameResult::Err(GameError::ResourceLoadError(
  83. "Map must be orthogonal orientation".to_string(),
  84. ));
  85. }
  86. // FIXME BS NOW: manage correctly error
  87. let background_image = match &(tiled_map.image_layers.first().unwrap()).image.as_ref() {
  88. None => {
  89. return GameResult::Err(GameError::ResourceLoadError(
  90. "No image layer found in map ".to_string(),
  91. ))
  92. }
  93. Some(image) => image.clone(),
  94. };
  95. let terrain_tileset: Tileset = match tiled_map
  96. .tilesets
  97. .clone()
  98. .into_iter()
  99. .filter(|t| t.name == "terrain")
  100. .collect::<Vec<Tileset>>()
  101. .first()
  102. {
  103. None => {
  104. return GameResult::Err(GameError::ResourceLoadError(
  105. "No terrain tileset found in map ".to_string(),
  106. ))
  107. }
  108. Some(tileset) => tileset.clone(),
  109. };
  110. let terrain_image = {
  111. match terrain_tileset.images.first() {
  112. None => {
  113. return GameResult::Err(GameError::ResourceLoadError(
  114. "No terrain image found in terrain tileset".to_string(),
  115. ))
  116. }
  117. Some(terrain_image) => terrain_image.clone(),
  118. }
  119. };
  120. let terrain_layer: Layer = match tiled_map
  121. .layers
  122. .clone()
  123. .into_iter()
  124. .filter(|l| l.name == "terrain")
  125. .collect::<Vec<Layer>>()
  126. .first()
  127. {
  128. None => {
  129. return GameResult::Err(GameError::ResourceLoadError(
  130. "No terrain layer found in map ".to_string(),
  131. ))
  132. }
  133. Some(layer) => layer.clone(),
  134. };
  135. let mut tiles: HashMap<(u32, u32), Tile> = HashMap::new();
  136. match terrain_layer.tiles {
  137. LayerData::Finite(layer_tiles) => {
  138. for (x, tiles_row) in layer_tiles.iter().enumerate() {
  139. for (y, layer_tile) in tiles_row.iter().enumerate() {
  140. let tile = get_tile_from_terrain_tileset_with_id(
  141. &terrain_tileset,
  142. layer_tile.gid,
  143. terrain_image.width as u32,
  144. terrain_image.height as u32,
  145. )?;
  146. tiles.insert((y as u32, x as u32), tile);
  147. }
  148. }
  149. }
  150. LayerData::Infinite(_) => {
  151. return GameResult::Err(GameError::ResourceLoadError(
  152. "Terrain layer must be finite".to_string(),
  153. ))
  154. }
  155. }
  156. GameResult::Ok(Map {
  157. tiled_map: tiled_map.clone(),
  158. background_image: background_image.clone(),
  159. terrain_image,
  160. terrain_tileset,
  161. tiles,
  162. })
  163. }
  164. }