1use super::{
2 hotbar::{self, Slot as HotbarSlot},
3 img_ids,
4 item_imgs::ItemImgs,
5 util,
6};
7use crate::ui::slot::{self, SlotKey, SumSlot};
8use common::{
9 comp::{
10 ActiveAbilities, Body, Buffs, CharacterState, Combo, Energy, Inventory, Item, ItemKey,
11 SkillSet, Stance, Stats,
12 ability::{Ability, AbilityInput, AuxiliaryAbility},
13 item::tool::ToolKind,
14 slot::{InvSlotId, Slot},
15 },
16 recipe::ComponentRecipeBook,
17};
18use conrod_core::{Color, image};
19use specs::Entity as EcsEntity;
20use std::fmt::{Debug, Formatter};
21
22pub use common::comp::slot::{ArmorSlot, EquipSlot};
23
24#[derive(Clone, Copy, Debug, PartialEq)]
25pub enum SlotKind {
26 Inventory(InventorySlot),
27 Equip(EquipSlot),
28 Hotbar(HotbarSlot),
29 Trade(TradeSlot),
30 Ability(AbilitySlot),
31 Crafting(CraftSlot),
32 }
34
35pub type SlotManager = slot::SlotManager<SlotKind>;
36
37#[derive(Clone, Copy, Debug, PartialEq, Eq)]
38pub struct InventorySlot {
39 pub slot: Slot,
40 pub entity: EcsEntity,
41 pub ours: bool,
42}
43
44impl SlotKey<Inventory, ItemImgs> for InventorySlot {
45 type ImageKey = ItemKey;
46
47 fn image_key(&self, source: &Inventory) -> Option<(Self::ImageKey, Option<Color>)> {
48 source.get_slot(self.slot).map(|i| (i.into(), None))
49 }
50
51 fn amount(&self, source: &Inventory) -> Option<u32> {
52 source
53 .get_slot(self.slot)
54 .map(|item| item.amount())
55 .filter(|amount| *amount > 1)
56 }
57
58 fn image_ids(key: &Self::ImageKey, source: &ItemImgs) -> Vec<image::Id> {
59 source.img_ids_or_not_found_img(key.clone())
60 }
61}
62
63impl SlotKey<Inventory, ItemImgs> for EquipSlot {
64 type ImageKey = ItemKey;
65
66 fn image_key(&self, source: &Inventory) -> Option<(Self::ImageKey, Option<Color>)> {
67 let item = source.equipped(*self);
68 item.map(|i| (i.into(), None))
69 }
70
71 fn amount(&self, _: &Inventory) -> Option<u32> { None }
72
73 fn image_ids(key: &Self::ImageKey, source: &ItemImgs) -> Vec<image::Id> {
74 source.img_ids_or_not_found_img(key.clone())
75 }
76}
77
78#[derive(Clone, Copy, Debug, PartialEq, Eq)]
79pub struct TradeSlot {
80 pub index: usize,
81 pub quantity: u32,
82 pub invslot: Option<InvSlotId>,
83 pub entity: EcsEntity,
84 pub ours: bool,
85}
86
87impl SlotKey<Inventory, ItemImgs> for TradeSlot {
88 type ImageKey = ItemKey;
89
90 fn image_key(&self, source: &Inventory) -> Option<(Self::ImageKey, Option<Color>)> {
91 self.invslot.and_then(|inv_id| {
92 InventorySlot {
93 slot: Slot::Inventory(inv_id),
94 ours: self.ours,
95 entity: self.entity,
96 }
97 .image_key(source)
98 })
99 }
100
101 fn amount(&self, source: &Inventory) -> Option<u32> {
102 self.invslot
103 .and_then(|inv_id| {
104 InventorySlot {
105 slot: Slot::Inventory(inv_id),
106 ours: self.ours,
107 entity: self.entity,
108 }
109 .amount(source)
110 })
111 .map(|x| x.min(self.quantity))
112 }
113
114 fn image_ids(key: &Self::ImageKey, source: &ItemImgs) -> Vec<image::Id> {
115 source.img_ids_or_not_found_img(key.clone())
116 }
117}
118
119#[derive(Clone, PartialEq, Eq)]
120pub enum HotbarImage {
121 Item(ItemKey),
122 Ability(String),
123}
124
125type HotbarSource<'a> = (
126 &'a hotbar::State,
127 &'a Inventory,
128 &'a Energy,
129 &'a SkillSet,
130 Option<&'a ActiveAbilities>,
131 &'a Body,
132 Option<&'a Combo>,
133 Option<&'a CharacterState>,
134 Option<&'a Stance>,
135 Option<&'a Stats>,
136 Option<&'a Buffs>,
137);
138type HotbarImageSource<'a> = (&'a ItemImgs, &'a img_ids::Imgs);
139
140impl<'a> SlotKey<HotbarSource<'a>, HotbarImageSource<'a>> for HotbarSlot {
141 type ImageKey = HotbarImage;
142
143 fn image_key(
144 &self,
145 (
146 hotbar,
147 inventory,
148 energy,
149 skillset,
150 active_abilities,
151 body,
152 combo,
153 char_state,
154 stance,
155 stats,
156 buffs,
157 ): &HotbarSource<'a>,
158 ) -> Option<(Self::ImageKey, Option<Color>)> {
159 const GREYED_OUT: Color = Color::Rgba(0.3, 0.3, 0.3, 0.8);
160 hotbar.get(*self).and_then(|contents| match contents {
161 hotbar::SlotContents::Inventory(item_hash, item_key) => {
162 let item = inventory.get_by_hash(item_hash);
163 match item {
164 Some(item) => Some((HotbarImage::Item(item.into()), None)),
165 None => Some((HotbarImage::Item(item_key), Some(GREYED_OUT))),
166 }
167 },
168 hotbar::SlotContents::Ability(i) => {
169 let ability_id = active_abilities.and_then(|a| {
170 a.auxiliary_set(Some(inventory), Some(skillset))
171 .get(i)
172 .and_then(|a| {
173 Ability::from(*a).ability_id(
174 *char_state,
175 Some(inventory),
176 Some(skillset),
177 *stance,
178 *combo,
179 *buffs,
180 )
181 })
182 });
183
184 ability_id
185 .map(|id| HotbarImage::Ability(id.to_string()))
186 .and_then(|image| {
187 active_abilities
188 .and_then(|a| {
189 a.activate_ability(
190 AbilityInput::Auxiliary(i),
191 Some(inventory),
192 skillset,
193 Some(body),
194 *char_state,
195 *stance,
196 *combo,
197 *stats,
198 *buffs,
199 )
200 })
201 .map(|(ability, _, _)| {
202 (
203 image,
204 if energy.current() >= ability.energy_cost()
205 && combo
206 .is_some_and(|c| c.counter() >= ability.combo_cost())
207 && ability
208 .ability_meta()
209 .requirements
210 .requirements_met(*stance, Some(*inventory))
211 {
212 Some(Color::Rgba(1.0, 1.0, 1.0, 1.0))
213 } else {
214 Some(GREYED_OUT)
215 },
216 )
217 })
218 })
219 },
220 })
221 }
222
223 fn amount(&self, (hotbar, inventory, ..): &HotbarSource<'a>) -> Option<u32> {
224 hotbar
225 .get(*self)
226 .and_then(|content| match content {
227 hotbar::SlotContents::Inventory(item_hash, _) => inventory.get_by_hash(item_hash),
228 hotbar::SlotContents::Ability(_) => None,
229 })
230 .map(|item| item.amount())
231 .filter(|amount| *amount > 1)
232 }
233
234 fn image_ids(
235 key: &Self::ImageKey,
236 (item_imgs, imgs): &HotbarImageSource<'a>,
237 ) -> Vec<image::Id> {
238 match key {
239 HotbarImage::Item(key) => item_imgs.img_ids_or_not_found_img(key.clone()),
240 HotbarImage::Ability(ability_id) => vec![util::ability_image(imgs, ability_id)],
241 }
242 }
243}
244
245#[derive(Clone, Copy, Debug, PartialEq, Eq)]
246pub enum AbilitySlot {
247 Slot(usize),
248 Ability(AuxiliaryAbility),
249}
250
251type AbilitiesSource<'a> = (
252 &'a ActiveAbilities,
253 &'a Inventory,
254 &'a SkillSet,
255 Option<&'a Stance>,
256 Option<&'a Combo>,
257 Option<&'a CharacterState>,
258 Option<&'a Stats>,
259 Option<&'a Buffs>,
260);
261
262impl<'a> SlotKey<AbilitiesSource<'a>, img_ids::Imgs> for AbilitySlot {
263 type ImageKey = String;
264
265 fn image_key(
266 &self,
267 (active_abilities, inventory, skillset, stance, combo, char_state, stats, buffs): &AbilitiesSource<
268 'a,
269 >,
270 ) -> Option<(Self::ImageKey, Option<Color>)> {
271 let ability_id = match self {
272 Self::Slot(index) => active_abilities
273 .get_ability(
274 AbilityInput::Auxiliary(*index),
275 Some(inventory),
276 Some(skillset),
277 *stats,
278 )
279 .ability_id(
280 *char_state,
281 Some(inventory),
282 Some(skillset),
283 *stance,
284 *combo,
285 *buffs,
286 ),
287 Self::Ability(ability) => Ability::from(*ability).ability_id(
288 *char_state,
289 Some(inventory),
290 Some(skillset),
291 *stance,
292 *combo,
293 *buffs,
294 ),
295 };
296
297 ability_id.map(|id| (String::from(id), None))
298 }
299
300 fn amount(&self, _source: &AbilitiesSource) -> Option<u32> { None }
301
302 fn image_ids(ability_id: &Self::ImageKey, imgs: &img_ids::Imgs) -> Vec<image::Id> {
303 vec![util::ability_image(imgs, ability_id)]
304 }
305}
306
307#[derive(Clone, Copy)]
308pub struct CraftSlot {
309 pub index: u32,
310 pub slot: Option<Slot>,
311 pub requirement: fn(&Item, &ComponentRecipeBook, Option<CraftSlotInfo>) -> bool,
312 pub info: Option<CraftSlotInfo>,
313}
314
315impl CraftSlot {
316 pub fn item<'a>(&'a self, inv: &'a Inventory) -> Option<&'a Item> {
317 match self.slot {
318 Some(Slot::Inventory(slot)) => inv.get(slot),
319 Some(Slot::Equip(slot)) => inv.equipped(slot),
320 Some(Slot::Overflow(_)) => None,
321 None => None,
322 }
323 }
324}
325
326#[derive(Clone, Copy, Debug)]
327pub enum CraftSlotInfo {
328 Tool(ToolKind),
329}
330
331impl PartialEq for CraftSlot {
332 fn eq(&self, other: &Self) -> bool { (self.index, self.slot) == (other.index, other.slot) }
333}
334
335impl Debug for CraftSlot {
336 fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
337 f.debug_struct("CraftSlot")
338 .field("index", &self.index)
339 .field("slot", &self.slot)
340 .field("requirement", &"fn ptr")
341 .finish()
342 }
343}
344
345impl SlotKey<Inventory, ItemImgs> for CraftSlot {
346 type ImageKey = ItemKey;
347
348 fn image_key(&self, source: &Inventory) -> Option<(Self::ImageKey, Option<Color>)> {
349 self.item(source).map(|i| (i.into(), None))
350 }
351
352 fn amount(&self, source: &Inventory) -> Option<u32> {
353 self.item(source)
354 .map(|item| item.amount())
355 .filter(|amount| *amount > 1)
356 }
357
358 fn image_ids(key: &Self::ImageKey, source: &ItemImgs) -> Vec<image::Id> {
359 source.img_ids_or_not_found_img(key.clone())
360 }
361}
362
363impl From<InventorySlot> for SlotKind {
364 fn from(inventory: InventorySlot) -> Self { Self::Inventory(inventory) }
365}
366
367impl From<EquipSlot> for SlotKind {
368 fn from(equip: EquipSlot) -> Self { Self::Equip(equip) }
369}
370
371impl From<HotbarSlot> for SlotKind {
372 fn from(hotbar: HotbarSlot) -> Self { Self::Hotbar(hotbar) }
373}
374
375impl From<TradeSlot> for SlotKind {
376 fn from(trade: TradeSlot) -> Self { Self::Trade(trade) }
377}
378
379impl From<AbilitySlot> for SlotKind {
380 fn from(ability: AbilitySlot) -> Self { Self::Ability(ability) }
381}
382
383impl From<CraftSlot> for SlotKind {
384 fn from(craft: CraftSlot) -> Self { Self::Crafting(craft) }
385}
386
387impl SumSlot for SlotKind {
388 fn drag_size(&self) -> Option<[f64; 2]> {
389 Some(match self {
390 Self::Ability(_) => [80.0; 2],
391 _ => return None,
392 })
393 }
394}