Play liveChoose your opponent
Join
games
What shoal-games changes in this game
Branched from main at 1b3bc26 by @shoal shoal · 1 commit ahead, 0 behind main
Continue
Contribute back
91 lines · 6.5 KB Raw Permalink History
1// The 确定 / 取消 / 结束回合 buttons, as 无名杀 shows them above your hand. Which buttons show, when they are enabled and
2// what they do is the upstream's CenterActionBar (src/client/components/CenterActionBar.tsx), condition for condition;
3// only the labels are 无名杀's: 确定 for 出牌, 打出, 使用 and 确认弃牌, 取消 for 不回应 and 取消选择.
4import type { PendingView } from '../../../src/engine/types';
5import type { PlayInteractionResult } from '../../../src/client/hooks/usePlayInteraction';
6import { useGameView } from '../../../src/client/components/GameViewCtx';
7import { displaySkillName } from '../../../src/client/utils/skillDisplay';
8
9interface Props {
10 play: PlayInteractionResult;
11 pending: PendingView | null;
12 isRespondPending: boolean;
13 respondCandidateCount: number | null;
14 showCancelSelection: boolean;
15 showEndTurn: boolean;
16 isDiscardPhase: boolean;
17 isPerspectiveAwaiting: boolean;
18 discardMin: number;
19 discardMax: number;
20}
21
22type Btn = { key: string; label: string; kind: 'ok' | 'cancel' | 'end' | 'plain'; onClick: () => void; disabled?: boolean; title?: string };
23
24export function ActionBar({ play, pending, isRespondPending, respondCandidateCount, showCancelSelection, showEndTurn, isDiscardPhase, isPerspectiveAwaiting, discardMin, discardMax }: Props) {
25 const { canOperate } = useGameView();
26 const {
27 selectedCardId, selectedTarget, selectedForDiscard, transformMode, transformWrapperName, distributeMode, activeDistribute, isDistributeActive, distSelected, distAllocations,
28 distTargetName, selectedActive, playButtonState, transformSubmit, selectedRespondCardId, respondTargetReady, altActions, handlePlayCard, handleSkillAction, handleTransformPlay,
29 isRespondTransformContext, handleRespond, handlePlayRespond, handleEndTurn, handleConfirmDiscard, handleDiscardSelectAll, handleTransformSelectAll, handleTransformInvert,
30 handleDistSubmit, handleDistClear, handleDistSelectAll, handleDistInvert, cancelSelection, setDistributeMode,
31 } = play;
32 const b: Btn[] = [];
33 const weapon = pending?.prompt?.type === 'useCardAndTarget';
34
35 if (isRespondPending) {
36 if (respondCandidateCount !== 0) b.push({ key: 'respond', label: '确定', kind: 'ok', onClick: handlePlayRespond, disabled: !selectedRespondCardId || !respondTargetReady });
37 b.push({ key: 'skip', label: weapon ? '交出武器' : '取消', kind: 'cancel', onClick: () => handleRespond(), title: respondCandidateCount === 0 ? '没有可回应的牌' : undefined });
38 }
39 if (canOperate && transformMode && transformMode.minCards > 1) {
40 const ids = transformMode.selectedCardIds;
41 b.push({ key: 't-all', label: '全选', kind: 'plain', onClick: handleTransformSelectAll, disabled: ids.length >= transformMode.maxCards });
42 b.push({ key: 't-inv', label: '反选', kind: 'plain', onClick: handleTransformInvert, disabled: ids.length === 0 });
43 }
44 if (canOperate && (selectedActive || isRespondTransformContext) && transformMode && transformMode.minCards > 1) {
45 const ok = transformSubmit?.canSubmit ?? false;
46 const needsTarget = transformSubmit?.needsTarget ?? false;
47 b.push({ key: 't-go', label: '确定', kind: 'ok', onClick: () => ok && handleTransformPlay(needsTarget ? selectedTarget! : ''), disabled: !ok, title: `使用${transformWrapperName ?? transformMode.wrapperName}` });
48 }
49 if (canOperate && (selectedActive || isRespondTransformContext) && transformMode?.minCards === 1 && selectedCardId) {
50 if (isRespondTransformContext) b.push({ key: 't-one', label: '确定', kind: 'ok', onClick: () => handleTransformPlay(''), title: `使用${transformWrapperName ?? transformMode.wrapperName}` });
51 else b.push({ key: 't-one', label: '确定', kind: 'ok', onClick: () => selectedTarget && handleTransformPlay(selectedTarget), disabled: !selectedTarget, title: `使用${transformWrapperName ?? transformMode.wrapperName}` });
52 }
53 if (canOperate && selectedActive && !transformMode && selectedCardId && playButtonState)
54 b.push({ key: 'play', label: '确定', kind: 'ok', onClick: handlePlayCard, disabled: !playButtonState.canPlay, title: `出牌${playButtonState.targetLabel}` });
55 if (canOperate && !transformMode && selectedCardId) for (const a of altActions) b.push({ key: `${a.skillId}:${a.actionType}`, label: displaySkillName(a.label), kind: 'plain', onClick: () => handleSkillAction(a) });
56 if (!transformMode && showCancelSelection) b.push({ key: 'cancel', label: '取消', kind: 'cancel', onClick: cancelSelection });
57 if (showEndTurn) b.push({ key: 'end', label: '结束回合', kind: 'end', onClick: handleEndTurn });
58 if (canOperate && isDiscardPhase && isPerspectiveAwaiting) {
59 const n = selectedForDiscard.length;
60 b.push({ key: 'discard', label: '确定', kind: 'ok', onClick: handleConfirmDiscard, disabled: n < discardMin || n > discardMax, title: `弃置 ${n}/${discardMin}` });
61 b.push({ key: 'd-all', label: '全选', kind: 'plain', onClick: handleDiscardSelectAll, disabled: n >= discardMax });
62 }
63 if (canOperate && isDistributeActive && activeDistribute) {
64 const mode = activeDistribute.prompt.mode ?? 'allocate';
65 const minTotal = activeDistribute.prompt.minTotal ?? 1;
66 const maxTotal = activeDistribute.prompt.maxTotal ?? 99;
67 const ok =
68 mode === 'select'
69 ? distSelected.size >= minTotal && distSelected.size <= maxTotal
70 : activeDistribute.externalTargetSelection
71 ? distSelected.size >= minTotal && distSelected.size <= maxTotal && !!distTargetName
72 : distAllocations.flatMap((a) => a.cardIds).length >= minTotal;
73 if (mode === 'select') {
74 b.push({ key: 'x-all', label: '全选', kind: 'plain', onClick: handleDistSelectAll, disabled: distSelected.size >= activeDistribute.cardIds.length });
75 b.push({ key: 'x-inv', label: '反选', kind: 'plain', onClick: handleDistInvert, disabled: distSelected.size === 0 });
76 }
77 b.push({ key: 'x-clear', label: '重选', kind: 'plain', onClick: handleDistClear, disabled: distSelected.size === 0 && distAllocations.length === 0 });
78 b.push({ key: 'x-go', label: '确定', kind: 'ok', onClick: handleDistSubmit, disabled: !ok, title: activeDistribute.prompt.title });
79 if (distributeMode) b.push({ key: 'x-cancel', label: '取消', kind: 'cancel', onClick: () => setDistributeMode(null) });
80 }
81 if (!b.length) return null;
82 return (
83 <div className="nm-control">
84 {b.map(({ key, label, kind, onClick, disabled, title }) => (
85 <button key={key} className={`nm-btn ${kind}`} onClick={onClick} disabled={disabled} title={title}>
86 {label}
87 </button>
88 ))}
89 </div>
90 );
91}