inventory_system.rs 13 KB

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