aboutsummaryrefslogtreecommitdiff
path: root/azalea-block/src/fluid_state.rs
diff options
context:
space:
mode:
authormat <27899617+mat-1@users.noreply.github.com>2025-01-10 16:45:27 -0600
committerGitHub <noreply@github.com>2025-01-10 16:45:27 -0600
commit0d16f01571ec8315f3979eae46981e559ade1cf9 (patch)
treeea43c32a57b0e6a67579d75a134dfbc009d09781 /azalea-block/src/fluid_state.rs
parent615d8f9d2ac56b3244d328587243301da253eafd (diff)
downloadazalea-drasl-0d16f01571ec8315f3979eae46981e559ade1cf9.tar.xz
Fluid physics (#199)
* start implementing fluid physics * Initial implementation of fluid pushing * different travel function in water * bubble columns * jumping in water * cleanup * change ultrawarm to be required * fix for clippy
Diffstat (limited to 'azalea-block/src/fluid_state.rs')
-rw-r--r--azalea-block/src/fluid_state.rs137
1 files changed, 137 insertions, 0 deletions
diff --git a/azalea-block/src/fluid_state.rs b/azalea-block/src/fluid_state.rs
new file mode 100644
index 00000000..edceac05
--- /dev/null
+++ b/azalea-block/src/fluid_state.rs
@@ -0,0 +1,137 @@
+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(Default, Clone, Debug, PartialEq, Eq)]
+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 = azalea_registry::Block::from(state);
+ match registry_block {
+ azalea_registry::Block::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,
+ };
+ }
+ azalea_registry::Block::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,
+ };
+ }
+ azalea_registry::Block::BubbleColumn => {
+ return Self::new_source_block(FluidKind::Water, false);
+ }
+ _ => {}
+ }
+
+ Self::default()
+ }
+}
+
+/// Sometimes Minecraft represents fluids with 0 being empty and 8 being full,
+/// and sometimes it's the opposite. You can use this function to convert
+/// in between those two representations.
+///
+/// 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),
+ }),
+ }
+ }
+}