refactoring API
This commit is contained in:
parent
a75f565cbb
commit
278b549094
|
@ -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>) {
|
|
@ -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> {
|
|
@ -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();
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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
130
src/filter/drunkard.rs
Normal 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
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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)
|
||||||
}
|
}
|
|
@ -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;
|
|
@ -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
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user