mirror of
https://github.com/duckietm/Nitro-V3.git
synced 2026-06-20 15:36:18 +00:00
Merge pull request #13 from Lorenzune/pr/wired-freeze-furni-movement-ui-clean-20260318
feat(wired-ui): add freeze and furni movement action views
This commit is contained in:
@@ -32,6 +32,9 @@ export class WiredActionLayoutCode
|
|||||||
public static USERS_AREA_SELECTOR: number = 31;
|
public static USERS_AREA_SELECTOR: number = 31;
|
||||||
public static USERS_NEIGHBORHOOD_SELECTOR: number = 32;
|
public static USERS_NEIGHBORHOOD_SELECTOR: number = 32;
|
||||||
public static SEND_SIGNAL: number = 33;
|
public static SEND_SIGNAL: number = 33;
|
||||||
public static SET_ALTITUDE: number = 39;
|
public static FREEZE: number = 34;
|
||||||
public static RELATIVE_MOVE: number = 40;
|
public static UNFREEZE: number = 35;
|
||||||
|
public static FURNI_TO_USER: number = 36;
|
||||||
|
public static USER_TO_FURNI: number = 37;
|
||||||
|
public static FURNI_TO_FURNI: number = 38;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,25 +3,42 @@ import { GetRoomEngine, IRoomObject, IRoomObjectSpriteVisualization, RoomObjectC
|
|||||||
export class WiredSelectionVisualizer
|
export class WiredSelectionVisualizer
|
||||||
{
|
{
|
||||||
private static _selectionShader: WiredFilter = new WiredFilter({
|
private static _selectionShader: WiredFilter = new WiredFilter({
|
||||||
lineColor: [ 1, 1, 1 ],
|
lineColor: [ 0.45, 0.95, 0.55 ],
|
||||||
color: [ 0.6, 0.6, 0.6 ]
|
color: [ 0.18, 0.78, 0.30 ]
|
||||||
|
});
|
||||||
|
private static _secondarySelectionShader: WiredFilter = new WiredFilter({
|
||||||
|
lineColor: [ 0.45, 0.78, 1 ],
|
||||||
|
color: [ 0.20, 0.52, 0.95 ]
|
||||||
});
|
});
|
||||||
|
|
||||||
public static show(furniId: number): void
|
public static show(furniId: number): void
|
||||||
{
|
{
|
||||||
WiredSelectionVisualizer.applySelectionShader(WiredSelectionVisualizer.getRoomObject(furniId));
|
WiredSelectionVisualizer.applySelectionShader(WiredSelectionVisualizer.getRoomObject(furniId), WiredSelectionVisualizer._selectionShader);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static hide(furniId: number): void
|
public static hide(furniId: number): void
|
||||||
{
|
{
|
||||||
WiredSelectionVisualizer.clearSelectionShader(WiredSelectionVisualizer.getRoomObject(furniId));
|
const roomObject = WiredSelectionVisualizer.getRoomObject(furniId);
|
||||||
|
|
||||||
|
WiredSelectionVisualizer.clearSelectionShader(roomObject, WiredSelectionVisualizer._selectionShader);
|
||||||
|
WiredSelectionVisualizer.clearSelectionShader(roomObject, WiredSelectionVisualizer._secondarySelectionShader);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static showSecondary(furniId: number): void
|
||||||
|
{
|
||||||
|
WiredSelectionVisualizer.applySelectionShader(WiredSelectionVisualizer.getRoomObject(furniId), WiredSelectionVisualizer._secondarySelectionShader);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static hideSecondary(furniId: number): void
|
||||||
|
{
|
||||||
|
WiredSelectionVisualizer.clearSelectionShader(WiredSelectionVisualizer.getRoomObject(furniId), WiredSelectionVisualizer._secondarySelectionShader);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static clearSelectionShaderFromFurni(furniIds: number[]): void
|
public static clearSelectionShaderFromFurni(furniIds: number[]): void
|
||||||
{
|
{
|
||||||
for(const furniId of furniIds)
|
for(const furniId of furniIds)
|
||||||
{
|
{
|
||||||
WiredSelectionVisualizer.clearSelectionShader(WiredSelectionVisualizer.getRoomObject(furniId));
|
WiredSelectionVisualizer.clearSelectionShader(WiredSelectionVisualizer.getRoomObject(furniId), WiredSelectionVisualizer._selectionShader);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -29,7 +46,39 @@ export class WiredSelectionVisualizer
|
|||||||
{
|
{
|
||||||
for(const furniId of furniIds)
|
for(const furniId of furniIds)
|
||||||
{
|
{
|
||||||
WiredSelectionVisualizer.applySelectionShader(WiredSelectionVisualizer.getRoomObject(furniId));
|
WiredSelectionVisualizer.applySelectionShader(WiredSelectionVisualizer.getRoomObject(furniId), WiredSelectionVisualizer._selectionShader);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static clearSecondarySelectionShaderFromFurni(furniIds: number[]): void
|
||||||
|
{
|
||||||
|
for(const furniId of furniIds)
|
||||||
|
{
|
||||||
|
WiredSelectionVisualizer.clearSelectionShader(WiredSelectionVisualizer.getRoomObject(furniId), WiredSelectionVisualizer._secondarySelectionShader);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static applySecondarySelectionShaderToFurni(furniIds: number[]): void
|
||||||
|
{
|
||||||
|
for(const furniId of furniIds)
|
||||||
|
{
|
||||||
|
WiredSelectionVisualizer.applySelectionShader(WiredSelectionVisualizer.getRoomObject(furniId), WiredSelectionVisualizer._secondarySelectionShader);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static clearAllSelectionShaders(): void
|
||||||
|
{
|
||||||
|
const roomEngine = GetRoomEngine();
|
||||||
|
const roomId = roomEngine.activeRoomId;
|
||||||
|
|
||||||
|
if(roomId < 0) return;
|
||||||
|
|
||||||
|
const roomObjects = roomEngine.getRoomObjects(roomId, RoomObjectCategory.FLOOR);
|
||||||
|
|
||||||
|
for(const roomObject of roomObjects)
|
||||||
|
{
|
||||||
|
WiredSelectionVisualizer.clearSelectionShader(roomObject, WiredSelectionVisualizer._selectionShader);
|
||||||
|
WiredSelectionVisualizer.clearSelectionShader(roomObject, WiredSelectionVisualizer._secondarySelectionShader);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -40,7 +89,7 @@ export class WiredSelectionVisualizer
|
|||||||
return roomEngine.getRoomObject(roomEngine.activeRoomId, objectId, RoomObjectCategory.FLOOR);
|
return roomEngine.getRoomObject(roomEngine.activeRoomId, objectId, RoomObjectCategory.FLOOR);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static applySelectionShader(roomObject: IRoomObject): void
|
private static applySelectionShader(roomObject: IRoomObject, filter: WiredFilter): void
|
||||||
{
|
{
|
||||||
if(!roomObject) return;
|
if(!roomObject) return;
|
||||||
|
|
||||||
@@ -54,13 +103,15 @@ export class WiredSelectionVisualizer
|
|||||||
|
|
||||||
if(!sprite.filters) sprite.filters = [];
|
if(!sprite.filters) sprite.filters = [];
|
||||||
|
|
||||||
sprite.filters.push(WiredSelectionVisualizer._selectionShader);
|
if(sprite.filters.includes(filter)) continue;
|
||||||
|
|
||||||
|
sprite.filters.push(filter);
|
||||||
|
|
||||||
sprite.increaseUpdateCounter();
|
sprite.increaseUpdateCounter();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static clearSelectionShader(roomObject: IRoomObject): void
|
private static clearSelectionShader(roomObject: IRoomObject, filter: WiredFilter): void
|
||||||
{
|
{
|
||||||
if(!roomObject) return;
|
if(!roomObject) return;
|
||||||
|
|
||||||
@@ -72,7 +123,7 @@ export class WiredSelectionVisualizer
|
|||||||
{
|
{
|
||||||
if(!sprite.filters) continue;
|
if(!sprite.filters) continue;
|
||||||
|
|
||||||
const index = sprite.filters.indexOf(WiredSelectionVisualizer._selectionShader);
|
const index = sprite.filters.indexOf(filter);
|
||||||
|
|
||||||
if(index >= 0)
|
if(index >= 0)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -24,7 +24,11 @@ export const WiredBaseView: FC<PropsWithChildren<WiredBaseViewProps>> = props =>
|
|||||||
const [ needsSave, setNeedsSave ] = useState<boolean>(false);
|
const [ needsSave, setNeedsSave ] = useState<boolean>(false);
|
||||||
const { trigger = null, setTrigger = null, setIntParams = null, setStringParam = null, setFurniIds = null, setAllowsFurni = null, saveWired = null } = useWired();
|
const { trigger = null, setTrigger = null, setIntParams = null, setStringParam = null, setFurniIds = null, setAllowsFurni = null, saveWired = null } = useWired();
|
||||||
|
|
||||||
const onClose = () => setTrigger(null);
|
const onClose = () =>
|
||||||
|
{
|
||||||
|
WiredSelectionVisualizer.clearAllSelectionShaders();
|
||||||
|
setTrigger(null);
|
||||||
|
};
|
||||||
|
|
||||||
const onSave = () =>
|
const onSave = () =>
|
||||||
{
|
{
|
||||||
@@ -48,6 +52,8 @@ export const WiredBaseView: FC<PropsWithChildren<WiredBaseViewProps>> = props =>
|
|||||||
{
|
{
|
||||||
if(!trigger) return;
|
if(!trigger) return;
|
||||||
|
|
||||||
|
WiredSelectionVisualizer.clearAllSelectionShaders();
|
||||||
|
|
||||||
const spriteId = (trigger.spriteId || -1);
|
const spriteId = (trigger.spriteId || -1);
|
||||||
const furniData = GetSessionDataManager().getFloorItemData(spriteId);
|
const furniData = GetSessionDataManager().getFloorItemData(spriteId);
|
||||||
|
|
||||||
|
|||||||
@@ -16,45 +16,66 @@ export const USER_SOURCES = [
|
|||||||
{ value: 201, label: 'wiredfurni.params.sources.users.201' }
|
{ value: 201, label: 'wiredfurni.params.sources.users.201' }
|
||||||
];
|
];
|
||||||
|
|
||||||
|
export interface WiredSourceOption
|
||||||
|
{
|
||||||
|
value: number;
|
||||||
|
label: string;
|
||||||
|
}
|
||||||
|
|
||||||
interface WiredSourcesSelectorProps
|
interface WiredSourcesSelectorProps
|
||||||
{
|
{
|
||||||
showFurni?: boolean;
|
showFurni?: boolean;
|
||||||
showUsers?: boolean;
|
showUsers?: boolean;
|
||||||
furniSource?: number;
|
furniSource?: number;
|
||||||
userSource?: number;
|
userSource?: number;
|
||||||
|
furniTitle?: string;
|
||||||
|
usersTitle?: string;
|
||||||
|
furniSources?: WiredSourceOption[];
|
||||||
|
userSources?: WiredSourceOption[];
|
||||||
onChangeFurni?: (source: number) => void;
|
onChangeFurni?: (source: number) => void;
|
||||||
onChangeUsers?: (source: number) => void;
|
onChangeUsers?: (source: number) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const WiredSourcesSelector: FC<WiredSourcesSelectorProps> = props =>
|
export const WiredSourcesSelector: FC<WiredSourcesSelectorProps> = props =>
|
||||||
{
|
{
|
||||||
const { showFurni = false, showUsers = false, furniSource = 0, userSource = 0, onChangeFurni = null, onChangeUsers = null } = props;
|
const {
|
||||||
|
showFurni = false,
|
||||||
|
showUsers = false,
|
||||||
|
furniSource = 0,
|
||||||
|
userSource = 0,
|
||||||
|
furniTitle = 'wiredfurni.params.sources.furni.title',
|
||||||
|
usersTitle = 'wiredfurni.params.sources.users.title',
|
||||||
|
furniSources = FURNI_SOURCES,
|
||||||
|
userSources = USER_SOURCES,
|
||||||
|
onChangeFurni = null,
|
||||||
|
onChangeUsers = null
|
||||||
|
} = props;
|
||||||
|
|
||||||
const furniIndex = Math.max(0, FURNI_SOURCES.findIndex(s => s.value === furniSource));
|
const furniIndex = Math.max(0, furniSources.findIndex(s => s.value === furniSource));
|
||||||
const userIndex = Math.max(0, USER_SOURCES.findIndex(s => s.value === userSource));
|
const userIndex = Math.max(0, userSources.findIndex(s => s.value === userSource));
|
||||||
|
|
||||||
const prevFurni = () =>
|
const prevFurni = () =>
|
||||||
{
|
{
|
||||||
const next = (furniIndex - 1 + FURNI_SOURCES.length) % FURNI_SOURCES.length;
|
const next = (furniIndex - 1 + furniSources.length) % furniSources.length;
|
||||||
onChangeFurni && onChangeFurni(FURNI_SOURCES[next].value);
|
onChangeFurni && onChangeFurni(furniSources[next].value);
|
||||||
};
|
};
|
||||||
|
|
||||||
const nextFurni = () =>
|
const nextFurni = () =>
|
||||||
{
|
{
|
||||||
const next = (furniIndex + 1) % FURNI_SOURCES.length;
|
const next = (furniIndex + 1) % furniSources.length;
|
||||||
onChangeFurni && onChangeFurni(FURNI_SOURCES[next].value);
|
onChangeFurni && onChangeFurni(furniSources[next].value);
|
||||||
};
|
};
|
||||||
|
|
||||||
const prevUsers = () =>
|
const prevUsers = () =>
|
||||||
{
|
{
|
||||||
const next = (userIndex - 1 + USER_SOURCES.length) % USER_SOURCES.length;
|
const next = (userIndex - 1 + userSources.length) % userSources.length;
|
||||||
onChangeUsers && onChangeUsers(USER_SOURCES[next].value);
|
onChangeUsers && onChangeUsers(userSources[next].value);
|
||||||
};
|
};
|
||||||
|
|
||||||
const nextUsers = () =>
|
const nextUsers = () =>
|
||||||
{
|
{
|
||||||
const next = (userIndex + 1) % USER_SOURCES.length;
|
const next = (userIndex + 1) % userSources.length;
|
||||||
onChangeUsers && onChangeUsers(USER_SOURCES[next].value);
|
onChangeUsers && onChangeUsers(userSources[next].value);
|
||||||
};
|
};
|
||||||
|
|
||||||
if(!showFurni && !showUsers) return null;
|
if(!showFurni && !showUsers) return null;
|
||||||
@@ -63,11 +84,11 @@ export const WiredSourcesSelector: FC<WiredSourcesSelectorProps> = props =>
|
|||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
{ showFurni &&
|
{ showFurni &&
|
||||||
<>
|
<>
|
||||||
<Text bold>{ LocalizeText('wiredfurni.params.sources.furni.title') }</Text>
|
<Text bold>{ LocalizeText(furniTitle) }</Text>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<Button variant="primary" className="px-2 py-1" onClick={ prevFurni }><FaChevronLeft /></Button>
|
<Button variant="primary" className="px-2 py-1" onClick={ prevFurni }><FaChevronLeft /></Button>
|
||||||
<div className="flex flex-1 items-center justify-center">
|
<div className="flex flex-1 items-center justify-center">
|
||||||
<Text small>{ LocalizeText(FURNI_SOURCES[furniIndex].label) }</Text>
|
<Text small>{ LocalizeText(furniSources[furniIndex].label) }</Text>
|
||||||
</div>
|
</div>
|
||||||
<Button variant="primary" className="px-2 py-1" onClick={ nextFurni }><FaChevronRight /></Button>
|
<Button variant="primary" className="px-2 py-1" onClick={ nextFurni }><FaChevronRight /></Button>
|
||||||
</div>
|
</div>
|
||||||
@@ -77,11 +98,11 @@ export const WiredSourcesSelector: FC<WiredSourcesSelectorProps> = props =>
|
|||||||
|
|
||||||
{ showUsers &&
|
{ showUsers &&
|
||||||
<>
|
<>
|
||||||
<Text bold>{ LocalizeText('wiredfurni.params.sources.users.title') }</Text>
|
<Text bold>{ LocalizeText(usersTitle) }</Text>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<Button variant="primary" className="px-2 py-1" onClick={ prevUsers }><FaChevronLeft /></Button>
|
<Button variant="primary" className="px-2 py-1" onClick={ prevUsers }><FaChevronLeft /></Button>
|
||||||
<div className="flex flex-1 items-center justify-center">
|
<div className="flex flex-1 items-center justify-center">
|
||||||
<Text small>{ LocalizeText(USER_SOURCES[userIndex].label) }</Text>
|
<Text small>{ LocalizeText(userSources[userIndex].label) }</Text>
|
||||||
</div>
|
</div>
|
||||||
<Button variant="primary" className="px-2 py-1" onClick={ nextUsers }><FaChevronRight /></Button>
|
<Button variant="primary" className="px-2 py-1" onClick={ nextUsers }><FaChevronRight /></Button>
|
||||||
</div>
|
</div>
|
||||||
@@ -89,4 +110,3 @@ export const WiredSourcesSelector: FC<WiredSourcesSelectorProps> = props =>
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,54 @@
|
|||||||
|
import { FC, useEffect, useState } from 'react';
|
||||||
|
import { LocalizeText, WiredFurniType } from '../../../../api';
|
||||||
|
import { Text } from '../../../../common';
|
||||||
|
import { useWired } from '../../../../hooks';
|
||||||
|
import { WiredActionBaseView } from './WiredActionBaseView';
|
||||||
|
import { WiredSourcesSelector } from '../WiredSourcesSelector';
|
||||||
|
|
||||||
|
const EFFECT_OPTIONS = [
|
||||||
|
{ value: 218, label: 'fx_218' },
|
||||||
|
{ value: 12, label: 'fx_12' },
|
||||||
|
{ value: 11, label: 'fx_11' },
|
||||||
|
{ value: 53, label: 'fx_53' },
|
||||||
|
{ value: 163, label: 'fx_163' }
|
||||||
|
];
|
||||||
|
|
||||||
|
export const WiredActionFreezeView: FC<{}> = () =>
|
||||||
|
{
|
||||||
|
const [ effectId, setEffectId ] = useState(218);
|
||||||
|
const [ cancelOnTeleport, setCancelOnTeleport ] = useState(false);
|
||||||
|
const [ userSource, setUserSource ] = useState(0);
|
||||||
|
const { trigger = null, setIntParams = null } = useWired();
|
||||||
|
|
||||||
|
const save = () => setIntParams([
|
||||||
|
effectId,
|
||||||
|
cancelOnTeleport ? 1 : 0,
|
||||||
|
userSource
|
||||||
|
]);
|
||||||
|
|
||||||
|
useEffect(() =>
|
||||||
|
{
|
||||||
|
setEffectId((trigger?.intData?.length > 0) ? trigger.intData[0] : 218);
|
||||||
|
setCancelOnTeleport((trigger?.intData?.length > 1) ? (trigger.intData[1] === 1) : false);
|
||||||
|
setUserSource((trigger?.intData?.length > 2) ? trigger.intData[2] : 0);
|
||||||
|
}, [ trigger ]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<WiredActionBaseView
|
||||||
|
hasSpecialInput={ true }
|
||||||
|
requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE }
|
||||||
|
save={ save }
|
||||||
|
footer={ <WiredSourcesSelector showUsers={ true } userSource={ userSource } onChangeUsers={ setUserSource } /> }>
|
||||||
|
<div className="flex flex-col gap-1">
|
||||||
|
<Text bold>Effect</Text>
|
||||||
|
<select className="form-select form-select-sm" value={ effectId } onChange={ event => setEffectId(parseInt(event.target.value)) }>
|
||||||
|
{ EFFECT_OPTIONS.map(option => <option key={ option.value } value={ option.value }>{ LocalizeText(option.label) }</option>) }
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-1">
|
||||||
|
<input checked={ cancelOnTeleport } className="form-check-input" id="freezeCancelOnTeleport" type="checkbox" onChange={ event => setCancelOnTeleport(event.target.checked) } />
|
||||||
|
<Text>{ LocalizeText('wiredfurni.params.freeze.cancel_on_teleport') }</Text>
|
||||||
|
</div>
|
||||||
|
</WiredActionBaseView>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -0,0 +1,222 @@
|
|||||||
|
import { FC, useCallback, useEffect, useRef, useState } from 'react';
|
||||||
|
import { LocalizeText, WiredFurniType, WiredSelectionVisualizer } from '../../../../api';
|
||||||
|
import { Button, Text } from '../../../../common';
|
||||||
|
import { useWired } from '../../../../hooks';
|
||||||
|
import { WiredSourcesSelector, FURNI_SOURCES, WiredSourceOption } from '../WiredSourcesSelector';
|
||||||
|
import { WiredActionBaseView } from './WiredActionBaseView';
|
||||||
|
|
||||||
|
const SOURCE_TRIGGER = 0;
|
||||||
|
const SOURCE_SELECTED = 100;
|
||||||
|
const SOURCE_SECONDARY_SELECTED = 101;
|
||||||
|
const FURNI_DELIMITER = ';';
|
||||||
|
|
||||||
|
const TARGET_FURNI_SOURCES: WiredSourceOption[] = [
|
||||||
|
{ value: 0, label: 'wiredfurni.params.sources.furni.0' },
|
||||||
|
{ value: SOURCE_SECONDARY_SELECTED, label: 'wiredfurni.params.sources.furni.101' },
|
||||||
|
{ value: 200, label: 'wiredfurni.params.sources.furni.200' },
|
||||||
|
{ value: 201, label: 'wiredfurni.params.sources.furni.201' }
|
||||||
|
];
|
||||||
|
|
||||||
|
type SelectionMode = 'move' | 'target';
|
||||||
|
|
||||||
|
const parseIds = (data: string): number[] =>
|
||||||
|
{
|
||||||
|
if(!data || !data.length) return [];
|
||||||
|
|
||||||
|
const ids = new Set<number>();
|
||||||
|
|
||||||
|
for(const part of data.split(/[;,\t]/))
|
||||||
|
{
|
||||||
|
const trimmed = part.trim();
|
||||||
|
if(!trimmed.length) continue;
|
||||||
|
|
||||||
|
const value = parseInt(trimmed, 10);
|
||||||
|
if(!isNaN(value) && value > 0) ids.add(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Array.from(ids);
|
||||||
|
};
|
||||||
|
|
||||||
|
const serializeIds = (ids: number[]): string =>
|
||||||
|
{
|
||||||
|
if(!ids || !ids.length) return '';
|
||||||
|
|
||||||
|
return ids.filter(id => (id > 0)).join(FURNI_DELIMITER);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const WiredActionFurniToFurniView: FC<{}> = () =>
|
||||||
|
{
|
||||||
|
const [ moveSource, setMoveSource ] = useState<number>(SOURCE_TRIGGER);
|
||||||
|
const [ targetSource, setTargetSource ] = useState<number>(SOURCE_TRIGGER);
|
||||||
|
const [ moveFurniIds, setMoveFurniIds ] = useState<number[]>([]);
|
||||||
|
const [ targetFurniIds, setTargetFurniIds ] = useState<number[]>([]);
|
||||||
|
const [ selectionMode, setSelectionMode ] = useState<SelectionMode>('move');
|
||||||
|
|
||||||
|
const highlightedIds = useRef<number[]>([]);
|
||||||
|
|
||||||
|
const { trigger = null, furniIds = [], setFurniIds, setIntParams, setStringParam, setAllowsFurni } = useWired();
|
||||||
|
|
||||||
|
const syncHighlights = useCallback((nextMoveIds: number[], nextTargetIds: number[]) =>
|
||||||
|
{
|
||||||
|
if(highlightedIds.current.length)
|
||||||
|
{
|
||||||
|
WiredSelectionVisualizer.clearSelectionShaderFromFurni(highlightedIds.current);
|
||||||
|
WiredSelectionVisualizer.clearSecondarySelectionShaderFromFurni(highlightedIds.current);
|
||||||
|
}
|
||||||
|
|
||||||
|
const targetSet = new Set(nextTargetIds);
|
||||||
|
const moveOnlyIds = nextMoveIds.filter(id => !targetSet.has(id));
|
||||||
|
|
||||||
|
if(moveOnlyIds.length) WiredSelectionVisualizer.applySelectionShaderToFurni(moveOnlyIds);
|
||||||
|
if(nextTargetIds.length) WiredSelectionVisualizer.applySecondarySelectionShaderToFurni(nextTargetIds);
|
||||||
|
|
||||||
|
highlightedIds.current = Array.from(new Set([ ...nextMoveIds, ...nextTargetIds ]));
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const switchSelection = useCallback((mode: SelectionMode) =>
|
||||||
|
{
|
||||||
|
const canEditMove = (moveSource === SOURCE_SELECTED);
|
||||||
|
const canEditTarget = (targetSource === SOURCE_SECONDARY_SELECTED);
|
||||||
|
|
||||||
|
if(mode === 'move' && !canEditMove) return;
|
||||||
|
if(mode === 'target' && !canEditTarget) return;
|
||||||
|
|
||||||
|
setSelectionMode(mode);
|
||||||
|
setFurniIds([ ...(mode === 'move' ? moveFurniIds : targetFurniIds) ]);
|
||||||
|
}, [ moveSource, targetSource, moveFurniIds, targetFurniIds, setFurniIds ]);
|
||||||
|
|
||||||
|
useEffect(() =>
|
||||||
|
{
|
||||||
|
if(!trigger) return;
|
||||||
|
|
||||||
|
const nextMoveIds = trigger.selectedItems ?? [];
|
||||||
|
const nextTargetIds = parseIds(trigger.stringData);
|
||||||
|
const nextMoveSource = (trigger.intData.length >= 1)
|
||||||
|
? trigger.intData[0]
|
||||||
|
: (nextMoveIds.length ? SOURCE_SELECTED : SOURCE_TRIGGER);
|
||||||
|
const nextTargetSourceRaw = (trigger.intData.length >= 2)
|
||||||
|
? trigger.intData[1]
|
||||||
|
: (nextTargetIds.length ? SOURCE_SECONDARY_SELECTED : SOURCE_TRIGGER);
|
||||||
|
const nextTargetSource = (nextTargetSourceRaw === SOURCE_SELECTED) ? SOURCE_SECONDARY_SELECTED : nextTargetSourceRaw;
|
||||||
|
|
||||||
|
setMoveSource(nextMoveSource);
|
||||||
|
setTargetSource(nextTargetSource);
|
||||||
|
setMoveFurniIds(nextMoveIds);
|
||||||
|
setTargetFurniIds(nextTargetIds);
|
||||||
|
setSelectionMode('move');
|
||||||
|
setFurniIds([ ...nextMoveIds ]);
|
||||||
|
}, [ trigger, setFurniIds ]);
|
||||||
|
|
||||||
|
useEffect(() =>
|
||||||
|
{
|
||||||
|
if(selectionMode === 'move') setMoveFurniIds(furniIds);
|
||||||
|
else setTargetFurniIds(furniIds);
|
||||||
|
}, [ furniIds, selectionMode ]);
|
||||||
|
|
||||||
|
useEffect(() =>
|
||||||
|
{
|
||||||
|
syncHighlights(moveFurniIds, targetFurniIds);
|
||||||
|
}, [ moveFurniIds, targetFurniIds, syncHighlights ]);
|
||||||
|
|
||||||
|
useEffect(() =>
|
||||||
|
{
|
||||||
|
const canEditMove = (moveSource === SOURCE_SELECTED);
|
||||||
|
const canEditTarget = (targetSource === SOURCE_SECONDARY_SELECTED);
|
||||||
|
|
||||||
|
if(selectionMode === 'move' && !canEditMove && canEditTarget)
|
||||||
|
{
|
||||||
|
switchSelection('target');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(selectionMode === 'target' && !canEditTarget && canEditMove)
|
||||||
|
{
|
||||||
|
switchSelection('move');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const canEditCurrent = ((selectionMode === 'move') ? canEditMove : canEditTarget);
|
||||||
|
setAllowsFurni(canEditCurrent ? WiredFurniType.STUFF_SELECTION_OPTION_BY_ID : WiredFurniType.STUFF_SELECTION_OPTION_NONE);
|
||||||
|
}, [ selectionMode, moveSource, targetSource, switchSelection, setAllowsFurni ]);
|
||||||
|
|
||||||
|
useEffect(() =>
|
||||||
|
{
|
||||||
|
return () =>
|
||||||
|
{
|
||||||
|
if(!highlightedIds.current.length) return;
|
||||||
|
|
||||||
|
WiredSelectionVisualizer.clearSelectionShaderFromFurni(highlightedIds.current);
|
||||||
|
WiredSelectionVisualizer.clearSecondarySelectionShaderFromFurni(highlightedIds.current);
|
||||||
|
highlightedIds.current = [];
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const save = useCallback(() =>
|
||||||
|
{
|
||||||
|
if(selectionMode === 'target')
|
||||||
|
{
|
||||||
|
setSelectionMode('move');
|
||||||
|
setFurniIds([ ...moveFurniIds ]);
|
||||||
|
}
|
||||||
|
|
||||||
|
setIntParams([
|
||||||
|
moveSource,
|
||||||
|
targetSource
|
||||||
|
]);
|
||||||
|
|
||||||
|
setStringParam(serializeIds(targetFurniIds));
|
||||||
|
}, [ selectionMode, moveFurniIds, moveSource, targetSource, targetFurniIds, setFurniIds, setIntParams, setStringParam ]);
|
||||||
|
|
||||||
|
const selectionLimit = trigger?.maximumItemSelectionCount ?? 0;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<WiredActionBaseView
|
||||||
|
hasSpecialInput={ true }
|
||||||
|
requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_BY_ID }
|
||||||
|
save={ save }
|
||||||
|
footer={
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
<WiredSourcesSelector
|
||||||
|
showFurni={ true }
|
||||||
|
furniTitle="wiredfurni.params.sources.furni.title.mv.0"
|
||||||
|
furniSources={ FURNI_SOURCES }
|
||||||
|
furniSource={ moveSource }
|
||||||
|
onChangeFurni={ setMoveSource } />
|
||||||
|
<hr className="m-0 bg-dark" />
|
||||||
|
<WiredSourcesSelector
|
||||||
|
showFurni={ true }
|
||||||
|
furniTitle="wiredfurni.params.sources.furni.title.mv.1"
|
||||||
|
furniSources={ TARGET_FURNI_SOURCES }
|
||||||
|
furniSource={ targetSource }
|
||||||
|
onChangeFurni={ value => setTargetSource((value === SOURCE_SELECTED) ? SOURCE_SECONDARY_SELECTED : value) } />
|
||||||
|
</div>
|
||||||
|
}>
|
||||||
|
<div className="flex flex-col gap-3">
|
||||||
|
<div className="flex flex-col gap-1">
|
||||||
|
<Text bold>{ LocalizeText('wiredfurni.params.sources.furni.title.mv.0') }</Text>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<Button
|
||||||
|
variant={ (selectionMode === 'move') ? 'primary' : 'secondary' }
|
||||||
|
disabled={ moveSource !== SOURCE_SELECTED }
|
||||||
|
onClick={ () => switchSelection('move') }>
|
||||||
|
{ LocalizeText('wiredfurni.params.sources.furni.100') }
|
||||||
|
</Button>
|
||||||
|
<Text small>{ selectionLimit ? `${ moveFurniIds.length }/${ selectionLimit }` : moveFurniIds.length }</Text>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col gap-1">
|
||||||
|
<Text bold>{ LocalizeText('wiredfurni.params.sources.furni.title.mv.1') }</Text>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<Button
|
||||||
|
variant={ (selectionMode === 'target') ? 'primary' : 'secondary' }
|
||||||
|
disabled={ targetSource !== SOURCE_SECONDARY_SELECTED }
|
||||||
|
onClick={ () => switchSelection('target') }>
|
||||||
|
{ LocalizeText('wiredfurni.params.sources.furni.101') }
|
||||||
|
</Button>
|
||||||
|
<Text small>{ selectionLimit ? `${ targetFurniIds.length }/${ selectionLimit }` : targetFurniIds.length }</Text>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</WiredActionBaseView>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
import { WiredActionLayoutCode } from '../../../../api';
|
import { WiredActionLayoutCode } from '../../../../api';
|
||||||
import { WiredActionBotChangeFigureView } from './WiredActionBotChangeFigureView';
|
import { WiredActionBotChangeFigureView } from './WiredActionBotChangeFigureView';
|
||||||
import { WiredActionSetAltitudeView } from './WiredActionSetAltitudeView';
|
import { WiredActionFreezeView } from './WiredActionFreezeView';
|
||||||
|
import { WiredActionFurniToFurniView } from './WiredActionFurniToFurniView';
|
||||||
import { WiredActionSendSignalView } from './WiredActionSendSignalView';
|
import { WiredActionSendSignalView } from './WiredActionSendSignalView';
|
||||||
import { WiredActionFurniAreaView } from '../selectors/WiredActionFurniAreaView';
|
import { WiredActionFurniAreaView } from '../selectors/WiredActionFurniAreaView';
|
||||||
import { WiredSelectorFurniNeighborhoodView } from '../selectors/WiredSelectorFurniNeighborhoodView';
|
import { WiredSelectorFurniNeighborhoodView } from '../selectors/WiredSelectorFurniNeighborhoodView';
|
||||||
@@ -32,6 +33,7 @@ import { WiredActionResetView } from './WiredActionResetView';
|
|||||||
import { WiredActionSetFurniStateToView } from './WiredActionSetFurniStateToView';
|
import { WiredActionSetFurniStateToView } from './WiredActionSetFurniStateToView';
|
||||||
import { WiredActionTeleportView } from './WiredActionTeleportView';
|
import { WiredActionTeleportView } from './WiredActionTeleportView';
|
||||||
import { WiredActionToggleFurniStateView } from './WiredActionToggleFurniStateView';
|
import { WiredActionToggleFurniStateView } from './WiredActionToggleFurniStateView';
|
||||||
|
import { WiredActionUnfreezeView } from './WiredActionUnfreezeView';
|
||||||
|
|
||||||
export const WiredActionLayoutView = (code: number) =>
|
export const WiredActionLayoutView = (code: number) =>
|
||||||
{
|
{
|
||||||
@@ -59,8 +61,12 @@ export const WiredActionLayoutView = (code: number) =>
|
|||||||
return <WiredActionChatView />;
|
return <WiredActionChatView />;
|
||||||
case WiredActionLayoutCode.FLEE:
|
case WiredActionLayoutCode.FLEE:
|
||||||
return <WiredActionFleeView />;
|
return <WiredActionFleeView />;
|
||||||
case WiredActionLayoutCode.SET_ALTITUDE:
|
case WiredActionLayoutCode.FREEZE:
|
||||||
return <WiredActionSetAltitudeView />;
|
return <WiredActionFreezeView />;
|
||||||
|
case WiredActionLayoutCode.FURNI_TO_USER:
|
||||||
|
return <WiredActionTeleportView />;
|
||||||
|
case WiredActionLayoutCode.FURNI_TO_FURNI:
|
||||||
|
return <WiredActionFurniToFurniView />;
|
||||||
case WiredActionLayoutCode.GIVE_REWARD:
|
case WiredActionLayoutCode.GIVE_REWARD:
|
||||||
return <WiredActionGiveRewardView />;
|
return <WiredActionGiveRewardView />;
|
||||||
case WiredActionLayoutCode.GIVE_SCORE:
|
case WiredActionLayoutCode.GIVE_SCORE:
|
||||||
@@ -91,6 +97,10 @@ export const WiredActionLayoutView = (code: number) =>
|
|||||||
return <WiredActionTeleportView />;
|
return <WiredActionTeleportView />;
|
||||||
case WiredActionLayoutCode.TOGGLE_FURNI_STATE:
|
case WiredActionLayoutCode.TOGGLE_FURNI_STATE:
|
||||||
return <WiredActionToggleFurniStateView />;
|
return <WiredActionToggleFurniStateView />;
|
||||||
|
case WiredActionLayoutCode.UNFREEZE:
|
||||||
|
return <WiredActionUnfreezeView />;
|
||||||
|
case WiredActionLayoutCode.USER_TO_FURNI:
|
||||||
|
return <WiredActionTeleportView />;
|
||||||
case WiredActionLayoutCode.FURNI_AREA_SELECTOR:
|
case WiredActionLayoutCode.FURNI_AREA_SELECTOR:
|
||||||
return <WiredActionFurniAreaView />;
|
return <WiredActionFurniAreaView />;
|
||||||
case WiredActionLayoutCode.FURNI_NEIGHBORHOOD_SELECTOR:
|
case WiredActionLayoutCode.FURNI_NEIGHBORHOOD_SELECTOR:
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
import { FC, useEffect, useState } from 'react';
|
||||||
|
import { WiredFurniType } from '../../../../api';
|
||||||
|
import { useWired } from '../../../../hooks';
|
||||||
|
import { WiredActionBaseView } from './WiredActionBaseView';
|
||||||
|
import { WiredSourcesSelector } from '../WiredSourcesSelector';
|
||||||
|
|
||||||
|
export const WiredActionUnfreezeView: FC<{}> = () =>
|
||||||
|
{
|
||||||
|
const [ userSource, setUserSource ] = useState(0);
|
||||||
|
const { trigger = null, setIntParams = null } = useWired();
|
||||||
|
|
||||||
|
const save = () => setIntParams([ userSource ]);
|
||||||
|
|
||||||
|
useEffect(() =>
|
||||||
|
{
|
||||||
|
setUserSource((trigger?.intData?.length > 0) ? trigger.intData[0] : 0);
|
||||||
|
}, [ trigger ]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<WiredActionBaseView
|
||||||
|
hasSpecialInput={ true }
|
||||||
|
requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE }
|
||||||
|
save={ save }
|
||||||
|
footer={ <WiredSourcesSelector showUsers={ true } userSource={ userSource } onChangeUsers={ setUserSource } /> } />
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -235,6 +235,7 @@ const useWiredState = () =>
|
|||||||
{
|
{
|
||||||
const parser = event.getParser();
|
const parser = event.getParser();
|
||||||
|
|
||||||
|
WiredSelectionVisualizer.clearAllSelectionShaders();
|
||||||
setTrigger(null);
|
setTrigger(null);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -275,6 +276,7 @@ const useWiredState = () =>
|
|||||||
|
|
||||||
return () =>
|
return () =>
|
||||||
{
|
{
|
||||||
|
WiredSelectionVisualizer.clearAllSelectionShaders();
|
||||||
setIntParams([]);
|
setIntParams([]);
|
||||||
setStringParam('');
|
setStringParam('');
|
||||||
setActionDelay(0);
|
setActionDelay(0);
|
||||||
|
|||||||
Reference in New Issue
Block a user