From f26adf352ec9b98b4417c2031e14a82b0dd0d186 Mon Sep 17 00:00:00 2001 From: Llywelwyn Date: Fri, 7 Jul 2023 08:37:29 +0100 Subject: [PATCH] inventory finishes --- src/components.rs | 6 ++++++ src/gui.rs | 42 ++++++++++++++++++++++++++++++++++++++++ src/inventory_system.rs | 43 ++++++++++++++++++++++++++++++++++++++++- src/main.rs | 23 +++++++++++++++++++++- src/player.rs | 1 + src/spawner.rs | 16 ++++++++++++--- 6 files changed, 126 insertions(+), 5 deletions(-) diff --git a/src/components.rs b/src/components.rs index 250d780..47bc127 100644 --- a/src/components.rs +++ b/src/components.rs @@ -13,6 +13,7 @@ pub struct Renderable { pub glyph: rltk::FontCharType, pub fg: RGB, pub bg: RGB, + pub render_order: i32, } #[derive(Component, Debug)] @@ -84,6 +85,11 @@ pub struct WantsToPickupItem { pub item: Entity, } +#[derive(Component, Debug, Clone)] +pub struct WantsToDropItem { + pub item: Entity, +} + #[derive(Component, Debug)] pub struct WantsToDrinkPotion { pub potion: Entity, diff --git a/src/gui.rs b/src/gui.rs index 8b146df..b36a127 100644 --- a/src/gui.rs +++ b/src/gui.rs @@ -159,3 +159,45 @@ pub fn show_inventory(gs: &mut State, ctx: &mut Rltk) -> (ItemMenuResult, Option }, } } + +pub fn drop_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), "Drop what?"); + ctx.print_color(18, y + count as i32 + 1, RGB::named(rltk::YELLOW), RGB::named(rltk::BLACK), "ESC 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 6832571..384a91e 100644 --- a/src/inventory_system.rs +++ b/src/inventory_system.rs @@ -1,4 +1,7 @@ -use super::{gamelog::GameLog, CombatStats, InBackpack, Name, Position, Potion, WantsToDrinkPotion, WantsToPickupItem}; +use super::{ + gamelog::GameLog, CombatStats, InBackpack, Name, Position, Potion, WantsToDrinkPotion, WantsToDropItem, + WantsToPickupItem, +}; use specs::prelude::*; pub struct ItemCollectionSystem {} @@ -66,3 +69,41 @@ impl<'a> System<'a> for PotionUseSystem { wants_drink.clear(); } } + +pub struct ItemDropSystem {} + +impl<'a> System<'a> for ItemDropSystem { + #[allow(clippy::type_complexity)] + type SystemData = ( + ReadExpect<'a, Entity>, + WriteExpect<'a, GameLog>, + Entities<'a>, + WriteStorage<'a, WantsToDropItem>, + ReadStorage<'a, Name>, + WriteStorage<'a, Position>, + WriteStorage<'a, InBackpack>, + ); + + fn run(&mut self, data: Self::SystemData) { + let (player_entity, mut gamelog, entities, mut wants_drop, names, mut positions, mut backpack) = data; + + for (entity, to_drop) in (&entities, &wants_drop).join() { + let mut dropper_pos: Position = Position { x: 0, y: 0 }; + { + let dropped_pos = positions.get(entity).unwrap(); + dropper_pos.x = dropped_pos.x; + dropper_pos.y = dropped_pos.y; + } + positions + .insert(to_drop.item, Position { x: dropper_pos.x, y: dropper_pos.y }) + .expect("Failed to insert position."); + backpack.remove(to_drop.item); + + if entity == *player_entity { + gamelog.entries.push(format!("You drop the {}.", names.get(to_drop.item).unwrap().name)); + } + } + + wants_drop.clear(); + } +} diff --git a/src/main.rs b/src/main.rs index bbc1da6..a80b99a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -37,6 +37,7 @@ pub enum RunState { PlayerTurn, MonsterTurn, ShowInventory, + ShowDropItem, } pub struct State { @@ -59,6 +60,8 @@ impl State { inventory_system.run_now(&self.ecs); let mut potion_system = PotionUseSystem {}; potion_system.run_now(&self.ecs); + let mut drop_system = ItemDropSystem {}; + drop_system.run_now(&self.ecs); self.ecs.maintain(); } } @@ -75,7 +78,9 @@ impl GameState for State { let renderables = self.ecs.read_storage::(); let map = self.ecs.fetch::(); - for (pos, render) in (&positions, &renderables).join() { + let mut data = (&positions, &renderables).join().collect::>(); + data.sort_by(|&a, &b| b.1.render_order.cmp(&a.1.render_order)); + for (pos, render) in data.iter() { let idx = map.xy_idx(pos.x, pos.y); if map.visible_tiles[idx] { ctx.set(pos.x, pos.y, render.fg, render.bg, render.glyph); @@ -124,6 +129,21 @@ impl GameState for State { } } } + 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_entity = result.1.unwrap(); + let mut intent = self.ecs.write_storage::(); + intent + .insert(*self.ecs.fetch::(), WantsToDropItem { item: item_entity }) + .expect("Unable to insert intent"); + new_runstate = RunState::PlayerTurn; + } + } + } } { @@ -159,6 +179,7 @@ fn main() -> rltk::BError { gs.ecs.register::(); gs.ecs.register::(); gs.ecs.register::(); + gs.ecs.register::(); gs.ecs.register::(); let map = Map::new_map_rooms_and_corridors(); diff --git a/src/player.rs b/src/player.rs index 58ee2ff..23d7a47 100644 --- a/src/player.rs +++ b/src/player.rs @@ -97,6 +97,7 @@ pub fn player_input(gs: &mut State, ctx: &mut Rltk) -> RunState { // Items VirtualKeyCode::G => get_item(&mut gs.ecs), VirtualKeyCode::I => return RunState::ShowInventory, + VirtualKeyCode::D => return RunState::ShowDropItem, _ => { return RunState::AwaitingInput; } diff --git a/src/spawner.rs b/src/spawner.rs index afe74c5..8e8617f 100644 --- a/src/spawner.rs +++ b/src/spawner.rs @@ -8,7 +8,12 @@ use specs::prelude::*; pub fn player(ecs: &mut World, player_x: i32, player_y: i32) -> Entity { ecs.create_entity() .with(Position { x: player_x, y: player_y }) - .with(Renderable { glyph: rltk::to_cp437('@'), fg: RGB::named(rltk::YELLOW), bg: RGB::named(rltk::BLACK) }) + .with(Renderable { + glyph: rltk::to_cp437('@'), + fg: RGB::named(rltk::YELLOW), + bg: RGB::named(rltk::BLACK), + render_order: 0, + }) .with(Player {}) .with(Viewshed { visible_tiles: Vec::new(), range: 8, dirty: true }) .with(Name { name: "hero (you)".to_string() }) @@ -35,7 +40,7 @@ const MAX_ITEMS: i32 = 2; fn monster(ecs: &mut World, x: i32, y: i32, glyph: rltk::FontCharType, name: S) { ecs.create_entity() .with(Position { x, y }) - .with(Renderable { glyph: glyph, fg: RGB::named(rltk::RED), bg: RGB::named(rltk::BLACK) }) + .with(Renderable { glyph: glyph, fg: RGB::named(rltk::RED), bg: RGB::named(rltk::BLACK), render_order: 1 }) .with(Viewshed { visible_tiles: Vec::new(), range: 8, dirty: true }) .with(Monster {}) .with(Name { name: name.to_string() }) @@ -105,7 +110,12 @@ pub fn spawn_room(ecs: &mut World, room: &Rect) { fn health_potion(ecs: &mut World, x: i32, y: i32) { ecs.create_entity() .with(Position { x, y }) - .with(Renderable { glyph: rltk::to_cp437('i'), fg: RGB::named(rltk::MAGENTA), bg: RGB::named(rltk::BLACK) }) + .with(Renderable { + glyph: rltk::to_cp437('i'), + fg: RGB::named(rltk::MAGENTA), + bg: RGB::named(rltk::BLACK), + render_order: 2, + }) .with(Name { name: "health potion".to_string() }) .with(Item {}) .with(Potion { heal_amount: 8 })