refactor: update game pages for room-based multiplayer

Update game implementations to support new room system:
- Arcade matching game with room integration
- Local/Room memory pairs providers
- Complement race game modes
- Memory quiz game
- GameModeContext with room awareness
- MemoryGrid component updates

Games now properly integrate with room-based multiplayer,
moderation, and real-time updates.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Thomas Hallock
2025-10-13 11:25:04 -05:00
parent 31ac958d33
commit 54846bdc3f
13 changed files with 16 additions and 30 deletions

View File

@@ -13,7 +13,7 @@ import { SetupPhase } from './SetupPhase'
export function MemoryPairsGame() {
const router = useRouter()
const { state, exitSession, resetGame, goToSetup, canModifyPlayers } = useMemoryPairs()
const { state, exitSession, resetGame, goToSetup } = useMemoryPairs()
const { setFullscreenElement } = useFullscreen()
const gameRef = useRef<HTMLDivElement>(null)
@@ -34,7 +34,6 @@ export function MemoryPairsGame() {
navTitle={navTitle}
navEmoji={navEmoji}
emphasizeGameContext={state.gamePhase === 'setup'}
canModifyPlayers={canModifyPlayers}
onExitSession={() => {
exitSession()
router.push('/arcade')

View File

@@ -2,7 +2,6 @@
import { type ReactNode, useCallback, useEffect, useMemo, useReducer } from 'react'
import { useRouter } from 'next/navigation'
import { useArcadeRedirect } from '@/hooks/useArcadeRedirect'
import { useViewerId } from '@/hooks/useViewerId'
import { useUserPlayers } from '@/hooks/useUserPlayers'
import { generateGameCards } from '../utils/cardGeneration'
@@ -310,9 +309,6 @@ export function LocalMemoryPairsProvider({ children }: { children: ReactNode })
// LOCAL-ONLY: Get only the current user's players (no room members)
const { data: userPlayers = [] } = useUserPlayers()
// Use arcade redirect to determine button visibility
const { canModifyPlayers } = useArcadeRedirect({ currentGame: 'matching' })
// Build players map from current user's players only
const players = useMemo(() => {
const map = new Map()
@@ -573,7 +569,6 @@ export function LocalMemoryPairsProvider({ children }: { children: ReactNode })
currentGameStatistics,
hasConfigChanged,
canResumeGame,
canModifyPlayers,
startGame,
resumeGame,
flipCard,

View File

@@ -568,7 +568,6 @@ export function RoomMemoryPairsProvider({ children }: { children: ReactNode }) {
currentGameStatistics,
hasConfigChanged,
canResumeGame,
canModifyPlayers: false, // Room-based games: always show buttons (false = show buttons)
startGame,
resumeGame,
flipCard,

View File

@@ -124,7 +124,6 @@ export interface MemoryPairsContextValue {
currentGameStatistics: GameStatistics
gameMode: GameMode // Derived from global context
activePlayers: Player[] // Active player IDs from arena
canModifyPlayers: boolean // Whether players can be added/removed (controls button visibility)
// PAUSE/RESUME: Computed pause/resume values
hasConfigChanged?: boolean

View File

@@ -1,13 +1,10 @@
import { ArcadeGuardedPage } from '@/components/ArcadeGuardedPage'
import { MemoryPairsGame } from './components/MemoryPairsGame'
import { LocalMemoryPairsProvider } from './context/LocalMemoryPairsProvider'
export default function MatchingPage() {
return (
<ArcadeGuardedPage>
<LocalMemoryPairsProvider>
<MemoryPairsGame />
</LocalMemoryPairsProvider>
</ArcadeGuardedPage>
<LocalMemoryPairsProvider>
<MemoryPairsGame />
</LocalMemoryPairsProvider>
)
}

View File

@@ -6,7 +6,7 @@ import { ComplementRaceProvider } from './context/ComplementRaceContext'
export default function ComplementRacePage() {
return (
<PageWithNav navTitle="Speed Complement Race" navEmoji="🏁">
<PageWithNav navTitle="Speed Complement Race" navEmoji="🏁" gameName="complement-race">
<ComplementRaceProvider>
<ComplementRaceGame />
</ComplementRaceProvider>

View File

@@ -6,7 +6,7 @@ import { ComplementRaceProvider } from '../context/ComplementRaceContext'
export default function PracticeModePage() {
return (
<PageWithNav navTitle="Practice Mode" navEmoji="🏁">
<PageWithNav navTitle="Practice Mode" navEmoji="🏁" gameName="complement-race">
<ComplementRaceProvider initialStyle="practice">
<ComplementRaceGame />
</ComplementRaceProvider>

View File

@@ -6,7 +6,7 @@ import { ComplementRaceProvider } from '../context/ComplementRaceContext'
export default function SprintModePage() {
return (
<PageWithNav navTitle="Steam Sprint" navEmoji="🚂">
<PageWithNav navTitle="Steam Sprint" navEmoji="🚂" gameName="complement-race">
<ComplementRaceProvider initialStyle="sprint">
<ComplementRaceGame />
</ComplementRaceProvider>

View File

@@ -6,7 +6,7 @@ import { ComplementRaceProvider } from '../context/ComplementRaceContext'
export default function SurvivalModePage() {
return (
<PageWithNav navTitle="Survival Mode" navEmoji="🔄">
<PageWithNav navTitle="Survival Mode" navEmoji="🔄" gameName="complement-race">
<ComplementRaceProvider initialStyle="survival">
<ComplementRaceGame />
</ComplementRaceProvider>

View File

@@ -31,6 +31,7 @@ export function MemoryPairsGame() {
<PageWithNav
navTitle={navTitle}
navEmoji={navEmoji}
gameName="matching"
emphasizeGameContext={state.gamePhase === 'setup'}
currentPlayerId={state.currentPlayer}
playerScores={state.scores}

View File

@@ -1989,7 +1989,7 @@ export default function MemoryQuizPage() {
}, [state.prefixAcceptanceTimeout])
return (
<PageWithNav navTitle="Memory Lightning" navEmoji="🧠">
<PageWithNav navTitle="Memory Lightning" navEmoji="🧠" gameName="memory-quiz">
<style dangerouslySetInnerHTML={{ __html: globalAnimations }} />
<div

View File

@@ -116,7 +116,9 @@ export interface MemoryGridProps<TCard = any> {
* Unified MemoryGrid component that works for both single-player and multiplayer modes.
* Conditionally enables multiplayer presence features (hover avatars) when configured.
*/
export function MemoryGrid<TCard extends { id: string; matched: boolean; type: string; number: number }>({
export function MemoryGrid<
TCard extends { id: string; matched: boolean; type: string; number: number },
>({
state,
gridConfig,
flipCard,
@@ -137,13 +139,7 @@ export function MemoryGrid<TCard extends { id: string; matched: boolean; type: s
// In room games, check if current player belongs to this user
const currentPlayerMetadata = state.playerMetadata?.[state.currentPlayer || '']
return currentPlayerMetadata?.userId === viewerId
}, [
enableMultiplayerPresence,
gameMode,
state.currentPlayer,
state.playerMetadata,
viewerId,
])
}, [enableMultiplayerPresence, gameMode, state.currentPlayer, state.playerMetadata, viewerId])
if (!state.gameCards.length) {
return null

View File

@@ -89,7 +89,7 @@ export function GameModeProvider({ children }: { children: ReactNode }) {
const players = useMemo(() => {
const map = new Map<string, Player>(localPlayers)
if (roomData) {
if (roomData?.memberPlayers) {
// Add players from other room members (marked as remote)
Object.entries(roomData.memberPlayers).forEach(([userId, memberPlayers]) => {
// Skip the current user's players (already in localPlayers)
@@ -116,7 +116,7 @@ export function GameModeProvider({ children }: { children: ReactNode }) {
const activePlayers = useMemo(() => {
const set = new Set<string>()
if (roomData) {
if (roomData?.memberPlayers) {
// In room mode: all players from all members are active
Object.values(roomData.memberPlayers).forEach((memberPlayers) => {
memberPlayers.forEach((player) => {