feat(game): 添加麻将牌图片映射配置并优化成都麻将页面
- 新增 tileMap.ts 配置文件,定义麻将牌图片映射逻辑 - 实现根据花色和点数获取对应图片路径的功能 - 添加麻将牌验证和基础牌生成工具函数 - 在 ChengduGamePage.vue 中导入并使用 getTileImage 函数 - 添加服务器响应日志用于调试 - 优化玩家手牌显示区域的布局结构
This commit is contained in:
111
src/config/tileMap.ts
Normal file
111
src/config/tileMap.ts
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
// src/config/tileMap.ts
|
||||||
|
|
||||||
|
export type Suit = 'W' | 'T' | 'B'
|
||||||
|
|
||||||
|
export interface Tile {
|
||||||
|
id: number
|
||||||
|
suit: Suit
|
||||||
|
value: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export type TilePosition = 'bottom'
|
||||||
|
|
||||||
|
const SUIT_INDEX_MAP: Record<Suit, 1 | 2 | 3> = {
|
||||||
|
W: 1, // 万
|
||||||
|
T: 2, // 筒
|
||||||
|
B: 3, // 条
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 当前目录结构:
|
||||||
|
* /src/assets/images/tiles/bottom/p4b1_1.png
|
||||||
|
* /src/assets/images/tiles/bottom/p4b2_1.png
|
||||||
|
* /src/assets/images/tiles/bottom/p4b3_1.png
|
||||||
|
*/
|
||||||
|
function buildTileImageKey(
|
||||||
|
suit: Suit,
|
||||||
|
value: number,
|
||||||
|
position: TilePosition = 'bottom',
|
||||||
|
): string {
|
||||||
|
const suitIndex = SUIT_INDEX_MAP[suit]
|
||||||
|
return `/src/assets/images/tiles/${position}/p4b${suitIndex}_${value}.png`
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通过 Vite 收集所有麻将牌资源
|
||||||
|
*/
|
||||||
|
const tileImageModules = import.meta.glob(
|
||||||
|
'/src/assets/images/tiles/bottom/*.png',
|
||||||
|
{
|
||||||
|
eager: true,
|
||||||
|
import: 'default',
|
||||||
|
},
|
||||||
|
) as Record<string, string>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断是否为合法花色
|
||||||
|
*/
|
||||||
|
export function isValidSuit(suit: string): suit is Suit {
|
||||||
|
return suit === 'W' || suit === 'T' || suit === 'B'
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断是否为合法点数
|
||||||
|
*/
|
||||||
|
export function isValidTileValue(value: number): boolean {
|
||||||
|
return Number.isInteger(value) && value >= 1 && value <= 9
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断是否为合法牌
|
||||||
|
*/
|
||||||
|
export function isValidTile(tile: { suit: string; value: number }): tile is Pick<Tile, 'suit' | 'value'> {
|
||||||
|
return isValidSuit(tile.suit) && isValidTileValue(tile.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据花色 + 点数获取图片路径
|
||||||
|
*/
|
||||||
|
export function getTileImageBySuitAndValue(
|
||||||
|
suit: Suit,
|
||||||
|
value: number,
|
||||||
|
position: TilePosition = 'bottom',
|
||||||
|
): string {
|
||||||
|
if (!isValidTileValue(value)) {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
|
||||||
|
const key = buildTileImageKey(suit, value, position)
|
||||||
|
return tileImageModules[key] || ''
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据 Tile 获取图片路径
|
||||||
|
*/
|
||||||
|
export function getTileImage(
|
||||||
|
tile: Pick<Tile, 'suit' | 'value'>,
|
||||||
|
position: TilePosition = 'bottom',
|
||||||
|
): string {
|
||||||
|
if (!isValidTile(tile)) {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
|
||||||
|
const key = buildTileImageKey(tile.suit, tile.value, position)
|
||||||
|
return tileImageModules[key] || ''
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取全部基础牌
|
||||||
|
*/
|
||||||
|
export function getAllTiles(): Array<Pick<Tile, 'suit' | 'value'>> {
|
||||||
|
const suits: Suit[] = ['W', 'T', 'B']
|
||||||
|
const result: Array<Pick<Tile, 'suit' | 'value'>> = []
|
||||||
|
|
||||||
|
for (const suit of suits) {
|
||||||
|
for (let value = 1; value <= 9; value++) {
|
||||||
|
result.push({suit, value})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
@@ -37,6 +37,7 @@ import {useGameStore} from '../store/gameStore'
|
|||||||
import {setActiveRoom, useActiveRoomState} from '../store'
|
import {setActiveRoom, useActiveRoomState} from '../store'
|
||||||
import type {PlayerState} from '../types/state'
|
import type {PlayerState} from '../types/state'
|
||||||
import type {Tile} from '../types/tile'
|
import type {Tile} from '../types/tile'
|
||||||
|
import {getTileImage} from "../config/tileMap.ts";
|
||||||
|
|
||||||
const gameStore = useGameStore()
|
const gameStore = useGameStore()
|
||||||
const activeRoom = useActiveRoomState()
|
const activeRoom = useActiveRoomState()
|
||||||
@@ -451,6 +452,7 @@ function handleRoomInfoResponse(message: unknown): void {
|
|||||||
const summary = asRecord(payload.summary) ?? asRecord(payload.room_summary) ?? null
|
const summary = asRecord(payload.summary) ?? asRecord(payload.room_summary) ?? null
|
||||||
const publicState = asRecord(payload.public) ?? asRecord(payload.public_state) ?? null
|
const publicState = asRecord(payload.public) ?? asRecord(payload.public_state) ?? null
|
||||||
const privateState = asRecord(payload.private) ?? asRecord(payload.private_state) ?? null
|
const privateState = asRecord(payload.private) ?? asRecord(payload.private_state) ?? null
|
||||||
|
console.log("server response payload: " + payload)
|
||||||
const roomId =
|
const roomId =
|
||||||
readString(summary ?? {}, 'room_id', 'roomId') ||
|
readString(summary ?? {}, 'room_id', 'roomId') ||
|
||||||
readString(publicState ?? {}, 'room_id', 'roomId') ||
|
readString(publicState ?? {}, 'room_id', 'roomId') ||
|
||||||
@@ -1455,8 +1457,8 @@ onBeforeUnmount(() => {
|
|||||||
<span class="ready-toggle-label">开始游戏</span>
|
<span class="ready-toggle-label">开始游戏</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="player-hand" v-if="myHandTiles.length > 0">
|
<div class="player-hand" v-if="myHandTiles.length > 0">
|
||||||
|
|
||||||
<button
|
<button
|
||||||
v-for="tile in myHandTiles"
|
v-for="tile in myHandTiles"
|
||||||
:key="tile.id"
|
:key="tile.id"
|
||||||
|
|||||||
Reference in New Issue
Block a user