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