use super::prefab_builder::prefab_sections; use super::{ AreaEndingPosition, AreaStartingPosition, BspDungeonBuilder, BuilderChain, BuilderMap, CellularAutomataBuilder, CullUnreachable, DLABuilder, DistantExit, DrunkardsWalkBuilder, MetaMapBuilder, NearestCorridors, PrefabBuilder, RoomBasedSpawner, RoomDrawer, RoomExploder, RoomSort, RoomSorter, VoronoiSpawning, XEnd, XStart, YEnd, YStart, }; use crate::map::TileType; use crate::rng::roll_dice; pub fn limestone_cavern_builder(new_depth: i32, width: i32, height: i32) -> BuilderChain { let mut chain = BuilderChain::new(new_depth, width, height, "Limestone Caverns"); chain .start_with(DrunkardsWalkBuilder::winding_passages()) .with(AreaStartingPosition::new(XStart::Center, YStart::Center)) .with(CullUnreachable::new()) .with(AreaStartingPosition::new(XStart::Left, YStart::Center)) .with(VoronoiSpawning::new()) .with(DistantExit::new()) .with(CaveDecorator::new()); chain } pub fn limestone_deep_cavern_builder(new_depth: i32, width: i32, height: i32) -> BuilderChain { let mut chain = BuilderChain::new(new_depth, width, height, "Deep Limestone Caverns"); chain .start_with(DLABuilder::central_attractor()) .with(AreaStartingPosition::new(XStart::Left, YStart::Top)) .with(VoronoiSpawning::new()) .with(DistantExit::new()) .with(CaveDecorator::new()) .with(PrefabBuilder::sectional(prefab_sections::ORC_CAMP)); chain } pub fn limestone_transition_builder(new_depth: i32, width: i32, height: i32) -> BuilderChain { let mut chain = BuilderChain::new(new_depth, width, height, "Dwarf Fort - Upper Reaches"); chain .start_with(CellularAutomataBuilder::new()) .with(AreaStartingPosition::new(XStart::Center, YStart::Center)) .with(CullUnreachable::new()) .with(AreaStartingPosition::new(XStart::Left, YStart::Center)) .with(VoronoiSpawning::new()) .with(CaveDecorator::new()) .with(CaveTransition::new()) .with(AreaStartingPosition::new(XStart::Left, YStart::Center)) .with(CullUnreachable::new()) .with(AreaEndingPosition::new(XEnd::Right, YEnd::Center)); chain } pub struct CaveDecorator {} impl MetaMapBuilder for CaveDecorator { fn build_map(&mut self, build_data: &mut BuilderMap) { self.build(build_data); } } impl CaveDecorator { #[allow(dead_code)] pub fn new() -> Box { Box::new(CaveDecorator {}) } fn build(&mut self, build_data: &mut BuilderMap) { let old_map = build_data.map.clone(); for (idx, tt) in build_data.map.tiles.iter_mut().enumerate() { // Gravel Spawning if *tt == TileType::Floor && roll_dice(1, 6) == 1 { *tt = TileType::Gravel; } else if *tt == TileType::Floor && roll_dice(1, 10) == 1 { // Spawn passable pools *tt = TileType::ShallowWater; } else if *tt == TileType::Wall { // Spawn deep pools and stalactites let mut neighbors = 0; let x = idx as i32 % old_map.width; let y = idx as i32 / old_map.width; if x > 0 && old_map.tiles[idx - 1] == TileType::Wall { neighbors += 1; } if x < old_map.width - 2 && old_map.tiles[idx + 1] == TileType::Wall { neighbors += 1 } if y > 0 && old_map.tiles[idx - old_map.width as usize] == TileType::Wall { neighbors += 1 } if y < old_map.height - 2 && old_map.tiles[idx + old_map.width as usize] == TileType::Wall { neighbors += 1 } if neighbors == 2 { *tt = TileType::DeepWater; } else if neighbors == 1 { *tt = match roll_dice(1, 4) { 1 => TileType::Stalactite, 2 => TileType::Stalagmite, _ => *tt, } } } } build_data.take_snapshot(); build_data.map.outdoors = false; } } pub struct CaveTransition {} impl MetaMapBuilder for CaveTransition { fn build_map(&mut self, build_data: &mut BuilderMap) { self.build(build_data); } } impl CaveTransition { #[allow(dead_code)] pub fn new() -> Box { Box::new(CaveTransition {}) } fn build(&mut self, build_data: &mut BuilderMap) { build_data.map.depth = 5; build_data.take_snapshot(); // Build a BSP-based dungeon let mut builder = BuilderChain::new(5, build_data.width, build_data.height, "New Map"); builder .start_with(BspDungeonBuilder::new()) .with(RoomDrawer::new()) .with(RoomSorter::new(RoomSort::RightMost)) .with(NearestCorridors::new()) .with(RoomExploder::new()) .with(RoomBasedSpawner::new()); builder.build_map(); // Add the history to our history for h in builder.build_data.history.iter() { build_data.history.push(h.clone()); } build_data.take_snapshot(); // Copy the right half of the BSP map into our map for x in build_data.map.width / 2..build_data.map.width { for y in 0..build_data.map.height { let idx = build_data.map.xy_idx(x, y); build_data.map.tiles[idx] = builder.build_data.map.tiles[idx]; } } build_data.take_snapshot(); // Keep Voronoi spawn data from the left half of the map let w = build_data.map.width; build_data.spawn_list.retain(|s| { let x = s.0 as i32 / w; x < w / 2 }); // Keep room spawn data from the right half of the map for s in builder.build_data.spawn_list.iter() { let x = s.0 as i32 / w; if x < w / 2 { build_data.spawn_list.push(s.clone()); } } } }