From 8bb6bf35d61ee95847b179a296bc6ed26a1fb888 Mon Sep 17 00:00:00 2001 From: Timothy Warren Date: Wed, 1 Dec 2021 10:47:41 -0500 Subject: [PATCH] Refactor map generation into its own submodule --- src/main.rs | 7 +-- src/map.rs | 86 ++-------------------------------- src/map_builders/common.rs | 31 ++++++++++++ src/map_builders/mod.rs | 14 ++++++ src/map_builders/simple_map.rs | 64 +++++++++++++++++++++++++ 5 files changed, 117 insertions(+), 85 deletions(-) create mode 100644 src/map_builders/common.rs create mode 100644 src/map_builders/mod.rs create mode 100644 src/map_builders/simple_map.rs diff --git a/src/main.rs b/src/main.rs index e45700b..0b646aa 100644 --- a/src/main.rs +++ b/src/main.rs @@ -9,6 +9,7 @@ mod gui; mod hunger_system; mod inventory_system; mod map; +pub mod map_builders; mod map_indexing_system; mod melee_combat_system; mod monster_ai_system; @@ -389,7 +390,7 @@ impl State { let mut worldmap_resource = self.ecs.write_resource::(); current_depth = worldmap_resource.depth; - *worldmap_resource = Map::new_map_rooms_and_corridors(current_depth + 1); + *worldmap_resource = map_builders::build_random_map(current_depth + 1); worldmap = worldmap_resource.clone(); } @@ -443,7 +444,7 @@ impl State { let worldmap; { let mut worldmap_resource = self.ecs.write_resource::(); - *worldmap_resource = Map::new_map_rooms_and_corridors(1); + *worldmap_resource = map_builders::build_random_map(1); worldmap = worldmap_resource.clone(); } @@ -525,7 +526,7 @@ fn main() -> rltk::BError { gs.ecs.insert(SimpleMarkerAllocator::::new()); - let map = Map::new_map_rooms_and_corridors(1); + let map = map_builders::build_random_map(1); let (player_x, player_y) = map.rooms[0].center(); let player_entity = spawner::player(&mut gs.ecs, player_x, player_y); diff --git a/src/map.rs b/src/map.rs index 809abe3..8ddee99 100644 --- a/src/map.rs +++ b/src/map.rs @@ -1,8 +1,7 @@ use crate::Rect; -use rltk::{Algorithm2D, BaseMap, Point, RandomNumberGenerator, Rltk, SmallVec, RGB}; +use rltk::{Algorithm2D, BaseMap, Point, Rltk, SmallVec, RGB}; use serde::{Deserialize, Serialize}; use specs::prelude::*; -use std::cmp::{max, min}; use std::collections::HashSet; pub const MAP_WIDTH: usize = 80; @@ -38,35 +37,6 @@ impl Map { (y as usize * self.width as usize) + x as usize } - fn apply_room_to_map(&mut self, room: &Rect) { - for y in room.y1 + 1..=room.y2 { - for x in room.x1 + 1..=room.x2 { - let idx = self.xy_idx(x, y); - self.tiles[idx] = TileType::Floor; - } - } - } - - fn apply_horizontal_tunnel(&mut self, x1: i32, x2: i32, y: i32) { - for x in min(x1, x2)..=max(x1, x2) { - let idx = self.xy_idx(x, y); - - if idx > 0 && idx < self.width as usize * self.height as usize { - self.tiles[idx as usize] = TileType::Floor; - } - } - } - - fn apply_vertical_tunnel(&mut self, y1: i32, y2: i32, x: i32) { - for y in min(y1, y2)..=max(y1, y2) { - let idx = self.xy_idx(x, y); - - if idx > 0 && idx < self.width as usize * self.height as usize { - self.tiles[idx as usize] = TileType::Floor; - } - } - } - fn is_exit_valid(&self, x: i32, y: i32) -> bool { if x < 1 || x > self.width - 1 || y < 1 || y > self.height - 1 { return false; @@ -89,10 +59,9 @@ impl Map { } } - /// Makes a new map using the algorithm from http://rogueliketutorials.com/tutorials/tcod/part-3/ - /// This gives a handful of random rooms and corridors joining them together - pub fn new_map_rooms_and_corridors(new_depth: i32) -> Map { - let mut map = Map { + /// Generates an empty map, consisting entirely of solid walls + pub fn new(new_depth: i32) -> Map { + Map { tiles: vec![TileType::Wall; MAP_COUNT], rooms: Vec::new(), width: MAP_WIDTH as i32, @@ -103,54 +72,7 @@ impl Map { tile_content: vec![Vec::new(); MAP_COUNT], depth: new_depth, bloodstains: HashSet::new(), - }; - - const MAX_ROOMS: i32 = 30; - const MIN_SIZE: i32 = 6; - const MAX_SIZE: i32 = 10; - - let mut rng = RandomNumberGenerator::new(); - - for _ in 0..MAX_ROOMS { - let w = rng.range(MIN_SIZE, MAX_SIZE); - let h = rng.range(MIN_SIZE, MAX_SIZE); - let x = rng.roll_dice(1, map.width - w - 1) - 1; - let y = rng.roll_dice(1, map.height - h - 1) - 1; - - let new_room = Rect::new(x, y, w, h); - let mut ok = true; - - for other_room in map.rooms.iter() { - if new_room.intersect(other_room) { - ok = false; - } - } - - if ok { - map.apply_room_to_map(&new_room); - - if !map.rooms.is_empty() { - let (new_x, new_y) = new_room.center(); - let (prev_x, prev_y) = map.rooms[map.rooms.len() - 1].center(); - - if rng.range(0, 2) == 1 { - map.apply_horizontal_tunnel(prev_x, new_x, prev_y); - map.apply_vertical_tunnel(prev_y, new_y, new_x); - } else { - map.apply_vertical_tunnel(prev_y, new_y, prev_x); - map.apply_horizontal_tunnel(prev_x, new_x, new_y); - } - } - - map.rooms.push(new_room); - } } - - let stairs_position = map.rooms[map.rooms.len() - 1].center(); - let stairs_idx = map.xy_idx(stairs_position.0, stairs_position.1); - map.tiles[stairs_idx] = TileType::DownStairs; - - map } } diff --git a/src/map_builders/common.rs b/src/map_builders/common.rs new file mode 100644 index 0000000..d68c663 --- /dev/null +++ b/src/map_builders/common.rs @@ -0,0 +1,31 @@ +use crate::{Map, Rect, TileType}; +use std::cmp::{max, min}; + +pub fn apply_room_to_map(map: &mut Map, room: &Rect) { + for y in room.y1 + 1..=room.y2 { + for x in room.x1 + 1..=room.x2 { + let idx = map.xy_idx(x, y); + map.tiles[idx] = TileType::Floor; + } + } +} + +pub fn apply_horizontal_tunnel(map: &mut Map, x1: i32, x2: i32, y: i32) { + for x in min(x1, x2)..=max(x1, x2) { + let idx = map.xy_idx(x, y); + + if idx > 0 && idx < map.width as usize * map.height as usize { + map.tiles[idx as usize] = TileType::Floor; + } + } +} + +pub fn apply_vertical_tunnel(map: &mut Map, y1: i32, y2: i32, x: i32) { + for y in min(y1, y2)..=max(y1, y2) { + let idx = map.xy_idx(x, y); + + if idx > 0 && idx < map.width as usize * map.height as usize { + map.tiles[idx as usize] = TileType::Floor; + } + } +} diff --git a/src/map_builders/mod.rs b/src/map_builders/mod.rs new file mode 100644 index 0000000..9bf7575 --- /dev/null +++ b/src/map_builders/mod.rs @@ -0,0 +1,14 @@ +mod common; +mod simple_map; + +use crate::Map; +use common::*; +use simple_map::SimpleMapBuilder; + +trait MapBuilder { + fn build(new_depth: i32) -> Map; +} + +pub fn build_random_map(new_depth: i32) -> Map { + SimpleMapBuilder::build(new_depth) +} diff --git a/src/map_builders/simple_map.rs b/src/map_builders/simple_map.rs new file mode 100644 index 0000000..c2667df --- /dev/null +++ b/src/map_builders/simple_map.rs @@ -0,0 +1,64 @@ +use super::{apply_horizontal_tunnel, apply_room_to_map, apply_vertical_tunnel, MapBuilder}; +use crate::{Map, Rect, TileType}; +use rltk::RandomNumberGenerator; + +pub struct SimpleMapBuilder {} + +impl MapBuilder for SimpleMapBuilder { + fn build(new_depth: i32) -> Map { + let mut map = Map::new(new_depth); + + SimpleMapBuilder::rooms_and_corridors(&mut map); + + map + } +} + +impl SimpleMapBuilder { + fn rooms_and_corridors(map: &mut Map) { + const MAX_ROOMS: i32 = 30; + const MIN_SIZE: i32 = 6; + const MAX_SIZE: i32 = 10; + + let mut rng = RandomNumberGenerator::new(); + + for _ in 0..MAX_ROOMS { + let w = rng.range(MIN_SIZE, MAX_SIZE); + let h = rng.range(MIN_SIZE, MAX_SIZE); + let x = rng.roll_dice(1, map.width - w - 1) - 1; + let y = rng.roll_dice(1, map.height - h - 1) - 1; + + let new_room = Rect::new(x, y, w, h); + let mut ok = true; + + for other_room in map.rooms.iter() { + if new_room.intersect(other_room) { + ok = false; + } + } + + if ok { + apply_room_to_map(map, &new_room); + + if !map.rooms.is_empty() { + let (new_x, new_y) = new_room.center(); + let (prev_x, prev_y) = map.rooms[map.rooms.len() - 1].center(); + + if rng.range(0, 2) == 1 { + apply_horizontal_tunnel(map, prev_x, new_x, prev_y); + apply_vertical_tunnel(map, prev_y, new_y, new_x); + } else { + apply_vertical_tunnel(map, prev_y, new_y, prev_x); + apply_horizontal_tunnel(map, prev_x, new_x, new_y); + } + } + + map.rooms.push(new_room); + } + } + + let stairs_position = map.rooms[map.rooms.len() - 1].center(); + let stairs_idx = map.xy_idx(stairs_position.0, stairs_position.1); + map.tiles[stairs_idx] = TileType::DownStairs; + } +}