Skip to main content

steel_core/behavior/items/
shovel.rs

1use steel_macros::item_behavior;
2use steel_registry::{
3    blocks::{
4        Block,
5        block_state_ext::BlockStateExt,
6        properties::{BlockStateProperties, BoolProperty},
7    },
8    level_events, sound_events,
9    vanilla_block_tags::BlockTag,
10    vanilla_blocks, vanilla_game_events,
11};
12use steel_utils::Direction;
13use steel_utils::types::UpdateFlags;
14
15use crate::{
16    behavior::{InteractionResult, ItemBehavior, UseOnContext},
17    entity::Entity,
18    world::game_event::GameEventContext,
19};
20
21const FLATTENABLES: [&Block; 6] = [
22    &vanilla_blocks::GRASS_BLOCK,
23    &vanilla_blocks::DIRT,
24    &vanilla_blocks::PODZOL,
25    &vanilla_blocks::COARSE_DIRT,
26    &vanilla_blocks::MYCELIUM,
27    &vanilla_blocks::ROOTED_DIRT,
28];
29
30const LIT_PROPERTY: BoolProperty = BlockStateProperties::LIT;
31
32/// Behavior for Shovels, extinguishes campfires and turns grass blocks into paths
33#[item_behavior]
34pub struct ShovelItem;
35
36impl ItemBehavior for ShovelItem {
37    fn use_on(&self, context: &mut UseOnContext) -> InteractionResult {
38        if context.hit_result.direction == Direction::Down {
39            return InteractionResult::Pass;
40        }
41
42        let block_state = context.world.get_block_state(context.hit_result.block_pos);
43        let block = block_state.get_block();
44
45        // Flattenables — vanilla checks these first
46        if FLATTENABLES.contains(&block) {
47            if !context
48                .world
49                .get_block_state(context.hit_result.block_pos.above())
50                .is_air()
51            {
52                return InteractionResult::Pass;
53            }
54            context.world.play_block_sound(
55                &sound_events::ITEM_SHOVEL_FLATTEN,
56                context.hit_result.block_pos,
57                1.0,
58                1.0,
59                Some(context.player.id()),
60            );
61            let infinite_materials = context.player.has_infinite_materials();
62            context
63                .inv
64                .with_item(|item| item.hurt_and_break(1, infinite_materials));
65            let updated_state = vanilla_blocks::DIRT_PATH.default_state();
66            context.world.set_block(
67                context.hit_result.block_pos,
68                updated_state,
69                UpdateFlags::UPDATE_ALL_IMMEDIATE,
70            );
71            context.world.game_event(
72                &vanilla_game_events::BLOCK_CHANGE,
73                context.hit_result.block_pos,
74                &GameEventContext::new(Some(context.player), Some(updated_state)),
75            );
76            return InteractionResult::Success;
77        }
78
79        // Campfire extinguishing
80        if block.has_tag(&BlockTag::CAMPFIRES) {
81            if !block_state.get_value(&LIT_PROPERTY) {
82                return InteractionResult::Pass;
83            }
84            context.world.level_event(
85                level_events::SOUND_EXTINGUISH_FIRE,
86                context.hit_result.block_pos,
87                0,
88                None,
89            );
90            let updated_state = block_state.set_value(&LIT_PROPERTY, false);
91            context.world.set_block(
92                context.hit_result.block_pos,
93                updated_state,
94                UpdateFlags::UPDATE_ALL_IMMEDIATE,
95            );
96            // TODO: hurt_and_break(1, ...) — shovels take durability damage
97            context.world.game_event(
98                &vanilla_game_events::BLOCK_CHANGE,
99                context.hit_result.block_pos,
100                &GameEventContext::new(Some(context.player), Some(updated_state)),
101            );
102            return InteractionResult::Success;
103        }
104
105        InteractionResult::Pass
106    }
107}