document.addEventListener('DOMContentLoaded', () => { // State const characterState = { name: '', age: 24, gender: 'Male', traits: [], points: 3, stats: { logic: 50, emotion: 50, id: 50, strength: 50, agility: 50, toughness: 50, perception: 50 }, beliefs: { collectivism: 50, loyalty: 20, rationality: 70, life: 40 }, background: { childhood: 'Vat-Grown Soldier', adulthood: 'Corporate Executive', skills: { melee: 4, ranged: 2, social: 3, intellectual: 5, crafting: 2, medicine: 0 } } }; // Tab Switching Logic const tabBtns = document.querySelectorAll('.tab-btn'); const tabContents = document.querySelectorAll('.tab-content'); tabBtns.forEach(btn => { btn.addEventListener('click', () => { const target = btn.dataset.target; // Update buttons tabBtns.forEach(b => { b.classList.remove('text-neural-blue', 'border-b-2', 'border-neural-blue'); b.classList.add('text-slate-500', 'border-transparent'); }); btn.classList.remove('text-slate-500', 'border-transparent'); btn.classList.add('text-neural-blue', 'border-b-2', 'border-neural-blue'); // Update content tabContents.forEach(content => { if (content.id === target) { content.classList.remove('hidden'); // Trigger specific animations or logic per tab if needed if (target === 'psychology') { drawNeuralPreview(); } } else { content.classList.add('hidden'); } }); }); }); // Trait Selection Logic const traitItems = document.querySelectorAll('.trait-item'); const pointsDisplay = document.getElementById('trait-points'); traitItems.forEach(item => { item.addEventListener('click', () => { const costText = item.querySelector('span.text-xs').innerText; const cost = parseInt(costText); const traitName = item.querySelector('h4').innerText; if (item.classList.contains('selected')) { // Deselect item.classList.remove('selected'); characterState.traits = characterState.traits.filter(t => t !== traitName); characterState.points += cost; } else { // Select if (characterState.points - cost >= 0) { item.classList.add('selected'); characterState.traits.push(traitName); characterState.points -= cost; } else { // Shake effect or error feedback could go here console.log("Not enough points"); } } pointsDisplay.innerText = characterState.points; updateNeuralPreview(); }); }); // Character Name Input const charNameInput = document.getElementById('char-name'); const charNameDisplay = document.getElementById('char-name-display'); charNameInput.addEventListener('input', (e) => { characterState.name = e.target.value; charNameDisplay.innerText = e.target.value || 'Unknown'; }); // Age Slider const ageSlider = document.getElementById('char-age'); const ageValue = document.getElementById('age-value'); const ageDisplay = document.getElementById('char-age-display'); ageSlider.addEventListener('input', (e) => { characterState.age = parseInt(e.target.value); ageValue.innerText = e.target.value; ageDisplay.innerText = e.target.value; }); // Cognitive & Physical Stats Sliders const statControls = document.querySelectorAll('.stat-control'); statControls.forEach(control => { const range = control.querySelector('input[type=range]'); const display = control.querySelector('.value-display'); const statName = control.dataset.stat; range.addEventListener('input', (e) => { const val = e.target.value; display.innerText = val + '%'; characterState.stats[statName] = parseInt(val); updateNeuralPreview(); }); }); // Childhood Selection const childhoodItems = document.querySelectorAll('.childhood-item'); childhoodItems.forEach(item => { item.addEventListener('click', () => { childhoodItems.forEach(i => { i.classList.remove('selected'); i.classList.remove('border-neural-blue', 'bg-slate-900/50'); i.classList.add('border-slate-700', 'bg-slate-800'); i.querySelector('.check-icon').setAttribute('data-feather', 'circle'); i.querySelector('.check-icon').classList.remove('text-neural-blue'); i.querySelector('.check-icon').classList.add('text-slate-600'); }); item.classList.add('selected'); item.classList.remove('border-slate-700', 'bg-slate-800'); item.classList.add('border-neural-blue', 'bg-slate-900/50'); item.querySelector('.check-icon').setAttribute('data-feather', 'check-circle'); item.querySelector('.check-icon').classList.add('text-neural-blue'); item.querySelector('.check-icon').classList.remove('text-slate-600'); characterState.background.childhood = item.querySelector('h4').innerText; updateSkills(); feather.replace(); }); }); // Adulthood Selection const adulthoodItems = document.querySelectorAll('.adulthood-item'); adulthoodItems.forEach(item => { item.addEventListener('click', () => { adulthoodItems.forEach(i => { i.classList.remove('selected'); i.classList.remove('border-neural-blue', 'bg-slate-900/50'); i.classList.add('border-slate-700', 'bg-slate-800'); i.querySelector('.check-icon').setAttribute('data-feather', 'circle'); i.querySelector('.check-icon').classList.remove('text-neural-blue'); i.querySelector('.check-icon').classList.add('text-slate-600'); }); item.classList.add('selected'); item.classList.remove('border-slate-700', 'bg-slate-800'); item.classList.add('border-neural-blue', 'bg-slate-900/50'); item.querySelector('.check-icon').setAttribute('data-feather', 'check-circle'); item.querySelector('.check-icon').classList.add('text-neural-blue'); item.querySelector('.check-icon').classList.remove('text-slate-600'); characterState.background.adulthood = item.querySelector('h4').innerText; updateSkills(); feather.replace(); }); }); // Update Skills Display based on background function updateSkills() { const skillsGrid = document.getElementById('skills-grid'); if (!skillsGrid) return; // Base skills from childhood let skills = { melee: 0, ranged: 0, social: 0, intellectual: 0, crafting: 0, medicine: 0 }; switch(characterState.background.childhood) { case 'Vat-Grown Soldier': skills.melee += 2; skills.ranged += 1; skills.social -= 2; break; case 'Street Urchin': skills.melee += 1; break; case 'Academic Prodigy': skills.intellectual += 3; skills.social += 1; break; case 'Farm Hand': skills.intellectual -= 1; break; } // Skills from adulthood switch(characterState.background.adulthood) { case 'Mercenary': skills.melee += 1; skills.ranged += 2; break; case 'Cyber-Surgeon': skills.medicine += 3; skills.crafting += 2; break; case 'Corporate Executive': skills.social += 3; skills.intellectual += 2; break; case 'Wasteland Scavenger': skills.crafting += 3; skills.melee += 1; break; } // Update display skillsGrid.innerHTML = `
⚔️
Melee
${Math.max(0, skills.melee)}
🔫
Ranged
${Math.max(0, skills.ranged)}
💬
Social
${Math.max(0, skills.social)}
🧠
Intellectual
${Math.max(0, skills.intellectual)}
🔧
Crafting
${Math.max(0, skills.crafting)}
🩺
Medicine
${Math.max(0, skills.medicine)}
`; characterState.background.skills = skills; } // Belief Sliders Logic (Visual only for now) const beliefSliders = document.querySelectorAll('.belief-slider input[type=range]'); beliefSliders.forEach(slider => { slider.addEventListener('input', (e) => { const val = e.target.value; // Update the visual thumb position const thumb = e.target.parentElement.querySelector('div.absolute.rounded-full.shadow'); thumb.style.left = val + '%'; }); }); // Canvas Neural Preview Animation const canvas = document.getElementById('neural-preview'); const ctx = canvas.getContext('2d'); let animationId; function resizeCanvas() { if(canvas) { canvas.width = canvas.parentElement.offsetWidth; canvas.height = canvas.parentElement.offsetHeight; drawNeuralPreview(); } } window.addEventListener('resize', resizeCanvas); class Node { constructor(x, y, type) { this.x = x; this.y = y; this.type = type; // 'center', 'satellite' this.radius = type === 'center' ? 15 : 4; this.baseX = x; this.baseY = y; this.offset = Math.random() * Math.PI * 2; } draw() { ctx.beginPath(); ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2); if (this.type === 'center') { ctx.fillStyle = '#0ea5e9'; ctx.shadowBlur = 20; ctx.shadowColor = '#0ea5e9'; } else { ctx.fillStyle = '#94a3b8'; ctx.shadowBlur = 0; } ctx.fill(); ctx.closePath(); ctx.shadowBlur = 0; // Reset } update(time) { if (this.type === 'satellite') { // Float gently this.x = this.baseX + Math.sin(time + this.offset) * 5; this.y = this.baseY + Math.cos(time + this.offset) * 5; } } } let nodes = []; function initNodes() { nodes = []; const cx = canvas.width / 2; const cy = canvas.height / 2; nodes.push(new Node(cx, cy, 'center')); // Create satellite nodes based on stats complexity const complexity = (characterState.stats.logic + characterState.stats.emotion) / 20; const count = 5 + Math.floor(complexity); for (let i = 0; i < count; i++) { const angle = (Math.PI * 2 / count) * i; const dist = 50 + Math.random() * 40; nodes.push(new Node( cx + Math.cos(angle) * dist, cy + Math.sin(angle) * dist, 'satellite' )); } } function drawNeuralPreview() { if (!ctx) return; const time = Date.now() * 0.002; ctx.clearRect(0, 0, canvas.width, canvas.height); // Draw Connections ctx.strokeStyle = 'rgba(56, 189, 248, 0.2)'; ctx.lineWidth = 1; if (nodes.length > 0) { const center = nodes[0]; for (let i = 1; i < nodes.length; i++) { ctx.beginPath(); ctx.moveTo(center.x, center.y); ctx.lineTo(nodes[i].x, nodes[i].y); ctx.stroke(); // Draw inter-satellite connections occasionally if (i < nodes.length - 1) { ctx.beginPath(); ctx.moveTo(nodes[i].x, nodes[i].y); ctx.lineTo(nodes[i+1].x, nodes[i+1].y); ctx.stroke(); } } } // Draw Nodes nodes.forEach(node => { node.update(time); node.draw(); }); animationId = requestAnimationFrame(drawNeuralPreview); } function updateNeuralPreview() { // Re-init nodes based on stats changes initNodes(); } // Initialize setTimeout(() => { resizeCanvas(); initNodes(); drawNeuralPreview(); }, 100); // Save character data to localStorage when navigating to game const beginSimulationBtn = document.querySelector('aside a[href="game.html"]'); if (beginSimulationBtn) { beginSimulationBtn.addEventListener('click', () => { localStorage.setItem('neuroscape-character', JSON.stringify(characterState)); }); } });