////////////////////////////////////////////// // Game.js - hlavný kód pre minmihru miner // // Autor: Adam Renak // // Diplomová práca - 28.8.2025 // ////////////////////////////////////////////// // ========================================== // GLOBÁLNE PREMENNÉ PRE PRELOADING // ========================================== let preloadedImages = {}; // Cache pre prednačítané obrázky let totalResources = 0; // Celkový počet zdrojov let loadedResources = 0; // Počet načítaných zdrojov let isPreloadingComplete = false; // Flag či je preloading hotový ////////////////////////////////////////////// // ============ LOADING SCREEN ============ // // Čakanie na načítanie DOM obsahu // // Skrytie loading screen s animáciou // ////////////////////////////////////////////// document.addEventListener('DOMContentLoaded', function() { console.log('🎮 DOM načítaný, spúšťam preloading...'); // Inicializuj Speech Recognition initSpeechRecognition(); // Spusti prednačítanie zdrojov startPreloading(); }); /** * Hlavná funkcia preloadingu */ async function startPreloading() { try { // 1. Získaj konfiguráciu levelu z URL const levelConfig = getLevelConfigFromURL(); // 2. Zisti všetky obrázky na načítanie const imagesToLoad = collectAllImages(levelConfig); totalResources = imagesToLoad.length; console.log(`📦 Načítavam ${totalResources} obrázkov...`); // 3. Načítaj všetky obrázky paralelne const promises = imagesToLoad.map(imagePath => preloadImage(imagePath)); await Promise.all(promises); console.log('✅ Všetky obrázky načítané!'); isPreloadingComplete = true; // 4. Skry loading screen a spusti hru setTimeout(() => { hideLoadingScreen(); // Inicializuj hru s level configom if (levelConfig) { initializeGameWithLevel(levelConfig); } }, 500); } catch (error) { console.error('❌ Chyba pri preloadingu:', error); // Aj pri chybe spusti hru hideLoadingScreen(); } } /** * Získanie konfigurácie levelu z URL parametrov */ function getLevelConfigFromURL() { const urlParams = new URLSearchParams(window.location.search); const worldId = urlParams.get('worldId') || urlParams.get('world'); const levelId = urlParams.get('levelId') || urlParams.get('level'); // ======================================== // PRIDANÉ: Načítanie tréningovej konfigurácie // ======================================== const isTraining = urlParams.get('training') === 'true'; const trainingConfig = urlParams.get('config'); // Ak je tréningový level, načítaj konfiguráciu z URL if (isTraining && trainingConfig) { try { const config = JSON.parse(decodeURIComponent(trainingConfig)); console.log('📋 Načítaná tréningová konfigurácia:', config); return config; // Vráť tréningovú konfiguráciu } catch (error) { console.error('❌ Chyba pri parsovaní tréningovej konfigurácie:', error); // Pokračuj na fallback } } console.log('URL parametre:', { worldId, levelId }); // Ak máme levelId, načítaj konfiguráciu if (levelId && typeof window.getLevelConfig === 'function') { const config = window.getLevelConfig(levelId); console.log('📋 Načítaná level konfigurácia:', config); return config; } // Fallback konfigurácia console.warn('⚠️ Používam fallback konfiguráciu'); return { id: 'fallback', worldId: worldId || 'world_r', words: ['rak', 'ryba', 'ruka', 'ruža'], gameConfig: { diamonds: 3, golds: 3, crystals: 1 } }; } /** * Zber všetkých obrázkov ktoré treba načítať */ function collectAllImages(levelConfig) { const images = []; // ========================================== // 1. OBRÁZKY SLOV (najdôležitejšie!) // ========================================== if (levelConfig && levelConfig.words) { levelConfig.words.forEach(word => { images.push(`images/slova/${word}.png`); }); console.log(` ✅ Pridaných ${levelConfig.words.length} obrázkov slov`); } // ========================================== // 2. SPRITE OBRÁZKY HRÁČA // ========================================== images.push( 'images/hrac.png', 'images/hrac-otoceny-vlavo.png', 'images/hrac-otoceny-vpravo.png', 'images/hrac-kope-vpravo.png', 'images/hrac-kope-vlavo.png' ); // ========================================== // 3. ITEMY (diamanty, zlato, kryštály) // ========================================== images.push( 'images/diamond.png', 'images/gold.png', 'images/kov.png', 'images/stone.png' ); // ========================================== // 4. UI ELEMENTY // ========================================== images.push( 'images/spravne.png', 'images/nespravne.png', 'images/star_active.png', 'images/star_inactive.png', 'images/banik.ico' ); // ========================================== // 5. POZADIE SVETA (ak existuje) // ========================================== if (levelConfig && levelConfig.worldId) { const worldBackgrounds = { 'world_r': 'images/worlds/world_r.png', 'world_l': 'images/worlds/world_l.png', 'world_s': 'images/worlds/world_s.png', 'world_ch': 'images/worlds/world_ch.png', 'world_z': 'images/worlds/world_z.jpg', 'world_c': 'images/worlds/world_c.png', 'world_sh': 'images/worlds/world_sh.png', 'world_zh': 'images/worlds/world_zh.png', 'world_d': 'images/worlds/world_d.png', 'world_t': 'images/worlds/world_t.png', 'world_n': 'images/worlds/world_n.png', 'world_k': 'images/worlds/world_k.png', 'world_g': 'images/worlds/world_g.png' }; const worldBg = worldBackgrounds[levelConfig.worldId]; if (worldBg) { images.push(worldBg); } } // Základné pozadie images.push('images/pozadie.jpg'); // Menu obrázky images.push( 'images/menubutton.png', 'images/cursor.png', 'images/active_cursor4.png' ); console.log(`📦 Celkovo zozbieraných ${images.length} obrázkov`); return images; } /** * Načítanie jedného obrázka */ function preloadImage(imagePath) { return new Promise((resolve) => { // Ak už je načítaný, vráť ho if (preloadedImages[imagePath]) { updateProgress(); resolve(preloadedImages[imagePath]); return; } const img = new Image(); // Pri úspešnom načítaní img.onload = () => { preloadedImages[imagePath] = img; // Ulož do cache updateProgress(); console.log(`✅ ${imagePath}`); resolve(img); }; // Pri chybe (nechaj hru pokračovať) img.onerror = () => { console.warn(`⚠️ Chyba pri načítaní: ${imagePath}`); updateProgress(); resolve(null); }; // Spusti načítanie img.src = imagePath; }); } /** * Aktualizácia progress baru */ function updateProgress() { loadedResources++; // Vypočítaj percento const percentage = Math.round((loadedResources / totalResources) * 100); // Aktualizuj progress bar const progressFill = document.getElementById('loading-progress-fill'); if (progressFill) { progressFill.style.width = `${percentage}%`; } // Aktualizuj text percentá const progressPercentage = document.getElementById('loading-progress-percentage'); if (progressPercentage) { progressPercentage.textContent = `${percentage}%`; } // Aktualizuj počet načítaných const progressDetails = document.getElementById('loading-progress-details'); if (progressDetails) { progressDetails.textContent = `${loadedResources}/${totalResources} zdrojov`; } // Aktualizuj hlavnú správu const loadingMessage = document.getElementById('loading-message'); if (loadingMessage) { if (percentage < 100) { loadingMessage.textContent = 'Načítavam obrázky...'; } else { loadingMessage.textContent = 'Hotovo! Spúšťam hru...'; } } console.log(`📊 Progress: ${percentage}% (${loadedResources}/${totalResources})`); } /** * Skrytie loading screenu s animáciou */ function hideLoadingScreen() { const loadingScreen = document.getElementById('loading-screen'); if (loadingScreen) { loadingScreen.style.opacity = '0'; setTimeout(() => { loadingScreen.style.display = 'none'; }, 500); } console.log('👋 Loading screen skrytý, hra pripravená!'); } /** * Pomocná funkcia na získanie prednačítaného obrázka * Môžeš ju použiť neskôr v kóde ak chceš */ function getPreloadedImage(imagePath) { return preloadedImages[imagePath] || null; } // Export pre ostatné časti kódu if (typeof window !== 'undefined') { window.preloadedImages = preloadedImages; window.getPreloadedImage = getPreloadedImage; } /////////////////////////////////////////////// // ========== ZAKLADNE PREMENNE ============ // // Diamonds, kov, Golds, Kov, Zvukové efekty // // velkosti blokov, pocet ziskanych itemov // /////////////////////////////////////////////// ////////////////////////////////// // Získanie canvasu a kontextu // ////////////////////////////////// const canvas = document.getElementById('gameCanvas'); const ctx = canvas.getContext('2d'); const blockSize = 50; // Veľkosť jednej blokovej kocky const playerSize = blockSize; // Veľkosť hráča const diamondSize = blockSize; // Veľkosť diamantu const GoldSize = blockSize; // Veľkosť diamantu const claySize = blockSize; // Veľkosť hliny const kovSize = blockSize; // Veľkosť diamantu const mapWidth = 16; // Počet blokov na šírku const mapHeight = 10; // Počet blokov na výšku let playerX = blockSize; // Začiatočná pozícia hráča na osi X let playerY = blockSize; // Začiatočná pozícia hráča na osi Y const diamonds = []; const kov = []; /////////////////////// const golds = []; // Základné premenné // const clay = []; /////////////////////// let generatedPositions = []; // Globálny zoznam pozícií všetkých objektov let PocetGenDiamant = 3; let PocetGenKov = 1; let PocetGenGolds = 4; let diamondsDestroyed = 0; // Počet zničených diamantov let kovDestroyed = 0; // Počet zničených diamantov let goldsDestroyed = 0; // Počet zničených goldov let isDestroying = false; // Premenná určujúca, či hráč zničí blok let playerRotation = 0; // Úvodná rotácia hráča let diamondsCollected = 0; // Počet zozbieraných diamantov let kovCollected = 0; // Počet zozbieraných kovov let goldsCollected = 0; // Počet zozbieraných diamantov let spaceBarPressed = 0; // Počet stlačení medzerníka let playerBlockX; // Pozicia hraca X let playerBlockY; // Pozicia hraca Y let targetBlockX; let targetBlockY; let blockX; let blockY; let correctAnswers = 0; // Počet správnych odpovedi let incorrectAnswers = 0; // Počet nesprávnych odpovedi ///////////////////////////////////////// // Globálne premenné pre časový systém // ///////////////////////////////////////// let gameTimer = { startTime: null, // Čas spustenia hry currentTime: 0, // Aktuálny čas v sekundách intervalId: null, // ID intervalu pre aktualizáciu timeLimit: null, // Časový limit z levelConfig (v sekundách) isRunning: false, // Označuje či timer beží isPaused: false // Označuje či je timer pozastavený }; ////////////////////////////////////////////// // Globálne premenné pre Speech Recognition // ////////////////////////////////////////////// let speechRecognition = null; // Globálna inštancia - vytvorí sa len raz let isListening = false; // Flag či práve počúvame let currentExpectedWord = ''; // Aktuálne očakávané slovo let isExerciseActive = false; // Flag či práve prebieha cvičenie /////////////////////// // Obrázky postavy // /////////////////////// playerX = 100; playerY = 200; const goldImg = new Image(); goldImg.src = 'images/gold.png'; const diamondImg = new Image(); diamondImg.src = 'images/diamond.png'; const kovImg = new Image(); kovImg.src = 'images/kov.png'; const clayImg = new Image(); clayImg.src = 'images/stone.png'; const playerImg = new Image(); playerImg.src = 'images/hrac.png'; const playerImgVl = new Image(); playerImgVl.src = 'images/hrac-otoceny-vlavo.png'; const playerImgVp = new Image(); playerImgVp.src = 'images/hrac-otoceny-vpravo.png'; const playerImgchrbat = new Image(); playerImgchrbat.src = 'images/hrac.png'; const hracKopaVpravoImg = new Image(); hracKopaVpravoImg.src = 'images/hrac-kope-vpravo.png'; const hracKopaVlavoImg = new Image(); hracKopaVlavoImg.src = 'images/hrac-kope-vlavo.png'; let playerDirection = 'front'; let kope = false; //////////////////// // zvukové efekty // //////////////////// const EffectssoundFolder = `zvuky/effects`; let effectVyhra = new Howl({ src: [`zvuky/effects/vyhra.mp3`] }); let effectZle = new Howl({ src: [`zvuky/effects/zle.mp3`] }); let effectSpravne = new Howl({ src: [`zvuky/effects/spravne.mp3`] }); let effectkopanie = new Howl({ src: [`zvuky/effects/kopanie.wav`] }); let effectzlato = new Howl({ src: [`zvuky/effects/zlato.wav`] }); //////////////////////////////////////////////////////////// // ========== KONFIGURÁCIA LEVELU ========== // // Globálna premenná pre konfiguráciu aktuálneho levelu // // Obsahuje: words, diamonds, golds, crystals, timeLimit // // positions // //////////////////////////////////////////////////////////// let currentLevelConfig = null; let isCustomLevel = false; // Označuje či je spustený custom level /** * Inicializácia hry s konfiguráciou levelu * @param {Object} levelConfig - konfigurácia levelu z levels.js * @param {Boolean} customLevel - true ak je to custom level */ function initializeGameWithLevel(levelConfig, customLevel = false) { console.log('Inicializujem hru s levelConfig:', levelConfig); currentLevelConfig = levelConfig; isCustomLevel = customLevel; // Aktualizácia počtov objektov podľa levelConfig // OPRAVA: Použuj gameConfig namiesto priameho prístupu if (levelConfig.gameConfig && levelConfig.gameConfig.diamonds) { PocetGenDiamant = levelConfig.gameConfig.diamonds; } if (levelConfig.gameConfig && levelConfig.gameConfig.golds) { PocetGenGolds = levelConfig.gameConfig.golds; } if (levelConfig.gameConfig && levelConfig.gameConfig.crystals) { PocetGenKov = levelConfig.gameConfig.crystals; } console.log(`Aktualizované počty z gameConfig: Diamanty=${PocetGenDiamant}, Zlato=${PocetGenGolds}, Kryštály=${PocetGenKov}`); // Nastavenie pozície hráča ak je definovaná v levelConfig if (levelConfig.positions && levelConfig.positions.player) { playerX = levelConfig.positions.player.x * blockSize; playerY = levelConfig.positions.player.y * blockSize; console.log(`Pozícia hráča nastavená na: ${levelConfig.positions.player.x}, ${levelConfig.positions.player.y}`); } else { // Predvolená pozícia playerX = blockSize; playerY = blockSize; } console.log(`Nastavené počty: Diamanty=${PocetGenDiamant}, Zlato=${PocetGenGolds}, Kryštály=${PocetGenKov}`); console.log('Custom level:', isCustomLevel); // Inicializácia sledovania výkonu initializePerformanceTracking(); const timeLimit = levelConfig && levelConfig.timeLimit ? levelConfig.timeLimit : null; startTimer(timeLimit); resetGame(); } ////////////////////////////////////////////////// // ========== ČASOMIERA ========== // // spustenie časomiery, zastavenie časomiery // // pozastavenie a obnovenie časomiery // // aktualizacia časomiery atd // ////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////// // Spustenie časomery - volať pri štarte hry // // @param {number|null} timeLimit - Časový limit v sekundách (null = bez limitu) // ////////////////////////////////////////////////////////////////////////////////////// function startTimer(timeLimit = null) { console.log('Spúšťam časomeru...', timeLimit ? `Limit: ${timeLimit}s` : 'Bez limitu'); // Nastav časový limit z parametra gameTimer.timeLimit = timeLimit; gameTimer.startTime = Date.now(); gameTimer.currentTime = 0; gameTimer.isRunning = true; gameTimer.isPaused = false; // Aktualizuj UI ihneď updateTimerDisplay(); // Spusti pravidelné aktualizácie každú sekundu gameTimer.intervalId = setInterval(() => { if (!gameTimer.isPaused && gameTimer.isRunning) { // Vypočítaj aktuálny čas gameTimer.currentTime = Math.floor((Date.now() - gameTimer.startTime) / 1000); // Aktualizuj zobrazenie updateTimerDisplay(); // Kontrola časového limitu if (gameTimer.timeLimit && gameTimer.currentTime >= gameTimer.timeLimit) { console.log('Čas vypršal!'); handleTimeUp(); } } }, 1000); } //////////////////////////////////////// // Zastavenie časomery // //////////////////////////////////////// function stopTimer() { console.log('Zastavujem časomeru...'); gameTimer.isRunning = false; if (gameTimer.intervalId) { clearInterval(gameTimer.intervalId); gameTimer.intervalId = null; } } //////////////////////////////////////////////////////////////////////////////// // Pozastavenie/obnovenie časomery // // @param {boolean} pause - true = pozastav, false = pokračuj // //////////////////////////////////////////////////////////////////////////////// function pauseTimer(pause = true) { console.log(pause ? 'Pozastavujem časomeru...' : 'Obnovujem časomeru...'); gameTimer.isPaused = pause; if (!pause && gameTimer.isRunning) { // Pri obnovení prepočítaj štartovací čas gameTimer.startTime = Date.now() - (gameTimer.currentTime * 1000); } } ////////////////////////////////////////// // Aktualizácia zobrazenia času v HTML // ////////////////////////////////////////// function updateTimerDisplay() { const timeElement = document.getElementById('game-timer'); if (timeElement) { let displayTime; if (gameTimer.timeLimit) { // Ak je nastavený limit, zobrazuj zostávajúci čas const remainingTime = Math.max(0, gameTimer.timeLimit - gameTimer.currentTime); displayTime = formatTime(remainingTime); // Pridaj varovnú farbu keď zostáva málo času if (remainingTime <= 30) { timeElement.style.color = '#ff4444'; // Červená } else if (remainingTime <= 60) { timeElement.style.color = '#ffaa00'; // Oranžová } else { timeElement.style.color = ''; // Pôvodná farba } } else { // Bez limitu, zobrazuj uplynulý čas displayTime = formatTime(gameTimer.currentTime); timeElement.style.color = ''; // Pôvodná farba } timeElement.textContent = displayTime; } else { console.warn('Element #game-timer nebol nájdený v HTML'); } } ////////////////////////////////////////////////// // Formátovanie času do MM:SS formátu // // @param {number} seconds - Čas v sekundách // // @returns {string} - Formátovaný čas // ////////////////////////////////////////////////// function formatTime(seconds) { const minutes = Math.floor(seconds / 60); const remainingSeconds = seconds % 60; return `${minutes.toString().padStart(2, '0')}:${remainingSeconds.toString().padStart(2, '0')}`; } //////////////////////////////////////// // Obsluha vypršania času // //////////////////////////////////////// function handleTimeUp() { console.log('Čas vypršal! Ukončujem hru...'); stopTimer(); // Zastav hru gameRunning = false; // Zobraz konečný dialog s informáciou o vypršaní času setTimeout(() => { document.getElementById("endgame").style.display = "block"; document.getElementById("blur-background").style.display = "block"; document.body.style.overflow = "hidden"; // Pridaj informáciu o vypršaní času do konečného dialógu const endGameContent = document.querySelector('#endgame .execise-window'); if (endGameContent && !endGameContent.querySelector('.time-up-message')) { const timeUpMessage = document.createElement('div'); timeUpMessage.className = 'time-up-message'; timeUpMessage.innerHTML = '

