import { IcyNetUser } from '../../common/types/user'; import { Socket } from 'socket.io-client'; import { PonyEntity } from './model/pony'; import { Scene, Vector2, Vector3 } from 'three'; export class Player extends PonyEntity { public keydownMap: { [x: string]: boolean } = {}; private _prevKeydownMap: { [x: string]: boolean } = {}; /** * Normal vector of the currently faced direction */ private _lookVector = new Vector3(); /** * Direction of the current movement. * X axis controls angular velocity, Y axis controls linear velocity direction by world coordinates. */ private _direction = new Vector2(); /** * Joystick or other external movement usage. */ private _externalDirection = new Vector2(); /** * Was external movement used this tick */ private _externalForce = false; /** * Was moving last tick */ private _wasMoving = false; /** * Was turning last tick */ private _wasTurning = false; /** * When true, left and right movement will not rotate, instead will move. */ private _cameraLock = false; constructor(public user: IcyNetUser) { super(); } public static fromUser(user: IcyNetUser, scene: Scene): Player { const entity = new Player(user); entity.initialize(); scene.add(entity.container); return entity; } initialize(): void { super.initialize(); window.addEventListener('keydown', (e) => { this.keydownMap[e.key.toLowerCase()] = true; }); window.addEventListener('keyup', (e) => { this.keydownMap[e.key.toLowerCase()] = false; }); } public createPacket(socket: Socket): void { if (Object.keys(this.changes).length) { socket.emit('move', this.changes); this.changes = {}; } } public applyForce(vec: Vector2) { this._externalDirection.copy(vec); this._externalForce = true; } public moveCharacter(dt: number) { const vector = new Vector2(); const wasExternalForce = this._externalForce; this._externalForce = false; if (wasExternalForce) { vector.copy(this._externalDirection); } else { vector.copy(this._direction); } if (vector.x !== 0 && !this._cameraLock) { this.angularVelocity.set(0, Math.PI * vector.x, 0); this.changes.angular = this.angularVelocity.toArray(); this._wasTurning = true; } else if (this._wasTurning && !wasExternalForce) { this._wasTurning = false; this.angularVelocity.set(0, 0, 0); this.changes.angular = this.angularVelocity.toArray(); this.changes.rotation = this.container.rotation.toArray(); } if (vector.y !== 0 || (this._cameraLock && vector.x !== 0)) { const directional = this._lookVector .clone() .multiplyScalar(vector.y * 2.5); if (this._cameraLock && vector.x !== 0) { const sideways = new Vector3(); sideways.copy(this._lookVector); sideways.applyAxisAngle(new Vector3(0, 1, 0), Math.PI / 2); sideways.normalize(); sideways.multiplyScalar(vector.x * 2.5); directional.add(sideways); } this.velocity.set(directional.x, this.velocity.y, directional.z); this.changes.velocity = this.velocity.toArray(); this._wasMoving = true; this.setWalkAnimationState(1); } else if (this._wasMoving && !wasExternalForce) { this._wasMoving = false; this.velocity.set(0, this.velocity.y, 0); this.changes.velocity = this.velocity.toArray(); this.changes.position = this.container.position.toArray(); this.setWalkAnimationState(0); } this.changes.rotation = this.container.rotation.toArray(); this.changes.position = this.container.position.toArray(); } update(dt: number): void { this.container.getWorldDirection(this._lookVector); if (this.keydownMap['w']) { this._direction.y = 1; } else if (this.keydownMap['s']) { this._direction.y = -1; } else { this._direction.y = 0; } if (this.keydownMap['a']) { this._direction.x = 1; } else if (this.keydownMap['d']) { this._direction.x = -1; } else { this._direction.x = 0; } if (this.keydownMap[' '] && !this._prevKeydownMap[' ']) { this.jump(); } this.moveCharacter(dt); super.update(dt); this._prevKeydownMap = { ...this.keydownMap }; } public setCameraLock(isLocked: boolean) { this._cameraLock = isLocked; } }