Skip to main content

steel_core/behavior/blocks/decoration/
banner_block.rs

1use steel_macros::block_behavior;
2use steel_math::{DEGREE_180, convert_to_rotation_segment};
3use steel_registry::REGISTRY;
4use steel_registry::blocks::properties::{
5    BlockStateProperties, Direction, EnumProperty, IntProperty,
6};
7use steel_registry::blocks::{BlockRef, block_state_ext::BlockStateExt};
8use steel_registry::vanilla_blocks;
9use steel_utils::{BlockPos, BlockStateId};
10
11use crate::behavior::{BlockBehavior, BlockPlaceContext};
12use crate::world::{LevelReader, ScheduledTickAccess};
13
14const FACING: &EnumProperty<Direction> = &BlockStateProperties::HORIZONTAL_FACING;
15
16/// Shared behavior for standing banner blocks
17#[block_behavior]
18pub struct BannerBlock {
19    block: BlockRef,
20}
21
22const ROTATION_16: &IntProperty = &BlockStateProperties::ROTATION_16;
23
24impl BannerBlock {
25    /// Creates a new banner block behavior
26    #[must_use]
27    pub const fn new(block: BlockRef) -> Self {
28        Self { block }
29    }
30}
31
32impl BlockBehavior for BannerBlock {
33    fn is_possible_to_respawn_in_this(&self, _state: BlockStateId) -> bool {
34        true
35    }
36
37    fn can_survive(&self, _state: BlockStateId, world: &dyn LevelReader, pos: BlockPos) -> bool {
38        world.get_block_state(pos.below()).is_solid()
39    }
40
41    fn update_shape(
42        &self,
43        state: BlockStateId,
44        world: &dyn ScheduledTickAccess,
45        pos: BlockPos,
46        direction: Direction,
47        _neighbor_pos: BlockPos,
48        _neighbor_state: BlockStateId,
49    ) -> BlockStateId {
50        if direction == Direction::Down && !self.can_survive(state, world, pos) {
51            return REGISTRY.blocks.get_default_state_id(&vanilla_blocks::AIR);
52        }
53        state
54    }
55
56    fn get_state_for_placement(&self, context: &BlockPlaceContext<'_>) -> Option<BlockStateId> {
57        Some(self.block.default_state().set_value(
58            ROTATION_16,
59            convert_to_rotation_segment(context.rotation() + DEGREE_180),
60        ))
61    }
62}
63
64/// Shared behavior for wall banner blocks
65#[block_behavior]
66pub struct WallBannerBlock {
67    block: BlockRef,
68}
69
70impl WallBannerBlock {
71    /// Creates a new wall banner block behavior
72    #[must_use]
73    pub const fn new(block: BlockRef) -> Self {
74        Self { block }
75    }
76}
77
78impl BlockBehavior for WallBannerBlock {
79    fn is_possible_to_respawn_in_this(&self, _state: BlockStateId) -> bool {
80        true
81    }
82
83    fn can_survive(&self, state: BlockStateId, world: &dyn LevelReader, pos: BlockPos) -> bool {
84        let facing = state.get_value(FACING);
85        world
86            .get_block_state(facing.opposite().relative(pos))
87            .is_solid()
88    }
89
90    fn update_shape(
91        &self,
92        state: BlockStateId,
93        world: &dyn ScheduledTickAccess,
94        pos: BlockPos,
95        direction: Direction,
96        _neighbor_pos: BlockPos,
97        _neighbor_state: BlockStateId,
98    ) -> BlockStateId {
99        let facing = state.get_value(FACING);
100        if direction == facing.opposite() && !self.can_survive(state, world, pos) {
101            return REGISTRY.blocks.get_default_state_id(&vanilla_blocks::AIR);
102        }
103        state
104    }
105
106    fn get_state_for_placement(&self, context: &BlockPlaceContext<'_>) -> Option<BlockStateId> {
107        for direction in context.get_nearest_looking_directions() {
108            if !direction.is_horizontal() {
109                continue;
110            }
111
112            let state = self
113                .block
114                .default_state()
115                .set_value(FACING, direction.opposite());
116            if self.can_survive(state, context.world.as_ref(), context.place_pos()) {
117                return Some(state);
118            }
119        }
120
121        None
122    }
123}