Files
node-metaverse/lib/classes/public/Avatar.ts

264 lines
7.5 KiB
TypeScript

import { AvatarQueryResult } from './AvatarQueryResult';
import { GameObject } from './GameObject';
import { Vector3 } from '../Vector3';
import { Quaternion } from '../Quaternion';
import { Subject, Subscription } from 'rxjs';
import { UUID } from '../UUID';
import Timer = NodeJS.Timer;
export class Avatar extends AvatarQueryResult
{
private rotation: Quaternion = Quaternion.getIdentity();
private title = '';
public onMoved: Subject<Avatar> = new Subject<Avatar>();
public onTitleChanged: Subject<Avatar> = new Subject<Avatar>();
public onLeftRegion: Subject<Avatar> = new Subject<Avatar>();
public onAttachmentAdded: Subject<GameObject> = new Subject<GameObject>();
public onAttachmentRemoved: Subject<GameObject> = new Subject<GameObject>();
public onVisibleChanged: Subject<Avatar> = new Subject<Avatar>();
private _isVisible = false;
private _gameObject?: GameObject;
private _position: Vector3 = Vector3.getZero();
private _coarsePosition: Vector3 = Vector3.getZero();
private attachments: {[key: string]: GameObject} = {};
static fromGameObject(obj: GameObject): Avatar
{
let firstName = 'Unknown';
let lastName = 'Avatar';
if (obj.NameValue['FirstName'] !== undefined)
{
firstName = obj.NameValue['FirstName'].value;
}
if (obj.NameValue['LastName'] !== undefined)
{
lastName = obj.NameValue['LastName'].value;
}
const av = new Avatar(obj, firstName , lastName);
if (obj.NameValue['Title'] !== undefined)
{
av.setTitle(obj.NameValue['Title'].value);
}
av.processObjectUpdate(obj);
return av;
}
constructor(gameObjectOrID: GameObject | UUID, firstName: string, lastName: string)
{
super((gameObjectOrID instanceof UUID) ? gameObjectOrID : gameObjectOrID.FullID, firstName, lastName);
if (gameObjectOrID instanceof GameObject)
{
this.gameObject = gameObjectOrID;
}
}
set gameObject(obj: GameObject)
{
if (this._gameObject !== obj)
{
this._gameObject = obj;
const objs: GameObject[] = this._gameObject.region.objects.getObjectsByParent(this._gameObject.ID);
for (const attachment of objs)
{
this._gameObject.region.clientCommands.region.resolveObject(attachment, true, false).then(() =>
{
this.addAttachment(attachment);
}).catch(() =>
{
console.error('Failed to resolve attachment for avatar');
});
}
}
}
get isVisible(): boolean
{
return this._isVisible;
}
set isVisible(value: boolean)
{
if (this._isVisible !== value)
{
this._isVisible = value;
this.onVisibleChanged.next(this);
}
}
setTitle(newTitle: string): void
{
if (newTitle !== this.title)
{
this.title = newTitle;
this.onTitleChanged.next(this);
}
}
getTitle(): string
{
return this.title;
}
get position(): Vector3
{
if (this._isVisible)
{
return new Vector3(this._position);
}
else
{
const pos: Vector3 = new Vector3(this._coarsePosition);
if (pos.z === 1020 && this._position.z > 1020)
{
pos.z = this._position.z;
}
return pos;
}
}
set coarsePosition(pos: Vector3)
{
const oldPos = this._coarsePosition;
this._coarsePosition = pos;
if (!this._isVisible)
{
if (Vector3.distance(pos, oldPos) > 0.0001)
{
this.onMoved.next(this);
}
}
}
getRotation(): Quaternion
{
return new Quaternion(this.rotation);
}
processObjectUpdate(obj: GameObject): void
{
if (obj !== this._gameObject)
{
this.gameObject = obj;
}
if (obj.Position !== undefined && obj.Rotation !== undefined)
{
this.setGeometry(obj.Position, obj.Rotation);
}
if (obj.NameValue['Title'] !== undefined)
{
this.setTitle(obj.NameValue['Title'].value);
}
this.isVisible = true;
}
setGeometry(position: Vector3, rotation: Quaternion): void
{
const oldPosition = this._position;
const oldRotation = this.rotation;
this._position = new Vector3(position);
this._coarsePosition = new Vector3(position);
this.rotation = new Quaternion(rotation);
const rotDist = new Quaternion(this.rotation).angleBetween(oldRotation);
if (Vector3.distance(position, oldPosition) > 0.0001 || rotDist > 0.0001)
{
this.onMoved.next(this);
}
}
getAttachment(itemID: UUID): GameObject
{
if (this.attachments[itemID.toString()] !== undefined)
{
return this.attachments[itemID.toString()];
}
throw new Error('Attachment not found');
}
waitForAttachment(itemID: UUID | string, timeout: number = 30000): Promise<GameObject>
{
return new Promise<GameObject>((resolve, reject) =>
{
if (typeof itemID === 'string')
{
itemID = new UUID(itemID);
}
try
{
const attach = this.getAttachment(itemID);
resolve(attach);
}
catch (ignore)
{
let subs: Subscription | undefined = undefined;
let timr: Timer | undefined = undefined;
subs = this.onAttachmentAdded.subscribe((obj: GameObject) =>
{
if (obj.itemID.equals(itemID))
{
if (subs !== undefined)
{
subs.unsubscribe();
subs = undefined;
}
if (timr !== undefined)
{
clearTimeout(timr);
timr = undefined;
}
resolve(obj);
}
});
timr = setTimeout(() =>
{
if (subs !== undefined)
{
subs.unsubscribe();
subs = undefined;
}
if (timr !== undefined)
{
clearTimeout(timr);
timr = undefined;
}
reject(new Error('WaitForAttachment timed out'));
}, timeout);
}
});
}
addAttachment(obj: GameObject): void
{
if (obj.itemID !== undefined)
{
this.attachments[obj.itemID.toString()] = obj;
this.onAttachmentAdded.next(obj);
}
}
removeAttachment(obj: GameObject): void
{
if (obj.NameValue['AttachItemID'])
{
const itemID = new UUID(obj.NameValue['AttachItemID'].value);
if (this.attachments[itemID.toString()] !== undefined)
{
this.onAttachmentRemoved.next(obj);
delete this.attachments[itemID.toString()];
}
}
}
coarseLeftRegion(): void
{
this.onLeftRegion.next(this);
}
}