Fix game freeze with no images loaded

Critical fix for desktop startup:
- Fixed startGame() function to properly handle new customImages object format
- Previously trying to call .length on object instead of array
- Added proper backward compatibility checking for both old array and new object formats
- Added comprehensive logging to debug image management screen freeze
- Now correctly counts custom images from both task and consequence categories

Fixes error where:
- User clicks Start Game with no images
- Game redirects to image management screen but freezes
- TypeError: customImages.length when customImages is {task: [], consequence: []}

Error resolved with proper object structure handling and logging.
This commit is contained in:
fritzsenpai 2025-09-25 21:03:12 -05:00
parent 85ea7f8938
commit aa3b7d8a1c
5 changed files with 169 additions and 54 deletions

65
game.js
View File

@ -312,6 +312,9 @@ class TaskChallengeGame {
// Theme selector
document.getElementById('theme-dropdown').addEventListener('change', (e) => this.changeTheme(e.target.value));
// Options menu toggle
document.getElementById('options-menu-btn').addEventListener('click', () => this.toggleOptionsMenu());
// Music controls
document.getElementById('music-toggle').addEventListener('click', () => this.toggleMusic());
document.getElementById('loop-btn').addEventListener('click', () => this.toggleLoop());
@ -711,29 +714,23 @@ class TaskChallengeGame {
}
updateStartScreenStatus() {
// Status message removed for cleaner home screen interface
const statusDiv = document.getElementById('start-screen-status');
if (!statusDiv) return;
if (statusDiv) {
statusDiv.style.display = 'none';
}
}
toggleOptionsMenu() {
const optionsMenu = document.getElementById('options-menu');
const button = document.getElementById('options-menu-btn');
const totalImages = gameData.discoveredTaskImages.length + gameData.discoveredConsequenceImages.length;
const customImages = this.dataManager.get('customImages') || [];
const totalAvailable = totalImages + customImages.length;
if (totalAvailable === 0) {
statusDiv.innerHTML = `
<div class="status-warning">
No images found!
<br>Go to "Manage Images" to upload images or scan directories.
</div>
`;
statusDiv.style.display = 'block';
if (optionsMenu.style.display === 'none' || optionsMenu.style.display === '') {
optionsMenu.style.display = 'block';
button.textContent = '⚙️ Close Options';
} else {
statusDiv.innerHTML = `
<div class="status-success">
Ready to play! Found ${totalAvailable} images
(${totalImages} from directories, ${customImages.length} uploaded)
</div>
`;
statusDiv.style.display = 'block';
optionsMenu.style.display = 'none';
button.textContent = '⚙️ Options';
}
}
@ -852,26 +849,35 @@ class TaskChallengeGame {
// Image Management Methods
showImageManagement() {
console.log('showImageManagement() called');
this.showScreen('image-management-screen');
console.log('Screen switched to image-management-screen');
this.setupImageManagementEventListeners();
console.log('Event listeners set up');
// Wait for image discovery to complete before loading gallery
if (!this.imageDiscoveryComplete) {
console.log('Image discovery not complete, waiting...');
const gallery = document.getElementById('image-gallery');
gallery.innerHTML = '<div class="loading">Discovering images...</div>';
// Wait and try again
setTimeout(() => {
console.log('Timeout reached, checking discovery status...');
if (this.imageDiscoveryComplete) {
console.log('Discovery complete, loading gallery...');
this.loadImageGallery();
} else {
console.log('Discovery still not complete, waiting longer...');
gallery.innerHTML = '<div class="loading">Still discovering images... Please wait</div>';
setTimeout(() => this.loadImageGallery(), 1000);
}
}, 500);
} else {
console.log('Image discovery already complete, loading gallery...');
this.loadImageGallery();
}
console.log('showImageManagement() completed');
}
switchImageTab(tabType) {
@ -1024,14 +1030,21 @@ class TaskChallengeGame {
}
loadImageGallery() {
console.log('loadImageGallery() called');
// Load both task and consequence image galleries
console.log('Cleaning up invalid images...');
this.cleanupInvalidImages(); // Clean up invalid images first
console.log('Loading task images...');
this.loadTaskImages();
console.log('Loading consequence images...');
this.loadConsequenceImages();
console.log('Updating image counts...');
this.updateImageCounts();
// Initialize with task tab active and update controls
console.log('Updating gallery controls for task tab...');
this.updateImageGalleryControls('task');
console.log('loadImageGallery() completed');
}
loadTaskImages() {
@ -1831,9 +1844,17 @@ ${usagePercent > 85 ? '⚠️ Storage getting full - consider deleting some imag
// Check if we have any images available
const totalImages = gameData.discoveredTaskImages.length + gameData.discoveredConsequenceImages.length;
const customImages = this.dataManager.get('customImages') || [];
const customImages = this.dataManager.get('customImages') || { task: [], consequence: [] };
if (totalImages === 0 && customImages.length === 0) {
// Handle both old array format and new object format for backward compatibility
let customImageCount = 0;
if (Array.isArray(customImages)) {
customImageCount = customImages.length;
} else {
customImageCount = (customImages.task || []).length + (customImages.consequence || []).length;
}
if (totalImages === 0 && customImageCount === 0) {
// No images available - guide user to add images
this.showNotification('No images found! Please upload images or scan directories first.', 'error', 5000);
this.showScreen('image-management-screen');

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1020 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

View File

@ -43,42 +43,52 @@
<h2>Ready to Start?</h2>
<p>Complete tasks, but beware of skipping - there are consequences!</p>
<!-- Status Message -->
<div id="start-screen-status" class="status-message" style="display: none;"></div>
<!-- Theme Selector -->
<div class="theme-selector">
<label for="theme-dropdown" class="theme-label">Choose Your Vibe:</label>
<select id="theme-dropdown" class="theme-dropdown">
<option value="ocean">🌊 Ocean Blue</option>
<option value="sunset">🌅 Warm Sunset</option>
<option value="forest">🌲 Nature Green</option>
<option value="midnight">🌙 Midnight Dark</option>
<option value="pastel">🌸 Soft Pastel</option>
<option value="neon">⚡ Neon Cyber</option>
<option value="autumn">🍂 Autumn Vibes</option>
<option value="monochrome">⚫ Monochrome</option>
</select>
<!-- Main Action Buttons -->
<div class="main-actions">
<button id="start-btn" class="btn btn-primary">Start Game</button>
<button id="manage-tasks-btn" class="btn btn-secondary">Manage Tasks</button>
<button id="manage-images-btn" class="btn btn-secondary">Manage Images</button>
</div>
<!-- Data Management -->
<div class="data-controls">
<button id="export-btn" class="btn btn-secondary" title="Export Save File">
<span class="btn-text">💾 Export</span>
<span class="btn-loading" style="display: none;">⏳ Exporting...</span>
</button>
<button id="import-btn" class="btn btn-secondary" title="Import Save File">
<span class="btn-text">📁 Import</span>
<span class="btn-loading" style="display: none;">⏳ Importing...</span>
</button>
<!-- Options Menu -->
<div class="options-section">
<button id="options-menu-btn" class="btn btn-tertiary">⚙️ Options</button>
<div id="options-menu" class="options-menu" style="display: none;">
<!-- Theme Selector -->
<div class="option-item theme-selector">
<label for="theme-dropdown" class="option-label">Choose Your Vibe:</label>
<select id="theme-dropdown" class="theme-dropdown">
<option value="ocean">🌊 Ocean Blue</option>
<option value="sunset">🌅 Warm Sunset</option>
<option value="forest">🌲 Nature Green</option>
<option value="midnight">🌙 Midnight Dark</option>
<option value="pastel">🌸 Soft Pastel</option>
<option value="neon">⚡ Neon Cyber</option>
<option value="autumn">🍂 Autumn Vibes</option>
<option value="monochrome">⚫ Monochrome</option>
</select>
</div>
<!-- Data Management -->
<div class="option-item data-controls">
<button id="import-btn" class="btn btn-option" title="Import Save File">
<span class="btn-text"><EFBFBD> Import</span>
<span class="btn-loading" style="display: none;">⏳ Importing...</span>
</button>
<button id="export-btn" class="btn btn-option" title="Export Save File">
<span class="btn-text"><EFBFBD> Export</span>
<span class="btn-loading" style="display: none;">⏳ Exporting...</span>
</button>
</div>
<!-- Other Options -->
<div class="option-item other-controls">
<button id="stats-btn" class="btn btn-option" title="View Statistics">📊 Stats</button>
<button id="help-btn" class="btn btn-option" title="Keyboard Shortcuts & Help">❓ Help</button>
</div>
</div>
<input type="file" id="import-file" accept=".json" style="display: none;">
<button id="stats-btn" class="btn btn-secondary" title="View Statistics">📊 Stats</button>
<button id="help-btn" class="btn btn-secondary" title="Keyboard Shortcuts & Help">❓ Help</button>
</div>
<button id="start-btn" class="btn btn-primary">Start Game</button>
<button id="manage-tasks-btn" class="btn btn-secondary">Manage Tasks</button>
<button id="manage-images-btn" class="btn btn-secondary">Manage Images</button>
</div>
<!-- Task Management Screen -->

View File

@ -865,6 +865,90 @@ body {
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
/* Options Menu Styles */
.options-section {
margin-top: 20px;
position: relative;
}
.btn-tertiary {
background: #6c757d;
color: white;
margin-bottom: 10px;
}
.btn-tertiary:hover {
background: #545b62;
}
.options-menu {
background: #f8f9fa;
border: 1px solid #dee2e6;
border-radius: 8px;
padding: 20px;
margin-top: 10px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}
.option-item {
margin-bottom: 15px;
padding-bottom: 15px;
border-bottom: 1px solid #e9ecef;
}
.option-item:last-child {
margin-bottom: 0;
padding-bottom: 0;
border-bottom: none;
}
.option-label {
display: block;
font-weight: 600;
margin-bottom: 8px;
color: #495057;
}
.theme-dropdown {
width: 100%;
padding: 8px 12px;
border: 1px solid #ced4da;
border-radius: 4px;
font-size: 0.95em;
}
.data-controls, .other-controls {
display: flex;
gap: 10px;
flex-wrap: wrap;
}
.btn-option {
background: #e9ecef;
color: #495057;
padding: 8px 16px;
font-size: 0.9em;
min-width: auto;
}
.btn-option:hover {
background: #dee2e6;
transform: translateY(-1px);
}
.main-actions {
display: flex;
flex-direction: column;
gap: 15px;
margin-bottom: 20px;
}
.main-actions .btn {
width: 100%;
max-width: 300px;
margin: 0 auto;
}
.mercy-cost {
display: block;
font-size: 0.8em;