功能:記分板語音改為報比分、發球區、賽末點與獲勝隊伍

摘要:
- 得分播報改為「比分(發球方先報)+ 發球者左右發球區」
- 發球方到賽末點(再得 1 分即獲勝)時,比分後加唸「賽末點」
- 賽末點得分獲勝時,整段改播「<獲勝隊伍> 贏得比賽」
- 發球區左右一律用實際球場方向,取消上方隊伍鏡像;畫面「發球區」顯示同步改為不鏡像,與語音一致

根本原因:
- 現場記分需要即時聽到比分與發球位置,原本只唸「誰得分、誰發球」較不直覺
- 先前發球區對上方隊伍做鏡像,導致語音與實際球場方向相反

影響:
- src/App.tsx:recordPoint 計算發球方比分、發球區、賽末點與獲勝隊伍,重整 voiceAnnouncement 欄位
- src/pages/ScoreboardPage.tsx:語音組字改為「X比Y(賽末點)」「OO左/右邊發球」、獲勝改播「贏得比賽」;發球區顯示移除鏡像;語音設定「播報誰得分」更名「播報比分」

修法:
- 發球方分數先報;賽末點僅在發球方再得 1 分就獲勝時觸發;發球區統一用 getServiceCourt 實際方向

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-15 17:42:05 +08:00
parent 091e654bdb
commit 9eb60baa37
2 changed files with 19 additions and 12 deletions
+12 -5
View File
@@ -89,6 +89,7 @@ type VoiceAnnouncement = {
opponentScore: number
serverName: string
serverCourt: 'left' | 'right'
matchPoint: boolean
winnerTeamName: string | null
}
@@ -773,13 +774,18 @@ function App() {
: getTeamDisplayName(rightTeam)
: null
// 得分方接著發球,報分以發球方分數為先;左側隊伍的發球區需鏡像對應畫面
// 得分方接著發球,報分以發球方分數為先;發球區一律用實際球場左右,不做鏡像
const servingScore = side === 'left' ? nextScoreState.scoreLeft : nextScoreState.scoreRight
const opponentScore = side === 'left' ? nextScoreState.scoreRight : nextScoreState.scoreLeft
const serverCourt =
side === 'left'
? getMirroredCourt(getServiceCourt(servingScore))
: getServiceCourt(servingScore)
const serverCourt = getServiceCourt(servingScore)
// 只有發球方(剛得分那隊)再得 1 分就能贏,才算賽末點。
const matchPoint =
!reachedTarget &&
hasWonGame(
side === 'left'
? { ...nextScoreState, scoreLeft: nextScoreState.scoreLeft + 1 }
: { ...nextScoreState, scoreRight: nextScoreState.scoreRight + 1 },
)
setScoreHistory((current) => [...current, { pointLog, scoreState }])
setPointLog(nextPointLog)
@@ -790,6 +796,7 @@ function App() {
opponentScore,
serverName: getNextServerName(nextScoreState, leftTeam, rightTeam, side),
serverCourt,
matchPoint,
winnerTeamName,
})
+7 -7
View File
@@ -3,7 +3,6 @@ import { useEffect, useMemo, useRef, useState } from 'react'
import { Link } from 'react-router-dom'
import {
getCourtAssignments,
getMirroredCourt,
getReceivingPlayer,
getServiceCourt,
getServingPlayer,
@@ -64,6 +63,7 @@ type ScoreboardPageProps = {
opponentScore: number
serverName: string
serverCourt: 'left' | 'right'
matchPoint: boolean
winnerTeamName: string | null
} | null
targetDate: string
@@ -260,7 +260,11 @@ export function ScoreboardPage({
const parts: string[] = []
if (voiceSettings.announceScore) {
parts.push(`${voiceAnnouncement.servingScore}${voiceAnnouncement.opponentScore}`)
parts.push(
`${voiceAnnouncement.servingScore}${voiceAnnouncement.opponentScore}${
voiceAnnouncement.matchPoint ? ' 賽末點' : ''
}`,
)
}
if (voiceSettings.announceServer && voiceAnnouncement.serverName) {
@@ -484,11 +488,7 @@ export function ScoreboardPage({
onSwapPlayers={() => onSwapTeamPlayers('left')}
onSwapTeams={onSwapMatchup}
score={scoreState.scoreLeft}
serviceCourt={
scoreState.serving === 'left' && servingCourt
? getMirroredCourt(servingCourt)
: null
}
serviceCourt={scoreState.serving === 'left' ? servingCourt : null}
showServingPrompt={scoreState.serving === null}
team={leftTeam}
teamSlot="top"