Skip to main content

steel_core/behavior/blocks/building/
spawner_block.rs

1//! Normal mob-spawner block behavior.
2
3use std::sync::{Arc, Weak};
4
5use steel_macros::block_behavior;
6use steel_registry::block_entity_type::BlockEntityTypeRef;
7use steel_registry::blocks::BlockRef;
8use steel_registry::item_stack::ItemStack;
9use steel_registry::vanilla_block_entity_types;
10use steel_utils::{BlockPos, BlockStateId};
11
12use crate::behavior::{BlockBehavior, BlockEntityCreation, BlockPlaceContext};
13use crate::block_entity::{BLOCK_ENTITIES, BlockEntityTicker};
14use crate::world::World;
15
16/// Behavior for normal mob spawner blocks.
17#[block_behavior]
18pub struct SpawnerBlock {
19    block: BlockRef,
20}
21
22impl SpawnerBlock {
23    /// Guaranteed experience dropped when a spawner is broken.
24    const BASE_BREAK_EXPERIENCE: i32 = 15;
25    /// Exclusive bound of each of the two random experience bonuses.
26    const BONUS_BREAK_EXPERIENCE_BOUND: i32 = 15;
27
28    /// Creates a spawner block behavior.
29    #[must_use]
30    pub const fn new(block: BlockRef) -> Self {
31        Self { block }
32    }
33}
34
35impl BlockBehavior for SpawnerBlock {
36    fn get_state_for_placement(&self, _context: &BlockPlaceContext<'_>) -> Option<BlockStateId> {
37        Some(self.block.default_state())
38    }
39
40    fn new_block_entity(
41        &self,
42        level: Weak<World>,
43        pos: BlockPos,
44        state: BlockStateId,
45    ) -> BlockEntityCreation {
46        BlockEntityCreation::from_registered_factory(BLOCK_ENTITIES.create(
47            &vanilla_block_entity_types::MOB_SPAWNER,
48            level,
49            pos,
50            state,
51        ))
52    }
53
54    fn get_block_entity_ticker(
55        &self,
56        _world: &Arc<World>,
57        _state: BlockStateId,
58        block_entity_type: BlockEntityTypeRef,
59    ) -> Option<BlockEntityTicker> {
60        BlockEntityTicker::for_matching_entity_tick(
61            block_entity_type,
62            &vanilla_block_entity_types::MOB_SPAWNER,
63        )
64    }
65
66    fn trigger_event(
67        &self,
68        _state: BlockStateId,
69        world: &Arc<World>,
70        pos: BlockPos,
71        event: i32,
72        data: i32,
73    ) -> bool {
74        world
75            .get_block_entity(pos)
76            .is_some_and(|block_entity| block_entity.trigger_event(event, data))
77    }
78
79    fn spawn_after_break(
80        &self,
81        _state: BlockStateId,
82        world: &Arc<World>,
83        pos: BlockPos,
84        _tool: &ItemStack,
85        drop_experience: bool,
86    ) {
87        if drop_experience {
88            let experience = Self::BASE_BREAK_EXPERIENCE
89                + rand::random_range(0..Self::BONUS_BREAK_EXPERIENCE_BOUND)
90                + rand::random_range(0..Self::BONUS_BREAK_EXPERIENCE_BOUND);
91            world.pop_experience(pos, experience);
92        }
93    }
94}