Implement streak bonus system with visual feedback
- Added streak tracking: currentStreak, totalStreakBonuses, lastStreakMilestone - 10 bonus points awarded every 10 consecutive completed regular tasks - Streak resets to 0 when task is skipped (consequence tasks don't affect streak) - Real-time streak display in game stats with fire emoji and milestone highlighting - Animated streak bonus notification with fire icon and gradient background - Final stats display includes best streak and total streak bonus points earned - Enhanced visual feedback with glowing effects for streak milestones (10+) - Auto-save functionality preserves streak data across game sessions
This commit is contained in:
parent
a74fefd1a3
commit
f1f88d5f23
94
game.js
94
game.js
|
|
@ -26,7 +26,10 @@ class TaskChallengeGame {
|
|||
usedConsequenceImages: [], // Track which consequence images have been shown
|
||||
gameMode: 'complete-all', // Game mode: 'complete-all', 'timed', 'score-target'
|
||||
timeLimit: 300, // Time limit in seconds (5 minutes default)
|
||||
scoreTarget: 1000 // Score target (default 1000 points)
|
||||
scoreTarget: 1000, // Score target (default 1000 points)
|
||||
currentStreak: 0, // Track consecutive completed regular tasks
|
||||
totalStreakBonuses: 0, // Track total streak bonus points earned
|
||||
lastStreakMilestone: 0 // Track the last streak milestone reached
|
||||
};
|
||||
|
||||
this.timerInterval = null;
|
||||
|
|
@ -2751,6 +2754,44 @@ ${usagePercent > 85 ? '⚠️ Storage getting full - consider deleting some imag
|
|||
}
|
||||
}
|
||||
|
||||
checkStreakBonus() {
|
||||
// Award streak bonus every 10 completed tasks
|
||||
const streakMilestone = Math.floor(this.gameState.currentStreak / 10);
|
||||
|
||||
if (streakMilestone > this.gameState.lastStreakMilestone) {
|
||||
this.gameState.lastStreakMilestone = streakMilestone;
|
||||
return 10; // 10 bonus points per streak milestone
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
showStreakBonusNotification(streak, bonusPoints) {
|
||||
// Create streak bonus notification
|
||||
const notification = document.createElement('div');
|
||||
notification.className = 'streak-bonus-notification';
|
||||
notification.innerHTML = `
|
||||
<div class="streak-bonus-content">
|
||||
<div class="streak-icon">🔥</div>
|
||||
<div class="streak-text">
|
||||
<div class="streak-title">${streak} Task Streak!</div>
|
||||
<div class="streak-bonus">+${bonusPoints} Bonus Points</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
document.body.appendChild(notification);
|
||||
|
||||
// Animate in
|
||||
setTimeout(() => notification.classList.add('show'), 10);
|
||||
|
||||
// Remove after 3 seconds
|
||||
setTimeout(() => {
|
||||
notification.classList.remove('show');
|
||||
setTimeout(() => document.body.removeChild(notification), 300);
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
completeTask() {
|
||||
if (!this.gameState.isRunning || this.gameState.isPaused) return;
|
||||
|
||||
|
|
@ -2760,14 +2801,26 @@ ${usagePercent > 85 ? '⚠️ Storage getting full - consider deleting some imag
|
|||
this.gameState.consequenceCount++;
|
||||
// Clear the last skipped task when consequence is completed
|
||||
this.gameState.lastSkippedTask = null;
|
||||
// Consequence tasks don't award points
|
||||
// Consequence tasks don't award points or affect streak
|
||||
} else {
|
||||
this.gameState.usedMainTasks.push(this.gameState.currentTask.id);
|
||||
this.gameState.completedCount++;
|
||||
|
||||
// Increment streak for regular tasks
|
||||
this.gameState.currentStreak++;
|
||||
|
||||
// Award points for completing main tasks
|
||||
const difficulty = this.gameState.currentTask.difficulty || 'Medium';
|
||||
const points = this.getPointsForDifficulty(difficulty);
|
||||
this.gameState.score += points;
|
||||
const basePoints = this.getPointsForDifficulty(difficulty);
|
||||
this.gameState.score += basePoints;
|
||||
|
||||
// Check for streak bonus (every 10 consecutive completed tasks)
|
||||
const streakBonus = this.checkStreakBonus();
|
||||
if (streakBonus > 0) {
|
||||
this.gameState.score += streakBonus;
|
||||
this.gameState.totalStreakBonuses += streakBonus;
|
||||
this.showStreakBonusNotification(this.gameState.currentStreak, streakBonus);
|
||||
}
|
||||
|
||||
// Check for score target win condition
|
||||
if (this.gameState.gameMode === 'score-target' && this.gameState.score >= this.gameState.scoreTarget) {
|
||||
|
|
@ -2800,6 +2853,10 @@ ${usagePercent > 85 ? '⚠️ Storage getting full - consider deleting some imag
|
|||
this.gameState.usedMainTasks.push(this.gameState.currentTask.id);
|
||||
this.gameState.skippedCount++;
|
||||
|
||||
// Reset streak when task is skipped
|
||||
this.gameState.currentStreak = 0;
|
||||
this.gameState.lastStreakMilestone = 0;
|
||||
|
||||
// Load a consequence task
|
||||
this.gameState.isConsequenceTask = true;
|
||||
this.loadNextTask();
|
||||
|
|
@ -2861,7 +2918,10 @@ ${usagePercent > 85 ? '⚠️ Storage getting full - consider deleting some imag
|
|||
usedConsequenceImages: [],
|
||||
gameMode: 'complete-all',
|
||||
timeLimit: 300,
|
||||
scoreTarget: 1000
|
||||
scoreTarget: 1000,
|
||||
currentStreak: 0,
|
||||
totalStreakBonuses: 0,
|
||||
lastStreakMilestone: 0
|
||||
};
|
||||
|
||||
this.stopTimer();
|
||||
|
|
@ -2944,6 +3004,20 @@ ${usagePercent > 85 ? '⚠️ Storage getting full - consider deleting some imag
|
|||
document.getElementById('skipped-count').textContent = this.gameState.skippedCount;
|
||||
document.getElementById('consequence-count').textContent = this.gameState.consequenceCount;
|
||||
document.getElementById('score').textContent = this.gameState.score;
|
||||
|
||||
// Update streak display
|
||||
const streakElement = document.getElementById('current-streak');
|
||||
if (streakElement) {
|
||||
streakElement.textContent = this.gameState.currentStreak;
|
||||
|
||||
// Add visual indicator for streak milestones
|
||||
const streakContainer = streakElement.parentElement;
|
||||
if (this.gameState.currentStreak >= 10) {
|
||||
streakContainer.classList.add('streak-milestone');
|
||||
} else {
|
||||
streakContainer.classList.remove('streak-milestone');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
showFinalStats(reason = 'complete-all') {
|
||||
|
|
@ -2977,6 +3051,16 @@ ${usagePercent > 85 ? '⚠️ Storage getting full - consider deleting some imag
|
|||
document.getElementById('final-skipped').textContent = this.gameState.skippedCount;
|
||||
document.getElementById('final-consequences').textContent = this.gameState.consequenceCount;
|
||||
|
||||
// Update streak bonus stats
|
||||
const bestStreakElement = document.getElementById('final-best-streak');
|
||||
const streakBonusesElement = document.getElementById('final-streak-bonuses');
|
||||
if (bestStreakElement) {
|
||||
bestStreakElement.textContent = this.gameState.currentStreak;
|
||||
}
|
||||
if (streakBonusesElement) {
|
||||
streakBonusesElement.textContent = this.gameState.totalStreakBonuses;
|
||||
}
|
||||
|
||||
// Add game mode info to stats
|
||||
let gameModeText = '';
|
||||
switch (this.gameState.gameMode) {
|
||||
|
|
|
|||
|
|
@ -378,6 +378,10 @@
|
|||
<span class="stat-label">Completed:</span>
|
||||
<span id="completed-count" class="stat-value">0</span>
|
||||
</div>
|
||||
<div class="stat streak-stat">
|
||||
<span class="stat-label">Streak:</span>
|
||||
<span id="current-streak" class="stat-value">0</span>🔥
|
||||
</div>
|
||||
<div class="stat">
|
||||
<span class="stat-label">Skipped:</span>
|
||||
<span id="skipped-count" class="stat-value">0</span>
|
||||
|
|
@ -408,6 +412,8 @@
|
|||
<p>Tasks Completed: <span id="final-completed"></span></p>
|
||||
<p>Tasks Skipped: <span id="final-skipped"></span></p>
|
||||
<p>Consequence Tasks: <span id="final-consequences"></span></p>
|
||||
<p>Best Streak: <span id="final-best-streak"></span> 🔥</p>
|
||||
<p>Streak Bonus Points: <span id="final-streak-bonuses"></span></p>
|
||||
</div>
|
||||
<button id="play-again-btn" class="btn btn-primary">Play Again</button>
|
||||
</div>
|
||||
|
|
|
|||
71
styles.css
71
styles.css
|
|
@ -378,6 +378,77 @@ body {
|
|||
border-color: var(--primary-color);
|
||||
background: rgba(var(--primary-color-rgb), 0.1);
|
||||
}
|
||||
|
||||
/* Streak Bonus Notification */
|
||||
.streak-bonus-notification {
|
||||
position: fixed;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%) scale(0.8);
|
||||
opacity: 0;
|
||||
background: linear-gradient(135deg, #ff6b35, #f7931e);
|
||||
color: white;
|
||||
padding: 20px;
|
||||
border-radius: 15px;
|
||||
text-align: center;
|
||||
box-shadow: 0 10px 30px rgba(255, 107, 53, 0.4);
|
||||
z-index: 1002;
|
||||
transition: all 0.3s ease;
|
||||
backdrop-filter: blur(10px);
|
||||
border: 2px solid rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
.streak-bonus-notification.show {
|
||||
transform: translate(-50%, -50%) scale(1);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.streak-bonus-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.streak-icon {
|
||||
font-size: 2.5em;
|
||||
animation: fireFlicker 1s ease-in-out infinite alternate;
|
||||
}
|
||||
|
||||
.streak-text {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.streak-title {
|
||||
font-size: 1.2em;
|
||||
font-weight: bold;
|
||||
margin-bottom: 5px;
|
||||
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.streak-bonus {
|
||||
font-size: 1em;
|
||||
opacity: 0.9;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
@keyframes fireFlicker {
|
||||
0% { transform: rotate(-2deg) scale(1); }
|
||||
100% { transform: rotate(2deg) scale(1.05); }
|
||||
}
|
||||
|
||||
/* Streak Milestone Styling */
|
||||
.streak-stat.streak-milestone {
|
||||
background: linear-gradient(45deg, rgba(255, 107, 53, 0.1), rgba(247, 147, 30, 0.1));
|
||||
border-radius: 8px;
|
||||
padding: 5px 8px;
|
||||
border: 1px solid rgba(255, 107, 53, 0.3);
|
||||
animation: streakGlow 2s ease-in-out infinite alternate;
|
||||
}
|
||||
|
||||
@keyframes streakGlow {
|
||||
0% { box-shadow: 0 0 5px rgba(255, 107, 53, 0.3); }
|
||||
100% { box-shadow: 0 0 15px rgba(255, 107, 53, 0.5); }
|
||||
}
|
||||
.audio-bar:nth-child(4) { animation-delay: 0.6s; }
|
||||
.audio-bar:nth-child(5) { animation-delay: 0.8s; }
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue