From eee0c23f5f0a2a572b2dbbc800f9f17afd3d8940 Mon Sep 17 00:00:00 2001 From: Alex Csengery Date: Thu, 18 Jan 2024 22:29:30 -0500 Subject: [PATCH] Part 3: The Dungeon Adding stuff to build the dungeon, rooms, and hallways between them. --- Cargo.lock | 76 +++++++++++++++++++++++++++++++ Cargo.toml | 3 +- src/main.rs | 128 ++++++++++++++++++++++++++++++++++++++++++++++++---- 3 files changed, 198 insertions(+), 9 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b26e0c6..ed6e405 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,6 +17,12 @@ dependencies = [ "libc", ] +[[package]] +name = "fuchsia-cprng" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" + [[package]] name = "lazy_static" version = "0.1.16" @@ -35,10 +41,58 @@ version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69d3587f8a9e599cc7ec2c00e331f71c4e69a5f9a4b8a6efd5b07466b9736f9a" +[[package]] +name = "rand" +version = "0.3.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64ac302d8f83c0c1974bf758f6b041c6c8ada916fbb44a609158ca8b064cc76c" +dependencies = [ + "libc", + "rand 0.4.6", +] + +[[package]] +name = "rand" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293" +dependencies = [ + "fuchsia-cprng", + "libc", + "rand_core 0.3.1", + "rdrand", + "winapi", +] + +[[package]] +name = "rand_core" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" +dependencies = [ + "rand_core 0.4.2", +] + +[[package]] +name = "rand_core" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" + +[[package]] +name = "rdrand" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" +dependencies = [ + "rand_core 0.3.1", +] + [[package]] name = "roguelike" version = "0.1.0" dependencies = [ + "rand 0.3.23", "tcod", ] @@ -62,3 +116,25 @@ dependencies = [ "cc", "pkg-config", ] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/Cargo.toml b/Cargo.toml index 9d52f34..6c44aab 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,4 +6,5 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -tcod = "0.15" \ No newline at end of file +tcod = "0.15" +rand = "0.3.9" diff --git a/src/main.rs b/src/main.rs index 987ab1f..275c4a3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,5 @@ +use rand::Rng; +use std::cmp; use tcod::colors::*; use tcod::console::*; @@ -15,6 +17,11 @@ const COLOR_DARK_GROUND: Color = Color { b: 150, }; +// parameters for dungeon generator +const ROOM_MAX_SIZE: i32 = 10; +const ROOM_MIN_SIZE: i32 = 6; +const MAX_ROOMS: i32 = 30; + // 20 FPS max const LIMIT_FPS: i32 = 20; @@ -141,17 +148,120 @@ struct Game { map: Map, } -fn make_map() -> Map { - // fill map with "unblocked" tiles - let mut map = vec![vec![Tile::empty(); MAP_HEIGHT as usize]; MAP_WIDTH as usize]; +fn make_map(player: &mut Object) -> Map { + // fill map with "blocked" tiles + let mut map = vec![vec![Tile::wall(); MAP_HEIGHT as usize]; MAP_WIDTH as usize]; - // place two pillars to test the map - map[30][22] = Tile::wall(); - map[50][22] = Tile::wall(); + let mut rooms = vec![]; + for _ in 0..MAX_ROOMS { + // random width and height + let w = rand::thread_rng().gen_range(ROOM_MIN_SIZE, ROOM_MAX_SIZE + 1); + let h = rand::thread_rng().gen_range(ROOM_MIN_SIZE, ROOM_MAX_SIZE + 1); + // random position without going out of the boundaries of the map + let x = rand::thread_rng().gen_range(0, MAP_WIDTH - w); + let y = rand::thread_rng().gen_range(0, MAP_HEIGHT - h); + + let new_room = Rect::new(x, y, w, h); + + // run through the other rooms and see if they intersect with this one + let failed = rooms + .iter() + .any(|other_room| new_room.intersects_with(other_room)); + + if !failed { + // room is valid, no intersections + create_room(new_room, &mut map); + + // center coordinates of the new room (useful later) + let (new_x, new_y) = new_room.center(); + + if rooms.is_empty() { + // this is the first room, have the user start here + player.x = new_x; + player.y = new_y; + } else { + // all other rooms after the first - connect them to the prev with a tunnel + + // center coordinates of the previous room + let (prev_x, prev_y) = rooms[rooms.len() - 1].center(); + + // get a random bool value + if rand::random() { + // first move horizontally then vertically + create_h_tunnel(prev_x, new_x, prev_y, &mut map); + create_v_tunnel(prev_y, new_y, new_x, &mut map); + } else { + // vertically then horizontally + create_v_tunnel(prev_y, new_y, prev_x, &mut map); + create_h_tunnel(prev_x, new_x, new_y, &mut map); + } + } + + // append room to the list + rooms.push(new_room); + } + } map } +// A rectangle on the map, used to characterize a room +#[derive(Clone, Copy, Debug)] +struct Rect { + x1: i32, + y1: i32, + x2: i32, + y2: i32, +} + +impl Rect { + pub fn new(x: i32, y: i32, w: i32, h: i32) -> Self { + Rect { + x1: x, + y1: y, + x2: x + w, + y2: y + h, + } + } + + pub fn center(&self) -> (i32, i32) { + let center_x = (self.x1 + self.x2) / 2; + let center_y = (self.y1 + self.y2) / 2; + (center_x, center_y) + } + + pub fn intersects_with(&self, other: &Rect) -> bool { + // returns true if this rectangle intersects with another one + (self.x1 <= other.x2) + && (self.y1 <= other.y2) + && (self.y2 >= other.y1) + && (self.y2 >= other.y1) + } +} + +fn create_room(room: Rect, map: &mut Map) { + // go through the tles in the rectangle and make them passable + for x in (room.x1 + 1)..room.x2 { + for y in (room.y1 + 1)..room.y2 { + map[x as usize][y as usize] = Tile::empty(); + } + } +} + +fn create_h_tunnel(x1: i32, x2: i32, y: i32, map: &mut Map) { + // horizontal tunnel `min()` and `max()` are used in case `x1 > x2` + for x in cmp::min(x1, x2)..(cmp::max(x1, x2) + 1) { + map[x as usize][y as usize] = Tile::empty(); + } +} + +fn create_v_tunnel(y1: i32, y2: i32, x: i32, map: &mut Map) { + // vertical tunnel + for y in cmp::min(y1, y2)..(cmp::max(y1, y2) + 1) { + map[x as usize][y as usize] = Tile::empty(); + } +} + fn main() { let root = Root::initializer() .font("arial10x10.png", FontLayout::Tcod) @@ -169,14 +279,16 @@ fn main() { let mut player_y = SCREEN_HEIGHT / 2; // create object representing the player - let player = Object::new(SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2, '@', WHITE); + let player = Object::new(0, 0, '@', WHITE); // create an NPC let npc = Object::new(SCREEN_WIDTH / 2 - 5, SCREEN_HEIGHT / 2, '@', YELLOW); // the list of objects with the two above let mut objects = [player, npc]; // Generate map - let game = Game { map: make_map() }; + let game = Game { + map: make_map(&mut objects[0]), + }; // Main game loop while !tcod.root.window_closed() {