steel_core/behavior/blocks/vegetation/
mossy_carpet_block.rs1use 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#[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 #[must_use]
30 pub const fn new(block: BlockRef) -> Self {
31 Self { block }
32 }
33
34 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 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 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 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}