refactor(gameplay): extract start/show transitions from lobby views
This commit is contained in:
@@ -1,18 +1,23 @@
|
||||
import random
|
||||
from datetime import timedelta
|
||||
from dataclasses import dataclass
|
||||
from typing import Any
|
||||
|
||||
from django.db import transaction
|
||||
from django.utils import timezone
|
||||
|
||||
from .models import GameSession, Guess, LieAnswer, Player, Question, RoundConfig, RoundQuestion, ScoreEvent
|
||||
from .models import Category, GameSession, Guess, LieAnswer, Player, Question, RoundConfig, RoundQuestion, ScoreEvent
|
||||
from .payloads import (
|
||||
build_finish_game_phase_event,
|
||||
build_finish_game_response,
|
||||
build_lie_started_payload,
|
||||
build_question_shown_payload,
|
||||
build_question_shown_response,
|
||||
build_reveal_scoreboard_response,
|
||||
build_scoreboard_phase_event,
|
||||
build_start_next_round_phase_event,
|
||||
build_start_next_round_response,
|
||||
build_start_round_response,
|
||||
)
|
||||
|
||||
|
||||
@@ -121,6 +126,81 @@ def prepare_mixed_answers(round_question: RoundQuestion) -> list[str]:
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def start_round(session: GameSession, category_slug: str) -> RoundTransitionResult:
|
||||
try:
|
||||
category = Category.objects.get(slug=category_slug, is_active=True)
|
||||
except Category.DoesNotExist:
|
||||
raise ValueError("category_not_found")
|
||||
|
||||
if not Question.objects.filter(category=category, is_active=True).exists():
|
||||
raise ValueError("category_has_no_questions")
|
||||
|
||||
with transaction.atomic():
|
||||
locked_session = GameSession.objects.select_for_update().get(pk=session.pk)
|
||||
if locked_session.status != GameSession.Status.LOBBY:
|
||||
raise ValueError("round_start_invalid_phase")
|
||||
|
||||
if RoundConfig.objects.filter(session=locked_session, number=locked_session.current_round).exists():
|
||||
raise ValueError("round_already_configured")
|
||||
|
||||
round_config = RoundConfig(
|
||||
session=locked_session,
|
||||
number=locked_session.current_round,
|
||||
category=category,
|
||||
)
|
||||
|
||||
round_question = select_round_question(locked_session, round_config)
|
||||
|
||||
round_config.save()
|
||||
locked_session.status = GameSession.Status.LIE
|
||||
locked_session.save(update_fields=["status"])
|
||||
|
||||
phase_event = {
|
||||
"name": "phase.lie_started",
|
||||
"payload": build_lie_started_payload(locked_session, round_config, round_question),
|
||||
}
|
||||
return RoundTransitionResult(
|
||||
session=locked_session,
|
||||
round_config=round_config,
|
||||
round_question=round_question,
|
||||
should_broadcast=True,
|
||||
response_payload=build_start_round_response(locked_session, round_config, round_question),
|
||||
phase_event_name=phase_event["name"],
|
||||
phase_event_payload=phase_event["payload"],
|
||||
)
|
||||
|
||||
|
||||
def show_question(session: GameSession) -> RoundTransitionResult:
|
||||
if session.status != GameSession.Status.LIE:
|
||||
raise ValueError("show_question_invalid_phase")
|
||||
|
||||
try:
|
||||
round_config = RoundConfig.objects.get(session=session, number=session.current_round)
|
||||
except RoundConfig.DoesNotExist:
|
||||
raise ValueError("round_config_missing")
|
||||
|
||||
round_question = get_current_round_question(session)
|
||||
if round_question is None:
|
||||
round_question = select_round_question(session, round_config)
|
||||
|
||||
lie_deadline_at = round_question.shown_at + timedelta(seconds=round_config.lie_seconds)
|
||||
lie_deadline_iso = lie_deadline_at.isoformat()
|
||||
phase_event = {
|
||||
"name": "phase.question_shown",
|
||||
"payload": build_question_shown_payload(round_question, lie_deadline_iso, round_config.lie_seconds),
|
||||
}
|
||||
return RoundTransitionResult(
|
||||
session=session,
|
||||
round_config=round_config,
|
||||
round_question=round_question,
|
||||
should_broadcast=True,
|
||||
response_payload=build_question_shown_response(round_question, lie_deadline_iso, round_config.lie_seconds),
|
||||
phase_event_name=phase_event["name"],
|
||||
phase_event_payload=phase_event["payload"],
|
||||
)
|
||||
|
||||
def start_next_round(session: GameSession) -> RoundTransitionResult:
|
||||
with transaction.atomic():
|
||||
locked_session = GameSession.objects.select_for_update().get(pk=session.pk)
|
||||
|
||||
Reference in New Issue
Block a user