Create a rough town map generator, starting section 5.4
This commit is contained in:
parent
5d7f9e509f
commit
2dc420bef9
@ -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) {
|
||||||
|
@ -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);
|
||||||
|
41
src/map.rs
41
src/map.rs
@ -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
40
src/map/tiletype.rs
Normal 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,
|
||||||
|
}
|
||||||
|
}
|
@ -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),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -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
89
src/map_builders/town.rs
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
@ -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,
|
||||||
|
Loading…
Reference in New Issue
Block a user