From 663c84b8ca34295f5711a663c6b75de456811506 Mon Sep 17 00:00:00 2001 From: Bill Date: Wed, 30 Jun 2021 02:06:21 -0400 Subject: [PATCH] Add wired furni selector --- src/events/wired/WiredSelectObjectEvent.ts | 27 ++++++ src/events/wired/index.ts | 1 + .../handlers/RoomWidgetInfostandHandler.ts | 7 +- src/views/wired/WiredView.tsx | 2 +- src/views/wired/WiredView.types.ts | 7 -- .../wired/common/WiredSelectionFilter.ts | 95 +++++++++++++++++++ .../wired/common/WiredSelectionVisualizer.ts | 68 +++++++++++++ src/views/wired/views/base/WiredBaseView.tsx | 29 ++++-- .../furni-selector/WiredFurniSelectorView.tsx | 83 ++++++++++++++-- .../WiredFurniSelectorView.types.ts | 2 - 10 files changed, 292 insertions(+), 29 deletions(-) create mode 100644 src/events/wired/WiredSelectObjectEvent.ts create mode 100644 src/views/wired/common/WiredSelectionFilter.ts create mode 100644 src/views/wired/common/WiredSelectionVisualizer.ts diff --git a/src/events/wired/WiredSelectObjectEvent.ts b/src/events/wired/WiredSelectObjectEvent.ts new file mode 100644 index 00000000..e82fc9d1 --- /dev/null +++ b/src/events/wired/WiredSelectObjectEvent.ts @@ -0,0 +1,27 @@ +import { WiredEvent } from './WiredEvent'; + +export class WiredSelectObjectEvent extends WiredEvent +{ + public static SELECT_OBJECT: string = 'WE_SELECT_OBJECT'; + + private _objectId: number; + private _category: number; + + constructor(objectId = -1, category = -1) + { + super(WiredSelectObjectEvent.SELECT_OBJECT); + + this._objectId = objectId; + this._category = category; + } + + public get objectId(): number + { + return this._objectId; + } + + public get category(): number + { + return this._category; + } +} diff --git a/src/events/wired/index.ts b/src/events/wired/index.ts index 9f43fa22..63ab5775 100644 --- a/src/events/wired/index.ts +++ b/src/events/wired/index.ts @@ -1 +1,2 @@ export * from './WiredEvent'; +export * from './WiredSelectObjectEvent'; diff --git a/src/views/room/handlers/RoomWidgetInfostandHandler.ts b/src/views/room/handlers/RoomWidgetInfostandHandler.ts index eda7705d..fc68071a 100644 --- a/src/views/room/handlers/RoomWidgetInfostandHandler.ts +++ b/src/views/room/handlers/RoomWidgetInfostandHandler.ts @@ -1,5 +1,7 @@ import { IFurnitureData, Nitro, NitroEvent, ObjectDataFactory, PetFigureData, PetType, RoomAdsUpdateComposer, RoomControllerLevel, RoomModerationSettings, RoomObjectCategory, RoomObjectOperationType, RoomObjectType, RoomObjectVariable, RoomSessionPetInfoUpdateEvent, RoomSessionUserBadgesEvent, RoomTradingLevelEnum, RoomUnitDropHandItemComposer, RoomUnitGiveHandItemComposer, RoomUnitGiveHandItemPetComposer, RoomUserData, RoomWidgetEnumItemExtradataParameter, SecurityLevel, Vector3d } from 'nitro-renderer'; import { GetConnection, GetRoomEngine, GetSessionDataManager, IsOwnerOfFurniture } from '../../../api'; +import { WiredSelectObjectEvent } from '../../../events'; +import { dispatchUiEvent } from '../../../hooks/events'; import { LocalizeText } from '../../../utils/LocalizeText'; import { RoomWidgetObjectNameEvent, RoomWidgetUpdateEvent, RoomWidgetUpdateInfostandFurniEvent, RoomWidgetUpdateInfostandPetEvent, RoomWidgetUpdateInfostandRentableBotEvent, RoomWidgetUpdateInfostandUserEvent } from '../events'; import { RoomWidgetChangeMottoMessage, RoomWidgetFurniActionMessage, RoomWidgetMessage, RoomWidgetRoomObjectMessage, RoomWidgetUserActionMessage } from '../messages'; @@ -386,10 +388,7 @@ export class RoomWidgetInfostandHandler extends RoomWidgetHandler event.rentCouldBeUsedForBuyout = furnitureData.rentCouldBeUsedForBuyout; event.availableForBuildersClub = furnitureData.availableForBuildersClub; - // if(this._container.wiredService && (k.category === RoomObjectCategory.FLOOR)) - // { - // this._container.wiredService.selectFurniture(roomObject.id, furnitureData.name); - // } + dispatchUiEvent(new WiredSelectObjectEvent(event.id, event.category)); } } diff --git a/src/views/wired/WiredView.tsx b/src/views/wired/WiredView.tsx index 96c9de19..e438dd67 100644 --- a/src/views/wired/WiredView.tsx +++ b/src/views/wired/WiredView.tsx @@ -13,7 +13,7 @@ export const WiredView: FC = props => const [ trigger, setTrigger ] = useState(null); const [ intParams, setIntParams ] = useState(null); const [ stringParam, setStringParam ] = useState(null); - const [ furniIds, setFurniIds ] = useState(null); + const [ furniIds, setFurniIds ] = useState([]); const [ actionDelay, setActionDelay ] = useState(null); const wiredLayout = useMemo(() => diff --git a/src/views/wired/WiredView.types.ts b/src/views/wired/WiredView.types.ts index aa76a13e..823d8fb3 100644 --- a/src/views/wired/WiredView.types.ts +++ b/src/views/wired/WiredView.types.ts @@ -1,13 +1,6 @@ -import { ConditionDefinition, TriggerDefinition, WiredActionDefinition } from 'nitro-renderer'; - export class WiredFurniSelectorViewProps {} -export class WiredLayoutViewProps -{ - wiredDefinition: TriggerDefinition | WiredActionDefinition | ConditionDefinition; -} - export class WiredFurniType { public static STUFF_SELECTION_OPTION_NONE: number = 0; diff --git a/src/views/wired/common/WiredSelectionFilter.ts b/src/views/wired/common/WiredSelectionFilter.ts new file mode 100644 index 00000000..0ae889df --- /dev/null +++ b/src/views/wired/common/WiredSelectionFilter.ts @@ -0,0 +1,95 @@ +import { ColorConverter, NitroFilter } from 'nitro-renderer'; + +const vertex = ` +attribute vec2 aVertexPosition; +attribute vec2 aTextureCoord; +uniform mat3 projectionMatrix; +varying vec2 vTextureCoord; +void main(void) +{ + gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0); + vTextureCoord = aTextureCoord; +}`; + +const fragment = ` +varying vec2 vTextureCoord; +uniform sampler2D uSampler; +uniform vec3 lineColor; +uniform vec3 color; +void main(void) { + vec4 currentColor = texture2D(uSampler, vTextureCoord); + vec3 colorLine = lineColor * currentColor.a; + vec3 colorOverlay = color * currentColor.a; + + if(currentColor.r == 0.0 && currentColor.g == 0.0 && currentColor.b == 0.0 && currentColor.a > 0.0) { + gl_FragColor = vec4(colorLine.r, colorLine.g, colorLine.b, currentColor.a); + } else if(currentColor.a > 0.0) { + gl_FragColor = vec4(colorOverlay.r, colorOverlay.g, colorOverlay.b, currentColor.a); + } +}`; + +export class WiredSelectionFilter extends NitroFilter +{ + private _lineColor: number; + private _color: number; + + constructor(lineColor: number | number[], color: number | number[]) + { + super(vertex, fragment); + + this.uniforms.lineColor = new Float32Array(3); + this.uniforms.color = new Float32Array(3); + this.lineColor = lineColor; + this.color = color; + } + + public get lineColor(): number | number[] + { + return this._lineColor; + } + + public set lineColor(value: number | number[]) + { + const arr = this.uniforms.lineColor; + + if(typeof value === 'number') + { + ColorConverter.hex2rgb(value, arr); + + this._lineColor = value; + } + else + { + arr[0] = value[0]; + arr[1] = value[1]; + arr[2] = value[2]; + + this._lineColor = ColorConverter.rgb2hex(arr); + } + } + + public get color(): number | number[] + { + return this._color; + } + + public set color(value: number | number[]) + { + const arr = this.uniforms.color; + + if(typeof value === 'number') + { + ColorConverter.hex2rgb(value, arr); + + this._color = value; + } + else + { + arr[0] = value[0]; + arr[1] = value[1]; + arr[2] = value[2]; + + this._color = ColorConverter.rgb2hex(arr); + } + } +} diff --git a/src/views/wired/common/WiredSelectionVisualizer.ts b/src/views/wired/common/WiredSelectionVisualizer.ts new file mode 100644 index 00000000..283c089a --- /dev/null +++ b/src/views/wired/common/WiredSelectionVisualizer.ts @@ -0,0 +1,68 @@ +import { IRoomObject, IRoomObjectSpriteVisualization, NitroFilter, RoomObjectCategory } from 'nitro-renderer'; +import { GetRoomEngine } from '../../../api'; +import { WiredSelectionFilter } from './WiredSelectionFilter'; + +export class WiredSelectionVisualizer +{ + private static _selectionShader: NitroFilter = new WiredSelectionFilter([ 1, 1, 1 ], [ 0.6, 0.6, 0.6 ]); + + public static show(furniId: number): void + { + WiredSelectionVisualizer.applySelectionShader(WiredSelectionVisualizer.getRoomObject(furniId)); + } + + public static hide(furniId: number): void + { + WiredSelectionVisualizer.clearSelectionShader(WiredSelectionVisualizer.getRoomObject(furniId)); + } + + public static clearSelectionShaderFromFurni(furniIds: number[]): void + { + for(const furniId of furniIds) + { + WiredSelectionVisualizer.clearSelectionShader(WiredSelectionVisualizer.getRoomObject(furniId)); + } + } + + public static applySelectionShaderToFurni(furniIds: number[]): void + { + for(const furniId of furniIds) + { + WiredSelectionVisualizer.applySelectionShader(WiredSelectionVisualizer.getRoomObject(furniId)); + } + } + + private static getRoomObject(objectId: number): IRoomObject + { + const roomEngine = GetRoomEngine(); + + return roomEngine.getRoomObject(roomEngine.activeRoomId, objectId, RoomObjectCategory.FLOOR); + } + + private static applySelectionShader(roomObject: IRoomObject): void + { + if(!roomObject) return; + + const visualization = (roomObject.visualization as IRoomObjectSpriteVisualization); + + if(!visualization) return; + + for(const sprite of visualization.sprites) + { + if(sprite.blendMode === 1) continue; // BLEND_MODE: ADD + + sprite.filters = [ WiredSelectionVisualizer._selectionShader ]; + } + } + + private static clearSelectionShader(roomObject: IRoomObject): void + { + if(!roomObject) return; + + const visualization = (roomObject.visualization as IRoomObjectSpriteVisualization); + + if(!visualization) return; + + for(const sprite of visualization.sprites) sprite.filters = []; + } +} diff --git a/src/views/wired/views/base/WiredBaseView.tsx b/src/views/wired/views/base/WiredBaseView.tsx index 105218a3..679eb070 100644 --- a/src/views/wired/views/base/WiredBaseView.tsx +++ b/src/views/wired/views/base/WiredBaseView.tsx @@ -4,8 +4,10 @@ import { WiredEvent } from '../../../../events'; import { dispatchUiEvent } from '../../../../hooks/events'; import { NitroCardContentView, NitroCardView } from '../../../../layout'; import { LocalizeText } from '../../../../utils/LocalizeText'; +import { WiredSelectionVisualizer } from '../../common/WiredSelectionVisualizer'; import { useWiredContext } from '../../context/WiredContext'; import { WiredFurniType } from '../../WiredView.types'; +import { WiredFurniSelectorView } from '../furni-selector/WiredFurniSelectorView'; import { WiredBaseViewProps } from './WiredBaseView.types'; export const WiredBaseView: FC = props => @@ -36,7 +38,19 @@ export const WiredBaseView: FC = props => setIntParams(trigger.intData); setStringParam(trigger.stringData); - setFurniIds(trigger.selectedItems); + setFurniIds(prevValue => + { + if(prevValue && prevValue.length) WiredSelectionVisualizer.clearSelectionShaderFromFurni(prevValue); + + if(trigger.selectedItems && trigger.selectedItems.length) + { + WiredSelectionVisualizer.applySelectionShaderToFurni(trigger.selectedItems); + + return trigger.selectedItems; + } + + return []; + }); }, [ trigger, setIntParams, setStringParam, setFurniIds ]); const onSave = useCallback(() => @@ -52,10 +66,12 @@ export const WiredBaseView: FC = props => }, [ setTrigger ]); return ( - +
-
{LocalizeText('wiredfurni.title')}
-
+
{ LocalizeText('wiredfurni.title') }
+
+ +
@@ -69,11 +85,10 @@ export const WiredBaseView: FC = props => { children } }
- { (requiresFurni !== WiredFurniType.STUFF_SELECTION_OPTION_NONE) && + { (requiresFurni > WiredFurniType.STUFF_SELECTION_OPTION_NONE) && <>
-
{ LocalizeText('wiredfurni.pickfurnis.caption', [ 'count', 'limit' ], [ '0', trigger.maximumItemSelectionCount.toString() ]) }
-
{ LocalizeText('wiredfurni.pickfurnis.desc') }
+ }
diff --git a/src/views/wired/views/furni-selector/WiredFurniSelectorView.tsx b/src/views/wired/views/furni-selector/WiredFurniSelectorView.tsx index a9b3a38e..aef71694 100644 --- a/src/views/wired/views/furni-selector/WiredFurniSelectorView.tsx +++ b/src/views/wired/views/furni-selector/WiredFurniSelectorView.tsx @@ -1,17 +1,84 @@ -import { FC } from 'react'; +import { FC, useCallback, useEffect } from 'react'; +import { WiredSelectObjectEvent } from '../../../../events'; +import { useUiEvent } from '../../../../hooks/events'; import { LocalizeText } from '../../../../utils/LocalizeText'; +import { WiredSelectionVisualizer } from '../../common/WiredSelectionVisualizer'; +import { useWiredContext } from '../../context/WiredContext'; import { WiredFurniSelectorViewProps } from './WiredFurniSelectorView.types'; export const WiredFurniSelectorView: FC = props => { - const { selectedItemsCount = null, maximumItemsCount = null } = props; + const { trigger = null, furniIds = [], setFurniIds = null } = useWiredContext(); + + const onWiredSelectObjectEvent = useCallback((event: WiredSelectObjectEvent) => + { + const furniId = event.objectId; + + if(furniId === -1) return; + + let newFurniIds: number[] = null; + + setFurniIds(prevValue => + { + newFurniIds = [ ...prevValue ]; + + const index = prevValue.indexOf(furniId); + + if(index >= 0) + { + newFurniIds.splice(index, 1); + + WiredSelectionVisualizer.hide(furniId); + } + else + { + if(newFurniIds.length < trigger.maximumItemSelectionCount) + { + newFurniIds.push(furniId); + + WiredSelectionVisualizer.show(furniId); + } + } + + return newFurniIds; + }); + }, [ trigger, setFurniIds ]); + + useUiEvent(WiredSelectObjectEvent.SELECT_OBJECT, onWiredSelectObjectEvent); + + useEffect(() => + { + if(!trigger) return; + + setFurniIds(prevValue => + { + if(prevValue && prevValue.length) WiredSelectionVisualizer.clearSelectionShaderFromFurni(prevValue); + + if(trigger.selectedItems && trigger.selectedItems.length) + { + WiredSelectionVisualizer.applySelectionShaderToFurni(trigger.selectedItems); + + return trigger.selectedItems; + } + + return []; + }); + + return () => + { + setFurniIds(prevValue => + { + WiredSelectionVisualizer.clearSelectionShaderFromFurni(prevValue); + + return []; + }); + } + }, [ trigger, setFurniIds ]); return ( - <> -
-
{ LocalizeText('wiredfurni.pickfurnis.caption', ['count', 'limit'], [selectedItemsCount.toString(), maximumItemsCount.toString()]) }
-
{ LocalizeText('wiredfurni.pickfurnis.desc') }
-
- +
+
{ LocalizeText('wiredfurni.pickfurnis.caption', ['count', 'limit'], [ furniIds.length.toString(), trigger.maximumItemSelectionCount.toString() ]) }
+ { LocalizeText('wiredfurni.pickfurnis.desc') } +
); } diff --git a/src/views/wired/views/furni-selector/WiredFurniSelectorView.types.ts b/src/views/wired/views/furni-selector/WiredFurniSelectorView.types.ts index 88144289..4bca7969 100644 --- a/src/views/wired/views/furni-selector/WiredFurniSelectorView.types.ts +++ b/src/views/wired/views/furni-selector/WiredFurniSelectorView.types.ts @@ -1,5 +1,3 @@ export class WiredFurniSelectorViewProps { - selectedItemsCount: number; - maximumItemsCount: number; }