nitro-v36-with-renderer-v36
/
src /
components /
wired /
views /
conditions /
WiredConditionTeamHasScoreView.tsx
WiredConditionTeamHasScoreView.tsx 7.8 Ko
Télécharger le fichier
import { FC, useEffect, useState } from 'react';
import { LocalizeText, localizeWithFallback, WiredFurniType } from '../../../../api';
import { Slider, Text } from '../../../../common';
import { useWired } from '../../../../hooks';
import { normalizeWiredComparison, WIRED_CMP_GREATER_EQUAL, WiredComparisonOperator } from '../WiredComparisonOperator';
import { WiredSourcesSelector } from '../WiredSourcesSelector';
import { WiredConditionBaseView } from './WiredConditionBaseView';
import { WiredConditionQuantifierSection } from './WiredConditionQuantifierSection';
import { Radio } from '../../../../common/Radio';
import { BaseButton } from '../../../../common/BaseButton';
import { NativeTextTree } from '../../../../common/native-text/NativeTextTree';
// 0 is the triggering user's team, as officially.
const TEAM_OPTIONS = [0, 1, 2, 3, 4];
const COMPARISON_OPTIONS = [0, 1, 2];
const MIN_SCORE = 0;
// Official TeamHasScore slider: 0..1000.
const MAX_SCORE = 1000;
/** What the server's normalizeScore lets through. A credit threshold is useless capped at 1000. */
const MAX_AMOUNT = 1_000_000;
const SCORE_PATTERN = /^\d*$/;
const clampScore = (value: number, max: number) => {
if (isNaN(value)) return MIN_SCORE;
return Math.max(MIN_SCORE, Math.min(max, Math.floor(value)));
};
interface WiredConditionTeamHasScoreViewProps {
/**
* The item-count and currency conditions share this dialog for its amount and user source. They have
* no team, and their comparison uses the six shared wired codes rather than
* the three team-score ones, so they pass false and get the shared operator selector instead.
*/
scoped?: boolean;
}
export const WiredConditionTeamHasScoreView: FC<WiredConditionTeamHasScoreViewProps> = ({ scoped = true }) => {
const { trigger = null, setIntParams = null } = useWired();
const ceiling = scoped ? MAX_SCORE : MAX_AMOUNT;
const [team, setTeam] = useState(1);
const [comparison, setComparison] = useState(scoped ? 1 : WIRED_CMP_GREATER_EQUAL);
const [score, setScore] = useState(0);
const [scoreInput, setScoreInput] = useState('0');
const [userSource, setUserSource] = useState(0);
const [showAdvanced, setShowAdvanced] = useState(false);
useEffect(() => {
if (!trigger) return;
// Official team score: [team, points, comparison, ...]; the shared amount boxes keep [_, comparison, amount, ...].
const nextTeam = trigger.intData.length > 0 ? trigger.intData[0] : 1;
const nextComparison = trigger.intData.length > (scoped ? 2 : 1) ? trigger.intData[scoped ? 2 : 1] : scoped ? 1 : WIRED_CMP_GREATER_EQUAL;
const nextScore = clampScore(trigger.intData.length > (scoped ? 1 : 2) ? trigger.intData[scoped ? 1 : 2] : 0, ceiling);
const nextUserSource = trigger.intData.length > 3 ? trigger.intData[3] : 0;
setTeam(TEAM_OPTIONS.includes(nextTeam) && (scoped || nextTeam !== 0) ? nextTeam : 1);
setComparison(scoped ? (COMPARISON_OPTIONS.includes(nextComparison) ? nextComparison : 1) : normalizeWiredComparison(nextComparison));
setScore(nextScore);
setScoreInput(nextScore.toString());
setUserSource(nextUserSource);
setShowAdvanced(nextUserSource !== 0 || (trigger.quantifierCode ?? 0) !== 0);
}, [ceiling, scoped, trigger]);
const updateScore = (value: number) => {
const nextValue = clampScore(value, ceiling);
setScore(nextValue);
setScoreInput(nextValue.toString());
};
const updateScoreInput = (value: string) => {
if (!SCORE_PATTERN.test(value)) return;
setScoreInput(value);
if (!value.length) {
setScore(0);
return;
}
updateScore(parseInt(value));
};
const save = () => {
setIntParams(
scoped
? [team, clampScore(score, ceiling), comparison, userSource]
: [team, comparison, clampScore(score, ceiling), userSource]
);
};
return (
<NativeTextTree>
<WiredConditionBaseView
hasSpecialInput={true}
requiresFurni={WiredFurniType.STUFF_SELECTION_OPTION_NONE}
save={save}
footerCollapsible={false}
showQuantifier={false}
footer={
<div className="flex flex-col gap-2">
<BaseButton className="octane-wired__advanced-toggle" type="button" onClick={() => setShowAdvanced((value) => !value)}>
{LocalizeText(showAdvanced ? 'wiredfurni.params.sources.collapse' : 'wiredfurni.params.sources.expand')}
</BaseButton>
{showAdvanced && (
<>
<WiredConditionQuantifierSection />
<WiredSourcesSelector showUsers={true} userSource={userSource} onChangeUsers={setUserSource} />
</>
)}
</div>
}
>
{!scoped && <WiredComparisonOperator name="userAmountComparison" value={comparison} onChange={setComparison} />}
{scoped && (
<>
<div className="flex flex-col gap-1">
<Text bold>{LocalizeText('wiredfurni.params.team')}</Text>
{TEAM_OPTIONS.map((value) => {
return (
<div key={value} className="flex items-center gap-1">
<Radio checked={team === value} id={`teamHasScore${value}`} name="teamHasScore" onCheckedChange={() => setTeam(value)} />
<Text>{LocalizeText(value === 0 ? 'wiredfurni.params.team.triggerer' : `wiredfurni.params.team.${value}`)}</Text>
</div>
);
})}
</div>
<div className="flex flex-col gap-1">
<Text bold>{LocalizeText('wiredfurni.params.comparison_selection')}</Text>
{COMPARISON_OPTIONS.map((value) => {
return (
<div key={value} className="flex items-center gap-1">
<Radio checked={comparison === value} id={`teamScoreComparison${value}`} name="teamScoreComparison" onCheckedChange={() => setComparison(value)} />
<Text>{LocalizeText(`wiredfurni.params.comparison.${value}`)}</Text>
</div>
);
})}
</div>
</>
)}
<div className="flex flex-col gap-1">
<Text bold>{scoped ? LocalizeText('wiredfurni.params.setscore2') : localizeWithFallback('wiredfurni.params.user_amount', 'Amount:')}</Text>
<input
className="form-control form-control-sm"
inputMode="numeric"
type="text"
value={scoreInput}
onBlur={() => setScoreInput(clampScore(score, ceiling).toString())}
onChange={(event) => updateScoreInput(event.target.value)}
/>
</div>
{scoped && (
<div className="flex flex-col gap-1">
<Slider max={MAX_SCORE} min={MIN_SCORE} step={1} value={score} onChange={(event) => updateScore(event as number)} />
<Text small>{score}</Text>
</div>
)}
</WiredConditionBaseView>
</NativeTextTree>
);
};
