azalea_entity/
lib.rs

1#![allow(clippy::derived_hash_with_manual_eq)]
2
3pub mod attributes;
4mod data;
5mod dimensions;
6mod effects;
7mod enchantments;
8pub mod metadata;
9pub mod mining;
10pub mod particle;
11mod plugin;
12pub mod vec_delta_codec;
13
14use std::{
15    fmt::Debug,
16    hash::{Hash, Hasher},
17};
18
19pub use attributes::Attributes;
20use azalea_block::{fluid_state::FluidKind, BlockState};
21use azalea_buf::AzBuf;
22use azalea_core::{
23    aabb::AABB,
24    math,
25    position::{BlockPos, ChunkPos, Vec3},
26    resource_location::ResourceLocation,
27};
28use azalea_world::{ChunkStorage, InstanceName};
29use bevy_ecs::{bundle::Bundle, component::Component};
30pub use data::*;
31use derive_more::{Deref, DerefMut};
32pub use dimensions::EntityDimensions;
33use plugin::indexing::EntityChunkPos;
34use uuid::Uuid;
35use vec_delta_codec::VecDeltaCodec;
36
37use self::attributes::AttributeInstance;
38pub use crate::plugin::*;
39
40pub fn move_relative(
41    physics: &mut Physics,
42    direction: &LookDirection,
43    speed: f32,
44    acceleration: &Vec3,
45) {
46    let input_vector = input_vector(direction, speed, acceleration);
47    physics.velocity += input_vector;
48}
49
50pub fn input_vector(direction: &LookDirection, speed: f32, acceleration: &Vec3) -> Vec3 {
51    let distance = acceleration.length_squared();
52    if distance < 1.0E-7 {
53        return Vec3::default();
54    }
55    let acceleration = if distance > 1.0 {
56        acceleration.normalize()
57    } else {
58        *acceleration
59    }
60    .scale(speed as f64);
61    let y_rot = math::sin(direction.y_rot * 0.017453292f32);
62    let x_rot = math::cos(direction.y_rot * 0.017453292f32);
63    Vec3 {
64        x: acceleration.x * (x_rot as f64) - acceleration.z * (y_rot as f64),
65        y: acceleration.y,
66        z: acceleration.z * (x_rot as f64) + acceleration.x * (y_rot as f64),
67    }
68}
69
70pub fn view_vector(look_direction: &LookDirection) -> Vec3 {
71    let x_rot = look_direction.x_rot * 0.017453292;
72    let y_rot = -look_direction.y_rot * 0.017453292;
73    let y_rot_cos = math::cos(y_rot);
74    let y_rot_sin = math::sin(y_rot);
75    let x_rot_cos = math::cos(x_rot);
76    let x_rot_sin = math::sin(x_rot);
77    Vec3 {
78        x: (y_rot_sin * x_rot_cos) as f64,
79        y: (-x_rot_sin) as f64,
80        z: (y_rot_cos * x_rot_cos) as f64,
81    }
82}
83
84/// Get the position of the block below the entity, but a little lower.
85pub fn on_pos_legacy(chunk_storage: &ChunkStorage, position: &Position) -> BlockPos {
86    on_pos(0.2, chunk_storage, position)
87}
88
89// int x = Mth.floor(this.position.x);
90// int y = Mth.floor(this.position.y - (double)var1);
91// int z = Mth.floor(this.position.z);
92// BlockPos var5 = new BlockPos(x, y, z);
93// if (this.level.getBlockState(var5).isAir()) {
94//    BlockPos var6 = var5.below();
95//    BlockState var7 = this.level.getBlockState(var6);
96//    if (var7.is(BlockTags.FENCES) || var7.is(BlockTags.WALLS) ||
97// var7.getBlock() instanceof FenceGateBlock) {       return var6;
98//    }
99// }
100// return var5;
101pub fn on_pos(offset: f32, chunk_storage: &ChunkStorage, pos: &Position) -> BlockPos {
102    let x = pos.x.floor() as i32;
103    let y = (pos.y - offset as f64).floor() as i32;
104    let z = pos.z.floor() as i32;
105    let pos = BlockPos { x, y, z };
106
107    // TODO: check if block below is a fence, wall, or fence gate
108    let block_pos = pos.down(1);
109    let block_state = chunk_storage.get_block_state(&block_pos);
110    if block_state == Some(BlockState::AIR) {
111        let block_pos_below = block_pos.down(1);
112        let block_state_below = chunk_storage.get_block_state(&block_pos_below);
113        if let Some(_block_state_below) = block_state_below {
114            // if block_state_below.is_fence()
115            //     || block_state_below.is_wall()
116            //     || block_state_below.is_fence_gate()
117            // {
118            //     return block_pos_below;
119            // }
120        }
121    }
122
123    pos
124}
125
126/// The Minecraft UUID of the entity. For players, this is their actual player
127/// UUID, and for other entities it's just random.
128#[derive(Component, Deref, DerefMut, Clone, Copy, Default)]
129pub struct EntityUuid(Uuid);
130impl EntityUuid {
131    pub fn new(uuid: Uuid) -> Self {
132        Self(uuid)
133    }
134}
135impl Debug for EntityUuid {
136    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
137        (self.0).fmt(f)
138    }
139}
140
141/// The position of the entity right now.
142///
143/// You are free to change this; there's systems that update the indexes
144/// automatically.
145#[derive(Component, Clone, Copy, Debug, Default, PartialEq, Deref, DerefMut)]
146pub struct Position(Vec3);
147impl Position {
148    pub fn new(pos: Vec3) -> Self {
149        Self(pos)
150    }
151}
152impl From<&Position> for Vec3 {
153    fn from(value: &Position) -> Self {
154        value.0
155    }
156}
157impl From<Position> for ChunkPos {
158    fn from(value: Position) -> Self {
159        ChunkPos::from(&value.0)
160    }
161}
162impl From<Position> for BlockPos {
163    fn from(value: Position) -> Self {
164        BlockPos::from(&value.0)
165    }
166}
167impl From<&Position> for ChunkPos {
168    fn from(value: &Position) -> Self {
169        ChunkPos::from(value.0)
170    }
171}
172impl From<&Position> for BlockPos {
173    fn from(value: &Position) -> Self {
174        BlockPos::from(value.0)
175    }
176}
177
178/// The second most recent position of the entity that was sent over the
179/// network. This is currently only updated for our own local player entities.
180#[derive(Component, Clone, Copy, Debug, Default, PartialEq, Deref, DerefMut)]
181pub struct LastSentPosition(Vec3);
182impl From<&LastSentPosition> for Vec3 {
183    fn from(value: &LastSentPosition) -> Self {
184        value.0
185    }
186}
187impl From<LastSentPosition> for ChunkPos {
188    fn from(value: LastSentPosition) -> Self {
189        ChunkPos::from(&value.0)
190    }
191}
192impl From<LastSentPosition> for BlockPos {
193    fn from(value: LastSentPosition) -> Self {
194        BlockPos::from(&value.0)
195    }
196}
197impl From<&LastSentPosition> for ChunkPos {
198    fn from(value: &LastSentPosition) -> Self {
199        ChunkPos::from(value.0)
200    }
201}
202impl From<&LastSentPosition> for BlockPos {
203    fn from(value: &LastSentPosition) -> Self {
204        BlockPos::from(value.0)
205    }
206}
207
208/// A component for entities that can jump.
209///
210/// If this is true, the entity will try to jump every tick. It's equivalent to
211/// the space key being held in vanilla.
212#[derive(Debug, Component, Copy, Clone, Deref, DerefMut, Default)]
213pub struct Jumping(bool);
214
215/// A component that contains the direction an entity is looking.
216#[derive(Debug, Component, Copy, Clone, Default, PartialEq, AzBuf)]
217pub struct LookDirection {
218    /// Left and right. Aka yaw.
219    pub y_rot: f32,
220    /// Up and down. Aka pitch.
221    pub x_rot: f32,
222}
223
224impl LookDirection {
225    pub fn new(y_rot: f32, x_rot: f32) -> Self {
226        Self { y_rot, x_rot }
227    }
228}
229
230impl From<LookDirection> for (f32, f32) {
231    fn from(value: LookDirection) -> Self {
232        (value.y_rot, value.x_rot)
233    }
234}
235impl From<(f32, f32)> for LookDirection {
236    fn from((y_rot, x_rot): (f32, f32)) -> Self {
237        Self { y_rot, x_rot }
238    }
239}
240
241impl Hash for LookDirection {
242    fn hash<H: Hasher>(&self, state: &mut H) {
243        self.y_rot.to_bits().hash(state);
244        self.x_rot.to_bits().hash(state);
245    }
246}
247impl Eq for LookDirection {}
248
249/// The physics data relating to the entity, such as position, velocity, and
250/// bounding box.
251#[derive(Debug, Component, Clone, Default)]
252pub struct Physics {
253    /// How fast the entity is moving.
254    ///
255    /// Sometimes referred to as the delta movement.
256    pub velocity: Vec3,
257    pub vec_delta_codec: VecDeltaCodec,
258
259    /// The position of the entity before it moved this tick.
260    ///
261    /// This is set immediately before physics is done.
262    pub old_position: Vec3,
263
264    /// The acceleration here is the force that will be attempted to be added to
265    /// the entity's velocity next tick.
266    ///
267    /// You should typically not set this yourself, since it's controlled by how
268    /// the entity is trying to move.
269    pub x_acceleration: f32,
270    pub y_acceleration: f32,
271    pub z_acceleration: f32,
272
273    on_ground: bool,
274    last_on_ground: bool,
275
276    /// The number of ticks until we jump again, if the jump key is being held.
277    ///
278    /// This must be 0 for us to be able to jump. Sets to 10 when we do a jump
279    /// and sets to 0 if we're not trying to jump.
280    pub no_jump_delay: u32,
281
282    /// The width and height of the entity.
283    pub dimensions: EntityDimensions,
284    /// The bounding box of the entity. This is more than just width and height,
285    /// unlike dimensions.
286    pub bounding_box: AABB,
287
288    pub has_impulse: bool,
289
290    pub horizontal_collision: bool,
291    // pub minor_horizontal_collision: bool,
292    pub vertical_collision: bool,
293
294    pub water_fluid_height: f64,
295    pub lava_fluid_height: f64,
296    pub was_touching_water: bool,
297
298    // TODO: implement fall_distance
299    pub fall_distance: f32,
300    // TODO: implement remaining_fire_ticks
301    pub remaining_fire_ticks: i32,
302}
303
304impl Physics {
305    pub fn new(dimensions: EntityDimensions, pos: Vec3) -> Self {
306        Self {
307            velocity: Vec3::default(),
308            vec_delta_codec: VecDeltaCodec::new(pos),
309
310            old_position: pos,
311
312            x_acceleration: 0.,
313            y_acceleration: 0.,
314            z_acceleration: 0.,
315
316            on_ground: false,
317            last_on_ground: false,
318
319            no_jump_delay: 0,
320
321            bounding_box: dimensions.make_bounding_box(&pos),
322            dimensions,
323
324            has_impulse: false,
325
326            horizontal_collision: false,
327            vertical_collision: false,
328
329            water_fluid_height: 0.,
330            lava_fluid_height: 0.,
331            was_touching_water: false,
332
333            fall_distance: 0.,
334            remaining_fire_ticks: 0,
335        }
336    }
337
338    pub fn on_ground(&self) -> bool {
339        self.on_ground
340    }
341    /// Updates [`Self::on_ground`] and [`Self::last_on_ground`].
342    pub fn set_on_ground(&mut self, on_ground: bool) {
343        self.last_on_ground = self.on_ground;
344        self.on_ground = on_ground;
345    }
346
347    /// The last value of the on_ground value.
348    ///
349    /// This is used by Azalea internally for physics, it might not work as you
350    /// expect since it can be influenced by packets sent by the server.
351    pub fn last_on_ground(&self) -> bool {
352        self.last_on_ground
353    }
354    pub fn set_last_on_ground(&mut self, last_on_ground: bool) {
355        self.last_on_ground = last_on_ground;
356    }
357
358    pub fn reset_fall_distance(&mut self) {
359        self.fall_distance = 0.;
360    }
361    pub fn clear_fire(&mut self) {
362        self.remaining_fire_ticks = 0;
363    }
364
365    pub fn is_in_water(&self) -> bool {
366        self.was_touching_water
367    }
368    pub fn is_in_lava(&self) -> bool {
369        // TODO: also check `!this.firstTick &&`
370        self.lava_fluid_height > 0.
371    }
372
373    pub fn set_old_pos(&mut self, pos: &Position) {
374        self.old_position = **pos;
375    }
376}
377
378/// Marker component for entities that are dead.
379///
380/// "Dead" means that the entity has 0 health.
381#[derive(Component, Copy, Clone, Default)]
382pub struct Dead;
383
384/// A component that contains the offset of the entity's eyes from the entity
385/// coordinates.
386///
387/// This is used to calculate the camera position for players, when spectating
388/// an entity, and when raycasting from the entity.
389#[derive(Component, Clone, Copy, Debug, PartialEq, Deref, DerefMut)]
390pub struct EyeHeight(f32);
391impl EyeHeight {
392    pub fn new(height: f32) -> Self {
393        Self(height)
394    }
395}
396impl From<EyeHeight> for f32 {
397    fn from(value: EyeHeight) -> Self {
398        value.0
399    }
400}
401impl From<EyeHeight> for f64 {
402    fn from(value: EyeHeight) -> Self {
403        value.0 as f64
404    }
405}
406impl From<&EyeHeight> for f32 {
407    fn from(value: &EyeHeight) -> Self {
408        value.0
409    }
410}
411impl From<&EyeHeight> for f64 {
412    fn from(value: &EyeHeight) -> Self {
413        value.0 as f64
414    }
415}
416
417/// A component NewType for [`azalea_registry::EntityKind`].
418///
419/// Most of the time, you should be using `azalea_registry::EntityKind`
420/// directly instead.
421#[derive(Component, Clone, Copy, Debug, PartialEq, Deref)]
422pub struct EntityKind(pub azalea_registry::EntityKind);
423
424/// A bundle of components that every entity has. This doesn't contain metadata,
425/// that has to be added separately.
426#[derive(Bundle)]
427pub struct EntityBundle {
428    pub kind: EntityKind,
429    pub uuid: EntityUuid,
430    pub world_name: InstanceName,
431    pub position: Position,
432    pub last_sent_position: LastSentPosition,
433
434    pub chunk_pos: EntityChunkPos,
435
436    pub physics: Physics,
437    pub direction: LookDirection,
438    pub eye_height: EyeHeight,
439    pub attributes: Attributes,
440    pub jumping: Jumping,
441    pub fluid_on_eyes: FluidOnEyes,
442    pub on_climbable: OnClimbable,
443}
444
445impl EntityBundle {
446    pub fn new(
447        uuid: Uuid,
448        pos: Vec3,
449        kind: azalea_registry::EntityKind,
450        world_name: ResourceLocation,
451    ) -> Self {
452        let dimensions = EntityDimensions::from(kind);
453        let eye_height = dimensions.height * 0.85;
454
455        Self {
456            kind: EntityKind(kind),
457            uuid: EntityUuid(uuid),
458            world_name: InstanceName(world_name),
459            position: Position(pos),
460            chunk_pos: EntityChunkPos(ChunkPos::from(&pos)),
461            last_sent_position: LastSentPosition(pos),
462            physics: Physics::new(dimensions, pos),
463            eye_height: EyeHeight(eye_height),
464            direction: LookDirection::default(),
465
466            attributes: Attributes {
467                // TODO: do the correct defaults for everything, some
468                // entities have different defaults
469                speed: AttributeInstance::new(0.1),
470                attack_speed: AttributeInstance::new(4.0),
471                water_movement_efficiency: AttributeInstance::new(0.0),
472            },
473
474            jumping: Jumping(false),
475            fluid_on_eyes: FluidOnEyes(FluidKind::Empty),
476            on_climbable: OnClimbable(false),
477        }
478    }
479}
480
481/// A marker component that signifies that this entity is "local" and shouldn't
482/// be updated by other clients.
483///
484/// If this is for a client then all of our clients will have this.
485#[derive(Component, Clone, Debug)]
486pub struct LocalEntity;
487
488#[derive(Component, Clone, Debug, PartialEq, Deref, DerefMut)]
489pub struct FluidOnEyes(FluidKind);
490
491impl FluidOnEyes {
492    pub fn new(fluid: FluidKind) -> Self {
493        Self(fluid)
494    }
495}
496
497#[derive(Component, Clone, Debug, PartialEq, Deref, DerefMut)]
498pub struct OnClimbable(bool);