CURRENT_SLOT_REFACTOR_AUDIT.md
Audit du projet existant (Tails Never Fails v2, approuvé & publié) en vue du Slot Factory Engine
Réf. audité : TAILS NEVER FAILS/game/index.html (2 551 lignes : ~874 CSS + ~1 508 JS + DOM) · TAILS NEVER FAILS/math/tails_never_fails/ · état au 15 juil. 2026.
Règle d'or de cette migration : la build v2 approuvée par Stake est GELÉE. On ne refactore jamais le fichier soumis en place ; on extrait vers le moteur et TNF devient un consommateur de la couche partagée. Zéro risque de régression sur un jeu déjà en production.
0. Chiffres du couplage (mesurés)
| Mesure | Valeur | Verdict |
|---|---|---|
| Fichier unique index.html | 2 551 lignes | Monolithe — à découper en modules |
| Chemins d'assets codés en dur | 70 (./assets/...) |
À déplacer vers un asset manifest par thème |
| Références au thème (goblin/panther/ferret) | 52 | Majoritairement déjà dans des objets data → JSON de thème |
| Constantes math dans le front | FREE_FLIPS=5, BUY_COST=100 (+ P_TAILS dans le bloc demo) |
À servir par la config du jeu (dérivée d'index.json math) |
| Blocs demo strippés par regex | 2 blocs __DEMO_ONLY |
Fragile — remplacer par une vraie étape de build |
| Assets totaux | ~123 Mo (52 Mo de clips villains) | OK pour TNF (jeu à vidéo) ; INTERDIT pour un slot à rouleaux (voir PERFORMANCE_BUDGET) |
1. DÉJÀ GÉNÉRIQUE — extraire tel quel (les bijoux de famille)
Ces systèmes ont été validés en production par la review Stake. Ils deviennent les premiers modules du moteur, quasiment sans modification.
| Système | Où (repères) | Devient |
|---|---|---|
Client RGS complet — rgsCall, authenticate/play/end-round/balance/bet-event, montants en micro-unités (×1e6), params de launch (rgs_url, sessionID, lang, device, social, currency), reprise de round actif, erreurs (y compris erreurs dans un body 200, quirk vérifié contre le source ts-client), payoutMultiplier float direct, /bet/event en string |
let RGS = {...} + rgsCall |
packages/stake-rgs/ — LA couche Stake Integration, commune à tous les jeux |
Bet Replay — replay=true, GET /bet/replay/..., bet bar visible, SPIN rejoue, zéro appel wallet authentifié |
bootReplay() |
Module replay du moteur (mode du GameCore) |
Couche social/stake.us — table STR/STR_SOCIAL (bet→play, pays→wins…), SC/GC sans $, audité 0 résidu |
S(), applyStrings() |
Module i18n+compliance (le pattern de substitution est générique) |
Audio Web Audio — loadBuf/sfx, décodage buffers, armAudio sur geste, résilience iOS (resume dans sfx + au trigger bonus), holdBgm/releaseBgm autour des cinématiques, alias de fichiers, fallback HTMLAudio |
bloc audio | modules/audio/ — AudioBus générique piloté par manifest |
Lecteur de cinématiques — playCinema/hideCinema : safety timeout, hook « pluie à N s de la fin », fondu du coin, shield anti-tap |
modules/cinematics/ |
|
| fitVal() — aucun montant ne déborde jamais de sa plaque (boucle scrollWidth, min 8px) | util UI du moteur | |
| Framework modales conformité — règles/paytable (disclaimer officiel verbatim, table coût/RTP/max-win par mode), confirmations autoplay & bonus-buy | .modal-veil etc. |
modules/modals/ + contenu injecté par config |
| Gestion d'erreurs — écran fatal, toasts, codes RGS | fatal(), toast() |
GameCore |
| Harnais de QA Playwright — screenshots multi-viewports, forçage d'événements, comparaison au mockup, tests de clic/opacité | scripts scratchpad de la session | factory/qa/ — formalisé en scripts de validation (Phase 10) |
| Pipeline de déploiement — wrangler Pages, previews hashées par déploiement | factory/deploy/ |
2. RÉUTILISABLE APRÈS REFACTOR LÉGER
| Système | Couplage actuel | Refactor |
|---|---|---|
Moteur FX texte or (fxAnnounce canvas : layout par glyphe, enter/hold/exit, sticky+tags, halo, pièces) |
Cinzel 900 + palette or codées en dur ; coords cy par jeu |
Paramétrer police/palettes/positions via thème → modules/fx-text/ |
Acteur personnage chroma-key (villainLoop : distance couleur + de-spill sur canvas, crossfade #villainFade, boucles panic alternées) |
CHROMA par villain, chemins _gs.mp4 en dur |
→ modules/character-actor/ : config {clips, chromaRGB, states, loopPolicy}. Réutilisable pour TOUT jeu à personnage réactif (gros différenciateur Cool Kids) |
| Autoplay (compteurs, garde bonus/busy, confirmation) | mélangé au flux TNF | Extraire la machine à états ; hooks onAutoStep |
| Logique bet bar (stepper mise, MAX, clamp 0.10..balance, niveaux RGS, ~30 devises) | skin TNF dans le CSS | Séparer logique (générique) / skin (thème) |
Objet 3D CSP-safe (Three.js + glTF en data-URI car le CDN Stake bloque blob:) |
Coin.gltf spécifique | Pattern documenté + loader générique modules/three-actor/ (optionnel par jeu) |
Pattern playBook — itérer les events du book → handler map (evCoinFlip, evBonusTrigger…) — c'est exactement le bookEventHandlerMap du web-sdk |
handlers spécifiques TNF | Le moteur fournit playBook + bus d'événements ; chaque jeu déclare ses handlers (cœur de l'architecture Phase 4) |
3. FORTEMENT COUPLÉ AU THÈME (à passer en config/thème JSON)
- Les 70 chemins d'assets →
theme/assets.json(manifest). Le code ne référence plus que des clés logiques (ui.spinButton,bg.room,character.goblin.idle). - Le roster :
VILLAINS,REACT,VLINE,VOICE_FILES,CHROMA,REACT_CY,PANIC_BY_VILLAIN,BONUS_CLIPS— déjà des objets data dans le code, la moitié du travail est faite : les sortir entheme/characters.json. - Tout le CSS skin (16:9 fixed-stage 1920×1080 + portrait en couches vw) : tokeniser couleurs/fonts en CSS custom properties + images via manifest. Les DEUX stratégies de layout (stage fixe scalé / couches vw plein écran) deviennent des presets de layout du moteur — elles sont toutes les deux validées en prod.
- Landing « Choose your opponent » : structure générique (cards + deep-link
?villain=), contenu par thème.
4. FORTEMENT COUPLÉ AUX MATHS ACTUELLES
FREE_FLIPS,BUY_COSTdupliqués front/math → source unique : la config jeu, générée depuis les fichiers math (index.jsonporte déjàcostpar mode).- Le schéma d'events de book (
coinFlip{villain,cover,landed,multiplier,win},bonusFlip,finalWin) est propre à TNF → chaque jeu déclare son schéma d'events + handlers ; le moteur n'en connaît que l'enveloppe (playBook,betEventcheckpoints). - Le MockRgs demo embarque
P_TAILS/R_BONUS→ le mock du moteur lira une table de tirage générée depuis les books (pas de constantes recopiées).
5. À RECONSTRUIRE (n'existe pas dans TNF)
Tout le cœur « rouleaux » — TNF est un coin-flip, il n'y a NI grille, NI symboles, NI paylines : * ReelGrid (n×m, masque, cadre), reel strips, spin/stop/anticipation, turbo ; * évaluation des gains (paylines / ways / cluster / scatter-pays) côté présentation uniquement (le résultat vient toujours du book) ; * présentation des gains (lignes, compteur, paliers Big/Mega/Epic Win) ; * cascades/tumble, symboles spéciaux (wild/scatter/mystery/sticky/géants).
→ C'est le chantier neuf principal. Recommandation techno en Phase 4 (PixiJS 8 self-hosté pour le rendu grille, en s'appuyant sur les schémas du web-sdk officiel — bookEventHandlerMap, typesBookEvent — dont on a déjà un fork dans game/web-sdk-app/).
Côté math : le math-sdk officiel gère nativement reels/paytable/optimizer — nos scripts (generate_v2.py, verify_v2.py) apportent en plus le pinning RTP exact en Fractions et le vérificateur book↔LUT ligne à ligne → généralisés en factory/math-tools/ (validation de N'IMPORTE quel package math avant soumission).
6. À SUPPRIMER / ASSAINIR
| Élément | Action |
|---|---|
play.html (redirect legacy) |
Supprimer du moteur (garder dans TNF gelé) |
| Assets sources non référencés (mp3 bruts, mockups, vieux boutons — déjà exclus du zip v2) | Politique factory : les sources vivent HORS de game/assets/ (dossier art-src/ non buildé) |
| Strip des blocs demo par regex | Étape de build réelle (flag --production qui exclut le module mock/devpanel) |
Duplication dist-submission* à la main |
Généré par l'action Export du Slot Factory |
7. RISQUES IDENTIFIÉS & PARADES
| Risque | Gravité | Parade |
|---|---|---|
| Régression sur TNF publié | ÉLEVÉE | TNF v2 gelé ; migration sur COPIE ; diff visuel Playwright pixel-à-pixel avant tout swap |
| Le moteur reels devient une usine à gaz abstraite | ÉLEVÉE | On ne généralise que ce que DEUX jeux réels consomment ; chaque module doit avoir un consommateur concret (règle : pas d'abstraction sans 2e client) |
| Poids assets des slots vidéo-heavy | MOYENNE | Budgets stricts par catégorie (PERFORMANCE_BUDGET.md) ; le style « personnage vidéo chroma » reste un module OPT-IN coûteux |
| Sur-couplage au web-sdk Svelte (chaîne de build lourde) | MOYENNE | On adopte les schémas du web-sdk (events, structure math) sans forcément sa stack UI ; décision argumentée en Phase 4 |
| Pièges Safari re-découverts à chaque jeu (filter+transform, background-clip+scale, clip-path+filter, autoplay audio…) | MOYENNE | Tous les pièges rencontrés sont encodés dans le moteur (lint CSS + doc SAFARI_TRAPS.md + tests QA dédiés) |
| Deux sources de vérité front/math | MOYENNE | La config front est générée depuis le package math (costs, wincap, features) — jamais éditée à la main |
8. Verdict de migration TNF
TNF ne devient PAS un jeu à rouleaux : il est migré comme « template machine ultra-simple » du factory — il consomme stake-rgs, audio, fx-text, character-actor, cinematics, modals, i18n, et garde sa présentation propre. C'est la preuve vivante que le moteur supporte des jeux non-reels (l'ADN Cool Kids), pendant que le template reels 5×3 sert les jeux « classiques » (cowboy, aliens, mythologie, horreur).
9. RÉSULTAT DE MIGRATION (exécutée)
La migration décrite au §8 a été réalisée le 2026-07-15 :
- Copie migrée :
migration/tails_never_fails/(assets symlinkés vers la build gelée — hors degames/pour ne pas alourdir les builds Vite). - Couche remplacée : le client RGS inline (127 lignes) est supprimé ; la copie importe
engine/src/stake-rgs/rgs.js— LE même module que les jeux à rouleaux. La devise est fournie par un getter (state.currency), lue à chaqueplaycomme dans l'original.boot()attendRGS_READYavantauthenticate. - Acceptance mesurée (Playwright, 1280×720 + 390×844) :
- landing : 0.000 % de pixels différents (identique au pixel près)
- scène de jeu : 0.013 – 0.509 % — uniquement le frame courant de la vidéo du villain (timing non déterministe), aucune différence de rendu
- flip complet en demo : solde débité/crédité, 0 erreur JS
- La v2 approuvée par Stake reste gelée dans
TAILS NEVER FAILS/game/— aucun octet modifié. - Modules suivants (audio, cinématiques, modales) : déjà extraits dans le moteur sous forme généralisée (audiobus, fx, modals) ; leur branchement dans la copie TNF suivra le même patron adaptateur, sans urgence puisque la build publiée est gelée.