diff --git a/index.php b/index.php index e0ce76a..b3280b1 100644 --- a/index.php +++ b/index.php @@ -13,6 +13,7 @@ +
diff --git a/js/game.js b/js/game.js index f1b73a0..604f36b 100644 --- a/js/game.js +++ b/js/game.js @@ -8,6 +8,36 @@ const leaderboardEl = document.getElementById('leaderboard'); const saveScoreFormEl = document.getElementById('saveScoreForm'); const playerNameEl = document.getElementById('playerName'); const restartBtnEl = document.getElementById('restartBtn'); +const musicToggleEl = document.getElementById('musicToggle'); + +const MUSIC_STORAGE_KEY = 'dinoLand.musicEnabled'; + +function resolveInitialMusicEnabled() { + try { + const raw = window.localStorage.getItem(MUSIC_STORAGE_KEY); + if (raw === null) return false; + return raw !== '0' && raw !== 'false'; + } catch (_) { + return false; + } +} + +function persistMusicEnabled(enabled) { + try { + window.localStorage.setItem(MUSIC_STORAGE_KEY, enabled ? '1' : '0'); + } catch (_) { + // no-op in restricted contexts + } +} + +let musicEnabled = resolveInitialMusicEnabled(); + +function refreshMusicToggleUi() { + if (!musicToggleEl) return; + musicToggleEl.textContent = musicEnabled ? '🎵 Music: On' : '🔇 Music: Off'; + musicToggleEl.setAttribute('aria-pressed', musicEnabled ? 'true' : 'false'); + musicToggleEl.setAttribute('title', musicEnabled ? 'Disable music' : 'Enable music (first click also unlocks audio)'); +} const startScreenEl = document.getElementById('startScreen'); const startFormEl = document.getElementById('startForm'); @@ -64,10 +94,22 @@ let gameConfig = { accessMode: 'standard', difficulty: 'medium' }; function ensureAudioCtx() { if (!audioCtx) audioCtx = new (window.AudioContext || window.webkitAudioContext)(); - if (audioCtx.state === 'suspended') audioCtx.resume(); + return audioCtx; +} + +async function resumeAudioContext() { + const ctx = ensureAudioCtx(); + if (ctx.state !== 'suspended') return true; + try { + await ctx.resume(); + return true; + } catch (_) { + return false; + } } function playSfx(type) { + if (!musicEnabled) return; try { ensureAudioCtx(); const now = audioCtx.currentTime; @@ -238,6 +280,22 @@ window.addEventListener('keydown', (e) => { }); window.addEventListener('keyup', (e) => keys.delete(e.key)); +if (musicToggleEl) { + refreshMusicToggleUi(); + musicToggleEl.addEventListener('click', async (ev) => { + ev.preventDefault(); + ev.stopPropagation(); + + if (!musicEnabled) { + await resumeAudioContext(); + } + + musicEnabled = !musicEnabled; + persistMusicEnabled(musicEnabled); + refreshMusicToggleUi(); + }); +} + startFormEl.addEventListener('submit', (e) => { e.preventDefault(); const pass = passwordEl.value.trim(); diff --git a/styles.css b/styles.css index 53b74a5..7e508ed 100644 --- a/styles.css +++ b/styles.css @@ -25,6 +25,23 @@ body { border: 2px solid #1b3a47; background: #b5ecff; } +#musicToggle { + position: fixed; + right: 16px; + bottom: 16px; + z-index: 9; + width: auto; + min-width: 132px; + padding: 10px 12px; + border-radius: 10px; + border: 1px solid rgba(255,255,255,0.24); + background: rgba(0,0,0,0.65); + color: #fff; + font-size: 14px; + font-weight: 700; + cursor: pointer; +} + #deathScreen, #startScreen { position: fixed;