static keys - items in inventory will save their assigned key

precursor to cleaning up/modularising inventory display, instead of needing to iterate through every item held to find unique copies, we can just check if the button pressed corresponds to any entity's Key {} index
This commit is contained in:
Llywelwyn 2023-10-05 00:52:34 +01:00
parent a7b4f621fb
commit d11971126c
19 changed files with 445 additions and 122 deletions

View file

@ -5,7 +5,7 @@
"renderable": { "glyph": "!", "fg": "#FF00FF", "bg": "#000000", "order": 4 }, "renderable": { "glyph": "!", "fg": "#FF00FF", "bg": "#000000", "order": 4 },
"weight": 1, "weight": 1,
"value": 50, "value": 50,
"flags": ["CONSUMABLE", "DESTRUCTIBLE"], "flags": ["CONSUMABLE", "DESTRUCTIBLE", "STACKABLE"],
"effects": { "heal": "4d4+2" }, "effects": { "heal": "4d4+2" },
"magic": { "class": "uncommon", "naming": "potion" } "magic": { "class": "uncommon", "naming": "potion" }
}, },
@ -15,7 +15,7 @@
"renderable": { "glyph": "!", "fg": "#FF00FF", "bg": "#000000", "order": 4 }, "renderable": { "glyph": "!", "fg": "#FF00FF", "bg": "#000000", "order": 4 },
"weight": 1, "weight": 1,
"value": 25, "value": 25,
"flags": ["CONSUMABLE", "DESTRUCTIBLE"], "flags": ["CONSUMABLE", "DESTRUCTIBLE", "STACKABLE"],
"effects": { "heal": "2d4+2" }, "effects": { "heal": "2d4+2" },
"magic": { "class": "uncommon", "naming": "potion" } "magic": { "class": "uncommon", "naming": "potion" }
}, },
@ -401,7 +401,7 @@
"renderable": { "glyph": "%", "fg": "#FFA07A", "bg": "#000000", "order": 4 }, "renderable": { "glyph": "%", "fg": "#FFA07A", "bg": "#000000", "order": 4 },
"weight": 1, "weight": 1,
"value": 1, "value": 1,
"flags": ["FOOD", "CONSUMABLE"] "flags": ["FOOD", "CONSUMABLE", "STACKABLE"]
}, },
{ {
"id": "food_apple", "id": "food_apple",
@ -409,6 +409,6 @@
"renderable": { "glyph": "%", "fg": "#00FF00", "bg": "#000000", "order": 4 }, "renderable": { "glyph": "%", "fg": "#00FF00", "bg": "#000000", "order": 4 },
"weight": 0.5, "weight": 0.5,
"value": 1, "value": 1,
"flags": ["FOOD", "CONSUMABLE"] "flags": ["FOOD", "CONSUMABLE", "STACKABLE"]
} }
] ]

View file

