Browse Source

Some rudimentary room generation

Getty Ritter 4 years ago
parent
commit
9ac53c9059
3 changed files with 83 additions and 17 deletions
  1. 10 1
      carpet/src/board.rs
  2. 25 4
      carpet/src/types.rs
  3. 48 12
      ch4/src/main.rs

+ 10 - 1
carpet/src/board.rs

@@ -9,6 +9,16 @@ pub struct Board<T> {
     storage: Vec<T>,
 }
 
+impl<T: Clone> Board<T> {
+    pub fn new_with_default(width: usize, height: usize, default: T) -> Board<T> {
+        let mut storage = Vec::with_capacity(width * height);
+        for _ in 0..width*height {
+            storage.push(default.clone())
+        }
+        Board { width, height, storage }
+    }
+}
+
 impl<T> Board<T> {
     pub fn new_from(width: usize, height: usize, mut func: impl FnMut(usize, usize) -> T) -> Board<T> {
         let mut storage = Vec::with_capacity(width * height);
@@ -60,7 +70,6 @@ impl<T> Board<T> {
     pub fn contains_rect(&self, rect: Rect) -> bool {
         let max_x = rect.width() + rect.x() - 1;
         let max_y = rect.height() + rect.y() - 1;
-        println!("checking if ({}, {}) is contained", max_x, max_y);
         self.contains([max_x, max_y])
     }
 

+ 25 - 4
carpet/src/types.rs

@@ -40,18 +40,22 @@ pub struct Rect {
 }
 
 impl Rect {
-    pub fn new(origin: Coord, size: Size) -> Rect{
+    pub fn new(origin: impl Into<Coord>, size: impl Into<Size>) -> Rect{
+        let origin = origin.into();
+        let size = size.into();
         Rect { origin, size }
     }
 
-    pub fn from_points(p1: Coord, p2: Coord) -> Rect {
+    pub fn from_points(p1: impl Into<Coord>, p2: impl Into<Coord>) -> Rect {
+        let p1 = p1.into();
+        let p2 = p2.into();
         let origin = Coord {
             x: min(p1.x, p2.x),
             y: min(p1.y, p2.y),
         };
         let size = Size {
-            width: max(p1.x, p2.x) - origin.x,
-            height: max(p1.y, p2.y) - origin.y,
+            width: max(max(p1.x, p2.x) - origin.x, 1),
+            height: max(max(p1.y, p2.y) - origin.y, 1),
         };
         Rect { origin, size }
     }
@@ -83,4 +87,21 @@ impl Rect {
             pt.x < self.origin.x + self.size.width &&
             pt.y < self.origin.y + self.size.height
     }
+
+    pub fn overlaps(&self, other: Rect) -> bool {
+        if self.x() > other.x() + other.width() || other.x() > self.x() + self.width() {
+            return false;
+        }
+        if self.y() > other.y() + other.height() || other.y() > self.y() + self.height() {
+            return false;
+        }
+        true
+    }
+
+    pub fn center(&self) -> Coord {
+        Coord {
+            x: self.x() + (self.width() / 2),
+            y: self.y() + (self.height() / 2),
+        }
+    }
 }

+ 48 - 12
ch4/src/main.rs

@@ -24,21 +24,55 @@ impl TileType {
 
 pub struct Map {
     tiles: carpet::Board<TileType>,
+    rooms: Vec<carpet::Rect>,
 }
 
 impl Map {
     fn new() -> Map {
         let mut rng = rand::thread_rng();
-        let tiles = carpet::Board::new_from(80, 50, |x, y| {
-            if x == 0 || x == 79 || y == 0 || y == 49 {
-                TileType::Wall
-            } else if rng.gen::<u8>() % 8 == 0 {
-                TileType::Wall
-            } else {
-                TileType::Floor
+        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 { tiles }
+            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 {
@@ -136,13 +170,15 @@ fn main() -> Result<(), GameError> {
     game.register::<Motion>();
     game.register::<Player>();
 
-    game.insert(Map::new());
+    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(Pos { x: 40, y: 25 })
+        .with(Pos { x: player_start.x, y: player_start.y })
         .with(Player)
         .with(Renderable {
-            glyph: carpet::CP437::from_char('A'),
+            glyph: carpet::CP437::from_char('@'),
             color: carpet::Color::Blue,
         })
         .build();