mirror of
https://github.com/cmclark00/tetris-3d.git
synced 2025-05-17 23:25:21 +01:00
Add start level selector and fix hard drop piece disappearing bug
This commit is contained in:
parent
0f52dad8d4
commit
210a518ad5
3 changed files with 170 additions and 9 deletions
21
index.html
21
index.html
|
@ -108,7 +108,26 @@
|
||||||
<span class="tooltip">Reduces visual effects for better performance on mobile devices</span>
|
<span class="tooltip">Reduces visual effects for better performance on mobile devices</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button id="options-close-btn" class="game-btn">Close</button>
|
<div class="option-row">
|
||||||
|
<label for="starting-level">Starting Level</label>
|
||||||
|
<select id="starting-level">
|
||||||
|
<option value="1">Level 1</option>
|
||||||
|
<option value="2">Level 2</option>
|
||||||
|
<option value="3">Level 3</option>
|
||||||
|
<option value="4">Level 4</option>
|
||||||
|
<option value="5">Level 5</option>
|
||||||
|
<option value="6">Level 6</option>
|
||||||
|
<option value="7">Level 7</option>
|
||||||
|
<option value="8">Level 8</option>
|
||||||
|
<option value="9">Level 9</option>
|
||||||
|
<option value="10">Level 10</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="button-row">
|
||||||
|
<button id="options-apply-btn" class="game-btn">Apply</button>
|
||||||
|
<button id="options-close-btn" class="game-btn">Close</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
145
script.js
145
script.js
|
@ -70,6 +70,7 @@ let enableSpinAnimations = true;
|
||||||
let animationSpeed = 0.05;
|
let animationSpeed = 0.05;
|
||||||
let forceMobileControls = false;
|
let forceMobileControls = false;
|
||||||
let reduceEffectsOnMobile = true; // New option to reduce effects on mobile
|
let reduceEffectsOnMobile = true; // New option to reduce effects on mobile
|
||||||
|
let startingLevel = 1; // New option for starting level
|
||||||
|
|
||||||
// Controller variables
|
// Controller variables
|
||||||
let gamepadConnected = false;
|
let gamepadConnected = false;
|
||||||
|
@ -703,7 +704,8 @@ class Piece {
|
||||||
// Calculate shadow for new piece
|
// Calculate shadow for new piece
|
||||||
p.calculateShadowY();
|
p.calculateShadowY();
|
||||||
|
|
||||||
// Draw the new piece
|
// Draw the new piece and board
|
||||||
|
drawBoard();
|
||||||
p.draw();
|
p.draw();
|
||||||
|
|
||||||
// Play hard drop sound
|
// Play hard drop sound
|
||||||
|
@ -1823,7 +1825,8 @@ function togglePause() {
|
||||||
// Show pause message
|
// Show pause message
|
||||||
showMessage('PAUSED', 'Press P to Resume');
|
showMessage('PAUSED', 'Press P to Resume');
|
||||||
} else {
|
} 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';
|
pauseBtn.textContent = 'Pause';
|
||||||
// Hide pause message
|
// Hide pause message
|
||||||
hideMessage();
|
hideMessage();
|
||||||
|
@ -1834,7 +1837,9 @@ function togglePause() {
|
||||||
function resetGame() {
|
function resetGame() {
|
||||||
// Reset game variables
|
// Reset game variables
|
||||||
score = 0;
|
score = 0;
|
||||||
level = 1;
|
console.log("Starting level is set to:", startingLevel);
|
||||||
|
level = startingLevel;
|
||||||
|
console.log("Level is now set to:", level);
|
||||||
lines = 0;
|
lines = 0;
|
||||||
gameOver = false;
|
gameOver = false;
|
||||||
paused = false;
|
paused = false;
|
||||||
|
@ -1862,13 +1867,18 @@ function resetGame() {
|
||||||
// Start the game interval
|
// Start the game interval
|
||||||
dropStart = Date.now();
|
dropStart = Date.now();
|
||||||
clearInterval(gameInterval);
|
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
|
// Toggle options modal
|
||||||
function toggleOptionsModal() {
|
function toggleOptionsModal() {
|
||||||
if (optionsModal.classList.contains('active')) {
|
if (optionsModal.classList.contains('active')) {
|
||||||
optionsModal.classList.remove('active');
|
optionsModal.classList.remove('active');
|
||||||
|
// Apply options when closing the modal
|
||||||
|
applyOptions();
|
||||||
} else {
|
} else {
|
||||||
optionsModal.classList.add('active');
|
optionsModal.classList.add('active');
|
||||||
}
|
}
|
||||||
|
@ -1880,6 +1890,7 @@ function applyOptions() {
|
||||||
enableSpinAnimations = toggleSpinAnimations.checked;
|
enableSpinAnimations = toggleSpinAnimations.checked;
|
||||||
animationSpeed = parseFloat(animationSpeedSlider.value);
|
animationSpeed = parseFloat(animationSpeedSlider.value);
|
||||||
forceMobileControls = toggleMobileControls.checked;
|
forceMobileControls = toggleMobileControls.checked;
|
||||||
|
startingLevel = parseInt(document.getElementById('starting-level').value);
|
||||||
|
|
||||||
// Apply mobile controls if checked or if on mobile device
|
// Apply mobile controls if checked or if on mobile device
|
||||||
if (forceMobileControls) {
|
if (forceMobileControls) {
|
||||||
|
@ -1914,7 +1925,8 @@ function saveOptions() {
|
||||||
enableSpinAnimations,
|
enableSpinAnimations,
|
||||||
animationSpeed,
|
animationSpeed,
|
||||||
forceMobileControls,
|
forceMobileControls,
|
||||||
reduceEffectsOnMobile
|
reduceEffectsOnMobile,
|
||||||
|
startingLevel
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1929,6 +1941,7 @@ function loadOptions() {
|
||||||
animationSpeed = options.animationSpeed !== undefined ? options.animationSpeed : 0.05;
|
animationSpeed = options.animationSpeed !== undefined ? options.animationSpeed : 0.05;
|
||||||
forceMobileControls = options.forceMobileControls !== undefined ? options.forceMobileControls : false;
|
forceMobileControls = options.forceMobileControls !== undefined ? options.forceMobileControls : false;
|
||||||
reduceEffectsOnMobile = options.reduceEffectsOnMobile !== undefined ? options.reduceEffectsOnMobile : true;
|
reduceEffectsOnMobile = options.reduceEffectsOnMobile !== undefined ? options.reduceEffectsOnMobile : true;
|
||||||
|
startingLevel = options.startingLevel !== undefined ? options.startingLevel : 1;
|
||||||
|
|
||||||
// Update UI controls
|
// Update UI controls
|
||||||
toggle3DEffects.checked = enable3DEffects;
|
toggle3DEffects.checked = enable3DEffects;
|
||||||
|
@ -1942,6 +1955,10 @@ function loadOptions() {
|
||||||
if (document.getElementById('toggle-mobile-performance')) {
|
if (document.getElementById('toggle-mobile-performance')) {
|
||||||
document.getElementById('toggle-mobile-performance').checked = reduceEffectsOnMobile;
|
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
|
// Load saved options
|
||||||
loadOptions();
|
loadOptions();
|
||||||
|
|
||||||
|
// Set initial level
|
||||||
|
level = startingLevel;
|
||||||
|
levelElement.textContent = level;
|
||||||
|
|
||||||
// Draw the board
|
// Draw the board
|
||||||
drawBoard();
|
drawBoard();
|
||||||
|
|
||||||
|
@ -1965,7 +1986,9 @@ function init() {
|
||||||
|
|
||||||
// Set up game interval
|
// Set up game interval
|
||||||
dropStart = Date.now();
|
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
|
// Listen for keyboard events
|
||||||
document.addEventListener('keydown', control);
|
document.addEventListener('keydown', control);
|
||||||
|
@ -1984,6 +2007,14 @@ function init() {
|
||||||
optionsBtn.addEventListener('click', toggleOptionsModal);
|
optionsBtn.addEventListener('click', toggleOptionsModal);
|
||||||
optionsCloseBtn.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
|
// Options event listeners
|
||||||
toggle3DEffects.addEventListener('change', function() {
|
toggle3DEffects.addEventListener('change', function() {
|
||||||
applyOptions();
|
applyOptions();
|
||||||
|
@ -1997,6 +2028,12 @@ function init() {
|
||||||
applyOptions();
|
applyOptions();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (document.getElementById('starting-level')) {
|
||||||
|
document.getElementById('starting-level').addEventListener('change', function() {
|
||||||
|
applyOptions();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (document.getElementById('toggle-mobile-performance')) {
|
if (document.getElementById('toggle-mobile-performance')) {
|
||||||
document.getElementById('toggle-mobile-performance').addEventListener('change', function() {
|
document.getElementById('toggle-mobile-performance').addEventListener('change', function() {
|
||||||
mobilePerformanceMode = this.checked;
|
mobilePerformanceMode = this.checked;
|
||||||
|
@ -2890,4 +2927,98 @@ window.onload = function() {
|
||||||
// Force resize to ensure proper mobile layout
|
// Force resize to ensure proper mobile layout
|
||||||
window.dispatchEvent(new Event('resize'));
|
window.dispatchEvent(new Event('resize'));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
});
|
||||||
|
}
|
13
style.css
13
style.css
|
@ -385,8 +385,9 @@ canvas#tetris {
|
||||||
/* Options menu styles */
|
/* Options menu styles */
|
||||||
.option-row {
|
.option-row {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
margin: 15px 0;
|
margin-bottom: 15px;
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -794,4 +795,14 @@ input[type=range]::-moz-range-thumb {
|
||||||
.instructions-btn {
|
.instructions-btn {
|
||||||
margin-top: 15px;
|
margin-top: 15px;
|
||||||
background: linear-gradient(45deg, #ff9900, #ff5500);
|
background: linear-gradient(45deg, #ff9900, #ff5500);
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-row {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-row .game-btn {
|
||||||
|
margin: 0 5px;
|
||||||
}
|
}
|
Loading…
Add table
Add a link
Reference in a new issue