diff --git a/index.html b/index.html index 1d0cff8..29b4d90 100644 --- a/index.html +++ b/index.html @@ -21,6 +21,18 @@ + + +
diff --git a/src/audio.js b/src/audio.js index 3090314..7042f9c 100644 --- a/src/audio.js +++ b/src/audio.js @@ -134,7 +134,15 @@ export class AudioSystem { } speakDirections(directions) { - if (!this.speechEnabled || !('speechSynthesis' in window)) { + console.log('speakDirections called with:', directions); + + if (!this.speechEnabled) { + console.log('Speech is disabled'); + return; + } + + if (!('speechSynthesis' in window)) { + console.error('Speech synthesis not supported in this browser'); return; } @@ -146,7 +154,10 @@ export class AudioSystem { if (directions.left) activeDirections.push('left'); if (directions.right) activeDirections.push('right'); + console.log('Active directions:', activeDirections); + if (activeDirections.length === 0) { + console.log('No active directions to announce'); return; } @@ -160,8 +171,12 @@ export class AudioSystem { textToSpeak = activeDirections.slice(0, -1).join(', ') + ', and ' + activeDirections[activeDirections.length - 1]; } + console.log('Text to speak:', textToSpeak); + console.log('Last spoken direction:', this.lastSpokenDirection); + // Avoid repeating the same direction announcement if (textToSpeak === this.lastSpokenDirection) { + console.log('Skipping duplicate direction announcement'); return; } this.lastSpokenDirection = textToSpeak; @@ -169,21 +184,89 @@ export class AudioSystem { // Stop any currently playing speech speechSynthesis.cancel(); - // Create and configure the speech utterance - const utterance = new SpeechSynthesisUtterance(textToSpeak); - utterance.rate = 1.2; // Slightly faster speech - utterance.pitch = 1.0; - utterance.volume = 0.8; - - // Clear the last spoken direction when speech ends - utterance.onend = () => { - setTimeout(() => { - this.lastSpokenDirection = ''; - }, 500); // Small delay to prevent immediate repetition - }; + try { + // Check if speechSynthesis is ready + if (speechSynthesis.speaking) { + console.log('Speech synthesis is currently speaking, canceling...'); + speechSynthesis.cancel(); + } - // Speak the directions - speechSynthesis.speak(utterance); + // Wait a bit for cancellation to complete + setTimeout(() => { + try { + // Create and configure the speech utterance + const utterance = new SpeechSynthesisUtterance(textToSpeak); + utterance.rate = 1.0; // Normal speech rate to avoid issues + utterance.pitch = 1.0; + utterance.volume = 0.8; + utterance.lang = 'en-US'; // Explicitly set language + + // Enhanced event handlers + utterance.onstart = () => { + console.log('Speech started for:', textToSpeak); + }; + + utterance.onend = () => { + console.log('Speech ended for:', textToSpeak); + setTimeout(() => { + this.lastSpokenDirection = ''; + }, 500); + }; + + utterance.onerror = (event) => { + console.error('Speech synthesis error:', event.error, 'for text:', textToSpeak); + console.error('Error details:', event); + + // Try to recover by clearing the last spoken direction + setTimeout(() => { + this.lastSpokenDirection = ''; + }, 100); + + // Fallback: try a simpler approach + this.fallbackSpeech(textToSpeak); + }; + + utterance.onpause = () => { + console.log('Speech paused'); + }; + + utterance.onresume = () => { + console.log('Speech resumed'); + }; + + // Check if voices are available + const voices = speechSynthesis.getVoices(); + console.log('Available voices:', voices.length, voices.map(v => v.name)); + + if (voices.length > 0) { + // Try to use a default English voice + const englishVoice = voices.find(voice => + voice.lang.startsWith('en') && voice.default + ) || voices.find(voice => + voice.lang.startsWith('en') + ) || voices[0]; + + if (englishVoice) { + utterance.voice = englishVoice; + console.log('Using voice:', englishVoice.name, englishVoice.lang); + } + } + + console.log('Starting speech synthesis for:', textToSpeak); + + // Speak the directions + speechSynthesis.speak(utterance); + + } catch (innerError) { + console.error('Inner error in speech synthesis:', innerError); + this.fallbackSpeech(textToSpeak); + } + }, 100); // Small delay to ensure cancellation completes + + } catch (error) { + console.error('Outer error in speech synthesis:', error); + this.fallbackSpeech(textToSpeak); + } } setSpeechEnabled(enabled) { @@ -193,6 +276,31 @@ export class AudioSystem { this.lastSpokenDirection = ''; } } + + // Debug methods for testing directions + debugTestDirection(direction) { + console.log('Debug: Testing direction:', direction); + const testDirections = { + up: direction === 'up', + down: direction === 'down', + left: direction === 'left', + right: direction === 'right', + squares: [{ x: 0, y: 0, dx: 0, dy: 0 }] // Mock square data + }; + this.speakDirections(testDirections); + } + + debugTestMultipleDirections(directionsArray) { + console.log('Debug: Testing multiple directions:', directionsArray); + const testDirections = { + up: directionsArray.includes('up'), + down: directionsArray.includes('down'), + left: directionsArray.includes('left'), + right: directionsArray.includes('right'), + squares: [{ x: 0, y: 0, dx: 0, dy: 0 }] // Mock square data + }; + this.speakDirections(testDirections); + } async playWinSound() { await this.ensureAudioContext(); diff --git a/src/game.js b/src/game.js index 21233e5..4a24ba7 100644 --- a/src/game.js +++ b/src/game.js @@ -25,6 +25,7 @@ export class Game { this.soundEnabled = true; this.speechEnabled = true; this.vibrationEnabled = true; + this.debugMode = false; this.lastProximityWarning = 0; this.setupControls(); @@ -35,6 +36,7 @@ export class Game { const soundToggle = document.getElementById('soundToggle'); const speechToggle = document.getElementById('speechToggle'); const vibrationToggle = document.getElementById('vibrationToggle'); + const debugToggle = document.getElementById('debugToggle'); soundToggle.addEventListener('click', () => { this.soundEnabled = !this.soundEnabled; @@ -54,6 +56,76 @@ export class Game { this.vibrationEnabled = !this.vibrationEnabled; vibrationToggle.textContent = `📳 Vibration: ${this.vibrationEnabled ? 'ON' : 'OFF'}`; }); + + debugToggle.addEventListener('click', () => { + this.debugMode = !this.debugMode; + debugToggle.textContent = `🐛 Debug: ${this.debugMode ? 'ON' : 'OFF'}`; + + // Show/hide debug controls + const debugControls = document.getElementById('debugControls'); + debugControls.style.display = this.debugMode ? 'block' : 'none'; + + // Setup debug button listeners if debug mode is enabled + if (this.debugMode) { + this.setupDebugControls(); + } + }); + } + + setupDebugControls() { + const testUp = document.getElementById('testUp'); + const testDown = document.getElementById('testDown'); + const testLeft = document.getElementById('testLeft'); + const testRight = document.getElementById('testRight'); + const testMultiple = document.getElementById('testMultiple'); + const testAllDirections = document.getElementById('testAllDirections'); + + // Remove existing listeners to prevent duplicates + const buttons = [testUp, testDown, testLeft, testRight, testMultiple, testAllDirections]; + buttons.forEach(button => { + if (button) { + button.replaceWith(button.cloneNode(true)); + } + }); + + // Get fresh references after cloning + const newTestUp = document.getElementById('testUp'); + const newTestDown = document.getElementById('testDown'); + const newTestLeft = document.getElementById('testLeft'); + const newTestRight = document.getElementById('testRight'); + const newTestMultiple = document.getElementById('testMultiple'); + const newTestAllDirections = document.getElementById('testAllDirections'); + + // Add click listeners + newTestUp?.addEventListener('click', () => { + console.log('Debug: Testing UP direction'); + this.audioSystem.debugTestDirection('up'); + }); + + newTestDown?.addEventListener('click', () => { + console.log('Debug: Testing DOWN direction'); + this.audioSystem.debugTestDirection('down'); + }); + + newTestLeft?.addEventListener('click', () => { + console.log('Debug: Testing LEFT direction'); + this.audioSystem.debugTestDirection('left'); + }); + + newTestRight?.addEventListener('click', () => { + console.log('Debug: Testing RIGHT direction'); + this.audioSystem.debugTestDirection('right'); + }); + + newTestMultiple?.addEventListener('click', () => { + console.log('Debug: Testing UP and RIGHT directions'); + this.audioSystem.debugTestMultipleDirections(['up', 'right']); + }); + + newTestAllDirections?.addEventListener('click', () => { + console.log('Debug: Testing ALL directions'); + this.audioSystem.debugTestMultipleDirections(['up', 'down', 'left', 'right']); + }); } bindEvents() { @@ -149,6 +221,7 @@ export class Game { // Use a timer to avoid playing too frequently const now = Date.now(); if (!this.lastProximityWarning || now - this.lastProximityWarning > 1500) { + console.log('Playing directional proximity sound'); this.audioSystem.playDirectionalProximitySound(directions); this.lastProximityWarning = now; } @@ -159,8 +232,8 @@ export class Game { this.ctx.fillStyle = '#1a1a1a'; this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height); - // Render maze - this.maze.render(this.ctx, this.cellSize); + // Render maze with debug mode + this.maze.render(this.ctx, this.cellSize, this.debugMode); // Render player this.player.render(this.ctx); diff --git a/src/maze.js b/src/maze.js index 7e961e3..01536e3 100644 --- a/src/maze.js +++ b/src/maze.js @@ -275,7 +275,7 @@ export class Maze { return this.grid[y][x] === this.EXIT; } - render(ctx, cellSize) { + render(ctx, cellSize, debugMode = false) { for (let y = 0; y < this.rows; y++) { for (let x = 0; x < this.cols; x++) { const pixelX = x * cellSize; @@ -291,8 +291,7 @@ export class Maze { break; case this.SAFE: - case this.DANGEROUS_AUDIO: - // Safe squares and audio-danger squares look identical (white) + // Safe squares - white ctx.fillStyle = '#ffffff'; ctx.fillRect(pixelX, pixelY, cellSize, cellSize); // Add subtle grid lines @@ -300,9 +299,41 @@ export class Maze { ctx.lineWidth = 1; ctx.strokeRect(pixelX, pixelY, cellSize, cellSize); break; - //debug - // case this.DANGEROUS_AUDIO: + case this.DANGEROUS_AUDIO: + if (debugMode) { + // In debug mode, show audio danger squares with a blue overlay + ctx.fillStyle = '#ffffff'; + ctx.fillRect(pixelX, pixelY, cellSize, cellSize); + + // Add blue debug overlay with pulsing effect + const time = Date.now() * 0.004; + const pulse = (Math.sin(time + x + y) + 1) * 0.5; + const alpha = 0.3 + pulse * 0.4; + ctx.fillStyle = `rgba(0, 100, 255, ${alpha})`; + ctx.fillRect(pixelX, pixelY, cellSize, cellSize); + + // Add debug border + ctx.strokeStyle = '#0066ff'; + ctx.lineWidth = 2; + ctx.strokeRect(pixelX, pixelY, cellSize, cellSize); + + // Add audio symbol + ctx.fillStyle = '#ffffff'; + ctx.font = `${cellSize * 0.4}px Arial`; + ctx.textAlign = 'center'; + ctx.textBaseline = 'middle'; + ctx.fillText('🔊', pixelX + cellSize / 2, pixelY + cellSize / 2); + } else { + // In normal mode, audio-danger squares look identical to safe squares (white) + ctx.fillStyle = '#ffffff'; + ctx.fillRect(pixelX, pixelY, cellSize, cellSize); + // Add subtle grid lines + ctx.strokeStyle = '#e0e0e0'; + ctx.lineWidth = 1; + ctx.strokeRect(pixelX, pixelY, cellSize, cellSize); + } + break; case this.DANGEROUS_VISUAL: // Visual-only danger squares - animated red appearance diff --git a/src/style.css b/src/style.css index 58fc1ec..b133eb5 100644 --- a/src/style.css +++ b/src/style.css @@ -130,6 +130,55 @@ h1 { box-shadow: 0 2px 10px rgba(76, 175, 80, 0.3); } +/* Debug Controls */ +.debug-controls { + margin-top: 1.5rem; + padding: 1.5rem; + background: rgba(255, 100, 100, 0.1); + border: 2px solid rgba(255, 100, 100, 0.3); + border-radius: 10px; + box-shadow: 0 4px 15px rgba(255, 100, 100, 0.2); +} + +.debug-controls h3 { + margin-top: 0; + margin-bottom: 1rem; + color: #ff6b6b; + font-size: 1.3em; +} + +.debug-buttons { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); + gap: 0.8rem; + justify-content: center; +} + +.debug-buttons button { + background: linear-gradient(45deg, #ff6b6b, #ff5252); + color: white; + border: none; + padding: 0.6rem 1rem; + border-radius: 20px; + font-size: 0.9rem; + font-weight: bold; + cursor: pointer; + transition: all 0.3s ease; + box-shadow: 0 3px 10px rgba(255, 107, 107, 0.3); + border: 1px solid rgba(255, 255, 255, 0.2); +} + +.debug-buttons button:hover { + transform: translateY(-2px); + box-shadow: 0 5px 15px rgba(255, 107, 107, 0.4); + background: linear-gradient(45deg, #ff5252, #ff6b6b); +} + +.debug-buttons button:active { + transform: translateY(0); + box-shadow: 0 2px 8px rgba(255, 107, 107, 0.3); +} + /* Responsive design */ @media (max-width: 768px) { #app {