Initial commit of chemistry game prototype
This commit is contained in:
commit
c7ec0b3506
169
app.js
Normal file
169
app.js
Normal 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
25
index.html
Normal 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
74
style.css
Normal 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);
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user