From c7ec0b3506ece3398e36520b16f184198d6a1be9 Mon Sep 17 00:00:00 2001 From: kreidler90 Date: Sat, 4 Jul 2026 00:00:30 +0000 Subject: [PATCH] Initial commit of chemistry game prototype --- app.js | 169 +++++++++++++++++++++++++++++++++++++++++++++++++++++ index.html | 25 ++++++++ style.css | 74 +++++++++++++++++++++++ 3 files changed, 268 insertions(+) create mode 100644 app.js create mode 100644 index.html create mode 100644 style.css diff --git a/app.js b/app.js new file mode 100644 index 0000000..1d612c8 --- /dev/null +++ b/app.js @@ -0,0 +1,169 @@ +const workspace = document.getElementById('workspace'); +const spawnBtns = document.querySelectorAll('.spawn-btn'); + +let elementsOnDesk = []; +let draggingElement = null; +let offsetX = 0; +let offsetY = 0; + +// Rezepturenbuch (die Formeln müssen genau den Kombinationen der Symbole entsprechen) +const recipes = [ + { + ingredients: ['H', 'H', 'O'], // 2x Wasserstoff, 1x Sauerstoff + result: 'H₂O (Wasser)', + color: '#3498db' + }, + { + ingredients: ['Cl', 'Na'], // 1x Natrium, 1x Chlor + result: 'NaCl (Kochsalz)', + color: '#ecf0f1' + }, + { + ingredients: ['O', 'O'], // 2x Sauerstoff + result: 'O₂ (Sauerstoff-Molekül)', + color: '#bdc3c7' + } +]; + +// Button-Klicks registrieren +spawnBtns.forEach(btn => { + btn.addEventListener('click', () => { + spawnAtom(btn.dataset.element, btn.dataset.color); + }); +}); + +function spawnAtom(symbol, color) { + const el = document.createElement('div'); + el.classList.add('atom'); + el.innerText = symbol; + el.dataset.symbol = symbol; + el.style.backgroundColor = color; + + // Zufällige Position auf dem Tisch + const x = Math.random() * (workspace.clientWidth - 50); + const y = Math.random() * (workspace.clientHeight - 50); + + el.style.left = x + 'px'; + el.style.top = y + 'px'; + + makeDraggable(el); + workspace.appendChild(el); + elementsOnDesk.push(el); +} + +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; + }); +} + +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 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 = ''; + checkForReactions(); + draggingElement = null; + } +}); + +function checkForReactions() { + if (!draggingElement) return; + + const rect1 = draggingElement.getBoundingClientRect(); + + // Finde alle Elemente, die sich berühren (inklusive des gezogenen Elements) + // Einfache Kollisionserkennung: Wir gruppieren alle Elemente, die nahe beieinander liegen. + let cluster = [draggingElement]; + let added = true; + + // Erweitere den Cluster iterativ, bis keine berührenden Elemente mehr gefunden werden + 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) { + 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 = draggingElement.style.left; + const midY = draggingElement.style.top; + + // Lösche alte Atome + cluster.forEach(el => { + if (workspace.contains(el)) { + workspace.removeChild(el); + } + elementsOnDesk = elementsOnDesk.filter(e => e !== el); + }); + + // Neues Molekül erstellen + createMolecule(recipe.result, recipe.color, midX, midY); + break; + } + } + } +} + +function createMolecule(name, color, x, y) { + const el = document.createElement('div'); + el.classList.add('molecule'); + el.innerText = name; + el.dataset.symbol = name; // Für diesen Prototyp können Moleküle noch nicht weiterreagieren + el.style.backgroundColor = color; + el.style.left = x; + el.style.top = y; + + makeDraggable(el); + workspace.appendChild(el); + elementsOnDesk.push(el); + + // Animation + el.animate([ + { transform: 'scale(0.5)' }, + { transform: 'scale(1.2)' }, + { transform: 'scale(1)' } + ], { duration: 300 }); +} diff --git a/index.html b/index.html new file mode 100644 index 0000000..ab7b474 --- /dev/null +++ b/index.html @@ -0,0 +1,25 @@ + + + + + + Alchemie Labor + + + + +
+

Labor-Tisch

+

Ziehe Atome übereinander (z.B. 2x H und 1x O), um Reaktionen auszulösen!

+
+
+ + + \ No newline at end of file diff --git a/style.css b/style.css new file mode 100644 index 0000000..048253a --- /dev/null +++ b/style.css @@ -0,0 +1,74 @@ +body { + display: flex; + margin: 0; + height: 100vh; + font-family: Arial, sans-serif; + background-color: #2c3e50; + color: white; +} +#sidebar { + width: 250px; + background-color: #34495e; + padding: 20px; + box-shadow: 2px 0 5px rgba(0,0,0,0.5); +} +.spawn-btn { + padding: 10px; + margin-bottom: 10px; + background-color: #7f8c8d; + cursor: pointer; + border-radius: 5px; + text-align: center; + user-select: none; + color: #333; + font-weight: bold; +} +.spawn-btn:hover { + filter: brightness(1.1); +} +#lab-desk { + flex-grow: 1; + padding: 20px; + display: flex; + flex-direction: column; +} +#workspace { + flex-grow: 1; + background-color: #ecf0f1; + border-radius: 10px; + position: relative; + overflow: hidden; + color: #333; + box-shadow: inset 0 0 10px rgba(0,0,0,0.1); +} +.atom { + width: 50px; + height: 50px; + border-radius: 50%; + display: flex; + justify-content: center; + align-items: center; + position: absolute; + font-weight: bold; + cursor: grab; + box-shadow: 0 4px 6px rgba(0,0,0,0.3); + user-select: none; + border: 2px solid rgba(0,0,0,0.2); +} +.atom:active { + cursor: grabbing; +} +.molecule { + padding: 10px 20px; + border-radius: 20px; + display: flex; + justify-content: center; + align-items: center; + position: absolute; + font-weight: bold; + cursor: grab; + box-shadow: 0 4px 6px rgba(0,0,0,0.3); + background-color: #f1c40f; + user-select: none; + border: 2px solid rgba(0,0,0,0.2); +}