steel_core/behavior/items/
hanging_sign_item.rs1use std::sync::Arc;
5use steel_macros::item_behavior;
6use steel_registry::REGISTRY;
7use steel_registry::blocks::BlockRef;
8use steel_registry::blocks::block_state_ext::BlockStateExt;
9use steel_registry::blocks::properties::{BlockStateProperties, Direction};
10use steel_registry::blocks::shapes::SupportType;
11use steel_registry::vanilla_block_tags::BlockTag;
12use steel_utils::{BlockPos, BlockStateId};
13
14use crate::behavior::context::{InteractionResult, UseOnContext};
15use crate::behavior::items::block_item::SurvivalCheck;
16use crate::behavior::{BlockItem, ItemBehavior, StandingAndWallBlockItem};
17use crate::world::{LevelReader as _, World};
18
19#[item_behavior]
23pub struct HangingSignItem {
24 #[json_arg(vanilla_blocks, json = "block")]
26 pub ceiling_block: BlockRef,
27 #[json_arg(vanilla_blocks, json = "wall_block")]
29 pub wall_block: BlockRef,
30
31 base: StandingAndWallBlockItem,
32}
33
34impl HangingSignItem {
35 #[must_use]
37 pub const fn new(ceiling_block: BlockRef, wall_block: BlockRef) -> Self {
38 Self {
39 ceiling_block,
40 wall_block,
41 base: StandingAndWallBlockItem::new(ceiling_block, wall_block, Direction::Up),
42 }
43 }
44}
45
46fn can_attach_to(
50 world: &Arc<World>,
51 sign_facing: Direction,
52 attach_pos: BlockPos,
53 attach_face: Direction,
54) -> bool {
55 let attach_state = world.get_block_state(attach_pos);
56 let attach_block = REGISTRY.blocks.by_state_id(attach_state);
57
58 if let Some(block) = attach_block
59 && block.has_tag(&BlockTag::WALL_HANGING_SIGNS)
60 && let Some(neighbor_facing) =
61 attach_state.try_get_value(&BlockStateProperties::HORIZONTAL_FACING)
62 {
63 return neighbor_facing.axis() == sign_facing.axis();
64 }
65 world.is_face_sturdy_for(attach_state, attach_pos, attach_face, SupportType::Full)
66}
67
68fn can_wall_hanging_sign_place(world: &Arc<World>, state: BlockStateId, pos: BlockPos) -> bool {
73 let Some(facing) = state.try_get_value(&BlockStateProperties::HORIZONTAL_FACING) else {
74 return false;
75 };
76
77 let clockwise = facing.rotate_y_clockwise();
78 let counter_clockwise = facing.rotate_y_counter_clockwise();
79
80 let can_attach_clockwise = {
81 let attach_pos = clockwise.relative(pos);
82 can_attach_to(world, facing, attach_pos, counter_clockwise)
83 };
84
85 let can_attach_counter = {
86 let attach_pos = counter_clockwise.relative(pos);
87 can_attach_to(world, facing, attach_pos, clockwise)
88 };
89
90 can_attach_clockwise || can_attach_counter
91}
92
93fn can_place_hanging_sign(world: &Arc<World>, state: BlockStateId, pos: BlockPos) -> bool {
100 let is_wall_hanging_sign = REGISTRY
101 .blocks
102 .by_state_id(state)
103 .is_some_and(|block| block.has_tag(&BlockTag::WALL_HANGING_SIGNS));
104
105 !is_wall_hanging_sign || can_wall_hanging_sign_place(world, state, pos)
106}
107
108impl ItemBehavior for HangingSignItem {
109 fn use_on(&self, context: &mut UseOnContext) -> InteractionResult {
111 self.base.base.place_with_policy(
112 context.build_place_context(),
113 Some,
114 SurvivalCheck::Skipped,
115 BlockItem::place_block,
116 self.ceiling_block.config.sound_type.place_sound,
117 |context| {
118 self.base
119 .get_placement_state(context, can_place_hanging_sign)
120 },
121 )
122 }
123}