// ---------- Affichage des écrans, timeline d'animation ---------- // Extrait de templates/play.html (voir plan de modularisation — un // fichier = une responsabilité, aussi pour le JS) : navigation entre // écrans, proportion de la zone de jeu, lecture des clips d'animation // posés dans l'éditeur ("Timeline d'animation"). // Élément à onglets (voir render_onglets.py) : bascule le panneau visible // sans aller-retour serveur — même fonction que dans screen_edit.html, // pour un comportement identique éditeur/jeu. function forgeShowTab(btn) { const groupId = btn.dataset.tabBtn; const targetId = btn.dataset.tabTarget; document.querySelectorAll(`[data-tab-btn="${groupId}"]`).forEach((b) => { b.classList.toggle('is-active', b === btn); }); document.querySelectorAll(`[data-tab-group="${groupId}"]`).forEach((p) => { p.style.display = (p.id === targetId) ? '' : 'none'; }); } function showScreen(id) { // Déjà l'écran affiché : rien à refaire. Sans ce garde-fou, l'action // "Ouvrir la ligne cliquée" (voir plus bas) — pensée pour révéler un // détail SUR LE MÊME écran qu'un Répéteur, en la combinant avec une // action "Modifier un élément → Visibilité" sur le même clic — rejouait // à chaque clic sur une ligne les déclencheurs "À l'affichage de // l'écran" et relançait la timeline d'animation depuis le début, ce qui // donnait l'impression trompeuse d'un rechargement de page (éléments // qui rejouent leur animation d'entrée, visibilité qu'un déclencheur // "affichage" repasse à son état initial en écrasant l'action "Rendre // visible" du même clic...). Aller vers un écran DIFFÉRENT continue de // tout rejouer normalement, comme un vrai premier affichage. if (id === window.currentScreenId) return; document.querySelectorAll('.playScreen').forEach((s) => s.style.display = 'none'); const el = document.querySelector(`.playScreen[data-screen-id="${id}"]`); if (el) { el.style.display = 'block'; window.currentScreenId = id; applyScreenAspect(el.dataset.aspect || '9/16'); applyScreenKind(el.dataset.kind || 'document'); } // Phase 7 — nettoie les animations de sprites de l'écran quitté (sinon // une boucle setInterval reste active sur un élément désormais caché, // mirroring le nettoyage déjà fait pour activeTimerTriggerIntervals // dans triggers.js). stopAllSpriteAnimations(); runScreenShowTriggers(id); runScreenTimerTriggers(id); runScreenHeldKeyTriggers(id); runAnimationTimeline(id); runScreenBackgroundMusic(id); // Déplacement/animation automatiques de tout personnage posé sur cet // écran (voir static/js/play/personnage-controller.js) — n'a d'effet // que sur un écran "jeu_2d" contenant au moins un objet kind="personnage". forgeStartPersonnageControllers(screensData.find((s) => s.id === id)); // Règles "à la collision/dans un périmètre -> action" (voir // static/js/play/collision-rules-controller.js) — démarré juste après // le contrôleur de personnage dont il dépend (position du joueur). forgeStartCollisionRuleControllers(screensData.find((s) => s.id === id)); // Déclencheurs D'ÉCRAN "à l'affichage" (voir screen-triggers-controller.js, // screens/rendering/screen_triggers.py) — narration/cinématique sans // objet requis, exécutée à chaque affichage de CET écran. forgeRunScreenTriggers(screensData.find((s) => s.id === id)); // Widget "🏆 Score" (voir static/js/play/dialogue-box-controller.js) : // synchronise le compteur affiché sur CET écran avec le total déjà // gagné — sans ça, un score posé sur un écran affiché APRÈS que le // joueur ait déjà marqué des points ailleurs resterait figé à "0" // (valeur d'exemple rendue côté serveur) jusqu'au prochain point gagné. if (typeof forgeUpdateAllScoreWidgets === 'function') forgeUpdateAllScoreWidgets(); } // Phase 6 — musique de fond par écran (voir screen-bg-music-url dans // screen_edit.html) : démarrée en boucle à l'affichage de l'écran, // arrêtée au changement d'écran — un seul Audio actif à la fois (pas un // tableau comme activeTimerTriggerIntervals dans triggers.js, une // musique de fond ne se cumule jamais avec une autre). let currentBackgroundAudio = null; function runScreenBackgroundMusic(screenId) { if (currentBackgroundAudio) { currentBackgroundAudio.pause(); currentBackgroundAudio = null; } const screenData = screensData.find((s) => String(s.id) === String(screenId)); const url = screenData && screenData.background_music_url; if (!url) return; currentBackgroundAudio = new Audio(url); currentBackgroundAudio.loop = true; currentBackgroundAudio.play().catch(() => {}); // NOSONAR S2486 - autoplay bloque, voir CODE_QUALITY.md } // Voir le commentaire sur #playFrame dans le