Feature - Football Gate

This commit is contained in:
oobjectt 2023-04-07 03:46:10 +02:00
parent aba4ddf2db
commit ec284d5b52
8 changed files with 187 additions and 42 deletions

View File

@ -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<string>(null);
const [ needsReset, setNeedsReset ] = useState(true);
const [ isInitalized, setIsInitalized ] = useState(false);
const [ genderFootballGate, setGenderFootballGate ] = useState<string>(null);
const [ objectFootballGate, setObjectFootballGate ] = useState<number>(null);
const maxWardrobeSlots = useMemo(() => GetConfiguration<number>('avatar.wardrobe.max.slots', 10), []);
const onClose = () =>
{
setGenderFootballGate(null);
setObjectFootballGate(null);
setIsVisible(false);
}
useMessageEvent<FigureSetIdsMessageEvent>(FigureSetIdsMessageEvent, event =>
{
const parser = event.getParser();
@ -72,13 +83,21 @@ export const AvatarEditorView: FC<{}> = props =>
{
const categories = new Map();
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) =>
{
@ -155,6 +174,9 @@ export const AvatarEditorView: FC<{}> = props =>
{
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 (
<NitroCardView uniqueKey="avatar-editor" className="nitro-avatar-editor">
<NitroCardHeaderView headerText={ LocalizeText('avatareditor.title') } onCloseClick={ event => setIsVisible(false) } />
<NitroCardHeaderView headerText={ !genderFootballGate ? LocalizeText('avatareditor.title') : LocalizeText('widget.furni.clothingchange.editor.title') } onCloseClick={ onClose } />
<NitroCardTabsView>
{ categories && (categories.size > 0) && Array.from(categories.keys()).map(category =>
{
@ -278,21 +310,24 @@ export const AvatarEditorView: FC<{}> = props =>
</NitroCardTabsItemView>
);
}) }
{ (!genderFootballGate) &&
<NitroCardTabsItemView isActive={ isWardrobeVisible } onClick={ event => setIsWardrobeVisible(true) }>
{ LocalizeText('avatareditor.category.wardrobe') }
</NitroCardTabsItemView>
}
</NitroCardTabsView>
<NitroCardContentView>
<Grid>
<Column size={ 9 } overflow="hidden">
{ (activeCategory && !isWardrobeVisible) &&
<AvatarEditorModelView model={ activeCategory } gender={ figureData.gender } setGender={ setGender } /> }
<AvatarEditorModelView model={ activeCategory } gender={ figureData.gender } isFromFootballGate={ !genderFootballGate ? false : true } setGender={ setGender } /> }
{ isWardrobeVisible &&
<AvatarEditorWardrobeView figureData={ figureData } savedFigures={ savedFigures } setSavedFigures={ setSavedFigures } loadAvatarInEditor={ loadAvatarInEditor } /> }
</Column>
<Column size={ 3 } overflow="hidden">
<AvatarEditorFigurePreviewView figureData={ figureData } />
<Column grow gap={ 1 }>
{ (!genderFootballGate) &&
<ButtonGroup>
<Button variant="secondary" onClick={ event => processAction(AvatarEditorAction.ACTION_RESET) }>
<FaUndo className="fa-icon" />
@ -304,6 +339,7 @@ export const AvatarEditorView: FC<{}> = props =>
<FaDice className="fa-icon" />
</Button>
</ButtonGroup>
}
<Button className="w-100" variant="success" onClick={ event => processAction(AvatarEditorAction.ACTION_SAVE) }>
{ LocalizeText('avatareditor.save') }
</Button>

View File

@ -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<SetStateAction<string>>;
}
export const AvatarEditorModelView: FC<AvatarEditorModelViewProps> = props =>
{
const { model = null, gender = null, setGender = null } = props;
const { model = null, gender = null, isFromFootballGate = false, setGender = null } = props;
const [ activeCategory, setActiveCategory ] = useState<CategoryData>(null);
const [ maxPaletteCount, setMaxPaletteCount ] = useState(1);
@ -68,14 +71,21 @@ export const AvatarEditorModelView: FC<AvatarEditorModelViewProps> = props =>
const category = model.categories.get(name);
return (
<Flex center pointer key={ name } className="category-item" onClick={ event => selectCategory(name) }>
<div key={ name }>
<Flex center pointer className="category-item" onClick={ event => selectCategory(name) }>
{ (isFromFootballGate && CATEGORY_FOOTBALL_GATE.includes(category.name)) &&
<AvatarEditorIcon icon={ category.name } selected={ (activeCategory === category) } />
}
{ (!isFromFootballGate) &&
<AvatarEditorIcon icon={ category.name } selected={ (activeCategory === category) } />
}
</Flex>
</div>
);
}) }
</Column>
<Column size={ 5 } overflow="hidden">
<AvatarEditorFigureSetView model={ model } category={ activeCategory } setMaxPaletteCount={ setMaxPaletteCount } />
<AvatarEditorFigureSetView model={ model } category={ activeCategory } isFromFootballGate={ isFromFootballGate } setMaxPaletteCount={ setMaxPaletteCount } />
</Column>
<Column size={ 5 } overflow="hidden">
{ (maxPaletteCount >= 1) &&

View File

@ -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<SetStateAction<number>>;
}
export const AvatarEditorFigureSetView: FC<AvatarEditorFigureSetViewProps> = props =>
{
const { model = null, category = null, setMaxPaletteCount = null } = props;
const { model = null, category = null, isFromFootballGate = false, setMaxPaletteCount = null } = props;
const elementRef = useRef<HTMLDivElement>(null);
const selectPart = useCallback((item: AvatarEditorGridPartItem) =>
@ -37,8 +42,10 @@ export const AvatarEditorFigureSetView: FC<AvatarEditorFigureSetViewProps> = pro
return (
<AutoGrid innerRef={ elementRef } columnCount={ 3 } columnMinHeight={ 50 }>
{ (category.parts.length > 0) && category.parts.map((item, index) =>
<AvatarEditorFigureSetItemView key={ index } partItem={ item } onClick={ event => 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))) &&
<AvatarEditorFigureSetItemView key={ item.id } partItem={ item } onClick={ event => selectPart(item) } />)
}
</AutoGrid>
);
}

