feat: Implement smart auto-pass for non-active players and add a UI button to suspend it.
This commit is contained in:
@@ -78,6 +78,7 @@ export const GameView: React.FC<GameViewProps> = ({ gameState, currentPlayerId }
|
|||||||
const [hoveredCard, setHoveredCard] = useState<CardInstance | null>(null);
|
const [hoveredCard, setHoveredCard] = useState<CardInstance | null>(null);
|
||||||
const [dragAnimationMode, setDragAnimationMode] = useState<'start' | 'end'>('end');
|
const [dragAnimationMode, setDragAnimationMode] = useState<'start' | 'end'>('end');
|
||||||
const [previewTappedIds, setPreviewTappedIds] = useState<Set<string>>(new Set());
|
const [previewTappedIds, setPreviewTappedIds] = useState<Set<string>>(new Set());
|
||||||
|
const [stopRequested, setStopRequested] = useState(false);
|
||||||
|
|
||||||
// Auto-Pass Priority if Yielding
|
// Auto-Pass Priority if Yielding
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -162,7 +163,31 @@ export const GameView: React.FC<GameViewProps> = ({ gameState, currentPlayerId }
|
|||||||
// If I manually enable it, isBotTurn is false. We simply don't interfere.
|
// If I manually enable it, isBotTurn is false. We simply don't interfere.
|
||||||
// This allows accurate Manual Yielding!
|
// This allows accurate Manual Yielding!
|
||||||
}
|
}
|
||||||
}, [gameState.activePlayerId, gameState.step, isBotTurn]); // Removed isYielding dependency to avoid loops?
|
}, [gameState.activePlayerId, gameState.step, isBotTurn]);
|
||||||
|
|
||||||
|
// --- Smart Auto-Pass (Suspend Logic) ---
|
||||||
|
useEffect(() => {
|
||||||
|
setStopRequested(false);
|
||||||
|
}, [gameState.step, gameState.turn]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// Smart Auto-Pass Logic for NAP
|
||||||
|
if (!gameState.activePlayerId) return;
|
||||||
|
const amActivePlayer = gameState.activePlayerId === currentPlayerId;
|
||||||
|
const amPriorityPlayer = gameState.priorityPlayerId === currentPlayerId;
|
||||||
|
|
||||||
|
// Condition: I am NAP, I have Priority, and I have NOT requested a stop.
|
||||||
|
// Logic: Auto-Pass.
|
||||||
|
if (!amActivePlayer && amPriorityPlayer && !stopRequested) {
|
||||||
|
if (gameState.step === 'declare_blockers') return; // Explicit wait for blockers
|
||||||
|
|
||||||
|
console.log("[Smart Auto-Pass] Auto-passing priority as NAP (No Suspend requested).");
|
||||||
|
const timer = setTimeout(() => {
|
||||||
|
socketService.socket.emit('game_strict_action', { action: { type: 'PASS_PRIORITY' } });
|
||||||
|
}, 800);
|
||||||
|
return () => clearTimeout(timer);
|
||||||
|
}
|
||||||
|
}, [gameState.activePlayerId, gameState.priorityPlayerId, stopRequested, currentPlayerId, gameState.step]);
|
||||||
// If I access `isYielding` inside `setIsYielding`, I don't need it in dependency.
|
// If I access `isYielding` inside `setIsYielding`, I don't need it in dependency.
|
||||||
// But wait, the `if (['declare_...'].includes)` logic needs to potentially set it false.
|
// But wait, the `if (['declare_...'].includes)` logic needs to potentially set it false.
|
||||||
// setIsYielding(false) is fine.
|
// setIsYielding(false) is fine.
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ interface PhaseStripProps {
|
|||||||
contextData?: any;
|
contextData?: any;
|
||||||
isYielding?: boolean;
|
isYielding?: boolean;
|
||||||
onYieldToggle?: () => void;
|
onYieldToggle?: () => void;
|
||||||
|
stopRequested?: boolean;
|
||||||
|
onToggleSuspend?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const PhaseStrip: React.FC<PhaseStripProps> = ({
|
export const PhaseStrip: React.FC<PhaseStripProps> = ({
|
||||||
@@ -18,7 +20,9 @@ export const PhaseStrip: React.FC<PhaseStripProps> = ({
|
|||||||
onAction,
|
onAction,
|
||||||
contextData,
|
contextData,
|
||||||
isYielding,
|
isYielding,
|
||||||
onYieldToggle
|
onYieldToggle,
|
||||||
|
stopRequested,
|
||||||
|
onToggleSuspend
|
||||||
}) => {
|
}) => {
|
||||||
const currentPhase = gameState.phase as Phase;
|
const currentPhase = gameState.phase as Phase;
|
||||||
const currentStep = gameState.step as Step;
|
const currentStep = gameState.step as Step;
|
||||||
@@ -63,12 +67,6 @@ export const PhaseStrip: React.FC<PhaseStripProps> = ({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (currentStep === 'declare_blockers') {
|
} else if (currentStep === 'declare_blockers') {
|
||||||
// If it's MY turn (Active Player), I should NEVER verify blocks myself?
|
|
||||||
// Actually Rules say AP gets priority after blocks.
|
|
||||||
// So if I have priority, it MUST mean blocks are done (or I'm waiting for them, but then I wouldn't have priority?)
|
|
||||||
// Wait, if I am AP, and I have priority in this step, it means blocks are implicitly done (flag should be true).
|
|
||||||
// Fallback: If I am Active Player, always show "To Damage".
|
|
||||||
|
|
||||||
const showToDamage = gameState.blockersDeclared || isMyTurn; // UI Safety for AP
|
const showToDamage = gameState.blockersDeclared || isMyTurn; // UI Safety for AP
|
||||||
|
|
||||||
if (showToDamage) {
|
if (showToDamage) {
|
||||||
@@ -88,19 +86,36 @@ export const PhaseStrip: React.FC<PhaseStripProps> = ({
|
|||||||
else if (gameState.phase === 'main2') actionLabel = "End Turn";
|
else if (gameState.phase === 'main2') actionLabel = "End Turn";
|
||||||
else actionLabel = "Pass";
|
else actionLabel = "Pass";
|
||||||
} else {
|
} else {
|
||||||
// Resolve
|
// Resolve Logic
|
||||||
// const topItem = gameState.stack![gameState.stack!.length - 1]; // Unused
|
|
||||||
actionLabel = "Resolve";
|
actionLabel = "Resolve";
|
||||||
actionType = 'PASS_PRIORITY';
|
actionType = 'PASS_PRIORITY';
|
||||||
ActionIcon = Zap;
|
ActionIcon = Zap;
|
||||||
actionColor = "bg-amber-600 hover:bg-amber-500 shadow-[0_0_10px_rgba(245,158,11,0.4)]";
|
actionColor = "bg-amber-600 hover:bg-amber-500 shadow-[0_0_10px_rgba(245,158,11,0.4)]";
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Waiting
|
// NOT PRIORITY (Waiting)
|
||||||
actionLabel = "Waiting...";
|
// Suspend Button Logic for NAP
|
||||||
ActionIcon = Hand;
|
if (!isMyTurn) {
|
||||||
actionColor = "bg-white/5 text-slate-500 cursor-not-allowed";
|
isActionEnabled = true;
|
||||||
isActionEnabled = false;
|
|
||||||
|
if (stopRequested) {
|
||||||
|
actionLabel = "Stop Set";
|
||||||
|
ActionIcon = Hand;
|
||||||
|
actionColor = "bg-red-600 hover:bg-red-500 animate-pulse font-bold border border-red-400";
|
||||||
|
actionType = 'TOGGLE_SUSPEND';
|
||||||
|
} else {
|
||||||
|
actionLabel = "Suspend";
|
||||||
|
ActionIcon = Hand;
|
||||||
|
actionColor = "bg-yellow-600/80 hover:bg-yellow-500 text-yellow-50 border border-yellow-500/50";
|
||||||
|
actionType = 'TOGGLE_SUSPEND';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// I am AP but don't have priority? (Maybe waiting for server?)
|
||||||
|
actionLabel = "Waiting...";
|
||||||
|
ActionIcon = Hourglass;
|
||||||
|
actionColor = "bg-white/5 text-slate-500 cursor-not-allowed";
|
||||||
|
isActionEnabled = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleAction = (e: React.MouseEvent) => {
|
const handleAction = (e: React.MouseEvent) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user