mod area_starting_points; mod bsp_dungeon; mod bsp_interior; mod cellular_automata; mod common; mod cull_unreachable; mod distant_exit; mod dla; mod drunkard; mod maze; mod prefab_builder; mod room_based_spawner; mod room_based_stairs; mod room_based_starting_position; mod simple_map; mod voronoi; mod voronoi_spawning; mod waveform_collapse; use ::rltk::RandomNumberGenerator; use area_starting_points::{AreaStartingPosition, XStart, YStart}; use bsp_dungeon::BspDungeonBuilder; use bsp_interior::BspInteriorBuilder; use cellular_automata::CellularAutomataBuilder; use cull_unreachable::CullUnreachable; use distant_exit::DistantExit; use dla::DLABuilder; use drunkard::DrunkardsWalkBuilder; use maze::MazeBuilder; use prefab_builder::PrefabBuilder; use room_based_spawner::RoomBasedSpawner; use room_based_stairs::RoomBasedStairs; use room_based_starting_position::RoomBasedStartingPosition; use simple_map::SimpleMapBuilder; use specs::prelude::*; use voronoi::VoronoiCellBuilder; use voronoi_spawning::VoronoiSpawning; use waveform_collapse::WaveformCollapseBuilder; 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 history: Vec, } impl BuilderMap { fn new(new_depth: i32) -> BuilderMap { BuilderMap { spawn_list: Vec::new(), map: Map::new(new_depth), starting_position: None, rooms: None, history: Vec::new(), } } 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) -> BuilderChain { BuilderChain { starter: None, builders: Vec::new(), build_data: BuilderMap::new(new_depth), } } 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, rng: &mut RandomNumberGenerator) -> &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(rng, &mut self.build_data); } } // Build additional layers in turn for metabuilder in self.builders.iter_mut() { metabuilder.build_map(rng, &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 fn get_map(&self) -> Map { self.build_data.map.clone() } pub fn get_starting_position(&self) -> Option { self.build_data.starting_position } pub fn get_snapshot_history(&self) -> Vec { self.build_data.history.clone() } pub fn get_spawn_list(&self) -> &Vec<(usize, String)> { &self.build_data.spawn_list } } pub trait InitialMapBuilder { fn build_map(&mut self, rng: &mut RandomNumberGenerator, build_data: &mut BuilderMap); } pub trait MetaMapBuilder { fn build_map(&mut self, rng: &mut RandomNumberGenerator, build_data: &mut BuilderMap); } fn random_initial_builder(rng: &mut RandomNumberGenerator) -> (Box, bool) { match rng.roll_dice(1, 17) { 1 => (BspDungeonBuilder::new(), true), 2 => (BspInteriorBuilder::new(), true), 3 => (CellularAutomataBuilder::new(), false), 4 => (DrunkardsWalkBuilder::open_area(), false), 5 => (DrunkardsWalkBuilder::open_halls(), false), 6 => (DrunkardsWalkBuilder::winding_passages(), false), 7 => (DrunkardsWalkBuilder::fat_passages(), false), 8 => (DrunkardsWalkBuilder::fearful_symmetry(), false), 9 => (MazeBuilder::new(), false), 10 => (DLABuilder::walk_inwards(), false), 11 => (DLABuilder::walk_outwards(), false), 12 => (DLABuilder::central_attractor(), false), 13 => (DLABuilder::insectoid(), false), 14 => (VoronoiCellBuilder::pythagoras(), false), 15 => (VoronoiCellBuilder::manhattan(), false), 16 => ( PrefabBuilder::constant(prefab_builder::prefab_levels::WFC_POPULATED), false, ), _ => (SimpleMapBuilder::new(), true), } } pub fn random_builder(new_depth: i32, rng: &mut RandomNumberGenerator) -> BuilderChain { let mut builder = BuilderChain::new(new_depth); let (random_starter, has_rooms) = random_initial_builder(rng); builder.start_with(random_starter); if has_rooms { builder .with(RoomBasedSpawner::new()) .with(RoomBasedStairs::new()) .with(RoomBasedStartingPosition::new()); } else { builder .with(AreaStartingPosition::new(XStart::Center, YStart::Center)) .with(CullUnreachable::new()) .with(VoronoiSpawning::new()) .with(DistantExit::new()); } if rng.roll_dice(1, 3) == 1 { builder.with(WaveformCollapseBuilder::new()); } if rng.roll_dice(1, 20) == 1 { builder.with(PrefabBuilder::sectional( prefab_builder::prefab_sections::UNDERGROUND_FORT, )); } builder.with(PrefabBuilder::vaults()); builder }