Implement obfuscated names for magic scrolls

This commit is contained in:
Timothy Warren 2022-01-19 10:15:51 -05:00
parent ec0f13dcdd
commit dd8a3b4f6f
8 changed files with 129 additions and 25 deletions

View File

@ -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.

View File

@ -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::<MagicItem>().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::<Name>().get(item) {
if ecs.read_storage::<MagicItem>().get(item).is_some() {
let dm = ecs.fetch::<MasterDungeonMap>();
if dm.identified_items.contains(&name.name) {
name.name.clone()
} else if let Some(obfuscated) = ecs.read_storage::<ObfuscatedName>().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::<Equipped>();
let name = ecs.read_storage::<Name>();
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::<Consumable>();
let backpack = ecs.read_storage::<InBackpack>();
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<Entity> = 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<Entity> = 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<Entity> = 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);

View File

@ -91,6 +91,7 @@ fn main() -> ::rltk::BError {
MyTurn,
Name,
NaturalAttackDefense,
ObfuscatedName,
OtherLevelPosition,
ParticleLifetime,
Player,

View File

@ -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<i32, Map>,
pub identified_items: HashSet<String>,
pub scroll_mappings: HashMap<String, String>,
}
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<Vec<Map>> {
// Obtain the master dungeon map
let dungeon_master = ecs.read_resource::<MasterDungeonMap>();

View File

@ -46,4 +46,5 @@ pub struct Wearable {
#[derive(Deserialize, Debug)]
pub struct MagicItem {
pub class: String,
pub naming: String,
}

View File

@ -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<String> {
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<Entity> {
if raws.item_index.contains_key(key) {
let item_template = &raws.raws.items[raws.item_index[key]];
let scroll_names = ecs.fetch::<MasterDungeonMap>().scroll_mappings.clone();
let mut eb = ecs.create_entity().marked::<SimpleMarker<SerializeMe>>();
// 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());

View File

@ -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,

View File

@ -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
}