Skip to main content

steel_core/behavior/blocks/vegetation/
mangrove_propagule_block.rs

1use steel_macros::block_behavior;
2use steel_registry::blocks::block_state_ext::BlockStateExt;
3use steel_registry::blocks::properties::{BlockStateProperties, Direction};
4use steel_registry::vanilla_block_tags::BlockTag;
5use steel_registry::vanilla_blocks;
6use steel_utils::{BlockPos, BlockStateId};
7
8use crate::behavior::block::{BlockBehavior, schedule_water_tick_if_waterlogged};
9use crate::behavior::context::BlockPlaceContext;
10use crate::world::{LevelReader, ScheduledTickAccess};
11
12use super::{BlockRef, default_surviving_state};
13
14/// Vanilla `MangrovePropaguleBlock` survival.
15///
16/// - Hanging: block above must be in `SUPPORTS_HANGING_MANGROVE_PROPAGULE`.
17/// - Planted: block below must be in `SUPPORTS_MANGROVE_PROPAGULE` (vanilla's
18///   `mayPlaceOn` override applied to the `VegetationBlock` survival rule).
19// TODO: Implement growth ticking and bonemeal advance.
20#[block_behavior]
21pub struct MangrovePropaguleBlock {
22    block: BlockRef,
23}
24
25impl MangrovePropaguleBlock {
26    /// Creates a new mangrove propagule block behavior.
27    #[must_use]
28    pub const fn new(block: BlockRef) -> Self {
29        Self { block }
30    }
31
32    /// Creates vanilla's initial hanging propagule state.
33    pub(crate) fn create_new_hanging_propagule() -> BlockStateId {
34        vanilla_blocks::MANGROVE_PROPAGULE
35            .default_state()
36            .set_value(&BlockStateProperties::HANGING, true)
37            .set_value(&BlockStateProperties::AGE_4, 0)
38    }
39}
40
41impl BlockBehavior for MangrovePropaguleBlock {
42    fn update_shape(
43        &self,
44        state: BlockStateId,
45        world: &dyn ScheduledTickAccess,
46        pos: BlockPos,
47        _direction: Direction,
48        _neighbor_pos: BlockPos,
49        _neighbor_state: BlockStateId,
50    ) -> BlockStateId {
51        schedule_water_tick_if_waterlogged(state, world, pos);
52        if self.can_survive(state, world, pos) {
53            state
54        } else {
55            vanilla_blocks::AIR.default_state()
56        }
57    }
58
59    fn can_survive(&self, state: BlockStateId, world: &dyn LevelReader, pos: BlockPos) -> bool {
60        if state.get_value(&BlockStateProperties::HANGING) {
61            let above = world.get_block_state(pos.above());
62            return above
63                .get_block()
64                .has_tag(&BlockTag::SUPPORTS_HANGING_MANGROVE_PROPAGULE);
65        }
66
67        let below = world.get_block_state(pos.below());
68        below
69            .get_block()
70            .has_tag(&BlockTag::SUPPORTS_MANGROVE_PROPAGULE)
71    }
72
73    fn get_state_for_placement(&self, context: &BlockPlaceContext<'_>) -> Option<BlockStateId> {
74        default_surviving_state(self.block, self, context)
75    }
76}
77
78#[cfg(test)]
79mod tests {
80    use steel_registry::init_vanilla_registry;
81
82    use super::*;
83    use crate::test_support::TestLevel;
84
85    #[test]
86    fn new_hanging_propagule_starts_at_age_zero() {
87        init_vanilla_registry();
88
89        let state = MangrovePropaguleBlock::create_new_hanging_propagule();
90
91        assert_eq!(state.get_block(), &vanilla_blocks::MANGROVE_PROPAGULE);
92        assert!(state.get_value(&BlockStateProperties::HANGING));
93        assert_eq!(state.get_value(&BlockStateProperties::AGE_4), 0);
94    }
95
96    #[test]
97    fn unsupported_waterlogged_propagule_schedules_water_before_breaking() {
98        init_vanilla_registry();
99        let behavior = MangrovePropaguleBlock::new(&vanilla_blocks::MANGROVE_PROPAGULE);
100        let state = vanilla_blocks::MANGROVE_PROPAGULE
101            .default_state()
102            .set_value(&BlockStateProperties::WATERLOGGED, true);
103        let level = TestLevel::default();
104
105        assert!(
106            behavior
107                .update_shape(
108                    state,
109                    &level,
110                    BlockPos::ZERO,
111                    Direction::Down,
112                    BlockPos::ZERO.below(),
113                    vanilla_blocks::AIR.default_state(),
114                )
115                .is_air()
116        );
117        assert!(level.scheduled_water_tick());
118    }
119}