const workspace = document.getElementById('workspace');
const spawnBtns = document.querySelectorAll('.spawn-btn');
const clearDeskBtn = document.getElementById('clear-desk-btn');
const discoveryBook = document.getElementById('discovery-book');
const discoveryProgress = document.getElementById('discovery-progress');
const atomCountEl = document.getElementById('atom-count');
const moleculeCountEl = document.getElementById('molecule-count');
// Modal Elements
const infoModal = document.getElementById('info-modal');
const modalTitle = document.getElementById('modal-title');
const modalFormula = document.getElementById('modal-formula-badge');
const modalName = document.getElementById('modal-molecule-name');
const modalDesc = document.getElementById('modal-description');
const modalCloseBtn = document.getElementById('modal-close-btn');
const modalOverlay = document.querySelector('.modal-overlay');
let elementsOnDesk = [];
let draggingElement = null;
let offsetX = 0;
let offsetY = 0;
// Elemente Datenbank für Spawnen (Symbole, Farben, Namen)
const elementDetails = {
'H': { color: '#ff7675', name: 'Wasserstoff' },
'O': { color: '#74b9ff', name: 'Sauerstoff' },
'C': { color: '#2d3436', name: 'Kohlenstoff' },
'N': { color: '#a29bfe', name: 'Stickstoff' },
'Na': { color: '#ffeaa7', name: 'Natrium' },
'Cl': { color: '#55efc4', name: 'Chlor' },
'S': { color: '#fdcb6e', name: 'Schwefel' },
'Fe': { color: '#b2bec3', name: 'Eisen' }
};
// Rezepturenbuch (die Formeln müssen genau den Kombinationen der Symbole entsprechen)
const recipes = [
{
id: 'h2o',
ingredients: ['H', 'H', 'O'], // 2x Wasserstoff, 1x Sauerstoff
result: 'H₂O (Wasser)',
color: '#3498db',
desc: 'Wasser ist die absolute Grundlage allen bekannten Lebens! Über 70% der Erdoberfläche sind mit Wasser bedeckt, und auch unser eigener Körper besteht zu mehr als der Hälfte daraus.'
},
{
id: 'nacl',
ingredients: ['Cl', 'Na'], // 1x Natrium, 1x Chlor
result: 'NaCl (Kochsalz)',
color: '#f5f6fa',
desc: 'Natriumchlorid kennen wir alle als einfaches Speisesalz. Faszinierend: Natrium ist ein explosives, hochreaktives Metall und Chlor ein giftiges Gas – zusammen ergeben sie ein absolut lebenswichtiges und leckeres Gewürz!'
},
{
id: 'o2',
ingredients: ['O', 'O'], // 2x Sauerstoff
result: 'O₂ (Sauerstoff-Molekül)',
color: '#a8dadc',
desc: 'Der molekulare Sauerstoff, den wir jede Sekunde atmen. Pflanzen produzieren ihn als Abfallprodukt bei der Photosynthese, während sie Kohlendioxid abbauen.'
},
{
id: 'co2',
ingredients: ['C', 'O', 'O'], // 1x Kohlenstoff, 2x Sauerstoff
result: 'CO₂ (Kohlendioxid)',
color: '#7f8c8d',
desc: 'Ein farb- und geruchloses Treibhausgas. Wir atmen es ständig aus. Obwohl es die Erderwärmung anheizt, ist es für Pflanzen lebenswichtig, da sie es zur Photosynthese benötigen.'
},
{
id: 'ch4',
ingredients: ['C', 'H', 'H', 'H', 'H'], // 1x Kohlenstoff, 4x Wasserstoff
result: 'CH₄ (Methan)',
color: '#fdcb6e',
desc: 'Methan ist das einfachste Alkan und der Hauptbestandteil von brennbarem Erdgas. Es brennt sauber ab, ist aber als Treibhausgas in der Atmosphäre rund 25-mal klimaschädlicher als CO₂!'
},
{
id: 'nh3',
ingredients: ['N', 'H', 'H', 'H'], // 1x Stickstoff, 3x Wasserstoff
result: 'NH₃ (Ammoniak)',
color: '#9b59b6',
desc: 'Ein stechend riechendes, farbloses Gas. Ammoniak ist extrem wichtig für die chemische Industrie, da es die Grundlage für fast alle Kunstdünger bildet (erfunden durch das Haber-Bosch-Verfahren).'
},
{
id: 'hcl',
ingredients: ['Cl', 'H'], // 1x Chlor, 1x Wasserstoff
result: 'HCl (Salzsäure)',
color: '#e17055',
desc: 'Chlorwasserstoff ist ein stechendes Gas. In Wasser gelöst bildet es die extrem ätzende Salzsäure – diese ist auch ein wichtiger Bestandteil unseres eigenen Magensaftes, um Nahrung zu zersetzen!'
},
{
id: 'naoh',
ingredients: ['H', 'Na', 'O'], // 1x Natrium, 1x Sauerstoff, 1x Wasserstoff
result: 'NaOH (Natronlauge)',
color: '#fd79a8',
desc: 'Natriumhydroxid ist eine extrem starke Base. In Wasser gelöst wird es als Natronlauge bezeichnet, die Haare und Fett auflösen kann, weshalb man sie oft in Abflussreinigern findet. Sie wird auch für echte Seife genutzt.'
},
{
id: 'h2s',
ingredients: ['H', 'H', 'S'], // 2x Wasserstoff, 1x Schwefel
result: 'H₂S (Schwefelwasserstoff)',
color: '#ffeaa7',
desc: 'Dieses hochentzündliche und extrem giftige Gas riecht unverkennbar nach faulen Eiern! Es entsteht bei Fäulnisprozessen von Proteinen, im Darm und in vulkanischen Gasen.'
},
{
id: 'fes',
ingredients: ['Fe', 'S'], // 1x Eisen, 1x Schwefel
result: 'FeS (Eisensulfid)',
color: '#2d3436',
desc: 'Ein dunkelbrauner/schwarzer Feststoff. Das Mischen und Erhitzen von Eisenpulver und Schwefelpulver ist der absolute Klassiker im Chemie-Unterricht, um eine chemische Synthese zu demonstrieren.'
},
{
id: 'so2',
ingredients: ['O', 'O', 'S'], // 1x Schwefel, 2x Sauerstoff
result: 'SO₂ (Schwefeldioxid)',
color: '#b2bec3',
desc: 'Ein stechend riechendes, saures Gas, das beim Verbrennen von schwefelhaltigen fossilen Brennstoffen entsteht. Es schädigt die Atemwege und trägt in Wolken zur Bildung von saurem Regen bei.'
},
{
id: 'co',
ingredients: ['C', 'O'], // 1x Kohlenstoff, 1x Sauerstoff
result: 'CO (Kohlenmonoxid)',
color: '#636e72',
desc: 'Ein unsichtbares, geruchloses und hochgradig tödliches Gas! Es entsteht bei der unvollständigen Verbrennung von Kohle oder Holz und blockiert die Sauerstoffaufnahme im menschlichen Blutkreislauf.'
},
{
id: 'h2o2',
ingredients: ['H', 'H', 'O', 'O'], // 2x Wasserstoff, 2x Sauerstoff
result: 'H₂O₂ (Wasserstoffperoxid)',
color: '#74b9ff',
desc: 'Wasserstoffperoxid ist ein extrem starkes Bleichmittel und Oxidationsmittel. Es wird zum Blondieren von Haaren, zum Desinfizieren von Wunden und industriell zum Bleichen von Papier genutzt.'
},
{
id: 'hcn',
ingredients: ['C', 'H', 'N'], // 1x Wasserstoff, 1x Kohlenstoff, 1x Stickstoff
result: 'HCN (Blausäure)',
color: '#d63031',
desc: 'Cyanwasserstoff ist ein extrem giftiges Gas, das gelöst in Wasser als Blausäure bezeichnet wird. Es riecht für manche Menschen charakteristisch nach bitteren Mandeln und wirkt in Sekunden tödlich.'
},
{
id: 'n2',
ingredients: ['N', 'N'], // 2x Stickstoff
result: 'N₂ (Stickstoff)',
color: '#a29bfe',
desc: 'Stickstoff ist ein extrem reaktionsträges (inertes) Gas und bildet fast 78% der Luft, die wir atmen! Er schützt unseren Planeten vor verheerenden globalen Bränden, die in reinem Sauerstoff wüten würden.'
},
{
id: 'cl2',
ingredients: ['Cl', 'Cl'], // 2x Chlor
result: 'Cl₂ (Chlor)',
color: '#55efc4',
desc: 'Chlor-Gas ist giftig, hat einen stechenden Geruch und eine grüngelbe Farbe. Es wurde im Ersten Weltkrieg als Kampfgas eingesetzt, rettet heute aber als Desinfektionsmittel in Trinkwasser und Pools Millionen Leben.'
},
{
id: 'h2',
ingredients: ['H', 'H'], // 2x Wasserstoff
result: 'H₂ (Wasserstoff)',
color: '#ff7675',
desc: 'Das häufigste, einfachste und leichteste Element im Universum. Wasserstoff ist hochentzündlich (Knallgas-Reaktion) und bildet den primären Kernbrennstoff für alle leuchtenden Sterne.'
},
{
id: 'c2h4',
ingredients: ['C', 'C', 'H', 'H', 'H', 'H'], // 2x Kohlenstoff, 4x Wasserstoff
result: 'C₂H₄ (Ethen)',
color: '#ffeaa7',
desc: 'Ethen (Ethylen) ist ein gasförmiges Pflanzenhormon, das Früchte reifen lässt. Industriell ist es der absolut wichtigste Baustein zur Herstellung des Kunststoffs Polyethylen (PE).'
}
];
// Geladene Entdeckungen aus dem LocalStorage
let discoveredMolecules = new Set(JSON.parse(localStorage.getItem('chem_lab_discoveries') || '[]'));
// Initiales Laden
updateStats();
renderDiscoveryBook();
// Button-Klicks registrieren für Spawnen
spawnBtns.forEach(btn => {
btn.addEventListener('click', () => {
const symbol = btn.dataset.element;
const color = btn.dataset.color;
spawnAtom(symbol, color);
});
});
// Tisch leeren Button
clearDeskBtn.addEventListener('click', () => {
workspace.innerHTML = '';
elementsOnDesk = [];
updateStats();
});
// Modal Events
modalCloseBtn.addEventListener('click', closeModal);
modalOverlay.addEventListener('click', closeModal);
function closeModal() {
infoModal.classList.add('hidden');
}
function spawnAtom(symbol, color) {
const el = document.createElement('div');
el.classList.add('atom');
el.innerText = symbol;
el.dataset.symbol = symbol;
el.style.backgroundColor = color;
// Farblich angepasster Text für dunkle Elemente
if (symbol === 'C' || symbol === 'Fe') {
el.style.color = '#fff';
} else {
el.style.color = '#333';
}
// Zufällige Position auf dem Tisch
const x = Math.random() * (workspace.clientWidth - 60);
const y = Math.random() * (workspace.clientHeight - 60);
el.style.left = x + 'px';
el.style.top = y + 'px';
makeDraggable(el);
workspace.appendChild(el);
elementsOnDesk.push(el);
updateStats();
// Animation beim Erscheinen
el.animate([
{ transform: 'scale(0)' },
{ transform: 'scale(1.1)' },
{ transform: 'scale(1)' }
], { duration: 250, easing: 'ease-out' });
}
function makeDraggable(el) {
el.addEventListener('mousedown', (e) => {
draggingElement = el;
const rect = el.getBoundingClientRect();
offsetX = e.clientX - rect.left;
offsetY = e.clientY - rect.top;
el.style.zIndex = 1000;
// Füge Schatten beim Ziehen hinzu
el.style.boxShadow = '0 10px 20px rgba(0,0,0,0.4)';
});
}
document.addEventListener('mousemove', (e) => {
if (draggingElement) {
const workspaceRect = workspace.getBoundingClientRect();
let newX = e.clientX - workspaceRect.left - offsetX;
let newY = e.clientY - workspaceRect.top - offsetY;
// Grenzen des Workspaces beachten
newX = Math.max(0, Math.min(newX, workspaceRect.width - draggingElement.offsetWidth));
newY = Math.max(0, Math.min(newY, workspaceRect.height - draggingElement.offsetHeight));
draggingElement.style.left = newX + 'px';
draggingElement.style.top = newY + 'px';
}
});
document.addEventListener('mouseup', () => {
if (draggingElement) {
draggingElement.style.zIndex = '';
draggingElement.style.boxShadow = '';
checkForReactions();
draggingElement = null;
}
});
function checkForReactions() {
if (!draggingElement) return;
// Finde alle Elemente, die sich berühren (inklusive des gezogenen Elements)
let cluster = [draggingElement];
let added = true;
while(added) {
added = false;
for (let el of elementsOnDesk) {
if (cluster.includes(el)) continue;
// Prüfe Kollision mit irgendeinem Element im Cluster
let touches = cluster.some(cEl => {
const r1 = cEl.getBoundingClientRect();
const r2 = el.getBoundingClientRect();
return !(r1.right < r2.left ||
r1.left > r2.right ||
r1.bottom < r2.top ||
r1.top > r2.bottom);
});
if (touches) {
cluster.push(el);
added = true;
}
}
}
if (cluster.length > 1) {
// Ignoriere Cluster, die bereits Moleküle enthalten (nur Atome fusionieren in diesem Prototyp)
const hasMolecule = cluster.some(el => el.classList.contains('molecule'));
if (hasMolecule) return;
const symbols = cluster.map(el => el.dataset.symbol).sort();
// Prüfe gegen alle Rezepte
for (const recipe of recipes) {
const recipeSymbols = [...recipe.ingredients].sort();
if (JSON.stringify(symbols) === JSON.stringify(recipeSymbols)) {
// REAKTION!
const midX = parseFloat(draggingElement.style.left) + draggingElement.offsetWidth / 2;
const midY = parseFloat(draggingElement.style.top) + draggingElement.offsetHeight / 2;
// Lösche fusionierte Atome vom Tisch
cluster.forEach(el => {
if (workspace.contains(el)) {
workspace.removeChild(el);
}
elementsOnDesk = elementsOnDesk.filter(e => e !== el);
});
// Partikel-Explosion erzeugen
triggerExplosion(midX, midY, recipe.color);
// Neues Molekül erstellen
const molX = midX - 60; // Zentrieren
const molY = midY - 20;
createMolecule(recipe, molX, molY);
// Entdeckung registrieren
const isNew = !discoveredMolecules.has(recipe.id);
if (isNew) {
discoveredMolecules.add(recipe.id);
localStorage.setItem('chem_lab_discoveries', JSON.stringify([...discoveredMolecules]));
renderDiscoveryBook();
}
// Info-Modal anzeigen (bei neuer Entdeckung)
showModal(recipe, isNew);
break;
}
}
}
}
function createMolecule(recipe, x, y) {
const el = document.createElement('div');
el.classList.add('molecule');
el.innerText = recipe.result.split(' ')[0]; // Zeigt z.B. H₂O an
el.dataset.symbol = recipe.result;
el.style.backgroundColor = recipe.color;
// Grenzen im Workspace einhalten
const maxLeft = workspace.clientWidth - 120;
const maxTop = workspace.clientHeight - 40;
el.style.left = Math.max(0, Math.min(x, maxLeft)) + 'px';
el.style.top = Math.max(0, Math.min(y, maxTop)) + 'px';
makeDraggable(el);
workspace.appendChild(el);
elementsOnDesk.push(el);
updateStats();
// Entstehungsanimation
el.animate([
{ transform: 'scale(0.5)' },
{ transform: 'scale(1.2)' },
{ transform: 'scale(1)' }
], { duration: 300, easing: 'cubic-bezier(0.175, 0.885, 0.32, 1.275)' });
}
function triggerExplosion(x, y, color) {
const numParticles = 18;
for (let i = 0; i < numParticles; i++) {
const p = document.createElement('div');
p.classList.add('particle');
p.style.backgroundColor = color || '#ff7675';
p.style.left = x + 'px';
p.style.top = y + 'px';
workspace.appendChild(p);
// Zufälliger Winkel und Geschwindigkeit für Explosion
const angle = Math.random() * Math.PI * 2;
const velocity = 40 + Math.random() * 80;
const targetX = Math.cos(angle) * velocity;
const targetY = Math.sin(angle) * velocity + 20; // Leichte Gravitation (nach unten gezogen)
p.animate([
{ transform: 'translate(0, 0) scale(1)', opacity: 1 },
{ transform: `translate(${targetX}px, ${targetY}px) scale(0)`, opacity: 0 }
], {
duration: 600 + Math.random() * 300,
easing: 'cubic-bezier(0.1, 0.8, 0.3, 1)',
fill: 'forwards'
});
setTimeout(() => p.remove(), 1000);
}
}
function showModal(recipe, isNew = true) {
if (isNew) {
modalTitle.innerText = "Molekül entdeckt! 🎉";
document.querySelector('.modal-emoji').innerText = "🎉";
} else {
modalTitle.innerText = "Molekül-Lexikon 📖";
document.querySelector('.modal-emoji').innerText = "📖";
}
// Subscripts für Formel schön rendern
const prettyFormula = recipe.id.toUpperCase().replace(/\d/g, m => '' + m + '');
modalFormula.innerHTML = prettyFormula;
modalFormula.style.backgroundColor = recipe.color;
// Reiner Name (Text in Klammern aus z.B. "H₂O (Wasser)")
const cleanName = recipe.result.substring(recipe.result.indexOf('(') + 1, recipe.result.indexOf(')'));
modalName.innerText = cleanName;
modalDesc.innerText = recipe.desc;
infoModal.classList.remove('hidden');
}
function renderDiscoveryBook() {
discoveryBook.innerHTML = '';
let count = 0;
recipes.forEach(recipe => {
const isDiscovered = discoveredMolecules.has(recipe.id);
const item = document.createElement('div');
item.classList.add('book-item');
// Formel mit tiefgestellten Ziffern
const prettyFormula = recipe.id.toUpperCase().replace(/\d/g, m => '' + m + '');
if (isDiscovered) {
item.classList.add('unlocked');
// Reiner Name aus Rezept
const cleanName = recipe.result.substring(recipe.result.indexOf('(') + 1, recipe.result.indexOf(')'));
item.innerHTML = `
${prettyFormula}
${cleanName}
🔓
`;
item.addEventListener('click', () => {
showModal(recipe, false);
});
count++;
} else {
item.classList.add('locked');
item.innerHTML = `
🧪 ???
Unbekannt
🔒
`;
}
discoveryBook.appendChild(item);
});
discoveryProgress.innerText = `${count}/${recipes.length}`;
}
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;
}