Skip to main content

veloren_voxygen/hud/
slots.rs

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    /* Spellbook(SpellbookSlot), TODO */
33}
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}