Skip to main content

steel_core/behavior/blocks/vegetation/
pumpkin_block.rs

1use crate::behavior::block::drop_from_block_interact_loot_table;
2use crate::behavior::{
3    BlockBehavior, BlockPlaceContext, BlockRef, InteractionResult, InventoryAccess,
4};
5use crate::entity::Entity;
6use crate::player::Player;
7use crate::world::World;
8use crate::world::game_event::GameEventContext;
9use glam::DVec3;
10use rand::RngExt;
11use std::sync::Arc;
12use steel_macros::block_behavior;
13use steel_registry::blocks::block_state_ext::BlockStateExt;
14use steel_registry::blocks::properties::BlockStateProperties;
15use steel_registry::items::item::BlockHitResult;
16use steel_registry::{
17    sound_events, vanilla_blocks, vanilla_game_events, vanilla_items, vanilla_loot_tables,
18};
19use steel_utils::axis::Axis;
20use steel_utils::types::{InteractionHand, UpdateFlags};
21use steel_utils::{BlockPos, BlockStateId};
22
23/// Behavior for pumpkins.
24#[block_behavior]
25pub struct PumpkinBlock {
26    block: BlockRef,
27}
28
29impl PumpkinBlock {
30    /// Creates a pumpkin block behavior.
31    #[must_use]
32    pub const fn new(block: BlockRef) -> Self {
33        Self { block }
34    }
35}
36
37impl BlockBehavior for PumpkinBlock {
38    fn get_state_for_placement(&self, _context: &BlockPlaceContext<'_>) -> Option<BlockStateId> {
39        Some(self.block.default_state())
40    }
41
42    fn use_item_on(
43        &self,
44        state: BlockStateId,
45        world: &Arc<World>,
46        pos: BlockPos,
47        player: &Player,
48        _hand: InteractionHand,
49        hit_result: &BlockHitResult,
50        inv: &mut InventoryAccess,
51    ) -> InteractionResult {
52        let mut rng = rand::rng();
53
54        let Some(drops) = inv.with_item(|item_stack| {
55            if !item_stack.is(&vanilla_items::SHEARS) {
56                return None;
57            }
58
59            Some(drop_from_block_interact_loot_table(
60                &vanilla_loot_tables::CARVE_PUMPKIN,
61                state,
62                world.get_block_entity(pos),
63                Some(item_stack),
64                Some(player),
65                &mut rng,
66            ))
67        }) else {
68            return InteractionResult::TryEmptyHandInteraction;
69        };
70
71        let clicked_direction = hit_result.direction;
72        let direction = if clicked_direction.axis() == Axis::Y {
73            player.direction_yaw().opposite()
74        } else {
75            clicked_direction
76        };
77
78        let (x_offset, z_offset) = {
79            let (x, _, z) = direction.offset();
80            (f64::from(x), f64::from(z))
81        };
82
83        for drop in drops {
84            world.spawn_item_with_velocity(
85                DVec3::new(
86                    f64::from(pos.x()) + 0.5 + x_offset * 0.65,
87                    f64::from(pos.y()) + 0.1,
88                    f64::from(pos.z()) + 0.5 + z_offset * 0.65,
89                ),
90                drop,
91                DVec3::new(
92                    0.05 * x_offset + rng.random::<f64>() * 0.02,
93                    0.05,
94                    0.05 * z_offset + rng.random::<f64>() * 0.02,
95                ),
96            );
97        }
98
99        world.play_block_sound(&sound_events::BLOCK_PUMPKIN_CARVE, pos, 1.0, 1.0, None);
100        world.set_block(
101            pos,
102            vanilla_blocks::CARVED_PUMPKIN
103                .default_state()
104                // TODO: Once CarvedPumpkinBlock is implemented, replace HORIZONTAL_FACING with FACING of CarvedPumpkinBlock.
105                .set_value(&BlockStateProperties::HORIZONTAL_FACING, direction),
106            UpdateFlags::UPDATE_IMMEDIATE
107                | UpdateFlags::UPDATE_CLIENTS
108                | UpdateFlags::UPDATE_NEIGHBORS,
109        );
110        let has_infinite_materials = player.has_infinite_materials();
111        inv.with_item(|item_stack| item_stack.hurt_and_break(1, has_infinite_materials));
112
113        world.game_event(
114            &vanilla_game_events::SHEAR,
115            pos,
116            &GameEventContext::new(Some(player), None),
117        );
118
119        // TODO: Award statistic ITEM_USED with SHEARS.
120
121        InteractionResult::Success
122    }
123}