diff --git a/app.js b/app.js index 5c72f4b..aa40f7f 100644 --- a/app.js +++ b/app.js @@ -46,6 +46,7 @@ let discoveredMolecules = new Set(JSON.parse(localStorage.getItem('chem_lab_disc let currentLevelIndex = parseInt(localStorage.getItem('chem_lab_story_level') || '0'); let completedLevels = JSON.parse(localStorage.getItem('chem_lab_story_completed') || '[]'); let currentAllowedAtoms = []; +let playerEnergy = parseInt(localStorage.getItem('chem_lab_energy') || '0'); // Elemente Datenbank für Spawnen (Symbole, Farben, Namen) const elementDetails = { @@ -430,6 +431,36 @@ clearDeskBtn.addEventListener('click', () => { document.getElementById('reactor-synth-btn').addEventListener('click', synthesizeReactor); document.getElementById('reactor-decompose-btn').addEventListener('click', decomposeReactor); +// Machine Toggles in Reaktor Box +const heatToggle = document.getElementById('reactor-heat-toggle'); +const elecToggle = document.getElementById('reactor-elec-toggle'); + +if (heatToggle) { + heatToggle.addEventListener('change', () => { + if (heatToggle.checked) { + setAssistantText("Brenner gezündet! 🔥 Hitze liefert die nötige Aktivierungsenergie, um reaktionsträge Dreifachbindungen anzuregen."); + } else { + setAssistantText("Brenner aus. Der Reaktor kühlt langsam ab."); + } + }); +} + +if (elecToggle) { + elecToggle.addEventListener('change', () => { + if (elecToggle.checked) { + setAssistantText("Strom ein! ⚡ Die Elektrolyse zwingt Elektronen auf Bahnen und spaltet selbst extrem stabile Moleküle."); + } else { + setAssistantText("Strom aus. Die Ionenbewegung normalisiert sich."); + } + }); +} + +// Particle Accelerator fire event +const accFireBtn = document.getElementById('acc-fire-btn'); +if (accFireBtn) { + accFireBtn.addEventListener('click', fireAccelerator); +} + // ========================================= // MODALS LOGIC // ========================================= @@ -700,12 +731,39 @@ function ejectMoleculeFromReactor(idx) { } } +function analyzeFailedSynthesis() { + const counts = {}; + reactorAtoms.forEach(sym => { + counts[sym] = (counts[sym] || 0) + 1; + }); + + if (counts['N'] && counts['H'] && counts['H'] && counts['H'] !== 3) { + setAssistantText("Achtung! Stickstoff (N) benötigt genau 3 Wasserstoff-Atome (H) für Ammoniak (NH₃). Deine Mischung lässt freie Valenzen ungesättigt. Füge noch ein H hinzu!", true); + } else if (counts['H'] === 1 && counts['O'] === 1) { + setAssistantText("Wasserstoff (H) und Sauerstoff (O) können nicht im Verhältnis 1:1 binden. Sie brauchen entweder 2x H und 1x O für Wasser (H₂O) oder 2x H und 2x O für Wasserstoffperoxid (H₂O₂).", true); + } else if (counts['C'] && counts['O'] && counts['C'] === 1 && counts['O'] !== 1 && counts['O'] !== 2) { + setAssistantText("Kohlenstoff (C) bindet sich mit Sauerstoff (O) entweder zu Kohlenmonoxid (CO, 1:1) oder Kohlendioxid (CO₂, 1:2). Passe dein Verhältnis an!", true); + } else { + setAssistantText("Diese Atome können sich nicht zu einem stabilen Molekül verbinden. Achte auf die Oktettregel und die Wertigkeiten (H=1, O=2, N=3, C=4, Na=1, Cl=1)!", true); + } +} + function synthesizeReactor() { if (reactorAtoms.length === 0) { alert("Der Reaktor enthält keine freien Atome für die Synthese!"); return; } + const hasNitrogen = reactorAtoms.includes('N'); + const heatOn = document.getElementById('reactor-heat-toggle').checked; + const elecOn = document.getElementById('reactor-elec-toggle').checked; + + if (hasNitrogen && !heatOn && !elecOn) { + setAssistantText("Halt! Stickstoff (N) ist extrem reaktionsträge aufgrund seiner starken Dreifachbindung! Um ihn zur Reaktion zu bringen, benötigst du zusätzliche Aktivierungsenergie. Schalte die Brenner-Station (Hitze) oder die Elektrolyse-Kammer (Strom) hinzu!", true); + alert("Synthese blockiert: Aktivierungsenergie fehlt für Stickstoff!"); + return; + } + // Sort recipes by ingredient length descending so we build larger molecules first const sortedRecipes = [...recipes].sort((a, b) => b.ingredients.length - a.ingredients.length); @@ -749,6 +807,11 @@ function synthesizeReactor() { const ry = parseFloat(reactorBox.style.top || (workspace.clientHeight - 190)) + Math.random() * 40; createMolecule(recipe, Math.max(10, rx), Math.max(10, ry)); + // Award energy + const energyAwarded = recipe.ingredients.length * 15; + playerEnergy += energyAwarded; + localStorage.setItem('chem_lab_energy', playerEnergy); + // Explosion at the reactor center const rect = reactorBox.getBoundingClientRect(); const workspaceRect = workspace.getBoundingClientRect(); @@ -774,6 +837,9 @@ function synthesizeReactor() { showModal(recipe, isNew); + const cleanName = recipe.result.substring(recipe.result.indexOf('(') + 1, recipe.result.indexOf(')')); + setAssistantText(`Sensationell! Wir haben ${cleanName} synthetisiert und dabei ${energyAwarded} J Energie freigesetzt!`); + synthesizedAny = true; keepMatching = true; break; @@ -784,10 +850,19 @@ function synthesizeReactor() { if (synthesizedAny) { updateReactorUI(); } else { - alert("Aus den vorhandenen Atomen konnte kein bekanntes Molekül synthetisiert werden!"); + analyzeFailedSynthesis(); } } +function setAssistantText(text, isWarning = false) { + const avatar = document.querySelector('.assistant-avatar'); + const textEl = document.getElementById('assistant-text'); + if (!avatar || !textEl) return; + + avatar.innerText = isWarning ? "⚠️" : "🔬"; + textEl.innerText = text; +} + function decomposeReactor() { if (reactorMolecules.length === 0) { alert("Es befinden sich keine Moleküle in der Reaktor-Box, die zerlegt werden können!"); @@ -970,11 +1045,33 @@ function renderDiscoveryBook() { discoveryProgress.innerText = `${count}/${recipes.length}`; } +function checkPhysicsUnlock() { + const physicsBtn = document.getElementById('tab-physics-btn'); + if (!physicsBtn) return; + if (playerEnergy >= 150) { + physicsBtn.classList.remove('locked'); + physicsBtn.disabled = false; + physicsBtn.title = "Klicke, um den Teilchenbeschleuniger zu öffnen!"; + physicsBtn.innerHTML = "⚛️ Physik"; + } else { + physicsBtn.classList.add('locked'); + physicsBtn.disabled = true; + physicsBtn.innerHTML = `⚛️ Physik 🔒 (${playerEnergy}/150J)`; + } +} + function updateStats() { const atoms = elementsOnDesk.filter(el => el.classList.contains('atom')).length; const molecules = elementsOnDesk.filter(el => el.classList.contains('molecule')).length; atomCountEl.innerText = atoms; moleculeCountEl.innerText = molecules; + + const energyCountEl = document.getElementById('energy-count'); + if (energyCountEl) { + energyCountEl.innerText = playerEnergy; + } + localStorage.setItem('chem_lab_energy', playerEnergy); + checkPhysicsUnlock(); } // ========================================= @@ -1113,8 +1210,10 @@ function renderBohrModel(electronsArray) { // Dom Tabs switching const tabSandboxBtn = document.getElementById('tab-sandbox-btn'); const tabStoryBtn = document.getElementById('tab-story-btn'); +const tabPhysicsBtn = document.getElementById('tab-physics-btn'); const sandboxContentEl = document.getElementById('sandbox-content'); const storyContentEl = document.getElementById('story-content'); +const physicsContentEl = document.getElementById('physics-content'); tabSandboxBtn.addEventListener('click', () => { switchMode('sandbox'); @@ -1124,6 +1223,14 @@ tabStoryBtn.addEventListener('click', () => { switchMode('story'); }); +if (tabPhysicsBtn) { + tabPhysicsBtn.addEventListener('click', () => { + if (!tabPhysicsBtn.classList.contains('locked')) { + switchMode('physics'); + } + }); +} + function switchMode(mode) { gameMode = mode; const elementsToRemove = workspace.querySelectorAll('.atom, .molecule'); @@ -1139,19 +1246,38 @@ function switchMode(mode) { if (mode === 'sandbox') { tabSandboxBtn.classList.add('active'); tabStoryBtn.classList.remove('active'); + if (tabPhysicsBtn) tabPhysicsBtn.classList.remove('active'); + sandboxContentEl.classList.remove('hidden'); storyContentEl.classList.add('hidden'); + if (physicsContentEl) physicsContentEl.classList.add('hidden'); deskTitle.innerText = "Labor-Tisch (Sandbox)"; - deskSubtitle.innerText = "Ziehe Atome übereinander, um Reaktionen frei auszulösen!"; + deskSubtitle.innerText = "Ziehe Atome in die Reaktor-Box, um Reaktionen auszulösen!"; currentAllowedAtoms = Object.keys(elementDetails); - } else { + } else if (mode === 'story') { tabSandboxBtn.classList.remove('active'); tabStoryBtn.classList.add('active'); + if (tabPhysicsBtn) tabPhysicsBtn.classList.remove('active'); + sandboxContentEl.classList.add('hidden'); storyContentEl.classList.remove('hidden'); + if (physicsContentEl) physicsContentEl.classList.add('hidden'); loadStoryLevel(currentLevelIndex); + } else if (mode === 'physics') { + tabSandboxBtn.classList.remove('active'); + tabStoryBtn.classList.remove('active'); + if (tabPhysicsBtn) tabPhysicsBtn.classList.add('active'); + + sandboxContentEl.classList.add('hidden'); + storyContentEl.classList.add('hidden'); + if (physicsContentEl) physicsContentEl.classList.remove('hidden'); + + deskTitle.innerText = "Labor-Tisch (Kernphysik)"; + deskSubtitle.innerText = "Teilchenbeschleuniger geladen. Kollidiere Atome, um neue Elemente zu erschaffen!"; + currentAllowedAtoms = Object.keys(elementDetails); + populateAcceleratorDropdowns(); } } @@ -1321,3 +1447,109 @@ storyNextLevelBtn.addEventListener('click', () => { // Zusätzliche Modal Close-Events if (storySuccessCloseIcon) storySuccessCloseIcon.addEventListener('click', closeStorySuccessModal); if (storySuccessOverlay) storySuccessOverlay.addEventListener('click', closeStorySuccessModal); + +function populateAcceleratorDropdowns() { + const s1 = document.getElementById('acc-source-1'); + const s2 = document.getElementById('acc-source-2'); + if (!s1 || !s2) return; + + s1.innerHTML = ''; + s2.innerHTML = ''; + + const playable = pseElements.filter(el => el.playable); + playable.forEach(el => { + const opt1 = document.createElement('option'); + opt1.value = el.symbol; + opt1.innerText = `${el.symbol} (${el.name}, Z=${el.number})`; + s1.appendChild(opt1); + + const opt2 = document.createElement('option'); + opt2.value = el.symbol; + opt2.innerText = `${el.symbol} (${el.name}, Z=${el.number})`; + s2.appendChild(opt2); + }); +} + +function fireAccelerator() { + if (playerEnergy < 20) { + alert("Nicht genug Energie! Du benötigst mindestens 20 J, um den Teilchenbeschleuniger zu laden."); + return; + } + + const s1 = document.getElementById('acc-source-1'); + const s2 = document.getElementById('acc-source-2'); + if (!s1 || !s2) return; + + const sym1 = s1.value; + const sym2 = s2.value; + + const el1 = pseElements.find(e => e.symbol === sym1); + const el2 = pseElements.find(e => e.symbol === sym2); + if (!el1 || !el2) return; + + // Cost + playerEnergy -= 20; + updateStats(); + + // Animate + const animZone = document.getElementById('acc-animation-zone'); + const fireBtn = document.getElementById('acc-fire-btn'); + const resultBox = document.getElementById('acc-result-box'); + + if (animZone) animZone.classList.add('firing'); + if (resultBox) resultBox.classList.add('hidden'); + if (fireBtn) fireBtn.disabled = true; + + // Set labels + const p1 = document.getElementById('acc-particle-1'); + const p2 = document.getElementById('acc-particle-2'); + if (p1) p1.innerText = el1.symbol; + if (p2) p2.innerText = el2.symbol; + + setTimeout(() => { + if (animZone) animZone.classList.remove('firing'); + if (fireBtn) fireBtn.disabled = false; + + const totalZ = el1.number + el2.number; + const resultEl = pseElements.find(e => e.number === totalZ); + + if (resultBox) resultBox.classList.remove('hidden'); + + const resSymbolEl = document.getElementById('acc-result-element'); + const resDescEl = document.getElementById('acc-result-desc'); + + if (resultEl) { + // SUCCESS! + resultBox.classList.remove('decayed'); + if (resSymbolEl) { + resSymbolEl.innerText = resultEl.symbol; + resSymbolEl.className = 'acc-result-circle'; + resSymbolEl.style.backgroundColor = getElementColor(resultEl.symbol); + } + if (resDescEl) { + resDescEl.innerText = `Kernfusion erfolgreich! Du hast ${resultEl.name} (Ordnungszahl ${totalZ}) erschaffen.`; + } + + // Spawn on desk! + spawnAtom(resultEl.symbol, getElementColor(resultEl.symbol)); + } else { + // DECAY! + resultBox.classList.add('decayed'); + if (resSymbolEl) { + resSymbolEl.innerText = "💥"; + resSymbolEl.style.backgroundColor = "#e74c3c"; + } + if (resDescEl) { + resDescEl.innerText = `Instabiler Kern! Das Element mit Ordnungszahl ${totalZ} ist sofort in leichtere Trümmer-Isotope zerfallen.`; + } + + // Spawn random lighter elements as debris + const count = 2 + Math.floor(Math.random() * 2); + for (let i = 0; i < count; i++) { + const randomLight = pseElements.filter(e => e.playable && e.number < 20); + const randomEl = randomLight[Math.floor(Math.random() * randomLight.length)]; + spawnAtom(randomEl.symbol, getElementColor(randomEl.symbol)); + } + } + }, 800); +} diff --git a/index.html b/index.html index 18c993a..cb76b4e 100644 --- a/index.html +++ b/index.html @@ -16,6 +16,7 @@ @@ -86,6 +87,43 @@ + + +
@@ -96,7 +134,7 @@
- Atome: 0 | Moleküle: 0 + ⚡ Energie: 0 J | Atome: 0 | Moleküle: 0
@@ -108,10 +146,28 @@

☢️ Reaktor-Box

+ +
+
🔬
+
+

Hallo! Ich bin dein Labor-Assistent. Ziehe Atome in die Box, schalte bei Bedarf Brenner oder Elektrolyse ein und klicke auf Synthetisieren!

+
+
Atome/Moleküle hierher ziehen!
+ +
+ + +
diff --git a/style.css b/style.css index 61679b9..52f9bc8 100644 --- a/style.css +++ b/style.css @@ -1177,3 +1177,301 @@ body { font-style: italic; color: #7f8c8d; } + +/* KI-LABORASSISTENT */ +.reactor-assistant { + display: flex; + gap: 12px; + background: rgba(0, 0, 0, 0.4); + border-radius: 8px; + padding: 10px; + border: 1px solid var(--border-color); + margin-top: 5px; + box-shadow: inset 0 1px 3px rgba(0,0,0,0.5); +} + +.assistant-avatar { + font-size: 1.8rem; + display: flex; + align-items: center; + justify-content: center; + animation: pulse-avatar 2s infinite ease-in-out; +} + +@keyframes pulse-avatar { + 0% { transform: scale(1); } + 50% { transform: scale(1.1); } + 100% { transform: scale(1); } +} + +.assistant-bubble { + flex-grow: 1; + font-size: 0.75rem; + line-height: 1.35; + color: #dfe4ea; + display: flex; + align-items: center; +} + +.assistant-bubble p { + margin: 0; +} + +/* REAKTOR-MASCHINEN */ +.reactor-machines { + display: flex; + gap: 10px; + margin-top: 5px; +} + +.machine-switch { + flex: 1; + position: relative; + display: inline-block; + height: 36px; +} + +.machine-switch input { + opacity: 0; + width: 0; + height: 0; +} + +.machine-switch .slider { + position: absolute; + cursor: pointer; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: #57606f; + transition: .2s; + border-radius: 6px; + display: flex; + align-items: center; + justify-content: center; + font-size: 0.8rem; + font-weight: bold; + color: #fff; + border: 1px solid rgba(255,255,255,0.08); + box-shadow: inset 0 1px 3px rgba(0,0,0,0.3); + user-select: none; +} + +.machine-switch input:checked + #slider-heat { + background-color: #e67e22; + box-shadow: 0 0 12px rgba(230, 126, 34, 0.6); + border-color: #f39c12; +} + +.machine-switch input:checked + #slider-elec { + background-color: #9b59b6; + box-shadow: 0 0 12px rgba(155, 89, 182, 0.6); + border-color: #8e44ad; +} + +/* KERNPHYSIK / TEILCHENBESCHLEUNIGER STYLES */ +.tab-btn.locked { + opacity: 0.5; + cursor: not-allowed; +} + +.tab-btn.locked:hover { + color: var(--text-muted); +} + +#physics-panel { + padding: 20px; + display: flex; + flex-direction: column; + gap: 15px; +} + +#physics-panel h3 { + font-size: 1.1rem; + color: #fff; + margin: 0; + text-transform: uppercase; + letter-spacing: 1px; +} + +.physics-desc { + font-size: 0.8rem; + color: var(--text-muted); + line-height: 1.4; + margin: 0; +} + +.accelerator-controls { + display: flex; + flex-direction: column; + gap: 12px; + background: rgba(0,0,0,0.2); + padding: 12px; + border-radius: 8px; + border: 1px solid var(--border-color); +} + +.target-select-box { + display: flex; + flex-direction: column; + gap: 5px; +} + +.target-select-box label { + font-size: 0.75rem; + color: var(--text-muted); +} + +.pse-legend { + display: flex; + flex-wrap: wrap; + gap: 15px; + padding: 10px; + font-size: 0.75rem; +} + +.acc-select { + background: #2f3542; + border: 1px solid var(--border-color); + color: #fff; + padding: 8px; + border-radius: 6px; + font-size: 0.85rem; + outline: none; +} + +.acc-btn { + background: #e74c3c; + color: #fff; + border: none; + padding: 10px; + border-radius: 6px; + font-weight: bold; + font-size: 0.85rem; + cursor: pointer; + box-shadow: 0 4px 6px rgba(0,0,0,0.15); + transition: background 0.15s, transform 0.1s; +} + +.acc-btn:hover { + background: #c0392b; +} + +.acc-btn:active { + transform: scale(0.95); +} + +#acc-animation-zone { + height: 100px; + background: #111; + border-radius: 8px; + border: 1px solid var(--border-color); + position: relative; + overflow: hidden; + display: flex; + align-items: center; + justify-content: center; +} + +.acc-ring { + width: 80px; + height: 80px; + border: 2px dashed rgba(231, 76, 60, 0.3); + border-radius: 50%; + position: relative; +} + +.acc-particle { + width: 24px; + height: 24px; + border-radius: 50%; + background: #3498db; + color: #fff; + font-size: 0.7rem; + font-weight: bold; + display: flex; + align-items: center; + justify-content: center; + position: absolute; + box-shadow: 0 0 8px #3498db; + border: 1px solid rgba(255,255,255,0.2); + z-index: 5; +} + +#acc-particle-1 { + left: -12px; + top: calc(50% - 12px); + background: #e74c3c; + box-shadow: 0 0 8px #e74c3c; +} + +#acc-particle-2 { + right: -12px; + top: calc(50% - 12px); + background: #2ecc71; + box-shadow: 0 0 8px #2ecc71; +} + +/* Animations for firing */ +@keyframes fire-left { + 0% { left: -12px; } + 100% { left: calc(50% - 12px); } +} + +@keyframes fire-right { + 0% { right: -12px; } + 100% { right: calc(50% - 12px); } +} + +.firing #acc-particle-1 { + animation: fire-left 0.8s forwards cubic-bezier(0.5, 0, 0.5, 1); +} + +.firing #acc-particle-2 { + animation: fire-right 0.8s forwards cubic-bezier(0.5, 0, 0.5, 1); +} + +#acc-result-box { + background: rgba(46, 204, 113, 0.1); + border: 1px solid #2ecc71; + padding: 12px; + border-radius: 8px; + display: flex; + flex-direction: column; + align-items: center; + gap: 8px; + animation: fade-in 0.3s ease-out; +} + +#acc-result-box.decayed { + background: rgba(231, 76, 60, 0.1); + border-color: #e74c3c; +} + +@keyframes fade-in { + from { opacity: 0; transform: translateY(10px); } + to { opacity: 1; transform: translateY(0); } +} + +#acc-result-element { + width: 50px; + height: 50px; + border-radius: 50%; + background-color: #ffeaa7; + color: #333; + font-size: 1.3rem; + font-weight: bold; + display: flex; + align-items: center; + justify-content: center; + border: 2px solid rgba(0,0,0,0.15); + box-shadow: 0 4px 10px rgba(0,0,0,0.2); +} + +#acc-result-desc { + font-size: 0.8rem; + text-align: center; + margin: 0; + color: #fff; +}