mirror of
https://github.com/billsonnn/nitro-react.git
synced 2024-11-22 22:30:52 +01:00
Continue avatar editor changes
This commit is contained in:
parent
c659074eb3
commit
1664baef92
@ -1,4 +1,4 @@
|
||||
import { AvatarFigurePartType, GetAssetManager, GetAvatarRenderManager, IFigurePart, IGraphicAsset, IPartColor, NitroAlphaFilter, NitroContainer, NitroSprite, TextureUtils } from '@nitrots/nitro-renderer';
|
||||
import { AvatarFigurePartType, AvatarScaleType, AvatarSetType, GetAssetManager, GetAvatarRenderManager, IFigurePart, IGraphicAsset, IPartColor, NitroAlphaFilter, NitroContainer, NitroSprite, TextureUtils } from '@nitrots/nitro-renderer';
|
||||
import { FigureData } from './FigureData';
|
||||
import { IAvatarEditorCategoryPartItem } from './IAvatarEditorCategoryPartItem';
|
||||
|
||||
@ -40,7 +40,12 @@ export class AvatarEditorThumbnailsHelper
|
||||
return `${ setType }-${ part.partSet.id }`;
|
||||
}
|
||||
|
||||
public static async build(setType: string, part: IAvatarEditorCategoryPartItem, useColors: boolean, isDisabled: boolean = false): Promise<string>
|
||||
public static clearCache(): void
|
||||
{
|
||||
this.THUMBNAIL_CACHE.clear();
|
||||
}
|
||||
|
||||
public static async build(setType: string, part: IAvatarEditorCategoryPartItem, useColors: boolean, partColors: IPartColor[], isDisabled: boolean = false): Promise<string>
|
||||
{
|
||||
if(!setType || !setType.length || !part || !part.partSet || !part.partSet.parts || !part.partSet.parts.length) return null;
|
||||
|
||||
@ -49,7 +54,7 @@ export class AvatarEditorThumbnailsHelper
|
||||
|
||||
if(cached) return cached;
|
||||
|
||||
const buildContainer = (part: IAvatarEditorCategoryPartItem, useColors: boolean, isDisabled: boolean = false) =>
|
||||
const buildContainer = (part: IAvatarEditorCategoryPartItem, useColors: boolean, partColors: IPartColor[], isDisabled: boolean = false) =>
|
||||
{
|
||||
const container = new NitroContainer();
|
||||
const parts = part.partSet.parts.concat().sort(this.sortByDrawOrder);
|
||||
@ -82,20 +87,17 @@ export class AvatarEditorThumbnailsHelper
|
||||
|
||||
const x = asset.offsetX;
|
||||
const y = asset.offsetY;
|
||||
let partColor: IPartColor = null;
|
||||
|
||||
if(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;
|
||||
if(useColors && (part.colorLayerIndex > 0) && partColors && partColors.length)
|
||||
{
|
||||
const color = partColors[(part.colorLayerIndex - 1)];
|
||||
|
||||
if(color) sprite.tint = color.rgb;
|
||||
}
|
||||
|
||||
if(isDisabled) container.filters = [ AvatarEditorThumbnailsHelper.ALPHA_FILTER ];
|
||||
|
||||
@ -109,12 +111,12 @@ export class AvatarEditorThumbnailsHelper
|
||||
{
|
||||
const resetFigure = async (figure: string) =>
|
||||
{
|
||||
const container = buildContainer(part, useColors, isDisabled);
|
||||
const url = await TextureUtils.generateImageUrl(container);
|
||||
const container = buildContainer(part, useColors, partColors, isDisabled);
|
||||
const imageUrl = await TextureUtils.generateImageUrl(container);
|
||||
|
||||
AvatarEditorThumbnailsHelper.THUMBNAIL_CACHE.set(thumbnailKey, url);
|
||||
AvatarEditorThumbnailsHelper.THUMBNAIL_CACHE.set(thumbnailKey, imageUrl);
|
||||
|
||||
resolve(url);
|
||||
resolve(imageUrl);
|
||||
}
|
||||
|
||||
const figureContainer = GetAvatarRenderManager().createFigureContainer(`${ setType }-${ part.partSet.id }`);
|
||||
@ -134,6 +136,41 @@ export class AvatarEditorThumbnailsHelper
|
||||
});
|
||||
}
|
||||
|
||||
public static async buildForFace(figureString: string, isDisabled: boolean = false): Promise<string>
|
||||
{
|
||||
if(!figureString || !figureString.length) return null;
|
||||
|
||||
const thumbnailKey = figureString;
|
||||
const cached = this.THUMBNAIL_CACHE.get(thumbnailKey);
|
||||
|
||||
if(cached) return cached;
|
||||
|
||||
return new Promise(async (resolve, reject) =>
|
||||
{
|
||||
const resetFigure = async (figure: string) =>
|
||||
{
|
||||
const avatarImage = GetAvatarRenderManager().createAvatarImage(figure, AvatarScaleType.LARGE, null, { resetFigure, dispose: null, disposed: false });
|
||||
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
|
||||
});
|
||||
|
||||
sprite.destroy();
|
||||
avatarImage.dispose();
|
||||
|
||||
if(!avatarImage.isPlaceholder()) AvatarEditorThumbnailsHelper.THUMBNAIL_CACHE.set(thumbnailKey, imageUrl);
|
||||
|
||||
resolve(imageUrl);
|
||||
}
|
||||
|
||||
resetFigure(figureString);
|
||||
});
|
||||
}
|
||||
|
||||
private static sortByDrawOrder(a: IFigurePart, b: IFigurePart): number
|
||||
{
|
||||
const indexA = AvatarEditorThumbnailsHelper.DRAW_ORDER.indexOf(a.type);
|
||||
|
@ -12,7 +12,7 @@ const DEFAULT_FEMALE_FIGURE: string = 'hr-515-33.hd-600-1.ch-635-70.lg-716-66-62
|
||||
export const AvatarEditorNewView: FC<{}> = props =>
|
||||
{
|
||||
const [ isVisible, setIsVisible ] = useState(false);
|
||||
const { avatarModels, activeModelKey, setActiveModelKey } = useAvatarEditor();
|
||||
const { setIsVisible: setEditorVisibility, avatarModels, activeModelKey, setActiveModelKey } = useAvatarEditor();
|
||||
|
||||
const processAction = (action: string) =>
|
||||
{
|
||||
@ -59,6 +59,11 @@ export const AvatarEditorNewView: FC<{}> = props =>
|
||||
return () => RemoveLinkEventTracker(linkTracker);
|
||||
}, []);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
setEditorVisibility(isVisible)
|
||||
}, [ isVisible, setEditorVisibility ]);
|
||||
|
||||
if(!isVisible) return null;
|
||||
|
||||
return (
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { FC, useEffect, useState } from 'react';
|
||||
import { AvatarEditorThumbnailsHelper, GetConfigurationValue, IAvatarEditorCategoryPartItem } from '../../../../api';
|
||||
import { AvatarEditorThumbnailsHelper, FigureData, GetConfigurationValue, IAvatarEditorCategoryPartItem } from '../../../../api';
|
||||
import { LayoutCurrencyIcon, LayoutGridItem, LayoutGridItemProps } from '../../../../common';
|
||||
import { useAvatarEditor } from '../../../../hooks';
|
||||
import { AvatarEditorIcon } from '../AvatarEditorIcon';
|
||||
|
||||
export const AvatarEditorFigureSetItemView: FC<{
|
||||
@ -11,6 +12,7 @@ export const AvatarEditorFigureSetItemView: FC<{
|
||||
{
|
||||
const { setType = null, partItem = null, isSelected = false, ...rest } = props;
|
||||
const [ assetUrl, setAssetUrl ] = useState<string>('');
|
||||
const { selectedColorParts = null, getFigureStringWithFace = null } = useAvatarEditor();
|
||||
|
||||
const isHC = !GetConfigurationValue<boolean>('hc.disabled', false) && ((partItem.partSet?.clubLevel ?? 0) > 0);
|
||||
|
||||
@ -21,18 +23,28 @@ export const AvatarEditorFigureSetItemView: FC<{
|
||||
const loadImage = async () =>
|
||||
{
|
||||
const isHC = !GetConfigurationValue<boolean>('hc.disabled', false) && ((partItem.partSet?.clubLevel ?? 0) > 0);
|
||||
const url = await AvatarEditorThumbnailsHelper.build(setType, partItem, partItem.usesColor, isHC);
|
||||
|
||||
let url: string = null;
|
||||
|
||||
if(setType === FigureData.FACE)
|
||||
{
|
||||
url = await AvatarEditorThumbnailsHelper.buildForFace(getFigureStringWithFace(partItem.id), isHC);
|
||||
}
|
||||
else
|
||||
{
|
||||
url = await AvatarEditorThumbnailsHelper.build(setType, partItem, partItem.usesColor, selectedColorParts[setType] ?? null, isHC);
|
||||
}
|
||||
|
||||
if(url && url.length) setAssetUrl(url);
|
||||
}
|
||||
|
||||
loadImage();
|
||||
}, [ setType, partItem ]);
|
||||
}, [ setType, partItem, selectedColorParts, getFigureStringWithFace ]);
|
||||
|
||||
if(!partItem || !partItem.partSet) return null;
|
||||
if(!partItem) return null;
|
||||
|
||||
return (
|
||||
<LayoutGridItem itemImage={ (partItem.isClear ? undefined : assetUrl) } itemActive={ isSelected } style={ { width: '100%' } } { ...rest }>
|
||||
<LayoutGridItem itemImage={ (partItem.isClear ? undefined : assetUrl) } itemActive={ isSelected } style={ { width: '100%', 'flex': '1' } } { ...rest }>
|
||||
{ !partItem.isClear && isHC && <LayoutCurrencyIcon className="position-absolute end-1 bottom-1" type="hc" /> }
|
||||
{ partItem.isClear && <AvatarEditorIcon icon="clear" /> }
|
||||
{ !partItem.isClear && partItem.partSet.isSellable && <AvatarEditorIcon icon="sellable" position="absolute" className="end-1 bottom-1" /> }
|
||||
|
@ -9,7 +9,7 @@ export const AvatarEditorFigureSetView: FC<{
|
||||
}> = props =>
|
||||
{
|
||||
const { category = null } = props;
|
||||
const { selectedParts = null, selectPart } = useAvatarEditor();
|
||||
const { selectedParts = null, selectEditorPart } = useAvatarEditor();
|
||||
const elementRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const isPartItemSelected = (partItem: IAvatarEditorCategoryPartItem) =>
|
||||
@ -29,7 +29,7 @@ export const AvatarEditorFigureSetView: FC<{
|
||||
if(!item) return null;
|
||||
|
||||
return (
|
||||
<AvatarEditorFigureSetItemView key={ item.id } setType={ category.setType } partItem={ item } isSelected={ isPartItemSelected(item) } onClick={ event => selectPart(category.setType, item.partSet?.id ?? -1) } style={ { width: ~~(100 / columnCount) + '%' } } />
|
||||
<AvatarEditorFigureSetItemView key={ item.id } setType={ category.setType } partItem={ item } isSelected={ isPartItemSelected(item) } onClick={ event => selectEditorPart(category.setType, item.partSet?.id ?? -1) } />
|
||||
)
|
||||
} } />
|
||||
);
|
||||
|
@ -12,7 +12,7 @@ export const AvatarEditorPaletteSetView: FC<{
|
||||
{
|
||||
const { category = null, paletteIndex = -1 } = props;
|
||||
const paletteSet = category?.colorItems[paletteIndex] ?? null;
|
||||
const { selectedColors = null, selectColor } = useAvatarEditor();
|
||||
const { selectedColors = null, selectEditorColor } = useAvatarEditor();
|
||||
const elementRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const isPartColorSelected = (partColor: IPartColor) =>
|
||||
@ -27,7 +27,7 @@ export const AvatarEditorPaletteSetView: FC<{
|
||||
return (
|
||||
<AutoGrid innerRef={ elementRef } gap={ 1 } columnCount={ 5 } columnMinWidth={ 30 }>
|
||||
{ (paletteSet.length > 0) && paletteSet.map(item =>
|
||||
<AvatarEditorPaletteSetItem key={ item.id } setType={ category.setType } partColor={ item } isSelected={ isPartColorSelected(item) } onClick={ event => selectColor(category.setType, paletteIndex, item.id) } />) }
|
||||
<AvatarEditorPaletteSetItem key={ item.id } setType={ category.setType } partColor={ item } isSelected={ isPartColorSelected(item) } onClick={ event => selectEditorColor(category.setType, paletteIndex, item.id) } />) }
|
||||
</AutoGrid>
|
||||
);
|
||||
}
|
||||
|
@ -1 +1,2 @@
|
||||
export * from './useAvatarEditor';
|
||||
export * from './useFigureData';
|
||||
|
@ -1,23 +1,49 @@
|
||||
import { AvatarEditorFigureCategory, GetAvatarRenderManager, IFigurePartSet, IPartColor } from '@nitrots/nitro-renderer';
|
||||
import { AvatarEditorFigureCategory, FigureSetIdsMessageEvent, GetAvatarRenderManager, GetSessionDataManager, IFigurePartSet, IPartColor } from '@nitrots/nitro-renderer';
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { useBetween } from 'use-between';
|
||||
import { FigureData, GetClubMemberLevel, GetConfigurationValue, IAvatarEditorCategory, IAvatarEditorCategoryPartItem } from '../../api';
|
||||
import { AvatarEditorThumbnailsHelper, FigureData, GetClubMemberLevel, IAvatarEditorCategory, IAvatarEditorCategoryPartItem } from '../../api';
|
||||
import { useMessageEvent } from '../events';
|
||||
import { useFigureData } from './useFigureData';
|
||||
|
||||
const MAX_PALETTES: number = 2;
|
||||
|
||||
const useAvatarEditorState = () =>
|
||||
{
|
||||
const [ isVisible, setIsVisible ] = useState<boolean>(false);
|
||||
const [ avatarModels, setAvatarModels ] = useState<{ [index: string]: IAvatarEditorCategory[] }>({});
|
||||
const [ activeModelKey, setActiveModelKey ] = useState<string>('');
|
||||
const [ selectedParts, setSelectedParts ] = useState<{ [index: string]: number }>({});
|
||||
const [ selectedColors, setSelectedColors ] = useState<{ [index: string]: { [index: number]: number }}>({});
|
||||
const [ maxPaletteCount, setMaxPaletteCount ] = useState<number>(1);
|
||||
const [ figureSetIds, setFigureSetIds ] = useState<number[]>([]);
|
||||
const [ boundFurnitureNames, setBoundFurnitureNames ] = useState<string[]>([]);
|
||||
const { gender, selectedParts, selectedColors, loadAvatarData, selectPart, selectColor, getFigureStringWithFace } = useFigureData();
|
||||
|
||||
const clubItemsFirst = useMemo(() => GetConfigurationValue<boolean>('avatareditor.show.clubitems.first', true), []);
|
||||
const clubItemsDimmed = useMemo(() => GetConfigurationValue<boolean>('avatareditor.show.clubitems.dimmed', true), []);
|
||||
const activeModel = useMemo(() => (avatarModels[activeModelKey] ?? null), [ activeModelKey, avatarModels ]);
|
||||
|
||||
const selectPart = useCallback((setType: string, partId: number) =>
|
||||
const selectedColorParts = useMemo(() =>
|
||||
{
|
||||
const colorSets: { [index: string]: IPartColor[] } = {};
|
||||
|
||||
for(const setType of Object.keys(selectedColors))
|
||||
{
|
||||
if(!selectedColors[setType]) continue;
|
||||
|
||||
const parts: IPartColor[] = [];
|
||||
|
||||
for(const paletteId of Object.keys(selectedColors[setType]))
|
||||
{
|
||||
const partColor = activeModel.find(category => (category.setType === setType))?.colorItems[paletteId]?.find(partColor => (partColor.id === selectedColors[setType][paletteId]));
|
||||
|
||||
if(partColor) parts.push(partColor);
|
||||
}
|
||||
|
||||
colorSets[setType] = parts;
|
||||
}
|
||||
|
||||
return colorSets;
|
||||
|
||||
}, [ activeModel, selectedColors ]);
|
||||
|
||||
const selectEditorPart = useCallback((setType: string, partId: number) =>
|
||||
{
|
||||
if(!setType || !setType.length) return;
|
||||
|
||||
@ -39,17 +65,10 @@ const useAvatarEditorState = () =>
|
||||
|
||||
setMaxPaletteCount(partItem.maxPaletteCount || 1);
|
||||
|
||||
setSelectedParts(prevValue =>
|
||||
{
|
||||
const newValue = { ...prevValue };
|
||||
selectPart(setType, partId);
|
||||
}, [ activeModel, selectPart ]);
|
||||
|
||||
newValue[setType] = partItem.id;
|
||||
|
||||
return newValue;
|
||||
});
|
||||
}, [ activeModel ]);
|
||||
|
||||
const selectColor = useCallback((setType: string, paletteId: number, colorId: number) =>
|
||||
const selectEditorColor = useCallback((setType: string, paletteId: number, colorId: number) =>
|
||||
{
|
||||
if(!setType || !setType.length) return;
|
||||
|
||||
@ -67,22 +86,26 @@ const useAvatarEditorState = () =>
|
||||
|
||||
if(GetClubMemberLevel() < partColor.clubLevel) return;
|
||||
|
||||
setSelectedColors(prevValue =>
|
||||
{
|
||||
const newValue = { ...prevValue };
|
||||
selectColor(setType, paletteId, colorId);
|
||||
}, [ activeModel, selectColor ]);
|
||||
|
||||
if(!newValue[setType]) newValue[setType] = {};
|
||||
useMessageEvent<FigureSetIdsMessageEvent>(FigureSetIdsMessageEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
if(!newValue[setType][paletteId]) newValue[setType][paletteId] = -1;
|
||||
|
||||
newValue[setType][paletteId] = partColor.id;
|
||||
|
||||
return newValue;
|
||||
})
|
||||
}, [ activeModel ]);
|
||||
setFigureSetIds(parser.figureSetIds);
|
||||
setBoundFurnitureNames(parser.boundsFurnitureNames);
|
||||
});
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
AvatarEditorThumbnailsHelper.clearCache();
|
||||
}, [ selectedColorParts ]);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
if(!isVisible) return;
|
||||
|
||||
const newAvatarModels: { [index: string]: IAvatarEditorCategory[] } = {};
|
||||
|
||||
const buildCategory = (setType: string) =>
|
||||
@ -117,16 +140,7 @@ const useAvatarEditorState = () =>
|
||||
} */
|
||||
}
|
||||
|
||||
let mandatorySetIds: string[] = [];
|
||||
|
||||
if(clubItemsDimmed)
|
||||
{
|
||||
//mandatorySetIds = GetAvatarRenderManager().getMandatoryAvatarPartSetIds(this.CURRENT_FIGURE.gender, 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
//mandatorySetIds = GetAvatarRenderManager().getMandatoryAvatarPartSetIds(this.CURRENT_FIGURE.gender, clubMemberLevel);
|
||||
}
|
||||
let mandatorySetIds: string[] = GetAvatarRenderManager().getMandatoryAvatarPartSetIds(gender, GetClubMemberLevel());
|
||||
|
||||
const isntMandatorySet = (mandatorySetIds.indexOf(setType) === -1);
|
||||
|
||||
@ -139,7 +153,9 @@ const useAvatarEditorState = () =>
|
||||
{
|
||||
const partSet = partSets.getWithIndex(i);
|
||||
|
||||
if(!partSet || !partSet.isSelectable) continue;
|
||||
if(!partSet || !partSet.isSelectable || ((partSet.gender !== gender) && (partSet.gender !== FigureData.UNISEX))) continue;
|
||||
|
||||
if(partSet.isSellable && figureSetIds.indexOf(partSet.id) === -1) continue;
|
||||
|
||||
let maxPaletteCount = 0;
|
||||
|
||||
@ -148,7 +164,7 @@ const useAvatarEditorState = () =>
|
||||
partItems.push({ id: partSet.id, partSet, usesColor, maxPaletteCount });
|
||||
}
|
||||
|
||||
partItems.sort(clubItemsFirst ? clubSorter : noobSorter);
|
||||
partItems.sort(partSorter(false));
|
||||
|
||||
for(let i = 0; i < MAX_PALETTES; i++) colorItems[i].sort(colorSorter);
|
||||
|
||||
@ -165,33 +181,52 @@ const useAvatarEditorState = () =>
|
||||
|
||||
setAvatarModels(newAvatarModels);
|
||||
setActiveModelKey(AvatarEditorFigureCategory.GENERIC);
|
||||
}, [ clubItemsDimmed, clubItemsFirst ]);
|
||||
}, [ isVisible, gender, figureSetIds ]);
|
||||
|
||||
return { avatarModels, activeModelKey, setActiveModelKey, selectedParts, selectedColors, maxPaletteCount, selectPart, selectColor };
|
||||
useEffect(() =>
|
||||
{
|
||||
if(!isVisible) return;
|
||||
|
||||
loadAvatarData(GetSessionDataManager().figure, GetSessionDataManager().gender);
|
||||
}, [ isVisible, loadAvatarData ]);
|
||||
|
||||
return { isVisible, setIsVisible, avatarModels, activeModelKey, setActiveModelKey, selectedParts, selectedColors, maxPaletteCount, selectedColorParts, selectEditorPart, selectEditorColor, getFigureStringWithFace };
|
||||
}
|
||||
|
||||
export const useAvatarEditor = () => useBetween(useAvatarEditorState);
|
||||
|
||||
const clubSorter = (a: { partSet: IFigurePartSet, usesColor: boolean, isClear?: boolean }, b: { partSet: IFigurePartSet, usesColor: boolean, isClear?: boolean }) =>
|
||||
const partSorter = (hcFirst: boolean) =>
|
||||
{
|
||||
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);
|
||||
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(isSellableA && !isSellableB) return 1;
|
||||
|
||||
if(isSellableB && !isSellableA) return -1;
|
||||
if(isSellableB && !isSellableA) return -1;
|
||||
|
||||
if(clubLevelA > clubLevelB) return -1;
|
||||
if(hcFirst)
|
||||
{
|
||||
if(clubLevelA > clubLevelB) return -1;
|
||||
|
||||
if(clubLevelA < clubLevelB) return 1;
|
||||
if(clubLevelA < clubLevelB) return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(clubLevelA < clubLevelB) return -1;
|
||||
|
||||
if(a.partSet.id > b.partSet.id) 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;
|
||||
if(a.partSet.id > b.partSet.id) return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
const colorSorter = (a: IPartColor, b: IPartColor) =>
|
||||
@ -209,25 +244,3 @@ const colorSorter = (a: IPartColor, b: IPartColor) =>
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const noobSorter = (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(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;
|
||||
}
|
||||
|
114
src/hooks/avatar-editor/useFigureData.ts
Normal file
114
src/hooks/avatar-editor/useFigureData.ts
Normal file
@ -0,0 +1,114 @@
|
||||
import { useCallback, useState } from 'react';
|
||||
import { FigureData } from '../../api';
|
||||
|
||||
const useFigureDataState = () =>
|
||||
{
|
||||
const [ selectedParts, setSelectedParts ] = useState<{ [index: string]: number }>({});
|
||||
const [ selectedColors, setSelectedColors ] = useState<{ [index: string]: number[] }>({});
|
||||
const [ gender, setGender ] = useState<string>(FigureData.MALE);
|
||||
|
||||
const loadAvatarData = useCallback((figureString: string, gender: string) =>
|
||||
{
|
||||
const parse = (figure: string) =>
|
||||
{
|
||||
const sets = figure.split('.');
|
||||
|
||||
if(!sets || !sets.length) return;
|
||||
|
||||
const partSets: { [index: string]: number } = {};
|
||||
const colorSets: { [index: string]: number[] } = {};
|
||||
|
||||
for(const set of sets)
|
||||
{
|
||||
const parts = set.split('-');
|
||||
|
||||
if(!parts.length) continue;
|
||||
|
||||
const setType = parts[0];
|
||||
const partId = 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);
|
||||
|
||||
if(partId >= 0) partSets[setType] = partId;
|
||||
|
||||
if(colorIds.length) colorSets[setType] = colorIds;
|
||||
}
|
||||
|
||||
return { partSets, colorSets };
|
||||
}
|
||||
|
||||
const { partSets, colorSets } = parse(figureString);
|
||||
|
||||
setSelectedParts(partSets);
|
||||
setSelectedColors(colorSets);
|
||||
setGender(gender);
|
||||
}, []);
|
||||
|
||||
const selectPart = useCallback((setType: string, partId: number) =>
|
||||
{
|
||||
if(!setType || !setType.length) return;
|
||||
|
||||
setSelectedParts(prevValue =>
|
||||
{
|
||||
const newValue = { ...prevValue };
|
||||
|
||||
newValue[setType] = partId;
|
||||
|
||||
return newValue;
|
||||
});
|
||||
}, []);
|
||||
|
||||
const selectColor = useCallback((setType: string, paletteId: number, colorId: number) =>
|
||||
{
|
||||
if(!setType || !setType.length) return;
|
||||
|
||||
setSelectedColors(prevValue =>
|
||||
{
|
||||
const newValue = { ...prevValue };
|
||||
|
||||
if(!newValue[setType]) newValue[setType] = [];
|
||||
|
||||
if(!newValue[setType][paletteId]) newValue[setType][paletteId] = 0;
|
||||
|
||||
newValue[setType][paletteId] = colorId;
|
||||
|
||||
return newValue;
|
||||
})
|
||||
}, []);
|
||||
|
||||
const getFigureStringWithFace = useCallback((overridePartId: number, override: boolean = true) =>
|
||||
{
|
||||
const figureSets = [ FigureData.FACE ].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];
|
||||
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('');
|
||||
}
|
||||
|
||||
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 };
|
||||
}
|
||||
|
||||
export const useFigureData = useFigureDataState;
|
Loading…
Reference in New Issue
Block a user