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::{
ai::CARRY_CAPACITY_PER_STRENGTH, camera, gamelog, gamesystem, rex_assets::RexAssets, ArmourClassBonus, Attributes,
Burden, Equipped, Hidden, HungerClock, HungerState, InBackpack, MagicItem, Map, MasterDungeonMap, Name,
ObfuscatedName, Player, Point, Pools, Position, Prop, Renderable, RunState, Skill, Skills, State, Viewshed, Wand,
Burden, Equipped, Hidden, HungerClock, HungerState, InBackpack, MagicItem, MagicItemClass, Map, MasterDungeonMap,
Name, ObfuscatedName, Player, Point, Pools, Position, Prop, Renderable, RunState, Skill, Skills, State, Viewshed,
Wand,
};
use rltk::{Rltk, VirtualKeyCode, RGB};
use specs::prelude::*;
@ -144,12 +145,17 @@ pub fn draw_ui(ecs: &World, ctx: &mut Rltk) {
}
// Draw equipment
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();
for (entity, _equipped, renderable) in
(&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;
if !equipment.is_empty() {
@ -159,8 +165,9 @@ pub fn draw_ui(ecs: &World, ctx: &mut Rltk) {
y += 1;
ctx.set(72, y, RGB::named(rltk::YELLOW), RGB::named(rltk::BLACK), 97 + j as rltk::FontCharType);
j += 1;
ctx.print_color(74, y, item.1, RGB::named(rltk::BLACK), &item.0);
ctx.print_color(74 + &item.0.len() + 1, y, RGB::named(rltk::WHITE), RGB::named(rltk::BLACK), "(worn)");
ctx.set(74, y, item.2, RGB::named(rltk::BLACK), item.3);
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;
}
@ -190,7 +197,7 @@ pub fn draw_ui(ecs: &World, ctx: &mut Rltk) {
let props = ecs.read_storage::<Prop>();
let map = ecs.fetch::<Map>();
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() {
let idx = map.xy_idx(tile.x, tile.y);
for entity in map.tile_content[idx].iter() {
@ -210,12 +217,17 @@ pub fn draw_ui(ecs: &World, ctx: &mut Rltk) {
draw = false;
}
if draw {
let fg = if let Some(renderable) = renderables.get(*entity) {
renderable.fg
let (render_fg, glyph) = if let Some(renderable) = renderables.get(*entity) {
(renderable.fg, renderable.glyph)
} 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");
for entity in seen_entities {
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 mut width: i32 = -1;
for (item, item_count) in &inventory {
let fg = RGB::from_u8(item.rgb.0, item.rgb.1, item.rgb.2);
x = initial_x;
// Print the character required to access this item. i.e. (a)
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);
}
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;
let fg = RGB::from_u8(item.rgb.0, item.rgb.1, item.rgb.2);
if item_count > &1 {
// If more than one, print the number and pluralise
// i.e. (a) 3 daggers
ctx.print_color(x, y, fg, RGB::named(rltk::BLACK), item_count);
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 {
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");
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'
// i.e. (a) an apple
ctx.print_color(x, y, fg, RGB::named(rltk::BLACK), "an");
@ -335,9 +356,9 @@ pub fn print_options(
x += 2;
}
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;
width = if width > this_width { width } else { this_width };
}
let this_width = x - initial_x + item.display_name.singular.len() as i32;
width = if width > this_width { width } else { this_width };
y += 1;
j += 1;
@ -351,14 +372,14 @@ pub fn get_max_inventory_width(inventory: &BTreeMap<UniqueInventoryItem, i32>) -
for (item, count) in inventory {
let mut this_width = item.display_name.singular.len() as i32;
if count < &1 {
this_width += 4;
this_width += 6;
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)) {
this_width += 1;
this_width += 3;
}
} else {
this_width += 4;
this_width += 6;
}
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());
if let Some(name) = ecs.read_storage::<Name>().get(item) {
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) {
(singular, plural) = (name.name.clone(), name.plural.clone());
} 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);
}
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 {
let mut x = 3;
let mut y = 12;
@ -485,6 +529,8 @@ struct DisplayName {
pub struct UniqueInventoryItem {
display_name: DisplayName,
rgb: (u8, u8, u8),
renderables: (u8, u8, u8),
glyph: u16,
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 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.
let (r, g, b): (u8, u8, u8) = if let Some(renderable) = renderables.get(entity) {
((renderable.fg.r * 255.0) as u8, (renderable.fg.g * 255.0) as u8, (renderable.fg.b * 255.0) as u8)
} else {
(255, 255, 255)
};
let item_colour = get_item_colour(ecs, entity);
let renderables =
((renderable.fg.r * 255.0) as u8, (renderable.fg.g * 255.0) as u8, (renderable.fg.b * 255.0) as u8);
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);
player_inventory
.entry(UniqueInventoryItem {
display_name: DisplayName { singular: singular.clone(), plural: plural },
rgb: (r, g, b),
renderables: renderables,
glyph: renderable.glyph,
name: name.name.clone(),
})
.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 width = 3;
let mut width = 2;
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();
let this_width = 5 + this_name.len();
width = if width > this_width { width } else { this_width };
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 renderables = gs.ecs.read_storage::<Renderable>();
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.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;
j += 1;
}

View file

@ -9,7 +9,7 @@ use std::collections::{HashMap, HashSet};
pub struct MasterDungeonMap {
maps: HashMap<i32, Map>,
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 wand_map: HashMap<String, String>,
}
@ -27,8 +27,8 @@ impl MasterDungeonMap {
// 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));
let unid_singular = make_scroll_name(&mut rng);
dm.scroll_map.insert(scroll_tag.to_string(), unid_singular);
}
let mut used_potion_names: HashSet<String> = HashSet::new();
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) {
let len = 4 + rng.roll_dice(1, 4);
let mut singular = "scroll of ".to_string();
let mut plural = "scrolls of ".to_string();
fn make_scroll_name(rng: &mut RandomNumberGenerator) -> String {
let len = 4 + rng.roll_dice(1, 6);
let space_at_i = if len > 6 && rng.roll_dice(1, 2) == 1 { rng.roll_dice(1, len - 6) + 3 } else { -1 };
let offset = rng.roll_dice(1, 2) - 1;
let mut name = "".to_string();
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) {
1 => "A",
2 => "E",
@ -72,8 +76,7 @@ fn make_scroll_name(rng: &mut RandomNumberGenerator) -> (String, String) {
4 => "O",
_ => "U",
};
singular += char;
plural += char;
name += char;
} else {
let char = match rng.roll_dice(1, 21) {
1 => "B",
@ -98,11 +101,11 @@ fn make_scroll_name(rng: &mut RandomNumberGenerator) -> (String, String) {
20 => "Y",
_ => "Z",
};
singular += char;
plural += char;
name += char;
}
}
return (singular, plural);
name += " scroll";
return name;
}
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)]
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(),
})
let singular = scroll_names[&item_template.name.name].clone();
let mut plural = singular.clone();
plural += "s";
eb = eb.with(ObfuscatedName { name: singular, plural: plural })
}
"potion" => {
let singular = potion_names[&item_template.name.name].clone();