steel_core/behavior/blocks/vegetation/
pumpkin_block.rs1use 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#[block_behavior]
26pub struct PumpkinBlock {
27 block: BlockRef,
28}
29
30const HORIZONTAL_FACING: &EnumProperty<Direction> = &BlockStateProperties::HORIZONTAL_FACING;
31
32impl PumpkinBlock {
33 #[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}