- settings carry `categories` (empty = all); quiz/image pool filters by category (DB + in-code bank), threaded through prepareQuizForRoom/loadGroup/loadQuizPool - public GET /api/categories lists available categories - lobby: category chips (Toutes + each), shown when quiz/images is active - shared: RoomSettings.categories + UpdateSettingsPayload Verified e2e: /api/categories returns the list; filtering by ["Pokémon"] serves only Pokémon questions. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
144 lines
4.5 KiB
TypeScript
144 lines
4.5 KiB
TypeScript
// Modèle de domaine partagé entre client et serveur.
|
|
// Le serveur est la source de vérité ; le client n'affiche qu'un état reçu.
|
|
|
|
/** Statut courant d'une room (machine à états du jeu). */
|
|
export type RoomStatus = "lobby" | "in_round" | "reveal" | "scores" | "ended"
|
|
|
|
/** Types d'épreuves disponibles. Étendre = ajouter une valeur + un module GameRound. */
|
|
export type RoundType = "blindtest" | "quiz"
|
|
|
|
/** Type de partie choisi au lobby : mélange par défaut, ou un seul mode. */
|
|
export type GameType = "mixed" | "quiz" | "image" | "blindtest"
|
|
|
|
/** Sous-modes activables quand la partie est "mixed". */
|
|
export type MixMode = "quiz" | "image" | "blindtest"
|
|
|
|
/** Modes de blindtest, déterminent la forme du vote et le barème. */
|
|
export type BlindtestMode = "title_artist" | "who_added" | "mixed"
|
|
|
|
/** Formats de question quiz. */
|
|
export type QuizFormat = "mcq" | "truefalse" | "free" | "image_reveal"
|
|
|
|
/** Joueur connecté à une room. */
|
|
export interface Player {
|
|
id: string
|
|
name: string
|
|
connected: boolean
|
|
}
|
|
|
|
/** Configuration d'une épreuve planifiée dans la séquence de la partie. */
|
|
export interface RoundConfig {
|
|
type: RoundType
|
|
/** Pour une manche quiz : dans quel sous-pool piocher (quiz "classique" ou images). */
|
|
pool?: "quiz" | "image"
|
|
}
|
|
|
|
/** Réglages de la room, modifiables dans le lobby par l'hôte. */
|
|
export interface RoomSettings {
|
|
/** Type de partie choisi dans le lobby (pilote l'UI partagée). */
|
|
gameType: GameType
|
|
/** Sous-modes actifs quand gameType === "mixed". */
|
|
mixedModes: MixMode[]
|
|
blindtestMode: BlindtestMode
|
|
/** Durée d'une manche en secondes (def. 60). */
|
|
roundDuration: number
|
|
/** Nombre de titres que chaque joueur soumet (blindtest). */
|
|
tracksPerPlayer: number
|
|
/** Catégories de quiz autorisées (vide = toutes). */
|
|
categories: string[]
|
|
/** Séquence d'épreuves de la partie. */
|
|
rounds: RoundConfig[]
|
|
}
|
|
|
|
/** Réglages par défaut d'une nouvelle room. */
|
|
export const DEFAULT_ROOM_SETTINGS: RoomSettings = {
|
|
gameType: "mixed",
|
|
mixedModes: ["quiz", "image"],
|
|
blindtestMode: "title_artist",
|
|
roundDuration: 60,
|
|
tracksPerPlayer: 2,
|
|
categories: [],
|
|
rounds: [],
|
|
}
|
|
|
|
// --- Votes ---------------------------------------------------------------
|
|
|
|
/** Vote blindtest, forme selon RoomSettings.blindtestMode. */
|
|
export type BlindtestAnswer =
|
|
| { title: string; artist: string }
|
|
| { guessedPlayerId: string }
|
|
| { title: string; artist: string; guessedPlayerId: string }
|
|
|
|
/** Vote quiz, forme selon le format de la question. */
|
|
export type QuizAnswer = { choiceIndex: number } | { text: string }
|
|
|
|
/** Réponse soumise par un joueur pour la manche en cours. */
|
|
export type Answer = BlindtestAnswer | QuizAnswer
|
|
|
|
// --- Scores --------------------------------------------------------------
|
|
|
|
/** Score d'un joueur. */
|
|
export interface PlayerScore {
|
|
playerId: string
|
|
score: number
|
|
}
|
|
|
|
/** Variation de score appliquée à l'issue d'une manche. */
|
|
export interface ScoreDelta {
|
|
playerId: string
|
|
delta: number
|
|
}
|
|
|
|
/** Joueur ayant marqué sur une manche (récap de fin de partie). */
|
|
export interface RoundRecapScorer {
|
|
playerId: string
|
|
points: number
|
|
}
|
|
|
|
/** Réponse d'un joueur sur une manche (récap). */
|
|
export interface RoundPlayerAnswer {
|
|
playerId: string
|
|
answer: string
|
|
correct: boolean
|
|
}
|
|
|
|
/** Récapitulatif d'une manche jouée (affiché en fin de partie). */
|
|
export interface RoundRecap {
|
|
index: number
|
|
type: RoundType
|
|
/** Intitulé (question quiz) ou libellé du mode. */
|
|
label: string
|
|
/** Bonne réponse révélée. */
|
|
answer: string
|
|
category?: string
|
|
imageUrl?: string
|
|
youtubeId?: string
|
|
url?: string
|
|
/** Blindtest : qui avait ajouté le titre. */
|
|
addedBy?: string
|
|
/** Joueurs ayant marqué sur cette manche. */
|
|
scorers: RoundRecapScorer[]
|
|
/** Réponse de chaque joueur ayant répondu. */
|
|
answers: RoundPlayerAnswer[]
|
|
}
|
|
|
|
// --- Snapshot diffusé au client (room:state) -----------------------------
|
|
|
|
/**
|
|
* Vue publique de la room poussée à tous les clients.
|
|
* Ne contient JAMAIS de réponse non révélée ni d'info secrète (ex: submittedBy).
|
|
*/
|
|
export interface RoomSnapshot {
|
|
code: string
|
|
status: RoomStatus
|
|
hostId: string
|
|
players: Player[]
|
|
settings: RoomSettings
|
|
scores: PlayerScore[]
|
|
/** Index de la manche en cours, -1 dans le lobby. */
|
|
currentRound: number
|
|
/** Nombre total de manches planifiées. */
|
|
totalRounds: number
|
|
/** Blindtest : nombre de titres soumis par joueur (comptes seulement, jamais les titres). */
|
|
submissions: { playerId: string; count: number }[]
|
|
}
|