From e55218c32517ddce70a0b8bab6ede83b0ab1f615 Mon Sep 17 00:00:00 2001 From: Timothy Warren Date: Wed, 1 Dec 2021 14:45:27 -0500 Subject: [PATCH] Make map setup more DRY, add map generation visualizer, completes section 4.2 --- src/main.rs | 170 ++++++++++++++++++--------------- src/map.rs | 4 +- src/map_builders/mod.rs | 2 + src/map_builders/simple_map.rs | 23 ++++- 4 files changed, 114 insertions(+), 85 deletions(-) diff --git a/src/main.rs b/src/main.rs index 362cb10..c1944db 100644 --- a/src/main.rs +++ b/src/main.rs @@ -23,11 +23,10 @@ mod spawner; mod trigger_system; mod visibility_system; -use crate::inventory_system::ItemRemoveSystem; use components::*; use damage_system::DamageSystem; pub use game_log::GameLog; -use inventory_system::{ItemCollectionSystem, ItemDropSystem, ItemUseSystem}; +use inventory_system::{ItemCollectionSystem, ItemDropSystem, ItemRemoveSystem, ItemUseSystem}; pub use map::*; use map_indexing_system::MapIndexingSystem; use melee_combat_system::MeleeCombatSystem; @@ -47,6 +46,8 @@ macro_rules! register { } } +const SHOW_MAPGEN_VISUALIZER: bool = true; + #[derive(PartialEq, Copy, Clone)] pub enum RunState { AwaitingInput, @@ -69,15 +70,28 @@ pub enum RunState { MagicMapReveal { row: i32, }, + MapGeneration, } pub struct State { pub ecs: World, + mapgen_next_state: Option, + mapgen_history: Vec, + mapgen_index: usize, + mapgen_timer: f32, } impl State { fn new() -> Self { - State { ecs: World::new() } + 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, + } } fn run_systems(&mut self) { @@ -137,7 +151,7 @@ impl GameState for State { RunState::GameOver { .. } => {} _ => { // Draw the UI - draw_map(&self.ecs, ctx); + draw_map(&self.ecs.fetch::(), ctx); { let positions = self.ecs.read_storage::(); let renderables = self.ecs.read_storage::(); @@ -160,6 +174,22 @@ impl GameState for State { } match newrunstate { + RunState::MapGeneration => { + if !SHOW_MAPGEN_VISUALIZER { + newrunstate = self.mapgen_next_state.unwrap(); + } + 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() { + newrunstate = self.mapgen_next_state.unwrap(); + } + } + } RunState::PreRun => { self.run_systems(); self.ecs.maintain(); @@ -384,14 +414,60 @@ impl State { } // Build a new map and place the player - let mut builder; let current_depth; + { + let worldmap_resource = self.ecs.fetch::(); + current_depth = worldmap_resource.depth; + } + self.generate_world_map(current_depth + 1); + + // Notify the player + let player_entity = self.ecs.fetch::(); + let mut gamelog = self.ecs.fetch_mut::(); + gamelog.append("You descend to the next level, and take a moment to heal."); + + // Give them some health + let mut player_health_store = self.ecs.write_storage::(); + if let Some(player_health) = player_health_store.get_mut(*player_entity) { + player_health.hp = i32::max(player_health.hp, player_health.max_hp / 2); + } + } + + fn game_over_cleanup(&mut self) { + // Delete everything + let mut to_delete = Vec::new(); + for e in self.ecs.entities().join() { + to_delete.push(e); + } + for del in to_delete.iter() { + self.ecs + .delete_entity(*del) + .expect("Failed to delete entity"); + } + + // Spawn a new player + { + let player_entity = spawner::player(&mut self.ecs, 0, 0); + let mut player_entity_writer = self.ecs.write_resource::(); + *player_entity_writer = player_entity; + } + + // Build a new map and place the player + self.generate_world_map(1); + } + + fn generate_world_map(&mut self, new_depth: i32) { + self.mapgen_index = 0; + self.mapgen_timer = 0.0; + self.mapgen_history.clear(); + + let mut builder = map_builders::random_builder(new_depth); + builder.build_map(); + self.mapgen_history = builder.get_snapshot_history(); + let player_start; { let mut worldmap_resource = self.ecs.write_resource::(); - current_depth = worldmap_resource.depth; - builder = map_builders::random_builder(current_depth + 1); - builder.build_map(); *worldmap_resource = builder.get_map(); player_start = builder.get_starting_position(); } @@ -415,63 +491,6 @@ impl State { if let Some(vs) = viewshed_components.get_mut(*player_entity) { vs.dirty = true; } - - // Notify the player and give them some health - let mut gamelog = self.ecs.fetch_mut::(); - gamelog - .entries - .push("You descend to the next level, and take a moment to heal.".to_string()); - let mut player_health_store = self.ecs.write_storage::(); - let player_health = player_health_store.get_mut(*player_entity); - if let Some(player_health) = player_health { - player_health.hp = i32::max(player_health.hp, player_health.max_hp / 2); - } - } - - fn game_over_cleanup(&mut self) { - // Delete everything - let mut to_delete = Vec::new(); - for e in self.ecs.entities().join() { - to_delete.push(e); - } - for del in to_delete.iter() { - self.ecs - .delete_entity(*del) - .expect("Failed to delete entity"); - } - - // Build a new map and place the player - let mut builder = map_builders::random_builder(1); - let player_start; - { - let mut worldmap_resource = self.ecs.write_resource::(); - builder.build_map(); - player_start = builder.get_starting_position(); - *worldmap_resource = builder.get_map(); - } - - // Spawn bad guys - builder.spawn_entities(&mut self.ecs); - - // Place the player and update resources - let (player_x, player_y) = (player_start.x, player_start.y); - let player_entity = spawner::player(&mut self.ecs, player_x, player_y); - let mut player_position = self.ecs.write_resource::(); - *player_position = Point::new(player_x, player_y); - let mut position_components = self.ecs.write_storage::(); - let mut player_entity_writer = self.ecs.write_resource::(); - *player_entity_writer = player_entity; - if let Some(player_pos_comp) = position_components.get_mut(player_entity) { - player_pos_comp.x = player_x; - player_pos_comp.y = player_y; - } - - // Mark the player's visibility as dirty - let mut viewshed_components = self.ecs.write_storage::(); - let vs = viewshed_components.get_mut(player_entity); - if let Some(vs) = vs { - vs.dirty = true; - } } } @@ -526,26 +545,19 @@ fn main() -> rltk::BError { gs.ecs.insert(SimpleMarkerAllocator::::new()); - let mut builder = map_builders::random_builder(1); - builder.build_map(); - let player_start = builder.get_starting_position(); - let map = builder.get_map(); - let (player_x, player_y) = (player_start.x, player_start.y); - - let player_entity = spawner::player(&mut gs.ecs, player_x, player_y); - + gs.ecs.insert(Map::new(1)); + gs.ecs.insert(Point::new(0, 0)); gs.ecs.insert(rltk::RandomNumberGenerator::new()); - builder.spawn_entities(&mut gs.ecs); - gs.ecs.insert(map); - gs.ecs.insert(Point::new(player_x, player_y)); + let player_entity = spawner::player(&mut gs.ecs, 0, 0); gs.ecs.insert(player_entity); - gs.ecs.insert(RunState::MainMenu { - menu_selection: gui::MainMenuSelection::NewGame, - }); + + gs.ecs.insert(RunState::MapGeneration {}); gs.ecs.insert(GameLog::new("Welcome to Rusty Roguelike")); gs.ecs.insert(particle_system::ParticleBuilder::new()); gs.ecs.insert(rex_assets::RexAssets::new()); + gs.generate_world_map(1); + rltk::main_loop(context, gs) } diff --git a/src/map.rs b/src/map.rs index 3c726ae..71f9c56 100644 --- a/src/map.rs +++ b/src/map.rs @@ -130,9 +130,7 @@ impl Algorithm2D 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; diff --git a/src/map_builders/mod.rs b/src/map_builders/mod.rs index f5ab291..3dc5c91 100644 --- a/src/map_builders/mod.rs +++ b/src/map_builders/mod.rs @@ -11,6 +11,8 @@ pub trait MapBuilder { fn spawn_entities(&mut self, ecs: &mut World); fn get_map(&self) -> Map; fn get_starting_position(&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 6b372f5..f4bfbf1 100644 --- a/src/map_builders/simple_map.rs +++ b/src/map_builders/simple_map.rs @@ -1,5 +1,5 @@ use super::{apply_horizontal_tunnel, apply_room_to_map, apply_vertical_tunnel, MapBuilder}; -use crate::spawner; +use crate::{spawner, SHOW_MAPGEN_VISUALIZER}; use crate::{Map, Position, Rect, TileType}; use rltk::RandomNumberGenerator; use specs::prelude::*; @@ -8,7 +8,8 @@ pub struct SimpleMapBuilder { map: Map, starting_position: Position, depth: i32, - rooms: Vec + rooms: Vec, + history: Vec, } impl MapBuilder for SimpleMapBuilder { @@ -29,15 +30,30 @@ impl MapBuilder for SimpleMapBuilder { fn get_starting_position(&self) -> Position { self.starting_position.clone() } + + fn get_snapshot_history(&self) -> Vec { + self.history.clone() + } + + fn take_snapshot(&mut self) { + if SHOW_MAPGEN_VISUALIZER { + let mut snapshot = self.map.clone(); + for v in snapshot.revealed_tiles.iter_mut() { + *v = true; + } + self.history.push(snapshot); + } + } } impl SimpleMapBuilder { pub fn new(new_depth: i32) -> SimpleMapBuilder { - SimpleMapBuilder{ + SimpleMapBuilder { map: Map::new(new_depth), starting_position: Position { x: 0, y: 0 }, depth: new_depth, rooms: Vec::new(), + history: Vec::new(), } } @@ -80,6 +96,7 @@ impl SimpleMapBuilder { } self.rooms.push(new_room); + self.take_snapshot(); } }