dino-land: add autoplay-safe music toggle on correct CI/CD path
This commit is contained in:
parent
d57951466e
commit
800880ed30
@ -13,6 +13,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<canvas id="game" width="960" height="540" aria-label="Dino Land game canvas"></canvas>
|
<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 id="startScreen">
|
||||||
<div class="panel">
|
<div class="panel">
|
||||||
|
|||||||
60
js/game.js
60
js/game.js
@ -8,6 +8,36 @@ const leaderboardEl = document.getElementById('leaderboard');
|
|||||||
const saveScoreFormEl = document.getElementById('saveScoreForm');
|
const saveScoreFormEl = document.getElementById('saveScoreForm');
|
||||||
const playerNameEl = document.getElementById('playerName');
|
const playerNameEl = document.getElementById('playerName');
|
||||||
const restartBtnEl = document.getElementById('restartBtn');
|
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 startScreenEl = document.getElementById('startScreen');
|
||||||
const startFormEl = document.getElementById('startForm');
|
const startFormEl = document.getElementById('startForm');
|
||||||
@ -64,10 +94,22 @@ let gameConfig = { accessMode: 'standard', difficulty: 'medium' };
|
|||||||
|
|
||||||
function ensureAudioCtx() {
|
function ensureAudioCtx() {
|
||||||
if (!audioCtx) audioCtx = new (window.AudioContext || window.webkitAudioContext)();
|
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) {
|
function playSfx(type) {
|
||||||
|
if (!musicEnabled) return;
|
||||||
try {
|
try {
|
||||||
ensureAudioCtx();
|
ensureAudioCtx();
|
||||||
const now = audioCtx.currentTime;
|
const now = audioCtx.currentTime;
|
||||||
@ -238,6 +280,22 @@ window.addEventListener('keydown', (e) => {
|
|||||||
});
|
});
|
||||||
window.addEventListener('keyup', (e) => keys.delete(e.key));
|
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) => {
|
startFormEl.addEventListener('submit', (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const pass = passwordEl.value.trim();
|
const pass = passwordEl.value.trim();
|
||||||
|
|||||||
17
styles.css
17
styles.css
@ -25,6 +25,23 @@ body {
|
|||||||
border: 2px solid #1b3a47;
|
border: 2px solid #1b3a47;
|
||||||
background: #b5ecff;
|
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,
|
#deathScreen,
|
||||||
#startScreen {
|
#startScreen {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user