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