1276 lines
51 KiB
HTML
1276 lines
51 KiB
HTML
<!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> |