dino-land: add autoplay-safe music toggle on correct CI/CD path

This commit is contained in:
OpenClaw Engineer 2026-03-14 20:08:59 -05:00
parent d57951466e
commit 800880ed30
3 changed files with 77 additions and 1 deletions

View File

@ -13,6 +13,7 @@
</div>
<canvas id="game" width="960" height="540" aria-label="Dino Land game canvas"></canvas>
<button id="musicToggle" type="button" aria-pressed="false" aria-label="Toggle music">🔇 Music: Off</button>
<div id="startScreen">
<div class="panel">

View File

@ -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();

View File

@ -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;