diff --git a/src/components.rs b/src/components.rs index 3060ae3..b809b22 100644 --- a/src/components.rs +++ b/src/components.rs @@ -379,6 +379,11 @@ pub struct MagicItem { pub class: MagicItemClass, } +#[derive(Component, Debug, Serialize, Deserialize, Clone)] +pub struct ObfuscatedName { + pub name: String, +} + // Serialization helper code. We need to implement ConvertSaveLoad for each type that contains an // Entity. diff --git a/src/gui.rs b/src/gui.rs index 85f10d3..1196b82 100644 --- a/src/gui.rs +++ b/src/gui.rs @@ -9,7 +9,10 @@ use crate::components::{ }; use crate::game_log::GameLog; use crate::rex_assets::RexAssets; -use crate::{camera, colors, Equipped, Hidden, MagicItem, Map, RunState, State, VendorMode}; +use crate::{ + camera, colors, Equipped, Hidden, MagicItem, Map, MasterDungeonMap, ObfuscatedName, RunState, + State, VendorMode, +}; pub fn get_item_color(ecs: &World, item: Entity) -> RGB { if let Some(magic) = ecs.read_storage::().get(item) { @@ -23,6 +26,25 @@ pub fn get_item_color(ecs: &World, item: Entity) -> RGB { } } +pub fn get_item_display_name(ecs: &World, item: Entity) -> String { + if let Some(name) = ecs.read_storage::().get(item) { + if ecs.read_storage::().get(item).is_some() { + let dm = ecs.fetch::(); + if dm.identified_items.contains(&name.name) { + name.name.clone() + } else if let Some(obfuscated) = ecs.read_storage::().get(item) { + obfuscated.name.clone() + } else { + "Unidentified magic item".to_string() + } + } else { + name.name.clone() + } + } else { + "Nameless item (bug)".to_string() + } +} + pub fn draw_hollow_box( console: &mut Rltk, sx: i32, @@ -166,15 +188,14 @@ pub fn draw_ui(ecs: &World, ctx: &mut Rltk) { let mut y = 13; let entities = ecs.entities(); let equipped = ecs.read_storage::(); - let name = ecs.read_storage::(); - for (entity, equipped_by, item_name) in (&entities, &equipped, &name).join() { + for (entity, equipped_by) in (&entities, &equipped).join() { if equipped_by.owner == *player_entity { ctx.print_color( 50, y, get_item_color(ecs, entity), colors::BLACK, - &item_name.name, + &get_item_display_name(ecs, entity), ); y += 1; } @@ -185,9 +206,7 @@ pub fn draw_ui(ecs: &World, ctx: &mut Rltk) { let consumables = ecs.read_storage::(); let backpack = ecs.read_storage::(); let mut index = 1; - for (entity, carried_by, _consumable, item_name) in - (&entities, &backpack, &consumables, &name).join() - { + for (entity, carried_by, _consumable) in (&entities, &backpack, &consumables).join() { if carried_by.owner == *player_entity && index < 10 { ctx.print_color(50, y, colors::YELLOW, colors::BLACK, &format!("↑{}", index)); ctx.print_color( @@ -195,7 +214,7 @@ pub fn draw_ui(ecs: &World, ctx: &mut Rltk) { y, get_item_color(ecs, entity), colors::BLACK, - &item_name.name, + &get_item_display_name(ecs, entity), ); y += 1; index += 1; @@ -452,7 +471,7 @@ pub fn show_inventory(gs: &mut State, ctx: &mut Rltk) -> (ItemMenuResult, Option let mut equippable: Vec = Vec::new(); let mut j = 0; #[allow(clippy::explicit_counter_loop)] - for (entity, _pack, name) in (&entities, &backpack, &names) + for (entity, _pack) in (&entities, &backpack) .join() .filter(|item| item.1.owner == *player_entity) { @@ -471,7 +490,7 @@ pub fn show_inventory(gs: &mut State, ctx: &mut Rltk) -> (ItemMenuResult, Option y, get_item_color(&gs.ecs, entity), colors::BLACK, - &name.name.to_string(), + &get_item_display_name(&gs.ecs, entity), ); equippable.push(entity); y += 1; @@ -528,7 +547,7 @@ pub fn drop_item_menu(gs: &mut State, ctx: &mut Rltk) -> (ItemMenuResult, Option let mut equippable: Vec = Vec::new(); let mut j = 0; #[allow(clippy::explicit_counter_loop)] - for (entity, _pack, name) in (&entities, &backpack, &names) + for (entity, _pack) in (&entities, &backpack) .join() .filter(|item| item.1.owner == *player_entity) { @@ -547,7 +566,7 @@ pub fn drop_item_menu(gs: &mut State, ctx: &mut Rltk) -> (ItemMenuResult, Option y, get_item_color(&gs.ecs, entity), colors::BLACK, - &name.name.to_string(), + &get_item_display_name(&gs.ecs, entity), ); equippable.push(entity); y += 1; @@ -610,7 +629,7 @@ pub fn remove_item_menu(gs: &mut State, ctx: &mut Rltk) -> (ItemMenuResult, Opti let mut equippable: Vec = Vec::new(); let mut j = 0; #[allow(clippy::explicit_counter_loop)] - for (entity, _pack, name) in (&entities, &backpack, &names) + for (entity, _pack) in (&entities, &backpack) .join() .filter(|item| item.1.owner == *player_entity) { @@ -629,7 +648,7 @@ pub fn remove_item_menu(gs: &mut State, ctx: &mut Rltk) -> (ItemMenuResult, Opti y, get_item_color(&gs.ecs, entity), colors::BLACK, - &name.name.to_string(), + &get_item_display_name(&gs.ecs, entity), ); equippable.push(entity); diff --git a/src/main.rs b/src/main.rs index a14c86d..e94cba6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -91,6 +91,7 @@ fn main() -> ::rltk::BError { MyTurn, Name, NaturalAttackDefense, + ObfuscatedName, OtherLevelPosition, ParticleLifetime, Player, diff --git a/src/map/dungeon.rs b/src/map/dungeon.rs index 22f026f..5948802 100644 --- a/src/map/dungeon.rs +++ b/src/map/dungeon.rs @@ -1,4 +1,4 @@ -use std::collections::HashMap; +use std::collections::{HashMap, HashSet}; use ::rltk::{Point, RandomNumberGenerator}; use ::serde::{Deserialize, Serialize}; @@ -11,13 +11,26 @@ use crate::map_builders::level_builder; #[derive(Default, Serialize, Deserialize, Clone)] pub struct MasterDungeonMap { maps: HashMap, + pub identified_items: HashSet, + pub scroll_mappings: HashMap, } impl MasterDungeonMap { pub fn new() -> MasterDungeonMap { - MasterDungeonMap { + let mut dm = MasterDungeonMap { maps: HashMap::new(), + identified_items: HashSet::new(), + scroll_mappings: HashMap::new(), + }; + + let mut rng = RandomNumberGenerator::new(); + for scroll_tag in crate::raws::get_scroll_tags().iter() { + let masked_name = make_scroll_name(&mut rng); + dm.scroll_mappings + .insert(scroll_tag.to_string(), masked_name); } + + dm } pub fn store_map(&mut self, map: &Map) { @@ -33,6 +46,49 @@ impl MasterDungeonMap { } } +fn make_scroll_name(rng: &mut rltk::RandomNumberGenerator) -> String { + let length = 4 + rng.roll_dice(1, 4); + let mut name = "Scroll of ".to_string(); + + for i in 0..length { + if i % 2 == 0 { + name += match rng.roll_dice(1, 5) { + 1 => "a", + 2 => "e", + 3 => "i", + 4 => "o", + _ => "u", + } + } else { + name += match rng.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 +} + pub fn level_transition(ecs: &mut World, new_depth: i32, offset: i32) -> Option> { // Obtain the master dungeon map let dungeon_master = ecs.read_resource::(); diff --git a/src/raws/item_structs.rs b/src/raws/item_structs.rs index 9fe3056..eb08d10 100644 --- a/src/raws/item_structs.rs +++ b/src/raws/item_structs.rs @@ -46,4 +46,5 @@ pub struct Wearable { #[derive(Deserialize, Debug)] pub struct MagicItem { pub class: String, + pub naming: String, } diff --git a/src/raws/rawmaster.rs b/src/raws/rawmaster.rs index 1019f64..954b32f 100644 --- a/src/raws/rawmaster.rs +++ b/src/raws/rawmaster.rs @@ -7,8 +7,9 @@ use ::specs::saveload::{MarkedBuilder, SimpleMarker}; use crate::components::*; use crate::gamesystem::{mana_at_level, npc_hp}; +use crate::map::MasterDungeonMap; use crate::random_table::RandomTable; -use crate::raws::{Raws, Reaction}; +use crate::raws::{Raws, Reaction, RAWS}; pub fn parse_dice_string(dice: &str) -> (i32, i32, i32) { lazy_static! { @@ -180,6 +181,21 @@ pub fn get_vendor_items(categories: &[String], raws: &RawMaster) -> Vec<(String, result } +pub fn get_scroll_tags() -> Vec { + let raws = &RAWS.lock().unwrap(); + let mut result = Vec::new(); + + for item in raws.raws.items.iter() { + if let Some(magic) = &item.magic { + if &magic.naming == "scroll" { + result.push(item.name.clone()); + } + } + } + + result +} + fn spawn_position<'a>( pos: SpawnType, new_entity: EntityBuilder<'a>, @@ -235,7 +251,7 @@ pub fn spawn_named_item( ) -> Option { if raws.item_index.contains_key(key) { let item_template = &raws.raws.items[raws.item_index[key]]; - + let scroll_names = ecs.fetch::().scroll_mappings.clone(); let mut eb = ecs.create_entity().marked::>(); // Spawn in the specified location @@ -333,7 +349,17 @@ pub fn spawn_named_item( "legendary" => MagicItemClass::Legendary, _ => MagicItemClass::Common, }; - eb = eb.with(MagicItem { class }) + eb = eb.with(MagicItem { class }); + + #[allow(clippy::single_match)] + match magic.naming.as_str() { + "scroll" => { + eb = eb.with(ObfuscatedName { + name: scroll_names[&item_template.name].clone(), + }) + } + _ => {} + } } return Some(eb.build()); diff --git a/src/saveload_system.rs b/src/saveload_system.rs index 484a06b..0ec007a 100644 --- a/src/saveload_system.rs +++ b/src/saveload_system.rs @@ -93,6 +93,7 @@ pub fn save_game(ecs: &mut World) { MyTurn, Name, NaturalAttackDefense, + ObfuscatedName, OtherLevelPosition, ParticleLifetime, Player, @@ -207,6 +208,7 @@ pub fn load_game(ecs: &mut World) { MyTurn, Name, NaturalAttackDefense, + ObfuscatedName, OtherLevelPosition, ParticleLifetime, Player, diff --git a/src/spawner.rs b/src/spawner.rs index e7bf94e..59cd913 100644 --- a/src/spawner.rs +++ b/src/spawner.rs @@ -95,12 +95,6 @@ pub fn player(ecs: &mut World, player_x: i32, player_y: i32) -> Entity { "Old Boots", SpawnType::Equipped { by: player }, ); - spawn_named_entity( - &RAWS.lock().unwrap(), - ecs, - "Town Portal Scroll", - SpawnType::Carried { by: player }, - ); player }