Skip to main content

steel_core/fluid/fluids/
water.rs

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