No Description
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

tile.rs 6.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. use error;
  2. use sprite::Sprite;
  3. #[derive(Debug, Clone, Copy, PartialEq, Eq)]
  4. pub struct Tile2D {
  5. pub x: i32,
  6. pub y: i32,
  7. }
  8. impl Tile2D {
  9. pub fn log(self, _msg: &str) {
  10. // log(&format!("{}: ({}, {})", msg, self.x, self.y))
  11. }
  12. }
  13. #[derive(Debug, Clone, Copy)]
  14. pub struct TileDimension {
  15. pub width: i32,
  16. pub height: i32,
  17. }
  18. #[derive(Debug, Clone, Copy, PartialEq, Eq)]
  19. pub enum SokobanColour {
  20. None,
  21. Wood,
  22. Red,
  23. Blue,
  24. Green,
  25. Stone,
  26. }
  27. #[derive(Debug, Clone, Copy)]
  28. pub enum Direction {
  29. North,
  30. East,
  31. South,
  32. West,
  33. }
  34. #[derive(Debug, Clone, Copy, PartialEq, Eq)]
  35. pub enum EntityType {
  36. Block,
  37. Marker,
  38. Wall,
  39. Hole,
  40. SunkenBlock,
  41. Coin,
  42. }
  43. #[derive(Debug, Clone, Copy, PartialEq, Eq)]
  44. pub struct Entity {
  45. pub sprite: Sprite,
  46. pub entity_type: EntityType,
  47. pub colour: SokobanColour,
  48. }
  49. impl Default for Entity {
  50. fn default() -> Entity {
  51. Entity {
  52. sprite: Sprite::Coin,
  53. entity_type: EntityType::Coin,
  54. colour: SokobanColour::None,
  55. }
  56. }
  57. }
  58. impl Entity {
  59. pub fn is_movable(&self) -> bool {
  60. match self.entity_type {
  61. EntityType::Block => true,
  62. _ => false,
  63. }
  64. }
  65. pub fn is_barrier_for_hero(&self) -> bool {
  66. match self.entity_type {
  67. EntityType::Wall => true,
  68. EntityType::Hole => true,
  69. _ => false,
  70. }
  71. }
  72. }
  73. #[derive(Debug, Clone)]
  74. pub struct Tile {
  75. pub entities: Vec<Entity>,
  76. }
  77. impl Tile {
  78. pub fn new() -> Self {
  79. Tile {
  80. entities: Vec::with_capacity(3),
  81. }
  82. }
  83. pub fn replace_with(&mut self, other: &Tile) {
  84. self.entities.clear();
  85. for entity in other.entities.iter() {
  86. self.entities.push(*entity);
  87. }
  88. }
  89. // is this tile in a completed state? (the level is finished if all tiles are completed)
  90. pub fn completed(&self) -> bool {
  91. // tiles with markers need to have an appropriate block on them
  92. match self
  93. .entities
  94. .iter()
  95. .find(|e| e.entity_type == EntityType::Marker)
  96. {
  97. Some(marker) => self.entities.iter().any(|e| {
  98. if e.entity_type == EntityType::Block || e.entity_type == EntityType::SunkenBlock {
  99. e.colour == marker.colour || marker.colour == SokobanColour::None
  100. } else {
  101. false
  102. }
  103. }),
  104. None => true,
  105. }
  106. }
  107. // not just a simple add operation. Has to store the correct result in this tile
  108. // e.g. adding a blue block to a tile with a blue hole will result in a sunken
  109. // blue block sprite replacing the pre-existing blue hole
  110. //
  111. pub fn add_entity(&mut self, entity: Entity) -> error::Result<()> {
  112. // the logic for can_entity_be_moved_here has already be run, so this
  113. // is always going to be a 'legal' move
  114. // the other time this is called is during level creation and it's
  115. // assumed that the level creator knows what they are doing, so once
  116. // again this should be a 'legal' move
  117. if self.entities.is_empty() {
  118. self.entities.push(entity);
  119. return Ok(());
  120. }
  121. if entity.entity_type == EntityType::Block {
  122. let mut replace_hole = false;
  123. let mut hole_colour = SokobanColour::None;
  124. if let Some(first_hole) = self
  125. .entities
  126. .iter()
  127. .find(|e| e.entity_type == EntityType::Hole)
  128. {
  129. replace_hole = true;
  130. hole_colour = first_hole.colour;
  131. }
  132. if replace_hole {
  133. // remove the hole
  134. self.entities.retain(|e| e.entity_type != EntityType::Hole);
  135. // replace with a sunken block of the correct colour
  136. let sprite = match hole_colour {
  137. SokobanColour::None => match entity.colour {
  138. SokobanColour::None => return Err(error::SokobanError::InvalidBlockColour),
  139. SokobanColour::Wood => Sprite::BlockAngledSunkWood,
  140. SokobanColour::Red => Sprite::BlockAngledSunkRed,
  141. SokobanColour::Blue => Sprite::BlockAngledSunkBlue,
  142. SokobanColour::Green => Sprite::BlockAngledSunkGreen,
  143. SokobanColour::Stone => Sprite::BlockAngledSunkStone,
  144. },
  145. SokobanColour::Wood => Sprite::BlockAngledSunkWood,
  146. SokobanColour::Red => Sprite::BlockAngledSunkRed,
  147. SokobanColour::Blue => Sprite::BlockAngledSunkBlue,
  148. SokobanColour::Green => Sprite::BlockAngledSunkGreen,
  149. SokobanColour::Stone => Sprite::BlockAngledSunkStone,
  150. };
  151. self.entities.push(Entity {
  152. sprite,
  153. entity_type: EntityType::SunkenBlock,
  154. colour: entity.colour,
  155. });
  156. return Ok(());
  157. }
  158. }
  159. self.entities.push(entity);
  160. Ok(())
  161. }
  162. pub fn can_hero_be_moved_here(&self) -> bool {
  163. !self.entities.iter().any(|e| e.is_barrier_for_hero())
  164. }
  165. pub fn can_entity_be_moved_here(&self, entity: &Entity) -> bool {
  166. if self.entities.is_empty() {
  167. return true;
  168. }
  169. if entity.entity_type == EntityType::Block {
  170. let matching_colour = self.entities.iter().any(|e| match e.entity_type {
  171. EntityType::Hole => {
  172. // blocks go into their coloured holes, holes without a colour only accept wood blocks
  173. (e.colour == entity.colour)
  174. || (e.colour == SokobanColour::None && entity.colour == SokobanColour::Wood)
  175. }
  176. _ => false,
  177. });
  178. if matching_colour {
  179. return true;
  180. }
  181. if self
  182. .entities
  183. .iter()
  184. .any(|e| e.entity_type == EntityType::Block)
  185. {
  186. // can't add a block on a tile already occupied by another block
  187. return false;
  188. }
  189. }
  190. self.entities.iter().any(|e| match e.entity_type {
  191. EntityType::Wall => false,
  192. EntityType::Hole => false,
  193. _ => true,
  194. })
  195. }
  196. }