18 KiB
Phase 6: Stats/Achievements/Library Systems
Priority: MEDIUM - Cross-cutting systems for engagement
Estimated Effort: 14-18 hours
Status: Not Started
Dependencies: Phase 1 (for library foundation), can run parallel with Phase 2-5
🎯 Phase Goals
Implement comprehensive statistics tracking, achievement system, and advanced library management. These cross-cutting systems enhance engagement throughout the entire campaign and provide depth for post-graduation content.
📋 What This Phase Delivers
Core Systems:
- Statistics Dashboard - Track all Academy metrics
- Achievement System - 25+ achievements with unlock notifications
- Advanced Library Management - Quality scoring, diversity scoring, smart playlists
- Curator Rank Progression - Novice → Master with benefits
- Stats Visualization - Charts, graphs, progress bars
- Export/Import - Save/load library data, share achievements
End Result: Rich meta-game layer that rewards progress and curation.
🏗️ Implementation Tasks
Task 1: Achievement System (5-6 hours)
File to Create: src/features/academy/progressionManager.js
Responsibilities:
- Define all achievements
- Track achievement progress
- Unlock achievements
- Display achievement notifications
- Achievement showcase
Achievement Definitions:
const achievements = [
// Progression Achievements
{
id: 'first_level',
name: 'First Steps',
description: 'Complete Level 1',
icon: '🎓',
category: 'progression',
condition: (stats) => stats.completedLevels.includes(1),
reward: null
},
{
id: 'foundation_complete',
name: 'Foundation Graduate',
description: 'Complete the Foundation Arc (L1-5)',
icon: '🏛️',
category: 'progression',
condition: (stats) => stats.completedLevels.length >= 5,
reward: null
},
{
id: 'halfway',
name: 'Halfway There',
description: 'Complete 15 levels',
icon: '🔥',
category: 'progression',
condition: (stats) => stats.completedLevels.length >= 15,
reward: null
},
{
id: 'all_30',
name: 'Academy Graduate',
description: 'Complete all 30 levels',
icon: '🎓',
category: 'progression',
condition: (stats) => stats.graduationCompleted,
reward: 'Certificate of Completion'
},
// Edging Achievements
{
id: 'edge_100',
name: 'Century Club',
description: 'Edge 100 times total',
icon: '💯',
category: 'edging',
condition: (stats) => stats.totalEdges >= 100,
reward: null
},
{
id: 'edge_500',
name: 'Edge Master',
description: 'Edge 500 times total',
icon: '🔥',
category: 'edging',
condition: (stats) => stats.totalEdges >= 500,
reward: null
},
{
id: 'edge_1000',
name: 'Edge Legend',
description: 'Edge 1000 times total',
icon: '⭐',
category: 'edging',
condition: (stats) => stats.totalEdges >= 1000,
reward: null
},
// Time Achievements
{
id: 'time_10h',
name: '10 Hours Strong',
description: 'Goon for 10 hours total',
icon: '⏰',
category: 'time',
condition: (stats) => stats.totalSessionTime >= 36000, // 10 hours in seconds
reward: null
},
{
id: 'time_50h',
name: 'Dedicated Gooner',
description: 'Goon for 50 hours total',
icon: '⏱️',
category: 'time',
condition: (stats) => stats.totalSessionTime >= 180000, // 50 hours
reward: null
},
{
id: 'time_100h',
name: 'Gooner for Life',
description: 'Goon for 100 hours total',
icon: '🏆',
category: 'time',
condition: (stats) => stats.totalSessionTime >= 360000, // 100 hours
reward: 'Honorary Title: Eternal Gooner'
},
// Feature Achievements
{
id: 'webcam_unlock',
name: 'The Observer',
description: 'Unlock webcam feature',
icon: '📹',
category: 'features',
condition: (stats) => stats.featuresUnlocked.includes('webcam'),
reward: null
},
{
id: 'all_features',
name: 'Feature Complete',
description: 'Unlock all features',
icon: '🎯',
category: 'features',
condition: (stats) => stats.featuresUnlocked.length >= 10,
reward: null
},
// Library Achievements
{
id: 'first_directory',
name: 'Curator Initiate',
description: 'Add your first directory to the library',
icon: '📁',
category: 'library',
condition: (stats) => stats.library.directories.length >= 1,
reward: null
},
{
id: 'tag_100',
name: 'Tagger',
description: 'Tag 100 files',
icon: '🏷️',
category: 'library',
condition: (stats) => stats.library.taggedFiles >= 100,
reward: null
},
{
id: 'tag_500',
name: 'Master Tagger',
description: 'Tag 500 files',
icon: '🏷️',
category: 'library',
condition: (stats) => stats.library.taggedFiles >= 500,
reward: null
},
{
id: 'coverage_90',
name: 'Meticulous Curator',
description: 'Reach 90% tag coverage',
icon: '✨',
category: 'library',
condition: (stats) => stats.library.tagCoverage >= 90,
reward: null
},
{
id: 'curator_master',
name: 'Master Curator',
description: 'Achieve Master curator rank',
icon: '👑',
category: 'library',
condition: (stats) => stats.library.curatorRank === 'Master',
reward: 'Access to advanced library features'
},
// Challenge Achievements
{
id: 'marathon_first',
name: 'Marathon Runner',
description: 'Complete your first marathon session (15x)',
icon: '🏃',
category: 'challenge',
condition: (stats) => stats.marathonSessionsCompleted >= 1,
reward: null
},
{
id: 'no_failures',
name: 'Flawless',
description: 'Complete 10 levels without any failures',
icon: '💎',
category: 'challenge',
condition: (stats) => stats.consecutiveLevelsWithoutFailure >= 10,
reward: null
},
{
id: 'path_master',
name: 'Path Master',
description: 'Complete your chosen path (L22-25)',
icon: '🛤️',
category: 'challenge',
condition: (stats) => stats.pathLevelsCompleted >= 4,
reward: null
},
// Hidden Achievements
{
id: 'secret_ascended',
name: 'Ascended',
description: 'Complete 5 Ascended Mode levels',
icon: '🌟',
category: 'hidden',
hidden: true,
condition: (stats) => stats.ascendedLevelsCompleted >= 5,
reward: 'Secret reward'
},
{
id: 'secret_perfectionist',
name: 'Perfectionist',
description: 'Complete all 30 levels without any failures',
icon: '🏆',
category: 'hidden',
hidden: true,
condition: (stats) => stats.completedLevels.length === 30 && stats.totalFailedAttempts === 0,
reward: 'Ultimate gooner title'
}
// Total: 25+ achievements
];
Methods:
class ProgressionManager {
checkAchievements(stats) {
// Loop through all achievements
// Check conditions
// Unlock new achievements
// Return newly unlocked achievements
}
unlockAchievement(achievementId) {
// Set achievement as unlocked
// Show notification
// Save to gameData
}
getUnlockedAchievements() {
// Return list of unlocked achievements
}
getAchievementProgress(achievementId) {
// For progressive achievements, return % progress
}
displayAchievementNotification(achievement) {
// Show toast notification
// "Achievement Unlocked: [NAME]"
}
}
Achievement Notification UI:
<div id="achievement-toast" class="toast hidden">
<div class="achievement-icon"></div>
<div class="achievement-content">
<h4>Achievement Unlocked!</h4>
<p class="achievement-name"></p>
<p class="achievement-description"></p>
</div>
</div>
Testing Checklist:
- All 25+ achievements defined
- Achievement conditions check correctly
- Achievements unlock at right moments
- Notifications display
- Achievements persist
- Hidden achievements stay hidden until unlocked
Task 2: Statistics Dashboard (4-5 hours)
File to Update: src/features/academy/progressionManager.js
File to Create: src/features/academy/statsVisualizer.js
Stats to Track:
const academyStats = {
// Progression
currentLevel: 1,
highestUnlockedLevel: 1,
completedLevels: [],
arcsCompleted: [],
// Time
totalSessionTime: 0, // seconds
longestSession: 0, // seconds
averageSessionLength: 0, // seconds
lastPlayedDate: null,
// Edging
totalEdges: 0,
edgesPerLevel: {}, // { level: count }
averageEdgesPerSession: 0,
// Features
featuresUnlocked: [],
webcamTimeUsed: 0,
ttsCommandsHeard: 0,
hypnoTimeWatched: 0,
captionsDisplayed: 0,
interruptionsCompleted: 0,
popupsViewed: 0,
// Library
library: {
totalFiles: 0,
taggedFiles: 0,
tagCoverage: 0,
curatorRank: 'Novice',
directoriesAdded: 0
},
// Path
selectedPath: null,
pathLevelsCompleted: 0,
// Failures
totalFailedAttempts: 0,
failuresByReason: {
cumming: 0,
abandoned: 0,
featureClosed: 0
},
consecutiveLevelsWithoutFailure: 0,
// Achievements
achievementsUnlocked: [],
achievementProgress: {}
};
Stats Dashboard UI:
<div id="stats-dashboard">
<h2>Academy Statistics</h2>
<section id="progression-stats">
<h3>Progression</h3>
<p>Current Level: <strong id="current-level"></strong></p>
<p>Completed Levels: <strong id="completed-levels"></strong> / 30</p>
<div class="progress-bar">
<div class="progress-fill" id="level-progress"></div>
</div>
<p>Arcs Completed: <strong id="arcs-completed"></strong></p>
</section>
<section id="time-stats">
<h3>Time Investment</h3>
<p>Total Time: <strong id="total-time"></strong></p>
<p>Longest Session: <strong id="longest-session"></strong></p>
<p>Average Session: <strong id="average-session"></strong></p>
</section>
<section id="edge-stats">
<h3>Edging Statistics</h3>
<p>Total Edges: <strong id="total-edges"></strong></p>
<p>Average per Session: <strong id="avg-edges"></strong></p>
<canvas id="edges-chart"></canvas>
</section>
<section id="feature-stats">
<h3>Feature Usage</h3>
<ul>
<li>Webcam Time: <span id="webcam-time"></span></li>
<li>TTS Commands: <span id="tts-commands"></span></li>
<li>Hypno Time: <span id="hypno-time"></span></li>
<li>Captions Shown: <span id="captions"></span></li>
<li>Interruptions: <span id="interruptions"></span></li>
<li>Popups Viewed: <span id="popups"></span></li>
</ul>
</section>
<section id="library-stats">
<h3>Library Curation</h3>
<p>Total Files: <strong id="library-files"></strong></p>
<p>Tagged Files: <strong id="tagged-files"></strong></p>
<p>Tag Coverage: <strong id="tag-coverage"></strong>%</p>
<div class="progress-bar">
<div class="progress-fill" id="coverage-bar"></div>
</div>
<p>Curator Rank: <strong id="curator-rank"></strong></p>
</section>
<section id="achievement-showcase">
<h3>Achievements</h3>
<div id="achievement-grid">
<!-- Achievement cards -->
</div>
</section>
</div>
Visualization Methods:
class StatsVisualizer {
renderProgressBar(percent, elementId) {
// Update progress bar width
}
renderEdgeChart(edgesPerLevel) {
// Use Chart.js or similar to render edge graph
}
formatTime(seconds) {
// Convert seconds to "Xh Ym" format
}
renderAchievementGrid(achievements) {
// Display unlocked + locked achievements
}
}
Testing Checklist:
- All stats track correctly
- Dashboard displays all sections
- Progress bars update
- Charts render correctly
- Time formats correctly
- Achievement grid displays
Task 3: Advanced Library Management (3-4 hours)
File to Update: src/features/academy/libraryManager.js
New Features:
- Quality Scoring - Rate files 1-5 stars
- Diversity Scoring - Measure tag variety
- Smart Playlists - Auto-generate playlists from tags
Quality Scoring:
class LibraryManager {
// ... existing methods ...
rateFile(filePath, rating) {
// rating: 1-5 stars
// Save to individualFiles
}
getHighestRatedFiles(count = 10) {
// Return top-rated files
}
calculateAverageQuality() {
// Average rating across all rated files
}
}
Diversity Scoring:
class LibraryManager {
// ... existing ...
calculateDiversityScore() {
// Count unique tags
// Compare to total tags in catalog
// Return % (0-100)
}
getUnderrepresentedTags() {
// Return tags with < 10 files
// Suggest adding content with these tags
}
}
Smart Playlists:
class LibraryManager {
// ... existing ...
createSmartPlaylist(name, tagCombination, matchMode = 'AND') {
// tagCombination: ['amateur', 'pov']
// matchMode: 'AND' or 'OR'
// Return playlist of matching files
}
getSuggestedPlaylists(preferences) {
// Based on user preferences, suggest playlists
// e.g., "Sissy + Humiliation", "Denial + Edging"
}
savePlaylist(playlist) {
// Save to gameData
}
loadPlaylists() {
// Return all saved playlists
}
}
Smart Playlist UI:
<div id="smart-playlists">
<h3>Smart Playlists</h3>
<button id="create-playlist">Create Playlist</button>
<div id="playlist-creator" class="hidden">
<input type="text" id="playlist-name" placeholder="Playlist Name">
<div id="tag-selector">
<!-- Multi-select tags -->
</div>
<select id="match-mode">
<option value="AND">Match ALL tags</option>
<option value="OR">Match ANY tag</option>
</select>
<button id="save-playlist">Save Playlist</button>
</div>
<div id="playlist-list">
<!-- Saved playlists -->
</div>
</div>
Testing Checklist:
- Can rate files 1-5 stars
- Quality score calculates
- Diversity score calculates
- Smart playlists create correctly
- Playlists save and load
- Suggested playlists generate
Task 4: Curator Rank Benefits (2-3 hours)
Curator Rank System:
const curatorRanks = {
Novice: {
minCoverage: 0,
benefits: ['Basic tagging']
},
Apprentice: {
minCoverage: 50,
benefits: ['Bulk tagging', 'Tag suggestions']
},
Journeyman: {
minCoverage: 75,
benefits: ['Smart playlists', 'Quality rating']
},
Expert: {
minCoverage: 90,
benefits: ['Advanced filters', 'Diversity insights']
},
Master: {
minCoverage: 98,
benefits: ['Full library analytics', 'Export/import', 'Custom tags']
}
};
Rank-Gated Features:
- Bulk Tagging: Apprentice+ can tag multiple files at once
- Smart Playlists: Journeyman+ can create smart playlists
- Advanced Filters: Expert+ can filter by multiple criteria
- Export/Import: Master can export library data
Testing Checklist:
- Rank calculates based on coverage
- Benefits unlock at correct ranks
- Features gate correctly
Task 5: Export/Import System (2-3 hours)
File to Create: src/features/academy/dataExporter.js
Responsibilities:
- Export library data as JSON
- Export achievements as JSON
- Import library data
- Share library configurations
Methods:
class DataExporter {
exportLibrary() {
// Return JSON of libraryData
}
exportAchievements() {
// Return JSON of unlocked achievements
}
importLibrary(jsonData) {
// Merge imported data with existing library
// Avoid duplicates
}
downloadAsFile(data, filename) {
// Trigger browser download
}
}
UI:
<div id="export-import">
<h3>Data Management</h3>
<button id="export-library">Export Library</button>
<button id="export-achievements">Export Achievements</button>
<input type="file" id="import-file" accept=".json">
<button id="import-library">Import Library</button>
</div>
Testing Checklist:
- Library exports as JSON
- Achievements export as JSON
- Import merges correctly
- No duplicate entries after import
📏 Measurable Test Criteria
After Phase 6, ALL of these must pass:
Achievement System:
- All 25+ achievements defined
- "First Steps" unlocks after L1
- "Foundation Graduate" unlocks after L5
- "Century Club" unlocks at 100 edges
- "10 Hours Strong" unlocks at 10 hours
- "Curator Initiate" unlocks after adding first directory
- Achievement notifications display
- Achievements persist across refresh
- Hidden achievements stay hidden
Statistics Dashboard:
- Dashboard displays all sections
- Current level displays correctly
- Completed levels count correct
- Progress bar updates
- Total time calculates correctly
- Total edges track correctly
- Feature usage stats accurate
- Library stats display
- Achievement grid shows unlocked + locked
Library Management:
- Can rate files 1-5 stars
- Average quality calculates
- Diversity score calculates
- Smart playlists create
- Playlists save and load
- Tag suggestions work
Curator Ranks:
- Novice at 0% coverage
- Apprentice at 50% coverage
- Journeyman at 75% coverage
- Expert at 90% coverage
- Master at 98% coverage
- Benefits unlock correctly
Export/Import:
- Library exports as valid JSON
- Achievements export as valid JSON
- Import merges without duplicates
- Download triggers correctly
Overall:
- No console errors
- All systems persist data
- Performance acceptable with large library
🎯 Success Criteria
Phase 6 Complete When:
- Achievement system fully functional (25+ achievements)
- Stats dashboard displays all metrics
- Advanced library features work (quality, diversity, playlists)
- Curator rank progression works
- Export/import functional
- All measurable tests pass
- Meta-game layer enhances engagement
📂 Files Created/Modified
New Files:
src/features/academy/progressionManager.jssrc/features/academy/statsVisualizer.jssrc/features/academy/dataExporter.jssrc/data/academy/achievements.js
Modified Files:
src/features/academy/libraryManager.jssrc/core/gameDataManager.jstraining-academy.html
🚀 Next Phase
After Phase 6: Phase 7: Polish/UI/UX/Testing (Final refinements and QA)