Skip to main content

veloren_common/comp/inventory/item/
tool.rs

1// Note: If you changes here "break" old character saves you can change the
2// version in voxygen\src\meta.rs in order to reset save files to being empty
3
4use crate::{
5    assets::{Asset, AssetCache, AssetExt, AssetHandle, BoxedError, Ron, SharedString},
6    comp::{
7        CharacterAbility, Combo, SkillSet,
8        ability::Stance,
9        buff::{BuffKind, Buffs},
10        inventory::{
11            Inventory,
12            item::{DurabilityMultiplier, ItemKind},
13            slot::EquipSlot,
14        },
15        skills::Skill,
16    },
17};
18use hashbrown::HashMap;
19use serde::{Deserialize, Serialize};
20use std::ops::{Add, AddAssign, Div, Mul, MulAssign, Sub};
21use strum::EnumIter;
22use tracing::warn;
23
24#[derive(
25    Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize, Ord, PartialOrd, EnumIter,
26)]
27pub enum ToolKind {
28    // weapons
29    Sword,
30    Axe,
31    Hammer,
32    Bow,
33    Staff,
34    Sceptre,
35    // future weapons
36    Dagger,
37    Shield,
38    Spear,
39    Blowgun,
40    // tools
41    Debug,
42    Farming,
43    Pick,
44    Shovel,
45    /// Music Instruments
46    Instrument,
47    /// Throwable item
48    Throwable,
49    // npcs
50    /// Intended for invisible weapons (e.g. a creature using its claws or
51    /// biting)
52    Natural,
53    /// This is an placeholder item, it is used by non-humanoid npcs to attack
54    Empty,
55}
56
57impl ToolKind {
58    pub fn identifier_name(&self) -> &'static str {
59        match self {
60            ToolKind::Sword => "sword",
61            ToolKind::Axe => "axe",
62            ToolKind::Hammer => "hammer",
63            ToolKind::Bow => "bow",
64            ToolKind::Dagger => "dagger",
65            ToolKind::Staff => "staff",
66            ToolKind::Spear => "spear",
67            ToolKind::Blowgun => "blowgun",
68            ToolKind::Sceptre => "sceptre",
69            ToolKind::Shield => "shield",
70            ToolKind::Natural => "natural",
71            ToolKind::Debug => "debug",
72            ToolKind::Farming => "farming",
73            ToolKind::Pick => "pickaxe",
74            ToolKind::Shovel => "shovel",
75            ToolKind::Instrument => "instrument",
76            ToolKind::Throwable => "throwable",
77            ToolKind::Empty => "empty",
78        }
79    }
80
81    pub fn gains_combat_xp(&self) -> bool {
82        matches!(
83            self,
84            ToolKind::Sword
85                | ToolKind::Axe
86                | ToolKind::Hammer
87                | ToolKind::Bow
88                | ToolKind::Dagger
89                | ToolKind::Staff
90                | ToolKind::Spear
91                | ToolKind::Blowgun
92                | ToolKind::Sceptre
93                | ToolKind::Shield
94        )
95    }
96
97    pub fn can_block(&self) -> bool {
98        matches!(
99            self,
100            ToolKind::Sword
101                | ToolKind::Axe
102                | ToolKind::Hammer
103                | ToolKind::Shield
104                | ToolKind::Dagger
105        )
106    }
107
108    pub fn block_priority(&self) -> i32 {
109        match self {
110            ToolKind::Debug => 0,
111            ToolKind::Blowgun => 1,
112            ToolKind::Bow => 2,
113            ToolKind::Staff => 3,
114            ToolKind::Sceptre => 4,
115            ToolKind::Empty => 5,
116            ToolKind::Natural => 6,
117            ToolKind::Throwable => 7,
118            ToolKind::Instrument => 8,
119            ToolKind::Farming => 9,
120            ToolKind::Shovel => 10,
121            ToolKind::Pick => 11,
122            ToolKind::Dagger => 12,
123            ToolKind::Spear => 13,
124            ToolKind::Hammer => 14,
125            ToolKind::Axe => 15,
126            ToolKind::Sword => 16,
127            ToolKind::Shield => 17,
128        }
129    }
130}
131
132#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
133pub enum Hands {
134    One,
135    Two,
136}
137
138#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
139pub struct Stats {
140    pub equip_time_secs: f32,
141    pub power: f32,
142    pub effect_power: f32,
143    pub speed: f32,
144    pub range: f32,
145    pub energy_efficiency: f32,
146    pub buff_strength: f32,
147}
148
149impl Stats {
150    pub fn zero() -> Stats {
151        Stats {
152            equip_time_secs: 0.0,
153            power: 0.0,
154            effect_power: 0.0,
155            speed: 0.0,
156            range: 0.0,
157            energy_efficiency: 0.0,
158            buff_strength: 0.0,
159        }
160    }
161
162    pub fn one() -> Stats {
163        Stats {
164            equip_time_secs: 1.0,
165            power: 1.0,
166            effect_power: 1.0,
167            speed: 1.0,
168            range: 1.0,
169            energy_efficiency: 1.0,
170            buff_strength: 1.0,
171        }
172    }
173
174    /// Calculates a diminished buff strength where the buff strength is clamped
175    /// by the power, and then excess buff strength above the power is added
176    /// with diminishing returns.
177    // TODO: Remove this later when there are more varied high tier materials.
178    // Mainly exists for now as a hack to allow some progression in strength of
179    // directly applied buffs.
180    pub fn diminished_buff_strength(&self) -> f32 {
181        let base = self.buff_strength.clamp(0.0, self.power);
182        let diminished = (self.buff_strength - base + 1.0).log(5.0);
183        base + diminished
184    }
185
186    pub fn with_durability_mult(&self, dur_mult: DurabilityMultiplier) -> Self {
187        let less_scaled = dur_mult.0 * 0.5 + 0.5;
188        Self {
189            equip_time_secs: self.equip_time_secs / less_scaled.max(0.01),
190            power: self.power * dur_mult.0,
191            effect_power: self.effect_power * dur_mult.0,
192            speed: self.speed * less_scaled,
193            range: self.range * less_scaled,
194            energy_efficiency: self.energy_efficiency * less_scaled,
195            buff_strength: self.buff_strength * dur_mult.0,
196        }
197    }
198}
199
200impl Add<Stats> for Stats {
201    type Output = Self;
202
203    fn add(self, other: Self) -> Self {
204        Self {
205            equip_time_secs: self.equip_time_secs + other.equip_time_secs,
206            power: self.power + other.power,
207            effect_power: self.effect_power + other.effect_power,
208            speed: self.speed + other.speed,
209            range: self.range + other.range,
210            energy_efficiency: self.energy_efficiency + other.energy_efficiency,
211            buff_strength: self.buff_strength + other.buff_strength,
212        }
213    }
214}
215
216impl AddAssign<Stats> for Stats {
217    fn add_assign(&mut self, other: Stats) { *self = *self + other; }
218}
219
220impl Sub<Stats> for Stats {
221    type Output = Self;
222
223    fn sub(self, other: Self) -> Self::Output {
224        Self {
225            equip_time_secs: self.equip_time_secs - other.equip_time_secs,
226            power: self.power - other.power,
227            effect_power: self.effect_power - other.effect_power,
228            speed: self.speed - other.speed,
229            range: self.range - other.range,
230            energy_efficiency: self.energy_efficiency - other.energy_efficiency,
231            buff_strength: self.buff_strength - other.buff_strength,
232        }
233    }
234}
235
236impl Mul<Stats> for Stats {
237    type Output = Self;
238
239    fn mul(self, other: Self) -> Self {
240        Self {
241            equip_time_secs: self.equip_time_secs * other.equip_time_secs,
242            power: self.power * other.power,
243            effect_power: self.effect_power * other.effect_power,
244            speed: self.speed * other.speed,
245            range: self.range * other.range,
246            energy_efficiency: self.energy_efficiency * other.energy_efficiency,
247            buff_strength: self.buff_strength * other.buff_strength,
248        }
249    }
250}
251
252impl MulAssign<Stats> for Stats {
253    fn mul_assign(&mut self, other: Stats) { *self = *self * other; }
254}
255
256impl Div<f32> for Stats {
257    type Output = Self;
258
259    fn div(self, scalar: f32) -> Self {
260        Self {
261            equip_time_secs: self.equip_time_secs / scalar,
262            power: self.power / scalar,
263            effect_power: self.effect_power / scalar,
264            speed: self.speed / scalar,
265            range: self.range / scalar,
266            energy_efficiency: self.energy_efficiency / scalar,
267            buff_strength: self.buff_strength / scalar,
268        }
269    }
270}
271
272impl Mul<DurabilityMultiplier> for Stats {
273    type Output = Self;
274
275    fn mul(self, value: DurabilityMultiplier) -> Self { self.with_durability_mult(value) }
276}
277
278#[derive(Clone, Debug, Serialize, Deserialize)]
279pub struct Tool {
280    pub kind: ToolKind,
281    pub hands: Hands,
282    stats: Stats,
283    // TODO: item specific abilities
284}
285
286impl Tool {
287    // DO NOT USE UNLESS YOU KNOW WHAT YOU ARE DOING
288    // Added for CSV import of stats
289    pub fn new(kind: ToolKind, hands: Hands, stats: Stats) -> Self { Self { kind, hands, stats } }
290
291    pub fn empty() -> Self {
292        Self {
293            kind: ToolKind::Empty,
294            hands: Hands::One,
295            stats: Stats {
296                equip_time_secs: 0.0,
297                power: 1.00,
298                effect_power: 1.00,
299                speed: 1.00,
300                range: 1.0,
301                energy_efficiency: 1.0,
302                buff_strength: 1.0,
303            },
304        }
305    }
306
307    pub fn stats(&self, durability_multiplier: DurabilityMultiplier) -> Stats {
308        self.stats * durability_multiplier
309    }
310}
311
312#[derive(Clone, Debug, Serialize, Deserialize)]
313pub struct AbilitySet<T> {
314    pub guard: Option<AbilityKind<T>>,
315    pub primary: AbilityKind<T>,
316    pub secondary: AbilityKind<T>,
317    pub abilities: Vec<AbilityKind<T>>,
318}
319
320#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
321pub enum AbilityKind<T> {
322    Simple(Option<Skill>, T),
323    Contextualized {
324        pseudo_id: String,
325        abilities: Vec<(AbilityContext, (Option<Skill>, T))>,
326    },
327}
328
329/// The contextual index indicates which entry in a contextual ability was used.
330/// This should only be necessary for the frontend to distinguish between the
331/// options when a contextual ability is used.
332#[derive(Clone, Debug, Serialize, Deserialize, Copy, Eq, PartialEq)]
333pub struct ContextualIndex(pub usize);
334
335impl<T> AbilityKind<T> {
336    pub fn map<U, F: FnMut(T) -> U>(self, mut f: F) -> AbilityKind<U> {
337        match self {
338            Self::Simple(s, x) => AbilityKind::<U>::Simple(s, f(x)),
339            Self::Contextualized {
340                pseudo_id,
341                abilities,
342            } => AbilityKind::<U>::Contextualized {
343                pseudo_id,
344                abilities: abilities
345                    .into_iter()
346                    .map(|(c, (s, x))| (c, (s, f(x))))
347                    .collect(),
348            },
349        }
350    }
351
352    pub fn map_ref<U, F: FnMut(&T) -> U>(&self, mut f: F) -> AbilityKind<U> {
353        match self {
354            Self::Simple(s, x) => AbilityKind::<U>::Simple(*s, f(x)),
355            Self::Contextualized {
356                pseudo_id,
357                abilities,
358            } => AbilityKind::<U>::Contextualized {
359                pseudo_id: pseudo_id.clone(),
360                abilities: abilities
361                    .iter()
362                    .map(|(c, (s, x))| (*c, (*s, f(x))))
363                    .collect(),
364            },
365        }
366    }
367
368    pub fn ability(
369        &self,
370        skillset: Option<&SkillSet>,
371        stance: Option<&Stance>,
372        inv: Option<&Inventory>,
373        combo: Option<&Combo>,
374        buffs: Option<&Buffs>,
375    ) -> Option<(&T, Option<ContextualIndex>)> {
376        let unlocked = |s: Option<Skill>, a| {
377            // If there is a skill requirement and the skillset does not contain the
378            // required skill, return None
379            s.is_none_or(|s| skillset.is_some_and(|ss| ss.has_skill(s)))
380                .then_some(a)
381        };
382
383        match self {
384            AbilityKind::Simple(s, a) => unlocked(*s, a).map(|a| (a, None)),
385            AbilityKind::Contextualized {
386                pseudo_id: _,
387                abilities,
388            } => abilities
389                .iter()
390                .enumerate()
391                .filter_map(|(i, (req_contexts, (s, a)))| {
392                    unlocked(*s, a).map(|a| (i, (req_contexts, a)))
393                })
394                .find_map(|(i, (req_context, a))| {
395                    req_context
396                        .fulfilled_by(stance, inv, combo, buffs)
397                        .then_some((a, Some(ContextualIndex(i))))
398                }),
399        }
400    }
401}
402
403#[derive(Clone, Debug, Serialize, Deserialize, Copy, Eq, PartialEq, Hash, Default)]
404pub struct AbilityContext {
405    /// Note, in this context `Stance::None` isn't intended to be used. e.g. the
406    /// stance field should be `None` instead of `Some(Stance::None)` in the
407    /// ability map config files(s).
408    pub stance: Option<Stance>,
409    #[serde(default)]
410    pub dual_wielding_same_kind: bool,
411    pub combo: Option<u32>,
412    pub buff: Option<BuffKind>,
413}
414
415impl AbilityContext {
416    fn fulfilled_by(
417        &self,
418        stance: Option<&Stance>,
419        inv: Option<&Inventory>,
420        combo: Option<&Combo>,
421        buffs: Option<&Buffs>,
422    ) -> bool {
423        let dual_wielding_same_kind = if let Some(inv) = inv {
424            let tool_kind = |slot| {
425                inv.equipped(slot).and_then(|i| {
426                    if let ItemKind::Tool(tool) = &*i.kind() {
427                        Some(tool.kind)
428                    } else {
429                        None
430                    }
431                })
432            };
433            tool_kind(EquipSlot::ActiveMainhand) == tool_kind(EquipSlot::ActiveOffhand)
434        } else {
435            false
436        };
437
438        // Either stance not required or context is in the same stance
439        let stance_check = self.stance.is_none_or(|s| stance.copied() == Some(s));
440        // Either dual wield not required or context is dual wielding
441        let dual_wield_check = !self.dual_wielding_same_kind || dual_wielding_same_kind;
442        // Either no minimum combo needed or context has sufficient combo
443        let combo_check = self
444            .combo
445            .is_none_or(|c_req| combo.is_some_and(|c| c.counter() >= c_req));
446        // Either no buff requored or entity has buff present
447        let buff_check = self
448            .buff
449            .is_none_or(|b| buffs.is_some_and(|buffs| buffs.contains(b)));
450
451        stance_check && dual_wield_check && combo_check && buff_check
452    }
453}
454
455impl AbilitySet<AbilityItem> {
456    #[must_use]
457    pub fn modified_by_tool(
458        self,
459        tool: &Tool,
460        durability_multiplier: DurabilityMultiplier,
461    ) -> Self {
462        self.map(|a| AbilityItem {
463            id: a.id,
464            ability: a
465                .ability
466                .adjusted_by_stats(tool.stats(durability_multiplier)),
467        })
468    }
469}
470
471impl<T> AbilitySet<T> {
472    pub fn map<U, F: FnMut(T) -> U>(self, mut f: F) -> AbilitySet<U> {
473        AbilitySet {
474            guard: self.guard.map(|g| g.map(&mut f)),
475            primary: self.primary.map(&mut f),
476            secondary: self.secondary.map(&mut f),
477            abilities: self.abilities.into_iter().map(|x| x.map(&mut f)).collect(),
478        }
479    }
480
481    pub fn map_ref<U, F: FnMut(&T) -> U>(&self, mut f: F) -> AbilitySet<U> {
482        AbilitySet {
483            guard: self.guard.as_ref().map(|g| g.map_ref(&mut f)),
484            primary: self.primary.map_ref(&mut f),
485            secondary: self.secondary.map_ref(&mut f),
486            abilities: self.abilities.iter().map(|x| x.map_ref(&mut f)).collect(),
487        }
488    }
489
490    pub fn guard(
491        &self,
492        skillset: Option<&SkillSet>,
493        stance: Option<&Stance>,
494        inv: Option<&Inventory>,
495        combo: Option<&Combo>,
496        buffs: Option<&Buffs>,
497    ) -> Option<(&T, Option<ContextualIndex>)> {
498        self.guard
499            .as_ref()
500            .and_then(|g| g.ability(skillset, stance, inv, combo, buffs))
501    }
502
503    pub fn primary(
504        &self,
505        skillset: Option<&SkillSet>,
506        stance: Option<&Stance>,
507        inv: Option<&Inventory>,
508        combo: Option<&Combo>,
509        buffs: Option<&Buffs>,
510    ) -> Option<(&T, Option<ContextualIndex>)> {
511        self.primary.ability(skillset, stance, inv, combo, buffs)
512    }
513
514    pub fn secondary(
515        &self,
516        skillset: Option<&SkillSet>,
517        stance: Option<&Stance>,
518        inv: Option<&Inventory>,
519        combo: Option<&Combo>,
520        buffs: Option<&Buffs>,
521    ) -> Option<(&T, Option<ContextualIndex>)> {
522        self.secondary.ability(skillset, stance, inv, combo, buffs)
523    }
524
525    pub fn auxiliary(
526        &self,
527        index: usize,
528        skillset: Option<&SkillSet>,
529        stance: Option<&Stance>,
530        inv: Option<&Inventory>,
531        combo: Option<&Combo>,
532        buffs: Option<&Buffs>,
533    ) -> Option<(&T, Option<ContextualIndex>)> {
534        self.abilities
535            .get(index)
536            .and_then(|a| a.ability(skillset, stance, inv, combo, buffs))
537    }
538}
539
540impl Default for AbilitySet<AbilityItem> {
541    fn default() -> Self {
542        AbilitySet {
543            guard: None,
544            primary: AbilityKind::Simple(None, AbilityItem {
545                id: String::new(),
546                ability: CharacterAbility::default(),
547            }),
548            secondary: AbilityKind::Simple(None, AbilityItem {
549                id: String::new(),
550                ability: CharacterAbility::default(),
551            }),
552            abilities: Vec::new(),
553        }
554    }
555}
556
557#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
558pub enum AbilitySpec {
559    Tool(ToolKind),
560    Custom(String),
561}
562
563#[derive(Clone, Debug, Serialize, Deserialize)]
564pub struct AbilityItem {
565    pub id: String,
566    pub ability: CharacterAbility,
567}
568
569#[derive(Clone, Debug, Serialize, Deserialize)]
570pub enum AbilityMapEntry<T = AbilityItem> {
571    AbilitySet(AbilitySet<T>),
572    AbilitySetOverride {
573        parent: AbilitySpec,
574        guard: Option<AbilityKind<T>>,
575        primary: Option<AbilityKind<T>>,
576        secondary: Option<AbilityKind<T>>,
577        added_abilities: Vec<AbilityKind<T>>,
578        removed_abilities: Vec<AbilityKind<T>>,
579    },
580}
581
582impl<T: Clone + Eq> AbilityMapEntry<T> {
583    pub fn map_ref<U, F: FnMut(&T) -> U>(&self, mut f: F) -> AbilityMapEntry<U> {
584        match self {
585            AbilityMapEntry::AbilitySet(ability_set) => {
586                AbilityMapEntry::AbilitySet(ability_set.map_ref(f))
587            },
588            AbilityMapEntry::AbilitySetOverride {
589                parent,
590                guard,
591                primary,
592                secondary,
593                added_abilities,
594                removed_abilities,
595            } => AbilityMapEntry::AbilitySetOverride {
596                parent: parent.clone(),
597                guard: guard.as_ref().map(|g| g.map_ref(&mut f)),
598                primary: primary.as_ref().map(|p| p.map_ref(&mut f)),
599                secondary: secondary.as_ref().map(|s| s.map_ref(&mut f)),
600                added_abilities: added_abilities.iter().map(|x| x.map_ref(&mut f)).collect(),
601                removed_abilities: removed_abilities
602                    .iter()
603                    .map(|x| x.map_ref(&mut f))
604                    .collect(),
605            },
606        }
607    }
608
609    pub fn inherit(self, parent: &Self) -> Self {
610        match self {
611            AbilityMapEntry::AbilitySet(_) => self,
612            AbilityMapEntry::AbilitySetOverride {
613                guard,
614                primary,
615                secondary,
616                mut added_abilities,
617                mut removed_abilities,
618                ..
619            } => match parent {
620                AbilityMapEntry::AbilitySet(parent) => {
621                    added_abilities.extend(
622                        parent
623                            .abilities
624                            .iter()
625                            .filter(|x| !removed_abilities.contains(x))
626                            .cloned(),
627                    );
628
629                    AbilityMapEntry::AbilitySet(AbilitySet {
630                        guard: guard.or(parent.guard.clone()),
631                        primary: primary.unwrap_or(parent.primary.clone()),
632                        secondary: secondary.unwrap_or(parent.secondary.clone()),
633                        abilities: added_abilities,
634                    })
635                },
636                AbilityMapEntry::AbilitySetOverride {
637                    parent: p_parent,
638                    guard: p_guard,
639                    primary: p_primary,
640                    secondary: p_secondary,
641                    added_abilities: p_added_abilities,
642                    removed_abilities: p_removed_abilities,
643                } => {
644                    added_abilities.extend(
645                        p_added_abilities
646                            .iter()
647                            .filter(|x| !removed_abilities.contains(x))
648                            .cloned(),
649                    );
650                    removed_abilities.extend(
651                        p_removed_abilities
652                            .iter()
653                            .filter(|x| !added_abilities.contains(x))
654                            .cloned(),
655                    );
656
657                    AbilityMapEntry::AbilitySetOverride {
658                        parent: p_parent.clone(),
659                        guard: guard.or(p_guard.clone()),
660                        primary: primary.or(p_primary.clone()),
661                        secondary: secondary.or(p_secondary.clone()),
662                        added_abilities,
663                        removed_abilities,
664                    }
665                },
666            },
667        }
668    }
669}
670
671#[derive(Clone, Debug, Serialize, Deserialize)]
672pub struct AbilityMap<T = AbilityItem>(HashMap<AbilitySpec, AbilityMapEntry<T>>);
673
674impl AbilityMap {
675    pub fn load() -> AssetHandle<Self> {
676        Self::load_expect("common.abilities.ability_set_manifest")
677    }
678}
679
680impl<T> AbilityMap<T> {
681    pub fn get_ability_set(&self, key: &AbilitySpec) -> Option<&AbilitySet<T>> {
682        self.0.get(key).and_then(|entry| match entry {
683            AbilityMapEntry::AbilitySet(ability_set) => Some(ability_set),
684            AbilityMapEntry::AbilitySetOverride { .. } => None,
685        })
686    }
687}
688
689impl Asset for AbilityMap {
690    fn load(cache: &AssetCache, specifier: &SharedString) -> Result<Self, BoxedError> {
691        let mut ability_map = cache
692            .load::<Ron<AbilityMap<String>>>(specifier)?
693            .read()
694            .0
695            .0
696            .clone();
697
698        // Find child entries and inherit from their parent
699        while let Some((spec, mut entry)) = {
700            let spec = ability_map
701                .iter()
702                .find(|(_, entry)| matches!(entry, AbilityMapEntry::AbilitySetOverride { .. }))
703                .map(|(spec, _)| spec.clone());
704
705            spec.and_then(|spec| ability_map.remove_entry(&spec))
706        } {
707            let parent = if let AbilityMapEntry::AbilitySetOverride { parent, .. } = &entry {
708                Some(parent)
709            } else {
710                None
711            }
712            .and_then(|parent| ability_map.get(parent));
713
714            if let Some(parent) = parent {
715                entry = entry.inherit(parent);
716            }
717
718            ability_map.insert(spec, entry);
719        }
720
721        Ok(AbilityMap(
722            ability_map
723                .into_iter()
724                .map(|(kind, set)| {
725                    (
726                        kind.clone(),
727                        set.map_ref(|s| AbilityItem {
728                            id: s.clone(),
729                            ability: if let Ok(handle) = cache.load::<Ron<CharacterAbility>>(s) {
730                                handle.cloned().into_inner()
731                            } else {
732                                warn!(?s, "missing specified ability file");
733                                CharacterAbility::default()
734                            },
735                        }),
736                    )
737                })
738                .collect::<HashMap<_, _>>(),
739        ))
740    }
741}