diff --git a/src/components.rs b/src/components.rs index 3f774f7..dc14ee4 100644 --- a/src/components.rs +++ b/src/components.rs @@ -148,3 +148,20 @@ pub struct SerializeMe; pub struct SerializationHelper { pub map: crate::map::Map, } + +#[derive(PartialEq, Copy, Clone, Serialize, Deserialize)] +pub enum EquipmentSlot { + Melee, + Shield, +} + +#[derive(Component, Serialize, Deserialize, Clone)] +pub struct Equippable { + pub slot: EquipmentSlot, +} + +#[derive(Component, ConvertSaveload, Clone)] +pub struct Equipped { + pub owner: Entity, + pub slot: EquipmentSlot, +} diff --git a/src/inventory_system.rs b/src/inventory_system.rs index 4546b67..d0d6c0d 100644 --- a/src/inventory_system.rs +++ b/src/inventory_system.rs @@ -1,8 +1,5 @@ -use crate::{ - game_log::GameLog, AreaOfEffect, CombatStats, Confusion, Consumable, InBackpack, - InflictsDamage, Map, Name, Position, ProvidesHealing, SufferDamage, WantsToDropItem, - WantsToPickupItem, WantsToUseItem, -}; +use crate::components::*; +use crate::{game_log::GameLog, Map}; use specs::prelude::*; pub struct ItemCollectionSystem {} @@ -63,6 +60,9 @@ impl<'a> System<'a> for ItemUseSystem { WriteStorage<'a, SufferDamage>, ReadStorage<'a, AreaOfEffect>, WriteStorage<'a, Confusion>, + ReadStorage<'a, Equippable>, + WriteStorage<'a, Equipped>, + WriteStorage<'a, InBackpack>, ); fn run(&mut self, data: Self::SystemData) { @@ -80,6 +80,9 @@ impl<'a> System<'a> for ItemUseSystem { mut suffer_damage, aoe, mut confused, + equippable, + mut equipped, + mut backpack, ) = data; for (entity, useitem) in (&entities, &wants_use).join() { @@ -119,6 +122,53 @@ impl<'a> System<'a> for ItemUseSystem { } } + // If it is equippable, then we want to equip it - and unequip whatever else was in that slot + match equippable.get(useitem.item) { + None => {} + Some(can_equip) => { + let target_slot = can_equip.slot; + let target = targets[0]; + + // Remove any items the target has in the item's slot + let mut to_unequip: Vec = Vec::new(); + for (item_entity, already_equipped, name) in + (&entities, &equipped, &names).join() + { + if already_equipped.owner == target && already_equipped.slot == target_slot + { + to_unequip.push(item_entity); + if target == *player_entity { + gamelog.entries.push(format!("You unequip {}.", name.name)); + } + } + } + for item in to_unequip.iter() { + equipped.remove(*item); + backpack + .insert(*item, InBackpack { owner: target }) + .expect("Unable to put unequipped item back in backpack"); + } + + // Wield the item + equipped + .insert( + useitem.item, + Equipped { + owner: target, + slot: target_slot, + }, + ) + .expect("Failed to equip item"); + backpack.remove(useitem.item); + if target == *player_entity { + gamelog.entries.push(format!( + "You equip {}.", + names.get(useitem.item).unwrap().name + )); + } + } + } + // If the item heals, apply the healing match healing.get(useitem.item) { None => {} diff --git a/src/main.rs b/src/main.rs index 5ba1e39..4b10a4a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -275,6 +275,7 @@ impl State { let player = self.ecs.read_storage::(); let backpack = self.ecs.read_storage::(); let player_entity = self.ecs.fetch::(); + let equipped = self.ecs.read_storage::(); let mut to_delete: Vec = Vec::new(); for entity in entities.join() { @@ -294,6 +295,13 @@ impl State { } } + let eq = equipped.get(entity); + if let Some(eq) = eq { + if eq.owner == *player_entity { + should_delete = false; + } + } + if should_delete { to_delete.push(entity); } @@ -393,6 +401,7 @@ fn main() -> rltk::BError { Confusion, SimpleMarker, SerializationHelper, + Equippable, ); gs.ecs.insert(SimpleMarkerAllocator::::new()); diff --git a/src/saveload_system.rs b/src/saveload_system.rs index 2f51fab..21f64af 100644 --- a/src/saveload_system.rs +++ b/src/saveload_system.rs @@ -84,7 +84,8 @@ pub fn save_game(ecs: &mut World) { WantsToPickupItem, WantsToUseItem, WantsToDropItem, - SerializationHelper + SerializationHelper, + Equippable ); } @@ -145,7 +146,8 @@ pub fn load_game(ecs: &mut World) { WantsToPickupItem, WantsToUseItem, WantsToDropItem, - SerializationHelper + SerializationHelper, + Equippable ); } diff --git a/src/spawner.rs b/src/spawner.rs index 995eac1..7eab31c 100644 --- a/src/spawner.rs +++ b/src/spawner.rs @@ -1,6 +1,7 @@ use crate::components::{ - AreaOfEffect, BlocksTile, CombatStats, Confusion, Consumable, InflictsDamage, Item, Monster, - Name, Player, Position, ProvidesHealing, Ranged, Renderable, SerializeMe, Viewshed, + AreaOfEffect, BlocksTile, CombatStats, Confusion, Consumable, EquipmentSlot, Equippable, + InflictsDamage, Item, Monster, Name, Player, Position, ProvidesHealing, Ranged, Renderable, + SerializeMe, Viewshed, }; use crate::{random_table::RandomTable, Rect, MAP_WIDTH}; use rltk::{RandomNumberGenerator, RGB}; @@ -44,6 +45,8 @@ fn room_table(map_depth: i32) -> RandomTable { .add("Fireball Scroll", 2 + map_depth) .add("Confusion Scroll", 2 + map_depth) .add("Magic Missile Scroll", 4) + .add("Dagger", 3) + .add("Shield", 3) } /// fills a room with stuff! @@ -88,6 +91,8 @@ pub fn spawn_room(ecs: &mut World, room: &Rect, map_depth: i32) { "Fireball Scroll" => fireball_scroll(ecs, x, y), "Confusion Scroll" => confusion_scroll(ecs, x, y), "Magic Missile Scroll" => magic_missile_scroll(ecs, x, y), + "Dagger" => dagger(ecs, x, y), + "Shield" => shield(ecs, x, y), _ => {} } } @@ -194,3 +199,39 @@ fn confusion_scroll(ecs: &mut World, x: i32, y: i32) { .marked::>() .build(); } + +fn dagger(ecs: &mut World, x: i32, y: i32) { + ecs.create_entity() + .with(Position { x, y }) + .with(Renderable { + glyph: rltk::to_cp437('/'), + fg: RGB::named(rltk::CYAN), + bg: RGB::named(rltk::BLACK), + render_order: 2, + }) + .with(Name::new("Dagger")) + .with(Item {}) + .with(Equippable { + slot: EquipmentSlot::Melee, + }) + .marked::>() + .build(); +} + +fn shield(ecs: &mut World, x: i32, y: i32) { + ecs.create_entity() + .with(Position { x, y }) + .with(Renderable { + glyph: rltk::to_cp437('('), + fg: RGB::named(rltk::CYAN), + bg: RGB::named(rltk::BLACK), + render_order: 2, + }) + .with(Name::new("Shield")) + .with(Item {}) + .with(Equippable { + slot: EquipmentSlot::Shield, + }) + .marked::>() + .build(); +}