use std::collections::{HashSet, VecDeque};
use std::sync::RwLock;
use super::{DoesDunGen, MapId, Portal, SupportsDunGen, MAPS};
pub struct MergePortalMapsAsSubMapsGenerator<TPortalFilter>
where
TPortalFilter: Fn(&Portal) -> bool,
{
portal_filter: TPortalFilter,
recursion_depth: usize,
visited: RwLock<HashSet<MapId>>,
}
impl<TPortalFilter> MergePortalMapsAsSubMapsGenerator<TPortalFilter>
where
TPortalFilter: Fn(&Portal) -> bool,
{
#[must_use]
pub fn new(recursion_depth: usize, portal_filter: TPortalFilter) -> Self {
Self {
portal_filter,
recursion_depth,
visited: RwLock::new(HashSet::new()),
}
}
}
impl<TPortalFilter> DoesDunGen for MergePortalMapsAsSubMapsGenerator<TPortalFilter>
where
TPortalFilter: Fn(&Portal) -> bool,
{
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 visited = self.visited.write().unwrap();
if visited.contains(&map_id) {
return;
}
visited.insert(map_id);
if self.recursion_depth == 0 {
return;
}
let recursion_depth = self.recursion_depth;
let maps = &MAPS.read();
let map = &mut maps[map_id].write();
let mut on_maps = VecDeque::new();
let mut positions_map_ids = Vec::new();
{
for portal in map.portals() {
if (self.portal_filter)(portal) {
let portal_map_id = portal.target();
if visited.contains(&portal_map_id) {
continue;
}
visited.insert(portal_map_id);
let portal_map_position =
*portal.local_position() - (*portal.portal_to_map_position());
positions_map_ids.push((portal_map_position, portal_map_id));
}
}
for (portal_map_position, portal_map_id) in &positions_map_ids {
map.add_sub_map(*portal_map_position, *portal_map_id);
if recursion_depth > 1 {
on_maps.push_back((*portal_map_position, recursion_depth - 1, *portal_map_id));
}
}
}
if recursion_depth == 1 {
return;
}
while !on_maps.is_empty() {
let (accumulated_position, new_recursion_depth, target_map_id) =
on_maps.pop_front().unwrap();
if new_recursion_depth == 0 {
continue;
}
positions_map_ids.clear();
let target_map = &maps[target_map_id].read();
for portal in target_map.portals() {
if (self.portal_filter)(portal) {
let portal_map_id = portal.target();
if visited.contains(&portal_map_id) {
continue;
}
visited.insert(portal_map_id);
let portal_map_position =
*portal.local_position() - (*portal.portal_to_map_position());
positions_map_ids.push((portal_map_position, portal_map_id));
}
}
for (portal_map_position, portal_map_id) in &positions_map_ids {
map.add_sub_map(accumulated_position + *portal_map_position, *portal_map_id);
on_maps.push_back((
accumulated_position + *portal_map_position,
new_recursion_depth - 1,
*portal_map_id,
));
}
}
}
}