1#![allow(clippy::derived_hash_with_manual_eq)]
2
3pub mod attributes;
4mod data;
5pub mod 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 f64::consts::PI,
16 fmt::{self, Debug},
17 hash::{Hash, Hasher},
18};
19
20pub use attributes::Attributes;
21use azalea_block::{BlockState, fluid_state::FluidKind};
22use azalea_buf::AzBuf;
23use azalea_core::{
24 aabb::Aabb,
25 identifier::Identifier,
26 math,
27 position::{BlockPos, ChunkPos, Vec3},
28};
29use azalea_registry::EntityKind;
30use azalea_world::{ChunkStorage, InstanceName};
31use bevy_ecs::{bundle::Bundle, component::Component};
32pub use data::*;
33use derive_more::{Deref, DerefMut};
34pub use effects::{ActiveEffects, MobEffectData};
35use plugin::indexing::EntityChunkPos;
36use uuid::Uuid;
37use vec_delta_codec::VecDeltaCodec;
38
39use self::attributes::AttributeInstance;
40use crate::dimensions::EntityDimensions;
41pub use crate::plugin::*;
42
43pub fn move_relative(
44 physics: &mut Physics,
45 direction: LookDirection,
46 speed: f32,
47 acceleration: Vec3,
48) {
49 let input_vector = input_vector(direction, speed, acceleration);
50 physics.velocity += input_vector;
51}
52
53pub fn input_vector(direction: LookDirection, speed: f32, acceleration: Vec3) -> Vec3 {
54 let distance = acceleration.length_squared();
55 if distance < 1.0e-7 {
56 return Vec3::ZERO;
57 }
58 let acceleration = if distance > 1. {
59 acceleration.normalize()
60 } else {
61 acceleration
62 }
63 .scale(speed as f64);
64 let y_rot = math::sin(direction.y_rot * (PI / 180.) as f32);
65 let x_rot = math::cos(direction.y_rot * (PI / 180.) as f32);
66 Vec3 {
67 x: acceleration.x * (x_rot as f64) - acceleration.z * (y_rot as f64),
68 y: acceleration.y,
69 z: acceleration.z * (x_rot as f64) + acceleration.x * (y_rot as f64),
70 }
71}
72
73pub fn view_vector(look_direction: LookDirection) -> Vec3 {
74 let x_rot = look_direction.x_rot * 0.017453292;
75 let y_rot = -look_direction.y_rot * 0.017453292;
76 let y_rot_cos = math::cos(y_rot);
77 let y_rot_sin = math::sin(y_rot);
78 let x_rot_cos = math::cos(x_rot);
79 let x_rot_sin = math::sin(x_rot);
80 Vec3 {
81 x: (y_rot_sin * x_rot_cos) as f64,
82 y: (-x_rot_sin) as f64,
83 z: (y_rot_cos * x_rot_cos) as f64,
84 }
85}
86
87pub fn on_pos_legacy(chunk_storage: &ChunkStorage, position: Position) -> BlockPos {
89 on_pos(0.2, chunk_storage, position)
90}
91
92pub fn on_pos(offset: f32, chunk_storage: &ChunkStorage, pos: Position) -> BlockPos {
105 let x = pos.x.floor() as i32;
106 let y = (pos.y - offset as f64).floor() as i32;
107 let z = pos.z.floor() as i32;
108 let pos = BlockPos { x, y, z };
109
110 let block_pos = pos.down(1);
112 let block_state = chunk_storage.get_block_state(block_pos);
113 if block_state == Some(BlockState::AIR) {
114 let block_pos_below = block_pos.down(1);
115 let block_state_below = chunk_storage.get_block_state(block_pos_below);
116 if let Some(_block_state_below) = block_state_below {
117 }
124 }
125
126 pos
127}
128
129#[derive(Component, Deref, DerefMut, Clone, Copy, Default, PartialEq)]
134pub struct EntityUuid(Uuid);
135impl EntityUuid {
136 pub fn new(uuid: Uuid) -> Self {
137 Self(uuid)
138 }
139}
140impl Debug for EntityUuid {
141 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
142 (self.0).fmt(f)
143 }
144}
145
146#[derive(Component, Clone, Copy, Debug, Default, PartialEq, Deref, DerefMut)]
154pub struct Position(Vec3);
155impl Position {
156 pub fn new(pos: Vec3) -> Self {
157 Self(pos)
158 }
159}
160impl From<&Position> for Vec3 {
161 fn from(value: &Position) -> Self {
162 value.0
163 }
164}
165impl From<Position> for ChunkPos {
166 fn from(value: Position) -> Self {
167 ChunkPos::from(&value.0)
168 }
169}
170impl From<Position> for BlockPos {
171 fn from(value: Position) -> Self {
172 BlockPos::from(&value.0)
173 }
174}
175impl From<&Position> for ChunkPos {
176 fn from(value: &Position) -> Self {
177 ChunkPos::from(value.0)
178 }
179}
180impl From<&Position> for BlockPos {
181 fn from(value: &Position) -> Self {
182 BlockPos::from(value.0)
183 }
184}
185
186#[derive(Component, Clone, Copy, Debug, Default, PartialEq, Deref, DerefMut)]
191pub struct LastSentPosition(Vec3);
192impl From<&LastSentPosition> for Vec3 {
193 fn from(value: &LastSentPosition) -> Self {
194 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}
207impl From<&LastSentPosition> for ChunkPos {
208 fn from(value: &LastSentPosition) -> Self {
209 ChunkPos::from(value.0)
210 }
211}
212impl From<&LastSentPosition> for BlockPos {
213 fn from(value: &LastSentPosition) -> Self {
214 BlockPos::from(value.0)
215 }
216}
217
218#[derive(Debug, Component, Copy, Clone, Deref, DerefMut, Default, PartialEq, Eq)]
223pub struct Jumping(pub bool);
224
225#[derive(Debug, Component, Copy, Clone, Default, PartialEq, AzBuf)]
230pub struct LookDirection {
231 y_rot: f32,
233 x_rot: f32,
235}
236
237impl LookDirection {
238 pub fn new(y_rot: f32, x_rot: f32) -> Self {
240 apply_clamp_look_direction(Self { y_rot, x_rot })
241 }
242 pub fn y_rot(&self) -> f32 {
247 self.y_rot
248 }
249 pub fn x_rot(&self) -> f32 {
253 self.x_rot
254 }
255
256 pub fn update(&mut self, new: LookDirection) {
261 self.update_with_sensitivity(new, 1.);
262 }
263 pub fn update_y_rot(&mut self, new_y_rot: f32) {
268 self.update(LookDirection {
269 y_rot: new_y_rot,
270 x_rot: self.x_rot,
271 });
272 }
273 pub fn update_x_rot(&mut self, new_x_rot: f32) {
278 self.update(LookDirection {
279 y_rot: self.y_rot,
280 x_rot: new_x_rot,
281 });
282 }
283
284 pub fn update_with_sensitivity(&mut self, new: LookDirection, sensitivity: f32) {
290 let mut delta_y_rot = new.y_rot.rem_euclid(360.) - self.y_rot.rem_euclid(360.);
291 let delta_x_rot = new.x_rot - self.x_rot;
292
293 if delta_y_rot > 180. {
294 delta_y_rot -= 360.;
295 } else if delta_y_rot < -180. {
296 delta_y_rot += 360.;
297 }
298
299 let sensitivity = sensitivity * 0.15;
300 let delta_y_rot = (delta_y_rot / sensitivity).round() * sensitivity;
301 let delta_x_rot = (delta_x_rot / sensitivity).round() * sensitivity;
302
303 self.y_rot += delta_y_rot;
304 self.x_rot += delta_x_rot;
305 }
306}
307
308impl From<LookDirection> for (f32, f32) {
309 fn from(value: LookDirection) -> Self {
310 (value.y_rot, value.x_rot)
311 }
312}
313impl From<(f32, f32)> for LookDirection {
314 fn from((y_rot, x_rot): (f32, f32)) -> Self {
315 Self { y_rot, x_rot }
316 }
317}
318
319impl Hash for LookDirection {
320 fn hash<H: Hasher>(&self, state: &mut H) {
321 self.y_rot.to_bits().hash(state);
322 self.x_rot.to_bits().hash(state);
323 }
324}
325impl Eq for LookDirection {}
326
327#[derive(Debug, Component, Clone, Default)]
330pub struct Physics {
331 pub velocity: Vec3,
337 pub vec_delta_codec: VecDeltaCodec,
338
339 pub old_position: Vec3,
343
344 pub x_acceleration: f32,
350 pub y_acceleration: f32,
351 pub z_acceleration: f32,
352
353 on_ground: bool,
354 last_on_ground: bool,
355
356 pub no_jump_delay: u32,
361
362 pub bounding_box: Aabb,
366
367 pub has_impulse: bool,
368
369 pub horizontal_collision: bool,
370 pub minor_horizontal_collision: bool,
372 pub vertical_collision: bool,
373
374 pub water_fluid_height: f64,
375 pub lava_fluid_height: f64,
376 pub was_touching_water: bool,
377
378 pub fall_distance: f64,
379 pub remaining_fire_ticks: i32,
381}
382
383impl Physics {
384 pub fn new(dimensions: &EntityDimensions, pos: Vec3) -> Self {
385 Self {
386 velocity: Vec3::ZERO,
387 vec_delta_codec: VecDeltaCodec::new(pos),
388
389 old_position: pos,
390
391 x_acceleration: 0.,
392 y_acceleration: 0.,
393 z_acceleration: 0.,
394
395 on_ground: false,
396 last_on_ground: false,
397
398 no_jump_delay: 0,
399
400 bounding_box: dimensions.make_bounding_box(pos),
401
402 has_impulse: false,
403
404 horizontal_collision: false,
405 minor_horizontal_collision: false,
406 vertical_collision: false,
407
408 water_fluid_height: 0.,
409 lava_fluid_height: 0.,
410 was_touching_water: false,
411
412 fall_distance: 0.,
413 remaining_fire_ticks: 0,
414 }
415 }
416
417 pub fn on_ground(&self) -> bool {
418 self.on_ground
419 }
420 pub fn set_on_ground(&mut self, on_ground: bool) {
422 self.last_on_ground = self.on_ground;
423 self.on_ground = on_ground;
424 }
425
426 pub fn last_on_ground(&self) -> bool {
431 self.last_on_ground
432 }
433 pub fn set_last_on_ground(&mut self, last_on_ground: bool) {
434 self.last_on_ground = last_on_ground;
435 }
436
437 pub fn reset_fall_distance(&mut self) {
438 self.fall_distance = 0.;
439 }
440 pub fn clear_fire(&mut self) {
441 self.remaining_fire_ticks = 0;
442 }
443
444 pub fn is_in_water(&self) -> bool {
445 self.was_touching_water
446 }
447 pub fn is_in_lava(&self) -> bool {
448 self.lava_fluid_height > 0.
450 }
451
452 pub fn set_old_pos(&mut self, pos: Position) {
453 self.old_position = *pos;
454 }
455}
456
457#[derive(Component, Copy, Clone, Default)]
461pub struct Dead;
462
463#[derive(Component, Clone, Copy, Debug, PartialEq, Deref)]
468pub struct EntityKindComponent(pub azalea_registry::EntityKind);
469
470#[derive(Bundle)]
474pub struct EntityBundle {
475 pub kind: EntityKindComponent,
476 pub uuid: EntityUuid,
477 pub world_name: InstanceName,
478 pub position: Position,
479 pub last_sent_position: LastSentPosition,
480
481 pub chunk_pos: EntityChunkPos,
482
483 pub physics: Physics,
484 pub direction: LookDirection,
485 pub dimensions: EntityDimensions,
486 pub attributes: Attributes,
487 pub jumping: Jumping,
488 pub crouching: Crouching,
489 pub fluid_on_eyes: FluidOnEyes,
490 pub on_climbable: OnClimbable,
491 pub active_effects: ActiveEffects,
492}
493
494impl EntityBundle {
495 pub fn new(
496 uuid: Uuid,
497 pos: Vec3,
498 kind: azalea_registry::EntityKind,
499 world_name: Identifier,
500 ) -> Self {
501 let dimensions = EntityDimensions::from(kind);
502
503 Self {
504 kind: EntityKindComponent(kind),
505 uuid: EntityUuid(uuid),
506 world_name: InstanceName(world_name),
507 position: Position(pos),
508 chunk_pos: EntityChunkPos(ChunkPos::from(&pos)),
509 last_sent_position: LastSentPosition(pos),
510 physics: Physics::new(&dimensions, pos),
511 dimensions,
512 direction: LookDirection::default(),
513
514 attributes: default_attributes(EntityKind::Player),
515
516 jumping: Jumping(false),
517 crouching: Crouching(false),
518 fluid_on_eyes: FluidOnEyes(FluidKind::Empty),
519 on_climbable: OnClimbable(false),
520 active_effects: ActiveEffects::default(),
521 }
522 }
523}
524
525pub fn default_attributes(_entity_kind: EntityKind) -> Attributes {
526 Attributes {
529 movement_speed: AttributeInstance::new(0.1f32 as f64),
530 sneaking_speed: AttributeInstance::new(0.3),
531 attack_speed: AttributeInstance::new(4.0),
532 water_movement_efficiency: AttributeInstance::new(0.0),
533 block_interaction_range: AttributeInstance::new(4.5),
534 entity_interaction_range: AttributeInstance::new(3.0),
535 step_height: AttributeInstance::new(0.6),
536 }
537}
538
539#[derive(Component, Clone, Copy, Debug, Default)]
546pub struct LocalEntity;
547
548#[derive(Component, Clone, Copy, Debug, PartialEq, Deref, DerefMut)]
549pub struct FluidOnEyes(FluidKind);
550
551impl FluidOnEyes {
552 pub fn new(fluid: FluidKind) -> Self {
553 Self(fluid)
554 }
555}
556
557#[derive(Component, Clone, Copy, Debug, PartialEq, Deref, DerefMut)]
558pub struct OnClimbable(bool);
559
560#[derive(Component, Clone, Copy, Deref, DerefMut, Default)]
569pub struct Crouching(bool);
570
571#[derive(Clone, Debug, Component, Default)]
576pub struct PlayerAbilities {
577 pub invulnerable: bool,
578 pub flying: bool,
579 pub can_fly: bool,
580 pub instant_break: bool,
583
584 pub flying_speed: f32,
585 pub walking_speed: f32,
587}