SLOT_FACTORY_ARCHITECTURE.md
Architecture du Slot Factory Engine — Cool Kids Objectif : produire N machines publiables sur Stake Engine en remplaçant assets, thème, layout, maths et features — sans toucher au moteur.
1. Décision structurante n°1 — la stack
Options évaluées
| Option | Description | Pour | Contre |
|---|---|---|---|
| A. Web-sdk Stake complet | Svelte 5 + PixiJS 8 + TS (monorepo officiel) | Ce que les reviewers voient le plus ; reels/état déjà modélisés | Chaîne lourde ; notre expertise validée est ailleurs ; personnalisation profonde du rendu = se battre contre le framework ; notre fork n'a que le sample number-picker |
| B. Vanilla single-file étendu (TNF) | Continuer le pattern 1 fichier | Zéro build ; pipeline validé en review | Inmaintenable à l'échelle factory (2 551 lignes pour UN jeu simple) ; pas de réutilisation propre ; strip par regex fragile |
| C. Hybride (RECOMMANDÉ) | Vite + modules ES vanilla/TS · PixiJS 8 self-hosté pour la grille · nos modules TNF extraits en packages · schémas du web-sdk adoptés (bookEvents, handler map) | Le meilleur des deux : notre code éprouvé en review + un vrai renderer de grille + build réel (strip dev propre, atlas, hashing) ; UI chrome en DOM (ce qui a passé la review TNF, et bien plus simple à thémer depuis le Factory) | Introduit une étape de build (Vite) — assumé et documenté |
Décision C — détails d'application
- PixiJS 8 (MIT, self-hosté comme three.js sur TNF — static-files-only) uniquement pour le ReelStage : grille, atlas, masques, spins, particules, win-lines. Tout le chrome (bet bar, modales, plaques, FX texte) reste DOM/canvas, patterns validés TNF.
- Compatibilité math : on adopte les schémas du web-sdk officiel (
typesBookEvent,bookEventHandlerMap) → nos books restent 100 % au format ACP, et un jeu pourrait migrer vers le web-sdk sans refaire les maths. - Build : Vite, deux cibles —
dev(mock RGS + devpanel + simulation) etproduction(modules dev exclus du bundle, plus de strip par regex). Sortie =index.html+assets/à la racine du zip (structure de soumission validée TNF v2). - Three.js reste un module opt-in (
three-actor) pour les jeux à objet 3D (le coin TNF).
2. Décision structurante n°2 — le périmètre de généricité
Règle : un module n'entre dans le moteur que s'il a (ou aura immédiatement) DEUX consommateurs. Sinon il reste dans le dossier du jeu. C'est la parade anti-usine-à-gaz. Conséquence : v1 du moteur = modules consommés par (a) le template reels 5×3 et (b) TNF migré.
3. Vue d'ensemble — arborescence
SLOT FACTORY/
├── docs/ # les 8 livrables .md + guides
├── engine/ # LE MOTEUR (npm workspace)
│ └── packages/
│ ├── core/ # GameCore : machine à états, playBook, bus d'événements, session
│ ├── stake-rgs/ # Couche Stake (extraite de TNF, source-verified) — GELÉE et testée
│ ├── config/ # chargement + validation des game.config.json / math profiles
│ ├── theme/ # résolveur d'asset manifest, tokens CSS, fonts, préchargement
│ ├── layout/ # presets : fixed-stage-landscape, vw-layers-portrait, grid geometry
│ ├── reels/ # ReelStage PixiJS : strips, spin/stop/anticipation/turbo, win presentation
│ ├── features/ # modules de bonus (interface commune, § 6)
│ ├── ui/ # bet bar, boutons, modales, autoplay, fitVal, toasts, fatal
│ ├── audio/ # AudioBus (Web Audio, résilience iOS, hold/release bgm)
│ ├── fx/ # fx-text (annonceur or), particules, pluie de pièces, win tiers
│ ├── character-actor/ # personnages vidéo chroma-key (pattern TNF) — opt-in
│ ├── cinematics/ # vidéos plein écran (muted + piste Web Audio synchro)
│ ├── i18n/ # locales + couche compliance social (stake.us)
│ └── devtools/ # mock RGS, panneau de forçage, simulation — EXCLU du build prod
├── factory/ # L'OUTIL INTERNE
│ ├── site/ # panneau web : créer/dupliquer/importer assets/preview/valider/exporter
│ ├── qa/ # harnais Playwright (smoke, viewports, diff visuel, pièges Safari)
│ ├── math-tools/ # générateurs + vérificateurs (RTP exact Fraction, book↔LUT, REVIEW-EVENTS)
│ └── deploy/ # wrangler previews + packaging de soumission
├── templates/ # points de départ clonables
│ ├── lines-5x3/ # paylines classique
│ ├── ways-5x3/ # 243 ways
│ ├── ultra-simple/ # sans rouleaux (ADN TNF : un geste + un personnage)
│ └── (v2 : cluster-6x5, cascading, hold-and-win)
└── games/ # UN DOSSIER = UN JEU (aucun code moteur dedans)
├── _placeholder/ # thème neutre complet (fallbacks universels)
├── tails-never-fails/ # TNF migré (consomme le moteur ; la v2 soumise reste gelée ailleurs)
└── <nouveau-jeu>/
├── game.config.json # LA définition du jeu (cf. GAME_CONFIGURATION_SCHEMA.md)
├── theme/assets.json # manifest d'assets (clé logique → fichier)
├── theme/audio.json
├── locales/en.json
├── math/ # profil math (généré/validé par math-tools)
├── assets/ # fichiers finaux (nomenclature du SLOT_ASSET_MANIFEST)
└── art-src/ # sources de travail — jamais buildé
4. Les 7 couches (séparation stricte)
┌────────────────────────────────────────────────────────────┐
│ FACTORY SITE (création, validation, export) │
└──────────────┬─────────────────────────────────────────────┘
│ écrit/valide
┌──────────────▼─────────────────────────────────────────────┐
│ CONFIG D'UN JEU = math profile + theme + layout + features│
└──────────────┬─────────────────────────────────────────────┘
│ charge
┌──────────────▼───────────────┐ ┌────────────────────────┐
│ GAME CORE (machine à états) │◄──┤ STAKE INTEGRATION │
│ IDLE→BET→PLAY→BOOK→SETTLE │ │ (rgs client, replay, │
│ playBook → bus d'événements │ │ erreurs, resume, demo)│
└──────┬───────────┬───────────┘ └────────────────────────┘
│ events │ hooks
┌──────▼─────┐ ┌───▼──────────┐
│ FEATURES │ │ PRESENTATION │ ← ne décide JAMAIS d'un résultat
│ (modules) │ │ reels · ui · │ (le book fait foi, pattern TNF/web-sdk)
└────────────┘ │ fx · audio · │
│ char · ciné │
└──────────────┘
- GameCore : état du jeu, spins, résultats, gains, session, transitions, événements génériques (
spin:start,book:event:<type>,win:present,settle:done…). Ne connaît ni Pixi, ni le DOM, ni un thème. - Math Configuration : RTP, volatilité, reel strips, poids, paylines/ways, paytable, fréquence bonus, max win, multiplicateurs, bonus buy — tout vit dans le package math (books +
index.json+ unmath.meta.jsongénéré). Le front ne fait que LIRE (coûts de modes, wincap, labels) — jamais de constante math recopiée (leçon TNFBUY_COST). - Theme Configuration : nom, logo, couleurs (tokens CSS), fonts, backgrounds, cadres, symboles, vidéos, sons, textes, ambiance — 100 %
theme/*.json+ fichiers. - Layout Configuration : rouleaux/lignes, dimensions/position de grille, taille/espacement des symboles, position de l'UI, presets desktop (stage fixe 1920×1080 scalé) & mobile (couches vw plein écran) — les deux stratégies déjà validées en review sur TNF.
- Feature Modules : § 6.
- Presentation Layer : affichage, animations, transitions, états de boutons, responsive, audio-visuel. Interdiction architecturale de calculer un gain : elle présente ce que le book dit.
- Stake Integration Layer :
stake-rgs(authenticate/play/end-round/balance/bet-event, micro-unités, erreurs 200-body, resume, replay, mode demo/sandbox, checkpointsbet/event). Testée seule, commune à tous les jeux, et c'est le SEUL endroit qui parle au réseau.
5. Flux d'un spin (runtime)
tap SPIN
→ core.bet() # vérifie solde/mise (affichage seulement)
→ stake-rgs.play(amount, mode) # ou mock en dev
→ reçoit book { events[], payoutMultiplier }
→ reels.spinStart() # la grille tourne pendant l'aller-retour réseau
→ core.playBook(events) # séquence : chaque event dispatché
├─ 'reveal' → reels.stopOn(board) # positions du book
├─ 'winInfo' → fx.presentWins(lines/ways) # + audio tiers
├─ 'freeSpinTrigger' → features.freespins.enter()
├─ '<custom>' → handler déclaré par le jeu
└─ 'finalWin' → ui.updateWin()
→ stake-rgs.endRound() # si gain
→ core.settle() → IDLE
L'anticipation, le turbo, l'ordre d'arrêt des rouleaux = chorégraphie de présentation paramétrée par le layout/thème — le résultat est déjà connu.
6. Interface d'un Feature Module
interface FeatureModule {
id: 'freeSpins' | 'bonusBuy' | 'multiplier' | 'respins' | 'holdAndWin'
| 'pickBonus' | 'mysterySymbol' | 'symbolTransform' | 'collectionMeter' | string;
paramsSchema: JSONSchema; // paramètres autorisés (validés au chargement)
requiredAssets(params): AssetKey[];// ce que le manifest doit fournir (checklist Factory)
requiredSounds(params): SoundKey[];
bookEvents: string[]; // events du book qu'il consomme
mathDeps: string[]; // ce que le package math doit déclarer (ex. mode bonusbuy)
hooks: {
onRegister(ctx) // câblage (bus, ui, assets)
onSpinStart?(ctx)
onBookEvent?(ctx, ev) // cœur du module
onSettle?(ctx)
};
overlay?: () => Screen; // écran/overlay plein écran éventuel (intro FS, wheel…)
devForces?: ForceSpec[]; // entrées du panneau de simulation (dev-only)
}
Activation par config : features: { freeSpins: {...params}, bonusBuy: {...} } — un module absent de la config n'est pas chargé (tree-shaken du bundle). Détail par module : FEATURE_MODULE_SPECIFICATION.md.
7. Thème & assets — résolution
theme/assets.jsonmappe clé logique → fichier (+ variantes landscape/portrait, états).- Le loader précharge par priorité (P0 : premier écran ; P1 : spin ; P2 : bonus — lazy).
- Clé manquante → placeholder du thème neutre + warning remonté au Factory (jamais de crash).
- Couleurs/fonts → CSS custom properties injectées (
--gold,--panel,--font-display…). - Les pièges Safari connus (filter+transform, background-clip+scale, clip-path+filter, drop-shadow sur PNG) sont encapsulés dans les composants du moteur — un thème ne peut pas les réintroduire.
8. Migration de TNF (preuve de généricité n°1)
- La v2 soumise reste gelée (
TAILS NEVER FAILS/intouché, prod). games/tails-never-fails/: copie restructurée qui importestake-rgs,audio,fx,character-actor,cinematics,ui.modals,i18n,devtools— sa présentation coin-flip reste spécifique (templateultra-simple).- Critère de réussite : diff visuel Playwright ≈ 0 sur les parcours clés (landing, spin, gain, bonus, replay, portrait+landscape) entre la v2 gelée et la version migrée.
- Bénéfice immédiat : toute correction RGS/compliance future se fait UNE fois dans le moteur.
9. Second jeu démo (preuve n°2)
games/demo-frontier/ (western) : template lines-5x3, thème placeholder habillé cowboy minimal, maths générées par math-tools (profil médium), free spins + bonus buy. Même moteur, zéro ligne de code spécifique hors config/thème → c'est le test d'acceptation du Factory.
10. Critères de fin de phase (définition of done)
| Phase | Fini quand |
|---|---|
| Docs | Les 8 .md livrés et validés par toi |
| Moteur core | Template lines-5x3 joue en mock : spin, gains, free spins, bonus buy, autoplay, turbo, portrait+landscape |
| Placeholder | Un jeu 100 % placeholder tourne sans un seul asset custom |
| Factory site | Create → import assets → preview → validate → export produit un zip conforme |
| Migration TNF | Diff visuel ≈ 0 + tests verts |
| Démo 2e thème | Jeu western jouable, généré sans toucher au moteur |
| Tests | Suite verte (unit + Playwright) ; simulation dev-only inerte en prod |