mapgen/filter/
drunkard.rs

1//! Example generator usage:
2//! ```
3//! use rand::prelude::*;
4//! use mapgen::{MapBuffer, MapFilter};
5//! use mapgen::filter::DrunkardsWalk;
6//! 
7//! let mut rng = StdRng::seed_from_u64(100);
8//! let gen = DrunkardsWalk::open_area();
9//! let map = gen.modify_map(&mut rng, &MapBuffer::new(80, 50));
10//! 
11//! assert_eq!(map.width, 80);
12//! assert_eq!(map.height, 50);
13//! ```
14//! 
15
16use rand::prelude::*;
17use crate::MapFilter;
18use crate::{
19    map_buffer::{MapBuffer, Symmetry},
20    geometry::Point,
21    random::Rng
22};
23
24
25#[derive(PartialEq, Copy, Clone)]
26pub enum DrunkSpawnMode { StartingPoint, Random }
27
28pub struct DrunkardsWalk {
29    spawn_mode : DrunkSpawnMode,
30    drunken_lifetime : i32,
31    floor_percent: f32,
32    brush_size: usize,
33    symmetry: Symmetry
34}
35
36impl MapFilter for DrunkardsWalk {
37    fn modify_map(&self, rng: &mut StdRng, map: &MapBuffer)  -> MapBuffer {
38        self.build(rng, map)
39    }
40}
41
42impl DrunkardsWalk {
43    pub fn new( spawn_mode: DrunkSpawnMode, 
44                drunken_lifetime: i32, 
45                floor_percent: f32,
46                brush_size: usize, 
47                symmetry: Symmetry) -> Box<DrunkardsWalk>
48    {
49        Box::new(DrunkardsWalk{
50            spawn_mode,
51            drunken_lifetime,
52            floor_percent,
53            brush_size,
54            symmetry
55        })
56    }
57
58    pub fn open_area() -> Box<DrunkardsWalk> {
59        Self::new(DrunkSpawnMode::StartingPoint, 400, 0.5, 1, Symmetry::None)
60    }
61
62    pub fn open_halls() -> Box<DrunkardsWalk> {
63        Self::new(DrunkSpawnMode::Random, 400, 0.5, 1, Symmetry::None)
64    }
65
66    pub fn winding_passages() -> Box<DrunkardsWalk> {
67        Self::new(DrunkSpawnMode::Random, 400, 0.4, 1, Symmetry::None)
68    }
69
70    pub fn fat_passages() -> Box<DrunkardsWalk> {
71        Self::new(DrunkSpawnMode::Random, 400, 0.4, 2, Symmetry::None)
72    }
73
74    pub fn fearful_symmetry() -> Box<DrunkardsWalk> {
75        Self::new(DrunkSpawnMode::Random, 400, 0.4, 1, Symmetry::Both)
76    }
77    
78    fn build(&self, rng: &mut StdRng, map: &MapBuffer) -> MapBuffer {
79        let mut new_map = map.clone();
80        // Set a central starting point
81        let starting_position = Point::new( new_map.width / 2, new_map.height / 2 );
82        new_map.set_walkable(starting_position.x, starting_position.y, true);
83
84        let total_tiles = new_map.width * new_map.height;
85        let desired_floor_tiles = (self.floor_percent * total_tiles as f32) as usize;
86        let mut floor_tile_count = new_map.walkables.iter().filter(|&&a| a).count();
87        let mut digger_count = 0;
88        while floor_tile_count  < desired_floor_tiles {
89            let mut drunk_x;
90            let mut drunk_y;
91            match self.spawn_mode {
92                DrunkSpawnMode::StartingPoint => {
93                    drunk_x = starting_position.x;
94                    drunk_y = starting_position.y;
95                }
96                DrunkSpawnMode::Random => {
97                    if digger_count == 0 {
98                        drunk_x = starting_position.x;
99                        drunk_y = starting_position.y;
100                    } else {
101                        drunk_x = rng.roll_dice(1, new_map.width - 3) + 1;
102                        drunk_y = rng.roll_dice(1, new_map.height - 3) + 1;
103                    }
104                }
105            }
106            let mut drunk_life = self.drunken_lifetime;
107
108            while drunk_life > 0 {
109                new_map.set_walkable(drunk_x, drunk_y, false); 
110                new_map.paint(self.symmetry, self.brush_size, drunk_x, drunk_y);
111
112                let stagger_direction = rng.roll_dice(1, 4);
113                match stagger_direction {
114                    1 => { if drunk_x > 1 { drunk_x -= 1; } }
115                    2 => { if drunk_x < new_map.width-2 { drunk_x += 1; } }
116                    3 => { if drunk_y > 1 { drunk_y -=1; } }
117                    _ => { if drunk_y < new_map.height-2 { drunk_y += 1; } }
118                }
119
120                drunk_life -= 1;
121            }
122
123            digger_count += 1;
124            floor_tile_count = new_map.walkables.iter().filter(|&&a| a).count();
125        }
126
127        new_map
128    }
129}