調整發球鏡像規則並更新說明文件
This commit is contained in:
+64
-8
@@ -14,6 +14,8 @@ import {
|
||||
convertDateToKey,
|
||||
convertDbRecordToGroups,
|
||||
formatDateInputValue,
|
||||
getMirroredCourt,
|
||||
getServiceCourt,
|
||||
getServingPlayer,
|
||||
getTeamDisplayName,
|
||||
getWinnerName,
|
||||
@@ -56,6 +58,7 @@ const initialScoreState: ScoreState = {
|
||||
gamesRight: 0,
|
||||
currentGame: 1,
|
||||
targetScore: 21,
|
||||
initialServing: null,
|
||||
serving: null,
|
||||
leftRightCourtPlayer: 'playerA',
|
||||
rightRightCourtPlayer: 'playerA',
|
||||
@@ -81,6 +84,13 @@ type VictoryAnnouncement = {
|
||||
title: string
|
||||
}
|
||||
|
||||
type VoiceAnnouncement = {
|
||||
key: number
|
||||
scorerName: string
|
||||
serverChanged: boolean
|
||||
serverName: string
|
||||
}
|
||||
|
||||
const STREAK_TITLES: Record<number, string> = {
|
||||
3: '大殺特殺',
|
||||
4: '暴走',
|
||||
@@ -129,6 +139,7 @@ function App() {
|
||||
})
|
||||
const [streakAnnouncement, setStreakAnnouncement] = useState<StreakAnnouncement | null>(null)
|
||||
const [victoryAnnouncement, setVictoryAnnouncement] = useState<VictoryAnnouncement | null>(null)
|
||||
const [voiceAnnouncement, setVoiceAnnouncement] = useState<VoiceAnnouncement | null>(null)
|
||||
const [pwaUpdateReady, setPwaUpdateReady] = useState(false)
|
||||
const [liveRoomSession, setLiveRoomSession] = useState<LiveRoomSession | null>(null)
|
||||
const [navigationLockMessage, setNavigationLockMessage] = useState('')
|
||||
@@ -288,6 +299,7 @@ function App() {
|
||||
setPointLog([])
|
||||
setStreakAnnouncement(null)
|
||||
setVictoryAnnouncement(null)
|
||||
setVoiceAnnouncement(null)
|
||||
setSettlement({
|
||||
error: '',
|
||||
open: false,
|
||||
@@ -441,9 +453,9 @@ function App() {
|
||||
}
|
||||
|
||||
const winnerTeamName =
|
||||
scoreState.scoreLeft >= scoreState.targetScore
|
||||
hasWonGame(scoreState) && scoreState.scoreLeft > scoreState.scoreRight
|
||||
? getTeamDisplayName(leftTeam)
|
||||
: scoreState.scoreRight >= scoreState.targetScore
|
||||
: hasWonGame(scoreState) && scoreState.scoreRight > scoreState.scoreLeft
|
||||
? getTeamDisplayName(rightTeam)
|
||||
: null
|
||||
const nextStatus = winnerTeamName ? 'finished' : 'live'
|
||||
@@ -668,6 +680,12 @@ function App() {
|
||||
: current.serving === 'right'
|
||||
? 'left'
|
||||
: null,
|
||||
initialServing:
|
||||
current.initialServing === 'left'
|
||||
? 'right'
|
||||
: current.initialServing === 'right'
|
||||
? 'left'
|
||||
: null,
|
||||
leftRightCourtPlayer: current.rightRightCourtPlayer,
|
||||
rightRightCourtPlayer: current.leftRightCourtPlayer,
|
||||
}))
|
||||
@@ -692,12 +710,13 @@ function App() {
|
||||
}
|
||||
|
||||
const setServing = (side: ScoreSide) => {
|
||||
if (scoreHistory.length > 0) {
|
||||
if (scoreHistory.length > 0 || scoreState.initialServing !== null) {
|
||||
return
|
||||
}
|
||||
|
||||
setScoreState((current) => ({
|
||||
...current,
|
||||
initialServing: side,
|
||||
serving: side,
|
||||
}))
|
||||
}
|
||||
@@ -747,6 +766,12 @@ function App() {
|
||||
setScoreHistory((current) => [...current, { pointLog, scoreState }])
|
||||
setPointLog(nextPointLog)
|
||||
setScoreState(nextScoreState)
|
||||
setVoiceAnnouncement({
|
||||
key: Date.now(),
|
||||
scorerName: side === 'left' ? leftTeam.playerA : rightTeam.playerA,
|
||||
serverChanged: side === scoreState.serving,
|
||||
serverName: getNextServerName(nextScoreState, leftTeam, rightTeam, side),
|
||||
})
|
||||
|
||||
if (streakTitle) {
|
||||
setStreakAnnouncement({
|
||||
@@ -757,9 +782,7 @@ function App() {
|
||||
})
|
||||
}
|
||||
|
||||
const reachedTarget =
|
||||
nextScoreState.scoreLeft >= nextScoreState.targetScore ||
|
||||
nextScoreState.scoreRight >= nextScoreState.targetScore
|
||||
const reachedTarget = hasWonGame(nextScoreState)
|
||||
|
||||
if (reachedTarget) {
|
||||
setVictoryAnnouncement({
|
||||
@@ -988,6 +1011,7 @@ function App() {
|
||||
selectedGroup={selectedGroup}
|
||||
streakAnnouncement={streakAnnouncement}
|
||||
victoryAnnouncement={victoryAnnouncement}
|
||||
voiceAnnouncement={voiceAnnouncement}
|
||||
targetDate={targetDate}
|
||||
onApplyMatchup={applyMatchup}
|
||||
onCloseFinishDialog={closeSettlementDialog}
|
||||
@@ -1081,7 +1105,7 @@ function getServerHistoryIndex(
|
||||
return null
|
||||
}
|
||||
|
||||
return server.slot === 'playerA' ? 0 : 1
|
||||
return getMirroredCourt(getServiceCourt(state.scoreLeft)) === 'left' ? 0 : 1
|
||||
}
|
||||
|
||||
if (state.serving === 'right') {
|
||||
@@ -1091,12 +1115,44 @@ function getServerHistoryIndex(
|
||||
return null
|
||||
}
|
||||
|
||||
return server.slot === 'playerB' ? 2 : 3
|
||||
return getServiceCourt(state.scoreRight) === 'right' ? 2 : 3
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
function getNextServerName(
|
||||
state: ScoreState,
|
||||
leftTeam: GroupTeam,
|
||||
rightTeam: GroupTeam,
|
||||
side: ScoreSide,
|
||||
) {
|
||||
if (side === 'left') {
|
||||
return getServingPlayer(leftTeam, state.leftRightCourtPlayer, state.scoreLeft)?.name ?? ''
|
||||
}
|
||||
|
||||
return getServingPlayer(rightTeam, state.rightRightCourtPlayer, state.scoreRight)?.name ?? ''
|
||||
}
|
||||
|
||||
function hasWonGame(state: ScoreState) {
|
||||
const leadingScore = Math.max(state.scoreLeft, state.scoreRight)
|
||||
const trailingScore = Math.min(state.scoreLeft, state.scoreRight)
|
||||
|
||||
if (leadingScore < state.targetScore) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (leadingScore >= 30) {
|
||||
return true
|
||||
}
|
||||
|
||||
if (trailingScore >= state.targetScore - 1) {
|
||||
return leadingScore - trailingScore >= 2
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
function formatPlayedAt(timestamp: number) {
|
||||
return new Date(timestamp * 1000).toLocaleString('zh-TW', { hour12: false })
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user