Implement 3 Game Modes: Complete All, Timed Challenge, and Score Target

Complete game mode system with dynamic UI and win conditions:

##  Three Game Modes:

### 1. Complete All Tasks (Default)
- Finish every main task in the game
- Original gameplay experience
- Game ends when all tasks completed

### 2. Timed Challenge
- Race against the clock to complete as many tasks as possible
- Configurable time limits: 5, 10, 15, 20, or 30 minutes
- Countdown timer with color warnings (red < 30s, orange < 60s)
- Game ends when time runs out

### 3. Score Target
- Reach a target score to win
- Configurable targets: 500, 1000, 1500, 2000, or 3000 points
- Game ends immediately when target reached
- Strategic gameplay focusing on high-value tasks

##  Enhanced UI Features:

### Game Mode Selection:
- Beautiful radio button interface on start screen
- Mode-specific configuration options (time/score targets)
- Dynamic show/hide of relevant settings
- Visual feedback with hover effects and selection states

### Smart Timer Display:
- Elapsed time for Complete All and Score Target modes
- Countdown timer for Timed Challenge mode
- Color-coded time warnings (red/orange when running low)
- Dynamic timer status indicators

### Dynamic Game Over Screen:
- Mode-specific victory messages and titles
- Shows selected game mode in final stats
- Different celebration text based on completion reason
- Comprehensive final statistics display

##  Technical Implementation:

### Game State Enhancement:
- Added gameMode, timeLimit, scoreTarget to gameState
- Proper game state reset handling for all modes
- Mode persistence and configuration management

### Win Condition Logic:
- Score target checking on task completion
- Timer countdown with automatic game end
- Complete-all detection (existing functionality preserved)
- Multiple end game reasons ('time', 'score-target', 'complete-all')

### Event System:
- Game mode selection event listeners
- Dynamic configuration updates
- Real-time UI state management
- Mode-specific timer updates

**Ready to challenge yourself in 3 different ways! **
This commit is contained in:
fritzsenpai 2025-09-25 22:00:44 -05:00
parent 927c19c45c
commit cc853ad667
7 changed files with 271 additions and 6 deletions

Binary file not shown.

135
game.js
View File

