Add ability to associate arbitrary BuilderData
with maps.
I'm finding that, for best results, I need to integrate everything into my map generation process. So for instance, object/monster spawns need to run as a filter so they can influence future steps. This associates a `Clone + Default` type with maps and makes it available to filters. `NoData` exists for the current behavior. All examples/tests/demos have been updated accordingly.
This commit is contained in:
parent
1ef42b11ad
commit
957dcca447
|
@ -1,7 +1,7 @@
|
|||
use wasm_bindgen::prelude::*;
|
||||
use web_sys;
|
||||
use rand::prelude::*;
|
||||
use mapgen::{Map, MapBuilder, geometry::Point};
|
||||
use mapgen::{Map, MapBuilder, NoData, geometry::Point};
|
||||
use mapgen::filter::*;
|
||||
use mapgen::metric;
|
||||
|
||||
|
@ -19,7 +19,7 @@ pub struct World {
|
|||
width: u32,
|
||||
height: u32,
|
||||
tiles: Vec<Cell>,
|
||||
map: Map,
|
||||
map: Map<NoData>,
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
|
@ -32,7 +32,7 @@ pub struct Position {
|
|||
#[wasm_bindgen]
|
||||
impl World {
|
||||
|
||||
fn new(width: u32, height: u32, map: Map) -> World {
|
||||
fn new(width: u32, height: u32, map: Map<NoData>) -> World {
|
||||
let tiles = (0..map.tiles.len())
|
||||
.map(|i| if map.tiles[i].is_walkable() {Cell::Floor} else {Cell::Wall})
|
||||
.collect();
|
||||
|
@ -176,7 +176,7 @@ impl World {
|
|||
div.set_inner_html(&info);
|
||||
}
|
||||
|
||||
fn print_map_metrics(map: &Map) {
|
||||
fn print_map_metrics(map: &Map<NoData>) {
|
||||
let window = web_sys::window().expect("no global `window` exists");
|
||||
let document = window.document().expect("should have a document on window");
|
||||
let div = document.get_element_by_id("map-metrics").expect("Need div with id: map-metrics");
|
||||
|
|
|
@ -4,7 +4,7 @@ use mapgen::*;
|
|||
|
||||
fn main() {
|
||||
let mut rng = StdRng::seed_from_u64(907647352);
|
||||
let gen = BspInterior::new();
|
||||
let gen = BspInterior::<NoData>::new();
|
||||
let map = gen.modify_map(&mut rng, &Map::new(20, 10));
|
||||
println!("{:}", &map);
|
||||
}
|
|
@ -6,7 +6,7 @@ use mapgen::*;
|
|||
fn main() {
|
||||
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 gen = BspRooms::new();
|
||||
let gen = BspRooms::<NoData>::new();
|
||||
let map = gen.modify_map(&mut rng, &Map::new(80, 50));
|
||||
println!("{:}", &map);
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
use mapgen::{
|
||||
MapBuilder,
|
||||
NoData,
|
||||
filter::{
|
||||
NoiseGenerator,
|
||||
CellularAutomata,
|
||||
|
@ -12,7 +13,7 @@ use mapgen::{
|
|||
|
||||
|
||||
fn main() {
|
||||
let map = MapBuilder::new(20, 20)
|
||||
let map = MapBuilder::<NoData>::new(20, 20)
|
||||
.with(NoiseGenerator::uniform())
|
||||
.with(CellularAutomata::new())
|
||||
.with(AreaStartingPosition::new(XStart::CENTER, YStart::CENTER))
|
||||
|
|
|
@ -25,25 +25,26 @@
|
|||
//! ---
|
||||
//!
|
||||
|
||||
use std::collections::VecDeque;
|
||||
use std::{collections::VecDeque, marker::PhantomData};
|
||||
use std::f32::MAX;
|
||||
use super::map::Map;
|
||||
use super::map::{BuilderData, 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).
|
||||
/// size_x and size_y are stored for overflow avoidance.
|
||||
/// max_depth is the maximum number of iterations this search shall support.
|
||||
pub struct DijkstraMap {
|
||||
pub struct DijkstraMap<D> {
|
||||
pub tiles: Vec<f32>,
|
||||
size_x: usize,
|
||||
size_y: usize,
|
||||
max_depth: f32,
|
||||
phantom: PhantomData<D>,
|
||||
}
|
||||
|
||||
impl DijkstraMap {
|
||||
impl<D: BuilderData> DijkstraMap<D> {
|
||||
//! Construct a new Dijkstra map, ready to run.
|
||||
pub fn new(map: &Map) -> DijkstraMap {
|
||||
pub fn new(map: &Map<D>) -> DijkstraMap<D> {
|
||||
let len = map.width * map.height;
|
||||
let tiles = vec![MAX; len];
|
||||
let mut d = DijkstraMap {
|
||||
|
@ -51,6 +52,7 @@ impl DijkstraMap {
|
|||
size_x: map.width,
|
||||
size_y: map.height,
|
||||
max_depth: len as f32,
|
||||
phantom: PhantomData,
|
||||
};
|
||||
d.build(map);
|
||||
d
|
||||
|
@ -61,7 +63,7 @@ impl DijkstraMap {
|
|||
/// depth is further than the current depth.
|
||||
/// WARNING: Will give incorrect results when used with non-uniform exit costs. Much slower
|
||||
/// algorithm required to support that.
|
||||
fn build(&mut self, map: &Map) {
|
||||
fn build(&mut self, map: &Map<D>) {
|
||||
let mapsize: usize = (self.size_x * self.size_y) as usize;
|
||||
let mut open_list: VecDeque<((usize, usize), f32)> = VecDeque::with_capacity(mapsize);
|
||||
|
||||
|
@ -97,7 +99,7 @@ impl DijkstraMap {
|
|||
mod tests {
|
||||
use super::*;
|
||||
use crate::geometry::Point;
|
||||
use crate::map::Map;
|
||||
use crate::map::{Map, NoData};
|
||||
|
||||
#[test]
|
||||
fn test_culling() {
|
||||
|
@ -106,7 +108,7 @@ mod tests {
|
|||
# # #
|
||||
##########
|
||||
";
|
||||
let mut map = Map::from_string(map_str);
|
||||
let mut map = Map::<NoData>::from_string(map_str);
|
||||
map.starting_point = Some(Point::new(8, 1));
|
||||
let dm = DijkstraMap::new(&map);
|
||||
|
||||
|
@ -134,7 +136,7 @@ mod tests {
|
|||
# #
|
||||
####
|
||||
";
|
||||
let mut map = Map::from_string(map_str);
|
||||
let mut map = Map::<NoData>::from_string(map_str);
|
||||
map.starting_point = Some(Point::new(2, 2));
|
||||
let dm = DijkstraMap::new(&map);
|
||||
let expected = [MAX, MAX, MAX, MAX,
|
||||
|
@ -153,7 +155,7 @@ mod tests {
|
|||
# # #
|
||||
##########
|
||||
";
|
||||
let mut map = Map::from_string(map_str);
|
||||
let mut map = Map::<NoData>::from_string(map_str);
|
||||
map.starting_point = Some(Point::new(8, 2));
|
||||
let dm = DijkstraMap::new(&map);
|
||||
let expected = [MAX, MAX, MAX, MAX, MAX, MAX, MAX, MAX, MAX, MAX,
|
||||
|
|
|
@ -6,13 +6,13 @@
|
|||
//! Example generator usage:
|
||||
//! ```
|
||||
//! use rand::prelude::*;
|
||||
//! use mapgen::{Map, MapFilter};
|
||||
//! use mapgen::{Map, MapFilter, NoData};
|
||||
//! use mapgen::filter::{
|
||||
//! BspInterior
|
||||
//! };
|
||||
//!
|
||||
//! let mut rng = StdRng::seed_from_u64(100);
|
||||
//! let gen = BspInterior::new();
|
||||
//! let gen = BspInterior::<NoData>::new();
|
||||
//! let map = gen.modify_map(&mut rng, &Map::new(80, 50));
|
||||
//!
|
||||
//! assert_eq!(map.width, 80);
|
||||
|
@ -20,32 +20,36 @@
|
|||
//! ```
|
||||
//!
|
||||
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use rand::prelude::*;
|
||||
use crate::MapFilter;
|
||||
use crate::{BuilderData, MapFilter};
|
||||
use crate::geometry::{Point, Rect};
|
||||
use crate::random::Rng;
|
||||
use crate::Map;
|
||||
|
||||
|
||||
pub struct BspInterior {
|
||||
pub struct BspInterior<D: BuilderData> {
|
||||
min_room_size: usize,
|
||||
phantom: PhantomData<D>,
|
||||
}
|
||||
|
||||
impl MapFilter for BspInterior {
|
||||
fn modify_map(&self, rng: &mut StdRng, map: &Map) -> Map {
|
||||
impl<D: BuilderData> MapFilter<D> for BspInterior<D> {
|
||||
fn modify_map(&self, rng: &mut StdRng, map: &Map<D>) -> Map<D> {
|
||||
self.build(rng, map)
|
||||
}
|
||||
}
|
||||
|
||||
impl BspInterior {
|
||||
impl<D: BuilderData> BspInterior<D> {
|
||||
|
||||
pub fn new() -> Box<BspInterior> {
|
||||
pub fn new() -> Box<BspInterior<D>> {
|
||||
Box::new(BspInterior{
|
||||
min_room_size: 8,
|
||||
phantom: PhantomData,
|
||||
})
|
||||
}
|
||||
|
||||
fn build(&self, rng: &mut StdRng, map: &Map) -> Map {
|
||||
fn build(&self, rng: &mut StdRng, map: &Map<D>) -> Map<D> {
|
||||
let mut new_map = map.clone();
|
||||
let mut rects: Vec<Rect> = Vec::new();
|
||||
rects.push( Rect::new(1, 1, new_map.width-2, new_map.height-2) );
|
||||
|
@ -114,12 +118,12 @@ impl BspInterior {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::{Map};
|
||||
use crate::{Map, map::NoData};
|
||||
|
||||
#[test]
|
||||
fn no_corridors_on_borders() {
|
||||
let mut rng = StdRng::seed_from_u64(907647352);
|
||||
let gen = BspInterior::new();
|
||||
let gen = BspInterior::<NoData>::new();
|
||||
let map = gen.modify_map(&mut rng, &Map::new(80, 50));
|
||||
for i in 0..80 {
|
||||
assert!(map.at(i, 0).is_blocked());
|
||||
|
|
|
@ -6,11 +6,11 @@
|
|||
//! Example generator usage:
|
||||
//! ```
|
||||
//! use rand::prelude::*;
|
||||
//! use mapgen::{Map, MapFilter};
|
||||
//! use mapgen::{Map, MapFilter, NoData};
|
||||
//! use mapgen::filter::BspRooms;
|
||||
//!
|
||||
//! let mut rng = StdRng::seed_from_u64(100);
|
||||
//! let gen = BspRooms::new();
|
||||
//! let gen = BspRooms::<NoData>::new();
|
||||
//! let map = gen.modify_map(&mut rng, &Map::new(80, 50));
|
||||
//!
|
||||
//! assert_eq!(map.width, 80);
|
||||
|
@ -18,31 +18,36 @@
|
|||
//! ```
|
||||
//!
|
||||
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use rand::prelude::*;
|
||||
use crate::BuilderData;
|
||||
use crate::MapFilter;
|
||||
use crate::geometry::Rect;
|
||||
use crate::random::Rng;
|
||||
use crate::Map;
|
||||
|
||||
|
||||
pub struct BspRooms {
|
||||
pub struct BspRooms<D: BuilderData> {
|
||||
max_split: usize,
|
||||
phantom: PhantomData<D>,
|
||||
}
|
||||
|
||||
impl MapFilter for BspRooms {
|
||||
fn modify_map(&self, rng: &mut StdRng, map: &Map) -> Map {
|
||||
impl<D: BuilderData> MapFilter<D> for BspRooms<D> {
|
||||
fn modify_map(&self, rng: &mut StdRng, map: &Map<D>) -> Map<D> {
|
||||
self.build_rooms(map, rng)
|
||||
}
|
||||
}
|
||||
|
||||
impl BspRooms {
|
||||
pub fn new() -> Box<BspRooms> {
|
||||
impl<D: BuilderData> BspRooms<D> {
|
||||
pub fn new() -> Box<BspRooms<D>> {
|
||||
Box::new(BspRooms {
|
||||
max_split: 240,
|
||||
phantom: PhantomData,
|
||||
})
|
||||
}
|
||||
|
||||
fn build_rooms(&self, map: &Map, rng : &mut StdRng) -> Map {
|
||||
fn build_rooms(&self, map: &Map<D>, rng : &mut StdRng) -> Map<D> {
|
||||
let mut new_map = map.clone();
|
||||
let mut rects: Vec<Rect> = Vec::new();
|
||||
// Start with a single map-sized rectangle
|
||||
|
@ -102,7 +107,7 @@ impl BspRooms {
|
|||
result
|
||||
}
|
||||
|
||||
fn is_possible(&self, rect: Rect, map: &Map) -> bool {
|
||||
fn is_possible(&self, rect: Rect, map: &Map<D>) -> bool {
|
||||
let mut expanded = rect;
|
||||
expanded.x1 -= 2;
|
||||
expanded.x2 += 2;
|
||||
|
@ -139,12 +144,12 @@ impl BspRooms {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::map::Map;
|
||||
use crate::map::{Map, NoData};
|
||||
|
||||
#[test]
|
||||
fn no_corridors_on_borders() {
|
||||
let mut rng = StdRng::seed_from_u64(907647352);
|
||||
let gen = BspRooms::new();
|
||||
let gen = BspRooms::<NoData>::new();
|
||||
let map = gen.modify_map(&mut rng, &Map::new(80, 50));
|
||||
for i in 0..80 {
|
||||
assert!(map.at(i, 0).is_blocked());
|
||||
|
|
|
@ -9,11 +9,11 @@
|
|||
//! Example usage:
|
||||
//! ```
|
||||
//! use rand::prelude::*;
|
||||
//! use mapgen::{Map, MapFilter};
|
||||
//! use mapgen::{Map, MapFilter, NoData};
|
||||
//! use mapgen::filter::CellularAutomata;
|
||||
//!
|
||||
//! let mut rng = StdRng::seed_from_u64(100);
|
||||
//! let gen = CellularAutomata::new();
|
||||
//! let gen = CellularAutomata::<NoData>::new();
|
||||
//! let map = gen.modify_map(&mut rng, &Map::new(80, 50));
|
||||
//!
|
||||
//! assert_eq!(map.width, 80);
|
||||
|
@ -21,30 +21,37 @@
|
|||
//! ```
|
||||
//!
|
||||
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use rand::prelude::*;
|
||||
use crate::BuilderData;
|
||||
use crate::MapFilter;
|
||||
use crate::{Map, Tile};
|
||||
|
||||
|
||||
/// Map filter
|
||||
pub struct CellularAutomata {
|
||||
pub struct CellularAutomata<D: BuilderData> {
|
||||
num_iteraction: u32,
|
||||
phantom: PhantomData<D>,
|
||||
}
|
||||
|
||||
impl MapFilter for CellularAutomata {
|
||||
fn modify_map(&self, _rng: &mut StdRng, map: &Map) -> Map {
|
||||
impl<D: BuilderData> MapFilter<D> for CellularAutomata<D> {
|
||||
fn modify_map(&self, _rng: &mut StdRng, map: &Map<D>) -> Map<D> {
|
||||
self.build(map)
|
||||
}
|
||||
}
|
||||
|
||||
impl CellularAutomata {
|
||||
impl<D: BuilderData> CellularAutomata<D> {
|
||||
/// Create generator which will create map with the given dimension.
|
||||
pub fn new() -> Box<CellularAutomata> {
|
||||
Box::new(CellularAutomata { num_iteraction: 15})
|
||||
pub fn new() -> Box<CellularAutomata<D>> {
|
||||
Box::new(CellularAutomata {
|
||||
num_iteraction: 15,
|
||||
phantom: PhantomData,
|
||||
})
|
||||
}
|
||||
|
||||
/// Generate map
|
||||
fn build(&self, map: &Map) -> Map {
|
||||
fn build(&self, map: &Map<D>) -> Map<D> {
|
||||
let mut new_map = map.clone();
|
||||
for _ in 0..self.num_iteraction {
|
||||
new_map = apply_iteration(&new_map);
|
||||
|
@ -55,7 +62,7 @@ impl CellularAutomata {
|
|||
|
||||
}
|
||||
|
||||
fn apply_iteration(map: &Map) -> Map {
|
||||
fn apply_iteration<D: BuilderData>(map: &Map<D>) -> Map<D> {
|
||||
let mut new_map = map.clone();
|
||||
|
||||
for y in 1..map.height-1 {
|
||||
|
@ -85,11 +92,13 @@ fn apply_iteration(map: &Map) -> Map {
|
|||
/// ------------------------------------------------------------------------------------------------
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::map::NoData;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_iteration_wal() {
|
||||
let map = Map::new(3, 3);
|
||||
let map = Map::<NoData>::new(3, 3);
|
||||
let new_map = apply_iteration(&map);
|
||||
assert!(new_map.at(1, 1).is_blocked());
|
||||
}
|
||||
|
@ -97,7 +106,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_iteration_floor() {
|
||||
let mut map = Map::new(3, 3);
|
||||
let mut map = Map::<NoData>::new(3, 3);
|
||||
for i in 0..3 {
|
||||
for j in 0..2 {
|
||||
map.set_tile(i, j, Tile::floor());
|
||||
|
|
|
@ -4,28 +4,34 @@
|
|||
//! It will add wall on every tile which is not accessible from the starting point.
|
||||
//!
|
||||
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use rand::prelude::StdRng;
|
||||
use crate::MapFilter;
|
||||
use crate::{Map, Tile};
|
||||
use crate::{BuilderData, Map, Tile};
|
||||
use crate::dijkstra::DijkstraMap;
|
||||
|
||||
|
||||
/// Remove unreachable areas from the map.
|
||||
pub struct CullUnreachable {}
|
||||
pub struct CullUnreachable<D: BuilderData> {
|
||||
phantom: PhantomData<D>,
|
||||
}
|
||||
|
||||
impl MapFilter for CullUnreachable {
|
||||
fn modify_map(&self, _: &mut StdRng, map: &Map) -> Map {
|
||||
impl<D: BuilderData> MapFilter<D> for CullUnreachable<D> {
|
||||
fn modify_map(&self, _: &mut StdRng, map: &Map<D>) -> Map<D> {
|
||||
self.build(map)
|
||||
}
|
||||
}
|
||||
|
||||
impl CullUnreachable {
|
||||
impl<D: BuilderData> CullUnreachable<D> {
|
||||
#[allow(dead_code)]
|
||||
pub fn new() -> Box<CullUnreachable> {
|
||||
Box::new(CullUnreachable{})
|
||||
pub fn new() -> Box<CullUnreachable<D>> {
|
||||
Box::new(CullUnreachable {
|
||||
phantom: PhantomData,
|
||||
})
|
||||
}
|
||||
|
||||
fn build(&self, map: &Map) -> Map {
|
||||
fn build(&self, map: &Map<D>) -> Map<D> {
|
||||
let mut new_map = map.clone();
|
||||
|
||||
let dijkstra_map = DijkstraMap::new(map);
|
||||
|
@ -51,7 +57,7 @@ mod tests {
|
|||
use super::*;
|
||||
use super::MapFilter;
|
||||
use crate::geometry::Point;
|
||||
use crate::map::Map;
|
||||
use crate::map::{Map, NoData};
|
||||
|
||||
#[test]
|
||||
fn test_culling() {
|
||||
|
@ -60,14 +66,14 @@ mod tests {
|
|||
# # #
|
||||
##########
|
||||
";
|
||||
let mut map = Map::from_string(map_str);
|
||||
let mut map = Map::<NoData>::from_string(map_str);
|
||||
map.starting_point = Some(Point::new(9, 1));
|
||||
let expected_map_str = "
|
||||
##########
|
||||
#### #
|
||||
##########
|
||||
";
|
||||
let expected_map = Map::from_string(expected_map_str);
|
||||
let expected_map = Map::<NoData>::from_string(expected_map_str);
|
||||
|
||||
|
||||
let modifier = CullUnreachable::new();
|
||||
|
|
|
@ -5,7 +5,9 @@
|
|||
//!
|
||||
|
||||
use std::f32;
|
||||
use std::marker::PhantomData;
|
||||
use rand::prelude::StdRng;
|
||||
use crate::BuilderData;
|
||||
use crate::geometry::Point;
|
||||
use crate::MapFilter;
|
||||
use crate::Map;
|
||||
|
@ -13,21 +15,25 @@ use crate::dijkstra::DijkstraMap;
|
|||
|
||||
|
||||
/// Add exist position to the map based on the distance from the start point.
|
||||
pub struct DistantExit {}
|
||||
pub struct DistantExit<D: BuilderData> {
|
||||
phantom: PhantomData<D>,
|
||||
}
|
||||
|
||||
impl MapFilter for DistantExit {
|
||||
fn modify_map(&self, _: &mut StdRng, map: &Map) -> Map {
|
||||
impl<D: BuilderData> MapFilter<D> for DistantExit<D> {
|
||||
fn modify_map(&self, _: &mut StdRng, map: &Map<D>) -> Map<D> {
|
||||
self.build(map)
|
||||
}
|
||||
}
|
||||
|
||||
impl DistantExit {
|
||||
impl<D: BuilderData> DistantExit<D> {
|
||||
#[allow(dead_code)]
|
||||
pub fn new() -> Box<DistantExit> {
|
||||
Box::new(DistantExit{})
|
||||
pub fn new() -> Box<DistantExit<D>> {
|
||||
Box::new(DistantExit {
|
||||
phantom: PhantomData,
|
||||
})
|
||||
}
|
||||
|
||||
fn build(&self, map: &Map) -> Map {
|
||||
fn build(&self, map: &Map<D>) -> Map<D> {
|
||||
let mut new_map = map.clone();
|
||||
|
||||
let mut best_idx = 0;
|
||||
|
@ -55,7 +61,7 @@ mod tests {
|
|||
use super::*;
|
||||
use super::MapFilter;
|
||||
use crate::geometry::Point;
|
||||
use crate::map::Map;
|
||||
use crate::map::{Map, NoData};
|
||||
|
||||
#[test]
|
||||
fn test_exit() {
|
||||
|
@ -65,7 +71,7 @@ mod tests {
|
|||
# # #
|
||||
##########
|
||||
";
|
||||
let mut map = Map::from_string(map_str);
|
||||
let mut map = Map::<NoData>::from_string(map_str);
|
||||
map.starting_point = Some(Point::new(9, 2));
|
||||
|
||||
let modifier = DistantExit::new();
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
//! Example generator usage:
|
||||
//! ```
|
||||
//! use rand::prelude::*;
|
||||
//! use mapgen::{Map, MapFilter};
|
||||
//! use mapgen::{Map, MapFilter, NoData};
|
||||
//! use mapgen::filter::DrunkardsWalk;
|
||||
//!
|
||||
//! let mut rng = StdRng::seed_from_u64(100);
|
||||
//! let gen = DrunkardsWalk::open_area();
|
||||
//! let gen = DrunkardsWalk::<NoData>::open_area();
|
||||
//! let map = gen.modify_map(&mut rng, &Map::new(80, 50));
|
||||
//!
|
||||
//! assert_eq!(map.width, 80);
|
||||
|
@ -13,9 +13,12 @@
|
|||
//! ```
|
||||
//!
|
||||
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use rand::prelude::*;
|
||||
use crate::MapFilter;
|
||||
use crate::{
|
||||
BuilderData,
|
||||
map::{Map, Symmetry, Tile},
|
||||
geometry::Point,
|
||||
random::Rng
|
||||
|
@ -25,57 +28,59 @@ use crate::{
|
|||
#[derive(PartialEq, Copy, Clone)]
|
||||
pub enum DrunkSpawnMode { StartingPoint, Random }
|
||||
|
||||
pub struct DrunkardsWalk {
|
||||
pub struct DrunkardsWalk<D: BuilderData> {
|
||||
spawn_mode : DrunkSpawnMode,
|
||||
drunken_lifetime : i32,
|
||||
floor_percent: f32,
|
||||
brush_size: usize,
|
||||
symmetry: Symmetry
|
||||
symmetry: Symmetry,
|
||||
phantom: PhantomData<D>,
|
||||
}
|
||||
|
||||
impl MapFilter for DrunkardsWalk {
|
||||
fn modify_map(&self, rng: &mut StdRng, map: &Map) -> Map {
|
||||
impl<D: BuilderData> MapFilter<D> for DrunkardsWalk<D> {
|
||||
fn modify_map(&self, rng: &mut StdRng, map: &Map<D>) -> Map<D> {
|
||||
self.build(rng, map)
|
||||
}
|
||||
}
|
||||
|
||||
impl DrunkardsWalk {
|
||||
impl<D: BuilderData> DrunkardsWalk<D> {
|
||||
pub fn new( spawn_mode: DrunkSpawnMode,
|
||||
drunken_lifetime: i32,
|
||||
floor_percent: f32,
|
||||
brush_size: usize,
|
||||
symmetry: Symmetry) -> Box<DrunkardsWalk>
|
||||
symmetry: Symmetry) -> Box<DrunkardsWalk<D>>
|
||||
{
|
||||
Box::new(DrunkardsWalk{
|
||||
spawn_mode,
|
||||
drunken_lifetime,
|
||||
floor_percent,
|
||||
brush_size,
|
||||
symmetry
|
||||
symmetry,
|
||||
phantom: PhantomData,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn open_area() -> Box<DrunkardsWalk> {
|
||||
pub fn open_area() -> Box<DrunkardsWalk<D>> {
|
||||
Self::new(DrunkSpawnMode::StartingPoint, 400, 0.5, 1, Symmetry::None)
|
||||
}
|
||||
|
||||
pub fn open_halls() -> Box<DrunkardsWalk> {
|
||||
pub fn open_halls() -> Box<DrunkardsWalk<D>> {
|
||||
Self::new(DrunkSpawnMode::Random, 400, 0.5, 1, Symmetry::None)
|
||||
}
|
||||
|
||||
pub fn winding_passages() -> Box<DrunkardsWalk> {
|
||||
pub fn winding_passages() -> Box<DrunkardsWalk<D>> {
|
||||
Self::new(DrunkSpawnMode::Random, 400, 0.4, 1, Symmetry::None)
|
||||
}
|
||||
|
||||
pub fn fat_passages() -> Box<DrunkardsWalk> {
|
||||
pub fn fat_passages() -> Box<DrunkardsWalk<D>> {
|
||||
Self::new(DrunkSpawnMode::Random, 400, 0.4, 2, Symmetry::None)
|
||||
}
|
||||
|
||||
pub fn fearful_symmetry() -> Box<DrunkardsWalk> {
|
||||
pub fn fearful_symmetry() -> Box<DrunkardsWalk<D>> {
|
||||
Self::new(DrunkSpawnMode::Random, 400, 0.4, 1, Symmetry::Both)
|
||||
}
|
||||
|
||||
fn build(&self, rng: &mut StdRng, map: &Map) -> Map {
|
||||
fn build(&self, rng: &mut StdRng, map: &Map<D>) -> Map<D> {
|
||||
let mut new_map = map.clone();
|
||||
// Set a central starting point
|
||||
let starting_position = Point::new( new_map.width / 2, new_map.height / 2 );
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
//! Example generator usage:
|
||||
//! ```
|
||||
//! use rand::prelude::*;
|
||||
//! use mapgen::{Map, MapFilter};
|
||||
//! use mapgen::{Map, MapFilter, NoData};
|
||||
//! use mapgen::filter::MazeBuilder;
|
||||
//!
|
||||
//! let mut rng = StdRng::seed_from_u64(100);
|
||||
//! let gen = MazeBuilder::new();
|
||||
//! let gen = MazeBuilder::<NoData>::new();
|
||||
//! let map = gen.modify_map(&mut rng, &Map::new(80, 50));
|
||||
//!
|
||||
//! assert_eq!(map.width, 80);
|
||||
|
@ -13,29 +13,35 @@
|
|||
//! ```
|
||||
//!
|
||||
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use rand::prelude::*;
|
||||
use crate::MapFilter;
|
||||
use crate::{
|
||||
map::{Map, Tile},
|
||||
map::{BuilderData, Map, Tile},
|
||||
random::Rng
|
||||
};
|
||||
|
||||
|
||||
pub struct MazeBuilder {}
|
||||
pub struct MazeBuilder<D: BuilderData> {
|
||||
phantom: PhantomData<D>,
|
||||
}
|
||||
|
||||
impl MapFilter for MazeBuilder {
|
||||
fn modify_map(&self, rng: &mut StdRng, map: &Map) -> Map {
|
||||
impl<D: BuilderData> MapFilter<D> for MazeBuilder<D> {
|
||||
fn modify_map(&self, rng: &mut StdRng, map: &Map<D>) -> Map<D> {
|
||||
self.build(rng, map)
|
||||
}
|
||||
}
|
||||
|
||||
impl MazeBuilder {
|
||||
pub fn new() -> Box<MazeBuilder> {
|
||||
Box::new(MazeBuilder{})
|
||||
impl<D: BuilderData> MazeBuilder<D> {
|
||||
pub fn new() -> Box<MazeBuilder<D>> {
|
||||
Box::new(MazeBuilder {
|
||||
phantom: PhantomData,
|
||||
})
|
||||
}
|
||||
|
||||
#[allow(clippy::map_entry)]
|
||||
fn build(&self, rng: &mut StdRng, map: &Map) -> Map {
|
||||
fn build(&self, rng: &mut StdRng, map: &Map<D>) -> Map<D> {
|
||||
let mut new_map = map.clone();
|
||||
let mut maze = Grid::new((map.width as i32/ 2)-2, (map.height as i32/ 2)-2, rng);
|
||||
maze.generate_maze(&mut new_map);
|
||||
|
@ -91,24 +97,26 @@ impl Cell {
|
|||
}
|
||||
}
|
||||
|
||||
struct Grid<'a> {
|
||||
struct Grid<'a, D: BuilderData> {
|
||||
width: i32,
|
||||
height: i32,
|
||||
cells: Vec<Cell>,
|
||||
backtrace: Vec<usize>,
|
||||
current: usize,
|
||||
rng : &'a mut StdRng
|
||||
rng : &'a mut StdRng,
|
||||
phantom: PhantomData<D>,
|
||||
}
|
||||
|
||||
impl<'a> Grid<'a> {
|
||||
fn new(width: i32, height:i32, rng: &mut StdRng) -> Grid {
|
||||
impl<'a, D: BuilderData> Grid<'a, D> {
|
||||
fn new(width: i32, height:i32, rng: &mut StdRng) -> Grid<D> {
|
||||
let mut grid = Grid{
|
||||
width,
|
||||
height,
|
||||
cells: Vec::new(),
|
||||
backtrace: Vec::new(),
|
||||
current: 0,
|
||||
rng
|
||||
rng,
|
||||
phantom: PhantomData,
|
||||
};
|
||||
|
||||
for row in 0..height {
|
||||
|
@ -162,7 +170,7 @@ impl<'a> Grid<'a> {
|
|||
None
|
||||
}
|
||||
|
||||
fn generate_maze(&mut self, map: &mut Map) {
|
||||
fn generate_maze(&mut self, map: &mut Map<D>) {
|
||||
let mut i = 0;
|
||||
loop {
|
||||
self.cells[self.current].visited = true;
|
||||
|
@ -199,7 +207,7 @@ impl<'a> Grid<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
fn copy_to_map(&self, map: &mut Map) {
|
||||
fn copy_to_map(&self, map: &mut Map<D>) {
|
||||
// Clear the map
|
||||
for i in map.tiles.iter_mut() { *i = Tile::wall(); }
|
||||
|
||||
|
|
|
@ -4,11 +4,11 @@
|
|||
//! Example usage:
|
||||
//! ```
|
||||
//! use rand::prelude::*;
|
||||
//! use mapgen::{Map, MapFilter};
|
||||
//! use mapgen::{Map, MapFilter, NoData};
|
||||
//! use mapgen::filter::NoiseGenerator;
|
||||
//!
|
||||
//! let mut rng = StdRng::seed_from_u64(100);
|
||||
//! let gen = NoiseGenerator::uniform();
|
||||
//! let gen = NoiseGenerator::<NoData>::uniform();
|
||||
//! let map = gen.modify_map(&mut rng, &Map::new(80, 50));
|
||||
//!
|
||||
//! assert_eq!(map.width, 80);
|
||||
|
@ -16,39 +16,44 @@
|
|||
//! ```
|
||||
//!
|
||||
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use rand::prelude::*;
|
||||
use crate::MapFilter;
|
||||
use crate::{Map, Tile};
|
||||
use crate::{BuilderData, Map, Tile};
|
||||
|
||||
|
||||
/// Map noise generator
|
||||
pub struct NoiseGenerator {
|
||||
pub struct NoiseGenerator<D: BuilderData> {
|
||||
prob: f32,
|
||||
phantom: PhantomData<D>,
|
||||
}
|
||||
|
||||
impl MapFilter for NoiseGenerator {
|
||||
fn modify_map(&self, rng: &mut StdRng, map: &Map) -> Map {
|
||||
impl<D: BuilderData> MapFilter<D> for NoiseGenerator<D> {
|
||||
fn modify_map(&self, rng: &mut StdRng, map: &Map<D>) -> Map<D> {
|
||||
self.build(map, rng)
|
||||
}
|
||||
}
|
||||
|
||||
impl NoiseGenerator {
|
||||
impl<D: BuilderData> NoiseGenerator<D> {
|
||||
/// Create noise with custom probability
|
||||
pub fn new(prob: f32) -> Box<NoiseGenerator> {
|
||||
pub fn new(prob: f32) -> Box<NoiseGenerator<D>> {
|
||||
Box::new(NoiseGenerator {
|
||||
prob,
|
||||
phantom: PhantomData,
|
||||
})
|
||||
}
|
||||
|
||||
/// Create uniform noise (Probablity 0.5)
|
||||
pub fn uniform() -> Box<NoiseGenerator> {
|
||||
pub fn uniform() -> Box<NoiseGenerator<D>> {
|
||||
Box::new(NoiseGenerator {
|
||||
prob: 0.5,
|
||||
phantom: PhantomData,
|
||||
})
|
||||
}
|
||||
|
||||
/// Generate map
|
||||
fn build(&self, map: &Map, rng: &mut StdRng) -> Map {
|
||||
fn build(&self, map: &Map<D>, rng: &mut StdRng) -> Map<D> {
|
||||
let mut new_map = map.clone();
|
||||
let p = (self.prob * 100.0) as u32;
|
||||
for y in 1..new_map.height-1 {
|
||||
|
|
|
@ -1,26 +1,32 @@
|
|||
//! Connect nearest rooms on the map with corridors
|
||||
//!
|
||||
use rand::prelude::StdRng;
|
||||
use crate::BuilderData;
|
||||
use crate::MapFilter;
|
||||
use crate::Map;
|
||||
use std::collections::HashSet;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
|
||||
pub struct NearestCorridors {}
|
||||
pub struct NearestCorridors<D: BuilderData> {
|
||||
phantom: PhantomData<D>,
|
||||
}
|
||||
|
||||
impl MapFilter for NearestCorridors {
|
||||
fn modify_map(&self, _: &mut StdRng, map: &Map) -> Map {
|
||||
impl<D: BuilderData> MapFilter<D> for NearestCorridors<D> {
|
||||
fn modify_map(&self, _: &mut StdRng, map: &Map<D>) -> Map<D> {
|
||||
self.corridors(map)
|
||||
}
|
||||
}
|
||||
|
||||
impl NearestCorridors {
|
||||
impl<D: BuilderData> NearestCorridors<D> {
|
||||
|
||||
pub fn new() -> Box<NearestCorridors> {
|
||||
Box::new(NearestCorridors{})
|
||||
pub fn new() -> Box<NearestCorridors<D>> {
|
||||
Box::new(NearestCorridors {
|
||||
phantom: PhantomData,
|
||||
})
|
||||
}
|
||||
|
||||
fn corridors(&self, map: &Map) -> Map {
|
||||
fn corridors(&self, map: &Map<D>) -> Map<D> {
|
||||
let mut new_map = map.clone();
|
||||
|
||||
let mut connected : HashSet<usize> = HashSet::new();
|
||||
|
|
|
@ -6,11 +6,11 @@
|
|||
//! Example generator usage:
|
||||
//! ```
|
||||
//! use rand::prelude::*;
|
||||
//! use mapgen::{MapFilter, Map};
|
||||
//! use mapgen::{MapFilter, Map, NoData};
|
||||
//! use mapgen::filter::SimpleRooms;
|
||||
//!
|
||||
//! let mut rng = StdRng::seed_from_u64(100);
|
||||
//! let gen = SimpleRooms::new();
|
||||
//! let gen = SimpleRooms::<NoData>::new();
|
||||
//! let map = gen.modify_map(&mut rng, &Map::new(80, 50));
|
||||
//!
|
||||
//! assert_eq!(map.width, 80);
|
||||
|
@ -18,36 +18,41 @@
|
|||
//! ```
|
||||
//!
|
||||
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use rand::prelude::*;
|
||||
use crate::BuilderData;
|
||||
use crate::MapFilter;
|
||||
use crate::geometry::Rect;
|
||||
use crate::random::Rng;
|
||||
use crate::Map;
|
||||
|
||||
|
||||
pub struct SimpleRooms {
|
||||
pub struct SimpleRooms<D: BuilderData> {
|
||||
max_rooms: usize,
|
||||
min_room_size: usize,
|
||||
max_room_size: usize,
|
||||
phantom: PhantomData<D>,
|
||||
}
|
||||
|
||||
impl MapFilter for SimpleRooms {
|
||||
fn modify_map(&self, rng: &mut StdRng, map: &Map) -> Map {
|
||||
impl<D: BuilderData> MapFilter<D> for SimpleRooms<D> {
|
||||
fn modify_map(&self, rng: &mut StdRng, map: &Map<D>) -> Map<D> {
|
||||
self.build_rooms(map, rng)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl SimpleRooms {
|
||||
pub fn new() -> Box<SimpleRooms> {
|
||||
impl<D: BuilderData> SimpleRooms<D> {
|
||||
pub fn new() -> Box<SimpleRooms<D>> {
|
||||
Box::new(SimpleRooms{
|
||||
max_rooms: 30,
|
||||
min_room_size: 6,
|
||||
max_room_size: 10
|
||||
max_room_size: 10,
|
||||
phantom: PhantomData,
|
||||
})
|
||||
}
|
||||
|
||||
fn build_rooms(&self, map: &Map, rng : &mut StdRng) -> Map {
|
||||
fn build_rooms(&self, map: &Map<D>, rng : &mut StdRng) -> Map<D> {
|
||||
let mut new_map = map.clone();
|
||||
|
||||
// Create room dimensions
|
||||
|
|
|
@ -6,12 +6,12 @@
|
|||
//! Example modifier usage:
|
||||
//! ```
|
||||
//! use rand::prelude::*;
|
||||
//! use mapgen::{MapFilter, Map, Tile};
|
||||
//! use mapgen::{MapFilter, Map, NoData, Tile};
|
||||
//! use mapgen::filter::starting_point::{AreaStartingPosition, XStart, YStart};
|
||||
//! use mapgen::geometry::Point;
|
||||
//!
|
||||
//! let mut rng = StdRng::seed_from_u64(100);
|
||||
//! let mut map = Map::new(80, 50);
|
||||
//! let mut map = Map::<NoData>::new(80, 50);
|
||||
//! map.set_tile(10, 10, Tile::floor());
|
||||
//! let modifier = AreaStartingPosition::new(XStart::LEFT, YStart::TOP);
|
||||
//! let new_map = modifier.modify_map(&mut rng, &map);
|
||||
|
@ -20,7 +20,10 @@
|
|||
//! ```
|
||||
//!
|
||||
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use rand::prelude::StdRng;
|
||||
use crate::BuilderData;
|
||||
use crate::MapFilter;
|
||||
use crate::geometry::Point;
|
||||
use crate::Map;
|
||||
|
@ -33,26 +36,28 @@ pub enum XStart { LEFT, CENTER, RIGHT }
|
|||
pub enum YStart { TOP, CENTER, BOTTOM }
|
||||
|
||||
/// Add starting position to the map
|
||||
pub struct AreaStartingPosition {
|
||||
pub struct AreaStartingPosition<D: BuilderData> {
|
||||
x : XStart,
|
||||
y : YStart
|
||||
y : YStart,
|
||||
phantom: PhantomData<D>,
|
||||
}
|
||||
|
||||
impl MapFilter for AreaStartingPosition {
|
||||
fn modify_map(&self, _: &mut StdRng, map: &Map) -> Map {
|
||||
impl<D: BuilderData> MapFilter<D> for AreaStartingPosition<D> {
|
||||
fn modify_map(&self, _: &mut StdRng, map: &Map<D>) -> Map<D> {
|
||||
self.build(map)
|
||||
}
|
||||
}
|
||||
|
||||
impl AreaStartingPosition {
|
||||
impl<D: BuilderData> AreaStartingPosition<D> {
|
||||
/// Create new modifier with given region
|
||||
pub fn new(x : XStart, y : YStart) -> Box<AreaStartingPosition> {
|
||||
pub fn new(x : XStart, y : YStart) -> Box<AreaStartingPosition<D>> {
|
||||
Box::new(AreaStartingPosition{
|
||||
x, y
|
||||
x, y,
|
||||
phantom: PhantomData,
|
||||
})
|
||||
}
|
||||
|
||||
fn build(&self, map : &Map) -> Map {
|
||||
fn build(&self, map : &Map<D>) -> Map<D> {
|
||||
let seed_x = match self.x {
|
||||
XStart::LEFT => 1,
|
||||
XStart::CENTER => map.width / 2,
|
||||
|
@ -101,7 +106,7 @@ mod tests {
|
|||
use super::*;
|
||||
use super::MapFilter;
|
||||
use crate::geometry::Point;
|
||||
use crate::map::Map;
|
||||
use crate::map::{Map, NoData};
|
||||
|
||||
#[test]
|
||||
fn test_exit() {
|
||||
|
@ -111,7 +116,7 @@ mod tests {
|
|||
# # # #
|
||||
##########
|
||||
";
|
||||
let mut map = Map::from_string(map_str);
|
||||
let mut map = Map::<NoData>::from_string(map_str);
|
||||
map.starting_point = Some(Point::new(9, 2));
|
||||
|
||||
let modifier = AreaStartingPosition::new(XStart::CENTER, YStart::TOP);
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
//! Example generator usage:
|
||||
//! ```
|
||||
//! use rand::prelude::*;
|
||||
//! use mapgen::{Map, MapFilter};
|
||||
//! use mapgen::{Map, MapFilter, NoData};
|
||||
//! use mapgen::filter::VoronoiHive;
|
||||
//!
|
||||
//! let mut rng = StdRng::seed_from_u64(100);
|
||||
//! let gen = VoronoiHive::new();
|
||||
//! let gen = VoronoiHive::<NoData>::new();
|
||||
//! let map = gen.modify_map(&mut rng, &Map::new(80, 50));
|
||||
//!
|
||||
//! assert_eq!(map.width, 80);
|
||||
|
@ -13,34 +13,38 @@
|
|||
//! ```
|
||||
//!
|
||||
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use rand::prelude::*;
|
||||
use crate::MapFilter;
|
||||
use crate::{
|
||||
map::{Map, Tile},
|
||||
map::{BuilderData, Map, Tile},
|
||||
random::Rng,
|
||||
geometry::Point,
|
||||
};
|
||||
|
||||
|
||||
pub struct VoronoiHive {
|
||||
pub struct VoronoiHive<D: BuilderData> {
|
||||
n_seeds: usize,
|
||||
phantom: PhantomData<D>,
|
||||
}
|
||||
|
||||
|
||||
impl MapFilter for VoronoiHive {
|
||||
fn modify_map(&self, rng: &mut StdRng, map: &Map) -> Map {
|
||||
impl<D: BuilderData> MapFilter<D> for VoronoiHive<D> {
|
||||
fn modify_map(&self, rng: &mut StdRng, map: &Map<D>) -> Map<D> {
|
||||
self.build(rng, map)
|
||||
}
|
||||
}
|
||||
|
||||
impl VoronoiHive {
|
||||
pub fn new() -> Box<VoronoiHive> {
|
||||
impl<D: BuilderData> VoronoiHive<D> {
|
||||
pub fn new() -> Box<VoronoiHive<D>> {
|
||||
Box::new(VoronoiHive{
|
||||
n_seeds: 64,
|
||||
phantom: PhantomData,
|
||||
})
|
||||
}
|
||||
|
||||
fn build(&self, rng: &mut StdRng, map: &Map) -> Map {
|
||||
fn build(&self, rng: &mut StdRng, map: &Map<D>) -> Map<D> {
|
||||
let mut new_map = map.clone();
|
||||
let seeds = self.generate_seeds(rng, map.width, map.height);
|
||||
|
||||
|
|
28
src/lib.rs
28
src/lib.rs
|
@ -8,7 +8,7 @@
|
|||
//!
|
||||
//! Example
|
||||
//! ```
|
||||
//! use mapgen::{MapFilter, MapBuilder, Map, Tile};
|
||||
//! use mapgen::{MapFilter, MapBuilder, Map, NoData, Tile};
|
||||
//! use mapgen::filter::{
|
||||
//! NoiseGenerator,
|
||||
//! CellularAutomata,
|
||||
|
@ -16,7 +16,7 @@
|
|||
//! };
|
||||
//! use mapgen::geometry::Point;
|
||||
//!
|
||||
//! let map = MapBuilder::new(80, 50)
|
||||
//! let map = MapBuilder::<NoData>::new(80, 50)
|
||||
//! .with(NoiseGenerator::uniform())
|
||||
//! .with(CellularAutomata::new())
|
||||
//! .with(AreaStartingPosition::new(XStart::CENTER, YStart::CENTER))
|
||||
|
@ -33,7 +33,7 @@ pub mod geometry;
|
|||
pub mod map;
|
||||
pub mod metric;
|
||||
|
||||
pub use map::{Map, Symmetry, Tile};
|
||||
pub use map::{BuilderData, Map, NoData, Symmetry, Tile};
|
||||
pub use filter::*;
|
||||
|
||||
pub (crate) mod dijkstra;
|
||||
|
@ -45,20 +45,20 @@ use rand::prelude::*;
|
|||
|
||||
/// Trait which should be implemented by map modifier.
|
||||
/// Modifier takes initiall map and apply changes to it.
|
||||
pub trait MapFilter {
|
||||
fn modify_map(&self, rng: &mut StdRng, map: &Map) -> Map;
|
||||
pub trait MapFilter<D: BuilderData> {
|
||||
fn modify_map(&self, rng: &mut StdRng, map: &Map<D>) -> Map<D>;
|
||||
}
|
||||
|
||||
/// Used to chain MapBuilder and MapModifiers to create the final map.
|
||||
pub struct MapBuilder {
|
||||
pub struct MapBuilder<D> {
|
||||
width: usize,
|
||||
height: usize,
|
||||
modifiers: Vec<Box<dyn MapFilter>>,
|
||||
modifiers: Vec<Box<dyn MapFilter<D>>>,
|
||||
}
|
||||
|
||||
impl MapBuilder {
|
||||
impl<D: BuilderData> MapBuilder<D> {
|
||||
/// Create Map Builder with initial map generator
|
||||
pub fn new(width: usize, height: usize) -> MapBuilder {
|
||||
pub fn new(width: usize, height: usize) -> MapBuilder<D> {
|
||||
MapBuilder {
|
||||
width,
|
||||
height,
|
||||
|
@ -66,20 +66,20 @@ impl MapBuilder {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn with(&mut self, modifier : Box<dyn MapFilter>) -> &mut MapBuilder {
|
||||
pub fn with(&mut self, modifier : Box<dyn MapFilter<D>>) -> &mut MapBuilder<D> {
|
||||
self.modifiers.push(modifier);
|
||||
self
|
||||
}
|
||||
|
||||
/// Build map using random number seeded with system time
|
||||
pub fn build(&mut self) -> Map {
|
||||
pub fn build(&mut self) -> Map<D> {
|
||||
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);
|
||||
self.build_with_rng(&mut rng)
|
||||
}
|
||||
|
||||
/// Build map using provided random number generator
|
||||
pub fn build_with_rng(&mut self, rng: &mut StdRng) -> Map {
|
||||
pub fn build_with_rng(&mut self, rng: &mut StdRng) -> Map<D> {
|
||||
let mut map = Map::new(self.width, self.height);
|
||||
|
||||
// Build additional layers in turn
|
||||
|
@ -97,6 +97,8 @@ impl MapBuilder {
|
|||
/// ------------------------------------------------------------------------------------------------
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::map::NoData;
|
||||
|
||||
use super::*;
|
||||
use filter::{
|
||||
CellularAutomata,
|
||||
|
@ -106,7 +108,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_ca_map() {
|
||||
let map = MapBuilder::new(80, 50)
|
||||
let map = MapBuilder::<NoData>::new(80, 50)
|
||||
.with(NoiseGenerator::new(0.55))
|
||||
.with(CellularAutomata::new())
|
||||
.with(AreaStartingPosition::new(XStart::CENTER, YStart::CENTER))
|
||||
|
|
37
src/map.rs
37
src/map.rs
|
@ -21,9 +21,18 @@ pub struct Tile {
|
|||
pub enum Symmetry { None, Horizontal, Vertical, Both }
|
||||
|
||||
|
||||
/// Arbitrary data associated with each map
|
||||
pub trait BuilderData: Clone + Default {}
|
||||
|
||||
/// No build data
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct NoData;
|
||||
|
||||
impl BuilderData for NoData {}
|
||||
|
||||
/// Map data
|
||||
#[derive(Default, Clone)]
|
||||
pub struct Map {
|
||||
pub struct Map<D> {
|
||||
pub tiles : Vec<Tile>,
|
||||
pub width : usize,
|
||||
pub height : usize,
|
||||
|
@ -31,6 +40,7 @@ pub struct Map {
|
|||
pub exit_point: Option<Point>,
|
||||
pub rooms: Vec<Rect>,
|
||||
pub corridors: Vec<Vec<Point>>,
|
||||
pub data: D,
|
||||
}
|
||||
|
||||
impl Tile {
|
||||
|
@ -59,10 +69,10 @@ impl Tile {
|
|||
}
|
||||
}
|
||||
|
||||
impl Map {
|
||||
impl<D: BuilderData> Map<D> {
|
||||
|
||||
/// Generates an empty map, consisting entirely of solid walls
|
||||
pub fn new(width: usize, height: usize) -> Map {
|
||||
pub fn new(width: usize, height: usize) -> Map<D> {
|
||||
let map_tile_count = width*height;
|
||||
Map{
|
||||
tiles : vec![Tile::wall(); map_tile_count],
|
||||
|
@ -71,12 +81,13 @@ impl Map {
|
|||
starting_point: None,
|
||||
exit_point: None,
|
||||
rooms: Vec::new(),
|
||||
corridors: Vec::new()
|
||||
corridors: Vec::new(),
|
||||
data: Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
/// Create map from given string
|
||||
pub fn from_string(map_string: &str) -> Map {
|
||||
pub fn from_string(map_string: &str) -> Map<D> {
|
||||
let lines: Vec<&str> = map_string.split("\n")
|
||||
.map(|l| l.trim())
|
||||
.filter(|l| l.len() > 0)
|
||||
|
@ -235,7 +246,7 @@ impl Map {
|
|||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Map {
|
||||
impl<D: BuilderData> fmt::Display for Map<D> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
for y in 0..self.height {
|
||||
let bytes: Vec<u8> = (0..self.width)
|
||||
|
@ -257,7 +268,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_new_map() {
|
||||
let map = Map::new(10, 10);
|
||||
let map = Map::<NoData>::new(10, 10);
|
||||
for i in 0..10 {
|
||||
for j in 0..10 {
|
||||
assert!(map.at(i, j).is_blocked);
|
||||
|
@ -272,7 +283,7 @@ mod tests {
|
|||
# #
|
||||
##########
|
||||
";
|
||||
let map = Map::from_string(map_str);
|
||||
let map = Map::<NoData>::from_string(map_str);
|
||||
|
||||
assert_eq!(map.width, 10);
|
||||
assert_eq!(map.height, 3);
|
||||
|
@ -295,7 +306,7 @@ mod tests {
|
|||
# #
|
||||
##########
|
||||
";
|
||||
let map = Map::from_string(map_str);
|
||||
let map = Map::<NoData>::from_string(map_str);
|
||||
let exists = map.get_available_exits(1, 1);
|
||||
let expected_exists = vec![(2, 1, 1.0), (1, 2, 1.0), (2, 2, 1.45)];
|
||||
assert_eq!(exists, expected_exists);
|
||||
|
@ -303,7 +314,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_create_room() {
|
||||
let mut map = Map::new(5, 5);
|
||||
let mut map = Map::<NoData>::new(5, 5);
|
||||
map.add_room(Rect::new(1, 1, 3, 3));
|
||||
for x in 0..map.width {
|
||||
for y in 0..map.height {
|
||||
|
@ -323,13 +334,13 @@ mod tests {
|
|||
# # #
|
||||
##########
|
||||
";
|
||||
let mut map = Map::from_string(map_str);
|
||||
let mut map = Map::<NoData>::from_string(map_str);
|
||||
let expected_map_str = "
|
||||
##########
|
||||
# #
|
||||
##########
|
||||
";
|
||||
let expected_map = Map::from_string(expected_map_str);
|
||||
let expected_map = Map::<NoData>::from_string(expected_map_str);
|
||||
|
||||
map.add_corridor(Point::new(1, 1), Point::new(8, 1));
|
||||
|
||||
|
@ -344,7 +355,7 @@ mod tests {
|
|||
# # #
|
||||
##########
|
||||
";
|
||||
let map = Map::from_string(map_str);
|
||||
let map = Map::<NoData>::from_string(map_str);
|
||||
let exists = map.get_available_exits(0, 0);
|
||||
|
||||
assert_eq!(exists.len(), 1);
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
//! and the provide generator score as an average.
|
||||
//!
|
||||
|
||||
use super::BuilderData;
|
||||
use super::map::Map;
|
||||
use super::dijkstra::DijkstraMap;
|
||||
|
||||
|
@ -11,7 +12,7 @@ use super::dijkstra::DijkstraMap;
|
|||
/// This metric calculates the percentage of walkable cells (Floor).
|
||||
/// If this number is very low (like < 10%) then it means that the map
|
||||
/// is probably to degenerated and shouldn't be used
|
||||
pub fn density(map: &Map) -> f32 {
|
||||
pub fn density<D>(map: &Map<D>) -> f32 {
|
||||
let floor_count = map.tiles.iter()
|
||||
.filter(|&t| t.is_walkable())
|
||||
.count();
|
||||
|
@ -22,7 +23,7 @@ pub fn density(map: &Map) -> f32 {
|
|||
/// Calculate the length of the shortes path from the starting point
|
||||
/// to the exit.
|
||||
/// If this path is very short, then the map is probably degenerated.
|
||||
pub fn path_length(map: &Map) -> f32 {
|
||||
pub fn path_length<D: BuilderData>(map: &Map<D>) -> f32 {
|
||||
if map.starting_point.is_none() {
|
||||
return 0.0
|
||||
}
|
||||
|
@ -43,12 +44,12 @@ pub fn path_length(map: &Map) -> f32 {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::geometry::Point;
|
||||
use crate::{geometry::Point, map::NoData};
|
||||
|
||||
|
||||
#[test]
|
||||
fn test_density_no_floor() {
|
||||
let map = Map::new(10, 10);
|
||||
let map = Map::<NoData>::new(10, 10);
|
||||
let score = density(&map);
|
||||
assert_eq!(score, 0.0);
|
||||
}
|
||||
|
@ -60,7 +61,7 @@ mod tests {
|
|||
# ## #
|
||||
##########
|
||||
";
|
||||
let map = Map::from_string(map_str);
|
||||
let map = Map::<NoData>::from_string(map_str);
|
||||
let score = density(&map);
|
||||
assert_eq!(score, 0.2);
|
||||
}
|
||||
|
@ -72,7 +73,7 @@ mod tests {
|
|||
# ## #
|
||||
##########
|
||||
";
|
||||
let map = Map::from_string(map_str);
|
||||
let map = Map::<NoData>::from_string(map_str);
|
||||
let score = path_length(&map);
|
||||
assert_eq!(score, 0.0);
|
||||
}
|
||||
|
@ -85,7 +86,7 @@ mod tests {
|
|||
# #
|
||||
##########
|
||||
";
|
||||
let mut map = Map::from_string(map_str);
|
||||
let mut map = Map::<NoData>::from_string(map_str);
|
||||
map.starting_point = Some(Point::new(1,1));
|
||||
map.exit_point = Some(Point::new(8,1));
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user