steel_core/behavior/blocks/container/
barrel_block.rs1use std::sync::{Arc, Weak};
6
7use steel_macros::block_behavior;
8use steel_registry::blocks::BlockRef;
9use steel_registry::blocks::block_state_ext::BlockStateExt;
10use steel_registry::blocks::properties::{BlockStateProperties, Direction, EnumProperty};
11use steel_registry::{vanilla_block_entity_types, vanilla_custom_stats};
12use steel_utils::{BlockPos, BlockStateId, translations};
13use text_components::TextComponent;
14
15use crate::behavior::InventoryAccess;
16use crate::behavior::block::{BlockBehavior, BlockEntityCreation};
17use crate::behavior::context::{BlockHitResult, BlockPlaceContext, InteractionResult};
18use crate::block_entity::BLOCK_ENTITIES;
19use crate::inventory::container::calculate_redstone_signal_from_container;
20use crate::inventory::lock::{ContainerLockGuard, ContainerRef};
21use crate::inventory::menu::kinds::chest;
22use crate::player::Player;
23use crate::world::{LevelReader, World};
24
25#[block_behavior]
30pub struct BarrelBlock {
31 block: BlockRef,
32}
33
34const FACING: &EnumProperty<Direction> = &BlockStateProperties::FACING;
35
36impl BarrelBlock {
37 #[must_use]
39 pub const fn new(block: BlockRef) -> Self {
40 Self { block }
41 }
42}
43
44impl BlockBehavior for BarrelBlock {
45 fn get_state_for_placement(&self, context: &BlockPlaceContext<'_>) -> Option<BlockStateId> {
46 let facing = context.get_nearest_looking_direction().opposite();
48
49 Some(self.block.default_state().set_value(FACING, facing))
50 }
51
52 fn use_without_item(
53 &self,
54 _state: BlockStateId,
55 world: &Arc<World>,
56 pos: BlockPos,
57 player: &Player,
58 _hit_result: &BlockHitResult,
59 _inv: &mut InventoryAccess,
60 ) -> InteractionResult {
61 let Some(block_entity) = world.get_block_entity(pos) else {
63 return InteractionResult::Pass;
64 };
65
66 let Some(container_ref) = ContainerRef::from_block_entity(block_entity) else {
68 return InteractionResult::Pass;
69 };
70
71 let inventory = player.inventory.clone();
73 player.open_menu(
74 TextComponent::translated(translations::CONTAINER_BARREL.msg()),
75 move |context| chest(inventory, context.container_id, container_ref, 3),
76 );
77
78 player.award_custom_stat(&vanilla_custom_stats::OPEN_BARREL);
79 InteractionResult::Success
85 }
86
87 fn new_block_entity(
88 &self,
89 level: Weak<World>,
90 pos: BlockPos,
91 state: BlockStateId,
92 ) -> BlockEntityCreation {
93 BlockEntityCreation::from_registered_factory(BLOCK_ENTITIES.create(
94 &vanilla_block_entity_types::BARREL,
95 level,
96 pos,
97 state,
98 ))
99 }
100
101 fn has_analog_output_signal(&self, _state: BlockStateId) -> bool {
102 true
103 }
104
105 fn get_analog_output_signal(
106 &self,
107 _state: BlockStateId,
108 world: &dyn LevelReader,
109 pos: BlockPos,
110 _direction: Direction,
111 ) -> i32 {
112 let Some(container_ref) = world
114 .get_block_entity(pos)
115 .and_then(ContainerRef::from_block_entity)
116 else {
117 return 0;
118 };
119 let guard = ContainerLockGuard::lock_all(&[&container_ref]);
120 guard
121 .get(container_ref.container_id())
122 .map_or(0, |container| {
123 calculate_redstone_signal_from_container(container)
124 })
125 }
126}