Implement new notification system for game events, enhancing user feedback on score, lives, and bonuses. Update game.js to replace popup notifications with a dynamic notification banner. Modify index.html to include notification structure and update styles.css for notification styling and animations.
This commit is contained in:
parent
50ab82b641
commit
1c8782b1cd
3 changed files with 201 additions and 15 deletions
168
game.js
168
game.js
|
@ -359,12 +359,15 @@ function handleLevelCompletion(squares) {
|
|||
|
||||
const finalBonus = baseTimeBonus + speedBonus + comboBonus;
|
||||
|
||||
// Show bonus breakdown
|
||||
showTimeBonusPopup(finalBonus, {
|
||||
base: baseTimeBonus,
|
||||
speed: speedBonus,
|
||||
combo: comboBonus
|
||||
});
|
||||
// Show bonus notification
|
||||
let bonusMessage = `<span class="bonus-change">Level Complete! +${finalBonus}s</span>`;
|
||||
if (speedBonus > 0) {
|
||||
bonusMessage += `<br><span class="score-change">Speed Bonus: +${speedBonus}s</span>`;
|
||||
}
|
||||
if (comboBonus > 0) {
|
||||
bonusMessage += `<br><span class="score-change">Combo Bonus: +${comboBonus}s</span>`;
|
||||
}
|
||||
showNotification(bonusMessage, 2000);
|
||||
|
||||
// Clear squares with effects
|
||||
squares.forEach(square => {
|
||||
|
@ -408,25 +411,42 @@ function handleLevelCompletion(squares) {
|
|||
function handleSquareClick(square, squares) {
|
||||
const index = squares.indexOf(square);
|
||||
if (index > -1) {
|
||||
let scoreChange = 0;
|
||||
let livesChange = 0;
|
||||
let bonusChange = 0;
|
||||
let bonusLife = false;
|
||||
let notificationParts = [];
|
||||
|
||||
// Handle scoring
|
||||
if (square.color === gameConfig.colors.good) {
|
||||
gameState.score += 1;
|
||||
scoreChange = 1;
|
||||
gameState.currentCombo++;
|
||||
showPointGainPopup(1);
|
||||
notificationParts.push(`<span class="score-change">+${scoreChange} Point${scoreChange !== 1 ? 's' : ''}</span>`);
|
||||
} else if (square.color === gameConfig.colors.bonus) {
|
||||
gameState.score += 3;
|
||||
scoreChange = 2;
|
||||
bonusChange = 2;
|
||||
gameState.currentCombo += 2;
|
||||
showPointGainPopup(3);
|
||||
notificationParts.push(`<span class="bonus-change">+${scoreChange} Bonus Point${scoreChange !== 1 ? 's' : ''}</span>`);
|
||||
// Bonus life logic (medium only, if lost a life)
|
||||
const config = gameConfig.difficulties[gameState.selectedDifficulty];
|
||||
if (
|
||||
gameState.selectedDifficulty === 'medium' &&
|
||||
gameState.lives < config.lives &&
|
||||
Math.random() < 0.1 // 10% chance
|
||||
) {
|
||||
gameState.lives += 1;
|
||||
bonusLife = true;
|
||||
notificationParts.push('<span class="lives-change">+1 Bonus Life</span>');
|
||||
}
|
||||
}
|
||||
|
||||
// Handle red squares
|
||||
if (square.color === gameConfig.colors.bad) {
|
||||
const config = gameConfig.difficulties[gameState.selectedDifficulty];
|
||||
if (config.lives !== Infinity) {
|
||||
gameState.lives--;
|
||||
livesChange = -1;
|
||||
gameState.currentCombo = 0;
|
||||
showLifeLossPopup(1);
|
||||
|
||||
notificationParts.push('<span class="lives-change">-1 Life</span>');
|
||||
if (gameState.lives <= 0) {
|
||||
gameState.isPlaying = false;
|
||||
showDeathScreen();
|
||||
|
@ -434,6 +454,15 @@ function handleSquareClick(square, squares) {
|
|||
}
|
||||
}
|
||||
|
||||
// Update game state
|
||||
gameState.score += scoreChange;
|
||||
gameState.lives += livesChange;
|
||||
|
||||
// Create notification message
|
||||
if (notificationParts.length > 0) {
|
||||
showNotification(notificationParts.join(', '));
|
||||
}
|
||||
|
||||
// Create effects
|
||||
for (let i = 0; i < gameConfig.particleCount; i++) {
|
||||
const angle = (Math.PI * 2 * i) / gameConfig.particleCount;
|
||||
|
@ -531,8 +560,106 @@ function initializeGame() {
|
|||
squareCenterY <= bottom;
|
||||
});
|
||||
|
||||
// Handle selected squares
|
||||
selectedSquares.forEach(square => handleSquareClick(square, squares));
|
||||
// Calculate total changes
|
||||
let totalScoreChange = 0;
|
||||
let totalBonusChange = 0;
|
||||
let totalLivesChange = 0;
|
||||
let bonusLife = false;
|
||||
let notificationParts = [];
|
||||
|
||||
// Handle each selected square
|
||||
selectedSquares.forEach(square => {
|
||||
if (square.color === gameConfig.colors.good) {
|
||||
totalScoreChange += 1;
|
||||
gameState.currentCombo++;
|
||||
} else if (square.color === gameConfig.colors.bonus) {
|
||||
totalScoreChange += 2;
|
||||
totalBonusChange += 2;
|
||||
gameState.currentCombo += 2;
|
||||
|
||||
// Bonus life logic (medium only, if lost a life)
|
||||
const config = gameConfig.difficulties[gameState.selectedDifficulty];
|
||||
if (
|
||||
gameState.selectedDifficulty === 'medium' &&
|
||||
gameState.lives < config.lives &&
|
||||
Math.random() < 0.1 // 10% chance
|
||||
) {
|
||||
totalLivesChange += 1;
|
||||
bonusLife = true;
|
||||
}
|
||||
} else if (square.color === gameConfig.colors.bad) {
|
||||
const config = gameConfig.difficulties[gameState.selectedDifficulty];
|
||||
if (config.lives !== Infinity) {
|
||||
totalLivesChange -= 1;
|
||||
gameState.currentCombo = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Create effects
|
||||
for (let i = 0; i < gameConfig.particleCount; i++) {
|
||||
const angle = (Math.PI * 2 * i) / gameConfig.particleCount;
|
||||
const x = square.x + square.size / 2;
|
||||
const y = square.y + square.size / 2;
|
||||
createParticle(x, y, square.color);
|
||||
}
|
||||
|
||||
// Extra sparkles for bonus squares
|
||||
if (square.color === gameConfig.colors.bonus) {
|
||||
for (let i = 0; i < gameConfig.sparkleCount; i++) {
|
||||
const angle = (Math.PI * 2 * i) / gameConfig.sparkleCount;
|
||||
const x = square.x + square.size / 2 + Math.cos(angle) * 20;
|
||||
const y = square.y + square.size / 2 + Math.sin(angle) * 20;
|
||||
createSparkle(x, y);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Build notification message
|
||||
if (totalScoreChange > 0) {
|
||||
notificationParts.push(`<span class="score-change">+${totalScoreChange} Point${totalScoreChange !== 1 ? 's' : ''}</span>`);
|
||||
}
|
||||
if (totalBonusChange > 0) {
|
||||
notificationParts.push(`<span class="bonus-change">+${totalBonusChange} Bonus Point${totalBonusChange !== 1 ? 's' : ''}</span>`);
|
||||
}
|
||||
if (totalLivesChange > 0) {
|
||||
notificationParts.push(`<span class="lives-change">+${totalLivesChange} Bonus Life${totalLivesChange !== 1 ? 's' : ''}</span>`);
|
||||
}
|
||||
if (totalLivesChange < 0) {
|
||||
notificationParts.push(`<span class="lives-change">${totalLivesChange} Life${totalLivesChange !== -1 ? 's' : ''}</span>`);
|
||||
}
|
||||
|
||||
// Show notification if there are any changes
|
||||
if (notificationParts.length > 0) {
|
||||
showNotification(notificationParts.join(', '));
|
||||
}
|
||||
|
||||
// Update game state
|
||||
gameState.score += totalScoreChange;
|
||||
gameState.lives += totalLivesChange;
|
||||
|
||||
// Check for game over
|
||||
if (gameState.lives <= 0) {
|
||||
gameState.isPlaying = false;
|
||||
showDeathScreen();
|
||||
}
|
||||
|
||||
// Remove selected squares
|
||||
selectedSquares.forEach(square => {
|
||||
const index = squares.indexOf(square);
|
||||
if (index > -1) {
|
||||
squares.splice(index, 1);
|
||||
}
|
||||
});
|
||||
|
||||
// Update max combo
|
||||
if (gameState.currentCombo > gameState.maxCombo) {
|
||||
gameState.maxCombo = gameState.currentCombo;
|
||||
}
|
||||
|
||||
// Check for level completion
|
||||
if (isLevelComplete(squares)) {
|
||||
handleLevelCompletion(squares);
|
||||
}
|
||||
|
||||
updateStats();
|
||||
}
|
||||
|
@ -913,3 +1040,14 @@ function showTimeBonusPopup(seconds, breakdown) {
|
|||
timeBonusPopup.remove();
|
||||
}, 2000);
|
||||
}
|
||||
|
||||
// New notification system
|
||||
function showNotification(message, duration = 1500) {
|
||||
const notificationContent = document.querySelector('.notification-content');
|
||||
notificationContent.innerHTML = message;
|
||||
notificationContent.classList.add('show');
|
||||
|
||||
setTimeout(() => {
|
||||
notificationContent.classList.remove('show');
|
||||
}, duration);
|
||||
}
|
|
@ -51,6 +51,9 @@
|
|||
<body>
|
||||
<div class="container">
|
||||
<h1>Zone Out</h1>
|
||||
<div class="notification-banner">
|
||||
<div class="notification-content"></div>
|
||||
</div>
|
||||
<div class="settings-icon">
|
||||
<i class="fas fa-cog"></i>
|
||||
<div class="settings-menu">
|
||||
|
|
45
styles.css
45
styles.css
|
@ -643,3 +643,48 @@ button:hover {
|
|||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.notification-banner {
|
||||
position: fixed;
|
||||
top: 120px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
z-index: 1000;
|
||||
width: 300px;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.notification-content {
|
||||
background: rgba(15, 23, 42, 0.95);
|
||||
backdrop-filter: blur(8px);
|
||||
border: 1px solid rgba(96, 165, 250, 0.2);
|
||||
border-radius: 12px;
|
||||
padding: 12px 20px;
|
||||
color: white;
|
||||
font-size: 1.1rem;
|
||||
text-align: center;
|
||||
opacity: 0;
|
||||
transform: translateY(-20px);
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
|
||||
.notification-content.show {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.notification-content .score-change {
|
||||
color: #4ecca3;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.notification-content .lives-change {
|
||||
color: #ff6b6b;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.notification-content .bonus-change {
|
||||
color: #ffd93d;
|
||||
font-weight: 600;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue