here_be_dragons/src/filter/drunkard.rs

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
}
}