Data model — conventions partagées
Ce fichier est le contrat commun des documents
docs/data-model/*.md. Toute entité décrite ailleurs respecte le vocabulaire, les types de base et la séparation définition / instance décrits ici. Si une fiche s'en écarte, c'est un choix explicite et il est justifié dans la fiche.
1. Le principe structurant : Définition ≠ Instance
C'est la distinction qui porte tout le modèle. Chaque concept du jeu existe sous deux formes :
Définition (*Def) | Instance (*State) | |
|---|---|---|
| Qui la crée | l'auteur, dans le backoffice | le moteur, pendant une partie |
| Quand | au design-time | au runtime |
| Où elle vit | catalogue de contenu (JSON versionné en git + PocketBase) | état de session, dérivé du journal d'événements |
| Mutable ? | oui, mais versionnée | jamais directement : uniquement par réduction d'événements |
| Exemple | « Marion V., actrice, cachet 4 000 €/j, capricieuse » | « Marion V. est engagée, moral 42, 3 jours travaillés, en retard aujourd'hui » |
Corollaire : une fiche d'entité décrit toujours les deux, et jamais un champ
runtime dans une Def. Une Def doit pouvoir être rejouée à l'identique dans
mille parties différentes.
2. Identité et base commune
/** Toute définition de contenu hérite de ceci. */
type ContentBase = {
id: string // ULID, stable pour toujours, jamais réutilisé
slug: string // lisible, unique par kind : "marion-vasseur"
kind: EntityKind // discriminant
name: string // libellé affiché
summary?: string // une ligne, pour le backoffice et les listes
tags: string[] // libre, sert au filtrage éditeur et aux règles
packId: string // à quel « pack » de contenu / scénario ça appartient
schemaVersion: number
authoring: { // métadonnées d'atelier, jamais lues par le moteur
status: 'draft' | 'review' | 'ready'
notes?: string
updatedAt: string
}
}
type EntityKind =
| 'person' | 'element' | 'scene' | 'shot' | 'event'
| 'deal' | 'day' | 'budgetLine' | 'vision' // cf. docs/03-data-model.md
/** Tout état runtime hérite de ceci. */
type InstanceBase = {
instanceId: string // ULID généré à la création de l'instance
defId: string // -> ContentBase.id
createdAtTick: Tick // quand l'instance est apparue dans la partie
}
3. Unités et échelles — à respecter partout
- Argent : entier, en euros (pas de centimes ; un long métrage français
se compte en dizaines de milliers). Champ suffixé
_eur. Jamais de flottant. - Temps de jeu :
Tick= entier. 1 tick = une demi-journée de production (matin / après-midi). Une journée de tournage = 2 ticks. ⚠️ À reconsidérer : le cadrage retenu (une partie de 30–60 min, 12 à 20 tours au total) plaide pour un tick = une journée au tournage et une semaine en préparation et en post. La granularité réelle est arbitrée dansdocs/04-game-loop.md§1 ; les fiches ci-dessous ont été écrites avant cette décision et raisonnent en demi-journées. - Durée écran : en secondes (une scène de 2 min = 120).
- Statistiques : entier 0–100, jamais normalisé en 0–1 dans les données (0–1 seulement à l'intérieur des formules). 50 = compétent standard. Vocabulaire fixe des paliers : 0–19 catastrophique, 20–39 faible, 40–59 correct, 60–79 bon, 80–94 excellent, 95–100 exceptionnel.
- Probabilités :
0..1flottant, toujours suffixé_p. - Identifiants : ULID (triables chronologiquement, générables offline —
important, cf. le local-first de
docs/01-architecture.md).
4. La règle du perçu vs réel
Le cœur pédagogique du jeu — « engager le pas cher, le payer plus tard » — ne
fonctionne que si le joueur ne voit pas les vraies valeurs. Donc tout stat
qui peut mentir est un Rated<T> :
type Rated = {
actual: number // la vérité, jamais envoyée au client tant qu'elle
// n'est pas révélée
claimed: number // ce que le CV / l'agent / la démo annonce
confidence_p: number // 0..1 — à quel point le joueur peut se fier au claimed
revealedAtTick?: Tick // quand la vérité a été découverte, si elle l'a été
}
Règles :
- Le backoffice édite
actualetclaimedséparément. Un écart = une intrigue. - Le moteur calcule toujours avec
actual. - L'UI affiche
claimed+ une incertitude visuelle tant querevealedAtTickest vide. La révélation est un événement, pas un changement de champ. - Certains stats sont toujours honnêtes (le tarif journalier, la disponibilité
contractuelle) : ils restent des
numbernus. Le choix honnête/menteur est un point de design à trancher par entité — chaque fiche doit dire lesquels mentent.
5. Vocabulaire métier — les postes (départements)
Enum partagée, en français, utilisée par le dépouillement, le recrutement, le budget et les événements. C'est le vocabulaire que le joueur apprend.
type Poste =
| 'realisation' // réalisateur, 1er/2e assistant, scripte
| 'production' // directeur de prod (le joueur), administration, compta
| 'regie' // régie générale, régie adjointe, cantine, transports
| 'casting' // directeur de casting, comédiens, figuration
| 'image' // chef op, cadre, 1er assistant caméra, DIT
| 'machinerie' // chef machiniste, travellings, grues
| 'electro' // chef électro, groupe, lumière
| 'son' // ingé son, perchman
| 'deco' // chef décorateur, ensemblier, accessoiriste, construction
| 'costumes' // chef costumière, habilleuse
| 'hmc' // habillage-maquillage-coiffure (maquillage, coiffure, FX makeup)
| 'cascades' // coordinateur cascades, cascadeurs, doublures
| 'sfx' // effets spéciaux de plateau (pluie, feu, fumée, armurerie)
| 'vfx' // effets visuels numériques (superviseur sur le plateau + studio)
| 'animaux' // dresseur, soigneur
| 'enfants' // coach enfant, répétiteur, contraintes horaires légales
| 'post' // montage image, montage son, mixage, étalonnage, musique
| 'juridique' // contrats, droits, autorisations, assurances
Chaque Poste porte, dans une table de référence à part
(content/reference/postes.json), sa couleur de stabilo (le dépouillement
est un jeu de surlignage, cf. docs/06-ui-desktop-metaphor.md), son libellé
long, sa définition pédagogique et son chapitre de devis par défaut.
6. Vocabulaire métier — les chapitres du devis
Le budget n'est pas un nombre unique. Il est réparti en chapitres, à la manière d'un devis de long métrage français :
1 Droits artistiques (scénario, adaptation, droits musicaux)
2 Personnel (équipe technique)
3 Interprétation (comédiens, figuration, doublures)
4 Charges sociales (calculées, pas dépensées librement)
5 Décors et costumes (construction, location, HMC, accessoires)
6 Transports, défraiements, régie
7 Moyens techniques (caméra, machinerie, lumière, son, studio)
8 Post-production (montage, mixage, étalonnage, VFX, DCP)
9 Assurances et frais divers
10 Frais généraux + imprévus (l'enveloppe qui sauve ou qui manque)
⚠️ À vérifier contre un vrai devis CNC avant de figer les libellés et la numérotation. La structure (chapitres, charges sociales calculées, imprévus ~10 %) est la bonne ; les intitulés exacts sont à confirmer. Voir
docs/08-open-questions.md.
7. Comment une entité participe à la résolution
Le pipeline canonique du jeu, que chaque fiche doit situer :
Scene.requirements ──┐
├──► Assignment (qui/quoi, quand) ──► résolution ──► Shot
People + Elements ────────┘ ▲ │
│ ├─► qualité par critère
Day / plan de travail ├─► argent dépensé
│ ├─► temps consommé
Events (aléas) ───────────────────┘└─► nouveaux Events
Chaque fiche répond explicitement à : qu'est-ce que cette entité apporte ou consomme dans ce pipeline ?
8. Événements : tout changement d'état est un événement
Le state runtime est dérivé d'un journal append-only (cf.
docs/02-event-sourcing.md). Donc :
- une fiche ne décrit jamais « on met à jour le champ X » ;
- elle décrit « l'événement
personne.moral.baisseest émis, le réducteur applique … ». - Nommage des événements :
domaine.sujet.verbe_au_passé, en snake_case français, ex.casting.comedien.engage,tournage.plan.tourne,budget.ligne.depassee.
8 bis. i18n : aucune chaîne affichable n'est un string
Décidé après la rédaction des fiches, à appliquer partout : FR d'abord, EN ensuite, mais le modèle est bilingue dès maintenant.
type LocalizedText = { fr: string; en?: string }
- S'applique à
name,summary, à tous les textes d'incident et d'option, au texte des scènes, aux libellés de traits — bref, à tout ce qu'un joueur lit. - Ne s'applique pas aux
id,slug,tag,Poste, ni aux types d'événement : ce sont des identifiants techniques en français métier, et ils restent tels quels même en version anglaise (avec une infobulle). - Les fiches ci-dessous écrivent
stringlà où il faudra lireLocalizedText. À harmoniser au moment de figer les schémas Zod.
9. Plan des fiches
| fiche | entité | statut |
|---|---|---|
people.md | People — comédiens, technicien·ne·s, agents | |
production-elements.md | Éléments de production — matériel, décors, lieux, véhicules, animaux | |
scenes.md | Scènes — besoins (dépouillement) et résultats attendus | |
shots.md | Plans — l'unité produite, agrégée en scènes puis en film | |
events.md | Événements — aléas, conséquences, graphe narratif |
Les entités manquantes que ces cinq ne couvrent pas (Deal, Day, BudgetLine,
Vision, Reputation…) sont argumentées dans
../03-data-model.md.