add jury voting, bingo WS message handlers

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-12 19:51:32 +01:00
parent 7cb52291f3
commit ceba5521dc

View File

@@ -47,7 +47,8 @@ function sendGameState(ws: WSContext, roomCode: string, sessionId: string) {
if (!gm || !playerId) return
const allPlayerIds = roomManager.getAllPlayerIds(roomCode)
const gameState = gm.getGameStateForPlayer(playerId, allPlayerIds)
const displayNames = roomManager.getPlayerDisplayNames(roomCode)
const gameState = gm.getGameStateForPlayer(playerId, allPlayerIds, displayNames)
sendTo(ws, { type: "game_state", gameState })
}
@@ -56,7 +57,8 @@ function sendDisplayGameState(ws: WSContext, roomCode: string) {
if (!gm) return
const allPlayerIds = roomManager.getAllPlayerIds(roomCode)
const gameState = gm.getGameStateForDisplay(allPlayerIds)
const displayNames = roomManager.getPlayerDisplayNames(roomCode)
const gameState = gm.getGameStateForDisplay(allPlayerIds, displayNames)
sendTo(ws, { type: "game_state", gameState })
}
@@ -211,12 +213,15 @@ export function registerWebSocketRoutes() {
type: "act_changed",
newAct: result.newAct,
})
// Lock predictions when moving from pre-show to live-event
// Lock predictions and generate bingo cards when entering live-event
if (result.newAct === "live-event") {
const gm = roomManager.getGameManager(roomCode)
if (gm) {
gm.lockPredictions()
broadcast(roomCode, { type: "predictions_locked" })
const allPlayerIds = roomManager.getAllPlayerIds(roomCode)
gm.generateBingoCards(allPlayerIds)
broadcastGameStateToAll(roomCode)
}
}
break
@@ -258,6 +263,141 @@ export function registerWebSocketRoutes() {
broadcastGameStateToAll(roomCode)
break
}
case "open_jury_vote": {
if (!sessionId) {
sendError(ws, "Not joined")
return
}
const room = roomManager.getRoom(roomCode)
if (room?.currentAct !== "live-event") {
sendError(ws, "Jury voting is only available during Live Event")
return
}
if (!roomManager.isHost(roomCode, sessionId)) {
sendError(ws, "Only the host can open jury voting")
return
}
const gm = roomManager.getGameManager(roomCode)
if (!gm) {
sendError(ws, "Room not found")
return
}
const entry = gm.getLineup().entries.find((e) => e.country.code === msg.countryCode)
if (!entry) {
sendError(ws, "Invalid country code")
return
}
const result = gm.openJuryRound(entry.country.code, entry.country.name, entry.country.flag)
if ("error" in result) {
sendError(ws, result.error)
return
}
const round = gm.getCurrentJuryRound()!
broadcast(roomCode, {
type: "jury_vote_opened",
roundId: round.id,
countryCode: round.countryCode,
countryName: round.countryName,
countryFlag: round.countryFlag,
})
broadcastGameStateToAll(roomCode)
break
}
case "close_jury_vote": {
if (!sessionId) {
sendError(ws, "Not joined")
return
}
if (!roomManager.isHost(roomCode, sessionId)) {
sendError(ws, "Only the host can close jury voting")
return
}
const gm = roomManager.getGameManager(roomCode)
if (!gm) {
sendError(ws, "Room not found")
return
}
const result = gm.closeJuryRound()
if ("error" in result) {
sendError(ws, result.error)
return
}
broadcast(roomCode, {
type: "jury_vote_closed",
countryCode: result.countryCode,
countryName: result.countryName,
countryFlag: result.countryFlag,
averageRating: result.averageRating,
totalVotes: result.totalVotes,
})
broadcastGameStateToAll(roomCode)
break
}
case "submit_jury_vote": {
if (!sessionId) {
sendError(ws, "Not joined")
return
}
if (roomManager.getRoom(roomCode)?.currentAct !== "live-event") {
sendError(ws, "Jury voting is only available during Live Event")
return
}
const playerId = roomManager.getPlayerIdBySession(roomCode, sessionId)
const gm = roomManager.getGameManager(roomCode)
if (!playerId || !gm) {
sendError(ws, "Room not found")
return
}
const result = gm.submitJuryVote(playerId, msg.rating)
if ("error" in result) {
sendError(ws, result.error)
return
}
sendGameState(ws, roomCode, sessionId)
break
}
case "tap_bingo_square": {
if (!sessionId) {
sendError(ws, "Not joined")
return
}
if (roomManager.getRoom(roomCode)?.currentAct !== "live-event") {
sendError(ws, "Bingo is only available during Live Event")
return
}
const playerId = roomManager.getPlayerIdBySession(roomCode, sessionId)
const gm = roomManager.getGameManager(roomCode)
if (!playerId || !gm) {
sendError(ws, "Room not found")
return
}
const result = gm.tapBingoSquare(playerId, msg.tropeId)
if ("error" in result) {
sendError(ws, result.error)
return
}
sendGameState(ws, roomCode, sessionId)
if (result.hasBingo) {
const room = roomManager.getRoom(roomCode)
const player = room?.players.find((p) => p.sessionId === sessionId)
if (player) {
const isNew = gm.addBingoAnnouncement(playerId, player.displayName)
if (isNew) {
broadcast(roomCode, {
type: "bingo_announced",
playerId,
displayName: player.displayName,
})
broadcastGameStateToAll(roomCode)
}
}
}
break
}
}
},