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