//! Functionality that is common to all the currently generated maps. use std::collections::{HashMap, HashSet}; use ::bracket_lib::prelude::*; use ::serde::{Deserialize, Serialize}; use ::specs::prelude::*; use crate::components::{OtherLevelPosition, Position, Viewshed}; use crate::map::{Map, TileType}; use crate::map_builders::level_builder; use crate::raws; use crate::rng::roll_dice; #[derive(Default, Serialize, Deserialize, Clone)] pub struct MasterDungeonMap { maps: HashMap, pub identified_items: HashSet, pub scroll_mappings: HashMap, pub potion_mappings: HashMap, } impl MasterDungeonMap { pub fn new() -> MasterDungeonMap { let mut dm = MasterDungeonMap { maps: HashMap::new(), identified_items: HashSet::new(), scroll_mappings: HashMap::new(), potion_mappings: HashMap::new(), }; for scroll_tag in raws::get_scroll_tags().iter() { let masked_name = make_scroll_name(); dm.scroll_mappings .insert(scroll_tag.to_string(), masked_name); } let mut used_potion_names = HashSet::new(); for potion_tag in raws::get_potion_tags().iter() { let masked_name = make_potion_name(&mut used_potion_names); dm.potion_mappings .insert(potion_tag.to_string(), masked_name); } dm } 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) { Some(self.maps[&depth].clone()) } else { None } } } fn make_scroll_name() -> String { let length = 4 + roll_dice(1, 4); let mut name = "Scroll of ".to_string(); for i in 0..length { if i % 2 == 0 { name += match roll_dice(1, 5) { 1 => "a", 2 => "e", 3 => "i", 4 => "o", _ => "u", } } else { name += match roll_dice(1, 21) { 1 => "b", 2 => "c", 3 => "d", 4 => "f", 5 => "g", 6 => "h", 7 => "j", 8 => "k", 9 => "l", 10 => "m", 11 => "n", 12 => "p", 13 => "q", 14 => "r", 15 => "s", 16 => "t", 17 => "v", 18 => "w", 19 => "x", 20 => "y", _ => "z", } } } name } const POTION_COLORS: &[&str] = &[ "Red", "Orange", "Yellow", "Green", "Brown", "Indigo", "Violet", ]; const POTION_ADJECTIVES: &[&str] = &[ "Swirling", "Effervescent", "Slimey", "Oiley", "Viscous", "Smelly", "Glowing", ]; fn make_potion_name(used_names: &mut HashSet) -> String { loop { let mut name = POTION_ADJECTIVES[roll_dice(1, POTION_ADJECTIVES.len() as i32) as usize - 1] .to_string(); name += " "; name += POTION_COLORS[roll_dice(1, POTION_COLORS.len() as i32) as usize - 1]; name += " Potion"; if !used_names.contains(&name) { used_names.insert(name.clone()); return name; } } } pub fn level_transition(ecs: &mut World, new_depth: i32, offset: 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, offset); 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 builder = level_builder(new_depth, 80, 50); builder.build_map(); 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 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, offset: 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; let stair_type = if offset < 0 { TileType::DownStairs } else { TileType::UpStairs }; for (idx, tt) in map.tiles.iter().enumerate() { if *tt == stair_type { 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; } } pub fn freeze_level_entities(ecs: &mut World) { // Obtain ECS access let entities = ecs.entities(); let mut positions = ecs.write_storage::(); let mut other_level_positions = ecs.write_storage::(); let player_entity = ecs.fetch::(); let map_depth = ecs.fetch::().depth; // Find positions and make OtherLevelPosition let mut pos_to_delete: Vec = Vec::new(); for (entity, pos) in (&entities, &positions).join() { if entity != *player_entity { other_level_positions .insert( entity, OtherLevelPosition { x: pos.x, y: pos.y, depth: map_depth, }, ) .expect("Failed to insert OtherLevelPosition"); pos_to_delete.push(entity); } } // Remove positions for p in pos_to_delete.iter() { positions.remove(*p); } } pub fn thaw_level_entities(ecs: &mut World) { // Obtain ECS access let entities = ecs.entities(); let mut positions = ecs.write_storage::(); let mut other_level_positions = ecs.write_storage::(); let player_entity = ecs.fetch::(); let map_depth = ecs.fetch::().depth; // Find OtherLevelPosition let mut pos_to_delete: Vec = Vec::new(); for (entity, pos) in (&entities, &other_level_positions).join() { if entity != *player_entity && pos.depth == map_depth { positions .insert(entity, Position { x: pos.x, y: pos.y }) .expect("Failed to insert Position"); pos_to_delete.push(entity); } } // Remove positions for p in pos_to_delete.iter() { other_level_positions.remove(*p); } }