steel_registry/data_components/components/
tool.rs1use std::io::{Result, Write};
4use std::str::FromStr;
5
6use steel_utils::{
7 BlockStateId, Identifier,
8 hash::{ComponentHasher, HashComponent},
9 serial::{ReadFrom, WriteTo},
10};
11
12use crate::REGISTRY;
13
14#[derive(Debug, Clone, PartialEq)]
17pub struct ToolRule {
18 pub blocks: Vec<Identifier>,
21 pub speed: Option<f32>,
23 pub correct_for_drops: Option<bool>,
26}
27
28impl ToolRule {
29 #[must_use]
31 pub fn mines_and_drops(blocks: Vec<Identifier>, speed: f32) -> Self {
32 Self {
33 blocks,
34 speed: Some(speed),
35 correct_for_drops: Some(true),
36 }
37 }
38
39 #[must_use]
41 pub fn denies_drops(blocks: Vec<Identifier>) -> Self {
42 Self {
43 blocks,
44 speed: None,
45 correct_for_drops: Some(false),
46 }
47 }
48
49 #[must_use]
51 pub fn override_speed(blocks: Vec<Identifier>, speed: f32) -> Self {
52 Self {
53 blocks,
54 speed: Some(speed),
55 correct_for_drops: None,
56 }
57 }
58
59 #[must_use]
62 pub fn matches_block(&self, block_state_id: BlockStateId) -> bool {
63 let Some(block) = REGISTRY.blocks.by_state_id(block_state_id) else {
64 return false;
65 };
66
67 for block_id in &self.blocks {
68 let id_str = format!("{}:{}", block_id.namespace, block_id.path);
69
70 if let Some(tag_str) = id_str.strip_prefix('#') {
72 if let Ok(tag_id) = Identifier::from_str(tag_str)
73 && block.has_tag(&tag_id)
74 {
75 return true;
76 }
77 } else {
78 if block.key == *block_id {
80 return true;
81 }
82 }
83 }
84
85 false
86 }
87}
88
89#[derive(Debug, Clone, PartialEq)]
91pub struct Tool {
92 pub rules: Vec<ToolRule>,
94 pub default_mining_speed: f32,
96 pub damage_per_block: i32,
98 pub can_destroy_blocks_in_creative: bool,
100}
101
102impl Default for Tool {
103 fn default() -> Self {
104 Self {
105 rules: Vec::new(),
106 default_mining_speed: 1.0,
107 damage_per_block: 1,
108 can_destroy_blocks_in_creative: true,
109 }
110 }
111}
112
113impl Tool {
114 #[must_use]
118 pub fn get_mining_speed(&self, block_state_id: BlockStateId) -> f32 {
119 for rule in &self.rules {
120 if let Some(speed) = rule.speed
121 && rule.matches_block(block_state_id)
122 {
123 return speed;
124 }
125 }
126 self.default_mining_speed
127 }
128
129 #[must_use]
133 pub fn is_correct_for_drops(&self, block_state_id: BlockStateId) -> bool {
134 for rule in &self.rules {
135 if let Some(correct) = rule.correct_for_drops
136 && rule.matches_block(block_state_id)
137 {
138 return correct;
139 }
140 }
141 false
142 }
143}
144
145impl WriteTo for Tool {
146 fn write(&self, _writer: &mut impl Write) -> Result<()> {
147 Ok(())
150 }
151}
152
153impl ReadFrom for Tool {
154 fn read(_data: &mut std::io::Cursor<&[u8]>) -> Result<Self> {
155 Ok(Self::default())
157 }
158}
159
160impl HashComponent for Tool {
161 fn hash_component(&self, hasher: &mut ComponentHasher) {
162 hasher.start_map();
165 hasher.end_map();
167 }
168}
169
170impl simdnbt::ToNbtTag for Tool {
171 fn to_nbt_tag(self) -> simdnbt::owned::NbtTag {
172 use simdnbt::owned::{NbtCompound, NbtList, NbtTag};
173
174 let mut compound = NbtCompound::new();
175
176 let rules: Vec<NbtCompound> = self
178 .rules
179 .into_iter()
180 .map(|rule| {
181 let mut rule_compound = NbtCompound::new();
182
183 let blocks: Vec<NbtTag> = rule
185 .blocks
186 .into_iter()
187 .map(|id| NbtTag::String(id.to_string().into()))
188 .collect();
189 rule_compound.insert(
190 "blocks",
191 NbtList::String(
192 blocks
193 .into_iter()
194 .filter_map(|t| match t {
195 NbtTag::String(s) => Some(s),
196 _ => None,
197 })
198 .collect(),
199 ),
200 );
201
202 if let Some(speed) = rule.speed {
203 rule_compound.insert("speed", speed);
204 }
205 if let Some(correct) = rule.correct_for_drops {
206 rule_compound.insert("correct_for_drops", i8::from(correct));
207 }
208
209 rule_compound
210 })
211 .collect();
212 compound.insert("rules", NbtList::Compound(rules));
213
214 compound.insert("default_mining_speed", self.default_mining_speed);
215 compound.insert("damage_per_block", self.damage_per_block);
216 compound.insert(
217 "can_destroy_blocks_in_creative",
218 i8::from(self.can_destroy_blocks_in_creative),
219 );
220
221 NbtTag::Compound(compound)
222 }
223}
224
225impl simdnbt::FromNbtTag for Tool {
226 fn from_nbt_tag(tag: simdnbt::borrow::NbtTag) -> Option<Self> {
227 let compound = tag.compound()?;
228
229 let mut rules = Vec::new();
231 if let Some(rules_list) = compound.get("rules").and_then(|t| t.list())
232 && let Some(compounds) = rules_list.compounds()
233 {
234 for rule_compound in compounds {
235 let mut blocks = Vec::new();
236 if let Some(blocks_list) = rule_compound.get("blocks").and_then(|t| t.list())
237 && let Some(strings) = blocks_list.strings()
238 {
239 for s in strings {
240 if let Ok(id) = s.to_str().parse() {
241 blocks.push(id);
242 }
243 }
244 }
245
246 let speed = rule_compound.get("speed").and_then(|t| t.float());
247 let correct_for_drops = rule_compound
248 .get("correct_for_drops")
249 .and_then(|t| t.byte())
250 .map(|b| b != 0);
251
252 rules.push(ToolRule {
253 blocks,
254 speed,
255 correct_for_drops,
256 });
257 }
258 }
259
260 let default_mining_speed = compound
261 .get("default_mining_speed")
262 .and_then(|t| t.float())
263 .unwrap_or(1.0);
264
265 let damage_per_block = compound
266 .get("damage_per_block")
267 .and_then(|t| t.int())
268 .unwrap_or(1);
269
270 let can_destroy_blocks_in_creative = compound
271 .get("can_destroy_blocks_in_creative")
272 .and_then(|t| t.byte())
273 .map(|b| b != 0)
274 .unwrap_or(true);
275
276 Some(Self {
277 rules,
278 default_mining_speed,
279 damage_per_block,
280 can_destroy_blocks_in_creative,
281 })
282 }
283}