Add wired furni selector

This commit is contained in:
Bill 2021-06-30 02:06:21 -04:00
parent 06af222057
commit 663c84b8ca
10 changed files with 292 additions and 29 deletions

View File

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

View File

@ -1 +1,2 @@
export * from './WiredEvent'; export * from './WiredEvent';
export * from './WiredSelectObjectEvent';

View File

@ -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 { 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 { GetConnection, GetRoomEngine, GetSessionDataManager, IsOwnerOfFurniture } from '../../../api';
import { WiredSelectObjectEvent } from '../../../events';
import { dispatchUiEvent } from '../../../hooks/events';
import { LocalizeText } from '../../../utils/LocalizeText'; import { LocalizeText } from '../../../utils/LocalizeText';
import { RoomWidgetObjectNameEvent, RoomWidgetUpdateEvent, RoomWidgetUpdateInfostandFurniEvent, RoomWidgetUpdateInfostandPetEvent, RoomWidgetUpdateInfostandRentableBotEvent, RoomWidgetUpdateInfostandUserEvent } from '../events'; import { RoomWidgetObjectNameEvent, RoomWidgetUpdateEvent, RoomWidgetUpdateInfostandFurniEvent, RoomWidgetUpdateInfostandPetEvent, RoomWidgetUpdateInfostandRentableBotEvent, RoomWidgetUpdateInfostandUserEvent } from '../events';
import { RoomWidgetChangeMottoMessage, RoomWidgetFurniActionMessage, RoomWidgetMessage, RoomWidgetRoomObjectMessage, RoomWidgetUserActionMessage } from '../messages'; import { RoomWidgetChangeMottoMessage, RoomWidgetFurniActionMessage, RoomWidgetMessage, RoomWidgetRoomObjectMessage, RoomWidgetUserActionMessage } from '../messages';
@ -386,10 +388,7 @@ export class RoomWidgetInfostandHandler extends RoomWidgetHandler
event.rentCouldBeUsedForBuyout = furnitureData.rentCouldBeUsedForBuyout; event.rentCouldBeUsedForBuyout = furnitureData.rentCouldBeUsedForBuyout;
event.availableForBuildersClub = furnitureData.availableForBuildersClub; event.availableForBuildersClub = furnitureData.availableForBuildersClub;
// if(this._container.wiredService && (k.category === RoomObjectCategory.FLOOR)) dispatchUiEvent(new WiredSelectObjectEvent(event.id, event.category));
// {
// this._container.wiredService.selectFurniture(roomObject.id, furnitureData.name);
// }
} }
} }

View File

@ -13,7 +13,7 @@ export const WiredView: FC<WiredFurniSelectorViewProps> = props =>
const [ trigger, setTrigger ] = useState<Triggerable>(null); const [ trigger, setTrigger ] = useState<Triggerable>(null);
const [ intParams, setIntParams ] = useState<number[]>(null); const [ intParams, setIntParams ] = useState<number[]>(null);
const [ stringParam, setStringParam ] = useState<string>(null); const [ stringParam, setStringParam ] = useState<string>(null);
const [ furniIds, setFurniIds ] = useState<number[]>(null); const [ furniIds, setFurniIds ] = useState<number[]>([]);
const [ actionDelay, setActionDelay ] = useState<number>(null); const [ actionDelay, setActionDelay ] = useState<number>(null);
const wiredLayout = useMemo(() => const wiredLayout = useMemo(() =>

View File

@ -1,13 +1,6 @@
import { ConditionDefinition, TriggerDefinition, WiredActionDefinition } from 'nitro-renderer';
export class WiredFurniSelectorViewProps export class WiredFurniSelectorViewProps
{} {}
export class WiredLayoutViewProps
{
wiredDefinition: TriggerDefinition | WiredActionDefinition | ConditionDefinition;
}
export class WiredFurniType export class WiredFurniType
{ {
public static STUFF_SELECTION_OPTION_NONE: number = 0; public static STUFF_SELECTION_OPTION_NONE: number = 0;

View File

@ -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);
}
}
}

View File

@ -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 = [];
}
}

View File

