feat(wired-ui): expand advanced wired editors
@@ -20,65 +20,5 @@
|
|||||||
"widget.room.chat.clear_history": "leeg geschiedenis",
|
"widget.room.chat.clear_history": "leeg geschiedenis",
|
||||||
"widget.room.youtube.shared": "YouTube word gedeeld",
|
"widget.room.youtube.shared": "YouTube word gedeeld",
|
||||||
"widget.room.youtube.open_video": "Open de video",
|
"widget.room.youtube.open_video": "Open de video",
|
||||||
"wiredfurni.params.area_selection.selected": "Geselecteerd gebied: Lengte=%x%, Breedte=%y%, breedte=%w%, hoogte=%h%",
|
"wiredfurni.params.area_selection.selected": "Geselecteerd gebied: Lengte=%x%, Breedte=%y%, breedte=%w%, hoogte=%h%"
|
||||||
"wiredfurni.params.sources.collapse": "Nascondi le impostazioni avanzate",
|
|
||||||
"wiredfurni.params.sources.expand": "Mostra le impostazioni avanzate",
|
|
||||||
"wiredfurni.params.quantifier_selection": "Abbina condizione se:",
|
|
||||||
"wiredfurni.params.quantifier.users.0": "Tutti gli utenti corrispondono",
|
|
||||||
"wiredfurni.params.quantifier.users.1": "Uno qualsiasi degli utenti corrisponde",
|
|
||||||
"wiredfurni.params.quantifier.users.neg.0": "Uno qualsiasi degli utenti non corrisponde",
|
|
||||||
"wiredfurni.params.quantifier.users.neg.1": "Nessuno degli utenti corrisponde",
|
|
||||||
"wiredfurni.params.quantifier.furni.0": "Tutti i Furni corrispondono",
|
|
||||||
"wiredfurni.params.quantifier.furni.1": "Uno qualsiasi dei Furni corrisponde",
|
|
||||||
"wiredfurni.params.usertype.1": "Habbo",
|
|
||||||
"wiredfurni.params.usertype.2": "Cucciolo",
|
|
||||||
"wiredfurni.params.usertype.4": "Bot",
|
|
||||||
"wiredfurni.params.sources.users.title.match.0": "Gli utenti da abbinare:",
|
|
||||||
"wiredfurni.params.sources.users.title.match.1": "Utenti da comparare con:",
|
|
||||||
"wiredfurni.params.sources.users.101": "Usa l'utente specificato dal nome",
|
|
||||||
"wiredfurni.params.comparison.0": "Più basso di",
|
|
||||||
"wiredfurni.params.comparison.1": "È uguale a",
|
|
||||||
"wiredfurni.params.comparison.2": "Più alto di",
|
|
||||||
"wiredfurni.params.team": "Scegli una squadra",
|
|
||||||
"wiredfurni.params.team.1": "Rossa",
|
|
||||||
"wiredfurni.params.team.2": "Verde",
|
|
||||||
"wiredfurni.params.team.3": "Blu",
|
|
||||||
"wiredfurni.params.team.4": "Gialla",
|
|
||||||
"wiredfurni.params.team.triggerer": "Squadra dell'innescatore",
|
|
||||||
"wiredfurni.params.comparison_selection": "Scegli tipo:",
|
|
||||||
"wiredfurni.params.setscore2": "La squadra deve segnare:",
|
|
||||||
"wiredfurni.params.placement_selection": "Posizione:",
|
|
||||||
"wiredfurni.params.placement.1": "1.",
|
|
||||||
"wiredfurni.params.placement.2": "2.",
|
|
||||||
"wiredfurni.params.placement.3": "3.",
|
|
||||||
"wiredfurni.params.placement.4": "4.",
|
|
||||||
"wiredfurni.params.time.hour_selection": "Ore:",
|
|
||||||
"wiredfurni.params.time.minute_selection": "Minuti:",
|
|
||||||
"wiredfurni.params.time.second_selection": "Secondi:",
|
|
||||||
"wiredfurni.params.time.skip": "Non usare il filtro",
|
|
||||||
"wiredfurni.params.time.exact": "Esatto",
|
|
||||||
"wiredfurni.params.time.range": "Range",
|
|
||||||
"wiredfurni.params.time.weekday_selection": "Giorno della settimana:",
|
|
||||||
"wiredfurni.params.time.weekday.1": "Lunedì",
|
|
||||||
"wiredfurni.params.time.weekday.2": "Martedì",
|
|
||||||
"wiredfurni.params.time.weekday.3": "Mercoledì",
|
|
||||||
"wiredfurni.params.time.weekday.4": "Giovedì",
|
|
||||||
"wiredfurni.params.time.weekday.5": "Venerdì",
|
|
||||||
"wiredfurni.params.time.weekday.6": "Sabato",
|
|
||||||
"wiredfurni.params.time.weekday.7": "Domenica",
|
|
||||||
"wiredfurni.params.time.day_selection": "Giorno:",
|
|
||||||
"wiredfurni.params.time.month_selection": "Mese:",
|
|
||||||
"wiredfurni.params.time.month.10": "Ott.",
|
|
||||||
"wiredfurni.params.time.month.11": "Nov.",
|
|
||||||
"wiredfurni.params.time.month.12": "Dic.",
|
|
||||||
"wiredfurni.params.time.month.1": "Gen.",
|
|
||||||
"wiredfurni.params.time.month.2": "Feb.",
|
|
||||||
"wiredfurni.params.time.month.3": "Mar.",
|
|
||||||
"wiredfurni.params.time.month.4": "Apr.",
|
|
||||||
"wiredfurni.params.time.month.5": "Mag.",
|
|
||||||
"wiredfurni.params.time.month.6": "Giu.",
|
|
||||||
"wiredfurni.params.time.month.7": "Lug.",
|
|
||||||
"wiredfurni.params.time.month.8": "Ago.",
|
|
||||||
"wiredfurni.params.time.month.9": "Set.",
|
|
||||||
"wiredfurni.params.time.year_selection": "Anno:"
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,4 +41,19 @@ export class WiredActionLayoutCode
|
|||||||
public static RELATIVE_MOVE: number = 40;
|
public static RELATIVE_MOVE: number = 40;
|
||||||
public static CONTROL_CLOCK: number = 41;
|
public static CONTROL_CLOCK: number = 41;
|
||||||
public static ADJUST_CLOCK: number = 42;
|
public static ADJUST_CLOCK: number = 42;
|
||||||
|
public static MOVE_ROTATE_USER: number = 43;
|
||||||
|
public static FURNI_ALTITUDE_SELECTOR: number = 44;
|
||||||
|
public static FURNI_ON_FURNI_SELECTOR: number = 45;
|
||||||
|
public static FURNI_PICKS_SELECTOR: number = 46;
|
||||||
|
public static FURNI_SIGNAL_SELECTOR: number = 47;
|
||||||
|
public static USERS_SIGNAL_SELECTOR: number = 48;
|
||||||
|
public static USERS_BY_TYPE_SELECTOR: number = 49;
|
||||||
|
public static USERS_TEAM_SELECTOR: number = 50;
|
||||||
|
public static USERS_BY_ACTION_SELECTOR: number = 51;
|
||||||
|
public static USERS_BY_NAME_SELECTOR: number = 52;
|
||||||
|
public static USERS_ON_FURNI_SELECTOR: number = 53;
|
||||||
|
public static USERS_GROUP_SELECTOR: number = 54;
|
||||||
|
public static USERS_HANDITEM_SELECTOR: number = 55;
|
||||||
|
public static FILTER_FURNI_EXTRA: number = 56;
|
||||||
|
public static FILTER_USER_EXTRA: number = 57;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ export class WiredConditionlayout
|
|||||||
public static NOT_ACTOR_WEARING_EFFECT: number = 23;
|
public static NOT_ACTOR_WEARING_EFFECT: number = 23;
|
||||||
public static DATE_RANGE_ACTIVE: number = 24;
|
public static DATE_RANGE_ACTIVE: number = 24;
|
||||||
public static ACTOR_HAS_HANDITEM: number = 25;
|
public static ACTOR_HAS_HANDITEM: number = 25;
|
||||||
|
public static MOVEMENT_VALIDATION: number = 26;
|
||||||
public static COUNTER_TIME_MATCHES: number = 27;
|
public static COUNTER_TIME_MATCHES: number = 27;
|
||||||
public static USER_PERFORMS_ACTION: number = 28;
|
public static USER_PERFORMS_ACTION: number = 28;
|
||||||
public static HAS_ALTITUDE: number = 29;
|
public static HAS_ALTITUDE: number = 29;
|
||||||
@@ -37,4 +38,6 @@ export class WiredConditionlayout
|
|||||||
public static TEAM_HAS_RANK: number = 35;
|
public static TEAM_HAS_RANK: number = 35;
|
||||||
public static MATCH_TIME: number = 36;
|
public static MATCH_TIME: number = 36;
|
||||||
public static MATCH_DATE: number = 37;
|
public static MATCH_DATE: number = 37;
|
||||||
|
public static ACTOR_DIR: number = 38;
|
||||||
|
public static SLC_QUANTITY: number = 39;
|
||||||
}
|
}
|
||||||
|
|||||||
|
After Width: | Height: | Size: 153 B |
|
After Width: | Height: | Size: 161 B |
|
After Width: | Height: | Size: 153 B |
|
After Width: | Height: | Size: 244 B |
|
After Width: | Height: | Size: 231 B |
|
After Width: | Height: | Size: 156 B |
|
After Width: | Height: | Size: 153 B |
|
After Width: | Height: | Size: 232 B |
|
After Width: | Height: | Size: 235 B |
|
After Width: | Height: | Size: 164 B |
|
After Width: | Height: | Size: 154 B |
@@ -6,16 +6,17 @@ import { NitroCardContextProvider } from './NitroCardContext';
|
|||||||
export interface NitroCardViewProps extends DraggableWindowProps, ColumnProps
|
export interface NitroCardViewProps extends DraggableWindowProps, ColumnProps
|
||||||
{
|
{
|
||||||
theme?: string;
|
theme?: string;
|
||||||
|
isResizable?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const NitroCardView: FC<NitroCardViewProps> = props =>
|
export const NitroCardView: FC<NitroCardViewProps> = props =>
|
||||||
{
|
{
|
||||||
const { theme = 'primary', uniqueKey = null, handleSelector = '.drag-handler', windowPosition = DraggableWindowPosition.CENTER, disableDrag = false, overflow = 'hidden', position = 'relative', gap = 0, classNames = [], ...rest } = props;
|
const { theme = 'primary', uniqueKey = null, handleSelector = '.drag-handler', windowPosition = DraggableWindowPosition.CENTER, disableDrag = false, overflow = 'hidden', position = 'relative', gap = 0, classNames = [], isResizable = true, ...rest } = props;
|
||||||
const elementRef = useRef<HTMLDivElement>();
|
const elementRef = useRef<HTMLDivElement>();
|
||||||
|
|
||||||
const getClassNames = useMemo(() =>
|
const getClassNames = useMemo(() =>
|
||||||
{
|
{
|
||||||
const newClassNames: string[] = [ 'resize', 'rounded', 'shadow', ];
|
const newClassNames: string[] = [ isResizable ? 'resize' : 'resize-none', 'rounded', 'shadow' ];
|
||||||
|
|
||||||
// Card Theme Changer
|
// Card Theme Changer
|
||||||
newClassNames.push('border border-[#283F5D]');
|
newClassNames.push('border border-[#283F5D]');
|
||||||
@@ -23,7 +24,7 @@ export const NitroCardView: FC<NitroCardViewProps> = props =>
|
|||||||
if(classNames.length) newClassNames.push(...classNames);
|
if(classNames.length) newClassNames.push(...classNames);
|
||||||
|
|
||||||
return newClassNames;
|
return newClassNames;
|
||||||
}, [ classNames ]);
|
}, [ classNames, isResizable ]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<NitroCardContextProvider value={ { theme } }>
|
<NitroCardContextProvider value={ { theme } }>
|
||||||
|
|||||||
@@ -232,7 +232,9 @@ export const DraggableWindow: FC<DraggableWindowProps> = props => {
|
|||||||
...dragStyle,
|
...dragStyle,
|
||||||
left: 0,
|
left: 0,
|
||||||
top: 0,
|
top: 0,
|
||||||
transform: `translate(${offset.x + delta.x}px, ${offset.y + delta.y}px)`,
|
transform: `translate3d(${offset.x + delta.x}px, ${offset.y + delta.y}px, 0)`,
|
||||||
|
willChange: isDragging ? 'transform' : undefined,
|
||||||
|
backfaceVisibility: 'hidden',
|
||||||
visibility: isPositioned ? 'visible' : 'hidden'
|
visibility: isPositioned ? 'visible' : 'hidden'
|
||||||
}}
|
}}
|
||||||
onMouseDownCapture={onMouseDown}
|
onMouseDownCapture={onMouseDown}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { GetSessionDataManager, RoomEngineEvent, RoomEnterEffect, RoomSessionDanceEvent } from '@nitrots/nitro-renderer';
|
import { AddLinkEventTracker, GetRoomEngine, GetSessionDataManager, ILinkEventTracker, RemoveLinkEventTracker, RoomEngineEvent, RoomEnterEffect, RoomSessionDanceEvent } from '@nitrots/nitro-renderer';
|
||||||
import { FC, useState } from 'react';
|
import { FC, useEffect, useState } from 'react';
|
||||||
import { AvatarInfoFurni, AvatarInfoPet, AvatarInfoRentableBot, AvatarInfoUser, GetConfigurationValue, RoomWidgetUpdateRentableBotChatEvent } from '../../../../api';
|
import { AvatarInfoFurni, AvatarInfoPet, AvatarInfoRentableBot, AvatarInfoUser, GetConfigurationValue, RoomWidgetUpdateRentableBotChatEvent } from '../../../../api';
|
||||||
import { Column } from '../../../../common';
|
import { Column } from '../../../../common';
|
||||||
import { useAvatarInfoWidget, useNitroEvent, useRoom, useUiEvent } from '../../../../hooks';
|
import { useAvatarInfoWidget, useNitroEvent, useRoom, useUiEvent } from '../../../../hooks';
|
||||||
@@ -23,12 +23,29 @@ import { AvatarInfoWidgetRentableBotView } from './menu/AvatarInfoWidgetRentable
|
|||||||
|
|
||||||
export const AvatarInfoWidgetView: FC<{}> = props =>
|
export const AvatarInfoWidgetView: FC<{}> = props =>
|
||||||
{
|
{
|
||||||
|
const BLOCK_MENU_WINDOW_MS = 500;
|
||||||
|
const BLOCK_ROTATE_WINDOW_MS = 500;
|
||||||
const [ isGameMode, setGameMode ] = useState(false);
|
const [ isGameMode, setGameMode ] = useState(false);
|
||||||
const [ isDancing, setIsDancing ] = useState(false);
|
const [ isDancing, setIsDancing ] = useState(false);
|
||||||
const [ rentableBotChatEvent, setRentableBotChatEvent ] = useState<RoomWidgetUpdateRentableBotChatEvent>(null);
|
const [ rentableBotChatEvent, setRentableBotChatEvent ] = useState<RoomWidgetUpdateRentableBotChatEvent>(null);
|
||||||
const { avatarInfo = null, setAvatarInfo = null, activeNameBubble = null, setActiveNameBubble = null, nameBubbles = [], removeNameBubble = null, productBubbles = [], confirmingProduct = null, updateConfirmingProduct = null, removeProductBubble = null, isDecorating = false, setIsDecorating = null } = useAvatarInfoWidget();
|
const { avatarInfo = null, setAvatarInfo = null, activeNameBubble = null, setActiveNameBubble = null, nameBubbles = [], removeNameBubble = null, productBubbles = [], confirmingProduct = null, updateConfirmingProduct = null, removeProductBubble = null, isDecorating = false, setIsDecorating = null } = useAvatarInfoWidget();
|
||||||
const { roomSession = null } = useRoom();
|
const { roomSession = null } = useRoom();
|
||||||
|
|
||||||
|
const updateAvatarClickControl = (updates: { suppressMenuUntil?: number; suppressRotateUntil?: number; }) =>
|
||||||
|
{
|
||||||
|
const globalScope = (globalThis as any);
|
||||||
|
|
||||||
|
if(!globalScope.__nitroAvatarClickControl)
|
||||||
|
{
|
||||||
|
globalScope.__nitroAvatarClickControl = {
|
||||||
|
suppressMenuUntil: 0,
|
||||||
|
suppressRotateUntil: 0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.assign(globalScope.__nitroAvatarClickControl, updates);
|
||||||
|
};
|
||||||
|
|
||||||
useNitroEvent<RoomEngineEvent>(RoomEngineEvent.NORMAL_MODE, event =>
|
useNitroEvent<RoomEngineEvent>(RoomEngineEvent.NORMAL_MODE, event =>
|
||||||
{
|
{
|
||||||
if(isGameMode) setGameMode(false);
|
if(isGameMode) setGameMode(false);
|
||||||
@@ -48,6 +65,41 @@ export const AvatarInfoWidgetView: FC<{}> = props =>
|
|||||||
|
|
||||||
useUiEvent<RoomWidgetUpdateRentableBotChatEvent>(RoomWidgetUpdateRentableBotChatEvent.UPDATE_CHAT, event => setRentableBotChatEvent(event));
|
useUiEvent<RoomWidgetUpdateRentableBotChatEvent>(RoomWidgetUpdateRentableBotChatEvent.UPDATE_CHAT, event => setRentableBotChatEvent(event));
|
||||||
|
|
||||||
|
useEffect(() =>
|
||||||
|
{
|
||||||
|
const linkTracker: ILinkEventTracker = {
|
||||||
|
linkReceived: (url: string) =>
|
||||||
|
{
|
||||||
|
const parts = url.split('/');
|
||||||
|
|
||||||
|
if(parts.length < 2) return;
|
||||||
|
|
||||||
|
switch(parts[1])
|
||||||
|
{
|
||||||
|
case 'hide':
|
||||||
|
setAvatarInfo(null);
|
||||||
|
setActiveNameBubble(null);
|
||||||
|
|
||||||
|
if(roomSession) GetRoomEngine().clearSelectedAvatar(roomSession.roomId);
|
||||||
|
return;
|
||||||
|
case 'block-menu':
|
||||||
|
updateAvatarClickControl({ suppressMenuUntil: Date.now() + BLOCK_MENU_WINDOW_MS });
|
||||||
|
setAvatarInfo(null);
|
||||||
|
setActiveNameBubble(null);
|
||||||
|
return;
|
||||||
|
case 'block-rotate':
|
||||||
|
updateAvatarClickControl({ suppressRotateUntil: Date.now() + BLOCK_ROTATE_WINDOW_MS });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
eventUrlPrefix: 'avatar-info/'
|
||||||
|
};
|
||||||
|
|
||||||
|
AddLinkEventTracker(linkTracker);
|
||||||
|
|
||||||
|
return () => RemoveLinkEventTracker(linkTracker);
|
||||||
|
}, [ roomSession, setActiveNameBubble, setAvatarInfo ]);
|
||||||
|
|
||||||
const getMenuView = () =>
|
const getMenuView = () =>
|
||||||
{
|
{
|
||||||
if(!roomSession || isGameMode) return null;
|
if(!roomSession || isGameMode) return null;
|
||||||
|
|||||||
@@ -61,6 +61,8 @@ export const InfoStandWidgetUserView: FC<InfoStandWidgetUserViewProps> = props =
|
|||||||
if (oldBadges === event.badges.join('')) return;
|
if (oldBadges === event.badges.join('')) return;
|
||||||
|
|
||||||
setAvatarInfo(prevValue => {
|
setAvatarInfo(prevValue => {
|
||||||
|
if (!prevValue) return prevValue;
|
||||||
|
|
||||||
const newValue = CloneObject(prevValue);
|
const newValue = CloneObject(prevValue);
|
||||||
newValue.badges = event.badges;
|
newValue.badges = event.badges;
|
||||||
return newValue;
|
return newValue;
|
||||||
@@ -71,6 +73,8 @@ export const InfoStandWidgetUserView: FC<InfoStandWidgetUserViewProps> = props =
|
|||||||
if (!avatarInfo || avatarInfo.roomIndex !== event.roomIndex) return;
|
if (!avatarInfo || avatarInfo.roomIndex !== event.roomIndex) return;
|
||||||
|
|
||||||
setAvatarInfo(prevValue => {
|
setAvatarInfo(prevValue => {
|
||||||
|
if (!prevValue) return prevValue;
|
||||||
|
|
||||||
const newValue = CloneObject(prevValue);
|
const newValue = CloneObject(prevValue);
|
||||||
newValue.figure = event.figure;
|
newValue.figure = event.figure;
|
||||||
newValue.motto = event.customInfo;
|
newValue.motto = event.customInfo;
|
||||||
@@ -86,6 +90,8 @@ export const InfoStandWidgetUserView: FC<InfoStandWidgetUserViewProps> = props =
|
|||||||
if (!avatarInfo || avatarInfo.roomIndex !== event.roomIndex) return;
|
if (!avatarInfo || avatarInfo.roomIndex !== event.roomIndex) return;
|
||||||
|
|
||||||
setAvatarInfo(prevValue => {
|
setAvatarInfo(prevValue => {
|
||||||
|
if (!prevValue) return prevValue;
|
||||||
|
|
||||||
const newValue = CloneObject(prevValue);
|
const newValue = CloneObject(prevValue);
|
||||||
const clearGroup = (event.status === -1) || (event.habboGroupId <= 0);
|
const clearGroup = (event.status === -1) || (event.habboGroupId <= 0);
|
||||||
|
|
||||||
@@ -272,4 +278,4 @@ export const InfoStandWidgetUserView: FC<InfoStandWidgetUserViewProps> = props =
|
|||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { GetSessionDataManager } from '@nitrots/nitro-renderer';
|
import { GetRoomEngine, GetSessionDataManager } from '@nitrots/nitro-renderer';
|
||||||
import { CSSProperties, FC, PropsWithChildren, ReactNode, useEffect, useState } from 'react';
|
import { CSSProperties, FC, PropsWithChildren, ReactNode, useEffect, useState } from 'react';
|
||||||
import { LocalizeText, WiredFurniType, WiredSelectionVisualizer } from '../../../api';
|
import { LocalizeText, WiredFurniType, WiredSelectionVisualizer } from '../../../api';
|
||||||
import { Button, NitroCardContentView, NitroCardHeaderView, NitroCardView, Text } from '../../../common';
|
import { Button, NitroCardContentView, NitroCardHeaderView, NitroCardView, Text } from '../../../common';
|
||||||
@@ -24,8 +24,15 @@ export const WiredBaseView: FC<PropsWithChildren<WiredBaseViewProps>> = props =>
|
|||||||
const [ needsSave, setNeedsSave ] = useState<boolean>(false);
|
const [ needsSave, setNeedsSave ] = useState<boolean>(false);
|
||||||
const { trigger = null, setTrigger = null, setIntParams = null, setStringParam = null, setFurniIds = null, setAllowsFurni = null, saveWired = null } = useWired();
|
const { trigger = null, setTrigger = null, setIntParams = null, setStringParam = null, setFurniIds = null, setAllowsFurni = null, saveWired = null } = useWired();
|
||||||
|
|
||||||
|
const clearRoomAreaSelection = () =>
|
||||||
|
{
|
||||||
|
GetRoomEngine().areaSelectionManager.clearHighlight();
|
||||||
|
GetRoomEngine().areaSelectionManager.deactivate();
|
||||||
|
};
|
||||||
|
|
||||||
const onClose = () =>
|
const onClose = () =>
|
||||||
{
|
{
|
||||||
|
clearRoomAreaSelection();
|
||||||
WiredSelectionVisualizer.clearAllSelectionShaders();
|
WiredSelectionVisualizer.clearAllSelectionShaders();
|
||||||
setTrigger(null);
|
setTrigger(null);
|
||||||
};
|
};
|
||||||
@@ -92,6 +99,11 @@ export const WiredBaseView: FC<PropsWithChildren<WiredBaseViewProps>> = props =>
|
|||||||
}
|
}
|
||||||
}, [ trigger, hasSpecialInput, setIntParams, setStringParam, setFurniIds ]);
|
}, [ trigger, hasSpecialInput, setIntParams, setStringParam, setFurniIds ]);
|
||||||
|
|
||||||
|
useEffect(() =>
|
||||||
|
{
|
||||||
|
return () => clearRoomAreaSelection();
|
||||||
|
}, []);
|
||||||
|
|
||||||
useEffect(() =>
|
useEffect(() =>
|
||||||
{
|
{
|
||||||
if(!trigger) return;
|
if(!trigger) return;
|
||||||
@@ -99,8 +111,16 @@ export const WiredBaseView: FC<PropsWithChildren<WiredBaseViewProps>> = props =>
|
|||||||
setAllowsFurni(requiresFurni);
|
setAllowsFurni(requiresFurni);
|
||||||
}, [ trigger, requiresFurni, setAllowsFurni ]);
|
}, [ trigger, requiresFurni, setAllowsFurni ]);
|
||||||
|
|
||||||
|
const resolvedCardStyle: CSSProperties = { ...cardStyle };
|
||||||
|
|
||||||
|
if(resolvedCardStyle.width !== undefined)
|
||||||
|
{
|
||||||
|
resolvedCardStyle.minWidth = resolvedCardStyle.width;
|
||||||
|
delete resolvedCardStyle.width;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<NitroCardView className="nitro-wired" theme="primary-slim" uniqueKey="nitro-wired" style={ cardStyle }>
|
<NitroCardView className="nitro-wired" theme="primary-slim" uniqueKey="nitro-wired" isResizable={ false } style={ resolvedCardStyle }>
|
||||||
<NitroCardHeaderView headerText={ LocalizeText('wiredfurni.title') } onCloseClick={ onClose } />
|
<NitroCardHeaderView headerText={ LocalizeText('wiredfurni.title') } onCloseClick={ onClose } />
|
||||||
<NitroCardContentView>
|
<NitroCardContentView>
|
||||||
<div className="flex flex-col gap-1">
|
<div className="flex flex-col gap-1">
|
||||||
|
|||||||
@@ -0,0 +1,53 @@
|
|||||||
|
import { FC } from 'react';
|
||||||
|
import iconWiredDirE from '../../../assets/images/wired/icon_wired_dir_e.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 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 iconWiredDirW from '../../../assets/images/wired/icon_wired_dir_w.png';
|
||||||
|
|
||||||
|
export const WIRED_DIRECTION_GRID = [
|
||||||
|
[ 7, 0, 1, 2 ],
|
||||||
|
[ 6, 5, 4, 3 ]
|
||||||
|
];
|
||||||
|
|
||||||
|
interface WiredDirectionIconProps
|
||||||
|
{
|
||||||
|
direction: number;
|
||||||
|
selected?: boolean;
|
||||||
|
debugValue?: number | string;
|
||||||
|
iconSrc?: string;
|
||||||
|
showImage?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const DIRECTION_ICON_MAP: Record<number, string> = {
|
||||||
|
0: iconWiredDirN,
|
||||||
|
1: iconWiredDirNe,
|
||||||
|
2: iconWiredDirE,
|
||||||
|
3: iconWiredDirSe,
|
||||||
|
4: iconWiredDirS,
|
||||||
|
5: iconWiredDirSw,
|
||||||
|
6: iconWiredDirW,
|
||||||
|
7: iconWiredDirNw
|
||||||
|
};
|
||||||
|
|
||||||
|
export const WiredDirectionIcon: FC<WiredDirectionIconProps> = props =>
|
||||||
|
{
|
||||||
|
const { direction = 0, selected = false, debugValue = null, iconSrc = null, showImage = true } = props;
|
||||||
|
const icon = iconSrc ?? DIRECTION_ICON_MAP[direction];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<span className="inline-flex flex-col items-center justify-center leading-none">
|
||||||
|
{ showImage &&
|
||||||
|
<img
|
||||||
|
alt=""
|
||||||
|
className={ `h-auto w-auto object-contain ${ selected ? 'brightness-100' : 'opacity-90' }` }
|
||||||
|
draggable={ false }
|
||||||
|
src={ icon } /> }
|
||||||
|
{ (debugValue !== null && debugValue !== undefined) &&
|
||||||
|
<span className={ `${ showImage ? 'mt-[1px]' : '' } text-[9px] text-black` }>{ debugValue }</span> }
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
import { RoomObjectVariable } from '@nitrots/nitro-renderer';
|
||||||
|
import { FC, useMemo } from 'react';
|
||||||
|
import { GetOwnRoomObject, LocalizeText } from '../../../api';
|
||||||
|
import { Button, Text } from '../../../common';
|
||||||
|
|
||||||
|
export const DEFAULT_HAND_ITEM_IDS: number[] = [ 2, 5, 7, 8, 9, 10, 27 ];
|
||||||
|
|
||||||
|
interface WiredHandItemFieldProps
|
||||||
|
{
|
||||||
|
handItemId: number;
|
||||||
|
onChange: (value: number) => void;
|
||||||
|
labelKey?: string;
|
||||||
|
showCopyButton?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const WiredHandItemField: FC<WiredHandItemFieldProps> = props =>
|
||||||
|
{
|
||||||
|
const { handItemId = 0, onChange = null, labelKey = 'wiredfurni.params.handitem', showCopyButton = false } = props;
|
||||||
|
|
||||||
|
const options = useMemo(() =>
|
||||||
|
{
|
||||||
|
const values = [ ...DEFAULT_HAND_ITEM_IDS ];
|
||||||
|
|
||||||
|
if(handItemId > 0 && !values.includes(handItemId)) values.unshift(handItemId);
|
||||||
|
|
||||||
|
return values;
|
||||||
|
}, [ handItemId ]);
|
||||||
|
|
||||||
|
const getLabel = (value: number) =>
|
||||||
|
{
|
||||||
|
const key = `handitem${ value }`;
|
||||||
|
const localized = LocalizeText(key);
|
||||||
|
|
||||||
|
if(localized && localized !== key) return localized;
|
||||||
|
|
||||||
|
return `${ value }`;
|
||||||
|
};
|
||||||
|
|
||||||
|
const copyOwnHandItem = () =>
|
||||||
|
{
|
||||||
|
const roomObject = GetOwnRoomObject();
|
||||||
|
const copiedHandItem = (roomObject?.model?.getValue<number>(RoomObjectVariable.FIGURE_CARRY_OBJECT) || 0);
|
||||||
|
|
||||||
|
onChange && onChange(copiedHandItem);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col gap-1">
|
||||||
|
<Text bold>{ LocalizeText(labelKey) }</Text>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<select className="form-select form-select-sm flex-1" value={ handItemId } onChange={ event => onChange(parseInt(event.target.value)) }>
|
||||||
|
<option value={ 0 }>------</option>
|
||||||
|
{ options.map(value => <option key={ value } value={ value }>{ getLabel(value) }</option>) }
|
||||||
|
</select>
|
||||||
|
{ showCopyButton && <Button variant="secondary" onClick={ copyOwnHandItem }>{ LocalizeText('wiredfurni.params.capture.handitem') }</Button> }
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -16,6 +16,13 @@ export const USER_SOURCES = [
|
|||||||
{ value: 201, label: 'wiredfurni.params.sources.users.201' }
|
{ value: 201, label: 'wiredfurni.params.sources.users.201' }
|
||||||
];
|
];
|
||||||
|
|
||||||
|
export const BOT_SOURCES = [
|
||||||
|
{ 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' }
|
||||||
|
];
|
||||||
|
|
||||||
export interface WiredSourceOption
|
export interface WiredSourceOption
|
||||||
{
|
{
|
||||||
value: number;
|
value: number;
|
||||||
|
|||||||
@@ -5,31 +5,43 @@ import { Button, LayoutAvatarImageView, Text } from '../../../../common';
|
|||||||
import { useWired } from '../../../../hooks';
|
import { useWired } from '../../../../hooks';
|
||||||
import { NitroInput } from '../../../../layout';
|
import { NitroInput } from '../../../../layout';
|
||||||
import { WiredActionBaseView } from './WiredActionBaseView';
|
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 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 =>
|
export const WiredActionBotChangeFigureView: FC<{}> = props =>
|
||||||
{
|
{
|
||||||
const [ botName, setBotName ] = useState('');
|
const [ botName, setBotName ] = useState('');
|
||||||
const [ figure, setFigure ] = 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(() =>
|
useEffect(() =>
|
||||||
{
|
{
|
||||||
const data = trigger.stringData.split(WIRED_STRING_DELIMETER);
|
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);
|
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 ]);
|
}, [ trigger ]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<WiredActionBaseView hasSpecialInput={ true } requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE } save={ save }>
|
<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))) } /> }>
|
||||||
<div className="flex flex-col gap-1">
|
{ (botSource === 100) &&
|
||||||
<Text bold>{ LocalizeText('wiredfurni.params.bot.name') }</Text>
|
<div className="flex flex-col gap-1">
|
||||||
<NitroInput maxLength={ 32 } type="text" value={ botName } onChange={ event => setBotName(event.target.value) } />
|
<Text bold>{ LocalizeText('wiredfurni.params.bot.name') }</Text>
|
||||||
</div>
|
<NitroInput maxLength={ 32 } type="text" value={ botName } onChange={ event => setBotName(event.target.value) } />
|
||||||
|
</div> }
|
||||||
<div className="flex items-center justify-center">
|
<div className="flex items-center justify-center">
|
||||||
<LayoutAvatarImageView direction={ 4 } figure={ figure } />
|
<LayoutAvatarImageView direction={ 4 } figure={ figure } />
|
||||||
<Button onClick={ event => setFigure(GetSessionDataManager().figure) }>{ LocalizeText('wiredfurni.params.capture.figure') }</Button>
|
<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 { useWired } from '../../../../hooks';
|
||||||
import { NitroInput } from '../../../../layout';
|
import { NitroInput } from '../../../../layout';
|
||||||
import { WiredActionBaseView } from './WiredActionBaseView';
|
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 =>
|
export const WiredActionBotFollowAvatarView: FC<{}> = props =>
|
||||||
{
|
{
|
||||||
const [ botName, setBotName ] = useState('');
|
const [ botName, setBotName ] = useState('');
|
||||||
const [ followMode, setFollowMode ] = useState(-1);
|
const [ followMode, setFollowMode ] = useState(-1);
|
||||||
const { trigger = null, setStringParam = null, setIntParams = null } = useWired();
|
const { trigger = null, setStringParam = null, setIntParams = null } = useWired();
|
||||||
|
const [ botSource, setBotSource ] = useState<number>(100);
|
||||||
const [ userSource, setUserSource ] = useState<number>(() =>
|
const [ userSource, setUserSource ] = useState<number>(() =>
|
||||||
{
|
{
|
||||||
if(trigger?.intData?.length > 1) return trigger.intData[1];
|
if(trigger?.intData?.length > 1) return trigger.intData[1];
|
||||||
@@ -19,15 +22,17 @@ export const WiredActionBotFollowAvatarView: FC<{}> = props =>
|
|||||||
|
|
||||||
const save = () =>
|
const save = () =>
|
||||||
{
|
{
|
||||||
setStringParam(botName);
|
setStringParam((botSource === 100) ? botName : '');
|
||||||
setIntParams([ followMode, userSource ]);
|
setIntParams([ followMode, userSource, botSource ]);
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() =>
|
useEffect(() =>
|
||||||
{
|
{
|
||||||
setBotName(trigger.stringData);
|
const nextBotName = trigger.stringData || '';
|
||||||
|
setBotName(nextBotName);
|
||||||
setFollowMode((trigger.intData.length > 0) ? trigger.intData[0] : 0);
|
setFollowMode((trigger.intData.length > 0) ? trigger.intData[0] : 0);
|
||||||
setUserSource((trigger.intData.length > 1) ? trigger.intData[1] : 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 ]);
|
}, [ trigger ]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -35,11 +40,18 @@ export const WiredActionBotFollowAvatarView: FC<{}> = props =>
|
|||||||
hasSpecialInput={ true }
|
hasSpecialInput={ true }
|
||||||
requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE }
|
requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE }
|
||||||
save={ save }
|
save={ save }
|
||||||
footer={ <WiredSourcesSelector showUsers={ true } userSource={ userSource } onChangeUsers={ setUserSource } /> }>
|
footer={
|
||||||
<div className="flex flex-col gap-1">
|
<div className="flex flex-col gap-2">
|
||||||
<Text bold>{ LocalizeText('wiredfurni.params.bot.name') }</Text>
|
<WiredSourcesSelector showUsers={ true } userSource={ userSource } onChangeUsers={ setUserSource } />
|
||||||
<NitroInput maxLength={ 32 } type="text" value={ botName } onChange={ event => setBotName(event.target.value) } />
|
<hr className="m-0 bg-dark" />
|
||||||
</div>
|
<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 flex-col gap-1">
|
||||||
<div className="flex items-center 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) } />
|
<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 { useWired } from '../../../../hooks';
|
||||||
import { NitroInput } from '../../../../layout';
|
import { NitroInput } from '../../../../layout';
|
||||||
import { WiredActionBaseView } from './WiredActionBaseView';
|
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 =>
|
export const WiredActionBotGiveHandItemView: FC<{}> = props =>
|
||||||
{
|
{
|
||||||
const [ botName, setBotName ] = useState('');
|
const [ botName, setBotName ] = useState('');
|
||||||
const [ handItemId, setHandItemId ] = useState(-1);
|
const [ handItemId, setHandItemId ] = useState(-1);
|
||||||
const { trigger = null, setStringParam = null, setIntParams = null } = useWired();
|
const { trigger = null, setStringParam = null, setIntParams = null } = useWired();
|
||||||
const [ userSource, setUserSource ] = useState<number>(() =>
|
const [ userSource, setUserSource ] = useState<number>(0);
|
||||||
{
|
const [ botSource, setBotSource ] = useState<number>(100);
|
||||||
if(trigger?.intData?.length > 1) return trigger.intData[1];
|
|
||||||
return 0;
|
|
||||||
});
|
|
||||||
|
|
||||||
const save = () =>
|
const save = () =>
|
||||||
{
|
{
|
||||||
setStringParam(botName);
|
setStringParam((botSource === 100) ? botName : '');
|
||||||
setIntParams([ handItemId, userSource ]);
|
setIntParams([ handItemId, userSource, botSource ]);
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() =>
|
useEffect(() =>
|
||||||
{
|
{
|
||||||
setBotName(trigger.stringData);
|
const nextBotName = trigger.stringData || '';
|
||||||
|
|
||||||
|
setBotName(nextBotName);
|
||||||
setHandItemId((trigger.intData.length > 0) ? trigger.intData[0] : 0);
|
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 ]);
|
}, [ trigger ]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -37,18 +52,23 @@ export const WiredActionBotGiveHandItemView: FC<{}> = props =>
|
|||||||
hasSpecialInput={ true }
|
hasSpecialInput={ true }
|
||||||
requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE }
|
requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE }
|
||||||
save={ save }
|
save={ save }
|
||||||
footer={ <WiredSourcesSelector showUsers={ true } userSource={ userSource } onChangeUsers={ setUserSource } /> }>
|
footer={
|
||||||
<div className="flex flex-col gap-1">
|
<div className="flex flex-col gap-2">
|
||||||
<Text bold>{ LocalizeText('wiredfurni.params.bot.name') }</Text>
|
<WiredSourcesSelector showUsers={ true } userSource={ userSource } userSources={ USER_SOURCE_OPTIONS } onChangeUsers={ setUserSource } />
|
||||||
<NitroInput maxLength={ 32 } type="text" value={ botName } onChange={ event => setBotName(event.target.value) } />
|
<hr className="m-0 bg-dark" />
|
||||||
</div>
|
<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 className="flex flex-col gap-1">
|
</div>
|
||||||
<Text bold>{ LocalizeText('wiredfurni.params.handitem') }</Text>
|
}>
|
||||||
<select className="form-select form-select-sm" value={ handItemId } onChange={ event => setHandItemId(parseInt(event.target.value)) }>
|
<div className="form-check">
|
||||||
<option value="0">------</option>
|
<input checked={ (botSource === 100) } className="form-check-input" id="botGiveHandItemUseNamedBot" type="checkbox" onChange={ event => setBotSource(event.target.checked ? 100 : 0) } />
|
||||||
{ ALLOWED_HAND_ITEM_IDS.map(value => <option key={ value } value={ value }>{ LocalizeText(`handitem${ value }`) }</option>) }
|
<label className="form-check-label" htmlFor="botGiveHandItemUseNamedBot">{ LocalizeText('wiredfurni.params.bot.usage') }</label>
|
||||||
</select>
|
|
||||||
</div>
|
</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>
|
</WiredActionBaseView>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,11 +4,14 @@ import { Text } from '../../../../common';
|
|||||||
import { useWired } from '../../../../hooks';
|
import { useWired } from '../../../../hooks';
|
||||||
import { NitroInput } from '../../../../layout';
|
import { NitroInput } from '../../../../layout';
|
||||||
import { WiredActionBaseView } from './WiredActionBaseView';
|
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 =>
|
export const WiredActionBotMoveView: FC<{}> = props =>
|
||||||
{
|
{
|
||||||
const [ botName, setBotName ] = useState('');
|
const [ botName, setBotName ] = useState('');
|
||||||
|
const [ botSource, setBotSource ] = useState<number>(100);
|
||||||
const { trigger = null, setStringParam = null, setIntParams = null } = useWired();
|
const { trigger = null, setStringParam = null, setIntParams = null } = useWired();
|
||||||
|
|
||||||
const [ furniSource, setFurniSource ] = useState<number>(() =>
|
const [ furniSource, setFurniSource ] = useState<number>(() =>
|
||||||
@@ -19,18 +22,21 @@ export const WiredActionBotMoveView: FC<{}> = props =>
|
|||||||
|
|
||||||
const save = () =>
|
const save = () =>
|
||||||
{
|
{
|
||||||
setStringParam(botName);
|
setStringParam((botSource === 100) ? botName : '');
|
||||||
setIntParams([ furniSource ]);
|
setIntParams([ furniSource, botSource ]);
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() =>
|
useEffect(() =>
|
||||||
{
|
{
|
||||||
if(!trigger) return;
|
if(!trigger) return;
|
||||||
|
|
||||||
setBotName(trigger.stringData);
|
const nextBotName = trigger.stringData || '';
|
||||||
|
setBotName(nextBotName);
|
||||||
|
|
||||||
if(trigger.intData.length >= 1) setFurniSource(trigger.intData[0]);
|
if(trigger.intData.length >= 1) setFurniSource(trigger.intData[0]);
|
||||||
else setFurniSource((trigger.selectedItems?.length ?? 0) > 0 ? 100 : 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 ]);
|
}, [ trigger ]);
|
||||||
|
|
||||||
const onChangeFurniSource = (next: number) => setFurniSource(next);
|
const onChangeFurniSource = (next: number) => setFurniSource(next);
|
||||||
@@ -42,11 +48,18 @@ export const WiredActionBotMoveView: FC<{}> = props =>
|
|||||||
hasSpecialInput={ true }
|
hasSpecialInput={ true }
|
||||||
requiresFurni={ requiresFurni }
|
requiresFurni={ requiresFurni }
|
||||||
save={ save }
|
save={ save }
|
||||||
footer={ <WiredSourcesSelector showFurni={ true } furniSource={ furniSource } onChangeFurni={ onChangeFurniSource } /> }>
|
footer={
|
||||||
<div className="flex flex-col gap-1">
|
<div className="flex flex-col gap-2">
|
||||||
<Text bold>{ LocalizeText('wiredfurni.params.bot.name') }</Text>
|
<WiredSourcesSelector showFurni={ true } furniSource={ furniSource } onChangeFurni={ onChangeFurniSource } />
|
||||||
<NitroInput maxLength={ 32 } type="text" value={ botName } onChange={ event => setBotName(event.target.value) } />
|
<hr className="m-0 bg-dark" />
|
||||||
</div>
|
<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>
|
</WiredActionBaseView>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,13 +4,16 @@ import { Text } from '../../../../common';
|
|||||||
import { useWired } from '../../../../hooks';
|
import { useWired } from '../../../../hooks';
|
||||||
import { NitroInput } from '../../../../layout';
|
import { NitroInput } from '../../../../layout';
|
||||||
import { WiredActionBaseView } from './WiredActionBaseView';
|
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 =>
|
export const WiredActionBotTalkToAvatarView: FC<{}> = props =>
|
||||||
{
|
{
|
||||||
const [ botName, setBotName ] = useState('');
|
const [ botName, setBotName ] = useState('');
|
||||||
const [ message, setMessage ] = useState('');
|
const [ message, setMessage ] = useState('');
|
||||||
const [ talkMode, setTalkMode ] = useState(-1);
|
const [ talkMode, setTalkMode ] = useState(-1);
|
||||||
|
const [ botSource, setBotSource ] = useState<number>(100);
|
||||||
const { trigger = null, setStringParam = null, setIntParams = null } = useWired();
|
const { trigger = null, setStringParam = null, setIntParams = null } = useWired();
|
||||||
const [ userSource, setUserSource ] = useState<number>(() =>
|
const [ userSource, setUserSource ] = useState<number>(() =>
|
||||||
{
|
{
|
||||||
@@ -20,19 +23,21 @@ export const WiredActionBotTalkToAvatarView: FC<{}> = props =>
|
|||||||
|
|
||||||
const save = () =>
|
const save = () =>
|
||||||
{
|
{
|
||||||
setStringParam(botName + WIRED_STRING_DELIMETER + message);
|
setStringParam(((botSource === 100) ? botName : '') + WIRED_STRING_DELIMETER + message);
|
||||||
setIntParams([ talkMode, userSource ]);
|
setIntParams([ talkMode, userSource, botSource ]);
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() =>
|
useEffect(() =>
|
||||||
{
|
{
|
||||||
const data = trigger.stringData.split(WIRED_STRING_DELIMETER);
|
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] : '');
|
if(data.length > 1) setMessage(data[1].length > 0 ? data[1] : '');
|
||||||
|
|
||||||
setTalkMode((trigger.intData.length > 0) ? trigger.intData[0] : 0);
|
setTalkMode((trigger.intData.length > 0) ? trigger.intData[0] : 0);
|
||||||
setUserSource((trigger.intData.length > 1) ? trigger.intData[1] : 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 ]);
|
}, [ trigger ]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -40,11 +45,18 @@ export const WiredActionBotTalkToAvatarView: FC<{}> = props =>
|
|||||||
hasSpecialInput={ true }
|
hasSpecialInput={ true }
|
||||||
requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE }
|
requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE }
|
||||||
save={ save }
|
save={ save }
|
||||||
footer={ <WiredSourcesSelector showUsers={ true } userSource={ userSource } onChangeUsers={ setUserSource } /> }>
|
footer={
|
||||||
<div className="flex flex-col gap-1">
|
<div className="flex flex-col gap-2">
|
||||||
<Text bold>{ LocalizeText('wiredfurni.params.bot.name') }</Text>
|
<WiredSourcesSelector showUsers={ true } userSource={ userSource } onChangeUsers={ setUserSource } />
|
||||||
<NitroInput maxLength={ 32 } type="text" value={ botName } onChange={ event => setBotName(event.target.value) } />
|
<hr className="m-0 bg-dark" />
|
||||||
</div>
|
<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 flex-col gap-1">
|
||||||
<Text bold>{ LocalizeText('wiredfurni.params.message') }</Text>
|
<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) } />
|
<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 { useWired } from '../../../../hooks';
|
||||||
import { NitroInput } from '../../../../layout';
|
import { NitroInput } from '../../../../layout';
|
||||||
import { WiredActionBaseView } from './WiredActionBaseView';
|
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 =>
|
export const WiredActionBotTalkView: FC<{}> = props =>
|
||||||
{
|
{
|
||||||
const [ botName, setBotName ] = useState('');
|
const [ botName, setBotName ] = useState('');
|
||||||
const [ message, setMessage ] = useState('');
|
const [ message, setMessage ] = useState('');
|
||||||
const [ talkMode, setTalkMode ] = useState(-1);
|
const [ talkMode, setTalkMode ] = useState(-1);
|
||||||
|
const [ botSource, setBotSource ] = useState<number>(100);
|
||||||
const { trigger = null, setStringParam = null, setIntParams = null } = useWired();
|
const { trigger = null, setStringParam = null, setIntParams = null } = useWired();
|
||||||
|
|
||||||
const save = () =>
|
const save = () =>
|
||||||
{
|
{
|
||||||
setStringParam(botName + WIRED_STRING_DELIMETER + message);
|
setStringParam(((botSource === 100) ? botName : '') + WIRED_STRING_DELIMETER + message);
|
||||||
setIntParams([ talkMode ]);
|
setIntParams([ talkMode, botSource ]);
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() =>
|
useEffect(() =>
|
||||||
{
|
{
|
||||||
const data = trigger.stringData.split(WIRED_STRING_DELIMETER);
|
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] : '');
|
if(data.length > 1) setMessage(data[1].length > 0 ? data[1] : '');
|
||||||
|
|
||||||
setTalkMode((trigger.intData.length > 0) ? trigger.intData[0] : 0);
|
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 ]);
|
}, [ trigger ]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<WiredActionBaseView hasSpecialInput={ true } requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE } save={ save }>
|
<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))) } /> }>
|
||||||
<div className="flex flex-col gap-1">
|
{ (botSource === 100) &&
|
||||||
<Text bold>{ LocalizeText('wiredfurni.params.bot.name') }</Text>
|
<div className="flex flex-col gap-1">
|
||||||
<NitroInput maxLength={ 32 } type="text" value={ botName } onChange={ event => setBotName(event.target.value) } />
|
<Text bold>{ LocalizeText('wiredfurni.params.bot.name') }</Text>
|
||||||
</div>
|
<NitroInput maxLength={ 32 } type="text" value={ botName } onChange={ event => setBotName(event.target.value) } />
|
||||||
|
</div> }
|
||||||
<div className="flex flex-col gap-1">
|
<div className="flex flex-col gap-1">
|
||||||
<Text bold>{ LocalizeText('wiredfurni.params.message') }</Text>
|
<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) } />
|
<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 { useWired } from '../../../../hooks';
|
||||||
import { NitroInput } from '../../../../layout';
|
import { NitroInput } from '../../../../layout';
|
||||||
import { WiredActionBaseView } from './WiredActionBaseView';
|
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 =>
|
export const WiredActionBotTeleportView: FC<{}> = props =>
|
||||||
{
|
{
|
||||||
const [ botName, setBotName ] = useState('');
|
const [ botName, setBotName ] = useState('');
|
||||||
|
const [ botSource, setBotSource ] = useState<number>(100);
|
||||||
const { trigger = null, setStringParam = null, setIntParams = null } = useWired();
|
const { trigger = null, setStringParam = null, setIntParams = null } = useWired();
|
||||||
|
|
||||||
const [ furniSource, setFurniSource ] = useState<number>(() =>
|
const [ furniSource, setFurniSource ] = useState<number>(() =>
|
||||||
@@ -19,18 +22,21 @@ export const WiredActionBotTeleportView: FC<{}> = props =>
|
|||||||
|
|
||||||
const save = () =>
|
const save = () =>
|
||||||
{
|
{
|
||||||
setStringParam(botName);
|
setStringParam((botSource === 100) ? botName : '');
|
||||||
setIntParams([ furniSource ]);
|
setIntParams([ furniSource, botSource ]);
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() =>
|
useEffect(() =>
|
||||||
{
|
{
|
||||||
if(!trigger) return;
|
if(!trigger) return;
|
||||||
|
|
||||||
setBotName(trigger.stringData);
|
const nextBotName = trigger.stringData || '';
|
||||||
|
setBotName(nextBotName);
|
||||||
|
|
||||||
if(trigger.intData.length >= 1) setFurniSource(trigger.intData[0]);
|
if(trigger.intData.length >= 1) setFurniSource(trigger.intData[0]);
|
||||||
else setFurniSource((trigger.selectedItems?.length ?? 0) > 0 ? 100 : 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 ]);
|
}, [ trigger ]);
|
||||||
|
|
||||||
const onChangeFurniSource = (next: number) => setFurniSource(next);
|
const onChangeFurniSource = (next: number) => setFurniSource(next);
|
||||||
@@ -42,11 +48,18 @@ export const WiredActionBotTeleportView: FC<{}> = props =>
|
|||||||
hasSpecialInput={ true }
|
hasSpecialInput={ true }
|
||||||
requiresFurni={ requiresFurni }
|
requiresFurni={ requiresFurni }
|
||||||
save={ save }
|
save={ save }
|
||||||
footer={ <WiredSourcesSelector showFurni={ true } furniSource={ furniSource } onChangeFurni={ onChangeFurniSource } /> }>
|
footer={
|
||||||
<div className="flex flex-col gap-1">
|
<div className="flex flex-col gap-2">
|
||||||
<Text bold>{ LocalizeText('wiredfurni.params.bot.name') }</Text>
|
<WiredSourcesSelector showFurni={ true } furniSource={ furniSource } onChangeFurni={ onChangeFurniSource } />
|
||||||
<NitroInput maxLength={ 32 } type="text" value={ botName } onChange={ event => setBotName(event.target.value) } />
|
<hr className="m-0 bg-dark" />
|
||||||
</div>
|
<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>
|
</WiredActionBaseView>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -7,24 +7,24 @@ import { WiredActionBaseView } from './WiredActionBaseView';
|
|||||||
export const WiredActionGiveScoreToPredefinedTeamView: FC<{}> = props =>
|
export const WiredActionGiveScoreToPredefinedTeamView: FC<{}> = props =>
|
||||||
{
|
{
|
||||||
const [ points, setPoints ] = useState(1);
|
const [ points, setPoints ] = useState(1);
|
||||||
const [ time, setTime ] = useState(1);
|
const [ operation, setOperation ] = useState(0);
|
||||||
const [ selectedTeam, setSelectedTeam ] = useState(1);
|
const [ selectedTeam, setSelectedTeam ] = useState(1);
|
||||||
const { trigger = null, setIntParams = null } = useWired();
|
const { trigger = null, setIntParams = null } = useWired();
|
||||||
|
|
||||||
const save = () => setIntParams([ points, time, selectedTeam ]);
|
const save = () => setIntParams([ points, operation, selectedTeam ]);
|
||||||
|
|
||||||
useEffect(() =>
|
useEffect(() =>
|
||||||
{
|
{
|
||||||
if(trigger.intData.length >= 2)
|
if(trigger.intData.length >= 3)
|
||||||
{
|
{
|
||||||
setPoints(trigger.intData[0]);
|
setPoints(trigger.intData[0]);
|
||||||
setTime(trigger.intData[1]);
|
setOperation(trigger.intData[1]);
|
||||||
setSelectedTeam(trigger.intData[2]);
|
setSelectedTeam(trigger.intData[2]);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
setPoints(1);
|
setPoints(1);
|
||||||
setTime(1);
|
setOperation(0);
|
||||||
setSelectedTeam(1);
|
setSelectedTeam(1);
|
||||||
}
|
}
|
||||||
}, [ trigger ]);
|
}, [ trigger ]);
|
||||||
@@ -40,12 +40,13 @@ export const WiredActionGiveScoreToPredefinedTeamView: FC<{}> = props =>
|
|||||||
onChange={ event => setPoints(event) } />
|
onChange={ event => setPoints(event) } />
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col gap-1">
|
<div className="flex flex-col gap-1">
|
||||||
<Text bold>{ LocalizeText('wiredfurni.params.settimesingame', [ 'times' ], [ time.toString() ]) }</Text>
|
<Text bold>{ LocalizeText('wiredfurni.params.choose_type') }</Text>
|
||||||
<Slider
|
{ [ 0, 1 ].map(value => (
|
||||||
max={ 10 }
|
<label key={ value } className="flex items-center gap-1">
|
||||||
min={ 1 }
|
<input checked={ (operation === value) } className="form-check-input" name="pointsOperation" type="radio" onChange={ () => setOperation(value) } />
|
||||||
value={ time }
|
<Text>{ LocalizeText(`wiredfurni.params.points_operation.${ value }`) }</Text>
|
||||||
onChange={ event => setTime(event) } />
|
</label>
|
||||||
|
)) }
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col gap-1">
|
<div className="flex flex-col gap-1">
|
||||||
<Text bold>{ LocalizeText('wiredfurni.params.team') }</Text>
|
<Text bold>{ LocalizeText('wiredfurni.params.team') }</Text>
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import { WiredSourcesSelector } from '../WiredSourcesSelector';
|
|||||||
export const WiredActionGiveScoreView: FC<{}> = props =>
|
export const WiredActionGiveScoreView: FC<{}> = props =>
|
||||||
{
|
{
|
||||||
const [ points, setPoints ] = useState(1);
|
const [ points, setPoints ] = useState(1);
|
||||||
const [ time, setTime ] = useState(1);
|
const [ operation, setOperation ] = useState(0);
|
||||||
const { trigger = null, setIntParams = null } = useWired();
|
const { trigger = null, setIntParams = null } = useWired();
|
||||||
const [ userSource, setUserSource ] = useState<number>(() =>
|
const [ userSource, setUserSource ] = useState<number>(() =>
|
||||||
{
|
{
|
||||||
@@ -16,19 +16,19 @@ export const WiredActionGiveScoreView: FC<{}> = props =>
|
|||||||
return 0;
|
return 0;
|
||||||
});
|
});
|
||||||
|
|
||||||
const save = () => setIntParams([ points, time, userSource ]);
|
const save = () => setIntParams([ points, operation, userSource ]);
|
||||||
|
|
||||||
useEffect(() =>
|
useEffect(() =>
|
||||||
{
|
{
|
||||||
if(trigger.intData.length >= 2)
|
if(trigger.intData.length >= 2)
|
||||||
{
|
{
|
||||||
setPoints(trigger.intData[0]);
|
setPoints(trigger.intData[0]);
|
||||||
setTime(trigger.intData[1]);
|
setOperation(trigger.intData[1]);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
setPoints(1);
|
setPoints(1);
|
||||||
setTime(1);
|
setOperation(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
setUserSource((trigger.intData.length > 2) ? trigger.intData[2] : 0);
|
setUserSource((trigger.intData.length > 2) ? trigger.intData[2] : 0);
|
||||||
@@ -49,12 +49,13 @@ export const WiredActionGiveScoreView: FC<{}> = props =>
|
|||||||
onChange={ event => setPoints(event) } />
|
onChange={ event => setPoints(event) } />
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col gap-1">
|
<div className="flex flex-col gap-1">
|
||||||
<Text bold>{ LocalizeText('wiredfurni.params.settimesingame', [ 'times' ], [ time.toString() ]) }</Text>
|
<Text bold>{ LocalizeText('wiredfurni.params.choose_type') }</Text>
|
||||||
<Slider
|
{ [ 0, 1 ].map(value => (
|
||||||
max={ 10 }
|
<label key={ value } className="flex items-center gap-1">
|
||||||
min={ 1 }
|
<input checked={ (operation === value) } className="form-check-input" name="pointsOperation" type="radio" onChange={ () => setOperation(value) } />
|
||||||
value={ time }
|
<Text>{ LocalizeText(`wiredfurni.params.points_operation.${ value }`) }</Text>
|
||||||
onChange={ event => setTime(event) } />
|
</label>
|
||||||
|
)) }
|
||||||
</div>
|
</div>
|
||||||
</WiredActionBaseView>
|
</WiredActionBaseView>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -7,20 +7,32 @@ import { WiredSourcesSelector } from '../WiredSourcesSelector';
|
|||||||
|
|
||||||
export const WiredActionJoinTeamView: FC<{}> = props =>
|
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 { trigger = null, setIntParams = null } = useWired();
|
||||||
const [ userSource, setUserSource ] = useState<number>(() =>
|
const [ userSource, setUserSource ] = useState<number>(() =>
|
||||||
{
|
{
|
||||||
|
if(trigger?.intData?.length > 2) return trigger.intData[2];
|
||||||
if(trigger?.intData?.length > 1) return trigger.intData[1];
|
if(trigger?.intData?.length > 1) return trigger.intData[1];
|
||||||
return 0;
|
return 0;
|
||||||
});
|
});
|
||||||
|
|
||||||
const save = () => setIntParams([ selectedTeam, userSource ]);
|
const save = () => setIntParams([ selectedTeamType, selectedTeam, userSource ]);
|
||||||
|
|
||||||
useEffect(() =>
|
useEffect(() =>
|
||||||
{
|
{
|
||||||
setSelectedTeam((trigger.intData.length > 0) ? trigger.intData[0] : 0);
|
if(trigger.intData.length > 2)
|
||||||
setUserSource((trigger.intData.length > 1) ? trigger.intData[1] : 0);
|
{
|
||||||
|
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 ]);
|
}, [ trigger ]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -29,6 +41,22 @@ export const WiredActionJoinTeamView: FC<{}> = props =>
|
|||||||
requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE }
|
requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE }
|
||||||
save={ save }
|
save={ save }
|
||||||
footer={ <WiredSourcesSelector showUsers={ true } userSource={ userSource } onChangeUsers={ setUserSource } /> }>
|
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">
|
<div className="flex flex-col gap-1">
|
||||||
<Text bold>{ LocalizeText('wiredfurni.params.team') }</Text>
|
<Text bold>{ LocalizeText('wiredfurni.params.team') }</Text>
|
||||||
{ [ 1, 2, 3, 4 ].map(team =>
|
{ [ 1, 2, 3, 4 ].map(team =>
|
||||||
|
|||||||
@@ -9,8 +9,20 @@ import { WiredActionSendSignalView } from './WiredActionSendSignalView';
|
|||||||
import { WiredActionFurniAreaView } from '../selectors/WiredActionFurniAreaView';
|
import { WiredActionFurniAreaView } from '../selectors/WiredActionFurniAreaView';
|
||||||
import { WiredSelectorFurniNeighborhoodView } from '../selectors/WiredSelectorFurniNeighborhoodView';
|
import { WiredSelectorFurniNeighborhoodView } from '../selectors/WiredSelectorFurniNeighborhoodView';
|
||||||
import { WiredSelectorFurniByTypeView } from '../selectors/WiredSelectorFurniByTypeView';
|
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 { 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 { WiredSelectorUsersNeighborhoodView } from '../selectors/WiredSelectorUsersNeighborhoodView';
|
||||||
|
import { WiredSelectorUsersSignalView } from '../selectors/WiredSelectorUsersSignalView';
|
||||||
|
import { WiredSelectorUsersTeamView } from '../selectors/WiredSelectorUsersTeamView';
|
||||||
import { WiredActionBotFollowAvatarView } from './WiredActionBotFollowAvatarView';
|
import { WiredActionBotFollowAvatarView } from './WiredActionBotFollowAvatarView';
|
||||||
import { WiredActionBotGiveHandItemView } from './WiredActionBotGiveHandItemView';
|
import { WiredActionBotGiveHandItemView } from './WiredActionBotGiveHandItemView';
|
||||||
import { WiredActionBotMoveView } from './WiredActionBotMoveView';
|
import { WiredActionBotMoveView } from './WiredActionBotMoveView';
|
||||||
@@ -28,6 +40,7 @@ import { WiredActionJoinTeamView } from './WiredActionJoinTeamView';
|
|||||||
import { WiredActionKickFromRoomView } from './WiredActionKickFromRoomView';
|
import { WiredActionKickFromRoomView } from './WiredActionKickFromRoomView';
|
||||||
import { WiredActionLeaveTeamView } from './WiredActionLeaveTeamView';
|
import { WiredActionLeaveTeamView } from './WiredActionLeaveTeamView';
|
||||||
import { WiredActionMoveAndRotateFurniView } from './WiredActionMoveAndRotateFurniView';
|
import { WiredActionMoveAndRotateFurniView } from './WiredActionMoveAndRotateFurniView';
|
||||||
|
import { WiredActionMoveRotateUserView } from './WiredActionMoveRotateUserView';
|
||||||
import { WiredActionMoveFurniToView } from './WiredActionMoveFurniToView';
|
import { WiredActionMoveFurniToView } from './WiredActionMoveFurniToView';
|
||||||
import { WiredActionMoveFurniView } from './WiredActionMoveFurniView';
|
import { WiredActionMoveFurniView } from './WiredActionMoveFurniView';
|
||||||
import { WiredActionMuteUserView } from './WiredActionMuteUserView';
|
import { WiredActionMuteUserView } from './WiredActionMuteUserView';
|
||||||
@@ -37,6 +50,8 @@ import { WiredActionSetFurniStateToView } from './WiredActionSetFurniStateToView
|
|||||||
import { WiredActionTeleportView } from './WiredActionTeleportView';
|
import { WiredActionTeleportView } from './WiredActionTeleportView';
|
||||||
import { WiredActionToggleFurniStateView } from './WiredActionToggleFurniStateView';
|
import { WiredActionToggleFurniStateView } from './WiredActionToggleFurniStateView';
|
||||||
import { WiredActionUnfreezeView } from './WiredActionUnfreezeView';
|
import { WiredActionUnfreezeView } from './WiredActionUnfreezeView';
|
||||||
|
import { WiredExtraFilterFurniView } from '../extras/WiredExtraFilterFurniView';
|
||||||
|
import { WiredExtraFilterUserView } from '../extras/WiredExtraFilterUserView';
|
||||||
|
|
||||||
export const WiredActionLayoutView = (code: number) =>
|
export const WiredActionLayoutView = (code: number) =>
|
||||||
{
|
{
|
||||||
@@ -92,6 +107,8 @@ export const WiredActionLayoutView = (code: number) =>
|
|||||||
return <WiredActionMoveFurniView />;
|
return <WiredActionMoveFurniView />;
|
||||||
case WiredActionLayoutCode.MOVE_AND_ROTATE_FURNI:
|
case WiredActionLayoutCode.MOVE_AND_ROTATE_FURNI:
|
||||||
return <WiredActionMoveAndRotateFurniView />;
|
return <WiredActionMoveAndRotateFurniView />;
|
||||||
|
case WiredActionLayoutCode.MOVE_ROTATE_USER:
|
||||||
|
return <WiredActionMoveRotateUserView />;
|
||||||
case WiredActionLayoutCode.MOVE_FURNI_TO:
|
case WiredActionLayoutCode.MOVE_FURNI_TO:
|
||||||
return <WiredActionMoveFurniToView />;
|
return <WiredActionMoveFurniToView />;
|
||||||
case WiredActionLayoutCode.MUTE_USER:
|
case WiredActionLayoutCode.MUTE_USER:
|
||||||
@@ -116,10 +133,38 @@ export const WiredActionLayoutView = (code: number) =>
|
|||||||
return <WiredSelectorFurniNeighborhoodView />;
|
return <WiredSelectorFurniNeighborhoodView />;
|
||||||
case WiredActionLayoutCode.FURNI_BYTYPE_SELECTOR:
|
case WiredActionLayoutCode.FURNI_BYTYPE_SELECTOR:
|
||||||
return <WiredSelectorFurniByTypeView />;
|
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:
|
case WiredActionLayoutCode.USERS_AREA_SELECTOR:
|
||||||
return <WiredSelectorUsersAreaView />;
|
return <WiredSelectorUsersAreaView />;
|
||||||
case WiredActionLayoutCode.USERS_NEIGHBORHOOD_SELECTOR:
|
case WiredActionLayoutCode.USERS_NEIGHBORHOOD_SELECTOR:
|
||||||
return <WiredSelectorUsersNeighborhoodView />;
|
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:
|
case WiredActionLayoutCode.SEND_SIGNAL:
|
||||||
return <WiredActionSendSignalView />;
|
return <WiredActionSendSignalView />;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,34 +2,17 @@ import { FC, useEffect, useState } from 'react';
|
|||||||
import { LocalizeText, WiredFurniType } from '../../../../api';
|
import { LocalizeText, WiredFurniType } from '../../../../api';
|
||||||
import { Text } from '../../../../common';
|
import { Text } from '../../../../common';
|
||||||
import { useWired } from '../../../../hooks';
|
import { useWired } from '../../../../hooks';
|
||||||
|
import { WiredDirectionIcon, WIRED_DIRECTION_GRID } from '../WiredDirectionIcon';
|
||||||
import { WiredActionBaseView } from './WiredActionBaseView';
|
import { WiredActionBaseView } from './WiredActionBaseView';
|
||||||
import { WiredSourcesSelector } from '../WiredSourcesSelector';
|
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 ];
|
const rotationOptions: number[] = [ 0, 1, 2, 3, 4, 5, 6 ];
|
||||||
|
|
||||||
export const WiredActionMoveAndRotateFurniView: FC<{}> = props =>
|
export const WiredActionMoveAndRotateFurniView: FC<{}> = props =>
|
||||||
{
|
{
|
||||||
const [ movement, setMovement ] = useState(-1);
|
const [ movement, setMovement ] = useState(-1);
|
||||||
const [ rotation, setRotation ] = useState(-1);
|
const [ rotation, setRotation ] = useState(-1);
|
||||||
|
const [ blockOnUserCollision, setBlockOnUserCollision ] = useState(false);
|
||||||
const { trigger = null, setIntParams = null } = useWired();
|
const { trigger = null, setIntParams = null } = useWired();
|
||||||
const [ furniSource, setFurniSource ] = useState<number>(() =>
|
const [ furniSource, setFurniSource ] = useState<number>(() =>
|
||||||
{
|
{
|
||||||
@@ -37,7 +20,7 @@ export const WiredActionMoveAndRotateFurniView: FC<{}> = props =>
|
|||||||
return (trigger?.selectedItems?.length ?? 0) > 0 ? 100 : 0;
|
return (trigger?.selectedItems?.length ?? 0) > 0 ? 100 : 0;
|
||||||
});
|
});
|
||||||
|
|
||||||
const save = () => setIntParams([ movement, rotation, furniSource ]);
|
const save = () => setIntParams([ movement, rotation, furniSource, blockOnUserCollision ? 1 : 0 ]);
|
||||||
|
|
||||||
useEffect(() =>
|
useEffect(() =>
|
||||||
{
|
{
|
||||||
@@ -54,6 +37,8 @@ export const WiredActionMoveAndRotateFurniView: FC<{}> = props =>
|
|||||||
|
|
||||||
if(trigger.intData.length > 2) setFurniSource(trigger.intData[2]);
|
if(trigger.intData.length > 2) setFurniSource(trigger.intData[2]);
|
||||||
else setFurniSource((trigger.selectedItems?.length ?? 0) > 0 ? 100 : 0);
|
else setFurniSource((trigger.selectedItems?.length ?? 0) > 0 ? 100 : 0);
|
||||||
|
|
||||||
|
setBlockOnUserCollision((trigger.intData?.length ?? 0) > 3 ? trigger.intData[3] === 1 : false);
|
||||||
}, [ trigger ]);
|
}, [ trigger ]);
|
||||||
|
|
||||||
const onChangeFurniSource = (next: number) => setFurniSource(next);
|
const onChangeFurniSource = (next: number) => setFurniSource(next);
|
||||||
@@ -68,18 +53,23 @@ export const WiredActionMoveAndRotateFurniView: FC<{}> = props =>
|
|||||||
footer={ <WiredSourcesSelector showFurni={ true } furniSource={ furniSource } onChangeFurni={ onChangeFurniSource } /> }>
|
footer={ <WiredSourcesSelector showFurni={ true } furniSource={ furniSource } onChangeFurni={ onChangeFurniSource } /> }>
|
||||||
<div className="flex flex-col gap-1">
|
<div className="flex flex-col gap-1">
|
||||||
<Text bold>{ LocalizeText('wiredfurni.params.startdir') }</Text>
|
<Text bold>{ LocalizeText('wiredfurni.params.startdir') }</Text>
|
||||||
<div className="flex gap-1">
|
<div className="grid grid-cols-4 gap-2 max-w-[240px]">
|
||||||
{ directionOptions.map(option =>
|
{ WIRED_DIRECTION_GRID.flatMap((row, rowIndex) => row.map((direction, columnIndex) =>
|
||||||
{
|
{
|
||||||
|
if(direction === null)
|
||||||
|
{
|
||||||
|
return <div key={ `move-to-dir-empty-${ rowIndex }-${ columnIndex }` } />;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div key={ option.value } className="flex items-center gap-1">
|
<label key={ `move-to-dir-${ 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) } />
|
<input checked={ (movement === direction) } className="form-check-input" id={ `movement${ direction }` } name="movement" type="radio" onChange={ () => setMovement(direction) } />
|
||||||
<Text>
|
<span className="inline-flex items-center justify-center">
|
||||||
<i className={ `icon icon-${ option.icon }` } />
|
<WiredDirectionIcon direction={ direction } selected={ movement === direction } />
|
||||||
</Text>
|
</span>
|
||||||
</div>
|
</label>
|
||||||
);
|
);
|
||||||
}) }
|
})) }
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col gap-1">
|
<div className="flex flex-col gap-1">
|
||||||
@@ -94,6 +84,13 @@ export const WiredActionMoveAndRotateFurniView: FC<{}> = props =>
|
|||||||
);
|
);
|
||||||
}) }
|
}) }
|
||||||
</div>
|
</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>
|
</WiredActionBaseView>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,37 +2,44 @@ import { FC, useEffect, useState } from 'react';
|
|||||||
import { LocalizeText, WiredFurniType } from '../../../../api';
|
import { LocalizeText, WiredFurniType } from '../../../../api';
|
||||||
import { Text } from '../../../../common';
|
import { Text } from '../../../../common';
|
||||||
import { useWired } from '../../../../hooks';
|
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 { WiredActionBaseView } from './WiredActionBaseView';
|
||||||
import { WiredSourcesSelector } from '../WiredSourcesSelector';
|
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,
|
value: 1,
|
||||||
icon: 'ne'
|
icon: iconWiredDirRandom
|
||||||
},
|
|
||||||
{
|
|
||||||
value: 5,
|
|
||||||
icon: 'se'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: 6,
|
|
||||||
icon: 'sw'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: 7,
|
|
||||||
icon: 'nw'
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
value: 2,
|
value: 2,
|
||||||
icon: 'mv-2'
|
icon: iconWiredDirHorizontalRandom
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
value: 3,
|
value: 3,
|
||||||
icon: 'mv-3'
|
icon: iconWiredDirVerticalRandom
|
||||||
},
|
|
||||||
{
|
|
||||||
value: 1,
|
|
||||||
icon: 'mv-1'
|
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -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) } />
|
<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>
|
<Text>{ LocalizeText('wiredfurni.params.movefurni.0') }</Text>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex gap-1">
|
<div className="grid grid-cols-4 gap-2 max-w-[240px]">
|
||||||
{ directionOptions.map(option =>
|
{ 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 (
|
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) } />
|
<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 }` } />
|
<span className="inline-flex items-center justify-center">
|
||||||
</div>
|
<WiredDirectionIcon direction={ option.value } iconSrc={ option.icon } selected={ movement === option.value } />
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
);
|
);
|
||||||
}) }
|
}) }
|
||||||
<div className="col" />
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col gap-1">
|
<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 { LocalizeText, WiredFurniType, WiredSelectionVisualizer } from '../../../../api';
|
||||||
import { Button, Text } from '../../../../common';
|
import { Button, Text } from '../../../../common';
|
||||||
import { useWired } from '../../../../hooks';
|
import { useWired } from '../../../../hooks';
|
||||||
@@ -48,6 +48,7 @@ export const WiredActionSendSignalView: FC<{}> = () =>
|
|||||||
const [ antennaIds, setAntennaIds ] = useState<number[]>([]);
|
const [ antennaIds, setAntennaIds ] = useState<number[]>([]);
|
||||||
const [ forwardFurniIds, setForwardFurniIds ] = useState<number[]>([]);
|
const [ forwardFurniIds, setForwardFurniIds ] = useState<number[]>([]);
|
||||||
const [ selectionMode, setSelectionMode ] = useState<SelectionMode>('antenna');
|
const [ selectionMode, setSelectionMode ] = useState<SelectionMode>('antenna');
|
||||||
|
const highlightedIds = useRef<number[]>([]);
|
||||||
|
|
||||||
const { trigger = null, furniIds = [], setFurniIds = null, setIntParams = null, setStringParam = null, setAllowedInteractionTypes = null } = useWired();
|
const { trigger = null, furniIds = [], setFurniIds = null, setIntParams = null, setStringParam = null, setAllowedInteractionTypes = null } = useWired();
|
||||||
|
|
||||||
@@ -84,28 +85,39 @@ export const WiredActionSendSignalView: FC<{}> = () =>
|
|||||||
else setForwardFurniIds(furniIds);
|
else setForwardFurniIds(furniIds);
|
||||||
}, [ furniIds, selectionMode ]);
|
}, [ furniIds, selectionMode ]);
|
||||||
|
|
||||||
const applySelection = useCallback((nextIds: number[]) =>
|
const syncHighlights = useCallback((nextAntennaIds: number[], nextForwardFurniIds: number[], nextSelectionMode: SelectionMode, nextFurniSource: number) =>
|
||||||
{
|
{
|
||||||
if(!setFurniIds) return;
|
if(highlightedIds.current.length)
|
||||||
|
|
||||||
setFurniIds(prev =>
|
|
||||||
{
|
{
|
||||||
if(prev && prev.length) WiredSelectionVisualizer.clearSelectionShaderFromFurni(prev);
|
WiredSelectionVisualizer.clearSelectionShaderFromFurni(highlightedIds.current);
|
||||||
if(nextIds && nextIds.length) WiredSelectionVisualizer.applySelectionShaderToFurni(nextIds);
|
WiredSelectionVisualizer.clearSecondarySelectionShaderFromFurni(highlightedIds.current);
|
||||||
|
}
|
||||||
|
|
||||||
return [ ...nextIds ];
|
const visibleForwardIds = (nextFurniSource === SOURCE_SELECTED) ? nextForwardFurniIds : [];
|
||||||
});
|
const activeIds = (nextSelectionMode === 'antenna') ? nextAntennaIds : visibleForwardIds;
|
||||||
}, [ setFurniIds ]);
|
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) =>
|
const switchSelection = useCallback((mode: SelectionMode) =>
|
||||||
{
|
{
|
||||||
if(mode === selectionMode) return;
|
if(mode === selectionMode) return;
|
||||||
if(mode === 'furni' && furniSource !== SOURCE_SELECTED) return;
|
if(mode === 'furni' && furniSource !== SOURCE_SELECTED) return;
|
||||||
|
|
||||||
const nextIds = (mode === 'antenna') ? antennaIds : forwardFurniIds;
|
|
||||||
applySelection(nextIds);
|
|
||||||
setSelectionMode(mode);
|
setSelectionMode(mode);
|
||||||
}, [ selectionMode, furniSource, antennaIds, forwardFurniIds, applySelection ]);
|
if(setFurniIds) setFurniIds([ ...((mode === 'antenna') ? antennaIds : forwardFurniIds) ]);
|
||||||
|
}, [ selectionMode, furniSource, antennaIds, forwardFurniIds, setFurniIds ]);
|
||||||
|
|
||||||
const onChangeFurniSource = (next: number) =>
|
const onChangeFurniSource = (next: number) =>
|
||||||
{
|
{
|
||||||
@@ -113,7 +125,7 @@ export const WiredActionSendSignalView: FC<{}> = () =>
|
|||||||
|
|
||||||
if(selectionMode === 'furni')
|
if(selectionMode === 'furni')
|
||||||
{
|
{
|
||||||
applySelection(antennaIds);
|
if(setFurniIds) setFurniIds([ ...antennaIds ]);
|
||||||
setSelectionMode('antenna');
|
setSelectionMode('antenna');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -122,12 +134,6 @@ export const WiredActionSendSignalView: FC<{}> = () =>
|
|||||||
|
|
||||||
const save = useCallback(() =>
|
const save = useCallback(() =>
|
||||||
{
|
{
|
||||||
if(selectionMode === 'furni')
|
|
||||||
{
|
|
||||||
setSelectionMode('antenna');
|
|
||||||
applySelection(antennaIds);
|
|
||||||
}
|
|
||||||
|
|
||||||
const antennaSource = (antennaIds && antennaIds.length) ? antennaIds[0] : 0;
|
const antennaSource = (antennaIds && antennaIds.length) ? antennaIds[0] : 0;
|
||||||
|
|
||||||
setIntParams([
|
setIntParams([
|
||||||
@@ -140,7 +146,19 @@ export const WiredActionSendSignalView: FC<{}> = () =>
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
setStringParam(serializeForwardIds(forwardFurniIds));
|
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 selectionLimit = trigger?.maximumItemSelectionCount ?? 0;
|
||||||
const forwardSelectionEnabled = (furniSource === SOURCE_SELECTED);
|
const forwardSelectionEnabled = (furniSource === SOURCE_SELECTED);
|
||||||
|
|||||||
@@ -10,22 +10,26 @@ export const WiredActionSetFurniStateToView: FC<{}> = props =>
|
|||||||
const [ stateFlag, setStateFlag ] = useState(0);
|
const [ stateFlag, setStateFlag ] = useState(0);
|
||||||
const [ directionFlag, setDirectionFlag ] = useState(0);
|
const [ directionFlag, setDirectionFlag ] = useState(0);
|
||||||
const [ positionFlag, setPositionFlag ] = useState(0);
|
const [ positionFlag, setPositionFlag ] = useState(0);
|
||||||
|
const [ altitudeFlag, setAltitudeFlag ] = useState(0);
|
||||||
const { trigger = null, setIntParams = null } = useWired();
|
const { trigger = null, setIntParams = null } = useWired();
|
||||||
const [ furniSource, setFurniSource ] = useState<number>(() =>
|
const [ furniSource, setFurniSource ] = useState<number>(() =>
|
||||||
{
|
{
|
||||||
|
if(trigger?.intData?.length > 4) return trigger.intData[4];
|
||||||
if(trigger?.intData?.length > 3) return trigger.intData[3];
|
if(trigger?.intData?.length > 3) return trigger.intData[3];
|
||||||
return (trigger?.selectedItems?.length ?? 0) > 0 ? 100 : 0;
|
return (trigger?.selectedItems?.length ?? 0) > 0 ? 100 : 0;
|
||||||
});
|
});
|
||||||
|
|
||||||
const save = () => setIntParams([ stateFlag, directionFlag, positionFlag, furniSource ]);
|
const save = () => setIntParams([ stateFlag, directionFlag, positionFlag, altitudeFlag, furniSource ]);
|
||||||
|
|
||||||
useEffect(() =>
|
useEffect(() =>
|
||||||
{
|
{
|
||||||
setStateFlag(trigger.getBoolean(0) ? 1 : 0);
|
setStateFlag(trigger.getBoolean(0) ? 1 : 0);
|
||||||
setDirectionFlag(trigger.getBoolean(1) ? 1 : 0);
|
setDirectionFlag(trigger.getBoolean(1) ? 1 : 0);
|
||||||
setPositionFlag(trigger.getBoolean(2) ? 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);
|
else setFurniSource((trigger.selectedItems?.length ?? 0) > 0 ? 100 : 0);
|
||||||
}, [ trigger ]);
|
}, [ 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) } />
|
<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>
|
<Text>{ LocalizeText('wiredfurni.params.condition.position') }</Text>
|
||||||
</div>
|
</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>
|
</div>
|
||||||
</WiredActionBaseView>
|
</WiredActionBaseView>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,21 +1,39 @@
|
|||||||
import { FC, useEffect, useState } from 'react';
|
import { FC, useEffect, useState } from 'react';
|
||||||
import { WiredFurniType } from '../../../../api';
|
import { LocalizeText, WiredFurniType } from '../../../../api';
|
||||||
|
import { Text } from '../../../../common';
|
||||||
import { useWired } from '../../../../hooks';
|
import { useWired } from '../../../../hooks';
|
||||||
import { WiredActionBaseView } from './WiredActionBaseView';
|
import { WiredActionBaseView } from './WiredActionBaseView';
|
||||||
|
import { WiredActionLayoutCode } from '../../../../api/wired/WiredActionLayoutCode';
|
||||||
import { WiredSourcesSelector } from '../WiredSourcesSelector';
|
import { WiredSourcesSelector } from '../WiredSourcesSelector';
|
||||||
|
|
||||||
export const WiredActionTeleportView: FC<{}> = props =>
|
export const WiredActionTeleportView: FC<{}> = props =>
|
||||||
{
|
{
|
||||||
const { trigger = null, setIntParams = null } = useWired();
|
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>(() =>
|
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];
|
if(trigger?.intData?.length >= 1) return trigger.intData[0];
|
||||||
return (trigger?.selectedItems?.length ?? 0) > 0 ? 100 : 0;
|
return (trigger?.selectedItems?.length ?? 0) > 0 ? 100 : 0;
|
||||||
});
|
});
|
||||||
|
|
||||||
const [ userSource, setUserSource ] = useState<number>(() =>
|
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];
|
if(trigger?.intData?.length >= 2) return trigger.intData[1];
|
||||||
return 0;
|
return 0;
|
||||||
});
|
});
|
||||||
@@ -24,16 +42,37 @@ export const WiredActionTeleportView: FC<{}> = props =>
|
|||||||
{
|
{
|
||||||
if(!trigger) return;
|
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]);
|
if(trigger.intData.length >= 1) setFurniSource(trigger.intData[0]);
|
||||||
else setFurniSource((trigger.selectedItems?.length ?? 0) > 0 ? 100 : 0);
|
else setFurniSource((trigger.selectedItems?.length ?? 0) > 0 ? 100 : 0);
|
||||||
|
|
||||||
if(trigger.intData.length >= 2) setUserSource(trigger.intData[1]);
|
if(trigger.intData.length >= 2) setUserSource(trigger.intData[1]);
|
||||||
else setUserSource(0);
|
else setUserSource(0);
|
||||||
}, [ trigger ]);
|
}, [ isTeleportEffect, isUserToFurniEffect, trigger ]);
|
||||||
|
|
||||||
const onChangeFurniSource = (next: number) => setFurniSource(next);
|
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;
|
const requiresFurni = WiredFurniType.STUFF_SELECTION_OPTION_BY_ID_BY_TYPE_OR_FROM_CONTEXT;
|
||||||
|
|
||||||
@@ -50,6 +89,21 @@ export const WiredActionTeleportView: FC<{}> = props =>
|
|||||||
userSource={ userSource }
|
userSource={ userSource }
|
||||||
onChangeFurni={ onChangeFurniSource }
|
onChangeFurni={ onChangeFurniSource }
|
||||||
onChangeUsers={ setUserSource } />
|
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 { FC, useEffect, useState } from 'react';
|
||||||
import { WiredFurniType } from '../../../../api';
|
import { LocalizeText, WiredFurniType } from '../../../../api';
|
||||||
|
import { Text } from '../../../../common';
|
||||||
import { WiredActionBaseView } from './WiredActionBaseView';
|
import { WiredActionBaseView } from './WiredActionBaseView';
|
||||||
import { WiredSourcesSelector } from '../WiredSourcesSelector';
|
import { WiredSourcesSelector } from '../WiredSourcesSelector';
|
||||||
import { useWired } from '../../../../hooks';
|
import { useWired } from '../../../../hooks';
|
||||||
@@ -7,8 +8,14 @@ import { useWired } from '../../../../hooks';
|
|||||||
export const WiredActionToggleFurniStateView: FC<{}> = props =>
|
export const WiredActionToggleFurniStateView: FC<{}> = props =>
|
||||||
{
|
{
|
||||||
const { trigger = null, setIntParams = null } = useWired();
|
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>(() =>
|
const [ furniSource, setFurniSource ] = useState<number>(() =>
|
||||||
{
|
{
|
||||||
|
if(trigger?.intData?.length > 1) return trigger.intData[1];
|
||||||
if(trigger?.intData?.length >= 1) return trigger.intData[0];
|
if(trigger?.intData?.length >= 1) return trigger.intData[0];
|
||||||
return (trigger?.selectedItems?.length ?? 0) > 0 ? 100 : 0;
|
return (trigger?.selectedItems?.length ?? 0) > 0 ? 100 : 0;
|
||||||
});
|
});
|
||||||
@@ -17,13 +24,26 @@ export const WiredActionToggleFurniStateView: FC<{}> = props =>
|
|||||||
{
|
{
|
||||||
if(!trigger) return;
|
if(!trigger) return;
|
||||||
|
|
||||||
if(trigger.intData.length >= 1) setFurniSource(trigger.intData[0]);
|
if(trigger.intData.length > 1)
|
||||||
else setFurniSource((trigger.selectedItems?.length ?? 0) > 0 ? 100 : 0);
|
{
|
||||||
|
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 ]);
|
}, [ trigger ]);
|
||||||
|
|
||||||
const onChangeFurniSource = (next: number) => setFurniSource(next);
|
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;
|
const requiresFurni = WiredFurniType.STUFF_SELECTION_OPTION_BY_ID_BY_TYPE_OR_FROM_CONTEXT;
|
||||||
|
|
||||||
@@ -32,6 +52,16 @@ export const WiredActionToggleFurniStateView: FC<{}> = props =>
|
|||||||
hasSpecialInput={ true }
|
hasSpecialInput={ true }
|
||||||
requiresFurni={ requiresFurni }
|
requiresFurni={ requiresFurni }
|
||||||
save={ save }
|
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>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,100 @@
|
|||||||
|
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 { WiredSourcesSelector } from '../WiredSourcesSelector';
|
||||||
|
import { WiredConditionBaseView } from './WiredConditionBaseView';
|
||||||
|
|
||||||
|
const toggleDirection = (mask: number, direction: number, checked: boolean) =>
|
||||||
|
{
|
||||||
|
if(checked) return (mask | (1 << direction));
|
||||||
|
|
||||||
|
return (mask & ~(1 << direction));
|
||||||
|
};
|
||||||
|
|
||||||
|
export const WiredConditionActorDirView: FC<{}> = () =>
|
||||||
|
{
|
||||||
|
const { trigger = null, setIntParams = null } = useWired();
|
||||||
|
const [ directionMask, setDirectionMask ] = useState(0);
|
||||||
|
const [ userSource, setUserSource ] = useState(0);
|
||||||
|
const [ quantifier, setQuantifier ] = useState(0);
|
||||||
|
const [ showAdvanced, setShowAdvanced ] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() =>
|
||||||
|
{
|
||||||
|
if(!trigger) return;
|
||||||
|
|
||||||
|
const nextDirectionMask = trigger.intData.length > 0 ? trigger.intData[0] : 0;
|
||||||
|
const nextUserSource = trigger.intData.length > 1 ? trigger.intData[1] : 0;
|
||||||
|
const nextQuantifier = trigger.intData.length > 2 ? trigger.intData[2] : 0;
|
||||||
|
|
||||||
|
setDirectionMask(nextDirectionMask);
|
||||||
|
setUserSource(nextUserSource);
|
||||||
|
setQuantifier((nextQuantifier === 1) ? 1 : 0);
|
||||||
|
setShowAdvanced(nextUserSource !== 0 || nextQuantifier !== 0);
|
||||||
|
}, [ trigger ]);
|
||||||
|
|
||||||
|
const save = () =>
|
||||||
|
{
|
||||||
|
setIntParams([
|
||||||
|
directionMask,
|
||||||
|
userSource,
|
||||||
|
quantifier
|
||||||
|
]);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<WiredConditionBaseView
|
||||||
|
hasSpecialInput={ true }
|
||||||
|
requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE }
|
||||||
|
save={ save }
|
||||||
|
footer={
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
<button className="btn btn-link p-0 align-self-start" type="button" onClick={ () => setShowAdvanced(value => !value) }>
|
||||||
|
{ LocalizeText(showAdvanced ? 'wiredfurni.params.sources.collapse' : 'wiredfurni.params.sources.expand') }
|
||||||
|
</button>
|
||||||
|
{ showAdvanced &&
|
||||||
|
<>
|
||||||
|
<div className="flex flex-col gap-1">
|
||||||
|
<Text bold>{ LocalizeText('wiredfurni.params.quantifier_selection') }</Text>
|
||||||
|
{ [ 0, 1 ].map(value =>
|
||||||
|
{
|
||||||
|
return (
|
||||||
|
<label key={ value } className="flex items-center gap-1">
|
||||||
|
<input checked={ (quantifier === value) } className="form-check-input" name="actorDirQuantifier" type="radio" onChange={ () => setQuantifier(value) } />
|
||||||
|
<Text>{ LocalizeText(`wiredfurni.params.quantifier.users.${ value }`) }</Text>
|
||||||
|
</label>
|
||||||
|
);
|
||||||
|
}) }
|
||||||
|
</div>
|
||||||
|
<WiredSourcesSelector showUsers={ true } userSource={ userSource } onChangeUsers={ setUserSource } />
|
||||||
|
</> }
|
||||||
|
</div>
|
||||||
|
}>
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
<Text bold>{ LocalizeText('wiredfurni.params.direction_selection') }</Text>
|
||||||
|
<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={ `actor-dir-empty-${ rowIndex }-${ columnIndex }` } />;
|
||||||
|
}
|
||||||
|
|
||||||
|
const checked = ((directionMask & (1 << direction)) !== 0);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<label key={ `actor-dir-${ direction }` } className="flex items-center justify-center gap-[2px] cursor-pointer">
|
||||||
|
<input checked={ checked } className="form-check-input" type="checkbox" onChange={ event => setDirectionMask(toggleDirection(directionMask, direction, event.target.checked)) } />
|
||||||
|
<span className="inline-flex items-center justify-center">
|
||||||
|
<WiredDirectionIcon direction={ direction } selected={ checked } />
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
);
|
||||||
|
})) }
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</WiredConditionBaseView>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -2,14 +2,19 @@ import { FC, useEffect, useState } from 'react';
|
|||||||
import { LocalizeText, WiredFurniType } from '../../../../api';
|
import { LocalizeText, WiredFurniType } from '../../../../api';
|
||||||
import { Text } from '../../../../common';
|
import { Text } from '../../../../common';
|
||||||
import { useWired } from '../../../../hooks';
|
import { useWired } from '../../../../hooks';
|
||||||
|
import { WiredHandItemField } from '../WiredHandItemField';
|
||||||
import { WiredConditionBaseView } from './WiredConditionBaseView';
|
import { WiredConditionBaseView } from './WiredConditionBaseView';
|
||||||
import { WiredSourcesSelector } from '../WiredSourcesSelector';
|
import { WiredSourcesSelector } from '../WiredSourcesSelector';
|
||||||
|
|
||||||
const ALLOWED_HAND_ITEM_IDS: number[] = [ 2, 5, 7, 8, 9, 10, 27 ];
|
interface WiredConditionActorHasHandItemViewProps
|
||||||
|
{
|
||||||
|
negative?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
export const WiredConditionActorHasHandItemView: FC<{}> = props =>
|
export const WiredConditionActorHasHandItemView: FC<WiredConditionActorHasHandItemViewProps> = ({ negative = false }) =>
|
||||||
{
|
{
|
||||||
const [ handItemId, setHandItemId ] = useState(-1);
|
const [ handItemId, setHandItemId ] = useState(-1);
|
||||||
|
const [ quantifier, setQuantifier ] = useState(0);
|
||||||
const { trigger = null, setIntParams = null } = useWired();
|
const { trigger = null, setIntParams = null } = useWired();
|
||||||
const [ userSource, setUserSource ] = useState<number>(() =>
|
const [ userSource, setUserSource ] = useState<number>(() =>
|
||||||
{
|
{
|
||||||
@@ -17,12 +22,13 @@ export const WiredConditionActorHasHandItemView: FC<{}> = props =>
|
|||||||
return 0;
|
return 0;
|
||||||
});
|
});
|
||||||
|
|
||||||
const save = () => setIntParams([ handItemId, userSource ]);
|
const save = () => setIntParams([ handItemId, userSource, quantifier ]);
|
||||||
|
|
||||||
useEffect(() =>
|
useEffect(() =>
|
||||||
{
|
{
|
||||||
setHandItemId((trigger.intData.length > 0) ? trigger.intData[0] : 0);
|
setHandItemId((trigger.intData.length > 0) ? trigger.intData[0] : 0);
|
||||||
setUserSource((trigger.intData.length > 1) ? trigger.intData[1] : 0);
|
setUserSource((trigger.intData.length > 1) ? trigger.intData[1] : 0);
|
||||||
|
setQuantifier((trigger.intData.length > 2 && trigger.intData[2] === 1) ? 1 : 0);
|
||||||
}, [ trigger ]);
|
}, [ trigger ]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -32,14 +38,15 @@ export const WiredConditionActorHasHandItemView: FC<{}> = props =>
|
|||||||
save={ save }
|
save={ save }
|
||||||
footer={ <WiredSourcesSelector showUsers={ true } userSource={ userSource } onChangeUsers={ setUserSource } /> }>
|
footer={ <WiredSourcesSelector showUsers={ true } userSource={ userSource } onChangeUsers={ setUserSource } /> }>
|
||||||
<div className="flex flex-col gap-1">
|
<div className="flex flex-col gap-1">
|
||||||
<Text bold>{ LocalizeText('wiredfurni.params.handitem') }</Text>
|
<Text bold>{ LocalizeText('wiredfurni.params.quantifier_selection') }</Text>
|
||||||
<select className="form-select form-select-sm" value={ handItemId } onChange={ event => setHandItemId(parseInt(event.target.value)) }>
|
{ [ 0, 1 ].map(value => (
|
||||||
{ ALLOWED_HAND_ITEM_IDS.map(value =>
|
<label key={ value } className="flex items-center gap-1">
|
||||||
{
|
<input checked={ (quantifier === value) } className="form-check-input" name="handItemQuantifier" type="radio" onChange={ () => setQuantifier(value) } />
|
||||||
return <option key={ value } value={ value }>{ LocalizeText(`handitem${ value }`) }</option>;
|
<Text>{ LocalizeText(`wiredfurni.params.quantifier.users${ negative ? '.neg' : '' }.${ value }`) }</Text>
|
||||||
}) }
|
</label>
|
||||||
</select>
|
)) }
|
||||||
</div>
|
</div>
|
||||||
|
<WiredHandItemField handItemId={ handItemId } onChange={ setHandItemId } showCopyButton={ true } />
|
||||||
</WiredConditionBaseView>
|
</WiredConditionBaseView>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,32 +1,96 @@
|
|||||||
import { FC, useEffect, useState } from 'react';
|
import { CatalogGroupsComposer, GuildMembershipsMessageEvent, HabboGroupEntryData } from '@nitrots/nitro-renderer';
|
||||||
import { WiredFurniType } from '../../../../api';
|
import { FC, useEffect, useMemo, useState } from 'react';
|
||||||
import { useWired } from '../../../../hooks';
|
import { LocalizeText, SendMessageComposer, WiredFurniType } from '../../../../api';
|
||||||
import { WiredConditionBaseView } from './WiredConditionBaseView';
|
import { Text } from '../../../../common';
|
||||||
|
import { useMessageEvent, useWired } from '../../../../hooks';
|
||||||
import { WiredSourcesSelector } from '../WiredSourcesSelector';
|
import { WiredSourcesSelector } from '../WiredSourcesSelector';
|
||||||
|
import { WiredConditionBaseView } from './WiredConditionBaseView';
|
||||||
|
|
||||||
export const WiredConditionActorIsGroupMemberView: FC<{}> = props =>
|
const GROUP_CURRENT_ROOM = 0;
|
||||||
|
const GROUP_SELECTED = 1;
|
||||||
|
|
||||||
|
interface WiredConditionActorIsGroupMemberViewProps
|
||||||
{
|
{
|
||||||
|
negative?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const WiredConditionActorIsGroupMemberView: FC<WiredConditionActorIsGroupMemberViewProps> = ({ negative = false }) =>
|
||||||
|
{
|
||||||
|
const [ groups, setGroups ] = useState<HabboGroupEntryData[]>([]);
|
||||||
|
const [ userSource, setUserSource ] = useState(0);
|
||||||
|
const [ groupType, setGroupType ] = useState(GROUP_CURRENT_ROOM);
|
||||||
|
const [ selectedGroupId, setSelectedGroupId ] = useState(0);
|
||||||
|
const [ quantifier, setQuantifier ] = useState(0);
|
||||||
const { trigger = null, setIntParams = null } = useWired();
|
const { trigger = null, setIntParams = null } = useWired();
|
||||||
const [ userSource, setUserSource ] = useState<number>(() =>
|
|
||||||
|
useMessageEvent<GuildMembershipsMessageEvent>(GuildMembershipsMessageEvent, event =>
|
||||||
{
|
{
|
||||||
if(trigger?.intData?.length >= 1) return trigger.intData[0];
|
const parser = event.getParser();
|
||||||
return 0;
|
|
||||||
|
setGroups(parser.groups || []);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
useEffect(() =>
|
||||||
|
{
|
||||||
|
SendMessageComposer(new CatalogGroupsComposer());
|
||||||
|
}, []);
|
||||||
|
|
||||||
useEffect(() =>
|
useEffect(() =>
|
||||||
{
|
{
|
||||||
if(!trigger) return;
|
if(!trigger) return;
|
||||||
if(trigger.intData.length >= 1) setUserSource(trigger.intData[0]);
|
|
||||||
else setUserSource(0);
|
const params = trigger.intData;
|
||||||
|
|
||||||
|
setUserSource(params.length > 0 ? params[0] : 0);
|
||||||
|
setGroupType(params.length > 1 ? params[1] : GROUP_CURRENT_ROOM);
|
||||||
|
setSelectedGroupId(params.length > 2 ? params[2] : 0);
|
||||||
|
setQuantifier((params.length > 3 && params[3] === 1) ? 1 : 0);
|
||||||
}, [ trigger ]);
|
}, [ trigger ]);
|
||||||
|
|
||||||
const save = () => setIntParams([ userSource ]);
|
useEffect(() =>
|
||||||
|
{
|
||||||
|
if((groupType !== GROUP_SELECTED) || selectedGroupId || !groups.length) return;
|
||||||
|
|
||||||
|
setSelectedGroupId(groups[0].groupId);
|
||||||
|
}, [ groupType, selectedGroupId, groups ]);
|
||||||
|
|
||||||
|
const selectedGroupOptions = useMemo(() => groups.map(group => ({ value: group.groupId, label: group.groupName })), [ groups ]);
|
||||||
|
|
||||||
|
const save = () => setIntParams([ userSource, groupType, selectedGroupId, quantifier ]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<WiredConditionBaseView
|
<WiredConditionBaseView
|
||||||
hasSpecialInput={ true }
|
hasSpecialInput={ true }
|
||||||
requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE }
|
requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE }
|
||||||
save={ save }
|
save={ save }
|
||||||
footer={ <WiredSourcesSelector showUsers={ true } userSource={ userSource } onChangeUsers={ setUserSource } /> } />
|
footer={ <WiredSourcesSelector showUsers={ true } userSource={ userSource } onChangeUsers={ setUserSource } /> }>
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
<div className="flex flex-col gap-1">
|
||||||
|
<Text bold>{ LocalizeText('wiredfurni.params.quantifier_selection') }</Text>
|
||||||
|
{ [ 0, 1 ].map(value => (
|
||||||
|
<label key={ value } className="flex items-center gap-1">
|
||||||
|
<input checked={ (quantifier === value) } className="form-check-input" name="conditionGroupQuantifier" type="radio" onChange={ () => setQuantifier(value) } />
|
||||||
|
<Text>{ LocalizeText(`wiredfurni.params.quantifier.users${ negative ? '.neg' : '' }.${ value }`) }</Text>
|
||||||
|
</label>
|
||||||
|
)) }
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex flex-col gap-1">
|
||||||
|
<Text bold>{ LocalizeText('wiredfurni.params.groupselection') }</Text>
|
||||||
|
{ [ GROUP_CURRENT_ROOM, GROUP_SELECTED ].map(value => (
|
||||||
|
<label key={ value } className="flex items-center gap-1">
|
||||||
|
<input checked={ (groupType === value) } className="form-check-input" name="conditionGroupType" type="radio" onChange={ () => setGroupType(value) } />
|
||||||
|
<Text>{ LocalizeText(`wiredfurni.params.grouptype.${ value }`) }</Text>
|
||||||
|
</label>
|
||||||
|
)) }
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{ (groupType === GROUP_SELECTED) &&
|
||||||
|
<select className="form-select form-select-sm" value={ selectedGroupId } onChange={ event => setSelectedGroupId(parseInt(event.target.value)) }>
|
||||||
|
{ !selectedGroupOptions.length && <option value={ 0 }>-</option> }
|
||||||
|
{ selectedGroupOptions.map(group => <option key={ group.value } value={ group.value }>{ group.label }</option>) }
|
||||||
|
</select> }
|
||||||
|
</div>
|
||||||
|
</WiredConditionBaseView>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,10 +1,16 @@
|
|||||||
import { FC, useEffect, useState } from 'react';
|
import { FC, useEffect, useState } from 'react';
|
||||||
import { WiredFurniType } from '../../../../api';
|
import { LocalizeText, WiredFurniType } from '../../../../api';
|
||||||
|
import { Text } from '../../../../common';
|
||||||
import { useWired } from '../../../../hooks';
|
import { useWired } from '../../../../hooks';
|
||||||
import { WiredConditionBaseView } from './WiredConditionBaseView';
|
import { WiredConditionBaseView } from './WiredConditionBaseView';
|
||||||
import { WiredSourcesSelector } from '../WiredSourcesSelector';
|
import { WiredSourcesSelector } from '../WiredSourcesSelector';
|
||||||
|
|
||||||
export const WiredConditionActorIsOnFurniView: FC<{}> = props =>
|
interface WiredConditionActorIsOnFurniViewProps
|
||||||
|
{
|
||||||
|
negative?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const WiredConditionActorIsOnFurniView: FC<WiredConditionActorIsOnFurniViewProps> = ({ negative = false }) =>
|
||||||
{
|
{
|
||||||
const { trigger = null, setIntParams = null } = useWired();
|
const { trigger = null, setIntParams = null } = useWired();
|
||||||
const [ furniSource, setFurniSource ] = useState<number>(() =>
|
const [ furniSource, setFurniSource ] = useState<number>(() =>
|
||||||
@@ -17,6 +23,11 @@ export const WiredConditionActorIsOnFurniView: FC<{}> = props =>
|
|||||||
if(trigger?.intData?.length > 1) return trigger.intData[1];
|
if(trigger?.intData?.length > 1) return trigger.intData[1];
|
||||||
return 0;
|
return 0;
|
||||||
});
|
});
|
||||||
|
const [ quantifier, setQuantifier ] = useState<number>(() =>
|
||||||
|
{
|
||||||
|
if(trigger?.intData?.length > 2) return trigger.intData[2];
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
|
||||||
useEffect(() =>
|
useEffect(() =>
|
||||||
{
|
{
|
||||||
@@ -27,11 +38,14 @@ export const WiredConditionActorIsOnFurniView: FC<{}> = props =>
|
|||||||
|
|
||||||
if(trigger.intData.length > 1) setUserSource(trigger.intData[1]);
|
if(trigger.intData.length > 1) setUserSource(trigger.intData[1]);
|
||||||
else setUserSource(0);
|
else setUserSource(0);
|
||||||
|
|
||||||
|
if(trigger.intData.length > 2) setQuantifier(trigger.intData[2] === 1 ? 1 : 0);
|
||||||
|
else setQuantifier(0);
|
||||||
}, [ trigger ]);
|
}, [ trigger ]);
|
||||||
|
|
||||||
const onChangeFurniSource = (next: number) => setFurniSource(next);
|
const onChangeFurniSource = (next: number) => setFurniSource(next);
|
||||||
|
|
||||||
const save = () => setIntParams([ furniSource, userSource ]);
|
const save = () => setIntParams([ furniSource, userSource, quantifier ]);
|
||||||
|
|
||||||
const requiresFurni = WiredFurniType.STUFF_SELECTION_OPTION_BY_ID;
|
const requiresFurni = WiredFurniType.STUFF_SELECTION_OPTION_BY_ID;
|
||||||
|
|
||||||
@@ -41,13 +55,24 @@ export const WiredConditionActorIsOnFurniView: FC<{}> = props =>
|
|||||||
requiresFurni={ requiresFurni }
|
requiresFurni={ requiresFurni }
|
||||||
save={ save }
|
save={ save }
|
||||||
footer={ (
|
footer={ (
|
||||||
<WiredSourcesSelector
|
<div className="flex flex-col gap-2">
|
||||||
showFurni={ true }
|
<div className="flex flex-col gap-1">
|
||||||
showUsers={ true }
|
<Text bold>{ LocalizeText('wiredfurni.params.quantifier_selection') }</Text>
|
||||||
furniSource={ furniSource }
|
{ [ 0, 1 ].map(value => (
|
||||||
userSource={ userSource }
|
<label key={ value } className="flex items-center gap-1">
|
||||||
onChangeFurni={ onChangeFurniSource }
|
<input checked={ (quantifier === value) } className="form-check-input" name="triggerOnFurniQuantifier" type="radio" onChange={ () => setQuantifier(value) } />
|
||||||
onChangeUsers={ setUserSource } />
|
<Text>{ LocalizeText(`wiredfurni.params.quantifier.users${ negative ? '.neg' : '' }.${ value }`) }</Text>
|
||||||
|
</label>
|
||||||
|
)) }
|
||||||
|
</div>
|
||||||
|
<WiredSourcesSelector
|
||||||
|
showFurni={ true }
|
||||||
|
showUsers={ true }
|
||||||
|
furniSource={ furniSource }
|
||||||
|
userSource={ userSource }
|
||||||
|
onChangeFurni={ onChangeFurniSource }
|
||||||
|
onChangeUsers={ setUserSource } />
|
||||||
|
</div>
|
||||||
) } />
|
) } />
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -7,9 +7,15 @@ import { WiredSourcesSelector } from '../WiredSourcesSelector';
|
|||||||
|
|
||||||
const teamIds: number[] = [ 1, 2, 3, 4 ];
|
const teamIds: number[] = [ 1, 2, 3, 4 ];
|
||||||
|
|
||||||
export const WiredConditionActorIsTeamMemberView: FC<{}> = props =>
|
interface WiredConditionActorIsTeamMemberViewProps
|
||||||
|
{
|
||||||
|
negative?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const WiredConditionActorIsTeamMemberView: FC<WiredConditionActorIsTeamMemberViewProps> = ({ negative = false }) =>
|
||||||
{
|
{
|
||||||
const [ selectedTeam, setSelectedTeam ] = useState(-1);
|
const [ selectedTeam, setSelectedTeam ] = useState(-1);
|
||||||
|
const [ quantifier, setQuantifier ] = useState(1);
|
||||||
const { trigger = null, setIntParams = null } = useWired();
|
const { trigger = null, setIntParams = null } = useWired();
|
||||||
const [ userSource, setUserSource ] = useState<number>(() =>
|
const [ userSource, setUserSource ] = useState<number>(() =>
|
||||||
{
|
{
|
||||||
@@ -17,12 +23,13 @@ export const WiredConditionActorIsTeamMemberView: FC<{}> = props =>
|
|||||||
return 0;
|
return 0;
|
||||||
});
|
});
|
||||||
|
|
||||||
const save = () => setIntParams([ selectedTeam, userSource ]);
|
const save = () => setIntParams([ selectedTeam, userSource, quantifier ]);
|
||||||
|
|
||||||
useEffect(() =>
|
useEffect(() =>
|
||||||
{
|
{
|
||||||
setSelectedTeam((trigger.intData.length > 0) ? trigger.intData[0] : 0);
|
setSelectedTeam((trigger.intData.length > 0) ? trigger.intData[0] : 0);
|
||||||
setUserSource((trigger.intData.length > 1) ? trigger.intData[1] : 0);
|
setUserSource((trigger.intData.length > 1) ? trigger.intData[1] : 0);
|
||||||
|
setQuantifier((trigger.intData.length > 2) ? (trigger.intData[2] === 1 ? 1 : 0) : 1);
|
||||||
}, [ trigger ]);
|
}, [ trigger ]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -31,6 +38,15 @@ export const WiredConditionActorIsTeamMemberView: FC<{}> = props =>
|
|||||||
requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE }
|
requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE }
|
||||||
save={ save }
|
save={ save }
|
||||||
footer={ <WiredSourcesSelector showUsers={ true } userSource={ userSource } onChangeUsers={ setUserSource } /> }>
|
footer={ <WiredSourcesSelector showUsers={ true } userSource={ userSource } onChangeUsers={ setUserSource } /> }>
|
||||||
|
<div className="flex flex-col gap-1">
|
||||||
|
<Text bold>{ LocalizeText('wiredfurni.params.quantifier_selection') }</Text>
|
||||||
|
{ [ 0, 1 ].map(value => (
|
||||||
|
<label key={ value } className="flex items-center gap-1">
|
||||||
|
<input checked={ (quantifier === value) } className="form-check-input" name="teamMemberQuantifier" type="radio" onChange={ () => setQuantifier(value) } />
|
||||||
|
<Text>{ LocalizeText(`wiredfurni.params.quantifier.users${ negative ? '.neg' : '' }.${ value }`) }</Text>
|
||||||
|
</label>
|
||||||
|
)) }
|
||||||
|
</div>
|
||||||
<div className="flex flex-col gap-1">
|
<div className="flex flex-col gap-1">
|
||||||
<Text bold>{ LocalizeText('wiredfurni.params.team') }</Text>
|
<Text bold>{ LocalizeText('wiredfurni.params.team') }</Text>
|
||||||
{ teamIds.map(value =>
|
{ teamIds.map(value =>
|
||||||
|
|||||||
@@ -6,9 +6,15 @@ import { NitroInput } from '../../../../layout';
|
|||||||
import { WiredConditionBaseView } from './WiredConditionBaseView';
|
import { WiredConditionBaseView } from './WiredConditionBaseView';
|
||||||
import { WiredSourcesSelector } from '../WiredSourcesSelector';
|
import { WiredSourcesSelector } from '../WiredSourcesSelector';
|
||||||
|
|
||||||
export const WiredConditionActorIsWearingBadgeView: FC<{}> = props =>
|
interface WiredConditionActorIsWearingBadgeViewProps
|
||||||
|
{
|
||||||
|
negative?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const WiredConditionActorIsWearingBadgeView: FC<WiredConditionActorIsWearingBadgeViewProps> = ({ negative = false }) =>
|
||||||
{
|
{
|
||||||
const [ badge, setBadge ] = useState('');
|
const [ badge, setBadge ] = useState('');
|
||||||
|
const [ quantifier, setQuantifier ] = useState(1);
|
||||||
const { trigger = null, setStringParam = null, setIntParams = null } = useWired();
|
const { trigger = null, setStringParam = null, setIntParams = null } = useWired();
|
||||||
const [ userSource, setUserSource ] = useState<number>(() =>
|
const [ userSource, setUserSource ] = useState<number>(() =>
|
||||||
{
|
{
|
||||||
@@ -19,7 +25,7 @@ export const WiredConditionActorIsWearingBadgeView: FC<{}> = props =>
|
|||||||
const save = () =>
|
const save = () =>
|
||||||
{
|
{
|
||||||
setStringParam(badge);
|
setStringParam(badge);
|
||||||
setIntParams([ userSource ]);
|
setIntParams([ userSource, quantifier ]);
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() =>
|
useEffect(() =>
|
||||||
@@ -27,6 +33,7 @@ export const WiredConditionActorIsWearingBadgeView: FC<{}> = props =>
|
|||||||
setBadge(trigger.stringData);
|
setBadge(trigger.stringData);
|
||||||
if(trigger.intData.length >= 1) setUserSource(trigger.intData[0]);
|
if(trigger.intData.length >= 1) setUserSource(trigger.intData[0]);
|
||||||
else setUserSource(0);
|
else setUserSource(0);
|
||||||
|
setQuantifier((trigger.intData.length >= 2) ? (trigger.intData[1] === 1 ? 1 : 0) : 1);
|
||||||
}, [ trigger ]);
|
}, [ trigger ]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -35,6 +42,15 @@ export const WiredConditionActorIsWearingBadgeView: FC<{}> = props =>
|
|||||||
requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE }
|
requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE }
|
||||||
save={ save }
|
save={ save }
|
||||||
footer={ <WiredSourcesSelector showUsers={ true } userSource={ userSource } onChangeUsers={ setUserSource } /> }>
|
footer={ <WiredSourcesSelector showUsers={ true } userSource={ userSource } onChangeUsers={ setUserSource } /> }>
|
||||||
|
<div className="flex flex-col gap-1">
|
||||||
|
<Text bold>{ LocalizeText('wiredfurni.params.quantifier_selection') }</Text>
|
||||||
|
{ [ 0, 1 ].map(value => (
|
||||||
|
<label key={ value } className="flex items-center gap-1">
|
||||||
|
<input checked={ (quantifier === value) } className="form-check-input" name="badgeQuantifier" type="radio" onChange={ () => setQuantifier(value) } />
|
||||||
|
<Text>{ LocalizeText(`wiredfurni.params.quantifier.users${ negative ? '.neg' : '' }.${ value }`) }</Text>
|
||||||
|
</label>
|
||||||
|
)) }
|
||||||
|
</div>
|
||||||
<div className="flex flex-col gap-1">
|
<div className="flex flex-col gap-1">
|
||||||
<Text bold>{ LocalizeText('wiredfurni.params.badgecode') }</Text>
|
<Text bold>{ LocalizeText('wiredfurni.params.badgecode') }</Text>
|
||||||
<NitroInput type="text" value={ badge } onChange={ event => setBadge(event.target.value) } />
|
<NitroInput type="text" value={ badge } onChange={ event => setBadge(event.target.value) } />
|
||||||
|
|||||||
@@ -6,9 +6,15 @@ import { NitroInput } from '../../../../layout';
|
|||||||
import { WiredConditionBaseView } from './WiredConditionBaseView';
|
import { WiredConditionBaseView } from './WiredConditionBaseView';
|
||||||
import { WiredSourcesSelector } from '../WiredSourcesSelector';
|
import { WiredSourcesSelector } from '../WiredSourcesSelector';
|
||||||
|
|
||||||
export const WiredConditionActorIsWearingEffectView: FC<{}> = props =>
|
interface WiredConditionActorIsWearingEffectViewProps
|
||||||
|
{
|
||||||
|
negative?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const WiredConditionActorIsWearingEffectView: FC<WiredConditionActorIsWearingEffectViewProps> = ({ negative = false }) =>
|
||||||
{
|
{
|
||||||
const [ effect, setEffect ] = useState(-1);
|
const [ effect, setEffect ] = useState(-1);
|
||||||
|
const [ quantifier, setQuantifier ] = useState(1);
|
||||||
const { trigger = null, setIntParams = null } = useWired();
|
const { trigger = null, setIntParams = null } = useWired();
|
||||||
const [ userSource, setUserSource ] = useState<number>(() =>
|
const [ userSource, setUserSource ] = useState<number>(() =>
|
||||||
{
|
{
|
||||||
@@ -16,13 +22,14 @@ export const WiredConditionActorIsWearingEffectView: FC<{}> = props =>
|
|||||||
return 0;
|
return 0;
|
||||||
});
|
});
|
||||||
|
|
||||||
const save = () => setIntParams([ effect, userSource ]);
|
const save = () => setIntParams([ effect, userSource, quantifier ]);
|
||||||
|
|
||||||
useEffect(() =>
|
useEffect(() =>
|
||||||
{
|
{
|
||||||
setEffect(trigger?.intData[0] ?? 0);
|
setEffect(trigger?.intData[0] ?? 0);
|
||||||
if(trigger?.intData?.length > 1) setUserSource(trigger.intData[1]);
|
if(trigger?.intData?.length > 1) setUserSource(trigger.intData[1]);
|
||||||
else setUserSource(0);
|
else setUserSource(0);
|
||||||
|
setQuantifier((trigger?.intData?.length > 2) ? (trigger.intData[2] === 1 ? 1 : 0) : 1);
|
||||||
}, [ trigger ]);
|
}, [ trigger ]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -31,6 +38,15 @@ export const WiredConditionActorIsWearingEffectView: FC<{}> = props =>
|
|||||||
requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE }
|
requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE }
|
||||||
save={ save }
|
save={ save }
|
||||||
footer={ <WiredSourcesSelector showUsers={ true } userSource={ userSource } onChangeUsers={ setUserSource } /> }>
|
footer={ <WiredSourcesSelector showUsers={ true } userSource={ userSource } onChangeUsers={ setUserSource } /> }>
|
||||||
|
<div className="flex flex-col gap-1">
|
||||||
|
<Text bold>{ LocalizeText('wiredfurni.params.quantifier_selection') }</Text>
|
||||||
|
{ [ 0, 1 ].map(value => (
|
||||||
|
<label key={ value } className="flex items-center gap-1">
|
||||||
|
<input checked={ (quantifier === value) } className="form-check-input" name="effectQuantifier" type="radio" onChange={ () => setQuantifier(value) } />
|
||||||
|
<Text>{ LocalizeText(`wiredfurni.params.quantifier.users${ negative ? '.neg' : '' }.${ value }`) }</Text>
|
||||||
|
</label>
|
||||||
|
)) }
|
||||||
|
</div>
|
||||||
<div className="flex flex-col gap-1">
|
<div className="flex flex-col gap-1">
|
||||||
<Text bold>{ LocalizeText('wiredfurni.tooltip.effectid') }</Text>
|
<Text bold>{ LocalizeText('wiredfurni.tooltip.effectid') }</Text>
|
||||||
<NitroInput type="number" value={ effect } onChange={ event => setEffect(parseInt(event.target.value)) } />
|
<NitroInput type="number" value={ effect } onChange={ event => setEffect(parseInt(event.target.value)) } />
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ export const WiredConditionCounterTimeMatchesView: FC<{}> = () =>
|
|||||||
});
|
});
|
||||||
const [ minutes, setMinutes ] = useState(0);
|
const [ minutes, setMinutes ] = useState(0);
|
||||||
const [ halfSeconds, setHalfSeconds ] = useState(0);
|
const [ halfSeconds, setHalfSeconds ] = useState(0);
|
||||||
|
const [ quantifier, setQuantifier ] = useState(0);
|
||||||
|
|
||||||
const secondsLabel = useMemo(() => formatSeconds(halfSeconds), [ halfSeconds ]);
|
const secondsLabel = useMemo(() => formatSeconds(halfSeconds), [ halfSeconds ]);
|
||||||
|
|
||||||
@@ -55,7 +56,8 @@ export const WiredConditionCounterTimeMatchesView: FC<{}> = () =>
|
|||||||
normalizeComparison(comparison),
|
normalizeComparison(comparison),
|
||||||
normalizeMinutes(minutes),
|
normalizeMinutes(minutes),
|
||||||
normalizeHalfSeconds(halfSeconds),
|
normalizeHalfSeconds(halfSeconds),
|
||||||
furniSource
|
furniSource,
|
||||||
|
quantifier
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -67,6 +69,7 @@ export const WiredConditionCounterTimeMatchesView: FC<{}> = () =>
|
|||||||
setMinutes((trigger.intData.length > 1) ? normalizeMinutes(trigger.intData[1]) : 0);
|
setMinutes((trigger.intData.length > 1) ? normalizeMinutes(trigger.intData[1]) : 0);
|
||||||
setHalfSeconds((trigger.intData.length > 2) ? normalizeHalfSeconds(trigger.intData[2]) : 0);
|
setHalfSeconds((trigger.intData.length > 2) ? normalizeHalfSeconds(trigger.intData[2]) : 0);
|
||||||
setFurniSource((trigger.intData.length > 3) ? trigger.intData[3] : ((trigger.selectedItems?.length ?? 0) > 0 ? 100 : 0));
|
setFurniSource((trigger.intData.length > 3) ? trigger.intData[3] : ((trigger.selectedItems?.length ?? 0) > 0 ? 100 : 0));
|
||||||
|
setQuantifier((trigger.intData.length > 4 && trigger.intData[4] === 1) ? 1 : 0);
|
||||||
}, [ trigger ]);
|
}, [ trigger ]);
|
||||||
|
|
||||||
useEffect(() =>
|
useEffect(() =>
|
||||||
@@ -86,7 +89,20 @@ export const WiredConditionCounterTimeMatchesView: FC<{}> = () =>
|
|||||||
hasSpecialInput={ true }
|
hasSpecialInput={ true }
|
||||||
requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_BY_ID_BY_TYPE_OR_FROM_CONTEXT }
|
requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_BY_ID_BY_TYPE_OR_FROM_CONTEXT }
|
||||||
save={ save }
|
save={ save }
|
||||||
footer={ <WiredSourcesSelector showFurni={ true } furniSource={ furniSource } onChangeFurni={ setFurniSource } /> }>
|
footer={
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
<div className="flex flex-col gap-1">
|
||||||
|
<Text bold>{ LocalizeText('wiredfurni.params.quantifier_selection') }</Text>
|
||||||
|
{ [ 0, 1 ].map(value => (
|
||||||
|
<label key={ value } className="flex items-center gap-1">
|
||||||
|
<input checked={ (quantifier === value) } className="form-check-input" name="counterTimeQuantifier" type="radio" onChange={ () => setQuantifier(value) } />
|
||||||
|
<Text>{ LocalizeText(`wiredfurni.params.quantifier.furni.${ value }`) }</Text>
|
||||||
|
</label>
|
||||||
|
)) }
|
||||||
|
</div>
|
||||||
|
<WiredSourcesSelector showFurni={ true } furniSource={ furniSource } onChangeFurni={ setFurniSource } />
|
||||||
|
</div>
|
||||||
|
}>
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
{ COMPARISON_OPTIONS.map(option =>
|
{ COMPARISON_OPTIONS.map(option =>
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,28 +1,41 @@
|
|||||||
import { FC, useEffect, useState } from 'react';
|
import { FC, useEffect, useState } from 'react';
|
||||||
import { WiredFurniType } from '../../../../api';
|
import { LocalizeText, WiredFurniType } from '../../../../api';
|
||||||
|
import { Text } from '../../../../common';
|
||||||
import { useWired } from '../../../../hooks';
|
import { useWired } from '../../../../hooks';
|
||||||
import { WiredConditionBaseView } from './WiredConditionBaseView';
|
import { WiredConditionBaseView } from './WiredConditionBaseView';
|
||||||
import { WiredSourcesSelector } from '../WiredSourcesSelector';
|
import { WiredSourcesSelector } from '../WiredSourcesSelector';
|
||||||
|
|
||||||
export const WiredConditionFurniHasAvatarOnView: FC<{}> = props =>
|
interface WiredConditionFurniHasAvatarOnViewProps
|
||||||
|
{
|
||||||
|
negative?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const WiredConditionFurniHasAvatarOnView: FC<WiredConditionFurniHasAvatarOnViewProps> = ({ negative = false }) =>
|
||||||
{
|
{
|
||||||
const { trigger = null, setIntParams = null } = useWired();
|
const { trigger = null, setIntParams = null } = useWired();
|
||||||
|
const [ requireAll, setRequireAll ] = useState(0);
|
||||||
const [ furniSource, setFurniSource ] = useState<number>(() =>
|
const [ furniSource, setFurniSource ] = useState<number>(() =>
|
||||||
{
|
{
|
||||||
if(trigger?.intData?.length >= 1) return trigger.intData[0];
|
if(trigger?.intData?.length > 1) return trigger.intData[1];
|
||||||
|
if(trigger?.intData?.length >= 1 && trigger.intData[0] > 1) return trigger.intData[0];
|
||||||
return (trigger?.selectedItems?.length ?? 0) > 0 ? 100 : 0;
|
return (trigger?.selectedItems?.length ?? 0) > 0 ? 100 : 0;
|
||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() =>
|
useEffect(() =>
|
||||||
{
|
{
|
||||||
if(!trigger) return;
|
if(!trigger) return;
|
||||||
if(trigger.intData.length >= 1) setFurniSource(trigger.intData[0]);
|
|
||||||
|
if(trigger.intData.length >= 1) setRequireAll(trigger.intData[0] === 1 ? 1 : 0);
|
||||||
|
else setRequireAll(0);
|
||||||
|
|
||||||
|
if(trigger.intData.length > 1) setFurniSource(trigger.intData[1]);
|
||||||
|
else if(trigger.intData.length >= 1 && trigger.intData[0] > 1) setFurniSource(trigger.intData[0]);
|
||||||
else setFurniSource((trigger.selectedItems?.length ?? 0) > 0 ? 100 : 0);
|
else setFurniSource((trigger.selectedItems?.length ?? 0) > 0 ? 100 : 0);
|
||||||
}, [ trigger ]);
|
}, [ trigger ]);
|
||||||
|
|
||||||
const onChangeFurniSource = (next: number) => setFurniSource(next);
|
const onChangeFurniSource = (next: number) => setFurniSource(next);
|
||||||
|
|
||||||
const save = () => setIntParams([ furniSource ]);
|
const save = () => setIntParams([ requireAll, furniSource ]);
|
||||||
|
|
||||||
const requiresFurni = WiredFurniType.STUFF_SELECTION_OPTION_BY_ID;
|
const requiresFurni = WiredFurniType.STUFF_SELECTION_OPTION_BY_ID;
|
||||||
|
|
||||||
@@ -31,6 +44,16 @@ export const WiredConditionFurniHasAvatarOnView: FC<{}> = props =>
|
|||||||
hasSpecialInput={ true }
|
hasSpecialInput={ true }
|
||||||
requiresFurni={ requiresFurni }
|
requiresFurni={ requiresFurni }
|
||||||
save={ save }
|
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.requireall') }</Text>
|
||||||
|
{ [ 0, 1 ].map(value => (
|
||||||
|
<label key={ value } className="flex items-center gap-1">
|
||||||
|
<input checked={ (requireAll === value) } className="form-check-input" name="furniHasAvatarRequireAll" type="radio" onChange={ () => setRequireAll(value) } />
|
||||||
|
<Text>{ LocalizeText(`wiredfurni.params.${ negative ? 'not_requireall' : 'requireall' }.${ value + 2 }`) }</Text>
|
||||||
|
</label>
|
||||||
|
)) }
|
||||||
|
</div>
|
||||||
|
</WiredConditionBaseView>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,36 +1,240 @@
|
|||||||
import { FC, useEffect, useState } from 'react';
|
import { FC, useCallback, useEffect, useRef, useState } from 'react';
|
||||||
import { WiredFurniType } from '../../../../api';
|
import { LocalizeText, WiredFurniType, WiredSelectionVisualizer } from '../../../../api';
|
||||||
|
import { Button, Text } from '../../../../common';
|
||||||
import { useWired } from '../../../../hooks';
|
import { useWired } from '../../../../hooks';
|
||||||
|
import { FURNI_SOURCES, WiredSourceOption, WiredSourcesSelector } from '../WiredSourcesSelector';
|
||||||
import { WiredConditionBaseView } from './WiredConditionBaseView';
|
import { WiredConditionBaseView } from './WiredConditionBaseView';
|
||||||
import { WiredSourcesSelector } from '../WiredSourcesSelector';
|
|
||||||
|
|
||||||
export const WiredConditionFurniIsOfTypeView: FC<{}> = props =>
|
const SOURCE_TRIGGER = 0;
|
||||||
|
const SOURCE_SELECTED = 100;
|
||||||
|
const SOURCE_SECONDARY_SELECTED = 101;
|
||||||
|
const FURNI_DELIMITER = ';';
|
||||||
|
|
||||||
|
const MATCH_FURNI_SOURCES: WiredSourceOption[] = [
|
||||||
|
...FURNI_SOURCES,
|
||||||
|
{ value: SOURCE_SECONDARY_SELECTED, label: 'wiredfurni.params.sources.furni.101' }
|
||||||
|
];
|
||||||
|
|
||||||
|
type SelectionMode = 'primary' | 'secondary';
|
||||||
|
|
||||||
|
const parseIds = (data: string): number[] =>
|
||||||
{
|
{
|
||||||
const { trigger = null, setIntParams = null } = useWired();
|
if(!data || !data.length) return [];
|
||||||
const [ furniSource, setFurniSource ] = useState<number>(() =>
|
|
||||||
|
const ids = new Set<number>();
|
||||||
|
|
||||||
|
for(const part of data.split(/[;,\t]/))
|
||||||
{
|
{
|
||||||
if(trigger?.intData?.length >= 1) return trigger.intData[0];
|
const trimmed = part.trim();
|
||||||
return (trigger?.selectedItems?.length ?? 0) > 0 ? 100 : 0;
|
if(!trimmed.length) continue;
|
||||||
});
|
|
||||||
|
const value = parseInt(trimmed, 10);
|
||||||
|
if(!isNaN(value) && value > 0) ids.add(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Array.from(ids);
|
||||||
|
};
|
||||||
|
|
||||||
|
const serializeIds = (ids: number[]): string =>
|
||||||
|
{
|
||||||
|
if(!ids || !ids.length) return '';
|
||||||
|
|
||||||
|
return ids.filter(id => (id > 0)).join(FURNI_DELIMITER);
|
||||||
|
};
|
||||||
|
|
||||||
|
interface WiredConditionFurniIsOfTypeViewProps
|
||||||
|
{
|
||||||
|
negative?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const WiredConditionFurniIsOfTypeView: FC<WiredConditionFurniIsOfTypeViewProps> = ({ negative = false }) =>
|
||||||
|
{
|
||||||
|
const [ matchSource, setMatchSource ] = useState<number>(SOURCE_TRIGGER);
|
||||||
|
const [ compareSource, setCompareSource ] = useState<number>(SOURCE_TRIGGER);
|
||||||
|
const [ quantifier, setQuantifier ] = useState<number>(0);
|
||||||
|
const [ primaryFurniIds, setPrimaryFurniIds ] = useState<number[]>([]);
|
||||||
|
const [ secondaryFurniIds, setSecondaryFurniIds ] = useState<number[]>([]);
|
||||||
|
const [ selectionMode, setSelectionMode ] = useState<SelectionMode>('primary');
|
||||||
|
|
||||||
|
const highlightedIds = useRef<number[]>([]);
|
||||||
|
|
||||||
|
const { trigger = null, furniIds = [], setFurniIds, setIntParams, setStringParam, setAllowsFurni } = useWired();
|
||||||
|
|
||||||
|
const syncHighlights = useCallback((nextPrimaryIds: number[], nextSecondaryIds: number[]) =>
|
||||||
|
{
|
||||||
|
if(highlightedIds.current.length)
|
||||||
|
{
|
||||||
|
WiredSelectionVisualizer.clearSelectionShaderFromFurni(highlightedIds.current);
|
||||||
|
WiredSelectionVisualizer.clearSecondarySelectionShaderFromFurni(highlightedIds.current);
|
||||||
|
}
|
||||||
|
|
||||||
|
const secondarySet = new Set(nextSecondaryIds);
|
||||||
|
const primaryOnlyIds = nextPrimaryIds.filter(id => !secondarySet.has(id));
|
||||||
|
|
||||||
|
if(primaryOnlyIds.length) WiredSelectionVisualizer.applySelectionShaderToFurni(primaryOnlyIds);
|
||||||
|
if(nextSecondaryIds.length) WiredSelectionVisualizer.applySecondarySelectionShaderToFurni(nextSecondaryIds);
|
||||||
|
|
||||||
|
highlightedIds.current = Array.from(new Set([ ...nextPrimaryIds, ...nextSecondaryIds ]));
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const switchSelection = useCallback((mode: SelectionMode) =>
|
||||||
|
{
|
||||||
|
const canEditPrimary = (matchSource === SOURCE_SELECTED) || (compareSource === SOURCE_SELECTED);
|
||||||
|
const canEditSecondary = (matchSource === SOURCE_SECONDARY_SELECTED) || (compareSource === SOURCE_SECONDARY_SELECTED);
|
||||||
|
|
||||||
|
if(mode === 'primary' && !canEditPrimary) return;
|
||||||
|
if(mode === 'secondary' && !canEditSecondary) return;
|
||||||
|
|
||||||
|
setSelectionMode(mode);
|
||||||
|
setFurniIds([ ...(mode === 'primary' ? primaryFurniIds : secondaryFurniIds) ]);
|
||||||
|
}, [ matchSource, compareSource, primaryFurniIds, secondaryFurniIds, setFurniIds ]);
|
||||||
|
|
||||||
useEffect(() =>
|
useEffect(() =>
|
||||||
{
|
{
|
||||||
if(!trigger) return;
|
if(!trigger) return;
|
||||||
if(trigger.intData.length >= 1) setFurniSource(trigger.intData[0]);
|
|
||||||
else setFurniSource((trigger.selectedItems?.length ?? 0) > 0 ? 100 : 0);
|
|
||||||
}, [ trigger ]);
|
|
||||||
|
|
||||||
const onChangeFurniSource = (next: number) => setFurniSource(next);
|
const nextPrimaryIds = trigger.selectedItems ?? [];
|
||||||
|
const nextSecondaryIds = parseIds(trigger.stringData);
|
||||||
|
const nextMatchSource = (trigger.intData.length >= 1)
|
||||||
|
? trigger.intData[0]
|
||||||
|
: (nextPrimaryIds.length ? SOURCE_SELECTED : SOURCE_TRIGGER);
|
||||||
|
const nextCompareSource = (trigger.intData.length >= 2)
|
||||||
|
? trigger.intData[1]
|
||||||
|
: (nextSecondaryIds.length ? SOURCE_SECONDARY_SELECTED : SOURCE_TRIGGER);
|
||||||
|
const nextQuantifier = (trigger.intData.length >= 3)
|
||||||
|
? (trigger.intData[2] === 1 ? 1 : 0)
|
||||||
|
: 0;
|
||||||
|
|
||||||
const save = () => setIntParams([ furniSource ]);
|
setMatchSource(nextMatchSource);
|
||||||
|
setCompareSource(nextCompareSource);
|
||||||
|
setQuantifier(nextQuantifier);
|
||||||
|
setPrimaryFurniIds(nextPrimaryIds);
|
||||||
|
setSecondaryFurniIds(nextSecondaryIds);
|
||||||
|
setSelectionMode('primary');
|
||||||
|
setFurniIds([ ...nextPrimaryIds ]);
|
||||||
|
}, [ trigger, setFurniIds, negative ]);
|
||||||
|
|
||||||
const requiresFurni = WiredFurniType.STUFF_SELECTION_OPTION_BY_ID_OR_BY_TYPE;
|
useEffect(() =>
|
||||||
|
{
|
||||||
|
if(selectionMode === 'primary') setPrimaryFurniIds(furniIds);
|
||||||
|
else setSecondaryFurniIds(furniIds);
|
||||||
|
}, [ furniIds, selectionMode ]);
|
||||||
|
|
||||||
|
useEffect(() =>
|
||||||
|
{
|
||||||
|
syncHighlights(primaryFurniIds, secondaryFurniIds);
|
||||||
|
}, [ primaryFurniIds, secondaryFurniIds, syncHighlights ]);
|
||||||
|
|
||||||
|
useEffect(() =>
|
||||||
|
{
|
||||||
|
const canEditPrimary = (matchSource === SOURCE_SELECTED) || (compareSource === SOURCE_SELECTED);
|
||||||
|
const canEditSecondary = (matchSource === SOURCE_SECONDARY_SELECTED) || (compareSource === SOURCE_SECONDARY_SELECTED);
|
||||||
|
|
||||||
|
if(selectionMode === 'primary' && !canEditPrimary && canEditSecondary)
|
||||||
|
{
|
||||||
|
switchSelection('secondary');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(selectionMode === 'secondary' && !canEditSecondary && canEditPrimary)
|
||||||
|
{
|
||||||
|
switchSelection('primary');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const canEditCurrent = ((selectionMode === 'primary') ? canEditPrimary : canEditSecondary);
|
||||||
|
|
||||||
|
setAllowsFurni(canEditCurrent ? WiredFurniType.STUFF_SELECTION_OPTION_BY_ID_OR_BY_TYPE : WiredFurniType.STUFF_SELECTION_OPTION_NONE);
|
||||||
|
}, [ selectionMode, matchSource, compareSource, switchSelection, setAllowsFurni ]);
|
||||||
|
|
||||||
|
useEffect(() =>
|
||||||
|
{
|
||||||
|
return () =>
|
||||||
|
{
|
||||||
|
if(!highlightedIds.current.length) return;
|
||||||
|
|
||||||
|
WiredSelectionVisualizer.clearSelectionShaderFromFurni(highlightedIds.current);
|
||||||
|
WiredSelectionVisualizer.clearSecondarySelectionShaderFromFurni(highlightedIds.current);
|
||||||
|
highlightedIds.current = [];
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const save = useCallback(() =>
|
||||||
|
{
|
||||||
|
if(selectionMode === 'secondary')
|
||||||
|
{
|
||||||
|
setSelectionMode('primary');
|
||||||
|
setFurniIds([ ...primaryFurniIds ]);
|
||||||
|
}
|
||||||
|
|
||||||
|
setIntParams([
|
||||||
|
matchSource,
|
||||||
|
compareSource,
|
||||||
|
quantifier
|
||||||
|
]);
|
||||||
|
setStringParam(serializeIds(secondaryFurniIds));
|
||||||
|
}, [ selectionMode, primaryFurniIds, matchSource, compareSource, quantifier, secondaryFurniIds, setFurniIds, setIntParams, setStringParam ]);
|
||||||
|
|
||||||
|
const selectionLimit = trigger?.maximumItemSelectionCount ?? 0;
|
||||||
|
const quantifierKeyPrefix = negative ? 'wiredfurni.params.quantifier.furni.neg' : 'wiredfurni.params.quantifier.furni';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<WiredConditionBaseView
|
<WiredConditionBaseView
|
||||||
hasSpecialInput={ true }
|
hasSpecialInput={ true }
|
||||||
requiresFurni={ requiresFurni }
|
requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_BY_ID_OR_BY_TYPE }
|
||||||
save={ save }
|
save={ save }
|
||||||
footer={ <WiredSourcesSelector showFurni={ true } furniSource={ furniSource } onChangeFurni={ onChangeFurniSource } /> } />
|
footer={
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
<WiredSourcesSelector
|
||||||
|
showFurni={ true }
|
||||||
|
furniTitle="wiredfurni.params.sources.furni.title.match.0"
|
||||||
|
furniSources={ MATCH_FURNI_SOURCES }
|
||||||
|
furniSource={ matchSource }
|
||||||
|
onChangeFurni={ setMatchSource } />
|
||||||
|
<hr className="m-0 bg-dark" />
|
||||||
|
<WiredSourcesSelector
|
||||||
|
showFurni={ true }
|
||||||
|
furniTitle="wiredfurni.params.sources.furni.title.match.1"
|
||||||
|
furniSources={ MATCH_FURNI_SOURCES }
|
||||||
|
furniSource={ compareSource }
|
||||||
|
onChangeFurni={ setCompareSource } />
|
||||||
|
</div>
|
||||||
|
}>
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
<Text bold>{ LocalizeText('wiredfurni.params.quantifier_selection') }</Text>
|
||||||
|
{ [ 0, 1 ].map(value => (
|
||||||
|
<label key={ value } className="flex items-center gap-1">
|
||||||
|
<input checked={ (quantifier === value) } className="form-check-input" name="stuffIsQuantifier" type="radio" onChange={ () => setQuantifier(value) } />
|
||||||
|
<Text>{ LocalizeText(`${ quantifierKeyPrefix }.${ value }`) }</Text>
|
||||||
|
</label>
|
||||||
|
)) }
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col gap-3">
|
||||||
|
<div className="flex flex-col gap-1">
|
||||||
|
<Text bold>{ LocalizeText('wiredfurni.params.sources.furni.title.match.0') }</Text>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<Button
|
||||||
|
variant={ (selectionMode === 'primary') ? 'primary' : 'secondary' }
|
||||||
|
disabled={ matchSource !== SOURCE_SELECTED && compareSource !== SOURCE_SELECTED }
|
||||||
|
onClick={ () => switchSelection('primary') }>
|
||||||
|
{ LocalizeText('wiredfurni.params.sources.furni.100') }
|
||||||
|
</Button>
|
||||||
|
<Text small>{ selectionLimit ? `${ primaryFurniIds.length }/${ selectionLimit }` : primaryFurniIds.length }</Text>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col gap-1">
|
||||||
|
<Text bold>{ LocalizeText('wiredfurni.params.sources.furni.title.match.1') }</Text>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<Button
|
||||||
|
variant={ (selectionMode === 'secondary') ? 'primary' : 'secondary' }
|
||||||
|
disabled={ matchSource !== SOURCE_SECONDARY_SELECTED && compareSource !== SOURCE_SECONDARY_SELECTED }
|
||||||
|
onClick={ () => switchSelection('secondary') }>
|
||||||
|
{ LocalizeText('wiredfurni.params.sources.furni.101') }
|
||||||
|
</Button>
|
||||||
|
<Text small>{ selectionLimit ? `${ secondaryFurniIds.length }/${ selectionLimit }` : secondaryFurniIds.length }</Text>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</WiredConditionBaseView>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,27 +5,38 @@ import { useWired } from '../../../../hooks';
|
|||||||
import { WiredConditionBaseView } from './WiredConditionBaseView';
|
import { WiredConditionBaseView } from './WiredConditionBaseView';
|
||||||
import { WiredSourcesSelector } from '../WiredSourcesSelector';
|
import { WiredSourcesSelector } from '../WiredSourcesSelector';
|
||||||
|
|
||||||
export const WiredConditionFurniMatchesSnapshotView: FC<{}> = props =>
|
interface WiredConditionFurniMatchesSnapshotViewProps
|
||||||
|
{
|
||||||
|
negative?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const WiredConditionFurniMatchesSnapshotView: FC<WiredConditionFurniMatchesSnapshotViewProps> = ({ negative = false }) =>
|
||||||
{
|
{
|
||||||
const [ stateFlag, setStateFlag ] = useState(0);
|
const [ stateFlag, setStateFlag ] = useState(0);
|
||||||
const [ directionFlag, setDirectionFlag ] = useState(0);
|
const [ directionFlag, setDirectionFlag ] = useState(0);
|
||||||
const [ positionFlag, setPositionFlag ] = useState(0);
|
const [ positionFlag, setPositionFlag ] = useState(0);
|
||||||
|
const [ altitudeFlag, setAltitudeFlag ] = useState(0);
|
||||||
|
const [ quantifier, setQuantifier ] = useState(0);
|
||||||
const { trigger = null, setIntParams = null } = useWired();
|
const { trigger = null, setIntParams = null } = useWired();
|
||||||
const [ furniSource, setFurniSource ] = useState<number>(() =>
|
const [ furniSource, setFurniSource ] = useState<number>(() =>
|
||||||
{
|
{
|
||||||
|
if(trigger?.intData?.length > 4) return trigger.intData[4];
|
||||||
if(trigger?.intData?.length > 3) return trigger.intData[3];
|
if(trigger?.intData?.length > 3) return trigger.intData[3];
|
||||||
return (trigger?.selectedItems?.length ?? 0) > 0 ? 100 : 0;
|
return (trigger?.selectedItems?.length ?? 0) > 0 ? 100 : 0;
|
||||||
});
|
});
|
||||||
|
|
||||||
const save = () => setIntParams([ stateFlag, directionFlag, positionFlag, furniSource ]);
|
const save = () => setIntParams([ stateFlag, directionFlag, positionFlag, altitudeFlag, furniSource, quantifier ]);
|
||||||
|
|
||||||
useEffect(() =>
|
useEffect(() =>
|
||||||
{
|
{
|
||||||
setStateFlag(trigger.getBoolean(0) ? 1 : 0);
|
setStateFlag(trigger.getBoolean(0) ? 1 : 0);
|
||||||
setDirectionFlag(trigger.getBoolean(1) ? 1 : 0);
|
setDirectionFlag(trigger.getBoolean(1) ? 1 : 0);
|
||||||
setPositionFlag(trigger.getBoolean(2) ? 1 : 0);
|
setPositionFlag(trigger.getBoolean(2) ? 1 : 0);
|
||||||
if(trigger.intData.length > 3) setFurniSource(trigger.intData[3]);
|
setAltitudeFlag((trigger.intData.length > 4 && trigger.getBoolean(3)) ? 1 : 0);
|
||||||
|
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);
|
else setFurniSource((trigger.selectedItems?.length ?? 0) > 0 ? 100 : 0);
|
||||||
|
setQuantifier((trigger.intData.length > 5 && trigger.intData[5] === 1) ? 1 : 0);
|
||||||
}, [ trigger ]);
|
}, [ trigger ]);
|
||||||
|
|
||||||
const onChangeFurniSource = (next: number) => setFurniSource(next);
|
const onChangeFurniSource = (next: number) => setFurniSource(next);
|
||||||
@@ -37,7 +48,23 @@ export const WiredConditionFurniMatchesSnapshotView: FC<{}> = props =>
|
|||||||
hasSpecialInput={ true }
|
hasSpecialInput={ true }
|
||||||
requiresFurni={ requiresFurni }
|
requiresFurni={ requiresFurni }
|
||||||
save={ save }
|
save={ save }
|
||||||
footer={ <WiredSourcesSelector showFurni={ true } furniSource={ furniSource } onChangeFurni={ onChangeFurniSource } /> }>
|
footer={
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
<div className="flex flex-col gap-1">
|
||||||
|
<Text bold>{ LocalizeText('wiredfurni.params.quantifier_selection') }</Text>
|
||||||
|
{ [ 0, 1 ].map(value =>
|
||||||
|
{
|
||||||
|
return (
|
||||||
|
<label key={ value } className="flex items-center gap-1">
|
||||||
|
<input checked={ (quantifier === value) } className="form-check-input" name="snapshotQuantifier" type="radio" onChange={ () => setQuantifier(value) } />
|
||||||
|
<Text>{ LocalizeText(`wiredfurni.params.quantifier.furni${ negative ? '.neg' : '' }.${ value }`) }</Text>
|
||||||
|
</label>
|
||||||
|
);
|
||||||
|
}) }
|
||||||
|
</div>
|
||||||
|
<WiredSourcesSelector showFurni={ true } furniSource={ furniSource } onChangeFurni={ onChangeFurniSource } />
|
||||||
|
</div>
|
||||||
|
}>
|
||||||
<div className="flex flex-col gap-1">
|
<div className="flex flex-col gap-1">
|
||||||
<Text bold>{ LocalizeText('wiredfurni.params.conditions') }</Text>
|
<Text bold>{ LocalizeText('wiredfurni.params.conditions') }</Text>
|
||||||
<div className="flex items-center gap-1">
|
<div className="flex items-center gap-1">
|
||||||
@@ -52,6 +79,10 @@ export const WiredConditionFurniMatchesSnapshotView: FC<{}> = props =>
|
|||||||
<input checked={ !!positionFlag } className="form-check-input" id="positionFlag" type="checkbox" onChange={ event => setPositionFlag(event.target.checked ? 1 : 0) } />
|
<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>
|
<Text>{ LocalizeText('wiredfurni.params.condition.position') }</Text>
|
||||||
</div>
|
</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>
|
</div>
|
||||||
</WiredConditionBaseView>
|
</WiredConditionBaseView>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { WiredConditionlayout } from '../../../../api';
|
import { WiredConditionlayout } from '../../../../api';
|
||||||
import { WiredConditionActorHasHandItemView } from './WiredConditionActorHasHandItem';
|
import { WiredConditionActorHasHandItemView } from './WiredConditionActorHasHandItem';
|
||||||
|
import { WiredConditionActorDirView } from './WiredConditionActorDirView';
|
||||||
import { WiredConditionActorIsGroupMemberView } from './WiredConditionActorIsGroupMemberView';
|
import { WiredConditionActorIsGroupMemberView } from './WiredConditionActorIsGroupMemberView';
|
||||||
import { WiredConditionActorIsOnFurniView } from './WiredConditionActorIsOnFurniView';
|
import { WiredConditionActorIsOnFurniView } from './WiredConditionActorIsOnFurniView';
|
||||||
import { WiredConditionActorIsTeamMemberView } from './WiredConditionActorIsTeamMemberView';
|
import { WiredConditionActorIsTeamMemberView } from './WiredConditionActorIsTeamMemberView';
|
||||||
@@ -10,6 +11,7 @@ import { WiredConditionDateRangeView } from './WiredConditionDateRangeView';
|
|||||||
import { WiredConditionMatchDateView } from './WiredConditionMatchDateView';
|
import { WiredConditionMatchDateView } from './WiredConditionMatchDateView';
|
||||||
import { WiredConditionMatchTimeView } from './WiredConditionMatchTimeView';
|
import { WiredConditionMatchTimeView } from './WiredConditionMatchTimeView';
|
||||||
import { WiredConditionHasAltitudeView } from './WiredConditionHasAltitudeView';
|
import { WiredConditionHasAltitudeView } from './WiredConditionHasAltitudeView';
|
||||||
|
import { WiredConditionMovementValidationView } from './WiredConditionMovementValidationView';
|
||||||
import { WiredConditionFurniHasAvatarOnView } from './WiredConditionFurniHasAvatarOnView';
|
import { WiredConditionFurniHasAvatarOnView } from './WiredConditionFurniHasAvatarOnView';
|
||||||
import { WiredConditionFurniHasFurniOnView } from './WiredConditionFurniHasFurniOnView';
|
import { WiredConditionFurniHasFurniOnView } from './WiredConditionFurniHasFurniOnView';
|
||||||
import { WiredConditionFurniHasNotFurniOnView } from './WiredConditionFurniHasNotFurniOnView';
|
import { WiredConditionFurniHasNotFurniOnView } from './WiredConditionFurniHasNotFurniOnView';
|
||||||
@@ -22,51 +24,68 @@ import { WiredConditionTeamHasScoreView } from './WiredConditionTeamHasScoreView
|
|||||||
import { WiredConditionTriggererMatchView } from './WiredConditionTriggererMatchView';
|
import { WiredConditionTriggererMatchView } from './WiredConditionTriggererMatchView';
|
||||||
import { WiredConditionUserPerformsActionView } from './WiredConditionUserPerformsActionView';
|
import { WiredConditionUserPerformsActionView } from './WiredConditionUserPerformsActionView';
|
||||||
import { WiredConditionUserCountInRoomView } from './WiredConditionUserCountInRoomView';
|
import { WiredConditionUserCountInRoomView } from './WiredConditionUserCountInRoomView';
|
||||||
|
import { WiredConditionSelectionQuantityView } from './WiredConditionSelectionQuantityView';
|
||||||
|
|
||||||
export const WiredConditionLayoutView = (code: number) =>
|
export const WiredConditionLayoutView = (code: number) =>
|
||||||
{
|
{
|
||||||
switch(code)
|
switch(code)
|
||||||
{
|
{
|
||||||
case WiredConditionlayout.ACTOR_HAS_HANDITEM:
|
case WiredConditionlayout.ACTOR_HAS_HANDITEM:
|
||||||
case WiredConditionlayout.NOT_ACTOR_HAS_HANDITEM:
|
|
||||||
return <WiredConditionActorHasHandItemView />;
|
return <WiredConditionActorHasHandItemView />;
|
||||||
|
case WiredConditionlayout.NOT_ACTOR_HAS_HANDITEM:
|
||||||
|
return <WiredConditionActorHasHandItemView negative={ true } />;
|
||||||
|
case WiredConditionlayout.ACTOR_DIR:
|
||||||
|
return <WiredConditionActorDirView />;
|
||||||
|
case WiredConditionlayout.SLC_QUANTITY:
|
||||||
|
return <WiredConditionSelectionQuantityView />;
|
||||||
case WiredConditionlayout.TRIGGERER_MATCH:
|
case WiredConditionlayout.TRIGGERER_MATCH:
|
||||||
case WiredConditionlayout.NOT_TRIGGERER_MATCH:
|
|
||||||
return <WiredConditionTriggererMatchView />;
|
return <WiredConditionTriggererMatchView />;
|
||||||
|
case WiredConditionlayout.NOT_TRIGGERER_MATCH:
|
||||||
|
return <WiredConditionTriggererMatchView negative={ true } />;
|
||||||
case WiredConditionlayout.ACTOR_IS_GROUP_MEMBER:
|
case WiredConditionlayout.ACTOR_IS_GROUP_MEMBER:
|
||||||
case WiredConditionlayout.NOT_ACTOR_IN_GROUP:
|
|
||||||
return <WiredConditionActorIsGroupMemberView />;
|
return <WiredConditionActorIsGroupMemberView />;
|
||||||
|
case WiredConditionlayout.NOT_ACTOR_IN_GROUP:
|
||||||
|
return <WiredConditionActorIsGroupMemberView negative={ true } />;
|
||||||
case WiredConditionlayout.ACTOR_IS_ON_FURNI:
|
case WiredConditionlayout.ACTOR_IS_ON_FURNI:
|
||||||
case WiredConditionlayout.NOT_ACTOR_ON_FURNI:
|
|
||||||
return <WiredConditionActorIsOnFurniView />;
|
return <WiredConditionActorIsOnFurniView />;
|
||||||
|
case WiredConditionlayout.NOT_ACTOR_ON_FURNI:
|
||||||
|
return <WiredConditionActorIsOnFurniView negative={ true } />;
|
||||||
case WiredConditionlayout.ACTOR_IS_IN_TEAM:
|
case WiredConditionlayout.ACTOR_IS_IN_TEAM:
|
||||||
case WiredConditionlayout.NOT_ACTOR_IN_TEAM:
|
|
||||||
return <WiredConditionActorIsTeamMemberView />;
|
return <WiredConditionActorIsTeamMemberView />;
|
||||||
|
case WiredConditionlayout.NOT_ACTOR_IN_TEAM:
|
||||||
|
return <WiredConditionActorIsTeamMemberView negative={ true } />;
|
||||||
case WiredConditionlayout.ACTOR_IS_WEARING_BADGE:
|
case WiredConditionlayout.ACTOR_IS_WEARING_BADGE:
|
||||||
case WiredConditionlayout.NOT_ACTOR_WEARS_BADGE:
|
|
||||||
return <WiredConditionActorIsWearingBadgeView />;
|
return <WiredConditionActorIsWearingBadgeView />;
|
||||||
|
case WiredConditionlayout.NOT_ACTOR_WEARS_BADGE:
|
||||||
|
return <WiredConditionActorIsWearingBadgeView negative={ true } />;
|
||||||
case WiredConditionlayout.ACTOR_IS_WEARING_EFFECT:
|
case WiredConditionlayout.ACTOR_IS_WEARING_EFFECT:
|
||||||
case WiredConditionlayout.NOT_ACTOR_WEARING_EFFECT:
|
|
||||||
return <WiredConditionActorIsWearingEffectView />;
|
return <WiredConditionActorIsWearingEffectView />;
|
||||||
|
case WiredConditionlayout.NOT_ACTOR_WEARING_EFFECT:
|
||||||
|
return <WiredConditionActorIsWearingEffectView negative={ true } />;
|
||||||
case WiredConditionlayout.DATE_RANGE_ACTIVE:
|
case WiredConditionlayout.DATE_RANGE_ACTIVE:
|
||||||
return <WiredConditionDateRangeView />;
|
return <WiredConditionDateRangeView />;
|
||||||
|
case WiredConditionlayout.MOVEMENT_VALIDATION:
|
||||||
|
return <WiredConditionMovementValidationView />;
|
||||||
case WiredConditionlayout.MATCH_TIME:
|
case WiredConditionlayout.MATCH_TIME:
|
||||||
return <WiredConditionMatchTimeView />;
|
return <WiredConditionMatchTimeView />;
|
||||||
case WiredConditionlayout.MATCH_DATE:
|
case WiredConditionlayout.MATCH_DATE:
|
||||||
return <WiredConditionMatchDateView />;
|
return <WiredConditionMatchDateView />;
|
||||||
case WiredConditionlayout.FURNIS_HAVE_AVATARS:
|
case WiredConditionlayout.FURNIS_HAVE_AVATARS:
|
||||||
case WiredConditionlayout.FURNI_NOT_HAVE_HABBO:
|
|
||||||
return <WiredConditionFurniHasAvatarOnView />;
|
return <WiredConditionFurniHasAvatarOnView />;
|
||||||
|
case WiredConditionlayout.FURNI_NOT_HAVE_HABBO:
|
||||||
|
return <WiredConditionFurniHasAvatarOnView negative={ true } />;
|
||||||
case WiredConditionlayout.HAS_STACKED_FURNIS:
|
case WiredConditionlayout.HAS_STACKED_FURNIS:
|
||||||
return <WiredConditionFurniHasFurniOnView />;
|
return <WiredConditionFurniHasFurniOnView />;
|
||||||
case WiredConditionlayout.NOT_HAS_STACKED_FURNIS:
|
case WiredConditionlayout.NOT_HAS_STACKED_FURNIS:
|
||||||
return <WiredConditionFurniHasNotFurniOnView />;
|
return <WiredConditionFurniHasNotFurniOnView />;
|
||||||
case WiredConditionlayout.STUFF_TYPE_MATCHES:
|
case WiredConditionlayout.STUFF_TYPE_MATCHES:
|
||||||
case WiredConditionlayout.NOT_FURNI_IS_OF_TYPE:
|
|
||||||
return <WiredConditionFurniIsOfTypeView />;
|
return <WiredConditionFurniIsOfTypeView />;
|
||||||
|
case WiredConditionlayout.NOT_FURNI_IS_OF_TYPE:
|
||||||
|
return <WiredConditionFurniIsOfTypeView negative={ true } />;
|
||||||
case WiredConditionlayout.STATES_MATCH:
|
case WiredConditionlayout.STATES_MATCH:
|
||||||
case WiredConditionlayout.NOT_STATES_MATCH:
|
|
||||||
return <WiredConditionFurniMatchesSnapshotView />;
|
return <WiredConditionFurniMatchesSnapshotView />;
|
||||||
|
case WiredConditionlayout.NOT_STATES_MATCH:
|
||||||
|
return <WiredConditionFurniMatchesSnapshotView negative={ true } />;
|
||||||
case WiredConditionlayout.TIME_ELAPSED_LESS:
|
case WiredConditionlayout.TIME_ELAPSED_LESS:
|
||||||
return <WiredConditionTimeElapsedLessView />;
|
return <WiredConditionTimeElapsedLessView />;
|
||||||
case WiredConditionlayout.TIME_ELAPSED_MORE:
|
case WiredConditionlayout.TIME_ELAPSED_MORE:
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
import { FC } from 'react';
|
||||||
|
import { WiredFurniType } from '../../../../api';
|
||||||
|
import { useWired } from '../../../../hooks';
|
||||||
|
import { WiredConditionBaseView } from './WiredConditionBaseView';
|
||||||
|
|
||||||
|
export const WiredConditionMovementValidationView: FC<{}> = () =>
|
||||||
|
{
|
||||||
|
const { setIntParams = null, setStringParam = null } = useWired();
|
||||||
|
|
||||||
|
const save = () =>
|
||||||
|
{
|
||||||
|
setIntParams([]);
|
||||||
|
setStringParam('');
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<WiredConditionBaseView
|
||||||
|
hasSpecialInput={ true }
|
||||||
|
requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE }
|
||||||
|
save={ save } />
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -0,0 +1,178 @@
|
|||||||
|
import { FC, useEffect, useMemo, useState } from 'react';
|
||||||
|
import { FaChevronLeft, FaChevronRight } from 'react-icons/fa';
|
||||||
|
import { LocalizeText, WiredFurniType } from '../../../../api';
|
||||||
|
import { Button, Slider, Text } from '../../../../common';
|
||||||
|
import { useWired } from '../../../../hooks';
|
||||||
|
import { WiredConditionBaseView } from './WiredConditionBaseView';
|
||||||
|
|
||||||
|
const SOURCE_GROUP_USERS = 0;
|
||||||
|
const SOURCE_GROUP_FURNI = 1;
|
||||||
|
const COMPARISON_OPTIONS = [ 0, 1, 2 ];
|
||||||
|
const MIN_QUANTITY = 0;
|
||||||
|
const MAX_QUANTITY = 100;
|
||||||
|
const QUANTITY_PATTERN = /^\d*$/;
|
||||||
|
|
||||||
|
const USER_SOURCES = [
|
||||||
|
{ 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 FURNI_SOURCES = [
|
||||||
|
{ value: 0, label: 'wiredfurni.params.sources.furni.0' },
|
||||||
|
{ value: 200, label: 'wiredfurni.params.sources.furni.200' },
|
||||||
|
{ value: 201, label: 'wiredfurni.params.sources.furni.201' }
|
||||||
|
];
|
||||||
|
|
||||||
|
const clampQuantity = (value: number) =>
|
||||||
|
{
|
||||||
|
if(isNaN(value)) return MIN_QUANTITY;
|
||||||
|
|
||||||
|
return Math.max(MIN_QUANTITY, Math.min(MAX_QUANTITY, Math.floor(value)));
|
||||||
|
};
|
||||||
|
|
||||||
|
const normalizeSource = (value: number, allowed: number[]) =>
|
||||||
|
{
|
||||||
|
return allowed.includes(value) ? value : 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const WiredConditionSelectionQuantityView: FC<{}> = () =>
|
||||||
|
{
|
||||||
|
const { trigger = null, setIntParams = null, setStringParam = null } = useWired();
|
||||||
|
const [ comparison, setComparison ] = useState(1);
|
||||||
|
const [ quantity, setQuantity ] = useState(0);
|
||||||
|
const [ quantityInput, setQuantityInput ] = useState('0');
|
||||||
|
const [ sourceGroup, setSourceGroup ] = useState(SOURCE_GROUP_USERS);
|
||||||
|
const [ userSource, setUserSource ] = useState(0);
|
||||||
|
const [ furniSource, setFurniSource ] = useState(0);
|
||||||
|
|
||||||
|
useEffect(() =>
|
||||||
|
{
|
||||||
|
if(!trigger) return;
|
||||||
|
|
||||||
|
const nextComparison = (trigger.intData.length > 0) ? trigger.intData[0] : 1;
|
||||||
|
const nextQuantity = clampQuantity((trigger.intData.length > 1) ? trigger.intData[1] : 0);
|
||||||
|
const nextSourceGroup = (trigger.intData.length > 2 && trigger.intData[2] === SOURCE_GROUP_FURNI) ? SOURCE_GROUP_FURNI : SOURCE_GROUP_USERS;
|
||||||
|
const nextSourceType = (trigger.intData.length > 3) ? trigger.intData[3] : 0;
|
||||||
|
|
||||||
|
setComparison(COMPARISON_OPTIONS.includes(nextComparison) ? nextComparison : 1);
|
||||||
|
setQuantity(nextQuantity);
|
||||||
|
setQuantityInput(nextQuantity.toString());
|
||||||
|
setSourceGroup(nextSourceGroup);
|
||||||
|
setUserSource(nextSourceGroup === SOURCE_GROUP_USERS ? normalizeSource(nextSourceType, USER_SOURCES.map(source => source.value)) : 0);
|
||||||
|
setFurniSource(nextSourceGroup === SOURCE_GROUP_FURNI ? normalizeSource(nextSourceType, FURNI_SOURCES.map(source => source.value)) : 0);
|
||||||
|
}, [ trigger ]);
|
||||||
|
|
||||||
|
const activeSources = useMemo(() => (sourceGroup === SOURCE_GROUP_FURNI) ? FURNI_SOURCES : USER_SOURCES, [ sourceGroup ]);
|
||||||
|
const activeSource = (sourceGroup === SOURCE_GROUP_FURNI) ? furniSource : userSource;
|
||||||
|
const activeSourceIndex = Math.max(0, activeSources.findIndex(source => source.value === activeSource));
|
||||||
|
|
||||||
|
const updateQuantity = (value: number) =>
|
||||||
|
{
|
||||||
|
const nextValue = clampQuantity(value);
|
||||||
|
|
||||||
|
setQuantity(nextValue);
|
||||||
|
setQuantityInput(nextValue.toString());
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateQuantityInput = (value: string) =>
|
||||||
|
{
|
||||||
|
if(!QUANTITY_PATTERN.test(value)) return;
|
||||||
|
|
||||||
|
setQuantityInput(value);
|
||||||
|
|
||||||
|
if(!value.length)
|
||||||
|
{
|
||||||
|
setQuantity(0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateQuantity(parseInt(value));
|
||||||
|
};
|
||||||
|
|
||||||
|
const cycleSource = (direction: -1 | 1) =>
|
||||||
|
{
|
||||||
|
const nextIndex = (activeSourceIndex + direction + activeSources.length) % activeSources.length;
|
||||||
|
const nextValue = activeSources[nextIndex].value;
|
||||||
|
|
||||||
|
if(sourceGroup === SOURCE_GROUP_FURNI) setFurniSource(nextValue);
|
||||||
|
else setUserSource(nextValue);
|
||||||
|
};
|
||||||
|
|
||||||
|
const save = () =>
|
||||||
|
{
|
||||||
|
setIntParams([
|
||||||
|
comparison,
|
||||||
|
clampQuantity(quantity),
|
||||||
|
sourceGroup,
|
||||||
|
(sourceGroup === SOURCE_GROUP_FURNI) ? furniSource : userSource
|
||||||
|
]);
|
||||||
|
setStringParam('');
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<WiredConditionBaseView
|
||||||
|
hasSpecialInput={ true }
|
||||||
|
requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE }
|
||||||
|
save={ save }>
|
||||||
|
<div className="flex flex-col gap-1">
|
||||||
|
<Text bold>{ LocalizeText('wiredfurni.params.comparison_selection') }</Text>
|
||||||
|
{ COMPARISON_OPTIONS.map(value =>
|
||||||
|
{
|
||||||
|
return (
|
||||||
|
<label key={ value } className="flex items-center gap-1">
|
||||||
|
<input checked={ (comparison === value) } className="form-check-input" name="selectionQuantityComparison" type="radio" onChange={ () => setComparison(value) } />
|
||||||
|
<Text>{ LocalizeText(`wiredfurni.params.comparison.${ value }`) }</Text>
|
||||||
|
</label>
|
||||||
|
);
|
||||||
|
}) }
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
<input
|
||||||
|
className="form-control form-control-sm"
|
||||||
|
inputMode="numeric"
|
||||||
|
max={ MAX_QUANTITY }
|
||||||
|
min={ MIN_QUANTITY }
|
||||||
|
type="text"
|
||||||
|
value={ quantityInput }
|
||||||
|
onBlur={ () => setQuantityInput(clampQuantity(quantity).toString()) }
|
||||||
|
onChange={ event => updateQuantityInput(event.target.value) } />
|
||||||
|
<Slider
|
||||||
|
max={ MAX_QUANTITY }
|
||||||
|
min={ MIN_QUANTITY }
|
||||||
|
step={ 1 }
|
||||||
|
value={ quantity }
|
||||||
|
onChange={ event => updateQuantity(event as number) } />
|
||||||
|
<Text small>{ quantity }</Text>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
<Text bold>{ LocalizeText('wiredfurni.params.sources.merged.title') }</Text>
|
||||||
|
<div className="flex gap-1">
|
||||||
|
<Button
|
||||||
|
fullWidth
|
||||||
|
variant={ (sourceGroup === SOURCE_GROUP_USERS) ? 'primary' : 'secondary' }
|
||||||
|
onClick={ () => setSourceGroup(SOURCE_GROUP_USERS) }>
|
||||||
|
{ LocalizeText('wiredfurni.params.sources.users.title') }
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
fullWidth
|
||||||
|
variant={ (sourceGroup === SOURCE_GROUP_FURNI) ? 'primary' : 'secondary' }
|
||||||
|
onClick={ () => setSourceGroup(SOURCE_GROUP_FURNI) }>
|
||||||
|
{ LocalizeText('wiredfurni.params.sources.furni.title') }
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<Button variant="primary" className="px-2 py-1" onClick={ () => cycleSource(-1) }>
|
||||||
|
<FaChevronLeft />
|
||||||
|
</Button>
|
||||||
|
<div className="flex flex-1 items-center justify-center">
|
||||||
|
<Text small>{ LocalizeText(activeSources[activeSourceIndex].label) }</Text>
|
||||||
|
</div>
|
||||||
|
<Button variant="primary" className="px-2 py-1" onClick={ () => cycleSource(1) }>
|
||||||
|
<FaChevronRight />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</WiredConditionBaseView>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -24,7 +24,12 @@ const COMPARE_USER_SOURCES: WiredSourceOption[] = [
|
|||||||
{ value: SOURCE_SPECIFIED_USERNAME, label: 'wiredfurni.params.sources.users.101' }
|
{ value: SOURCE_SPECIFIED_USERNAME, label: 'wiredfurni.params.sources.users.101' }
|
||||||
];
|
];
|
||||||
|
|
||||||
export const WiredConditionTriggererMatchView: FC<{}> = () =>
|
interface WiredConditionTriggererMatchViewProps
|
||||||
|
{
|
||||||
|
negative?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const WiredConditionTriggererMatchView: FC<WiredConditionTriggererMatchViewProps> = ({ negative = false }) =>
|
||||||
{
|
{
|
||||||
const [ entityType, setEntityType ] = useState(ENTITY_HABBO);
|
const [ entityType, setEntityType ] = useState(ENTITY_HABBO);
|
||||||
const [ avatarMode, setAvatarMode ] = useState(AVATAR_MODE_ANY);
|
const [ avatarMode, setAvatarMode ] = useState(AVATAR_MODE_ANY);
|
||||||
@@ -36,6 +41,7 @@ export const WiredConditionTriggererMatchView: FC<{}> = () =>
|
|||||||
const { trigger = null, setIntParams = null, setStringParam = null } = useWired();
|
const { trigger = null, setIntParams = null, setStringParam = null } = useWired();
|
||||||
|
|
||||||
const needsUsername = (avatarMode === AVATAR_MODE_CERTAIN) || (compareUserSource === SOURCE_SPECIFIED_USERNAME);
|
const needsUsername = (avatarMode === AVATAR_MODE_CERTAIN) || (compareUserSource === SOURCE_SPECIFIED_USERNAME);
|
||||||
|
const quantifierKeyPrefix = negative ? 'wiredfurni.params.quantifier.users.neg' : 'wiredfurni.params.quantifier.users';
|
||||||
|
|
||||||
const save = () =>
|
const save = () =>
|
||||||
{
|
{
|
||||||
@@ -81,7 +87,7 @@ export const WiredConditionTriggererMatchView: FC<{}> = () =>
|
|||||||
return (
|
return (
|
||||||
<div key={ value } className="flex items-center gap-1">
|
<div key={ value } className="flex items-center gap-1">
|
||||||
<input checked={ (quantifier === value) } className="form-check-input" id={ `triggererMatchQuantifier${ value }` } name="triggererMatchQuantifier" type="radio" onChange={ () => setQuantifier(value) } />
|
<input checked={ (quantifier === value) } className="form-check-input" id={ `triggererMatchQuantifier${ value }` } name="triggererMatchQuantifier" type="radio" onChange={ () => setQuantifier(value) } />
|
||||||
<Text>{ LocalizeText(`wiredfurni.params.quantifier.users.${ value }`) }</Text>
|
<Text>{ LocalizeText(`${ quantifierKeyPrefix }.${ value }`) }</Text>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}) }
|
}) }
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
import { WiredActionDefinition } from '@nitrots/nitro-renderer';
|
||||||
|
import { CSSProperties, FC, PropsWithChildren, ReactNode, useEffect } from 'react';
|
||||||
|
import { WiredFurniType } from '../../../../api';
|
||||||
|
import { useWired } from '../../../../hooks';
|
||||||
|
import { WiredBaseView } from '../WiredBaseView';
|
||||||
|
|
||||||
|
export interface WiredExtraBaseViewProps
|
||||||
|
{
|
||||||
|
hasSpecialInput: boolean;
|
||||||
|
requiresFurni: number;
|
||||||
|
save: () => void;
|
||||||
|
validate?: () => boolean;
|
||||||
|
cardStyle?: CSSProperties;
|
||||||
|
footer?: ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const WiredExtraBaseView: FC<PropsWithChildren<WiredExtraBaseViewProps>> = props =>
|
||||||
|
{
|
||||||
|
const { requiresFurni = WiredFurniType.STUFF_SELECTION_OPTION_NONE, save = null, validate = null, hasSpecialInput = false, children = null, cardStyle = undefined, footer = null } = props;
|
||||||
|
const { trigger = null, setActionDelay = null } = useWired();
|
||||||
|
|
||||||
|
useEffect(() =>
|
||||||
|
{
|
||||||
|
setActionDelay((trigger as WiredActionDefinition)?.delayInPulses ?? 0);
|
||||||
|
}, [ trigger, setActionDelay ]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<WiredBaseView hasSpecialInput={ hasSpecialInput } requiresFurni={ requiresFurni } save={ save } validate={ validate } wiredType="extra" cardStyle={ cardStyle } footer={ footer }>
|
||||||
|
{ children }
|
||||||
|
</WiredBaseView>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
import { ChangeEvent, FC, useEffect, useState } from 'react';
|
||||||
|
import { LocalizeText, WiredFurniType } from '../../../../api';
|
||||||
|
import { Text } from '../../../../common';
|
||||||
|
import { useWired } from '../../../../hooks';
|
||||||
|
import { WiredExtraBaseView } from './WiredExtraBaseView';
|
||||||
|
|
||||||
|
const MIN_FILTER = 0;
|
||||||
|
const MAX_FILTER = 10000;
|
||||||
|
|
||||||
|
const clampValue = (value: number) =>
|
||||||
|
{
|
||||||
|
if(isNaN(value)) return MIN_FILTER;
|
||||||
|
|
||||||
|
return Math.max(MIN_FILTER, Math.min(MAX_FILTER, Math.floor(value)));
|
||||||
|
};
|
||||||
|
|
||||||
|
export const WiredExtraFilterFurniView: FC<{}> = () =>
|
||||||
|
{
|
||||||
|
const { trigger = null, setIntParams = null, setStringParam = null } = useWired();
|
||||||
|
const [ amount, setAmount ] = useState(0);
|
||||||
|
|
||||||
|
useEffect(() =>
|
||||||
|
{
|
||||||
|
if(!trigger) return;
|
||||||
|
|
||||||
|
setAmount(clampValue((trigger.intData.length > 0) ? trigger.intData[0] : 0));
|
||||||
|
}, [ trigger ]);
|
||||||
|
|
||||||
|
const onChange = (event: ChangeEvent<HTMLInputElement>) =>
|
||||||
|
{
|
||||||
|
setAmount(clampValue(Number(event.target.value)));
|
||||||
|
};
|
||||||
|
|
||||||
|
const save = () =>
|
||||||
|
{
|
||||||
|
setIntParams([ clampValue(amount) ]);
|
||||||
|
setStringParam('');
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<WiredExtraBaseView hasSpecialInput={ true } requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE } save={ save } cardStyle={ { width: 360 } }>
|
||||||
|
<div className="flex flex-col gap-1">
|
||||||
|
<Text bold>{ LocalizeText('wiredfurni.params.setfilter') }</Text>
|
||||||
|
<input className="form-control form-control-sm" max={ MAX_FILTER } min={ MIN_FILTER } type="number" value={ amount } onChange={ onChange } />
|
||||||
|
</div>
|
||||||
|
</WiredExtraBaseView>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
import { ChangeEvent, FC, useEffect, useState } from 'react';
|
||||||
|
import { LocalizeText, WiredFurniType } from '../../../../api';
|
||||||
|
import { Text } from '../../../../common';
|
||||||
|
import { useWired } from '../../../../hooks';
|
||||||
|
import { WiredExtraBaseView } from './WiredExtraBaseView';
|
||||||
|
|
||||||
|
const MIN_FILTER = 0;
|
||||||
|
const MAX_FILTER = 10000;
|
||||||
|
|
||||||
|
const clampValue = (value: number) =>
|
||||||
|
{
|
||||||
|
if(isNaN(value)) return MIN_FILTER;
|
||||||
|
|
||||||
|
return Math.max(MIN_FILTER, Math.min(MAX_FILTER, Math.floor(value)));
|
||||||
|
};
|
||||||
|
|
||||||
|
export const WiredExtraFilterUserView: FC<{}> = () =>
|
||||||
|
{
|
||||||
|
const { trigger = null, setIntParams = null, setStringParam = null } = useWired();
|
||||||
|
const [ amount, setAmount ] = useState(0);
|
||||||
|
|
||||||
|
useEffect(() =>
|
||||||
|
{
|
||||||
|
if(!trigger) return;
|
||||||
|
|
||||||
|
setAmount(clampValue((trigger.intData.length > 0) ? trigger.intData[0] : 0));
|
||||||
|
}, [ trigger ]);
|
||||||
|
|
||||||
|
const onChange = (event: ChangeEvent<HTMLInputElement>) =>
|
||||||
|
{
|
||||||
|
setAmount(clampValue(Number(event.target.value)));
|
||||||
|
};
|
||||||
|
|
||||||
|
const save = () =>
|
||||||
|
{
|
||||||
|
setIntParams([ clampValue(amount) ]);
|
||||||
|
setStringParam('');
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<WiredExtraBaseView hasSpecialInput={ true } requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE } save={ save } cardStyle={ { width: 360 } }>
|
||||||
|
<div className="flex flex-col gap-1">
|
||||||
|
<Text bold>{ LocalizeText('wiredfurni.params.setfilter') }</Text>
|
||||||
|
<input className="form-control form-control-sm" max={ MAX_FILTER } min={ MIN_FILTER } type="number" value={ amount } onChange={ onChange } />
|
||||||
|
</div>
|
||||||
|
</WiredExtraBaseView>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -3,7 +3,7 @@ import { FC, useCallback, useEffect, useState } from 'react';
|
|||||||
import { LocalizeText } from '../../../../api';
|
import { LocalizeText } from '../../../../api';
|
||||||
import { Button, Text } from '../../../../common';
|
import { Button, Text } from '../../../../common';
|
||||||
import { useWired } from '../../../../hooks';
|
import { useWired } from '../../../../hooks';
|
||||||
import { WiredActionBaseView } from '../actions/WiredActionBaseView';
|
import { WiredSelectorBaseView } from './WiredSelectorBaseView';
|
||||||
|
|
||||||
export const WiredActionFurniAreaView: FC<{}> = props =>
|
export const WiredActionFurniAreaView: FC<{}> = props =>
|
||||||
{
|
{
|
||||||
@@ -101,7 +101,7 @@ export const WiredActionFurniAreaView: FC<{}> = props =>
|
|||||||
: 0;
|
: 0;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<WiredActionBaseView hasSpecialInput={ true } requiresFurni={ 0 } save={ save } hideDelay={ true } cardStyle={ { width: '385px'} }>
|
<WiredSelectorBaseView hasSpecialInput={ true } requiresFurni={ 0 } save={ save } hideDelay={ true } cardStyle={ { width: '385px'} }>
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<Text bold>{ LocalizeText('wiredfurni.params.area_selection') }</Text>
|
<Text bold>{ LocalizeText('wiredfurni.params.area_selection') }</Text>
|
||||||
<Text small>{ LocalizeText('wiredfurni.params.area_selection.info') }</Text>
|
<Text small>{ LocalizeText('wiredfurni.params.area_selection.info') }</Text>
|
||||||
@@ -154,6 +154,6 @@ export const WiredActionFurniAreaView: FC<{}> = props =>
|
|||||||
<Text small>{ LocalizeText('wiredfurni.params.selector_option.1') }</Text>
|
<Text small>{ LocalizeText('wiredfurni.params.selector_option.1') }</Text>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</WiredActionBaseView>
|
</WiredSelectorBaseView>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
import { CSSProperties, FC, PropsWithChildren, ReactNode } from 'react';
|
||||||
|
import { WiredFurniType } from '../../../../api';
|
||||||
|
import { WiredBaseView } from '../WiredBaseView';
|
||||||
|
|
||||||
|
export interface WiredSelectorBaseViewProps
|
||||||
|
{
|
||||||
|
hasSpecialInput: boolean;
|
||||||
|
requiresFurni: number;
|
||||||
|
save: () => void;
|
||||||
|
validate?: () => boolean;
|
||||||
|
cardStyle?: CSSProperties;
|
||||||
|
hideDelay?: boolean;
|
||||||
|
footer?: ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const WiredSelectorBaseView: FC<PropsWithChildren<WiredSelectorBaseViewProps>> = props =>
|
||||||
|
{
|
||||||
|
const { requiresFurni = WiredFurniType.STUFF_SELECTION_OPTION_NONE, save = null, validate = null, hasSpecialInput = false, children = null, cardStyle = undefined, footer = null } = props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<WiredBaseView hasSpecialInput={ hasSpecialInput } requiresFurni={ requiresFurni } save={ save } validate={ validate } wiredType="selector" cardStyle={ cardStyle } footer={ footer }>
|
||||||
|
{ children }
|
||||||
|
</WiredBaseView>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -0,0 +1,165 @@
|
|||||||
|
import { FC, useCallback, useEffect, useState } from 'react';
|
||||||
|
import { LocalizeText, WiredFurniType } from '../../../../api';
|
||||||
|
import { Slider, Text } from '../../../../common';
|
||||||
|
import { useWired } from '../../../../hooks';
|
||||||
|
import { WiredSelectorBaseView } from './WiredSelectorBaseView';
|
||||||
|
|
||||||
|
const MIN_ALTITUDE = 0;
|
||||||
|
const MAX_ALTITUDE = 40;
|
||||||
|
const ALTITUDE_STEP = 0.5;
|
||||||
|
const ALTITUDE_PATTERN = /^\d*(\.\d{0,2})?$/;
|
||||||
|
|
||||||
|
const clampAltitude = (value: number) =>
|
||||||
|
{
|
||||||
|
if(isNaN(value)) return MIN_ALTITUDE;
|
||||||
|
|
||||||
|
const clamped = Math.min(MAX_ALTITUDE, Math.max(MIN_ALTITUDE, value));
|
||||||
|
|
||||||
|
return parseFloat(clamped.toFixed(2));
|
||||||
|
};
|
||||||
|
|
||||||
|
const formatAltitude = (value: number) =>
|
||||||
|
{
|
||||||
|
const normalized = clampAltitude(value);
|
||||||
|
const text = normalized.toFixed(2);
|
||||||
|
|
||||||
|
return text.replace(/\.00$/, '').replace(/(\.\d)0$/, '$1');
|
||||||
|
};
|
||||||
|
|
||||||
|
const parseAltitude = (value: string) =>
|
||||||
|
{
|
||||||
|
if(!value || !value.trim().length) return 0;
|
||||||
|
|
||||||
|
const parsed = parseFloat(value);
|
||||||
|
|
||||||
|
if(isNaN(parsed)) return 0;
|
||||||
|
|
||||||
|
return clampAltitude(parsed);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const WiredSelectorFurniAltitudeView: FC<{}> = () =>
|
||||||
|
{
|
||||||
|
const { trigger = null, setIntParams = null, setStringParam = null } = useWired();
|
||||||
|
const [ comparison, setComparison ] = useState(1);
|
||||||
|
const [ filterExisting, setFilterExisting ] = useState(false);
|
||||||
|
const [ invert, setInvert ] = useState(false);
|
||||||
|
const [ altitude, setAltitude ] = useState(0);
|
||||||
|
const [ altitudeInput, setAltitudeInput ] = useState('0');
|
||||||
|
|
||||||
|
useEffect(() =>
|
||||||
|
{
|
||||||
|
if(!trigger) return;
|
||||||
|
|
||||||
|
const nextAltitude = parseAltitude(trigger.stringData);
|
||||||
|
|
||||||
|
setComparison((trigger.intData.length > 0) ? trigger.intData[0] : 1);
|
||||||
|
setFilterExisting((trigger.intData.length > 1) ? (trigger.intData[1] === 1) : false);
|
||||||
|
setInvert((trigger.intData.length > 2) ? (trigger.intData[2] === 1) : false);
|
||||||
|
setAltitude(nextAltitude);
|
||||||
|
setAltitudeInput(formatAltitude(nextAltitude));
|
||||||
|
}, [ trigger ]);
|
||||||
|
|
||||||
|
const updateAltitude = (value: number) =>
|
||||||
|
{
|
||||||
|
const nextValue = clampAltitude(value);
|
||||||
|
|
||||||
|
setAltitude(nextValue);
|
||||||
|
setAltitudeInput(formatAltitude(nextValue));
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateAltitudeInput = (value: string) =>
|
||||||
|
{
|
||||||
|
if(!ALTITUDE_PATTERN.test(value)) return;
|
||||||
|
|
||||||
|
setAltitudeInput(value);
|
||||||
|
|
||||||
|
if(!value.length)
|
||||||
|
{
|
||||||
|
setAltitude(0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const parsedValue = parseFloat(value);
|
||||||
|
|
||||||
|
if(isNaN(parsedValue)) return;
|
||||||
|
|
||||||
|
if(parsedValue > MAX_ALTITUDE)
|
||||||
|
{
|
||||||
|
updateAltitude(MAX_ALTITUDE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setAltitude(clampAltitude(parsedValue));
|
||||||
|
};
|
||||||
|
|
||||||
|
const save = useCallback(() =>
|
||||||
|
{
|
||||||
|
setIntParams([
|
||||||
|
comparison,
|
||||||
|
filterExisting ? 1 : 0,
|
||||||
|
invert ? 1 : 0
|
||||||
|
]);
|
||||||
|
setStringParam(formatAltitude(altitude));
|
||||||
|
}, [ altitude, comparison, filterExisting, invert, setIntParams, setStringParam ]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<WiredSelectorBaseView hasSpecialInput={ true } requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE } save={ save } hideDelay={ true } cardStyle={ { width: 400 } }>
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
<div className="flex flex-col gap-1">
|
||||||
|
{ [ 0, 1, 2 ].map(value =>
|
||||||
|
{
|
||||||
|
return (
|
||||||
|
<label key={ value } className="flex items-center gap-1">
|
||||||
|
<input checked={ (comparison === value) } className="form-check-input" name="furniAltitudeComparison" type="radio" onChange={ () => setComparison(value) } />
|
||||||
|
<Text>{ LocalizeText(`wiredfurni.params.comparison.${ value }`) }</Text>
|
||||||
|
</label>
|
||||||
|
);
|
||||||
|
}) }
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex flex-col gap-1">
|
||||||
|
<Text bold>{ LocalizeText('wiredfurni.params.setaltitude') }</Text>
|
||||||
|
<input
|
||||||
|
className="form-control form-control-sm"
|
||||||
|
inputMode="decimal"
|
||||||
|
type="text"
|
||||||
|
value={ altitudeInput }
|
||||||
|
onBlur={ () => setAltitudeInput(formatAltitude(altitude)) }
|
||||||
|
onChange={ event => updateAltitudeInput(event.target.value) } />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex flex-col gap-1">
|
||||||
|
<Slider
|
||||||
|
max={ MAX_ALTITUDE }
|
||||||
|
min={ MIN_ALTITUDE }
|
||||||
|
step={ ALTITUDE_STEP }
|
||||||
|
value={ altitude }
|
||||||
|
onChange={ event => updateAltitude(event as number) } />
|
||||||
|
<Text small>{ formatAltitude(altitude) }</Text>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr className="m-0 bg-dark" />
|
||||||
|
|
||||||
|
<Text bold>{ LocalizeText('wiredfurni.params.selector_options_selector') }</Text>
|
||||||
|
|
||||||
|
<label className="flex items-center gap-2">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
className="form-check-input"
|
||||||
|
checked={ filterExisting }
|
||||||
|
onChange={ event => setFilterExisting(event.target.checked) } />
|
||||||
|
<Text small>{ LocalizeText('wiredfurni.params.selector_option.0') }</Text>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label className="flex items-center gap-2">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
className="form-check-input"
|
||||||
|
checked={ invert }
|
||||||
|
onChange={ event => setInvert(event.target.checked) } />
|
||||||
|
<Text small>{ LocalizeText('wiredfurni.params.selector_option.1') }</Text>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</WiredSelectorBaseView>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -2,7 +2,7 @@ import { FC, useCallback, useEffect, useState } from 'react';
|
|||||||
import { LocalizeText, WiredFurniType } from '../../../../api';
|
import { LocalizeText, WiredFurniType } from '../../../../api';
|
||||||
import { Text } from '../../../../common';
|
import { Text } from '../../../../common';
|
||||||
import { useWired } from '../../../../hooks';
|
import { useWired } from '../../../../hooks';
|
||||||
import { WiredActionBaseView } from '../actions/WiredActionBaseView';
|
import { WiredSelectorBaseView } from './WiredSelectorBaseView';
|
||||||
|
|
||||||
const SOURCE_FURNI_PICKED = 0;
|
const SOURCE_FURNI_PICKED = 0;
|
||||||
|
|
||||||
@@ -42,7 +42,7 @@ export const WiredSelectorFurniByTypeView: FC<{}> = () =>
|
|||||||
const requiresFurni = WiredFurniType.STUFF_SELECTION_OPTION_BY_ID;
|
const requiresFurni = WiredFurniType.STUFF_SELECTION_OPTION_BY_ID;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<WiredActionBaseView hasSpecialInput={ true } requiresFurni={ requiresFurni } save={ save } hideDelay={ true } cardStyle={ { width: 400 } }>
|
<WiredSelectorBaseView hasSpecialInput={ true } requiresFurni={ requiresFurni } save={ save } hideDelay={ true } cardStyle={ { width: 400 } }>
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
|
|
||||||
<label className="flex items-center gap-2">
|
<label className="flex items-center gap-2">
|
||||||
@@ -76,6 +76,6 @@ export const WiredSelectorFurniByTypeView: FC<{}> = () =>
|
|||||||
<Text small>{ LocalizeText('wiredfurni.params.selector_option.1') }</Text>
|
<Text small>{ LocalizeText('wiredfurni.params.selector_option.1') }</Text>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</WiredActionBaseView>
|
</WiredSelectorBaseView>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import { GetRoomEngine, RoomObjectCategory } from '@nitrots/nitro-renderer';
|
import { GetRoomEngine } from '@nitrots/nitro-renderer';
|
||||||
import { CSSProperties, FC, useCallback, useEffect, useState } from 'react';
|
import { CSSProperties, FC, MouseEvent as ReactMouseEvent, useCallback, useEffect, useState } from 'react';
|
||||||
import { FaMinus, FaPlus, FaTimes } from 'react-icons/fa';
|
import { FaMinus, FaPlus, FaTimes } from 'react-icons/fa';
|
||||||
import { MdGridOn } from 'react-icons/md';
|
import { MdGridOn } from 'react-icons/md';
|
||||||
import { GetRoomSession, LocalizeText, WiredFurniType, WiredSelectionVisualizer } from '../../../../api';
|
import { LocalizeText, WiredFurniType } from '../../../../api';
|
||||||
import { Button, Text } from '../../../../common';
|
import { Button, Text } from '../../../../common';
|
||||||
import { useWired } from '../../../../hooks';
|
import { useWired } from '../../../../hooks';
|
||||||
import { WiredActionBaseView } from '../actions/WiredActionBaseView';
|
import { WiredSelectorBaseView } from './WiredSelectorBaseView';
|
||||||
|
|
||||||
const SOURCE_USER_TRIGGER = 0;
|
const SOURCE_USER_TRIGGER = 0;
|
||||||
const SOURCE_USER_SIGNAL = 1;
|
const SOURCE_USER_SIGNAL = 1;
|
||||||
@@ -47,33 +47,74 @@ const tileTop = (rx: number, ry: number) =>
|
|||||||
|
|
||||||
interface GridProps {
|
interface GridProps {
|
||||||
selectedTiles: Tile[];
|
selectedTiles: Tile[];
|
||||||
|
targetTile: Tile;
|
||||||
invert: boolean;
|
invert: boolean;
|
||||||
onToggle: (x: number, y: number) => void;
|
onSetTile: (x: number, y: number, selected: boolean) => void;
|
||||||
|
onMoveTarget: (x: number, y: number) => void;
|
||||||
|
targetPlacementMode: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const NeighborhoodGrid: FC<GridProps> = ({ selectedTiles, invert, onToggle }) =>
|
const NeighborhoodGrid: FC<GridProps> = ({ selectedTiles, targetTile, invert, onSetTile, onMoveTarget, targetPlacementMode }) =>
|
||||||
{
|
{
|
||||||
|
const [ dragMode, setDragMode ] = useState<'add' | 'remove' | 'target' | null>(null);
|
||||||
const tiles: JSX.Element[] = [];
|
const tiles: JSX.Element[] = [];
|
||||||
|
|
||||||
|
useEffect(() =>
|
||||||
|
{
|
||||||
|
const stopDragging = () => setDragMode(null);
|
||||||
|
|
||||||
|
window.addEventListener('mouseup', stopDragging);
|
||||||
|
|
||||||
|
return () => window.removeEventListener('mouseup', stopDragging);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const beginTileDrag = (event: ReactMouseEvent<HTMLDivElement>, rx: number, ry: number, isTarget: boolean, isSelected: boolean) =>
|
||||||
|
{
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
if(targetPlacementMode)
|
||||||
|
{
|
||||||
|
setDragMode('target');
|
||||||
|
onMoveTarget(rx, ry);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const nextMode = isSelected ? 'remove' : 'add';
|
||||||
|
|
||||||
|
setDragMode(nextMode);
|
||||||
|
onSetTile(rx, ry, nextMode === 'add');
|
||||||
|
};
|
||||||
|
|
||||||
|
const continueTileDrag = (event: ReactMouseEvent<HTMLDivElement>, rx: number, ry: number, isTarget: boolean) =>
|
||||||
|
{
|
||||||
|
if(!(event.buttons & 1) || !dragMode) return;
|
||||||
|
|
||||||
|
if(dragMode === 'target')
|
||||||
|
{
|
||||||
|
onMoveTarget(rx, ry);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
onSetTile(rx, ry, dragMode === 'add');
|
||||||
|
};
|
||||||
|
|
||||||
for (let ry = -GRID_RANGE; ry <= GRID_RANGE; ry++)
|
for (let ry = -GRID_RANGE; ry <= GRID_RANGE; ry++)
|
||||||
{
|
{
|
||||||
for (let rx = -GRID_RANGE; rx <= GRID_RANGE; rx++)
|
for (let rx = -GRID_RANGE; rx <= GRID_RANGE; rx++)
|
||||||
{
|
{
|
||||||
const isCenter = rx === 0 && ry === 0;
|
const isTarget = rx === targetTile.x && ry === targetTile.y;
|
||||||
const isSelected = tileIncluded(selectedTiles, rx, ry);
|
const isSelected = tileIncluded(selectedTiles, rx, ry);
|
||||||
const isActive = invert ? !isSelected : isSelected;
|
const isActive = invert ? !isSelected : isSelected;
|
||||||
const left = tileLeft(rx, ry);
|
const left = tileLeft(rx, ry);
|
||||||
const top_ = tileTop(rx, ry);
|
const top_ = tileTop(rx, ry);
|
||||||
const zIdx = rx + ry + GRID_RANGE * 2 + 10;
|
const zIdx = rx + ry + GRID_RANGE * 2 + 10;
|
||||||
|
|
||||||
const bgColor = isCenter
|
const bgColor = isActive
|
||||||
? '#ff9500'
|
? '#3399ff'
|
||||||
: isActive
|
: '#2a3042';
|
||||||
? '#3399ff'
|
|
||||||
: '#2a3042';
|
|
||||||
|
|
||||||
const borderColor = isCenter
|
const borderColor = isTarget
|
||||||
? '#cc6600'
|
? '#ffffff'
|
||||||
: isActive
|
: isActive
|
||||||
? '#1166cc'
|
? '#1166cc'
|
||||||
: '#1a2032';
|
: '#1a2032';
|
||||||
@@ -86,36 +127,61 @@ const NeighborhoodGrid: FC<GridProps> = ({ selectedTiles, invert, onToggle }) =>
|
|||||||
top: top_,
|
top: top_,
|
||||||
clipPath: 'polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%)',
|
clipPath: 'polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%)',
|
||||||
backgroundColor: bgColor,
|
backgroundColor: bgColor,
|
||||||
cursor: isCenter ? 'default' : 'pointer',
|
cursor: 'pointer',
|
||||||
zIndex: zIdx,
|
zIndex: zIdx,
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
color: isTarget ? '#ffffff' : 'transparent',
|
||||||
|
fontSize: 10
|
||||||
};
|
};
|
||||||
|
|
||||||
const border: CSSProperties = {
|
const border: CSSProperties = {
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
width: TILE_W + 2,
|
width: TILE_W + (isTarget ? 6 : 2),
|
||||||
height: TILE_H + 2,
|
height: TILE_H + (isTarget ? 6 : 2),
|
||||||
left: left - 1,
|
left: left - (isTarget ? 3 : 1),
|
||||||
top: top_ - 1,
|
top: top_ - (isTarget ? 3 : 1),
|
||||||
clipPath: 'polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%)',
|
clipPath: 'polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%)',
|
||||||
backgroundColor: borderColor,
|
backgroundColor: borderColor,
|
||||||
zIndex: zIdx - 1,
|
zIndex: zIdx - 1,
|
||||||
pointerEvents: 'none',
|
pointerEvents: 'none',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const targetOutline: CSSProperties = {
|
||||||
|
position: 'absolute',
|
||||||
|
width: TILE_W + 4,
|
||||||
|
height: TILE_H + 4,
|
||||||
|
left: left - 2,
|
||||||
|
top: top_ - 2,
|
||||||
|
zIndex: zIdx + 1,
|
||||||
|
pointerEvents: 'none',
|
||||||
|
overflow: 'visible'
|
||||||
|
};
|
||||||
|
|
||||||
tiles.push(
|
tiles.push(
|
||||||
<div key={ `b-${ rx }-${ ry }` } style={ border } />,
|
<div key={ `b-${ rx }-${ ry }` } style={ border } />,
|
||||||
|
isTarget && (
|
||||||
|
<svg key={ `o-${ rx }-${ ry }` } style={ targetOutline } viewBox={ `0 0 ${ TILE_W + 4 } ${ TILE_H + 4 }` }>
|
||||||
|
<polygon
|
||||||
|
points={ `${ (TILE_W + 4) / 2 },2 ${ TILE_W + 2 },${ (TILE_H + 4) / 2 } ${ (TILE_W + 4) / 2 },${ TILE_H + 2 } 2,${ (TILE_H + 4) / 2 }` }
|
||||||
|
fill="none"
|
||||||
|
stroke="#ffffff"
|
||||||
|
strokeWidth="1" />
|
||||||
|
</svg>
|
||||||
|
),
|
||||||
<div
|
<div
|
||||||
key={ `t-${ rx }-${ ry }` }
|
key={ `t-${ rx }-${ ry }` }
|
||||||
style={ diamond }
|
style={ diamond }
|
||||||
title={ `(${ rx }, ${ ry })` }
|
title={ `(${ rx }, ${ ry })` }
|
||||||
onClick={ () => !isCenter && onToggle(rx, ry) }
|
onMouseDown={ event => beginTileDrag(event, rx, ry, isTarget, isSelected) }
|
||||||
/>,
|
onMouseEnter={ event => continueTileDrag(event, rx, ry, isTarget) } />,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={ { position: 'relative', width: GRID_PX_W, height: GRID_PX_H, flexShrink: 0 } }>
|
<div style={ { position: 'relative', width: GRID_PX_W, height: GRID_PX_H, flexShrink: 0 } } onContextMenu={ event => event.preventDefault() }>
|
||||||
{ tiles }
|
{ tiles }
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@@ -127,10 +193,18 @@ export const WiredSelectorFurniNeighborhoodView: FC<{}> = () =>
|
|||||||
const [ filterExisting, setFilterExisting ] = useState(false);
|
const [ filterExisting, setFilterExisting ] = useState(false);
|
||||||
const [ invert, setInvert ] = useState(false);
|
const [ invert, setInvert ] = useState(false);
|
||||||
const [ sourceType, setSourceType ] = useState(SOURCE_USER_TRIGGER);
|
const [ sourceType, setSourceType ] = useState(SOURCE_USER_TRIGGER);
|
||||||
|
const [ targetTile, setTargetTile ] = useState<Tile>({ x: 0, y: 0 });
|
||||||
|
const [ targetPlacementMode, setTargetPlacementMode ] = useState(false);
|
||||||
const [ curX, setCurX ] = useState(0);
|
const [ curX, setCurX ] = useState(0);
|
||||||
const [ curY, setCurY ] = useState(0);
|
const [ curY, setCurY ] = useState(0);
|
||||||
|
|
||||||
const { trigger = null, furniIds = [], setIntParams, setFurniIds } = useWired();
|
const { trigger = null, furniIds = [], setIntParams } = useWired();
|
||||||
|
|
||||||
|
useEffect(() =>
|
||||||
|
{
|
||||||
|
GetRoomEngine().areaSelectionManager.clearHighlight();
|
||||||
|
GetRoomEngine().areaSelectionManager.deactivate();
|
||||||
|
}, []);
|
||||||
|
|
||||||
useEffect(() =>
|
useEffect(() =>
|
||||||
{
|
{
|
||||||
@@ -140,15 +214,17 @@ export const WiredSelectorFurniNeighborhoodView: FC<{}> = () =>
|
|||||||
if(p.length >= 1) setSourceType(p[0]);
|
if(p.length >= 1) setSourceType(p[0]);
|
||||||
if(p.length >= 2) setFilterExisting(p[1] === 1);
|
if(p.length >= 2) setFilterExisting(p[1] === 1);
|
||||||
if(p.length >= 3) setInvert(p[2] === 1);
|
if(p.length >= 3) setInvert(p[2] === 1);
|
||||||
|
if(p.length >= 5) setTargetTile({ x: p[3], y: p[4] });
|
||||||
|
else setTargetTile({ x: 0, y: 0 });
|
||||||
|
|
||||||
if(p.length >= 4)
|
if(p.length >= 6)
|
||||||
{
|
{
|
||||||
const n = p[3];
|
const n = p[5];
|
||||||
const tiles: Tile[] = [];
|
const tiles: Tile[] = [];
|
||||||
|
|
||||||
for(let i = 0; i < n; i++)
|
for(let i = 0; i < n; i++)
|
||||||
{
|
{
|
||||||
const xi = 4 + i * 2;
|
const xi = 6 + i * 2;
|
||||||
if(xi + 1 < p.length) tiles.push({ x: p[xi], y: p[xi + 1] });
|
if(xi + 1 < p.length) tiles.push({ x: p[xi], y: p[xi + 1] });
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -160,71 +236,47 @@ export const WiredSelectorFurniNeighborhoodView: FC<{}> = () =>
|
|||||||
}
|
}
|
||||||
}, [ trigger ]);
|
}, [ trigger ]);
|
||||||
|
|
||||||
useEffect(() =>
|
|
||||||
{
|
|
||||||
if(sourceType !== SOURCE_FURNI_PICKED || !trigger) return;
|
|
||||||
|
|
||||||
const roomId = GetRoomSession().roomId;
|
|
||||||
const wiredObj = GetRoomEngine().getRoomObject(roomId, trigger.id, RoomObjectCategory.FLOOR);
|
|
||||||
|
|
||||||
if(!wiredObj) return;
|
|
||||||
|
|
||||||
const wiredPos = wiredObj.getLocation();
|
|
||||||
const tileSet = new Set(selectedTiles.map(t => `${ t.x },${ t.y }`));
|
|
||||||
const limit = trigger.maximumItemSelectionCount;
|
|
||||||
|
|
||||||
const allFloorObjects = GetRoomEngine().getRoomObjects(roomId, RoomObjectCategory.FLOOR);
|
|
||||||
const newIds: number[] = [];
|
|
||||||
|
|
||||||
for(const obj of allFloorObjects)
|
|
||||||
{
|
|
||||||
if(newIds.length >= limit) break;
|
|
||||||
if(obj.id < 0 || obj.id === trigger.id) continue;
|
|
||||||
|
|
||||||
const pos = obj.getLocation();
|
|
||||||
const relX = Math.round(pos.x - wiredPos.x);
|
|
||||||
const relY = Math.round(pos.y - wiredPos.y);
|
|
||||||
|
|
||||||
const isInTiles = tileSet.has(`${ relX },${ relY }`);
|
|
||||||
|
|
||||||
if(invert ? !isInTiles : isInTiles) newIds.push(obj.id);
|
|
||||||
}
|
|
||||||
|
|
||||||
setFurniIds(prevValue =>
|
|
||||||
{
|
|
||||||
if(prevValue && prevValue.length) WiredSelectionVisualizer.clearSelectionShaderFromFurni(prevValue);
|
|
||||||
|
|
||||||
WiredSelectionVisualizer.applySelectionShaderToFurni(newIds);
|
|
||||||
|
|
||||||
return newIds;
|
|
||||||
});
|
|
||||||
}, [ sourceType, selectedTiles, invert, trigger, setFurniIds ]);
|
|
||||||
|
|
||||||
const save = useCallback(() =>
|
const save = useCallback(() =>
|
||||||
{
|
{
|
||||||
const params: number[] = [
|
const params: number[] = [
|
||||||
sourceType,
|
sourceType,
|
||||||
filterExisting ? 1 : 0,
|
filterExisting ? 1 : 0,
|
||||||
invert ? 1 : 0,
|
invert ? 1 : 0,
|
||||||
|
targetTile.x,
|
||||||
|
targetTile.y,
|
||||||
selectedTiles.length,
|
selectedTiles.length,
|
||||||
...selectedTiles.flatMap(t => [ t.x, t.y ]),
|
...selectedTiles.flatMap(t => [ t.x, t.y ]),
|
||||||
];
|
];
|
||||||
|
|
||||||
setIntParams(params);
|
setIntParams(params);
|
||||||
}, [ sourceType, filterExisting, invert, selectedTiles, setIntParams ]);
|
}, [ sourceType, filterExisting, invert, selectedTiles, targetTile.x, targetTile.y, setIntParams ]);
|
||||||
|
|
||||||
const toggleTile = useCallback((x: number, y: number) =>
|
const setTileSelection = useCallback((x: number, y: number, selected: boolean) =>
|
||||||
{
|
{
|
||||||
setSelectedTiles(prev =>
|
setSelectedTiles(prev =>
|
||||||
tileIncluded(prev, x, y)
|
{
|
||||||
? prev.filter(t => !(t.x === x && t.y === y))
|
const alreadySelected = tileIncluded(prev, x, y);
|
||||||
: [ ...prev, { x, y } ]
|
|
||||||
);
|
if(selected)
|
||||||
|
{
|
||||||
|
if(alreadySelected) return prev;
|
||||||
|
|
||||||
|
return [ ...prev, { x, y } ];
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!alreadySelected) return prev;
|
||||||
|
|
||||||
|
return prev.filter(t => !(t.x === x && t.y === y));
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const moveTargetTile = useCallback((x: number, y: number) =>
|
||||||
|
{
|
||||||
|
setTargetTile({ x, y });
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const addTile = useCallback(() =>
|
const addTile = useCallback(() =>
|
||||||
{
|
{
|
||||||
if(curX === 0 && curY === 0) return;
|
|
||||||
if(!tileIncluded(selectedTiles, curX, curY))
|
if(!tileIncluded(selectedTiles, curX, curY))
|
||||||
setSelectedTiles(prev => [ ...prev, { x: curX, y: curY } ]);
|
setSelectedTiles(prev => [ ...prev, { x: curX, y: curY } ]);
|
||||||
}, [ curX, curY, selectedTiles ]);
|
}, [ curX, curY, selectedTiles ]);
|
||||||
@@ -278,12 +330,23 @@ export const WiredSelectorFurniNeighborhoodView: FC<{}> = () =>
|
|||||||
const pickedLimit = trigger?.maximumItemSelectionCount ?? 20;
|
const pickedLimit = trigger?.maximumItemSelectionCount ?? 20;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<WiredActionBaseView hasSpecialInput={ true } requiresFurni={ requiresFurni } save={ save } hideDelay={ true } cardStyle={ { width: '400px' } }>
|
<WiredSelectorBaseView hasSpecialInput={ true } requiresFurni={ requiresFurni } save={ save } hideDelay={ true } cardStyle={ { width: '400px' } }>
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
|
|
||||||
<Text bold>{ LocalizeText('wiredfurni.params.neighborhood_selection') }</Text>
|
<Text bold>{ LocalizeText('wiredfurni.params.neighborhood_selection') }</Text>
|
||||||
|
|
||||||
<div className="flex items-center gap-1">
|
<div className="flex items-center gap-1">
|
||||||
|
<Button
|
||||||
|
variant={ targetPlacementMode ? 'success' : 'secondary' }
|
||||||
|
className="px-2 py-1"
|
||||||
|
onClick={ () => setTargetPlacementMode(value => !value) }
|
||||||
|
title="Sposta target">
|
||||||
|
<span aria-hidden className="relative inline-block h-[14px] w-[14px]">
|
||||||
|
<span className="absolute left-1/2 top-0 h-full w-px -translate-x-1/2 bg-current" />
|
||||||
|
<span className="absolute left-0 top-1/2 h-px w-full -translate-y-1/2 bg-current" />
|
||||||
|
<span className="absolute left-1/2 top-1/2 h-[8px] w-[8px] -translate-x-1/2 -translate-y-1/2 rounded-full border border-current" />
|
||||||
|
</span>
|
||||||
|
</Button>
|
||||||
<Button variant="success" className="px-2 py-1" onClick={ addTile } title={ LocalizeText('wiredfurni.tooltip.select.tile') }>
|
<Button variant="success" className="px-2 py-1" onClick={ addTile } title={ LocalizeText('wiredfurni.tooltip.select.tile') }>
|
||||||
<FaPlus />
|
<FaPlus />
|
||||||
</Button>
|
</Button>
|
||||||
@@ -299,7 +362,13 @@ export const WiredSelectorFurniNeighborhoodView: FC<{}> = () =>
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex justify-center">
|
<div className="flex justify-center">
|
||||||
<NeighborhoodGrid selectedTiles={ selectedTiles } invert={ invert } onToggle={ toggleTile } />
|
<NeighborhoodGrid
|
||||||
|
selectedTiles={ selectedTiles }
|
||||||
|
targetTile={ targetTile }
|
||||||
|
invert={ invert }
|
||||||
|
onSetTile={ setTileSelection }
|
||||||
|
onMoveTarget={ moveTargetTile }
|
||||||
|
targetPlacementMode={ targetPlacementMode } />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
@@ -378,6 +447,6 @@ export const WiredSelectorFurniNeighborhoodView: FC<{}> = () =>
|
|||||||
</Text> }
|
</Text> }
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</WiredActionBaseView>
|
</WiredSelectorBaseView>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,82 @@
|
|||||||
|
import { FC, useEffect, useState } from 'react';
|
||||||
|
import { LocalizeText, WiredFurniType } from '../../../../api';
|
||||||
|
import { Text } from '../../../../common';
|
||||||
|
import { useWired } from '../../../../hooks';
|
||||||
|
import { WiredSourcesSelector } from '../WiredSourcesSelector';
|
||||||
|
import { WiredSelectorBaseView } from './WiredSelectorBaseView';
|
||||||
|
|
||||||
|
export const WiredSelectorFurniOnFurniView: FC<{}> = () =>
|
||||||
|
{
|
||||||
|
const { trigger = null, setIntParams = null } = useWired();
|
||||||
|
const [ selectionType, setSelectionType ] = useState(0);
|
||||||
|
const [ filterExisting, setFilterExisting ] = useState(false);
|
||||||
|
const [ invert, setInvert ] = useState(false);
|
||||||
|
const [ furniSource, setFurniSource ] = useState<number>(() =>
|
||||||
|
{
|
||||||
|
if(trigger?.intData?.length > 1) return trigger.intData[1];
|
||||||
|
return (trigger?.selectedItems?.length ?? 0) > 0 ? 100 : 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() =>
|
||||||
|
{
|
||||||
|
if(!trigger) return;
|
||||||
|
|
||||||
|
setSelectionType((trigger.intData.length > 0) ? trigger.intData[0] : 0);
|
||||||
|
setFurniSource((trigger.intData.length > 1) ? trigger.intData[1] : ((trigger.selectedItems?.length ?? 0) > 0 ? 100 : 0));
|
||||||
|
setFilterExisting((trigger.intData.length > 2) ? (trigger.intData[2] === 1) : false);
|
||||||
|
setInvert((trigger.intData.length > 3) ? (trigger.intData[3] === 1) : false);
|
||||||
|
}, [ trigger ]);
|
||||||
|
|
||||||
|
const save = () => setIntParams([
|
||||||
|
selectionType,
|
||||||
|
furniSource,
|
||||||
|
filterExisting ? 1 : 0,
|
||||||
|
invert ? 1 : 0
|
||||||
|
]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<WiredSelectorBaseView
|
||||||
|
hasSpecialInput={ true }
|
||||||
|
requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_BY_ID_BY_TYPE_OR_FROM_CONTEXT }
|
||||||
|
save={ save }
|
||||||
|
hideDelay={ true }
|
||||||
|
footer={ <WiredSourcesSelector showFurni={ true } furniSource={ furniSource } onChangeFurni={ setFurniSource } /> }>
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
<div className="flex flex-col gap-1">
|
||||||
|
<Text bold>{ LocalizeText('wiredfurni.params.selection_type') }</Text>
|
||||||
|
{ [ 0, 1, 2, 3 ].map(value =>
|
||||||
|
{
|
||||||
|
return (
|
||||||
|
<label key={ value } className="flex items-center gap-1">
|
||||||
|
<input checked={ (selectionType === value) } className="form-check-input" name="furniOnFurniSelectionType" type="radio" onChange={ () => setSelectionType(value) } />
|
||||||
|
<Text>{ LocalizeText(`wiredfurni.params.onfurni.${ value }`) }</Text>
|
||||||
|
</label>
|
||||||
|
);
|
||||||
|
}) }
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr className="m-0 bg-dark" />
|
||||||
|
|
||||||
|
<Text bold>{ LocalizeText('wiredfurni.params.selector_options_selector') }</Text>
|
||||||
|
|
||||||
|
<label className="flex items-center gap-2">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
className="form-check-input"
|
||||||
|
checked={ filterExisting }
|
||||||
|
onChange={ event => setFilterExisting(event.target.checked) } />
|
||||||
|
<Text small>{ LocalizeText('wiredfurni.params.selector_option.0') }</Text>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label className="flex items-center gap-2">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
className="form-check-input"
|
||||||
|
checked={ invert }
|
||||||
|
onChange={ event => setInvert(event.target.checked) } />
|
||||||
|
<Text small>{ LocalizeText('wiredfurni.params.selector_option.1') }</Text>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</WiredSelectorBaseView>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
import { FC, useCallback, useEffect, useState } from 'react';
|
||||||
|
import { LocalizeText, WiredFurniType } from '../../../../api';
|
||||||
|
import { Text } from '../../../../common';
|
||||||
|
import { useWired } from '../../../../hooks';
|
||||||
|
import { WiredSelectorBaseView } from './WiredSelectorBaseView';
|
||||||
|
|
||||||
|
export const WiredSelectorFurniPicksView: FC<{}> = () =>
|
||||||
|
{
|
||||||
|
const [ filterExisting, setFilterExisting ] = useState(false);
|
||||||
|
const [ invert, setInvert ] = useState(false);
|
||||||
|
const { trigger = null, setIntParams = null } = useWired();
|
||||||
|
|
||||||
|
useEffect(() =>
|
||||||
|
{
|
||||||
|
if(!trigger) return;
|
||||||
|
|
||||||
|
const params = trigger.intData;
|
||||||
|
setFilterExisting(params.length > 0 ? (params[0] === 1) : false);
|
||||||
|
setInvert(params.length > 1 ? (params[1] === 1) : false);
|
||||||
|
}, [ trigger ]);
|
||||||
|
|
||||||
|
const save = useCallback(() =>
|
||||||
|
{
|
||||||
|
setIntParams([
|
||||||
|
filterExisting ? 1 : 0,
|
||||||
|
invert ? 1 : 0
|
||||||
|
]);
|
||||||
|
}, [ filterExisting, invert, setIntParams ]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<WiredSelectorBaseView hasSpecialInput={ true } requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_BY_ID } save={ save } hideDelay={ true } cardStyle={ { width: 400 } }>
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
<Text bold>{ LocalizeText('wiredfurni.params.selector_options_selector') }</Text>
|
||||||
|
|
||||||
|
<label className="flex items-center gap-2">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
className="form-check-input"
|
||||||
|
checked={ filterExisting }
|
||||||
|
onChange={ event => setFilterExisting(event.target.checked) } />
|
||||||
|
<Text small>{ LocalizeText('wiredfurni.params.selector_option.0') }</Text>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label className="flex items-center gap-2">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
className="form-check-input"
|
||||||
|
checked={ invert }
|
||||||
|
onChange={ event => setInvert(event.target.checked) } />
|
||||||
|
<Text small>{ LocalizeText('wiredfurni.params.selector_option.1') }</Text>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</WiredSelectorBaseView>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
import { FC, useCallback, useEffect, useState } from 'react';
|
||||||
|
import { LocalizeText } from '../../../../api';
|
||||||
|
import { Text } from '../../../../common';
|
||||||
|
import { useWired } from '../../../../hooks';
|
||||||
|
import { WiredSelectorBaseView } from './WiredSelectorBaseView';
|
||||||
|
|
||||||
|
export const WiredSelectorFurniSignalView: FC<{}> = () =>
|
||||||
|
{
|
||||||
|
const [ filterExisting, setFilterExisting ] = useState(false);
|
||||||
|
const [ invert, setInvert ] = useState(false);
|
||||||
|
const { trigger = null, setIntParams = null } = useWired();
|
||||||
|
|
||||||
|
useEffect(() =>
|
||||||
|
{
|
||||||
|
if(!trigger) return;
|
||||||
|
|
||||||
|
const params = trigger.intData;
|
||||||
|
setFilterExisting(params.length > 0 ? (params[0] === 1) : false);
|
||||||
|
setInvert(params.length > 1 ? (params[1] === 1) : false);
|
||||||
|
}, [ trigger ]);
|
||||||
|
|
||||||
|
const save = useCallback(() =>
|
||||||
|
{
|
||||||
|
setIntParams([
|
||||||
|
filterExisting ? 1 : 0,
|
||||||
|
invert ? 1 : 0
|
||||||
|
]);
|
||||||
|
}, [ filterExisting, invert, setIntParams ]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<WiredSelectorBaseView hasSpecialInput={ true } requiresFurni={ 0 } save={ save } hideDelay={ true } cardStyle={ { width: 400 } }>
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
<Text bold>{ LocalizeText('wiredfurni.params.selector_options_selector') }</Text>
|
||||||
|
|
||||||
|
<label className="flex items-center gap-2">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
className="form-check-input"
|
||||||
|
checked={ filterExisting }
|
||||||
|
onChange={ event => setFilterExisting(event.target.checked) } />
|
||||||
|
<Text small>{ LocalizeText('wiredfurni.params.selector_option.0') }</Text>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label className="flex items-center gap-2">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
className="form-check-input"
|
||||||
|
checked={ invert }
|
||||||
|
onChange={ event => setInvert(event.target.checked) } />
|
||||||
|
<Text small>{ LocalizeText('wiredfurni.params.selector_option.1') }</Text>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</WiredSelectorBaseView>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -3,7 +3,7 @@ import { FC, useCallback, useEffect, useState } from 'react';
|
|||||||
import { LocalizeText } from '../../../../api';
|
import { LocalizeText } from '../../../../api';
|
||||||
import { Button, Text } from '../../../../common';
|
import { Button, Text } from '../../../../common';
|
||||||
import { useWired } from '../../../../hooks';
|
import { useWired } from '../../../../hooks';
|
||||||
import { WiredActionBaseView } from '../actions/WiredActionBaseView';
|
import { WiredSelectorBaseView } from './WiredSelectorBaseView';
|
||||||
|
|
||||||
export const WiredSelectorUsersAreaView: FC<{}> = props =>
|
export const WiredSelectorUsersAreaView: FC<{}> = props =>
|
||||||
{
|
{
|
||||||
@@ -13,14 +13,12 @@ export const WiredSelectorUsersAreaView: FC<{}> = props =>
|
|||||||
const [ areaHeight, setAreaHeight ] = useState(0);
|
const [ areaHeight, setAreaHeight ] = useState(0);
|
||||||
const [ filterExisting, setFilterExisting ] = useState(false);
|
const [ filterExisting, setFilterExisting ] = useState(false);
|
||||||
const [ invert, setInvert ] = useState(false);
|
const [ invert, setInvert ] = useState(false);
|
||||||
const [ excludeBots, setExcludeBots ] = useState(false);
|
|
||||||
const [ excludePets, setExcludePets ] = useState(false);
|
|
||||||
const { trigger = null, setIntParams } = useWired();
|
const { trigger = null, setIntParams } = useWired();
|
||||||
|
|
||||||
const save = useCallback(() =>
|
const save = useCallback(() =>
|
||||||
{
|
{
|
||||||
setIntParams([ rootX, rootY, areaWidth, areaHeight, filterExisting ? 1 : 0, invert ? 1 : 0, excludeBots ? 1 : 0, excludePets ? 1 : 0 ]);
|
setIntParams([ rootX, rootY, areaWidth, areaHeight, filterExisting ? 1 : 0, invert ? 1 : 0 ]);
|
||||||
}, [ rootX, rootY, areaWidth, areaHeight, filterExisting, invert, excludeBots, excludePets, setIntParams ]);
|
}, [ rootX, rootY, areaWidth, areaHeight, filterExisting, invert, setIntParams ]);
|
||||||
|
|
||||||
useEffect(() =>
|
useEffect(() =>
|
||||||
{
|
{
|
||||||
@@ -76,8 +74,6 @@ export const WiredSelectorUsersAreaView: FC<{}> = props =>
|
|||||||
|
|
||||||
setFilterExisting(trigger.intData.length >= 5 && trigger.intData[4] === 1);
|
setFilterExisting(trigger.intData.length >= 5 && trigger.intData[4] === 1);
|
||||||
setInvert(trigger.intData.length >= 6 && trigger.intData[5] === 1);
|
setInvert(trigger.intData.length >= 6 && trigger.intData[5] === 1);
|
||||||
setExcludeBots(trigger.intData.length >= 7 && trigger.intData[6] === 1);
|
|
||||||
setExcludePets(trigger.intData.length >= 8 && trigger.intData[7] === 1);
|
|
||||||
}, [ trigger ]);
|
}, [ trigger ]);
|
||||||
|
|
||||||
useEffect(() =>
|
useEffect(() =>
|
||||||
@@ -92,7 +88,7 @@ export const WiredSelectorUsersAreaView: FC<{}> = props =>
|
|||||||
const hasArea = areaWidth > 0 && areaHeight > 0;
|
const hasArea = areaWidth > 0 && areaHeight > 0;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<WiredActionBaseView hasSpecialInput={ true } requiresFurni={ 0 } save={ save } hideDelay={ true } cardStyle={ { width: '385px' } }>
|
<WiredSelectorBaseView hasSpecialInput={ true } requiresFurni={ 0 } save={ save } hideDelay={ true } cardStyle={ { width: '385px' } }>
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<Text bold>{ LocalizeText('wiredfurni.params.area_selection') }</Text>
|
<Text bold>{ LocalizeText('wiredfurni.params.area_selection') }</Text>
|
||||||
<Text small>{ LocalizeText('wiredfurni.params.area_selection.info') }</Text>
|
<Text small>{ LocalizeText('wiredfurni.params.area_selection.info') }</Text>
|
||||||
@@ -134,25 +130,7 @@ export const WiredSelectorUsersAreaView: FC<{}> = props =>
|
|||||||
onChange={ e => setInvert(e.target.checked) } />
|
onChange={ e => setInvert(e.target.checked) } />
|
||||||
<Text small>{ LocalizeText('wiredfurni.params.selector_option.1') }</Text>
|
<Text small>{ LocalizeText('wiredfurni.params.selector_option.1') }</Text>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<label className="flex items-center gap-1">
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
className="form-check-input"
|
|
||||||
checked={ excludeBots }
|
|
||||||
onChange={ e => setExcludeBots(e.target.checked) } />
|
|
||||||
<Text small>{ LocalizeText('wiredfurni.params.selector_option.bot') }</Text>
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<label className="flex items-center gap-1">
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
className="form-check-input"
|
|
||||||
checked={ excludePets }
|
|
||||||
onChange={ e => setExcludePets(e.target.checked) } />
|
|
||||||
<Text small>{ LocalizeText('wiredfurni.params.selector_option.pet') }</Text>
|
|
||||||
</label>
|
|
||||||
</div>
|
</div>
|
||||||
</WiredActionBaseView>
|
</WiredSelectorBaseView>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,142 @@
|
|||||||
|
import { FC, useCallback, useEffect, useState } from 'react';
|
||||||
|
import { LocalizeText } from '../../../../api';
|
||||||
|
import { Text } from '../../../../common';
|
||||||
|
import { useWired } from '../../../../hooks';
|
||||||
|
import { WiredSelectorBaseView } from './WiredSelectorBaseView';
|
||||||
|
|
||||||
|
const ACTION_WAVE = 1;
|
||||||
|
const ACTION_BLOW_KISS = 2;
|
||||||
|
const ACTION_LAUGH = 3;
|
||||||
|
const ACTION_AWAKE = 4;
|
||||||
|
const ACTION_RELAX = 5;
|
||||||
|
const ACTION_SIT = 6;
|
||||||
|
const ACTION_STAND = 7;
|
||||||
|
const ACTION_LAY = 8;
|
||||||
|
const ACTION_SIGN = 9;
|
||||||
|
const ACTION_DANCE = 10;
|
||||||
|
const ACTION_THUMB_UP = 11;
|
||||||
|
|
||||||
|
const ACTION_OPTIONS = [
|
||||||
|
{ value: ACTION_WAVE, label: 'widget.memenu.wave' },
|
||||||
|
{ value: ACTION_BLOW_KISS, label: 'widget.memenu.blow' },
|
||||||
|
{ value: ACTION_LAUGH, label: 'widget.memenu.laugh' },
|
||||||
|
{ value: ACTION_THUMB_UP, label: 'widget.memenu.thumb' },
|
||||||
|
{ value: ACTION_AWAKE, label: 'wiredfurni.params.action.4' },
|
||||||
|
{ value: ACTION_RELAX, label: 'avatar.widget.random_walk' },
|
||||||
|
{ value: ACTION_SIT, label: 'widget.memenu.sit' },
|
||||||
|
{ value: ACTION_STAND, label: 'widget.memenu.stand' },
|
||||||
|
{ value: ACTION_LAY, label: 'wiredfurni.params.action.8' },
|
||||||
|
{ value: ACTION_SIGN, label: 'widget.memenu.sign' },
|
||||||
|
{ value: ACTION_DANCE, label: 'widget.memenu.dance' }
|
||||||
|
];
|
||||||
|
|
||||||
|
const SIGN_OPTIONS = Array.from({ length: 18 }, (_, value) => ({
|
||||||
|
value,
|
||||||
|
label: `wiredfurni.params.action.sign.${ value }`
|
||||||
|
}));
|
||||||
|
|
||||||
|
const DANCE_OPTIONS = [
|
||||||
|
{ value: 1, label: 'widget.memenu.dance1' },
|
||||||
|
{ value: 2, label: 'widget.memenu.dance2' },
|
||||||
|
{ value: 3, label: 'widget.memenu.dance3' },
|
||||||
|
{ value: 4, label: 'widget.memenu.dance4' }
|
||||||
|
];
|
||||||
|
|
||||||
|
export const WiredSelectorUsersByActionView: FC<{}> = () =>
|
||||||
|
{
|
||||||
|
const [ selectedAction, setSelectedAction ] = useState(ACTION_WAVE);
|
||||||
|
const [ signFilterEnabled, setSignFilterEnabled ] = useState(false);
|
||||||
|
const [ signId, setSignId ] = useState(0);
|
||||||
|
const [ danceFilterEnabled, setDanceFilterEnabled ] = useState(false);
|
||||||
|
const [ danceId, setDanceId ] = useState(1);
|
||||||
|
const [ filterExisting, setFilterExisting ] = useState(false);
|
||||||
|
const [ invert, setInvert ] = useState(false);
|
||||||
|
const { trigger = null, setIntParams = null } = useWired();
|
||||||
|
|
||||||
|
useEffect(() =>
|
||||||
|
{
|
||||||
|
if(!trigger) return;
|
||||||
|
|
||||||
|
const params = trigger.intData;
|
||||||
|
|
||||||
|
setSelectedAction(params.length > 0 ? params[0] : ACTION_WAVE);
|
||||||
|
setSignFilterEnabled(params.length > 1 ? (params[1] === 1) : false);
|
||||||
|
setSignId(params.length > 2 ? params[2] : 0);
|
||||||
|
setDanceFilterEnabled(params.length > 3 ? (params[3] === 1) : false);
|
||||||
|
setDanceId(params.length > 4 ? params[4] : 1);
|
||||||
|
setFilterExisting(params.length > 5 ? (params[5] === 1) : false);
|
||||||
|
setInvert(params.length > 6 ? (params[6] === 1) : false);
|
||||||
|
}, [ trigger ]);
|
||||||
|
|
||||||
|
const save = useCallback(() =>
|
||||||
|
{
|
||||||
|
setIntParams([
|
||||||
|
selectedAction,
|
||||||
|
signFilterEnabled ? 1 : 0,
|
||||||
|
signId,
|
||||||
|
danceFilterEnabled ? 1 : 0,
|
||||||
|
danceId,
|
||||||
|
filterExisting ? 1 : 0,
|
||||||
|
invert ? 1 : 0
|
||||||
|
]);
|
||||||
|
}, [ selectedAction, signFilterEnabled, signId, danceFilterEnabled, danceId, filterExisting, invert, setIntParams ]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<WiredSelectorBaseView hasSpecialInput={ true } requiresFurni={ 0 } save={ save } hideDelay={ true } cardStyle={ { width: 400 } }>
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
<div className="flex flex-col gap-1">
|
||||||
|
<Text bold>Action</Text>
|
||||||
|
<select className="form-select form-select-sm" value={ selectedAction } onChange={ event => setSelectedAction(parseInt(event.target.value)) }>
|
||||||
|
{ ACTION_OPTIONS.map(option => <option key={ option.value } value={ option.value }>{ LocalizeText(option.label) }</option>) }
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{ (selectedAction === ACTION_SIGN) &&
|
||||||
|
<div className="flex flex-col gap-1">
|
||||||
|
<label className="flex items-center gap-1">
|
||||||
|
<input checked={ signFilterEnabled } className="form-check-input" type="checkbox" onChange={ event => setSignFilterEnabled(event.target.checked) } />
|
||||||
|
<Text>{ LocalizeText('wiredfurni.params.sign_filter') }</Text>
|
||||||
|
</label>
|
||||||
|
{ signFilterEnabled &&
|
||||||
|
<select className="form-select form-select-sm" value={ signId } onChange={ event => setSignId(parseInt(event.target.value)) }>
|
||||||
|
{ SIGN_OPTIONS.map(option => <option key={ option.value } value={ option.value }>{ LocalizeText(option.label) }</option>) }
|
||||||
|
</select> }
|
||||||
|
</div> }
|
||||||
|
|
||||||
|
{ (selectedAction === ACTION_DANCE) &&
|
||||||
|
<div className="flex flex-col gap-1">
|
||||||
|
<label className="flex items-center gap-1">
|
||||||
|
<input checked={ danceFilterEnabled } className="form-check-input" type="checkbox" onChange={ event => setDanceFilterEnabled(event.target.checked) } />
|
||||||
|
<Text>{ LocalizeText('wiredfurni.params.dance_filter') }</Text>
|
||||||
|
</label>
|
||||||
|
{ danceFilterEnabled &&
|
||||||
|
<select className="form-select form-select-sm" value={ danceId } onChange={ event => setDanceId(parseInt(event.target.value)) }>
|
||||||
|
{ DANCE_OPTIONS.map(option => <option key={ option.value } value={ option.value }>{ LocalizeText(option.label) }</option>) }
|
||||||
|
</select> }
|
||||||
|
</div> }
|
||||||
|
|
||||||
|
<hr className="m-0 bg-dark" />
|
||||||
|
|
||||||
|
<Text bold>{ LocalizeText('wiredfurni.params.selector_options_selector') }</Text>
|
||||||
|
|
||||||
|
<label className="flex items-center gap-2">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
className="form-check-input"
|
||||||
|
checked={ filterExisting }
|
||||||
|
onChange={ event => setFilterExisting(event.target.checked) } />
|
||||||
|
<Text small>{ LocalizeText('wiredfurni.params.selector_option.0') }</Text>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label className="flex items-center gap-2">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
className="form-check-input"
|
||||||
|
checked={ invert }
|
||||||
|
onChange={ event => setInvert(event.target.checked) } />
|
||||||
|
<Text small>{ LocalizeText('wiredfurni.params.selector_option.1') }</Text>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</WiredSelectorBaseView>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
import { FC, useCallback, useEffect, useState } from 'react';
|
||||||
|
import { LocalizeText } from '../../../../api';
|
||||||
|
import { Text } from '../../../../common';
|
||||||
|
import { useWired } from '../../../../hooks';
|
||||||
|
import { WiredSelectorBaseView } from './WiredSelectorBaseView';
|
||||||
|
|
||||||
|
export const WiredSelectorUsersByNameView: FC<{}> = () =>
|
||||||
|
{
|
||||||
|
const [ namesText, setNamesText ] = useState('');
|
||||||
|
const [ filterExisting, setFilterExisting ] = useState(false);
|
||||||
|
const [ invert, setInvert ] = useState(false);
|
||||||
|
const { trigger = null, setIntParams = null, setStringParam = null } = useWired();
|
||||||
|
|
||||||
|
useEffect(() =>
|
||||||
|
{
|
||||||
|
if(!trigger) return;
|
||||||
|
|
||||||
|
const params = trigger.intData;
|
||||||
|
|
||||||
|
setNamesText(trigger.stringData || '');
|
||||||
|
setFilterExisting(params.length > 0 ? (params[0] === 1) : false);
|
||||||
|
setInvert(params.length > 1 ? (params[1] === 1) : false);
|
||||||
|
}, [ trigger ]);
|
||||||
|
|
||||||
|
const save = useCallback(() =>
|
||||||
|
{
|
||||||
|
setStringParam(namesText);
|
||||||
|
setIntParams([
|
||||||
|
filterExisting ? 1 : 0,
|
||||||
|
invert ? 1 : 0
|
||||||
|
]);
|
||||||
|
}, [ namesText, filterExisting, invert, setStringParam, setIntParams ]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<WiredSelectorBaseView hasSpecialInput={ true } requiresFurni={ 0 } save={ save } hideDelay={ true } cardStyle={ { width: 400 } }>
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
<div className="flex flex-col gap-1">
|
||||||
|
<Text bold>{ LocalizeText('wiredfurni.params.enter_names') }</Text>
|
||||||
|
<textarea
|
||||||
|
className="form-control form-control-sm min-h-[140px] resize-none"
|
||||||
|
value={ namesText }
|
||||||
|
onChange={ event => setNamesText(event.target.value) } />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr className="m-0 bg-dark" />
|
||||||
|
|
||||||
|
<Text bold>{ LocalizeText('wiredfurni.params.selector_options_selector') }</Text>
|
||||||
|
|
||||||
|
<label className="flex items-center gap-2">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
className="form-check-input"
|
||||||
|
checked={ filterExisting }
|
||||||
|
onChange={ event => setFilterExisting(event.target.checked) } />
|
||||||
|
<Text small>{ LocalizeText('wiredfurni.params.selector_option.0') }</Text>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label className="flex items-center gap-2">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
className="form-check-input"
|
||||||
|
checked={ invert }
|
||||||
|
onChange={ event => setInvert(event.target.checked) } />
|
||||||
|
<Text small>{ LocalizeText('wiredfurni.params.selector_option.1') }</Text>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</WiredSelectorBaseView>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
import { FC, useCallback, useEffect, useState } from 'react';
|
||||||
|
import { LocalizeText } from '../../../../api';
|
||||||
|
import { Text } from '../../../../common';
|
||||||
|
import { useWired } from '../../../../hooks';
|
||||||
|
import { WiredSelectorBaseView } from './WiredSelectorBaseView';
|
||||||
|
|
||||||
|
const USER_TYPES = [ 1, 2, 4 ];
|
||||||
|
|
||||||
|
export const WiredSelectorUsersByTypeView: FC<{}> = () =>
|
||||||
|
{
|
||||||
|
const [ userType, setUserType ] = useState(1);
|
||||||
|
const [ filterExisting, setFilterExisting ] = useState(false);
|
||||||
|
const [ invert, setInvert ] = useState(false);
|
||||||
|
const { trigger = null, setIntParams = null } = useWired();
|
||||||
|
|
||||||
|
useEffect(() =>
|
||||||
|
{
|
||||||
|
if(!trigger) return;
|
||||||
|
|
||||||
|
const params = trigger.intData;
|
||||||
|
|
||||||
|
setUserType(params.length > 0 ? params[0] : 1);
|
||||||
|
setFilterExisting(params.length > 1 ? (params[1] === 1) : false);
|
||||||
|
setInvert(params.length > 2 ? (params[2] === 1) : false);
|
||||||
|
}, [ trigger ]);
|
||||||
|
|
||||||
|
const save = useCallback(() =>
|
||||||
|
{
|
||||||
|
setIntParams([
|
||||||
|
userType,
|
||||||
|
filterExisting ? 1 : 0,
|
||||||
|
invert ? 1 : 0
|
||||||
|
]);
|
||||||
|
}, [ userType, filterExisting, invert, setIntParams ]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<WiredSelectorBaseView hasSpecialInput={ true } requiresFurni={ 0 } save={ save } hideDelay={ true } cardStyle={ { width: 400 } }>
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
<div className="flex flex-col gap-1">
|
||||||
|
<Text bold>{ LocalizeText('wiredfurni.params.usertype') }</Text>
|
||||||
|
{ USER_TYPES.map(value => (
|
||||||
|
<label key={ value } className="flex items-center gap-1">
|
||||||
|
<input checked={ (userType === value) } className="form-check-input" name="usersByTypeSelector" type="radio" onChange={ () => setUserType(value) } />
|
||||||
|
<Text>{ LocalizeText(`wiredfurni.params.usertype.${ value }`) }</Text>
|
||||||
|
</label>
|
||||||
|
)) }
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr className="m-0 bg-dark" />
|
||||||
|
|
||||||
|
<Text bold>{ LocalizeText('wiredfurni.params.selector_options_selector') }</Text>
|
||||||
|
|
||||||
|
<label className="flex items-center gap-2">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
className="form-check-input"
|
||||||
|
checked={ filterExisting }
|
||||||
|
onChange={ event => setFilterExisting(event.target.checked) } />
|
||||||
|
<Text small>{ LocalizeText('wiredfurni.params.selector_option.0') }</Text>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label className="flex items-center gap-2">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
className="form-check-input"
|
||||||
|
checked={ invert }
|
||||||
|
onChange={ event => setInvert(event.target.checked) } />
|
||||||
|
<Text small>{ LocalizeText('wiredfurni.params.selector_option.1') }</Text>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</WiredSelectorBaseView>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -0,0 +1,103 @@
|
|||||||
|
import { CatalogGroupsComposer, GuildMembershipsMessageEvent, HabboGroupEntryData } from '@nitrots/nitro-renderer';
|
||||||
|
import { FC, useEffect, useMemo, useState } from 'react';
|
||||||
|
import { LocalizeText, SendMessageComposer } from '../../../../api';
|
||||||
|
import { Text } from '../../../../common';
|
||||||
|
import { useMessageEvent, useWired } from '../../../../hooks';
|
||||||
|
import { WiredSelectorBaseView } from './WiredSelectorBaseView';
|
||||||
|
|
||||||
|
const GROUP_CURRENT_ROOM = 0;
|
||||||
|
const GROUP_SELECTED = 1;
|
||||||
|
|
||||||
|
export const WiredSelectorUsersGroupView: FC<{}> = () =>
|
||||||
|
{
|
||||||
|
const [ groups, setGroups ] = useState<HabboGroupEntryData[]>([]);
|
||||||
|
const [ groupType, setGroupType ] = useState(GROUP_CURRENT_ROOM);
|
||||||
|
const [ selectedGroupId, setSelectedGroupId ] = useState(0);
|
||||||
|
const [ filterExisting, setFilterExisting ] = useState(false);
|
||||||
|
const [ invert, setInvert ] = useState(false);
|
||||||
|
const { trigger = null, setIntParams = null } = useWired();
|
||||||
|
|
||||||
|
useMessageEvent<GuildMembershipsMessageEvent>(GuildMembershipsMessageEvent, event =>
|
||||||
|
{
|
||||||
|
const parser = event.getParser();
|
||||||
|
|
||||||
|
setGroups(parser.groups || []);
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() =>
|
||||||
|
{
|
||||||
|
SendMessageComposer(new CatalogGroupsComposer());
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() =>
|
||||||
|
{
|
||||||
|
if(!trigger) return;
|
||||||
|
|
||||||
|
const params = trigger.intData;
|
||||||
|
|
||||||
|
setGroupType(params.length > 0 ? params[0] : GROUP_CURRENT_ROOM);
|
||||||
|
setSelectedGroupId(params.length > 1 ? params[1] : 0);
|
||||||
|
setFilterExisting(params.length > 2 ? (params[2] === 1) : false);
|
||||||
|
setInvert(params.length > 3 ? (params[3] === 1) : false);
|
||||||
|
}, [ trigger ]);
|
||||||
|
|
||||||
|
useEffect(() =>
|
||||||
|
{
|
||||||
|
if((groupType !== GROUP_SELECTED) || selectedGroupId || !groups.length) return;
|
||||||
|
|
||||||
|
setSelectedGroupId(groups[0].groupId);
|
||||||
|
}, [ groupType, selectedGroupId, groups ]);
|
||||||
|
|
||||||
|
const selectedGroupOptions = useMemo(() => groups.map(group => ({ value: group.groupId, label: group.groupName })), [ groups ]);
|
||||||
|
|
||||||
|
const save = () => setIntParams([
|
||||||
|
groupType,
|
||||||
|
selectedGroupId,
|
||||||
|
filterExisting ? 1 : 0,
|
||||||
|
invert ? 1 : 0
|
||||||
|
]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<WiredSelectorBaseView hasSpecialInput={ true } requiresFurni={ 0 } save={ save } hideDelay={ true } cardStyle={ { width: 400 } }>
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
<div className="flex flex-col gap-1">
|
||||||
|
<Text bold>{ LocalizeText('wiredfurni.params.groupselection') }</Text>
|
||||||
|
{ [ GROUP_CURRENT_ROOM, GROUP_SELECTED ].map(value => (
|
||||||
|
<label key={ value } className="flex items-center gap-1">
|
||||||
|
<input checked={ (groupType === value) } className="form-check-input" name="usersGroupSelectorType" type="radio" onChange={ () => setGroupType(value) } />
|
||||||
|
<Text>{ LocalizeText(`wiredfurni.params.grouptype.${ value }`) }</Text>
|
||||||
|
</label>
|
||||||
|
)) }
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{ (groupType === GROUP_SELECTED) &&
|
||||||
|
<select className="form-select form-select-sm" value={ selectedGroupId } onChange={ event => setSelectedGroupId(parseInt(event.target.value)) }>
|
||||||
|
{ !selectedGroupOptions.length && <option value={ 0 }>-</option> }
|
||||||
|
{ selectedGroupOptions.map(group => <option key={ group.value } value={ group.value }>{ group.label }</option>) }
|
||||||
|
</select> }
|
||||||
|
|
||||||
|
<hr className="m-0 bg-dark" />
|
||||||
|
|
||||||
|
<Text bold>{ LocalizeText('wiredfurni.params.selector_options_selector') }</Text>
|
||||||
|
|
||||||
|
<label className="flex items-center gap-2">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
className="form-check-input"
|
||||||
|
checked={ filterExisting }
|
||||||
|
onChange={ event => setFilterExisting(event.target.checked) } />
|
||||||
|
<Text small>{ LocalizeText('wiredfurni.params.selector_option.0') }</Text>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label className="flex items-center gap-2">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
className="form-check-input"
|
||||||
|
checked={ invert }
|
||||||
|
onChange={ event => setInvert(event.target.checked) } />
|
||||||
|
<Text small>{ LocalizeText('wiredfurni.params.selector_option.1') }</Text>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</WiredSelectorBaseView>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
import { FC, useCallback, useEffect, useState } from 'react';
|
||||||
|
import { LocalizeText } from '../../../../api';
|
||||||
|
import { Text } from '../../../../common';
|
||||||
|
import { useWired } from '../../../../hooks';
|
||||||
|
import { WiredSelectorBaseView } from './WiredSelectorBaseView';
|
||||||
|
import { WiredHandItemField } from '../WiredHandItemField';
|
||||||
|
|
||||||
|
export const WiredSelectorUsersHandItemView: FC<{}> = () =>
|
||||||
|
{
|
||||||
|
const [ handItemId, setHandItemId ] = useState(0);
|
||||||
|
const [ filterExisting, setFilterExisting ] = useState(false);
|
||||||
|
const [ invert, setInvert ] = useState(false);
|
||||||
|
const { trigger = null, setIntParams = null } = useWired();
|
||||||
|
|
||||||
|
useEffect(() =>
|
||||||
|
{
|
||||||
|
if(!trigger) return;
|
||||||
|
|
||||||
|
const params = trigger.intData;
|
||||||
|
|
||||||
|
setHandItemId(params.length > 0 ? params[0] : 0);
|
||||||
|
setFilterExisting(params.length > 1 ? (params[1] === 1) : false);
|
||||||
|
setInvert(params.length > 2 ? (params[2] === 1) : false);
|
||||||
|
}, [ trigger ]);
|
||||||
|
|
||||||
|
const save = useCallback(() =>
|
||||||
|
{
|
||||||
|
setIntParams([
|
||||||
|
handItemId,
|
||||||
|
filterExisting ? 1 : 0,
|
||||||
|
invert ? 1 : 0
|
||||||
|
]);
|
||||||
|
}, [ handItemId, filterExisting, invert, setIntParams ]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<WiredSelectorBaseView hasSpecialInput={ true } requiresFurni={ 0 } save={ save } hideDelay={ true } cardStyle={ { width: 400 } }>
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
<WiredHandItemField handItemId={ handItemId } onChange={ setHandItemId } showCopyButton={ true } />
|
||||||
|
|
||||||
|
<hr className="m-0 bg-dark" />
|
||||||
|
|
||||||
|
<Text bold>{ LocalizeText('wiredfurni.params.selector_options_selector') }</Text>
|
||||||
|
|
||||||
|
<label className="flex items-center gap-2">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
className="form-check-input"
|
||||||
|
checked={ filterExisting }
|
||||||
|
onChange={ event => setFilterExisting(event.target.checked) } />
|
||||||
|
<Text small>{ LocalizeText('wiredfurni.params.selector_option.0') }</Text>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label className="flex items-center gap-2">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
className="form-check-input"
|
||||||
|
checked={ invert }
|
||||||
|
onChange={ event => setInvert(event.target.checked) } />
|
||||||
|
<Text small>{ LocalizeText('wiredfurni.params.selector_option.1') }</Text>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</WiredSelectorBaseView>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -1,14 +1,18 @@
|
|||||||
import { CSSProperties, FC, useCallback, useEffect, useState } from 'react';
|
import { GetRoomEngine } from '@nitrots/nitro-renderer';
|
||||||
|
import { CSSProperties, FC, MouseEvent as ReactMouseEvent, useCallback, useEffect, useState } from 'react';
|
||||||
import { FaMinus, FaPlus, FaTimes } from 'react-icons/fa';
|
import { FaMinus, FaPlus, FaTimes } from 'react-icons/fa';
|
||||||
import { MdGridOn } from 'react-icons/md';
|
import { MdGridOn } from 'react-icons/md';
|
||||||
import { LocalizeText } from '../../../../api';
|
import { LocalizeText, WiredFurniType } from '../../../../api';
|
||||||
import { Button, Text } from '../../../../common';
|
import { Button, Text } from '../../../../common';
|
||||||
import { useWired } from '../../../../hooks';
|
import { useWired } from '../../../../hooks';
|
||||||
import { WiredActionBaseView } from '../actions/WiredActionBaseView';
|
import { WiredSelectorBaseView } from './WiredSelectorBaseView';
|
||||||
|
|
||||||
const SOURCE_USER_TRIGGER = 0;
|
const SOURCE_USER_TRIGGER = 0;
|
||||||
const SOURCE_USER_SIGNAL = 1;
|
const SOURCE_USER_SIGNAL = 1;
|
||||||
const SOURCE_USER_CLICKED = 2;
|
const SOURCE_USER_CLICKED = 2;
|
||||||
|
const SOURCE_FURNI_TRIGGER = 3;
|
||||||
|
const SOURCE_FURNI_PICKED = 4;
|
||||||
|
const SOURCE_FURNI_SIGNAL = 5;
|
||||||
|
|
||||||
const USER_SOURCES = [
|
const USER_SOURCES = [
|
||||||
{ value: SOURCE_USER_TRIGGER, label: 'wiredfurni.params.sources.users.0' },
|
{ value: SOURCE_USER_TRIGGER, label: 'wiredfurni.params.sources.users.0' },
|
||||||
@@ -16,6 +20,12 @@ const USER_SOURCES = [
|
|||||||
{ value: SOURCE_USER_CLICKED, label: 'wiredfurni.params.sources.users.11' },
|
{ value: SOURCE_USER_CLICKED, label: 'wiredfurni.params.sources.users.11' },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const FURNI_SOURCES = [
|
||||||
|
{ value: SOURCE_FURNI_TRIGGER, label: 'wiredfurni.params.sources.furni.0' },
|
||||||
|
{ value: SOURCE_FURNI_PICKED, label: 'wiredfurni.params.sources.furni.100' },
|
||||||
|
{ value: SOURCE_FURNI_SIGNAL, label: 'wiredfurni.params.sources.furni.201' },
|
||||||
|
];
|
||||||
|
|
||||||
const TILE_W = 22;
|
const TILE_W = 22;
|
||||||
const TILE_H = 11;
|
const TILE_H = 11;
|
||||||
const GRID_RANGE = 4;
|
const GRID_RANGE = 4;
|
||||||
@@ -37,33 +47,74 @@ const tileTop = (rx: number, ry: number) =>
|
|||||||
|
|
||||||
interface GridProps {
|
interface GridProps {
|
||||||
selectedTiles: Tile[];
|
selectedTiles: Tile[];
|
||||||
|
targetTile: Tile;
|
||||||
invert: boolean;
|
invert: boolean;
|
||||||
onToggle: (x: number, y: number) => void;
|
onSetTile: (x: number, y: number, selected: boolean) => void;
|
||||||
|
onMoveTarget: (x: number, y: number) => void;
|
||||||
|
targetPlacementMode: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const NeighborhoodGrid: FC<GridProps> = ({ selectedTiles, invert, onToggle }) =>
|
const NeighborhoodGrid: FC<GridProps> = ({ selectedTiles, targetTile, invert, onSetTile, onMoveTarget, targetPlacementMode }) =>
|
||||||
{
|
{
|
||||||
|
const [ dragMode, setDragMode ] = useState<'add' | 'remove' | 'target' | null>(null);
|
||||||
const tiles: JSX.Element[] = [];
|
const tiles: JSX.Element[] = [];
|
||||||
|
|
||||||
|
useEffect(() =>
|
||||||
|
{
|
||||||
|
const stopDragging = () => setDragMode(null);
|
||||||
|
|
||||||
|
window.addEventListener('mouseup', stopDragging);
|
||||||
|
|
||||||
|
return () => window.removeEventListener('mouseup', stopDragging);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const beginTileDrag = (event: ReactMouseEvent<HTMLDivElement>, rx: number, ry: number, isTarget: boolean, isSelected: boolean) =>
|
||||||
|
{
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
if(targetPlacementMode)
|
||||||
|
{
|
||||||
|
setDragMode('target');
|
||||||
|
onMoveTarget(rx, ry);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const nextMode = isSelected ? 'remove' : 'add';
|
||||||
|
|
||||||
|
setDragMode(nextMode);
|
||||||
|
onSetTile(rx, ry, nextMode === 'add');
|
||||||
|
};
|
||||||
|
|
||||||
|
const continueTileDrag = (event: ReactMouseEvent<HTMLDivElement>, rx: number, ry: number, isTarget: boolean) =>
|
||||||
|
{
|
||||||
|
if(!(event.buttons & 1) || !dragMode) return;
|
||||||
|
|
||||||
|
if(dragMode === 'target')
|
||||||
|
{
|
||||||
|
onMoveTarget(rx, ry);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
onSetTile(rx, ry, dragMode === 'add');
|
||||||
|
};
|
||||||
|
|
||||||
for (let ry = -GRID_RANGE; ry <= GRID_RANGE; ry++)
|
for (let ry = -GRID_RANGE; ry <= GRID_RANGE; ry++)
|
||||||
{
|
{
|
||||||
for (let rx = -GRID_RANGE; rx <= GRID_RANGE; rx++)
|
for (let rx = -GRID_RANGE; rx <= GRID_RANGE; rx++)
|
||||||
{
|
{
|
||||||
const isCenter = rx === 0 && ry === 0;
|
const isTarget = rx === targetTile.x && ry === targetTile.y;
|
||||||
const isSelected = tileIncluded(selectedTiles, rx, ry);
|
const isSelected = tileIncluded(selectedTiles, rx, ry);
|
||||||
const isActive = invert ? !isSelected : isSelected;
|
const isActive = invert ? !isSelected : isSelected;
|
||||||
const left = tileLeft(rx, ry);
|
const left = tileLeft(rx, ry);
|
||||||
const top_ = tileTop(rx, ry);
|
const top_ = tileTop(rx, ry);
|
||||||
const zIdx = rx + ry + GRID_RANGE * 2 + 10;
|
const zIdx = rx + ry + GRID_RANGE * 2 + 10;
|
||||||
|
|
||||||
const bgColor = isCenter
|
const bgColor = isActive
|
||||||
? '#ff9500'
|
? '#3399ff'
|
||||||
: isActive
|
: '#2a3042';
|
||||||
? '#3399ff'
|
|
||||||
: '#2a3042';
|
|
||||||
|
|
||||||
const borderColor = isCenter
|
const borderColor = isTarget
|
||||||
? '#cc6600'
|
? '#ffffff'
|
||||||
: isActive
|
: isActive
|
||||||
? '#1166cc'
|
? '#1166cc'
|
||||||
: '#1a2032';
|
: '#1a2032';
|
||||||
@@ -76,36 +127,61 @@ const NeighborhoodGrid: FC<GridProps> = ({ selectedTiles, invert, onToggle }) =>
|
|||||||
top: top_,
|
top: top_,
|
||||||
clipPath: 'polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%)',
|
clipPath: 'polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%)',
|
||||||
backgroundColor: bgColor,
|
backgroundColor: bgColor,
|
||||||
cursor: isCenter ? 'default' : 'pointer',
|
cursor: 'pointer',
|
||||||
zIndex: zIdx,
|
zIndex: zIdx,
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
color: isTarget ? '#ffffff' : 'transparent',
|
||||||
|
fontSize: 10
|
||||||
};
|
};
|
||||||
|
|
||||||
const border: CSSProperties = {
|
const border: CSSProperties = {
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
width: TILE_W + 2,
|
width: TILE_W + (isTarget ? 6 : 2),
|
||||||
height: TILE_H + 2,
|
height: TILE_H + (isTarget ? 6 : 2),
|
||||||
left: left - 1,
|
left: left - (isTarget ? 3 : 1),
|
||||||
top: top_ - 1,
|
top: top_ - (isTarget ? 3 : 1),
|
||||||
clipPath: 'polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%)',
|
clipPath: 'polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%)',
|
||||||
backgroundColor: borderColor,
|
backgroundColor: borderColor,
|
||||||
zIndex: zIdx - 1,
|
zIndex: zIdx - 1,
|
||||||
pointerEvents: 'none',
|
pointerEvents: 'none',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const targetOutline: CSSProperties = {
|
||||||
|
position: 'absolute',
|
||||||
|
width: TILE_W + 4,
|
||||||
|
height: TILE_H + 4,
|
||||||
|
left: left - 2,
|
||||||
|
top: top_ - 2,
|
||||||
|
zIndex: zIdx + 1,
|
||||||
|
pointerEvents: 'none',
|
||||||
|
overflow: 'visible'
|
||||||
|
};
|
||||||
|
|
||||||
tiles.push(
|
tiles.push(
|
||||||
<div key={ `b-${ rx }-${ ry }` } style={ border } />,
|
<div key={ `b-${ rx }-${ ry }` } style={ border } />,
|
||||||
|
isTarget && (
|
||||||
|
<svg key={ `o-${ rx }-${ ry }` } style={ targetOutline } viewBox={ `0 0 ${ TILE_W + 4 } ${ TILE_H + 4 }` }>
|
||||||
|
<polygon
|
||||||
|
points={ `${ (TILE_W + 4) / 2 },2 ${ TILE_W + 2 },${ (TILE_H + 4) / 2 } ${ (TILE_W + 4) / 2 },${ TILE_H + 2 } 2,${ (TILE_H + 4) / 2 }` }
|
||||||
|
fill="none"
|
||||||
|
stroke="#ffffff"
|
||||||
|
strokeWidth="1" />
|
||||||
|
</svg>
|
||||||
|
),
|
||||||
<div
|
<div
|
||||||
key={ `t-${ rx }-${ ry }` }
|
key={ `t-${ rx }-${ ry }` }
|
||||||
style={ diamond }
|
style={ diamond }
|
||||||
title={ `(${ rx }, ${ ry })` }
|
title={ `(${ rx }, ${ ry })` }
|
||||||
onClick={ () => !isCenter && onToggle(rx, ry) }
|
onMouseDown={ event => beginTileDrag(event, rx, ry, isTarget, isSelected) }
|
||||||
/>,
|
onMouseEnter={ event => continueTileDrag(event, rx, ry, isTarget) } />,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={ { position: 'relative', width: GRID_PX_W, height: GRID_PX_H, flexShrink: 0 } }>
|
<div style={ { position: 'relative', width: GRID_PX_W, height: GRID_PX_H, flexShrink: 0 } } onContextMenu={ event => event.preventDefault() }>
|
||||||
{ tiles }
|
{ tiles }
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@@ -117,19 +193,29 @@ export const WiredSelectorUsersNeighborhoodView: FC<{}> = () =>
|
|||||||
const [ filterExisting, setFilterExisting ] = useState(false);
|
const [ filterExisting, setFilterExisting ] = useState(false);
|
||||||
const [ invert, setInvert ] = useState(false);
|
const [ invert, setInvert ] = useState(false);
|
||||||
const [ sourceType, setSourceType ] = useState(SOURCE_USER_TRIGGER);
|
const [ sourceType, setSourceType ] = useState(SOURCE_USER_TRIGGER);
|
||||||
|
const [ targetTile, setTargetTile ] = useState<Tile>({ x: 0, y: 0 });
|
||||||
|
const [ targetPlacementMode, setTargetPlacementMode ] = useState(false);
|
||||||
const [ curX, setCurX ] = useState(0);
|
const [ curX, setCurX ] = useState(0);
|
||||||
const [ curY, setCurY ] = useState(0);
|
const [ curY, setCurY ] = useState(0);
|
||||||
|
|
||||||
const { trigger = null, setIntParams } = useWired();
|
const { trigger = null, furniIds = [], setIntParams } = useWired();
|
||||||
|
|
||||||
|
useEffect(() =>
|
||||||
|
{
|
||||||
|
GetRoomEngine().areaSelectionManager.clearHighlight();
|
||||||
|
GetRoomEngine().areaSelectionManager.deactivate();
|
||||||
|
}, []);
|
||||||
|
|
||||||
useEffect(() =>
|
useEffect(() =>
|
||||||
{
|
{
|
||||||
if(!trigger) return;
|
if(!trigger) return;
|
||||||
|
|
||||||
const p = trigger.intData;
|
const p = trigger.intData;
|
||||||
if(p.length >= 1) setSourceType(Math.min(p[0], SOURCE_USER_CLICKED));
|
if(p.length >= 1) setSourceType(p[0]);
|
||||||
if(p.length >= 2) setFilterExisting(p[1] === 1);
|
if(p.length >= 2) setFilterExisting(p[1] === 1);
|
||||||
if(p.length >= 3) setInvert(p[2] === 1);
|
if(p.length >= 3) setInvert(p[2] === 1);
|
||||||
|
if(p.length >= 5) setTargetTile({ x: p[3], y: p[4] });
|
||||||
|
else setTargetTile({ x: 0, y: 0 });
|
||||||
|
|
||||||
if(p.length >= 6)
|
if(p.length >= 6)
|
||||||
{
|
{
|
||||||
@@ -156,25 +242,41 @@ export const WiredSelectorUsersNeighborhoodView: FC<{}> = () =>
|
|||||||
sourceType,
|
sourceType,
|
||||||
filterExisting ? 1 : 0,
|
filterExisting ? 1 : 0,
|
||||||
invert ? 1 : 0,
|
invert ? 1 : 0,
|
||||||
|
targetTile.x,
|
||||||
|
targetTile.y,
|
||||||
selectedTiles.length,
|
selectedTiles.length,
|
||||||
...selectedTiles.flatMap(t => [ t.x, t.y ]),
|
...selectedTiles.flatMap(t => [ t.x, t.y ]),
|
||||||
];
|
];
|
||||||
|
|
||||||
setIntParams(params);
|
setIntParams(params);
|
||||||
}, [ sourceType, filterExisting, invert, selectedTiles, setIntParams ]);
|
}, [ sourceType, filterExisting, invert, selectedTiles, targetTile.x, targetTile.y, setIntParams ]);
|
||||||
|
|
||||||
const toggleTile = useCallback((x: number, y: number) =>
|
const setTileSelection = useCallback((x: number, y: number, selected: boolean) =>
|
||||||
{
|
{
|
||||||
setSelectedTiles(prev =>
|
setSelectedTiles(prev =>
|
||||||
tileIncluded(prev, x, y)
|
{
|
||||||
? prev.filter(t => !(t.x === x && t.y === y))
|
const alreadySelected = tileIncluded(prev, x, y);
|
||||||
: [ ...prev, { x, y } ]
|
|
||||||
);
|
if(selected)
|
||||||
|
{
|
||||||
|
if(alreadySelected) return prev;
|
||||||
|
|
||||||
|
return [ ...prev, { x, y } ];
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!alreadySelected) return prev;
|
||||||
|
|
||||||
|
return prev.filter(t => !(t.x === x && t.y === y));
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const moveTargetTile = useCallback((x: number, y: number) =>
|
||||||
|
{
|
||||||
|
setTargetTile({ x, y });
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const addTile = useCallback(() =>
|
const addTile = useCallback(() =>
|
||||||
{
|
{
|
||||||
if(curX === 0 && curY === 0) return;
|
|
||||||
if(!tileIncluded(selectedTiles, curX, curY))
|
if(!tileIncluded(selectedTiles, curX, curY))
|
||||||
setSelectedTiles(prev => [ ...prev, { x: curX, y: curY } ]);
|
setSelectedTiles(prev => [ ...prev, { x: curX, y: curY } ]);
|
||||||
}, [ curX, curY, selectedTiles ]);
|
}, [ curX, curY, selectedTiles ]);
|
||||||
@@ -202,21 +304,50 @@ export const WiredSelectorUsersNeighborhoodView: FC<{}> = () =>
|
|||||||
setSelectedTiles(tiles);
|
setSelectedTiles(tiles);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const sourceIndex = USER_SOURCES.findIndex(s => s.value === sourceType);
|
const isUserGroup = sourceType <= SOURCE_USER_CLICKED;
|
||||||
|
const activeSources = isUserGroup ? USER_SOURCES : FURNI_SOURCES;
|
||||||
|
const groupOffset = isUserGroup ? 0 : SOURCE_FURNI_TRIGGER;
|
||||||
|
const groupIndex = sourceType - groupOffset;
|
||||||
|
|
||||||
const prevSource = () =>
|
const prevSource = () =>
|
||||||
setSourceType(USER_SOURCES[(sourceIndex - 1 + USER_SOURCES.length) % USER_SOURCES.length].value);
|
setSourceType(groupOffset + ((groupIndex - 1 + activeSources.length) % activeSources.length));
|
||||||
|
|
||||||
const nextSource = () =>
|
const nextSource = () =>
|
||||||
setSourceType(USER_SOURCES[(sourceIndex + 1) % USER_SOURCES.length].value);
|
setSourceType(groupOffset + ((groupIndex + 1) % activeSources.length));
|
||||||
|
|
||||||
|
const switchGroup = (toUser: boolean) =>
|
||||||
|
{
|
||||||
|
if(toUser === isUserGroup) return;
|
||||||
|
|
||||||
|
const newOffset = toUser ? 0 : SOURCE_FURNI_TRIGGER;
|
||||||
|
setSourceType(newOffset + groupIndex);
|
||||||
|
};
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<WiredActionBaseView hasSpecialInput={ true } requiresFurni={ 0 } save={ save } hideDelay={ true } cardStyle={ { width: '400px' } }>
|
<WiredSelectorBaseView hasSpecialInput={ true } requiresFurni={ requiresFurni } save={ save } hideDelay={ true } cardStyle={ { width: '400px' } }>
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
|
|
||||||
<Text bold>{ LocalizeText('wiredfurni.params.neighborhood_selection') }</Text>
|
<Text bold>{ LocalizeText('wiredfurni.params.neighborhood_selection') }</Text>
|
||||||
|
|
||||||
<div className="flex items-center gap-1">
|
<div className="flex items-center gap-1">
|
||||||
|
<Button
|
||||||
|
variant={ targetPlacementMode ? 'success' : 'secondary' }
|
||||||
|
className="px-2 py-1"
|
||||||
|
onClick={ () => setTargetPlacementMode(value => !value) }
|
||||||
|
title="Sposta target">
|
||||||
|
<span aria-hidden className="relative inline-block h-[14px] w-[14px]">
|
||||||
|
<span className="absolute left-1/2 top-0 h-full w-px -translate-x-1/2 bg-current" />
|
||||||
|
<span className="absolute left-0 top-1/2 h-px w-full -translate-y-1/2 bg-current" />
|
||||||
|
<span className="absolute left-1/2 top-1/2 h-[8px] w-[8px] -translate-x-1/2 -translate-y-1/2 rounded-full border border-current" />
|
||||||
|
</span>
|
||||||
|
</Button>
|
||||||
<Button variant="success" className="px-2 py-1" onClick={ addTile } title={ LocalizeText('wiredfurni.tooltip.select.tile') }>
|
<Button variant="success" className="px-2 py-1" onClick={ addTile } title={ LocalizeText('wiredfurni.tooltip.select.tile') }>
|
||||||
<FaPlus />
|
<FaPlus />
|
||||||
</Button>
|
</Button>
|
||||||
@@ -232,7 +363,13 @@ export const WiredSelectorUsersNeighborhoodView: FC<{}> = () =>
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex justify-center">
|
<div className="flex justify-center">
|
||||||
<NeighborhoodGrid selectedTiles={ selectedTiles } invert={ invert } onToggle={ toggleTile } />
|
<NeighborhoodGrid
|
||||||
|
selectedTiles={ selectedTiles }
|
||||||
|
targetTile={ targetTile }
|
||||||
|
invert={ invert }
|
||||||
|
onSetTile={ setTileSelection }
|
||||||
|
onMoveTarget={ moveTargetTile }
|
||||||
|
targetPlacementMode={ targetPlacementMode } />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
@@ -282,15 +419,35 @@ export const WiredSelectorUsersNeighborhoodView: FC<{}> = () =>
|
|||||||
|
|
||||||
<Text bold>{ LocalizeText('wiredfurni.params.sources.merged.title.neighborhood') }</Text>
|
<Text bold>{ LocalizeText('wiredfurni.params.sources.merged.title.neighborhood') }</Text>
|
||||||
|
|
||||||
|
<div className="flex gap-1">
|
||||||
|
<Button
|
||||||
|
fullWidth
|
||||||
|
variant={ isUserGroup ? 'primary' : 'secondary' }
|
||||||
|
onClick={ () => switchGroup(true) }>
|
||||||
|
{ LocalizeText('wiredfurni.params.furni_neighborhood.group.user') }
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
fullWidth
|
||||||
|
variant={ !isUserGroup ? 'primary' : 'secondary' }
|
||||||
|
onClick={ () => switchGroup(false) }>
|
||||||
|
{ LocalizeText('wiredfurni.params.furni_neighborhood.group.furni') }
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<Button variant="primary" className="px-2 py-1" onClick={ prevSource }>‹</Button>
|
<Button variant="primary" className="px-2 py-1" onClick={ prevSource }>‹</Button>
|
||||||
<div className="flex flex-1 items-center justify-center">
|
<div className="flex flex-1 items-center justify-center">
|
||||||
<Text small>{ LocalizeText(USER_SOURCES[sourceIndex].label) }</Text>
|
<Text small>{ LocalizeText(activeSources[groupIndex].label) }</Text>
|
||||||
</div>
|
</div>
|
||||||
<Button variant="primary" className="px-2 py-1" onClick={ nextSource }>›</Button>
|
<Button variant="primary" className="px-2 py-1" onClick={ nextSource }>›</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{ sourceType === SOURCE_FURNI_PICKED &&
|
||||||
|
<Text small className="text-center">
|
||||||
|
{ LocalizeText('wiredfurni.pickfurnis.caption', [ 'count', 'limit' ], [ pickedCount.toString(), pickedLimit.toString() ]) }
|
||||||
|
</Text> }
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</WiredActionBaseView>
|
</WiredSelectorBaseView>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,64 @@
|
|||||||
|
import { FC, useEffect, useState } from 'react';
|
||||||
|
import { LocalizeText, WiredFurniType } from '../../../../api';
|
||||||
|
import { Text } from '../../../../common';
|
||||||
|
import { useWired } from '../../../../hooks';
|
||||||
|
import { WiredSourcesSelector } from '../WiredSourcesSelector';
|
||||||
|
import { WiredSelectorBaseView } from './WiredSelectorBaseView';
|
||||||
|
|
||||||
|
export const WiredSelectorUsersOnFurniView: FC<{}> = () =>
|
||||||
|
{
|
||||||
|
const { trigger = null, setIntParams = null } = useWired();
|
||||||
|
const [ filterExisting, setFilterExisting ] = useState(false);
|
||||||
|
const [ invert, setInvert ] = useState(false);
|
||||||
|
const [ furniSource, setFurniSource ] = useState<number>(() =>
|
||||||
|
{
|
||||||
|
if(trigger?.intData?.length > 0) return trigger.intData[0];
|
||||||
|
return (trigger?.selectedItems?.length ?? 0) > 0 ? 100 : 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() =>
|
||||||
|
{
|
||||||
|
if(!trigger) return;
|
||||||
|
|
||||||
|
setFurniSource((trigger.intData.length > 0) ? trigger.intData[0] : ((trigger.selectedItems?.length ?? 0) > 0 ? 100 : 0));
|
||||||
|
setFilterExisting((trigger.intData.length > 1) ? (trigger.intData[1] === 1) : false);
|
||||||
|
setInvert((trigger.intData.length > 2) ? (trigger.intData[2] === 1) : false);
|
||||||
|
}, [ trigger ]);
|
||||||
|
|
||||||
|
const save = () => setIntParams([
|
||||||
|
furniSource,
|
||||||
|
filterExisting ? 1 : 0,
|
||||||
|
invert ? 1 : 0
|
||||||
|
]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<WiredSelectorBaseView
|
||||||
|
hasSpecialInput={ true }
|
||||||
|
requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_BY_ID_BY_TYPE_OR_FROM_CONTEXT }
|
||||||
|
save={ save }
|
||||||
|
hideDelay={ true }
|
||||||
|
footer={ <WiredSourcesSelector showFurni={ true } furniSource={ furniSource } onChangeFurni={ setFurniSource } /> }>
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
<Text bold>{ LocalizeText('wiredfurni.params.selector_options_selector') }</Text>
|
||||||
|
|
||||||
|
<label className="flex items-center gap-2">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
className="form-check-input"
|
||||||
|
checked={ filterExisting }
|
||||||
|
onChange={ event => setFilterExisting(event.target.checked) } />
|
||||||
|
<Text small>{ LocalizeText('wiredfurni.params.selector_option.0') }</Text>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label className="flex items-center gap-2">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
className="form-check-input"
|
||||||
|
checked={ invert }
|
||||||
|
onChange={ event => setInvert(event.target.checked) } />
|
||||||
|
<Text small>{ LocalizeText('wiredfurni.params.selector_option.1') }</Text>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</WiredSelectorBaseView>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
import { FC, useCallback, useEffect, useState } from 'react';
|
||||||
|
import { LocalizeText } from '../../../../api';
|
||||||
|
import { Text } from '../../../../common';
|
||||||
|
import { useWired } from '../../../../hooks';
|
||||||
|
import { WiredSelectorBaseView } from './WiredSelectorBaseView';
|
||||||
|
|
||||||
|
export const WiredSelectorUsersSignalView: FC<{}> = () =>
|
||||||
|
{
|
||||||
|
const [ filterExisting, setFilterExisting ] = useState(false);
|
||||||
|
const [ invert, setInvert ] = useState(false);
|
||||||
|
const { trigger = null, setIntParams = null } = useWired();
|
||||||
|
|
||||||
|
useEffect(() =>
|
||||||
|
{
|
||||||
|
if(!trigger) return;
|
||||||
|
|
||||||
|
const params = trigger.intData;
|
||||||
|
setFilterExisting(params.length > 0 ? (params[0] === 1) : false);
|
||||||
|
setInvert(params.length > 1 ? (params[1] === 1) : false);
|
||||||
|
}, [ trigger ]);
|
||||||
|
|
||||||
|
const save = useCallback(() =>
|
||||||
|
{
|
||||||
|
setIntParams([
|
||||||
|
filterExisting ? 1 : 0,
|
||||||
|
invert ? 1 : 0
|
||||||
|
]);
|
||||||
|
}, [ filterExisting, invert, setIntParams ]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<WiredSelectorBaseView hasSpecialInput={ true } requiresFurni={ 0 } save={ save } hideDelay={ true } cardStyle={ { width: 400 } }>
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
<Text bold>{ LocalizeText('wiredfurni.params.selector_options_selector') }</Text>
|
||||||
|
|
||||||
|
<label className="flex items-center gap-2">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
className="form-check-input"
|
||||||
|
checked={ filterExisting }
|
||||||
|
onChange={ event => setFilterExisting(event.target.checked) } />
|
||||||
|
<Text small>{ LocalizeText('wiredfurni.params.selector_option.0') }</Text>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label className="flex items-center gap-2">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
className="form-check-input"
|
||||||
|
checked={ invert }
|
||||||
|
onChange={ event => setInvert(event.target.checked) } />
|
||||||
|
<Text small>{ LocalizeText('wiredfurni.params.selector_option.1') }</Text>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</WiredSelectorBaseView>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
import { FC, useCallback, useEffect, useState } from 'react';
|
||||||
|
import { LocalizeText } from '../../../../api';
|
||||||
|
import { Text } from '../../../../common';
|
||||||
|
import { useWired } from '../../../../hooks';
|
||||||
|
import { WiredSelectorBaseView } from './WiredSelectorBaseView';
|
||||||
|
|
||||||
|
const TEAM_TYPES = [ 0, 1, 2, 3, 4 ];
|
||||||
|
|
||||||
|
export const WiredSelectorUsersTeamView: FC<{}> = () =>
|
||||||
|
{
|
||||||
|
const [ teamType, setTeamType ] = useState(0);
|
||||||
|
const [ filterExisting, setFilterExisting ] = useState(false);
|
||||||
|
const [ invert, setInvert ] = useState(false);
|
||||||
|
const { trigger = null, setIntParams = null } = useWired();
|
||||||
|
|
||||||
|
useEffect(() =>
|
||||||
|
{
|
||||||
|
if(!trigger) return;
|
||||||
|
|
||||||
|
const params = trigger.intData;
|
||||||
|
|
||||||
|
setTeamType(params.length > 0 ? params[0] : 0);
|
||||||
|
setFilterExisting(params.length > 1 ? (params[1] === 1) : false);
|
||||||
|
setInvert(params.length > 2 ? (params[2] === 1) : false);
|
||||||
|
}, [ trigger ]);
|
||||||
|
|
||||||
|
const save = useCallback(() =>
|
||||||
|
{
|
||||||
|
setIntParams([
|
||||||
|
teamType,
|
||||||
|
filterExisting ? 1 : 0,
|
||||||
|
invert ? 1 : 0
|
||||||
|
]);
|
||||||
|
}, [ teamType, filterExisting, invert, setIntParams ]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<WiredSelectorBaseView hasSpecialInput={ true } requiresFurni={ 0 } save={ save } hideDelay={ true } cardStyle={ { width: 400 } }>
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
<div className="flex flex-col gap-1">
|
||||||
|
<Text bold>{ LocalizeText('wiredfurni.params.team') }</Text>
|
||||||
|
{ TEAM_TYPES.map(value => (
|
||||||
|
<label key={ value } className="flex items-center gap-1">
|
||||||
|
<input checked={ (teamType === value) } className="form-check-input" name="usersTeamSelector" type="radio" onChange={ () => setTeamType(value) } />
|
||||||
|
<Text>{ LocalizeText((value === 0) ? 'wiredfurni.params.team.any' : `wiredfurni.params.team.${ value }`) }</Text>
|
||||||
|
</label>
|
||||||
|
)) }
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr className="m-0 bg-dark" />
|
||||||
|
|
||||||
|
<Text bold>{ LocalizeText('wiredfurni.params.selector_options_selector') }</Text>
|
||||||
|
|
||||||
|
<label className="flex items-center gap-2">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
className="form-check-input"
|
||||||
|
checked={ filterExisting }
|
||||||
|
onChange={ event => setFilterExisting(event.target.checked) } />
|
||||||
|
<Text small>{ LocalizeText('wiredfurni.params.selector_option.0') }</Text>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label className="flex items-center gap-2">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
className="form-check-input"
|
||||||
|
checked={ invert }
|
||||||
|
onChange={ event => setInvert(event.target.checked) } />
|
||||||
|
<Text small>{ LocalizeText('wiredfurni.params.selector_option.1') }</Text>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</WiredSelectorBaseView>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -1,4 +1,3 @@
|
|||||||
import { GetSessionDataManager } from '@nitrots/nitro-renderer';
|
|
||||||
import { FC, useEffect, useState } from 'react';
|
import { FC, useEffect, useState } from 'react';
|
||||||
import { LocalizeText, WiredFurniType } from '../../../../api';
|
import { LocalizeText, WiredFurniType } from '../../../../api';
|
||||||
import { Text } from '../../../../common';
|
import { Text } from '../../../../common';
|
||||||
@@ -6,22 +5,34 @@ import { useWired } from '../../../../hooks';
|
|||||||
import { NitroInput } from '../../../../layout';
|
import { NitroInput } from '../../../../layout';
|
||||||
import { WiredTriggerBaseView } from './WiredTriggerBaseView';
|
import { WiredTriggerBaseView } from './WiredTriggerBaseView';
|
||||||
|
|
||||||
export const WiredTriggerAvatarSaysSomethingView: FC<{}> = props =>
|
const MATCH_CONTAINS = 0;
|
||||||
|
const MATCH_EXACT = 1;
|
||||||
|
const MATCH_ALL = 2;
|
||||||
|
|
||||||
|
export const WiredTriggerAvatarSaysSomethingView: FC<{}> = () =>
|
||||||
{
|
{
|
||||||
const [ message, setMessage ] = useState('');
|
const [ message, setMessage ] = useState('');
|
||||||
const [ triggererAvatar, setTriggererAvatar ] = useState(-1);
|
const [ matchMode, setMatchMode ] = useState(MATCH_CONTAINS);
|
||||||
|
const [ hideMessage, setHideMessage ] = useState(false);
|
||||||
|
const [ ownerOnly, setOwnerOnly ] = useState(false);
|
||||||
const { trigger = null, setStringParam = null, setIntParams = null } = useWired();
|
const { trigger = null, setStringParam = null, setIntParams = null } = useWired();
|
||||||
|
|
||||||
const save = () =>
|
const save = () =>
|
||||||
{
|
{
|
||||||
setStringParam(message);
|
setStringParam(message);
|
||||||
setIntParams([ triggererAvatar ]);
|
setIntParams([
|
||||||
|
matchMode,
|
||||||
|
hideMessage ? 1 : 0,
|
||||||
|
ownerOnly ? 1 : 0
|
||||||
|
]);
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() =>
|
useEffect(() =>
|
||||||
{
|
{
|
||||||
setMessage(trigger.stringData);
|
setMessage(trigger?.stringData ?? '');
|
||||||
setTriggererAvatar((trigger.intData.length > 0) ? trigger.intData[0] : 0);
|
setMatchMode((trigger?.intData?.length > 0) ? trigger.intData[0] : MATCH_CONTAINS);
|
||||||
|
setHideMessage((trigger?.intData?.length > 1) ? (trigger.intData[1] === 1) : false);
|
||||||
|
setOwnerOnly((trigger?.intData?.length > 2) ? (trigger.intData[2] === 1) : false);
|
||||||
}, [ trigger ]);
|
}, [ trigger ]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -31,14 +42,27 @@ export const WiredTriggerAvatarSaysSomethingView: FC<{}> = props =>
|
|||||||
<NitroInput type="text" value={ message } onChange={ event => setMessage(event.target.value) } />
|
<NitroInput type="text" value={ message } onChange={ event => setMessage(event.target.value) } />
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col gap-1">
|
<div className="flex flex-col gap-1">
|
||||||
<Text bold>{ LocalizeText('wiredfurni.params.picktriggerer') }</Text>
|
|
||||||
<div className="flex items-center gap-1">
|
<div className="flex items-center gap-1">
|
||||||
<input checked={ (triggererAvatar === 0) } className="form-check-input" id="triggererAvatar0" name="triggererAvatar" type="radio" onChange={ event => setTriggererAvatar(0) } />
|
<input checked={ (matchMode === MATCH_CONTAINS) } className="form-check-input" id="sayMatchContains" name="sayMatchMode" type="radio" onChange={ () => setMatchMode(MATCH_CONTAINS) } />
|
||||||
<Text>{ LocalizeText('wiredfurni.params.anyavatar') }</Text>
|
<Text>{ LocalizeText('wiredfurni.params.chatcontains') }</Text>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-1">
|
<div className="flex items-center gap-1">
|
||||||
<input checked={ (triggererAvatar === 1) } className="form-check-input" id="triggererAvatar1" name="triggererAvatar" type="radio" onChange={ event => setTriggererAvatar(1) } />
|
<input checked={ (matchMode === MATCH_EXACT) } className="form-check-input" id="sayMatchExact" name="sayMatchMode" type="radio" onChange={ () => setMatchMode(MATCH_EXACT) } />
|
||||||
<Text>{ GetSessionDataManager().userName }</Text>
|
<Text>{ LocalizeText('wiredfurni.params.exactmatch') }</Text>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-1">
|
||||||
|
<input checked={ (matchMode === MATCH_ALL) } className="form-check-input" id="sayMatchAll" name="sayMatchMode" type="radio" onChange={ () => setMatchMode(MATCH_ALL) } />
|
||||||
|
<Text>{ LocalizeText('wiredfurni.params.allmatch') }</Text>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col gap-1">
|
||||||
|
<div className="flex items-center gap-1">
|
||||||
|
<input checked={ hideMessage } className="form-check-input" id="sayHideMessage" type="checkbox" onChange={ event => setHideMessage(event.target.checked) } />
|
||||||
|
<Text>{ LocalizeText('wiredfurni.params.chat.hide') }</Text>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-1">
|
||||||
|
<input checked={ ownerOnly } className="form-check-input" id="sayOwnerOnly" type="checkbox" onChange={ event => setOwnerOnly(event.target.checked) } />
|
||||||
|
<Text>{ LocalizeText('wiredfurni.params.chat.onlyowner') }</Text>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</WiredTriggerBaseView>
|
</WiredTriggerBaseView>
|
||||||
|
|||||||
@@ -1,8 +1,33 @@
|
|||||||
import { FC } from 'react';
|
import { FC, useEffect, useState } from 'react';
|
||||||
import { WiredFurniType } from '../../../../api';
|
import { WiredFurniType } from '../../../../api';
|
||||||
|
import { useWired } from '../../../../hooks';
|
||||||
|
import { WiredSourceOption, WiredSourcesSelector } from '../WiredSourcesSelector';
|
||||||
import { WiredTriggerBaseView } from './WiredTriggerBaseView';
|
import { WiredTriggerBaseView } from './WiredTriggerBaseView';
|
||||||
|
|
||||||
export const WiredTriggerAvatarWalksOffFurniView: FC<{}> = props =>
|
const FURNI_SOURCE_OPTIONS: WiredSourceOption[] = [
|
||||||
|
{ value: 100, label: 'wiredfurni.params.sources.furni.100' },
|
||||||
|
{ value: 200, label: 'wiredfurni.params.sources.furni.200' }
|
||||||
|
];
|
||||||
|
|
||||||
|
const normalizeFurniSource = (value: number) => (FURNI_SOURCE_OPTIONS.some(option => (option.value === value)) ? value : 100);
|
||||||
|
|
||||||
|
export const WiredTriggerAvatarWalksOffFurniView: FC<{}> = () =>
|
||||||
{
|
{
|
||||||
return <WiredTriggerBaseView hasSpecialInput={ false } requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_BY_ID_OR_BY_TYPE } save={ null } />;
|
const { trigger = null, setIntParams = null } = useWired();
|
||||||
|
const [ furniSource, setFurniSource ] = useState(100);
|
||||||
|
|
||||||
|
const save = () => setIntParams([ furniSource ]);
|
||||||
|
|
||||||
|
useEffect(() =>
|
||||||
|
{
|
||||||
|
setFurniSource((trigger?.intData?.length > 0) ? normalizeFurniSource(trigger.intData[0]) : 100);
|
||||||
|
}, [ trigger ]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<WiredTriggerBaseView
|
||||||
|
hasSpecialInput={ false }
|
||||||
|
requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_BY_ID_BY_TYPE_OR_FROM_CONTEXT }
|
||||||
|
save={ save }
|
||||||
|
footer={ <WiredSourcesSelector showFurni={ true } furniSource={ furniSource } furniSources={ FURNI_SOURCE_OPTIONS } onChangeFurni={ setFurniSource } /> } />
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,8 +1,33 @@
|
|||||||
import { FC } from 'react';
|
import { FC, useEffect, useState } from 'react';
|
||||||
import { WiredFurniType } from '../../../../api';
|
import { WiredFurniType } from '../../../../api';
|
||||||
|
import { useWired } from '../../../../hooks';
|
||||||
|
import { WiredSourceOption, WiredSourcesSelector } from '../WiredSourcesSelector';
|
||||||
import { WiredTriggerBaseView } from './WiredTriggerBaseView';
|
import { WiredTriggerBaseView } from './WiredTriggerBaseView';
|
||||||
|
|
||||||
export const WiredTriggerAvatarWalksOnFurniView: FC<{}> = props =>
|
const FURNI_SOURCE_OPTIONS: WiredSourceOption[] = [
|
||||||
|
{ value: 100, label: 'wiredfurni.params.sources.furni.100' },
|
||||||
|
{ value: 200, label: 'wiredfurni.params.sources.furni.200' }
|
||||||
|
];
|
||||||
|
|
||||||
|
const normalizeFurniSource = (value: number) => (FURNI_SOURCE_OPTIONS.some(option => (option.value === value)) ? value : 100);
|
||||||
|
|
||||||
|
export const WiredTriggerAvatarWalksOnFurniView: FC<{}> = () =>
|
||||||
{
|
{
|
||||||
return <WiredTriggerBaseView hasSpecialInput={ false } requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_BY_ID_OR_BY_TYPE } save={ null } />;
|
const { trigger = null, setIntParams = null } = useWired();
|
||||||
|
const [ furniSource, setFurniSource ] = useState(100);
|
||||||
|
|
||||||
|
const save = () => setIntParams([ furniSource ]);
|
||||||
|
|
||||||
|
useEffect(() =>
|
||||||
|
{
|
||||||
|
setFurniSource((trigger?.intData?.length > 0) ? normalizeFurniSource(trigger.intData[0]) : 100);
|
||||||
|
}, [ trigger ]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<WiredTriggerBaseView
|
||||||
|
hasSpecialInput={ false }
|
||||||
|
requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_BY_ID_BY_TYPE_OR_FROM_CONTEXT }
|
||||||
|
save={ save }
|
||||||
|
footer={ <WiredSourcesSelector showFurni={ true } furniSource={ furniSource } furniSources={ FURNI_SOURCE_OPTIONS } onChangeFurni={ setFurniSource } /> } />
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,26 +3,52 @@ import { LocalizeText, WiredFurniType } from '../../../../api';
|
|||||||
import { Text } from '../../../../common';
|
import { Text } from '../../../../common';
|
||||||
import { useWired } from '../../../../hooks';
|
import { useWired } from '../../../../hooks';
|
||||||
import { NitroInput } from '../../../../layout';
|
import { NitroInput } from '../../../../layout';
|
||||||
|
import { WiredSourceOption, WiredSourcesSelector } from '../WiredSourcesSelector';
|
||||||
import { WiredTriggerBaseView } from './WiredTriggerBaseView';
|
import { WiredTriggerBaseView } from './WiredTriggerBaseView';
|
||||||
|
|
||||||
|
const BOT_SOURCE_OPTIONS: WiredSourceOption[] = [
|
||||||
|
{ value: 100, label: 'wiredfurni.params.sources.users.100' },
|
||||||
|
{ value: 200, label: 'wiredfurni.params.sources.users.200' }
|
||||||
|
];
|
||||||
|
|
||||||
|
const normalizeBotSource = (value: number) => (BOT_SOURCE_OPTIONS.some(option => (option.value === value)) ? value : 100);
|
||||||
|
|
||||||
export const WiredTriggerBotReachedAvatarView: FC<{}> = props =>
|
export const WiredTriggerBotReachedAvatarView: FC<{}> = props =>
|
||||||
{
|
{
|
||||||
const [ botName, setBotName ] = useState('');
|
const [ botName, setBotName ] = useState('');
|
||||||
const { trigger = null, setStringParam = null } = useWired();
|
const [ botSource, setBotSource ] = useState(100);
|
||||||
|
const { trigger = null, setStringParam = null, setIntParams = null } = useWired();
|
||||||
|
|
||||||
const save = () => setStringParam(botName);
|
const save = () =>
|
||||||
|
{
|
||||||
|
setStringParam((botSource === 100) ? botName : '');
|
||||||
|
setIntParams([ botSource ]);
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() =>
|
useEffect(() =>
|
||||||
{
|
{
|
||||||
setBotName(trigger.stringData);
|
setBotName(trigger.stringData);
|
||||||
|
setBotSource((trigger?.intData?.length > 0) ? normalizeBotSource(trigger.intData[0]) : 100);
|
||||||
}, [ trigger ]);
|
}, [ trigger ]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<WiredTriggerBaseView hasSpecialInput={ true } requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE } save={ save }>
|
<WiredTriggerBaseView
|
||||||
<div className="flex flex-col gap-1">
|
hasSpecialInput={ true }
|
||||||
<Text bold>{ LocalizeText('wiredfurni.params.bot.name') }</Text>
|
requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE }
|
||||||
<NitroInput maxLength={ 32 } type="text" value={ botName } onChange={ event => setBotName(event.target.value) } />
|
save={ save }
|
||||||
</div>
|
footer={
|
||||||
|
<WiredSourcesSelector
|
||||||
|
showUsers={ true }
|
||||||
|
userSource={ botSource }
|
||||||
|
userSources={ BOT_SOURCE_OPTIONS }
|
||||||
|
usersTitle="wiredfurni.params.sources.users.title.bots"
|
||||||
|
onChangeUsers={ setBotSource } />
|
||||||
|
}>
|
||||||
|
{ (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> }
|
||||||
</WiredTriggerBaseView>
|
</WiredTriggerBaseView>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,26 +3,64 @@ import { LocalizeText, WiredFurniType } from '../../../../api';
|
|||||||
import { Text } from '../../../../common';
|
import { Text } from '../../../../common';
|
||||||
import { useWired } from '../../../../hooks';
|
import { useWired } from '../../../../hooks';
|
||||||
import { NitroInput } from '../../../../layout';
|
import { NitroInput } from '../../../../layout';
|
||||||
|
import { WiredSourceOption, WiredSourcesSelector } from '../WiredSourcesSelector';
|
||||||
import { WiredTriggerBaseView } from './WiredTriggerBaseView';
|
import { WiredTriggerBaseView } from './WiredTriggerBaseView';
|
||||||
|
|
||||||
|
const FURNI_SOURCE_OPTIONS: WiredSourceOption[] = [
|
||||||
|
{ value: 100, label: 'wiredfurni.params.sources.furni.100' },
|
||||||
|
{ value: 200, label: 'wiredfurni.params.sources.furni.200' }
|
||||||
|
];
|
||||||
|
|
||||||
|
const BOT_SOURCE_OPTIONS: WiredSourceOption[] = [
|
||||||
|
{ value: 100, label: 'wiredfurni.params.sources.users.100' },
|
||||||
|
{ value: 200, label: 'wiredfurni.params.sources.users.200' }
|
||||||
|
];
|
||||||
|
|
||||||
|
const normalizeFurniSource = (value: number) => (FURNI_SOURCE_OPTIONS.some(option => (option.value === value)) ? value : 100);
|
||||||
|
const normalizeBotSource = (value: number) => (BOT_SOURCE_OPTIONS.some(option => (option.value === value)) ? value : 100);
|
||||||
|
|
||||||
export const WiredTriggerBotReachedStuffView: FC<{}> = props =>
|
export const WiredTriggerBotReachedStuffView: FC<{}> = props =>
|
||||||
{
|
{
|
||||||
const [ botName, setBotName ] = useState('');
|
const [ botName, setBotName ] = useState('');
|
||||||
const { trigger = null, setStringParam = null } = useWired();
|
const [ furniSource, setFurniSource ] = useState(100);
|
||||||
|
const [ botSource, setBotSource ] = useState(100);
|
||||||
|
const { trigger = null, setStringParam = null, setIntParams = null } = useWired();
|
||||||
|
|
||||||
const save = () => setStringParam(botName);
|
const save = () =>
|
||||||
|
{
|
||||||
|
setStringParam((botSource === 100) ? botName : '');
|
||||||
|
setIntParams([ furniSource, botSource ]);
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() =>
|
useEffect(() =>
|
||||||
{
|
{
|
||||||
setBotName(trigger.stringData);
|
setBotName(trigger.stringData);
|
||||||
|
setFurniSource((trigger?.intData?.length > 0) ? normalizeFurniSource(trigger.intData[0]) : 100);
|
||||||
|
setBotSource((trigger?.intData?.length > 1) ? normalizeBotSource(trigger.intData[1]) : 100);
|
||||||
}, [ trigger ]);
|
}, [ trigger ]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<WiredTriggerBaseView hasSpecialInput={ true } requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_BY_ID_OR_BY_TYPE } save={ save }>
|
<WiredTriggerBaseView
|
||||||
<div className="flex flex-col gap-1">
|
hasSpecialInput={ true }
|
||||||
<Text bold>{ LocalizeText('wiredfurni.params.bot.name') }</Text>
|
requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_BY_ID_BY_TYPE_OR_FROM_CONTEXT }
|
||||||
<NitroInput maxLength={ 32 } type="text" value={ botName } onChange={ event => setBotName(event.target.value) } />
|
save={ save }
|
||||||
</div>
|
footer={
|
||||||
|
<WiredSourcesSelector
|
||||||
|
showFurni={ true }
|
||||||
|
showUsers={ true }
|
||||||
|
furniSource={ furniSource }
|
||||||
|
userSource={ botSource }
|
||||||
|
furniSources={ FURNI_SOURCE_OPTIONS }
|
||||||
|
userSources={ BOT_SOURCE_OPTIONS }
|
||||||
|
usersTitle="wiredfurni.params.sources.users.title.bots"
|
||||||
|
onChangeFurni={ setFurniSource }
|
||||||
|
onChangeUsers={ setBotSource } />
|
||||||
|
}>
|
||||||
|
{ (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> }
|
||||||
</WiredTriggerBaseView>
|
</WiredTriggerBaseView>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,8 +1,33 @@
|
|||||||
import { FC } from 'react';
|
import { FC, useEffect, useState } from 'react';
|
||||||
import { WiredFurniType } from '../../../../api';
|
import { WiredFurniType } from '../../../../api';
|
||||||
|
import { useWired } from '../../../../hooks';
|
||||||
|
import { WiredSourceOption, WiredSourcesSelector } from '../WiredSourcesSelector';
|
||||||
import { WiredTriggerBaseView } from './WiredTriggerBaseView';
|
import { WiredTriggerBaseView } from './WiredTriggerBaseView';
|
||||||
|
|
||||||
|
const FURNI_SOURCE_OPTIONS: WiredSourceOption[] = [
|
||||||
|
{ value: 100, label: 'wiredfurni.params.sources.furni.100' },
|
||||||
|
{ value: 200, label: 'wiredfurni.params.sources.furni.200' }
|
||||||
|
];
|
||||||
|
|
||||||
|
const normalizeFurniSource = (value: number) => (FURNI_SOURCE_OPTIONS.some(option => (option.value === value)) ? value : 100);
|
||||||
|
|
||||||
export const WiredTriggerClickFurniView: FC<{}> = () =>
|
export const WiredTriggerClickFurniView: FC<{}> = () =>
|
||||||
{
|
{
|
||||||
return <WiredTriggerBaseView hasSpecialInput={ false } requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_BY_ID_OR_BY_TYPE } save={ null } />;
|
const { trigger = null, setIntParams = null } = useWired();
|
||||||
|
const [ furniSource, setFurniSource ] = useState(100);
|
||||||
|
|
||||||
|
const save = () => setIntParams([ furniSource ]);
|
||||||
|
|
||||||
|
useEffect(() =>
|
||||||
|
{
|
||||||
|
setFurniSource((trigger?.intData?.length > 0) ? normalizeFurniSource(trigger.intData[0]) : 100);
|
||||||
|
}, [ trigger ]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<WiredTriggerBaseView
|
||||||
|
hasSpecialInput={ false }
|
||||||
|
requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_BY_ID_BY_TYPE_OR_FROM_CONTEXT }
|
||||||
|
save={ save }
|
||||||
|
footer={ <WiredSourcesSelector showFurni={ true } furniSource={ furniSource } furniSources={ FURNI_SOURCE_OPTIONS } onChangeFurni={ setFurniSource } /> } />
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,20 +1,46 @@
|
|||||||
import { FC, useEffect } from 'react';
|
import { FC, useEffect, useState } from 'react';
|
||||||
import { WiredFurniType } from '../../../../api';
|
import { WiredFurniType } from '../../../../api';
|
||||||
import { useWired } from '../../../../hooks';
|
import { useWired } from '../../../../hooks';
|
||||||
|
import { WiredSourceOption, WiredSourcesSelector } from '../WiredSourcesSelector';
|
||||||
import { WiredTriggerBaseView } from './WiredTriggerBaseView';
|
import { WiredTriggerBaseView } from './WiredTriggerBaseView';
|
||||||
|
|
||||||
const CLICK_TILE_INTERACTION_TYPES = [ 'room_invisible_click_tile' ];
|
const CLICK_TILE_INTERACTION_TYPES = [ 'room_invisible_click_tile' ];
|
||||||
|
const FURNI_SOURCE_OPTIONS: WiredSourceOption[] = [
|
||||||
|
{ value: 100, label: 'wiredfurni.params.sources.furni.100' },
|
||||||
|
{ value: 200, label: 'wiredfurni.params.sources.furni.200' }
|
||||||
|
];
|
||||||
|
|
||||||
|
const normalizeFurniSource = (value: number) => (FURNI_SOURCE_OPTIONS.some(option => (option.value === value)) ? value : 100);
|
||||||
|
|
||||||
export const WiredTriggerClickTileView: FC<{}> = () =>
|
export const WiredTriggerClickTileView: FC<{}> = () =>
|
||||||
{
|
{
|
||||||
const { setAllowedInteractionTypes } = useWired();
|
const { trigger = null, setIntParams = null, setAllowedInteractionTypes = null, setAllowedInteractionErrorKey = null } = useWired();
|
||||||
|
const [ furniSource, setFurniSource ] = useState(100);
|
||||||
|
|
||||||
|
const save = () => setIntParams([ furniSource ]);
|
||||||
|
|
||||||
|
useEffect(() =>
|
||||||
|
{
|
||||||
|
setFurniSource((trigger?.intData?.length > 0) ? normalizeFurniSource(trigger.intData[0]) : 100);
|
||||||
|
}, [ trigger ]);
|
||||||
|
|
||||||
useEffect(() =>
|
useEffect(() =>
|
||||||
{
|
{
|
||||||
setAllowedInteractionTypes(CLICK_TILE_INTERACTION_TYPES);
|
setAllowedInteractionTypes(CLICK_TILE_INTERACTION_TYPES);
|
||||||
|
setAllowedInteractionErrorKey('wiredfurni.error.require_click_tiles');
|
||||||
|
|
||||||
return () => setAllowedInteractionTypes(null);
|
return () =>
|
||||||
}, [ setAllowedInteractionTypes ]);
|
{
|
||||||
|
setAllowedInteractionTypes(null);
|
||||||
|
setAllowedInteractionErrorKey(null);
|
||||||
|
};
|
||||||
|
}, [ setAllowedInteractionErrorKey, setAllowedInteractionTypes ]);
|
||||||
|
|
||||||
return <WiredTriggerBaseView hasSpecialInput={ false } requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_BY_ID_OR_BY_TYPE } save={ null } />;
|
return (
|
||||||
|
<WiredTriggerBaseView
|
||||||
|
hasSpecialInput={ false }
|
||||||
|
requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_BY_ID_BY_TYPE_OR_FROM_CONTEXT }
|
||||||
|
save={ save }
|
||||||
|
footer={ <WiredSourcesSelector showFurni={ true } furniSource={ furniSource } furniSources={ FURNI_SOURCE_OPTIONS } onChangeFurni={ setFurniSource } /> } />
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,8 +1,38 @@
|
|||||||
import { FC } from 'react';
|
import { FC, useEffect, useState } from 'react';
|
||||||
import { WiredFurniType } from '../../../../api';
|
import { LocalizeText, WiredFurniType } from '../../../../api';
|
||||||
|
import { Text } from '../../../../common';
|
||||||
|
import { useWired } from '../../../../hooks';
|
||||||
import { WiredTriggerBaseView } from './WiredTriggerBaseView';
|
import { WiredTriggerBaseView } from './WiredTriggerBaseView';
|
||||||
|
|
||||||
export const WiredTriggerClickUserView: FC<{}> = () =>
|
export const WiredTriggerClickUserView: FC<{}> = () =>
|
||||||
{
|
{
|
||||||
return <WiredTriggerBaseView hasSpecialInput={ false } requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE } save={ null } />;
|
const [ blockMenuOpen, setBlockMenuOpen ] = useState(false);
|
||||||
|
const [ doNotRotate, setDoNotRotate ] = useState(false);
|
||||||
|
const { trigger = null, setIntParams = null } = useWired();
|
||||||
|
|
||||||
|
const save = () => setIntParams([
|
||||||
|
blockMenuOpen ? 1 : 0,
|
||||||
|
doNotRotate ? 1 : 0
|
||||||
|
]);
|
||||||
|
|
||||||
|
useEffect(() =>
|
||||||
|
{
|
||||||
|
setBlockMenuOpen((trigger?.intData?.length > 0) ? (trigger.intData[0] === 1) : false);
|
||||||
|
setDoNotRotate((trigger?.intData?.length > 1) ? (trigger.intData[1] === 1) : false);
|
||||||
|
}, [ trigger ]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<WiredTriggerBaseView hasSpecialInput={ true } requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE } save={ save }>
|
||||||
|
<div className="flex flex-col gap-1">
|
||||||
|
<div className="flex items-center gap-1">
|
||||||
|
<input checked={ blockMenuOpen } className="form-check-input" id="clickUserBlockMenuOpen" type="checkbox" onChange={ event => setBlockMenuOpen(event.target.checked) } />
|
||||||
|
<Text>{ LocalizeText('wiredfurni.params.click_user.block_menu_open') }</Text>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-1">
|
||||||
|
<input checked={ doNotRotate } className="form-check-input" id="clickUserDoNotRotate" type="checkbox" onChange={ event => setDoNotRotate(event.target.checked) } />
|
||||||
|
<Text>{ LocalizeText('wiredfurni.params.click_user.do_not_rotate') }</Text>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</WiredTriggerBaseView>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -11,13 +11,13 @@ const MINUTES_MAX = 99;
|
|||||||
const HALF_SECONDS_MIN = 0;
|
const HALF_SECONDS_MIN = 0;
|
||||||
const HALF_SECONDS_MAX = 119;
|
const HALF_SECONDS_MAX = 119;
|
||||||
const TRIGGER_FURNI_SOURCES: WiredSourceOption[] = [
|
const TRIGGER_FURNI_SOURCES: WiredSourceOption[] = [
|
||||||
{ value: 0, label: 'wiredfurni.params.sources.furni.0' },
|
{ value: 100, label: 'wiredfurni.params.sources.furni.100' },
|
||||||
{ value: 100, label: 'wiredfurni.params.sources.furni.100' }
|
{ value: 200, label: 'wiredfurni.params.sources.furni.200' }
|
||||||
];
|
];
|
||||||
|
|
||||||
const normalizeMinutes = (value: number) => Math.max(MINUTES_MIN, Math.min(MINUTES_MAX, value));
|
const normalizeMinutes = (value: number) => Math.max(MINUTES_MIN, Math.min(MINUTES_MAX, value));
|
||||||
const normalizeHalfSeconds = (value: number) => Math.max(HALF_SECONDS_MIN, Math.min(HALF_SECONDS_MAX, value));
|
const normalizeHalfSeconds = (value: number) => Math.max(HALF_SECONDS_MIN, Math.min(HALF_SECONDS_MAX, value));
|
||||||
const normalizeFurniSource = (value: number) => (value === 100 ? 100 : 0);
|
const normalizeFurniSource = (value: number) => (TRIGGER_FURNI_SOURCES.some(option => (option.value === value)) ? value : 100);
|
||||||
|
|
||||||
const formatSeconds = (halfSeconds: number) =>
|
const formatSeconds = (halfSeconds: number) =>
|
||||||
{
|
{
|
||||||
@@ -33,7 +33,7 @@ export const WiredTriggerClockCounterView: FC<{}> = () =>
|
|||||||
const [ furniSource, setFurniSource ] = useState<number>(() =>
|
const [ furniSource, setFurniSource ] = useState<number>(() =>
|
||||||
{
|
{
|
||||||
if(trigger?.intData?.length > 2) return normalizeFurniSource(trigger.intData[2]);
|
if(trigger?.intData?.length > 2) return normalizeFurniSource(trigger.intData[2]);
|
||||||
return (trigger?.selectedItems?.length ?? 0) > 0 ? 100 : 0;
|
return 100;
|
||||||
});
|
});
|
||||||
const [ minutes, setMinutes ] = useState(0);
|
const [ minutes, setMinutes ] = useState(0);
|
||||||
const [ halfSeconds, setHalfSeconds ] = useState(0);
|
const [ halfSeconds, setHalfSeconds ] = useState(0);
|
||||||
@@ -55,7 +55,7 @@ export const WiredTriggerClockCounterView: FC<{}> = () =>
|
|||||||
|
|
||||||
setMinutes((trigger.intData.length > 0) ? normalizeMinutes(trigger.intData[0]) : 0);
|
setMinutes((trigger.intData.length > 0) ? normalizeMinutes(trigger.intData[0]) : 0);
|
||||||
setHalfSeconds((trigger.intData.length > 1) ? normalizeHalfSeconds(trigger.intData[1]) : 0);
|
setHalfSeconds((trigger.intData.length > 1) ? normalizeHalfSeconds(trigger.intData[1]) : 0);
|
||||||
setFurniSource((trigger.intData.length > 2) ? normalizeFurniSource(trigger.intData[2]) : ((trigger.selectedItems?.length ?? 0) > 0 ? 100 : 0));
|
setFurniSource((trigger.intData.length > 2) ? normalizeFurniSource(trigger.intData[2]) : 100);
|
||||||
}, [ trigger ]);
|
}, [ trigger ]);
|
||||||
|
|
||||||
useEffect(() =>
|
useEffect(() =>
|
||||||
|
|||||||
@@ -2,35 +2,59 @@ import { FC, useEffect, useState } from 'react';
|
|||||||
import { LocalizeText, WiredFurniType } from '../../../../api';
|
import { LocalizeText, WiredFurniType } from '../../../../api';
|
||||||
import { Text } from '../../../../common';
|
import { Text } from '../../../../common';
|
||||||
import { useWired } from '../../../../hooks';
|
import { useWired } from '../../../../hooks';
|
||||||
|
import { WiredSourceOption, WiredSourcesSelector } from '../WiredSourcesSelector';
|
||||||
import { WiredTriggerBaseView } from './WiredTriggerBaseView';
|
import { WiredTriggerBaseView } from './WiredTriggerBaseView';
|
||||||
|
|
||||||
|
const FURNI_SOURCE_OPTIONS: WiredSourceOption[] = [
|
||||||
|
{ value: 100, label: 'wiredfurni.params.sources.furni.100' },
|
||||||
|
{ value: 200, label: 'wiredfurni.params.sources.furni.200' }
|
||||||
|
];
|
||||||
|
|
||||||
const ANTENNA_INTERACTION_TYPES = [ 'antenna' ];
|
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<{}> = () =>
|
export const WiredTriggerReceiveSignalView: FC<{}> = () =>
|
||||||
{
|
{
|
||||||
const [ senderCount, setSenderCount ] = useState(0);
|
const [ senderCount, setSenderCount ] = useState(0);
|
||||||
const [ maxSenders, setMaxSenders ] = useState(5);
|
const [ maxSenders, setMaxSenders ] = useState(5);
|
||||||
|
const [ channel, setChannel ] = useState(0);
|
||||||
|
const [ furniSource, setFurniSource ] = useState(100);
|
||||||
|
|
||||||
const { trigger = null, setAllowedInteractionTypes } = useWired();
|
const { trigger = null, setIntParams = null, setAllowedInteractionTypes = null, setAllowedInteractionErrorKey = null } = useWired();
|
||||||
|
|
||||||
|
const save = () => setIntParams([ channel, furniSource ]);
|
||||||
|
|
||||||
useEffect(() =>
|
useEffect(() =>
|
||||||
{
|
{
|
||||||
if(!trigger) return;
|
if(!trigger) return;
|
||||||
|
|
||||||
const p = trigger.intData;
|
const p = trigger.intData;
|
||||||
|
if(p.length >= 1) setChannel(p[0]);
|
||||||
if(p.length >= 2) setSenderCount(p[1]);
|
if(p.length >= 2) setSenderCount(p[1]);
|
||||||
if(p.length >= 3) setMaxSenders(p[2]);
|
if(p.length >= 3) setMaxSenders(p[2]);
|
||||||
|
if(p.length >= 4) setFurniSource(normalizeFurniSource(p[3]));
|
||||||
|
else setFurniSource(100);
|
||||||
}, [ trigger ]);
|
}, [ trigger ]);
|
||||||
|
|
||||||
useEffect(() =>
|
useEffect(() =>
|
||||||
{
|
{
|
||||||
setAllowedInteractionTypes(ANTENNA_INTERACTION_TYPES);
|
setAllowedInteractionTypes(ANTENNA_INTERACTION_TYPES);
|
||||||
|
setAllowedInteractionErrorKey(ANTENNA_ERROR_MESSAGE);
|
||||||
|
|
||||||
return () => setAllowedInteractionTypes(null);
|
return () =>
|
||||||
}, [ setAllowedInteractionTypes ]);
|
{
|
||||||
|
setAllowedInteractionTypes(null);
|
||||||
|
setAllowedInteractionErrorKey(null);
|
||||||
|
};
|
||||||
|
}, [ setAllowedInteractionErrorKey, setAllowedInteractionTypes ]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<WiredTriggerBaseView hasSpecialInput={ true } requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_BY_ID } save={ null }>
|
<WiredTriggerBaseView
|
||||||
|
hasSpecialInput={ true }
|
||||||
|
requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_BY_ID_BY_TYPE_OR_FROM_CONTEXT }
|
||||||
|
save={ save }
|
||||||
|
footer={ <WiredSourcesSelector showFurni={ true } furniSource={ furniSource } furniSources={ FURNI_SOURCE_OPTIONS } onChangeFurni={ setFurniSource } /> }>
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<Text small>{ LocalizeText('wiredfurni.params.signal.senders_connected') }</Text>
|
<Text small>{ LocalizeText('wiredfurni.params.signal.senders_connected') }</Text>
|
||||||
<Text bold small>{ senderCount }/{ maxSenders }</Text>
|
<Text bold small>{ senderCount }/{ maxSenders }</Text>
|
||||||
|
|||||||
@@ -4,16 +4,20 @@ import { Slider, Text } from '../../../../common';
|
|||||||
import { useWired } from '../../../../hooks';
|
import { useWired } from '../../../../hooks';
|
||||||
import { WiredTriggerBaseView } from './WiredTriggerBaseView';
|
import { WiredTriggerBaseView } from './WiredTriggerBaseView';
|
||||||
|
|
||||||
|
const TEAM_TYPES = [ 0, 1, 2, 3, 4 ];
|
||||||
|
|
||||||
export const WiredTriggeScoreAchievedView: FC<{}> = props =>
|
export const WiredTriggeScoreAchievedView: FC<{}> = props =>
|
||||||
{
|
{
|
||||||
const [ points, setPoints ] = useState(1);
|
const [ points, setPoints ] = useState(1);
|
||||||
|
const [ teamType, setTeamType ] = useState(0);
|
||||||
const { trigger = null, setIntParams = null } = useWired();
|
const { trigger = null, setIntParams = null } = useWired();
|
||||||
|
|
||||||
const save = () => setIntParams([ points ]);
|
const save = () => setIntParams([ points, teamType ]);
|
||||||
|
|
||||||
useEffect(() =>
|
useEffect(() =>
|
||||||
{
|
{
|
||||||
setPoints((trigger.intData.length > 0) ? trigger.intData[0] : 0);
|
setPoints((trigger.intData.length > 0) ? trigger.intData[0] : 0);
|
||||||
|
setTeamType((trigger.intData.length > 1) ? trigger.intData[1] : 0);
|
||||||
}, [ trigger ]);
|
}, [ trigger ]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -25,6 +29,14 @@ export const WiredTriggeScoreAchievedView: FC<{}> = props =>
|
|||||||
min={ 1 }
|
min={ 1 }
|
||||||
value={ points }
|
value={ points }
|
||||||
onChange={ event => setPoints(event) } />
|
onChange={ event => setPoints(event) } />
|
||||||
|
<hr className="m-0 bg-dark" />
|
||||||
|
<Text bold>{ LocalizeText('wiredfurni.params.team') }</Text>
|
||||||
|
{ TEAM_TYPES.map(value => (
|
||||||
|
<label key={ value } className="flex items-center gap-1">
|
||||||
|
<input checked={ (teamType === value) } className="form-check-input" name="scoreAchievedTeamType" type="radio" onChange={ () => setTeamType(value) } />
|
||||||
|
<Text>{ LocalizeText((value === 0) ? 'wiredfurni.params.team.any' : `wiredfurni.params.team.${ value }`) }</Text>
|
||||||
|
</label>
|
||||||
|
)) }
|
||||||
</div>
|
</div>
|
||||||
</WiredTriggerBaseView>
|
</WiredTriggerBaseView>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -2,22 +2,36 @@ import { FC, useEffect, useState } from 'react';
|
|||||||
import { LocalizeText, WiredFurniType } from '../../../../api';
|
import { LocalizeText, WiredFurniType } from '../../../../api';
|
||||||
import { Text } from '../../../../common';
|
import { Text } from '../../../../common';
|
||||||
import { useWired } from '../../../../hooks';
|
import { useWired } from '../../../../hooks';
|
||||||
|
import { WiredSourceOption, WiredSourcesSelector } from '../WiredSourcesSelector';
|
||||||
import { WiredTriggerBaseView } from './WiredTriggerBaseView';
|
import { WiredTriggerBaseView } from './WiredTriggerBaseView';
|
||||||
|
|
||||||
|
const FURNI_SOURCE_OPTIONS: WiredSourceOption[] = [
|
||||||
|
{ value: 100, label: 'wiredfurni.params.sources.furni.100' },
|
||||||
|
{ value: 200, label: 'wiredfurni.params.sources.furni.200' }
|
||||||
|
];
|
||||||
|
|
||||||
|
const normalizeFurniSource = (value: number) => (FURNI_SOURCE_OPTIONS.some(option => (option.value === value)) ? value : 100);
|
||||||
|
|
||||||
export const WiredTriggerToggleFurniView: FC<{}> = () =>
|
export const WiredTriggerToggleFurniView: FC<{}> = () =>
|
||||||
{
|
{
|
||||||
const [ triggerMode, setTriggerMode ] = useState(0);
|
const [ triggerMode, setTriggerMode ] = useState(0);
|
||||||
|
const [ furniSource, setFurniSource ] = useState(100);
|
||||||
const { trigger = null, setIntParams = null } = useWired();
|
const { trigger = null, setIntParams = null } = useWired();
|
||||||
|
|
||||||
const save = () => setIntParams([ triggerMode ]);
|
const save = () => setIntParams([ triggerMode, furniSource ]);
|
||||||
|
|
||||||
useEffect(() =>
|
useEffect(() =>
|
||||||
{
|
{
|
||||||
setTriggerMode((trigger?.intData?.length > 0) ? trigger.intData[0] : 0);
|
setTriggerMode((trigger?.intData?.length > 0) ? trigger.intData[0] : 0);
|
||||||
|
setFurniSource((trigger?.intData?.length > 1) ? normalizeFurniSource(trigger.intData[1]) : 100);
|
||||||
}, [ trigger ]);
|
}, [ trigger ]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<WiredTriggerBaseView hasSpecialInput={ true } requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_BY_ID } save={ save }>
|
<WiredTriggerBaseView
|
||||||
|
hasSpecialInput={ true }
|
||||||
|
requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_BY_ID_BY_TYPE_OR_FROM_CONTEXT }
|
||||||
|
save={ save }
|
||||||
|
footer={ <WiredSourcesSelector showFurni={ true } furniSource={ furniSource } furniSources={ FURNI_SOURCE_OPTIONS } onChangeFurni={ setFurniSource } /> }>
|
||||||
<div className="flex flex-col gap-1">
|
<div className="flex flex-col gap-1">
|
||||||
<Text bold>{ LocalizeText('wiredfurni.params.condition.state') }</Text>
|
<Text bold>{ LocalizeText('wiredfurni.params.condition.state') }</Text>
|
||||||
<div className="flex items-center gap-1">
|
<div className="flex items-center gap-1">
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { GetRoomEngine, GetSessionDataManager, RoomEngineObjectEvent, RoomEngineUseProductEvent, RoomObjectCategory, RoomObjectType, RoomObjectVariable, RoomSessionPetInfoUpdateEvent, RoomSessionPetStatusUpdateEvent, RoomSessionUserDataUpdateEvent } from '@nitrots/nitro-renderer';
|
import { GetRoomEngine, GetSessionDataManager, RoomEngineObjectEvent, RoomEngineUseProductEvent, RoomObjectCategory, RoomObjectType, RoomObjectVariable, RoomSessionPetInfoUpdateEvent, RoomSessionPetStatusUpdateEvent, RoomSessionUserDataUpdateEvent } from '@nitrots/nitro-renderer';
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useRef, useState } from 'react';
|
||||||
import { AvatarInfoFurni, AvatarInfoName, AvatarInfoPet, AvatarInfoRentableBot, AvatarInfoUser, AvatarInfoUtilities, CanManipulateFurniture, FurniCategory, IAvatarInfo, IsOwnerOfFurniture, RoomWidgetUpdateRoomObjectEvent, UseProductItem } from '../../../api';
|
import { AvatarInfoFurni, AvatarInfoName, AvatarInfoPet, AvatarInfoRentableBot, AvatarInfoUser, AvatarInfoUtilities, CanManipulateFurniture, FurniCategory, IAvatarInfo, IsOwnerOfFurniture, RoomWidgetUpdateRoomObjectEvent, UseProductItem } from '../../../api';
|
||||||
import { useNitroEvent, useUiEvent } from '../../events';
|
import { useNitroEvent, useUiEvent } from '../../events';
|
||||||
import { useFriends } from '../../friends';
|
import { useFriends } from '../../friends';
|
||||||
@@ -9,6 +9,7 @@ import { useRoom } from '../useRoom';
|
|||||||
|
|
||||||
const useAvatarInfoWidgetState = () =>
|
const useAvatarInfoWidgetState = () =>
|
||||||
{
|
{
|
||||||
|
const CLICK_USER_DEBOUNCE_MS = 120;
|
||||||
const [ avatarInfo, setAvatarInfo ] = useState<IAvatarInfo>(null);
|
const [ avatarInfo, setAvatarInfo ] = useState<IAvatarInfo>(null);
|
||||||
const [ activeNameBubble, setActiveNameBubble ] = useState<AvatarInfoName>(null);
|
const [ activeNameBubble, setActiveNameBubble ] = useState<AvatarInfoName>(null);
|
||||||
const [ nameBubbles, setNameBubbles ] = useState<AvatarInfoName[]>([]);
|
const [ nameBubbles, setNameBubbles ] = useState<AvatarInfoName[]>([]);
|
||||||
@@ -16,10 +17,26 @@ const useAvatarInfoWidgetState = () =>
|
|||||||
const [ confirmingProduct, setConfirmingProduct ] = useState<UseProductItem>(null);
|
const [ confirmingProduct, setConfirmingProduct ] = useState<UseProductItem>(null);
|
||||||
const [ pendingPetId, setPendingPetId ] = useState<number>(-1);
|
const [ pendingPetId, setPendingPetId ] = useState<number>(-1);
|
||||||
const [ isDecorating, setIsDecorating ] = useState(false);
|
const [ isDecorating, setIsDecorating ] = useState(false);
|
||||||
|
const pendingAvatarInfoTimeout = useRef<ReturnType<typeof setTimeout>>(null);
|
||||||
const { friends = [] } = useFriends();
|
const { friends = [] } = useFriends();
|
||||||
const { selectObjectForWired = null } = useWired();
|
const { selectObjectForWired = null } = useWired();
|
||||||
const { roomSession = null } = useRoom();
|
const { roomSession = null } = useRoom();
|
||||||
|
|
||||||
|
const clearPendingAvatarInfo = () =>
|
||||||
|
{
|
||||||
|
if(!pendingAvatarInfoTimeout.current) return;
|
||||||
|
|
||||||
|
clearTimeout(pendingAvatarInfoTimeout.current);
|
||||||
|
pendingAvatarInfoTimeout.current = null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const isAvatarMenuBlocked = () =>
|
||||||
|
{
|
||||||
|
const control = (globalThis as any).__nitroAvatarClickControl;
|
||||||
|
|
||||||
|
return !!control && (control.suppressMenuUntil > Date.now());
|
||||||
|
};
|
||||||
|
|
||||||
const removeNameBubble = (index: number) =>
|
const removeNameBubble = (index: number) =>
|
||||||
{
|
{
|
||||||
setNameBubbles(prevValue =>
|
setNameBubbles(prevValue =>
|
||||||
@@ -64,6 +81,8 @@ const useAvatarInfoWidgetState = () =>
|
|||||||
|
|
||||||
const getObjectInfo = (objectId: number, category: number) =>
|
const getObjectInfo = (objectId: number, category: number) =>
|
||||||
{
|
{
|
||||||
|
clearPendingAvatarInfo();
|
||||||
|
|
||||||
let info: IAvatarInfo = null;
|
let info: IAvatarInfo = null;
|
||||||
|
|
||||||
switch(category)
|
switch(category)
|
||||||
@@ -101,7 +120,20 @@ const useAvatarInfoWidgetState = () =>
|
|||||||
|
|
||||||
if(!info) return;
|
if(!info) return;
|
||||||
|
|
||||||
setAvatarInfo(info);
|
if(category !== RoomObjectCategory.UNIT)
|
||||||
|
{
|
||||||
|
setAvatarInfo(info);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
pendingAvatarInfoTimeout.current = setTimeout(() =>
|
||||||
|
{
|
||||||
|
pendingAvatarInfoTimeout.current = null;
|
||||||
|
|
||||||
|
if(isAvatarMenuBlocked()) return;
|
||||||
|
|
||||||
|
setAvatarInfo(info);
|
||||||
|
}, CLICK_USER_DEBOUNCE_MS);
|
||||||
};
|
};
|
||||||
|
|
||||||
const processUsableRoomObject = (objectId: number) =>
|
const processUsableRoomObject = (objectId: number) =>
|
||||||
@@ -271,6 +303,7 @@ const useAvatarInfoWidgetState = () =>
|
|||||||
|
|
||||||
useObjectDeselectedEvent(event =>
|
useObjectDeselectedEvent(event =>
|
||||||
{
|
{
|
||||||
|
clearPendingAvatarInfo();
|
||||||
setAvatarInfo(null);
|
setAvatarInfo(null);
|
||||||
setProductBubbles([]);
|
setProductBubbles([]);
|
||||||
});
|
});
|
||||||
@@ -344,6 +377,11 @@ const useAvatarInfoWidgetState = () =>
|
|||||||
setNameBubbles([]);
|
setNameBubbles([]);
|
||||||
}, [ activeNameBubble ]);
|
}, [ activeNameBubble ]);
|
||||||
|
|
||||||
|
useEffect(() =>
|
||||||
|
{
|
||||||
|
return () => clearPendingAvatarInfo();
|
||||||
|
}, []);
|
||||||
|
|
||||||
useEffect(() =>
|
useEffect(() =>
|
||||||
{
|
{
|
||||||
if(!roomSession) return;
|
if(!roomSession) return;
|
||||||
|
|||||||
@@ -87,7 +87,13 @@ const useWiredState = () =>
|
|||||||
|
|
||||||
const handleDisallowedInteraction = () =>
|
const handleDisallowedInteraction = () =>
|
||||||
{
|
{
|
||||||
if(allowedInteractionErrorKey) simpleAlert(LocalizeText(allowedInteractionErrorKey), null, null, null, LocalizeText('wiredfurni.title'));
|
if(!allowedInteractionErrorKey) return;
|
||||||
|
|
||||||
|
const message = (/^[a-z0-9_.]+$/i.test(allowedInteractionErrorKey))
|
||||||
|
? LocalizeText(allowedInteractionErrorKey)
|
||||||
|
: allowedInteractionErrorKey;
|
||||||
|
|
||||||
|
simpleAlert(message, null, null, null, LocalizeText('wiredfurni.title'));
|
||||||
};
|
};
|
||||||
|
|
||||||
if(selectByType && category === RoomObjectCategory.FLOOR)
|
if(selectByType && category === RoomObjectCategory.FLOOR)
|
||||||
|
|||||||