Skip to main content

steel_core/behavior/blocks/falling/
concrete_powder_block.rs

1use std::sync::Arc;
2
3use steel_macros::block_behavior;
4use steel_registry::blocks::properties::Direction;
5use steel_registry::blocks::{BlockRef, block_state_ext::BlockStateExt as _};
6use steel_registry::fluid::FluidStateExt as _;
7use steel_utils::{BlockPos, BlockStateId, types::UpdateFlags};
8
9use crate::behavior::{BlockBehavior, BlockPlaceContext, Fallable};
10use crate::entity::entities::FallingBlockEntity;
11use crate::world::{LevelReader, ScheduledTickAccess, World};
12
13use super::FallingBlock;
14
15/// Vanilla `ConcretePowderBlock` behavior.
16#[block_behavior]
17pub struct ConcretePowderBlock {
18    falling: FallingBlock,
19    #[json_arg(vanilla_blocks, json = "concrete")]
20    concrete: BlockRef,
21}
22
23impl ConcretePowderBlock {
24    /// Creates concrete powder with its extractor-owned solid block mapping.
25    #[must_use]
26    pub const fn new(block: BlockRef, concrete: BlockRef) -> Self {
27        Self {
28            falling: FallingBlock::new(block),
29            concrete,
30        }
31    }
32
33    fn can_solidify(state: BlockStateId) -> bool {
34        state.get_fluid_state().is_water()
35    }
36
37    fn touches_liquid(world: &dyn LevelReader, pos: BlockPos) -> bool {
38        let mut test_pos = pos;
39        for direction in Direction::ALL {
40            let state_at_test_pos = world.get_block_state(test_pos);
41            if direction == Direction::Down && !Self::can_solidify(state_at_test_pos) {
42                continue;
43            }
44
45            test_pos = pos.relative(direction);
46            let neighbor_state = world.get_block_state(test_pos);
47            if Self::can_solidify(neighbor_state)
48                && !world.is_face_sturdy(neighbor_state, pos, direction.opposite())
49            {
50                return true;
51            }
52        }
53        false
54    }
55
56    fn should_solidify(
57        world: &dyn LevelReader,
58        pos: BlockPos,
59        replaced_state: BlockStateId,
60    ) -> bool {
61        Self::can_solidify(replaced_state) || Self::touches_liquid(world, pos)
62    }
63}
64
65impl Fallable for ConcretePowderBlock {
66    fn on_land(
67        &self,
68        world: &Arc<World>,
69        pos: BlockPos,
70        _state: BlockStateId,
71        replaced_state: BlockStateId,
72        _entity: &FallingBlockEntity,
73    ) {
74        if Self::should_solidify(world.as_ref(), pos, replaced_state) {
75            world.set_block(pos, self.concrete.default_state(), UpdateFlags::UPDATE_ALL);
76        }
77    }
78
79    fn is_concrete_powder(&self) -> bool {
80        true
81    }
82}
83
84impl BlockBehavior for ConcretePowderBlock {
85    fn get_state_for_placement(&self, context: &BlockPlaceContext<'_>) -> Option<BlockStateId> {
86        let pos = context.place_pos();
87        let replaced_state = context.world.get_block_state(pos);
88        if Self::should_solidify(context.world.as_ref(), pos, replaced_state) {
89            Some(self.concrete.default_state())
90        } else {
91            Some(self.falling.block().default_state())
92        }
93    }
94
95    fn on_place(
96        &self,
97        _state: BlockStateId,
98        world: &Arc<World>,
99        pos: BlockPos,
100        _old_state: BlockStateId,
101        _moved_by_piston: bool,
102    ) {
103        self.falling.on_place(world, pos);
104    }
105
106    fn update_shape(
107        &self,
108        state: BlockStateId,
109        ticks: &dyn ScheduledTickAccess,
110        pos: BlockPos,
111        _direction: Direction,
112        _neighbor_pos: BlockPos,
113        _neighbor_state: BlockStateId,
114    ) -> BlockStateId {
115        if Self::touches_liquid(ticks, pos) {
116            self.concrete.default_state()
117        } else {
118            self.falling.update_shape(state, ticks, pos)
119        }
120    }
121
122    fn tick(&self, state: BlockStateId, world: &Arc<World>, pos: BlockPos) {
123        let _ = FallingBlock::tick(state, world, pos);
124    }
125
126    fn as_fallable(&self) -> Option<&dyn Fallable> {
127        Some(self)
128    }
129}