Skip to main content

steel_core/behavior/blocks/container/
beacon_block.rs

1//! Beacon block behavior implementation.
2//!
3//! Opens the beacon menu when right-clicked and owns the block entity and its
4//! server ticker.
5
6use std::sync::{Arc, Weak};
7
8use steel_macros::block_behavior;
9use steel_registry::block_entity_type::BlockEntityTypeRef;
10use steel_registry::blocks::BlockRef;
11use steel_registry::{DyeColor, vanilla_block_entity_types, vanilla_custom_stats};
12use steel_utils::{BlockPos, BlockStateId, Downcast as _, 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, BlockEntityTicker, entities::BeaconBlockEntity};
19use crate::inventory::menu::kinds::beacon;
20use crate::player::Player;
21use crate::world::World;
22
23/// Behavior for the beacon block.
24#[block_behavior]
25pub struct BeaconBlock {
26    block: BlockRef,
27}
28
29impl BeaconBlock {
30    /// Creates a new beacon block behavior.
31    #[must_use]
32    pub const fn new(block: BlockRef) -> Self {
33        Self { block }
34    }
35}
36
37impl BlockBehavior for BeaconBlock {
38    fn get_state_for_placement(&self, _context: &BlockPlaceContext<'_>) -> Option<BlockStateId> {
39        Some(self.block.default_state())
40    }
41
42    fn use_without_item(
43        &self,
44        _state: BlockStateId,
45        world: &Arc<World>,
46        pos: BlockPos,
47        player: &Player,
48        _hit_result: &BlockHitResult,
49        _inv: &mut InventoryAccess,
50    ) -> InteractionResult {
51        let Some(block_entity) = world.get_block_entity(pos) else {
52            return InteractionResult::Success;
53        };
54        let Some(beacon_entity) = block_entity.downcast_ref::<BeaconBlockEntity>() else {
55            return InteractionResult::Success;
56        };
57        let state = beacon_entity.state();
58        let base = beacon_entity.base_handle();
59
60        let inventory = player.inventory.clone();
61        let world = Arc::clone(world);
62        player.open_menu(
63            TextComponent::translated(translations::CONTAINER_BEACON.msg()),
64            move |context| beacon(inventory, context.container_id, pos, &world, state, base),
65        );
66
67        player.award_custom_stat(&vanilla_custom_stats::INTERACT_WITH_BEACON);
68        InteractionResult::Success
69    }
70
71    /// Vanilla `BeaconBlock implements BeaconBeamBlock` with `DyeColor.WHITE`; the beacon is the
72    /// first block its own scan visits, so this seeds the beam's initial section.
73    fn beacon_beam_color(&self, _state: BlockStateId) -> Option<DyeColor> {
74        Some(DyeColor::White)
75    }
76
77    fn new_block_entity(
78        &self,
79        level: Weak<World>,
80        pos: BlockPos,
81        state: BlockStateId,
82    ) -> BlockEntityCreation {
83        BlockEntityCreation::from_registered_factory(BLOCK_ENTITIES.create(
84            &vanilla_block_entity_types::BEACON,
85            level,
86            pos,
87            state,
88        ))
89    }
90
91    fn get_block_entity_ticker(
92        &self,
93        _world: &Arc<World>,
94        _state: BlockStateId,
95        block_entity_type: BlockEntityTypeRef,
96    ) -> Option<BlockEntityTicker> {
97        BlockEntityTicker::for_matching_entity_tick(
98            block_entity_type,
99            &vanilla_block_entity_types::BEACON,
100        )
101    }
102}