main.rs 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  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 specs::prelude::*;
  8. use rand::Rng;
  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.tiles.window_iter_mut(rect)
  63. .expect(&format!("Rect {:?} of map bounds", rect));
  64. for (_, _, t) in iter {
  65. *t = TileType::Floor;
  66. }
  67. }
  68. fn passable(&self, (x, y): (usize, usize)) -> bool {
  69. Some(&TileType::Floor) == self.tiles.get(x, y)
  70. }
  71. }
  72. #[derive(Component)]
  73. pub struct Renderable {
  74. glyph: carpet::CP437,
  75. color: carpet::Color,
  76. }
  77. #[derive(Component)]
  78. pub struct Player;
  79. impl Player {
  80. fn get_entity(world: &mut specs::World) -> Entity {
  81. let storage = (&world.read_component::<Player>(), &world.entities());
  82. storage.join().next().expect("No entities tagged as Player").1
  83. }
  84. }
  85. #[derive(Component)]
  86. pub struct Motion {
  87. down: i8,
  88. right: i8,
  89. }
  90. impl Motion {
  91. fn move_player(world: &mut specs::World, down: i8, right: i8) {
  92. let player = Player::get_entity(world);
  93. world.write_component::<Motion>().insert(player, Motion { down, right }).unwrap();
  94. }
  95. }
  96. system_impl! {
  97. Draw(
  98. resource mut game_board: carpet::GameBoard<carpet::CP437>,
  99. resource map: Map,
  100. renderable: Renderable,
  101. pos: Coord,
  102. ) {
  103. game_board.clear();
  104. for (x, y, t) in map.tiles.iter() {
  105. game_board.set([x, y], t.glyph());
  106. }
  107. for (p, r) in (&pos, &renderable).join() {
  108. game_board.set_with_color([p.x, p.y], r.glyph, r.color);
  109. }
  110. }
  111. }
  112. system! {
  113. Move (
  114. resource map: Map,
  115. mut motion: Motion,
  116. mut pos: Coord,
  117. ) {
  118. let tgt_x = (pos.x as i8 + motion.right) as usize;
  119. let tgt_y = (pos.y as i8 + motion.down) as usize;
  120. if map.passable((tgt_x, tgt_y)) {
  121. pos.x = tgt_x;
  122. pos.y = tgt_y;
  123. }
  124. } finally {
  125. motion.clear();
  126. }
  127. }
  128. fn main() -> Result<(), GameError> {
  129. let mut game: carpet::Game<carpet::CP437> =
  130. carpet::GameBuilder::new()
  131. .name("game")
  132. .author("me")
  133. .resource_path({
  134. let base = std::env::var("CARGO_MANIFEST_DIR").unwrap();
  135. let mut path = std::path::PathBuf::from(base);
  136. path.pop();
  137. path.push("resources");
  138. path
  139. })
  140. .tileset("/haberdash.gif", [12, 12])
  141. .map_size(80, 50)
  142. .build()?;
  143. game.register::<Coord>();
  144. game.register::<Renderable>();
  145. game.register::<Motion>();
  146. game.register::<Player>();
  147. let map = Map::new();
  148. let player_start = map.rooms.first().map(|r| r.center()).unwrap_or([40, 25].into());
  149. game.insert(map);
  150. game.create_entity()
  151. .with(player_start)
  152. .with(Player)
  153. .with(Renderable {
  154. glyph: carpet::CP437::from_char('@'),
  155. color: carpet::Color::Blue,
  156. })
  157. .build();
  158. {
  159. // set up all the keybindings
  160. use carpet::VirtualKeyCode::*;
  161. let none = carpet::KeyMods::NONE;
  162. game.on_key((W, none), |world| Motion::move_player(world, -1, 0));
  163. game.on_key((A, none), |world| Motion::move_player(world, 0, -1));
  164. game.on_key((S, none), |world| Motion::move_player(world, 1, 0));
  165. game.on_key((D, none), |world| Motion::move_player(world, 0, 1));
  166. }
  167. game.run_with_systems(|world| {
  168. Draw.run_now(&world);
  169. Move.run_now(&world);
  170. })
  171. }