aboutsummaryrefslogtreecommitdiff
path: root/azalea-entity/src/lib.rs
diff options
context:
space:
mode:
authormat <27899617+mat-1@users.noreply.github.com>2023-07-14 22:20:40 -0500
committerGitHub <noreply@github.com>2023-07-14 22:20:40 -0500
commit7405427199e5a994d4a6a706f84434a69cb7a7d9 (patch)
treeca537e5d761bc053187d952fced0915c850b92aa /azalea-entity/src/lib.rs
parentd1afd02aa84e7b4450c1607277f078eb2a0f1bf3 (diff)
downloadazalea-drasl-7405427199e5a994d4a6a706f84434a69cb7a7d9.tar.xz
Mining (#95)
* more mining stuff * initialize azalea-tags crate * more mining stuff 2 * mining in ecs * well technically mining works but no codegen for how long it takes to mine each block yet * rename downloads to __cache__ it was bothering me since it's not *just* downloads * codegen block behavior * fix not sending packet to finish breaking block * mining animation 🎉 * clippy * cleanup, move Client::mine into a client extension * add azalea/src/mining.rs --------- Co-authored-by: mat <git@matdoes.dev>
Diffstat (limited to 'azalea-entity/src/lib.rs')
-rw-r--r--azalea-entity/src/lib.rs390
1 files changed, 390 insertions, 0 deletions
diff --git a/azalea-entity/src/lib.rs b/azalea-entity/src/lib.rs
new file mode 100644
index 00000000..53e8bfdb
--- /dev/null
+++ b/azalea-entity/src/lib.rs
@@ -0,0 +1,390 @@
+#![allow(clippy::derived_hash_with_manual_eq)]
+
+pub mod attributes;
+mod data;
+mod dimensions;
+mod effects;
+mod enchantments;
+mod info;
+pub mod metadata;
+pub mod mining;
+mod systems;
+
+use self::{attributes::AttributeInstance, metadata::Health};
+pub use attributes::Attributes;
+use azalea_block::BlockState;
+use azalea_core::{BlockPos, ChunkPos, ResourceLocation, Vec3, AABB};
+use azalea_world::{ChunkStorage, InstanceName};
+use bevy_ecs::{
+ bundle::Bundle,
+ component::Component,
+ entity::Entity,
+ query::Changed,
+ system::{Commands, Query},
+};
+pub use data::*;
+use derive_more::{Deref, DerefMut};
+pub use dimensions::{update_bounding_box, EntityDimensions};
+pub use info::{
+ clamp_look_direction, EntityInfos, EntityPlugin, EntityUpdateSet, LoadedBy,
+ RelativeEntityUpdate,
+};
+use std::fmt::Debug;
+use uuid::Uuid;
+
+pub fn move_relative(
+ physics: &mut Physics,
+ direction: &LookDirection,
+ speed: f32,
+ acceleration: &Vec3,
+) {
+ let input_vector = input_vector(direction, speed, acceleration);
+ physics.delta += input_vector;
+}
+
+pub fn input_vector(direction: &LookDirection, speed: f32, acceleration: &Vec3) -> Vec3 {
+ let distance = acceleration.length_squared();
+ if distance < 1.0E-7 {
+ return Vec3::default();
+ }
+ let acceleration = if distance > 1.0 {
+ acceleration.normalize()
+ } else {
+ *acceleration
+ }
+ .scale(speed as f64);
+ let y_rot = f32::sin(direction.y_rot * 0.017453292f32);
+ let x_rot = f32::cos(direction.y_rot * 0.017453292f32);
+ Vec3 {
+ x: acceleration.x * (x_rot as f64) - acceleration.z * (y_rot as f64),
+ y: acceleration.y,
+ z: acceleration.z * (x_rot as f64) + acceleration.x * (y_rot as f64),
+ }
+}
+
+pub fn view_vector(look_direction: &LookDirection) -> Vec3 {
+ let x_rot = look_direction.x_rot * 0.017453292;
+ let y_rot = -look_direction.y_rot * 0.017453292;
+ let y_rot_cos = f32::cos(y_rot);
+ let y_rot_sin = f32::sin(y_rot);
+ let x_rot_cos = f32::cos(x_rot);
+ let x_rot_sin = f32::sin(x_rot);
+ Vec3 {
+ x: (y_rot_sin * x_rot_cos) as f64,
+ y: (-x_rot_sin) as f64,
+ z: (y_rot_cos * x_rot_cos) as f64,
+ }
+}
+
+/// Get the position of the block below the entity, but a little lower.
+pub fn on_pos_legacy(chunk_storage: &ChunkStorage, position: &Position) -> BlockPos {
+ on_pos(0.2, chunk_storage, position)
+}
+
+// int x = Mth.floor(this.position.x);
+// int y = Mth.floor(this.position.y - (double)var1);
+// int z = Mth.floor(this.position.z);
+// BlockPos var5 = new BlockPos(x, y, z);
+// if (this.level.getBlockState(var5).isAir()) {
+// BlockPos var6 = var5.below();
+// BlockState var7 = this.level.getBlockState(var6);
+// if (var7.is(BlockTags.FENCES) || var7.is(BlockTags.WALLS) ||
+// var7.getBlock() instanceof FenceGateBlock) { return var6;
+// }
+// }
+// return var5;
+pub fn on_pos(offset: f32, chunk_storage: &ChunkStorage, pos: &Position) -> BlockPos {
+ let x = pos.x.floor() as i32;
+ let y = (pos.y - offset as f64).floor() as i32;
+ let z = pos.z.floor() as i32;
+ let pos = BlockPos { x, y, z };
+
+ // TODO: check if block below is a fence, wall, or fence gate
+ let block_pos = pos.down(1);
+ let block_state = chunk_storage.get_block_state(&block_pos);
+ if block_state == Some(BlockState::AIR) {
+ let block_pos_below = block_pos.down(1);
+ let block_state_below = chunk_storage.get_block_state(&block_pos_below);
+ if let Some(_block_state_below) = block_state_below {
+ // if block_state_below.is_fence()
+ // || block_state_below.is_wall()
+ // || block_state_below.is_fence_gate()
+ // {
+ // return block_pos_below;
+ // }
+ }
+ }
+
+ pos
+}
+
+/// The Minecraft UUID of the entity. For players, this is their actual player
+/// UUID, and for other entities it's just random.
+#[derive(Component, Deref, DerefMut, Clone, Copy)]
+pub struct EntityUuid(Uuid);
+impl Debug for EntityUuid {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ (self.0).fmt(f)
+ }
+}
+
+/// The position of the entity right now.
+///
+/// You are free to change this; there's systems that update the indexes
+/// automatically.
+#[derive(Component, Clone, Copy, Debug, Default, PartialEq, Deref, DerefMut)]
+pub struct Position(Vec3);
+impl From<&Position> for Vec3 {
+ fn from(value: &Position) -> Self {
+ value.0
+ }
+}
+impl From<Position> for ChunkPos {
+ fn from(value: Position) -> Self {
+ ChunkPos::from(&value.0)
+ }
+}
+impl From<Position> for BlockPos {
+ fn from(value: Position) -> Self {
+ BlockPos::from(&value.0)
+ }
+}
+impl From<&Position> for ChunkPos {
+ fn from(value: &Position) -> Self {
+ ChunkPos::from(value.0)
+ }
+}
+impl From<&Position> for BlockPos {
+ fn from(value: &Position) -> Self {
+ BlockPos::from(value.0)
+ }
+}
+
+/// The last position of the entity that was sent over the network.
+#[derive(Component, Clone, Copy, Debug, Default, PartialEq, Deref, DerefMut)]
+pub struct LastSentPosition(Vec3);
+impl From<&LastSentPosition> for Vec3 {
+ fn from(value: &LastSentPosition) -> Self {
+ value.0
+ }
+}
+impl From<LastSentPosition> for ChunkPos {
+ fn from(value: LastSentPosition) -> Self {
+ ChunkPos::from(&value.0)
+ }
+}
+impl From<LastSentPosition> for BlockPos {
+ fn from(value: LastSentPosition) -> Self {
+ BlockPos::from(&value.0)
+ }
+}
+impl From<&LastSentPosition> for ChunkPos {
+ fn from(value: &LastSentPosition) -> Self {
+ ChunkPos::from(value.0)
+ }
+}
+impl From<&LastSentPosition> for BlockPos {
+ fn from(value: &LastSentPosition) -> Self {
+ BlockPos::from(value.0)
+ }
+}
+
+/// A component for entities that can jump.
+///
+/// If this is true, the entity will try to jump every tick. (It's equivalent to
+/// the space key being held in vanilla.)
+#[derive(Debug, Component, Clone, Deref, DerefMut)]
+pub struct Jumping(bool);
+
+/// A component that contains the direction an entity is looking.
+#[derive(Debug, Component, Clone, Default)]
+pub struct LookDirection {
+ pub x_rot: f32,
+ pub y_rot: f32,
+}
+
+/// The physics data relating to the entity, such as position, velocity, and
+/// bounding box.
+#[derive(Debug, Component)]
+pub struct Physics {
+ pub delta: Vec3,
+
+ /// X acceleration.
+ pub xxa: f32,
+ /// Y acceleration.
+ pub yya: f32,
+ /// Z acceleration.
+ pub zza: f32,
+
+ pub on_ground: bool,
+ pub last_on_ground: bool,
+
+ /// The width and height of the entity.
+ pub dimensions: EntityDimensions,
+ /// The bounding box of the entity. This is more than just width and height,
+ /// unlike dimensions.
+ pub bounding_box: AABB,
+
+ pub has_impulse: bool,
+}
+
+/// Marker component for entities that are dead.
+///
+/// "Dead" means that the entity has 0 health.
+#[derive(Component, Copy, Clone, Default)]
+pub struct Dead;
+
+/// System that adds the [`Dead`] marker component if an entity's health is set
+/// to 0 (or less than 0). This will be present if an entity is doing the death
+/// animation.
+///
+/// Entities that are dead can not be revived.
+/// TODO: fact check this in-game by setting an entity's health to 0 and then
+/// not 0
+pub fn add_dead(mut commands: Commands, query: Query<(Entity, &Health), Changed<Health>>) {
+ for (entity, health) in query.iter() {
+ if **health <= 0.0 {
+ commands.entity(entity).insert(Dead);
+ }
+ }
+}
+
+/// A component that contains the offset of the entity's eyes from the entity
+/// coordinates.
+///
+/// This is used to calculate the camera position for players, when spectating
+/// an entity, and when raytracing from the entity.
+#[derive(Component, Clone, Copy, Debug, PartialEq, Deref, DerefMut)]
+pub struct EyeHeight(f32);
+impl From<EyeHeight> for f32 {
+ fn from(value: EyeHeight) -> Self {
+ value.0
+ }
+}
+impl From<EyeHeight> for f64 {
+ fn from(value: EyeHeight) -> Self {
+ value.0 as f64
+ }
+}
+impl From<&EyeHeight> for f32 {
+ fn from(value: &EyeHeight) -> Self {
+ value.0
+ }
+}
+impl From<&EyeHeight> for f64 {
+ fn from(value: &EyeHeight) -> Self {
+ value.0 as f64
+ }
+}
+
+/// A component NewType for [`azalea_registry::EntityKind`].
+///
+/// Most of the time, you should be using `azalea_registry::EntityKind`
+/// directly instead.
+#[derive(Component, Clone, Copy, Debug, PartialEq, Deref)]
+pub struct EntityKind(pub azalea_registry::EntityKind);
+
+/// A bundle of components that every entity has. This doesn't contain metadata,
+/// that has to be added separately.
+#[derive(Bundle)]
+pub struct EntityBundle {
+ pub kind: EntityKind,
+ pub uuid: EntityUuid,
+ pub world_name: InstanceName,
+ pub position: Position,
+ pub last_sent_position: LastSentPosition,
+ pub physics: Physics,
+ pub direction: LookDirection,
+ pub eye_height: EyeHeight,
+ pub attributes: Attributes,
+ pub jumping: Jumping,
+ pub fluid_on_eyes: FluidOnEyes,
+}
+
+impl EntityBundle {
+ pub fn new(
+ uuid: Uuid,
+ pos: Vec3,
+ kind: azalea_registry::EntityKind,
+ world_name: ResourceLocation,
+ ) -> Self {
+ // TODO: get correct entity dimensions by having them codegen'd somewhere
+ let dimensions = EntityDimensions {
+ width: 0.6,
+ height: 1.8,
+ };
+ let eye_height = dimensions.height * 0.85;
+
+ Self {
+ kind: EntityKind(kind),
+ uuid: EntityUuid(uuid),
+ world_name: InstanceName(world_name),
+ position: Position(pos),
+ last_sent_position: LastSentPosition(pos),
+ physics: Physics {
+ delta: Vec3::default(),
+
+ xxa: 0.,
+ yya: 0.,
+ zza: 0.,
+
+ on_ground: false,
+ last_on_ground: false,
+
+ // TODO: have this be based on the entity type
+ bounding_box: dimensions.make_bounding_box(&pos),
+ dimensions,
+
+ has_impulse: false,
+ },
+ eye_height: EyeHeight(eye_height),
+ direction: LookDirection::default(),
+
+ attributes: Attributes {
+ // TODO: do the correct defaults for everything, some
+ // entities have different defaults
+ speed: AttributeInstance::new(0.1),
+ },
+
+ jumping: Jumping(false),
+ fluid_on_eyes: FluidOnEyes(azalea_registry::Fluid::Empty),
+ }
+ }
+}
+
+/// A bundle of the components that are always present for a player.
+#[derive(Bundle)]
+pub struct PlayerBundle {
+ pub entity: EntityBundle,
+ pub metadata: metadata::PlayerMetadataBundle,
+}
+
+/// A marker component that signifies that this entity is "local" and shouldn't
+/// be updated by other clients.
+#[derive(Component)]
+pub struct Local;
+
+#[derive(Component, Clone, Debug, PartialEq, Deref, DerefMut)]
+pub struct FluidOnEyes(azalea_registry::Fluid);
+
+// #[cfg(test)]
+// mod tests {
+// use super::*;
+// use crate::PartialWorld;
+
+// #[test]
+// fn from_mut_entity_to_ref_entity() {
+// let mut world = PartialWorld::default();
+// let uuid = Uuid::from_u128(100);
+// world.add_entity(
+// 0,
+// EntityData::new(
+// uuid,
+// Vec3::default(),
+// EntityMetadata::Player(metadata::Player::default()),
+// ),
+// );
+// let entity: Entity = world.entity_mut(0).unwrap();
+// assert_eq!(entity.uuid, uuid);
+// }
+// }