This commit is contained in:
Llywelwyn 2023-07-16 07:59:24 +01:00
parent 363ff4c0a3
commit f578938390
5 changed files with 329 additions and 8 deletions

239
src/map_builders/maze.rs Normal file
View file

@ -0,0 +1,239 @@
use super::{
generate_voronoi_spawn_regions, remove_unreachable_areas_returning_most_distant, spawner, Map, MapBuilder,
Position, TileType, SHOW_MAPGEN,
};
use rltk::RandomNumberGenerator;
use specs::prelude::*;
use std::collections::HashMap;
pub struct MazeBuilder {
map: Map,
starting_position: Position,
depth: i32,
history: Vec<Map>,
noise_areas: HashMap<i32, Vec<usize>>,
}
impl MapBuilder for MazeBuilder {
fn build_map(&mut self, rng: &mut RandomNumberGenerator) {
return self.build(rng);
}
fn spawn_entities(&mut self, ecs: &mut World) {
for area in self.noise_areas.iter() {
spawner::spawn_region(ecs, area.1, self.depth);
}
}
// Getters
fn get_map(&mut self) -> Map {
return self.map.clone();
}
fn get_starting_pos(&mut self) -> Position {
return self.starting_position.clone();
}
// Mapgen visualisation stuff
fn get_snapshot_history(&self) -> Vec<Map> {
return self.history.clone();
}
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);
}
}
}
impl MazeBuilder {
pub fn new(new_depth: i32) -> MazeBuilder {
MazeBuilder {
map: Map::new(new_depth),
starting_position: Position { x: 0, y: 0 },
depth: new_depth,
history: Vec::new(),
noise_areas: HashMap::new(),
}
}
#[allow(clippy::map_entry)]
fn build(&mut self, rng: &mut RandomNumberGenerator) {
// Maze gen
let mut maze = Grid::new((self.map.width / 2) - 2, (self.map.height / 2) - 2, rng);
maze.generate_maze(self);
// Starting point @ top left
self.starting_position = Position { x: 2, y: 2 };
let start_idx = self.map.xy_idx(self.starting_position.x, self.starting_position.y);
self.take_snapshot();
// Find all tiles we can reach from the starting point
let exit_tile = remove_unreachable_areas_returning_most_distant(&mut self.map, start_idx);
self.take_snapshot();
// Place the stairs
self.map.tiles[exit_tile] = TileType::DownStair;
self.take_snapshot();
// Now we build a noise map for use in spawning entities later
self.noise_areas = generate_voronoi_spawn_regions(&self.map, rng);
}
}
struct Grid<'a> {
width: i32,
height: i32,
cells: Vec<Cell>,
backtrace: Vec<usize>,
current: usize,
rng: &'a mut RandomNumberGenerator,
}
impl<'a> Grid<'a> {
fn new(width: i32, height: i32, rng: &mut RandomNumberGenerator) -> Grid {
let mut grid = Grid { width, height, cells: Vec::new(), backtrace: Vec::new(), current: 0, rng };
for row in 0..height {
for column in 0..width {
grid.cells.push(Cell::new(row, column))
}
}
return grid;
}
fn calculate_index(&self, row: i32, column: i32) -> i32 {
if row < 0 || column < 0 || column > self.width - 1 || row > self.height - 1 {
-1
} else {
column + (row * self.width)
}
}
fn get_available_neighbours(&self) -> Vec<usize> {
let mut neighbours: Vec<usize> = Vec::new();
let current_row = self.cells[self.current].row;
let current_column = self.cells[self.current].column;
let neighbour_indices: [i32; 4] = [
self.calculate_index(current_row - 1, current_column),
self.calculate_index(current_row, current_column + 1),
self.calculate_index(current_row + 1, current_column),
self.calculate_index(current_row, current_column - 1),
];
for i in neighbour_indices.iter() {
if *i != -1 && !self.cells[*i as usize].visited {
neighbours.push(*i as usize);
}
}
return neighbours;
}
fn find_next_cell(&mut self) -> Option<usize> {
let neighbours = self.get_available_neighbours();
if neighbours.is_empty() {
return None;
}
if neighbours.len() == 1 {
return Some(neighbours[0]);
}
return Some(neighbours[(self.rng.roll_dice(1, neighbours.len() as i32) - 1) as usize]);
}
fn generate_maze(&mut self, generator: &mut MazeBuilder) {
let mut i = 0;
loop {
self.cells[self.current].visited = true;
let next = self.find_next_cell();
match next {
Some(next) => {
self.cells[next].visited = true;
self.backtrace.push(self.current);
let (lower_part, higher_part) = self.cells.split_at_mut(std::cmp::max(self.current, next));
let cell1 = &mut lower_part[std::cmp::min(self.current, next)];
let cell2 = &mut higher_part[0];
cell1.remove_walls(cell2);
self.current = next;
}
None => {
if self.backtrace.is_empty() {
break;
}
self.current = self.backtrace[0];
self.backtrace.remove(0);
}
}
if i % 50 == 0 {
self.copy_to_map(&mut generator.map);
generator.take_snapshot();
}
i += 1;
}
}
fn copy_to_map(&self, map: &mut Map) {
// Clear map
for i in map.tiles.iter_mut() {
*i = TileType::Wall;
}
for cell in self.cells.iter() {
let x = cell.column + 1;
let y = cell.row + 1;
let idx = map.xy_idx(x * 2, y * 2);
map.tiles[idx] = TileType::Floor;
if !cell.walls[TOP] {
map.tiles[idx - map.width as usize] = TileType::Floor
}
if !cell.walls[RIGHT] {
map.tiles[idx + 1] = TileType::Floor
}
if !cell.walls[BOTTOM] {
map.tiles[idx + map.width as usize] = TileType::Floor
}
if !cell.walls[LEFT] {
map.tiles[idx - 1] = TileType::Floor
}
}
}
}
const TOP: usize = 0;
const RIGHT: usize = 1;
const BOTTOM: usize = 2;
const LEFT: usize = 3;
#[derive(Copy, Clone)]
struct Cell {
row: i32,
column: i32,
walls: [bool; 4],
visited: bool,
}
impl Cell {
fn new(row: i32, column: i32) -> Cell {
Cell { row, column, walls: [true, true, true, true], visited: false }
}
fn remove_walls(&mut self, next: &mut Cell) {
let x = self.column - next.column;
let y = self.row - next.row;
if x == 1 {
self.walls[LEFT] = false;
next.walls[RIGHT] = false;
} else if x == -1 {
self.walls[RIGHT] = false;
next.walls[LEFT] = false;
} else if y == 1 {
self.walls[TOP] = false;
next.walls[BOTTOM] = false
} else if y == -1 {
self.walls[BOTTOM] = false;
next.walls[TOP] = false;
}
}
}

