/** * Player Statistics Tracker * Tracks comprehensive viewing and engagement statistics */ class PlayerStats { constructor() { this.stats = this.loadStats(); this.currentSession = { startTime: Date.now(), watchTime: 0, videosWatched: 0, isVideoPlaying: false, currentVideoStartTime: null, currentVideoPath: null, videoWatchStartTime: null }; this.initializeSession(); console.log('📊 Player Stats initialized'); } loadStats() { try { const saved = localStorage.getItem('playerStats'); const defaultStats = this.getDefaultStats(); if (saved) { const parsed = JSON.parse(saved); // Merge with defaults to ensure all properties exist return { ...defaultStats, ...parsed }; } return defaultStats; } catch (error) { console.error('📊 Error loading stats:', error); return this.getDefaultStats(); } } getDefaultStats() { return { // Core Viewing Stats totalWatchTime: 0, // milliseconds videosWatched: 0, // Only videos completed 90%+ // Engagement Stats playlistsCreated: 0, videosAddedToPlaylists: 0, favoriteVideos: 0, videoLibrarySize: 0, // Behavioral Stats mostWatchedVideo: { path: null, name: null, count: 0 }, videosSkipped: 0, // Achievement Stats daysActive: [], longestStreak: 0, bingeSessions: 0, // XP and Game Progress totalXP: 0, quickPlaySessions: 0, quickPlayTasksCompleted: 0, quickPlayXP: 0, quickPlayTimeSpent: 0, // milliseconds scenarioGameXP: 0, pornCinemaXP: 0, // Detailed Tracking videoPlayCounts: {}, // { videoPath: count } videoWatchTimes: {}, // { videoPath: totalTime } dailyStats: {}, // { date: { watchTime, videos, sessions } } // Metadata firstPlayDate: null, lastPlayDate: null, version: '1.0' }; } saveStats() { try { localStorage.setItem('playerStats', JSON.stringify(this.stats)); } catch (error) { console.error('📊 Error saving stats:', error); } } initializeSession() { // Initialize daily stats tracking this.updateDailyStats('videos', 0); // Initialize today's entry this.saveStats(); console.log('📊 Stats tracking initialized'); } // ===== VIDEO PLAYBACK TRACKING ===== onVideoStart(video) { console.log('📊 Video started:', video.name); // Stop previous video tracking if any if (this.currentSession.isVideoPlaying) { this.onVideoPause(); } // Start new video tracking this.currentSession.isVideoPlaying = true; this.currentSession.currentVideoStartTime = Date.now(); this.currentSession.currentVideoPath = video.path; this.currentSession.videoWatchStartTime = Date.now(); // Update video play count this.incrementVideoPlayCount(video); // Note: videosWatched will only be incremented on 90%+ completion // Set first/last play dates const now = new Date().toISOString(); if (!this.stats.firstPlayDate) { this.stats.firstPlayDate = now; } this.stats.lastPlayDate = now; // Start XP tracking interval (every 30 seconds) this.startXPTracking(); this.saveStats(); } onVideoPlay() { if (!this.currentSession.isVideoPlaying && this.currentSession.currentVideoPath) { console.log('📊 Video resumed'); this.currentSession.isVideoPlaying = true; this.currentSession.videoWatchStartTime = Date.now(); this.startXPTracking(); // Restart XP tracking when video resumes } } onVideoPause() { if (this.currentSession.isVideoPlaying) { console.log('📊 Video paused'); this.recordCurrentWatchTime(); this.currentSession.isVideoPlaying = false; this.currentSession.videoWatchStartTime = null; this.stopXPTracking(); // Stop XP tracking when video pauses } } onVideoEnd(completionPercentage = 100) { console.log('📊 Video ended, completion:', completionPercentage + '%'); if (this.currentSession.isVideoPlaying) { this.recordCurrentWatchTime(); } // Only count as "watched" if 90%+ completed if (completionPercentage >= 90) { this.stats.videosWatched++; this.currentSession.videosWatched++; this.updateDailyStats('videos', 1); console.log('📊 Video completed (90%+) - counted as watched'); } else if (completionPercentage < 10) { this.stats.videosSkipped++; console.log('📊 Video skipped (<10%) - counted as skipped'); } // XP is now awarded continuously in recordCurrentWatchTime() // Stop XP tracking this.stopXPTracking(); // Reset current video tracking this.currentSession.isVideoPlaying = false; this.currentSession.currentVideoStartTime = null; this.currentSession.currentVideoPath = null; this.currentSession.videoWatchStartTime = null; this.saveStats(); } recordCurrentWatchTime() { if (this.currentSession.videoWatchStartTime && this.currentSession.currentVideoPath) { const watchTime = Date.now() - this.currentSession.videoWatchStartTime; // Add to total watch time this.stats.totalWatchTime += watchTime; this.currentSession.watchTime += watchTime; // Add to video-specific watch time const videoPath = this.currentSession.currentVideoPath; if (!this.stats.videoWatchTimes[videoPath]) { this.stats.videoWatchTimes[videoPath] = 0; } this.stats.videoWatchTimes[videoPath] += watchTime; // Update daily stats this.updateDailyStats('watchTime', watchTime); console.log(`📊 Recorded ${this.formatDuration(watchTime)} of watch time`); // Reset the watch start time for next interval this.currentSession.videoWatchStartTime = Date.now(); } } // Check for XP awards separately from watch time recording checkForXPAwards() { const totalWatchMinutes = this.stats.totalWatchTime / (1000 * 60); const totalXPEarned = Math.floor(totalWatchMinutes / 5); const currentPornCinemaXP = this.stats.pornCinemaXP || 0; if (totalXPEarned > currentPornCinemaXP) { const newXP = totalXPEarned - currentPornCinemaXP; this.awardXP(newXP, 'porncinema'); console.log(`🏆 Awarded ${newXP} XP for reaching ${totalWatchMinutes.toFixed(1)} total minutes watched`); } } // ===== XP TRACKING DURING PLAYBACK ===== startXPTracking() { // Clear any existing interval this.stopXPTracking(); // Start new interval to track watch time every 30 seconds this.xpTrackingInterval = setInterval(() => { if (this.currentSession.isVideoPlaying) { this.recordCurrentWatchTime(); this.saveStats(); } }, 30000); // 30 seconds // Separate interval to check for XP awards every 5 minutes this.xpAwardInterval = setInterval(() => { if (this.currentSession.isVideoPlaying) { this.checkForXPAwards(); } }, 300000); // 5 minutes console.log('📊 Started XP tracking interval'); } stopXPTracking() { if (this.xpTrackingInterval) { clearInterval(this.xpTrackingInterval); this.xpTrackingInterval = null; } if (this.xpAwardInterval) { clearInterval(this.xpAwardInterval); this.xpAwardInterval = null; } console.log('📊 Stopped XP tracking interval'); } incrementVideoPlayCount(video) { const path = video.path; if (!this.stats.videoPlayCounts[path]) { this.stats.videoPlayCounts[path] = 0; } this.stats.videoPlayCounts[path]++; // Update most watched video if (this.stats.videoPlayCounts[path] > this.stats.mostWatchedVideo.count) { this.stats.mostWatchedVideo = { path: path, name: video.name, count: this.stats.videoPlayCounts[path] }; } } // ===== PLAYLIST TRACKING ===== onPlaylistCreated(playlist) { console.log('📊 Playlist created:', playlist.name); this.stats.playlistsCreated++; this.saveStats(); } onVideoAddedToPlaylist(video) { console.log('📊 Video added to playlist:', video.name); this.stats.videosAddedToPlaylists++; this.saveStats(); } // ===== XP TRACKING ===== awardXP(amount, source = 'general') { console.log(`🏆 Awarded ${amount} XP from ${source}`); // Calculate old level before adding XP const oldLevel = this.getCurrentLevel(); const oldTotalXP = this.stats.totalXP; this.stats.totalXP += amount; // Track by source switch (source) { case 'quickplay': this.stats.quickPlayXP += amount; break; case 'scenario': this.stats.scenarioGameXP += amount; break; case 'porncinema': this.stats.pornCinemaXP += amount; break; } this.updateDailyStats('xp', amount); this.saveStats(); // Calculate new level after adding XP const newLevel = this.getCurrentLevel(); // Check for level up if (newLevel.level > oldLevel.level) { console.log(`🎉 LEVEL UP! ${oldLevel.level} → ${newLevel.level} (${oldLevel.name} → ${newLevel.name})`); this.triggerLevelUpAnimation(oldLevel, newLevel, amount, source); } // Check for new achievements this.checkForNewAchievements(oldTotalXP, this.stats.totalXP); // Dispatch event to trigger display updates if (typeof window !== 'undefined') { window.dispatchEvent(new CustomEvent('playerStatsUpdated', { detail: { totalXP: this.stats.totalXP, source: source, amount: amount, oldLevel: oldLevel, newLevel: newLevel, leveledUp: newLevel.level > oldLevel.level } })); console.log(`📊 Dispatched playerStatsUpdated event: ${amount} XP from ${source}`); } } onQuickPlaySessionComplete(sessionData) { console.log('📊 Quick Play session completed:', sessionData); this.stats.quickPlaySessions++; this.stats.quickPlayTasksCompleted += sessionData.tasksCompleted || 0; this.stats.quickPlayTimeSpent += sessionData.timeSpent || 0; if (sessionData.xpEarned) { this.awardXP(sessionData.xpEarned, 'quickplay'); } this.saveStats(); } onTaskCompleted(taskData) { console.log('📊 Task completed:', taskData); if (taskData.xpEarned) { this.awardXP(taskData.xpEarned, taskData.source || 'general'); } // Update daily task completion stats this.updateDailyStats('tasksCompleted', 1); this.saveStats(); } // ===== LEVEL AND ACHIEVEMENT SYSTEM ===== getCurrentLevel() { return this.calculateLevel(this.stats.totalXP); } calculateLevel(totalXP) { const levelData = this.getLevelData(); let currentLevel = levelData[0]; for (let i = levelData.length - 1; i >= 0; i--) { if (totalXP >= levelData[i].xpRequired) { currentLevel = levelData[i]; break; } } // Calculate progress toward next level const nextLevel = levelData.find(l => l.level === currentLevel.level + 1); const currentLevelXP = totalXP - currentLevel.xpRequired; const xpNeededForNext = nextLevel ? nextLevel.xpRequired - currentLevel.xpRequired : 100; const progressPercent = nextLevel ? (currentLevelXP / xpNeededForNext) * 100 : 100; return { level: currentLevel.level, name: currentLevel.name, icon: currentLevel.icon, description: currentLevel.description, totalXP: totalXP, currentLevelXP: currentLevelXP, xpNeededForNext: xpNeededForNext, progressPercent: Math.min(progressPercent, 100), nextLevelName: nextLevel ? nextLevel.name : 'Max Level' }; } getLevelData() { return [ { level: 1, name: "Virgin", xpRequired: 0, icon: '🌱', description: 'Your first taste of pleasure awaits' }, { level: 2, name: "Curious", xpRequired: 10, icon: '🌿', description: 'Start exploring your desires' }, { level: 3, name: "Eager", xpRequired: 25, icon: '🌱', description: 'Developing your appetites' }, { level: 4, name: "Aroused", xpRequired: 48, icon: '🌿', description: 'Your arousal is building' }, { level: 5, name: "Lustful", xpRequired: 82, icon: '🌟', description: 'Consumed by lust and craving more' }, { level: 6, name: "Passionate", xpRequired: 133, icon: '🔥', description: 'Your passion burns hot with desire' }, { level: 7, name: "Addicted", xpRequired: 209, icon: '⭐', description: 'Hopelessly addicted to pleasure' }, { level: 8, name: "Obsessed", xpRequired: 323, icon: '🎯', description: 'Your obsession drives you to new heights' }, { level: 9, name: "Deviant", xpRequired: 494, icon: '🏆', description: 'A true deviant exploring desire' }, { level: 10, name: "Kinky", xpRequired: 751, icon: '💎', description: 'Welcome to the kinky elite' }, // After rank 10, progression slows down (linear increases instead of exponential) { level: 11, name: "Perverted", xpRequired: 1100, icon: '🌟', description: 'A seasoned pervert with experience' }, { level: 12, name: "Depraved", xpRequired: 1500, icon: '🔮', description: 'Mastered the art of depravity' }, { level: 13, name: "Dominant", xpRequired: 1950, icon: '👑', description: 'A dominant force commanding respect' }, { level: 14, name: "Submissive", xpRequired: 2450, icon: '🚀', description: 'Embracing submission and control' }, { level: 15, name: "Hedonist", xpRequired: 3000, icon: '🌌', description: 'Pure hedonism guides your moves' }, { level: 16, name: "Insatiable", xpRequired: 3600, icon: '⚡', description: 'Your appetite knows no bounds' }, { level: 17, name: "Transcendent", xpRequired: 4250, icon: '🔥', description: 'Transcending mortal desire' }, { level: 18, name: "Enlightened", xpRequired: 4950, icon: '🌠', description: 'Achieved enlightened pleasure' }, { level: 19, name: "Godlike", xpRequired: 5700, icon: '🏛️', description: 'Godlike levels of sexual prowess' }, { level: 20, name: "Omnipotent", xpRequired: 6500, icon: '👁️', description: 'Ultimate master of pleasure' } ]; } checkForNewAchievements(oldXP, newXP) { const achievements = this.getAchievements(); const stats = this.stats; achievements.forEach(achievement => { const wasUnlocked = achievement.condition({ ...stats, totalXP: oldXP }); const isNowUnlocked = achievement.condition(stats); if (!wasUnlocked && isNowUnlocked) { console.log(`🏆 NEW ACHIEVEMENT UNLOCKED: ${achievement.title}`); this.triggerAchievementAnimation(achievement); } }); } getAchievements() { return [ { id: 'first-video', icon: '🎬', title: 'First Taste', description: 'Watch your first video', condition: stats => (stats.videosWatched || 0) >= 1 }, { id: 'early-bird', icon: '🐦', title: 'Early Bird', description: 'Watch 10 videos', condition: stats => (stats.videosWatched || 0) >= 10 }, { id: 'marathon-viewer', icon: '🏃‍♂️', title: 'Marathon Viewer', description: 'Watch for 2+ hours total', condition: stats => (stats.totalWatchTime || 0) >= 7200000 // 2 hours in milliseconds }, { id: 'playlist-curator', icon: '📝', title: 'Playlist Curator', description: 'Create 5 playlists', condition: stats => (stats.playlistsCreated || 0) >= 5 }, { id: 'consistency-king', icon: '🔥', title: 'Consistency King', description: 'Maintain a 7-day streak', condition: stats => (stats.longestStreak || 0) >= 7 }, { id: 'xp-master', icon: '⭐', title: 'Passionate Soul', description: 'Earn 500 total XP (Level 6)', condition: stats => (stats.totalXP || 0) >= 500 }, { id: 'level-up', icon: '🆙', title: 'Lustful Awakening', description: 'Reach Level 5 (Lustful)', condition: stats => this.calculateLevel(stats.totalXP || 0).level >= 5 }, { id: 'kinky-elite', icon: '💎', title: 'Kinky Elite', description: 'Reach Level 10 (Kinky)', condition: stats => this.calculateLevel(stats.totalXP || 0).level >= 10 }, { id: 'depraved-master', icon: '🔮', title: 'Depraved Master', description: 'Reach Level 12 (Depraved)', condition: stats => this.calculateLevel(stats.totalXP || 0).level >= 12 }, { id: 'collector', icon: '🎭', title: 'Desire Collector', description: 'Watch 100 different videos', condition: stats => (stats.videosWatched || 0) >= 100 } ]; } triggerLevelUpAnimation(oldLevel, newLevel, xpAmount, source) { // Create and trigger level up animation if (typeof window !== 'undefined' && window.showLevelUpAnimation) { window.showLevelUpAnimation(oldLevel, newLevel, xpAmount, source); } else { // Fallback for environments without animation support console.log(`🎉 LEVEL UP ACHIEVED! ${oldLevel.name} (${oldLevel.level}) → ${newLevel.name} (${newLevel.level})`); } // Dispatch level up event if (typeof window !== 'undefined') { window.dispatchEvent(new CustomEvent('levelUp', { detail: { oldLevel: oldLevel, newLevel: newLevel, xpAmount: xpAmount, source: source } })); } } triggerAchievementAnimation(achievement) { // Create and trigger achievement animation if (typeof window !== 'undefined' && window.showAchievementAnimation) { window.showAchievementAnimation(achievement); } else { // Fallback for environments without animation support console.log(`🏆 ACHIEVEMENT UNLOCKED! ${achievement.title}: ${achievement.description}`); } // Dispatch achievement event if (typeof window !== 'undefined') { window.dispatchEvent(new CustomEvent('achievementUnlocked', { detail: { achievement: achievement } })); } } // ===== DAILY STATS TRACKING ===== updateDailyStats(type, value) { const today = new Date().toISOString().split('T')[0]; // YYYY-MM-DD if (!this.stats.dailyStats[today]) { this.stats.dailyStats[today] = { watchTime: 0, videos: 0, xp: 0, tasksCompleted: 0 }; } this.stats.dailyStats[today][type] += value; // Update days active if (!this.stats.daysActive.includes(today)) { this.stats.daysActive.push(today); this.calculateStreak(); } } calculateStreak() { const sortedDays = this.stats.daysActive.sort(); let currentStreak = 1; let maxStreak = 1; for (let i = 1; i < sortedDays.length; i++) { const prevDate = new Date(sortedDays[i - 1]); const currDate = new Date(sortedDays[i]); const dayDiff = (currDate - prevDate) / (1000 * 60 * 60 * 24); if (dayDiff === 1) { currentStreak++; maxStreak = Math.max(maxStreak, currentStreak); } else { currentStreak = 1; } } this.stats.longestStreak = maxStreak; } // ===== DAILY STATS TRACKING ===== getFormattedStats() { return { // Viewing Stats totalWatchTime: this.formatDuration(this.stats.totalWatchTime), videosWatched: this.stats.videosWatched.toLocaleString(), // Only 90%+ completed // Game Progress Stats totalXP: this.stats.totalXP.toLocaleString(), quickPlayXP: this.stats.quickPlayXP.toLocaleString(), scenarioGameXP: this.stats.scenarioGameXP.toLocaleString(), pornCinemaXP: this.stats.pornCinemaXP.toLocaleString(), quickPlaySessions: this.stats.quickPlaySessions.toLocaleString(), quickPlayTasksCompleted: this.stats.quickPlayTasksCompleted.toLocaleString(), quickPlayTimeSpent: this.formatDuration(this.stats.quickPlayTimeSpent), // General Stats playlistsCreated: this.stats.playlistsCreated.toLocaleString(), videosSkipped: this.stats.videosSkipped.toLocaleString(), daysActive: this.stats.daysActive.length.toLocaleString(), longestStreak: this.stats.longestStreak.toLocaleString(), mostWatchedVideo: this.stats.mostWatchedVideo.name || 'None yet' }; } formatDuration(milliseconds) { if (!milliseconds || milliseconds === 0) return '0m'; const hours = Math.floor(milliseconds / (1000 * 60 * 60)); const minutes = Math.floor((milliseconds % (1000 * 60 * 60)) / (1000 * 60)); if (hours > 0) { return `${hours}h ${minutes}m`; } else { return `${minutes}m`; } } // ===== PUBLIC API ===== getRawStats() { return { ...this.stats }; } resetStats() { this.stats = this.getDefaultStats(); this.saveStats(); console.log('📊 Stats reset'); } exportStats() { return { stats: this.stats, exportDate: new Date().toISOString(), version: '1.0' }; } } // Auto-cleanup on page unload - record any current watch time window.addEventListener('beforeunload', () => { if (window.playerStats && window.playerStats.currentSession.isVideoPlaying) { window.playerStats.recordCurrentWatchTime(); window.playerStats.saveStats(); } });