Browse Source

start adding monsters

Getty Ritter 1 year ago
parent
commit
8cbc339f87
5 changed files with 304 additions and 0 deletions
  1. 15 0
      ch6/Cargo.toml
  2. 37 0
      ch6/src/components.rs
  3. 81 0
      ch6/src/main.rs
  4. 107 0
      ch6/src/map.rs
  5. 64 0
      ch6/src/systems.rs

+ 15 - 0
ch6/Cargo.toml

@@ -0,0 +1,15 @@
+[package]
+name = "ch6"
+version = "0.1.0"
+authors = ["Getty Ritter <gettylefou@gmail.com>"]
+edition = "2018"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+ggez = "*"
+carpet = { path = "../carpet" }
+specs = "*"
+specs-derive = "*"
+specs-system-macro = { git = "https://git.infinitenegativeutility.com/getty/specs-system-macro.git" }
+rand = "*"

+ 37 - 0
ch6/src/components.rs

@@ -0,0 +1,37 @@
+use specs::prelude::*;
+
+#[derive(Component)]
+pub struct Renderable {
+    pub glyph: carpet::CP437,
+    pub color: carpet::Color,
+}
+
+#[derive(Component)]
+pub struct Player;
+
+impl Player {
+    fn get_entity(world: &mut specs::World) -> Entity {
+        let storage = (&world.read_component::<Player>(), &world.entities());
+        storage
+            .join()
+            .next()
+            .expect("No entities tagged as Player")
+            .1
+    }
+}
+
+#[derive(Component)]
+pub struct Motion {
+    pub down: i8,
+    pub right: i8,
+}
+
+impl Motion {
+    pub fn move_player(world: &mut specs::World, down: i8, right: i8) {
+        let player = Player::get_entity(world);
+        world
+            .write_component::<Motion>()
+            .insert(player, Motion { down, right })
+            .unwrap();
+    }
+}

+ 81 - 0
ch6/src/main.rs

@@ -0,0 +1,81 @@
+#[macro_use]
+extern crate specs_derive;
+#[macro_use]
+extern crate specs_system_macro;
+
+mod components;
+mod map;
+mod systems;
+
+use ggez::GameError;
+use specs::prelude::{Builder, RunNow};
+
+use components as com;
+
+fn main() -> Result<(), GameError> {
+    let mut game: carpet::Game<carpet::CP437> = 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::<carpet::Coord>();
+    game.register::<com::Renderable>();
+    game.register::<com::Motion>();
+    game.register::<com::Player>();
+
+    let map = map::Map::new();
+    let player_start = map
+        .rooms
+        .first()
+        .map(|r| r.center())
+        .unwrap_or_else(|| [40, 25].into());
+    let mut vs = carpet::Viewshed::create(&map.tiles, map::Cell::blocks_view);
+    vs.range = Some(20);
+    game.insert(map::Viewshed { vs, dirty: true });
+
+    for r in map.rooms.iter().skip(1) {
+        game.create_entity()
+            .with(r.center())
+            .with(com::Renderable {
+                glyph: carpet::CP437::from_char('g'),
+                color: carpet::Color::Red,
+            })
+            .build();
+    }
+
+    game.insert(map);
+
+    game.create_entity()
+        .with(player_start)
+        .with(com::Player)
+        .with(com::Renderable {
+            glyph: carpet::CP437::from_char('@'),
+            color: carpet::Color::Blue,
+        })
+        .build();
+
+    {
+        // set up all the keybindings
+        use carpet::KeyCode::*;
+        let none = carpet::KeyMods::NONE;
+        game.on_key((W, none), |world| com::Motion::move_player(world, -1, 0));
+        game.on_key((A, none), |world| com::Motion::move_player(world, 0, -1));
+        game.on_key((S, none), |world| com::Motion::move_player(world, 1, 0));
+        game.on_key((D, none), |world| com::Motion::move_player(world, 0, 1));
+    }
+
+    game.run_with_systems(|world| {
+        systems::Visibility.run_now(world);
+        systems::Draw.run_now(world);
+        systems::Move.run_now(world);
+    })
+}

