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 math,
26 position::{BlockPos, ChunkPos, Vec3},
27 resource_location::ResourceLocation,
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};
34use plugin::indexing::EntityChunkPos;
35use uuid::Uuid;
36use vec_delta_codec::VecDeltaCodec;
37
38use self::attributes::AttributeInstance;
39use crate::dimensions::EntityDimensions;
40pub use crate::plugin::*;
41
42pub fn move_relative(
43 physics: &mut Physics,
44 direction: LookDirection,
45 speed: f32,
46 acceleration: Vec3,
47) {
48 let input_vector = input_vector(direction, speed, acceleration);
49 physics.velocity += input_vector;
50}
51
52pub fn input_vector(direction: LookDirection, speed: f32, acceleration: Vec3) -> Vec3 {
53 let distance = acceleration.length_squared();
54 if distance < 1.0e-7 {
55 return Vec3::ZERO;
56 }
57 let acceleration = if distance > 1. {
58 acceleration.normalize()
59 } else {
60 acceleration
61 }
62 .scale(speed as f64);
63 let y_rot = math::sin(direction.y_rot * (PI / 180.) as f32);
64 let x_rot = math::cos(direction.y_rot * (PI / 180.) as f32);
65 Vec3 {
66 x: acceleration.x * (x_rot as f64) - acceleration.z * (y_rot as f64),
67 y: acceleration.y,
68 z: acceleration.z * (x_rot as f64) + acceleration.x * (y_rot as f64),
69 }
70}
71
72pub fn view_vector(look_direction: LookDirection) -> Vec3 {
73 let x_rot = look_direction.x_rot * 0.017453292;
74 let y_rot = -look_direction.y_rot * 0.017453292;
75 let y_rot_cos = math::cos(y_rot);
76 let y_rot_sin = math::sin(y_rot);
77 let x_rot_cos = math::cos(x_rot);
78 let x_rot_sin = math::sin(x_rot);
79 Vec3 {
80 x: (y_rot_sin * x_rot_cos) as f64,
81 y: (-x_rot_sin) as f64,
82 z: (y_rot_cos * x_rot_cos) as f64,
83 }
84}
85
86pub fn on_pos_legacy(chunk_storage: &ChunkStorage, position: Position) -> BlockPos {
88 on_pos(0.2, chunk_storage, position)
89}
90
91pub fn on_pos(offset: f32, chunk_storage: &ChunkStorage, pos: Position) -> BlockPos {
104 let x = pos.x.floor() as i32;
105 let y = (pos.y - offset as f64).floor() as i32;
106 let z = pos.z.floor() as i32;
107 let pos = BlockPos { x, y, z };
108
109 let block_pos = pos.down(1);
111 let block_state = chunk_storage.get_block_state(block_pos);
112 if block_state == Some(BlockState::AIR) {
113 let block_pos_below = block_pos.down(1);
114 let block_state_below = chunk_storage.get_block_state(block_pos_below);
115 if let Some(_block_state_below) = block_state_below {
116 }
123 }
124
125 pos
126}
127
128#[derive(Component, Deref, DerefMut, Clone, Copy, Default, PartialEq)]
133pub struct EntityUuid(Uuid);
134impl EntityUuid {
135 pub fn new(uuid: Uuid) -> Self {
136 Self(uuid)
137 }
138}
139impl Debug for EntityUuid {
140 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
141 (self.0).fmt(f)
142 }
143}
144
145#[derive(Component, Clone, Copy, Debug, Default, PartialEq, Deref, DerefMut)]
153pub struct Position(Vec3);
154impl Position {
155 pub fn new(pos: Vec3) -> Self {
156 Self(pos)
157 }
158}
159impl From<&Position> for Vec3 {
160 fn from(value: &Position) -> Self {
161 value.0
162 }
163}
164impl From<Position> for ChunkPos {
165 fn from(value: Position) -> Self {
166 ChunkPos::from(&value.0)
167 }
168}
169impl From<Position> for BlockPos {
170 fn from(value: Position) -> Self {
171 BlockPos::from(&value.0)
172 }
173}
174impl From<&Position> for ChunkPos {
175 fn from(value: &Position) -> Self {
176 ChunkPos::from(value.0)
177 }
178}
179impl From<&Position> for BlockPos {
180 fn from(value: &Position) -> Self {
181 BlockPos::from(value.0)
182 }
183}
184
185#[derive(Component, Clone, Copy, Debug, Default, PartialEq, Deref, DerefMut)]
190pub struct LastSentPosition(Vec3);
191impl From<&LastSentPosition> for Vec3 {
192 fn from(value: &LastSentPosition) -> Self {
193 value.0
194 }
195}
196impl From<LastSentPosition> for ChunkPos {
197 fn from(value: LastSentPosition) -> Self {
198 ChunkPos::from(&value.0)
199 }
200}
201impl From<LastSentPosition> for BlockPos {
202 fn from(value: LastSentPosition) -> Self {
203 BlockPos::from(&value.0)
204 }
205}
206impl From<&LastSentPosition> for ChunkPos {
207 fn from(value: &LastSentPosition) -> Self {
208 ChunkPos::from(value.0)
209 }
210}
211impl From<&LastSentPosition> for BlockPos {
212 fn from(value: &LastSentPosition) -> Self {
213 BlockPos::from(value.0)
214 }
215}
216
217#[derive(Debug, Component, Copy, Clone, Deref, DerefMut, Default, PartialEq, Eq)]
222pub struct Jumping(pub bool);
223
224#[derive(Debug, Component, Copy, Clone, Default, PartialEq, AzBuf)]
229pub struct LookDirection {
230 y_rot: f32,
232 x_rot: f32,
234}
235
236impl LookDirection {
237 pub fn new(y_rot: f32, x_rot: f32) -> Self {
239 apply_clamp_look_direction(Self { y_rot, x_rot })
240 }
241 pub fn y_rot(&self) -> f32 {
246 self.y_rot
247 }
248 pub fn x_rot(&self) -> f32 {
252 self.x_rot
253 }
254
255 pub fn update(&mut self, new: LookDirection) {
260 self.update_with_sensitivity(new, 1.);
261 }
262 pub fn update_y_rot(&mut self, new_y_rot: f32) {
267 self.update(LookDirection {
268 y_rot: new_y_rot,
269 x_rot: self.x_rot,
270 });
271 }
272 pub fn update_x_rot(&mut self, new_x_rot: f32) {
277 self.update(LookDirection {
278 y_rot: self.y_rot,
279 x_rot: new_x_rot,
280 });
281 }
282
283 pub fn update_with_sensitivity(&mut self, new: LookDirection, sensitivity: f32) {
289 let mut delta_y_rot = new.y_rot.rem_euclid(360.) - self.y_rot.rem_euclid(360.);
290 let delta_x_rot = new.x_rot - self.x_rot;
291
292 if delta_y_rot > 180. {
293 delta_y_rot -= 360.;
294 } else if delta_y_rot < -180. {
295 delta_y_rot += 360.;
296 }
297
298 let sensitivity = sensitivity * 0.15;
299 let delta_y_rot = (delta_y_rot / sensitivity).round() * sensitivity;
300 let delta_x_rot = (delta_x_rot / sensitivity).round() * sensitivity;
301
302 self.y_rot += delta_y_rot;
303 self.x_rot += delta_x_rot;
304 }
305}
306
307impl From<LookDirection> for (f32, f32) {
308 fn from(value: LookDirection) -> Self {
309 (value.y_rot, value.x_rot)
310 }
311}
312impl From<(f32, f32)> for LookDirection {
313 fn from((y_rot, x_rot): (f32, f32)) -> Self {
314 Self { y_rot, x_rot }
315 }
316}
317
318impl Hash for LookDirection {
319 fn hash<H: Hasher>(&self, state: &mut H) {
320 self.y_rot.to_bits().hash(state);
321 self.x_rot.to_bits().hash(state);
322 }
323}
324impl Eq for LookDirection {}
325
326#[derive(Debug, Component, Clone, Default)]
329pub struct Physics {
330 pub velocity: Vec3,
336 pub vec_delta_codec: VecDeltaCodec,
337
338 pub old_position: Vec3,
342
343 pub x_acceleration: f32,
349 pub y_acceleration: f32,
350 pub z_acceleration: f32,
351
352 on_ground: bool,
353 last_on_ground: bool,
354
355 pub no_jump_delay: u32,
360
361 pub bounding_box: Aabb,
365
366 pub has_impulse: bool,
367
368 pub horizontal_collision: bool,
369 pub minor_horizontal_collision: bool,
371 pub vertical_collision: bool,
372
373 pub water_fluid_height: f64,
374 pub lava_fluid_height: f64,
375 pub was_touching_water: bool,
376
377 pub fall_distance: f64,
378 pub remaining_fire_ticks: i32,
380}
381
382impl Physics {
383 pub fn new(dimensions: &EntityDimensions, pos: Vec3) -> Self {
384 Self {
385 velocity: Vec3::ZERO,
386 vec_delta_codec: VecDeltaCodec::new(pos),
387
388 old_position: pos,
389
390 x_acceleration: 0.,
391 y_acceleration: 0.,
392 z_acceleration: 0.,
393
394 on_ground: false,
395 last_on_ground: false,
396
397 no_jump_delay: 0,
398
399 bounding_box: dimensions.make_bounding_box(pos),
400
401 has_impulse: false,
402
403 horizontal_collision: false,
404 minor_horizontal_collision: false,
405 vertical_collision: false,
406
407 water_fluid_height: 0.,
408 lava_fluid_height: 0.,
409 was_touching_water: false,
410
411 fall_distance: 0.,
412 remaining_fire_ticks: 0,
413 }
414 }
415
416 pub fn on_ground(&self) -> bool {
417 self.on_ground
418 }
419 pub fn set_on_ground(&mut self, on_ground: bool) {
421 self.last_on_ground = self.on_ground;
422 self.on_ground = on_ground;
423 }
424
425 pub fn last_on_ground(&self) -> bool {
430 self.last_on_ground
431 }
432 pub fn set_last_on_ground(&mut self, last_on_ground: bool) {
433 self.last_on_ground = last_on_ground;
434 }
435
436 pub fn reset_fall_distance(&mut self) {
437 self.fall_distance = 0.;
438 }
439 pub fn clear_fire(&mut self) {
440 self.remaining_fire_ticks = 0;
441 }
442
443 pub fn is_in_water(&self) -> bool {
444 self.was_touching_water
445 }
446 pub fn is_in_lava(&self) -> bool {
447 self.lava_fluid_height > 0.
449 }
450
451 pub fn set_old_pos(&mut self, pos: Position) {
452 self.old_position = *pos;
453 }
454}
455
456#[derive(Component, Copy, Clone, Default)]
460pub struct Dead;
461
462#[derive(Component, Clone, Copy, Debug, PartialEq, Deref)]
467pub struct EntityKindComponent(pub azalea_registry::EntityKind);
468
469#[derive(Bundle)]
473pub struct EntityBundle {
474 pub kind: EntityKindComponent,
475 pub uuid: EntityUuid,
476 pub world_name: InstanceName,
477 pub position: Position,
478 pub last_sent_position: LastSentPosition,
479
480 pub chunk_pos: EntityChunkPos,
481
482 pub physics: Physics,
483 pub direction: LookDirection,
484 pub dimensions: EntityDimensions,
485 pub attributes: Attributes,
486 pub jumping: Jumping,
487 pub crouching: Crouching,
488 pub fluid_on_eyes: FluidOnEyes,
489 pub on_climbable: OnClimbable,
490}
491
492impl EntityBundle {
493 pub fn new(
494 uuid: Uuid,
495 pos: Vec3,
496 kind: azalea_registry::EntityKind,
497 world_name: ResourceLocation,
498 ) -> Self {
499 let dimensions = EntityDimensions::from(kind);
500
501 Self {
502 kind: EntityKindComponent(kind),
503 uuid: EntityUuid(uuid),
504 world_name: InstanceName(world_name),
505 position: Position(pos),
506 chunk_pos: EntityChunkPos(ChunkPos::from(&pos)),
507 last_sent_position: LastSentPosition(pos),
508 physics: Physics::new(&dimensions, pos),
509 dimensions,
510 direction: LookDirection::default(),
511
512 attributes: default_attributes(EntityKind::Player),
513
514 jumping: Jumping(false),
515 crouching: Crouching(false),
516 fluid_on_eyes: FluidOnEyes(FluidKind::Empty),
517 on_climbable: OnClimbable(false),
518 }
519 }
520}
521
522pub fn default_attributes(_entity_kind: EntityKind) -> Attributes {
523 Attributes {
526 movement_speed: AttributeInstance::new(0.1f32 as f64),
527 sneaking_speed: AttributeInstance::new(0.3),
528 attack_speed: AttributeInstance::new(4.0),
529 water_movement_efficiency: AttributeInstance::new(0.0),
530 block_interaction_range: AttributeInstance::new(4.5),
531 entity_interaction_range: AttributeInstance::new(3.0),
532 step_height: AttributeInstance::new(0.6),
533 }
534}
535
536#[derive(Component, Clone, Copy, Debug, Default)]
543pub struct LocalEntity;
544
545#[derive(Component, Clone, Copy, Debug, PartialEq, Deref, DerefMut)]
546pub struct FluidOnEyes(FluidKind);
547
548impl FluidOnEyes {
549 pub fn new(fluid: FluidKind) -> Self {
550 Self(fluid)
551 }
552}
553
554#[derive(Component, Clone, Copy, Debug, PartialEq, Deref, DerefMut)]
555pub struct OnClimbable(bool);
556
557#[derive(Component, Clone, Copy, Deref, DerefMut, Default)]
566pub struct Crouching(bool);
567
568#[derive(Clone, Debug, Component, Default)]
573pub struct PlayerAbilities {
574 pub invulnerable: bool,
575 pub flying: bool,
576 pub can_fly: bool,
577 pub instant_break: bool,
580
581 pub flying_speed: f32,
582 pub walking_speed: f32,
584}