Create a rough town map generator, starting section 5.4

This commit is contained in:
Timothy Warren 2021-12-24 10:20:29 -05:00
parent 5d7f9e509f
commit 2dc420bef9
8 changed files with 185 additions and 43 deletions

View File

@ -1,5 +1,5 @@
use rltk::{Point, Rltk, RGB}; use ::rltk::{Point, Rltk, RGB};
use specs::prelude::*; use ::specs::prelude::*;
use crate::{Hidden, Map, Position, Renderable, TileType}; use crate::{Hidden, Map, Position, Renderable, TileType};
@ -127,25 +127,22 @@ pub fn render_debug_map(map: &Map, ctx: &mut Rltk) {
} }
fn get_tile_glyph(idx: usize, map: &Map) -> (rltk::FontCharType, RGB, RGB) { fn get_tile_glyph(idx: usize, map: &Map) -> (rltk::FontCharType, RGB, RGB) {
let glyph;
let mut fg;
let mut bg = RGB::from_f32(0., 0., 0.); let mut bg = RGB::from_f32(0., 0., 0.);
match map.tiles[idx] { let (glyph, mut fg) = match map.tiles[idx] {
TileType::Floor => { TileType::Floor => (rltk::to_cp437('.'), RGB::from_f32(0., 0.5, 0.5)),
glyph = rltk::to_cp437('.'); TileType::WoodFloor => (rltk::to_cp437('.'), RGB::named(rltk::CHOCOLATE)),
fg = RGB::from_f32(0., 0.5, 0.5);
}
TileType::Wall => { TileType::Wall => {
let x = idx as i32 % map.width; let x = idx as i32 % map.width;
let y = idx as i32 / map.width; let y = idx as i32 / map.width;
glyph = wall_glyph(&*map, x, y); (wall_glyph(&*map, x, y), RGB::from_f32(0., 1.0, 0.))
fg = RGB::from_f32(0., 1.0, 0.);
}
TileType::DownStairs => {
glyph = rltk::to_cp437('>');
fg = RGB::from_f32(0., 1.0, 1.0);
} }
TileType::DownStairs => (rltk::to_cp437('>'), RGB::from_f32(0., 1.0, 1.0)),
TileType::Bridge => (rltk::to_cp437('.'), RGB::named(rltk::CHOCOLATE)),
TileType::Road => (rltk::to_cp437('~'), RGB::named(rltk::GRAY)),
TileType::Grass => (rltk::to_cp437('"'), RGB::named(rltk::GREEN)),
TileType::ShallowWater => (rltk::to_cp437('≈'), RGB::named(rltk::CYAN)),
TileType::DeepWater => (rltk::to_cp437('≈'), RGB::named(rltk::NAVY_BLUE)),
}; };
if map.bloodstains.contains(&idx) { if map.bloodstains.contains(&idx) {

View File

@ -24,6 +24,9 @@ mod visibility_system;
#[macro_use] #[macro_use]
extern crate lazy_static; extern crate lazy_static;
use ::rltk::{GameState, Point, RandomNumberGenerator, Rltk};
use ::specs::prelude::*;
use ::specs::saveload::{SimpleMarker, SimpleMarkerAllocator};
use components::*; use components::*;
use damage_system::DamageSystem; use damage_system::DamageSystem;
pub use game_log::GameLog; pub use game_log::GameLog;
@ -34,9 +37,6 @@ use melee_combat_system::MeleeCombatSystem;
use monster_ai_system::MonsterAI; use monster_ai_system::MonsterAI;
use player::*; use player::*;
pub use rect::Rect; pub use rect::Rect;
use rltk::{GameState, Point, RandomNumberGenerator, Rltk};
use specs::prelude::*;
use specs::saveload::{SimpleMarker, SimpleMarkerAllocator};
use visibility_system::VisibilitySystem; use visibility_system::VisibilitySystem;
/// Cut down on the amount of syntax to register components /// Cut down on the amount of syntax to register components
@ -450,7 +450,7 @@ impl State {
self.mapgen_history.clear(); self.mapgen_history.clear();
let mut rng = self.ecs.write_resource::<RandomNumberGenerator>(); let mut rng = self.ecs.write_resource::<RandomNumberGenerator>();
let mut builder = map_builders::random_builder(new_depth, &mut rng, 80, 50); let mut builder = map_builders::level_builder(new_depth, &mut rng, 80, 50);
builder.build_map(&mut rng); builder.build_map(&mut rng);
std::mem::drop(rng); std::mem::drop(rng);

View File

@ -1,15 +1,13 @@
mod tiletype;
use std::collections::HashSet; use std::collections::HashSet;
use rltk::{Algorithm2D, BaseMap, Point, SmallVec}; use ::rltk::{Algorithm2D, BaseMap, Point, SmallVec};
use serde::{Deserialize, Serialize}; use ::serde::{Deserialize, Serialize};
use specs::prelude::*; use ::specs::prelude::*;
pub use tiletype::{tile_opaque, tile_walkable, TileType};
#[derive(PartialEq, Eq, Hash, Copy, Clone, Serialize, Deserialize)] use crate::map::tiletype::tile_cost;
pub enum TileType {
Wall,
Floor,
DownStairs,
}
#[derive(Default, Serialize, Deserialize, Clone)] #[derive(Default, Serialize, Deserialize, Clone)]
pub struct Map { pub struct Map {
@ -45,7 +43,7 @@ impl Map {
pub fn populate_blocked(&mut self) { pub fn populate_blocked(&mut self) {
for (i, tile) in self.tiles.iter_mut().enumerate() { for (i, tile) in self.tiles.iter_mut().enumerate() {
self.blocked[i] = *tile == TileType::Wall; self.blocked[i] = !tile_walkable(*tile);
} }
} }
@ -76,7 +74,11 @@ impl Map {
impl BaseMap for Map { impl BaseMap for Map {
fn is_opaque(&self, idx: usize) -> bool { fn is_opaque(&self, idx: usize) -> bool {
self.tiles[idx] == TileType::Wall || self.view_blocked.contains(&idx) if idx > 0 && idx < self.tiles.len() {
tile_opaque(self.tiles[idx]) || self.view_blocked.contains(&idx)
} else {
true
}
} }
fn get_available_exits(&self, idx: usize) -> SmallVec<[(usize, f32); 10]> { fn get_available_exits(&self, idx: usize) -> SmallVec<[(usize, f32); 10]> {
@ -84,33 +86,34 @@ impl BaseMap for Map {
let x = idx as i32 % self.width; let x = idx as i32 % self.width;
let y = idx as i32 / self.width; let y = idx as i32 / self.width;
let w = self.width as usize; let w = self.width as usize;
let tt = self.tiles[idx];
// Cardinal directions // Cardinal directions
if self.is_exit_valid(x - 1, y) { if self.is_exit_valid(x - 1, y) {
exits.push((idx - 1, 1.0)) exits.push((idx - 1, tile_cost(tt)))
}; };
if self.is_exit_valid(x + 1, y) { if self.is_exit_valid(x + 1, y) {
exits.push((idx + 1, 1.0)) exits.push((idx + 1, tile_cost(tt)))
}; };
if self.is_exit_valid(x, y - 1) { if self.is_exit_valid(x, y - 1) {
exits.push((idx - w, 1.0)) exits.push((idx - w, tile_cost(tt)))
}; };
if self.is_exit_valid(x, y + 1) { if self.is_exit_valid(x, y + 1) {
exits.push((idx + w, 1.0)) exits.push((idx + w, tile_cost(tt)))
}; };
// Diagonals // Diagonals
if self.is_exit_valid(x - 1, y - 1) { if self.is_exit_valid(x - 1, y - 1) {
exits.push(((idx - w) - 1, 1.45)); exits.push(((idx - w) - 1, tile_cost(tt) * 1.45));
} }
if self.is_exit_valid(x + 1, y - 1) { if self.is_exit_valid(x + 1, y - 1) {
exits.push(((idx - w) + 1, 1.45)); exits.push(((idx - w) + 1, tile_cost(tt) * 1.45));
} }
if self.is_exit_valid(x - 1, y + 1) { if self.is_exit_valid(x - 1, y + 1) {
exits.push(((idx + w) - 1, 1.45)); exits.push(((idx + w) - 1, tile_cost(tt) * 1.45));
} }
if self.is_exit_valid(x + 1, y + 1) { if self.is_exit_valid(x + 1, y + 1) {
exits.push(((idx + w) + 1, 1.45)); exits.push(((idx + w) + 1, tile_cost(tt) * 1.45));
} }
exits exits

40
src/map/tiletype.rs Normal file
View File

@ -0,0 +1,40 @@
use serde::{Deserialize, Serialize};
#[derive(PartialEq, Eq, Hash, Copy, Clone, Serialize, Deserialize)]
pub enum TileType {
Wall,
Floor,
DownStairs,
Road,
Grass,
ShallowWater,
DeepWater,
WoodFloor,
Bridge,
}
pub fn tile_walkable(tt: TileType) -> bool {
matches!(
tt,
TileType::Floor
| TileType::DownStairs
| TileType::Road
| TileType::Grass
| TileType::ShallowWater
| TileType::WoodFloor
| TileType::Bridge
)
}
pub fn tile_opaque(tt: TileType) -> bool {
matches!(tt, TileType::Wall)
}
pub fn tile_cost(tt: TileType) -> f32 {
match tt {
TileType::Road => 0.8,
TileType::Grass => 1.1,
TileType::ShallowWater => 1.2,
_ => 1.0,
}
}

View File

@ -23,6 +23,7 @@ mod rooms_corridors_dogleg;
mod rooms_corridors_lines; mod rooms_corridors_lines;
mod rooms_corridors_nearest; mod rooms_corridors_nearest;
mod simple_map; mod simple_map;
mod town;
mod voronoi; mod voronoi;
mod voronoi_spawning; mod voronoi_spawning;
mod waveform_collapse; mod waveform_collapse;
@ -53,6 +54,7 @@ use rooms_corridors_lines::StraightLineCorridors;
use rooms_corridors_nearest::NearestCorridors; use rooms_corridors_nearest::NearestCorridors;
use simple_map::SimpleMapBuilder; use simple_map::SimpleMapBuilder;
use specs::prelude::*; use specs::prelude::*;
use town::town_builder;
use voronoi::VoronoiCellBuilder; use voronoi::VoronoiCellBuilder;
use voronoi_spawning::VoronoiSpawning; use voronoi_spawning::VoronoiSpawning;
use waveform_collapse::WaveformCollapseBuilder; use waveform_collapse::WaveformCollapseBuilder;
@ -175,7 +177,7 @@ pub trait MetaMapBuilder {
fn build_map(&mut self, rng: &mut RandomNumberGenerator, build_data: &mut BuilderMap); fn build_map(&mut self, rng: &mut RandomNumberGenerator, build_data: &mut BuilderMap);
} }
fn random_start_position(rng: &mut rltk::RandomNumberGenerator) -> (XStart, YStart) { pub fn random_start_position(rng: &mut rltk::RandomNumberGenerator) -> (XStart, YStart) {
let x = match rng.roll_dice(1, 3) { let x = match rng.roll_dice(1, 3) {
1 => XStart::Left, 1 => XStart::Left,
2 => XStart::Center, 2 => XStart::Center,
@ -330,3 +332,16 @@ pub fn random_builder(
builder builder
} }
pub fn level_builder(
new_depth: i32,
rng: &mut RandomNumberGenerator,
width: i32,
height: i32,
) -> BuilderChain {
rltk::console::log(format!("Depth: {}", new_depth));
match new_depth {
1 => town_builder(new_depth, rng, width, height),
_ => random_builder(new_depth, rng, width, height),
}
}

View File

@ -1,7 +1,7 @@
use rltk::RandomNumberGenerator; use rltk::RandomNumberGenerator;
use crate::map_builders::{BuilderMap, MetaMapBuilder}; use crate::map_builders::{BuilderMap, MetaMapBuilder};
use crate::{Position, TileType}; use crate::{map, Position};
#[allow(dead_code)] #[allow(dead_code)]
pub enum XStart { pub enum XStart {
@ -49,7 +49,7 @@ impl AreaStartingPosition {
let mut available_floors: Vec<(usize, f32)> = Vec::new(); let mut available_floors: Vec<(usize, f32)> = Vec::new();
for (idx, tiletype) in build_data.map.tiles.iter().enumerate() { for (idx, tiletype) in build_data.map.tiles.iter().enumerate() {
if *tiletype == TileType::Floor { if map::tile_walkable(*tiletype) {
available_floors.push(( available_floors.push((
idx, idx,
rltk::DistanceAlg::PythagorasSquared.distance2d( rltk::DistanceAlg::PythagorasSquared.distance2d(

89
src/map_builders/town.rs Normal file
View File

@ -0,0 +1,89 @@
use rltk::RandomNumberGenerator;
use super::{BuilderChain, BuilderMap, InitialMapBuilder};
use crate::map_builders::area_starting_points::AreaStartingPosition;
use crate::map_builders::distant_exit::DistantExit;
use crate::TileType;
pub fn town_builder(
new_depth: i32,
rng: &mut RandomNumberGenerator,
width: i32,
height: i32,
) -> BuilderChain {
let (start_x, start_y) = super::random_start_position(rng);
let mut chain = BuilderChain::new(new_depth, width, height);
chain
.start_with(TownBuilder::new())
.with(AreaStartingPosition::new(start_x, start_y))
.with(DistantExit::new());
chain
}
pub struct TownBuilder {}
impl InitialMapBuilder for TownBuilder {
#[allow(dead_code)]
fn build_map(&mut self, rng: &mut RandomNumberGenerator, build_data: &mut BuilderMap) {
self.build_rooms(rng, build_data);
}
}
impl TownBuilder {
pub fn new() -> Box<TownBuilder> {
Box::new(TownBuilder {})
}
pub fn build_rooms(&mut self, rng: &mut RandomNumberGenerator, build_data: &mut BuilderMap) {
self.grass_layer(build_data);
self.water_and_piers(rng, build_data);
// Make visible for screenshot
for t in build_data.map.visible_tiles.iter_mut() {
*t = true;
}
build_data.take_snapshot();
}
fn grass_layer(&mut self, build_data: &mut BuilderMap) {
// We'll start with a nice layer of grass
for t in build_data.map.tiles.iter_mut() {
*t = TileType::Grass
}
build_data.take_snapshot();
}
fn water_and_piers(&mut self, rng: &mut RandomNumberGenerator, build_data: &mut BuilderMap) {
let mut n = (rng.roll_dice(1, 65535) as f32) / 65535_f32;
let mut water_width: Vec<i32> = Vec::new();
for y in 0..build_data.height {
let n_water = (f32::sin(n) * 10.0) as i32 + 14 + rng.roll_dice(1, 6);
water_width.push(n_water);
n += 0.1;
for x in 0..n_water {
let idx = build_data.map.xy_idx(x, y);
build_data.map.tiles[idx] = TileType::DeepWater;
}
for x in n_water..n_water + 3 {
let idx = build_data.map.xy_idx(x, y);
build_data.map.tiles[idx] = TileType::ShallowWater;
}
}
build_data.take_snapshot();
// Add piers
for _i in 0..rng.roll_dice(1, 4) + 6 {
let y = rng.roll_dice(1, build_data.height) - 1;
for x in 2 + rng.roll_dice(1, 6)..water_width[y as usize] + 4 {
let idx = build_data.map.xy_idx(x, y);
build_data.map.tiles[idx] = TileType::WoodFloor;
}
}
build_data.take_snapshot();
}
}

View File

@ -1,7 +1,5 @@
use serde::Deserialize; use serde::Deserialize;
use super::Renderable;
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug)]
pub struct SpawnTableEntry { pub struct SpawnTableEntry {
pub name: String, pub name: String,