training-academy/training-academy.html

3474 lines
159 KiB
HTML
Raw Blame History

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Gooner Training Academy</title>
<!-- Core Styles -->
<link rel="stylesheet" href="src/styles/styles.css">
<link rel="stylesheet" href="src/styles/styles-dark-edgy.css">
<link rel="stylesheet" href="src/styles/base-video-player.css">
<style>
/* Training Academy Specific Styles */
.academy-header {
text-align: center;
background: linear-gradient(135deg, #1a1a1a, #2d1b3d);
padding: 2rem;
margin: 1rem;
border-radius: 15px;
border: 2px solid #9b59b6;
box-shadow: 0 8px 32px rgba(155, 89, 182, 0.3);
}
.academy-header h1 {
color: #9b59b6;
font-size: 2.5rem;
margin: 0;
text-shadow: 0 0 20px rgba(155, 89, 182, 0.5);
}
.academy-header .subtitle {
color: #e8e8e8;
font-size: 1.2rem;
margin-top: 0.5rem;
font-style: italic;
}
.training-controls {
background: rgba(155, 89, 182, 0.1);
border: 1px solid #9b59b6;
border-radius: 10px;
padding: 1.5rem;
margin: 1rem;
}
.library-status {
background: rgba(52, 152, 219, 0.1);
border: 1px solid #3498db;
border-radius: 8px;
padding: 1rem;
margin: 1rem 0;
font-family: 'Courier New', monospace;
}
.library-status h3 {
color: #3498db;
margin: 0 0 0.5rem 0;
}
.academy-mode-selection {
display: flex;
flex-wrap: wrap;
justify-content: center;
align-items: center;
gap: 1rem;
margin: 1rem auto;
max-width: 1000px;
}
.training-mode-card {
background: linear-gradient(135deg, #2c3e50, #34495e);
border: 2px solid #3498db;
border-radius: 8px;
padding: 0.8rem;
cursor: pointer;
transition: all 0.3s ease;
text-align: center;
margin: 0.5rem;
width: 200px;
flex-shrink: 0;
}
.training-mode-card:hover {
border-color: #e74c3c;
box-shadow: 0 4px 15px rgba(231, 76, 60, 0.3);
transform: translateY(-1px);
}
.training-mode-card.selected {
border-color: #e74c3c;
background: linear-gradient(135deg, #c0392b, #e74c3c);
box-shadow: 0 4px 15px rgba(231, 76, 60, 0.4);
}
.mode-icon {
font-size: 1.8rem;
margin-bottom: 0.4rem;
}
.mode-name {
font-size: 1rem;
font-weight: bold;
color: #ecf0f1;
margin-bottom: 0.2rem;
}
.mode-description {
color: #bdc3c7;
font-size: 0.75rem;
line-height: 1.2;
}
.academy-start-controls {
text-align: center;
margin: 1rem;
}
.start-training-btn {
background: linear-gradient(135deg, #e74c3c, #c0392b);
color: white;
border: none;
border-radius: 25px;
padding: 1rem 2rem;
font-size: 1.2rem;
font-weight: bold;
cursor: pointer;
transition: all 0.3s ease;
box-shadow: 0 4px 15px rgba(231, 76, 60, 0.3);
}
.start-training-btn:hover:not(:disabled) {
background: linear-gradient(135deg, #c0392b, #a93226);
box-shadow: 0 6px 20px rgba(231, 76, 60, 0.4);
transform: translateY(-2px);
}
.start-training-btn:disabled {
background: #7f8c8d;
cursor: not-allowed;
box-shadow: none;
}
.back-navigation {
position: fixed;
top: 20px;
left: 20px;
z-index: 1000;
}
.back-btn {
background: rgba(52, 73, 94, 0.9);
color: #ecf0f1;
border: 1px solid #34495e;
border-radius: 8px;
padding: 0.5rem 1rem;
text-decoration: none;
font-weight: bold;
transition: all 0.3s ease;
}
.back-btn:hover {
background: rgba(52, 73, 94, 1);
border-color: #3498db;
}
/* Mirror Task Action Buttons */
.action-btn {
background: linear-gradient(135deg, #27ae60, #2ecc71);
color: white;
border: none;
border-radius: 8px;
padding: 0.75rem 1.5rem;
font-size: 1rem;
font-weight: bold;
cursor: pointer;
transition: all 0.3s ease;
box-shadow: 0 4px 15px rgba(39, 174, 96, 0.3);
margin: 0.5rem;
}
.action-btn:hover {
background: linear-gradient(135deg, #219a52, #27ae60);
box-shadow: 0 6px 20px rgba(39, 174, 96, 0.4);
transform: translateY(-2px);
}
.skip-btn {
background: linear-gradient(135deg, #95a5a6, #bdc3c7);
color: #2c3e50;
border: none;
border-radius: 8px;
padding: 0.75rem 1.5rem;
font-size: 1rem;
font-weight: bold;
cursor: pointer;
transition: all 0.3s ease;
box-shadow: 0 4px 15px rgba(149, 165, 166, 0.3);
margin: 0.5rem;
}
.skip-btn:hover {
background: linear-gradient(135deg, #7f8c8d, #95a5a6);
box-shadow: 0 6px 20px rgba(149, 165, 166, 0.4);
transform: translateY(-2px);
}
.next-btn {
background: linear-gradient(135deg, #3498db, #5dade2);
color: white;
border: none;
border-radius: 8px;
padding: 0.75rem 1.5rem;
font-size: 1rem;
font-weight: bold;
cursor: pointer;
transition: all 0.3s ease;
box-shadow: 0 4px 15px rgba(52, 152, 219, 0.3);
margin: 0.5rem;
}
.next-btn:hover {
background: linear-gradient(135deg, #2980b9, #3498db);
box-shadow: 0 6px 20px rgba(52, 152, 219, 0.4);
transform: translateY(-2px);
}
/* Video player container for background videos */
.training-video-container {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: -1;
background: #000;
overflow: hidden;
}
.training-video-container video {
width: 100%;
height: 100%;
object-fit: cover;
opacity: 0.3;
}
/* Ensure main content is above video */
.main-content {
position: relative;
z-index: 1;
background: rgba(0, 0, 0, 0.7);
min-height: 100vh;
}
/* Video controls overlay */
.video-controls-overlay {
position: fixed;
bottom: 20px;
right: 20px;
background: rgba(0, 0, 0, 0.8);
border-radius: 10px;
padding: 10px;
z-index: 1000;
display: none; /* Hidden until video loads */
}
.video-controls-overlay button {
background: #3498db;
color: white;
border: none;
border-radius: 5px;
padding: 5px 10px;
margin: 2px;
cursor: pointer;
font-size: 0.8rem;
}
.video-controls-overlay button:hover {
background: #2980b9;
}
/* Training Academy Specific Styles */
.training-status, .training-task {
background: rgba(20, 20, 20, 0.95);
border: 2px solid #ff006e;
border-radius: 15px;
padding: 25px;
margin: 20px;
text-align: center;
backdrop-filter: blur(10px);
}
.training-task {
text-align: left;
max-width: 800px;
margin: 20px auto;
}
.training-task h3 {
color: #ff006e;
margin-bottom: 15px;
text-align: center;
}
.task-content {
background: rgba(0, 0, 0, 0.7);
padding: 20px;
border-radius: 10px;
margin: 20px 0;
border-left: 4px solid #8338ec;
font-size: 1.1em;
line-height: 1.5;
}
.task-story {
background: rgba(58, 134, 255, 0.2);
padding: 15px;
border-radius: 10px;
margin: 15px 0;
border-left: 4px solid #3a86ff;
font-size: 1em;
line-height: 1.4;
}
.task-story p {
margin: 0;
color: #e0e0e0;
}
.training-controls {
display: flex;
gap: 15px;
justify-content: center;
margin-top: 25px;
}
.complete-btn, .next-btn {
background: linear-gradient(45deg, #ff006e, #8338ec);
color: white;
border: none;
padding: 12px 25px;
border-radius: 25px;
cursor: pointer;
font-weight: bold;
font-size: 1em;
transition: all 0.3s ease;
text-transform: uppercase;
letter-spacing: 1px;
}
.complete-btn:hover, .next-btn:hover {
transform: translateY(-2px);
box-shadow: 0 8px 20px rgba(255, 0, 110, 0.4);
}
.next-btn {
background: linear-gradient(45deg, #8338ec, #3a86ff);
}
.next-btn:hover {
box-shadow: 0 8px 20px rgba(131, 56, 236, 0.4);
}
.interactive-notice {
background: linear-gradient(45deg, rgba(131, 56, 236, 0.2), rgba(58, 134, 255, 0.2));
border: 1px solid #8338ec;
border-radius: 10px;
padding: 15px;
margin: 15px 0;
text-align: center;
}
.interactive-notice p {
margin: 0;
color: #8338ec;
font-weight: bold;
}
/* Scenario Adventure Styles */
.scenario-choice, .scenario-action, .scenario-ending, .scenario-action-progress {
background: rgba(20, 20, 20, 0.95);
border: 2px solid #ff006e;
border-radius: 15px;
padding: 25px;
margin: 20px auto;
max-width: 900px;
backdrop-filter: blur(10px);
}
.scenario-story {
background: rgba(0, 0, 0, 0.8);
padding: 20px;
border-radius: 10px;
margin: 20px 0;
border-left: 4px solid #ff006e;
font-size: 1.1em;
line-height: 1.6;
color: #e0e0e0;
}
.scenario-choices {
display: grid;
gap: 15px;
margin: 25px 0;
}
.choice-option {
background: linear-gradient(135deg, rgba(131, 56, 236, 0.4), rgba(58, 134, 255, 0.3));
border: 2px solid #8338ec;
border-radius: 12px;
padding: 25px;
cursor: pointer;
transition: all 0.3s ease;
margin: 10px 0;
}
.choice-option:hover {
background: linear-gradient(135deg, rgba(131, 56, 236, 0.7), rgba(58, 134, 255, 0.5));
border-color: #3a86ff;
transform: translateY(-3px);
box-shadow: 0 8px 25px rgba(131, 56, 236, 0.4);
}
.choice-option h4 {
color: #ff006e;
margin: 0 0 12px 0;
font-size: 1.3em;
font-weight: bold;
}
.choice-preview {
color: #b8b8b8;
margin: 0;
font-style: italic;
font-size: 1em;
line-height: 1.4;
}
.action-details {
background: rgba(58, 134, 255, 0.2);
border: 1px solid #3a86ff;
border-radius: 10px;
padding: 20px;
margin: 20px 0;
}
.action-text {
color: #3a86ff;
font-weight: bold;
font-size: 1.1em;
margin: 10px 0;
}
.duration-text {
color: #ff6b35;
font-weight: bold;
margin: 10px 0;
}
.action-timer {
background: rgba(0, 0, 0, 0.7);
border-radius: 10px;
padding: 20px;
margin: 20px 0;
text-align: center;
}
.progress-bar {
width: 100%;
height: 20px;
background: rgba(255, 255, 255, 0.2);
border-radius: 10px;
overflow: hidden;
margin: 15px 0;
}
.progress-fill {
height: 100%;
background: linear-gradient(90deg, #ff006e, #8338ec);
width: 0%;
transition: width 1s ease;
}
.ending-details {
background: linear-gradient(135deg, rgba(255, 215, 0, 0.2), rgba(255, 140, 0, 0.2));
border: 2px solid #ffd700;
border-radius: 10px;
padding: 20px;
margin: 20px 0;
text-align: center;
}
.ending-details h4 {
color: #ffd700;
margin: 0 0 15px 0;
font-size: 1.3em;
}
/* Photo Session Styles */
.photo-section {
background: linear-gradient(135deg, rgba(138, 43, 226, 0.2), rgba(255, 20, 147, 0.2));
border: 2px solid #8a2be2;
border-radius: 15px;
padding: 20px;
margin: 20px 0;
}
.photo-capture-controls {
background: rgba(0, 0, 0, 0.5);
border-radius: 10px;
padding: 15px;
}
.photo-capture-controls button {
background: linear-gradient(135deg, #ff1493, #8a2be2);
border: none;
color: white;
padding: 12px 24px;
border-radius: 8px;
cursor: pointer;
font-size: 1em;
font-weight: bold;
transition: all 0.3s ease;
}
.photo-capture-controls button:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(255, 20, 147, 0.4);
}
#photo-progress {
color: #ff1493;
font-weight: bold;
margin-top: 15px;
}
#webcam-container {
border-radius: 10px;
overflow: hidden;
max-width: 100%;
margin: 0 auto;
}
.outcome-text {
color: #ffb347;
font-weight: bold;
margin: 0;
}
/* Focus Task Styles */
.focus-task-simple, .focus-session-active, .focus-completed {
background: rgba(20, 20, 20, 0.95);
border: 2px solid #673ab7;
border-radius: 15px;
padding: 25px;
margin: 20px auto;
max-width: 800px;
text-align: center;
backdrop-filter: blur(10px);
}
.focus-timer-large {
font-size: 72px;
font-weight: bold;
color: #673ab7;
margin: 30px 0;
text-shadow: 0 0 20px rgba(103, 58, 183, 0.5);
}
.focus-instructions {
background: rgba(103, 58, 183, 0.2);
border: 1px solid #673ab7;
border-radius: 10px;
padding: 20px;
margin: 20px 0;
}
.focus-instructions p {
margin: 8px 0;
color: #e0e0e0;
}
.focus-status {
margin: 20px 0;
}
#focus-status-text {
font-size: 1.1em;
font-weight: bold;
color: #673ab7;
transition: color 0.3s ease;
}
.completion-message {
background: linear-gradient(135deg, rgba(76, 175, 80, 0.3), rgba(139, 195, 74, 0.2));
border: 2px solid #4caf50;
border-radius: 10px;
padding: 20px;
margin: 20px 0;
}
.completion-message p {
margin: 10px 0;
color: #e8f5e8;
}
/* Video Control Panel Styles */
.control-sidebar {
position: fixed;
top: 20px;
right: 20px;
width: 220px;
display: flex;
flex-direction: column;
background: rgba(0, 0, 0, 0.8);
border: 1px solid rgba(0, 212, 255, 0.3);
border-radius: 10px;
padding: 15px;
gap: 10px;
height: fit-content;
z-index: 1000;
}
.video-control-panel {
background: rgba(0, 0, 0, 0.9);
border: 1px solid rgba(139, 92, 246, 0.4);
border-radius: 8px;
margin-bottom: 15px;
overflow: hidden;
}
.video-control-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 10px 12px;
background: rgba(139, 92, 246, 0.2);
cursor: pointer;
transition: background 0.3s ease;
}
.video-control-header:hover {
background: rgba(139, 92, 246, 0.3);
}
.video-control-icon {
font-size: 1.2rem;
}
.video-control-title {
font-weight: 600;
color: #E5E7EB;
flex: 1;
text-align: center;
}
.video-control-toggle {
font-size: 0.9rem;
transition: transform 0.3s ease;
}
.video-control-toggle.collapsed {
transform: rotate(-90deg);
}
.video-control-content {
padding: 12px;
display: block;
transition: all 0.3s ease;
max-height: 400px;
overflow: hidden;
}
.video-control-content.collapsed {
display: none;
max-height: 0;
padding: 0 12px;
}
.control-group {
margin-bottom: 12px;
}
.control-group:last-child {
margin-bottom: 0;
}
.control-label {
display: block;
font-size: 0.8rem;
color: #9CA3AF;
margin-bottom: 5px;
font-weight: 500;
}
.control-row {
display: flex;
gap: 6px;
align-items: center;
justify-content: space-between;
}
.control-btn {
background: rgba(139, 92, 246, 0.2);
color: #E5E7EB;
border: 1px solid rgba(139, 92, 246, 0.4);
border-radius: 6px;
padding: 6px 8px;
cursor: pointer;
transition: all 0.3s ease;
font-size: 0.8rem;
flex: 1;
min-width: 0;
text-align: center;
}
.control-btn:hover {
background: rgba(139, 92, 246, 0.4);
border-color: rgba(139, 92, 246, 0.6);
}
.volume-control {
display: flex;
align-items: center;
gap: 8px;
}
.volume-slider {
flex: 1;
height: 4px;
background: rgba(255, 255, 255, 0.2);
border-radius: 2px;
outline: none;
-webkit-appearance: none;
appearance: none;
}
.volume-slider::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 14px;
height: 14px;
background: #8B5CF6;
border-radius: 50%;
cursor: pointer;
}
.volume-display {
font-size: 0.8rem;
color: #9CA3AF;
min-width: 35px;
text-align: right;
}
.playlist-select {
width: 100%;
background: rgba(0, 0, 0, 0.6);
color: #E5E7EB;
border: 1px solid rgba(139, 92, 246, 0.4);
border-radius: 6px;
padding: 6px 8px;
font-size: 0.8rem;
outline: none;
cursor: pointer;
}
.playlist-select:hover {
border-color: rgba(139, 92, 246, 0.6);
}
.playlist-select:focus {
border-color: #8B5CF6;
box-shadow: 0 0 0 2px rgba(139, 92, 246, 0.2);
}
.video-info {
background: rgba(0, 0, 0, 0.6);
border-radius: 6px;
padding: 8px;
}
.current-video-name {
font-size: 0.8rem;
color: #E5E7EB;
margin-bottom: 6px;
text-align: center;
font-weight: 500;
}
.video-progress {
display: flex;
flex-direction: column;
gap: 4px;
}
.progress-bar {
height: 4px;
background: rgba(255, 255, 255, 0.2);
border-radius: 2px;
overflow: hidden;
}
.progress-fill {
height: 100%;
background: linear-gradient(90deg, #8B5CF6, #EC4899);
width: 0%;
transition: width 0.1s ease;
}
.video-time {
font-size: 0.7rem;
color: #9CA3AF;
text-align: center;
}
</style>
</head>
<body>
<!-- Background Video Container -->
<div class="training-video-container" id="trainingVideoContainer">
<!-- Video will be inserted here -->
</div>
<!-- Video Control Panel (Collapsible) -->
<div class="control-sidebar">
<div id="video-control-panel" class="video-control-panel">
<div class="video-control-header" onclick="toggleVideoControls()">
<span class="video-control-icon">🎬</span>
<span class="video-control-title">Video Controls</span>
<span id="video-control-toggle" class="video-control-toggle collapsed"></span>
</div>
<div id="video-control-content" class="video-control-content collapsed">
<!-- Playback Controls -->
<div class="control-group">
<div class="control-row">
<button id="video-rewind" class="control-btn" title="Rewind 10s"></button>
<button id="video-play-pause" class="control-btn" title="Play/Pause">⏸️</button>
<button id="video-forward" class="control-btn" title="Forward 10s"></button>
<button id="video-skip" class="control-btn" title="Skip Video">⏭️</button>
</div>
</div>
<!-- Volume Control -->
<div class="control-group">
<label class="control-label">🔊 Volume</label>
<div class="volume-control">
<input type="range" id="video-volume" class="volume-slider" min="0" max="100" value="50">
<span id="video-volume-display" class="volume-display">50%</span>
</div>
</div>
<!-- Playlist Selection -->
<div class="control-group">
<label class="control-label">📂 Source</label>
<select id="video-playlist-select" class="playlist-select">
<option value="random">🎲 Random Videos</option>
<option value="playlist1">📝 Playlist 1</option>
<option value="playlist2">📝 Playlist 2</option>
<option value="playlist3">📝 Playlist 3</option>
</select>
</div>
<!-- Video Info -->
<div class="control-group">
<div class="video-info">
<div id="current-video-name" class="current-video-name">No video loaded</div>
<div id="video-progress" class="video-progress">
<div class="progress-bar">
<div id="progress-fill" class="progress-fill"></div>
</div>
<div id="video-time" class="video-time">00:00 / 00:00</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Navigation -->
<div class="back-navigation">
<a href="index.html" class="back-btn">← Back to Main</a>
</div>
<!-- Main Content -->
<div class="main-content">
<!-- Academy Header -->
<div class="academy-header">
<h1>🎓 Gooner Training Academy</h1>
<div class="subtitle">Master the Art of Dedicated Gooning</div>
</div>
<!-- Library Status -->
<div class="library-status" id="libraryStatus">
<h3>📚 Library Status</h3>
<div id="videoLibraryStatus">Initializing video library...</div>
<div id="photoLibraryStatus">Checking photo directories...</div>
<button onclick="showAllPhotos()" id="view-gallery-btn" style="margin-top: 10px; padding: 8px 16px; background: #9c27b0; color: white; border: none; border-radius: 5px; cursor: pointer; display: none;">
📸 View Photo Gallery
</button>
</div>
<!-- Training Mode Selection -->
<div class="training-controls">
<div class="academy-mode-selection" id="trainingModeSelection">
<!-- Training modes will be populated here -->
</div>
</div>
<!-- Start Controls -->
<div class="academy-start-controls">
<button class="start-training-btn" id="startTrainingBtn" onclick="startTrainingSession()" disabled>
Start Training Session
</button>
</div>
<!-- Game Interface (Hidden initially) -->
<div id="gameInterface" style="display: none;">
<!-- Full game interface container -->
<div id="gameContainer">
<!-- Game will be injected here -->
</div>
<!-- Required game elements -->
<div id="game-area" style="display: none;"></div>
<div id="task-area" style="display: none;"></div>
<div id="current-task" style="display: none;"></div>
<div id="task-content" style="display: none;"></div>
<div id="task-image" style="display: none;"></div>
<div id="task-text" style="display: none;"></div>
<div id="controls-area" style="display: none;"></div>
<div id="game-controls" style="display: none;"></div>
<!-- Required game buttons -->
<button id="complete-btn" style="display: none;">Complete Task</button>
<button id="skip-btn" style="display: none;">Quit Training</button>
<button id="pause-btn" style="display: none;">Pause</button>
<button id="quit-btn" style="display: none;">Quit Training</button>
<button id="play-again-btn" style="display: none;">Play Again</button>
<!-- Game status elements -->
<div id="game-status" style="display: none;"></div>
<div id="timer-display" style="display: none;"></div>
<div id="xp-display" style="display: none;"></div>
<div id="task-counter" style="display: none;"></div>
</div>
</div>
<!-- Core Scripts -->
<script>
// Only load Electron-specific scripts if in Electron environment
if (typeof window !== 'undefined' && window.process && window.process.type === 'renderer') {
console.log('🖥️ Electron environment detected - loading desktop scripts');
// Preload script is automatically loaded by Electron, no need to include here
} else {
console.log('🌐 Browser environment detected - skipping desktop scripts');
}
</script>
<script src="src/utils/desktop-file-manager.js"></script>
<script src="src/features/media/baseVideoPlayer.js"></script>
<script src="src/features/video/videoPlayerManager.js"></script>
<script src="src/features/media/videoLibrary.js"></script>
<script src="src/features/images/popupImageManager.js"></script>
<script src="src/features/audio/audioManager.js"></script>
<script src="src/features/tts/voiceManager.js"></script>
<script src="src/features/webcam/webcamManager.js"></script>
<script src="src/features/stats/playerStats.js"></script>
<script src="src/features/ui/flashMessageManager.js"></script>
<script src="src/features/tasks/interactiveTaskManager.js"></script>
<!-- Data Scripts -->
<script src="src/data/gameData.js"></script>
<script src="src/data/modes/trainingGameData.js"></script>
<script src="src/data/modes/humiliationGameData.js"></script>
<script src="src/data/modes/dressUpGameData.js"></script>
<script src="src/data/modes/enduranceGameData.js"></script>
<script src="src/data/gameDataManager.js"></script>
<!-- Core Game Scripts -->
<script src="src/core/gameModeManager.js"></script>
<script src="src/core/game.js"></script>
<!-- Note: main.js is Electron-specific and not needed in browser -->
<script>
// Training Academy Global Variables
let trainingVideoLibrary = [];
let trainingPhotoLibrary = [];
let currentTrainingVideo = null;
let currentVideoIndex = 0;
let selectedTrainingMode = null;
let videoOpacity = 0.3;
let isVideoPlaying = true;
let videoControlListenersInitialized = false;
// Helper function to get format from file path
function getFormatFromPath(filePath) {
const extension = filePath.split('.').pop().toLowerCase();
return extension || 'unknown';
}
// Get available modes from GameModeManager
function getAvailableModes() {
// Show all available training modes
// Training Academy has multiple specialized modes
const allModes = {
'photography-studio': {
name: 'Photography Studio',
description: 'Dedicated webcam photography and dressing sessions',
icon: '📸'
},
'training-academy': {
name: 'Training Academy',
description: 'Structured training programs and challenges',
icon: '🎓'
},
'punishment-gauntlet': {
name: 'Punishment Gauntlet',
description: 'Face intense punishment and humiliation challenges',
icon: '⛓️'
},
'endurance-trials': {
name: 'Endurance Trials',
description: 'Test your limits with marathon sessions',
icon: '💪'
}
};
// Return all available training modes
return allModes;
}
// Initialize Video Library (similar to Quick Play)
async function initializeVideoLibrary() {
try {
console.log('🎬 Training Academy: Initializing video library...');
// Check if we're in Electron environment with proper API access
const isElectron = window.electronAPI && (
window.electronAPI.readVideoDirectory ||
window.electronAPI.readDirectory ||
window.electronAPI.getVideoFiles
);
if (!isElectron) {
console.log('🌐 Electron API not available - checking alternative methods...');
// Try to use the unified video library from desktop file manager if available
if (window.desktopFileManager && window.desktopFileManager.unifiedVideoLibrary) {
const unifiedLibrary = window.desktopFileManager.unifiedVideoLibrary;
trainingVideoLibrary = [...unifiedLibrary];
console.log(`🎬 Using unified video library: ${trainingVideoLibrary.length} videos`);
if (trainingVideoLibrary.length > 0) {
document.getElementById('videoLibraryStatus').innerHTML =
`<span style="color: #27ae60;">✅ ${trainingVideoLibrary.length} videos loaded (unified library)</span>`;
await startBackgroundVideo();
} else {
document.getElementById('videoLibraryStatus').innerHTML =
'<span style="color: #f39c12;">⚠️ No videos in unified library. Use Library management to add directories.</span>';
}
return;
}
document.getElementById('videoLibraryStatus').innerHTML =
'<span style="color: #f39c12;">⚠️ Video library unavailable - Electron API not accessible</span>';
return;
}
// Get linked video directories from localStorage
const linkedDirectories = JSON.parse(localStorage.getItem('linkedVideoDirectories') || '[]');
console.log('📁 Linked video directories:', linkedDirectories);
if (linkedDirectories.length === 0) {
document.getElementById('videoLibraryStatus').innerHTML =
'<span style="color: #f39c12;">⚠️ No video directories linked. Use Library management to add directories.</span>';
return;
}
// Initialize desktop file manager
if (window.desktopFileManager && typeof window.desktopFileManager.init === 'function') {
await window.desktopFileManager.init();
}
trainingVideoLibrary = [];
// Scan each linked directory for videos
for (const directoryData of linkedDirectories) {
try {
// Handle both string paths and directory objects
const directoryPath = typeof directoryData === 'string' ? directoryData : directoryData.path;
if (!directoryPath) {
console.warn('⚠️ Invalid directory data:', directoryData);
continue;
}
console.log(`📂 Scanning directory: ${directoryPath}`);
// Use the same video scanning approach as Quick Play
const videoExtensions = /\.(mp4|webm|avi|mov|mkv|wmv|flv|m4v)$/i;
let files = [];
if (window.electronAPI.readVideoDirectory) {
files = await window.electronAPI.readVideoDirectory(directoryPath);
} else if (window.electronAPI.readVideoDirectoryRecursive) {
files = await window.electronAPI.readVideoDirectoryRecursive(directoryPath);
} else if (window.electronAPI.readDirectory) {
const allFiles = await window.electronAPI.readDirectory(directoryPath);
files = allFiles.filter(file => videoExtensions.test(file.name));
} else if (window.electronAPI.getVideoFiles) {
files = await window.electronAPI.getVideoFiles(directoryPath);
}
if (files && files.length > 0) {
console.log(`📹 Found ${files.length} videos in ${directoryPath}`);
// Add directory info to each video
files.forEach(file => {
trainingVideoLibrary.push({
name: file.name,
path: file.path,
fullPath: file.path,
size: file.size || 0,
duration: file.duration || 0,
directory: directoryPath,
dateAdded: new Date().toISOString(),
category: 'directory'
});
});
} else {
console.log(`📹 No videos found in ${directoryPath}`);
}
} catch (error) {
console.error(`❌ Error scanning directory ${directoryData}:`, error);
}
}
console.log(`🎬 Total videos loaded: ${trainingVideoLibrary.length}`);
// Update status display
if (trainingVideoLibrary.length > 0) {
document.getElementById('videoLibraryStatus').innerHTML =
`<span style="color: #27ae60;">✅ ${trainingVideoLibrary.length} videos loaded</span>`;
// Start background video
await startBackgroundVideo();
} else {
document.getElementById('videoLibraryStatus').innerHTML =
'<span style="color: #e74c3c;">❌ No videos found in linked directories</span>';
}
} catch (error) {
console.error('❌ Error initializing video library:', error);
document.getElementById('videoLibraryStatus').innerHTML =
'<span style="color: #e74c3c;">❌ Error loading video library</span>';
}
}
// Initialize Photo Library
async function initializePhotoLibrary() {
try {
console.log('📸 Training Academy: Initializing photo library...');
// Debug: Check what APIs are available
console.log('🔍 Available APIs:', {
electronAPI: !!window.electronAPI,
getImageFiles: window.electronAPI && typeof window.electronAPI.getImageFiles,
readDirectory: window.electronAPI && typeof window.electronAPI.readDirectory,
desktopFileManager: !!window.desktopFileManager
});
// Check if we're in Electron environment with proper API access
const isElectron = window.electronAPI && (
typeof window.electronAPI.getImageFiles === 'function' ||
typeof window.electronAPI.readDirectory === 'function'
);
if (!isElectron && !window.desktopFileManager) {
document.getElementById('photoLibraryStatus').innerHTML =
'<span style="color: #f39c12;">⚠️ Photo library unavailable - Electron API not accessible</span>';
return;
}
const linkedPhotoDirectories = JSON.parse(localStorage.getItem('linkedImageDirectories') || '[]');
const linkedIndividualImages = JSON.parse(localStorage.getItem('linkedIndividualImages') || '[]');
console.log('📁 Linked photo directories:', linkedPhotoDirectories);
console.log('📷 Linked individual images:', linkedIndividualImages);
if (linkedPhotoDirectories.length === 0 && linkedIndividualImages.length === 0) {
document.getElementById('photoLibraryStatus').innerHTML =
'<span style="color: #f39c12;">⚠️ No photo directories linked</span>';
return;
}
trainingPhotoLibrary = [];
let totalPhotos = 0;
// Process linked directories
for (const directoryData of linkedPhotoDirectories) {
try {
// Handle both string paths and directory objects
const directoryPath = typeof directoryData === 'string' ? directoryData : directoryData.path;
if (!directoryPath) {
console.warn('⚠️ Invalid photo directory data:', directoryData);
continue;
}
console.log(`📂 Scanning photo directory: ${directoryPath}`);
let photos = [];
// Try different Electron APIs for reading image files
if (window.electronAPI && window.electronAPI.getImageFiles) {
photos = await window.electronAPI.getImageFiles(directoryPath);
} else if (window.electronAPI && window.electronAPI.readDirectory) {
const allFiles = await window.electronAPI.readDirectory(directoryPath);
// Filter for image files
photos = allFiles.filter(file =>
/\.(jpg|jpeg|png|gif|bmp|webp)$/i.test(file.name || file.filename)
);
} else if (window.desktopFileManager) {
// Fallback to desktop file manager
photos = window.desktopFileManager.getImagesFromDirectory(directoryPath) || [];
}
console.log(`📷 Found ${photos.length} photos in ${directoryPath}`);
const photosWithPath = photos.map(photo => ({
...photo,
directory: directoryPath,
fullPath: photo.path
}));
trainingPhotoLibrary.push(...photosWithPath);
totalPhotos += photos.length;
} catch (error) {
console.error(`❌ Error scanning photo directory ${directoryData}:`, error);
}
}
// Process individual linked images
for (const imageData of linkedIndividualImages) {
try {
const imageWithPath = {
...imageData,
fullPath: imageData.path || imageData.filePath
};
trainingPhotoLibrary.push(imageWithPath);
totalPhotos++;
} catch (error) {
console.error(`❌ Error processing individual image ${imageData}:`, error);
}
}
console.log(`📸 Total photos loaded: ${totalPhotos} (${linkedPhotoDirectories.length} directories + ${linkedIndividualImages.length} individual images)`);
// Load previously captured webcam photos from localStorage
try {
const savedWebcamPhotos = JSON.parse(localStorage.getItem('trainingAcademyPhotos') || '[]');
if (savedWebcamPhotos.length > 0) {
trainingPhotoLibrary.push(...savedWebcamPhotos);
console.log(`📸 Loaded ${savedWebcamPhotos.length} previously captured photos from localStorage`);
}
} catch (error) {
console.warn('⚠️ Failed to load saved webcam photos:', error);
}
document.getElementById('photoLibraryStatus').innerHTML =
`<span style="color: #27ae60;">✅ ${trainingPhotoLibrary.length} photos available</span>`;
// Show gallery button if there are photos
if (trainingPhotoLibrary.length > 0) {
document.getElementById('view-gallery-btn').style.display = 'inline-block';
}
} catch (error) {
console.error('❌ Error initializing photo library:', error);
document.getElementById('photoLibraryStatus').innerHTML =
'<span style="color: #e74c3c;">❌ Error loading photo library</span>';
}
}
// Start Background Video
async function startBackgroundVideo() {
if (trainingVideoLibrary.length === 0) return;
try {
const randomIndex = Math.floor(Math.random() * trainingVideoLibrary.length);
const selectedVideo = trainingVideoLibrary[randomIndex];
currentVideoIndex = randomIndex;
console.log(`🎬 Starting background video: ${selectedVideo.name}`);
const videoContainer = document.getElementById('trainingVideoContainer');
videoContainer.innerHTML = `
<video id="backgroundVideo" autoplay muted loop style="opacity: ${videoOpacity};">
<source src="file://${selectedVideo.fullPath}" type="video/mp4">
Your browser does not support the video tag.
</video>
`;
const video = document.getElementById('backgroundVideo');
video.onloadstart = () => {
console.log('🎬 Video loading started');
};
video.onloadeddata = () => {
console.log('🎬 Video data loaded');
setupVideoControlListeners(); // Initialize control functionality
};
video.onloadedmetadata = () => {
console.log('🎬 Video metadata loaded');
// Update video info once metadata is available
setTimeout(() => {
updateVideoInfo();
updateVideoProgress();
// Ensure play/pause button state is correct
const playPauseBtn = document.getElementById('video-play-pause');
if (playPauseBtn) {
if (video.paused) {
playPauseBtn.textContent = '▶️';
playPauseBtn.title = 'Play';
} else {
playPauseBtn.textContent = '⏸️';
playPauseBtn.title = 'Pause';
}
console.log('🎮 Button state updated on metadata load:', video.paused ? 'Play' : 'Pause');
}
}, 100);
};
video.onplay = () => {
console.log('🎬 Video started playing');
const playPauseBtn = document.getElementById('video-play-pause');
if (playPauseBtn) {
playPauseBtn.textContent = '⏸️';
playPauseBtn.title = 'Pause';
}
};
video.onerror = (e) => {
console.error('❌ Video error:', e);
skipToNextVideo(); // Try next video on error
};
currentTrainingVideo = video;
} catch (error) {
console.error('❌ Error starting background video:', error);
}
}
// Video Control Functions
function skipToNextVideo() {
if (trainingVideoLibrary.length === 0) return;
currentVideoIndex = (currentVideoIndex + 1) % trainingVideoLibrary.length;
const nextVideo = trainingVideoLibrary[currentVideoIndex];
const video = document.getElementById('backgroundVideo');
if (video) {
console.log(`🎬 Switching from video paused state: ${video.paused}`);
video.src = `file://${nextVideo.fullPath}`;
console.log(`🎬 Switched to: ${nextVideo.name}`);
console.log(`🎬 After src change, video paused state: ${video.paused}`);
// Update video info and ensure controls work for the new video
video.addEventListener('loadedmetadata', function() {
updateVideoInfo();
updateVideoProgress();
refreshVideoControlState();
}, { once: true });
// Ensure button state updates when new video plays
video.addEventListener('play', function() {
refreshVideoControlState();
}, { once: true });
}
}
function toggleVideoOpacity() {
const opacityLevels = [0.1, 0.3, 0.5, 0.7];
const currentIndex = opacityLevels.indexOf(videoOpacity);
videoOpacity = opacityLevels[(currentIndex + 1) % opacityLevels.length];
const video = document.getElementById('backgroundVideo');
if (video) {
video.style.opacity = videoOpacity;
console.log(`🎬 Video opacity: ${(videoOpacity * 100)}%`);
}
}
function toggleVideoPlayback() {
const video = document.getElementById('backgroundVideo');
if (video) {
if (isVideoPlaying) {
video.pause();
isVideoPlaying = false;
console.log('⏸️ Video paused');
} else {
video.play();
isVideoPlaying = true;
console.log('▶️ Video resumed');
}
}
}
// Refresh Video Control State (for video changes)
function refreshVideoControlState() {
const video = document.getElementById('backgroundVideo');
const playPauseBtn = document.getElementById('video-play-pause');
if (video && playPauseBtn) {
if (video.paused) {
playPauseBtn.textContent = '▶️';
playPauseBtn.title = 'Play';
} else {
playPauseBtn.textContent = '⏸️';
playPauseBtn.title = 'Pause';
}
console.log('🎮 Control state refreshed:', video.paused ? 'Play' : 'Pause');
}
}
// Toggle Video Controls Panel
function toggleVideoControls() {
const content = document.getElementById('video-control-content');
const toggle = document.getElementById('video-control-toggle');
if (content.classList.contains('collapsed')) {
content.classList.remove('collapsed');
toggle.classList.remove('collapsed');
toggle.textContent = '▼';
} else {
content.classList.add('collapsed');
toggle.classList.add('collapsed');
toggle.textContent = '▶';
}
}
// Setup Complete Video Control System (from Quick Play)
function setupVideoControlListeners() {
// Prevent duplicate listener setup
if (videoControlListenersInitialized) {
console.log('🎮 Video control listeners already initialized, skipping...');
return;
}
const video = document.getElementById('backgroundVideo');
if (!video) {
console.log('⚠️ Background video element not found during control setup');
return;
}
console.log('🎮 Setting up video control listeners...');
// Play/Pause control
const playPauseBtn = document.getElementById('video-play-pause');
console.log('🎮 Play/pause button found:', !!playPauseBtn);
if (playPauseBtn) {
playPauseBtn.addEventListener('click', async () => {
const currentVideo = document.getElementById('backgroundVideo');
console.log('🎬 Video element found:', !!currentVideo);
console.log('🎬 Video paused state:', currentVideo ? currentVideo.paused : 'no video');
if (!currentVideo) {
console.log('No video element found');
return;
}
try {
if (currentVideo.paused) {
await currentVideo.play();
playPauseBtn.textContent = '⏸️';
playPauseBtn.title = 'Pause';
console.log('▶️ Video resumed');
} else {
currentVideo.pause();
playPauseBtn.textContent = '▶️';
playPauseBtn.title = 'Play';
console.log('⏸️ Video paused');
}
} catch (error) {
console.error('Video playback error:', error);
}
});
} else {
console.error('❌ Play/pause button not found!');
}
// Rewind 10 seconds
const rewindBtn = document.getElementById('video-rewind');
if (rewindBtn) {
rewindBtn.addEventListener('click', () => {
const currentVideo = document.getElementById('backgroundVideo');
if (currentVideo && currentVideo.duration && isFinite(currentVideo.duration)) {
currentVideo.currentTime = Math.max(0, currentVideo.currentTime - 10);
console.log('⏪ Rewound 10 seconds');
} else {
console.log('Cannot rewind: video not ready');
}
});
}
// Forward 10 seconds
const forwardBtn = document.getElementById('video-forward');
if (forwardBtn) {
forwardBtn.addEventListener('click', () => {
const currentVideo = document.getElementById('backgroundVideo');
if (currentVideo && currentVideo.duration && isFinite(currentVideo.duration)) {
currentVideo.currentTime = Math.min(currentVideo.duration, currentVideo.currentTime + 10);
console.log('⏩ Fast forwarded 10 seconds');
} else {
console.log('Cannot fast forward: video not ready');
}
});
}
// Skip video
const skipBtn = document.getElementById('video-skip');
if (skipBtn) {
skipBtn.addEventListener('click', () => {
skipToNextVideo();
});
}
// Volume control
const volumeSlider = document.getElementById('video-volume');
const volumeDisplay = document.getElementById('video-volume-display');
if (volumeSlider && volumeDisplay) {
volumeSlider.addEventListener('input', (e) => {
const currentVideo = document.getElementById('backgroundVideo');
if (currentVideo) {
const volume = e.target.value / 100;
currentVideo.volume = volume;
volumeDisplay.textContent = e.target.value + '%';
console.log(`🔊 Volume set to ${e.target.value}% (${volume})`);
// Auto-mute when volume is 0
if (volume === 0) {
currentVideo.muted = true;
} else if (currentVideo.muted && volume > 0) {
currentVideo.muted = false;
}
} else {
console.warn('⚠️ No background video element found for volume control');
}
});
// Set initial volume when video loads
const initializeVolume = () => {
const currentVideo = document.getElementById('backgroundVideo');
if (currentVideo) {
currentVideo.volume = volumeSlider.value / 100;
volumeDisplay.textContent = volumeSlider.value + '%';
console.log(`🔊 Initial volume set to ${volumeSlider.value}%`);
}
};
// Try to set initial volume now and also when video loads
initializeVolume();
// Also set up a listener for when new videos are loaded
document.addEventListener('videoLoaded', initializeVolume);
}
// Playlist selection
const playlistSelect = document.getElementById('video-playlist-select');
if (playlistSelect) {
playlistSelect.addEventListener('change', (e) => {
const selectedPlaylist = e.target.value;
console.log('🎵 Playlist changed to:', selectedPlaylist);
// For now, just load a random video from current library
skipToNextVideo();
});
}
// Video progress and time updates
video.addEventListener('timeupdate', updateVideoProgress);
video.addEventListener('loadedmetadata', updateVideoInfo);
video.addEventListener('play', () => {
const playPauseBtn = document.getElementById('video-play-pause');
if (playPauseBtn) {
playPauseBtn.textContent = '⏸️';
playPauseBtn.title = 'Pause';
}
});
video.addEventListener('pause', () => {
const playPauseBtn = document.getElementById('video-play-pause');
if (playPauseBtn) {
playPauseBtn.textContent = '▶️';
playPauseBtn.title = 'Play';
}
});
// Initial updates
setTimeout(() => {
updateVideoInfo();
updateVideoProgress();
// Set initial play/pause button state
const initialPlayPauseBtn = document.getElementById('video-play-pause');
if (initialPlayPauseBtn && video) {
if (video.paused) {
initialPlayPauseBtn.textContent = '▶️';
initialPlayPauseBtn.title = 'Play';
} else {
initialPlayPauseBtn.textContent = '⏸️';
initialPlayPauseBtn.title = 'Pause';
}
console.log('🎮 Initial button state set:', video.paused ? 'Play' : 'Pause');
}
}, 100);
console.log('✅ Video control listeners set up successfully');
videoControlListenersInitialized = true;
}
function updateVideoProgress() {
const video = document.getElementById('backgroundVideo');
const progressFill = document.getElementById('progress-fill');
const videoTime = document.getElementById('video-time');
if (video && progressFill && videoTime && video.duration && isFinite(video.duration)) {
const progress = (video.currentTime / video.duration) * 100;
progressFill.style.width = isNaN(progress) ? '0%' : progress + '%';
const currentMinutes = Math.floor(video.currentTime / 60);
const currentSeconds = Math.floor(video.currentTime % 60);
const durationMinutes = Math.floor(video.duration / 60);
const durationSeconds = Math.floor(video.duration % 60);
videoTime.textContent = `${currentMinutes.toString().padStart(2, '0')}:${currentSeconds.toString().padStart(2, '0')} / ${durationMinutes.toString().padStart(2, '0')}:${durationSeconds.toString().padStart(2, '0')}`;
} else if (videoTime) {
videoTime.textContent = '00:00 / 00:00';
}
}
function updateVideoInfo() {
const video = document.getElementById('backgroundVideo');
const videoName = document.getElementById('current-video-name');
if (video && videoName) {
const videoPath = video.src || (video.querySelector('source') && video.querySelector('source').src);
if (videoPath) {
const fileName = videoPath.split('/').pop().split('\\').pop().split('.')[0];
const formattedName = fileName.replace(/[-_]/g, ' ');
// Capitalize each word
const displayName = formattedName.split(' ').map(word =>
word.charAt(0).toUpperCase() + word.slice(1)
).join(' ');
videoName.textContent = displayName;
} else {
videoName.textContent = 'No video loaded';
}
}
}
// Training Mode Selection
function setupTrainingModeSelection() {
const container = document.getElementById('trainingModeSelection');
const availableModes = getAvailableModes();
Object.entries(availableModes).forEach(([modeId, mode]) => {
const modeCard = document.createElement('div');
modeCard.className = 'training-mode-card';
modeCard.dataset.mode = modeId;
modeCard.onclick = () => selectTrainingMode(modeId);
modeCard.innerHTML = `
<div class="mode-icon">${mode.icon}</div>
<div class="mode-name">${mode.name}</div>
<div class="mode-description">${mode.description}</div>
`;
container.appendChild(modeCard);
});
}
function selectTrainingMode(modeId) {
// Remove previous selection
document.querySelectorAll('.training-mode-card').forEach(card => {
card.classList.remove('selected');
});
// Select new mode
document.querySelector(`[data-mode="${modeId}"]`).classList.add('selected');
selectedTrainingMode = modeId;
// Enable start button
document.getElementById('startTrainingBtn').disabled = false;
const availableModes = getAvailableModes();
console.log(`🎯 Selected training mode: ${availableModes[modeId].name}`);
}
// Start Training Session
function startTrainingSession() {
if (!selectedTrainingMode) {
alert('Please select a training mode first!');
return;
}
console.log(`🚀 Starting training session: ${selectedTrainingMode}`);
// Hide setup interface
document.querySelector('.academy-header').style.display = 'none';
document.querySelector('.library-status').style.display = 'none';
document.querySelector('.training-controls').style.display = 'none';
document.querySelector('.academy-start-controls').style.display = 'none';
// Show game interface
const gameInterface = document.getElementById('gameInterface');
gameInterface.style.display = 'block';
// Also hide the video controls to reduce distractions during training
const videoControls = document.getElementById('videoControlsOverlay');
if (videoControls) {
videoControls.style.opacity = '0.5'; // Make less prominent during game
}
// Initialize training-specific game mode
initializeTrainingGame();
}
// Initialize Training Game
function initializeTrainingGame() {
try {
console.log('🎓 Initializing Training Academy game...');
// Debug: Check if humiliation data is properly loaded
console.log('🔍 Debug: Checking humiliation data...');
console.log('🔍 window.humiliationGameData exists:', !!window.humiliationGameData);
if (window.humiliationGameData) {
console.log('🔍 Humiliation scenarios:', window.humiliationGameData.scenarios?.length || 0);
console.log('🔍 Humiliation scenario IDs:', window.humiliationGameData.scenarios?.map(s => s.id) || []);
}
// Ensure we have GameModeManager
if (!window.gameModeManager) {
console.error('❌ GameModeManager not available');
alert('Error: Game system not properly loaded. Please refresh the page.');
return;
}
// Set up game mode manager for training
window.gameModeManager.currentMode = selectedTrainingMode;
console.log(`🎯 Set GameModeManager to: ${selectedTrainingMode}`);
// Get scenarios from GameModeManager's scenario collections
let scenarioIds = [];
if (window.gameModeManager.scenarioCollections[selectedTrainingMode]) {
scenarioIds = window.gameModeManager.scenarioCollections[selectedTrainingMode];
console.log(`📋 Found scenario collection for ${selectedTrainingMode}:`, scenarioIds);
} else {
// Fallback scenario IDs for training-academy
scenarioIds = [
'scenario-training-regimen',
'scenario-creative-tasks',
'scenario-training-session',
'scenario-obedience-training'
];
console.log('📋 Using fallback scenario IDs:', scenarioIds);
}
// Load training tasks using GameModeManager
let trainingTasks = [];
try {
trainingTasks = window.gameModeManager.getScenarioModeTasks();
console.log(`📋 Loaded ${trainingTasks.length} tasks from GameModeManager`);
// Debug: Log the actual task structure
if (trainingTasks.length > 0) {
console.log('🔍 First training task structure:', trainingTasks[0]);
console.log('🔍 Task IDs:', trainingTasks.map(t => t.id));
console.log('🔍 Task types:', trainingTasks.map(t => t.interactiveType));
}
} catch (error) {
console.warn('⚠️ GameModeManager task loading failed:', error);
}
// Special handling for specific modes
if (selectedTrainingMode === 'punishment-gauntlet' && window.humiliationGameData && window.humiliationGameData.scenarios) {
console.log('🎯 Punishment Gauntlet mode detected - loading humiliation scenarios directly');
trainingTasks = window.humiliationGameData.scenarios.map(scenario => ({
id: scenario.id,
text: scenario.text,
difficulty: scenario.difficulty,
type: 'main',
interactiveType: scenario.interactiveType,
interactiveData: scenario.interactiveData,
isScenario: true
}));
console.log(`📋 Loaded ${trainingTasks.length} punishment scenarios directly from humiliation data`);
} else if (selectedTrainingMode === 'training-academy' && window.trainingGameData && window.trainingGameData.scenarios) {
console.log('🎓 Training Academy mode detected - loading training scenarios directly');
trainingTasks = window.trainingGameData.scenarios.map(scenario => ({
id: scenario.id,
text: scenario.text,
difficulty: scenario.difficulty,
type: 'main',
interactiveType: scenario.interactiveType,
interactiveData: scenario.interactiveData,
isScenario: true
}));
console.log(`📋 Loaded ${trainingTasks.length} training scenarios directly from training data`);
console.log('🔍 First scenario interactive data:', trainingTasks[0]?.interactiveData ? 'Present' : 'Missing');
} else if (selectedTrainingMode === 'photography-studio' && window.dressUpGameData && window.dressUpGameData.scenarios) {
console.log('📸 Photography Studio mode detected - loading dress-up scenarios directly');
trainingTasks = window.dressUpGameData.scenarios.map(scenario => ({
id: scenario.id,
text: scenario.text,
difficulty: scenario.difficulty,
type: 'main',
interactiveType: scenario.interactiveType,
interactiveData: scenario.interactiveData,
isScenario: true
}));
console.log(`📋 Loaded ${trainingTasks.length} photography scenarios directly from dress-up data`);
} else if (selectedTrainingMode === 'endurance-trials' && window.enduranceGameData && window.enduranceGameData.scenarios) {
console.log('💪 Endurance Trials mode detected - loading endurance scenarios directly');
trainingTasks = window.enduranceGameData.scenarios.map(scenario => ({
id: scenario.id,
text: scenario.text,
difficulty: scenario.difficulty,
type: 'main',
interactiveType: scenario.interactiveType,
interactiveData: scenario.interactiveData,
isScenario: true
}));
console.log(`📋 Loaded ${trainingTasks.length} endurance scenarios directly from endurance data`);
}
// Fallback for other modes or if GameModeManager failed
if (trainingTasks.length === 0) {
let fallbackData = null;
if (selectedTrainingMode === 'punishment-gauntlet' && window.humiliationGameData && window.humiliationGameData.scenarios) {
fallbackData = window.humiliationGameData.scenarios;
console.log(`📋 Using humiliation data fallback for punishment-gauntlet mode`);
} else if (selectedTrainingMode === 'photography-studio' && window.dressUpGameData && window.dressUpGameData.scenarios) {
fallbackData = window.dressUpGameData.scenarios;
console.log(`📋 Using dress-up data fallback for photography-studio mode`);
} else if (selectedTrainingMode === 'endurance-trials' && window.enduranceGameData && window.enduranceGameData.scenarios) {
fallbackData = window.enduranceGameData.scenarios;
console.log(`📋 Using endurance data fallback for endurance-trials mode`);
} else if (selectedTrainingMode === 'training-academy' && window.trainingGameData && window.trainingGameData.scenarios) {
// Only use training data scenarios if they exist and the mode is specifically training-academy
fallbackData = window.trainingGameData.scenarios;
console.log(`📋 Using training data fallback for training-academy mode`);
}
// Removed the generic fallback to prevent using wrong task types
if (fallbackData) {
trainingTasks = fallbackData.map(scenario => ({
id: scenario.id,
text: scenario.text,
difficulty: scenario.difficulty,
type: 'main',
interactiveType: scenario.interactiveType,
interactiveData: scenario.interactiveData,
isScenario: true
}));
console.log(`📋 Loaded ${trainingTasks.length} tasks from fallback data`);
}
}
// Check if we have any tasks to work with
if (trainingTasks.length === 0) {
console.error('❌ No training tasks available for mode:', selectedTrainingMode);
alert(`Error: No training scenarios found for ${selectedTrainingMode} mode. This training mode may not have content available yet.`);
// Return to mode selection instead of failing
returnToModeSelection();
return;
}
// Filter out any invalid tasks and ensure proper structure
const validTrainingTasks = trainingTasks.filter(task => {
const isValid = task && task.id && task.text && task.interactiveType;
// Also exclude the default focus-hold training tasks that shouldn't be in scenarios
const isUnwantedDefaultTask = task.id === 'edge-focus-training' ||
task.id === 'stroking-endurance-training' ||
task.id === 'extended-edging-session';
if (isUnwantedDefaultTask) {
console.warn('⚠️ Filtering out unwanted default task:', task.id);
return false;
}
if (!isValid) {
console.warn('⚠️ Filtering out invalid task:', task);
return false;
}
return true;
}).map(task => {
// Ensure each task has all required properties for the game engine
return {
...task,
// Required basic properties
id: task.id,
text: task.text || 'Training Scenario',
difficulty: task.difficulty || 'Medium',
type: task.type || 'main',
// Interactive properties
interactiveType: task.interactiveType,
interactiveData: task.interactiveData || {},
// Game engine expectations
isScenario: true,
image: task.image || null,
story: task.story || task.text,
// Ensure task is marked as available/valid
disabled: false,
completed: false
};
});
if (validTrainingTasks.length === 0) {
console.error('❌ No valid training tasks after filtering');
alert('Error: All training tasks are invalid. Please check task structure.');
return;
}
console.log(`✅ Using ${validTrainingTasks.length} valid training tasks`);
console.log('🔍 First valid task:', validTrainingTasks[0]);
trainingTasks = validTrainingTasks;
// Store training tasks globally for progression
window.originalTrainingTasks = validTrainingTasks;
console.log('💾 Stored training tasks for progression:', validTrainingTasks.length);
// Set up the game container with proper game interface
const gameContainer = document.getElementById('gameContainer');
gameContainer.innerHTML = `
<div id="main-container" class="container">
<div id="game-content">
<!-- Game will inject the full interface here -->
<div id="game-area"></div>
<div id="task-area"></div>
<div id="controls-area"></div>
</div>
</div>
`;
// Initialize the game properly using the existing game system
setTimeout(() => {
if (window.game) {
// Set the game mode and tasks directly
window.game.gameState = window.game.gameState || {};
window.game.gameState.gameMode = 'complete-all'; // Use complete-all for scenarios
window.game.gameState.trainingMode = selectedTrainingMode;
window.game.gameState.isRunning = false; // Not started yet
// Override the task loading system at multiple levels
console.log('📋 Setting up training task overrides...');
// 1. Override GameDataManager's task loading for this session
if (window.gameDataManager) {
const originalGetTasksForMode = window.gameDataManager.getTasksForMode.bind(window.gameDataManager);
window.gameDataManager.getTasksForMode = function(mode) {
if (mode === 'standard' || mode === selectedTrainingMode) {
console.log(`🎓 Overriding getTasksForMode(${mode}) with training tasks`);
return trainingTasks;
}
return originalGetTasksForMode(mode);
};
}
// 2. Override GameModeManager's task loading
if (window.gameModeManager) {
const originalGetTasksForMode = window.gameModeManager.getTasksForMode.bind(window.gameModeManager);
window.gameModeManager.getTasksForMode = function() {
console.log('🎓 Overriding GameModeManager.getTasksForMode() with training tasks');
return trainingTasks;
};
}
// 3. Set the training tasks directly in gameData
if (window.gameData) {
// Backup original tasks
window.gameData.originalMainTasks = window.gameData.originalMainTasks || [...(window.gameData.mainTasks || [])];
// Set training tasks as the active tasks
window.gameData.mainTasks = trainingTasks;
console.log('📋 Set training tasks as active game data');
// Create a robust getter/setter to prevent unwanted task replacement
const tasksList = [...trainingTasks];
Object.defineProperty(window.gameData, 'mainTasks', {
get: function() {
return this._trainingTasks || tasksList;
},
set: function(value) {
// Filter out unwanted default tasks if they somehow get added
if (Array.isArray(value)) {
const filteredTasks = value.filter(task =>
task.id !== 'edge-focus-training' &&
task.id !== 'stroking-endurance-training' &&
task.id !== 'extended-edging-session'
);
this._trainingTasks = filteredTasks.length > 0 ? filteredTasks : tasksList;
console.log('🛡️ Training Academy: Filtered tasks, keeping', this._trainingTasks.length, 'valid tasks');
} else {
this._trainingTasks = tasksList;
}
}
});
// Initialize with training tasks
window.gameData.mainTasks = trainingTasks;
}
// 3.5. Override interactive task display to prevent UI errors
if (window.game && window.game.interactiveTaskManager) {
const originalDisplayInteractiveTask = window.game.interactiveTaskManager.displayInteractiveTask.bind(window.game.interactiveTaskManager);
window.game.interactiveTaskManager.displayInteractiveTask = function(task) {
console.log('🎯 Training Academy: Custom interactive task display called - skipping');
// Skip interactive tasks in training academy
return Promise.resolve();
};
console.log('✅ Interactive task manager override applied');
} else {
console.log('⚠️ Interactive task manager not available for override');
}
// 3.6. Override completeTask to prevent errors during training academy
if (window.game) {
const originalCompleteTask = window.game.completeTask.bind(window.game);
window.game.completeTask = function() {
console.log('🎓 Training Academy: Preventing completeTask call during training');
// In training academy, task completion is handled by scenario flow
// Don't call the main game's task completion logic
return;
};
console.log('✅ CompleteTask override applied for training academy');
}
// 4. Override screen management to handle missing elements
const originalShowScreen = window.game.showScreen.bind(window.game);
window.game.showScreen = function(screenId) {
console.log(`🖥️ Training Academy: Attempting to show screen: ${screenId}`);
const element = document.getElementById(screenId);
if (!element) {
console.log(`⚠️ Training Academy: Screen ${screenId} not found, using game container instead`);
return; // Skip showing missing screens
}
return originalShowScreen(screenId);
};
// 5. Override task completion and game ending logic
const originalLoadMainTask = window.game.loadMainTask.bind(window.game);
window.game.loadMainTask = function() {
console.log('🎓 Training Academy: Custom loadMainTask called');
// Check if we have training tasks available
if (this.gameData && this.gameData.mainTasks && this.gameData.mainTasks.length > 0) {
console.log(`📋 Found ${this.gameData.mainTasks.length} training tasks available`);
// Validate the first task before proceeding
const firstTask = this.gameData.mainTasks[0];
if (!firstTask || !firstTask.text) {
console.log('❌ First task is invalid, skipping loadMainTask');
return false;
}
return originalLoadMainTask.call(this);
} else {
console.log('⚠️ No tasks available, preventing game end');
return false;
}
};
// Override the task selection to ensure proper task structure
const originalSetCurrentTask = window.game.setCurrentTask ? window.game.setCurrentTask.bind(window.game) : null;
if (originalSetCurrentTask) {
window.game.setCurrentTask = function(task) {
console.log('🎯 Training Academy: Setting current task:', task);
if (!task || !task.text) {
console.log('❌ Invalid task provided to setCurrentTask');
return;
}
return originalSetCurrentTask.call(this, task);
};
}
// Override any method that sets currentTask to null
if (window.game.gameState) {
const originalGameState = window.game.gameState;
const currentTaskValue = originalGameState.currentTask;
Object.defineProperty(window.game.gameState, 'currentTask', {
get: function() {
return this._currentTask;
},
set: function(value) {
console.log('🎯 Training Academy: Setting gameState.currentTask:', value);
if (value && !value.text) {
console.log('❌ Preventing assignment of task without text property');
return;
}
this._currentTask = value;
}
});
// Initialize with current value
originalGameState._currentTask = currentTaskValue;
}
// 6. Override game ending to prevent immediate completion
const originalEndGame = window.game.endGame.bind(window.game);
window.game.endGame = function() {
console.log('🎓 Training Academy: Game end prevented - continuing training session');
// Don't actually end the game in training mode
return;
};
// 7. Override task display for training academy UI
const originalDisplayCurrentTask = window.game.displayCurrentTask.bind(window.game);
window.game.displayCurrentTask = function() {
console.log('🎓 Training Academy: Custom displayCurrentTask called');
console.log('📋 Current task state:', this.currentTask);
console.log('📋 Game state current task:', this.gameState?.currentTask);
// Check both possible task locations
const task = this.currentTask || this.gameState?.currentTask;
if (!task || !task.text) {
console.log('⚠️ No valid task available or missing text property');
// Show training academy specific message
const gameContainer = document.getElementById('game-container');
if (gameContainer) {
gameContainer.innerHTML = `
<div class="training-status">
<h3>🎓 Training Academy</h3>
<p>Select a training mode to begin your session.</p>
<button onclick="selectMode('training-academy')" class="mode-btn">Start Training</button>
</div>
`;
}
return; // NEVER call original method with invalid task
}
// For valid tasks, display in training academy format
const gameContainer = document.getElementById('game-container');
console.log('🎮 Game container found:', !!gameContainer);
if (gameContainer) {
// Check if it's an interactive task
const isInteractive = task.interactiveType && task.interactiveType !== 'none';
const taskTypeLabel = isInteractive ? `Interactive: ${task.interactiveType}` : task.type || 'General';
console.log('🎯 Displaying task in training academy UI...');
console.log('🎯 Task is interactive:', isInteractive);
console.log('🎯 Interactive data:', task.interactiveData);
// Handle different interactive types
if (task.interactiveType === 'scenario-adventure' && task.interactiveData) {
displayScenarioAdventure(gameContainer, task);
} else if (task.interactiveType === 'focus-hold') {
console.log('🧘 Detected focus-hold task - using interactive focus system');
displayFocusHoldTask(gameContainer, task);
} else {
// Standard task display
gameContainer.innerHTML = `
<div class="training-task">
<h3>🎯 Training Task ${task.id || 'Unknown'}</h3>
<p><strong>Difficulty:</strong> ${task.difficulty || 'Standard'}</p>
<p><strong>Type:</strong> ${taskTypeLabel}</p>
${isInteractive ? '<p><strong>Mode:</strong> Interactive Training</p>' : ''}
<div class="task-content">
${task.text}
</div>
${task.story ? `
<div class="task-story">
<p><strong>Instructions:</strong> ${task.story}</p>
</div>
` : ''}
${isInteractive ? `
<div class="interactive-notice">
<p>🎮 This is an interactive training scenario. Follow the instructions above to proceed.</p>
</div>
` : ''}
<div class="training-controls">
<button onclick="completeTrainingTask()" class="complete-btn">Complete Task</button>
<button onclick="loadNextTrainingTask()" class="next-btn">Next Task</button>
</div>
</div>
`;
}
console.log('✅ Task UI updated in game container');
return;
} else {
console.log('❌ Game container not found - creating fallback display');
// Try to create a temporary display in the body
const fallbackContainer = document.querySelector('.game-content') || document.body;
// Remove any existing task displays first
const existingDisplays = document.querySelectorAll('#training-task-display');
existingDisplays.forEach(display => display.remove());
console.log('🧹 Removed', existingDisplays.length, 'existing task displays');
// Check if it's a scenario adventure
if (task.interactiveType === 'scenario-adventure' && task.interactiveData) {
console.log('🎭 Detected scenario adventure in fallback - using scenario display');
const tempDiv = document.createElement('div');
tempDiv.id = 'training-task-display';
tempDiv.style.cssText = 'position: fixed; top: 10%; left: 50%; transform: translateX(-50%); z-index: 9999; width: 90%; max-width: 1000px;';
fallbackContainer.appendChild(tempDiv);
displayScenarioAdventure(tempDiv, task);
console.log('✅ Scenario adventure fallback display created');
return;
}
const tempDiv = document.createElement('div');
tempDiv.id = 'training-task-display';
tempDiv.style.cssText = 'position: fixed; top: 20%; left: 50%; transform: translateX(-50%); z-index: 9999; width: 80%; max-width: 800px;';
// Check if it's an interactive task
const isInteractive = task.interactiveType && task.interactiveType !== 'none';
const taskTypeLabel = isInteractive ? `Interactive: ${task.interactiveType}` : task.type || 'General';
tempDiv.innerHTML = `
<div class="training-task">
<h3>🎯 Training Task ${task.id || 'Unknown'}</h3>
<p><strong>Difficulty:</strong> ${task.difficulty || 'Standard'}</p>
<p><strong>Type:</strong> ${taskTypeLabel}</p>
${isInteractive ? '<p><strong>Mode:</strong> Interactive Training</p>' : ''}
<div class="task-content">
${task.text}
</div>
${task.story ? `
<div class="task-story">
<p><strong>Instructions:</strong> ${task.story}</p>
</div>
` : ''}
${isInteractive ? `
<div class="interactive-notice">
<p>🎮 This is an interactive training scenario. Follow the instructions above to proceed.</p>
</div>
` : ''}
<div class="training-controls">
<button onclick="completeTrainingTask()" class="complete-btn">Complete Task</button>
<button onclick="loadNextTrainingTask()" class="next-btn">Next Task</button>
</div>
</div>
`;
fallbackContainer.appendChild(tempDiv);
console.log('✅ Fallback task display created and added to DOM');
console.log('🎯 Display element:', tempDiv);
return;
}
// Only call original if we have a valid task with text
if (task && task.text) {
// Ensure gameState.currentTask is set properly before calling original
if (!this.gameState.currentTask) {
this.gameState.currentTask = task;
}
return originalDisplayCurrentTask.call(this);
}
};
// Show the game interface elements that are needed
const elementsToShow = ['game-area', 'task-area', 'current-task', 'controls-area', 'game-controls'];
elementsToShow.forEach(id => {
const element = document.getElementById(id);
if (element) element.style.display = 'block';
});
// Bypass the normal startGame flow and go directly to task loading
console.log('🎮 Starting training game with custom flow...');
try {
// Set up game state manually
window.game.gameState.isRunning = true;
window.game.gameState.isPaused = false;
window.game.gameState.startTime = Date.now();
window.game.gameState.xp = 0;
// Load the first training task directly
if (trainingTasks.length > 0) {
console.log('<27> Loading first training task directly...');
window.game.currentTaskIndex = 0;
window.game.currentTask = trainingTasks[0];
window.game.displayCurrentTask();
console.log('✅ Training task loaded successfully');
} else {
throw new Error('No training tasks available to load');
}
} catch (error) {
console.error('❌ Error in custom training flow:', error);
// Final fallback: Try the standard game start
console.log('🔄 Falling back to standard game start...');
try {
window.game.startGame();
console.log('✅ Standard game start succeeded');
} catch (e2) {
console.error('❌ Standard game start also failed:', e2);
alert('Error: Unable to start training session. Please refresh and try again.');
}
}
} else {
console.error('❌ Game instance not available');
alert('Error: Game engine not loaded properly.');
}
}, 500);
} catch (error) {
console.error('❌ Error initializing training game:', error);
alert('Error initializing training session. Please refresh the page and try again.');
}
}
// Training task button functions
function completeTrainingTask() {
console.log('✅ Training task completed by user');
try {
// Mark current task as completed
if (window.game && window.game.gameState && window.game.gameState.currentTask) {
console.log('📋 Marking current task as completed');
window.game.gameState.currentTask.completed = true;
}
// Load next task
loadNextTrainingTask();
} catch (error) {
console.error('❌ Error completing training task:', error);
// Still try to load next task
loadNextTrainingTask();
}
}
function loadNextTrainingTask() {
console.log('➡️ Loading next training task...');
try {
// Get current training tasks from multiple possible locations
let trainingTasks = window.game?.gameData?.mainTasks || [];
// If not found there, try the original training tasks we stored
if (trainingTasks.length === 0 && window.originalTrainingTasks) {
trainingTasks = window.originalTrainingTasks;
console.log('📋 Using stored original training tasks:', trainingTasks.length);
}
console.log('📋 Available training tasks:', trainingTasks.length);
console.log('📋 Task IDs:', trainingTasks.map(t => t.id));
if (trainingTasks.length === 0) {
console.log('⚠️ No training tasks found in any location');
showTrainingComplete();
return;
}
// Find current task index
const currentTask = window.game?.gameState?.currentTask || window.game?.currentTask;
let currentIndex = -1;
if (currentTask) {
currentIndex = trainingTasks.findIndex(task => task.id === currentTask.id);
console.log('📋 Current task index:', currentIndex, 'for task:', currentTask.id);
}
// Get next task
const nextIndex = currentIndex + 1;
console.log('📋 Next task index:', nextIndex, 'of', trainingTasks.length);
if (nextIndex < trainingTasks.length) {
const nextTask = trainingTasks[nextIndex];
console.log('📋 Loading next task:', nextTask.id);
// Set as current task in both locations
window.game.gameState.currentTask = nextTask;
window.game.currentTask = nextTask;
// Display the task
window.game.displayCurrentTask();
} else {
console.log('🎉 All training tasks completed!');
showTrainingComplete();
}
} catch (error) {
console.error('❌ Error loading next training task:', error);
alert('Error loading next task. Please restart the training session.');
}
}
function showTrainingComplete() {
console.log('🎓 Training session completed');
// Remove any existing task displays
const existing = document.getElementById('training-task-display');
if (existing) existing.remove();
// Clear the game container
const gameContainer = document.getElementById('game-container');
if (gameContainer) gameContainer.innerHTML = '';
// Return to mode selection by resetting the interface
returnToModeSelection();
}
function returnToModeSelection() {
console.log('🔄 Returning to training mode selection...');
// Clear any active training state
selectedTrainingMode = null;
currentScenarioTask = null;
currentScenarioStep = 'start';
// Clear any game overrides and restore original state
if (window.game) {
// Stop the game session
window.game.gameState.isRunning = false;
window.game.gameState.isPaused = false;
// Clear current task
window.game.gameState.currentTask = null;
window.game.currentTask = null;
// Reset any overridden methods if needed
if (window.gameData && window.gameData.originalMainTasks) {
window.gameData.mainTasks = window.gameData.originalMainTasks;
}
}
// Show setup interface again
document.querySelector('.academy-header').style.display = 'block';
document.querySelector('.library-status').style.display = 'block';
document.querySelector('.training-controls').style.display = 'block';
document.querySelector('.academy-start-controls').style.display = 'block';
// Hide game interface
const gameInterface = document.getElementById('gameInterface');
gameInterface.style.display = 'none';
// Reset training mode cards
document.querySelectorAll('.training-mode-card').forEach(card => {
card.classList.remove('selected');
});
// Disable start button until new mode is selected
document.getElementById('startTrainingBtn').disabled = true;
// Restore video controls visibility
const videoControls = document.getElementById('videoControlsOverlay');
if (videoControls) {
videoControls.style.opacity = '1';
}
console.log('✅ Returned to training mode selection screen');
}
// Scenario Adventure Display System
let currentScenarioStep = 'start';
let currentScenarioTask = null;
function displayScenarioAdventure(container, task) {
console.log('🎭 Displaying scenario adventure:', task.id);
console.log('🎭 Task interactive data:', task.interactiveData);
console.log('🎭 Container element:', container);
currentScenarioTask = task;
currentScenarioStep = 'start';
if (!task.interactiveData || !task.interactiveData.steps) {
console.error('❌ No scenario data found for task:', task.id);
console.error('❌ Interactive data:', task.interactiveData);
return;
}
console.log('🎭 Starting scenario from step:', currentScenarioStep);
displayScenarioStep(container, currentScenarioStep);
}
function displayScenarioStep(container, stepId) {
const scenario = currentScenarioTask.interactiveData;
const step = scenario.steps[stepId];
if (!step) {
console.error('❌ Scenario step not found:', stepId);
return;
}
console.log('🎭 Displaying scenario step:', stepId, step.type);
let stepHtml = '';
if (step.type === 'choice') {
stepHtml = `
<div class="training-task scenario-choice">
<h3>🎭 ${scenario.title}</h3>
<div class="scenario-story">
${step.story}
</div>
<div class="scenario-choices">
${step.choices.map((choice, index) => `
<div class="choice-option" onclick="selectScenarioChoice('${choice.nextStep}')">
<h4>${choice.text}</h4>
<p class="choice-preview">${choice.preview}</p>
</div>
`).join('')}
</div>
<div class="training-controls">
<button onclick="loadNextTrainingTask()" class="next-btn">Quit Scenario</button>
</div>
</div>
`;
} else if (step.type === 'text') {
stepHtml = `
<div class="training-task scenario-text">
<h3>🎭 ${scenario.title}</h3>
<div class="scenario-story">
${step.story}
</div>
<div class="training-controls">
<button onclick="selectScenarioChoice('${step.nextStep}')" class="action-btn">Continue</button>
<button onclick="loadNextTrainingTask()" class="next-btn">Quit Training</button>
</div>
</div>
`;
} else if (step.type === 'mirror-action') {
stepHtml = `
<div class="training-task scenario-mirror">
<h3>🪞 ${scenario.title}</h3>
<div class="scenario-story">
${step.story}
</div>
<div class="mirror-instructions">
<h4>📹 Mirror Instructions:</h4>
<p>${step.mirrorInstructions}</p>
</div>
<div class="mirror-task-content">
<h4>🎯 Task:</h4>
<p>${step.mirrorTaskText}</p>
<p><strong>Duration: ${step.duration} seconds</strong></p>
</div>
<div class="training-controls">
<button onclick="startMirrorAction('${step.nextStep}', ${step.duration})" class="action-btn">Start Mirror Task</button>
<button onclick="selectScenarioChoice('${step.nextStep}')" class="skip-btn">Quit Training</button>
</div>
</div>
`;
} else if (step.type === 'action') {
const duration = step.duration || 60;
const minutes = Math.floor(duration / 60);
const seconds = duration % 60;
const timeDisplay = minutes > 0 ? `${minutes}:${seconds.toString().padStart(2, '0')}` : `${seconds}s`;
// Check if this is a photo-related action
const isPhotoAction = (step.actionText && step.actionText.toLowerCase().includes('photograph')) ||
(step.story && step.story.toLowerCase().includes('photograph')) ||
(step.story && step.story.toLowerCase().includes('photo')) ||
selectedTrainingMode === 'photography-studio';
stepHtml = `
<div class="training-task scenario-action">
<h3>🎭 ${scenario.title}</h3>
<div class="scenario-story">
${step.story}
</div>
<div class="action-details">
<h4>📋 Action Required:</h4>
<p class="action-text">${step.actionText}</p>
<p class="duration-text">⏱️ Duration: ${timeDisplay}</p>
</div>
${isPhotoAction ? `
<div class="photo-section" style="margin: 20px 0;">
<div class="webcam-controls">
<div class="photo-capture-controls" style="text-align: center; margin-top: 15px;">
<button id="start-photo-session-btn" onclick="startPhotoSession()" class="start-btn">
📸 Start Photo Session
</button>
<div id="photo-progress" style="display: none; margin-top: 10px;">
<p>📸 Photos taken: <span id="photos-count">0</span> / <span id="photos-needed">3</span></p>
<button onclick="completePhotoSession()" class="end-session-btn" style="margin-top: 10px; background: #ff6b35; color: white; border: none; padding: 8px 16px; border-radius: 5px; cursor: pointer;">
🔚 End Session
</button>
</div>
</div>
</div>
</div>
` : ''}
<div class="action-timer" style="display: block;">
<p>⏱️ Time remaining: <span id="timer-display">${Math.floor(duration/60)}:${(duration%60).toString().padStart(2,'0')}</span></p>
<div class="progress-bar">
<div id="progress-fill" class="progress-fill"></div>
</div>
<p><strong>⚠️ You must complete the full timer to progress.</strong></p>
</div>
<div class="training-controls">
<button id="complete-action-btn" onclick="completeScenarioAction('${step.nextStep}')" class="complete-btn" disabled>
Action Complete (<span id="btn-timer">${Math.floor(duration/60)}:${(duration%60).toString().padStart(2,'0')}</span>)
</button>
<button onclick="selectScenarioChoice('${step.nextStep}')" class="next-btn">Quit Training</button>
</div>
</div>
`;
// Auto-start the timer immediately when displaying this step
setTimeout(() => {
startActionTimer(duration, step.nextStep, true);
}, 100); // Small delay to ensure DOM is ready
} else if (step.type === 'completion') {
stepHtml = `
<div class="training-task scenario-completion">
<h3>✅ ${scenario.title} - Complete!</h3>
<div class="scenario-story">
${step.story}
</div>
<div class="completion-outcome">
<h4>🎯 Outcome: ${step.outcome || 'Completed'}</h4>
</div>
<div class="training-controls">
<button onclick="loadNextTrainingTask()" class="complete-btn">Continue Training</button>
<button onclick="location.reload()" class="next-btn">New Session</button>
</div>
</div>
`;
} else if (step.type === 'verification-required') {
stepHtml = `
<div class="training-task scenario-verification">
<h3>🔍 ${scenario.title} - Verification Required</h3>
<div class="scenario-story">
${step.story}
</div>
<div class="verification-requirements">
<h4>📋 Position Requirements:</h4>
<p class="verification-instructions">${step.verificationInstructions}</p>
<p class="verification-text">${step.verificationText}</p>
<p class="verification-duration"><strong>Duration:</strong> ${step.verificationDuration} seconds</p>
</div>
<div class="verification-warning">
<p>⚠️ <strong>Warning:</strong> You must maintain the required position for the full duration. The webcam will verify your compliance.</p>
</div>
<div class="training-controls">
<button onclick="startPositionVerification('${step.nextStep}', ${step.verificationDuration}, '${step.verificationInstructions}', '${step.verificationText}')" class="action-btn">🔍 Start Position Verification</button>
<button onclick="selectScenarioChoice('${step.nextStep}')" class="skip-btn">Quit Training</button>
</div>
</div>
`;
} else if (step.type === 'ending') {
stepHtml = `
<div class="training-task scenario-ending">
<h3>🎉 ${scenario.title} - Complete!</h3>
<div class="scenario-story">
${step.story}
</div>
<div class="ending-details">
<h4>🏆 ${step.endingText}</h4>
<p class="outcome-text">Outcome: ${step.outcome}</p>
</div>
<div class="training-controls">
<button onclick="completeTrainingTask()" class="complete-btn">Complete Training</button>
<button onclick="loadNextTrainingTask()" class="next-btn">Next Task</button>
</div>
</div>
`;
}
container.innerHTML = stepHtml;
}
function selectScenarioChoice(nextStep) {
console.log('🎯 Selected scenario choice, moving to:', nextStep);
currentScenarioStep = nextStep;
// Find the appropriate container
const gameContainer = document.getElementById('game-container');
const fallbackContainer = document.getElementById('training-task-display');
const container = gameContainer || fallbackContainer;
if (container) {
displayScenarioStep(container, nextStep);
} else {
console.error('❌ No container found for scenario display');
}
}
function startMirrorAction(nextStep, duration) {
console.log('🪞 Starting mirror action for', duration, 'seconds');
// Store next step for completion callback
window.trainingAcademyNextStep = nextStep;
// Always start the visual timer regardless of webcam status
startActionTimer(duration, nextStep, true);
// Create mirror task data for the webcam system
const mirrorTaskData = {
mirrorInstructions: currentScenarioTask.interactiveData.steps[currentScenarioStep].mirrorInstructions,
mirrorTaskText: currentScenarioTask.interactiveData.steps[currentScenarioStep].mirrorTaskText,
duration: duration,
taskText: currentScenarioTask.interactiveData.steps[currentScenarioStep].mirrorTaskText,
onComplete: function() {
console.log('🪞 Mirror task completed by user, proceeding to:', window.trainingAcademyNextStep);
proceedToNextStep(window.trainingAcademyNextStep);
}
};
// Use the webcam manager to start mirror mode
if (window.game && window.game.webcamManager) {
console.log('🎥 Starting webcam mirror mode...');
window.game.webcamManager.startMirrorMode(mirrorTaskData).then((success) => {
if (success) {
console.log('🪞 Mirror mode started successfully');
} else {
console.warn('⚠️ Mirror mode failed to start, timer will handle completion');
}
}).catch(error => {
console.error('❌ Mirror mode failed:', error);
console.log('⚠️ Timer will handle completion');
});
} else {
console.warn('⚠️ Webcam manager not available, using timer only');
}
}
function proceedToNextStep(nextStep) {
// Prevent duplicate calls
if (window.trainingAcademyProcessing) {
console.log('🚫 Already processing next step, ignoring duplicate call');
return;
}
window.trainingAcademyProcessing = true;
setTimeout(() => {
currentScenarioStep = nextStep;
// Find the appropriate container and continue scenario
const gameContainer = document.getElementById('game-container');
const fallbackContainer = document.getElementById('training-task-display');
const container = gameContainer || fallbackContainer;
if (container) {
displayScenarioStep(container, nextStep);
}
window.trainingAcademyProcessing = false;
}, 100);
}
function startPositionVerification(nextStep, duration, instructions, verificationText) {
console.log('🔍 Starting position verification for', duration, 'seconds');
// Store next step for completion callback
window.trainingAcademyNextStep = nextStep;
// Create verification data for the webcam system
const verificationData = {
instructions: "Position verification required for training compliance",
verificationInstructions: instructions,
verificationText: verificationText,
verificationDuration: duration,
onComplete: function() {
console.log('🔍 Position verification completed, proceeding to:', window.trainingAcademyNextStep);
proceedToNextStep(window.trainingAcademyNextStep);
}
};
// Use the webcam manager to start verification mode
if (window.game && window.game.webcamManager) {
console.log('🎥 Starting webcam verification mode...');
window.game.webcamManager.startVerificationMode(verificationData).then((success) => {
if (success) {
console.log('🔍 Verification mode started successfully');
// Listen for verification completion
document.addEventListener('verificationComplete', function(event) {
if (event.detail.success) {
console.log('✅ Verification completed successfully');
proceedToNextStep(nextStep);
}
}, { once: true });
} else {
console.warn('⚠️ Verification mode failed to start');
// Show failure and allow skip or retry
alert('Camera access required for position verification. Please enable camera access.');
}
}).catch(error => {
console.error('❌ Verification mode failed:', error);
alert('Failed to start position verification. Check camera access.');
});
} else {
console.warn('⚠️ Webcam manager not available');
alert('Webcam verification system not available. Cannot proceed with position verification.');
}
}
function startScenarioAction(nextStep, duration) {
console.log('🎬 Starting scenario action for', duration, 'seconds');
// Check if current task is focus-hold type - use interactive system
if (currentScenarioTask && currentScenarioTask.interactiveType === 'focus-hold') {
console.log('🧘 Detected focus-hold task - using interactive focus system');
startFocusHoldAction(nextStep, duration);
return;
}
// For regular timed actions, the timer should already be running
// This function is now mainly a fallback if timer wasn't auto-started
console.log('⏰ Timer should already be running for this action');
// Start timer if it's not already running
if (!document.getElementById('timer-display') || document.getElementById('timer-display').textContent.includes(':')) {
startActionTimer(duration, nextStep, true);
}
}
function startFocusHoldAction(nextStep, duration) {
console.log('🧘 Starting focus-hold action for', duration, 'seconds');
// Set up focus mode
if (window.game && window.game.gameState) {
window.game.gameState.isInFocusActivity = true;
}
// Find the appropriate container
const gameContainer = document.getElementById('game-container');
const fallbackContainer = document.getElementById('training-task-display');
const container = gameContainer || fallbackContainer;
if (container && window.game && window.game.interactiveTaskManager) {
// Create a focus task object
const focusTask = {
...currentScenarioTask,
duration: duration,
instructions: currentScenarioTask.story || 'Focus and follow the instructions'
};
// Use the game's interactive task manager to create the focus task
window.game.interactiveTaskManager.createFocusTask(focusTask, container).then(() => {
console.log('🧘 Focus task created successfully');
// Override the completion to advance the scenario
overrideFocusTaskCompletion(nextStep);
});
} else {
console.error('❌ Focus system not available, falling back to standard timer');
// Fallback to standard timer
startStandardTimer(nextStep, duration);
}
}
function overrideFocusTaskCompletion(nextStep) {
// Wait for focus task to initialize then override completion
setTimeout(() => {
const completeBtn = document.querySelector('#complete-task, .complete-btn');
const skipBtn = document.querySelector('#skip-task, .next-btn');
// Hide original buttons if they exist
if (completeBtn) completeBtn.style.display = 'none';
if (skipBtn) skipBtn.style.display = 'none';
// Add scenario advancement when focus completes
const originalCompleteHandler = window.game?.interactiveTaskManager?.completeCurrentTask;
if (originalCompleteHandler) {
window.game.interactiveTaskManager.completeCurrentTask = function() {
console.log('🧘 Focus task completed - advancing scenario');
// End focus mode
if (window.game && window.game.gameState) {
window.game.gameState.isInFocusActivity = false;
}
// Advance to next scenario step
setTimeout(() => {
completeScenarioAction(nextStep);
}, 1000);
return originalCompleteHandler.call(this);
};
}
}, 1000);
}
function startStandardTimer(nextStep, duration) {
// Fallback standard timer implementation
const container = document.getElementById('game-container') || document.getElementById('training-task-display');
if (container) {
container.innerHTML = `
<div class="training-task scenario-action-progress">
<h3>🎭 Focus Training in Progress</h3>
<div class="action-timer">
<p>⏱️ Time remaining: <span id="timer-display">${Math.floor(duration/60)}:${(duration%60).toString().padStart(2,'0')}</span></p>
<div class="progress-bar">
<div id="progress-fill" class="progress-fill"></div>
</div>
</div>
<div class="action-status">
<p>🧘 <strong>Focus on the training and maintain your position...</strong></p>
<p><strong>⚠️ Complete the full duration to progress.</strong></p>
</div>
<div class="training-controls">
<button id="complete-action-btn" onclick="completeScenarioAction('${nextStep}')" class="complete-btn" disabled>
Focus Complete (<span id="btn-timer">${Math.floor(duration/60)}:${(duration%60).toString().padStart(2,'0')}</span>)
</button>
</div>
</div>
`;
startActionTimer(duration, nextStep, true);
}
}
function startActionTimer(duration, nextStep, forceComplete = false) {
let timeLeft = duration;
const timerDisplay = document.getElementById('timer-display');
const progressFill = document.getElementById('progress-fill');
const completeBtn = document.getElementById('complete-action-btn');
const btnTimer = document.getElementById('btn-timer');
console.log('⏱️ Starting action timer:', duration, 'seconds, forced:', forceComplete);
const timer = setInterval(() => {
timeLeft--;
if (timerDisplay) {
const minutes = Math.floor(timeLeft / 60);
const seconds = timeLeft % 60;
timerDisplay.textContent = `${minutes}:${seconds.toString().padStart(2, '0')}`;
}
if (btnTimer) {
const minutes = Math.floor(timeLeft / 60);
const seconds = timeLeft % 60;
btnTimer.textContent = `${minutes}:${seconds.toString().padStart(2, '0')}`;
}
if (progressFill) {
const progress = ((duration - timeLeft) / duration) * 100;
progressFill.style.width = progress + '%';
}
if (timeLeft <= 0) {
clearInterval(timer);
if (forceComplete) {
// Enable the complete button and auto-advance
if (completeBtn) {
completeBtn.disabled = false;
completeBtn.textContent = 'Action Complete - Click to Continue';
completeBtn.style.background = 'linear-gradient(45deg, #28a745, #20c997)';
}
if (timerDisplay) {
timerDisplay.textContent = 'COMPLETE!';
timerDisplay.style.color = '#28a745';
}
// Auto-advance after 2 seconds
setTimeout(() => {
completeScenarioAction(nextStep);
}, 2000);
} else {
completeScenarioAction(nextStep);
}
}
}, 1000);
// Store timer reference for potential cleanup
window.currentActionTimer = timer;
}
function completeScenarioAction(nextStep) {
console.log('✅ Scenario action completed, moving to:', nextStep);
selectScenarioChoice(nextStep);
}
function displayFocusHoldTask(container, task) {
console.log('🧘 Displaying focus-hold task:', task.id);
// Set up focus mode
if (window.game && window.game.gameState) {
window.game.gameState.isInFocusActivity = true;
}
if (window.game && window.game.interactiveTaskManager) {
// Use the interactive task manager to create the focus task
window.game.interactiveTaskManager.createFocusTask(task, container).then(() => {
console.log('🧘 Focus task created successfully');
// Override completion to advance to next training task
overrideFocusTaskForTraining();
});
} else {
console.log('⚠️ Interactive task manager not available, showing simple focus display');
// Fallback simple focus display
container.innerHTML = `
<div class="training-task focus-task-simple">
<h3>🧘 ${task.text}</h3>
<p><strong>Difficulty:</strong> ${task.difficulty}</p>
<p><strong>Type:</strong> Focus Training</p>
<div class="task-content">
${task.text}
</div>
${task.story ? `
<div class="task-story">
<p><strong>Instructions:</strong> ${task.story}</p>
</div>
` : ''}
<div class="focus-instructions">
<p>🧘 <strong>Focus training requires your complete attention.</strong></p>
<p>Duration: ${task.duration || 60} seconds</p>
</div>
<div class="training-controls">
<button onclick="startSimpleFocusSession(${task.duration || 60})" class="complete-btn">Start Focus Session</button>
<button onclick="loadNextTrainingTask()" class="next-btn">Quit Training</button>
</div>
</div>
`;
}
}
function overrideFocusTaskForTraining() {
// Override the interactive task completion to advance training
setTimeout(() => {
const originalCompleteHandler = window.game?.interactiveTaskManager?.completeCurrentTask;
if (originalCompleteHandler) {
window.game.interactiveTaskManager.completeCurrentTask = function() {
console.log('🧘 Focus task completed - advancing to next training task');
// End focus mode
if (window.game && window.game.gameState) {
window.game.gameState.isInFocusActivity = false;
}
// Advance to next training task
setTimeout(() => {
loadNextTrainingTask();
}, 1000);
return originalCompleteHandler.call(this);
};
}
}, 1000);
}
function startSimpleFocusSession(duration) {
console.log('🧘 Starting simple focus session for', duration, 'seconds');
const container = document.getElementById('game-container') || document.getElementById('training-task-display');
if (container) {
container.innerHTML = `
<div class="training-task focus-session-active">
<h3>🧘 Focus Session Active</h3>
<div class="focus-timer-large">
<span id="focus-timer-display">${Math.floor(duration/60)}:${(duration%60).toString().padStart(2,'0')}</span>
</div>
<div class="focus-instructions">
<p>🎯 <strong>Maintain your focus and position</strong></p>
<p>📱 Do not look away from the screen</p>
<p>🧘 Breathe steadily and concentrate</p>
</div>
<div class="progress-bar">
<div id="focus-progress-fill" class="progress-fill"></div>
</div>
<div class="focus-status">
<p id="focus-status-text">Focus session in progress...</p>
</div>
</div>
`;
startFocusTimer(duration);
}
}
function startFocusTimer(duration) {
let timeLeft = duration;
const timerDisplay = document.getElementById('focus-timer-display');
const progressFill = document.getElementById('focus-progress-fill');
const statusText = document.getElementById('focus-status-text');
const timer = setInterval(() => {
timeLeft--;
if (timerDisplay) {
const minutes = Math.floor(timeLeft / 60);
const seconds = timeLeft % 60;
timerDisplay.textContent = `${minutes}:${seconds.toString().padStart(2, '0')}`;
}
if (progressFill) {
const progress = ((duration - timeLeft) / duration) * 100;
progressFill.style.width = progress + '%';
}
if (statusText) {
if (timeLeft > 30) {
statusText.textContent = 'Focus session in progress... maintain concentration';
} else if (timeLeft > 10) {
statusText.textContent = 'Almost complete... keep focusing';
statusText.style.color = '#ff9800';
} else {
statusText.textContent = 'Final moments... maintain focus!';
statusText.style.color = '#f44336';
}
}
if (timeLeft <= 0) {
clearInterval(timer);
completeFocusSession();
}
}, 1000);
}
function completeFocusSession() {
console.log('🧘 Focus session completed successfully');
// End focus mode
if (window.game && window.game.gameState) {
window.game.gameState.isInFocusActivity = false;
}
const container = document.getElementById('game-container') || document.getElementById('training-task-display');
if (container) {
container.innerHTML = `
<div class="training-task focus-completed">
<h3>🎉 Focus Session Complete!</h3>
<div class="completion-message">
<p>✅ <strong>Excellent focus and concentration!</strong></p>
<p>You have successfully completed the focus training.</p>
</div>
<div class="training-controls">
<button onclick="loadNextTrainingTask()" class="complete-btn">Continue Training</button>
</div>
</div>
`;
}
}
// Initialize Training Academy
async function initializeTrainingAcademy() {
console.log('🎓 Initializing Gooner Training Academy...');
try {
// Set up training mode selection
setupTrainingModeSelection();
// Initialize libraries
await initializeVideoLibrary();
await initializePhotoLibrary();
console.log('✅ Training Academy ready');
} catch (error) {
console.error('❌ Error initializing Training Academy:', error);
}
}
// Start initialization when DOM is loaded
document.addEventListener('DOMContentLoaded', () => {
console.log('🎓 Training Academy DOM loaded');
// Wait a moment for all scripts to load
setTimeout(initializeTrainingAcademy, 1000);
});
// Listen for game ready events
window.addEventListener('gameReady', (event) => {
console.log('🎮 Game engine ready for training');
});
// Photo Session Functions for Action Steps
let photoSessionActive = false;
let photosCaptured = 0;
let photosNeeded = 3;
let currentWebcamManager = null;
async function startPhotoSession() {
console.log('📸 Starting photo session for action step');
try {
photoSessionActive = true;
photosCaptured = 0;
// Initialize webcam manager if not already done
if (!currentWebcamManager) {
currentWebcamManager = new WebcamManager();
await currentWebcamManager.init();
}
// Update UI elements
document.getElementById('start-photo-session-btn').style.display = 'none';
document.getElementById('photo-progress').style.display = 'block';
updatePhotoProgress();
// Start photo session
const sessionData = {
requirements: {
count: photosNeeded,
description: 'Take photos during this feminization session'
}
};
const success = await currentWebcamManager.startPhotoSession('dress-up-session', sessionData);
if (!success) {
throw new Error('Failed to start webcam session');
}
// Monitor the WebcamManager's captured photos array
const originalCapturedPhotosLength = currentWebcamManager.capturedPhotos.length;
const checkPhotoProgress = setInterval(() => {
const newPhotoCount = currentWebcamManager.capturedPhotos.length - originalCapturedPhotosLength;
if (newPhotoCount > photosCaptured) {
photosCaptured = newPhotoCount;
updatePhotoProgress();
// Check if we've reached the target
if (photosCaptured >= photosNeeded) {
clearInterval(checkPhotoProgress);
setTimeout(completePhotoSession, 1500); // Delay to show final photo
}
}
}, 500); // Check every 500ms
// Store the interval so we can clear it if needed
window.photoProgressInterval = checkPhotoProgress;
console.log('📸 Photo session started successfully');
} catch (error) {
console.error('❌ Failed to start photo session:', error);
alert('Failed to start camera. Please ensure camera access is allowed.');
photoSessionActive = false;
// Clear any intervals that might have been set
if (window.photoProgressInterval) {
clearInterval(window.photoProgressInterval);
window.photoProgressInterval = null;
}
// Reset UI
document.getElementById('start-photo-session-btn').style.display = 'block';
document.getElementById('photo-progress').style.display = 'none';
}
}
function capturePhotoForSession() {
if (!photoSessionActive || !currentWebcamManager) {
return;
}
console.log('📸 Capturing photo for session');
// Increment photo count
photosCaptured++;
updatePhotoProgress();
// Check if we've taken enough photos
if (photosCaptured >= photosNeeded) {
setTimeout(completePhotoSession, 1000); // Small delay to show the final photo
}
}
function updatePhotoProgress() {
const photosCountEl = document.getElementById('photos-count');
const photosNeededEl = document.getElementById('photos-needed');
if (photosCountEl) photosCountEl.textContent = photosCaptured;
if (photosNeededEl) photosNeededEl.textContent = photosNeeded;
}
function completePhotoSession() {
console.log('📸 Photo session completed');
photoSessionActive = false;
// Clear the photo progress monitoring interval
if (window.photoProgressInterval) {
clearInterval(window.photoProgressInterval);
window.photoProgressInterval = null;
}
// Get newly captured photos from WebcamManager
const capturedPhotos = currentWebcamManager ? currentWebcamManager.capturedPhotos : [];
console.log(`📸 Found ${capturedPhotos.length} captured photos`);
// Add captured photos to training photo library
if (capturedPhotos.length > 0) {
addCapturedPhotosToLibrary(capturedPhotos);
}
// Show completion message
const progressDiv = document.getElementById('photo-progress');
if (progressDiv) {
progressDiv.innerHTML = `
<div style="color: #4CAF50; font-weight: bold; margin-top: 10px;">
✅ Photo session complete! ${photosCaptured} photos captured and added to gallery.
</div>
<button onclick="showCapturedPhotos()" style="margin-top: 10px; padding: 8px 16px; background: #2196F3; color: white; border: none; border-radius: 5px; cursor: pointer;">
📖 View Captured Photos
</button>
`;
}
// Close webcam interface
if (currentWebcamManager) {
// Stop the camera using WebcamManager's method
currentWebcamManager.stopCamera();
// Remove the camera overlay created by WebcamManager
const overlay = document.getElementById('camera-overlay');
if (overlay) {
overlay.remove();
console.log('📸 Camera overlay removed');
}
// Reset webcam manager state
currentWebcamManager.isActive = false;
currentWebcamManager.currentPhotoSession = null;
}
}
// Add captured photos to the training photo library
function addCapturedPhotosToLibrary(capturedPhotos) {
const newPhotos = [];
// Get existing photos from localStorage
const existingPhotos = JSON.parse(localStorage.getItem('capturedPhotos') || '[]');
capturedPhotos.forEach((photo, index) => {
if (photo.dataURL) {
const photoData = {
filename: `captured_photo_${Date.now()}_${index}.jpg`,
path: photo.dataURL, // Use data URL as path for webcam photos
fullPath: photo.dataURL,
isWebcamCapture: true,
timestamp: photo.timestamp || Date.now(),
sessionType: photo.sessionType || 'dress-up-session',
imageData: photo.dataURL // This is what the main gallery expects
};
newPhotos.push(photoData);
trainingPhotoLibrary.push(photoData);
existingPhotos.push(photoData); // Add to localStorage format
}
});
// Save updated photos to localStorage for main gallery
localStorage.setItem('capturedPhotos', JSON.stringify(existingPhotos));
console.log(`📸 Added ${newPhotos.length} photos to training library and localStorage`);
console.log(`📸 Total photos in localStorage: ${existingPhotos.length}`);
// Update photo library status
const statusEl = document.getElementById('photoLibraryStatus');
if (statusEl) {
statusEl.innerHTML = `<span style="color: #27ae60;">✅ ${trainingPhotoLibrary.length} photos available (${newPhotos.length} newly captured)</span>`;
}
// Save to localStorage for persistence
try {
const existingPhotos = JSON.parse(localStorage.getItem('trainingAcademyPhotos') || '[]');
existingPhotos.push(...newPhotos);
localStorage.setItem('trainingAcademyPhotos', JSON.stringify(existingPhotos));
console.log('📸 Photos saved to localStorage for persistence');
} catch (error) {
console.warn('⚠️ Failed to save photos to localStorage:', error);
}
}
// Show captured photos in a gallery view
function showCapturedPhotos() {
const webcamPhotos = trainingPhotoLibrary.filter(photo => photo.isWebcamCapture);
if (webcamPhotos.length === 0) {
alert('No captured photos found in the gallery.');
return;
}
const galleryHtml = `
<div style="position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.9); z-index: 10000; display: flex; flex-direction: column; align-items: center; justify-content: center;">
<h2 style="color: white; margin-bottom: 20px;">📸 Captured Photos Gallery</h2>
<div style="display: flex; flex-wrap: wrap; gap: 15px; max-width: 90%; max-height: 70%; overflow-y: auto; justify-content: center;">
${webcamPhotos.map((photo, index) => `
<div style="border: 2px solid #fff; border-radius: 10px; overflow: hidden;">
<img src="${photo.path}" style="width: 200px; height: 150px; object-fit: cover;" alt="Captured photo ${index + 1}">
<div style="background: rgba(0,0,0,0.8); color: white; padding: 5px; text-align: center; font-size: 12px;">
Photo ${index + 1} - ${new Date(photo.timestamp).toLocaleTimeString()}
</div>
</div>
`).join('')}
</div>
<button onclick="this.parentElement.remove()" style="margin-top: 20px; padding: 10px 20px; background: #f44336; color: white; border: none; border-radius: 5px; cursor: pointer; font-size: 16px;">
❌ Close Gallery
</button>
</div>
`;
document.body.insertAdjacentHTML('beforeend', galleryHtml);
}
// Show all photos in the training library
function showAllPhotos() {
if (trainingPhotoLibrary.length === 0) {
alert('No photos found in the gallery.');
return;
}
const webcamPhotos = trainingPhotoLibrary.filter(photo => photo.isWebcamCapture);
const filePhotos = trainingPhotoLibrary.filter(photo => !photo.isWebcamCapture);
const galleryHtml = `
<div style="position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.9); z-index: 10000; display: flex; flex-direction: column; align-items: center; justify-content: flex-start; overflow-y: auto; padding: 20px;">
<h2 style="color: white; margin-bottom: 20px;">📸 Complete Photo Gallery</h2>
${webcamPhotos.length > 0 ? `
<div style="width: 100%; max-width: 1200px;">
<h3 style="color: #ff1493; margin-bottom: 15px;">📷 Captured Photos (${webcamPhotos.length})</h3>
<div style="display: flex; flex-wrap: wrap; gap: 15px; margin-bottom: 30px; justify-content: center;">
${webcamPhotos.map((photo, index) => `
<div style="border: 2px solid #ff1493; border-radius: 10px; overflow: hidden;">
<img src="${photo.path}" style="width: 200px; height: 150px; object-fit: cover;" alt="Captured photo ${index + 1}">
<div style="background: rgba(255,20,147,0.8); color: white; padding: 5px; text-align: center; font-size: 12px;">
Captured - ${new Date(photo.timestamp).toLocaleDateString()}
</div>
</div>
`).join('')}
</div>
</div>
` : ''}
${filePhotos.length > 0 ? `
<div style="width: 100%; max-width: 1200px;">
<h3 style="color: #27ae60; margin-bottom: 15px;">📁 Library Photos (${filePhotos.length})</h3>
<div style="display: flex; flex-wrap: wrap; gap: 15px; margin-bottom: 20px; justify-content: center;">
${filePhotos.slice(0, 20).map((photo, index) => `
<div style="border: 2px solid #27ae60; border-radius: 10px; overflow: hidden;">
<img src="file://${photo.fullPath}" style="width: 200px; height: 150px; object-fit: cover;" alt="Library photo ${index + 1}">
<div style="background: rgba(39,174,96,0.8); color: white; padding: 5px; text-align: center; font-size: 12px;">
${photo.filename}
</div>
</div>
`).join('')}
</div>
${filePhotos.length > 20 ? `<p style="color: #ccc; text-align: center;">... and ${filePhotos.length - 20} more photos</p>` : ''}
</div>
` : ''}
<button onclick="this.parentElement.remove()" style="margin-top: 20px; padding: 10px 20px; background: #f44336; color: white; border: none; border-radius: 5px; cursor: pointer; font-size: 16px;">
❌ Close Gallery
</button>
</div>
`;
document.body.insertAdjacentHTML('beforeend', galleryHtml);
}
</script>
</body>
</html>