Skip to main content

steel_core/behavior/blocks/vegetation/
chorus_plant_block.rs

1use steel_macros::block_behavior;
2use steel_registry::blocks::block_state_ext::BlockStateExt;
3use steel_registry::blocks::properties::BlockStateProperties;
4use steel_registry::vanilla_block_tags::BlockTag;
5use steel_registry::vanilla_blocks;
6use steel_utils::{BlockPos, BlockStateId, Direction};
7
8use crate::behavior::block::BlockBehavior;
9use crate::behavior::context::BlockPlaceContext;
10use crate::world::LevelReader;
11
12use super::BlockRef;
13
14const HORIZONTAL_DIRECTIONS: [Direction; 4] = [
15    Direction::North,
16    Direction::East,
17    Direction::South,
18    Direction::West,
19];
20
21/// Vanilla `ChorusPlantBlock` connection and survival behavior.
22// TODO: Implement ticking and full shape-update side effects.
23#[block_behavior]
24pub struct ChorusPlantBlock {
25    block: BlockRef,
26}
27
28impl ChorusPlantBlock {
29    /// Creates a new chorus plant block behavior.
30    #[must_use]
31    pub const fn new(block: BlockRef) -> Self {
32        Self { block }
33    }
34
35    #[must_use]
36    pub(crate) fn state_with_connections(
37        world: &dyn LevelReader,
38        pos: BlockPos,
39        mut state: BlockStateId,
40    ) -> BlockStateId {
41        let down = world.get_block_state(pos.below());
42        let up = world.get_block_state(pos.above());
43        let north = world.get_block_state(pos.north());
44        let east = world.get_block_state(pos.east());
45        let south = world.get_block_state(pos.south());
46        let west = world.get_block_state(pos.west());
47        let block = state.get_block();
48
49        state = state.set_value(
50            &BlockStateProperties::DOWN,
51            down.get_block() == block
52                || down.get_block() == &vanilla_blocks::CHORUS_FLOWER
53                || down.get_block().has_tag(&BlockTag::SUPPORTS_CHORUS_PLANT),
54        );
55        state = state.set_value(
56            &BlockStateProperties::UP,
57            up.get_block() == block || up.get_block() == &vanilla_blocks::CHORUS_FLOWER,
58        );
59        state = state.set_value(
60            &BlockStateProperties::NORTH,
61            north.get_block() == block || north.get_block() == &vanilla_blocks::CHORUS_FLOWER,
62        );
63        state = state.set_value(
64            &BlockStateProperties::EAST,
65            east.get_block() == block || east.get_block() == &vanilla_blocks::CHORUS_FLOWER,
66        );
67        state = state.set_value(
68            &BlockStateProperties::SOUTH,
69            south.get_block() == block || south.get_block() == &vanilla_blocks::CHORUS_FLOWER,
70        );
71        state.set_value(
72            &BlockStateProperties::WEST,
73            west.get_block() == block || west.get_block() == &vanilla_blocks::CHORUS_FLOWER,
74        )
75    }
76}
77
78impl BlockBehavior for ChorusPlantBlock {
79    fn can_survive(&self, _state: BlockStateId, world: &dyn LevelReader, pos: BlockPos) -> bool {
80        let below_state = world.get_block_state(pos.below());
81        let block_above_or_below =
82            !world.get_block_state(pos.above()).is_air() && !below_state.is_air();
83
84        for direction in HORIZONTAL_DIRECTIONS {
85            let neighbor_pos = pos.relative(direction);
86            let neighbor_state = world.get_block_state(neighbor_pos);
87            if neighbor_state.get_block() == self.block {
88                if block_above_or_below {
89                    return false;
90                }
91
92                let below = world.get_block_state(neighbor_pos.below());
93                if below.get_block() == self.block
94                    || below.get_block().has_tag(&BlockTag::SUPPORTS_CHORUS_PLANT)
95                {
96                    return true;
97                }
98            }
99        }
100
101        below_state.get_block() == self.block
102            || below_state
103                .get_block()
104                .has_tag(&BlockTag::SUPPORTS_CHORUS_PLANT)
105    }
106
107    fn get_state_for_placement(&self, context: &BlockPlaceContext<'_>) -> Option<BlockStateId> {
108        Some(Self::state_with_connections(
109            context.world.as_ref(),
110            context.place_pos(),
111            self.block.default_state(),
112        ))
113    }
114}