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(
47 state: BlockStateId,
48 world: &dyn LevelReader,
49 pos: BlockPos,
50 ) -> bool {
51 if state.try_get_value(WATERLOGGED) == Some(true) {
52 return true;
53 }
54
55 Direction::ALL.iter().any(|direction| {
56 world
57 .get_block_state(pos.relative(*direction))
58 .get_fluid_state()
59 .is_water()
60 })
61 }
62
63 pub(super) fn schedule_die_tick(
64 state: BlockStateId,
65 world: &dyn ScheduledTickAccess,
66 pos: BlockPos,
67 block: BlockRef,
68 ) {
69 if Self::scan_for_water(state, world, pos) {
70 return;
71 }
72
73 let delay = MIN_WATER_CHECK_DELAY + rand::random_range(0..40);
75 let _ = world.schedule_block_tick_default(pos, block, delay);
76 }
77}
78
79impl BlockBehavior for CoralBlock {
80 fn update_shape(
81 &self,
82 state: BlockStateId,
83 world: &dyn ScheduledTickAccess,
84 pos: BlockPos,
85 _direction: Direction,
86 _neighbor_pos: BlockPos,
87 _neighbor_state: BlockStateId,
88 ) -> BlockStateId {
89 Self::schedule_die_tick(state, world, pos, self.block);
90
91 state
92 }
93
94 fn get_state_for_placement(&self, context: &BlockPlaceContext<'_>) -> Option<BlockStateId> {
95 let state = self.block.default_state();
96 Self::schedule_die_tick(state, context.world, context.place_pos(), self.block);
97 Some(state)
98 }
99
100 fn tick(&self, state: BlockStateId, world: &Arc<World>, pos: BlockPos) {
101 if !Self::scan_for_water(state, world, pos) {
102 world.set_block(pos, self.dead_state(), UpdateFlags::UPDATE_CLIENTS);
103 }
104 }
105}