From 2dc2a4549af3745603dffa11118d5128d3f92b3c Mon Sep 17 00:00:00 2001 From: Timothy Warren Date: Wed, 19 Jan 2022 11:04:10 -0500 Subject: [PATCH] Allow identifying items by use or purchase --- src/components.rs | 5 +++++ src/gui.rs | 4 ++-- src/inventory_system.rs | 50 ++++++++++++++++++++++++++++++++++++++++- src/main.rs | 1 + src/raws/rawmaster.rs | 33 ++++++++++++++++++++------- src/saveload_system.rs | 2 ++ src/state.rs | 16 +++++++++++-- 7 files changed, 98 insertions(+), 13 deletions(-) diff --git a/src/components.rs b/src/components.rs index b809b22..d296b3e 100644 --- a/src/components.rs +++ b/src/components.rs @@ -384,6 +384,11 @@ pub struct ObfuscatedName { pub name: String, } +#[derive(Component, Debug, Serialize, Deserialize, Clone)] +pub struct IdentifiedItem { + 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 1196b82..aae6e00 100644 --- a/src/gui.rs +++ b/src/gui.rs @@ -761,8 +761,8 @@ pub fn main_menu(gs: &mut State, ctx: &mut Rltk) -> MainMenuResult { ctx.draw_box_double(24, 18, 31, 10, colors::WHEAT, colors::BLACK); - ctx.print_color_centered(20, colors::YELLOW, colors::BLACK, "Rust Roguelike Tutorial"); - ctx.print_color_centered(21, colors::CYAN, colors::BLACK, "by Herbert Wolverson"); + ctx.print_color_centered(20, colors::YELLOW, colors::BLACK, "Rust Roguelike"); + ctx.print_color_centered(21, colors::CYAN, colors::BLACK, "by Timothy J. Warren"); ctx.print_color_centered( 22, colors::GRAY, diff --git a/src/inventory_system.rs b/src/inventory_system.rs index dfc0783..3f10071 100644 --- a/src/inventory_system.rs +++ b/src/inventory_system.rs @@ -3,7 +3,7 @@ use ::specs::prelude::*; use crate::components::*; use crate::game_log::GameLog; use crate::particle_system::ParticleBuilder; -use crate::{colors, spatial, Map, RunState}; +use crate::{colors, raws, spatial, Map, MasterDungeonMap, RunState}; pub struct ItemCollectionSystem {} @@ -85,6 +85,7 @@ impl<'a> System<'a> for ItemUseSystem { WriteExpect<'a, RunState>, WriteStorage<'a, EquipmentChanged>, ReadStorage<'a, TownPortal>, + WriteStorage<'a, IdentifiedItem>, ); #[allow(clippy::cognitive_complexity)] @@ -114,6 +115,7 @@ impl<'a> System<'a> for ItemUseSystem { mut runstate, mut dirty, town_portal, + mut identified_item, ) = data; for (entity, useitem) in (&entities, &wants_use).join() { @@ -161,6 +163,18 @@ impl<'a> System<'a> for ItemUseSystem { } } + // Identify + if entity == *player_entity { + identified_item + .insert( + entity, + IdentifiedItem { + name: names.get(useitem.item).unwrap().name.clone(), + }, + ) + .expect("Unable to identify item"); + } + // If it is equippable, then we want to equip it - and unequip whatever else was in that slot match equippable.get(useitem.item) { None => {} @@ -468,3 +482,37 @@ impl<'a> System<'a> for ItemRemoveSystem { wants_remove.clear(); } } + +pub struct ItemIdentificationSystem {} + +impl<'a> System<'a> for ItemIdentificationSystem { + #[allow(clippy::type_complexity)] + type SystemData = ( + ReadStorage<'a, Player>, + WriteStorage<'a, IdentifiedItem>, + WriteExpect<'a, MasterDungeonMap>, + ReadStorage<'a, Item>, + ReadStorage<'a, Name>, + WriteStorage<'a, ObfuscatedName>, + Entities<'a>, + ); + + fn run(&mut self, data: Self::SystemData) { + let (player, mut identified, mut dm, items, names, mut obfuscated_names, entities) = data; + + for (_p, id) in (&player, &identified).join() { + if !dm.identified_items.contains(&id.name) && raws::is_tag_magic(&id.name) { + dm.identified_items.insert(id.name.clone()); + + for (entity, _item, name) in (&entities, &items, &names).join() { + if name.name == id.name { + obfuscated_names.remove(entity); + } + } + } + } + + // Clean up + identified.clear(); + } +} diff --git a/src/main.rs b/src/main.rs index e94cba6..4c2073a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -78,6 +78,7 @@ fn main() -> ::rltk::BError { Faction, Hidden, HungerClock, + IdentifiedItem, InBackpack, InflictsDamage, Initiative, diff --git a/src/raws/rawmaster.rs b/src/raws/rawmaster.rs index 954b32f..ce3201e 100644 --- a/src/raws/rawmaster.rs +++ b/src/raws/rawmaster.rs @@ -196,6 +196,16 @@ pub fn get_scroll_tags() -> Vec { result } +pub fn is_tag_magic(tag: &str) -> bool { + let raws = &RAWS.lock().unwrap(); + if raws.item_index.contains_key(tag) { + let item_template = &raws.raws.items[raws.item_index[tag]]; + item_template.magic.is_some() + } else { + false + } +} + fn spawn_position<'a>( pos: SpawnType, new_entity: EntityBuilder<'a>, @@ -251,7 +261,12 @@ 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 dm = ecs.fetch::(); + let scroll_names = dm.scroll_mappings.clone(); + let identified = dm.identified_items.clone(); + std::mem::drop(dm); + let mut eb = ecs.create_entity().marked::>(); // Spawn in the specified location @@ -351,14 +366,16 @@ pub fn spawn_named_item( }; 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(), - }) + if !identified.contains(&item_template.name) { + #[allow(clippy::single_match)] + match magic.naming.as_str() { + "scroll" => { + eb = eb.with(ObfuscatedName { + name: scroll_names[&item_template.name].clone(), + }) + } + _ => {} } - _ => {} } } diff --git a/src/saveload_system.rs b/src/saveload_system.rs index 0ec007a..7f728d1 100644 --- a/src/saveload_system.rs +++ b/src/saveload_system.rs @@ -80,6 +80,7 @@ pub fn save_game(ecs: &mut World) { Faction, Hidden, HungerClock, + IdentifiedItem, InBackpack, InflictsDamage, Initiative, @@ -195,6 +196,7 @@ pub fn load_game(ecs: &mut World) { Faction, Hidden, HungerClock, + IdentifiedItem, InBackpack, InflictsDamage, Initiative, diff --git a/src/state.rs b/src/state.rs index e79874a..2d49687 100644 --- a/src/state.rs +++ b/src/state.rs @@ -7,7 +7,7 @@ use crate::game_log::GameLog; use crate::gui::{self, show_cheat_mode, CheatMenuResult, MainMenuSelection}; use crate::hunger_system::HungerSystem; use crate::inventory_system::{ - ItemCollectionSystem, ItemDropSystem, ItemRemoveSystem, ItemUseSystem, + ItemCollectionSystem, ItemDropSystem, ItemIdentificationSystem, ItemRemoveSystem, ItemUseSystem, }; use crate::lighting_system::LightingSystem; use crate::map::{self, *}; @@ -127,6 +127,9 @@ impl State { let mut items = ItemUseSystem {}; items.run_now(&self.ecs); + let mut item_id = ItemIdentificationSystem {}; + item_id.run_now(&self.ecs); + let mut drop_items = ItemDropSystem {}; drop_items.run_now(&self.ecs); @@ -466,7 +469,16 @@ impl GameState for State { let tag = result.2.unwrap(); let price = result.3.unwrap(); let mut pools = self.ecs.write_storage::(); - let player_pools = pools.get_mut(*self.ecs.fetch::()).unwrap(); + let player_entity = self.ecs.fetch::(); + + let mut identified = self.ecs.write_storage::(); + identified + .insert(*player_entity, IdentifiedItem { name: tag.clone() }) + .expect("Unable to identify item"); + std::mem::drop(identified); + + let player_pools = pools.get_mut(*player_entity).unwrap(); + std::mem::drop(player_entity); if player_pools.gold >= price { player_pools.gold -= price; std::mem::drop(pools);