From d11971126c0c86df24e2b6d59b3b1ef13e33ab15 Mon Sep 17 00:00:00 2001 From: Llywelwyn Date: Thu, 5 Oct 2023 00:52:34 +0100 Subject: [PATCH] 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 --- raws/items.json | 8 +- src/components.rs | 14 ++ src/consts/messages.rs | 1 + src/damage_system.rs | 1 + src/gui/inventory.rs | 6 +- src/gui/mod.rs | 139 +++++++++++----- src/inventory/collection_system.rs | 34 ++-- src/inventory/drop_system.rs | 6 + src/inventory/keyhandling.rs | 153 ++++++++++++++++++ src/inventory/mod.rs | 2 + src/invkeys.rs | 55 +++++++ src/lib.rs | 1 + src/main.rs | 13 +- src/map_builders/mod.rs | 8 +- .../prefab_builder/prefab_levels.rs | 49 ------ src/player.rs | 3 + src/raws/rawmaster.rs | 36 ++++- src/saveload_system.rs | 8 + src/states/state.rs | 30 +++- 19 files changed, 445 insertions(+), 122 deletions(-) create mode 100644 src/inventory/keyhandling.rs create mode 100644 src/invkeys.rs diff --git a/raws/items.json b/raws/items.json index 40ac3fa..41137ae 100644 --- a/raws/items.json +++ b/raws/items.json @@ -5,7 +5,7 @@ "renderable": { "glyph": "!", "fg": "#FF00FF", "bg": "#000000", "order": 4 }, "weight": 1, "value": 50, - "flags": ["CONSUMABLE", "DESTRUCTIBLE"], + "flags": ["CONSUMABLE", "DESTRUCTIBLE", "STACKABLE"], "effects": { "heal": "4d4+2" }, "magic": { "class": "uncommon", "naming": "potion" } }, @@ -15,7 +15,7 @@ "renderable": { "glyph": "!", "fg": "#FF00FF", "bg": "#000000", "order": 4 }, "weight": 1, "value": 25, - "flags": ["CONSUMABLE", "DESTRUCTIBLE"], + "flags": ["CONSUMABLE", "DESTRUCTIBLE", "STACKABLE"], "effects": { "heal": "2d4+2" }, "magic": { "class": "uncommon", "naming": "potion" } }, @@ -401,7 +401,7 @@ "renderable": { "glyph": "%", "fg": "#FFA07A", "bg": "#000000", "order": 4 }, "weight": 1, "value": 1, - "flags": ["FOOD", "CONSUMABLE"] + "flags": ["FOOD", "CONSUMABLE", "STACKABLE"] }, { "id": "food_apple", @@ -409,6 +409,6 @@ "renderable": { "glyph": "%", "fg": "#00FF00", "bg": "#000000", "order": 4 }, "weight": 0.5, "value": 1, - "flags": ["FOOD", "CONSUMABLE"] + "flags": ["FOOD", "CONSUMABLE", "STACKABLE"] } ] diff --git a/src/components.rs b/src/components.rs index 126a77e..9184dda 100644 --- a/src/components.rs +++ b/src/components.rs @@ -468,9 +468,20 @@ pub struct IdentifiedItem { pub name: String, } +#[derive(Component, Debug, Serialize, Deserialize, Clone)] +pub struct Stackable {} + #[derive(Component, Debug, Serialize, Deserialize, Clone)] 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)] pub enum BurdenLevel { Burdened, @@ -654,6 +665,9 @@ pub struct InBackpack { pub owner: Entity, } +#[derive(Component, Serialize, Deserialize, Clone)] +pub struct WantsToAssignKey {} + #[derive(Component, Debug, ConvertSaveload)] pub struct WantsToPickupItem { pub collected_by: Entity, diff --git a/src/consts/messages.rs b/src/consts/messages.rs index 89e39c8..7175b2a 100644 --- a/src/consts/messages.rs +++ b/src/consts/messages.rs @@ -25,6 +25,7 @@ pub const NUTRITION_BLESSED: &str = "Delicious"; pub const LEVELUP_PLAYER: &str = "Welcome to experience level"; 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_EQUIP_ITEM: &str = "You equip the"; pub const YOU_REMOVE_ITEM: &str = "You unequip your"; diff --git a/src/damage_system.rs b/src/damage_system.rs index 197d1cb..65a63cd 100644 --- a/src/damage_system.rs +++ b/src/damage_system.rs @@ -82,6 +82,7 @@ pub fn delete_the_dead(ecs: &mut World) { // For everything that died, increment the event log, and delete. for victim in dead { 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."); } } diff --git a/src/gui/inventory.rs b/src/gui/inventory.rs index 0b08274..7a5f1a1 100644 --- a/src/gui/inventory.rs +++ b/src/gui/inventory.rs @@ -4,14 +4,14 @@ use specs::prelude::*; use super::TILESIZE; 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 offsets = crate::camera::get_offset(); super::print_options( draw, font, &inv, - (offsets.x as f32) * TILESIZE, - (offsets.y as f32) * TILESIZE + ((x as f32) + (offsets.x as f32)) * TILESIZE, + ((y as f32) + (offsets.y as f32)) * TILESIZE ); } diff --git a/src/gui/mod.rs b/src/gui/mod.rs index 6db389a..7673861 100644 --- a/src/gui/mod.rs +++ b/src/gui/mod.rs @@ -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 { singular: String, plural: String, } -#[derive(PartialEq, Eq, PartialOrd, Ord)] +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct UniqueInventoryItem { display_name: DisplayName, rgb: (u8, u8, u8), @@ -1256,6 +1256,75 @@ pub struct UniqueInventoryItem { pub type PlayerInventory = BTreeMap; +pub fn unique( + entity: Entity, + names: &ReadStorage, + obfuscated_names: &ReadStorage, + renderables: &ReadStorage, + beatitudes: &ReadStorage, + magic_items: &ReadStorage, + charges: Option<&ReadStorage>, + 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::(), + &ecs.read_storage::(), + &ecs.read_storage::(), + &ecs.read_storage::(), + &ecs.read_storage::(), + Some(&ecs.read_storage::()), + &ecs.fetch::() + ); +} + pub fn get_player_inventory(ecs: &World) -> PlayerInventory { let player_entity = ecs.fetch::(); let names = ecs.read_storage::(); @@ -1338,55 +1407,39 @@ pub fn show_inventory(gs: &mut State, ctx: &mut App) -> (ItemMenuResult, Option< return (ItemMenuResult::NoResponse, None); } -pub fn drop_item_menu(gs: &mut State, ctx: &mut BTerm) -> (ItemMenuResult, Option) { +pub fn drop_item_menu(gs: &mut State, ctx: &mut App) -> (ItemMenuResult, Option) { let player_inventory = get_player_inventory(&gs.ecs); let count = player_inventory.len(); - - let (x_offset, y_offset) = (1, 10); - let on_overmap = gs.ecs.fetch::().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 x = 1 + x_offset; - let y = 3 + y_offset; - let width = get_max_inventory_width(&player_inventory); - 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); - if selection > -1 && selection < (count as i32) { - 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 - ), - ); - } + let key = &ctx.keyboard; + for keycode in key.pressed.iter() { + match *keycode { + KeyCode::Escape => { + return (ItemMenuResult::Cancel, None); + } + _ => { + let shift = key.shift(); + let selection = letter_to_option::letter_to_option(*keycode, shift); + if selection > -1 && selection < (count as i32) { + 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 + ), + ); } - (ItemMenuResult::NoResponse, None) } } + } } + (ItemMenuResult::NoResponse, None) } pub fn remove_item_menu(gs: &mut State, ctx: &mut BTerm) -> (ItemMenuResult, Option) { diff --git a/src/inventory/collection_system.rs b/src/inventory/collection_system.rs index 08dcac2..e602454 100644 --- a/src/inventory/collection_system.rs +++ b/src/inventory/collection_system.rs @@ -12,6 +12,9 @@ use crate::{ ObfuscatedName, Position, WantsToPickupItem, + WantsToAssignKey, + Renderable, + Stackable, }; use specs::prelude::*; use crate::consts::messages; @@ -30,9 +33,12 @@ impl<'a> System<'a> for ItemCollectionSystem { WriteStorage<'a, EquipmentChanged>, ReadStorage<'a, MagicItem>, ReadStorage<'a, ObfuscatedName>, + ReadStorage<'a, Renderable>, ReadStorage<'a, Beatitude>, ReadExpect<'a, MasterDungeonMap>, ReadStorage<'a, Charges>, + ReadStorage<'a, WantsToAssignKey>, + ReadStorage<'a, Stackable>, ); fn run(&mut self, data: Self::SystemData) { @@ -45,20 +51,17 @@ impl<'a> System<'a> for ItemCollectionSystem { mut equipment_changed, magic_items, obfuscated_names, + renderables, beatitudes, dm, wands, + wants_key, + stackable, ) = data; - for pickup in wants_pickup.join() { - 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."); - + let mut to_remove: Vec = Vec::new(); + // For every item that wants to be picked up, that *isn't* still waiting on a key assignment. + for (pickup, _key) in (&wants_pickup, !&wants_key).join() { if pickup.collected_by == *player_entity { gamelog::Logger ::new() @@ -82,8 +85,17 @@ impl<'a> System<'a> for ItemCollectionSystem { .period() .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(); } } diff --git a/src/inventory/drop_system.rs b/src/inventory/drop_system.rs index 686be03..ad058a4 100644 --- a/src/inventory/drop_system.rs +++ b/src/inventory/drop_system.rs @@ -12,6 +12,7 @@ use crate::{ ObfuscatedName, Position, WantsToDropItem, + WantsToRemoveKey, }; use specs::prelude::*; use crate::consts::messages; @@ -34,6 +35,7 @@ impl<'a> System<'a> for ItemDropSystem { ReadStorage<'a, ObfuscatedName>, ReadExpect<'a, MasterDungeonMap>, ReadStorage<'a, Charges>, + WriteStorage<'a, WantsToRemoveKey>, ); fn run(&mut self, data: Self::SystemData) { @@ -50,6 +52,7 @@ impl<'a> System<'a> for ItemDropSystem { obfuscated_names, dm, wands, + mut keys, ) = data; for (entity, to_drop) in (&entities, &wants_drop).join() { @@ -68,6 +71,9 @@ impl<'a> System<'a> for ItemDropSystem { backpack.remove(to_drop.item); if entity == *player_entity { + keys.insert(to_drop.item, WantsToRemoveKey {}).expect( + "Unable to insert WantsToRemoveKey" + ); gamelog::Logger ::new() .append(messages::YOU_DROP_ITEM) diff --git a/src/inventory/keyhandling.rs b/src/inventory/keyhandling.rs new file mode 100644 index 0000000..b5739d5 --- /dev/null +++ b/src/inventory/keyhandling.rs @@ -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(); + } +} diff --git a/src/inventory/mod.rs b/src/inventory/mod.rs index eceaccb..76748e0 100644 --- a/src/inventory/mod.rs +++ b/src/inventory/mod.rs @@ -4,6 +4,7 @@ mod equip_system; mod identification_system; mod remove_system; mod use_system; +mod keyhandling; pub use self::{ collection_system::ItemCollectionSystem, @@ -12,4 +13,5 @@ pub use self::{ identification_system::ItemIdentificationSystem, remove_system::ItemRemoveSystem, use_system::ItemUseSystem, + keyhandling::KeyHandling, }; diff --git a/src/invkeys.rs b/src/invkeys.rs new file mode 100644 index 0000000..f7e5d3a --- /dev/null +++ b/src/invkeys.rs @@ -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> = Mutex::new(HashMap::new()); + pub static ref ASSIGNEDKEYS: Mutex> = Mutex::new(vec![false; 52]); +} + +/// For (de)serialization. +pub fn clone_invkeys() -> HashMap { + let invkeys = INVKEYS.lock().unwrap(); + invkeys.clone() +} +pub fn restore_invkeys(invkeys: HashMap) { + INVKEYS.lock().unwrap().clear(); + INVKEYS.lock().unwrap().extend(invkeys); +} + +pub fn item_exists(item: &UniqueInventoryItem) -> Option { + 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 { + 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); +} diff --git a/src/lib.rs b/src/lib.rs index 3e9b288..e619f18 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -35,6 +35,7 @@ pub mod rex_assets; pub mod spatial; pub mod morgue; pub mod states; +pub mod invkeys; pub use components::*; use particle_system::ParticleBuilder; diff --git a/src/main.rs b/src/main.rs index b079714..81695f5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -133,6 +133,10 @@ fn setup(gfx: &mut Graphics) -> State { gs.ecs.register::(); gs.ecs.register::(); gs.ecs.register::(); + gs.ecs.register::(); + gs.ecs.register::(); + gs.ecs.register::(); + gs.ecs.register::(); gs.ecs.register::>(); gs.ecs.register::(); gs.ecs.register::(); @@ -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); } 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) { 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) .size(FONTSIZE); } diff --git a/src/map_builders/mod.rs b/src/map_builders/mod.rs index 728d1f6..7acb549 100644 --- a/src/map_builders/mod.rs +++ b/src/map_builders/mod.rs @@ -299,7 +299,7 @@ fn random_shape_builder( end: bool ) -> bool { // 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; match builder_roll { 1 => builder.start_with(CellularAutomataBuilder::new()), @@ -317,11 +317,7 @@ fn random_shape_builder( 10 => builder.start_with(DLABuilder::central_attractor()), 11 => builder.start_with(DLABuilder::insectoid()), 12 => builder.start_with(VoronoiBuilder::pythagoras()), - 13 => builder.start_with(VoronoiBuilder::manhattan()), - _ => - builder.start_with( - PrefabBuilder::constant(prefab_builder::prefab_levels::WFC_POPULATED) - ), + _ => builder.start_with(VoronoiBuilder::manhattan()), } // 'Select' the centre by placing a starting position, and cull everywhere unreachable. diff --git a/src/map_builders/prefab_builder/prefab_levels.rs b/src/map_builders/prefab_builder/prefab_levels.rs index 52831eb..997c533 100644 --- a/src/map_builders/prefab_builder/prefab_levels.rs +++ b/src/map_builders/prefab_builder/prefab_levels.rs @@ -5,57 +5,8 @@ pub struct PrefabLevel { 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 }; -#[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 = " ^^^^^^^^^^^^^^^^^^^^^^^^^^^≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈ diff --git a/src/player.rs b/src/player.rs index 2677c95..7c18f81 100644 --- a/src/player.rs +++ b/src/player.rs @@ -30,6 +30,7 @@ use super::{ Viewshed, WantsToMelee, WantsToPickupItem, + WantsToAssignKey, get_dest, Destination, DamageType, @@ -640,7 +641,9 @@ fn get_item(ecs: &mut World) -> RunState { return RunState::AwaitingInput; } Some(item) => { + let mut assignkey = ecs.write_storage::(); let mut pickup = ecs.write_storage::(); + assignkey.insert(item, WantsToAssignKey {}).expect("Unable to insert WantsToAssignKey"); pickup .insert(*player_entity, WantsToPickupItem { collected_by: *player_entity, item }) .expect("Unable to insert want to pickup item."); diff --git a/src/raws/rawmaster.rs b/src/raws/rawmaster.rs index 5cc8735..3c29d0b 100644 --- a/src/raws/rawmaster.rs +++ b/src/raws/rawmaster.rs @@ -61,6 +61,7 @@ macro_rules! apply_flags { "IDENTIFY" => $eb = $eb.with(ProvidesIdentify {}), "DIGGER" => $eb = $eb.with(Digger {}), "MAGICMAP" => $eb = $eb.with(MagicMapper {}), + "STACKABLE" => $eb = $eb.with(Stackable {}), // CAN BE DESTROYED BY DAMAGE "DESTRUCTIBLE" => $eb = $eb.with(Destructible {}), // --- EQUIP SLOTS --- @@ -276,6 +277,7 @@ pub fn spawn_named_item( if known_beatitude && !identified_items.contains(&item_template.name.name) { 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(dm); // -- 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), }); eb = spawn_position(pos, eb, key, raws); + if needs_key { + eb = eb.with(WantsToAssignKey {}); + } if let Some(renderable) = &item_template.renderable { eb = eb.with(get_renderable_component(renderable)); @@ -387,6 +392,7 @@ pub fn spawn_named_mob( if raws.mob_index.contains_key(key) { let mob_template = &raws.raws.mobs[raws.mob_index[key]]; let mut player_level = 1; + let needs_key; { let pools = ecs.read_storage::(); let player_entity = ecs.fetch::(); @@ -394,12 +400,15 @@ pub fn spawn_named_mob( if let Some(pool) = player_pool { player_level = pool.level; } + needs_key = is_player_owned(&player_entity, &pos); } - let mut eb; // New entity with a position, name, combatstats, and viewshed eb = ecs.create_entity().marked::>(); 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(Viewshed { visible_tiles: Vec::new(), @@ -620,10 +629,18 @@ pub fn spawn_named_prop( pos: SpawnType ) -> Option { if raws.prop_index.contains_key(key) { + let needs_key; + { + let player_entity = ecs.fetch::(); + needs_key = is_player_owned(&player_entity, &pos); + } // ENTITY BUILDER PREP let prop_template = &raws.raws.props[raws.prop_index[key]]; let mut eb = ecs.create_entity().marked::>(); eb = spawn_position(pos, eb, key, raws); + if needs_key { + eb = eb.with(WantsToAssignKey {}); + } // APPLY MANDATORY COMPONENTS FOR A PROP: // - Name // - Prop {} @@ -691,6 +708,23 @@ fn spawn_position<'a>( 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( renderable: &super::item_structs::Renderable ) -> crate::components::Renderable { diff --git a/src/saveload_system.rs b/src/saveload_system.rs index dce6f76..e4a8528 100644 --- a/src/saveload_system.rs +++ b/src/saveload_system.rs @@ -103,6 +103,7 @@ pub fn save_game(ecs: &mut World) { InflictsDamage, Intrinsics, Item, + Key, KnownSpells, LootTable, MagicItem, @@ -132,17 +133,20 @@ pub fn save_game(ecs: &mut World) { SpawnParticleBurst, SpawnParticleLine, SpawnParticleSimple, + Stackable, TakingTurn, Telepath, ToHitBonus, Viewshed, Charges, WantsToApproach, + WantsToAssignKey, WantsToDropItem, WantsToFlee, WantsToMelee, WantsToPickupItem, WantsToRemoveItem, + WantsToRemoveKey, WantsToUseItem, SerializationHelper, DMSerializationHelper @@ -235,6 +239,7 @@ pub fn load_game(ecs: &mut World) { InflictsDamage, Intrinsics, Item, + Key, KnownSpells, LootTable, MagicItem, @@ -264,17 +269,20 @@ pub fn load_game(ecs: &mut World) { SpawnParticleBurst, SpawnParticleLine, SpawnParticleSimple, + Stackable, TakingTurn, Telepath, ToHitBonus, Viewshed, Charges, WantsToApproach, + WantsToAssignKey, WantsToDropItem, WantsToFlee, WantsToMelee, WantsToPickupItem, WantsToRemoveItem, + WantsToRemoveKey, WantsToUseItem, SerializationHelper, DMSerializationHelper diff --git a/src/states/state.rs b/src/states/state.rs index 9fcea38..8ade2f4 100644 --- a/src/states/state.rs +++ b/src/states/state.rs @@ -102,12 +102,13 @@ impl State { fn resolve_entity_decisions(&mut self) { let mut trigger_system = trigger_system::TriggerSystem {}; - let mut inventory_system = inventory::ItemCollectionSystem {}; let mut item_equip_system = inventory::ItemEquipSystem {}; let mut item_use_system = inventory::ItemUseSystem {}; let mut item_drop_system = inventory::ItemDropSystem {}; let mut item_remove_system = inventory::ItemRemoveSystem {}; + let mut inventory_system = inventory::ItemCollectionSystem {}; let mut item_id_system = inventory::ItemIdentificationSystem {}; + let mut key_system = inventory::KeyHandling {}; let mut melee_system = MeleeCombatSystem {}; trigger_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_remove_system.run_now(&self.ecs); item_id_system.run_now(&self.ecs); + key_system.run_now(&self.ecs); melee_system.run_now(&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::(); + let mut intent = self.ecs.write_storage::(); + removekey + .insert(item, WantsToRemoveKey {}) + .expect("Unable to insert WantsToRemoveKey"); + intent + .insert(*self.ecs.fetch::(), WantsToDropItem { + item, + }) + .expect("Unable to insert WantsToDropItem"); + new_runstate = RunState::Ticking; + } + } + } // RunState::ShowRemoveItem // RunState::ShowTargeting // RunState::ShowRemoveCurse @@ -648,7 +672,7 @@ impl State { } } 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 { gui::ItemMenuResult::Cancel => { new_runstate = RunState::AwaitingInput;