import { ref } from 'vue' export const DEFAULT_MAX_PLAYERS = 4 export type RoomStatus = 'waiting' | 'playing' | 'finished' export interface RoomPlayerState { index: number playerId: string displayName?: string ready: boolean handCount?: number melds?: string[] outTiles?: string[] hasHu?: boolean missingSuit?: string | null } export interface RuleState { name: string isBloodFlow: boolean hasHongZhong: boolean } export interface GamePlayerState { playerId: string index: number ready: boolean } export interface EngineState { phase: string dealerIndex: number currentTurn: number needDraw: boolean players: GamePlayerState[] wall: string[] lastDiscardTile: string | null lastDiscardBy: string pendingClaim: Record | null winners: string[] scores: Record lastDrawPlayerId: string lastDrawFromGang: boolean lastDrawIsLastTile: boolean huWay: string } export interface GameState { rule: RuleState | null state: EngineState | null } export interface RoomState { id: string name: string gameType: string ownerId: string maxPlayers: number playerCount: number status: RoomStatus | string createdAt: string updatedAt: string game: GameState | null players: RoomPlayerState[] currentTurnIndex: number | null myHand: string[] } function createInitialRoomState(): RoomState { return { id: '', name: '', gameType: 'chengdu', ownerId: '', maxPlayers: DEFAULT_MAX_PLAYERS, playerCount: 0, status: 'waiting', createdAt: '', updatedAt: '', game: null, players: [], currentTurnIndex: null, myHand: [], } } export const activeRoomState = ref(createInitialRoomState()) export function destroyActiveRoomState(): void { activeRoomState.value = createInitialRoomState() } export function resetActiveRoomState(seed?: Partial): void { destroyActiveRoomState() if (!seed) { return } activeRoomState.value = { ...activeRoomState.value, ...seed, players: seed.players ?? [], myHand: seed.myHand ?? [], } } export function mergeActiveRoomState(next: RoomState): void { if (activeRoomState.value.id && next.id && next.id !== activeRoomState.value.id) { return } activeRoomState.value = { ...activeRoomState.value, ...next, name: next.name || activeRoomState.value.name, gameType: next.gameType || activeRoomState.value.gameType, ownerId: next.ownerId || activeRoomState.value.ownerId, status: next.status || activeRoomState.value.status, createdAt: next.createdAt || activeRoomState.value.createdAt, updatedAt: next.updatedAt || activeRoomState.value.updatedAt, game: next.game ?? activeRoomState.value.game, players: next.players.length > 0 ? next.players : activeRoomState.value.players, currentTurnIndex: next.currentTurnIndex !== null ? next.currentTurnIndex : activeRoomState.value.currentTurnIndex, myHand: next.myHand.length > 0 ? next.myHand : activeRoomState.value.myHand, } } export function hydrateActiveRoomFromSelection(input: { roomId: string roomName?: string gameType?: string ownerId?: string maxPlayers?: number playerCount?: number status?: string createdAt?: string updatedAt?: string players?: RoomPlayerState[] currentTurnIndex?: number | null myHand?: string[] }): void { resetActiveRoomState({ id: input.roomId, name: input.roomName ?? '', gameType: input.gameType ?? 'chengdu', ownerId: input.ownerId ?? '', maxPlayers: input.maxPlayers ?? DEFAULT_MAX_PLAYERS, playerCount: input.playerCount ?? 0, status: input.status ?? 'waiting', createdAt: input.createdAt ?? '', updatedAt: input.updatedAt ?? '', players: input.players ?? [], currentTurnIndex: input.currentTurnIndex ?? null, myHand: input.myHand ?? [], }) }