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