itemtypes, and filtering items by itemtype/buc/etc.

This commit is contained in:
Llywelwyn 2023-10-05 03:54:31 +01:00
parent f3af75bf44
commit 9c1298df6b
10 changed files with 386 additions and 227 deletions

View file

@ -3,6 +3,7 @@
"id": "potion_health",
"name": { "name": "potion of health", "plural": "potions of health" },
"renderable": { "glyph": "!", "fg": "#FF00FF", "bg": "#000000", "order": 4 },
"class": "potion",
"weight": 1,
"value": 50,
"flags": ["CONSUMABLE", "DESTRUCTIBLE", "STACKABLE"],
@ -13,6 +14,7 @@
"id": "potion_health_weak",
"name": { "name": "potion of lesser health", "plural": "potions of lesser health" },
"renderable": { "glyph": "!", "fg": "#FF00FF", "bg": "#000000", "order": 4 },
"class": "potion",
"weight": 1,
"value": 25,
"flags": ["CONSUMABLE", "DESTRUCTIBLE", "STACKABLE"],
@ -23,6 +25,7 @@
"id": "scroll_identify",
"name": { "name": "scroll of identify", "plural": "scrolls of identify" },
"renderable": { "glyph": "?", "sprite": { "id": "scroll", "colour": false }, "fg": "#0FFFFF", "bg": "#000000", "order": 4 },
"class": "scroll",
"weight": 0.5,
"value": 100,
"flags": ["CONSUMABLE", "DESTRUCTIBLE", "IDENTIFY"],
@ -32,6 +35,7 @@
"id": "scroll_removecurse",
"name": { "name": "scroll of remove curse", "plural": "scrolls of remove curse" },
"renderable": { "glyph": "?", "sprite": { "id": "scroll", "colour": false }, "fg": "#0FFFFF", "bg": "#000000", "order": 4 },
"class": "scroll",
"weight": 0.5,
"value": 200,
"flags": ["CONSUMABLE", "DESTRUCTIBLE", "REMOVE_CURSE"],
@ -41,6 +45,7 @@
"id": "scroll_health",
"name": { "name": "scroll of healing word", "plural": "scrolls of healing word" },
"renderable": { "glyph": "?", "sprite": { "id": "scroll", "colour": false }, "fg": "#00FFFF", "bg": "#000000", "order": 4 },
"class": "scroll",
"weight": 0.5,
"value": 50,
"flags": ["CONSUMABLE", "DESTRUCTIBLE"],
@ -51,6 +56,7 @@
"id": "scroll_mass_health",
"name": { "name": "scroll of mass healing word", "plural": "scrolls of mass healing word" },
"renderable": { "glyph": "?", "sprite": { "id": "scroll", "colour": false }, "fg": "#00FFFF", "bg": "#000000", "order": 4 },
"class": "scroll",
"weight": 0.5,
"value": 200,
"flags": ["CONSUMABLE", "DESTRUCTIBLE"],
@ -61,6 +67,7 @@
"id": "scroll_magicmissile",
"name": { "name": "scroll of magic missile", "plural": "scrolls of magic missile" },
"renderable": { "glyph": "?", "sprite": { "id": "scroll", "colour": false }, "fg": "#00FFFF", "bg": "#000000", "order": 4 },
"class": "scroll",
"weight": 0.5,
"value": 50,
"flags": ["CONSUMABLE", "DESTRUCTIBLE"],
@ -71,6 +78,7 @@
"id": "scroll_embers",
"name": { "name": "scroll of embers", "plural": "scrolls of embers" },
"renderable": { "glyph": "?", "sprite": { "id": "scroll", "colour": false }, "fg": "#00FFFF", "bg": "#000000", "order": 4 },
"class": "scroll",
"weight": 0.5,
"value": 100,
"flags": ["CONSUMABLE", "DESTRUCTIBLE"],
@ -81,6 +89,7 @@
"id": "scroll_fireball",
"name": { "name": "scroll of fireball", "plural": "scrolls of fireball" },
"renderable": { "glyph": "?", "sprite": { "id": "scroll", "colour": false }, "fg": "#00FFFF", "bg": "#000000", "order": 4 },
"class": "scroll",
"weight": 0.5,
"value": 200,
"flags": ["CONSUMABLE", "DESTRUCTIBLE"],
@ -96,6 +105,7 @@
"id": "scroll_confusion",
"name": { "name": "scroll of confusion", "plural": "scrolls of confusion" },
"renderable": { "glyph": "?", "sprite": { "id": "scroll", "colour": false }, "fg": "#00FFFF", "bg": "#000000", "order": 4 },
"class": "scroll",
"weight": 0.5,
"value": 100,
"flags": ["CONSUMABLE", "DESTRUCTIBLE"],
@ -106,6 +116,7 @@
"id": "scroll_mass_confusion",
"name": { "name": "scroll of mass confusion", "plural": "scrolls of mass confusion" },
"renderable": { "glyph": "?", "sprite": { "id": "scroll", "colour": false }, "fg": "#00FFFF", "bg": "#000000", "order": 4 },
"class": "scroll",
"weight": 0.5,
"value": 200,
"flags": ["CONSUMABLE", "DESTRUCTIBLE"],
@ -116,6 +127,7 @@
"id": "scroll_magicmap",
"name": { "name": "scroll of magic mapping", "plural": "scrolls of magic mapping" },
"renderable": { "glyph": "?", "sprite": { "id": "scroll", "colour": false }, "fg": "#00FFFF", "bg": "#000000", "order": 4 },
"class": "scroll",
"weight": 0.5,
"value": 50,
"flags": ["CONSUMABLE", "DESTRUCTIBLE", "MAGICMAP"],
@ -126,6 +138,7 @@
"id": "equip_dagger",
"name": { "name": "dagger", "plural": "daggers" },
"renderable": { "glyph": ")", "fg": "#808080", "bg": "#000000", "order": 4 },
"class": "weapon",
"weight": 1,
"value": 2,
"flags": ["EQUIP_MELEE"],
@ -135,6 +148,7 @@
"id": "equip_shortsword",
"name": { "name": "shortsword", "plural": "shortswords" },
"renderable": { "glyph": ")", "fg": "#C0C0C0", "bg": "#000000", "order": 4 },
"class": "weapon",
"weight": 2,
"value": 10,
"flags": ["EQUIP_MELEE"],
@ -144,6 +158,7 @@
"id": "equip_rapier",
"name": { "name": "rapier", "plural": "rapiers" },
"renderable": { "glyph": ")", "fg": "#C0C0C0", "bg": "#000000", "order": 4 },
"class": "weapon",
"weight": 2,
"value": 10,
"flags": ["EQUIP_MELEE"],
@ -153,6 +168,7 @@
"id": "equip_pitchfork",
"name": { "name": "pitchfork", "plural": "pitchforks" },
"renderable": { "glyph": ")", "fg": "#C0C0C0", "bg": "#000000", "order": 4 },
"class": "weapon",
"weight": 2,
"value": 5,
"flags": ["EQUIP_MELEE"],
@ -162,6 +178,7 @@
"id": "equip_sickle",
"name": { "name": "sickle", "plural": "sickles" },
"renderable": { "glyph": ")", "fg": "#C0C0C0", "bg": "#000000", "order": 4 },
"class": "weapon",
"weight": 2,
"value": 5,
"flags": ["EQUIP_MELEE"],
@ -171,6 +188,7 @@
"id": "equip_handaxe",
"name": { "name": "handaxe", "plural": "handaxes" },
"renderable": { "glyph": ")", "fg": "#C0C0C0", "bg": "#000000", "order": 4 },
"class": "weapon",
"weight": 2,
"value": 5,
"flags": ["EQUIP_MELEE"],
@ -180,6 +198,7 @@
"id": "equip_longsword",
"name": { "name": "longsword", "plural": "longswords" },
"renderable": { "glyph": ")", "fg": "#FFF8DC", "bg": "#000000", "order": 4 },
"class": "weapon",
"weight": 3,
"value": 15,
"flags": ["EQUIP_MELEE"],
@ -189,6 +208,7 @@
"id": "artifact_icingdeath",
"name": { "name": "Icingdeath", "plural": "Icingdeath" },
"renderable": { "glyph": ")", "fg": "#37aecc", "bg": "#000000", "order": 4 },
"class": "weapon",
"weight": 3,
"value": 300,
"flags": ["EQUIP_MELEE"],
@ -198,6 +218,7 @@
"id": "equip_smallshield",
"name": { "name": "buckler", "plural": "bucklers" },
"renderable": { "glyph": "[", "fg": "#808080", "bg": "#000000", "order": 4 },
"class": "armour",
"weight": 2,
"value": 5,
"flags": ["EQUIP_SHIELD"],
@ -207,6 +228,7 @@
"id": "equip_mediumshield",
"name": { "name": "medium shield", "plural": "medium shields" },
"renderable": { "glyph": "[", "fg": "#C0C0C0", "bg": "#000000", "order": 4 },
"class": "armour",
"weight": 6,
"value": 10,
"flags": ["EQUIP_SHIELD"],
@ -216,6 +238,7 @@
"id": "equip_largeshield",
"name": { "name": "large shield", "plural": "large shields" },
"renderable": { "glyph": "[", "fg": "#FFF8DC", "bg": "#000000", "order": 4 },
"class": "armour",
"weight": 12,
"value": 35,
"flags": ["EQUIP_SHIELD"],
@ -225,6 +248,7 @@
"id": "equip_body_weakleather",
"name": { "name": "leather jacket", "plural": "leather jackets" },
"renderable": { "glyph": "[", "fg": "#aa6000", "bg": "#000000", "order": 4 },
"class": "armour",
"weight": 8,
"value": 5,
"flags": ["EQUIP_BODY"],
@ -234,6 +258,7 @@
"id": "equip_body_leather",
"name": { "name": "leather chestpiece", "plural": "leather chestpiece" },
"renderable": { "glyph": "[", "fg": "#aa6000", "bg": "#000000", "order": 4 },
"class": "armour",
"weight": 10,
"value": 10,
"flags": ["EQUIP_BODY"],
@ -243,6 +268,7 @@
"id": "equip_body_studdedleather",
"name": { "name": "studded leather chestpiece", "plural": "studded leather chestpieces" },
"renderable": { "glyph": "[", "fg": "#aa6000", "bg": "#000000", "order": 4 },
"class": "armour",
"weight": 13,
"value": 45,
"flags": ["EQUIP_BODY"],
@ -252,6 +278,7 @@
"id": "equip_body_ringmail_o",
"name": { "name": "orcish ring mail", "plural": "orcish ring mail" },
"renderable": { "glyph": "[", "fg": "#aa6000", "bg": "#000000", "order": 4 },
"class": "armour",
"weight": 45,
"value": 50,
"flags": ["EQUIP_BODY"],
@ -261,6 +288,7 @@
"id": "equip_body_ringmail",
"name": { "name": "ring mail", "plural": "ring mail" },
"renderable": { "glyph": "[", "fg": "#aa6000", "bg": "#000000", "order": 4 },
"class": "armour",
"weight": 45,
"value": 70,
"flags": ["EQUIP_BODY"],
@ -270,6 +298,7 @@
"id": "equip_head_leather",
"name": { "name": "leather cap", "plural": "leather caps" },
"renderable": { "glyph": "[", "fg": "#aa6000", "bg": "#000000", "order": 4 },
"class": "armour",
"weight": 2,
"value": 10,
"flags": ["EQUIP_HEAD"],
@ -279,6 +308,7 @@
"id": "equip_head_elvish",
"name": { "name": "elvish leather helm", "plural": "elvish leather helms" },
"renderable": { "glyph": "[", "fg": "#aa6000", "bg": "#000000", "order": 4 },
"class": "armour",
"weight": 2,
"value": 25,
"flags": ["EQUIP_HEAD"],
@ -288,6 +318,7 @@
"id": "equip_head_o",
"name": { "name": "orcish helm", "plural": "orcish helm" },
"renderable": { "glyph": "[", "fg": "#aa6000", "bg": "#000000", "order": 4 },
"class": "armour",
"weight": 6,
"value": 25,
"flags": ["EQUIP_HEAD"],
@ -297,6 +328,7 @@
"id": "equip_head_iron",
"name": { "name": "iron helm", "plural": "iron helm" },
"renderable": { "glyph": "[", "fg": "#aa6000", "bg": "#000000", "order": 4 },
"class": "armour",
"weight": 10,
"value": 45,
"flags": ["EQUIP_HEAD"],
@ -306,6 +338,7 @@
"id": "equip_feet_leather",
"name": { "name": "leather shoes", "plural": "leather shoes" },
"renderable": { "glyph": "[", "fg": "#aa6000", "bg": "#000000", "order": 4 },
"class": "armour",
"weight": 2,
"value": 10,
"flags": ["EQUIP_FEET"]
@ -314,6 +347,7 @@
"id": "equip_feet_elvish",
"name": { "name": "elvish leather shoes", "plural": "elvish leather shoes" },
"renderable": { "glyph": "[", "fg": "#aa6000", "bg": "#000000", "order": 4 },
"class": "armour",
"weight": 2,
"value": 25,
"flags": ["EQUIP_FEET"],
@ -323,6 +357,7 @@
"id": "equip_feet_o",
"name": { "name": "orcish boots", "plural": "orcish boots" },
"renderable": { "glyph": "[", "fg": "#aa6000", "bg": "#000000", "order": 4 },
"class": "armour",
"weight": 6,
"value": 25,
"flags": ["EQUIP_FEET"],
@ -332,6 +367,7 @@
"id": "equip_feet_iron",
"name": { "name": "iron boots", "plural": "iron boots" },
"renderable": { "glyph": "[", "fg": "#aa6000", "bg": "#000000", "order": 4 },
"class": "armour",
"weight": 10,
"value": 45,
"flags": ["EQUIP_FEET"],
@ -341,6 +377,7 @@
"id": "equip_neck_protection",
"name": { "name": "amulet of protection", "plural": "amulets of protection" },
"renderable": { "glyph": "\"", "fg": "#aa6000", "bg": "#000000", "order": 4 },
"class": "amulet",
"weight": 1,
"value": 200,
"flags": ["EQUIP_NECK"],
@ -350,6 +387,7 @@
"id": "equip_back_protection",
"name": { "name": "cloak of protection", "plural": "cloaks of protection" },
"renderable": { "glyph": "[", "fg": "#aa6000", "bg": "#000000", "order": 4 },
"class": "armour",
"weight": 1,
"value": 200,
"flags": ["EQUIP_BACK"],
@ -359,6 +397,7 @@
"id": "wand_magicmissile",
"name": { "name": "wand of magic missile", "plural": "wands of magic missile" },
"renderable": { "glyph": "/", "fg": "#00FFFF", "bg": "#000000", "order": 4 },
"class": "wand",
"weight": 2,
"value": 100,
"flags": ["CHARGES"],
@ -369,6 +408,7 @@
"id": "wand_fireball",
"name": { "name": "wand of fireball", "plural": "wands of fireball" },
"renderable": { "glyph": "/", "fg": "#00FFFF", "bg": "#000000", "order": 4 },
"class": "wand",
"weight": 2,
"value": 300,
"flags": ["CHARGES"],
@ -379,6 +419,7 @@
"id": "wand_confusion",
"name": { "name": "wand of confusion", "plural": "wands of confusion" },
"renderable": { "glyph": "/", "fg": "#00FFFF", "bg": "#000000", "order": 4 },
"class": "wand",
"weight": 2,
"value": 200,
"flags": ["CHARGES"],
@ -389,6 +430,7 @@
"id": "wand_digging",
"name": { "name": "wand of digging", "plural": "wands of digging" },
"renderable": { "glyph": "/", "fg": "#00FFFF", "bg": "#000000", "order": 4 },
"class": "wand",
"weight": 2,
"value": 300,
"flags": ["CHARGES", "DIGGER"],
@ -399,6 +441,7 @@
"id": "food_rations",
"name": { "name": "rations", "plural": "rations" },
"renderable": { "glyph": "%", "fg": "#FFA07A", "bg": "#000000", "order": 4 },
"class": "comestible",
"weight": 1,
"value": 1,
"flags": ["FOOD", "CONSUMABLE", "STACKABLE"]
@ -407,6 +450,7 @@
"id": "food_apple",
"name": { "name": "apple", "plural": "apples" },
"renderable": { "glyph": "%", "fg": "#00FF00", "bg": "#000000", "order": 4 },
"class": "comestible",
"weight": 0.5,
"value": 1,
"flags": ["FOOD", "CONSUMABLE", "STACKABLE"]

