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/// Vanilla `BigDripleafStemBlock` survival.
24///
25/// Below must be stem or in `SUPPORTS_BIG_DRIPLEAF`; above must be stem or big
26/// dripleaf head.
27#[block_behavior]
28pub struct BigDripleafStemBlock {
29    block: BlockRef,
30}
31
32impl BigDripleafStemBlock {
33    /// Creates a new big dripleaf stem block behavior.
34    #[must_use]
35    pub const fn new(block: BlockRef) -> Self {
36        Self { block }
37    }
38    /// places a big dripleaf stem block in target position with specific properties
39    pub fn place(
40        world: &Arc<World>,
41        pos: BlockPos,
42        fluid_state: FluidState,
43        facing: Direction,
44    ) -> bool {
45        let new_state = vanilla_blocks::BIG_DRIPLEAF_STEM
46            .default_state()
47            .set_value(
48                &WATERLOGGED,
49                fluid_state.is_source() && fluid_state.is_water(),
50            )
51            .set_value(&FACING, facing);
52        world.set_block(pos, new_state, UpdateFlags::UPDATE_ALL)
53    }
54}
55
56impl BlockBehavior for BigDripleafStemBlock {
57    fn can_survive(&self, _state: BlockStateId, world: &dyn LevelReader, pos: BlockPos) -> bool {
58        let below = world.get_block_state(pos.below());
59        let below_block = below.get_block();
60
61        let above = world.get_block_state(pos.above());
62        let above_block = above.get_block();
63
64        let below_check =
65            below_block == self.block || below_block.has_tag(&BlockTag::SUPPORTS_BIG_DRIPLEAF);
66        let above_check = above_block == self.block || above_block == &vanilla_blocks::BIG_DRIPLEAF;
67
68        below_check && above_check
69    }
70    fn as_bonemealable(&self) -> Option<&dyn Bonemealable> {
71        Some(self)
72    }
73    fn get_state_for_placement(&self, _context: &BlockPlaceContext<'_>) -> Option<BlockStateId> {
74        Some(self.block.default_state())
75    }
76
77    fn update_shape(
78        &self,
79        state: BlockStateId,
80        world: &dyn ScheduledTickAccess,
81        pos: BlockPos,
82        direction: Direction,
83        _neighbor_pos: BlockPos,
84        _neighbor_state: BlockStateId,
85    ) -> BlockStateId {
86        if (direction == Direction::Down || direction == Direction::Up)
87            && !self.can_survive(state, world, pos)
88        {
89            world.schedule_block_tick_default(pos, self.block, 1);
90        }
91        if state.get_value(&WATERLOGGED) {
92            world.schedule_fluid_tick_default(
93                pos,
94                &vanilla_fluids::WATER,
95                vanilla_fluids::WATER.tick_delay as i32,
96            );
97        }
98
99        state
100    }
101    fn tick(&self, state: BlockStateId, world: &Arc<World>, pos: BlockPos) {
102        if !self.can_survive(state, world, pos) {
103            world.destroy_block(pos, true);
104        }
105    }
106}
107impl Bonemealable for BigDripleafStemBlock {
108    fn is_valid_bonemeal_target(
109        &self,
110        _state: BlockStateId,
111        world: &dyn LevelReader,
112        pos: BlockPos,
113    ) -> bool {
114        let head_pos = get_top_connected_block(
115            world,
116            pos,
117            self.block,
118            Direction::Up,
119            &vanilla_blocks::BIG_DRIPLEAF,
120        );
121        match head_pos {
122            Some(head_pos) => BigDripleafBlock::can_grow_into(world, head_pos.above()),
123            None => false,
124        }
125    }
126
127    fn perform_bonemeal(
128        &self,
129        state: BlockStateId,
130        world: &Arc<World>,
131        _rng: &mut dyn Rng,
132        pos: BlockPos,
133    ) {
134        let forward_pos = get_top_connected_block(
135            world,
136            pos,
137            self.block,
138            Direction::Up,
139            &vanilla_blocks::BIG_DRIPLEAF,
140        );
141        let Some(head_pos) = forward_pos else {
142            return;
143        };
144        let place_head_pos = head_pos.above();
145        let facing = state.get_value(&FACING);
146        Self::place(
147            world,
148            head_pos,
149            world.get_block_state(head_pos).get_fluid_state(),
150            facing,
151        );
152        BigDripleafBlock::place(
153            world,
154            place_head_pos,
155            world.get_block_state(place_head_pos).get_fluid_state(),
156            facing,
157        );
158    }
159
160    fn bonemeal_action_type(&self) -> BonemealAction {
161        BonemealAction::Grower
162    }
163}