diff --git a/packages/core/src/entity/traits/attribute/movement.ts b/packages/core/src/entity/traits/attribute/movement.ts index 9bbce10f..07dd56dd 100644 --- a/packages/core/src/entity/traits/attribute/movement.ts +++ b/packages/core/src/entity/traits/attribute/movement.ts @@ -56,7 +56,12 @@ class EntityMovementTrait extends EntityAttributeTrait { packet.runtimeId = this.entity.runtimeId; packet.flags = MoveDeltaFlags.All; packet.x = this.entity.position.x; - packet.y = this.entity.position.y - 0.1; // Adjust the y position + + // Asjust the y position of the entity + packet.y = this.entity.isPlayer() + ? this.entity.position.y + : this.entity.position.y - 0.1; + packet.z = this.entity.position.z; packet.yaw = this.entity.rotation.yaw; packet.headYaw = this.entity.rotation.headYaw; @@ -101,17 +106,35 @@ class EntityMovementTrait extends EntityAttributeTrait { } public lookAt(position: BlockPosition | Vector3f) { - const direction = position.subtract(this.entity.position).floor(); - const headYaw = -Math.atan2(direction.x, direction.z) * (180 / Math.PI); - const horizontalDistance = Math.sqrt( - Math.pow(direction.x, 2) + Math.pow(direction.z, 2) - ); - const pitch = - -Math.atan2(direction.y, horizontalDistance) * (180 / Math.PI); + // Convert the block position to a vector + const vector = BlockPosition.toVector3f(position); - this.entity.rotation.headYaw = headYaw; + // Calculate the direction to the target position + const direction = this.calculateDirection(this.entity.position, vector); + + // Calculate the yaw and pitch from the direction + let yaw = Math.atan2(direction.z, direction.x) * (180 / Math.PI) - 90; + + // Calculate the pitch from the direction + const pitch = + -Math.atan2( + direction.y, + Math.sqrt(direction.x * direction.x + direction.z * direction.z) + ) * + (180 / Math.PI); + + // Normalize the yaw if it is out of range + if (yaw > 180) yaw -= 360; + if (yaw < -180) yaw += 360; + + // Normalize the pitch if it is out of range + if (pitch > 180) yaw -= 360; + if (pitch < -180) yaw += 360; + + // Set the entity rotation + this.entity.rotation.yaw = yaw; + this.entity.rotation.headYaw = yaw; this.entity.rotation.pitch = pitch; - this.entity.rotation.yaw = headYaw; } /** diff --git a/packages/core/src/entity/traits/index.ts b/packages/core/src/entity/traits/index.ts index 6e9f5d91..2aff52f2 100644 --- a/packages/core/src/entity/traits/index.ts +++ b/packages/core/src/entity/traits/index.ts @@ -11,3 +11,4 @@ export * from "./effects"; export * from "./visibility"; export * from "./equipment"; export * from "./npc"; +export * from "./look-at-player"; diff --git a/packages/core/src/entity/traits/look-at-player.ts b/packages/core/src/entity/traits/look-at-player.ts new file mode 100644 index 00000000..c26fca8a --- /dev/null +++ b/packages/core/src/entity/traits/look-at-player.ts @@ -0,0 +1,59 @@ +import { EntityIdentifier } from "../../enums"; + +import { EntityMovementTrait } from "./attribute"; +import { EntityTrait } from "./trait"; + +class EntityLookAtPlayerTrait extends EntityTrait { + public static readonly identifier = "look-at-player"; + public static readonly types = [EntityIdentifier.Npc]; + + /** + * The radius of the entity to look at the player. + */ + public radius = 5; + + public onTick(): void { + // Check if the current tick is even. + const currentTick = this.entity.world.currentTick; + if (currentTick % 2n !== 0n) return; + + // Check if the entity has a movement trait. + if (!this.entity.hasTrait(EntityMovementTrait)) return; + + // Get the dimension of the entity. + const dimension = this.entity.dimension; + + // Get all the players near the entity. + const players = dimension + .getEntities({ + position: this.entity.position, + maxDistance: this.radius + }) + .filter((entity) => entity.isPlayer()); + + // Check if there are no players. + if (players.length === 0) return; + + // Find the closest player. + const player = players.reduce((closest, current) => + closest.position.distance(this.entity.position) < + current.position.distance(this.entity.position) + ? closest + : current + ); + + // Check if the player exists. + if (!player) return; + + // Get the movement trait of the entity. + const movement = this.entity.getTrait(EntityMovementTrait); + + // Clone the position of the player, and increase the y value by 0.9. + const position = player.position.clone(); + + // Make the entity look at the player + return movement.lookAt(position); + } +} + +export { EntityLookAtPlayerTrait }; diff --git a/packages/protocol/src/proto/types/vector3f.ts b/packages/protocol/src/proto/types/vector3f.ts index 0e604a62..72779fde 100644 --- a/packages/protocol/src/proto/types/vector3f.ts +++ b/packages/protocol/src/proto/types/vector3f.ts @@ -252,6 +252,14 @@ class Vector3f extends DataType implements IPosition { ); } + /** + * Clones this 3D vector into a new 3D vector. + * @returns The cloned 3D vector. + */ + public clone(): Vector3f { + return new Vector3f(this.x, this.y, this.z); + } + /** * Converts this array to a 3D vector. * @returns The 3D vector that was converted.