Files
Skori/docs/creation-jeu.md
T
Zed 9352bb08d9
Build and Publish Docker Image / build-and-push-image (push) Successful in 1m2s
Import de jeu : catégories conditionnelles (mode alternatif)
- ScoringCategory.requiresOption : une catégorie n'apparaît/compte que si
  l'option booléenne correspondante est activée (mode/extension déclaratif)
- toGameConfig génère getScoringCategories filtrant selon les options
- Gabarit harmonies-esprits.json : mode "Esprits de la nature" activable
  ajoutant la catégorie "Animaux spirituels" au décompte
- Doc à jour

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-22 18:12:54 +02:00

12 KiB

Créer un jeu personnalisé pour Skori

Ce document décrit tous les paramètres d'un fichier de définition de jeu (.json) importable dans Skori via Paramètres → Jeux → Gérer les jeux importés → Importer.

Un jeu importé ajoute une nouvelle tuile à l'écran d'accueil, avec ses propres règles de score, options, icône et couleurs — sans recompiler l'application.

Les règles de calcul sont déclaratives (décrites par des données, pas du code). C'est volontaire : un fichier importé ne peut jamais exécuter de code arbitraire sur votre appareil.


1. Structure minimale

Le plus petit fichier valide :

{
  "schemaVersion": 1,
  "id": "mon-jeu",
  "name": "Mon Jeu",
  "minPlayers": 2,
  "maxPlayers": 6,
  "scoreType": "positive",
  "scoreFormula": { "strategy": "sum_rounds" },
  "colors": { "primary": "#3b82f6" }
}

Tout le reste est optionnel.


2. Champs racine

Champ Type Obligatoire Description
schemaVersion 1 Toujours 1 (version du format).
id texte Identifiant unique. Lettres, chiffres, _ et - uniquement. Ne peut pas être un id natif (voir §9). Réimporter avec le même id met à jour le jeu.
name texte Nom affiché (max 60 caractères).
minPlayers entier Nombre minimum de joueurs (1 à 20).
maxPlayers entier Nombre maximum de joueurs (1 à 20).
scoreType énum Nature du score — voir §3.
scoreFormula objet Comment additionner les manches — voir §4.
colors objet Couleur(s) du thème — voir §7.
description texte Phrase affichée sur l'écran de la partie (max 400 caractères).
targetScore objet Score de fin de partie — voir §5.
targetScoreCondition "reaches" | "exceeds" reaches = fin si score cible (défaut logique) ; exceeds = fin si score > cible.
fixedRounds entier La partie se termine après ce nombre de manches — voir §6.
scoringCategories tableau Catégories de score saisies une par une — voir §8.
options tableau Options configurables avant la partie — voir §6/§8.
icon texte Nom d'icône lucide (ex: TreePine, Ghost, Sword) utilisée si aucune iconImage. Voir les noms sur lucide.dev/icons.
iconImage texte Icône du jeu (image en base64 « data URI »). Prioritaire sur icon — voir §7.
bannerImage texte Bannière de l'écran de fin (base64 data URI) — voir §7.

3. scoreType — nature du score

Détermine qui gagne et la façon dont le score est interprété.

Valeur Le gagnant est… Exemple
positive le plus haut score Azul, Harmonies
negative le plus bas score Skyjo, Cabo, Odin
target le plus haut (course vers un objectif) jeux à objectif de points
fixed_rounds le plus haut, sur un nombre fixe de manches
sudden_death le plus haut (mort subite)

En pratique, le classement de fin de partie utilise negative → plus petit gagne, tous les autres → plus grand gagne. Choisissez negative pour un jeu « le moins de points possible », sinon positive.


4. scoreFormula — calcul du total

Comment le total d'un joueur est calculé à partir de ses scores de manche.

sum_rounds (par défaut)

Simple addition de toutes les manches.

"scoreFormula": { "strategy": "sum_rounds" }

sum_rounds_with_reset

Addition, avec remise à un palier quand le total atteint exactement un seuil (règle « Cabo » : tomber pile sur 100 redescend à 50).

