Skip to main content

steel_core/behavior/blocks/vegetation/
big_dripleaf_stem_block.rs

1use 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/// Vanilla `BigDripleafStemBlock` survival.
25///
26/// Below must be stem or in `SUPPORTS_BIG_DRIPLEAF`; above must be stem or big
27/// dripleaf head.
28#[block_behavior]
29pub struct BigDripleafStemBlock {
30    block: BlockRef,
31}
32
33impl BigDripleafStemBlock {
34    /// Creates a new big dripleaf stem block behavior.
35    #[must_use]
36    pub const fn new(block: BlockRef) -> Self {
37        Self { block }
38    }
39    /// places a big dripleaf stem block in target position with specific properties
40    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}