training-academy/user-profile.html

1276 lines
51 KiB
HTML
Raw Blame History

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>User Profile - webGame</title>
<link rel="stylesheet" href="src/styles/color-variables.css">
<script src="src/utils/themeManager.js"></script>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
background: linear-gradient(135deg, #1a0033 0%, #0a0015 50%, #1a0033 100%);
color: #e0e0e0;
line-height: 1.6;
padding: 20px;
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
}
.main-content-wrapper {
width: calc(100vw - 350px);
max-width: calc(100vw - 350px);
margin: 10px auto;
}
.header {
position: relative;
text-align: center;
margin-bottom: 20px;
padding: 20px;
background: linear-gradient(180deg, rgba(138, 43, 226, 0.2) 0%, transparent 100%);
border-radius: 20px;
box-shadow: 0 10px 30px rgba(138, 43, 226, 0.3);
}
.header h1 {
font-size: 2.5rem;
margin-bottom: 10px;
font-weight: 700;
}
.header p {
font-size: 1.1rem;
opacity: 0.9;
}
.profile-container {
max-width: 100%;
margin: 0 auto;
display: grid;
grid-template-columns: 1fr 2fr;
gap: 20px;
margin-bottom: 20px;
}
.profile-info {
background: rgba(26, 26, 26, 0.8);
border-radius: 15px;
padding: 30px;
border: 1px solid rgba(138, 43, 226, 0.3);
height: fit-content;
}
.profile-avatar {
width: 150px;
height: 150px;
background: var(--gradient);
border-radius: 50%;
margin: 0 auto 20px;
display: flex;
align-items: center;
justify-content: center;
font-size: 3rem;
font-weight: bold;
cursor: pointer;
transition: transform 0.3s ease;
overflow: hidden;
position: relative;
border: 3px solid var(--color-primary);
}
.profile-avatar img {
width: 100%;
height: 100%;
object-fit: cover;
}
.profile-avatar:hover {
transform: scale(1.05);
}
.profile-avatar:hover::after {
content: '📷 Change Photo';
position: absolute;
inset: 0;
background: rgba(0, 0, 0, 0.7);
display: flex;
align-items: center;
justify-content: center;
font-size: 0.9rem;
color: white;
}
.character-side {
position: fixed;
z-index: 3;
pointer-events: none;
background-size: contain;
background-repeat: no-repeat;
background-position: center;
}
.character-side.left {
left: 0;
top: 50%;
transform: translateY(-50%);
width: 150px;
height: 300px;
}
.character-side.right {
right: 0;
top: 50%;
transform: translateY(-50%);
width: 150px;
height: 300px;
}
.profile-details h2 {
font-size: 1.8rem;
margin-bottom: 15px;
text-align: center;
}
.profile-field {
margin-bottom: 20px;
}
.profile-field label {
display: block;
font-weight: 600;
margin-bottom: 8px;
color: var(--color-text-secondary);
}
.profile-field input, .profile-field textarea {
width: 100%;
padding: 12px;
background: var(--color-background);
border: 1px solid var(--color-border);
border-radius: 8px;
color: var(--color-text);
font-size: 1rem;
transition: border-color 0.3s ease;
}
.profile-field input:focus, .profile-field textarea:focus {
outline: none;
border-color: var(--color-primary);
}
.profile-field textarea {
min-height: 80px;
resize: vertical;
}
.stats-overview {
background: var(--color-surface);
border-radius: 15px;
padding: 30px;
border: 1px solid var(--color-border);
}
.stats-overview h3 {
font-size: 1.5rem;
margin-bottom: 20px;
display: flex;
align-items: center;
gap: 10px;
}
.quick-stats {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
gap: 15px;
margin-bottom: 20px;
}
.quick-stat {
background: var(--color-background);
border-radius: 10px;
padding: 20px;
text-align: center;
border: 1px solid var(--color-border);
}
.quick-stat-value {
font-size: 1.4rem;
font-weight: bold;
color: var(--color-secondary);
display: block;
line-height: 1.2;
min-height: 2em;
display: flex;
align-items: center;
justify-content: center;
}
.quick-stat-label {
font-size: 0.9rem;
color: var(--color-text-secondary);
margin-top: 5px;
}
.level-progress {
background: var(--color-background);
border-radius: 10px;
padding: 20px;
margin-top: 20px;
}
.level-info {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 15px;
}
.level-bar {
background: var(--color-surface);
height: 20px;
border-radius: 10px;
overflow: hidden;
position: relative;
}
.level-fill {
background: var(--gradient);
height: 100%;
border-radius: 10px;
transition: width 0.3s ease;
}
.level-text {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
font-size: 0.8rem;
font-weight: bold;
color: white;
text-shadow: 1px 1px 2px rgba(0,0,0,0.7);
}
.achievements {
background: var(--color-surface);
border-radius: 15px;
padding: 30px;
border: 1px solid var(--color-border);
margin-top: 30px;
}
.achievements h3 {
font-size: 1.5rem;
margin-bottom: 20px;
display: flex;
align-items: center;
gap: 10px;
}
.achievement-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: 15px;
}
.achievement {
background: var(--color-background);
border-radius: 10px;
padding: 20px;
text-align: center;
border: 1px solid var(--color-border);
transition: transform 0.3s ease;
}
.achievement.unlocked {
border-color: var(--color-success);
background: linear-gradient(135deg, rgba(16, 185, 129, 0.1), rgba(16, 185, 129, 0.05));
}
.achievement.locked {
opacity: 0.5;
}
.achievement:hover {
transform: translateY(-2px);
}
.achievement-icon {
font-size: 2.5rem;
margin-bottom: 10px;
display: block;
}
.achievement-title {
font-weight: bold;
margin-bottom: 5px;
}
.achievement-desc {
font-size: 0.9rem;
color: var(--color-text-secondary);
}
.action-buttons {
display: flex;
gap: 15px;
margin-top: 30px;
flex-wrap: wrap;
justify-content: center;
}
.btn {
padding: 12px 25px;
border: none;
border-radius: 8px;
font-size: 1rem;
cursor: pointer;
transition: all 0.3s ease;
text-decoration: none;
display: inline-flex;
align-items: center;
gap: 8px;
font-weight: 600;
}
.btn-primary {
background: var(--gradient);
color: white;
}
.btn-primary:hover {
transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(139, 92, 246, 0.4);
}
.btn-secondary {
background: var(--color-surface);
color: var(--color-text);
border: 1px solid var(--color-border);
}
.btn-secondary:hover {
background: var(--color-surface-hover);
transform: translateY(-2px);
}
@media (max-width: 768px) {
.profile-container {
grid-template-columns: 1fr;
}
.header h1 {
font-size: 2rem;
}
.quick-stats {
grid-template-columns: 1fr;
}
.quick-stat-value {
font-size: 1.2rem;
}
.achievement-grid {
grid-template-columns: 1fr;
}
}
.export-section {
background: var(--color-surface);
border-radius: 15px;
padding: 30px;
border: 1px solid var(--color-border);
margin-top: 30px;
}
.export-section h3 {
font-size: 1.5rem;
margin-bottom: 20px;
display: flex;
align-items: center;
gap: 10px;
}
/* Detailed Stats Styles */
.stat-detail {
background: var(--color-background);
border-radius: 8px;
padding: 15px;
text-align: center;
border: 1px solid var(--color-border);
}
.stat-detail-value {
font-size: 1.5rem;
font-weight: bold;
color: var(--color-secondary);
display: block;
margin-bottom: 5px;
}
.stat-detail-label {
font-size: 0.85rem;
color: var(--color-text-secondary);
}
.xp-mode {
background: var(--color-background);
border-radius: 8px;
padding: 15px;
text-align: center;
border: 1px solid var(--color-border);
}
.xp-mode-value {
font-size: 1.3rem;
font-weight: bold;
color: var(--color-warning);
display: block;
margin-bottom: 5px;
}
.xp-mode-label {
font-size: 0.85rem;
color: var(--color-text-secondary);
}
.reset-section {
background: var(--color-surface);
border-radius: 15px;
padding: 30px;
border: 1px solid var(--color-error);
margin-top: 30px;
}
.reset-section h3 {
color: var(--color-error);
font-size: 1.5rem;
margin-bottom: 20px;
display: flex;
align-items: center;
gap: 10px;
}
.btn-danger {
background: var(--color-error);
color: white;
}
.btn-danger:hover {
background: #DC2626;
transform: translateY(-2px);
}
</style>
</head>
<body>
<!-- Side Characters -->
<div class="character-side left" style="background-image: url('assets/hentai/1.png'); top: 10%;"></div>
<div class="character-side right" style="background-image: url('assets/hentai/11.png'); top: 15%;"></div>
<div class="character-side left" style="background-image: url('assets/hentai/3.png'); top: 30%;"></div>
<div class="character-side right" style="background-image: url('assets/hentai/4.png'); top: 35%;"></div>
<div class="character-side left" style="background-image: url('assets/hentai/5.png'); top: 50%;"></div>
<div class="character-side right" style="background-image: url('assets/hentai/6.png'); top: 55%;"></div>
<div class="character-side left" style="background-image: url('assets/hentai/7.png'); top: 70%;"></div>
<div class="character-side right" style="background-image: url('assets/hentai/8.png'); top: 75%;"></div>
<div class="character-side left" style="background-image: url('assets/hentai/9.png'); top: 90%;"></div>
<div class="character-side right" style="background-image: url('assets/hentai/10.png'); top: 95%;"></div>
<div class="character-side left" style="background-image: url('assets/hentai/12.png'); top: 110%;"></div>
<div class="character-side right" style="background-image: url('assets/hentai/13.png'); top: 115%;"></div>
<div class="main-content-wrapper">
<div class="header">
<div id="theme-switcher-container" style="position: absolute; top: 20px; right: 20px;"></div>
<h1>👤 User Profile</h1>
<p>Customize your gaming experience and track your progress</p>
</div>
<div class="profile-container">
<div class="profile-info">
<div class="profile-avatar" id="profile-avatar">
<span id="avatar-text">U</span>
<img id="avatar-image" style="display: none;">
</div>
<input type="file" id="avatar-upload" accept="image/*" style="display: none;">
<button class="btn btn-primary" onclick="document.getElementById('avatar-upload').click()" style="width: 100%; margin-bottom: 20px;">Upload Profile Picture</button>
<div class="profile-details">
<h2>Profile Information</h2>
<div class="profile-field">
<label for="username">Username</label>
<input type="text" id="username" placeholder="Enter your username">
</div>
<div class="profile-field">
<label for="bio">Bio</label>
<textarea id="bio" placeholder="Tell us about yourself..."></textarea>
</div>
<div class="profile-field">
<label for="join-date">Member Since</label>
<input type="text" id="join-date" readonly>
</div>
<div class="profile-field">
<label for="theme">Preferred Theme</label>
<select id="theme" style="width: 100%; padding: 12px; background: var(--background); border: 1px solid var(--border); border-radius: 8px; color: var(--text-primary);">
<option value="dark">Dark Theme</option>
<option value="light">Light Theme</option>
<option value="auto">Auto (System)</option>
</select>
</div>
</div>
</div>
<div class="stats-overview">
<h3>📊 Player Statistics</h3>
<div class="quick-stats">
<div class="quick-stat">
<span class="quick-stat-value" id="total-watch-time">0h 0m</span>
<div class="quick-stat-label">Total Watch Time</div>
</div>
<div class="quick-stat">
<span class="quick-stat-value" id="videos-watched">0</span>
<div class="quick-stat-label">Videos Watched</div>
</div>
<div class="quick-stat">
<span class="quick-stat-value" id="total-xp">0</span>
<div class="quick-stat-label">Total XP</div>
</div>
<div class="quick-stat">
<span class="quick-stat-value" id="current-level">1</span>
<div class="quick-stat-label">Current Level</div>
</div>
<div class="quick-stat">
<span class="quick-stat-value" id="current-streak">0</span>
<div class="quick-stat-label">Day Streak</div>
</div>
<div class="quick-stat">
<span class="quick-stat-value" id="playlists-created">0</span>
<div class="quick-stat-label">Playlists Created</div>
</div>
</div>
<div class="level-progress">
<div class="level-info">
<span><strong>Level Progress</strong></span>
<span id="level-xp">0 / 100 XP</span>
</div>
<div class="level-bar">
<div class="level-fill" id="level-fill" style="width: 0%"></div>
<div class="level-text" id="level-text">0%</div>
</div>
<div class="level-description" style="margin-top: 15px; text-align: center; padding: 10px; background: rgba(139, 92, 246, 0.1); border-radius: 8px;">
<div id="level-desc" style="font-style: italic; color: var(--text-secondary);">Your first taste of pleasure awaits</div>
</div>
</div>
<!-- Detailed Statistics Section -->
<div class="detailed-stats" style="margin-top: 30px;">
<h4 style="margin-bottom: 20px; color: var(--primary-light);">📈 Detailed Statistics</h4>
<div class="stats-grid" style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 15px;">
<div class="stat-detail">
<div class="stat-detail-value" id="days-active">0</div>
<div class="stat-detail-label">Days Active</div>
</div>
<div class="stat-detail">
<div class="stat-detail-value" id="longest-streak">0</div>
<div class="stat-detail-label">Longest Streak</div>
</div>
<div class="stat-detail">
<div class="stat-detail-value" id="videos-added-playlists">0</div>
<div class="stat-detail-label">Videos Added to Playlists</div>
</div>
<div class="stat-detail">
<div class="stat-detail-value" id="videos-skipped">0</div>
<div class="stat-detail-label">Videos Skipped</div>
</div>
<div class="stat-detail">
<div class="stat-detail-value" id="binge-sessions">0</div>
<div class="stat-detail-label">Binge Sessions (2+ hrs)</div>
</div>
<div class="stat-detail">
<div class="stat-detail-value" id="most-watched-video">None</div>
<div class="stat-detail-label">Most Watched Video</div>
</div>
</div>
<!-- Game Mode XP Breakdown -->
<div class="xp-breakdown" style="margin-top: 25px;">
<h4 style="margin-bottom: 15px; color: var(--primary-light);">🎮 XP Breakdown by Game Mode</h4>
<div class="xp-modes" style="display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 15px;">
<div class="xp-mode">
<div class="xp-mode-value" id="quick-play-xp">0</div>
<div class="xp-mode-label">Quick Play XP</div>
</div>
<div class="xp-mode">
<div class="xp-mode-value" id="porn-cinema-xp">0</div>
<div class="xp-mode-label">Cinema XP</div>
</div>
<div class="xp-mode">
<div class="xp-mode-value" id="scenario-xp">0</div>
<div class="xp-mode-label">Scenario XP</div>
</div>
</div>
</div>
<!-- Activity Timeline -->
<div class="activity-info" style="margin-top: 25px; background: var(--background); padding: 20px; border-radius: 10px;">
<h4 style="margin-bottom: 15px; color: var(--primary-light);">📅 Activity Timeline</h4>
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 15px;">
<div>
<strong>First Play Date:</strong>
<div id="first-play-date" style="color: var(--text-secondary);">N/A</div>
</div>
<div>
<strong>Last Activity:</strong>
<div id="last-activity" style="color: var(--text-secondary);">N/A</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="achievements">
<h3>🏆 Achievements</h3>
<div class="achievement-grid" id="achievement-grid">
<!-- Achievements will be populated by JavaScript -->
</div>
</div>
<div class="export-section">
<h3>💾 Profile & Statistics Data</h3>
<p>Manage your profile and statistics data</p>
<div class="action-buttons">
<button class="btn btn-secondary" onclick="exportProfile()"><EFBFBD> Export Profile</button>
<button class="btn btn-secondary" onclick="importProfile()"><EFBFBD> Import Profile</button>
<button class="btn btn-secondary" onclick="exportStats()"><EFBFBD> Export Statistics</button>
<button class="btn btn-primary" onclick="saveProfile()">💾 Save Changes</button>
</div>
</div>
<div class="reset-section">
<h3>⚠️ Danger Zone</h3>
<p>These actions cannot be undone. Please be careful!</p>
<div class="action-buttons">
<button class="btn btn-danger" onclick="resetStats()">🗑️ Reset All Statistics</button>
<button class="btn btn-danger" onclick="resetProfile()">👤 Reset Profile</button>
</div>
</div>
<div class="action-buttons">
<a href="index.html" class="btn btn-secondary">🏠 Back to Main Menu</a>
<a href="porn-cinema.html" class="btn btn-secondary">🎬 Cinema</a>
<a href="quick-play.html" class="btn btn-secondary">⚡ Quick Play</a>
</div>
<script src="src/features/stats/playerStats.js"></script>
<script>
class UserProfile {
constructor() {
this.playerStats = window.PlayerStats;
this.profile = this.loadProfile();
this.achievements = this.initializeAchievements();
this.init();
}
loadProfile() {
const saved = localStorage.getItem('userProfile');
if (saved) {
return JSON.parse(saved);
}
// Default profile
return {
username: 'Player',
bio: '',
joinDate: new Date().toISOString().split('T')[0],
theme: 'dark',
avatar: 'P'
};
}
saveProfile() {
this.profile.username = document.getElementById('username').value || 'Player';
this.profile.bio = document.getElementById('bio').value;
this.profile.theme = document.getElementById('theme').value;
this.profile.avatar = this.profile.username.charAt(0).toUpperCase();
localStorage.setItem('userProfile', JSON.stringify(this.profile));
// Show success message
this.showNotification('Profile saved successfully!', 'success');
this.updateDisplay();
}
init() {
this.updateDisplay();
this.updateStats();
this.renderAchievements();
// Update stats every 30 seconds
setInterval(() => this.updateStats(), 30000);
}
updateDisplay() {
document.getElementById('username').value = this.profile.username;
document.getElementById('bio').value = this.profile.bio;
document.getElementById('join-date').value = this.formatDate(this.profile.joinDate);
document.getElementById('theme').value = this.profile.theme;
document.getElementById('avatar-text').textContent = this.profile.avatar;
}
updateStats() {
if (!this.playerStats) {
// Initialize playerStats if not available
window.playerStats = new PlayerStats();
this.playerStats = window.playerStats;
}
const stats = this.playerStats.getFormattedStats();
const rawStats = this.playerStats.getRawStats();
// Update quick stats
document.getElementById('total-watch-time').textContent = stats.totalWatchTime;
document.getElementById('videos-watched').textContent = stats.videosWatched;
document.getElementById('total-xp').textContent = rawStats.totalXP || 0;
document.getElementById('current-streak').textContent = stats.currentStreak;
document.getElementById('playlists-created').textContent = stats.playlistsCreated;
// Calculate level based on XP
const levelData = this.calculateLevel(rawStats.totalXP || 0);
const level = levelData.level;
const levelInfo = levelData;
document.getElementById('current-level').textContent = `${level} - ${levelInfo.name}`;
// Update level description
document.getElementById('level-desc').textContent = levelInfo.description;
// Update level progress
this.updateLevelProgress(rawStats.totalXP || 0);
// Update detailed stats
document.getElementById('days-active').textContent = stats.daysActive;
document.getElementById('longest-streak').textContent = stats.longestStreak;
document.getElementById('videos-added-playlists').textContent = rawStats.videosAddedToPlaylists || 0;
document.getElementById('videos-skipped').textContent = rawStats.videosSkipped || 0;
document.getElementById('binge-sessions').textContent = rawStats.bingeSessions || 0;
document.getElementById('most-watched-video').textContent = stats.mostWatchedVideo || 'None';
// Update XP breakdown
document.getElementById('quick-play-xp').textContent = rawStats.quickPlayXP || 0;
document.getElementById('porn-cinema-xp').textContent = rawStats.pornCinemaXP || 0;
document.getElementById('scenario-xp').textContent = rawStats.scenarioGameXP || 0;
// Update activity timeline
document.getElementById('first-play-date').textContent = rawStats.firstPlayDate ?
new Date(rawStats.firstPlayDate).toLocaleDateString() : 'N/A';
document.getElementById('last-activity').textContent = rawStats.lastPlayDate ?
new Date(rawStats.lastPlayDate).toLocaleDateString() : 'N/A';
// Update achievements
this.updateAchievements(rawStats);
}
// ===== EXPONENTIAL LEVEL SYSTEM (MATCHES PLAYERSTATS) =====
// Exponential XP scaling: starts at 10 XP for level 2, then grows exponentially
getLevelData() {
// Use PlayerStats level data if available for consistency
if (window.playerStats && typeof window.playerStats.getLevelData === 'function') {
return window.playerStats.getLevelData();
}
// Fallback level data (matches PlayerStats)
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' },
{ level: 11, name: "Perverted", xpRequired: 1137, icon: '🌟', description: 'A seasoned pervert with experience' },
{ level: 12, name: "Depraved", xpRequired: 1716, icon: '🔮', description: 'Mastered the art of depravity' },
{ level: 13, name: "Dominant", xpRequired: 2585, icon: '👑', description: 'A dominant force commanding respect' },
{ level: 14, name: "Submissive", xpRequired: 3889, icon: '🚀', description: 'Embracing submission and control' },
{ level: 15, name: "Hedonist", xpRequired: 5844, icon: '🌌', description: 'Pure hedonism guides your moves' },
{ level: 16, name: "Insatiable", xpRequired: 8777, icon: '⚡', description: 'Your appetite knows no bounds' },
{ level: 17, name: "Transcendent", xpRequired: 13176, icon: '🔥', description: 'Transcending mortal desire' },
{ level: 18, name: "Enlightened", xpRequired: 19775, icon: '🌠', description: 'Achieved enlightened pleasure' },
{ level: 19, name: "Godlike", xpRequired: 29673, icon: '🏛️', description: 'Godlike levels of sexual prowess' },
{ level: 20, name: "Omnipotent", xpRequired: 44520, icon: '👁️', description: 'Ultimate master of pleasure' }
];
}
calculateLevel(totalXP) {
// Use PlayerStats calculation if available for consistency
if (window.playerStats && typeof window.playerStats.calculateLevel === 'function') {
return window.playerStats.calculateLevel(totalXP);
}
// Fallback calculation
const levelData = this.getLevelData();
// Find the highest level that the user has reached
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,
totalXP: totalXP,
currentLevelXP: currentLevelXP,
xpNeededForNext: xpNeededForNext,
progressPercent: Math.min(progressPercent, 100),
nextLevelName: nextLevel ? nextLevel.name : 'Max Level',
icon: currentLevel.icon,
description: currentLevel.description
};
}
getLevelInfo(level) {
const levelData = this.getLevelData();
const levelInfo = levelData.find(l => l.level === level);
// For levels beyond 20, continue with Omnipotent
if (level > 20) {
return {
name: `Omnipotent ${level}`,
icon: '👁️',
description: 'Beyond mortal comprehension'
};
}
return levelInfo || { name: 'Unknown', icon: '❓', description: 'Mysterious level' };
}
updateLevelProgress(totalXp) {
const levelInfo = this.calculateLevel(totalXp);
const progressPercent = levelInfo.progressPercent;
document.getElementById('level-fill').style.width = progressPercent + '%';
document.getElementById('level-text').textContent = Math.round(progressPercent) + '%';
document.getElementById('level-xp').textContent = `${levelInfo.currentLevelXP} / ${levelInfo.xpNeededForNext} XP`;
}
initializeAchievements() {
return [
{
id: 'first-video',
icon: '🎬',
title: 'First Taste',
description: 'Watch your first video',
condition: stats => (stats.videosWatched || 0) >= 1
},
{
id: 'early-bird',
icon: '🌅',
title: 'Eager Explorer',
description: 'Watch 10 videos',
condition: stats => (stats.videosWatched || 0) >= 10
},
{
id: 'marathon',
icon: '🏃',
title: 'Marathon Session',
description: 'Watch for 2+ hours total',
condition: stats => (stats.totalWatchTime || 0) >= 7200
},
{
id: 'curator',
icon: '📚',
title: 'Pleasure Curator',
description: 'Create 5 playlists',
condition: stats => (stats.playlistsCreated || 0) >= 5
},
{
id: 'consistent',
icon: '🔥',
title: 'Addiction 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: '<27>',
title: 'Lustful Awakening',
description: 'Reach Level 5 (Lustful)',
condition: stats => {
if (window.playerStats && typeof window.playerStats.calculateLevel === 'function') {
return window.playerStats.calculateLevel(stats.totalXP || 0).level >= 5;
}
return Math.floor((stats.totalXP || 0) / 100) + 1 >= 5;
}
},
{
id: 'kinky-elite',
icon: '💎',
title: 'Kinky Elite',
description: 'Reach Level 10 (Kinky)',
condition: stats => {
if (window.playerStats && typeof window.playerStats.calculateLevel === 'function') {
return window.playerStats.calculateLevel(stats.totalXP || 0).level >= 10;
}
return Math.floor((stats.totalXP || 0) / 100) + 1 >= 10;
}
},
{
id: 'depraved-master',
icon: '🔮',
title: 'Depraved Master',
description: 'Reach Level 12 (Depraved)',
condition: stats => {
if (window.playerStats && typeof window.playerStats.calculateLevel === 'function') {
return window.playerStats.calculateLevel(stats.totalXP || 0).level >= 12;
}
return Math.floor((stats.totalXP || 0) / 100) + 1 >= 12;
}
},
{
id: 'collector',
icon: '🎭',
title: 'Desire Collector',
description: 'Watch 100 different videos',
condition: stats => (stats.videosWatched || 0) >= 100
}
];
}
updateAchievements(stats) {
this.achievements.forEach(achievement => {
const element = document.getElementById(`achievement-${achievement.id}`);
if (element) {
if (achievement.condition(stats)) {
element.classList.add('unlocked');
element.classList.remove('locked');
} else {
element.classList.add('locked');
element.classList.remove('unlocked');
}
}
});
}
renderAchievements() {
const grid = document.getElementById('achievement-grid');
grid.innerHTML = '';
this.achievements.forEach(achievement => {
const element = document.createElement('div');
element.className = 'achievement locked';
element.id = `achievement-${achievement.id}`;
element.innerHTML = `
<span class="achievement-icon">${achievement.icon}</span>
<div class="achievement-title">${achievement.title}</div>
<div class="achievement-desc">${achievement.description}</div>
`;
grid.appendChild(element);
});
}
formatTime(seconds) {
const hours = Math.floor(seconds / 3600);
const minutes = Math.floor((seconds % 3600) / 60);
return `${hours}h ${minutes}m`;
}
formatDate(dateString) {
return new Date(dateString).toLocaleDateString();
}
showNotification(message, type = 'info') {
// Create notification element
const notification = document.createElement('div');
notification.style.cssText = `
position: fixed;
top: 20px;
right: 20px;
background: ${type === 'success' ? 'var(--success)' : 'var(--primary-color)'};
color: white;
padding: 15px 20px;
border-radius: 8px;
box-shadow: 0 5px 15px rgba(0,0,0,0.3);
z-index: 1000;
font-weight: 600;
transform: translateX(100%);
transition: transform 0.3s ease;
`;
notification.textContent = message;
document.body.appendChild(notification);
// Animate in
setTimeout(() => {
notification.style.transform = 'translateX(0)';
}, 100);
// Remove after 3 seconds
setTimeout(() => {
notification.style.transform = 'translateX(100%)';
setTimeout(() => {
if (notification.parentNode) {
notification.parentNode.removeChild(notification);
}
}, 300);
}, 3000);
}
}
// Global functions for buttons
function saveProfile() {
if (window.userProfile) {
window.userProfile.saveProfile();
}
}
function exportProfile() {
const profile = localStorage.getItem('userProfile');
const stats = localStorage.getItem('playerStats');
const exportData = {
profile: profile ? JSON.parse(profile) : null,
stats: stats ? JSON.parse(stats) : null,
exportDate: new Date().toISOString()
};
const blob = new Blob([JSON.stringify(exportData, null, 2)], { type: 'application/json' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `webgame-profile-${new Date().toISOString().split('T')[0]}.json`;
a.click();
URL.revokeObjectURL(url);
if (window.userProfile) {
window.userProfile.showNotification('Profile exported successfully!', 'success');
}
}
function exportStats() {
if (!window.playerStats) {
alert('No statistics available to export');
return;
}
const exportData = window.playerStats.exportStats();
const dataStr = JSON.stringify(exportData, null, 2);
const dataBlob = new Blob([dataStr], { type: 'application/json' });
const url = URL.createObjectURL(dataBlob);
const link = document.createElement('a');
link.href = url;
link.download = `player-stats-${new Date().toISOString().split('T')[0]}.json`;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
URL.revokeObjectURL(url);
if (window.userProfile) {
window.userProfile.showNotification('Statistics exported successfully!', 'success');
}
}
function resetStats() {
if (confirm('Are you sure you want to reset all statistics? This action cannot be undone.')) {
if (window.playerStats) {
window.playerStats.resetStats();
if (window.userProfile) {
window.userProfile.updateStats();
window.userProfile.showNotification('Statistics reset successfully!', 'success');
}
}
}
}
function resetProfile() {
if (confirm('Are you sure you want to reset your profile? This action cannot be undone.')) {
localStorage.removeItem('userProfile');
if (window.userProfile) {
window.userProfile.profile = window.userProfile.loadProfile();
window.userProfile.updateDisplay();
window.userProfile.showNotification('Profile reset successfully!', 'success');
}
}
}
function importProfile() {
const input = document.createElement('input');
input.type = 'file';
input.accept = '.json';
input.onchange = function(e) {
const file = e.target.files[0];
if (!file) return;
const reader = new FileReader();
reader.onload = function(e) {
try {
const data = JSON.parse(e.target.result);
if (data.profile) {
localStorage.setItem('userProfile', JSON.stringify(data.profile));
}
if (data.stats) {
localStorage.setItem('playerStats', JSON.stringify(data.stats));
}
// Reload page to apply changes
window.location.reload();
} catch (error) {
alert('Invalid profile file format');
}
};
reader.readAsText(file);
};
input.click();
}
// Initialize when page loads
document.addEventListener('DOMContentLoaded', () => {
// Initialize theme switcher UI
if (window.themeManager) {
const themeSwitcher = window.themeManager.createThemeToggle();
const container = document.getElementById('theme-switcher-container');
if (container) {
container.appendChild(themeSwitcher);
console.log(' Theme switcher initialized');
}
}
window.userProfile = new UserProfile();
// Profile Picture Upload Handler
document.getElementById('avatar-upload').addEventListener('change', function(e) {
const file = e.target.files[0];
if (file && file.type.startsWith('image/')) {
const reader = new FileReader();
reader.onload = function(event) {
const avatarImage = document.getElementById('avatar-image');
const avatarText = document.getElementById('avatar-text');
avatarImage.src = event.target.result;
avatarImage.style.display = 'block';
avatarText.style.display = 'none';
// Save to localStorage
localStorage.setItem('profilePicture', event.target.result);
if (window.userProfile) {
window.userProfile.showNotification('Profile picture updated!', 'success');
}
};
reader.readAsDataURL(file);
}
});
// Load saved profile picture
const savedPicture = localStorage.getItem('profilePicture');
if (savedPicture) {
const avatarImage = document.getElementById('avatar-image');
const avatarText = document.getElementById('avatar-text');
avatarImage.src = savedPicture;
avatarImage.style.display = 'block';
avatarText.style.display = 'none';
}
// Profile avatar click handler
document.getElementById('profile-avatar').addEventListener('click', function() {
document.getElementById('avatar-upload').click();
});
// Apply sidebar image theme from home page settings
applySidebarTheme();
});
// Function to apply sidebar image theme
async function applySidebarTheme() {
const savedTheme = localStorage.getItem('selectedTheme') || 'hentai';
const characterSides = document.querySelectorAll('.character-side');
console.log('Applying sidebar theme:', savedTheme);
if (savedTheme === 'none') {
characterSides.forEach(element => {
element.style.display = 'none';
});
return;
}
// Show all sides
characterSides.forEach(element => {
element.style.display = 'block';
});
// Update image sources based on theme
const availableImages = [];
if (savedTheme === 'library') {
// Use user library images if available
const libraryImages = JSON.parse(localStorage.getItem('userLibraryImages') || '[]');
console.log('Library images from localStorage:', libraryImages.length, 'images found');
if (libraryImages.length > 0) {
characterSides.forEach((element, index) => {
const randomImage = libraryImages[Math.floor(Math.random() * libraryImages.length)];
const imagePath = randomImage.path || randomImage;
console.log(`Setting sidebar ${index} to:`, imagePath);
element.style.backgroundImage = `url('file:///${imagePath.replace(/\\/g, '/')}')`;
});
return;
} else {
// No library images available, hide sidebar
console.log('No library images in localStorage, hiding sidebar');
characterSides.forEach(element => {
element.style.display = 'none';
});
return;
}
}
// For asset themes (hentai, pornstars, BBC, feet)
const themePath = `assets/${savedTheme}`;
characterSides.forEach((element, index) => {
// Keep the existing image numbering but update the folder
const imageNum = index < 10 ? [1,11,3,4,5,6,7,8,9,10,12,13][index] : 1;
element.style.backgroundImage = `url('${themePath}/${imageNum}.png')`;
});
}
</script>
</div>
</body>
</html>