1use rustc_hash::FxHashMap;
4use steel_utils::Identifier;
5
6use crate::mob_effect::MobEffectRef;
7use crate::{REGISTRY, RegistryTags, TaggedRegistryExt};
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 #[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}