Skip to main content

steel_core/behavior/blocks/vegetation/
bamboo_sapling.rs

1use std::sync::Arc;
2
3use steel_macros::block_behavior;
4use steel_registry::{
5    blocks::{
6        BlockRef,
7        block_state_ext::BlockStateExt,
8        properties::{BambooLeaves, BlockStateProperties, EnumProperty},
9    },
10    item_stack::ItemStack,
11    vanilla_blocks, vanilla_items,
12};
13use steel_utils::{BlockPos, BlockStateId, Direction, types::UpdateFlags};
14
15use crate::{
16    behavior::{
17        BlockBehavior, BlockPlaceContext,
18        blocks::vegetation::{BambooStalkBlock, bonemealable::Bonemealable},
19    },
20    block_entity::SharedBlockEntity,
21    world::{LevelReader, ScheduledTickAccess, World},
22};
23
24/// Behavior for the Bamboo Sapling Block
25#[block_behavior]
26pub struct BambooSaplingBlock {
27    block: BlockRef,
28}
29
30const BAMBOO_LEAVES: &EnumProperty<BambooLeaves> = &BlockStateProperties::BAMBOO_LEAVES;
31
32impl BambooSaplingBlock {
33    /// Creates a new Bamboo Sapling Behavior
34    #[must_use]
35    pub const fn new(block: BlockRef) -> Self {
36        Self { block }
37    }
38
39    /// Grows the Bamboo Sapling
40    pub fn grow(world: &Arc<World>, pos: BlockPos) {
41        world.set_block(
42            pos.above(),
43            vanilla_blocks::BAMBOO
44                .default_state()
45                .set_value(BAMBOO_LEAVES, BambooLeaves::Small),
46            UpdateFlags::UPDATE_ALL,
47        );
48    }
49}
50
51impl Bonemealable for BambooSaplingBlock {
52    fn get_bonemeal_age_increase(&self, _world: &Arc<World>, _rng: &mut dyn rand::Rng) -> u8 {
53        1
54    }
55
56    fn is_valid_bonemeal_target(
57        &self,
58        _state: BlockStateId,
59        world: &dyn LevelReader,
60        pos: BlockPos,
61    ) -> bool {
62        !world.is_outside_build_height(pos.above().y())
63            && world.get_block_state(pos.above()).is_air()
64    }
65
66    fn perform_bonemeal(
67        &self,
68        _state: BlockStateId,
69        world: &Arc<World>,
70        _rng: &mut dyn rand::Rng,
71        pos: BlockPos,
72    ) {
73        Self::grow(world, pos);
74    }
75}
76
77impl BlockBehavior for BambooSaplingBlock {
78    fn get_state_for_placement(&self, context: &BlockPlaceContext<'_>) -> Option<BlockStateId> {
79        let state = self.block.default_state();
80        self.can_survive(state, context.world, context.place_pos())
81            .then_some(state)
82    }
83
84    fn update_shape(
85        &self,
86        state: BlockStateId,
87        world: &dyn ScheduledTickAccess,
88        pos: BlockPos,
89        direction: Direction,
90        _neighbor_pos: BlockPos,
91        neighbor_state: BlockStateId,
92    ) -> BlockStateId {
93        if !BambooStalkBlock::can_survive(world, pos) {
94            return vanilla_blocks::AIR.default_state();
95        }
96
97        if direction == Direction::Up && neighbor_state.get_block() == &vanilla_blocks::BAMBOO {
98            return vanilla_blocks::BAMBOO.default_state();
99        }
100
101        state
102    }
103
104    fn can_survive(&self, _state: BlockStateId, world: &dyn LevelReader, pos: BlockPos) -> bool {
105        BambooStalkBlock::can_survive(world, pos)
106    }
107
108    fn random_tick(&self, _state: BlockStateId, world: &Arc<World>, pos: BlockPos) {
109        if rand::random_range(0..3) == 0
110            && world.get_block_state(pos.above()).is_air()
111            && world.raw_brightness(pos.above(), 0) >= 9
112        {
113            Self::grow(world, pos);
114        }
115    }
116
117    fn get_clone_item_stack(
118        &self,
119        _block: BlockRef,
120        _state: BlockStateId,
121        _block_entity: Option<SharedBlockEntity>,
122        _include_data: bool,
123    ) -> Option<ItemStack> {
124        Some(ItemStack::new(&vanilla_items::BAMBOO))
125    }
126
127    fn as_bonemealable(&self) -> Option<&dyn Bonemealable> {
128        Some(self)
129    }
130}