steel_core/behavior/blocks/vegetation/
big_dripleaf_stem_block.rs1use rand::Rng;
2use steel_macros::block_behavior;
3use steel_registry::blocks::block_state_ext::BlockStateExt;
4use steel_registry::blocks::properties::{BlockStateProperties, BoolProperty, EnumProperty};
5use steel_registry::fluid::{FluidState, FluidStateExt};
6use steel_registry::vanilla_block_tags::BlockTag;
7use steel_registry::{vanilla_blocks, vanilla_fluids};
8use steel_utils::types::UpdateFlags;
9use steel_utils::{BlockPos, BlockStateId, Direction};
10
11use crate::behavior::blocks::BigDripleafBlock;
12use crate::behavior::blocks::vegetation::bonemealable::BonemealAction;
13use crate::behavior::blocks::vegetation::get_top_connected_block;
14use crate::behavior::context::BlockPlaceContext;
15use crate::behavior::{block::BlockBehavior, blocks::vegetation::bonemealable::Bonemealable};
16use crate::world::{LevelReader, ScheduledTickAccess, World};
17use std::sync::Arc;
18
19use super::BlockRef;
20
21const WATERLOGGED: &BoolProperty = &BlockStateProperties::WATERLOGGED;
22const FACING: &EnumProperty<Direction> = &BlockStateProperties::FACING;
23
24#[block_behavior]
29pub struct BigDripleafStemBlock {
30 block: BlockRef,
31}
32
33impl BigDripleafStemBlock {
34 #[must_use]
36 pub const fn new(block: BlockRef) -> Self {
37 Self { block }
38 }
39 pub fn place(
41 world: &Arc<World>,
42 pos: BlockPos,
43 fluid_state: FluidState,
44 facing: Direction,
45 ) -> bool {
46 let new_state = vanilla_blocks::BIG_DRIPLEAF_STEM
47 .default_state()
48 .set_value(
49 WATERLOGGED,
50 fluid_state.is_source() && fluid_state.is_water(),
51 )
52 .set_value(FACING, facing);
53 world.set_block(pos, new_state, UpdateFlags::UPDATE_ALL)
54 }
55}
56
57impl BlockBehavior for BigDripleafStemBlock {
58 fn can_survive(&self, _state: BlockStateId, world: &dyn LevelReader, pos: BlockPos) -> bool {
59 let below = world.get_block_state(pos.below());
60 let below_block = below.get_block();
61
62 let above = world.get_block_state(pos.above());
63 let above_block = above.get_block();
64
65 let below_check =
66 below_block == self.block || below_block.has_tag(&BlockTag::SUPPORTS_BIG_DRIPLEAF);
67 let above_check = above_block == self.block || above_block == &vanilla_blocks::BIG_DRIPLEAF;
68
69 below_check && above_check
70 }
71 fn as_bonemealable(&self) -> Option<&dyn Bonemealable> {
72 Some(self)
73 }
74 fn get_state_for_placement(&self, _context: &BlockPlaceContext<'_>) -> Option<BlockStateId> {
75 Some(self.block.default_state())
76 }
77
78 fn update_shape(
79 &self,
80 state: BlockStateId,
81 world: &dyn ScheduledTickAccess,
82 pos: BlockPos,
83 direction: Direction,
84 _neighbor_pos: BlockPos,
85 _neighbor_state: BlockStateId,
86 ) -> BlockStateId {
87 if (direction == Direction::Down || direction == Direction::Up)
88 && !self.can_survive(state, world, pos)
89 {
90 world.schedule_block_tick_default(pos, self.block, 1);
91 }
92 if state.get_value(WATERLOGGED) {
93 world.schedule_fluid_tick_default(
94 pos,
95 &vanilla_fluids::WATER,
96 vanilla_fluids::WATER.tick_delay as i32,
97 );
98 }
99
100 state
101 }
102 fn tick(&self, state: BlockStateId, world: &Arc<World>, pos: BlockPos) {
103 if !self.can_survive(state, world, pos) {
104 world.destroy_block(pos, true);
105 }
106 }
107}
108impl Bonemealable for BigDripleafStemBlock {
109 fn is_valid_bonemeal_target(
110 &self,
111 _state: BlockStateId,
112 world: &dyn LevelReader,
113 pos: BlockPos,
114 ) -> bool {
115 let head_pos = get_top_connected_block(
116 world,
117 pos,
118 self.block,
119 Direction::Up,
120 &vanilla_blocks::BIG_DRIPLEAF,
121 );
122 match head_pos {
123 Some(head_pos) => BigDripleafBlock::can_grow_into(world, head_pos.above()),
124 None => false,
125 }
126 }
127
128 fn perform_bonemeal(
129 &self,
130 state: BlockStateId,
131 world: &Arc<World>,
132 _rng: &mut dyn Rng,
133 pos: BlockPos,
134 ) {
135 let forward_pos = get_top_connected_block(
136 world,
137 pos,
138 self.block,
139 Direction::Up,
140 &vanilla_blocks::BIG_DRIPLEAF,
141 );
142 let Some(head_pos) = forward_pos else {
143 return;
144 };
145 let place_head_pos = head_pos.above();
146 let facing = state.get_value(FACING);
147 Self::place(
148 world,
149 head_pos,
150 world.get_block_state(head_pos).get_fluid_state(),
151 facing,
152 );
153 BigDripleafBlock::place(
154 world,
155 place_head_pos,
156 world.get_block_state(place_head_pos).get_fluid_state(),
157 facing,
158 );
159 }
160
161 fn bonemeal_action_type(&self) -> BonemealAction {
162 BonemealAction::Grower
163 }
164}