@ -23,7 +23,10 @@ class TaskChallengeGame {
usedMainTasks: [],
usedConsequenceTasks: [],
usedTaskImages: [], // Track which task images have been shown
usedConsequenceImages: [] // Track which consequence images have been shown
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)
};
this.timerInterval = null;
@ -332,6 +335,9 @@ class TaskChallengeGame {
document.getElementById('quit-btn').addEventListener('click', () => this.quitGame());
document.getElementById('play-again-btn').addEventListener('click', () => this.resetGame());
// Game mode selection
this.initializeGameModeListeners();
// Game actions
document.getElementById('complete-btn').addEventListener('click', () => this.completeTask());
document.getElementById('skip-btn').addEventListener('click', () => this.skipTask());
@ -381,6 +387,36 @@ class TaskChallengeGame {
this.loadSavedTheme();
}
initializeGameModeListeners() {
const gameModeRadios = document.querySelectorAll('input[name="gameMode"]');
gameModeRadios.forEach(radio => {
radio.addEventListener('change', () => this.handleGameModeChange());
});
// Initialize with default mode
this.handleGameModeChange();
}
handleGameModeChange() {
const selectedMode = document.querySelector('input[name="gameMode"]:checked').value;
this.gameState.gameMode = selectedMode;
// Show/hide configuration options based on selected mode
document.getElementById('timed-config').style.display =
selectedMode === 'timed' ? 'block' : 'none';
document.getElementById('score-target-config').style.display =
selectedMode === 'score-target' ? 'block' : 'none';
// Update game state with selected values
if (selectedMode === 'timed') {
this.gameState.timeLimit = parseInt(document.getElementById('time-limit-select').value);
} else if (selectedMode === 'score-target') {
this.gameState.scoreTarget = parseInt(document.getElementById('score-target-select').value);
}
console.log(`Game mode changed to: ${selectedMode}`, this.gameState);
}
setupKeyboardShortcuts() {
document.addEventListener('keydown', (e) => {
// Don't trigger shortcuts when typing in inputs or textareas
@ -2607,6 +2643,13 @@ ${usagePercent > 85 ? '⚠️ Storage getting full - consider deleting some imag
const difficulty = this.gameState.currentTask.difficulty || 'Medium';
const points = this.getPointsForDifficulty(difficulty);
this.gameState.score += points;
// Check for score target win condition
if (this.gameState.gameMode === 'score-target' && this.gameState.score >= this.gameState.scoreTarget) {
this.updateStats(); // Update stats before ending game
this.endGame('score-target');
return;
}
}
// Load next main task (consequence tasks don't chain)
@ -2666,10 +2709,10 @@ ${usagePercent > 85 ? '⚠️ Storage getting full - consider deleting some imag
this.endGame();
}
endGame() {
endGame(reason = 'complete-all') {
this.gameState.isRunning = false;
this.stopTimer();
this.showFinalStats();
this.showFinalStats(reason);
this.showScreen('game-over-screen');
}
@ -2685,8 +2728,15 @@ ${usagePercent > 85 ? '⚠️ Storage getting full - consider deleting some imag
completedCount: 0,
skippedCount: 0,
consequenceCount: 0,
score: 0,
lastSkippedTask: null,
usedMainTasks: [],
usedConsequenceTasks: []
usedConsequenceTasks: [],
usedTaskImages: [],
usedConsequenceImages: [],
gameMode: 'complete-all',
timeLimit: 300,
scoreTarget: 1000
};
this.stopTimer();
@ -2711,7 +2761,37 @@ ${usagePercent > 85 ? '⚠️ Storage getting full - consider deleting some imag
const currentTime = Date.now();
const elapsed = currentTime - this.gameState.startTime - this.gameState.totalPausedTime;
const formattedTime = this.formatTime(elapsed);
let formattedTime;
if (this.gameState.gameMode === 'timed') {
// Countdown timer for timed mode
const remainingMs = (this.gameState.timeLimit * 1000) - elapsed;
if (remainingMs <= 0) {
// Time's up!
formattedTime = '00:00';
document.getElementById('timer').textContent = formattedTime;
this.endGame('time');
return;
}
formattedTime = this.formatTime(remainingMs);
// Change color when time is running low (less than 30 seconds)
const timerElement = document.getElementById('timer');
if (remainingMs <= 30000) {
timerElement.style.color = '#ff4757';
timerElement.style.fontWeight = 'bold';
} else if (remainingMs <= 60000) {
timerElement.style.color = '#ffa502';
timerElement.style.fontWeight = 'bold';
} else {
timerElement.style.color = '';
timerElement.style.fontWeight = '';
}
} else {
// Normal elapsed timer for other modes
formattedTime = this.formatTime(elapsed);
}
document.getElementById('timer').textContent = formattedTime;
@ -2719,6 +2799,8 @@ ${usagePercent > 85 ? '⚠️ Storage getting full - consider deleting some imag
const timerStatus = document.getElementById('timer-status');
if (this.gameState.isPaused) {
timerStatus.textContent = '(PAUSED)';
} else if (this.gameState.gameMode === 'timed') {
timerStatus.textContent = '(TIME LEFT)';
} else {
timerStatus.textContent = '';
}
@ -2739,16 +2821,57 @@ ${usagePercent > 85 ? '⚠️ Storage getting full - consider deleting some imag
document.getElementById('score').textContent = this.gameState.score;
}
showFinalStats() {
showFinalStats(reason = 'complete-all') {
const currentTime = Date.now();
const totalTime = currentTime - this.gameState.startTime - this.gameState.totalPausedTime;
const formattedTime = this.formatTime(totalTime);
// Update the game over title based on end reason
const gameOverTitle = document.querySelector('#game-over-screen h2');
const gameOverMessage = document.querySelector('#game-over-screen p');
switch (reason) {
case 'time':
gameOverTitle.textContent = '⏰ Time\'s Up!';
gameOverMessage.textContent = 'You ran out of time! See how many tasks you completed.';
break;
case 'score-target':
gameOverTitle.textContent = '🏆 Target Reached!';
gameOverMessage.textContent = `Congratulations! You reached the target score of ${this.gameState.scoreTarget} points!`;
break;
case 'complete-all':
default:
gameOverTitle.textContent = '🎉 All Tasks Complete!';
gameOverMessage.textContent = 'Congratulations! You\'ve completed all available tasks!';
break;
}
document.getElementById('final-score').textContent = this.gameState.score;
document.getElementById('final-time').textContent = formattedTime;
document.getElementById('final-completed').textContent = this.gameState.completedCount;
document.getElementById('final-skipped').textContent = this.gameState.skippedCount;
document.getElementById('final-consequences').textContent = this.gameState.consequenceCount;
// Add game mode info to stats
let gameModeText = '';
switch (this.gameState.gameMode) {
case 'timed':
gameModeText = `Timed Challenge (${this.gameState.timeLimit / 60} minutes)`;
break;
case 'score-target':
gameModeText = `Score Target (${this.gameState.scoreTarget} points)`;
break;
case 'complete-all':
default:
gameModeText = 'Complete All Tasks';
break;
}
// Show game mode info if element exists
const gameModeElement = document.getElementById('final-game-mode');
if (gameModeElement) {
gameModeElement.textContent = gameModeText;
}
}
}

View File

@ -51,6 +51,56 @@
<button id="manage-audio-btn" class="btn btn-secondary">🎵 Manage Audio</button>
</div>
<!-- Game Mode Selection -->
<div class="game-mode-selection">
<h3>Game Mode</h3>
<div class="game-mode-options">
<div class="game-mode-option">
<input type="radio" id="mode-complete-all" name="gameMode" value="complete-all" checked>
<label for="mode-complete-all">
<strong>🎯 Complete All Tasks</strong>
<p>Finish every task in the game</p>
</label>
</div>
<div class="game-mode-option">
<input type="radio" id="mode-timed" name="gameMode" value="timed">
<label for="mode-timed">
<strong>⏱️ Timed Challenge</strong>
<p>Complete as many tasks as possible within the time limit</p>
<div class="mode-config" id="timed-config" style="display: none;">
<label>Time Limit:
<select id="time-limit-select">
<option value="300">5 minutes</option>
<option value="600">10 minutes</option>
<option value="900">15 minutes</option>
<option value="1200">20 minutes</option>
<option value="1800">30 minutes</option>
</select>
</label>
</div>
</label>
</div>
<div class="game-mode-option">
<input type="radio" id="mode-score-target" name="gameMode" value="score-target">
<label for="mode-score-target">
<strong>🏆 Score Target</strong>
<p>Reach the target score to win</p>
<div class="mode-config" id="score-target-config" style="display: none;">
<label>Target Score:
<select id="score-target-select">
<option value="500">500 points</option>
<option value="1000">1000 points</option>
<option value="1500">1500 points</option>
<option value="2000">2000 points</option>
<option value="3000">3000 points</option>
</select>
</label>
</div>
</label>
</div>
</div>
</div>
<!-- Options Menu -->
<div class="options-section">
<button id="options-menu-btn" class="btn btn-tertiary">⚙️ Options</button>
@ -328,7 +378,9 @@
<!-- Game Over Screen -->
<div id="game-over-screen" class="screen">
<h2>Game Complete!</h2>
<p>Congratulations! You've completed all available tasks!</p>
<div id="final-stats" class="final-stats">
<p><strong>Game Mode:</strong> <span id="final-game-mode"></span></p>
<p>Final Score: <span id="final-score"></span> points</p>
<p>Final Time: <span id="final-time"></span></p>
<p>Tasks Completed: <span id="final-completed"></span></p>

View File

@ -949,6 +949,96 @@ body {
margin: 0 auto;
}
/* Game Mode Selection */
.game-mode-selection {
background: #f8f9fa;
border-radius: 10px;
padding: 20px;
margin: 20px 0;
border: 2px solid #e0e0e0;
}
.game-mode-selection h3 {
color: #333;
margin-bottom: 15px;
text-align: center;
font-size: 1.2em;
}
.game-mode-options {
display: flex;
flex-direction: column;
gap: 15px;
}
.game-mode-option {
position: relative;
}
.game-mode-option input[type="radio"] {
position: absolute;
opacity: 0;
cursor: pointer;
}
.game-mode-option label {
display: block;
background: white;
border: 2px solid #ddd;
border-radius: 8px;
padding: 15px;
cursor: pointer;
transition: all 0.3s ease;
position: relative;
}
.game-mode-option label:hover {
border-color: #4facfe;
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(79, 172, 254, 0.2);
}
.game-mode-option input[type="radio"]:checked + label {
border-color: #4facfe;
background: linear-gradient(135deg, #4facfe22, #00f2fe11);
box-shadow: 0 4px 12px rgba(79, 172, 254, 0.3);
}
.game-mode-option label strong {
color: #333;
font-size: 1.1em;
display: block;
margin-bottom: 5px;
}
.game-mode-option label p {
color: #666;
font-size: 0.9em;
margin: 0;
line-height: 1.4;
}
.mode-config {
margin-top: 15px;
padding-top: 15px;
border-top: 1px solid #eee;
}
.mode-config label {
font-size: 0.9em;
color: #555;
font-weight: 500;
}
.mode-config select {
margin-left: 10px;
padding: 5px 10px;
border: 1px solid #ddd;
border-radius: 4px;
background: white;
font-size: 0.9em;
}
.mercy-cost {
display: block;
font-size: 0.8em;