map types
This commit is contained in:
parent
7ae2967522
commit
35f8e4cc73
12
src/main.rs
12
src/main.rs
@ -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);
|
||||
|
88
src/map_builder/automata.rs
Normal file
88
src/map_builder/automata.rs
Normal 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
27
src/map_builder/empty.rs
Normal 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
|
||||
}
|
||||
}
|
@ -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
26
src/map_builder/rooms.rs
Normal 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
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user