diff --git a/src/App.scss b/src/App.scss index eaf0c4c..eff22b0 100644 --- a/src/App.scss +++ b/src/App.scss @@ -21,8 +21,8 @@ $toolbar-height: 55px; $achievement-width: 375px; $achievement-height: 405px; -$avatar-editor-width: 746px; -$avatar-editor-height: 445px; +$avatar-editor-width: 520px; +$avatar-editor-height: 553px; $catalog-width: 650px; $catalog-height: 480px; diff --git a/src/assets/images/avatareditor/avatar_shadow.png b/src/assets/images/avatareditor/avatar_shadow.png new file mode 100644 index 0000000..33397f1 Binary files /dev/null and b/src/assets/images/avatareditor/avatar_shadow.png differ diff --git a/src/assets/images/avatareditor/body.png b/src/assets/images/avatareditor/body.png new file mode 100644 index 0000000..6f81867 Binary files /dev/null and b/src/assets/images/avatareditor/body.png differ diff --git a/src/assets/images/avatareditor/choice_bg.png b/src/assets/images/avatareditor/choice_bg.png new file mode 100644 index 0000000..13a3164 Binary files /dev/null and b/src/assets/images/avatareditor/choice_bg.png differ diff --git a/src/assets/images/avatareditor/color_frame.png b/src/assets/images/avatareditor/color_frame.png new file mode 100644 index 0000000..ed546d7 Binary files /dev/null and b/src/assets/images/avatareditor/color_frame.png differ diff --git a/src/assets/images/avatareditor/color_frame_active.png b/src/assets/images/avatareditor/color_frame_active.png new file mode 100644 index 0000000..4139152 Binary files /dev/null and b/src/assets/images/avatareditor/color_frame_active.png differ diff --git a/src/assets/images/avatareditor/hc_icon.png b/src/assets/images/avatareditor/hc_icon.png new file mode 100644 index 0000000..df4c23d Binary files /dev/null and b/src/assets/images/avatareditor/hc_icon.png differ diff --git a/src/assets/images/avatareditor/head.png b/src/assets/images/avatareditor/head.png new file mode 100644 index 0000000..76baf40 Binary files /dev/null and b/src/assets/images/avatareditor/head.png differ diff --git a/src/assets/images/avatareditor/legs.png b/src/assets/images/avatareditor/legs.png new file mode 100644 index 0000000..59076fd Binary files /dev/null and b/src/assets/images/avatareditor/legs.png differ diff --git a/src/assets/images/avatareditor/randomize.png b/src/assets/images/avatareditor/randomize.png new file mode 100644 index 0000000..048f94e Binary files /dev/null and b/src/assets/images/avatareditor/randomize.png differ diff --git a/src/assets/images/avatareditor/randomize_transparent.png b/src/assets/images/avatareditor/randomize_transparent.png new file mode 100644 index 0000000..382b9e0 Binary files /dev/null and b/src/assets/images/avatareditor/randomize_transparent.png differ diff --git a/src/assets/images/avatareditor/rotation_arrow.png b/src/assets/images/avatareditor/rotation_arrow.png new file mode 100644 index 0000000..fba2b87 Binary files /dev/null and b/src/assets/images/avatareditor/rotation_arrow.png differ diff --git a/src/assets/images/avatareditor/shirts.png b/src/assets/images/avatareditor/shirts.png new file mode 100644 index 0000000..ff77247 Binary files /dev/null and b/src/assets/images/avatareditor/shirts.png differ diff --git a/src/assets/images/avatareditor/wardrobe_bg.png b/src/assets/images/avatareditor/wardrobe_bg.png new file mode 100644 index 0000000..83fb613 Binary files /dev/null and b/src/assets/images/avatareditor/wardrobe_bg.png differ diff --git a/src/assets/images/avatareditor/wardrobe_left.png b/src/assets/images/avatareditor/wardrobe_left.png new file mode 100644 index 0000000..c809048 Binary files /dev/null and b/src/assets/images/avatareditor/wardrobe_left.png differ diff --git a/src/assets/images/avatareditor/wardrobe_right.png b/src/assets/images/avatareditor/wardrobe_right.png new file mode 100644 index 0000000..9ddd734 Binary files /dev/null and b/src/assets/images/avatareditor/wardrobe_right.png differ diff --git a/src/assets/images/avatareditor/wardrobe_user_bg.png b/src/assets/images/avatareditor/wardrobe_user_bg.png new file mode 100644 index 0000000..239658c Binary files /dev/null and b/src/assets/images/avatareditor/wardrobe_user_bg.png differ diff --git a/src/assets/images/wardrobe/hd.png b/src/assets/images/wardrobe/hd.png new file mode 100644 index 0000000..307d6f7 Binary files /dev/null and b/src/assets/images/wardrobe/hd.png differ diff --git a/src/assets/images/wardrobe/head.png b/src/assets/images/wardrobe/head.png new file mode 100644 index 0000000..977012d Binary files /dev/null and b/src/assets/images/wardrobe/head.png differ diff --git a/src/assets/images/wardrobe/legs.png b/src/assets/images/wardrobe/legs.png new file mode 100644 index 0000000..5978a3a Binary files /dev/null and b/src/assets/images/wardrobe/legs.png differ diff --git a/src/assets/images/wardrobe/torso.png b/src/assets/images/wardrobe/torso.png new file mode 100644 index 0000000..a58918b Binary files /dev/null and b/src/assets/images/wardrobe/torso.png differ diff --git a/src/assets/images/wardrobe/wardrobe.png b/src/assets/images/wardrobe/wardrobe.png new file mode 100644 index 0000000..6913da7 Binary files /dev/null and b/src/assets/images/wardrobe/wardrobe.png differ diff --git a/src/common/card/NitroCardView.scss b/src/common/card/NitroCardView.scss index a46b39d..3899272 100644 --- a/src/common/card/NitroCardView.scss +++ b/src/common/card/NitroCardView.scss @@ -1,5 +1,5 @@ $nitro-card-header-height: 33px; -$nitro-card-tabs-height: 33px; +$nitro-card-tabs-height: 42px; .nitro-card { resize: both; diff --git a/src/common/layout/LayoutGridColorPickerItem.tsx b/src/common/layout/LayoutGridColorPickerItem.tsx new file mode 100644 index 0000000..cfd7f68 --- /dev/null +++ b/src/common/layout/LayoutGridColorPickerItem.tsx @@ -0,0 +1,75 @@ +import { FC, useMemo } from 'react'; +import { Base } from '../Base'; +import { Column, ColumnProps } from '../Column'; +import { LayoutItemCountView } from './LayoutItemCountView'; +import { LayoutLimitedEditionStyledNumberView } from './limited-edition'; + +export interface LayoutGridColorPickerItemProps extends ColumnProps +{ + itemImage?: string; + itemColor?: string; + itemActive?: boolean; + itemCount?: number; + itemCountMinimum?: number; + itemUniqueSoldout?: boolean; + itemUniqueNumber?: number; + itemUnseen?: boolean; + itemHighlight?: boolean; + disabled?: boolean; +} + +export const LayoutGridColorPickerItem: FC = props => +{ + const { itemImage = undefined, itemColor = undefined, itemActive = false, itemCount = 1, itemCountMinimum = 1, itemUniqueSoldout = false, itemUniqueNumber = -2, itemUnseen = false, itemHighlight = false, disabled = false, center = true, column = true, style = {}, classNames = [], position = 'relative', overflow = 'hidden', children = null, ...rest } = props; + + const getClassNames = useMemo(() => + { + const newClassNames: string[] = [ 'layout-grid-item', 'color-picker-frame' ]; + + if(itemActive) newClassNames.push('active'); + + if(itemUniqueSoldout || (itemUniqueNumber > 0)) newClassNames.push('unique-item'); + + if(itemUniqueSoldout) newClassNames.push('sold-out'); + + if(itemUnseen) newClassNames.push('unseen'); + + if(itemHighlight) newClassNames.push('has-highlight'); + + if(disabled) newClassNames.push('disabled') + + if(itemImage === null) newClassNames.push('icon', 'loading-icon'); + + if(classNames.length) newClassNames.push(...classNames); + + return newClassNames; + }, [ itemActive, itemUniqueSoldout, itemUniqueNumber, itemUnseen, itemHighlight, disabled, itemImage, classNames ]); + + const getStyle = useMemo(() => + { + let newStyle = { ...style }; + + if(itemImage) newStyle.backgroundImage = `url(${ itemImage })`; + + if(itemColor) newStyle.backgroundColor = itemColor; + + if(Object.keys(style).length) newStyle = { ...newStyle, ...style }; + + return newStyle; + }, [ style, itemImage, itemColor ]); + + return ( + + { (itemCount > itemCountMinimum) && + } + { (itemUniqueNumber > 0) && + <> + +
+ +
+ } + { children } +
+ ); +} diff --git a/src/components/avatar-editor/AvatarEditorView.scss b/src/components/avatar-editor/AvatarEditorView.scss index 15f7fb5..160bd17 100644 --- a/src/components/avatar-editor/AvatarEditorView.scss +++ b/src/components/avatar-editor/AvatarEditorView.scss @@ -6,146 +6,146 @@ height: 21px; background-position: -226px -131px; } - + &.arrow-right-icon { width: 28px; height: 21px; background-position: -226px -162px; } - + &.ca-icon { width: 25px; height: 25px; background-position: -226px -61px; - &.selected { + &.selected, &:hover { width: 25px; height: 25px; background-position: -226px -96px; } } - + &.cc-icon { width: 31px; height: 29px; background-position: -145px -5px; - &.selected { + &.selected, &:hover { width: 31px; height: 29px; background-position: -145px -44px; } } - + &.ch-icon { width: 29px; height: 24px; background-position: -186px -39px; - &.selected { + &.selected, &:hover { width: 29px; height: 24px; background-position: -186px -73px; } } - + &.clear-icon { width: 27px; height: 27px; background-position: -145px -157px; } - + &.cp-icon { width: 30px; height: 24px; background-position: -145px -264px; - &.selected { + &.selected, &:hover { width: 30px; height: 24px; background-position: -186px -5px; } } - + &.ea-icon { width: 35px; height: 16px; background-position: -226px -193px; - &.selected { + &.selected, &:hover { width: 35px; height: 16px; background-position: -226px -219px; } } - + &.fa-icon { width: 27px; height: 20px; background-position: -186px -137px; - &.selected { + &.selected, &:hover { width: 27px; height: 20px; background-position: -186px -107px; } } - + &.female-icon { width: 18px; height: 27px; background-position: -186px -202px; - &.selected { + &.selected, &:hover { width: 18px; height: 27px; background-position: -186px -239px; } } - + &.ha-icon { width: 25px; height: 22px; background-position: -226px -245px; - &.selected { + &.selected, &:hover { width: 25px; height: 22px; background-position: -226px -277px; } } - + &.he-icon { width: 31px; height: 27px; background-position: -145px -83px; - &.selected { + &.selected, &:hover { width: 31px; height: 27px; background-position: -145px -120px; } } - + &.hr-icon { width: 29px; height: 25px; background-position: -145px -194px; - &.selected { + &.selected, &:hover { width: 29px; height: 25px; background-position: -145px -229px; } } - + &.lg-icon { width: 19px; height: 20px; background-position: -303px -45px; - &.selected { + &.selected, &:hover { width: 19px; height: 20px; background-position: -303px -75px; @@ -157,54 +157,54 @@ height: 25px; background-position: -186px -167px; } - + &.male-icon { width: 21px; height: 21px; background-position: -186px -276px; - &.selected { + &.selected, &:hover { width: 21px; height: 21px; background-position: -272px -5px; } } - + &.sellable-icon { width: 17px; height: 15px; background-position: -303px -105px; } - + &.sh-icon { width: 37px; height: 10px; background-position: -303px -5px; - &.selected { + &.selected, &:hover { width: 37px; height: 10px; background-position: -303px -25px; } } - + &.spotlight-icon { width: 130px; height: 305px; background-position: -5px -5px; } - + &.wa-icon { width: 36px; height: 18px; background-position: -226px -5px; - &.selected { + &.selected, &:hover { width: 36px; height: 18px; background-position: -226px -33px; @@ -212,18 +212,64 @@ } } -.nitro-avatar-editor-wardrobe-figure-preview { - background-color: $pale-sky; - overflow: hidden; - z-index: 1; +.saved-outfits-title { + color: #a7a6a2; + font-weight: bold; +} + +.saved-outfit-container { + display: flex; + width: 100% !important; + height: 91.5%; .avatar-image { - position: absolute; - bottom: -15px; - margin: 0 auto; + width: 40px !important; z-index: 4; + transform: scale(0.5); } + .avatar-figure { + margin-top: -46px; + margin-left: -9px; + image-rendering: auto !important; + } + + .nitro-avatar-editor-wardrobe-container { + background-color: #cacaca; + border-radius: 0.3rem; + border: solid 1px #000; + height: 386px; + width: 100%; + display: grid; + grid-template-columns: repeat(2, 1fr); + grid-gap: 10px; + padding: 10px 12px 10px 0; + overflow-y: auto; + } + + .avatar-container { + height: 50px; + border-radius: 0.3rem; + background-color: #a7a6a2; + width: 30px; + } + + .saved-outfit-button { + margin-top: -3px; + background-color: transparent; + border: none; + } +} + + +.nitro-avatar-editor-wardrobe-figure-preview { + border-image-source: url(@/assets/images/avatareditor/wardrobe_user_bg.png); + border-image-slice: 4 4 4 4 fill; + border-image-width: 4px 4px 4px 4px; + background-color: transparent; + overflow: hidden; + z-index: 1; + .avatar-shadow { position: absolute; left: 0; @@ -237,19 +283,6 @@ z-index: 2; } - &:after { - position: absolute; - content: ''; - top: 75%; - bottom: 0; - left: 0; - right: 0; - border-radius: 50%; - background-color: $pale-sky; - box-shadow: 0 0 8px 2px rgba($white,.6); - transform: scale(2); - } - .button-container { position: absolute; bottom: 0; @@ -258,8 +291,11 @@ } .nitro-avatar-editor { - width: $avatar-editor-width; height: $avatar-editor-height; + min-width: $avatar-editor-width; + min-height: $avatar-editor-height; + max-width: $avatar-editor-width; + max-height: $avatar-editor-height; .category-item { height: 40px; @@ -268,7 +304,6 @@ .figure-preview-container { position: relative; height: 100%; - background-color: #b69b83; overflow: hidden; z-index: 1; @@ -279,9 +314,17 @@ padding: 0 10px; display: flex; justify-content: space-between; - bottom: 12px; + bottom: 50px; z-index: 5; - + + .arrow-left { + background-image: url(@/assets/images/avatareditor/rotation_arrow.png); + width: 44px; + height: 29px; + margin-left: auto; + margin-right: auto; + } + .icon { cursor: pointer; } @@ -291,46 +334,194 @@ position: absolute; left: 0; right: 0; - bottom: 50px; + bottom: 125px; margin: 0 auto; z-index: 4; } - .avatar-spotlight { - position: absolute; - top: -10px; - left: 0; - right: 0; - margin: 0 auto; - opacity: 0.3; - pointer-events: none; - z-index: 3; - } - .avatar-shadow { position: absolute; left: 0; right: 0; - bottom: 15px; - width: 70px; - height: 30px; + bottom: 88px; + width: 68px; + height: 34px; margin: 0 auto; - border-radius: 100%; - background-color: rgba(0, 0, 0, 0.20); + background: url(@/assets/images/avatareditor/avatar_shadow.png); z-index: 2; } - - &:after { - position: absolute; - content: ''; - top: 75%; - bottom: 0; - left: 0; - right: 0; - border-radius: 50%; - background-color: #a68d76; - box-shadow: 0 0 8px 2px rgba($white,.6); - transform: scale(2); - } } } + +.nitro-avatar-editor.expanded { + max-width: $avatar-editor-width + 264px; + min-width: $avatar-editor-width + 164px; +} + + .choose-clothing { + width: 320px; + } + + .color-picker-frame { + border-image-source: url(@/assets/images/avatareditor/color_frame.png); + border-image-slice: 6 6 6 6 fill; + border-image-width: 6px 6px 6px 6px; + width: 14px; + height: 21px; + border-radius: 4px; + + &.active { + border-image-source: url(@/assets/images/avatareditor/color_frame_active.png); + height: 21px; + margin-top: -2px; + } + + &:hover { + border-image-source: url(@/assets/images/avatareditor/color_frame_active.png); + height: 21px; + margin-top: -2px; + } + } + + .hc-icon { + background-image: url(@/assets/images/avatareditor/hc_icon.png); + height: 9px; + width: 10px; + bottom: 2px; + left: 2px; + } + + .avatar-editor-tabs { + position: relative; + + .tab { + background-position-x: 0; + background-position-y: 0; + width: 34px; + height: 22px; + } + + .hd { + background: url(@/assets/images/wardrobe/hd.png) no-repeat center; + } + + .head { + background: url(@/assets/images/wardrobe/head.png) no-repeat center; + } + + .torso { + background: url(@/assets/images/wardrobe/torso.png) no-repeat center; + } + + .legs { + background: url(@/assets/images/wardrobe/legs.png) no-repeat center; + } + + .tab-wardrobe { + width: 40px; + height: 28px; + background-size: 38px 28px; + background-image: url(@/assets/images/wardrobe/wardrobe.png); + background-repeat: no-repeat; + background-position: center; + filter: contrast(1.2) brightness(1.05); + } + + + .nav-tabs .nav-link { + position: relative; + border-image-source: url(@/assets/images/boxes/card/tabs_avatareditor.png); + border-image-slice: 7 7 7 7 fill; + border-image-width: 7px 7px 7px 7px; + border-image-outset: 0px 0px 0px 0px; + border-image-repeat: repeat repeat; + margin-bottom: -2px; + margin-left: -2px; + + &:hover { + border-image-source: url(@/assets/images/boxes/card/tabs_active.png); + } + } + + + .nav-tabs .nav-link.active { + border-image-source: url(@/assets/images/boxes/card/tabs_active.png); + } + } + + .randomize-container { + bottom: 95px; + left: 330px; + z-index: 2; + } + + .randomize-icon { + background-image: url(@/assets/images/avatareditor/randomize_transparent.png); + height: 33px; + width: 39px; + + &:hover { + background-image: url(@/assets/images/avatareditor/randomize.png); + } + } + + .avatar-wardrobe { + border-image-source: url(@/assets/images/avatareditor/wardrobe_bg.png); + border-image-slice: 6 6 6 6 fill; + border-image-width: 6px 6px 6px 6px; + } + + .avatar-container { + padding: 3px; + } + + .avatar-parts { + border: none !important; + height: 42px; + width: 42px; + background-position: center; + background-repeat: no-repeat; + border-radius: 2rem !important; + overflow: visible !important; + background-color: transparent; + + &:hover { + box-shadow: 0 0 0 3px #dbdad5 !important; + background-color: #cecdc8 !important; + } + + &:active, + &.part-selected { + box-shadow: 0 0 0 3px #c5c3c0 !important; + background-color: #b1b1b1 !important; + } + } + +.avatar-parts-container { + height: 70%; + padding-left: 10px; +} + +.avatar-color-palette-container { + height: 30%; + width: 100%!important; + padding-left: 10px; +} + +.dual-palette { + display: flex !important; + flex-direction: row !important; +} + +.avatar-editor-palette-set-view { + padding-right: 15px !important; + flex-grow: 1; +} + +.clothing-container { + padding-right: 15px !important; +} + +.action-buttons { + gap: 5px; +} diff --git a/src/components/avatar-editor/AvatarEditorView.tsx b/src/components/avatar-editor/AvatarEditorView.tsx index 6b3766e..fac7986 100644 --- a/src/components/avatar-editor/AvatarEditorView.tsx +++ b/src/components/avatar-editor/AvatarEditorView.tsx @@ -1,8 +1,8 @@ -import { AvatarEditorFigureCategory, FigureSetIdsMessageEvent, GetWardrobeMessageComposer, IAvatarFigureContainer, ILinkEventTracker, UserFigureComposer, UserWardrobePageEvent } from '@nitrots/nitro-renderer'; +import { AvatarEditorFigureCategory, FigureSetIdsMessageEvent, GetWardrobeMessageComposer, IAvatarFigureContainer, ILinkEventTracker, SetClothingChangeDataMessageComposer, UserFigureComposer, UserWardrobePageEvent } from '@nitrots/nitro-renderer'; import { FC, useCallback, useEffect, useMemo, useState } from 'react'; import { FaDice, FaTrash, FaUndo } from 'react-icons/fa'; -import { AddEventLinkTracker, AvatarEditorAction, AvatarEditorUtilities, BodyModel, FigureData, generateRandomFigure, GetAvatarRenderManager, GetClubMemberLevel, GetConfiguration, GetSessionDataManager, HeadModel, IAvatarEditorCategoryModel, LegModel, LocalizeText, RemoveLinkEventTracker, SendMessageComposer, TorsoModel } from '../../api'; -import { Button, ButtonGroup, Column, Grid, NitroCardContentView, NitroCardHeaderView, NitroCardTabsItemView, NitroCardTabsView, NitroCardView } from '../../common'; +import { AddEventLinkTracker, AvatarEditorAction, AvatarEditorUtilities, BodyModel, FigureData, GetAvatarRenderManager, GetClubMemberLevel, GetConfiguration, GetSessionDataManager, HeadModel, IAvatarEditorCategoryModel, LegModel, LocalizeText, RemoveLinkEventTracker, SendMessageComposer, SetLocalStorage, TorsoModel, generateRandomFigure } from '../../api'; +import { Button, ButtonGroup, Column, Flex, Grid, NitroCardContentView, NitroCardHeaderView, NitroCardTabsItemView, NitroCardTabsView, NitroCardView } from '../../common'; import { useMessageEvent } from '../../hooks'; import { AvatarEditorFigurePreviewView } from './views/AvatarEditorFigurePreviewView'; import { AvatarEditorModelView } from './views/AvatarEditorModelView'; @@ -26,8 +26,19 @@ export const AvatarEditorView: FC<{}> = props => const [ lastGender, setLastGender ] = useState(null); const [ needsReset, setNeedsReset ] = useState(true); const [ isInitalized, setIsInitalized ] = useState(false); - + const [ genderFootballGate, setGenderFootballGate ] = useState(null); + const [ objectFootballGate, setObjectFootballGate ] = useState(null); + + const DEFAULT_MALE_FOOTBALL_GATE = JSON.parse(window.localStorage.getItem('nitro.look.footballgate.M')) || 'ch-3109-92-1408.lg-3116-82-1408.sh-3115-1408-1408'; + const DEFAULT_FEMALE_FOOTBALL_GATE = JSON.parse(window.localStorage.getItem('nitro.look.footballgate.F')) || 'ch-3112-1408-1408.lg-3116-71-1408.sh-3115-1408-1408'; const maxWardrobeSlots = useMemo(() => GetConfiguration('avatar.wardrobe.max.slots', 10), []); + + const onClose = () => + { + setGenderFootballGate(null); + setObjectFootballGate(null); + setIsVisible(false); + } useMessageEvent(FigureSetIdsMessageEvent, event => { @@ -50,7 +61,7 @@ export const AvatarEditorView: FC<{}> = props => i++; } - + for(let [ index, [ look, gender ] ] of parser.looks.entries()) { const container = GetAvatarRenderManager().createFigureContainer(look); @@ -64,7 +75,7 @@ export const AvatarEditorView: FC<{}> = props => const selectCategory = useCallback((name: string) => { if(!categories) return; - + setActiveCategory(categories.get(name)); }, [ categories ]); @@ -72,13 +83,21 @@ export const AvatarEditorView: FC<{}> = props => { const categories = new Map(); - categories.set(AvatarEditorFigureCategory.GENERIC, new BodyModel()); - categories.set(AvatarEditorFigureCategory.HEAD, new HeadModel()); - categories.set(AvatarEditorFigureCategory.TORSO, new TorsoModel()); - categories.set(AvatarEditorFigureCategory.LEGS, new LegModel()); + if (!genderFootballGate) + { + categories.set(AvatarEditorFigureCategory.GENERIC, new BodyModel()); + categories.set(AvatarEditorFigureCategory.HEAD, new HeadModel()); + categories.set(AvatarEditorFigureCategory.TORSO, new TorsoModel()); + categories.set(AvatarEditorFigureCategory.LEGS, new LegModel()); + } + else + { + categories.set(AvatarEditorFigureCategory.TORSO, new TorsoModel()); + categories.set(AvatarEditorFigureCategory.LEGS, new LegModel()); + } setCategories(categories); - }, []); + }, [ genderFootballGate ]); const setupFigures = useCallback(() => { @@ -135,11 +154,12 @@ export const AvatarEditorView: FC<{}> = props => resetCategories(); return; case AvatarEditorAction.ACTION_SAVE: - SendMessageComposer(new UserFigureComposer(figureData.gender, figureData.getFigureString())); - setIsVisible(false); + !genderFootballGate ? SendMessageComposer(new UserFigureComposer(figureData.gender, figureData.getFigureString())) : SendMessageComposer(new SetClothingChangeDataMessageComposer(objectFootballGate, genderFootballGate, figureData.getFigureString())); + SetLocalStorage(`nitro.look.footballgate.${ genderFootballGate }`, figureData.getFigureString()); + onClose(); return; } - }, [ figureData, lastFigure, lastGender, figureSetIds, loadAvatarInEditor, resetCategories ]) + }, [ loadAvatarInEditor, figureData, resetCategories, lastFigure, lastGender, figureSetIds, genderFootballGate, objectFootballGate ]) const setGender = useCallback((gender: string) => { @@ -154,9 +174,12 @@ export const AvatarEditorView: FC<{}> = props => linkReceived: (url: string) => { const parts = url.split('/'); - + + setGenderFootballGate(parts[2] ? parts[2] : null); + setObjectFootballGate(parts[3] ? Number(parts[3]) : null); + if(parts.length < 2) return; - + switch(parts[1]) { case 'show': @@ -185,25 +208,15 @@ export const AvatarEditorView: FC<{}> = props => useEffect(() => { - if(!isWardrobeVisible) return; - - setActiveCategory(null); SendMessageComposer(new GetWardrobeMessageComposer()); - }, [ isWardrobeVisible ]); - - useEffect(() => - { - if(!activeCategory) return; - - setIsWardrobeVisible(false); - }, [ activeCategory ]); + }, []); useEffect(() => { if(!categories) return; - selectCategory(AvatarEditorFigureCategory.GENERIC); - }, [ categories, selectCategory ]); + selectCategory(!genderFootballGate ? AvatarEditorFigureCategory.GENERIC : AvatarEditorFigureCategory.TORSO); + }, [ categories, genderFootballGate, selectCategory ]); useEffect(() => { @@ -248,9 +261,22 @@ export const AvatarEditorView: FC<{}> = props => { if(!isVisible || !isInitalized || !needsReset) return; - loadAvatarInEditor(GetSessionDataManager().figure, GetSessionDataManager().gender); + if (!genderFootballGate) loadAvatarInEditor(GetSessionDataManager().figure, GetSessionDataManager().gender); + if (genderFootballGate) loadAvatarInEditor(genderFootballGate === FigureData.MALE ? DEFAULT_MALE_FOOTBALL_GATE : DEFAULT_FEMALE_FOOTBALL_GATE, genderFootballGate); setNeedsReset(false); - }, [ isVisible, isInitalized, needsReset, loadAvatarInEditor ]); + }, [ isVisible, isInitalized, needsReset, loadAvatarInEditor, genderFootballGate, DEFAULT_MALE_FOOTBALL_GATE, DEFAULT_FEMALE_FOOTBALL_GATE ]); + + useEffect(() => // This is so when you have the look editor open and you change the mode to Boy or Girl + { + if(!isVisible) return; + + return () => + { + setupFigures(); + setIsWardrobeVisible(false); + setNeedsReset(true); + } + }, [ isVisible, genderFootballGate, setupFigures ]); useEffect(() => { @@ -264,50 +290,64 @@ export const AvatarEditorView: FC<{}> = props => if(!isVisible || !figureData) return null; + const avatarEditorClasses = `nitro-avatar-editor no-resize ${ isWardrobeVisible ? 'expanded' : '' }`; + return ( - - setIsVisible(false) } /> - + + + { categories && (categories.size > 0) && Array.from(categories.keys()).map(category => { const isActive = (activeCategory && (activeCategory.name === category)); return ( selectCategory(category) }> - { LocalizeText(`avatareditor.category.${ category }`) } +
); }) } - setIsWardrobeVisible(true) }> - { LocalizeText('avatareditor.category.wardrobe') } - + { (!genderFootballGate) && + setIsWardrobeVisible(!isWardrobeVisible) }> +
+
+ }
- - { (activeCategory && !isWardrobeVisible) && - } - { isWardrobeVisible && - } + + { (activeCategory) && + + } - - - - - - - - - - + + + + + + { (!genderFootballGate) && + + + + + + } + + + + { isWardrobeVisible && + + + + } + diff --git a/src/components/avatar-editor/views/AvatarEditorFigurePreviewView.tsx b/src/components/avatar-editor/views/AvatarEditorFigurePreviewView.tsx index d5715ac..8bde241 100644 --- a/src/components/avatar-editor/views/AvatarEditorFigurePreviewView.tsx +++ b/src/components/avatar-editor/views/AvatarEditorFigurePreviewView.tsx @@ -44,11 +44,9 @@ export const AvatarEditorFigurePreviewView: FC - - rotateFigure(figureData.direction + 1) } /> - rotateFigure(figureData.direction - 1) } /> + rotateFigure(figureData.direction + 1) } /> ); diff --git a/src/components/avatar-editor/views/AvatarEditorModelView.tsx b/src/components/avatar-editor/views/AvatarEditorModelView.tsx index 6eb8fe3..e88512e 100644 --- a/src/components/avatar-editor/views/AvatarEditorModelView.tsx +++ b/src/components/avatar-editor/views/AvatarEditorModelView.tsx @@ -1,9 +1,12 @@ import { Dispatch, FC, SetStateAction, useCallback, useEffect, useState } from 'react'; -import { CategoryData, FigureData, IAvatarEditorCategoryModel } from '../../../api'; -import { Column, Flex, Grid } from '../../../common'; +import { CategoryData, FigureData, IAvatarEditorCategoryModel, LocalizeText } from '../../../api'; +import { Column, Flex, Grid, Text } from '../../../common'; import { AvatarEditorIcon } from './AvatarEditorIcon'; import { AvatarEditorFigureSetView } from './figure-set/AvatarEditorFigureSetView'; import { AvatarEditorPaletteSetView } from './palette-set/AvatarEditorPaletteSetView'; + +const CATEGORY_FOOTBALL_GATE = [ 'ch', 'cp', 'lg', 'sh' ]; + export interface AvatarEditorModelViewProps { model: IAvatarEditorCategoryModel; @@ -13,7 +16,7 @@ export interface AvatarEditorModelViewProps export const AvatarEditorModelView: FC = props => { - const { model = null, gender = null, setGender = null } = props; + const { model = null, gender = null, isFromFootballGate = false, setGender = null } = props; const [ activeCategory, setActiveCategory ] = useState(null); const [ maxPaletteCount, setMaxPaletteCount ] = useState(1); @@ -53,35 +56,48 @@ export const AvatarEditorModelView: FC = props => return ( - - { model.canSetGender && + + + { model.canSetGender && <> - setGender(FigureData.MALE) }> + setGender(FigureData.MALE) }> + { LocalizeText('avatareditor.generic.boy') } - setGender(FigureData.FEMALE) }> + setGender(FigureData.FEMALE) }> + { LocalizeText('avatareditor.generic.girl') } } - { !model.canSetGender && model.categories && (model.categories.size > 0) && Array.from(model.categories.keys()).map(name => - { - const category = model.categories.get(name); + { !model.canSetGender && model.categories && (model.categories.size > 0) && Array.from(model.categories.keys()).map(name => + { + const category = model.categories.get(name); - return ( - selectCategory(name) }> - - - ); - }) } - - - - - - { (maxPaletteCount >= 1) && + return ( +
+ selectCategory(name) }> + { (isFromFootballGate && CATEGORY_FOOTBALL_GATE.includes(category.name)) && + + } + { (!isFromFootballGate) && + + } + +
+ ); + }) } + + + + + + { (maxPaletteCount >= 1) && } - { (maxPaletteCount === 2) && + { (maxPaletteCount === 2) && } +
); diff --git a/src/components/avatar-editor/views/AvatarEditorWardrobeView.tsx b/src/components/avatar-editor/views/AvatarEditorWardrobeView.tsx index 9811ab8..4bd3f1b 100644 --- a/src/components/avatar-editor/views/AvatarEditorWardrobeView.tsx +++ b/src/components/avatar-editor/views/AvatarEditorWardrobeView.tsx @@ -1,8 +1,8 @@ -import { IAvatarFigureContainer, SaveWardrobeOutfitMessageComposer } from '@nitrots/nitro-renderer'; +import { HabboClubLevelEnum, IAvatarFigureContainer, SaveWardrobeOutfitMessageComposer } from '@nitrots/nitro-renderer'; import { Dispatch, FC, SetStateAction, useCallback, useMemo } from 'react'; -import { FigureData, GetAvatarRenderManager, GetClubMemberLevel, GetConfiguration, LocalizeText, SendMessageComposer } from '../../../api'; -import { AutoGrid, Base, Button, Flex, LayoutAvatarImageView, LayoutCurrencyIcon, LayoutGridItem } from '../../../common'; - +import { MdKeyboardArrowLeft, MdKeyboardArrowRight } from 'react-icons/md'; +import { CreateLinkEvent, FigureData, GetAvatarRenderManager, GetClubMemberLevel, GetConfiguration, GetSessionDataManager, LocalizeText, SendMessageComposer } from '../../../api'; +import { Flex, LayoutAvatarImageView, LayoutCurrencyIcon } from '../../../common'; export interface AvatarEditorWardrobeViewProps { figureData: FigureData; @@ -30,6 +30,8 @@ export const AvatarEditorWardrobeView: FC = props { if(!figureData || (index >= savedFigures.length) || (index < 0)) return; + if (GetSessionDataManager().clubLevel === HabboClubLevelEnum.NO_CLUB) return CreateLinkEvent('habboUI/open/hccenter'); + const newFigures = [ ...savedFigures ]; const figure = figureData.getFigureString(); @@ -41,6 +43,22 @@ export const AvatarEditorWardrobeView: FC = props SendMessageComposer(new SaveWardrobeOutfitMessageComposer((index + 1), figure, gender)); }, [ figureData, savedFigures, setSavedFigures ]); + const getClubLevel = useCallback(() => + { + let highestClubLevel = 0; + + savedFigures.forEach(([ figureContainer, gender ]) => + { + if (figureContainer) + { + const clubLevel = GetAvatarRenderManager().getFigureClubLevel(figureContainer, gender); + highestClubLevel = Math.max(highestClubLevel, clubLevel); + } + }); + + return highestClubLevel; + }, [ savedFigures ]); + const figures = useMemo(() => { if(!savedFigures || !savedFigures.length) return []; @@ -54,26 +72,52 @@ export const AvatarEditorWardrobeView: FC = props if(figureContainer) clubLevel = GetAvatarRenderManager().getFigureClubLevel(figureContainer, gender); items.push( - - { figureContainer && - } - - { !hcDisabled && (clubLevel > 0) && } - - - { figureContainer && - } + + + + { figureContainer && ( + + ) } - +
+ { figureContainer && ( + + ) } +
+ ); }); return items; - }, [ savedFigures, hcDisabled, saveFigureAtWardrobeIndex, wearFigureAtIndex ]); + }, [ savedFigures, saveFigureAtWardrobeIndex, wearFigureAtIndex ]); return ( - - { figures } - +
+
+ + { LocalizeText('avatareditor.wardrobe.title') } + + + { !hcDisabled && getClubLevel() > 0 && ( + + ) } + +
+
+
{ figures }
+
+
); + } diff --git a/src/components/avatar-editor/views/figure-set/AvatarEditorFigureSetItemView.tsx b/src/components/avatar-editor/views/figure-set/AvatarEditorFigureSetItemView.tsx index fd28dc5..e960413 100644 --- a/src/components/avatar-editor/views/figure-set/AvatarEditorFigureSetItemView.tsx +++ b/src/components/avatar-editor/views/figure-set/AvatarEditorFigureSetItemView.tsx @@ -25,11 +25,13 @@ export const AvatarEditorFigureSetItemView: FC - { !hcDisabled && partItem.isHC && } - { partItem.isClear && } - { partItem.isSellable && } - { children } - +
+ + { !hcDisabled && partItem.isHC && } + { partItem.isClear && } + { partItem.isSellable && } + { children } + +
); } diff --git a/src/components/avatar-editor/views/figure-set/AvatarEditorFigureSetView.tsx b/src/components/avatar-editor/views/figure-set/AvatarEditorFigureSetView.tsx index 3755731..f6eb6b1 100644 --- a/src/components/avatar-editor/views/figure-set/AvatarEditorFigureSetView.tsx +++ b/src/components/avatar-editor/views/figure-set/AvatarEditorFigureSetView.tsx @@ -1,8 +1,14 @@ +import { HabboClubLevelEnum } from '@nitrots/nitro-renderer'; import { Dispatch, FC, SetStateAction, useCallback, useEffect, useRef } from 'react'; -import { AvatarEditorGridPartItem, CategoryData, IAvatarEditorCategoryModel } from '../../../../api'; +import { AvatarEditorGridPartItem, CategoryData, CreateLinkEvent, GetSessionDataManager, IAvatarEditorCategoryModel } from '../../../../api'; import { AutoGrid } from '../../../../common'; import { AvatarEditorFigureSetItemView } from './AvatarEditorFigureSetItemView'; +const TSHIRT_FOOTBALL_GATE = [ 3111, 3110, 3109, 3030, 3114, 266, 265, 262, 3113, 3112, 691, 690, 667 ]; +const NUMBER_BEHIND_FOOTBALL_GATE = [ 3128, 3127, 3126, 3125, 3124, 3123, 3122, 3121, 3120, 3119 ]; +const PANTS_FOOTBALL_GATE = [ 3116, 281, 275, 715, 700, 696, 3006 ]; +const SHOES_FOOTBALL_GATE = [ 3115, 3068, 906 ]; + export interface AvatarEditorFigureSetViewProps { model: IAvatarEditorCategoryModel; @@ -12,7 +18,7 @@ export interface AvatarEditorFigureSetViewProps export const AvatarEditorFigureSetView: FC = props => { - const { model = null, category = null, setMaxPaletteCount = null } = props; + const { model = null, category = null, isFromFootballGate = false, setMaxPaletteCount = null } = props; const elementRef = useRef(null); const selectPart = useCallback((item: AvatarEditorGridPartItem) => @@ -21,6 +27,8 @@ export const AvatarEditorFigureSetView: FC = pro if(index === -1) return; + if (item.isHC && GetSessionDataManager().clubLevel === HabboClubLevelEnum.NO_CLUB) return CreateLinkEvent('habboUI/open/hccenter'); + model.selectPart(category.name, index); const partItem = category.getCurrentPart(); @@ -36,9 +44,11 @@ export const AvatarEditorFigureSetView: FC = pro }, [ model, category ]); return ( - - { (category.parts.length > 0) && category.parts.map((item, index) => - selectPart(item) } />) } - + + { (category.parts.length > 0) && category.parts.map(item => + (!isFromFootballGate || (isFromFootballGate && TSHIRT_FOOTBALL_GATE.includes(item.id) || NUMBER_BEHIND_FOOTBALL_GATE.includes(item.id) || PANTS_FOOTBALL_GATE.includes(item.id) || SHOES_FOOTBALL_GATE.includes(item.id))) && + selectPart(item) } />) + } + ); } diff --git a/src/components/avatar-editor/views/palette-set/AvatarEditorPaletteSetItemView.tsx b/src/components/avatar-editor/views/palette-set/AvatarEditorPaletteSetItemView.tsx index 638a9d1..610995d 100644 --- a/src/components/avatar-editor/views/palette-set/AvatarEditorPaletteSetItemView.tsx +++ b/src/components/avatar-editor/views/palette-set/AvatarEditorPaletteSetItemView.tsx @@ -1,6 +1,7 @@ import { FC, useEffect, useState } from 'react'; import { AvatarEditorGridColorItem, GetConfiguration } from '../../../../api'; import { LayoutCurrencyIcon, LayoutGridItem, LayoutGridItemProps } from '../../../../common'; +import { LayoutGridColorPickerItem } from '../../../../common/layout/LayoutGridColorPickerItem'; export interface AvatarEditorPaletteSetItemProps extends LayoutGridItemProps { @@ -24,9 +25,9 @@ export const AvatarEditorPaletteSetItem: FC = p }, [ colorItem ]); return ( - - { !hcDisabled && colorItem.isHC && } + + { !hcDisabled && colorItem.isHC && } { children } - + ); } diff --git a/src/components/avatar-editor/views/palette-set/AvatarEditorPaletteSetView.tsx b/src/components/avatar-editor/views/palette-set/AvatarEditorPaletteSetView.tsx index c55dcb4..3fe0ff9 100644 --- a/src/components/avatar-editor/views/palette-set/AvatarEditorPaletteSetView.tsx +++ b/src/components/avatar-editor/views/palette-set/AvatarEditorPaletteSetView.tsx @@ -1,5 +1,6 @@ +import { HabboClubLevelEnum } from '@nitrots/nitro-renderer'; import { FC, useCallback, useEffect, useRef } from 'react'; -import { AvatarEditorGridColorItem, CategoryData, IAvatarEditorCategoryModel } from '../../../../api'; +import { AvatarEditorGridColorItem, CategoryData, CreateLinkEvent, GetSessionDataManager, IAvatarEditorCategoryModel } from '../../../../api'; import { AutoGrid } from '../../../../common'; import { AvatarEditorPaletteSetItem } from './AvatarEditorPaletteSetItemView'; @@ -21,6 +22,8 @@ export const AvatarEditorPaletteSetView: FC = p const index = paletteSet.indexOf(item); if(index === -1) return; + + if (item.isHC && GetSessionDataManager().clubLevel === HabboClubLevelEnum.NO_CLUB) return CreateLinkEvent('habboUI/open/hccenter'); model.selectColor(category.name, index, paletteIndex); }, [ model, category, paletteSet, paletteIndex ]); @@ -33,9 +36,21 @@ export const AvatarEditorPaletteSetView: FC = p }, [ model, category ]); return ( - - { (paletteSet.length > 0) && paletteSet.map((item, index) => - selectColor(item) } />) } + + { paletteSet.length > 0 && + paletteSet.map((item, index) => ( + selectColor(item) } + /> + )) } ); }