diff --git a/src/assets/images/camera/base.png b/src/assets/images/camera/base.png deleted file mode 100644 index d6cf994d..00000000 Binary files a/src/assets/images/camera/base.png and /dev/null differ diff --git a/src/assets/images/camera/hud.png b/src/assets/images/camera/hud.png deleted file mode 100644 index ab6a9b24..00000000 Binary files a/src/assets/images/camera/hud.png and /dev/null differ diff --git a/src/assets/images/camera/snap.png b/src/assets/images/camera/snap.png deleted file mode 100644 index c3f57a04..00000000 Binary files a/src/assets/images/camera/snap.png and /dev/null differ diff --git a/src/assets/images/mannequin/background.png b/src/assets/images/mannequin/background.png deleted file mode 100644 index e3c824a6..00000000 Binary files a/src/assets/images/mannequin/background.png and /dev/null differ diff --git a/src/assets/images/mannequin/hc.png b/src/assets/images/mannequin/hc.png deleted file mode 100644 index 949f3d47..00000000 Binary files a/src/assets/images/mannequin/hc.png and /dev/null differ diff --git a/src/assets/images/room-widgets/camera-widget/camera-spritesheet.png b/src/assets/images/room-widgets/camera-widget/camera-spritesheet.png new file mode 100644 index 00000000..3d3f4184 Binary files /dev/null and b/src/assets/images/room-widgets/camera-widget/camera-spritesheet.png differ diff --git a/src/assets/images/camera/selected.png b/src/assets/images/room-widgets/camera-widget/selected.png similarity index 100% rename from src/assets/images/camera/selected.png rename to src/assets/images/room-widgets/camera-widget/selected.png diff --git a/src/assets/images/camera/selector.png b/src/assets/images/room-widgets/camera-widget/selector.png similarity index 100% rename from src/assets/images/camera/selector.png rename to src/assets/images/room-widgets/camera-widget/selector.png diff --git a/src/assets/images/room-widgets/engraving-lock-widget/engraving-lock-spritesheet.png b/src/assets/images/room-widgets/engraving-lock-widget/engraving-lock-spritesheet.png new file mode 100644 index 00000000..472dc85b Binary files /dev/null and b/src/assets/images/room-widgets/engraving-lock-widget/engraving-lock-spritesheet.png differ diff --git a/src/assets/images/room-widgets/mannequin-widget/mannequin-spritesheet.png b/src/assets/images/room-widgets/mannequin-widget/mannequin-spritesheet.png new file mode 100644 index 00000000..45e11f34 Binary files /dev/null and b/src/assets/images/room-widgets/mannequin-widget/mannequin-spritesheet.png differ diff --git a/src/assets/images/trophies/shine.png b/src/assets/images/trophies/shine.png deleted file mode 100644 index 4f42c70a..00000000 Binary files a/src/assets/images/trophies/shine.png and /dev/null differ diff --git a/src/events/index.ts b/src/events/index.ts index 81a2cdd4..70ffd6ca 100644 --- a/src/events/index.ts +++ b/src/events/index.ts @@ -3,3 +3,4 @@ export * from './catalog'; export * from './friend-list'; export * from './inventory'; export * from './navigator'; +export * from './room-widgets'; diff --git a/src/events/room-widgets/camera/RoomWidgetCameraEvent.ts b/src/events/room-widgets/camera/RoomWidgetCameraEvent.ts new file mode 100644 index 00000000..0eb6b4f0 --- /dev/null +++ b/src/events/room-widgets/camera/RoomWidgetCameraEvent.ts @@ -0,0 +1,13 @@ +import { NitroEvent } from 'nitro-renderer'; + +export class RoomWidgetCameraEvent extends NitroEvent +{ + public static SHOW_CAMERA: string = 'NE_SHOW_CAMERA'; + public static HIDE_CAMERA: string = 'NE_HIDE_CAMERA'; + public static TOGGLE_CAMERA: string = 'NE_TOGGLE_CAMERA'; + + constructor(type: string) + { + super(type); + } +} diff --git a/src/events/room-widgets/camera/index.ts b/src/events/room-widgets/camera/index.ts new file mode 100644 index 00000000..a04e80d0 --- /dev/null +++ b/src/events/room-widgets/camera/index.ts @@ -0,0 +1 @@ +export * from './RoomWidgetCameraEvent'; diff --git a/src/events/room-widgets/index.ts b/src/events/room-widgets/index.ts new file mode 100644 index 00000000..5eeca25b --- /dev/null +++ b/src/events/room-widgets/index.ts @@ -0,0 +1 @@ +export * from './camera'; diff --git a/src/views/catalog/views/page/layout/pets3/CatalogLayoutPets3View.tsx b/src/views/catalog/views/page/layout/pets3/CatalogLayoutPets3View.tsx index cc065c60..78239150 100644 --- a/src/views/catalog/views/page/layout/pets3/CatalogLayoutPets3View.tsx +++ b/src/views/catalog/views/page/layout/pets3/CatalogLayoutPets3View.tsx @@ -8,10 +8,10 @@ export const CatalogLayoutPets3View: FC = props => return (
-
-
-
-
+
+
+
+
diff --git a/src/views/room/RoomView.tsx b/src/views/room/RoomView.tsx index 381caa1b..99019599 100644 --- a/src/views/room/RoomView.tsx +++ b/src/views/room/RoomView.tsx @@ -7,6 +7,7 @@ import { DispatchTouchEvent } from '../../api/nitro/room/DispatchTouchEvent'; import { GetRoomEngine } from '../../api/nitro/room/GetRoomEngine'; import { RoomViewProps } from './RoomView.types'; import { AvatarInfoWidgetView } from './widgets/avatar-info/AvatarInfoWidgetView'; +import { CameraWidgetView } from './widgets/camera/CameraWidgetView'; import { ChatInputView } from './widgets/chat-input/ChatInputView'; import { ChatWidgetView } from './widgets/chat/ChatWidgetView'; import { FurnitureWidgetsView } from './widgets/furniture/FurnitureWidgetsView'; @@ -92,6 +93,7 @@ export function RoomView(props: RoomViewProps): JSX.Element createPortal(props.children, document.getElementById('room-view').appendChild(roomCanvas)) && <> + diff --git a/src/views/room/widgets/RoomWidgets.scss b/src/views/room/widgets/RoomWidgets.scss index 71970ddb..8b1fa8ca 100644 --- a/src/views/room/widgets/RoomWidgets.scss +++ b/src/views/room/widgets/RoomWidgets.scss @@ -1,3 +1,4 @@ +@import './camera/CameraWidgetView'; @import './chat/ChatWidgetView'; @import './chat-input/ChatInputView'; @import './furniture/FurnitureWidgets'; diff --git a/src/views/room/widgets/camera/CameraWidgetView.scss b/src/views/room/widgets/camera/CameraWidgetView.scss new file mode 100644 index 00000000..163f651b --- /dev/null +++ b/src/views/room/widgets/camera/CameraWidgetView.scss @@ -0,0 +1,24 @@ +.nitro-camera { + width: 340px; + height: 462px; + + background-image: url('../../../../assets/images/room-widgets/camera-widget/camera-spritesheet.png'); + + .camera-button { + width: 94px; + height: 94px; + cursor: pointer; + margin-top: 334px; + + background-image: url('../../../../assets/images/room-widgets/camera-widget/camera-spritesheet.png'); + background-position: -340px 0px; + + &:hover { + background-position: -340px -94px; + } + + &:active { + background-position: -340px -188px; + } + } +} diff --git a/src/views/room/widgets/camera/CameraWidgetView.tsx b/src/views/room/widgets/camera/CameraWidgetView.tsx new file mode 100644 index 00000000..9b53b7a7 --- /dev/null +++ b/src/views/room/widgets/camera/CameraWidgetView.tsx @@ -0,0 +1,57 @@ +import { FC, useCallback, useState } from 'react'; +import { RoomWidgetCameraEvent } from '../../../../events/room-widgets/camera/RoomWidgetCameraEvent'; +import { DraggableWindow } from '../../../../hooks/draggable-window/DraggableWindow'; +import { useUiEvent } from '../../../../hooks/events/ui/ui-event'; +import { CameraWidgetViewProps } from './CameraWidgetView.types'; + +export const CameraWidgetView: FC = props => +{ + const [ isVisible, setIsVisible ] = useState(false); + + const onRoomWidgetCameraEvent = useCallback((event: RoomWidgetCameraEvent) => + { + switch(event.type) + { + case RoomWidgetCameraEvent.SHOW_CAMERA: + setIsVisible(true); + return; + case RoomWidgetCameraEvent.HIDE_CAMERA: + setIsVisible(false); + return; + case RoomWidgetCameraEvent.TOGGLE_CAMERA: + setIsVisible(value => !value); + return; + } + }, []); + + useUiEvent(RoomWidgetCameraEvent.SHOW_CAMERA, onRoomWidgetCameraEvent); + useUiEvent(RoomWidgetCameraEvent.HIDE_CAMERA, onRoomWidgetCameraEvent); + useUiEvent(RoomWidgetCameraEvent.TOGGLE_CAMERA, onRoomWidgetCameraEvent); + + const processAction = useCallback((type: string, value: string = null) => + { + switch(type) + { + case 'close': + setIsVisible(false); + return; + } + }, []); + + if(!isVisible) return null; + + return ( + +
+
+
processAction('close') }> + +
+
+
+
+
+
+
+ ); +} diff --git a/src/views/room/widgets/camera/CameraWidgetView.types.ts b/src/views/room/widgets/camera/CameraWidgetView.types.ts new file mode 100644 index 00000000..e97e9b69 --- /dev/null +++ b/src/views/room/widgets/camera/CameraWidgetView.types.ts @@ -0,0 +1,2 @@ +export interface CameraWidgetViewProps +{} diff --git a/src/views/room/widgets/furniture/FurnitureWidgets.scss b/src/views/room/widgets/furniture/FurnitureWidgets.scss index d58c1b66..270dd3ec 100644 --- a/src/views/room/widgets/furniture/FurnitureWidgets.scss +++ b/src/views/room/widgets/furniture/FurnitureWidgets.scss @@ -1,3 +1,6 @@ +@import './engraving-lock/FurnitureEngravingLockView'; +@import './exchange-credit/FurnitureExchangeCreditView'; @import './manipulation-menu/FurnitureManipulationMenuView'; +@import './mannequin/FurnitureMannequinView'; @import './stickie/FurnitureStickieView'; @import './trophy/FurnitureTrophyView'; diff --git a/src/views/room/widgets/furniture/FurnitureWidgetsView.tsx b/src/views/room/widgets/furniture/FurnitureWidgetsView.tsx index 06959d8d..651c04a1 100644 --- a/src/views/room/widgets/furniture/FurnitureWidgetsView.tsx +++ b/src/views/room/widgets/furniture/FurnitureWidgetsView.tsx @@ -1,3 +1,4 @@ +import { FurnitureEngravingLockView } from './engraving-lock/FurnitureEngravingLockView'; import { FurnitureExchangeCreditView } from './exchange-credit/FurnitureExchangeCreditView'; import { FurnitureWidgetsViewProps } from './FurnitureWidgetsView.types'; import { FurnitureHighScoreView } from './high-score/FurnitureHighScoreView'; @@ -13,6 +14,7 @@ export function FurnitureWidgetsView(props: FurnitureWidgetsViewProps): JSX.Elem return (
+ diff --git a/src/views/room/widgets/furniture/engraving-lock/FurnitureEngravingLockData.ts b/src/views/room/widgets/furniture/engraving-lock/FurnitureEngravingLockData.ts new file mode 100644 index 00000000..7a58ecdc --- /dev/null +++ b/src/views/room/widgets/furniture/engraving-lock/FurnitureEngravingLockData.ts @@ -0,0 +1,10 @@ +export class FurnitureEngravingLockData +{ + constructor( + public objectId: number, + public category: number = 0, + public type: number = 0, + public usernames: string[] = [], + public figures: string[] = [], + public date: string = null) {} +} diff --git a/src/views/room/widgets/furniture/engraving-lock/FurnitureEngravingLockView.scss b/src/views/room/widgets/furniture/engraving-lock/FurnitureEngravingLockView.scss new file mode 100644 index 00000000..eb6d8987 --- /dev/null +++ b/src/views/room/widgets/furniture/engraving-lock/FurnitureEngravingLockView.scss @@ -0,0 +1,76 @@ +.nitro-engraving-lock { + width: 300px; + + .engraving-lock-stage-1 { + width: 31px; + height: 39px; + background-position: -380px -43px; + background-image: url('../../../../../assets/images/room-widgets/engraving-lock-widget/engraving-lock-spritesheet.png'); + } + + .engraving-lock-stage-2 { + width: 36px; + height: 43px; + background-position: -375px 0px; + background-image: url('../../../../../assets/images/room-widgets/engraving-lock-widget/engraving-lock-spritesheet.png'); + } +} + +.nitro-engraving-lock-view { + width: 375px; + height: 210px; + background-position: 0px 0px; + background-image: url('../../../../../assets/images/room-widgets/engraving-lock-widget/engraving-lock-spritesheet.png'); + + color: #622e54; + font-weight: bold; + font-size: 16px; + text-shadow: 0px 1px white; + + &.engraving-lock-3 { + background-position: 0px -210px; + color: #614110; + } + + &.engraving-lock-4 { + background-position: 0px -420px; + color: #f1dcc8; + text-shadow: 0px 2px rgba(0, 0, 0, .4); + + .engraving-lock-avatar { + margin-bottom: 10px; + } + } + + .engraving-lock-close { + position: absolute; + cursor: pointer; + width: 15px; + height: 15px; + top: 34px; + right: 27px; + } + + .engraving-lock-avatar { + width: 70px; + height: 120px; + + div { + position: absolute; + margin-top: -5px; + } + + &:nth-child(1) { + div { + margin-left: -10px; + } + } + + &:nth-child(2) { + div { + margin-left: -15px; + } + } + } + +} diff --git a/src/views/room/widgets/furniture/engraving-lock/FurnitureEngravingLockView.tsx b/src/views/room/widgets/furniture/engraving-lock/FurnitureEngravingLockView.tsx new file mode 100644 index 00000000..ad6eda9b --- /dev/null +++ b/src/views/room/widgets/furniture/engraving-lock/FurnitureEngravingLockView.tsx @@ -0,0 +1,149 @@ +import { LoveLockFurniFinishedEvent, LoveLockFurniFriendConfirmedEvent, LoveLockFurniStartEvent, LoveLockStartConfirmComposer, NitroEvent, RoomEngineTriggerWidgetEvent, RoomObjectVariable } from 'nitro-renderer'; +import { FC, useCallback, useState } from 'react'; +import { GetRoomSession } from '../../../../../api'; +import { GetRoomEngine } from '../../../../../api/nitro/room/GetRoomEngine'; +import { DraggableWindow } from '../../../../../hooks/draggable-window/DraggableWindow'; +import { CreateEventDispatcherHook } from '../../../../../hooks/events/event-dispatcher.base'; +import { useRoomEngineEvent } from '../../../../../hooks/events/nitro/room/room-engine-event'; +import { CreateMessageHook } from '../../../../../hooks/messages/message-event'; +import { NitroCardContentView } from '../../../../../layout/card/content/NitroCardContentView'; +import { NitroCardHeaderView } from '../../../../../layout/card/header/NitroCardHeaderView'; +import { NitroCardView } from '../../../../../layout/card/NitroCardView'; +import { LocalizeText } from '../../../../../utils/LocalizeText'; +import { AvatarImageView } from '../../../../avatar-image/AvatarImageView'; +import { RoomWidgetRoomObjectUpdateEvent } from '../../events/RoomWidgetRoomObjectUpdateEvent'; +import { FurnitureEngravingLockData } from './FurnitureEngravingLockData'; +import { FurnitureEngravingLockViewProps } from './FurnitureEngravingLockView.types'; + +export const FurnitureEngravingLockView: FC = props => +{ + const [ engravingLockData, setEngravingLockData ] = useState(null); + const [ engravingStage, setEngravingStage ] = useState(0); + + const onNitroEvent = (event: NitroEvent) => + { + switch(event.type) + { + case RoomEngineTriggerWidgetEvent.REQUEST_FRIEND_FURNITURE_ENGRAVING: { + const widgetEvent = (event as RoomEngineTriggerWidgetEvent); + + const roomObject = GetRoomEngine().getRoomObject(widgetEvent.roomId, widgetEvent.objectId, widgetEvent.category); + + if(!roomObject) return; + + const data = roomObject.model.getValue(RoomObjectVariable.FURNITURE_DATA); + const type = roomObject.model.getValue(RoomObjectVariable.FURNITURE_FRIENDFURNI_ENGRAVING) + + if(data[0] === '1') + { + if(data.length !== 6) return; + + setEngravingLockData(new FurnitureEngravingLockData(widgetEvent.objectId, widgetEvent.category, type, [ data[1], data[2] ], [ data[3], data[4] ], data[5])); + } + return; + } + case RoomWidgetRoomObjectUpdateEvent.FURNI_REMOVED: { + const widgetEvent = (event as RoomWidgetRoomObjectUpdateEvent); + + setEngravingLockData(prevState => + { + if(!prevState || (widgetEvent.id !== prevState.objectId) || (widgetEvent.category !== prevState.category)) return prevState; + + return null; + }); + return; + } + } + }; + + useRoomEngineEvent(RoomEngineTriggerWidgetEvent.REQUEST_FRIEND_FURNITURE_ENGRAVING, onNitroEvent); + CreateEventDispatcherHook(RoomWidgetRoomObjectUpdateEvent.FURNI_REMOVED, props.events, onNitroEvent); + + const onLoveLockFurniStartEvent = useCallback((event: LoveLockFurniStartEvent) => + { + const parser = event.getParser(); + + setEngravingLockData(new FurnitureEngravingLockData(parser.furniId)); + + if(parser.start) + setEngravingStage(1); + else + setEngravingStage(2); + + }, [ engravingStage ]); + + const onLoveLockDoneEvent = useCallback((event: LoveLockFurniFinishedEvent | LoveLockFurniFriendConfirmedEvent) => + { + processAction('close_request'); + }, []); + + CreateMessageHook(LoveLockFurniStartEvent, onLoveLockFurniStartEvent); + CreateMessageHook(LoveLockFurniFinishedEvent, onLoveLockDoneEvent); + CreateMessageHook(LoveLockFurniFriendConfirmedEvent, onLoveLockDoneEvent); + + const processAction = useCallback((type: string, value: string = null) => + { + switch(type) + { + case 'close_view': + setEngravingLockData(null); + return; + case 'accept_request': + GetRoomSession().connection.send(new LoveLockStartConfirmComposer(engravingLockData.objectId, true)); + return; + case 'reject_request': + GetRoomSession().connection.send(new LoveLockStartConfirmComposer(engravingLockData.objectId, false)); + processAction('close_request'); + return; + case 'close_request': + setEngravingStage(0); + setEngravingLockData(null); + return; + } + }, [ engravingLockData ]); + + return ( + <> + { engravingStage > 0 && + processAction('close_request') } /> + +
+ { LocalizeText('friend.furniture.confirm.lock.subtitle') } +
+
+
+
+ { engravingStage === 2 &&
{ LocalizeText('friend.furniture.confirm.lock.other.locked') }
} +
+ + +
+
+
} + { engravingLockData && engravingLockData.usernames.length > 0 && +
+
processAction('close_view') }>
+
+
+ +
+
+ +
+
+
+
+ { engravingLockData.type === 0 && LocalizeText('lovelock.engraving.caption') } + { engravingLockData.type === 3 && LocalizeText('wildwest.engraving.caption') } +
+
{ engravingLockData.date }
+
+
{ engravingLockData.usernames[0] }
+
{ engravingLockData.usernames[1] }
+
+
+
+
} + + ); +} diff --git a/src/views/room/widgets/furniture/engraving-lock/FurnitureEngravingLockView.types.ts b/src/views/room/widgets/furniture/engraving-lock/FurnitureEngravingLockView.types.ts new file mode 100644 index 00000000..b5d92bae --- /dev/null +++ b/src/views/room/widgets/furniture/engraving-lock/FurnitureEngravingLockView.types.ts @@ -0,0 +1,4 @@ +import { FurnitureWidgetProps } from '../FurnitureWidget.types'; + +export interface FurnitureEngravingLockViewProps extends FurnitureWidgetProps +{} diff --git a/src/views/room/widgets/furniture/exchange-credit/FurnitureExchangeCreditView.scss b/src/views/room/widgets/furniture/exchange-credit/FurnitureExchangeCreditView.scss new file mode 100644 index 00000000..d7665d50 --- /dev/null +++ b/src/views/room/widgets/furniture/exchange-credit/FurnitureExchangeCreditView.scss @@ -0,0 +1,3 @@ +.nitro-exchange-credit { + width: 250px; +} diff --git a/src/views/room/widgets/furniture/exchange-credit/FurnitureExchangeCreditView.tsx b/src/views/room/widgets/furniture/exchange-credit/FurnitureExchangeCreditView.tsx index 414e5ac0..2055134e 100644 --- a/src/views/room/widgets/furniture/exchange-credit/FurnitureExchangeCreditView.tsx +++ b/src/views/room/widgets/furniture/exchange-credit/FurnitureExchangeCreditView.tsx @@ -23,6 +23,8 @@ export const FurnitureExchangeCreditView: FC = pro const roomObject = GetRoomEngine().getRoomObject(widgetEvent.roomId, widgetEvent.objectId, widgetEvent.category); + if(!roomObject) return; + const value = roomObject.model.getValue(RoomObjectVariable.FURNITURE_CREDIT_VALUE); setExchangeCreditData(new FurnitureExchangeCreditData(widgetEvent.objectId, widgetEvent.category, value)); @@ -63,7 +65,7 @@ export const FurnitureExchangeCreditView: FC = pro if(!exchangeCreditData) return null; return ( - + processAction('close') } />
diff --git a/src/views/room/widgets/furniture/high-score/FurnitureHighScoreView.tsx b/src/views/room/widgets/furniture/high-score/FurnitureHighScoreView.tsx index 6d77fe7b..222d39a4 100644 --- a/src/views/room/widgets/furniture/high-score/FurnitureHighScoreView.tsx +++ b/src/views/room/widgets/furniture/high-score/FurnitureHighScoreView.tsx @@ -1,17 +1,17 @@ -import { RoomEngineObjectEvent, RoomEngineTriggerWidgetEvent } from 'nitro-renderer'; +import { NitroEvent, RoomEngineTriggerWidgetEvent } from 'nitro-renderer'; import { FC } from 'react'; import { useRoomEngineEvent } from '../../../../../hooks/events/nitro/room/room-engine-event'; import { FurnitureHighScoreViewProps } from './FurnitureHighScoreView.types'; export const FurnitureHighScoreView: FC = props => { - const onRoomEngineObjectEvent = (event: RoomEngineObjectEvent) => + const onNitroEvent = (event: NitroEvent) => { console.log(event); }; - useRoomEngineEvent(RoomEngineTriggerWidgetEvent.REQUEST_HIGH_SCORE_DISPLAY, onRoomEngineObjectEvent); - useRoomEngineEvent(RoomEngineTriggerWidgetEvent.REQUEST_HIDE_HIGH_SCORE_DISPLAY, onRoomEngineObjectEvent); + useRoomEngineEvent(RoomEngineTriggerWidgetEvent.REQUEST_HIGH_SCORE_DISPLAY, onNitroEvent); + useRoomEngineEvent(RoomEngineTriggerWidgetEvent.REQUEST_HIDE_HIGH_SCORE_DISPLAY, onNitroEvent); return null; } diff --git a/src/views/room/widgets/furniture/mannequin/FurnitureMannequinData.ts b/src/views/room/widgets/furniture/mannequin/FurnitureMannequinData.ts new file mode 100644 index 00000000..10928618 --- /dev/null +++ b/src/views/room/widgets/furniture/mannequin/FurnitureMannequinData.ts @@ -0,0 +1,11 @@ +export class FurnitureMannequinData +{ + constructor( + public objectId: number, + public category: number, + public name: string, + public figure: string, + public gender: string, + public clubLevel: number, + public renderedFigure: string = null) {} +} diff --git a/src/views/room/widgets/furniture/mannequin/FurnitureMannequinView.scss b/src/views/room/widgets/furniture/mannequin/FurnitureMannequinView.scss index e69de29b..cc16ce4c 100644 --- a/src/views/room/widgets/furniture/mannequin/FurnitureMannequinView.scss +++ b/src/views/room/widgets/furniture/mannequin/FurnitureMannequinView.scss @@ -0,0 +1,9 @@ +.nitro-mannequin { + width: 350px; + + .mannequin-preview { + width: 83px; + height: 130px; + background-image: url('../../../../../assets/images/room-widgets/mannequin-widget/mannequin-spritesheet.png'); + } +} diff --git a/src/views/room/widgets/furniture/mannequin/FurnitureMannequinView.tsx b/src/views/room/widgets/furniture/mannequin/FurnitureMannequinView.tsx index 8c2d8e17..45ff3cad 100644 --- a/src/views/room/widgets/furniture/mannequin/FurnitureMannequinView.tsx +++ b/src/views/room/widgets/furniture/mannequin/FurnitureMannequinView.tsx @@ -1,16 +1,218 @@ -import { RoomEngineObjectEvent, RoomEngineTriggerWidgetEvent } from 'nitro-renderer'; -import { FC } from 'react'; +import { AvatarFigurePartType, FurnitureMannequinSaveLookComposer, FurnitureMannequinSaveNameComposer, FurnitureMultiStateComposer, IAvatarFigureContainer, Nitro, NitroEvent, RoomEngineTriggerWidgetEvent, RoomObjectVariable } from 'nitro-renderer'; +import { FC, KeyboardEvent, useCallback, useEffect, useState } from 'react'; +import { GetRoomEngine } from '../../../../../api/nitro/room/GetRoomEngine'; +import { GetRoomSession } from '../../../../../api/nitro/session/GetRoomSession'; +import { GetSessionDataManager } from '../../../../../api/nitro/session/GetSessionDataManager'; +import { CreateEventDispatcherHook } from '../../../../../hooks/events/event-dispatcher.base'; import { useRoomEngineEvent } from '../../../../../hooks/events/nitro/room/room-engine-event'; -import { FurnitureMannequinViewProps } from './FurnitureMannequinView.types'; +import { NitroCardContentView } from '../../../../../layout/card/content/NitroCardContentView'; +import { NitroCardHeaderView } from '../../../../../layout/card/header/NitroCardHeaderView'; +import { NitroCardView } from '../../../../../layout/card/NitroCardView'; +import { LocalizeText } from '../../../../../utils/LocalizeText'; +import { AvatarImageView } from '../../../../avatar-image/AvatarImageView'; +import { RoomWidgetRoomObjectUpdateEvent } from '../../events'; +import { FurnitureMannequinData } from './FurnitureMannequinData'; +import { FurnitureMannequinViewMode, FurnitureMannequinViewProps } from './FurnitureMannequinView.types'; export const FurnitureMannequinView: FC = props => { - const onRoomEngineObjectEvent = (event: RoomEngineObjectEvent) => + const parts = [ + AvatarFigurePartType.CHEST_ACCESSORY, + AvatarFigurePartType.COAT_CHEST, + AvatarFigurePartType.CHEST, + AvatarFigurePartType.LEGS, + AvatarFigurePartType.SHOES, + AvatarFigurePartType.WAIST_ACCESSORY + ]; + const baseAvatar = ['hd', 99999, 99998]; + + const [ mannequinData, setMannequinData ] = useState(null); + const [ viewMode, setViewMode ] = useState(''); + + useEffect(() => { - console.log(event); + if(mannequinData && !mannequinData.renderedFigure) + { + const figureContainer = Nitro.instance.avatar.createFigureContainer(mannequinData.figure); + loadMannequinFigure(figureContainer); + } + }, [ mannequinData ]); + + const onNitroEvent = useCallback((event: NitroEvent) => + { + switch(event.type) + { + case RoomEngineTriggerWidgetEvent.REQUEST_MANNEQUIN: { + const widgetEvent = (event as RoomEngineTriggerWidgetEvent); + + const roomObject = GetRoomEngine().getRoomObject(widgetEvent.roomId, widgetEvent.objectId, widgetEvent.category); + + if(!roomObject) return; + + const figure = roomObject.model.getValue(RoomObjectVariable.FURNITURE_MANNEQUIN_FIGURE); + const gender = roomObject.model.getValue(RoomObjectVariable.FURNITURE_MANNEQUIN_GENDER); + const name = roomObject.model.getValue(RoomObjectVariable.FURNITURE_MANNEQUIN_NAME); + + const figureContainer = Nitro.instance.avatar.createFigureContainer(figure); + const clubLevel = Nitro.instance.avatar.getFigureClubLevel(figureContainer, gender, parts); + + const mannequinData = new FurnitureMannequinData(widgetEvent.objectId, widgetEvent.category, name, figure, gender, clubLevel); + + setMannequinData(mannequinData); + loadViewMode(mannequinData); + return; + } + case RoomWidgetRoomObjectUpdateEvent.FURNI_REMOVED: { + const widgetEvent = (event as RoomWidgetRoomObjectUpdateEvent); + + setMannequinData(prevState => + { + if(!prevState || (widgetEvent.id !== prevState.objectId) || (widgetEvent.category !== prevState.category)) return prevState; + + return null; + }); + return; + } + } + }, []); + + useRoomEngineEvent(RoomEngineTriggerWidgetEvent.REQUEST_MANNEQUIN, onNitroEvent); + CreateEventDispatcherHook(RoomWidgetRoomObjectUpdateEvent.FURNI_REMOVED, props.events, onNitroEvent); + + const loadMannequinFigure = useCallback((figureContainer: IAvatarFigureContainer) => + { + for(const item of figureContainer.getPartTypeIds()) + { + if(parts.indexOf(item) == -1) + { + figureContainer.removePart(item); + } + } + + figureContainer.updatePart(baseAvatar[0].toString(), Number(baseAvatar[1]), [ Number(baseAvatar[2]) ]); + + setMannequinData(mannequinData => new FurnitureMannequinData(mannequinData.objectId, mannequinData.category, mannequinData.name, mannequinData.figure, mannequinData.gender, mannequinData.clubLevel, figureContainer.getFigureString())); + }, []); + + const loadViewMode = useCallback((mannequinData: FurnitureMannequinData) => + { + if(!mannequinData) return; + + const userCanEdit = (GetRoomSession().isRoomOwner || GetSessionDataManager().isModerator); + const userGender = Nitro.instance.sessionDataManager.gender; + const userClubLevel = Nitro.instance.sessionDataManager.clubLevel; + + if(userCanEdit) + { + setViewMode(FurnitureMannequinViewMode.EDIT); + } + else + { + if(!mannequinData.figure || mannequinData.figure.length <= 1) return; + + if(userGender.toUpperCase() !== mannequinData.gender.toUpperCase()) + { + setViewMode(FurnitureMannequinViewMode.INCOMPATIBLE_GENDER); + } + else if(userClubLevel < mannequinData.clubLevel) + { + setViewMode(FurnitureMannequinViewMode.CLUB); + } + else + { + setViewMode(FurnitureMannequinViewMode.DEFAULT); + } + } + }, []); + + const processAction = useCallback((type: string, value: string = null) => + { + switch(type) + { + case 'close': + setMannequinData(null); + return; + case 'set_name': + setMannequinData(mannequinData => new FurnitureMannequinData(mannequinData.objectId, mannequinData.category, value, mannequinData.figure, mannequinData.gender, mannequinData.clubLevel, mannequinData.renderedFigure)); + return; + case 'load_figure': + loadMannequinFigure(Nitro.instance.avatar.createFigureContainer(Nitro.instance.sessionDataManager.figure)); + setViewMode(FurnitureMannequinViewMode.SAVE); + return; + case 'back': + loadMannequinFigure(Nitro.instance.avatar.createFigureContainer(mannequinData.figure)); + setViewMode(FurnitureMannequinViewMode.EDIT); + return; + case 'save_name': + GetRoomSession().connection.send(new FurnitureMannequinSaveNameComposer(mannequinData.objectId, mannequinData.name)); + return; + case 'save_figure': + GetRoomSession().connection.send(new FurnitureMannequinSaveLookComposer(mannequinData.objectId)); + processAction('save_name'); + processAction('close'); + return; + case 'wear': + GetRoomSession().connection.send(new FurnitureMultiStateComposer(mannequinData.objectId)); + processAction('close'); + return; + } + }, [ mannequinData ]); + + const handleKeyDown = (event: KeyboardEvent) => + { + if(event.key !== 'Enter') return; + + processAction('save_name'); }; - useRoomEngineEvent(RoomEngineTriggerWidgetEvent.REQUEST_MANNEQUIN, onRoomEngineObjectEvent); + if(!mannequinData) return null; - return null; + return ( + + processAction('close') } /> + +
+
+
+ +
+
+
+ { viewMode === FurnitureMannequinViewMode.EDIT && <> + processAction('set_name', event.target.value) } onKeyDown={ event => handleKeyDown(event) } /> +
processAction('load_figure') }>{ LocalizeText('mannequin.widget.style') }
+
processAction('wear') }>{ LocalizeText('mannequin.widget.wear') }
+ } + { viewMode === FurnitureMannequinViewMode.SAVE && <> +
+
+
{ mannequinData.name }
+
{ LocalizeText('mannequin.widget.savetext') }
+
+
+
processAction('back') }>{ LocalizeText('mannequin.widget.back') }
+
processAction('save_figure') }>{ LocalizeText('mannequin.widget.save') }
+
+
+ } + { viewMode === FurnitureMannequinViewMode.DEFAULT && <> +
+
+
{ mannequinData.name }
+
{ LocalizeText('mannequin.widget.weartext') }
+
+
processAction('wear') }>{ LocalizeText('mannequin.widget.wear') }
+
+ } + { viewMode === FurnitureMannequinViewMode.CLUB && <> +
{ LocalizeText('mannequin.widget.clubnotification') }
+ } + { viewMode === FurnitureMannequinViewMode.INCOMPATIBLE_GENDER && <> +
{ LocalizeText('mannequin.widget.wronggender') }
+ } +
+
+
+
+ ); } diff --git a/src/views/room/widgets/furniture/mannequin/FurnitureMannequinView.types.tsx b/src/views/room/widgets/furniture/mannequin/FurnitureMannequinView.types.tsx index b4e5e0ff..eae1f80c 100644 --- a/src/views/room/widgets/furniture/mannequin/FurnitureMannequinView.types.tsx +++ b/src/views/room/widgets/furniture/mannequin/FurnitureMannequinView.types.tsx @@ -1,6 +1,13 @@ import { FurnitureWidgetProps } from '../FurnitureWidget.types'; export interface FurnitureMannequinViewProps extends FurnitureWidgetProps -{ +{} +export class FurnitureMannequinViewMode +{ + public static readonly EDIT: string = 'edit'; + public static readonly SAVE: string = 'save'; + public static readonly CLUB: string = 'club'; + public static readonly DEFAULT: string = 'default'; + public static readonly INCOMPATIBLE_GENDER: string = 'incompatible_gender'; } diff --git a/src/views/room/widgets/furniture/present/FurniturePresentView.tsx b/src/views/room/widgets/furniture/present/FurniturePresentView.tsx index 7b3616b3..97714245 100644 --- a/src/views/room/widgets/furniture/present/FurniturePresentView.tsx +++ b/src/views/room/widgets/furniture/present/FurniturePresentView.tsx @@ -1,16 +1,16 @@ -import { RoomEngineObjectEvent, RoomEngineTriggerWidgetEvent } from 'nitro-renderer'; +import { NitroEvent, RoomEngineTriggerWidgetEvent } from 'nitro-renderer'; import { FC } from 'react'; import { useRoomEngineEvent } from '../../../../../hooks/events/nitro/room/room-engine-event'; import { FurniturePresentViewProps } from './FurniturePresentView.types'; export const FurniturePresentView: FC = props => { - const onRoomEngineObjectEvent = (event: RoomEngineObjectEvent) => + const onNitroEvent = (event: NitroEvent) => { console.log(event); }; - useRoomEngineEvent(RoomEngineTriggerWidgetEvent.REQUEST_PRESENT, onRoomEngineObjectEvent); + useRoomEngineEvent(RoomEngineTriggerWidgetEvent.REQUEST_PRESENT, onNitroEvent); return null; } diff --git a/src/views/toolbar/ToolbarView.tsx b/src/views/toolbar/ToolbarView.tsx index 49e85ce4..fa8ccedb 100644 --- a/src/views/toolbar/ToolbarView.tsx +++ b/src/views/toolbar/ToolbarView.tsx @@ -1,7 +1,8 @@ import { UserInfoEvent } from 'nitro-renderer/src/nitro/communication/messages/incoming/user/data/UserInfoEvent'; import { UserInfoDataParser } from 'nitro-renderer/src/nitro/communication/messages/parser/user/data/UserInfoDataParser'; -import { useCallback, useState } from 'react'; +import { FC, useCallback, useState } from 'react'; import { AvatarEditorEvent, CatalogEvent, FriendListEvent, InventoryEvent, NavigatorEvent } from '../../events'; +import { RoomWidgetCameraEvent } from '../../events/room-widgets/camera/RoomWidgetCameraEvent'; import { dispatchUiEvent } from '../../hooks/events/ui/ui-event'; import { CreateMessageHook } from '../../hooks/messages/message-event'; import { TransitionAnimation } from '../../transitions/TransitionAnimation'; @@ -10,7 +11,7 @@ import { AvatarImageView } from '../avatar-image/AvatarImageView'; import { ToolbarMeView } from './me/ToolbarMeView'; import { ToolbarViewItems, ToolbarViewProps } from './ToolbarView.types'; -export function ToolbarView(props: ToolbarViewProps): JSX.Element +export const ToolbarView: FC = props => { const { isInRoom } = props; @@ -44,8 +45,12 @@ export function ToolbarView(props: ToolbarViewProps): JSX.Element case ToolbarViewItems.FRIEND_LIST_ITEM: dispatchUiEvent(new CatalogEvent(FriendListEvent.TOGGLE_FRIEND_LIST)); return; + case ToolbarViewItems.CAMERA_ITEM: + dispatchUiEvent(new RoomWidgetCameraEvent(RoomWidgetCameraEvent.TOGGLE_CAMERA)); + return; case ToolbarViewItems.CLOTHING_ITEM: dispatchUiEvent(new AvatarEditorEvent(AvatarEditorEvent.TOGGLE_EDITOR)); + setMeExpanded(false); return; } }, []); @@ -100,6 +105,10 @@ export function ToolbarView(props: ToolbarViewProps): JSX.Element { (unseenFriendListCount > 0) && (
{ unseenFriendListCount }
) }
+ { isInRoom && ( +
handleToolbarItemClick(ToolbarViewItems.CAMERA_ITEM) }> + +
) }
diff --git a/src/views/toolbar/ToolbarView.types.ts b/src/views/toolbar/ToolbarView.types.ts index b0b97068..6c280e6c 100644 --- a/src/views/toolbar/ToolbarView.types.ts +++ b/src/views/toolbar/ToolbarView.types.ts @@ -10,4 +10,5 @@ export class ToolbarViewItems public static CATALOG_ITEM: string = 'TVI_CATALOG_ITEM'; public static FRIEND_LIST_ITEM: string = 'TVI_FRIEND_LIST_ITEM'; public static CLOTHING_ITEM: string = 'TVI_CLOTHING_ITEM'; + public static CAMERA_ITEM: string = 'TVI_CAMERA_ITEM'; } diff --git a/src/views/toolbar/me/ToolbarMeView.tsx b/src/views/toolbar/me/ToolbarMeView.tsx index 48564dfd..31cc71ab 100644 --- a/src/views/toolbar/me/ToolbarMeView.tsx +++ b/src/views/toolbar/me/ToolbarMeView.tsx @@ -1,9 +1,9 @@ import { MouseEventType } from 'nitro-renderer'; -import { useEffect, useRef } from 'react'; +import { FC, useEffect, useRef } from 'react'; import { ToolbarViewItems } from '../ToolbarView.types'; import { ToolbarMeViewProps } from './ToolbarMeView.types'; -export function ToolbarMeView(props: ToolbarMeViewProps): JSX.Element +export const ToolbarMeView: FC = props => { const { setMeExpanded = null, handleToolbarItemClick = null } = props;