rust-rl/src/map_builders/mod.rs

311 lines
10 KiB
Rust

use super::{spawner, Map, Position, Rect, TileType, SHOW_MAPGEN};
mod bsp_dungeon;
use bsp_dungeon::BspDungeonBuilder;
mod bsp_interior;
use bsp_interior::BspInteriorBuilder;
mod cellular_automata;
use cellular_automata::CellularAutomataBuilder;
mod common;
mod dla;
use dla::DLABuilder;
mod drunkard;
use drunkard::DrunkardsWalkBuilder;
mod maze;
use maze::MazeBuilder;
mod simple_map;
use simple_map::SimpleMapBuilder;
mod voronoi;
use voronoi::VoronoiBuilder;
mod prefab_builder;
use prefab_builder::PrefabBuilder;
mod room_based_spawner;
mod wfc;
use room_based_spawner::*;
mod room_based_stairs;
use room_based_stairs::*;
mod room_based_starting_position;
use room_based_starting_position::*;
mod area_starting_points;
use area_starting_points::{AreaStartingPosition, XStart, YStart};
mod cull_unreachable;
use cull_unreachable::CullUnreachable;
mod distant_exit;
use distant_exit::DistantExit;
mod voronoi_spawning;
use common::*;
use specs::prelude::*;
use voronoi_spawning::VoronoiSpawning;
use wfc::WaveFunctionCollapseBuilder;
mod room_exploder;
use room_exploder::RoomExploder;
mod room_corner_rounding;
use room_corner_rounding::RoomCornerRounder;
mod rooms_corridors_dogleg;
use rooms_corridors_dogleg::DoglegCorridors;
mod rooms_corridors_bsp;
use rooms_corridors_bsp::BspCorridors;
mod room_sorter;
use room_sorter::{RoomSort, RoomSorter};
mod room_draw;
use room_draw::RoomDrawer;
mod rooms_corridors_nearest;
use rooms_corridors_nearest::NearestCorridors;
mod rooms_corridors_bresenham;
use rooms_corridors_bresenham::BresenhamCorridors;
mod rooms_corridors_spawner;
use rooms_corridors_spawner::CorridorSpawner;
mod door_placement;
use door_placement::DoorPlacement;
// Shared data to be passed around build chain
pub struct BuilderMap {
pub spawn_list: Vec<(usize, String)>,
pub map: Map,
pub starting_position: Option<Position>,
pub rooms: Option<Vec<Rect>>,
pub corridors: Option<Vec<Vec<usize>>>,
pub history: Vec<Map>,
}
impl BuilderMap {
fn take_snapshot(&mut self) {
if SHOW_MAPGEN {
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 {
pub fn new(new_depth: i32) -> BuilderChain {
BuilderChain {
starter: None,
builders: Vec::new(),
build_data: BuilderMap {
spawn_list: Vec::new(),
map: Map::new(new_depth),
starting_position: None,
rooms: None,
corridors: None,
history: Vec::new(),
},
}
}
pub fn start_with(&mut self, starter: Box<dyn InitialMapBuilder>) {
match self.starter {
None => self.starter = Some(starter),
Some(_) => panic!("You can only have one starting builder."),
};
}
pub fn with(&mut self, metabuilder: Box<dyn MetaMapBuilder>) {
self.builders.push(metabuilder);
}
pub fn build_map(&mut self, rng: &mut rltk::RandomNumberGenerator) {
match &mut self.starter {
None => panic!("Cannot run a map builder chain without a starting 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);
}
}
pub fn spawn_entities(&mut self, ecs: &mut World) {
for entity in self.build_data.spawn_list.iter() {
spawner::spawn_entity(ecs, &(&entity.0, &entity.1));
}
}
}
pub trait InitialMapBuilder {
fn build_map(&mut self, rng: &mut rltk::RandomNumberGenerator, build_data: &mut BuilderMap);
}
pub trait MetaMapBuilder {
fn build_map(&mut self, rng: &mut rltk::RandomNumberGenerator, build_data: &mut BuilderMap);
}
fn random_start_position(rng: &mut rltk::RandomNumberGenerator) -> (XStart, YStart) {
let x;
let xroll = rng.roll_dice(1, 3);
match xroll {
1 => x = XStart::LEFT,
2 => x = XStart::CENTRE,
_ => x = XStart::RIGHT,
}
let y;
let yroll = rng.roll_dice(1, 3);
match yroll {
1 => y = YStart::BOTTOM,
2 => y = YStart::CENTRE,
_ => y = YStart::TOP,
}
(x, y)
}
fn random_room_builder(rng: &mut rltk::RandomNumberGenerator, builder: &mut BuilderChain) {
let build_roll = rng.roll_dice(1, 3);
// Start with a room builder.
match build_roll {
1 => builder.start_with(SimpleMapBuilder::new()),
2 => builder.start_with(BspDungeonBuilder::new()),
_ => builder.start_with(BspInteriorBuilder::new()),
}
// BspInterior makes its own doorways. If we're not using that one,
// select a sorting method, a type of corridor, and modifiers.
if build_roll != 3 {
// Sort by one of the 5 available algorithms
let sort_roll = rng.roll_dice(1, 5);
match sort_roll {
1 => builder.with(RoomSorter::new(RoomSort::LEFTMOST)),
2 => builder.with(RoomSorter::new(RoomSort::RIGHTMOST)),
3 => builder.with(RoomSorter::new(RoomSort::TOPMOST)),
4 => builder.with(RoomSorter::new(RoomSort::BOTTOMMOST)),
_ => builder.with(RoomSorter::new(RoomSort::CENTRAL)),
}
builder.with(RoomDrawer::new());
let corridor_roll = rng.roll_dice(1, 2);
match corridor_roll {
1 => builder.with(DoglegCorridors::new()),
_ => builder.with(BspCorridors::new()),
}
let corridor_roll = rng.roll_dice(1, 4);
match corridor_roll {
1 => builder.with(DoglegCorridors::new()),
2 => builder.with(NearestCorridors::new()),
3 => builder.with(BresenhamCorridors::new()),
_ => builder.with(BspCorridors::new()),
}
let cspawn_roll = rng.roll_dice(1, 2);
if cspawn_roll == 1 {
builder.with(CorridorSpawner::new());
}
let modifier_roll = rng.roll_dice(1, 6);
match modifier_roll {
1 => builder.with(RoomExploder::new()),
2 => builder.with(RoomCornerRounder::new()),
_ => {}
}
}
// Pick a starting position, in a room or elsewhere.
let start_roll = rng.roll_dice(1, 2);
match start_roll {
1 => builder.with(RoomBasedStartingPosition::new()),
_ => {
let (start_x, start_y) = random_start_position(rng);
builder.with(AreaStartingPosition::new(start_x, start_y));
}
}
// Decide where to put the exit - in a room or far away, anywhere.
let exit_roll = rng.roll_dice(1, 2);
match exit_roll {
1 => builder.with(RoomBasedStairs::new()),
_ => builder.with(DistantExit::new()),
}
// Decide whether to spawn entities only in rooms, or with voronoi noise.
let spawn_roll = rng.roll_dice(1, 2);
match spawn_roll {
1 => builder.with(RoomBasedSpawner::new()),
_ => builder.with(VoronoiSpawning::new()),
}
}
fn random_shape_builder(rng: &mut rltk::RandomNumberGenerator, builder: &mut BuilderChain) {
// Pick an initial builder
let builder_roll = rng.roll_dice(1, 16);
match builder_roll {
1 => builder.start_with(CellularAutomataBuilder::new()),
2 => builder.start_with(DrunkardsWalkBuilder::open_area()),
3 => builder.start_with(DrunkardsWalkBuilder::open_halls()),
4 => builder.start_with(DrunkardsWalkBuilder::winding_passages()),
5 => builder.start_with(DrunkardsWalkBuilder::fat_passages()),
6 => builder.start_with(DrunkardsWalkBuilder::fearful_symmetry()),
7 => builder.start_with(MazeBuilder::new()),
8 => builder.start_with(DLABuilder::walk_inwards()),
9 => builder.start_with(DLABuilder::walk_outwards()),
10 => builder.start_with(DLABuilder::central_attractor()),
11 => builder.start_with(DLABuilder::insectoid()),
12 => builder.start_with(VoronoiBuilder::pythagoras()),
13 => builder.start_with(VoronoiBuilder::manhattan()),
_ => builder.start_with(PrefabBuilder::constant(prefab_builder::prefab_levels::WFC_POPULATED)),
}
// 'Select' the centre by placing a starting position, and cull everywhere unreachable.
builder.with(AreaStartingPosition::new(XStart::CENTRE, YStart::CENTRE));
builder.with(CullUnreachable::new());
// Now set the start to a random spot in our remaining area.
let (start_x, start_y) = random_start_position(rng);
builder.with(AreaStartingPosition::new(start_x, start_y));
// Place the exit and spawn mobs
builder.with(VoronoiSpawning::new());
builder.with(DistantExit::new());
}
pub fn random_builder(new_depth: i32, rng: &mut rltk::RandomNumberGenerator) -> BuilderChain {
/*let mut builder = BuilderChain::new(new_depth);
let type_roll = rng.roll_dice(1, 2);
match type_roll {
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());
builder.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*/
let mut builder = BuilderChain::new(new_depth);
builder.start_with(BspInteriorBuilder::new());
builder.with(DoorPlacement::new());
builder.with(RoomBasedSpawner::new());
builder.with(PrefabBuilder::vaults());
builder.with(RoomBasedStairs::new());
builder.with(RoomBasedStartingPosition::new());
builder
}