azalea_block/
fluid_state.rs

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