azalea_block/
fluid_state.rs

1use azalea_registry::builtin::BlockKind;
2
3use crate::block_state::{BlockState, BlockStateIntegerRepr};
4
5#[derive(Clone, Debug)]
6pub struct FluidState {
7    pub kind: FluidKind,
8    /// 0 = empty, 8 = full, 9 = max.
9    ///
10    /// 9 is meant to be used when there's another fluid block of the same type
11    /// above it, but it's usually unused by this struct.
12    ///
13    /// This is different from [`crate::blocks::Water::level`], which is
14    /// basically the opposite (0 = full, 8 = empty). You can convert between
15    /// the two representations with [`to_or_from_legacy_fluid_level`].
16    pub amount: u8,
17
18    /// Whether this fluid is at the max level and there's another fluid of the
19    /// same type above it.
20    ///
21    /// TODO: this is currently unused (always false), make this actually get
22    /// set (see FlowingFluid.getFlowing)
23    pub falling: bool,
24}
25#[derive(Default, Clone, Copy, Debug, PartialEq, Eq)]
26pub enum FluidKind {
27    #[default]
28    Empty,
29    Water,
30    Lava,
31}
32impl FluidState {
33    pub fn new_source_block(kind: FluidKind, falling: bool) -> Self {
34        Self {
35            kind,
36            amount: 8,
37            falling,
38        }
39    }
40
41    /// A floating point number in between 0 and 1 representing the height (as a
42    /// percentage of a full block) of the fluid.
43    pub fn height(&self) -> f32 {
44        self.amount as f32 / 9.
45    }
46    pub fn is_empty(&self) -> bool {
47        self.amount == 0
48    }
49
50    pub fn affects_flow(&self, other: &FluidState) -> bool {
51        other.amount == 0 || self.is_same_kind(other)
52    }
53
54    pub fn is_same_kind(&self, other: &FluidState) -> bool {
55        (other.kind == self.kind) || (self.amount == 0 && other.amount == 0)
56    }
57}
58
59impl Default for FluidState {
60    fn default() -> Self {
61        Self {
62            kind: FluidKind::Empty,
63            amount: 0,
64            falling: false,
65        }
66    }
67}
68
69impl From<BlockState> for FluidState {
70    fn from(state: BlockState) -> Self {
71        // note that 8 here might be treated as 9 in some cases if there's another fluid
72        // block of the same type above it
73
74        if state
75            .property::<crate::properties::Waterlogged>()
76            .unwrap_or_default()
77        {
78            return Self {
79                kind: FluidKind::Water,
80                amount: 8,
81                falling: false,
82            };
83        }
84
85        let registry_block = BlockKind::from(state);
86        match registry_block {
87            BlockKind::Water => {
88                let level = state
89                    .property::<crate::properties::WaterLevel>()
90                    .expect("water block should always have WaterLevel");
91                return Self {
92                    kind: FluidKind::Water,
93                    amount: to_or_from_legacy_fluid_level(level as u8),
94                    falling: false,
95                };
96            }
97            BlockKind::Lava => {
98                let level = state
99                    .property::<crate::properties::LavaLevel>()
100                    .expect("lava block should always have LavaLevel");
101                return Self {
102                    kind: FluidKind::Lava,
103                    amount: to_or_from_legacy_fluid_level(level as u8),
104                    falling: false,
105                };
106            }
107            BlockKind::BubbleColumn => {
108                return Self::new_source_block(FluidKind::Water, false);
109            }
110            _ => {}
111        }
112
113        Self::default()
114    }
115}
116
117/// Convert between Minecraft's two fluid level representations.
118///
119/// This exists because sometimes Minecraft represents fluids with 0 being empty
120/// and 8 being full, and sometimes it's the opposite.
121///
122/// You usually don't need to call this yourself, see [`FluidState`].
123pub fn to_or_from_legacy_fluid_level(level: u8) -> u8 {
124    // see FlowingFluid.getLegacyLevel
125    8_u8.saturating_sub(level)
126}
127
128impl From<FluidState> for BlockState {
129    fn from(state: FluidState) -> Self {
130        match state.kind {
131            FluidKind::Empty => BlockState::AIR,
132            FluidKind::Water => BlockState::from(crate::blocks::Water {
133                level: crate::properties::WaterLevel::from(state.amount as BlockStateIntegerRepr),
134            }),
135            FluidKind::Lava => BlockState::from(crate::blocks::Lava {
136                level: crate::properties::LavaLevel::from(state.amount as BlockStateIntegerRepr),
137            }),
138        }
139    }
140}