Skip to main content

steel_core/behavior/block/
waterlogging.rs

1use super::{
2    Arc, BlockBehavior, BlockPos, BlockStateExt, BlockStateId, BlockStateProperties,
3    ConditionalBlockSetResult, FluidState, GameType, ItemStack, LevelAccessor, PickupResult,
4    Player, ScheduledTickAccess, UpdateFlags, World, sound_events, vanilla_blocks, vanilla_fluids,
5    vanilla_items,
6};
7
8#[must_use]
9pub(crate) fn drained_waterlogged_state(state: BlockStateId) -> Option<BlockStateId> {
10    (state.try_get_value(&BlockStateProperties::WATERLOGGED) == Some(true))
11        .then(|| state.set_value(&BlockStateProperties::WATERLOGGED, false))
12}
13
14pub(crate) fn pickup_waterlogged_block(
15    behavior: &dyn BlockBehavior,
16    world: &Arc<World>,
17    pos: BlockPos,
18    state: BlockStateId,
19    player: Option<&Player>,
20) -> Option<PickupResult> {
21    let new_state = drained_waterlogged_state(state)?;
22    if !can_pick_up_drained_waterlogged_state(state, player) {
23        return None;
24    }
25
26    if world.set_block_if_unchanged(pos, state, new_state, UpdateFlags::UPDATE_ALL)
27        != ConditionalBlockSetResult::Changed
28    {
29        return None;
30    }
31
32    if !behavior.can_survive(new_state, world, pos) {
33        world.destroy_block(pos, true);
34    }
35
36    Some(PickupResult {
37        filled_bucket: ItemStack::new(&vanilla_items::WATER_BUCKET),
38        sound: Some(&sound_events::ITEM_BUCKET_FILL),
39    })
40}
41
42pub(super) fn can_pick_up_drained_waterlogged_state(
43    state: BlockStateId,
44    player: Option<&Player>,
45) -> bool {
46    // Vanilla BarrierBlock only delegates to SimpleWaterloggedBlock for creative players;
47    // sponge passes no user and therefore must not drain it.
48    if state.get_block() != &vanilla_blocks::BARRIER {
49        return true;
50    }
51
52    player.is_some_and(|player| player.game_mode() == GameType::Creative)
53}
54
55pub(crate) fn schedule_placed_liquid_tick(
56    level: &dyn LevelAccessor,
57    pos: BlockPos,
58    fluid_state: FluidState,
59) {
60    let delay = level.fluid_tick_delay(fluid_state.fluid_id);
61    level.schedule_fluid_tick_default(pos, fluid_state.fluid_id, delay);
62}
63
64/// Mirrors the water-tick side effect shared by Vanilla waterlogged block
65/// `updateShape` implementations.
66pub(crate) fn schedule_water_tick_if_waterlogged(
67    state: BlockStateId,
68    level: &dyn ScheduledTickAccess,
69    pos: BlockPos,
70) {
71    if state.try_get_value(&BlockStateProperties::WATERLOGGED) == Some(true) {
72        let delay = level.fluid_tick_delay(&vanilla_fluids::WATER);
73        level.schedule_fluid_tick_default(pos, &vanilla_fluids::WATER, delay);
74    }
75}
76
77pub(crate) fn place_simple_waterlogged_liquid(
78    level: &dyn LevelAccessor,
79    pos: BlockPos,
80    state: BlockStateId,
81    fluid_state: FluidState,
82) -> bool {
83    if state.try_get_value(&BlockStateProperties::WATERLOGGED) != Some(false)
84        || fluid_state.fluid_id != &vanilla_fluids::WATER
85    {
86        return false;
87    }
88
89    let new_state = state.set_value(&BlockStateProperties::WATERLOGGED, true);
90    level.set_block_state(pos, new_state, UpdateFlags::UPDATE_ALL);
91    schedule_placed_liquid_tick(level, pos, fluid_state);
92    true
93}
94
95pub(crate) fn simple_waterlogged_is_liquid_container(state: BlockStateId) -> bool {
96    state
97        .try_get_value(&BlockStateProperties::WATERLOGGED)
98        .is_some()
99}