From 25befa9343d0941b4cb341727c3a7c7dbd338311 Mon Sep 17 00:00:00 2001 From: Llywelwyn Date: Sun, 13 Aug 2023 08:53:33 +0100 Subject: [PATCH] Refactors worldmap gen, now uses a master list of maps for backtracking --- src/main.rs | 51 +++------------------ src/map/colours.rs | 2 +- src/map/dungeon.rs | 105 +++++++++++++++++++++++++++++++++++++++++++- src/map/glyphs.rs | 1 + src/map/mod.rs | 1 + src/map/themes.rs | 3 +- src/map/tiletype.rs | 4 +- 7 files changed, 119 insertions(+), 48 deletions(-) diff --git a/src/main.rs b/src/main.rs index 795fb4d..96cd1b1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -81,50 +81,9 @@ impl State { self.mapgen_index = 0; self.mapgen_timer = 0.0; self.mapgen_history.clear(); - let mut rng = self.ecs.write_resource::(); - let mut player_level = 1; - { - let player = self.ecs.read_storage::(); - let pools = self.ecs.read_storage::(); - for (_p, pool) in (&player, &pools).join() { - player_level = pool.level; - } - } - let mut builder = map_builders::level_builder(new_id, &mut rng, 100, 50, player_level); - builder.build_map(&mut rng); - std::mem::drop(rng); - self.mapgen_history = builder.build_data.history.clone(); - let player_start; - { - let mut worldmap_resource = self.ecs.write_resource::(); - *worldmap_resource = builder.build_data.map.clone(); - // Unwrap so we get a CTD if there's no starting pos. - player_start = builder.build_data.starting_position.as_mut().unwrap().clone(); - } - // Spawn entities - builder.spawn_entities(&mut self.ecs); - - // Place player and update resources - let mut player_position = self.ecs.write_resource::(); - *player_position = Point::new(player_start.x, player_start.y); - let mut position_components = self.ecs.write_storage::(); - let player_entity = self.ecs.fetch::(); - let player_pos_component = position_components.get_mut(*player_entity); - if let Some(player_pos_component) = player_pos_component { - player_pos_component.x = player_start.x; - player_pos_component.y = player_start.y; - } - - // Mark viewshed as dirty (force refresh) - let mut viewshed_components = self.ecs.write_storage::(); - let mut telepath_components = self.ecs.write_storage::(); - let vision_vs = viewshed_components.get_mut(*player_entity); - let telepath_vs = telepath_components.get_mut(*player_entity); - if let Some(vs) = vision_vs { - vs.dirty = true; - } - if let Some(vs) = telepath_vs { - vs.dirty = true; + let map_building_info = map::level_transition(&mut self.ecs, new_id); + if let Some(history) = map_building_info { + self.mapgen_history = history; } } @@ -266,6 +225,8 @@ impl State { let mut player_entity_writer = self.ecs.write_resource::(); *player_entity_writer = player_entity; } + // Replace map list + self.ecs.insert(map::dungeon::MasterDungeonMap::new()); self.generate_world_map(1); gamelog::setup_log(); @@ -615,7 +576,9 @@ fn main() -> rltk::BError { raws::load_raws(); + // Insert calls gs.ecs.insert(rltk::RandomNumberGenerator::new()); + gs.ecs.insert(map::MasterDungeonMap::new()); // Master map list gs.ecs.insert(Map::new(1, 64, 64, 0, "New Map")); // Map gs.ecs.insert(Point::new(0, 0)); // Player pos let player_entity = spawner::player(&mut gs.ecs, 0, 0); diff --git a/src/map/colours.rs b/src/map/colours.rs index 9b7c31b..3bd60a9 100644 --- a/src/map/colours.rs +++ b/src/map/colours.rs @@ -5,7 +5,7 @@ pub const BLOODSTAIN_COLOUR: (u8, u8, u8) = (153, 0, 0); pub const DEFAULT_BG_COLOUR: (u8, u8, u8) = (29, 50, 50); pub const WALL_COLOUR: (u8, u8, u8) = (229, 191, 94); pub const FLOOR_COLOUR: (u8, u8, u8) = (25, 204, 122); -pub const DOWN_STAIR_COLOUR: (u8, u8, u8) = (200, 200, 0); +pub const STAIR_COLOUR: (u8, u8, u8) = (200, 200, 0); pub const WOOD_FLOOR_COLOUR: (u8, u8, u8) = (41, 30, 20); pub const FENCE_FG_COLOUR: (u8, u8, u8) = (110, 24, 0); pub const FENCE_COLOUR: (u8, u8, u8) = (45, 30, 10); diff --git a/src/map/dungeon.rs b/src/map/dungeon.rs index df6611d..e46e1a8 100644 --- a/src/map/dungeon.rs +++ b/src/map/dungeon.rs @@ -1,5 +1,8 @@ -use super::Map; +use super::{Map, TileType}; +use crate::{gamelog, map_builders, Position, Telepath, Viewshed}; +use rltk::prelude::*; use serde::{Deserialize, Serialize}; +use specs::prelude::*; use std::collections::HashMap; #[derive(Default, Serialize, Deserialize, Clone)] @@ -27,3 +30,103 @@ impl MasterDungeonMap { } } } + +pub fn level_transition(ecs: &mut World, new_id: i32) -> Option> { + // Obtain master + let dungeon_master = ecs.read_resource::(); + if dungeon_master.get_map(new_id).is_some() { + std::mem::drop(dungeon_master); + transition_to_existing_map(ecs, new_id); + return None; + } else { + std::mem::drop(dungeon_master); + return Some(transition_to_new_map(ecs, new_id)); + } +} + +fn transition_to_existing_map(ecs: &mut World, new_id: i32) { + let dungeon_master = ecs.read_resource::(); + // Unwrapping here panics if new_id isn't present. But this should + // never be called without new_id being present by level_transition. + let map = dungeon_master.get_map(new_id).unwrap(); + let mut worldmap_resource = ecs.write_resource::(); + let player_entity = ecs.fetch::(); + // Find down stairs, place player + let w = map.width; + for (idx, tt) in map.tiles.iter().enumerate() { + if *tt == TileType::DownStair { + let mut player_position = ecs.write_resource::(); + *player_position = Point::new(idx as i32 % w, idx as i32 / w); + let mut position_components = ecs.write_storage::(); + let player_pos_component = position_components.get_mut(*player_entity); + if let Some(player_pos_component) = player_pos_component { + player_pos_component.x = idx as i32 % w; + player_pos_component.y = idx as i32 / w; + } + } + } + *worldmap_resource = map; + // Dirtify viewsheds (forces refresh) + let mut viewshed_components = ecs.write_storage::(); + let mut telepath_components = ecs.write_storage::(); + let vision_vs = viewshed_components.get_mut(*player_entity); + let telepath_vs = telepath_components.get_mut(*player_entity); + if let Some(vs) = vision_vs { + vs.dirty = true; + } + if let Some(vs) = telepath_vs { + vs.dirty = true; + } +} + +fn transition_to_new_map(ecs: &mut World, new_id: i32) -> Vec { + let mut rng = ecs.write_resource::(); + // Might need this to fallback to 1, but if player + // level isn't found at all, there's a bigger concern + // concern than just this function not working. + let player_level = gamelog::get_event_count("player_level"); + let mut builder = map_builders::level_builder(new_id, &mut rng, 100, 50, player_level); + builder.build_map(&mut rng); + std::mem::drop(rng); + if new_id > 1 { + if let Some(pos) = &builder.build_data.starting_position { + let up_idx = builder.build_data.map.xy_idx(pos.x, pos.y); + builder.build_data.map.tiles[up_idx] = TileType::UpStair; + } + } + let mapgen_history = builder.build_data.history.clone(); + let player_start; + { + let mut worldmap_resource = ecs.write_resource::(); + *worldmap_resource = builder.build_data.map.clone(); + // Unwrap so we get a CTD if there's no starting pos. + player_start = builder.build_data.starting_position.as_mut().unwrap().clone(); + } + // Spawn entities + builder.spawn_entities(ecs); + // Place player and update resources + let mut player_position = ecs.write_resource::(); + *player_position = Point::new(player_start.x, player_start.y); + let mut position_components = ecs.write_storage::(); + let player_entity = ecs.fetch::(); + let player_pos_component = position_components.get_mut(*player_entity); + if let Some(player_pos_component) = player_pos_component { + player_pos_component.x = player_start.x; + player_pos_component.y = player_start.y; + } + // Mark viewshed as dirty (force refresh) + let mut viewshed_components = ecs.write_storage::(); + let mut telepath_components = ecs.write_storage::(); + let vision_vs = viewshed_components.get_mut(*player_entity); + let telepath_vs = telepath_components.get_mut(*player_entity); + if let Some(vs) = vision_vs { + vs.dirty = true; + } + if let Some(vs) = telepath_vs { + vs.dirty = true; + } + // Store newly minted map + let mut dungeon_master = ecs.write_resource::(); + dungeon_master.store_map(&builder.build_data.map); + return mapgen_history; +} diff --git a/src/map/glyphs.rs b/src/map/glyphs.rs index be16fe7..ba9ae67 100644 --- a/src/map/glyphs.rs +++ b/src/map/glyphs.rs @@ -3,6 +3,7 @@ pub const WALL_GLYPH: char = '#'; pub const FLOOR_GLYPH: char = '.'; pub const DOWN_STAIR_GLYPH: char = '>'; +pub const UP_STAIR_GLYPH: char = '<'; pub const WOOD_FLOOR_GLYPH: char = '.'; pub const FENCE_GLYPH: char = '='; pub const BRIDGE_GLYPH: char = '.'; diff --git a/src/map/mod.rs b/src/map/mod.rs index 7142321..f4f66f2 100644 --- a/src/map/mod.rs +++ b/src/map/mod.rs @@ -9,6 +9,7 @@ pub use tiletype::{tile_cost, tile_opaque, tile_walkable, TileType}; mod interval_spawning_system; pub use interval_spawning_system::try_spawn_interval; pub mod dungeon; +pub use dungeon::{level_transition, MasterDungeonMap}; pub mod themes; // FIXME: If the map size gets too small, entities stop being rendered starting from the right. diff --git a/src/map/themes.rs b/src/map/themes.rs index fb294a1..e801ef1 100644 --- a/src/map/themes.rs +++ b/src/map/themes.rs @@ -36,7 +36,8 @@ pub fn get_default_theme_renderables(idx: usize, map: &Map) -> (rltk::FontCharTy TileType::WoodFloor => { glyph = rltk::to_cp437(WOOD_FLOOR_GLYPH); bg = RGB::named(WOOD_FLOOR_COLOUR); } TileType::Fence => { glyph = rltk::to_cp437(FENCE_GLYPH); fg = RGB::named(FENCE_FG_COLOUR); bg = RGB::named(FENCE_COLOUR); } TileType::Wall => { let x = idx as i32 % map.width; let y = idx as i32 / map.width; glyph = wall_glyph(&*map, x, y); fg = RGB::named(WALL_COLOUR); bg = RGB::named(DEFAULT_BG_COLOUR); } - TileType::DownStair => { glyph = rltk::to_cp437(DOWN_STAIR_GLYPH); fg = RGB::named(DOWN_STAIR_COLOUR); bg = RGB::named(DEFAULT_BG_COLOUR); } + TileType::DownStair => { glyph = rltk::to_cp437(DOWN_STAIR_GLYPH); fg = RGB::named(STAIR_COLOUR); bg = RGB::named(DEFAULT_BG_COLOUR); } + TileType::UpStair => { glyph = rltk::to_cp437(UP_STAIR_GLYPH); fg = RGB::named(STAIR_COLOUR); bg = RGB::named(DEFAULT_BG_COLOUR); } TileType::Bridge => { glyph = rltk::to_cp437(BRIDGE_GLYPH); bg = RGB::named(BRIDGE_COLOUR); } TileType::Gravel => { glyph = rltk::to_cp437(GRAVEL_GLYPH); bg = RGB::named(GRAVEL_COLOUR); } TileType::Road => { glyph = rltk::to_cp437(ROAD_GLYPH); bg = RGB::named(ROAD_COLOUR); } diff --git a/src/map/tiletype.rs b/src/map/tiletype.rs index 978a9be..8d9c47c 100644 --- a/src/map/tiletype.rs +++ b/src/map/tiletype.rs @@ -21,6 +21,7 @@ pub enum TileType { Bridge, // Stairs (changes floor) DownStair, + UpStair, } pub fn tile_walkable(tt: TileType) -> bool { @@ -35,7 +36,8 @@ pub fn tile_walkable(tt: TileType) -> bool { | TileType::Sand | TileType::ShallowWater | TileType::Bridge - | TileType::DownStair => true, + | TileType::DownStair + | TileType::UpStair => true, _ => false, } }