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