+
+ Type
+ Amount / Value
+ { uniqueRewards ? 'Mode' : 'Chance %' }
+ { hasCustomCurrencyReward ? 'Currency Type' : 'Extra / Info' }
+ Action
+
{ rewards && rewards.map((reward, index) =>
{
+ const rewardTypeOptions = (reward.rewardType === 'respect')
+ ? REWARD_TYPES
+ : SELECTABLE_REWARD_TYPES;
+
return (
-
-
-
updateReward(index, e.target.checked, reward.itemCode, reward.probability) } />
-
Badge?
+
+
+
updateReward(index, prevValue => ({ ...prevValue, rewardValue: event.target.value })) } />
+ { uniqueRewards
+ ?
+ Unique
+
+ : updateReward(index, prevValue => ({ ...prevValue, probability: Number(event.target.value) })) } /> }
+ { (reward.rewardType === 'points')
+ ?
+ updateReward(index, prevValue => ({ ...prevValue, pointsType: Number(event.target.value) })) } />
+ :
+ { getExtraFieldLabel(reward.rewardType) }
+
}
+
+ { (index > 0) &&
+ }
- updateReward(index, reward.isBadge, e.target.value, reward.probability) } />
- updateReward(index, reward.isBadge, reward.itemCode, Number(e.target.value)) } />
- { (index > 0) &&
- }
);
}) }
+
+ Extra Currency uses Amount as the quantity and Currency Type as the purse type id. Example: amount 200 + type 105.
+
);
};
diff --git a/src/components/wired/views/actions/WiredActionLayoutView.tsx b/src/components/wired/views/actions/WiredActionLayoutView.tsx
index 23c16d1..551da1a 100644
--- a/src/components/wired/views/actions/WiredActionLayoutView.tsx
+++ b/src/components/wired/views/actions/WiredActionLayoutView.tsx
@@ -1,5 +1,7 @@
import { WiredActionLayoutCode } from '../../../../api';
import { WiredActionBotChangeFigureView } from './WiredActionBotChangeFigureView';
+import { WiredActionFreezeView } from './WiredActionFreezeView';
+import { WiredActionFurniToFurniView } from './WiredActionFurniToFurniView';
import { WiredActionSendSignalView } from './WiredActionSendSignalView';
import { WiredActionFurniAreaView } from '../selectors/WiredActionFurniAreaView';
import { WiredSelectorFurniNeighborhoodView } from '../selectors/WiredSelectorFurniNeighborhoodView';
@@ -26,10 +28,12 @@ import { WiredActionMoveAndRotateFurniView } from './WiredActionMoveAndRotateFur
import { WiredActionMoveFurniToView } from './WiredActionMoveFurniToView';
import { WiredActionMoveFurniView } from './WiredActionMoveFurniView';
import { WiredActionMuteUserView } from './WiredActionMuteUserView';
+import { WiredActionRelativeMoveView } from './WiredActionRelativeMoveView';
import { WiredActionResetView } from './WiredActionResetView';
import { WiredActionSetFurniStateToView } from './WiredActionSetFurniStateToView';
import { WiredActionTeleportView } from './WiredActionTeleportView';
import { WiredActionToggleFurniStateView } from './WiredActionToggleFurniStateView';
+import { WiredActionUnfreezeView } from './WiredActionUnfreezeView';
export const WiredActionLayoutView = (code: number) =>
{
@@ -57,6 +61,12 @@ export const WiredActionLayoutView = (code: number) =>
return
;
case WiredActionLayoutCode.FLEE:
return
;
+ case WiredActionLayoutCode.FREEZE:
+ return
;
+ case WiredActionLayoutCode.FURNI_TO_USER:
+ return
;
+ case WiredActionLayoutCode.FURNI_TO_FURNI:
+ return
;
case WiredActionLayoutCode.GIVE_REWARD:
return
;
case WiredActionLayoutCode.GIVE_SCORE:
@@ -77,6 +87,8 @@ export const WiredActionLayoutView = (code: number) =>
return
;
case WiredActionLayoutCode.MUTE_USER:
return
;
+ case WiredActionLayoutCode.RELATIVE_MOVE:
+ return
;
case WiredActionLayoutCode.RESET:
return
;
case WiredActionLayoutCode.SET_FURNI_STATE:
@@ -85,6 +97,10 @@ export const WiredActionLayoutView = (code: number) =>
return
;
case WiredActionLayoutCode.TOGGLE_FURNI_STATE:
return
;
+ case WiredActionLayoutCode.UNFREEZE:
+ return
;
+ case WiredActionLayoutCode.USER_TO_FURNI:
+ return
;
case WiredActionLayoutCode.FURNI_AREA_SELECTOR:
return
;
case WiredActionLayoutCode.FURNI_NEIGHBORHOOD_SELECTOR:
diff --git a/src/components/wired/views/actions/WiredActionRelativeMoveView.tsx b/src/components/wired/views/actions/WiredActionRelativeMoveView.tsx
new file mode 100644
index 0000000..c6f33a1
--- /dev/null
+++ b/src/components/wired/views/actions/WiredActionRelativeMoveView.tsx
@@ -0,0 +1,120 @@
+import { FC, useEffect, useState } from 'react';
+import { FaArrowDown, FaArrowLeft, FaArrowRight, FaArrowUp } from 'react-icons/fa';
+import { LocalizeText, WiredFurniType } from '../../../../api';
+import { Slider, Text } from '../../../../common';
+import { useWired } from '../../../../hooks';
+import { WiredSourcesSelector } from '../WiredSourcesSelector';
+import { WiredActionBaseView } from './WiredActionBaseView';
+
+const MAX_DISTANCE = 20;
+
+const HORIZONTAL_OPTIONS = [
+ { value: 0, icon:
},
+ { value: 1, icon:
}
+];
+
+const VERTICAL_OPTIONS = [
+ { value: 0, icon:
},
+ { value: 1, icon:
}
+];
+
+const normalizeDirection = (value: number, fallback = 1) =>
+{
+ if(value === 0 || value === 1) return value;
+
+ return fallback;
+};
+
+const normalizeDistance = (value: number) =>
+{
+ if(isNaN(value)) return 0;
+
+ return Math.max(0, Math.min(MAX_DISTANCE, value));
+};
+
+export const WiredActionRelativeMoveView: FC<{}> = () =>
+{
+ const { trigger = null, setIntParams = null } = useWired();
+
+ const [horizontalDirection, setHorizontalDirection] = useState(1);
+ const [horizontalDistance, setHorizontalDistance] = useState(0);
+ const [verticalDirection, setVerticalDirection] = useState(1);
+ const [verticalDistance, setVerticalDistance] = useState(0);
+ const [ furniSource, setFurniSource ] = useState
(() =>
+ {
+ if(trigger?.intData?.length > 4) return trigger.intData[4];
+ return (trigger?.selectedItems?.length ?? 0) > 0 ? 100 : 0;
+ });
+
+ useEffect(() =>
+ {
+ if(!trigger) return;
+
+ setHorizontalDirection((trigger.intData.length > 0) ? normalizeDirection(trigger.intData[0], 1) : 1);
+ setHorizontalDistance((trigger.intData.length > 1) ? normalizeDistance(trigger.intData[1]) : 0);
+ setVerticalDirection((trigger.intData.length > 2) ? normalizeDirection(trigger.intData[2], 1) : 1);
+ setVerticalDistance((trigger.intData.length > 3) ? normalizeDistance(trigger.intData[3]) : 0);
+
+ if(trigger.intData.length > 4) setFurniSource(trigger.intData[4]);
+ else setFurniSource((trigger.selectedItems?.length ?? 0) > 0 ? 100 : 0);
+ }, [ trigger ]);
+
+ const save = () => setIntParams([
+ horizontalDirection,
+ horizontalDistance,
+ verticalDirection,
+ verticalDistance,
+ furniSource
+ ]);
+
+ return (
+ }>
+
+
{ LocalizeText('wiredfurni.params.movement.horizontal.selection') }
+
+ { HORIZONTAL_OPTIONS.map(option =>
+ {
+ return (
+
+ );
+ }) }
+
+
{ LocalizeText('wiredfurni.params.movement.horizontal.distance', [ 'distance' ], [ horizontalDistance.toString() ]) }
+
setHorizontalDistance(value as number) } />
+
+
+
{ LocalizeText('wiredfurni.params.movement.vertical.selection') }
+
+ { VERTICAL_OPTIONS.map(option =>
+ {
+ return (
+
+ );
+ }) }
+
+
{ LocalizeText('wiredfurni.params.movement.vertical.distance', [ 'distance' ], [ verticalDistance.toString() ]) }
+
setVerticalDistance(value as number) } />
+
+
+ );
+};
diff --git a/src/components/wired/views/actions/WiredActionSetAltitudeView.tsx b/src/components/wired/views/actions/WiredActionSetAltitudeView.tsx
new file mode 100644
index 0000000..3e4017b
--- /dev/null
+++ b/src/components/wired/views/actions/WiredActionSetAltitudeView.tsx
@@ -0,0 +1,162 @@
+import { FC, useEffect, useMemo, useState } from 'react';
+import { LocalizeText, WiredFurniType } from '../../../../api';
+import { Slider, Text } from '../../../../common';
+import { useWired } from '../../../../hooks';
+import { WiredSourcesSelector } from '../WiredSourcesSelector';
+import { WiredActionBaseView } from './WiredActionBaseView';
+
+const MIN_ALTITUDE = 0;
+const MAX_ALTITUDE = 40;
+const ALTITUDE_STEP = 0.01;
+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);
+};
+
+const OPERATOR_OPTIONS = [
+ { value: 0, label: 'wiredfurni.params.operator.0' },
+ { value: 1, label: 'wiredfurni.params.operator.1' },
+ { value: 2, label: 'wiredfurni.params.operator.2' }
+];
+
+const normalizeOperator = (value: number) =>
+{
+ if(value < 0 || value > 2) return 2;
+
+ return value;
+};
+
+export const WiredActionSetAltitudeView: FC<{}> = () =>
+{
+ const { trigger = null, setIntParams = null, setStringParam = null } = useWired();
+
+ const [ operator, setOperator ] = useState(2);
+ const [ furniSource, setFurniSource ] = useState(() =>
+ {
+ if(trigger?.intData?.length > 1) return trigger.intData[1];
+ return (trigger?.selectedItems?.length ?? 0) > 0 ? 100 : 0;
+ });
+ const [ altitude, setAltitude ] = useState(0);
+ const [ altitudeInput, setAltitudeInput ] = useState('0');
+
+ const normalizedAltitudeText = useMemo(() => formatAltitude(altitude), [ altitude ]);
+
+ useEffect(() =>
+ {
+ if(!trigger) return;
+
+ setOperator((trigger.intData.length > 0) ? normalizeOperator(trigger.intData[0]) : 2);
+ setFurniSource((trigger.intData.length > 1) ? trigger.intData[1] : ((trigger.selectedItems?.length ?? 0) > 0 ? 100 : 0));
+
+ const nextAltitude = parseAltitude(trigger.stringData);
+ 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 = () =>
+ {
+ setIntParams([
+ operator,
+ furniSource
+ ]);
+
+ setStringParam(normalizedAltitudeText);
+ };
+
+ return (
+ }>
+
+ { OPERATOR_OPTIONS.map(option =>
+ {
+ return (
+
+ setOperator(option.value) } />
+ { LocalizeText(option.label) }
+
+ );
+ }) }
+
+
+ { LocalizeText('wiredfurni.params.setaltitude') }
+ setAltitudeInput(formatAltitude(altitude)) }
+ onChange={ event => updateAltitudeInput(event.target.value) } />
+
+
+ updateAltitude(event as number) } />
+ { normalizedAltitudeText }
+
+
+ );
+};
diff --git a/src/components/wired/views/actions/WiredActionUnfreezeView.tsx b/src/components/wired/views/actions/WiredActionUnfreezeView.tsx
new file mode 100644
index 0000000..98b31a6
--- /dev/null
+++ b/src/components/wired/views/actions/WiredActionUnfreezeView.tsx
@@ -0,0 +1,26 @@
+import { FC, useEffect, useState } from 'react';
+import { WiredFurniType } from '../../../../api';
+import { useWired } from '../../../../hooks';
+import { WiredActionBaseView } from './WiredActionBaseView';
+import { WiredSourcesSelector } from '../WiredSourcesSelector';
+
+export const WiredActionUnfreezeView: FC<{}> = () =>
+{
+ const [ userSource, setUserSource ] = useState(0);
+ const { trigger = null, setIntParams = null } = useWired();
+
+ const save = () => setIntParams([ userSource ]);
+
+ useEffect(() =>
+ {
+ setUserSource((trigger?.intData?.length > 0) ? trigger.intData[0] : 0);
+ }, [ trigger ]);
+
+ return (
+ } />
+ );
+};
diff --git a/src/components/wired/views/conditions/WiredConditionHasAltitudeView.tsx b/src/components/wired/views/conditions/WiredConditionHasAltitudeView.tsx
new file mode 100644
index 0000000..91a6a82
--- /dev/null
+++ b/src/components/wired/views/conditions/WiredConditionHasAltitudeView.tsx
@@ -0,0 +1,185 @@
+import { FC, useEffect, useState } from 'react';
+import { LocalizeText, WiredFurniType } from '../../../../api';
+import { Slider, Text } from '../../../../common';
+import { useWired } from '../../../../hooks';
+import { WiredSourcesSelector } from '../WiredSourcesSelector';
+import { WiredConditionBaseView } from './WiredConditionBaseView';
+
+const COUNTER_INTERACTION_TYPES = [ 'game_upcounter' ];
+const MIN_ALTITUDE = 0;
+const MAX_ALTITUDE = 40;
+const ALTITUDE_STEP = 0.01;
+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 WiredConditionHasAltitudeView: FC<{}> = () =>
+{
+ const { trigger = null, setIntParams = null, setStringParam = null, setAllowedInteractionTypes = null, setAllowedInteractionErrorKey = null } = useWired();
+ const [ comparison, setComparison ] = useState(1);
+ const [ furniSource, setFurniSource ] = useState(() =>
+ {
+ if(trigger?.intData?.length > 1) return trigger.intData[1];
+ return (trigger?.selectedItems?.length ?? 0) > 0 ? 100 : 0;
+ });
+ const [ quantifier, setQuantifier ] = useState(0);
+ const [ showAdvanced, setShowAdvanced ] = useState(false);
+ const [ altitude, setAltitude ] = useState(0);
+ const [ altitudeInput, setAltitudeInput ] = useState('0');
+
+ useEffect(() =>
+ {
+ setAllowedInteractionTypes(COUNTER_INTERACTION_TYPES);
+ setAllowedInteractionErrorKey('wiredfurni.error.require_counter_furni');
+
+ return () =>
+ {
+ setAllowedInteractionTypes(null);
+ setAllowedInteractionErrorKey(null);
+ };
+ }, [ setAllowedInteractionErrorKey, setAllowedInteractionTypes ]);
+
+ useEffect(() =>
+ {
+ if(!trigger) return;
+
+ setComparison((trigger.intData.length > 0) ? trigger.intData[0] : 1);
+ setFurniSource((trigger.intData.length > 1) ? trigger.intData[1] : ((trigger.selectedItems?.length ?? 0) > 0 ? 100 : 0));
+ setQuantifier((trigger.intData.length > 2) ? trigger.intData[2] : 0);
+ setShowAdvanced((trigger.intData.length > 1) ? (trigger.intData[1] !== 0 || trigger.intData[2] !== 0) : false);
+
+ const nextAltitude = parseAltitude(trigger.stringData);
+ 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 = () =>
+ {
+ setIntParams([
+ comparison,
+ furniSource,
+ quantifier
+ ]);
+ setStringParam(formatAltitude(altitude));
+ };
+
+ return (
+
+
+ { showAdvanced &&
+ <>
+
+
{ LocalizeText('wiredfurni.params.quantifier_selection') }
+ { [ 0, 1 ].map(value =>
+ {
+ return (
+
+ setQuantifier(value) } />
+ { LocalizeText(`wiredfurni.params.quantifier.furni.${ value }`) }
+
+ );
+ }) }
+
+
+ > }
+
+ }>
+
+ { [ 0, 1, 2 ].map(value =>
+ {
+ return (
+
+ setComparison(value) } />
+ { LocalizeText(`wiredfurni.params.comparison.${ value }`) }
+
+ );
+ }) }
+
+
+ { LocalizeText('wiredfurni.params.setaltitude') }
+ setAltitudeInput(formatAltitude(altitude)) }
+ onChange={ event => updateAltitudeInput(event.target.value) } />
+
+
+ updateAltitude(event as number) } />
+ { formatAltitude(altitude) }
+
+
+ );
+};
diff --git a/src/components/wired/views/conditions/WiredConditionLayoutView.tsx b/src/components/wired/views/conditions/WiredConditionLayoutView.tsx
index a1a88c2..7783540 100644
--- a/src/components/wired/views/conditions/WiredConditionLayoutView.tsx
+++ b/src/components/wired/views/conditions/WiredConditionLayoutView.tsx
@@ -5,7 +5,11 @@ import { WiredConditionActorIsOnFurniView } from './WiredConditionActorIsOnFurni
import { WiredConditionActorIsTeamMemberView } from './WiredConditionActorIsTeamMemberView';
import { WiredConditionActorIsWearingBadgeView } from './WiredConditionActorIsWearingBadgeView';
import { WiredConditionActorIsWearingEffectView } from './WiredConditionActorIsWearingEffectView';
+// import { WiredConditionCounterTimeMatchesView } from './WiredConditionCounterTimeMatchesView';
import { WiredConditionDateRangeView } from './WiredConditionDateRangeView';
+import { WiredConditionMatchDateView } from './WiredConditionMatchDateView';
+import { WiredConditionMatchTimeView } from './WiredConditionMatchTimeView';
+import { WiredConditionHasAltitudeView } from './WiredConditionHasAltitudeView';
import { WiredConditionFurniHasAvatarOnView } from './WiredConditionFurniHasAvatarOnView';
import { WiredConditionFurniHasFurniOnView } from './WiredConditionFurniHasFurniOnView';
import { WiredConditionFurniHasNotFurniOnView } from './WiredConditionFurniHasNotFurniOnView';
@@ -13,6 +17,10 @@ import { WiredConditionFurniIsOfTypeView } from './WiredConditionFurniIsOfTypeVi
import { WiredConditionFurniMatchesSnapshotView } from './WiredConditionFurniMatchesSnapshotView';
import { WiredConditionTimeElapsedLessView } from './WiredConditionTimeElapsedLessView';
import { WiredConditionTimeElapsedMoreView } from './WiredConditionTimeElapsedMoreView';
+import { WiredConditionTeamHasRankView } from './WiredConditionTeamHasRankView';
+import { WiredConditionTeamHasScoreView } from './WiredConditionTeamHasScoreView';
+import { WiredConditionTriggererMatchView } from './WiredConditionTriggererMatchView';
+import { WiredConditionUserPerformsActionView } from './WiredConditionUserPerformsActionView';
import { WiredConditionUserCountInRoomView } from './WiredConditionUserCountInRoomView';
export const WiredConditionLayoutView = (code: number) =>
@@ -20,7 +28,11 @@ export const WiredConditionLayoutView = (code: number) =>
switch(code)
{
case WiredConditionlayout.ACTOR_HAS_HANDITEM:
+ case WiredConditionlayout.NOT_ACTOR_HAS_HANDITEM:
return
;
+ case WiredConditionlayout.TRIGGERER_MATCH:
+ case WiredConditionlayout.NOT_TRIGGERER_MATCH:
+ return
;
case WiredConditionlayout.ACTOR_IS_GROUP_MEMBER:
case WiredConditionlayout.NOT_ACTOR_IN_GROUP:
return
;
@@ -38,6 +50,10 @@ export const WiredConditionLayoutView = (code: number) =>
return
;
case WiredConditionlayout.DATE_RANGE_ACTIVE:
return
;
+ case WiredConditionlayout.MATCH_TIME:
+ return
;
+ case WiredConditionlayout.MATCH_DATE:
+ return
;
case WiredConditionlayout.FURNIS_HAVE_AVATARS:
case WiredConditionlayout.FURNI_NOT_HAVE_HABBO:
return
;
@@ -58,6 +74,18 @@ export const WiredConditionLayoutView = (code: number) =>
case WiredConditionlayout.USER_COUNT_IN:
case WiredConditionlayout.NOT_USER_COUNT_IN:
return
;
+ case WiredConditionlayout.COUNTER_TIME_MATCHES:
+ return
;
+ case WiredConditionlayout.USER_PERFORMS_ACTION:
+ return
;
+ case WiredConditionlayout.NOT_USER_PERFORMS_ACTION:
+ return
;
+ case WiredConditionlayout.HAS_ALTITUDE:
+ return
;
+ case WiredConditionlayout.TEAM_HAS_SCORE:
+ return
;
+ case WiredConditionlayout.TEAM_HAS_RANK:
+ return
;
}
return null;
diff --git a/src/components/wired/views/conditions/WiredConditionMatchDateView.tsx b/src/components/wired/views/conditions/WiredConditionMatchDateView.tsx
new file mode 100644
index 0000000..f1b9ccc
--- /dev/null
+++ b/src/components/wired/views/conditions/WiredConditionMatchDateView.tsx
@@ -0,0 +1,195 @@
+import { ChangeEvent, FC, useEffect, useMemo, useState } from 'react';
+import { LocalizeText, WiredFurniType } from '../../../../api';
+import { Text } from '../../../../common';
+import { useWired } from '../../../../hooks';
+import { WiredConditionBaseView } from './WiredConditionBaseView';
+
+const MODE_SKIP = 0;
+const MODE_EXACT = 1;
+const MODE_RANGE = 2;
+const MODE_OPTIONS = [ MODE_SKIP, MODE_EXACT, MODE_RANGE ];
+const WEEKDAY_OPTIONS = [ 1, 2, 3, 4, 5, 6, 7 ];
+const MONTH_OPTIONS = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 ];
+
+const createMask = (values: number[]) => values.reduce((mask, value) => (mask | (1 << value)), 0);
+const ALL_WEEKDAYS_MASK = createMask(WEEKDAY_OPTIONS);
+const ALL_MONTHS_MASK = createMask(MONTH_OPTIONS);
+
+const clampValue = (value: number, min: number, max: number) =>
+{
+ if(isNaN(value)) return min;
+
+ return Math.max(min, Math.min(max, Math.floor(value)));
+};
+
+const parseInputValue = (event: ChangeEvent
, min: number, max: number) =>
+{
+ return clampValue(parseInt(event.target.value || min.toString(), 10), min, max);
+};
+
+const toggleMaskValue = (mask: number, value: number, enabled: boolean) =>
+{
+ if(enabled) return (mask | (1 << value));
+
+ return (mask & ~(1 << value));
+};
+
+const InlineNumberInput: FC<{ value: number; min: number; max: number; onChange: (value: number) => void }> = props =>
+{
+ const { value = 0, min = 0, max = 0, onChange = null } = props;
+
+ return (
+ onChange(parseInputValue(event, min, max)) } />
+ );
+};
+
+interface MatchDateSectionProps
+{
+ sectionId: string;
+ titleKey: string;
+ mode: number;
+ fromValue: number;
+ toValue: number;
+ min: number;
+ max: number;
+ onModeChange: (value: number) => void;
+ onFromChange: (value: number) => void;
+ onToChange: (value: number) => void;
+}
+
+const MatchDateSection: FC = props =>
+{
+ const { sectionId = '', titleKey = '', mode = MODE_SKIP, fromValue = 0, toValue = 0, min = 0, max = 0, onModeChange = null, onFromChange = null, onToChange = null } = props;
+
+ return (
+
+ );
+};
+
+export const WiredConditionMatchDateView: FC<{}> = () =>
+{
+ const { trigger = null, setIntParams = null } = useWired();
+ const currentYear = useMemo(() => new Date().getFullYear(), []);
+ const [ weekdayMask, setWeekdayMask ] = useState(ALL_WEEKDAYS_MASK);
+ const [ dayMode, setDayMode ] = useState(MODE_SKIP);
+ const [ dayFrom, setDayFrom ] = useState(1);
+ const [ dayTo, setDayTo ] = useState(31);
+ const [ monthMask, setMonthMask ] = useState(ALL_MONTHS_MASK);
+ const [ yearMode, setYearMode ] = useState(MODE_SKIP);
+ const [ yearFrom, setYearFrom ] = useState(currentYear);
+ const [ yearTo, setYearTo ] = useState(currentYear);
+
+ useEffect(() =>
+ {
+ if(!trigger) return;
+
+ setWeekdayMask((trigger.intData[0] && (trigger.intData[0] > 0)) ? trigger.intData[0] : ALL_WEEKDAYS_MASK);
+ setDayMode(MODE_OPTIONS.includes(trigger.intData[1]) ? trigger.intData[1] : MODE_SKIP);
+ setDayFrom(clampValue(trigger.intData[2] ?? 1, 1, 31));
+ setDayTo(clampValue(trigger.intData[3] ?? 31, 1, 31));
+ setMonthMask((trigger.intData[4] && (trigger.intData[4] > 0)) ? trigger.intData[4] : ALL_MONTHS_MASK);
+ setYearMode(MODE_OPTIONS.includes(trigger.intData[5]) ? trigger.intData[5] : MODE_SKIP);
+ setYearFrom(clampValue(trigger.intData[6] ?? currentYear, 1, 9999));
+ setYearTo(clampValue(trigger.intData[7] ?? currentYear, 1, 9999));
+ }, [ currentYear, trigger ]);
+
+ const save = () =>
+ {
+ setIntParams([
+ weekdayMask || ALL_WEEKDAYS_MASK,
+ dayMode,
+ clampValue(dayFrom, 1, 31),
+ clampValue(dayTo, 1, 31),
+ monthMask || ALL_MONTHS_MASK,
+ yearMode,
+ clampValue(yearFrom, 1, 9999),
+ clampValue(yearTo, 1, 9999)
+ ]);
+ };
+
+ return (
+
+
+
+
{ LocalizeText('wiredfurni.params.time.weekday_selection') }
+
+ { WEEKDAY_OPTIONS.map(value =>
+ {
+ const checked = ((weekdayMask & (1 << value)) !== 0);
+
+ return (
+
+ );
+ }) }
+
+
+
setDayFrom(clampValue(value, 1, 31)) }
+ onModeChange={ setDayMode }
+ onToChange={ value => setDayTo(clampValue(value, 1, 31)) } />
+
+
{ LocalizeText('wiredfurni.params.time.month_selection') }
+
+ { MONTH_OPTIONS.map(value =>
+ {
+ const checked = ((monthMask & (1 << value)) !== 0);
+
+ return (
+
+ );
+ }) }
+
+
+ setYearFrom(clampValue(value, 1, 9999)) }
+ onModeChange={ setYearMode }
+ onToChange={ value => setYearTo(clampValue(value, 1, 9999)) } />
+
+
+ );
+};
diff --git a/src/components/wired/views/conditions/WiredConditionMatchTimeView.tsx b/src/components/wired/views/conditions/WiredConditionMatchTimeView.tsx
new file mode 100644
index 0000000..426ab99
--- /dev/null
+++ b/src/components/wired/views/conditions/WiredConditionMatchTimeView.tsx
@@ -0,0 +1,163 @@
+import { ChangeEvent, FC, useEffect, useState } from 'react';
+import { LocalizeText, WiredFurniType } from '../../../../api';
+import { Text } from '../../../../common';
+import { useWired } from '../../../../hooks';
+import { WiredConditionBaseView } from './WiredConditionBaseView';
+
+const MODE_SKIP = 0;
+const MODE_EXACT = 1;
+const MODE_RANGE = 2;
+const MODE_OPTIONS = [ MODE_SKIP, MODE_EXACT, MODE_RANGE ];
+
+const clampValue = (value: number, min: number, max: number) =>
+{
+ if(isNaN(value)) return min;
+
+ return Math.max(min, Math.min(max, Math.floor(value)));
+};
+
+interface TimeFilterSectionProps
+{
+ sectionId: string;
+ titleKey: string;
+ min: number;
+ max: number;
+ mode: number;
+ fromValue: number;
+ toValue: number;
+ onModeChange: (value: number) => void;
+ onFromChange: (value: number) => void;
+ onToChange: (value: number) => void;
+}
+
+const parseInputValue = (event: ChangeEvent, min: number, max: number) =>
+{
+ return clampValue(parseInt(event.target.value || min.toString(), 10), min, max);
+};
+
+const InlineNumberInput: FC<{ value: number; min: number; max: number; onChange: (value: number) => void }> = props =>
+{
+ const { value = 0, min = 0, max = 0, onChange = null } = props;
+
+ return (
+ onChange(parseInputValue(event, min, max)) } />
+ );
+};
+
+const TimeFilterSection: FC = props =>
+{
+ const { sectionId = '', titleKey = '', min = 0, max = 0, mode = MODE_SKIP, fromValue = 0, toValue = 0, onModeChange = null, onFromChange = null, onToChange = null } = props;
+
+ return (
+
+ );
+};
+
+export const WiredConditionMatchTimeView: FC<{}> = () =>
+{
+ const { trigger = null, setIntParams = null } = useWired();
+ const [ hourMode, setHourMode ] = useState(MODE_SKIP);
+ const [ hourFrom, setHourFrom ] = useState(0);
+ const [ hourTo, setHourTo ] = useState(0);
+ const [ minuteMode, setMinuteMode ] = useState(MODE_SKIP);
+ const [ minuteFrom, setMinuteFrom ] = useState(0);
+ const [ minuteTo, setMinuteTo ] = useState(0);
+ const [ secondMode, setSecondMode ] = useState(MODE_SKIP);
+ const [ secondFrom, setSecondFrom ] = useState(0);
+ const [ secondTo, setSecondTo ] = useState(0);
+
+ useEffect(() =>
+ {
+ if(!trigger) return;
+
+ setHourMode(MODE_OPTIONS.includes(trigger.intData[0]) ? trigger.intData[0] : MODE_SKIP);
+ setHourFrom(clampValue(trigger.intData[1] ?? 0, 0, 23));
+ setHourTo(clampValue(trigger.intData[2] ?? 0, 0, 23));
+ setMinuteMode(MODE_OPTIONS.includes(trigger.intData[3]) ? trigger.intData[3] : MODE_SKIP);
+ setMinuteFrom(clampValue(trigger.intData[4] ?? 0, 0, 59));
+ setMinuteTo(clampValue(trigger.intData[5] ?? 0, 0, 59));
+ setSecondMode(MODE_OPTIONS.includes(trigger.intData[6]) ? trigger.intData[6] : MODE_SKIP);
+ setSecondFrom(clampValue(trigger.intData[7] ?? 0, 0, 59));
+ setSecondTo(clampValue(trigger.intData[8] ?? 0, 0, 59));
+ }, [ trigger ]);
+
+ const save = () =>
+ {
+ setIntParams([
+ hourMode,
+ clampValue(hourFrom, 0, 23),
+ clampValue(hourTo, 0, 23),
+ minuteMode,
+ clampValue(minuteFrom, 0, 59),
+ clampValue(minuteTo, 0, 59),
+ secondMode,
+ clampValue(secondFrom, 0, 59),
+ clampValue(secondTo, 0, 59)
+ ]);
+ };
+
+ return (
+
+
+ setHourFrom(clampValue(value, 0, 23)) }
+ onModeChange={ setHourMode }
+ onToChange={ value => setHourTo(clampValue(value, 0, 23)) } />
+ setMinuteFrom(clampValue(value, 0, 59)) }
+ onModeChange={ setMinuteMode }
+ onToChange={ value => setMinuteTo(clampValue(value, 0, 59)) } />
+ setSecondFrom(clampValue(value, 0, 59)) }
+ onModeChange={ setSecondMode }
+ onToChange={ value => setSecondTo(clampValue(value, 0, 59)) } />
+
+
+ );
+};
diff --git a/src/components/wired/views/conditions/WiredConditionTeamHasRankView.tsx b/src/components/wired/views/conditions/WiredConditionTeamHasRankView.tsx
new file mode 100644
index 0000000..1df5347
--- /dev/null
+++ b/src/components/wired/views/conditions/WiredConditionTeamHasRankView.tsx
@@ -0,0 +1,102 @@
+import { FC, useEffect, useState } from 'react';
+import { LocalizeText, WiredFurniType } from '../../../../api';
+import { Text } from '../../../../common';
+import { useWired } from '../../../../hooks';
+import { WiredSourcesSelector } from '../WiredSourcesSelector';
+import { WiredConditionBaseView } from './WiredConditionBaseView';
+
+const TEAM_OPTIONS = [ 0, 1, 2, 3, 4 ];
+const PLACEMENT_OPTIONS = [ 1, 2, 3, 4 ];
+
+export const WiredConditionTeamHasRankView: FC<{}> = () =>
+{
+ const { trigger = null, setIntParams = null } = useWired();
+ const [ team, setTeam ] = useState(1);
+ const [ placement, setPlacement ] = useState(1);
+ const [ userSource, setUserSource ] = useState(0);
+ const [ quantifier, setQuantifier ] = useState(0);
+ const [ showAdvanced, setShowAdvanced ] = useState(false);
+
+ useEffect(() =>
+ {
+ if(!trigger) return;
+
+ const nextTeam = (trigger.intData.length > 0) ? trigger.intData[0] : 1;
+ const nextPlacement = (trigger.intData.length > 1) ? trigger.intData[1] : 1;
+ const nextUserSource = (trigger.intData.length > 2) ? trigger.intData[2] : 0;
+ const nextQuantifier = (trigger.intData.length > 3) ? trigger.intData[3] : 0;
+
+ setTeam(TEAM_OPTIONS.includes(nextTeam) ? nextTeam : 1);
+ setPlacement(PLACEMENT_OPTIONS.includes(nextPlacement) ? nextPlacement : 1);
+ setUserSource(nextUserSource);
+ setQuantifier((nextQuantifier === 1) ? 1 : 0);
+ setShowAdvanced(nextUserSource !== 0 || nextQuantifier !== 0);
+ }, [ trigger ]);
+
+ const save = () =>
+ {
+ setIntParams([
+ team,
+ placement,
+ userSource,
+ quantifier
+ ]);
+ };
+
+ return (
+
+
+ { showAdvanced &&
+ <>
+
+
{ LocalizeText('wiredfurni.params.quantifier_selection') }
+ { [ 0, 1 ].map(value =>
+ {
+ return (
+
+ setQuantifier(value) } />
+ { LocalizeText(`wiredfurni.params.quantifier.users.${ value }`) }
+
+ );
+ }) }
+
+
+ > }
+
+ }>
+