Aller au contenu principal

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éel'auteur, dans le backofficele moteur, pendant une partie
Quandau design-timeau runtime
Où elle vitcatalogue de contenu (JSON versionné en git + PocketBase)état de session, dérivé du journal d'événements
Mutable ?oui, mais versionnéejamais 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 dans docs/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..1 flottant, 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 actual et claimed séparément. Un écart = une intrigue.
  • Le moteur calcule toujours avec actual.
  • L'UI affiche claimed + une incertitude visuelle tant que revealedAtTick est 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 number nus. 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.baisse est é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 string là où il faudra lire LocalizedText. À harmoniser au moment de figer les schémas Zod.

9. Plan des fiches

ficheentitéstatut
people.mdPeople — comédiens, technicien·ne·s, agents
production-elements.mdÉléments de production — matériel, décors, lieux, véhicules, animaux
scenes.mdScènes — besoins (dépouillement) et résultats attendus
shots.mdPlans — 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.