"scoreFormula": {
  "strategy": "sum_rounds_with_reset",
  "resetThreshold": 100,
  "resetTo": 50,
  "oncePerPlayer": true
}
Paramètre Type Description
resetThreshold nombre Si le total égale exactement cette valeur…
resetTo nombre …il est ramené à cette valeur.
oncePerPlayer booléen true = la remise ne peut avoir lieu qu'une seule fois par joueur.

5. targetScore — fin de partie sur un score

Déclenche la fin de la partie dès qu'un joueur atteint (ou dépasse, selon targetScoreCondition) une valeur cible. Combinable avec fixedRounds (la première condition atteinte l'emporte).

Cible fixe

"targetScore": { "type": "fixed", "value": 100 }

Cible dépendant d'une option

Permet à l'utilisateur de choisir la cible avant la partie (comme Odin : « 15 pts / 20 pts / personnalisé »).

"targetScore": {
  "type": "fromOption",
  "optionId": "target_score",
  "customSentinel": "custom",
  "customOptionId": "custom_target_score"
}
Paramètre Description
optionId id de l'option (select ou number) qui contient la cible.
customSentinel (optionnel) valeur de l'option signifiant « personnalisé ».
customOptionId (optionnel) id de l'option number lue si la valeur = customSentinel.

6. fixedRounds — nombre de manches fixe

"fixedRounds": 5
  • La partie se termine automatiquement après ce nombre de manches.
  • fixedRounds: 1 active un mode spécial « Décompte final » (une seule saisie de scores, idéal pour un jeu où l'on ne compte qu'à la fin).

7. Apparence : colors, iconImage, bannerImage

Couleurs

"colors": { "primary": "#6e1d5a", "secondary": "#c2410c" }
  • primary (obligatoire) : couleur principale du thème du jeu (format hex).
  • secondary (optionnel) : couleur d'appoint.

Images (base64)

iconImage et bannerImage sont des data URI : l'image encodée directement dans le fichier (aucun chemin externe). Formats : PNG, SVG, WebP, JPEG.

"iconImage": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA…"

Pour convertir une image en data URI :

  • en ligne : chercher « image to base64 data uri » ;
  • ou dans un terminal :
    • macOS/Linux : echo "data:image/png;base64,$(base64 -w0 icone.png)"
    • Windows PowerShell : "data:image/png;base64," + [Convert]::ToBase64String([IO.File]::ReadAllBytes("icone.png"))

Gardez les images légères (idéalement < 200 Ko) : elles sont stockées telles quelles dans l'appareil. Si iconImage est absent, une icône générique est utilisée.


8. Options et catégories

options — réglages avant la partie

Chaque option apparaît sur l'écran de préparation de la partie.

"options": [
  {
    "id": "kamikaze",
    "label": "Règle Kamikaze",
    "type": "boolean",
    "defaultValue": true
  },
  {
    "id": "target_score",
    "label": "Score de fin",
    "type": "select",
    "defaultValue": "15",
    "options": [
      { "label": "Courte (15)", "value": "15" },
      { "label": "Classique (20)", "value": "20" },
      { "label": "Personnalisé", "value": "custom" }
    ]
  },
  {
    "id": "custom_target_score",
    "label": "Score personnalisé",
    "type": "number",
    "defaultValue": 25
  }
]
Champ Description
id Identifiant de l'option (référencé par targetScore.fromOption).
label Libellé affiché.
type boolean (case à cocher), number (champ numérique), select (liste).
defaultValue Valeur par défaut.
options (select uniquement) liste de { label, value }.

scoringCategories — saisie par catégories

Active une saisie étape par étape (une catégorie à la fois, comme Harmonies). Le score de la manche = somme des catégories.

