map types

This commit is contained in:
Tyrel Souza 2021-08-31 01:35:32 -04:00
parent 7ae2967522
commit 35f8e4cc73
5 changed files with 195 additions and 28 deletions

View File

@ -42,11 +42,9 @@ impl State {
spawn_player(&mut ecs, map_builder.player_start);
spawn_amulet_of_yala(&mut ecs, map_builder.amulet_start);
map_builder
.rooms
.monster_spawns
.iter()
.skip(1)
.map(|r| r.center())
.for_each(|pos| spawn_monster(&mut ecs, &mut rng, pos));
.for_each(|pos| spawn_monster(&mut ecs, &mut rng, *pos));
resources.insert(map_builder.map);
resources.insert(Camera::new(map_builder.player_start));
@ -94,11 +92,9 @@ impl State {
spawn_player(&mut self.ecs, map_builder.player_start);
spawn_amulet_of_yala(&mut self.ecs, map_builder.amulet_start);
map_builder
.rooms
.monster_spawns
.iter()
.skip(1)
.map(|r| r.center())
.for_each(|pos| spawn_monster(&mut self.ecs, &mut rng, pos));
.for_each(|pos| spawn_monster(&mut self.ecs, &mut rng, *pos));
self.resources.insert(map_builder.map);
self.resources.insert(Camera::new(map_builder.player_start));
self.resources.insert(TurnState::AwaitingInput);

View File

@ -0,0 +1,88 @@
use super::MapArchitect;
use crate::prelude::*;
pub struct AutomataArchitect {}
impl MapArchitect for AutomataArchitect {
fn new(&mut self, rng: &mut RandomNumberGenerator) -> MapBuilder {
let mut mb = MapBuilder {
map: Map::new(),
rooms: Vec::new(),
monster_spawns: Vec::new(),
player_start: Point::zero(),
amulet_start: Point::zero(),
};
self.random_noise_map(rng, &mut mb.map);
for _ in 0..10 {
self.iteration(&mut mb.map);
}
let start = self.find_start(&mb.map);
mb.monster_spawns = mb.spawn_monsters(&start, rng);
mb.player_start = start;
mb.amulet_start = mb.find_most_distant();
mb
}
}
impl AutomataArchitect {
fn random_noise_map(&mut self, rng: &mut RandomNumberGenerator, map: &mut Map) {
map.tiles.iter_mut().for_each(|t| {
let roll = rng.range(0, 100);
if roll > 55 {
*t = TileType::Floor;
} else {
*t = TileType::Wall;
}
})
}
fn count_neighbors(&self, x: i32, y: i32, map: &Map) -> usize {
let mut neighbors = 0;
for iy in -1..=1 {
for ix in -1..=1 {
if !(ix == 0 && iy == 0) && map.tiles[map_idx(x + ix, y + iy)] == TileType::Wall {
neighbors += 1;
}
}
}
neighbors
}
fn iteration(&mut self, map: &mut Map) {
let mut new_tiles = map.tiles.clone();
for y in 1..SCREEN_HEIGHT - 1 {
for x in 1..SCREEN_WIDTH - 1 {
let neighbors = self.count_neighbors(x, y, map);
let idx = map_idx(x, y);
if neighbors > 4 || neighbors == 0 {
new_tiles[idx] = TileType::Wall;
} else {
new_tiles[idx] = TileType::Floor;
}
}
}
map.tiles = new_tiles;
}
fn find_start(&self, map: &Map) -> Point {
let center = Point::new(SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2);
let closest_point = map
.tiles
.iter()
.enumerate()
.filter(|(_, t)| **t == TileType::Floor)
.map(|(idx, _)| {
(
idx,
DistanceAlg::Pythagoras.distance2d(center, map.index_to_point2d(idx)),
)
})
.min_by(|(_, distance), (_, distance2)| distance.partial_cmp(&distance2).unwrap())
.map(|(idx, _)| idx)
.unwrap();
map.index_to_point2d(closest_point)
}
}

27
src/map_builder/empty.rs Normal file
View File

@ -0,0 +1,27 @@
use super::MapArchitect;
use crate::prelude::*;
pub struct EmptyArchitect {}
impl MapArchitect for EmptyArchitect {
fn new(&mut self, rng: &mut RandomNumberGenerator) -> MapBuilder {
let mut mb = MapBuilder {
map: Map::new(),
rooms: Vec::new(),
monster_spawns: Vec::new(),
player_start: Point::zero(),
amulet_start: Point::zero(),
};
mb.fill(TileType::Floor);
mb.player_start = Point::new(SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2);
mb.amulet_start = mb.find_most_distant();
for _ in 0..50 {
mb.monster_spawns.push(Point::new(
rng.range(1, SCREEN_WIDTH),
rng.range(1, SCREEN_HEIGHT),
))
}
mb
}
}

View File

@ -1,35 +1,47 @@
mod automata;
mod empty;
mod rooms;
use automata::AutomataArchitect;
use empty::EmptyArchitect;
use rooms::RoomsArchitect;
use crate::prelude::*;
trait MapArchitect {
fn new(&mut self, rng: &mut RandomNumberGenerator) -> MapBuilder;
}
const NUM_ROOMS: usize = 20;
pub struct MapBuilder {
pub map: Map,
pub rooms: Vec<Rect>,
pub monster_spawns: Vec<Point>,
pub player_start: Point,
pub amulet_start: Point,
}
impl MapBuilder {
pub fn new(rng: &mut RandomNumberGenerator) -> Self {
let mut mb = MapBuilder {
map: Map::new(),
rooms: Vec::new(),
player_start: Point::zero(),
amulet_start: Point::zero(),
};
mb.fill(TileType::Wall);
mb.build_random_rooms(rng);
mb.build_corridors(rng);
mb.player_start = mb.rooms[0].center();
let mut architecht = AutomataArchitect {};
architecht.new(rng)
}
fn fill(&mut self, tile: TileType) {
self.map.tiles.iter_mut().for_each(|t| *t = tile);
}
fn find_most_distant(&self) -> Point {
let dijkstra_map = DijkstraMap::new(
SCREEN_WIDTH,
SCREEN_HEIGHT,
&vec![mb.map.point2d_to_index(mb.player_start)],
&mb.map,
&vec![self.map.point2d_to_index(self.player_start)],
&self.map,
1024.0,
);
const UNREACHABLE: &f32 = &f32::MAX;
mb.amulet_start = mb.map.index_to_point2d(
self.map.index_to_point2d(
dijkstra_map
.map
.iter()
@ -38,13 +50,7 @@ impl MapBuilder {
.max_by(|a, b| a.1.partial_cmp(b.1).unwrap())
.unwrap()
.0,
);
mb
}
fn fill(&mut self, tile: TileType) {
self.map.tiles.iter_mut().for_each(|t| *t = tile);
)
}
fn build_random_rooms(&mut self, rng: &mut RandomNumberGenerator) {
@ -109,4 +115,28 @@ impl MapBuilder {
}
}
}
fn spawn_monsters(&self, start: &Point, rng: &mut RandomNumberGenerator) -> Vec<Point> {
const NUM_MONSTERS: usize = 50;
let mut spawnable_tiles: Vec<Point> = self
.map
.tiles
.iter()
.enumerate()
.filter(|(idx, t)| {
**t == TileType::Floor
&& DistanceAlg::Pythagoras.distance2d(*start, self.map.index_to_point2d(*idx))
> 10.0
})
.map(|(idx, _)| self.map.index_to_point2d(idx))
.collect();
let mut spawns = Vec::new();
for _ in 0..NUM_MONSTERS {
let target_index = rng.random_slice_index(&spawnable_tiles).unwrap();
spawns.push(spawnable_tiles[target_index].clone());
spawnable_tiles.remove(target_index);
}
spawns
}
}

26
src/map_builder/rooms.rs Normal file
View File

@ -0,0 +1,26 @@
use super::MapArchitect;
use crate::prelude::*;
pub struct RoomsArchitect {}
impl MapArchitect for RoomsArchitect {
fn new(&mut self, rng: &mut RandomNumberGenerator) -> MapBuilder {
let mut mb = MapBuilder {
map: Map::new(),
rooms: Vec::new(),
monster_spawns: Vec::new(),
player_start: Point::zero(),
amulet_start: Point::zero(),
};
mb.fill(TileType::Wall);
mb.build_random_rooms(rng);
mb.build_corridors(rng);
mb.player_start = mb.rooms[0].center();
mb.amulet_start = mb.find_most_distant();
for room in mb.rooms.iter().skip(1) {
mb.monster_spawns.push(room.center());
}
mb
}
}