Skip to main content

steel_registry/
potion.rs

1//! Potion registry values extracted from Vanilla.
2
3use rustc_hash::FxHashMap;
4use steel_utils::Identifier;
5
6use crate::mob_effect::MobEffectRef;
7use crate::{REGISTRY, RegistryTags, TaggedRegistryExt};
8
9/// One base effect supplied by a registered potion.
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub struct PotionEffect {
12    pub effect: MobEffectRef,
13    pub duration: i32,
14    pub amplifier: i32,
15}
16
17/// Registered potion definition.
18#[derive(Debug)]
19pub struct Potion {
20    pub key: Identifier,
21    /// Translation-key suffix used for item names.
22    pub name: &'static str,
23    pub effects: &'static [PotionEffect],
24}
25
26impl Potion {
27    #[must_use]
28    pub const fn new(
29        key: Identifier,
30        name: &'static str,
31        effects: &'static [PotionEffect],
32    ) -> Self {
33        Self { key, name, effects }
34    }
35
36    /// Returns `true` if this potion is tagged with the given tag.
37    #[must_use]
38    pub fn has_tag(&'static self, tag: &Identifier) -> bool {
39        REGISTRY.potions.is_in_tag(self, tag)
40    }
41}
42
43pub type PotionRef = &'static Potion;
44
45pub struct PotionRegistry {
46    potions_by_id: Vec<PotionRef>,
47    potions_by_key: FxHashMap<Identifier, usize>,
48    tags: RegistryTags,
49    allows_registering: bool,
50}
51
52impl PotionRegistry {
53    #[must_use]
54    pub fn new() -> Self {
55        Self {
56            potions_by_id: Vec::new(),
57            potions_by_key: FxHashMap::default(),
58            tags: RegistryTags::default(),
59            allows_registering: true,
60        }
61    }
62}
63
64crate::impl_standard_methods!(
65    PotionRegistry,
66    PotionRef,
67    potions_by_id,
68    potions_by_key,
69    allows_registering
70);
71
72crate::impl_registry!(
73    PotionRegistry,
74    Potion,
75    potions_by_id,
76    potions_by_key,
77    potions
78);
79crate::impl_tagged_registry!(PotionRegistry, potions_by_key, "potion");
80
81#[cfg(test)]
82mod tests {
83    use steel_utils::Identifier;
84
85    use crate::init_vanilla_registry;
86    use crate::{REGISTRY, RegistryExt, TaggedRegistryExt, potion::Potion};
87
88    static FIRST_DUPLICATE: Potion =
89        Potion::new(Identifier::vanilla_static("duplicate"), "duplicate", &[]);
90    static SECOND_DUPLICATE: Potion =
91        Potion::new(Identifier::vanilla_static("duplicate"), "duplicate", &[]);
92
93    #[test]
94    fn extracted_potions_follow_vanilla_ids_and_effects() {
95        init_vanilla_registry();
96        assert_eq!(REGISTRY.potions.len(), 46);
97        assert_eq!(
98            REGISTRY.potions.by_id(0).map(|potion| &potion.key),
99            Some(&Identifier::vanilla_static("water"))
100        );
101        let long_swiftness = REGISTRY
102            .potions
103            .by_key(&Identifier::vanilla_static("long_swiftness"))
104            .expect("long swiftness should be registered");
105        assert_eq!(long_swiftness.name, "swiftness");
106        let turtle_master = REGISTRY
107            .potions
108            .by_key(&Identifier::vanilla_static("turtle_master"))
109            .expect("turtle master should be registered");
110        assert_eq!(turtle_master.effects.len(), 2);
111        assert_eq!(
112            turtle_master.effects[0].effect.key.path.as_ref(),
113            "slowness"
114        );
115        assert_eq!(
116            turtle_master.effects[1].effect.key.path.as_ref(),
117            "resistance"
118        );
119    }
120
121    #[test]
122    fn vanilla_potion_tags_are_generated_from_builtin_data() {
123        init_vanilla_registry();
124        let tradeable = Identifier::vanilla_static("tradeable");
125        assert!(REGISTRY.potions.get_tag(&tradeable).is_some());
126    }
127
128    #[test]
129    fn tag_membership_and_iteration_follow_duplicate_key_replacement() {
130        let mut registry = super::PotionRegistry::new();
131        let tag = Identifier::vanilla_static("test_tag");
132        registry.register(&FIRST_DUPLICATE);
133        registry.register_tag(tag.clone(), &["duplicate"]);
134        registry.register(&SECOND_DUPLICATE);
135
136        assert!(registry.is_in_tag(&FIRST_DUPLICATE, &tag));
137        assert!(registry.is_in_tag(&SECOND_DUPLICATE, &tag));
138        assert!(
139            registry
140                .iter_tag(&tag)
141                .next()
142                .is_some_and(|entry| std::ptr::eq(entry, &raw const SECOND_DUPLICATE))
143        );
144    }
145}