View file

@ -437,10 +437,40 @@ pub struct Beatitude {
pub known: bool,
}
#[derive(Debug, Serialize, Deserialize, Copy, Clone, PartialEq, Eq)]
pub enum ItemType {
Amulet,
Weapon,
Armour,
Comestible,
Scroll,
Spellbook,
Potion,
Ring,
Wand,
}
impl ItemType {
pub fn string(&self) -> &str {
match self {
ItemType::Amulet => "Amulets",
ItemType::Weapon => "Weapons",
ItemType::Armour => "Armour",
ItemType::Comestible => "Comestibles",
ItemType::Scroll => "Scrolls",
ItemType::Spellbook => "Spellbooks",
ItemType::Potion => "Potions",
ItemType::Ring => "Rings",
ItemType::Wand => "Wands",
}
}
}
#[derive(Component, Debug, Serialize, Deserialize, Clone)]
pub struct Item {
pub weight: f32, // in lbs
pub value: f32, // base
pub category: ItemType,
}
#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq, Hash)]

View file

@ -3,10 +3,12 @@ use super::{
item_colour_ecs,
obfuscate_name_ecs,
print_options,
unique_ecs,
renderable_colour,
ItemMenuResult,
UniqueInventoryItem,
BUC,
Key,
};
use crate::{
gamelog,
@ -23,7 +25,7 @@ use crate::{
};
use bracket_lib::prelude::*;
use specs::prelude::*;
use std::collections::BTreeMap;
use std::collections::HashMap;
/// Handles the Identify menu.
pub fn identify(gs: &mut State, ctx: &mut BTerm) -> (ItemMenuResult, Option<Entity>) {
@ -37,9 +39,12 @@ pub fn identify(gs: &mut State, ctx: &mut BTerm) -> (ItemMenuResult, Option<Enti
let names = gs.ecs.read_storage::<Name>();
let renderables = gs.ecs.read_storage::<Renderable>();
let beatitudes = gs.ecs.read_storage::<Beatitude>();
let keys = gs.ecs.read_storage::<Key>();
let build_identify_iterator = || {
(&entities, &items, &renderables, &names).join().filter(|(item_entity, _i, _r, n)| {
(&entities, &items, &renderables, &names, &keys)
.join()
.filter(|(item_entity, _i, _r, n, _k)| {
// If not owned by the player, return false.
let mut keep = false;
if let Some(bp) = backpack.get(*item_entity) {
@ -91,34 +96,15 @@ pub fn identify(gs: &mut State, ctx: &mut BTerm) -> (ItemMenuResult, Option<Enti
.log();
return (ItemMenuResult::Selected, Some(build_identify_iterator().nth(0).unwrap().0));
}
let mut player_inventory: super::PlayerInventory = BTreeMap::new();
for (entity, _i, renderable, name) in build_identify_iterator() {
let (singular, plural) = obfuscate_name_ecs(&gs.ecs, entity);
let beatitude_status = if
let Some(beatitude) = gs.ecs.read_storage::<Beatitude>().get(entity)
{
match beatitude.buc {
BUC::Blessed => 1,
BUC::Uncursed => 2,
BUC::Cursed => 3,
}
} else {
0
};
let unique_item = UniqueInventoryItem {
display_name: super::DisplayName { singular: singular.clone(), plural: plural.clone() },
rgb: item_colour_ecs(&gs.ecs, entity),
renderables: renderable_colour(&renderables, entity),
glyph: renderable.glyph,
beatitude_status: beatitude_status,
name: name.name.clone(),
};
let mut player_inventory: super::PlayerInventory = HashMap::new();
for (entity, _i, renderable, name, key) in build_identify_iterator() {
let unique_item = unique_ecs(&gs.ecs, entity);
player_inventory
.entry(unique_item)
.and_modify(|(_e, count)| {
*count += 1;
.and_modify(|slot| {
slot.count += 1;
})
.or_insert((entity, 1));
.or_insert(super::InventorySlot { item: entity, count: 1, idx: key.idx });
}
// Get display args
let width = get_max_inventory_width(&player_inventory);
@ -135,7 +121,7 @@ pub fn identify(gs: &mut State, ctx: &mut BTerm) -> (ItemMenuResult, Option<Enti
ctx.draw_box(x, y, width + 2, count + 1, RGB::named(WHITE), RGB::named(BLACK));
// Input
match ctx.key {
/*match ctx.key {
None => (ItemMenuResult::NoResponse, None),
Some(key) =>
match key {
@ -161,4 +147,6 @@ pub fn identify(gs: &mut State, ctx: &mut BTerm) -> (ItemMenuResult, Option<Enti
}
}
}
*/
(ItemMenuResult::NoResponse, None)
}

View file

@ -1,13 +1,15 @@
use notan::prelude::*;
use notan::draw::{ Draw, Font };
use notan::draw::{ Draw, Font, DrawTextSection };
use specs::prelude::*;
use super::TILESIZE;
use crate::Fonts;
use crate::{ Fonts, camera::get_offset };
use super::{ items, Filter, print_options, ItemType };
pub fn draw_inventory(ecs: &World, draw: &mut Draw, font: &Fonts, x: i32, y: i32) {
let inv = super::get_player_inventory(ecs);
let inv = items(ecs, Filter::Backpack);
let offsets = crate::camera::get_offset();
super::print_options(
print_options(
ecs,
draw,
font,
&inv,
@ -15,3 +17,29 @@ pub fn draw_inventory(ecs: &World, draw: &mut Draw, font: &Fonts, x: i32, y: i32
((y as f32) + (offsets.y as f32)) * TILESIZE
);
}
pub fn draw_all(ecs: &World, draw: &mut Draw, font: &Fonts, x: f32, y: f32) {
let mut y = y;
let itemtypes = vec![
ItemType::Weapon,
ItemType::Armour,
ItemType::Comestible,
ItemType::Potion,
ItemType::Scroll,
ItemType::Spellbook,
ItemType::Wand,
ItemType::Amulet,
ItemType::Ring
];
for itemtype in itemtypes {
let inv = items(ecs, Filter::Category(itemtype));
if inv.is_empty() {
continue;
}
draw.text(&font.b(), itemtype.string()).position(x, y).color(Color::WHITE);
y += TILESIZE;
y = print_options(ecs, draw, font, &inv, x, y) + TILESIZE;
}
}

View file

@ -1,65 +1,65 @@
use notan::prelude::*;
pub fn letter_to_option(key: KeyCode, shift: bool) -> i32 {
pub fn letter_to_option(key: KeyCode, shift: bool) -> Option<usize> {
if shift {
match key {
KeyCode::A => 26,
KeyCode::B => 27,
KeyCode::C => 28,
KeyCode::D => 29,
KeyCode::E => 30,
KeyCode::F => 31,
KeyCode::G => 32,
KeyCode::H => 33,
KeyCode::I => 34,
KeyCode::J => 35,
KeyCode::K => 36,
KeyCode::L => 37,
KeyCode::M => 38,
KeyCode::N => 39,
KeyCode::O => 40,
KeyCode::P => 41,
KeyCode::Q => 42,
KeyCode::R => 43,
KeyCode::S => 44,
KeyCode::T => 45,
KeyCode::U => 46,
KeyCode::V => 47,
KeyCode::W => 48,
KeyCode::X => 49,
KeyCode::Y => 50,
KeyCode::Z => 51,
_ => -1,
KeyCode::A => Some(26),
KeyCode::B => Some(27),
KeyCode::C => Some(28),
KeyCode::D => Some(29),
KeyCode::E => Some(30),
KeyCode::F => Some(31),
KeyCode::G => Some(32),
KeyCode::H => Some(33),
KeyCode::I => Some(34),
KeyCode::J => Some(35),
KeyCode::K => Some(36),
KeyCode::L => Some(37),
KeyCode::M => Some(38),
KeyCode::N => Some(39),
KeyCode::O => Some(40),
KeyCode::P => Some(41),
KeyCode::Q => Some(42),
KeyCode::R => Some(43),
KeyCode::S => Some(44),
KeyCode::T => Some(45),
KeyCode::U => Some(46),
KeyCode::V => Some(47),
KeyCode::W => Some(48),
KeyCode::X => Some(49),
KeyCode::Y => Some(50),
KeyCode::Z => Some(51),
_ => None,
}
} else {
match key {
KeyCode::A => 0,
KeyCode::B => 1,
KeyCode::C => 2,
KeyCode::D => 3,
KeyCode::E => 4,
KeyCode::F => 5,
KeyCode::G => 6,
KeyCode::H => 7,
KeyCode::I => 8,
KeyCode::J => 9,
KeyCode::K => 10,
KeyCode::L => 11,
KeyCode::M => 12,
KeyCode::N => 13,
KeyCode::O => 14,
KeyCode::P => 15,
KeyCode::Q => 16,
KeyCode::R => 17,
KeyCode::S => 18,
KeyCode::T => 19,
KeyCode::U => 20,
KeyCode::V => 21,
KeyCode::W => 22,
KeyCode::X => 23,
KeyCode::Y => 24,
KeyCode::Z => 25,
_ => -1,
KeyCode::A => Some(0),
KeyCode::B => Some(1),
KeyCode::C => Some(2),
KeyCode::D => Some(3),
KeyCode::E => Some(4),
KeyCode::F => Some(5),
KeyCode::G => Some(6),
KeyCode::H => Some(7),
KeyCode::I => Some(8),
KeyCode::J => Some(9),
KeyCode::K => Some(10),
KeyCode::L => Some(11),
KeyCode::M => Some(12),
KeyCode::N => Some(13),
KeyCode::O => Some(14),
KeyCode::P => Some(15),
KeyCode::Q => Some(16),
KeyCode::R => Some(17),
KeyCode::S => Some(18),
KeyCode::T => Some(19),
KeyCode::U => Some(20),
KeyCode::V => Some(21),
KeyCode::W => Some(22),
KeyCode::X => Some(23),
KeyCode::Y => Some(24),
KeyCode::Z => Some(25),
_ => None,
}
}
}

View file

@ -32,6 +32,9 @@ use super::{
Skills,
Viewshed,
BUC,
Key,
Item,
ItemType,
consts::ids::get_local_col,
};
use crate::consts::prelude::*;
@ -51,7 +54,8 @@ use notan::draw::{ Draw, DrawTextSection, DrawImages, DrawShapes };
use std::collections::HashMap;
use bracket_lib::prelude::*;
use specs::prelude::*;
use std::collections::BTreeMap;
use std::collections::{ BTreeMap, HashSet };
use crate::invkeys::check_key;
mod character_creation;
mod cheat_menu;
@ -361,7 +365,8 @@ pub fn draw_ui2(ecs: &World, draw: &mut Draw, atlas: &HashMap<String, Texture>,
.size(FONTSIZE);
}
// Equipment
let renderables = ecs.read_storage::<Renderable>();
draw_all(ecs, draw, font, ((VIEWPORT_W + 3) as f32) * TILESIZE, TILESIZE);
/*let renderables = ecs.read_storage::<Renderable>();
let mut equipment: Vec<(String, RGB, RGB, FontCharType)> = Vec::new();
let entities = ecs.entities();
for (entity, _equipped, renderable) in (&entities, &equipped, &renderables)
@ -424,8 +429,8 @@ pub fn draw_ui2(ecs: &World, draw: &mut Draw, atlas: &HashMap<String, Texture>,
)
.position(((DISPLAYWIDTH - 1) as f32) * TILESIZE, (y as f32) * TILESIZE)
.size(FONTSIZE)
.h_align_right();
let player_inventory = get_player_inventory(&ecs);
.h_align_right();*/
//let player_inventory = get_player_inventory(&ecs);
// TODO: print_options()
}
}
@ -690,7 +695,7 @@ pub fn draw_ui(ecs: &World, ctx: &mut BTerm) {
)
);
y += 1;
let player_inventory = get_player_inventory(&ecs);
//let player_inventory = get_player_inventory(&ecs);
// Draw spells - if we have any -- NYI!
if let Some(known_spells) = ecs.read_storage::<KnownSpells>().get(*player_entity) {
y += 1;
@ -914,72 +919,84 @@ pub enum ItemMenuResult {
}
pub fn print_options(
ecs: &World,
draw: &mut Draw,
font: &Fonts,
inventory: &PlayerInventory,
mut x: f32,
mut y: f32
) -> (f32, i32) {
let mut j = 0;
) -> f32 {
let initial_x: f32 = x;
let mut width: i32 = -1;
for (item, (_e, item_count)) in inventory {
let mut sorted: Vec<_> = inventory.iter().collect();
sorted.sort_by(|a, b| a.1.idx.cmp(&b.1.idx));
for (info, slot) in sorted {
x = initial_x;
// Print the character required to access this item. i.e. (a)
if j < 26 {
draw.text(&font.b(), &format!("{} ", (97 + j) as u8 as char))
if slot.idx < 26 {
draw.text(&font.b(), &format!("{} ", (97 + slot.idx) as u8 as char))
.position(x, y)
.color(Color::YELLOW)
.size(FONTSIZE);
} else {
// If we somehow have more than 26, start using capitals
draw.text(&font.b(), &format!("{} ", (65 - 26 + j) as u8 as char))
draw.text(&font.b(), &format!("{} ", (65 - 26 + slot.idx) as u8 as char))
.position(x, y)
.color(Color::YELLOW)
.size(FONTSIZE);
}
x = draw.last_text_bounds().max_x();
let fg = RGB::from_u8(item.renderables.0, item.renderables.1, item.renderables.2);
draw.text(&font.n(), &format!("{} ", item.glyph as u8 as char))
let fg = RGB::from_u8(info.renderables.0, info.renderables.1, info.renderables.2);
draw.text(&font.n(), &format!("{} ", info.glyph as u8 as char))
.position(x, y)
.size(FONTSIZE)
.color(Color::from_rgb(fg.r, fg.g, fg.b));
x = draw.last_text_bounds().max_x();
let fg = RGB::from_u8(item.rgb.0, item.rgb.1, item.rgb.2);
if item_count > &1 {
draw.text(&font.n(), &format!("{} {}", item_count, item.display_name.plural))
let fg = RGB::from_u8(info.rgb.0, info.rgb.1, info.rgb.2);
if slot.count > 1 {
draw.text(&font.n(), &format!("{} {}", slot.count, info.display_name.plural))
.position(x, y)
.color(Color::from_rgb(fg.r, fg.g, fg.b))
.size(FONTSIZE);
} else {
let prefix = if item.display_name.singular.to_lowercase().ends_with("s") {
let prefix = if info.display_name.singular.to_lowercase().ends_with("s") {
"some"
} else if
['a', 'e', 'i', 'o', 'u']
.iter()
.any(|&v| item.display_name.singular.to_lowercase().starts_with(v))
.any(|&v| info.display_name.singular.to_lowercase().starts_with(v))
{
"an"
} else {
"a"
};
draw.text(&font.n(), &format!("{} {}", prefix, item.display_name.singular))
draw.text(&font.n(), &format!("{} {}", prefix, info.display_name.singular))
.position(x, y)
.color(Color::from_rgb(fg.r, fg.g, fg.b))
.size(FONTSIZE);
if let Some(worn) = ecs.read_storage::<Equipped>().get(slot.item) {
x = draw.last_text_bounds().max_x();
use crate::EquipmentSlot;
let text = match worn.slot {
EquipmentSlot::Melee | EquipmentSlot::Shield => "being held",
_ => "being worn",
};
draw.text(&font.ib(), &format!(" ({})", text))
.position(x, y)
.color(Color::WHITE)
.size(FONTSIZE);
};
}
y += TILESIZE;
j += 1;
}
return (y, width);
return y;
}
pub fn get_max_inventory_width(inventory: &PlayerInventory) -> i32 {
let mut width: i32 = 0;
for (item, (_e, count)) in inventory {
for (item, slot) in inventory {
let mut this_width = 4; // The spaces before and after the character to select this item, etc.
if count <= &1 {
if slot.count <= 1 {
this_width += item.display_name.singular.len() as i32;
if item.display_name.singular == item.display_name.plural {
this_width += 4; // "some".len
@ -992,7 +1009,7 @@ pub fn get_max_inventory_width(inventory: &PlayerInventory) -> i32 {
}
} else {
this_width += item.display_name.plural.len() as i32;
this_width += count.to_string().len() as i32; // i.e. "12".len
this_width += slot.count.to_string().len() as i32; // i.e. "12".len
}
width = if width > this_width { width } else { this_width };
}
@ -1043,7 +1060,7 @@ pub fn obfuscate_name(
if has_beatitude.known {
let prefix = match has_beatitude.buc {
BUC::Cursed => Some("cursed "),
BUC::Uncursed => None,
BUC::Uncursed => Some("uncursed "),
BUC::Blessed => Some("blessed "),
};
if prefix.is_some() {
@ -1098,6 +1115,12 @@ pub fn obfuscate_name_ecs(ecs: &World, item: Entity) -> (String, String) {
}
}
}
if let Some(worn) = ecs.read_storage::<Equipped>().get(item) {
if worn.owner == *ecs.fetch::<Entity>() {
singular.insert_str(singular.len(), " (worn)");
plural.insert_str(plural.len(), " (worn)");
}
}
return (singular, plural);
}
@ -1254,8 +1277,6 @@ pub struct UniqueInventoryItem {
name: String,
}
pub type PlayerInventory = BTreeMap<UniqueInventoryItem, (Entity, i32)>;
pub fn unique(
entity: Entity,
names: &ReadStorage<Name>,
@ -1325,57 +1346,74 @@ pub fn unique_ecs(ecs: &World, entity: Entity) -> UniqueInventoryItem {
);
}
pub fn get_player_inventory(ecs: &World) -> PlayerInventory {
let player_entity = ecs.fetch::<Entity>();
let names = ecs.read_storage::<Name>();
let backpack = ecs.read_storage::<InBackpack>();
let entities = ecs.entities();
let renderables = ecs.read_storage::<Renderable>();
pub struct InventorySlot {
pub item: Entity,
pub count: i32,
pub idx: usize,
}
let mut player_inventory: BTreeMap<UniqueInventoryItem, (Entity, i32)> = BTreeMap::new();
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 item_colour = item_colour_ecs(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 (singular, plural) = obfuscate_name_ecs(ecs, entity);
let beatitude_status = if let Some(beatitude) = ecs.read_storage::<Beatitude>().get(entity) {
match beatitude.buc {
BUC::Blessed => 1,
BUC::Uncursed => 2,
BUC::Cursed => 3,
}
} else {
0
};
let unique_item = UniqueInventoryItem {
display_name: DisplayName { singular: singular.clone(), plural: plural },
rgb: item_colour,
renderables: renderables,
glyph: renderable.glyph,
beatitude_status: beatitude_status,
name: name.name.clone(),
};
player_inventory
.entry(unique_item)
.and_modify(|(_e, count)| {
*count += 1;
pub type PlayerInventory = HashMap<UniqueInventoryItem, InventorySlot>;
pub enum Filter {
All,
Backpack,
Equipped,
Category(ItemType),
}
macro_rules! includeitem {
($inv:expr, $ecs:expr, $e:expr, $k:expr) => {
$inv.entry(unique_ecs($ecs, $e))
.and_modify(|slot| {
slot.count += 1;
})
.or_insert((entity, 1));
.or_insert(InventorySlot {
item: $e,
count: 1,
idx: $k.idx,
});
};
}
pub fn items(ecs: &World, filter: Filter) -> HashMap<UniqueInventoryItem, InventorySlot> {
let entities = ecs.entities();
let keys = ecs.read_storage::<Key>();
let mut inv: HashMap<UniqueInventoryItem, InventorySlot> = HashMap::new();
match filter {
Filter::All => {
for (e, k) in (&entities, &keys).join() {
includeitem!(inv, ecs, e, k);
}
}
Filter::Backpack => {
let backpack = ecs.read_storage::<InBackpack>();
for (e, k, _b) in (&entities, &keys, &backpack).join() {
includeitem!(inv, ecs, e, k);
}
}
Filter::Equipped => {
let equipped = ecs.read_storage::<Equipped>();
for (e, k, _e) in (&entities, &keys, &equipped).join() {
includeitem!(inv, ecs, e, k);
}
}
Filter::Category(itemtype) => {
let items = ecs.read_storage::<Item>();
for (e, k, _i) in (&entities, &keys, &items)
.join()
.filter(|e| e.2.category == itemtype) {
includeitem!(inv, ecs, e, k);
}
}
}
return player_inventory;
inv
}
pub fn show_inventory(gs: &mut State, ctx: &mut App) -> (ItemMenuResult, Option<Entity>) {
let player_inventory = get_player_inventory(&gs.ecs);
let on_overmap = gs.ecs.fetch::<Map>().overmap;
let count = player_inventory.len();
let key = &ctx.keyboard;
for keycode in key.pressed.iter() {
@ -1385,20 +1423,27 @@ pub fn show_inventory(gs: &mut State, ctx: &mut App) -> (ItemMenuResult, Option<
}
_ => {
let shift = key.shift();
let selection = letter_to_option::letter_to_option(*keycode, shift);
if selection > -1 && selection < (count as i32) {
let selection = if
let Some(key) = letter_to_option::letter_to_option(*keycode, shift)
{
key
} else {
continue;
};
if check_key(selection) {
if on_overmap {
gamelog::Logger::new().append("You can't use items on the overmap.").log();
} else {
return (
ItemMenuResult::Selected,
Some(
player_inventory
.iter()
.nth(selection as usize)
.unwrap().1.0
),
);
// Get the first entity with a Key {} component that has an idx matching "selection".
let entities = gs.ecs.entities();
let keyed_items = gs.ecs.read_storage::<Key>();
let backpack = gs.ecs.read_storage::<InBackpack>();
for (e, key, _b) in (&entities, &keyed_items, &backpack).join() {
if key.idx == selection {
return (ItemMenuResult::Selected, Some(e));
}
}
// TODO: Probably some gamelog about not having the selected item?
}
}
}
@ -1408,8 +1453,6 @@ pub fn show_inventory(gs: &mut State, ctx: &mut App) -> (ItemMenuResult, Option<
}
pub fn drop_item_menu(gs: &mut State, ctx: &mut App) -> (ItemMenuResult, Option<Entity>) {
let player_inventory = get_player_inventory(&gs.ecs);
let count = player_inventory.len();
let on_overmap = gs.ecs.fetch::<Map>().overmap;
let key = &ctx.keyboard;
@ -1420,20 +1463,26 @@ pub fn drop_item_menu(gs: &mut State, ctx: &mut App) -> (ItemMenuResult, Option<
}
_ => {
let shift = key.shift();
let selection = letter_to_option::letter_to_option(*keycode, shift);
if selection > -1 && selection < (count as i32) {
let selection = if
let Some(key) = letter_to_option::letter_to_option(*keycode, shift)
{
key
} else {
continue;
};
if check_key(selection) {
if on_overmap {
gamelog::Logger::new().append("You can't drop items on the overmap.").log();
} else {
return (
ItemMenuResult::Selected,
Some(
player_inventory
.iter()
.nth(selection as usize)
.unwrap().1.0
),
);
// Get the first entity with a Key {} component that has an idx matching "selection".
let entities = gs.ecs.entities();
let keyed_items = gs.ecs.read_storage::<Key>();
let backpack = gs.ecs.read_storage::<InBackpack>();
for (e, key, _b) in (&entities, &keyed_items, &backpack).join() {
if key.idx == selection {
return (ItemMenuResult::Selected, Some(e));
}
}
}
}
}

View file

@ -3,9 +3,11 @@ use super::{
item_colour_ecs,
obfuscate_name_ecs,
print_options,
unique_ecs,
renderable_colour,
ItemMenuResult,
UniqueInventoryItem,
InventorySlot,
};
use crate::{
gamelog,
@ -18,10 +20,11 @@ use crate::{
Renderable,
states::state::*,
BUC,
Key,
};
use bracket_lib::prelude::*;
use specs::prelude::*;
use std::collections::BTreeMap;
use std::collections::HashMap;
/// Handles the Remove Curse menu.
pub fn remove_curse(gs: &mut State, ctx: &mut BTerm) -> (ItemMenuResult, Option<Entity>) {
@ -33,11 +36,12 @@ pub fn remove_curse(gs: &mut State, ctx: &mut BTerm) -> (ItemMenuResult, Option<
let beatitudes = gs.ecs.read_storage::<Beatitude>();
let names = gs.ecs.read_storage::<Name>();
let renderables = gs.ecs.read_storage::<Renderable>();
let keys = gs.ecs.read_storage::<Key>();
let build_cursed_iterator = || {
(&entities, &items, &beatitudes, &renderables, &names)
(&entities, &items, &beatitudes, &renderables, &names, &keys)
.join()
.filter(|(item_entity, _i, b, _r, _n)| {
.filter(|(item_entity, _i, b, _r, _n, _k)| {
// Set all items to FALSE initially.
let mut keep = false;
// If found in the player's backpack, set to TRUE
@ -86,8 +90,8 @@ pub fn remove_curse(gs: &mut State, ctx: &mut BTerm) -> (ItemMenuResult, Option<
.log();
return (ItemMenuResult::Selected, Some(item));
}
let mut player_inventory: super::PlayerInventory = BTreeMap::new();
for (entity, _i, _b, renderable, name) in build_cursed_iterator() {
let mut player_inventory: super::PlayerInventory = HashMap::new();
for (entity, _i, _b, renderable, name, key) in build_cursed_iterator() {
let (singular, plural) = obfuscate_name_ecs(&gs.ecs, entity);
let beatitude_status = if
let Some(beatitude) = gs.ecs.read_storage::<Beatitude>().get(entity)
@ -100,20 +104,17 @@ pub fn remove_curse(gs: &mut State, ctx: &mut BTerm) -> (ItemMenuResult, Option<
} else {
0
};
let unique_item = UniqueInventoryItem {
display_name: super::DisplayName { singular: singular.clone(), plural: plural.clone() },
rgb: item_colour_ecs(&gs.ecs, entity),
renderables: renderable_colour(&renderables, entity),
glyph: renderable.glyph,
beatitude_status: beatitude_status,
name: name.name.clone(),
};
let unique_item = unique_ecs(&gs.ecs, entity);
player_inventory
.entry(unique_item)
.and_modify(|(_e, count)| {
*count += 1;
.and_modify(|slot| {
slot.count += 1;
})
.or_insert((entity, 1));
.or_insert(InventorySlot {
item: entity,
count: 1,
idx: key.idx,
});
}
// Get display args
let width = get_max_inventory_width(&player_inventory);
@ -130,7 +131,7 @@ pub fn remove_curse(gs: &mut State, ctx: &mut BTerm) -> (ItemMenuResult, Option<
ctx.draw_box(x, y, width + 2, count + 1, RGB::named(WHITE), RGB::named(BLACK));
// Input
match ctx.key {
/*match ctx.key {
None => (ItemMenuResult::NoResponse, None),
Some(key) =>
match key {
@ -155,5 +156,6 @@ pub fn remove_curse(gs: &mut State, ctx: &mut BTerm) -> (ItemMenuResult, Option<
(ItemMenuResult::NoResponse, None)
}
}
}
}*/
(ItemMenuResult::NoResponse, None)
}

View file

@ -18,6 +18,11 @@ pub fn restore_invkeys(invkeys: HashMap<UniqueInventoryItem, usize>) {
INVKEYS.lock().unwrap().extend(invkeys);
}
pub fn check_key(idx: usize) -> bool {
let lock = ASSIGNEDKEYS.lock().unwrap();
lock[idx]
}
pub fn item_exists(item: &UniqueInventoryItem) -> Option<usize> {
let invkeys = INVKEYS.lock().unwrap();
use bracket_lib::prelude::*;

View file

@ -6,6 +6,7 @@ pub struct Item {
pub id: String,
pub name: Name,
pub renderable: Option<Renderable>,
pub class: String,
pub weight: Option<f32>,
pub value: Option<f32>,
pub equip: Option<Equippable>,

View file

@ -290,6 +290,18 @@ pub fn spawn_named_item(
eb = eb.with(Item {
weight: item_template.weight.unwrap_or(0.0),
value: item_template.value.unwrap_or(0.0),
category: match item_template.class.as_str() {
"amulet" => ItemType::Amulet,
"weapon" => ItemType::Weapon,
"armour" => ItemType::Armour,
"comestible" => ItemType::Comestible,
"scroll" => ItemType::Scroll,
"spellbook" => ItemType::Spellbook,
"potion" => ItemType::Potion,
"ring" => ItemType::Ring,
"wand" => ItemType::Wand,
_ => unreachable!("Unknown item type."),
},
});
eb = spawn_position(pos, eb, key, raws);
if needs_key {