⏰ Čas vypršal!

'; endGameContent.insertBefore(timeUpMessage, endGameContent.querySelector('nav')); } }, 100); } ////////////////////////////////////////////////// // Získanie aktuálneho času hry // // @returns {number} - Aktuálny čas v sekundách // ////////////////////////////////////////////////// function getCurrentGameTime() { return gameTimer.currentTime; } ////////////////////////////////////////////////////////////////////////////////////// // Získanie zostávajúceho času (ak je nastavený limit) // // @returns {number|null} - Zostávajúci čas v sekundách alebo null ak nie je limit // ////////////////////////////////////////////////////////////////////////////////////// function getRemainingTime() { if (gameTimer.timeLimit) { return Math.max(0, gameTimer.timeLimit - gameTimer.currentTime); } return null; } ////////////////////////////////////////////////// // ===== SPUSTENIE HRY S URL PARAMETRAMI ===== // // Inicializácia hry na základe URL parametrov // // Očakáva parametry: worldId, levelId // ////////////////////////////////////////////////// function initializeFromURL() { const urlParams = new URLSearchParams(window.location.search); // OPRAVENÉ: Podporuj oba formáty parametrov const worldId = urlParams.get('worldId') || urlParams.get('world'); const levelId = urlParams.get('levelId') || urlParams.get('level'); const isTraining = urlParams.get('training') === 'true'; const trainingConfig = urlParams.get('config'); console.log('URL parametre:', { worldId, levelId }); // Zvyšok funkcie zostáva rovnaký... if (worldId && levelId) { if (typeof getLevelConfig === 'function') { const levelConfig = getLevelConfig(levelId); if (levelConfig) { console.log('Načítaná konfigurácia levelu:', levelConfig); initializeGameWithLevel(levelConfig); return; } else { console.warn(`Level ${levelId} nebol nájdený`); } } else { console.warn('Funkcia getLevelConfig nie je dostupná - levels.js nebol načítaný'); } } if (isTraining && trainingConfig) { try { const config = JSON.parse(decodeURIComponent(trainingConfig)); console.log('Spúšťam tréningový level s konfiguráciou:', config); initializeGameWithLevel(config, true); // true = custom level return; } catch (error) { console.error('Chyba pri načítaní tréningovej konfigurácie:', error); } } // OPRAVENÝ FALLBACK - správne počty console.log('Spúšťam hru s predvolenými nastaveniami'); const fallbackLevelConfig = { words: ['rak', 'ryba', 'ruka', 'rosa'], diamonds: 2, golds: 3, crystals: 1, timeLimit: null, positions: { diamonds: [{ x: 3, y: 8 }, { x: 12, y: 7 }], // len 2 pozície golds: [{ x: 2, y: 9 }, { x: 7, y: 8 }, { x: 14, y: 6 }], // len 3 pozície crystals: [{ x: 9, y: 7 }], player: { x: 1, y: 1 } } }; console.log('Fallback timeLimit:', fallbackLevelConfig.timeLimit); initializeGameWithLevel(fallbackLevelConfig); } /////////////////////////////////////////////// // Spustenie inicializácie po načítaní DOM // /////////////////////////////////////////////// document.addEventListener('DOMContentLoaded', function() { // Čakaj kým sa načítajú všetky scripty, potom inicializuj setTimeout(initializeFromURL, 100); initializeNavigation(); }); ////////////////////////////////////////////////// // ====== VIRTUAL JOYSTICK FUNKCIONALITA ====== // // MECHANIKA POHYBU PRE MOBILY // ////////////////////////////////////////////////// /////////////////////////////////////////////// // Premenné pre virtual joystick // /////////////////////////////////////////////// let joystickActive = false; let joystickCenter = { x: 0, y: 0 }; let joystickKnob = null; let joystickBase = null; let joystickContainer = null; let joystickRadius = 45; // Polomer pohybu knobu let lastMoveTime = 0; const moveDelay = 150; // Delay medzi pohybmi v ms /////////////////////////////////////////////// // Inicializácia joysticku po načítaní DOM // /////////////////////////////////////////////// document.addEventListener('DOMContentLoaded', function() { initVirtualJoystick(); }); function initVirtualJoystick() { joystickContainer = document.querySelector('.virtual-joystick'); joystickKnob = document.querySelector('.joystick-knob'); joystickBase = document.querySelector('.joystick-base'); if (!joystickContainer || !joystickKnob) return; // Získanie centra joysticku const rect = joystickContainer.getBoundingClientRect(); joystickCenter.x = rect.width / 2; joystickCenter.y = rect.height / 2; // Event listenery pre touch joystickKnob.addEventListener('touchstart', handleTouchStart, { passive: false }); joystickKnob.addEventListener('touchmove', handleTouchMove, { passive: false }); joystickKnob.addEventListener('touchend', handleTouchEnd, { passive: false }); // Event listenery pre mouse (testovanie na desktop) joystickKnob.addEventListener('mousedown', handleMouseStart); document.addEventListener('mousemove', handleMouseMove); document.addEventListener('mouseup', handleMouseEnd); } /////////////////////////////////////////////// // Touch start // /////////////////////////////////////////////// function handleTouchStart(e) { e.preventDefault(); joystickActive = true; joystickContainer.classList.add('active'); } /////////////////////////////////////////////// // Touch move // /////////////////////////////////////////////// function handleTouchMove(e) { e.preventDefault(); if (!joystickActive) return; const touch = e.touches[0]; const rect = joystickContainer.getBoundingClientRect(); const x = touch.clientX - rect.left - joystickCenter.x; const y = touch.clientY - rect.top - joystickCenter.y; updateJoystickPosition(x, y); } /////////////////////////////////////////////// // Touch end // /////////////////////////////////////////////// function handleTouchEnd(e) { e.preventDefault(); resetJoystick(); } /////////////////////////////////////////////// // Mouse start (pre testovanie) // /////////////////////////////////////////////// function handleMouseStart(e) { e.preventDefault(); joystickActive = true; joystickContainer.classList.add('active'); } /////////////////////////////////////////////// // Mouse move // /////////////////////////////////////////////// function handleMouseMove(e) { if (!joystickActive) return; const rect = joystickContainer.getBoundingClientRect(); const x = e.clientX - rect.left - joystickCenter.x; const y = e.clientY - rect.top - joystickCenter.y; updateJoystickPosition(x, y); } /////////////////////////////////////////////// // Mouse end // /////////////////////////////////////////////// function handleMouseEnd(e) { resetJoystick(); } /////////////////////////////////////////////// // Aktualizácia pozície knobu a pohyb hráča // /////////////////////////////////////////////// function updateJoystickPosition(x, y) { // Obmedz pohyb v kruhu const distance = Math.sqrt(x * x + y * y); const maxDistance = joystickRadius; if (distance > maxDistance) { x = (x / distance) * maxDistance; y = (y / distance) * maxDistance; } // Aktualizuj pozíciu knobu joystickKnob.style.transform = `translate(calc(-50% + ${x}px), calc(-50% + ${y}px))`; // Pohyb hráča na základe pozície joysticku const currentTime = Date.now(); if (currentTime - lastMoveTime > moveDelay) { handleJoystickMovement(x, y); lastMoveTime = currentTime; } } /////////////////////////////////////////////// // Spracovanie pohybu hráča // /////////////////////////////////////////////// function handleJoystickMovement(x, y) { const threshold = 15; // Minimálna vzdialenosť pre aktiváciu pohybu const distance = Math.sqrt(x * x + y * y); if (distance < threshold) return; // Určenie smeru na základe uhla const angle = Math.atan2(y, x) * (180 / Math.PI); let direction = ''; if (angle >= -45 && angle <= 45) { direction = 'right'; } else if (angle > 45 && angle <= 135) { direction = 'down'; } else if (angle > 135 || angle <= -135) { direction = 'left'; } else if (angle > -135 && angle < -45) { direction = 'up'; } // Vykonaj pohyb movePlayer(direction); } /////////////////////////////////////////////// // Pohyb hráča (používa existujúcu logiku) // /////////////////////////////////////////////// function movePlayer(direction) { const newPlayerX = playerX; const newPlayerY = playerY; switch (direction) { case 'up': if (playerY - blockSize >= 0) { playerY -= blockSize; playerRotation = 0; playerDirection = 'front'; } break; case 'left': if (playerX - blockSize >= 0) { playerX -= blockSize; playerRotation = 270; playerDirection = 'vlavo'; } break; case 'down': if (playerY + blockSize < 800) { playerY += blockSize; playerRotation = 180; playerDirection = 'front'; } break; case 'right': if (playerX + blockSize < 800) { playerX += blockSize; playerRotation = 90; playerDirection = 'vpravo'; } break; } // Kontrola kolízií (použije existujúcu logiku) checkCollisions(newPlayerX, newPlayerY); } ///////////////////////////////////////////////////// // Kontrola kolízií (extrahované z pôvodného kódu) // ///////////////////////////////////////////////////// function checkCollisions(newPlayerX, newPlayerY) { // Kontrola kolízií s clay clay.forEach((clayBlock, clayIndex) => { const blockX = clayBlock.x; const blockY = clayBlock.y; if (playerX === blockX && playerY === blockY) { if (isDestroying) { clay.splice(clayIndex, 1); isDestroying = false; } else { playerX = newPlayerX; playerY = newPlayerY; } } }); // Kontrola kolízií s diamonds diamonds.forEach((diamond, diamondIndex) => { const blockX = diamond.x; const blockY = diamond.y; if (playerX === blockX && playerY === blockY && !diamond.destroyed) { if (isDestroying) { diamond.destroyed = true; isDestroying = false; } else { playerX = newPlayerX; playerY = newPlayerY; } } }); // Kontrola kolízií s kov kov.forEach((kov, kovIndex) => { const blockX = kov.x; const blockY = kov.y; if (playerX === blockX && playerY === blockY && !kov.destroyed) { if (isDestroying) { kov.destroyed = true; isDestroying = false; } else { playerX = newPlayerX; playerY = newPlayerY; } } }); // Kontrola kolízií s golds golds.forEach((gold, goldIndex) => { const blockX = gold.x; const blockY = gold.y; if (playerX === blockX && playerY === blockY && !gold.destroyed) { if (isDestroying) { gold.destroyed = true; isDestroying = false; } else { playerX = newPlayerX; playerY = newPlayerY; } } }); } /////////////////////////////////////////////// // Reset joysticku na stred // /////////////////////////////////////////////// function resetJoystick() { joystickActive = false; joystickContainer.classList.remove('active'); joystickKnob.style.transform = 'translate(-50%, -50%)'; } /////////////////////////////////////////////// // Akčné tlačidlo // /////////////////////////////////////////////// document.addEventListener('DOMContentLoaded', function() { const actionButton = document.querySelector('.action-button'); if (actionButton) { actionButton.addEventListener('touchstart', handleActionTouch, { passive: false }); actionButton.addEventListener('click', handleActionClick); } }); function handleActionTouch(e) { e.preventDefault(); destroyBlock(); animateDigging(); } function handleActionClick(e) { e.preventDefault(); destroyBlock(); animateDigging(); } /////////////////////////////////////////////// // ========== OVLADANIE PRE PC ============= // // POHYB - KLAVESNICA (PC) // /////////////////////////////////////////////// window.addEventListener('keydown', (e) => { if (isExerciseActive) { console.log('⚠️ Pohyb zablokovaný - cvičenie prebieha'); return; // Ignoruj všetky klávesy } const newPlayerX = playerX; const newPlayerY = playerY; switch (e.key) { case 'w': case 'ArrowUp': if (playerY - blockSize >= 0) { // Kontrola pohybu nahor playerY -= blockSize; playerRotation = 0; // Rotácia smeru hore playerDirection = 'front'; } break; case 'a': case 'ArrowLeft': if (playerX - blockSize >= 0) { playerX -= blockSize; playerRotation = 270; playerDirection = 'vlavo'; } break; case 's': case 'ArrowDown': if (playerY + blockSize < 800) { playerY += blockSize; playerRotation = 180; playerDirection = 'front'; } break; case 'd': case 'ArrowRight': if (playerX + blockSize < 800) { playerX += blockSize; playerRotation = 90; playerDirection = 'vpravo'; } break; } ////////////////////////////////////////////// // Kontrola kolízií // ////////////////////////////////////////////// clay.forEach((clayBlock, clayIndex) => { const blockX = clayBlock.x; const blockY = clayBlock.y; if (playerX === blockX && playerY === blockY) { if (isDestroying) { clay.splice(clayIndex, 1); isDestroying = false; } else { // Nastavenie hráča späť na pôvodné miesto, keď sa snaží prejsť cez blok playerX = newPlayerX; playerY = newPlayerY; } } }); diamonds.forEach((diamond, diamondIndex) => { const blockX = diamond.x; const blockY = diamond.y; if (playerX === blockX && playerY === blockY && !diamond.destroyed) { if (isDestroying) { diamond.destroyed = true; isDestroying = false; } else { playerX = newPlayerX; playerY = newPlayerY; } } }); kov.forEach((kov, kovIndex) => { const blockX = kov.x; const blockY = kov.y; if (playerX === blockX && playerY === blockY && !kov.destroyed) { if (isDestroying) { kov.destroyed = true; isDestroying = false; } else { playerX = newPlayerX; playerY = newPlayerY; } } }); golds.forEach((gold, goldIndex) => { const blockX = gold.x; const blockY = gold.y; if (playerX === blockX && playerY === blockY && !gold.destroyed) { if (isDestroying) { gold.destroyed = true; isDestroying = false; } else { playerX = newPlayerX; playerY = newPlayerY; } } }); }); document.addEventListener('keydown', (e) => { if (e.key === ' ') { destroyBlock(); animateDigging(); } }); /////////////////////////////////////////////// // Funkcia na ničenie itemov // /////////////////////////////////////////////// function destroyBlock() { if (isExerciseActive) { console.log('⚠️ Kopanie zablokované - cvičenie prebieha'); return; // Ignoruj akciu kopania } playerBlockX = Math.floor(playerX / blockSize); playerBlockY = Math.floor(playerY / blockSize); targetBlockX = playerBlockX; targetBlockY = playerBlockY; // Zistí smer hráča a určí cieľový blok podľa toho switch (playerRotation) { case 0: targetBlockY--; break; case 90: targetBlockX++; break; case 180: targetBlockY++; break; case 270: targetBlockX--; break; } clay.forEach((clayBlock, clayIndex) => { const blockX = clayBlock.x / blockSize; const blockY = clayBlock.y / blockSize; // Kontrola zničenia bloku, ktorý je pred hráčom v smeru, ktorým je hráč otáčaný if (blockX === targetBlockX && blockY === targetBlockY) { clay.splice(clayIndex, 1); } }); diamonds.forEach((diamond, diamondIndex) => { blockX = diamond.x / blockSize; blockY = diamond.y / blockSize; if (blockX === targetBlockX && blockY === targetBlockY && !diamond.destroyed) { spaceBarPressed++; if (spaceBarPressed === 3) { openCvicenie(); } } }); golds.forEach((gold, goldIndex) => { const blockX = gold.x / blockSize; const blockY = gold.y / blockSize; if (blockX === targetBlockX && blockY === targetBlockY && !gold.destroyed) { spaceBarPressed++; if (spaceBarPressed === 2) { gold.destroyed = true; goldsCollected++; effectzlato.play(); updateGoldCount(); updategoldsCollected(goldsCollected); recordGoldCollected(); checkWinConditionWithRating(); spaceBarPressed = 0; } } }); kov.forEach((kov, kovIndex) => { const blockX = kov.x / blockSize; const blockY = kov.y / blockSize; if (blockX === targetBlockX && blockY === targetBlockY && !kov.destroyed) { spaceBarPressed++; if (spaceBarPressed === 4) { showInfoDialog(); } } }); } /////////////////////////////////////////////// // Animácia kopania // /////////////////////////////////////////////// function animateDigging() { if (isExerciseActive) { console.log('⚠️ Pohyb zablokovaný - cvičenie prebieha'); return; // Ignoruj všetky klávesy } kope = true; drawPlayer(); effectkopanie.play(); setTimeout(() => { kope = false; drawPlayer(); }, 200); // Čas, po ktorom sa obrázok vráti späť (200 milisekúnd) } ////////////////////////////////////////////////// // ======= GENEROVANIE SVETA A ITEMOV ======= // // Inicializácia sveta, generovanie a kreslenie // // itemov a hrača // ////////////////////////////////////////////////// ////////////////////////////////////////////////// // Generovanie diamantov // // Podporuje presné pozície z levelConfig // // alebo náhodné generovanie // ////////////////////////////////////////////////// function generateDiamonds() { // Ak máme presné pozície v levelConfig a nie je to custom level if (currentLevelConfig && currentLevelConfig.positions && currentLevelConfig.positions.diamonds && !isCustomLevel) { console.log('Generujem diamanty na presných pozíciach z levelConfig'); currentLevelConfig.positions.diamonds.forEach(pos => { const newPosition = { x: pos.x * blockSize, y: pos.y * blockSize }; diamonds.push(newPosition); generatedPositions.push(newPosition); console.log(`Diamant na pozícii: ${pos.x}, ${pos.y}`); }); } else { // Náhodné generovanie (pre custom levely alebo ak nie sú definované pozície) console.log('Generujem diamanty náhodne'); while (diamonds.length != PocetGenDiamant) { const diamondX = Math.floor(Math.random() * mapWidth) * blockSize; const diamondY = (Math.floor(Math.random() * mapHeight) + 6) * blockSize; const newPosition = { x: diamondX, y: diamondY }; const positionExists = generatedPositions.some(pos => pos.x === diamondX && pos.y === diamondY); if (!positionExists) { diamonds.push(newPosition); generatedPositions.push(newPosition); } } } initializeDiamonds(PocetGenDiamant); } ////////////////////////////////////////////////// // Generovanie Kovov // // Podporuje presné pozície z levelConfig // // alebo náhodné generovanie // ////////////////////////////////////////////////// function generateKov() { if (currentLevelConfig && currentLevelConfig.positions && currentLevelConfig.positions.crystals && !isCustomLevel) { console.log('Generujem kryštály na presných pozíciach z levelConfig'); currentLevelConfig.positions.crystals.forEach(pos => { const newPosition = { x: pos.x * blockSize, y: pos.y * blockSize }; kov.push(newPosition); generatedPositions.push(newPosition); console.log(`Kryštál na pozícii: ${pos.x}, ${pos.y}`); }); } else { console.log('Generujem kryštály náhodne'); while (kov.length != PocetGenKov) { const kovX = Math.floor(Math.random() * mapWidth) * blockSize; const kovY = (Math.floor(Math.random() * mapHeight) + 6) * blockSize; const newPosition = { x: kovX, y: kovY }; const positionExists = generatedPositions.some(pos => pos.x === kovX && pos.y === kovY); if (!positionExists) { kov.push(newPosition); generatedPositions.push(newPosition); } } } initializeKov(PocetGenKov); } ////////////////////////////////////////////////// // Generovanie Goldov // // Podporuje presné pozície z levelConfig // // alebo náhodné generovanie // ////////////////////////////////////////////////// function generateGolds() { if (currentLevelConfig && currentLevelConfig.positions && currentLevelConfig.positions.golds && !isCustomLevel) { console.log('Generujem goldy na presných pozíciách z levelConfig'); currentLevelConfig.positions.golds.forEach(pos => { const newPosition = { x: pos.x * blockSize, y: pos.y * blockSize }; golds.push(newPosition); generatedPositions.push(newPosition); console.log(`Gold na pozícii: ${pos.x}, ${pos.y}`); }); } else { console.log('Generujem goldy náhodne'); while (golds.length !== PocetGenGolds) { const goldX = Math.floor(Math.random() * mapWidth) * blockSize; const goldY = (Math.floor(Math.random() * mapHeight) + 6) * blockSize; const newPosition = { x: goldX, y: goldY }; const positionExists = generatedPositions.some(pos => pos.x === goldX && pos.y === goldY) || diamonds.some(diamond => diamond.x === goldX && diamond.y === goldY) || kov.some(kov => kov.x === goldX && kov.y === goldY); if (!positionExists) { golds.push(newPosition); generatedPositions.push(newPosition); } } } initializeGolds(PocetGenGolds); } ////////////////////////////////////////////////// // Generovanie Clay // // Generuje hlinu všade okrem pozícií // // kde sú iné objekty // ////////////////////////////////////////////////// function generateClay() { for (let y = 0; y < mapHeight; y++) { for (let x = 0; x < mapWidth; x++) { let isPositionEmpty = true; diamonds.forEach(diamond => { if (diamond.x === x * blockSize && diamond.y === (y + 6) * blockSize) { isPositionEmpty = false; } }); kov.forEach(kov => { if (kov.x === x * blockSize && kov.y === (y + 6) * blockSize) { isPositionEmpty = false; } }); golds.forEach(gold => { if (gold.x === x * blockSize && gold.y === (y + 6) * blockSize) { isPositionEmpty = false; } }); if (isPositionEmpty) { clay.push({ x: x * blockSize, y: (y + 6) * blockSize }); } } } } ////////////////////////////////////////////////// // Funkcia na vykreslovanie postavy // ////////////////////////////////////////////////// function drawPlayer() { let image; if (playerDirection == 'front' ){ image = playerImg; if(kope == true){ image = hracKopaVpravoImg; } }else if (playerDirection == 'vpravo' ){ image = playerImgVp; if(kope == true){ image = hracKopaVpravoImg; } } else if (playerDirection == 'vlavo' ){ image = playerImgVl; if(kope == true){ image = hracKopaVlavoImg; } }else{ image = playerImg; } ctx.drawImage(image, playerX, playerY, playerSize, playerSize); if(kope == true){ image = hracKopaVpravoImg; } } ////////////////////////////////////////////////// // Funkcia na vykreslovanie diamantov // ////////////////////////////////////////////////// function drawDiamonds() { diamonds.forEach(diamond => { if (!diamond.destroyed) { ctx.drawImage(diamondImg, diamond.x, diamond.y, diamondSize, diamondSize); } }); } ////////////////////////////////////////////////// // Funkcia na vykreslovanie kovu // ////////////////////////////////////////////////// function drawKov() { kov.forEach(kov => { if (!kov.destroyed) { ctx.drawImage(kovImg, kov.x, kov.y, kovSize, kovSize); } }); } ////////////////////////////////////////////////// // Funkcia na vykreslovanie goldov // ////////////////////////////////////////////////// function drawGolds() { golds.forEach(gold => { if (!gold.destroyed) { ctx.drawImage(goldImg, gold.x, gold.y, GoldSize, GoldSize); } }); } ////////////////////////////////////////////////// // Funkcia na vykreslovanie hliny // ////////////////////////////////////////////////// function drawClay() { ctx.lineWidth = 2; clay.forEach(clayObj => { ctx.drawImage(clayImg, clayObj.x, clayObj.y, claySize, claySize); }); } ////////////////////////////////////////////////// // ========================================== // // ====== SYSTÉM HVIEZD A HODNOTENIA ======= // // ========================================== // ////////////////////////////////////////////////// ///////////////////////////////////////////// // Globálne premenné pre sledovanie výkonu // ///////////////////////////////////////////// let gamePerformance = { speechExercises: { totalExercises: 0, // Celkový počet rečových cvičení v leveli (diamantov) completedExercises: 0, // Dokončené rečové cvičenia completedRounds: 0, // Počet absolvovaných kôl (slov) naprieč všetkými cvičeniami roundResults: [], // Výsledky každého kola [{attempts: 1, points: 3, success: true}, ...] currentExerciseRounds: 0, // Počet kôl v aktuálnom cvičení (z levelConfig) totalPoints: 0 // Celkové body za rečové cvičenia }, listeningExercises: { totalExercises: 0, // Celkový počet posluchových cvičení (kryštálov) completedExercises: 0, // Dokončené posluchové cvičenia completedRounds: 0, // Počet absolvovaných kôl (párov) naprieč všetkými cvičeniami roundResults: [], // Výsledky každého kola [{attempts: 1, points: 3, success: true}, ...] currentExerciseRounds: 0, // Počet kôl v aktuálnom cvičení (z levelConfig) totalPoints: 0 // Celkové body za posluchové cvičenia }, golds: { total: 0, // Celkový počet goldov collected: 0 // Zozbierané goldy }, levelCompleted: false, // Či bol level dokončený finalStars: 0, // Finálny počet hviezd totalPoints: 0, // Celkové body maxPossiblePoints: 0 // Maximálne možné body (vypočítané dynamicky) }; ////////////////////////////////////////////////////////// // Inicializácia sledovania výkonu na začiatku levelu // ////////////////////////////////////////////////////////// function initializePerformanceTracking() { console.log('Inicializujem sledovanie výkonu...'); // Resetuj gamePerformance gamePerformance = { speechExercises: { totalExercises: PocetGenDiamant, completedExercises: 0, completedRounds: 0, // Nová vlastnosť - absolvované kolá roundResults: [], // Nová vlastnosť - výsledky jednotlivých kôl currentExerciseRounds: 0, // Nová vlastnosť - počet kôl v aktuálnom cvičení totalPoints: 0 }, listeningExercises: { totalExercises: PocetGenKov, completedExercises: 0, completedRounds: 0, // Nová vlastnosť - absolvované kolá roundResults: [], // Nová vlastnosť - výsledky jednotlivých kôl currentExerciseRounds: 0, // Nová vlastnosť - počet kôl v aktuálnom cvičení totalPoints: 0 }, golds: { total: PocetGenGolds, collected: 0 }, levelCompleted: false, finalStars: 0, totalPoints: 0, maxPossiblePoints: 0 // Bude vypočítané dynamicky v calculateFinalRating() }; console.log(`Výkon inicializovaný - Max body sa vypočítajú dynamicky podľa absolvovaných kôl`); console.log(`Diamanty: ${PocetGenDiamant}, Kryštály: ${PocetGenKov}, Goldy: ${PocetGenGolds}`); } ////////////////////////////////////////////////////////// /** // * Zaznamenanie výsledku rečového cvičenia // * NOVÝ SYSTÉM: Volá sa po KAŽDOM DOKONČENOM SLOVE // * @param {number} attempts - Počet pokusov (1-3, alebo 0 pri neúspechu) // * @param {boolean} success - Či bolo kolo úspešné // */ // ////////////////////////////////////////////////////////// function recordSpeechExerciseResult(attempts, success) { console.log(`📝 Zaznamenávam rečové kolo: ${attempts} pokusov, úspech: ${success}`); // Výpočet bodov podľa počtu pokusov let points = 0; if (success) { switch(attempts) { case 1: points = 3; break; // 1 pokus = 3 body case 2: points = 2; break; // 2 pokusy = 2 body case 3: points = 1; break; // 3 pokusy = 1 bod default: points = 0; break; // Inak 0 bodov } gamePerformance.speechExercises.completedRounds++; } // Ulož výsledok kola do poľa gamePerformance.speechExercises.roundResults.push({ attempts: attempts, points: points, success: success }); // Pripočítaj body gamePerformance.speechExercises.totalPoints += points; const totalRounds = gamePerformance.speechExercises.roundResults.length; console.log(`✅ Rečové kolo dokončené: +${points} bodov (celkom: ${gamePerformance.speechExercises.totalPoints} z ${totalRounds * 3} možných)`); } ////////////////////////////////////////////////////////////////////// /** // * Zaznamenanie výsledku posluchového cvičenia // * NOVÝ SYSTÉM: Volá sa po KAŽDOM DOKONČENOM PÁRE (KOLE) // * @param {number} attempts - Počet pokusov (1-3, alebo 0 pri neúspechu) // * @param {boolean} success - Či bolo kolo úspešné // */ // ////////////////////////////////////////////////////////////////////// function recordListeningExerciseResult(attempts, success) { console.log(`📝 Zaznamenávam posluchové kolo: ${attempts} pokusov, úspech: ${success}`); // Výpočet bodov podľa počtu pokusov let points = 0; if (success) { switch(attempts) { case 1: points = 3; break; // 1 pokus = 3 body case 2: points = 2; break; // 2 pokusy = 2 body case 3: points = 1; break; // 3 pokusy = 1 bod default: points = 0; break; // Inak 0 bodov } gamePerformance.listeningExercises.completedRounds++; } // Ulož výsledok kola do poľa gamePerformance.listeningExercises.roundResults.push({ attempts: attempts, points: points, success: success }); // Pripočítaj body gamePerformance.listeningExercises.totalPoints += points; const totalRounds = gamePerformance.listeningExercises.roundResults.length; console.log(`✅ Posluchové kolo dokončené: +${points} bodov (celkom: ${gamePerformance.listeningExercises.totalPoints} z ${totalRounds * 3} možných)`); } ////////////////////////////////////// // Zaznamenanie zozbierania goldu // ////////////////////////////////////// function recordGoldCollected() { gamePerformance.golds.collected++; } ////////////////////////////////////////////////////////////////////////////// /** // * Výpočet finálneho hodnotenia // * @returns {Object} Objekt s hviezdami a detailnými štatistikami // */ // ////////////////////////////////////////////////////////////////////////////// function calculateFinalRating() { console.log('🧮 Vypočítavam finálne hodnotenie...'); // === DYNAMICKÝ PREPOČET MAX BODOV === // Max body = počet absolvovaných kôl × 3 (každé kolo má max 3 body) const speechRoundsCompleted = gamePerformance.speechExercises.roundResults.length; const listeningRoundsCompleted = gamePerformance.listeningExercises.roundResults.length; // Každé absolvované kolo (či úspešné alebo nie) sa započítava do max bodov gamePerformance.maxPossiblePoints = (speechRoundsCompleted * 3) + // Rečové kolá: max 3 body/kolo (listeningRoundsCompleted * 3); // Posluchové kolá: max 3 body/kolo console.log(`📊 Rečové kolá: ${speechRoundsCompleted} × 3 = ${speechRoundsCompleted * 3} bodov`); console.log(`📊 Posluchové kolá: ${listeningRoundsCompleted} × 3 = ${listeningRoundsCompleted * 3} bodov`); console.log(`📊 Max možné body: ${gamePerformance.maxPossiblePoints}`); // Celkové body const totalPoints = gamePerformance.speechExercises.totalPoints + gamePerformance.listeningExercises.totalPoints; gamePerformance.totalPoints = totalPoints; // Percentuálny výkon const percentage = gamePerformance.maxPossiblePoints > 0 ? (totalPoints / gamePerformance.maxPossiblePoints) * 100 : 0; // Výpočet hviezd podľa percentuálneho výkonu let stars = 0; if (percentage >= 70) { stars = 3; } else if (percentage >= 40) { stars = 2; } else if (percentage >= 20) { stars = 1; } else { stars = 0; } gamePerformance.finalStars = stars; const result = { stars: stars, totalPoints: totalPoints, maxPossiblePoints: gamePerformance.maxPossiblePoints, percentage: Math.round(percentage), speechPoints: gamePerformance.speechExercises.totalPoints, listeningPoints: gamePerformance.listeningExercises.totalPoints, speechSuccess: gamePerformance.speechExercises.completedExercises, speechTotal: gamePerformance.speechExercises.totalExercises, listeningSuccess: gamePerformance.listeningExercises.completedExercises, listeningTotal: gamePerformance.listeningExercises.totalExercises, goldsCollected: gamePerformance.golds.collected, goldsTotal: gamePerformance.golds.total, gameTime: getCurrentGameTime() }; console.log('Finálne hodnotenie:', result); return result; } ////////////////////////////////////////////////////////// /** // * Uloženie výsledkov do progress managera // * @param {Object} rating - Výsledky hodnotenia // */ // ////////////////////////////////////////////////////////// function saveResultsToProgress(rating) { console.log('💾 Ukladám výsledky do progress managera...', rating); // Získaj informácie o aktuálnom leveli const urlParams = new URLSearchParams(window.location.search); const worldId = urlParams.get('worldId') || urlParams.get('world'); const levelId = urlParams.get('levelId') || urlParams.get('level'); const isTraining = urlParams.get('training') === 'true'; // Pre tréningové levely neukladaj do progress managera if (isTraining) { console.log('🎯 Tréningový level - neukladám do progress managera'); return { saved: false, reason: 'training_level', unlocked: { levels: [], worlds: [] } }; } if (!worldId || !levelId) { console.warn('⚠️ Chýbajú URL parametre pre uloženie pokroku'); return { saved: false, reason: 'missing_params', unlocked: { levels: [], worlds: [] } }; } if (!window.progressManager) { console.warn('⚠️ ProgressManager nie je dostupný'); return { saved: false, reason: 'no_progress_manager', unlocked: { levels: [], worlds: [] } }; } try { // Príprava rozšírených dát pre progress manager const levelData = { // Základné údaje stars: rating.stars, completed: true, time: rating.gameTime, // Detailné štatistiky points: rating.totalPoints, maxPoints: rating.maxPossiblePoints, percentage: rating.percentage, // Rozdelenie bodov speechExercises: { completed: rating.speechSuccess, total: rating.speechTotal, points: rating.speechPoints, roundResults: gamePerformance.speechExercises.roundResults // NOVÁ ŠTRUKTÚRA: Výsledky každého kola }, listeningExercises: { completed: rating.listeningSuccess, total: rating.listeningTotal, points: rating.listeningPoints, roundResults: gamePerformance.listeningExercises.roundResults // NOVÁ ŠTRUKTÚRA: Výsledky každého kola }, golds: { collected: rating.goldsCollected, total: rating.goldsTotal }, // Metadáta timestamp: new Date().toISOString(), gameVersion: '2.0', levelType: 'banik' }; console.log('📊 Ukladané dáta:', levelData); // Ulož do progress managera - VOLÁ VYLEPŠENÚ FUNKCIU const success = window.progressManager.updateLevelProgress(worldId, levelId, levelData); if (success) { console.log('✅ Výsledky úspešne uložené do progress managera'); // Aktualizuj celkové štatistiky hráča const playerStats = { wordsSpoken: rating.speechTotal, correctPronunciations: rating.speechSuccess, incorrectPronunciations: rating.speechTotal - rating.speechSuccess, gamesPlayed: 1, gameType: 'banik', totalPlayTime: rating.gameTime }; if (typeof window.progressManager.updateProgressStatistics === 'function') { window.progressManager.updateProgressStatistics(playerStats); } // Získaj informácie o odomknutom obsahu const worldProgress = window.progressManager.getWorldProgress(worldId); const detailedProgress = window.progressManager.getDetailedWorldProgress(worldId); console.log('🏆 Aktuálny pokrok sveta:', detailedProgress); // Vráť informácie o uložení a odomknutom obsahu return { saved: true, worldProgress: worldProgress, detailedProgress: detailedProgress, unlocked: { levels: [], // Vyplní sa automaticky v progress manageri worlds: [] // Vyplní sa automaticky v progress manageri } }; } else { console.error('❌ Chyba pri ukladaní do progress managera'); return { saved: false, reason: 'save_failed', unlocked: { levels: [], worlds: [] } }; } } catch (error) { console.error('💥 Chyba pri ukladaní výsledkov:', error); return { saved: false, reason: 'exception', error: error.message, unlocked: { levels: [], worlds: [] } }; } } ////////////////////////////////////////////////////////// /// Aktualizácia zobrazenia výkonu v UI (voliteľné) // ////////////////////////////////////////////////////////// function updateAnswerCounters() { const correctElement = document.getElementById('correct-words'); const incorrectElement = document.getElementById('incorrect-words'); if (correctElement) { correctElement.innerHTML = `${correctAnswers} `; } if (incorrectElement) { incorrectElement.innerHTML = `${incorrectAnswers} `; } console.log(`Počítadlá aktualizované: ${correctAnswers} správnych, ${incorrectAnswers} nesprávnych`); } ///////////////////////////////////////////////////////////////////////////////////// /** // * Zobrazenie detailných výsledkov v win dialógu // * @param {Object} rating - Výsledky hodnotenia // * @param {Object} saveResult - Výsledok uloženia do progress managera // */ // ///////////////////////////////////////////////////////////////////////////////////// function displayResultsInWinDialog(rating, saveResult) { console.log('📋 Zobrazujem výsledky v existujúcom win dialógu...', rating); // Nájdi win dialóg const endGameDialog = document.querySelector('#endgame'); if (!endGameDialog) { console.warn('⚠️ Win dialóg nebol nájdený'); return; } // OPRAVA: Aktualizuj existujúce HTML elementy namiesto pridávania nových // 1. Aktualizuj čas const timeSpan = endGameDialog.querySelector('.stats div:first-child span'); if (timeSpan) { timeSpan.textContent = formatTime(rating.gameTime); console.log('✅ Čas aktualizovaný:', formatTime(rating.gameTime)); } // 2. Aktualizuj slová - nájdi správny div (druhý div v stats) const wordsDiv = endGameDialog.querySelector('.stats div:nth-child(2)'); if (wordsDiv) { // Vypočítaj nesprávne pokusy const speechIncorrect = rating.speechTotal - rating.speechSuccess; // Aktualizuj HTML obsah celého div-u wordsDiv.innerHTML = ` Slová: ${rating.speechSuccess} / ${speechIncorrect} `; console.log(`✅ Slová aktualizované: ${rating.speechSuccess} správne, ${speechIncorrect} nesprávne`); } // 3. Pridaj nové štatistiky pod existujúce (ak nie sú už tam) const statsDiv = endGameDialog.querySelector('.stats'); if (statsDiv && !statsDiv.querySelector('.additional-stats')) { // Vytvor div pre dodatočné štatistiky const additionalStatsDiv = document.createElement('div'); additionalStatsDiv.className = 'additional-stats'; additionalStatsDiv.innerHTML = `
Body: ${rating.totalPoints}/${rating.maxPossiblePoints}
Úspešnosť: ${rating.percentage}%
`; statsDiv.appendChild(additionalStatsDiv); console.log('✅ Dodatočné štatistiky pridané'); } // 4. Aktualizuj hviezdy v existujúcom modal-stars div const starsDiv = endGameDialog.querySelector('#modal-stars'); if (starsDiv) { starsDiv.innerHTML = generateStarsHTML(rating.stars); console.log(`✅ Hviezdy aktualizované: ${rating.stars}/3`); } console.log('🎉 Win dialóg úspešne aktualizovaný!'); } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** // * Generovanie HTML pre zobrazenie hviezd (kompatibilné s existujúcim dizajnom) // * @param {number} stars - Počet hviezd (0-3) // * @returns {string} HTML string // */ // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// function generateStarsHTML(stars) { let starsHTML = '
'; for (let i = 1; i <= 3; i++) { if (i <= stars) { // Aktívna hviezda starsHTML += `Hviezda`; } else { // Neaktívna hviezda starsHTML += `Hviezda`; } } starsHTML += '
'; return starsHTML; } ////////////////////////////////////////////////////////// // kontrola vitaznych podmienok - pridať hodnotenie // ////////////////////////////////////////////////////////// function checkWinConditionWithRating() { if (diamondsCollected === PocetGenDiamant && goldsCollected === PocetGenGolds && kovCollected === PocetGenKov) { gamePerformance.levelCompleted = true; // Zastav timer stopTimer(); // Vypočítaj hodnotenie const rating = calculateFinalRating(); const saveResult = saveResultsToProgress(rating); // Aktualizuj navigáciu updateDialogNavigation(); setTimeout(() => { effectVyhra.play(); document.getElementById("endgame").style.display = "block"; document.getElementById("blur-background").style.display = "block"; document.body.style.overflow = "hidden"; // Zobraz výsledky v dialógu displayResultsInWinDialog(rating, saveResult); }, 100); } } /////////////////////////////// // Reset hry - vynulovanie // /////////////////////////////// function resetGame() { stopTimer(); initializePerformanceTracking(); let hasWon = false; // Premenná na sledovanie, či hráč už vyhral playerX = blockSize; // Začiatočná pozícia hráča na osi X playerY = blockSize; // Začiatočná pozícia hráča na osi Y diamonds.length = 0; kov.length = 0; clay.length = 0; golds.length = 0; wordList = []; currentWordIndex = 0; diamondsDestroyed = 0; // Počet zničených diamantov kovsDestroyed = 0; // Počet zničených diamantov goldsDestroyed = 0; isDestroying = false; // Premenná určujúca, či hráč zničí blok playerRotation = 0; // Úvodná rotácia hráča diamondsCollected = 0; // Počet zozbieraných diamantov kovCollected = 0; // Počet zozbieraných diamantov goldsCollected = 0; generatedPositions = []; // Vyčisti pozície objektov correctAnswers = 0; incorrectAnswers = 0; updateAnswerCounters(); ctx.clearRect(0, 0, canvas.width, canvas.height); generateDiamonds(); generateKov(); generateGolds(); generateClay(); drawPlayer(); drawClay(); drawDiamonds(); drawKov(); drawGolds(); requestAnimationFrame(gameLoop); const timeLimit = currentLevelConfig && currentLevelConfig.timeLimit ? currentLevelConfig.timeLimit : null; startTimer(timeLimit); console.log('Hra spustená s časovým systémom'); } ////////////////////// // BOČNY PANEL // ////////////////////// function updateDiamondCount() { const diamondCountElement = document.getElementById('diamondCount'); if (diamondCountElement) { diamondCountElement.textContent = diamondsCollected; } } function updateKovCount() { const kovCountElement = document.getElementById('kovCount'); if (kovCountElement) { kovCountElement.textContent = kovCollected; } } function updateGoldCount() { const goldCountElement = document.getElementById('goldCount'); if (goldCountElement) { goldCountElement.textContent = goldsCollected; } } /////////////////////////////////////////////////// // Funkcia na inicializáciu zobrazenia itemu // /////////////////////////////////////////////////// function initializeDiamonds(count) { const diamondsContainer = document.querySelector('.diamonds-container'); // Vymažte všetky existujúce diamantové položky diamondsContainer.innerHTML = ''; // Vytvorte a pridajte diamantové položky na základe počtu diamantov for (let i = 0; i < count; i++) { const diamondItem = document.createElement('div'); diamondItem.classList.add('diamond-item'); const diamondImage = document.createElement('img'); diamondImage.src = 'images/diamond.png'; diamondImage.alt = 'Diamond'; diamondImage.classList.add('diamond-image'); const diamondOverlay = document.createElement('div'); diamondOverlay.classList.add('diamond-overlay'); diamondItem.appendChild(diamondImage); diamondItem.appendChild(diamondOverlay); diamondsContainer.appendChild(diamondItem); } } function initializeKov(count) { const kovContainer = document.querySelector('.kov-container'); kovContainer.innerHTML = ''; for (let i = 0; i < count; i++) { const kovItem = document.createElement('div'); kovItem.classList.add('kov-item'); const kovImage = document.createElement('img'); kovImage.src = 'images/kov.png'; kovImage.alt = 'Kov'; kovImage.classList.add('kov-image'); const kovOverlay = document.createElement('div'); kovOverlay.classList.add('kov-overlay'); kovItem.appendChild(kovImage); kovItem.appendChild(kovOverlay); kovContainer.appendChild(kovItem); } } function initializeGolds(count) { const goldsContainer = document.querySelector('.golds-container'); goldsContainer.innerHTML = ''; for (let i = 0; i < count; i++) { const goldItem = document.createElement('div'); goldItem.classList.add('gold-item'); const goldImage = document.createElement('img'); goldImage.src = 'images/gold.png'; goldImage.alt = 'Gold'; goldImage.classList.add('gold-image'); const goldOverlay = document.createElement('div'); goldOverlay.classList.add('gold-overlay'); goldItem.appendChild(goldImage); goldItem.appendChild(goldOverlay); goldsContainer.appendChild(goldItem); } } ///////////////////////////////////////////////////////////////////////////// // Funkcia na aktualizáciu zobrazenia itemov po získaní nového itemu // ///////////////////////////////////////////////////////////////////////////// function updateDiamondsCollected(count) { const diamonds = document.querySelectorAll('.diamond-item'); // Aktualizujte triedy pre všetky diamanty po získaní nového diamantu for (let i = 0; i < count; i++) { diamonds[i].classList.add('collected'); } } function updateKovCollected(count) { const kov = document.querySelectorAll('.kov-item'); for (let i = 0; i < count; i++) { kov[i].classList.add('collected'); } } function updategoldsCollected(count) { const golds = document.querySelectorAll('.gold-item'); for (let i = 0; i < count; i++) { golds[i].classList.add('collected'); } } ////////////////////////////////////////////////////////////////////// // ================================================================ // // ===== CVIČENIE NA ROZPOZNANIE PODOBNÝCH ZVUKOV ===== // // ================================================================ // ////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////// // ================================================================ // // ===== POSLUCHOVÉ MINICVIČENIE ===== // // ================================================================ // ////////////////////////////////////////////////////////////////////// ////////////////////////////////////////// // ====== POSLUCHOVÉ CVIČENIE ====== // // Premenné pre posluchové cvičenie // ////////////////////////////////////////// let listeningCurrentIndex = 0; // Aktuálny index cvičenia let listeningWordPairs = []; // Pár slov pre porovnanie let listeningAttempts = 0; // Počet pokusov v aktuálnom cvičení (DEPRECATED - už nepoužívame) let listeningCurrentAttempts = 0; // NOVÁ PREMENNÁ: Počet pokusov pre aktuálny pár (kolo) let listeningMaxAttempts = 3; // Maximálny počet pokusov na jedno cvičenie let listeningCorrectAnswers = 0; // Počet správnych odpovedí (DEPRECATED) let listeningIncorrectAnswers = 0; // Počet nesprávnych odpovedí (DEPRECATED) let listeningTotalExercises = 2; // Celkový počet cvičení (rovnaké ako rečové) let listeningCompletedExercises = 0; // Dokončené cvičenia // Zvukové objekty pre posluchové cvičenie let listeningSound1 = null; let listeningSound2 = null; ////////////////////////////////////////// // Funkcia na otvorenie posluchového // // cvičenia - podobne ako openCvicenie // ////////////////////////////////////////// function openListeningExercise() { console.log('Otváranie posluchového cvičenia...'); // Nastav flag - posluchové cvičenie je aktívne isExerciseActive = true; console.log('🔒 Herné akcie zablokované - posluchové cvičenie aktívne'); console.log('=== DEBUG openListeningExercise ==='); console.log('currentLevelConfig:', currentLevelConfig); // Nastavenie počtu cvičení z levelConfig - používame pocetcviceni ak existuje if (currentLevelConfig && currentLevelConfig.gameConfig && currentLevelConfig.gameConfig.listeningExercises) { listeningTotalExercises = currentLevelConfig.gameConfig.listeningExercises; console.log(`Počet posluchových cvičení nastavený z levelConfig: ${listeningTotalExercises}`); } else { listeningTotalExercises = 2; // predvolená hodnota console.log(`Používam predvolenú hodnotu posluchových cvičení: ${listeningTotalExercises}`); } // Ulož počet kôl pre aktuálne cvičenie do gamePerformance if (typeof gamePerformance !== 'undefined') { gamePerformance.listeningExercises.currentExerciseRounds = listeningTotalExercises; console.log(`✅ Posluchové cvičenie bude mať ${listeningTotalExercises} kôl (párov)`); } // Reset hodnôt pre nové cvičenie listeningCurrentIndex = 0; listeningAttempts = 0; listeningCorrectAnswers = 0; listeningIncorrectAnswers = 0; listeningCompletedExercises = 0; // Generovanie párov slov pre cvičenie generateListeningPairs(); // Zobrazenie dialogového okna const infoDialog = document.getElementById('zvuky'); if (infoDialog) { infoDialog.style.display = 'block'; } document.getElementById("blur-background").style.display = "block"; document.body.classList.add("cvicenie-open"); document.body.style.overflow = "hidden"; // Zobrazenie prvého cvičenia displayListeningExercise(); } ////////////////////////////////////////// // Generovanie párov slov pre cvičenie // // Používa pôvodný systém s priečinkami// ////////////////////////////////////////// function generateListeningPairs() { listeningWordPairs = []; // Generujeme páry pre každé cvičenie for (let i = 0; i < listeningTotalExercises; i++) { // Náhodný výber priečinka (1-15 ako v pôvodnom kóde) const randomFolder = Math.floor(Math.random() * 15) + 1; const soundFolder = `zvuky/${randomFolder}/`; // Získanie zoznamu zvukov (1.wav, 2.wav) const allSounds = ['1.wav', '2.wav']; // Náhodný výber dvoch indexov (môžu byť rovnaké alebo rôzne) const index1 = Math.floor(Math.random() * allSounds.length); const index2 = Math.floor(Math.random() * allSounds.length); // Vytvorenie páru listeningWordPairs.push({ folder: randomFolder, soundFolder: soundFolder, index1: index1, index2: index2, sound1Path: `${soundFolder}${allSounds[index1]}`, sound2Path: `${soundFolder}${allSounds[index2]}`, areSame: index1 === index2 // Sú rovnaké ak majú rovnaký index }); } console.log('Generované páry zvukov pre posluchové cvičenie:', listeningWordPairs); } ////////////////////////////////////////// // Zobrazenie aktuálneho cvičenia // // Podobne ako displayWord() // ////////////////////////////////////////// function displayListeningExercise() { // === RESET POKUSOV PRE NOVÝ PÁR === listeningCurrentAttempts = 0; console.log(`🔄 Začínam nový pár (kolo ${listeningCurrentIndex + 1}/${listeningWordPairs.length})`); if (listeningCurrentIndex >= listeningWordPairs.length) { // Všetky cvičenia dokončené closeListeningExercise(true); return; } const currentPair = listeningWordPairs[listeningCurrentIndex]; console.log(`Zobrazujem posluchové cvičenie ${listeningCurrentIndex + 1}/${listeningWordPairs.length}`); console.log('Aktuálny pár:', currentPair); // Aktualizácia obsahu dialógu const dialogContent = document.querySelector('.cvicenie-content-2'); if (dialogContent) { // Resetovanie pokusov pre nové cvičenie listeningAttempts = 0; dialogContent.innerHTML = `

