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