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:
@@ -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')
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -31,6 +31,7 @@ export function MemoryPairsGame() {
|
||||
<PageWithNav
|
||||
navTitle={navTitle}
|
||||
navEmoji={navEmoji}
|
||||
gameName="matching"
|
||||
emphasizeGameContext={state.gamePhase === 'setup'}
|
||||
currentPlayerId={state.currentPlayer}
|
||||
playerScores={state.scores}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
Reference in New Issue
Block a user