import type { ClientLevel } from "../level/client_level.ts"; // anything that exists in the level and moves, like minecraft's Entity. position is the middle of its feet export abstract class Entity { id: string; level: ClientLevel; x = 0; y = 0; z = 0; // blocks per second vx = 0; vy = 0; vz = 0; yaw = 0; pitch = 0; // the collision box, width is used for both x and z width: number; height: number; eye_height: number; gravity = -15.8; // which way it hit something on each axis in the last move: 1 or -1, 0 for nothing. // 1 on y means it's standing on something colliding_x = 0; colliding_y = 0; colliding_z = 0; constructor(level: ClientLevel, id: string, width: number, height: number, eye_height: number) { this.level = level; this.id = id; this.width = width; this.height = height; this.eye_height = eye_height; } get on_ground() { return this.colliding_y === 1; } set_position(x: number, y: number, z: number) { this.x = x; this.y = y; this.z = z; } abstract tick(delta: number): void; // falls and moves by its velocity, stopping on each axis it would run into a block on move(delta: number) { this.vy += this.gravity * delta; this.colliding_x = this.vx !== 0 && this.#collides(this.x + this.vx * delta, this.y, this.z) ? -Math.sign(this.vx) : 0; if (this.colliding_x !== 0) { this.vx = 0; } this.colliding_y = this.vy !== 0 && this.#collides(this.x, this.y + this.vy * delta, this.z) ? -Math.sign(this.vy) : 0; if (this.colliding_y !== 0) { this.vy = 0; } this.colliding_z = this.vz !== 0 && this.#collides(this.x, this.y, this.z + this.vz * delta) ? -Math.sign(this.vz) : 0; if (this.colliding_z !== 0) { this.vz = 0; } this.x += this.vx * delta; this.y += this.vy * delta; this.z += this.vz * delta; } // whether the collision box at x/y/z overlaps any block, unloaded chunks included #collides(x: number, y: number, z: number) { const min_x = Math.floor(x - this.width / 2); const max_x = Math.floor(x + this.width / 2); const min_y = Math.floor(y); const max_y = Math.floor(y + this.height); const min_z = Math.floor(z - this.width / 2); const max_z = Math.floor(z + this.width / 2); for (let bx = min_x; bx <= max_x; bx++) { for (let by = min_y; by <= max_y; by++) { for (let bz = min_z; bz <= max_z; bz++) { if (this.level.get_block(bx, by, bz)) { return true; } } } } return false; } }