feat(wired-ui): expand advanced wired editors

This commit is contained in:
Lorenzune
2026-03-21 14:27:57 +01:00
parent cb0a9242b5
commit 27cb71f0cc
90 changed files with 3529 additions and 538 deletions
@@ -5,31 +5,43 @@ import { Button, LayoutAvatarImageView, Text } from '../../../../common';
import { useWired } from '../../../../hooks';
import { NitroInput } from '../../../../layout';
import { WiredActionBaseView } from './WiredActionBaseView';
import { BOT_SOURCES, WiredSourcesSelector } from '../WiredSourcesSelector';
const DEFAULT_FIGURE: string = 'hd-180-1.ch-210-66.lg-270-82.sh-290-81';
const normalizeBotSource = (value: number, hasBotName = false) => (BOT_SOURCES.some(option => (option.value === value)) ? value : (hasBotName ? 100 : 0));
export const WiredActionBotChangeFigureView: FC<{}> = props =>
{
const [ botName, setBotName ] = useState('');
const [ figure, setFigure ] = useState('');
const { trigger = null, setStringParam = null } = useWired();
const [ botSource, setBotSource ] = useState<number>(100);
const { trigger = null, setStringParam = null, setIntParams = null } = useWired();
const save = () => setStringParam((botName + WIRED_STRING_DELIMETER + figure));
const save = () =>
{
setStringParam(((botSource === 100) ? botName : '') + WIRED_STRING_DELIMETER + figure);
setIntParams([ botSource ]);
};
useEffect(() =>
{
const data = trigger.stringData.split(WIRED_STRING_DELIMETER);
const nextBotName = (data.length > 0) ? data[0] : '';
if(data.length > 0) setBotName(data[0]);
if(data.length > 0) setBotName(nextBotName);
if(data.length > 1) setFigure(data[1].length > 0 ? data[1] : DEFAULT_FIGURE);
else setFigure(DEFAULT_FIGURE);
setBotSource((trigger.intData.length > 0) ? normalizeBotSource(trigger.intData[0], (nextBotName.length > 0)) : normalizeBotSource(-1, (nextBotName.length > 0)));
}, [ trigger ]);
return (
<WiredActionBaseView hasSpecialInput={ true } requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE } save={ save }>
<div className="flex flex-col gap-1">
<Text bold>{ LocalizeText('wiredfurni.params.bot.name') }</Text>
<NitroInput maxLength={ 32 } type="text" value={ botName } onChange={ event => setBotName(event.target.value) } />
</div>
<WiredActionBaseView hasSpecialInput={ true } requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE } save={ save } footer={ <WiredSourcesSelector showUsers={ true } userSource={ botSource } userSources={ BOT_SOURCES } usersTitle="wiredfurni.params.sources.users.title.bots" onChangeUsers={ value => setBotSource(normalizeBotSource(value, (botName.length > 0))) } /> }>
{ (botSource === 100) &&
<div className="flex flex-col gap-1">
<Text bold>{ LocalizeText('wiredfurni.params.bot.name') }</Text>
<NitroInput maxLength={ 32 } type="text" value={ botName } onChange={ event => setBotName(event.target.value) } />
</div> }
<div className="flex items-center justify-center">
<LayoutAvatarImageView direction={ 4 } figure={ figure } />
<Button onClick={ event => setFigure(GetSessionDataManager().figure) }>{ LocalizeText('wiredfurni.params.capture.figure') }</Button>
@@ -4,13 +4,16 @@ import { Text } from '../../../../common';
import { useWired } from '../../../../hooks';
import { NitroInput } from '../../../../layout';
import { WiredActionBaseView } from './WiredActionBaseView';
import { WiredSourcesSelector } from '../WiredSourcesSelector';
import { BOT_SOURCES, WiredSourcesSelector } from '../WiredSourcesSelector';
const normalizeBotSource = (value: number, hasBotName = false) => (BOT_SOURCES.some(option => (option.value === value)) ? value : (hasBotName ? 100 : 0));
export const WiredActionBotFollowAvatarView: FC<{}> = props =>
{
const [ botName, setBotName ] = useState('');
const [ followMode, setFollowMode ] = useState(-1);
const { trigger = null, setStringParam = null, setIntParams = null } = useWired();
const [ botSource, setBotSource ] = useState<number>(100);
const [ userSource, setUserSource ] = useState<number>(() =>
{
if(trigger?.intData?.length > 1) return trigger.intData[1];
@@ -19,15 +22,17 @@ export const WiredActionBotFollowAvatarView: FC<{}> = props =>
const save = () =>
{
setStringParam(botName);
setIntParams([ followMode, userSource ]);
setStringParam((botSource === 100) ? botName : '');
setIntParams([ followMode, userSource, botSource ]);
};
useEffect(() =>
{
setBotName(trigger.stringData);
const nextBotName = trigger.stringData || '';
setBotName(nextBotName);
setFollowMode((trigger.intData.length > 0) ? trigger.intData[0] : 0);
setUserSource((trigger.intData.length > 1) ? trigger.intData[1] : 0);
setBotSource((trigger.intData.length > 2) ? normalizeBotSource(trigger.intData[2], (nextBotName.length > 0)) : normalizeBotSource(-1, (nextBotName.length > 0)));
}, [ trigger ]);
return (
@@ -35,11 +40,18 @@ export const WiredActionBotFollowAvatarView: FC<{}> = props =>
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>{ LocalizeText('wiredfurni.params.bot.name') }</Text>
<NitroInput maxLength={ 32 } type="text" value={ botName } onChange={ event => setBotName(event.target.value) } />
</div>
footer={
<div className="flex flex-col gap-2">
<WiredSourcesSelector showUsers={ true } userSource={ userSource } onChangeUsers={ setUserSource } />
<hr className="m-0 bg-dark" />
<WiredSourcesSelector showUsers={ true } userSource={ botSource } userSources={ BOT_SOURCES } usersTitle="wiredfurni.params.sources.users.title.bots" onChangeUsers={ value => setBotSource(normalizeBotSource(value, (botName.length > 0))) } />
</div>
}>
{ (botSource === 100) &&
<div className="flex flex-col gap-1">
<Text bold>{ LocalizeText('wiredfurni.params.bot.name') }</Text>
<NitroInput maxLength={ 32 } type="text" value={ botName } onChange={ event => setBotName(event.target.value) } />
</div> }
<div className="flex flex-col gap-1">
<div className="flex items-center gap-1">
<input checked={ (followMode === 1) } className="form-check-input" id="followMode1" name="followMode" type="radio" onChange={ event => setFollowMode(1) } />
@@ -4,32 +4,47 @@ import { Text } from '../../../../common';
import { useWired } from '../../../../hooks';
import { NitroInput } from '../../../../layout';
import { WiredActionBaseView } from './WiredActionBaseView';
import { WiredSourcesSelector } from '../WiredSourcesSelector';
import { WiredSourceOption, WiredSourcesSelector } from '../WiredSourcesSelector';
import { WiredHandItemField } from '../WiredHandItemField';
const ALLOWED_HAND_ITEM_IDS: number[] = [ 2, 5, 7, 8, 9, 10, 27 ];
const USER_SOURCE_OPTIONS: WiredSourceOption[] = [
{ value: 0, label: 'wiredfurni.params.sources.users.0' },
{ value: 200, label: 'wiredfurni.params.sources.users.200' },
{ value: 201, label: 'wiredfurni.params.sources.users.201' }
];
const BOT_SOURCE_OPTIONS: WiredSourceOption[] = [
{ value: 0, label: 'wiredfurni.params.sources.users.0' },
{ value: 100, label: 'wiredfurni.params.sources.users.100' },
{ value: 200, label: 'wiredfurni.params.sources.users.200' },
{ value: 201, label: 'wiredfurni.params.sources.users.201' }
];
const normalizeUserSource = (value: number) => (USER_SOURCE_OPTIONS.some(option => (option.value === value)) ? value : 0);
const normalizeBotSource = (value: number, hasBotName = false) => (BOT_SOURCE_OPTIONS.some(option => (option.value === value)) ? value : (hasBotName ? 100 : 0));
export const WiredActionBotGiveHandItemView: FC<{}> = props =>
{
const [ botName, setBotName ] = useState('');
const [ handItemId, setHandItemId ] = useState(-1);
const { trigger = null, setStringParam = null, setIntParams = null } = useWired();
const [ userSource, setUserSource ] = useState<number>(() =>
{
if(trigger?.intData?.length > 1) return trigger.intData[1];
return 0;
});
const [ userSource, setUserSource ] = useState<number>(0);
const [ botSource, setBotSource ] = useState<number>(100);
const save = () =>
{
setStringParam(botName);
setIntParams([ handItemId, userSource ]);
setStringParam((botSource === 100) ? botName : '');
setIntParams([ handItemId, userSource, botSource ]);
};
useEffect(() =>
{
setBotName(trigger.stringData);
const nextBotName = trigger.stringData || '';
setBotName(nextBotName);
setHandItemId((trigger.intData.length > 0) ? trigger.intData[0] : 0);
setUserSource((trigger.intData.length > 1) ? trigger.intData[1] : 0);
setUserSource((trigger.intData.length > 1) ? normalizeUserSource(trigger.intData[1]) : 0);
setBotSource((trigger.intData.length > 2) ? normalizeBotSource(trigger.intData[2], (nextBotName.length > 0)) : normalizeBotSource(-1, (nextBotName.length > 0)));
}, [ trigger ]);
return (
@@ -37,18 +52,23 @@ export const WiredActionBotGiveHandItemView: FC<{}> = props =>
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>{ LocalizeText('wiredfurni.params.bot.name') }</Text>
<NitroInput maxLength={ 32 } type="text" value={ botName } onChange={ event => setBotName(event.target.value) } />
</div>
<div className="flex flex-col gap-1">
<Text bold>{ LocalizeText('wiredfurni.params.handitem') }</Text>
<select className="form-select form-select-sm" value={ handItemId } onChange={ event => setHandItemId(parseInt(event.target.value)) }>
<option value="0">------</option>
{ ALLOWED_HAND_ITEM_IDS.map(value => <option key={ value } value={ value }>{ LocalizeText(`handitem${ value }`) }</option>) }
</select>
footer={
<div className="flex flex-col gap-2">
<WiredSourcesSelector showUsers={ true } userSource={ userSource } userSources={ USER_SOURCE_OPTIONS } onChangeUsers={ setUserSource } />
<hr className="m-0 bg-dark" />
<WiredSourcesSelector showUsers={ true } userSource={ botSource } userSources={ BOT_SOURCE_OPTIONS } usersTitle="wiredfurni.params.sources.users.title.bots" onChangeUsers={ value => setBotSource(normalizeBotSource(value, (botName.length > 0))) } />
</div>
}>
<div className="form-check">
<input checked={ (botSource === 100) } className="form-check-input" id="botGiveHandItemUseNamedBot" type="checkbox" onChange={ event => setBotSource(event.target.checked ? 100 : 0) } />
<label className="form-check-label" htmlFor="botGiveHandItemUseNamedBot">{ LocalizeText('wiredfurni.params.bot.usage') }</label>
</div>
{ (botSource === 100) &&
<div className="flex flex-col gap-1">
<Text bold>{ LocalizeText('wiredfurni.params.bot.name') }</Text>
<NitroInput maxLength={ 32 } type="text" value={ botName } onChange={ event => setBotName(event.target.value) } />
</div> }
<WiredHandItemField handItemId={ handItemId } onChange={ setHandItemId } showCopyButton={ true } />
</WiredActionBaseView>
);
};
@@ -4,11 +4,14 @@ import { Text } from '../../../../common';
import { useWired } from '../../../../hooks';
import { NitroInput } from '../../../../layout';
import { WiredActionBaseView } from './WiredActionBaseView';
import { WiredSourcesSelector } from '../WiredSourcesSelector';
import { BOT_SOURCES, WiredSourcesSelector } from '../WiredSourcesSelector';
const normalizeBotSource = (value: number, hasBotName = false) => (BOT_SOURCES.some(option => (option.value === value)) ? value : (hasBotName ? 100 : 0));
export const WiredActionBotMoveView: FC<{}> = props =>
{
const [ botName, setBotName ] = useState('');
const [ botSource, setBotSource ] = useState<number>(100);
const { trigger = null, setStringParam = null, setIntParams = null } = useWired();
const [ furniSource, setFurniSource ] = useState<number>(() =>
@@ -19,18 +22,21 @@ export const WiredActionBotMoveView: FC<{}> = props =>
const save = () =>
{
setStringParam(botName);
setIntParams([ furniSource ]);
setStringParam((botSource === 100) ? botName : '');
setIntParams([ furniSource, botSource ]);
};
useEffect(() =>
{
if(!trigger) return;
setBotName(trigger.stringData);
const nextBotName = trigger.stringData || '';
setBotName(nextBotName);
if(trigger.intData.length >= 1) setFurniSource(trigger.intData[0]);
else setFurniSource((trigger.selectedItems?.length ?? 0) > 0 ? 100 : 0);
setBotSource((trigger.intData.length >= 2) ? normalizeBotSource(trigger.intData[1], (nextBotName.length > 0)) : normalizeBotSource(-1, (nextBotName.length > 0)));
}, [ trigger ]);
const onChangeFurniSource = (next: number) => setFurniSource(next);
@@ -42,11 +48,18 @@ export const WiredActionBotMoveView: FC<{}> = props =>
hasSpecialInput={ true }
requiresFurni={ requiresFurni }
save={ save }
footer={ <WiredSourcesSelector showFurni={ true } furniSource={ furniSource } onChangeFurni={ onChangeFurniSource } /> }>
<div className="flex flex-col gap-1">
<Text bold>{ LocalizeText('wiredfurni.params.bot.name') }</Text>
<NitroInput maxLength={ 32 } type="text" value={ botName } onChange={ event => setBotName(event.target.value) } />
</div>
footer={
<div className="flex flex-col gap-2">
<WiredSourcesSelector showFurni={ true } furniSource={ furniSource } onChangeFurni={ onChangeFurniSource } />
<hr className="m-0 bg-dark" />
<WiredSourcesSelector showUsers={ true } userSource={ botSource } userSources={ BOT_SOURCES } usersTitle="wiredfurni.params.sources.users.title.bots" onChangeUsers={ value => setBotSource(normalizeBotSource(value, (botName.length > 0))) } />
</div>
}>
{ (botSource === 100) &&
<div className="flex flex-col gap-1">
<Text bold>{ LocalizeText('wiredfurni.params.bot.name') }</Text>
<NitroInput maxLength={ 32 } type="text" value={ botName } onChange={ event => setBotName(event.target.value) } />
</div> }
</WiredActionBaseView>
);
};
@@ -4,13 +4,16 @@ import { Text } from '../../../../common';
import { useWired } from '../../../../hooks';
import { NitroInput } from '../../../../layout';
import { WiredActionBaseView } from './WiredActionBaseView';
import { WiredSourcesSelector } from '../WiredSourcesSelector';
import { BOT_SOURCES, WiredSourcesSelector } from '../WiredSourcesSelector';
const normalizeBotSource = (value: number, hasBotName = false) => (BOT_SOURCES.some(option => (option.value === value)) ? value : (hasBotName ? 100 : 0));
export const WiredActionBotTalkToAvatarView: FC<{}> = props =>
{
const [ botName, setBotName ] = useState('');
const [ message, setMessage ] = useState('');
const [ talkMode, setTalkMode ] = useState(-1);
const [ botSource, setBotSource ] = useState<number>(100);
const { trigger = null, setStringParam = null, setIntParams = null } = useWired();
const [ userSource, setUserSource ] = useState<number>(() =>
{
@@ -20,19 +23,21 @@ export const WiredActionBotTalkToAvatarView: FC<{}> = props =>
const save = () =>
{
setStringParam(botName + WIRED_STRING_DELIMETER + message);
setIntParams([ talkMode, userSource ]);
setStringParam(((botSource === 100) ? botName : '') + WIRED_STRING_DELIMETER + message);
setIntParams([ talkMode, userSource, botSource ]);
};
useEffect(() =>
{
const data = trigger.stringData.split(WIRED_STRING_DELIMETER);
const nextBotName = (data.length > 0) ? data[0] : '';
if(data.length > 0) setBotName(data[0]);
if(data.length > 0) setBotName(nextBotName);
if(data.length > 1) setMessage(data[1].length > 0 ? data[1] : '');
setTalkMode((trigger.intData.length > 0) ? trigger.intData[0] : 0);
setUserSource((trigger.intData.length > 1) ? trigger.intData[1] : 0);
setBotSource((trigger.intData.length > 2) ? normalizeBotSource(trigger.intData[2], (nextBotName.length > 0)) : normalizeBotSource(-1, (nextBotName.length > 0)));
}, [ trigger ]);
return (
@@ -40,11 +45,18 @@ export const WiredActionBotTalkToAvatarView: FC<{}> = props =>
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>{ LocalizeText('wiredfurni.params.bot.name') }</Text>
<NitroInput maxLength={ 32 } type="text" value={ botName } onChange={ event => setBotName(event.target.value) } />
</div>
footer={
<div className="flex flex-col gap-2">
<WiredSourcesSelector showUsers={ true } userSource={ userSource } onChangeUsers={ setUserSource } />
<hr className="m-0 bg-dark" />
<WiredSourcesSelector showUsers={ true } userSource={ botSource } userSources={ BOT_SOURCES } usersTitle="wiredfurni.params.sources.users.title.bots" onChangeUsers={ value => setBotSource(normalizeBotSource(value, (botName.length > 0))) } />
</div>
}>
{ (botSource === 100) &&
<div className="flex flex-col gap-1">
<Text bold>{ LocalizeText('wiredfurni.params.bot.name') }</Text>
<NitroInput maxLength={ 32 } type="text" value={ botName } onChange={ event => setBotName(event.target.value) } />
</div> }
<div className="flex flex-col gap-1">
<Text bold>{ LocalizeText('wiredfurni.params.message') }</Text>
<NitroInput maxLength={ GetConfigurationValue<number>('wired.action.bot.talk.to.avatar.max.length', 64) } type="text" value={ message } onChange={ event => setMessage(event.target.value) } />
@@ -4,36 +4,43 @@ import { Text } from '../../../../common';
import { useWired } from '../../../../hooks';
import { NitroInput } from '../../../../layout';
import { WiredActionBaseView } from './WiredActionBaseView';
import { BOT_SOURCES, WiredSourcesSelector } from '../WiredSourcesSelector';
const normalizeBotSource = (value: number, hasBotName = false) => (BOT_SOURCES.some(option => (option.value === value)) ? value : (hasBotName ? 100 : 0));
export const WiredActionBotTalkView: FC<{}> = props =>
{
const [ botName, setBotName ] = useState('');
const [ message, setMessage ] = useState('');
const [ talkMode, setTalkMode ] = useState(-1);
const [ botSource, setBotSource ] = useState<number>(100);
const { trigger = null, setStringParam = null, setIntParams = null } = useWired();
const save = () =>
{
setStringParam(botName + WIRED_STRING_DELIMETER + message);
setIntParams([ talkMode ]);
setStringParam(((botSource === 100) ? botName : '') + WIRED_STRING_DELIMETER + message);
setIntParams([ talkMode, botSource ]);
};
useEffect(() =>
{
const data = trigger.stringData.split(WIRED_STRING_DELIMETER);
const nextBotName = (data.length > 0) ? data[0] : '';
if(data.length > 0) setBotName(data[0]);
if(data.length > 0) setBotName(nextBotName);
if(data.length > 1) setMessage(data[1].length > 0 ? data[1] : '');
setTalkMode((trigger.intData.length > 0) ? trigger.intData[0] : 0);
setBotSource((trigger.intData.length > 1) ? normalizeBotSource(trigger.intData[1], (nextBotName.length > 0)) : normalizeBotSource(-1, (nextBotName.length > 0)));
}, [ trigger ]);
return (
<WiredActionBaseView hasSpecialInput={ true } requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE } save={ save }>
<div className="flex flex-col gap-1">
<Text bold>{ LocalizeText('wiredfurni.params.bot.name') }</Text>
<NitroInput maxLength={ 32 } type="text" value={ botName } onChange={ event => setBotName(event.target.value) } />
</div>
<WiredActionBaseView hasSpecialInput={ true } requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE } save={ save } footer={ <WiredSourcesSelector showUsers={ true } userSource={ botSource } userSources={ BOT_SOURCES } usersTitle="wiredfurni.params.sources.users.title.bots" onChangeUsers={ value => setBotSource(normalizeBotSource(value, (botName.length > 0))) } /> }>
{ (botSource === 100) &&
<div className="flex flex-col gap-1">
<Text bold>{ LocalizeText('wiredfurni.params.bot.name') }</Text>
<NitroInput maxLength={ 32 } type="text" value={ botName } onChange={ event => setBotName(event.target.value) } />
</div> }
<div className="flex flex-col gap-1">
<Text bold>{ LocalizeText('wiredfurni.params.message') }</Text>
<NitroInput maxLength={ GetConfigurationValue<number>('wired.action.bot.talk.max.length', 64) } type="text" value={ message } onChange={ event => setMessage(event.target.value) } />
@@ -4,11 +4,14 @@ import { Text } from '../../../../common';
import { useWired } from '../../../../hooks';
import { NitroInput } from '../../../../layout';
import { WiredActionBaseView } from './WiredActionBaseView';
import { WiredSourcesSelector } from '../WiredSourcesSelector';
import { BOT_SOURCES, WiredSourcesSelector } from '../WiredSourcesSelector';
const normalizeBotSource = (value: number, hasBotName = false) => (BOT_SOURCES.some(option => (option.value === value)) ? value : (hasBotName ? 100 : 0));
export const WiredActionBotTeleportView: FC<{}> = props =>
{
const [ botName, setBotName ] = useState('');
const [ botSource, setBotSource ] = useState<number>(100);
const { trigger = null, setStringParam = null, setIntParams = null } = useWired();
const [ furniSource, setFurniSource ] = useState<number>(() =>
@@ -19,18 +22,21 @@ export const WiredActionBotTeleportView: FC<{}> = props =>
const save = () =>
{
setStringParam(botName);
setIntParams([ furniSource ]);
setStringParam((botSource === 100) ? botName : '');
setIntParams([ furniSource, botSource ]);
};
useEffect(() =>
{
if(!trigger) return;
setBotName(trigger.stringData);
const nextBotName = trigger.stringData || '';
setBotName(nextBotName);
if(trigger.intData.length >= 1) setFurniSource(trigger.intData[0]);
else setFurniSource((trigger.selectedItems?.length ?? 0) > 0 ? 100 : 0);
setBotSource((trigger.intData.length >= 2) ? normalizeBotSource(trigger.intData[1], (nextBotName.length > 0)) : normalizeBotSource(-1, (nextBotName.length > 0)));
}, [ trigger ]);
const onChangeFurniSource = (next: number) => setFurniSource(next);
@@ -42,11 +48,18 @@ export const WiredActionBotTeleportView: FC<{}> = props =>
hasSpecialInput={ true }
requiresFurni={ requiresFurni }
save={ save }
footer={ <WiredSourcesSelector showFurni={ true } furniSource={ furniSource } onChangeFurni={ onChangeFurniSource } /> }>
<div className="flex flex-col gap-1">
<Text bold>{ LocalizeText('wiredfurni.params.bot.name') }</Text>
<NitroInput maxLength={ 32 } type="text" value={ botName } onChange={ event => setBotName(event.target.value) } />
</div>
footer={
<div className="flex flex-col gap-2">
<WiredSourcesSelector showFurni={ true } furniSource={ furniSource } onChangeFurni={ onChangeFurniSource } />
<hr className="m-0 bg-dark" />
<WiredSourcesSelector showUsers={ true } userSource={ botSource } userSources={ BOT_SOURCES } usersTitle="wiredfurni.params.sources.users.title.bots" onChangeUsers={ value => setBotSource(normalizeBotSource(value, (botName.length > 0))) } />
</div>
}>
{ (botSource === 100) &&
<div className="flex flex-col gap-1">
<Text bold>{ LocalizeText('wiredfurni.params.bot.name') }</Text>
<NitroInput maxLength={ 32 } type="text" value={ botName } onChange={ event => setBotName(event.target.value) } />
</div> }
</WiredActionBaseView>
);
};
@@ -7,24 +7,24 @@ import { WiredActionBaseView } from './WiredActionBaseView';
export const WiredActionGiveScoreToPredefinedTeamView: FC<{}> = props =>
{
const [ points, setPoints ] = useState(1);
const [ time, setTime ] = useState(1);
const [ operation, setOperation ] = useState(0);
const [ selectedTeam, setSelectedTeam ] = useState(1);
const { trigger = null, setIntParams = null } = useWired();
const save = () => setIntParams([ points, time, selectedTeam ]);
const save = () => setIntParams([ points, operation, selectedTeam ]);
useEffect(() =>
{
if(trigger.intData.length >= 2)
if(trigger.intData.length >= 3)
{
setPoints(trigger.intData[0]);
setTime(trigger.intData[1]);
setOperation(trigger.intData[1]);
setSelectedTeam(trigger.intData[2]);
}
else
{
setPoints(1);
setTime(1);
setOperation(0);
setSelectedTeam(1);
}
}, [ trigger ]);
@@ -40,12 +40,13 @@ export const WiredActionGiveScoreToPredefinedTeamView: FC<{}> = props =>
onChange={ event => setPoints(event) } />
</div>
<div className="flex flex-col gap-1">
<Text bold>{ LocalizeText('wiredfurni.params.settimesingame', [ 'times' ], [ time.toString() ]) }</Text>
<Slider
max={ 10 }
min={ 1 }
value={ time }
onChange={ event => setTime(event) } />
<Text bold>{ LocalizeText('wiredfurni.params.choose_type') }</Text>
{ [ 0, 1 ].map(value => (
<label key={ value } className="flex items-center gap-1">
<input checked={ (operation === value) } className="form-check-input" name="pointsOperation" type="radio" onChange={ () => setOperation(value) } />
<Text>{ LocalizeText(`wiredfurni.params.points_operation.${ value }`) }</Text>
</label>
)) }
</div>
<div className="flex flex-col gap-1">
<Text bold>{ LocalizeText('wiredfurni.params.team') }</Text>
@@ -8,7 +8,7 @@ import { WiredSourcesSelector } from '../WiredSourcesSelector';
export const WiredActionGiveScoreView: FC<{}> = props =>
{
const [ points, setPoints ] = useState(1);
const [ time, setTime ] = useState(1);
const [ operation, setOperation ] = useState(0);
const { trigger = null, setIntParams = null } = useWired();
const [ userSource, setUserSource ] = useState<number>(() =>
{
@@ -16,19 +16,19 @@ export const WiredActionGiveScoreView: FC<{}> = props =>
return 0;
});
const save = () => setIntParams([ points, time, userSource ]);
const save = () => setIntParams([ points, operation, userSource ]);
useEffect(() =>
{
if(trigger.intData.length >= 2)
{
setPoints(trigger.intData[0]);
setTime(trigger.intData[1]);
setOperation(trigger.intData[1]);
}
else
{
setPoints(1);
setTime(1);
setOperation(0);
}
setUserSource((trigger.intData.length > 2) ? trigger.intData[2] : 0);
@@ -49,12 +49,13 @@ export const WiredActionGiveScoreView: FC<{}> = props =>
onChange={ event => setPoints(event) } />
</div>
<div className="flex flex-col gap-1">
<Text bold>{ LocalizeText('wiredfurni.params.settimesingame', [ 'times' ], [ time.toString() ]) }</Text>
<Slider
max={ 10 }
min={ 1 }
value={ time }
onChange={ event => setTime(event) } />
<Text bold>{ LocalizeText('wiredfurni.params.choose_type') }</Text>
{ [ 0, 1 ].map(value => (
<label key={ value } className="flex items-center gap-1">
<input checked={ (operation === value) } className="form-check-input" name="pointsOperation" type="radio" onChange={ () => setOperation(value) } />
<Text>{ LocalizeText(`wiredfurni.params.points_operation.${ value }`) }</Text>
</label>
)) }
</div>
</WiredActionBaseView>
);
@@ -7,20 +7,32 @@ import { WiredSourcesSelector } from '../WiredSourcesSelector';
export const WiredActionJoinTeamView: FC<{}> = props =>
{
const [ selectedTeam, setSelectedTeam ] = useState(-1);
const [ selectedTeamType, setSelectedTeamType ] = useState(0);
const [ selectedTeam, setSelectedTeam ] = useState(1);
const { trigger = null, setIntParams = null } = useWired();
const [ userSource, setUserSource ] = useState<number>(() =>
{
if(trigger?.intData?.length > 2) return trigger.intData[2];
if(trigger?.intData?.length > 1) return trigger.intData[1];
return 0;
});
const save = () => setIntParams([ selectedTeam, userSource ]);
const save = () => setIntParams([ selectedTeamType, selectedTeam, userSource ]);
useEffect(() =>
{
setSelectedTeam((trigger.intData.length > 0) ? trigger.intData[0] : 0);
setUserSource((trigger.intData.length > 1) ? trigger.intData[1] : 0);
if(trigger.intData.length > 2)
{
setSelectedTeamType(trigger.intData[0]);
setSelectedTeam(trigger.intData[1]);
setUserSource(trigger.intData[2]);
}
else
{
setSelectedTeamType(0);
setSelectedTeam((trigger.intData.length > 0) ? trigger.intData[0] : 1);
setUserSource((trigger.intData.length > 1) ? trigger.intData[1] : 0);
}
}, [ trigger ]);
return (
@@ -29,6 +41,22 @@ export const WiredActionJoinTeamView: FC<{}> = props =>
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>{ LocalizeText('wiredfurni.params.choose_type') }</Text>
{ [
{ value: 0, label: 'Wired' },
{ value: 1, label: 'Banzai' },
{ value: 2, label: 'Freeze' }
].map(option =>
{
return (
<div key={ option.value } className="flex gap-1">
<input checked={ (selectedTeamType === option.value) } className="form-check-input" id={ `selectedTeamType${ option.value }` } name="selectedTeamType" type="radio" onChange={ () => setSelectedTeamType(option.value) } />
<Text>{ option.label }</Text>
</div>
);
}) }
</div>
<div className="flex flex-col gap-1">
<Text bold>{ LocalizeText('wiredfurni.params.team') }</Text>
{ [ 1, 2, 3, 4 ].map(team =>
@@ -9,8 +9,20 @@ import { WiredActionSendSignalView } from './WiredActionSendSignalView';
import { WiredActionFurniAreaView } from '../selectors/WiredActionFurniAreaView';
import { WiredSelectorFurniNeighborhoodView } from '../selectors/WiredSelectorFurniNeighborhoodView';
import { WiredSelectorFurniByTypeView } from '../selectors/WiredSelectorFurniByTypeView';
import { WiredSelectorFurniAltitudeView } from '../selectors/WiredSelectorFurniAltitudeView';
import { WiredSelectorFurniOnFurniView } from '../selectors/WiredSelectorFurniOnFurniView';
import { WiredSelectorFurniPicksView } from '../selectors/WiredSelectorFurniPicksView';
import { WiredSelectorFurniSignalView } from '../selectors/WiredSelectorFurniSignalView';
import { WiredSelectorUsersAreaView } from '../selectors/WiredSelectorUsersAreaView';
import { WiredSelectorUsersByTypeView } from '../selectors/WiredSelectorUsersByTypeView';
import { WiredSelectorUsersByActionView } from '../selectors/WiredSelectorUsersByActionView';
import { WiredSelectorUsersByNameView } from '../selectors/WiredSelectorUsersByNameView';
import { WiredSelectorUsersOnFurniView } from '../selectors/WiredSelectorUsersOnFurniView';
import { WiredSelectorUsersGroupView } from '../selectors/WiredSelectorUsersGroupView';
import { WiredSelectorUsersHandItemView } from '../selectors/WiredSelectorUsersHandItemView';
import { WiredSelectorUsersNeighborhoodView } from '../selectors/WiredSelectorUsersNeighborhoodView';
import { WiredSelectorUsersSignalView } from '../selectors/WiredSelectorUsersSignalView';
import { WiredSelectorUsersTeamView } from '../selectors/WiredSelectorUsersTeamView';
import { WiredActionBotFollowAvatarView } from './WiredActionBotFollowAvatarView';
import { WiredActionBotGiveHandItemView } from './WiredActionBotGiveHandItemView';
import { WiredActionBotMoveView } from './WiredActionBotMoveView';
@@ -28,6 +40,7 @@ import { WiredActionJoinTeamView } from './WiredActionJoinTeamView';
import { WiredActionKickFromRoomView } from './WiredActionKickFromRoomView';
import { WiredActionLeaveTeamView } from './WiredActionLeaveTeamView';
import { WiredActionMoveAndRotateFurniView } from './WiredActionMoveAndRotateFurniView';
import { WiredActionMoveRotateUserView } from './WiredActionMoveRotateUserView';
import { WiredActionMoveFurniToView } from './WiredActionMoveFurniToView';
import { WiredActionMoveFurniView } from './WiredActionMoveFurniView';
import { WiredActionMuteUserView } from './WiredActionMuteUserView';
@@ -37,6 +50,8 @@ import { WiredActionSetFurniStateToView } from './WiredActionSetFurniStateToView
import { WiredActionTeleportView } from './WiredActionTeleportView';
import { WiredActionToggleFurniStateView } from './WiredActionToggleFurniStateView';
import { WiredActionUnfreezeView } from './WiredActionUnfreezeView';
import { WiredExtraFilterFurniView } from '../extras/WiredExtraFilterFurniView';
import { WiredExtraFilterUserView } from '../extras/WiredExtraFilterUserView';
export const WiredActionLayoutView = (code: number) =>
{
@@ -92,6 +107,8 @@ export const WiredActionLayoutView = (code: number) =>
return <WiredActionMoveFurniView />;
case WiredActionLayoutCode.MOVE_AND_ROTATE_FURNI:
return <WiredActionMoveAndRotateFurniView />;
case WiredActionLayoutCode.MOVE_ROTATE_USER:
return <WiredActionMoveRotateUserView />;
case WiredActionLayoutCode.MOVE_FURNI_TO:
return <WiredActionMoveFurniToView />;
case WiredActionLayoutCode.MUTE_USER:
@@ -116,10 +133,38 @@ export const WiredActionLayoutView = (code: number) =>
return <WiredSelectorFurniNeighborhoodView />;
case WiredActionLayoutCode.FURNI_BYTYPE_SELECTOR:
return <WiredSelectorFurniByTypeView />;
case WiredActionLayoutCode.FURNI_ALTITUDE_SELECTOR:
return <WiredSelectorFurniAltitudeView />;
case WiredActionLayoutCode.FURNI_ON_FURNI_SELECTOR:
return <WiredSelectorFurniOnFurniView />;
case WiredActionLayoutCode.FURNI_PICKS_SELECTOR:
return <WiredSelectorFurniPicksView />;
case WiredActionLayoutCode.FURNI_SIGNAL_SELECTOR:
return <WiredSelectorFurniSignalView />;
case WiredActionLayoutCode.USERS_AREA_SELECTOR:
return <WiredSelectorUsersAreaView />;
case WiredActionLayoutCode.USERS_NEIGHBORHOOD_SELECTOR:
return <WiredSelectorUsersNeighborhoodView />;
case WiredActionLayoutCode.USERS_SIGNAL_SELECTOR:
return <WiredSelectorUsersSignalView />;
case WiredActionLayoutCode.USERS_BY_TYPE_SELECTOR:
return <WiredSelectorUsersByTypeView />;
case WiredActionLayoutCode.USERS_BY_ACTION_SELECTOR:
return <WiredSelectorUsersByActionView />;
case WiredActionLayoutCode.USERS_BY_NAME_SELECTOR:
return <WiredSelectorUsersByNameView />;
case WiredActionLayoutCode.USERS_ON_FURNI_SELECTOR:
return <WiredSelectorUsersOnFurniView />;
case WiredActionLayoutCode.USERS_GROUP_SELECTOR:
return <WiredSelectorUsersGroupView />;
case WiredActionLayoutCode.USERS_HANDITEM_SELECTOR:
return <WiredSelectorUsersHandItemView />;
case WiredActionLayoutCode.USERS_TEAM_SELECTOR:
return <WiredSelectorUsersTeamView />;
case WiredActionLayoutCode.FILTER_FURNI_EXTRA:
return <WiredExtraFilterFurniView />;
case WiredActionLayoutCode.FILTER_USER_EXTRA:
return <WiredExtraFilterUserView />;
case WiredActionLayoutCode.SEND_SIGNAL:
return <WiredActionSendSignalView />;
}
@@ -2,34 +2,17 @@ import { FC, useEffect, useState } from 'react';
import { LocalizeText, WiredFurniType } from '../../../../api';
import { Text } from '../../../../common';
import { useWired } from '../../../../hooks';
import { WiredDirectionIcon, WIRED_DIRECTION_GRID } from '../WiredDirectionIcon';
import { WiredActionBaseView } from './WiredActionBaseView';
import { WiredSourcesSelector } from '../WiredSourcesSelector';
const directionOptions: { value: number, icon: string }[] = [
{
value: 0,
icon: 'ne'
},
{
value: 2,
icon: 'se'
},
{
value: 4,
icon: 'sw'
},
{
value: 6,
icon: 'nw'
}
];
const rotationOptions: number[] = [ 0, 1, 2, 3, 4, 5, 6 ];
export const WiredActionMoveAndRotateFurniView: FC<{}> = props =>
{
const [ movement, setMovement ] = useState(-1);
const [ rotation, setRotation ] = useState(-1);
const [ blockOnUserCollision, setBlockOnUserCollision ] = useState(false);
const { trigger = null, setIntParams = null } = useWired();
const [ furniSource, setFurniSource ] = useState<number>(() =>
{
@@ -37,7 +20,7 @@ export const WiredActionMoveAndRotateFurniView: FC<{}> = props =>
return (trigger?.selectedItems?.length ?? 0) > 0 ? 100 : 0;
});
const save = () => setIntParams([ movement, rotation, furniSource ]);
const save = () => setIntParams([ movement, rotation, furniSource, blockOnUserCollision ? 1 : 0 ]);
useEffect(() =>
{
@@ -54,6 +37,8 @@ export const WiredActionMoveAndRotateFurniView: FC<{}> = props =>
if(trigger.intData.length > 2) setFurniSource(trigger.intData[2]);
else setFurniSource((trigger.selectedItems?.length ?? 0) > 0 ? 100 : 0);
setBlockOnUserCollision((trigger.intData?.length ?? 0) > 3 ? trigger.intData[3] === 1 : false);
}, [ trigger ]);
const onChangeFurniSource = (next: number) => setFurniSource(next);
@@ -68,18 +53,23 @@ export const WiredActionMoveAndRotateFurniView: FC<{}> = props =>
footer={ <WiredSourcesSelector showFurni={ true } furniSource={ furniSource } onChangeFurni={ onChangeFurniSource } /> }>
<div className="flex flex-col gap-1">
<Text bold>{ LocalizeText('wiredfurni.params.startdir') }</Text>
<div className="flex gap-1">
{ directionOptions.map(option =>
<div className="grid grid-cols-4 gap-2 max-w-[240px]">
{ WIRED_DIRECTION_GRID.flatMap((row, rowIndex) => row.map((direction, columnIndex) =>
{
if(direction === null)
{
return <div key={ `move-to-dir-empty-${ rowIndex }-${ columnIndex }` } />;
}
return (
<div key={ option.value } className="flex items-center gap-1">
<input checked={ (movement === option.value) } className="form-check-input" id={ `movement${ option.value }` } name="movement" type="radio" onChange={ event => setMovement(option.value) } />
<Text>
<i className={ `icon icon-${ option.icon }` } />
</Text>
</div>
<label key={ `move-to-dir-${ direction }` } className="flex items-center justify-center gap-[2px] cursor-pointer">
<input checked={ (movement === direction) } className="form-check-input" id={ `movement${ direction }` } name="movement" type="radio" onChange={ () => setMovement(direction) } />
<span className="inline-flex items-center justify-center">
<WiredDirectionIcon direction={ direction } selected={ movement === direction } />
</span>
</label>
);
}) }
})) }
</div>
</div>
<div className="flex flex-col gap-1">
@@ -94,6 +84,13 @@ export const WiredActionMoveAndRotateFurniView: FC<{}> = props =>
);
}) }
</div>
<div className="flex flex-col gap-1">
<Text bold>{ LocalizeText('wiredfurni.params.user_collide') }</Text>
<label className="flex items-center gap-1 cursor-pointer">
<input checked={ blockOnUserCollision } className="form-check-input" type="checkbox" onChange={ event => setBlockOnUserCollision(event.target.checked) } />
<Text>{ LocalizeText('wiredfurni.params.user_collide.0') }</Text>
</label>
</div>
</WiredActionBaseView>
);
};
@@ -2,37 +2,44 @@ import { FC, useEffect, useState } from 'react';
import { LocalizeText, WiredFurniType } from '../../../../api';
import { Text } from '../../../../common';
import { useWired } from '../../../../hooks';
import iconWiredDirE from '../../../../assets/images/wired/icon_wired_dir_e.png';
import iconWiredDirHorizontalRandom from '../../../../assets/images/wired/icon_wired_dir_horizontal_random.png';
import iconWiredDirN from '../../../../assets/images/wired/icon_wired_dir_n.png';
import iconWiredDirNe from '../../../../assets/images/wired/icon_wired_dir_ne.png';
import iconWiredDirNw from '../../../../assets/images/wired/icon_wired_dir_nw.png';
import iconWiredDirRandom from '../../../../assets/images/wired/icon_wired_dir_random.png';
import iconWiredDirS from '../../../../assets/images/wired/icon_wired_dir_s.png';
import iconWiredDirSe from '../../../../assets/images/wired/icon_wired_dir_se.png';
import iconWiredDirSw from '../../../../assets/images/wired/icon_wired_dir_sw.png';
import iconWiredDirVerticalRandom from '../../../../assets/images/wired/icon_wired_dir_vertical_random.png';
import iconWiredDirW from '../../../../assets/images/wired/icon_wired_dir_w.png';
import { WiredDirectionIcon, WIRED_DIRECTION_GRID } from '../WiredDirectionIcon';
import { WiredActionBaseView } from './WiredActionBaseView';
import { WiredSourcesSelector } from '../WiredSourcesSelector';
const directionOptions: { value: number, icon: string }[] = [
const NORMAL_DIRECTION_VALUE_MAP: Record<number, { value: number; icon: string }> = {
0: { value: 6, icon: iconWiredDirN },
1: { value: 8, icon: iconWiredDirNe },
2: { value: 5, icon: iconWiredDirE },
3: { value: 9, icon: iconWiredDirSe },
4: { value: 4, icon: iconWiredDirS },
5: { value: 10, icon: iconWiredDirSw },
6: { value: 7, icon: iconWiredDirW },
7: { value: 11, icon: iconWiredDirNw }
};
const extraDirectionOptions: { value: number, icon: string }[] = [
{
value: 4,
icon: 'ne'
},
{
value: 5,
icon: 'se'
},
{
value: 6,
icon: 'sw'
},
{
value: 7,
icon: 'nw'
value: 1,
icon: iconWiredDirRandom
},
{
value: 2,
icon: 'mv-2'
icon: iconWiredDirHorizontalRandom
},
{
value: 3,
icon: 'mv-3'
},
{
value: 1,
icon: 'mv-1'
icon: iconWiredDirVerticalRandom
}
];
@@ -84,17 +91,38 @@ export const WiredActionMoveFurniView: FC<{}> = props =>
<input checked={ (movement === 0) } className="form-check-input" id="movement0" name="selectedTeam" type="radio" onChange={ event => setMovement(0) } />
<Text>{ LocalizeText('wiredfurni.params.movefurni.0') }</Text>
</div>
<div className="flex gap-1">
{ directionOptions.map(option =>
<div className="grid grid-cols-4 gap-2 max-w-[240px]">
{ WIRED_DIRECTION_GRID.flatMap((row, rowIndex) => row.map((direction, columnIndex) =>
{
if(direction === null)
{
return <div key={ `move-furni-empty-${ rowIndex }-${ columnIndex }` } />;
}
const option = NORMAL_DIRECTION_VALUE_MAP[direction];
return (
<label key={ `move-furni-${ direction }` } className="flex items-center justify-center gap-[2px] cursor-pointer">
<input checked={ (movement === option.value) } className="form-check-input" id={ `movement${ option.value }` } name="movement" type="radio" onChange={ event => setMovement(option.value) } />
<span className="inline-flex items-center justify-center">
<WiredDirectionIcon direction={ option.value } iconSrc={ option.icon } selected={ movement === option.value } />
</span>
</label>
);
})) }
</div>
<div className="flex flex-wrap gap-2">
{ extraDirectionOptions.map(option =>
{
return (
<div key={ option.value } className="flex items-center gap-1">
<label key={ `extra-${ option.value }` } className="flex items-center gap-[2px] cursor-pointer">
<input checked={ (movement === option.value) } className="form-check-input" id={ `movement${ option.value }` } name="movement" type="radio" onChange={ event => setMovement(option.value) } />
<i className={ `nitro.icon icon-${ option.icon }` } />
</div>
<span className="inline-flex items-center justify-center">
<WiredDirectionIcon direction={ option.value } iconSrc={ option.icon } selected={ movement === option.value } />
</span>
</label>
);
}) }
<div className="col" />
</div>
</div>
<div className="flex flex-col gap-1">
@@ -0,0 +1,121 @@
import { FC, useEffect, useState } from 'react';
import { LocalizeText, WiredFurniType } from '../../../../api';
import { Text } from '../../../../common';
import { useWired } from '../../../../hooks';
import iconRotateClockwise from '../../../../assets/images/wired/icon_wired_rotate_clockwise.png';
import iconRotateCounterClockwise from '../../../../assets/images/wired/icon_wired_rotate_counter_clockwise.png';
import { WiredDirectionIcon, WIRED_DIRECTION_GRID } from '../WiredDirectionIcon';
import { WiredActionBaseView } from './WiredActionBaseView';
import { WiredSourcesSelector } from '../WiredSourcesSelector';
const ROTATION_CLOCKWISE = 8;
const ROTATION_COUNTER_CLOCKWISE = 9;
interface DirectionExtraOption
{
value: number;
icon: string;
label: string;
}
interface DirectionPickerProps
{
name: string;
title: string;
noneLabel: string;
value: number;
onChange: (value: number) => void;
extraOptions?: DirectionExtraOption[];
}
const DirectionPicker: FC<DirectionPickerProps> = props =>
{
const { name = '', title = '', noneLabel = '', value = -1, onChange = null, extraOptions = [] } = props;
return (
<div className="flex flex-col gap-2">
<Text bold>{ title }</Text>
<label className="flex items-center gap-2 text-[12px]">
<input checked={ (value === -1) } className="form-check-input" name={ name } type="radio" onChange={ () => onChange(-1) } />
<span>{ noneLabel }</span>
</label>
<div className="grid grid-cols-4 gap-2 max-w-[240px]">
{ WIRED_DIRECTION_GRID.flatMap((row, rowIndex) => row.map((direction, columnIndex) =>
{
if(direction === null)
{
return <div key={ `${ name }-empty-${ rowIndex }-${ columnIndex }` } />;
}
const selected = (value === direction);
return (
<label key={ `${ name }-${ direction }` } className="flex items-center justify-center gap-[2px] cursor-pointer">
<input checked={ selected } className="form-check-input" name={ name } type="radio" onChange={ () => onChange(direction) } />
<span className="inline-flex items-center justify-center">
<WiredDirectionIcon direction={ direction } selected={ selected } />
</span>
</label>
);
})) }
</div>
{ extraOptions.length > 0 &&
<div className="flex flex-wrap gap-3">
{ extraOptions.map(option => (
<label key={ `${ name }-extra-${ option.value }` } className="flex items-center gap-[2px] cursor-pointer" title={ option.label }>
<input checked={ (value === option.value) } className="form-check-input" name={ name } type="radio" onChange={ () => onChange(option.value) } />
<img alt="" className="h-auto w-auto object-contain" draggable={ false } src={ option.icon } />
</label>
)) }
</div> }
</div>
);
};
export const WiredActionMoveRotateUserView: FC<{}> = props =>
{
const [ movementDirection, setMovementDirection ] = useState(-1);
const [ rotationDirection, setRotationDirection ] = useState(-1);
const { trigger = null, setIntParams = null } = useWired();
const [ userSource, setUserSource ] = useState<number>(() =>
{
if(trigger?.intData?.length > 2) return trigger.intData[2];
return 0;
});
const save = () => setIntParams([ movementDirection, rotationDirection, userSource ]);
const rotationExtraOptions: DirectionExtraOption[] = [
{ value: ROTATION_CLOCKWISE, icon: iconRotateClockwise, label: LocalizeText('wiredfurni.params.rotatefurni.1') },
{ value: ROTATION_COUNTER_CLOCKWISE, icon: iconRotateCounterClockwise, label: LocalizeText('wiredfurni.params.rotatefurni.2') }
];
useEffect(() =>
{
setMovementDirection((trigger.intData.length > 0) ? trigger.intData[0] : -1);
setRotationDirection((trigger.intData.length > 1) ? trigger.intData[1] : -1);
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 } /> }>
<DirectionPicker
name="wired-move-user-direction"
title={ LocalizeText('wiredfurni.params.moveuser') }
noneLabel={ LocalizeText('wiredfurni.params.movefurni.0') }
value={ movementDirection }
onChange={ setMovementDirection } />
<DirectionPicker
name="wired-rotate-user-direction"
title={ LocalizeText('wiredfurni.params.rotateuser') }
noneLabel={ LocalizeText('wiredfurni.params.rotatefurni.0') }
extraOptions={ rotationExtraOptions }
value={ rotationDirection }
onChange={ setRotationDirection } />
</WiredActionBaseView>
);
};
@@ -1,4 +1,4 @@
import { FC, useCallback, useEffect, useState } from 'react';
import { FC, useCallback, useEffect, useRef, useState } from 'react';
import { LocalizeText, WiredFurniType, WiredSelectionVisualizer } from '../../../../api';
import { Button, Text } from '../../../../common';
import { useWired } from '../../../../hooks';
@@ -48,6 +48,7 @@ export const WiredActionSendSignalView: FC<{}> = () =>
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();
@@ -84,28 +85,39 @@ export const WiredActionSendSignalView: FC<{}> = () =>
else setForwardFurniIds(furniIds);
}, [ furniIds, selectionMode ]);
const applySelection = useCallback((nextIds: number[]) =>
const syncHighlights = useCallback((nextAntennaIds: number[], nextForwardFurniIds: number[], nextSelectionMode: SelectionMode, nextFurniSource: number) =>
{
if(!setFurniIds) return;
setFurniIds(prev =>
if(highlightedIds.current.length)
{
if(prev && prev.length) WiredSelectionVisualizer.clearSelectionShaderFromFurni(prev);
if(nextIds && nextIds.length) WiredSelectionVisualizer.applySelectionShaderToFurni(nextIds);
WiredSelectionVisualizer.clearSelectionShaderFromFurni(highlightedIds.current);
WiredSelectionVisualizer.clearSecondarySelectionShaderFromFurni(highlightedIds.current);
}
return [ ...nextIds ];
});
}, [ setFurniIds ]);
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 nextIds = (mode === 'antenna') ? antennaIds : forwardFurniIds;
applySelection(nextIds);
setSelectionMode(mode);
}, [ selectionMode, furniSource, antennaIds, forwardFurniIds, applySelection ]);
if(setFurniIds) setFurniIds([ ...((mode === 'antenna') ? antennaIds : forwardFurniIds) ]);
}, [ selectionMode, furniSource, antennaIds, forwardFurniIds, setFurniIds ]);
const onChangeFurniSource = (next: number) =>
{
@@ -113,7 +125,7 @@ export const WiredActionSendSignalView: FC<{}> = () =>
if(selectionMode === 'furni')
{
applySelection(antennaIds);
if(setFurniIds) setFurniIds([ ...antennaIds ]);
setSelectionMode('antenna');
}
@@ -122,12 +134,6 @@ export const WiredActionSendSignalView: FC<{}> = () =>
const save = useCallback(() =>
{
if(selectionMode === 'furni')
{
setSelectionMode('antenna');
applySelection(antennaIds);
}
const antennaSource = (antennaIds && antennaIds.length) ? antennaIds[0] : 0;
setIntParams([
@@ -140,7 +146,19 @@ export const WiredActionSendSignalView: FC<{}> = () =>
]);
setStringParam(serializeForwardIds(forwardFurniIds));
}, [ selectionMode, antennaIds, furniSource, userSource, signalPerFurni, signalPerUser, forwardFurniIds, setIntParams, setStringParam, applySelection, setSelectionMode ]);
}, [ antennaIds, furniSource, userSource, signalPerFurni, signalPerUser, forwardFurniIds, 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);
@@ -10,22 +10,26 @@ export const WiredActionSetFurniStateToView: FC<{}> = props =>
const [ stateFlag, setStateFlag ] = useState(0);
const [ directionFlag, setDirectionFlag ] = useState(0);
const [ positionFlag, setPositionFlag ] = useState(0);
const [ altitudeFlag, setAltitudeFlag ] = useState(0);
const { trigger = null, setIntParams = null } = useWired();
const [ furniSource, setFurniSource ] = useState<number>(() =>
{
if(trigger?.intData?.length > 4) return trigger.intData[4];
if(trigger?.intData?.length > 3) return trigger.intData[3];
return (trigger?.selectedItems?.length ?? 0) > 0 ? 100 : 0;
});
const save = () => setIntParams([ stateFlag, directionFlag, positionFlag, furniSource ]);
const save = () => setIntParams([ stateFlag, directionFlag, positionFlag, altitudeFlag, furniSource ]);
useEffect(() =>
{
setStateFlag(trigger.getBoolean(0) ? 1 : 0);
setDirectionFlag(trigger.getBoolean(1) ? 1 : 0);
setPositionFlag(trigger.getBoolean(2) ? 1 : 0);
setAltitudeFlag((trigger.intData.length > 4 && trigger.getBoolean(3)) ? 1 : 0);
if(trigger.intData.length > 3) setFurniSource(trigger.intData[3]);
if(trigger.intData.length > 4) setFurniSource(trigger.intData[4]);
else if(trigger.intData.length > 3) setFurniSource(trigger.intData[3]);
else setFurniSource((trigger.selectedItems?.length ?? 0) > 0 ? 100 : 0);
}, [ trigger ]);
@@ -53,6 +57,10 @@ export const WiredActionSetFurniStateToView: FC<{}> = props =>
<input checked={ !!positionFlag } className="form-check-input" id="positionFlag" type="checkbox" onChange={ event => setPositionFlag(event.target.checked ? 1 : 0) } />
<Text>{ LocalizeText('wiredfurni.params.condition.position') }</Text>
</div>
<div className="flex items-center gap-1">
<input checked={ !!altitudeFlag } className="form-check-input" id="altitudeFlag" type="checkbox" onChange={ event => setAltitudeFlag(event.target.checked ? 1 : 0) } />
<Text>{ LocalizeText('wiredfurni.params.condition.altitude') }</Text>
</div>
</div>
</WiredActionBaseView>
);
@@ -1,21 +1,39 @@
import { FC, useEffect, useState } from 'react';
import { WiredFurniType } from '../../../../api';
import { LocalizeText, WiredFurniType } from '../../../../api';
import { Text } from '../../../../common';
import { useWired } from '../../../../hooks';
import { WiredActionBaseView } from './WiredActionBaseView';
import { WiredActionLayoutCode } from '../../../../api/wired/WiredActionLayoutCode';
import { WiredSourcesSelector } from '../WiredSourcesSelector';
export const WiredActionTeleportView: FC<{}> = props =>
{
const { trigger = null, setIntParams = null } = useWired();
const isTeleportEffect = (trigger?.code === WiredActionLayoutCode.TELEPORT);
const isUserToFurniEffect = (trigger?.code === WiredActionLayoutCode.USER_TO_FURNI);
const [ fastTeleport, setFastTeleport ] = useState<boolean>(() =>
{
if(isTeleportEffect && trigger?.intData?.length >= 3) return (trigger.intData[0] === 1);
return false;
});
const [ walkMode, setWalkMode ] = useState<number>(() =>
{
if(isUserToFurniEffect && trigger?.intData?.length >= 3) return trigger.intData[2];
return 1;
});
const [ furniSource, setFurniSource ] = useState<number>(() =>
{
if(isTeleportEffect && trigger?.intData?.length >= 3) return trigger.intData[1];
if(isUserToFurniEffect && trigger?.intData?.length >= 3) return trigger.intData[0];
if(trigger?.intData?.length >= 1) return trigger.intData[0];
return (trigger?.selectedItems?.length ?? 0) > 0 ? 100 : 0;
});
const [ userSource, setUserSource ] = useState<number>(() =>
{
if(isTeleportEffect && trigger?.intData?.length >= 3) return trigger.intData[2];
if(isUserToFurniEffect && trigger?.intData?.length >= 3) return trigger.intData[1];
if(trigger?.intData?.length >= 2) return trigger.intData[1];
return 0;
});
@@ -24,16 +42,37 @@ export const WiredActionTeleportView: FC<{}> = props =>
{
if(!trigger) return;
if(isTeleportEffect && trigger.intData.length >= 3)
{
setFastTeleport(trigger.intData[0] === 1);
setFurniSource(trigger.intData[1]);
setUserSource(trigger.intData[2]);
setWalkMode(1);
return;
}
if(isUserToFurniEffect && trigger.intData.length >= 3)
{
setFastTeleport(false);
setFurniSource(trigger.intData[0]);
setUserSource(trigger.intData[1]);
setWalkMode(trigger.intData[2]);
return;
}
setFastTeleport(false);
setWalkMode(1);
if(trigger.intData.length >= 1) setFurniSource(trigger.intData[0]);
else setFurniSource((trigger.selectedItems?.length ?? 0) > 0 ? 100 : 0);
if(trigger.intData.length >= 2) setUserSource(trigger.intData[1]);
else setUserSource(0);
}, [ trigger ]);
}, [ isTeleportEffect, isUserToFurniEffect, trigger ]);
const onChangeFurniSource = (next: number) => setFurniSource(next);
const save = () => setIntParams([ furniSource, userSource ]);
const save = () => setIntParams(isTeleportEffect ? [ fastTeleport ? 1 : 0, furniSource, userSource ] : (isUserToFurniEffect ? [ furniSource, userSource, walkMode ] : [ furniSource, userSource ]));
const requiresFurni = WiredFurniType.STUFF_SELECTION_OPTION_BY_ID_BY_TYPE_OR_FROM_CONTEXT;
@@ -50,6 +89,21 @@ export const WiredActionTeleportView: FC<{}> = props =>
userSource={ userSource }
onChangeFurni={ onChangeFurniSource }
onChangeUsers={ setUserSource } />
} />
}>
{ isTeleportEffect &&
<div className="flex items-center gap-1">
<input checked={ fastTeleport } className="form-check-input" type="checkbox" onChange={ event => setFastTeleport(event.target.checked) } />
<Text>{ LocalizeText('wiredfurni.params.teleport.options.0') }</Text>
</div> }
{ isUserToFurniEffect &&
<div className="flex flex-col gap-1">
{ [ 0, 1, 2 ].map(option => (
<label key={ option } className="flex items-center gap-1 cursor-pointer">
<input checked={ walkMode === option } className="form-check-input" name="userWalkMode" type="radio" onChange={ () => setWalkMode(option) } />
<Text>{ LocalizeText(`wiredfurni.params.user_move.walkmode.${ option }`) }</Text>
</label>
)) }
</div> }
</WiredActionBaseView>
);
};
@@ -1,5 +1,6 @@
import { FC, useEffect, useState } from 'react';
import { WiredFurniType } from '../../../../api';
import { LocalizeText, WiredFurniType } from '../../../../api';
import { Text } from '../../../../common';
import { WiredActionBaseView } from './WiredActionBaseView';
import { WiredSourcesSelector } from '../WiredSourcesSelector';
import { useWired } from '../../../../hooks';
@@ -7,8 +8,14 @@ import { useWired } from '../../../../hooks';
export const WiredActionToggleFurniStateView: FC<{}> = props =>
{
const { trigger = null, setIntParams = null } = useWired();
const [ toggleType, setToggleType ] = useState<number>(() =>
{
if(trigger?.intData?.length > 1) return trigger.intData[0];
return 0;
});
const [ furniSource, setFurniSource ] = useState<number>(() =>
{
if(trigger?.intData?.length > 1) return trigger.intData[1];
if(trigger?.intData?.length >= 1) return trigger.intData[0];
return (trigger?.selectedItems?.length ?? 0) > 0 ? 100 : 0;
});
@@ -17,13 +24,26 @@ export const WiredActionToggleFurniStateView: FC<{}> = props =>
{
if(!trigger) return;
if(trigger.intData.length >= 1) setFurniSource(trigger.intData[0]);
else setFurniSource((trigger.selectedItems?.length ?? 0) > 0 ? 100 : 0);
if(trigger.intData.length > 1)
{
setToggleType(trigger.intData[0]);
setFurniSource(trigger.intData[1]);
}
else if(trigger.intData.length >= 1)
{
setToggleType(0);
setFurniSource(trigger.intData[0]);
}
else
{
setToggleType(0);
setFurniSource((trigger.selectedItems?.length ?? 0) > 0 ? 100 : 0);
}
}, [ trigger ]);
const onChangeFurniSource = (next: number) => setFurniSource(next);
const save = () => setIntParams([ furniSource ]);
const save = () => setIntParams([ toggleType, furniSource ]);
const requiresFurni = WiredFurniType.STUFF_SELECTION_OPTION_BY_ID_BY_TYPE_OR_FROM_CONTEXT;
@@ -32,6 +52,16 @@ export const WiredActionToggleFurniStateView: FC<{}> = props =>
hasSpecialInput={ true }
requiresFurni={ requiresFurni }
save={ save }
footer={ <WiredSourcesSelector showFurni={ true } furniSource={ furniSource } onChangeFurni={ onChangeFurniSource } /> } />
footer={ <WiredSourcesSelector showFurni={ true } furniSource={ furniSource } onChangeFurni={ onChangeFurniSource } /> }>
<div className="flex flex-col gap-1">
<Text bold>{ LocalizeText('wiredfurni.params.operator.2') }</Text>
{ [ 0, 1 ].map(option => (
<label key={ option } className="flex items-center gap-1">
<input checked={ (toggleType === option) } className="form-check-input" name="toggleType" type="radio" onChange={ () => setToggleType(option) } />
<Text>{ LocalizeText(`wiredfurni.params.toggletype.${ option }`) }</Text>
</label>
)) }
</div>
</WiredActionBaseView>
);
};