DP-Logopedicka-Platforma/js/game.js
2026-04-24 14:58:15 +02:00

4035 lines
146 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//////////////////////////////////////////////
// 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 = '<h3 style="color: #ff4444;">⏰ Čas vypršal!</h3>';
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} <img src="images/spravne.png">`;
}
if (incorrectElement) {
incorrectElement.innerHTML = `${incorrectAnswers} <img src="images/nespravne.png">`;
}
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 = `
<a>Slová: </a>
<span>${rating.speechSuccess}</span> <img src="images/spravne.png">
<span> / </span>
<span>${speechIncorrect}</span> <img src="images/nespravne.png">
`;
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 = `
<div style="margin-top: 10px;">
<a>Body: </a>
<span>${rating.totalPoints}/${rating.maxPossiblePoints}</span>
</div>
<div>
<a>Úspešnosť: </a>
<span>${rating.percentage}%</span>
</div>
`;
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 = '<div class="stars-container" style="display: flex; gap: 5px; justify-content: center; margin: 0px 0px 10px 0px;">';
for (let i = 1; i <= 3; i++) {
if (i <= stars) {
// Aktívna hviezda
starsHTML += `<img src="images/star_active.png" alt="Hviezda" style="width: 50px; height: 50px;">`;
} else {
// Neaktívna hviezda
starsHTML += `<img src="images/star_inactive.png" alt="Hviezda" style="width: 50px; height: 5updatePerformanceDisplayupdatePerformanceDisplay0px; opacity: 0.3;">`;
}
}
starsHTML += '</div>';
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 = `
<!-- Hlavička -->
<div class="cvicenie-text">
<H1 style="font-size: 30px">POSLUCHOVÉ CVIČENIE</h1>
</div>
<div id="listening-word-progress" class="word-progress">
<div class="progress-text">Kolo ${listeningCurrentIndex + 1} / ${listeningWordPairs.length}</div>
<div class="progress-bar">
<div class="progress-fill" style="width: ${((listeningCurrentIndex + 1) / listeningWordPairs.length) * 100}%"></div>
</div>
</div>
<!-- Inštrukcie -->
<div class="listening-instruction">
<p>Počúvaj pozorne a rozhodni, či sú slová rovnaké alebo rôzne</p>
</div>
<!-- Status zvuku -->
<div class="sound-status-container">
<div id="listening-status" class="sound-status">Pripravujem slová...</div>
</div>
<!-- Tlačidlá pre odpoveď -->
<div id="listening-buttons-container" class="listening-buttons-container" style="display: flex;">
<button id="listening-same-btn" class="listening-btn listening-btn-same disabled-btn" disabled>
<img src="images/rovnake.png" alt="Rovnaké">
<span>ROVNAKÉ</span>
</button>
<button id="listening-different-btn" class="listening-btn listening-btn-different disabled-btn" disabled>
<img src="images/rozdielne.png" alt="Rozdielne">
<span>RÔZNE</span>
</button>
</div>
`;
// 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 = `<center> <img src="images/spravne.png" alt="Správne"> <div class="success-message">Výborne!</div></center>`;
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 = `<div class="attempt-message">Zostávajúce pokusy: ${remainingAttempts}</div>`;
vysledokElement.innerHTML = `
<center>
<img src="images/nespravne.png" alt="Nesprávne">
${attemptMessage}
</center>
`;
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 = `<div class="attempt-message final-attempt">Posledný pokus vyčerpaný</div>`;
vysledokElement.innerHTML = `
<center>
<img src="images/nespravne.png" alt="Nesprávne">
${attemptMessage}
</center>
`;
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 = `
<center>
<img src="images/spravne.png" alt="Správne">
<div class="success-message">Výborne!</div>
</center>
`;
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 = `
<center>
<img src="images/nespravne.png" alt="Nesprávne">
<div class="attempt-message">
<span style="color: #ff6b6b; font-size: 28px;">SKÚSTE ZNOVA</span><br>
<span style="color: white; font-size: 20px;">Zostávajú ${remainingAttempts} pokusy</span>
</div>
</center>
`;
} else {
document.getElementById("vysledok").innerHTML = `
<center>
<img src="images/nespravne.png" alt="Nesprávne">
<div class="attempt-message">
<span style="color: #ff6b6b; font-size: 28px;">VYČERPANÉ POKUSY</span><br>
<span style="color: white; font-size: 18px;">Pokračujeme ďalej</span>
</div>
</center>
`;
}
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 = '<a>POČÚVAM...</a>';
console.log('🎙️ UI: Počúvam...');
} else {
// Stav: Pripravený
button.disabled = false;
tlacidloDiv.classList.remove('recording');
button.innerHTML = '<a>HOVORIŤ</a>';
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 = `
<center>
<div class="error-message">
<span style="color: #ff6b6b; font-size: 24px;">⚠️</span><br>
<span style="color: white; font-size: 18px;">${message}</span>
</div>
</center>
`;
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 = `
<div id="word-progress" class="word-progress">
<div class="progress-text">Slovo 1 / 2</div>
<div class="progress-bar">
<div class="progress-fill"></div>
</div>
</div>
`;
// 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 = `
<li><button onclick="restartCurrentLevel()" class="menu-button">Hrať znova</button></li>
<li><button onclick="goToNextLevel()" class="menu-button">Ďalší level</button></li>
<li><button onclick="returnToMenu()" class="menu-button">Mapa levelov</button></li>
`;
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 = `
<li><button onclick="restartCurrentLevel()" class="menu-button">Reštart</button></li>
<li><button onclick="returnToMenu()" class="menu-button">Svety</button></li>
<li><button onclick="window.location.href='index.html'" class="menu-button">Menu</button></li>
`;
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);
}