inventory_system.rs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  1. extern crate specs;
  2. use specs::prelude::*;
  3. use super::{WantsToPickupItem, Name, InBackpack, Position, gamelog::GameLog, WantsToUseItem,
  4. Consumable, ProvidesHealing, CombatStats, WantsToDropItem, InflictsDamage, Map, SufferDamage,
  5. AreaOfEffect, Confusion, Equippable, Equipped, WantsToRemoveItem};
  6. pub struct ItemCollectionSystem {}
  7. impl<'a> System<'a> for ItemCollectionSystem {
  8. #[allow(clippy::type_complexity)]
  9. type SystemData = ( ReadExpect<'a, Entity>,
  10. WriteExpect<'a, GameLog>,
  11. WriteStorage<'a, WantsToPickupItem>,
  12. WriteStorage<'a, Position>,
  13. ReadStorage<'a, Name>,
  14. WriteStorage<'a, InBackpack>
  15. );
  16. fn run(&mut self, data : Self::SystemData) {
  17. let (player_entity, mut gamelog, mut wants_pickup, mut positions, names, mut backpack) = data;
  18. for pickup in wants_pickup.join() {
  19. positions.remove(pickup.item);
  20. backpack.insert(pickup.item, InBackpack{ owner: pickup.collected_by }).expect("Unable to insert backpack entry");
  21. if pickup.collected_by == *player_entity {
  22. gamelog.entries.insert(0, format!("You pick up the {}.", names.get(pickup.item).unwrap().name));
  23. }
  24. }
  25. wants_pickup.clear();
  26. }
  27. }
  28. pub struct ItemUseSystem {}
  29. impl<'a> System<'a> for ItemUseSystem {
  30. #[allow(clippy::type_complexity)]
  31. type SystemData = ( ReadExpect<'a, Entity>,
  32. WriteExpect<'a, GameLog>,
  33. ReadExpect<'a, Map>,
  34. Entities<'a>,
  35. WriteStorage<'a, WantsToUseItem>,
  36. ReadStorage<'a, Name>,
  37. ReadStorage<'a, Consumable>,
  38. ReadStorage<'a, ProvidesHealing>,
  39. ReadStorage<'a, InflictsDamage>,
  40. WriteStorage<'a, CombatStats>,
  41. WriteStorage<'a, SufferDamage>,
  42. ReadStorage<'a, AreaOfEffect>,
  43. WriteStorage<'a, Confusion>,
  44. ReadStorage<'a, Equippable>,
  45. WriteStorage<'a, Equipped>,
  46. WriteStorage<'a, InBackpack>
  47. );
  48. #[allow(clippy::cognitive_complexity)]
  49. fn run(&mut self, data : Self::SystemData) {
  50. let (player_entity, mut gamelog, map, entities, mut wants_use, names,
  51. consumables, healing, inflict_damage, mut combat_stats, mut suffer_damage,
  52. aoe, mut confused, equippable, mut equipped, mut backpack) = data;
  53. for (entity, useitem) in (&entities, &wants_use).join() {
  54. let mut used_item = true;
  55. // Targeting
  56. let mut targets : Vec<Entity> = Vec::new();
  57. match useitem.target {
  58. None => { targets.push( *player_entity ); }
  59. Some(target) => {
  60. let area_effect = aoe.get(useitem.item);
  61. match area_effect {
  62. None => {
  63. // Single target in tile
  64. let idx = map.xy_idx(target.x, target.y);
  65. for mob in map.tile_content[idx].iter() {
  66. targets.push(*mob);
  67. }
  68. }
  69. Some(area_effect) => {
  70. // AoE
  71. let blast_tiles = rltk::field_of_view(target, area_effect.radius, &*map);
  72. for tile_idx in blast_tiles.iter() {
  73. let idx = map.xy_idx(tile_idx.x, tile_idx.y);
  74. for mob in map.tile_content[idx].iter() {
  75. targets.push(*mob);
  76. }
  77. }
  78. }
  79. }
  80. }
  81. }
  82. // If it is equippable, then we want to equip it - and unequip whatever else was in that slot
  83. let item_equippable = equippable.get(useitem.item);
  84. match item_equippable {
  85. None => {}
  86. Some(can_equip) => {
  87. let target_slot = can_equip.slot;
  88. let target = targets[0];
  89. // Remove any items the target has in the item's slot
  90. let mut to_unequip : Vec<Entity> = Vec::new();
  91. for (item_entity, already_equipped, name) in (&entities, &equipped, &names).join() {
  92. if already_equipped.owner == target && already_equipped.slot == target_slot {
  93. to_unequip.push(item_entity);
  94. if target == *player_entity {
  95. gamelog.entries.insert(0, format!("You unequip {}.", name.name));
  96. }
  97. }
  98. }
  99. for item in to_unequip.iter() {
  100. equipped.remove(*item);
  101. backpack.insert(*item, InBackpack{ owner: target }).expect("Unable to insert backpack entry");
  102. }
  103. // Wield the item
  104. equipped.insert(useitem.item, Equipped{ owner: target, slot: target_slot }).expect("Unable to insert equipped component");
  105. backpack.remove(useitem.item);
  106. if target == *player_entity {
  107. gamelog.entries.insert(0, format!("You equip {}.", names.get(useitem.item).unwrap().name));
  108. }
  109. }
  110. }
  111. // If it heals, apply the healing
  112. let item_heals = healing.get(useitem.item);
  113. match item_heals {
  114. None => {}
  115. Some(healer) => {
  116. used_item = false;
  117. for target in targets.iter() {
  118. let stats = combat_stats.get_mut(*target);
  119. if let Some(stats) = stats {
  120. stats.hp = i32::max(stats.max_hp, stats.hp + healer.heal_amount);
  121. if entity == *player_entity {
  122. gamelog.entries.insert(0, format!("You use the {}, healing {} hp.", names.get(useitem.item).unwrap().name, healer.heal_amount));
  123. }
  124. used_item = true;
  125. }
  126. }
  127. }
  128. }
  129. // If it inflicts damage, apply it to the target cell
  130. let item_damages = inflict_damage.get(useitem.item);
  131. match item_damages {
  132. None => {}
  133. Some(damage) => {
  134. used_item = false;
  135. for mob in targets.iter() {
  136. suffer_damage.insert(*mob, SufferDamage{ amount : damage.damage }).expect("Unable to insert");
  137. if entity == *player_entity {
  138. let mob_name = names.get(*mob).unwrap();
  139. let item_name = names.get(useitem.item).unwrap();
  140. gamelog.entries.insert(0, format!("You use {} on {}, inflicting {} hp.", item_name.name, mob_name.name, damage.damage));
  141. }
  142. used_item = true;
  143. }
  144. }
  145. }
  146. // Can it pass along confusion? Note the use of scopes to escape from the borrow checker!
  147. let mut add_confusion = Vec::new();
  148. {
  149. let causes_confusion = confused.get(useitem.item);
  150. match causes_confusion {
  151. None => {}
  152. Some(confusion) => {
  153. used_item = false;
  154. for mob in targets.iter() {
  155. add_confusion.push((*mob, confusion.turns ));
  156. if entity == *player_entity {
  157. let mob_name = names.get(*mob).unwrap();
  158. let item_name = names.get(useitem.item).unwrap();
  159. gamelog.entries.insert(0, format!("You use {} on {}, confusing them.", item_name.name, mob_name.name));
  160. }
  161. }
  162. }
  163. }
  164. }
  165. for mob in add_confusion.iter() {
  166. confused.insert(mob.0, Confusion{ turns: mob.1 }).expect("Unable to insert status");
  167. }
  168. // If its a consumable, we delete it on use
  169. if used_item {
  170. let consumable = consumables.get(useitem.item);
  171. match consumable {
  172. None => {}
  173. Some(_) => {
  174. entities.delete(useitem.item).expect("Delete failed");
  175. }
  176. }
  177. }
  178. }
  179. wants_use.clear();
  180. }
  181. }
  182. pub struct ItemDropSystem {}
  183. impl<'a> System<'a> for ItemDropSystem {
  184. #[allow(clippy::type_complexity)]
  185. type SystemData = ( ReadExpect<'a, Entity>,
  186. WriteExpect<'a, GameLog>,
  187. Entities<'a>,
  188. WriteStorage<'a, WantsToDropItem>,
  189. ReadStorage<'a, Name>,
  190. WriteStorage<'a, Position>,
  191. WriteStorage<'a, InBackpack>
  192. );
  193. fn run(&mut self, data : Self::SystemData) {
  194. let (player_entity, mut gamelog, entities, mut wants_drop, names, mut positions, mut backpack) = data;
  195. for (entity, to_drop) in (&entities, &wants_drop).join() {
  196. let mut dropper_pos : Position = Position{x:0, y:0};
  197. {
  198. let dropped_pos = positions.get(entity).unwrap();
  199. dropper_pos.x = dropped_pos.x;
  200. dropper_pos.y = dropped_pos.y;
  201. }
  202. positions.insert(to_drop.item, Position{ x : dropper_pos.x, y : dropper_pos.y }).expect("Unable to insert position");
  203. backpack.remove(to_drop.item);
  204. if entity == *player_entity {
  205. gamelog.entries.insert(0, format!("You drop up the {}.", names.get(to_drop.item).unwrap().name));
  206. }
  207. }
  208. wants_drop.clear();
  209. }
  210. }
  211. pub struct ItemRemoveSystem {}
  212. impl<'a> System<'a> for ItemRemoveSystem {
  213. #[allow(clippy::type_complexity)]
  214. type SystemData = (
  215. Entities<'a>,
  216. WriteStorage<'a, WantsToRemoveItem>,
  217. WriteStorage<'a, Equipped>,
  218. WriteStorage<'a, InBackpack>
  219. );
  220. fn run(&mut self, data : Self::SystemData) {
  221. let (entities, mut wants_remove, mut equipped, mut backpack) = data;
  222. for (entity, to_remove) in (&entities, &wants_remove).join() {
  223. equipped.remove(to_remove.item);
  224. backpack.insert(to_remove.item, InBackpack{ owner: entity }).expect("Unable to insert backpack");
  225. }
  226. wants_remove.clear();
  227. }
  228. }