diff --git a/src/assets/styles/room.css b/src/assets/styles/room.css index f170ce1..6443de8 100644 --- a/src/assets/styles/room.css +++ b/src/assets/styles/room.css @@ -896,11 +896,57 @@ } .bottom-action-bar { + position: relative; display: flex; + align-items: center; + gap: 12px; justify-content: flex-end; + min-height: 56px; margin-bottom: 10px; } +.ding-que-bar { + position: absolute; + right: 150px; + top: -90px; + display: flex; + align-items: center; + gap: 10px; +} + +.ding-que-button { + min-width: 64px; + height: 46px; + border: 1px solid rgba(220, 191, 118, 0.24); + border-radius: 999px; + color: #e5c472; + font-size: 16px; + font-weight: 800; + letter-spacing: 0.5px; + background: + linear-gradient(180deg, rgba(14, 55, 40, 0.92), rgba(8, 36, 27, 0.96)), + radial-gradient(circle at 20% 24%, rgba(237, 214, 157, 0.08), transparent 34%); + box-shadow: + inset 0 1px 0 rgba(255, 244, 214, 0.1), + inset 0 -1px 0 rgba(0, 0, 0, 0.22), + 0 8px 18px rgba(0, 0, 0, 0.2); + text-shadow: + -1px 0 rgba(0, 0, 0, 0.38), + 0 1px rgba(0, 0, 0, 0.38), + 1px 0 rgba(0, 0, 0, 0.38), + 0 -1px rgba(0, 0, 0, 0.38); + cursor: pointer; +} + +.ding-que-button:active { + transform: translateY(1px) scale(0.96); +} + +.ding-que-button:disabled { + opacity: 0.56; + cursor: not-allowed; +} + .control-copy { margin-bottom: 10px; text-align: center; diff --git a/src/views/ChengduGamePage.vue b/src/views/ChengduGamePage.vue index ae0b25e..6ee8074 100644 --- a/src/views/ChengduGamePage.vue +++ b/src/views/ChengduGamePage.vue @@ -80,6 +80,7 @@ const wsError = ref('') const leaveRoomPending = ref(false) const readyTogglePending = ref(false) const startGamePending = ref(false) +const dingQuePending = ref(false) let clockTimer: number | null = null let unsubscribe: (() => void) | null = null let pendingRoomInfoRequest = false @@ -345,6 +346,19 @@ const showReadyToggle = computed(() => { return true }) +const showDingQueChooser = computed(() => { + const player = myPlayer.value + if (!player) { + return false + } + + if (gameStore.phase === 'waiting' || gameStore.phase === 'settlement') { + return false + } + + return player.handTiles.length > 0 && !player.missingSuit +}) + function applyPlayerReadyState(playerId: string, ready: boolean): void { const player = gameStore.players[playerId] if (player) { @@ -796,6 +810,7 @@ function handleRoomInfoResponse(message: unknown): void { gameStore.roomId = roomId if (Object.keys(nextPlayers).length > 0) { gameStore.players = nextPlayers + dingQuePending.value = false } const phaseMap: Record = { @@ -1115,6 +1130,7 @@ function handlePlayerHandResponse(message: unknown): void { existingPlayer.handTiles = handTiles existingPlayer.handCount = handTiles.length } + dingQuePending.value = false const room = activeRoom.value if (room && room.roomId === (roomId || gameStore.roomId)) { @@ -1455,6 +1471,21 @@ function startGame(): void { }) } +function chooseDingQue(suit: Tile['suit']): void { + if (dingQuePending.value || !showDingQueChooser.value) { + return + } + + dingQuePending.value = true + sendWsMessage({ + type: 'ding_que', + roomId: gameStore.roomId, + payload: { + suit, + }, + }) +} + function handleLeaveRoom(): void { menuOpen.value = false backHall() @@ -1763,7 +1794,34 @@ onBeforeUnmount(() => {
-
+
+
+ + + +
+