From 9e85abb29bcac0915350f6a37272ed60d6373c0f Mon Sep 17 00:00:00 2001 From: Timothy Warren Date: Tue, 18 Jan 2022 09:32:48 -0500 Subject: [PATCH] Implement map builder for level 5 --- src/map/themes.rs | 8 +++ src/map_builders.rs | 7 ++- src/map_builders/area_ending_point.rs | 73 +++++++++++++++++++++ src/map_builders/limestone_cavern.rs | 91 +++++++++++++++++++++++++-- 4 files changed, 174 insertions(+), 5 deletions(-) create mode 100644 src/map_builders/area_ending_point.rs diff --git a/src/map/themes.rs b/src/map/themes.rs index e9c0a28..bd2e279 100644 --- a/src/map/themes.rs +++ b/src/map/themes.rs @@ -5,6 +5,14 @@ use crate::colors; pub fn tile_glyph(idx: usize, map: &Map) -> (FontCharType, RGB, RGB) { let (glyph, mut fg, mut bg) = match map.depth { + 5 => { + let x = idx as i32 % map.width; + if x < map.width / 2 { + get_limestone_cavern_glyph(idx, map) + } else { + get_tile_glyph_default(idx, map) + } + } 3 | 4 => get_limestone_cavern_glyph(idx, map), 2 => get_forest_glyph(idx, map), _ => get_tile_glyph_default(idx, map), diff --git a/src/map_builders.rs b/src/map_builders.rs index be4bd98..28c0eb4 100644 --- a/src/map_builders.rs +++ b/src/map_builders.rs @@ -1,3 +1,4 @@ +mod area_ending_point; mod area_starting_points; mod bsp_dungeon; mod bsp_interior; @@ -32,6 +33,7 @@ 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; @@ -42,7 +44,9 @@ use dla::DLABuilder; use door_placement::DoorPlacement; use drunkard::DrunkardsWalkBuilder; use forest::forest_builder; -use limestone_cavern::{limestone_cavern_builder, limestone_deep_cavern_builder}; +use limestone_cavern::{ + limestone_cavern_builder, limestone_deep_cavern_builder, limestone_transition_builder, +}; use maze::MazeBuilder; use prefab_builder::PrefabBuilder; use room_based_spawner::RoomBasedSpawner; @@ -349,6 +353,7 @@ pub fn level_builder( 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), _ => random_builder(new_depth, rng, width, height), } } diff --git a/src/map_builders/area_ending_point.rs b/src/map_builders/area_ending_point.rs new file mode 100644 index 0000000..e43a38a --- /dev/null +++ b/src/map_builders/area_ending_point.rs @@ -0,0 +1,73 @@ +use ::rltk::{DistanceAlg, Point, RandomNumberGenerator}; + +use super::{BuilderMap, MetaMapBuilder}; +use crate::{map, Map, TileType}; + +#[allow(dead_code)] +pub enum XEnd { + Left, + Center, + Right, +} + +#[allow(dead_code)] +pub enum YEnd { + Top, + Center, + Bottom, +} + +pub struct AreaEndingPosition { + x: XEnd, + y: YEnd, +} + +impl MetaMapBuilder for AreaEndingPosition { + fn build_map(&mut self, rng: &mut RandomNumberGenerator, build_data: &mut BuilderMap) { + self.build(rng, build_data); + } +} + +impl AreaEndingPosition { + #[allow(dead_code)] + pub fn new(x: XEnd, y: YEnd) -> Box { + Box::new(AreaEndingPosition { x, y }) + } + + fn build(&mut self, _rng: &mut RandomNumberGenerator, build_data: &mut BuilderMap) { + let seed_x = match self.x { + XEnd::Left => 1, + XEnd::Center => build_data.map.width / 2, + XEnd::Right => build_data.map.width - 2, + }; + let seed_y = match self.y { + YEnd::Top => 1, + YEnd::Center => build_data.map.height / 2, + YEnd::Bottom => build_data.map.height - 2, + }; + + let mut available_floors: Vec<(usize, f32)> = Vec::new(); + for (idx, tiletype) in build_data.map.tiles.iter().enumerate() { + if map::tile_walkable(*tiletype) { + available_floors.push(( + idx, + DistanceAlg::PythagorasSquared.distance2d( + Point::new( + idx as i32 % build_data.map.width, + idx as i32 / build_data.map.width, + ), + Point::new(seed_x, seed_y), + ), + )); + } + } + + if available_floors.is_empty() { + panic!("No valid floors to start on"); + } + + available_floors.sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap()); + + build_data.map.tiles[available_floors[0].0] = TileType::DownStairs; + } +} diff --git a/src/map_builders/limestone_cavern.rs b/src/map_builders/limestone_cavern.rs index 435b836..983566f 100644 --- a/src/map_builders/limestone_cavern.rs +++ b/src/map_builders/limestone_cavern.rs @@ -1,12 +1,12 @@ use ::rltk::RandomNumberGenerator; use super::{ - AreaStartingPosition, BuilderChain, BuilderMap, CullUnreachable, DistantExit, - DrunkardsWalkBuilder, MetaMapBuilder, VoronoiSpawning, XStart, YStart, + prefab_builder::prefab_sections, AreaEndingPosition, AreaStartingPosition, BspDungeonBuilder, + BuilderChain, BuilderMap, CellularAutomataBuilder, CullUnreachable, DLABuilder, DistantExit, + DrunkardsWalkBuilder, MetaMapBuilder, NearestCorridors, PrefabBuilder, RoomBasedSpawner, + RoomDrawer, RoomExploder, RoomSort, RoomSorter, VoronoiSpawning, XEnd, XStart, YEnd, YStart, }; use crate::map::TileType; -use crate::map_builders::dla::DLABuilder; -use crate::map_builders::prefab_builder::{prefab_sections, PrefabBuilder}; pub fn limestone_cavern_builder( new_depth: i32, @@ -45,6 +45,28 @@ pub fn limestone_deep_cavern_builder( chain } +pub fn limestone_transition_builder( + new_depth: i32, + _rng: &mut RandomNumberGenerator, + width: i32, + height: i32, +) -> BuilderChain { + let mut chain = BuilderChain::new(new_depth, width, height, "Dwarf Fort - Upper Reaches"); + chain + .start_with(CellularAutomataBuilder::new()) + .with(AreaStartingPosition::new(XStart::Center, YStart::Center)) + .with(CullUnreachable::new()) + .with(AreaStartingPosition::new(XStart::Left, YStart::Center)) + .with(VoronoiSpawning::new()) + .with(CaveDecorator::new()) + .with(CaveTransition::new()) + .with(AreaStartingPosition::new(XStart::Left, YStart::Center)) + .with(CullUnreachable::new()) + .with(AreaEndingPosition::new(XEnd::Right, YEnd::Center)); + + chain +} + pub struct CaveDecorator {} impl MetaMapBuilder for CaveDecorator { @@ -105,3 +127,64 @@ impl CaveDecorator { build_data.map.outdoors = false; } } + +pub struct CaveTransition {} + +impl MetaMapBuilder for CaveTransition { + fn build_map(&mut self, rng: &mut RandomNumberGenerator, build_data: &mut BuilderMap) { + self.build(rng, build_data); + } +} + +impl CaveTransition { + #[allow(dead_code)] + pub fn new() -> Box { + Box::new(CaveTransition {}) + } + + fn build(&mut self, rng: &mut RandomNumberGenerator, build_data: &mut BuilderMap) { + build_data.map.depth = 5; + build_data.take_snapshot(); + + // Build a BSP-based dungeon + let mut builder = BuilderChain::new(5, build_data.width, build_data.height, "New Map"); + builder + .start_with(BspDungeonBuilder::new()) + .with(RoomDrawer::new()) + .with(RoomSorter::new(RoomSort::RightMost)) + .with(NearestCorridors::new()) + .with(RoomExploder::new()) + .with(RoomBasedSpawner::new()); + builder.build_map(rng); + + // Add the history to our history + for h in builder.build_data.history.iter() { + build_data.history.push(h.clone()); + } + build_data.take_snapshot(); + + // Copy the right half of the BSP map into our map + for x in build_data.map.width / 2..build_data.map.width { + for y in 0..build_data.map.height { + let idx = build_data.map.xy_idx(x, y); + build_data.map.tiles[idx] = builder.build_data.map.tiles[idx]; + } + } + build_data.take_snapshot(); + + // Keep Voronoi spawn data from the left half of the map + let w = build_data.map.width; + build_data.spawn_list.retain(|s| { + let x = s.0 as i32 / w; + x < w / 2 + }); + + // Keep room spawn data from the right half of the map + for s in builder.build_data.spawn_list.iter() { + let x = s.0 as i32 / w; + if x < w / 2 { + build_data.spawn_list.push(s.clone()); + } + } + } +}