use specs::prelude::*; use crate::gamelog::GameLog; use crate::particle_system::ParticleBuilder; use crate::{ AreaOfEffect, CombatStats, Confusion, Consumable, Equippable, Equipped, HungerClock, HungerState, InBackpack, InflictsDamage, MagicMapper, Map, Name, Position, ProvidesFood, ProvidesHealing, Ranged, RunState, SufferDamage, WantsToDropItem, WantsToPickupItem, WantsToRemoveItem, WantsToUseItem, }; pub struct ItemCollectionSystem {} impl<'a> System<'a> for ItemCollectionSystem { #[allow(clippy::complexity)] type SystemData = ( ReadExpect<'a, Entity>, WriteExpect<'a, GameLog>, WriteStorage<'a, WantsToPickupItem>, WriteStorage<'a, Position>, ReadStorage<'a, Name>, WriteStorage<'a, InBackpack>, ); fn run(&mut self, data: Self::SystemData) { let (player_entity, mut gamelog, mut wants_pickup, mut positions, names, mut backpack) = data; for pickup in wants_pickup.join() { positions.remove(pickup.item); backpack .insert( pickup.item, InBackpack { owner: pickup.collected_by, }, ) .expect("Unable to insert backpack entry"); if pickup.collected_by == *player_entity { gamelog.entries.push(format!( "You pick up the {}.", names.get(pickup.item).unwrap().name )) } } wants_pickup.clear(); } } pub struct ItemUseSystem {} impl<'a> System<'a> for ItemUseSystem { #[allow(clippy::complexity)] type SystemData = ( ReadExpect<'a, Entity>, WriteExpect<'a, GameLog>, Entities<'a>, WriteStorage<'a, WantsToUseItem>, ReadStorage<'a, Name>, ReadStorage<'a, Consumable>, WriteStorage<'a, CombatStats>, ReadStorage<'a, ProvidesHealing>, ReadStorage<'a, InflictsDamage>, ReadStorage<'a, Ranged>, WriteExpect<'a, Map>, WriteStorage<'a, SufferDamage>, ReadStorage<'a, AreaOfEffect>, WriteStorage<'a, Confusion>, ReadStorage<'a, Equippable>, WriteStorage<'a, Equipped>, WriteStorage<'a, InBackpack>, WriteExpect<'a, ParticleBuilder>, ReadStorage<'a, Position>, ReadStorage<'a, ProvidesFood>, WriteStorage<'a, HungerClock>, ReadStorage<'a, MagicMapper>, WriteExpect<'a, RunState>, ); fn run(&mut self, data: Self::SystemData) { let ( player_entity, mut game_log, entities, mut wants_use, names, consumables, mut combat_stats, healing, inflicts_damage, _ranged, map, mut suffer_damage, aoe, mut confused, equippable, mut equipped, mut backpack, mut particle_builder, positions, provides_food, mut hunger_clocks, magic_mapper, mut run_state, ) = data; for (entity, use_item) in (&entities, &wants_use).join() { let mut used_item = true; let mut targets: Vec = Vec::new(); match use_item.target { None => targets.push(*player_entity), Some(target) => { let area_effect = aoe.get(use_item.item); match area_effect { None => { let idx = map.xy_idx(target.x, target.y); for mob in map.tile_content[idx].iter() { targets.push(*mob); } } Some(area_effect) => { let mut black_tiles = rltk::field_of_view(target, area_effect.radius, &*map); black_tiles.retain(|p| { p.x > 0 && p.x < map.width - 1 && p.y > 0 && p.y < map.height - 1 }); for tile_idx in black_tiles.iter() { let idx = map.xy_idx(tile_idx.x, tile_idx.y); for mob in map.tile_content[idx].iter() { targets.push(*mob); } particle_builder.request( tile_idx.x, tile_idx.y, rltk::RGB::named(rltk::ORANGE), rltk::RGB::named(rltk::BLACK), rltk::to_cp437('░'), 200., ); } } } } } if let Some(_mapper) = magic_mapper.get(use_item.item) { used_item = true; game_log .entries .push("The map is revealed to you!".to_string()); *run_state = RunState::MagicMapReveal { row: 0 }; } if let Some(_item_edible) = provides_food.get(use_item.item) { used_item = true; let target = targets[0]; if let Some(hc) = hunger_clocks.get_mut(target) { hc.state = HungerState::WellFed; hc.duration = 20; game_log.entries.push(format!( "You eat the {}.", names.get(use_item.item).unwrap().name )); } } if let Some(item_equippable) = equippable.get(use_item.item) { let target_slot = item_equippable.slot; let target = targets[0]; 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 { game_log.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 insert item into backpack"); } equipped .insert( use_item.item, Equipped { owner: target, slot: target_slot, }, ) .expect("Unable to equip item"); backpack.remove(use_item.item); if target == *player_entity { game_log.entries.push(format!( "You equip item {}.", names.get(use_item.item).unwrap().name )) } } if let Some(item_damages) = inflicts_damage.get(use_item.item) { used_item = false; for mob in targets.iter() { SufferDamage::new_damage(&mut suffer_damage, *mob, item_damages.damage); if entity == *player_entity { let mob_name = names.get(*mob).unwrap(); let item_name = names.get(use_item.item).unwrap(); game_log.entries.push(format!( "You use {} on {}, inflicting {} hp.", item_name.name, mob_name.name, item_damages.damage )); if let Some(pos) = positions.get(*mob) { particle_builder.request( pos.x, pos.y, rltk::RGB::named(rltk::RED), rltk::RGB::named(rltk::BLACK), rltk::to_cp437('‼'), 200., ); } } used_item = true; } } if let Some(item_heals) = healing.get(use_item.item) { used_item = false; for target in targets.iter() { let stats = combat_stats.get_mut(*target); if let Some(stats) = stats { stats.hp = i32::min(stats.max_hp, stats.hp + item_heals.heal_amount); if entity == *player_entity { game_log.entries.push(format!( "You drink the {}, healing {} hp.", names.get(use_item.item).unwrap().name, item_heals.heal_amount )); } used_item = true; if let Some(pos) = positions.get(*target) { particle_builder.request( pos.x, pos.y, rltk::RGB::named(rltk::GREEN), rltk::RGB::named(rltk::BLACK), rltk::to_cp437('♥'), 200., ); } } } } let mut add_confusion = Vec::new(); { if let Some(caused_confusion) = confused.get(use_item.item) { used_item = false; for mob in targets.iter() { add_confusion.push((*mob, caused_confusion.turns)); if entity == *player_entity { let mob_name = names.get(*mob).unwrap(); let item_name = names.get(use_item.item).unwrap(); game_log.entries.push(format!( "You use {} on {}, confusing them.", item_name.name, mob_name.name )); if let Some(pos) = positions.get(*mob) { particle_builder.request( pos.x, pos.y, rltk::RGB::named(rltk::MAGENTA), rltk::RGB::named(rltk::BLACK), rltk::to_cp437('?'), 200., ); } } } } } for mob in add_confusion.iter() { confused .insert(mob.0, Confusion { turns: mob.1 }) .expect("Unable to insert status"); } if used_item { let consumable = consumables.get(use_item.item); match consumable { None => {} Some(_) => { entities.delete(use_item.item).expect("Delete failed"); } } } } wants_use.clear(); } } pub struct ItemDropSystem {} impl<'a> System<'a> for ItemDropSystem { 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 game_log, 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("Unable to insert position"); backpack.remove(to_drop.item); if entity == *player_entity { game_log.entries.push(format!( "You drop the {}.", names.get(to_drop.item).unwrap().name )); } } 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_to_remove_item, mut equipped, mut backpack) = data; for (entity, to_remove) in (&entities, &wants_to_remove_item).join() { equipped.remove(to_remove.item); backpack .insert(to_remove.item, InBackpack { owner: entity }) .expect("Unable to insert item into backpack"); } wants_to_remove_item.clear(); } }