+ 107 - 0
ch6/src/map.rs

@@ -0,0 +1,107 @@
+use rand::Rng;
+
+use specs::prelude::*;
+
+#[derive(PartialEq, Clone, Copy, Debug)]
+pub struct Cell {
+    pub tile: TileType,
+    pub seen: bool,
+}
+
+impl Cell {
+    pub fn glyph(&self) -> carpet::CP437 {
+        match self.tile {
+            TileType::Wall => carpet::CP437::from_char('#'),
+            TileType::Floor => carpet::CP437::from_u8(0),
+        }
+    }
+
+    pub fn blocks_view(&self) -> bool {
+        match self.tile {
+            TileType::Wall => true,
+            TileType::Floor => false,
+        }
+    }
+}
+
+#[derive(PartialEq, Clone, Copy, Debug)]
+pub enum TileType {
+    Wall,
+    Floor,
+}
+
+pub struct Map {
+    pub tiles: carpet::Board<Cell>,
+    pub rooms: Vec<carpet::Rect>,
+}
+
+impl Map {
+    pub fn new() -> Map {
+        let mut rng = rand::thread_rng();
+        let mut map = Map {
+            tiles: carpet::Board::new_with_default(
+                80,
+                50,
+                Cell {
+                    tile: TileType::Wall,
+                    seen: false,
+                },
+            ),
+            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)
+            .unwrap_or_else(|| panic!("Rect {:?} of map bounds", rect));
+        for (_, _, t) in iter {
+            t.tile = TileType::Floor;
+        }
+    }
+
+    pub fn passable(&self, (x, y): (usize, usize)) -> bool {
+        if let Some(cell) = self.tiles.get(x, y) {
+            return cell.tile == TileType::Floor;
+        }
+        false
+    }
+}
+
+#[derive(Component)]
+pub struct Viewshed {
+    pub vs: carpet::Viewshed<Cell>,
+    pub dirty: bool,
+}

+ 64 - 0
ch6/src/systems.rs

@@ -0,0 +1,64 @@
+use crate::components::{Motion, Player, Renderable};
+use crate::map::{Map, Viewshed};
+
+system_impl! {
+    Draw(
+        resource mut game_board: carpet::GameBoard<carpet::CP437>,
+        resource map: Map,
+        resource vs: Viewshed,
+        renderable: Renderable,
+        pos: carpet::Coord,
+    ) {
+        game_board.clear();
+        for (x, y, t) in map.tiles.iter() {
+            if vs.vs.visibility([x, y]) {
+                game_board.set([x, y], t.glyph());
+            } else if t.seen {
+                game_board.set_with_color([x, y], t.glyph(), (0.5, 0.5, 0.5));
+            }
+        }
+        for (p, r) in (&pos, &renderable).join() {
+            if vs.vs.visibility([p.x, p.y]) {
+                game_board.set_with_color([p.x, p.y], r.glyph, r.color);
+            }
+        }
+    }
+}
+
+system! {
+    Move (
+        resource map: Map,
+        resource mut viewshed: Viewshed,
+        mut motion: Motion,
+        mut pos: carpet::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)) {
+            viewshed.dirty = true;
+            pos.x = tgt_x;
+            pos.y = tgt_y;
+        }
+    } finally {
+        motion.clear();
+    }
+}
+
+system! {
+    Visibility(
+        resource mut map: Map,
+        resource mut viewshed: Viewshed,
+        pos: carpet::Coord,
+        _player: Player,
+    ) {
+        if viewshed.dirty {
+            viewshed.vs.calculate_from(&map.tiles, *pos);
+            for (x, y, vis) in viewshed.vs.vis.iter() {
+                if *vis {
+                    map.tiles[(x, y)].seen = true;
+                }
+            }
+            viewshed.dirty = false;
+        }
+    }
+}