feat: add advanced wired variable tools UI

This commit is contained in:
Lorenzune
2026-04-02 04:44:04 +02:00
parent 0a23bfaee4
commit 83540ff329
69 changed files with 10040 additions and 434 deletions
@@ -12,6 +12,7 @@ import { WiredTriggerClickUserView } from './WiredTriggerClickUserView';
import { WiredTriggerClockCounterView } from './WiredTriggerClockCounterView';
import { WiredTriggerCollisionView } from './WiredTriggerCollisionView';
import { WiredTriggerUserPerformsActionView } from './WiredTriggerUserPerformsActionView';
import { WiredTriggerVariableChangedView } from './WiredTriggerVariableChangedView';
import { WiredTriggeExecuteOnceView } from './WiredTriggerExecuteOnceView';
import { WiredTriggeExecutePeriodicallyLongView } from './WiredTriggerExecutePeriodicallyLongView';
import { WiredTriggeExecutePeriodicallyView } from './WiredTriggerExecutePeriodicallyView';
@@ -48,6 +49,8 @@ export const WiredTriggerLayoutView = (code: number) =>
return <WiredTriggerClickUserView />;
case WiredTriggerLayout.CLOCK_COUNTER:
return <WiredTriggerClockCounterView />;
case WiredTriggerLayout.VARIABLE_CHANGED:
return <WiredTriggerVariableChangedView />;
case WiredTriggerLayout.USER_PERFORMS_ACTION:
return <WiredTriggerUserPerformsActionView />;
case WiredTriggerLayout.COLLISION:
@@ -10,8 +10,6 @@ const FURNI_SOURCE_OPTIONS: WiredSourceOption[] = [
{ value: 200, label: 'wiredfurni.params.sources.furni.200' }
];
const ANTENNA_INTERACTION_TYPES = [ 'antenna' ];
const ANTENNA_ERROR_MESSAGE = 'Puoi selezionare solo furni antenna.';
const normalizeFurniSource = (value: number) => (FURNI_SOURCE_OPTIONS.some(option => (option.value === value)) ? value : 100);
export const WiredTriggerReceiveSignalView: FC<{}> = () =>
@@ -21,7 +19,7 @@ export const WiredTriggerReceiveSignalView: FC<{}> = () =>
const [ channel, setChannel ] = useState(0);
const [ furniSource, setFurniSource ] = useState(100);
const { trigger = null, setIntParams = null, setAllowedInteractionTypes = null, setAllowedInteractionErrorKey = null } = useWired();
const { trigger = null, setIntParams = null } = useWired();
const save = () => setIntParams([ channel, furniSource ]);
@@ -37,18 +35,6 @@ export const WiredTriggerReceiveSignalView: FC<{}> = () =>
else setFurniSource(100);
}, [ trigger ]);
useEffect(() =>
{
setAllowedInteractionTypes(ANTENNA_INTERACTION_TYPES);
setAllowedInteractionErrorKey(ANTENNA_ERROR_MESSAGE);
return () =>
{
setAllowedInteractionTypes(null);
setAllowedInteractionErrorKey(null);
};
}, [ setAllowedInteractionErrorKey, setAllowedInteractionTypes ]);
return (
<WiredTriggerBaseView
hasSpecialInput={ true }
@@ -0,0 +1,198 @@
import { FC, useEffect, useMemo, useState } from 'react';
import { LocalizeText, WiredFurniType } from '../../../../api';
import furniVariableIcon from '../../../../assets/images/wired/var/icon_source_furni.png';
import globalVariableIcon from '../../../../assets/images/wired/var/icon_source_global.png';
import userVariableIcon from '../../../../assets/images/wired/var/icon_source_user.png';
import { Text } from '../../../../common';
import { useWired, useWiredTools } from '../../../../hooks';
import { WiredVariablePicker } from '../WiredVariablePicker';
import { IWiredVariablePickerEntry, buildWiredVariablePickerEntries, createFallbackVariableEntry, flattenWiredVariablePickerEntries, normalizeVariableTokenFromWire } from '../WiredVariablePickerData';
import { WiredTriggerBaseView } from './WiredTriggerBaseView';
type VariableTargetType = 'user' | 'furni' | 'global';
const TARGET_USER = 0;
const TARGET_FURNI = 1;
const TARGET_GLOBAL = 3;
const TARGET_BUTTONS: Array<{ key: VariableTargetType; icon: string; }> = [
{ key: 'furni', icon: furniVariableIcon },
{ key: 'user', icon: userVariableIcon },
{ key: 'global', icon: globalVariableIcon }
];
const filterCustomEntries = (entries: IWiredVariablePickerEntry[]): IWiredVariablePickerEntry[] =>
{
return entries
.filter(entry => (entry.kind === 'custom'))
.map(entry => ({
...entry,
children: entry.children?.filter(child => (child.kind === 'custom'))
}));
};
const normalizeTargetType = (value: number): VariableTargetType =>
{
switch(value)
{
case TARGET_FURNI: return 'furni';
case TARGET_GLOBAL: return 'global';
default: return 'user';
}
};
const getTargetValue = (value: VariableTargetType) =>
{
switch(value)
{
case 'furni': return TARGET_FURNI;
case 'global': return TARGET_GLOBAL;
default: return TARGET_USER;
}
};
export const WiredTriggerVariableChangedView: FC<{}> = () =>
{
const { trigger = null, setIntParams = null, setStringParam = null } = useWired();
const { userVariableDefinitions = [], furniVariableDefinitions = [], roomVariableDefinitions = [] } = useWiredTools();
const [ targetType, setTargetType ] = useState<VariableTargetType>('user');
const [ variableToken, setVariableToken ] = useState('');
const [ createdEnabled, setCreatedEnabled ] = useState(true);
const [ valueChangedEnabled, setValueChangedEnabled ] = useState(true);
const [ increasedEnabled, setIncreasedEnabled ] = useState(true);
const [ decreasedEnabled, setDecreasedEnabled ] = useState(true);
const [ unchangedEnabled, setUnchangedEnabled ] = useState(true);
const [ deletedEnabled, setDeletedEnabled ] = useState(true);
const variableDefinitions = useMemo(() =>
{
switch(targetType)
{
case 'furni': return furniVariableDefinitions;
case 'global': return roomVariableDefinitions;
default: return userVariableDefinitions;
}
}, [ furniVariableDefinitions, roomVariableDefinitions, targetType, userVariableDefinitions ]);
const variableEntries = useMemo(() => filterCustomEntries(buildWiredVariablePickerEntries(targetType, 'condition', variableDefinitions)), [ targetType, variableDefinitions ]);
const resolvedVariableEntries = useMemo(() =>
{
if(!variableToken) return variableEntries;
if(flattenWiredVariablePickerEntries(variableEntries).some(entry => (entry.token === variableToken))) return variableEntries;
const fallbackEntry = createFallbackVariableEntry(targetType, variableToken);
return fallbackEntry && (fallbackEntry.kind === 'custom') ? [ fallbackEntry, ...variableEntries ] : variableEntries;
}, [ targetType, variableEntries, variableToken ]);
const effectiveCreatedEnabled = (targetType === 'global') ? false : createdEnabled;
const effectiveDeletedEnabled = (targetType === 'global') ? false : deletedEnabled;
const effectiveIncreasedEnabled = valueChangedEnabled && increasedEnabled;
const effectiveDecreasedEnabled = valueChangedEnabled && decreasedEnabled;
const effectiveUnchangedEnabled = valueChangedEnabled && unchangedEnabled;
useEffect(() =>
{
if(!trigger) return;
const intData = trigger.intData || [];
setTargetType(normalizeTargetType((intData.length > 0) ? intData[0] : TARGET_USER));
setVariableToken(normalizeVariableTokenFromWire(trigger.stringData || ''));
setCreatedEnabled((intData.length <= 1) || (intData[1] === 1));
setValueChangedEnabled((intData.length <= 2) || (intData[2] === 1));
setIncreasedEnabled((intData.length <= 3) || (intData[3] === 1));
setDecreasedEnabled((intData.length <= 4) || (intData[4] === 1));
setUnchangedEnabled((intData.length <= 5) || (intData[5] === 1));
setDeletedEnabled((intData.length <= 6) || (intData[6] === 1));
}, [ trigger ]);
useEffect(() =>
{
if(targetType !== 'global') return;
setCreatedEnabled(false);
setDeletedEnabled(false);
}, [ targetType ]);
const save = () =>
{
setStringParam(variableToken);
setIntParams([
getTargetValue(targetType),
effectiveCreatedEnabled ? 1 : 0,
valueChangedEnabled ? 1 : 0,
effectiveIncreasedEnabled ? 1 : 0,
effectiveDecreasedEnabled ? 1 : 0,
effectiveUnchangedEnabled ? 1 : 0,
effectiveDeletedEnabled ? 1 : 0
]);
};
return (
<WiredTriggerBaseView hasSpecialInput={ true } requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE } save={ save }>
<div className="nitro-wired__give-var" style={ { width: 244 } }>
<div className="nitro-wired__give-var-heading">
<Text>{ LocalizeText('wiredfurni.params.variables.variable_selection') }</Text>
<div className="nitro-wired__give-var-targets">
{ TARGET_BUTTONS.map(button => (
<button
key={ button.key }
type="button"
className={ `nitro-wired__give-var-target nitro-wired__give-var-target--${ button.key } ${ targetType === button.key ? 'is-active' : '' }` }
onClick={ () =>
{
if(targetType === button.key) return;
setTargetType(button.key);
setVariableToken('');
} }>
<img src={ button.icon } alt={ button.key } />
</button>
)) }
</div>
</div>
<WiredVariablePicker
entries={ resolvedVariableEntries }
recentScope="variable-triggers"
selectedToken={ variableToken }
onSelect={ entry => setVariableToken(entry.token) } />
<div className="nitro-wired__divider" />
<div className="flex flex-col gap-1">
<Text bold>{ LocalizeText('wiredfurni.params.variables.trigger_options') }</Text>
<label className="flex items-center gap-1">
<input checked={ effectiveCreatedEnabled } className="form-check-input" disabled={ targetType === 'global' } type="checkbox" onChange={ event => setCreatedEnabled(event.target.checked) } />
<Text>{ LocalizeText('wiredfurni.params.variables.trigger_options.0') }</Text>
</label>
<label className="flex items-center gap-1">
<input checked={ valueChangedEnabled } className="form-check-input" type="checkbox" onChange={ event => setValueChangedEnabled(event.target.checked) } />
<Text>{ LocalizeText('wiredfurni.params.variables.trigger_options.1') }</Text>
</label>
<div className="ml-3 flex flex-col gap-1">
<label className="flex items-center gap-1">
<input checked={ effectiveIncreasedEnabled } className="form-check-input" disabled={ !valueChangedEnabled } type="checkbox" onChange={ event => setIncreasedEnabled(event.target.checked) } />
<Text>{ LocalizeText('wiredfurni.params.variables.trigger_options.1.0') }</Text>
</label>
<label className="flex items-center gap-1">
<input checked={ effectiveDecreasedEnabled } className="form-check-input" disabled={ !valueChangedEnabled } type="checkbox" onChange={ event => setDecreasedEnabled(event.target.checked) } />
<Text>{ LocalizeText('wiredfurni.params.variables.trigger_options.1.1') }</Text>
</label>
<label className="flex items-center gap-1">
<input checked={ effectiveUnchangedEnabled } className="form-check-input" disabled={ !valueChangedEnabled } type="checkbox" onChange={ event => setUnchangedEnabled(event.target.checked) } />
<Text>{ LocalizeText('wiredfurni.params.variables.trigger_options.1.2') }</Text>
</label>
</div>
<label className="flex items-center gap-1">
<input checked={ effectiveDeletedEnabled } className="form-check-input" disabled={ targetType === 'global' } type="checkbox" onChange={ event => setDeletedEnabled(event.target.checked) } />
<Text>{ LocalizeText('wiredfurni.params.variables.trigger_options.2') }</Text>
</label>
</div>
</div>
</WiredTriggerBaseView>
);
};