visuals - scrolls with two-word names, and ui colours

This commit is contained in:
Llywelwyn 2023-08-14 23:11:23 +01:00
parent 662f6ec4aa
commit 67f348db4a
3 changed files with 109 additions and 50 deletions

View file

@ -1,7 +1,8 @@
use super::{ use super::{
ai::CARRY_CAPACITY_PER_STRENGTH, camera, gamelog, gamesystem, rex_assets::RexAssets, ArmourClassBonus, Attributes, ai::CARRY_CAPACITY_PER_STRENGTH, camera, gamelog, gamesystem, rex_assets::RexAssets, ArmourClassBonus, Attributes,
Burden, Equipped, Hidden, HungerClock, HungerState, InBackpack, MagicItem, Map, MasterDungeonMap, Name, Burden, Equipped, Hidden, HungerClock, HungerState, InBackpack, MagicItem, MagicItemClass, Map, MasterDungeonMap,
ObfuscatedName, Player, Point, Pools, Position, Prop, Renderable, RunState, Skill, Skills, State, Viewshed, Wand, Name, ObfuscatedName, Player, Point, Pools, Position, Prop, Renderable, RunState, Skill, Skills, State, Viewshed,
Wand,
}; };
use rltk::{Rltk, VirtualKeyCode, RGB}; use rltk::{Rltk, VirtualKeyCode, RGB};
use specs::prelude::*; use specs::prelude::*;
@ -144,12 +145,17 @@ pub fn draw_ui(ecs: &World, ctx: &mut Rltk) {
} }
// Draw equipment // Draw equipment
let renderables = ecs.read_storage::<Renderable>(); let renderables = ecs.read_storage::<Renderable>();
let mut equipment: Vec<(String, RGB)> = Vec::new(); let mut equipment: Vec<(String, RGB, RGB, rltk::FontCharType)> = Vec::new();
let entities = ecs.entities(); let entities = ecs.entities();
for (entity, _equipped, renderable) in for (entity, _equipped, renderable) in
(&entities, &equipped, &renderables).join().filter(|item| item.1.owner == *player_entity) (&entities, &equipped, &renderables).join().filter(|item| item.1.owner == *player_entity)
{ {
equipment.push((get_item_display_name(ecs, entity).0, renderable.fg)); equipment.push((
get_item_display_name(ecs, entity).0,
get_item_colour(ecs, entity),
renderable.fg,
renderable.glyph,
));
} }
let mut y = 1; let mut y = 1;
if !equipment.is_empty() { if !equipment.is_empty() {
@ -159,8 +165,9 @@ pub fn draw_ui(ecs: &World, ctx: &mut Rltk) {
y += 1; y += 1;
ctx.set(72, y, RGB::named(rltk::YELLOW), RGB::named(rltk::BLACK), 97 + j as rltk::FontCharType); ctx.set(72, y, RGB::named(rltk::YELLOW), RGB::named(rltk::BLACK), 97 + j as rltk::FontCharType);
j += 1; j += 1;
ctx.print_color(74, y, item.1, RGB::named(rltk::BLACK), &item.0); ctx.set(74, y, item.2, RGB::named(rltk::BLACK), item.3);
ctx.print_color(74 + &item.0.len() + 1, y, RGB::named(rltk::WHITE), RGB::named(rltk::BLACK), "(worn)"); ctx.print_color(76, y, item.1, RGB::named(rltk::BLACK), &item.0);
ctx.print_color(76 + &item.0.len() + 1, y, RGB::named(rltk::WHITE), RGB::named(rltk::BLACK), "(worn)");
} }
y += 2; y += 2;
} }
@ -190,7 +197,7 @@ pub fn draw_ui(ecs: &World, ctx: &mut Rltk) {
let props = ecs.read_storage::<Prop>(); let props = ecs.read_storage::<Prop>();
let map = ecs.fetch::<Map>(); let map = ecs.fetch::<Map>();
let viewshed = viewsheds.get(*player_entity).unwrap(); let viewshed = viewsheds.get(*player_entity).unwrap();
let mut seen_entities: Vec<(String, RGB)> = Vec::new(); let mut seen_entities: Vec<(String, RGB, RGB, u16)> = Vec::new();
for tile in viewshed.visible_tiles.iter() { for tile in viewshed.visible_tiles.iter() {
let idx = map.xy_idx(tile.x, tile.y); let idx = map.xy_idx(tile.x, tile.y);
for entity in map.tile_content[idx].iter() { for entity in map.tile_content[idx].iter() {
@ -210,12 +217,17 @@ pub fn draw_ui(ecs: &World, ctx: &mut Rltk) {
draw = false; draw = false;
} }
if draw { if draw {
let fg = if let Some(renderable) = renderables.get(*entity) { let (render_fg, glyph) = if let Some(renderable) = renderables.get(*entity) {
renderable.fg (renderable.fg, renderable.glyph)
} else { } else {
RGB::named(rltk::WHITE) (RGB::named(rltk::WHITE), rltk::to_cp437('-'))
}; };
seen_entities.push((get_item_display_name(ecs, *entity).0, fg)); seen_entities.push((
get_item_display_name(ecs, *entity).0,
get_item_colour(ecs, *entity),
render_fg,
glyph,
));
} }
} }
} }
@ -226,7 +238,8 @@ pub fn draw_ui(ecs: &World, ctx: &mut Rltk) {
ctx.print_color(72, y, RGB::named(rltk::BLACK), RGB::named(rltk::WHITE), "In View"); ctx.print_color(72, y, RGB::named(rltk::BLACK), RGB::named(rltk::WHITE), "In View");
for entity in seen_entities { for entity in seen_entities {
y += 1; y += 1;
ctx.print_color(72, y, entity.1, RGB::named(rltk::BLACK), entity.0); ctx.set(72, y, entity.2, RGB::named(rltk::BLACK), entity.3);
ctx.print_color(74, y, entity.1, RGB::named(rltk::BLACK), entity.0);
} }
} }
} }
@ -301,7 +314,6 @@ pub fn print_options(
let initial_x: i32 = x; let initial_x: i32 = x;
let mut width: i32 = -1; let mut width: i32 = -1;
for (item, item_count) in &inventory { for (item, item_count) in &inventory {
let fg = RGB::from_u8(item.rgb.0, item.rgb.1, item.rgb.2);
x = initial_x; x = initial_x;
// Print the character required to access this item. i.e. (a) // Print the character required to access this item. i.e. (a)
if j < 26 { if j < 26 {
@ -311,19 +323,28 @@ pub fn print_options(
ctx.set(x, y, RGB::named(rltk::YELLOW), RGB::named(rltk::BLACK), 65 - 26 + j as rltk::FontCharType); ctx.set(x, y, RGB::named(rltk::YELLOW), RGB::named(rltk::BLACK), 65 - 26 + j as rltk::FontCharType);
} }
x += 2;
let fg = RGB::from_u8(item.renderables.0, item.renderables.1, item.renderables.2);
ctx.set(x, y, fg, RGB::named(rltk::BLACK), item.glyph);
x += 2; x += 2;
let fg = RGB::from_u8(item.rgb.0, item.rgb.1, item.rgb.2);
if item_count > &1 { if item_count > &1 {
// If more than one, print the number and pluralise // If more than one, print the number and pluralise
// i.e. (a) 3 daggers // i.e. (a) 3 daggers
ctx.print_color(x, y, fg, RGB::named(rltk::BLACK), item_count); ctx.print_color(x, y, fg, RGB::named(rltk::BLACK), item_count);
x += 2; x += 2;
ctx.print_color(x, y, fg, RGB::named(rltk::BLACK), item.display_name.singular.to_string()); ctx.print_color(x, y, fg, RGB::named(rltk::BLACK), item.display_name.plural.to_string());
let this_width = x - initial_x + item.display_name.plural.len() as i32;
width = if width > this_width { width } else { this_width };
} else { } else {
if item.display_name.singular.ends_with("s") { if item.display_name.singular.to_lowercase().ends_with("s") {
ctx.print_color(x, y, fg, RGB::named(rltk::BLACK), "some"); ctx.print_color(x, y, fg, RGB::named(rltk::BLACK), "some");
x += 5; x += 5;
} else if ['a', 'e', 'i', 'o', 'u'].iter().any(|&v| item.display_name.singular.starts_with(v)) { } else if ['a', 'e', 'i', 'o', 'u']
.iter()
.any(|&v| item.display_name.singular.to_lowercase().starts_with(v))
{
// If one and starts with a vowel, print 'an' // If one and starts with a vowel, print 'an'
// i.e. (a) an apple // i.e. (a) an apple
ctx.print_color(x, y, fg, RGB::named(rltk::BLACK), "an"); ctx.print_color(x, y, fg, RGB::named(rltk::BLACK), "an");
@ -335,9 +356,9 @@ pub fn print_options(
x += 2; x += 2;
} }
ctx.print_color(x, y, fg, RGB::named(rltk::BLACK), item.display_name.singular.to_string()); ctx.print_color(x, y, fg, RGB::named(rltk::BLACK), item.display_name.singular.to_string());
}
let this_width = x - initial_x + item.display_name.singular.len() as i32; let this_width = x - initial_x + item.display_name.singular.len() as i32;
width = if width > this_width { width } else { this_width }; width = if width > this_width { width } else { this_width };
}
y += 1; y += 1;
j += 1; j += 1;
@ -351,14 +372,14 @@ pub fn get_max_inventory_width(inventory: &BTreeMap<UniqueInventoryItem, i32>) -
for (item, count) in inventory { for (item, count) in inventory {
let mut this_width = item.display_name.singular.len() as i32; let mut this_width = item.display_name.singular.len() as i32;
if count < &1 { if count < &1 {
this_width += 4; this_width += 6;
if item.display_name.singular.ends_with("s") { if item.display_name.singular.ends_with("s") {
this_width += 3; this_width += 5;
} else if ['a', 'e', 'i', 'o', 'u'].iter().any(|&v| item.display_name.singular.starts_with(v)) { } else if ['a', 'e', 'i', 'o', 'u'].iter().any(|&v| item.display_name.singular.starts_with(v)) {
this_width += 1; this_width += 3;
} }
} else { } else {
this_width += 4; this_width += 6;
} }
width = if width > this_width { width } else { this_width }; width = if width > this_width { width } else { this_width };
} }
@ -406,7 +427,7 @@ pub fn get_item_display_name(ecs: &World, item: Entity) -> (String, String) {
let (mut singular, mut plural) = ("nameless item (bug)".to_string(), "nameless items (bug)".to_string()); let (mut singular, mut plural) = ("nameless item (bug)".to_string(), "nameless items (bug)".to_string());
if let Some(name) = ecs.read_storage::<Name>().get(item) { if let Some(name) = ecs.read_storage::<Name>().get(item) {
if ecs.read_storage::<MagicItem>().get(item).is_some() { if ecs.read_storage::<MagicItem>().get(item).is_some() {
let dm = ecs.fetch::<crate::map::MasterDungeonMap>(); let dm = ecs.fetch::<MasterDungeonMap>();
if dm.identified_items.contains(&name.name) { if dm.identified_items.contains(&name.name) {
(singular, plural) = (name.name.clone(), name.plural.clone()); (singular, plural) = (name.name.clone(), name.plural.clone());
} else if let Some(obfuscated) = ecs.read_storage::<ObfuscatedName>().get(item) { } else if let Some(obfuscated) = ecs.read_storage::<ObfuscatedName>().get(item) {
@ -428,6 +449,29 @@ pub fn get_item_display_name(ecs: &World, item: Entity) -> (String, String) {
return (singular, plural); return (singular, plural);
} }
pub fn get_item_colour(ecs: &World, item: Entity) -> RGB {
let dm = ecs.fetch::<MasterDungeonMap>();
if let Some(name) = ecs.read_storage::<Name>().get(item) {
if let Some(magic) = ecs.read_storage::<MagicItem>().get(item) {
if dm.identified_items.contains(&name.name) {
// If identified magic item, use rarity colour
match magic.class {
MagicItemClass::Common => return RGB::named(rltk::WHITE),
MagicItemClass::Uncommon => return RGB::named(rltk::GREEN),
MagicItemClass::Rare => return RGB::named(rltk::BLUE),
MagicItemClass::VeryRare => return RGB::named(rltk::PURPLE),
MagicItemClass::Legendary => return RGB::named(rltk::GOLD),
}
} else {
// Unidentified magic item
return RGB::named(rltk::GREY);
}
}
}
// If nonmagic, just use white
return RGB::named(rltk::WHITE);
}
pub fn show_help(ctx: &mut Rltk) -> YesNoResult { pub fn show_help(ctx: &mut Rltk) -> YesNoResult {
let mut x = 3; let mut x = 3;
let mut y = 12; let mut y = 12;
@ -485,6 +529,8 @@ struct DisplayName {
pub struct UniqueInventoryItem { pub struct UniqueInventoryItem {
display_name: DisplayName, display_name: DisplayName,
rgb: (u8, u8, u8), rgb: (u8, u8, u8),
renderables: (u8, u8, u8),
glyph: u16,
name: String, name: String,
} }
@ -497,18 +543,22 @@ pub fn get_player_inventory(ecs: &World) -> (BTreeMap<UniqueInventoryItem, i32>,
let mut inventory_ids: BTreeMap<String, Entity> = BTreeMap::new(); let mut inventory_ids: BTreeMap<String, Entity> = BTreeMap::new();
let mut player_inventory: BTreeMap<UniqueInventoryItem, i32> = BTreeMap::new(); let mut player_inventory: BTreeMap<UniqueInventoryItem, i32> = BTreeMap::new();
for (entity, _pack, name) in (&entities, &backpack, &names).join().filter(|item| item.1.owner == *player_entity) { for (entity, _pack, name, renderable) in
(&entities, &backpack, &names, &renderables).join().filter(|item| item.1.owner == *player_entity)
{
// RGB can't be used as a key. This is converting the RGB (tuple of f32) into a tuple of u8s. // RGB can't be used as a key. This is converting the RGB (tuple of f32) into a tuple of u8s.
let (r, g, b): (u8, u8, u8) = if let Some(renderable) = renderables.get(entity) { let item_colour = get_item_colour(ecs, entity);
((renderable.fg.r * 255.0) as u8, (renderable.fg.g * 255.0) as u8, (renderable.fg.b * 255.0) as u8) let renderables =
} else { ((renderable.fg.r * 255.0) as u8, (renderable.fg.g * 255.0) as u8, (renderable.fg.b * 255.0) as u8);
(255, 255, 255) let (r, g, b): (u8, u8, u8) =
}; ((item_colour.r * 255.0) as u8, (item_colour.g * 255.0) as u8, (item_colour.b * 255.0) as u8);
let (singular, plural) = get_item_display_name(ecs, entity); let (singular, plural) = get_item_display_name(ecs, entity);
player_inventory player_inventory
.entry(UniqueInventoryItem { .entry(UniqueInventoryItem {
display_name: DisplayName { singular: singular.clone(), plural: plural }, display_name: DisplayName { singular: singular.clone(), plural: plural },
rgb: (r, g, b), rgb: (r, g, b),
renderables: renderables,
glyph: renderable.glyph,
name: name.name.clone(), name: name.name.clone(),
}) })
.and_modify(|count| *count += 1) .and_modify(|count| *count += 1)
@ -607,10 +657,10 @@ pub fn remove_item_menu(gs: &mut State, ctx: &mut Rltk) -> (ItemMenuResult, Opti
); );
let mut equippable: Vec<(Entity, String)> = Vec::new(); let mut equippable: Vec<(Entity, String)> = Vec::new();
let mut width = 3; let mut width = 2;
for (entity, _pack) in (&entities, &backpack).join().filter(|item| item.1.owner == *player_entity) { 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_name = &get_item_display_name(&gs.ecs, entity).0;
let this_width = 3 + this_name.len(); let this_width = 5 + this_name.len();
width = if width > this_width { width } else { this_width }; width = if width > this_width { width } else { this_width };
equippable.push((entity, this_name.to_string())); equippable.push((entity, this_name.to_string()));
} }
@ -624,9 +674,15 @@ pub fn remove_item_menu(gs: &mut State, ctx: &mut Rltk) -> (ItemMenuResult, Opti
let mut j = 0; let mut j = 0;
let renderables = gs.ecs.read_storage::<Renderable>(); let renderables = gs.ecs.read_storage::<Renderable>();
for (e, name) in &equippable { for (e, name) in &equippable {
let fg = if let Some(renderable) = renderables.get(*e) { renderable.fg } else { RGB::named(rltk::WHITE) }; let (mut fg, glyph) = if let Some(renderable) = renderables.get(*e) {
(renderable.fg, renderable.glyph)
} else {
(RGB::named(rltk::WHITE), rltk::to_cp437('-'))
};
ctx.set(x + 1, y, RGB::named(rltk::YELLOW), RGB::named(rltk::BLACK), 97 + j as rltk::FontCharType); ctx.set(x + 1, y, RGB::named(rltk::YELLOW), RGB::named(rltk::BLACK), 97 + j as rltk::FontCharType);
ctx.print_color(x + 3, y, fg, RGB::named(rltk::BLACK), name); ctx.set(x + 3, y, fg, RGB::named(rltk::BLACK), glyph);
fg = get_item_colour(&gs.ecs, *e);
ctx.print_color(x + 5, y, fg, RGB::named(rltk::BLACK), name);
y += 1; y += 1;
j += 1; j += 1;
} }

View file

@ -9,7 +9,7 @@ use std::collections::{HashMap, HashSet};
pub struct MasterDungeonMap { pub struct MasterDungeonMap {
maps: HashMap<i32, Map>, maps: HashMap<i32, Map>,
pub identified_items: HashSet<String>, pub identified_items: HashSet<String>,
pub scroll_map: HashMap<String, (String, String)>, pub scroll_map: HashMap<String, String>,
pub potion_map: HashMap<String, String>, pub potion_map: HashMap<String, String>,
pub wand_map: HashMap<String, String>, pub wand_map: HashMap<String, String>,
} }
@ -27,8 +27,8 @@ impl MasterDungeonMap {
// TODO: Use stored RNG // TODO: Use stored RNG
let mut rng = RandomNumberGenerator::new(); let mut rng = RandomNumberGenerator::new();
for scroll_tag in crate::raws::get_scroll_tags().iter() { for scroll_tag in crate::raws::get_scroll_tags().iter() {
let (unid_singular, unid_plural) = make_scroll_name(&mut rng); let unid_singular = make_scroll_name(&mut rng);
dm.scroll_map.insert(scroll_tag.to_string(), (unid_singular, unid_plural)); dm.scroll_map.insert(scroll_tag.to_string(), unid_singular);
} }
let mut used_potion_names: HashSet<String> = HashSet::new(); let mut used_potion_names: HashSet<String> = HashSet::new();
for potion_tag in crate::raws::get_potion_tags().iter() { for potion_tag in crate::raws::get_potion_tags().iter() {
@ -59,12 +59,16 @@ impl MasterDungeonMap {
} }
} }
fn make_scroll_name(rng: &mut RandomNumberGenerator) -> (String, String) { fn make_scroll_name(rng: &mut RandomNumberGenerator) -> String {
let len = 4 + rng.roll_dice(1, 4); let len = 4 + rng.roll_dice(1, 6);
let mut singular = "scroll of ".to_string(); let space_at_i = if len > 6 && rng.roll_dice(1, 2) == 1 { rng.roll_dice(1, len - 6) + 3 } else { -1 };
let mut plural = "scrolls of ".to_string(); let offset = rng.roll_dice(1, 2) - 1;
let mut name = "".to_string();
for i in 0..len { for i in 0..len {
if i % 2 == 0 { if i == space_at_i {
name += " ";
}
if i % 2 == 0 + offset {
let char = match rng.roll_dice(1, 5) { let char = match rng.roll_dice(1, 5) {
1 => "A", 1 => "A",
2 => "E", 2 => "E",
@ -72,8 +76,7 @@ fn make_scroll_name(rng: &mut RandomNumberGenerator) -> (String, String) {
4 => "O", 4 => "O",
_ => "U", _ => "U",
}; };
singular += char; name += char;
plural += char;
} else { } else {
let char = match rng.roll_dice(1, 21) { let char = match rng.roll_dice(1, 21) {
1 => "B", 1 => "B",
@ -98,11 +101,11 @@ fn make_scroll_name(rng: &mut RandomNumberGenerator) -> (String, String) {
20 => "Y", 20 => "Y",
_ => "Z", _ => "Z",
}; };
singular += char; name += char;
plural += char;
} }
} }
return (singular, plural); name += " scroll";
return name;
} }
const POTION_COLOURS: &[&str] = &[ const POTION_COLOURS: &[&str] = &[

View file

@ -193,10 +193,10 @@ pub fn spawn_named_item(raws: &RawMaster, ecs: &mut World, key: &str, pos: Spawn
#[allow(clippy::single_match)] #[allow(clippy::single_match)]
match magic_item.naming.as_str() { match magic_item.naming.as_str() {
"scroll" => { "scroll" => {
eb = eb.with(ObfuscatedName { let singular = scroll_names[&item_template.name.name].clone();
name: scroll_names[&item_template.name.name].0.clone(), let mut plural = singular.clone();
plural: scroll_names[&item_template.name.name].1.clone(), plural += "s";
}) eb = eb.with(ObfuscatedName { name: singular, plural: plural })
} }
"potion" => { "potion" => {
let singular = potion_names[&item_template.name.name].clone(); let singular = potion_names[&item_template.name.name].clone();