2022-02-01 15:41:08 -05:00
|
|
|
//! Procedurally generated map builders
|
2022-01-18 09:32:48 -05:00
|
|
|
mod area_ending_point;
|
2021-12-15 10:08:05 -05:00
|
|
|
mod area_starting_points;
|
2021-12-10 14:36:27 -05:00
|
|
|
mod bsp_dungeon;
|
|
|
|
mod bsp_interior;
|
|
|
|
mod cellular_automata;
|
|
|
|
mod common;
|
2021-12-15 10:08:05 -05:00
|
|
|
mod cull_unreachable;
|
|
|
|
mod distant_exit;
|
2021-12-10 14:36:27 -05:00
|
|
|
mod dla;
|
2021-12-17 13:53:14 -05:00
|
|
|
mod door_placement;
|
2021-12-10 14:36:27 -05:00
|
|
|
mod drunkard;
|
2022-01-28 10:24:00 -05:00
|
|
|
mod dwarf_fort;
|
2022-01-04 15:02:50 -05:00
|
|
|
mod forest;
|
2022-01-10 09:57:59 -05:00
|
|
|
mod limestone_cavern;
|
2021-12-10 14:36:27 -05:00
|
|
|
mod maze;
|
2022-01-28 12:28:42 -05:00
|
|
|
mod mushroom_forest;
|
2021-12-10 14:36:27 -05:00
|
|
|
mod prefab_builder;
|
2021-12-14 16:29:36 -05:00
|
|
|
mod room_based_spawner;
|
|
|
|
mod room_based_stairs;
|
|
|
|
mod room_based_starting_position;
|
2021-12-15 15:50:17 -05:00
|
|
|
mod room_corner_rounding;
|
2021-12-17 11:29:16 -05:00
|
|
|
mod room_corridor_spawner;
|
2021-12-16 16:07:33 -05:00
|
|
|
mod room_draw;
|
2021-12-15 15:50:17 -05:00
|
|
|
mod room_exploder;
|
2021-12-16 13:26:48 -05:00
|
|
|
mod room_sorter;
|
|
|
|
mod rooms_corridors_bsp;
|
2021-12-16 11:38:53 -05:00
|
|
|
mod rooms_corridors_dogleg;
|
2021-12-17 10:57:03 -05:00
|
|
|
mod rooms_corridors_lines;
|
2021-12-17 10:44:35 -05:00
|
|
|
mod rooms_corridors_nearest;
|
2021-12-10 14:36:27 -05:00
|
|
|
mod simple_map;
|
2021-12-24 10:20:29 -05:00
|
|
|
mod town;
|
2021-12-10 14:36:27 -05:00
|
|
|
mod voronoi;
|
2021-12-15 10:08:05 -05:00
|
|
|
mod voronoi_spawning;
|
2021-12-10 14:36:27 -05:00
|
|
|
mod waveform_collapse;
|
|
|
|
|
2022-02-03 16:14:39 -05:00
|
|
|
use ::rltk::prelude::*;
|
2021-12-24 10:38:44 -05:00
|
|
|
use ::specs::prelude::*;
|
2022-01-18 09:32:48 -05:00
|
|
|
use area_ending_point::{AreaEndingPosition, XEnd, YEnd};
|
2021-12-15 10:08:05 -05:00
|
|
|
use area_starting_points::{AreaStartingPosition, XStart, YStart};
|
2021-12-10 14:36:27 -05:00
|
|
|
use bsp_dungeon::BspDungeonBuilder;
|
|
|
|
use bsp_interior::BspInteriorBuilder;
|
|
|
|
use cellular_automata::CellularAutomataBuilder;
|
2021-12-15 10:08:05 -05:00
|
|
|
use cull_unreachable::CullUnreachable;
|
|
|
|
use distant_exit::DistantExit;
|
2021-12-10 14:36:27 -05:00
|
|
|
use dla::DLABuilder;
|
2021-12-17 13:53:14 -05:00
|
|
|
use door_placement::DoorPlacement;
|
2021-12-10 14:36:27 -05:00
|
|
|
use drunkard::DrunkardsWalkBuilder;
|
2022-01-28 10:24:00 -05:00
|
|
|
use dwarf_fort::*;
|
2022-01-04 15:02:50 -05:00
|
|
|
use forest::forest_builder;
|
2022-01-18 09:32:48 -05:00
|
|
|
use limestone_cavern::{
|
|
|
|
limestone_cavern_builder, limestone_deep_cavern_builder, limestone_transition_builder,
|
|
|
|
};
|
2021-12-10 14:36:27 -05:00
|
|
|
use maze::MazeBuilder;
|
2022-01-28 12:28:42 -05:00
|
|
|
use mushroom_forest::*;
|
2021-12-10 14:36:27 -05:00
|
|
|
use prefab_builder::PrefabBuilder;
|
2021-12-14 16:29:36 -05:00
|
|
|
use room_based_spawner::RoomBasedSpawner;
|
|
|
|
use room_based_stairs::RoomBasedStairs;
|
|
|
|
use room_based_starting_position::RoomBasedStartingPosition;
|
2021-12-15 15:50:17 -05:00
|
|
|
use room_corner_rounding::RoomCornerRounder;
|
2021-12-17 11:29:16 -05:00
|
|
|
use room_corridor_spawner::CorridorSpawner;
|
2021-12-16 16:07:33 -05:00
|
|
|
use room_draw::RoomDrawer;
|
2021-12-15 15:50:17 -05:00
|
|
|
use room_exploder::RoomExploder;
|
2021-12-16 13:48:36 -05:00
|
|
|
use room_sorter::{RoomSort, RoomSorter};
|
2021-12-16 13:26:48 -05:00
|
|
|
use rooms_corridors_bsp::BspCorridors;
|
2021-12-16 11:38:53 -05:00
|
|
|
use rooms_corridors_dogleg::DoglegCorridors;
|
2021-12-17 10:57:03 -05:00
|
|
|
use rooms_corridors_lines::StraightLineCorridors;
|
2021-12-17 10:44:35 -05:00
|
|
|
use rooms_corridors_nearest::NearestCorridors;
|
2021-12-10 14:36:27 -05:00
|
|
|
use simple_map::SimpleMapBuilder;
|
2021-12-24 10:20:29 -05:00
|
|
|
use town::town_builder;
|
2021-12-10 14:36:27 -05:00
|
|
|
use voronoi::VoronoiCellBuilder;
|
2021-12-15 10:08:05 -05:00
|
|
|
use voronoi_spawning::VoronoiSpawning;
|
2021-12-10 14:36:27 -05:00
|
|
|
use waveform_collapse::WaveformCollapseBuilder;
|
|
|
|
|
2022-02-03 14:59:35 -05:00
|
|
|
use crate::rng::roll_dice;
|
2021-12-15 10:08:05 -05:00
|
|
|
use crate::{spawner, Map, Position, Rect, SHOW_MAPGEN_VISUALIZER};
|
|
|
|
|
2021-12-14 16:29:36 -05:00
|
|
|
pub struct BuilderMap {
|
|
|
|
pub spawn_list: Vec<(usize, String)>,
|
|
|
|
pub map: Map,
|
|
|
|
pub starting_position: Option<Position>,
|
|
|
|
pub rooms: Option<Vec<Rect>>,
|
2021-12-17 11:14:41 -05:00
|
|
|
pub corridors: Option<Vec<Vec<usize>>>,
|
2021-12-14 16:29:36 -05:00
|
|
|
pub history: Vec<Map>,
|
2021-12-17 16:35:30 -05:00
|
|
|
pub width: i32,
|
|
|
|
pub height: i32,
|
2021-12-14 16:29:36 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
impl BuilderMap {
|
2022-01-04 12:12:08 -05:00
|
|
|
fn new<S: ToString>(new_depth: i32, width: i32, height: i32, name: S) -> BuilderMap {
|
2021-12-15 10:08:05 -05:00
|
|
|
BuilderMap {
|
|
|
|
spawn_list: Vec::new(),
|
2022-01-04 12:12:08 -05:00
|
|
|
map: Map::new(new_depth, width, height, name),
|
2021-12-15 10:08:05 -05:00
|
|
|
starting_position: None,
|
|
|
|
rooms: None,
|
2021-12-17 11:14:41 -05:00
|
|
|
corridors: None,
|
2021-12-15 10:08:05 -05:00
|
|
|
history: Vec::new(),
|
2021-12-17 16:35:30 -05:00
|
|
|
width,
|
|
|
|
height,
|
2021-12-15 10:08:05 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-14 16:29:36 -05:00
|
|
|
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<Box<dyn InitialMapBuilder>>,
|
|
|
|
builders: Vec<Box<dyn MetaMapBuilder>>,
|
|
|
|
pub build_data: BuilderMap,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl BuilderChain {
|
2022-01-04 12:12:08 -05:00
|
|
|
pub fn new<S: ToString>(new_depth: i32, width: i32, height: i32, name: S) -> BuilderChain {
|
2021-12-14 16:29:36 -05:00
|
|
|
BuilderChain {
|
|
|
|
starter: None,
|
|
|
|
builders: Vec::new(),
|
2022-01-04 12:12:08 -05:00
|
|
|
build_data: BuilderMap::new(new_depth, width, height, name),
|
2021-12-14 16:29:36 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn start_with(&mut self, starter: Box<dyn InitialMapBuilder>) -> &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<dyn MetaMapBuilder>) -> &mut Self {
|
|
|
|
self.builders.push(metabuilder);
|
|
|
|
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
2022-02-03 14:59:35 -05:00
|
|
|
pub fn build_map(&mut self) -> &mut Self {
|
2021-12-14 16:29:36 -05:00
|
|
|
match &mut self.starter {
|
|
|
|
None => panic!("Cannot run a map builder chain without starting a build system"),
|
|
|
|
Some(starter) => {
|
|
|
|
// Build the starting map
|
2022-02-03 14:59:35 -05:00
|
|
|
starter.build_map(&mut self.build_data);
|
2021-12-14 16:29:36 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Build additional layers in turn
|
|
|
|
for metabuilder in self.builders.iter_mut() {
|
2022-02-03 14:59:35 -05:00
|
|
|
metabuilder.build_map(&mut self.build_data);
|
2021-12-14 16:29:36 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
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<Position> {
|
|
|
|
self.build_data.starting_position
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn get_snapshot_history(&self) -> Vec<Map> {
|
|
|
|
self.build_data.history.clone()
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn get_spawn_list(&self) -> &Vec<(usize, String)> {
|
|
|
|
&self.build_data.spawn_list
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub trait InitialMapBuilder {
|
2022-02-03 14:59:35 -05:00
|
|
|
fn build_map(&mut self, build_data: &mut BuilderMap);
|
2021-12-14 16:29:36 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
pub trait MetaMapBuilder {
|
2022-02-03 14:59:35 -05:00
|
|
|
fn build_map(&mut self, build_data: &mut BuilderMap);
|
2021-12-14 16:29:36 -05:00
|
|
|
}
|
2021-12-10 20:16:48 -05:00
|
|
|
|
2022-02-03 14:59:35 -05:00
|
|
|
pub fn random_start_position() -> (XStart, YStart) {
|
|
|
|
let x = match roll_dice(1, 3) {
|
2021-12-16 14:26:50 -05:00
|
|
|
1 => XStart::Left,
|
|
|
|
2 => XStart::Center,
|
|
|
|
_ => XStart::Right,
|
|
|
|
};
|
|
|
|
|
2022-02-03 14:59:35 -05:00
|
|
|
let y = match roll_dice(1, 3) {
|
2021-12-16 14:26:50 -05:00
|
|
|
1 => YStart::Bottom,
|
|
|
|
2 => YStart::Center,
|
|
|
|
_ => YStart::Top,
|
|
|
|
};
|
|
|
|
|
|
|
|
(x, y)
|
|
|
|
}
|
|
|
|
|
2022-02-03 14:59:35 -05:00
|
|
|
fn random_room_builder(builder: &mut BuilderChain) {
|
|
|
|
let build_roll = roll_dice(1, 3);
|
2021-12-16 14:26:50 -05:00
|
|
|
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
|
2022-02-03 14:59:35 -05:00
|
|
|
let room_sort = match roll_dice(1, 5) {
|
2021-12-16 14:26:50 -05:00
|
|
|
1 => RoomSort::LeftMost,
|
|
|
|
2 => RoomSort::RightMost,
|
|
|
|
3 => RoomSort::TopMost,
|
|
|
|
4 => RoomSort::BottomMost,
|
|
|
|
_ => RoomSort::Central,
|
|
|
|
};
|
|
|
|
builder.with(RoomSorter::new(room_sort));
|
|
|
|
|
2021-12-16 16:23:38 -05:00
|
|
|
builder.with(RoomDrawer::new());
|
|
|
|
|
2021-12-16 14:26:50 -05:00
|
|
|
// Pick a corridor type
|
2022-02-03 14:59:35 -05:00
|
|
|
match roll_dice(1, 4) {
|
2021-12-16 14:26:50 -05:00
|
|
|
1 => builder.with(DoglegCorridors::new()),
|
2021-12-17 11:33:21 -05:00
|
|
|
2 => builder.with(NearestCorridors::new()),
|
|
|
|
3 => builder.with(StraightLineCorridors::new()),
|
2021-12-16 14:26:50 -05:00
|
|
|
_ => builder.with(BspCorridors::new()),
|
|
|
|
};
|
|
|
|
|
2021-12-17 11:33:21 -05:00
|
|
|
// Maybe spawn stuff in the corridor
|
2022-02-03 14:59:35 -05:00
|
|
|
if roll_dice(1, 2) == 1 {
|
2021-12-17 11:33:21 -05:00
|
|
|
builder.with(CorridorSpawner::new());
|
|
|
|
}
|
|
|
|
|
2021-12-16 14:26:50 -05:00
|
|
|
// Add additional modifier
|
2022-02-03 14:59:35 -05:00
|
|
|
match roll_dice(1, 6) {
|
2021-12-16 14:26:50 -05:00
|
|
|
1 => {
|
|
|
|
builder.with(RoomExploder::new());
|
|
|
|
}
|
|
|
|
2 => {
|
|
|
|
builder.with(RoomCornerRounder::new());
|
|
|
|
}
|
|
|
|
_ => {}
|
|
|
|
};
|
2021-12-15 12:08:44 -05:00
|
|
|
}
|
2021-12-16 14:26:50 -05:00
|
|
|
|
|
|
|
// Find a starting position
|
2022-02-03 14:59:35 -05:00
|
|
|
match roll_dice(1, 2) {
|
2021-12-16 14:26:50 -05:00
|
|
|
1 => builder.with(RoomBasedStartingPosition::new()),
|
|
|
|
_ => {
|
2022-02-03 14:59:35 -05:00
|
|
|
let (start_x, start_y) = random_start_position();
|
2021-12-16 14:26:50 -05:00
|
|
|
builder.with(AreaStartingPosition::new(start_x, start_y))
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
// Find an exit
|
2022-02-03 14:59:35 -05:00
|
|
|
match roll_dice(1, 2) {
|
2021-12-16 14:26:50 -05:00
|
|
|
1 => builder.with(RoomBasedStairs::new()),
|
|
|
|
_ => builder.with(DistantExit::new()),
|
|
|
|
};
|
|
|
|
|
|
|
|
// Spawn stuff
|
2022-02-03 14:59:35 -05:00
|
|
|
match roll_dice(1, 2) {
|
2021-12-16 14:26:50 -05:00
|
|
|
1 => builder.with(RoomBasedSpawner::new()),
|
|
|
|
_ => builder.with(VoronoiSpawning::new()),
|
|
|
|
};
|
2021-12-15 12:08:44 -05:00
|
|
|
}
|
|
|
|
|
2022-02-03 14:59:35 -05:00
|
|
|
fn random_shape_builder(builder: &mut BuilderChain) {
|
|
|
|
let first_builder: Box<dyn InitialMapBuilder> = match roll_dice(1, 16) {
|
2021-12-16 14:26:50 -05:00
|
|
|
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),
|
|
|
|
};
|
2021-12-15 12:08:44 -05:00
|
|
|
|
2021-12-16 14:26:50 -05:00
|
|
|
// Set the start to the center and cull
|
2021-12-15 15:51:01 -05:00
|
|
|
builder
|
2021-12-16 14:26:50 -05:00
|
|
|
.start_with(first_builder)
|
2021-12-15 15:51:01 -05:00
|
|
|
.with(AreaStartingPosition::new(XStart::Center, YStart::Center))
|
2021-12-16 14:26:50 -05:00
|
|
|
.with(CullUnreachable::new());
|
|
|
|
|
|
|
|
// Now set the start ot a random starting area
|
2022-02-03 14:59:35 -05:00
|
|
|
let (start_x, start_y) = random_start_position();
|
2021-12-16 14:26:50 -05:00
|
|
|
builder.with(AreaStartingPosition::new(start_x, start_y));
|
|
|
|
|
|
|
|
// Setup an exit and spawn mobs
|
|
|
|
builder.with(VoronoiSpawning::new());
|
|
|
|
builder.with(DistantExit::new());
|
|
|
|
}
|
|
|
|
|
2022-02-03 14:59:35 -05:00
|
|
|
pub fn random_builder(new_depth: i32, width: i32, height: i32) -> BuilderChain {
|
2022-01-04 12:12:08 -05:00
|
|
|
let mut builder = BuilderChain::new(new_depth, width, height, "New Map");
|
2021-12-16 14:26:50 -05:00
|
|
|
|
2022-02-03 14:59:35 -05:00
|
|
|
match roll_dice(1, 2) {
|
|
|
|
1 => random_room_builder(&mut builder),
|
|
|
|
_ => random_shape_builder(&mut builder),
|
2021-12-16 14:26:50 -05:00
|
|
|
};
|
|
|
|
|
2022-02-03 14:59:35 -05:00
|
|
|
if roll_dice(1, 3) == 1 {
|
2021-12-16 14:26:50 -05:00
|
|
|
builder.with(WaveformCollapseBuilder::new());
|
2021-12-17 14:31:39 -05:00
|
|
|
|
|
|
|
// Now set the start to a random starting area
|
2022-02-03 14:59:35 -05:00
|
|
|
let (start_x, start_y) = random_start_position();
|
2021-12-17 14:31:39 -05:00
|
|
|
builder.with(AreaStartingPosition::new(start_x, start_y));
|
|
|
|
|
|
|
|
// Setup an exit and spawn mobs
|
|
|
|
builder
|
|
|
|
.with(VoronoiSpawning::new())
|
|
|
|
.with(DistantExit::new());
|
2021-12-16 14:26:50 -05:00
|
|
|
}
|
|
|
|
|
2022-02-03 14:59:35 -05:00
|
|
|
if roll_dice(1, 20) == 1 {
|
2021-12-16 14:26:50 -05:00
|
|
|
builder.with(PrefabBuilder::sectional(
|
|
|
|
prefab_builder::prefab_sections::UNDERGROUND_FORT,
|
|
|
|
));
|
|
|
|
}
|
|
|
|
|
2021-12-17 13:53:14 -05:00
|
|
|
builder.with(DoorPlacement::new());
|
2021-12-16 14:26:50 -05:00
|
|
|
builder.with(PrefabBuilder::vaults());
|
2021-12-14 14:16:18 -05:00
|
|
|
|
2021-12-14 16:29:36 -05:00
|
|
|
builder
|
2021-12-10 14:36:27 -05:00
|
|
|
}
|
2021-12-24 10:20:29 -05:00
|
|
|
|
2022-02-03 14:59:35 -05:00
|
|
|
pub fn level_builder(new_depth: i32, width: i32, height: i32) -> BuilderChain {
|
2022-02-03 16:14:39 -05:00
|
|
|
console::log(format!("Depth: {}", new_depth));
|
2021-12-24 10:20:29 -05:00
|
|
|
match new_depth {
|
2022-02-03 14:59:35 -05:00
|
|
|
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),
|
|
|
|
_ => random_builder(new_depth, width, height),
|
2021-12-24 10:20:29 -05:00
|
|
|
}
|
|
|
|
}
|