First commit

This commit is contained in:
Bill 2021-09-02 13:31:56 -04:00
commit a314dc2abe
193 changed files with 14154 additions and 0 deletions

16
.editorconfig Normal file
View File

@ -0,0 +1,16 @@
# Editor configuration, see https://editorconfig.org
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 4
insert_final_newline = true
trim_trailing_whitespace = true
[*.ts]
quote_type = single
[*.md]
max_line_length = off
trim_trailing_whitespace = false

1
.env Normal file
View File

@ -0,0 +1 @@
CONFIG_URL=http://client.nitrots.co:3000/renderer-config.json

51
.gitignore vendored Normal file
View File

@ -0,0 +1,51 @@
# See http://help.github.com/ignore-files/ for more about ignoring files.
# compiled output
/dist
/tmp
/out-tsc
# Only exists if Bazel was run
/bazel-out
# dependencies
/node_modules
# profiling files
chrome-profiler-events*.json
speed-measure-plugin*.json
# IDEs and editors
/.idea
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace
# IDE - VSCode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
.history/*
# misc
/.sass-cache
/connect.lock
/coverage
/libpeerconnection.log
npm-debug.log
yarn-error.log
testem.log
/typings
.git
# System Files
.DS_Store
Thumbs.db
*.zip
*.as
*.bin

16
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,16 @@
{
"typescript.tsdk": "node_modules\\typescript\\lib",
"typescript.preferences.importModuleSpecifier": "relative",
"typescript.preferences.quoteStyle": "single",
"typescript.format.placeOpenBraceOnNewLineForControlBlocks": true,
"typescript.format.placeOpenBraceOnNewLineForFunctions": true,
"editor.codeActionsOnSave": {
"source.fixAll": true,
"source.organizeImports": true,
},
"emmet.showExpandedAbbreviation": "never",
"git.ignoreLimitWarning": true,
"files.eol": "\n",
"files.insertFinalNewline": true,
"files.trimFinalNewlines": true
}

1373
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

38
package.json Normal file
View File

@ -0,0 +1,38 @@
{
"name": "nitro-imager",
"version": "1.0.0",
"description": "",
"main": "index.js",
"dependencies": {
"bytebuffer": "^5.0.1",
"canvas": "^2.8.0",
"chalk": "^4.1.2",
"dotenv": "^10.0.0",
"express": "^4.17.1",
"node-fetch": "^2.6.1",
"pako": "^2.0.4"
},
"devDependencies": {
"@types/bytebuffer": "^5.0.42",
"@types/chalk": "^2.2.0",
"@types/express": "^4.17.13",
"@types/node": "^14.17.12",
"@types/node-fetch": "^2.5.12",
"@types/pako": "^1.0.2",
"ts-node-dev": "^1.1.8",
"typescript": "^4.4.2"
},
"scripts": {
"start:dev": "ts-node-dev --respawn --transpile-only ./src/main.ts"
},
"repository": {
"type": "git",
"url": "git+https://github.com/billsonnn/nitro-imager.git"
},
"author": "",
"license": "ISC",
"bugs": {
"url": "https://github.com/billsonnn/nitro-imager/issues"
},
"homepage": "https://github.com/billsonnn/nitro-imager#readme"
}

63
src/app/Application.ts Normal file
View File

@ -0,0 +1,63 @@
import { INitroCore, NitroManager } from '../core';
import { AvatarRenderManager, AvatarScaleType, AvatarSetType, IAvatarRenderManager } from './avatar';
import { IApplication } from './IApplication';
export class Application extends NitroManager implements IApplication
{
private static INSTANCE: IApplication = null;
private _core: INitroCore;
private _avatar: IAvatarRenderManager;
constructor(core: INitroCore)
{
super();
Application.INSTANCE = this;
this._core = core;
this._avatar = new AvatarRenderManager(core.asset);
}
protected async onInit(): Promise<void>
{
if(this._core) await this._core.init();
if(this._avatar) await this._avatar.init();
const image = await this._avatar.createAvatarImage('hd-207-14.lg-3216-1408.cc-3007-86-88.ha-3054-1408-1408.he-3079-64.ea-1402-0.ch-230-72.hr-110-40', AvatarScaleType.LARGE, 'M');
//image.setDirection(AvatarSetType.FULL, 2);
const canvas = await image.getImage(AvatarSetType.FULL, false);
console.log(canvas.toDataURL());
this.logger.log(`Initialized`);
}
protected async onDispose(): Promise<void>
{
if(this._avatar) await this._avatar.dispose();
if(this._core) await this._core.dispose();
}
public getConfiguration<T>(key: string, value: T = null): T
{
return this._core.configuration.getValue<T>(key, value);
}
public get core(): INitroCore
{
return this._core;
}
public get avatar(): IAvatarRenderManager
{
return this._avatar;
}
public static get instance(): IApplication
{
return this.INSTANCE;
}
}

9
src/app/IApplication.ts Normal file
View File

@ -0,0 +1,9 @@
import { INitroCore, INitroManager } from '../core';
import { IAvatarRenderManager } from './avatar';
export interface IApplication extends INitroManager
{
getConfiguration<T>(key: string, value?: T): T;
core: INitroCore;
avatar: IAvatarRenderManager;
}

View File

@ -0,0 +1,67 @@
import { IAssetManager } from '../../core';
export class AvatarAssetDownloadLibrary
{
private static NOT_LOADED: number = 0;
private static LOADING: number = 1;
private static LOADED: number = 2;
private _state: number;
private _libraryName: string;
private _revision: string;
private _downloadUrl: string;
private _assets: IAssetManager;
constructor(id: string, revision: string, assets: IAssetManager, assetUrl: string)
{
this._state = AvatarAssetDownloadLibrary.NOT_LOADED;
this._libraryName = id;
this._revision = revision;
this._downloadUrl = assetUrl;
this._assets = assets;
this._downloadUrl = this._downloadUrl.replace(/%libname%/gi, this._libraryName);
this._downloadUrl = this._downloadUrl.replace(/%revision%/gi, this._revision);
this.checkIfAssetLoaded();
}
private checkIfAssetLoaded(): boolean
{
if(this._state === AvatarAssetDownloadLibrary.LOADED) return true;
const asset = this._assets.getCollection(this._libraryName);
if(asset)
{
this._state = AvatarAssetDownloadLibrary.LOADED;
return true;
}
return false;
}
public async downloadAsset(): Promise<void>
{
if(!this._assets || (this._state === AvatarAssetDownloadLibrary.LOADING)) return;
if(this.checkIfAssetLoaded()) return;
this._state = AvatarAssetDownloadLibrary.LOADING;
await this._assets.downloadAsset(this._downloadUrl);
this._state = AvatarAssetDownloadLibrary.LOADED;
}
public get libraryName(): string
{
return this._libraryName;
}
public get isLoaded(): boolean
{
return (this._state === AvatarAssetDownloadLibrary.LOADED);
}
}

View File

@ -0,0 +1,154 @@
import fetch from 'node-fetch';
import { AdvancedMap, IAssetManager } from '../../core';
import { Application } from '../Application';
import { AvatarAssetDownloadLibrary } from './AvatarAssetDownloadLibrary';
import { AvatarStructure } from './AvatarStructure';
import { IAvatarFigureContainer } from './IAvatarFigureContainer';
export class AvatarAssetDownloadManager
{
private _assets: IAssetManager;
private _structure: AvatarStructure;
private _missingMandatoryLibs: string[];
private _figureMap: AdvancedMap<string, AvatarAssetDownloadLibrary[]>;
private _libraryNames: string[];
constructor(assets: IAssetManager, structure: AvatarStructure)
{
this._assets = assets;
this._structure = structure;
this._missingMandatoryLibs = Application.instance.getConfiguration<string[]>('avatar.mandatory.libraries');
this._figureMap = new AdvancedMap();
this._libraryNames = [];
}
public async loadFigureMap(): Promise<void>
{
const url = Application.instance.getConfiguration<string>('avatar.figuremap.url');
const data = await fetch(url);
const json = await data.json();
this.processFigureMap(json.libraries);
await this.processMissingLibraries();
}
private processFigureMap(data: any): void
{
if(!data) return;
const avatarAssetUrl = Application.instance.getConfiguration<string>('avatar.asset.url');
for(const library of data)
{
if(!library) continue;
const id = (library.id as string);
const revision = (library.revision || '');
if(this._libraryNames.indexOf(id) >= 0) continue;
this._libraryNames.push(id);
const downloadLibrary = new AvatarAssetDownloadLibrary(id, revision, this._assets, avatarAssetUrl);
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.getValue(partString);
if(!existing) existing = [];
existing.push(downloadLibrary);
this._figureMap.add(partString, existing);
}
}
}
public async processMissingLibraries(): Promise<void>
{
const missingLibraries = this._missingMandatoryLibs.slice();
for(const library of missingLibraries)
{
if(!library) continue;
const libraries = this._figureMap.getValue(library);
if(libraries) for(const library of libraries) (library && await this.downloadLibrary(library));
}
}
public isAvatarFigureContainerReady(container: IAvatarFigureContainer): boolean
{
const pendingLibraries = this.getAvatarFigurePendingLibraries(container);
return !pendingLibraries.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.getValue(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 async downloadAvatarFigure(container: IAvatarFigureContainer): Promise<void>
{
const pendingLibraries = this.getAvatarFigurePendingLibraries(container);
for(const library of pendingLibraries) (library && await this.downloadLibrary(library));
}
private async downloadLibrary(library: AvatarAssetDownloadLibrary): Promise<void>
{
if(!library || library.isLoaded) return;
await library.downloadAsset();
}
}

View File

@ -0,0 +1,117 @@
import { AdvancedMap } from '../../core';
import { IAvatarFigureContainer } from './IAvatarFigureContainer';
export class AvatarFigureContainer implements IAvatarFigureContainer
{
private _parts: AdvancedMap<string, AdvancedMap<string, any>>;
constructor(figure: string)
{
this._parts = new AdvancedMap();
this.parseFigure(figure);
}
public getPartTypeIds(): string[]
{
return this.partSets().getKeys();
}
public hasPartType(k: string): boolean
{
return !!this.partSets().getValue(k);
}
public getPartSetId(k: string): number
{
const existing = this.partSets().getValue(k);
if(!existing) return 0;
return existing.getValue('setid');
}
public getPartColorIds(k: string): number[]
{
const existing = this.partSets().getValue(k);
if(!existing) return null;
return existing.getValue('colorids');
}
public updatePart(setType: string, partSetId: number, colorIds: number[]): void
{
const set: AdvancedMap<string, any> = new AdvancedMap();
set.add('type', setType);
set.add('setid', partSetId);
set.add('colorids', colorIds);
const existingSets = this.partSets();
existingSets.remove(setType);
existingSets.add(setType, set);
}
public removePart(k: string): void
{
this.partSets().remove(k);
}
public getFigureString(): string
{
const parts: string[] = [];
for(const key of this.partSets().getKeys())
{
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(): AdvancedMap<string, AdvancedMap<string, any>>
{
if(!this._parts) this._parts = new AdvancedMap();
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);
}
}
}
}

View File

@ -0,0 +1,741 @@
import { Canvas, createCanvas } from 'canvas';
import { IGraphicAsset } from '../../core';
import { ActiveActionData, IActionDefinition, IActiveActionData } from './actions';
import { AssetAliasCollection } from './alias';
import { IAnimationLayerData, IAvatarDataContainer, ISpriteDataContainer } from './animation';
import { AvatarFigureContainer } from './AvatarFigureContainer';
import { AvatarStructure } from './AvatarStructure';
import { AvatarImageCache } from './cache';
import { EffectAssetDownloadManager } from './EffectAssetDownloadManager';
import { AvatarAction, AvatarDirectionAngle, AvatarScaleType, AvatarSetType } from './enum';
import { IAvatarFigureContainer } from './IAvatarFigureContainer';
import { IAvatarImage } from './IAvatarImage';
import { IPartColor } from './structure';
export class AvatarImage implements IAvatarImage
{
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 _structure: AvatarStructure;
protected _scale: string;
protected _mainDirection: number;
protected _headDirection: number;
protected _mainAction: IActiveActionData;
protected _disposed: boolean;
protected _canvasOffsets: number[];
protected _assets: AssetAliasCollection;
protected _cache: AvatarImageCache;
protected _figure: AvatarFigureContainer;
protected _avatarSpriteData: IAvatarDataContainer;
protected _actions: ActiveActionData[];
private _defaultAction: IActiveActionData;
private _frameCounter: number = 0;
private _directionOffset: number = 0;
private _sprites: ISpriteDataContainer[];
private _isAnimating: boolean = false;
private _animationHasResetOnToggle: boolean = false;
private _actionsSorted: boolean = false;
private _sortedActions: IActiveActionData[];
private _lastActionsString: string;
private _currentActionsString: string;
private _effectIdInUse: number = -1;
private _animationFrameCount: number;
private _cachedBodyParts: string[];
private _cachedBodyPartsDirection: number = -1;
private _cachedBodyPartsGeometryType: string = null;
private _cachedBodyPartsAvatarSet: string = null;
private _effectManager: EffectAssetDownloadManager;
constructor(k: AvatarStructure, _arg_2: AssetAliasCollection, _arg_3: AvatarFigureContainer, _arg_4: string, _arg_5: EffectAssetDownloadManager)
{
this._canvasOffsets = [];
this._actions = [];
this._cachedBodyParts = [];
this._disposed = false;
this._effectManager = _arg_5;
this._structure = k;
this._assets = _arg_2;
this._scale = _arg_4;
if(this._scale == null)
{
this._scale = AvatarScaleType.LARGE;
}
if(_arg_3 == null)
{
_arg_3 = new AvatarFigureContainer('hr-893-45.hd-180-2.ch-210-66.lg-270-82.sh-300-91.wa-2007-.ri-1-');
}
this._figure = _arg_3;
this._cache = new AvatarImageCache(this._structure, this, this._assets, this._scale);
this.setDirection(AvatarImage.DEFAULT_AVATAR_SET, AvatarImage.DEFAULT_DIRECTION);
this._actions = [];
this._defaultAction = new ActiveActionData(AvatarAction.POSTURE_STAND);
this._defaultAction.definition = this._structure.getActionDefinition(AvatarImage.DEFAULT_ACTION);
this.resetActions();
this._animationFrameCount = 0;
}
public async dispose(): Promise<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._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(k: string, _arg_2: number): void
{
_arg_2 = (_arg_2 + this._directionOffset);
if(_arg_2 < AvatarDirectionAngle.MIN_DIRECTION)
{
_arg_2 = (AvatarDirectionAngle.MAX_DIRECTION + (_arg_2 + 1));
}
if(_arg_2 > AvatarDirectionAngle.MAX_DIRECTION)
{
_arg_2 = (_arg_2 - (AvatarDirectionAngle.MAX_DIRECTION + 1));
}
if(this._structure.isMainAvatarSet(k))
{
this._mainDirection = _arg_2;
}
if((k === AvatarSetType.HEAD) || (k === AvatarSetType.FULL))
{
if((k === AvatarSetType.HEAD) && (this.isHeadTurnPreventedByAction()))
{
_arg_2 = this._mainDirection;
}
this._headDirection = _arg_2;
}
this._cache.setDirection(k, _arg_2);
}
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;
}
public resetAnimationFrameCounter(): void
{
this._frameCounter = 0;
}
private getFullImageCacheKey(): string
{
if(((this._sortedActions.length == 1) && (this._mainDirection == this._headDirection)))
{
return (this._mainDirection + this._currentActionsString) + (this._frameCounter % 4);
}
if(this._sortedActions.length == 2)
{
for(const k of this._sortedActions)
{
if(((k.actionType == 'fx') && ((((k.actionParameter == '33') || (k.actionParameter == '34')) || (k.actionParameter == '35')) || (k.actionParameter == '36'))))
{
return (this._mainDirection + this._currentActionsString) + 0;
}
if(((k.actionType == 'fx') && ((k.actionParameter == '38') || (k.actionParameter == '39'))))
{
return (((this._mainDirection + '_') + this._headDirection) + this._currentActionsString) + (this._frameCounter % 11);
}
if((k.actionType === 'dance') && ((k.actionParameter === '1') || (k.actionParameter === '2') || (k.actionParameter === '3') || (k.actionParameter === '4')))
{
let frame = (this._frameCounter % 8);
if((k.actionParameter === '3')) frame = (this._frameCounter % 10);
if((k.actionParameter === '4')) frame = (this._frameCounter % 16);
return (((this._mainDirection + k.actionType) + k.actionParameter) + frame);
}
}
}
return null;
}
private getBodyParts(k: string, _arg_2: string, _arg_3: number): string[]
{
if((((!(_arg_3 == this._cachedBodyPartsDirection)) || (!(_arg_2 == this._cachedBodyPartsGeometryType))) || (!(k == this._cachedBodyPartsAvatarSet))))
{
this._cachedBodyPartsDirection = _arg_3;
this._cachedBodyPartsGeometryType = _arg_2;
this._cachedBodyPartsAvatarSet = k;
this._cachedBodyParts = this._structure.getBodyParts(k, _arg_2, _arg_3);
}
return this._cachedBodyParts;
}
public getAvatarPartsForCamera(k: string): void
{
let _local_4: string;
if(this._mainAction == null)
{
return;
}
const _local_2 = this._structure.getCanvas(this._scale, this._mainAction.definition.geometryType);
if(_local_2 == null)
{
return;
}
const _local_3 = this.getBodyParts(k, this._mainAction.definition.geometryType, this._mainDirection);
let _local_6 = (_local_3.length - 1);
while(_local_6 >= 0)
{
_local_4 = _local_3[_local_6];
const _local_5 = this._cache.getImageContainer(_local_4, this._frameCounter);
_local_6--;
}
}
public async getImage(setType: string, hightlight: boolean, scale: number = 1): Promise<Canvas>
{
if(!this._mainAction) return null;
if(!this._actionsSorted) await this.endActionAppends();
const avatarCanvas = this._structure.getCanvas(this._scale, this._mainAction.definition.geometryType);
if(!avatarCanvas) return null;
const bodyParts = this.getBodyParts(setType, this._mainAction.definition.geometryType, this._mainDirection);
const canvas = createCanvas(avatarCanvas.width, avatarCanvas.height);
const ctx = canvas.getContext('2d');
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) return null;
const point = part.regPoint.clone();
if(point)
{
point.x += avatarCanvas.offset.x;
point.y += avatarCanvas.offset.y;
point.x += avatarCanvas.regPoint.x;
point.y += avatarCanvas.regPoint.y;
ctx.save();
ctx.scale(scale, 1);
ctx.drawImage(part.image, point.x, point.y, part.image.width, part.image.height);
ctx.restore();
}
}
partCount--;
}
//if(this._avatarSpriteData && this._avatarSpriteData.paletteIsGrayscale) this.convertToGrayscale(container);
return canvas;
}
// public applyPalette(texture: RenderTexture, reds: number[] = [], greens: number[] = [], blues: number[] = []): RenderTexture
// {
// const textureCanvas = Nitro.instance.renderer.extract.canvas(texture);
// const textureCtx = textureCanvas.getContext('2d');
// const textureImageData = textureCtx.getImageData(0, 0, textureCanvas.width, textureCanvas.height);
// const data = textureImageData.data;
// for(const i = 0; i < data.length; i += 4)
// {
// let paletteColor = this._palette[data[ i + 1 ]];
// if(paletteColor === undefined) paletteColor = [ 0, 0, 0 ];
// data[ i ] = paletteColor[0];
// data[ i + 1 ] = paletteColor[1];
// data[ i + 2 ] = paletteColor[2];
// }
// textureCtx.putImageData(textureImageData, 0, 0);
// return Texture.from(textureCanvas);
// }
public getAsset(k: string): IGraphicAsset
{
return this._assets.getAsset(k);
}
public getDirection(): number
{
return this._mainDirection;
}
public initActionAppends(): void
{
this._actions = [];
this._actionsSorted = false;
this._currentActionsString = '';
}
public async endActionAppends(): Promise<void>
{
let k:ActiveActionData;
if(!this.sortActions()) return;
for(const k of this._sortedActions)
{
if(k.actionType === AvatarAction.EFFECT)
{
if(!this._effectManager.isAvatarEffectReady(parseInt(k.actionParameter))) await this._effectManager.downloadAvatarEffect(parseInt(k.actionParameter));
}
}
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:
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(k: string, _arg_2: string=''): void
{
let _local_3:ActiveActionData;
if(!this._actions) this._actions = [];
let _local_4 = 0;
while(_local_4 < this._actions.length)
{
_local_3 = this._actions[_local_4];
if(((_local_3.actionType == k) && (_local_3.actionParameter == _arg_2)))
{
return;
}
_local_4++;
}
this._actions.push(new ActiveActionData(k, _arg_2, 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
{
let _local_2: IActionDefinition;
let _local_3: ActiveActionData;
let k: boolean;
if(this._sortedActions == null)
{
return false;
}
for(const _local_3 of this._sortedActions)
{
_local_2 = this._structure.getActionDefinitionWithState(_local_3.actionType);
if(((!(_local_2 == null)) && (_local_2.getPreventHeadTurn(_local_3.actionParameter))))
{
k = true;
}
}
return k;
}
private sortActions(): boolean
{
let _local_2: boolean;
let _local_3: boolean;
let _local_4:ActiveActionData;
let _local_5: number;
let k: boolean;
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 !== '')
{
k = true;
this._lastActionsString = '';
}
}
else
{
this._canvasOffsets = this._structure.getCanvasOffsets(this._sortedActions, this._scale, this._mainDirection);
for(const _local_4 of this._sortedActions)
{
this._currentActionsString = (this._currentActionsString + (_local_4.actionType + _local_4.actionParameter));
if(_local_4.actionType === AvatarAction.EFFECT)
{
const _local_5 = parseInt(_local_4.actionParameter);
if(this._effectIdInUse !== _local_5) _local_2 = true;
this._effectIdInUse = _local_5;
_local_3 = true;
}
}
if(!_local_3)
{
if(this._effectIdInUse > -1) _local_2 = true;
this._effectIdInUse = -1;
}
if(_local_2) this._cache.disposeInactiveActions(0);
if(this._lastActionsString != this._currentActionsString)
{
k = true;
this._lastActionsString = this._currentActionsString;
}
}
this._actionsSorted = true;
return k;
}
private setActionsToParts(): void
{
if(!this._sortedActions == null) return;
const _local_3: number = Date.now();
const _local_4: string[] = [];
for(const k of this._sortedActions) _local_4.push(k.actionType);
for(const k of this._sortedActions)
{
if((k && k.definition) && k.definition.isAnimation)
{
const _local_2 = this._structure.getAnimation(((k.definition.state + '.') + k.actionParameter));
if(_local_2 && _local_2.hasOverriddenActions())
{
const _local_5 = _local_2.overriddenActionNames();
if(_local_5)
{
for(const _local_6 of _local_5)
{
if(_local_4.indexOf(_local_6) >= 0) k.overridingAction = _local_2.overridingAction(_local_6);
}
}
}
if(_local_2 && _local_2.resetOnToggle)
{
this._animationHasResetOnToggle = true;
}
}
}
for(const k of this._sortedActions)
{
if(!((!(k)) || (!(k.definition))))
{
if(k.definition.isAnimation && (k.actionParameter === '')) k.actionParameter = '1';
this.setActionToParts(k, _local_3);
if(k.definition.isAnimation)
{
this._isAnimating = k.definition.isAnimated(k.actionParameter);
const _local_2 = this._structure.getAnimation(((k.definition.state + '.') + k.actionParameter));
if(_local_2)
{
this._sprites = this._sprites.concat(_local_2.spriteData);
if(_local_2.hasDirectionData()) this._directionOffset = _local_2.directionData.offset;
if(_local_2.hasAvatarData()) this._avatarSpriteData = _local_2.avatarData;
}
}
}
}
}
private setActionToParts(k: IActiveActionData, _arg_2: number): void
{
if(((k == null) || (k.definition == null)))
{
return;
}
if(k.definition.assetPartDefinition == '')
{
return;
}
if(k.definition.isMain)
{
this._mainAction = k;
this._cache.setGeometryType(k.definition.geometryType);
}
this._cache.setAction(k, _arg_2);
}
private resetBodyPartCache(k: IActiveActionData): void
{
if(!k) return;
if(k.definition.assetPartDefinition === '') return;
if(k.definition.isMain)
{
this._mainAction = k;
this._cache.setGeometryType(k.definition.geometryType);
}
this._cache.resetBodyPartCache(k);
}
public get avatarSpriteData(): IAvatarDataContainer
{
return this._avatarSpriteData;
}
// private convertToGrayscale(container: Canvas, channel: string = 'CHANNELS_EQUAL'): Canvas
// {
// let _local_3 = 0.33;
// let _local_4 = 0.33;
// let _local_5 = 0.33;
// const _local_6 = 1;
// switch(channel)
// {
// case AvatarImage.CHANNELS_UNIQUE:
// _local_3 = 0.3;
// _local_4 = 0.59;
// _local_5 = 0.11;
// break;
// case AvatarImage.CHANNELS_RED:
// _local_3 = 1;
// _local_4 = 0;
// _local_5 = 0;
// break;
// case AvatarImage.CHANNELS_GREEN:
// _local_3 = 0;
// _local_4 = 1;
// _local_5 = 0;
// break;
// case AvatarImage.CHANNELS_BLUE:
// _local_3 = 0;
// _local_4 = 0;
// _local_5 = 1;
// break;
// case AvatarImage.CHANNELS_DESATURATED:
// _local_3 = 0.3086;
// _local_4 = 0.6094;
// _local_5 = 0.082;
// break;
// }
// const colorFilter = new ColorMatrixFilter();
// colorFilter.matrix = [_local_3, _local_4, _local_5, 0, 0, _local_3, _local_4, _local_5, 0, 0, _local_3, _local_4, _local_5, 0, 0, 0, 0, 0, 1, 0];
// container.filters = [ colorFilter ];
// return container;
// }
public isPlaceholder(): boolean
{
return false;
}
public forceActionUpdate(): void
{
this._lastActionsString = '';
}
public get animationHasResetOnToggle(): boolean
{
return this._animationHasResetOnToggle;
}
public get mainAction(): string
{
return this._mainAction.actionType;
}
}

View File

@ -0,0 +1,74 @@
import { Canvas } from 'canvas';
import { Point } from '../../core';
export class AvatarImageBodyPartContainer
{
private _image: Canvas;
private _regPoint: Point;
private _offset: Point;
private _isCacheable: boolean;
constructor(image: Canvas, regPoint: Point, isCacheable: boolean)
{
this._image = image;
this._regPoint = regPoint;
this._offset = new Point(0, 0);
this._isCacheable = isCacheable;
this.cleanPoints();
}
public dispose(): void
{
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(): Canvas
{
return this._image;
}
public set image(k: Canvas)
{
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;
}
}

View File

@ -0,0 +1,134 @@
import { IActionDefinition } from './actions/IActionDefinition';
import { AvatarAnimationFrame } from './structure/animation/AvatarAnimationFrame';
import { IPartColor } from './structure/figure/IPartColor';
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(k: string, _arg_2: string, _arg_3: string, _arg_4: IPartColor, _arg_5: AvatarAnimationFrame[], _arg_6: IActionDefinition, _arg_7: boolean, _arg_8: number, _arg_9: string = '', _arg_10: boolean = false, _arg_11: number = 1)
{
this._bodyPartId = k;
this._partType = _arg_2;
this._partId = _arg_3;
this._color = _arg_4;
this._frames = _arg_5;
this._action = _arg_6;
this._isColorable = _arg_7;
this._paletteMapId = _arg_8;
this._flippedPartType = _arg_9;
this._isBlendable = _arg_10;
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(':');
}
}

View File

@ -0,0 +1,310 @@
import fetch from 'node-fetch';
import { IAssetManager, IGraphicAsset, NitroManager } from '../../core';
import { Application } from '../Application';
import { AssetAliasCollection } from './alias';
import { AvatarAssetDownloadManager } from './AvatarAssetDownloadManager';
import { AvatarFigureContainer } from './AvatarFigureContainer';
import { AvatarImage } from './AvatarImage';
import { AvatarStructure } from './AvatarStructure';
import { HabboAvatarAnimations, HabboAvatarGeometry, HabboAvatarPartSets } from './data';
import { EffectAssetDownloadManager } from './EffectAssetDownloadManager';
import { AvatarSetType } from './enum';
import { FigureDataContainer } from './FigureDataContainer';
import { IAvatarFigureContainer } from './IAvatarFigureContainer';
import { IAvatarImage } from './IAvatarImage';
import { IAvatarRenderManager } from './IAvatarRenderManager';
import { IFigureData } from './interfaces';
import { IFigurePartSet, IStructureData } from './structure';
export class AvatarRenderManager extends NitroManager implements IAvatarRenderManager
{
private static DEFAULT_FIGURE: string = 'hd-99999-99999';
private _aliasCollection: AssetAliasCollection;
private _assets: IAssetManager;
private _structure: AvatarStructure;
private _avatarAssetDownloadManager: AvatarAssetDownloadManager;
private _effectAssetDownloadManager: EffectAssetDownloadManager;
private _placeHolderFigure: AvatarFigureContainer;
constructor(assets: IAssetManager)
{
super();
this._assets = assets;
this._structure = null;
this._avatarAssetDownloadManager = null;
this._effectAssetDownloadManager = null;
this._placeHolderFigure = null;
}
public async onInit(): Promise<void>
{
this._structure = new AvatarStructure(this);
this._structure.initGeometry(HabboAvatarGeometry.geometry);
this._structure.initPartSets(HabboAvatarPartSets.partSets);
this._structure.initAnimation(HabboAvatarAnimations.animations);
await this.loadActions();
this.loadFigureData();
this._aliasCollection = new AssetAliasCollection(this, this._assets);
this._aliasCollection.init();
if(!this._avatarAssetDownloadManager)
{
this._avatarAssetDownloadManager = new AvatarAssetDownloadManager(this._assets, this._structure);
await this._avatarAssetDownloadManager.loadFigureMap();
}
if(!this._effectAssetDownloadManager)
{
this._effectAssetDownloadManager = new EffectAssetDownloadManager(this._assets, this._structure);
await this._effectAssetDownloadManager.loadEffectMap();
}
}
private async loadActions(): Promise<void>
{
this._structure.initActions({
"actions": [
{
"id": "Default",
"state": "std",
"precedence": 1000,
"main": true,
"isDefault": true,
"geometryType": "vertical",
"activePartSet": "figure",
"assetPartDefinition": "std"
}
]
});
const url = Application.instance.getConfiguration<string>('avatar.actions.url');
const data = await fetch(url);
this._structure.updateActions(await data.json());
}
private async loadFigureData(): Promise<void>
{
const defaultFigureData = Application.instance.getConfiguration<IFigureData>('avatar.default.figuredata');
if(this._structure) this._structure.initFigureData(defaultFigureData);
const url = Application.instance.getConfiguration<string>('avatar.figuredata.url');
const data = await fetch(url);
this._structure.figureData.appendJSON(await data.json());
}
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 async createAvatarImage(figure: string, size: string, gender: string): Promise<IAvatarImage>
{
if(!this._structure || !this._avatarAssetDownloadManager) return null;
const figureContainer = new AvatarFigureContainer(figure);
if(gender) this.validateAvatarFigure(figureContainer, gender);
if(!this._avatarAssetDownloadManager.isAvatarFigureContainerReady(figureContainer))
{
await this._avatarAssetDownloadManager.downloadAvatarFigure(figureContainer);
}
if(!this._placeHolderFigure) this._placeHolderFigure = new AvatarFigureContainer(AvatarRenderManager.DEFAULT_FIGURE);
return new AvatarImage(this._structure, this._aliasCollection, figureContainer, size, this._effectAssetDownloadManager);
}
public async downloadAvatarFigure(container: IAvatarFigureContainer): Promise<void>
{
if(!this._avatarAssetDownloadManager) return;
await this._avatarAssetDownloadManager.downloadAvatarFigure(container);
}
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[]): 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);
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);
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(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(k: string, _arg_2: string, _arg_3: number[]): string
{
const container = new FigureDataContainer();
container.loadAvatarData(k, _arg_2);
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(k: number[]): IFigurePartSet[]
{
const structure = this.structureData;
const partSets: IFigurePartSet[] = [];
for(const _local_4 of k)
{
const partSet = structure.getFigurePartSet(_local_4);
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 this._assets;
}
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;
}
}

View File

@ -0,0 +1,630 @@
import { AdvancedMap, IAssetAnimation, IAssetManager, Point } from '../../core';
import { ActionDefinition, AvatarActionManager, IActionDefinition, IActiveActionData } from './actions';
import { Animation, AnimationManager, AvatarAnimationLayerData } from './animation';
import { AvatarImagePartContainer } from './AvatarImagePartContainer';
import { AvatarRenderManager } from './AvatarRenderManager';
import { AvatarDirectionAngle } from './enum';
import { AvatarModelGeometry } from './geometry';
import { IAvatarFigureContainer } from './IAvatarFigureContainer';
import { IAvatarImage } from './IAvatarImage';
import { IAvatarRenderManager } from './IAvatarRenderManager';
import { IFigureData } from './interfaces';
import { AnimationAction, AvatarAnimationData, AvatarAnimationFrame, AvatarCanvas, FigureSetData, IFigurePartSet, IPartColor, IStructureData, 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 dispose(): void
{
this._renderManager = null;
this._figureData = null;
this._partSetsData = null;
this._animationData = null;
this._mandatorySetTypeIds = null;
}
public initGeometry(k: any): void
{
if(!k) return;
this._geometry = new AvatarModelGeometry(k);
}
public initActions(actions: any): void
{
this._actionManager = new AvatarActionManager(actions);
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: AdvancedMap<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.getValue(_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.getValue(_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.getValue(_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.getValues()) _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;
}
}

View File

@ -0,0 +1,78 @@
import { IAssetAnimation, IAssetManager } from '../../core';
export class EffectAssetDownloadLibrary
{
private static NOT_LOADED: number = 0;
private static LOADING: number = 1;
private static LOADED: number = 2;
private _state: number;
private _libraryName: string;
private _revision: string;
private _downloadUrl: string;
private _assets: IAssetManager;
private _animation: { [index: string]: IAssetAnimation };
constructor(id: string, revision: string, assets: IAssetManager, assetUrl: string)
{
this._state = EffectAssetDownloadLibrary.NOT_LOADED;
this._libraryName = id;
this._revision = revision;
this._downloadUrl = assetUrl;
this._assets = assets;
this._animation = null;
this._downloadUrl = this._downloadUrl.replace(/%libname%/gi, this._libraryName);
this._downloadUrl = this._downloadUrl.replace(/%revision%/gi, this._revision);
this.checkIfAssetLoaded();
}
private checkIfAssetLoaded(): boolean
{
if(this._state === EffectAssetDownloadLibrary.LOADED) return true;
const asset = this._assets.getCollection(this._libraryName);
if(asset)
{
this._state = EffectAssetDownloadLibrary.LOADED;
return true;
}
return false;
}
public async downloadAsset(): Promise<void>
{
if(!this._assets || (this._state === EffectAssetDownloadLibrary.LOADING)) return;
if(this.checkIfAssetLoaded()) return;
this._state = EffectAssetDownloadLibrary.LOADING;
await this._assets.downloadAsset(this._downloadUrl);
const collection = this._assets.getCollection(this._libraryName);
if(collection) this._animation = collection.data.animations;
this._state = EffectAssetDownloadLibrary.LOADED;
}
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);
}
}

View File

@ -0,0 +1,123 @@
import fetch from 'node-fetch';
import { AdvancedMap, IAssetManager } from '../../core';
import { Application } from '../Application';
import { AvatarStructure } from './AvatarStructure';
import { EffectAssetDownloadLibrary } from './EffectAssetDownloadLibrary';
export class EffectAssetDownloadManager
{
private _assets: IAssetManager;
private _structure: AvatarStructure;
private _missingMandatoryLibs: string[];
private _effectMap: AdvancedMap<string, EffectAssetDownloadLibrary[]>;
private _libraryNames: string[];
constructor(assets: IAssetManager, structure: AvatarStructure)
{
this._assets = assets;
this._structure = structure;
this._missingMandatoryLibs = Application.instance.getConfiguration<string[]>('avatar.mandatory.effect.libraries');
this._effectMap = new AdvancedMap();
this._libraryNames = [];
}
public async loadEffectMap(): Promise<void>
{
const url = Application.instance.getConfiguration<string>('avatar.effectmap.url');
const data = await fetch(url);
const json = await data.json();
this.processEffectMap(json.effects);
await this.processMissingLibraries();
}
private processEffectMap(data: any): void
{
if(!data) return;
const avatarEffectAssetUrl = Application.instance.getConfiguration<string>('avatar.asset.effect.url');
for(const effect of data)
{
if(!effect) continue;
const id = (effect.id as string);
const lib = (effect.lib as string);
const revision = (effect.revision || '');
if(this._libraryNames.indexOf(lib) >= 0) continue;
this._libraryNames.push(lib);
const downloadLibrary = new EffectAssetDownloadLibrary(lib, revision, this._assets, avatarEffectAssetUrl);
let existing = this._effectMap.getValue(id);
if(!existing) existing = [];
existing.push(downloadLibrary);
this._effectMap.add(id, existing);
}
}
public async downloadAvatarEffect(id: number): Promise<void>
{
const pendingLibraries = this.getAvatarEffectPendingLibraries(id);
for(const library of pendingLibraries) (library && await this.downloadLibrary(library));
}
public async processMissingLibraries(): Promise<void>
{
const missingLibraries = this._missingMandatoryLibs.slice();
for(const library of missingLibraries)
{
if(!library) continue;
const libraries = this._effectMap.getValue(library);
if(libraries) for(const library of libraries) (library && await this.downloadLibrary(library));
}
}
public isAvatarEffectReady(effect: number): boolean
{
const pendingLibraries = this.getAvatarEffectPendingLibraries(effect);
return !pendingLibraries.length;
}
private getAvatarEffectPendingLibraries(id: number): EffectAssetDownloadLibrary[]
{
const pendingLibraries: EffectAssetDownloadLibrary[] = [];
if(!this._structure) return pendingLibraries;
const libraries = this._effectMap.getValue(id.toString());
if(libraries)
{
for(const library of libraries)
{
if(!library || library.isLoaded) continue;
if(pendingLibraries.indexOf(library) === -1) pendingLibraries.push(library);
}
}
return pendingLibraries;
}
private async downloadLibrary(library: EffectAssetDownloadLibrary): Promise<void>
{
if(!library || library.isLoaded) return;
await library.downloadAsset();
}
}

View File

@ -0,0 +1,244 @@
import { AdvancedMap } from '../../core';
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: AdvancedMap<string, number>;
private _colors: AdvancedMap<string, number[]>;
private _gender: string = 'M';
private _isDisposed: boolean;
private _avatarEffectType: number = -1;
public loadAvatarData(k: string, _arg_2: string): void
{
this._data = new AdvancedMap();
this._colors = new AdvancedMap();
this._gender = _arg_2;
this.parseFigureString(k);
}
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.getValue(k);
}
public getPartSetId(k: string): number
{
if(this.hasSetType(k)) return this._data.getValue(k);
return -1;
}
public getColourIds(k: string): number[]
{
if(this._colors.getValue(k)) return this._colors.getValue(k);
return [];
}
public getFigureString(): string
{
let figure = '';
const sets: string[] = [];
for(const key of this._data.getKeys())
{
const value = this._data.getValue(key);
let set = ((key + '-') + value);
const colors = this._colors.getValue(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.add(k, _arg_2);
}
else
{
this._data.remove(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.add(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.getValue(part);
if(colors)
{
let setId = this._data.getValue(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;
}
}

View File

@ -0,0 +1,10 @@
export interface IAvatarFigureContainer
{
getPartTypeIds(): string[];
hasPartType(_arg_1: string): boolean;
getPartSetId(_arg_1: string): number;
getPartColorIds(_arg_1: string): number[];
updatePart(_arg_1: string, _arg_2: number, _arg_3: number[]): void;
removePart(_arg_1: string): void;
getFigureString(): string;
}

View File

@ -0,0 +1,31 @@
import { Canvas } from 'canvas';
import { IDisposable, IGraphicAsset } from '../../core';
import { IAnimationLayerData, IAvatarDataContainer, ISpriteDataContainer } from './animation';
import { IAvatarFigureContainer } from './IAvatarFigureContainer';
import { IPartColor } from './structure';
export interface IAvatarImage extends IDisposable
{
setDirection(_arg_1: string, _arg_2: number): void;
setDirectionAngle(_arg_1: string, _arg_2: number): void;
updateAnimationByFrames(_arg_1?: number): void;
getScale(): string;
getSprites(): ISpriteDataContainer[];
getLayerData(_arg_1: ISpriteDataContainer): IAnimationLayerData;
getImage(setType: string, hightlight: boolean, scale?: number, cache?: boolean): Promise<Canvas>;
getAsset(_arg_1: string): IGraphicAsset;
getDirection(): number;
getFigure(): IAvatarFigureContainer;
getPartColor(_arg_1: string): IPartColor;
isAnimating(): boolean;
getCanvasOffsets(): number[];
initActionAppends(): void;
endActionAppends(): void;
appendAction(_arg_1: string, ..._args: any[]): boolean;
avatarSpriteData: IAvatarDataContainer;
isPlaceholder(): boolean;
forceActionUpdate(): void;
animationHasResetOnToggle: boolean;
resetAnimationFrameCounter(): void;
mainAction: string;
}

View File

@ -0,0 +1,23 @@
import { IAssetManager, IGraphicAsset, INitroManager } from '../../core';
import { AvatarAssetDownloadManager } from './AvatarAssetDownloadManager';
import { AvatarStructure } from './AvatarStructure';
import { IAvatarFigureContainer } from './IAvatarFigureContainer';
import { IAvatarImage } from './IAvatarImage';
import { IStructureData } from './structure/IStructureData';
export interface IAvatarRenderManager extends INitroManager
{
createFigureContainer(figure: string): IAvatarFigureContainer;
isFigureContainerReady(container: IAvatarFigureContainer): boolean;
createAvatarImage(figure: string, size: string, gender: string): Promise<IAvatarImage>;
downloadAvatarFigure(container: IAvatarFigureContainer): void;
getFigureClubLevel(container: IAvatarFigureContainer, gender: string, searchParts: string[]): number;
isValidFigureSetForGender(setId: number, gender: string): boolean;
getFigureStringWithFigureIds(k: string, _arg_2: string, _arg_3: number[]): string;
getMandatoryAvatarPartSetIds(k: string, _arg_2: number): string[];
getAssetByName(name: string): IGraphicAsset;
assets: IAssetManager;
structure: AvatarStructure;
structureData: IStructureData;
downloadManager: AvatarAssetDownloadManager;
}

View File

@ -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);
}
public isPlaceholder(): boolean
{
return true;
}
}

View File

@ -0,0 +1,225 @@
import { AdvancedMap } from '../../../core';
import { ActionType } from './ActionType';
import { IActionDefinition } from './IActionDefinition';
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: AdvancedMap<number, ActionType>;
private _params: AdvancedMap<string, string>;
private _defaultParameterValue: string;
private _canvasOffsets: AdvancedMap<string, AdvancedMap<number, number[]>>;
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;
this._types = new AdvancedMap();
this._params = new AdvancedMap();
this._defaultParameterValue = '';
this._canvasOffsets = null;
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.add(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.add(action.id, action);
}
}
}
public setOffsets(k: string, _arg_2: number, _arg_3: number[]): void
{
if(!this._canvasOffsets) this._canvasOffsets = new AdvancedMap();
let existing = this._canvasOffsets.getValue(k);
if(!existing)
{
existing = new AdvancedMap();
this._canvasOffsets.add(k, existing);
}
existing.add(_arg_2, _arg_3);
}
public getOffsets(k: string, _arg_2: number): number[]
{
if(!this._canvasOffsets) return null;
const existing = this._canvasOffsets.getValue(k);
if(!existing) return null;
return existing.getValue(_arg_2);
}
public getType(id: string): ActionType
{
if(!id) return null;
const existing = this._types.getValue(parseInt(id));
if(!existing) return null;
return existing;
}
public getParameterValue(id: string): string
{
if(!id) return '';
const existing = this._params.getValue(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.getValue(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(): AdvancedMap<string, string>
{
return this._params;
}
}

View File

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

View File

@ -0,0 +1,74 @@
import { IActionDefinition } from './IActionDefinition';
import { IActiveActionData } from './IActiveActionData';
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;
}
}

View File

@ -0,0 +1,185 @@
import { AdvancedMap } from '../../../core';
import { ActionDefinition } from './ActionDefinition';
import { IActiveActionData } from './IActiveActionData';
export class AvatarActionManager
{
private _actions: AdvancedMap<string, ActionDefinition>;
private _defaultAction: ActionDefinition;
constructor(data: any)
{
this._actions = new AdvancedMap();
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.add(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.getValue(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.getValues())
{
if(!action || (action.id !== id)) continue;
return action;
}
return null;
}
public getActionDefinitionWithState(state: string): ActionDefinition
{
const existing = this._actions.getValue(state);
if(!existing) return null;
return existing;
}
public getDefaultAction(): ActionDefinition
{
if(this._defaultAction) return this._defaultAction;
for(const action of this._actions.getValues())
{
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.getValue(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.getValue(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.getValue(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;
}
}

View File

@ -0,0 +1,19 @@
export interface IActionDefinition
{
id: string;
state: string;
precedence: number;
activePartSet: string;
isMain: boolean;
isDefault: boolean;
assetPartDefinition: string;
lay: string;
geometryType: string;
isAnimation: boolean;
startFromFrameZero: boolean;
isAnimated(_arg_1: string): boolean;
getPrevents(_arg_1: string): string[];
getPreventHeadTurn(_arg_1: string): boolean;
setOffsets(_arg_1: string, _arg_2: number, _arg_3: []): void;
getOffsets(_arg_1: string, _arg_2: number): number[];
}

View File

@ -0,0 +1,11 @@
import { IActionDefinition } from './IActionDefinition';
export interface IActiveActionData
{
id: string;
actionType: string;
actionParameter: string;
startFrame: number;
definition: IActionDefinition;
overridingAction: string;
}

View File

@ -0,0 +1,6 @@
export * from './ActionDefinition';
export * from './ActionType';
export * from './ActiveActionData';
export * from './AvatarActionManager';
export * from './IActionDefinition';
export * from './IActiveActionData';

View File

@ -0,0 +1,37 @@
import { IAssetAlias } from '../../../core';
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;
}
}

View File

@ -0,0 +1,89 @@
import { AdvancedMap, IAssetManager, IGraphicAsset } from '../../../core';
import { AvatarRenderManager } from '../AvatarRenderManager';
import { AssetAlias } from './AssetAlias';
export class AssetAliasCollection
{
private _assets: IAssetManager;
private _aliases: AdvancedMap<string, AssetAlias>;
private _avatarRenderManager: AvatarRenderManager;
private _missingAssetNames: string[];
constructor(renderManager: AvatarRenderManager, assetManager: IAssetManager)
{
this._avatarRenderManager = renderManager;
this._aliases = new AdvancedMap();
this._assets = assetManager;
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.getValues())
{
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.add(name, new AssetAlias(name, alias));
}
}
}
public hasAlias(name: string): boolean
{
const alias = this._aliases.getValue(name);
if(alias) return true;
return false;
}
public getAssetName(k: string): string
{
let linkName = k;
let tries = 5;
while(this.hasAlias(linkName) && (tries >= 0))
{
const alias = this._aliases.getValue(linkName);
linkName = alias.link;
tries--;
}
return linkName;
}
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;
}
}

View File

@ -0,0 +1,2 @@
export * from './AssetAlias';
export * from './AssetAliasCollection';

View File

@ -0,0 +1,58 @@
import { IAssetAnimationAdd } from '../../../core';
export class AddDataContainer
{
private _id: string;
private _align: string;
private _base: string;
private _ink: number;
private _blend: number;
constructor(animation: IAssetAnimationAdd)
{
this._id = (animation.id || '');
this._align = (animation.align || '');
this._base = (animation.base || '');
this._ink = (animation.ink || 0);
this._blend = 0;
const blend = animation.blend;
if(blend && (blend.length > 0))
{
this._blend = parseInt(blend);
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);
}
}

View File

@ -0,0 +1,312 @@
import { AdvancedMap, IAssetAnimation, IAssetAnimationFrame } from '../../../core';
import { AvatarStructure } from '../AvatarStructure';
import { AddDataContainer } from './AddDataContainer';
import { AvatarAnimationLayerData } from './AvatarAnimationLayerData';
import { AvatarDataContainer } from './AvatarDataContainer';
import { DirectionDataContainer } from './DirectionDataContainer';
import { IAnimation } from './IAnimation';
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: AdvancedMap<string, string>;
private _overrideFrames: AdvancedMap<string, AvatarAnimationLayerData[][]>;
private _resetOnToggle: boolean;
constructor(structure: AvatarStructure, animation: IAssetAnimation)
{
this._id = animation.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 = (animation.resetOnToggle || false);
if(animation.sprites && animation.sprites.length)
{
this._spriteData = [];
for(const sprite of animation.sprites) this._spriteData.push(new SpriteDataContainer(this, sprite));
}
if(animation.avatars && animation.avatars.length) this._avatarData = new AvatarDataContainer(animation.avatars[0]);
if(animation.directions && animation.directions.length) this._directionData = new DirectionDataContainer(animation.directions[0]);
if(animation.removes && animation.removes.length)
{
this._removeData = [];
for(const remove of animation.removes) this._removeData.push(remove.id);
}
if(animation.adds && animation.adds.length)
{
this._addData = [];
for(const add of animation.adds) this._addData.push(new AddDataContainer(add));
}
if(animation.overrides && animation.overrides.length)
{
this._overrideFrames = new AdvancedMap();
this._overriddenActions = new AdvancedMap();
for(const override of animation.overrides)
{
const name = override.name;
const value = override.override;
this._overriddenActions.add(value, name);
const frames: AvatarAnimationLayerData[][] = [];
this.parseFrames(frames, override.frames, structure);
this._overrideFrames.add(name, frames);
}
}
this.parseFrames(this._frames, animation.frames, structure);
}
private parseFrames(frames: AvatarAnimationLayerData[][], animationFrame: IAssetAnimationFrame[], structure: AvatarStructure): void
{
if(!animationFrame || !animationFrame.length) return;
for(const frame of animationFrame)
{
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 = structure.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 = structure.getActionDefinition(fx.action);
const layer = new AvatarAnimationLayerData(fx, AvatarAnimationLayerData.FX, definition);
layers.push(layer);
}
}
frames.push(layers);
index++;
}
}
}
public frameCount(frame: string = null): number
{
if(!frame) return this._frames.length;
if(this._overrideFrames)
{
const layerData = this._overrideFrames.getValue(frame);
if(layerData) return layerData.length;
}
return 0;
}
public hasOverriddenActions(): boolean
{
if(!this._overriddenActions) return false;
return (this._overriddenActions.length > 0);
}
public overriddenActionNames(): string[]
{
if(!this._overriddenActions) return null;
const keys: string[] = [];
for(const key of this._overriddenActions.getKeys()) keys.push(key);
return keys;
}
public overridingAction(action: string): string
{
if(!this._overriddenActions) return null;
return this._overriddenActions.getValue(action);
}
private getFrame(frameCount: number, frame: string = null): AvatarAnimationLayerData[]
{
if(frameCount < 0) frameCount = 0;
let layers: AvatarAnimationLayerData[] = [];
if(!frame)
{
if(this._frames.length > 0)
{
layers = this._frames[(frameCount % this._frames.length)];
}
}
else
{
const overrideLayers = this._overrideFrames.getValue(frame);
if(overrideLayers && (overrideLayers.length > 0))
{
layers = overrideLayers[(frameCount % overrideLayers.length)];
}
}
return layers;
}
public getAnimatedBodyPartIds(frameCount: number, frame: string = null): string[]
{
const partIds: string[] = [];
for(const layer of this.getFrame(frameCount, frame))
{
if(layer.type === AvatarAnimationLayerData.BODYPART)
{
partIds.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) partIds.push(_local_5.align);
}
}
}
}
return partIds;
}
public getLayerData(frameCount: number, spriteId: string, frame: string = null): AvatarAnimationLayerData
{
for(const layer of this.getFrame(frameCount, frame))
{
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(id: string): AddDataContainer
{
if(this._addData)
{
for(const data of this._addData)
{
if(data.id === id) return data;
}
}
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;
}
}

View File

@ -0,0 +1,50 @@
import { AdvancedMap, IAssetAnimation } from '../../../core';
import { AvatarStructure } from '../AvatarStructure';
import { Animation } from './Animation';
import { IAnimation } from './IAnimation';
import { IAnimationLayerData } from './IAnimationLayerData';
import { IAnimationManager } from './IAnimationManager';
export class AnimationManager implements IAnimationManager
{
private _animations: AdvancedMap<string, Animation>;
constructor()
{
this._animations = new AdvancedMap();
}
public registerAnimation(structure: AvatarStructure, animations: { [index: string]: IAssetAnimation }): boolean
{
const animationData = animations[Object.keys(animations)[0]];
const animation = new Animation(structure, animationData);
this._animations.add(animationData.name, animation);
return true;
}
public getAnimation(animation: string): Animation
{
const existing = this._animations.getValue(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(): AdvancedMap<string, IAnimation>
{
return this._animations;
}
}

View File

@ -0,0 +1,110 @@
import { AdvancedMap, IAssetAnimationFramePart } from '../../../core';
import { ActiveActionData, IActionDefinition, IActiveActionData } from '../actions';
import { IAnimationLayerData } from './IAnimationLayerData';
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: AdvancedMap<string, string>;
constructor(framePart: IAssetAnimationFramePart, type: string, definition: IActionDefinition)
{
this._id = framePart.id;
this._animationFrame = (framePart.frame || 0);
this._dx = (framePart.dx || 0);
this._dy = (framePart.dy || 0);
this._dz = (framePart.dz || 0);
this._directionOffset = (framePart.dd || 0);
this._type = type;
this._base = (framePart.base || '');
this._items = new AdvancedMap();
if(framePart.items) for(const partItem of framePart.items) this._items.add(partItem.id, partItem.base);
let base = '';
if(this._base !== '') base = this.baseAsInt().toString();
if(definition)
{
this._action = new ActiveActionData(definition.state, this.base);
this._action.definition = definition;
}
}
public get items(): AdvancedMap<string, string>
{
return this._items;
}
private baseAsInt(): number
{
let base = 0;
let index = 0;
while(index < this._base.length)
{
base = (base + this._base.charCodeAt(index));
index++;
}
return base;
}
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;
}
}

View File

@ -0,0 +1,129 @@
import { AdvancedMap, IAssetAnimationAvatar } from '../../../core';
import { IAvatarDataContainer } from './IAvatarDataContainer';
export class AvatarDataContainer implements IAvatarDataContainer
{
private _ink: number;
private _foreGround: number;
private _backGround: number;
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: AdvancedMap<string, number[]>;
private _paletteIsGrayscale: boolean;
constructor(animation: IAssetAnimationAvatar)
{
this._ink = animation.ink;
let foreground = animation.foreground;
let background = animation.background;
foreground = foreground.replace('#', '');
background = background.replace('#', '');
this._foreGround = parseInt(foreground, 16);
this._backGround = parseInt(background, 16);
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._colorMap = this.generatePaletteMapForGrayscale(this._backGround, this._foreGround);
}
public get ink(): number
{
return this._ink;
}
public get reds(): number[]
{
return this._colorMap.getValue('reds');
}
public get greens(): number[]
{
return this._colorMap.getValue('greens');
}
public get blues(): number[]
{
return this._colorMap.getValue('blues');
}
public get alphas(): number[]
{
return this._colorMap.getValue('alphas');
}
public get paletteIsGrayscale(): boolean
{
return this._paletteIsGrayscale;
}
private generatePaletteMapForGrayscale(k: number, _arg_2: number): AdvancedMap<string, number[]>
{
const _local_3 = ((k >> 24) & 0xFF);
const _local_4 = ((k >> 16) & 0xFF);
const _local_5 = ((k >> 8) & 0xFF);
const _local_6 = ((k >> 0) & 0xFF);
const _local_7 = ((_arg_2 >> 24) & 0xFF);
const _local_8 = ((_arg_2 >> 16) & 0xFF);
const _local_9 = ((_arg_2 >> 8) & 0xFF);
const _local_10 = ((_arg_2 >> 0) & 0xFF);
const _local_11 = ((_local_7 - _local_3) / 0xFF);
const _local_12 = ((_local_8 - _local_4) / 0xFF);
const _local_13 = ((_local_9 - _local_5) / 0xFF);
const _local_14 = ((_local_10 - _local_6) / 0xFF);
const _local_15: AdvancedMap<string, number[]> = new AdvancedMap();
const reds: number[] = [];
const greens: number[] = [];
const blues: number[] = [];
const _local_19: number[] = [];
let _local_20 = _local_3;
let _local_21 = _local_4;
let _local_22 = _local_5;
let _local_23 = _local_6;
let _local_24 = 0;
while(_local_24 < 0x0100)
{
if((((_local_21 == _local_4) && (_local_22 == _local_5)) && (_local_23 == _local_6)))
{
_local_20 = 0;
}
_local_20 = (_local_20 + _local_11);
_local_21 = (_local_21 + _local_12);
_local_22 = (_local_22 + _local_13);
_local_23 = (_local_23 + _local_14);
_local_19.push((_local_20 << 24));
reds.push(((((_local_20 << 24) | (_local_21 << 16)) | (_local_22 << 8)) | _local_23));
greens.push(((((_local_20 << 24) | (_local_21 << 16)) | (_local_22 << 8)) | _local_23));
blues.push(((((_local_20 << 24) | (_local_21 << 16)) | (_local_22 << 8)) | _local_23));
_local_24++;
}
_local_15.add('alphas', reds);
_local_15.add('reds', reds);
_local_15.add('greens', greens);
_local_15.add('blues', blues);
return _local_15;
}
}

View File

@ -0,0 +1,16 @@
import { IAssetAnimationDirection } from '../../../core';
export class DirectionDataContainer
{
private _offset: number;
constructor(direction: IAssetAnimationDirection)
{
this._offset = direction.offset;
}
public get offset(): number
{
return this._offset;
}
}

View File

@ -0,0 +1,11 @@
export interface IAnimation
{
hasAvatarData(): boolean;
hasDirectionData(): boolean;
hasAddData(): boolean;
id: string;
spriteData: any;
removeData: any;
addData: any;
resetOnToggle: boolean;
}

View File

@ -0,0 +1,12 @@
import { IActiveActionData } from '../actions';
export interface IAnimationLayerData
{
id: string;
action: IActiveActionData;
animationFrame: number;
dx: number;
dy: number;
dz: number;
dd: number;
}

View File

@ -0,0 +1,10 @@
import { AdvancedMap } from '../../../core';
import { IAnimation } from './IAnimation';
import { IAnimationLayerData } from './IAnimationLayerData';
export interface IAnimationManager
{
animations: AdvancedMap<any, any>;
getAnimation(animation: string): IAnimation;
getLayerData(animation: string, frameCount: number, spriteId: string): IAnimationLayerData;
}

View File

@ -0,0 +1,9 @@
export interface IAvatarDataContainer
{
ink: number;
paletteIsGrayscale: boolean;
reds: number[];
greens: number[];
blues: number[];
alphas: number[];
}

View File

@ -0,0 +1,14 @@
import { IAnimation } from './IAnimation';
export interface ISpriteDataContainer
{
animation: IAnimation;
id: string;
ink: number;
member: string;
hasDirections: boolean;
hasStaticY: boolean;
getDirectionOffsetX(direction: number): number;
getDirectionOffsetY(direction: number): number;
getDirectionOffsetZ(direction: number): number;
}

View File

@ -0,0 +1,96 @@
import { IAssetAnimationSprite } from '../../../core';
import { IAnimation } from './IAnimation';
import { ISpriteDataContainer } from './ISpriteDataContainer';
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(animation: IAnimation, sprite: IAssetAnimationSprite)
{
this._animation = animation;
this._id = sprite.id;
this._ink = sprite.ink;
this._member = sprite.member;
this._hasStaticY = sprite.staticY ? true : false;
this._hasDirections = sprite.directions ? true : false;
this._dx = [];
this._dy = [];
this._dz = [];
const directions = sprite.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;
}
}

View File

@ -0,0 +1,12 @@
export * from './AddDataContainer';
export * from './Animation';
export * from './AnimationManager';
export * from './AvatarAnimationLayerData';
export * from './AvatarDataContainer';
export * from './DirectionDataContainer';
export * from './IAnimation';
export * from './IAnimationLayerData';
export * from './IAnimationManager';
export * from './IAvatarDataContainer';
export * from './ISpriteDataContainer';
export * from './SpriteDataContainer';

View File

@ -0,0 +1,57 @@
import { AdvancedMap } from '../../../core';
import { AvatarImageDirectionCache } from './AvatarImageDirectionCache';
export class AvatarImageActionCache
{
private _cache: AdvancedMap<string, AvatarImageDirectionCache>;
private _lastAccessTime: number;
constructor()
{
this._cache = new AdvancedMap();
this.setLastAccessTime(Date.now());
}
public dispose(): void
{
this.debugInfo('[dispose]');
if(!this._cache) return;
for(const direction of this._cache.getValues())
{
if(direction) direction.dispose();
}
this._cache.reset();
}
public getDirectionCache(k: number): AvatarImageDirectionCache
{
const existing = this._cache.getValue(k.toString());
if(!existing) return null;
return existing;
}
public updateDirectionCache(k: number, _arg_2: AvatarImageDirectionCache): void
{
this._cache.add(k.toString(), _arg_2);
}
public setLastAccessTime(k: number): void
{
this._lastAccessTime = k;
}
public getLastAccessTime(): number
{
return this._lastAccessTime;
}
private debugInfo(k: string): void
{
}
}

View File

@ -0,0 +1,99 @@
import { AdvancedMap } from '../../../core';
import { IActiveActionData } from '../actions';
import { AvatarImageActionCache } from './AvatarImageActionCache';
export class AvatarImageBodyPartCache
{
private _cache: AdvancedMap<string, AvatarImageActionCache>;
private _currentAction: IActiveActionData;
private _currentDirection: number;
private _disposed: boolean;
constructor()
{
this._cache = new AdvancedMap();
}
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.reset();
this._cache = null;
this._disposed = true;
}
}
public disposeActions(k: number, _arg_2: number): void
{
if(!this._cache || this._disposed) return;
for(const key of this._cache.getKeys())
{
const cache = this._cache.getValue(key);
if(!cache) continue;
const _local_3 = cache.getLastAccessTime();
if((_arg_2 - _local_3) >= k)
{
cache.dispose();
this._cache.remove(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.getValue(k.overridingAction);
return this._cache.getValue(k.id);
}
public updateActionCache(k: IActiveActionData, _arg_2: AvatarImageActionCache): void
{
if(k.overridingAction) this._cache.add(k.overridingAction, _arg_2);
else this._cache.add(k.id, _arg_2);
}
private debugInfo(k: string): void
{
}
}

485
src/app/avatar/cache/AvatarImageCache.ts vendored Normal file
View File

@ -0,0 +1,485 @@
import { createCanvas } from 'canvas';
import { AdvancedMap, Point, Rectangle } from '../../../core';
import { IActiveActionData } from '../actions';
import { AssetAliasCollection } from '../alias';
import { AvatarAnimationLayerData } from '../animation';
import { AvatarImageBodyPartContainer } from '../AvatarImageBodyPartContainer';
import { AvatarImagePartContainer } from '../AvatarImagePartContainer';
import { AvatarStructure } from '../AvatarStructure';
import { AvatarDirectionAngle, AvatarFigurePartType, AvatarScaleType, GeometryType } from '../enum';
import { IAvatarImage } from '../IAvatarImage';
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: AdvancedMap<string, AvatarImageBodyPartCache>;
private _canvas: AvatarCanvas;
private _disposed: boolean;
private _geometryType: string;
private _unionImages: ImageData[];
constructor(structure: AvatarStructure, avatarImage: IAvatarImage, aliasCollection: AssetAliasCollection, scale: string)
{
this._structure = structure;
this._avatar = avatarImage;
this._assets = aliasCollection;
this._scale = scale;
this._cache = new AdvancedMap();
this._canvas = null;
this._disposed = false;
this._unionImages = [];
}
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.getValues())
{
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 = Date.now();
if(this._cache)
{
for(const cache of this._cache.getValues())
{
if(!cache) continue;
cache.disposeActions(k, time);
}
}
}
public resetBodyPartCache(k: IActiveActionData): void
{
if(this._cache)
{
for(const cache of this._cache.getValues())
{
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(k: string, frameNumber: number): AvatarImageBodyPartContainer
{
let _local_4 = this.getBodyPartCache(k);
if(!_local_4)
{
_local_4 = new AvatarImageBodyPartCache();
this._cache.add(k, _local_4);
}
let _local_5 = _local_4.getDirection();
let _local_7 = _local_4.getAction();
let frameCount = frameNumber;
if(_local_7.definition.startFromFrameZero) frameCount -= _local_7.startFrame;
let _local_8 = _local_7;
let _local_9: string[] = [];
let _local_10: AdvancedMap<string, string> = new AdvancedMap();
const _local_11 = new Point();
if(!((!(_local_7)) || (!(_local_7.definition))))
{
if(_local_7.definition.isAnimation)
{
let _local_15 = _local_5;
const _local_16 = this._structure.getAnimation(((_local_7.definition.state + '.') + _local_7.actionParameter));
const _local_17 = (frameNumber - _local_7.startFrame);
if(_local_16)
{
const _local_18 = _local_16.getLayerData(_local_17, k, _local_7.overridingAction);
if(_local_18)
{
_local_15 = (_local_5 + _local_18.dd);
if(_local_18.dd < 0)
{
if(_local_15 < 0)
{
_local_15 = (8 + _local_15);
}
else if(_local_15 > 7) _local_15 = (8 - _local_15);
}
else
{
if(_local_15 < 0)
{
_local_15 = (_local_15 + 8);
}
else if(_local_15 > 7) _local_15 = (_local_15 - 8);
}
if(this._scale === AvatarScaleType.LARGE)
{
_local_11.x = _local_18.dx;
_local_11.y = _local_18.dy;
}
else
{
_local_11.x = (_local_18.dx / 2);
_local_11.y = (_local_18.dy / 2);
}
frameCount = _local_18.animationFrame;
if(_local_18.action)
{
_local_7 = _local_18.action;
}
if(_local_18.type === AvatarAnimationLayerData.BODYPART)
{
if(_local_18.action != null)
{
_local_8 = _local_18.action;
}
_local_5 = _local_15;
}
else if(_local_18.type === AvatarAnimationLayerData.FX) _local_5 = _local_15;
_local_10 = _local_18.items;
}
_local_9 = _local_16.removeData;
}
}
}
let _local_12 = _local_4.getActionCache(_local_8);
if(!_local_12)
{
_local_12 = new AvatarImageActionCache();
_local_4.updateActionCache(_local_8, _local_12);
}
let _local_13 = _local_12.getDirectionCache(_local_5);
if(!_local_13)
{
const _local_19 = this._structure.getParts(k, this._avatar.getFigure(), _local_8, this._geometryType, _local_5, _local_9, this._avatar, _local_10);
_local_13 = new AvatarImageDirectionCache(_local_19);
_local_12.updateDirectionCache(_local_5, _local_13);
}
let _local_14 = _local_13.getImageContainer(frameCount);
if(!_local_14)
{
const _local_20 = _local_13.getPartList();
_local_14 = this.renderBodyPart(_local_5, _local_20, frameCount, _local_7);
if(_local_14)
{
if(_local_14.isCacheable) _local_13.updateImageContainer(_local_14, frameCount);
}
else
{
return null;
}
}
const offset = this._structure.getFrameBodyPartOffset(_local_8, _local_5, frameCount, k);
_local_11.x += offset.x;
_local_11.y += offset.y;
_local_14.offset = _local_11;
return _local_14;
}
public getBodyPartCache(k: string): AvatarImageBodyPartCache
{
let existing = this._cache.getValue(k);
if(!existing)
{
existing = new AvatarImageBodyPartCache();
this._cache.add(k, existing);
}
return existing;
}
private renderBodyPart(direction: number, containers: AvatarImagePartContainer[], frameCount: number, _arg_4: 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 = _arg_4.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)
{
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.texture, 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(imageDatas: ImageData[], isFlipped: boolean): ImageData
{
const bounds = new Rectangle();
for(const data of imageDatas) data && bounds.enlarge(data.offsetRect);
const point = new Point(-(bounds.x), -(bounds.y));
const canvas = createCanvas(bounds.width, bounds.height);
const ctx = canvas.getContext('2d');
for(const data of imageDatas)
{
if(!data) continue;
const texture = data.texture;
const color = data.colorTransform;
const flipH = (!(isFlipped && data.flipH) && (isFlipped || data.flipH));
const regPoint = point.clone();
regPoint.x -= data.regPoint.x;
regPoint.y -= data.regPoint.y;
if(isFlipped) regPoint.x = (canvas.width - (regPoint.x + data.rect.width));
let scale = 1;
let tx = 0;
let ty = 0;
if(flipH)
{
scale = -1;
tx = ((data.rect.x + data.rect.width) + regPoint.x);
ty = (regPoint.y - data.rect.y);
}
else
{
scale = 1;
tx = (regPoint.x - data.rect.x);
ty = (regPoint.y - data.rect.y);
}
//ctx.save();
ctx.scale(scale, 1);
//ctx.transform(scale, 1, 0, 0, tx, ty);
ctx.drawImage(texture, tx, ty, texture.width, texture.height);
//ctx.restore();
// set the color
//console.log(canvas.toDataURL());
}
return new ImageData(canvas, new Rectangle(0, 0, canvas.width, canvas.height), point, isFlipped, null);
}
}

View File

@ -0,0 +1,60 @@
import { AdvancedMap } from '../../../core';
import { AvatarImageBodyPartContainer } from '../AvatarImageBodyPartContainer';
import { AvatarImagePartContainer } from '../AvatarImagePartContainer';
export class AvatarImageDirectionCache
{
private _partList: AvatarImagePartContainer[];
private _images: AdvancedMap<string, AvatarImageBodyPartContainer>;
constructor(k: AvatarImagePartContainer[])
{
this._partList = k;
this._images = new AdvancedMap();
}
public dispose(): void
{
for(const image of this._images.getValues()) image && image.dispose();
this._images = null;
}
public getPartList(): AvatarImagePartContainer[]
{
return this._partList;
}
public getImageContainer(k: number): AvatarImageBodyPartContainer
{
const existing = this._images.getValue(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.getValue(name);
if(existing) existing.dispose();
this._images.add(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
{
}
}

59
src/app/avatar/cache/ImageData.ts vendored Normal file
View File

@ -0,0 +1,59 @@
import { Canvas } from 'canvas';
import { Point, Rectangle } from '../../../core';
export class ImageData
{
private _texture: Canvas;
private _rect: Rectangle;
private _regPoint: Point;
private _flipH: boolean;
private _colorTransform: number;
constructor(texture: Canvas, rectangle: Rectangle, _arg_3: Point, flipH: boolean, color: number)
{
this._texture = texture;
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(): Canvas
{
return this._texture;
}
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);
}
}

5
src/app/avatar/cache/index.ts vendored Normal file
View File

@ -0,0 +1,5 @@
export * from './AvatarImageActionCache';
export * from './AvatarImageBodyPartCache';
export * from './AvatarImageCache';
export * from './AvatarImageDirectionCache';
export * from './ImageData';

View File

@ -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

View File

@ -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'
}
]
}
]
}
};

View File

@ -0,0 +1,3 @@
export * from './HabboAvatarAnimations';
export * from './HabboAvatarGeometry';
export * from './HabboAvatarPartSets';

View File

@ -0,0 +1,127 @@
export class AvatarAction
{
public static CARRY_OBJECT = 'cri';
public static DANCE = 'dance';
public static EFFECT = 'fx';
public static EXPRESSION = 'expression';
public static EXPRESSION_BLOW_A_KISS = 'blow';
public static EXPRESSION_CRY = 'cry';
public static EXPRESSION_IDLE = 'idle';
public static EXPRESSION_LAUGH = 'laugh';
public static EXPRESSION_RESPECT = 'respect';
public static EXPRESSION_RIDE_JUMP = 'ridejump';
public static EXPRESSION_SNOWBOARD_OLLIE = 'sbollie';
public static EXPRESSION_SNOWBORD_360 = 'sb360';
public static EXPRESSION_WAVE = 'wave';
public static GESTURE = 'gest';
public static GESTURE_AGGRAVATED = 'agr';
public static GESTURE_SAD = 'sad';
public static GESTURE_SMILE = 'sml';
public static GESTURE_SURPRISED = 'srp';
public static GUIDE_STATUS = 'guide';
public static MUTED = 'muted';
public static PET_GESTURE_BLINK = 'eyb';
public static PET_GESTURE_CRAZY = 'crz';
public static PET_GESTURE_JOY = 'joy';
public static PET_GESTURE_MISERABLE = 'mis';
public static PET_GESTURE_PUZZLED = 'puz';
public static PET_GESTURE_TONGUE = 'tng';
public static PLAYING_GAME = 'playing_game';
public static POSTURE = 'posture';
public static POSTURE_FLOAT = 'float';
public static POSTURE_LAY = 'lay';
public static POSTURE_SIT = 'sit';
public static POSTURE_STAND = 'std';
public static POSTURE_SWIM = 'swim';
public static POSTURE_WALK = 'mv';
public static SIGN = 'sign';
public static SLEEP = 'sleep';
public static SNOWWAR_DIE_BACK = 'swdieback';
public static SNOWWAR_DIE_FRONT = 'swdiefront';
public static SNOWWAR_PICK = 'swpick';
public static SNOWWAR_RUN = 'swrun';
public static SNOWWAR_THROW = 'swthrow';
public static TALK = 'talk';
public static BLINK = 'blink';
public static TYPING = 'typing';
public static USE_OBJECT = 'usei';
public static VOTE = 'vote';
public static GESTURE_MAP = [ '', AvatarAction.GESTURE_SMILE, AvatarAction.GESTURE_AGGRAVATED, AvatarAction.GESTURE_SURPRISED, AvatarAction.GESTURE_SAD, AvatarAction.PET_GESTURE_JOY, AvatarAction.PET_GESTURE_CRAZY, AvatarAction.PET_GESTURE_TONGUE, AvatarAction.PET_GESTURE_BLINK, AvatarAction.PET_GESTURE_MISERABLE, AvatarAction.PET_GESTURE_PUZZLED ];
public static EXPRESSION_MAP = [ '', AvatarAction.EXPRESSION_WAVE, AvatarAction.EXPRESSION_BLOW_A_KISS, AvatarAction.EXPRESSION_LAUGH, AvatarAction.EXPRESSION_CRY, AvatarAction.EXPRESSION_IDLE, AvatarAction.DANCE, AvatarAction.EXPRESSION_RESPECT, AvatarAction.EXPRESSION_SNOWBOARD_OLLIE, AvatarAction.EXPRESSION_SNOWBORD_360, AvatarAction.EXPRESSION_RIDE_JUMP ];
public static getExpressionTimeout(expressionId: number): number
{
expressionId = parseInt(expressionId as any);
switch(expressionId)
{
case 1:
return 5000;
case 2:
return 1400;
case 3:
return 2000;
case 4:
return 2000;
case 5:
return 0;
case 6:
return 700;
case 7:
return 2000;
case 8:
return 1500;
case 9:
return 1500;
case 10:
return 1500;
default:
return 0;
}
}
public static getExpressionId(expression: string): number
{
return AvatarAction.EXPRESSION_MAP.indexOf(expression);
}
public static getExpression(expressionId: number): string
{
if(expressionId > AvatarAction.EXPRESSION_MAP.length) return null;
return AvatarAction.EXPRESSION_MAP[expressionId];
}
public static getGestureId(gesture: string): number
{
return AvatarAction.GESTURE_MAP.indexOf(gesture);
}
public static getGesture(gestureId: number): string
{
if(gestureId > AvatarAction.GESTURE_MAP.length) return null;
return AvatarAction.GESTURE_MAP[gestureId];
}
public static idToAvatarActionState(id: string): string
{
if(id === 'Lay') return 'lay';
if(id === 'Float') return 'float';
if(id === 'Swim') return 'swim';
if(id === 'Sit') return 'sit';
if(id === 'Respect') return 'respect';
if(id === 'Wave') return 'wave';
if(id === 'Idle') return 'idle';
if(id === 'Dance') return 'dance';
if(id === 'UseItem') return 'usei';
if(id === 'CarryItem') return 'cri';
if(id === 'Talk') return 'talk';
if(id === 'Sleep') return 'Sleep';
if(id === 'Move') return 'mv';
return 'std';
}
}

View File

@ -0,0 +1,7 @@
export class AvatarDirectionAngle
{
public static DIRECTION_TO_ANGLE: number[] = [45, 90, 135, 180, 225, 270, 315, 0];
public static DIRECTION_IS_FLIPPED: boolean[] = [false, false, false, false, true, true, true, false];
public static MIN_DIRECTION: number = 0;
public static MAX_DIRECTION: number = 7;
}

View File

@ -0,0 +1,29 @@
export class AvatarFigurePartType
{
public static BODY: string = 'bd';
public static SHOES: string = 'sh';
public static LEGS: string = 'lg';
public static CHEST: string = 'ch';
public static WAIST_ACCESSORY: string = 'wa';
public static CHEST_ACCESSORY: string = 'ca';
public static HEAD: string = 'hd';
public static HAIR: string = 'hr';
public static FACE_ACCESSORY: string = 'fa';
public static EYE_ACCESSORY: string = 'ea';
public static HEAD_ACCESSORY: string = 'ha';
public static HEAD_ACCESSORY_EXTRA: string = 'he';
public static COAT_CHEST: string = 'cc';
public static CHEST_PRINT: string = 'cp';
public static LEFT_HAND_ITEM: string = 'li';
public static LEFT_HAND: string = 'lh';
public static LEFT_SLEEVE: string = 'ls';
public static RIGHT_HAND: string = 'rh';
public static RIGHT_SLEEVE: string = 'rs';
public static FACE: string = 'fc';
public static EYES: string = 'ey';
public static HAIR_BIG: string = 'hrb';
public static RIGHT_HAND_ITEM: string = 'ri';
public static LEFT_COAT_SLEEVE: string = 'lc';
public static RIGHT_COAT_SLEEVE: string = 'rc';
public static FIGURE_SETS: string[] = [ AvatarFigurePartType.SHOES, AvatarFigurePartType.LEGS, AvatarFigurePartType.CHEST, AvatarFigurePartType.WAIST_ACCESSORY, AvatarFigurePartType.CHEST_ACCESSORY, AvatarFigurePartType.HEAD, AvatarFigurePartType.HAIR, AvatarFigurePartType.FACE_ACCESSORY, AvatarFigurePartType.EYE_ACCESSORY, AvatarFigurePartType.HEAD_ACCESSORY, AvatarFigurePartType.HEAD_ACCESSORY_EXTRA, AvatarFigurePartType.COAT_CHEST, AvatarFigurePartType.CHEST_PRINT ];
}

View File

@ -0,0 +1,5 @@
export class AvatarScaleType
{
public static LARGE: string = 'h';
public static SMALL: string = 'sh';
}

View File

@ -0,0 +1,6 @@
export class AvatarSetType
{
public static FULL: string = 'full';
public static HEAD: string = 'head';
public static BODY: string = 'body';
}

View File

@ -0,0 +1,8 @@
export class GeometryType
{
public static VERTICAL: string = 'vertical';
public static SITTING: string = 'sitting';
public static HORIZONTAL: string = 'horizontal';
public static SWIM: string = 'swim';
public static SNOWWARS_HORIZONTAL: string = 'swhorizontal';
}

View File

@ -0,0 +1,7 @@
export class RenderMode
{
public static TOOL: string = 'tool';
public static COMPONENT: string = 'component';
public static ONLINE_TOOL: string = 'online_tool';
public static LOCAL_ONLY: string = 'local_only';
}

View File

@ -0,0 +1,7 @@
export * from './AvatarAction';
export * from './AvatarDirectionAngle';
export * from './AvatarFigurePartType';
export * from './AvatarScaleType';
export * from './AvatarSetType';
export * from './GeometryType';
export * from './RenderMode';

View File

@ -0,0 +1,288 @@
import { AdvancedMap } from '../../../core';
import { IAvatarImage } from '../IAvatarImage';
import { AvatarCanvas } from '../structure';
import { AvatarSet } from './AvatarSet';
import { GeometryBodyPart } from './GeometryBodyPart';
import { Matrix4x4 } from './Matrix4x4';
import { Vector3D } from './Vector3D';
export class AvatarModelGeometry
{
private _camera: Vector3D;
private _avatarSet: AvatarSet;
private _geometryTypes: AdvancedMap<string, AdvancedMap<string, GeometryBodyPart>>;
private _itemIdToBodyPartMap: AdvancedMap<string, AdvancedMap<string, GeometryBodyPart>>;
private _transformation: Matrix4x4;
private _canvases: AdvancedMap<string, AdvancedMap<string, AvatarCanvas>>;
constructor(k: any)
{
this._camera = new Vector3D(0, 0, 10);
this._avatarSet = new AvatarSet(k.avatarSets[0]);
this._geometryTypes = new AdvancedMap();
this._itemIdToBodyPartMap = new AdvancedMap();
this._transformation = new Matrix4x4();
this._canvases = new AdvancedMap();
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: AdvancedMap<string, AvatarCanvas> = new AdvancedMap();
if(canvas.geometries && (canvas.geometries.length > 0))
{
for(const geometry of canvas.geometries)
{
if(!geometry) continue;
const avatarCanvas = new AvatarCanvas(geometry, scale);
geometries.add(avatarCanvas.id, avatarCanvas);
}
}
this._canvases.add(scale, geometries);
}
}
if(k.types && (k.types.length > 0))
{
for(const type of k.types)
{
if(!type) continue;
const bodyParts: AdvancedMap<string, GeometryBodyPart> = new AdvancedMap();
const itemIds: AdvancedMap<string, GeometryBodyPart> = new AdvancedMap();
if(type.bodyParts && (type.bodyParts.length > 0))
{
for(const bodyPart of type.bodyParts)
{
if(!bodyPart) continue;
const geometryBodyPart = new GeometryBodyPart(bodyPart);
bodyParts.add(geometryBodyPart.id, geometryBodyPart);
for(const part of geometryBodyPart.getPartIds(null))
{
itemIds.add(part, geometryBodyPart);
}
}
}
this._geometryTypes.add(type.id, bodyParts);
this._itemIdToBodyPartMap.add(type.id, itemIds);
}
}
}
public removeDynamicItems(k: IAvatarImage): void
{
for(const geometry of this._geometryTypes.getValues())
{
if(!geometry) continue;
for(const part of geometry.getValues())
{
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.getValue(k);
if(!canvas) return null;
return (canvas.getValue(_arg_2) || null);
}
private typeExists(k: string): boolean
{
const existing = this._geometryTypes.getValue(k);
if(existing) return true;
return false;
}
private hasBodyPart(k: string, _arg_2: string): boolean
{
if(this.typeExists(k))
{
const existing = this._geometryTypes.getValue(k);
if(existing && existing.getValue(_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.getValues())
{
if(!part) continue;
types.push(part.id);
}
}
return types;
}
private getBodyPartsOfType(k: string): AdvancedMap<string, GeometryBodyPart>
{
if(this.typeExists(k)) return this._geometryTypes.getValue(k);
return new AdvancedMap();
}
public getBodyPart(k: string, _arg_2: string): GeometryBodyPart
{
return (this.getBodyPartsOfType(k).getValue(_arg_2) || null);
}
public getBodyPartOfItem(k: string, _arg_2: string, _arg_3:IAvatarImage): GeometryBodyPart
{
const itemIds = this._itemIdToBodyPartMap.getValue(k);
if(itemIds)
{
const part = itemIds.getValue(_arg_2);
if(part) return part;
const parts = this.getBodyPartsOfType(k);
if(parts)
{
for(const part of parts.getValues())
{
if(!part) continue;
if(part.hasPart(_arg_2, _arg_3)) return part;
}
}
}
return null;
}
private getBodyPartsInAvatarSet(k: AdvancedMap<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.getValue(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)
{
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).getValue(_arg_2);
this._transformation = Matrix4x4.getYRotationMatrix(_arg_3);
return part.getParts(this._transformation, this._camera, _arg_4, _arg_5);
}
return [];
}
}

View File

@ -0,0 +1,94 @@
import { AdvancedMap } from '../../../core';
export class AvatarSet
{
private _id: string;
private _isMain: boolean;
private _avatarSets: AdvancedMap<string, AvatarSet>;
private _bodyParts: string[];
private _allBodyParts: string[];
constructor(k: any)
{
this._id = k.id;
this._isMain = k.main || false;
this._avatarSets = new AdvancedMap();
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.add(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.getValues())
{
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.getValues())
{
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.getValues())
{
if(!avatarSet) continue;
if(!avatarSet.isMain) continue;
return true;
}
return false;
}
}

View File

@ -0,0 +1,195 @@
import { AdvancedMap } from '../../../core';
import { IAvatarImage } from '../IAvatarImage';
import { GeometryItem } from './GeometryItem';
import { Matrix4x4 } from './Matrix4x4';
import { Node3D } from './Node3D';
import { Vector3D } from './Vector3D';
export class GeometryBodyPart extends Node3D
{
private _id: string;
private _radius: number;
private _parts: AdvancedMap<string, GeometryItem>;
private _dynamicParts: AdvancedMap<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 AdvancedMap();
this._dynamicParts = new AdvancedMap();
if(k.items && (k.items.length > 0))
{
for(const item of k.items)
{
if(!item) continue;
const geometryItem = new GeometryItem(item);
this._parts.add(geometryItem.id, geometryItem);
}
}
}
public getDynamicParts(k: IAvatarImage): GeometryItem[]
{
const existing = this._dynamicParts.getValue(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.getValues())
{
if(!part) continue;
ids.push(part.id);
}
if(k)
{
const existing = this._dynamicParts.getValue(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.remove(k);
return true;
}
public addPart(k: any, _arg_2: IAvatarImage): boolean
{
if(this.hasPart(k.id, _arg_2)) return false;
let existing = this._dynamicParts.getValue(_arg_2);
if(!existing)
{
existing = {};
this._dynamicParts.add(_arg_2, existing);
}
existing[k.id] = new GeometryItem(k, true);
return true;
}
public hasPart(k: string, _arg_2: IAvatarImage): boolean
{
let existingPart = (this._parts.getValue(k) || null);
if(!existingPart && (this._dynamicParts.getValue(_arg_2) !== undefined))
{
existingPart = (this._dynamicParts.getValue(_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.getValues())
{
if(!part) continue;
part.applyTransform(k);
parts.push([ part.getDistance(_arg_2), part ]);
}
const existingDynamic = this._dynamicParts.getValue(_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;
}
}

View File

@ -0,0 +1,55 @@
import { Node3D } from './Node3D';
import { Vector3D } from './Vector3D';
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;
}
}

View File

@ -0,0 +1,133 @@
import { Vector3D } from './Vector3D';
export class Matrix4x4
{
public static IDENTITY:Matrix4x4 = new Matrix4x4(1, 0, 0, 0, 1, 0, 0, 0, 1);
private static TOLERANS: number = 1E-18;
private _data: number[];
constructor(k: number = 0, _arg_2: number = 0, _arg_3: number = 0, _arg_4: number = 0, _arg_5: number = 0, _arg_6: number = 0, _arg_7: number = 0, _arg_8: number = 0, _arg_9: number = 0)
{
this._data = [k, _arg_2, _arg_3, _arg_4, _arg_5, _arg_6, _arg_7, _arg_8, _arg_9];
}
public static getXRotationMatrix(k: number): Matrix4x4
{
const _local_2 = ((k * Math.PI) / 180);
const _local_3 = Math.cos(_local_2);
const _local_4 = Math.sin(_local_2);
return new Matrix4x4(1, 0, 0, 0, _local_3, -(_local_4), 0, _local_4, _local_3);
}
public static getYRotationMatrix(k: number): Matrix4x4
{
const _local_2 = ((k * Math.PI) / 180);
const _local_3 = Math.cos(_local_2);
const _local_4 = Math.sin(_local_2);
return new Matrix4x4(_local_3, 0, _local_4, 0, 1, 0, -(_local_4), 0, _local_3);
}
public static getZRotationMatrix(k: number): Matrix4x4
{
const _local_2 = ((k * Math.PI) / 180);
const _local_3 = Math.cos(_local_2);
const _local_4 = Math.sin(_local_2);
return new Matrix4x4(_local_3, -(_local_4), 0, _local_4, _local_3, 0, 0, 0, 1);
}
public identity(): Matrix4x4
{
this._data = [1, 0, 0, 0, 1, 0, 0, 0, 1];
return this;
}
public vectorMultiplication(k: Vector3D): Vector3D
{
const _local_2 = (((k.x * this._data[0]) + (k.y * this._data[3])) + (k.z * this._data[6]));
const _local_3 = (((k.x * this._data[1]) + (k.y * this._data[4])) + (k.z * this._data[7]));
const _local_4 = (((k.x * this._data[2]) + (k.y * this._data[5])) + (k.z * this._data[8]));
return new Vector3D(_local_2, _local_3, _local_4);
}
public multiply(k:Matrix4x4): Matrix4x4
{
const _local_2 = (((this._data[0] * k.data[0]) + (this._data[1] * k.data[3])) + (this._data[2] * k.data[6]));
const _local_3 = (((this._data[0] * k.data[1]) + (this._data[1] * k.data[4])) + (this._data[2] * k.data[7]));
const _local_4 = (((this._data[0] * k.data[2]) + (this._data[1] * k.data[5])) + (this._data[2] * k.data[8]));
const _local_5 = (((this._data[3] * k.data[0]) + (this._data[4] * k.data[3])) + (this._data[5] * k.data[6]));
const _local_6 = (((this._data[3] * k.data[1]) + (this._data[4] * k.data[4])) + (this._data[5] * k.data[7]));
const _local_7 = (((this._data[3] * k.data[2]) + (this._data[4] * k.data[5])) + (this._data[5] * k.data[8]));
const _local_8 = (((this._data[6] * k.data[0]) + (this._data[7] * k.data[3])) + (this._data[8] * k.data[6]));
const _local_9 = (((this._data[6] * k.data[1]) + (this._data[7] * k.data[4])) + (this._data[8] * k.data[7]));
const _local_10 = (((this._data[6] * k.data[2]) + (this._data[7] * k.data[5])) + (this._data[8] * k.data[8]));
return new Matrix4x4(_local_2, _local_3, _local_4, _local_5, _local_6, _local_7, _local_8, _local_9, _local_10);
}
public scalarMultiply(k: number): void
{
let index = 0;
while(index < this._data.length)
{
this._data[index] = (this._data[index] * k);
index++;
}
}
public rotateX(k: number): Matrix4x4
{
const _local_2 = ((k * Math.PI) / 180);
const _local_3 = Math.cos(_local_2);
const _local_4 = Math.sin(_local_2);
const _local_5 = new Matrix4x4(1, 0, 0, 0, _local_3, -(_local_4), 0, _local_4, _local_3);
return _local_5.multiply(this);
}
public rotateY(k: number): Matrix4x4
{
const _local_2 = ((k * Math.PI) / 180);
const _local_3 = Math.cos(_local_2);
const _local_4 = Math.sin(_local_2);
const _local_5 = new Matrix4x4(_local_3, 0, _local_4, 0, 1, 0, -(_local_4), 0, _local_3);
return _local_5.multiply(this);
}
public rotateZ(k: number): Matrix4x4
{
const _local_2 = ((k * Math.PI) / 180);
const _local_3 = Math.cos(_local_2);
const _local_4 = Math.sin(_local_2);
const _local_5 = new Matrix4x4(_local_3, -(_local_4), 0, _local_4, _local_3, 0, 0, 0, 1);
return _local_5.multiply(this);
}
public skew(): void
{
}
public transpose(): Matrix4x4
{
return new Matrix4x4(this._data[0], this._data[3], this._data[6], this._data[1], this._data[4], this._data[7], this._data[2], this._data[5], this._data[8]);
}
public equals(k: Matrix4x4): boolean
{
return false;
}
public get data(): number[]
{
return this._data;
}
}

View File

@ -0,0 +1,33 @@
import { Matrix4x4 } from './Matrix4x4';
import { Vector3D } from './Vector3D';
export class Node3D
{
private _location: Vector3D;
private _transformedLocation: Vector3D;
private _needsTransformation: boolean;
constructor(k: number, _arg_2: number, _arg_3: number)
{
this._location = new Vector3D(k, _arg_2, _arg_3);
this._transformedLocation = new Vector3D();
this._needsTransformation = false;
if(((!(k == 0)) || (!(_arg_2 == 0))) || (!(_arg_3 == 0))) this._needsTransformation = true;
}
public get location(): Vector3D
{
return this._location;
}
public get transformedLocation(): Vector3D
{
return this._transformedLocation;
}
public applyTransform(k: Matrix4x4): void
{
if(this._needsTransformation) this._transformedLocation = k.vectorMultiplication(this._location);
}
}

View File

@ -0,0 +1,120 @@
export class Vector3D
{
private _x: number;
private _y: number;
private _z: number;
constructor(k: number = 0, _arg_2: number = 0, _arg_3: number = 0)
{
this._x = k;
this._y = _arg_2;
this._z = _arg_3;
}
public static dot(k: Vector3D, _arg_2: Vector3D): number
{
return ((k.x * _arg_2.x) + (k.y * _arg_2.y)) + (k.z * _arg_2.z);
}
public static cross(k: Vector3D, _arg_2: Vector3D): Vector3D
{
const _local_3 = new Vector3D();
_local_3.x = ((k.y * _arg_2.z) - (k.z * _arg_2.y));
_local_3.y = ((k.z * _arg_2.x) - (k.x * _arg_2.z));
_local_3.z = ((k.x * _arg_2.y) - (k.y * _arg_2.x));
return _local_3;
}
public static subtract(k: Vector3D, _arg_2: Vector3D): Vector3D
{
return new Vector3D((k.x - _arg_2.x), (k.y - _arg_2.y), (k.z - _arg_2.z));
}
public dot(k: Vector3D): number
{
return ((this._x * k.x) + (this._y * k.y)) + (this._z * k.z);
}
public cross(k: Vector3D): Vector3D
{
const _local_2 = new Vector3D();
_local_2.x = ((this._y * k.z) - (this._z * k.y));
_local_2.y = ((this._z * k.x) - (this._x * k.z));
_local_2.z = ((this._x * k.y) - (this._y * k.x));
return _local_2;
}
public subtract(k: Vector3D): void
{
this._x = (this._x - k.x);
this._y = (this._y - k.y);
this._z = (this._z - k.z);
}
public add(k: Vector3D): void
{
this._x = (this._x + k.x);
this._y = (this._y + k.y);
this._z = (this._z + k.z);
}
public normalize(): void
{
const k = (1 / this.length());
this._x = (this._x * k);
this._y = (this._y * k);
this._z = (this._z * k);
}
public scaleBy(value: number): void
{
this._x *= value;
this._y *= value;
this._z *= value;
}
public length(): number
{
return Math.sqrt((((this._x * this._x) + (this._y * this._y)) + (this._z * this._z)));
}
public toString(): string
{
return (((((('Vector3D: (' + this._x) + ',') + this._y) + ',') + this._z) + ')');
}
public get x(): number
{
return this._x;
}
public set x(k: number)
{
this._x = k;
}
public get y(): number
{
return this._y;
}
public set y(k: number)
{
this._y = k;
}
public get z(): number
{
return this._z;
}
public set z(k: number)
{
this._z = k;
}
}

View File

@ -0,0 +1,7 @@
export * from './AvatarModelGeometry';
export * from './AvatarSet';
export * from './GeometryBodyPart';
export * from './GeometryItem';
export * from './Matrix4x4';
export * from './Node3D';
export * from './Vector3D';

24
src/app/avatar/index.ts Normal file
View File

@ -0,0 +1,24 @@
export * from './actions';
export * from './alias';
export * from './animation';
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 './cache';
export * from './data';
export * from './EffectAssetDownloadLibrary';
export * from './EffectAssetDownloadManager';
export * from './enum';
export * from './FigureDataContainer';
export * from './geometry';
export * from './IAvatarFigureContainer';
export * from './IAvatarImage';
export * from './IAvatarRenderManager';
export * from './interfaces';
export * from './PlaceHolderAvatarImage';
export * from './structure';

View File

@ -0,0 +1,8 @@
import { IFigureDataPalette } from './IFigureDataPalette';
import { IFigureDataSetType } from './IFigureDataSetType';
export interface IFigureData
{
palettes?: IFigureDataPalette[];
setTypes?: IFigureDataSetType[];
}

View File

@ -0,0 +1,8 @@
export interface IFigureDataColor
{
id?: number;
index?: number;
club?: number;
selectable?: boolean;
hexCode?: string;
}

View File

@ -0,0 +1,4 @@
export interface IFigureDataHiddenLayer
{
partType?: string;
}

View File

@ -0,0 +1,7 @@
import { IFigureDataColor } from './IFigureDataColor';
export interface IFigureDataPalette
{
id?: number;
colors?: IFigureDataColor[];
}

View File

@ -0,0 +1,8 @@
export interface IFigureDataPart
{
id?: number;
type?: string;
colorable?: boolean;
index?: number;
colorindex?: number;
}

View File

@ -0,0 +1,15 @@
import { IFigureDataHiddenLayer } from './IFigureDataHiddenLayer';
import { IFigureDataPart } from './IFigureDataPart';
export interface IFigureDataSet
{
id?: number;
gender?: string;
club?: number;
colorable?: boolean;
selectable?: boolean;
preselectable?: boolean;
sellable?: boolean;
parts?: IFigureDataPart[];
hiddenLayers?: IFigureDataHiddenLayer[];
}

View File

@ -0,0 +1,12 @@
import { IFigureDataSet } from './IFigureDataSet';
export interface IFigureDataSetType
{
type?: string;
paletteId?: number;
mandatory_m_0?: boolean;
mandatory_f_0?: boolean;
mandatory_m_1?: boolean;
mandatory_f_1?: boolean;
sets?: IFigureDataSet[];
}

View File

@ -0,0 +1,7 @@
export * from './IFigureData';
export * from './IFigureDataColor';
export * from './IFigureDataHiddenLayer';
export * from './IFigureDataPalette';
export * from './IFigureDataPart';
export * from './IFigureDataSet';
export * from './IFigureDataSetType';

View File

@ -0,0 +1 @@
export * from './figuredata';

View File

@ -0,0 +1,59 @@
import { AdvancedMap } from '../../../core';
import { IActionDefinition } from '../actions';
import { AnimationAction } from './animation';
import { IFigureSetData } from './IFigureSetData';
export class AvatarAnimationData implements IFigureSetData
{
private _actions: AdvancedMap<string, AnimationAction>;
constructor()
{
this._actions = new AdvancedMap();
}
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.add(newAnimation.id, newAnimation);
}
}
return true;
}
public appendJSON(k: any): boolean
{
for(const _local_2 of k.action)
{
this._actions.add(_local_2.id, new AnimationAction(_local_2));
}
return true;
}
public getAction(action: IActionDefinition): AnimationAction
{
const existing = this._actions.getValue(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;
}
}

View File

@ -0,0 +1,47 @@
import { Point } from '../../../core';
import { AvatarScaleType } from '../enum';
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;
}
}

View File

@ -0,0 +1,132 @@
import { AdvancedMap } from '../../../core';
import { IFigureData } from '../interfaces';
import { IFigurePartSet, IPalette, ISetType, Palette, SetType } from './figure';
import { IFigureSetData } from './IFigureSetData';
import { IStructureData } from './IStructureData';
export class FigureSetData implements IFigureSetData, IStructureData
{
private _palettes: AdvancedMap<string, Palette>;
private _setTypes: AdvancedMap<string, SetType>;
constructor()
{
this._palettes = new AdvancedMap();
this._setTypes = new AdvancedMap();
}
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.add(newPalette.id.toString(), newPalette);
}
for(const set of data.setTypes)
{
const newSet = new SetType(set);
if(!newSet) continue;
this._setTypes.add(newSet.type, newSet);
}
return true;
}
public injectJSON(data: IFigureData): void
{
for(const setType of data.setTypes)
{
const existingSetType = this._setTypes.getValue(setType.type);
if(existingSetType) existingSetType.cleanUp(setType);
else this._setTypes.add(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.getValue(id);
if(!existingPalette) this._palettes.add(id, new Palette(palette));
else existingPalette.append(palette);
}
for(const setType of data.setTypes)
{
const type = setType.type;
const existingSetType = this._setTypes.getValue(type);
if(!existingSetType) this._setTypes.add(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.getValues())
{
if(!set || !set.isMandatory(k, _arg_2)) continue;
types.push(set.type);
}
return types;
}
public getDefaultPartSet(k: string, _arg_2: string): IFigurePartSet
{
const setType = this._setTypes.getValue(k);
if(!setType) return null;
return setType.getDefaultPartSet(_arg_2);
}
public getSetType(k: string): ISetType
{
return (this._setTypes.getValue(k) || null);
}
public getPalette(k: number): IPalette
{
return (this._palettes.getValue(k.toString()) || null);
}
public getFigurePartSet(k: number): IFigurePartSet
{
for(const set of this._setTypes.getValues())
{
const partSet = set.getPartSet(k);
if(!partSet) continue;
return partSet;
}
return null;
}
}

View File

@ -0,0 +1,7 @@
import { IFigureData } from '../interfaces';
export interface IFigureSetData
{
parse(data: any): boolean;
appendJSON(data: IFigureData): boolean;
}

View File

@ -0,0 +1,11 @@
import { IFigureData } from '../interfaces';
import { IFigurePartSet, IPalette, ISetType } from './figure';
export interface IStructureData
{
parse(data: any): boolean;
appendJSON(k: IFigureData): boolean;
getSetType(_arg_1: string): ISetType;
getPalette(_arg_1: number): IPalette;
getFigurePartSet(_arg_1: number): IFigurePartSet;
}

View File

@ -0,0 +1,119 @@
import { AdvancedMap } from '../../../core';
import { ActionDefinition, IActionDefinition } from '../actions';
import { IFigureSetData } from './IFigureSetData';
import { ActivePartSet, PartDefinition } from './parts';
export class PartSetsData implements IFigureSetData
{
private _parts: AdvancedMap<string, PartDefinition>;
private _activePartSets: AdvancedMap<string, ActivePartSet>;
constructor()
{
this._parts = new AdvancedMap();
this._activePartSets = new AdvancedMap();
}
public parse(data: any): boolean
{
if(data.partSet && (data.partSet.length > 0))
{
for(const part of data.partSet)
{
if(!part) continue;
this._parts.add(part.setType, new PartDefinition(part));
}
}
if(data.activePartSets && (data.activePartSets.length > 0))
{
for(const activePart of data.activePartSets)
{
if(!activePart) continue;
this._activePartSets.add(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.add(part.setType, new PartDefinition(part));
}
}
if(data.activePartSets && (data.activePartSets.length > 0))
{
for(const activePart of data.activePartSets)
{
if(!activePart) continue;
this._activePartSets.add(activePart.id, new ActivePartSet(activePart));
}
}
return false;
}
public getActiveParts(k:IActionDefinition): string[]
{
const activePartSet = this._activePartSets.getValue(k.activePartSet);
if(!activePartSet) return [];
return activePartSet.parts;
}
public getPartDefinition(part: string): PartDefinition
{
const existing = this._parts.getValue(part);
if(!existing) return null;
return existing;
}
public addPartDefinition(k: any): PartDefinition
{
const _local_2 = k.setType as string;
let existing = this._parts.getValue(_local_2);
if(!existing)
{
existing = new PartDefinition(k);
this._parts.add(_local_2, existing);
}
return existing;
}
public getActivePartSet(k: ActionDefinition): ActivePartSet
{
const existing = this._activePartSets.getValue(k.activePartSet);
if(!existing) return null;
return existing;
}
public get parts(): AdvancedMap<string, PartDefinition>
{
return this._parts;
}
public get activePartSets(): AdvancedMap<string, ActivePartSet>
{
return this._activePartSets;
}
}

View File

@ -0,0 +1,138 @@
import { AdvancedMap, Point } from '../../../../core';
import { AnimationActionPart } from './AnimationActionPart';
export class AnimationAction
{
public static DEFAULT_OFFSET: Point = new Point(0, 0);
private _id: string;
private _actionParts: AdvancedMap<string, AnimationActionPart>;
private _bodyPartOffsets: AdvancedMap<number, AdvancedMap<number, AdvancedMap<string, Point>>>;
private _frameCount: number;
private _frameIndexes: number[];
constructor(data: any)
{
this._id = data.id;
this._actionParts = new AdvancedMap();
this._bodyPartOffsets = new AdvancedMap();
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.add(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: AdvancedMap<number, AdvancedMap<string, Point>> = new AdvancedMap();
this._bodyPartOffsets.add(frameId, directions);
if(frame.directions && (frame.directions.length > 0))
{
for(const direction of frame.directions)
{
if(!direction) continue;
const directionId = direction.id;
const offsets: AdvancedMap<string, Point> = new AdvancedMap();
directions.add(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.add(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.getValue(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.getValue(frameNumber);
if(!offsets) return AnimationAction.DEFAULT_OFFSET;
const frameOffset = offsets.getValue(frameId);
if(!frameOffset) return AnimationAction.DEFAULT_OFFSET;
const offset = frameOffset.getValue(partId);
if(!offset) return AnimationAction.DEFAULT_OFFSET;
return offset;
}
public get id(): string
{
return this._id;
}
public get parts(): AdvancedMap<string, AnimationActionPart>
{
return this._actionParts;
}
public get frameCount(): number
{
return this._frameCount;
}
}

View File

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

View File

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

View File

@ -0,0 +1,3 @@
export * from './AnimationAction';
export * from './AnimationActionPart';
export * from './AvatarAnimationFrame';

View File

@ -0,0 +1,59 @@
import { IFigureDataPart } from '../../interfaces';
import { IFigurePart } from './IFigurePart';
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 dispose(): void
{
}
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;
}
}

View File

@ -0,0 +1,143 @@
import { IFigureDataSet } from '../../interfaces';
import { FigurePart } from './FigurePart';
import { IFigurePart } from './IFigurePart';
import { IFigurePartSet } from './IFigurePartSet';
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
{
for(const part of this._parts)
{
const figurePart = (part as FigurePart);
figurePart.dispose();
}
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;
}
}

View File

@ -0,0 +1,9 @@
export interface IFigurePart
{
id: number;
type: string;
breed: number;
index: number;
colorLayerIndex: number;
paletteMap: number;
}

View File

@ -0,0 +1,16 @@
import { IFigurePart } from './IFigurePart';
export interface IFigurePartSet
{
getPart(_arg_1: string, _arg_2: number): IFigurePart;
id: number;
type: string;
gender: string;
clubLevel: number;
isColorable: boolean;
isSelectable: boolean;
parts: IFigurePart[];
hiddenLayers: string[];
isPreSelectable: boolean;
isSellable: boolean;
}

View File

@ -0,0 +1,9 @@
import { AdvancedMap } from '../../../../core';
import { IPartColor } from './IPartColor';
export interface IPalette
{
getColor(id: number): IPartColor;
id: number;
colors: AdvancedMap<string, IPartColor>;
}

View File

@ -0,0 +1,8 @@
export interface IPartColor
{
id: number;
index: number;
clubLevel: number;
isSelectable: boolean;
rgb: number;
}

View File

@ -0,0 +1,12 @@
import { AdvancedMap } from '../../../../core';
import { IFigurePartSet } from './IFigurePartSet';
export interface ISetType
{
getPartSet(_arg_1: number): IFigurePartSet;
isMandatory(_arg_1: string, _arg_2: number): boolean;
optionalFromClubLevel(_arg_1: string): number;
type: string;
paletteID: number;
partSets: AdvancedMap<string, IFigurePartSet>;
}

View File

@ -0,0 +1,48 @@
import { AdvancedMap } from '../../../../core';
import { IFigureDataPalette } from '../../interfaces';
import { IPalette } from './IPalette';
import { IPartColor } from './IPartColor';
import { PartColor } from './PartColor';
export class Palette implements IPalette
{
private _id: number;
private _colors: AdvancedMap<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(): AdvancedMap<string, IPartColor>
{
return this._colors;
}
}

Some files were not shown because too many files have changed in this diff Show More