FEATURE_MODULE_SPECIFICATION.md
Spécification des modules de features — Slot Factory Engine. Un bonus = un module indépendant, activé par la config du jeu, chargé dynamiquement (absent de la config ⇒ absent du bundle).
1. Interface commune (contrat)
export interface FeatureModule {
id: string; // 'freeSpins', 'bonusBuy', …
version: string;
// Déclarations (consommées par le Factory pour la checklist & la validation)
paramsSchema: JSONSchema; // paramètres autorisés dans game.config.json
requiredAssets(params): AssetKey[]; // clés de thème exigées
requiredSounds(params): SoundKey[];
requiredStrings(params): StringKey[]; // clés de locale exigées
bookEvents: string[]; // types d'events du book consommés
mathDeps: MathDep[]; // ex. { mode:'bonusbuy' }, { field:'freeSpinTrigger' }
conflictsWith?: string[]; // ex. anteBet ⟂ bonusBuy actifs ensemble
// Cycle de vie
hooks: {
onRegister(ctx: EngineContext): void; // câblage bus/ui/assets
onSpinStart?(ctx): void;
onBookEvent?(ctx, ev: BookEvent): Promise<void>; // cœur : PRESENTATION d'un fait du book
onSettle?(ctx): void;
onModeChange?(ctx, mode: string): void;
};
overlay?: () => Screen; // écran plein écran éventuel (intro FS, wheel, pick…)
uiSlots?: UiSlotSpec[]; // boutons/compteurs à insérer (ex. bouton BUY, meter)
devForces?: ForceSpec[]; // entrées du panneau simulation (dev-only)
}
Règles d'or : un module présente des events du book, il ne calcule JAMAIS un résultat · toutes ses chaînes passent par i18n (couche social incluse) · tout son visuel passe par le manifest (fallback placeholder) · il déclare tout ce qu'il consomme (le Factory rend l'invisible visible).
2. Modules v1 (consommés par les templates lines-5x3 / ways-5x3 / ultra-simple)
2.1 FreeSpinsFeature
- Params :
introScreen,summaryScreen,retrigger,counterPosition. - Assets :
bonus.freespins.intro,bg.bonus, panneau récap ; Sons : trigger, bgm bonus, retrigger ; Strings :fs.awarded,fs.remaining,fs.totalWin,fs.retrigger. - BookEvents :
freeSpinTrigger { total },updateFreeSpin { current,total },freeSpinEnd { totalWin }(noms alignés sur les samples web-sdk). - Écrans : intro (« X FREE SPINS » — tap/auto 3 s), récap (« TOTAL WIN » — rollup skippable).
- Math : mode BONUS dans index.json ; fréquence de trigger lue de
math.meta.json. - DevForces :
force fs trigger,force fs retrigger,force fs bigwin.
2.2 BonusBuyFeature
- Params :
modes(liste des modes d'achat — support multi-tiers 80×/200×/400×),confirmation(toujours true). - UI slot : plaque BUY à gauche de la grille (prix = cost×mise, live) ; modale de confirmation (pattern TNF validé).
- Math : coûts lus d'
index.json— jamais dans la config front. - Interdits : achat pendant autoplay actif ⇒ stoppe l'autoplay d'abord (bug TNF corrigé, encodé ici) ; désactivé si
anteBetactif.
2.3 MultiplierFeature
- Params :
style(orbs|badge|header),collectTo,sumOrProgressive(présentation). - BookEvents :
multiplierLand {cell,value},multiplierApply {total}. - Rendu : orbes sur cellules + vol vers le compteur + application au rollup (texte FX or du moteur).
2.4 AutoplayFeature (transverse, quasi-toujours actif)
- Compteurs configurables, conditions d'arrêt (any win / win > X / balance − X / bonus trigger), désactivable par juridiction, shield pendant les cinématiques (pattern TNF).
2.5 TurboFeature (transverse)
- Timings ÷3 ; auto-désactivé si
disabledTurbo(flag RGS d'authenticate) — le bouton disparaît.
2.6 CharacterActorFeature (différenciateur Cool Kids — opt-in)
- Params :
characters: { id, chromaRGB, clips: {state→file}, loopPolicy, reactions: {event→state+line} }. - Le pipeline chroma-key runtime complet de TNF (keying distance couleur + de-spill, crossfade, boucles alternées, voix synchro bulle).
- BookEvents : mappe les events de gain/perte/bonus sur des réactions.
- C'est le module qui « upgrade » n'importe quel template en machine à personnage.
3. Modules v2 (spécifiés, non construits — attendent leur 2e consommateur)
| Module | Cœur | Assets clés | BookEvents type |
|---|---|---|---|
RespinsFeature |
N respins, reset sur land | compteur, overlay colonne | respinTrigger/Step/End |
HoldAndWinFeature |
grille verrouillée, cash symbols, 3 respins reset | symbol.cash, cellules locked/empty, bg dédié |
hnwStart/Land/Reset/End{total} |
PickBonusFeature |
écran de picks, révélations | items idle/revealed ×N, bg | pickStart/Reveal{value}/End |
WheelFeature |
roue segmentée, résultat du book | disc/pointer/center | wheelStart/Result{segment} |
MysterySymbolFeature |
symboles voilés → reveal synchronisé | symbol.mystery + reveal |
mysteryReveal{cells,symbol} |
SymbolTransformFeature |
morph de symboles (ex. low→high) | anims transform ou fondu moteur | symbolTransform{from,to,cells} |
CollectionMeterFeature |
jauge de collecte persistante D'UN ROUND (jamais cross-round — RGS stateless, leçon TNF) | meter + icône | collectStep{n}/collectComplete |
AnteBetFeature |
+25 % coût, ×2 fréquence trigger, désactive BUY | toggle UI | (mode math dédié) |
ExpandingWildFeature etc. |
présentation d'events wild du book | états wild | wildExpand{reel} … |
JackpotDisplayFeature |
pseudo-jackpots FIXES par palier (pas de progressif cross-joueur — impossible en pré-simulé) | plaques paliers | jackpotHit{tier} |
4. Anti-patterns interdits (le lint les bloque)
- Un module qui lit
Math.random()pour décider d'un contenu de gain (seul le book décide ; le random cosmétique — particules, variations d'anim — est OK). - Un module qui écrit dans l'état d'un autre module (communication par bus d'événements uniquement).
- Un état persistant entre rounds côté client qui influence la présentation d'un résultat (RGS stateless).
- Un texte en dur (bypass i18n/social) ou un chemin d'asset en dur.
- Un écran plein-écran qui ne rend pas la main (
overlaydoit résoudre sa Promise — safety timeout du moteur, patternplayCinema).