Skip to main content

steel_core/behavior/blocks/vegetation/
vegetation_block.rs

1use steel_registry::{
2    blocks::{
3        block_state_ext::BlockStateExt,
4        properties::{BlockStateProperties, DoubleBlockHalf},
5    },
6    vanilla_block_tags::BlockTag,
7    vanilla_blocks,
8};
9use steel_utils::{BlockPos, BlockStateId, Direction, axis::Axis};
10
11use crate::{
12    behavior::BlockBehavior,
13    world::{LevelReader, ScheduledTickAccess},
14};
15
16/// Common behavior for vegetation blocks
17pub trait Vegetation {
18    /// Checks if the vegetation block can be placed on the given block state below on the given position below.
19    fn may_place_on(&self, state: BlockStateId, _world: &dyn LevelReader, _pos: BlockPos) -> bool {
20        state.get_block().has_tag(&BlockTag::SUPPORTS_VEGETATION)
21    }
22}
23
24/// Shared survival logic for basic vegetation.
25pub fn vegetation_can_survive<H: Vegetation>(
26    hooks: &H,
27    _state: BlockStateId,
28    world: &dyn LevelReader,
29    pos: BlockPos,
30) -> bool {
31    let state_below = world.get_block_state(pos.below());
32    hooks.may_place_on(state_below, world, pos.below())
33}
34
35/// Shared update-shape logic for blocks that break when they can no longer survive.
36///
37/// Important: this calls the final `BlockBehavior::can_survive`,
38/// not `vegetation_can_survive`, so leaf blocks can override survival.
39pub fn survival_update_shape<B: BlockBehavior>(
40    block: &B,
41    state: BlockStateId,
42    world: &dyn ScheduledTickAccess,
43    pos: BlockPos,
44) -> BlockStateId {
45    if block.can_survive(state, world, pos) {
46        state
47    } else {
48        vanilla_blocks::AIR.default_state()
49    }
50}
51
52/// Shared survival logic for double plants.
53pub fn double_plant_can_survive<H: Vegetation>(
54    hooks: &H,
55    state: BlockStateId,
56    world: &dyn LevelReader,
57    pos: BlockPos,
58) -> bool {
59    if state.get_value(&BlockStateProperties::DOUBLE_BLOCK_HALF) == DoubleBlockHalf::Upper {
60        let state_below = world.get_block_state(pos.below());
61        state_below.get_block() == state.get_block()
62            && state_below.get_value(&BlockStateProperties::DOUBLE_BLOCK_HALF)
63                == DoubleBlockHalf::Lower
64    } else {
65        vegetation_can_survive(hooks, state, world, pos)
66    }
67}
68
69/// Shared update-shape logic for double plants.
70///
71/// This mirrors the Java superclass logic, but explicitly.
72pub fn double_plant_update_shape<B: BlockBehavior>(
73    block: &B,
74    state: BlockStateId,
75    world: &dyn ScheduledTickAccess,
76    pos: BlockPos,
77    direction: Direction,
78    neighbor_state: BlockStateId,
79) -> BlockStateId {
80    let half = state.get_value(&BlockStateProperties::DOUBLE_BLOCK_HALF);
81
82    if direction.axis() != Axis::Y
83        || ((half == DoubleBlockHalf::Lower) != (direction == Direction::Up))
84        || (neighbor_state.get_block() == state.get_block()
85            && neighbor_state.get_value(&BlockStateProperties::DOUBLE_BLOCK_HALF) != half)
86    {
87        if half == DoubleBlockHalf::Lower
88            && direction == Direction::Down
89            && !block.can_survive(state, world, pos)
90        {
91            return vanilla_blocks::AIR.default_state();
92        }
93
94        survival_update_shape(block, state, world, pos)
95    } else {
96        vanilla_blocks::AIR.default_state()
97    }
98}