steel_core/behavior/items/
lead.rs1use crate::behavior::{InteractionResult, ItemBehavior, UseOnContext};
2use crate::entity::entities::LeashFenceKnotEntity;
3use crate::entity::{RemovalReason, leashables_leashed_to_holder_in_area_near_position};
4use crate::player::Player;
5use crate::world::World;
6use crate::world::game_event::GameEventContext;
7use std::sync::Arc;
8use steel_macros::item_behavior;
9use steel_registry::blocks::block_state_ext::BlockStateExt;
10use steel_registry::vanilla_block_tags::BlockTag;
11use steel_registry::{sound_events, vanilla_game_events};
12use steel_utils::BlockPos;
13
14#[item_behavior]
20pub struct LeadItem;
21
22impl LeadItem {
23 pub fn bind_player_mobs(
25 player: &Player,
26 world: &Arc<World>,
27 pos: BlockPos,
28 ) -> InteractionResult {
29 let entities_to_leash = leashables_leashed_to_holder_in_area_near_position(
30 world,
31 pos.get_center().into(),
32 player,
33 );
34 if entities_to_leash.is_empty() {
35 return InteractionResult::Pass;
36 }
37
38 let existing_knot_exists = LeashFenceKnotEntity::get_knot(world, pos).is_some();
39 let Some(knot) = LeashFenceKnotEntity::get_or_create_knot(world, pos) else {
40 return InteractionResult::Pass;
41 };
42 let mut any_leashed = false;
43
44 for entity in entities_to_leash {
45 if let Some(leashable) = entity.as_leashable()
46 && leashable.can_have_a_leash_attached_to(knot.as_ref())
47 {
48 leashable.set_leashed_to(&knot);
49 any_leashed = true;
50 }
51 }
52
53 if any_leashed {
54 knot.play_sound(&sound_events::ITEM_LEAD_TIED, 1.0, 1.0);
55 world.game_event(
56 &vanilla_game_events::BLOCK_ATTACH,
57 pos,
58 &GameEventContext::new(Some(player), None),
59 );
60 InteractionResult::SuccessServer
61 } else {
62 if !existing_knot_exists {
63 knot.set_removed(RemovalReason::Discarded);
64 }
65 InteractionResult::Pass
66 }
67 }
68}
69
70impl ItemBehavior for LeadItem {
71 fn use_on(&self, context: &mut UseOnContext) -> InteractionResult {
72 let pos = context.hit_result.block_pos;
73 let state = context.world.get_block_state(pos);
74 if state.get_block().has_tag(&BlockTag::FENCES) {
75 Self::bind_player_mobs(context.player, context.world, pos)
76 } else {
77 InteractionResult::Pass
78 }
79 }
80}