steel_core/behavior/blocks/decoration/
banner_block.rs1use 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#[block_behavior]
18pub struct BannerBlock {
19 block: BlockRef,
20}
21
22const ROTATION_16: &IntProperty = &BlockStateProperties::ROTATION_16;
23
24impl BannerBlock {
25 #[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#[block_behavior]
66pub struct WallBannerBlock {
67 block: BlockRef,
68}
69
70impl WallBannerBlock {
71 #[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}