1use crate::common::{
18 Aabb, BiomeInfo, BlockInfo, BlockPhysics, Collision, DigMaterial, EffectInfo, EnchantmentInfo,
19 EntityInfo, EntityKind, Fluid, FluidKind, FoodInfo, ItemInfo, NeighbourShape, PhysicsFlags,
20 ShapeDependency, lookup_id, lookup_name,
21};
22use crate::generated::v1_12 as g;
23
24pub const BLOCK_ID_SPACE: usize = 256;
26
27#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
29pub enum MaterialKind {
30 Air,
32 Water,
34 Lava,
36 Ice,
38 Other,
40}
41
42#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
44pub struct Material {
45 pub name: &'static str,
47 pub kind: MaterialKind,
49 pub blocks_movement: bool,
51 pub solid: bool,
53 pub liquid: bool,
55 pub opaque: bool,
57 pub replaceable: bool,
59}
60
61#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default)]
63pub struct StateFlags(pub u8);
64
65impl StateFlags {
66 pub const FULL_CUBE: StateFlags = StateFlags(1 << 0);
68 pub const NORMAL_CUBE: StateFlags = StateFlags(1 << 1);
71 pub const BLOCK_NORMAL_CUBE: StateFlags = StateFlags(1 << 2);
73 pub const TOP_SOLID: StateFlags = StateFlags(1 << 3);
75 pub const OPAQUE_CUBE: StateFlags = StateFlags(1 << 4);
77 pub const FULL_BLOCK: StateFlags = StateFlags(1 << 5);
79 pub const CAUSES_SUFFOCATION: StateFlags = StateFlags(1 << 6);
81
82 #[must_use]
84 pub const fn contains(self, other: StateFlags) -> bool {
85 self.0 & other.0 == other.0
86 }
87}
88
89#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
91pub enum Face {
92 Down,
94 Up,
96 North,
98 South,
100 West,
102 East,
104}
105
106#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
109pub enum FaceShape {
110 Solid,
112 Bowl,
114 CenterSmall,
116 MiddlePoleThin,
118 Center,
120 MiddlePole,
122 CenterBig,
124 MiddlePoleThick,
126 Undefined,
128}
129
130const FACE_SHAPES: [FaceShape; 9] = [
131 FaceShape::Solid,
132 FaceShape::Bowl,
133 FaceShape::CenterSmall,
134 FaceShape::MiddlePoleThin,
135 FaceShape::Center,
136 FaceShape::MiddlePole,
137 FaceShape::CenterBig,
138 FaceShape::MiddlePoleThick,
139 FaceShape::Undefined,
140];
141
142#[derive(Clone, Copy, Debug, PartialEq)]
144pub struct Selection {
145 pub bounds: Aabb,
147 pub dependency: ShapeDependency,
149 pub multi_pass_ray: bool,
151 pub can_collide: bool,
153 pub can_collide_liquid: bool,
155 pub has_collision_box: bool,
157}
158
159#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
161pub struct Block(pub(crate) u8);
162
163impl Block {
164 #[must_use]
166 pub fn from_id(id: u8) -> Option<Block> {
167 (g::blocks::BLOCK_SLOT[id as usize] != 255).then_some(Block(id))
168 }
169
170 #[must_use]
172 pub fn from_name(name: &str) -> Option<Block> {
173 lookup_name(&g::blocks::BLOCK_NAMES, name).map(Block)
174 }
175
176 #[must_use]
178 pub const fn id(self) -> u8 {
179 self.0
180 }
181
182 #[must_use]
184 pub fn info(self) -> &'static BlockInfo {
185 &g::blocks::BLOCKS[g::blocks::BLOCK_SLOT[self.0 as usize] as usize]
186 }
187
188 #[must_use]
190 pub fn name(self) -> &'static str {
191 self.info().name
192 }
193
194 #[must_use]
196 pub fn physics(self) -> &'static BlockPhysics {
197 &self.info().physics
198 }
199
200 #[must_use]
202 pub fn material(self) -> Material {
203 g::blocks::MATERIALS[g::blocks::BLOCK_MATERIAL[self.0 as usize] as usize]
204 }
205
206 #[must_use]
208 pub fn is_collidable(self) -> bool {
209 g::blocks::BLOCK_BITS[self.0 as usize] & 1 != 0
210 }
211
212 #[must_use]
215 pub fn fence_excluded(self) -> bool {
216 g::blocks::BLOCK_BITS[self.0 as usize] & 2 != 0
217 }
218
219 #[must_use]
221 pub fn wall_excluded(self) -> bool {
222 g::blocks::BLOCK_BITS[self.0 as usize] & 4 != 0
223 }
224
225 #[must_use]
227 pub fn pane_excluded(self) -> bool {
228 g::blocks::BLOCK_BITS[self.0 as usize] & 8 != 0
229 }
230
231 #[must_use]
233 pub fn shape_dependency(self) -> ShapeDependency {
234 g::blocks::BLOCK_SHAPE_DEPENDENCY[self.0 as usize]
235 }
236
237 #[must_use]
239 pub const fn with_meta(self, meta: u8) -> BlockState {
240 BlockState(((self.0 as u16) << 4) | (meta & 15) as u16)
241 }
242
243 #[must_use]
245 pub const fn default_state(self) -> BlockState {
246 self.with_meta(0)
247 }
248
249 pub fn all() -> impl Iterator<Item = Block> {
251 (0..=255u8).filter_map(Block::from_id)
252 }
253}
254
255#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
257pub struct BlockState(u16);
258
259impl BlockState {
260 pub const AIR: BlockState = BlockState(0);
262
263 #[must_use]
265 pub fn from_raw(raw: u16) -> Option<BlockState> {
266 (raw < 4096 && g::blocks::BLOCK_SLOT[(raw >> 4) as usize] != 255).then_some(BlockState(raw))
267 }
268
269 #[must_use]
271 pub fn from_raw_vanilla(raw: u16) -> BlockState {
272 match BlockState::from_raw(raw) {
273 Some(s) if s.is_registered() => s,
274 _ => BlockState::AIR,
275 }
276 }
277
278 #[must_use]
280 pub fn is_registered(self) -> bool {
281 g::blocks::REGISTERED_METAS[(self.0 >> 4) as usize] & (1 << (self.0 & 15)) != 0
282 }
283
284 #[must_use]
286 pub const fn new(block: Block, meta: u8) -> BlockState {
287 block.with_meta(meta)
288 }
289
290 #[must_use]
292 pub const fn raw(self) -> u16 {
293 self.0
294 }
295
296 #[must_use]
298 pub const fn block(self) -> Block {
299 Block((self.0 >> 4) as u8)
300 }
301
302 #[must_use]
304 pub const fn meta(self) -> u8 {
305 (self.0 & 15) as u8
306 }
307
308 #[must_use]
310 pub const fn is_air(self) -> bool {
311 self.0 >> 4 == 0
312 }
313
314 #[must_use]
317 pub fn collision(self) -> Collision {
318 Collision {
319 boxes: self.shape(),
320 dependency: g::blocks::BLOCK_SHAPE_DEPENDENCY[(self.0 >> 4) as usize],
321 world_coords: None,
322 }
323 }
324
325 #[must_use]
327 pub fn shape(self) -> &'static [Aabb] {
328 g::blocks::SHAPES[g::blocks::STATE_SHAPE[self.0 as usize] as usize]
329 }
330
331 #[must_use]
333 pub fn flags(self) -> StateFlags {
334 StateFlags(g::blocks::STATE_FLAGS[self.0 as usize])
335 }
336
337 #[must_use]
339 pub fn is_normal_cube(self) -> bool {
340 self.flags().contains(StateFlags::NORMAL_CUBE)
341 }
342
343 #[must_use]
345 pub fn is_full_cube(self) -> bool {
346 self.flags().contains(StateFlags::FULL_CUBE)
347 }
348
349 #[must_use]
352 pub fn face_shape(self, face: Face) -> FaceShape {
353 let v = g::blocks::STATE_FACES[self.0 as usize] >> (4 * face as u32);
354 FACE_SHAPES[(v & 15) as usize]
355 }
356
357 #[must_use]
359 pub fn selection(self) -> Selection {
360 let f = g::blocks::STATE_SELECTION_FLAGS[self.0 as usize];
361 let dependency = g::blocks::BLOCK_SHAPE_DEPENDENCY[(self.0 >> 4) as usize];
362 Selection {
363 bounds: g::blocks::SELECTION_BOUNDS
364 [g::blocks::STATE_SELECTION[self.0 as usize] as usize],
365 dependency,
366 multi_pass_ray: dependency == ShapeDependency::Neighbours(NeighbourShape::Stairs),
367 can_collide: f & 1 != 0,
368 can_collide_liquid: f & 2 != 0,
369 has_collision_box: f & 4 != 0,
370 }
371 }
372
373 #[must_use]
375 pub fn fluid(self) -> Option<Fluid> {
376 let flags = self.block().physics().flags;
377 let kind = if flags.contains(PhysicsFlags::WATER) {
378 FluidKind::Water
379 } else if flags.contains(PhysicsFlags::LAVA) {
380 FluidKind::Lava
381 } else {
382 return None;
383 };
384 Some(Fluid {
385 kind,
386 level: self.meta(),
387 })
388 }
389}
390
391#[must_use]
394#[allow(clippy::cast_precision_loss)]
395pub fn slime_dimensions(n: i32) -> crate::common::EntityDimensions {
396 let s = 0.510_000_05_f32 * n as f32;
397 crate::common::EntityDimensions {
398 width: s,
399 height: s,
400 eye_height: 0.625 * s,
401 }
402}
403
404crate::macros::common_api!();