mod area_ending_point; mod area_starting_points; mod bsp_dungeon; mod bsp_interior; mod cellular_automata; mod common; mod cull_unreachable; 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 ::rltk::RandomNumberGenerator; 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 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::{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, 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); } pub fn random_start_position(rng: &mut rltk::RandomNumberGenerator) -> (XStart, YStart) { let x = match rng.roll_dice(1, 3) { 1 => XStart::Left, 2 => XStart::Center, _ => XStart::Right, }; let y = match rng.roll_dice(1, 3) { 1 => YStart::Bottom, 2 => YStart::Center, _ => YStart::Top, }; (x, y) } fn random_room_builder(rng: &mut RandomNumberGenerator, builder: &mut BuilderChain) { let build_roll = rng.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 rng.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 rng.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 rng.roll_dice(1, 2) == 1 { builder.with(CorridorSpawner::new()); } // Add additional modifier match rng.roll_dice(1, 6) { 1 => { builder.with(RoomExploder::new()); } 2 => { builder.with(RoomCornerRounder::new()); } _ => {} }; } // Find a starting position match rng.roll_dice(1, 2) { 1 => builder.with(RoomBasedStartingPosition::new()), _ => { let (start_x, start_y) = random_start_position(rng); builder.with(AreaStartingPosition::new(start_x, start_y)) } }; // Find an exit match rng.roll_dice(1, 2) { 1 => builder.with(RoomBasedStairs::new()), _ => builder.with(DistantExit::new()), }; // Spawn stuff match rng.roll_dice(1, 2) { 1 => builder.with(RoomBasedSpawner::new()), _ => builder.with(VoronoiSpawning::new()), }; } fn random_shape_builder(rng: &mut RandomNumberGenerator, builder: &mut BuilderChain) { let first_builder: Box = match rng.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(rng); 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, rng: &mut RandomNumberGenerator, width: i32, height: i32, ) -> BuilderChain { let mut builder = BuilderChain::new(new_depth, width, height, "New Map"); match rng.roll_dice(1, 2) { 1 => random_room_builder(rng, &mut builder), _ => random_shape_builder(rng, &mut builder), }; if rng.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(rng); builder.with(AreaStartingPosition::new(start_x, start_y)); // Setup an exit and spawn mobs builder .with(VoronoiSpawning::new()) .with(DistantExit::new()); } if rng.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, rng: &mut RandomNumberGenerator, width: i32, height: i32, ) -> BuilderChain { rltk::console::log(format!("Depth: {}", new_depth)); match new_depth { 1 => town_builder(new_depth, rng, width, height), 2 => forest_builder(new_depth, rng, width, height), 3 => limestone_cavern_builder(new_depth, rng, width, height), 4 => limestone_deep_cavern_builder(new_depth, rng, width, height), 5 => limestone_transition_builder(new_depth, rng, width, height), 6 => dwarf_fort_builder(new_depth, rng, width, height), 7 => mushroom_entrance(new_depth, rng, width, height), 8 => mushroom_builder(new_depth, rng, width, height), _ => random_builder(new_depth, rng, width, height), } }