Skip to main content

steel_registry/poi/
mod.rs

1//! Point of Interest (POI) type registry.
2//!
3//! POI types track special blocks (beds, workstations, bells, nether portals, etc.)
4//! so game systems can efficiently query for nearby points of interest
5//! without scanning every block.
6
7use rustc_hash::FxHashMap;
8use steel_utils::{BlockStateId, Identifier};
9
10/// A type of point of interest (e.g., bed, workstation, bell, nether portal).
11///
12/// Each type maps to specific block states and defines how many entities
13/// can claim it via tickets (e.g., a bed has 1 ticket for 1 villager).
14#[derive(Debug)]
15pub struct PointOfInterestType {
16    pub key: Identifier,
17    pub block_state_ids: &'static [BlockStateId],
18    pub ticket_count: u32,
19    pub search_distance: u32,
20}
21
22/// Static reference to a POI type definition.
23pub type PoiTypeRef = &'static PointOfInterestType;
24
25/// Registry of all POI types, with reverse lookup from block state to type.
26pub struct PoiTypeRegistry {
27    types_by_id: Vec<PoiTypeRef>,
28    types_by_key: FxHashMap<Identifier, usize>,
29    /// O(1) block state -> POI type ID lookup.
30    state_to_type: FxHashMap<BlockStateId, usize>,
31    tags: FxHashMap<Identifier, Vec<Identifier>>,
32    allows_registering: bool,
33}
34
35impl Default for PoiTypeRegistry {
36    fn default() -> Self {
37        Self::new()
38    }
39}
40
41impl PoiTypeRegistry {
42    #[must_use]
43    pub fn new() -> Self {
44        Self {
45            types_by_id: Vec::new(),
46            types_by_key: FxHashMap::default(),
47            state_to_type: FxHashMap::default(),
48            tags: FxHashMap::default(),
49            allows_registering: true,
50        }
51    }
52
53    pub fn register(&mut self, poi_type: PoiTypeRef) -> usize {
54        assert!(
55            self.allows_registering,
56            "Cannot register POI types after the registry has been frozen"
57        );
58
59        let id = self.types_by_id.len();
60        self.types_by_key.insert(poi_type.key.clone(), id);
61
62        for &state_id in poi_type.block_state_ids {
63            self.state_to_type.insert(state_id, id);
64        }
65
66        self.types_by_id.push(poi_type);
67        id
68    }
69
70    #[must_use]
71    pub fn type_for_state(&self, state_id: BlockStateId) -> Option<PoiTypeRef> {
72        use crate::RegistryExt;
73        self.state_to_type
74            .get(&state_id)
75            .and_then(|id| self.by_id(*id))
76    }
77
78    #[must_use]
79    pub fn type_id_for_state(&self, state_id: BlockStateId) -> Option<usize> {
80        self.state_to_type.get(&state_id).copied()
81    }
82
83    #[must_use]
84    pub fn is_poi_state(&self, state_id: BlockStateId) -> bool {
85        self.state_to_type.contains_key(&state_id)
86    }
87
88    pub fn iter(&self) -> impl Iterator<Item = (usize, PoiTypeRef)> + '_ {
89        self.types_by_id
90            .iter()
91            .enumerate()
92            .map(|(id, &poi_type)| (id, poi_type))
93    }
94}
95
96crate::impl_registry!(
97    PoiTypeRegistry,
98    PointOfInterestType,
99    types_by_id,
100    types_by_key,
101    poi_types
102);
103crate::impl_tagged_registry!(PoiTypeRegistry, types_by_key, "POI type");