diff --git a/src/assets/images/modtool/m_icon.png b/src/assets/images/modtool/m_icon.png new file mode 100644 index 00000000..1116b4de Binary files /dev/null and b/src/assets/images/modtool/m_icon.png differ diff --git a/src/assets/images/room-widgets/avatar-info/monsterplant-preview.png b/src/assets/images/room-widgets/avatar-info/monsterplant-preview.png new file mode 100644 index 00000000..8d3d771e Binary files /dev/null and b/src/assets/images/room-widgets/avatar-info/monsterplant-preview.png differ diff --git a/src/assets/images/room-widgets/avatar-info/preview-background.png b/src/assets/images/room-widgets/avatar-info/preview-background.png new file mode 100644 index 00000000..dea4f08d Binary files /dev/null and b/src/assets/images/room-widgets/avatar-info/preview-background.png differ diff --git a/src/assets/images/room-widgets/stickie-widget/stickie-christmas.png b/src/assets/images/room-widgets/stickie-widget/stickie-christmas.png new file mode 100644 index 00000000..82b4732f Binary files /dev/null and b/src/assets/images/room-widgets/stickie-widget/stickie-christmas.png differ diff --git a/src/views/room/widgets/RoomWidgets.scss b/src/views/room/widgets/RoomWidgets.scss index 982c8ade..744fa399 100644 --- a/src/views/room/widgets/RoomWidgets.scss +++ b/src/views/room/widgets/RoomWidgets.scss @@ -1,3 +1,4 @@ +@import './avatar-info/AvatarInfoWidgetView'; @import './camera/CameraWidgetView'; @import './chat/ChatWidgetView'; @import './chat-input/ChatInputView'; diff --git a/src/views/room/widgets/avatar-info/AvatarInfoWidgetView.scss b/src/views/room/widgets/avatar-info/AvatarInfoWidgetView.scss new file mode 100644 index 00000000..dffb096d --- /dev/null +++ b/src/views/room/widgets/avatar-info/AvatarInfoWidgetView.scss @@ -0,0 +1,13 @@ +.nitro-use-product-confirmation { + + .product-preview { + width: 122px; + height: 130px; + background: url('../../../../assets/images/room-widgets/avatar-info/preview-background.png') no-repeat center; + + .pet-image { + width: 122px; + height: 130px; + } + } +} diff --git a/src/views/room/widgets/avatar-info/AvatarInfoWidgetView.tsx b/src/views/room/widgets/avatar-info/AvatarInfoWidgetView.tsx index d691e06e..57ef9fdd 100644 --- a/src/views/room/widgets/avatar-info/AvatarInfoWidgetView.tsx +++ b/src/views/room/widgets/avatar-info/AvatarInfoWidgetView.tsx @@ -57,6 +57,11 @@ export const AvatarInfoWidgetView: FC = props => }); }, [ confirmingProduct ]); + const clearProductBubbles = useCallback(() => + { + setProductBubbles([]); + }, []); + const clearInfoStandEvent = useCallback(() => { setInfoStandEvent(null); @@ -67,6 +72,12 @@ export const AvatarInfoWidgetView: FC = props => setName(null); }, []); + const updateConfirmingProduct = useCallback((product: UseProductItem) => + { + setConfirmingProduct(product); + setProductBubbles([]); + }, []); + const onRoomWidgetRoomEngineUpdateEvent = useCallback((event: RoomWidgetRoomEngineUpdateEvent) => { switch(event.type) @@ -131,20 +142,20 @@ export const AvatarInfoWidgetView: FC = props => { if(infoStandEvent instanceof RoomWidgetUpdateInfostandFurniEvent) { - if(infoStandEvent.id === event.id) setInfoStandEvent(null); + if(infoStandEvent.id === event.id) clearInfoStandEvent(); } else if((infoStandEvent instanceof RoomWidgetUpdateInfostandUserEvent) || (infoStandEvent instanceof RoomWidgetUpdateInfostandRentableBotEvent)) { - if(infoStandEvent.roomIndex === event.id) setInfoStandEvent(null); + if(infoStandEvent.roomIndex === event.id) clearInfoStandEvent(); } else if(infoStandEvent instanceof RoomWidgetUpdateInfostandPetEvent) { - if(infoStandEvent.roomIndex === event.id) setInfoStandEvent(null); + if(infoStandEvent.roomIndex === event.id) clearInfoStandEvent(); } } - }, [ name, infoStandEvent, nameBubbles, productBubbles, removeNameBubble ]); + }, [ name, infoStandEvent, nameBubbles, productBubbles, removeNameBubble, clearInfoStandEvent ]); CreateEventDispatcherHook(RoomWidgetRoomObjectUpdateEvent.USER_REMOVED, eventDispatcher, onRoomObjectRemoved); CreateEventDispatcherHook(RoomWidgetRoomObjectUpdateEvent.FURNI_REMOVED, eventDispatcher, onRoomObjectRemoved); @@ -179,9 +190,9 @@ export const AvatarInfoWidgetView: FC = props => const onObjectDeselected = useCallback((event: RoomWidgetRoomObjectUpdateEvent) => { - if(infoStandEvent) setInfoStandEvent(null); + if(infoStandEvent) clearInfoStandEvent(); if(productBubbles.length) setProductBubbles([]); - }, [ infoStandEvent, productBubbles ]); + }, [ infoStandEvent, productBubbles, clearInfoStandEvent ]); CreateEventDispatcherHook(RoomWidgetRoomObjectUpdateEvent.OBJECT_DESELECTED, eventDispatcher, onObjectDeselected); @@ -190,7 +201,8 @@ export const AvatarInfoWidgetView: FC = props => if(event.category !== RoomObjectCategory.UNIT) return; setName(event); - }, []); + clearProductBubbles(); + }, [ clearProductBubbles ]); CreateEventDispatcherHook(RoomWidgetObjectNameEvent.TYPE, eventDispatcher, onRoomWidgetObjectNameEvent); @@ -198,9 +210,11 @@ export const AvatarInfoWidgetView: FC = props => { if(name) setName(null); - if(event.type === RoomWidgetUpdateInfostandFurniEvent.FURNI) setInfoStandEvent(null); + if(event.type === RoomWidgetUpdateInfostandFurniEvent.FURNI) clearInfoStandEvent(); else setInfoStandEvent(event); - }, [ name ]); + + clearProductBubbles(); + }, [ name, clearInfoStandEvent, clearProductBubbles ]); CreateEventDispatcherHook(RoomWidgetUpdateInfostandFurniEvent.FURNI, eventDispatcher, onRoomWidgetUpdateInfostandEvent); CreateEventDispatcherHook(RoomWidgetUpdateInfostandUserEvent.OWN_USER, eventDispatcher, onRoomWidgetUpdateInfostandEvent); @@ -225,6 +239,7 @@ export const AvatarInfoWidgetView: FC = props => const onRoomWidgetUseProductBubbleEvent = useCallback((event: RoomWidgetUseProductBubbleEvent) => { + setConfirmingProduct(null); setProductBubbles(prevValue => { const newBubbles = [ ...prevValue ]; @@ -342,7 +357,7 @@ export const AvatarInfoWidgetView: FC = props => }) } { (productBubbles.length > 0) && productBubbles.map((item, index) => { - return removeProductBubble(index) } />; + return removeProductBubble(index) } />; }) } { rentableBotChatEvent && } { confirmingProduct && setConfirmingProduct(null) } /> } diff --git a/src/views/room/widgets/avatar-info/views/use-product-confirm/AvatarInfoUseProductConfirmView.tsx b/src/views/room/widgets/avatar-info/views/use-product-confirm/AvatarInfoUseProductConfirmView.tsx index 32ea2d7d..8a7b2bf9 100644 --- a/src/views/room/widgets/avatar-info/views/use-product-confirm/AvatarInfoUseProductConfirmView.tsx +++ b/src/views/room/widgets/avatar-info/views/use-product-confirm/AvatarInfoUseProductConfirmView.tsx @@ -1,9 +1,10 @@ -import { IFurnitureData, RoomObjectCategory, RoomUserData } from 'nitro-renderer'; -import { FC, useCallback, useEffect, useState } from 'react'; -import { GetFurnitureDataForRoomObject } from '../../../../../../api'; +import { IFurnitureData, PetCustomPart, PetFigureData, RoomObjectCategory, RoomObjectVariable, RoomUserData } from 'nitro-renderer'; +import { FC, useCallback, useEffect, useMemo, useState } from 'react'; +import { GetFurnitureDataForRoomObject, GetRoomEngine } from '../../../../../../api'; import { NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../../../../../layout'; import { LocalizeText } from '../../../../../../utils/LocalizeText'; import { FurniCategory } from '../../../../../inventory/common/FurniCategory'; +import { PetImageView } from '../../../../../shared/pet-image/PetImageView'; import { useRoomContext } from '../../../../context/RoomContext'; import { RoomWidgetUseProductMessage } from '../../../../messages'; import { AvatarInfoUseProductConfirmViewProps } from './AvatarInfoUseProductConfirmView.types'; @@ -28,7 +29,145 @@ export const AvatarInfoUseProductConfirmView: FC { widgetHandler.processWidgetMessage(new RoomWidgetUseProductMessage(RoomWidgetUseProductMessage.PET_PRODUCT, item.requestRoomObjectId, petData.webID)); - }, [ widgetHandler, item, petData ]); + + close(); + }, [ widgetHandler, item, petData, close ]); + + const getPetImage = useMemo(() => + { + if(!petData || !furniData) return null; + + const petFigureData = new PetFigureData(petData.figure); + const customParts = furniData.customParams.split(' '); + const petIndex = parseInt(customParts[0]); + + switch(furniData.specialType) + { + case FurniCategory._Str_7696: { + if(customParts.length < 2) return null; + + const currentPalette = GetRoomEngine().getPetColorResult(petIndex, petFigureData.paletteId); + const possiblePalettes = GetRoomEngine().getPetColorResultsForTag(petIndex, customParts[1]); + + let paletteId = -1; + + for(const result of possiblePalettes) + { + if(result.breed === currentPalette.breed) + { + paletteId = parseInt(result.id); + + break; + } + } + + return + } + case FurniCategory._Str_7297: { + if(customParts.length < 4) return null; + + const newCustomParts: PetCustomPart[] = []; + + const _local_6 = customParts[1].split(',').map(piece => parseInt(piece)); + const _local_7 = customParts[2].split(",").map(piece => parseInt(piece)); + const _local_8 = customParts[3].split(",").map(piece => parseInt(piece)); + + let _local_10 = 0; + + while(_local_10 < _local_6.length) + { + const _local_13 = _local_6[_local_10]; + const _local_15 = petFigureData.getCustomPart(_local_13); + + let _local_12 = _local_8[_local_10]; + + if (_local_15 != null) _local_12 = _local_15.paletteId; + + newCustomParts.push(new PetCustomPart(_local_13, _local_7[_local_10], _local_12)); + + _local_10++; + } + + return ; + } + case FurniCategory._Str_7954: { + if(customParts.length < 3) return null; + + const newCustomParts: PetCustomPart[] = []; + + const _local_6 = customParts[1].split(",").map(piece => parseInt(piece)); + const _local_8 = customParts[2].split(",").map(piece => parseInt(piece)); + + let _local_10 = 0; + + while(_local_10 < _local_6.length) + { + const _local_13 = _local_6[_local_10]; + const _local_15 = petFigureData.getCustomPart(_local_13); + + let _local_14 = -1; + + if(_local_15 != null) _local_14 = _local_15.partId; + + newCustomParts.push(new PetCustomPart(_local_6[_local_10], _local_14, _local_8[_local_10])); + + _local_10++; + } + + return ; + } + case FurniCategory._Str_6096: { + if(customParts.length < 4) return null; + + const newCustomParts: PetCustomPart[] = []; + + const _local_6 = customParts[1].split(',').map(piece => parseInt(piece)); + const _local_7 = customParts[2].split(",").map(piece => parseInt(piece)); + const _local_8 = customParts[3].split(",").map(piece => parseInt(piece)); + + let _local_10 = 0; + + while(_local_10 < _local_6.length) + { + newCustomParts.push(new PetCustomPart(_local_6[_local_10], _local_7[_local_10], _local_8[_local_10])); + + _local_10++; + } + + for(const _local_21 of petFigureData.customParts) + { + if(_local_6.indexOf(_local_21.layerId) === -1) + { + newCustomParts.push(_local_21); + } + } + + return ; + } + case FurniCategory._Str_8726: + case FurniCategory._Str_6915: + case FurniCategory._Str_9449: { + let posture = 'rip'; + + const roomObject = GetRoomEngine().getRoomObject(roomSession.roomId, petData.roomIndex, RoomObjectCategory.UNIT); + + if(roomObject) + { + posture = roomObject.model.getValue(RoomObjectVariable.FIGURE_POSTURE); + + if(posture === 'rip') + { + const level = petData.petLevel; + + if(level < 7) posture = `grw${ level }`; + else posture = 'std'; + } + } + + return ; + } + } + }, [ petData, furniData, roomSession ]); useEffect(() => { @@ -72,11 +211,60 @@ export const AvatarInfoUseProductConfirmView: FC + - +
+
+ { getPetImage } +
+
+
+ { (mode === _Str_11906) && + <> +
{ LocalizeText('useproduct.widget.text.shampoo', [ 'productName' ], [ furniData.name ] ) }
+
{ LocalizeText('useproduct.widget.info.shampoo') }
+ } + { (mode === _Str_11214) && + <> +
{ LocalizeText('useproduct.widget.text.custompart', [ 'productName' ], [ furniData.name ] ) }
+
{ LocalizeText('useproduct.widget.info.custompart') }
+ } + { (mode === _Str_11733) && + <> +
{ LocalizeText('useproduct.widget.text.custompartshampoo', [ 'productName' ], [ furniData.name ] ) }
+
{ LocalizeText('useproduct.widget.info.custompartshampoo') }
+ } + { (mode === _Str_11369) && + <> +
{ LocalizeText('useproduct.widget.text.saddle', [ 'productName' ], [ furniData.name ] ) }
+
{ LocalizeText('useproduct.widget.info.saddle') }
+ } + { (mode === _Str_8759) && + <> +
{ LocalizeText('useproduct.widget.text.revive_monsterplant', [ 'productName' ], [ furniData.name ] ) }
+
{ LocalizeText('useproduct.widget.info.revive_monsterplant') }
+ } + { (mode === _Str_8432) && + <> +
{ LocalizeText('useproduct.widget.text.rebreed_monsterplant', [ 'productName' ], [ furniData.name ] ) }
+
{ LocalizeText('useproduct.widget.info.rebreed_monsterplant') }
+ } + { (mode === _Str_9653) && + <> +
{ LocalizeText('useproduct.widget.text.fertilize_monsterplant', [ 'productName' ], [ furniData.name ] ) }
+
{ LocalizeText('useproduct.widget.info.fertilize_monsterplant') }
+ } +
+
+ + +
+
+
) diff --git a/src/views/room/widgets/avatar-info/views/use-product/AvatarInfoUseProductView.tsx b/src/views/room/widgets/avatar-info/views/use-product/AvatarInfoUseProductView.tsx index f2e66720..aca532b0 100644 --- a/src/views/room/widgets/avatar-info/views/use-product/AvatarInfoUseProductView.tsx +++ b/src/views/room/widgets/avatar-info/views/use-product/AvatarInfoUseProductView.tsx @@ -20,7 +20,7 @@ const _Str_14611: number = 7; export const AvatarInfoUseProductView: FC = props => { - const { item = null, setConfirmingProduct = null, close = null } = props; + const { item = null, updateConfirmingProduct = null, close = null } = props; const [ mode, setMode ] = useState(0); const { roomSession = null } = useRoomContext(); @@ -64,30 +64,23 @@ export const AvatarInfoUseProductView: FC = props const processAction = useCallback((name: string) => { - let hideMenu = true; - - if(name) + if(!name) return; + + switch(name) { - switch(name) - { - case 'use_product': - case 'use_product_shampoo': - case 'use_product_custom_part': - case 'use_product_custom_part_shampoo': - case 'use_product_saddle': - case 'replace_product_saddle': - case 'revive_monsterplant': - case 'rebreed_monsterplant': - case 'fertilize_monsterplant': - setConfirmingProduct(item); - break; - default: - break; - } + case 'use_product': + case 'use_product_shampoo': + case 'use_product_custom_part': + case 'use_product_custom_part_shampoo': + case 'use_product_saddle': + case 'replace_product_saddle': + case 'revive_monsterplant': + case 'rebreed_monsterplant': + case 'fertilize_monsterplant': + updateConfirmingProduct(item); + break; } - - if(hideMenu) close(); - }, [ item, setConfirmingProduct, close ]); + }, [ item, updateConfirmingProduct ]); return ( diff --git a/src/views/room/widgets/avatar-info/views/use-product/AvatarInfoUseProductView.types.ts b/src/views/room/widgets/avatar-info/views/use-product/AvatarInfoUseProductView.types.ts index 1407ee5e..f759da68 100644 --- a/src/views/room/widgets/avatar-info/views/use-product/AvatarInfoUseProductView.types.ts +++ b/src/views/room/widgets/avatar-info/views/use-product/AvatarInfoUseProductView.types.ts @@ -1,9 +1,8 @@ -import { Dispatch, SetStateAction } from 'react'; import { UseProductItem } from '../../../../events'; export interface AvatarInfoUseProductViewProps { item: UseProductItem; - setConfirmingProduct: Dispatch>; + updateConfirmingProduct: (product: UseProductItem) => void; close: () => void; }