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