steel_core/behavior/blocks/vegetation/
bamboo_sapling.rs1use 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#[block_behavior]
26pub struct BambooSaplingBlock {
27 block: BlockRef,
28}
29
30const BAMBOO_LEAVES: &EnumProperty<BambooLeaves> = &BlockStateProperties::BAMBOO_LEAVES;
31
32impl BambooSaplingBlock {
33 #[must_use]
35 pub const fn new(block: BlockRef) -> Self {
36 Self { block }
37 }
38
39 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}