cargo fmt
This commit is contained in:
parent
efad0132ff
commit
ffaad6cf48
|
@ -1,10 +1,9 @@
|
||||||
use rand::prelude::*;
|
|
||||||
use mapgen::*;
|
use mapgen::*;
|
||||||
|
use rand::prelude::*;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let mut rng = StdRng::seed_from_u64(907647352);
|
let mut rng = StdRng::seed_from_u64(907647352);
|
||||||
let gen = BspInterior::<NoData>::new();
|
let gen = BspInterior::<NoData>::new();
|
||||||
let map = gen.modify_map(&mut rng, &Map::new(20, 10));
|
let map = gen.modify_map(&mut rng, &Map::new(20, 10));
|
||||||
println!("{:}", &map);
|
println!("{:}", &map);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
use std::time::{SystemTime, UNIX_EPOCH};
|
|
||||||
use rand::prelude::*;
|
|
||||||
use mapgen::*;
|
use mapgen::*;
|
||||||
|
use rand::prelude::*;
|
||||||
|
use std::time::{SystemTime, UNIX_EPOCH};
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let system_time = SystemTime::now().duration_since(UNIX_EPOCH).expect("Can't access system time");
|
let system_time = SystemTime::now()
|
||||||
|
.duration_since(UNIX_EPOCH)
|
||||||
|
.expect("Can't access system time");
|
||||||
let mut rng = StdRng::seed_from_u64(system_time.as_millis() as u64);
|
let mut rng = StdRng::seed_from_u64(system_time.as_millis() as u64);
|
||||||
let gen = BspRooms::<NoData>::new();
|
let gen = BspRooms::<NoData>::new();
|
||||||
let map = gen.modify_map(&mut rng, &Map::new(80, 50));
|
let map = gen.modify_map(&mut rng, &Map::new(80, 50));
|
||||||
println!("{:}", &map);
|
println!("{:}", &map);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,24 +1,17 @@
|
||||||
use mapgen::{
|
use mapgen::{
|
||||||
MapBuilder,
|
|
||||||
NoData,
|
|
||||||
filter::{
|
filter::{
|
||||||
NoiseGenerator,
|
AreaStartingPosition, CellularAutomata, CullUnreachable, NoiseGenerator, XStart, YStart,
|
||||||
CellularAutomata,
|
|
||||||
CullUnreachable,
|
|
||||||
AreaStartingPosition,
|
|
||||||
XStart,
|
|
||||||
YStart,
|
|
||||||
},
|
},
|
||||||
|
MapBuilder, NoData,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let map = MapBuilder::<NoData>::new(20, 20)
|
let map = MapBuilder::<NoData>::new(20, 20)
|
||||||
.with(NoiseGenerator::uniform())
|
.with(NoiseGenerator::uniform())
|
||||||
.with(CellularAutomata::new())
|
.with(CellularAutomata::new())
|
||||||
.with(AreaStartingPosition::new(XStart::CENTER, YStart::CENTER))
|
.with(AreaStartingPosition::new(XStart::CENTER, YStart::CENTER))
|
||||||
.with(CullUnreachable::new())
|
.with(CullUnreachable::new())
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
println!("{:}", &map);
|
println!("{:}", &map);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
//! Calculate Dijkstra influence map
|
//! Calculate Dijkstra influence map
|
||||||
//!
|
//!
|
||||||
//! http://www.roguebasin.com/index.php?title=The_Incredible_Power_of_Dijkstra_Maps
|
//! http://www.roguebasin.com/index.php?title=The_Incredible_Power_of_Dijkstra_Maps
|
||||||
//!
|
//!
|
||||||
//! This algorithm calculates cost (distance) of moving from the given starting point
|
//! This algorithm calculates cost (distance) of moving from the given starting point
|
||||||
//! to the each point on the map. Point which are not reachable will get f32::MAX value.
|
//! to the each point on the map. Point which are not reachable will get f32::MAX value.
|
||||||
//!
|
//!
|
||||||
//! Example generator usage:
|
//! Example generator usage:
|
||||||
//! ---
|
//! ---
|
||||||
//! use rand::prelude::*;
|
//! use rand::prelude::*;
|
||||||
|
@ -14,21 +14,20 @@
|
||||||
//! map::{Map, TileType},
|
//! map::{Map, TileType},
|
||||||
//! starting_point::{AreaStartingPosition, XStart, YStart}
|
//! starting_point::{AreaStartingPosition, XStart, YStart}
|
||||||
//! };
|
//! };
|
||||||
//!
|
//!
|
||||||
//! 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);
|
||||||
//! map.set_tile(10, 10, TileType::Floor);
|
//! map.set_tile(10, 10, TileType::Floor);
|
||||||
//! let modifier = AreaStartingPosition::new(XStart::LEFT, YStart::TOP);
|
//! let modifier = AreaStartingPosition::new(XStart::LEFT, YStart::TOP);
|
||||||
//! let new_map = modifier.modify_map(&mut rng, &map);
|
//! let new_map = modifier.modify_map(&mut rng, &map);
|
||||||
//!
|
//!
|
||||||
//! assert_eq!(new_map.starting_point, Some(Point::new(10, 10)));
|
//! assert_eq!(new_map.starting_point, Some(Point::new(10, 10)));
|
||||||
//! ---
|
//! ---
|
||||||
//!
|
//!
|
||||||
|
|
||||||
use std::{collections::VecDeque, marker::PhantomData};
|
|
||||||
use std::f32::MAX;
|
|
||||||
use super::map::{BuilderData, Map};
|
use super::map::{BuilderData, Map};
|
||||||
|
use std::f32::MAX;
|
||||||
|
use std::{collections::VecDeque, marker::PhantomData};
|
||||||
|
|
||||||
/// Representation of a Dijkstra flow map.
|
/// Representation of a Dijkstra flow map.
|
||||||
/// map is a vector of floats, having a size equal to size_x * size_y (one per tile).
|
/// map is a vector of floats, having a size equal to size_x * size_y (one per tile).
|
||||||
|
@ -43,9 +42,9 @@ pub struct DijkstraMap<D> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<D: BuilderData> DijkstraMap<D> {
|
impl<D: BuilderData> DijkstraMap<D> {
|
||||||
//! Construct a new Dijkstra map, ready to run.
|
//! Construct a new Dijkstra map, ready to run.
|
||||||
pub fn new(map: &Map<D>) -> DijkstraMap<D> {
|
pub fn new(map: &Map<D>) -> DijkstraMap<D> {
|
||||||
let len = map.width * map.height;
|
let len = map.width * map.height;
|
||||||
let tiles = vec![MAX; len];
|
let tiles = vec![MAX; len];
|
||||||
let mut d = DijkstraMap {
|
let mut d = DijkstraMap {
|
||||||
tiles: tiles,
|
tiles: tiles,
|
||||||
|
@ -79,8 +78,12 @@ impl<D: BuilderData> DijkstraMap<D> {
|
||||||
let idx = self.xy_idx(x, y);
|
let idx = self.xy_idx(x, y);
|
||||||
let new_depth = depth + add_depth;
|
let new_depth = depth + add_depth;
|
||||||
let prev_depth = self.tiles[idx];
|
let prev_depth = self.tiles[idx];
|
||||||
if new_depth >= prev_depth { continue; }
|
if new_depth >= prev_depth {
|
||||||
if new_depth >= self.max_depth { continue; }
|
continue;
|
||||||
|
}
|
||||||
|
if new_depth >= self.max_depth {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
self.tiles[idx] = new_depth;
|
self.tiles[idx] = new_depth;
|
||||||
open_list.push_back(((x, y), new_depth));
|
open_list.push_back(((x, y), new_depth));
|
||||||
}
|
}
|
||||||
|
@ -88,7 +91,7 @@ impl<D: BuilderData> DijkstraMap<D> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn xy_idx(&self, x: usize, y: usize) -> usize {
|
fn xy_idx(&self, x: usize, y: usize) -> usize {
|
||||||
(y * self.size_x ) + x
|
(y * self.size_x) + x
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -112,13 +115,19 @@ mod tests {
|
||||||
map.starting_point = Some(Point::new(8, 1));
|
map.starting_point = Some(Point::new(8, 1));
|
||||||
let dm = DijkstraMap::new(&map);
|
let dm = DijkstraMap::new(&map);
|
||||||
|
|
||||||
println!("{:?}", &dm.tiles.iter().map(|&v| if v == f32::MAX {9.0} else {v}).collect::<Vec<f32>>());
|
println!(
|
||||||
|
"{:?}",
|
||||||
|
&dm.tiles
|
||||||
|
.iter()
|
||||||
|
.map(|&v| if v == f32::MAX { 9.0 } else { v })
|
||||||
|
.collect::<Vec<f32>>()
|
||||||
|
);
|
||||||
|
|
||||||
assert_eq!(dm.size_x, 10);
|
assert_eq!(dm.size_x, 10);
|
||||||
assert_eq!(dm.size_y, 3);
|
assert_eq!(dm.size_y, 3);
|
||||||
for i in 0..10 {
|
for i in 0..10 {
|
||||||
assert_eq!(dm.tiles[i], MAX);
|
assert_eq!(dm.tiles[i], MAX);
|
||||||
assert_eq!(dm.tiles[2*dm.size_x + i], MAX);
|
assert_eq!(dm.tiles[2 * dm.size_x + i], MAX);
|
||||||
let idx = dm.size_x + i;
|
let idx = dm.size_x + i;
|
||||||
if i < 3 || i == 9 {
|
if i < 3 || i == 9 {
|
||||||
assert_eq!(dm.tiles[idx], MAX);
|
assert_eq!(dm.tiles[idx], MAX);
|
||||||
|
@ -139,10 +148,9 @@ mod tests {
|
||||||
let mut map = Map::<NoData>::from_string(map_str);
|
let mut map = Map::<NoData>::from_string(map_str);
|
||||||
map.starting_point = Some(Point::new(2, 2));
|
map.starting_point = Some(Point::new(2, 2));
|
||||||
let dm = DijkstraMap::new(&map);
|
let dm = DijkstraMap::new(&map);
|
||||||
let expected = [MAX, MAX, MAX, MAX,
|
let expected = [
|
||||||
MAX, 1.45, 1.0, MAX,
|
MAX, MAX, MAX, MAX, MAX, 1.45, 1.0, MAX, MAX, 1.0, 0.0, MAX, MAX, MAX, MAX, MAX,
|
||||||
MAX, 1.0, 0.0, MAX,
|
];
|
||||||
MAX, MAX, MAX, MAX];
|
|
||||||
|
|
||||||
assert_eq!(dm.tiles, expected);
|
assert_eq!(dm.tiles, expected);
|
||||||
}
|
}
|
||||||
|
@ -158,13 +166,14 @@ mod tests {
|
||||||
let mut map = Map::<NoData>::from_string(map_str);
|
let mut map = Map::<NoData>::from_string(map_str);
|
||||||
map.starting_point = Some(Point::new(8, 2));
|
map.starting_point = Some(Point::new(8, 2));
|
||||||
let dm = DijkstraMap::new(&map);
|
let dm = DijkstraMap::new(&map);
|
||||||
let expected = [MAX, MAX, MAX, MAX, MAX, MAX, MAX, MAX, MAX, MAX,
|
let expected = [
|
||||||
MAX, 7.45, 6.45, 5.45, 4.45, 3.45, 2.45, 1.45, 1.0, MAX,
|
MAX, MAX, MAX, MAX, MAX, MAX, MAX, MAX, MAX, MAX, MAX, 7.45, 6.45, 5.45, 4.45, 3.45,
|
||||||
MAX, 7.9, 6.9, MAX, 4.0, 3.0, 2.0, 1.0, 0.0, MAX,
|
2.45, 1.45, 1.0, MAX, MAX, 7.9, 6.9, MAX, 4.0, 3.0, 2.0, 1.0, 0.0, MAX, MAX, MAX, MAX,
|
||||||
MAX, MAX, MAX, MAX, MAX, MAX, MAX, MAX, MAX, MAX];
|
MAX, MAX, MAX, MAX, MAX, MAX, MAX,
|
||||||
|
];
|
||||||
|
|
||||||
for (v, e) in dm.tiles.iter().zip(expected.iter()) {
|
for (v, e) in dm.tiles.iter().zip(expected.iter()) {
|
||||||
assert!(f32::abs(v - e) <= 0.01);
|
assert!(f32::abs(v - e) <= 0.01);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
//! Random rooms map generator.
|
//! Random rooms map generator.
|
||||||
//!
|
//!
|
||||||
//! Try to generate rooms of different size to fill the map area.
|
//! Try to generate rooms of different size to fill the map area.
|
||||||
//! Rooms will not overlap.
|
//! Rooms will not overlap.
|
||||||
//!
|
//!
|
||||||
//! Example generator usage:
|
//! Example generator usage:
|
||||||
//! ```
|
//! ```
|
||||||
//! use rand::prelude::*;
|
//! use rand::prelude::*;
|
||||||
|
@ -10,24 +10,23 @@
|
||||||
//! use mapgen::filter::{
|
//! use mapgen::filter::{
|
||||||
//! BspInterior
|
//! BspInterior
|
||||||
//! };
|
//! };
|
||||||
//!
|
//!
|
||||||
//! let mut rng = StdRng::seed_from_u64(100);
|
//! let mut rng = StdRng::seed_from_u64(100);
|
||||||
//! let gen = BspInterior::<NoData>::new();
|
//! let gen = BspInterior::<NoData>::new();
|
||||||
//! let map = gen.modify_map(&mut rng, &Map::new(80, 50));
|
//! 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);
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
|
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
use rand::prelude::*;
|
|
||||||
use crate::{BuilderData, MapFilter};
|
|
||||||
use crate::geometry::{Point, Rect};
|
use crate::geometry::{Point, Rect};
|
||||||
use crate::random::Rng;
|
use crate::random::Rng;
|
||||||
use crate::Map;
|
use crate::Map;
|
||||||
|
use crate::{BuilderData, MapFilter};
|
||||||
|
use rand::prelude::*;
|
||||||
|
|
||||||
pub struct BspInterior<D: BuilderData> {
|
pub struct BspInterior<D: BuilderData> {
|
||||||
min_room_size: usize,
|
min_room_size: usize,
|
||||||
|
@ -41,9 +40,8 @@ impl<D: BuilderData> MapFilter<D> for BspInterior<D> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<D: BuilderData> BspInterior<D> {
|
impl<D: BuilderData> BspInterior<D> {
|
||||||
|
|
||||||
pub fn new() -> Box<BspInterior<D>> {
|
pub fn new() -> Box<BspInterior<D>> {
|
||||||
Box::new(BspInterior{
|
Box::new(BspInterior {
|
||||||
min_room_size: 8,
|
min_room_size: 8,
|
||||||
phantom: PhantomData,
|
phantom: PhantomData,
|
||||||
})
|
})
|
||||||
|
@ -52,10 +50,10 @@ impl<D: BuilderData> BspInterior<D> {
|
||||||
fn build(&self, rng: &mut StdRng, map: &Map<D>) -> Map<D> {
|
fn build(&self, rng: &mut StdRng, map: &Map<D>) -> Map<D> {
|
||||||
let mut new_map = map.clone();
|
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, new_map.width-2, new_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);
|
||||||
|
|
||||||
let rooms_copy = rects.clone();
|
let rooms_copy = rects.clone();
|
||||||
for r in rooms_copy.iter() {
|
for r in rooms_copy.iter() {
|
||||||
|
@ -64,9 +62,9 @@ impl<D: BuilderData> BspInterior<D> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now we want corridors
|
// Now we want corridors
|
||||||
for i in 0..new_map.rooms.len()-1 {
|
for i in 0..new_map.rooms.len() - 1 {
|
||||||
let room = new_map.rooms[i];
|
let room = new_map.rooms[i];
|
||||||
let next_room = new_map.rooms[i+1];
|
let next_room = new_map.rooms[i + 1];
|
||||||
let start_x = rng.random_range(room.x1, room.x2);
|
let start_x = rng.random_range(room.x1, room.x2);
|
||||||
let start_y = rng.random_range(room.y1, room.y2);
|
let start_y = rng.random_range(room.y1, room.y2);
|
||||||
let end_x = rng.random_range(next_room.x1, next_room.x2);
|
let end_x = rng.random_range(next_room.x1, next_room.x2);
|
||||||
|
@ -74,7 +72,7 @@ impl<D: BuilderData> BspInterior<D> {
|
||||||
new_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));
|
||||||
}
|
}
|
||||||
|
|
||||||
new_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>) {
|
||||||
|
@ -84,7 +82,7 @@ impl<D: BuilderData> BspInterior<D> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate boundaries
|
// Calculate boundaries
|
||||||
let width = rect.x2 - rect.x1;
|
let width = rect.x2 - rect.x1;
|
||||||
let height = rect.y2 - rect.y1;
|
let height = rect.y2 - rect.y1;
|
||||||
let half_width = width / 2;
|
let half_width = width / 2;
|
||||||
let half_height = height / 2;
|
let half_height = height / 2;
|
||||||
|
@ -93,46 +91,52 @@ impl<D: BuilderData> BspInterior<D> {
|
||||||
|
|
||||||
if split <= 2 {
|
if split <= 2 {
|
||||||
// Horizontal split
|
// Horizontal split
|
||||||
let h1 = Rect::new( rect.x1, rect.y1, half_width-1, height );
|
let h1 = Rect::new(rect.x1, rect.y1, half_width - 1, height);
|
||||||
rects.push( h1 );
|
rects.push(h1);
|
||||||
if half_width > self.min_room_size { self.add_subrects(h1, rng, rects); }
|
if half_width > self.min_room_size {
|
||||||
let h2 = Rect::new( rect.x1 + half_width, rect.y1, half_width, height );
|
self.add_subrects(h1, rng, rects);
|
||||||
rects.push( h2 );
|
}
|
||||||
if half_width > self.min_room_size { self.add_subrects(h2, rng, rects); }
|
let h2 = Rect::new(rect.x1 + half_width, rect.y1, half_width, height);
|
||||||
|
rects.push(h2);
|
||||||
|
if half_width > self.min_room_size {
|
||||||
|
self.add_subrects(h2, rng, rects);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// Vertical split
|
// Vertical split
|
||||||
let v1 = Rect::new( rect.x1, rect.y1, width, half_height-1 );
|
let v1 = Rect::new(rect.x1, rect.y1, width, half_height - 1);
|
||||||
rects.push(v1);
|
rects.push(v1);
|
||||||
if half_height > self.min_room_size { self.add_subrects(v1, rng, rects); }
|
if half_height > self.min_room_size {
|
||||||
let v2 = Rect::new( rect.x1, rect.y1 + half_height, width, half_height );
|
self.add_subrects(v1, rng, rects);
|
||||||
|
}
|
||||||
|
let v2 = Rect::new(rect.x1, rect.y1 + half_height, width, half_height);
|
||||||
rects.push(v2);
|
rects.push(v2);
|
||||||
if half_height > self.min_room_size { self.add_subrects(v2, rng, rects); }
|
if half_height > self.min_room_size {
|
||||||
|
self.add_subrects(v2, rng, rects);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// ------------------------------------------------------------------------------------------------
|
/// ------------------------------------------------------------------------------------------------
|
||||||
/// Module unit tests
|
/// Module unit tests
|
||||||
/// ------------------------------------------------------------------------------------------------
|
/// ------------------------------------------------------------------------------------------------
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::{Map, map::NoData};
|
use crate::{map::NoData, Map};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn no_corridors_on_borders() {
|
fn no_corridors_on_borders() {
|
||||||
let mut rng = StdRng::seed_from_u64(907647352);
|
let mut rng = StdRng::seed_from_u64(907647352);
|
||||||
let gen = BspInterior::<NoData>::new();
|
let gen = BspInterior::<NoData>::new();
|
||||||
let map = gen.modify_map(&mut rng, &Map::new(80, 50));
|
let map = gen.modify_map(&mut rng, &Map::new(80, 50));
|
||||||
for i in 0..80 {
|
for i in 0..80 {
|
||||||
assert!(map.at(i, 0).is_blocked());
|
assert!(map.at(i, 0).is_blocked());
|
||||||
assert!(map.at(i, 49).is_blocked());
|
assert!(map.at(i, 49).is_blocked());
|
||||||
}
|
}
|
||||||
for j in 0..50 {
|
for j in 0..50 {
|
||||||
assert!(map.at(0, j).is_blocked());
|
assert!(map.at(0, j).is_blocked());
|
||||||
assert!(map.at(79, j).is_blocked());
|
assert!(map.at(79, j).is_blocked());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
|
||||||
|
|
|
@ -1,32 +1,31 @@
|
||||||
//! Random rooms map generator.
|
//! Random rooms map generator.
|
||||||
//!
|
//!
|
||||||
//! Try to generate rooms of different size to fill the map area.
|
//! Try to generate rooms of different size to fill the map area.
|
||||||
//! Rooms will not overlap.
|
//! Rooms will not overlap.
|
||||||
//!
|
//!
|
||||||
//! Example generator usage:
|
//! Example generator usage:
|
||||||
//! ```
|
//! ```
|
||||||
//! use rand::prelude::*;
|
//! use rand::prelude::*;
|
||||||
//! use mapgen::{Map, MapFilter, NoData};
|
//! use mapgen::{Map, MapFilter, NoData};
|
||||||
//! use mapgen::filter::BspRooms;
|
//! use mapgen::filter::BspRooms;
|
||||||
//!
|
//!
|
||||||
//! let mut rng = StdRng::seed_from_u64(100);
|
//! let mut rng = StdRng::seed_from_u64(100);
|
||||||
//! let gen = BspRooms::<NoData>::new();
|
//! let gen = BspRooms::<NoData>::new();
|
||||||
//! let map = gen.modify_map(&mut rng, &Map::new(80, 50));
|
//! 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);
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
|
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
use rand::prelude::*;
|
|
||||||
use crate::BuilderData;
|
|
||||||
use crate::MapFilter;
|
|
||||||
use crate::geometry::Rect;
|
use crate::geometry::Rect;
|
||||||
use crate::random::Rng;
|
use crate::random::Rng;
|
||||||
|
use crate::BuilderData;
|
||||||
use crate::Map;
|
use crate::Map;
|
||||||
|
use crate::MapFilter;
|
||||||
|
use rand::prelude::*;
|
||||||
|
|
||||||
pub struct BspRooms<D: BuilderData> {
|
pub struct BspRooms<D: BuilderData> {
|
||||||
max_split: usize,
|
max_split: usize,
|
||||||
|
@ -34,7 +33,7 @@ pub struct BspRooms<D: BuilderData> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<D: BuilderData> MapFilter<D> for BspRooms<D> {
|
impl<D: BuilderData> MapFilter<D> for BspRooms<D> {
|
||||||
fn modify_map(&self, rng: &mut StdRng, map: &Map<D>) -> Map<D> {
|
fn modify_map(&self, rng: &mut StdRng, map: &Map<D>) -> Map<D> {
|
||||||
self.build_rooms(map, rng)
|
self.build_rooms(map, rng)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -47,11 +46,11 @@ impl<D: BuilderData> BspRooms<D> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_rooms(&self, map: &Map<D>, rng : &mut StdRng) -> Map<D> {
|
fn build_rooms(&self, map: &Map<D>, rng: &mut StdRng) -> Map<D> {
|
||||||
let mut new_map = map.clone();
|
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, new_map.width-5, new_map.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
|
||||||
|
|
||||||
|
@ -77,16 +76,33 @@ impl<D: BuilderData> BspRooms<D> {
|
||||||
let half_width = usize::max(width / 2, 1);
|
let half_width = usize::max(width / 2, 1);
|
||||||
let half_height = usize::max(height / 2, 1);
|
let half_height = usize::max(height / 2, 1);
|
||||||
|
|
||||||
rects.push(Rect::new( rect.x1, rect.y1, half_width, half_height ));
|
rects.push(Rect::new(rect.x1, rect.y1, half_width, half_height));
|
||||||
rects.push(Rect::new( rect.x1, rect.y1 + half_height, half_width, half_height ));
|
rects.push(Rect::new(
|
||||||
rects.push(Rect::new( rect.x1 + half_width, rect.y1, half_width, half_height ));
|
rect.x1,
|
||||||
rects.push(Rect::new( rect.x1 + half_width, rect.y1 + half_height, half_width, half_height ));
|
rect.y1 + half_height,
|
||||||
|
half_width,
|
||||||
|
half_height,
|
||||||
|
));
|
||||||
|
rects.push(Rect::new(
|
||||||
|
rect.x1 + half_width,
|
||||||
|
rect.y1,
|
||||||
|
half_width,
|
||||||
|
half_height,
|
||||||
|
));
|
||||||
|
rects.push(Rect::new(
|
||||||
|
rect.x1 + half_width,
|
||||||
|
rect.y1 + half_height,
|
||||||
|
half_width,
|
||||||
|
half_height,
|
||||||
|
));
|
||||||
|
|
||||||
rects
|
rects
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_random_rect(&self, rng : &mut StdRng, rects: &Vec<Rect>) -> Rect {
|
fn get_random_rect(&self, rng: &mut StdRng, rects: &Vec<Rect>) -> Rect {
|
||||||
if rects.len() == 1 { return rects[0]; }
|
if rects.len() == 1 {
|
||||||
|
return rects[0];
|
||||||
|
}
|
||||||
let idx = rng.random_range(0, rects.len());
|
let idx = rng.random_range(0, rects.len());
|
||||||
rects[idx]
|
rects[idx]
|
||||||
}
|
}
|
||||||
|
@ -117,15 +133,25 @@ impl<D: BuilderData> BspRooms<D> {
|
||||||
let mut can_build = true;
|
let mut can_build = true;
|
||||||
|
|
||||||
for r in map.rooms.iter() {
|
for r in map.rooms.iter() {
|
||||||
if r.intersect(&rect) { can_build = false; }
|
if r.intersect(&rect) {
|
||||||
|
can_build = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for y in expanded.y1 ..= expanded.y2 {
|
for y in expanded.y1..=expanded.y2 {
|
||||||
for x in expanded.x1 ..= expanded.x2 {
|
for x in expanded.x1..=expanded.x2 {
|
||||||
if x > map.width - 2 { can_build = false; }
|
if x > map.width - 2 {
|
||||||
if y > map.height - 2 { can_build = false; }
|
can_build = false;
|
||||||
if x < 1 { can_build = false; }
|
}
|
||||||
if y < 1 { can_build = false; }
|
if y > map.height - 2 {
|
||||||
|
can_build = false;
|
||||||
|
}
|
||||||
|
if x < 1 {
|
||||||
|
can_build = false;
|
||||||
|
}
|
||||||
|
if y < 1 {
|
||||||
|
can_build = false;
|
||||||
|
}
|
||||||
if can_build {
|
if can_build {
|
||||||
if map.at(x as usize, y as usize).is_walkable() {
|
if map.at(x as usize, y as usize).is_walkable() {
|
||||||
can_build = false;
|
can_build = false;
|
||||||
|
@ -148,17 +174,16 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn no_corridors_on_borders() {
|
fn no_corridors_on_borders() {
|
||||||
let mut rng = StdRng::seed_from_u64(907647352);
|
let mut rng = StdRng::seed_from_u64(907647352);
|
||||||
let gen = BspRooms::<NoData>::new();
|
let gen = BspRooms::<NoData>::new();
|
||||||
let map = gen.modify_map(&mut rng, &Map::new(80, 50));
|
let map = gen.modify_map(&mut rng, &Map::new(80, 50));
|
||||||
for i in 0..80 {
|
for i in 0..80 {
|
||||||
assert!(map.at(i, 0).is_blocked());
|
assert!(map.at(i, 0).is_blocked());
|
||||||
assert!(map.at(i, 49).is_blocked());
|
assert!(map.at(i, 49).is_blocked());
|
||||||
}
|
}
|
||||||
for j in 0..50 {
|
for j in 0..50 {
|
||||||
assert!(map.at(0, j).is_blocked());
|
assert!(map.at(0, j).is_blocked());
|
||||||
assert!(map.at(79, j).is_blocked());
|
assert!(map.at(79, j).is_blocked());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
|
||||||
|
|
|
@ -1,33 +1,32 @@
|
||||||
//! Cellular automata map filter.
|
//! Cellular automata map filter.
|
||||||
//!
|
//!
|
||||||
//! Check this [article](http://www.roguebasin.com/index.php?title=Cellular_Automata_Method_for_Generating_Random_Cave-Like_Levels)
|
//! Check this [article](http://www.roguebasin.com/index.php?title=Cellular_Automata_Method_for_Generating_Random_Cave-Like_Levels)
|
||||||
//! for more information about the algorithm behind this generator.
|
//! for more information about the algorithm behind this generator.
|
||||||
//!
|
//!
|
||||||
//! This algorithm requires that map first is filtered with some noise.
|
//! This algorithm requires that map first is filtered with some noise.
|
||||||
//! For example `UniformNoise`. It can also be apply to any other non empty map.
|
//! For example `UniformNoise`. It can also be apply to any other non empty map.
|
||||||
//!
|
//!
|
||||||
//! Example usage:
|
//! Example usage:
|
||||||
//! ```
|
//! ```
|
||||||
//! use rand::prelude::*;
|
//! use rand::prelude::*;
|
||||||
//! use mapgen::{Map, MapFilter, NoData};
|
//! use mapgen::{Map, MapFilter, NoData};
|
||||||
//! use mapgen::filter::CellularAutomata;
|
//! use mapgen::filter::CellularAutomata;
|
||||||
//!
|
//!
|
||||||
//! let mut rng = StdRng::seed_from_u64(100);
|
//! let mut rng = StdRng::seed_from_u64(100);
|
||||||
//! let gen = CellularAutomata::<NoData>::new();
|
//! let gen = CellularAutomata::<NoData>::new();
|
||||||
//! let map = gen.modify_map(&mut rng, &Map::new(80, 50));
|
//! 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);
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
|
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
use rand::prelude::*;
|
|
||||||
use crate::BuilderData;
|
use crate::BuilderData;
|
||||||
use crate::MapFilter;
|
use crate::MapFilter;
|
||||||
use crate::{Map, Tile};
|
use crate::{Map, Tile};
|
||||||
|
use rand::prelude::*;
|
||||||
|
|
||||||
/// Map filter
|
/// Map filter
|
||||||
pub struct CellularAutomata<D: BuilderData> {
|
pub struct CellularAutomata<D: BuilderData> {
|
||||||
|
@ -36,7 +35,7 @@ pub struct CellularAutomata<D: BuilderData> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<D: BuilderData> MapFilter<D> for CellularAutomata<D> {
|
impl<D: BuilderData> MapFilter<D> for CellularAutomata<D> {
|
||||||
fn modify_map(&self, _rng: &mut StdRng, map: &Map<D>) -> Map<D> {
|
fn modify_map(&self, _rng: &mut StdRng, map: &Map<D>) -> Map<D> {
|
||||||
self.build(map)
|
self.build(map)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -59,26 +58,31 @@ impl<D: BuilderData> CellularAutomata<D> {
|
||||||
|
|
||||||
new_map
|
new_map
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_iteration<D: BuilderData>(map: &Map<D>) -> Map<D> {
|
fn apply_iteration<D: BuilderData>(map: &Map<D>) -> Map<D> {
|
||||||
let mut new_map = map.clone();
|
let mut new_map = map.clone();
|
||||||
|
|
||||||
for y in 1..map.height-1 {
|
for y in 1..map.height - 1 {
|
||||||
for x in 1..map.width-1 {
|
for x in 1..map.width - 1 {
|
||||||
let idxs = [
|
let idxs = [
|
||||||
(x-1, y-1), (x, y-1), (x+1, y-1),
|
(x - 1, y - 1),
|
||||||
(x-1, y), (x+1, y),
|
(x, y - 1),
|
||||||
(x-1, y+1), (x, y+1), (x+1, y+1)];
|
(x + 1, y - 1),
|
||||||
let neighbors = idxs.iter()
|
(x - 1, y),
|
||||||
|
(x + 1, y),
|
||||||
|
(x - 1, y + 1),
|
||||||
|
(x, y + 1),
|
||||||
|
(x + 1, y + 1),
|
||||||
|
];
|
||||||
|
let neighbors = idxs
|
||||||
|
.iter()
|
||||||
.filter(|(x, y)| map.at(*x, *y).is_blocked())
|
.filter(|(x, y)| map.at(*x, *y).is_blocked())
|
||||||
.count();
|
.count();
|
||||||
|
|
||||||
if neighbors > 4 || neighbors == 0 {
|
if neighbors > 4 || neighbors == 0 {
|
||||||
new_map.set_tile(x, y, Tile::wall())
|
new_map.set_tile(x, y, Tile::wall())
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
new_map.set_tile(x, y, Tile::floor());
|
new_map.set_tile(x, y, Tile::floor());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -103,7 +107,6 @@ mod tests {
|
||||||
assert!(new_map.at(1, 1).is_blocked());
|
assert!(new_map.at(1, 1).is_blocked());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_iteration_floor() {
|
fn test_iteration_floor() {
|
||||||
let mut map = Map::<NoData>::new(3, 3);
|
let mut map = Map::<NoData>::new(3, 3);
|
||||||
|
@ -115,5 +118,4 @@ mod tests {
|
||||||
let new_map = apply_iteration(&map);
|
let new_map = apply_iteration(&map);
|
||||||
assert!(new_map.at(1, 1).is_walkable());
|
assert!(new_map.at(1, 1).is_walkable());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
|
||||||
|
|
|
@ -1,16 +1,15 @@
|
||||||
//! Remove unreachable areas from the map.
|
//! Remove unreachable areas from the map.
|
||||||
//!
|
//!
|
||||||
//! This modifier reiquires starting position on the map.
|
//! This modifier reiquires starting position on the map.
|
||||||
//! It will add wall on every tile which is not accessible from the starting point.
|
//! It will add wall on every tile which is not accessible from the starting point.
|
||||||
//!
|
//!
|
||||||
|
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
use rand::prelude::StdRng;
|
use crate::dijkstra::DijkstraMap;
|
||||||
use crate::MapFilter;
|
use crate::MapFilter;
|
||||||
use crate::{BuilderData, Map, Tile};
|
use crate::{BuilderData, Map, Tile};
|
||||||
use crate::dijkstra::DijkstraMap;
|
use rand::prelude::StdRng;
|
||||||
|
|
||||||
|
|
||||||
/// Remove unreachable areas from the map.
|
/// Remove unreachable areas from the map.
|
||||||
pub struct CullUnreachable<D: BuilderData> {
|
pub struct CullUnreachable<D: BuilderData> {
|
||||||
|
@ -18,7 +17,7 @@ pub struct CullUnreachable<D: BuilderData> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<D: BuilderData> MapFilter<D> for CullUnreachable<D> {
|
impl<D: BuilderData> MapFilter<D> for CullUnreachable<D> {
|
||||||
fn modify_map(&self, _: &mut StdRng, map: &Map<D>) -> Map<D> {
|
fn modify_map(&self, _: &mut StdRng, map: &Map<D>) -> Map<D> {
|
||||||
self.build(map)
|
self.build(map)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -53,11 +52,11 @@ impl<D: BuilderData> CullUnreachable<D> {
|
||||||
/// ------------------------------------------------------------------------------------------------
|
/// ------------------------------------------------------------------------------------------------
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use rand::prelude::*;
|
|
||||||
use super::*;
|
|
||||||
use super::MapFilter;
|
use super::MapFilter;
|
||||||
|
use super::*;
|
||||||
use crate::geometry::Point;
|
use crate::geometry::Point;
|
||||||
use crate::map::{Map, NoData};
|
use crate::map::{Map, NoData};
|
||||||
|
use rand::prelude::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_culling() {
|
fn test_culling() {
|
||||||
|
@ -75,11 +74,10 @@ mod tests {
|
||||||
";
|
";
|
||||||
let expected_map = Map::<NoData>::from_string(expected_map_str);
|
let expected_map = Map::<NoData>::from_string(expected_map_str);
|
||||||
|
|
||||||
|
|
||||||
let modifier = CullUnreachable::new();
|
let modifier = CullUnreachable::new();
|
||||||
let mut rng = StdRng::seed_from_u64(0);
|
let mut rng = StdRng::seed_from_u64(0);
|
||||||
let new_map = modifier.modify_map(&mut rng, &map);
|
let new_map = modifier.modify_map(&mut rng, &map);
|
||||||
|
|
||||||
assert_eq!(new_map.tiles, expected_map.tiles);
|
assert_eq!(new_map.tiles, expected_map.tiles);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,18 +1,17 @@
|
||||||
//! Add exit point to the map
|
//! Add exit point to the map
|
||||||
//!
|
//!
|
||||||
//! This modifier will try to add exit point as far as possible from the starting point.
|
//! This modifier will try to add exit point as far as possible from the starting point.
|
||||||
//! It means that starting point have to be set before this Modyfier will start.
|
//! It means that starting point have to be set before this Modyfier will start.
|
||||||
//!
|
//!
|
||||||
|
|
||||||
|
use crate::dijkstra::DijkstraMap;
|
||||||
|
use crate::geometry::Point;
|
||||||
|
use crate::BuilderData;
|
||||||
|
use crate::Map;
|
||||||
|
use crate::MapFilter;
|
||||||
|
use rand::prelude::StdRng;
|
||||||
use std::f32;
|
use std::f32;
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
use rand::prelude::StdRng;
|
|
||||||
use crate::BuilderData;
|
|
||||||
use crate::geometry::Point;
|
|
||||||
use crate::MapFilter;
|
|
||||||
use crate::Map;
|
|
||||||
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<D: BuilderData> {
|
pub struct DistantExit<D: BuilderData> {
|
||||||
|
@ -20,7 +19,7 @@ pub struct DistantExit<D: BuilderData> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<D: BuilderData> MapFilter<D> for DistantExit<D> {
|
impl<D: BuilderData> MapFilter<D> for DistantExit<D> {
|
||||||
fn modify_map(&self, _: &mut StdRng, map: &Map<D>) -> Map<D> {
|
fn modify_map(&self, _: &mut StdRng, map: &Map<D>) -> Map<D> {
|
||||||
self.build(map)
|
self.build(map)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -43,7 +42,7 @@ impl<D: BuilderData> DistantExit<D> {
|
||||||
if value < f32::MAX && value > best_value {
|
if value < f32::MAX && value > best_value {
|
||||||
best_value = value;
|
best_value = value;
|
||||||
best_idx = i;
|
best_idx = i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let x = best_idx % map.width;
|
let x = best_idx % map.width;
|
||||||
let y = best_idx / map.width;
|
let y = best_idx / map.width;
|
||||||
|
@ -57,11 +56,11 @@ impl<D: BuilderData> DistantExit<D> {
|
||||||
/// ------------------------------------------------------------------------------------------------
|
/// ------------------------------------------------------------------------------------------------
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use rand::prelude::*;
|
|
||||||
use super::*;
|
|
||||||
use super::MapFilter;
|
use super::MapFilter;
|
||||||
|
use super::*;
|
||||||
use crate::geometry::Point;
|
use crate::geometry::Point;
|
||||||
use crate::map::{Map, NoData};
|
use crate::map::{Map, NoData};
|
||||||
|
use rand::prelude::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_exit() {
|
fn test_exit() {
|
||||||
|
@ -80,4 +79,4 @@ mod tests {
|
||||||
|
|
||||||
assert_eq!(new_map.exit_point, Some(Point::new(1, 2)));
|
assert_eq!(new_map.exit_point, Some(Point::new(1, 2)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,34 +3,36 @@
|
||||||
//! use rand::prelude::*;
|
//! use rand::prelude::*;
|
||||||
//! use mapgen::{Map, MapFilter, NoData};
|
//! use mapgen::{Map, MapFilter, NoData};
|
||||||
//! use mapgen::filter::DrunkardsWalk;
|
//! use mapgen::filter::DrunkardsWalk;
|
||||||
//!
|
//!
|
||||||
//! let mut rng = StdRng::seed_from_u64(100);
|
//! let mut rng = StdRng::seed_from_u64(100);
|
||||||
//! let gen = DrunkardsWalk::<NoData>::open_area();
|
//! let gen = DrunkardsWalk::<NoData>::open_area();
|
||||||
//! let map = gen.modify_map(&mut rng, &Map::new(80, 50));
|
//! 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);
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
|
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
use rand::prelude::*;
|
|
||||||
use crate::MapFilter;
|
use crate::MapFilter;
|
||||||
use crate::{
|
use crate::{
|
||||||
BuilderData,
|
|
||||||
map::{Map, Symmetry, Tile},
|
|
||||||
geometry::Point,
|
geometry::Point,
|
||||||
random::Rng
|
map::{Map, Symmetry, Tile},
|
||||||
|
random::Rng,
|
||||||
|
BuilderData,
|
||||||
};
|
};
|
||||||
|
use rand::prelude::*;
|
||||||
|
|
||||||
#[derive(PartialEq, Copy, Clone)]
|
#[derive(PartialEq, Copy, Clone)]
|
||||||
pub enum DrunkSpawnMode { StartingPoint, Random }
|
pub enum DrunkSpawnMode {
|
||||||
|
StartingPoint,
|
||||||
|
Random,
|
||||||
|
}
|
||||||
|
|
||||||
pub struct DrunkardsWalk<D: BuilderData> {
|
pub struct DrunkardsWalk<D: BuilderData> {
|
||||||
spawn_mode : DrunkSpawnMode,
|
spawn_mode: DrunkSpawnMode,
|
||||||
drunken_lifetime : i32,
|
drunken_lifetime: i32,
|
||||||
floor_percent: f32,
|
floor_percent: f32,
|
||||||
brush_size: usize,
|
brush_size: usize,
|
||||||
symmetry: Symmetry,
|
symmetry: Symmetry,
|
||||||
|
@ -38,19 +40,20 @@ pub struct DrunkardsWalk<D: BuilderData> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<D: BuilderData> MapFilter<D> for DrunkardsWalk<D> {
|
impl<D: BuilderData> MapFilter<D> for DrunkardsWalk<D> {
|
||||||
fn modify_map(&self, rng: &mut StdRng, map: &Map<D>) -> Map<D> {
|
fn modify_map(&self, rng: &mut StdRng, map: &Map<D>) -> Map<D> {
|
||||||
self.build(rng, map)
|
self.build(rng, map)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<D: BuilderData> DrunkardsWalk<D> {
|
impl<D: BuilderData> DrunkardsWalk<D> {
|
||||||
pub fn new( spawn_mode: DrunkSpawnMode,
|
pub fn new(
|
||||||
drunken_lifetime: i32,
|
spawn_mode: DrunkSpawnMode,
|
||||||
floor_percent: f32,
|
drunken_lifetime: i32,
|
||||||
brush_size: usize,
|
floor_percent: f32,
|
||||||
symmetry: Symmetry) -> Box<DrunkardsWalk<D>>
|
brush_size: usize,
|
||||||
{
|
symmetry: Symmetry,
|
||||||
Box::new(DrunkardsWalk{
|
) -> Box<DrunkardsWalk<D>> {
|
||||||
|
Box::new(DrunkardsWalk {
|
||||||
spawn_mode,
|
spawn_mode,
|
||||||
drunken_lifetime,
|
drunken_lifetime,
|
||||||
floor_percent,
|
floor_percent,
|
||||||
|
@ -79,18 +82,18 @@ impl<D: BuilderData> DrunkardsWalk<D> {
|
||||||
pub fn fearful_symmetry() -> Box<DrunkardsWalk<D>> {
|
pub fn fearful_symmetry() -> Box<DrunkardsWalk<D>> {
|
||||||
Self::new(DrunkSpawnMode::Random, 400, 0.4, 1, Symmetry::Both)
|
Self::new(DrunkSpawnMode::Random, 400, 0.4, 1, Symmetry::Both)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build(&self, rng: &mut StdRng, map: &Map<D>) -> Map<D> {
|
fn build(&self, rng: &mut StdRng, map: &Map<D>) -> Map<D> {
|
||||||
let mut new_map = map.clone();
|
let mut new_map = map.clone();
|
||||||
// Set a central starting point
|
// Set a central starting point
|
||||||
let starting_position = Point::new( new_map.width / 2, new_map.height / 2 );
|
let starting_position = Point::new(new_map.width / 2, new_map.height / 2);
|
||||||
new_map.set_tile(starting_position.x, starting_position.y, Tile::floor());
|
new_map.set_tile(starting_position.x, starting_position.y, Tile::floor());
|
||||||
|
|
||||||
let total_tiles = new_map.width * new_map.height;
|
let total_tiles = new_map.width * new_map.height;
|
||||||
let desired_floor_tiles = (self.floor_percent * total_tiles as f32) as usize;
|
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.is_walkable()).count();
|
let mut floor_tile_count = new_map.tiles.iter().filter(|a| a.is_walkable()).count();
|
||||||
let mut digger_count = 0;
|
let mut digger_count = 0;
|
||||||
while floor_tile_count < desired_floor_tiles {
|
while floor_tile_count < desired_floor_tiles {
|
||||||
let mut drunk_x;
|
let mut drunk_x;
|
||||||
let mut drunk_y;
|
let mut drunk_y;
|
||||||
match self.spawn_mode {
|
match self.spawn_mode {
|
||||||
|
@ -111,15 +114,31 @@ impl<D: BuilderData> DrunkardsWalk<D> {
|
||||||
let mut drunk_life = self.drunken_lifetime;
|
let mut drunk_life = self.drunken_lifetime;
|
||||||
|
|
||||||
while drunk_life > 0 {
|
while drunk_life > 0 {
|
||||||
new_map.set_tile(drunk_x, drunk_y, Tile::wall());
|
new_map.set_tile(drunk_x, drunk_y, Tile::wall());
|
||||||
new_map.paint(self.symmetry, self.brush_size, drunk_x, drunk_y);
|
new_map.paint(self.symmetry, self.brush_size, drunk_x, drunk_y);
|
||||||
|
|
||||||
let stagger_direction = rng.roll_dice(1, 4);
|
let stagger_direction = rng.roll_dice(1, 4);
|
||||||
match stagger_direction {
|
match stagger_direction {
|
||||||
1 => { if drunk_x > 1 { drunk_x -= 1; } }
|
1 => {
|
||||||
2 => { if drunk_x < new_map.width-2 { drunk_x += 1; } }
|
if drunk_x > 1 {
|
||||||
3 => { if drunk_y > 1 { drunk_y -=1; } }
|
drunk_x -= 1;
|
||||||
_ => { if drunk_y < new_map.height-2 { drunk_y += 1; } }
|
}
|
||||||
|
}
|
||||||
|
2 => {
|
||||||
|
if drunk_x < new_map.width - 2 {
|
||||||
|
drunk_x += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
3 => {
|
||||||
|
if drunk_y > 1 {
|
||||||
|
drunk_y -= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
if drunk_y < new_map.height - 2 {
|
||||||
|
drunk_y += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
drunk_life -= 1;
|
drunk_life -= 1;
|
||||||
|
@ -131,4 +150,4 @@ impl<D: BuilderData> DrunkardsWalk<D> {
|
||||||
|
|
||||||
new_map
|
new_map
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,32 +3,31 @@
|
||||||
//! use rand::prelude::*;
|
//! use rand::prelude::*;
|
||||||
//! use mapgen::{Map, MapFilter, NoData};
|
//! use mapgen::{Map, MapFilter, NoData};
|
||||||
//! use mapgen::filter::MazeBuilder;
|
//! use mapgen::filter::MazeBuilder;
|
||||||
//!
|
//!
|
||||||
//! let mut rng = StdRng::seed_from_u64(100);
|
//! let mut rng = StdRng::seed_from_u64(100);
|
||||||
//! let gen = MazeBuilder::<NoData>::new();
|
//! let gen = MazeBuilder::<NoData>::new();
|
||||||
//! let map = gen.modify_map(&mut rng, &Map::new(80, 50));
|
//! 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);
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
|
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
use rand::prelude::*;
|
|
||||||
use crate::MapFilter;
|
use crate::MapFilter;
|
||||||
use crate::{
|
use crate::{
|
||||||
map::{BuilderData, Map, Tile},
|
map::{BuilderData, Map, Tile},
|
||||||
random::Rng
|
random::Rng,
|
||||||
};
|
};
|
||||||
|
use rand::prelude::*;
|
||||||
|
|
||||||
pub struct MazeBuilder<D: BuilderData> {
|
pub struct MazeBuilder<D: BuilderData> {
|
||||||
phantom: PhantomData<D>,
|
phantom: PhantomData<D>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<D: BuilderData> MapFilter<D> for MazeBuilder<D> {
|
impl<D: BuilderData> MapFilter<D> for MazeBuilder<D> {
|
||||||
fn modify_map(&self, rng: &mut StdRng, map: &Map<D>) -> Map<D> {
|
fn modify_map(&self, rng: &mut StdRng, map: &Map<D>) -> Map<D> {
|
||||||
self.build(rng, map)
|
self.build(rng, map)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -43,7 +42,7 @@ impl<D: BuilderData> MazeBuilder<D> {
|
||||||
#[allow(clippy::map_entry)]
|
#[allow(clippy::map_entry)]
|
||||||
fn build(&self, rng: &mut StdRng, map: &Map<D>) -> Map<D> {
|
fn build(&self, rng: &mut StdRng, map: &Map<D>) -> Map<D> {
|
||||||
let mut new_map = map.clone();
|
let mut new_map = map.clone();
|
||||||
let mut maze = Grid::new((map.width as i32/ 2)-2, (map.height as i32/ 2)-2, rng);
|
let mut maze = Grid::new((map.width as i32 / 2) - 2, (map.height as i32 / 2) - 2, rng);
|
||||||
maze.generate_maze(&mut new_map);
|
maze.generate_maze(&mut new_map);
|
||||||
new_map
|
new_map
|
||||||
}
|
}
|
||||||
|
@ -51,10 +50,10 @@ impl<D: BuilderData> MazeBuilder<D> {
|
||||||
|
|
||||||
/* Maze code taken under MIT from https://github.com/cyucelen/mazeGenerator/ */
|
/* Maze code taken under MIT from https://github.com/cyucelen/mazeGenerator/ */
|
||||||
|
|
||||||
const TOP : usize = 0;
|
const TOP: usize = 0;
|
||||||
const RIGHT : usize = 1;
|
const RIGHT: usize = 1;
|
||||||
const BOTTOM : usize = 2;
|
const BOTTOM: usize = 2;
|
||||||
const LEFT : usize = 3;
|
const LEFT: usize = 3;
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
struct Cell {
|
struct Cell {
|
||||||
|
@ -66,31 +65,28 @@ struct Cell {
|
||||||
|
|
||||||
impl Cell {
|
impl Cell {
|
||||||
fn new(row: i32, column: i32) -> Cell {
|
fn new(row: i32, column: i32) -> Cell {
|
||||||
Cell{
|
Cell {
|
||||||
row,
|
row,
|
||||||
column,
|
column,
|
||||||
walls: [true, true, true, true],
|
walls: [true, true, true, true],
|
||||||
visited: false
|
visited: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn remove_walls(&mut self, next : &mut Cell) {
|
fn remove_walls(&mut self, next: &mut Cell) {
|
||||||
let x = self.column - next.column;
|
let x = self.column - next.column;
|
||||||
let y = self.row - next.row;
|
let y = self.row - next.row;
|
||||||
|
|
||||||
if x == 1 {
|
if x == 1 {
|
||||||
self.walls[LEFT] = false;
|
self.walls[LEFT] = false;
|
||||||
next.walls[RIGHT] = false;
|
next.walls[RIGHT] = false;
|
||||||
}
|
} else if x == -1 {
|
||||||
else if x == -1 {
|
|
||||||
self.walls[RIGHT] = false;
|
self.walls[RIGHT] = false;
|
||||||
next.walls[LEFT] = false;
|
next.walls[LEFT] = false;
|
||||||
}
|
} else if y == 1 {
|
||||||
else if y == 1 {
|
|
||||||
self.walls[TOP] = false;
|
self.walls[TOP] = false;
|
||||||
next.walls[BOTTOM] = false;
|
next.walls[BOTTOM] = false;
|
||||||
}
|
} else if y == -1 {
|
||||||
else if y == -1 {
|
|
||||||
self.walls[BOTTOM] = false;
|
self.walls[BOTTOM] = false;
|
||||||
next.walls[TOP] = false;
|
next.walls[TOP] = false;
|
||||||
}
|
}
|
||||||
|
@ -103,13 +99,13 @@ struct Grid<'a, D: BuilderData> {
|
||||||
cells: Vec<Cell>,
|
cells: Vec<Cell>,
|
||||||
backtrace: Vec<usize>,
|
backtrace: Vec<usize>,
|
||||||
current: usize,
|
current: usize,
|
||||||
rng : &'a mut StdRng,
|
rng: &'a mut StdRng,
|
||||||
phantom: PhantomData<D>,
|
phantom: PhantomData<D>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, D: BuilderData> Grid<'a, D> {
|
impl<'a, D: BuilderData> Grid<'a, D> {
|
||||||
fn new(width: i32, height:i32, rng: &mut StdRng) -> Grid<D> {
|
fn new(width: i32, height: i32, rng: &mut StdRng) -> Grid<D> {
|
||||||
let mut grid = Grid{
|
let mut grid = Grid {
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
cells: Vec::new(),
|
cells: Vec::new(),
|
||||||
|
@ -129,7 +125,7 @@ impl<'a, D: BuilderData> Grid<'a, D> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn calculate_index(&self, row: i32, column: i32) -> i32 {
|
fn calculate_index(&self, row: i32, column: i32) -> i32 {
|
||||||
if row < 0 || column < 0 || column > self.width-1 || row > self.height-1 {
|
if row < 0 || column < 0 || column > self.width - 1 || row > self.height - 1 {
|
||||||
-1
|
-1
|
||||||
} else {
|
} else {
|
||||||
column + (row * self.width)
|
column + (row * self.width)
|
||||||
|
@ -137,16 +133,16 @@ impl<'a, D: BuilderData> Grid<'a, D> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_available_neighbors(&self) -> Vec<usize> {
|
fn get_available_neighbors(&self) -> Vec<usize> {
|
||||||
let mut neighbors : Vec<usize> = Vec::new();
|
let mut neighbors: Vec<usize> = Vec::new();
|
||||||
|
|
||||||
let current_row = self.cells[self.current].row;
|
let current_row = self.cells[self.current].row;
|
||||||
let current_column = self.cells[self.current].column;
|
let current_column = self.cells[self.current].column;
|
||||||
|
|
||||||
let neighbor_indices : [i32; 4] = [
|
let neighbor_indices: [i32; 4] = [
|
||||||
self.calculate_index(current_row -1, current_column),
|
self.calculate_index(current_row - 1, current_column),
|
||||||
self.calculate_index(current_row, current_column + 1),
|
self.calculate_index(current_row, current_column + 1),
|
||||||
self.calculate_index(current_row + 1, current_column),
|
self.calculate_index(current_row + 1, current_column),
|
||||||
self.calculate_index(current_row, current_column - 1)
|
self.calculate_index(current_row, current_column - 1),
|
||||||
];
|
];
|
||||||
|
|
||||||
for i in neighbor_indices.iter() {
|
for i in neighbor_indices.iter() {
|
||||||
|
@ -164,7 +160,7 @@ impl<'a, D: BuilderData> Grid<'a, D> {
|
||||||
if neighbors.len() == 1 {
|
if neighbors.len() == 1 {
|
||||||
return Some(neighbors[0]);
|
return Some(neighbors[0]);
|
||||||
} else {
|
} else {
|
||||||
return Some(neighbors[(self.rng.roll_dice(1, neighbors.len())-1) as usize]);
|
return Some(neighbors[(self.rng.roll_dice(1, neighbors.len()) - 1) as usize]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
|
@ -209,17 +205,27 @@ impl<'a, D: BuilderData> Grid<'a, D> {
|
||||||
|
|
||||||
fn copy_to_map(&self, map: &mut Map<D>) {
|
fn copy_to_map(&self, map: &mut Map<D>) {
|
||||||
// Clear the map
|
// Clear the map
|
||||||
for i in map.tiles.iter_mut() { *i = Tile::wall(); }
|
for i in map.tiles.iter_mut() {
|
||||||
|
*i = Tile::wall();
|
||||||
|
}
|
||||||
|
|
||||||
for cell in self.cells.iter() {
|
for cell in self.cells.iter() {
|
||||||
let x = (cell.column as usize + 1) * 2;
|
let x = (cell.column as usize + 1) * 2;
|
||||||
let y = (cell.row as usize + 1) * 2;
|
let y = (cell.row as usize + 1) * 2;
|
||||||
|
|
||||||
map.set_tile(x, y, Tile::floor());
|
map.set_tile(x, y, Tile::floor());
|
||||||
if !cell.walls[TOP] { map.set_tile(x, y-1, Tile::floor()) }
|
if !cell.walls[TOP] {
|
||||||
if !cell.walls[RIGHT] { map.set_tile(x+1, y, Tile::floor()) }
|
map.set_tile(x, y - 1, Tile::floor())
|
||||||
if !cell.walls[BOTTOM] { map.set_tile(x, y+1, Tile::floor()) }
|
}
|
||||||
if !cell.walls[LEFT] { map.set_tile(x-1, y, Tile::floor()) }
|
if !cell.walls[RIGHT] {
|
||||||
|
map.set_tile(x + 1, y, Tile::floor())
|
||||||
|
}
|
||||||
|
if !cell.walls[BOTTOM] {
|
||||||
|
map.set_tile(x, y + 1, Tile::floor())
|
||||||
|
}
|
||||||
|
if !cell.walls[LEFT] {
|
||||||
|
map.set_tile(x - 1, y, Tile::floor())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
//! Generators for dungeon type maps.
|
//! Generators for dungeon type maps.
|
||||||
//!
|
//!
|
||||||
|
|
||||||
pub mod bsp_interior;
|
pub mod bsp_interior;
|
||||||
pub mod bsp_rooms;
|
pub mod bsp_rooms;
|
||||||
|
@ -9,8 +9,8 @@ pub mod distant_exit;
|
||||||
pub mod drunkard;
|
pub mod drunkard;
|
||||||
pub mod maze;
|
pub mod maze;
|
||||||
pub mod noise_generator;
|
pub mod noise_generator;
|
||||||
pub mod simple_rooms;
|
|
||||||
pub mod rooms_corridors_nearest;
|
pub mod rooms_corridors_nearest;
|
||||||
|
pub mod simple_rooms;
|
||||||
pub mod starting_point;
|
pub mod starting_point;
|
||||||
pub mod voronoi;
|
pub mod voronoi;
|
||||||
|
|
||||||
|
@ -22,8 +22,7 @@ pub use distant_exit::DistantExit;
|
||||||
pub use drunkard::DrunkardsWalk;
|
pub use drunkard::DrunkardsWalk;
|
||||||
pub use maze::MazeBuilder;
|
pub use maze::MazeBuilder;
|
||||||
pub use noise_generator::NoiseGenerator;
|
pub use noise_generator::NoiseGenerator;
|
||||||
pub use simple_rooms::SimpleRooms;
|
|
||||||
pub use rooms_corridors_nearest::NearestCorridors;
|
pub use rooms_corridors_nearest::NearestCorridors;
|
||||||
|
pub use simple_rooms::SimpleRooms;
|
||||||
pub use starting_point::{AreaStartingPosition, XStart, YStart};
|
pub use starting_point::{AreaStartingPosition, XStart, YStart};
|
||||||
pub use voronoi::VoronoiHive;
|
pub use voronoi::VoronoiHive;
|
||||||
|
|
||||||
|
|
|
@ -1,27 +1,26 @@
|
||||||
//! Apply noise to the map.
|
//! Apply noise to the map.
|
||||||
//! Each cell will be set to Floor with the given probabilty.
|
//! Each cell will be set to Floor with the given probabilty.
|
||||||
//!
|
//!
|
||||||
//! Example usage:
|
//! Example usage:
|
||||||
//! ```
|
//! ```
|
||||||
//! use rand::prelude::*;
|
//! use rand::prelude::*;
|
||||||
//! use mapgen::{Map, MapFilter, NoData};
|
//! use mapgen::{Map, MapFilter, NoData};
|
||||||
//! use mapgen::filter::NoiseGenerator;
|
//! use mapgen::filter::NoiseGenerator;
|
||||||
//!
|
//!
|
||||||
//! let mut rng = StdRng::seed_from_u64(100);
|
//! let mut rng = StdRng::seed_from_u64(100);
|
||||||
//! let gen = NoiseGenerator::<NoData>::uniform();
|
//! let gen = NoiseGenerator::<NoData>::uniform();
|
||||||
//! let map = gen.modify_map(&mut rng, &Map::new(80, 50));
|
//! 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);
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
|
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
use rand::prelude::*;
|
|
||||||
use crate::MapFilter;
|
use crate::MapFilter;
|
||||||
use crate::{BuilderData, Map, Tile};
|
use crate::{BuilderData, Map, Tile};
|
||||||
|
use rand::prelude::*;
|
||||||
|
|
||||||
/// Map noise generator
|
/// Map noise generator
|
||||||
pub struct NoiseGenerator<D: BuilderData> {
|
pub struct NoiseGenerator<D: BuilderData> {
|
||||||
|
@ -30,7 +29,7 @@ pub struct NoiseGenerator<D: BuilderData> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<D: BuilderData> MapFilter<D> for NoiseGenerator<D> {
|
impl<D: BuilderData> MapFilter<D> for NoiseGenerator<D> {
|
||||||
fn modify_map(&self, rng: &mut StdRng, map: &Map<D>) -> Map<D> {
|
fn modify_map(&self, rng: &mut StdRng, map: &Map<D>) -> Map<D> {
|
||||||
self.build(map, rng)
|
self.build(map, rng)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -56,15 +55,17 @@ impl<D: BuilderData> NoiseGenerator<D> {
|
||||||
fn build(&self, map: &Map<D>, rng: &mut StdRng) -> Map<D> {
|
fn build(&self, map: &Map<D>, rng: &mut StdRng) -> Map<D> {
|
||||||
let mut new_map = map.clone();
|
let mut new_map = map.clone();
|
||||||
let p = (self.prob * 100.0) as u32;
|
let p = (self.prob * 100.0) as u32;
|
||||||
for y in 1..new_map.height-1 {
|
for y in 1..new_map.height - 1 {
|
||||||
for x in 1..new_map.width-1 {
|
for x in 1..new_map.width - 1 {
|
||||||
let roll = rng.next_u32() % 100;
|
let roll = rng.next_u32() % 100;
|
||||||
if roll > p { new_map.set_tile(x, y, Tile::floor()) }
|
if roll > p {
|
||||||
else { new_map.set_tile(x, y, Tile::wall()) }
|
new_map.set_tile(x, y, Tile::floor())
|
||||||
|
} else {
|
||||||
|
new_map.set_tile(x, y, Tile::wall())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
new_map
|
new_map
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,25 +1,23 @@
|
||||||
//! Connect nearest rooms on the map with corridors
|
//! Connect nearest rooms on the map with corridors
|
||||||
//!
|
//!
|
||||||
use rand::prelude::StdRng;
|
|
||||||
use crate::BuilderData;
|
use crate::BuilderData;
|
||||||
use crate::MapFilter;
|
|
||||||
use crate::Map;
|
use crate::Map;
|
||||||
|
use crate::MapFilter;
|
||||||
|
use rand::prelude::StdRng;
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
|
|
||||||
pub struct NearestCorridors<D: BuilderData> {
|
pub struct NearestCorridors<D: BuilderData> {
|
||||||
phantom: PhantomData<D>,
|
phantom: PhantomData<D>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<D: BuilderData> MapFilter<D> for NearestCorridors<D> {
|
impl<D: BuilderData> MapFilter<D> for NearestCorridors<D> {
|
||||||
fn modify_map(&self, _: &mut StdRng, map: &Map<D>) -> Map<D> {
|
fn modify_map(&self, _: &mut StdRng, map: &Map<D>) -> Map<D> {
|
||||||
self.corridors(map)
|
self.corridors(map)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<D: BuilderData> NearestCorridors<D> {
|
impl<D: BuilderData> NearestCorridors<D> {
|
||||||
|
|
||||||
pub fn new() -> Box<NearestCorridors<D>> {
|
pub fn new() -> Box<NearestCorridors<D>> {
|
||||||
Box::new(NearestCorridors {
|
Box::new(NearestCorridors {
|
||||||
phantom: PhantomData,
|
phantom: PhantomData,
|
||||||
|
@ -29,11 +27,11 @@ impl<D: BuilderData> NearestCorridors<D> {
|
||||||
fn corridors(&self, map: &Map<D>) -> Map<D> {
|
fn corridors(&self, map: &Map<D>) -> Map<D> {
|
||||||
let mut new_map = map.clone();
|
let mut new_map = map.clone();
|
||||||
|
|
||||||
let mut connected : HashSet<usize> = HashSet::new();
|
let mut connected: HashSet<usize> = HashSet::new();
|
||||||
for (i,room) in map.rooms.iter().enumerate() {
|
for (i, room) in map.rooms.iter().enumerate() {
|
||||||
let mut room_distance : Vec<(usize, f32)> = Vec::new();
|
let mut room_distance: Vec<(usize, f32)> = Vec::new();
|
||||||
let room_center = room.center();
|
let room_center = room.center();
|
||||||
for (j,other_room) in new_map.rooms.iter().enumerate() {
|
for (j, other_room) in new_map.rooms.iter().enumerate() {
|
||||||
if i != j && !connected.contains(&j) {
|
if i != j && !connected.contains(&j) {
|
||||||
let other_center = other_room.center();
|
let other_center = other_room.center();
|
||||||
let distance = room_center.distance_to(&other_center);
|
let distance = room_center.distance_to(&other_center);
|
||||||
|
@ -42,7 +40,7 @@ impl<D: BuilderData> NearestCorridors<D> {
|
||||||
}
|
}
|
||||||
|
|
||||||
if !room_distance.is_empty() {
|
if !room_distance.is_empty() {
|
||||||
room_distance.sort_by(|a,b| a.1.partial_cmp(&b.1).unwrap() );
|
room_distance.sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap());
|
||||||
let dest_center = new_map.rooms[room_distance[0].0].center();
|
let dest_center = new_map.rooms[room_distance[0].0].center();
|
||||||
new_map.add_corridor(room_center, dest_center);
|
new_map.add_corridor(room_center, dest_center);
|
||||||
connected.insert(i);
|
connected.insert(i);
|
||||||
|
@ -50,4 +48,4 @@ impl<D: BuilderData> NearestCorridors<D> {
|
||||||
}
|
}
|
||||||
new_map
|
new_map
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,32 +1,31 @@
|
||||||
//! Random rooms map generator.
|
//! Random rooms map generator.
|
||||||
//!
|
//!
|
||||||
//! Try to generate rooms of different size to fill the map area.
|
//! Try to generate rooms of different size to fill the map area.
|
||||||
//! Rooms will not overlap.
|
//! Rooms will not overlap.
|
||||||
//!
|
//!
|
||||||
//! Example generator usage:
|
//! Example generator usage:
|
||||||
//! ```
|
//! ```
|
||||||
//! use rand::prelude::*;
|
//! use rand::prelude::*;
|
||||||
//! use mapgen::{MapFilter, Map, NoData};
|
//! use mapgen::{MapFilter, Map, NoData};
|
||||||
//! use mapgen::filter::SimpleRooms;
|
//! use mapgen::filter::SimpleRooms;
|
||||||
//!
|
//!
|
||||||
//! let mut rng = StdRng::seed_from_u64(100);
|
//! let mut rng = StdRng::seed_from_u64(100);
|
||||||
//! let gen = SimpleRooms::<NoData>::new();
|
//! let gen = SimpleRooms::<NoData>::new();
|
||||||
//! let map = gen.modify_map(&mut rng, &Map::new(80, 50));
|
//! 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);
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
|
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
use rand::prelude::*;
|
|
||||||
use crate::BuilderData;
|
|
||||||
use crate::MapFilter;
|
|
||||||
use crate::geometry::Rect;
|
use crate::geometry::Rect;
|
||||||
use crate::random::Rng;
|
use crate::random::Rng;
|
||||||
|
use crate::BuilderData;
|
||||||
use crate::Map;
|
use crate::Map;
|
||||||
|
use crate::MapFilter;
|
||||||
|
use rand::prelude::*;
|
||||||
|
|
||||||
pub struct SimpleRooms<D: BuilderData> {
|
pub struct SimpleRooms<D: BuilderData> {
|
||||||
max_rooms: usize,
|
max_rooms: usize,
|
||||||
|
@ -36,15 +35,14 @@ pub struct SimpleRooms<D: BuilderData> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<D: BuilderData> MapFilter<D> for SimpleRooms<D> {
|
impl<D: BuilderData> MapFilter<D> for SimpleRooms<D> {
|
||||||
fn modify_map(&self, rng: &mut StdRng, map: &Map<D>) -> Map<D> {
|
fn modify_map(&self, rng: &mut StdRng, map: &Map<D>) -> Map<D> {
|
||||||
self.build_rooms(map, rng)
|
self.build_rooms(map, rng)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
impl<D: BuilderData> SimpleRooms<D> {
|
impl<D: BuilderData> SimpleRooms<D> {
|
||||||
pub fn new() -> Box<SimpleRooms<D>> {
|
pub fn new() -> Box<SimpleRooms<D>> {
|
||||||
Box::new(SimpleRooms{
|
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,
|
||||||
|
@ -52,7 +50,7 @@ impl<D: BuilderData> SimpleRooms<D> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_rooms(&self, map: &Map<D>, rng : &mut StdRng) -> Map<D> {
|
fn build_rooms(&self, map: &Map<D>, rng: &mut StdRng) -> Map<D> {
|
||||||
let mut new_map = map.clone();
|
let mut new_map = map.clone();
|
||||||
|
|
||||||
// Create room dimensions
|
// Create room dimensions
|
||||||
|
@ -67,7 +65,7 @@ impl<D: BuilderData> SimpleRooms<D> {
|
||||||
new_map.add_room(new_room);
|
new_map.add_room(new_room);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
new_map
|
new_map
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,92 +1,98 @@
|
||||||
//! Add starting point to the map
|
//! Add starting point to the map
|
||||||
//!
|
//!
|
||||||
//! This modifier will try to add starting point by finding the floor title closes
|
//! This modifier will try to add starting point by finding the floor title closes
|
||||||
//! to the given point.
|
//! to the given point.
|
||||||
//!
|
//!
|
||||||
//! Example modifier usage:
|
//! Example modifier usage:
|
||||||
//! ```
|
//! ```
|
||||||
//! use rand::prelude::*;
|
//! use rand::prelude::*;
|
||||||
//! use mapgen::{MapFilter, Map, NoData, Tile};
|
//! use mapgen::{MapFilter, Map, NoData, Tile};
|
||||||
//! use mapgen::filter::starting_point::{AreaStartingPosition, XStart, YStart};
|
//! use mapgen::filter::starting_point::{AreaStartingPosition, XStart, YStart};
|
||||||
//! use mapgen::geometry::Point;
|
//! use mapgen::geometry::Point;
|
||||||
//!
|
//!
|
||||||
//! let mut rng = StdRng::seed_from_u64(100);
|
//! let mut rng = StdRng::seed_from_u64(100);
|
||||||
//! let mut map = Map::<NoData>::new(80, 50);
|
//! let mut map = Map::<NoData>::new(80, 50);
|
||||||
//! map.set_tile(10, 10, Tile::floor());
|
//! map.set_tile(10, 10, Tile::floor());
|
||||||
//! let modifier = AreaStartingPosition::new(XStart::LEFT, YStart::TOP);
|
//! let modifier = AreaStartingPosition::new(XStart::LEFT, YStart::TOP);
|
||||||
//! let new_map = modifier.modify_map(&mut rng, &map);
|
//! let new_map = modifier.modify_map(&mut rng, &map);
|
||||||
//!
|
//!
|
||||||
//! assert_eq!(new_map.starting_point, Some(Point::new(10, 10)));
|
//! assert_eq!(new_map.starting_point, Some(Point::new(10, 10)));
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
|
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
use rand::prelude::StdRng;
|
|
||||||
use crate::BuilderData;
|
|
||||||
use crate::MapFilter;
|
|
||||||
use crate::geometry::Point;
|
use crate::geometry::Point;
|
||||||
|
use crate::BuilderData;
|
||||||
use crate::Map;
|
use crate::Map;
|
||||||
|
use crate::MapFilter;
|
||||||
|
use rand::prelude::StdRng;
|
||||||
|
|
||||||
/// Initial x region position
|
/// Initial x region position
|
||||||
pub enum XStart { LEFT, CENTER, RIGHT }
|
pub enum XStart {
|
||||||
|
LEFT,
|
||||||
|
CENTER,
|
||||||
|
RIGHT,
|
||||||
|
}
|
||||||
|
|
||||||
/// Initial y region position
|
/// Initial y region position
|
||||||
pub enum YStart { TOP, CENTER, BOTTOM }
|
pub enum YStart {
|
||||||
|
TOP,
|
||||||
|
CENTER,
|
||||||
|
BOTTOM,
|
||||||
|
}
|
||||||
|
|
||||||
/// Add starting position to the map
|
/// Add starting position to the map
|
||||||
pub struct AreaStartingPosition<D: BuilderData> {
|
pub struct AreaStartingPosition<D: BuilderData> {
|
||||||
x : XStart,
|
x: XStart,
|
||||||
y : YStart,
|
y: YStart,
|
||||||
phantom: PhantomData<D>,
|
phantom: PhantomData<D>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<D: BuilderData> MapFilter<D> for AreaStartingPosition<D> {
|
impl<D: BuilderData> MapFilter<D> for AreaStartingPosition<D> {
|
||||||
fn modify_map(&self, _: &mut StdRng, map: &Map<D>) -> Map<D> {
|
fn modify_map(&self, _: &mut StdRng, map: &Map<D>) -> Map<D> {
|
||||||
self.build(map)
|
self.build(map)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<D: BuilderData> AreaStartingPosition<D> {
|
impl<D: BuilderData> AreaStartingPosition<D> {
|
||||||
/// Create new modifier with given region
|
/// Create new modifier with given region
|
||||||
pub fn new(x : XStart, y : YStart) -> Box<AreaStartingPosition<D>> {
|
pub fn new(x: XStart, y: YStart) -> Box<AreaStartingPosition<D>> {
|
||||||
Box::new(AreaStartingPosition{
|
Box::new(AreaStartingPosition {
|
||||||
x, y,
|
x,
|
||||||
|
y,
|
||||||
phantom: PhantomData,
|
phantom: PhantomData,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build(&self, map : &Map<D>) -> Map<D> {
|
fn build(&self, map: &Map<D>) -> Map<D> {
|
||||||
let seed_x = match self.x {
|
let seed_x = match self.x {
|
||||||
XStart::LEFT => 1,
|
XStart::LEFT => 1,
|
||||||
XStart::CENTER => map.width / 2,
|
XStart::CENTER => map.width / 2,
|
||||||
XStart::RIGHT => map.width - 2
|
XStart::RIGHT => map.width - 2,
|
||||||
};
|
};
|
||||||
|
|
||||||
let seed_y = match self.y {
|
let seed_y = match self.y {
|
||||||
YStart::TOP => 1,
|
YStart::TOP => 1,
|
||||||
YStart::CENTER => map.height / 2,
|
YStart::CENTER => map.height / 2,
|
||||||
YStart::BOTTOM => map.height - 2
|
YStart::BOTTOM => map.height - 2,
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut available_floors : Vec<(usize, f32)> = Vec::new();
|
let mut available_floors: Vec<(usize, f32)> = Vec::new();
|
||||||
for (idx, tiletype) in map.tiles.iter().enumerate() {
|
for (idx, tiletype) in map.tiles.iter().enumerate() {
|
||||||
if tiletype.is_walkable() {
|
if tiletype.is_walkable() {
|
||||||
available_floors.push(
|
available_floors.push((
|
||||||
(
|
idx,
|
||||||
idx,
|
Point::new(idx % map.width, idx / map.width)
|
||||||
Point::new(idx % map.width, idx / map.width)
|
.distance_to(&Point::new(seed_x, seed_y)),
|
||||||
.distance_to(&Point::new(seed_x, seed_y))
|
));
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if available_floors.is_empty() {
|
if available_floors.is_empty() {
|
||||||
panic!("No valid floors to start on");
|
panic!("No valid floors to start on");
|
||||||
}
|
}
|
||||||
|
|
||||||
available_floors.sort_by(|a,b| a.1.partial_cmp(&b.1).unwrap());
|
available_floors.sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap());
|
||||||
|
|
||||||
let start_x = available_floors[0].0 % map.width;
|
let start_x = available_floors[0].0 % map.width;
|
||||||
let start_y = available_floors[0].0 / map.width;
|
let start_y = available_floors[0].0 / map.width;
|
||||||
|
@ -102,11 +108,11 @@ impl<D: BuilderData> AreaStartingPosition<D> {
|
||||||
/// ------------------------------------------------------------------------------------------------
|
/// ------------------------------------------------------------------------------------------------
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use rand::prelude::*;
|
|
||||||
use super::*;
|
|
||||||
use super::MapFilter;
|
use super::MapFilter;
|
||||||
|
use super::*;
|
||||||
use crate::geometry::Point;
|
use crate::geometry::Point;
|
||||||
use crate::map::{Map, NoData};
|
use crate::map::{Map, NoData};
|
||||||
|
use rand::prelude::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_exit() {
|
fn test_exit() {
|
||||||
|
@ -125,4 +131,4 @@ mod tests {
|
||||||
|
|
||||||
assert_eq!(new_map.starting_point, Some(Point::new(6, 1)));
|
assert_eq!(new_map.starting_point, Some(Point::new(6, 1)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,42 +3,40 @@
|
||||||
//! use rand::prelude::*;
|
//! use rand::prelude::*;
|
||||||
//! use mapgen::{Map, MapFilter, NoData};
|
//! use mapgen::{Map, MapFilter, NoData};
|
||||||
//! use mapgen::filter::VoronoiHive;
|
//! use mapgen::filter::VoronoiHive;
|
||||||
//!
|
//!
|
||||||
//! let mut rng = StdRng::seed_from_u64(100);
|
//! let mut rng = StdRng::seed_from_u64(100);
|
||||||
//! let gen = VoronoiHive::<NoData>::new();
|
//! let gen = VoronoiHive::<NoData>::new();
|
||||||
//! let map = gen.modify_map(&mut rng, &Map::new(80, 50));
|
//! 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);
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
|
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
use rand::prelude::*;
|
|
||||||
use crate::MapFilter;
|
use crate::MapFilter;
|
||||||
use crate::{
|
use crate::{
|
||||||
|
geometry::Point,
|
||||||
map::{BuilderData, Map, Tile},
|
map::{BuilderData, Map, Tile},
|
||||||
random::Rng,
|
random::Rng,
|
||||||
geometry::Point,
|
|
||||||
};
|
};
|
||||||
|
use rand::prelude::*;
|
||||||
|
|
||||||
pub struct VoronoiHive<D: BuilderData> {
|
pub struct VoronoiHive<D: BuilderData> {
|
||||||
n_seeds: usize,
|
n_seeds: usize,
|
||||||
phantom: PhantomData<D>,
|
phantom: PhantomData<D>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
impl<D: BuilderData> MapFilter<D> for VoronoiHive<D> {
|
impl<D: BuilderData> MapFilter<D> for VoronoiHive<D> {
|
||||||
fn modify_map(&self, rng: &mut StdRng, map: &Map<D>) -> Map<D> {
|
fn modify_map(&self, rng: &mut StdRng, map: &Map<D>) -> Map<D> {
|
||||||
self.build(rng, map)
|
self.build(rng, map)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<D: BuilderData> VoronoiHive<D> {
|
impl<D: BuilderData> VoronoiHive<D> {
|
||||||
pub fn new() -> Box<VoronoiHive<D>> {
|
pub fn new() -> Box<VoronoiHive<D>> {
|
||||||
Box::new(VoronoiHive{
|
Box::new(VoronoiHive {
|
||||||
n_seeds: 64,
|
n_seeds: 64,
|
||||||
phantom: PhantomData,
|
phantom: PhantomData,
|
||||||
})
|
})
|
||||||
|
@ -48,8 +46,8 @@ impl<D: BuilderData> VoronoiHive<D> {
|
||||||
let mut new_map = map.clone();
|
let mut new_map = map.clone();
|
||||||
let seeds = self.generate_seeds(rng, map.width, map.height);
|
let seeds = self.generate_seeds(rng, map.width, map.height);
|
||||||
|
|
||||||
let mut voronoi_distance = vec![(0, 0.0f32) ; self.n_seeds];
|
let mut voronoi_distance = vec![(0, 0.0f32); self.n_seeds];
|
||||||
let mut voronoi_membership : Vec<i32> = vec![0 ; map.width as usize * map.height as usize];
|
let mut voronoi_membership: Vec<i32> = vec![0; map.width as usize * map.height as usize];
|
||||||
for (i, vid) in voronoi_membership.iter_mut().enumerate() {
|
for (i, vid) in voronoi_membership.iter_mut().enumerate() {
|
||||||
let x = i % map.width;
|
let x = i % map.width;
|
||||||
let y = i / map.width;
|
let y = i / map.width;
|
||||||
|
@ -59,20 +57,28 @@ impl<D: BuilderData> VoronoiHive<D> {
|
||||||
voronoi_distance[seed] = (seed, distance);
|
voronoi_distance[seed] = (seed, distance);
|
||||||
}
|
}
|
||||||
|
|
||||||
voronoi_distance.sort_by(|a,b| a.1.partial_cmp(&b.1).unwrap());
|
voronoi_distance.sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap());
|
||||||
|
|
||||||
*vid = voronoi_distance[0].0 as i32;
|
*vid = voronoi_distance[0].0 as i32;
|
||||||
}
|
}
|
||||||
|
|
||||||
for y in 1..new_map.height-1 {
|
for y in 1..new_map.height - 1 {
|
||||||
for x in 1..new_map.width-1 {
|
for x in 1..new_map.width - 1 {
|
||||||
let mut neighbors = 0;
|
let mut neighbors = 0;
|
||||||
let my_idx = new_map.xy_idx(x, y);
|
let my_idx = new_map.xy_idx(x, y);
|
||||||
let my_seed = voronoi_membership[my_idx];
|
let my_seed = voronoi_membership[my_idx];
|
||||||
if voronoi_membership[new_map.xy_idx(x-1, y)] != my_seed { neighbors += 1; }
|
if voronoi_membership[new_map.xy_idx(x - 1, y)] != my_seed {
|
||||||
if voronoi_membership[new_map.xy_idx(x+1, y)] != my_seed { neighbors += 1; }
|
neighbors += 1;
|
||||||
if voronoi_membership[new_map.xy_idx(x, y-1)] != my_seed { neighbors += 1; }
|
}
|
||||||
if voronoi_membership[new_map.xy_idx(x, y+1)] != my_seed { neighbors += 1; }
|
if voronoi_membership[new_map.xy_idx(x + 1, y)] != my_seed {
|
||||||
|
neighbors += 1;
|
||||||
|
}
|
||||||
|
if voronoi_membership[new_map.xy_idx(x, y - 1)] != my_seed {
|
||||||
|
neighbors += 1;
|
||||||
|
}
|
||||||
|
if voronoi_membership[new_map.xy_idx(x, y + 1)] != my_seed {
|
||||||
|
neighbors += 1;
|
||||||
|
}
|
||||||
|
|
||||||
if neighbors < 2 {
|
if neighbors < 2 {
|
||||||
new_map.set_tile(x, y, Tile::floor());
|
new_map.set_tile(x, y, Tile::floor());
|
||||||
|
@ -81,15 +87,15 @@ impl<D: BuilderData> VoronoiHive<D> {
|
||||||
}
|
}
|
||||||
|
|
||||||
new_map
|
new_map
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generate random seeds
|
/// Generate random seeds
|
||||||
fn generate_seeds(&self, rng: &mut StdRng, width: usize, height: usize) -> Vec<Point> {
|
fn generate_seeds(&self, rng: &mut StdRng, width: usize, height: usize) -> Vec<Point> {
|
||||||
let mut seeds: Vec<Point> = Vec::new();
|
let mut seeds: Vec<Point> = Vec::new();
|
||||||
|
|
||||||
while seeds.len() < self.n_seeds {
|
while seeds.len() < self.n_seeds {
|
||||||
let vx = rng.roll_dice(1, width-1);
|
let vx = rng.roll_dice(1, width - 1);
|
||||||
let vy = rng.roll_dice(1, height-1);
|
let vy = rng.roll_dice(1, height - 1);
|
||||||
let candidate = Point::new(vx, vy);
|
let candidate = Point::new(vx, vy);
|
||||||
if !seeds.contains(&candidate) {
|
if !seeds.contains(&candidate) {
|
||||||
seeds.push(candidate);
|
seeds.push(candidate);
|
||||||
|
@ -98,5 +104,4 @@ impl<D: BuilderData> VoronoiHive<D> {
|
||||||
|
|
||||||
seeds
|
seeds
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
|
||||||
|
|
|
@ -1,17 +1,17 @@
|
||||||
//! Support function for 2D geometry
|
//! Support function for 2D geometry
|
||||||
//!
|
//!
|
||||||
|
|
||||||
/// Position on the map
|
/// Position on the map
|
||||||
#[derive(Default, PartialEq, Copy, Clone, Debug, Eq, Hash)]
|
#[derive(Default, PartialEq, Copy, Clone, Debug, Eq, Hash)]
|
||||||
pub struct Point {
|
pub struct Point {
|
||||||
pub x: usize,
|
pub x: usize,
|
||||||
pub y: usize
|
pub y: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Point {
|
impl Point {
|
||||||
/// Create new point
|
/// Create new point
|
||||||
pub fn new(x: usize, y: usize) -> Point {
|
pub fn new(x: usize, y: usize) -> Point {
|
||||||
Point {x, y}
|
Point { x, y }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create new point from i32 coords
|
/// Create new point from i32 coords
|
||||||
|
@ -30,28 +30,33 @@ impl Point {
|
||||||
/// Rectangle region on the map
|
/// Rectangle region on the map
|
||||||
#[derive(PartialEq, Copy, Clone, Debug)]
|
#[derive(PartialEq, Copy, Clone, Debug)]
|
||||||
pub struct Rect {
|
pub struct Rect {
|
||||||
pub x1 : usize,
|
pub x1: usize,
|
||||||
pub x2 : usize,
|
pub x2: usize,
|
||||||
pub y1 : usize,
|
pub y1: usize,
|
||||||
pub y2 : usize
|
pub y2: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Rect {
|
impl Rect {
|
||||||
pub fn new(x: usize, y: usize, width: usize, height: usize) -> Rect {
|
pub fn new(x: usize, y: usize, width: usize, height: usize) -> Rect {
|
||||||
Rect{x1:x, y1:y, x2:x+width, y2:y+height}
|
Rect {
|
||||||
|
x1: x,
|
||||||
|
y1: y,
|
||||||
|
x2: x + width,
|
||||||
|
y2: y + height,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_i32(x:i32, y: i32, width:i32, height:i32) -> Rect {
|
pub fn new_i32(x: i32, y: i32, width: i32, height: i32) -> Rect {
|
||||||
Rect::new(x as usize, y as usize, width as usize, height as usize)
|
Rect::new(x as usize, y as usize, width as usize, height as usize)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns true if this overlaps with other
|
/// Returns true if this overlaps with other
|
||||||
pub fn intersect(&self, other:&Rect) -> bool {
|
pub fn intersect(&self, other: &Rect) -> bool {
|
||||||
self.x1 <= other.x2 && self.x2 >= other.x1 && self.y1 <= other.y2 && self.y2 >= other.y1
|
self.x1 <= other.x2 && self.x2 >= other.x1 && self.y1 <= other.y2 && self.y2 >= other.y1
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn center(&self) -> Point {
|
pub fn center(&self) -> Point {
|
||||||
Point::new((self.x1 + self.x2)/2, (self.y1 + self.y2)/2)
|
Point::new((self.x1 + self.x2) / 2, (self.y1 + self.y2) / 2)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn width(&self) -> usize {
|
pub fn width(&self) -> usize {
|
||||||
|
@ -75,12 +80,16 @@ impl Rect {
|
||||||
/// Example:
|
/// Example:
|
||||||
/// ```
|
/// ```
|
||||||
/// use mapgen::geometry::usize_abs;
|
/// use mapgen::geometry::usize_abs;
|
||||||
///
|
///
|
||||||
/// assert_eq!(usize_abs(5, 3), 2);
|
/// assert_eq!(usize_abs(5, 3), 2);
|
||||||
/// assert_eq!(usize_abs(3, 5), 2);
|
/// assert_eq!(usize_abs(3, 5), 2);
|
||||||
/// ```
|
/// ```
|
||||||
pub fn usize_abs(x: usize, y: usize) -> usize {
|
pub fn usize_abs(x: usize, y: usize) -> usize {
|
||||||
if x >= y {x - y} else {y - x}
|
if x >= y {
|
||||||
|
x - y
|
||||||
|
} else {
|
||||||
|
y - x
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// ------------------------------------------------------------------------------------------------
|
/// ------------------------------------------------------------------------------------------------
|
||||||
|
@ -111,5 +120,4 @@ mod tests {
|
||||||
assert_eq!(rect1.width(), 40);
|
assert_eq!(rect1.width(), 40);
|
||||||
assert_eq!(rect1.height(), 30);
|
assert_eq!(rect1.height(), 30);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
|
||||||
|
|
41
src/lib.rs
41
src/lib.rs
|
@ -1,11 +1,11 @@
|
||||||
//! Generators for dungeon type maps.
|
//! Generators for dungeon type maps.
|
||||||
//!
|
//!
|
||||||
//! Generators can bu used directly or they can be combined with
|
//! Generators can bu used directly or they can be combined with
|
||||||
//! `MapGenerator`s and `MapModifier`s
|
//! `MapGenerator`s and `MapModifier`s
|
||||||
//!
|
//!
|
||||||
//! * MapGenerators are use to create initial map.
|
//! * MapGenerators are use to create initial map.
|
||||||
//! * MapModifiers modify existing map.
|
//! * MapModifiers modify existing map.
|
||||||
//!
|
//!
|
||||||
//! Example
|
//! Example
|
||||||
//! ```
|
//! ```
|
||||||
//! use mapgen::{MapFilter, MapBuilder, Map, NoData, Tile};
|
//! use mapgen::{MapFilter, MapBuilder, Map, NoData, Tile};
|
||||||
|
@ -15,35 +15,34 @@
|
||||||
//! starting_point::{AreaStartingPosition, XStart, YStart}
|
//! starting_point::{AreaStartingPosition, XStart, YStart}
|
||||||
//! };
|
//! };
|
||||||
//! use mapgen::geometry::Point;
|
//! use mapgen::geometry::Point;
|
||||||
//!
|
//!
|
||||||
//! let map = MapBuilder::<NoData>::new(80, 50)
|
//! let map = MapBuilder::<NoData>::new(80, 50)
|
||||||
//! .with(NoiseGenerator::uniform())
|
//! .with(NoiseGenerator::uniform())
|
||||||
//! .with(CellularAutomata::new())
|
//! .with(CellularAutomata::new())
|
||||||
//! .with(AreaStartingPosition::new(XStart::CENTER, YStart::CENTER))
|
//! .with(AreaStartingPosition::new(XStart::CENTER, YStart::CENTER))
|
||||||
//! .build();
|
//! .build();
|
||||||
//!
|
//!
|
||||||
//! assert_eq!(map.width, 80);
|
//! assert_eq!(map.width, 80);
|
||||||
//! assert_eq!(map.height, 50);
|
//! assert_eq!(map.height, 50);
|
||||||
//! assert_eq!(map.starting_point.is_some(), true);
|
//! assert_eq!(map.starting_point.is_some(), true);
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
|
|
||||||
pub mod filter;
|
pub mod filter;
|
||||||
pub mod geometry;
|
pub mod geometry;
|
||||||
pub mod map;
|
pub mod map;
|
||||||
pub mod metric;
|
pub mod metric;
|
||||||
|
|
||||||
pub use map::{BuilderData, Map, NoData, Symmetry, Tile};
|
|
||||||
pub use filter::*;
|
pub use filter::*;
|
||||||
|
pub use map::{BuilderData, Map, NoData, Symmetry, Tile};
|
||||||
|
|
||||||
pub (crate) mod dijkstra;
|
pub(crate) mod dijkstra;
|
||||||
pub (crate) mod random;
|
pub(crate) mod random;
|
||||||
|
|
||||||
use std::time::{SystemTime, UNIX_EPOCH};
|
|
||||||
use rand::prelude::*;
|
use rand::prelude::*;
|
||||||
|
use std::time::{SystemTime, UNIX_EPOCH};
|
||||||
|
|
||||||
|
/// 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 MapFilter<D: BuilderData> {
|
pub trait MapFilter<D: BuilderData> {
|
||||||
fn modify_map(&self, rng: &mut StdRng, map: &Map<D>) -> Map<D>;
|
fn modify_map(&self, rng: &mut StdRng, map: &Map<D>) -> Map<D>;
|
||||||
|
@ -59,21 +58,23 @@ pub struct MapBuilder<D> {
|
||||||
impl<D: BuilderData> MapBuilder<D> {
|
impl<D: BuilderData> MapBuilder<D> {
|
||||||
/// Create Map Builder with initial map generator
|
/// Create Map Builder with initial map generator
|
||||||
pub fn new(width: usize, height: usize) -> MapBuilder<D> {
|
pub fn new(width: usize, height: usize) -> MapBuilder<D> {
|
||||||
MapBuilder {
|
MapBuilder {
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
modifiers: Vec::new(),
|
modifiers: Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn with(&mut self, modifier : Box<dyn MapFilter<D>>) -> &mut MapBuilder<D> {
|
pub fn with(&mut self, modifier: Box<dyn MapFilter<D>>) -> &mut MapBuilder<D> {
|
||||||
self.modifiers.push(modifier);
|
self.modifiers.push(modifier);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Build map using random number seeded with system time
|
/// Build map using random number seeded with system time
|
||||||
pub fn build(&mut self) -> Map<D> {
|
pub fn build(&mut self) -> Map<D> {
|
||||||
let system_time = SystemTime::now().duration_since(UNIX_EPOCH).expect("Can't access system time");
|
let system_time = SystemTime::now()
|
||||||
|
.duration_since(UNIX_EPOCH)
|
||||||
|
.expect("Can't access system time");
|
||||||
let mut rng = StdRng::seed_from_u64(system_time.as_millis() as u64);
|
let mut rng = StdRng::seed_from_u64(system_time.as_millis() as u64);
|
||||||
self.build_with_rng(&mut rng)
|
self.build_with_rng(&mut rng)
|
||||||
}
|
}
|
||||||
|
@ -81,7 +82,7 @@ impl<D: BuilderData> MapBuilder<D> {
|
||||||
/// Build map using provided random number generator
|
/// Build map using provided random number generator
|
||||||
pub fn build_with_rng(&mut self, rng: &mut StdRng) -> Map<D> {
|
pub fn build_with_rng(&mut self, rng: &mut StdRng) -> Map<D> {
|
||||||
let mut map = Map::new(self.width, self.height);
|
let mut map = Map::new(self.width, self.height);
|
||||||
|
|
||||||
// Build additional layers in turn
|
// Build additional layers in turn
|
||||||
for modifier in self.modifiers.iter() {
|
for modifier in self.modifiers.iter() {
|
||||||
map = modifier.modify_map(rng, &map);
|
map = modifier.modify_map(rng, &map);
|
||||||
|
@ -89,7 +90,6 @@ impl<D: BuilderData> MapBuilder<D> {
|
||||||
|
|
||||||
map
|
map
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// ------------------------------------------------------------------------------------------------
|
/// ------------------------------------------------------------------------------------------------
|
||||||
|
@ -101,9 +101,7 @@ mod tests {
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use filter::{
|
use filter::{
|
||||||
CellularAutomata,
|
CellularAutomata, NoiseGenerator, {AreaStartingPosition, XStart, YStart},
|
||||||
NoiseGenerator,
|
|
||||||
{AreaStartingPosition, XStart, YStart},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -118,5 +116,4 @@ mod tests {
|
||||||
assert_eq!(map.height, 50);
|
assert_eq!(map.height, 50);
|
||||||
assert_eq!(map.starting_point.is_some(), true);
|
assert_eq!(map.starting_point.is_some(), true);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
|
||||||
|
|
109
src/map.rs
109
src/map.rs
|
@ -1,15 +1,14 @@
|
||||||
//! Map structure contains information about tiles and other elements on the map.
|
//! Map structure contains information about tiles and other elements on the map.
|
||||||
//!
|
//!
|
||||||
//! Map is created with generators and then can by modified with MapModifiers.
|
//! Map is created with generators and then can by modified with MapModifiers.
|
||||||
//!
|
//!
|
||||||
//! This structure is not intented to be your map in the game (But can be used as one).
|
//! This structure is not intented to be your map in the game (But can be used as one).
|
||||||
//! Rather the information from this map will be copied to the structures required by
|
//! Rather the information from this map will be copied to the structures required by
|
||||||
//! specific game.
|
//! specific game.
|
||||||
//!
|
//!
|
||||||
|
|
||||||
|
use super::geometry::{usize_abs, Point, Rect};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use super::geometry::{Point, Rect, usize_abs};
|
|
||||||
|
|
||||||
|
|
||||||
#[derive(PartialEq, Copy, Clone, Debug, Eq, Hash)]
|
#[derive(PartialEq, Copy, Clone, Debug, Eq, Hash)]
|
||||||
pub struct Tile {
|
pub struct Tile {
|
||||||
|
@ -18,8 +17,12 @@ pub struct Tile {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Copy, Clone)]
|
#[derive(PartialEq, Copy, Clone)]
|
||||||
pub enum Symmetry { None, Horizontal, Vertical, Both }
|
pub enum Symmetry {
|
||||||
|
None,
|
||||||
|
Horizontal,
|
||||||
|
Vertical,
|
||||||
|
Both,
|
||||||
|
}
|
||||||
|
|
||||||
/// Arbitrary data associated with each map
|
/// Arbitrary data associated with each map
|
||||||
pub trait BuilderData: Clone + Default {}
|
pub trait BuilderData: Clone + Default {}
|
||||||
|
@ -33,9 +36,9 @@ impl BuilderData for NoData {}
|
||||||
/// Map data
|
/// Map data
|
||||||
#[derive(Default, Clone)]
|
#[derive(Default, Clone)]
|
||||||
pub struct Map<D> {
|
pub struct Map<D> {
|
||||||
pub tiles : Vec<Tile>,
|
pub tiles: Vec<Tile>,
|
||||||
pub width : usize,
|
pub width: usize,
|
||||||
pub height : usize,
|
pub height: usize,
|
||||||
pub starting_point: Option<Point>,
|
pub starting_point: Option<Point>,
|
||||||
pub exit_point: Option<Point>,
|
pub exit_point: Option<Point>,
|
||||||
pub rooms: Vec<Rect>,
|
pub rooms: Vec<Rect>,
|
||||||
|
@ -45,13 +48,13 @@ pub struct Map<D> {
|
||||||
|
|
||||||
impl Tile {
|
impl Tile {
|
||||||
pub fn new(is_blocked: bool, index: usize) -> Tile {
|
pub fn new(is_blocked: bool, index: usize) -> Tile {
|
||||||
Tile { is_blocked, index}
|
Tile { is_blocked, index }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn wall() -> Tile {
|
pub fn wall() -> Tile {
|
||||||
Tile::new(true, 0)
|
Tile::new(true, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn floor() -> Tile {
|
pub fn floor() -> Tile {
|
||||||
Tile::new(false, 0)
|
Tile::new(false, 0)
|
||||||
}
|
}
|
||||||
|
@ -70,29 +73,34 @@ impl Tile {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<D: BuilderData> Map<D> {
|
impl<D: BuilderData> Map<D> {
|
||||||
|
|
||||||
/// Generates an empty map, consisting entirely of solid walls
|
/// Generates an empty map, consisting entirely of solid walls
|
||||||
pub fn new(width: usize, height: usize) -> Map<D> {
|
pub fn new(width: usize, height: usize) -> Map<D> {
|
||||||
let map_tile_count = width*height;
|
let map_tile_count = width * height;
|
||||||
Map{
|
Map {
|
||||||
tiles : vec![Tile::wall(); map_tile_count],
|
tiles: vec![Tile::wall(); map_tile_count],
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
starting_point: None,
|
starting_point: None,
|
||||||
exit_point: None,
|
exit_point: None,
|
||||||
rooms: Vec::new(),
|
rooms: Vec::new(),
|
||||||
corridors: Vec::new(),
|
corridors: Vec::new(),
|
||||||
data: Default::default()
|
data: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create map from given string
|
/// Create map from given string
|
||||||
pub fn from_string(map_string: &str) -> Map<D> {
|
pub fn from_string(map_string: &str) -> Map<D> {
|
||||||
let lines: Vec<&str> = map_string.split("\n")
|
let lines: Vec<&str> = map_string
|
||||||
|
.split("\n")
|
||||||
.map(|l| l.trim())
|
.map(|l| l.trim())
|
||||||
.filter(|l| l.len() > 0)
|
.filter(|l| l.len() > 0)
|
||||||
.collect();
|
.collect();
|
||||||
let cols = lines.iter().map(|l| l.len()).max().get_or_insert(1).to_owned();
|
let cols = lines
|
||||||
|
.iter()
|
||||||
|
.map(|l| l.len())
|
||||||
|
.max()
|
||||||
|
.get_or_insert(1)
|
||||||
|
.to_owned();
|
||||||
let rows = lines.len();
|
let rows = lines.len();
|
||||||
let mut map = Map::new(cols, rows);
|
let mut map = Map::new(cols, rows);
|
||||||
|
|
||||||
|
@ -122,22 +130,38 @@ impl<D: BuilderData> Map<D> {
|
||||||
let mut exits = Vec::new();
|
let mut exits = Vec::new();
|
||||||
|
|
||||||
// Cardinal directions
|
// Cardinal directions
|
||||||
if x > 0 && self.is_exit_valid(x-1, y) { exits.push((x-1, y, 1.0)) };
|
if x > 0 && self.is_exit_valid(x - 1, y) {
|
||||||
if self.is_exit_valid(x+1, y) { exits.push((x+1, y, 1.0)) };
|
exits.push((x - 1, y, 1.0))
|
||||||
if y > 0 && self.is_exit_valid(x, y-1) { exits.push((x, y-1, 1.0)) };
|
};
|
||||||
if self.is_exit_valid(x, y+1) { exits.push((x, y+1, 1.0)) };
|
if self.is_exit_valid(x + 1, y) {
|
||||||
|
exits.push((x + 1, y, 1.0))
|
||||||
|
};
|
||||||
|
if y > 0 && self.is_exit_valid(x, y - 1) {
|
||||||
|
exits.push((x, y - 1, 1.0))
|
||||||
|
};
|
||||||
|
if self.is_exit_valid(x, y + 1) {
|
||||||
|
exits.push((x, y + 1, 1.0))
|
||||||
|
};
|
||||||
|
|
||||||
// Diagonals
|
// Diagonals
|
||||||
if x > 0 && y > 0 && self.is_exit_valid(x-1, y-1) { exits.push((x-1, y-1, 1.45)); }
|
if x > 0 && y > 0 && self.is_exit_valid(x - 1, y - 1) {
|
||||||
if y > 0 && self.is_exit_valid(x+1, y-1) { exits.push((x+1, y-1, 1.45)); }
|
exits.push((x - 1, y - 1, 1.45));
|
||||||
if x > 0 && self.is_exit_valid(x-1, y+1) { exits.push((x-1, y+1, 1.45)); }
|
}
|
||||||
if self.is_exit_valid(x+1, y+1) { exits.push((x+1, y+1, 1.45)); }
|
if y > 0 && self.is_exit_valid(x + 1, y - 1) {
|
||||||
|
exits.push((x + 1, y - 1, 1.45));
|
||||||
|
}
|
||||||
|
if x > 0 && self.is_exit_valid(x - 1, y + 1) {
|
||||||
|
exits.push((x - 1, y + 1, 1.45));
|
||||||
|
}
|
||||||
|
if self.is_exit_valid(x + 1, y + 1) {
|
||||||
|
exits.push((x + 1, y + 1, 1.45));
|
||||||
|
}
|
||||||
|
|
||||||
exits
|
exits
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if given tile can be accessed
|
// Check if given tile can be accessed
|
||||||
fn is_exit_valid(&self, x:usize, y:usize) -> bool {
|
fn is_exit_valid(&self, x: usize, y: usize) -> bool {
|
||||||
self.at(x, y).is_blocked == false
|
self.at(x, y).is_blocked == false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -150,9 +174,9 @@ impl<D: BuilderData> Map<D> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn xy_idx(&self, x: usize, y: usize) -> usize {
|
pub fn xy_idx(&self, x: usize, y: usize) -> usize {
|
||||||
y * self.width + x
|
y * self.width + x
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create room on the map at given location
|
/// Create room on the map at given location
|
||||||
/// Room is created by setting all tiles in the room to the Floor
|
/// Room is created by setting all tiles in the room to the Floor
|
||||||
pub fn add_room(&mut self, rect: Rect) {
|
pub fn add_room(&mut self, rect: Rect) {
|
||||||
|
@ -164,7 +188,7 @@ impl<D: BuilderData> Map<D> {
|
||||||
self.rooms.push(rect);
|
self.rooms.push(rect);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_corridor(&mut self, from: Point, to:Point) {
|
pub fn add_corridor(&mut self, from: Point, to: Point) {
|
||||||
let mut corridor = Vec::new();
|
let mut corridor = Vec::new();
|
||||||
let mut x = from.x;
|
let mut x = from.x;
|
||||||
let mut y = from.y;
|
let mut y = from.y;
|
||||||
|
@ -234,9 +258,13 @@ impl<D: BuilderData> Map<D> {
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
let half_brush_size = brush_size / 2;
|
let half_brush_size = brush_size / 2;
|
||||||
for brush_y in y-half_brush_size .. y+half_brush_size {
|
for brush_y in y - half_brush_size..y + half_brush_size {
|
||||||
for brush_x in x-half_brush_size .. x+half_brush_size {
|
for brush_x in x - half_brush_size..x + half_brush_size {
|
||||||
if brush_x > 1 && brush_x < self.width-1 && brush_y > 1 && brush_y < self.height-1 {
|
if brush_x > 1
|
||||||
|
&& brush_x < self.width - 1
|
||||||
|
&& brush_y > 1
|
||||||
|
&& brush_y < self.height - 1
|
||||||
|
{
|
||||||
self.set_tile(brush_x, brush_y, Tile::floor());
|
self.set_tile(brush_x, brush_y, Tile::floor());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -250,7 +278,7 @@ impl<D: BuilderData> fmt::Display for Map<D> {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
for y in 0..self.height {
|
for y in 0..self.height {
|
||||||
let bytes: Vec<u8> = (0..self.width)
|
let bytes: Vec<u8> = (0..self.width)
|
||||||
.map(|x| if self.at(x, y).is_blocked {'#'} else {' '} as u8)
|
.map(|x| if self.at(x, y).is_blocked { '#' } else { ' ' } as u8)
|
||||||
.collect();
|
.collect();
|
||||||
let line = String::from_utf8(bytes).expect("Can't convert map to string");
|
let line = String::from_utf8(bytes).expect("Can't convert map to string");
|
||||||
let _ = write!(f, "{}\n", line);
|
let _ = write!(f, "{}\n", line);
|
||||||
|
@ -312,7 +340,7 @@ mod tests {
|
||||||
assert_eq!(exists, expected_exists);
|
assert_eq!(exists, expected_exists);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_create_room() {
|
fn test_create_room() {
|
||||||
let mut map = Map::<NoData>::new(5, 5);
|
let mut map = Map::<NoData>::new(5, 5);
|
||||||
map.add_room(Rect::new(1, 1, 3, 3));
|
map.add_room(Rect::new(1, 1, 3, 3));
|
||||||
|
@ -347,7 +375,6 @@ mod tests {
|
||||||
assert_eq!(map.tiles, expected_map.tiles);
|
assert_eq!(map.tiles, expected_map.tiles);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_available_exists() {
|
fn test_available_exists() {
|
||||||
let map_str = "
|
let map_str = "
|
||||||
|
@ -360,4 +387,4 @@ mod tests {
|
||||||
|
|
||||||
assert_eq!(exists.len(), 1);
|
assert_eq!(exists.len(), 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,42 +2,37 @@
|
||||||
//! Can be used to meausre the quality of the map or the quality of the generator.
|
//! Can be used to meausre the quality of the map or the quality of the generator.
|
||||||
//! To meause the quality of the generator; generate lots of maps, measure them
|
//! To meause the quality of the generator; generate lots of maps, measure them
|
||||||
//! and the provide generator score as an average.
|
//! and the provide generator score as an average.
|
||||||
//!
|
//!
|
||||||
|
|
||||||
use super::BuilderData;
|
|
||||||
use super::map::Map;
|
|
||||||
use super::dijkstra::DijkstraMap;
|
use super::dijkstra::DijkstraMap;
|
||||||
|
use super::map::Map;
|
||||||
|
use super::BuilderData;
|
||||||
|
|
||||||
/// This metric calculates the percentage of walkable cells (Floor).
|
/// This metric calculates the percentage of walkable cells (Floor).
|
||||||
/// If this number is very low (like < 10%) then it means that the map
|
/// If this number is very low (like < 10%) then it means that the map
|
||||||
/// is probably to degenerated and shouldn't be used
|
/// is probably to degenerated and shouldn't be used
|
||||||
pub fn density<D>(map: &Map<D>) -> f32 {
|
pub fn density<D>(map: &Map<D>) -> f32 {
|
||||||
let floor_count = map.tiles.iter()
|
let floor_count = map.tiles.iter().filter(|&t| t.is_walkable()).count();
|
||||||
.filter(|&t| t.is_walkable())
|
|
||||||
.count();
|
|
||||||
floor_count as f32 / map.tiles.len() as f32
|
floor_count as f32 / map.tiles.len() as f32
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Calculate the length of the shortes path from the starting point
|
/// Calculate the length of the shortes path from the starting point
|
||||||
/// to the exit.
|
/// to the exit.
|
||||||
/// If this path is very short, then the map is probably degenerated.
|
/// If this path is very short, then the map is probably degenerated.
|
||||||
pub fn path_length<D: BuilderData>(map: &Map<D>) -> f32 {
|
pub fn path_length<D: BuilderData>(map: &Map<D>) -> f32 {
|
||||||
if map.starting_point.is_none() {
|
if map.starting_point.is_none() {
|
||||||
return 0.0
|
return 0.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
match map.exit_point {
|
match map.exit_point {
|
||||||
None => 0.0,
|
None => 0.0,
|
||||||
Some(exit) => {
|
Some(exit) => {
|
||||||
let dijkstra = DijkstraMap::new(map);
|
let dijkstra = DijkstraMap::new(map);
|
||||||
dijkstra.tiles[map.xy_idx(exit.x, exit.y)]
|
dijkstra.tiles[map.xy_idx(exit.x, exit.y)]
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// ------------------------------------------------------------------------------------------------
|
/// ------------------------------------------------------------------------------------------------
|
||||||
/// Module unit tests
|
/// Module unit tests
|
||||||
/// ------------------------------------------------------------------------------------------------
|
/// ------------------------------------------------------------------------------------------------
|
||||||
|
@ -46,7 +41,6 @@ mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::{geometry::Point, map::NoData};
|
use crate::{geometry::Point, map::NoData};
|
||||||
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_density_no_floor() {
|
fn test_density_no_floor() {
|
||||||
let map = Map::<NoData>::new(10, 10);
|
let map = Map::<NoData>::new(10, 10);
|
||||||
|
@ -87,10 +81,10 @@ mod tests {
|
||||||
##########
|
##########
|
||||||
";
|
";
|
||||||
let mut map = Map::<NoData>::from_string(map_str);
|
let mut map = Map::<NoData>::from_string(map_str);
|
||||||
map.starting_point = Some(Point::new(1,1));
|
map.starting_point = Some(Point::new(1, 1));
|
||||||
map.exit_point = Some(Point::new(8,1));
|
map.exit_point = Some(Point::new(8, 1));
|
||||||
|
|
||||||
let score = path_length(&map);
|
let score = path_length(&map);
|
||||||
assert!(f32::abs(score - 7.9) <= 0.01);
|
assert!(f32::abs(score - 7.9) <= 0.01);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
//! Helper function for random number generator
|
//! Helper function for random number generator
|
||||||
//!
|
//!
|
||||||
use rand::prelude::*;
|
use rand::prelude::*;
|
||||||
|
|
||||||
pub trait Rng {
|
pub trait Rng {
|
||||||
|
@ -11,7 +11,7 @@ pub trait Rng {
|
||||||
|
|
||||||
impl Rng for StdRng {
|
impl Rng for StdRng {
|
||||||
fn roll_dice(&mut self, min: usize, max: usize) -> usize {
|
fn roll_dice(&mut self, min: usize, max: usize) -> usize {
|
||||||
self.random_range(min, max+1)
|
self.random_range(min, max + 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn random_range(&mut self, start: usize, end: usize) -> usize {
|
fn random_range(&mut self, start: usize, end: usize) -> usize {
|
||||||
|
@ -29,8 +29,8 @@ impl Rng for StdRng {
|
||||||
/// ------------------------------------------------------------------------------------------------
|
/// ------------------------------------------------------------------------------------------------
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use rand::prelude::*;
|
|
||||||
use super::Rng;
|
use super::Rng;
|
||||||
|
use rand::prelude::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_range() {
|
fn test_range() {
|
||||||
|
@ -64,5 +64,4 @@ mod tests {
|
||||||
assert_eq!(min, 1);
|
assert_eq!(min, 1);
|
||||||
assert_eq!(max, 7);
|
assert_eq!(max, 7);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user