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},
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 world::{LevelReader, ScheduledTickAccess, World},
21};
22
23#[block_behavior]
25pub struct BambooSaplingBlock {
26 block: BlockRef,
27}
28
29impl BambooSaplingBlock {
30 #[must_use]
32 pub const fn new(block: BlockRef) -> Self {
33 Self { block }
34 }
35
36 pub fn grow(world: &Arc<World>, pos: BlockPos) {
38 world.set_block(
39 pos.above(),
40 vanilla_blocks::BAMBOO
41 .default_state()
42 .set_value(&BlockStateProperties::BAMBOO_LEAVES, BambooLeaves::Small),
43 UpdateFlags::UPDATE_ALL,
44 );
45 }
46}
47
48impl Bonemealable for BambooSaplingBlock {
49 fn get_bonemeal_age_increase(&self, _world: &Arc<World>, _rng: &mut dyn rand::Rng) -> u8 {
50 1
51 }
52
53 fn is_valid_bonemeal_target(
54 &self,
55 _state: BlockStateId,
56 world: &dyn LevelReader,
57 pos: BlockPos,
58 ) -> bool {
59 !world.is_outside_build_height(pos.above().y())
60 && world.get_block_state(pos.above()).is_air()
61 }
62
63 fn perform_bonemeal(
64 &self,
65 _state: BlockStateId,
66 world: &Arc<World>,
67 _rng: &mut dyn rand::Rng,
68 pos: BlockPos,
69 ) {
70 Self::grow(world, pos);
71 }
72}
73
74impl BlockBehavior for BambooSaplingBlock {
75 fn get_state_for_placement(&self, context: &BlockPlaceContext<'_>) -> Option<BlockStateId> {
76 let state = self.block.default_state();
77 self.can_survive(state, context.world, context.place_pos())
78 .then_some(state)
79 }
80
81 fn update_shape(
82 &self,
83 state: BlockStateId,
84 world: &dyn ScheduledTickAccess,
85 pos: BlockPos,
86 direction: Direction,
87 _neighbor_pos: BlockPos,
88 neighbor_state: BlockStateId,
89 ) -> BlockStateId {
90 if !BambooStalkBlock::can_survive(world, pos) {
91 return vanilla_blocks::AIR.default_state();
92 }
93
94 if direction == Direction::Up && neighbor_state.get_block() == &vanilla_blocks::BAMBOO {
95 return vanilla_blocks::BAMBOO.default_state();
96 }
97
98 state
99 }
100
101 fn can_survive(&self, _state: BlockStateId, world: &dyn LevelReader, pos: BlockPos) -> bool {
102 BambooStalkBlock::can_survive(world, pos)
103 }
104
105 fn random_tick(&self, _state: BlockStateId, world: &Arc<World>, pos: BlockPos) {
106 if rand::random_range(0..3) == 0
107 && world.get_block_state(pos.above()).is_air()
108 && world.raw_brightness(pos.above(), 0) >= 9
109 {
110 Self::grow(world, pos);
111 }
112 }
113
114 fn get_clone_item_stack(
115 &self,
116 _block: BlockRef,
117 _state: BlockStateId,
118 _include_data: bool,
119 ) -> Option<ItemStack> {
120 Some(ItemStack::new(&vanilla_items::BAMBOO))
121 }
122
123 fn as_bonemealable(&self) -> Option<&dyn Bonemealable> {
124 Some(self)
125 }
126}