Initial commit of chemistry game prototype

This commit is contained in:
kreidler90 2026-07-04 00:00:30 +00:00
commit c7ec0b3506
3 changed files with 268 additions and 0 deletions

169
app.js Normal file
View File

@ -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 });
}

25
index.html Normal file
View File

@ -0,0 +1,25 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Alchemie Labor</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div id="sidebar">
<h2>Elemente</h2>
<p>Klicke, um Atome zu spawnen.</p>
<div class="spawn-btn" data-element="H" data-color="#ff9999">Wasserstoff (H)</div>
<div class="spawn-btn" data-element="O" data-color="#99ccff">Sauerstoff (O)</div>
<div class="spawn-btn" data-element="Na" data-color="#ffcc99">Natrium (Na)</div>
<div class="spawn-btn" data-element="Cl" data-color="#99ff99">Chlor (Cl)</div>
</div>
<div id="lab-desk">
<h2>Labor-Tisch</h2>
<p>Ziehe Atome übereinander (z.B. 2x H und 1x O), um Reaktionen auszulösen!</p>
<div id="workspace"></div>
</div>
<script src="app.js"></script>
</body>
</html>

74
style.css Normal file
View File

@ -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);
}