mapgen visualisation
This commit is contained in:
parent
011b26088e
commit
0728a1db41
6 changed files with 144 additions and 81 deletions
43
src/main.rs
43
src/main.rs
|
|
@ -43,6 +43,9 @@ rltk::embedded_resource!(TERMINAL8X8, "../resources/terminal8x8.jpg");
|
|||
rltk::embedded_resource!(SCANLINESFS, "../resources/scanlines.fs");
|
||||
rltk::embedded_resource!(SCANLINESVS, "../resources/scanlines.vs");
|
||||
|
||||
//Consts
|
||||
pub const SHOW_MAPGEN: bool = true;
|
||||
|
||||
#[derive(PartialEq, Copy, Clone)]
|
||||
pub enum RunState {
|
||||
AwaitingInput,
|
||||
|
|
@ -58,22 +61,33 @@ pub enum RunState {
|
|||
GameOver,
|
||||
NextLevel,
|
||||
MagicMapReveal { row: i32, cursed: bool },
|
||||
MapGeneration,
|
||||
}
|
||||
|
||||
pub struct State {
|
||||
pub ecs: World,
|
||||
mapgen_next_state: Option<RunState>,
|
||||
mapgen_history: Vec<Map>,
|
||||
mapgen_index: usize,
|
||||
mapgen_timer: f32,
|
||||
}
|
||||
|
||||
impl State {
|
||||
fn generate_world_map(&mut self, new_depth: i32) {
|
||||
// Visualisation stuff
|
||||
self.mapgen_index = 0;
|
||||
self.mapgen_timer = 0.0;
|
||||
self.mapgen_history.clear();
|
||||
// Create new builder
|
||||
let mut builder = map_builders::random_builder(new_depth);
|
||||
let player_start;
|
||||
// Scope for borrow checker
|
||||
{
|
||||
// Fetch RNG from resources
|
||||
let mut rng = self.ecs.write_resource::<RandomNumberGenerator>();
|
||||
// Build a new map using RNG (to retain seed)
|
||||
builder.build_map(&mut rng);
|
||||
self.mapgen_history = builder.get_snapshot_history();
|
||||
let mut worldmap_resource = self.ecs.write_resource::<Map>();
|
||||
*worldmap_resource = builder.get_map();
|
||||
player_start = builder.get_starting_pos();
|
||||
|
|
@ -236,7 +250,7 @@ impl GameState for State {
|
|||
RunState::MainMenu { .. } => {}
|
||||
_ => {
|
||||
// Draw map and ui
|
||||
draw_map(&self.ecs, ctx);
|
||||
draw_map(&self.ecs.fetch::<Map>(), ctx);
|
||||
{
|
||||
let positions = self.ecs.read_storage::<Position>();
|
||||
let renderables = self.ecs.read_storage::<Renderable>();
|
||||
|
|
@ -442,6 +456,23 @@ impl GameState for State {
|
|||
new_runstate = RunState::MagicMapReveal { row: row + 1, cursed: cursed };
|
||||
}
|
||||
}
|
||||
RunState::MapGeneration => {
|
||||
if !SHOW_MAPGEN {
|
||||
new_runstate = self.mapgen_next_state.unwrap();
|
||||
} else {
|
||||
ctx.cls();
|
||||
draw_map(&self.mapgen_history[self.mapgen_index], ctx);
|
||||
|
||||
self.mapgen_timer += ctx.frame_time_ms;
|
||||
if self.mapgen_timer > 300.0 {
|
||||
self.mapgen_timer = 0.0;
|
||||
self.mapgen_index += 1;
|
||||
if self.mapgen_index >= self.mapgen_history.len() {
|
||||
new_runstate = self.mapgen_next_state.unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
|
|
@ -467,7 +498,13 @@ fn main() -> rltk::BError {
|
|||
context.with_post_scanlines(false);
|
||||
//context.screen_burn_color(RGB::named((150, 255, 255)));
|
||||
|
||||
let mut gs = State { ecs: World::new() };
|
||||
let mut gs = State {
|
||||
ecs: World::new(),
|
||||
mapgen_next_state: Some(RunState::MainMenu { menu_selection: gui::MainMenuSelection::NewGame }),
|
||||
mapgen_index: 0,
|
||||
mapgen_history: Vec::new(),
|
||||
mapgen_timer: 0.0,
|
||||
};
|
||||
|
||||
gs.ecs.register::<Position>();
|
||||
gs.ecs.register::<Renderable>();
|
||||
|
|
@ -517,7 +554,7 @@ fn main() -> rltk::BError {
|
|||
gs.ecs.insert(Point::new(0, 0));
|
||||
gs.ecs.insert(player_entity);
|
||||
gs.ecs.insert(rltk::RandomNumberGenerator::new());
|
||||
gs.ecs.insert(RunState::MainMenu { menu_selection: gui::MainMenuSelection::NewGame });
|
||||
gs.ecs.insert(RunState::MapGeneration {});
|
||||
gs.ecs.insert(particle_system::ParticleBuilder::new());
|
||||
gs.ecs.insert(rex_assets::RexAssets::new());
|
||||
|
||||
|
|
|
|||
|
|
@ -139,9 +139,7 @@ impl BaseMap for Map {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn draw_map(ecs: &World, ctx: &mut Rltk) {
|
||||
let map = ecs.fetch::<Map>();
|
||||
|
||||
pub fn draw_map(map: &Map, ctx: &mut Rltk) {
|
||||
let mut y = 0;
|
||||
let mut x = 0;
|
||||
for (idx, tile) in map.tiles.iter().enumerate() {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use super::{spawner, Map, Position, Rect, TileType};
|
||||
use super::{spawner, Map, Position, Rect, TileType, SHOW_MAPGEN};
|
||||
mod simple_map;
|
||||
use simple_map::SimpleMapBuilder;
|
||||
mod common;
|
||||
|
|
@ -11,6 +11,8 @@ pub trait MapBuilder {
|
|||
fn spawn_entities(&mut self, ecs: &mut World);
|
||||
fn get_map(&mut self) -> Map;
|
||||
fn get_starting_pos(&mut self) -> Position;
|
||||
fn get_snapshot_history(&self) -> Vec<Map>;
|
||||
fn take_snapshot(&mut self);
|
||||
}
|
||||
|
||||
pub fn random_builder(new_depth: i32) -> Box<dyn MapBuilder> {
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
use super::{
|
||||
apply_horizontal_tunnel, apply_room_to_map, apply_vertical_tunnel, spawner, Map, MapBuilder, Position, Rect,
|
||||
TileType,
|
||||
TileType, SHOW_MAPGEN,
|
||||
};
|
||||
use rltk::RandomNumberGenerator;
|
||||
use rltk::{console, RandomNumberGenerator};
|
||||
use specs::prelude::*;
|
||||
|
||||
pub struct SimpleMapBuilder {
|
||||
|
|
@ -10,24 +10,36 @@ pub struct SimpleMapBuilder {
|
|||
starting_position: Position,
|
||||
depth: i32,
|
||||
rooms: Vec<Rect>,
|
||||
history: Vec<Map>,
|
||||
}
|
||||
|
||||
impl MapBuilder for SimpleMapBuilder {
|
||||
fn get_map(&mut self) -> Map {
|
||||
return self.map.clone();
|
||||
}
|
||||
|
||||
fn get_starting_pos(&mut self) -> Position {
|
||||
return self.starting_position.clone();
|
||||
}
|
||||
|
||||
fn build_map(&mut self, rng: &mut RandomNumberGenerator) {
|
||||
return self.rooms_and_corridors(rng);
|
||||
}
|
||||
|
||||
fn spawn_entities(&mut self, ecs: &mut World) {
|
||||
for room in self.rooms.iter().skip(1) {
|
||||
return spawner::spawn_room(ecs, room, self.depth);
|
||||
spawner::spawn_room(ecs, room, self.depth);
|
||||
}
|
||||
}
|
||||
// Getters
|
||||
fn get_map(&mut self) -> Map {
|
||||
return self.map.clone();
|
||||
}
|
||||
fn get_starting_pos(&mut self) -> Position {
|
||||
return self.starting_position.clone();
|
||||
}
|
||||
// Mapgen visualisation stuff
|
||||
fn get_snapshot_history(&self) -> Vec<Map> {
|
||||
return self.history.clone();
|
||||
}
|
||||
fn take_snapshot(&mut self) {
|
||||
if SHOW_MAPGEN {
|
||||
let mut snapshot = self.map.clone();
|
||||
for v in snapshot.revealed_tiles.iter_mut() {
|
||||
*v = true;
|
||||
}
|
||||
self.history.push(snapshot);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -39,6 +51,7 @@ impl SimpleMapBuilder {
|
|||
starting_position: Position { x: 0, y: 0 },
|
||||
depth: new_depth,
|
||||
rooms: Vec::new(),
|
||||
history: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -89,6 +102,8 @@ impl SimpleMapBuilder {
|
|||
}
|
||||
|
||||
self.rooms.push(new_room);
|
||||
console::log("pushed new room");
|
||||
self.take_snapshot();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
123
src/player.rs
123
src/player.rs
|
|
@ -6,7 +6,7 @@ use rltk::{Point, RandomNumberGenerator, Rltk, VirtualKeyCode};
|
|||
use specs::prelude::*;
|
||||
use std::cmp::{max, min};
|
||||
|
||||
pub fn try_move_player(delta_x: i32, delta_y: i32, ecs: &mut World) {
|
||||
pub fn try_move_player(delta_x: i32, delta_y: i32, ecs: &mut World) -> bool {
|
||||
let mut positions = ecs.write_storage::<Position>();
|
||||
let mut players = ecs.write_storage::<Player>();
|
||||
let mut viewsheds = ecs.write_storage::<Viewshed>();
|
||||
|
|
@ -24,7 +24,7 @@ pub fn try_move_player(delta_x: i32, delta_y: i32, ecs: &mut World) {
|
|||
|| pos.y + delta_y < 1
|
||||
|| pos.y + delta_y > map.height - 1
|
||||
{
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
let destination_idx = map.xy_idx(pos.x + delta_x, pos.y + delta_y);
|
||||
|
||||
|
|
@ -32,56 +32,62 @@ pub fn try_move_player(delta_x: i32, delta_y: i32, ecs: &mut World) {
|
|||
let target = combat_stats.get(*potential_target);
|
||||
if let Some(_target) = target {
|
||||
wants_to_melee.insert(entity, WantsToMelee { target: *potential_target }).expect("Add target failed.");
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if !map.blocked[destination_idx] {
|
||||
let names = ecs.read_storage::<Name>();
|
||||
let hidden = ecs.read_storage::<Hidden>();
|
||||
// Push every entity name in the pile to a vector of strings
|
||||
let mut item_names: Vec<String> = Vec::new();
|
||||
let mut some = false;
|
||||
for entity in map.tile_content[destination_idx].iter() {
|
||||
if let Some(_hidden) = hidden.get(*entity) {
|
||||
} else {
|
||||
if let Some(name) = names.get(*entity) {
|
||||
let item_name = &name.name;
|
||||
item_names.push(item_name.to_string());
|
||||
some = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
// If some names were found, append. Logger = logger is necessary
|
||||
// makes logger called a mutable self. It's not the most efficient
|
||||
// but it happens infrequently enough (once per player turn at most)
|
||||
// that it shouldn't matter.
|
||||
if some {
|
||||
let mut logger = gamelog::Logger::new().append("You see a");
|
||||
for i in 0..item_names.len() {
|
||||
if i > 0 && i < item_names.len() {
|
||||
logger = logger.append(", a");
|
||||
}
|
||||
logger = logger.item_name_n(&item_names[i]);
|
||||
}
|
||||
logger.period().log();
|
||||
}
|
||||
pos.x = min((MAPWIDTH as i32) - 1, max(0, pos.x + delta_x));
|
||||
pos.y = min((MAPHEIGHT as i32) - 1, max(0, pos.y + delta_y));
|
||||
|
||||
// Dirty viewsheds, and check only now if telepath viewshed exists
|
||||
viewshed.dirty = true;
|
||||
|
||||
let is_telepath = telepaths.get_mut(entity);
|
||||
if let Some(telepathy) = is_telepath {
|
||||
telepathy.dirty = true;
|
||||
}
|
||||
let mut ppos = ecs.write_resource::<Point>();
|
||||
ppos.x = pos.x;
|
||||
ppos.y = pos.y;
|
||||
entity_moved.insert(entity, EntityMoved {}).expect("Unable to insert marker");
|
||||
if map.blocked[destination_idx] {
|
||||
return false;
|
||||
}
|
||||
|
||||
let names = ecs.read_storage::<Name>();
|
||||
let hidden = ecs.read_storage::<Hidden>();
|
||||
// Push every entity name in the pile to a vector of strings
|
||||
let mut item_names: Vec<String> = Vec::new();
|
||||
let mut some = false;
|
||||
for entity in map.tile_content[destination_idx].iter() {
|
||||
if let Some(_hidden) = hidden.get(*entity) {
|
||||
} else {
|
||||
if let Some(name) = names.get(*entity) {
|
||||
let item_name = &name.name;
|
||||
item_names.push(item_name.to_string());
|
||||
some = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
// If some names were found, append. Logger = logger is necessary
|
||||
// makes logger called a mutable self. It's not the most efficient
|
||||
// but it happens infrequently enough (once per player turn at most)
|
||||
// that it shouldn't matter.
|
||||
if some {
|
||||
let mut logger = gamelog::Logger::new().append("You see a");
|
||||
for i in 0..item_names.len() {
|
||||
if i > 0 && i < item_names.len() {
|
||||
logger = logger.append(", a");
|
||||
}
|
||||
logger = logger.item_name_n(&item_names[i]);
|
||||
}
|
||||
logger.period().log();
|
||||
}
|
||||
pos.x = min((MAPWIDTH as i32) - 1, max(0, pos.x + delta_x));
|
||||
pos.y = min((MAPHEIGHT as i32) - 1, max(0, pos.y + delta_y));
|
||||
|
||||
// Dirty viewsheds, and check only now if telepath viewshed exists
|
||||
viewshed.dirty = true;
|
||||
|
||||
let is_telepath = telepaths.get_mut(entity);
|
||||
if let Some(telepathy) = is_telepath {
|
||||
telepathy.dirty = true;
|
||||
}
|
||||
let mut ppos = ecs.write_resource::<Point>();
|
||||
ppos.x = pos.x;
|
||||
ppos.y = pos.y;
|
||||
entity_moved.insert(entity, EntityMoved {}).expect("Unable to insert marker");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
fn get_item(ecs: &mut World) {
|
||||
|
|
@ -111,27 +117,28 @@ fn get_item(ecs: &mut World) {
|
|||
|
||||
pub fn player_input(gs: &mut State, ctx: &mut Rltk) -> RunState {
|
||||
// Player movement
|
||||
let mut result = false;
|
||||
match ctx.key {
|
||||
None => return RunState::AwaitingInput,
|
||||
Some(key) => match key {
|
||||
// Cardinals
|
||||
VirtualKeyCode::Left | VirtualKeyCode::Numpad4 | VirtualKeyCode::H => {
|
||||
try_move_player(-1, 0, &mut gs.ecs);
|
||||
result = try_move_player(-1, 0, &mut gs.ecs);
|
||||
}
|
||||
VirtualKeyCode::Right | VirtualKeyCode::Numpad6 | VirtualKeyCode::L => {
|
||||
try_move_player(1, 0, &mut gs.ecs);
|
||||
result = try_move_player(1, 0, &mut gs.ecs);
|
||||
}
|
||||
VirtualKeyCode::Up | VirtualKeyCode::Numpad8 | VirtualKeyCode::K => {
|
||||
try_move_player(0, -1, &mut gs.ecs);
|
||||
result = try_move_player(0, -1, &mut gs.ecs);
|
||||
}
|
||||
VirtualKeyCode::Down | VirtualKeyCode::Numpad2 | VirtualKeyCode::J => {
|
||||
try_move_player(0, 1, &mut gs.ecs);
|
||||
result = try_move_player(0, 1, &mut gs.ecs);
|
||||
}
|
||||
// Diagonals
|
||||
VirtualKeyCode::Numpad9 | VirtualKeyCode::U => try_move_player(1, -1, &mut gs.ecs),
|
||||
VirtualKeyCode::Numpad7 | VirtualKeyCode::Y => try_move_player(-1, -1, &mut gs.ecs),
|
||||
VirtualKeyCode::Numpad3 | VirtualKeyCode::N => try_move_player(1, 1, &mut gs.ecs),
|
||||
VirtualKeyCode::Numpad1 | VirtualKeyCode::B => try_move_player(-1, 1, &mut gs.ecs),
|
||||
VirtualKeyCode::Numpad9 | VirtualKeyCode::U => result = try_move_player(1, -1, &mut gs.ecs),
|
||||
VirtualKeyCode::Numpad7 | VirtualKeyCode::Y => result = try_move_player(-1, -1, &mut gs.ecs),
|
||||
VirtualKeyCode::Numpad3 | VirtualKeyCode::N => result = try_move_player(1, 1, &mut gs.ecs),
|
||||
VirtualKeyCode::Numpad1 | VirtualKeyCode::B => result = try_move_player(-1, 1, &mut gs.ecs),
|
||||
// Depth
|
||||
VirtualKeyCode::Period => {
|
||||
if ctx.shift {
|
||||
|
|
@ -158,7 +165,11 @@ pub fn player_input(gs: &mut State, ctx: &mut Rltk) -> RunState {
|
|||
}
|
||||
},
|
||||
}
|
||||
RunState::PlayerTurn
|
||||
if result {
|
||||
return RunState::PlayerTurn;
|
||||
} else {
|
||||
return RunState::AwaitingInput;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn try_next_level(ecs: &mut World) -> bool {
|
||||
|
|
|
|||
|
|
@ -78,12 +78,10 @@ pub fn spawn_room(ecs: &mut World, room: &Rect, map_depth: i32) {
|
|||
// Scope for borrow checker
|
||||
{
|
||||
let mut rng = ecs.write_resource::<RandomNumberGenerator>();
|
||||
let num_spawns = rng.roll_dice(1, MAX_ENTITIES + 3) + (map_depth - 1) - 3;
|
||||
// With a MAX_ENTITIES of 4, this means each room has between:
|
||||
// d1: -2 to 4
|
||||
// d2: -1 to 5
|
||||
// d3: 0 to 6
|
||||
// etc.
|
||||
let num_spawns = rng.roll_dice(1, MAX_ENTITIES + 2) - 2;
|
||||
console::log(format!("room of: {}", num_spawns));
|
||||
// [-1, 0, 1, 2, 3, 4] things in a room
|
||||
// 2/6 chance for nothing, 4/6 for something
|
||||
|
||||
for _i in 0..num_spawns {
|
||||
let mut added = false;
|
||||
|
|
@ -104,8 +102,10 @@ pub fn spawn_room(ecs: &mut World, room: &Rect, map_depth: i32) {
|
|||
}
|
||||
spawn_points.insert(idx, spawn_table.roll(&mut rng));
|
||||
added = true;
|
||||
console::log(format!("added spawnpoint"));
|
||||
} else {
|
||||
tries += 1;
|
||||
console::log(format!("failed {} times", tries));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue