diff --git a/index.html b/index.html
index 401fa07..58f8758 100644
--- a/index.html
+++ b/index.html
@@ -108,7 +108,26 @@
Reduces visual effects for better performance on mobile devices
-
+
+
+
+
+
+
+
+
+
diff --git a/script.js b/script.js
index d21c22e..800b148 100644
--- a/script.js
+++ b/script.js
@@ -70,6 +70,7 @@ let enableSpinAnimations = true;
let animationSpeed = 0.05;
let forceMobileControls = false;
let reduceEffectsOnMobile = true; // New option to reduce effects on mobile
+let startingLevel = 1; // New option for starting level
// Controller variables
let gamepadConnected = false;
@@ -703,7 +704,8 @@ class Piece {
// Calculate shadow for new piece
p.calculateShadowY();
- // Draw the new piece
+ // Draw the new piece and board
+ drawBoard();
p.draw();
// Play hard drop sound
@@ -1823,7 +1825,8 @@ function togglePause() {
// Show pause message
showMessage('PAUSED', 'Press P to Resume');
} else {
- gameInterval = setInterval(dropPiece, Math.max(100, 1000 - (level * 100)));
+ let speed = Math.max(100, 1000 - (level * 100));
+ gameInterval = setInterval(dropPiece, speed);
pauseBtn.textContent = 'Pause';
// Hide pause message
hideMessage();
@@ -1834,7 +1837,9 @@ function togglePause() {
function resetGame() {
// Reset game variables
score = 0;
- level = 1;
+ console.log("Starting level is set to:", startingLevel);
+ level = startingLevel;
+ console.log("Level is now set to:", level);
lines = 0;
gameOver = false;
paused = false;
@@ -1862,13 +1867,18 @@ function resetGame() {
// Start the game interval
dropStart = Date.now();
clearInterval(gameInterval);
- gameInterval = setInterval(dropPiece, 1000);
+ // Set interval based on starting level
+ let speed = Math.max(100, 1000 - (level * 100));
+ console.log("Game speed set to:", speed, "ms");
+ gameInterval = setInterval(dropPiece, speed);
}
// Toggle options modal
function toggleOptionsModal() {
if (optionsModal.classList.contains('active')) {
optionsModal.classList.remove('active');
+ // Apply options when closing the modal
+ applyOptions();
} else {
optionsModal.classList.add('active');
}
@@ -1880,6 +1890,7 @@ function applyOptions() {
enableSpinAnimations = toggleSpinAnimations.checked;
animationSpeed = parseFloat(animationSpeedSlider.value);
forceMobileControls = toggleMobileControls.checked;
+ startingLevel = parseInt(document.getElementById('starting-level').value);
// Apply mobile controls if checked or if on mobile device
if (forceMobileControls) {
@@ -1914,7 +1925,8 @@ function saveOptions() {
enableSpinAnimations,
animationSpeed,
forceMobileControls,
- reduceEffectsOnMobile
+ reduceEffectsOnMobile,
+ startingLevel
}));
}
@@ -1929,6 +1941,7 @@ function loadOptions() {
animationSpeed = options.animationSpeed !== undefined ? options.animationSpeed : 0.05;
forceMobileControls = options.forceMobileControls !== undefined ? options.forceMobileControls : false;
reduceEffectsOnMobile = options.reduceEffectsOnMobile !== undefined ? options.reduceEffectsOnMobile : true;
+ startingLevel = options.startingLevel !== undefined ? options.startingLevel : 1;
// Update UI controls
toggle3DEffects.checked = enable3DEffects;
@@ -1942,6 +1955,10 @@ function loadOptions() {
if (document.getElementById('toggle-mobile-performance')) {
document.getElementById('toggle-mobile-performance').checked = reduceEffectsOnMobile;
}
+
+ if (document.getElementById('starting-level')) {
+ document.getElementById('starting-level').value = startingLevel;
+ }
}
}
@@ -1953,6 +1970,10 @@ function init() {
// Load saved options
loadOptions();
+ // Set initial level
+ level = startingLevel;
+ levelElement.textContent = level;
+
// Draw the board
drawBoard();
@@ -1965,7 +1986,9 @@ function init() {
// Set up game interval
dropStart = Date.now();
- gameInterval = setInterval(dropPiece, 1000);
+ // Use level-appropriate speed
+ let speed = Math.max(100, 1000 - (level * 100));
+ gameInterval = setInterval(dropPiece, speed);
// Listen for keyboard events
document.addEventListener('keydown', control);
@@ -1984,6 +2007,14 @@ function init() {
optionsBtn.addEventListener('click', toggleOptionsModal);
optionsCloseBtn.addEventListener('click', toggleOptionsModal);
+ // Add event listener for the apply button
+ if (document.getElementById('options-apply-btn')) {
+ document.getElementById('options-apply-btn').addEventListener('click', function() {
+ applyOptions();
+ console.log("Options applied, starting level is now:", startingLevel);
+ });
+ }
+
// Options event listeners
toggle3DEffects.addEventListener('change', function() {
applyOptions();
@@ -1997,6 +2028,12 @@ function init() {
applyOptions();
});
+ if (document.getElementById('starting-level')) {
+ document.getElementById('starting-level').addEventListener('change', function() {
+ applyOptions();
+ });
+ }
+
if (document.getElementById('toggle-mobile-performance')) {
document.getElementById('toggle-mobile-performance').addEventListener('change', function() {
mobilePerformanceMode = this.checked;
@@ -2890,4 +2927,98 @@ window.onload = function() {
// Force resize to ensure proper mobile layout
window.dispatchEvent(new Event('resize'));
}
-};
\ No newline at end of file
+};
+
+// Check for completed rows and clear them
+function checkRows() {
+ let linesCleared = 0;
+ let clearedRows = [];
+
+ for (let r = 0; r < ROWS; r++) {
+ let isRowFull = true;
+
+ // Check if the row is full
+ for (let c = 0; c < COLS; c++) {
+ if (board[r][c] === EMPTY) {
+ isRowFull = false;
+ break;
+ }
+ }
+
+ // If the row is full, clear it
+ if (isRowFull) {
+ clearedRows.push(r);
+ linesCleared++;
+
+ // Shift rows down
+ for (let y = r; y > 0; y--) {
+ for (let c = 0; c < COLS; c++) {
+ board[y][c] = board[y-1][c];
+ }
+ }
+
+ // Clear the top row
+ for (let c = 0; c < COLS; c++) {
+ board[0][c] = EMPTY;
+ }
+ }
+ }
+
+ // Create fireworks for each cleared row
+ if (clearedRows.length > 0) {
+ // Reduce number of fireworks on mobile for better performance
+ const fireworksPerRow = mobilePerformanceMode ? 1 : 3;
+
+ for (let i = 0; i < clearedRows.length; i++) {
+ // Create multiple fireworks along the row
+ for (let j = 0; j < fireworksPerRow; j++) {
+ const x = (Math.random() * COLS * BLOCK_SIZE) + BLOCK_SIZE/2;
+ const y = (clearedRows[i] * BLOCK_SIZE) + BLOCK_SIZE/2;
+ fireworks.push(new Firework(x, y));
+ }
+ }
+
+ // Play sound effect for line clear
+ playLineClearSound(clearedRows.length);
+ }
+
+ return linesCleared;
+}
+
+// Update score based on lines cleared
+function updateScore() {
+ // Get number of lines cleared
+ let linesCleared = checkRows();
+
+ // Update score
+ if (linesCleared > 0) {
+ // Points increase for multiple lines cleared at once
+ const linePoints = [0, 100, 300, 500, 800]; // 0, 1, 2, 3, 4 lines
+ score += linePoints[linesCleared] * level;
+ lines += linesCleared;
+
+ // Level up every 10 lines
+ if (Math.floor(lines / 10) > level - 1) {
+ level = Math.floor(lines / 10) + 1;
+ // Speed up the game as level increases
+ clearInterval(gameInterval);
+ gameInterval = setInterval(dropPiece, Math.max(100, 1000 - (level * 100)));
+ }
+
+ // Update UI
+ scoreElement.textContent = score;
+ levelElement.textContent = level;
+ linesElement.textContent = lines;
+ }
+}
+
+// Simple function to play audio
+function playSound(audioElement) {
+ // Reset the audio to the beginning
+ audioElement.currentTime = 0;
+ // Play the sound
+ audioElement.play().catch(e => {
+ // Suppress errors from autoplay restrictions
+ console.log("Sound play failed:", e);
+ });
+}
\ No newline at end of file
diff --git a/style.css b/style.css
index e52a7de..96b4f96 100644
--- a/style.css
+++ b/style.css
@@ -385,8 +385,9 @@ canvas#tetris {
/* Options menu styles */
.option-row {
display: flex;
+ justify-content: space-between;
align-items: center;
- margin: 15px 0;
+ margin-bottom: 15px;
position: relative;
}
@@ -794,4 +795,14 @@ input[type=range]::-moz-range-thumb {
.instructions-btn {
margin-top: 15px;
background: linear-gradient(45deg, #ff9900, #ff5500);
+}
+
+.button-row {
+ display: flex;
+ justify-content: space-between;
+ margin-top: 20px;
+}
+
+.button-row .game-btn {
+ margin: 0 5px;
}
\ No newline at end of file