diff --git a/src/components.rs b/src/components.rs index 84afd8a..c5e31e0 100644 --- a/src/components.rs +++ b/src/components.rs @@ -175,3 +175,8 @@ pub struct MeleePowerBonus { pub struct DefenseBonus { pub defense: i32, } + +#[derive(Component, Debug, ConvertSaveload, Clone)] +pub struct WantsToRemoveItem { + pub item: Entity, +} diff --git a/src/gui.rs b/src/gui.rs index 563abab..746440f 100644 --- a/src/gui.rs +++ b/src/gui.rs @@ -1,5 +1,5 @@ use crate::components::{CombatStats, InBackpack, Name, Player, Position, Viewshed}; -use crate::{game_log::GameLog, Map, RunState, State}; +use crate::{game_log::GameLog, Equipped, Map, RunState, State}; use rltk::{Point, Rltk, VirtualKeyCode, RGB}; use specs::prelude::*; @@ -530,3 +530,92 @@ pub fn main_menu(gs: &mut State, ctx: &mut Rltk) -> MainMenuResult { selected: MainMenuSelection::NewGame, } } + +pub fn remove_item_menu(gs: &mut State, ctx: &mut Rltk) -> (ItemMenuResult, Option) { + let player_entity = gs.ecs.fetch::(); + let names = gs.ecs.read_storage::(); + let backpack = gs.ecs.read_storage::(); + let entities = gs.ecs.entities(); + + let inventory = (&backpack, &names) + .join() + .filter(|item| item.0.owner == *player_entity); + let count = inventory.count(); + + let mut y = (25 - (count / 2)) as i32; + ctx.draw_box( + 15, + y - 2, + 31, + (count + 3) as i32, + RGB::named(rltk::WHITE), + RGB::named(rltk::BLACK), + ); + ctx.print_color( + 18, + y - 2, + RGB::named(rltk::YELLOW), + RGB::named(rltk::BLACK), + "Remove Which Item?", + ); + ctx.print_color( + 18, + y + count as i32 + 1, + RGB::named(rltk::YELLOW), + RGB::named(rltk::BLACK), + "ESCAPE to cancel", + ); + + let mut equippable: Vec = Vec::new(); + let mut j = 0; + for (entity, _pack, name) in (&entities, &backpack, &names) + .join() + .filter(|item| item.1.owner == *player_entity) + { + ctx.set( + 17, + y, + RGB::named(rltk::WHITE), + RGB::named(rltk::BLACK), + rltk::to_cp437('('), + ); + ctx.set( + 18, + y, + RGB::named(rltk::YELLOW), + RGB::named(rltk::BLACK), + 97 + j as rltk::FontCharType, + ); + ctx.set( + 19, + y, + RGB::named(rltk::WHITE), + RGB::named(rltk::BLACK), + rltk::to_cp437(')'), + ); + + ctx.print(21, y, &name.name.to_string()); + + equippable.push(entity); + y += 1; + j += 1; + } + + match ctx.key { + None => (ItemMenuResult::NoResponse, None), + Some(key) => match key { + VirtualKeyCode::Escape => (ItemMenuResult::Cancel, None), + _ => { + let selection = rltk::letter_to_option(key); + if selection > -1 && selection < count as i32 { + return ( + ItemMenuResult::Selected, + Some(equippable[selection as usize]), + ); + } + + (ItemMenuResult::NoResponse, None) + } + }, + } +} diff --git a/src/inventory_system.rs b/src/inventory_system.rs index d0d6c0d..e19410d 100644 --- a/src/inventory_system.rs +++ b/src/inventory_system.rs @@ -319,3 +319,26 @@ impl<'a> System<'a> for ItemDropSystem { wants_drop.clear(); } } + +pub struct ItemRemoveSystem {} + +impl<'a> System<'a> for ItemRemoveSystem { + type SystemData = ( + Entities<'a>, + WriteStorage<'a, WantsToRemoveItem>, + WriteStorage<'a, Equipped>, + WriteStorage<'a, InBackpack> + ); + + fn run(&mut self, data: Self::SystemData) { + let (entities, mut wants_remove, mut equipped, mut backpack) = data; + + for (entity, to_remove) in (&entities, &wants_remove).join() { + equipped.remove(to_remove.item); + backpack.insert(to_remove.item, InBackpack { owner: entity}) + .expect("Unable to remove item and put in backpack"); + } + + wants_remove.clear(); + } +} diff --git a/src/main.rs b/src/main.rs index ff48ea6..7584b6c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -30,6 +30,7 @@ use monster_ai_system::MonsterAI; use player::*; pub use rect::Rect; use visibility_system::VisibilitySystem; +use crate::inventory_system::ItemRemoveSystem; /// Cut down on the amount of syntax to register components macro_rules! register { @@ -61,6 +62,7 @@ pub enum RunState { }, SaveGame, NextLevel, + ShowRemoveItem, } pub struct State { @@ -97,6 +99,9 @@ impl State { let mut drop_items = ItemDropSystem {}; drop_items.run_now(&self.ecs); + let mut item_remove = ItemRemoveSystem {}; + item_remove.run_now(&self.ecs); + self.ecs.maintain(); } } @@ -258,6 +263,25 @@ impl GameState for State { self.goto_next_level(); newrunstate = RunState::PreRun; } + RunState::ShowRemoveItem => { + let result = gui::remove_item_menu(self, ctx); + match result.0 { + gui::ItemMenuResult::Cancel => newrunstate = RunState::AwaitingInput, + gui::ItemMenuResult::NoResponse => {} + gui::ItemMenuResult::Selected => { + let item_entity = result.1.unwrap(); + let mut intent = self.ecs.write_storage::(); + intent + .insert( + *self.ecs.fetch::(), + WantsToRemoveItem { item: item_entity }, + ) + .expect("Unable to insert intent to remove item"); + + newrunstate = RunState::PlayerTurn; + } + } + } } { @@ -405,6 +429,7 @@ fn main() -> rltk::BError { Equipped, MeleePowerBonus, DefenseBonus, + WantsToRemoveItem, ); gs.ecs.insert(SimpleMarkerAllocator::::new()); diff --git a/src/player.rs b/src/player.rs index 6de1a64..c720b4b 100644 --- a/src/player.rs +++ b/src/player.rs @@ -117,6 +117,9 @@ pub fn player_input(gs: &mut State, ctx: &mut Rltk) -> RunState { // Skip Turn VirtualKeyCode::Numpad5 | VirtualKeyCode::Space => return skip_turn(&mut gs.ecs), + // Remove item + VirtualKeyCode::R => return RunState::ShowRemoveItem, + _ => return RunState::AwaitingInput, }, } diff --git a/src/saveload_system.rs b/src/saveload_system.rs index 21f64af..cf12730 100644 --- a/src/saveload_system.rs +++ b/src/saveload_system.rs @@ -85,7 +85,11 @@ pub fn save_game(ecs: &mut World) { WantsToUseItem, WantsToDropItem, SerializationHelper, - Equippable + Equippable, + Equipped, + MeleePowerBonus, + DefenseBonus, + WantsToRemoveItem ); } @@ -147,7 +151,11 @@ pub fn load_game(ecs: &mut World) { WantsToUseItem, WantsToDropItem, SerializationHelper, - Equippable + Equippable, + Equipped, + MeleePowerBonus, + DefenseBonus, + WantsToRemoveItem ); }