"scoringCategories": [
  { "id": "arbres", "label": "Arbres", "icon": "TreePine", "color": "#16a34a" },
  { "id": "montagnes", "label": "Montagnes", "icon": "Mountain", "color": "#64748b" },
  { "id": "eau", "label": "Rivière", "icon": "Waves", "color": "#0ea5e9" }
]
Champ Description
id Identifiant de la catégorie.
label Libellé affiché.
icon (optionnel) nom d'icône lucide affichée à côté de la catégorie.
color (optionnel) teinte hex de l'icône.
requiresOption (optionnel) id d'une option booléenne : la catégorie n'apparaît (et n'est comptée) que si cette option est activée. Idéal pour un mode/extension (ex : « Animaux spirituels »).

Généralement associé à fixedRounds: 1 (un seul décompte final réparti en catégories).


9. Contraintes & validation

À l'import, le fichier est validé. En cas d'erreur, un message précis indique le champ fautif.

  • schemaVersion doit valoir 1.
  • id : lettres/chiffres/_/-, et pas un id réservé : azul, cabo, skyjo, harmonie, odin_cards.
  • name ≤ 60 caractères, description ≤ 400 caractères.
  • minPlayers / maxPlayers entre 1 et 20.
  • scoreType parmi les valeurs du §3.
  • scoreFormula.strategy parmi sum_rounds, sum_rounds_with_reset.
  • Les jeux importés sont propres à chaque profil (pas encore synchronisés entre appareils).

Non pris en charge en v1 : les formules 100 % personnalisées par expression (custom_expression). Les deux stratégies ci-dessus couvrent tous les jeux fournis en standard.


10. Exemples complets

A. Skyjo (le moins de points, fin à 100)

{
  "schemaVersion": 1,
  "id": "mon-skyjo",
  "name": "Skyjo maison",
  "minPlayers": 2,
  "maxPlayers": 8,
  "scoreType": "negative",
  "scoreFormula": { "strategy": "sum_rounds" },
  "targetScore": { "type": "fixed", "value": 100 },
  "targetScoreCondition": "reaches",
  "description": "Le moins de points possible. Fin dès qu'un joueur atteint 100.",
  "colors": { "primary": "#0f766e" }
}

B. Cabo (remise à 50 sur 100 pile)

{
  "schemaVersion": 1,
  "id": "mon-cabo",
  "name": "Cabo maison",
  "minPlayers": 2,
  "maxPlayers": 5,
  "scoreType": "negative",
  "scoreFormula": {
    "strategy": "sum_rounds_with_reset",
    "resetThreshold": 100,
    "resetTo": 50,
    "oncePerPlayer": true
  },
  "targetScore": { "type": "fixed", "value": 100 },
  "targetScoreCondition": "reaches",
  "colors": { "primary": "#6e1d5a" }
}

C. Odin (cible choisie par option)

{
  "schemaVersion": 1,
  "id": "mon-odin",
  "name": "Odin maison",
  "minPlayers": 2,
  "maxPlayers": 6,
  "scoreType": "negative",
  "scoreFormula": { "strategy": "sum_rounds" },
  "targetScore": {
    "type": "fromOption",
    "optionId": "target_score",
    "customSentinel": "custom",
    "customOptionId": "custom_target_score"
  },
  "targetScoreCondition": "reaches",
  "options": [
    {
      "id": "target_score",
      "label": "Score de fin de partie",
      "type": "select",
      "defaultValue": "15",
      "options": [
        { "label": "Partie courte (15 pts)", "value": "15" },
        { "label": "Partie classique (20 pts)", "value": "20" },
        { "label": "Personnalisé", "value": "custom" }
      ]
    },
    {
      "id": "custom_target_score",
      "label": "Score personnalisé",
      "type": "number",
      "defaultValue": 25
    }
  ],
  "colors": { "primary": "#c2410c" }
}

D. Jeu par catégories (le plus de points, décompte final)

{
  "schemaVersion": 1,
  "id": "mon-jeu-categories",
  "name": "Jeu à catégories",
  "minPlayers": 1,
  "maxPlayers": 4,
  "scoreType": "positive",
  "scoreFormula": { "strategy": "sum_rounds" },
  "fixedRounds": 1,
  "scoringCategories": [
    { "id": "cat1", "label": "Objectifs" },
    { "id": "cat2", "label": "Bonus" },
    { "id": "cat3", "label": "Pénalités" }
  ],
  "colors": { "primary": "#16a34a" }
}