steel_core/behavior/blocks/vegetation/
double_plant_block.rs1use std::sync::Arc;
2
3use steel_macros::block_behavior;
4use steel_registry::blocks::block_state_ext::BlockStateExt;
5use steel_registry::blocks::properties::{BlockStateProperties, Direction, DoubleBlockHalf};
6use steel_registry::vanilla_blocks;
7use steel_utils::{BlockPos, BlockStateId, axis::Axis, types::UpdateFlags};
8
9use crate::behavior::BlockStateBehaviorExt;
10use crate::behavior::block::BlockBehavior;
11use crate::behavior::blocks::vegetation::Vegetation;
12use crate::behavior::blocks::vegetation::vegetation_block::double_plant_can_survive;
13use crate::behavior::context::{BlockPlaceContext, PlacementSource};
14use crate::fluid::{FluidStateExt as _, get_fluid_state};
15use crate::world::{LevelReader, ScheduledTickAccess, World};
16
17use super::BlockRef;
18
19#[block_behavior]
21pub struct DoublePlantBlock {
22 block: BlockRef,
23}
24
25impl DoublePlantBlock {
26 #[must_use]
28 pub const fn new(block: BlockRef) -> Self {
29 Self { block }
30 }
31
32 pub(super) fn copy_waterlogged_from(
33 world: &Arc<World>,
34 pos: BlockPos,
35 state: BlockStateId,
36 ) -> BlockStateId {
37 if state
38 .try_get_value(&BlockStateProperties::WATERLOGGED)
39 .is_some()
40 {
41 state.set_value(
42 &BlockStateProperties::WATERLOGGED,
43 get_fluid_state(world, pos).is_water(),
44 )
45 } else {
46 state
47 }
48 }
49
50 pub(super) fn update_shape_with_survival(
53 &self,
54 survival_behavior: &dyn BlockBehavior,
55 state: BlockStateId,
56 world: &dyn ScheduledTickAccess,
57 pos: BlockPos,
58 direction: Direction,
59 neighbor_state: BlockStateId,
60 ) -> BlockStateId {
61 let half = state.get_value(&BlockStateProperties::DOUBLE_BLOCK_HALF);
62 let neighbor_is_matching_other_half = neighbor_state.get_block() == self.block
63 && neighbor_state.get_value(&BlockStateProperties::DOUBLE_BLOCK_HALF) != half;
64
65 if direction.get_axis() == Axis::Y
66 && (half == DoubleBlockHalf::Lower) == (direction == Direction::Up)
67 && !neighbor_is_matching_other_half
68 {
69 return vanilla_blocks::AIR.default_state();
70 }
71
72 if half == DoubleBlockHalf::Lower
73 && direction == Direction::Down
74 && !survival_behavior.can_survive(state, world, pos)
75 {
76 return vanilla_blocks::AIR.default_state();
77 }
78
79 state
80 }
81}
82
83impl Vegetation for DoublePlantBlock {}
84
85impl BlockBehavior for DoublePlantBlock {
86 fn update_shape(
87 &self,
88 state: BlockStateId,
89 world: &dyn ScheduledTickAccess,
90 pos: BlockPos,
91 direction: Direction,
92 _neighbor_pos: BlockPos,
93 neighbor_state: BlockStateId,
94 ) -> BlockStateId {
95 self.update_shape_with_survival(self, state, world, pos, direction, neighbor_state)
96 }
97
98 fn can_survive(&self, state: BlockStateId, world: &dyn LevelReader, pos: BlockPos) -> bool {
99 double_plant_can_survive(self, state, world, pos)
100 }
101
102 fn set_placed_by(
103 &self,
104 _state: BlockStateId,
105 world: &Arc<World>,
106 pos: BlockPos,
107 _source: &PlacementSource<'_>,
108 ) {
109 let upper_pos = pos.above();
110 let upper_state = Self::copy_waterlogged_from(
111 world,
112 upper_pos,
113 self.block.default_state().set_value(
114 &BlockStateProperties::DOUBLE_BLOCK_HALF,
115 DoubleBlockHalf::Upper,
116 ),
117 );
118 world.set_block(upper_pos, upper_state, UpdateFlags::UPDATE_ALL);
119 }
120
121 fn get_state_for_placement(&self, context: &BlockPlaceContext<'_>) -> Option<BlockStateId> {
122 if context.place_pos().y() >= context.world.max_y_exclusive() - 1 {
123 return None;
124 }
125 if !context
126 .world
127 .get_block_state(context.place_pos().above())
128 .can_be_replaced(context)
129 {
130 return None;
131 }
132 Some(self.block.default_state())
133 }
134}