Upgrade Chemie-Spiel: add 18 recipes, discovery book, modal, particle effects, stats, and PLAN.md
This commit is contained in:
parent
0b7c186ec0
commit
e193638b70
119
PLAN.md
Normal file
119
PLAN.md
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
# Chemie-Lernspiel Upgrade-Plan
|
||||||
|
|
||||||
|
Dieses Dokument beschreibt die geplanten Erweiterungen für das Alchemie-Labor-Chemie-Lernspiel im Verzeichnis `chemie-spiel-dev`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Ziele des Upgrades
|
||||||
|
- **Lernwert steigern:** Spieler erhalten spannende und verständliche Fakten über die entdeckten Moleküle.
|
||||||
|
- **Gamification:** Ein "Entdecker-Buch" (Lexikon/Bibliothek) im Sidebar motiviert dazu, alle Moleküle freizuschalten. Fortschritt wird im Browser (`localStorage`) gespeichert.
|
||||||
|
- **Visuelles Feedback & Saftigkeit (Juiciness):** Reaktionen erzeugen Partikel-Explosionen, Atome sehen aus wie 3D-Kugeln, und das Interface fühlt sich dynamischer an.
|
||||||
|
- **Mehr Inhalt:** Zusätzliche Elemente (Schwefel, Eisen) und 12 neue Reaktionen (insgesamt 18 Rezepte).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Detaillierte Features
|
||||||
|
|
||||||
|
### A. Element- & Rezept-Erweiterung
|
||||||
|
Wir führen zwei neue Basiselemente ein:
|
||||||
|
- **Schwefel (S)** (Gelb, `#f1c40f`)
|
||||||
|
- **Eisen (Fe)** (Metallisch-Grau, `#95a5a6`)
|
||||||
|
|
||||||
|
Damit erweitern wir das Rezeptbuch um folgende spannende Kombinationen:
|
||||||
|
1. **H₂O (Wasser)**: 2x H + 1x O
|
||||||
|
2. **NaCl (Kochsalz)**: 1x Na + 1x Cl
|
||||||
|
3. **O₂ (Sauerstoff)**: 2x O
|
||||||
|
4. **CO₂ (Kohlendioxid)**: 1x C + 2x O
|
||||||
|
5. **CH₄ (Methan)**: 1x C + 4x H
|
||||||
|
6. **NH₃ (Ammoniak)**: 1x N + 3x H
|
||||||
|
7. **HCl (Salzsäure)**: 1x H + 1x Cl
|
||||||
|
8. **NaOH (Natronlauge)**: 1x Na + 1x O + 1x H
|
||||||
|
9. **H₂S (Schwefelwasserstoff)**: 2x H + 1x S
|
||||||
|
10. **FeS (Eisensulfid)**: 1x Fe + 1x S
|
||||||
|
11. **SO₂ (Schwefeldioxid)**: 1x S + 2x O
|
||||||
|
12. **CO (Kohlenmonoxid)**: 1x C + 1x O
|
||||||
|
13. **H₂O₂ (Wasserstoffperoxid)**: 2x H + 2x O
|
||||||
|
14. **HCN (Blausäure)**: 1x H + 1x C + 1x N
|
||||||
|
15. **N₂ (Stickstoff)**: 2x N
|
||||||
|
16. **Cl₂ (Chlor)**: 2x Cl
|
||||||
|
17. **H₂ (Wasserstoff)**: 2x H
|
||||||
|
18. **C₂H₄ (Ethen)**: 2x C + 4x H
|
||||||
|
|
||||||
|
### B. Das Entdecker-Buch (Lexikon)
|
||||||
|
- Befindet sich in der Sidebar unter der Elementen-Auswahl.
|
||||||
|
- Zeigt den aktuellen Fortschritt an (z. B. "Entdeckt: 2 / 18").
|
||||||
|
- Ungelöste Rezepte werden als ausgegraute Fragezeichen dargestellt (z. B. `??? (H₂O)` oder komplett anonymisiert).
|
||||||
|
- Entdeckte Moleküle leuchten auf, zeigen ihren vollen Namen und können angeklickt werden, um die Info-Karte erneut zu öffnen.
|
||||||
|
- Speicherung des Fortschritts in `localStorage`.
|
||||||
|
|
||||||
|
### C. Info-Modal (Pop-up)
|
||||||
|
- Sobald eine Reaktion stattfindet, öffnet sich ein wunderschönes Modal (verdunkelter, weichgezeichneter Hintergrund).
|
||||||
|
- Inhalt:
|
||||||
|
- Großer Titel mit der chemischen Formel (z. B. **H₂O (Wasser)**).
|
||||||
|
- Ein kurzes, interessantes "Wusstest du schon?"-Faktum (z. B. über die Giftigkeit von Natrium und Chlor einzeln vs. die Harmlosigkeit von Kochsalz).
|
||||||
|
- Ein "Schließen"-Button, der das Modal schließt und den Spielfluss fortsetzt.
|
||||||
|
|
||||||
|
### D. Visuelle Effekte & Animationen
|
||||||
|
- **Partikel-Explosion:** Bei einer erfolgreichen Reaktion fliegen 10-15 kleine, farbige Funken vom Reaktionszentrum in alle Richtungen weg und verblassen.
|
||||||
|
- **3D-Atome:** Atome erhalten einen radialen Farbverlauf, der ihnen ein plastisches, rundes Aussehen verleiht.
|
||||||
|
- **Tisch-Statistiken:** Ein kleiner Counter zeigt an, wie viele Atome und Moleküle sich gerade auf dem Tisch befinden.
|
||||||
|
- **Tisch leeren:** Ein Papierkorb-Button löscht alle Elemente vom Tisch, um Platz für neue Experimente zu schaffen.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Schritt-für-Schritt-Implementierung
|
||||||
|
|
||||||
|
### Schritt 1: HTML-Struktur aktualisieren (`index.html`)
|
||||||
|
- Neue Buttons für Schwefel (S) und Eisen (Fe) hinzufügen.
|
||||||
|
- Eine Sektion `#discovery-book` in der Sidebar einbauen.
|
||||||
|
- Den "Tisch leeren"-Button und die Statistik-Zähler im Labor-Tisch ergänzen.
|
||||||
|
- Das `#info-modal` Container-Skelett am Ende des Bodys hinzufügen.
|
||||||
|
|
||||||
|
### Schritt 2: CSS-Styling verfeinern (`style.css`)
|
||||||
|
- Sidebar-Scrollbarkeit verbessern, falls das Lexikon lang wird.
|
||||||
|
- Modernes Styling für das Modal (`position: fixed`, Flexbox-Zentrierung, `backdrop-filter: blur(5px)`).
|
||||||
|
- 3D-Look für Atome via Radial-Gradient (`background: radial-gradient(circle at 30% 30%, color 0%, darkcolor 100%)`).
|
||||||
|
- Schönes Design für das Entdecker-Buch (Grid oder List-Layout, Transitionen beim Freischalten).
|
||||||
|
- Partikel-CSS-Klasse für die Reaktionsexplosion.
|
||||||
|
|
||||||
|
### Schritt 3: JavaScript-Logik überarbeiten (`app.js`)
|
||||||
|
- Rezepte-Array mit allen 18 Rezepten, Farben und detaillierten Texten anlegen.
|
||||||
|
- Speicherlogik via `localStorage` einrichten (`discoveredMolecules` Set).
|
||||||
|
- Funktion `updateDiscoveryBook()` schreiben, die das Lexikon-UI aktualisiert.
|
||||||
|
- Funktion `triggerExplosion(x, y, color)` entwickeln, die DOM-basierte Partikel erzeugt und animiert.
|
||||||
|
- Modal-Logik integrieren (`showModal(recipe)`), die bei einer Reaktion aufgerufen wird und bei Klick auf das Lexikon.
|
||||||
|
- Event-Listener für "Tisch leeren" und die Positions-Zähler aktualisieren.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Validierung & Tests
|
||||||
|
- **Webserver:** Läuft im Hintergrund auf Port 8080.
|
||||||
|
- **Funktionalität:**
|
||||||
|
1. Spawnen von Atomen klappt.
|
||||||
|
2. Drag & Drop funktioniert flüssig.
|
||||||
|
3. Zusammenführen von 2x H + 1x O öffnet das Wasser-Modal und erzeugt eine Partikel-Explosion.
|
||||||
|
4. Wasser wird im Entdecker-Buch freigeschaltet und persistiert bei Seiten-Reload.
|
||||||
|
5. "Tisch leeren" entfernt alle Atome und Moleküle.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Rezept-Daten (Vorschau für `app.js`)
|
||||||
|
```javascript
|
||||||
|
const recipes = [
|
||||||
|
{
|
||||||
|
id: "h2o",
|
||||||
|
ingredients: ['H', 'H', 'O'],
|
||||||
|
result: 'H₂O (Wasser)',
|
||||||
|
color: '#3498db',
|
||||||
|
desc: "Wasser ist die Grundlage allen Lebens auf der Erde. Unser Körper besteht zu über 60% daraus, und es bedeckt mehr als 70% unseres Planeten!"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "nacl",
|
||||||
|
ingredients: ['Cl', 'Na'],
|
||||||
|
result: 'NaCl (Kochsalz)',
|
||||||
|
color: '#f5f6fa',
|
||||||
|
desc: "Natriumchlorid kennen wir alle als Speisesalz. Faszinierend: Natrium ist ein explosives Metall und Chlor ein giftiges Gas – zusammen ergeben sie ein lebenswichtiges Gewürz!"
|
||||||
|
},
|
||||||
|
// ...weitere 16 Rezepte
|
||||||
|
];
|
||||||
|
```
|
||||||
344
app.js
344
app.js
@ -1,52 +1,198 @@
|
|||||||
const workspace = document.getElementById('workspace');
|
const workspace = document.getElementById('workspace');
|
||||||
const spawnBtns = document.querySelectorAll('.spawn-btn');
|
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 elementsOnDesk = [];
|
||||||
let draggingElement = null;
|
let draggingElement = null;
|
||||||
let offsetX = 0;
|
let offsetX = 0;
|
||||||
let offsetY = 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)
|
// Rezepturenbuch (die Formeln müssen genau den Kombinationen der Symbole entsprechen)
|
||||||
const recipes = [
|
const recipes = [
|
||||||
{
|
{
|
||||||
|
id: 'h2o',
|
||||||
ingredients: ['H', 'H', 'O'], // 2x Wasserstoff, 1x Sauerstoff
|
ingredients: ['H', 'H', 'O'], // 2x Wasserstoff, 1x Sauerstoff
|
||||||
result: 'H₂O (Wasser)',
|
result: 'H₂O (Wasser)',
|
||||||
color: '#3498db'
|
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
|
ingredients: ['Cl', 'Na'], // 1x Natrium, 1x Chlor
|
||||||
result: 'NaCl (Kochsalz)',
|
result: 'NaCl (Kochsalz)',
|
||||||
color: '#ecf0f1'
|
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
|
ingredients: ['O', 'O'], // 2x Sauerstoff
|
||||||
result: 'O₂ (Sauerstoff-Molekül)',
|
result: 'O₂ (Sauerstoff-Molekül)',
|
||||||
color: '#bdc3c7'
|
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
|
ingredients: ['C', 'O', 'O'], // 1x Kohlenstoff, 2x Sauerstoff
|
||||||
result: 'CO₂ (Kohlendioxid)',
|
result: 'CO₂ (Kohlendioxid)',
|
||||||
color: '#7f8c8d'
|
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
|
ingredients: ['C', 'H', 'H', 'H', 'H'], // 1x Kohlenstoff, 4x Wasserstoff
|
||||||
result: 'CH₄ (Methan)',
|
result: 'CH₄ (Methan)',
|
||||||
color: '#f39c12'
|
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
|
ingredients: ['N', 'H', 'H', 'H'], // 1x Stickstoff, 3x Wasserstoff
|
||||||
result: 'NH₃ (Ammoniak)',
|
result: 'NH₃ (Ammoniak)',
|
||||||
color: '#9b59b6'
|
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).'
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
// Button-Klicks registrieren
|
// 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 => {
|
spawnBtns.forEach(btn => {
|
||||||
btn.addEventListener('click', () => {
|
btn.addEventListener('click', () => {
|
||||||
spawnAtom(btn.dataset.element, btn.dataset.color);
|
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) {
|
function spawnAtom(symbol, color) {
|
||||||
const el = document.createElement('div');
|
const el = document.createElement('div');
|
||||||
el.classList.add('atom');
|
el.classList.add('atom');
|
||||||
@ -54,9 +200,16 @@ function spawnAtom(symbol, color) {
|
|||||||
el.dataset.symbol = symbol;
|
el.dataset.symbol = symbol;
|
||||||
el.style.backgroundColor = color;
|
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
|
// Zufällige Position auf dem Tisch
|
||||||
const x = Math.random() * (workspace.clientWidth - 50);
|
const x = Math.random() * (workspace.clientWidth - 60);
|
||||||
const y = Math.random() * (workspace.clientHeight - 50);
|
const y = Math.random() * (workspace.clientHeight - 60);
|
||||||
|
|
||||||
el.style.left = x + 'px';
|
el.style.left = x + 'px';
|
||||||
el.style.top = y + 'px';
|
el.style.top = y + 'px';
|
||||||
@ -64,6 +217,15 @@ function spawnAtom(symbol, color) {
|
|||||||
makeDraggable(el);
|
makeDraggable(el);
|
||||||
workspace.appendChild(el);
|
workspace.appendChild(el);
|
||||||
elementsOnDesk.push(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) {
|
function makeDraggable(el) {
|
||||||
@ -74,6 +236,9 @@ function makeDraggable(el) {
|
|||||||
offsetX = e.clientX - rect.left;
|
offsetX = e.clientX - rect.left;
|
||||||
offsetY = e.clientY - rect.top;
|
offsetY = e.clientY - rect.top;
|
||||||
el.style.zIndex = 1000;
|
el.style.zIndex = 1000;
|
||||||
|
|
||||||
|
// Füge Schatten beim Ziehen hinzu
|
||||||
|
el.style.boxShadow = '0 10px 20px rgba(0,0,0,0.4)';
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -84,7 +249,7 @@ document.addEventListener('mousemove', (e) => {
|
|||||||
let newX = e.clientX - workspaceRect.left - offsetX;
|
let newX = e.clientX - workspaceRect.left - offsetX;
|
||||||
let newY = e.clientY - workspaceRect.top - offsetY;
|
let newY = e.clientY - workspaceRect.top - offsetY;
|
||||||
|
|
||||||
// Grenzen beachten
|
// Grenzen des Workspaces beachten
|
||||||
newX = Math.max(0, Math.min(newX, workspaceRect.width - draggingElement.offsetWidth));
|
newX = Math.max(0, Math.min(newX, workspaceRect.width - draggingElement.offsetWidth));
|
||||||
newY = Math.max(0, Math.min(newY, workspaceRect.height - draggingElement.offsetHeight));
|
newY = Math.max(0, Math.min(newY, workspaceRect.height - draggingElement.offsetHeight));
|
||||||
|
|
||||||
@ -96,6 +261,7 @@ document.addEventListener('mousemove', (e) => {
|
|||||||
document.addEventListener('mouseup', () => {
|
document.addEventListener('mouseup', () => {
|
||||||
if (draggingElement) {
|
if (draggingElement) {
|
||||||
draggingElement.style.zIndex = '';
|
draggingElement.style.zIndex = '';
|
||||||
|
draggingElement.style.boxShadow = '';
|
||||||
checkForReactions();
|
checkForReactions();
|
||||||
draggingElement = null;
|
draggingElement = null;
|
||||||
}
|
}
|
||||||
@ -104,14 +270,10 @@ document.addEventListener('mouseup', () => {
|
|||||||
function checkForReactions() {
|
function checkForReactions() {
|
||||||
if (!draggingElement) return;
|
if (!draggingElement) return;
|
||||||
|
|
||||||
const rect1 = draggingElement.getBoundingClientRect();
|
|
||||||
|
|
||||||
// Finde alle Elemente, die sich berühren (inklusive des gezogenen Elements)
|
// 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 cluster = [draggingElement];
|
||||||
let added = true;
|
let added = true;
|
||||||
|
|
||||||
// Erweitere den Cluster iterativ, bis keine berührenden Elemente mehr gefunden werden
|
|
||||||
while(added) {
|
while(added) {
|
||||||
added = false;
|
added = false;
|
||||||
for (let el of elementsOnDesk) {
|
for (let el of elementsOnDesk) {
|
||||||
@ -135,6 +297,10 @@ function checkForReactions() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (cluster.length > 1) {
|
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();
|
const symbols = cluster.map(el => el.dataset.symbol).sort();
|
||||||
|
|
||||||
// Prüfe gegen alle Rezepte
|
// Prüfe gegen alle Rezepte
|
||||||
@ -143,10 +309,10 @@ function checkForReactions() {
|
|||||||
|
|
||||||
if (JSON.stringify(symbols) === JSON.stringify(recipeSymbols)) {
|
if (JSON.stringify(symbols) === JSON.stringify(recipeSymbols)) {
|
||||||
// REAKTION!
|
// REAKTION!
|
||||||
const midX = draggingElement.style.left;
|
const midX = parseFloat(draggingElement.style.left) + draggingElement.offsetWidth / 2;
|
||||||
const midY = draggingElement.style.top;
|
const midY = parseFloat(draggingElement.style.top) + draggingElement.offsetHeight / 2;
|
||||||
|
|
||||||
// Lösche alte Atome
|
// Lösche fusionierte Atome vom Tisch
|
||||||
cluster.forEach(el => {
|
cluster.forEach(el => {
|
||||||
if (workspace.contains(el)) {
|
if (workspace.contains(el)) {
|
||||||
workspace.removeChild(el);
|
workspace.removeChild(el);
|
||||||
@ -154,31 +320,155 @@ function checkForReactions() {
|
|||||||
elementsOnDesk = elementsOnDesk.filter(e => e !== el);
|
elementsOnDesk = elementsOnDesk.filter(e => e !== el);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Partikel-Explosion erzeugen
|
||||||
|
triggerExplosion(midX, midY, recipe.color);
|
||||||
|
|
||||||
// Neues Molekül erstellen
|
// Neues Molekül erstellen
|
||||||
createMolecule(recipe.result, recipe.color, midX, midY);
|
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;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function createMolecule(name, color, x, y) {
|
function createMolecule(recipe, x, y) {
|
||||||
const el = document.createElement('div');
|
const el = document.createElement('div');
|
||||||
el.classList.add('molecule');
|
el.classList.add('molecule');
|
||||||
el.innerText = name;
|
el.innerText = recipe.result.split(' ')[0]; // Zeigt z.B. H₂O an
|
||||||
el.dataset.symbol = name; // Für diesen Prototyp können Moleküle noch nicht weiterreagieren
|
el.dataset.symbol = recipe.result;
|
||||||
el.style.backgroundColor = color;
|
el.style.backgroundColor = recipe.color;
|
||||||
el.style.left = x;
|
|
||||||
el.style.top = y;
|
// 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);
|
makeDraggable(el);
|
||||||
workspace.appendChild(el);
|
workspace.appendChild(el);
|
||||||
elementsOnDesk.push(el);
|
elementsOnDesk.push(el);
|
||||||
|
|
||||||
// Animation
|
updateStats();
|
||||||
|
|
||||||
|
// Entstehungsanimation
|
||||||
el.animate([
|
el.animate([
|
||||||
{ transform: 'scale(0.5)' },
|
{ transform: 'scale(0.5)' },
|
||||||
{ transform: 'scale(1.2)' },
|
{ transform: 'scale(1.2)' },
|
||||||
{ transform: 'scale(1)' }
|
{ transform: 'scale(1)' }
|
||||||
], { duration: 300 });
|
], { 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 => '<sub>' + m + '</sub>');
|
||||||
|
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 => '<sub>' + m + '</sub>');
|
||||||
|
|
||||||
|
if (isDiscovered) {
|
||||||
|
item.classList.add('unlocked');
|
||||||
|
// Reiner Name aus Rezept
|
||||||
|
const cleanName = recipe.result.substring(recipe.result.indexOf('(') + 1, recipe.result.indexOf(')'));
|
||||||
|
|
||||||
|
item.innerHTML = `
|
||||||
|
<span class="book-formula" style="color: ${recipe.color}">${prettyFormula}</span>
|
||||||
|
<span class="book-name">${cleanName}</span>
|
||||||
|
<span class="book-status">🔓</span>
|
||||||
|
`;
|
||||||
|
|
||||||
|
item.addEventListener('click', () => {
|
||||||
|
showModal(recipe, false);
|
||||||
|
});
|
||||||
|
count++;
|
||||||
|
} else {
|
||||||
|
item.classList.add('locked');
|
||||||
|
item.innerHTML = `
|
||||||
|
<span class="book-formula" title="Zutaten: ${recipe.ingredients.sort().join(' + ')}">🧪 ???</span>
|
||||||
|
<span class="book-name" style="filter: blur(4px)">Unbekannt</span>
|
||||||
|
<span class="book-status">🔒</span>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
|
|||||||
73
index.html
73
index.html
@ -3,25 +3,76 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>Alchemie Labor</title>
|
<title>Chemisches Alchemie Labor</title>
|
||||||
<link rel="stylesheet" href="style.css">
|
<link rel="stylesheet" href="style.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="sidebar">
|
<div id="sidebar">
|
||||||
<h2>Elemente</h2>
|
<div class="sidebar-header">
|
||||||
<p>Klicke, um Atome zu spawnen.</p>
|
<h2>🧪 Labor</h2>
|
||||||
<div class="spawn-btn" data-element="H" data-color="#ff9999">Wasserstoff (H)</div>
|
<p>Atome spawnen & fusionieren</p>
|
||||||
<div class="spawn-btn" data-element="O" data-color="#99ccff">Sauerstoff (O)</div>
|
</div>
|
||||||
<div class="spawn-btn" data-element="C" data-color="#bdc3c7">Kohlenstoff (C)</div>
|
|
||||||
<div class="spawn-btn" data-element="N" data-color="#cc99ff">Stickstoff (N)</div>
|
<div id="elements-section">
|
||||||
<div class="spawn-btn" data-element="Na" data-color="#ffcc99">Natrium (Na)</div>
|
<h3>Atome</h3>
|
||||||
<div class="spawn-btn" data-element="Cl" data-color="#99ff99">Chlor (Cl)</div>
|
<div class="elements-grid">
|
||||||
|
<div class="spawn-btn" data-element="H" data-color="#ff7675" title="Wasserstoff">H</div>
|
||||||
|
<div class="spawn-btn" data-element="O" data-color="#74b9ff" title="Sauerstoff">O</div>
|
||||||
|
<div class="spawn-btn" data-element="C" data-color="#2d3436" title="Kohlenstoff">C</div>
|
||||||
|
<div class="spawn-btn" data-element="N" data-color="#a29bfe" title="Stickstoff">N</div>
|
||||||
|
<div class="spawn-btn" data-element="Na" data-color="#ffeaa7" title="Natrium">Na</div>
|
||||||
|
<div class="spawn-btn" data-element="Cl" data-color="#55efc4" title="Chlor">Cl</div>
|
||||||
|
<div class="spawn-btn" data-element="S" data-color="#fdcb6e" title="Schwefel">S</div>
|
||||||
|
<div class="spawn-btn" data-element="Fe" data-color="#b2bec3" title="Eisen">Fe</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="book-section">
|
||||||
|
<div class="book-header">
|
||||||
|
<h3>📖 Lexikon</h3>
|
||||||
|
<span id="discovery-progress">0/18</span>
|
||||||
|
</div>
|
||||||
|
<div id="discovery-book">
|
||||||
|
<!-- Dynamisch gefüllt durch app.js -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="lab-desk">
|
<div id="lab-desk">
|
||||||
<h2>Labor-Tisch</h2>
|
<div class="desk-header">
|
||||||
<p>Ziehe Atome übereinander (z.B. 2x H und 1x O), um Reaktionen auszulösen!</p>
|
<div>
|
||||||
|
<h2>Labor-Tisch</h2>
|
||||||
|
<p>Ziehe Atome übereinander, um Reaktionen auszulösen!</p>
|
||||||
|
</div>
|
||||||
|
<div class="desk-controls">
|
||||||
|
<div id="stats">
|
||||||
|
Atome: <span id="atom-count">0</span> | Moleküle: <span id="molecule-count">0</span>
|
||||||
|
</div>
|
||||||
|
<button id="clear-desk-btn">🧹 Tisch leeren</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div id="workspace"></div>
|
<div id="workspace"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Info-Modal für Entdeckungen -->
|
||||||
|
<div id="info-modal" class="modal hidden">
|
||||||
|
<div class="modal-overlay"></div>
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<span class="modal-emoji">🎉</span>
|
||||||
|
<h2 id="modal-title">Molekül entdeckt!</h2>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<div id="modal-formula-badge">H₂O</div>
|
||||||
|
<h3 id="modal-molecule-name">Wasser</h3>
|
||||||
|
<p id="modal-description">Beschreibung...</p>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button id="modal-close-btn">Großartig!</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<script src="app.js"></script>
|
<script src="app.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
452
style.css
452
style.css
@ -1,76 +1,448 @@
|
|||||||
|
:root {
|
||||||
|
--bg-dark: #2c3e50;
|
||||||
|
--bg-sidebar: #1e272e;
|
||||||
|
--border-color: #3d4f5d;
|
||||||
|
--workspace-bg: #f8f9fa;
|
||||||
|
--text-light: #f5f6fa;
|
||||||
|
--text-muted: #a4b0be;
|
||||||
|
--primary: #3498db;
|
||||||
|
--primary-hover: #2980b9;
|
||||||
|
--shadow: 0 4px 15px rgba(0, 0, 0, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
display: flex;
|
display: flex;
|
||||||
margin: 0;
|
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
font-family: Arial, sans-serif;
|
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||||
background-color: #2c3e50;
|
background-color: var(--bg-dark);
|
||||||
color: white;
|
color: var(--text-light);
|
||||||
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Sidebar Layout */
|
||||||
#sidebar {
|
#sidebar {
|
||||||
width: 250px;
|
width: 320px;
|
||||||
background-color: #34495e;
|
background-color: var(--bg-sidebar);
|
||||||
padding: 20px;
|
border-right: 1px solid var(--border-color);
|
||||||
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;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.sidebar-header {
|
||||||
|
padding: 20px;
|
||||||
|
border-bottom: 1px solid var(--border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-header h2 {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-header p {
|
||||||
|
font-size: 0.85rem;
|
||||||
|
color: var(--text-muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Elements Selector */
|
||||||
|
#elements-section {
|
||||||
|
padding: 20px;
|
||||||
|
border-bottom: 1px solid var(--border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
#elements-section h3, #book-section h3 {
|
||||||
|
font-size: 0.9rem;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
color: var(--text-muted);
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.elements-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(4, 1fr);
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.spawn-btn {
|
||||||
|
height: 50px;
|
||||||
|
width: 50px;
|
||||||
|
border-radius: 50%;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 1.1rem;
|
||||||
|
cursor: pointer;
|
||||||
|
user-select: none;
|
||||||
|
color: #333;
|
||||||
|
border: 2px solid rgba(0,0,0,0.15);
|
||||||
|
box-shadow: 0 4px 6px rgba(0,0,0,0.2);
|
||||||
|
background-image: radial-gradient(circle at 30% 30%, rgba(255, 255, 255, 0.4) 0%, rgba(0, 0, 0, 0.3) 100%);
|
||||||
|
transition: transform 0.1s, box-shadow 0.1s;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Element Colors defined in JS, but base defaults for fallback */
|
||||||
|
.spawn-btn:hover {
|
||||||
|
transform: scale(1.1);
|
||||||
|
box-shadow: 0 6px 10px rgba(0,0,0,0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.spawn-btn:active {
|
||||||
|
transform: scale(0.95);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Specific button text colors */
|
||||||
|
.spawn-btn[data-element="C"] {
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Discovery Book (Lexikon) */
|
||||||
|
#book-section {
|
||||||
|
padding: 20px;
|
||||||
|
flex-grow: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.book-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#discovery-progress {
|
||||||
|
background-color: var(--primary);
|
||||||
|
padding: 3px 8px;
|
||||||
|
border-radius: 12px;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
#discovery-book {
|
||||||
|
flex-grow: 1;
|
||||||
|
overflow-y: auto;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
padding-right: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Scrollbar styles */
|
||||||
|
#discovery-book::-webkit-scrollbar {
|
||||||
|
width: 6px;
|
||||||
|
}
|
||||||
|
#discovery-book::-webkit-scrollbar-track {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
#discovery-book::-webkit-scrollbar-thumb {
|
||||||
|
background: var(--border-color);
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.book-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 10px 15px;
|
||||||
|
background-color: rgba(255, 255, 255, 0.03);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.05);
|
||||||
|
border-radius: 8px;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
transition: background-color 0.2s, border-color 0.2s;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.book-item.locked {
|
||||||
|
color: var(--text-muted);
|
||||||
|
opacity: 0.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.book-item.unlocked {
|
||||||
|
background-color: rgba(52, 152, 219, 0.1);
|
||||||
|
border-color: rgba(52, 152, 219, 0.2);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.book-item.unlocked:hover {
|
||||||
|
background-color: rgba(52, 152, 219, 0.2);
|
||||||
|
border-color: rgba(52, 152, 219, 0.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.book-formula {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.book-name {
|
||||||
|
font-size: 0.85rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Labor Tisch Layout */
|
||||||
|
#lab-desk {
|
||||||
|
flex-grow: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100%;
|
||||||
|
background-color: var(--bg-dark);
|
||||||
|
}
|
||||||
|
|
||||||
|
.desk-header {
|
||||||
|
padding: 20px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
background-color: rgba(0, 0, 0, 0.1);
|
||||||
|
border-bottom: 1px solid var(--border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.desk-header h2 {
|
||||||
|
font-size: 1.4rem;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.desk-header p {
|
||||||
|
font-size: 0.85rem;
|
||||||
|
color: var(--text-muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
.desk-controls {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#stats {
|
||||||
|
font-size: 0.9rem;
|
||||||
|
color: var(--text-muted);
|
||||||
|
background-color: rgba(0,0,0,0.2);
|
||||||
|
padding: 8px 12px;
|
||||||
|
border-radius: 6px;
|
||||||
|
border: 1px solid rgba(255,255,255,0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
#clear-desk-btn {
|
||||||
|
background-color: #e74c3c;
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
padding: 8px 15px;
|
||||||
|
border-radius: 6px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
transition: background-color 0.2s, transform 0.1s;
|
||||||
|
}
|
||||||
|
|
||||||
|
#clear-desk-btn:hover {
|
||||||
|
background-color: #c0392b;
|
||||||
|
}
|
||||||
|
|
||||||
|
#clear-desk-btn:active {
|
||||||
|
transform: scale(0.95);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Workspace Canvas */
|
||||||
#workspace {
|
#workspace {
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
background-color: #ecf0f1;
|
background-color: var(--workspace-bg);
|
||||||
background-image: linear-gradient(rgba(0,0,0,0.05) 1px, transparent 1px), linear-gradient(90deg, rgba(0,0,0,0.05) 1px, transparent 1px);
|
background-image:
|
||||||
background-size: 20px 20px;
|
linear-gradient(rgba(0, 0, 0, 0.05) 1px, transparent 1px),
|
||||||
border-radius: 10px;
|
linear-gradient(90deg, rgba(0, 0, 0, 0.05) 1px, transparent 1px);
|
||||||
|
background-size: 30px 30px;
|
||||||
|
margin: 20px;
|
||||||
|
border-radius: 12px;
|
||||||
position: relative;
|
position: relative;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
color: #333;
|
box-shadow: inset 0 2px 10px rgba(0, 0, 0, 0.15);
|
||||||
box-shadow: inset 0 0 10px rgba(0,0,0,0.1);
|
border: 1px solid var(--border-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Atoms and Molecules inside workspace */
|
||||||
.atom {
|
.atom {
|
||||||
width: 50px;
|
width: 55px;
|
||||||
height: 50px;
|
height: 55px;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
|
font-size: 1.15rem;
|
||||||
cursor: grab;
|
cursor: grab;
|
||||||
box-shadow: 0 4px 6px rgba(0,0,0,0.3);
|
box-shadow: 0 4px 10px rgba(0,0,0,0.3);
|
||||||
user-select: none;
|
user-select: none;
|
||||||
border: 2px solid rgba(0,0,0,0.2);
|
border: 2px solid rgba(0,0,0,0.2);
|
||||||
|
background-image: radial-gradient(circle at 30% 30%, rgba(255, 255, 255, 0.4) 0%, rgba(0, 0, 0, 0.45) 100%);
|
||||||
|
transition: transform 0.1s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.atom:active {
|
.atom:active {
|
||||||
cursor: grabbing;
|
cursor: grabbing;
|
||||||
|
transform: scale(1.05);
|
||||||
}
|
}
|
||||||
|
|
||||||
.molecule {
|
.molecule {
|
||||||
padding: 10px 20px;
|
padding: 12px 24px;
|
||||||
border-radius: 20px;
|
border-radius: 30px;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
|
font-size: 1.05rem;
|
||||||
cursor: grab;
|
cursor: grab;
|
||||||
box-shadow: 0 4px 6px rgba(0,0,0,0.3);
|
box-shadow: 0 5px 12px rgba(0,0,0,0.3);
|
||||||
background-color: #f1c40f;
|
color: white;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
border: 2px solid rgba(0,0,0,0.2);
|
border: 2px solid rgba(0,0,0,0.25);
|
||||||
|
background-image: linear-gradient(135deg, rgba(255,255,255,0.2) 0%, rgba(0,0,0,0.2) 100%);
|
||||||
|
transition: transform 0.1s;
|
||||||
|
text-shadow: 0 1px 2px rgba(0,0,0,0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.molecule:active {
|
||||||
|
cursor: grabbing;
|
||||||
|
transform: scale(1.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Particle Explosion Sparks */
|
||||||
|
.particle {
|
||||||
|
position: absolute;
|
||||||
|
width: 8px;
|
||||||
|
height: 8px;
|
||||||
|
border-radius: 50%;
|
||||||
|
pointer-events: none;
|
||||||
|
z-index: 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Modals */
|
||||||
|
.modal {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
z-index: 2000;
|
||||||
|
transition: opacity 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal.hidden {
|
||||||
|
opacity: 0;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-overlay {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background-color: rgba(0, 0, 0, 0.6);
|
||||||
|
backdrop-filter: blur(5px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-content {
|
||||||
|
position: relative;
|
||||||
|
background-color: #2c3e50;
|
||||||
|
border: 2px solid var(--border-color);
|
||||||
|
border-radius: 16px;
|
||||||
|
width: 450px;
|
||||||
|
max-width: 90%;
|
||||||
|
box-shadow: 0 10px 30px rgba(0,0,0,0.5);
|
||||||
|
overflow: hidden;
|
||||||
|
transform: scale(0.8);
|
||||||
|
transition: transform 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal:not(.hidden) .modal-content {
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-header {
|
||||||
|
background-color: rgba(0, 0, 0, 0.2);
|
||||||
|
padding: 20px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 15px;
|
||||||
|
border-bottom: 1px solid var(--border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-emoji {
|
||||||
|
font-size: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-header h2 {
|
||||||
|
font-size: 1.3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-body {
|
||||||
|
padding: 30px 20px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#modal-formula-badge {
|
||||||
|
background-color: var(--primary);
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 1.8rem;
|
||||||
|
padding: 10px 25px;
|
||||||
|
border-radius: 50px;
|
||||||
|
box-shadow: 0 4px 10px rgba(0,0,0,0.3);
|
||||||
|
margin-bottom: 15px;
|
||||||
|
border: 2px solid rgba(255,255,255,0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#modal-molecule-name {
|
||||||
|
font-size: 1.4rem;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
color: #ffeaa7;
|
||||||
|
}
|
||||||
|
|
||||||
|
#modal-description {
|
||||||
|
font-size: 0.95rem;
|
||||||
|
line-height: 1.5;
|
||||||
|
color: #dfe6e9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-footer {
|
||||||
|
padding: 15px 20px;
|
||||||
|
background-color: rgba(0, 0, 0, 0.1);
|
||||||
|
border-top: 1px solid var(--border-color);
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#modal-close-btn {
|
||||||
|
background-color: var(--primary);
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
padding: 10px 30px;
|
||||||
|
border-radius: 8px;
|
||||||
|
font-size: 1rem;
|
||||||
|
font-weight: bold;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background-color 0.2s, transform 0.1s;
|
||||||
|
box-shadow: 0 4px 6px rgba(0,0,0,0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
#modal-close-btn:hover {
|
||||||
|
background-color: var(--primary-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
#modal-close-btn:active {
|
||||||
|
transform: scale(0.95);
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user