diff --git a/Js/script.js b/Js/script.js
index 3e0dcd4..e233a80 100644
--- a/Js/script.js
+++ b/Js/script.js
@@ -19,6 +19,9 @@ const resultsElement = document.getElementById("results");
const endScoreElement = document.getElementById("end-score");
const endHighScoreElement = document.getElementById("end-high-score");
const restartTextElement = document.getElementById("restart-text");
+const instructionsMenuElement = document.getElementById("instructions-menu");
+const openInstructionsBtn = document.getElementById("open-instructions");
+const closeInstructionsBtn = document.getElementById("close-instructions");
// High score
const HIGH_SCORE_KEY = "stackerHighScore";
@@ -67,6 +70,15 @@ function saveHighScore(value) {
// Initialize the game
init();
+// Initialize menu state after DOM is loaded
+document.addEventListener('DOMContentLoaded', () => {
+ // Update menu theme buttons to match current theme
+ updateMenuThemeButtons();
+
+ // Update menu audio button to match current mute state
+ updateMenuAudioButton();
+});
+
// Determines how precise the game is on autopilot
function setRobotPrecision() {
robotPrecision = Math.random() - 0.5;
@@ -473,6 +485,93 @@ function missedTheSpot() {
playFailSound();
}
+// ----- Instructions Menu -----
+function toggleInstructionsMenu() {
+ if (instructionsMenuElement) {
+ const isHidden = instructionsMenuElement.style.display === "none" || !instructionsMenuElement.style.display;
+ instructionsMenuElement.style.display = isHidden ? "flex" : "none";
+ }
+}
+
+// Open instructions menu button
+if (openInstructionsBtn) {
+ openInstructionsBtn.addEventListener("click", (e) => {
+ e.preventDefault();
+ e.stopPropagation();
+ toggleInstructionsMenu();
+ });
+}
+
+// Close instructions menu button
+if (closeInstructionsBtn) {
+ closeInstructionsBtn.addEventListener("click", (e) => {
+ e.preventDefault();
+ e.stopPropagation();
+ if (instructionsMenuElement) {
+ instructionsMenuElement.style.display = "none";
+ }
+ });
+}
+
+// Menu theme buttons
+const menuThemeButtons = {
+ default: document.getElementById("menu-theme-default"),
+ night: document.getElementById("menu-theme-night"),
+ sunset: document.getElementById("menu-theme-sunset")
+};
+
+// Menu audio button
+const menuMuteBtn = document.getElementById("menu-mute-btn");
+const menuAudioIcon = menuMuteBtn?.querySelector(".audio-icon");
+const menuAudioText = menuMuteBtn?.querySelector(".audio-text");
+
+// Update menu theme buttons to match current theme
+function updateMenuThemeButtons() {
+ const currentTheme = window.themeManager?.getCurrentTheme() || "default";
+ for (const [theme, button] of Object.entries(menuThemeButtons)) {
+ if (button) {
+ button.classList.toggle("active", theme === currentTheme);
+ }
+ }
+}
+
+// Update menu audio button to match current mute state
+function updateMenuAudioButton() {
+ if (menuAudioIcon && menuAudioText) {
+ menuAudioIcon.textContent = muted ? "🔇" : "🔊";
+ menuAudioText.textContent = muted ? "Sound Off" : "Sound On";
+ }
+}
+
+// Add event listeners for menu theme buttons
+for (const [theme, button] of Object.entries(menuThemeButtons)) {
+ if (button) {
+ button.addEventListener("click", (e) => {
+ e.preventDefault();
+ e.stopPropagation();
+ if (window.themeManager) {
+ window.themeManager.setTheme(theme);
+ updateMenuThemeButtons();
+ }
+ });
+ }
+}
+
+// Add event listener for menu audio button
+if (menuMuteBtn) {
+ menuMuteBtn.addEventListener("click", (e) => {
+ e.preventDefault();
+ e.stopPropagation();
+ muted = !muted;
+ sounds.bgm.muted = muted;
+ updateMenuAudioButton();
+ // Also update the main mute button
+ if (muteBtn) {
+ muteBtn.textContent = muted ? "🔇" : "🔊";
+ }
+ });
+}
+
// ----- Audio Controls -----
const muteBtn = document.createElement("button");
muteBtn.id = "muteBtn";
@@ -527,6 +626,16 @@ document.addEventListener("keydown", (e) => {
startGame();
}
}
+ if (e.key === "m" || e.key === "M") {
+ e.preventDefault();
+ muted = !muted;
+ sounds.bgm.muted = muted;
+ muteBtn.textContent = muted ? "🔇" : "🔊";
+ }
+ if (e.key === "h" || e.key === "H") {
+ e.preventDefault();
+ toggleInstructionsMenu();
+ }
}, true);
window.addEventListener(
"touchstart",
@@ -534,8 +643,10 @@ window.addEventListener(
if (
e.target.closest(".twitter-link") || // The Twitter link
e.target.closest("#muteBtn") || // The mute button
- e.target.closest("#theme-controls") || // The theme buttons container
- e.target.closest("#close-results")
+ e.target.closest("#instructions-menu") || // The instructions menu
+ e.target.closest("#open-instructions") || // The instructions button
+ e.target.closest("#close-results") || // The close results button
+ e.target.closest("#close-instructions") // The close instructions button
) {
// If it was, do nothing and let the browser handle the 'click' event
return;
diff --git a/css/style.css b/css/style.css
index 5b4ea15..b368dc5 100644
--- a/css/style.css
+++ b/css/style.css
@@ -74,6 +74,256 @@ body {
}
}
+/* Instructions/Controls Menu */
+#instructions-menu {
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ display: none;
+ padding: 0;
+ background: var(--ui-panel-bg);
+ border-radius: 20px;
+ box-shadow: 0 20px 60px var(--ui-shadow-color),
+ 0 0 0 1px var(--ui-border-color),
+ inset 0 1px 0 rgba(255, 220, 150, 0.15);
+ backdrop-filter: blur(10px);
+ width: 90%;
+ max-width: 600px;
+ min-width: 320px;
+ max-height: 80vh;
+ overflow-y: auto;
+ animation: resultsFadeIn 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275);
+ z-index: 1000;
+}
+
+#instructions-menu .content {
+ padding: 2rem 2rem;
+ width: 100%;
+}
+
+#instructions-menu h2 {
+ margin: 0 0 2rem 0;
+ font-size: clamp(1.5rem, 4vw, 2rem);
+ font-weight: 900;
+ background: var(--ui-gradient-primary);
+ -webkit-background-clip: text;
+ background-clip: text;
+ -webkit-text-fill-color: transparent;
+ text-align: center;
+}
+
+.menu-section {
+ margin-bottom: 2rem;
+}
+
+.menu-section h3 {
+ margin: 0 0 1rem 0;
+ font-size: clamp(1.1rem, 3vw, 1.3rem);
+ font-weight: 700;
+ color: rgba(255, 255, 255, 0.9);
+ display: flex;
+ align-items: center;
+ gap: 0.5rem;
+}
+
+.instruction-content {
+ background: rgba(60, 50, 40, 0.3);
+ border: 1px solid var(--ui-border-color);
+ border-radius: 12px;
+ padding: 1.25rem;
+}
+
+.instruction-content p {
+ margin: 0 0 1rem 0;
+ color: rgba(255, 255, 255, 0.9);
+ font-size: clamp(0.9rem, 2.2vw, 1rem);
+ line-height: 1.5;
+}
+
+.instruction-content ul {
+ margin: 0;
+ padding-left: 1.5rem;
+ color: rgba(255, 255, 255, 0.8);
+ font-size: clamp(0.85rem, 2vw, 0.95rem);
+ line-height: 1.6;
+}
+
+.instruction-content li {
+ margin-bottom: 0.5rem;
+}
+
+.blue-text {
+ color: #4facfe;
+ font-weight: 600;
+}
+
+.controls-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
+ gap: 1rem;
+ margin-bottom: 1rem;
+}
+
+.control-item {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ background: rgba(60, 50, 40, 0.4);
+ border: 1px solid var(--ui-border-color);
+ border-radius: 8px;
+ padding: 1rem 1.25rem;
+ transition: all 0.3s ease;
+}
+
+.control-item:hover {
+ background: rgba(80, 65, 50, 0.5);
+ transform: translateX(4px);
+ border-color: rgba(255, 200, 100, 0.3);
+}
+
+.control-item kbd {
+ background: var(--ui-gradient-button);
+ border: 2px solid var(--ui-border-color);
+ border-radius: 6px;
+ padding: 0.4rem 0.8rem;
+ font-family: 'Courier New', monospace;
+ font-weight: 700;
+ font-size: clamp(0.85rem, 2vw, 1rem);
+ box-shadow: 0 2px 0 rgba(0, 0, 0, 0.3),
+ inset 0 1px 0 rgba(255, 220, 150, 0.2);
+ color: #ffcc00;
+ min-width: 60px;
+ text-align: center;
+}
+
+.control-item span {
+ color: rgba(255, 255, 255, 0.9);
+ font-size: clamp(0.9rem, 2.2vw, 1.1rem);
+ font-weight: 500;
+}
+
+.controls-note {
+ text-align: center;
+ color: rgba(255, 255, 255, 0.6);
+ font-size: clamp(0.8rem, 2vw, 0.9rem);
+ font-style: italic;
+ margin: 0;
+}
+
+.setting-group {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ margin-bottom: 1.5rem;
+ padding: 1rem;
+ background: rgba(60, 50, 40, 0.3);
+ border: 1px solid var(--ui-border-color);
+ border-radius: 12px;
+}
+
+.setting-group label {
+ color: rgba(255, 255, 255, 0.9);
+ font-size: clamp(0.9rem, 2.2vw, 1rem);
+ font-weight: 600;
+ margin: 0;
+}
+
+.theme-buttons {
+ display: flex;
+ gap: 0.75rem;
+}
+
+.theme-buttons .theme-btn {
+ background: rgba(255, 255, 255, 0.2);
+ border: 2px solid rgba(255, 255, 255, 0.3);
+ border-radius: 12px;
+ padding: 8px 12px;
+ cursor: pointer;
+ font-size: 1.2rem;
+ transition: all 0.3s ease;
+ backdrop-filter: blur(10px);
+}
+
+.theme-buttons .theme-btn:hover {
+ background: rgba(255, 255, 255, 0.3);
+ border-color: rgba(255, 255, 255, 0.6);
+ transform: scale(1.1);
+}
+
+.theme-buttons .theme-btn.active {
+ background: rgba(255, 200, 100, 0.4);
+ border-color: rgba(255, 200, 100, 0.8);
+ box-shadow: 0 0 15px rgba(255, 200, 100, 0.3);
+}
+
+.audio-btn {
+ display: flex;
+ align-items: center;
+ gap: 0.5rem;
+ background: rgba(255, 255, 255, 0.2);
+ border: 2px solid rgba(255, 255, 255, 0.3);
+ border-radius: 12px;
+ padding: 0.5rem 1rem;
+ cursor: pointer;
+ transition: all 0.3s ease;
+ backdrop-filter: blur(10px);
+ color: rgba(255, 255, 255, 0.9);
+ font-size: clamp(0.85rem, 2vw, 0.95rem);
+}
+
+.audio-btn:hover {
+ background: rgba(255, 255, 255, 0.3);
+ border-color: rgba(255, 255, 255, 0.6);
+ transform: scale(1.05);
+}
+
+.audio-icon {
+ font-size: 1.1em;
+}
+
+.menu-footer {
+ text-align: center;
+ color: rgba(255, 255, 255, 0.6);
+ font-size: clamp(0.85rem, 2vw, 0.95rem);
+ margin: 0;
+ padding-top: 1rem;
+ border-top: 1px solid var(--ui-border-color);
+}
+
+/* Instructions Button */
+#instructions-button {
+ position: absolute;
+ top: clamp(8px, 2.5vh, 20px);
+ left: clamp(12px, 2.5vw, 24px);
+ z-index: 11;
+}
+
+.instructions-btn {
+ background: linear-gradient(145deg, rgba(255, 255, 255, 0.25), rgba(255, 255, 255, 0.1));
+ border: none;
+ border-radius: 12px;
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.2), inset 0 1px 1px rgba(255, 255, 255, 0.3);
+ text-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
+ backdrop-filter: blur(8px);
+ -webkit-backdrop-filter: blur(8px);
+ font-size: clamp(1.5rem, 4vw, 2.5rem);
+ font-weight: 900;
+ padding: 0.5rem 1.25rem;
+ cursor: pointer;
+ transition: transform 0.3s ease, box-shadow 0.3s ease;
+ color: white;
+ line-height: 1;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+
+.instructions-btn:hover {
+ transform: scale(1.05);
+ box-shadow: 0 6px 25px rgba(0, 0, 0, 0.3), inset 0 1px 1px rgba(255, 255, 255, 0.3);
+}
+
#results .content {
padding: 2rem 2rem;
width: 100%;
@@ -530,6 +780,84 @@ a:visited {
padding: 1.5rem 1.25rem;
}
+ #instructions-menu {
+ min-width: 280px;
+ max-width: 92vw;
+ width: 90%;
+ max-height: 85vh;
+ }
+
+ #instructions-menu .content {
+ padding: 1.5rem 1.25rem;
+ }
+
+ #instructions-menu h2 {
+ font-size: 1.5rem;
+ margin-bottom: 1.5rem;
+ }
+
+ .menu-section {
+ margin-bottom: 1.5rem;
+ }
+
+ .menu-section h3 {
+ font-size: 1rem;
+ margin-bottom: 0.75rem;
+ }
+
+ .instruction-content {
+ padding: 1rem;
+ }
+
+ .instruction-content p {
+ font-size: 0.85rem;
+ }
+
+ .instruction-content ul {
+ font-size: 0.8rem;
+ padding-left: 1.25rem;
+ }
+
+ .controls-grid {
+ grid-template-columns: 1fr;
+ gap: 0.75rem;
+ }
+
+ .control-item {
+ padding: 0.75rem 1rem;
+ }
+
+ .control-item kbd {
+ font-size: 0.8rem;
+ padding: 0.3rem 0.6rem;
+ min-width: 50px;
+ }
+
+ .control-item span {
+ font-size: 0.85rem;
+ }
+
+ .setting-group {
+ flex-direction: column;
+ align-items: flex-start;
+ gap: 0.75rem;
+ padding: 0.75rem;
+ }
+
+ .theme-buttons {
+ gap: 0.5rem;
+ }
+
+ .theme-buttons .theme-btn {
+ padding: 6px 10px;
+ font-size: 1rem;
+ }
+
+ .audio-btn {
+ padding: 0.4rem 0.8rem;
+ font-size: 0.8rem;
+ }
+
.score-container {
flex-direction: column;
gap: 1.5rem;
diff --git a/index.html b/index.html
index 70849f5..2ed6139 100644
--- a/index.html
+++ b/index.html
@@ -27,6 +27,81 @@
+
+
+
@@ -101,6 +176,11 @@
Game Over!
+
+
+
+
+