main.rs 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  1. #[macro_use]
  2. extern crate specs_derive;
  3. #[macro_use]
  4. extern crate specs_system_macro;
  5. use carpet::Coord;
  6. use ggez::GameError;
  7. use rand::Rng;
  8. use specs::prelude::*;
  9. #[derive(PartialEq, Clone, Copy, Debug)]
  10. pub enum TileType {
  11. Wall,
  12. Floor,
  13. }
  14. impl TileType {
  15. fn glyph(&self) -> carpet::CP437 {
  16. match self {
  17. TileType::Wall => carpet::CP437::from_char('#'),
  18. TileType::Floor => carpet::CP437::from_u8(0),
  19. }
  20. }
  21. }
  22. pub struct Map {
  23. tiles: carpet::Board<TileType>,
  24. rooms: Vec<carpet::Rect>,
  25. }
  26. impl Map {
  27. fn new() -> Map {
  28. let mut rng = rand::thread_rng();
  29. let mut map = Map {
  30. tiles: carpet::Board::new_with_default(80, 50, TileType::Wall),
  31. rooms: Vec::new(),
  32. };
  33. const MAX_ROOMS: usize = 30;
  34. const MIN_SIZE: usize = 6;
  35. const MAX_SIZE: usize = 10;
  36. for _ in 0..MAX_ROOMS {
  37. let w = rng.gen_range(MIN_SIZE, MAX_SIZE);
  38. let h = rng.gen_range(MIN_SIZE, MAX_SIZE);
  39. let x = rng.gen_range(1, 80 - w);
  40. let y = rng.gen_range(1, 50 - h);
  41. let room = carpet::Rect::new([x, y], [w, h]);
  42. if map.rooms.iter().any(|r| r.overlaps(room)) {
  43. continue;
  44. }
  45. map.carve(room);
  46. if let Some(prev) = map.rooms.first() {
  47. let c1 = room.center();
  48. let c2 = prev.center();
  49. let join = if rng.gen() {
  50. carpet::Coord { x: c1.x, y: c2.y }
  51. } else {
  52. carpet::Coord { x: c2.x, y: c1.y }
  53. };
  54. map.carve(carpet::Rect::from_points(c1, join));
  55. map.carve(carpet::Rect::from_points(join, c2));
  56. }
  57. map.rooms.push(room);
  58. }
  59. map
  60. }
  61. fn carve(&mut self, rect: carpet::Rect) {
  62. let iter = self
  63. .tiles
  64. .window_iter_mut(rect)
  65. .expect(&format!("Rect {:?} of map bounds", rect));
  66. for (_, _, t) in iter {
  67. *t = TileType::Floor;
  68. }
  69. }
  70. fn passable(&self, (x, y): (usize, usize)) -> bool {
  71. Some(&TileType::Floor) == self.tiles.get(x, y)
  72. }
  73. }
  74. #[derive(Component)]
  75. pub struct Renderable {
  76. glyph: carpet::CP437,
  77. color: carpet::Color,
  78. }
  79. #[derive(Component)]
  80. pub struct Player;
  81. impl Player {
  82. fn get_entity(world: &mut specs::World) -> Entity {
  83. let storage = (&world.read_component::<Player>(), &world.entities());
  84. storage
  85. .join()
  86. .next()
  87. .expect("No entities tagged as Player")
  88. .1
  89. }
  90. }
  91. #[derive(Component)]
  92. pub struct Motion {
  93. down: i8,
  94. right: i8,
  95. }
  96. impl Motion {
  97. fn move_player(world: &mut specs::World, down: i8, right: i8) {
  98. let player = Player::get_entity(world);
  99. world
  100. .write_component::<Motion>()
  101. .insert(player, Motion { down, right })
  102. .unwrap();
  103. }
  104. }
  105. system_impl! {
  106. Draw(
  107. resource mut game_board: carpet::GameBoard<carpet::CP437>,
  108. resource map: Map,
  109. renderable: Renderable,
  110. pos: Coord,
  111. ) {
  112. game_board.clear();
  113. for (x, y, t) in map.tiles.iter() {
  114. game_board.set([x, y], t.glyph());
  115. }
  116. for (p, r) in (&pos, &renderable).join() {
  117. game_board.set_with_color([p.x, p.y], r.glyph, r.color);
  118. }
  119. }
  120. }
  121. system! {
  122. Move (
  123. resource map: Map,
  124. mut motion: Motion,
  125. mut pos: Coord,
  126. ) {
  127. let tgt_x = (pos.x as i8 + motion.right) as usize;
  128. let tgt_y = (pos.y as i8 + motion.down) as usize;
  129. if map.passable((tgt_x, tgt_y)) {
  130. pos.x = tgt_x;
  131. pos.y = tgt_y;
  132. }
  133. } finally {
  134. motion.clear();
  135. }
  136. }
  137. fn main() -> Result<(), GameError> {
  138. let mut game: carpet::Game<carpet::CP437> = carpet::GameBuilder::new()
  139. .name("game")
  140. .author("me")
  141. .resource_path({
  142. let base = std::env::var("CARGO_MANIFEST_DIR").unwrap();
  143. let mut path = std::path::PathBuf::from(base);
  144. path.pop();
  145. path.push("resources");
  146. path
  147. })
  148. .tileset("/haberdash.gif", [12, 12])
  149. .map_size(80, 50)
  150. .build()?;
  151. game.register::<Coord>();
  152. game.register::<Renderable>();
  153. game.register::<Motion>();
  154. game.register::<Player>();
  155. let map = Map::new();
  156. let player_start = map
  157. .rooms
  158. .first()
  159. .map(|r| r.center())
  160. .unwrap_or([40, 25].into());
  161. game.insert(map);
  162. game.create_entity()
  163. .with(player_start)
  164. .with(Player)
  165. .with(Renderable {
  166. glyph: carpet::CP437::from_char('@'),
  167. color: carpet::Color::Blue,
  168. })
  169. .build();
  170. {
  171. // set up all the keybindings
  172. use carpet::VirtualKeyCode::*;
  173. let none = carpet::KeyMods::NONE;
  174. game.on_key((W, none), |world| Motion::move_player(world, -1, 0));
  175. game.on_key((A, none), |world| Motion::move_player(world, 0, -1));
  176. game.on_key((S, none), |world| Motion::move_player(world, 1, 0));
  177. game.on_key((D, none), |world| Motion::move_player(world, 0, 1));
  178. }
  179. game.run_with_systems(|world| {
  180. Draw.run_now(&world);
  181. Move.run_now(&world);
  182. })
  183. }