steel_core/behavior/blocks/vegetation/
coral_block.rs1use std::sync::Arc;
2
3use steel_macros::block_behavior;
4use steel_registry::blocks::block_state_ext::BlockStateExt;
5use steel_registry::blocks::properties::{BlockStateProperties, BoolProperty, Direction};
6use steel_registry::fluid::FluidStateExt;
7use steel_utils::{BlockPos, BlockStateId, types::UpdateFlags};
8
9use crate::behavior::block::BlockBehavior;
10use crate::behavior::context::BlockPlaceContext;
11use crate::world::{LevelReader, ScheduledTickAccess, World};
12
13use super::BlockRef;
14
15#[block_behavior]
17pub struct CoralBlock {
18 block: BlockRef,
19 #[json_arg(vanilla_blocks, json = "dead_block")]
20 dead_block: BlockRef,
21}
22
23const WATERLOGGED: &BoolProperty = &BlockStateProperties::WATERLOGGED;
24const MIN_WATER_CHECK_DELAY: i32 = 60;
25
26impl CoralBlock {
27 #[must_use]
29 pub const fn new(block: BlockRef, dead_block: BlockRef) -> Self {
30 Self { block, dead_block }
31 }
32
33 fn dead_state(&self) -> BlockStateId {
34 self.dead_block.default_state()
35 }
36 pub(super) fn can_survive(world: &dyn LevelReader, pos: BlockPos) -> bool {
41 let below_pos = pos.below();
42 let below = world.get_block_state(below_pos);
43 world.is_face_sturdy(below, below_pos, Direction::Up)
44 }
45
46 pub(super) fn scan_for_water(
48 state: BlockStateId,
49 world: &dyn LevelReader,
50 pos: BlockPos,
51 ) -> bool {
52 if state.try_get_value(WATERLOGGED) == Some(true) {
53 return true;
54 }
55
56 Direction::ALL.iter().any(|direction| {
57 world
58 .get_block_state(pos.relative(*direction))
59 .get_fluid_state()
60 .is_water()
61 })
62 }
63
64 pub(super) fn schedule_die_tick(
66 state: BlockStateId,
67 world: &dyn ScheduledTickAccess,
68 pos: BlockPos,
69 block: BlockRef,
70 ) {
71 if Self::scan_for_water(state, world, pos) {
72 return;
73 }
74
75 let delay = MIN_WATER_CHECK_DELAY + rand::random_range(0..40);
77 let _ = world.schedule_block_tick_default(pos, block, delay);
78 }
79}
80
81impl BlockBehavior for CoralBlock {
82 fn update_shape(
83 &self,
84 state: BlockStateId,
85 world: &dyn ScheduledTickAccess,
86 pos: BlockPos,
87 _direction: Direction,
88 _neighbor_pos: BlockPos,
89 _neighbor_state: BlockStateId,
90 ) -> BlockStateId {
91 Self::schedule_die_tick(state, world, pos, self.block);
92
93 state
94 }
95
96 fn get_state_for_placement(&self, context: &BlockPlaceContext<'_>) -> Option<BlockStateId> {
97 let state = self.block.default_state();
98 Self::schedule_die_tick(state, context.world, context.place_pos(), self.block);
99 Some(state)
100 }
101
102 fn tick(&self, state: BlockStateId, world: &Arc<World>, pos: BlockPos) {
103 if !Self::scan_for_water(state, world, pos) {
104 world.set_block(pos, self.dead_state(), UpdateFlags::UPDATE_CLIENTS);
105 }
106 }
107}