#[macro_use] extern crate specs_derive; #[macro_use] extern crate specs_system_macro; use carpet::Coord; use ggez::GameError; use rand::Rng; use specs::prelude::*; #[derive(PartialEq, Clone, Copy, Debug)] pub enum TileType { Wall, Floor, } impl TileType { fn glyph(&self) -> carpet::CP437 { match self { TileType::Wall => carpet::CP437::from_char('#'), TileType::Floor => carpet::CP437::from_u8(0), } } } pub struct Map { tiles: carpet::Board, rooms: Vec, } impl Map { fn new() -> Map { let mut rng = rand::thread_rng(); let mut map = Map { tiles: carpet::Board::new_with_default(80, 50, TileType::Wall), rooms: Vec::new(), }; const MAX_ROOMS: usize = 30; const MIN_SIZE: usize = 6; const MAX_SIZE: usize = 10; for _ in 0..MAX_ROOMS { let w = rng.gen_range(MIN_SIZE, MAX_SIZE); let h = rng.gen_range(MIN_SIZE, MAX_SIZE); let x = rng.gen_range(1, 80 - w); let y = rng.gen_range(1, 50 - h); let room = carpet::Rect::new([x, y], [w, h]); if map.rooms.iter().any(|r| r.overlaps(room)) { continue; } map.carve(room); if let Some(prev) = map.rooms.first() { let c1 = room.center(); let c2 = prev.center(); let join = if rng.gen() { carpet::Coord { x: c1.x, y: c2.y } } else { carpet::Coord { x: c2.x, y: c1.y } }; map.carve(carpet::Rect::from_points(c1, join)); map.carve(carpet::Rect::from_points(join, c2)); } map.rooms.push(room); } map } fn carve(&mut self, rect: carpet::Rect) { let iter = self .tiles .window_iter_mut(rect) .expect(&format!("Rect {:?} of map bounds", rect)); for (_, _, t) in iter { *t = TileType::Floor; } } fn passable(&self, (x, y): (usize, usize)) -> bool { Some(&TileType::Floor) == self.tiles.get(x, y) } } #[derive(Component)] pub struct Renderable { glyph: carpet::CP437, color: carpet::Color, } #[derive(Component)] pub struct Player; impl Player { fn get_entity(world: &mut specs::World) -> Entity { let storage = (&world.read_component::(), &world.entities()); storage .join() .next() .expect("No entities tagged as Player") .1 } } #[derive(Component)] pub struct Motion { down: i8, right: i8, } impl Motion { fn move_player(world: &mut specs::World, down: i8, right: i8) { let player = Player::get_entity(world); world .write_component::() .insert(player, Motion { down, right }) .unwrap(); } } system_impl! { Draw( resource mut game_board: carpet::GameBoard, resource map: Map, renderable: Renderable, pos: Coord, ) { game_board.clear(); for (x, y, t) in map.tiles.iter() { game_board.set([x, y], t.glyph()); } for (p, r) in (&pos, &renderable).join() { game_board.set_with_color([p.x, p.y], r.glyph, r.color); } } } system! { Move ( resource map: Map, mut motion: Motion, mut pos: Coord, ) { let tgt_x = (pos.x as i8 + motion.right) as usize; let tgt_y = (pos.y as i8 + motion.down) as usize; if map.passable((tgt_x, tgt_y)) { pos.x = tgt_x; pos.y = tgt_y; } } finally { motion.clear(); } } fn main() -> Result<(), GameError> { let mut game: carpet::Game = carpet::GameBuilder::new() .name("game") .author("me") .resource_path({ let base = std::env::var("CARGO_MANIFEST_DIR").unwrap(); let mut path = std::path::PathBuf::from(base); path.pop(); path.push("resources"); path }) .tileset("/haberdash.gif", [12, 12]) .map_size(80, 50) .build()?; game.register::(); game.register::(); game.register::(); game.register::(); let map = Map::new(); let player_start = map .rooms .first() .map(|r| r.center()) .unwrap_or([40, 25].into()); game.insert(map); game.create_entity() .with(player_start) .with(Player) .with(Renderable { glyph: carpet::CP437::from_char('@'), color: carpet::Color::Blue, }) .build(); { // set up all the keybindings use carpet::VirtualKeyCode::*; let none = carpet::KeyMods::NONE; game.on_key((W, none), |world| Motion::move_player(world, -1, 0)); game.on_key((A, none), |world| Motion::move_player(world, 0, -1)); game.on_key((S, none), |world| Motion::move_player(world, 1, 0)); game.on_key((D, none), |world| Motion::move_player(world, 0, 1)); } game.run_with_systems(|world| { Draw.run_now(&world); Move.run_now(&world); }) }