Skip to main content

steel_core/fluid/fluids/
water.rs

1//! Water fluid callbacks.
2//!
3//! Based on vanilla's `WaterFluid.java`.
4//! Implements `FluidBehavior` and `FlowingFluid` for sharing base spread logic.
5
6use std::sync::Arc;
7
8use glam::DVec3;
9use steel_registry::blocks::properties::Direction;
10use steel_registry::vanilla_game_rules::WATER_SOURCE_CONVERSION;
11use steel_utils::BlockPos;
12use steel_utils::BlockStateId;
13
14use crate::entity::{Entity, InsideBlockEffectCollector, InsideBlockEffectType};
15use crate::fluid::{FlowingFluid, FluidBehavior, get_flow as flowing_fluid_flow};
16use crate::fluid::{FluidRef, FluidState, is_water_fluid, water_id};
17use crate::world::World;
18
19/// Water fluid implementation.
20///
21/// Implements [`FluidBehavior`] with water-specific parameters and
22/// behaviors (lava→water chemistry and game-rule source conversion).
23pub struct WaterFluid;
24
25impl FluidBehavior for WaterFluid {
26    fn fluid_type(&self) -> FluidRef {
27        water_id()
28    }
29
30    fn is_same(&self, fluid: FluidRef) -> bool {
31        is_water_fluid(fluid)
32    }
33
34    fn tick_delay(&self, _world: &Arc<World>) -> i32 {
35        5
36    }
37
38    fn drop_off(&self, _world: &Arc<World>) -> u8 {
39        1
40    }
41
42    fn slope_find_distance(&self, _world: &Arc<World>) -> u8 {
43        4
44    }
45
46    fn explosion_resistance(&self) -> f32 {
47        100.0
48    }
49
50    fn can_convert_to_source(&self, world: &Arc<World>) -> bool {
51        world.get_game_rule(&WATER_SOURCE_CONVERSION)
52    }
53
54    fn get_flow(&self, world: &Arc<World>, pos: BlockPos, fluid_state: FluidState) -> DVec3 {
55        flowing_fluid_flow(world, pos, fluid_state)
56    }
57
58    /// Water can only be replaced from below and only by non-water fluids.
59    fn can_be_replaced_with(
60        &self,
61        _fluid_state: FluidState,
62        _world: &Arc<World>,
63        _pos: BlockPos,
64        other_fluid: FluidRef,
65        direction: Direction,
66    ) -> bool {
67        direction == Direction::Down && !is_water_fluid(other_fluid)
68    }
69
70    /// Drops block resources when water replaces a non-air block.
71    fn before_destroying_block(&self, world: &Arc<World>, pos: BlockPos, state: BlockStateId) {
72        world.drop_resources(state, pos);
73    }
74
75    /// Vanilla parity: `WaterFluid.entityInside()` extinguishes fire.
76    fn entity_inside(
77        &self,
78        _world: &Arc<World>,
79        _pos: BlockPos,
80        _entity: &dyn Entity,
81        effect_collector: &mut InsideBlockEffectCollector,
82    ) {
83        effect_collector.apply(InsideBlockEffectType::Extinguish);
84    }
85
86    fn tick(
87        &self,
88        world: &Arc<World>,
89        pos: BlockPos,
90        block_state: BlockStateId,
91        fluid_state: FluidState,
92    ) {
93        self.base_tick(world, pos, block_state, fluid_state);
94    }
95
96    fn spread(
97        &self,
98        world: &Arc<World>,
99        pos: BlockPos,
100        block_state: BlockStateId,
101        fluid_state: FluidState,
102    ) {
103        self.base_spread(world, pos, block_state, fluid_state);
104    }
105}
106
107// Marker impl to provide base FlowingFluid logic
108impl FlowingFluid for WaterFluid {}