//////////////////////////////////////////////
// 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}
`;
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 += `

`;
} else {
// Neaktívna hviezda
starsHTML += `

`;
}
}
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
`;
// 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 = `
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 = `
${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 = `
${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 = `
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 = `
SKÚSTE ZNOVA
Zostávajú ${remainingAttempts} pokusy
`;
} else {
document.getElementById("vysledok").innerHTML = `
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 = `
`;
// 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);
}