fix(gameplay): add explicit scoreboard phase (#288)
All checks were successful
CI / test-and-quality (push) Successful in 2m12s
CI / test-and-quality (pull_request) Successful in 2m11s

This commit is contained in:
2026-03-13 16:11:06 +00:00
parent b968ea4430
commit a0277fd8be
8 changed files with 94 additions and 34 deletions

View File

@@ -67,6 +67,7 @@ def _build_phase_view_model(session: GameSession, *, players_count: int, has_rou
in_lie = status == GameSession.Status.LIE
in_guess = status == GameSession.Status.GUESS
in_reveal = status == GameSession.Status.REVEAL
in_scoreboard = status == GameSession.Status.SCOREBOARD
in_finished = status == GameSession.Status.FINISHED
min_players_reached = players_count >= 3
@@ -88,8 +89,8 @@ def _build_phase_view_model(session: GameSession, *, players_count: int, has_rou
"can_mix_answers": in_lie or in_guess,
"can_calculate_scores": in_guess,
"can_reveal_scoreboard": in_reveal,
"can_start_next_round": in_reveal,
"can_finish_game": in_reveal,
"can_start_next_round": in_scoreboard,
"can_finish_game": in_scoreboard,
},
"player": {
"can_join": status in JOINABLE_STATUSES,
@@ -710,8 +711,14 @@ def reveal_scoreboard(request: HttpRequest, code: str) -> JsonResponse:
if session.host_id != request.user.id:
return JsonResponse({"error": "Only host can view scoreboard"}, status=403)
if session.status != GameSession.Status.REVEAL:
return JsonResponse({"error": "Scoreboard is only available in reveal phase"}, status=400)
with transaction.atomic():
locked_session = GameSession.objects.select_for_update().get(pk=session.pk)
if locked_session.status not in {GameSession.Status.REVEAL, GameSession.Status.SCOREBOARD}:
return JsonResponse({"error": "Scoreboard is only available in reveal or scoreboard phase"}, status=400)
if locked_session.status == GameSession.Status.REVEAL:
locked_session.status = GameSession.Status.SCOREBOARD
locked_session.save(update_fields=["status"])
leaderboard = list(
Player.objects.filter(session=session)
@@ -723,8 +730,8 @@ def reveal_scoreboard(request: HttpRequest, code: str) -> JsonResponse:
{
"session": {
"code": session.code,
"status": session.status,
"current_round": session.current_round,
"status": locked_session.status,
"current_round": locked_session.current_round,
},
"leaderboard": leaderboard,
}
@@ -746,8 +753,8 @@ def start_next_round(request: HttpRequest, code: str) -> JsonResponse:
with transaction.atomic():
locked_session = GameSession.objects.select_for_update().get(pk=session.pk)
if locked_session.status != GameSession.Status.REVEAL:
return JsonResponse({"error": "Next round can only start from reveal phase"}, status=400)
if locked_session.status != GameSession.Status.SCOREBOARD:
return JsonResponse({"error": "Next round can only start from scoreboard phase"}, status=400)
locked_session.current_round += 1
locked_session.status = GameSession.Status.LOBBY
@@ -778,8 +785,8 @@ def finish_game(request: HttpRequest, code: str) -> JsonResponse:
with transaction.atomic():
locked_session = GameSession.objects.select_for_update().get(pk=session.pk)
if locked_session.status != GameSession.Status.REVEAL:
return JsonResponse({"error": "Game can only be finished from reveal phase"}, status=400)
if locked_session.status != GameSession.Status.SCOREBOARD:
return JsonResponse({"error": "Game can only be finished from scoreboard phase"}, status=400)
locked_session.status = GameSession.Status.FINISHED
locked_session.save(update_fields=["status"])