From 0728a1db417664035a1bf4ef242bcbedccc8fb7e Mon Sep 17 00:00:00 2001 From: Llywelwyn Date: Sat, 15 Jul 2023 13:38:51 +0100 Subject: [PATCH] mapgen visualisation --- src/main.rs | 43 +++++++++++- src/map.rs | 4 +- src/map_builders/mod.rs | 4 +- src/map_builders/simple_map.rs | 39 +++++++---- src/player.rs | 123 ++++++++++++++++++--------------- src/spawner.rs | 12 ++-- 6 files changed, 144 insertions(+), 81 deletions(-) diff --git a/src/main.rs b/src/main.rs index 95a26c8..8fe3c75 100644 --- a/src/main.rs +++ b/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, + mapgen_history: Vec, + 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::(); // 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::(); *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::(), ctx); { let positions = self.ecs.read_storage::(); let renderables = self.ecs.read_storage::(); @@ -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::(); gs.ecs.register::(); @@ -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()); diff --git a/src/map.rs b/src/map.rs index 31d528a..4d83f4d 100644 --- a/src/map.rs +++ b/src/map.rs @@ -139,9 +139,7 @@ impl BaseMap for Map { } } -pub fn draw_map(ecs: &World, ctx: &mut Rltk) { - let map = ecs.fetch::(); - +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() { diff --git a/src/map_builders/mod.rs b/src/map_builders/mod.rs index 03653f2..b52448d 100644 --- a/src/map_builders/mod.rs +++ b/src/map_builders/mod.rs @@ -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; + fn take_snapshot(&mut self); } pub fn random_builder(new_depth: i32) -> Box { diff --git a/src/map_builders/simple_map.rs b/src/map_builders/simple_map.rs index 2151e52..aba2510 100644 --- a/src/map_builders/simple_map.rs +++ b/src/map_builders/simple_map.rs @@ -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, + history: Vec, } 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 { + 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(); } } diff --git a/src/player.rs b/src/player.rs index dcd7433..c5f33b8 100644 --- a/src/player.rs +++ b/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::(); let mut players = ecs.write_storage::(); let mut viewsheds = ecs.write_storage::(); @@ -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::(); - let hidden = ecs.read_storage::(); - // Push every entity name in the pile to a vector of strings - let mut item_names: Vec = 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::(); - 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::(); + let hidden = ecs.read_storage::(); + // Push every entity name in the pile to a vector of strings + let mut item_names: Vec = 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::(); + 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 { diff --git a/src/spawner.rs b/src/spawner.rs index 21975fe..880e976 100644 --- a/src/spawner.rs +++ b/src/spawner.rs @@ -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::(); - 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)); } } }