use rand::{thread_rng, Rng};
use super::{DoesDunGen, MapId, SupportsDunGen, MAPS};
use crate::geometry::{
CardinalDirection, Containment, HasHeight, HasWidth, IsPosition, Position, ProvidesCount,
};
pub struct EdgePortalsGenerator<TProvidesCount>
where
TProvidesCount: ProvidesCount + Sized,
{
provides_count: TProvidesCount,
placed_map_box_func: Box<dyn Fn() -> MapId>,
}
impl<TProvidesCount> EdgePortalsGenerator<TProvidesCount>
where
TProvidesCount: ProvidesCount + Sized,
{
pub fn new(
provides_count: TProvidesCount,
placed_map_box_func: Box<dyn Fn() -> MapId>,
) -> Self {
Self {
provides_count,
placed_map_box_func,
}
}
}
impl<TProvidesCount> DoesDunGen for EdgePortalsGenerator<TProvidesCount>
where
TProvidesCount: ProvidesCount + Sized,
{
fn dun_gen(&self, target: &mut dyn SupportsDunGen) {
let map_id = target.get_map_id();
self.dun_gen_map(map_id);
}
fn dun_gen_map(&self, map_id: MapId) {
let mut data = Vec::<(Position, CardinalDirection)>::new();
{
let maps = &MAPS.read();
let map = &mut maps[map_id].write();
let area = *map.area();
if area.width() < 3 || area.height() < 3 {
return;
}
let mut edge_tiles = Vec::new();
for x in (map.left() + 1)..map.right() {
let position = Position::new(x, 0);
if map.contains_position(position) == Containment::Intersects {
edge_tiles.push(position);
}
}
for y in (map.top() + 1)..map.bottom() {
{
let position = Position::new(map.left(), y);
if map.contains_position(position) == Containment::Intersects {
edge_tiles.push(position);
}
}
{
let position = Position::new(map.right(), y);
if map.contains_position(position) == Containment::Intersects {
edge_tiles.push(position);
}
}
}
for x in (map.left() + 1)..map.right() {
let position = Position::new(x, map.bottom());
if map.contains_position(position) == Containment::Intersects {
edge_tiles.push(position);
}
}
let count = self.provides_count.provide_count();
let mut rng = thread_rng();
for _ in 0..count {
let index = rng.gen_range(0..edge_tiles.len());
let edge_portal_position = edge_tiles[index];
edge_tiles.truncate(edge_tiles.len() - 1);
data.push((
edge_portal_position,
if edge_portal_position.x() == map.left() {
CardinalDirection::East
} else if edge_portal_position.x() == map.right() {
CardinalDirection::West
} else if edge_portal_position.y() == map.top() {
CardinalDirection::South
} else {
CardinalDirection::North
},
));
}
}
let data = data
.iter()
.map(|(local_position, portal_to_map_facing)| {
(
local_position,
portal_to_map_facing,
(self.placed_map_box_func)(),
)
})
.collect::<Vec<_>>();
{
let maps = &MAPS.read();
let map = &mut maps[map_id].write();
for data in data {
map.add_portal(*data.0, *data.1, Position::zero(), data.2);
}
}
}
}