View file

@ -4,6 +4,7 @@ mod bsp_interior;
mod cellular_automata; mod cellular_automata;
mod common; mod common;
mod drunkard; mod drunkard;
mod maze;
mod simple_map; mod simple_map;
use common::*; use common::*;
use rltk::RandomNumberGenerator; use rltk::RandomNumberGenerator;
@ -20,7 +21,7 @@ pub trait MapBuilder {
pub fn random_builder(new_depth: i32) -> Box<dyn MapBuilder> { pub fn random_builder(new_depth: i32) -> Box<dyn MapBuilder> {
let mut rng = rltk::RandomNumberGenerator::new(); let mut rng = rltk::RandomNumberGenerator::new();
let builder = rng.roll_dice(1, 7); /* let builder = rng.roll_dice(1, 8);
match builder { match builder {
1 => Box::new(bsp_dungeon::BspDungeonBuilder::new(new_depth)), 1 => Box::new(bsp_dungeon::BspDungeonBuilder::new(new_depth)),
2 => Box::new(bsp_interior::BspInteriorBuilder::new(new_depth)), 2 => Box::new(bsp_interior::BspInteriorBuilder::new(new_depth)),
@ -28,6 +29,8 @@ pub fn random_builder(new_depth: i32) -> Box<dyn MapBuilder> {
4 => Box::new(drunkard::DrunkardsWalkBuilder::open_area(new_depth)), 4 => Box::new(drunkard::DrunkardsWalkBuilder::open_area(new_depth)),
5 => Box::new(drunkard::DrunkardsWalkBuilder::open_halls(new_depth)), 5 => Box::new(drunkard::DrunkardsWalkBuilder::open_halls(new_depth)),
6 => Box::new(drunkard::DrunkardsWalkBuilder::winding_passages(new_depth)), 6 => Box::new(drunkard::DrunkardsWalkBuilder::winding_passages(new_depth)),
7 => Box::new(maze::MazeBuilder::new(new_depth)),
_ => Box::new(simple_map::SimpleMapBuilder::new(new_depth)), _ => Box::new(simple_map::SimpleMapBuilder::new(new_depth)),
} } */
Box::new(maze::MazeBuilder::new(new_depth))
} }

View file

@ -0,0 +1,79 @@
use super::{
generate_voronoi_spawn_regions, remove_unreachable_areas_returning_most_distant, spawner, Map, MapBuilder,
Position, TileType, SHOW_MAPGEN,
};
use rltk::RandomNumberGenerator;
use specs::prelude::*;
use std::collections::HashMap;
pub struct MazeBuilder {
map: Map,
starting_position: Position,
depth: i32,
history: Vec<Map>,
noise_areas: HashMap<i32, Vec<usize>>,
}
impl MapBuilder for MazeBuilder {
fn build_map(&mut self, rng: &mut RandomNumberGenerator) {
return self.build(rng);
}
fn spawn_entities(&mut self, ecs: &mut World) {
for area in self.noise_areas.iter() {
spawner::spawn_region(ecs, area.1, self.depth);
}
}
// Getters
fn get_map(&mut self) -> Map {
return self.map.clone();
}
fn get_starting_pos(&mut self) -> Position {
return self.starting_position.clone();
}
// Mapgen visualisation stuff
fn get_snapshot_history(&self) -> Vec<Map> {
return self.history.clone();
}
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);
}
}
}
impl MazeBuilder {
pub fn new(new_depth: i32) -> MazeBuilder {
MazeBuilder {
map: Map::new(new_depth),
starting_position: Position { x: 0, y: 0 },
depth: new_depth,
history: Vec::new(),
noise_areas: HashMap::new(),
}
}
#[allow(clippy::map_entry)]
fn build(&mut self, rng: &mut RandomNumberGenerator) {
// Find a starting point; start at the middle and walk left until we find an open tile
self.starting_position = Position { x: self.map.width / 2, y: self.map.height / 2 };
let mut start_idx = self.map.xy_idx(self.starting_position.x, self.starting_position.y);
while self.map.tiles[start_idx] != TileType::Floor {
self.starting_position.x -= 1;
start_idx = self.map.xy_idx(self.starting_position.x, self.starting_position.y);
}
self.take_snapshot();
// Find all tiles we can reach from the starting point
let exit_tile = remove_unreachable_areas_returning_most_distant(&mut self.map, start_idx);
self.take_snapshot();
// Place the stairs
self.map.tiles[exit_tile] = TileType::DownStair;
self.take_snapshot();
// Now we build a noise map for use in spawning entities later
self.noise_areas = generate_voronoi_spawn_regions(&self.map, rng);
}
}

