diff --git a/src/main.rs b/src/main.rs index a1435c9..05bd033 100644 --- a/src/main.rs +++ b/src/main.rs @@ -14,6 +14,7 @@ mod rect; pub use rect::Rect; mod gamelog; mod gui; +pub mod map_builders; mod saveload_system; mod spawner; mod visibility_system; @@ -143,11 +144,14 @@ impl State { // Build new map let worldmap; let current_depth; + let player_start; { let mut worldmap_resource = self.ecs.write_resource::(); current_depth = worldmap_resource.depth; let mut rng = self.ecs.write_resource::(); - *worldmap_resource = Map::new_map_rooms_and_corridors(&mut rng, current_depth + 1); + let (newmap, start) = map_builders::build_random_map(&mut rng, current_depth + 1); + *worldmap_resource = newmap; + player_start = start; worldmap = worldmap_resource.clone(); } @@ -157,15 +161,14 @@ impl State { } // Place the player and update resources - let (player_x, player_y) = worldmap.rooms[0].centre(); let mut player_position = self.ecs.write_resource::(); - *player_position = Point::new(player_x, player_y); + *player_position = Point::new(player_start.x, player_start.y); let mut position_components = self.ecs.write_storage::(); let player_entity = self.ecs.fetch::(); let player_pos_comp = position_components.get_mut(*player_entity); if let Some(player_pos_comp) = player_pos_comp { - player_pos_comp.x = player_x; - player_pos_comp.y = player_y; + player_pos_comp.x = player_start.x; + player_pos_comp.y = player_start.y; } // Dirtify viewshed @@ -196,10 +199,13 @@ impl State { // Build a new map and place the player let worldmap; + let player_start; { let mut worldmap_resource = self.ecs.write_resource::(); let mut rng = self.ecs.write_resource::(); - *worldmap_resource = Map::new_map_rooms_and_corridors(&mut rng, 1); + let (newmap, start) = map_builders::build_random_map(&mut rng, 1); + *worldmap_resource = newmap; + player_start = start; worldmap = worldmap_resource.clone(); } @@ -209,7 +215,7 @@ impl State { } // Place the player and update resources - let (player_x, player_y) = worldmap.rooms[0].centre(); + let (player_x, player_y) = (player_start.x, player_start.y); let player_entity = spawner::player(&mut self.ecs, player_x, player_y, "Player".to_string()); let mut player_position = self.ecs.write_resource::(); *player_position = Point::new(player_x, player_y); @@ -524,23 +530,22 @@ fn main() -> rltk::BError { gs.ecs.register::(); gs.ecs.insert(SimpleMarkerAllocator::::new()); - // Create RNG. + // Create seed. let mut rng = rltk::RandomNumberGenerator::new(); // Use seed to generate the map. - let map = Map::new_map_rooms_and_corridors(&mut rng, 1); + let (map, player_start) = map_builders::build_random_map(&mut rng, 1); // Insert seed into the ECS. gs.ecs.insert(rng); - let (player_x, player_y) = map.rooms[0].centre(); let player_name = "wanderer".to_string(); - let player_entity = spawner::player(&mut gs.ecs, player_x, player_y, player_name); + let player_entity = spawner::player(&mut gs.ecs, player_start.x, player_start.y, player_name); for room in map.rooms.iter().skip(1) { spawner::spawn_room(&mut gs.ecs, room, 1); } gs.ecs.insert(map); - gs.ecs.insert(Point::new(player_x, player_y)); + gs.ecs.insert(Point::new(player_start.x, player_start.y)); gs.ecs.insert(player_entity); gamelog::setup_log(); diff --git a/src/map.rs b/src/map.rs index 038e768..a0aec24 100644 --- a/src/map.rs +++ b/src/map.rs @@ -1,8 +1,7 @@ use super::Rect; -use rltk::{Algorithm2D, BaseMap, Point, RandomNumberGenerator, Rltk, RGB}; +use rltk::{Algorithm2D, BaseMap, Point, Rltk, RGB}; use serde::{Deserialize, Serialize}; use specs::prelude::*; -use std::cmp::{max, min}; use std::collections::HashSet; use std::ops::{Add, Mul}; @@ -15,7 +14,6 @@ pub enum TileType { pub const MAPWIDTH: usize = 80; pub const MAPHEIGHT: usize = 43; -const MAX_OFFSET: u8 = 32; pub const MAPCOUNT: usize = MAPHEIGHT * MAPWIDTH; #[derive(Default, Serialize, Deserialize, Clone)] @@ -45,30 +43,23 @@ 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; - } + pub fn new(new_depth: i32) -> Map { + Map { + tiles: vec![TileType::Wall; MAPCOUNT], + rooms: Vec::new(), + width: MAPWIDTH as i32, + height: MAPHEIGHT as i32, + revealed_tiles: vec![false; MAPCOUNT], + visible_tiles: vec![false; MAPCOUNT], + lit_tiles: vec![true; MAPCOUNT], // NYI: Light sources. Once those exist, we can set this to false. + telepath_tiles: vec![false; MAPCOUNT], + red_offset: vec![0; MAPCOUNT], + green_offset: vec![0; MAPCOUNT], + blue_offset: vec![0; MAPCOUNT], + blocked: vec![false; MAPCOUNT], + depth: new_depth, + bloodstains: HashSet::new(), + tile_content: vec![Vec::new(); MAPCOUNT], } } @@ -92,81 +83,6 @@ impl Map { content.clear(); } } - - /// Makes a procgen map out of rooms and corridors, and returns the rooms and the map. - pub fn new_map_rooms_and_corridors(rng: &mut RandomNumberGenerator, new_depth: i32) -> Map { - let mut map = Map { - tiles: vec![TileType::Wall; MAPCOUNT], - rooms: Vec::new(), - width: MAPWIDTH as i32, - height: MAPHEIGHT as i32, - revealed_tiles: vec![false; MAPCOUNT], - visible_tiles: vec![false; MAPCOUNT], - lit_tiles: vec![true; MAPCOUNT], // NYI: Light sources. Once those exist, we can set this to false. - telepath_tiles: vec![false; MAPCOUNT], - red_offset: vec![0; MAPCOUNT], - green_offset: vec![0; MAPCOUNT], - blue_offset: vec![0; MAPCOUNT], - blocked: vec![false; MAPCOUNT], - depth: new_depth, - bloodstains: HashSet::new(), - tile_content: vec![Vec::new(); MAPCOUNT], - }; - - const MAX_ROOMS: i32 = 30; - const MIN_SIZE: i32 = 6; - const MAX_SIZE: i32 = 10; - - for idx in 0..map.red_offset.len() { - let roll = rng.roll_dice(1, MAX_OFFSET as i32); - map.red_offset[idx] = roll as u8; - } - for idx in 0..map.green_offset.len() { - let roll = rng.roll_dice(1, MAX_OFFSET as i32); - map.green_offset[idx] = roll as u8; - } - for idx in 0..map.blue_offset.len() { - let roll = rng.roll_dice(1, MAX_OFFSET as i32); - map.blue_offset[idx] = roll as u8; - } - - for _i 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.centre(); - let (prev_x, prev_y) = map.rooms[map.rooms.len() - 1].centre(); - 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].centre(); - let stairs_idx = map.xy_idx(stairs_position.0, stairs_position.1); - map.tiles[stairs_idx] = TileType::DownStair; - - map - } } impl Algorithm2D for Map { diff --git a/src/map_builders/common.rs b/src/map_builders/common.rs new file mode 100644 index 0000000..b3d314b --- /dev/null +++ b/src/map_builders/common.rs @@ -0,0 +1,29 @@ +use super::{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..5bf7e33 --- /dev/null +++ b/src/map_builders/mod.rs @@ -0,0 +1,15 @@ +use super::{spawner, Map, Position, Rect, TileType}; +mod simple_map; +use simple_map::SimpleMapBuilder; +mod common; +use common::*; +use rltk::RandomNumberGenerator; +use specs::prelude::*; + +trait MapBuilder { + fn build(rng: &mut RandomNumberGenerator, new_depth: i32) -> (Map, Position); +} + +pub fn build_random_map(rng: &mut RandomNumberGenerator, new_depth: i32) -> (Map, Position) { + SimpleMapBuilder::build(rng, new_depth) +} diff --git a/src/map_builders/simple_map.rs b/src/map_builders/simple_map.rs new file mode 100644 index 0000000..76a9258 --- /dev/null +++ b/src/map_builders/simple_map.rs @@ -0,0 +1,77 @@ +use super::{ + apply_horizontal_tunnel, apply_room_to_map, apply_vertical_tunnel, spawner, Map, MapBuilder, Position, Rect, + TileType, +}; +use rltk::RandomNumberGenerator; +use specs::prelude::*; + +pub struct SimpleMapBuilder {} + +impl MapBuilder for SimpleMapBuilder { + fn build(rng: &mut RandomNumberGenerator, new_depth: i32) -> (Map, Position) { + let mut map = Map::new(new_depth); + let player_pos = SimpleMapBuilder::rooms_and_corridors(rng, &mut map); + + return (map, player_pos); + } +} + +impl SimpleMapBuilder { + fn rooms_and_corridors(rng: &mut RandomNumberGenerator, map: &mut Map) -> Position { + const MAX_ROOMS: i32 = 30; + const MIN_SIZE: i32 = 6; + const MAX_SIZE: i32 = 10; + const MAX_OFFSET: u8 = 32; + + for idx in 0..map.red_offset.len() { + let roll = rng.roll_dice(1, MAX_OFFSET as i32); + map.red_offset[idx] = roll as u8; + } + for idx in 0..map.green_offset.len() { + let roll = rng.roll_dice(1, MAX_OFFSET as i32); + map.green_offset[idx] = roll as u8; + } + for idx in 0..map.blue_offset.len() { + let roll = rng.roll_dice(1, MAX_OFFSET as i32); + map.blue_offset[idx] = roll as u8; + } + + for _i 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.centre(); + let (prev_x, prev_y) = map.rooms[map.rooms.len() - 1].centre(); + 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].centre(); + let stairs_idx = map.xy_idx(stairs_position.0, stairs_position.1); + map.tiles[stairs_idx] = TileType::DownStair; + + let start_pos = map.rooms[0].centre(); + return Position { x: start_pos.0, y: start_pos.1 }; + } +}