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_player(&mut ecs, map_builder.player_start);
|
||||||
spawn_amulet_of_yala(&mut ecs, map_builder.amulet_start);
|
spawn_amulet_of_yala(&mut ecs, map_builder.amulet_start);
|
||||||
map_builder
|
map_builder
|
||||||
.rooms
|
.monster_spawns
|
||||||
.iter()
|
.iter()
|
||||||
.skip(1)
|
.for_each(|pos| spawn_monster(&mut ecs, &mut rng, *pos));
|
||||||
.map(|r| r.center())
|
|
||||||
.for_each(|pos| spawn_monster(&mut ecs, &mut rng, pos));
|
|
||||||
resources.insert(map_builder.map);
|
resources.insert(map_builder.map);
|
||||||
resources.insert(Camera::new(map_builder.player_start));
|
resources.insert(Camera::new(map_builder.player_start));
|
||||||
|
|
||||||
@ -94,11 +92,9 @@ impl State {
|
|||||||
spawn_player(&mut self.ecs, map_builder.player_start);
|
spawn_player(&mut self.ecs, map_builder.player_start);
|
||||||
spawn_amulet_of_yala(&mut self.ecs, map_builder.amulet_start);
|
spawn_amulet_of_yala(&mut self.ecs, map_builder.amulet_start);
|
||||||
map_builder
|
map_builder
|
||||||
.rooms
|
.monster_spawns
|
||||||
.iter()
|
.iter()
|
||||||
.skip(1)
|
.for_each(|pos| spawn_monster(&mut self.ecs, &mut rng, *pos));
|
||||||
.map(|r| r.center())
|
|
||||||
.for_each(|pos| spawn_monster(&mut self.ecs, &mut rng, pos));
|
|
||||||
self.resources.insert(map_builder.map);
|
self.resources.insert(map_builder.map);
|
||||||
self.resources.insert(Camera::new(map_builder.player_start));
|
self.resources.insert(Camera::new(map_builder.player_start));
|
||||||
self.resources.insert(TurnState::AwaitingInput);
|
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::*;
|
use crate::prelude::*;
|
||||||
|
|
||||||
|
trait MapArchitect {
|
||||||
|
fn new(&mut self, rng: &mut RandomNumberGenerator) -> MapBuilder;
|
||||||
|
}
|
||||||
|
|
||||||
const NUM_ROOMS: usize = 20;
|
const NUM_ROOMS: usize = 20;
|
||||||
pub struct MapBuilder {
|
pub struct MapBuilder {
|
||||||
pub map: Map,
|
pub map: Map,
|
||||||
pub rooms: Vec<Rect>,
|
pub rooms: Vec<Rect>,
|
||||||
|
pub monster_spawns: Vec<Point>,
|
||||||
pub player_start: Point,
|
pub player_start: Point,
|
||||||
pub amulet_start: Point,
|
pub amulet_start: Point,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MapBuilder {
|
impl MapBuilder {
|
||||||
pub fn new(rng: &mut RandomNumberGenerator) -> Self {
|
pub fn new(rng: &mut RandomNumberGenerator) -> Self {
|
||||||
let mut mb = MapBuilder {
|
let mut architecht = AutomataArchitect {};
|
||||||
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();
|
|
||||||
|
|
||||||
|
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(
|
let dijkstra_map = DijkstraMap::new(
|
||||||
SCREEN_WIDTH,
|
SCREEN_WIDTH,
|
||||||
SCREEN_HEIGHT,
|
SCREEN_HEIGHT,
|
||||||
&vec![mb.map.point2d_to_index(mb.player_start)],
|
&vec![self.map.point2d_to_index(self.player_start)],
|
||||||
&mb.map,
|
&self.map,
|
||||||
1024.0,
|
1024.0,
|
||||||
);
|
);
|
||||||
const UNREACHABLE: &f32 = &f32::MAX;
|
const UNREACHABLE: &f32 = &f32::MAX;
|
||||||
mb.amulet_start = mb.map.index_to_point2d(
|
self.map.index_to_point2d(
|
||||||
dijkstra_map
|
dijkstra_map
|
||||||
.map
|
.map
|
||||||
.iter()
|
.iter()
|
||||||
@ -38,13 +50,7 @@ impl MapBuilder {
|
|||||||
.max_by(|a, b| a.1.partial_cmp(b.1).unwrap())
|
.max_by(|a, b| a.1.partial_cmp(b.1).unwrap())
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.0,
|
.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) {
|
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