From f437db2faec59a12d9192aacf49159d2c3b82c31 Mon Sep 17 00:00:00 2001 From: Lorenzune Date: Mon, 16 Mar 2026 15:12:42 +0100 Subject: [PATCH] feat(wired): update source-aware furni and signal UI --- .../views/actions/WiredActionBotMoveView.tsx | 22 +- .../actions/WiredActionBotTeleportView.tsx | 22 +- .../WiredActionCallAnotherStackView.tsx | 22 +- .../views/actions/WiredActionChaseView.tsx | 22 +- .../views/actions/WiredActionFleeView.tsx | 22 +- .../WiredActionMoveAndRotateFurniView.tsx | 22 +- .../actions/WiredActionMoveFurniToView.tsx | 22 +- .../actions/WiredActionMoveFurniView.tsx | 22 +- .../actions/WiredActionSendSignalView.tsx | 320 ++++++++++-------- .../WiredActionSetFurniStateToView.tsx | 22 +- .../views/actions/WiredActionTeleportView.tsx | 22 +- .../WiredActionToggleFurniStateView.tsx | 22 +- .../WiredConditionActorIsOnFurniView.tsx | 22 +- .../WiredConditionFurniHasAvatarOnView.tsx | 22 +- .../WiredConditionFurniHasFurniOnView.tsx | 22 +- .../WiredConditionFurniHasNotFurniOnView.tsx | 22 +- .../WiredConditionFurniIsOfTypeView.tsx | 22 +- ...WiredConditionFurniMatchesSnapshotView.tsx | 22 +- .../WiredSelectorFurniByTypeView.tsx | 59 +--- ...iredTriggerExecutePeriodicallyLongView.tsx | 2 +- .../WiredTriggerExecutePeriodicallyView.tsx | 2 +- .../WiredTriggerReceiveSignalView.tsx | 13 +- src/hooks/wired/useWired.ts | 85 ++++- 23 files changed, 339 insertions(+), 516 deletions(-) diff --git a/src/components/wired/views/actions/WiredActionBotMoveView.tsx b/src/components/wired/views/actions/WiredActionBotMoveView.tsx index 14a3d47..d07bc7b 100644 --- a/src/components/wired/views/actions/WiredActionBotMoveView.tsx +++ b/src/components/wired/views/actions/WiredActionBotMoveView.tsx @@ -1,5 +1,5 @@ import { FC, useEffect, useState } from 'react'; -import { LocalizeText, WiredFurniType, WiredSelectionVisualizer } from '../../../../api'; +import { LocalizeText, WiredFurniType } from '../../../../api'; import { Text } from '../../../../common'; import { useWired } from '../../../../hooks'; import { NitroInput } from '../../../../layout'; @@ -9,7 +9,7 @@ import { WiredSourcesSelector } from '../WiredSourcesSelector'; export const WiredActionBotMoveView: FC<{}> = props => { const [ botName, setBotName ] = useState(''); - const { trigger = null, furniIds = [], setFurniIds = null, setStringParam = null, setIntParams = null } = useWired(); + const { trigger = null, setStringParam = null, setIntParams = null } = useWired(); const [ furniSource, setFurniSource ] = useState(() => { @@ -33,23 +33,9 @@ export const WiredActionBotMoveView: FC<{}> = props => else setFurniSource((trigger.selectedItems?.length ?? 0) > 0 ? 100 : 0); }, [ trigger ]); - const onChangeFurniSource = (next: number) => - { - if(furniIds.length && setFurniIds) - { - setFurniIds(prev => - { - if(prev && prev.length) WiredSelectionVisualizer.clearSelectionShaderFromFurni(prev); - return []; - }); - } + const onChangeFurniSource = (next: number) => setFurniSource(next); - setFurniSource(next); - }; - - const requiresFurni = (furniSource === 100) - ? WiredFurniType.STUFF_SELECTION_OPTION_BY_ID - : WiredFurniType.STUFF_SELECTION_OPTION_NONE; + const requiresFurni = WiredFurniType.STUFF_SELECTION_OPTION_BY_ID; return ( = props => { const [ botName, setBotName ] = useState(''); - const { trigger = null, furniIds = [], setFurniIds = null, setStringParam = null, setIntParams = null } = useWired(); + const { trigger = null, setStringParam = null, setIntParams = null } = useWired(); const [ furniSource, setFurniSource ] = useState(() => { @@ -33,23 +33,9 @@ export const WiredActionBotTeleportView: FC<{}> = props => else setFurniSource((trigger.selectedItems?.length ?? 0) > 0 ? 100 : 0); }, [ trigger ]); - const onChangeFurniSource = (next: number) => - { - if(furniIds.length && setFurniIds) - { - setFurniIds(prev => - { - if(prev && prev.length) WiredSelectionVisualizer.clearSelectionShaderFromFurni(prev); - return []; - }); - } + const onChangeFurniSource = (next: number) => setFurniSource(next); - setFurniSource(next); - }; - - const requiresFurni = (furniSource === 100) - ? WiredFurniType.STUFF_SELECTION_OPTION_BY_ID - : WiredFurniType.STUFF_SELECTION_OPTION_NONE; + const requiresFurni = WiredFurniType.STUFF_SELECTION_OPTION_BY_ID; return ( = props => { - const { trigger = null, furniIds = [], setFurniIds = null, setIntParams = null } = useWired(); + const { trigger = null, setIntParams = null } = useWired(); const [ furniSource, setFurniSource ] = useState(() => { if(trigger?.intData?.length >= 1) return trigger.intData[0]; @@ -21,25 +21,11 @@ export const WiredActionCallAnotherStackView: FC<{}> = props => else setFurniSource((trigger.selectedItems?.length ?? 0) > 0 ? 100 : 0); }, [ trigger ]); - const onChangeFurniSource = (next: number) => - { - if(furniIds.length && setFurniIds) - { - setFurniIds(prev => - { - if(prev && prev.length) WiredSelectionVisualizer.clearSelectionShaderFromFurni(prev); - return []; - }); - } - - setFurniSource(next); - }; + const onChangeFurniSource = (next: number) => setFurniSource(next); const save = () => setIntParams([ furniSource ]); - const requiresFurni = (furniSource === 100) - ? WiredFurniType.STUFF_SELECTION_OPTION_BY_ID_BY_TYPE_OR_FROM_CONTEXT - : WiredFurniType.STUFF_SELECTION_OPTION_NONE; + const requiresFurni = WiredFurniType.STUFF_SELECTION_OPTION_BY_ID_BY_TYPE_OR_FROM_CONTEXT; return ( = props => { - const { trigger = null, furniIds = [], setFurniIds = null, setIntParams = null } = useWired(); + const { trigger = null, setIntParams = null } = useWired(); const [ furniSource, setFurniSource ] = useState(() => { if(trigger?.intData?.length >= 1) return trigger.intData[0]; @@ -21,25 +21,11 @@ export const WiredActionChaseView: FC<{}> = props => else setFurniSource((trigger.selectedItems?.length ?? 0) > 0 ? 100 : 0); }, [ trigger ]); - const onChangeFurniSource = (next: number) => - { - if(furniIds.length && setFurniIds) - { - setFurniIds(prev => - { - if(prev && prev.length) WiredSelectionVisualizer.clearSelectionShaderFromFurni(prev); - return []; - }); - } - - setFurniSource(next); - }; + const onChangeFurniSource = (next: number) => setFurniSource(next); const save = () => setIntParams([ furniSource ]); - const requiresFurni = (furniSource === 100) - ? WiredFurniType.STUFF_SELECTION_OPTION_BY_ID_BY_TYPE_OR_FROM_CONTEXT - : WiredFurniType.STUFF_SELECTION_OPTION_NONE; + const requiresFurni = WiredFurniType.STUFF_SELECTION_OPTION_BY_ID_BY_TYPE_OR_FROM_CONTEXT; return ( = props => { - const { trigger = null, furniIds = [], setFurniIds = null, setIntParams = null } = useWired(); + const { trigger = null, setIntParams = null } = useWired(); const [ furniSource, setFurniSource ] = useState(() => { if(trigger?.intData?.length >= 1) return trigger.intData[0]; @@ -21,25 +21,11 @@ export const WiredActionFleeView: FC<{}> = props => else setFurniSource((trigger.selectedItems?.length ?? 0) > 0 ? 100 : 0); }, [ trigger ]); - const onChangeFurniSource = (next: number) => - { - if(furniIds.length && setFurniIds) - { - setFurniIds(prev => - { - if(prev && prev.length) WiredSelectionVisualizer.clearSelectionShaderFromFurni(prev); - return []; - }); - } - - setFurniSource(next); - }; + const onChangeFurniSource = (next: number) => setFurniSource(next); const save = () => setIntParams([ furniSource ]); - const requiresFurni = (furniSource === 100) - ? WiredFurniType.STUFF_SELECTION_OPTION_BY_ID_BY_TYPE_OR_FROM_CONTEXT - : WiredFurniType.STUFF_SELECTION_OPTION_NONE; + const requiresFurni = WiredFurniType.STUFF_SELECTION_OPTION_BY_ID_BY_TYPE_OR_FROM_CONTEXT; return ( = props => { const [ movement, setMovement ] = useState(-1); const [ rotation, setRotation ] = useState(-1); - const { trigger = null, furniIds = [], setFurniIds = null, setIntParams = null } = useWired(); + const { trigger = null, setIntParams = null } = useWired(); const [ furniSource, setFurniSource ] = useState(() => { if(trigger?.intData?.length > 2) return trigger.intData[2]; @@ -56,23 +56,9 @@ export const WiredActionMoveAndRotateFurniView: FC<{}> = props => else setFurniSource((trigger.selectedItems?.length ?? 0) > 0 ? 100 : 0); }, [ trigger ]); - const onChangeFurniSource = (next: number) => - { - if(furniIds.length && setFurniIds) - { - setFurniIds(prev => - { - if(prev && prev.length) WiredSelectionVisualizer.clearSelectionShaderFromFurni(prev); - return []; - }); - } + const onChangeFurniSource = (next: number) => setFurniSource(next); - setFurniSource(next); - }; - - const requiresFurni = (furniSource === 100) - ? WiredFurniType.STUFF_SELECTION_OPTION_BY_ID_BY_TYPE_OR_FROM_CONTEXT - : WiredFurniType.STUFF_SELECTION_OPTION_NONE; + const requiresFurni = WiredFurniType.STUFF_SELECTION_OPTION_BY_ID_BY_TYPE_OR_FROM_CONTEXT; return ( = props => { const [ spacing, setSpacing ] = useState(-1); const [ movement, setMovement ] = useState(-1); - const { trigger = null, furniIds = [], setFurniIds = null, setIntParams = null } = useWired(); + const { trigger = null, setIntParams = null } = useWired(); const [ furniSource, setFurniSource ] = useState(() => { if(trigger?.intData?.length > 2) return trigger.intData[2]; @@ -54,23 +54,9 @@ export const WiredActionMoveFurniToView: FC<{}> = props => else setFurniSource((trigger.selectedItems?.length ?? 0) > 0 ? 100 : 0); }, [ trigger ]); - const onChangeFurniSource = (next: number) => - { - if(furniIds.length && setFurniIds) - { - setFurniIds(prev => - { - if(prev && prev.length) WiredSelectionVisualizer.clearSelectionShaderFromFurni(prev); - return []; - }); - } + const onChangeFurniSource = (next: number) => setFurniSource(next); - setFurniSource(next); - }; - - const requiresFurni = (furniSource === 100) - ? WiredFurniType.STUFF_SELECTION_OPTION_BY_ID_OR_BY_TYPE - : WiredFurniType.STUFF_SELECTION_OPTION_NONE; + const requiresFurni = WiredFurniType.STUFF_SELECTION_OPTION_BY_ID_OR_BY_TYPE; return ( = props => { const [ movement, setMovement ] = useState(-1); const [ rotation, setRotation ] = useState(-1); - const { trigger = null, furniIds = [], setFurniIds = null, setIntParams = null } = useWired(); + const { trigger = null, setIntParams = null } = useWired(); const [ furniSource, setFurniSource ] = useState(() => { if(trigger?.intData?.length > 2) return trigger.intData[2]; @@ -68,23 +68,9 @@ export const WiredActionMoveFurniView: FC<{}> = props => else setFurniSource((trigger.selectedItems?.length ?? 0) > 0 ? 100 : 0); }, [ trigger ]); - const onChangeFurniSource = (next: number) => - { - if(furniIds.length && setFurniIds) - { - setFurniIds(prev => - { - if(prev && prev.length) WiredSelectionVisualizer.clearSelectionShaderFromFurni(prev); - return []; - }); - } + const onChangeFurniSource = (next: number) => setFurniSource(next); - setFurniSource(next); - }; - - const requiresFurni = (furniSource === 100) - ? WiredFurniType.STUFF_SELECTION_OPTION_BY_ID_BY_TYPE_OR_FROM_CONTEXT - : WiredFurniType.STUFF_SELECTION_OPTION_NONE; + const requiresFurni = WiredFurniType.STUFF_SELECTION_OPTION_BY_ID_BY_TYPE_OR_FROM_CONTEXT; return ( +{ + if(!data || !data.length) return []; + + const ids = new Set(); + + 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 serializeForwardIds = (ids: number[]): string => +{ + if(!ids || !ids.length) return ''; + + return ids.filter(id => (id > 0)).join(FORWARD_ITEM_DELIMITER); +}; export const WiredActionSendSignalView: FC<{}> = () => { - const [ antennaSource, setAntennaSource ] = useState(ANTENNA_PICKED); - const [ furniForward, setFurniForward ] = useState(FORWARD_NONE); - const [ userForward, setUserForward ] = useState(FORWARD_NONE); + const [ furniSource, setFurniSource ] = useState(SOURCE_TRIGGER); + const [ userSource, setUserSource ] = useState(SOURCE_TRIGGER); const [ signalPerFurni, setSignalPerFurni ] = useState(false); const [ signalPerUser, setSignalPerUser ] = useState(false); - const [ showAdvanced, setShowAdvanced ] = useState(false); + const [ antennaIds, setAntennaIds ] = useState([]); + const [ forwardFurniIds, setForwardFurniIds ] = useState([]); + const [ selectionMode, setSelectionMode ] = useState('antenna'); - const { trigger = null, setIntParams } = useWired(); + const { trigger = null, furniIds = [], setFurniIds = null, setIntParams = null, setStringParam = null, setAllowedInteractionTypes = null } = useWired(); useEffect(() => { if(!trigger) return; const p = trigger.intData; - if(p.length >= 1) setAntennaSource(p[0]); - if(p.length >= 2) setFurniForward(p[1]); - if(p.length >= 3) setUserForward(p[2]); - if(p.length >= 4) setSignalPerFurni(p[3] === 1); - if(p.length >= 5) setSignalPerUser(p[4] === 1); + if(p.length > 1) setFurniSource(p[1]); + else setFurniSource(SOURCE_TRIGGER); - if(p.length >= 1 && (p[0] !== ANTENNA_PICKED || p[1] !== FORWARD_NONE || - p[2] !== FORWARD_NONE || (p.length >= 4 && p[3] === 1) || (p.length >= 5 && p[4] === 1))) - { - setShowAdvanced(true); - } + if(p.length > 2) setUserSource(p[2]); + else setUserSource(SOURCE_TRIGGER); + + setSignalPerFurni(p.length > 3 && p[3] === 1); + setSignalPerUser(p.length > 4 && p[4] === 1); + + setAntennaIds(trigger.selectedItems ?? []); + setForwardFurniIds(parseForwardIds(trigger.stringData)); + setSelectionMode('antenna'); }, [ trigger ]); + useEffect(() => + { + if(selectionMode === 'antenna') setAllowedInteractionTypes(ANTENNA_INTERACTION_TYPES); + else setAllowedInteractionTypes(null); + + return () => setAllowedInteractionTypes(null); + }, [ selectionMode, setAllowedInteractionTypes ]); + + useEffect(() => + { + if(selectionMode === 'antenna') setAntennaIds(furniIds); + else setForwardFurniIds(furniIds); + }, [ furniIds, selectionMode ]); + + const applySelection = useCallback((nextIds: number[]) => + { + if(!setFurniIds) return; + + setFurniIds(prev => + { + if(prev && prev.length) WiredSelectionVisualizer.clearSelectionShaderFromFurni(prev); + if(nextIds && nextIds.length) WiredSelectionVisualizer.applySelectionShaderToFurni(nextIds); + + return [ ...nextIds ]; + }); + }, [ setFurniIds ]); + + const switchSelection = useCallback((mode: SelectionMode) => + { + if(mode === selectionMode) return; + if(mode === 'furni' && furniSource !== SOURCE_SELECTED) return; + + const nextIds = (mode === 'antenna') ? antennaIds : forwardFurniIds; + applySelection(nextIds); + setSelectionMode(mode); + }, [ selectionMode, furniSource, antennaIds, forwardFurniIds, applySelection ]); + + const onChangeFurniSource = (next: number) => + { + if(forwardFurniIds.length) setForwardFurniIds([]); + + if(selectionMode === 'furni') + { + applySelection(antennaIds); + setSelectionMode('antenna'); + } + + setFurniSource(next); + }; + const save = useCallback(() => { + if(selectionMode === 'furni') + { + setSelectionMode('antenna'); + applySelection(antennaIds); + } + + const antennaSource = (antennaIds && antennaIds.length) ? antennaIds[0] : 0; + setIntParams([ antennaSource, - furniForward, - userForward, + furniSource, + userSource, signalPerFurni ? 1 : 0, signalPerUser ? 1 : 0, + 0, ]); - }, [ antennaSource, furniForward, userForward, signalPerFurni, signalPerUser, setIntParams ]); + + setStringParam(serializeForwardIds(forwardFurniIds)); + }, [ selectionMode, antennaIds, furniSource, userSource, signalPerFurni, signalPerUser, forwardFurniIds, setIntParams, setStringParam, applySelection, setSelectionMode ]); + + const selectionLimit = trigger?.maximumItemSelectionCount ?? 0; + const forwardSelectionEnabled = (furniSource === SOURCE_SELECTED); return ( -
- -
setShowAdvanced(!showAdvanced) }> - - { showAdvanced - ? LocalizeText('wiredfurni.params.hide_advanced') - : LocalizeText('wiredfurni.params.show_advanced') } - + cardStyle={ { width: '400px' } } + save={ save } + footer={ ( + + ) }> +
+
+ Antenne selezionate +
+ + { selectionLimit ? `${ antennaIds.length }/${ selectionLimit }` : antennaIds.length } +
+
+
+ Furni selezionati +
+ + { selectionLimit ? `${ forwardFurniIds.length }/${ selectionLimit }` : forwardFurniIds.length } +
- { showAdvanced && <> - - { /* --- Antennas --- */ } - { LocalizeText('wiredfurni.params.sources.furni.title.signal_antenna') } -
- setAntennaSource(antennaSource === ANTENNA_PICKED ? ANTENNA_TRIGGER : ANTENNA_PICKED) } /> - -
- - { /* --- Furni to forward --- */ } - { LocalizeText('wiredfurni.params.sources.furni.title.signal_forward') } -
-
- setFurniForward(e.target.checked ? FORWARD_TRIGGER : FORWARD_NONE) } /> - -
- { furniForward !== FORWARD_NONE && - - } -
- - { /* --- Users to forward --- */ } - { LocalizeText('wiredfurni.params.sources.users.title.signal_forward') } -
-
- setUserForward(e.target.checked ? FORWARD_TRIGGER : FORWARD_NONE) } /> - -
- { userForward !== FORWARD_NONE && - - } -
- - { LocalizeText('wiredfurni.params.signal.options') } -
- setSignalPerFurni(e.target.checked) } /> - -
-
- setSignalPerUser(e.target.checked) } /> - -
- } - + { LocalizeText('wiredfurni.params.signal.options') } +
+ setSignalPerFurni(e.target.checked) } /> + +
+
+ setSignalPerUser(e.target.checked) } /> + +
); diff --git a/src/components/wired/views/actions/WiredActionSetFurniStateToView.tsx b/src/components/wired/views/actions/WiredActionSetFurniStateToView.tsx index f7ee4c3..8f31754 100644 --- a/src/components/wired/views/actions/WiredActionSetFurniStateToView.tsx +++ b/src/components/wired/views/actions/WiredActionSetFurniStateToView.tsx @@ -1,5 +1,5 @@ import { FC, useEffect, useState } from 'react'; -import { LocalizeText, WiredFurniType, WiredSelectionVisualizer } from '../../../../api'; +import { LocalizeText, WiredFurniType } from '../../../../api'; import { Text } from '../../../../common'; import { useWired } from '../../../../hooks'; import { WiredActionBaseView } from './WiredActionBaseView'; @@ -10,7 +10,7 @@ export const WiredActionSetFurniStateToView: FC<{}> = props => const [ stateFlag, setStateFlag ] = useState(0); const [ directionFlag, setDirectionFlag ] = useState(0); const [ positionFlag, setPositionFlag ] = useState(0); - const { trigger = null, furniIds = [], setFurniIds = null, setIntParams = null } = useWired(); + const { trigger = null, setIntParams = null } = useWired(); const [ furniSource, setFurniSource ] = useState(() => { if(trigger?.intData?.length > 3) return trigger.intData[3]; @@ -29,23 +29,9 @@ export const WiredActionSetFurniStateToView: FC<{}> = props => else setFurniSource((trigger.selectedItems?.length ?? 0) > 0 ? 100 : 0); }, [ trigger ]); - const onChangeFurniSource = (next: number) => - { - if(furniIds.length && setFurniIds) - { - setFurniIds(prev => - { - if(prev && prev.length) WiredSelectionVisualizer.clearSelectionShaderFromFurni(prev); - return []; - }); - } + const onChangeFurniSource = (next: number) => setFurniSource(next); - setFurniSource(next); - }; - - const requiresFurni = (furniSource === 100) - ? WiredFurniType.STUFF_SELECTION_OPTION_BY_ID - : WiredFurniType.STUFF_SELECTION_OPTION_NONE; + const requiresFurni = WiredFurniType.STUFF_SELECTION_OPTION_BY_ID; return ( = props => { - const { trigger = null, furniIds = [], setFurniIds = null, setIntParams = null } = useWired(); + const { trigger = null, setIntParams = null } = useWired(); const [ furniSource, setFurniSource ] = useState(() => { @@ -31,25 +31,11 @@ export const WiredActionTeleportView: FC<{}> = props => else setUserSource(0); }, [ trigger ]); - const onChangeFurniSource = (next: number) => - { - if(furniIds.length && setFurniIds) - { - setFurniIds(prev => - { - if(prev && prev.length) WiredSelectionVisualizer.clearSelectionShaderFromFurni(prev); - return []; - }); - } - - setFurniSource(next); - }; + const onChangeFurniSource = (next: number) => setFurniSource(next); const save = () => setIntParams([ furniSource, userSource ]); - const requiresFurni = (furniSource === 100) - ? WiredFurniType.STUFF_SELECTION_OPTION_BY_ID_BY_TYPE_OR_FROM_CONTEXT - : WiredFurniType.STUFF_SELECTION_OPTION_NONE; + const requiresFurni = WiredFurniType.STUFF_SELECTION_OPTION_BY_ID_BY_TYPE_OR_FROM_CONTEXT; return ( = props => { - const { trigger = null, furniIds = [], setFurniIds = null, setIntParams = null } = useWired(); + const { trigger = null, setIntParams = null } = useWired(); const [ furniSource, setFurniSource ] = useState(() => { if(trigger?.intData?.length >= 1) return trigger.intData[0]; @@ -21,25 +21,11 @@ export const WiredActionToggleFurniStateView: FC<{}> = props => else setFurniSource((trigger.selectedItems?.length ?? 0) > 0 ? 100 : 0); }, [ trigger ]); - const onChangeFurniSource = (next: number) => - { - if(furniIds.length && setFurniIds) - { - setFurniIds(prev => - { - if(prev && prev.length) WiredSelectionVisualizer.clearSelectionShaderFromFurni(prev); - return []; - }); - } - - setFurniSource(next); - }; + const onChangeFurniSource = (next: number) => setFurniSource(next); const save = () => setIntParams([ furniSource ]); - const requiresFurni = (furniSource === 100) - ? WiredFurniType.STUFF_SELECTION_OPTION_BY_ID_BY_TYPE_OR_FROM_CONTEXT - : WiredFurniType.STUFF_SELECTION_OPTION_NONE; + const requiresFurni = WiredFurniType.STUFF_SELECTION_OPTION_BY_ID_BY_TYPE_OR_FROM_CONTEXT; return ( = props => { - const { trigger = null, furniIds = [], setFurniIds = null, setIntParams = null } = useWired(); + const { trigger = null, setIntParams = null } = useWired(); const [ furniSource, setFurniSource ] = useState(() => { if(trigger?.intData?.length > 0) return trigger.intData[0]; @@ -29,25 +29,11 @@ export const WiredConditionActorIsOnFurniView: FC<{}> = props => else setUserSource(0); }, [ trigger ]); - const onChangeFurniSource = (next: number) => - { - if(furniIds.length && setFurniIds) - { - setFurniIds(prev => - { - if(prev && prev.length) WiredSelectionVisualizer.clearSelectionShaderFromFurni(prev); - return []; - }); - } - - setFurniSource(next); - }; + const onChangeFurniSource = (next: number) => setFurniSource(next); const save = () => setIntParams([ furniSource, userSource ]); - const requiresFurni = (furniSource === 100) - ? WiredFurniType.STUFF_SELECTION_OPTION_BY_ID - : WiredFurniType.STUFF_SELECTION_OPTION_NONE; + const requiresFurni = WiredFurniType.STUFF_SELECTION_OPTION_BY_ID; return ( = props => { - const { trigger = null, furniIds = [], setFurniIds = null, setIntParams = null } = useWired(); + const { trigger = null, setIntParams = null } = useWired(); const [ furniSource, setFurniSource ] = useState(() => { if(trigger?.intData?.length >= 1) return trigger.intData[0]; @@ -20,25 +20,11 @@ export const WiredConditionFurniHasAvatarOnView: FC<{}> = props => else setFurniSource((trigger.selectedItems?.length ?? 0) > 0 ? 100 : 0); }, [ trigger ]); - const onChangeFurniSource = (next: number) => - { - if(furniIds.length && setFurniIds) - { - setFurniIds(prev => - { - if(prev && prev.length) WiredSelectionVisualizer.clearSelectionShaderFromFurni(prev); - return []; - }); - } - - setFurniSource(next); - }; + const onChangeFurniSource = (next: number) => setFurniSource(next); const save = () => setIntParams([ furniSource ]); - const requiresFurni = (furniSource === 100) - ? WiredFurniType.STUFF_SELECTION_OPTION_BY_ID - : WiredFurniType.STUFF_SELECTION_OPTION_NONE; + const requiresFurni = WiredFurniType.STUFF_SELECTION_OPTION_BY_ID; return ( = props => { const [ requireAll, setRequireAll ] = useState(-1); - const { trigger = null, furniIds = [], setFurniIds = null, setIntParams = null } = useWired(); + const { trigger = null, setIntParams = null } = useWired(); const [ furniSource, setFurniSource ] = useState(() => { if(trigger?.intData?.length > 1) return trigger.intData[1]; @@ -24,23 +24,9 @@ export const WiredConditionFurniHasFurniOnView: FC<{}> = props => else setFurniSource((trigger.selectedItems?.length ?? 0) > 0 ? 100 : 0); }, [ trigger ]); - const onChangeFurniSource = (next: number) => - { - if(furniIds.length && setFurniIds) - { - setFurniIds(prev => - { - if(prev && prev.length) WiredSelectionVisualizer.clearSelectionShaderFromFurni(prev); - return []; - }); - } + const onChangeFurniSource = (next: number) => setFurniSource(next); - setFurniSource(next); - }; - - const requiresFurni = (furniSource === 100) - ? WiredFurniType.STUFF_SELECTION_OPTION_BY_ID - : WiredFurniType.STUFF_SELECTION_OPTION_NONE; + const requiresFurni = WiredFurniType.STUFF_SELECTION_OPTION_BY_ID; return ( = props => { const [ requireAll, setRequireAll ] = useState(-1); - const { trigger = null, furniIds = [], setFurniIds = null, setIntParams = null } = useWired(); + const { trigger = null, setIntParams = null } = useWired(); const [ furniSource, setFurniSource ] = useState(() => { if(trigger?.intData?.length > 1) return trigger.intData[1]; @@ -24,23 +24,9 @@ export const WiredConditionFurniHasNotFurniOnView: FC<{}> = props => else setFurniSource((trigger.selectedItems?.length ?? 0) > 0 ? 100 : 0); }, [ trigger ]); - const onChangeFurniSource = (next: number) => - { - if(furniIds.length && setFurniIds) - { - setFurniIds(prev => - { - if(prev && prev.length) WiredSelectionVisualizer.clearSelectionShaderFromFurni(prev); - return []; - }); - } + const onChangeFurniSource = (next: number) => setFurniSource(next); - setFurniSource(next); - }; - - const requiresFurni = (furniSource === 100) - ? WiredFurniType.STUFF_SELECTION_OPTION_BY_ID - : WiredFurniType.STUFF_SELECTION_OPTION_NONE; + const requiresFurni = WiredFurniType.STUFF_SELECTION_OPTION_BY_ID; return ( = props => { - const { trigger = null, furniIds = [], setFurniIds = null, setIntParams = null } = useWired(); + const { trigger = null, setIntParams = null } = useWired(); const [ furniSource, setFurniSource ] = useState(() => { if(trigger?.intData?.length >= 1) return trigger.intData[0]; @@ -20,25 +20,11 @@ export const WiredConditionFurniIsOfTypeView: FC<{}> = props => else setFurniSource((trigger.selectedItems?.length ?? 0) > 0 ? 100 : 0); }, [ trigger ]); - const onChangeFurniSource = (next: number) => - { - if(furniIds.length && setFurniIds) - { - setFurniIds(prev => - { - if(prev && prev.length) WiredSelectionVisualizer.clearSelectionShaderFromFurni(prev); - return []; - }); - } - - setFurniSource(next); - }; + const onChangeFurniSource = (next: number) => setFurniSource(next); const save = () => setIntParams([ furniSource ]); - const requiresFurni = (furniSource === 100) - ? WiredFurniType.STUFF_SELECTION_OPTION_BY_ID_OR_BY_TYPE - : WiredFurniType.STUFF_SELECTION_OPTION_NONE; + const requiresFurni = WiredFurniType.STUFF_SELECTION_OPTION_BY_ID_OR_BY_TYPE; return ( = props => const [ stateFlag, setStateFlag ] = useState(0); const [ directionFlag, setDirectionFlag ] = useState(0); const [ positionFlag, setPositionFlag ] = useState(0); - const { trigger = null, furniIds = [], setFurniIds = null, setIntParams = null } = useWired(); + const { trigger = null, setIntParams = null } = useWired(); const [ furniSource, setFurniSource ] = useState(() => { if(trigger?.intData?.length > 3) return trigger.intData[3]; @@ -28,23 +28,9 @@ export const WiredConditionFurniMatchesSnapshotView: FC<{}> = props => else setFurniSource((trigger.selectedItems?.length ?? 0) > 0 ? 100 : 0); }, [ trigger ]); - const onChangeFurniSource = (next: number) => - { - if(furniIds.length && setFurniIds) - { - setFurniIds(prev => - { - if(prev && prev.length) WiredSelectionVisualizer.clearSelectionShaderFromFurni(prev); - return []; - }); - } + const onChangeFurniSource = (next: number) => setFurniSource(next); - setFurniSource(next); - }; - - const requiresFurni = (furniSource === 100) - ? WiredFurniType.STUFF_SELECTION_OPTION_BY_ID - : WiredFurniType.STUFF_SELECTION_OPTION_NONE; + const requiresFurni = WiredFurniType.STUFF_SELECTION_OPTION_BY_ID; return ( = () => { - const [ sourceType, setSourceType ] = useState(SOURCE_FURNI_PICKED); const [ matchState, setMatchState ] = useState(false); const [ filterExisting, setFilterExisting ] = useState(false); const [ invert, setInvert ] = useState(false); - const { trigger = null, furniIds = [], setIntParams, setSelectByType, setInvertSelection } = useWired(); + const { trigger = null, setIntParams, setSelectByType } = useWired(); useEffect(() => { if(!trigger) return; const p = trigger.intData; - if(p.length >= 1) setSourceType(p[0]); if(p.length >= 2) setMatchState(p[1] === 1); if(p.length >= 3) setFilterExisting(p[2] === 1); if(p.length >= 4) setInvert(p[3] === 1); @@ -37,38 +26,20 @@ export const WiredSelectorFurniByTypeView: FC<{}> = () => useEffect(() => { - setSelectByType(sourceType === SOURCE_FURNI_PICKED); - }, [ sourceType, setSelectByType ]); - - useEffect(() => - { - setInvertSelection(invert); - }, [ invert, setInvertSelection ]); + setSelectByType(true); + }, [ setSelectByType ]); const save = useCallback(() => { setIntParams([ - sourceType, + SOURCE_FURNI_PICKED, matchState ? 1 : 0, filterExisting ? 1 : 0, invert ? 1 : 0, ]); - }, [ sourceType, matchState, filterExisting, invert, setIntParams ]); + }, [ matchState, filterExisting, invert, setIntParams ]); - const sourceIndex = SOURCES.findIndex(s => s.value === sourceType); - - const prevSource = () => - setSourceType(SOURCES[(sourceIndex - 1 + SOURCES.length) % SOURCES.length].value); - - const nextSource = () => - setSourceType(SOURCES[(sourceIndex + 1) % SOURCES.length].value); - - const requiresFurni = sourceType === SOURCE_FURNI_PICKED - ? WiredFurniType.STUFF_SELECTION_OPTION_BY_ID - : WiredFurniType.STUFF_SELECTION_OPTION_NONE; - - const pickedCount = furniIds.length; - const pickedLimit = trigger?.maximumItemSelectionCount ?? 20; + const requiresFurni = WiredFurniType.STUFF_SELECTION_OPTION_BY_ID; return ( @@ -104,22 +75,6 @@ export const WiredSelectorFurniByTypeView: FC<{}> = () => onChange={ e => setInvert(e.target.checked) } /> { LocalizeText('wiredfurni.params.selector_option.1') } - -
- - { LocalizeText('wiredfurni.params.sources.furni.title') } - -
- -
- { LocalizeText(SOURCES[sourceIndex >= 0 ? sourceIndex : 0].label) } -
- -
); diff --git a/src/components/wired/views/triggers/WiredTriggerExecutePeriodicallyLongView.tsx b/src/components/wired/views/triggers/WiredTriggerExecutePeriodicallyLongView.tsx index 7b9a766..6cb4726 100644 --- a/src/components/wired/views/triggers/WiredTriggerExecutePeriodicallyLongView.tsx +++ b/src/components/wired/views/triggers/WiredTriggerExecutePeriodicallyLongView.tsx @@ -1,6 +1,6 @@ import { FC, useEffect, useState } from 'react'; import { FriendlyTime, LocalizeText, WiredFurniType } from '../../../../api'; -import { Text } from '../../../../common'; +import { Slider, Text } from '../../../../common'; import { useWired } from '../../../../hooks'; import { WiredTriggerBaseView } from './WiredTriggerBaseView'; diff --git a/src/components/wired/views/triggers/WiredTriggerExecutePeriodicallyView.tsx b/src/components/wired/views/triggers/WiredTriggerExecutePeriodicallyView.tsx index 7c3056a..ebed0ee 100644 --- a/src/components/wired/views/triggers/WiredTriggerExecutePeriodicallyView.tsx +++ b/src/components/wired/views/triggers/WiredTriggerExecutePeriodicallyView.tsx @@ -1,6 +1,6 @@ import { FC, useEffect, useState } from 'react'; import { GetWiredTimeLocale, LocalizeText, WiredFurniType } from '../../../../api'; -import { Text } from '../../../../common'; +import { Slider, Text } from '../../../../common'; import { useWired } from '../../../../hooks'; import { WiredTriggerBaseView } from './WiredTriggerBaseView'; diff --git a/src/components/wired/views/triggers/WiredTriggerReceiveSignalView.tsx b/src/components/wired/views/triggers/WiredTriggerReceiveSignalView.tsx index 036f81d..30bc9a6 100644 --- a/src/components/wired/views/triggers/WiredTriggerReceiveSignalView.tsx +++ b/src/components/wired/views/triggers/WiredTriggerReceiveSignalView.tsx @@ -4,12 +4,14 @@ import { Text } from '../../../../common'; import { useWired } from '../../../../hooks'; import { WiredTriggerBaseView } from './WiredTriggerBaseView'; +const ANTENNA_INTERACTION_TYPES = [ 'antenna' ]; + export const WiredTriggerReceiveSignalView: FC<{}> = () => { const [ senderCount, setSenderCount ] = useState(0); const [ maxSenders, setMaxSenders ] = useState(5); - const { trigger = null } = useWired(); + const { trigger = null, setAllowedInteractionTypes } = useWired(); useEffect(() => { @@ -20,8 +22,15 @@ export const WiredTriggerReceiveSignalView: FC<{}> = () => if(p.length >= 3) setMaxSenders(p[2]); }, [ trigger ]); + useEffect(() => + { + setAllowedInteractionTypes(ANTENNA_INTERACTION_TYPES); + + return () => setAllowedInteractionTypes(null); + }, [ setAllowedInteractionTypes ]); + return ( - +
{ LocalizeText('wiredfurni.params.signal.senders_connected') } { senderCount }/{ maxSenders } diff --git a/src/hooks/wired/useWired.ts b/src/hooks/wired/useWired.ts index b455766..b0c3f32 100644 --- a/src/hooks/wired/useWired.ts +++ b/src/hooks/wired/useWired.ts @@ -14,9 +14,9 @@ const useWiredState = () => const [ actionDelay, setActionDelay ] = useState(0); const [ allowsFurni, setAllowsFurni ] = useState(WiredFurniType.STUFF_SELECTION_OPTION_NONE); const [ selectByType, setSelectByType ] = useState(false); - const [ invertSelection, setInvertSelection ] = useState(false); const [ neighborhoodTiles, setNeighborhoodTiles ] = useState<{ x: number; y: number }[] | null>(null); const [ neighborhoodInvert, setNeighborhoodInvert ] = useState(false); + const [ allowedInteractionTypes, setAllowedInteractionTypes ] = useState(null); const { showConfirm = null, simpleAlert = null } = useNotification(); const saveWired = () => @@ -60,6 +60,30 @@ const useWiredState = () => if(objectId <= 0) return; + const getInteractionTypeName = (furniData: any): string => + { + if(!furniData) return null; + + const rawValue = (furniData as any).interactionType + ?? (furniData as any).interactionTypeName + ?? (furniData as any).interactionTypeId; + + if(rawValue === undefined || rawValue === null) return null; + if(typeof rawValue !== 'string') return null; + + return rawValue.toLowerCase(); + }; + + const isAllowedInteraction = (furniData: any): boolean => + { + if(!allowedInteractionTypes || !allowedInteractionTypes.length) return true; + + const interactionType = getInteractionTypeName(furniData); + if(!interactionType) return true; + + return allowedInteractionTypes.some(type => (type && type.toLowerCase() === interactionType)); + }; + if(selectByType && category === RoomObjectCategory.FLOOR) { const roomId = GetRoomSession().roomId; @@ -71,6 +95,21 @@ const useWiredState = () => const sourceFurniData = GetSessionDataManager().getFloorItemData(typeId); if(!sourceFurniData) return; + if(!isAllowedInteraction(sourceFurniData)) + { + setFurniIds(prevValue => + { + if(!prevValue.includes(objectId)) return prevValue; + + const remaining = prevValue.filter(id => id !== objectId); + + WiredSelectionVisualizer.hide(objectId); + + return remaining; + }); + + return; + } const matchFurniLine = sourceFurniData.furniLine; const matchName = sourceFurniData.name; @@ -84,6 +123,8 @@ const useWiredState = () => const fd = GetSessionDataManager().getFloorItemData(tId); if(!fd) return false; + if(!isAllowedInteraction(fd)) return false; + const furniLineMatch = matchFurniLine && matchFurniLine.length > 0 && fd.furniLine === matchFurniLine; return furniLineMatch || fd.name === matchName; }; @@ -102,10 +143,8 @@ const useWiredState = () => } // ── Select a new group ────────────────────────────────────── - if(prevValue && prevValue.length) WiredSelectionVisualizer.clearSelectionShaderFromFurni(prevValue); - const allFloorObjects = GetRoomEngine().getRoomObjects(roomId, RoomObjectCategory.FLOOR); - const newIds: number[] = []; + const newIds = [ ...prevValue ]; const limit = trigger.maximumItemSelectionCount; for(const obj of allFloorObjects) @@ -116,14 +155,16 @@ const useWiredState = () => const tId = obj.model.getValue(RoomObjectVariable.FURNITURE_TYPE_ID); const fd = GetSessionDataManager().getFloorItemData(tId); if(!fd) continue; + if(!isAllowedInteraction(fd)) continue; const furniLineMatch = matchFurniLine && matchFurniLine.length > 0 && fd.furniLine === matchFurniLine; const matches = furniLineMatch || fd.name === matchName; - if(invertSelection ? !matches : matches) newIds.push(obj.id); + if(matches && !newIds.includes(obj.id)) newIds.push(obj.id); } - WiredSelectionVisualizer.applySelectionShaderToFurni(newIds); + const addedIds = newIds.filter(id => !prevValue.includes(id)); + if(addedIds.length) WiredSelectionVisualizer.applySelectionShaderToFurni(addedIds); return newIds; }); @@ -131,6 +172,34 @@ const useWiredState = () => return; } + if(category === RoomObjectCategory.FLOOR && allowedInteractionTypes && allowedInteractionTypes.length) + { + const roomId = GetRoomSession().roomId; + const clickedObject = GetRoomEngine().getRoomObject(roomId, objectId, RoomObjectCategory.FLOOR); + + if(!clickedObject) return; + + const typeId = clickedObject.model.getValue(RoomObjectVariable.FURNITURE_TYPE_ID); + const sourceFurniData = GetSessionDataManager().getFloorItemData(typeId); + + if(!sourceFurniData) return; + if(!isAllowedInteraction(sourceFurniData)) + { + setFurniIds(prevValue => + { + if(!prevValue.includes(objectId)) return prevValue; + + const remaining = prevValue.filter(id => id !== objectId); + + WiredSelectionVisualizer.hide(objectId); + + return remaining; + }); + + return; + } + } + setFurniIds(prevValue => { const newFurniIds = [ ...prevValue ]; @@ -217,13 +286,13 @@ const useWiredState = () => }); setAllowsFurni(WiredFurniType.STUFF_SELECTION_OPTION_NONE); setSelectByType(false); - setInvertSelection(false); setNeighborhoodTiles(null); setNeighborhoodInvert(false); + setAllowedInteractionTypes(null); }; }, [ trigger ]); - return { trigger, setTrigger, intParams, setIntParams, stringParam, setStringParam, furniIds, setFurniIds, actionDelay, setActionDelay, setAllowsFurni, saveWired, selectObjectForWired, setSelectByType, setInvertSelection, setNeighborhoodTiles, setNeighborhoodInvert }; + return { trigger, setTrigger, intParams, setIntParams, stringParam, setStringParam, furniIds, setFurniIds, actionDelay, setActionDelay, setAllowsFurni, saveWired, selectObjectForWired, setSelectByType, setNeighborhoodTiles, setNeighborhoodInvert, setAllowedInteractionTypes }; }; export const useWired = () => useBetween(useWiredState);