diff --git a/src/main.rs b/src/main.rs index f534f13..26746ee 100644 --- a/src/main.rs +++ b/src/main.rs @@ -448,6 +448,9 @@ impl State { *player_entity_writer = player_entity; } + // Replace the world maps + self.ecs.insert(map::MasterDungeonMap::new()); + // Build a new map and place the player self.generate_world_map(1); } @@ -457,38 +460,8 @@ impl State { self.mapgen_timer = 0.0; self.mapgen_history.clear(); - let mut rng = self.ecs.write_resource::(); - let mut builder = map_builders::level_builder(new_depth, &mut rng, 80, 50); - builder.build_map(&mut rng); - - std::mem::drop(rng); - - self.mapgen_history = builder.get_snapshot_history(); - - let player_start; - { - let mut worldmap_resource = self.ecs.write_resource::(); - *worldmap_resource = builder.get_map(); - player_start = *builder.get_starting_position().as_mut().unwrap(); - } - - // Spawn bad guys - builder.spawn_entities(&mut self.ecs); - - // Place the player and update resources - let mut player_position = self.ecs.write_resource::(); - *player_position = player_start.into(); - let mut position_components = self.ecs.write_storage::(); - let player_entity = self.ecs.fetch::(); - if let Some(player_pos_comp) = position_components.get_mut(*player_entity) { - player_pos_comp.x = player_start.x; - player_pos_comp.y = player_start.y; - } - - // Mark the player's visibility as dirty - let mut viewshed_components = self.ecs.write_storage::(); - if let Some(vs) = viewshed_components.get_mut(*player_entity) { - vs.dirty = true; + if let Some(history) = map::level_transition(&mut self.ecs, new_depth) { + self.mapgen_history = history; } } } @@ -556,6 +529,7 @@ fn main() -> ::rltk::BError { raws::load_raws(); + gs.ecs.insert(map::MasterDungeonMap::new()); gs.ecs.insert(Map::new(1, 64, 64, "New Map")); gs.ecs.insert(Point::zero()); gs.ecs.insert(RandomNumberGenerator::new()); diff --git a/src/map.rs b/src/map.rs index fec0388..a73a45f 100644 --- a/src/map.rs +++ b/src/map.rs @@ -1,3 +1,4 @@ +mod dungeon; mod themes; mod tiletype; @@ -6,6 +7,7 @@ use std::collections::HashSet; use ::rltk::{Algorithm2D, BaseMap, Point, SmallVec}; use ::serde::{Deserialize, Serialize}; use ::specs::prelude::*; +pub use dungeon::*; pub use themes::*; pub use tiletype::{tile_opaque, tile_walkable, TileType}; diff --git a/src/map/dungeon.rs b/src/map/dungeon.rs new file mode 100644 index 0000000..d3ed45b --- /dev/null +++ b/src/map/dungeon.rs @@ -0,0 +1,132 @@ +use std::collections::HashMap; + +use ::rltk::{Point, RandomNumberGenerator}; +use ::serde::{Deserialize, Serialize}; +use ::specs::prelude::*; + +use crate::components::{Position, Viewshed}; +use crate::map::{Map, TileType}; +use crate::map_builders::level_builder; + +#[derive(Default, Serialize, Deserialize, Clone)] +pub struct MasterDungeonMap { + maps: HashMap, +} + +impl MasterDungeonMap { + pub fn new() -> MasterDungeonMap { + MasterDungeonMap { + maps: HashMap::new(), + } + } + + pub fn store_map(&mut self, map: &Map) { + self.maps.insert(map.depth, map.clone()); + } + + pub fn get_map(&self, depth: i32) -> Option { + if self.maps.contains_key(&depth) { + let mut result = self.maps[&depth].clone(); + result.tile_content = vec![Vec::new(); (result.width * result.height) as usize]; + + Some(result) + } else { + None + } + } +} + +pub fn level_transition(ecs: &mut World, new_depth: i32) -> Option> { + // Obtain the master dungeon map + let dungeon_master = ecs.read_resource::(); + + // Do we already have a map? + if dungeon_master.get_map(new_depth).is_some() { + std::mem::drop(dungeon_master); + transition_to_existing_map(ecs, new_depth); + + None + } else { + std::mem::drop(dungeon_master); + + Some(transition_to_new_map(ecs, new_depth)) + } +} + +fn transition_to_new_map(ecs: &mut World, new_depth: i32) -> Vec { + let mut rng = ecs.write_resource::(); + let mut builder = level_builder(new_depth, &mut rng, 80, 50); + builder.build_map(&mut rng); + + if new_depth > 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::UpStairs; + } + } + + 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(); + player_start = *builder.build_data.starting_position.as_mut().unwrap(); + } + + // Spawn bad guys + std::mem::drop(rng); + builder.spawn_entities(ecs); + + // Place the player and update resources + let (player_x, player_y) = (player_start.x, player_start.y); + let mut player_position = ecs.write_resource::(); + *player_position = player_start.into(); + + let mut position_components = ecs.write_storage::(); + let player_entity = ecs.fetch::(); + 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 visiblity as dirty + let mut viewshed_components = ecs.write_storage::(); + if let Some(vs) = viewshed_components.get_mut(*player_entity) { + vs.dirty = true; + } + + // Store the newly minted map + let mut dungeon_master = ecs.write_resource::(); + dungeon_master.store_map(&builder.build_data.map); + + mapgen_history +} + +fn transition_to_existing_map(ecs: &mut World, new_depth: i32) { + let dungeon_master = ecs.write_resource::(); + let map = dungeon_master.get_map(new_depth).unwrap(); + let mut worldmap_resource = ecs.write_resource::(); + let player_entity = ecs.fetch::(); + + // Find the down stairs and place the player + let w = map.width; + for (idx, tt) in map.tiles.iter().enumerate() { + if *tt == TileType::DownStairs { + 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::(); + if let Some(player_pos_comp) = position_components.get_mut(*player_entity) { + player_pos_comp.x = idx as i32 % w; + player_pos_comp.y = idx as i32 / w; + } + } + } + + *worldmap_resource = map; + + // Mark the player's visibility as dirty + let mut viewshed_components = ecs.write_storage::(); + if let Some(vs) = viewshed_components.get_mut(*player_entity) { + vs.dirty = true; + } +} diff --git a/src/map/themes.rs b/src/map/themes.rs index 3c31950..eee58c1 100644 --- a/src/map/themes.rs +++ b/src/map/themes.rs @@ -1,4 +1,4 @@ -use ::rltk::{FontCharType, RGB}; +use ::rltk::{to_cp437, FontCharType, RGB}; use super::{Map, TileType}; @@ -21,8 +21,6 @@ pub fn tile_glyph(idx: usize, map: &Map) -> (FontCharType, RGB, RGB) { } fn get_forest_glyph(idx: usize, map: &Map) -> (FontCharType, RGB, RGB) { - use rltk::to_cp437; - let bg = RGB::from_f32(0., 0., 0.); let (glyph, fg) = match map.tiles[idx] { @@ -34,6 +32,7 @@ fn get_forest_glyph(idx: usize, map: &Map) -> (FontCharType, RGB, RGB) { TileType::DeepWater => (to_cp437('~'), RGB::named(rltk::BLUE)), TileType::Gravel => (to_cp437(';'), RGB::from_f32(0.5, 0.5, 0.5)), TileType::DownStairs => (to_cp437('>'), RGB::from_f32(0., 1.0, 1.0)), + TileType::UpStairs => (to_cp437('<'), RGB::from_f32(0., 1.0, 1.0)), _ => (to_cp437('"'), RGB::from_f32(0.0, 0.6, 0.0)), }; @@ -44,20 +43,21 @@ fn get_tile_glyph_default(idx: usize, map: &Map) -> (rltk::FontCharType, RGB, RG let mut bg = RGB::from_f32(0., 0., 0.); let (glyph, mut fg) = match map.tiles[idx] { - TileType::Floor => (rltk::to_cp437('.'), RGB::from_f32(0., 0.5, 0.5)), - TileType::WoodFloor => (rltk::to_cp437('░'), RGB::named(rltk::CHOCOLATE)), + TileType::Floor => (to_cp437('.'), RGB::from_f32(0., 0.5, 0.5)), + TileType::WoodFloor => (to_cp437('░'), RGB::named(rltk::CHOCOLATE)), TileType::Wall => { let x = idx as i32 % map.width; let y = idx as i32 / map.width; (wall_glyph(&*map, x, y), RGB::from_f32(0., 1.0, 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)), - TileType::Gravel => (rltk::to_cp437(';'), RGB::named(rltk::GRAY)), + TileType::DownStairs => (to_cp437('>'), RGB::from_f32(0., 1.0, 1.0)), + TileType::Bridge => (to_cp437('.'), RGB::named(rltk::CHOCOLATE)), + TileType::Road => (to_cp437('≡'), RGB::named(rltk::GRAY)), + TileType::Grass => (to_cp437('"'), RGB::named(rltk::GREEN)), + TileType::ShallowWater => (to_cp437('~'), RGB::named(rltk::CYAN)), + TileType::DeepWater => (to_cp437('~'), RGB::named(rltk::NAVY_BLUE)), + TileType::Gravel => (to_cp437(';'), RGB::named(rltk::GRAY)), + TileType::UpStairs => (to_cp437('<'), RGB::from_f32(0., 1.0, 1.0)), }; if map.bloodstains.contains(&idx) { diff --git a/src/map/tiletype.rs b/src/map/tiletype.rs index 3d23a75..d12d640 100644 --- a/src/map/tiletype.rs +++ b/src/map/tiletype.rs @@ -12,6 +12,7 @@ pub enum TileType { WoodFloor, Bridge, Gravel, + UpStairs, } pub fn tile_walkable(tt: TileType) -> bool { @@ -25,6 +26,7 @@ pub fn tile_walkable(tt: TileType) -> bool { | TileType::WoodFloor | TileType::Bridge | TileType::Gravel + | TileType::UpStairs ) }