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

84 lines
2.4 KiB
TypeScript

import { ItemStack } from "$/common/inventory.ts";
import type { EntityInfo } from "$/common/protocol.ts";
import type { ClientLevel } from "../level/client_level.ts";
import { Entity } from "./entity.ts";
// how many ticks a server position takes to reach, like minecraft's lerpTo
const LERP_TICKS = 3;
// how many ticks it takes to fly into whoever picked it up
const PICKUP_TICKS = 3;
// an item lying on the ground. the server simulates it, this only moves where it's told and spins
export class ItemEntity extends Entity {
item: ItemStack;
// ticks since it showed up, for spinning and bobbing
age = 0;
// so items dropped together don't spin in step
readonly bob_offset = Math.random() * Math.PI * 2;
#target_x: number;
#target_y: number;
#target_z: number;
#lerp_ticks = 0;
// who's picking it up, and for how many ticks it has been flying to them
#picked_up_by: Entity | undefined;
#pickup_age = 0;
constructor(level: ClientLevel, info: EntityInfo) {
super(level, info.id, 0.25, 0.25, 0.125);
this.item = ItemStack.from_data(info.item);
this.set_position(info.x, info.y, info.z);
this.#target_x = info.x;
this.#target_y = info.y;
this.#target_z = info.z;
}
lerp_to(x: number, y: number, z: number) {
this.#target_x = x;
this.#target_y = y;
this.#target_z = z;
this.#lerp_ticks = LERP_TICKS;
}
// it already went into their inventory on the server, this is only the animation
pick_up(by: Entity) {
this.#picked_up_by = by;
}
tick() {
this.age += 1;
if (this.#picked_up_by) {
this.#pickup_age += 1;
if (this.#pickup_age >= PICKUP_TICKS) {
this.level.remove_entity(this.id);
}
return;
}
if (this.#lerp_ticks > 0) {
this.x += (this.#target_x - this.x) / this.#lerp_ticks;
this.y += (this.#target_y - this.y) / this.#lerp_ticks;
this.z += (this.#target_z - this.z) / this.#lerp_ticks;
this.#lerp_ticks -= 1;
}
}
// while being picked up it speeds towards the middle of whoever took it, like minecraft's ItemPickupParticle
override render_position(partial_tick: number) {
const position = super.render_position(partial_tick);
const by = this.#picked_up_by;
if (!by) {
return position;
}
const target = by.render_position(partial_tick);
const t = Math.min(1, (this.#pickup_age + partial_tick) / PICKUP_TICKS) ** 2;
return {
x: position.x + (target.x - position.x) * t,
y: position.y + (target.y + 0.5 - position.y) * t,
z: position.z + (target.z - position.z) * t,
};
}
}