@ -468,9 +468,20 @@ pub struct IdentifiedItem {
pub name: String, pub name: String,
} }
#[derive(Component, Debug, Serialize, Deserialize, Clone)]
pub struct Stackable {}
#[derive(Component, Debug, Serialize, Deserialize, Clone)] #[derive(Component, Debug, Serialize, Deserialize, Clone)]
pub struct EquipmentChanged {} pub struct EquipmentChanged {}
#[derive(Component, Debug, Serialize, Deserialize, Clone)]
pub struct WantsToRemoveKey {}
#[derive(Component, Debug, Serialize, Deserialize, Clone)]
pub struct Key {
pub idx: usize,
}
#[derive(PartialEq, Copy, Clone, Serialize, Deserialize)] #[derive(PartialEq, Copy, Clone, Serialize, Deserialize)]
pub enum BurdenLevel { pub enum BurdenLevel {
Burdened, Burdened,
@ -654,6 +665,9 @@ pub struct InBackpack {
pub owner: Entity, pub owner: Entity,
} }
#[derive(Component, Serialize, Deserialize, Clone)]
pub struct WantsToAssignKey {}
#[derive(Component, Debug, ConvertSaveload)] #[derive(Component, Debug, ConvertSaveload)]
pub struct WantsToPickupItem { pub struct WantsToPickupItem {
pub collected_by: Entity, pub collected_by: Entity,

View file

@ -25,6 +25,7 @@ pub const NUTRITION_BLESSED: &str = "Delicious";
pub const LEVELUP_PLAYER: &str = "Welcome to experience level"; pub const LEVELUP_PLAYER: &str = "Welcome to experience level";
pub const YOU_PICKUP_ITEM: &str = "You pick up the"; pub const YOU_PICKUP_ITEM: &str = "You pick up the";
pub const NO_MORE_KEYS: &str = "Your backpack cannot accomodate any more items";
pub const YOU_DROP_ITEM: &str = "You drop the"; pub const YOU_DROP_ITEM: &str = "You drop the";
pub const YOU_EQUIP_ITEM: &str = "You equip the"; pub const YOU_EQUIP_ITEM: &str = "You equip the";
pub const YOU_REMOVE_ITEM: &str = "You unequip your"; pub const YOU_REMOVE_ITEM: &str = "You unequip your";

View file

@ -82,6 +82,7 @@ pub fn delete_the_dead(ecs: &mut World) {
// For everything that died, increment the event log, and delete. // For everything that died, increment the event log, and delete.
for victim in dead { for victim in dead {
gamelog::record_event(events::EVENT::Turn(1)); gamelog::record_event(events::EVENT::Turn(1));
// TODO: Delete stuff from inventory? This should be handled elsewhere.
ecs.delete_entity(victim).expect("Unable to delete."); ecs.delete_entity(victim).expect("Unable to delete.");
} }
} }

View file

@ -4,14 +4,14 @@ use specs::prelude::*;
use super::TILESIZE; use super::TILESIZE;
use crate::Fonts; use crate::Fonts;
pub fn draw_inventory(ecs: &World, draw: &mut Draw, font: &Fonts) { pub fn draw_inventory(ecs: &World, draw: &mut Draw, font: &Fonts, x: i32, y: i32) {
let inv = super::get_player_inventory(ecs); let inv = super::get_player_inventory(ecs);
let offsets = crate::camera::get_offset(); let offsets = crate::camera::get_offset();
super::print_options( super::print_options(
draw, draw,
font, font,
&inv, &inv,
(offsets.x as f32) * TILESIZE, ((x as f32) + (offsets.x as f32)) * TILESIZE,
(offsets.y as f32) * TILESIZE ((y as f32) + (offsets.y as f32)) * TILESIZE
); );
} }

View file

@ -1238,13 +1238,13 @@ pub fn show_help(ctx: &mut BTerm) -> YesNoResult {
} }
} }
#[derive(PartialEq, Eq, PartialOrd, Ord)] #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
struct DisplayName { struct DisplayName {
singular: String, singular: String,
plural: String, plural: String,
} }
#[derive(PartialEq, Eq, PartialOrd, Ord)] #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct UniqueInventoryItem { pub struct UniqueInventoryItem {
display_name: DisplayName, display_name: DisplayName,
rgb: (u8, u8, u8), rgb: (u8, u8, u8),
@ -1256,6 +1256,75 @@ pub struct UniqueInventoryItem {
pub type PlayerInventory = BTreeMap<UniqueInventoryItem, (Entity, i32)>; pub type PlayerInventory = BTreeMap<UniqueInventoryItem, (Entity, i32)>;
pub fn unique(
entity: Entity,
names: &ReadStorage<Name>,
obfuscated_names: &ReadStorage<ObfuscatedName>,
renderables: &ReadStorage<Renderable>,
beatitudes: &ReadStorage<Beatitude>,
magic_items: &ReadStorage<MagicItem>,
charges: Option<&ReadStorage<Charges>>,
dm: &MasterDungeonMap
) -> UniqueInventoryItem {
let item_colour = item_colour(entity, beatitudes);
let (singular, plural) = obfuscate_name(
entity,
names,
magic_items,
obfuscated_names,
beatitudes,
dm,
charges
);
let (renderables, glyph) = 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,
),
renderable.glyph,
)
} else {
unreachable!("Item has no renderable component.")
};
let name = if let Some(name) = names.get(entity) {
name
} else {
unreachable!("Item has no name component.")
};
let beatitude_status = if let Some(beatitude) = beatitudes.get(entity) {
match beatitude.buc {
BUC::Blessed => 1,
BUC::Uncursed => 2,
BUC::Cursed => 3,
}
} else {
0
};
UniqueInventoryItem {
display_name: DisplayName { singular: singular.clone(), plural },
rgb: item_colour,
renderables,
glyph,
beatitude_status,
name: name.name.clone(),
}
}
pub fn unique_ecs(ecs: &World, entity: Entity) -> UniqueInventoryItem {
return unique(
entity,
&ecs.read_storage::<Name>(),
&ecs.read_storage::<ObfuscatedName>(),
&ecs.read_storage::<Renderable>(),
&ecs.read_storage::<Beatitude>(),
&ecs.read_storage::<MagicItem>(),
Some(&ecs.read_storage::<Charges>()),
&ecs.fetch::<MasterDungeonMap>()
);
}
pub fn get_player_inventory(ecs: &World) -> PlayerInventory { pub fn get_player_inventory(ecs: &World) -> PlayerInventory {
let player_entity = ecs.fetch::<Entity>(); let player_entity = ecs.fetch::<Entity>();
let names = ecs.read_storage::<Name>(); let names = ecs.read_storage::<Name>();
@ -1338,39 +1407,23 @@ pub fn show_inventory(gs: &mut State, ctx: &mut App) -> (ItemMenuResult, Option<
return (ItemMenuResult::NoResponse, None); return (ItemMenuResult::NoResponse, None);
} }
pub fn drop_item_menu(gs: &mut State, ctx: &mut BTerm) -> (ItemMenuResult, Option<Entity>) { pub fn drop_item_menu(gs: &mut State, ctx: &mut App) -> (ItemMenuResult, Option<Entity>) {
let player_inventory = get_player_inventory(&gs.ecs); let player_inventory = get_player_inventory(&gs.ecs);
let count = player_inventory.len(); let count = player_inventory.len();
let (x_offset, y_offset) = (1, 10);
let on_overmap = gs.ecs.fetch::<Map>().overmap; let on_overmap = gs.ecs.fetch::<Map>().overmap;
let message = if !on_overmap {
"Drop what? [aA-zZ][Esc.]"
} else {
"You can't drop items on the overmap [Esc.]"
};
ctx.print_color(1 + x_offset, 1 + y_offset, RGB::named(WHITE), RGB::named(BLACK), message); let key = &ctx.keyboard;
for keycode in key.pressed.iter() {
let x = 1 + x_offset; match *keycode {
let y = 3 + y_offset; KeyCode::Escape => {
let width = get_max_inventory_width(&player_inventory); return (ItemMenuResult::Cancel, None);
ctx.draw_box(x, y, width + 2, (count + 1) as i32, RGB::named(WHITE), RGB::named(BLACK)); }
match ctx.key {
None => (ItemMenuResult::NoResponse, None),
Some(key) =>
match key {
VirtualKeyCode::Escape => (ItemMenuResult::Cancel, None),
_ => { _ => {
let selection = letter_to_option(key); let shift = key.shift();
let selection = letter_to_option::letter_to_option(*keycode, shift);
if selection > -1 && selection < (count as i32) { if selection > -1 && selection < (count as i32) {
if on_overmap { if on_overmap {
gamelog::Logger gamelog::Logger::new().append("You can't drop items on the overmap.").log();
::new()
.append("You can't drop items on the overmap.")
.log();
} else { } else {
return ( return (
ItemMenuResult::Selected, ItemMenuResult::Selected,
@ -1383,11 +1436,11 @@ pub fn drop_item_menu(gs: &mut State, ctx: &mut BTerm) -> (ItemMenuResult, Optio
); );
} }
} }
}
}
}
(ItemMenuResult::NoResponse, None) (ItemMenuResult::NoResponse, None)
} }
}
}
}
pub fn remove_item_menu(gs: &mut State, ctx: &mut BTerm) -> (ItemMenuResult, Option<Entity>) { pub fn remove_item_menu(gs: &mut State, ctx: &mut BTerm) -> (ItemMenuResult, Option<Entity>) {
let player_entity = gs.ecs.fetch::<Entity>(); let player_entity = gs.ecs.fetch::<Entity>();

View file

@ -12,6 +12,9 @@ use crate::{
ObfuscatedName, ObfuscatedName,
Position, Position,
WantsToPickupItem, WantsToPickupItem,
WantsToAssignKey,
Renderable,
Stackable,
}; };
use specs::prelude::*; use specs::prelude::*;
use crate::consts::messages; use crate::consts::messages;
@ -30,9 +33,12 @@ impl<'a> System<'a> for ItemCollectionSystem {
WriteStorage<'a, EquipmentChanged>, WriteStorage<'a, EquipmentChanged>,
ReadStorage<'a, MagicItem>, ReadStorage<'a, MagicItem>,
ReadStorage<'a, ObfuscatedName>, ReadStorage<'a, ObfuscatedName>,
ReadStorage<'a, Renderable>,
ReadStorage<'a, Beatitude>, ReadStorage<'a, Beatitude>,
ReadExpect<'a, MasterDungeonMap>, ReadExpect<'a, MasterDungeonMap>,
ReadStorage<'a, Charges>, ReadStorage<'a, Charges>,
ReadStorage<'a, WantsToAssignKey>,
ReadStorage<'a, Stackable>,
); );
fn run(&mut self, data: Self::SystemData) { fn run(&mut self, data: Self::SystemData) {
@ -45,20 +51,17 @@ impl<'a> System<'a> for ItemCollectionSystem {
mut equipment_changed, mut equipment_changed,
magic_items, magic_items,
obfuscated_names, obfuscated_names,
renderables,
beatitudes, beatitudes,
dm, dm,
wands, wands,
wants_key,
stackable,
) = data; ) = data;
for pickup in wants_pickup.join() { let mut to_remove: Vec<Entity> = Vec::new();
positions.remove(pickup.item); // For every item that wants to be picked up, that *isn't* still waiting on a key assignment.
backpack for (pickup, _key) in (&wants_pickup, !&wants_key).join() {
.insert(pickup.item, InBackpack { owner: pickup.collected_by })
.expect("Unable to pickup item.");
equipment_changed
.insert(pickup.collected_by, EquipmentChanged {})
.expect("Unable to insert EquipmentChanged.");
if pickup.collected_by == *player_entity { if pickup.collected_by == *player_entity {
gamelog::Logger gamelog::Logger
::new() ::new()
@ -82,8 +85,17 @@ impl<'a> System<'a> for ItemCollectionSystem {
.period() .period()
.log(); .log();
} }
positions.remove(pickup.item);
backpack
.insert(pickup.item, InBackpack { owner: pickup.collected_by })
.expect("Unable to pickup item.");
equipment_changed
.insert(pickup.collected_by, EquipmentChanged {})
.expect("Unable to insert EquipmentChanged.");
to_remove.push(pickup.collected_by);
}
for item in to_remove.iter() {
wants_pickup.remove(*item);
} }
wants_pickup.clear();
} }
} }

