diff --git a/index.html b/index.html index ec77368..1b8b784 100644 --- a/index.html +++ b/index.html @@ -3,6 +3,7 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/games/harmonie/config.ts b/src/games/harmonie/config.ts index a3b6e62..be6e2cc 100644 --- a/src/games/harmonie/config.ts +++ b/src/games/harmonie/config.ts @@ -1,4 +1,18 @@ -import { GameConfig } from "../../types"; +import { GameConfig, ScoringCategory } from "../../types"; + +const BASE_CATEGORIES: ScoringCategory[] = [ + { id: "arbres", label: "Arbres" }, + { id: "montagnes", label: "Montagnes" }, + { id: "champs", label: "Champs" }, + { id: "batiments", label: "Bâtiments" }, + { id: "eau", label: "Rivière" }, + { id: "animaux", label: "Animaux" }, +]; + +const NATURE_SPIRIT_CATEGORY: ScoringCategory = { + id: "esprits_nature", + label: "Esprits de la nature", +}; export const harmonieConfig: GameConfig = { imagePath: "/games-assets/harmonies-logo.webp", @@ -15,12 +29,18 @@ export const harmonieConfig: GameConfig = { colors: { primary: "#065f46", // Vert émeraude sombre tiré de la boite Harmonies }, - scoringCategories: [ - { id: "arbres", label: "Arbres" }, - { id: "montagnes", label: "Montagnes" }, - { id: "champs", label: "Champs" }, - { id: "batiments", label: "Bâtiments" }, - { id: "eau", label: "Rivière" }, - { id: "animaux", label: "Animaux" }, + options: [ + { + id: "esprits_nature", + label: "Extension : Esprits de la nature", + type: "boolean", + defaultValue: false, + }, ], + scoringCategories: BASE_CATEGORIES, + // When the "Esprits de la nature" rule is on, add a final scoring category. + getScoringCategories: (options) => + options?.esprits_nature + ? [...BASE_CATEGORIES, NATURE_SPIRIT_CATEGORY] + : BASE_CATEGORIES, }; diff --git a/src/pages/GameOver.tsx b/src/pages/GameOver.tsx index 0bc6cf9..c657bf5 100644 --- a/src/pages/GameOver.tsx +++ b/src/pages/GameOver.tsx @@ -52,6 +52,11 @@ export default function GameOver() { const Icon = gameConfig?.icon ? (Icons as any)[gameConfig.icon] : null; + // Effective scoring categories (may depend on the session's options). + const categories = + gameConfig?.getScoringCategories?.(session.options) ?? + gameConfig?.scoringCategories; + const durationInMinutes = session.dateEnd ? Math.round((session.dateEnd - session.dateStart) / 60000) : 0; @@ -68,7 +73,7 @@ export default function GameOver() { const totals = session.players.map((p) => { // If it's a categorized game (Harmonies), extract details from the first (and only) round let details = undefined; - if (gameConfig?.scoringCategories && session.rounds.length > 0) { + if (categories && session.rounds.length > 0) { const pScore = session.rounds[0].scores.find((s) => s.playerId === p.id); details = pScore?.details; } @@ -232,9 +237,9 @@ export default function GameOver() { {t.score} {/* Display details if it's a categorized game like Harmonies */} - {t.details && gameConfig?.scoringCategories && ( + {t.details && categories && (
- {gameConfig.scoringCategories.map((cat) => { + {categories.map((cat) => { const val = t.details?.[cat.id]; if (!val || val === "0") return null; return ( diff --git a/src/pages/PlayGame.tsx b/src/pages/PlayGame.tsx index c0bc71e..c7cb224 100644 --- a/src/pages/PlayGame.tsx +++ b/src/pages/PlayGame.tsx @@ -39,6 +39,11 @@ export default function PlayGame() { const gameConfig = useGameConfig(activeSession?.gameId); useGameTheme(gameConfig); + // Effective scoring categories (may depend on the session's options). + const categories = + gameConfig?.getScoringCategories?.(activeSession?.options ?? {}) ?? + gameConfig?.scoringCategories; + useEffect(() => { if (sessionId) { loadSession(sessionId); @@ -109,7 +114,7 @@ export default function PlayGame() { const getLiveTotalScore = (playerId: string) => { let total = calculateTotalScore(playerId); - if (gameConfig?.scoringCategories) { + if (categories) { const pDetails = detailedScores[playerId] || {}; for (const key in pDetails) { const val = parseInt(pDetails[key] || "0", 10); @@ -144,7 +149,7 @@ export default function PlayGame() { const scores: RoundScore[] = activeSession.players.map((p) => { // For games with categories (like Harmonies), we store the sum as the main score // and keep the details in the `details` object - if (gameConfig?.scoringCategories) { + if (categories) { const pDetails = detailedScores[p.id] || {}; let total = 0; for (const key in pDetails) { @@ -318,14 +323,14 @@ export default function PlayGame() {

- {gameConfig.scoringCategories - ? gameConfig.scoringCategories[currentCategoryIndex].label + {categories + ? categories[currentCategoryIndex].label : isSingleRound ? "Saisie des scores" : "À vos marques !"}

- {gameConfig.scoringCategories && currentCategoryIndex > 0 && ( + {categories && currentCategoryIndex > 0 && (
- {gameConfig.scoringCategories ? ( + {categories ? ( {displayPlayers.map((player, idx) => { const pDetails = detailedScores[player.id] || {}; const catId = - gameConfig.scoringCategories![currentCategoryIndex].id; + categories![currentCategoryIndex].id; return (
{ if ( - gameConfig?.scoringCategories && - currentCategoryIndex < gameConfig.scoringCategories.length - 1 + categories && + currentCategoryIndex < categories.length - 1 ) { setCurrentCategoryIndex((prev) => prev + 1); } else { @@ -564,13 +569,13 @@ export default function PlayGame() { } }} > - {gameConfig?.scoringCategories && - currentCategoryIndex < gameConfig.scoringCategories.length - 1 ? ( + {categories && + currentCategoryIndex < categories.length - 1 ? ( "Suivant" ) : ( <> - {gameConfig?.scoringCategories || isSingleRound + {categories || isSingleRound ? "Valider le score" : "Valider la manche"} diff --git a/src/types/index.ts b/src/types/index.ts index dd11e71..b8640c0 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -22,6 +22,9 @@ export interface GameConfig { ) => number; fixedRounds?: number; // Used if scoreType is 'fixed_rounds' scoringCategories?: ScoringCategory[]; + // Optional: categories that depend on the chosen options (e.g. an extra + // scoring category enabled by a rule toggle). Falls back to scoringCategories. + getScoringCategories?: (options: Record) => ScoringCategory[]; description?: string; icon?: string; imagePath?: string; // Optional custom logo image (SVG, PNG, WebP...)