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, CharacterState, Combo, Energy, Inventory, Item, ItemKey, SkillSet,
11        Stance, Stats,
12        ability::{Ability, AbilityInput, AuxiliaryAbility},
13        item::tool::{AbilityContext, 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    &'a AbilityContext,
133    Option<&'a Combo>,
134    Option<&'a CharacterState>,
135    Option<&'a Stance>,
136    Option<&'a Stats>,
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            contexts,
153            combo,
154            char_state,
155            stance,
156            stats,
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                                contexts,
178                            )
179                        })
180                });
181
182                ability_id
183                    .map(|id| HotbarImage::Ability(id.to_string()))
184                    .and_then(|image| {
185                        active_abilities
186                            .and_then(|a| {
187                                a.activate_ability(
188                                    AbilityInput::Auxiliary(i),
189                                    Some(inventory),
190                                    skillset,
191                                    Some(body),
192                                    *char_state,
193                                    contexts,
194                                    *stats,
195                                )
196                            })
197                            .map(|(ability, _, _)| {
198                                (
199                                    image,
200                                    if energy.current() >= ability.energy_cost()
201                                        && combo
202                                            .is_some_and(|c| c.counter() >= ability.combo_cost())
203                                        && ability
204                                            .ability_meta()
205                                            .requirements
206                                            .requirements_met(*stance)
207                                    {
208                                        Some(Color::Rgba(1.0, 1.0, 1.0, 1.0))
209                                    } else {
210                                        Some(GREYED_OUT)
211                                    },
212                                )
213                            })
214                    })
215            },
216        })
217    }
218
219    fn amount(&self, (hotbar, inventory, ..): &HotbarSource<'a>) -> Option<u32> {
220        hotbar
221            .get(*self)
222            .and_then(|content| match content {
223                hotbar::SlotContents::Inventory(item_hash, _) => inventory.get_by_hash(item_hash),
224                hotbar::SlotContents::Ability(_) => None,
225            })
226            .map(|item| item.amount())
227            .filter(|amount| *amount > 1)
228    }
229
230    fn image_ids(
231        key: &Self::ImageKey,
232        (item_imgs, imgs): &HotbarImageSource<'a>,
233    ) -> Vec<image::Id> {
234        match key {
235            HotbarImage::Item(key) => item_imgs.img_ids_or_not_found_img(key.clone()),
236            HotbarImage::Ability(ability_id) => vec![util::ability_image(imgs, ability_id)],
237        }
238    }
239}
240
241#[derive(Clone, Copy, Debug, PartialEq, Eq)]
242pub enum AbilitySlot {
243    Slot(usize),
244    Ability(AuxiliaryAbility),
245}
246
247type AbilitiesSource<'a> = (
248    &'a ActiveAbilities,
249    &'a Inventory,
250    &'a SkillSet,
251    &'a AbilityContext,
252    Option<&'a CharacterState>,
253    Option<&'a Stats>,
254);
255
256impl<'a> SlotKey<AbilitiesSource<'a>, img_ids::Imgs> for AbilitySlot {
257    type ImageKey = String;
258
259    fn image_key(
260        &self,
261        (active_abilities, inventory, skillset, contexts, char_state, stats): &AbilitiesSource<'a>,
262    ) -> Option<(Self::ImageKey, Option<Color>)> {
263        let ability_id = match self {
264            Self::Slot(index) => active_abilities
265                .get_ability(
266                    AbilityInput::Auxiliary(*index),
267                    Some(inventory),
268                    Some(skillset),
269                    *stats,
270                )
271                .ability_id(*char_state, Some(inventory), Some(skillset), contexts),
272            Self::Ability(ability) => Ability::from(*ability).ability_id(
273                *char_state,
274                Some(inventory),
275                Some(skillset),
276                contexts,
277            ),
278        };
279
280        ability_id.map(|id| (String::from(id), None))
281    }
282
283    fn amount(&self, _source: &AbilitiesSource) -> Option<u32> { None }
284
285    fn image_ids(ability_id: &Self::ImageKey, imgs: &img_ids::Imgs) -> Vec<image::Id> {
286        vec![util::ability_image(imgs, ability_id)]
287    }
288}
289
290#[derive(Clone, Copy)]
291pub struct CraftSlot {
292    pub index: u32,
293    pub slot: Option<Slot>,
294    pub requirement: fn(&Item, &ComponentRecipeBook, Option<CraftSlotInfo>) -> bool,
295    pub info: Option<CraftSlotInfo>,
296}
297
298impl CraftSlot {
299    pub fn item<'a>(&'a self, inv: &'a Inventory) -> Option<&'a Item> {
300        match self.slot {
301            Some(Slot::Inventory(slot)) => inv.get(slot),
302            Some(Slot::Equip(slot)) => inv.equipped(slot),
303            Some(Slot::Overflow(_)) => None,
304            None => None,
305        }
306    }
307}
308
309#[derive(Clone, Copy, Debug)]
310pub enum CraftSlotInfo {
311    Tool(ToolKind),
312}
313
314impl PartialEq for CraftSlot {
315    fn eq(&self, other: &Self) -> bool { (self.index, self.slot) == (other.index, other.slot) }
316}
317
318impl Debug for CraftSlot {
319    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
320        f.debug_struct("CraftSlot")
321            .field("index", &self.index)
322            .field("slot", &self.slot)
323            .field("requirement", &"fn ptr")
324            .finish()
325    }
326}
327
328impl SlotKey<Inventory, ItemImgs> for CraftSlot {
329    type ImageKey = ItemKey;
330
331    fn image_key(&self, source: &Inventory) -> Option<(Self::ImageKey, Option<Color>)> {
332        self.item(source).map(|i| (i.into(), None))
333    }
334
335    fn amount(&self, source: &Inventory) -> Option<u32> {
336        self.item(source)
337            .map(|item| item.amount())
338            .filter(|amount| *amount > 1)
339    }
340
341    fn image_ids(key: &Self::ImageKey, source: &ItemImgs) -> Vec<image::Id> {
342        source.img_ids_or_not_found_img(key.clone())
343    }
344}
345
346impl From<InventorySlot> for SlotKind {
347    fn from(inventory: InventorySlot) -> Self { Self::Inventory(inventory) }
348}
349
350impl From<EquipSlot> for SlotKind {
351    fn from(equip: EquipSlot) -> Self { Self::Equip(equip) }
352}
353
354impl From<HotbarSlot> for SlotKind {
355    fn from(hotbar: HotbarSlot) -> Self { Self::Hotbar(hotbar) }
356}
357
358impl From<TradeSlot> for SlotKind {
359    fn from(trade: TradeSlot) -> Self { Self::Trade(trade) }
360}
361
362impl From<AbilitySlot> for SlotKind {
363    fn from(ability: AbilitySlot) -> Self { Self::Ability(ability) }
364}
365
366impl From<CraftSlot> for SlotKind {
367    fn from(craft: CraftSlot) -> Self { Self::Crafting(craft) }
368}
369
370impl SumSlot for SlotKind {
371    fn drag_size(&self) -> Option<[f64; 2]> {
372        Some(match self {
373            Self::Ability(_) => [80.0; 2],
374            _ => return None,
375        })
376    }
377}