inventory_system.rs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277
  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, particle_system::ParticleBuilder};
  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. WriteExpect<'a, ParticleBuilder>,
  48. ReadStorage<'a, Position>
  49. );
  50. #[allow(clippy::cognitive_complexity)]
  51. fn run(&mut self, data : Self::SystemData) {
  52. let (player_entity, mut gamelog, map, entities, mut wants_use, names,
  53. consumables, healing, inflict_damage, mut combat_stats, mut suffer_damage,
  54. aoe, mut confused, equippable, mut equipped, mut backpack, mut particle_builder, positions) = data;
  55. for (entity, useitem) in (&entities, &wants_use).join() {
  56. let mut used_item = true;
  57. // Targeting
  58. let mut targets : Vec<Entity> = Vec::new();
  59. match useitem.target {
  60. None => { targets.push( *player_entity ); }
  61. Some(target) => {
  62. let area_effect = aoe.get(useitem.item);
  63. match area_effect {
  64. None => {
  65. // Single target in tile
  66. let idx = map.xy_idx(target.x, target.y);
  67. for mob in map.tile_content[idx].iter() {
  68. targets.push(*mob);
  69. }
  70. }
  71. Some(area_effect) => {
  72. // AoE
  73. let blast_tiles = rltk::field_of_view(target, area_effect.radius, &*map);
  74. for tile_idx in blast_tiles.iter() {
  75. let idx = map.xy_idx(tile_idx.x, tile_idx.y);
  76. for mob in map.tile_content[idx].iter() {
  77. targets.push(*mob);
  78. }
  79. particle_builder.request(tile_idx.x, tile_idx.y, rltk::RGB::named(rltk::ORANGE), rltk::RGB::named(rltk::BLACK), rltk::to_cp437('░'), 200.0);
  80. }
  81. }
  82. }
  83. }
  84. }
  85. // If it is equippable, then we want to equip it - and unequip whatever else was in that slot
  86. let item_equippable = equippable.get(useitem.item);
  87. match item_equippable {
  88. None => {}
  89. Some(can_equip) => {
  90. let target_slot = can_equip.slot;
  91. let target = targets[0];
  92. // Remove any items the target has in the item's slot
  93. let mut to_unequip : Vec<Entity> = Vec::new();
  94. for (item_entity, already_equipped, name) in (&entities, &equipped, &names).join() {
  95. if already_equipped.owner == target && already_equipped.slot == target_slot {
  96. to_unequip.push(item_entity);
  97. if target == *player_entity {
  98. gamelog.entries.insert(0, format!("You unequip {}.", name.name));
  99. }
  100. }
  101. }
  102. for item in to_unequip.iter() {
  103. equipped.remove(*item);
  104. backpack.insert(*item, InBackpack{ owner: target }).expect("Unable to insert backpack entry");
  105. }
  106. // Wield the item
  107. equipped.insert(useitem.item, Equipped{ owner: target, slot: target_slot }).expect("Unable to insert equipped component");
  108. backpack.remove(useitem.item);
  109. if target == *player_entity {
  110. gamelog.entries.insert(0, format!("You equip {}.", names.get(useitem.item).unwrap().name));
  111. }
  112. }
  113. }
  114. // If it heals, apply the healing
  115. let item_heals = healing.get(useitem.item);
  116. match item_heals {
  117. None => {}
  118. Some(healer) => {
  119. used_item = false;
  120. for target in targets.iter() {
  121. let stats = combat_stats.get_mut(*target);
  122. if let Some(stats) = stats {
  123. stats.hp = i32::min(stats.max_hp, stats.hp + healer.heal_amount);
  124. if entity == *player_entity {
  125. gamelog.entries.insert(0, format!("You use the {}, healing {} hp.", names.get(useitem.item).unwrap().name, healer.heal_amount));
  126. }
  127. used_item = true;
  128. let pos = positions.get(*target);
  129. if let Some(pos) = pos {
  130. particle_builder.request(pos.x, pos.y, rltk::RGB::named(rltk::GREEN), rltk::RGB::named(rltk::BLACK), rltk::to_cp437('♥'), 200.0);
  131. }
  132. }
  133. }
  134. }
  135. }
  136. // If it inflicts damage, apply it to the target cell
  137. let item_damages = inflict_damage.get(useitem.item);
  138. match item_damages {
  139. None => {}
  140. Some(damage) => {
  141. used_item = false;
  142. for mob in targets.iter() {
  143. suffer_damage.insert(*mob, SufferDamage{ amount : damage.damage }).expect("Unable to insert");
  144. if entity == *player_entity {
  145. let mob_name = names.get(*mob).unwrap();
  146. let item_name = names.get(useitem.item).unwrap();
  147. gamelog.entries.insert(0, format!("You use {} on {}, inflicting {} hp.", item_name.name, mob_name.name, damage.damage));
  148. let pos = positions.get(*mob);
  149. if let Some(pos) = pos {
  150. particle_builder.request(pos.x, pos.y, rltk::RGB::named(rltk::RED), rltk::RGB::named(rltk::BLACK), rltk::to_cp437('‼'), 200.0);
  151. }
  152. }
  153. used_item = true;
  154. }
  155. }
  156. }
  157. // Can it pass along confusion? Note the use of scopes to escape from the borrow checker!
  158. let mut add_confusion = Vec::new();
  159. {
  160. let causes_confusion = confused.get(useitem.item);
  161. match causes_confusion {
  162. None => {}
  163. Some(confusion) => {
  164. used_item = false;
  165. for mob in targets.iter() {
  166. add_confusion.push((*mob, confusion.turns ));
  167. if entity == *player_entity {
  168. let mob_name = names.get(*mob).unwrap();
  169. let item_name = names.get(useitem.item).unwrap();
  170. gamelog.entries.insert(0, format!("You use {} on {}, confusing them.", item_name.name, mob_name.name));
  171. let pos = positions.get(*mob);
  172. if let Some(pos) = pos {
  173. particle_builder.request(pos.x, pos.y, rltk::RGB::named(rltk::MAGENTA), rltk::RGB::named(rltk::BLACK), rltk::to_cp437('?'), 200.0);
  174. }
  175. }
  176. }
  177. }
  178. }
  179. }
  180. for mob in add_confusion.iter() {
  181. confused.insert(mob.0, Confusion{ turns: mob.1 }).expect("Unable to insert status");
  182. }
  183. // If its a consumable, we delete it on use
  184. if used_item {
  185. let consumable = consumables.get(useitem.item);
  186. match consumable {
  187. None => {}
  188. Some(_) => {
  189. entities.delete(useitem.item).expect("Delete failed");
  190. }
  191. }
  192. }
  193. }
  194. wants_use.clear();
  195. }
  196. }
  197. pub struct ItemDropSystem {}
  198. impl<'a> System<'a> for ItemDropSystem {
  199. #[allow(clippy::type_complexity)]
  200. type SystemData = ( ReadExpect<'a, Entity>,
  201. WriteExpect<'a, GameLog>,
  202. Entities<'a>,
  203. WriteStorage<'a, WantsToDropItem>,
  204. ReadStorage<'a, Name>,
  205. WriteStorage<'a, Position>,
  206. WriteStorage<'a, InBackpack>
  207. );
  208. fn run(&mut self, data : Self::SystemData) {
  209. let (player_entity, mut gamelog, entities, mut wants_drop, names, mut positions, mut backpack) = data;
  210. for (entity, to_drop) in (&entities, &wants_drop).join() {
  211. let mut dropper_pos : Position = Position{x:0, y:0};
  212. {
  213. let dropped_pos = positions.get(entity).unwrap();
  214. dropper_pos.x = dropped_pos.x;
  215. dropper_pos.y = dropped_pos.y;
  216. }
  217. positions.insert(to_drop.item, Position{ x : dropper_pos.x, y : dropper_pos.y }).expect("Unable to insert position");
  218. backpack.remove(to_drop.item);
  219. if entity == *player_entity {
  220. gamelog.entries.insert(0, format!("You drop up the {}.", names.get(to_drop.item).unwrap().name));
  221. }
  222. }
  223. wants_drop.clear();
  224. }
  225. }
  226. pub struct ItemRemoveSystem {}
  227. impl<'a> System<'a> for ItemRemoveSystem {
  228. #[allow(clippy::type_complexity)]
  229. type SystemData = (
  230. Entities<'a>,
  231. WriteStorage<'a, WantsToRemoveItem>,
  232. WriteStorage<'a, Equipped>,
  233. WriteStorage<'a, InBackpack>
  234. );
  235. fn run(&mut self, data : Self::SystemData) {
  236. let (entities, mut wants_remove, mut equipped, mut backpack) = data;
  237. for (entity, to_remove) in (&entities, &wants_remove).join() {
  238. equipped.remove(to_remove.item);
  239. backpack.insert(to_remove.item, InBackpack{ owner: entity }).expect("Unable to insert backpack");
  240. }
  241. wants_remove.clear();
  242. }
  243. }