refactoring API

This commit is contained in:
klangner 2020-09-22 20:38:37 +02:00
parent a75f565cbb
commit 278b549094
12 changed files with 251 additions and 272 deletions

View File

@ -6,14 +6,14 @@
//! Example generator usage: //! Example generator usage:
//! ``` //! ```
//! use rand::prelude::*; //! use rand::prelude::*;
//! use mapgen::map_builder::{ //! use mapgen::{Map, MapFilter};
//! MapGenerator, //! use mapgen::filter::{
//! bsp_interior::BspInteriorGen //! BspInterior
//! }; //! };
//! //!
//! let mut rng = StdRng::seed_from_u64(100); //! let mut rng = StdRng::seed_from_u64(100);
//! let gen = BspInteriorGen::new(); //! let gen = BspInterior::new();
//! let map = gen.generate_map(80, 50, &mut rng); //! let map = gen.modify_map(&mut rng, &Map::new(80, 50));
//! //!
//! assert_eq!(map.width, 80); //! assert_eq!(map.width, 80);
//! assert_eq!(map.height, 50); //! assert_eq!(map.height, 50);
@ -21,34 +21,34 @@
//! //!
use rand::prelude::*; use rand::prelude::*;
use super::MapGenerator; use super::MapFilter;
use crate::geometry::{Point, Rect}; use crate::geometry::{Point, Rect};
use crate::random::Rng; use crate::random::Rng;
use crate::map::Map; use crate::map::Map;
pub struct BspInteriorGen { pub struct BspInterior {
min_room_size: usize, min_room_size: usize,
} }
impl MapGenerator for BspInteriorGen { impl MapFilter for BspInterior {
fn generate_map(&self, width: usize, height: usize, rng : &mut StdRng) -> Map { fn modify_map(&self, rng: &mut StdRng, map: &Map) -> Map {
self.build(rng, width, height) self.build(rng, map)
} }
} }
impl BspInteriorGen { impl BspInterior {
pub fn new() -> Box<BspInteriorGen> { pub fn new() -> Box<BspInterior> {
Box::new(BspInteriorGen{ Box::new(BspInterior{
min_room_size: 8, min_room_size: 8,
}) })
} }
fn build(&self, rng: &mut StdRng, width: usize, height: usize) -> Map { fn build(&self, rng: &mut StdRng, map: &Map) -> Map {
let mut map = Map::new(width, height); let mut new_map = map.clone();
let mut rects: Vec<Rect> = Vec::new(); let mut rects: Vec<Rect> = Vec::new();
rects.push( Rect::new(1, 1, map.width-2, map.height-2) ); rects.push( Rect::new(1, 1, new_map.width-2, new_map.height-2) );
let first_room = rects[0]; let first_room = rects[0];
// Divide the first room // Divide the first room
self.add_subrects(first_room, rng, &mut rects); self.add_subrects(first_room, rng, &mut rects);
@ -56,21 +56,21 @@ impl BspInteriorGen {
let rooms_copy = rects.clone(); let rooms_copy = rects.clone();
for r in rooms_copy.iter() { for r in rooms_copy.iter() {
let room = *r; let room = *r;
map.add_room(room); new_map.add_room(room);
} }
// Now we want corridors // Now we want corridors
for i in 0..map.rooms.len()-1 { for i in 0..new_map.rooms.len()-1 {
let room = map.rooms[i]; let room = new_map.rooms[i];
let next_room = map.rooms[i+1]; let next_room = new_map.rooms[i+1];
let start_x = room.x1 + rng.random_range(1, room.width()); let start_x = room.x1 + rng.random_range(1, room.width());
let start_y = room.y1 + rng.random_range(1, room.height()); let start_y = room.y1 + rng.random_range(1, room.height());
let end_x = next_room.x1 + (rng.random_range(1, next_room.width())); let end_x = next_room.x1 + (rng.random_range(1, next_room.width()));
let end_y = next_room.y1 + (rng.random_range(1, next_room.width())); let end_y = next_room.y1 + (rng.random_range(1, next_room.width()));
map.add_corridor(Point::new(start_x, start_y), Point::new(end_x, end_y)); new_map.add_corridor(Point::new(start_x, start_y), Point::new(end_x, end_y));
} }
map new_map
} }
fn add_subrects(&self, rect: Rect, rng: &mut StdRng, rects: &mut Vec<Rect>) { fn add_subrects(&self, rect: Rect, rng: &mut StdRng, rects: &mut Vec<Rect>) {

View File

@ -6,14 +6,12 @@
//! Example generator usage: //! Example generator usage:
//! ``` //! ```
//! use rand::prelude::*; //! use rand::prelude::*;
//! use mapgen::map_builder::{ //! use mapgen::{Map, MapFilter};
//! MapGenerator, //! use mapgen::filter::BspRooms;
//! bsp_rooms::BspRoomsGen
//! };
//! //!
//! let mut rng = StdRng::seed_from_u64(100); //! let mut rng = StdRng::seed_from_u64(100);
//! let gen = BspRoomsGen::new(); //! let gen = BspRooms::new();
//! let map = gen.generate_map(80, 50, &mut rng); //! let map = gen.modify_map(&mut rng, &Map::new(80, 50));
//! //!
//! assert_eq!(map.width, 80); //! assert_eq!(map.width, 80);
//! assert_eq!(map.height, 50); //! assert_eq!(map.height, 50);
@ -21,34 +19,34 @@
//! //!
use rand::prelude::*; use rand::prelude::*;
use super::MapGenerator; use super::MapFilter;
use crate::geometry::Rect; use crate::geometry::Rect;
use crate::random::Rng; use crate::random::Rng;
use crate::map::{Map, TileType}; use crate::map::{Map, TileType};
pub struct BspRoomsGen { pub struct BspRooms {
max_split: usize, max_split: usize,
} }
impl MapGenerator for BspRoomsGen { impl MapFilter for BspRooms {
fn generate_map(&self, width: usize, height: usize, rng : &mut StdRng) -> Map { fn modify_map(&self, rng: &mut StdRng, map: &Map) -> Map {
self.build_rooms(width, height, rng) self.build_rooms(map, rng)
} }
} }
impl BspRoomsGen { impl BspRooms {
pub fn new() -> Box<BspRoomsGen> { pub fn new() -> Box<BspRooms> {
Box::new(BspRoomsGen { Box::new(BspRooms {
max_split: 240, max_split: 240,
}) })
} }
fn build_rooms(&self, width: usize, height: usize, rng : &mut StdRng) -> Map { fn build_rooms(&self, map: &Map, rng : &mut StdRng) -> Map {
let mut map = Map::new(width, height); let mut new_map = map.clone();
let mut rects: Vec<Rect> = Vec::new(); let mut rects: Vec<Rect> = Vec::new();
// Start with a single map-sized rectangle // Start with a single map-sized rectangle
rects.push( Rect::new(2, 2, width-5, height-5) ); rects.push( Rect::new(2, 2, new_map.width-5, new_map.height-5) );
let first_room = rects[0]; let first_room = rects[0];
rects.append(&mut self.split_into_subrects(first_room)); // Divide the first room rects.append(&mut self.split_into_subrects(first_room)); // Divide the first room
@ -59,14 +57,14 @@ impl BspRoomsGen {
let rect = self.get_random_rect(rng, &rects); let rect = self.get_random_rect(rng, &rects);
let candidate = self.get_random_sub_rect(rect, rng); let candidate = self.get_random_sub_rect(rect, rng);
if self.is_possible(candidate, &map) { if self.is_possible(candidate, &new_map) {
map.add_room(candidate); new_map.add_room(candidate);
rects.append(&mut self.split_into_subrects(rect)); rects.append(&mut self.split_into_subrects(rect));
} }
n_rooms += 1; n_rooms += 1;
} }
map new_map
} }
fn split_into_subrects(&self, rect: Rect) -> Vec<Rect> { fn split_into_subrects(&self, rect: Rect) -> Vec<Rect> {

View File

@ -9,14 +9,12 @@
//! Example generator usage: //! Example generator usage:
//! ``` //! ```
//! use rand::prelude::*; //! use rand::prelude::*;
//! use mapgen::map_builder::{ //! use mapgen::{Map, MapFilter};
//! MapGenerator, //! use mapgen::filter::CellularAutomata;
//! cellular_automata::CellularAutomataGen
//! };
//! //!
//! let mut rng = StdRng::seed_from_u64(100); //! let mut rng = StdRng::seed_from_u64(100);
//! let gen = CellularAutomataGen::new(); //! let gen = CellularAutomata::new();
//! let map = gen.generate_map(80, 50, &mut rng); //! let map = gen.modify_map(&mut rng, &Map::new(80, 50));
//! //!
//! assert_eq!(map.width, 80); //! assert_eq!(map.width, 80);
//! assert_eq!(map.height, 50); //! assert_eq!(map.height, 50);
@ -24,54 +22,47 @@
//! //!
use rand::prelude::*; use rand::prelude::*;
use super::{MapGenerator, MapModifier}; use super::MapFilter;
use crate::map::{Map, TileType}; use crate::map::{Map, TileType};
/// Map generator and modifier /// Map generator and modifier
pub struct CellularAutomataGen {} pub struct CellularAutomata {}
impl MapGenerator for CellularAutomataGen { impl MapFilter for CellularAutomata {
fn generate_map(&self, width: usize, height: usize, rng : &mut StdRng) -> Map { fn modify_map(&self, rng: &mut StdRng, map: &Map) -> Map {
self.build(width, height, rng) self.build(map, rng)
} }
} }
impl CellularAutomataGen { impl CellularAutomata {
/// Create generator which will create map with the given dimension. /// Create generator which will create map with the given dimension.
pub fn new() -> Box<CellularAutomataGen> { pub fn new() -> Box<CellularAutomata> {
Box::new(CellularAutomataGen {}) Box::new(CellularAutomata {})
} }
/// Generate map /// Generate map
fn build(&self, width: usize, height: usize, rng: &mut StdRng) -> Map { fn build(&self, map: &Map, rng: &mut StdRng) -> Map {
let mut map = Map::new(width, height); let mut new_map = map.clone();
// First we completely randomize the map, setting 55% of it to be floor. // First we completely randomize the map, setting 55% of it to be floor.
for y in 1..height-1 { for y in 1..new_map.height-1 {
for x in 1..width-1 { for x in 1..new_map.width-1 {
let roll = rng.next_u32() % 100; let roll = rng.next_u32() % 100;
if roll > 55 { map.set_tile(x, y, TileType::Floor) } if roll > 55 { new_map.set_tile(x, y, TileType::Floor) }
else { map.set_tile(x, y, TileType::Wall) } else { new_map.set_tile(x, y, TileType::Wall) }
} }
} }
// Now we iteratively apply cellular automata rules // Now we iteratively apply cellular automata rules
for _ in 0..15 { for _ in 0..15 {
map = apply_iteration(&map); new_map = apply_iteration(&new_map);
} }
map new_map
} }
} }
impl MapModifier for CellularAutomataGen {
fn modify_map(&self, _rng: &mut StdRng, map : &Map) -> Map {
apply_iteration(map)
}
}
fn apply_iteration(map: &Map) -> Map { fn apply_iteration(map: &Map) -> Map {
let mut new_map = map.clone(); let mut new_map = map.clone();

View File

@ -5,7 +5,7 @@
//! //!
use rand::prelude::StdRng; use rand::prelude::StdRng;
use super::MapModifier; use super::MapFilter;
use crate::map::{Map, TileType}; use crate::map::{Map, TileType};
use crate::dijkstra::DijkstraMap; use crate::dijkstra::DijkstraMap;
@ -13,7 +13,7 @@ use crate::dijkstra::DijkstraMap;
/// Remove unreachable areas from the map. /// Remove unreachable areas from the map.
pub struct CullUnreachable {} pub struct CullUnreachable {}
impl MapModifier for CullUnreachable { impl MapFilter for CullUnreachable {
fn modify_map(&self, _: &mut StdRng, map: &Map) -> Map { fn modify_map(&self, _: &mut StdRng, map: &Map) -> Map {
self.build(map) self.build(map)
} }
@ -49,7 +49,7 @@ impl CullUnreachable {
mod tests { mod tests {
use rand::prelude::*; use rand::prelude::*;
use super::*; use super::*;
use super::MapModifier; use super::MapFilter;
use crate::geometry::Point; use crate::geometry::Point;
use crate::map::Map; use crate::map::Map;

View File

@ -7,7 +7,7 @@
use std::f32; use std::f32;
use rand::prelude::StdRng; use rand::prelude::StdRng;
use crate::geometry::Point; use crate::geometry::Point;
use super::MapModifier; use super::MapFilter;
use crate::map::Map; use crate::map::Map;
use crate::dijkstra::DijkstraMap; use crate::dijkstra::DijkstraMap;
@ -15,7 +15,7 @@ use crate::dijkstra::DijkstraMap;
/// Add exist position to the map based on the distance from the start point. /// Add exist position to the map based on the distance from the start point.
pub struct DistantExit {} pub struct DistantExit {}
impl MapModifier for DistantExit { impl MapFilter for DistantExit {
fn modify_map(&self, _: &mut StdRng, map: &Map) -> Map { fn modify_map(&self, _: &mut StdRng, map: &Map) -> Map {
self.build(map) self.build(map)
} }
@ -53,7 +53,7 @@ impl DistantExit {
mod tests { mod tests {
use rand::prelude::*; use rand::prelude::*;
use super::*; use super::*;
use super::MapModifier; use super::MapFilter;
use crate::geometry::Point; use crate::geometry::Point;
use crate::map::Map; use crate::map::Map;

130
src/filter/drunkard.rs Normal file
View File

@ -0,0 +1,130 @@
//! Example generator usage:
//! ```
//! use rand::prelude::*;
//! use mapgen::{Map, MapFilter};
//! use mapgen::filter::DrunkardsWalk;
//!
//! let mut rng = StdRng::seed_from_u64(100);
//! let gen = DrunkardsWalk::open_area();
//! let map = gen.modify_map(&mut rng, &Map::new(80, 50));
//!
//! assert_eq!(map.width, 80);
//! assert_eq!(map.height, 50);
//! ```
//!
use rand::prelude::*;
use super::MapFilter;
use crate::{
map::{Map, Symmetry, TileType},
geometry::Point,
random::Rng
};
#[derive(PartialEq, Copy, Clone)]
pub enum DrunkSpawnMode { StartingPoint, Random }
pub struct DrunkardsWalk {
spawn_mode : DrunkSpawnMode,
drunken_lifetime : i32,
floor_percent: f32,
brush_size: usize,
symmetry: Symmetry
}
impl MapFilter for DrunkardsWalk {
fn modify_map(&self, rng: &mut StdRng, map: &Map) -> Map {
self.build(rng, map)
}
}
impl DrunkardsWalk {
pub fn new( spawn_mode: DrunkSpawnMode,
drunken_lifetime: i32,
floor_percent: f32,
brush_size: usize,
symmetry: Symmetry) -> Box<DrunkardsWalk>
{
Box::new(DrunkardsWalk{
spawn_mode,
drunken_lifetime,
floor_percent,
brush_size,
symmetry
})
}
pub fn open_area() -> Box<DrunkardsWalk> {
Self::new(DrunkSpawnMode::StartingPoint, 400, 0.5, 1, Symmetry::None)
}
pub fn open_halls() -> Box<DrunkardsWalk> {
Self::new(DrunkSpawnMode::Random, 400, 0.5, 1, Symmetry::None)
}
pub fn winding_passages() -> Box<DrunkardsWalk> {
Self::new(DrunkSpawnMode::Random, 400, 0.4, 1, Symmetry::None)
}
pub fn fat_passages() -> Box<DrunkardsWalk> {
Self::new(DrunkSpawnMode::Random, 400, 0.4, 2, Symmetry::None)
}
pub fn fearful_symmetry() -> Box<DrunkardsWalk> {
Self::new(DrunkSpawnMode::Random, 400, 0.4, 1, Symmetry::Both)
}
fn build(&self, rng: &mut StdRng, map: &Map) -> Map {
let mut new_map = map.clone();
// Set a central starting point
let starting_position = Point::new( new_map.width / 2, new_map.height / 2 );
new_map.set_tile(starting_position.x, starting_position.y, TileType::Floor);
let total_tiles = new_map.width * new_map.height;
let desired_floor_tiles = (self.floor_percent * total_tiles as f32) as usize;
let mut floor_tile_count = new_map.tiles.iter().filter(|a| **a == TileType::Floor).count();
let mut digger_count = 0;
while floor_tile_count < desired_floor_tiles {
let mut drunk_x;
let mut drunk_y;
match self.spawn_mode {
DrunkSpawnMode::StartingPoint => {
drunk_x = starting_position.x;
drunk_y = starting_position.y;
}
DrunkSpawnMode::Random => {
if digger_count == 0 {
drunk_x = starting_position.x;
drunk_y = starting_position.y;
} else {
drunk_x = rng.roll_dice(1, new_map.width - 3) + 1;
drunk_y = rng.roll_dice(1, new_map.height - 3) + 1;
}
}
}
let mut drunk_life = self.drunken_lifetime;
while drunk_life > 0 {
new_map.set_tile(drunk_x, drunk_y, TileType::Wall);
new_map.paint(self.symmetry, self.brush_size, drunk_x, drunk_y);
// map.exit_point = Some(Point::new(drunk_x, drunk_y));
let stagger_direction = rng.roll_dice(1, 4);
match stagger_direction {
1 => { if drunk_x > 2 { drunk_x -= 1; } }
2 => { if drunk_x < new_map.width-2 { drunk_x += 1; } }
3 => { if drunk_y > 2 { drunk_y -=1; } }
_ => { if drunk_y < new_map.height-2 { drunk_y += 1; } }
}
drunk_life -= 1;
}
digger_count += 1;
floor_tile_count = new_map.tiles.iter().filter(|a| **a == TileType::Floor).count();
}
new_map
}
}

View File

@ -8,17 +8,15 @@
//! //!
//! Example //! Example
//! ``` //! ```
//! use mapgen::{ //! use mapgen::{MapFilter, MapBuilder, Map, TileType};
//! map_builder::{ //! use mapgen::filter::{
//! MapModifier, MapBuilder, //! CellularAutomata,
//! cellular_automata::CellularAutomataGen,
//! starting_point::{AreaStartingPosition, XStart, YStart} //! starting_point::{AreaStartingPosition, XStart, YStart}
//! },
//! map::{Map, TileType},
//! geometry::Point,
//! }; //! };
//! use mapgen::geometry::Point;
//! //!
//! let map = MapBuilder::new(CellularAutomataGen::new()) //! let map = MapBuilder::new()
//! .with(CellularAutomata::new())
//! .with(AreaStartingPosition::new(XStart::CENTER, YStart::CENTER)) //! .with(AreaStartingPosition::new(XStart::CENTER, YStart::CENTER))
//! .build_map(80, 50); //! .build_map(80, 50);
//! //!
@ -38,39 +36,41 @@ pub mod simple_rooms;
pub mod rooms_corridors_nearest; pub mod rooms_corridors_nearest;
pub mod starting_point; pub mod starting_point;
pub use bsp_interior::BspInterior;
pub use bsp_rooms::BspRooms;
pub use cellular_automata::CellularAutomata;
pub use cull_unreachable::CullUnreachable;
pub use distant_exit::DistantExit;
pub use drunkard::DrunkardsWalk;
pub use simple_rooms::SimpleRooms;
pub use rooms_corridors_nearest::NearestCorridors;
pub use starting_point::AreaStartingPosition;
use std::time::{SystemTime, UNIX_EPOCH}; use std::time::{SystemTime, UNIX_EPOCH};
use rand::prelude::*; use rand::prelude::*;
use crate::map::Map; use crate::map::Map;
/// Trait which should be implemented by any map generator which want to be used
/// by MapBuilder
pub trait MapGenerator {
fn generate_map(&self, width: usize, height: usize, rng: &mut StdRng) -> Map;
}
/// Trait which should be implemented by map modifier. /// Trait which should be implemented by map modifier.
/// Modifier takes initiall map and apply changes to it. /// Modifier takes initiall map and apply changes to it.
pub trait MapModifier { pub trait MapFilter {
fn modify_map(&self, rng: &mut StdRng, map: &Map) -> Map; fn modify_map(&self, rng: &mut StdRng, map: &Map) -> Map;
} }
/// Used to chain MapBuilder and MapModifiers to create the final map. /// Used to chain MapBuilder and MapModifiers to create the final map.
pub struct MapBuilder { pub struct MapBuilder {
generator: Box<dyn MapGenerator>, modifiers: Vec<Box<dyn MapFilter>>,
modifiers: Vec<Box<dyn MapModifier>>,
} }
impl MapBuilder { impl MapBuilder {
/// Create Map Builder with initial map generator /// Create Map Builder with initial map generator
pub fn new(generator : Box<dyn MapGenerator>) -> MapBuilder { pub fn new() -> MapBuilder {
MapBuilder { MapBuilder {
generator,
modifiers: Vec::new(), modifiers: Vec::new(),
} }
} }
pub fn with(&mut self, modifier : Box<dyn MapModifier>) -> &mut MapBuilder { pub fn with(&mut self, modifier : Box<dyn MapFilter>) -> &mut MapBuilder {
self.modifiers.push(modifier); self.modifiers.push(modifier);
self self
} }
@ -84,7 +84,7 @@ impl MapBuilder {
/// Build map using provided random number generator /// Build map using provided random number generator
pub fn build_map_with_rng(&mut self, width: usize, height: usize, rng: &mut StdRng) -> Map { pub fn build_map_with_rng(&mut self, width: usize, height: usize, rng: &mut StdRng) -> Map {
let mut map = self.generator.generate_map(width, height, rng); let mut map = Map::new(width, height);
// Build additional layers in turn // Build additional layers in turn
for modifier in self.modifiers.iter() { for modifier in self.modifiers.iter() {
@ -102,12 +102,13 @@ impl MapBuilder {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use cellular_automata::CellularAutomataGen; use cellular_automata::CellularAutomata;
use starting_point::{AreaStartingPosition, XStart, YStart}; use starting_point::{AreaStartingPosition, XStart, YStart};
#[test] #[test]
fn test_ca_map() { fn test_ca_map() {
let map = MapBuilder::new(CellularAutomataGen::new()) let map = MapBuilder::new()
.with(CellularAutomata::new())
.with(AreaStartingPosition::new(XStart::CENTER, YStart::CENTER)) .with(AreaStartingPosition::new(XStart::CENTER, YStart::CENTER))
.build_map(80, 50); .build_map(80, 50);

View File

@ -1,14 +1,14 @@
//! Connect nearest rooms on the map with corridors //! Connect nearest rooms on the map with corridors
//! //!
use rand::prelude::StdRng; use rand::prelude::StdRng;
use super::MapModifier; use super::MapFilter;
use crate::map::Map; use crate::map::Map;
use std::collections::HashSet; use std::collections::HashSet;
pub struct NearestCorridors {} pub struct NearestCorridors {}
impl MapModifier for NearestCorridors { impl MapFilter for NearestCorridors {
fn modify_map(&self, _: &mut StdRng, map: &Map) -> Map { fn modify_map(&self, _: &mut StdRng, map: &Map) -> Map {
self.corridors(map) self.corridors(map)
} }

View File

@ -6,14 +6,12 @@
//! Example generator usage: //! Example generator usage:
//! ``` //! ```
//! use rand::prelude::*; //! use rand::prelude::*;
//! use mapgen::map_builder::{ //! use mapgen::{MapFilter, Map};
//! MapGenerator, //! use mapgen::filter::SimpleRooms;
//! simple_rooms::SimpleRoomsGen
//! };
//! //!
//! let mut rng = StdRng::seed_from_u64(100); //! let mut rng = StdRng::seed_from_u64(100);
//! let gen = SimpleRoomsGen::new(); //! let gen = SimpleRooms::new();
//! let map = gen.generate_map(80, 50, &mut rng); //! let map = gen.modify_map(&mut rng, &Map::new(80, 50));
//! //!
//! assert_eq!(map.width, 80); //! assert_eq!(map.width, 80);
//! assert_eq!(map.height, 50); //! assert_eq!(map.height, 50);
@ -21,50 +19,50 @@
//! //!
use rand::prelude::*; use rand::prelude::*;
use super::MapGenerator; use super::MapFilter;
use crate::geometry::Rect; use crate::geometry::Rect;
use crate::random::Rng; use crate::random::Rng;
use crate::map::{Map}; use crate::map::{Map};
pub struct SimpleRoomsGen { pub struct SimpleRooms {
max_rooms: usize, max_rooms: usize,
min_room_size: usize, min_room_size: usize,
max_room_size: usize, max_room_size: usize,
} }
impl MapGenerator for SimpleRoomsGen { impl MapFilter for SimpleRooms {
fn generate_map(&self, width: usize, height: usize, rng : &mut StdRng) -> Map { fn modify_map(&self, rng: &mut StdRng, map: &Map) -> Map {
self.build_rooms(width, height, rng) self.build_rooms(map, rng)
} }
} }
impl SimpleRoomsGen { impl SimpleRooms {
pub fn new() -> Box<SimpleRoomsGen> { pub fn new() -> Box<SimpleRooms> {
Box::new(SimpleRoomsGen{ Box::new(SimpleRooms{
max_rooms: 30, max_rooms: 30,
min_room_size: 6, min_room_size: 6,
max_room_size: 10 max_room_size: 10
}) })
} }
fn build_rooms(&self, width: usize, height: usize, rng : &mut StdRng) -> Map { fn build_rooms(&self, map: &Map, rng : &mut StdRng) -> Map {
let mut map = Map::new(width, height); let mut new_map = map.clone();
// Create room dimensions // Create room dimensions
for _ in 0..self.max_rooms { for _ in 0..self.max_rooms {
let w = rng.random_range(self.min_room_size, self.max_room_size); let w = rng.random_range(self.min_room_size, self.max_room_size);
let h = rng.random_range(self.min_room_size, self.max_room_size); let h = rng.random_range(self.min_room_size, self.max_room_size);
let x = rng.random_range(1, width - w); let x = rng.random_range(1, new_map.width - w);
let y = rng.random_range(1, height - h); let y = rng.random_range(1, new_map.height - h);
let new_room = Rect::new(x, y, w, h); let new_room = Rect::new(x, y, w, h);
let intersects = map.rooms.iter().any(|r| new_room.intersect(r)); let intersects = new_map.rooms.iter().any(|r| new_room.intersect(r));
if !intersects { if !intersects {
map.add_room(new_room); new_map.add_room(new_room);
} }
} }
map new_map
} }
} }

View File

@ -6,14 +6,9 @@
//! Example modifier usage: //! Example modifier usage:
//! ``` //! ```
//! use rand::prelude::*; //! use rand::prelude::*;
//! use mapgen::{ //! use mapgen::{MapFilter, Map, TileType};
//! map_builder::{ //! use mapgen::filter::starting_point::{AreaStartingPosition, XStart, YStart};
//! MapModifier, //! use mapgen::geometry::Point;
//! starting_point::{AreaStartingPosition, XStart, YStart},
//! },
//! map::{Map, TileType},
//! geometry::Point,
//! };
//! //!
//! let mut rng = StdRng::seed_from_u64(100); //! let mut rng = StdRng::seed_from_u64(100);
//! let mut map = Map::new(80, 50); //! let mut map = Map::new(80, 50);
@ -26,7 +21,7 @@
//! //!
use rand::prelude::StdRng; use rand::prelude::StdRng;
use super::{MapModifier}; use super::MapFilter;
use crate::geometry::Point; use crate::geometry::Point;
use crate::map::{Map, TileType}; use crate::map::{Map, TileType};
@ -43,7 +38,7 @@ pub struct AreaStartingPosition {
y : YStart y : YStart
} }
impl MapModifier for AreaStartingPosition { impl MapFilter for AreaStartingPosition {
fn modify_map(&self, _: &mut StdRng, map: &Map) -> Map { fn modify_map(&self, _: &mut StdRng, map: &Map) -> Map {
self.build(map) self.build(map)
} }

View File

@ -4,9 +4,12 @@
//! * Dungeon maps //! * Dungeon maps
//! //!
pub mod map_builder; pub mod filter;
pub mod geometry; pub mod geometry;
pub mod map; pub mod map;
pub use map::{Map, TileType};
pub use filter::{MapFilter, MapBuilder};
pub (crate) mod dijkstra; pub (crate) mod dijkstra;
pub (crate) mod random; pub (crate) mod random;

View File

@ -1,137 +0,0 @@
//! Example generator usage:
//! ```
//! use rand::prelude::*;
//! use mapgen::map_builder::{
//! MapGenerator,
//! drunkard::DrunkardsWalkGen
//! };
//!
//! let mut rng = StdRng::seed_from_u64(100);
//! let gen = DrunkardsWalkGen::open_area();
//! let map = gen.generate_map(80, 50, &mut rng);
//!
//! assert_eq!(map.width, 80);
//! assert_eq!(map.height, 50);
//! ```
//!
use rand::prelude::*;
use super::MapGenerator;
use crate::{
map::{Map, Symmetry, TileType},
geometry::Point,
random::Rng
};
#[derive(PartialEq, Copy, Clone)]
pub enum DrunkSpawnMode { StartingPoint, Random }
pub struct DrunkardsWalkGen {
spawn_mode : DrunkSpawnMode,
drunken_lifetime : i32,
floor_percent: f32,
brush_size: usize,
symmetry: Symmetry
}
impl MapGenerator for DrunkardsWalkGen {
fn generate_map(&self, width: usize, height: usize, rng: &mut StdRng) -> Map {
self.build(rng, width, height)
}
}
impl DrunkardsWalkGen {
pub fn new( spawn_mode: DrunkSpawnMode,
drunken_lifetime: i32,
floor_percent: f32,
brush_size: usize,
symmetry: Symmetry) -> Box<DrunkardsWalkGen>
{
Box::new(DrunkardsWalkGen{
spawn_mode,
drunken_lifetime,
floor_percent,
brush_size,
symmetry
})
}
pub fn open_area() -> Box<DrunkardsWalkGen> {
DrunkardsWalkGen::new(DrunkSpawnMode::StartingPoint, 400, 0.5, 1, Symmetry::None)
}
pub fn open_halls() -> Box<DrunkardsWalkGen> {
DrunkardsWalkGen::new(DrunkSpawnMode::Random, 400, 0.5, 1, Symmetry::None)
}
pub fn winding_passages() -> Box<DrunkardsWalkGen> {
DrunkardsWalkGen::new(DrunkSpawnMode::Random, 400, 0.4, 1, Symmetry::None)
}
pub fn fat_passages() -> Box<DrunkardsWalkGen> {
DrunkardsWalkGen::new(DrunkSpawnMode::Random, 400, 0.4, 2, Symmetry::None)
}
pub fn fearful_symmetry() -> Box<DrunkardsWalkGen> {
DrunkardsWalkGen::new(DrunkSpawnMode::Random, 400, 0.4, 1, Symmetry::Both)
}
fn build(&self, rng: &mut StdRng, width: usize, height: usize) -> Map {
let mut map = Map::new(width, height);
// Set a central starting point
let starting_position = Point::new( map.width / 2, map.height / 2 );
map.set_tile(starting_position.x, starting_position.y, TileType::Floor);
let total_tiles = map.width * map.height;
let desired_floor_tiles = (self.floor_percent * total_tiles as f32) as usize;
let mut floor_tile_count = map.tiles.iter().filter(|a| **a == TileType::Floor).count();
let mut digger_count = 0;
while floor_tile_count < desired_floor_tiles {
let mut drunk_x;
let mut drunk_y;
match self.spawn_mode {
DrunkSpawnMode::StartingPoint => {
drunk_x = starting_position.x;
drunk_y = starting_position.y;
}
DrunkSpawnMode::Random => {
if digger_count == 0 {
drunk_x = starting_position.x;
drunk_y = starting_position.y;
} else {
drunk_x = rng.roll_dice(1, map.width - 3) + 1;
drunk_y = rng.roll_dice(1, map.height - 3) + 1;
}
}
}
let mut drunk_life = self.drunken_lifetime;
while drunk_life > 0 {
map.set_tile(drunk_x, drunk_y, TileType::Wall);
map.paint(self.symmetry, self.brush_size, drunk_x, drunk_y);
// map.exit_point = Some(Point::new(drunk_x, drunk_y));
let stagger_direction = rng.roll_dice(1, 4);
match stagger_direction {
1 => { if drunk_x > 2 { drunk_x -= 1; } }
2 => { if drunk_x < map.width-2 { drunk_x += 1; } }
3 => { if drunk_y > 2 { drunk_y -=1; } }
_ => { if drunk_y < map.height-2 { drunk_y += 1; } }
}
drunk_life -= 1;
}
digger_count += 1;
// for t in map.tiles.iter_mut() {
// if *t == TileType::DownStairs {
// *t = TileType::Floor;
// }
// }
floor_tile_count = map.tiles.iter().filter(|a| **a == TileType::Floor).count();
}
map
}
}