From 50cba7552971e64862f491f6747fcb5ed87987a0 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Sat, 28 Mar 2026 04:23:27 +0000 Subject: [PATCH] feat: add 'Play Again' button to game-over overlay - Add Play Again button visible in both qualifying and non-qualifying scenarios - For non-qualifying scores: button appears alongside auto-restart timer, clicking cancels timer and restarts immediately - For qualifying scores: button appears alongside name entry so players can skip submission and restart - Styled consistently with existing game-over overlay UI - Existing auto-restart behavior preserved if player doesn't click button Co-Authored-By: bot_apk --- index.html | 20 ++++++++++++++++++++ js/main.js | 16 +++++++++++----- public/css/styles.css | 19 +++++++++++++++++++ public/index.html | 1 + public/js/main.js | 8 +++++++- 5 files changed, 58 insertions(+), 6 deletions(-) diff --git a/index.html b/index.html index 5fd8ad9..65efce1 100644 --- a/index.html +++ b/index.html @@ -185,6 +185,25 @@ #name-submit:hover { background: #5aa0e9; } + #play-again-btn { + display: none; + margin-top: 16px; + font-size: 1em; + padding: 8px 24px; + border-radius: 8px; + border: 1px solid rgba(255,255,255,0.3); + background: transparent; + color: #fff; + cursor: pointer; + font-family: inherit; + font-weight: 600; + } + #play-again-btn:hover { + background: rgba(255,255,255,0.1); + } + #play-again-btn.visible { + display: inline-block; + } #leaderboard-panel { position: fixed; top: 0; left: 0; right: 0; bottom: 0; @@ -337,6 +356,7 @@
+
diff --git a/js/main.js b/js/main.js index 23a5a2b..7930f2b 100644 --- a/js/main.js +++ b/js/main.js @@ -33,6 +33,7 @@ const gameoverMessage = gameoverOverlay.querySelector('.go-message'); const nameEntry = document.getElementById('name-entry'); const nameInput = document.getElementById('name-input'); const nameSubmit = document.getElementById('name-submit'); +const playAgainBtn = document.getElementById('play-again-btn'); const leaderboardPanel = document.getElementById('leaderboard-panel'); const leaderboardList = document.getElementById('leaderboard-list'); const leaderboardClose = document.getElementById('leaderboard-close'); @@ -162,9 +163,15 @@ function enterGameOver() { gameoverTitle.textContent = 'GAME OVER'; gameoverScore.textContent = 'Score: ' + finalScore; + levelEl.style.display = 'none'; + timerEl.style.display = 'none'; + playAgainBtn.classList.remove('visible'); + gameoverOverlay.classList.add('visible'); + if (scoreQualifies(finalScore)) { gameoverMessage.textContent = 'New high score!'; nameEntry.classList.add('visible'); + playAgainBtn.classList.add('visible'); const savedName = loadPlayerName(); nameInput.value = savedName; nameInput.focus(); @@ -172,14 +179,11 @@ function enterGameOver() { nameInput.select(); } } else { - gameoverMessage.textContent = ''; + gameoverMessage.textContent = 'Restarting...'; nameEntry.classList.remove('visible'); + playAgainBtn.classList.add('visible'); resetTimer = setTimeout(() => { exitGameOver(); }, NON_QUALIFYING_DELAY); } - - levelEl.style.display = 'none'; - timerEl.style.display = 'none'; - gameoverOverlay.classList.add('visible'); } function submitScore() { @@ -194,6 +198,7 @@ function exitGameOver() { if (resetTimer) { clearTimeout(resetTimer); resetTimer = null; } gameoverOverlay.classList.remove('visible'); nameEntry.classList.remove('visible'); + playAgainBtn.classList.remove('visible'); resetTrack(); const config = getTrackConfig(); @@ -224,6 +229,7 @@ function getStartBallPosition(config) { nameSubmit.addEventListener('click', () => { submitScore(); }); nameInput.addEventListener('keydown', (e) => { if (e.key === 'Enter') submitScore(); }); +playAgainBtn.addEventListener('click', () => { exitGameOver(); }); leaderboardBtn.addEventListener('click', () => { showLeaderboard(); }); leaderboardClose.addEventListener('click', () => { hideLeaderboard(); }); leaderboardPanel.addEventListener('click', (e) => { if (e.target === leaderboardPanel) hideLeaderboard(); }); diff --git a/public/css/styles.css b/public/css/styles.css index 53c8470..a32a96e 100644 --- a/public/css/styles.css +++ b/public/css/styles.css @@ -199,6 +199,25 @@ canvas { display: block; } #name-submit:hover { background: #5aa0e9; } +#play-again-btn { + display: none; + margin-top: 16px; + font-size: 1em; + padding: 8px 24px; + border-radius: 8px; + border: 1px solid rgba(255,255,255,0.3); + background: transparent; + color: #fff; + cursor: pointer; + font-family: inherit; + font-weight: 600; +} +#play-again-btn:hover { + background: rgba(255,255,255,0.1); +} +#play-again-btn.visible { + display: inline-block; +} #leaderboard-panel { position: fixed; top: 0; left: 0; right: 0; bottom: 0; diff --git a/public/index.html b/public/index.html index c1bf9d7..235c3f2 100644 --- a/public/index.html +++ b/public/index.html @@ -36,6 +36,7 @@
+
diff --git a/public/js/main.js b/public/js/main.js index 12b9971..a39b242 100644 --- a/public/js/main.js +++ b/public/js/main.js @@ -33,6 +33,7 @@ const gameoverMessage = gameoverOverlay.querySelector('.go-message'); const nameEntry = document.getElementById('name-entry'); const nameInput = document.getElementById('name-input'); const nameSubmit = document.getElementById('name-submit'); +const playAgainBtn = document.getElementById('play-again-btn'); const leaderboardPanel = document.getElementById('leaderboard-panel'); const leaderboardList = document.getElementById('leaderboard-list'); const leaderboardClose = document.getElementById('leaderboard-close'); @@ -301,12 +302,14 @@ async function enterGameOver() { levelEl.style.display = 'none'; timerEl.style.display = 'none'; speedEl.style.display = 'none'; + playAgainBtn.classList.remove('visible'); gameoverOverlay.classList.add('visible'); const qualifies = await scoreQualifies(finalScore); if (qualifies) { gameoverMessage.textContent = 'New high score!'; nameEntry.classList.add('visible'); + playAgainBtn.classList.add('visible'); const savedName = loadPlayerName(); nameInput.value = savedName; nameInput.focus(); @@ -314,8 +317,9 @@ async function enterGameOver() { nameInput.select(); } } else { - gameoverMessage.textContent = ''; + gameoverMessage.textContent = 'Restarting...'; nameEntry.classList.remove('visible'); + playAgainBtn.classList.add('visible'); resetTimer = setTimeout(() => { exitGameOver(); }, NON_QUALIFYING_DELAY); } } @@ -340,6 +344,7 @@ function exitGameOver() { if (resetTimer) { clearTimeout(resetTimer); resetTimer = null; } gameoverOverlay.classList.remove('visible'); nameEntry.classList.remove('visible'); + playAgainBtn.classList.remove('visible'); resetTrack(); const config = getTrackConfig(); @@ -371,6 +376,7 @@ function getStartBallPosition(config) { nameSubmit.addEventListener('click', () => { submitScore(); }); nameInput.addEventListener('keydown', (e) => { if (e.key === 'Enter') submitScore(); }); +playAgainBtn.addEventListener('click', () => { exitGameOver(); }); leaderboardBtn.addEventListener('click', () => { showLeaderboard(); }); leaderboardClose.addEventListener('click', () => { hideLeaderboard(); }); leaderboardPanel.addEventListener('click', (e) => { if (e.target === leaderboardPanel) hideLeaderboard(); });