// === CHARGEUR DE JEUX DYNAMIQUE === const GameLoader = { currentGame: null, contentScanner: new ContentScanner(), loadedModules: { games: {}, content: {} }, async loadGame(gameType, contentType) { try { // Nettoyage du jeu précédent this.cleanup(); // Chargement parallèle du module de jeu et du contenu const [gameModule, contentModule] = await Promise.all([ this.loadGameModule(gameType), this.loadContentModule(contentType) ]); // Initialisation du jeu this.initGame(gameType, gameModule, contentModule); } catch (error) { console.error('Erreur lors du chargement du jeu:', error); throw error; } }, async loadGameModule(gameType) { // Vérifier si le module est déjà chargé if (this.loadedModules.games[gameType]) { return this.loadedModules.games[gameType]; } try { // Chargement dynamique du script await this.loadScript(`js/games/${gameType}.js`); // Récupération du module depuis l'objet global const module = window.GameModules?.[this.getModuleName(gameType)]; if (!module) { throw new Error(`Module de jeu ${gameType} non trouvé`); } // Cache du module this.loadedModules.games[gameType] = module; return module; } catch (error) { console.error(`Erreur chargement module jeu ${gameType}:`, error); throw error; } }, async loadContentModule(contentType) { // Utiliser le ContentScanner pour récupérer le contenu découvert try { // Récupérer le contenu déjà découvert par le scanner const contentInfo = await this.contentScanner.getContentById(contentType); if (!contentInfo) { throw new Error(`Contenu ${contentType} non trouvé par le scanner`); } // Charger le module JavaScript correspondant await this.loadScript(`js/content/${contentInfo.filename}`); // Récupérer le module depuis l'objet global const moduleName = this.getContentModuleName(contentType); const rawModule = window.ContentModules?.[moduleName]; if (!rawModule) { throw new Error(`Module ${moduleName} non trouvé après chargement`); } // Combiner les informations du scanner avec le contenu brut const enrichedContent = { ...rawModule, ...contentInfo, // S'assurer que le contenu brut du module est disponible rawContent: rawModule }; this.loadedModules.content[contentType] = enrichedContent; return enrichedContent; } catch (error) { console.error(`Erreur chargement contenu ${contentType}:`, error); throw error; } }, loadScript(src) { return new Promise((resolve, reject) => { // Vérifier si le script est déjà chargé const existingScript = document.querySelector(`script[src="${src}"]`); if (existingScript) { resolve(); return; } const script = document.createElement('script'); script.src = src; script.onload = resolve; script.onerror = () => reject(new Error(`Impossible de charger ${src}`)); document.head.appendChild(script); }); }, initGame(gameType, GameClass, contentData) { const gameContainer = document.getElementById('game-container'); const gameTitle = document.getElementById('game-title'); const scoreDisplay = document.getElementById('current-score'); // Le contenu est déjà enrichi par le ContentScanner, pas besoin d'adaptation supplémentaire const adaptedContent = contentData; // Mise à jour du titre const contentName = adaptedContent.name || contentType; gameTitle.textContent = this.getGameTitle(gameType, contentName); // Réinitialisation du score scoreDisplay.textContent = '0'; // Création de l'instance de jeu avec contenu enrichi this.currentGame = new GameClass({ container: gameContainer, content: adaptedContent, contentScanner: this.contentScanner, // Passer le scanner pour accès aux métadonnées onScoreUpdate: (score) => this.updateScore(score), onGameEnd: (finalScore) => this.handleGameEnd(finalScore) }); // Démarrage du jeu this.currentGame.start(); }, updateScore(score) { const scoreDisplay = document.getElementById('current-score'); scoreDisplay.textContent = score.toString(); // Animation du score Utils.animateElement(scoreDisplay, 'pulse', 200); }, handleGameEnd(finalScore) { // Sauvegarde du score this.saveScore(finalScore); // Affichage du résultat Utils.showToast(`Jeu terminé ! Score final: ${finalScore}`, 'success'); // Afficher les options de fin de jeu this.showGameEndOptions(finalScore); }, showGameEndOptions(finalScore) { const gameContainer = document.getElementById('game-container'); // Créer l'overlay de fin de jeu const endOverlay = document.createElement('div'); endOverlay.className = 'game-end-overlay'; endOverlay.innerHTML = `

🎉 Jeu Terminé !

