Files
bworld/client/entity/entity.ts
T
2026-09-25 16:05:25 -03:00

101 lines
2.4 KiB
TypeScript

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;
}
}