diff --git a/src/components/avatar-editor/AvatarEditorView.tsx b/src/components/avatar-editor/AvatarEditorView.tsx index 6b3766e23..ada82a52a 100644 --- a/src/components/avatar-editor/AvatarEditorView.tsx +++ b/src/components/avatar-editor/AvatarEditorView.tsx @@ -1,7 +1,7 @@ -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 { AddEventLinkTracker, AvatarEditorAction, AvatarEditorUtilities, BodyModel, FigureData, GetAvatarRenderManager, GetClubMemberLevel, GetConfiguration, GetSessionDataManager, HeadModel, IAvatarEditorCategoryModel, LegModel, LocalizeText, RemoveLinkEventTracker, SendMessageComposer, TorsoModel, generateRandomFigure } from '../../api'; import { Button, ButtonGroup, Column, Grid, NitroCardContentView, NitroCardHeaderView, NitroCardTabsItemView, NitroCardTabsView, NitroCardView } from '../../common'; import { useMessageEvent } from '../../hooks'; import { AvatarEditorFigurePreviewView } from './views/AvatarEditorFigurePreviewView'; @@ -10,6 +10,8 @@ import { AvatarEditorWardrobeView } from './views/AvatarEditorWardrobeView'; const DEFAULT_MALE_FIGURE: string = 'hr-100.hd-180-7.ch-215-66.lg-270-79.sh-305-62.ha-1002-70.wa-2007'; const DEFAULT_FEMALE_FIGURE: string = 'hr-515-33.hd-600-1.ch-635-70.lg-716-66-62.sh-735-68'; +const DEFAULT_MALE_FOOTBALL_GATE: string = 'ch-3109-92-1408.lg-3116-82-1408.sh-3115-1408-1408'; +const DEFAULT_FEMALE_FOOTBALL_GATE: string = 'ch-3112-1408-1408.lg-3116-71-1408.sh-3115-1408-1408'; export const AvatarEditorView: FC<{}> = props => { @@ -26,9 +28,18 @@ 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 maxWardrobeSlots = useMemo(() => GetConfiguration('avatar.wardrobe.max.slots', 10), []); + const onClose = () => + { + setGenderFootballGate(null); + setObjectFootballGate(null); + setIsVisible(false); + } + useMessageEvent(FigureSetIdsMessageEvent, event => { const parser = event.getParser(); @@ -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,11 @@ 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())); + onClose(); return; } - }, [ figureData, lastFigure, lastGender, figureSetIds, loadAvatarInEditor, resetCategories ]) + }, [ loadAvatarInEditor, figureData, resetCategories, lastFigure, lastGender, figureSetIds, genderFootballGate, objectFootballGate ]) const setGender = useCallback((gender: string) => { @@ -154,7 +173,10 @@ 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]) @@ -202,8 +224,8 @@ export const AvatarEditorView: FC<{}> = props => { if(!categories) return; - selectCategory(AvatarEditorFigureCategory.GENERIC); - }, [ categories, selectCategory ]); + selectCategory(!genderFootballGate ? AvatarEditorFigureCategory.GENERIC : AvatarEditorFigureCategory.TORSO); + }, [ categories, genderFootballGate, selectCategory ]); useEffect(() => { @@ -248,9 +270,19 @@ export const AvatarEditorView: FC<{}> = props => { if(!isVisible || !isInitalized || !needsReset) return; - loadAvatarInEditor(GetSessionDataManager().figure, GetSessionDataManager().gender); + loadAvatarInEditor(!genderFootballGate ? GetSessionDataManager().figure : (genderFootballGate === FigureData.MALE ? DEFAULT_MALE_FOOTBALL_GATE : DEFAULT_FEMALE_FOOTBALL_GATE), !genderFootballGate ? GetSessionDataManager().gender : genderFootballGate); setNeedsReset(false); - }, [ isVisible, isInitalized, needsReset, loadAvatarInEditor ]); + }, [ isVisible, isInitalized, needsReset, loadAvatarInEditor, genderFootballGate ]); + + useEffect(() => // This is so when you have the look editor open and you change the mode to Boy or Girl + { + if(!isVisible) return; + + return () => + { + setNeedsReset(true); + } + }, [ isVisible, genderFootballGate ]); useEffect(() => { @@ -266,7 +298,7 @@ export const AvatarEditorView: FC<{}> = props => return ( - setIsVisible(false) } /> + { categories && (categories.size > 0) && Array.from(categories.keys()).map(category => { @@ -278,32 +310,36 @@ export const AvatarEditorView: FC<{}> = props => ); }) } - setIsWardrobeVisible(true) }> - { LocalizeText('avatareditor.category.wardrobe') } - + { (!genderFootballGate) && + setIsWardrobeVisible(true) }> + { LocalizeText('avatareditor.category.wardrobe') } + + } { (activeCategory && !isWardrobeVisible) && - } + } { isWardrobeVisible && } - - - - - + { (!genderFootballGate) && + + + + + + } diff --git a/src/components/avatar-editor/views/AvatarEditorModelView.tsx b/src/components/avatar-editor/views/AvatarEditorModelView.tsx index 6eb8fe3ed..e1b683b8a 100644 --- a/src/components/avatar-editor/views/AvatarEditorModelView.tsx +++ b/src/components/avatar-editor/views/AvatarEditorModelView.tsx @@ -4,16 +4,19 @@ import { Column, Flex, Grid } 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; gender: string; + isFromFootballGate: boolean; setGender: Dispatch>; } 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); @@ -68,14 +71,15 @@ export const AvatarEditorModelView: FC = props => const category = model.categories.get(name); return ( - selectCategory(name) }> + (!isFromFootballGate || (isFromFootballGate && CATEGORY_FOOTBALL_GATE.includes(category.name))) && + selectCategory(name) }> - ); + ) }) } - + { (maxPaletteCount >= 1) && diff --git a/src/components/avatar-editor/views/figure-set/AvatarEditorFigureSetView.tsx b/src/components/avatar-editor/views/figure-set/AvatarEditorFigureSetView.tsx index 3755731c2..0fe6bbe92 100644 --- a/src/components/avatar-editor/views/figure-set/AvatarEditorFigureSetView.tsx +++ b/src/components/avatar-editor/views/figure-set/AvatarEditorFigureSetView.tsx @@ -3,16 +3,21 @@ import { AvatarEditorGridPartItem, CategoryData, IAvatarEditorCategoryModel } fr 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; category: CategoryData; + isFromFootballGate: boolean; setMaxPaletteCount: Dispatch>; } 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) => @@ -37,8 +42,10 @@ export const AvatarEditorFigureSetView: FC = pro 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/room/widgets/furniture/FurnitureFootballGateView.tsx b/src/components/room/widgets/furniture/FurnitureFootballGateView.tsx new file mode 100644 index 000000000..9e0ca4592 --- /dev/null +++ b/src/components/room/widgets/furniture/FurnitureFootballGateView.tsx @@ -0,0 +1,36 @@ +import { FC } from 'react'; +import { CreateLinkEvent, FigureData, LocalizeText } from '../../../../api'; +import { Button, Column, Flex, NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../../../common'; +import { useFurnitureFootballGateWidget } from '../../../../hooks'; + +export const FurnitureFootballGateView: FC<{}> = props => +{ + const { objectId, setObjectId, onClose } = useFurnitureFootballGateWidget(); + + const onGender = (gender: string) => + { + CreateLinkEvent(`avatar-editor/show/${ gender }/${ objectId }`); + setObjectId(-1); + } + + if(objectId === -1) return null; + + return ( + + + + + { LocalizeText('widget.furni.clothingchange.gender.info') } + + + + + + + + ); +} diff --git a/src/components/room/widgets/furniture/FurnitureWidgets.scss b/src/components/room/widgets/furniture/FurnitureWidgets.scss index 65b6559fc..06a76b5da 100644 --- a/src/components/room/widgets/furniture/FurnitureWidgets.scss +++ b/src/components/room/widgets/furniture/FurnitureWidgets.scss @@ -524,3 +524,18 @@ } } } + +.nitro-football-gate +{ + width: 300px; + + .football-gate-content + { + color: black; + + .size-buttons + { + width: 100px; + } + } +} diff --git a/src/components/room/widgets/furniture/FurnitureWidgetsView.tsx b/src/components/room/widgets/furniture/FurnitureWidgetsView.tsx index d0e4066d6..e20c0b14b 100644 --- a/src/components/room/widgets/furniture/FurnitureWidgetsView.tsx +++ b/src/components/room/widgets/furniture/FurnitureWidgetsView.tsx @@ -1,12 +1,12 @@ import { FC } from 'react'; import { Base } from '../../../../common'; -import { FurnitureContextMenuView } from './context-menu/FurnitureContextMenuView'; import { FurnitureBackgroundColorView } from './FurnitureBackgroundColorView'; import { FurnitureBadgeDisplayView } from './FurnitureBadgeDisplayView'; import { FurnitureCraftingView } from './FurnitureCraftingView'; import { FurnitureDimmerView } from './FurnitureDimmerView'; import { FurnitureExchangeCreditView } from './FurnitureExchangeCreditView'; import { FurnitureExternalImageView } from './FurnitureExternalImageView'; +import { FurnitureFootballGateView } from './FurnitureFootballGateView'; import { FurnitureFriendFurniView } from './FurnitureFriendFurniView'; import { FurnitureGiftOpeningView } from './FurnitureGiftOpeningView'; import { FurnitureHighScoreView } from './FurnitureHighScoreView'; @@ -18,6 +18,7 @@ import { FurnitureStackHeightView } from './FurnitureStackHeightView'; import { FurnitureStickieView } from './FurnitureStickieView'; import { FurnitureTrophyView } from './FurnitureTrophyView'; import { FurnitureYoutubeDisplayView } from './FurnitureYoutubeDisplayView'; +import { FurnitureContextMenuView } from './context-menu/FurnitureContextMenuView'; import { FurniturePlaylistEditorWidgetView } from './playlist-editor/FurniturePlaylistEditorWidgetView'; export const FurnitureWidgetsView: FC<{}> = props => @@ -43,6 +44,7 @@ export const FurnitureWidgetsView: FC<{}> = props => + ); } diff --git a/src/hooks/rooms/widgets/furniture/index.ts b/src/hooks/rooms/widgets/furniture/index.ts index 37fa57323..3d3ce5fdf 100644 --- a/src/hooks/rooms/widgets/furniture/index.ts +++ b/src/hooks/rooms/widgets/furniture/index.ts @@ -5,6 +5,7 @@ export * from './useFurnitureCraftingWidget'; export * from './useFurnitureDimmerWidget'; export * from './useFurnitureExchangeWidget'; export * from './useFurnitureExternalImageWidget'; +export * from './useFurnitureFootballGateWidget'; export * from './useFurnitureFriendFurniWidget'; export * from './useFurnitureHighScoreWidget'; export * from './useFurnitureInternalLinkWidget'; diff --git a/src/hooks/rooms/widgets/furniture/useFurnitureFootballGateWidget.ts b/src/hooks/rooms/widgets/furniture/useFurnitureFootballGateWidget.ts new file mode 100644 index 000000000..3c26750d8 --- /dev/null +++ b/src/hooks/rooms/widgets/furniture/useFurnitureFootballGateWidget.ts @@ -0,0 +1,38 @@ +import { RoomEngineTriggerWidgetEvent } from '@nitrots/nitro-renderer'; +import { useState } from 'react'; +import { GetRoomEngine, IsOwnerOfFurniture } from '../../../../api'; +import { useRoomEngineEvent } from '../../../events'; +import { useFurniRemovedEvent } from '../../engine'; + +const useFurnitureFootballGateWidgetState = () => +{ + const [ objectId, setObjectId ] = useState(-1); + const [ category, setCategory ] = useState(-1); + + const onClose = () => + { + setObjectId(-1); + setCategory(-1); + } + + useRoomEngineEvent(RoomEngineTriggerWidgetEvent.REQUEST_CLOTHING_CHANGE, event => + { + const roomObject = GetRoomEngine().getRoomObject(event.roomId, event.objectId, event.category); + + if(!roomObject || !IsOwnerOfFurniture(roomObject)) return; + + setObjectId(event.objectId); + setCategory(event.category); + }); + + useFurniRemovedEvent(((objectId !== -1) && (category !== -1)), event => + { + if((event.id !== objectId) || (event.category !== category)) return; + + onClose(); + }); + + return { objectId, setObjectId, onClose }; +} + +export const useFurnitureFootballGateWidget = useFurnitureFootballGateWidgetState;