17 KiB
Level Structure Template
Overview
This document defines the standard structure and components for creating Training Academy campaign levels. Follow this template to ensure consistency across all levels.
Level Data Structure
Basic Level Properties
{
id: 'academy-level-X', // Unique identifier (academy-level-1, academy-level-2, etc.)
name: 'Level Title', // Display name (e.g., "Edge Training 101")
arc: 'Arc Name', // Story arc (Foundation, Immersion, Advanced Training, etc.)
level: X, // Level number (1, 2, 3, etc.)
duration: 300, // Estimated duration in seconds
interactiveType: 'scenario-adventure', // Always use this for campaign levels
interactiveData: {
// Scenario data here...
}
}
Scenario Structure
Interactive Data Template
interactiveData: {
title: 'Level X: Level Title', // Match the level name
steps: {
// Steps defined here...
}
}
Step Types
1. Story Step (Narrative/Dialogue)
Purpose: Display story text without user interaction beyond continuing
start: {
type: 'story',
mood: 'welcoming', // Options: welcoming, instructional, praising, anticipatory, intense, etc.
story: 'Your story text here. This displays to the player.',
nextStep: 'next_step_id'
}
Common Moods:
welcoming- Friendly introductioninstructional- Teaching/guidancepraising- Positive reinforcementanticipatory- Building excitementintense- High energy/challengesatisfied- Completion/success
2. Action Step (Interactive Task)
Purpose: Execute an interactive task (edge, rhythm, library management, etc.)
step_id: {
type: 'action',
mood: 'instructional',
story: 'Explanation of what the player will do.',
interactiveType: 'edge', // Type of interactive task
params: {
// Task-specific parameters
count: 5,
instruction: 'Edge 5 times - slowly and deliberately',
showTimer: true, // ALWAYS include for countdown timer display
preserveContent: false // Set to true if you want to append to existing content
},
nextStep: 'next_step_id'
}
3. Completion Step
Purpose: Mark level as complete and show completion message
completion: {
type: 'completion',
mood: 'satisfied',
story: '✅ Level X Complete - Summary of achievements.',
outcome: 'levelX_complete'
}
Standard Interactive Task Types
Edge Task
Purpose: Edge training - stroke to the brink without release
{
type: 'action',
mood: 'instructional',
story: 'Edge training instructions.',
interactiveType: 'edge',
params: {
count: 5, // Number of edges (optional)
duration: 300, // Duration in seconds (optional)
instruction: 'Edge 5 times - slowly and deliberately',
showTimer: true, // Show countdown timer in sidebar
preserveContent: false, // Whether to preserve existing content
keepVideoPlaying: false // Keep video player active
},
nextStep: 'next_step_id'
}
Components Created:
- Edge instruction display
- Start button
- Dev skip button (auto-shown in dev mode)
- Session duration display
- Sidebar countdown timer (if showTimer: true)
- Status messages
Rhythm Task
Purpose: Follow specific stroking rhythm patterns
{
type: 'action',
mood: 'focused',
story: 'Follow the rhythm pattern.',
interactiveType: 'rhythm',
params: {
pattern: 'slow-fast-slow', // Pattern name (see patterns below)
duration: 180, // Duration in seconds
enableVideo: true, // Enable video playback
enableMetronomeSound: true, // Play metronome tick sounds
showTimer: true, // Show countdown timer in sidebar
multiPattern: [ // Optional: Multiple patterns in sequence
{ pattern: 'fast-slow-fast', duration: 300 },
{ pattern: 'varied-medium', duration: 360 }
]
},
nextStep: 'next_step_id'
}
Available Patterns:
slow-fast-slow- [60, 80, 100, 120, 140, 120, 100, 80, 60] BPMfast-slow-fast- [140, 120, 100, 80, 60, 80, 100, 120, 140] BPMsteady- [90, 90, 90, 90, 90, 90] BPMescalating- [60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160, 170, 180] BPMvaried-slow- [70, 80, 90, 100, 0, 80, 90, 110, 100, 0, 70, 85, 95, 80, 100] BPMvaried-medium- [100, 120, 140, 160, 150, 0, 130, 150, 170, 140, 180, 0, 120, 160, 140, 180, 200] BPMvaried-intense- [140, 160, 180, 200, 0, 160, 180, 200, 220, 0, 180, 200, 220, 240, 0, 200, 220, 180, 240] BPM
Components Created:
- Pattern title display
- BPM display
- Phase indicator
- Metronome visual (pulsing indicator)
- Video player (if enableVideo: true)
- Video controls (skip, volume)
- Metronome volume control
- Dev skip button
- Sidebar countdown timer
Rhythm Training Task (Advanced)
Purpose: Advanced rhythm training with custom tempo sequences
{
type: 'action',
mood: 'intense',
story: 'Advanced rhythm training with varying tempo.',
interactiveType: 'rhythm-training',
params: {
duration: 180,
enableVideo: false,
showTimer: true,
tempoSequence: [
{ tempo: 60, duration: 25.71 }, // Slow warmup
{ tempo: 120, duration: 25.71 }, // Medium
{ tempo: 150, duration: 25.71 }, // Fast
{ tempo: 60, duration: 25.71 }, // Back to slow
{ tempo: 150, duration: 25.71 }, // Fast again
{ tempo: 200, duration: 25.71 }, // Intense
{ tempo: 240, duration: 25.71 } // Extreme finish
]
},
nextStep: 'next_step_id'
}
Components Created:
- Beat timeline visualization (scrolling beats)
- Current BPM display
- Tempo label
- Video player (optional)
- Volume controls
- Dev skip button
- Sidebar countdown timer
Add Library Directory Task
Purpose: Add media directory to library
{
type: 'action',
mood: 'practical',
story: 'Add content to your library.',
interactiveType: 'add-library-directory',
params: {
suggestedTags: ['amateur', 'solo'],
showTimer: true
},
nextStep: 'next_step_id'
}
Components Created:
- Select directory button
- Suggested tags display
- Result status area
- Automatic completion on success
Tag Files Task
Purpose: Tag files in the library
{
type: 'action',
mood: 'practical',
story: 'Tag files in your library.',
interactiveType: 'tag-files',
params: {
minFiles: 10,
suggestedTags: ['pov', 'blowjob', 'riding', 'amateur'],
showTimer: true,
preserveContent: true // Keep existing content visible
},
nextStep: 'next_step_id'
}
Components Created:
- Open library button
- Tag progress tracker
- Suggested tags display
- Auto-completion when target reached
Video Start Task
Purpose: Start video playback
{
type: 'action',
mood: 'intense',
story: 'Watch the video.',
interactiveType: 'video-start',
params: {
player: 'focus', // Video player mode
tags: ['amateur'], // Filter tags
duration: 120, // Duration in seconds
showTimer: true // Show countdown timer in sidebar
},
nextStep: 'next_step_id'
}
Components Created:
- Video player container
- Start video button
- Dev skip button
- Sidebar countdown timer
- Auto-completion when duration complete
Standard UI Components
1. Sidebar Countdown Timer
Always Preserve Game Stats Panel
// ✅ CORRECT - Use specific timer elements
const countdownEl = document.getElementById('sidebar-countdown-display');
const countdownWrapper = document.getElementById('sidebar-countdown-timer');
// Show timer
if (countdownWrapper) {
countdownWrapper.style.display = 'block';
}
// Update timer
if (countdownEl) {
const minutes = Math.floor(timeRemaining / 60);
const seconds = timeRemaining % 60;
countdownEl.textContent = `${minutes}:${seconds.toString().padStart(2, '0')}`;
}
// Hide timer
if (countdownWrapper) {
countdownWrapper.style.display = 'none';
}
// ❌ WRONG - Never use sidebar.innerHTML
// This destroys the game stats panel!
sidebar.innerHTML = '<div>Timer</div>'; // DON'T DO THIS
Required Elements:
#sidebar-countdown-timer- Wrapper element (show/hide)#sidebar-countdown-display- Text element (update time)#game-stats-panel- Stats panel (NEVER destroy this)
See: docs/GAME_STATS_PANEL_FIX.md for complete guide
2. Dev Skip Button
Standard Implementation
// HTML
<button class="btn btn-warning skip-task-btn" id="skip-[task]-btn" style="display: none;">
⏩ Skip Task (Dev)
</button>
// JavaScript
const skipBtn = container.querySelector('#skip-[task]-btn');
// Show if dev mode
if (window.isDevMode && window.isDevMode()) {
skipBtn.style.display = 'inline-block';
}
// Handler
skipBtn.addEventListener('click', () => {
console.log('⏩ Dev skip - completing task');
// Clean up intervals/timers
if (timerInterval) clearInterval(timerInterval);
if (beatTimer) clearInterval(beatTimer);
// Stop audio
if (audioContext) audioContext.close();
if (ambientAudio) ambientAudio.pause();
// Hide countdown timer
if (countdownWrapper) {
countdownWrapper.style.display = 'none';
}
// Mark complete
task.completed = true;
// Enable continue button
const completeBtn = document.getElementById('interactive-complete-btn');
if (completeBtn) {
completeBtn.disabled = false;
completeBtn.textContent = 'Continue to Next Step ✓';
}
});
Required Cleanup:
- Clear all intervals and timers
- Stop all audio (metronome, ambient, video)
- Hide countdown timer
- Mark task as completed
- Enable continue button
3. Task Completion Pattern
Standard Completion Flow
// When task naturally completes
task.completed = true;
// Update UI
statusArea.innerHTML = '<div class="success">✅ Task complete!</div>';
// Hide countdown timer
if (showTimer && countdownWrapper) {
countdownWrapper.style.display = 'none';
}
// Enable continue button
const completeBtn = document.getElementById('interactive-complete-btn');
if (completeBtn) {
completeBtn.disabled = false;
completeBtn.textContent = 'Continue to Next Step ✓';
completeBtn.style.background = 'linear-gradient(135deg, var(--color-success), var(--color-primary))';
}
4. Cleanup Function
Every Task Must Have Cleanup
// Store cleanup function on task
task.cleanup = () => {
console.log('🧹 Task cleanup called');
// Clear intervals
if (timerInterval) clearInterval(timerInterval);
if (beatTimer) clearInterval(beatTimer);
// Stop audio
if (audioContext) {
audioContext.close().catch(err => console.warn('⚠️ Error closing audio:', err));
}
if (ambientAudio) {
ambientAudio.pause();
ambientAudio = null;
}
// Stop video
if (videoElement) {
videoElement.pause();
videoElement = null;
}
// Hide countdown timer
const countdownTimer = document.getElementById('sidebar-countdown-timer');
if (countdownTimer) {
countdownTimer.style.display = 'none';
}
console.log('✅ Cleanup complete');
};
Complete Level Example
Level 1: Edge Training 101
level1: {
id: 'academy-level-1',
name: 'Edge Training 101',
arc: 'Foundation',
level: 1,
duration: 300,
interactiveType: 'scenario-adventure',
interactiveData: {
title: 'Level 1: Edge Training 101',
steps: {
start: {
type: 'story',
mood: 'welcoming',
story: 'Welcome to The Academy, aspiring gooner. I am your instructor, and I will guide your transformation. Today, we begin with the foundation of all gooning: the edge. You will learn to ride the wave of pleasure without release. Are you ready to begin your journey?',
nextStep: 'intro_complete'
},
intro_complete: {
type: 'action',
mood: 'instructional',
story: 'Your first lesson is simple: edge 5 times. Stroke yourself to the very brink, feel that rush of pleasure, then stop. Hold yourself there. Feel it pulsing, demanding release. But you will not give in. This is control. This is the beginning.',
interactiveType: 'edge',
params: {
count: 5,
instruction: 'Edge 5 times - slowly and deliberately',
showTimer: true
},
nextStep: 'first_edges_done'
},
first_edges_done: {
type: 'story',
mood: 'praising',
story: 'Good. Very good. You felt it, didn\'t you? That moment where your body screamed for release, but your mind held firm. That is power. That is what we are building here. You have completed your first training session. Level 2 awaits when you\'re ready.',
nextStep: 'completion'
},
completion: {
type: 'completion',
mood: 'satisfied',
story: '✅ Level 1 Complete - You have taken your first steps into The Academy.',
outcome: 'level1_complete'
}
}
}
}
Step Flow Diagram
[start] (story)
↓
[intro_complete] (action - edge task)
↓
[first_edges_done] (story)
↓
[completion] (completion)
Critical Rules
✅ DO:
- Always include
showTimer: truein task params - Use specific timer elements (
#sidebar-countdown-display,#sidebar-countdown-timer) - Add dev skip buttons to all timed tasks
- Include cleanup functions for all interactive tasks
- Clear all intervals/timers in cleanup and skip handlers
- Hide countdown timer when task completes
- Enable continue button when task completes
- Use consistent step naming (start, completion)
- Include mood indicators for atmosphere
- Provide clear instructions in story text
❌ DON'T:
- Use
sidebar.innerHTML(destroys game stats panel) - Forget to clear intervals in cleanup
- Forget to hide countdown timer when complete
- Create tasks without cleanup functions
- Use generic button IDs that might conflict
- Forget to check for element existence before using
- Hardcode values that should be parameters
- Skip the dev skip button implementation
- Forget to stop audio in cleanup
Testing Checklist
When creating a new level, verify:
- Game stats panel stays visible throughout all tasks
- Countdown timer appears when tasks start
- Countdown timer updates every second
- Countdown timer hides when task completes
- Dev skip button appears in dev mode (
localStorage.setItem('devMode', 'true')) - Dev skip button completes task immediately
- All intervals/timers are cleared on completion
- All intervals/timers are cleared on skip
- All audio stops on completion
- All audio stops on skip
- Continue button enables when task completes
- Level transitions smoothly to next step
- Story text displays correctly
- Moods are appropriate for each step
- Task parameters work as expected
- No console errors during execution
- Cleanup function executes without errors
Quick Reference
Common Parameters
params: {
showTimer: true, // Show countdown timer
preserveContent: false, // Preserve existing content
duration: 180, // Duration in seconds
enableVideo: true, // Enable video player
suggestedTags: [], // Suggested tags array
instruction: '', // Custom instruction text
}
Common Element IDs
#sidebar-countdown-timer- Timer wrapper (show/hide)#sidebar-countdown-display- Timer text (update)#game-stats-panel- Stats panel (preserve)#interactive-complete-btn- Continue button (enable).skip-task-btn- Dev skip button class
Common Event Handlers
- Start button click → Begin task
- Skip button click → Complete immediately
- Timer interval → Update countdown
- Completion → Clean up and enable continue
Resources
- Game Stats Panel Fix:
docs/GAME_STATS_PANEL_FIX.md - Rhythm Timeline System:
docs/rhythm-timeline/README.md - Training Modules:
docs/TRAINING_MODULES.md - Level Breakdowns:
docs/LEVELS_1-15_BREAKDOWN.md,docs/LEVELS_16-20_BREAKDOWN.md
Version History
- v1.0 (2025-12-06) - Initial template based on Levels 1-3 structure