110 lines
3.5 KiB
Rust
110 lines
3.5 KiB
Rust
use std::sync::Arc;
|
|
|
|
use super::{Goal, GoalFuture, to_goal_ticks};
|
|
use crate::entity::mob::Mob;
|
|
use crate::entity::mob::enderman::EndermanEntity;
|
|
use pumpkin_data::BlockStateId;
|
|
use pumpkin_data::tag::{self, Taggable};
|
|
use pumpkin_util::math::{position::BlockPos, vector3::Vector3};
|
|
use pumpkin_world::world::BlockFlags;
|
|
use rand::RngExt;
|
|
|
|
pub struct PickUpBlockGoal {
|
|
enderman: Arc<EndermanEntity>,
|
|
}
|
|
|
|
impl PickUpBlockGoal {
|
|
pub const fn new(enderman: Arc<EndermanEntity>) -> Self {
|
|
Self { enderman }
|
|
}
|
|
}
|
|
|
|
impl Goal for PickUpBlockGoal {
|
|
fn can_start<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, bool> {
|
|
Box::pin(async move {
|
|
if self.enderman.get_carried_block().is_some() {
|
|
return false;
|
|
}
|
|
|
|
let entity = &mob.get_mob_entity().living_entity.entity;
|
|
let world = entity.world.load();
|
|
if !world.level_info.load().game_rules.mob_griefing {
|
|
return false;
|
|
}
|
|
|
|
if mob.get_random().random_range(0..to_goal_ticks(20)) != 0 {
|
|
return false;
|
|
}
|
|
|
|
true
|
|
})
|
|
}
|
|
|
|
fn tick<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> {
|
|
Box::pin(async move {
|
|
let entity = &mob.get_mob_entity().living_entity.entity;
|
|
let pos = entity.pos.load();
|
|
|
|
let (bx, by, bz) = {
|
|
let mut rng = mob.get_random();
|
|
(
|
|
pos.x.floor() as i32 + rng.random_range(-2..=2),
|
|
pos.y.floor() as i32 + rng.random_range(0..=2),
|
|
pos.z.floor() as i32 + rng.random_range(-2..=2),
|
|
)
|
|
};
|
|
|
|
let world = entity.world.load();
|
|
let target_pos = BlockPos::new(bx, by, bz);
|
|
|
|
let block = world.get_block(&target_pos);
|
|
|
|
if !block.has_tag(&tag::Block::MINECRAFT_ENDERMAN_HOLDABLE) {
|
|
return;
|
|
}
|
|
|
|
let enderman_block = entity.block_pos.load();
|
|
let enderman_center = Vector3::new(
|
|
enderman_block.0.x as f64 + 0.5,
|
|
by as f64 + 0.5,
|
|
enderman_block.0.z as f64 + 0.5,
|
|
);
|
|
let block_center = Vector3::new(bx as f64 + 0.5, by as f64 + 0.5, bz as f64 + 0.5);
|
|
if let Some((hit_pos, _)) = world
|
|
.raycast(enderman_center, block_center, async |block_pos, w| {
|
|
let state = w.get_block_state(block_pos);
|
|
state.is_solid()
|
|
})
|
|
.await
|
|
&& hit_pos != target_pos
|
|
{
|
|
return;
|
|
}
|
|
|
|
let default_state_id = block.default_state.id;
|
|
|
|
let mut event = crate::plugin::api::events::entity::entity_change_block::EntityChangeBlockEvent::new(
|
|
entity.entity_id,
|
|
target_pos,
|
|
"minecraft:air".to_string(),
|
|
);
|
|
if let Some(server) = world.server.upgrade() {
|
|
server.plugin_manager.fire(&server, &mut event).await;
|
|
}
|
|
if event.cancelled {
|
|
return;
|
|
}
|
|
|
|
// TODO: Emit game event (BLOCK_DESTROY)
|
|
world
|
|
.set_block_state(&target_pos, BlockStateId::AIR, BlockFlags::NOTIFY_ALL)
|
|
.await;
|
|
self.enderman.set_carried_block(Some(default_state_id));
|
|
})
|
|
}
|
|
|
|
fn should_continue<'a>(&'a self, _mob: &'a dyn Mob) -> GoalFuture<'a, bool> {
|
|
Box::pin(async { false })
|
|
}
|
|
}
|