Files
mahjong-web/src/state/active-room.ts
2026-03-24 15:25:40 +08:00

158 lines
3.8 KiB
TypeScript

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<string, unknown> | null
winners: string[]
scores: Record<string, number>
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<RoomState>(createInitialRoomState())
export function destroyActiveRoomState(): void {
activeRoomState.value = createInitialRoomState()
}
export function resetActiveRoomState(seed?: Partial<RoomState>): 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 ?? [],
})
}