Initial commit
This commit is contained in:
@@ -0,0 +1,966 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Avatar Creator - GamerComp</title>
|
||||
<style>
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
|
||||
:root {
|
||||
--bg-dark: #1a1a2e;
|
||||
--bg-panel: #16213e;
|
||||
--bg-item: #0f3460;
|
||||
--accent: #e94560;
|
||||
--accent-hover: #ff6b6b;
|
||||
--text: #eee;
|
||||
--text-muted: #aaa;
|
||||
--border: #2a3a5e;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||
background: var(--bg-dark);
|
||||
color: var(--text);
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
header {
|
||||
background: var(--bg-panel);
|
||||
padding: 1rem 2rem;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
border-bottom: 2px solid var(--border);
|
||||
}
|
||||
|
||||
header h1 {
|
||||
font-size: 1.5rem;
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
.btn {
|
||||
padding: 0.6rem 1.5rem;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
font-size: 1rem;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background: var(--accent);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
background: var(--accent-hover);
|
||||
transform: scale(1.02);
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
background: var(--bg-item);
|
||||
color: var(--text);
|
||||
border: 1px solid var(--border);
|
||||
}
|
||||
|
||||
.btn-secondary:hover {
|
||||
background: var(--border);
|
||||
}
|
||||
|
||||
main {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
padding: 1rem;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
/* Preview Panel */
|
||||
.preview-panel {
|
||||
width: 300px;
|
||||
background: var(--bg-panel);
|
||||
border-radius: 12px;
|
||||
padding: 1.5rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.preview-canvas-container {
|
||||
background: linear-gradient(135deg, #2a3a5e, #1a2a4e);
|
||||
border-radius: 12px;
|
||||
padding: 2rem;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
#previewCanvas {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.preview-modes {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
margin-bottom: 1rem;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.preview-modes button {
|
||||
padding: 0.4rem 0.8rem;
|
||||
font-size: 0.75rem;
|
||||
border-radius: 6px;
|
||||
background: var(--bg-item);
|
||||
border: 1px solid var(--border);
|
||||
color: var(--text-muted);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.preview-modes button.active {
|
||||
background: var(--accent);
|
||||
color: white;
|
||||
border-color: var(--accent);
|
||||
}
|
||||
|
||||
.preview-animations {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.preview-animations button {
|
||||
padding: 0.3rem 0.6rem;
|
||||
font-size: 0.7rem;
|
||||
border-radius: 4px;
|
||||
background: var(--bg-dark);
|
||||
border: 1px solid var(--border);
|
||||
color: var(--text-muted);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.preview-animations button:hover {
|
||||
border-color: var(--accent);
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
/* Customization Panel */
|
||||
.customization-panel {
|
||||
flex: 1;
|
||||
background: var(--bg-panel);
|
||||
border-radius: 12px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.category-tabs {
|
||||
display: flex;
|
||||
background: var(--bg-dark);
|
||||
padding: 0.5rem;
|
||||
gap: 0.5rem;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.category-tab {
|
||||
padding: 0.6rem 1rem;
|
||||
border-radius: 8px;
|
||||
background: transparent;
|
||||
border: none;
|
||||
color: var(--text-muted);
|
||||
cursor: pointer;
|
||||
white-space: nowrap;
|
||||
font-size: 0.9rem;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.category-tab:hover {
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
.category-tab.active {
|
||||
background: var(--accent);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.options-container {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.option-group {
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.option-group h3 {
|
||||
font-size: 0.9rem;
|
||||
color: var(--text-muted);
|
||||
margin-bottom: 0.75rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
|
||||
.option-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(50px, 1fr));
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.option-grid.colors {
|
||||
grid-template-columns: repeat(auto-fill, minmax(36px, 1fr));
|
||||
}
|
||||
|
||||
.option-item {
|
||||
aspect-ratio: 1;
|
||||
border-radius: 8px;
|
||||
border: 2px solid transparent;
|
||||
background: var(--bg-item);
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: all 0.2s;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.option-item:hover {
|
||||
border-color: var(--text-muted);
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
.option-item.selected {
|
||||
border-color: var(--accent);
|
||||
box-shadow: 0 0 10px rgba(233, 69, 96, 0.4);
|
||||
}
|
||||
|
||||
.option-item.color-swatch {
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.option-item img, .option-item canvas {
|
||||
max-width: 80%;
|
||||
max-height: 80%;
|
||||
}
|
||||
|
||||
.option-item .label {
|
||||
font-size: 0.6rem;
|
||||
color: var(--text-muted);
|
||||
position: absolute;
|
||||
bottom: 2px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* Accessories Panel */
|
||||
.accessories-panel {
|
||||
width: 280px;
|
||||
background: var(--bg-panel);
|
||||
border-radius: 12px;
|
||||
padding: 1rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.accessories-panel h2 {
|
||||
font-size: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
.accessory-section {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.accessory-section h3 {
|
||||
font-size: 0.8rem;
|
||||
color: var(--text-muted);
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.accessory-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.accessory-item {
|
||||
aspect-ratio: 1;
|
||||
border-radius: 6px;
|
||||
background: var(--bg-item);
|
||||
border: 2px solid transparent;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.accessory-item:hover {
|
||||
border-color: var(--text-muted);
|
||||
}
|
||||
|
||||
.accessory-item.equipped {
|
||||
border-color: var(--accent);
|
||||
}
|
||||
|
||||
.accessory-item.locked {
|
||||
opacity: 0.4;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.accessory-item .level-badge {
|
||||
position: absolute;
|
||||
top: 2px;
|
||||
right: 2px;
|
||||
font-size: 0.55rem;
|
||||
background: var(--bg-dark);
|
||||
padding: 1px 4px;
|
||||
border-radius: 3px;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.rarity-common { border-color: #9d9d9d !important; }
|
||||
.rarity-uncommon { border-color: #1eff00 !important; }
|
||||
.rarity-rare { border-color: #0070dd !important; }
|
||||
.rarity-epic { border-color: #a335ee !important; }
|
||||
.rarity-legendary { border-color: #ff8000 !important; }
|
||||
.rarity-mythic { border-color: #e6cc80 !important; }
|
||||
|
||||
/* Randomize Button */
|
||||
.randomize-btn {
|
||||
margin-top: auto;
|
||||
width: 100%;
|
||||
padding: 0.8rem;
|
||||
border-radius: 8px;
|
||||
background: var(--bg-item);
|
||||
border: 1px dashed var(--border);
|
||||
color: var(--text);
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.randomize-btn:hover {
|
||||
border-color: var(--accent);
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
/* Responsive */
|
||||
@media (max-width: 900px) {
|
||||
main {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.preview-panel, .accessories-panel {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.preview-canvas-container {
|
||||
padding: 1rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<h1>Avatar Creator</h1>
|
||||
<div style="display: flex; gap: 0.5rem;">
|
||||
<button class="btn btn-secondary" id="resetBtn">Reset</button>
|
||||
<button class="btn btn-primary" id="saveBtn">Save Avatar</button>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
<!-- Preview Panel -->
|
||||
<div class="preview-panel">
|
||||
<div class="preview-canvas-container">
|
||||
<canvas id="previewCanvas" width="128" height="256"></canvas>
|
||||
</div>
|
||||
|
||||
<div class="preview-modes">
|
||||
<button data-mode="HEAD_ONLY">Head</button>
|
||||
<button data-mode="HEAD_AND_SHOULDERS">Shoulders</button>
|
||||
<button data-mode="UPPER_BODY">Upper</button>
|
||||
<button data-mode="FULL_BODY" class="active">Full</button>
|
||||
</div>
|
||||
|
||||
<div class="preview-animations">
|
||||
<button data-anim="idle">Idle</button>
|
||||
<button data-anim="walk">Walk</button>
|
||||
<button data-anim="run">Run</button>
|
||||
<button data-anim="jump">Jump</button>
|
||||
<button data-anim="wave">Wave</button>
|
||||
<button data-anim="celebrate">Celebrate</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Customization Panel -->
|
||||
<div class="customization-panel">
|
||||
<div class="category-tabs">
|
||||
<button class="category-tab active" data-category="face">Face</button>
|
||||
<button class="category-tab" data-category="eyes">Eyes</button>
|
||||
<button class="category-tab" data-category="hair">Hair</button>
|
||||
<button class="category-tab" data-category="body">Body</button>
|
||||
<button class="category-tab" data-category="clothing">Clothing</button>
|
||||
</div>
|
||||
|
||||
<div class="options-container" id="optionsContainer">
|
||||
<!-- Options populated by JavaScript -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Accessories Panel -->
|
||||
<div class="accessories-panel">
|
||||
<h2>Equipped Accessories</h2>
|
||||
|
||||
<div class="accessory-section">
|
||||
<h3>Eyewear</h3>
|
||||
<div class="accessory-grid" id="eyewearGrid">
|
||||
<!-- Populated by JavaScript -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="accessory-section">
|
||||
<h3>Headwear</h3>
|
||||
<div class="accessory-grid" id="headwearGrid">
|
||||
<!-- Populated by JavaScript -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="accessory-section">
|
||||
<h3>Effects</h3>
|
||||
<div class="accessory-grid" id="effectsGrid">
|
||||
<!-- Populated by JavaScript -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button class="randomize-btn" id="randomizeBtn">
|
||||
<span>🎲</span> Randomize
|
||||
</button>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<!-- Load Avatar System -->
|
||||
<script src="/assets/avatar/avatarData.js"></script>
|
||||
<script src="/assets/avatar/animations.js"></script>
|
||||
<script src="/assets/avatar/accessories.js"></script>
|
||||
<script src="/assets/avatar/avatarRenderer.js"></script>
|
||||
<script src="/assets/avatar/accessoryRenderer.js"></script>
|
||||
<script src="/assets/avatar/avatarSystem.js"></script>
|
||||
|
||||
<script>
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
// Current avatar state
|
||||
var avatar = null;
|
||||
var currentMode = 'FULL_BODY';
|
||||
var currentAnimation = 'idle';
|
||||
var animationFrame = 0;
|
||||
var lastFrameTime = 0;
|
||||
|
||||
// Canvas setup
|
||||
var canvas = document.getElementById('previewCanvas');
|
||||
var ctx = canvas.getContext('2d');
|
||||
|
||||
// Initialize
|
||||
function init() {
|
||||
// Create default avatar
|
||||
avatar = AvatarSystem.createDefault();
|
||||
|
||||
// For demo, give some earned accessories
|
||||
avatar.earned = {
|
||||
eyewear: ['sunglasses-classic', 'nerd-glasses', 'aviator'],
|
||||
headwear: ['baseball-cap', 'beanie', 'crown-gold'],
|
||||
effects: ['sparkle-trail']
|
||||
};
|
||||
avatar.level = 42;
|
||||
|
||||
// Setup UI
|
||||
setupCategoryTabs();
|
||||
setupModeButtons();
|
||||
setupAnimationButtons();
|
||||
setupAccessoriesPanel();
|
||||
renderOptions('face');
|
||||
|
||||
// Start render loop
|
||||
requestAnimationFrame(renderLoop);
|
||||
}
|
||||
|
||||
// Render loop
|
||||
function renderLoop(time) {
|
||||
// Update animation frame
|
||||
var anim = window.AvatarAnimations && AvatarAnimations.getAnimation(currentMode, currentAnimation);
|
||||
if (anim) {
|
||||
var frameDuration = anim.frameDuration || 100;
|
||||
if (time - lastFrameTime > frameDuration) {
|
||||
animationFrame = (animationFrame + 1) % anim.frameCount;
|
||||
lastFrameTime = time;
|
||||
}
|
||||
}
|
||||
|
||||
// Clear and render
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||
|
||||
var modeSize = window.AvatarSystem ? AvatarSystem.getModeSize(currentMode) : { width: 64, height: 160 };
|
||||
var scale = Math.min(
|
||||
(canvas.width - 20) / modeSize.width,
|
||||
(canvas.height - 20) / modeSize.height
|
||||
);
|
||||
|
||||
var x = canvas.width / 2;
|
||||
var y = canvas.height / 2 + (modeSize.height * scale) / 4;
|
||||
|
||||
if (window.AvatarSystem) {
|
||||
AvatarSystem.render(avatar, currentMode, ctx, x, y, {
|
||||
scale: scale,
|
||||
animation: currentAnimation,
|
||||
frame: animationFrame,
|
||||
time: time / 1000
|
||||
});
|
||||
} else {
|
||||
// Fallback placeholder
|
||||
ctx.fillStyle = '#e94560';
|
||||
ctx.beginPath();
|
||||
ctx.arc(x, y - 40, 30, 0, Math.PI * 2);
|
||||
ctx.fill();
|
||||
ctx.fillStyle = '#16213e';
|
||||
ctx.fillRect(x - 20, y - 5, 40, 50);
|
||||
}
|
||||
|
||||
requestAnimationFrame(renderLoop);
|
||||
}
|
||||
|
||||
// Category tabs
|
||||
function setupCategoryTabs() {
|
||||
var tabs = document.querySelectorAll('.category-tab');
|
||||
tabs.forEach(function(tab) {
|
||||
tab.addEventListener('click', function() {
|
||||
tabs.forEach(function(t) { t.classList.remove('active'); });
|
||||
tab.classList.add('active');
|
||||
renderOptions(tab.dataset.category);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Mode buttons
|
||||
function setupModeButtons() {
|
||||
var buttons = document.querySelectorAll('.preview-modes button');
|
||||
buttons.forEach(function(btn) {
|
||||
btn.addEventListener('click', function() {
|
||||
buttons.forEach(function(b) { b.classList.remove('active'); });
|
||||
btn.classList.add('active');
|
||||
currentMode = btn.dataset.mode;
|
||||
animationFrame = 0;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Animation buttons
|
||||
function setupAnimationButtons() {
|
||||
var buttons = document.querySelectorAll('.preview-animations button');
|
||||
buttons.forEach(function(btn) {
|
||||
btn.addEventListener('click', function() {
|
||||
currentAnimation = btn.dataset.anim;
|
||||
animationFrame = 0;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Render customization options
|
||||
function renderOptions(category) {
|
||||
var container = document.getElementById('optionsContainer');
|
||||
container.innerHTML = '';
|
||||
|
||||
if (!window.AvatarData) {
|
||||
container.innerHTML = '<p style="color: var(--text-muted); padding: 2rem;">Loading options...</p>';
|
||||
return;
|
||||
}
|
||||
|
||||
switch (category) {
|
||||
case 'face':
|
||||
renderFaceOptions(container);
|
||||
break;
|
||||
case 'eyes':
|
||||
renderEyeOptions(container);
|
||||
break;
|
||||
case 'hair':
|
||||
renderHairOptions(container);
|
||||
break;
|
||||
case 'body':
|
||||
renderBodyOptions(container);
|
||||
break;
|
||||
case 'clothing':
|
||||
renderClothingOptions(container);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function renderFaceOptions(container) {
|
||||
// Face Shape
|
||||
var group1 = createOptionGroup('Face Shape');
|
||||
var grid1 = createOptionGrid();
|
||||
AvatarData.FACE_SHAPES.forEach(function(shape) {
|
||||
var item = createOptionItem(shape.name, shape.id === avatar.base.faceShape);
|
||||
item.addEventListener('click', function() {
|
||||
avatar.base.faceShape = shape.id;
|
||||
renderOptions('face');
|
||||
});
|
||||
grid1.appendChild(item);
|
||||
});
|
||||
group1.appendChild(grid1);
|
||||
container.appendChild(group1);
|
||||
|
||||
// Skin Tone
|
||||
var group2 = createOptionGroup('Skin Tone');
|
||||
var grid2 = createOptionGrid('colors');
|
||||
AvatarData.SKIN_TONES.forEach(function(tone) {
|
||||
var item = createColorSwatch(tone.hex, avatar.base.skinTone === tone.hex);
|
||||
item.addEventListener('click', function() {
|
||||
avatar.base.skinTone = tone.hex;
|
||||
renderOptions('face');
|
||||
});
|
||||
grid2.appendChild(item);
|
||||
});
|
||||
group2.appendChild(grid2);
|
||||
container.appendChild(group2);
|
||||
|
||||
// Nose
|
||||
var group3 = createOptionGroup('Nose');
|
||||
var grid3 = createOptionGrid();
|
||||
AvatarData.NOSE_SHAPES.forEach(function(nose) {
|
||||
var item = createOptionItem(nose.name, nose.id === avatar.base.nose);
|
||||
item.addEventListener('click', function() {
|
||||
avatar.base.nose = nose.id;
|
||||
renderOptions('face');
|
||||
});
|
||||
grid3.appendChild(item);
|
||||
});
|
||||
group3.appendChild(grid3);
|
||||
container.appendChild(group3);
|
||||
|
||||
// Mouth
|
||||
var group4 = createOptionGroup('Mouth');
|
||||
var grid4 = createOptionGrid();
|
||||
AvatarData.MOUTH_SHAPES.forEach(function(mouth) {
|
||||
var item = createOptionItem(mouth.name, mouth.id === avatar.base.mouth);
|
||||
item.addEventListener('click', function() {
|
||||
avatar.base.mouth = mouth.id;
|
||||
renderOptions('face');
|
||||
});
|
||||
grid4.appendChild(item);
|
||||
});
|
||||
group4.appendChild(grid4);
|
||||
container.appendChild(group4);
|
||||
}
|
||||
|
||||
function renderEyeOptions(container) {
|
||||
// Eye Shape
|
||||
var group1 = createOptionGroup('Eye Shape');
|
||||
var grid1 = createOptionGrid();
|
||||
AvatarData.EYE_SHAPES.forEach(function(shape) {
|
||||
var item = createOptionItem(shape.name, shape.id === avatar.base.eyes.shape);
|
||||
item.addEventListener('click', function() {
|
||||
avatar.base.eyes.shape = shape.id;
|
||||
renderOptions('eyes');
|
||||
});
|
||||
grid1.appendChild(item);
|
||||
});
|
||||
group1.appendChild(grid1);
|
||||
container.appendChild(group1);
|
||||
|
||||
// Eye Color
|
||||
var group2 = createOptionGroup('Eye Color');
|
||||
var grid2 = createOptionGrid('colors');
|
||||
AvatarData.EYE_COLORS.forEach(function(color) {
|
||||
var item = createColorSwatch(color.hex, color.id === avatar.base.eyes.color);
|
||||
item.addEventListener('click', function() {
|
||||
avatar.base.eyes.color = color.id;
|
||||
renderOptions('eyes');
|
||||
});
|
||||
grid2.appendChild(item);
|
||||
});
|
||||
group2.appendChild(grid2);
|
||||
container.appendChild(group2);
|
||||
|
||||
// Eyebrows
|
||||
var group3 = createOptionGroup('Eyebrows');
|
||||
var grid3 = createOptionGrid();
|
||||
AvatarData.EYEBROW_SHAPES.forEach(function(brow) {
|
||||
var item = createOptionItem(brow.name, brow.id === avatar.base.eyebrows);
|
||||
item.addEventListener('click', function() {
|
||||
avatar.base.eyebrows = brow.id;
|
||||
renderOptions('eyes');
|
||||
});
|
||||
grid3.appendChild(item);
|
||||
});
|
||||
group3.appendChild(grid3);
|
||||
container.appendChild(group3);
|
||||
}
|
||||
|
||||
function renderHairOptions(container) {
|
||||
// Hair Style
|
||||
var group1 = createOptionGroup('Hair Style');
|
||||
var grid1 = createOptionGrid();
|
||||
AvatarData.HAIR_STYLES.forEach(function(style) {
|
||||
var item = createOptionItem(style.name, style.id === avatar.base.hair.style);
|
||||
item.addEventListener('click', function() {
|
||||
avatar.base.hair.style = style.id;
|
||||
renderOptions('hair');
|
||||
});
|
||||
grid1.appendChild(item);
|
||||
});
|
||||
group1.appendChild(grid1);
|
||||
container.appendChild(group1);
|
||||
|
||||
// Hair Color
|
||||
var group2 = createOptionGroup('Hair Color');
|
||||
var grid2 = createOptionGrid('colors');
|
||||
AvatarData.HAIR_COLORS.forEach(function(color) {
|
||||
var item = createColorSwatch(color.hex, avatar.base.hair.color === color.hex);
|
||||
item.addEventListener('click', function() {
|
||||
avatar.base.hair.color = color.hex;
|
||||
renderOptions('hair');
|
||||
});
|
||||
grid2.appendChild(item);
|
||||
});
|
||||
group2.appendChild(grid2);
|
||||
container.appendChild(group2);
|
||||
|
||||
// Facial Hair
|
||||
var group3 = createOptionGroup('Facial Hair');
|
||||
var grid3 = createOptionGrid();
|
||||
AvatarData.FACIAL_HAIR.forEach(function(fh) {
|
||||
var item = createOptionItem(fh.name, fh.id === avatar.base.facialHair);
|
||||
item.addEventListener('click', function() {
|
||||
avatar.base.facialHair = fh.id;
|
||||
renderOptions('hair');
|
||||
});
|
||||
grid3.appendChild(item);
|
||||
});
|
||||
group3.appendChild(grid3);
|
||||
container.appendChild(group3);
|
||||
}
|
||||
|
||||
function renderBodyOptions(container) {
|
||||
// Body Type
|
||||
var group1 = createOptionGroup('Body Type');
|
||||
var grid1 = createOptionGrid();
|
||||
AvatarData.BODY_TYPES.forEach(function(type) {
|
||||
var item = createOptionItem(type.name, type.id === avatar.base.body.type);
|
||||
item.addEventListener('click', function() {
|
||||
avatar.base.body.type = type.id;
|
||||
renderOptions('body');
|
||||
});
|
||||
grid1.appendChild(item);
|
||||
});
|
||||
group1.appendChild(grid1);
|
||||
container.appendChild(group1);
|
||||
}
|
||||
|
||||
function renderClothingOptions(container) {
|
||||
// Clothing Style
|
||||
var group1 = createOptionGroup('Style');
|
||||
var grid1 = createOptionGrid();
|
||||
AvatarData.CLOTHING_STYLES.forEach(function(style) {
|
||||
var item = createOptionItem(style.name, style.id === avatar.base.clothing.style);
|
||||
item.addEventListener('click', function() {
|
||||
avatar.base.clothing.style = style.id;
|
||||
renderOptions('clothing');
|
||||
});
|
||||
grid1.appendChild(item);
|
||||
});
|
||||
group1.appendChild(grid1);
|
||||
container.appendChild(group1);
|
||||
|
||||
// Shirt Color
|
||||
var group2 = createOptionGroup('Shirt Color');
|
||||
var grid2 = createOptionGrid('colors');
|
||||
AvatarData.CLOTHING_COLORS.forEach(function(color) {
|
||||
var item = createColorSwatch(color, avatar.base.clothing.shirtColor === color);
|
||||
item.addEventListener('click', function() {
|
||||
avatar.base.clothing.shirtColor = color;
|
||||
renderOptions('clothing');
|
||||
});
|
||||
grid2.appendChild(item);
|
||||
});
|
||||
group2.appendChild(grid2);
|
||||
container.appendChild(group2);
|
||||
|
||||
// Pants Color
|
||||
var group3 = createOptionGroup('Pants Color');
|
||||
var grid3 = createOptionGrid('colors');
|
||||
AvatarData.CLOTHING_COLORS.forEach(function(color) {
|
||||
var item = createColorSwatch(color, avatar.base.clothing.pantsColor === color);
|
||||
item.addEventListener('click', function() {
|
||||
avatar.base.clothing.pantsColor = color;
|
||||
renderOptions('clothing');
|
||||
});
|
||||
grid3.appendChild(item);
|
||||
});
|
||||
group3.appendChild(grid3);
|
||||
container.appendChild(group3);
|
||||
}
|
||||
|
||||
// Accessories panel
|
||||
function setupAccessoriesPanel() {
|
||||
updateAccessoriesUI();
|
||||
|
||||
document.getElementById('randomizeBtn').addEventListener('click', randomizeAvatar);
|
||||
document.getElementById('resetBtn').addEventListener('click', function() {
|
||||
avatar = AvatarSystem.createDefault();
|
||||
avatar.earned = { eyewear: ['sunglasses-classic'], headwear: ['baseball-cap'], effects: [] };
|
||||
avatar.level = 1;
|
||||
renderOptions('face');
|
||||
updateAccessoriesUI();
|
||||
});
|
||||
|
||||
document.getElementById('saveBtn').addEventListener('click', function() {
|
||||
// Would save to API
|
||||
console.log('Saving avatar:', JSON.stringify(avatar, null, 2));
|
||||
alert('Avatar saved! (Check console for data)');
|
||||
});
|
||||
}
|
||||
|
||||
function updateAccessoriesUI() {
|
||||
if (!window.AvatarAccessories) return;
|
||||
|
||||
// Eyewear
|
||||
var eyewearGrid = document.getElementById('eyewearGrid');
|
||||
eyewearGrid.innerHTML = '';
|
||||
var eyewearItems = AvatarAccessories.getByCategory('eyewear').slice(0, 8);
|
||||
eyewearItems.forEach(function(item) {
|
||||
var el = createAccessoryItem(item, 'eyewear');
|
||||
eyewearGrid.appendChild(el);
|
||||
});
|
||||
|
||||
// Headwear
|
||||
var headwearGrid = document.getElementById('headwearGrid');
|
||||
headwearGrid.innerHTML = '';
|
||||
var headwearItems = AvatarAccessories.getByCategory('headwear').slice(0, 8);
|
||||
headwearItems.forEach(function(item) {
|
||||
var el = createAccessoryItem(item, 'headwear');
|
||||
headwearGrid.appendChild(el);
|
||||
});
|
||||
|
||||
// Effects
|
||||
var effectsGrid = document.getElementById('effectsGrid');
|
||||
effectsGrid.innerHTML = '';
|
||||
var effectItems = AvatarAccessories.getByCategory('effect').slice(0, 8);
|
||||
effectItems.forEach(function(item) {
|
||||
var el = createAccessoryItem(item, 'effect');
|
||||
effectsGrid.appendChild(el);
|
||||
});
|
||||
}
|
||||
|
||||
function createAccessoryItem(item, category) {
|
||||
var div = document.createElement('div');
|
||||
div.className = 'accessory-item';
|
||||
|
||||
var earned = avatar.earned[category === 'effect' ? 'effects' : category] || [];
|
||||
var isEarned = earned.includes(item.id);
|
||||
var isEquipped = avatar.equipped[category] === item.id;
|
||||
|
||||
if (!isEarned) {
|
||||
div.classList.add('locked');
|
||||
div.innerHTML = '<span class="level-badge">Lv' + item.unlockLevel + '</span>';
|
||||
} else {
|
||||
div.classList.add('rarity-' + item.rarity);
|
||||
if (isEquipped) div.classList.add('equipped');
|
||||
div.innerHTML = '<span style="font-size:1.2rem">' + getAccessoryEmoji(item.id) + '</span>';
|
||||
|
||||
div.addEventListener('click', function() {
|
||||
if (isEquipped) {
|
||||
avatar.equipped[category] = null;
|
||||
} else {
|
||||
avatar.equipped[category] = item.id;
|
||||
}
|
||||
updateAccessoriesUI();
|
||||
});
|
||||
}
|
||||
|
||||
div.title = item.name;
|
||||
return div;
|
||||
}
|
||||
|
||||
function getAccessoryEmoji(id) {
|
||||
var emojis = {
|
||||
'sunglasses-classic': '🕶️', 'aviator': '🥽', 'nerd-glasses': '🤓',
|
||||
'baseball-cap': '🧢', 'beanie': '🎿', 'crown-gold': '👑',
|
||||
'sparkle-trail': '✨', 'fire-aura': '🔥', 'lightning-crackle': '⚡'
|
||||
};
|
||||
return emojis[id] || '🎁';
|
||||
}
|
||||
|
||||
// Helper functions
|
||||
function createOptionGroup(title) {
|
||||
var div = document.createElement('div');
|
||||
div.className = 'option-group';
|
||||
var h3 = document.createElement('h3');
|
||||
h3.textContent = title;
|
||||
div.appendChild(h3);
|
||||
return div;
|
||||
}
|
||||
|
||||
function createOptionGrid(type) {
|
||||
var div = document.createElement('div');
|
||||
div.className = 'option-grid' + (type ? ' ' + type : '');
|
||||
return div;
|
||||
}
|
||||
|
||||
function createOptionItem(label, selected) {
|
||||
var div = document.createElement('div');
|
||||
div.className = 'option-item' + (selected ? ' selected' : '');
|
||||
var span = document.createElement('span');
|
||||
span.className = 'label';
|
||||
span.textContent = label.length > 8 ? label.substring(0, 7) + '…' : label;
|
||||
div.appendChild(span);
|
||||
return div;
|
||||
}
|
||||
|
||||
function createColorSwatch(color, selected) {
|
||||
var div = document.createElement('div');
|
||||
div.className = 'option-item color-swatch' + (selected ? ' selected' : '');
|
||||
div.style.backgroundColor = color;
|
||||
return div;
|
||||
}
|
||||
|
||||
function randomizeAvatar() {
|
||||
if (!window.AvatarData) return;
|
||||
|
||||
function pick(arr) { return arr[Math.floor(Math.random() * arr.length)]; }
|
||||
|
||||
avatar.base.faceShape = pick(AvatarData.FACE_SHAPES).id;
|
||||
avatar.base.skinTone = pick(AvatarData.SKIN_TONES).hex;
|
||||
avatar.base.eyes.shape = pick(AvatarData.EYE_SHAPES).id;
|
||||
avatar.base.eyes.color = pick(AvatarData.EYE_COLORS).id;
|
||||
avatar.base.eyebrows = pick(AvatarData.EYEBROW_SHAPES).id;
|
||||
avatar.base.nose = pick(AvatarData.NOSE_SHAPES).id;
|
||||
avatar.base.mouth = pick(AvatarData.MOUTH_SHAPES).id;
|
||||
avatar.base.hair.style = pick(AvatarData.HAIR_STYLES).id;
|
||||
avatar.base.hair.color = pick(AvatarData.HAIR_COLORS).hex;
|
||||
avatar.base.facialHair = pick(AvatarData.FACIAL_HAIR).id;
|
||||
avatar.base.body.type = pick(AvatarData.BODY_TYPES).id;
|
||||
avatar.base.clothing.style = pick(AvatarData.CLOTHING_STYLES).id;
|
||||
avatar.base.clothing.shirtColor = pick(AvatarData.CLOTHING_COLORS);
|
||||
avatar.base.clothing.pantsColor = pick(AvatarData.CLOTHING_COLORS);
|
||||
|
||||
renderOptions(document.querySelector('.category-tab.active').dataset.category);
|
||||
}
|
||||
|
||||
// Initialize when ready
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', init);
|
||||
} else {
|
||||
init();
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user