POSLUCHOVÉ CVIČENIE

Kolo ${listeningCurrentIndex + 1} / ${listeningWordPairs.length}

Počúvaj pozorne a rozhodni, či sú slová rovnaké alebo rôzne

Pripravujem slová...
`; // Pripojenie event listenerov na tlačidlá setTimeout(() => { const sameBtn = document.getElementById('listening-same-btn'); const differentBtn = document.getElementById('listening-different-btn'); if (sameBtn) { sameBtn.addEventListener('click', () => handleListeningAnswer(true)); } if (differentBtn) { differentBtn.addEventListener('click', () => handleListeningAnswer(false)); } }, 100); } // Spustenie prehrávania zvukov setTimeout(() => { playListeningSounds(currentPair); }, 1000); } ////////////////////////////////////////// // Prehranie zvukov pre posluchové // // cvičenie - používa pôvodné cesty // ////////////////////////////////////////// function playListeningSounds(soundPair) { const statusElement = document.getElementById('listening-status'); const sameBtn = document.getElementById('listening-same-btn'); const differentBtn = document.getElementById('listening-different-btn'); // BLOKUJ tlačidlá počas prehrávania if (sameBtn) { sameBtn.disabled = true; sameBtn.classList.add('disabled-btn'); } if (differentBtn) { differentBtn.disabled = true; differentBtn.classList.add('disabled-btn'); } // Vytvorenie Howl objektov s cestami z páru listeningSound1 = new Howl({ src: [soundPair.sound1Path], onloaderror: () => { console.warn(`Zvuk z cesty "${soundPair.sound1Path}" sa nepodarilo načítať`); } }); listeningSound2 = new Howl({ src: [soundPair.sound2Path], onloaderror: () => { console.warn(`Zvuk z cesty "${soundPair.sound2Path}" sa nepodarilo načítať`); } }); // Prehratie prvého zvuku if (statusElement) { statusElement.textContent = "Prehráva sa prvý zvuk..."; } console.log(`Prehrávam prvý zvuk: ${soundPair.sound1Path}`); listeningSound1.play(); // Po skončení prvého zvuku prehrať druhý listeningSound1.on('end', () => { setTimeout(() => { if (statusElement) { statusElement.textContent = "Prehráva sa druhý zvuk..."; } console.log(`Prehrávam druhý zvuk: ${soundPair.sound2Path}`); listeningSound2.play(); // Po skončení druhého zvuku ODBLOKOVAŤ tlačidlá listeningSound2.on('end', () => { setTimeout(() => { if (statusElement) { statusElement.textContent = "Vyber svoju odpoveď:"; } // ODBLOKOVAŤ tlačidlá if (sameBtn) { sameBtn.disabled = false; sameBtn.classList.remove('disabled-btn'); } if (differentBtn) { differentBtn.disabled = false; differentBtn.classList.remove('disabled-btn'); } const buttonsContainer = document.getElementById('listening-buttons-container'); if (buttonsContainer) { buttonsContainer.style.display = 'flex'; } }, 500); }); }, 1000); // Pauza medzi zvukmi }); } ////////////////////////////////////////// // Spracovanie odpovede hráča // // Podobne ako checkSpeechResult() // ////////////////////////////////////////// function handleListeningAnswer(playerAnswerSame) { // Skrytie tlačidiel const buttonsContainer = document.getElementById('listening-buttons-container'); if (buttonsContainer) { buttonsContainer.style.display = 'none'; } // Zastavenie zvukov if (listeningSound1) listeningSound1.stop(); if (listeningSound2) listeningSound2.stop(); const currentPair = listeningWordPairs[listeningCurrentIndex]; const isCorrect = playerAnswerSame === currentPair.areSame; console.log(`Hráčova odpoveď: ${playerAnswerSame ? 'rovnaké' : 'rôzne'}`); console.log(`Správna odpoveď: ${currentPair.areSame ? 'rovnaké' : 'rôzne'}`); console.log(`Je správne: ${isCorrect}`); // Nájdi alebo vytvor vysledok element (rovnako ako pri rečovom cvičení) let vysledokElement = document.getElementById('listening-vysledok'); if (!vysledokElement) { // Vytvor vysledok element ak neexistuje const execiseWindow = document.querySelector('#zvuky .execise-window'); vysledokElement = document.createElement('div'); vysledokElement.id = 'listening-vysledok'; vysledokElement.className = 'vysledok'; execiseWindow.appendChild(vysledokElement); } if (isCorrect) { // === SPRÁVNA ODPOVEĎ === listeningCurrentAttempts++; // Zvýš počet pokusov pre tento pár correctAnswers++; updateAnswerCounters(); vysledokElement.innerHTML = `
Správne
Výborne!
`; vysledokElement.classList.add('show'); // Použiť existujúci zvukový efekt if (typeof effectSpravne !== 'undefined') { effectSpravne.play(); } setTimeout(() => { vysledokElement.innerHTML = ''; vysledokElement.classList.remove('show'); // === ZAZNAMENANIE VÝSLEDKU TOHTO KOLA === if (typeof recordListeningExerciseResult === 'function') { recordListeningExerciseResult(listeningCurrentAttempts, true); } console.log(`✅ Posluchové kolo ${listeningCurrentIndex + 1}/${listeningWordPairs.length} dokončené s ${listeningCurrentAttempts} pokusmi`); // Prejsť na ďalšie cvičenie listeningCurrentIndex++; displayListeningExercise(); }, 2000); } else { // === NESPRÁVNA ODPOVEĎ === listeningCurrentAttempts++; // Zvýš počet pokusov pre tento pár incorrectAnswers++; updateAnswerCounters(); console.log(`Skús to znova, pokusy pre tento pár: ${listeningCurrentAttempts}`); // Vypočítaj zostávajúce pokusy const remainingAttempts = listeningMaxAttempts - listeningCurrentAttempts; if (remainingAttempts > 0) { // Ešte má pokusy - zobraz správu so zostávajúcimi pokusmi const attemptMessage = `
Zostávajúce pokusy: ${remainingAttempts}
`; vysledokElement.innerHTML = `
Nesprávne ${attemptMessage}
`; vysledokElement.classList.add('show'); // Použiť existujúci zvukový efekt if (typeof effectZle !== 'undefined') { effectZle.play(); } // Po 2 sekundách skryť výsledok a umožniť nový pokus setTimeout(() => { vysledokElement.innerHTML = ''; vysledokElement.classList.remove('show'); // Znovu zobraziť tlačidlá a prehrať zvuky if (buttonsContainer) { buttonsContainer.style.display = 'flex'; } const statusElement = document.getElementById('listening-status'); if (statusElement) { statusElement.textContent = "Počúvaj znova..."; } playListeningSounds(currentPair); }, 2000); } else { // Vyčerpané pokusy - OKAMŽITE zatvor po vyhodnotení const attemptMessage = `
Posledný pokus vyčerpaný
`; vysledokElement.innerHTML = `
Nesprávne ${attemptMessage}
`; vysledokElement.classList.add('show'); // Použiť existujúci zvukový efekt if (typeof effectZle !== 'undefined') { effectZle.play(); } // OKAMŽITE zatvor po 2 sekundách (bez čakania na zmiznutie výsledku) setTimeout(() => { // === ZAZNAMENANIE NEÚSPEŠNÉHO KOLA === if (typeof recordListeningExerciseResult === 'function') { recordListeningExerciseResult(0, false); } console.log(`❌ Posluchové kolo ${listeningCurrentIndex + 1}/${listeningWordPairs.length} neúspešné`); closeListeningExercise(false); }, 2000); } } } ////////////////////////////////////////// // Zatvorenie posluchového cvičenia // // Podobne ako closeCvicenie() // ////////////////////////////////////////// function closeListeningExercise(success = false) { isExerciseActive = false; console.log('🔓 Herné akcie odblokované - posluchové cvičenie ukončené'); // Zastavenie všetkých zvukov if (listeningSound1) { listeningSound1.stop(); listeningSound1 = null; } if (listeningSound2) { listeningSound2.stop(); listeningSound2 = null; } // OKAMŽITE skryj modal okno const infoDialog = document.getElementById('zvuky'); if (infoDialog) { infoDialog.style.display = 'none'; } document.getElementById("blur-background").style.display = "none"; document.body.classList.remove("cvicenie-open"); document.body.style.overflow = "auto"; // Vyčisti vysledok element const vysledokElement = document.getElementById('listening-vysledok'); if (vysledokElement) { vysledokElement.innerHTML = ''; vysledokElement.classList.remove('show'); } // Zaznamenanie výsledku if (success) { console.log('Posluchové cvičenie úspešne dokončené!'); listeningCompletedExercises = listeningWordPairs.length; // Zaznamenávanie už prebehlo priebežne po každom páre // Táto časť už len označí kryštál ako vykopaný // Označenie kryštálu ako vykopaného if (typeof kov !== 'undefined' && Array.isArray(kov)) { kov.forEach((kristal, kristalIndex) => { const blockX = kristal.x / blockSize; const blockY = kristal.y / blockSize; if (blockX === targetBlockX && blockY === targetBlockY && !kristal.destroyed) { kristal.destroyed = true; kovCollected++; if (typeof effectzlato !== 'undefined') { effectzlato.play(); } if (typeof updateKovCount === 'function') { updateKovCount(); } if (typeof updateKovCollected === 'function') { updateKovCollected(kovCollected); } if (typeof checkWinConditionWithRating === 'function') { checkWinConditionWithRating(); } spaceBarPressed = 0; } }); } } else { console.log('Posluchové cvičenie neúspešné'); // Zaznamenávanie neúspešného kola už prebehlo pri vyčerpaní pokusov spaceBarPressed = 0; } } ////////////////////////////////////////// // NOVÁ FUNKCIA showInfoDialog() // // Nahradenie starej funkcie // ////////////////////////////////////////// function showInfoDialog() { console.log('Otváranie posluchového cvičenia...'); openListeningExercise(); } ////////////////////////////////////////////////////////////////////// // ================================================================ // // ===== REČOVÉ MINICVIČENIE ===== // // ================================================================ // ////////////////////////////////////////////////////////////////////// ////////////////////////////////////////// // ====== HLASOVÉ CVIČENIE ====== // // Premenné // ////////////////////////////////////////// const url = 'slova.txt'; let currentWordIndex = 0; let wordList = []; // Pole slov na vyslovenie let pocetcviceni = 2; let kontrolacvicenia = 0; let slovicka = 0; ////////////////////////////////////////// // Inicializácia Speech Recognition // // Zavolá sa len raz pri načítaní stránky// ////////////////////////////////////////// function initSpeechRecognition() { console.log('🎤 Inicializácia speech recognition...'); // Kontrola či prehliadač podporuje Speech Recognition if (!('webkitSpeechRecognition' in window || 'SpeechRecognition' in window)) { console.error('❌ Speech recognition nie je podporované v tomto prehliadači'); // Zobraz chybovú hlášku pre používateľa const isMobile = /Android|iPhone|iPad|iPod/i.test(navigator.userAgent); if (isMobile) { const isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent); if (isIOS) { showErrorMessage('Používaj Safari prehliadač na iPhone/iPad pre rozpoznávanie reči.'); } else { showErrorMessage('Používaj Chrome prehliadač na Android pre rozpoznávanie reči.'); } } else { showErrorMessage('Tvoj prehliadač nepodporuje rozpoznávanie reči. Použi Chrome, Edge alebo Safari.'); } return false; } // Použijem štandardný alebo prefixovaný variant const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition; // Vytvorím globálnu inštanciu - LEN RAZ speechRecognition = new SpeechRecognition(); // Nastavím parametre rozpoznávania speechRecognition.lang = 'sk-SK'; // Slovenský jazyk speechRecognition.continuous = false; // Rozpozná len jedno slovo speechRecognition.interimResults = false; // Len finálne výsledky speechRecognition.maxAlternatives = 1; // Len jedna alternatíva // Nastavím callback funkcie - LEN RAZ setupSpeechCallbacks(); console.log('✅ Speech recognition úspešne inicializované'); return true; } ////////////////////////////////////////// // Nastavenie callback funkcií // // Volá sa len raz z initSpeechRecognition// ////////////////////////////////////////// function setupSpeechCallbacks() { // Callback keď sa rozpoznávanie spustí speechRecognition.onstart = () => { console.log('🎤 Rozpoznávanie spustené'); isListening = true; updateMicrophoneButton(true); }; // Callback keď sa rozpoznávanie ukončí speechRecognition.onend = () => { console.log('🎤 Rozpoznávanie ukončené'); isListening = false; updateMicrophoneButton(false); }; // Callback keď príde výsledok speechRecognition.onresult = (event) => { handleSpeechResult(event); }; // Callback pri chybe speechRecognition.onerror = (event) => { handleSpeechError(event); }; } ////////////////////////////////////////// // Bezpečné spustenie rozpoznávania // // Zavolá sa pri kliknutí na mikrofón // ////////////////////////////////////////// function startSpeechRecognition(expectedWord) { console.log('🎙️ Pokus o spustenie rozpoznávania pre slovo:', expectedWord); // CHECK 1: Je recognition inicializované? if (!speechRecognition) { console.warn('⚠️ Speech recognition nie je inicializované, inicializujem...'); const initialized = initSpeechRecognition(); if (!initialized) { console.error('❌ Nepodarilo sa inicializovať speech recognition'); return; } } // CHECK 2: Už beží rozpoznávanie? if (isListening) { console.warn('⚠️ Rozpoznávanie už beží, ignorujem pokus o opätovné spustenie'); return; } // Uložím očakávané slovo currentExpectedWord = expectedWord; // CHECK 3: Try-catch pre bezpečné spustenie try { console.log('▶️ Spúšťam rozpoznávanie...'); speechRecognition.start(); // isListening sa nastaví automaticky v onstart callback } catch (error) { console.error('❌ Chyba pri spustení rozpoznávania:', error); // Špecifické handling pre rôzne typy chýb if (error.message && error.message.includes('already started')) { console.warn('⚠️ Recognition už beží, pokúsim sa ho reštartovať'); // Pokúsim sa ho zastaviť a reštartovať isListening = false; speechRecognition.stop(); // Počkám chvíľu a skúsim znova setTimeout(() => { console.log('🔄 Reštartujem rozpoznávanie...'); startSpeechRecognition(expectedWord); }, 200); } else { // Iná chyba - zobraz hlášku používateľovi showErrorMessage('Nepodarilo sa spustiť rozpoznávanie. Skús to znova.'); isListening = false; updateMicrophoneButton(false); } } } ////////////////////////////////////////// // Spracovanie výsledku rozpoznávania // // Volá sa automaticky z callback // ////////////////////////////////////////// function handleSpeechResult(event) { // Získam rozpoznaný text const transcript = event.results[0][0].transcript; console.log('🗣️ Rozpoznaný text:', transcript); console.log('🎯 Očakávané slovo:', currentExpectedWord); // Vyčistím text (malé písmená, bez interpunkcie) const cleanedTranscript = transcript.trim().toLowerCase().replace(/[.,\/#!$%\^&\*;:{}=\-_`~()]/g, ""); const currentWord = currentExpectedWord.toLowerCase(); console.log('🔍 Porovnanie:', cleanedTranscript, 'vs', currentWord); // Kontrola či sa zhodujú if (cleanedTranscript === currentWord) { // ✅ SPRÁVNE VYSLOVENÉ console.log('✅ Správne vyslovené slovo "' + currentExpectedWord + '"'); // Zobraz správny výsledok document.getElementById("vysledok").innerHTML = `
Správne
Výborne!
`; document.getElementById("vysledok").classList.add('show'); // Prehraj zvuk if (typeof effectSpravne !== 'undefined') { effectSpravne.play(); } // Aktualizuj počítadlá correctAnswers++; updateAnswerCounters(); currentWordIndex++; // Zaznamenaj výsledok const attempts = slovicka + 1; if (typeof recordSpeechExerciseResult === 'function') { recordSpeechExerciseResult(attempts, true); } slovicka = 0; console.log(`✅ Kolo ${currentWordIndex}/${wordList.length} dokončené s ${attempts} pokusmi`); // Po 2 sekundách zobraz ďalšie slovo alebo ukonči setTimeout(() => { document.getElementById("vysledok").innerHTML = ''; document.getElementById("vysledok").classList.remove('show'); if (currentWordIndex < wordList.length) { displayWord(); // Zobraz ďalšie slovo } else { kontrolacvicenia = 1; closeCvicenie(); // Ukonči cvičenie } }, 2000); } else { // ❌ NESPRÁVNE VYSLOVENÉ console.log('❌ Nesprávne vyslovené. Slovo "' + currentExpectedWord + '" nebolo správne rozpoznané'); slovicka++; incorrectAnswers++; updateAnswerCounters(); console.log('Počet pokusov:', slovicka); // Vypočítaj zostávajúce pokusy const remainingAttempts = 3 - slovicka; // Zobraz chybovú správu if (remainingAttempts > 0) { document.getElementById("vysledok").innerHTML = `
Nesprávne
SKÚSTE ZNOVA
Zostávajú ${remainingAttempts} pokusy
`; } else { document.getElementById("vysledok").innerHTML = `
Nesprávne
VYČERPANÉ POKUSY
Pokračujeme ďalej
`; } document.getElementById("vysledok").classList.add('show'); // Prehraj zvuk if (typeof effectZle !== 'undefined') { effectZle.play(); } // Kontrola maximalneho poctu pokusov if (slovicka >= 3) { console.log('❌ Vyčerpané pokusy pre slovo:', currentExpectedWord); // Zaznamenaj neúspešné kolo if (typeof recordSpeechExerciseResult === 'function') { recordSpeechExerciseResult(3, false); } slovicka = 0; currentWordIndex++; // Po 2 sekundách prejdi na ďalšie slovo alebo ukonči setTimeout(() => { document.getElementById("vysledok").innerHTML = ''; document.getElementById("vysledok").classList.remove('show'); if (currentWordIndex < wordList.length) { displayWord(); // Zobraz ďalšie slovo } else { kontrolacvicenia = 2; closeCvicenie(); // Ukonči cvičenie } }, 2000); } else { // Ešte sú pokusy - skry hlášku po chvíli setTimeout(() => { document.getElementById("vysledok").innerHTML = ''; document.getElementById("vysledok").classList.remove('show'); }, 2000); } } } ////////////////////////////////////////// // Spracovanie chýb rozpoznávania // // Volá sa automaticky z callback // ////////////////////////////////////////// function handleSpeechError(event) { console.error('❌ Chyba rozpoznávania:', event.error); // Reset stavu isListening = false; updateMicrophoneButton(false); // Detailné error handling podľa typu chyby let errorTitle = ''; let errorMessage = ''; let canRetry = true; switch(event.error) { case 'no-speech': errorTitle = 'Nepočul som ťa'; errorMessage = 'Skús to znova a hovor hlasnejšie blízko mikrofónu.'; canRetry = true; break; case 'audio-capture': errorTitle = 'Problém s mikrofónom'; errorMessage = 'Skontroluj, či je mikrofón správne pripojený a funguje.'; canRetry = true; break; case 'not-allowed': errorTitle = 'Prístup zamietnutý'; errorMessage = 'Potrebujem povolenie na používanie mikrofónu. Klikni na ikonu zámku v adresovom riadku a povol mikrofón.'; canRetry = false; showPermissionsHelp(); // Zobraz návod break; case 'network': errorTitle = 'Problém s internetom'; errorMessage = 'Skontroluj svoje internetové pripojenie a skús to znova.'; canRetry = true; break; case 'aborted': errorTitle = 'Rozpoznávanie zrušené'; errorMessage = ''; canRetry = true; break; case 'service-not-allowed': errorTitle = 'Služba nie je dostupná'; errorMessage = 'Rozpoznávanie reči nie je povolené pre túto stránku. Skontroluj nastavenia prehliadača.'; canRetry = false; break; default: errorTitle = 'Neznáma chyba'; errorMessage = 'Niečo sa pokazilo. Skús to znova.'; canRetry = true; } // Zobraz chybovú hlášku if (errorMessage) { showErrorMessage(errorTitle + ': ' + errorMessage); } // Ak je to dočasná chyba, používateľ môže skúsiť znova kliknutím na mikrofón // Ak je to permissions problém, musí najprv povoliť mikrofón } ////////////////////////////////////////// // Aktualizácia tlačidla mikrofónu // // Volá sa z callbacks // ////////////////////////////////////////// function updateMicrophoneButton(listening) { const button = document.getElementById('rozpoznanie'); const tlacidloDiv = document.querySelector('.tlacidlo'); if (!button || !tlacidloDiv) { console.warn('⚠️ Tlačidlo mikrofónu nenájdené v DOM'); return; } if (listening) { // Stav: Počúvanie button.disabled = true; tlacidloDiv.classList.add('recording'); button.innerHTML = 'POČÚVAM...'; console.log('🎙️ UI: Počúvam...'); } else { // Stav: Pripravený button.disabled = false; tlacidloDiv.classList.remove('recording'); button.innerHTML = 'HOVORIŤ'; console.log('🎙️ UI: Pripravený'); } } ////////////////////////////////////////// // Zobrazenie chybovej hlášky // // Helper funkcia pre user feedback // ////////////////////////////////////////// function showErrorMessage(message) { console.log('💬 Zobrazujem hlášku:', message); // Zobraz hlášku v existujúcom vysledok div const vysledokDiv = document.getElementById("vysledok"); if (vysledokDiv) { vysledokDiv.innerHTML = `
⚠️
${message}
`; vysledokDiv.classList.add('show'); // Skry po 5 sekundách setTimeout(() => { vysledokDiv.innerHTML = ''; vysledokDiv.classList.remove('show'); }, 5000); } else { // Fallback na alert ak vysledok div neexistuje alert(message); } } ////////////////////////////////////////// // Zobrazenie návodu na povolenie // // mikrofónu // ////////////////////////////////////////// function showPermissionsHelp() { // Detekcia prehliadača const isChrome = /chrome/i.test(navigator.userAgent); const isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent); const isEdge = /edg/i.test(navigator.userAgent); let helpText = ''; if (isChrome) { helpText = 'V Chrome: Klikni na ikonu zámku vedľa URL a povol mikrofón.'; } else if (isEdge) { helpText = 'V Edge: Klikni na ikonu zámku vedľa URL a povol mikrofón.'; } else if (isSafari) { helpText = 'V Safari: Choď do Safari → Nastavenia pre túto webstránku → Mikrofón → Povoliť'; } else { helpText = 'V nastaveniach prehliadača povol mikrofón pre túto stránku.'; } console.log('📖 Návod na povolenie:', helpText); // Zobraz rozšírenú hlášku s návodom showErrorMessage('PRÍSTUP K MIKROFÓNU ZAMIETNUTÝ\n\n' + helpText); } ////////////////////////////////////////// // Detekcia mobile zariadení // // Helper funkcie // ////////////////////////////////////////// function isMobileDevice() { return /Android|iPhone|iPad|iPod/i.test(navigator.userAgent); } function isIOS() { return /iPad|iPhone|iPod/.test(navigator.userAgent); } function isAndroid() { return /Android/.test(navigator.userAgent); } ////////////////////////////////////////// // Funkcia na otvorenie cvičenia // // Používa slová z levelConfig // ////////////////////////////////////////// function openCvicenie() { // DEBUG informácie console.log('=== DEBUG openCvicenie ==='); console.log('currentLevelConfig:', currentLevelConfig); console.log('isCustomLevel:', isCustomLevel); console.log('URL parameters:', window.location.search); // Nastavit počet cvičení z levelConfig if (currentLevelConfig && currentLevelConfig.gameConfig && currentLevelConfig.gameConfig.speechExercises) { pocetcviceni = currentLevelConfig.gameConfig.speechExercises; console.log(`Počet rečových cvičení nastavený z levelConfig: ${pocetcviceni}`); } else { pocetcviceni = 2; // default hodnota console.log(`Používam predvolenú hodnotu rečových cvičení: ${pocetcviceni}`); } // Ulož počet kôl pre aktuálne cvičenie do gamePerformance if (typeof gamePerformance !== 'undefined') { gamePerformance.speechExercises.currentExerciseRounds = pocetcviceni; console.log(`✅ Rečové cvičenie bude mať ${pocetcviceni} kôl (slov)`); } // Kontrola či máme dostupnú konfiguráciu levelu if (!currentLevelConfig || !currentLevelConfig.words || currentLevelConfig.words.length === 0) { console.error('Chyba: Nie sú dostupné slová pre cvičenie v levelConfig'); console.error('currentLevelConfig je:', currentLevelConfig); // OPRAVENÝ FALLBACK - náhodný výber slov console.log('Používam fallback slová pre testovanie...'); const allFallbackWords = ['rak', 'ryba', 'ruka', 'rosa', 'ruža', 'robot', 'raketa', 'ryžou']; // Náhodný výber pocetcviceni slov z fallback zoznamu const shuffled = allFallbackWords.sort(() => 0.5 - Math.random()); wordList = shuffled.slice(0, pocetcviceni); console.log('Fallback slová (náhodne vybrané):', wordList); console.log(`Finálny zoznam slov pre cvičenie (${pocetcviceni} slov):`, wordList); startExercise(); return; } console.log('Začínam cvičenie s slovami z levelConfig:', currentLevelConfig.words); // Výber náhodných slov z levelConfig namiesto zo súboru let vybraneSlova = []; // Zoznam vybratých slov const dostupneSlova = currentLevelConfig.words; // Slová z levelConfig // Vyber pocet cviceni počet náhodných slov while (wordList.length < pocetcviceni && vybraneSlova.length < dostupneSlova.length) { const nahodnyIndex = Math.floor(Math.random() * dostupneSlova.length); const slovo = dostupneSlova[nahodnyIndex].trim(); if (!vybraneSlova.includes(slovo)) { // kontrola či sa vybralo iné/rozdielne slovo wordList.push(slovo); vybraneSlova.push(slovo); console.log(`Pridané slovo do cvičenia: ${slovo}`); } } // Ak nemáme dostatok slov, pridáme všetky dostupné if (wordList.length === 0) { console.warn('Neboli vybrané žiadne slová, používam všetky dostupné'); wordList = dostupneSlova.slice(0, Math.min(pocetcviceni, dostupneSlova.length)); } console.log('Finálny zoznam slov pre cvičenie:', wordList); startExercise(); } ////////////////////////////////////////// // Spustenie Cvicenia // ////////////////////////////////////////// function startExercise() { isExerciseActive = true; console.log('🔒 Herné akcie zablokované - cvičenie aktívne'); document.getElementById("cvicenie").style.display = "block"; document.getElementById("blur-background").style.display = "block"; document.body.classList.add("cvicenie-open"); document.body.style.overflow = "hidden"; displayWord(); } ////////////////////////////////////////// // Funkcia na zobrazenie aktuálneho // // slova na vyslovenie // ////////////////////////////////////////// function displayWord() { document.getElementById("word-display").innerText = wordList[currentWordIndex].toUpperCase(); const imageName = wordList[currentWordIndex] + ".png"; document.getElementById("cvicenie-image").src = "images/slova/" + imageName; updateWordProgress(); } ////////////////////////////////////////// // Samotna funckia na rozpoznanie // ////////////////////////////////////////// function rozpoznanieS() { console.log('🎤 rozpoznanieS() zavolaná'); // Získaj aktuálne očakávané slovo const expectedWord = wordList[currentWordIndex]; console.log('📝 Aktuálne slovo:', expectedWord); // Zavolaj bezpečnú funkciu na spustenie rozpoznávania startSpeechRecognition(expectedWord); } ////////////////////////////////////////// // Funkcia na zatvorenie cvičenia // ////////////////////////////////////////// function closeCvicenie() { if (kontrolacvicenia === 1) { // Zaznamenávanie už prebieha priebežne po každom slove // Táto časť už len označí diamant ako zozbieraný diamonds.forEach((diamond, diamondIndex) => { blockX = diamond.x / blockSize; blockY = diamond.y / blockSize; if (blockX === targetBlockX && blockY === targetBlockY && !diamond.destroyed) { diamond.destroyed = true; diamondsCollected++; effectzlato.play(); updateDiamondCount(); updateDiamondsCollected(diamondsCollected); checkWinConditionWithRating(); spaceBarPressed = 0; } }) } else if (kontrolacvicenia === 2) { // Zaznamenávanie neúspešného kola už prebehlo pri vyčerpaní pokusov spaceBarPressed = 0; } slovicka = 0; kontrolacvicenia = 0; currentWordIndex = 0; wordList = []; document.getElementById("cvicenie").style.display = "none"; document.getElementById("blur-background").style.display = "none"; document.body.classList.remove("cvicenie-open"); document.body.style.overflow = "auto"; // Reset progress indikátora const progressContainer = document.getElementById('word-progress'); if (progressContainer) { progressContainer.remove(); } isExerciseActive = false; console.log('🔓 Herné akcie odblokované - cvičenie ukončené'); } ////////////////////////////////////////// // Aktualizácia progress // // indikátora pre slová // ////////////////////////////////////////// function updateWordProgress() { // Nájde alebo vytvori progress kontajner let progressContainer = document.getElementById('word-progress'); if (!progressContainer) { progressContainer = createWordProgressElement(); } const currentWord = currentWordIndex + 1; const totalWords = wordList.length; // Aktualizuje text const progressText = progressContainer.querySelector('.progress-text'); progressText.textContent = `Slovo ${currentWord} / ${totalWords}`; // Aktualizuje progress bar const progressBar = progressContainer.querySelector('.progress-fill'); const percentage = (currentWord / totalWords) * 100; progressBar.style.width = `${percentage}%`; console.log(`Progress aktualizovaný: ${currentWord}/${totalWords} (${percentage}%)`); } ////////////////////////////////////////// // Vytvorenie progress elementu // // ak neexistuje // ////////////////////////////////////////// function createWordProgressElement() { const cvicenieContent = document.querySelector('.cvicenie-content'); const progressHTML = `
Slovo 1 / 2
`; // Pridaj progress na začiatok cvicenie-content cvicenieContent.insertAdjacentHTML('afterbegin', progressHTML); return document.getElementById('word-progress'); } const rozpoznanie = document.getElementById('rozpoznanie'); rozpoznanie.addEventListener('click', rozpoznanieS); //////////////////////////////////////////////// // ========================================== // // DYNAMICKÁ NAVIGÁCIA PRE KONEČNÉ DIALÓGY // // ========================================== // //////////////////////////////////////////////// ////////////////////////////////////////////////////////////// // Aktualizácia navigačných tlačidiel v konečných dialógoch // // Volať pri spustení hry a pri zobrazení dialógov // ////////////////////////////////////////////////////////////// function updateDialogNavigation() { console.log('Aktualizujem navigačné tlačidlá dialogov...'); // Získaj informácie o aktuálnom leveli const urlParams = new URLSearchParams(window.location.search); const worldId = urlParams.get('worldId') || urlParams.get('world'); const levelId = urlParams.get('levelId') || urlParams.get('level'); const isTraining = urlParams.get('training') === 'true'; console.log('Navigačné parametre:', { worldId, levelId, isTraining }); // Aktualizuj endgame dialog (pri výhre) updateEndGameDialog(worldId, levelId, isTraining); // Aktualizuj menu dialog (tlačidlo menu) updateMenuDialog(worldId, levelId, isTraining); } ////////////////////////////////////////////////// // Aktualizácia endgame dialógu (pri výhre) // ////////////////////////////////////////////////// function updateEndGameDialog(worldId, levelId, isTraining) { const endGameNav = document.querySelector('#endgame nav ul'); if (!endGameNav) { console.warn('Endgame navigation nebol nájdený'); return; } let restartUrl = 'game.html'; // fallback let backUrl = 'worldsmenu.html'; // fallback if (isTraining) { // Pre tréningové levely restartUrl = window.location.href; // Reštart s rovnakými parametrami backUrl = 'worldsmenu.html'; } else if (worldId && levelId) { // Pre normálne levely - zachovaj URL parametre restartUrl = `game.html?worldId=${worldId}&levelId=${levelId}`; backUrl = `worldsmenu.html`; // Späť na worlds menu } // Aktualizuj obsah endGameNav.innerHTML = `
  • `; console.log('Endgame dialog aktualizovaný'); } ////////////////////////////////////////////////// // Aktualizácia menu dialógu // ////////////////////////////////////////////////// function updateMenuDialog(worldId, levelId, isTraining) { const menuNav = document.querySelector('#dialogove-okno nav ul'); if (!menuNav) { console.warn('Menu dialog navigation nebol nájdený'); return; } let restartUrl = 'game.html'; let backUrl = 'worldsmenu.html'; if (isTraining) { restartUrl = window.location.href; backUrl = 'worldsmenu.html'; } else if (worldId && levelId) { restartUrl = `game.html?worldId=${worldId}&levelId=${levelId}`; backUrl = `worldsmenu.html`; } // Aktualizuj obsah menuNav.innerHTML = `
  • `; console.log('Menu dialog aktualizovaný'); } ////////////////////////////////////////////////// // Reštart aktuálneho levelu // ////////////////////////////////////////////////// function restartCurrentLevel() { console.log('Reštartujem aktuálny level...'); // Zatvor všetky dialógy closeAllDialogs(); // Reštartuj hru s rovnakými parametrami const urlParams = new URLSearchParams(window.location.search); const isTraining = urlParams.get('training') === 'true'; if (isTraining) { // Pre tréningové levely - reload stránky window.location.reload(); } else { // Pre normálne levely - reinicializuj s rovnakou konfiguráciou if (currentLevelConfig) { initializeGameWithLevel(currentLevelConfig, isCustomLevel); } else { window.location.reload(); } } } ////////////////////////////////////////////////// // Prechod na ďalší level // ////////////////////////////////////////////////// function goToNextLevel() { console.log('Pokúsim sa prejsť na ďalší level...'); // Získaj aktuálny worldId a levelId z URL parametrov const urlParams = new URLSearchParams(window.location.search); const worldId = urlParams.get('worldId') || urlParams.get('world'); const levelId = urlParams.get('levelId') || urlParams.get('level'); // Ak v URL chýbajú parametre, nemôžeme zistiť ďalší level - späť do menu if (!worldId || !levelId) { console.log('Chýbajú URL parametre (worldId/levelId), vraciam sa do menu'); returnToMenu(); return; } // Skontroluj, či je funkcia getNextLevel vôbec dostupná (z config/levels.js) if (typeof getNextLevel !== 'function') { console.error('Funkcia getNextLevel nie je dostupná - config/levels.js pravdepodobne nie je načítaný'); returnToMenu(); return; } // Získaj konfiguráciu ďalšieho levelu cez oficiálnu funkciu z levels.js const nextLevel = getNextLevel(levelId); // Ak ďalší level neexistuje (sme na poslednom leveli sveta), vráť sa do menu if (!nextLevel) { console.log(`Ďalší level po ${levelId} neexistuje - koniec sveta, vraciam sa do menu`); returnToMenu(); return; } // Zisti, aký typ hry má ďalší level a zostav správnu URL // gameType môže byť 'banik', 'pexeso' alebo 'mario' const gameUrls = { 'banik': 'game.html', 'pexeso': 'pexeso.html', 'mario': 'superjozino.html' }; const targetUrl = gameUrls[nextLevel.gameType]; // Ak typ hry nepoznáme, logni chybu a vráť sa do menu if (!targetUrl) { console.error(`Neznámy typ hry: ${nextLevel.gameType}`); returnToMenu(); return; } // Zatvor všetky otvorené dialógy pred presmerovaním closeAllDialogs(); // Presmeruj na ďalší level so správnymi URL parametrami const nextUrl = `${targetUrl}?worldId=${nextLevel.worldId}&levelId=${nextLevel.id}`; console.log(`Presmerovávam na ďalší level: ${nextUrl}`); window.location.href = nextUrl; } ////////////////////////////////////////////////// // Návrat do menu svetov // ////////////////////////////////////////////////// function returnToMenu() { console.log('Vraciam sa do menu svetov...'); closeAllDialogs(); window.location.href = 'worldsmenu.html'; } ////////////////////////////////////////////////// // Zatvorenie všetkých dialógov // ////////////////////////////////////////////////// function closeAllDialogs() { // Zatvor endgame dialog const endGameDialog = document.getElementById("endgame"); if (endGameDialog) { endGameDialog.style.display = "none"; } // Zatvor menu dialog const menuDialog = document.getElementById("dialogove-okno"); if (menuDialog) { menuDialog.style.display = "none"; } // Zatvor cvicenie dialog const cvicenieDialog = document.getElementById("cvicenie"); if (cvicenieDialog) { cvicenieDialog.style.display = "none"; } // Resetuj blur background const blurBackground = document.getElementById("blur-background"); if (blurBackground) { blurBackground.style.display = "none"; } // Resetuj body overflow document.body.classList.remove("dialog-open", "cvicenie-open"); document.body.style.overflow = "auto"; } //////////////////////////////////////////////// // ========================================== // // INICIALIZÁCIA NAVIGÁCIE // // ========================================== // //////////////////////////////////////////////// ////////////////////////////////////////// // Inicializácia navigačného systému // ////////////////////////////////////////// function initializeNavigation() { console.log('Inicializujem navigačný systém...'); // Aktualizuj dialógy hneď po načítaní updateDialogNavigation(); // Nastav event listenery pre existujúce funkcie setupNavigationEventListeners(); } ////////////////////////////////////////////////// // Nastavenie event listenerov pre navigáciu // ////////////////////////////////////////////////// function setupNavigationEventListeners() { // Existujúce funkcie closeDialog1 a openDialog1 zostávajú rovnaké // ale sa môžu rozšíriť o aktualizáciu navigácie console.log('Navigation event listenery nastavené'); } ///////////////////////////////////// // ====== Dodatočné funkcie ====== // ///////////////////////////////////// /* Otvorenie custom level modalu */ function openCustomLevelModal() { document.getElementById("custom-level-modal").style.display = "block"; document.getElementById("blur-background").style.display = "block"; document.body.style.overflow = "hidden"; } /* Zatvorenie custom level modalu */ function closeCustomModal() { document.getElementById("custom-level-modal").style.display = "none"; document.getElementById("blur-background").style.display = "none"; document.body.style.overflow = "auto"; } /* Spustenie custom levelu s vlastnými slovami */ function startCustomLevel() { const wordsInput = document.getElementById('custom-words-input').value.trim(); if (!wordsInput) { alert('Prosím zadajte aspoň jedno slovo!'); return; } // Rozdelenie slov po riadkoch a vyčistenie const customWords = wordsInput.split('\n') .map(word => word.trim()) .filter(word => word.length > 0); if (customWords.length === 0) { alert('Prosím zadajte platné slová!'); return; } console.log('Custom slová:', customWords); // Vytvorenie custom levelConfig const customLevelConfig = { words: customWords, diamonds: 3, // predvolené hodnoty golds: 4, crystals: 1, timeLimit: null, // bez časového limitu // žiadne positions = náhodné generovanie }; closeCustomModal(); initializeGameWithLevel(customLevelConfig, true); // true = custom level } ///////////////////////////////// // ===== HERNÁ SLUČKA ====== // // vykreslenie hraca // // vykreslenie itemov // ///////////////////////////////// function gameLoop() { ctx.clearRect(0, 0, canvas.width, canvas.height); drawPlayer(); drawClay(); drawDiamonds(); drawKov(); drawGolds(); requestAnimationFrame(gameLoop); } ///////////////////////////////// // ====== Hlavná slučka ====== // // Generovanie sveta, Gameloop // ///////////////////////////////// generateDiamonds(); generateKov(); generateGolds(); generateClay(); gameLoop(); ///////////////////////////////////////////// // ========== UŽITOČNÉ FUNKCIE ========== // ///////////////////////////////////////////// /** * Rýchle dokončenie levelu s perfektným výkonom * Použitie: Pre testovanie a debug */ function quickCompleteLevel() { console.log('Rýchle dokončenie levelu...'); // Inicializuj sledovanie výkonu initializePerformanceTracking(); // Simuluj perfektný výkon pre všetky rečové cvičenia // Každý diamant má X kôl (slov) podľa konfigurácie const speechRoundsPerDiamond = currentLevelConfig?.gameConfig?.speechExercises || 2; for (let diamond = 0; diamond < PocetGenDiamant; diamond++) { for (let round = 0; round < speechRoundsPerDiamond; round++) { recordSpeechExerciseResult(1, true); // 1 pokus = 3 body } } // Simuluj perfektný výkon pre všetky posluchové cvičenia // Každý kryštál má X kôl (párov) podľa konfigurácie const listeningRoundsPerCrystal = currentLevelConfig?.gameConfig?.listeningExercises || 2; for (let crystal = 0; crystal < PocetGenKov; crystal++) { for (let round = 0; round < listeningRoundsPerCrystal; round++) { recordListeningExerciseResult(1, true); // 1 pokus = 3 body } } // Zozbieraj všetky goldy for (let i = 0; i < PocetGenGolds; i++) { recordGoldCollected(); } // Nastav počty ako zozbierané diamondsCollected = PocetGenDiamant; goldsCollected = PocetGenGolds; kovCollected = PocetGenKov; gamePerformance.levelCompleted = true; // Aktualizuj UI updateDiamondsCollected(diamondsCollected); updategoldsCollected(goldsCollected); updateKovCollected(kovCollected); // Dokončenie levelu completeLevel(); console.log('Level dokončený s perfektným výkonom!'); } /** * Získanie detailných štatistík výkonu * @returns {Object} Objekt so štatistikami */ function getPerformanceStats() { const stats = { // Rečové cvičenia speech: { totalRounds: gamePerformance.speechExercises.roundResults.length, completedRounds: gamePerformance.speechExercises.completedRounds, totalPoints: gamePerformance.speechExercises.totalPoints, maxPossiblePoints: gamePerformance.speechExercises.roundResults.length * 3, roundDetails: gamePerformance.speechExercises.roundResults, averageAttempts: gamePerformance.speechExercises.roundResults.length > 0 ? (gamePerformance.speechExercises.roundResults.reduce((sum, r) => sum + r.attempts, 0) / gamePerformance.speechExercises.roundResults.length).toFixed(2) : 0 }, // Posluchové cvičenia listening: { totalRounds: gamePerformance.listeningExercises.roundResults.length, completedRounds: gamePerformance.listeningExercises.completedRounds, totalPoints: gamePerformance.listeningExercises.totalPoints, maxPossiblePoints: gamePerformance.listeningExercises.roundResults.length * 3, roundDetails: gamePerformance.listeningExercises.roundResults, averageAttempts: gamePerformance.listeningExercises.roundResults.length > 0 ? (gamePerformance.listeningExercises.roundResults.reduce((sum, r) => sum + r.attempts, 0) / gamePerformance.listeningExercises.roundResults.length).toFixed(2) : 0 }, // Celkové štatistiky overall: { totalPoints: gamePerformance.totalPoints, maxPossiblePoints: gamePerformance.maxPossiblePoints, percentage: gamePerformance.maxPossiblePoints > 0 ? Math.round((gamePerformance.totalPoints / gamePerformance.maxPossiblePoints) * 100) : 0, stars: gamePerformance.finalStars, levelCompleted: gamePerformance.levelCompleted }, // Goldy golds: { collected: gamePerformance.golds.collected, total: gamePerformance.golds.total }, // Čas time: { seconds: gameTimer.currentTime, formatted: formatTime(gameTimer.currentTime) } }; return stats; } /** * Vypočítanie počtu hviezd na základe percentuálneho výkonu * @param {number} percentage - Percentuálny výkon (0-100) * @returns {number} Počet hviezd (0-3) */ function getStarRating(percentage) { if (percentage >= 70) return 3; // 70%+ = 3 hviezdy if (percentage >= 40) return 2; // 40-69% = 2 hviezdy if (percentage >= 20) return 1; // 20-39% = 1 hviezda return 0; // <20% = 0 hviezd } /** * Zobrazenie súhrnu výkonu v konzole */ function showPerformanceSummary() { console.log(''); console.log('═══════════════════════════════════════'); console.log(' SÚHRN VÝKONU'); console.log('═══════════════════════════════════════'); const stats = getPerformanceStats(); // Rečové cvičenia console.log(''); console.log(' REČOVÉ CVIČENIA:'); console.log(` Absolvované kolá: ${stats.speech.completedRounds}/${stats.speech.totalRounds}`); console.log(` Body: ${stats.speech.totalPoints}/${stats.speech.maxPossiblePoints}`); console.log(` Priemerný počet pokusov: ${stats.speech.averageAttempts}`); // Detaily kôl if (stats.speech.roundDetails.length > 0) { console.log(' Detaily kôl:'); stats.speech.roundDetails.forEach((round, index) => { const icon = round.success ? '✅' : '❌'; console.log(` ${icon} Kolo ${index + 1}: ${round.attempts} pokusov = ${round.points} bodov`); }); } // Posluchové cvičenia console.log(''); console.log(' POSLUCHOVÉ CVIČENIA:'); console.log(` Absolvované kolá: ${stats.listening.completedRounds}/${stats.listening.totalRounds}`); console.log(` Body: ${stats.listening.totalPoints}/${stats.listening.maxPossiblePoints}`); console.log(` Priemerný počet pokusov: ${stats.listening.averageAttempts}`); // Detaily kôl if (stats.listening.roundDetails.length > 0) { console.log(' Detaily kôl:'); stats.listening.roundDetails.forEach((round, index) => { const icon = round.success ? '✅' : '❌'; console.log(` ${icon} Kolo ${index + 1}: ${round.attempts} pokusov = ${round.points} bodov`); }); } // Goldy console.log(''); console.log(' GOLDY:'); console.log(` Zozbierané: ${stats.golds.collected}/${stats.golds.total}`); // Celkový výsledok console.log(''); console.log(' CELKOVÝ VÝSLEDOK:'); console.log(` Body: ${stats.overall.totalPoints}/${stats.overall.maxPossiblePoints}`); console.log(` Úspešnosť: ${stats.overall.percentage}%`); console.log(` Hviezdy: ${'⭐'.repeat(stats.overall.stars)} (${stats.overall.stars}/3)`); console.log(` Čas: ${stats.time.formatted}`); console.log(` Level dokončený: ${stats.overall.levelCompleted ? 'Áno' : 'Nie'}`); console.log('═══════════════════════════════════════'); console.log(''); return stats; } /** * Reset levelu - kompletné resetovanie stavu hry */ function resetLevel() { console.log(' Resetujem level...'); // Reset zbieraných itemov diamondsCollected = 0; goldsCollected = 0; kovCollected = 0; correctAnswers = 0; incorrectAnswers = 0; // Reset výkonu initializePerformanceTracking(); // Reset timera if (gameTimer.intervalId) { clearInterval(gameTimer.intervalId); } gameTimer = { startTime: null, currentTime: 0, intervalId: null, timeLimit: currentLevelConfig?.timeLimit || null, isRunning: false, isPaused: false }; // Aktualizuj UI updateDiamondsCollected(0); updategoldsCollected(0); updateKovCollected(0); updateAnswerCounters(); // Resetuj timer display const timerElement = document.getElementById('game-timer'); if (timerElement) { timerElement.textContent = gameTimer.timeLimit ? formatTime(gameTimer.timeLimit) : '00:00'; } console.log(' Level resetovaný'); } /** * Získanie aktuálneho stavu hry * @returns {Object} Stav hry */ function getGameState() { return { items: { diamonds: { collected: diamondsCollected, total: PocetGenDiamant }, golds: { collected: goldsCollected, total: PocetGenGolds }, crystals: { collected: kovCollected, total: PocetGenKov } }, timer: { running: gameTimer.isRunning, currentTime: gameTimer.currentTime, timeLimit: gameTimer.timeLimit, formatted: formatTime(gameTimer.currentTime) }, performance: getPerformanceStats(), level: { config: currentLevelConfig, isCustom: isCustomLevel, completed: gamePerformance.levelCompleted } }; } // Pridanie funkcií do window pre ľahší prístup v konzole window.quickCompleteLevel = quickCompleteLevel; window.getPerformanceStats = getPerformanceStats; window.showPerformanceSummary = showPerformanceSummary; window.getStarRating = getStarRating; window.resetLevel = resetLevel; window.getGameState = getGameState; console.log('✅ Užitočné funkcie načítané:'); console.log(' - quickCompleteLevel() - Rýchle dokončenie levelu'); console.log(' - getPerformanceStats() - Získanie štatistík'); console.log(' - showPerformanceSummary() - Zobrazenie súhrnu'); console.log(' - getStarRating(percentage) - Výpočet hviezd'); console.log(' - resetLevel() - Reset levelu'); console.log(' - getGameState() - Aktuálny stav hry'); ///////////////////////////////////////////// // ===== DOKONČENIE LEVELU A POKROK ===== // ///////////////////////////////////////////// /** * Hlavná funkcia na dokončenie levelu * Volá všetky potrebné kroky vrátane uloženia a navigácie */ function completeLevel() { console.log(' Dokončujem level...'); // 1. Zastav timer stopTimer(); // 2. Vypočítaj hodnotenie const rating = calculateFinalRating(); console.log(' Hodnotenie levelu:', rating); // 3. Ulož výsledky do progress managera a získaj info o odomknutom obsahu const saveResult = saveResultsToProgress(rating); console.log(' Výsledok uloženia:', saveResult); // 4. Aktualizuj navigáciu pre win dialóg updateDialogNavigation(); // 5. Zobraz win dialóg s výsledkami setTimeout(() => { // Prehrať zvuk výhry if (typeof effectVyhra !== 'undefined') { effectVyhra.play(); } // Zobraziť dialóg document.getElementById("endgame").style.display = "block"; document.getElementById("blur-background").style.display = "block"; document.body.style.overflow = "hidden"; // Pridať výsledky do dialógu s informáciou o pokroku displayResultsInWinDialog(rating, saveResult); console.log(' Win dialóg zobrazený s výsledkami!'); // 6. Zobraz notifikácie o odomknutom obsahu if (saveResult.saved) { setTimeout(() => { showUnlockNotificationsInGame(saveResult); }, 1000); } }, 500); } /** * Kontrola a zobrazenie odomknutého obsahu */ function checkUnlockedContent() { if (!window.progressManager) { console.log('ProgressManager nie je dostupný pre kontrolu odomknutia'); return; } setTimeout(() => { const urlParams = new URLSearchParams(window.location.search); const worldId = urlParams.get('worldId') || urlParams.get('world'); const levelId = urlParams.get('levelId') || urlParams.get('level'); if (!worldId || !levelId) return; // Skontroluj či sa odomkol ďalší level if (typeof getLevelConfig === 'function') { const currentLevel = getLevelConfig(levelId); if (currentLevel) { const nextLevelNumber = currentLevel.levelNumber + 1; const nextLevelId = `${worldId}_${nextLevelNumber}`; const nextLevel = getLevelConfig(nextLevelId); if (nextLevel) { const nextProgress = window.progressManager.getLevelProgress(worldId, nextLevelId); if (nextProgress && nextProgress.isUnlocked) { console.log(` Odomknutý ďalší level: ${nextLevel.name}`); // Môžeš pridať notifikáciu do UI showUnlockNotification('level', nextLevel); } } } } // Skontroluj či sa odomkol nový svet const worldProgress = window.progressManager.getProgress().worlds[worldId]; if (worldProgress && worldProgress.completedLevels > 0) { console.log(` Pokrok sveta ${worldId}: ${worldProgress.completedLevels} levelov, ${worldProgress.stars} hviezd`); } }, 1000); }