Pumpkin/pumpkin/src/item/items/ender_eye.rs
2026-05-15 18:24:28 +02:00

151 lines
4.8 KiB
Rust

use std::pin::Pin;
use std::sync::Arc;
use crate::entity::Entity;
use crate::entity::projectile::eye_of_ender::EyeOfEnder;
use crate::item::{ItemBehaviour, ItemMetadata};
use crate::server::Server;
use crate::world::World;
use crate::world::portal::end::EndPortal;
use pumpkin_data::entity::EntityType;
use pumpkin_data::item::Item;
use pumpkin_data::item_stack::ItemStack;
use pumpkin_data::sound::{Sound, SoundCategory};
use pumpkin_data::structures::StructureSet;
use pumpkin_data::world::WorldEvent;
use pumpkin_data::{Block, BlockDirection};
use pumpkin_util::math::position::BlockPos;
use pumpkin_util::math::vector3::Vector3;
use pumpkin_world::generation::generator::structure_finder::find_nearest_structure;
use pumpkin_world::world::BlockFlags;
use crate::entity::player::Player;
pub struct EnderEyeItem;
impl ItemMetadata for EnderEyeItem {
fn ids() -> Box<[u16]> {
[Item::ENDER_EYE.id].into()
}
}
impl ItemBehaviour for EnderEyeItem {
fn use_on_block<'a>(
&'a self,
item: &'a mut ItemStack,
player: &'a Player,
location: BlockPos,
_face: BlockDirection,
_cursor_pos: Vector3<f32>,
block: &'a Block,
_server: &'a Server,
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
Box::pin(async move {
if block.id != Block::END_PORTAL_FRAME.id {
return;
}
let world = player.world();
let state_id = world.get_block_state_id(&location);
// Skip if the frame already holds an eye.
let props_raw = block.properties(state_id).unwrap().to_props();
if props_raw.iter().any(|(k, v)| *k == "eye" && *v == "true") {
return;
}
// Build new state with eye=true.
let props: Vec<(&str, &str)> = props_raw
.iter()
.map(|(k, v)| if *k == "eye" { (*k, "true") } else { (*k, *v) })
.collect();
let new_state_id = block.from_properties(&props).to_state_id(block);
world
.set_block_state(&location, new_state_id, BlockFlags::NOTIFY_LISTENERS)
.await;
// Consume one item.
item.decrement(1);
world.sync_world_event(WorldEvent::EndPortalFrameFill, location, 0);
// Try to complete the portal.
EndPortal::get_new_portal(&world, location).await;
})
}
fn normal_use<'a>(
&'a self,
_item: &'a Item,
player: &'a Player,
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
Box::pin(async move {
let world = player.world();
let (start_pos, end_pos) = self.get_start_and_end_pos(player);
let checker = async |pos: &BlockPos, w: &Arc<World>| {
w.get_block_state_id(pos) != Block::AIR.default_state.id
};
if let Some((hit_pos, _)) = world.raycast(start_pos, end_pos, checker).await
&& world.get_block(&hit_pos) == &Block::END_PORTAL_FRAME
{
return;
}
let origin = player.living_entity.entity.block_pos.load();
let target_block_pos = find_stronghold(&world, origin);
let Some(target) = target_block_pos else {
return;
};
let spawn_pos = Vector3::new(
player.living_entity.entity.pos.load().x,
player.living_entity.entity.pos.load().y
+ f64::from(EntityType::EYE_OF_ENDER.dimension[1]) * 0.5,
player.living_entity.entity.pos.load().z,
);
let entity = Entity::new(world.clone(), spawn_pos, &EntityType::EYE_OF_ENDER);
let eye = Arc::new(EyeOfEnder::new(entity));
let target_vec = Vector3::new(
f64::from(target.0.x),
f64::from(target.0.y),
f64::from(target.0.z),
);
eye.signal_to(target_vec).await;
world.spawn_entity(eye).await;
let pitch = 0.33f32 + rand::random::<f32>() * (0.5 - 0.33);
world.play_sound_fine(
Sound::EntityEnderEyeLaunch,
SoundCategory::Neutral,
&spawn_pos,
1.0,
pitch,
);
player.inventory.held_item().lock().await.decrement(1);
})
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
}
fn find_stronghold(world: &Arc<World>, origin: BlockPos) -> Option<BlockPos> {
let level = &world.level;
let generator = &level.world_gen;
let seed = level.seed.0;
find_nearest_structure(
origin,
&[&StructureSet::get("strongholds").unwrap().placement],
100, // max search radius in chunks, matches vanilla default
seed as i64,
&generator.global_structure_cache,
)
}