2920 lines
137 KiB
HTML
2920 lines
137 KiB
HTML
<!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;
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<!-- Background Video Container -->
|
||
<div class="training-video-container" id="trainingVideoContainer">
|
||
<!-- Video will be inserted here -->
|
||
</div>
|
||
|
||
<!-- Video Controls Overlay -->
|
||
<div class="video-controls-overlay" id="videoControlsOverlay">
|
||
<button onclick="skipToNextVideo()">Next Video</button>
|
||
<button onclick="toggleVideoOpacity()">Opacity</button>
|
||
<button onclick="toggleVideoPlayback()">Play/Pause</button>
|
||
</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;">Skip Task</button>
|
||
<button id="pause-btn" style="display: none;">Pause</button>
|
||
<button id="quit-btn" style="display: none;">Quit</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/aiTaskManager.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;
|
||
|
||
// 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');
|
||
document.getElementById('videoControlsOverlay').style.display = 'block';
|
||
};
|
||
|
||
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) {
|
||
video.src = `file://${nextVideo.fullPath}`;
|
||
console.log(`🎬 Switched to: ${nextVideo.name}`);
|
||
}
|
||
}
|
||
|
||
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');
|
||
}
|
||
}
|
||
}
|
||
|
||
// 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">Skip 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">Skip Scenario</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">Skip</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">Skip to Next</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">Skip (Failure)</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">Skip Task</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> |