mirror of
https://github.com/duckietm/Nitro-V3.git
synced 2026-06-19 15:06:20 +00:00
256 lines
10 KiB
TypeScript
256 lines
10 KiB
TypeScript
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 } from '../WiredSourcesSelector';
|
|
import { WiredActionBaseView } from './WiredActionBaseView';
|
|
|
|
const ANTENNA_INTERACTION_TYPES = [ 'antenna' ];
|
|
|
|
const SOURCE_TRIGGER = 0;
|
|
const SOURCE_SELECTED = 100;
|
|
|
|
const FORWARD_ITEM_DELIMITER = ';';
|
|
|
|
type SelectionMode = 'antenna' | 'furni';
|
|
|
|
const parseForwardIds = (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 serializeForwardIds = (ids: number[]): string =>
|
|
{
|
|
if(!ids || !ids.length) return '';
|
|
|
|
return ids.filter(id => (id > 0)).join(FORWARD_ITEM_DELIMITER);
|
|
};
|
|
|
|
export const WiredActionSendSignalView: FC<{}> = () =>
|
|
{
|
|
const [ furniSource, setFurniSource ] = useState<number>(SOURCE_TRIGGER);
|
|
const [ userSource, setUserSource ] = useState<number>(SOURCE_TRIGGER);
|
|
const [ signalPerFurni, setSignalPerFurni ] = useState(false);
|
|
const [ signalPerUser, setSignalPerUser ] = useState(false);
|
|
const [ antennaIds, setAntennaIds ] = useState<number[]>([]);
|
|
const [ forwardFurniIds, setForwardFurniIds ] = useState<number[]>([]);
|
|
const [ selectionMode, setSelectionMode ] = useState<SelectionMode>('antenna');
|
|
const highlightedIds = useRef<number[]>([]);
|
|
|
|
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) setFurniSource(p[1]);
|
|
else setFurniSource(SOURCE_TRIGGER);
|
|
|
|
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 syncHighlights = useCallback((nextAntennaIds: number[], nextForwardFurniIds: number[], nextSelectionMode: SelectionMode, nextFurniSource: number) =>
|
|
{
|
|
if(highlightedIds.current.length)
|
|
{
|
|
WiredSelectionVisualizer.clearSelectionShaderFromFurni(highlightedIds.current);
|
|
WiredSelectionVisualizer.clearSecondarySelectionShaderFromFurni(highlightedIds.current);
|
|
}
|
|
|
|
const visibleForwardIds = (nextFurniSource === SOURCE_SELECTED) ? nextForwardFurniIds : [];
|
|
const activeIds = (nextSelectionMode === 'antenna') ? nextAntennaIds : visibleForwardIds;
|
|
const passiveIds = (nextSelectionMode === 'antenna') ? visibleForwardIds : nextAntennaIds;
|
|
const activeSet = new Set(activeIds);
|
|
const passiveOnlyIds = passiveIds.filter(id => !activeSet.has(id));
|
|
|
|
if(activeIds.length) WiredSelectionVisualizer.applySelectionShaderToFurni(activeIds);
|
|
if(passiveOnlyIds.length) WiredSelectionVisualizer.applySecondarySelectionShaderToFurni(passiveOnlyIds);
|
|
|
|
highlightedIds.current = Array.from(new Set([ ...activeIds, ...passiveOnlyIds ]));
|
|
}, []);
|
|
|
|
useEffect(() =>
|
|
{
|
|
syncHighlights(antennaIds, forwardFurniIds, selectionMode, furniSource);
|
|
}, [ antennaIds, forwardFurniIds, selectionMode, furniSource, syncHighlights ]);
|
|
|
|
const switchSelection = useCallback((mode: SelectionMode) =>
|
|
{
|
|
if(mode === selectionMode) return;
|
|
if(mode === 'furni' && furniSource !== SOURCE_SELECTED) return;
|
|
|
|
const nextAntennaIds = (selectionMode === 'antenna') ? [ ...furniIds ] : [ ...antennaIds ];
|
|
const nextForwardFurniIds = (selectionMode === 'furni') ? [ ...furniIds ] : [ ...forwardFurniIds ];
|
|
|
|
setAntennaIds(nextAntennaIds);
|
|
setForwardFurniIds(nextForwardFurniIds);
|
|
setSelectionMode(mode);
|
|
if(setFurniIds) setFurniIds([ ...((mode === 'antenna') ? nextAntennaIds : nextForwardFurniIds) ]);
|
|
}, [ selectionMode, furniSource, furniIds, antennaIds, forwardFurniIds, setFurniIds ]);
|
|
|
|
const onChangeFurniSource = (next: number) =>
|
|
{
|
|
const nextAntennaIds = (selectionMode === 'antenna') ? [ ...furniIds ] : [ ...antennaIds ];
|
|
|
|
setAntennaIds(nextAntennaIds);
|
|
if(forwardFurniIds.length || selectionMode === 'furni')
|
|
{
|
|
setForwardFurniIds([]);
|
|
}
|
|
|
|
if(selectionMode === 'furni')
|
|
{
|
|
if(setFurniIds) setFurniIds([ ...nextAntennaIds ]);
|
|
setSelectionMode('antenna');
|
|
}
|
|
|
|
setFurniSource(next);
|
|
};
|
|
|
|
const save = useCallback(() =>
|
|
{
|
|
const nextAntennaIds = (selectionMode === 'antenna') ? [ ...furniIds ] : [ ...antennaIds ];
|
|
const nextForwardFurniIds = (selectionMode === 'furni') ? [ ...furniIds ] : [ ...forwardFurniIds ];
|
|
|
|
setAntennaIds(nextAntennaIds);
|
|
setForwardFurniIds(nextForwardFurniIds);
|
|
|
|
if(selectionMode === 'furni')
|
|
{
|
|
setSelectionMode('antenna');
|
|
if(setFurniIds) setFurniIds([ ...nextAntennaIds ]);
|
|
}
|
|
|
|
const antennaSource = (nextAntennaIds && nextAntennaIds.length) ? nextAntennaIds[0] : 0;
|
|
|
|
setIntParams([
|
|
antennaSource,
|
|
furniSource,
|
|
userSource,
|
|
signalPerFurni ? 1 : 0,
|
|
signalPerUser ? 1 : 0,
|
|
0,
|
|
]);
|
|
|
|
setStringParam(serializeForwardIds(nextForwardFurniIds));
|
|
}, [ selectionMode, furniIds, antennaIds, furniSource, userSource, signalPerFurni, signalPerUser, forwardFurniIds, setFurniIds, setIntParams, setStringParam ]);
|
|
|
|
useEffect(() =>
|
|
{
|
|
return () =>
|
|
{
|
|
if(!highlightedIds.current.length) return;
|
|
|
|
WiredSelectionVisualizer.clearSelectionShaderFromFurni(highlightedIds.current);
|
|
WiredSelectionVisualizer.clearSecondarySelectionShaderFromFurni(highlightedIds.current);
|
|
highlightedIds.current = [];
|
|
};
|
|
}, []);
|
|
|
|
const selectionLimit = trigger?.maximumItemSelectionCount ?? 0;
|
|
const forwardSelectionEnabled = (furniSource === SOURCE_SELECTED);
|
|
|
|
return (
|
|
<WiredActionBaseView
|
|
hasSpecialInput={ true }
|
|
requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_BY_ID }
|
|
cardStyle={ { width: '400px' } }
|
|
save={ save }
|
|
footer={ (
|
|
<WiredSourcesSelector
|
|
showFurni={ true }
|
|
showUsers={ true }
|
|
furniSource={ furniSource }
|
|
userSource={ userSource }
|
|
onChangeFurni={ onChangeFurniSource }
|
|
onChangeUsers={ setUserSource } />
|
|
) }>
|
|
<div className="flex flex-col gap-3">
|
|
<div className="flex flex-col gap-1">
|
|
<Text bold>Antenne selezionate</Text>
|
|
<div className="flex items-center gap-2">
|
|
<Button
|
|
variant={ (selectionMode === 'antenna') ? 'primary' : 'secondary' }
|
|
onClick={ () => switchSelection('antenna') }>
|
|
Antenne
|
|
</Button>
|
|
<Text small>{ selectionLimit ? `${ antennaIds.length }/${ selectionLimit }` : antennaIds.length }</Text>
|
|
</div>
|
|
</div>
|
|
<div className="flex flex-col gap-1">
|
|
<Text bold>Furni selezionati</Text>
|
|
<div className="flex items-center gap-2">
|
|
<Button
|
|
variant={ (selectionMode === 'furni') ? 'primary' : 'secondary' }
|
|
disabled={ !forwardSelectionEnabled }
|
|
onClick={ () => switchSelection('furni') }>
|
|
Furni
|
|
</Button>
|
|
<Text small>{ selectionLimit ? `${ forwardFurniIds.length }/${ selectionLimit }` : forwardFurniIds.length }</Text>
|
|
</div>
|
|
</div>
|
|
|
|
<Text bold>{ LocalizeText('wiredfurni.params.signal.options') }</Text>
|
|
<div className="form-check">
|
|
<input
|
|
type="checkbox"
|
|
className="form-check-input"
|
|
id="signal-per-furni"
|
|
checked={ signalPerFurni }
|
|
onChange={ e => setSignalPerFurni(e.target.checked) } />
|
|
<label className="form-check-label" htmlFor="signal-per-furni">
|
|
<Text small>{ LocalizeText('wiredfurni.params.signal.split_furni') }</Text>
|
|
</label>
|
|
</div>
|
|
<div className="form-check">
|
|
<input
|
|
type="checkbox"
|
|
className="form-check-input"
|
|
id="signal-per-user"
|
|
checked={ signalPerUser }
|
|
onChange={ e => setSignalPerUser(e.target.checked) } />
|
|
<label className="form-check-label" htmlFor="signal-per-user">
|
|
<Text small>{ LocalizeText('wiredfurni.params.signal.split_users') }</Text>
|
|
</label>
|
|
</div>
|
|
</div>
|
|
</WiredActionBaseView>
|
|
);
|
|
};
|