@ -4,8 +4,10 @@ import { WiredEvent } from '../../../../events';
import { dispatchUiEvent } from '../../../../hooks/events'; import { dispatchUiEvent } from '../../../../hooks/events';
import { NitroCardContentView, NitroCardView } from '../../../../layout'; import { NitroCardContentView, NitroCardView } from '../../../../layout';
import { LocalizeText } from '../../../../utils/LocalizeText'; import { LocalizeText } from '../../../../utils/LocalizeText';
import { WiredSelectionVisualizer } from '../../common/WiredSelectionVisualizer';
import { useWiredContext } from '../../context/WiredContext'; import { useWiredContext } from '../../context/WiredContext';
import { WiredFurniType } from '../../WiredView.types'; import { WiredFurniType } from '../../WiredView.types';
import { WiredFurniSelectorView } from '../furni-selector/WiredFurniSelectorView';
import { WiredBaseViewProps } from './WiredBaseView.types'; import { WiredBaseViewProps } from './WiredBaseView.types';
export const WiredBaseView: FC<WiredBaseViewProps> = props => export const WiredBaseView: FC<WiredBaseViewProps> = props =>
@ -36,7 +38,19 @@ export const WiredBaseView: FC<WiredBaseViewProps> = props =>
setIntParams(trigger.intData); setIntParams(trigger.intData);
setStringParam(trigger.stringData); 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 ]); }, [ trigger, setIntParams, setStringParam, setFurniIds ]);
const onSave = useCallback(() => const onSave = useCallback(() =>
@ -52,10 +66,12 @@ export const WiredBaseView: FC<WiredBaseViewProps> = props =>
}, [ setTrigger ]); }, [ setTrigger ]);
return ( return (
<NitroCardView className={`nitro-wired nitro-wired-${wiredType} ` + (wiredType == 'trigger' ? 'rounded-0' : 'rounded-2')}> <NitroCardView className={`nitro-wired nitro-wired-${wiredType} ` + (wiredType === 'trigger' ? 'rounded-0' : 'rounded-2')}>
<div className="nitro-wired-header d-flex"> <div className="nitro-wired-header d-flex">
<div className="nitro-wired-title rounded-start w-100 drag-handler">{LocalizeText('wiredfurni.title')}</div> <div className="nitro-wired-title rounded-start w-100 drag-handler">{ LocalizeText('wiredfurni.title') }</div>
<div className="nitro-wired-close rounded-end flex-shrink-0" onClick={ close }><i className="fas fa-times" /></div> <div className="nitro-wired-close rounded-end flex-shrink-0" onClick={ close }>
<i className="fas fa-times" />
</div>
</div> </div>
<NitroCardContentView> <NitroCardContentView>
<div className="d-flex align-items-center"> <div className="d-flex align-items-center">
@ -69,11 +85,10 @@ export const WiredBaseView: FC<WiredBaseViewProps> = props =>
{ children } { children }
</> } </> }
</div> </div>
{ (requiresFurni !== WiredFurniType.STUFF_SELECTION_OPTION_NONE) && { (requiresFurni > WiredFurniType.STUFF_SELECTION_OPTION_NONE) &&
<> <>
<hr className="my-1 mb-2 bg-dark" /> <hr className="my-1 mb-2 bg-dark" />
<div className="fw-bold">{ LocalizeText('wiredfurni.pickfurnis.caption', [ 'count', 'limit' ], [ '0', trigger.maximumItemSelectionCount.toString() ]) }</div> <WiredFurniSelectorView />
<div>{ LocalizeText('wiredfurni.pickfurnis.desc') }</div>
</> } </> }
<div className="d-flex mt-3"> <div className="d-flex mt-3">
<button className="btn btn-sm btn-success me-2 w-100" onClick={ onSave }>{ LocalizeText('wiredfurni.ready') }</button> <button className="btn btn-sm btn-success me-2 w-100" onClick={ onSave }>{ LocalizeText('wiredfurni.ready') }</button>

View File

@ -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 { LocalizeText } from '../../../../utils/LocalizeText';
import { WiredSelectionVisualizer } from '../../common/WiredSelectionVisualizer';
import { useWiredContext } from '../../context/WiredContext';
import { WiredFurniSelectorViewProps } from './WiredFurniSelectorView.types'; import { WiredFurniSelectorViewProps } from './WiredFurniSelectorView.types';
export const WiredFurniSelectorView: FC<WiredFurniSelectorViewProps> = props => export const WiredFurniSelectorView: FC<WiredFurniSelectorViewProps> = 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 ( return (
<> <div className="d-flex flex-column">
<hr className="p-0 m-1" /> <div className="fw-bold">{ LocalizeText('wiredfurni.pickfurnis.caption', ['count', 'limit'], [ furniIds.length.toString(), trigger.maximumItemSelectionCount.toString() ]) }</div>
<div className="fw-bold">{ LocalizeText('wiredfurni.pickfurnis.caption', ['count', 'limit'], [selectedItemsCount.toString(), maximumItemsCount.toString()]) }</div> { LocalizeText('wiredfurni.pickfurnis.desc') }
<div>{ LocalizeText('wiredfurni.pickfurnis.desc') }</div> </div>
<hr className="p-0 m-1" />
</>
); );
} }

View File

@ -1,5 +1,3 @@
export class WiredFurniSelectorViewProps export class WiredFurniSelectorViewProps
{ {
selectedItemsCount: number;
maximumItemsCount: number;
} }