Score final: ${finalScore}
Meilleur score: ${this.getBestScoreForCurrentGame()}
`; gameContainer.appendChild(endOverlay); // Ajouter les event listeners endOverlay.querySelector('.restart-game-btn').addEventListener('click', () => { this.removeGameEndOverlay(); this.restartCurrentGame(); }); endOverlay.querySelector('.back-to-levels-btn').addEventListener('click', () => { const params = Utils.getUrlParams(); AppNavigation.navigateTo('levels', params.game); }); endOverlay.querySelector('.back-to-games-btn').addEventListener('click', () => { AppNavigation.navigateTo('games'); }); // Fermer avec ESC const handleEscape = (e) => { if (e.key === 'Escape') { this.removeGameEndOverlay(); AppNavigation.goBack(); document.removeEventListener('keydown', handleEscape); } }; document.addEventListener('keydown', handleEscape); }, removeGameEndOverlay() { const overlay = document.querySelector('.game-end-overlay'); if (overlay) { overlay.remove(); } }, getBestScoreForCurrentGame() { const params = Utils.getUrlParams(); return this.getBestScore(params.game, params.content); }, restartCurrentGame() { if (this.currentGame && this.currentGame.restart) { this.currentGame.restart(); document.getElementById('current-score').textContent = '0'; } }, cleanup() { if (this.currentGame && this.currentGame.destroy) { this.currentGame.destroy(); } // Supprimer l'overlay de fin de jeu s'il existe this.removeGameEndOverlay(); const gameContainer = document.getElementById('game-container'); gameContainer.innerHTML = ''; this.currentGame = null; }, saveScore(score) { const params = Utils.getUrlParams(); const scoreKey = `score_${params.game}_${params.content}`; const currentScores = Utils.storage.get(scoreKey, []); currentScores.push({ score: score, date: new Date().toISOString(), timestamp: Date.now() }); // Garder seulement les 10 meilleurs scores currentScores.sort((a, b) => b.score - a.score); const bestScores = currentScores.slice(0, 10); Utils.storage.set(scoreKey, bestScores); }, getBestScore(gameType, contentType) { const scoreKey = `score_${gameType}_${contentType}`; const scores = Utils.storage.get(scoreKey, []); return scores.length > 0 ? scores[0].score : 0; }, // Utilitaires de nommage getModuleName(gameType) { const names = { 'whack-a-mole': 'WhackAMole', 'whack-a-mole-hard': 'WhackAMoleHard', 'memory-match': 'MemoryMatch', 'quiz-game': 'QuizGame', 'temp-games': 'TempGames', 'fill-the-blank': 'FillTheBlank', 'text-reader': 'TextReader', 'adventure-reader': 'AdventureReader', 'chinese-study': 'ChineseStudy' }; return names[gameType] || gameType; }, getContentModuleName(contentType) { // Utilise la même logique que le ContentScanner const mapping = { 'sbs-level-7-8-new': 'SBSLevel78New', 'basic-chinese': 'BasicChinese', 'english-class-demo': 'EnglishClassDemo' }; return mapping[contentType] || this.toPascalCase(contentType); }, toPascalCase(str) { return str.split('-').map(word => word.charAt(0).toUpperCase() + word.slice(1) ).join(''); }, getGameTitle(gameType, contentName) { const gameNames = { 'whack-a-mole': 'Whack-a-Mole', 'whack-a-mole-hard': 'Whack-a-Mole Hard', 'memory-match': 'Memory Match', 'quiz-game': 'Quiz Game', 'temp-games': 'Mini-Jeux', 'fill-the-blank': 'Fill the Blank', 'text-reader': 'Text Reader', 'adventure-reader': 'Adventure Reader' }; const gameName = gameNames[gameType] || gameType; return `${gameName} - ${contentName}`; }, // API pour les jeux createGameAPI() { return { showFeedback: (message, type = 'info') => Utils.showToast(message, type), playSound: (soundFile) => this.playSound(soundFile), updateScore: (score) => this.updateScore(score), endGame: (score) => this.handleGameEnd(score), getBestScore: () => { const params = Utils.getUrlParams(); return this.getBestScore(params.game, params.content); } }; }, playSound(soundFile) { if (Utils.canPlayAudio()) { try { const audio = new Audio(`assets/sounds/${soundFile}`); audio.volume = 0.5; audio.play().catch(e => console.warn('Cannot play sound:', e)); } catch (error) { console.warn('Sound error:', error); } } } }; // Export global window.GameLoader = GameLoader;