Skip to main content

steel_core/behavior/blocks/vegetation/
mossy_carpet_block.rs

1use steel_macros::block_behavior;
2use steel_registry::blocks::block_state_ext::BlockStateExt;
3use steel_registry::blocks::properties::{BlockStateProperties, Direction, EnumProperty, WallSide};
4use steel_registry::vanilla_blocks;
5use steel_utils::{BlockPos, BlockStateId};
6
7use crate::behavior::block::BlockBehavior;
8use crate::behavior::context::BlockPlaceContext;
9use crate::world::{LevelReader, ScheduledTickAccess};
10
11use super::{BlockRef, can_attach_to_multiface};
12
13/// Vanilla `MossyCarpetBlock` survival and side state updates.
14// TODO: Implement spreading, bonemeal, and the rest of vanilla behavior.
15#[block_behavior]
16pub struct MossyCarpetBlock {
17    block: BlockRef,
18}
19
20impl MossyCarpetBlock {
21    pub(crate) const HORIZONTAL_DIRECTIONS: [Direction; 4] = [
22        Direction::North,
23        Direction::East,
24        Direction::South,
25        Direction::West,
26    ];
27
28    /// Creates a new mossy-carpet block behavior.
29    #[must_use]
30    pub const fn new(block: BlockRef) -> Self {
31        Self { block }
32    }
33
34    /// Vanilla `MossyCarpetBlock.getPropertyForFace`.
35    pub(crate) const fn wall_property(direction: Direction) -> EnumProperty<WallSide> {
36        match direction {
37            Direction::North => BlockStateProperties::NORTH_WALL,
38            Direction::East => BlockStateProperties::EAST_WALL,
39            Direction::South => BlockStateProperties::SOUTH_WALL,
40            Direction::West => BlockStateProperties::WEST_WALL,
41            Direction::Down | Direction::Up => {
42                panic!("mossy carpet has no wall property for vertical direction")
43            }
44        }
45    }
46
47    /// Vanilla `MossyCarpetBlock.hasFaces`.
48    pub(crate) fn has_faces(state: BlockStateId) -> bool {
49        if state.get_value(&BlockStateProperties::BOTTOM) {
50            return true;
51        }
52
53        for direction in Self::HORIZONTAL_DIRECTIONS {
54            let property = Self::wall_property(direction);
55            if state.get_value(&property) != WallSide::None {
56                return true;
57            }
58        }
59
60        false
61    }
62
63    /// Vanilla `MossyCarpetBlock.canSupportAtFace`.
64    pub(crate) fn can_support_at_face(
65        world: &dyn LevelReader,
66        pos: BlockPos,
67        direction: Direction,
68    ) -> bool {
69        direction != Direction::Up
70            && can_attach_to_multiface(world, pos.relative(direction), direction)
71    }
72
73    /// Vanilla `MossyCarpetBlock.getUpdatedState`.
74    pub(crate) fn updated_state(
75        world: &dyn LevelReader,
76        mut state: BlockStateId,
77        pos: BlockPos,
78        create_sides: bool,
79    ) -> BlockStateId {
80        let create_sides = create_sides || state.get_value(&BlockStateProperties::BOTTOM);
81        let mut above_state = None;
82        let mut below_state = None;
83
84        for direction in Self::HORIZONTAL_DIRECTIONS {
85            let property = Self::wall_property(direction);
86            let mut side = if Self::can_support_at_face(world, pos, direction) {
87                if create_sides {
88                    WallSide::Low
89                } else {
90                    state.get_value(&property)
91                }
92            } else {
93                WallSide::None
94            };
95
96            if side == WallSide::Low {
97                let above = *above_state.get_or_insert_with(|| world.get_block_state(pos.above()));
98                if above.get_block() == &vanilla_blocks::PALE_MOSS_CARPET
99                    && above.get_value(&property) != WallSide::None
100                    && !above.get_value(&BlockStateProperties::BOTTOM)
101                {
102                    side = WallSide::Tall;
103                }
104
105                if !state.get_value(&BlockStateProperties::BOTTOM) {
106                    let below =
107                        *below_state.get_or_insert_with(|| world.get_block_state(pos.below()));
108                    if below.get_block() == &vanilla_blocks::PALE_MOSS_CARPET
109                        && below.get_value(&property) == WallSide::None
110                    {
111                        side = WallSide::None;
112                    }
113                }
114            }
115
116            state = state.set_value(&property, side);
117        }
118
119        state
120    }
121}
122
123impl BlockBehavior for MossyCarpetBlock {
124    fn update_shape(
125        &self,
126        state: BlockStateId,
127        world: &dyn ScheduledTickAccess,
128        pos: BlockPos,
129        _direction: Direction,
130        _neighbor_pos: BlockPos,
131        _neighbor_state: BlockStateId,
132    ) -> BlockStateId {
133        if !self.can_survive(state, world, pos) {
134            return vanilla_blocks::AIR.default_state();
135        }
136
137        let updated = Self::updated_state(world, state, pos, false);
138        if Self::has_faces(updated) {
139            updated
140        } else {
141            vanilla_blocks::AIR.default_state()
142        }
143    }
144
145    fn can_survive(&self, state: BlockStateId, world: &dyn LevelReader, pos: BlockPos) -> bool {
146        if state.get_value(&BlockStateProperties::BOTTOM) {
147            !world.get_block_state(pos.below()).is_air()
148        } else {
149            let below = world.get_block_state(pos.below());
150            below.get_block() == &vanilla_blocks::PALE_MOSS_CARPET
151                && below.get_value(&BlockStateProperties::BOTTOM)
152        }
153    }
154
155    fn get_state_for_placement(&self, context: &BlockPlaceContext<'_>) -> Option<BlockStateId> {
156        let state = Self::updated_state(
157            context.world,
158            self.block.default_state(),
159            context.place_pos(),
160            true,
161        );
162        (self.can_survive(state, context.world, context.place_pos()) && Self::has_faces(state))
163            .then_some(state)
164    }
165}