1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
//! Example generator usage:
//! ```
//! use rand::prelude::*;
//! use mapgen::{Map, MapFilter};
//! use mapgen::filter::DrunkardsWalk;
//! 
//! let mut rng = StdRng::seed_from_u64(100);
//! let gen = DrunkardsWalk::open_area();
//! let map = gen.modify_map(&mut rng, &Map::new(80, 50));
//! 
//! assert_eq!(map.width, 80);
//! assert_eq!(map.height, 50);
//! ```
//! 

use rand::prelude::*;
use crate::MapFilter;
use crate::{
    map::{Map, Symmetry, TileType},
    geometry::Point,
    random::Rng
};


#[derive(PartialEq, Copy, Clone)]
pub enum DrunkSpawnMode { StartingPoint, Random }

pub struct DrunkardsWalk {
    spawn_mode : DrunkSpawnMode,
    drunken_lifetime : i32,
    floor_percent: f32,
    brush_size: usize,
    symmetry: Symmetry
}

impl MapFilter for DrunkardsWalk {
    fn modify_map(&self, rng: &mut StdRng, map: &Map)  -> Map {
        self.build(rng, map)
    }
}

impl DrunkardsWalk {
    pub fn new( spawn_mode: DrunkSpawnMode, 
                drunken_lifetime: i32, 
                floor_percent: f32,
                brush_size: usize, 
                symmetry: Symmetry) -> Box<DrunkardsWalk>
    {
        Box::new(DrunkardsWalk{
            spawn_mode,
            drunken_lifetime,
            floor_percent,
            brush_size,
            symmetry
        })
    }

    pub fn open_area() -> Box<DrunkardsWalk> {
        Self::new(DrunkSpawnMode::StartingPoint, 400, 0.5, 1, Symmetry::None)
    }

    pub fn open_halls() -> Box<DrunkardsWalk> {
        Self::new(DrunkSpawnMode::Random, 400, 0.5, 1, Symmetry::None)
    }

    pub fn winding_passages() -> Box<DrunkardsWalk> {
        Self::new(DrunkSpawnMode::Random, 400, 0.4, 1, Symmetry::None)
    }

    pub fn fat_passages() -> Box<DrunkardsWalk> {
        Self::new(DrunkSpawnMode::Random, 400, 0.4, 2, Symmetry::None)
    }

    pub fn fearful_symmetry() -> Box<DrunkardsWalk> {
        Self::new(DrunkSpawnMode::Random, 400, 0.4, 1, Symmetry::Both)
    }
    
    fn build(&self, rng: &mut StdRng, map: &Map) -> Map {
        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, TileType::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 == TileType::Floor).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, TileType::Wall); 
                new_map.paint(self.symmetry, self.brush_size, drunk_x, drunk_y);
                // map.exit_point = Some(Point::new(drunk_x, drunk_y));

                let stagger_direction = rng.roll_dice(1, 4);
                match stagger_direction {
                    1 => { if drunk_x > 2 { drunk_x -= 1; } }
                    2 => { if drunk_x < new_map.width-2 { drunk_x += 1; } }
                    3 => { if drunk_y > 2 { 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 == TileType::Floor).count();
        }

        new_map
    }
}