Skip to main content

steel_core/behavior/items/
ender_pearl.rs

1//! Ender pearl item behavior (`EnderpearlItem`).
2//!
3//! Throwing an ender pearl spawns a [`EnderPearlEntity`] from the player's eye,
4//! shot along their look direction, and consumes one pearl unless the player
5//! has infinite materials. Mirrors vanilla `EnderpearlItem.use`.
6
7use std::sync::Arc;
8
9use steel_macros::item_behavior;
10use steel_protocol::packets::game::SoundSource;
11use steel_registry::stat::vanilla_stat_types;
12use steel_registry::{sound_events, vanilla_entities};
13
14use crate::behavior::context::{InteractionResult, UseItemContext};
15use crate::behavior::item::ItemBehavior;
16use crate::entity::entities::EnderPearlEntity;
17use crate::entity::{Entity, next_entity_id, spawn_throwable_item_projectile};
18
19/// Vanilla `EnderpearlItem.PROJECTILE_SHOOT_POWER`.
20const SHOOT_POWER: f32 = 1.5;
21/// Vanilla `EnderpearlItem.use` throw uncertainty (`spawnProjectileFromRotation`).
22const THROW_UNCERTAINTY: f32 = 1.0;
23
24/// Behavior for the ender pearl item.
25#[item_behavior(class = "EnderpearlItem")]
26pub struct EnderPearlItem;
27
28impl ItemBehavior for EnderPearlItem {
29    fn use_item(&self, context: &mut UseItemContext) -> InteractionResult {
30        let player = context.player;
31        let world = context.world;
32
33        let pitch = 0.4 / rand::random_range(0.8..1.2);
34        world.play_sound_at(
35            &sound_events::ENTITY_ENDER_PEARL_THROW,
36            SoundSource::Neutral,
37            player.position(),
38            0.5,
39            pitch,
40            None,
41        );
42
43        let mut thrown_item = context.inv.with_item(|item| item.clone());
44        let Some(entity) = spawn_throwable_item_projectile(
45            world,
46            player,
47            &mut thrown_item,
48            SHOOT_POWER,
49            THROW_UNCERTAINTY,
50            |spawn_pos| {
51                EnderPearlEntity::new(
52                    &vanilla_entities::ENDER_PEARL,
53                    next_entity_id(),
54                    spawn_pos,
55                    Arc::downgrade(world),
56                )
57            },
58        ) else {
59            return InteractionResult::Fail;
60        };
61        player.register_ender_pearl(&entity);
62
63        player.award_stat(&vanilla_stat_types::ITEM_USED, thrown_item.item);
64        let has_infinite_materials = player.has_infinite_materials();
65        context
66            .inv
67            .with_item(|item| item.consume_one(has_infinite_materials));
68
69        InteractionResult::Success
70    }
71}