diff --git a/raws/items.json b/raws/items.json index c98bfe7..3eb7132 100644 --- a/raws/items.json +++ b/raws/items.json @@ -24,7 +24,8 @@ "weight": 0.5, "value": 50, "flags": ["CONSUMABLE", "DESTRUCTIBLE"], - "effects": { "ranged": "12", "damage": "10" } + "effects": { "ranged": "12", "damage": "10" }, + "magic": { "class": "common", "naming": "scroll" } }, { "id": "scroll_fireball", @@ -33,7 +34,8 @@ "weight": 0.5, "value": 150, "flags": ["CONSUMABLE", "DESTRUCTIBLE"], - "effects": { "ranged": "10", "damage": "15", "aoe": "3" } + "effects": { "ranged": "10", "damage": "15", "aoe": "3" }, + "magic": { "class": "rare", "naming": "scroll" } }, { "id": "scroll_fireball_c", @@ -42,7 +44,8 @@ "weight": 0.5, "value": 150, "flags": ["CONSUMABLE", "DESTRUCTIBLE", "CURSED"], - "effects": { "ranged": "10", "damage": "15", "aoe": "3" } + "effects": { "ranged": "10", "damage": "15", "aoe": "3" }, + "magic": { "class": "rare", "naming": "scroll" } }, { "id": "scroll_confusion", @@ -51,7 +54,8 @@ "weight": 0.5, "value": 100, "flags": ["CONSUMABLE", "DESTRUCTIBLE"], - "effects": { "ranged": "10", "confusion": "4" } + "effects": { "ranged": "10", "confusion": "4" }, + "magic": { "class": "uncommon", "naming": "scroll" } }, { "id": "scroll_magicmap", @@ -60,7 +64,8 @@ "weight": 0.5, "value": 50, "flags": ["CONSUMABLE", "DESTRUCTIBLE"], - "effects": { "magicmapper": "" } + "effects": { "magicmapper": "" }, + "magic": { "class": "common", "naming": "scroll" } }, { "id": "scroll_magicmap_c", @@ -69,7 +74,8 @@ "weight": 0.5, "value": 50, "flags": ["CONSUMABLE", "DESTRUCTIBLE", "CURSED"], - "effects": { "magicmapper": "" } + "effects": { "magicmapper": "" }, + "magic": { "class": "common", "naming": "scroll" } }, { "id": "equip_dagger", diff --git a/src/components.rs b/src/components.rs index 15b5866..c70a557 100644 --- a/src/components.rs +++ b/src/components.rs @@ -222,6 +222,12 @@ pub struct MagicItem { pub class: MagicItemClass, } +#[derive(Component, Debug, Serialize, Deserialize, Clone)] +pub struct ObfuscatedName { + pub name: String, + pub plural: String, +} + #[derive(Component, Debug, Serialize, Deserialize, Clone)] pub struct EquipmentChanged {} diff --git a/src/gui/mod.rs b/src/gui/mod.rs index c30d08e..ebf88aa 100644 --- a/src/gui/mod.rs +++ b/src/gui/mod.rs @@ -1,7 +1,7 @@ use super::{ ai::CARRY_CAPACITY_PER_STRENGTH, camera, gamelog, gamesystem, rex_assets::RexAssets, ArmourClassBonus, Attributes, - Burden, Equipped, Hidden, HungerClock, HungerState, InBackpack, Map, Name, Player, Point, Pools, Position, Prop, - Renderable, RunState, Skill, Skills, State, Viewshed, + Burden, Equipped, Hidden, HungerClock, HungerState, InBackpack, MagicItem, Map, Name, ObfuscatedName, Player, + Point, Pools, Position, Prop, Renderable, RunState, Skill, Skills, State, Viewshed, }; use rltk::{Rltk, VirtualKeyCode, RGB}; use specs::prelude::*; @@ -143,13 +143,13 @@ pub fn draw_ui(ecs: &World, ctx: &mut Rltk) { ctx.print_color(20, 20, RGB::named(rltk::YELLOW), RGB::named(rltk::BLACK), "--- GODMODE: ON ---"); } // Draw equipment - let names = ecs.read_storage::(); let renderables = ecs.read_storage::(); let mut equipment: Vec<(String, RGB)> = Vec::new(); - for (_equipped, name, renderable) in - (&equipped, &names, &renderables).join().filter(|item| item.0.owner == *player_entity) + let entities = ecs.entities(); + for (entity, _equipped, renderable) in + (&entities, &equipped, &renderables).join().filter(|item| item.1.owner == *player_entity) { - equipment.push((name.name.to_string(), renderable.fg)); + equipment.push((get_item_display_name(ecs, entity).0, renderable.fg)); } let mut y = 1; if !equipment.is_empty() { @@ -205,12 +205,13 @@ pub fn draw_ui(ecs: &World, ctx: &mut Rltk) { if entity == &*player_entity { draw = false; } - let name = &names.get(*entity); - if let Some(name) = name { - if draw { - let fg = renderables.get(*entity).unwrap().fg; - seen_entities.push((name.name.to_string(), fg)); - } + if draw { + let fg = if let Some(renderable) = renderables.get(*entity) { + renderable.fg + } else { + RGB::named(rltk::WHITE) + }; + seen_entities.push((get_item_display_name(ecs, *entity).0, fg)); } } } @@ -360,6 +361,25 @@ pub fn get_max_inventory_width(inventory: &BTreeMap<(String, String, (u8, u8, u8 return width; } +pub fn get_item_display_name(ecs: &World, item: Entity) -> (String, 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) { + return (name.name.clone(), name.plural.clone()); + } else if let Some(obfuscated) = ecs.read_storage::().get(item) { + return (obfuscated.name.clone(), obfuscated.plural.clone()); + } else { + return ("unid magic item".to_string(), "unid magic items".to_string()); + } + } else { + return (name.name.clone(), name.plural.clone()); + } + } else { + return ("nameless item (bug)".to_string(), "nameless items (bug)".to_string()); + } +} + pub fn show_help(ctx: &mut Rltk) -> YesNoResult { let mut x = 3; let mut y = 12; @@ -423,10 +443,8 @@ pub fn get_player_inventory(ecs: &World) -> (BTreeMap<(String, String, (u8, u8, } else { (255, 255, 255) }; - player_inventory - .entry((name.name.to_string(), name.plural.to_string(), (r, g, b))) - .and_modify(|count| *count += 1) - .or_insert(1); + let (singular, plural) = get_item_display_name(ecs, entity); + player_inventory.entry((singular, plural, (r, g, b))).and_modify(|count| *count += 1).or_insert(1); inventory_ids.entry(name.name.to_string()).or_insert(entity); } @@ -505,10 +523,9 @@ pub fn drop_item_menu(gs: &mut State, ctx: &mut Rltk) -> (ItemMenuResult, Option pub fn remove_item_menu(gs: &mut State, ctx: &mut Rltk) -> (ItemMenuResult, Option) { let player_entity = gs.ecs.fetch::(); - let names = gs.ecs.read_storage::(); let backpack = gs.ecs.read_storage::(); let entities = gs.ecs.entities(); - let inventory = (&backpack, &names).join().filter(|item| item.0.owner == *player_entity); + let inventory = (&backpack).join().filter(|item| item.owner == *player_entity); let count = inventory.count(); let (x_offset, y_offset) = (1, 10); @@ -523,8 +540,8 @@ pub fn remove_item_menu(gs: &mut State, ctx: &mut Rltk) -> (ItemMenuResult, Opti let mut equippable: Vec<(Entity, String)> = Vec::new(); let mut width = 3; - for (entity, _pack, name) in (&entities, &backpack, &names).join().filter(|item| item.1.owner == *player_entity) { - let this_name = &name.name; + for (entity, _pack) in (&entities, &backpack).join().filter(|item| item.1.owner == *player_entity) { + let this_name = &get_item_display_name(&gs.ecs, entity).0; let this_width = 3 + this_name.len(); width = if width > this_width { width } else { this_width }; equippable.push((entity, this_name.to_string())); diff --git a/src/main.rs b/src/main.rs index a8928a3..28f251c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -526,6 +526,7 @@ fn main() -> rltk::BError { gs.ecs.register::(); gs.ecs.register::(); gs.ecs.register::(); + gs.ecs.register::(); gs.ecs.register::(); gs.ecs.register::(); gs.ecs.register::(); diff --git a/src/map/dungeon.rs b/src/map/dungeon.rs index 8f2395c..c9292fb 100644 --- a/src/map/dungeon.rs +++ b/src/map/dungeon.rs @@ -3,17 +3,28 @@ use crate::{gamelog, map_builders, OtherLevelPosition, Position, Telepath, Views use rltk::prelude::*; use serde::{Deserialize, Serialize}; use specs::prelude::*; -use std::collections::HashMap; +use std::collections::{HashMap, HashSet}; #[derive(Default, Serialize, Deserialize, Clone)] pub struct MasterDungeonMap { maps: HashMap, + pub identified_items: HashSet, + pub scroll_map: HashMap, } impl MasterDungeonMap { /// Initialises a blank MasterDungeonMap pub fn new() -> MasterDungeonMap { - return MasterDungeonMap { maps: HashMap::new() }; + let mut dm = + MasterDungeonMap { maps: HashMap::new(), identified_items: HashSet::new(), scroll_map: HashMap::new() }; + // TODO: Use stored RNG + let mut rng = RandomNumberGenerator::new(); + for scroll_tag in crate::raws::get_scroll_tags().iter() { + let (unid_singular, unid_plural) = make_scroll_name(&mut rng); + dm.scroll_map.insert(scroll_tag.to_string(), (unid_singular, unid_plural)); + } + + return dm; } /// Stores the given map in the MasterDungeonMap pub fn store_map(&mut self, map: &Map) { @@ -31,6 +42,52 @@ impl MasterDungeonMap { } } +fn make_scroll_name(rng: &mut RandomNumberGenerator) -> (String, String) { + let len = 4 + rng.roll_dice(1, 4); + let mut singular = "scroll of ".to_string(); + let mut plural = "scrolls of ".to_string(); + for i in 0..len { + if i % 2 == 0 { + let char = match rng.roll_dice(1, 5) { + 1 => "a", + 2 => "e", + 3 => "i", + 4 => "o", + _ => "u", + }; + singular += char; + plural += char; + } else { + let char = 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", + }; + singular += char; + plural += char; + } + } + return (singular, plural); +} + pub fn level_transition(ecs: &mut World, new_id: i32, offset: i32) -> Option> { // Obtain master let dungeon_master = ecs.read_resource::(); diff --git a/src/raws/item_structs.rs b/src/raws/item_structs.rs index 42a48f7..f93fd89 100644 --- a/src/raws/item_structs.rs +++ b/src/raws/item_structs.rs @@ -10,6 +10,7 @@ pub struct Item { pub value: Option, pub flags: Option>, pub effects: Option>, + pub magic: Option, } #[derive(Deserialize, Debug)] @@ -25,3 +26,9 @@ pub struct Renderable { pub bg: String, pub order: i32, } + +#[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 b440449..7e6362f 100644 --- a/src/raws/rawmaster.rs +++ b/src/raws/rawmaster.rs @@ -111,6 +111,7 @@ pub fn spawn_named_entity( pub fn spawn_named_item(raws: &RawMaster, ecs: &mut World, key: &str, pos: SpawnType) -> Option { if raws.item_index.contains_key(key) { let item_template = &raws.raws.items[raws.item_index[key]]; + let scroll_names = ecs.fetch::().scroll_map.clone(); let mut eb = ecs.create_entity().marked::>(); eb = eb.with(Name { name: item_template.name.name.clone(), plural: item_template.name.plural.clone() }); @@ -146,7 +147,6 @@ pub fn spawn_named_item(raws: &RawMaster, ecs: &mut World, key: &str, pos: Spawn } } } - let mut base_damage = "1d4"; let mut hit_bonus = 0; @@ -168,6 +168,27 @@ pub fn spawn_named_item(raws: &RawMaster, ecs: &mut World, key: &str, pos: Spawn } } } + if let Some(magic_item) = &item_template.magic { + let item_class = match magic_item.class.as_str() { + "common" => MagicItemClass::Common, + "uncommon" => MagicItemClass::Uncommon, + "rare" => MagicItemClass::Rare, + "veryrare" => MagicItemClass::VeryRare, + _ => MagicItemClass::Legendary, + }; + eb = eb.with(MagicItem { class: item_class }); + + #[allow(clippy::single_match)] + match magic_item.naming.as_str() { + "scroll" => { + eb = eb.with(ObfuscatedName { + name: scroll_names[&item_template.name.name].0.clone(), + plural: scroll_names[&item_template.name.name].1.clone(), + }) + } + _ => {} + } + } if weapon_type != -1 { let (n_dice, die_type, bonus) = parse_dice_string(base_damage); @@ -621,3 +642,16 @@ pub fn get_mob_spawn_amount(rng: &mut RandomNumberGenerator, spawn_type: &Spawns _ => return roll, }; } + +pub fn get_scroll_tags() -> Vec { + let raws = &super::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.name.clone()); + } + } + } + return result; +} diff --git a/src/saveload_system.rs b/src/saveload_system.rs index b0c96f2..e452689 100644 --- a/src/saveload_system.rs +++ b/src/saveload_system.rs @@ -85,6 +85,7 @@ pub fn save_game(ecs: &mut World) { MultiAttack, NaturalAttacks, Name, + ObfuscatedName, OtherLevelPosition, ParticleLifetime, Player, @@ -198,6 +199,7 @@ pub fn load_game(ecs: &mut World) { MultiAttack, NaturalAttacks, Name, + ObfuscatedName, OtherLevelPosition, ParticleLifetime, Player, diff --git a/src/spawner.rs b/src/spawner.rs index fe8bf79..8bbf765 100644 --- a/src/spawner.rs +++ b/src/spawner.rs @@ -69,6 +69,13 @@ pub fn player(ecs: &mut World, player_x: i32, player_y: i32) -> Entity { raws::SpawnType::Equipped { by: player }, 0, ); + raws::spawn_named_entity( + &raws::RAWS.lock().unwrap(), + ecs, + "equip_dagger", + raws::SpawnType::Equipped { by: player }, + 0, + ); raws::spawn_named_entity( &raws::RAWS.lock().unwrap(), ecs,