diff --git a/src/api/nitro/avatar/GetAvatarPalette.ts b/src/api/nitro/avatar/GetAvatarPalette.ts new file mode 100644 index 00000000..af3526c3 --- /dev/null +++ b/src/api/nitro/avatar/GetAvatarPalette.ts @@ -0,0 +1,7 @@ +import { IPalette } from 'nitro-renderer'; +import { GetAvatarRenderManager } from './GetAvatarRenderManager'; + +export function GetAvatarPalette(paletteId: number): IPalette +{ + return GetAvatarRenderManager().structureData.getPalette(paletteId); +} diff --git a/src/api/nitro/avatar/GetAvatarSetType.ts b/src/api/nitro/avatar/GetAvatarSetType.ts new file mode 100644 index 00000000..ba7c8edd --- /dev/null +++ b/src/api/nitro/avatar/GetAvatarSetType.ts @@ -0,0 +1,7 @@ +import { ISetType } from 'nitro-renderer'; +import { GetAvatarRenderManager } from './GetAvatarRenderManager'; + +export function GetAvatarSetType(setType: string): ISetType +{ + return GetAvatarRenderManager().structureData.getSetType(setType); +} diff --git a/src/api/nitro/avatar/index.ts b/src/api/nitro/avatar/index.ts index 0076857a..258a1ced 100644 --- a/src/api/nitro/avatar/index.ts +++ b/src/api/nitro/avatar/index.ts @@ -1 +1,3 @@ +export * from './GetAvatarPalette'; export * from './GetAvatarRenderManager'; +export * from './GetAvatarSetType'; diff --git a/src/api/nitro/session/GetClubMemberLevel.ts b/src/api/nitro/session/GetClubMemberLevel.ts new file mode 100644 index 00000000..be5e3ffc --- /dev/null +++ b/src/api/nitro/session/GetClubMemberLevel.ts @@ -0,0 +1,6 @@ +import { GetSessionDataManager } from './GetSessionDataManager'; + +export function GetClubMemberLevel(): number +{ + return GetSessionDataManager().clubLevel; +} diff --git a/src/api/nitro/session/index.ts b/src/api/nitro/session/index.ts index ff62191c..31861a96 100644 --- a/src/api/nitro/session/index.ts +++ b/src/api/nitro/session/index.ts @@ -1,6 +1,7 @@ export * from './CanManipulateFurniture'; export * from './GetCanStandUp'; export * from './GetCanUseExpression'; +export * from './GetClubMemberLevel'; export * from './GetFurnitureDataForProductOffer'; export * from './GetFurnitureDataForRoomObject'; export * from './GetOwnPosture'; diff --git a/src/assets/images/avatareditor/clear-icon.png b/src/assets/images/avatareditor/clear-icon.png index 5123270a..e0d50abc 100644 Binary files a/src/assets/images/avatareditor/clear-icon.png and b/src/assets/images/avatareditor/clear-icon.png differ diff --git a/src/assets/images/avatareditor/female-icon.png b/src/assets/images/avatareditor/female-icon.png index ff7bf11f..8e6e8202 100644 Binary files a/src/assets/images/avatareditor/female-icon.png and b/src/assets/images/avatareditor/female-icon.png differ diff --git a/src/assets/images/avatareditor/female-selected-icon.png b/src/assets/images/avatareditor/female-selected-icon.png index bfddd6b2..50ffde09 100644 Binary files a/src/assets/images/avatareditor/female-selected-icon.png and b/src/assets/images/avatareditor/female-selected-icon.png differ diff --git a/src/assets/images/avatareditor/lg-icon.png b/src/assets/images/avatareditor/lg-icon.png index aa9782f6..0bdd750e 100644 Binary files a/src/assets/images/avatareditor/lg-icon.png and b/src/assets/images/avatareditor/lg-icon.png differ diff --git a/src/assets/images/avatareditor/lg-selected-icon.png b/src/assets/images/avatareditor/lg-selected-icon.png index d2dfdc72..7a2853b0 100644 Binary files a/src/assets/images/avatareditor/lg-selected-icon.png and b/src/assets/images/avatareditor/lg-selected-icon.png differ diff --git a/src/assets/images/avatareditor/male-icon.png b/src/assets/images/avatareditor/male-icon.png index aeca1540..95a1b352 100644 Binary files a/src/assets/images/avatareditor/male-icon.png and b/src/assets/images/avatareditor/male-icon.png differ diff --git a/src/assets/images/avatareditor/male-selected-icon.png b/src/assets/images/avatareditor/male-selected-icon.png index 3b437c7b..85debbb3 100644 Binary files a/src/assets/images/avatareditor/male-selected-icon.png and b/src/assets/images/avatareditor/male-selected-icon.png differ diff --git a/src/assets/images/avatareditor/sh-icon.png b/src/assets/images/avatareditor/sh-icon.png index 3fe5a50b..915c7c1e 100644 Binary files a/src/assets/images/avatareditor/sh-icon.png and b/src/assets/images/avatareditor/sh-icon.png differ diff --git a/src/assets/images/avatareditor/sh-selected-icon.png b/src/assets/images/avatareditor/sh-selected-icon.png index 3bdb6052..12c6deb1 100644 Binary files a/src/assets/images/avatareditor/sh-selected-icon.png and b/src/assets/images/avatareditor/sh-selected-icon.png differ diff --git a/src/assets/images/avatareditor/wa-icon.png b/src/assets/images/avatareditor/wa-icon.png index 75dffe5b..8a73b7ae 100644 Binary files a/src/assets/images/avatareditor/wa-icon.png and b/src/assets/images/avatareditor/wa-icon.png differ diff --git a/src/assets/images/avatareditor/wa-selected-icon.png b/src/assets/images/avatareditor/wa-selected-icon.png index b86dc902..5348be31 100644 Binary files a/src/assets/images/avatareditor/wa-selected-icon.png and b/src/assets/images/avatareditor/wa-selected-icon.png differ diff --git a/src/assets/styles/icons.scss b/src/assets/styles/icons.scss index a56cfd12..477a46ab 100644 --- a/src/assets/styles/icons.scss +++ b/src/assets/styles/icons.scss @@ -179,15 +179,14 @@ &.clear-icon { background-image: url('../images/avatareditor/clear-icon.png'); - width: 16px; - height: 16px; + width: 27px; + height: 27px; } &.ca-icon { background-image: url('../images/avatareditor/ca-icon.png'); - width: 30px; - height: 24px; - background + width: 25px; + height: 25px; &.selected { background-image: url('../images/avatareditor/ca-selected-icon.png'); @@ -216,8 +215,8 @@ &.cp-icon { background-image: url('../images/avatareditor/cp-icon.png'); - width: 25px; - height: 25px; + width: 30px; + height: 24px; &.selected { background-image: url('../images/avatareditor/cp-selected-icon.png'); diff --git a/src/layout/card/content/NitroCardContentView.scss b/src/layout/card/content/NitroCardContentView.scss index 0bc895aa..4f94a06a 100644 --- a/src/layout/card/content/NitroCardContentView.scss +++ b/src/layout/card/content/NitroCardContentView.scss @@ -1,6 +1,8 @@ .content-area { padding-top: $container-padding-x; padding-bottom: $container-padding-x; + resize: vertical; + overflow: auto; &.simple { padding-left: ($container-padding-x + 25px); diff --git a/src/layout/card/grid/NitroCardGridView.tsx b/src/layout/card/grid/NitroCardGridView.tsx index 4726910c..a9a85c64 100644 --- a/src/layout/card/grid/NitroCardGridView.tsx +++ b/src/layout/card/grid/NitroCardGridView.tsx @@ -4,11 +4,11 @@ import { NitroCardGridThemes, NitroCardGridViewProps } from './NitroCardGridView export const NitroCardGridView: FC = props => { - const { columns = 5, theme = NitroCardGridThemes.THEME_DEFAULT, children = null } = props; + const { columns = 5, theme = NitroCardGridThemes.THEME_DEFAULT, className = '', children = null, ...rest } = props; return ( -
+
{ children }
diff --git a/src/layout/card/grid/NitroCardGridView.types.ts b/src/layout/card/grid/NitroCardGridView.types.ts index 58c052ac..f3cc6c80 100644 --- a/src/layout/card/grid/NitroCardGridView.types.ts +++ b/src/layout/card/grid/NitroCardGridView.types.ts @@ -1,4 +1,6 @@ -export interface NitroCardGridViewProps +import { DetailsHTMLAttributes } from 'react'; + +export interface NitroCardGridViewProps extends DetailsHTMLAttributes { columns?: number; theme?: string; diff --git a/src/layout/card/grid/item/NitroCardGridItemView.scss b/src/layout/card/grid/item/NitroCardGridItemView.scss index 19f6c716..69de83c1 100644 --- a/src/layout/card/grid/item/NitroCardGridItemView.scss +++ b/src/layout/card/grid/item/NitroCardGridItemView.scss @@ -3,6 +3,9 @@ max-height: 50px; .grid-item { + display: flex; + justify-content: center; + align-items: center; position: relative; width: 100%; height: 100%; diff --git a/src/views/avatar-editor/AvatarEditorView.scss b/src/views/avatar-editor/AvatarEditorView.scss index d94a3cdc..aaef735d 100644 --- a/src/views/avatar-editor/AvatarEditorView.scss +++ b/src/views/avatar-editor/AvatarEditorView.scss @@ -1,9 +1,13 @@ .nitro-avatar-editor { - width: 600px; + width: 620px; .content-area { - height: 330px; - max-height: 330px; + min-height: 300px; + height: 300px; + } + + .category-item { + height: 40px; } .figure-preview-container { @@ -13,27 +17,53 @@ overflow: hidden; z-index: 1; - .avatar-image { - margin: 45px auto 0; - z-index: 2; - } - .arrow-container { position: absolute; width: 100%; margin: 0 auto; + padding: 0 10px; display: flex; - justify-content: center; + justify-content: space-between; bottom: 12px; - z-index: 3; + z-index: 5; .icon { cursor: pointer; } - - .arrow-left-icon { - margin-right: 10px; - } + } + + .avatar-image { + position: absolute; + left: 0; + right: 0; + bottom: 50px; + margin: 0 auto; + z-index: 4; + } + + .avatar-spotlight { + position: absolute; + top: -10px; + width: 100%; + height: 305px; + margin: 0 auto; + background: transparent url('../../assets/images/avatareditor/spotlight.png') no-repeat center; + 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 { @@ -44,7 +74,8 @@ left: 0; right: 0; border-radius: 50%; - background-color: red; + background-color: $pale-sky; + box-shadow: 0 0 8px 2px rgba($white,.6); transform: scale(2); } } diff --git a/src/views/avatar-editor/AvatarEditorView.tsx b/src/views/avatar-editor/AvatarEditorView.tsx index b8d5a07d..1695ad15 100644 --- a/src/views/avatar-editor/AvatarEditorView.tsx +++ b/src/views/avatar-editor/AvatarEditorView.tsx @@ -1,103 +1,91 @@ -import { AvatarEditorFigureCategory } from 'nitro-renderer'; -import { FC, useCallback, useEffect, useReducer, useState } from 'react'; +import { AvatarDirectionAngle, AvatarEditorFigureCategory, UserFigureComposer } from 'nitro-renderer'; +import { FC, useCallback, useEffect, useState } from 'react'; import { GetSessionDataManager } from '../../api'; import { AvatarEditorEvent } from '../../events/avatar-editor'; +import { SendMessageHook } from '../../hooks'; import { useUiEvent } from '../../hooks/events/ui/ui-event'; import { NitroCardContentView, NitroCardHeaderView, NitroCardTabsItemView, NitroCardTabsView, NitroCardView } from '../../layout'; import { LocalizeText } from '../../utils/LocalizeText'; import { AvatarEditorViewProps } from './AvatarEditorView.types'; -import { AvatarEditor } from './common/AvatarEditor'; +import { AvatarEditorUtilities } from './common/AvatarEditorUtilities'; import { BodyModel } from './common/BodyModel'; import { FigureData } from './common/FigureData'; 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 { AvatarEditorFigurePreviewView } from './views/figure-preview/AvatarEditorFigurePreviewView'; import { AvatarEditorModelView } from './views/model/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 AvatarEditorView: FC = props => { const [ isVisible, setIsVisible ] = useState(false); - const [ avatarEditorState, dispatchAvatarEditorState ] = useReducer(AvatarEditorReducer, initialAvatarEditor); - const [ avatarEditor, setAvatarEditor ] = useState(null); + const [ figures, setFigures ] = useState>(null); + const [ figureData, setFigureData ] = useState(null); const [ categories, setCategories ] = useState>(null); const [ activeCategory, setActiveCategory ] = useState(null); + const [ lastFigure, setLastFigure ] = useState(null); + const [ lastGender, setLastGender ] = useState(null); + const [ needsReset, setNeedsReset ] = useState(false); const [ isInitalized, setIsInitalized ] = useState(false); const selectCategory = useCallback((name: string) => { + if(!categories) return; + setActiveCategory(categories.get(name)); }, [ categories ]); - const resetCategories = useCallback((editor: AvatarEditor) => + const resetCategories = useCallback(() => { const categories = new Map(); - categories.set(AvatarEditorFigureCategory.GENERIC, new BodyModel(editor)); - categories.set(AvatarEditorFigureCategory.HEAD, new HeadModel(editor)); - categories.set(AvatarEditorFigureCategory.TORSO, new TorsoModel(editor)); - categories.set(AvatarEditorFigureCategory.LEGS, new LegModel(editor)); + 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); - setActiveCategory(categories.get(AvatarEditorFigureCategory.GENERIC)); }, []); - const selectGender = useCallback((gender: string) => + const setupFigures = useCallback(() => { - if(gender === avatarEditor.gender) return; - - avatarEditor.gender = gender; + const figures: Map = new Map(); - resetCategories(avatarEditor); - }, [ avatarEditor, resetCategories ]); + 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) => { - if(!avatarEditor) return; - - switch(gender) + 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) { - 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; + setLastFigure(figureData.getFigureString()); + setLastGender(figureData.gender); } - - let update = false; - - if(gender !== avatarEditor.gender) - { - avatarEditor.gender = gender; - - update = true; - } - - const figureData = avatarEditor.figureData; - - if(!figureData) return; - - if(figure !== figureData.getFigureString()) - { - update = true; - } - - figureData.loadAvatarData(figure, gender); - - if(update) - { - resetCategories(avatarEditor); - } - }, [ avatarEditor, resetCategories ]); + }, [ figures, figureData ]); const onAvatarEditorEvent = useCallback((event: AvatarEditorEvent) => { @@ -105,12 +93,20 @@ export const AvatarEditorView: FC = props => { case AvatarEditorEvent.SHOW_EDITOR: setIsVisible(true); + setNeedsReset(true); return; case AvatarEditorEvent.HIDE_EDITOR: setIsVisible(false); return; case AvatarEditorEvent.TOGGLE_EDITOR: - setIsVisible(prevValue => !prevValue); + setIsVisible(prevValue => + { + const flag = !prevValue; + + if(flag) setNeedsReset(true); + + return flag; + }); return; } }, []); @@ -119,42 +115,130 @@ export const AvatarEditorView: FC = props => useUiEvent(AvatarEditorEvent.HIDE_EDITOR, onAvatarEditorEvent); useUiEvent(AvatarEditorEvent.TOGGLE_EDITOR, onAvatarEditorEvent); - useEffect(() => + const clearFigure = useCallback(() => { - if(!isVisible || isInitalized) return; + loadAvatarInEditor(figureData.getFigureStringWithFace(0, false), figureData.gender, false); + resetCategories(); + }, [ figureData, loadAvatarInEditor, resetCategories ]); - const newEditor = new AvatarEditor(); + const resetFigure = useCallback(() => + { + loadAvatarInEditor(lastFigure, lastGender); + resetCategories(); + }, [ lastFigure, lastGender, loadAvatarInEditor, resetCategories ]); - setAvatarEditor(newEditor); - setIsInitalized(true); - }, [ isVisible, isInitalized ]); + const rotateFigure = useCallback((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; + }, [ figureData ]); + + const saveFigure = useCallback(() => + { + SendMessageHook(new UserFigureComposer(figureData.gender, figureData.getFigureString())); + }, [ figureData ]); + + const setGender = useCallback((gender: string) => + { + gender = AvatarEditorUtilities.getGender(gender); + + setFigureData(figures.get(gender)); + }, [ figures ]); useEffect(() => { - if(!isVisible || !avatarEditor) return; + 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(() => + { + if(!isVisible) return; + + if(!figures) + { + setupFigures(); + + setIsInitalized(true); + + return; + } + }, [ isVisible, figures, setupFigures ]); + + useEffect(() => + { + if(!isVisible || !isInitalized || !needsReset) return; loadAvatarInEditor(GetSessionDataManager().figure, GetSessionDataManager().gender); - }, [ isVisible, avatarEditor, loadAvatarInEditor ]); + setNeedsReset(false); + }, [ isVisible, isInitalized, needsReset, loadAvatarInEditor ]); + + if(!isVisible) return null; return ( - - { isVisible && - - setIsVisible(false) } /> - - { categories && (categories.size > 0) && Array.from(categories.keys()).map(category => - { - return ( - selectCategory(category) }> - { LocalizeText(`avatareditor.category.${ category }`) } - - ); - })} - - - { activeCategory && } - - } - + + setIsVisible(false) } /> + + { categories && (categories.size > 0) && activeCategory && Array.from(categories.keys()).map(category => + { + return ( + selectCategory(category) }> + { LocalizeText(`avatareditor.category.${ category }`) } + + ); + })} + + +
+
+ { activeCategory && } +
+
+ { figureData && +
+ +
+
+
+ rotateFigure(figureData.direction + 1) } /> + rotateFigure(figureData.direction - 1) } /> +
+
} +
+
+ + +
+ +
+
+
+ + ); } diff --git a/src/views/avatar-editor/common/AvatarEditor.ts b/src/views/avatar-editor/common/AvatarEditorUtilities.ts similarity index 62% rename from src/views/avatar-editor/common/AvatarEditor.ts rename to src/views/avatar-editor/common/AvatarEditorUtilities.ts index 3ba88477..eb8b9d25 100644 --- a/src/views/avatar-editor/common/AvatarEditor.ts +++ b/src/views/avatar-editor/common/AvatarEditorUtilities.ts @@ -1,88 +1,79 @@ -import { IPalette, IPartColor, ISetType, IStructureData } from 'nitro-renderer'; -import { GetAvatarRenderManager, GetConfiguration, GetSessionDataManager } from '../../../api'; +import { IPartColor } from 'nitro-renderer'; +import { GetAvatarPalette, GetAvatarRenderManager, GetAvatarSetType, GetClubMemberLevel, GetConfiguration } from '../../../api'; import { AvatarEditorGridColorItem } from './AvatarEditorGridColorItem'; import { AvatarEditorGridPartItem } from './AvatarEditorGridPartItem'; import { CategoryBaseModel } from './CategoryBaseModel'; import { CategoryData } from './CategoryData'; import { FigureData } from './FigureData'; -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 +export class AvatarEditorUtilities { - private _figureStructureData: IStructureData = GetAvatarRenderManager().structureData; - private _figures: Map = new Map(); - private _gender: string = FigureData.MALE; - private _notifier: () => void = null; + private static MAX_PALETTES: number = 2; - constructor() + public static CURRENT_FIGURE: FigureData = null; + + public static getGender(gender: string): string { - const maleFigure = new FigureData(); - const femaleFigure = new FigureData(); + 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; + } - 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); + return gender; } - public getSetType(setType: string): ISetType + public static createCategory(model: CategoryBaseModel, name: string): CategoryData { - 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; + if(!model || !name || !this.CURRENT_FIGURE) return null; const partItems: AvatarEditorGridPartItem[] = []; const colorItems: AvatarEditorGridColorItem[][] = []; let i = 0; - while(i < MAX_PALETTES) + while(i < this.MAX_PALETTES) { colorItems.push([]); i++; } - const setType = this.getSetType(name); + const setType = GetAvatarSetType(name); if(!setType) return null; - const palette = this.getPalette(setType.paletteID); + const palette = GetAvatarPalette(setType.paletteID); if(!palette) return null; - let colorIds = this.figureData.getColourIds(name); + 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.values()) { - if(partColor.isSelectable && (clubItemsDimmed || (this.clubMemberLevel >= partColor.clubLevel))) + if(partColor.isSelectable && (clubItemsDimmed || (clubMemberLevel >= partColor.clubLevel))) { let i = 0; - while(i < MAX_PALETTES) + while(i < this.MAX_PALETTES) { - const isDisabled = (this.clubMemberLevel < partColor.clubLevel); + const isDisabled = (clubMemberLevel < partColor.clubLevel); const colorItem = new AvatarEditorGridColorItem(partColor, isDisabled); colorItems[i].push(colorItem); @@ -108,11 +99,11 @@ export class AvatarEditor if(clubItemsDimmed) { - mandatorySetIds = GetAvatarRenderManager().getMandatoryAvatarPartSetIds(this._gender, 2); + mandatorySetIds = GetAvatarRenderManager().getMandatoryAvatarPartSetIds(this.CURRENT_FIGURE.gender, 2); } else { - mandatorySetIds = GetAvatarRenderManager().getMandatoryAvatarPartSetIds(this._gender, this.clubMemberLevel); + mandatorySetIds = GetAvatarRenderManager().getMandatoryAvatarPartSetIds(this.CURRENT_FIGURE.gender, clubMemberLevel); } const isntMandatorySet = (mandatorySetIds.indexOf(name) === -1); @@ -142,17 +133,15 @@ export class AvatarEditor { isValidGender = true; } - else + + else if(partSet.gender === this.CURRENT_FIGURE.gender) { - if(partSet.gender === this._gender) - { - isValidGender = true; - } + isValidGender = true; } - if(partSet.isSelectable && isValidGender && (clubItemsDimmed || (this.clubMemberLevel >= partSet.clubLevel))) + if(partSet.isSelectable && isValidGender && (clubItemsDimmed || (clubMemberLevel >= partSet.clubLevel))) { - const isDisabled = (this.clubMemberLevel < partSet.clubLevel); + const isDisabled = (clubMemberLevel < partSet.clubLevel); let isValid = true; @@ -186,7 +175,7 @@ export class AvatarEditor i = 0; - while(i < MAX_PALETTES) + while(i < this.MAX_PALETTES) { colorItems[i].sort(this.colorSorter); @@ -196,7 +185,7 @@ export class AvatarEditor return new CategoryData(name, partItems, colorItems); } - private clubSorter(a: AvatarEditorGridPartItem, b: AvatarEditorGridPartItem): number + public static clubSorter(a: AvatarEditorGridPartItem, b: AvatarEditorGridPartItem): number { const clubLevelA = (!a.partSet ? 9999999999 : a.partSet.clubLevel); const clubLevelB = (!b.partSet ? 9999999999 : b.partSet.clubLevel); @@ -218,7 +207,23 @@ export class AvatarEditor return 0; } - private noobSorter(a: AvatarEditorGridPartItem, b: AvatarEditorGridPartItem): number + 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); @@ -240,67 +245,33 @@ export class AvatarEditor return 0; } - private colorSorter(a: AvatarEditorGridColorItem, b: AvatarEditorGridColorItem): number + public static avatarSetFirstSelectableColor(name: string): number { - const clubLevelA = (!a.partColor ? -1 : a.partColor.clubLevel); - const clubLevelB = (!b.partColor ? -1 : b.partColor.clubLevel); + const setType = GetAvatarSetType(name); - if(clubLevelA < clubLevelB) return -1; + if(!setType) return -1; - if(clubLevelA > clubLevelB) return 1; + const palette = GetAvatarPalette(setType.paletteID); - if(a.partColor.index < b.partColor.index) return -1; + if(!palette) return -1; - if(a.partColor.index > b.partColor.index) return 1; + for(const color of palette.colors.values()) + { + if(!color.isSelectable || (GetClubMemberLevel() < color.clubLevel)) continue; - return 0; + return color.id; + } + + return -1; } - public get clubMemberLevel(): number - { - return GetSessionDataManager().clubLevel; - } - - private get clubItemsFirst(): boolean + public static get clubItemsFirst(): boolean { return GetConfiguration('avatareditor.show.clubitems.first', true); } - private get clubItemsDimmed(): boolean + public static get clubItemsDimmed(): boolean { return GetConfiguration('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) - { - if(this._gender === gender) return; - - this._gender = gender; - - if(this.figureData) this.figureData.notify = this.notify; - - if(this.notify) this.notify(); - } - - public get notify(): () => void - { - return this._notifier; - } - - public set notify(notifier: () => void) - { - if(this.figureData) this.figureData.notify = notifier; - - this._notifier = notifier; - } } diff --git a/src/views/avatar-editor/common/BodyModel.ts b/src/views/avatar-editor/common/BodyModel.ts index 41971d51..02151180 100644 --- a/src/views/avatar-editor/common/BodyModel.ts +++ b/src/views/avatar-editor/common/BodyModel.ts @@ -1,5 +1,6 @@ import { AvatarEditorFigureCategory, AvatarScaleType, AvatarSetType, IAvatarImageListener } from 'nitro-renderer'; import { GetAvatarRenderManager } from '../../../api'; +import { AvatarEditorUtilities } from './AvatarEditorUtilities'; import { CategoryBaseModel } from './CategoryBaseModel'; import { FigureData } from './FigureData'; @@ -25,15 +26,15 @@ export class BodyModel extends CategoryBaseModel implements IAvatarImageListener protected updateSelectionsFromFigure(name: string): void { - if(!this._categories || !this._editor || !this._editor.figureData) return; + if(!this._categories || !AvatarEditorUtilities.CURRENT_FIGURE) return; const category = this._categories.get(name); if(!category) return; - const setId = this._editor.figureData.getPartSetId(name); + const setId = AvatarEditorUtilities.CURRENT_FIGURE.getPartSetId(name); - let colorIds = this._editor.figureData.getColourIds(name); + let colorIds = AvatarEditorUtilities.CURRENT_FIGURE.getColorIds(name); if(!colorIds) colorIds = []; @@ -42,7 +43,7 @@ export class BodyModel extends CategoryBaseModel implements IAvatarImageListener for(const part of category.parts) { - const figure = this._editor.figureData.getFigureStringWithFace(part.id); + const figure = AvatarEditorUtilities.CURRENT_FIGURE.getFigureStringWithFace(part.id); const avatarImage = GetAvatarRenderManager().createAvatarImage(figure, AvatarScaleType.LARGE, null, this); const sprite = avatarImage.getImageAsSprite(AvatarSetType.HEAD); diff --git a/src/views/avatar-editor/common/CategoryBaseModel.ts b/src/views/avatar-editor/common/CategoryBaseModel.ts index 9d1100ee..ab65af72 100644 --- a/src/views/avatar-editor/common/CategoryBaseModel.ts +++ b/src/views/avatar-editor/common/CategoryBaseModel.ts @@ -1,18 +1,16 @@ -import { AvatarEditor } from './AvatarEditor'; +import { AvatarEditorUtilities } from './AvatarEditorUtilities'; import { CategoryData } from './CategoryData'; import { IAvatarEditorCategoryModel } from './IAvatarEditorCategoryModel'; export class CategoryBaseModel implements IAvatarEditorCategoryModel { - protected _editor: AvatarEditor; protected _categories: Map; protected _isInitalized: boolean; protected _maxPaletteCount: number; private _disposed: boolean; - constructor(editor: AvatarEditor) + constructor() { - this._editor = editor; this._isInitalized = false; this._maxPaletteCount = 0; } @@ -51,7 +49,7 @@ export class CategoryBaseModel implements IAvatarEditorCategoryModel if(existing) return; - existing = this._editor.createCategory(this, name); + existing = AvatarEditorUtilities.createCategory(this, name); if(!existing) return; @@ -66,9 +64,9 @@ export class CategoryBaseModel implements IAvatarEditorCategoryModel if(!category) return; - const setId = this._editor.figureData.getPartSetId(figure); + const setId = AvatarEditorUtilities.CURRENT_FIGURE.getPartSetId(figure); - let colorIds = this._editor.figureData.getColourIds(figure); + let colorIds = AvatarEditorUtilities.CURRENT_FIGURE.getColorIds(figure); if(!colorIds) colorIds = []; @@ -120,9 +118,9 @@ export class CategoryBaseModel implements IAvatarEditorCategoryModel { const partItem = category.getCurrentPart(); - if(partItem && this._editor && this._editor.figureData) + if(partItem && AvatarEditorUtilities.CURRENT_FIGURE) { - this._editor.figureData.savePartData(name, partItem.id, category.getSelectedColorIds(), true); + AvatarEditorUtilities.CURRENT_FIGURE.savePartData(name, partItem.id, category.getSelectedColorIds(), true); } didStrip = true; @@ -148,9 +146,9 @@ export class CategoryBaseModel implements IAvatarEditorCategoryModel { const partItem = category.getCurrentPart(); - if(partItem && this._editor && this._editor.figureData) + if(partItem && AvatarEditorUtilities.CURRENT_FIGURE) { - this._editor.figureData.savePartData(name, partItem.id, category.getSelectedColorIds(), true); + AvatarEditorUtilities.CURRENT_FIGURE.savePartData(name, partItem.id, category.getSelectedColorIds(), true); } didStrip = true; @@ -185,7 +183,7 @@ export class CategoryBaseModel implements IAvatarEditorCategoryModel this._maxPaletteCount = partItem.maxColorIndex; - this._editor.figureData.savePartData(category, partItem.id, categoryData.getSelectedColorIds(), true); + AvatarEditorUtilities.CURRENT_FIGURE.savePartData(category, partItem.id, categoryData.getSelectedColorIds(), true); } public selectColor(category: string, colorIndex: number, paletteId: number): void @@ -209,7 +207,7 @@ export class CategoryBaseModel implements IAvatarEditorCategoryModel return; } - this._editor.figureData.savePartSetColourId(category, categoryData.getSelectedColorIds(), true); + AvatarEditorUtilities.CURRENT_FIGURE.savePartSetColourId(category, categoryData.getSelectedColorIds(), true); } public getCategoryData(category: string): CategoryData diff --git a/src/views/avatar-editor/common/FigureData.ts b/src/views/avatar-editor/common/FigureData.ts index 2e447cb3..d186b4c0 100644 --- a/src/views/avatar-editor/common/FigureData.ts +++ b/src/views/avatar-editor/common/FigureData.ts @@ -1,5 +1,9 @@ -export class FigureData +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'; @@ -24,6 +28,7 @@ private _data: Map; private _colors: Map; private _gender: string = 'M'; + private _direction: number = FigureData.DEFAULT_DIRECTION; private _avatarEffectType: number = -1; private _notifier: () => void = null; @@ -80,14 +85,13 @@ return -1; } - public getColourIds(colorId: string): number[] + public getColorIds(setType: string): number[] { - const existing = this._colors.get(colorId); + const existing = this._colors.get(setType); if(existing !== undefined) return existing; - return []; - // return [this._avatarEditor._Str_24919(k)]; + return [ AvatarEditorUtilities.avatarSetFirstSelectableColor(setType) ]; } public getFigureString(): string @@ -167,9 +171,9 @@ if(_arg_3) this.updateView(); } - public savePartSetColourId(k: string, _arg_2: number[], _arg_3: boolean = true): void + public savePartSetColourId(setType: string, colorIds: number[], update: boolean = true): void { - switch(k) + switch(setType) { case FigureData.FACE: case FigureData.HAIR: @@ -184,11 +188,11 @@ case FigureData.TROUSERS: case FigureData.SHOES: case FigureData.TROUSER_ACCESSORIES: - this._colors.set(k, _arg_2); + this._colors.set(setType, colorIds); break; } - if(_arg_3) this.updateView(); + if(update) this.updateView(); } public getFigureStringWithFace(k: number, override = true): string @@ -249,6 +253,18 @@ 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; diff --git a/src/views/avatar-editor/views/figure-preview/AvatarEditorFigurePreviewView.tsx b/src/views/avatar-editor/views/figure-preview/AvatarEditorFigurePreviewView.tsx index 73e37a69..352f7e58 100644 --- a/src/views/avatar-editor/views/figure-preview/AvatarEditorFigurePreviewView.tsx +++ b/src/views/avatar-editor/views/figure-preview/AvatarEditorFigurePreviewView.tsx @@ -4,7 +4,7 @@ import { AvatarEditorFigurePreviewViewProps } from './AvatarEditorFigurePreviewV export const AvatarEditorFigurePreviewView: FC = props => { - const { editor = null } = props; + const { figureData = null } = props; const [ updateId, setUpdateId ] = useState(-1); const rerender = useCallback(() => @@ -14,15 +14,15 @@ export const AvatarEditorFigurePreviewView: FC { - if(!editor) return; + if(!figureData) return; - editor.notify = rerender; + figureData.notify = rerender; return () => { - editor.notify = null; + figureData.notify = null; } - }, [ editor, rerender ] ); + }, [ figureData, rerender ] ); - return + return } diff --git a/src/views/avatar-editor/views/figure-preview/AvatarEditorFigurePreviewView.types.ts b/src/views/avatar-editor/views/figure-preview/AvatarEditorFigurePreviewView.types.ts index 0bfcf8f9..3956305c 100644 --- a/src/views/avatar-editor/views/figure-preview/AvatarEditorFigurePreviewView.types.ts +++ b/src/views/avatar-editor/views/figure-preview/AvatarEditorFigurePreviewView.types.ts @@ -1,6 +1,6 @@ -import { AvatarEditor } from '../../common/AvatarEditor'; +import { FigureData } from '../../common/FigureData'; export interface AvatarEditorFigurePreviewViewProps { - editor: AvatarEditor; + figureData: FigureData; } diff --git a/src/views/avatar-editor/views/figure-set-item/AvatarEditorFigureSetItemView.tsx b/src/views/avatar-editor/views/figure-set-item/AvatarEditorFigureSetItemView.tsx index ef4c0cc4..09b4e33e 100644 --- a/src/views/avatar-editor/views/figure-set-item/AvatarEditorFigureSetItemView.tsx +++ b/src/views/avatar-editor/views/figure-set-item/AvatarEditorFigureSetItemView.tsx @@ -24,8 +24,9 @@ export const AvatarEditorFigureSetItemView: FC onClick(partItem) }> + onClick(partItem) }> { partItem.isHC && } + { partItem.isClear && } ); } diff --git a/src/views/avatar-editor/views/model/AvatarEditorModelView.tsx b/src/views/avatar-editor/views/model/AvatarEditorModelView.tsx index c842c94e..68e1645b 100644 --- a/src/views/avatar-editor/views/model/AvatarEditorModelView.tsx +++ b/src/views/avatar-editor/views/model/AvatarEditorModelView.tsx @@ -1,17 +1,13 @@ -import { UserFigureComposer } from 'nitro-renderer'; import { FC, useCallback, useEffect, useState } from 'react'; -import { SendMessageHook } from '../../../../hooks'; -import { LocalizeText } from '../../../../utils/LocalizeText'; import { CategoryData } from '../../common/CategoryData'; import { FigureData } from '../../common/FigureData'; -import { AvatarEditorFigurePreviewView } from '../figure-preview/AvatarEditorFigurePreviewView'; import { AvatarEditorFigureSetView } from '../figure-set/AvatarEditorFigureSetView'; import { AvatarEditorPaletteSetView } from '../palette-set/AvatarEditorPaletteSetView'; import { AvatarEditorModelViewProps } from './AvatarEditorModelView.types'; export const AvatarEditorModelView: FC = props => { - const { model = null, editor = null, selectGender = null } = props; + const { model = null, gender = null, setGender = null } = props; const [ activeCategory, setActiveCategory ] = useState(null); const [ maxPaletteCount, setMaxPaletteCount ] = useState(1); @@ -35,13 +31,6 @@ export const AvatarEditorModelView: FC = props => } }, [ model ]); - const saveFigure = useCallback(() => - { - const figureData = editor.figureData; - - SendMessageHook(new UserFigureComposer(figureData.gender, figureData.getFigureString())); - }, [ editor ]); - useEffect(() => { model.init(); @@ -58,49 +47,35 @@ export const AvatarEditorModelView: FC = props => return (
-
+
{ model.canSetGender && <> - selectGender(FigureData.MALE) } /> - selectGender(FigureData.FEMALE) } /> +
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) } /> +
selectCategory(name) }> + +
); })}
-
+
-
-
- -
- - -
-
-
-
- - -
- -
-
-
+
{ (maxPaletteCount >= 1) && } { (maxPaletteCount === 2) && - } + }
); diff --git a/src/views/avatar-editor/views/model/AvatarEditorModelView.types.ts b/src/views/avatar-editor/views/model/AvatarEditorModelView.types.ts index dc81491a..b86b58aa 100644 --- a/src/views/avatar-editor/views/model/AvatarEditorModelView.types.ts +++ b/src/views/avatar-editor/views/model/AvatarEditorModelView.types.ts @@ -1,9 +1,9 @@ -import { AvatarEditor } from '../../common/AvatarEditor'; +import { Dispatch, SetStateAction } from 'react'; import { IAvatarEditorCategoryModel } from '../../common/IAvatarEditorCategoryModel'; export interface AvatarEditorModelViewProps { model: IAvatarEditorCategoryModel; - editor: AvatarEditor; - selectGender: (gender: string) => void; + gender: string; + setGender: Dispatch>; } diff --git a/src/views/avatar-editor/views/palette-set-item/AvatarEditorPaletteSetItem.tsx b/src/views/avatar-editor/views/palette-set-item/AvatarEditorPaletteSetItem.tsx index 2dab88d1..1a46b115 100644 --- a/src/views/avatar-editor/views/palette-set-item/AvatarEditorPaletteSetItem.tsx +++ b/src/views/avatar-editor/views/palette-set-item/AvatarEditorPaletteSetItem.tsx @@ -4,7 +4,7 @@ import { AvatarEditorPaletteSetItemProps } from './AvatarEditorPaletteSetItem.ty export const AvatarEditorPaletteSetItem: FC = props => { - const { colorItem = null, onClick = null } = props; + const { colorItem = null, ...rest } = props; const [ updateId, setUpdateId ] = useState(-1); const rerender = useCallback(() => @@ -22,5 +22,5 @@ export const AvatarEditorPaletteSetItem: FC = p } }) - return onClick(colorItem) } /> + return } diff --git a/src/views/avatar-editor/views/palette-set-item/AvatarEditorPaletteSetItem.types.ts b/src/views/avatar-editor/views/palette-set-item/AvatarEditorPaletteSetItem.types.ts index 5930f9a4..a0abc62e 100644 --- a/src/views/avatar-editor/views/palette-set-item/AvatarEditorPaletteSetItem.types.ts +++ b/src/views/avatar-editor/views/palette-set-item/AvatarEditorPaletteSetItem.types.ts @@ -1,7 +1,7 @@ +import { DetailsHTMLAttributes } from 'react'; import { AvatarEditorGridColorItem } from '../../common/AvatarEditorGridColorItem'; -export interface AvatarEditorPaletteSetItemProps +export interface AvatarEditorPaletteSetItemProps extends DetailsHTMLAttributes { colorItem: AvatarEditorGridColorItem; - onClick: (item: AvatarEditorGridColorItem) => void; } diff --git a/src/views/avatar-editor/views/palette-set/AvatarEditorPaletteSetView.tsx b/src/views/avatar-editor/views/palette-set/AvatarEditorPaletteSetView.tsx index fc8c4fc9..65c2968f 100644 --- a/src/views/avatar-editor/views/palette-set/AvatarEditorPaletteSetView.tsx +++ b/src/views/avatar-editor/views/palette-set/AvatarEditorPaletteSetView.tsx @@ -7,7 +7,7 @@ import { AvatarEditorPaletteSetViewProps } from './AvatarEditorPaletteSetView.ty export const AvatarEditorPaletteSetView: FC = props => { - const { model = null, category = null, paletteSet = [], paletteIndex = -1 } = props; + const { model = null, category = null, paletteSet = [], paletteIndex = -1, ...rest } = props; const selectColor = useCallback((item: AvatarEditorGridColorItem) => { @@ -19,10 +19,10 @@ export const AvatarEditorPaletteSetView: FC = p }, [ model, category, paletteSet, paletteIndex ]); return ( - + { (paletteSet.length > 0) && paletteSet.map((item, index) => { - return ; + return selectColor(item) } />; }) } ); diff --git a/src/views/avatar-editor/views/palette-set/AvatarEditorPaletteSetView.types.ts b/src/views/avatar-editor/views/palette-set/AvatarEditorPaletteSetView.types.ts index 9fa8e2b6..e7e62483 100644 --- a/src/views/avatar-editor/views/palette-set/AvatarEditorPaletteSetView.types.ts +++ b/src/views/avatar-editor/views/palette-set/AvatarEditorPaletteSetView.types.ts @@ -1,8 +1,9 @@ +import { DetailsHTMLAttributes } from 'react'; import { AvatarEditorGridColorItem } from '../../common/AvatarEditorGridColorItem'; import { CategoryData } from '../../common/CategoryData'; import { IAvatarEditorCategoryModel } from '../../common/IAvatarEditorCategoryModel'; -export interface AvatarEditorPaletteSetViewProps +export interface AvatarEditorPaletteSetViewProps extends DetailsHTMLAttributes { model: IAvatarEditorCategoryModel; category: CategoryData;