import { defineStore } from 'pinia' import { GAME_PHASE, type GameState, type PendingClaimState, } from '../types/state' import type { RoomPlayerUpdatePayload } from '../game/actions' import type { Tile } from '../types/tile' export const useGameStore = defineStore('game', { state: (): GameState => ({ roomId: '', phase: GAME_PHASE.WAITING, dealerIndex: 0, currentTurn: 0, players: {}, remainingTiles: 0, pendingClaim: undefined, winners: [], scores: {}, }), actions: { resetGame() { this.$reset() }, // 初始化 initGame(data: GameState) { Object.assign(this, data) }, // 摸牌 onDrawTile(data: { playerId: string; tile: Tile }) { const player = this.players[data.playerId] if (!player) return // 只更新自己的手牌 if (player.playerId === this.getMyPlayerId()) { player.handTiles.push(data.tile) } player.handCount += 1 // 剩余牌数减少 this.remainingTiles = Math.max(0, this.remainingTiles - 1) // 更新回合(seatIndex) this.currentTurn = player.seatIndex // 清除操作窗口 this.pendingClaim = undefined // 进入出牌阶段 this.phase = GAME_PHASE.PLAYING }, // 出牌 onPlayTile(data: { playerId: string tile: Tile nextSeat: number }) { const player = this.players[data.playerId] if (!player) return // 如果是自己,移除手牌 if (player.playerId === this.getMyPlayerId()) { const index = player.handTiles.findIndex( (t) => t.id === data.tile.id ) if (index !== -1) { player.handTiles.splice(index, 1) } } player.handCount = Math.max(0, player.handCount - 1) // 加入出牌区 player.discardTiles.push(data.tile) // 更新回合 this.currentTurn = data.nextSeat // 等待其他玩家响应 this.phase = GAME_PHASE.ACTION }, // 触发操作窗口(碰/杠/胡) onPendingClaim(data: PendingClaimState) { this.pendingClaim = data this.phase = GAME_PHASE.ACTION }, onRoomPlayerUpdate(payload: RoomPlayerUpdatePayload) { if (typeof payload.room_id === 'string' && payload.room_id) { this.roomId = payload.room_id } if (typeof payload.status === 'string' && payload.status) { const phaseMap: Record = { waiting: GAME_PHASE.WAITING, dealing: GAME_PHASE.DEALING, playing: GAME_PHASE.PLAYING, action: GAME_PHASE.ACTION, settlement: GAME_PHASE.SETTLEMENT, } this.phase = phaseMap[payload.status] ?? this.phase } const hasPlayerList = Array.isArray(payload.players) || Array.isArray(payload.player_ids) if (!hasPlayerList) { return } const nextPlayers: GameState['players'] = {} const players = Array.isArray(payload.players) ? payload.players : [] const playerIds = Array.isArray(payload.player_ids) ? payload.player_ids : [] players.forEach((raw, index) => { const playerId = (typeof raw.PlayerID === 'string' && raw.PlayerID) || (typeof raw.player_id === 'string' && raw.player_id) || playerIds[index] if (!playerId) { return } const previous = this.players[playerId] const seatRaw = raw.Index ?? raw.index ?? index const seatIndex = typeof seatRaw === 'number' && Number.isFinite(seatRaw) ? seatRaw : index const readyRaw = raw.Ready ?? raw.ready const displayNameRaw = raw.PlayerName ?? raw.player_name const avatarUrlRaw = raw.AvatarUrl ?? raw.avatar_url const missingSuitRaw = raw.MissingSuit ?? raw.missing_suit nextPlayers[playerId] = { playerId, seatIndex, displayName: typeof displayNameRaw === 'string' && displayNameRaw ? displayNameRaw : previous?.displayName, avatarURL: typeof avatarUrlRaw === 'string' ? avatarUrlRaw : previous?.avatarURL, missingSuit: typeof missingSuitRaw === 'string' || missingSuitRaw === null ? missingSuitRaw : previous?.missingSuit, handTiles: previous?.handTiles ?? [], handCount: previous?.handCount ?? 0, melds: previous?.melds ?? [], discardTiles: previous?.discardTiles ?? [], hasHu: previous?.hasHu ?? false, score: previous?.score ?? 0, isReady: typeof readyRaw === 'boolean' ? readyRaw : (previous?.isReady ?? false), } }) if (players.length === 0) { playerIds.forEach((playerId, index) => { if (typeof playerId !== 'string' || !playerId) { return } const previous = this.players[playerId] nextPlayers[playerId] = { playerId, seatIndex: previous?.seatIndex ?? index, displayName: previous?.displayName ?? playerId, avatarURL: previous?.avatarURL, missingSuit: previous?.missingSuit, handTiles: previous?.handTiles ?? [], handCount: previous?.handCount ?? 0, melds: previous?.melds ?? [], discardTiles: previous?.discardTiles ?? [], hasHu: previous?.hasHu ?? false, score: previous?.score ?? 0, isReady: previous?.isReady ?? false, } }) } this.players = nextPlayers }, // 清理操作窗口 clearPendingClaim() { this.pendingClaim = undefined this.phase = GAME_PHASE.PLAYING }, // 获取当前玩家ID(后续建议放到 userStore) getMyPlayerId(): string { return Object.keys(this.players)[0] || '' }, }, })