inventory_system.rs 14 KB

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