mirror of
https://github.com/billsonnn/nitro-react.git
synced 2024-11-23 14:40:50 +01:00
Start avatar editor
This commit is contained in:
parent
58d78ebb58
commit
9ef6750474
@ -1,16 +1,28 @@
|
||||
import { AvatarEditorFigureCategory } from 'nitro-renderer';
|
||||
import { FC, useCallback, useEffect, useReducer, useState } from 'react';
|
||||
import { AvatarEditorEvent } from '../../events/avatar-editor';
|
||||
import { useUiEvent } from '../../hooks/events/ui/ui-event';
|
||||
import { NitroCardContentView, NitroCardHeaderView, NitroCardTabsView, NitroCardView } from '../../layout';
|
||||
import { NitroCardContentView, NitroCardHeaderView, NitroCardTabsItemView, NitroCardTabsView, NitroCardView } from '../../layout';
|
||||
import { LocalizeText } from '../../utils/LocalizeText';
|
||||
import { AvatarEditorViewProps } from './AvatarEditorView.types';
|
||||
import { AvatarEditor } from './common/AvatarEditor';
|
||||
import { BodyModel } from './common/BodyModel';
|
||||
import { HeadModel } from './common/HeadModel';
|
||||
import { IAvatarEditorCategoryModel } from './common/IAvatarEditorCategoryModel';
|
||||
import { LegModel } from './common/LegModel';
|
||||
import { TorsoModel } from './common/TorsoModel';
|
||||
import { AvatarEditorContextProvider } from './context/AvatarEditorContext';
|
||||
import { AvatarEditorReducer, initialAvatarEditor } from './reducers/AvatarEditorReducer';
|
||||
import { AvatarEditorModelView } from './views/model/AvatarEditorModelView';
|
||||
|
||||
export const AvatarEditorView: FC<AvatarEditorViewProps> = props =>
|
||||
{
|
||||
const [ isVisible, setIsVisible ] = useState(false);
|
||||
const [ avatarEditorState, dispatchAvatarEditorState ] = useReducer(AvatarEditorReducer, initialAvatarEditor);
|
||||
const [ avatarEditor, setAvatarEditor ] = useState<AvatarEditor>(null);
|
||||
const [ categories, setCategories ] = useState<Map<string, IAvatarEditorCategoryModel>>(null);
|
||||
const [ activeCategory, setActiveCategory ] = useState<IAvatarEditorCategoryModel>(null);
|
||||
const [ isInitalized, setIsInitalized ] = useState(false);
|
||||
|
||||
const onAvatarEditorEvent = useCallback((event: AvatarEditorEvent) =>
|
||||
{
|
||||
@ -32,10 +44,30 @@ export const AvatarEditorView: FC<AvatarEditorViewProps> = props =>
|
||||
useUiEvent(AvatarEditorEvent.HIDE_EDITOR, onAvatarEditorEvent);
|
||||
useUiEvent(AvatarEditorEvent.TOGGLE_EDITOR, onAvatarEditorEvent);
|
||||
|
||||
const selectCategory = useCallback((name: string) =>
|
||||
{
|
||||
setActiveCategory(categories.get(name));
|
||||
}, [ categories ]);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
if(!isVisible) return;
|
||||
}, [ isVisible ]);
|
||||
if(!isVisible || isInitalized) return;
|
||||
|
||||
const newEditor = new AvatarEditor();
|
||||
|
||||
setAvatarEditor(newEditor);
|
||||
|
||||
const categories = new Map();
|
||||
|
||||
categories.set(AvatarEditorFigureCategory.GENERIC, new BodyModel(newEditor));
|
||||
categories.set(AvatarEditorFigureCategory.HEAD, new HeadModel(newEditor));
|
||||
categories.set(AvatarEditorFigureCategory.TORSO, new TorsoModel(newEditor));
|
||||
categories.set(AvatarEditorFigureCategory.LEGS, new LegModel(newEditor));
|
||||
|
||||
setCategories(categories);
|
||||
setActiveCategory(categories.get(AvatarEditorFigureCategory.GENERIC));
|
||||
setIsInitalized(true);
|
||||
}, [ isVisible, isInitalized ]);
|
||||
|
||||
return (
|
||||
<AvatarEditorContextProvider value={ { avatarEditorState, dispatchAvatarEditorState } }>
|
||||
@ -43,14 +75,17 @@ export const AvatarEditorView: FC<AvatarEditorViewProps> = props =>
|
||||
<NitroCardView className="nitro-avatar-editor">
|
||||
<NitroCardHeaderView headerText={ LocalizeText('avatareditor.title') } onCloseClick={ event => setIsVisible(false) } />
|
||||
<NitroCardTabsView>
|
||||
{ categories && Array.from(categories.keys()).map(category =>
|
||||
{
|
||||
return (
|
||||
<NitroCardTabsItemView key={ category } isActive={ (activeCategory.name === category) } onClick={ event => selectCategory(category) }>
|
||||
{ LocalizeText(`avatareditor.category.${ category }`) }
|
||||
</NitroCardTabsItemView>
|
||||
);
|
||||
})}
|
||||
</NitroCardTabsView>
|
||||
<NitroCardContentView>
|
||||
<div className="row h-100">
|
||||
<div className="col-3">
|
||||
</div>
|
||||
<div className="col">
|
||||
</div>
|
||||
</div>
|
||||
{ activeCategory && <AvatarEditorModelView model={ activeCategory } editor={ avatarEditor } /> }
|
||||
</NitroCardContentView>
|
||||
</NitroCardView> }
|
||||
</AvatarEditorContextProvider>
|
||||
|
289
src/views/avatar-editor/common/AvatarEditor.ts
Normal file
289
src/views/avatar-editor/common/AvatarEditor.ts
Normal file
@ -0,0 +1,289 @@
|
||||
import { FigureData, IPalette, IPartColor, ISetType, IStructureData } from 'nitro-renderer';
|
||||
import { GetAvatarRenderManager, GetConfiguration, GetSessionDataManager } from '../../../api';
|
||||
import { AvatarEditorGridColorItem } from './AvatarEditorGridColorItem';
|
||||
import { AvatarEditorGridPartItem } from './AvatarEditorGridPartItem';
|
||||
import { CategoryBaseModel } from './CategoryBaseModel';
|
||||
import { CategoryData } from './CategoryData';
|
||||
|
||||
const MAX_PALETTES: number = 2;
|
||||
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 class AvatarEditor
|
||||
{
|
||||
private _figureStructureData: IStructureData;
|
||||
private _figures: Map<string, FigureData>;
|
||||
private _gender: string;
|
||||
|
||||
constructor()
|
||||
{
|
||||
this._figureStructureData = GetAvatarRenderManager().structureData;
|
||||
this._figures = new Map();
|
||||
this._gender = FigureData.MALE;
|
||||
|
||||
const maleFigure = new FigureData();
|
||||
const femaleFigure = new FigureData();
|
||||
|
||||
maleFigure.loadAvatarData(DEFAULT_MALE_FIGURE, FigureData.MALE);
|
||||
femaleFigure.loadAvatarData(DEFAULT_FEMALE_FIGURE, FigureData.FEMALE);
|
||||
|
||||
this._figures.set(FigureData.MALE, maleFigure);
|
||||
this._figures.set(FigureData.FEMALE, femaleFigure);
|
||||
}
|
||||
|
||||
public getSetType(setType: string): ISetType
|
||||
{
|
||||
if(!this._figureStructureData) return null;
|
||||
|
||||
return this._figureStructureData.getSetType(setType);
|
||||
}
|
||||
|
||||
public getPalette(paletteId: number): IPalette
|
||||
{
|
||||
if(!this._figureStructureData) return null;
|
||||
|
||||
return this._figureStructureData.getPalette(paletteId);
|
||||
}
|
||||
|
||||
public createCategory(model: CategoryBaseModel, name: string): CategoryData
|
||||
{
|
||||
if(!model || !name) return null;
|
||||
|
||||
const partItems: AvatarEditorGridPartItem[] = [];
|
||||
const colorItems: AvatarEditorGridColorItem[][] = [];
|
||||
|
||||
let i = 0;
|
||||
|
||||
while(i < MAX_PALETTES)
|
||||
{
|
||||
colorItems.push([]);
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
const setType = this.getSetType(name);
|
||||
|
||||
if(!setType) return null;
|
||||
|
||||
const palette = this.getPalette(setType.paletteID);
|
||||
|
||||
if(!palette) return null;
|
||||
|
||||
let colorIds = this.figureData.getColourIds(name);
|
||||
|
||||
if(!colorIds) colorIds = [];
|
||||
|
||||
const partColors: IPartColor[] = new Array(colorIds.length);
|
||||
const clubItemsDimmed = this.clubItemsDimmed;
|
||||
|
||||
for(const partColor of palette.colors.values())
|
||||
{
|
||||
if(partColor.isSelectable && (clubItemsDimmed || (this.clubMemberLevel >= partColor.clubLevel)))
|
||||
{
|
||||
let i = 0;
|
||||
|
||||
while(i < MAX_PALETTES)
|
||||
{
|
||||
const isDisabled = (this.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._gender, 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
mandatorySetIds = GetAvatarRenderManager().getMandatoryAvatarPartSetIds(this._gender, this.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._gender)
|
||||
{
|
||||
isValidGender = true;
|
||||
}
|
||||
}
|
||||
|
||||
if(partSet.isSelectable && isValidGender && (clubItemsDimmed || (this.clubMemberLevel >= partSet.clubLevel)))
|
||||
{
|
||||
const isDisabled = (this.clubMemberLevel < partSet.clubLevel);
|
||||
|
||||
let isValid = true;
|
||||
|
||||
if(partSet.isSellable)
|
||||
{
|
||||
//isValid = (this._inventoryService && this._inventoryService.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 || Nitro.instance.getConfiguration<boolean>("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 < MAX_PALETTES)
|
||||
{
|
||||
colorItems[i].sort(this.colorSorter);
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
return new CategoryData(name, partItems, colorItems);
|
||||
}
|
||||
|
||||
private 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;
|
||||
}
|
||||
|
||||
private 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;
|
||||
}
|
||||
|
||||
private 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 get clubMemberLevel(): number
|
||||
{
|
||||
return GetSessionDataManager().clubLevel;
|
||||
}
|
||||
|
||||
private get clubItemsFirst(): boolean
|
||||
{
|
||||
return GetConfiguration<boolean>('avatareditor.show.clubitems.first', true);
|
||||
}
|
||||
|
||||
private get clubItemsDimmed(): boolean
|
||||
{
|
||||
return GetConfiguration<boolean>('avatareditor.show.clubitems.dimmed', true);
|
||||
}
|
||||
|
||||
public get figureData(): FigureData
|
||||
{
|
||||
return this._figures.get(this._gender);
|
||||
}
|
||||
|
||||
public get gender(): string
|
||||
{
|
||||
return this._gender;
|
||||
}
|
||||
|
||||
public set gender(gender: string)
|
||||
{
|
||||
this._gender = gender;
|
||||
}
|
||||
}
|
52
src/views/avatar-editor/common/AvatarEditorGridColorItem.ts
Normal file
52
src/views/avatar-editor/common/AvatarEditorGridColorItem.ts
Normal file
@ -0,0 +1,52 @@
|
||||
import { ColorConverter, IPartColor } from 'nitro-renderer';
|
||||
|
||||
export class AvatarEditorGridColorItem
|
||||
{
|
||||
private _partColor: IPartColor;
|
||||
private _isDisabled: boolean;
|
||||
private _isHC: boolean;
|
||||
private _isSelected: boolean;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
320
src/views/avatar-editor/common/AvatarEditorGridPartItem.ts
Normal file
320
src/views/avatar-editor/common/AvatarEditorGridPartItem.ts
Normal file
@ -0,0 +1,320 @@
|
||||
import { AvatarFigurePartType, FigureData, IAvatarImageListener, IAvatarRenderManager, IFigurePart, IFigurePartSet, IGraphicAsset, IPartColor, NitroContainer, NitroSprite, TextureUtils } from 'nitro-renderer';
|
||||
import { GetAvatarRenderManager } from '../../../api';
|
||||
|
||||
export class AvatarEditorGridPartItem implements IAvatarImageListener
|
||||
{
|
||||
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;
|
||||
|
||||
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 updateThumbVisualization(): void
|
||||
{
|
||||
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 = TextureUtils.generateImageUrl(container);
|
||||
}
|
||||
|
||||
private setAlpha(container: NitroContainer, alpha: number): NitroContainer
|
||||
{
|
||||
container.alpha = alpha;
|
||||
|
||||
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 colors(partColors: IPartColor[])
|
||||
{
|
||||
this._partColors = partColors;
|
||||
|
||||
this.update();
|
||||
}
|
||||
|
||||
public get isDisabledForWearing(): boolean
|
||||
{
|
||||
return this._isDisabled;
|
||||
}
|
||||
|
||||
public set iconImage(k: NitroContainer)
|
||||
{
|
||||
this._thumbContainer = k;
|
||||
|
||||
this.update();
|
||||
}
|
||||
|
||||
public get imageUrl(): string
|
||||
{
|
||||
return this._imageUrl;
|
||||
}
|
||||
|
||||
public get colorLayerCount(): 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;
|
||||
}
|
||||
}
|
80
src/views/avatar-editor/common/BodyModel.ts
Normal file
80
src/views/avatar-editor/common/BodyModel.ts
Normal file
@ -0,0 +1,80 @@
|
||||
import { AvatarEditorFigureCategory, AvatarScaleType, AvatarSetType, FigureData, IAvatarImageListener } from 'nitro-renderer';
|
||||
import { GetAvatarRenderManager } from '../../../api';
|
||||
import { CategoryBaseModel } from './CategoryBaseModel';
|
||||
|
||||
export class BodyModel extends CategoryBaseModel implements IAvatarImageListener
|
||||
{
|
||||
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 || !this._editor || !this._editor.figureData) return;
|
||||
|
||||
const category = this._categories.get(name);
|
||||
|
||||
if(!category) return;
|
||||
|
||||
const setId = this._editor.figureData.getPartSetId(name);
|
||||
|
||||
let colorIds = this._editor.figureData.getColourIds(name);
|
||||
|
||||
if(!colorIds) colorIds = [];
|
||||
|
||||
category.selectPartId(setId);
|
||||
category.selectColorIds(colorIds);
|
||||
|
||||
for(const part of category.parts)
|
||||
{
|
||||
const figure = this._editor.figureData.getFigureStringWithFace(part.id);
|
||||
const avatarImage = GetAvatarRenderManager().createAvatarImage(figure, AvatarScaleType.LARGE, null, this);
|
||||
|
||||
const sprite = avatarImage.getImageAsSprite(AvatarSetType.HEAD);
|
||||
|
||||
if(sprite)
|
||||
{
|
||||
sprite.y = 10;
|
||||
|
||||
part.iconImage = sprite;
|
||||
|
||||
setTimeout(() => avatarImage.dispose(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
// if (this._Str_2271) this._Str_2271._Str_5614(k, _local_4.length);
|
||||
}
|
||||
|
||||
public resetFigure(figure: string): void
|
||||
{
|
||||
if(this._imageCallBackHandled) return;
|
||||
|
||||
this._imageCallBackHandled = true;
|
||||
|
||||
this.updateSelectionsFromFigure(FigureData.FACE);
|
||||
}
|
||||
|
||||
public get canSetGender(): boolean
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public get name(): string
|
||||
{
|
||||
return AvatarEditorFigureCategory.GENERIC;
|
||||
}
|
||||
}
|
248
src/views/avatar-editor/common/CategoryBaseModel.ts
Normal file
248
src/views/avatar-editor/common/CategoryBaseModel.ts
Normal file
@ -0,0 +1,248 @@
|
||||
import { AvatarEditor } from './AvatarEditor';
|
||||
import { CategoryData } from './CategoryData';
|
||||
import { IAvatarEditorCategoryModel } from './IAvatarEditorCategoryModel';
|
||||
|
||||
export class CategoryBaseModel implements IAvatarEditorCategoryModel
|
||||
{
|
||||
protected _editor: AvatarEditor;
|
||||
protected _categories: Map<string, CategoryData>;
|
||||
protected _isInitalized: boolean;
|
||||
protected _maxPaletteCount: number;
|
||||
private _disposed: boolean;
|
||||
|
||||
constructor(editor: AvatarEditor)
|
||||
{
|
||||
this._editor = editor;
|
||||
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 = this._editor.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 = this._editor.figureData.getPartSetId(figure);
|
||||
|
||||
let colorIds = this._editor.figureData.getColourIds(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 && this._editor && this._editor.figureData)
|
||||
{
|
||||
this._editor.figureData.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 && this._editor && this._editor.figureData)
|
||||
{
|
||||
this._editor.figureData.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.isDisabledForWearing)
|
||||
{
|
||||
categoryData.selectPartIndex(selectedPartIndex);
|
||||
|
||||
// open hc window
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this._maxPaletteCount = partItem.colorLayerCount;
|
||||
|
||||
this._editor.figureData.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;
|
||||
}
|
||||
|
||||
this._editor.figureData.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<string, CategoryData>
|
||||
{
|
||||
return this._categories;
|
||||
}
|
||||
|
||||
public get canSetGender(): boolean
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public get maxPaletteCount(): number
|
||||
{
|
||||
return this._maxPaletteCount;
|
||||
}
|
||||
|
||||
public set maxPaletteCount(count: number)
|
||||
{
|
||||
this._maxPaletteCount = count;
|
||||
}
|
||||
|
||||
public get name(): string
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
487
src/views/avatar-editor/common/CategoryData.ts
Normal file
487
src/views/avatar-editor/common/CategoryData.ts
Normal file
@ -0,0 +1,487 @@
|
||||
import { IPartColor } from '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.colorLayerCount, 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.colors = 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;
|
||||
}
|
||||
}
|
23
src/views/avatar-editor/common/HeadModel.ts
Normal file
23
src/views/avatar-editor/common/HeadModel.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import { AvatarEditorFigureCategory, FigureData } from 'nitro-renderer';
|
||||
import { CategoryBaseModel } from './CategoryBaseModel';
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
19
src/views/avatar-editor/common/IAvatarEditorCategoryModel.ts
Normal file
19
src/views/avatar-editor/common/IAvatarEditorCategoryModel.ts
Normal file
@ -0,0 +1,19 @@
|
||||
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<string, CategoryData>;
|
||||
canSetGender: boolean;
|
||||
maxPaletteCount: number;
|
||||
name: string;
|
||||
}
|
21
src/views/avatar-editor/common/LegModel.ts
Normal file
21
src/views/avatar-editor/common/LegModel.ts
Normal file
@ -0,0 +1,21 @@
|
||||
import { AvatarEditorFigureCategory, FigureData } from 'nitro-renderer';
|
||||
import { CategoryBaseModel } from './CategoryBaseModel';
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
22
src/views/avatar-editor/common/TorsoModel.ts
Normal file
22
src/views/avatar-editor/common/TorsoModel.ts
Normal file
@ -0,0 +1,22 @@
|
||||
import { AvatarEditorFigureCategory, FigureData } from 'nitro-renderer';
|
||||
import { CategoryBaseModel } from './CategoryBaseModel';
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
import { FC, useEffect, useState } from 'react';
|
||||
import { NitroCardGridItemView } from '../../../../layout/card/grid/item/NitroCardGridItemView';
|
||||
import { AvatarEditorFigureSetItemViewProps } from './AvatarEditorFigureSetItemView.types';
|
||||
|
||||
export const AvatarEditorFigureSetItemView: FC<AvatarEditorFigureSetItemViewProps> = props =>
|
||||
{
|
||||
const { partItem = null, onClick = null } = props;
|
||||
const [ imageUrl, setImageUrl ] = useState<string>(null);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
setImageUrl(partItem.imageUrl);
|
||||
}, [ partItem.imageUrl ]);
|
||||
|
||||
return <NitroCardGridItemView itemImage={ imageUrl } onClick={ () => onClick(partItem) } />
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
import { AvatarEditorGridPartItem } from '../../common/AvatarEditorGridPartItem';
|
||||
|
||||
export interface AvatarEditorFigureSetItemViewProps
|
||||
{
|
||||
partItem: AvatarEditorGridPartItem;
|
||||
onClick: (item: AvatarEditorGridPartItem) => void;
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
import { FC, useCallback } from 'react';
|
||||
import { NitroCardGridView } from '../../../../layout/card/grid/NitroCardGridView';
|
||||
import { AvatarEditorGridPartItem } from '../../common/AvatarEditorGridPartItem';
|
||||
import { AvatarEditorFigureSetItemView } from '../figure-set-item/AvatarEditorFigureSetItemView';
|
||||
import { AvatarEditorFigureSetViewProps } from './AvatarEditorFigureSetView.types';
|
||||
|
||||
export const AvatarEditorFigureSetView: FC<AvatarEditorFigureSetViewProps> = props =>
|
||||
{
|
||||
const { model = null, category = null } = props;
|
||||
|
||||
const selectPart = useCallback((part: AvatarEditorGridPartItem) =>
|
||||
{
|
||||
const index = category.parts.indexOf(part);
|
||||
|
||||
if(index === -1) return;
|
||||
|
||||
model.selectPart(category.name, index);
|
||||
}, [ model, category ]);
|
||||
|
||||
return (
|
||||
<NitroCardGridView columns={ 3 }>
|
||||
{ (category.parts.length > 0) && category.parts.map((item, index) =>
|
||||
{
|
||||
return <AvatarEditorFigureSetItemView key={ index } partItem={ item } onClick={ selectPart } />;
|
||||
}) }
|
||||
</NitroCardGridView>
|
||||
)
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
import { CategoryData } from '../../common/CategoryData';
|
||||
import { IAvatarEditorCategoryModel } from '../../common/IAvatarEditorCategoryModel';
|
||||
|
||||
export interface AvatarEditorFigureSetViewProps
|
||||
{
|
||||
model: IAvatarEditorCategoryModel;
|
||||
category: CategoryData;
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
import { FC, useCallback, useEffect, useState } from 'react';
|
||||
import { CategoryData } from '../../common/CategoryData';
|
||||
import { AvatarEditorFigureSetView } from '../figure-set/AvatarEditorFigureSetView';
|
||||
import { AvatarEditorModelViewProps } from './AvatarEditorModelView.types';
|
||||
|
||||
export const AvatarEditorModelView: FC<AvatarEditorModelViewProps> = props =>
|
||||
{
|
||||
const { model = null, editor = null } = props;
|
||||
const [ activeCategory, setActiveCategory ] = useState<CategoryData>(null);
|
||||
|
||||
const selectGender = useCallback((gender: string) =>
|
||||
{
|
||||
editor.gender = gender;
|
||||
}, [ editor ]);
|
||||
|
||||
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;
|
||||
|
||||
model.maxPaletteCount = part.colorLayerCount;
|
||||
|
||||
break;
|
||||
}
|
||||
}, [ model ]);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
model.init();
|
||||
|
||||
for(const name of model.categories.keys())
|
||||
{
|
||||
selectCategory(name);
|
||||
|
||||
break;
|
||||
}
|
||||
}, [ model, selectCategory ]);
|
||||
|
||||
if(!activeCategory) return null;
|
||||
|
||||
return (
|
||||
<div className="row h-100">
|
||||
<div className="col-2 d-flex flex-column h-100"></div>
|
||||
<div className="col-3 d-flex flex-column h-100">
|
||||
<AvatarEditorFigureSetView model={ model } category={ activeCategory } />
|
||||
</div>
|
||||
<div className="col-3 d-flex flex-column h-100"></div>
|
||||
<div className="col-4 d-flex flex-column h-100"></div>
|
||||
</div>
|
||||
);
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
import { AvatarEditor } from '../../common/AvatarEditor';
|
||||
import { IAvatarEditorCategoryModel } from '../../common/IAvatarEditorCategoryModel';
|
||||
|
||||
export interface AvatarEditorModelViewProps
|
||||
{
|
||||
model: IAvatarEditorCategoryModel;
|
||||
editor: AvatarEditor;
|
||||
}
|
Loading…
Reference in New Issue
Block a user