diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 822a720..ed33c60 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -47,6 +47,8 @@ export function App() { setActiveWizardChallenge, showScoreboard, setShowScoreboard, + showPopups, + setShowPopups, registerPlayer, removePlayer, formParty, @@ -202,6 +204,8 @@ export function App() { playerCount={boardState.player_count} isConcluded={isConcluded} onOpenScoreboard={() => setShowScoreboard(true)} + showPopups={showPopups} + onTogglePopups={setShowPopups} /> {/* Main Content Area */} @@ -233,6 +237,8 @@ export function App() { onStepBot={stepActiveBotTurn} isAutoPlaying={isAutoPlaying} onToggleAutoPlay={() => setIsAutoPlaying((prev) => !prev)} + showPopups={showPopups} + onTogglePopups={setShowPopups} /> {/* Sidebar Player Roster with Turn Order & Parties */} @@ -278,10 +284,12 @@ export function App() { /> {/* 3-Bout D20 Battle Modal */} - + {showPopups && ( + + )} {/* Wizard Challenge Reward Selector Prompt Modal */} {/* 3-Bout D20 Wizard Challenge Modal */} - + {showPopups && ( + + )} {/* Game Conclusion Scoreboard Modal */} {showScoreboard && boardState.conclusion && boardState.conclusion.concluded && ( diff --git a/frontend/src/components/Header.tsx b/frontend/src/components/Header.tsx index 902e404..62531ed 100644 --- a/frontend/src/components/Header.tsx +++ b/frontend/src/components/Header.tsx @@ -10,6 +10,8 @@ interface HeaderProps { playerCount: number; isConcluded?: boolean; onOpenScoreboard?: () => void; + showPopups?: boolean; + onTogglePopups?: (value?: boolean) => void; } export const Header: React.FC = ({ @@ -22,6 +24,8 @@ export const Header: React.FC = ({ playerCount, isConcluded, onOpenScoreboard, + showPopups = true, + onTogglePopups, }) => { return (
@@ -108,6 +112,33 @@ export const Header: React.FC = ({ )} + {/* Battle & Wizard Result Popups Toggle */} + + void; isAutoPlaying: boolean; onToggleAutoPlay: () => void; + showPopups?: boolean; + onTogglePopups?: (value?: boolean) => void; } export const MovementControls: React.FC = ({ @@ -26,6 +28,8 @@ export const MovementControls: React.FC = ({ onStepBot, isAutoPlaying, onToggleAutoPlay, + showPopups = true, + onTogglePopups, }) => { const isStarted = Boolean(boardState.turn?.game_started); const currentTurnId = boardState.turn.current_player_id; @@ -382,6 +386,28 @@ export const MovementControls: React.FC = ({ + {/* Result Popups Toggle */} +
+ +
+
{isStarted ? 'WASD / Arrows / Numpad to move' : 'Bots can join & depart in Lobby'}
diff --git a/frontend/src/hooks/useGameSocket.ts b/frontend/src/hooks/useGameSocket.ts index 37bc145..0e9117a 100644 --- a/frontend/src/hooks/useGameSocket.ts +++ b/frontend/src/hooks/useGameSocket.ts @@ -51,6 +51,7 @@ export function useGameSocket() { const [activeBattle, setActiveBattle] = useState(null); const [activeWizardChallenge, setActiveWizardChallenge] = useState(null); const [showScoreboard, setShowScoreboard] = useState(false); + const [showPopups, setShowPopups] = useState(true); const wsRef = useRef(null); const reconnectTimeoutRef = useRef(null); @@ -59,6 +60,20 @@ export function useGameSocket() { activeBattleRef.current = activeBattle; const activeWizardChallengeRef = useRef(null); activeWizardChallengeRef.current = activeWizardChallenge; + const showPopupsRef = useRef(true); + showPopupsRef.current = showPopups; + + const updateShowPopups = useCallback((val?: boolean | ((prev: boolean) => boolean)) => { + setShowPopups((prev) => { + const next = typeof val === 'function' ? val(prev) : typeof val === 'boolean' ? val : !prev; + showPopupsRef.current = next; + if (!next) { + setActiveBattle(null); + setActiveWizardChallenge(null); + } + return next; + }); + }, []); const fetchBoard = useCallback(async () => { try { @@ -205,7 +220,9 @@ export function useGameSocket() { turn: data.turn ?? prev.turn, })); const b: BattleResult = data.battle; - setActiveBattle(b); + if (showPopupsRef.current) { + setActiveBattle(b); + } setLastEventMessage(`⚔️ 3-Bout D20 Battle: ${b.winner_party_name} defeated ${b.defeated_party_name}!`); } else if (data.event === 'wizard_challenge_resolved') { setBoardState((prev) => ({ @@ -215,7 +232,9 @@ export function useGameSocket() { turn: data.turn ?? prev.turn, })); const c: WizardChallengeResult = data.challenge_result || data.challenge; - setActiveWizardChallenge(c); + if (showPopupsRef.current) { + setActiveWizardChallenge(c); + } const wizName = c.wizard_name || 'Gary the Wizard'; let rewardLabel = `+${c.score_change} score`; if (c.reward_chosen === 'strength') { @@ -413,7 +432,9 @@ export function useGameSocket() { throw new Error(err.detail || 'Battle failed'); } const result: BattleResult = await res.json(); - setActiveBattle(result); + if (showPopupsRef.current) { + setActiveBattle(result); + } return result; }; @@ -432,7 +453,9 @@ export function useGameSocket() { setSelectedPlayer((curr) => (curr?.id === data.player.id ? data.player : curr)); } if (data.battle_triggered && data.battle_result) { - setActiveBattle(data.battle_result); + if (showPopupsRef.current) { + setActiveBattle(data.battle_result); + } } if (data.game_concluded && data.game_concluded.concluded) { setIsAutoPlaying(false); @@ -490,7 +513,9 @@ export function useGameSocket() { throw new Error(err.detail || 'Failed to challenge wizard'); } const data: WizardChallengeResult = await res.json(); - setActiveWizardChallenge(data); + if (showPopupsRef.current) { + setActiveWizardChallenge(data); + } return data; }; @@ -515,11 +540,15 @@ export function useGameSocket() { if (data.action_taken === 'formed_party' && data.formed_party) { setLastEventMessage(`🤝 ${data.player_name} formed party "${data.formed_party.name}" under leader ${data.formed_party.leader_name}!`); } else if (data.action_taken === 'battled' && data.battle_result) { - setActiveBattle(data.battle_result); + if (showPopupsRef.current) { + setActiveBattle(data.battle_result); + } setLastEventMessage(`⚔️ Battle clash: ${data.battle_result.winner_party_name} defeated ${data.battle_result.defeated_party_name}!`); } else if (data.action_taken === 'challenged_wizard' && data.wizard_challenge_result) { const wcr = data.wizard_challenge_result; - setActiveWizardChallenge(wcr); + if (showPopupsRef.current) { + setActiveWizardChallenge(wcr); + } const wizName = wcr.wizard_name || 'Gary the Wizard'; setLastEventMessage( wcr.player_won @@ -529,7 +558,9 @@ export function useGameSocket() { } else if (data.action_taken === 'slept' && data.sleep_result) { setLastEventMessage(`💤 ${data.player_name} took a restful sleep (+${data.sleep_result.health_gained} HP -> ${data.sleep_result.new_health} HP)!`); } else if (data.move_result?.battle_result) { - setActiveBattle(data.move_result.battle_result); + if (showPopupsRef.current) { + setActiveBattle(data.move_result.battle_result); + } } if (data.game_concluded && data.game_concluded.concluded) { @@ -575,6 +606,8 @@ export function useGameSocket() { setActiveWizardChallenge, showScoreboard, setShowScoreboard, + showPopups, + setShowPopups: updateShowPopups, registerPlayer, removePlayer, formParty, diff --git a/version.ini b/version.ini index db5e20d..1688f5c 100644 --- a/version.ini +++ b/version.ini @@ -1,2 +1,2 @@ [metadata] -version = 1.4 +version = 1.5