View file

@ -12,6 +12,7 @@ use crate::{
ObfuscatedName, ObfuscatedName,
Position, Position,
WantsToDropItem, WantsToDropItem,
WantsToRemoveKey,
}; };
use specs::prelude::*; use specs::prelude::*;
use crate::consts::messages; use crate::consts::messages;
@ -34,6 +35,7 @@ impl<'a> System<'a> for ItemDropSystem {
ReadStorage<'a, ObfuscatedName>, ReadStorage<'a, ObfuscatedName>,
ReadExpect<'a, MasterDungeonMap>, ReadExpect<'a, MasterDungeonMap>,
ReadStorage<'a, Charges>, ReadStorage<'a, Charges>,
WriteStorage<'a, WantsToRemoveKey>,
); );
fn run(&mut self, data: Self::SystemData) { fn run(&mut self, data: Self::SystemData) {
@ -50,6 +52,7 @@ impl<'a> System<'a> for ItemDropSystem {
obfuscated_names, obfuscated_names,
dm, dm,
wands, wands,
mut keys,
) = data; ) = data;
for (entity, to_drop) in (&entities, &wants_drop).join() { for (entity, to_drop) in (&entities, &wants_drop).join() {
@ -68,6 +71,9 @@ impl<'a> System<'a> for ItemDropSystem {
backpack.remove(to_drop.item); backpack.remove(to_drop.item);
if entity == *player_entity { if entity == *player_entity {
keys.insert(to_drop.item, WantsToRemoveKey {}).expect(
"Unable to insert WantsToRemoveKey"
);
gamelog::Logger gamelog::Logger
::new() ::new()
.append(messages::YOU_DROP_ITEM) .append(messages::YOU_DROP_ITEM)

View file

@ -0,0 +1,153 @@
use crate::{
gamelog,
gui::unique,
Beatitude,
Charges,
MagicItem,
MasterDungeonMap,
Name,
ObfuscatedName,
Stackable,
Renderable,
WantsToAssignKey,
WantsToRemoveKey,
Key,
};
use specs::prelude::*;
use crate::consts::messages;
use bracket_lib::prelude::*;
use crate::invkeys::*;
pub struct KeyHandling {}
const DEBUG_KEYHANDLING: bool = true;
impl<'a> System<'a> for KeyHandling {
#[allow(clippy::type_complexity)]
type SystemData = (
Entities<'a>,
WriteStorage<'a, WantsToAssignKey>,
WriteStorage<'a, WantsToRemoveKey>,
WriteStorage<'a, Key>,
ReadStorage<'a, Stackable>,
ReadStorage<'a, Name>,
ReadStorage<'a, ObfuscatedName>,
ReadStorage<'a, Renderable>,
ReadStorage<'a, Beatitude>,
ReadStorage<'a, MagicItem>,
ReadStorage<'a, Charges>,
ReadExpect<'a, MasterDungeonMap>,
);
fn run(&mut self, data: Self::SystemData) {
let (
entities,
mut wants_keys,
mut wants_removekey,
mut keys,
stackable,
names,
obfuscated_names,
renderables,
beatitudes,
magic_items,
wands,
dm,
) = data;
// For every entity that wants to be picked up, that still needs a key assigned.
for (e, _wants_key) in (&entities, &wants_keys).join() {
if DEBUG_KEYHANDLING {
console::log(&format!("KEYHANDLING: Assigning key to {:?}", e));
}
let (stacks, mut handled, unique) = (
if let Some(_) = stackable.get(e) { true } else { false },
false,
unique(
e,
&names,
&obfuscated_names,
&renderables,
&beatitudes,
&magic_items,
Some(&wands),
&dm
),
);
if stacks {
console::log(&format!("KEYHANDLING: Item is stackable."));
let maybe_key = item_exists(&unique);
if maybe_key.is_some() {
console::log(&format!("KEYHANDLING: Existing stack found for this item."));
let key = maybe_key.unwrap();
keys.insert(e, Key { idx: key }).expect("Unable to insert Key.");
console::log(&format!("KEYHANDLING: Assigned key idx {} to item.", key));
handled = true;
}
}
if !handled {
console::log(
&format!("KEYHANDLING: Item is not stackable, or no existing stack found.")
);
if let Some(idx) = assign_next_available() {
console::log(
&format!("KEYHANDLING: Assigned next available index {} to item.", idx)
);
keys.insert(e, Key { idx }).expect("Unable to insert Key.");
register_stackable(stacks, unique, idx);
} else {
console::log(&format!("KEYHANDLING: No more keys available."));
gamelog::Logger
::new()
.append(messages::NO_MORE_KEYS)
.colour(WHITE)
.period()
.log();
}
}
}
for (e, _wants_key) in (&entities, &wants_removekey).join() {
let idx = keys.get(e).unwrap().idx;
if DEBUG_KEYHANDLING {
console::log(&format!("KEYHANDLING: Removing key from {:?}", e));
}
// If the item is *not* stackable, then we can just remove the key and clear the index.
if let None = stackable.get(e) {
console::log(
&format!("KEYHANDLING: Item is not stackable, clearing index {}.", idx)
);
clear_idx(idx);
keys.remove(e);
continue;
}
// If the item *is* stackable, then we need to check if there are any other items that
// share this key assignment, before clearing the index.
console::log(
&format!(
"KEYHANDLING: Item is stackable, checking if any other items share this key."
)
);
let mut sole_item_with_key = true;
for (entity, key) in (&entities, &keys).join() {
if entity != e && key.idx == idx {
console::log(&format!("KEYHANDLING: Another item shares index {}", idx));
sole_item_with_key = false;
break;
}
}
// If no other items shared this key, free up the index.
if sole_item_with_key {
console::log(
&format!("KEYHANDLING: No other items found, clearing index {}.", idx)
);
clear_idx(idx);
}
// Either way, remove the key component from this item, because we're dropping it.
console::log(&format!("KEYHANDLING: Removing key component from item."));
keys.remove(e);
}
wants_removekey.clear();
wants_keys.clear();
}
}

View file

@ -4,6 +4,7 @@ mod equip_system;
mod identification_system; mod identification_system;
mod remove_system; mod remove_system;
mod use_system; mod use_system;
mod keyhandling;
pub use self::{ pub use self::{
collection_system::ItemCollectionSystem, collection_system::ItemCollectionSystem,
@ -12,4 +13,5 @@ pub use self::{
identification_system::ItemIdentificationSystem, identification_system::ItemIdentificationSystem,
remove_system::ItemRemoveSystem, remove_system::ItemRemoveSystem,
use_system::ItemUseSystem, use_system::ItemUseSystem,
keyhandling::KeyHandling,
}; };

55
src/invkeys.rs Normal file
View file

@ -0,0 +1,55 @@
use std::sync::Mutex;
use std::collections::{ HashMap };
use specs::prelude::*;
use crate::gui::UniqueInventoryItem;
lazy_static! {
pub static ref INVKEYS: Mutex<HashMap<UniqueInventoryItem, usize>> = Mutex::new(HashMap::new());
pub static ref ASSIGNEDKEYS: Mutex<Vec<bool>> = Mutex::new(vec![false; 52]);
}
/// For (de)serialization.
pub fn clone_invkeys() -> HashMap<UniqueInventoryItem, usize> {
let invkeys = INVKEYS.lock().unwrap();
invkeys.clone()
}
pub fn restore_invkeys(invkeys: HashMap<UniqueInventoryItem, usize>) {
INVKEYS.lock().unwrap().clear();
INVKEYS.lock().unwrap().extend(invkeys);
}
pub fn item_exists(item: &UniqueInventoryItem) -> Option<usize> {
let invkeys = INVKEYS.lock().unwrap();
use bracket_lib::prelude::*;
console::log(&format!("{:?}", item));
if invkeys.contains_key(item) {
Some(*invkeys.get(item).unwrap())
} else {
None
}
}
pub fn assign_next_available() -> Option<usize> {
let mut lock = ASSIGNEDKEYS.lock().unwrap();
for (i, key) in lock.iter_mut().enumerate() {
if !*key {
*key = true;
return Some(i);
}
}
None
}
pub fn register_stackable(stacks: bool, item: UniqueInventoryItem, idx: usize) {
if stacks {
let mut invkeys = INVKEYS.lock().unwrap();
invkeys.insert(item, idx);
}
}
pub fn clear_idx(idx: usize) {
let mut lock = ASSIGNEDKEYS.lock().unwrap();
lock[idx] = false;
let mut invkeys = INVKEYS.lock().unwrap();
invkeys.retain(|_k, v| *v != idx);
}

View file

@ -35,6 +35,7 @@ pub mod rex_assets;
pub mod spatial; pub mod spatial;
pub mod morgue; pub mod morgue;
pub mod states; pub mod states;
pub mod invkeys;
pub use components::*; pub use components::*;
use particle_system::ParticleBuilder; use particle_system::ParticleBuilder;

View file

@ -133,6 +133,10 @@ fn setup(gfx: &mut Graphics) -> State {
gs.ecs.register::<SpawnParticleLine>(); gs.ecs.register::<SpawnParticleLine>();
gs.ecs.register::<HasDamageModifiers>(); gs.ecs.register::<HasDamageModifiers>();
gs.ecs.register::<Intrinsics>(); gs.ecs.register::<Intrinsics>();
gs.ecs.register::<Stackable>();
gs.ecs.register::<WantsToAssignKey>();
gs.ecs.register::<Key>();
gs.ecs.register::<WantsToRemoveKey>();
gs.ecs.register::<SimpleMarker<SerializeMe>>(); gs.ecs.register::<SimpleMarker<SerializeMe>>();
gs.ecs.register::<SerializationHelper>(); gs.ecs.register::<SerializationHelper>();
gs.ecs.register::<DMSerializationHelper>(); gs.ecs.register::<DMSerializationHelper>();
@ -544,7 +548,12 @@ fn draw(_app: &mut App, gfx: &mut Graphics, gs: &mut State) {
corner_text("Create morgue file? [Y/N]", &mut draw, &gs.font); corner_text("Create morgue file? [Y/N]", &mut draw, &gs.font);
} }
RunState::ShowInventory => { RunState::ShowInventory => {
gui::draw_inventory(&gs.ecs, &mut draw, &gs.font); corner_text("Use what? [aA-zZ]/[Esc.]", &mut draw, &gs.font);
gui::draw_inventory(&gs.ecs, &mut draw, &gs.font, 1, 3);
}
RunState::ShowDropItem => {
corner_text("Drop what? [aA-zZ]/[Esc.]", &mut draw, &gs.font);
gui::draw_inventory(&gs.ecs, &mut draw, &gs.font, 1, 3);
} }
_ => {} _ => {}
} }
@ -558,7 +567,7 @@ fn update(ctx: &mut App, state: &mut State) {
fn corner_text(text: &str, draw: &mut Draw, font: &Fonts) { fn corner_text(text: &str, draw: &mut Draw, font: &Fonts) {
let offset = crate::camera::get_offset(); let offset = crate::camera::get_offset();
draw.text(&font.n(), &text) draw.text(&font.b(), &text)
.position(((offset.x + 1) as f32) * TILESIZE, ((offset.y + 1) as f32) * TILESIZE) .position(((offset.x + 1) as f32) * TILESIZE, ((offset.y + 1) as f32) * TILESIZE)
.size(FONTSIZE); .size(FONTSIZE);
} }

View file

@ -299,7 +299,7 @@ fn random_shape_builder(
end: bool end: bool
) -> bool { ) -> bool {
// Pick an initial builder // Pick an initial builder
let builder_roll = rng.roll_dice(1, 16); let builder_roll = rng.roll_dice(1, 13);
let mut want_doors = true; let mut want_doors = true;
match builder_roll { match builder_roll {
1 => builder.start_with(CellularAutomataBuilder::new()), 1 => builder.start_with(CellularAutomataBuilder::new()),
@ -317,11 +317,7 @@ fn random_shape_builder(
10 => builder.start_with(DLABuilder::central_attractor()), 10 => builder.start_with(DLABuilder::central_attractor()),
11 => builder.start_with(DLABuilder::insectoid()), 11 => builder.start_with(DLABuilder::insectoid()),
12 => builder.start_with(VoronoiBuilder::pythagoras()), 12 => builder.start_with(VoronoiBuilder::pythagoras()),
13 => builder.start_with(VoronoiBuilder::manhattan()), _ => builder.start_with(VoronoiBuilder::manhattan()),
_ =>
builder.start_with(
PrefabBuilder::constant(prefab_builder::prefab_levels::WFC_POPULATED)
),
} }
// 'Select' the centre by placing a starting position, and cull everywhere unreachable. // 'Select' the centre by placing a starting position, and cull everywhere unreachable.

View file

@ -5,57 +5,8 @@ pub struct PrefabLevel {
pub height: usize, pub height: usize,
} }
#[allow(dead_code)]
pub const WFC_POPULATED: PrefabLevel = PrefabLevel { template: LEVEL_MAP, width: 80, height: 43 };
pub const OVERMAP: PrefabLevel = PrefabLevel { template: OVERMAP_TEMPLATE, width: 69, height: 41 }; pub const OVERMAP: PrefabLevel = PrefabLevel { template: OVERMAP_TEMPLATE, width: 69, height: 41 };
#[allow(dead_code)]
const LEVEL_MAP: &str =
"
################################################################################
#          ########################################################    #########
#    @     ######    #########       ####     ###################        #######
#          ####   g  #                          ###############            #####
#          #### #    # #######       ####       #############                ###
##### ######### #    # #######       #########  ####    #####                ###
##### ######### ###### #######   o   #########  #### ## #####                ###
##                        ####       #########   ### ##         o            ###
##### ######### ###       ####       #######         ## #####                ###
##### ######### ###       ####       ####### #   ### ## #####                ###
##### ######### ###       ####       ####### #######    #####     o          ###
###          ## ###       ####       ####### ################                ###
###          ## ###   o   ###### ########### #   ############                ###
###          ## ###       ###### ###########     ###                         ###
###    %                  ###### ########### #   ###   !   ##                ###
###          ## ###              ######   ## #######       ##                ###
###          ## ###       ## ### #####     # ########################      #####
###          ## ###       ## ### #####     # #   ######################    #####
#### ## ####### ###### ##### ### ####          o ###########     ######    #####
#### ## ####### ###### ####   ## ####        #   #########         ###### ######
#    ## ####### ###### ####   ## ####        ############           ##### ######
# g  ## ####### ###### ####   ##        %    ###########   o      o  #### #    #
#    ## ###            ####   ## ####        #   #######   ##    ##  ####   g  #
#######                  ####### ####            ######     !    !    ### #    #
######                     ##### ####        #   ######               ### ######
#####                            #####     # ##########               ### ######
#####           !           ### ######     # ##########      o##o     ### #   ##
#####                       ### #######   ## #   ######               ###   g ##
#   ##                     #### ######## ###   o #######  ^########^ #### #   ##
# g    #                 ###### ######## #####   #######  ^        ^ #### ######
#   ##g####           ######    ######## ################           ##### ######
#   ## ########## ##########    ######## #################         ######      #
#####   ######### ########## %  ######## ###################     ######## ##   #
#### ### ######## ##########    ######## #################### ##########   #   #
### ##### ######   #########    ########          ########### #######   # g#   #
### #####           ###############      ###      ########### #######   ####   #
### ##### ####       ############## ######## g  g ########### ####         # ^ #
#### ###^####         ############# ########      #####       ####      # g#   #
#####   ######       ###            ########      ##### g     ####   !  ####^^ #
#!%^## ###  ##           ########## ########  gg                 g         # > #
#!%^   ###  ###     ############### ########      ##### g     ####      # g#   #
# %^##  ^   ###     ############### ########      #####       ##################
################################################################################";
const OVERMAP_TEMPLATE: &str = const OVERMAP_TEMPLATE: &str =
" "
^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^

View file

@ -30,6 +30,7 @@ use super::{
Viewshed, Viewshed,
WantsToMelee, WantsToMelee,
WantsToPickupItem, WantsToPickupItem,
WantsToAssignKey,
get_dest, get_dest,
Destination, Destination,
DamageType, DamageType,
@ -640,7 +641,9 @@ fn get_item(ecs: &mut World) -> RunState {
return RunState::AwaitingInput; return RunState::AwaitingInput;
} }
Some(item) => { Some(item) => {
let mut assignkey = ecs.write_storage::<WantsToAssignKey>();
let mut pickup = ecs.write_storage::<WantsToPickupItem>(); let mut pickup = ecs.write_storage::<WantsToPickupItem>();
assignkey.insert(item, WantsToAssignKey {}).expect("Unable to insert WantsToAssignKey");
pickup pickup
.insert(*player_entity, WantsToPickupItem { collected_by: *player_entity, item }) .insert(*player_entity, WantsToPickupItem { collected_by: *player_entity, item })
.expect("Unable to insert want to pickup item."); .expect("Unable to insert want to pickup item.");

View file

@ -61,6 +61,7 @@ macro_rules! apply_flags {
"IDENTIFY" => $eb = $eb.with(ProvidesIdentify {}), "IDENTIFY" => $eb = $eb.with(ProvidesIdentify {}),
"DIGGER" => $eb = $eb.with(Digger {}), "DIGGER" => $eb = $eb.with(Digger {}),
"MAGICMAP" => $eb = $eb.with(MagicMapper {}), "MAGICMAP" => $eb = $eb.with(MagicMapper {}),
"STACKABLE" => $eb = $eb.with(Stackable {}),
// CAN BE DESTROYED BY DAMAGE // CAN BE DESTROYED BY DAMAGE
"DESTRUCTIBLE" => $eb = $eb.with(Destructible {}), "DESTRUCTIBLE" => $eb = $eb.with(Destructible {}),
// --- EQUIP SLOTS --- // --- EQUIP SLOTS ---
@ -276,6 +277,7 @@ pub fn spawn_named_item(
if known_beatitude && !identified_items.contains(&item_template.name.name) { if known_beatitude && !identified_items.contains(&item_template.name.name) {
dm.identified_items.insert(item_template.name.name.clone()); dm.identified_items.insert(item_template.name.name.clone());
} }
let needs_key = is_player_owned(&player_entity, &pos);
std::mem::drop(player_entity); std::mem::drop(player_entity);
std::mem::drop(dm); std::mem::drop(dm);
// -- DROP EVERYTHING THAT INVOLVES THE ECS BEFORE THIS POINT --- // -- DROP EVERYTHING THAT INVOLVES THE ECS BEFORE THIS POINT ---
@ -290,6 +292,9 @@ pub fn spawn_named_item(
value: item_template.value.unwrap_or(0.0), value: item_template.value.unwrap_or(0.0),
}); });
eb = spawn_position(pos, eb, key, raws); eb = spawn_position(pos, eb, key, raws);
if needs_key {
eb = eb.with(WantsToAssignKey {});
}
if let Some(renderable) = &item_template.renderable { if let Some(renderable) = &item_template.renderable {
eb = eb.with(get_renderable_component(renderable)); eb = eb.with(get_renderable_component(renderable));
@ -387,6 +392,7 @@ pub fn spawn_named_mob(
if raws.mob_index.contains_key(key) { if raws.mob_index.contains_key(key) {
let mob_template = &raws.raws.mobs[raws.mob_index[key]]; let mob_template = &raws.raws.mobs[raws.mob_index[key]];
let mut player_level = 1; let mut player_level = 1;
let needs_key;
{ {
let pools = ecs.read_storage::<Pools>(); let pools = ecs.read_storage::<Pools>();
let player_entity = ecs.fetch::<Entity>(); let player_entity = ecs.fetch::<Entity>();
@ -394,12 +400,15 @@ pub fn spawn_named_mob(
if let Some(pool) = player_pool { if let Some(pool) = player_pool {
player_level = pool.level; player_level = pool.level;
} }
needs_key = is_player_owned(&player_entity, &pos);
} }
let mut eb; let mut eb;
// New entity with a position, name, combatstats, and viewshed // New entity with a position, name, combatstats, and viewshed
eb = ecs.create_entity().marked::<SimpleMarker<SerializeMe>>(); eb = ecs.create_entity().marked::<SimpleMarker<SerializeMe>>();
eb = spawn_position(pos, eb, key, raws); eb = spawn_position(pos, eb, key, raws);
if needs_key {
eb = eb.with(WantsToAssignKey {});
}
eb = eb.with(Name { name: mob_template.name.clone(), plural: mob_template.name.clone() }); eb = eb.with(Name { name: mob_template.name.clone(), plural: mob_template.name.clone() });
eb = eb.with(Viewshed { eb = eb.with(Viewshed {
visible_tiles: Vec::new(), visible_tiles: Vec::new(),
@ -620,10 +629,18 @@ pub fn spawn_named_prop(
pos: SpawnType pos: SpawnType
) -> Option<Entity> { ) -> Option<Entity> {
if raws.prop_index.contains_key(key) { if raws.prop_index.contains_key(key) {
let needs_key;
{
let player_entity = ecs.fetch::<Entity>();
needs_key = is_player_owned(&player_entity, &pos);
}
// ENTITY BUILDER PREP // ENTITY BUILDER PREP
let prop_template = &raws.raws.props[raws.prop_index[key]]; let prop_template = &raws.raws.props[raws.prop_index[key]];
let mut eb = ecs.create_entity().marked::<SimpleMarker<SerializeMe>>(); let mut eb = ecs.create_entity().marked::<SimpleMarker<SerializeMe>>();
eb = spawn_position(pos, eb, key, raws); eb = spawn_position(pos, eb, key, raws);
if needs_key {
eb = eb.with(WantsToAssignKey {});
}
// APPLY MANDATORY COMPONENTS FOR A PROP: // APPLY MANDATORY COMPONENTS FOR A PROP:
// - Name // - Name
// - Prop {} // - Prop {}
@ -691,6 +708,23 @@ fn spawn_position<'a>(
eb eb
} }
fn is_player_owned(player: &Entity, pos: &SpawnType) -> bool {
match pos {
SpawnType::Carried { by } => {
if by == player {
return true;
}
}
SpawnType::Equipped { by } => {
if by == player {
return true;
}
}
_ => {}
}
false
}
fn get_renderable_component( fn get_renderable_component(
renderable: &super::item_structs::Renderable renderable: &super::item_structs::Renderable
) -> crate::components::Renderable { ) -> crate::components::Renderable {

View file

@ -103,6 +103,7 @@ pub fn save_game(ecs: &mut World) {
InflictsDamage, InflictsDamage,
Intrinsics, Intrinsics,
Item, Item,
Key,
KnownSpells, KnownSpells,
LootTable, LootTable,
MagicItem, MagicItem,
@ -132,17 +133,20 @@ pub fn save_game(ecs: &mut World) {
SpawnParticleBurst, SpawnParticleBurst,
SpawnParticleLine, SpawnParticleLine,
SpawnParticleSimple, SpawnParticleSimple,
Stackable,
TakingTurn, TakingTurn,
Telepath, Telepath,
ToHitBonus, ToHitBonus,
Viewshed, Viewshed,
Charges, Charges,
WantsToApproach, WantsToApproach,
WantsToAssignKey,
WantsToDropItem, WantsToDropItem,
WantsToFlee, WantsToFlee,
WantsToMelee, WantsToMelee,
WantsToPickupItem, WantsToPickupItem,
WantsToRemoveItem, WantsToRemoveItem,
WantsToRemoveKey,
WantsToUseItem, WantsToUseItem,
SerializationHelper, SerializationHelper,
DMSerializationHelper DMSerializationHelper
@ -235,6 +239,7 @@ pub fn load_game(ecs: &mut World) {
InflictsDamage, InflictsDamage,
Intrinsics, Intrinsics,
Item, Item,
Key,
KnownSpells, KnownSpells,
LootTable, LootTable,
MagicItem, MagicItem,
@ -264,17 +269,20 @@ pub fn load_game(ecs: &mut World) {
SpawnParticleBurst, SpawnParticleBurst,
SpawnParticleLine, SpawnParticleLine,
SpawnParticleSimple, SpawnParticleSimple,
Stackable,
TakingTurn, TakingTurn,
Telepath, Telepath,
ToHitBonus, ToHitBonus,
Viewshed, Viewshed,
Charges, Charges,
WantsToApproach, WantsToApproach,
WantsToAssignKey,
WantsToDropItem, WantsToDropItem,
WantsToFlee, WantsToFlee,
WantsToMelee, WantsToMelee,
WantsToPickupItem, WantsToPickupItem,
WantsToRemoveItem, WantsToRemoveItem,
WantsToRemoveKey,
WantsToUseItem, WantsToUseItem,
SerializationHelper, SerializationHelper,
DMSerializationHelper DMSerializationHelper

View file

@ -102,12 +102,13 @@ impl State {
fn resolve_entity_decisions(&mut self) { fn resolve_entity_decisions(&mut self) {
let mut trigger_system = trigger_system::TriggerSystem {}; let mut trigger_system = trigger_system::TriggerSystem {};
let mut inventory_system = inventory::ItemCollectionSystem {};
let mut item_equip_system = inventory::ItemEquipSystem {}; let mut item_equip_system = inventory::ItemEquipSystem {};
let mut item_use_system = inventory::ItemUseSystem {}; let mut item_use_system = inventory::ItemUseSystem {};
let mut item_drop_system = inventory::ItemDropSystem {}; let mut item_drop_system = inventory::ItemDropSystem {};
let mut item_remove_system = inventory::ItemRemoveSystem {}; let mut item_remove_system = inventory::ItemRemoveSystem {};
let mut inventory_system = inventory::ItemCollectionSystem {};
let mut item_id_system = inventory::ItemIdentificationSystem {}; let mut item_id_system = inventory::ItemIdentificationSystem {};
let mut key_system = inventory::KeyHandling {};
let mut melee_system = MeleeCombatSystem {}; let mut melee_system = MeleeCombatSystem {};
trigger_system.run_now(&self.ecs); trigger_system.run_now(&self.ecs);
inventory_system.run_now(&self.ecs); inventory_system.run_now(&self.ecs);
@ -116,6 +117,7 @@ impl State {
item_drop_system.run_now(&self.ecs); item_drop_system.run_now(&self.ecs);
item_remove_system.run_now(&self.ecs); item_remove_system.run_now(&self.ecs);
item_id_system.run_now(&self.ecs); item_id_system.run_now(&self.ecs);
key_system.run_now(&self.ecs);
melee_system.run_now(&self.ecs); melee_system.run_now(&self.ecs);
effects::run_effects_queue(&mut self.ecs); effects::run_effects_queue(&mut self.ecs);
@ -351,7 +353,29 @@ impl State {
} }
} }
} }
// RunState::ShowDropItem RunState::ShowDropItem => {
let result = gui::drop_item_menu(self, ctx);
match result.0 {
gui::ItemMenuResult::Cancel => {
new_runstate = RunState::AwaitingInput;
}
gui::ItemMenuResult::NoResponse => {}
gui::ItemMenuResult::Selected => {
let item = result.1.unwrap();
let mut removekey = self.ecs.write_storage::<WantsToRemoveKey>();
let mut intent = self.ecs.write_storage::<WantsToDropItem>();
removekey
.insert(item, WantsToRemoveKey {})
.expect("Unable to insert WantsToRemoveKey");
intent
.insert(*self.ecs.fetch::<Entity>(), WantsToDropItem {
item,
})
.expect("Unable to insert WantsToDropItem");
new_runstate = RunState::Ticking;
}
}
}
// RunState::ShowRemoveItem // RunState::ShowRemoveItem
// RunState::ShowTargeting // RunState::ShowTargeting
// RunState::ShowRemoveCurse // RunState::ShowRemoveCurse
@ -648,7 +672,7 @@ impl State {
} }
} }
RunState::ShowDropItem => { RunState::ShowDropItem => {
let result = gui::drop_item_menu(self, ctx); let result = (gui::ItemMenuResult::Cancel, None); //gui::drop_item_menu(self, ctx);
match result.0 { match result.0 {
gui::ItemMenuResult::Cancel => { gui::ItemMenuResult::Cancel => {
new_runstate = RunState::AwaitingInput; new_runstate = RunState::AwaitingInput;