View File

@ -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 (
<NitroCardView className="nitro-football-gate no-resize" theme="primary-slim">
<NitroCardHeaderView headerText={ LocalizeText('widget.furni.clothingchange.gender.title') } onCloseClick={ onClose } />
<NitroCardContentView className="football-gate-content">
<Flex fullWidth center>
<Column>{ LocalizeText('widget.furni.clothingchange.gender.info') }</Column>
</Flex>
<Flex className="mt-4 px-2" justifyContent="between">
<Button className="size-buttons" onClick={ (e) => onGender(FigureData.MALE) }>
{ LocalizeText('widget.furni.clothingchange.gender.male') }
</Button>
<Button className="size-buttons" onClick={ (e) => onGender(FigureData.FEMALE) }>
{ LocalizeText('widget.furni.clothingchange.gender.female') }
</Button>
</Flex>
</NitroCardContentView>
</NitroCardView>
);
}

View File

@ -524,3 +524,18 @@
}
}
}
.nitro-football-gate
{
width: 300px;
.football-gate-content
{
color: black;
.size-buttons
{
width: 100px;
}
}
}

View File

@ -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 =>
<FurnitureTrophyView />
<FurnitureContextMenuView />
<FurnitureYoutubeDisplayView />
<FurnitureFootballGateView />
</Base>
);
}

View File

@ -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';

View File

@ -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<number>(-1);
const [ category, setCategory ] = useState<number>(-1);
const onClose = () =>
{
setObjectId(-1);
setCategory(-1);
}
useRoomEngineEvent<RoomEngineTriggerWidgetEvent>(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;