View file

@ -801,16 +801,16 @@ function __wbg_get_imports() {
const ret = wasm.memory; const ret = wasm.memory;
return addHeapObject(ret); return addHeapObject(ret);
}; };
imports.wbg.__wbindgen_closure_wrapper533 = function(arg0, arg1, arg2) { imports.wbg.__wbindgen_closure_wrapper763 = function(arg0, arg1, arg2) {
const ret = makeMutClosure(arg0, arg1, 86, __wbg_adapter_20); const ret = makeMutClosure(arg0, arg1, 174, __wbg_adapter_20);
return addHeapObject(ret); return addHeapObject(ret);
}; };
imports.wbg.__wbindgen_closure_wrapper1314 = function(arg0, arg1, arg2) { imports.wbg.__wbindgen_closure_wrapper1498 = function(arg0, arg1, arg2) {
const ret = makeMutClosure(arg0, arg1, 284, __wbg_adapter_23); const ret = makeMutClosure(arg0, arg1, 334, __wbg_adapter_23);
return addHeapObject(ret); return addHeapObject(ret);
}; };
imports.wbg.__wbindgen_closure_wrapper1316 = function(arg0, arg1, arg2) { imports.wbg.__wbindgen_closure_wrapper1500 = function(arg0, arg1, arg2) {
const ret = makeMutClosure(arg0, arg1, 284, __wbg_adapter_23); const ret = makeMutClosure(arg0, arg1, 334, __wbg_adapter_23);
return addHeapObject(ret); return addHeapObject(ret);
}; };

Binary file not shown.