147 lines
3.7 KiB
Rust
147 lines
3.7 KiB
Rust
use core::f32;
|
|
use std::sync::{
|
|
Arc,
|
|
atomic::{AtomicU32, Ordering},
|
|
};
|
|
|
|
use async_trait::async_trait;
|
|
use pumpkin_data::{damage::DamageType, entity::EntityType};
|
|
use pumpkin_util::math::vector3::Vector3;
|
|
use uuid::Uuid;
|
|
|
|
use crate::{server::Server, world::World};
|
|
|
|
use super::{Entity, EntityBase, living::LivingEntity, player::Player};
|
|
|
|
pub struct ExperienceOrbEntity {
|
|
entity: Entity,
|
|
amount: u32,
|
|
orb_age: AtomicU32,
|
|
}
|
|
|
|
impl ExperienceOrbEntity {
|
|
pub fn new(entity: Entity, amount: u32) -> Self {
|
|
entity.yaw.store(rand::random::<f32>() * 360.0);
|
|
Self {
|
|
entity,
|
|
amount,
|
|
orb_age: AtomicU32::new(0),
|
|
}
|
|
}
|
|
|
|
pub async fn spawn(world: &Arc<World>, position: Vector3<f64>, amount: u32) {
|
|
let mut amount = amount;
|
|
while amount > 0 {
|
|
let i = Self::round_to_orb_size(amount);
|
|
amount -= i;
|
|
let entity = Entity::new(
|
|
Uuid::new_v4(),
|
|
world.clone(),
|
|
position,
|
|
&EntityType::EXPERIENCE_ORB,
|
|
false,
|
|
);
|
|
let orb = Arc::new(Self::new(entity, i));
|
|
world.spawn_entity(orb).await;
|
|
}
|
|
}
|
|
|
|
fn round_to_orb_size(value: u32) -> u32 {
|
|
if value >= 2477 {
|
|
2477
|
|
} else if value >= 1237 {
|
|
1237
|
|
} else if value >= 617 {
|
|
617
|
|
} else if value >= 307 {
|
|
307
|
|
} else if value >= 149 {
|
|
149
|
|
} else if value >= 73 {
|
|
73
|
|
} else if value >= 37 {
|
|
37
|
|
} else if value >= 17 {
|
|
17
|
|
} else if value >= 7 {
|
|
7
|
|
} else if value >= 3 {
|
|
3
|
|
} else {
|
|
1
|
|
}
|
|
}
|
|
}
|
|
|
|
#[async_trait]
|
|
impl EntityBase for ExperienceOrbEntity {
|
|
async fn tick(&self, caller: Arc<dyn EntityBase>, server: &Server) {
|
|
let entity = &self.entity;
|
|
entity.tick(caller.clone(), server).await;
|
|
let bounding_box = entity.bounding_box.load();
|
|
|
|
let original_velo = entity.velocity.load();
|
|
|
|
let mut velo = original_velo;
|
|
|
|
let no_clip = !self
|
|
.entity
|
|
.world
|
|
.read()
|
|
.await
|
|
.is_space_empty(bounding_box.expand(-1.0e-7, -1.0e-7, -1.0e-7))
|
|
.await;
|
|
// TODO: isSubmergedIn
|
|
if !no_clip {
|
|
velo.y -= self.get_gravity();
|
|
}
|
|
|
|
entity.velocity.store(velo);
|
|
|
|
entity.move_entity(caller.clone(), velo).await;
|
|
|
|
entity.tick_block_collisions(&caller, server).await;
|
|
|
|
let age = self.orb_age.fetch_add(1, Ordering::Relaxed);
|
|
if age >= 6000 {
|
|
self.entity.remove().await;
|
|
}
|
|
}
|
|
|
|
fn get_entity(&self) -> &Entity {
|
|
&self.entity
|
|
}
|
|
|
|
async fn on_player_collision(&self, player: &Arc<Player>) {
|
|
if player.living_entity.health.load() > 0.0 {
|
|
let mut delay = player.experience_pick_up_delay.lock().await;
|
|
if *delay == 0 {
|
|
*delay = 2;
|
|
player.living_entity.pickup(&self.entity, 1).await;
|
|
player.add_experience_points(self.amount as i32).await;
|
|
// TODO: pickingCount for merging
|
|
self.entity.remove().await;
|
|
}
|
|
}
|
|
}
|
|
|
|
async fn damage_with_context(
|
|
&self,
|
|
_amount: f32,
|
|
_damage_type: DamageType,
|
|
_position: Option<Vector3<f64>>,
|
|
_source: Option<&dyn EntityBase>,
|
|
_cause: Option<&dyn EntityBase>,
|
|
) -> bool {
|
|
false
|
|
}
|
|
|
|
fn get_living_entity(&self) -> Option<&LivingEntity> {
|
|
None
|
|
}
|
|
|
|
fn get_gravity(&self) -> f64 {
|
|
0.03
|
|
}
|
|
}
|