From cf12075382d8cdd777414563d489b26fdd2d8bdf Mon Sep 17 00:00:00 2001 From: billsonnn Date: Wed, 3 Apr 2024 18:06:29 -0400 Subject: [PATCH] Avatar editor updates --- src/api/avatar/AvatarEditorColorSorter.ts | 17 + src/api/avatar/AvatarEditorGridColorItem.ts | 65 --- src/api/avatar/AvatarEditorGridPartItem.ts | 336 ------------ src/api/avatar/AvatarEditorPartSorter.ts | 35 ++ .../avatar/AvatarEditorThumbnailsHelper.ts | 13 +- src/api/avatar/AvatarEditorUtilities.ts | 277 ---------- src/api/avatar/BodyModel.ts | 75 --- src/api/avatar/CategoryBaseModel.ts | 246 --------- src/api/avatar/CategoryData.ts | 487 ------------------ src/api/avatar/FigureData.ts | 287 ----------- src/api/avatar/FigureGenerator.ts | 89 ---- src/api/avatar/HeadModel.ts | 24 - src/api/avatar/IAvatarEditorCategoryModel.ts | 19 - src/api/avatar/LegModel.ts | 22 - src/api/avatar/TorsoModel.ts | 23 - src/api/avatar/index.ts | 14 +- src/common/InfiniteGrid.tsx | 17 +- .../avatar-editor-new/AvatarEditorView.scss | 336 ------------ .../avatar-editor-new/AvatarEditorView.tsx | 114 ---- .../AvatarEditorPaletteSetView.tsx | 33 -- .../AvatarEditorFigurePreviewView.tsx | 40 ++ .../AvatarEditorIcon.tsx | 9 +- .../AvatarEditorModelView.tsx | 52 +- .../avatar-editor/AvatarEditorView.tsx | 258 ++-------- .../{views => }/AvatarEditorWardrobeView.tsx | 57 +- .../AvatarEditorFigureSetItemView.tsx | 11 +- .../figure-set/AvatarEditorFigureSetView.tsx | 24 +- .../figure-set/index.ts | 0 .../avatar-editor/{views => }/index.ts | 1 + .../AvatarEditorPaletteSetItemView.tsx | 12 +- .../AvatarEditorPaletteSetView.tsx | 35 ++ .../palette-set/index.ts | 0 .../views/AvatarEditorFigurePreviewView.tsx | 55 -- .../avatar-editor/views/AvatarEditorIcon.tsx | 30 -- .../views/AvatarEditorModelView.tsx | 88 ---- .../AvatarEditorFigureSetItemView.tsx | 35 -- .../figure-set/AvatarEditorFigureSetView.tsx | 44 -- .../avatar-editor/views/figure-set/index.ts | 2 - .../AvatarEditorPaletteSetItemView.tsx | 32 -- .../AvatarEditorPaletteSetView.tsx | 41 -- .../avatar-editor/views/palette-set/index.ts | 2 - src/components/main/MainView.tsx | 4 +- .../views/bubble-layouts/GetBubbleLayout.tsx | 6 +- .../PurchasableClothingConfirmView.tsx | 6 +- src/hooks/avatar-editor/useAvatarEditor.ts | 226 +++++--- src/hooks/avatar-editor/useFigureData.ts | 66 ++- 46 files changed, 459 insertions(+), 3206 deletions(-) create mode 100644 src/api/avatar/AvatarEditorColorSorter.ts delete mode 100644 src/api/avatar/AvatarEditorGridColorItem.ts delete mode 100644 src/api/avatar/AvatarEditorGridPartItem.ts create mode 100644 src/api/avatar/AvatarEditorPartSorter.ts delete mode 100644 src/api/avatar/AvatarEditorUtilities.ts delete mode 100644 src/api/avatar/BodyModel.ts delete mode 100644 src/api/avatar/CategoryBaseModel.ts delete mode 100644 src/api/avatar/CategoryData.ts delete mode 100644 src/api/avatar/FigureData.ts delete mode 100644 src/api/avatar/FigureGenerator.ts delete mode 100644 src/api/avatar/HeadModel.ts delete mode 100644 src/api/avatar/IAvatarEditorCategoryModel.ts delete mode 100644 src/api/avatar/LegModel.ts delete mode 100644 src/api/avatar/TorsoModel.ts delete mode 100644 src/components/avatar-editor-new/AvatarEditorView.scss delete mode 100644 src/components/avatar-editor-new/AvatarEditorView.tsx delete mode 100644 src/components/avatar-editor-new/views/palette-set/AvatarEditorPaletteSetView.tsx create mode 100644 src/components/avatar-editor/AvatarEditorFigurePreviewView.tsx rename src/components/{avatar-editor-new/views => avatar-editor}/AvatarEditorIcon.tsx (78%) rename src/components/{avatar-editor-new/views => avatar-editor}/AvatarEditorModelView.tsx (60%) rename src/components/avatar-editor/{views => }/AvatarEditorWardrobeView.tsx (50%) rename src/components/{avatar-editor-new/views => avatar-editor}/figure-set/AvatarEditorFigureSetItemView.tsx (82%) rename src/components/{avatar-editor-new/views => avatar-editor}/figure-set/AvatarEditorFigureSetView.tsx (51%) rename src/components/{avatar-editor-new/views => avatar-editor}/figure-set/index.ts (100%) rename src/components/avatar-editor/{views => }/index.ts (86%) rename src/components/{avatar-editor-new/views => avatar-editor}/palette-set/AvatarEditorPaletteSetItemView.tsx (71%) create mode 100644 src/components/avatar-editor/palette-set/AvatarEditorPaletteSetView.tsx rename src/components/{avatar-editor-new/views => avatar-editor}/palette-set/index.ts (100%) delete mode 100644 src/components/avatar-editor/views/AvatarEditorFigurePreviewView.tsx delete mode 100644 src/components/avatar-editor/views/AvatarEditorIcon.tsx delete mode 100644 src/components/avatar-editor/views/AvatarEditorModelView.tsx delete mode 100644 src/components/avatar-editor/views/figure-set/AvatarEditorFigureSetItemView.tsx delete mode 100644 src/components/avatar-editor/views/figure-set/AvatarEditorFigureSetView.tsx delete mode 100644 src/components/avatar-editor/views/figure-set/index.ts delete mode 100644 src/components/avatar-editor/views/palette-set/AvatarEditorPaletteSetItemView.tsx delete mode 100644 src/components/avatar-editor/views/palette-set/AvatarEditorPaletteSetView.tsx delete mode 100644 src/components/avatar-editor/views/palette-set/index.ts diff --git a/src/api/avatar/AvatarEditorColorSorter.ts b/src/api/avatar/AvatarEditorColorSorter.ts new file mode 100644 index 00000000..d64057a5 --- /dev/null +++ b/src/api/avatar/AvatarEditorColorSorter.ts @@ -0,0 +1,17 @@ +import { IPartColor } from '@nitrots/nitro-renderer'; + +export const AvatarEditorColorSorter = (a: IPartColor, b: IPartColor) => +{ + const clubLevelA = (!a ? -1 : a.clubLevel); + const clubLevelB = (!b ? -1 : b.clubLevel); + + if(clubLevelA < clubLevelB) return -1; + + if(clubLevelA > clubLevelB) return 1; + + if(a.index < b.index) return -1; + + if(a.index > b.index) return 1; + + return 0; +} diff --git a/src/api/avatar/AvatarEditorGridColorItem.ts b/src/api/avatar/AvatarEditorGridColorItem.ts deleted file mode 100644 index dee3daef..00000000 --- a/src/api/avatar/AvatarEditorGridColorItem.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { ColorConverter, IPartColor } from '@nitrots/nitro-renderer'; - -export class AvatarEditorGridColorItem -{ - private _partColor: IPartColor; - private _isDisabled: boolean; - private _isHC: boolean; - private _isSelected: boolean; - private _notifier: () => void; - - constructor(partColor: IPartColor, isDisabled: boolean = false) - { - this._partColor = partColor; - this._isDisabled = isDisabled; - this._isHC = (this._partColor.clubLevel > 0); - this._isSelected = false; - } - - public dispose(): void - { - this._partColor = null; - } - - public get partColor(): IPartColor - { - return this._partColor; - } - - public get color(): string - { - return ColorConverter.int2rgb(this._partColor.rgb); - } - - public get isDisabled(): boolean - { - return this._isDisabled; - } - - public get isHC(): boolean - { - return this._isHC; - } - - public get isSelected(): boolean - { - return this._isSelected; - } - - public set isSelected(flag: boolean) - { - this._isSelected = flag; - - if(this.notify) this.notify(); - } - - public get notify(): () => void - { - return this._notifier; - } - - public set notify(notifier: () => void) - { - this._notifier = notifier; - } -} diff --git a/src/api/avatar/AvatarEditorGridPartItem.ts b/src/api/avatar/AvatarEditorGridPartItem.ts deleted file mode 100644 index 45e94729..00000000 --- a/src/api/avatar/AvatarEditorGridPartItem.ts +++ /dev/null @@ -1,336 +0,0 @@ -import { AvatarFigurePartType, GetAvatarRenderManager, IAvatarImageListener, IAvatarRenderManager, IFigurePart, IFigurePartSet, IGraphicAsset, IPartColor, NitroAlphaFilter, NitroContainer, NitroSprite, TextureUtils } from '@nitrots/nitro-renderer'; -import { FigureData } from './FigureData'; - -export class AvatarEditorGridPartItem implements IAvatarImageListener -{ - private static ALPHA_FILTER: NitroAlphaFilter = new NitroAlphaFilter({ alpha: 0.2 }); - private static THUMB_DIRECTIONS: number[] = [ 2, 6, 0, 4, 3, 1 ]; - private static DRAW_ORDER: string[] = [ - AvatarFigurePartType.LEFT_HAND_ITEM, - AvatarFigurePartType.LEFT_HAND, - AvatarFigurePartType.LEFT_SLEEVE, - AvatarFigurePartType.LEFT_COAT_SLEEVE, - AvatarFigurePartType.BODY, - AvatarFigurePartType.SHOES, - AvatarFigurePartType.LEGS, - AvatarFigurePartType.CHEST, - AvatarFigurePartType.CHEST_ACCESSORY, - AvatarFigurePartType.COAT_CHEST, - AvatarFigurePartType.CHEST_PRINT, - AvatarFigurePartType.WAIST_ACCESSORY, - AvatarFigurePartType.RIGHT_HAND, - AvatarFigurePartType.RIGHT_SLEEVE, - AvatarFigurePartType.RIGHT_COAT_SLEEVE, - AvatarFigurePartType.HEAD, - AvatarFigurePartType.FACE, - AvatarFigurePartType.EYES, - AvatarFigurePartType.HAIR, - AvatarFigurePartType.HAIR_BIG, - AvatarFigurePartType.FACE_ACCESSORY, - AvatarFigurePartType.EYE_ACCESSORY, - AvatarFigurePartType.HEAD_ACCESSORY, - AvatarFigurePartType.HEAD_ACCESSORY_EXTRA, - AvatarFigurePartType.RIGHT_HAND_ITEM, - ]; - - private _renderManager: IAvatarRenderManager; - private _partSet: IFigurePartSet; - private _partColors: IPartColor[]; - private _useColors: boolean; - private _isDisabled: boolean; - private _thumbContainer: NitroContainer; - private _imageUrl: string; - private _maxColorIndex: number; - private _isValidFigure: boolean; - private _isHC: boolean; - private _isSellable: boolean; - private _isClear: boolean; - private _isSelected: boolean; - private _disposed: boolean; - private _isInitalized: boolean; - private _notifier: () => void; - - constructor(partSet: IFigurePartSet, partColors: IPartColor[], useColors: boolean = true, isDisabled: boolean = false) - { - this._renderManager = GetAvatarRenderManager(); - this._partSet = partSet; - this._partColors = partColors; - this._useColors = useColors; - this._isDisabled = isDisabled; - this._thumbContainer = null; - this._imageUrl = null; - this._maxColorIndex = 0; - this._isValidFigure = false; - this._isHC = false; - this._isSellable = false; - this._isClear = false; - this._isSelected = false; - this._disposed = false; - this._isInitalized = false; - - if(partSet) - { - const colors = partSet.parts; - - for(const color of colors) this._maxColorIndex = Math.max(this._maxColorIndex, color.colorLayerIndex); - } - } - - public init(): void - { - if(this._isInitalized) return; - - this._isInitalized = true; - - this.update(); - } - - public dispose(): void - { - if(this._disposed) return; - - this._renderManager = null; - this._partSet = null; - this._partColors = null; - this._imageUrl = null; - this._disposed = true; - this._isInitalized = false; - - if(this._thumbContainer) - { - this._thumbContainer.destroy(); - - this._thumbContainer = null; - } - } - - public update(): void - { - this.updateThumbVisualization(); - } - - private analyzeFigure(): boolean - { - if(!this._renderManager || !this._partSet || !this._partSet.parts || !this._partSet.parts.length) return false; - - const figureContainer = this._renderManager.createFigureContainer(((this.partSet.type + '-') + this.partSet.id)); - - if(!this._renderManager.isFigureContainerReady(figureContainer)) - { - this._renderManager.downloadAvatarFigure(figureContainer, this); - - return false; - } - - this._isValidFigure = true; - - return true; - } - - private renderThumb(): NitroContainer - { - if(!this._renderManager || !this._partSet) return null; - - if(!this._isValidFigure) - { - if(!this.analyzeFigure()) return null; - } - - const parts = this._partSet.parts.concat().sort(this.sortByDrawOrder); - const container = new NitroContainer(); - - for(const part of parts) - { - if(!part) continue; - - let asset: IGraphicAsset = null; - let direction = 0; - let hasAsset = false; - - while(!hasAsset && (direction < AvatarEditorGridPartItem.THUMB_DIRECTIONS.length)) - { - const assetName = ((((((((((FigureData.SCALE + '_') + FigureData.STD) + '_') + part.type) + '_') + part.id) + '_') + AvatarEditorGridPartItem.THUMB_DIRECTIONS[direction]) + '_') + FigureData.DEFAULT_FRAME); - - asset = this._renderManager.getAssetByName(assetName); - - if(asset && asset.texture) - { - hasAsset = true; - } - else - { - direction++; - } - } - - if(!hasAsset) continue; - - const x = asset.offsetX; - const y = asset.offsetY; - let partColor: IPartColor = null; - - if(this._useColors && (part.colorLayerIndex > 0)) - { - const color = this._partColors[(part.colorLayerIndex - 1)]; - - if(color) partColor = color; - } - - const sprite = new NitroSprite(asset.texture); - - sprite.position.set(x, y); - - if(partColor) sprite.tint = partColor.rgb; - - container.addChild(sprite); - } - - return container; - } - - private async updateThumbVisualization(): Promise - { - if(!this._isInitalized) return; - - let container = this._thumbContainer; - - if(!container) container = this.renderThumb(); - - if(!container) return; - - if(this._partSet) - { - this._isHC = (this._partSet.clubLevel > 0); - this._isSellable = this._partSet.isSellable; - } - else - { - this._isHC = false; - this._isSellable = false; - } - - if(this._isDisabled) this.setAlpha(container, 0.2); - - this._imageUrl = await TextureUtils.generateImageUrl(container); - - if(this.notify) this.notify(); - } - - private setAlpha(container: NitroContainer, alpha: number): NitroContainer - { - container.filters = [ AvatarEditorGridPartItem.ALPHA_FILTER ]; - - return container; - } - - private sortByDrawOrder(a: IFigurePart, b: IFigurePart): number - { - const indexA = AvatarEditorGridPartItem.DRAW_ORDER.indexOf(a.type); - const indexB = AvatarEditorGridPartItem.DRAW_ORDER.indexOf(b.type); - - if(indexA < indexB) return -1; - - if(indexA > indexB) return 1; - - if(a.index < b.index) return -1; - - if(a.index > b.index) return 1; - - return 0; - } - - public resetFigure(figure: string): void - { - if(!this.analyzeFigure()) return; - - this.update(); - } - - public get disposed(): boolean - { - return this._disposed; - } - - public get id(): number - { - if(!this._partSet) return -1; - - return this._partSet.id; - } - - public get partSet(): IFigurePartSet - { - return this._partSet; - } - - public set partColors(partColors: IPartColor[]) - { - this._partColors = partColors; - - this.update(); - } - - public get isDisabled(): boolean - { - return this._isDisabled; - } - - public set thumbContainer(container: NitroContainer) - { - this._thumbContainer = container; - - this.update(); - } - - public get imageUrl(): string - { - return this._imageUrl; - } - - public get maxColorIndex(): number - { - return this._maxColorIndex; - } - - public get isHC(): boolean - { - return this._isHC; - } - - public get isSellable(): boolean - { - return this._isSellable; - } - - public get isClear(): boolean - { - return this._isClear; - } - - public set isClear(flag: boolean) - { - this._isClear = flag; - } - - public get isSelected(): boolean - { - return this._isSelected; - } - - public set isSelected(flag: boolean) - { - this._isSelected = flag; - - if(this.notify) this.notify(); - } - - public get notify(): () => void - { - return this._notifier; - } - - public set notify(notifier: () => void) - { - this._notifier = notifier; - } -} diff --git a/src/api/avatar/AvatarEditorPartSorter.ts b/src/api/avatar/AvatarEditorPartSorter.ts new file mode 100644 index 00000000..276108f4 --- /dev/null +++ b/src/api/avatar/AvatarEditorPartSorter.ts @@ -0,0 +1,35 @@ +import { IFigurePartSet } from '@nitrots/nitro-renderer'; + +export const AvatarEditorPartSorter = (hcFirst: boolean) => +{ + return (a: { partSet: IFigurePartSet, usesColor: boolean, isClear?: boolean }, b: { partSet: IFigurePartSet, usesColor: boolean, isClear?: boolean }) => + { + const clubLevelA = (!a.partSet ? -1 : a.partSet.clubLevel); + const clubLevelB = (!b.partSet ? -1 : b.partSet.clubLevel); + const isSellableA = (!a.partSet ? false : a.partSet.isSellable); + const isSellableB = (!b.partSet ? false : b.partSet.isSellable); + + if(isSellableA && !isSellableB) return 1; + + if(isSellableB && !isSellableA) return -1; + + if(hcFirst) + { + if(clubLevelA > clubLevelB) return -1; + + if(clubLevelA < clubLevelB) return 1; + } + else + { + if(clubLevelA < clubLevelB) return -1; + + if(clubLevelA > clubLevelB) return 1; + } + + if(a.partSet.id < b.partSet.id) return -1; + + if(a.partSet.id > b.partSet.id) return 1; + + return 0; + } +} diff --git a/src/api/avatar/AvatarEditorThumbnailsHelper.ts b/src/api/avatar/AvatarEditorThumbnailsHelper.ts index 6be9b9be..c5eabaa4 100644 --- a/src/api/avatar/AvatarEditorThumbnailsHelper.ts +++ b/src/api/avatar/AvatarEditorThumbnailsHelper.ts @@ -1,5 +1,4 @@ -import { AvatarFigurePartType, AvatarScaleType, AvatarSetType, GetAssetManager, GetAvatarRenderManager, IFigurePart, IGraphicAsset, IPartColor, NitroAlphaFilter, NitroContainer, NitroSprite, TextureUtils } from '@nitrots/nitro-renderer'; -import { FigureData } from './FigureData'; +import { AvatarFigurePartType, AvatarScaleType, AvatarSetType, GetAssetManager, GetAvatarRenderManager, IFigurePart, IGraphicAsset, IPartColor, NitroAlphaFilter, NitroContainer, NitroRectangle, NitroSprite, TextureUtils } from '@nitrots/nitro-renderer'; import { IAvatarEditorCategoryPartItem } from './IAvatarEditorCategoryPartItem'; export class AvatarEditorThumbnailsHelper @@ -69,7 +68,7 @@ export class AvatarEditorThumbnailsHelper while(!hasAsset && (direction < AvatarEditorThumbnailsHelper.THUMB_DIRECTIONS.length)) { - const assetName = `${ FigureData.SCALE }_${ FigureData.STD }_${ part.type }_${ part.id }_${ AvatarEditorThumbnailsHelper.THUMB_DIRECTIONS[direction] }_${ FigureData.DEFAULT_FRAME }`; + const assetName = `${ AvatarFigurePartType.SCALE }_${ AvatarFigurePartType.STD }_${ part.type }_${ part.id }_${ AvatarEditorThumbnailsHelper.THUMB_DIRECTIONS[direction] }_${ AvatarFigurePartType.DEFAULT_FRAME }`; asset = GetAssetManager().getAsset(assetName); @@ -150,19 +149,23 @@ export class AvatarEditorThumbnailsHelper const resetFigure = async (figure: string) => { const avatarImage = GetAvatarRenderManager().createAvatarImage(figure, AvatarScaleType.LARGE, null, { resetFigure, dispose: null, disposed: false }); + + if(avatarImage.isPlaceholder()) return; + const texture = avatarImage.processAsTexture(AvatarSetType.HEAD, false); const sprite = new NitroSprite(texture); if(isDisabled) sprite.filters = [ AvatarEditorThumbnailsHelper.ALPHA_FILTER ]; const imageUrl = await TextureUtils.generateImageUrl({ - target: sprite + target: sprite, + frame: new NitroRectangle(0, 0, texture.width, texture.height) }); sprite.destroy(); avatarImage.dispose(); - if(!avatarImage.isPlaceholder()) AvatarEditorThumbnailsHelper.THUMBNAIL_CACHE.set(thumbnailKey, imageUrl); + AvatarEditorThumbnailsHelper.THUMBNAIL_CACHE.set(thumbnailKey, imageUrl); resolve(imageUrl); } diff --git a/src/api/avatar/AvatarEditorUtilities.ts b/src/api/avatar/AvatarEditorUtilities.ts deleted file mode 100644 index c7e24680..00000000 --- a/src/api/avatar/AvatarEditorUtilities.ts +++ /dev/null @@ -1,277 +0,0 @@ -import { GetAvatarRenderManager, IPartColor } from '@nitrots/nitro-renderer'; -import { GetClubMemberLevel, GetConfigurationValue } from '../nitro'; -import { AvatarEditorGridColorItem } from './AvatarEditorGridColorItem'; -import { AvatarEditorGridPartItem } from './AvatarEditorGridPartItem'; -import { CategoryBaseModel } from './CategoryBaseModel'; -import { CategoryData } from './CategoryData'; -import { FigureData } from './FigureData'; - -export class AvatarEditorUtilities -{ - private static MAX_PALETTES: number = 2; - - public static CURRENT_FIGURE: FigureData = null; - public static FIGURE_SET_IDS: number[] = []; - public static BOUND_FURNITURE_NAMES: string[] = []; - - public static getGender(gender: string): string - { - switch(gender) - { - case FigureData.MALE: - case 'm': - case 'M': - gender = FigureData.MALE; - break; - case FigureData.FEMALE: - case 'f': - case 'F': - gender = FigureData.FEMALE; - break; - default: - gender = FigureData.MALE; - } - - return gender; - } - - public static hasFigureSetId(setId: number): boolean - { - return (this.FIGURE_SET_IDS.indexOf(setId) >= 0); - } - - public static createCategory(model: CategoryBaseModel, name: string): CategoryData - { - if(!model || !name || !this.CURRENT_FIGURE) return null; - - const partItems: AvatarEditorGridPartItem[] = []; - const colorItems: AvatarEditorGridColorItem[][] = []; - - let i = 0; - - while(i < this.MAX_PALETTES) - { - colorItems.push([]); - - i++; - } - - const setType = GetAvatarRenderManager().structureData.getSetType(name); - - if(!setType) return null; - - const palette = GetAvatarRenderManager().structureData.getPalette(setType.paletteID); - - if(!palette) return null; - - let colorIds = this.CURRENT_FIGURE.getColorIds(name); - - if(!colorIds) colorIds = []; - - const partColors: IPartColor[] = new Array(colorIds.length); - const clubItemsDimmed = this.clubItemsDimmed; - const clubMemberLevel = GetClubMemberLevel(); - - for(const partColor of palette.colors.getValues()) - { - if(partColor.isSelectable && (clubItemsDimmed || (clubMemberLevel >= partColor.clubLevel))) - { - let i = 0; - - while(i < this.MAX_PALETTES) - { - const isDisabled = (clubMemberLevel < partColor.clubLevel); - const colorItem = new AvatarEditorGridColorItem(partColor, isDisabled); - - colorItems[i].push(colorItem); - - i++; - } - - if(name !== FigureData.FACE) - { - let i = 0; - - while(i < colorIds.length) - { - if(partColor.id === colorIds[i]) partColors[i] = partColor; - - i++; - } - } - } - } - - let mandatorySetIds: string[] = []; - - if(clubItemsDimmed) - { - mandatorySetIds = GetAvatarRenderManager().getMandatoryAvatarPartSetIds(this.CURRENT_FIGURE.gender, 2); - } - else - { - mandatorySetIds = GetAvatarRenderManager().getMandatoryAvatarPartSetIds(this.CURRENT_FIGURE.gender, clubMemberLevel); - } - - const isntMandatorySet = (mandatorySetIds.indexOf(name) === -1); - - if(isntMandatorySet) - { - const partItem = new AvatarEditorGridPartItem(null, null, false); - - partItem.isClear = true; - - partItems.push(partItem); - } - - const usesColors = (name !== FigureData.FACE); - const partSets = setType.partSets; - const totalPartSets = partSets.length; - - i = (totalPartSets - 1); - - while(i >= 0) - { - const partSet = partSets.getWithIndex(i); - - let isValidGender = false; - - if(partSet.gender === FigureData.UNISEX) - { - isValidGender = true; - } - - else if(partSet.gender === this.CURRENT_FIGURE.gender) - { - isValidGender = true; - } - - if(partSet.isSelectable && isValidGender && (clubItemsDimmed || (clubMemberLevel >= partSet.clubLevel))) - { - const isDisabled = (clubMemberLevel < partSet.clubLevel); - - let isValid = true; - - if(partSet.isSellable) isValid = this.hasFigureSetId(partSet.id); - - if(isValid) partItems.push(new AvatarEditorGridPartItem(partSet, partColors, usesColors, isDisabled)); - } - - i--; - } - - partItems.sort(this.clubItemsFirst ? this.clubSorter : this.noobSorter); - - // if(this._forceSellableClothingVisibility || GetNitroInstance().getConfiguration("avatareditor.support.sellablefurni", false)) - // { - // _local_31 = (this._manager.windowManager.assets.getAssetByName("camera_zoom_in") as BitmapDataAsset); - // _local_32 = (_local_31.content as BitmapData).clone(); - // _local_33 = (AvatarEditorView._Str_6802.clone() as IWindowContainer); - // _local_33.name = AvatarEditorGridView.GET_MORE; - // _local_7 = new AvatarEditorGridPartItem(_local_33, k, null, null, false); - // _local_7._Str_3093 = _local_32; - // _local_3.push(_local_7); - // } - - i = 0; - - while(i < this.MAX_PALETTES) - { - colorItems[i].sort(this.colorSorter); - - i++; - } - - return new CategoryData(name, partItems, colorItems); - } - - public static clubSorter(a: AvatarEditorGridPartItem, b: AvatarEditorGridPartItem): number - { - const clubLevelA = (!a.partSet ? 9999999999 : a.partSet.clubLevel); - const clubLevelB = (!b.partSet ? 9999999999 : b.partSet.clubLevel); - const isSellableA = (!a.partSet ? false : a.partSet.isSellable); - const isSellableB = (!b.partSet ? false : b.partSet.isSellable); - - if(isSellableA && !isSellableB) return 1; - - if(isSellableB && !isSellableA) return -1; - - if(clubLevelA > clubLevelB) return -1; - - if(clubLevelA < clubLevelB) return 1; - - if(a.partSet.id > b.partSet.id) return -1; - - if(a.partSet.id < b.partSet.id) return 1; - - return 0; - } - - public static colorSorter(a: AvatarEditorGridColorItem, b: AvatarEditorGridColorItem): number - { - const clubLevelA = (!a.partColor ? -1 : a.partColor.clubLevel); - const clubLevelB = (!b.partColor ? -1 : b.partColor.clubLevel); - - if(clubLevelA < clubLevelB) return -1; - - if(clubLevelA > clubLevelB) return 1; - - if(a.partColor.index < b.partColor.index) return -1; - - if(a.partColor.index > b.partColor.index) return 1; - - return 0; - } - - public static noobSorter(a: AvatarEditorGridPartItem, b: AvatarEditorGridPartItem): number - { - const clubLevelA = (!a.partSet ? -1 : a.partSet.clubLevel); - const clubLevelB = (!b.partSet ? -1 : b.partSet.clubLevel); - const isSellableA = (!a.partSet ? false : a.partSet.isSellable); - const isSellableB = (!b.partSet ? false : b.partSet.isSellable); - - if(isSellableA && !isSellableB) return 1; - - if(isSellableB && !isSellableA) return -1; - - if(clubLevelA < clubLevelB) return -1; - - if(clubLevelA > clubLevelB) return 1; - - if(a.partSet.id < b.partSet.id) return -1; - - if(a.partSet.id > b.partSet.id) return 1; - - return 0; - } - - public static avatarSetFirstSelectableColor(name: string): number - { - const setType = GetAvatarRenderManager().structureData.getSetType(name); - - if(!setType) return -1; - - const palette = GetAvatarRenderManager().structureData.getPalette(setType.paletteID); - - if(!palette) return -1; - - for(const color of palette.colors.getValues()) - { - if(!color.isSelectable || (GetClubMemberLevel() < color.clubLevel)) continue; - - return color.id; - } - - return -1; - } - - public static get clubItemsFirst(): boolean - { - return GetConfigurationValue('avatareditor.show.clubitems.first', true); - } - - public static get clubItemsDimmed(): boolean - { - return GetConfigurationValue('avatareditor.show.clubitems.dimmed', true); - } -} diff --git a/src/api/avatar/BodyModel.ts b/src/api/avatar/BodyModel.ts deleted file mode 100644 index d2cf7e4c..00000000 --- a/src/api/avatar/BodyModel.ts +++ /dev/null @@ -1,75 +0,0 @@ -import { AvatarEditorFigureCategory, AvatarScaleType, AvatarSetType, GetAvatarRenderManager } from '@nitrots/nitro-renderer'; -import { AvatarEditorUtilities } from './AvatarEditorUtilities'; -import { CategoryBaseModel } from './CategoryBaseModel'; -import { FigureData } from './FigureData'; - -export class BodyModel extends CategoryBaseModel -{ - private _imageCallBackHandled: boolean = false; - - public init(): void - { - super.init(); - - this.addCategory(FigureData.FACE); - - this._isInitalized = true; - } - - public selectColor(category: string, colorIndex: number, paletteId: number): void - { - super.selectColor(category, colorIndex, paletteId); - - this.updateSelectionsFromFigure(FigureData.FACE); - } - - protected updateSelectionsFromFigure(name: string): void - { - if(!this._categories || !AvatarEditorUtilities.CURRENT_FIGURE) return; - - const category = this._categories.get(name); - - if(!category) return; - - const setId = AvatarEditorUtilities.CURRENT_FIGURE.getPartSetId(name); - - let colorIds = AvatarEditorUtilities.CURRENT_FIGURE.getColorIds(name); - - if(!colorIds) colorIds = []; - - category.selectPartId(setId); - category.selectColorIds(colorIds); - - for(const part of category.parts) - { - const resetFigure = (figure: string) => - { - const figureString = AvatarEditorUtilities.CURRENT_FIGURE.getFigureStringWithFace(part.id); - const avatarImage = GetAvatarRenderManager().createAvatarImage(figureString, AvatarScaleType.LARGE, null, { resetFigure, dispose: null, disposed: false }); - - const sprite = avatarImage.processAsContainer(AvatarSetType.HEAD); - - if(sprite) - { - sprite.y = 10; - - part.thumbContainer = sprite; - - setTimeout(() => avatarImage.dispose(), 0); - } - } - - resetFigure(null); - } - } - - public get canSetGender(): boolean - { - return true; - } - - public get name(): string - { - return AvatarEditorFigureCategory.GENERIC; - } -} diff --git a/src/api/avatar/CategoryBaseModel.ts b/src/api/avatar/CategoryBaseModel.ts deleted file mode 100644 index 34dd9330..00000000 --- a/src/api/avatar/CategoryBaseModel.ts +++ /dev/null @@ -1,246 +0,0 @@ -import { AvatarEditorUtilities } from './AvatarEditorUtilities'; -import { CategoryData } from './CategoryData'; -import { IAvatarEditorCategoryModel } from './IAvatarEditorCategoryModel'; - -export class CategoryBaseModel implements IAvatarEditorCategoryModel -{ - protected _categories: Map; - protected _isInitalized: boolean; - protected _maxPaletteCount: number; - private _disposed: boolean; - - constructor() - { - this._isInitalized = false; - this._maxPaletteCount = 0; - } - - public dispose(): void - { - this._categories = null; - this._disposed = true; - } - - public get disposed(): boolean - { - return this._disposed; - } - - public init(): void - { - if(!this._categories) this._categories = new Map(); - } - - public reset(): void - { - this._isInitalized = false; - - if(this._categories) - { - for(const category of this._categories.values()) (category && category.dispose()); - } - - this._categories = new Map(); - } - - protected addCategory(name: string): void - { - let existing = this._categories.get(name); - - if(existing) return; - - existing = AvatarEditorUtilities.createCategory(this, name); - - if(!existing) return; - - this._categories.set(name, existing); - - this.updateSelectionsFromFigure(name); - } - - protected updateSelectionsFromFigure(figure: string): void - { - const category = this._categories.get(figure); - - if(!category) return; - - const setId = AvatarEditorUtilities.CURRENT_FIGURE.getPartSetId(figure); - - let colorIds = AvatarEditorUtilities.CURRENT_FIGURE.getColorIds(figure); - - if(!colorIds) colorIds = []; - - category.selectPartId(setId); - category.selectColorIds(colorIds); - } - - public hasClubSelectionsOverLevel(level: number): boolean - { - if(!this._categories) return false; - - for(const category of this._categories.values()) - { - if(!category) continue; - - if(category.hasClubSelectionsOverLevel(level)) return true; - } - - return false; - } - - public hasInvalidSelectedItems(ownedItems: number[]): boolean - { - if(!this._categories) return false; - - for(const category of this._categories.values()) - { - if(category.hasInvalidSelectedItems(ownedItems)) return true; - } - - return false; - } - - public stripClubItemsOverLevel(level: number): boolean - { - if(!this._categories) return false; - - let didStrip = false; - - for(const [ name, category ] of this._categories.entries()) - { - let isValid = false; - - if(category.stripClubItemsOverLevel(level)) isValid = true; - - if(category.stripClubColorsOverLevel(level)) isValid = true; - - if(isValid) - { - const partItem = category.getCurrentPart(); - - if(partItem && AvatarEditorUtilities.CURRENT_FIGURE) - { - AvatarEditorUtilities.CURRENT_FIGURE.savePartData(name, partItem.id, category.getSelectedColorIds(), true); - } - - didStrip = true; - } - } - - return didStrip; - } - - public stripInvalidSellableItems(): boolean - { - if(!this._categories) return false; - - let didStrip = false; - - for(const [ name, category ] of this._categories.entries()) - { - const isValid = false; - - // if(category._Str_8360(this._Str_2278.manager.inventory)) _local_6 = true; - - if(isValid) - { - const partItem = category.getCurrentPart(); - - if(partItem && AvatarEditorUtilities.CURRENT_FIGURE) - { - AvatarEditorUtilities.CURRENT_FIGURE.savePartData(name, partItem.id, category.getSelectedColorIds(), true); - } - - didStrip = true; - } - } - - return didStrip; - } - - public selectPart(category: string, partIndex: number): void - { - const categoryData = this._categories.get(category); - - if(!categoryData) return; - - const selectedPartIndex = categoryData.selectedPartIndex; - - categoryData.selectPartIndex(partIndex); - - const partItem = categoryData.getCurrentPart(); - - if(!partItem) return; - - if(partItem.isDisabled) - { - categoryData.selectPartIndex(selectedPartIndex); - - // open hc window - - return; - } - - this._maxPaletteCount = partItem.maxColorIndex; - - AvatarEditorUtilities.CURRENT_FIGURE.savePartData(category, partItem.id, categoryData.getSelectedColorIds(), true); - } - - public selectColor(category: string, colorIndex: number, paletteId: number): void - { - const categoryData = this._categories.get(category); - - if(!categoryData) return; - - const paletteIndex = categoryData.getCurrentColorIndex(paletteId); - - categoryData.selectColorIndex(colorIndex, paletteId); - - const colorItem = categoryData.getSelectedColor(paletteId); - - if(colorItem.isDisabled) - { - categoryData.selectColorIndex(paletteIndex, paletteId); - - // open hc window - - return; - } - - AvatarEditorUtilities.CURRENT_FIGURE.savePartSetColourId(category, categoryData.getSelectedColorIds(), true); - } - - public getCategoryData(category: string): CategoryData - { - if(!this._isInitalized) this.init(); - - if(!this._categories) return null; - - return this._categories.get(category); - } - - public get categories(): Map - { - return this._categories; - } - - public get canSetGender(): boolean - { - return false; - } - - public get maxPaletteCount(): number - { - return (this._maxPaletteCount || 1); - } - - public set maxPaletteCount(count: number) - { - this._maxPaletteCount = count; - } - - public get name(): string - { - return null; - } -} diff --git a/src/api/avatar/CategoryData.ts b/src/api/avatar/CategoryData.ts deleted file mode 100644 index db82f012..00000000 --- a/src/api/avatar/CategoryData.ts +++ /dev/null @@ -1,487 +0,0 @@ -import { IPartColor } from '@nitrots/nitro-renderer'; -import { AvatarEditorGridColorItem } from './AvatarEditorGridColorItem'; -import { AvatarEditorGridPartItem } from './AvatarEditorGridPartItem'; - -export class CategoryData -{ - private _name: string; - private _parts: AvatarEditorGridPartItem[]; - private _palettes: AvatarEditorGridColorItem[][]; - private _selectedPartIndex: number = -1; - private _paletteIndexes: number[]; - - constructor(name: string, partItems: AvatarEditorGridPartItem[], colorItems: AvatarEditorGridColorItem[][]) - { - this._name = name; - this._parts = partItems; - this._palettes = colorItems; - this._selectedPartIndex = -1; - } - - private static defaultColorId(palettes: AvatarEditorGridColorItem[], clubLevel: number): number - { - if(!palettes || !palettes.length) return -1; - - let i = 0; - - while(i < palettes.length) - { - const colorItem = palettes[i]; - - if(colorItem.partColor && (colorItem.partColor.clubLevel <= clubLevel)) - { - return colorItem.partColor.id; - } - - i++; - } - - return -1; - } - - public init(): void - { - for(const part of this._parts) - { - if(!part) continue; - - part.init(); - } - } - - public dispose(): void - { - if(this._parts) - { - for(const part of this._parts) part.dispose(); - - this._parts = null; - } - - if(this._palettes) - { - for(const palette of this._palettes) for(const colorItem of palette) colorItem.dispose(); - - this._palettes = null; - } - - this._selectedPartIndex = -1; - this._paletteIndexes = null; - } - - public selectPartId(partId: number): void - { - if(!this._parts) return; - - let i = 0; - - while(i < this._parts.length) - { - const partItem = this._parts[i]; - - if(partItem.id === partId) - { - this.selectPartIndex(i); - - return; - } - - i++; - } - } - - public selectColorIds(colorIds: number[]): void - { - if(!colorIds || !this._palettes) return; - - this._paletteIndexes = new Array(colorIds.length); - - let i = 0; - - while(i < this._palettes.length) - { - const palette = this.getPalette(i); - - if(palette) - { - let colorId = 0; - - if(colorIds.length > i) - { - colorId = colorIds[i]; - } - else - { - const colorItem = palette[0]; - - if(colorItem && colorItem.partColor) colorId = colorItem.partColor.id; - } - - let j = 0; - - while(j < palette.length) - { - const colorItem = palette[j]; - - if(colorItem.partColor.id === colorId) - { - this._paletteIndexes[i] = j; - - colorItem.isSelected = true; - } - else - { - colorItem.isSelected = false; - } - - j++; - } - } - - i++; - } - - this.updatePartColors(); - } - - public selectPartIndex(partIndex: number): AvatarEditorGridPartItem - { - if(!this._parts) return null; - - if((this._selectedPartIndex >= 0) && (this._parts.length > this._selectedPartIndex)) - { - const partItem = this._parts[this._selectedPartIndex]; - - if(partItem) partItem.isSelected = false; - } - - if(this._parts.length > partIndex) - { - const partItem = this._parts[partIndex]; - - if(partItem) - { - partItem.isSelected = true; - - this._selectedPartIndex = partIndex; - - return partItem; - } - } - - return null; - } - - public selectColorIndex(colorIndex: number, paletteId: number): AvatarEditorGridColorItem - { - const palette = this.getPalette(paletteId); - - if(!palette) return null; - - if(palette.length <= colorIndex) return null; - - this.deselectColorIndex(this._paletteIndexes[paletteId], paletteId); - - this._paletteIndexes[paletteId] = colorIndex; - - const colorItem = palette[colorIndex]; - - if(!colorItem) return null; - - colorItem.isSelected = true; - - this.updatePartColors(); - - return colorItem; - } - - public getCurrentColorIndex(k: number): number - { - return this._paletteIndexes[k]; - } - - private deselectColorIndex(colorIndex: number, paletteIndex: number): void - { - const palette = this.getPalette(paletteIndex); - - if(!palette) return; - - if(palette.length <= colorIndex) return; - - const colorItem = palette[colorIndex]; - - if(!colorItem) return; - - colorItem.isSelected = false; - } - - public getSelectedColorIds(): number[] - { - if(!this._paletteIndexes || !this._paletteIndexes.length) return null; - - if(!this._palettes || !this._palettes.length) return null; - - const palette = this._palettes[0]; - - if(!palette || (!palette.length)) return null; - - const colorItem = palette[0]; - - if(!colorItem || !colorItem.partColor) return null; - - const colorId = colorItem.partColor.id; - const colorIds: number[] = []; - - let i = 0; - - while(i < this._paletteIndexes.length) - { - const paletteSet = this._palettes[i]; - - if(!((!(paletteSet)) || (paletteSet.length <= i))) - { - if(paletteSet.length > this._paletteIndexes[i]) - { - const color = paletteSet[this._paletteIndexes[i]]; - - if(color && color.partColor) - { - colorIds.push(color.partColor.id); - } - else - { - colorIds.push(colorId); - } - } - else - { - colorIds.push(colorId); - } - } - - i++; - } - - const partItem = this.getCurrentPart(); - - if(!partItem) return null; - - return colorIds.slice(0, Math.max(partItem.maxColorIndex, 1)); - } - - private getSelectedColors(): IPartColor[] - { - const partColors: IPartColor[] = []; - - let i = 0; - - while(i < this._paletteIndexes.length) - { - const colorItem = this.getSelectedColor(i); - - if(colorItem) - { - partColors.push(colorItem.partColor); - } - else - { - partColors.push(null); - } - - i++; - } - - return partColors; - } - - public getSelectedColor(paletteId: number): AvatarEditorGridColorItem - { - const palette = this.getPalette(paletteId); - - if(!palette || (palette.length <= this._paletteIndexes[paletteId])) return null; - - return palette[this._paletteIndexes[paletteId]]; - } - - public getSelectedColorId(paletteId: number): number - { - const colorItem = this.getSelectedColor(paletteId); - - if(colorItem && (colorItem.partColor)) return colorItem.partColor.id; - - return 0; - } - - public getPalette(paletteId: number): AvatarEditorGridColorItem[] - { - if(!this._paletteIndexes || !this._palettes || (this._palettes.length <= paletteId)) - { - return null; - } - - return this._palettes[paletteId]; - } - - public getCurrentPart(): AvatarEditorGridPartItem - { - return this._parts[this._selectedPartIndex] as AvatarEditorGridPartItem; - } - - private updatePartColors(): void - { - const partColors = this.getSelectedColors(); - - for(const partItem of this._parts) - { - if(partItem) partItem.partColors = partColors; - } - } - - public hasClubSelectionsOverLevel(level: number): boolean - { - let hasInvalidSelections = false; - - const partColors = this.getSelectedColors(); - - if(partColors) - { - let i = 0; - - while(i < partColors.length) - { - const partColor = partColors[i]; - - if(partColor && (partColor.clubLevel > level)) hasInvalidSelections = true; - - i++; - } - } - - const partItem = this.getCurrentPart(); - - if(partItem && partItem.partSet) - { - const partSet = partItem.partSet; - - if(partSet && (partSet.clubLevel > level)) hasInvalidSelections = true; - } - - return hasInvalidSelections; - } - - public hasInvalidSelectedItems(ownedItems: number[]): boolean - { - const part = this.getCurrentPart(); - - if(!part) return false; - - const partSet = part.partSet; - - if(!partSet || !partSet.isSellable) return; - - return (ownedItems.indexOf(partSet.id) > -1); - } - - public stripClubItemsOverLevel(level: number): boolean - { - const partItem = this.getCurrentPart(); - - if(partItem && partItem.partSet) - { - const partSet = partItem.partSet; - - if(partSet.clubLevel > level) - { - const newPartItem = this.selectPartIndex(0); - - if(newPartItem && !newPartItem.partSet) this.selectPartIndex(1); - - return true; - } - } - - return false; - } - - public stripClubColorsOverLevel(level: number): boolean - { - const colorIds: number[] = []; - const partColors = this.getSelectedColors(); - const colorItems = this.getPalette(0); - - let didStrip = false; - - const colorId = CategoryData.defaultColorId(colorItems, level); - - if(colorId === -1) return false; - - let i = 0; - - while(i < partColors.length) - { - const partColor = partColors[i]; - - if(!partColor) - { - colorIds.push(colorId); - - didStrip = true; - } - else - { - if(partColor.clubLevel > level) - { - colorIds.push(colorId); - - didStrip = true; - } - else - { - colorIds.push(partColor.id); - } - } - - i++; - } - - if(didStrip) this.selectColorIds(colorIds); - - return didStrip; - } - - // public stripInvalidSellableItems(k:IHabboInventory): boolean - // { - // var _local_3:IFigurePartSet; - // var _local_4:AvatarEditorGridPartItem; - // var _local_2:AvatarEditorGridPartItem = this._Str_6315(); - // if (((_local_2) && (_local_2.partSet))) - // { - // _local_3 = _local_2.partSet; - // if (((_local_3.isSellable) && (!(k._Str_14439(_local_3.id))))) - // { - // _local_4 = this._Str_8066(0); - // if (((!(_local_4 == null)) && (_local_4.partSet == null))) - // { - // this._Str_8066(1); - // } - // return true; - // } - // } - // return false; - // } - - public get name(): string - { - return this._name; - } - - public get parts(): AvatarEditorGridPartItem[] - { - return this._parts; - } - - public get selectedPartIndex(): number - { - return this._selectedPartIndex; - } -} diff --git a/src/api/avatar/FigureData.ts b/src/api/avatar/FigureData.ts deleted file mode 100644 index 78014d11..00000000 --- a/src/api/avatar/FigureData.ts +++ /dev/null @@ -1,287 +0,0 @@ -import { AvatarEditorUtilities } from './AvatarEditorUtilities'; - -export class FigureData -{ - private static DEFAULT_DIRECTION: number = 4; - - public static MALE: string = 'M'; - public static FEMALE: string = 'F'; - public static UNISEX: string = 'U'; - public static SCALE: string = 'h'; - public static STD: string = 'std'; - public static DEFAULT_FRAME: string = '0'; - public static FACE: string = 'hd'; - public static HAIR: string = 'hr'; - public static HAT: string = 'ha'; - public static HEAD_ACCESSORIES: string = 'he'; - public static EYE_ACCESSORIES: string = 'ea'; - public static FACE_ACCESSORIES: string = 'fa'; - public static JACKET: string = 'cc'; - public static SHIRT: string = 'ch'; - public static CHEST_ACCESSORIES: string = 'ca'; - public static CHEST_PRINTS: string = 'cp'; - public static TROUSERS: string = 'lg'; - public static SHOES: string = 'sh'; - public static TROUSER_ACCESSORIES: string = 'wa'; - public static SET_TYPES = [ FigureData.FACE, FigureData.HAIR, FigureData.HAT, FigureData.HEAD_ACCESSORIES, FigureData.EYE_ACCESSORIES, FigureData.FACE_ACCESSORIES, FigureData.JACKET, FigureData.SHIRT, FigureData.CHEST_ACCESSORIES, FigureData.CHEST_PRINTS, FigureData.TROUSERS, FigureData.SHOES, FigureData.TROUSERS ]; - - private _data: Map; - private _colors: Map; - private _gender: string = 'M'; - private _direction: number = FigureData.DEFAULT_DIRECTION; - private _avatarEffectType: number = -1; - private _notifier: () => void = null; - - public loadAvatarData(figureString: string, gender: string): void - { - this._data = new Map(); - this._colors = new Map(); - this._gender = gender; - - this.parseFigureString(figureString); - this.updateView(); - } - - private parseFigureString(figure: string): void - { - if(!figure) return; - - const sets = figure.split('.'); - - if(!sets || !sets.length) return; - - for(const set of sets) - { - const parts = set.split('-'); - - if(!parts.length) continue; - - const setType = parts[0]; - const setId = parseInt(parts[1]); - const colorIds: number[] = []; - - let offset = 2; - - while(offset < parts.length) - { - colorIds.push(parseInt(parts[offset])); - - offset++; - } - - if(!colorIds.length) colorIds.push(0); - - this.savePartSetId(setType, setId, false); - this.savePartSetColourId(setType, colorIds, false); - } - } - - public getPartSetId(setType: string): number - { - const existing = this._data.get(setType); - - if(existing !== undefined) return existing; - - return -1; - } - - public getColorIds(setType: string): number[] - { - const existing = this._colors.get(setType); - - if(existing !== undefined) return existing; - - return [ AvatarEditorUtilities.avatarSetFirstSelectableColor(setType) ]; - } - - public getFigureString(): string - { - let figureString = ''; - const setParts: string[] = []; - - for(const [ setType, setId ] of this._data.entries()) - { - const colorIds = this._colors.get(setType); - - let setPart = ((setType + '-') + setId); - - if(colorIds && colorIds.length) - { - let i = 0; - - while(i < colorIds.length) - { - setPart = (setPart + ('-' + colorIds[i])); - - i++; - } - } - - setParts.push(setPart); - } - - let i = 0; - - while(i < setParts.length) - { - figureString = (figureString + setParts[i]); - - if(i < (setParts.length - 1)) figureString = (figureString + '.'); - - i++; - } - - return figureString; - } - - public savePartData(setType: string, partId: number, colorIds: number[], update: boolean = false): void - { - this.savePartSetId(setType, partId, update); - this.savePartSetColourId(setType, colorIds, update); - } - - private savePartSetId(setType: string, partId: number, update: boolean = true): void - { - switch(setType) - { - case FigureData.FACE: - case FigureData.HAIR: - case FigureData.HAT: - case FigureData.HEAD_ACCESSORIES: - case FigureData.EYE_ACCESSORIES: - case FigureData.FACE_ACCESSORIES: - case FigureData.SHIRT: - case FigureData.JACKET: - case FigureData.CHEST_ACCESSORIES: - case FigureData.CHEST_PRINTS: - case FigureData.TROUSERS: - case FigureData.SHOES: - case FigureData.TROUSER_ACCESSORIES: - if(partId >= 0) - { - this._data.set(setType, partId); - } - else - { - this._data.delete(setType); - } - break; - } - - if(update) this.updateView(); - } - - public savePartSetColourId(setType: string, colorIds: number[], update: boolean = true): void - { - switch(setType) - { - case FigureData.FACE: - case FigureData.HAIR: - case FigureData.HAT: - case FigureData.HEAD_ACCESSORIES: - case FigureData.EYE_ACCESSORIES: - case FigureData.FACE_ACCESSORIES: - case FigureData.SHIRT: - case FigureData.JACKET: - case FigureData.CHEST_ACCESSORIES: - case FigureData.CHEST_PRINTS: - case FigureData.TROUSERS: - case FigureData.SHOES: - case FigureData.TROUSER_ACCESSORIES: - this._colors.set(setType, colorIds); - break; - } - - if(update) this.updateView(); - } - - public getFigureStringWithFace(k: number, override = true): string - { - let figureString = ''; - - const setTypes: string[] = [ FigureData.FACE ]; - const figureSets: string[] = []; - - for(const setType of setTypes) - { - const colors = this._colors.get(setType); - - if(!colors) continue; - - let setId = this._data.get(setType); - - if((setType === FigureData.FACE) && override) setId = k; - - let figureSet = ((setType + '-') + setId); - - if(setId >= 0) - { - let i = 0; - - while(i < colors.length) - { - figureSet = (figureSet + ('-' + colors[i])); - - i++; - } - } - - figureSets.push(figureSet); - } - - let i = 0; - - while(i < figureSets.length) - { - figureString = (figureString + figureSets[i]); - - if(i < (figureSets.length - 1)) figureString = (figureString + '.'); - - i++; - } - - return figureString; - } - - public updateView(): void - { - if(this.notify) this.notify(); - } - - public get gender(): string - { - return this._gender; - } - - public get direction(): number - { - return this._direction; - } - - public set direction(direction: number) - { - this._direction = direction; - - this.updateView(); - } - - public set avatarEffectType(k: number) - { - this._avatarEffectType = k; - } - - public get avatarEffectType(): number - { - return this._avatarEffectType; - } - - public get notify(): () => void - { - return this._notifier; - } - - public set notify(notifier: () => void) - { - this._notifier = notifier; - } -} diff --git a/src/api/avatar/FigureGenerator.ts b/src/api/avatar/FigureGenerator.ts deleted file mode 100644 index 6fc00932..00000000 --- a/src/api/avatar/FigureGenerator.ts +++ /dev/null @@ -1,89 +0,0 @@ -import { AvatarFigureContainer, GetAvatarRenderManager, IFigurePartSet, IPalette, IPartColor, SetType } from '@nitrots/nitro-renderer'; -import { Randomizer } from '../utils'; -import { FigureData } from './FigureData'; - -function getTotalColors(partSet: IFigurePartSet): number -{ - const parts = partSet.parts; - - let totalColors = 0; - - for(const part of parts) totalColors = Math.max(totalColors, part.colorLayerIndex); - - return totalColors; -} - -function getRandomSetTypes(requiredSets: string[], options: string[]): string[] -{ - options = options.filter(option => (requiredSets.indexOf(option) === -1)); - - return [ ...requiredSets, ...Randomizer.getRandomElements(options, (Randomizer.getRandomNumber(options.length) + 1)) ]; -} - -function getRandomPartSet(setType: SetType, gender: string, clubLevel: number = 0, figureSetIds: number[] = []): IFigurePartSet -{ - if(!setType) return null; - - const options = setType.partSets.getValues().filter(option => - { - if(!option.isSelectable || ((option.gender !== 'U') && (option.gender !== gender)) || (option.clubLevel > clubLevel) || (option.isSellable && (figureSetIds.indexOf(option.id) === -1))) return null; - - return option; - }); - - if(!options || !options.length) return null; - - return Randomizer.getRandomElement(options); -} - -function getRandomColors(palette: IPalette, partSet: IFigurePartSet, clubLevel: number = 0): IPartColor[] -{ - if(!palette) return []; - - const options = palette.colors.getValues().filter(option => - { - if(!option.isSelectable || (option.clubLevel > clubLevel)) return null; - - return option; - }); - - if(!options || !options.length) return null; - - return Randomizer.getRandomElements(options, getTotalColors(partSet)); -} - -export function generateRandomFigure(figureData: FigureData, gender: string, clubLevel: number = 0, figureSetIds: number[] = [], ignoredSets: string[] = []): string -{ - const structure = GetAvatarRenderManager().structure; - const figureContainer = new AvatarFigureContainer(''); - const requiredSets = getRandomSetTypes(structure.getMandatorySetTypeIds(gender, clubLevel), FigureData.SET_TYPES); - - for(const setType of ignoredSets) - { - const partSetId = figureData.getPartSetId(setType); - const colors = figureData.getColorIds(setType); - - figureContainer.updatePart(setType, partSetId, colors); - } - - for(const type of requiredSets) - { - if(figureContainer.hasPartType(type)) continue; - - const setType = (structure.figureData.getSetType(type) as SetType); - const selectedSet = getRandomPartSet(setType, gender, clubLevel, figureSetIds); - - if(!selectedSet) continue; - - let selectedColors: number[] = []; - - if(selectedSet.isColorable) - { - selectedColors = getRandomColors(structure.figureData.getPalette(setType.paletteID), selectedSet, clubLevel).map(color => color.id); - } - - figureContainer.updatePart(setType.type, selectedSet.id, selectedColors); - } - - return figureContainer.getFigureString(); -} diff --git a/src/api/avatar/HeadModel.ts b/src/api/avatar/HeadModel.ts deleted file mode 100644 index 5dc30cdb..00000000 --- a/src/api/avatar/HeadModel.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { AvatarEditorFigureCategory } from '@nitrots/nitro-renderer'; -import { CategoryBaseModel } from './CategoryBaseModel'; -import { FigureData } from './FigureData'; - -export class HeadModel extends CategoryBaseModel -{ - public init(): void - { - super.init(); - - this.addCategory(FigureData.HAIR); - this.addCategory(FigureData.HAT); - this.addCategory(FigureData.HEAD_ACCESSORIES); - this.addCategory(FigureData.EYE_ACCESSORIES); - this.addCategory(FigureData.FACE_ACCESSORIES); - - this._isInitalized = true; - } - - public get name(): string - { - return AvatarEditorFigureCategory.HEAD; - } -} diff --git a/src/api/avatar/IAvatarEditorCategoryModel.ts b/src/api/avatar/IAvatarEditorCategoryModel.ts deleted file mode 100644 index dc9affae..00000000 --- a/src/api/avatar/IAvatarEditorCategoryModel.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { CategoryData } from './CategoryData'; - -export interface IAvatarEditorCategoryModel -{ - init(): void; - dispose(): void; - reset(): void; - getCategoryData(category: string): CategoryData; - selectPart(category: string, partIndex: number): void; - selectColor(category: string, colorIndex: number, paletteId: number): void; - hasClubSelectionsOverLevel(level: number): boolean; - hasInvalidSelectedItems(ownedItems: number[]): boolean; - stripClubItemsOverLevel(level: number): boolean; - stripInvalidSellableItems(): boolean; - categories: Map; - canSetGender: boolean; - maxPaletteCount: number; - name: string; -} diff --git a/src/api/avatar/LegModel.ts b/src/api/avatar/LegModel.ts deleted file mode 100644 index 56339302..00000000 --- a/src/api/avatar/LegModel.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { AvatarEditorFigureCategory } from '@nitrots/nitro-renderer'; -import { CategoryBaseModel } from './CategoryBaseModel'; -import { FigureData } from './FigureData'; - -export class LegModel extends CategoryBaseModel -{ - public init(): void - { - super.init(); - - this.addCategory(FigureData.TROUSERS); - this.addCategory(FigureData.SHOES); - this.addCategory(FigureData.TROUSER_ACCESSORIES); - - this._isInitalized = true; - } - - public get name(): string - { - return AvatarEditorFigureCategory.LEGS; - } -} diff --git a/src/api/avatar/TorsoModel.ts b/src/api/avatar/TorsoModel.ts deleted file mode 100644 index 43e48cf7..00000000 --- a/src/api/avatar/TorsoModel.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { AvatarEditorFigureCategory } from '@nitrots/nitro-renderer'; -import { CategoryBaseModel } from './CategoryBaseModel'; -import { FigureData } from './FigureData'; - -export class TorsoModel extends CategoryBaseModel -{ - public init(): void - { - super.init(); - - this.addCategory(FigureData.SHIRT); - this.addCategory(FigureData.CHEST_PRINTS); - this.addCategory(FigureData.JACKET); - this.addCategory(FigureData.CHEST_ACCESSORIES); - - this._isInitalized = true; - } - - public get name(): string - { - return AvatarEditorFigureCategory.TORSO; - } -} diff --git a/src/api/avatar/index.ts b/src/api/avatar/index.ts index acf945b7..415185e9 100644 --- a/src/api/avatar/index.ts +++ b/src/api/avatar/index.ts @@ -1,16 +1,6 @@ export * from './AvatarEditorAction'; -export * from './AvatarEditorGridColorItem'; -export * from './AvatarEditorGridPartItem'; +export * from './AvatarEditorColorSorter'; +export * from './AvatarEditorPartSorter'; export * from './AvatarEditorThumbnailsHelper'; -export * from './AvatarEditorUtilities'; -export * from './BodyModel'; -export * from './CategoryBaseModel'; -export * from './CategoryData'; -export * from './FigureData'; -export * from './FigureGenerator'; -export * from './HeadModel'; export * from './IAvatarEditorCategory'; -export * from './IAvatarEditorCategoryModel'; export * from './IAvatarEditorCategoryPartItem'; -export * from './LegModel'; -export * from './TorsoModel'; diff --git a/src/common/InfiniteGrid.tsx b/src/common/InfiniteGrid.tsx index 0103cafa..4c791a3e 100644 --- a/src/common/InfiniteGrid.tsx +++ b/src/common/InfiniteGrid.tsx @@ -8,19 +8,20 @@ interface InfiniteGridProps rows: T[]; columnCount: number; overscan?: number; - itemRender?: (item: T) => ReactElement; + estimateSize?: number; + itemRender?: (item: T, index?: number) => ReactElement; } export const InfiniteGrid: FC = props => { - const { rows = [], columnCount = 4, overscan = 5, itemRender = null } = props; + const { rows = [], columnCount = 4, overscan = 5, estimateSize = 45, itemRender = null } = props; const parentRef = useRef(null); const virtualizer = useVirtualizer({ count: Math.ceil(rows.length / columnCount), overscan, getScrollElement: () => parentRef.current, - estimateSize: () => 45, + estimateSize: () => estimateSize }); useEffect(() => @@ -58,7 +59,7 @@ export const InfiniteGrid: FC = props => style={ { display: 'grid', gap: '0.25rem', - minHeight: virtualRow.index === 0 ? 45 : virtualRow.size, + minHeight: virtualRow.index === 0 ? estimateSize : virtualRow.size, gridTemplateColumns: `repeat(${ columnCount }, 1fr)` } }> { Array.from(Array(columnCount)).map((e,i) => @@ -67,8 +68,12 @@ export const InfiniteGrid: FC = props => if(!item) return ; - - return itemRender(item); + + return ( + + { itemRender(item, i) } + + ); }) } )) } diff --git a/src/components/avatar-editor-new/AvatarEditorView.scss b/src/components/avatar-editor-new/AvatarEditorView.scss deleted file mode 100644 index 22b873d4..00000000 --- a/src/components/avatar-editor-new/AvatarEditorView.scss +++ /dev/null @@ -1,336 +0,0 @@ -.nitro-avatar-editor-spritesheet { - background: url('@/assets/images/avatareditor/avatar-editor-spritesheet.png') transparent no-repeat; - - &.arrow-left-icon { - width: 28px; - height: 21px; - background-position: -226px -131px; - } - - &.arrow-right-icon { - width: 28px; - height: 21px; - background-position: -226px -162px; - } - - &.ca-icon { - width: 25px; - height: 25px; - background-position: -226px -61px; - - &.selected { - width: 25px; - height: 25px; - background-position: -226px -96px; - } - } - - &.cc-icon { - width: 31px; - height: 29px; - background-position: -145px -5px; - - &.selected { - width: 31px; - height: 29px; - background-position: -145px -44px; - } - } - - &.ch-icon { - width: 29px; - height: 24px; - background-position: -186px -39px; - - &.selected { - width: 29px; - height: 24px; - background-position: -186px -73px; - } - } - - &.clear-icon { - width: 27px; - height: 27px; - background-position: -145px -157px; - } - - &.cp-icon { - width: 30px; - height: 24px; - background-position: -145px -264px; - - &.selected { - width: 30px; - height: 24px; - background-position: -186px -5px; - } - } - - - &.ea-icon { - width: 35px; - height: 16px; - background-position: -226px -193px; - - &.selected { - width: 35px; - height: 16px; - background-position: -226px -219px; - } - } - - &.fa-icon { - width: 27px; - height: 20px; - background-position: -186px -137px; - - &.selected { - width: 27px; - height: 20px; - background-position: -186px -107px; - } - } - - &.female-icon { - width: 18px; - height: 27px; - background-position: -186px -202px; - - &.selected { - width: 18px; - height: 27px; - background-position: -186px -239px; - } - } - - &.ha-icon { - width: 25px; - height: 22px; - background-position: -226px -245px; - - &.selected { - width: 25px; - height: 22px; - background-position: -226px -277px; - } - } - - &.he-icon { - width: 31px; - height: 27px; - background-position: -145px -83px; - - &.selected { - width: 31px; - height: 27px; - background-position: -145px -120px; - } - } - - &.hr-icon { - width: 29px; - height: 25px; - background-position: -145px -194px; - - &.selected { - width: 29px; - height: 25px; - background-position: -145px -229px; - } - } - - &.lg-icon { - width: 19px; - height: 20px; - background-position: -303px -45px; - - &.selected { - width: 19px; - height: 20px; - background-position: -303px -75px; - } - } - - &.loading-icon { - width: 21px; - height: 25px; - background-position: -186px -167px; - } - - - &.male-icon { - width: 21px; - height: 21px; - background-position: -186px -276px; - - &.selected { - width: 21px; - height: 21px; - background-position: -272px -5px; - } - } - - - &.sellable-icon { - width: 17px; - height: 15px; - background-position: -303px -105px; - } - - - &.sh-icon { - width: 37px; - height: 10px; - background-position: -303px -5px; - - &.selected { - width: 37px; - height: 10px; - background-position: -303px -25px; - } - } - - - &.spotlight-icon { - width: 130px; - height: 305px; - background-position: -5px -5px; - } - - - &.wa-icon { - width: 36px; - height: 18px; - background-position: -226px -5px; - - &.selected { - width: 36px; - height: 18px; - background-position: -226px -33px; - } - } -} - -.nitro-avatar-editor-wardrobe-figure-preview { - background-color: $pale-sky; - overflow: hidden; - z-index: 1; - - .avatar-image { - position: absolute; - bottom: -15px; - margin: 0 auto; - z-index: 4; - } - - .avatar-shadow { - position: absolute; - left: 0; - right: 0; - bottom: 25px; - width: 40px; - height: 20px; - margin: 0 auto; - border-radius: 100%; - background-color: rgba(0, 0, 0, 0.20); - z-index: 2; - } - - &:after { - position: absolute; - content: ''; - top: 75%; - bottom: 0; - left: 0; - right: 0; - border-radius: 50%; - background-color: $pale-sky; - box-shadow: 0 0 8px 2px rgba($white,.6); - transform: scale(2); - } - - .button-container { - position: absolute; - bottom: 0; - z-index: 5; - } -} - -.nitro-avatar-editor { - width: $avatar-editor-width; - height: $avatar-editor-height; - - .category-item { - height: 40px; - } - - .figure-preview-container { - position: relative; - height: 100%; - background-color: $pale-sky; - overflow: hidden; - z-index: 1; - - .arrow-container { - position: absolute; - width: 100%; - margin: 0 auto; - padding: 0 10px; - display: flex; - justify-content: space-between; - bottom: 12px; - z-index: 5; - - .icon { - cursor: pointer; - } - } - - .avatar-image { - position: absolute; - left: 0; - right: 0; - bottom: 50px; - margin: 0 auto; - z-index: 4; - } - - .avatar-spotlight { - position: absolute; - top: -10px; - left: 0; - right: 0; - margin: 0 auto; - opacity: 0.3; - pointer-events: none; - z-index: 3; - } - - .avatar-shadow { - position: absolute; - left: 0; - right: 0; - bottom: 15px; - width: 70px; - height: 30px; - margin: 0 auto; - border-radius: 100%; - background-color: rgba(0, 0, 0, 0.20); - z-index: 2; - } - - &:after { - position: absolute; - content: ''; - top: 75%; - bottom: 0; - left: 0; - right: 0; - border-radius: 50%; - background-color: $pale-sky; - box-shadow: 0 0 8px 2px rgba($white,.6); - transform: scale(2); - } - } -} diff --git a/src/components/avatar-editor-new/AvatarEditorView.tsx b/src/components/avatar-editor-new/AvatarEditorView.tsx deleted file mode 100644 index eb1973d1..00000000 --- a/src/components/avatar-editor-new/AvatarEditorView.tsx +++ /dev/null @@ -1,114 +0,0 @@ -import { AddLinkEventTracker, AvatarEditorFigureCategory, ILinkEventTracker, RemoveLinkEventTracker } from '@nitrots/nitro-renderer'; -import { FC, useEffect, useState } from 'react'; -import { FaDice, FaTrash, FaUndo } from 'react-icons/fa'; -import { AvatarEditorAction, LocalizeText } from '../../api'; -import { Button, ButtonGroup, Column, Grid, NitroCardContentView, NitroCardHeaderView, NitroCardTabsItemView, NitroCardTabsView, NitroCardView } from '../../common'; -import { useAvatarEditor } from '../../hooks'; -import { AvatarEditorModelView } from './views/AvatarEditorModelView'; - -const DEFAULT_MALE_FIGURE: string = 'hr-100.hd-180-7.ch-215-66.lg-270-79.sh-305-62.ha-1002-70.wa-2007'; -const DEFAULT_FEMALE_FIGURE: string = 'hr-515-33.hd-600-1.ch-635-70.lg-716-66-62.sh-735-68'; - -export const AvatarEditorNewView: FC<{}> = props => -{ - const [ isVisible, setIsVisible ] = useState(false); - const { setIsVisible: setEditorVisibility, avatarModels, activeModelKey, setActiveModelKey } = useAvatarEditor(); - - const processAction = (action: string) => - { - switch(action) - { - case AvatarEditorAction.ACTION_CLEAR: - return; - case AvatarEditorAction.ACTION_RESET: - return; - case AvatarEditorAction.ACTION_RANDOMIZE: - return; - case AvatarEditorAction.ACTION_SAVE: - return; - } - } - - useEffect(() => - { - const linkTracker: ILinkEventTracker = { - linkReceived: (url: string) => - { - const parts = url.split('/'); - - if(parts.length < 2) return; - - switch(parts[1]) - { - case 'show': - setIsVisible(true); - return; - case 'hide': - setIsVisible(false); - return; - case 'toggle': - setIsVisible(prevValue => !prevValue); - return; - } - }, - eventUrlPrefix: 'avatar-editor/' - }; - - AddLinkEventTracker(linkTracker); - - return () => RemoveLinkEventTracker(linkTracker); - }, []); - - useEffect(() => - { - setEditorVisibility(isVisible) - }, [ isVisible, setEditorVisibility ]); - - if(!isVisible) return null; - - return ( - - setIsVisible(false) } /> - - { Object.keys(avatarModels).map(modelKey => - { - const isActive = (activeModelKey === modelKey); - - return ( - setActiveModelKey(modelKey) }> - { LocalizeText(`avatareditor.category.${ modelKey }`) } - - ); - }) } - - - - - { ((activeModelKey.length > 0) && (activeModelKey !== AvatarEditorFigureCategory.WARDROBE)) && - } - { (activeModelKey === AvatarEditorFigureCategory.WARDROBE) } - - - { /* */ } - - - - - - - - - - - - - ); -} diff --git a/src/components/avatar-editor-new/views/palette-set/AvatarEditorPaletteSetView.tsx b/src/components/avatar-editor-new/views/palette-set/AvatarEditorPaletteSetView.tsx deleted file mode 100644 index 2e72cd46..00000000 --- a/src/components/avatar-editor-new/views/palette-set/AvatarEditorPaletteSetView.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import { IPartColor } from '@nitrots/nitro-renderer'; -import { FC, useRef } from 'react'; -import { IAvatarEditorCategory } from '../../../../api'; -import { AutoGrid } from '../../../../common'; -import { useAvatarEditor } from '../../../../hooks'; -import { AvatarEditorPaletteSetItem } from './AvatarEditorPaletteSetItemView'; - -export const AvatarEditorPaletteSetView: FC<{ - category: IAvatarEditorCategory, - paletteIndex: number; -}> = props => -{ - const { category = null, paletteIndex = -1 } = props; - const paletteSet = category?.colorItems[paletteIndex] ?? null; - const { selectedColors = null, selectEditorColor } = useAvatarEditor(); - const elementRef = useRef(null); - - const isPartColorSelected = (partColor: IPartColor) => - { - if(!category || !category.setType || !selectedColors || !selectedColors[category.setType] || !selectedColors[category.setType][paletteIndex]) return false; - - const colorId = selectedColors[category.setType][paletteIndex]; - - return (colorId === partColor.id); - } - - return ( - - { (paletteSet.length > 0) && paletteSet.map(item => - selectEditorColor(category.setType, paletteIndex, item.id) } />) } - - ); -} diff --git a/src/components/avatar-editor/AvatarEditorFigurePreviewView.tsx b/src/components/avatar-editor/AvatarEditorFigurePreviewView.tsx new file mode 100644 index 00000000..9efae75c --- /dev/null +++ b/src/components/avatar-editor/AvatarEditorFigurePreviewView.tsx @@ -0,0 +1,40 @@ +import { AvatarDirectionAngle } from '@nitrots/nitro-renderer'; +import { FC, useState } from 'react'; +import { Base, Column, LayoutAvatarImageView } from '../../common'; +import { useAvatarEditor } from '../../hooks'; +import { AvatarEditorIcon } from './AvatarEditorIcon'; + +const DEFAULT_DIRECTION: number = 4; + +export const AvatarEditorFigurePreviewView: FC<{}> = props => +{ + const [ direction, setDirection ] = useState(DEFAULT_DIRECTION); + const { getFigureString = null } = useAvatarEditor(); + + const rotateFigure = (newDirection: number) => + { + if(direction < AvatarDirectionAngle.MIN_DIRECTION) + { + newDirection = (AvatarDirectionAngle.MAX_DIRECTION + (direction + 1)); + } + + if(direction > AvatarDirectionAngle.MAX_DIRECTION) + { + newDirection = (direction - (AvatarDirectionAngle.MAX_DIRECTION + 1)); + } + + setDirection(newDirection); + } + + return ( + + + + + + rotateFigure(direction + 1) } /> + rotateFigure(direction - 1) } /> + + + ); +} diff --git a/src/components/avatar-editor-new/views/AvatarEditorIcon.tsx b/src/components/avatar-editor/AvatarEditorIcon.tsx similarity index 78% rename from src/components/avatar-editor-new/views/AvatarEditorIcon.tsx rename to src/components/avatar-editor/AvatarEditorIcon.tsx index a05baa1d..878018ce 100644 --- a/src/components/avatar-editor-new/views/AvatarEditorIcon.tsx +++ b/src/components/avatar-editor/AvatarEditorIcon.tsx @@ -1,15 +1,12 @@ import { FC, useMemo } from 'react'; -import { Base, BaseProps } from '../../../common'; +import { Base, BaseProps } from '../../common'; type AvatarIconType = 'male' | 'female' | 'clear' | 'sellable' | string; -export interface AvatarEditorIconProps extends BaseProps -{ +export const AvatarEditorIcon: FC<{ icon: AvatarIconType; selected?: boolean; -} - -export const AvatarEditorIcon: FC = props => +} & BaseProps> = props => { const { icon = null, selected = false, classNames = [], children = null, ...rest } = props; diff --git a/src/components/avatar-editor-new/views/AvatarEditorModelView.tsx b/src/components/avatar-editor/AvatarEditorModelView.tsx similarity index 60% rename from src/components/avatar-editor-new/views/AvatarEditorModelView.tsx rename to src/components/avatar-editor/AvatarEditorModelView.tsx index c012383f..876ca72d 100644 --- a/src/components/avatar-editor-new/views/AvatarEditorModelView.tsx +++ b/src/components/avatar-editor/AvatarEditorModelView.tsx @@ -1,8 +1,8 @@ -import { AvatarEditorFigureCategory } from '@nitrots/nitro-renderer'; -import { FC, useEffect, useMemo, useState } from 'react'; -import { FigureData, IAvatarEditorCategory } from '../../../api'; -import { Column, Flex, Grid } from '../../../common'; -import { useAvatarEditor } from '../../../hooks'; +import { AvatarEditorFigureCategory, AvatarFigurePartType, FigureDataContainer } from '@nitrots/nitro-renderer'; +import { FC, useCallback, useEffect, useMemo, useState } from 'react'; +import { IAvatarEditorCategory } from '../../api'; +import { Column, Flex, Grid } from '../../common'; +import { useAvatarEditor } from '../../hooks'; import { AvatarEditorIcon } from './AvatarEditorIcon'; import { AvatarEditorFigureSetView } from './figure-set'; import { AvatarEditorPaletteSetView } from './palette-set'; @@ -13,39 +13,35 @@ export const AvatarEditorModelView: FC<{ }> = props => { const { name = '', categories = [] } = props; + const [ didChange, setDidChange ] = useState(false); const [ activeSetType, setActiveSetType ] = useState(''); - const { maxPaletteCount = 1 } = useAvatarEditor(); + const { maxPaletteCount = 1, gender = null, setGender = null, selectedColorParts = null, getFirstSelectableColor = null, selectEditorColor = null } = useAvatarEditor(); const activeCategory = useMemo(() => { return categories.find(category => category.setType === activeSetType) ?? null; }, [ categories, activeSetType ]); - const setGender = (gender: string) => + const selectSet = useCallback((setType: string) => { - // - } + const selectedPalettes = selectedColorParts[setType]; + + if(!selectedPalettes || !selectedPalettes.length) selectEditorColor(setType, 0, getFirstSelectableColor(setType)); + + setActiveSetType(setType); + }, [ getFirstSelectableColor, selectEditorColor, selectedColorParts ]); useEffect(() => { - if(!activeCategory) return; + if(!categories || !categories.length || !didChange) return; - // we need to run this when we change which parts r selected - /* for(const partItem of activeCategory.partItems) - { - if(!partItem || !part.isSelected) continue; - - setMaxPaletteCount(part.maxColorIndex || 1); - - break; - } */ - }, [ activeCategory ]) + selectSet(categories[0]?.setType); + setDidChange(false); + }, [ categories, didChange, selectSet ]); useEffect(() => { - if(!categories || !categories.length) return; - - setActiveSetType(categories[0]?.setType) + setDidChange(true); }, [ categories ]); if(!activeCategory) return null; @@ -55,17 +51,17 @@ export const AvatarEditorModelView: FC<{ { (name === AvatarEditorFigureCategory.GENERIC) && <> - setGender(FigureData.MALE) }> - + setGender(AvatarFigurePartType.MALE) }> + - setGender(FigureData.FEMALE) }> - + setGender(AvatarFigurePartType.FEMALE) }> + } { (name !== AvatarEditorFigureCategory.GENERIC) && (categories.length > 0) && categories.map(category => { return ( - setActiveSetType(category.setType) }> + selectSet(category.setType) }> ); diff --git a/src/components/avatar-editor/AvatarEditorView.tsx b/src/components/avatar-editor/AvatarEditorView.tsx index f2b9c0b8..db00b34c 100644 --- a/src/components/avatar-editor/AvatarEditorView.tsx +++ b/src/components/avatar-editor/AvatarEditorView.tsx @@ -1,12 +1,12 @@ -import { AvatarEditorFigureCategory, FigureSetIdsMessageEvent, GetAvatarRenderManager, GetSessionDataManager, GetWardrobeMessageComposer, IAvatarFigureContainer, UserFigureComposer, UserWardrobePageEvent } from '@nitrots/nitro-renderer'; -import { FC, useCallback, useEffect, useMemo, useState } from 'react'; -import { FaDice, FaTrash, FaUndo } from 'react-icons/fa'; -import { AvatarEditorAction, AvatarEditorUtilities, BodyModel, FigureData, GetClubMemberLevel, GetConfigurationValue, HeadModel, IAvatarEditorCategoryModel, LegModel, LocalizeText, SendMessageComposer, TorsoModel, generateRandomFigure } from '../../api'; +import { AddLinkEventTracker, AvatarEditorFigureCategory, GetSessionDataManager, ILinkEventTracker, RemoveLinkEventTracker, UserFigureComposer } from '@nitrots/nitro-renderer'; +import { FC, useEffect, useState } from 'react'; +import { FaDice, FaRedo, FaTrash } from 'react-icons/fa'; +import { AvatarEditorAction, LocalizeText, SendMessageComposer } from '../../api'; import { Button, ButtonGroup, Column, Grid, NitroCardContentView, NitroCardHeaderView, NitroCardTabsItemView, NitroCardTabsView, NitroCardView } from '../../common'; -import { useMessageEvent } from '../../hooks'; -import { AvatarEditorFigurePreviewView } from './views/AvatarEditorFigurePreviewView'; -import { AvatarEditorModelView } from './views/AvatarEditorModelView'; -import { AvatarEditorWardrobeView } from './views/AvatarEditorWardrobeView'; +import { useAvatarEditor } from '../../hooks'; +import { AvatarEditorFigurePreviewView } from './AvatarEditorFigurePreviewView'; +import { AvatarEditorModelView } from './AvatarEditorModelView'; +import { AvatarEditorWardrobeView } from './AvatarEditorWardrobeView'; const DEFAULT_MALE_FIGURE: string = 'hr-100.hd-180-7.ch-215-66.lg-270-79.sh-305-62.ha-1002-70.wa-2007'; const DEFAULT_FEMALE_FIGURE: string = 'hr-515-33.hd-600-1.ch-635-70.lg-716-66-62.sh-735-68'; @@ -14,141 +14,29 @@ const DEFAULT_FEMALE_FIGURE: string = 'hr-515-33.hd-600-1.ch-635-70.lg-716-66-62 export const AvatarEditorView: FC<{}> = props => { const [ isVisible, setIsVisible ] = useState(false); - const [ figures, setFigures ] = useState>(null); - const [ figureData, setFigureData ] = useState(null); - const [ categories, setCategories ] = useState>(null); - const [ activeCategory, setActiveCategory ] = useState(null); - const [ figureSetIds, setFigureSetIds ] = useState([]); - const [ boundFurnitureNames, setBoundFurnitureNames ] = useState([]); - const [ savedFigures, setSavedFigures ] = useState<[ IAvatarFigureContainer, string ][]>([]); - const [ isWardrobeVisible, setIsWardrobeVisible ] = useState(false); - const [ lastFigure, setLastFigure ] = useState(null); - const [ lastGender, setLastGender ] = useState(null); - const [ needsReset, setNeedsReset ] = useState(true); - const [ isInitalized, setIsInitalized ] = useState(false); + const { setIsVisible: setEditorVisibility, avatarModels, activeModelKey, setActiveModelKey, loadAvatarData, getFigureStringWithFace, gender, figureSetIds = [], randomizeCurrentFigure = null, getFigureString = null } = useAvatarEditor(); - const maxWardrobeSlots = useMemo(() => GetConfigurationValue('avatar.wardrobe.max.slots', 10), []); - - useMessageEvent(FigureSetIdsMessageEvent, event => - { - const parser = event.getParser(); - - setFigureSetIds(parser.figureSetIds); - setBoundFurnitureNames(parser.boundsFurnitureNames); - }); - - useMessageEvent(UserWardrobePageEvent, event => - { - const parser = event.getParser(); - const savedFigures: [ IAvatarFigureContainer, string ][] = []; - - let i = 0; - - while(i < maxWardrobeSlots) - { - savedFigures.push([ null, null ]); - - i++; - } - - for(let [ index, [ look, gender ] ] of parser.looks.entries()) - { - const container = GetAvatarRenderManager().createFigureContainer(look); - - savedFigures[(index - 1)] = [ container, gender ]; - } - - setSavedFigures(savedFigures); - }); - - const selectCategory = useCallback((name: string) => - { - if(!categories) return; - - setActiveCategory(categories.get(name)); - }, [ categories ]); - - const resetCategories = useCallback(() => - { - const categories = new Map(); - - categories.set(AvatarEditorFigureCategory.GENERIC, new BodyModel()); - categories.set(AvatarEditorFigureCategory.HEAD, new HeadModel()); - categories.set(AvatarEditorFigureCategory.TORSO, new TorsoModel()); - categories.set(AvatarEditorFigureCategory.LEGS, new LegModel()); - - setCategories(categories); - }, []); - - const setupFigures = useCallback(() => - { - const figures: Map = new Map(); - - const maleFigure = new FigureData(); - const femaleFigure = new FigureData(); - - maleFigure.loadAvatarData(DEFAULT_MALE_FIGURE, FigureData.MALE); - femaleFigure.loadAvatarData(DEFAULT_FEMALE_FIGURE, FigureData.FEMALE); - - figures.set(FigureData.MALE, maleFigure); - figures.set(FigureData.FEMALE, femaleFigure); - - setFigures(figures); - setFigureData(figures.get(FigureData.MALE)); - }, []); - - const loadAvatarInEditor = useCallback((figure: string, gender: string, reset: boolean = true) => - { - gender = AvatarEditorUtilities.getGender(gender); - - let newFigureData = figureData; - - if(gender !== newFigureData.gender) newFigureData = figures.get(gender); - - if(figure !== newFigureData.getFigureString()) newFigureData.loadAvatarData(figure, gender); - - if(newFigureData !== figureData) setFigureData(newFigureData); - - if(reset) - { - setLastFigure(figureData.getFigureString()); - setLastGender(figureData.gender); - } - }, [ figures, figureData ]); - - const processAction = useCallback((action: string) => + const processAction = (action: string) => { switch(action) { - case AvatarEditorAction.ACTION_CLEAR: - loadAvatarInEditor(figureData.getFigureStringWithFace(0, false), figureData.gender, false); - resetCategories(); - return; case AvatarEditorAction.ACTION_RESET: - loadAvatarInEditor(lastFigure, lastGender); - resetCategories(); + loadAvatarData(GetSessionDataManager().figure, GetSessionDataManager().gender); + return; + case AvatarEditorAction.ACTION_CLEAR: + loadAvatarData(getFigureStringWithFace(0, false), gender); return; case AvatarEditorAction.ACTION_RANDOMIZE: - const figure = generateRandomFigure(figureData, figureData.gender, GetClubMemberLevel(), figureSetIds, [ FigureData.FACE ]); - - loadAvatarInEditor(figure, figureData.gender, false); - resetCategories(); + randomizeCurrentFigure(); return; case AvatarEditorAction.ACTION_SAVE: - SendMessageComposer(new UserFigureComposer(figureData.gender, figureData.getFigureString())); + SendMessageComposer(new UserFigureComposer(gender, getFigureString)); setIsVisible(false); return; } - }, [ figureData, lastFigure, lastGender, figureSetIds, loadAvatarInEditor, resetCategories ]) + } - const setGender = useCallback((gender: string) => - { - gender = AvatarEditorUtilities.getGender(gender); - - setFigureData(figures.get(gender)); - }, [ figures ]); - - /* useEffect(() => + useEffect(() => { const linkTracker: ILinkEventTracker = { linkReceived: (url: string) => @@ -176,126 +64,44 @@ export const AvatarEditorView: FC<{}> = props => AddLinkEventTracker(linkTracker); return () => RemoveLinkEventTracker(linkTracker); - }, []); */ + }, []); useEffect(() => { - setSavedFigures(new Array(maxWardrobeSlots)); - }, [ maxWardrobeSlots ]); + setEditorVisibility(isVisible) + }, [ isVisible, setEditorVisibility ]); - useEffect(() => - { - if(!isWardrobeVisible) return; - - setActiveCategory(null); - SendMessageComposer(new GetWardrobeMessageComposer()); - }, [ isWardrobeVisible ]); - - useEffect(() => - { - if(!activeCategory) return; - - setIsWardrobeVisible(false); - }, [ activeCategory ]); - - useEffect(() => - { - if(!categories) return; - - selectCategory(AvatarEditorFigureCategory.GENERIC); - }, [ categories, selectCategory ]); - - useEffect(() => - { - if(!figureData) return; - - AvatarEditorUtilities.CURRENT_FIGURE = figureData; - - resetCategories(); - - return () => AvatarEditorUtilities.CURRENT_FIGURE = null; - }, [ figureData, resetCategories ]); - - useEffect(() => - { - AvatarEditorUtilities.FIGURE_SET_IDS = figureSetIds; - AvatarEditorUtilities.BOUND_FURNITURE_NAMES = boundFurnitureNames; - - resetCategories(); - - return () => - { - AvatarEditorUtilities.FIGURE_SET_IDS = null; - AvatarEditorUtilities.BOUND_FURNITURE_NAMES = null; - } - }, [ figureSetIds, boundFurnitureNames, resetCategories ]); - - useEffect(() => - { - if(!isVisible) return; - - if(!figures) - { - setupFigures(); - - setIsInitalized(true); - - return; - } - }, [ isVisible, figures, setupFigures ]); - - useEffect(() => - { - if(!isVisible || !isInitalized || !needsReset) return; - - loadAvatarInEditor(GetSessionDataManager().figure, GetSessionDataManager().gender); - setNeedsReset(false); - }, [ isVisible, isInitalized, needsReset, loadAvatarInEditor ]); - - useEffect(() => - { - if(isVisible) return; - - return () => - { - setNeedsReset(true); - } - }, [ isVisible ]); - - if(!isVisible || !figureData) return null; + if(!isVisible) return null; return ( setIsVisible(false) } /> - { categories && (categories.size > 0) && Array.from(categories.keys()).map(category => + { Object.keys(avatarModels).map(modelKey => { - const isActive = (activeCategory && (activeCategory.name === category)); + const isActive = (activeModelKey === modelKey); return ( - selectCategory(category) }> - { LocalizeText(`avatareditor.category.${ category }`) } + setActiveModelKey(modelKey) }> + { LocalizeText(`avatareditor.category.${ modelKey }`) } ); }) } - setIsWardrobeVisible(true) }> - { LocalizeText('avatareditor.category.wardrobe') } - - { (activeCategory && !isWardrobeVisible) && - } - { isWardrobeVisible && - } + { ((activeModelKey.length > 0) && (activeModelKey !== AvatarEditorFigureCategory.WARDROBE)) && + } + { (activeModelKey === AvatarEditorFigureCategory.WARDROBE) && + } - + } - ); - }); - - return items; - }, [ savedFigures, hcDisabled, saveFigureAtWardrobeIndex, wearFigureAtIndex ]); - - return ( - - { figures } - + ) + } } /> ); } diff --git a/src/components/avatar-editor-new/views/figure-set/AvatarEditorFigureSetItemView.tsx b/src/components/avatar-editor/figure-set/AvatarEditorFigureSetItemView.tsx similarity index 82% rename from src/components/avatar-editor-new/views/figure-set/AvatarEditorFigureSetItemView.tsx rename to src/components/avatar-editor/figure-set/AvatarEditorFigureSetItemView.tsx index 1c00fc91..955d7bc9 100644 --- a/src/components/avatar-editor-new/views/figure-set/AvatarEditorFigureSetItemView.tsx +++ b/src/components/avatar-editor/figure-set/AvatarEditorFigureSetItemView.tsx @@ -1,7 +1,8 @@ +import { AvatarFigurePartType } from '@nitrots/nitro-renderer'; import { FC, useEffect, useState } from 'react'; -import { AvatarEditorThumbnailsHelper, FigureData, GetConfigurationValue, IAvatarEditorCategoryPartItem } from '../../../../api'; -import { LayoutCurrencyIcon, LayoutGridItem, LayoutGridItemProps } from '../../../../common'; -import { useAvatarEditor } from '../../../../hooks'; +import { AvatarEditorThumbnailsHelper, GetConfigurationValue, IAvatarEditorCategoryPartItem } from '../../../api'; +import { LayoutCurrencyIcon, LayoutGridItem, LayoutGridItemProps } from '../../../common'; +import { useAvatarEditor } from '../../../hooks'; import { AvatarEditorIcon } from '../AvatarEditorIcon'; export const AvatarEditorFigureSetItemView: FC<{ @@ -26,7 +27,7 @@ export const AvatarEditorFigureSetItemView: FC<{ let url: string = null; - if(setType === FigureData.FACE) + if(setType === AvatarFigurePartType.HEAD) { url = await AvatarEditorThumbnailsHelper.buildForFace(getFigureStringWithFace(partItem.id), isHC); } @@ -44,7 +45,7 @@ export const AvatarEditorFigureSetItemView: FC<{ if(!partItem) return null; return ( - + { !partItem.isClear && isHC && } { partItem.isClear && } { !partItem.isClear && partItem.partSet.isSellable && } diff --git a/src/components/avatar-editor-new/views/figure-set/AvatarEditorFigureSetView.tsx b/src/components/avatar-editor/figure-set/AvatarEditorFigureSetView.tsx similarity index 51% rename from src/components/avatar-editor-new/views/figure-set/AvatarEditorFigureSetView.tsx rename to src/components/avatar-editor/figure-set/AvatarEditorFigureSetView.tsx index 7db37430..57413de7 100644 --- a/src/components/avatar-editor-new/views/figure-set/AvatarEditorFigureSetView.tsx +++ b/src/components/avatar-editor/figure-set/AvatarEditorFigureSetView.tsx @@ -1,7 +1,7 @@ -import { FC, useRef } from 'react'; -import { IAvatarEditorCategory, IAvatarEditorCategoryPartItem } from '../../../../api'; -import { InfiniteGrid } from '../../../../common'; -import { useAvatarEditor } from '../../../../hooks'; +import { FC } from 'react'; +import { IAvatarEditorCategory, IAvatarEditorCategoryPartItem } from '../../../api'; +import { InfiniteGrid } from '../../../common'; +import { useAvatarEditor } from '../../../hooks'; import { AvatarEditorFigureSetItemView } from './AvatarEditorFigureSetItemView'; export const AvatarEditorFigureSetView: FC<{ @@ -10,26 +10,30 @@ export const AvatarEditorFigureSetView: FC<{ { const { category = null } = props; const { selectedParts = null, selectEditorPart } = useAvatarEditor(); - const elementRef = useRef(null); const isPartItemSelected = (partItem: IAvatarEditorCategoryPartItem) => { - if(!category || !category.setType || !selectedParts || !selectedParts[category.setType]) return false; + if(!category || !category.setType || !selectedParts) return false; + + if(!selectedParts[category.setType]) + { + if(partItem.isClear) return true; + + return false; + } const partId = selectedParts[category.setType]; return (partId === partItem.id); } - const columnCount = 3; - return ( - + { if(!item) return null; return ( - selectEditorPart(category.setType, item.partSet?.id ?? -1) } /> + selectEditorPart(category.setType, item.partSet?.id ?? -1) } /> ) } } /> ); diff --git a/src/components/avatar-editor-new/views/figure-set/index.ts b/src/components/avatar-editor/figure-set/index.ts similarity index 100% rename from src/components/avatar-editor-new/views/figure-set/index.ts rename to src/components/avatar-editor/figure-set/index.ts diff --git a/src/components/avatar-editor/views/index.ts b/src/components/avatar-editor/index.ts similarity index 86% rename from src/components/avatar-editor/views/index.ts rename to src/components/avatar-editor/index.ts index a92b3b7a..5ae66e54 100644 --- a/src/components/avatar-editor/views/index.ts +++ b/src/components/avatar-editor/index.ts @@ -1,6 +1,7 @@ export * from './AvatarEditorFigurePreviewView'; export * from './AvatarEditorIcon'; export * from './AvatarEditorModelView'; +export * from './AvatarEditorView'; export * from './AvatarEditorWardrobeView'; export * from './figure-set'; export * from './palette-set'; diff --git a/src/components/avatar-editor-new/views/palette-set/AvatarEditorPaletteSetItemView.tsx b/src/components/avatar-editor/palette-set/AvatarEditorPaletteSetItemView.tsx similarity index 71% rename from src/components/avatar-editor-new/views/palette-set/AvatarEditorPaletteSetItemView.tsx rename to src/components/avatar-editor/palette-set/AvatarEditorPaletteSetItemView.tsx index 672c3560..0f43efb8 100644 --- a/src/components/avatar-editor-new/views/palette-set/AvatarEditorPaletteSetItemView.tsx +++ b/src/components/avatar-editor/palette-set/AvatarEditorPaletteSetItemView.tsx @@ -1,17 +1,13 @@ import { ColorConverter, IPartColor } from '@nitrots/nitro-renderer'; import { FC } from 'react'; -import { GetConfigurationValue } from '../../../../api'; -import { LayoutCurrencyIcon, LayoutGridItem, LayoutGridItemProps } from '../../../../common'; +import { GetConfigurationValue } from '../../../api'; +import { LayoutCurrencyIcon, LayoutGridItem, LayoutGridItemProps } from '../../../common'; -export interface AvatarEditorPaletteSetItemProps extends LayoutGridItemProps -{ +export const AvatarEditorPaletteSetItem: FC<{ setType: string; partColor: IPartColor; isSelected: boolean; -} - -// its disabled if its hc and you dont have it -export const AvatarEditorPaletteSetItem: FC = props => +} & LayoutGridItemProps> = props => { const { setType = null, partColor = null, isSelected = false, ...rest } = props; diff --git a/src/components/avatar-editor/palette-set/AvatarEditorPaletteSetView.tsx b/src/components/avatar-editor/palette-set/AvatarEditorPaletteSetView.tsx new file mode 100644 index 00000000..45b32ddd --- /dev/null +++ b/src/components/avatar-editor/palette-set/AvatarEditorPaletteSetView.tsx @@ -0,0 +1,35 @@ +import { IPartColor } from '@nitrots/nitro-renderer'; +import { FC } from 'react'; +import { IAvatarEditorCategory } from '../../../api'; +import { InfiniteGrid } from '../../../common'; +import { useAvatarEditor } from '../../../hooks'; +import { AvatarEditorPaletteSetItem } from './AvatarEditorPaletteSetItemView'; + +export const AvatarEditorPaletteSetView: FC<{ + category: IAvatarEditorCategory, + paletteIndex: number; +}> = props => +{ + const { category = null, paletteIndex = -1 } = props; + const { selectedColorParts = null, selectEditorColor = null } = useAvatarEditor(); + + const isPartColorSelected = (partColor: IPartColor) => + { + if(!category || !category.setType || !selectedColorParts || !selectedColorParts[category.setType] || !selectedColorParts[category.setType][paletteIndex]) return false; + + const selectedColorPart = selectedColorParts[category.setType][paletteIndex]; + + return (selectedColorPart.id === partColor.id); + } + + return ( + + { + if(!item) return null; + + return ( + selectEditorColor(category.setType, paletteIndex, item.id) } /> + ) + } } /> + ); +} diff --git a/src/components/avatar-editor-new/views/palette-set/index.ts b/src/components/avatar-editor/palette-set/index.ts similarity index 100% rename from src/components/avatar-editor-new/views/palette-set/index.ts rename to src/components/avatar-editor/palette-set/index.ts diff --git a/src/components/avatar-editor/views/AvatarEditorFigurePreviewView.tsx b/src/components/avatar-editor/views/AvatarEditorFigurePreviewView.tsx deleted file mode 100644 index d5715acf..00000000 --- a/src/components/avatar-editor/views/AvatarEditorFigurePreviewView.tsx +++ /dev/null @@ -1,55 +0,0 @@ -import { AvatarDirectionAngle } from '@nitrots/nitro-renderer'; -import { FC, useEffect, useState } from 'react'; -import { FigureData } from '../../../api'; -import { Base, Column, LayoutAvatarImageView } from '../../../common'; -import { AvatarEditorIcon } from './AvatarEditorIcon'; - -export interface AvatarEditorFigurePreviewViewProps -{ - figureData: FigureData; -} - -export const AvatarEditorFigurePreviewView: FC = props => -{ - const { figureData = null } = props; - const [ updateId, setUpdateId ] = useState(-1); - - const rotateFigure = (direction: number) => - { - if(direction < AvatarDirectionAngle.MIN_DIRECTION) - { - direction = (AvatarDirectionAngle.MAX_DIRECTION + (direction + 1)); - } - - if(direction > AvatarDirectionAngle.MAX_DIRECTION) - { - direction = (direction - (AvatarDirectionAngle.MAX_DIRECTION + 1)); - } - - figureData.direction = direction; - } - - useEffect(() => - { - if(!figureData) return; - - figureData.notify = () => setUpdateId(prevValue => (prevValue + 1)); - - return () => - { - figureData.notify = null; - } - }, [ figureData ] ); - - return ( - - - - - - rotateFigure(figureData.direction + 1) } /> - rotateFigure(figureData.direction - 1) } /> - - - ); -} diff --git a/src/components/avatar-editor/views/AvatarEditorIcon.tsx b/src/components/avatar-editor/views/AvatarEditorIcon.tsx deleted file mode 100644 index a05baa1d..00000000 --- a/src/components/avatar-editor/views/AvatarEditorIcon.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import { FC, useMemo } from 'react'; -import { Base, BaseProps } from '../../../common'; - -type AvatarIconType = 'male' | 'female' | 'clear' | 'sellable' | string; - -export interface AvatarEditorIconProps extends BaseProps -{ - icon: AvatarIconType; - selected?: boolean; -} - -export const AvatarEditorIcon: FC = props => -{ - const { icon = null, selected = false, classNames = [], children = null, ...rest } = props; - - const getClassNames = useMemo(() => - { - const newClassNames: string[] = [ 'nitro-avatar-editor-spritesheet' ]; - - if(icon && icon.length) newClassNames.push(icon + '-icon'); - - if(selected) newClassNames.push('selected'); - - if(classNames.length) newClassNames.push(...classNames); - - return newClassNames; - }, [ icon, selected, classNames ]); - - return -} diff --git a/src/components/avatar-editor/views/AvatarEditorModelView.tsx b/src/components/avatar-editor/views/AvatarEditorModelView.tsx deleted file mode 100644 index 6eb8fe3e..00000000 --- a/src/components/avatar-editor/views/AvatarEditorModelView.tsx +++ /dev/null @@ -1,88 +0,0 @@ -import { Dispatch, FC, SetStateAction, useCallback, useEffect, useState } from 'react'; -import { CategoryData, FigureData, IAvatarEditorCategoryModel } from '../../../api'; -import { Column, Flex, Grid } from '../../../common'; -import { AvatarEditorIcon } from './AvatarEditorIcon'; -import { AvatarEditorFigureSetView } from './figure-set/AvatarEditorFigureSetView'; -import { AvatarEditorPaletteSetView } from './palette-set/AvatarEditorPaletteSetView'; -export interface AvatarEditorModelViewProps -{ - model: IAvatarEditorCategoryModel; - gender: string; - setGender: Dispatch>; -} - -export const AvatarEditorModelView: FC = props => -{ - const { model = null, gender = null, setGender = null } = props; - const [ activeCategory, setActiveCategory ] = useState(null); - const [ maxPaletteCount, setMaxPaletteCount ] = useState(1); - - const selectCategory = useCallback((name: string) => - { - const category = model.categories.get(name); - - if(!category) return; - - category.init(); - - setActiveCategory(category); - - for(const part of category.parts) - { - if(!part || !part.isSelected) continue; - - setMaxPaletteCount(part.maxColorIndex || 1); - - break; - } - }, [ model ]); - - useEffect(() => - { - model.init(); - - for(const name of model.categories.keys()) - { - selectCategory(name); - - break; - } - }, [ model, selectCategory ]); - - if(!model || !activeCategory) return null; - - return ( - - - { model.canSetGender && - <> - setGender(FigureData.MALE) }> - - - setGender(FigureData.FEMALE) }> - - - } - { !model.canSetGender && model.categories && (model.categories.size > 0) && Array.from(model.categories.keys()).map(name => - { - const category = model.categories.get(name); - - return ( - selectCategory(name) }> - - - ); - }) } - - - - - - { (maxPaletteCount >= 1) && - } - { (maxPaletteCount === 2) && - } - - - ); -} diff --git a/src/components/avatar-editor/views/figure-set/AvatarEditorFigureSetItemView.tsx b/src/components/avatar-editor/views/figure-set/AvatarEditorFigureSetItemView.tsx deleted file mode 100644 index 3cd6383a..00000000 --- a/src/components/avatar-editor/views/figure-set/AvatarEditorFigureSetItemView.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import { FC, useEffect, useState } from 'react'; -import { AvatarEditorGridPartItem, GetConfigurationValue } from '../../../../api'; -import { LayoutCurrencyIcon, LayoutGridItem, LayoutGridItemProps } from '../../../../common'; -import { AvatarEditorIcon } from '../AvatarEditorIcon'; - -export interface AvatarEditorFigureSetItemViewProps extends LayoutGridItemProps -{ - partItem: AvatarEditorGridPartItem; -} - -export const AvatarEditorFigureSetItemView: FC = props => -{ - const { partItem = null, children = null, ...rest } = props; - const [ updateId, setUpdateId ] = useState(-1); - - const hcDisabled = GetConfigurationValue('hc.disabled', false); - - useEffect(() => - { - const rerender = () => setUpdateId(prevValue => (prevValue + 1)); - - partItem.notify = rerender; - - return () => partItem.notify = null; - }, [ partItem ]); - - return ( - - { !hcDisabled && partItem.isHC && } - { partItem.isClear && } - { partItem.isSellable && } - { children } - - ); -} diff --git a/src/components/avatar-editor/views/figure-set/AvatarEditorFigureSetView.tsx b/src/components/avatar-editor/views/figure-set/AvatarEditorFigureSetView.tsx deleted file mode 100644 index 3755731c..00000000 --- a/src/components/avatar-editor/views/figure-set/AvatarEditorFigureSetView.tsx +++ /dev/null @@ -1,44 +0,0 @@ -import { Dispatch, FC, SetStateAction, useCallback, useEffect, useRef } from 'react'; -import { AvatarEditorGridPartItem, CategoryData, IAvatarEditorCategoryModel } from '../../../../api'; -import { AutoGrid } from '../../../../common'; -import { AvatarEditorFigureSetItemView } from './AvatarEditorFigureSetItemView'; - -export interface AvatarEditorFigureSetViewProps -{ - model: IAvatarEditorCategoryModel; - category: CategoryData; - setMaxPaletteCount: Dispatch>; -} - -export const AvatarEditorFigureSetView: FC = props => -{ - const { model = null, category = null, setMaxPaletteCount = null } = props; - const elementRef = useRef(null); - - const selectPart = useCallback((item: AvatarEditorGridPartItem) => - { - const index = category.parts.indexOf(item); - - if(index === -1) return; - - model.selectPart(category.name, index); - - const partItem = category.getCurrentPart(); - - setMaxPaletteCount(partItem.maxColorIndex || 1); - }, [ model, category, setMaxPaletteCount ]); - - useEffect(() => - { - if(!model || !category || !elementRef || !elementRef.current) return; - - elementRef.current.scrollTop = 0; - }, [ model, category ]); - - return ( - - { (category.parts.length > 0) && category.parts.map((item, index) => - selectPart(item) } />) } - - ); -} diff --git a/src/components/avatar-editor/views/figure-set/index.ts b/src/components/avatar-editor/views/figure-set/index.ts deleted file mode 100644 index 0c5880b2..00000000 --- a/src/components/avatar-editor/views/figure-set/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './AvatarEditorFigureSetItemView'; -export * from './AvatarEditorFigureSetView'; diff --git a/src/components/avatar-editor/views/palette-set/AvatarEditorPaletteSetItemView.tsx b/src/components/avatar-editor/views/palette-set/AvatarEditorPaletteSetItemView.tsx deleted file mode 100644 index 78680577..00000000 --- a/src/components/avatar-editor/views/palette-set/AvatarEditorPaletteSetItemView.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import { FC, useEffect, useState } from 'react'; -import { AvatarEditorGridColorItem, GetConfigurationValue } from '../../../../api'; -import { LayoutCurrencyIcon, LayoutGridItem, LayoutGridItemProps } from '../../../../common'; - -export interface AvatarEditorPaletteSetItemProps extends LayoutGridItemProps -{ - colorItem: AvatarEditorGridColorItem; -} - -export const AvatarEditorPaletteSetItem: FC = props => -{ - const { colorItem = null, children = null, ...rest } = props; - const [ updateId, setUpdateId ] = useState(-1); - - const hcDisabled = GetConfigurationValue('hc.disabled', false); - - useEffect(() => - { - const rerender = () => setUpdateId(prevValue => (prevValue + 1)); - - colorItem.notify = rerender; - - return () => colorItem.notify = null; - }, [ colorItem ]); - - return ( - - { !hcDisabled && colorItem.isHC && } - { children } - - ); -} diff --git a/src/components/avatar-editor/views/palette-set/AvatarEditorPaletteSetView.tsx b/src/components/avatar-editor/views/palette-set/AvatarEditorPaletteSetView.tsx deleted file mode 100644 index c55dcb47..00000000 --- a/src/components/avatar-editor/views/palette-set/AvatarEditorPaletteSetView.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import { FC, useCallback, useEffect, useRef } from 'react'; -import { AvatarEditorGridColorItem, CategoryData, IAvatarEditorCategoryModel } from '../../../../api'; -import { AutoGrid } from '../../../../common'; -import { AvatarEditorPaletteSetItem } from './AvatarEditorPaletteSetItemView'; - -export interface AvatarEditorPaletteSetViewProps -{ - model: IAvatarEditorCategoryModel; - category: CategoryData; - paletteSet: AvatarEditorGridColorItem[]; - paletteIndex: number; -} - -export const AvatarEditorPaletteSetView: FC = props => -{ - const { model = null, category = null, paletteSet = [], paletteIndex = -1 } = props; - const elementRef = useRef(null); - - const selectColor = useCallback((item: AvatarEditorGridColorItem) => - { - const index = paletteSet.indexOf(item); - - if(index === -1) return; - - model.selectColor(category.name, index, paletteIndex); - }, [ model, category, paletteSet, paletteIndex ]); - - useEffect(() => - { - if(!model || !category || !elementRef || !elementRef.current) return; - - elementRef.current.scrollTop = 0; - }, [ model, category ]); - - return ( - - { (paletteSet.length > 0) && paletteSet.map((item, index) => - selectColor(item) } />) } - - ); -} diff --git a/src/components/avatar-editor/views/palette-set/index.ts b/src/components/avatar-editor/views/palette-set/index.ts deleted file mode 100644 index 977e5b98..00000000 --- a/src/components/avatar-editor/views/palette-set/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './AvatarEditorPaletteSetItemView'; -export * from './AvatarEditorPaletteSetView'; diff --git a/src/components/main/MainView.tsx b/src/components/main/MainView.tsx index 43435abe..5fc3b1f9 100644 --- a/src/components/main/MainView.tsx +++ b/src/components/main/MainView.tsx @@ -3,8 +3,7 @@ import { FC, useEffect, useState } from 'react'; import { Base, TransitionAnimation, TransitionAnimationTypes } from '../../common'; import { useNitroEvent } from '../../hooks'; import { AchievementsView } from '../achievements/AchievementsView'; -import { AvatarEditorNewView } from '../avatar-editor-new/AvatarEditorView'; -import { AvatarEditorView } from '../avatar-editor/AvatarEditorView'; +import { AvatarEditorView } from '../avatar-editor'; import { CameraWidgetView } from '../camera/CameraWidgetView'; import { CampaignView } from '../campaign/CampaignView'; import { CatalogView } from '../catalog/CatalogView'; @@ -90,7 +89,6 @@ export const MainView: FC<{}> = props => - diff --git a/src/components/notification-center/views/bubble-layouts/GetBubbleLayout.tsx b/src/components/notification-center/views/bubble-layouts/GetBubbleLayout.tsx index b22a2f61..23b6e112 100644 --- a/src/components/notification-center/views/bubble-layouts/GetBubbleLayout.tsx +++ b/src/components/notification-center/views/bubble-layouts/GetBubbleLayout.tsx @@ -6,13 +6,13 @@ export const GetBubbleLayout = (item: NotificationBubbleItem, onClose: () => voi { if(!item) return null; - const props = { key: item.id, item, onClose }; + const props = { item, onClose }; switch(item.notificationType) { case NotificationBubbleType.CLUBGIFT: - return + return default: - return + return } } diff --git a/src/components/room/widgets/furniture/context-menu/PurchasableClothingConfirmView.tsx b/src/components/room/widgets/furniture/context-menu/PurchasableClothingConfirmView.tsx index 5ba9905b..090c1edc 100644 --- a/src/components/room/widgets/furniture/context-menu/PurchasableClothingConfirmView.tsx +++ b/src/components/room/widgets/furniture/context-menu/PurchasableClothingConfirmView.tsx @@ -1,6 +1,6 @@ -import { GetAvatarRenderManager, GetSessionDataManager, RedeemItemClothingComposer, RoomObjectCategory, UserFigureComposer } from '@nitrots/nitro-renderer'; +import { AvatarFigurePartType, GetAvatarRenderManager, GetSessionDataManager, RedeemItemClothingComposer, RoomObjectCategory, UserFigureComposer } from '@nitrots/nitro-renderer'; import { FC, useEffect, useState } from 'react'; -import { FigureData, FurniCategory, GetFurnitureDataForRoomObject, LocalizeText, SendMessageComposer } from '../../../../../api'; +import { FurniCategory, GetFurnitureDataForRoomObject, LocalizeText, SendMessageComposer } from '../../../../../api'; import { Base, Button, Column, Flex, LayoutAvatarImageView, NitroCardContentView, NitroCardHeaderView, NitroCardView, Text } from '../../../../../common'; import { useRoom } from '../../../../../hooks'; @@ -17,7 +17,7 @@ export const PurchasableClothingConfirmView: FC(FigureData.MALE); + const [ gender, setGender ] = useState(AvatarFigurePartType.MALE); const [ newFigure, setNewFigure ] = useState(null); const { roomSession = null } = useRoom(); diff --git a/src/hooks/avatar-editor/useAvatarEditor.ts b/src/hooks/avatar-editor/useAvatarEditor.ts index 82dd433c..d35bb5fb 100644 --- a/src/hooks/avatar-editor/useAvatarEditor.ts +++ b/src/hooks/avatar-editor/useAvatarEditor.ts @@ -1,7 +1,7 @@ -import { AvatarEditorFigureCategory, FigureSetIdsMessageEvent, GetAvatarRenderManager, GetSessionDataManager, IFigurePartSet, IPartColor } from '@nitrots/nitro-renderer'; +import { AvatarEditorFigureCategory, AvatarFigureContainer, AvatarFigurePartType, FigureSetIdsMessageEvent, GetAvatarRenderManager, GetSessionDataManager, GetWardrobeMessageComposer, IAvatarFigureContainer, IFigurePartSet, IPalette, IPartColor, SetType, UserWardrobePageEvent } from '@nitrots/nitro-renderer'; import { useCallback, useEffect, useMemo, useState } from 'react'; import { useBetween } from 'use-between'; -import { AvatarEditorThumbnailsHelper, FigureData, GetClubMemberLevel, IAvatarEditorCategory, IAvatarEditorCategoryPartItem } from '../../api'; +import { AvatarEditorColorSorter, AvatarEditorPartSorter, AvatarEditorThumbnailsHelper, GetClubMemberLevel, GetConfigurationValue, IAvatarEditorCategory, IAvatarEditorCategoryPartItem, Randomizer, SendMessageComposer } from '../../api'; import { useMessageEvent } from '../events'; import { useFigureData } from './useFigureData'; @@ -15,7 +15,8 @@ const useAvatarEditorState = () => const [ maxPaletteCount, setMaxPaletteCount ] = useState(1); const [ figureSetIds, setFigureSetIds ] = useState([]); const [ boundFurnitureNames, setBoundFurnitureNames ] = useState([]); - const { gender, selectedParts, selectedColors, loadAvatarData, selectPart, selectColor, getFigureStringWithFace } = useFigureData(); + const [ savedFigures, setSavedFigures ] = useState<[ IAvatarFigureContainer, string ][]>(null); + const { selectedColors, gender, setGender, loadAvatarData, selectPart, selectColor, getFigureString, getFigureStringWithFace, selectedParts } = useFigureData(); const activeModel = useMemo(() => (avatarModels[activeModelKey] ?? null), [ activeModelKey, avatarModels ]); @@ -57,7 +58,8 @@ const useAvatarEditorState = () => if(partItem.isClear) { - // clear the part + selectPart(setType, -1); + return; } @@ -89,6 +91,109 @@ const useAvatarEditorState = () => selectColor(setType, paletteId, colorId); }, [ activeModel, selectColor ]); + const getFirstSelectableColor = useCallback((setType: string) => + { + const set = GetAvatarRenderManager().structureData.getSetType(setType); + + if(!setType) return -1; + + const palette = GetAvatarRenderManager().structureData.getPalette(set.paletteID); + + if(!palette) return -1; + + for(const color of palette.colors.getValues()) + { + if(!color.isSelectable || (GetClubMemberLevel() < color.clubLevel)) continue; + + return color.id; + } + + return -1; + }, []); + + const randomizeCurrentFigure = useCallback((ignoredSets: string[] = []) => + { + const structure = GetAvatarRenderManager().structure; + const figureContainer = new AvatarFigureContainer(''); + + const getRandomSetTypes = (requiredSets: string[], options: string[]) => + { + options = options.filter(option => (requiredSets.indexOf(option) === -1)); + + return [ ...requiredSets, ...Randomizer.getRandomElements(options, (Randomizer.getRandomNumber(options.length) + 1)) ]; + } + + const requiredSets = getRandomSetTypes(structure.getMandatorySetTypeIds(gender, GetClubMemberLevel()), AvatarFigurePartType.FIGURE_SETS); + + const getRandomPartSet = (setType: SetType, gender: string, clubLevel: number, figureSetIds: number[]) => + { + const options = setType.partSets.getValues().filter(option => + { + if(!option.isSelectable || ((option.gender !== 'U') && (option.gender !== gender)) || (option.clubLevel > clubLevel) || (option.isSellable && (figureSetIds.indexOf(option.id) === -1))) return null; + + return option; + }); + + if(!options || !options.length) return null; + + return Randomizer.getRandomElement(options); + } + + const getRandomColors = (palette: IPalette, partSet: IFigurePartSet, clubLevel: number) => + { + const options = palette.colors.getValues().filter(option => + { + if(!option.isSelectable || (option.clubLevel > clubLevel)) return null; + + return option; + }); + + if(!options || !options.length) return null; + + const getTotalColors = (partSet: IFigurePartSet) => + { + const parts = partSet.parts; + + let totalColors = 0; + + for(const part of parts) totalColors = Math.max(totalColors, part.colorLayerIndex); + + return totalColors; + } + + return Randomizer.getRandomElements(options, getTotalColors(partSet)); + } + + for(const setType of ignoredSets) + { + const partSetId = selectedParts[setType]; + const colors = selectedColors[setType]; + + figureContainer.updatePart(setType, partSetId, colors); + } + + for(const type of requiredSets) + { + if(figureContainer.hasPartType(type)) continue; + + const setType = (structure.figureData.getSetType(type) as SetType); + const selectedSet = getRandomPartSet(setType, gender, GetClubMemberLevel(), figureSetIds); + + if(!selectedSet) continue; + + let selectedColors: number[] = []; + + if(selectedSet.isColorable) + { + selectedColors = getRandomColors(structure.figureData.getPalette(setType.paletteID), selectedSet, GetClubMemberLevel()).map(color => color.id); + } + + figureContainer.updatePart(setType.type, selectedSet.id, selectedColors); + } + + loadAvatarData(figureContainer.getFigureString(), gender); + }, [ figureSetIds, gender, loadAvatarData, selectedColors, selectedParts ]); + useMessageEvent(FigureSetIdsMessageEvent, event => { const parser = event.getParser(); @@ -97,6 +202,30 @@ const useAvatarEditorState = () => setBoundFurnitureNames(parser.boundsFurnitureNames); }); + useMessageEvent(UserWardrobePageEvent, event => + { + const parser = event.getParser(); + const savedFigures: [ IAvatarFigureContainer, string ][] = []; + + let i = 0; + + while(i < GetConfigurationValue('avatar.wardrobe.max.slots', 10)) + { + savedFigures.push([ null, null ]); + + i++; + } + + for(let [ index, [ look, gender ] ] of parser.looks.entries()) + { + const container = GetAvatarRenderManager().createFigureContainer(look); + + savedFigures[(index - 1)] = [ container, gender ]; + } + + setSavedFigures(savedFigures); + }); + useEffect(() => { AvatarEditorThumbnailsHelper.clearCache(); @@ -125,19 +254,6 @@ const useAvatarEditorState = () => if(!partColor || !partColor.isSelectable) continue; for(let i = 0; i < MAX_PALETTES; i++) colorItems[i].push(partColor); - - // TODO - check what this does - /* if(setType !== FigureData.FACE) - { - let i = 0; - - while(i < colorIds.length) - { - if(partColor.id === colorIds[i]) partColors[i] = partColor; - - i++; - } - } */ } let mandatorySetIds: string[] = GetAvatarRenderManager().getMandatoryAvatarPartSetIds(gender, GetClubMemberLevel()); @@ -146,14 +262,14 @@ const useAvatarEditorState = () => if(isntMandatorySet) partItems.push({ id: -1, isClear: true }); - const usesColor = (setType !== FigureData.FACE); + const usesColor = (setType !== AvatarFigurePartType.HEAD); const partSets = set.partSets; for(let i = (partSets.length); i >= 0; i--) { const partSet = partSets.getWithIndex(i); - if(!partSet || !partSet.isSelectable || ((partSet.gender !== gender) && (partSet.gender !== FigureData.UNISEX))) continue; + if(!partSet || !partSet.isSelectable || ((partSet.gender !== gender) && (partSet.gender !== AvatarFigurePartType.UNISEX))) continue; if(partSet.isSellable && figureSetIds.indexOf(partSet.id) === -1) continue; @@ -164,17 +280,17 @@ const useAvatarEditorState = () => partItems.push({ id: partSet.id, partSet, usesColor, maxPaletteCount }); } - partItems.sort(partSorter(false)); + partItems.sort(AvatarEditorPartSorter(false)); - for(let i = 0; i < MAX_PALETTES; i++) colorItems[i].sort(colorSorter); + for(let i = 0; i < MAX_PALETTES; i++) colorItems[i].sort(AvatarEditorColorSorter); return { setType, partItems, colorItems }; } - newAvatarModels[AvatarEditorFigureCategory.GENERIC] = [ FigureData.FACE ].map(setType => buildCategory(setType)); - newAvatarModels[AvatarEditorFigureCategory.HEAD] = [ FigureData.HAIR, FigureData.HAT, FigureData.HEAD_ACCESSORIES, FigureData.EYE_ACCESSORIES, FigureData.FACE_ACCESSORIES ].map(setType => buildCategory(setType)); - newAvatarModels[AvatarEditorFigureCategory.TORSO] = [ FigureData.SHIRT, FigureData.CHEST_PRINTS, FigureData.JACKET, FigureData.CHEST_ACCESSORIES ].map(setType => buildCategory(setType)); - newAvatarModels[AvatarEditorFigureCategory.LEGS] = [ FigureData.TROUSERS, FigureData.SHOES, FigureData.TROUSER_ACCESSORIES ].map(setType => buildCategory(setType)); + newAvatarModels[AvatarEditorFigureCategory.GENERIC] = [ AvatarFigurePartType.HEAD ].map(setType => buildCategory(setType)); + newAvatarModels[AvatarEditorFigureCategory.HEAD] = [ AvatarFigurePartType.HAIR, AvatarFigurePartType.HEAD_ACCESSORY, AvatarFigurePartType.HEAD_ACCESSORY_EXTRA, AvatarFigurePartType.EYE_ACCESSORY, AvatarFigurePartType.FACE_ACCESSORY ].map(setType => buildCategory(setType)); + newAvatarModels[AvatarEditorFigureCategory.TORSO] = [ AvatarFigurePartType.CHEST, AvatarFigurePartType.CHEST_PRINT, AvatarFigurePartType.COAT_CHEST, AvatarFigurePartType.CHEST_ACCESSORY ].map(setType => buildCategory(setType)); + newAvatarModels[AvatarEditorFigureCategory.LEGS] = [ AvatarFigurePartType.LEGS, AvatarFigurePartType.SHOES, AvatarFigurePartType.WAIST_ACCESSORY ].map(setType => buildCategory(setType)); newAvatarModels[AvatarEditorFigureCategory.WARDROBE] = []; setAvatarModels(newAvatarModels); @@ -188,57 +304,15 @@ const useAvatarEditorState = () => loadAvatarData(GetSessionDataManager().figure, GetSessionDataManager().gender); }, [ isVisible, loadAvatarData ]); - return { isVisible, setIsVisible, avatarModels, activeModelKey, setActiveModelKey, selectedParts, selectedColors, maxPaletteCount, selectedColorParts, selectEditorPart, selectEditorColor, getFigureStringWithFace }; + useEffect(() => + { + if(!isVisible || savedFigures) return; + + setSavedFigures(new Array(GetConfigurationValue('avatar.wardrobe.max.slots', 10))); + SendMessageComposer(new GetWardrobeMessageComposer()); + }, [ isVisible, savedFigures ]); + + return { isVisible, setIsVisible, avatarModels, activeModelKey, setActiveModelKey, maxPaletteCount, selectedColorParts, selectEditorColor, selectEditorPart, loadAvatarData, getFigureString, getFigureStringWithFace, selectedParts, gender, setGender, figureSetIds, randomizeCurrentFigure, savedFigures, setSavedFigures, getFirstSelectableColor }; } export const useAvatarEditor = () => useBetween(useAvatarEditorState); - -const partSorter = (hcFirst: boolean) => -{ - return (a: { partSet: IFigurePartSet, usesColor: boolean, isClear?: boolean }, b: { partSet: IFigurePartSet, usesColor: boolean, isClear?: boolean }) => - { - const clubLevelA = (!a.partSet ? -1 : a.partSet.clubLevel); - const clubLevelB = (!b.partSet ? -1 : b.partSet.clubLevel); - const isSellableA = (!a.partSet ? false : a.partSet.isSellable); - const isSellableB = (!b.partSet ? false : b.partSet.isSellable); - - if(isSellableA && !isSellableB) return 1; - - if(isSellableB && !isSellableA) return -1; - - if(hcFirst) - { - if(clubLevelA > clubLevelB) return -1; - - if(clubLevelA < clubLevelB) return 1; - } - else - { - if(clubLevelA < clubLevelB) return -1; - - if(clubLevelA > clubLevelB) return 1; - } - - if(a.partSet.id < b.partSet.id) return -1; - - if(a.partSet.id > b.partSet.id) return 1; - - return 0; - } -} - -const colorSorter = (a: IPartColor, b: IPartColor) => -{ - const clubLevelA = (!a ? -1 : a.clubLevel); - const clubLevelB = (!b ? -1 : b.clubLevel); - - if(clubLevelA < clubLevelB) return -1; - - if(clubLevelA > clubLevelB) return 1; - - if(a.index < b.index) return -1; - - if(a.index > b.index) return 1; - - return 0; -} diff --git a/src/hooks/avatar-editor/useFigureData.ts b/src/hooks/avatar-editor/useFigureData.ts index 5d68730a..0e1dbae0 100644 --- a/src/hooks/avatar-editor/useFigureData.ts +++ b/src/hooks/avatar-editor/useFigureData.ts @@ -1,11 +1,11 @@ -import { useCallback, useState } from 'react'; -import { FigureData } from '../../api'; +import { AvatarFigurePartType } from '@nitrots/nitro-renderer'; +import { useCallback, useMemo, useState } from 'react'; const useFigureDataState = () => { const [ selectedParts, setSelectedParts ] = useState<{ [index: string]: number }>({}); const [ selectedColors, setSelectedColors ] = useState<{ [index: string]: number[] }>({}); - const [ gender, setGender ] = useState(FigureData.MALE); + const [ gender, setGender ] = useState(AvatarFigurePartType.MALE); const loadAvatarData = useCallback((figureString: string, gender: string) => { @@ -62,7 +62,8 @@ const useFigureDataState = () => { const newValue = { ...prevValue }; - newValue[setType] = partId; + if(partId === -1) delete newValue[setType]; + else newValue[setType] = partId; return newValue; }); @@ -86,29 +87,64 @@ const useFigureDataState = () => }) }, []); + const getFigureString = useMemo(() => + { + let figureString = ''; + + const partSets: string[] = []; + const setTypes = Object.keys(selectedParts); + + for(const setType of setTypes) + { + const partId = selectedParts[setType]; + + if(!partId) continue; + + let setPart = `${ setType }-${ partId }`; + + if(selectedColors[setType] && selectedColors[setType].length) + { + let i = 0; + + while(i < selectedColors[setType].length) + { + setPart += `-${ selectedColors[setType][i] }`; + + i++; + } + } + + partSets.push(setPart); + } + + for(const partSet of partSets) + { + figureString += partSet; + + if(partSets.indexOf(partSet) < (partSets.length - 1)) figureString += '.'; + } + + return figureString; + }, [ selectedParts, selectedColors ]); + const getFigureStringWithFace = useCallback((overridePartId: number, override: boolean = true) => { - const figureSets = [ FigureData.FACE ].map(setType => + const figureSets = [ AvatarFigurePartType.HEAD ].map(setType => { - // Determine the part ID, with an option to override if the set type matches. - let partId = (setType === FigureData.FACE && override) ? overridePartId : selectedParts[setType]; + let partId = (setType === AvatarFigurePartType.HEAD && override) ? overridePartId : selectedParts[setType]; const colors = selectedColors[setType] || []; - // Construct the figure set string, including the type, part ID, and any colors. let figureSet = `${ setType }-${ partId }`; - if (partId >= 0) - { - figureSet += colors.map(color => `-${ color }`).join(''); - } + + if (partId >= 0) figureSet += colors.map(color => `-${ color }`).join(''); return figureSet; }); - - // Join all figure sets with '.', ensuring to only add '.' between items, not at the end. + return figureSets.join('.'); }, [ selectedParts, selectedColors ]); - return { selectedParts, selectedColors, gender, loadAvatarData, selectPart, selectColor, getFigureStringWithFace }; + return { selectedParts, selectedColors, gender, setGender, loadAvatarData, selectPart, selectColor, getFigureString, getFigureStringWithFace }; } export const useFigureData = useFigureDataState;