icy3dw/src/client/object/player-entity.ts

185 lines
4.8 KiB
TypeScript

import { IcyNetUser } from '../../common/types/user';
import { PonyEntity } from './pony';
import {
FullStatePacket,
PositionUpdatePacket,
} from '../../common/types/packet';
import { CanvasUtils } from './canvas-utils';
import { ChatBubble } from './chat-bubble';
import { Vector3, Scene, Euler } from 'three';
const chatBuilder = new CanvasUtils({
rounded: true,
roundedRadius: 8,
});
export class PlayerEntity extends PonyEntity {
private _updateQueue: PositionUpdatePacket[] = [];
private _targetFrame: any = null;
private _lastFrame: any = null;
private _chats: ChatBubble[] = [];
private _p1 = new Vector3();
private _p2 = new Vector3();
private _q1 = new Vector3();
private _q2 = new Vector3();
private _pf = new Vector3();
private _qf = new Vector3();
constructor(public user: IcyNetUser) {
super();
}
public static fromUser(user: IcyNetUser, scene: Scene): PlayerEntity {
const entity = new PlayerEntity(user);
entity.initialize();
entity.addNameTag(user.display_name);
scene.add(entity.container);
return entity;
}
public setVelocity(velocity: Vector3) {
this.velocity.copy(velocity);
}
public setAngularVelocity(velocity: Vector3) {
this.angularVelocity.copy(velocity);
}
public setPosition(pos: Vector3) {
this.container.position.copy(pos);
}
public addChat(message: string): void {
const lines = [];
let truncated = message;
while (truncated.length > 80) {
lines.push(truncated.substring(0, 80));
truncated = truncated.substring(80);
}
if (truncated) {
lines.push(truncated);
}
const newChat = new ChatBubble(chatBuilder, lines);
this._chats.forEach((item) => {
const scaled = item.tag.scale.y;
item.tag.position.add(new Vector3(0, scaled, 0));
});
this._chats.unshift(newChat);
newChat.tag.position.set(0, 1.8 + this.nameTag.tag.scale.y + 0.15, 0.5);
this.container.add(newChat.tag);
if (this._chats.length > 3) {
this._chats.splice(3, this._chats.length - 1).forEach((item) => {
this.container.remove(item.tag);
item.dispose();
});
}
setTimeout(() => {
const i = this._chats.indexOf(newChat);
if (i !== -1) {
this.container.remove(newChat.tag);
this._chats.splice(i, 1);
newChat.dispose();
}
}, 5000);
}
public setRotation(rot: Vector3 | Euler) {
this.container.rotation.copy(
rot instanceof Euler ? rot : new Euler(rot.x, rot.y, rot.z, 'XYZ'),
);
}
public addUncommittedChanges(packet: FullStatePacket) {
const appendix = { ...packet, time: 0.1 };
this._updateQueue.push(appendix);
if (!this._targetFrame) {
this.setFromPacket(packet);
this._targetFrame = appendix;
}
if (packet.character) {
this.setCharacter(packet.character);
}
}
public update(dt: number) {
this.commitServerUpdate(dt);
super.update(dt);
}
private setFromPacket(packet: FullStatePacket | PositionUpdatePacket) {
if ((packet as FullStatePacket).velocity) {
this.setVelocity(
new Vector3().fromArray((packet as FullStatePacket).velocity),
);
}
if ((packet as FullStatePacket).angular) {
this.setAngularVelocity(
new Vector3().fromArray((packet as FullStatePacket).angular),
);
}
if (packet.position) {
this.setPosition(new Vector3().fromArray(packet.position));
}
if (packet.rotation) {
this.setRotation(new Euler().fromArray(packet.rotation));
}
if (packet.animState) {
this.setWalkAnimationState(packet.animState);
}
}
private commitServerUpdate(dt: number) {
if (this._updateQueue.length == 0) {
return;
}
for (let i = 0; i < this._updateQueue.length; ++i) {
this._updateQueue[i].time -= dt;
}
while (this._updateQueue.length > 0 && this._updateQueue[0].time <= 0.0) {
this._lastFrame = {
animState: this._targetFrame.animState,
position: this.container.position.toArray(),
rotation: this.container.rotation.toArray(),
};
this._targetFrame = this._updateQueue.shift();
this._targetFrame.time = 0.0;
}
// Lerp the position and rotation
if (this._targetFrame && this._lastFrame) {
this._targetFrame.time += dt;
this._p1.fromArray(this._lastFrame.position);
this._p2.fromArray(this._targetFrame.position);
this._q1.fromArray(this._lastFrame.rotation);
this._q2.fromArray(this._targetFrame.rotation);
this._pf.copy(this._p1);
this._qf.copy(this._q1);
const t = Math.max(Math.min(this._targetFrame.time / 0.1, 1.0), 0.0);
this._pf.lerp(this._p2, t);
this._qf.lerp(this._q2, t);
this.setPosition(this._pf);
this.setRotation(this._qf);
this.setWalkAnimationState(this._lastFrame.animState);
}
}
}