//! Procedurally generated map builders mod area_ending_point; mod area_starting_points; mod bsp_dungeon; mod bsp_interior; mod cellular_automata; mod common; mod cull_unreachable; mod dark_elves; mod distant_exit; mod dla; mod door_placement; mod drunkard; mod dwarf_fort; mod forest; mod limestone_cavern; mod maze; mod mushroom_forest; mod prefab_builder; mod room_based_spawner; mod room_based_stairs; mod room_based_starting_position; mod room_corner_rounding; mod room_corridor_spawner; mod room_draw; mod room_exploder; mod room_sorter; mod rooms_corridors_bsp; mod rooms_corridors_dogleg; mod rooms_corridors_lines; mod rooms_corridors_nearest; mod simple_map; mod town; mod voronoi; mod voronoi_spawning; mod waveform_collapse; use ::specs::prelude::*; use area_ending_point::{AreaEndingPosition, XEnd, YEnd}; use area_starting_points::{AreaStartingPosition, XStart, YStart}; use bsp_dungeon::BspDungeonBuilder; use bsp_interior::BspInteriorBuilder; use cellular_automata::CellularAutomataBuilder; use cull_unreachable::CullUnreachable; use dark_elves::*; use distant_exit::DistantExit; use dla::DLABuilder; use door_placement::DoorPlacement; use drunkard::DrunkardsWalkBuilder; use dwarf_fort::*; use forest::forest_builder; use limestone_cavern::{ limestone_cavern_builder, limestone_deep_cavern_builder, limestone_transition_builder, }; use maze::MazeBuilder; use mushroom_forest::*; use prefab_builder::PrefabBuilder; use room_based_spawner::RoomBasedSpawner; use room_based_stairs::RoomBasedStairs; use room_based_starting_position::RoomBasedStartingPosition; use room_corner_rounding::RoomCornerRounder; use room_corridor_spawner::CorridorSpawner; use room_draw::RoomDrawer; use room_exploder::RoomExploder; use room_sorter::{RoomSort, RoomSorter}; use rooms_corridors_bsp::BspCorridors; use rooms_corridors_dogleg::DoglegCorridors; use rooms_corridors_lines::StraightLineCorridors; use rooms_corridors_nearest::NearestCorridors; use simple_map::SimpleMapBuilder; use town::town_builder; use voronoi::VoronoiCellBuilder; use voronoi_spawning::VoronoiSpawning; use waveform_collapse::WaveformCollapseBuilder; use crate::rng::roll_dice; use crate::{spawner, Map, Position, Rect, SHOW_MAPGEN_VISUALIZER}; pub struct BuilderMap { pub spawn_list: Vec<(usize, String)>, pub map: Map, pub starting_position: Option, pub rooms: Option>, pub corridors: Option>>, pub history: Vec, pub width: i32, pub height: i32, } impl BuilderMap { fn new(new_depth: i32, width: i32, height: i32, name: S) -> BuilderMap { BuilderMap { spawn_list: Vec::new(), map: Map::new(new_depth, width, height, name), starting_position: None, rooms: None, corridors: None, history: Vec::new(), width, height, } } fn take_snapshot(&mut self) { if SHOW_MAPGEN_VISUALIZER { let mut snapshot = self.map.clone(); for v in snapshot.revealed_tiles.iter_mut() { *v = true; } self.history.push(snapshot); } } } pub struct BuilderChain { starter: Option>, builders: Vec>, pub build_data: BuilderMap, } impl BuilderChain { pub fn new(new_depth: i32, width: i32, height: i32, name: S) -> BuilderChain { BuilderChain { starter: None, builders: Vec::new(), build_data: BuilderMap::new(new_depth, width, height, name), } } pub fn start_with(&mut self, starter: Box) -> &mut Self { match self.starter { None => self.starter = Some(starter), Some(_) => panic!("You can only have one starting builder."), } self } pub fn with(&mut self, metabuilder: Box) -> &mut Self { self.builders.push(metabuilder); self } pub fn build_map(&mut self) -> &mut Self { match &mut self.starter { None => panic!("Cannot run a map builder chain without starting a build system"), Some(starter) => { // Build the starting map starter.build_map(&mut self.build_data); } } // Build additional layers in turn for metabuilder in self.builders.iter_mut() { metabuilder.build_map(&mut self.build_data); } self } pub fn spawn_entities(&mut self, ecs: &mut World) -> &mut Self { for entity in self.build_data.spawn_list.iter() { spawner::spawn_entity(ecs, &(&entity.0, &entity.1)); } self } } pub trait InitialMapBuilder { fn build_map(&mut self, build_data: &mut BuilderMap); } pub trait MetaMapBuilder { fn build_map(&mut self, build_data: &mut BuilderMap); } pub fn random_start_position() -> (XStart, YStart) { let x = match roll_dice(1, 3) { 1 => XStart::Left, 2 => XStart::Center, _ => XStart::Right, }; let y = match roll_dice(1, 3) { 1 => YStart::Bottom, 2 => YStart::Center, _ => YStart::Top, }; (x, y) } fn random_room_builder(builder: &mut BuilderChain) { let build_roll = roll_dice(1, 3); match build_roll { 1 => builder.start_with(SimpleMapBuilder::new()), 2 => builder.start_with(BspDungeonBuilder::new()), _ => builder.start_with(BspInteriorBuilder::new()), }; // BSP Interior still makes holes in the walls if build_roll != 3 { // Sort by one of the 5 available algorithms let room_sort = match roll_dice(1, 5) { 1 => RoomSort::LeftMost, 2 => RoomSort::RightMost, 3 => RoomSort::TopMost, 4 => RoomSort::BottomMost, _ => RoomSort::Central, }; builder.with(RoomSorter::new(room_sort)); builder.with(RoomDrawer::new()); // Pick a corridor type match roll_dice(1, 4) { 1 => builder.with(DoglegCorridors::new()), 2 => builder.with(NearestCorridors::new()), 3 => builder.with(StraightLineCorridors::new()), _ => builder.with(BspCorridors::new()), }; // Maybe spawn stuff in the corridor if roll_dice(1, 2) == 1 { builder.with(CorridorSpawner::new()); } // Add additional modifier match roll_dice(1, 6) { 1 => { builder.with(RoomExploder::new()); } 2 => { builder.with(RoomCornerRounder::new()); } _ => {} }; } // Find a starting position match roll_dice(1, 2) { 1 => builder.with(RoomBasedStartingPosition::new()), _ => { let (start_x, start_y) = random_start_position(); builder.with(AreaStartingPosition::new(start_x, start_y)) } }; // Find an exit match roll_dice(1, 2) { 1 => builder.with(RoomBasedStairs::new()), _ => builder.with(DistantExit::new()), }; // Spawn stuff match roll_dice(1, 2) { 1 => builder.with(RoomBasedSpawner::new()), _ => builder.with(VoronoiSpawning::new()), }; } fn random_shape_builder(builder: &mut BuilderChain) { let first_builder: Box = match roll_dice(1, 16) { 1 => CellularAutomataBuilder::new(), 2 => DrunkardsWalkBuilder::open_area(), 3 => DrunkardsWalkBuilder::open_halls(), 4 => DrunkardsWalkBuilder::winding_passages(), 5 => DrunkardsWalkBuilder::fat_passages(), 6 => DrunkardsWalkBuilder::fearful_symmetry(), 7 => MazeBuilder::new(), 8 => DLABuilder::walk_inwards(), 9 => DLABuilder::walk_outwards(), 10 => DLABuilder::central_attractor(), 11 => DLABuilder::insectoid(), 12 => VoronoiCellBuilder::pythagoras(), 13 => VoronoiCellBuilder::manhattan(), _ => PrefabBuilder::constant(prefab_builder::prefab_levels::WFC_POPULATED), }; // Set the start to the center and cull builder .start_with(first_builder) .with(AreaStartingPosition::new(XStart::Center, YStart::Center)) .with(CullUnreachable::new()); // Now set the start ot a random starting area let (start_x, start_y) = random_start_position(); builder.with(AreaStartingPosition::new(start_x, start_y)); // Setup an exit and spawn mobs builder.with(VoronoiSpawning::new()); builder.with(DistantExit::new()); } pub fn random_builder(new_depth: i32, width: i32, height: i32) -> BuilderChain { let mut builder = BuilderChain::new(new_depth, width, height, "New Map"); match roll_dice(1, 2) { 1 => random_room_builder(&mut builder), _ => random_shape_builder(&mut builder), }; if roll_dice(1, 3) == 1 { builder.with(WaveformCollapseBuilder::new()); // Now set the start to a random starting area let (start_x, start_y) = random_start_position(); builder.with(AreaStartingPosition::new(start_x, start_y)); // Setup an exit and spawn mobs builder .with(VoronoiSpawning::new()) .with(DistantExit::new()); } if roll_dice(1, 20) == 1 { builder.with(PrefabBuilder::sectional( prefab_builder::prefab_sections::UNDERGROUND_FORT, )); } builder.with(DoorPlacement::new()); builder.with(PrefabBuilder::vaults()); builder } pub fn level_builder(new_depth: i32, width: i32, height: i32) -> BuilderChain { #[cfg(feature = "debug")] ::bracket_lib::prelude::console::log(format!("Depth: {}", new_depth)); match new_depth { 1 => town_builder(new_depth, width, height), 2 => forest_builder(new_depth, width, height), 3 => limestone_cavern_builder(new_depth, width, height), 4 => limestone_deep_cavern_builder(new_depth, width, height), 5 => limestone_transition_builder(new_depth, width, height), 6 => dwarf_fort_builder(new_depth, width, height), 7 => mushroom_entrance(new_depth, width, height), 8 => mushroom_builder(new_depth, width, height), 9 => mushroom_exit(new_depth, width, height), 10 => dark_elf_city(new_depth, width, height), 11 => dark_elf_plaza(new_depth, width, height), _ => random_builder(new_depth, width, height), } }