Browse Source

Start ch5 by splitting into multiple modules

Getty Ritter 1 year ago
parent
commit
d99b134e47
7 changed files with 243 additions and 0 deletions
  1. 12 0
      Cargo.lock
  2. 1 0
      Cargo.toml
  3. 15 0
      ch5/Cargo.toml
  4. 37 0
      ch5/src/components.rs
  5. 66 0
      ch5/src/main.rs
  6. 76 0
      ch5/src/map.rs
  7. 36 0
      ch5/src/systems.rs

+ 12 - 0
Cargo.lock

@@ -300,6 +300,18 @@ dependencies = [
  "specs-system-macro 0.1.0 (git+https://git.infinitenegativeutility.com/getty/specs-system-macro.git)",
 ]
 
+[[package]]
+name = "ch5"
+version = "0.1.0"
+dependencies = [
+ "carpet 0.1.0",
+ "ggez 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "specs 0.15.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "specs-derive 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "specs-system-macro 0.1.0 (git+https://git.infinitenegativeutility.com/getty/specs-system-macro.git)",
+]
+
 [[package]]
 name = "clang-sys"
 version = "0.28.1"

+ 1 - 0
Cargo.toml

@@ -6,4 +6,5 @@ members = [
   "ch2",
   "ch3",
   "ch4",
+  "ch5",
 ]

+ 15 - 0
ch5/Cargo.toml

@@ -0,0 +1,15 @@
+[package]
+name = "ch5"
+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
ch5/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();
+    }
+}

+ 66 - 0
ch5/src/main.rs

@@ -0,0 +1,66 @@
+#[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([40, 25].into());
+    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::VirtualKeyCode::*;
+        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::Draw.run_now(&world);
+        systems::Move.run_now(&world);
+    })
+}

+ 76 - 0
ch5/src/map.rs

@@ -0,0 +1,76 @@
+use rand::Rng;
+
+#[derive(PartialEq, Clone, Copy, Debug)]
+pub enum TileType {
+    Wall,
+    Floor,
+}
+
+impl TileType {
+    pub fn glyph(&self) -> carpet::CP437 {
+        match self {
+            TileType::Wall => carpet::CP437::from_char('#'),
+            TileType::Floor => carpet::CP437::from_u8(0),
+        }
+    }
+}
+
+pub struct Map {
+    pub tiles: carpet::Board<TileType>,
+    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, 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;
+        }
+    }
+
+    pub fn passable(&self, (x, y): (usize, usize)) -> bool {
+        Some(&TileType::Floor) == self.tiles.get(x, y)
+    }
+}

+ 36 - 0
ch5/src/systems.rs

@@ -0,0 +1,36 @@
+use crate::components::{Renderable, Motion};
+use crate::map::Map;
+
+system_impl! {
+    Draw(
+        resource mut game_board: carpet::GameBoard<carpet::CP437>,
+        resource map: Map,
+        renderable: Renderable,
+        pos: carpet::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: 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)) {
+            pos.x = tgt_x;
+            pos.y = tgt_y;
+        }
+    } finally {
+        motion.clear();
+    }
+}