steel_registry/items/
mod.rs1use std::sync::OnceLock;
2
3use rustc_hash::FxHashMap;
4
5use steel_utils::Identifier;
6
7pub mod item;
8
9use crate::{
10 REGISTRY, RegistryExt, TaggedRegistryExt, blocks::BlockRef, data_components::DataComponentMap,
11 item_stack::ItemStack,
12};
13
14pub struct Item {
16 pub key: Identifier,
17 pub components: DataComponentMap,
18 pub craft_remainder: Option<Identifier>,
21 pub id: OnceLock<usize>,
23}
24
25impl std::fmt::Debug for Item {
26 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
27 f.debug_struct("Item").field("key", &self.key).finish()
28 }
29}
30
31impl Item {
32 #[must_use]
33 pub fn from_block(block: BlockRef) -> Self {
34 Self {
35 key: block.key.clone(),
36 components: DataComponentMap::common_item_components(),
37 craft_remainder: None,
38 id: OnceLock::new(),
39 }
40 }
41
42 #[must_use]
43 pub fn from_block_custom_name(_block: BlockRef, name: &'static str) -> Self {
44 Self {
45 key: Identifier::vanilla_static(name),
46 components: DataComponentMap::common_item_components(),
47 craft_remainder: None,
48 id: OnceLock::new(),
49 }
50 }
51
52 #[must_use]
54 pub fn builder_set<T: crate::data_components::Component>(
55 mut self,
56 component: crate::data_components::DataComponentType<T>,
57 value: Option<T>,
58 ) -> Self {
59 self.components.set(component, value);
60 self
61 }
62
63 #[must_use]
66 pub fn get_crafting_remainder(&self) -> ItemStack {
67 match &self.craft_remainder {
68 Some(remainder_key) => {
69 if let Some(remainder_item) = REGISTRY.items.by_key(remainder_key) {
70 ItemStack::new(remainder_item)
71 } else {
72 ItemStack::empty()
73 }
74 }
75 None => ItemStack::empty(),
76 }
77 }
78
79 pub fn has_tag(&'static self, tag: &Identifier) -> bool {
81 REGISTRY.items.is_in_tag(self, tag)
82 }
83}
84
85pub type ItemRef = &'static Item;
86
87impl PartialEq for ItemRef {
88 #[expect(clippy::disallowed_methods)] fn eq(&self, other: &Self) -> bool {
90 std::ptr::eq(*self, *other)
91 }
92}
93
94impl Eq for ItemRef {}
95
96pub struct ItemRegistry {
97 items_by_id: Vec<ItemRef>,
98 items_by_key: FxHashMap<Identifier, usize>,
99 tags: FxHashMap<Identifier, Vec<Identifier>>,
100 allows_registering: bool,
101}
102
103impl Default for ItemRegistry {
104 fn default() -> Self {
105 Self::new()
106 }
107}
108
109impl ItemRegistry {
110 #[must_use]
111 pub fn new() -> Self {
112 Self {
113 items_by_id: Vec::new(),
114 items_by_key: FxHashMap::default(),
115 tags: FxHashMap::default(),
116 allows_registering: true,
117 }
118 }
119
120 pub fn register(&mut self, item: ItemRef) -> usize {
121 assert!(
122 self.allows_registering,
123 "Cannot register items after the registry has been frozen"
124 );
125
126 let id = self.items_by_id.len();
127 let cached = item.id.get_or_init(|| id);
128 assert_eq!(*cached, id, "item registered with conflicting id");
129 self.items_by_key.insert(item.key.clone(), id);
130 self.items_by_id.push(item);
131
132 id
133 }
134
135 pub fn iter(&self) -> impl Iterator<Item = (usize, ItemRef)> + '_ {
136 self.items_by_id
137 .iter()
138 .enumerate()
139 .map(|(id, &item)| (id, item))
140 }
141}
142
143crate::impl_registry_ext!(ItemRegistry, Item, items_by_id, items_by_key);
144crate::impl_tagged_registry!(ItemRegistry, items_by_key, "item");
145
146impl crate::RegistryEntry for Item {
147 fn key(&self) -> &Identifier {
148 &self.key
149 }
150
151 fn try_id(&self) -> Option<usize> {
152 self.id.get().copied()
153 }
154}