You've already forked Nitro_Render_V3
mirror of
https://github.com/duckietm/Nitro_Render_V3.git
synced 2026-06-20 07:26:18 +00:00
Move to Renderer V2
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
import { IAssetManager, IAvatarAssetDownloadLibrary } from '@nitrots/api';
|
||||
import { AvatarRenderLibraryEvent, GetEventDispatcher, NitroEventType } from '@nitrots/events';
|
||||
|
||||
export class AvatarAssetDownloadLibrary implements IAvatarAssetDownloadLibrary
|
||||
{
|
||||
private static NOT_LOADED: number = 0;
|
||||
private static LOADING: number = 1;
|
||||
private static LOADED: number = 2;
|
||||
|
||||
private _state: number = AvatarAssetDownloadLibrary.NOT_LOADED;
|
||||
private _libraryName: string;
|
||||
private _revision: string;
|
||||
private _downloadUrl: string;
|
||||
private _assetManager: IAssetManager;
|
||||
|
||||
constructor(libraryName: string, revision: string, downloadUrl: string, assetManager: IAssetManager)
|
||||
{
|
||||
this._libraryName = libraryName;
|
||||
this._revision = revision;
|
||||
this._downloadUrl = downloadUrl;
|
||||
this._assetManager = assetManager;
|
||||
|
||||
this._downloadUrl = this._downloadUrl.replace(/%libname%/gi, this._libraryName);
|
||||
this._downloadUrl = this._downloadUrl.replace(/%revision%/gi, this._revision);
|
||||
|
||||
this.checkIsLoaded();
|
||||
}
|
||||
|
||||
public async downloadAsset(): Promise<void>
|
||||
{
|
||||
if(!this._assetManager || (this._state === AvatarAssetDownloadLibrary.LOADING) || (this._state === AvatarAssetDownloadLibrary.LOADED)) return;
|
||||
|
||||
if(!this.checkIsLoaded())
|
||||
{
|
||||
this._state = AvatarAssetDownloadLibrary.LOADING;
|
||||
|
||||
const status = await this._assetManager.downloadAsset(this._downloadUrl);
|
||||
|
||||
if(!status) throw new Error('Could not download asset');
|
||||
}
|
||||
|
||||
if(this.checkIsLoaded()) GetEventDispatcher().dispatchEvent(new AvatarRenderLibraryEvent(NitroEventType.AVATAR_ASSET_DOWNLOADED, this));
|
||||
}
|
||||
|
||||
private checkIsLoaded(): boolean
|
||||
{
|
||||
const asset = this._assetManager.getCollection(this._libraryName);
|
||||
|
||||
if(!asset) return false;
|
||||
|
||||
this._state = AvatarAssetDownloadLibrary.LOADED;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public get libraryName(): string
|
||||
{
|
||||
return this._libraryName;
|
||||
}
|
||||
|
||||
public get isLoaded(): boolean
|
||||
{
|
||||
return (this._state === AvatarAssetDownloadLibrary.LOADED);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,246 @@
|
||||
import { IAssetManager, IAvatarFigureContainer, IAvatarImageListener } from '@nitrots/api';
|
||||
import { GetConfiguration } from '@nitrots/configuration';
|
||||
import { AvatarRenderLibraryEvent, GetEventDispatcher, NitroEvent, NitroEventType } from '@nitrots/events';
|
||||
import { AvatarAssetDownloadLibrary } from './AvatarAssetDownloadLibrary';
|
||||
import { AvatarStructure } from './AvatarStructure';
|
||||
|
||||
export class AvatarAssetDownloadManager
|
||||
{
|
||||
private _assets: IAssetManager;
|
||||
private _structure: AvatarStructure;
|
||||
|
||||
private _missingMandatoryLibs: string[] = [];
|
||||
private _figureMap: Map<string, AvatarAssetDownloadLibrary[]> = new Map();
|
||||
private _figureListeners: Map<string, IAvatarImageListener[]> = new Map();
|
||||
private _incompleteFigures: Map<string, AvatarAssetDownloadLibrary[]> = new Map();
|
||||
private _currentDownloads: AvatarAssetDownloadLibrary[] = [];
|
||||
private _libraryNames: string[] = [];
|
||||
|
||||
constructor(assets: IAssetManager, structure: AvatarStructure)
|
||||
{
|
||||
this._assets = assets;
|
||||
this._structure = structure;
|
||||
}
|
||||
|
||||
public async init(): Promise<void>
|
||||
{
|
||||
this._missingMandatoryLibs = GetConfiguration().getValue<string[]>('avatar.mandatory.libraries');
|
||||
|
||||
const url = GetConfiguration().getValue<string>('avatar.figuremap.url');
|
||||
|
||||
if(!url || !url.length) throw new Error('Invalid figure map url');
|
||||
|
||||
const response = await fetch(url);
|
||||
|
||||
if(response.status !== 200) throw new Error('Invalid figure map file');
|
||||
|
||||
const responseData = await response.json();
|
||||
|
||||
this.processFigureMap(responseData.libraries);
|
||||
|
||||
GetEventDispatcher().addEventListener(NitroEventType.AVATAR_ASSET_DOWNLOADED, (event: AvatarRenderLibraryEvent) => this.onLibraryLoaded(event));
|
||||
|
||||
await this.processMissingLibraries();
|
||||
}
|
||||
|
||||
private processFigureMap(data: any): void
|
||||
{
|
||||
if(!data) return;
|
||||
|
||||
const downloadUrl = GetConfiguration().getValue<string>('avatar.asset.url');
|
||||
|
||||
for(const library of data)
|
||||
{
|
||||
if(!library) continue;
|
||||
|
||||
const libraryName = (library.id as string);
|
||||
const revision = (library.revision || '');
|
||||
|
||||
if(this._libraryNames.indexOf(libraryName) >= 0) continue;
|
||||
|
||||
this._libraryNames.push(libraryName);
|
||||
|
||||
const downloadLibrary = new AvatarAssetDownloadLibrary(libraryName, revision, downloadUrl, this._assets);
|
||||
|
||||
for(const part of library.parts)
|
||||
{
|
||||
const id = (part.id as string);
|
||||
const type = (part.type as string);
|
||||
const partString = (type + ':' + id);
|
||||
|
||||
let existing = this._figureMap.get(partString);
|
||||
|
||||
if(!existing) existing = [];
|
||||
|
||||
existing.push(downloadLibrary);
|
||||
|
||||
this._figureMap.set(partString, existing);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async processMissingLibraries(): Promise<void>
|
||||
{
|
||||
const promises: Promise<void>[] = [];
|
||||
|
||||
this._missingMandatoryLibs.forEach(value =>
|
||||
{
|
||||
const libraries = this._figureMap.get(value);
|
||||
|
||||
if(libraries) for(const library of libraries) promises.push(library.downloadAsset());
|
||||
});
|
||||
|
||||
this._missingMandatoryLibs = [];
|
||||
|
||||
await Promise.all(promises);
|
||||
}
|
||||
|
||||
private onLibraryLoaded(event: AvatarRenderLibraryEvent): void
|
||||
{
|
||||
if(!event || !event.library) return;
|
||||
|
||||
const loadedFigures: string[] = [];
|
||||
|
||||
for(const [figure, libraries] of this._incompleteFigures.entries())
|
||||
{
|
||||
let isReady = true;
|
||||
|
||||
for(const library of libraries)
|
||||
{
|
||||
if(!library || library.isLoaded) continue;
|
||||
|
||||
isReady = false;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if(isReady)
|
||||
{
|
||||
loadedFigures.push(figure);
|
||||
|
||||
const listeners = this._figureListeners.get(figure);
|
||||
|
||||
if(listeners)
|
||||
{
|
||||
for(const listener of listeners)
|
||||
{
|
||||
if(!listener || listener.disposed) continue;
|
||||
|
||||
listener.resetFigure(figure);
|
||||
}
|
||||
}
|
||||
|
||||
this._figureListeners.delete(figure);
|
||||
|
||||
GetEventDispatcher().dispatchEvent(new NitroEvent(NitroEventType.AVATAR_ASSET_LOADED));
|
||||
}
|
||||
}
|
||||
|
||||
for(const figure of loadedFigures)
|
||||
{
|
||||
if(!figure) continue;
|
||||
|
||||
this._incompleteFigures.delete(figure);
|
||||
}
|
||||
|
||||
let index = 0;
|
||||
|
||||
while(index < this._currentDownloads.length)
|
||||
{
|
||||
const download = this._currentDownloads[index];
|
||||
|
||||
if(download)
|
||||
{
|
||||
if(download.libraryName === event.library.libraryName) this._currentDownloads.splice(index, 1);
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
public isAvatarFigureContainerReady(container: IAvatarFigureContainer): boolean
|
||||
{
|
||||
return !this.getAvatarFigurePendingLibraries(container).length;
|
||||
}
|
||||
|
||||
private getAvatarFigurePendingLibraries(container: IAvatarFigureContainer): AvatarAssetDownloadLibrary[]
|
||||
{
|
||||
const pendingLibraries: AvatarAssetDownloadLibrary[] = [];
|
||||
|
||||
if(!container || !this._structure) return pendingLibraries;
|
||||
|
||||
const figureData = this._structure.figureData;
|
||||
|
||||
if(!figureData) return pendingLibraries;
|
||||
|
||||
const setKeys = container.getPartTypeIds();
|
||||
|
||||
for(const key of setKeys)
|
||||
{
|
||||
const set = figureData.getSetType(key);
|
||||
|
||||
if(!set) continue;
|
||||
|
||||
const figurePartSet = set.getPartSet(container.getPartSetId(key));
|
||||
|
||||
if(!figurePartSet) continue;
|
||||
|
||||
for(const part of figurePartSet.parts)
|
||||
{
|
||||
if(!part) continue;
|
||||
|
||||
const name = (part.type + ':' + part.id);
|
||||
const existing = this._figureMap.get(name);
|
||||
|
||||
if(existing === undefined) continue;
|
||||
|
||||
for(const library of existing)
|
||||
{
|
||||
if(!library || library.isLoaded) continue;
|
||||
|
||||
if(pendingLibraries.indexOf(library) >= 0) continue;
|
||||
|
||||
pendingLibraries.push(library);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return pendingLibraries;
|
||||
}
|
||||
|
||||
public downloadAvatarFigure(container: IAvatarFigureContainer, listener: IAvatarImageListener): void
|
||||
{
|
||||
const figure = container.getFigureString();
|
||||
const pendingLibraries = this.getAvatarFigurePendingLibraries(container);
|
||||
|
||||
if(pendingLibraries && pendingLibraries.length)
|
||||
{
|
||||
if(listener && !listener.disposed)
|
||||
{
|
||||
let listeners = this._figureListeners.get(figure);
|
||||
|
||||
if(!listeners)
|
||||
{
|
||||
listeners = [];
|
||||
|
||||
this._figureListeners.set(figure, listeners);
|
||||
}
|
||||
|
||||
listeners.push(listener);
|
||||
}
|
||||
|
||||
this._incompleteFigures.set(figure, pendingLibraries);
|
||||
|
||||
for(const library of pendingLibraries)
|
||||
{
|
||||
if(!library) continue;
|
||||
|
||||
library.downloadAsset();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(listener && !listener.disposed) listener.resetFigure(figure);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
import { IAvatarFigureContainer } from '@nitrots/api';
|
||||
|
||||
export class AvatarFigureContainer implements IAvatarFigureContainer
|
||||
{
|
||||
private _parts: Map<string, Map<string, any>>;
|
||||
|
||||
constructor(figure: string)
|
||||
{
|
||||
this._parts = new Map();
|
||||
|
||||
this.parseFigure(figure);
|
||||
}
|
||||
|
||||
public getPartTypeIds(): IterableIterator<string>
|
||||
{
|
||||
return this.partSets().keys();
|
||||
}
|
||||
|
||||
public hasPartType(k: string): boolean
|
||||
{
|
||||
return !!this.partSets().get(k);
|
||||
}
|
||||
|
||||
public getPartSetId(k: string): number
|
||||
{
|
||||
const existing = this.partSets().get(k);
|
||||
|
||||
if(!existing) return 0;
|
||||
|
||||
return existing.get('setid');
|
||||
}
|
||||
|
||||
public getPartColorIds(k: string): number[]
|
||||
{
|
||||
const existing = this.partSets().get(k);
|
||||
|
||||
if(!existing) return null;
|
||||
|
||||
return existing.get('colorids');
|
||||
}
|
||||
|
||||
public updatePart(setType: string, partSetId: number, colorIds: number[]): void
|
||||
{
|
||||
const set: Map<string, any> = new Map();
|
||||
|
||||
set.set('type', setType);
|
||||
set.set('setid', partSetId);
|
||||
set.set('colorids', colorIds);
|
||||
|
||||
const existingSets = this.partSets();
|
||||
|
||||
existingSets.delete(setType);
|
||||
existingSets.set(setType, set);
|
||||
}
|
||||
|
||||
public removePart(k: string): void
|
||||
{
|
||||
this.partSets().delete(k);
|
||||
}
|
||||
|
||||
public getFigureString(): string
|
||||
{
|
||||
const parts: string[] = [];
|
||||
|
||||
for(const key of this.partSets().keys())
|
||||
{
|
||||
if(!key) continue;
|
||||
|
||||
let setParts = [];
|
||||
|
||||
setParts.push(key);
|
||||
setParts.push(this.getPartSetId(key));
|
||||
|
||||
setParts = setParts.concat(this.getPartColorIds(key));
|
||||
|
||||
parts.push(setParts.join('-'));
|
||||
}
|
||||
|
||||
return parts.join('.');
|
||||
}
|
||||
|
||||
private partSets(): Map<string, Map<string, any>>
|
||||
{
|
||||
if(!this._parts) this._parts = new Map();
|
||||
|
||||
return this._parts;
|
||||
}
|
||||
|
||||
private parseFigure(figure: string): void
|
||||
{
|
||||
if(!figure) figure = '';
|
||||
|
||||
for(const part of figure.split('.'))
|
||||
{
|
||||
const pieces = part.split('-');
|
||||
|
||||
if(pieces.length >= 2)
|
||||
{
|
||||
const type = pieces[0];
|
||||
const setId = parseInt(pieces[1]);
|
||||
const colors = [];
|
||||
|
||||
let index = 2;
|
||||
|
||||
while(index < pieces.length)
|
||||
{
|
||||
colors.push(parseInt(pieces[index]));
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
this.updatePart(type, setId, colors);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,764 @@
|
||||
import { AvatarAction, AvatarDirectionAngle, AvatarScaleType, AvatarSetType, IActiveActionData, IAnimationLayerData, IAvatarDataContainer, IAvatarEffectListener, IAvatarFigureContainer, IAvatarImage, IPartColor, ISpriteDataContainer } from '@nitrots/api';
|
||||
import { GetRenderer, GetTexturePool, GetTickerTime, PaletteMapFilter, TextureUtils } from '@nitrots/utils';
|
||||
import { ColorMatrixFilter, Container, RenderTexture, Sprite, Texture } from 'pixi.js';
|
||||
import { AvatarFigureContainer } from './AvatarFigureContainer';
|
||||
import { AvatarStructure } from './AvatarStructure';
|
||||
import { EffectAssetDownloadManager } from './EffectAssetDownloadManager';
|
||||
import { ActiveActionData } from './actions';
|
||||
import { AssetAliasCollection } from './alias';
|
||||
import { AvatarImageCache } from './cache';
|
||||
import { AvatarCanvas } from './structure';
|
||||
|
||||
export class AvatarImage implements IAvatarImage, IAvatarEffectListener
|
||||
{
|
||||
private static CHANNELS_EQUAL: string = 'CHANNELS_EQUAL';
|
||||
private static CHANNELS_UNIQUE: string = 'CHANNELS_UNIQUE';
|
||||
private static CHANNELS_RED: string = 'CHANNELS_RED';
|
||||
private static CHANNELS_GREEN: string = 'CHANNELS_GREEN';
|
||||
private static CHANNELS_BLUE: string = 'CHANNELS_BLUE';
|
||||
private static CHANNELS_DESATURATED: string = 'CHANNELS_DESATURATED';
|
||||
private static DEFAULT_ACTION: string = 'Default';
|
||||
private static DEFAULT_DIRECTION: number = 2;
|
||||
private static DEFAULT_AVATAR_SET: string = AvatarSetType.FULL;
|
||||
|
||||
protected _mainDirection: number;
|
||||
protected _headDirection: number;
|
||||
protected _mainAction: IActiveActionData;
|
||||
protected _disposed: boolean = false;
|
||||
protected _canvasOffsets: number[] = [];
|
||||
protected _cache: AvatarImageCache;
|
||||
protected _avatarSpriteData: IAvatarDataContainer;
|
||||
protected _actions: ActiveActionData[] = [];
|
||||
protected _activeTexture: Texture = null;
|
||||
|
||||
private _defaultAction: IActiveActionData = null;
|
||||
private _frameCounter: number = 0;
|
||||
private _directionOffset: number = 0;
|
||||
private _changes: boolean = true;
|
||||
private _sprites: ISpriteDataContainer[];
|
||||
private _isAnimating: boolean = false;
|
||||
private _animationHasResetOnToggle: boolean = false;
|
||||
private _actionsSorted: boolean = false;
|
||||
private _sortedActions: IActiveActionData[];
|
||||
private _lastActionsString: string = null;
|
||||
private _currentActionsString: string = null;
|
||||
private _effectIdInUse: number = -1;
|
||||
private _animationFrameCount: number = -1;
|
||||
private _cachedBodyParts: string[] = [];
|
||||
private _cachedBodyPartsDirection: number = -1;
|
||||
private _cachedBodyPartsGeometryType: string = null;
|
||||
private _cachedBodyPartsAvatarSet: string = null;
|
||||
|
||||
constructor(
|
||||
private _structure: AvatarStructure,
|
||||
private _assets: AssetAliasCollection,
|
||||
private _figure: AvatarFigureContainer,
|
||||
private _scale: string,
|
||||
private _effectManager: EffectAssetDownloadManager,
|
||||
private _effectListener: IAvatarEffectListener = null)
|
||||
{
|
||||
if(!this._figure) this._figure = new AvatarFigureContainer('hr-893-45.hd-180-2.ch-210-66.lg-270-82.sh-300-91.wa-2007-.ri-1-');
|
||||
if(!this._scale) this._scale = AvatarScaleType.LARGE;
|
||||
|
||||
this._cache = new AvatarImageCache(this._structure, this, this._assets, this._scale);
|
||||
this.setDirection(AvatarImage.DEFAULT_AVATAR_SET, AvatarImage.DEFAULT_DIRECTION);
|
||||
this._defaultAction = new ActiveActionData(AvatarAction.POSTURE_STAND);
|
||||
this._defaultAction.definition = this._structure.getActionDefinition(AvatarImage.DEFAULT_ACTION);
|
||||
this.resetActions();
|
||||
this._animationFrameCount = 0;
|
||||
}
|
||||
|
||||
public dispose(): void
|
||||
{
|
||||
if(this._disposed) return;
|
||||
|
||||
this._structure = null;
|
||||
this._assets = null;
|
||||
this._mainAction = null;
|
||||
this._figure = null;
|
||||
this._avatarSpriteData = null;
|
||||
this._actions = null;
|
||||
|
||||
if(this._activeTexture)
|
||||
{
|
||||
GetTexturePool().putTexture(this._activeTexture);
|
||||
|
||||
this._activeTexture = null;
|
||||
}
|
||||
|
||||
if(this._cache)
|
||||
{
|
||||
this._cache.dispose();
|
||||
this._cache = null;
|
||||
}
|
||||
|
||||
this._canvasOffsets = null;
|
||||
this._disposed = true;
|
||||
}
|
||||
|
||||
public get disposed(): boolean
|
||||
{
|
||||
return this._disposed;
|
||||
}
|
||||
|
||||
public getFigure(): IAvatarFigureContainer
|
||||
{
|
||||
return this._figure;
|
||||
}
|
||||
|
||||
public getScale(): string
|
||||
{
|
||||
return this._scale;
|
||||
}
|
||||
|
||||
public getPartColor(k: string): IPartColor
|
||||
{
|
||||
return this._structure.getPartColor(this._figure, k);
|
||||
}
|
||||
|
||||
public setDirection(avatarPart: string, direction: number): void
|
||||
{
|
||||
direction += this._directionOffset;
|
||||
|
||||
if(direction < AvatarDirectionAngle.MIN_DIRECTION)
|
||||
{
|
||||
direction = AvatarDirectionAngle.MAX_DIRECTION + (direction + 1);
|
||||
}
|
||||
else if(direction > AvatarDirectionAngle.MAX_DIRECTION)
|
||||
{
|
||||
direction -= (AvatarDirectionAngle.MAX_DIRECTION + 1);
|
||||
}
|
||||
|
||||
if(this._structure.isMainAvatarSet(avatarPart)) this._mainDirection = direction;
|
||||
|
||||
// Special handling for head direction, including prevention checks for turning
|
||||
if(avatarPart === AvatarSetType.HEAD || avatarPart === AvatarSetType.FULL)
|
||||
{
|
||||
if(avatarPart === AvatarSetType.HEAD && this.isHeadTurnPreventedByAction()) direction = this._mainDirection;
|
||||
|
||||
this._headDirection = direction;
|
||||
}
|
||||
|
||||
this._cache.setDirection(avatarPart, direction);
|
||||
this._changes = true;
|
||||
}
|
||||
|
||||
public setDirectionAngle(k: string, _arg_2: number): void
|
||||
{
|
||||
this.setDirection(k, Math.floor(_arg_2 / 45));
|
||||
}
|
||||
|
||||
public getSprites(): ISpriteDataContainer[]
|
||||
{
|
||||
return this._sprites;
|
||||
}
|
||||
|
||||
public getCanvasOffsets(): number[]
|
||||
{
|
||||
return this._canvasOffsets;
|
||||
}
|
||||
|
||||
public getLayerData(k: ISpriteDataContainer): IAnimationLayerData
|
||||
{
|
||||
return this._structure.getBodyPartData(k.animation.id, this._frameCounter, k.id);
|
||||
}
|
||||
|
||||
public updateAnimationByFrames(k: number = 1): void
|
||||
{
|
||||
this._frameCounter += k;
|
||||
this._changes = true;
|
||||
}
|
||||
|
||||
public resetAnimationFrameCounter(): void
|
||||
{
|
||||
this._frameCounter = 0;
|
||||
this._changes = true;
|
||||
}
|
||||
|
||||
private getBodyParts(avatarSet: string, geometryType: string, direction: number): string[]
|
||||
{
|
||||
const shouldUpdateCache = direction !== this._cachedBodyPartsDirection || geometryType !== this._cachedBodyPartsGeometryType || avatarSet !== this._cachedBodyPartsAvatarSet;
|
||||
|
||||
if(shouldUpdateCache)
|
||||
{
|
||||
this._cachedBodyPartsDirection = direction;
|
||||
this._cachedBodyPartsGeometryType = geometryType;
|
||||
this._cachedBodyPartsAvatarSet = avatarSet;
|
||||
|
||||
this._cachedBodyParts = this._structure.getBodyParts(avatarSet, geometryType, direction);
|
||||
}
|
||||
|
||||
return this._cachedBodyParts;
|
||||
}
|
||||
|
||||
private buildAvatarContainer(avatarCanvas: AvatarCanvas, setType: string): Container
|
||||
{
|
||||
const bodyParts = this.getBodyParts(setType, this._mainAction.definition.geometryType, this._mainDirection);
|
||||
const container = new Container();
|
||||
|
||||
let partCount = (bodyParts.length - 1);
|
||||
|
||||
while(partCount >= 0)
|
||||
{
|
||||
const set = bodyParts[partCount];
|
||||
const part = this._cache.getImageContainer(set, this._frameCounter);
|
||||
|
||||
if(part)
|
||||
{
|
||||
const partCacheContainer = part.image;
|
||||
|
||||
if(partCacheContainer)
|
||||
{
|
||||
const partContainer = new Container();
|
||||
|
||||
partContainer.addChild(partCacheContainer);
|
||||
|
||||
const point = part.regPoint.clone();
|
||||
|
||||
point.x += avatarCanvas.offset.x;
|
||||
point.y += avatarCanvas.offset.y;
|
||||
|
||||
point.x += avatarCanvas.regPoint.x;
|
||||
point.y += avatarCanvas.regPoint.y;
|
||||
|
||||
partContainer.position.set(point.x, point.y);
|
||||
|
||||
container.addChild(partContainer);
|
||||
}
|
||||
}
|
||||
|
||||
partCount--;
|
||||
}
|
||||
|
||||
container.filters = [];
|
||||
|
||||
if(this._avatarSpriteData)
|
||||
{
|
||||
if(this._avatarSpriteData.colorTransform)
|
||||
{
|
||||
if(container.filters === undefined || container.filters === null) container.filters = [ this._avatarSpriteData.colorTransform ];
|
||||
else if(Array.isArray(container.filters)) container.filters = [ ...container.filters, this._avatarSpriteData.colorTransform ];
|
||||
else container.filters = [ container.filters, this._avatarSpriteData.colorTransform ];
|
||||
}
|
||||
|
||||
if(this._avatarSpriteData.paletteIsGrayscale)
|
||||
{
|
||||
this.convertToGrayscale(container);
|
||||
|
||||
const paletteMapFilter = new PaletteMapFilter({
|
||||
palette: this._avatarSpriteData.reds,
|
||||
channel: PaletteMapFilter.CHANNEL_RED
|
||||
});
|
||||
|
||||
if(container.filters === undefined || container.filters === null) container.filters = [ paletteMapFilter ];
|
||||
else if(Array.isArray(container.filters)) container.filters = [ ...container.filters, paletteMapFilter ];
|
||||
else container.filters = [ container.filters, paletteMapFilter ];
|
||||
}
|
||||
}
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
public processAsTexture(setType: string, hightlight: boolean): Texture
|
||||
{
|
||||
if(!this._changes) return this._activeTexture;
|
||||
|
||||
if(!this._mainAction) return null;
|
||||
|
||||
if(!this._actionsSorted) this.endActionAppends();
|
||||
|
||||
const avatarCanvas = this._structure.getCanvas(this._scale, this._mainAction.definition.geometryType);
|
||||
|
||||
if(!avatarCanvas) return null;
|
||||
|
||||
const container = this.buildAvatarContainer(avatarCanvas, setType);
|
||||
|
||||
if(this._activeTexture && ((this._activeTexture.width !== avatarCanvas.width) || (this._activeTexture.height !== avatarCanvas.height)))
|
||||
{
|
||||
GetTexturePool().putTexture(this._activeTexture);
|
||||
|
||||
this._activeTexture = null;
|
||||
}
|
||||
|
||||
if(!this._activeTexture) this._activeTexture = GetTexturePool().getTexture(avatarCanvas.width, avatarCanvas.height);
|
||||
|
||||
if(!this._activeTexture) return null;
|
||||
|
||||
GetRenderer().render({
|
||||
target: this._activeTexture,
|
||||
container: container,
|
||||
clear: true
|
||||
});
|
||||
|
||||
container.destroy();
|
||||
|
||||
//@ts-ignore
|
||||
this._activeTexture.source.hitMap = null;
|
||||
|
||||
this._changes = false;
|
||||
|
||||
return this._activeTexture;
|
||||
}
|
||||
|
||||
public processAsImageUrl(setType: string, scale: number = 1): string
|
||||
{
|
||||
const texture = this.processAsTexture(setType, false);
|
||||
const canvas = GetRenderer().texture.generateCanvas(texture);
|
||||
|
||||
const url = canvas.toDataURL('image/png');
|
||||
|
||||
return url;
|
||||
}
|
||||
|
||||
public processAsContainer(setType: string): Container
|
||||
{
|
||||
if(!this._mainAction) return null;
|
||||
|
||||
if(!this._actionsSorted) this.endActionAppends();
|
||||
|
||||
const avatarCanvas = this._structure.getCanvas(this._scale, this._mainAction.definition.geometryType);
|
||||
|
||||
if(!avatarCanvas) return null;
|
||||
|
||||
return this.buildAvatarContainer(avatarCanvas, setType);
|
||||
}
|
||||
|
||||
public applyPalette(texture: RenderTexture, reds: number[] = [], greens: number[] = [], blues: number[] = []): RenderTexture
|
||||
{
|
||||
const textureCanvas = TextureUtils.generateCanvas(texture);
|
||||
const textureCtx = textureCanvas.getContext('2d');
|
||||
const textureImageData = textureCtx.getImageData(0, 0, textureCanvas.width, textureCanvas.height);
|
||||
const data = textureImageData.data;
|
||||
|
||||
for(let i = 0; i < data.length; i += 4)
|
||||
{
|
||||
if(reds.length == 256)
|
||||
{
|
||||
let paletteColor = reds[data[i]];
|
||||
if(paletteColor === undefined) paletteColor = 0;
|
||||
|
||||
data[i] = ((paletteColor >> 16) & 0xFF);
|
||||
data[i + 1] = ((paletteColor >> 8) & 0xFF);
|
||||
data[i + 2] = (paletteColor & 0xFF);
|
||||
}
|
||||
|
||||
if(greens.length == 256)
|
||||
{
|
||||
let paletteColor = greens[data[i + 1]];
|
||||
if(paletteColor === undefined) paletteColor = 0;
|
||||
|
||||
data[i] = ((paletteColor >> 16) & 0xFF);
|
||||
data[i + 1] = ((paletteColor >> 8) & 0xFF);
|
||||
data[i + 2] = (paletteColor & 0xFF);
|
||||
}
|
||||
if(blues.length == 256)
|
||||
{
|
||||
let paletteColor = greens[data[i + 2]];
|
||||
if(paletteColor === undefined) paletteColor = 0;
|
||||
|
||||
data[i] = ((paletteColor >> 16) & 0xFF);
|
||||
data[i + 1] = ((paletteColor >> 8) & 0xFF);
|
||||
data[i + 2] = (paletteColor & 0xFF);
|
||||
}
|
||||
}
|
||||
|
||||
textureCtx.putImageData(textureImageData, 0, 0);
|
||||
|
||||
const newTexture = new Sprite(Texture.from(textureCanvas));
|
||||
|
||||
TextureUtils.writeToTexture(newTexture, texture, true);
|
||||
|
||||
return texture;
|
||||
}
|
||||
|
||||
public getDirection(): number
|
||||
{
|
||||
return this._mainDirection;
|
||||
}
|
||||
|
||||
public initActionAppends(): void
|
||||
{
|
||||
this._actions = [];
|
||||
this._actionsSorted = false;
|
||||
this._currentActionsString = '';
|
||||
}
|
||||
|
||||
public endActionAppends(): void
|
||||
{
|
||||
if(!this.sortActions()) return;
|
||||
|
||||
for(const k of this._sortedActions)
|
||||
{
|
||||
if(k.actionType === AvatarAction.EFFECT)
|
||||
{
|
||||
if(!this._effectManager.isAvatarEffectReady(parseInt(k.actionParameter))) this._effectManager.downloadAvatarEffect(parseInt(k.actionParameter), this);
|
||||
}
|
||||
}
|
||||
|
||||
this.resetActions();
|
||||
this.setActionsToParts();
|
||||
}
|
||||
|
||||
public appendAction(k: string, ..._args: any[]): boolean
|
||||
{
|
||||
let _local_3 = '';
|
||||
|
||||
this._actionsSorted = false;
|
||||
|
||||
if(_args && (_args.length > 0)) _local_3 = _args[0];
|
||||
|
||||
if((_local_3 !== undefined) && (_local_3 !== null)) _local_3 = _local_3.toString();
|
||||
|
||||
switch(k)
|
||||
{
|
||||
case AvatarAction.POSTURE:
|
||||
switch(_local_3)
|
||||
{
|
||||
case AvatarAction.POSTURE_LAY:
|
||||
case AvatarAction.POSTURE_WALK:
|
||||
case AvatarAction.POSTURE_STAND:
|
||||
case AvatarAction.POSTURE_SWIM:
|
||||
case AvatarAction.POSTURE_FLOAT:
|
||||
case AvatarAction.POSTURE_SIT:
|
||||
case AvatarAction.SNOWWAR_RUN:
|
||||
case AvatarAction.SNOWWAR_DIE_FRONT:
|
||||
case AvatarAction.SNOWWAR_DIE_BACK:
|
||||
case AvatarAction.SNOWWAR_PICK:
|
||||
case AvatarAction.SNOWWAR_THROW:
|
||||
if((_local_3 === AvatarAction.POSTURE_LAY) || (_local_3 === AvatarAction.POSTURE_LAY) || (_local_3 === AvatarAction.POSTURE_LAY))
|
||||
{
|
||||
if(_local_3 === AvatarAction.POSTURE_LAY)
|
||||
{
|
||||
if(this._mainDirection == 0)
|
||||
{
|
||||
this.setDirection(AvatarSetType.FULL, 4);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.setDirection(AvatarSetType.FULL, 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.addActionData(_local_3);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case AvatarAction.GESTURE:
|
||||
switch(_local_3)
|
||||
{
|
||||
case AvatarAction.GESTURE_AGGRAVATED:
|
||||
case AvatarAction.GESTURE_SAD:
|
||||
case AvatarAction.GESTURE_SMILE:
|
||||
case AvatarAction.GESTURE_SURPRISED:
|
||||
this.addActionData(_local_3);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case AvatarAction.EFFECT:
|
||||
case AvatarAction.DANCE:
|
||||
case AvatarAction.TALK:
|
||||
case AvatarAction.EXPRESSION_WAVE:
|
||||
case AvatarAction.SLEEP:
|
||||
case AvatarAction.SIGN:
|
||||
case AvatarAction.EXPRESSION_RESPECT:
|
||||
case AvatarAction.EXPRESSION_BLOW_A_KISS:
|
||||
case AvatarAction.EXPRESSION_LAUGH:
|
||||
case AvatarAction.EXPRESSION_CRY:
|
||||
case AvatarAction.EXPRESSION_IDLE:
|
||||
case AvatarAction.EXPRESSION_SNOWBOARD_OLLIE:
|
||||
case AvatarAction.EXPRESSION_SNOWBORD_360:
|
||||
case AvatarAction.EXPRESSION_RIDE_JUMP:
|
||||
if(_local_3 === AvatarAction.EFFECT)
|
||||
{
|
||||
if((((((_local_3 === '33') || (_local_3 === '34')) || (_local_3 === '35')) || (_local_3 === '36')) || (_local_3 === '38')) || (_local_3 === '39'))
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
||||
|
||||
this.addActionData(k, _local_3);
|
||||
break;
|
||||
case AvatarAction.CARRY_OBJECT:
|
||||
case AvatarAction.USE_OBJECT: {
|
||||
const _local_4 = this._structure.getActionDefinitionWithState(k);
|
||||
if(_local_4) _local_3 = _local_4.getParameterValue(_local_3);
|
||||
this.addActionData(k, _local_3);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected addActionData(actionType: string, actionParameter: string = ''): void
|
||||
{
|
||||
if(!this._actions) this._actions = [];
|
||||
|
||||
const actionExists = this._actions.some(action =>
|
||||
action.actionType === actionType && action.actionParameter === actionParameter
|
||||
);
|
||||
|
||||
if(!actionExists) this._actions.push(new ActiveActionData(actionType, actionParameter, this._frameCounter));
|
||||
}
|
||||
|
||||
public isAnimating(): boolean
|
||||
{
|
||||
return (this._isAnimating) || (this._animationFrameCount > 1);
|
||||
}
|
||||
|
||||
private resetActions(): boolean
|
||||
{
|
||||
this._animationHasResetOnToggle = false;
|
||||
this._isAnimating = false;
|
||||
this._sprites = [];
|
||||
this._avatarSpriteData = null;
|
||||
this._directionOffset = 0;
|
||||
this._structure.removeDynamicItems(this);
|
||||
this._mainAction = this._defaultAction;
|
||||
this._mainAction.definition = this._defaultAction.definition;
|
||||
this.resetBodyPartCache(this._defaultAction);
|
||||
return true;
|
||||
}
|
||||
|
||||
private isHeadTurnPreventedByAction(): boolean
|
||||
{
|
||||
if(!this._sortedActions) return false;
|
||||
|
||||
for(const action of this._sortedActions)
|
||||
{
|
||||
const actionDefinition = this._structure.getActionDefinitionWithState(action.actionType);
|
||||
|
||||
if(actionDefinition != null && actionDefinition.getPreventHeadTurn(action.actionParameter)) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private sortActions(): boolean
|
||||
{
|
||||
let hasChanges = false;
|
||||
let hasEffectAction = false;
|
||||
let effectChanged = false;
|
||||
|
||||
this._currentActionsString = '';
|
||||
this._sortedActions = this._structure.sortActions(this._actions);
|
||||
this._animationFrameCount = this._structure.maxFrames(this._sortedActions);
|
||||
|
||||
if(!this._sortedActions)
|
||||
{
|
||||
this._canvasOffsets = [0, 0, 0];
|
||||
|
||||
if(this._lastActionsString !== '')
|
||||
{
|
||||
hasChanges = true;
|
||||
|
||||
this._lastActionsString = '';
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
this._canvasOffsets = this._structure.getCanvasOffsets(this._sortedActions, this._scale, this._mainDirection);
|
||||
|
||||
for(const action of this._sortedActions)
|
||||
{
|
||||
this._currentActionsString += action.actionType + action.actionParameter;
|
||||
|
||||
if(action.actionType === AvatarAction.EFFECT)
|
||||
{
|
||||
const effectId = parseInt(action.actionParameter);
|
||||
|
||||
if(this._effectIdInUse !== effectId) effectChanged = true;
|
||||
|
||||
this._effectIdInUse = effectId;
|
||||
|
||||
hasEffectAction = true;
|
||||
}
|
||||
}
|
||||
|
||||
if(!hasEffectAction)
|
||||
{
|
||||
if(this._effectIdInUse > -1) effectChanged = true;
|
||||
|
||||
this._effectIdInUse = -1;
|
||||
}
|
||||
|
||||
if(effectChanged) this._cache.disposeInactiveActions();
|
||||
|
||||
if(this._lastActionsString != this._currentActionsString)
|
||||
{
|
||||
hasChanges = true;
|
||||
|
||||
this._lastActionsString = this._currentActionsString;
|
||||
}
|
||||
}
|
||||
|
||||
this._actionsSorted = true;
|
||||
|
||||
return hasChanges;
|
||||
}
|
||||
|
||||
private setActionsToParts(): void
|
||||
{
|
||||
if(!this._sortedActions) return;
|
||||
|
||||
const currentTime = GetTickerTime();
|
||||
const actionTypes: string[] = [];
|
||||
|
||||
for(const action of this._sortedActions) actionTypes.push(action.actionType);
|
||||
|
||||
for(const action of this._sortedActions)
|
||||
{
|
||||
if(action && action.definition && action.definition.isAnimation)
|
||||
{
|
||||
const animation = this._structure.getAnimation(`${action.definition.state}.${action.actionParameter}`);
|
||||
|
||||
if(animation && animation.hasOverriddenActions())
|
||||
{
|
||||
const overriddenActionNames = animation.overriddenActionNames();
|
||||
|
||||
if(overriddenActionNames)
|
||||
{
|
||||
for(const overriddenActionName of overriddenActionNames)
|
||||
{
|
||||
if(actionTypes.includes(overriddenActionName)) action.overridingAction = animation.overridingAction(overriddenActionName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(animation && animation.resetOnToggle) this._animationHasResetOnToggle = true;
|
||||
}
|
||||
}
|
||||
|
||||
for(const action of this._sortedActions)
|
||||
{
|
||||
if(action && action.definition)
|
||||
{
|
||||
if(action.definition.isAnimation && action.actionParameter === '') action.actionParameter = '1';
|
||||
|
||||
this.setActionToParts(action, currentTime);
|
||||
|
||||
if(action.definition.isAnimation)
|
||||
{
|
||||
this._isAnimating = action.definition.isAnimated(action.actionParameter);
|
||||
|
||||
const animation = this._structure.getAnimation(`${action.definition.state}.${action.actionParameter}`);
|
||||
|
||||
if(animation)
|
||||
{
|
||||
this._sprites = [...this._sprites, ...animation.spriteData];
|
||||
|
||||
if(animation.hasDirectionData()) this._directionOffset = animation.directionData.offset;
|
||||
|
||||
if(animation.hasAvatarData()) this._avatarSpriteData = animation.avatarData;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private setActionToParts(action: IActiveActionData, currentTime: number): void
|
||||
{
|
||||
if(!action || !action.definition || action.definition.assetPartDefinition === '') return;
|
||||
|
||||
if(action.definition.isMain)
|
||||
{
|
||||
this._mainAction = action;
|
||||
this._cache.setGeometryType(action.definition.geometryType);
|
||||
}
|
||||
|
||||
this._cache.setAction(action, currentTime);
|
||||
|
||||
this._changes = true;
|
||||
}
|
||||
|
||||
private resetBodyPartCache(action: IActiveActionData): void
|
||||
{
|
||||
if(!action || action.definition.assetPartDefinition === '') return;
|
||||
|
||||
if(action.definition.isMain)
|
||||
{
|
||||
this._mainAction = action;
|
||||
this._cache.setGeometryType(action.definition.geometryType);
|
||||
}
|
||||
|
||||
this._cache.resetBodyPartCache(action);
|
||||
|
||||
this._changes = true;
|
||||
}
|
||||
|
||||
private convertToGrayscale(container: Container, channel: string = 'CHANNELS_EQUAL'): Container
|
||||
{
|
||||
let redWeight = 0.33;
|
||||
let greenWeight = 0.33;
|
||||
let blueWeight = 0.33;
|
||||
|
||||
switch(channel)
|
||||
{
|
||||
case AvatarImage.CHANNELS_UNIQUE:
|
||||
redWeight = 0.3;
|
||||
greenWeight = 0.59;
|
||||
blueWeight = 0.11;
|
||||
break;
|
||||
case AvatarImage.CHANNELS_RED:
|
||||
redWeight = 1;
|
||||
greenWeight = 0;
|
||||
blueWeight = 0;
|
||||
break;
|
||||
case AvatarImage.CHANNELS_GREEN:
|
||||
redWeight = 0;
|
||||
greenWeight = 1;
|
||||
blueWeight = 0;
|
||||
break;
|
||||
case AvatarImage.CHANNELS_BLUE:
|
||||
redWeight = 0;
|
||||
greenWeight = 0;
|
||||
blueWeight = 1;
|
||||
break;
|
||||
case AvatarImage.CHANNELS_DESATURATED:
|
||||
redWeight = 0.3086;
|
||||
greenWeight = 0.6094;
|
||||
blueWeight = 0.082;
|
||||
break;
|
||||
}
|
||||
|
||||
const filter = new ColorMatrixFilter();
|
||||
|
||||
filter.matrix = [
|
||||
redWeight, greenWeight, blueWeight, 0, 0, // Red channel
|
||||
redWeight, greenWeight, blueWeight, 0, 0, // Green channel
|
||||
redWeight, greenWeight, blueWeight, 0, 0, // Blue channel
|
||||
0, 0, 0, 1, 0 // Alpha channel
|
||||
];
|
||||
|
||||
if(container.filters === undefined || container.filters === null) container.filters = [ filter ];
|
||||
else if(Array.isArray(container.filters)) container.filters = [ ...container.filters, filter ];
|
||||
else container.filters = [ container.filters, filter ];
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
public isPlaceholder(): boolean
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public get animationHasResetOnToggle(): boolean
|
||||
{
|
||||
return this._animationHasResetOnToggle;
|
||||
}
|
||||
|
||||
public resetEffect(effect: number): void
|
||||
{
|
||||
if(effect === this._effectIdInUse)
|
||||
{
|
||||
this.resetActions();
|
||||
this.setActionsToParts();
|
||||
|
||||
this._animationHasResetOnToggle = true;
|
||||
this._changes = true;
|
||||
|
||||
if(this._effectListener) this._effectListener.resetEffect(effect);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
import { Container, Point } from 'pixi.js';
|
||||
|
||||
export class AvatarImageBodyPartContainer
|
||||
{
|
||||
private _image: Container;
|
||||
private _regPoint: Point;
|
||||
private _offset: Point;
|
||||
private _isCacheable: boolean;
|
||||
|
||||
constructor(k: Container, _arg_2: Point, _arg_3: boolean)
|
||||
{
|
||||
this._image = k;
|
||||
this._regPoint = _arg_2;
|
||||
this._offset = new Point(0, 0);
|
||||
this._regPoint = _arg_2;
|
||||
this._isCacheable = _arg_3;
|
||||
|
||||
this.cleanPoints();
|
||||
}
|
||||
|
||||
public dispose(): void
|
||||
{
|
||||
if(this._image)
|
||||
{
|
||||
this._image.destroy({
|
||||
children: true
|
||||
});
|
||||
}
|
||||
|
||||
this._image = null;
|
||||
this._regPoint = null;
|
||||
this._offset = null;
|
||||
}
|
||||
|
||||
private cleanPoints(): void
|
||||
{
|
||||
// this._regPoint.x = this._regPoint.x;
|
||||
// this._regPoint.y = this._regPoint.y;
|
||||
// this._offset.x = this._offset.x;
|
||||
// this._offset.y = this._offset.y;
|
||||
}
|
||||
|
||||
public setRegPoint(k: Point): void
|
||||
{
|
||||
this._regPoint = k;
|
||||
|
||||
this.cleanPoints();
|
||||
}
|
||||
|
||||
public get image(): Container
|
||||
{
|
||||
return this._image;
|
||||
}
|
||||
|
||||
public set image(k: Container)
|
||||
{
|
||||
if(this._image && (this._image !== k))
|
||||
{
|
||||
this._image.destroy({
|
||||
children: true
|
||||
});
|
||||
}
|
||||
|
||||
this._image = k;
|
||||
}
|
||||
|
||||
public get regPoint(): Point
|
||||
{
|
||||
const clone = this._regPoint.clone();
|
||||
|
||||
clone.x += this._offset.x;
|
||||
clone.y += this._offset.y;
|
||||
|
||||
return clone;
|
||||
}
|
||||
|
||||
public set offset(k: Point)
|
||||
{
|
||||
this._offset = k;
|
||||
|
||||
this.cleanPoints();
|
||||
}
|
||||
|
||||
public get isCacheable(): boolean
|
||||
{
|
||||
return this._isCacheable;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
import { IActionDefinition, IPartColor } from '@nitrots/api';
|
||||
import { AvatarAnimationFrame } from './structure';
|
||||
|
||||
export class AvatarImagePartContainer
|
||||
{
|
||||
private _bodyPartId: string;
|
||||
private _partType: string;
|
||||
private _flippedPartType: string;
|
||||
private _partId: string;
|
||||
private _color: IPartColor;
|
||||
private _frames: AvatarAnimationFrame[];
|
||||
private _action: IActionDefinition;
|
||||
private _isColorable: boolean;
|
||||
private _isBlendable: boolean;
|
||||
private _paletteMapId: number;
|
||||
|
||||
constructor(bodyPartId: string, partType: string, partId: string, partColor: IPartColor, frames: AvatarAnimationFrame[], action: IActionDefinition, isColorable: boolean, paletteMapId: number, flippedPartType: string = '', isBlendable: boolean = false, _arg_11: number = 1)
|
||||
{
|
||||
this._bodyPartId = bodyPartId;
|
||||
this._partType = partType;
|
||||
this._partId = partId;
|
||||
this._color = partColor;
|
||||
this._frames = frames;
|
||||
this._action = action;
|
||||
this._isColorable = isColorable;
|
||||
this._paletteMapId = paletteMapId;
|
||||
this._flippedPartType = flippedPartType;
|
||||
this._isBlendable = isBlendable;
|
||||
|
||||
if(this._partType === 'ey') this._isColorable = false;
|
||||
}
|
||||
|
||||
public getFrameIndex(k: number): number
|
||||
{
|
||||
if(!this._frames || !this._frames.length) return 0;
|
||||
|
||||
const frameNumber = (k % this._frames.length);
|
||||
|
||||
if(this._frames[frameNumber] instanceof AvatarAnimationFrame)
|
||||
{
|
||||
return this._frames[frameNumber].number;
|
||||
}
|
||||
|
||||
return frameNumber;
|
||||
}
|
||||
|
||||
public getFrameDefinition(k: number): AvatarAnimationFrame
|
||||
{
|
||||
const frameNumber = (k % this._frames.length);
|
||||
|
||||
if(this._frames && (this._frames.length > frameNumber))
|
||||
{
|
||||
if(this._frames[frameNumber] instanceof AvatarAnimationFrame)
|
||||
{
|
||||
return this._frames[frameNumber];
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public getCacheableKey(k: number): string
|
||||
{
|
||||
const frameNumber = (k % this._frames.length);
|
||||
|
||||
if(this._frames && (this._frames.length > frameNumber))
|
||||
{
|
||||
if(this._frames[frameNumber] instanceof AvatarAnimationFrame)
|
||||
{
|
||||
const frame = this._frames[frameNumber];
|
||||
|
||||
return (this.partId + ':' + frame.assetPartDefinition + ':' + frame.number);
|
||||
}
|
||||
}
|
||||
|
||||
return (this.partId + ':' + frameNumber);
|
||||
}
|
||||
|
||||
public get bodyPartId(): string
|
||||
{
|
||||
return this._bodyPartId;
|
||||
}
|
||||
|
||||
public get partType(): string
|
||||
{
|
||||
return this._partType;
|
||||
}
|
||||
|
||||
public get partId(): string
|
||||
{
|
||||
return this._partId;
|
||||
}
|
||||
|
||||
public get color(): IPartColor
|
||||
{
|
||||
return this._color;
|
||||
}
|
||||
|
||||
public get action(): IActionDefinition
|
||||
{
|
||||
return this._action;
|
||||
}
|
||||
|
||||
public get isColorable(): boolean
|
||||
{
|
||||
return this._isColorable;
|
||||
}
|
||||
|
||||
public set isColorable(k: boolean)
|
||||
{
|
||||
this._isColorable = k;
|
||||
}
|
||||
|
||||
public get paletteMapId(): number
|
||||
{
|
||||
return this._paletteMapId;
|
||||
}
|
||||
|
||||
public get flippedPartType(): string
|
||||
{
|
||||
return this._flippedPartType;
|
||||
}
|
||||
|
||||
public get isBlendable(): boolean
|
||||
{
|
||||
return this._isBlendable;
|
||||
}
|
||||
|
||||
public toString(): string
|
||||
{
|
||||
return [this._bodyPartId, this._partType, this._partId].join(':');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,292 @@
|
||||
import { AvatarSetType, IAssetManager, IAvatarEffectListener, IAvatarFigureContainer, IAvatarImage, IAvatarImageListener, IAvatarRenderManager, IFigureData, IFigurePartSet, IGraphicAsset, IStructureData } from '@nitrots/api';
|
||||
import { GetAssetManager } from '@nitrots/assets';
|
||||
import { GetConfiguration } from '@nitrots/configuration';
|
||||
import { GetEventDispatcher, NitroEventType } from '@nitrots/events';
|
||||
import { AvatarAssetDownloadManager } from './AvatarAssetDownloadManager';
|
||||
import { AvatarFigureContainer } from './AvatarFigureContainer';
|
||||
import { AvatarImage } from './AvatarImage';
|
||||
import { AvatarStructure } from './AvatarStructure';
|
||||
import { EffectAssetDownloadManager } from './EffectAssetDownloadManager';
|
||||
import { FigureDataContainer } from './FigureDataContainer';
|
||||
import { PlaceHolderAvatarImage } from './PlaceHolderAvatarImage';
|
||||
import { AssetAliasCollection } from './alias';
|
||||
import { HabboAvatarAnimations } from './data/HabboAvatarAnimations';
|
||||
import { HabboAvatarGeometry } from './data/HabboAvatarGeometry';
|
||||
import { HabboAvatarPartSets } from './data/HabboAvatarPartSets';
|
||||
|
||||
export class AvatarRenderManager implements IAvatarRenderManager
|
||||
{
|
||||
private static DEFAULT_FIGURE: string = 'hd-99999-99999';
|
||||
|
||||
private _structure: AvatarStructure = new AvatarStructure(this);
|
||||
private _aliasCollection: AssetAliasCollection = new AssetAliasCollection(this, GetAssetManager());
|
||||
private _avatarAssetDownloadManager: AvatarAssetDownloadManager = new AvatarAssetDownloadManager(GetAssetManager(), this._structure);
|
||||
private _effectAssetDownloadManager: EffectAssetDownloadManager = new EffectAssetDownloadManager(GetAssetManager(), this._structure);
|
||||
|
||||
private _placeHolderFigure: AvatarFigureContainer = new AvatarFigureContainer(AvatarRenderManager.DEFAULT_FIGURE);
|
||||
|
||||
public async init(): Promise<void>
|
||||
{
|
||||
this._structure?.initGeometry(HabboAvatarGeometry.geometry);
|
||||
this._structure?.initPartSets(HabboAvatarPartSets.partSets);
|
||||
|
||||
await this.loadActions();
|
||||
|
||||
this._structure?.initAnimation(HabboAvatarAnimations.animations);
|
||||
await this.loadFigureData();
|
||||
|
||||
this._aliasCollection.init();
|
||||
|
||||
GetEventDispatcher().addEventListener(NitroEventType.AVATAR_ASSET_LOADED, () => this._aliasCollection.reset());
|
||||
GetEventDispatcher().addEventListener(NitroEventType.AVATAR_EFFECT_LOADED, () => this._aliasCollection.reset());
|
||||
|
||||
await this._avatarAssetDownloadManager.init();
|
||||
await this._effectAssetDownloadManager.init();
|
||||
}
|
||||
|
||||
private async loadActions(): Promise<void>
|
||||
{
|
||||
const defaultActions = GetConfiguration().getValue<string>('avatar.default.actions');
|
||||
|
||||
if(defaultActions) this._structure.initActions(GetAssetManager(), defaultActions);
|
||||
|
||||
const url = GetConfiguration().getValue<string>('avatar.actions.url');
|
||||
|
||||
if(!url || !url.length) throw new Error('Invalid avatar action url');
|
||||
|
||||
const response = await fetch(url);
|
||||
|
||||
if(response.status !== 200) throw new Error('Invalid avatar action file');
|
||||
|
||||
this._structure.updateActions(await response.json());
|
||||
}
|
||||
|
||||
private async loadFigureData(): Promise<void>
|
||||
{
|
||||
const defaultFigureData = GetConfiguration().getValue<IFigureData>('avatar.default.figuredata');
|
||||
|
||||
if(defaultFigureData) this._structure?.initFigureData(defaultFigureData);
|
||||
|
||||
const url = GetConfiguration().getValue<string>('avatar.figuredata.url');
|
||||
|
||||
if(!url || !url.length) throw new Error('Invalid figure data url');
|
||||
|
||||
const response = await fetch(url);
|
||||
|
||||
if(response.status !== 200) throw new Error('Invalid figure data file');
|
||||
|
||||
this._structure.figureData.appendJSON(await response.json());
|
||||
|
||||
this._structure.init();
|
||||
}
|
||||
|
||||
public createFigureContainer(figure: string): IAvatarFigureContainer
|
||||
{
|
||||
return new AvatarFigureContainer(figure);
|
||||
}
|
||||
|
||||
public isFigureContainerReady(container: IAvatarFigureContainer): boolean
|
||||
{
|
||||
if(!this._avatarAssetDownloadManager) return false;
|
||||
|
||||
return this._avatarAssetDownloadManager.isAvatarFigureContainerReady(container);
|
||||
}
|
||||
|
||||
public createAvatarImage(figure: string, size: string, gender: string, listener: IAvatarImageListener = null, effectListener: IAvatarEffectListener = null): IAvatarImage
|
||||
{
|
||||
if(!this._structure || !this._avatarAssetDownloadManager) return null;
|
||||
|
||||
const figureContainer = new AvatarFigureContainer(figure);
|
||||
|
||||
if(gender) this.validateAvatarFigure(figureContainer, gender);
|
||||
|
||||
if(this._avatarAssetDownloadManager.isAvatarFigureContainerReady(figureContainer))
|
||||
{
|
||||
return new AvatarImage(this._structure, this._aliasCollection, figureContainer, size, this._effectAssetDownloadManager, effectListener);
|
||||
}
|
||||
|
||||
this._avatarAssetDownloadManager.downloadAvatarFigure(figureContainer, listener);
|
||||
|
||||
return new PlaceHolderAvatarImage(this._structure, this._aliasCollection, this._placeHolderFigure, size, this._effectAssetDownloadManager);
|
||||
}
|
||||
|
||||
public downloadAvatarFigure(container: IAvatarFigureContainer, listener: IAvatarImageListener): void
|
||||
{
|
||||
if(!this._avatarAssetDownloadManager) return;
|
||||
|
||||
this._avatarAssetDownloadManager.downloadAvatarFigure(container, listener);
|
||||
}
|
||||
|
||||
private validateAvatarFigure(container: AvatarFigureContainer, gender: string): boolean
|
||||
{
|
||||
let isValid = false;
|
||||
|
||||
const typeIds = this._structure.getMandatorySetTypeIds(gender, 2);
|
||||
|
||||
if(typeIds)
|
||||
{
|
||||
const figureData = this._structure.figureData;
|
||||
|
||||
for(const id of typeIds)
|
||||
{
|
||||
if(!container.hasPartType(id))
|
||||
{
|
||||
const figurePartSet = this._structure.getDefaultPartSet(id, gender);
|
||||
|
||||
if(figurePartSet)
|
||||
{
|
||||
container.updatePart(id, figurePartSet.id, [0]);
|
||||
|
||||
isValid = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const setType = figureData.getSetType(id);
|
||||
|
||||
if(setType)
|
||||
{
|
||||
const figurePartSet = setType.getPartSet(container.getPartSetId(id));
|
||||
|
||||
if(!figurePartSet)
|
||||
{
|
||||
const partSet = this._structure.getDefaultPartSet(id, gender);
|
||||
|
||||
if(partSet)
|
||||
{
|
||||
container.updatePart(id, partSet.id, [0]);
|
||||
|
||||
isValid = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return !(isValid);
|
||||
}
|
||||
|
||||
public getFigureClubLevel(container: IAvatarFigureContainer, gender: string, searchParts: string[] = null): number
|
||||
{
|
||||
if(!this._structure) return 0;
|
||||
|
||||
const figureData = this._structure.figureData;
|
||||
const parts = Array.from(container.getPartTypeIds());
|
||||
|
||||
let clubLevel = 0;
|
||||
|
||||
for(const part of parts)
|
||||
{
|
||||
const set = figureData.getSetType(part);
|
||||
|
||||
if(!set) continue;
|
||||
|
||||
const setId = container.getPartSetId(part);
|
||||
const partSet = set.getPartSet(setId);
|
||||
|
||||
if(partSet)
|
||||
{
|
||||
clubLevel = Math.max(partSet.clubLevel, clubLevel);
|
||||
|
||||
const palette = figureData.getPalette(set.paletteID);
|
||||
const colors = container.getPartColorIds(part);
|
||||
|
||||
for(const colorId of colors)
|
||||
{
|
||||
const color = palette.getColor(colorId);
|
||||
|
||||
if(!color) continue;
|
||||
|
||||
clubLevel = Math.max(color.clubLevel, clubLevel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!searchParts) searchParts = this._structure.getBodyPartsUnordered(AvatarSetType.FULL);
|
||||
|
||||
for(const part of searchParts)
|
||||
{
|
||||
const set = figureData.getSetType(part);
|
||||
|
||||
if(!set) continue;
|
||||
|
||||
if(parts.indexOf(part) === -1) clubLevel = Math.max(set.optionalFromClubLevel(gender), clubLevel);
|
||||
}
|
||||
|
||||
return clubLevel;
|
||||
}
|
||||
|
||||
public isValidFigureSetForGender(setId: number, gender: string): boolean
|
||||
{
|
||||
const structure = this.structureData;
|
||||
const partSet = structure.getFigurePartSet(setId);
|
||||
|
||||
return !!(partSet && ((partSet.gender.toUpperCase() === 'U') || (partSet.gender.toUpperCase() === gender.toUpperCase())));
|
||||
}
|
||||
|
||||
public getFigureStringWithFigureIds(figure: string, gender: string, _arg_3: number[]): string
|
||||
{
|
||||
const container = new FigureDataContainer();
|
||||
|
||||
container.loadAvatarData(figure, gender);
|
||||
|
||||
const partSets: IFigurePartSet[] = this.resolveFigureSets(_arg_3);
|
||||
|
||||
for(const partSet of partSets)
|
||||
{
|
||||
container.savePartData(partSet.type, partSet.id, container.getColourIds(partSet.type));
|
||||
}
|
||||
|
||||
return container.getFigureString();
|
||||
}
|
||||
|
||||
private resolveFigureSets(setIds: number[]): IFigurePartSet[]
|
||||
{
|
||||
const structure = this.structureData;
|
||||
const partSets: IFigurePartSet[] = [];
|
||||
|
||||
for(const setId of setIds)
|
||||
{
|
||||
const partSet = structure.getFigurePartSet(setId);
|
||||
|
||||
if(partSet) partSets.push(partSet);
|
||||
}
|
||||
|
||||
return partSets;
|
||||
}
|
||||
|
||||
public getMandatoryAvatarPartSetIds(k: string, _arg_2: number): string[]
|
||||
{
|
||||
if(!this._structure) return null;
|
||||
|
||||
return this._structure.getMandatorySetTypeIds(k, _arg_2);
|
||||
}
|
||||
|
||||
public getAssetByName(name: string): IGraphicAsset
|
||||
{
|
||||
return this._aliasCollection.getAsset(name);
|
||||
}
|
||||
|
||||
public get assets(): IAssetManager
|
||||
{
|
||||
return GetAssetManager();
|
||||
}
|
||||
|
||||
public get structure(): AvatarStructure
|
||||
{
|
||||
return this._structure;
|
||||
}
|
||||
|
||||
public get structureData(): IStructureData
|
||||
{
|
||||
if(this._structure) return this._structure.figureData;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public get downloadManager(): AvatarAssetDownloadManager
|
||||
{
|
||||
return this._avatarAssetDownloadManager;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,619 @@
|
||||
import { AvatarDirectionAngle, IActionDefinition, IActiveActionData, IAssetAnimation, IAssetManager, IAvatarFigureContainer, IAvatarImage, IAvatarRenderManager, IFigureData, IFigurePartSet, IPartColor, IStructureData } from '@nitrots/api';
|
||||
import { Point } from 'pixi.js';
|
||||
import { AvatarImagePartContainer } from './AvatarImagePartContainer';
|
||||
import { AvatarRenderManager } from './AvatarRenderManager';
|
||||
import { ActionDefinition, AvatarActionManager } from './actions';
|
||||
import { Animation, AnimationManager, AvatarAnimationLayerData } from './animation';
|
||||
import { AvatarModelGeometry } from './geometry';
|
||||
import { AnimationAction, AvatarAnimationData, AvatarAnimationFrame, AvatarCanvas, FigureSetData, PartSetsData } from './structure';
|
||||
|
||||
export class AvatarStructure
|
||||
{
|
||||
private _renderManager: AvatarRenderManager;
|
||||
private _geometry: AvatarModelGeometry;
|
||||
private _figureData: FigureSetData;
|
||||
private _partSetsData: PartSetsData;
|
||||
private _animationData: AvatarAnimationData;
|
||||
private _animationManager: AnimationManager;
|
||||
private _mandatorySetTypeIds: { [index: string]: { [index: number]: string[] } };
|
||||
private _actionManager: AvatarActionManager;
|
||||
private _defaultAction: IActionDefinition;
|
||||
|
||||
constructor(renderManager: AvatarRenderManager)
|
||||
{
|
||||
this._renderManager = renderManager;
|
||||
this._geometry = null;
|
||||
this._figureData = new FigureSetData();
|
||||
this._partSetsData = new PartSetsData();
|
||||
this._animationData = new AvatarAnimationData();
|
||||
this._animationManager = new AnimationManager();
|
||||
this._mandatorySetTypeIds = {};
|
||||
this._actionManager = null;
|
||||
this._defaultAction = null;
|
||||
}
|
||||
|
||||
public init(): void
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public initGeometry(k: any): void
|
||||
{
|
||||
if(!k) return;
|
||||
|
||||
this._geometry = new AvatarModelGeometry(k);
|
||||
}
|
||||
|
||||
public initActions(k: IAssetManager, _arg_2: any): void
|
||||
{
|
||||
if(!_arg_2) return;
|
||||
|
||||
this._actionManager = new AvatarActionManager(k, _arg_2);
|
||||
this._defaultAction = this._actionManager.getDefaultAction();
|
||||
}
|
||||
|
||||
public updateActions(data: any): void
|
||||
{
|
||||
this._actionManager.updateActions(data);
|
||||
|
||||
this._defaultAction = this._actionManager.getDefaultAction();
|
||||
}
|
||||
|
||||
public initPartSets(k: any): boolean
|
||||
{
|
||||
if(!k) return false;
|
||||
|
||||
if(this._partSetsData.parse(k))
|
||||
{
|
||||
this._partSetsData.getPartDefinition('ri').appendToFigure = true;
|
||||
this._partSetsData.getPartDefinition('li').appendToFigure = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public initAnimation(k: any): boolean
|
||||
{
|
||||
if(!k) return false;
|
||||
|
||||
return this._animationData.parse(k);
|
||||
}
|
||||
|
||||
public initFigureData(k: IFigureData): boolean
|
||||
{
|
||||
if(!k) return false;
|
||||
|
||||
return this._figureData.parse(k);
|
||||
}
|
||||
|
||||
public injectFigureData(data: IFigureData): void
|
||||
{
|
||||
this._figureData.injectJSON(data);
|
||||
}
|
||||
|
||||
public registerAnimations(k: IAssetManager, _arg_2: string = 'fx', _arg_3: number = 200): void
|
||||
{
|
||||
let index = 0;
|
||||
|
||||
while(index < _arg_3)
|
||||
{
|
||||
const collection = k.getCollection((_arg_2 + index));
|
||||
|
||||
if(collection)
|
||||
{
|
||||
const animationData = collection.data;
|
||||
|
||||
this._animationManager.registerAnimation(this, animationData.animations);
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
public registerAnimation(data: { [index: string]: IAssetAnimation }): void
|
||||
{
|
||||
this._animationManager.registerAnimation(this, data);
|
||||
}
|
||||
|
||||
public getPartColor(k: IAvatarFigureContainer, _arg_2: string, _arg_3: number = 0): IPartColor
|
||||
{
|
||||
const _local_4 = k.getPartColorIds(_arg_2);
|
||||
|
||||
if((!(_local_4)) || (_local_4.length < _arg_3)) return null;
|
||||
|
||||
const _local_5 = this._figureData.getSetType(_arg_2);
|
||||
|
||||
if(_local_5 == null) return null;
|
||||
|
||||
const _local_6 = this._figureData.getPalette(_local_5.paletteID);
|
||||
|
||||
if(!_local_6) return null;
|
||||
|
||||
return _local_6.getColor(_local_4[_arg_3]);
|
||||
}
|
||||
|
||||
public getBodyPartData(animation: string, frameCount: number, spriteId: string): AvatarAnimationLayerData
|
||||
{
|
||||
return this._animationManager.getLayerData(animation, frameCount, spriteId) as AvatarAnimationLayerData;
|
||||
}
|
||||
|
||||
public getAnimation(k: string): Animation
|
||||
{
|
||||
return this._animationManager.getAnimation(k) as Animation;
|
||||
}
|
||||
|
||||
public getActionDefinition(k: string): ActionDefinition
|
||||
{
|
||||
return this._actionManager.getActionDefinition(k);
|
||||
}
|
||||
|
||||
public getActionDefinitionWithState(k: string): ActionDefinition
|
||||
{
|
||||
return this._actionManager.getActionDefinitionWithState(k);
|
||||
}
|
||||
|
||||
public isMainAvatarSet(k: string): boolean
|
||||
{
|
||||
return this._geometry.isMainAvatarSet(k);
|
||||
}
|
||||
|
||||
public sortActions(k: IActiveActionData[]): IActiveActionData[]
|
||||
{
|
||||
return this._actionManager.sortActions(k);
|
||||
}
|
||||
|
||||
public maxFrames(k: IActiveActionData[]): number
|
||||
{
|
||||
let _local_2 = 0;
|
||||
|
||||
for(const _local_3 of k)
|
||||
{
|
||||
_local_2 = Math.max(_local_2, this._animationData.getFrameCount(_local_3.definition));
|
||||
}
|
||||
return _local_2;
|
||||
}
|
||||
|
||||
public getMandatorySetTypeIds(k: string, _arg_2: number): string[]
|
||||
{
|
||||
if(!this._mandatorySetTypeIds[k])
|
||||
{
|
||||
this._mandatorySetTypeIds[k] = [];
|
||||
}
|
||||
|
||||
if(this._mandatorySetTypeIds[k][_arg_2])
|
||||
{
|
||||
return this._mandatorySetTypeIds[k][_arg_2];
|
||||
}
|
||||
|
||||
this._mandatorySetTypeIds[k][_arg_2] = this._figureData.getMandatorySetTypeIds(k, _arg_2);
|
||||
|
||||
return this._mandatorySetTypeIds[k][_arg_2];
|
||||
}
|
||||
|
||||
public getDefaultPartSet(k: string, _arg_2: string): IFigurePartSet
|
||||
{
|
||||
return this._figureData.getDefaultPartSet(k, _arg_2);
|
||||
}
|
||||
|
||||
public getCanvasOffsets(k: IActiveActionData[], _arg_2: string, _arg_3: number): number[]
|
||||
{
|
||||
return this._actionManager.getCanvasOffsets(k, _arg_2, _arg_3);
|
||||
}
|
||||
|
||||
public getCanvas(k: string, _arg_2: string): AvatarCanvas
|
||||
{
|
||||
return this._geometry.getCanvas(k, _arg_2);
|
||||
}
|
||||
|
||||
public removeDynamicItems(k: IAvatarImage): void
|
||||
{
|
||||
this._geometry.removeDynamicItems(k);
|
||||
}
|
||||
|
||||
public getActiveBodyPartIds(k: IActiveActionData, _arg_2: IAvatarImage): string[]
|
||||
{
|
||||
let _local_3: string[] = [];
|
||||
|
||||
const _local_4: string[] = [];
|
||||
const _local_5 = k.definition.geometryType;
|
||||
|
||||
if(k.definition.isAnimation)
|
||||
{
|
||||
const _local_7 = ((k.definition.state + '.') + k.actionParameter);
|
||||
const _local_8 = this._animationManager.getAnimation(_local_7);
|
||||
|
||||
if(_local_8)
|
||||
{
|
||||
_local_3 = _local_8.getAnimatedBodyPartIds(0, k.overridingAction);
|
||||
|
||||
if(_local_8.hasAddData())
|
||||
{
|
||||
const _local_11 = {
|
||||
id: '',
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0,
|
||||
radius: 0.01,
|
||||
nx: 0,
|
||||
ny: 0,
|
||||
nz: -1,
|
||||
double: 1
|
||||
};
|
||||
|
||||
const _local_12 = {
|
||||
setType: ''
|
||||
};
|
||||
|
||||
for(const _local_13 of _local_8.addData)
|
||||
{
|
||||
const _local_6 = this._geometry.getBodyPart(_local_5, _local_13.align);
|
||||
|
||||
if(_local_6)
|
||||
{
|
||||
_local_11.id = _local_13.id;
|
||||
_local_6.addPart(_local_11, _arg_2);
|
||||
|
||||
_local_12.setType = _local_13.id;
|
||||
|
||||
const _local_10 = this._partSetsData.addPartDefinition(_local_12);
|
||||
_local_10.appendToFigure = true;
|
||||
|
||||
if(_local_13.base === '') _local_10.staticId = 1;
|
||||
|
||||
if(_local_4.indexOf(_local_6.id) === -1) _local_4.push(_local_6.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(const _local_9 of _local_3)
|
||||
{
|
||||
const _local_6 = this._geometry.getBodyPart(_local_5, _local_9);
|
||||
|
||||
if(_local_6 && (_local_4.indexOf(_local_6.id) === -1)) _local_4.push(_local_6.id);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_local_3 = this._partSetsData.getActiveParts(k.definition);
|
||||
|
||||
for(const _local_14 of _local_3)
|
||||
{
|
||||
const _local_6 = this._geometry.getBodyPartOfItem(_local_5, _local_14, _arg_2);
|
||||
|
||||
if(_local_6 && (_local_4.indexOf(_local_6.id) === -1)) _local_4.push(_local_6.id);
|
||||
}
|
||||
}
|
||||
|
||||
return _local_4;
|
||||
}
|
||||
|
||||
public getBodyPartsUnordered(k: string): string[]
|
||||
{
|
||||
return this._geometry.getBodyPartIdsInAvatarSet(k);
|
||||
}
|
||||
|
||||
public getBodyParts(k: string, _arg_2: string, _arg_3: number): string[]
|
||||
{
|
||||
const _local_4 = AvatarDirectionAngle.DIRECTION_TO_ANGLE[_arg_3];
|
||||
|
||||
return this._geometry.getBodyPartsAtAngle(k, _local_4, _arg_2);
|
||||
}
|
||||
|
||||
public getFrameBodyPartOffset(k: IActiveActionData, _arg_2: number, _arg_3: number, _arg_4: string): Point
|
||||
{
|
||||
const _local_5 = this._animationData.getAction(k.definition);
|
||||
|
||||
if(_local_5) return _local_5.getFrameBodyPartOffset(_arg_2, _arg_3, _arg_4);
|
||||
|
||||
return AnimationAction.DEFAULT_OFFSET;
|
||||
}
|
||||
|
||||
public getParts(k: string, _arg_2: IAvatarFigureContainer, _arg_3: IActiveActionData, _arg_4: string, _arg_5: number, removes: string[], _arg_7: IAvatarImage, _arg_8: Map<string, string> = null): AvatarImagePartContainer[]
|
||||
{
|
||||
const _local_10: Animation = null;
|
||||
let _local_34: IActionDefinition = null;
|
||||
|
||||
let _local_20: AvatarAnimationFrame[] = [];
|
||||
let _local_36: IPartColor = null;
|
||||
|
||||
if(!_arg_3 == null) return [];
|
||||
|
||||
const _local_9 = this._partSetsData.getActiveParts(_arg_3.definition);
|
||||
const _local_11: AvatarImagePartContainer[] = [];
|
||||
let _local_14: any[] = [0];
|
||||
const _local_15 = this._animationData.getAction(_arg_3.definition);
|
||||
|
||||
if(_arg_3.definition.isAnimation)
|
||||
{
|
||||
const _local_24 = ((_arg_3.definition.state + '.') + _arg_3.actionParameter);
|
||||
const _local_10 = this._animationManager.getAnimation(_local_24);
|
||||
|
||||
if(_local_10)
|
||||
{
|
||||
_local_14 = this.getPopulatedArray(_local_10.frameCount(_arg_3.overridingAction));
|
||||
|
||||
for(const _local_25 of _local_10.getAnimatedBodyPartIds(0, _arg_3.overridingAction))
|
||||
{
|
||||
if(_local_25 === k)
|
||||
{
|
||||
const _local_26 = this._geometry.getBodyPart(_arg_4, _local_25);
|
||||
|
||||
if(_local_26)
|
||||
{
|
||||
for(const _local_27 of _local_26.getDynamicParts(_arg_7))
|
||||
{
|
||||
_local_9.push(_local_27.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const _local_16 = this._geometry.getParts(_arg_4, k, _arg_5, _local_9, _arg_7);
|
||||
const _local_21 = _arg_2.getPartTypeIds();
|
||||
|
||||
for(const _local_17 of _local_21)
|
||||
{
|
||||
if(_arg_8)
|
||||
{
|
||||
if(_arg_8.get(_local_17)) continue;
|
||||
}
|
||||
|
||||
const _local_28 = _arg_2.getPartSetId(_local_17);
|
||||
const _local_29 = _arg_2.getPartColorIds(_local_17);
|
||||
const _local_30 = this._figureData.getSetType(_local_17);
|
||||
|
||||
|
||||
|
||||
if(_local_30)
|
||||
{
|
||||
const _local_31 = this._figureData.getPalette(_local_30.paletteID);
|
||||
|
||||
if(_local_31)
|
||||
{
|
||||
const _local_32 = _local_30.getPartSet(_local_28);
|
||||
|
||||
if(_local_32)
|
||||
{
|
||||
removes = removes.concat(_local_32.hiddenLayers);
|
||||
|
||||
for(const _local_33 of _local_32.parts)
|
||||
{
|
||||
if(_local_16.indexOf(_local_33.type) > -1)
|
||||
{
|
||||
if(_local_15)
|
||||
{
|
||||
const _local_19 = _local_15.getPart(_local_33.type);
|
||||
|
||||
if(_local_19)
|
||||
{
|
||||
_local_20 = _local_19.frames;
|
||||
}
|
||||
else
|
||||
{
|
||||
_local_20 = _local_14;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_local_20 = _local_14;
|
||||
}
|
||||
|
||||
_local_34 = _arg_3.definition;
|
||||
|
||||
if(_local_9.indexOf(_local_33.type) === -1) _local_34 = this._defaultAction;
|
||||
|
||||
const _local_13 = this._partSetsData.getPartDefinition(_local_33.type);
|
||||
|
||||
let _local_35 = (!_local_13) ? _local_33.type : _local_13.flippedSetType;
|
||||
|
||||
if(!_local_35 || (_local_35 === '')) _local_35 = _local_33.type;
|
||||
|
||||
if(_local_29 && (_local_29.length > (_local_33.colorLayerIndex - 1)))
|
||||
{
|
||||
_local_36 = _local_31.getColor(_local_29[(_local_33.colorLayerIndex - 1)]);
|
||||
}
|
||||
|
||||
const _local_37 = (_local_33.colorLayerIndex > 0);
|
||||
const _local_18 = new AvatarImagePartContainer(k, _local_33.type, _local_33.id.toString(), _local_36, _local_20, _local_34, _local_37, _local_33.paletteMap, _local_35);
|
||||
|
||||
_local_11.push(_local_18);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const _local_22: AvatarImagePartContainer[] = [];
|
||||
|
||||
for(const _local_12 of _local_16)
|
||||
{
|
||||
let _local_39: IPartColor = null;
|
||||
let _local_38 = false;
|
||||
|
||||
const _local_40 = ((_arg_8) && (_arg_8.get(_local_12)));
|
||||
|
||||
for(const _local_23 of _local_11)
|
||||
{
|
||||
if(_local_23.partType === _local_12)
|
||||
{
|
||||
if(_local_40)
|
||||
{
|
||||
_local_39 = _local_23.color;
|
||||
}
|
||||
else
|
||||
{
|
||||
_local_38 = true;
|
||||
|
||||
if(removes.indexOf(_local_12) === -1) _local_22.push(_local_23);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!_local_38)
|
||||
{
|
||||
if(_local_40)
|
||||
{
|
||||
const _local_41 = _arg_8.get(_local_12);
|
||||
|
||||
let _local_42 = 0;
|
||||
let _local_43 = 0;
|
||||
|
||||
while(_local_43 < _local_41.length)
|
||||
{
|
||||
_local_42 = (_local_42 + _local_41.charCodeAt(_local_43));
|
||||
_local_43++;
|
||||
}
|
||||
|
||||
if(_local_15)
|
||||
{
|
||||
const _local_19 = _local_15.getPart(_local_12);
|
||||
|
||||
if(_local_19)
|
||||
{
|
||||
_local_20 = _local_19.frames;
|
||||
}
|
||||
else
|
||||
{
|
||||
_local_20 = _local_14;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_local_20 = _local_14;
|
||||
}
|
||||
|
||||
const _local_18 = new AvatarImagePartContainer(k, _local_12, _local_41, _local_39, _local_20, _arg_3.definition, (!(_local_39 == null)), -1, _local_12, false, 1);
|
||||
|
||||
_local_22.push(_local_18);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(_local_9.indexOf(_local_12) > -1)
|
||||
{
|
||||
const _local_44 = this._geometry.getBodyPartOfItem(_arg_4, _local_12, _arg_7);
|
||||
|
||||
if(k !== _local_44.id)
|
||||
{
|
||||
//
|
||||
}
|
||||
else
|
||||
{
|
||||
const _local_13 = this._partSetsData.getPartDefinition(_local_12);
|
||||
|
||||
let _local_45 = false;
|
||||
let _local_46 = 1;
|
||||
|
||||
if(_local_13.appendToFigure)
|
||||
{
|
||||
let _local_47 = '1';
|
||||
|
||||
if(_arg_3.actionParameter !== '')
|
||||
{
|
||||
_local_47 = _arg_3.actionParameter;
|
||||
}
|
||||
|
||||
if(_local_13.hasStaticId())
|
||||
{
|
||||
_local_47 = _local_13.staticId.toString();
|
||||
}
|
||||
|
||||
if(_local_10 != null)
|
||||
{
|
||||
const _local_48 = _local_10.getAddData(_local_12);
|
||||
|
||||
if(_local_48)
|
||||
{
|
||||
_local_45 = _local_48.isBlended;
|
||||
_local_46 = _local_48.blend;
|
||||
}
|
||||
}
|
||||
|
||||
if(_local_15)
|
||||
{
|
||||
const _local_19 = _local_15.getPart(_local_12);
|
||||
|
||||
if(_local_19)
|
||||
{
|
||||
_local_20 = _local_19.frames;
|
||||
}
|
||||
else
|
||||
{
|
||||
_local_20 = _local_14;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_local_20 = _local_14;
|
||||
}
|
||||
|
||||
const _local_18 = new AvatarImagePartContainer(k, _local_12, _local_47, null, _local_20, _arg_3.definition, false, -1, _local_12, _local_45, _local_46);
|
||||
|
||||
_local_22.push(_local_18);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return _local_22;
|
||||
}
|
||||
|
||||
private getPopulatedArray(k: number): number[]
|
||||
{
|
||||
const _local_2: number[] = [];
|
||||
|
||||
let index = 0;
|
||||
|
||||
while(index < k)
|
||||
{
|
||||
_local_2.push(index);
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
return _local_2;
|
||||
}
|
||||
|
||||
public getItemIds(): string[]
|
||||
{
|
||||
if(this._actionManager)
|
||||
{
|
||||
const k = this._actionManager.getActionDefinition('CarryItem').params;
|
||||
|
||||
const _local_2 = [];
|
||||
|
||||
for(const _local_3 of k.values()) _local_2.push(_local_3);
|
||||
|
||||
return _local_2;
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
public get renderManager(): IAvatarRenderManager
|
||||
{
|
||||
return this._renderManager;
|
||||
}
|
||||
|
||||
public get figureData(): IStructureData
|
||||
{
|
||||
return this._figureData;
|
||||
}
|
||||
|
||||
public get partData(): PartSetsData
|
||||
{
|
||||
return this._partSetsData;
|
||||
}
|
||||
|
||||
public get animationManager(): AnimationManager
|
||||
{
|
||||
return this._animationManager;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
import { IAssetAnimation, IAssetManager, IEffectAssetDownloadLibrary } from '@nitrots/api';
|
||||
import { AvatarRenderEffectLibraryEvent, GetEventDispatcher, NitroEventType } from '@nitrots/events';
|
||||
|
||||
export class EffectAssetDownloadLibrary implements IEffectAssetDownloadLibrary
|
||||
{
|
||||
public static DOWNLOAD_COMPLETE: string = 'EADL_DOWNLOAD_COMPLETE';
|
||||
|
||||
private static NOT_LOADED: number = 0;
|
||||
private static LOADING: number = 1;
|
||||
private static LOADED: number = 2;
|
||||
|
||||
private _state: number = EffectAssetDownloadLibrary.NOT_LOADED;
|
||||
private _libraryName: string;
|
||||
private _revision: string;
|
||||
private _downloadUrl: string;
|
||||
private _assetManager: IAssetManager;
|
||||
private _animation: { [index: string]: IAssetAnimation } = null;
|
||||
|
||||
constructor(libraryName: string, revision: string, downloadUrl: string, assetManager: IAssetManager)
|
||||
{
|
||||
this._libraryName = libraryName;
|
||||
this._revision = revision;
|
||||
this._downloadUrl = downloadUrl;
|
||||
this._assetManager = assetManager;
|
||||
|
||||
this._downloadUrl = this._downloadUrl.replace(/%libname%/gi, this._libraryName);
|
||||
this._downloadUrl = this._downloadUrl.replace(/%revision%/gi, this._revision);
|
||||
|
||||
this.checkIsLoaded();
|
||||
}
|
||||
|
||||
public async downloadAsset(): Promise<void>
|
||||
{
|
||||
if(!this._assetManager || (this._state === EffectAssetDownloadLibrary.LOADING) || (this._state === EffectAssetDownloadLibrary.LOADED)) return;
|
||||
|
||||
if(!this.checkIsLoaded())
|
||||
{
|
||||
this._state = EffectAssetDownloadLibrary.LOADING;
|
||||
|
||||
const status = await this._assetManager.downloadAsset(this._downloadUrl);
|
||||
|
||||
if(!status) throw new Error('Could not download asset');
|
||||
}
|
||||
|
||||
if(this.checkIsLoaded()) GetEventDispatcher().dispatchEvent(new AvatarRenderEffectLibraryEvent(NitroEventType.AVATAR_EFFECT_DOWNLOADED, this));
|
||||
}
|
||||
|
||||
private checkIsLoaded(): boolean
|
||||
{
|
||||
const asset = this._assetManager.getCollection(this._libraryName);
|
||||
|
||||
if(!asset) return false;
|
||||
|
||||
this._state = EffectAssetDownloadLibrary.LOADED;
|
||||
|
||||
this._animation = asset.data.animations;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public get libraryName(): string
|
||||
{
|
||||
return this._libraryName;
|
||||
}
|
||||
|
||||
public get animation(): { [index: string]: IAssetAnimation }
|
||||
{
|
||||
return this._animation;
|
||||
}
|
||||
|
||||
public get isLoaded(): boolean
|
||||
{
|
||||
return (this._state === EffectAssetDownloadLibrary.LOADED);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,206 @@
|
||||
import { IAssetManager, IAvatarEffectListener } from '@nitrots/api';
|
||||
import { GetConfiguration } from '@nitrots/configuration';
|
||||
import { AvatarRenderEffectLibraryEvent, GetEventDispatcher, NitroEvent, NitroEventType } from '@nitrots/events';
|
||||
import { AvatarStructure } from './AvatarStructure';
|
||||
import { EffectAssetDownloadLibrary } from './EffectAssetDownloadLibrary';
|
||||
|
||||
export class EffectAssetDownloadManager
|
||||
{
|
||||
private _assets: IAssetManager;
|
||||
private _structure: AvatarStructure;
|
||||
|
||||
private _missingMandatoryLibs: string[] = [];
|
||||
private _effectMap: Map<string, EffectAssetDownloadLibrary[]> = new Map();
|
||||
private _effectListeners: Map<string, IAvatarEffectListener[]> = new Map();
|
||||
private _incompleteEffects: Map<string, EffectAssetDownloadLibrary[]> = new Map();
|
||||
private _currentDownloads: EffectAssetDownloadLibrary[] = [];
|
||||
private _libraryNames: string[] = [];
|
||||
|
||||
constructor(assets: IAssetManager, structure: AvatarStructure)
|
||||
{
|
||||
this._assets = assets;
|
||||
this._structure = structure;
|
||||
}
|
||||
|
||||
public async init(): Promise<void>
|
||||
{
|
||||
this._missingMandatoryLibs = GetConfiguration().getValue<string[]>('avatar.mandatory.effect.libraries');
|
||||
|
||||
const url = GetConfiguration().getValue<string>('avatar.effectmap.url');
|
||||
|
||||
if(!url || !url.length) throw new Error('Invalid effect map url');
|
||||
|
||||
const response = await fetch(url);
|
||||
|
||||
if(response.status !== 200) throw new Error('Invalid effect map file');
|
||||
|
||||
const responseData = await response.json();
|
||||
|
||||
this.processEffectMap(responseData.effects);
|
||||
|
||||
GetEventDispatcher().addEventListener(NitroEventType.AVATAR_EFFECT_DOWNLOADED, (event: AvatarRenderEffectLibraryEvent) => this.onLibraryLoaded(event));
|
||||
|
||||
await this.processMissingLibraries();
|
||||
}
|
||||
|
||||
private processEffectMap(data: any): void
|
||||
{
|
||||
if(!data) return;
|
||||
|
||||
const downloadUrl = GetConfiguration().getValue<string>('avatar.asset.effect.url');
|
||||
|
||||
for(const effect of data)
|
||||
{
|
||||
if(!effect) continue;
|
||||
|
||||
const id = (effect.id as string);
|
||||
const libraryName = (effect.lib as string);
|
||||
const revision = (effect.revision || '');
|
||||
|
||||
if(this._libraryNames.indexOf(libraryName) >= 0) continue;
|
||||
|
||||
this._libraryNames.push(libraryName);
|
||||
|
||||
const downloadLibrary = new EffectAssetDownloadLibrary(libraryName, revision, downloadUrl, this._assets);
|
||||
|
||||
let existing = this._effectMap.get(id);
|
||||
|
||||
if(!existing) existing = [];
|
||||
|
||||
existing.push(downloadLibrary);
|
||||
|
||||
this._effectMap.set(id, existing);
|
||||
}
|
||||
}
|
||||
|
||||
private async processMissingLibraries(): Promise<void>
|
||||
{
|
||||
const promises: Promise<void>[] = [];
|
||||
|
||||
this._missingMandatoryLibs.forEach(value =>
|
||||
{
|
||||
const libraries = this._effectMap.get(value);
|
||||
|
||||
if(libraries) for(const library of libraries) promises.push(library.downloadAsset());
|
||||
});
|
||||
|
||||
this._missingMandatoryLibs = [];
|
||||
|
||||
await Promise.all(promises);
|
||||
}
|
||||
|
||||
private onLibraryLoaded(event: AvatarRenderEffectLibraryEvent): void
|
||||
{
|
||||
if(!event || !event.library) return;
|
||||
|
||||
const loadedEffects: string[] = [];
|
||||
|
||||
this._structure.registerAnimation(event.library.animation);
|
||||
|
||||
for(const [id, libraries] of this._incompleteEffects.entries())
|
||||
{
|
||||
let isReady = true;
|
||||
|
||||
for(const library of libraries)
|
||||
{
|
||||
if(!library || library.isLoaded) continue;
|
||||
|
||||
isReady = false;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if(isReady)
|
||||
{
|
||||
loadedEffects.push(id);
|
||||
|
||||
const listeners = this._effectListeners.get(id);
|
||||
|
||||
for(const listener of listeners)
|
||||
{
|
||||
if(!listener || listener.disposed) continue;
|
||||
|
||||
listener.resetEffect(parseInt(id));
|
||||
}
|
||||
|
||||
this._effectListeners.delete(id);
|
||||
|
||||
GetEventDispatcher().dispatchEvent(new NitroEvent(NitroEventType.AVATAR_EFFECT_LOADED));
|
||||
}
|
||||
}
|
||||
|
||||
for(const id of loadedEffects) this._incompleteEffects.delete(id);
|
||||
|
||||
let index = 0;
|
||||
|
||||
while(index < this._currentDownloads.length)
|
||||
{
|
||||
const download = this._currentDownloads[index];
|
||||
|
||||
if(download)
|
||||
{
|
||||
if(download.libraryName === event.library.libraryName) this._currentDownloads.splice(index, 1);
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
public isAvatarEffectReady(effect: number): boolean
|
||||
{
|
||||
return !this.getAvatarEffectPendingLibraries(effect).length;
|
||||
}
|
||||
|
||||
private getAvatarEffectPendingLibraries(id: number): EffectAssetDownloadLibrary[]
|
||||
{
|
||||
const pendingLibraries: EffectAssetDownloadLibrary[] = [];
|
||||
|
||||
if(!this._structure) return pendingLibraries;
|
||||
|
||||
const libraries = this._effectMap.get(id.toString());
|
||||
|
||||
if(libraries)
|
||||
{
|
||||
for(const library of libraries)
|
||||
{
|
||||
if(!library || library.isLoaded) continue;
|
||||
|
||||
if(pendingLibraries.indexOf(library) === -1) pendingLibraries.push(library);
|
||||
}
|
||||
}
|
||||
|
||||
return pendingLibraries;
|
||||
}
|
||||
|
||||
public downloadAvatarEffect(id: number, listener: IAvatarEffectListener): void
|
||||
{
|
||||
const pendingLibraries = this.getAvatarEffectPendingLibraries(id);
|
||||
|
||||
if(pendingLibraries && pendingLibraries.length)
|
||||
{
|
||||
if(listener && !listener.disposed)
|
||||
{
|
||||
let listeners = this._effectListeners.get(id.toString());
|
||||
|
||||
if(!listeners) listeners = [];
|
||||
|
||||
listeners.push(listener);
|
||||
|
||||
this._effectListeners.set(id.toString(), listeners);
|
||||
}
|
||||
|
||||
this._incompleteEffects.set(id.toString(), pendingLibraries);
|
||||
|
||||
for(const library of pendingLibraries)
|
||||
{
|
||||
if(!library) continue;
|
||||
|
||||
library.downloadAsset();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(listener && !listener.disposed) listener.resetEffect(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,241 @@
|
||||
export class FigureDataContainer
|
||||
{
|
||||
private static MALE: string = 'M';
|
||||
private static FEMALE: string = 'F';
|
||||
private static UNISEX: string = 'U';
|
||||
private static SCALE: string = 'h';
|
||||
private static STD: string = 'std';
|
||||
private static DEFAULT_FRAME: string = '0';
|
||||
private static HD: string = 'hd';
|
||||
private static HAIR: string = 'hr';
|
||||
private static HAT: string = 'ha';
|
||||
private static HEAD_ACCESSORIES: string = 'he';
|
||||
private static EYE_ACCESSORIES: string = 'ea';
|
||||
private static FACE_ACCESSORIES: string = 'fa';
|
||||
private static JACKET: string = 'cc';
|
||||
private static SHIRT: string = 'ch';
|
||||
private static CHEST_ACCESSORIES: string = 'ca';
|
||||
private static CHEST_PRINTS: string = 'cp';
|
||||
private static TROUSERS: string = 'lg';
|
||||
private static SHOES: string = 'sh';
|
||||
private static TROUSER_ACCESSORIES: string = 'wa';
|
||||
private static BLOCKED_FX_TYPES: number[] = [28, 29, 30, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 68];
|
||||
|
||||
private _data: Map<string, number>;
|
||||
private _colors: Map<string, number[]>;
|
||||
private _gender: string = 'M';
|
||||
private _isDisposed: boolean;
|
||||
private _avatarEffectType: number = -1;
|
||||
|
||||
public loadAvatarData(figure: string, gender: string): void
|
||||
{
|
||||
this._data = new Map();
|
||||
this._colors = new Map();
|
||||
this._gender = gender;
|
||||
|
||||
this.parseFigureString(figure);
|
||||
}
|
||||
|
||||
public dispose(): void
|
||||
{
|
||||
this._data = null;
|
||||
this._colors = null;
|
||||
this._isDisposed = true;
|
||||
}
|
||||
|
||||
public get disposed(): boolean
|
||||
{
|
||||
return this._isDisposed;
|
||||
}
|
||||
|
||||
private parseFigureString(k: string): void
|
||||
{
|
||||
if(!k) return;
|
||||
|
||||
for(const set of k.split('.'))
|
||||
{
|
||||
const _local_3 = set.split('-');
|
||||
|
||||
if(_local_3.length > 0)
|
||||
{
|
||||
const part = _local_3[0];
|
||||
const setId = parseInt(_local_3[1]);
|
||||
const colors: number[] = [];
|
||||
|
||||
let i = 2;
|
||||
|
||||
while(i < _local_3.length)
|
||||
{
|
||||
colors.push(parseInt(_local_3[i]));
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
if(!colors.length) colors.push(0);
|
||||
|
||||
this.savePartSetId(part, setId, false);
|
||||
this.savePartSetColourId(part, colors, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public hasSetType(k: string): boolean
|
||||
{
|
||||
return !!this._data.get(k);
|
||||
}
|
||||
|
||||
public getPartSetId(k: string): number
|
||||
{
|
||||
if(this.hasSetType(k)) return this._data.get(k);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
public getColourIds(k: string): number[]
|
||||
{
|
||||
if(this._colors.get(k)) return this._colors.get(k);
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
public getFigureString(): string
|
||||
{
|
||||
let figure = '';
|
||||
|
||||
const sets: string[] = [];
|
||||
|
||||
for(const [key, value] of this._data.entries())
|
||||
{
|
||||
let set = ((key + '-') + value);
|
||||
|
||||
const colors = this._colors.get(key);
|
||||
|
||||
if(colors) for(const color of colors) set = (set + ('-' + color));
|
||||
|
||||
sets.push(set);
|
||||
}
|
||||
|
||||
let i = 0;
|
||||
|
||||
while(i < sets.length)
|
||||
{
|
||||
figure = (figure + sets[i]);
|
||||
|
||||
if(i < (sets.length - 1)) figure = (figure + '.');
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
return figure;
|
||||
}
|
||||
|
||||
public savePartData(k: string, _arg_2: number, _arg_3: number[], _arg_4: boolean = false): void
|
||||
{
|
||||
this.savePartSetId(k, _arg_2, _arg_4);
|
||||
this.savePartSetColourId(k, _arg_3, _arg_4);
|
||||
}
|
||||
|
||||
private savePartSetId(k: string, _arg_2: number, _arg_3: boolean = true): void
|
||||
{
|
||||
switch(k)
|
||||
{
|
||||
case FigureDataContainer.HD:
|
||||
case FigureDataContainer.HAIR:
|
||||
case FigureDataContainer.HAT:
|
||||
case FigureDataContainer.HEAD_ACCESSORIES:
|
||||
case FigureDataContainer.EYE_ACCESSORIES:
|
||||
case FigureDataContainer.FACE_ACCESSORIES:
|
||||
case FigureDataContainer.SHIRT:
|
||||
case FigureDataContainer.JACKET:
|
||||
case FigureDataContainer.CHEST_ACCESSORIES:
|
||||
case FigureDataContainer.CHEST_PRINTS:
|
||||
case FigureDataContainer.TROUSERS:
|
||||
case FigureDataContainer.SHOES:
|
||||
case FigureDataContainer.TROUSER_ACCESSORIES:
|
||||
if(_arg_2 >= 0)
|
||||
{
|
||||
this._data.set(k, _arg_2);
|
||||
}
|
||||
else
|
||||
{
|
||||
this._data.delete(k);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public savePartSetColourId(k: string, _arg_2: number[], _arg_3: boolean = true): void
|
||||
{
|
||||
switch(k)
|
||||
{
|
||||
case FigureDataContainer.HD:
|
||||
case FigureDataContainer.HAIR:
|
||||
case FigureDataContainer.HAT:
|
||||
case FigureDataContainer.HEAD_ACCESSORIES:
|
||||
case FigureDataContainer.EYE_ACCESSORIES:
|
||||
case FigureDataContainer.FACE_ACCESSORIES:
|
||||
case FigureDataContainer.SHIRT:
|
||||
case FigureDataContainer.JACKET:
|
||||
case FigureDataContainer.CHEST_ACCESSORIES:
|
||||
case FigureDataContainer.CHEST_PRINTS:
|
||||
case FigureDataContainer.TROUSERS:
|
||||
case FigureDataContainer.SHOES:
|
||||
case FigureDataContainer.TROUSER_ACCESSORIES:
|
||||
this._colors.set(k, _arg_2);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
public getFigureStringWithFace(k: number): string
|
||||
{
|
||||
const partSets: string[] = [FigureDataContainer.HD];
|
||||
|
||||
let figure = '';
|
||||
const sets: string[] = [];
|
||||
|
||||
for(const part of partSets)
|
||||
{
|
||||
const colors = this._colors.get(part);
|
||||
|
||||
if(colors)
|
||||
{
|
||||
let setId = this._data.get(part);
|
||||
|
||||
if(part === FigureDataContainer.HD) setId = k;
|
||||
|
||||
let set = ((part + '-') + setId);
|
||||
|
||||
if(setId >= 0)
|
||||
{
|
||||
let i = 0;
|
||||
|
||||
while(i < colors.length)
|
||||
{
|
||||
set = (set + ('-' + colors[i]));
|
||||
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
sets.push(set);
|
||||
}
|
||||
}
|
||||
|
||||
let i = 0;
|
||||
|
||||
while(i < sets.length)
|
||||
{
|
||||
figure = (figure + sets[i]);
|
||||
|
||||
if(i < (sets.length - 1)) figure = (figure + '.');
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
return figure;
|
||||
}
|
||||
|
||||
public get gender(): string
|
||||
{
|
||||
return this._gender;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
import { AvatarRenderManager } from './AvatarRenderManager';
|
||||
|
||||
const avatarRenderManager = new AvatarRenderManager();
|
||||
|
||||
export const GetAvatarRenderManager = () => avatarRenderManager;
|
||||
@@ -0,0 +1,18 @@
|
||||
import { AssetAliasCollection } from './alias';
|
||||
import { AvatarFigureContainer } from './AvatarFigureContainer';
|
||||
import { AvatarImage } from './AvatarImage';
|
||||
import { AvatarStructure } from './AvatarStructure';
|
||||
import { EffectAssetDownloadManager } from './EffectAssetDownloadManager';
|
||||
|
||||
export class PlaceHolderAvatarImage extends AvatarImage
|
||||
{
|
||||
constructor(k: AvatarStructure, _arg_2: AssetAliasCollection, _arg_3: AvatarFigureContainer, _arg_4: string, _arg_5: EffectAssetDownloadManager)
|
||||
{
|
||||
super(k, _arg_2, _arg_3, _arg_4, _arg_5, null);
|
||||
}
|
||||
|
||||
public isPlaceholder(): boolean
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,220 @@
|
||||
import { IActionDefinition } from '@nitrots/api';
|
||||
import { ActionType } from './ActionType';
|
||||
|
||||
export class ActionDefinition implements IActionDefinition
|
||||
{
|
||||
private _id: string;
|
||||
private _state: string;
|
||||
private _precedence: number;
|
||||
private _activePartSet: string;
|
||||
private _assetPartDefinition: string;
|
||||
private _lay: string;
|
||||
private _geometryType: string;
|
||||
private _isMain: boolean;
|
||||
private _isDefault: boolean;
|
||||
private _isAnimation: boolean;
|
||||
private _startFromFrameZero: boolean;
|
||||
private _prevents: string[];
|
||||
private _preventHeadTurn: boolean;
|
||||
private _types: Map<number, ActionType> = new Map();
|
||||
private _params: Map<string, string> = new Map();
|
||||
private _defaultParameterValue: string = '';
|
||||
private _canvasOffsets: Map<string, Map<number, number[]>> = new Map();
|
||||
|
||||
constructor(data: any)
|
||||
{
|
||||
this._id = data.id;
|
||||
this._state = data.state;
|
||||
this._precedence = data.precedence;
|
||||
this._activePartSet = data.activePartSet;
|
||||
this._assetPartDefinition = data.assetPartDefinition;
|
||||
this._lay = data.lay;
|
||||
this._geometryType = data.geometryType;
|
||||
this._isMain = data.main || false;
|
||||
this._isDefault = data.isDefault || false;
|
||||
this._isAnimation = data.animation || false;
|
||||
this._startFromFrameZero = data.startFromFrameZero || false;
|
||||
this._prevents = data.prevents || [];
|
||||
this._preventHeadTurn = data.preventHeadTurn || false;
|
||||
|
||||
if(data.params && (data.params.length > 0))
|
||||
{
|
||||
for(const param of data.params)
|
||||
{
|
||||
if(!param) continue;
|
||||
|
||||
if(param.id === 'default') this._defaultParameterValue = param.value;
|
||||
else this._params.set(param.id, param.value);
|
||||
}
|
||||
}
|
||||
|
||||
if(data.types && (data.types.length > 0))
|
||||
{
|
||||
for(const type of data.types)
|
||||
{
|
||||
if(!type) continue;
|
||||
|
||||
const action = new ActionType(type);
|
||||
|
||||
this._types.set(action.id, action);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public setOffsets(k: string, _arg_2: number, _arg_3: number[]): void
|
||||
{
|
||||
if(!this._canvasOffsets) this._canvasOffsets = new Map();
|
||||
|
||||
let existing = this._canvasOffsets.get(k);
|
||||
|
||||
if(!existing)
|
||||
{
|
||||
existing = new Map();
|
||||
|
||||
this._canvasOffsets.set(k, existing);
|
||||
}
|
||||
|
||||
existing.set(_arg_2, _arg_3);
|
||||
}
|
||||
|
||||
public getOffsets(k: string, _arg_2: number): number[]
|
||||
{
|
||||
if(!this._canvasOffsets) return null;
|
||||
|
||||
const existing = this._canvasOffsets.get(k);
|
||||
|
||||
if(!existing) return null;
|
||||
|
||||
return existing.get(_arg_2);
|
||||
}
|
||||
|
||||
public getType(id: string): ActionType
|
||||
{
|
||||
if(!id) return null;
|
||||
|
||||
const existing = this._types.get(parseInt(id));
|
||||
|
||||
if(!existing) return null;
|
||||
|
||||
return existing;
|
||||
}
|
||||
|
||||
public getParameterValue(id: string): string
|
||||
{
|
||||
if(!id) return '';
|
||||
|
||||
const existing = this._params.get(id);
|
||||
|
||||
if(!existing) return this._defaultParameterValue;
|
||||
|
||||
return existing;
|
||||
}
|
||||
|
||||
public getPrevents(type: string): string[]
|
||||
{
|
||||
return this._prevents.concat(this.getTypePrevents(type));
|
||||
}
|
||||
|
||||
private getTypePrevents(type: string): string[]
|
||||
{
|
||||
if(!type) return [];
|
||||
|
||||
const existing = this._types.get(parseInt(type));
|
||||
|
||||
if(!existing) return [];
|
||||
|
||||
return existing.prevents;
|
||||
}
|
||||
|
||||
public getPreventHeadTurn(k: string): boolean
|
||||
{
|
||||
if(!k) return this._preventHeadTurn;
|
||||
|
||||
const type = this.getType(k);
|
||||
|
||||
if(!type) return this._preventHeadTurn;
|
||||
|
||||
return type.preventHeadTurn;
|
||||
}
|
||||
|
||||
public isAnimated(k: string): boolean
|
||||
{
|
||||
if(!k) return true;
|
||||
|
||||
const type = this.getType(k);
|
||||
|
||||
if(!type) return true;
|
||||
|
||||
return type.isAnimated;
|
||||
}
|
||||
|
||||
public get id(): string
|
||||
{
|
||||
return this._id;
|
||||
}
|
||||
|
||||
public get state(): string
|
||||
{
|
||||
return this._state;
|
||||
}
|
||||
|
||||
public get precedence(): number
|
||||
{
|
||||
return this._precedence;
|
||||
}
|
||||
|
||||
public get activePartSet(): string
|
||||
{
|
||||
return this._activePartSet;
|
||||
}
|
||||
|
||||
public get assetPartDefinition(): string
|
||||
{
|
||||
return this._assetPartDefinition;
|
||||
}
|
||||
|
||||
public get lay(): string
|
||||
{
|
||||
return this._lay;
|
||||
}
|
||||
|
||||
public get geometryType(): string
|
||||
{
|
||||
return this._geometryType;
|
||||
}
|
||||
|
||||
public get isMain(): boolean
|
||||
{
|
||||
return this._isMain;
|
||||
}
|
||||
|
||||
public get isDefault(): boolean
|
||||
{
|
||||
return this._isDefault;
|
||||
}
|
||||
|
||||
public get isAnimation(): boolean
|
||||
{
|
||||
return this._isAnimation;
|
||||
}
|
||||
|
||||
public get startFromFrameZero(): boolean
|
||||
{
|
||||
return this._startFromFrameZero;
|
||||
}
|
||||
|
||||
public get prevents(): string[]
|
||||
{
|
||||
return this._prevents;
|
||||
}
|
||||
|
||||
public get preventHeadTurn(): boolean
|
||||
{
|
||||
return this._preventHeadTurn;
|
||||
}
|
||||
|
||||
public get params(): Map<string, string>
|
||||
{
|
||||
return this._params;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
export class ActionType
|
||||
{
|
||||
private _id: number;
|
||||
private _value: number;
|
||||
private _prevents: string[];
|
||||
private _preventHeadTurn: boolean;
|
||||
private _isAnimated: boolean;
|
||||
|
||||
constructor(data: any)
|
||||
{
|
||||
this._id = parseInt(data.id);
|
||||
this._value = parseInt(data.id);
|
||||
this._prevents = data.prevents || [];
|
||||
this._preventHeadTurn = data.preventHeadTurn || false;
|
||||
this._isAnimated = true;
|
||||
|
||||
if((data.animated !== undefined) && (data.animated === false)) this._isAnimated = false;
|
||||
}
|
||||
|
||||
public get id(): number
|
||||
{
|
||||
return this._id;
|
||||
}
|
||||
|
||||
public get value(): number
|
||||
{
|
||||
return this._value;
|
||||
}
|
||||
|
||||
public get prevents(): string[]
|
||||
{
|
||||
return this._prevents;
|
||||
}
|
||||
|
||||
public get preventHeadTurn(): boolean
|
||||
{
|
||||
return this._preventHeadTurn;
|
||||
}
|
||||
|
||||
public get isAnimated(): boolean
|
||||
{
|
||||
return this._isAnimated;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
import { IActionDefinition, IActiveActionData } from '@nitrots/api';
|
||||
|
||||
export class ActiveActionData implements IActiveActionData
|
||||
{
|
||||
private _actionType: string;
|
||||
private _actionParameter: string;
|
||||
private _definition: IActionDefinition;
|
||||
private _startFrame: number;
|
||||
private _overridingAction: string;
|
||||
|
||||
constructor(action: string, parameter: string = '', startFrame: number = 0)
|
||||
{
|
||||
this._actionType = action || '';
|
||||
this._actionParameter = parameter || '';
|
||||
this._definition = null;
|
||||
this._startFrame = startFrame || 0;
|
||||
this._overridingAction = null;
|
||||
}
|
||||
|
||||
public dispose(): void
|
||||
{
|
||||
this._actionType = null;
|
||||
this._actionParameter = null;
|
||||
this._definition = null;
|
||||
}
|
||||
|
||||
public get id(): string
|
||||
{
|
||||
if(!this._definition) return '';
|
||||
|
||||
return this._definition.id + '_' + this._actionParameter;
|
||||
}
|
||||
|
||||
public get actionType(): string
|
||||
{
|
||||
return this._actionType;
|
||||
}
|
||||
|
||||
public get actionParameter(): string
|
||||
{
|
||||
return this._actionParameter;
|
||||
}
|
||||
|
||||
public set actionParameter(parameter: string)
|
||||
{
|
||||
this._actionParameter = parameter;
|
||||
}
|
||||
|
||||
public get definition(): IActionDefinition
|
||||
{
|
||||
return this._definition;
|
||||
}
|
||||
|
||||
public set definition(definition: IActionDefinition)
|
||||
{
|
||||
this._definition = definition;
|
||||
}
|
||||
|
||||
public get startFrame(): number
|
||||
{
|
||||
return this._startFrame;
|
||||
}
|
||||
|
||||
public get overridingAction(): string
|
||||
{
|
||||
return this._overridingAction;
|
||||
}
|
||||
|
||||
public set overridingAction(action: string)
|
||||
{
|
||||
this._overridingAction = action;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,186 @@
|
||||
import { IActiveActionData, IAssetManager } from '@nitrots/api';
|
||||
import { ActionDefinition } from './ActionDefinition';
|
||||
|
||||
export class AvatarActionManager
|
||||
{
|
||||
private _assets: IAssetManager;
|
||||
private _actions: Map<string, ActionDefinition>;
|
||||
private _defaultAction: ActionDefinition;
|
||||
|
||||
constructor(k: IAssetManager, data: any)
|
||||
{
|
||||
this._assets = k;
|
||||
this._actions = new Map();
|
||||
this._defaultAction = null;
|
||||
|
||||
this.updateActions(data);
|
||||
}
|
||||
|
||||
public updateActions(data: any): void
|
||||
{
|
||||
if(!data) return;
|
||||
|
||||
for(const action of data.actions)
|
||||
{
|
||||
if(!action || !action.state) continue;
|
||||
|
||||
const definition = new ActionDefinition(action);
|
||||
|
||||
this._actions.set(definition.state, definition);
|
||||
}
|
||||
|
||||
if(data.actionOffsets) this.parseActionOffsets(data.actionOffsets);
|
||||
}
|
||||
|
||||
private parseActionOffsets(offsets: any): void
|
||||
{
|
||||
if(!offsets || !offsets.length) return;
|
||||
|
||||
for(const offset of offsets)
|
||||
{
|
||||
const action = this._actions.get(offset.action);
|
||||
|
||||
if(!action) continue;
|
||||
|
||||
for(const canvasOffset of offset.offsets)
|
||||
{
|
||||
const size = (canvasOffset.size || '');
|
||||
const direction = canvasOffset.direction;
|
||||
|
||||
if((size === '') || (direction === undefined)) continue;
|
||||
|
||||
const x = (canvasOffset.x || 0);
|
||||
const y = (canvasOffset.y || 0);
|
||||
const z = (canvasOffset.z || 0);
|
||||
|
||||
action.setOffsets(size, direction, [x, y, z]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public getActionDefinition(id: string): ActionDefinition
|
||||
{
|
||||
if(!id) return null;
|
||||
|
||||
for(const action of this._actions.values())
|
||||
{
|
||||
if(!action || (action.id !== id)) continue;
|
||||
|
||||
return action;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public getActionDefinitionWithState(state: string): ActionDefinition
|
||||
{
|
||||
const existing = this._actions.get(state);
|
||||
|
||||
if(!existing) return null;
|
||||
|
||||
return existing;
|
||||
}
|
||||
|
||||
public getDefaultAction(): ActionDefinition
|
||||
{
|
||||
if(this._defaultAction) return this._defaultAction;
|
||||
|
||||
for(const action of this._actions.values())
|
||||
{
|
||||
if(!action || !action.isDefault) continue;
|
||||
|
||||
this._defaultAction = action;
|
||||
|
||||
return action;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public getCanvasOffsets(k: IActiveActionData[], _arg_2: string, _arg_3: number): number[]
|
||||
{
|
||||
let canvasOffsets: number[] = [];
|
||||
|
||||
for(const activeAction of k)
|
||||
{
|
||||
if(!activeAction) continue;
|
||||
|
||||
const action = this._actions.get(activeAction.actionType);
|
||||
const offsets = action && action.getOffsets(_arg_2, _arg_3);
|
||||
|
||||
if(offsets) canvasOffsets = offsets;
|
||||
}
|
||||
|
||||
return canvasOffsets;
|
||||
}
|
||||
|
||||
public sortActions(actions: IActiveActionData[]): IActiveActionData[]
|
||||
{
|
||||
if(!actions) return null;
|
||||
|
||||
actions = this.filterActions(actions);
|
||||
|
||||
const validatedActions: IActiveActionData[] = [];
|
||||
|
||||
for(const action of actions)
|
||||
{
|
||||
if(!action) continue;
|
||||
|
||||
const definition = this._actions.get(action.actionType);
|
||||
|
||||
if(!definition) continue;
|
||||
|
||||
action.definition = definition;
|
||||
|
||||
validatedActions.push(action);
|
||||
}
|
||||
|
||||
validatedActions.sort(this.sortByPrecedence);
|
||||
|
||||
return validatedActions;
|
||||
}
|
||||
|
||||
private filterActions(actions: IActiveActionData[]): IActiveActionData[]
|
||||
{
|
||||
let preventions: string[] = [];
|
||||
const activeActions: IActiveActionData[] = [];
|
||||
|
||||
for(const action of actions)
|
||||
{
|
||||
if(!action) continue;
|
||||
|
||||
const localAction = this._actions.get(action.actionType);
|
||||
|
||||
if(localAction) preventions = preventions.concat(localAction.getPrevents(action.actionParameter));
|
||||
}
|
||||
|
||||
for(const action of actions)
|
||||
{
|
||||
if(!action) continue;
|
||||
|
||||
let actionType = action.actionType;
|
||||
|
||||
if(action.actionType === 'fx') actionType = (actionType + ('.' + action.actionParameter));
|
||||
|
||||
if(preventions.indexOf(actionType) >= 0) continue;
|
||||
|
||||
activeActions.push(action);
|
||||
}
|
||||
|
||||
return activeActions;
|
||||
}
|
||||
|
||||
private sortByPrecedence(actionOne: IActiveActionData, actionTwo: IActiveActionData): number
|
||||
{
|
||||
if(!actionOne || !actionTwo) return 0;
|
||||
|
||||
const precedenceOne = actionOne.definition.precedence;
|
||||
const precedenceTwo = actionTwo.definition.precedence;
|
||||
|
||||
if(precedenceOne < precedenceTwo) return 1;
|
||||
|
||||
if(precedenceOne > precedenceTwo) return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
export * from './ActionDefinition';
|
||||
export * from './ActionType';
|
||||
export * from './ActiveActionData';
|
||||
export * from './AvatarActionManager';
|
||||
@@ -0,0 +1,37 @@
|
||||
import { IAssetAlias } from '@nitrots/api';
|
||||
|
||||
export class AssetAlias
|
||||
{
|
||||
private _name: string;
|
||||
private _link: string;
|
||||
private _flipH: boolean;
|
||||
private _flipV: boolean;
|
||||
|
||||
constructor(name: string, alias: IAssetAlias)
|
||||
{
|
||||
this._name = name;
|
||||
this._link = alias.link;
|
||||
this._flipH = alias.flipH;
|
||||
this._flipV = alias.flipV;
|
||||
}
|
||||
|
||||
public get name(): string
|
||||
{
|
||||
return this._name;
|
||||
}
|
||||
|
||||
public get link(): string
|
||||
{
|
||||
return this._link;
|
||||
}
|
||||
|
||||
public get flipH(): boolean
|
||||
{
|
||||
return this._flipH;
|
||||
}
|
||||
|
||||
public get flipV(): boolean
|
||||
{
|
||||
return this._flipV;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
import { IAssetManager, IGraphicAsset } from '@nitrots/api';
|
||||
import { AvatarRenderManager } from '../AvatarRenderManager';
|
||||
import { AssetAlias } from './AssetAlias';
|
||||
|
||||
export class AssetAliasCollection
|
||||
{
|
||||
private _assets: IAssetManager;
|
||||
private _aliases: Map<string, AssetAlias>;
|
||||
private _avatarRenderManager: AvatarRenderManager;
|
||||
private _missingAssetNames: string[];
|
||||
|
||||
constructor(k: AvatarRenderManager, _arg_2: IAssetManager)
|
||||
{
|
||||
this._avatarRenderManager = k;
|
||||
this._aliases = new Map();
|
||||
this._assets = _arg_2;
|
||||
this._missingAssetNames = [];
|
||||
}
|
||||
|
||||
public dispose(): void
|
||||
{
|
||||
this._assets = null;
|
||||
this._aliases = null;
|
||||
}
|
||||
|
||||
public reset(): void
|
||||
{
|
||||
this.init();
|
||||
}
|
||||
|
||||
public init(): void
|
||||
{
|
||||
for(const collection of this._assets.collections.values())
|
||||
{
|
||||
if(!collection) continue;
|
||||
|
||||
const aliases = collection.data && collection.data.aliases;
|
||||
|
||||
if(!aliases) continue;
|
||||
|
||||
for(const name in aliases)
|
||||
{
|
||||
const alias = aliases[name];
|
||||
|
||||
if(!alias) continue;
|
||||
|
||||
this._aliases.set(name, new AssetAlias(name, alias));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public hasAlias(k: string): boolean
|
||||
{
|
||||
const alias = this._aliases.get(k);
|
||||
|
||||
if(alias) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public getAssetName(k: string): string
|
||||
{
|
||||
let _local_2 = k;
|
||||
let _local_3 = 5;
|
||||
|
||||
while(this.hasAlias(_local_2) && (_local_3 >= 0))
|
||||
{
|
||||
const _local_4 = this._aliases.get(_local_2);
|
||||
|
||||
_local_2 = _local_4.link;
|
||||
_local_3--;
|
||||
}
|
||||
|
||||
return _local_2;
|
||||
}
|
||||
|
||||
public getAsset(name: string): IGraphicAsset
|
||||
{
|
||||
if(!this._assets) return null;
|
||||
|
||||
name = this.getAssetName(name);
|
||||
|
||||
const asset = this._assets.getAsset(name);
|
||||
|
||||
if(!asset) return null;
|
||||
|
||||
return asset;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
export * from './AssetAlias';
|
||||
export * from './AssetAliasCollection';
|
||||
@@ -0,0 +1,61 @@
|
||||
import { IAssetAnimationAdd } from '@nitrots/api';
|
||||
|
||||
export class AddDataContainer
|
||||
{
|
||||
private _id: string;
|
||||
private _align: string;
|
||||
private _base: string;
|
||||
private _ink: number;
|
||||
private _blend: number;
|
||||
|
||||
constructor(k: IAssetAnimationAdd)
|
||||
{
|
||||
this._id = k.id || '';
|
||||
this._align = k.align || '';
|
||||
this._base = k.base || '';
|
||||
this._ink = k.ink || 0;
|
||||
this._blend = 0;
|
||||
|
||||
const _local_2 = k.blend;
|
||||
|
||||
if(_local_2)
|
||||
{
|
||||
if(_local_2.length > 0)
|
||||
{
|
||||
this._blend = parseInt(_local_2);
|
||||
|
||||
if(this._blend > 1) this._blend = (this._blend / 100);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public get id(): string
|
||||
{
|
||||
return this._id;
|
||||
}
|
||||
|
||||
public get align(): string
|
||||
{
|
||||
return this._align;
|
||||
}
|
||||
|
||||
public get base(): string
|
||||
{
|
||||
return this._base;
|
||||
}
|
||||
|
||||
public get ink(): number
|
||||
{
|
||||
return this._ink;
|
||||
}
|
||||
|
||||
public get blend(): number
|
||||
{
|
||||
return this._blend;
|
||||
}
|
||||
|
||||
public get isBlended(): boolean
|
||||
{
|
||||
return this._blend !== 1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,311 @@
|
||||
import { IAnimation, IAssetAnimation, IAssetAnimationFrame } from '@nitrots/api';
|
||||
import { AvatarStructure } from '../AvatarStructure';
|
||||
import { AddDataContainer } from './AddDataContainer';
|
||||
import { AvatarAnimationLayerData } from './AvatarAnimationLayerData';
|
||||
import { AvatarDataContainer } from './AvatarDataContainer';
|
||||
import { DirectionDataContainer } from './DirectionDataContainer';
|
||||
import { SpriteDataContainer } from './SpriteDataContainer';
|
||||
|
||||
export class Animation implements IAnimation
|
||||
{
|
||||
private static EMPTY_ARRAY: any[] = [];
|
||||
|
||||
private _id: string;
|
||||
private _description: string;
|
||||
private _frames: AvatarAnimationLayerData[][];
|
||||
private _spriteData: SpriteDataContainer[];
|
||||
private _avatarData: AvatarDataContainer;
|
||||
private _directionData: DirectionDataContainer;
|
||||
private _removeData: string[];
|
||||
private _addData: AddDataContainer[];
|
||||
private _overriddenActions: Map<string, string>;
|
||||
private _overrideFrames: Map<string, AvatarAnimationLayerData[][]>;
|
||||
private _resetOnToggle: boolean;
|
||||
|
||||
constructor(k: AvatarStructure, _arg_2: IAssetAnimation)
|
||||
{
|
||||
this._id = _arg_2.name;
|
||||
this._description = this._id;
|
||||
this._frames = [];
|
||||
this._spriteData = null;
|
||||
this._avatarData = null;
|
||||
this._directionData = null;
|
||||
this._removeData = null;
|
||||
this._addData = null;
|
||||
this._overriddenActions = null;
|
||||
this._overrideFrames = null;
|
||||
this._resetOnToggle = (_arg_2.resetOnToggle || false);
|
||||
|
||||
if(_arg_2.sprites && _arg_2.sprites.length)
|
||||
{
|
||||
this._spriteData = [];
|
||||
|
||||
for(const sprite of _arg_2.sprites) this._spriteData.push(new SpriteDataContainer(this, sprite));
|
||||
}
|
||||
|
||||
if(_arg_2.avatars && _arg_2.avatars.length) this._avatarData = new AvatarDataContainer(_arg_2.avatars[0]);
|
||||
|
||||
if(_arg_2.directions && _arg_2.directions.length) this._directionData = new DirectionDataContainer(_arg_2.directions[0]);
|
||||
|
||||
if(_arg_2.removes && _arg_2.removes.length)
|
||||
{
|
||||
this._removeData = [];
|
||||
|
||||
for(const remove of _arg_2.removes) this._removeData.push(remove.id);
|
||||
}
|
||||
|
||||
if(_arg_2.adds && _arg_2.adds.length)
|
||||
{
|
||||
this._addData = [];
|
||||
|
||||
for(const add of _arg_2.adds) this._addData.push(new AddDataContainer(add));
|
||||
}
|
||||
|
||||
if(_arg_2.overrides && _arg_2.overrides.length)
|
||||
{
|
||||
this._overrideFrames = new Map();
|
||||
this._overriddenActions = new Map();
|
||||
|
||||
for(const override of _arg_2.overrides)
|
||||
{
|
||||
const name = override.name;
|
||||
const value = override.override;
|
||||
|
||||
this._overriddenActions.set(value, name);
|
||||
|
||||
const frames: AvatarAnimationLayerData[][] = [];
|
||||
|
||||
this.parseFrames(frames, override.frames, k);
|
||||
|
||||
this._overrideFrames.set(name, frames);
|
||||
}
|
||||
}
|
||||
|
||||
this.parseFrames(this._frames, _arg_2.frames, k);
|
||||
}
|
||||
|
||||
private parseFrames(frames: AvatarAnimationLayerData[][], _arg_2: IAssetAnimationFrame[], _arg_3: AvatarStructure): void
|
||||
{
|
||||
if(!_arg_2 || !_arg_2.length) return;
|
||||
|
||||
for(const frame of _arg_2)
|
||||
{
|
||||
let repeats = 1;
|
||||
|
||||
if(frame.repeats && (frame.repeats > 1)) repeats = frame.repeats;
|
||||
|
||||
let index = 0;
|
||||
|
||||
while(index < repeats)
|
||||
{
|
||||
const layers: AvatarAnimationLayerData[] = [];
|
||||
|
||||
if(frame.bodyparts && frame.bodyparts.length)
|
||||
{
|
||||
for(const bodyPart of frame.bodyparts)
|
||||
{
|
||||
const definition = _arg_3.getActionDefinition(bodyPart.action);
|
||||
const layer = new AvatarAnimationLayerData(bodyPart, AvatarAnimationLayerData.BODYPART, definition);
|
||||
|
||||
layers.push(layer);
|
||||
}
|
||||
}
|
||||
|
||||
if(frame.fxs && frame.fxs.length)
|
||||
{
|
||||
for(const fx of frame.fxs)
|
||||
{
|
||||
const definition = _arg_3.getActionDefinition(fx.action);
|
||||
const layer = new AvatarAnimationLayerData(fx, AvatarAnimationLayerData.FX, definition);
|
||||
|
||||
layers.push(layer);
|
||||
}
|
||||
}
|
||||
|
||||
frames.push(layers);
|
||||
|
||||
index++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public frameCount(k: string = null): number
|
||||
{
|
||||
if(!k) return this._frames.length;
|
||||
|
||||
if(this._overrideFrames)
|
||||
{
|
||||
const _local_2 = this._overrideFrames.get(k);
|
||||
|
||||
if(_local_2) return _local_2.length;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public hasOverriddenActions(): boolean
|
||||
{
|
||||
if(!this._overriddenActions) return false;
|
||||
|
||||
return (this._overriddenActions.size > 0);
|
||||
}
|
||||
|
||||
public overriddenActionNames(): string[]
|
||||
{
|
||||
if(!this._overriddenActions) return null;
|
||||
|
||||
const keys: string[] = [];
|
||||
|
||||
for(const key of this._overriddenActions.keys()) keys.push(key);
|
||||
|
||||
return keys;
|
||||
}
|
||||
|
||||
public overridingAction(k: string): string
|
||||
{
|
||||
if(!this._overriddenActions) return null;
|
||||
|
||||
return this._overriddenActions.get(k);
|
||||
}
|
||||
|
||||
private getFrame(frameCount: number, _arg_2: string = null): AvatarAnimationLayerData[]
|
||||
{
|
||||
if(frameCount < 0) frameCount = 0;
|
||||
|
||||
let layers: AvatarAnimationLayerData[] = [];
|
||||
|
||||
if(!_arg_2)
|
||||
{
|
||||
if(this._frames.length > 0)
|
||||
{
|
||||
layers = this._frames[(frameCount % this._frames.length)];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const overrideLayers = this._overrideFrames.get(_arg_2);
|
||||
|
||||
if(overrideLayers && (overrideLayers.length > 0))
|
||||
{
|
||||
layers = overrideLayers[(frameCount % overrideLayers.length)];
|
||||
}
|
||||
}
|
||||
|
||||
return layers;
|
||||
}
|
||||
|
||||
public getAnimatedBodyPartIds(k: number, _arg_2: string = null): string[]
|
||||
{
|
||||
const _local_3: string[] = [];
|
||||
|
||||
for(const layer of this.getFrame(k, _arg_2))
|
||||
{
|
||||
if(layer.type === AvatarAnimationLayerData.BODYPART)
|
||||
{
|
||||
_local_3.push(layer.id);
|
||||
}
|
||||
|
||||
else if(layer.type === AvatarAnimationLayerData.FX)
|
||||
{
|
||||
if(this._addData && this._addData.length)
|
||||
{
|
||||
for(const _local_5 of this._addData)
|
||||
{
|
||||
if(_local_5.id === layer.id) _local_3.push(_local_5.align);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return _local_3;
|
||||
}
|
||||
|
||||
public getLayerData(frameCount: number, spriteId: string, _arg_3: string = null): AvatarAnimationLayerData
|
||||
{
|
||||
for(const layer of this.getFrame(frameCount, _arg_3))
|
||||
{
|
||||
if(layer.id === spriteId) return layer;
|
||||
|
||||
if(layer.type === AvatarAnimationLayerData.FX)
|
||||
{
|
||||
if(this._addData && this._addData.length)
|
||||
{
|
||||
for(const addData of this._addData)
|
||||
{
|
||||
if(((addData.align === spriteId) && (addData.id === layer.id))) return layer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public hasAvatarData(): boolean
|
||||
{
|
||||
return this._avatarData !== null;
|
||||
}
|
||||
|
||||
public hasDirectionData(): boolean
|
||||
{
|
||||
return this._directionData !== null;
|
||||
}
|
||||
|
||||
public hasAddData(): boolean
|
||||
{
|
||||
return this._addData !== null;
|
||||
}
|
||||
|
||||
public getAddData(k: string): AddDataContainer
|
||||
{
|
||||
if(this._addData)
|
||||
{
|
||||
for(const _local_2 of this._addData)
|
||||
{
|
||||
if(_local_2.id === k) return _local_2;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public get id(): string
|
||||
{
|
||||
return this._id;
|
||||
}
|
||||
|
||||
public get spriteData(): SpriteDataContainer[]
|
||||
{
|
||||
return this._spriteData || Animation.EMPTY_ARRAY;
|
||||
}
|
||||
|
||||
public get avatarData(): AvatarDataContainer
|
||||
{
|
||||
return this._avatarData;
|
||||
}
|
||||
|
||||
public get directionData(): DirectionDataContainer
|
||||
{
|
||||
return this._directionData;
|
||||
}
|
||||
|
||||
public get removeData(): string[]
|
||||
{
|
||||
return this._removeData || Animation.EMPTY_ARRAY;
|
||||
}
|
||||
|
||||
public get addData(): AddDataContainer[]
|
||||
{
|
||||
return this._addData || Animation.EMPTY_ARRAY;
|
||||
}
|
||||
|
||||
public toString(): string
|
||||
{
|
||||
return this._description;
|
||||
}
|
||||
|
||||
public get resetOnToggle(): boolean
|
||||
{
|
||||
return this._resetOnToggle;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
import { IAnimation, IAnimationLayerData, IAnimationManager, IAssetAnimation } from '@nitrots/api';
|
||||
import { AvatarStructure } from '../AvatarStructure';
|
||||
import { Animation } from './Animation';
|
||||
|
||||
export class AnimationManager implements IAnimationManager
|
||||
{
|
||||
private _animations: Map<string, Animation>;
|
||||
|
||||
constructor()
|
||||
{
|
||||
this._animations = new Map();
|
||||
}
|
||||
|
||||
public registerAnimation(structure: AvatarStructure, _arg_2: { [index: string]: IAssetAnimation }): boolean
|
||||
{
|
||||
if(!_arg_2) return false;
|
||||
|
||||
const animationData = _arg_2[Object.keys(_arg_2)[0]];
|
||||
|
||||
const animation = new Animation(structure, animationData);
|
||||
|
||||
this._animations.set(animationData.name, animation);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public getAnimation(animation: string): Animation
|
||||
{
|
||||
const existing = this._animations.get(animation);
|
||||
|
||||
if(!existing) return null;
|
||||
|
||||
return existing;
|
||||
}
|
||||
|
||||
public getLayerData(animation: string, frameCount: number, spriteId: string): IAnimationLayerData
|
||||
{
|
||||
const existing = this.getAnimation(animation);
|
||||
|
||||
if(!existing) return null;
|
||||
|
||||
return existing.getLayerData(frameCount, spriteId);
|
||||
}
|
||||
|
||||
public get animations(): Map<string, IAnimation>
|
||||
{
|
||||
return this._animations;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
import { IActionDefinition, IActiveActionData, IAnimationLayerData, IAssetAnimationFramePart } from '@nitrots/api';
|
||||
import { ActiveActionData } from '../actions';
|
||||
|
||||
export class AvatarAnimationLayerData implements IAnimationLayerData
|
||||
{
|
||||
public static BODYPART: string = 'bodypart';
|
||||
public static FX: string = 'fx';
|
||||
|
||||
private _id: string;
|
||||
private _action: IActiveActionData;
|
||||
private _animationFrame: number;
|
||||
private _dx: number;
|
||||
private _dy: number;
|
||||
private _dz: number;
|
||||
private _directionOffset: number;
|
||||
private _type: string;
|
||||
private _base: string;
|
||||
private _items: Map<string, string>;
|
||||
|
||||
constructor(k: IAssetAnimationFramePart, _arg_2: string, _arg_3: IActionDefinition)
|
||||
{
|
||||
this._id = k.id;
|
||||
this._animationFrame = (k.frame || 0);
|
||||
this._dx = (k.dx || 0);
|
||||
this._dy = (k.dy || 0);
|
||||
this._dz = (k.dz || 0);
|
||||
this._directionOffset = (k.dd || 0);
|
||||
this._type = _arg_2;
|
||||
this._base = (k.base || '');
|
||||
this._items = new Map();
|
||||
|
||||
if(k.items) for(const _local_4 of k.items) this._items.set(_local_4.id, _local_4.base);
|
||||
|
||||
let _local_5 = '';
|
||||
|
||||
if(this._base !== '') _local_5 = this.baseAsInt().toString();
|
||||
|
||||
if(_arg_3)
|
||||
{
|
||||
this._action = new ActiveActionData(_arg_3.state, this.base);
|
||||
this._action.definition = _arg_3;
|
||||
}
|
||||
}
|
||||
|
||||
public get items(): Map<string, string>
|
||||
{
|
||||
return this._items;
|
||||
}
|
||||
|
||||
private baseAsInt(): number
|
||||
{
|
||||
let k = 0;
|
||||
let index = 0;
|
||||
|
||||
while(index < this._base.length)
|
||||
{
|
||||
k = (k + this._base.charCodeAt(index));
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
return k;
|
||||
}
|
||||
|
||||
public get id(): string
|
||||
{
|
||||
return this._id;
|
||||
}
|
||||
|
||||
public get animationFrame(): number
|
||||
{
|
||||
return this._animationFrame;
|
||||
}
|
||||
|
||||
public get dx(): number
|
||||
{
|
||||
return this._dx;
|
||||
}
|
||||
|
||||
public get dy(): number
|
||||
{
|
||||
return this._dy;
|
||||
}
|
||||
|
||||
public get dz(): number
|
||||
{
|
||||
return this._dz;
|
||||
}
|
||||
|
||||
public get dd(): number
|
||||
{
|
||||
return this._directionOffset;
|
||||
}
|
||||
|
||||
public get type(): string
|
||||
{
|
||||
return this._type;
|
||||
}
|
||||
|
||||
public get base(): string
|
||||
{
|
||||
return this._base;
|
||||
}
|
||||
|
||||
public get action(): IActiveActionData
|
||||
{
|
||||
return this._action;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
import { IAssetAnimationAvatar, IAvatarDataContainer } from '@nitrots/api';
|
||||
import { AdjustmentFilter } from 'pixi-filters';
|
||||
|
||||
export class AvatarDataContainer implements IAvatarDataContainer
|
||||
{
|
||||
private _ink: number;
|
||||
private _foreGround: number;
|
||||
private _backGround: number;
|
||||
private _colorTransform: AdjustmentFilter;
|
||||
private _rgb: number;
|
||||
private _r: number;
|
||||
private _g: number;
|
||||
private _b: number;
|
||||
private _redMultiplier: number;
|
||||
private _greenMultiplier: number;
|
||||
private _blueMultiplier: number;
|
||||
private _alphaMultiplier: number;
|
||||
private _colorMap: Map<string, number[]>;
|
||||
private _paletteIsGrayscale: boolean;
|
||||
|
||||
constructor(k: IAssetAnimationAvatar)
|
||||
{
|
||||
this._ink = k.ink;
|
||||
|
||||
let foreground = k.foreground;
|
||||
let background = k.background;
|
||||
|
||||
foreground = foreground.replace('#', '');
|
||||
background = background.replace('#', '');
|
||||
|
||||
this._foreGround = parseInt(foreground, 16);
|
||||
this._backGround = parseInt(background, 16);
|
||||
this._colorTransform = null;
|
||||
this._rgb = parseInt(foreground, 16);
|
||||
this._r = ((this._rgb >> 16) & 0xFF);
|
||||
this._g = ((this._rgb >> 8) & 0xFF);
|
||||
this._b = ((this._rgb >> 0) & 0xFF);
|
||||
this._redMultiplier = ((this._r / 0xFF) * 1);
|
||||
this._greenMultiplier = ((this._g / 0xFF) * 1);
|
||||
this._blueMultiplier = ((this._b / 0xFF) * 1);
|
||||
this._alphaMultiplier = 1;
|
||||
this._paletteIsGrayscale = true;
|
||||
|
||||
if(this._ink === 37)
|
||||
{
|
||||
this._alphaMultiplier = 0.5;
|
||||
this._paletteIsGrayscale = false;
|
||||
}
|
||||
|
||||
this._colorTransform = new AdjustmentFilter({ red: (this._r / 255), green: (this._g / 255), blue: (this._b / 255), alpha: this._alphaMultiplier });
|
||||
this._colorMap = this.generatePaletteMapForGrayscale(this._backGround, this._foreGround);
|
||||
}
|
||||
|
||||
public get ink(): number
|
||||
{
|
||||
return this._ink;
|
||||
}
|
||||
|
||||
public get colorTransform(): AdjustmentFilter
|
||||
{
|
||||
return this._colorTransform;
|
||||
}
|
||||
|
||||
public get reds(): number[]
|
||||
{
|
||||
return this._colorMap.get('reds');
|
||||
}
|
||||
|
||||
public get greens(): number[]
|
||||
{
|
||||
return this._colorMap.get('greens');
|
||||
}
|
||||
|
||||
public get blues(): number[]
|
||||
{
|
||||
return this._colorMap.get('blues');
|
||||
}
|
||||
|
||||
public get alphas(): number[]
|
||||
{
|
||||
return this._colorMap.get('alphas');
|
||||
}
|
||||
|
||||
public get paletteIsGrayscale(): boolean
|
||||
{
|
||||
return this._paletteIsGrayscale;
|
||||
}
|
||||
|
||||
private generatePaletteMapForGrayscale(background: number, foreground: number): Map<string, number[]>
|
||||
{
|
||||
const alphaBackground = ((background >> 24) & 0xFF);
|
||||
const redBackground = ((background >> 16) & 0xFF);
|
||||
const greenBackground = ((background >> 8) & 0xFF);
|
||||
const blueBackground = ((background >> 0) & 0xFF);
|
||||
const alphaForeground = ((foreground >> 24) & 0xFF);
|
||||
const redForeground = ((foreground >> 16) & 0xFF);
|
||||
const greenForeground = ((foreground >> 8) & 0xFF);
|
||||
const blueForeground = ((foreground >> 0) & 0xFF);
|
||||
const alphaDifference = ((alphaForeground - alphaBackground) / 0xFF);
|
||||
const redDifference = ((redForeground - redBackground) / 0xFF);
|
||||
const greenDifference = ((greenForeground - greenBackground) / 0xFF);
|
||||
const blueDifference = ((blueForeground - blueBackground) / 0xFF);
|
||||
const _local_15: Map<string, number[]> = new Map();
|
||||
const _local_16: number[] = [];
|
||||
const _local_17: number[] = [];
|
||||
const _local_18: number[] = [];
|
||||
const _local_19: number[] = [];
|
||||
let _local_20 = alphaBackground;
|
||||
let _local_21 = redBackground;
|
||||
let _local_22 = greenBackground;
|
||||
let _local_23 = blueBackground;
|
||||
|
||||
for(let i = 0; i < 256; i++)
|
||||
{
|
||||
if((((_local_21 == redBackground) && (_local_22 == greenBackground)) && (_local_23 == blueBackground)))
|
||||
{
|
||||
_local_20 = 0;
|
||||
}
|
||||
_local_20 = (_local_20 + alphaDifference);
|
||||
_local_21 = (_local_21 + redDifference);
|
||||
_local_22 = (_local_22 + greenDifference);
|
||||
_local_23 = (_local_23 + blueDifference);
|
||||
_local_19.push((_local_20 << 24));
|
||||
_local_16.push(((((_local_20 << 24) | (_local_21 << 16)) | (_local_22 << 8)) | _local_23));
|
||||
_local_17.push(((((_local_20 << 24) | (_local_21 << 16)) | (_local_22 << 8)) | _local_23));
|
||||
_local_18.push(((((_local_20 << 24) | (_local_21 << 16)) | (_local_22 << 8)) | _local_23));
|
||||
}
|
||||
|
||||
_local_15.set('alphas', _local_16);
|
||||
_local_15.set('reds', _local_16);
|
||||
_local_15.set('greens', _local_17);
|
||||
_local_15.set('blues', _local_18);
|
||||
|
||||
return _local_15;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import { IAssetAnimationDirection } from '@nitrots/api';
|
||||
|
||||
export class DirectionDataContainer
|
||||
{
|
||||
private _offset: number;
|
||||
|
||||
constructor(k: IAssetAnimationDirection)
|
||||
{
|
||||
this._offset = k.offset;
|
||||
}
|
||||
|
||||
public get offset(): number
|
||||
{
|
||||
return this._offset;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
import { IAnimation, IAssetAnimationSprite, ISpriteDataContainer } from '@nitrots/api';
|
||||
|
||||
export class SpriteDataContainer implements ISpriteDataContainer
|
||||
{
|
||||
private _animation: IAnimation;
|
||||
private _id: string;
|
||||
private _ink: number;
|
||||
private _member: string;
|
||||
private _hasDirections: boolean;
|
||||
private _hasStaticY: boolean;
|
||||
private _dx: number[];
|
||||
private _dy: number[];
|
||||
private _dz: number[];
|
||||
|
||||
constructor(k: IAnimation, _arg_2: IAssetAnimationSprite)
|
||||
{
|
||||
this._animation = k;
|
||||
this._id = _arg_2.id;
|
||||
this._ink = _arg_2.ink;
|
||||
this._member = _arg_2.member;
|
||||
this._hasStaticY = _arg_2.staticY ? true : false;
|
||||
this._hasDirections = _arg_2.directions ? true : false;
|
||||
this._dx = [];
|
||||
this._dy = [];
|
||||
this._dz = [];
|
||||
|
||||
const directions = _arg_2.directionList;
|
||||
|
||||
if(directions && directions.length)
|
||||
{
|
||||
for(const direction of directions)
|
||||
{
|
||||
const id = direction.id;
|
||||
|
||||
if(id === undefined) continue;
|
||||
|
||||
this._dx[id] = (direction.dx || 0);
|
||||
this._dy[id] = (direction.dy || 0);
|
||||
this._dz[id] = (direction.dz || 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public getDirectionOffsetX(k: number): number
|
||||
{
|
||||
if(k < this._dx.length) return this._dx[k];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public getDirectionOffsetY(k: number): number
|
||||
{
|
||||
if(k < this._dy.length) return this._dy[k];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public getDirectionOffsetZ(k: number): number
|
||||
{
|
||||
if(k < this._dz.length) return this._dz[k];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public get animation(): IAnimation
|
||||
{
|
||||
return this._animation;
|
||||
}
|
||||
|
||||
public get id(): string
|
||||
{
|
||||
return this._id;
|
||||
}
|
||||
|
||||
public get ink(): number
|
||||
{
|
||||
return this._ink;
|
||||
}
|
||||
|
||||
public get member(): string
|
||||
{
|
||||
return this._member;
|
||||
}
|
||||
|
||||
public get hasDirections(): boolean
|
||||
{
|
||||
return this._hasDirections;
|
||||
}
|
||||
|
||||
public get hasStaticY(): boolean
|
||||
{
|
||||
return this._hasStaticY;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
export * from './AddDataContainer';
|
||||
export * from './Animation';
|
||||
export * from './AnimationManager';
|
||||
export * from './AvatarAnimationLayerData';
|
||||
export * from './AvatarDataContainer';
|
||||
export * from './DirectionDataContainer';
|
||||
export * from './SpriteDataContainer';
|
||||
@@ -0,0 +1,57 @@
|
||||
import { GetTickerTime } from '@nitrots/utils';
|
||||
import { AvatarImageDirectionCache } from './AvatarImageDirectionCache';
|
||||
|
||||
export class AvatarImageActionCache
|
||||
{
|
||||
private _cache: Map<string, AvatarImageDirectionCache>;
|
||||
private _lastAccessTime: number;
|
||||
|
||||
constructor()
|
||||
{
|
||||
this._cache = new Map();
|
||||
|
||||
this.setLastAccessTime(GetTickerTime());
|
||||
}
|
||||
|
||||
public dispose(): void
|
||||
{
|
||||
this.debugInfo('[dispose]');
|
||||
|
||||
if(!this._cache) return;
|
||||
|
||||
for(const direction of this._cache.values())
|
||||
{
|
||||
if(direction) direction.dispose();
|
||||
}
|
||||
|
||||
this._cache.clear();
|
||||
}
|
||||
|
||||
public getDirectionCache(k: number): AvatarImageDirectionCache
|
||||
{
|
||||
const existing = this._cache.get(k.toString());
|
||||
|
||||
if(!existing) return null;
|
||||
|
||||
return existing;
|
||||
}
|
||||
|
||||
public updateDirectionCache(k: number, _arg_2: AvatarImageDirectionCache): void
|
||||
{
|
||||
this._cache.set(k.toString(), _arg_2);
|
||||
}
|
||||
|
||||
public setLastAccessTime(k: number): void
|
||||
{
|
||||
this._lastAccessTime = k;
|
||||
}
|
||||
|
||||
public getLastAccessTime(): number
|
||||
{
|
||||
return this._lastAccessTime;
|
||||
}
|
||||
|
||||
private debugInfo(k: string): void
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
import { IActiveActionData } from '@nitrots/api';
|
||||
import { AvatarImageActionCache } from './AvatarImageActionCache';
|
||||
|
||||
export class AvatarImageBodyPartCache
|
||||
{
|
||||
private _cache: Map<string, AvatarImageActionCache>;
|
||||
private _currentAction: IActiveActionData;
|
||||
private _currentDirection: number;
|
||||
private _disposed: boolean;
|
||||
|
||||
constructor()
|
||||
{
|
||||
this._cache = new Map();
|
||||
}
|
||||
|
||||
public setAction(k: IActiveActionData, _arg_2: number): void
|
||||
{
|
||||
if(!this._currentAction) this._currentAction = k;
|
||||
|
||||
const _local_3 = this.getActionCache(this._currentAction);
|
||||
|
||||
if(_local_3) _local_3.setLastAccessTime(_arg_2);
|
||||
|
||||
this._currentAction = k;
|
||||
}
|
||||
|
||||
public dispose(): void
|
||||
{
|
||||
if(!this._disposed)
|
||||
{
|
||||
if(!this._cache) return;
|
||||
|
||||
this.disposeActions(0, 2147483647);
|
||||
|
||||
this._cache.clear();
|
||||
|
||||
this._cache = null;
|
||||
this._disposed = true;
|
||||
}
|
||||
}
|
||||
|
||||
public disposeActions(k: number, _arg_2: number): void
|
||||
{
|
||||
if(!this._cache || this._disposed) return;
|
||||
|
||||
for(const [key, cache] of this._cache.entries())
|
||||
{
|
||||
if(!cache) continue;
|
||||
|
||||
const _local_3 = cache.getLastAccessTime();
|
||||
|
||||
if((_arg_2 - _local_3) >= k)
|
||||
{
|
||||
cache.dispose();
|
||||
|
||||
this._cache.delete(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public getAction(): IActiveActionData
|
||||
{
|
||||
return this._currentAction;
|
||||
}
|
||||
|
||||
public setDirection(k: number): void
|
||||
{
|
||||
this._currentDirection = k;
|
||||
}
|
||||
|
||||
public getDirection(): number
|
||||
{
|
||||
return this._currentDirection;
|
||||
}
|
||||
|
||||
public getActionCache(k: IActiveActionData = null): AvatarImageActionCache
|
||||
{
|
||||
if(!this._currentAction) return null;
|
||||
|
||||
if(!k) k = this._currentAction;
|
||||
|
||||
if(k.overridingAction) return this._cache.get(k.overridingAction);
|
||||
|
||||
return this._cache.get(k.id);
|
||||
}
|
||||
|
||||
public updateActionCache(k: IActiveActionData, _arg_2: AvatarImageActionCache): void
|
||||
{
|
||||
if(k.overridingAction) this._cache.set(k.overridingAction, _arg_2);
|
||||
else this._cache.set(k.id, _arg_2);
|
||||
}
|
||||
|
||||
private debugInfo(k: string): void
|
||||
{
|
||||
}
|
||||
}
|
||||
+444
@@ -0,0 +1,444 @@
|
||||
import { AvatarDirectionAngle, AvatarFigurePartType, AvatarScaleType, GeometryType, IActiveActionData, IAvatarImage } from '@nitrots/api';
|
||||
import { GetTickerTime } from '@nitrots/utils';
|
||||
import { Container, Matrix, Point, Rectangle, Sprite, Texture } from 'pixi.js';
|
||||
import { AvatarImageBodyPartContainer } from '../AvatarImageBodyPartContainer';
|
||||
import { AvatarImagePartContainer } from '../AvatarImagePartContainer';
|
||||
import { AvatarStructure } from '../AvatarStructure';
|
||||
import { AssetAliasCollection } from '../alias';
|
||||
import { AvatarAnimationLayerData } from '../animation';
|
||||
import { AvatarCanvas } from '../structure';
|
||||
import { AvatarImageActionCache } from './AvatarImageActionCache';
|
||||
import { AvatarImageBodyPartCache } from './AvatarImageBodyPartCache';
|
||||
import { AvatarImageDirectionCache } from './AvatarImageDirectionCache';
|
||||
import { ImageData } from './ImageData';
|
||||
|
||||
export class AvatarImageCache
|
||||
{
|
||||
private static DEFAULT_MAX_CACHE_STORAGE_TIME_MS: number = 60000;
|
||||
|
||||
private _structure: AvatarStructure;
|
||||
private _avatar: IAvatarImage;
|
||||
private _assets: AssetAliasCollection;
|
||||
private _scale: string;
|
||||
private _cache: Map<string, AvatarImageBodyPartCache>;
|
||||
private _canvas: AvatarCanvas;
|
||||
private _disposed: boolean;
|
||||
private _geometryType: string;
|
||||
private _unionImages: ImageData[];
|
||||
private _matrix: Matrix;
|
||||
|
||||
constructor(k: AvatarStructure, _arg_2: IAvatarImage, _arg_3: AssetAliasCollection, _arg_4: string)
|
||||
{
|
||||
this._structure = k;
|
||||
this._avatar = _arg_2;
|
||||
this._assets = _arg_3;
|
||||
this._scale = _arg_4;
|
||||
this._cache = new Map();
|
||||
this._canvas = null;
|
||||
this._disposed = false;
|
||||
this._unionImages = [];
|
||||
this._matrix = new Matrix();
|
||||
}
|
||||
|
||||
public dispose(): void
|
||||
{
|
||||
if(this._disposed) return;
|
||||
|
||||
this._structure = null;
|
||||
this._avatar = null;
|
||||
this._assets = null;
|
||||
this._canvas = null;
|
||||
this._disposed = true;
|
||||
|
||||
if(this._cache)
|
||||
{
|
||||
for(const cache of this._cache.values())
|
||||
{
|
||||
if(!cache) continue;
|
||||
|
||||
cache.dispose();
|
||||
}
|
||||
|
||||
this._cache = null;
|
||||
}
|
||||
|
||||
if(this._unionImages)
|
||||
{
|
||||
for(const image of this._unionImages)
|
||||
{
|
||||
if(!image) continue;
|
||||
|
||||
image.dispose();
|
||||
}
|
||||
|
||||
this._unionImages = [];
|
||||
}
|
||||
}
|
||||
|
||||
public disposeInactiveActions(k: number = 60000): void
|
||||
{
|
||||
const time = GetTickerTime();
|
||||
|
||||
if(this._cache)
|
||||
{
|
||||
for(const cache of this._cache.values())
|
||||
{
|
||||
if(!cache) continue;
|
||||
|
||||
cache.disposeActions(k, time);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public resetBodyPartCache(k: IActiveActionData): void
|
||||
{
|
||||
if(this._cache)
|
||||
{
|
||||
for(const cache of this._cache.values())
|
||||
{
|
||||
if(!cache) continue;
|
||||
|
||||
cache.setAction(k, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public setDirection(k: string, _arg_2: number): void
|
||||
{
|
||||
const parts = this._structure.getBodyPartsUnordered(k);
|
||||
|
||||
if(parts)
|
||||
{
|
||||
for(const part of parts)
|
||||
{
|
||||
const actionCache = this.getBodyPartCache(part);
|
||||
|
||||
if(!actionCache) continue;
|
||||
|
||||
actionCache.setDirection(_arg_2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public setAction(k: IActiveActionData, _arg_2: number): void
|
||||
{
|
||||
const _local_3 = this._structure.getActiveBodyPartIds(k, this._avatar);
|
||||
|
||||
for(const _local_4 of _local_3)
|
||||
{
|
||||
const _local_5 = this.getBodyPartCache(_local_4);
|
||||
|
||||
if(_local_5) _local_5.setAction(k, _arg_2);
|
||||
}
|
||||
}
|
||||
|
||||
public setGeometryType(k: string): void
|
||||
{
|
||||
if(this._geometryType === k) return;
|
||||
|
||||
if((((this._geometryType === GeometryType.SITTING) && (k === GeometryType.VERTICAL)) || ((this._geometryType === GeometryType.VERTICAL) && (k === GeometryType.SITTING)) || ((this._geometryType === GeometryType.SNOWWARS_HORIZONTAL) && (k = GeometryType.SNOWWARS_HORIZONTAL))))
|
||||
{
|
||||
this._geometryType = k;
|
||||
this._canvas = null;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this.disposeInactiveActions(0);
|
||||
|
||||
this._geometryType = k;
|
||||
this._canvas = null;
|
||||
}
|
||||
|
||||
public getImageContainer(key: string, frameNumber: number, forceRefresh: boolean = false): AvatarImageBodyPartContainer
|
||||
{
|
||||
const bodyPartCache = this.getBodyPartCache(key) || new AvatarImageBodyPartCache();
|
||||
|
||||
this._cache.set(key, bodyPartCache);
|
||||
|
||||
let direction = bodyPartCache.getDirection();
|
||||
let action = bodyPartCache.getAction();
|
||||
let adjustedFrameCount = frameNumber;
|
||||
|
||||
if(action.definition.startFromFrameZero) adjustedFrameCount -= action.startFrame;
|
||||
|
||||
let adjustedAction = action;
|
||||
let removeData: string[] = [];
|
||||
let items: Map<string, string> = new Map();
|
||||
|
||||
const positionOffset = new Point();
|
||||
|
||||
if(action.definition.isAnimation)
|
||||
{
|
||||
let adjustedDirection = direction;
|
||||
|
||||
const animation = this._structure.getAnimation(((action.definition.state + '.') + action.actionParameter));
|
||||
const animationFrameOffset = (frameNumber - action.startFrame);
|
||||
|
||||
if(animation)
|
||||
{
|
||||
const layerData = animation.getLayerData(animationFrameOffset, key, action.overridingAction);
|
||||
|
||||
if(layerData)
|
||||
{
|
||||
adjustedDirection = (direction + layerData.dd + 8) % 8;
|
||||
|
||||
positionOffset.x = this._scale === AvatarScaleType.LARGE ? layerData.dx : layerData.dx / 2;
|
||||
positionOffset.y = this._scale === AvatarScaleType.LARGE ? layerData.dy : layerData.dy / 2;
|
||||
|
||||
adjustedFrameCount = layerData.animationFrame;
|
||||
|
||||
if(layerData.action) action = layerData.action;
|
||||
|
||||
if(layerData.type === AvatarAnimationLayerData.BODYPART)
|
||||
{
|
||||
if(layerData.action) adjustedAction = layerData.action;
|
||||
|
||||
direction = adjustedDirection;
|
||||
}
|
||||
else if(layerData.type === AvatarAnimationLayerData.FX) direction = adjustedDirection;
|
||||
|
||||
items = layerData.items;
|
||||
}
|
||||
|
||||
removeData = animation.removeData;
|
||||
}
|
||||
}
|
||||
|
||||
let actionCache = bodyPartCache.getActionCache(adjustedAction);
|
||||
|
||||
if(!actionCache || forceRefresh)
|
||||
{
|
||||
actionCache = new AvatarImageActionCache();
|
||||
bodyPartCache.updateActionCache(adjustedAction, actionCache);
|
||||
}
|
||||
|
||||
let directionCache = actionCache.getDirectionCache(direction);
|
||||
|
||||
if(!directionCache || forceRefresh)
|
||||
{
|
||||
const partList = this._structure.getParts(key, this._avatar.getFigure(), adjustedAction, this._geometryType, direction, removeData, this._avatar, items);
|
||||
|
||||
directionCache = new AvatarImageDirectionCache(partList);
|
||||
|
||||
actionCache.updateDirectionCache(direction, directionCache);
|
||||
}
|
||||
|
||||
let imageContainer = directionCache.getImageContainer(adjustedFrameCount);
|
||||
|
||||
if(!imageContainer || forceRefresh)
|
||||
{
|
||||
const partList = directionCache.getPartList();
|
||||
|
||||
imageContainer = this.renderBodyPart(direction, partList, adjustedFrameCount, action);
|
||||
|
||||
if(imageContainer && !forceRefresh)
|
||||
{
|
||||
if(imageContainer.isCacheable) directionCache.updateImageContainer(imageContainer, adjustedFrameCount);
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
const offset = this._structure.getFrameBodyPartOffset(adjustedAction, direction, adjustedFrameCount, key);
|
||||
|
||||
positionOffset.x += offset.x;
|
||||
positionOffset.y += offset.y;
|
||||
|
||||
imageContainer.offset = positionOffset;
|
||||
|
||||
return imageContainer;
|
||||
}
|
||||
|
||||
public getBodyPartCache(k: string): AvatarImageBodyPartCache
|
||||
{
|
||||
let existing = this._cache.get(k);
|
||||
|
||||
if(!existing)
|
||||
{
|
||||
existing = new AvatarImageBodyPartCache();
|
||||
|
||||
this._cache.set(k, existing);
|
||||
}
|
||||
|
||||
return existing;
|
||||
}
|
||||
|
||||
private renderBodyPart(direction: number, containers: AvatarImagePartContainer[], frameCount: number, action: IActiveActionData): AvatarImageBodyPartContainer
|
||||
{
|
||||
if(!containers || !containers.length) return null;
|
||||
|
||||
if(!this._canvas)
|
||||
{
|
||||
this._canvas = this._structure.getCanvas(this._scale, this._geometryType);
|
||||
|
||||
if(!this._canvas) return null;
|
||||
}
|
||||
|
||||
const isFlipped = AvatarDirectionAngle.DIRECTION_IS_FLIPPED[direction] || false;
|
||||
let assetPartDefinition = action.definition.assetPartDefinition;
|
||||
let isCacheable = true;
|
||||
let containerIndex = (containers.length - 1);
|
||||
|
||||
while(containerIndex >= 0)
|
||||
{
|
||||
const container = containers[containerIndex];
|
||||
|
||||
let color = 16777215;
|
||||
|
||||
if(!((direction == 7) && ((container.partType === 'fc') || (container.partType === 'ey'))))
|
||||
{
|
||||
if(!((container.partType === 'ri') && !container.partId))
|
||||
{
|
||||
const partId = container.partId;
|
||||
const animationFrame = container.getFrameDefinition(frameCount);
|
||||
|
||||
let partType = container.partType;
|
||||
let frameNumber = 0;
|
||||
|
||||
if(animationFrame)
|
||||
{
|
||||
frameNumber = animationFrame.number;
|
||||
|
||||
if((animationFrame.assetPartDefinition) && (animationFrame.assetPartDefinition !== '')) assetPartDefinition = animationFrame.assetPartDefinition;
|
||||
}
|
||||
else frameNumber = container.getFrameIndex(frameCount);
|
||||
|
||||
let assetDirection = direction;
|
||||
let flipH = false;
|
||||
|
||||
if(isFlipped)
|
||||
{
|
||||
if(((assetPartDefinition === 'wav') && (((partType === AvatarFigurePartType.LEFT_HAND) || (partType === AvatarFigurePartType.LEFT_SLEEVE)) || (partType === AvatarFigurePartType.LEFT_COAT_SLEEVE))) || ((assetPartDefinition === 'drk') && (((partType === AvatarFigurePartType.RIGHT_HAND) || (partType === AvatarFigurePartType.RIGHT_SLEEVE)) || (partType === AvatarFigurePartType.RIGHT_COAT_SLEEVE))) || ((assetPartDefinition === 'blw') && (partType === AvatarFigurePartType.RIGHT_HAND)) || ((assetPartDefinition === 'sig') && (partType === AvatarFigurePartType.LEFT_HAND)) || ((assetPartDefinition === 'respect') && (partType === AvatarFigurePartType.LEFT_HAND)) || (partType === AvatarFigurePartType.RIGHT_HAND_ITEM) || (partType === AvatarFigurePartType.LEFT_HAND_ITEM) || (partType === AvatarFigurePartType.CHEST_PRINT))
|
||||
{
|
||||
flipH = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(direction === 4) assetDirection = 2;
|
||||
else if(direction === 5) assetDirection = 1;
|
||||
else if(direction === 6) assetDirection = 0;
|
||||
|
||||
if(container.flippedPartType !== partType) partType = container.flippedPartType;
|
||||
}
|
||||
}
|
||||
|
||||
let assetName = (this._scale + '_' + assetPartDefinition + '_' + partType + '_' + partId + '_' + assetDirection + '_' + frameNumber);
|
||||
let asset = this._assets.getAsset(assetName);
|
||||
|
||||
if(!asset)
|
||||
{
|
||||
assetName = (this._scale + '_std_' + partType + '_' + partId + '_' + assetDirection + '_0');
|
||||
asset = this._assets.getAsset(assetName);
|
||||
}
|
||||
|
||||
if(asset)
|
||||
{
|
||||
const texture = asset.texture;
|
||||
|
||||
if(!texture || !texture.source)
|
||||
{
|
||||
isCacheable = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(container.isColorable && container.color) color = container.color.rgb;
|
||||
|
||||
const offset = new Point(-(asset.x), -(asset.y));
|
||||
|
||||
if(flipH) offset.x = (offset.x + ((this._scale === AvatarScaleType.LARGE) ? 65 : 31));
|
||||
|
||||
this._unionImages.push(new ImageData(texture, asset.rectangle, offset, flipH, color));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
containerIndex--;
|
||||
}
|
||||
|
||||
if(!this._unionImages.length) return null;
|
||||
|
||||
const imageData = this.createUnionImage(this._unionImages, isFlipped);
|
||||
const canvasOffset = ((this._scale === AvatarScaleType.LARGE) ? (this._canvas.height - 16) : (this._canvas.height - 8));
|
||||
const offset = new Point(-(imageData.regPoint.x), (canvasOffset - imageData.regPoint.y));
|
||||
|
||||
if(isFlipped && (assetPartDefinition !== 'lay')) offset.x = (offset.x + ((this._scale === AvatarScaleType.LARGE) ? 67 : 31));
|
||||
|
||||
let imageIndex = (this._unionImages.length - 1);
|
||||
|
||||
while(imageIndex >= 0)
|
||||
{
|
||||
const _local_17 = this._unionImages.pop();
|
||||
|
||||
if(_local_17) _local_17.dispose();
|
||||
|
||||
imageIndex--;
|
||||
}
|
||||
|
||||
return new AvatarImageBodyPartContainer(imageData.container, offset, isCacheable);
|
||||
}
|
||||
|
||||
private convertColorToHex(k: number): string
|
||||
{
|
||||
let _local_2: string = (k * 0xFF).toString(16);
|
||||
if(_local_2.length < 2)
|
||||
{
|
||||
_local_2 = ('0' + _local_2);
|
||||
}
|
||||
return _local_2;
|
||||
}
|
||||
|
||||
private createUnionImage(k: ImageData[], isFlipped: boolean): ImageData
|
||||
{
|
||||
const bounds = new Rectangle();
|
||||
|
||||
for(const data of k) data && bounds.enlarge(data.offsetRect);
|
||||
|
||||
const point = new Point(-(bounds.x), -(bounds.y));
|
||||
const container = new Container();
|
||||
|
||||
const sprite = new Sprite(Texture.EMPTY);
|
||||
|
||||
sprite.width = bounds.width;
|
||||
sprite.height = bounds.height;
|
||||
|
||||
container.addChild(sprite);
|
||||
|
||||
for(const data of k)
|
||||
{
|
||||
if(!data) continue;
|
||||
|
||||
const regPoint = point.clone();
|
||||
|
||||
regPoint.x -= data.regPoint.x;
|
||||
regPoint.y -= data.regPoint.y;
|
||||
|
||||
if(isFlipped) regPoint.x = (container.width - (regPoint.x + data.rect.width));
|
||||
|
||||
if(isFlipped != data.flipH)
|
||||
{
|
||||
this._matrix.a = -1;
|
||||
this._matrix.tx = ((data.rect.x + data.rect.width) + regPoint.x);
|
||||
this._matrix.ty = (regPoint.y - data.rect.y);
|
||||
}
|
||||
else
|
||||
{
|
||||
this._matrix.a = 1;
|
||||
this._matrix.tx = (regPoint.x - data.rect.x);
|
||||
this._matrix.ty = (regPoint.y - data.rect.y);
|
||||
}
|
||||
|
||||
const sprite = new Sprite(data.texture);
|
||||
|
||||
sprite.tint = data.colorTransform;
|
||||
sprite.setFromMatrix(this._matrix);
|
||||
|
||||
container.addChild(sprite);
|
||||
}
|
||||
|
||||
return new ImageData(null, container.getLocalBounds().rectangle, point, isFlipped, null, container);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
import { AvatarImageBodyPartContainer } from '../AvatarImageBodyPartContainer';
|
||||
import { AvatarImagePartContainer } from '../AvatarImagePartContainer';
|
||||
|
||||
export class AvatarImageDirectionCache
|
||||
{
|
||||
private _partList: AvatarImagePartContainer[];
|
||||
private _images: Map<string, AvatarImageBodyPartContainer>;
|
||||
|
||||
constructor(k: AvatarImagePartContainer[])
|
||||
{
|
||||
this._partList = k;
|
||||
this._images = new Map();
|
||||
}
|
||||
|
||||
public dispose(): void
|
||||
{
|
||||
for(const image of this._images.values()) image && image.dispose();
|
||||
|
||||
this._images = null;
|
||||
}
|
||||
|
||||
public getPartList(): AvatarImagePartContainer[]
|
||||
{
|
||||
return this._partList;
|
||||
}
|
||||
|
||||
public getImageContainer(k: number): AvatarImageBodyPartContainer
|
||||
{
|
||||
const existing = this._images.get(this.getCacheKey(k));
|
||||
|
||||
if(!existing) return null;
|
||||
|
||||
return existing;
|
||||
}
|
||||
|
||||
public updateImageContainer(k: AvatarImageBodyPartContainer, _arg_2: number): void
|
||||
{
|
||||
const name = this.getCacheKey(_arg_2);
|
||||
|
||||
const existing = this._images.get(name);
|
||||
|
||||
if(existing) existing.dispose();
|
||||
|
||||
this._images.set(name, k);
|
||||
}
|
||||
|
||||
private getCacheKey(k: number): string
|
||||
{
|
||||
let name = '';
|
||||
|
||||
for(const part of this._partList) name += (part.getCacheableKey(k) + '/');
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
private debugInfo(k: string): void
|
||||
{
|
||||
}
|
||||
}
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
import { Container, Point, Rectangle, Texture } from 'pixi.js';
|
||||
|
||||
export class ImageData
|
||||
{
|
||||
private _texture: Texture;
|
||||
private _container: Container;
|
||||
private _rect: Rectangle;
|
||||
private _regPoint: Point;
|
||||
private _flipH: boolean;
|
||||
private _colorTransform: number;
|
||||
|
||||
constructor(texture: Texture, rectangle: Rectangle, _arg_3: Point, flipH: boolean, color: number, container: Container = null)
|
||||
{
|
||||
this._texture = texture;
|
||||
this._container = container;
|
||||
this._rect = rectangle;
|
||||
this._regPoint = _arg_3;
|
||||
this._flipH = flipH;
|
||||
this._colorTransform = color;
|
||||
|
||||
if(flipH) this._regPoint.x = (-(this._regPoint.x) + rectangle.width);
|
||||
}
|
||||
|
||||
public dispose(): void
|
||||
{
|
||||
this._texture = null;
|
||||
this._regPoint = null;
|
||||
this._colorTransform = null;
|
||||
}
|
||||
|
||||
public get texture(): Texture
|
||||
{
|
||||
return this._texture;
|
||||
}
|
||||
|
||||
public get container(): Container
|
||||
{
|
||||
return this._container;
|
||||
}
|
||||
|
||||
public get rect(): Rectangle
|
||||
{
|
||||
return this._rect;
|
||||
}
|
||||
|
||||
public get regPoint(): Point
|
||||
{
|
||||
return this._regPoint;
|
||||
}
|
||||
|
||||
public get flipH(): boolean
|
||||
{
|
||||
return this._flipH;
|
||||
}
|
||||
|
||||
public get colorTransform(): number
|
||||
{
|
||||
return this._colorTransform;
|
||||
}
|
||||
|
||||
public get offsetRect(): Rectangle
|
||||
{
|
||||
return new Rectangle(-(this._regPoint.x), -(this._regPoint.y), this._rect.width, this._rect.height);
|
||||
}
|
||||
}
|
||||
Vendored
+5
@@ -0,0 +1,5 @@
|
||||
export * from './AvatarImageActionCache';
|
||||
export * from './AvatarImageBodyPartCache';
|
||||
export * from './AvatarImageCache';
|
||||
export * from './AvatarImageDirectionCache';
|
||||
export * from './ImageData';
|
||||
@@ -0,0 +1,827 @@
|
||||
export const HabboAvatarAnimations = {
|
||||
'animations': [
|
||||
{
|
||||
'id': 'Move',
|
||||
'parts': [
|
||||
{
|
||||
'setType': 'bd',
|
||||
'frames': [
|
||||
{
|
||||
'number': 0,
|
||||
'assetPartDefinition': 'wlk'
|
||||
},
|
||||
{
|
||||
'number': 1,
|
||||
'assetPartDefinition': 'wlk'
|
||||
},
|
||||
{
|
||||
'number': 2,
|
||||
'assetPartDefinition': 'wlk'
|
||||
},
|
||||
{
|
||||
'number': 3,
|
||||
'assetPartDefinition': 'wlk'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'setType': 'bds',
|
||||
'frames': [
|
||||
{
|
||||
'number': 0,
|
||||
'assetPartDefinition': 'wlk'
|
||||
},
|
||||
{
|
||||
'number': 1,
|
||||
'assetPartDefinition': 'wlk'
|
||||
},
|
||||
{
|
||||
'number': 2,
|
||||
'assetPartDefinition': 'wlk'
|
||||
},
|
||||
{
|
||||
'number': 3,
|
||||
'assetPartDefinition': 'wlk'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'setType': 'ss',
|
||||
'frames': [
|
||||
{
|
||||
'number': 0,
|
||||
'assetPartDefinition': 'wlk'
|
||||
},
|
||||
{
|
||||
'number': 1,
|
||||
'assetPartDefinition': 'wlk'
|
||||
},
|
||||
{
|
||||
'number': 2,
|
||||
'assetPartDefinition': 'wlk'
|
||||
},
|
||||
{
|
||||
'number': 3,
|
||||
'assetPartDefinition': 'wlk'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'setType': 'lg',
|
||||
'frames': [
|
||||
{
|
||||
'number': 0,
|
||||
'assetPartDefinition': 'wlk'
|
||||
},
|
||||
{
|
||||
'number': 1,
|
||||
'assetPartDefinition': 'wlk'
|
||||
},
|
||||
{
|
||||
'number': 2,
|
||||
'assetPartDefinition': 'wlk'
|
||||
},
|
||||
{
|
||||
'number': 3,
|
||||
'assetPartDefinition': 'wlk'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'setType': 'sh',
|
||||
'frames': [
|
||||
{
|
||||
'number': 0,
|
||||
'assetPartDefinition': 'wlk'
|
||||
},
|
||||
{
|
||||
'number': 1,
|
||||
'assetPartDefinition': 'wlk'
|
||||
},
|
||||
{
|
||||
'number': 2,
|
||||
'assetPartDefinition': 'wlk'
|
||||
},
|
||||
{
|
||||
'number': 3,
|
||||
'assetPartDefinition': 'wlk'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'setType': 'lh',
|
||||
'frames': [
|
||||
{
|
||||
'number': 0,
|
||||
'assetPartDefinition': 'wlk'
|
||||
},
|
||||
{
|
||||
'number': 1,
|
||||
'assetPartDefinition': 'wlk'
|
||||
},
|
||||
{
|
||||
'number': 2,
|
||||
'assetPartDefinition': 'wlk'
|
||||
},
|
||||
{
|
||||
'number': 3,
|
||||
'assetPartDefinition': 'wlk'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'setType': 'lhs',
|
||||
'frames': [
|
||||
{
|
||||
'number': 0,
|
||||
'assetPartDefinition': 'wlk'
|
||||
},
|
||||
{
|
||||
'number': 1,
|
||||
'assetPartDefinition': 'wlk'
|
||||
},
|
||||
{
|
||||
'number': 2,
|
||||
'assetPartDefinition': 'wlk'
|
||||
},
|
||||
{
|
||||
'number': 3,
|
||||
'assetPartDefinition': 'wlk'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'setType': 'ls',
|
||||
'frames': [
|
||||
{
|
||||
'number': 0,
|
||||
'assetPartDefinition': 'wlk'
|
||||
},
|
||||
{
|
||||
'number': 1,
|
||||
'assetPartDefinition': 'wlk'
|
||||
},
|
||||
{
|
||||
'number': 2,
|
||||
'assetPartDefinition': 'wlk'
|
||||
},
|
||||
{
|
||||
'number': 3,
|
||||
'assetPartDefinition': 'wlk'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'setType': 'lc',
|
||||
'frames': [
|
||||
{
|
||||
'number': 0,
|
||||
'assetPartDefinition': 'wlk'
|
||||
},
|
||||
{
|
||||
'number': 1,
|
||||
'assetPartDefinition': 'wlk'
|
||||
},
|
||||
{
|
||||
'number': 2,
|
||||
'assetPartDefinition': 'wlk'
|
||||
},
|
||||
{
|
||||
'number': 3,
|
||||
'assetPartDefinition': 'wlk'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'setType': 'rh',
|
||||
'frames': [
|
||||
{
|
||||
'number': 0,
|
||||
'assetPartDefinition': 'wlk'
|
||||
},
|
||||
{
|
||||
'number': 1,
|
||||
'assetPartDefinition': 'wlk'
|
||||
},
|
||||
{
|
||||
'number': 2,
|
||||
'assetPartDefinition': 'wlk'
|
||||
},
|
||||
{
|
||||
'number': 3,
|
||||
'assetPartDefinition': 'wlk'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'setType': 'rhs',
|
||||
'frames': [
|
||||
{
|
||||
'number': 0,
|
||||
'assetPartDefinition': 'wlk'
|
||||
},
|
||||
{
|
||||
'number': 1,
|
||||
'assetPartDefinition': 'wlk'
|
||||
},
|
||||
{
|
||||
'number': 2,
|
||||
'assetPartDefinition': 'wlk'
|
||||
},
|
||||
{
|
||||
'number': 3,
|
||||
'assetPartDefinition': 'wlk'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'setType': 'rs',
|
||||
'frames': [
|
||||
{
|
||||
'number': 0,
|
||||
'assetPartDefinition': 'wlk'
|
||||
},
|
||||
{
|
||||
'number': 1,
|
||||
'assetPartDefinition': 'wlk'
|
||||
},
|
||||
{
|
||||
'number': 2,
|
||||
'assetPartDefinition': 'wlk'
|
||||
},
|
||||
{
|
||||
'number': 3,
|
||||
'assetPartDefinition': 'wlk'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'setType': 'rc',
|
||||
'frames': [
|
||||
{
|
||||
'number': 0,
|
||||
'assetPartDefinition': 'wlk'
|
||||
},
|
||||
{
|
||||
'number': 1,
|
||||
'assetPartDefinition': 'wlk'
|
||||
},
|
||||
{
|
||||
'number': 2,
|
||||
'assetPartDefinition': 'wlk'
|
||||
},
|
||||
{
|
||||
'number': 3,
|
||||
'assetPartDefinition': 'wlk'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'setType': 'ch',
|
||||
'frames': [
|
||||
{
|
||||
'number': 0,
|
||||
'assetPartDefinition': 'wlk'
|
||||
},
|
||||
{
|
||||
'number': 1,
|
||||
'assetPartDefinition': 'wlk'
|
||||
},
|
||||
{
|
||||
'number': 2,
|
||||
'assetPartDefinition': 'wlk'
|
||||
},
|
||||
{
|
||||
'number': 3,
|
||||
'assetPartDefinition': 'wlk'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'id': 'Wave',
|
||||
'parts': [
|
||||
{
|
||||
'setType': 'lh',
|
||||
'frames': [
|
||||
{
|
||||
'number': 0,
|
||||
'assetPartDefinition': 'wav'
|
||||
},
|
||||
{
|
||||
'number': 1,
|
||||
'assetPartDefinition': 'wav'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'setType': 'lhs',
|
||||
'frames': [
|
||||
{
|
||||
'number': 0,
|
||||
'assetPartDefinition': 'wav'
|
||||
},
|
||||
{
|
||||
'number': 1,
|
||||
'assetPartDefinition': 'wav'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'setType': 'ls',
|
||||
'frames': [
|
||||
{
|
||||
'number': 0,
|
||||
'assetPartDefinition': 'wav'
|
||||
},
|
||||
{
|
||||
'number': 1,
|
||||
'assetPartDefinition': 'wav'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'setType': 'lc',
|
||||
'frames': [
|
||||
{
|
||||
'number': 0,
|
||||
'assetPartDefinition': 'wav'
|
||||
},
|
||||
{
|
||||
'number': 1,
|
||||
'assetPartDefinition': 'wav'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'setType': 'ch',
|
||||
'frames': [
|
||||
{
|
||||
'number': 0,
|
||||
'assetPartDefinition': 'wav'
|
||||
},
|
||||
{
|
||||
'number': 1,
|
||||
'assetPartDefinition': 'wav'
|
||||
},
|
||||
{
|
||||
'number': 2,
|
||||
'assetPartDefinition': 'wav'
|
||||
},
|
||||
{
|
||||
'number': 3,
|
||||
'assetPartDefinition': 'wav'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'id': 'Talk',
|
||||
'parts': [
|
||||
{
|
||||
'setType': 'hd',
|
||||
'frames': [
|
||||
{
|
||||
'number': 0,
|
||||
'assetPartDefinition': 'spk'
|
||||
},
|
||||
{
|
||||
'number': 1,
|
||||
'assetPartDefinition': 'spk'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'setType': 'fc',
|
||||
'frames': [
|
||||
{
|
||||
'number': 0,
|
||||
'assetPartDefinition': 'spk'
|
||||
},
|
||||
{
|
||||
'number': 1,
|
||||
'assetPartDefinition': 'spk'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'setType': 'fa',
|
||||
'frames': [
|
||||
{
|
||||
'number': 0,
|
||||
'assetPartDefinition': 'spk'
|
||||
},
|
||||
{
|
||||
'number': 1,
|
||||
'assetPartDefinition': 'spk'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'id': 'Sign',
|
||||
'parts': [
|
||||
{
|
||||
'setType': 'lh',
|
||||
'frames': [
|
||||
{
|
||||
'number': 0,
|
||||
'assetPartDefinition': 'sig'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'setType': 'li',
|
||||
'frames': [
|
||||
{
|
||||
'number': 0,
|
||||
'assetPartDefinition': 'sig'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'setType': 'ls',
|
||||
'frames': [
|
||||
{
|
||||
'number': 0,
|
||||
'assetPartDefinition': 'wav'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'setType': 'lc',
|
||||
'frames': [
|
||||
{
|
||||
'number': 0,
|
||||
'assetPartDefinition': 'wav'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'id': 'Respect',
|
||||
'parts': [
|
||||
{
|
||||
'setType': 'lh',
|
||||
'frames': [
|
||||
{
|
||||
'number': 0,
|
||||
'assetPartDefinition': 'respect',
|
||||
'repeats': 15
|
||||
},
|
||||
{
|
||||
'number': 1,
|
||||
'assetPartDefinition': 'respect',
|
||||
'repeats': 15
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'setType': 'ls',
|
||||
'frames': [
|
||||
{
|
||||
'number': 0,
|
||||
'assetPartDefinition': 'wav',
|
||||
'repeats': 15
|
||||
},
|
||||
{
|
||||
'number': 1,
|
||||
'assetPartDefinition': 'wav',
|
||||
'repeats': 15
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'setType': 'lc',
|
||||
'frames': [
|
||||
{
|
||||
'number': 0,
|
||||
'assetPartDefinition': 'wav',
|
||||
'repeats': 15
|
||||
},
|
||||
{
|
||||
'number': 1,
|
||||
'assetPartDefinition': 'wav',
|
||||
'repeats': 15
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'id': 'Blow',
|
||||
'parts': [
|
||||
{
|
||||
'setType': 'rh',
|
||||
'frames': [
|
||||
{
|
||||
'number': 0,
|
||||
'assetPartDefinition': 'blw',
|
||||
'repeats': 10
|
||||
},
|
||||
{
|
||||
'number': 1,
|
||||
'assetPartDefinition': 'blw',
|
||||
'repeats': 10
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'setType': 'rs',
|
||||
'frames': [
|
||||
{
|
||||
'number': 0,
|
||||
'assetPartDefinition': 'drk'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'setType': 'rc',
|
||||
'frames': [
|
||||
{
|
||||
'number': 0,
|
||||
'assetPartDefinition': 'drk'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'setType': 'ri',
|
||||
'frames': [
|
||||
{
|
||||
'number': 0,
|
||||
'assetPartDefinition': ''
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'setType': 'ey',
|
||||
'frames': [
|
||||
{
|
||||
'number': 0,
|
||||
'assetPartDefinition': 'std',
|
||||
'repeats': 10
|
||||
},
|
||||
{
|
||||
'number': 0,
|
||||
'assetPartDefinition': 'eyb',
|
||||
'repeats': 10
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'setType': 'fc',
|
||||
'frames': [
|
||||
{
|
||||
'number': 0,
|
||||
'assetPartDefinition': 'std',
|
||||
'repeats': 10
|
||||
},
|
||||
{
|
||||
'number': 0,
|
||||
'assetPartDefinition': 'blw',
|
||||
'repeats': 10
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'id': 'Laugh',
|
||||
'parts': [
|
||||
{
|
||||
'setType': 'rh',
|
||||
'frames': [
|
||||
{
|
||||
'number': 0,
|
||||
'assetPartDefinition': 'blw'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'setType': 'rs',
|
||||
'frames': [
|
||||
{
|
||||
'number': 0,
|
||||
'assetPartDefinition': 'drk'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'setType': 'rc',
|
||||
'frames': [
|
||||
{
|
||||
'number': 0,
|
||||
'assetPartDefinition': 'drk'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'setType': 'ri',
|
||||
'frames': [
|
||||
{
|
||||
'number': 0,
|
||||
'assetPartDefinition': ''
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'setType': 'ey',
|
||||
'frames': [
|
||||
{
|
||||
'number': 0,
|
||||
'assetPartDefinition': 'std',
|
||||
'repeats': 2
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'setType': 'fc',
|
||||
'frames': [
|
||||
{
|
||||
'number': 0,
|
||||
'assetPartDefinition': 'sml'
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
'offsets': {
|
||||
'frames': [
|
||||
{
|
||||
'id': 0,
|
||||
'directions': [
|
||||
{
|
||||
'id': 0,
|
||||
'bodyParts': [
|
||||
{
|
||||
'id': 'head',
|
||||
'dx': 0,
|
||||
'dy': 1
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'id': 1,
|
||||
'bodyParts': [
|
||||
{
|
||||
'id': 'head',
|
||||
'dx': 0,
|
||||
'dy': 1
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'id': 2,
|
||||
'bodyParts': [
|
||||
{
|
||||
'id': 'head',
|
||||
'dx': 0,
|
||||
'dy': 1
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'id': 3,
|
||||
'bodyParts': [
|
||||
{
|
||||
'id': 'head',
|
||||
'dx': 0,
|
||||
'dy': 1
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'id': 4,
|
||||
'bodyParts': [
|
||||
{
|
||||
'id': 'head',
|
||||
'dx': 0,
|
||||
'dy': 1
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'id': 5,
|
||||
'bodyParts': [
|
||||
{
|
||||
'id': 'head',
|
||||
'dx': 0,
|
||||
'dy': 1
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'id': 6,
|
||||
'bodyParts': [
|
||||
{
|
||||
'id': 'head',
|
||||
'dx': 0,
|
||||
'dy': 1
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'id': 7,
|
||||
'bodyParts': [
|
||||
{
|
||||
'id': 'head',
|
||||
'dx': 0,
|
||||
'dy': 1
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'id': 1,
|
||||
'directions': [
|
||||
{
|
||||
'id': 0,
|
||||
'bodyParts': [
|
||||
{
|
||||
'id': 'head',
|
||||
'dx': 0,
|
||||
'dy': 0
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'id': 1,
|
||||
'bodyParts': [
|
||||
{
|
||||
'id': 'head',
|
||||
'dx': 0,
|
||||
'dy': 0
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'id': 2,
|
||||
'bodyParts': [
|
||||
{
|
||||
'id': 'head',
|
||||
'dx': 0,
|
||||
'dy': 0
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'id': 3,
|
||||
'bodyParts': [
|
||||
{
|
||||
'id': 'head',
|
||||
'dx': 0,
|
||||
'dy': 0
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'id': 4,
|
||||
'bodyParts': [
|
||||
{
|
||||
'id': 'head',
|
||||
'dx': 0,
|
||||
'dy': 0
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'id': 5,
|
||||
'bodyParts': [
|
||||
{
|
||||
'id': 'head',
|
||||
'dx': 0,
|
||||
'dy': 0
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'id': 6,
|
||||
'bodyParts': [
|
||||
{
|
||||
'id': 'head',
|
||||
'dx': 0,
|
||||
'dy': 0
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'id': 7,
|
||||
'bodyParts': [
|
||||
{
|
||||
'id': 'head',
|
||||
'dx': 0,
|
||||
'dy': 0
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,418 @@
|
||||
export const HabboAvatarPartSets = {
|
||||
'partSets': {
|
||||
'partSet': [
|
||||
{
|
||||
'setType': 'ri',
|
||||
'flippedSetType': 'ri'
|
||||
},
|
||||
{
|
||||
'setType': 'ri',
|
||||
'flippedSetType': 'ri'
|
||||
},
|
||||
{
|
||||
'setType': 'rh',
|
||||
'flippedSetType': 'lh'
|
||||
},
|
||||
{
|
||||
'setType': 'rhs',
|
||||
'flippedSetType': 'lhs'
|
||||
},
|
||||
{
|
||||
'setType': 'rs',
|
||||
'swim': '0',
|
||||
'flippedSetType': 'ls'
|
||||
},
|
||||
{
|
||||
'setType': 'rc',
|
||||
'flippedSetType': 'lc'
|
||||
},
|
||||
{
|
||||
'setType': 'bd'
|
||||
},
|
||||
{
|
||||
'setType': 'bds'
|
||||
},
|
||||
{
|
||||
'setType': 'ss'
|
||||
},
|
||||
{
|
||||
'setType': 'sh'
|
||||
},
|
||||
{
|
||||
'setType': 'lg'
|
||||
},
|
||||
{
|
||||
'setType': 'ch'
|
||||
},
|
||||
{
|
||||
'setType': 'cp'
|
||||
},
|
||||
{
|
||||
'setType': 'cc'
|
||||
},
|
||||
{
|
||||
'setType': 'hd'
|
||||
},
|
||||
{
|
||||
'setType': 'fc'
|
||||
},
|
||||
{
|
||||
'setType': 'ey'
|
||||
},
|
||||
{
|
||||
'setType': 'hr'
|
||||
},
|
||||
{
|
||||
'setType': 'hrb',
|
||||
'removeSetType': 'hr'
|
||||
},
|
||||
{
|
||||
'setType': 'li',
|
||||
'flippedSetType': 'li'
|
||||
},
|
||||
{
|
||||
'setType': 'lh',
|
||||
'flippedSetType': 'rh'
|
||||
},
|
||||
{
|
||||
'setType': 'lhs',
|
||||
'flippedSetType': 'rhs'
|
||||
},
|
||||
{
|
||||
'setType': 'ls',
|
||||
'flippedSetType': 'rs'
|
||||
},
|
||||
{
|
||||
'setType': 'lc',
|
||||
'flippedSetType': 'rc'
|
||||
},
|
||||
{
|
||||
'setType': 'wa'
|
||||
},
|
||||
{
|
||||
'setType': 'ea'
|
||||
},
|
||||
{
|
||||
'setType': 'ca'
|
||||
},
|
||||
{
|
||||
'setType': 'fa'
|
||||
},
|
||||
{
|
||||
'setType': 'ha'
|
||||
},
|
||||
{
|
||||
'setType': 'he'
|
||||
}
|
||||
],
|
||||
'activePartSets': [
|
||||
{
|
||||
'id': 'figure',
|
||||
'activeParts': [
|
||||
{
|
||||
'setType': 'rh'
|
||||
},
|
||||
{
|
||||
'setType': 'rh'
|
||||
},
|
||||
{
|
||||
'setType': 'rhs'
|
||||
},
|
||||
{
|
||||
'setType': 'rs'
|
||||
},
|
||||
{
|
||||
'setType': 'rc'
|
||||
},
|
||||
{
|
||||
'setType': 'bd'
|
||||
},
|
||||
{
|
||||
'setType': 'bds'
|
||||
},
|
||||
{
|
||||
'setType': 'ss'
|
||||
},
|
||||
{
|
||||
'setType': 'sh'
|
||||
},
|
||||
{
|
||||
'setType': 'lg'
|
||||
},
|
||||
{
|
||||
'setType': 'ch'
|
||||
},
|
||||
{
|
||||
'setType': 'cp'
|
||||
},
|
||||
{
|
||||
'setType': 'cc'
|
||||
},
|
||||
{
|
||||
'setType': 'wa'
|
||||
},
|
||||
{
|
||||
'setType': 'hd'
|
||||
},
|
||||
{
|
||||
'setType': 'fc'
|
||||
},
|
||||
{
|
||||
'setType': 'ey'
|
||||
},
|
||||
{
|
||||
'setType': 'hr'
|
||||
},
|
||||
{
|
||||
'setType': 'hrb'
|
||||
},
|
||||
{
|
||||
'setType': 'lh'
|
||||
},
|
||||
{
|
||||
'setType': 'lhs'
|
||||
},
|
||||
{
|
||||
'setType': 'ls'
|
||||
},
|
||||
{
|
||||
'setType': 'lc'
|
||||
},
|
||||
{
|
||||
'setType': 'ea'
|
||||
},
|
||||
{
|
||||
'setType': 'ca'
|
||||
},
|
||||
{
|
||||
'setType': 'fa'
|
||||
},
|
||||
{
|
||||
'setType': 'ha'
|
||||
},
|
||||
{
|
||||
'setType': 'he'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'id': 'head',
|
||||
'activeParts': [
|
||||
{
|
||||
'setType': 'hd'
|
||||
},
|
||||
{
|
||||
'setType': 'fc'
|
||||
},
|
||||
{
|
||||
'setType': 'ey'
|
||||
},
|
||||
{
|
||||
'setType': 'hr'
|
||||
},
|
||||
{
|
||||
'setType': 'hrb'
|
||||
},
|
||||
{
|
||||
'setType': 'ea'
|
||||
},
|
||||
{
|
||||
'setType': 'fa'
|
||||
},
|
||||
{
|
||||
'setType': 'ha'
|
||||
},
|
||||
{
|
||||
'setType': 'he'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'id': 'speak',
|
||||
'activeParts': [
|
||||
{
|
||||
'setType': 'hd'
|
||||
},
|
||||
{
|
||||
'setType': 'hr'
|
||||
},
|
||||
{
|
||||
'setType': 'hrb'
|
||||
},
|
||||
{
|
||||
'setType': 'fc'
|
||||
},
|
||||
{
|
||||
'setType': 'fa'
|
||||
},
|
||||
{
|
||||
'setType': 'ha'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'id': 'gesture',
|
||||
'activeParts': [
|
||||
{
|
||||
'setType': 'ey'
|
||||
},
|
||||
{
|
||||
'setType': 'fc'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'id': 'eye',
|
||||
'activeParts': [
|
||||
{
|
||||
'setType': 'ey'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'id': 'handRight',
|
||||
'activeParts': [
|
||||
{
|
||||
'setType': 'rh'
|
||||
},
|
||||
{
|
||||
'setType': 'rhs'
|
||||
},
|
||||
{
|
||||
'setType': 'rs'
|
||||
},
|
||||
{
|
||||
'setType': 'rc'
|
||||
},
|
||||
{
|
||||
'setType': 'ri'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'id': 'handRightAndHead',
|
||||
'activeParts': [
|
||||
{
|
||||
'setType': 'rh'
|
||||
},
|
||||
{
|
||||
'setType': 'rhs'
|
||||
},
|
||||
{
|
||||
'setType': 'rs'
|
||||
},
|
||||
{
|
||||
'setType': 'rc'
|
||||
},
|
||||
{
|
||||
'setType': 'ri'
|
||||
},
|
||||
{
|
||||
'setType': 'ey'
|
||||
},
|
||||
{
|
||||
'setType': 'fc'
|
||||
},
|
||||
{
|
||||
'setType': 'hd'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'id': 'handLeft',
|
||||
'activeParts': [
|
||||
{
|
||||
'setType': 'lh'
|
||||
},
|
||||
{
|
||||
'setType': 'lhs'
|
||||
},
|
||||
{
|
||||
'setType': 'ls'
|
||||
},
|
||||
{
|
||||
'setType': 'lc'
|
||||
},
|
||||
{
|
||||
'setType': 'li'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'id': 'walk',
|
||||
'activeParts': [
|
||||
{
|
||||
'setType': 'bd'
|
||||
},
|
||||
{
|
||||
'setType': 'bds'
|
||||
},
|
||||
{
|
||||
'setType': 'ss'
|
||||
},
|
||||
{
|
||||
'setType': 'lg'
|
||||
},
|
||||
{
|
||||
'setType': 'lh'
|
||||
},
|
||||
{
|
||||
'setType': 'lhs'
|
||||
},
|
||||
{
|
||||
'setType': 'rh'
|
||||
},
|
||||
{
|
||||
'setType': 'rhs'
|
||||
},
|
||||
{
|
||||
'setType': 'ls'
|
||||
},
|
||||
{
|
||||
'setType': 'lc'
|
||||
},
|
||||
{
|
||||
'setType': 'rs'
|
||||
},
|
||||
{
|
||||
'setType': 'rc'
|
||||
},
|
||||
{
|
||||
'setType': 'sh'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'id': 'sit',
|
||||
'activeParts': [
|
||||
{
|
||||
'setType': 'bd'
|
||||
},
|
||||
{
|
||||
'setType': 'bds'
|
||||
},
|
||||
{
|
||||
'setType': 'ss'
|
||||
},
|
||||
{
|
||||
'setType': 'lg'
|
||||
},
|
||||
{
|
||||
'setType': 'sh'
|
||||
},
|
||||
{
|
||||
'setType': 'cc'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'id': 'itemRight',
|
||||
'activeParts': [
|
||||
{
|
||||
'setType': 'ri'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,287 @@
|
||||
import { IAvatarImage } from '@nitrots/api';
|
||||
import { Vector3d } from '@nitrots/utils';
|
||||
import { Matrix4x4 } from '@nitrots/utils/src/Matrix4x4';
|
||||
import { AvatarCanvas } from '../structure';
|
||||
import { AvatarSet } from './AvatarSet';
|
||||
import { GeometryBodyPart } from './GeometryBodyPart';
|
||||
|
||||
export class AvatarModelGeometry
|
||||
{
|
||||
private _camera: Vector3d;
|
||||
private _avatarSet: AvatarSet;
|
||||
private _geometryTypes: Map<string, Map<string, GeometryBodyPart>>;
|
||||
private _itemIdToBodyPartMap: Map<string, Map<string, GeometryBodyPart>>;
|
||||
private _transformation: Matrix4x4;
|
||||
private _canvases: Map<string, Map<string, AvatarCanvas>>;
|
||||
|
||||
constructor(k: any)
|
||||
{
|
||||
this._camera = new Vector3d(0, 0, 10);
|
||||
this._avatarSet = new AvatarSet(k.avatarSets[0]);
|
||||
this._geometryTypes = new Map();
|
||||
this._itemIdToBodyPartMap = new Map();
|
||||
this._transformation = new Matrix4x4();
|
||||
this._canvases = new Map();
|
||||
|
||||
const camera = k.camera;
|
||||
|
||||
if(camera)
|
||||
{
|
||||
this._camera.x = parseFloat(camera.x);
|
||||
this._camera.y = parseFloat(camera.y);
|
||||
this._camera.z = parseFloat(camera.z);
|
||||
}
|
||||
|
||||
if(k.canvases && (k.canvases.length > 0))
|
||||
{
|
||||
for(const canvas of k.canvases)
|
||||
{
|
||||
if(!canvas) continue;
|
||||
|
||||
const scale = canvas.scale;
|
||||
const geometries = new Map();
|
||||
|
||||
if(canvas.geometries && (canvas.geometries.length > 0))
|
||||
{
|
||||
for(const geometry of canvas.geometries)
|
||||
{
|
||||
if(!geometry) continue;
|
||||
|
||||
const avatarCanvas = new AvatarCanvas(geometry, scale);
|
||||
|
||||
geometries.set(avatarCanvas.id, avatarCanvas);
|
||||
}
|
||||
}
|
||||
|
||||
this._canvases.set(scale, geometries);
|
||||
}
|
||||
}
|
||||
|
||||
if(k.types && (k.types.length > 0))
|
||||
{
|
||||
for(const type of k.types)
|
||||
{
|
||||
if(!type) continue;
|
||||
|
||||
const bodyParts: Map<string, GeometryBodyPart> = new Map();
|
||||
const itemIds: Map<string, GeometryBodyPart> = new Map();
|
||||
|
||||
if(type.bodyParts && (type.bodyParts.length > 0))
|
||||
{
|
||||
for(const bodyPart of type.bodyParts)
|
||||
{
|
||||
if(!bodyPart) continue;
|
||||
|
||||
const geometryBodyPart = new GeometryBodyPart(bodyPart);
|
||||
|
||||
bodyParts.set(geometryBodyPart.id, geometryBodyPart);
|
||||
|
||||
for(const part of geometryBodyPart.getPartIds(null))
|
||||
{
|
||||
itemIds.set(part, geometryBodyPart);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this._geometryTypes.set(type.id, bodyParts);
|
||||
this._itemIdToBodyPartMap.set(type.id, itemIds);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public removeDynamicItems(k: IAvatarImage): void
|
||||
{
|
||||
for(const geometry of this._geometryTypes.values())
|
||||
{
|
||||
if(!geometry) continue;
|
||||
|
||||
for(const part of geometry.values())
|
||||
{
|
||||
if(!part) continue;
|
||||
|
||||
part.removeDynamicParts(k);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public getBodyPartIdsInAvatarSet(k: string): string[]
|
||||
{
|
||||
const avatarSet = this._avatarSet.findAvatarSet(k);
|
||||
|
||||
if(!avatarSet) return [];
|
||||
|
||||
return avatarSet.getBodyParts();
|
||||
}
|
||||
|
||||
public isMainAvatarSet(k: string): boolean
|
||||
{
|
||||
const avatarSet = this._avatarSet.findAvatarSet(k);
|
||||
|
||||
if(!avatarSet) return false;
|
||||
|
||||
return avatarSet.isMain;
|
||||
}
|
||||
|
||||
public getCanvas(k: string, _arg_2: string): AvatarCanvas
|
||||
{
|
||||
const canvas = this._canvases.get(k);
|
||||
|
||||
if(!canvas) return null;
|
||||
|
||||
return (canvas.get(_arg_2) || null);
|
||||
}
|
||||
|
||||
private typeExists(k: string): boolean
|
||||
{
|
||||
const existing = this._geometryTypes.get(k);
|
||||
|
||||
if(existing) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private hasBodyPart(k: string, _arg_2: string): boolean
|
||||
{
|
||||
if(this.typeExists(k))
|
||||
{
|
||||
const existing = this._geometryTypes.get(k);
|
||||
|
||||
if(existing && existing.get(_arg_2)) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private getBodyPartIDs(k: string): string[]
|
||||
{
|
||||
const parts = this.getBodyPartsOfType(k);
|
||||
|
||||
const types = [];
|
||||
|
||||
if(parts)
|
||||
{
|
||||
for(const part of parts.values())
|
||||
{
|
||||
if(!part) continue;
|
||||
|
||||
types.push(part.id);
|
||||
}
|
||||
}
|
||||
|
||||
return types;
|
||||
}
|
||||
|
||||
private getBodyPartsOfType(k: string): Map<string, GeometryBodyPart>
|
||||
{
|
||||
if(this.typeExists(k)) return this._geometryTypes.get(k);
|
||||
|
||||
return new Map();
|
||||
}
|
||||
|
||||
public getBodyPart(k: string, _arg_2: string): GeometryBodyPart
|
||||
{
|
||||
return (this.getBodyPartsOfType(k).get(_arg_2) || null);
|
||||
}
|
||||
|
||||
public getBodyPartOfItem(k: string, _arg_2: string, _arg_3: IAvatarImage): GeometryBodyPart
|
||||
{
|
||||
const itemIds = this._itemIdToBodyPartMap.get(k);
|
||||
|
||||
if(itemIds)
|
||||
{
|
||||
const part = itemIds.get(_arg_2);
|
||||
|
||||
if(part) return part;
|
||||
|
||||
const parts = this.getBodyPartsOfType(k);
|
||||
|
||||
if(parts)
|
||||
{
|
||||
for(const part of parts.values())
|
||||
{
|
||||
if(!part) continue;
|
||||
|
||||
if(part.hasPart(_arg_2, _arg_3)) return part;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private getBodyPartsInAvatarSet(k: Map<string, GeometryBodyPart>, _arg_2: string): GeometryBodyPart[]
|
||||
{
|
||||
const parts = this.getBodyPartIdsInAvatarSet(_arg_2);
|
||||
const geometryParts = [];
|
||||
|
||||
for(const part of parts)
|
||||
{
|
||||
if(!part) continue;
|
||||
|
||||
const bodyPart = k.get(part);
|
||||
|
||||
if(bodyPart)
|
||||
{
|
||||
geometryParts.push(bodyPart);
|
||||
}
|
||||
}
|
||||
|
||||
return geometryParts;
|
||||
}
|
||||
|
||||
public getBodyPartsAtAngle(k: string, _arg_2: number, _arg_3: string): string[]
|
||||
{
|
||||
if(!_arg_3) return [];
|
||||
|
||||
const geometryParts = this.getBodyPartsOfType(_arg_3);
|
||||
const parts = this.getBodyPartsInAvatarSet(geometryParts, k);
|
||||
const sets: [number, GeometryBodyPart][] = [];
|
||||
const ids: string[] = [];
|
||||
|
||||
this._transformation = Matrix4x4.getYRotationMatrix(_arg_2);
|
||||
|
||||
for(const part of parts.values())
|
||||
{
|
||||
if(!part) continue;
|
||||
|
||||
part.applyTransform(this._transformation);
|
||||
|
||||
sets.push([part.getDistance(this._camera), part]);
|
||||
}
|
||||
|
||||
sets.sort((a, b) =>
|
||||
{
|
||||
const partA = a[0];
|
||||
const partB = b[0];
|
||||
|
||||
if(partA < partB) return -1;
|
||||
|
||||
if(partA > partB) return 1;
|
||||
|
||||
return 0;
|
||||
});
|
||||
|
||||
for(const set of sets)
|
||||
{
|
||||
if(!set) continue;
|
||||
|
||||
ids.push(set[1].id);
|
||||
}
|
||||
|
||||
return ids;
|
||||
}
|
||||
|
||||
public getParts(k: string, _arg_2: string, _arg_3: number, _arg_4: any[], _arg_5: IAvatarImage): string[]
|
||||
{
|
||||
if(this.hasBodyPart(k, _arg_2))
|
||||
{
|
||||
const part = this.getBodyPartsOfType(k).get(_arg_2);
|
||||
|
||||
this._transformation = Matrix4x4.getYRotationMatrix(_arg_3);
|
||||
|
||||
return part.getParts(this._transformation, this._camera, _arg_4, _arg_5);
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
export class AvatarSet
|
||||
{
|
||||
private _id: string;
|
||||
private _isMain: boolean;
|
||||
private _avatarSets: Map<string, AvatarSet>;
|
||||
private _bodyParts: string[];
|
||||
private _allBodyParts: string[];
|
||||
|
||||
constructor(k: any)
|
||||
{
|
||||
this._id = k.id;
|
||||
this._isMain = k.main || false;
|
||||
this._avatarSets = new Map();
|
||||
this._bodyParts = [];
|
||||
this._allBodyParts = [];
|
||||
|
||||
if(k.avatarSets && (k.avatarSets.length > 0))
|
||||
{
|
||||
for(const avatarSet of k.avatarSets)
|
||||
{
|
||||
if(!avatarSet) continue;
|
||||
|
||||
const set = new AvatarSet(avatarSet);
|
||||
|
||||
this._avatarSets.set(set.id, set);
|
||||
}
|
||||
}
|
||||
|
||||
if(k.bodyParts && (k.bodyParts.length > 0))
|
||||
{
|
||||
for(const bodyPart of k.bodyParts)
|
||||
{
|
||||
if(!bodyPart) continue;
|
||||
|
||||
this._bodyParts.push(bodyPart.id);
|
||||
}
|
||||
}
|
||||
|
||||
let bodyParts = this._bodyParts.concat();
|
||||
|
||||
for(const avatarSet of this._avatarSets.values())
|
||||
{
|
||||
if(!avatarSet) continue;
|
||||
|
||||
bodyParts = bodyParts.concat(avatarSet.getBodyParts());
|
||||
}
|
||||
|
||||
this._allBodyParts = bodyParts;
|
||||
}
|
||||
|
||||
public findAvatarSet(k: string): AvatarSet
|
||||
{
|
||||
if(k === this._id) return this;
|
||||
|
||||
for(const avatarSet of this._avatarSets.values())
|
||||
{
|
||||
if(!avatarSet) continue;
|
||||
|
||||
if(!avatarSet.findAvatarSet(k)) continue;
|
||||
|
||||
return avatarSet;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public getBodyParts(): string[]
|
||||
{
|
||||
return this._allBodyParts.concat();
|
||||
}
|
||||
|
||||
public get id(): string
|
||||
{
|
||||
return this._id;
|
||||
}
|
||||
|
||||
public get isMain(): boolean
|
||||
{
|
||||
if(this._isMain) return true;
|
||||
|
||||
for(const avatarSet of this._avatarSets.values())
|
||||
{
|
||||
if(!avatarSet) continue;
|
||||
|
||||
if(!avatarSet.isMain) continue;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,192 @@
|
||||
import { IAvatarImage } from '@nitrots/api';
|
||||
import { Matrix4x4, Node3D, Vector3d } from '@nitrots/utils';
|
||||
import { GeometryItem } from './GeometryItem';
|
||||
|
||||
export class GeometryBodyPart extends Node3D
|
||||
{
|
||||
private _id: string;
|
||||
private _radius: number;
|
||||
private _parts: Map<string, GeometryItem>;
|
||||
private _dynamicParts: Map<IAvatarImage, { [index: string]: GeometryItem }>;
|
||||
|
||||
constructor(k: any)
|
||||
{
|
||||
super(parseFloat(k.x), parseFloat(k.y), parseFloat(k.z));
|
||||
|
||||
this._id = k.id;
|
||||
this._radius = parseFloat(k.radius);
|
||||
this._parts = new Map();
|
||||
this._dynamicParts = new Map();
|
||||
|
||||
if(k.items && (k.items.length > 0))
|
||||
{
|
||||
for(const item of k.items)
|
||||
{
|
||||
if(!item) continue;
|
||||
|
||||
const geometryItem = new GeometryItem(item);
|
||||
|
||||
this._parts.set(geometryItem.id, geometryItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public getDynamicParts(k: IAvatarImage): GeometryItem[]
|
||||
{
|
||||
const existing = this._dynamicParts.get(k);
|
||||
const parts: GeometryItem[] = [];
|
||||
|
||||
if(existing)
|
||||
{
|
||||
for(const index in existing)
|
||||
{
|
||||
const item = existing[index];
|
||||
|
||||
if(!item) continue;
|
||||
|
||||
parts.push(item);
|
||||
}
|
||||
}
|
||||
|
||||
return parts;
|
||||
}
|
||||
|
||||
public getPartIds(k: IAvatarImage): string[]
|
||||
{
|
||||
const ids: string[] = [];
|
||||
|
||||
for(const part of this._parts.values())
|
||||
{
|
||||
if(!part) continue;
|
||||
|
||||
ids.push(part.id);
|
||||
}
|
||||
|
||||
if(k)
|
||||
{
|
||||
const existing = this._dynamicParts.get(k);
|
||||
|
||||
if(existing)
|
||||
{
|
||||
for(const index in existing)
|
||||
{
|
||||
const part = existing[index];
|
||||
|
||||
if(!part) continue;
|
||||
|
||||
ids.push(part.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ids;
|
||||
}
|
||||
|
||||
public removeDynamicParts(k: IAvatarImage): boolean
|
||||
{
|
||||
this._dynamicParts.delete(k);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public addPart(k: any, _arg_2: IAvatarImage): boolean
|
||||
{
|
||||
if(this.hasPart(k.id, _arg_2)) return false;
|
||||
|
||||
let existing = this._dynamicParts.get(_arg_2);
|
||||
|
||||
if(!existing)
|
||||
{
|
||||
existing = {};
|
||||
|
||||
this._dynamicParts.set(_arg_2, existing);
|
||||
}
|
||||
|
||||
existing[k.id] = new GeometryItem(k, true);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public hasPart(k: string, _arg_2: IAvatarImage): boolean
|
||||
{
|
||||
let existingPart = (this._parts.get(k) || null);
|
||||
|
||||
if(!existingPart && (this._dynamicParts.get(_arg_2) !== undefined))
|
||||
{
|
||||
existingPart = (this._dynamicParts.get(_arg_2)[k] || null);
|
||||
}
|
||||
|
||||
return (existingPart !== null);
|
||||
}
|
||||
|
||||
public getParts(k: Matrix4x4, _arg_2: Vector3d, _arg_3: any[], _arg_4: IAvatarImage): string[]
|
||||
{
|
||||
const parts: [number, GeometryItem][] = [];
|
||||
|
||||
for(const part of this._parts.values())
|
||||
{
|
||||
if(!part) continue;
|
||||
|
||||
part.applyTransform(k);
|
||||
|
||||
parts.push([part.getDistance(_arg_2), part]);
|
||||
}
|
||||
|
||||
const existingDynamic = this._dynamicParts.get(_arg_4);
|
||||
|
||||
if(existingDynamic)
|
||||
{
|
||||
for(const index in existingDynamic)
|
||||
{
|
||||
const part = existingDynamic[index];
|
||||
|
||||
if(!part) continue;
|
||||
|
||||
part.applyTransform(k);
|
||||
|
||||
parts.push([part.getDistance(_arg_2), part]);
|
||||
}
|
||||
}
|
||||
|
||||
parts.sort((a, b) =>
|
||||
{
|
||||
const partA = a[0];
|
||||
const partB = b[0];
|
||||
|
||||
if(partA < partB) return -1;
|
||||
|
||||
if(partA > partB) return 1;
|
||||
|
||||
return 0;
|
||||
});
|
||||
|
||||
const partIds: string[] = [];
|
||||
|
||||
for(const part of parts)
|
||||
{
|
||||
if(!part) continue;
|
||||
|
||||
partIds.push(part[1].id);
|
||||
}
|
||||
|
||||
return partIds;
|
||||
}
|
||||
|
||||
public getDistance(k: Vector3d): number
|
||||
{
|
||||
const _local_2 = Math.abs(((k.z - this.transformedLocation.z) - this._radius));
|
||||
const _local_3 = Math.abs(((k.z - this.transformedLocation.z) + this._radius));
|
||||
|
||||
return Math.min(_local_2, _local_3);
|
||||
}
|
||||
|
||||
public get id(): string
|
||||
{
|
||||
return this._id;
|
||||
}
|
||||
|
||||
public get radius(): number
|
||||
{
|
||||
return this._radius;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
import { Node3D, Vector3d } from '@nitrots/utils';
|
||||
|
||||
export class GeometryItem extends Node3D
|
||||
{
|
||||
private _id: string;
|
||||
private _radius: number;
|
||||
private _normal: Vector3d;
|
||||
private _isDoubleSided: boolean;
|
||||
private _isDynamic: boolean;
|
||||
|
||||
constructor(k: any, _arg_2: boolean = false)
|
||||
{
|
||||
super(parseFloat(k.x), parseFloat(k.y), parseFloat(k.z));
|
||||
|
||||
this._id = k.id;
|
||||
this._radius = parseFloat(k.radius);
|
||||
this._normal = new Vector3d(parseFloat(k.nx), parseFloat(k.ny), parseFloat(k.nz));
|
||||
this._isDoubleSided = k.double || false;
|
||||
this._isDynamic = _arg_2;
|
||||
}
|
||||
|
||||
public getDistance(k: Vector3d): number
|
||||
{
|
||||
const _local_2 = Math.abs(((k.z - this.transformedLocation.z) - this._radius));
|
||||
const _local_3 = Math.abs(((k.z - this.transformedLocation.z) + this._radius));
|
||||
|
||||
return Math.min(_local_2, _local_3);
|
||||
}
|
||||
|
||||
public get id(): string
|
||||
{
|
||||
return this._id;
|
||||
}
|
||||
|
||||
public get normal(): Vector3d
|
||||
{
|
||||
return this._normal;
|
||||
}
|
||||
|
||||
public get isDoubleSided(): boolean
|
||||
{
|
||||
return this._isDoubleSided;
|
||||
}
|
||||
|
||||
public toString(): string
|
||||
{
|
||||
return ((((this._id + ': ') + this.location) + ' - ') + this.transformedLocation);
|
||||
}
|
||||
|
||||
public get isDynamic(): boolean
|
||||
{
|
||||
return this._isDynamic;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
export * from './AvatarModelGeometry';
|
||||
export * from './AvatarSet';
|
||||
export * from './GeometryBodyPart';
|
||||
export * from './GeometryItem';
|
||||
@@ -0,0 +1,25 @@
|
||||
export * from './AvatarAssetDownloadLibrary';
|
||||
export * from './AvatarAssetDownloadManager';
|
||||
export * from './AvatarFigureContainer';
|
||||
export * from './AvatarImage';
|
||||
export * from './AvatarImageBodyPartContainer';
|
||||
export * from './AvatarImagePartContainer';
|
||||
export * from './AvatarRenderManager';
|
||||
export * from './AvatarStructure';
|
||||
export * from './EffectAssetDownloadLibrary';
|
||||
export * from './EffectAssetDownloadManager';
|
||||
export * from './FigureDataContainer';
|
||||
export * from './GetAvatarRenderManager';
|
||||
export * from './PlaceHolderAvatarImage';
|
||||
export * from './actions';
|
||||
export * from './alias';
|
||||
export * from './animation';
|
||||
export * from './cache';
|
||||
export * from './data/HabboAvatarAnimations';
|
||||
export * from './data/HabboAvatarGeometry';
|
||||
export * from './data/HabboAvatarPartSets';
|
||||
export * from './geometry';
|
||||
export * from './structure';
|
||||
export * from './structure/animation';
|
||||
export * from './structure/figure';
|
||||
export * from './structure/parts';
|
||||
@@ -0,0 +1,57 @@
|
||||
import { IActionDefinition, IFigureSetData } from '@nitrots/api';
|
||||
import { AnimationAction } from './animation';
|
||||
|
||||
export class AvatarAnimationData implements IFigureSetData
|
||||
{
|
||||
private _actions: Map<string, AnimationAction>;
|
||||
|
||||
constructor()
|
||||
{
|
||||
this._actions = new Map();
|
||||
}
|
||||
|
||||
public parse(data: any): boolean
|
||||
{
|
||||
if(data && (data.length > 0))
|
||||
{
|
||||
for(const animation of data)
|
||||
{
|
||||
if(!animation) continue;
|
||||
|
||||
const newAnimation = new AnimationAction(animation);
|
||||
|
||||
this._actions.set(newAnimation.id, newAnimation);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public appendJSON(k: any): boolean
|
||||
{
|
||||
for(const _local_2 of k.action)
|
||||
{
|
||||
this._actions.set(_local_2.id, new AnimationAction(_local_2));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public getAction(action: IActionDefinition): AnimationAction
|
||||
{
|
||||
const existing = this._actions.get(action.id);
|
||||
|
||||
if(!existing) return null;
|
||||
|
||||
return existing;
|
||||
}
|
||||
|
||||
public getFrameCount(k: IActionDefinition): number
|
||||
{
|
||||
const animationAction = this.getAction(k);
|
||||
|
||||
if(!animationAction) return 0;
|
||||
|
||||
return animationAction.frameCount;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
import { AvatarScaleType } from '@nitrots/api';
|
||||
import { Point } from 'pixi.js';
|
||||
|
||||
export class AvatarCanvas
|
||||
{
|
||||
private _id: string;
|
||||
private _width: number;
|
||||
private _height: number;
|
||||
private _offset: Point;
|
||||
private _regPoint: Point;
|
||||
|
||||
constructor(k: any, _arg_2: string)
|
||||
{
|
||||
this._id = k.id;
|
||||
this._width = k.width;
|
||||
this._height = k.height;
|
||||
this._offset = new Point(k.dx, k.dy);
|
||||
|
||||
if(_arg_2 == AvatarScaleType.LARGE) this._regPoint = new Point(((this._width - 64) / 2), 0);
|
||||
else this._regPoint = new Point(((this._width - 32) / 2), 0);
|
||||
}
|
||||
|
||||
public get width(): number
|
||||
{
|
||||
return this._width;
|
||||
}
|
||||
|
||||
public get height(): number
|
||||
{
|
||||
return this._height;
|
||||
}
|
||||
|
||||
public get offset(): Point
|
||||
{
|
||||
return this._offset;
|
||||
}
|
||||
|
||||
public get id(): string
|
||||
{
|
||||
return this._id;
|
||||
}
|
||||
|
||||
public get regPoint(): Point
|
||||
{
|
||||
return this._regPoint;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
import { IFigureData, IFigurePartSet, IFigureSetData, IPalette, ISetType, IStructureData } from '@nitrots/api';
|
||||
import { Palette, SetType } from './figure';
|
||||
|
||||
export class FigureSetData implements IFigureSetData, IStructureData
|
||||
{
|
||||
private _palettes: Map<string, Palette>;
|
||||
private _setTypes: Map<string, SetType>;
|
||||
|
||||
constructor()
|
||||
{
|
||||
this._palettes = new Map();
|
||||
this._setTypes = new Map();
|
||||
}
|
||||
|
||||
public dispose(): void
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public parse(data: IFigureData): boolean
|
||||
{
|
||||
if(!data) return false;
|
||||
|
||||
for(const palette of data.palettes)
|
||||
{
|
||||
const newPalette = new Palette(palette);
|
||||
|
||||
if(!newPalette) continue;
|
||||
|
||||
this._palettes.set(newPalette.id.toString(), newPalette);
|
||||
}
|
||||
|
||||
for(const set of data.setTypes)
|
||||
{
|
||||
const newSet = new SetType(set);
|
||||
|
||||
if(!newSet) continue;
|
||||
|
||||
this._setTypes.set(newSet.type, newSet);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public injectJSON(data: IFigureData): void
|
||||
{
|
||||
for(const setType of data.setTypes)
|
||||
{
|
||||
const existingSetType = this._setTypes.get(setType.type);
|
||||
|
||||
if(existingSetType) existingSetType.cleanUp(setType);
|
||||
else this._setTypes.set(setType.type, new SetType(setType));
|
||||
}
|
||||
|
||||
this.appendJSON(data);
|
||||
}
|
||||
|
||||
public appendJSON(data: IFigureData): boolean
|
||||
{
|
||||
if(!data) return false;
|
||||
|
||||
for(const palette of data.palettes)
|
||||
{
|
||||
const id = palette.id.toString();
|
||||
const existingPalette = this._palettes.get(id);
|
||||
|
||||
if(!existingPalette) this._palettes.set(id, new Palette(palette));
|
||||
else existingPalette.append(palette);
|
||||
}
|
||||
|
||||
for(const setType of data.setTypes)
|
||||
{
|
||||
const type = setType.type;
|
||||
const existingSetType = this._setTypes.get(type);
|
||||
|
||||
if(!existingSetType) this._setTypes.set(type, new SetType(setType));
|
||||
else existingSetType.append(setType);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public getMandatorySetTypeIds(k: string, _arg_2: number): string[]
|
||||
{
|
||||
const types: string[] = [];
|
||||
|
||||
for(const set of this._setTypes.values())
|
||||
{
|
||||
if(!set || !set.isMandatory(k, _arg_2)) continue;
|
||||
|
||||
types.push(set.type);
|
||||
}
|
||||
|
||||
return types;
|
||||
}
|
||||
|
||||
public getDefaultPartSet(type: string, gender: string): IFigurePartSet
|
||||
{
|
||||
const setType = this._setTypes.get(type);
|
||||
|
||||
if(!setType) return null;
|
||||
|
||||
return setType.getDefaultPartSet(gender);
|
||||
}
|
||||
|
||||
public getSetType(k: string): ISetType
|
||||
{
|
||||
return (this._setTypes.get(k) || null);
|
||||
}
|
||||
|
||||
public getPalette(k: number): IPalette
|
||||
{
|
||||
return (this._palettes.get(k.toString()) || null);
|
||||
}
|
||||
|
||||
public getFigurePartSet(k: number): IFigurePartSet
|
||||
{
|
||||
for(const set of this._setTypes.values())
|
||||
{
|
||||
const partSet = set.getPartSet(k);
|
||||
|
||||
if(!partSet) continue;
|
||||
|
||||
return partSet;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
import { IActionDefinition, IFigureSetData } from '@nitrots/api';
|
||||
import { ActionDefinition } from '../actions';
|
||||
import { ActivePartSet, PartDefinition } from './parts';
|
||||
|
||||
export class PartSetsData implements IFigureSetData
|
||||
{
|
||||
private _parts: Map<string, PartDefinition>;
|
||||
private _activePartSets: Map<string, ActivePartSet>;
|
||||
|
||||
constructor()
|
||||
{
|
||||
this._parts = new Map();
|
||||
this._activePartSets = new Map();
|
||||
}
|
||||
|
||||
public parse(data: any): boolean
|
||||
{
|
||||
if(data.partSet && (data.partSet.length > 0))
|
||||
{
|
||||
for(const part of data.partSet)
|
||||
{
|
||||
if(!part) continue;
|
||||
|
||||
this._parts.set(part.setType, new PartDefinition(part));
|
||||
}
|
||||
}
|
||||
|
||||
if(data.activePartSets && (data.activePartSets.length > 0))
|
||||
{
|
||||
for(const activePart of data.activePartSets)
|
||||
{
|
||||
if(!activePart) continue;
|
||||
|
||||
this._activePartSets.set(activePart.id, new ActivePartSet(activePart));
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public appendJSON(data: any): boolean
|
||||
{
|
||||
if(data.partSet && (data.partSet.length > 0))
|
||||
{
|
||||
for(const part of data.partSet)
|
||||
{
|
||||
if(!part) continue;
|
||||
|
||||
this._parts.set(part.setType, new PartDefinition(part));
|
||||
}
|
||||
}
|
||||
|
||||
if(data.activePartSets && (data.activePartSets.length > 0))
|
||||
{
|
||||
for(const activePart of data.activePartSets)
|
||||
{
|
||||
if(!activePart) continue;
|
||||
|
||||
this._activePartSets.set(activePart.id, new ActivePartSet(activePart));
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public getActiveParts(k: IActionDefinition): string[]
|
||||
{
|
||||
const activePartSet = this._activePartSets.get(k.activePartSet);
|
||||
|
||||
if(!activePartSet) return [];
|
||||
|
||||
return activePartSet.parts;
|
||||
}
|
||||
|
||||
public getPartDefinition(part: string): PartDefinition
|
||||
{
|
||||
const existing = this._parts.get(part);
|
||||
|
||||
if(!existing) return null;
|
||||
|
||||
return existing;
|
||||
}
|
||||
|
||||
public addPartDefinition(k: any): PartDefinition
|
||||
{
|
||||
const _local_2 = k.setType as string;
|
||||
|
||||
let existing = this._parts.get(_local_2);
|
||||
|
||||
if(!existing)
|
||||
{
|
||||
existing = new PartDefinition(k);
|
||||
|
||||
this._parts.set(_local_2, existing);
|
||||
}
|
||||
|
||||
return existing;
|
||||
}
|
||||
|
||||
public getActivePartSet(k: ActionDefinition): ActivePartSet
|
||||
{
|
||||
const existing = this._activePartSets.get(k.activePartSet);
|
||||
|
||||
if(!existing) return null;
|
||||
|
||||
return existing;
|
||||
}
|
||||
|
||||
public get parts(): Map<string, PartDefinition>
|
||||
{
|
||||
return this._parts;
|
||||
}
|
||||
|
||||
public get activePartSets(): Map<string, ActivePartSet>
|
||||
{
|
||||
return this._activePartSets;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
import { Point } from 'pixi.js';
|
||||
import { AnimationActionPart } from './AnimationActionPart';
|
||||
|
||||
export class AnimationAction
|
||||
{
|
||||
public static DEFAULT_OFFSET: Point = new Point(0, 0);
|
||||
|
||||
private _id: string;
|
||||
private _actionParts: Map<string, AnimationActionPart>;
|
||||
private _bodyPartOffsets: Map<number, Map<number, Map<string, Point>>>;
|
||||
private _frameCount: number;
|
||||
private _frameIndexes: number[];
|
||||
|
||||
constructor(data: any)
|
||||
{
|
||||
this._id = data.id;
|
||||
this._actionParts = new Map();
|
||||
this._bodyPartOffsets = new Map();
|
||||
this._frameCount = 0;
|
||||
this._frameIndexes = [];
|
||||
|
||||
if(data.parts && (data.parts.length > 0))
|
||||
{
|
||||
for(const part of data.parts)
|
||||
{
|
||||
if(!part) continue;
|
||||
|
||||
const newPart = new AnimationActionPart(part);
|
||||
|
||||
this._actionParts.set(part.setType, newPart);
|
||||
|
||||
this._frameCount = Math.max(this._frameCount, newPart.frames.length);
|
||||
}
|
||||
}
|
||||
|
||||
if(data.offsets && data.offsets.frames && (data.offsets.frames.length > 0))
|
||||
{
|
||||
for(const frame of data.offsets.frames)
|
||||
{
|
||||
if(!frame) continue;
|
||||
|
||||
const frameId = frame.id;
|
||||
|
||||
this._frameCount = Math.max(this._frameCount, frameId);
|
||||
|
||||
const directions: Map<number, Map<string, Point>> = new Map();
|
||||
|
||||
this._bodyPartOffsets.set(frameId, directions);
|
||||
|
||||
if(frame.directions && (frame.directions.length > 0))
|
||||
{
|
||||
for(const direction of frame.directions)
|
||||
{
|
||||
if(!direction) continue;
|
||||
|
||||
const directionId = direction.id;
|
||||
|
||||
const offsets: Map<string, Point> = new Map();
|
||||
|
||||
directions.set(directionId, offsets);
|
||||
|
||||
if(direction.bodyParts && (direction.bodyParts.length > 0))
|
||||
{
|
||||
for(const part of direction.bodyParts)
|
||||
{
|
||||
if(!part) continue;
|
||||
|
||||
const partId = part.id;
|
||||
|
||||
let dx = 0;
|
||||
let dy = 0;
|
||||
|
||||
if(part.dx !== undefined) dx = part.dx;
|
||||
if(part.dy !== undefined) dy = part.dy;
|
||||
|
||||
offsets.set(partId, new Point(dx, dy));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this._frameIndexes.push(frameId);
|
||||
|
||||
if(frame.repeats !== undefined)
|
||||
{
|
||||
let repeats = frame.repeats || 0;
|
||||
|
||||
if(repeats > 1) while(--repeats > 0) this._frameIndexes.push(frameId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public getPart(type: string): AnimationActionPart
|
||||
{
|
||||
if(!type) return null;
|
||||
|
||||
const existing = this._actionParts.get(type);
|
||||
|
||||
if(!existing) return null;
|
||||
|
||||
return existing;
|
||||
}
|
||||
|
||||
public getFrameBodyPartOffset(frameId: number, frameCount: number, partId: string): Point
|
||||
{
|
||||
const frameIndex = (frameCount % this._frameIndexes.length);
|
||||
const frameNumber = this._frameIndexes[frameIndex];
|
||||
const offsets = this._bodyPartOffsets.get(frameNumber);
|
||||
|
||||
if(!offsets) return AnimationAction.DEFAULT_OFFSET;
|
||||
|
||||
const frameOffset = offsets.get(frameId);
|
||||
|
||||
if(!frameOffset) return AnimationAction.DEFAULT_OFFSET;
|
||||
|
||||
const offset = frameOffset.get(partId);
|
||||
|
||||
if(!offset) return AnimationAction.DEFAULT_OFFSET;
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
public get id(): string
|
||||
{
|
||||
return this._id;
|
||||
}
|
||||
|
||||
public get parts(): Map<string, AnimationActionPart>
|
||||
{
|
||||
return this._actionParts;
|
||||
}
|
||||
|
||||
public get frameCount(): number
|
||||
{
|
||||
return this._frameCount;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
import { AvatarAnimationFrame } from './AvatarAnimationFrame';
|
||||
|
||||
export class AnimationActionPart
|
||||
{
|
||||
private _frames: AvatarAnimationFrame[];
|
||||
|
||||
constructor(data: any)
|
||||
{
|
||||
this._frames = [];
|
||||
|
||||
if(data.frames && (data.frames.length > 0))
|
||||
{
|
||||
for(const frame of data.frames)
|
||||
{
|
||||
if(!frame) continue;
|
||||
|
||||
this._frames.push(new AvatarAnimationFrame(frame));
|
||||
|
||||
let repeats = frame.repeats || 0;
|
||||
|
||||
if(repeats > 1) while(--repeats > 0) this._frames.push(this._frames[(this._frames.length - 1)]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public get frames(): AvatarAnimationFrame[]
|
||||
{
|
||||
return this._frames;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
export class AvatarAnimationFrame
|
||||
{
|
||||
private _number: number;
|
||||
private _assetPartDefinition: string;
|
||||
|
||||
constructor(data: any)
|
||||
{
|
||||
this._number = data.number;
|
||||
this._assetPartDefinition = data.assetPartDefinition || null;
|
||||
}
|
||||
|
||||
public get number(): number
|
||||
{
|
||||
return this._number;
|
||||
}
|
||||
|
||||
public get assetPartDefinition(): string
|
||||
{
|
||||
return this._assetPartDefinition;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export * from './AnimationAction';
|
||||
export * from './AnimationActionPart';
|
||||
export * from './AvatarAnimationFrame';
|
||||
@@ -0,0 +1,53 @@
|
||||
import { IFigureDataPart, IFigurePart } from '@nitrots/api';
|
||||
|
||||
export class FigurePart implements IFigurePart
|
||||
{
|
||||
private _id: number;
|
||||
private _type: string;
|
||||
private _breed: number;
|
||||
private _index: number;
|
||||
private _colorLayerIndex: number;
|
||||
private _paletteMapId: number;
|
||||
|
||||
constructor(data: IFigureDataPart)
|
||||
{
|
||||
if(!data) throw new Error('invalid_data');
|
||||
|
||||
this._id = data.id;
|
||||
this._type = data.type;
|
||||
this._index = data.index;
|
||||
this._colorLayerIndex = data.colorindex;
|
||||
this._paletteMapId = -1;
|
||||
this._breed = -1;
|
||||
}
|
||||
|
||||
public get id(): number
|
||||
{
|
||||
return this._id;
|
||||
}
|
||||
|
||||
public get type(): string
|
||||
{
|
||||
return this._type;
|
||||
}
|
||||
|
||||
public get breed(): number
|
||||
{
|
||||
return this._breed;
|
||||
}
|
||||
|
||||
public get index(): number
|
||||
{
|
||||
return this._index;
|
||||
}
|
||||
|
||||
public get colorLayerIndex(): number
|
||||
{
|
||||
return this._colorLayerIndex;
|
||||
}
|
||||
|
||||
public get paletteMap(): number
|
||||
{
|
||||
return this._paletteMapId;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
import { IFigureDataSet, IFigurePart, IFigurePartSet } from '@nitrots/api';
|
||||
import { FigurePart } from './FigurePart';
|
||||
|
||||
export class FigurePartSet implements IFigurePartSet
|
||||
{
|
||||
private _id: number;
|
||||
private _type: string;
|
||||
private _gender: string;
|
||||
private _clubLevel: number;
|
||||
private _isColorable: boolean;
|
||||
private _isSelectable: boolean;
|
||||
private _parts: IFigurePart[];
|
||||
private _hiddenLayers: string[];
|
||||
private _isPreSelectable: boolean;
|
||||
private _isSellable: boolean;
|
||||
|
||||
constructor(type: string, data: IFigureDataSet)
|
||||
{
|
||||
if(!type || !data) throw new Error('invalid_data');
|
||||
|
||||
this._id = data.id;
|
||||
this._type = type;
|
||||
this._gender = data.gender;
|
||||
this._clubLevel = data.club;
|
||||
this._isColorable = data.colorable;
|
||||
this._isSelectable = data.selectable;
|
||||
this._parts = [];
|
||||
this._hiddenLayers = [];
|
||||
this._isPreSelectable = data.preselectable;
|
||||
this._isSellable = data.sellable;
|
||||
|
||||
for(const part of data.parts)
|
||||
{
|
||||
const newPart = new FigurePart(part);
|
||||
const partIndex = this.getPartIndex(newPart);
|
||||
|
||||
if(partIndex !== -1) this._parts.splice(partIndex, 0, newPart);
|
||||
else this._parts.push(newPart);
|
||||
}
|
||||
|
||||
if(data.hiddenLayers)
|
||||
{
|
||||
for(const hiddenLayer of data.hiddenLayers) this._hiddenLayers.push(hiddenLayer.partType);
|
||||
}
|
||||
}
|
||||
|
||||
public dispose(): void
|
||||
{
|
||||
this._parts = null;
|
||||
this._hiddenLayers = null;
|
||||
}
|
||||
|
||||
private getPartIndex(part: FigurePart): number
|
||||
{
|
||||
const totalParts = this._parts.length;
|
||||
|
||||
if(!totalParts) return -1;
|
||||
|
||||
for(let i = 0; i < totalParts; i++)
|
||||
{
|
||||
const existingPart = this._parts[i];
|
||||
|
||||
if(!existingPart) continue;
|
||||
|
||||
if(existingPart.type !== part.type || existingPart.index > part.index) continue;
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
public getPart(k: string, _arg_2: number): IFigurePart
|
||||
{
|
||||
for(const part of this._parts)
|
||||
{
|
||||
if((part.type !== k) || (part.id !== _arg_2)) continue;
|
||||
|
||||
return part;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public get id(): number
|
||||
{
|
||||
return this._id;
|
||||
}
|
||||
|
||||
public get type(): string
|
||||
{
|
||||
return this._type;
|
||||
}
|
||||
|
||||
public get gender(): string
|
||||
{
|
||||
return this._gender;
|
||||
}
|
||||
|
||||
public get clubLevel(): number
|
||||
{
|
||||
return this._clubLevel;
|
||||
}
|
||||
|
||||
public get isColorable(): boolean
|
||||
{
|
||||
return this._isColorable;
|
||||
}
|
||||
|
||||
public get isSelectable(): boolean
|
||||
{
|
||||
return this._isSelectable;
|
||||
}
|
||||
|
||||
public get parts(): IFigurePart[]
|
||||
{
|
||||
return this._parts;
|
||||
}
|
||||
|
||||
public get hiddenLayers(): string[]
|
||||
{
|
||||
return this._hiddenLayers;
|
||||
}
|
||||
|
||||
public get isPreSelectable(): boolean
|
||||
{
|
||||
return this._isPreSelectable;
|
||||
}
|
||||
|
||||
public get isSellable(): boolean
|
||||
{
|
||||
return this._isSellable;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
import { IAdvancedMap, IFigureDataPalette, IPalette, IPartColor } from '@nitrots/api';
|
||||
import { AdvancedMap } from '@nitrots/utils';
|
||||
import { PartColor } from './PartColor';
|
||||
|
||||
export class Palette implements IPalette
|
||||
{
|
||||
private _id: number;
|
||||
private _colors: IAdvancedMap<string, IPartColor>;
|
||||
|
||||
constructor(data: IFigureDataPalette)
|
||||
{
|
||||
if(!data) throw new Error('invalid_data');
|
||||
|
||||
this._id = data.id;
|
||||
this._colors = new AdvancedMap();
|
||||
|
||||
this.append(data);
|
||||
}
|
||||
|
||||
public append(data: IFigureDataPalette): void
|
||||
{
|
||||
for(const color of data.colors)
|
||||
{
|
||||
const newColor = new PartColor(color);
|
||||
|
||||
this._colors.add(color.id.toString(), newColor);
|
||||
}
|
||||
}
|
||||
|
||||
public getColor(id: number): IPartColor
|
||||
{
|
||||
if((id === undefined) || id < 0) return null;
|
||||
|
||||
return (this._colors.getValue(id.toString()) || null);
|
||||
}
|
||||
|
||||
public get id(): number
|
||||
{
|
||||
return this._id;
|
||||
}
|
||||
|
||||
public get colors(): IAdvancedMap<string, IPartColor>
|
||||
{
|
||||
return this._colors;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
import { IFigureDataColor, IPartColor } from '@nitrots/api';
|
||||
|
||||
export class PartColor implements IPartColor
|
||||
{
|
||||
private _id: number;
|
||||
private _index: number;
|
||||
private _clubLevel: number;
|
||||
private _isSelectable: boolean;
|
||||
private _rgb: number;
|
||||
|
||||
constructor(data: IFigureDataColor)
|
||||
{
|
||||
if(!data) throw new Error('invalid_data');
|
||||
|
||||
this._id = data.id;
|
||||
this._index = data.index;
|
||||
this._clubLevel = (data.club || 0);
|
||||
this._isSelectable = data.selectable;
|
||||
this._rgb = parseInt('0x' + data.hexCode, 16);
|
||||
}
|
||||
|
||||
public get id(): number
|
||||
{
|
||||
return this._id;
|
||||
}
|
||||
|
||||
public get index(): number
|
||||
{
|
||||
return this._index;
|
||||
}
|
||||
|
||||
public get clubLevel(): number
|
||||
{
|
||||
return this._clubLevel;
|
||||
}
|
||||
|
||||
public get isSelectable(): boolean
|
||||
{
|
||||
return this._isSelectable;
|
||||
}
|
||||
|
||||
public get rgb(): number
|
||||
{
|
||||
return this._rgb;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
import { IAdvancedMap, IFigureDataSetType, IFigurePartSet, ISetType } from '@nitrots/api';
|
||||
import { AdvancedMap } from '@nitrots/utils';
|
||||
import { FigurePartSet } from './FigurePartSet';
|
||||
|
||||
export class SetType implements ISetType
|
||||
{
|
||||
private _type: string;
|
||||
private _paletteId: number;
|
||||
private _isMandatory: { [index: string]: boolean[] };
|
||||
private _partSets: IAdvancedMap<string, IFigurePartSet>;
|
||||
|
||||
constructor(data: IFigureDataSetType)
|
||||
{
|
||||
if(!data) throw new Error('invalid_data');
|
||||
|
||||
this._type = data.type;
|
||||
this._paletteId = data.paletteId;
|
||||
this._isMandatory = {};
|
||||
this._isMandatory['F'] = [data.mandatory_f_0, data.mandatory_f_1];
|
||||
this._isMandatory['M'] = [data.mandatory_m_0, data.mandatory_m_1];
|
||||
this._partSets = new AdvancedMap();
|
||||
|
||||
this.append(data);
|
||||
}
|
||||
|
||||
public dispose(): void
|
||||
{
|
||||
for(const set of this._partSets.getValues())
|
||||
{
|
||||
const partSet = set as FigurePartSet;
|
||||
|
||||
partSet.dispose();
|
||||
}
|
||||
|
||||
this._partSets = null;
|
||||
}
|
||||
|
||||
public cleanUp(data: IFigureDataSetType): void
|
||||
{
|
||||
for(const set of data.sets)
|
||||
{
|
||||
const setId = set.id.toString();
|
||||
const partSet = (this._partSets.getValue(setId) as FigurePartSet);
|
||||
|
||||
if(partSet)
|
||||
{
|
||||
partSet.dispose();
|
||||
|
||||
this._partSets.remove(setId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public append(setType: IFigureDataSetType): void
|
||||
{
|
||||
if(!setType || !setType.sets) return;
|
||||
|
||||
for(const set of setType.sets) this._partSets.add(set.id.toString(), new FigurePartSet(this._type, set));
|
||||
}
|
||||
|
||||
public getDefaultPartSet(gender: string): IFigurePartSet
|
||||
{
|
||||
for(const set of this._partSets.getValues())
|
||||
{
|
||||
if(!set) continue;
|
||||
|
||||
if((set.clubLevel === 0) && ((set.gender === gender) || (set.gender === 'U'))) return set;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public getPartSet(k: number): IFigurePartSet
|
||||
{
|
||||
return this._partSets.getValue(k.toString());
|
||||
}
|
||||
|
||||
public get type(): string
|
||||
{
|
||||
return this._type;
|
||||
}
|
||||
|
||||
public get paletteID(): number
|
||||
{
|
||||
return this._paletteId;
|
||||
}
|
||||
|
||||
public isMandatory(k: string, _arg_2: number): boolean
|
||||
{
|
||||
return this._isMandatory[k.toUpperCase()][Math.min(_arg_2, 1)];
|
||||
}
|
||||
|
||||
public optionalFromClubLevel(k: string): number
|
||||
{
|
||||
const _local_2 = this._isMandatory[k.toUpperCase()];
|
||||
|
||||
return _local_2.indexOf(false);
|
||||
}
|
||||
|
||||
public get partSets(): IAdvancedMap<string, IFigurePartSet>
|
||||
{
|
||||
return this._partSets;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
export * from './FigurePart';
|
||||
export * from './FigurePartSet';
|
||||
export * from './Palette';
|
||||
export * from './PartColor';
|
||||
export * from './SetType';
|
||||
@@ -0,0 +1,7 @@
|
||||
export * from './AvatarAnimationData';
|
||||
export * from './AvatarCanvas';
|
||||
export * from './FigureSetData';
|
||||
export * from './PartSetsData';
|
||||
export * from './animation';
|
||||
export * from './figure';
|
||||
export * from './parts';
|
||||
@@ -0,0 +1,26 @@
|
||||
export class ActivePartSet
|
||||
{
|
||||
private _id: string;
|
||||
private _parts: string[];
|
||||
|
||||
constructor(data: any)
|
||||
{
|
||||
this._id = data.id;
|
||||
this._parts = [];
|
||||
|
||||
if(data.activeParts && (data.activeParts.length > 0))
|
||||
{
|
||||
for(const part of data.activeParts)
|
||||
{
|
||||
if(!part) continue;
|
||||
|
||||
this._parts.push(part.setType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public get parts(): string[]
|
||||
{
|
||||
return this._parts;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
export class PartDefinition
|
||||
{
|
||||
private _setType: string;
|
||||
private _flippedSetType: string;
|
||||
private _removeSetType: string;
|
||||
private _appendToFigure: boolean;
|
||||
private _staticId: number;
|
||||
|
||||
constructor(data: any)
|
||||
{
|
||||
if(!data) throw new Error('invalid_data');
|
||||
|
||||
this._setType = data.setType;
|
||||
this._flippedSetType = data.flippedSetType || null;
|
||||
this._removeSetType = data.removeSetType || null;
|
||||
this._appendToFigure = false;
|
||||
this._staticId = -1;
|
||||
}
|
||||
|
||||
public hasStaticId(): boolean
|
||||
{
|
||||
return this._staticId >= 0;
|
||||
}
|
||||
|
||||
public get staticId(): number
|
||||
{
|
||||
return this._staticId;
|
||||
}
|
||||
|
||||
public set staticId(k: number)
|
||||
{
|
||||
this._staticId = k;
|
||||
}
|
||||
|
||||
public get setType(): string
|
||||
{
|
||||
return this._setType;
|
||||
}
|
||||
|
||||
public get flippedSetType(): string
|
||||
{
|
||||
return this._flippedSetType;
|
||||
}
|
||||
|
||||
public set flippedSetType(type: string)
|
||||
{
|
||||
this._flippedSetType = type;
|
||||
}
|
||||
|
||||
public get removeSetType(): string
|
||||
{
|
||||
return this._removeSetType;
|
||||
}
|
||||
|
||||
public get appendToFigure(): boolean
|
||||
{
|
||||
return this._appendToFigure;
|
||||
}
|
||||
|
||||
public set appendToFigure(flag: boolean)
|
||||
{
|
||||
this._appendToFigure = flag;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
export * from './ActivePartSet';
|
||||
export * from './PartDefinition';
|
||||
Reference in New Issue
Block a user