Files
mahjong-web/src/store/gameStore.ts
wsy182 4f7a54cf08 refactor(game): 重构缺门花色处理逻辑并优化组件结构
- 移除硬编码的花色图标导入,改用动态加载方式
- 添加新的 flowerColorMap 配置文件统一管理缺门图标
- 引入 clearActiveRoom 函数用于清理活动房间状态
- 在游戏数据解析中添加缺失花色的读取函数
- 当房间数据为空时自动清理房间状态并跳转回大厅
- 统一玩家缺门花色数据处理逻辑
- 注释掉浮动状态显示区域以优化界面布局
- 调整CSS样式中缺门标记尺寸和旋转效果
- 在游戏存储模块中添加清除快照功能
- 重构座位玩家卡片组件中的花色图标计算逻辑
- 优化花色标签映射和归一化处理函数
2026-03-28 09:59:44 +08:00

211 lines
7.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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<string, GameState['phase']> = {
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] || ''
},
},
})