153 lines
4.6 KiB
Rust
153 lines
4.6 KiB
Rust
//! Example generator usage:
|
|
//! ```
|
|
//! use rand::prelude::*;
|
|
//! use here_be_dragons::{Map, MapFilter, NoData};
|
|
//! use here_be_dragons::filter::DrunkardsWalk;
|
|
//!
|
|
//! let mut rng = StdRng::seed_from_u64(100);
|
|
//! let gen = DrunkardsWalk::<NoData>::open_area();
|
|
//! let map = gen.modify_map(&mut rng, &Map::new(80, 50));
|
|
//!
|
|
//! assert_eq!(map.width, 80);
|
|
//! assert_eq!(map.height, 50);
|
|
//! ```
|
|
//!
|
|
|
|
use std::marker::PhantomData;
|
|
|
|
use crate::MapFilter;
|
|
use crate::{
|
|
geometry::Point,
|
|
map::{Map, Symmetry, Tile},
|
|
random::Rng,
|
|
};
|
|
use rand::prelude::*;
|
|
|
|
#[derive(PartialEq, Copy, Clone)]
|
|
pub enum DrunkSpawnMode {
|
|
StartingPoint,
|
|
Random,
|
|
}
|
|
|
|
pub struct DrunkardsWalk<D> {
|
|
spawn_mode: DrunkSpawnMode,
|
|
drunken_lifetime: i32,
|
|
floor_percent: f32,
|
|
brush_size: usize,
|
|
symmetry: Symmetry,
|
|
phantom: PhantomData<D>,
|
|
}
|
|
|
|
impl<D: Clone + Default> MapFilter<D> for DrunkardsWalk<D> {
|
|
fn modify_map(&self, rng: &mut StdRng, map: &Map<D>) -> Map<D> {
|
|
self.build(rng, map)
|
|
}
|
|
}
|
|
|
|
impl<D: Clone + Default> DrunkardsWalk<D> {
|
|
pub fn new(
|
|
spawn_mode: DrunkSpawnMode,
|
|
drunken_lifetime: i32,
|
|
floor_percent: f32,
|
|
brush_size: usize,
|
|
symmetry: Symmetry,
|
|
) -> Box<DrunkardsWalk<D>> {
|
|
Box::new(DrunkardsWalk {
|
|
spawn_mode,
|
|
drunken_lifetime,
|
|
floor_percent,
|
|
brush_size,
|
|
symmetry,
|
|
phantom: PhantomData,
|
|
})
|
|
}
|
|
|
|
pub fn open_area() -> Box<DrunkardsWalk<D>> {
|
|
Self::new(DrunkSpawnMode::StartingPoint, 400, 0.5, 1, Symmetry::None)
|
|
}
|
|
|
|
pub fn open_halls() -> Box<DrunkardsWalk<D>> {
|
|
Self::new(DrunkSpawnMode::Random, 400, 0.5, 1, Symmetry::None)
|
|
}
|
|
|
|
pub fn winding_passages() -> Box<DrunkardsWalk<D>> {
|
|
Self::new(DrunkSpawnMode::Random, 400, 0.4, 1, Symmetry::None)
|
|
}
|
|
|
|
pub fn fat_passages() -> Box<DrunkardsWalk<D>> {
|
|
Self::new(DrunkSpawnMode::Random, 400, 0.4, 2, Symmetry::None)
|
|
}
|
|
|
|
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<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);
|
|
new_map.set_tile(starting_position.x, starting_position.y, Tile::floor());
|
|
|
|
let total_tiles = new_map.width * new_map.height;
|
|
let desired_floor_tiles = (self.floor_percent * total_tiles as f32) as usize;
|
|
let mut floor_tile_count = new_map.tiles.iter().filter(|a| a.is_walkable()).count();
|
|
let mut digger_count = 0;
|
|
while floor_tile_count < desired_floor_tiles {
|
|
let mut drunk_x;
|
|
let mut drunk_y;
|
|
match self.spawn_mode {
|
|
DrunkSpawnMode::StartingPoint => {
|
|
drunk_x = starting_position.x;
|
|
drunk_y = starting_position.y;
|
|
}
|
|
DrunkSpawnMode::Random => {
|
|
if digger_count == 0 {
|
|
drunk_x = starting_position.x;
|
|
drunk_y = starting_position.y;
|
|
} else {
|
|
drunk_x = rng.roll_dice(1, new_map.width - 3) + 1;
|
|
drunk_y = rng.roll_dice(1, new_map.height - 3) + 1;
|
|
}
|
|
}
|
|
}
|
|
let mut drunk_life = self.drunken_lifetime;
|
|
|
|
while drunk_life > 0 {
|
|
new_map.set_tile(drunk_x, drunk_y, Tile::wall());
|
|
new_map.paint(self.symmetry, self.brush_size, drunk_x, drunk_y);
|
|
|
|
let stagger_direction = rng.roll_dice(1, 4);
|
|
match stagger_direction {
|
|
1 => {
|
|
if drunk_x > 1 {
|
|
drunk_x -= 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;
|
|
}
|
|
|
|
digger_count += 1;
|
|
floor_tile_count = new_map.tiles.iter().filter(|a| a.is_walkable()).count();
|
|
}
|
|
|
|
new_map
|
|
}
|
|
}
|