corridor types, doors, some refactoring
This commit is contained in:
parent
46e0c6ec6b
commit
ab5797078c
19 changed files with 380 additions and 35 deletions
|
|
@ -1,4 +1,4 @@
|
|||
use super::{BuilderMap, InitialMapBuilder, Map, Rect, TileType};
|
||||
use super::{BuilderMap, InitialMapBuilder, Rect, TileType};
|
||||
use rltk::RandomNumberGenerator;
|
||||
|
||||
pub struct BspDungeonBuilder {
|
||||
|
|
|
|||
|
|
@ -10,25 +10,32 @@ pub fn apply_room_to_map(map: &mut Map, room: &Rect) {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn apply_horizontal_tunnel(map: &mut Map, x1: i32, x2: i32, y: i32) {
|
||||
pub fn apply_horizontal_tunnel(map: &mut Map, x1: i32, x2: i32, y: i32) -> Vec<usize> {
|
||||
let mut corridor = Vec::new();
|
||||
for x in min(x1, x2)..=max(x1, x2) {
|
||||
let idx = map.xy_idx(x, y);
|
||||
if idx > 0 && idx < (map.width as usize) * (map.height as usize) {
|
||||
map.tiles[idx as usize] = TileType::Floor;
|
||||
corridor.push(idx as usize);
|
||||
}
|
||||
}
|
||||
return corridor;
|
||||
}
|
||||
|
||||
pub fn apply_vertical_tunnel(map: &mut Map, y1: i32, y2: i32, x: i32) {
|
||||
pub fn apply_vertical_tunnel(map: &mut Map, y1: i32, y2: i32, x: i32) -> Vec<usize> {
|
||||
let mut corridor = Vec::new();
|
||||
for y in min(y1, y2)..=max(y1, y2) {
|
||||
let idx = map.xy_idx(x, y);
|
||||
if idx > 0 && idx < (map.width as usize) * (map.height as usize) {
|
||||
map.tiles[idx as usize] = TileType::Floor;
|
||||
corridor.push(idx as usize);
|
||||
}
|
||||
}
|
||||
return corridor;
|
||||
}
|
||||
|
||||
pub fn draw_corridor(map: &mut Map, x1: i32, y1: i32, x2: i32, y2: i32) {
|
||||
pub fn draw_corridor(map: &mut Map, x1: i32, y1: i32, x2: i32, y2: i32) -> Vec<usize> {
|
||||
let mut corridor = Vec::new();
|
||||
let mut x = x1;
|
||||
let mut y = y1;
|
||||
|
||||
|
|
@ -44,8 +51,12 @@ pub fn draw_corridor(map: &mut Map, x1: i32, y1: i32, x2: i32, y2: i32) {
|
|||
}
|
||||
|
||||
let idx = map.xy_idx(x, y);
|
||||
map.tiles[idx] = TileType::Floor;
|
||||
if map.tiles[idx] != TileType::Floor {
|
||||
map.tiles[idx] = TileType::Floor;
|
||||
corridor.push(idx);
|
||||
}
|
||||
}
|
||||
return corridor;
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
|
|
|
|||
75
src/map_builders/door_placement.rs
Normal file
75
src/map_builders/door_placement.rs
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
use super::{BuilderMap, MetaMapBuilder, TileType};
|
||||
use rltk::RandomNumberGenerator;
|
||||
|
||||
pub struct DoorPlacement {}
|
||||
|
||||
impl MetaMapBuilder for DoorPlacement {
|
||||
#[allow(dead_code)]
|
||||
fn build_map(&mut self, rng: &mut rltk::RandomNumberGenerator, build_data: &mut BuilderMap) {
|
||||
self.doors(rng, build_data);
|
||||
}
|
||||
}
|
||||
|
||||
impl DoorPlacement {
|
||||
#[allow(dead_code)]
|
||||
pub fn new() -> Box<DoorPlacement> {
|
||||
Box::new(DoorPlacement {})
|
||||
}
|
||||
|
||||
fn doors(&mut self, rng: &mut RandomNumberGenerator, build_data: &mut BuilderMap) {
|
||||
if let Some(halls_original) = &build_data.corridors {
|
||||
let halls = halls_original.clone(); // Avoids nested borrow
|
||||
for hall in halls.iter() {
|
||||
if hall.len() > 2 {
|
||||
if self.door_possible(build_data, hall[0]) {
|
||||
build_data.spawn_list.push((hall[0], "door".to_string()));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// There are no corridors - scan for possible places
|
||||
let tiles = build_data.map.tiles.clone();
|
||||
for (i, tile) in tiles.iter().enumerate() {
|
||||
if *tile == TileType::Floor && self.door_possible(build_data, i) && rng.roll_dice(1, 3) == 1 {
|
||||
build_data.spawn_list.push((i, "door".to_string()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn door_possible(&self, build_data: &mut BuilderMap, idx: usize) -> bool {
|
||||
// Iterate through spawn list. If another entity wants to spawn on this tile, return false
|
||||
for spawn in build_data.spawn_list.iter() {
|
||||
if spawn.0 == idx {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
let x = idx % build_data.map.width as usize;
|
||||
let y = idx / build_data.map.width as usize;
|
||||
|
||||
// Check for east-west door possibility
|
||||
if build_data.map.tiles[idx] == TileType::Floor
|
||||
&& (x > 1 && build_data.map.tiles[idx - 1] == TileType::Floor)
|
||||
&& (x < build_data.map.width as usize - 2 && build_data.map.tiles[idx + 1] == TileType::Floor)
|
||||
&& (y > 1 && build_data.map.tiles[idx - build_data.map.width as usize] == TileType::Wall)
|
||||
&& (y < build_data.map.height as usize - 2
|
||||
&& build_data.map.tiles[idx + build_data.map.width as usize] == TileType::Wall)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check for north-south door possibility
|
||||
if build_data.map.tiles[idx] == TileType::Floor
|
||||
&& (x > 1 && build_data.map.tiles[idx - 1] == TileType::Wall)
|
||||
&& (x < build_data.map.width as usize - 2 && build_data.map.tiles[idx + 1] == TileType::Wall)
|
||||
&& (y > 1 && build_data.map.tiles[idx - build_data.map.width as usize] == TileType::Floor)
|
||||
&& (y < build_data.map.height as usize - 2
|
||||
&& build_data.map.tiles[idx + build_data.map.width as usize] == TileType::Floor)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
}
|
||||
|
|
@ -48,6 +48,14 @@ 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 {
|
||||
|
|
@ -55,6 +63,7 @@ pub struct BuilderMap {
|
|||
pub map: Map,
|
||||
pub starting_position: Option<Position>,
|
||||
pub rooms: Option<Vec<Rect>>,
|
||||
pub corridors: Option<Vec<Vec<usize>>>,
|
||||
pub history: Vec<Map>,
|
||||
}
|
||||
|
||||
|
|
@ -86,6 +95,7 @@ impl BuilderChain {
|
|||
map: Map::new(new_depth),
|
||||
starting_position: None,
|
||||
rooms: None,
|
||||
corridors: None,
|
||||
history: Vec::new(),
|
||||
},
|
||||
}
|
||||
|
|
@ -182,12 +192,19 @@ fn random_room_builder(rng: &mut rltk::RandomNumberGenerator, builder: &mut Buil
|
|||
_ => builder.with(BspCorridors::new()),
|
||||
}
|
||||
|
||||
let corridor_roll = rng.roll_dice(1, 2);
|
||||
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()),
|
||||
|
|
@ -255,7 +272,7 @@ fn random_shape_builder(rng: &mut rltk::RandomNumberGenerator, builder: &mut Bui
|
|||
}
|
||||
|
||||
pub fn random_builder(new_depth: i32, rng: &mut rltk::RandomNumberGenerator) -> BuilderChain {
|
||||
let mut builder = BuilderChain::new(new_depth);
|
||||
/*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),
|
||||
|
|
@ -264,13 +281,30 @@ pub fn random_builder(new_depth: i32, rng: &mut rltk::RandomNumberGenerator) ->
|
|||
|
||||
/*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(RoomBasedStairs::new());
|
||||
builder.with(RoomBasedStartingPosition::new());
|
||||
builder
|
||||
}
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ impl RoomDrawer {
|
|||
if let Some(rooms_builder) = &build_data.rooms {
|
||||
rooms = rooms_builder.clone();
|
||||
} else {
|
||||
panic!("Room Drawing require a builder with room structures");
|
||||
panic!("RoomDrawer require a builder with rooms");
|
||||
}
|
||||
|
||||
for room in rooms.iter() {
|
||||
|
|
|
|||
64
src/map_builders/rooms_corridors_bresenham.rs
Normal file
64
src/map_builders/rooms_corridors_bresenham.rs
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
use super::{BuilderMap, MetaMapBuilder, Rect, TileType};
|
||||
use rltk::RandomNumberGenerator;
|
||||
use std::collections::HashSet;
|
||||
|
||||
pub struct BresenhamCorridors {}
|
||||
|
||||
impl MetaMapBuilder for BresenhamCorridors {
|
||||
#[allow(dead_code)]
|
||||
fn build_map(&mut self, rng: &mut rltk::RandomNumberGenerator, build_data: &mut BuilderMap) {
|
||||
self.corridors(rng, build_data);
|
||||
}
|
||||
}
|
||||
|
||||
impl BresenhamCorridors {
|
||||
#[allow(dead_code)]
|
||||
pub fn new() -> Box<BresenhamCorridors> {
|
||||
Box::new(BresenhamCorridors {})
|
||||
}
|
||||
|
||||
fn corridors(&mut self, _rng: &mut RandomNumberGenerator, build_data: &mut BuilderMap) {
|
||||
let rooms: Vec<Rect>;
|
||||
if let Some(rooms_builder) = &build_data.rooms {
|
||||
rooms = rooms_builder.clone();
|
||||
} else {
|
||||
panic!("BresenhamCorridors require a builder with room structures");
|
||||
}
|
||||
|
||||
let mut connected: HashSet<usize> = HashSet::new();
|
||||
let mut corridors: Vec<Vec<usize>> = Vec::new();
|
||||
for (i, room) in rooms.iter().enumerate() {
|
||||
let mut room_distance: Vec<(usize, f32)> = Vec::new();
|
||||
let room_centre = room.centre();
|
||||
let room_centre_pt = rltk::Point::new(room_centre.0, room_centre.1);
|
||||
for (j, other_room) in rooms.iter().enumerate() {
|
||||
if i != j && !connected.contains(&j) {
|
||||
let other_centre = other_room.centre();
|
||||
let other_centre_pt = rltk::Point::new(other_centre.0, other_centre.1);
|
||||
let distance = rltk::DistanceAlg::Pythagoras.distance2d(room_centre_pt, other_centre_pt);
|
||||
room_distance.push((j, distance));
|
||||
}
|
||||
}
|
||||
|
||||
if !room_distance.is_empty() {
|
||||
room_distance.sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap());
|
||||
let dest_centre = rooms[room_distance[0].0].centre();
|
||||
let line = rltk::line2d(
|
||||
rltk::LineAlg::Bresenham,
|
||||
room_centre_pt,
|
||||
rltk::Point::new(dest_centre.0, dest_centre.1),
|
||||
);
|
||||
let mut corridor = Vec::new();
|
||||
for cell in line.iter() {
|
||||
let idx = build_data.map.xy_idx(cell.x, cell.y);
|
||||
build_data.map.tiles[idx] = TileType::Floor;
|
||||
corridor.push(idx);
|
||||
}
|
||||
corridors.push(corridor);
|
||||
connected.insert(i);
|
||||
build_data.take_snapshot();
|
||||
}
|
||||
}
|
||||
build_data.corridors = Some(corridors);
|
||||
}
|
||||
}
|
||||
|
|
@ -24,6 +24,7 @@ impl BspCorridors {
|
|||
panic!("BSP Corridors require a builder with room structures");
|
||||
}
|
||||
|
||||
let mut corridors: Vec<Vec<usize>> = Vec::new();
|
||||
for i in 0..rooms.len() - 1 {
|
||||
let room = rooms[i];
|
||||
let next_room = rooms[i + 1];
|
||||
|
|
@ -31,8 +32,10 @@ impl BspCorridors {
|
|||
let start_y = room.y1 + (rng.roll_dice(1, i32::abs(room.y1 - room.y2)) - 1);
|
||||
let end_x = next_room.x1 + (rng.roll_dice(1, i32::abs(next_room.x1 - next_room.x2)) - 1);
|
||||
let end_y = next_room.y1 + (rng.roll_dice(1, i32::abs(next_room.y1 - next_room.y2)) - 1);
|
||||
draw_corridor(&mut build_data.map, start_x, start_y, end_x, end_y);
|
||||
let corridor = draw_corridor(&mut build_data.map, start_x, start_y, end_x, end_y);
|
||||
corridors.push(corridor);
|
||||
build_data.take_snapshot();
|
||||
}
|
||||
build_data.corridors = Some(corridors);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,19 +24,25 @@ impl DoglegCorridors {
|
|||
panic!("DoglegCorridors require a builder with rooms.");
|
||||
}
|
||||
|
||||
let mut corridors: Vec<Vec<usize>> = Vec::new();
|
||||
for (i, room) in rooms.iter().enumerate() {
|
||||
if i > 0 {
|
||||
let (new_x, new_y) = room.centre();
|
||||
let (prev_x, prev_y) = rooms[i as usize - 1].centre();
|
||||
if rng.range(0, 2) == 1 {
|
||||
apply_horizontal_tunnel(&mut build_data.map, prev_x, new_x, prev_y);
|
||||
apply_vertical_tunnel(&mut build_data.map, prev_y, new_y, new_x);
|
||||
let mut c1 = apply_horizontal_tunnel(&mut build_data.map, prev_x, new_x, prev_y);
|
||||
let mut c2 = apply_vertical_tunnel(&mut build_data.map, prev_y, new_y, new_x);
|
||||
c1.append(&mut c2);
|
||||
corridors.push(c1);
|
||||
} else {
|
||||
apply_vertical_tunnel(&mut build_data.map, prev_y, new_y, prev_x);
|
||||
apply_horizontal_tunnel(&mut build_data.map, prev_x, new_x, new_y);
|
||||
let mut c1 = apply_vertical_tunnel(&mut build_data.map, prev_y, new_y, prev_x);
|
||||
let mut c2 = apply_horizontal_tunnel(&mut build_data.map, prev_x, new_x, new_y);
|
||||
c1.append(&mut c2);
|
||||
corridors.push(c1);
|
||||
}
|
||||
build_data.take_snapshot();
|
||||
}
|
||||
}
|
||||
build_data.corridors = Some(corridors);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
55
src/map_builders/rooms_corridors_nearest.rs
Normal file
55
src/map_builders/rooms_corridors_nearest.rs
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
use super::{draw_corridor, BuilderMap, MetaMapBuilder, Rect};
|
||||
use rltk::RandomNumberGenerator;
|
||||
use std::collections::HashSet;
|
||||
|
||||
pub struct NearestCorridors {}
|
||||
|
||||
impl MetaMapBuilder for NearestCorridors {
|
||||
#[allow(dead_code)]
|
||||
fn build_map(&mut self, rng: &mut rltk::RandomNumberGenerator, build_data: &mut BuilderMap) {
|
||||
self.corridors(rng, build_data);
|
||||
}
|
||||
}
|
||||
|
||||
impl NearestCorridors {
|
||||
#[allow(dead_code)]
|
||||
pub fn new() -> Box<NearestCorridors> {
|
||||
return Box::new(NearestCorridors {});
|
||||
}
|
||||
|
||||
fn corridors(&mut self, _rng: &mut RandomNumberGenerator, build_data: &mut BuilderMap) {
|
||||
let rooms: Vec<Rect>;
|
||||
if let Some(rooms_builder) = &build_data.rooms {
|
||||
rooms = rooms_builder.clone();
|
||||
} else {
|
||||
panic!("NearestCorridors requires a builder with rooms");
|
||||
}
|
||||
|
||||
let mut connected: HashSet<usize> = HashSet::new();
|
||||
let mut corridors: Vec<Vec<usize>> = Vec::new();
|
||||
for (i, room) in rooms.iter().enumerate() {
|
||||
let mut room_distance: Vec<(usize, f32)> = Vec::new();
|
||||
let room_centre = room.centre();
|
||||
let room_centre_pt = rltk::Point::new(room_centre.0, room_centre.1);
|
||||
for (j, other_room) in rooms.iter().enumerate() {
|
||||
if i != j && !connected.contains(&j) {
|
||||
let other_centre = other_room.centre();
|
||||
let other_centre_pt = rltk::Point::new(other_centre.0, other_centre.1);
|
||||
let distance = rltk::DistanceAlg::Pythagoras.distance2d(room_centre_pt, other_centre_pt);
|
||||
room_distance.push((j, distance));
|
||||
}
|
||||
}
|
||||
|
||||
if !room_distance.is_empty() {
|
||||
room_distance.sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap());
|
||||
let dest_centre = rooms[room_distance[0].0].centre();
|
||||
let corridor =
|
||||
draw_corridor(&mut build_data.map, room_centre.0, room_centre.1, dest_centre.0, dest_centre.1);
|
||||
connected.insert(i);
|
||||
build_data.take_snapshot();
|
||||
corridors.push(corridor);
|
||||
}
|
||||
}
|
||||
build_data.corridors = Some(corridors);
|
||||
}
|
||||
}
|
||||
28
src/map_builders/rooms_corridors_spawner.rs
Normal file
28
src/map_builders/rooms_corridors_spawner.rs
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
use super::{spawner, BuilderMap, MetaMapBuilder};
|
||||
use rltk::RandomNumberGenerator;
|
||||
|
||||
pub struct CorridorSpawner {}
|
||||
|
||||
impl MetaMapBuilder for CorridorSpawner {
|
||||
fn build_map(&mut self, rng: &mut rltk::RandomNumberGenerator, build_data: &mut BuilderMap) {
|
||||
self.build(rng, build_data);
|
||||
}
|
||||
}
|
||||
|
||||
impl CorridorSpawner {
|
||||
#[allow(dead_code)]
|
||||
pub fn new() -> Box<CorridorSpawner> {
|
||||
return Box::new(CorridorSpawner {});
|
||||
}
|
||||
|
||||
fn build(&mut self, rng: &mut RandomNumberGenerator, build_data: &mut BuilderMap) {
|
||||
if let Some(corridors) = &build_data.corridors {
|
||||
for corridor in corridors.iter() {
|
||||
let depth = build_data.map.depth;
|
||||
spawner::spawn_region(&build_data.map, rng, &corridor, depth, &mut build_data.spawn_list);
|
||||
}
|
||||
} else {
|
||||
panic!("CorridorSpawner only works after corridors have been created");
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue