- 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>
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. Choisisseznegativepour un jeu « le moins de points possible », sinonpositive.
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: 1active 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"))
- macOS/Linux :
Gardez les images légères (idéalement < 200 Ko) : elles sont stockées telles quelles dans l'appareil. Si
iconImageest 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.
schemaVersiondoit valoir1.id: lettres/chiffres/_/-, et pas un id réservé :azul,cabo,skyjo,harmonie,odin_cards.name≤ 60 caractères,description≤ 400 caractères.minPlayers/maxPlayersentre 1 et 20.scoreTypeparmi les valeurs du §3.scoreFormula.strategyparmisum_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" }
}