1149 lines
54 KiB
JavaScript
1149 lines
54 KiB
JavaScript
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');
|
||
const deskTitle = document.getElementById('desk-title');
|
||
const deskSubtitle = document.getElementById('desk-subtitle');
|
||
|
||
// Main 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('#info-modal .modal-overlay');
|
||
const modalCloseIcon = document.querySelector('#info-modal .modal-close-icon');
|
||
|
||
// Story Success Modal Elements
|
||
const storySuccessModal = document.getElementById('story-success-modal');
|
||
const storySuccessLevelName = document.getElementById('story-success-level-name');
|
||
const storySuccessText = document.getElementById('story-success-text');
|
||
const storyNextLevelBtn = document.getElementById('story-next-level-btn');
|
||
const storySuccessCloseIcon = document.querySelector('#story-success-modal .modal-close-icon');
|
||
const storySuccessOverlay = document.querySelector('#story-success-modal .modal-overlay');
|
||
|
||
// PSE Modal Elements
|
||
const pseToggleBtn = document.getElementById('pse-toggle-btn');
|
||
const pseModal = document.getElementById('pse-modal');
|
||
const pseCloseBtn = document.getElementById('pse-close-btn');
|
||
const pseCloseIcon = document.getElementById('pse-close-icon');
|
||
const pseOverlay = document.querySelector('#pse-modal .modal-overlay');
|
||
const pseSpawnBtn = document.getElementById('pse-spawn-btn');
|
||
|
||
let elementsOnDesk = [];
|
||
let draggingElement = null;
|
||
let offsetX = 0;
|
||
let offsetY = 0;
|
||
let gameMode = 'sandbox'; // 'sandbox' oder 'story'
|
||
let selectedPSEElement = null;
|
||
|
||
// Geladene Entdeckungen aus dem LocalStorage
|
||
let discoveredMolecules = new Set(JSON.parse(localStorage.getItem('chem_lab_discoveries') || '[]'));
|
||
let currentLevelIndex = parseInt(localStorage.getItem('chem_lab_story_level') || '0');
|
||
let completedLevels = JSON.parse(localStorage.getItem('chem_lab_story_completed') || '[]');
|
||
let currentAllowedAtoms = [];
|
||
|
||
// Elemente Datenbank für Spawnen (Symbole, Farben, Namen)
|
||
const elementDetails = {
|
||
'H': { color: '#ff7675', name: 'Wasserstoff' },
|
||
'He': { color: '#e84393', name: 'Helium' },
|
||
'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' }
|
||
};
|
||
|
||
// Periodensystem Elemente Datenbank (Wissenschaftliche Details & Schalenmodell)
|
||
const pseElements = [
|
||
{ number: 1, symbol: 'H', name: 'Wasserstoff', mass: '1.008 u', category: 'nichtmetall', row: 1, col: 1, playable: true, electrons: [1], desc: 'Das häufigste und leichteste Element im Universum. Es bildet den primären Kernbrennstoff für alle leuchtenden Sterne.' },
|
||
{ number: 2, symbol: 'He', name: 'Helium', mass: '4.003 u', category: 'edelgas', row: 1, col: 18, playable: true, electrons: [2], desc: 'Ein absolut reaktionsträges Edelgas. Es brennt nicht und ist viel leichter als Luft – perfekt für Ballons und lässt Stimmen lustig hoch klingen!' },
|
||
{ number: 3, symbol: 'Li', name: 'Lithium', mass: '6.94 u', category: 'alkalimetall', row: 2, col: 1, playable: false, electrons: [2, 1], desc: 'Ein extrem leichtes, reaktives Metall. Es ist das Herzstück moderner Lithium-Ionen-Akkus für Handys, Laptops und Elektroautos.' },
|
||
{ number: 4, symbol: 'Be', name: 'Beryllium', mass: '9.012 u', category: 'erdalkali', row: 2, col: 2, playable: false, electrons: [2, 2], desc: 'Ein seltenes, hochgiftiges Erdalkalimetall. Es ist extrem steif und temperaturbeständig und wird deshalb im Weltraumteleskop-Bau (z.B. James Webb) eingesetzt.' },
|
||
{ number: 5, symbol: 'B', name: 'Bor', mass: '10.81 u', category: 'metalloid', row: 2, col: 13, playable: false, electrons: [2, 3], desc: 'Ein hartes, hitzebeständiges Halbmetall. Es wird für extrem widerstandsfähiges Glas (Borosilikatglas) und im Raketentreibstoff-Sektor verwendet.' },
|
||
{ number: 6, symbol: 'C', name: 'Kohlenstoff', mass: '12.011 u', category: 'nichtmetall', row: 2, col: 14, playable: true, electrons: [2, 4], desc: 'Die chemische Basis allen Lebens auf der Erde! Kohlenstoff kommt in reiner Form sowohl als weicher Graphit (Bleistift) als auch als extrem harter Diamant vor.' },
|
||
{ number: 7, symbol: 'N', name: 'Stickstoff', mass: '14.007 u', category: 'nichtmetall', row: 2, col: 15, playable: true, electrons: [2, 5], desc: 'Stickstoff bildet fast 78% unserer Atemluft! Er reagiert unter normalen Bedingungen kaum und schützt die Erde vor unkontrollierbaren Bränden.' },
|
||
{ number: 8, symbol: 'O', name: 'Sauerstoff', mass: '15.999 u', category: 'nichtmetall', row: 2, col: 16, playable: true, electrons: [2, 6], desc: 'Lebensnotwendig für fast alle Tiere. Es ist hochreaktiv und verbindet sich leidenschaftlich gern mit anderen Elementen (Rosten von Eisen, Verbrennung).' },
|
||
{ number: 9, symbol: 'F', name: 'Fluor', mass: '18.998 u', category: 'halogen', row: 2, col: 17, playable: false, electrons: [2, 7], desc: 'Das reaktivste aller chemischen Elemente! Fluor-Gas zerfrisst fast jeden Stoff sofort unter Flammenbildung. Spuren von Fluorid schützen unseren Zahnschmelz.' },
|
||
{ number: 10, symbol: 'Ne', name: 'Neon', mass: '20.180 u', category: 'edelgas', row: 2, col: 18, playable: false, electrons: [2, 8], desc: 'Ein extrem reaktionsträges Edelgas. Unter Hochspannung leuchtet es intensiv rötlich-orange, weshalb es der Namensgeber für alle Leuchtreklamen ist.' },
|
||
{ number: 11, symbol: 'Na', name: 'Natrium', mass: '22.990 u', category: 'alkalimetall', row: 3, col: 1, playable: true, electrons: [2, 8, 1], desc: 'Ein extrem reaktionsfreudiges, weiches Alkalimetall. Es reagiert explosionsartig mit Wasser und muss daher in zähem Öl (Petroleum) gelagert werden.' },
|
||
{ number: 12, symbol: 'Mg', name: 'Magnesium', mass: '24.305 u', category: 'erdalkali', row: 3, col: 2, playable: false, electrons: [2, 8, 2], desc: 'Ein leichtes Erdalkalimetall. Es brennt mit einer extrem hellen, blendend weißen Flamme und ist wichtig für die Muskel- und Nervenfunktion im Körper.' },
|
||
{ number: 13, symbol: 'Al', name: 'Aluminium', mass: '26.982 u', category: 'metall', row: 3, col: 13, playable: false, electrons: [2, 8, 3], desc: 'Das dritthäufigste Element der Erdkruste. Es ist leicht, rostfrei und wird für Flugzeuge, Alufolie, Getränkedosen und Autoteile verwendet.' },
|
||
{ number: 14, symbol: 'Si', name: 'Silicium', mass: '28.085 u', category: 'metalloid', row: 3, col: 14, playable: false, electrons: [2, 8, 4], desc: 'Das Element, das unsere Computertechnologie antreibt! Silicium ist ein Halbleiter und die absolute Basis aller Mikrochips und Solarzellen.' },
|
||
{ number: 15, symbol: 'P', name: 'Phosphor', mass: '30.974 u', category: 'nichtmetall', row: 3, col: 15, playable: false, electrons: [2, 8, 5], desc: 'Ein lebenswichtiges Element, das im menschlichen Erbgut (DNA) und in Knochen vorkommt. Roter Phosphor wird auf Streichholz-Reibflächen verwendet.' },
|
||
{ number: 16, symbol: 'S', name: 'Schwefel', mass: '32.06 u', category: 'nichtmetall', row: 3, col: 16, playable: true, electrons: [2, 8, 6], desc: 'Ein zitronengelber, geruchloser Feststoff. Seine organischen Verbindungen riechen oft extrem faulig (z.B. faule Eier). Schwefel ist wichtig für Haare und Proteine.' },
|
||
{ number: 17, symbol: 'Cl', name: 'Chlor', mass: '35.45 u', category: 'halogen', row: 3, col: 17, playable: true, electrons: [2, 8, 7], desc: 'Ein giftiges, stechend riechendes gelbgrünes Gas. Chlor ist extrem reaktiv und wird weltweit zur Desinfektion von Trinkwasser und Pools genutzt.' },
|
||
{ number: 18, symbol: 'Ar', name: 'Argon', mass: '39.948 u', category: 'edelgas', row: 3, col: 18, playable: false, electrons: [2, 8, 8], desc: 'Das dritthäufigste Gas in unserer Atmosphäre. Argon wird als ungiftiges Schutzgas beim Schweißen und in Doppelglasscheiben zur Wärmeisolierung eingesetzt.' },
|
||
{ number: 19, symbol: 'K', name: 'Kalium', mass: '39.098 u', category: 'alkalimetall', row: 4, col: 1, playable: false, electrons: [2, 8, 8, 1], desc: 'Ein weiches, hochreaktives Alkalimetall. Es reagiert noch heftiger mit Wasser als Natrium und ist essenziell für die Weiterleitung von Nervensignalen.' },
|
||
{ number: 20, symbol: 'Ca', name: 'Calcium', mass: '40.078 u', category: 'erdalkali', row: 4, col: 2, playable: false, electrons: [2, 8, 8, 2], desc: 'Der elementare Baustein für Skelette und Zähne! Calcium bildet Knochen, Eierschalen, Muscheln und Kreide und ist wichtig für die Muskelarbeit.' },
|
||
{ number: 26, symbol: 'Fe', name: 'Eisen', mass: '55.845 u', category: 'uebergang', row: 4, col: 8, playable: true, electrons: [2, 8, 14, 2], desc: 'Das am häufigsten genutzte Metall der Menschheit. Eisen bildet das Zentrum unseres Erdkerns (Magnetfeld) und transportiert Sauerstoff in unserem Blut (Hämoglobin).' }
|
||
];
|
||
|
||
// 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 genutzt.'
|
||
},
|
||
{
|
||
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).'
|
||
},
|
||
{
|
||
id: 'nh4cl',
|
||
ingredients: ['Cl', 'H', 'H', 'H', 'H', 'N'],
|
||
result: 'NH₄Cl (Ammoniumchlorid)',
|
||
color: '#ffffff',
|
||
desc: 'Ammoniumchlorid (Salmiak) ist ein weißes, feinkristallines Salz. Es schmeckt extrem salzig-sauer und wird traditionell in Nordeuropa für die Herstellung von echtem, starkem Salzlakritz verwendet!'
|
||
},
|
||
{
|
||
id: 'h2so3',
|
||
ingredients: ['H', 'H', 'O', 'O', 'O', 'S'],
|
||
result: 'H₂SO₃ (Schweflige Säure)',
|
||
color: '#e74c3c',
|
||
desc: 'Schweflige Säure entsteht, wenn Schwefeldioxid mit Wasser reagiert. Sie ist eine unbeständige, säuerliche Flüssigkeit, die stark reduzierend wirkt und zum Bleichen oder Konservieren genutzt wird.'
|
||
},
|
||
{
|
||
id: 'h2co3',
|
||
ingredients: ['C', 'H', 'H', 'O', 'O', 'O'],
|
||
result: 'H₂CO₃ (Kohlensäure)',
|
||
color: '#1abc9c',
|
||
desc: 'Kohlensäure entsteht, wenn sich CO₂ in Wasser löst. Sie ist eine schwache Säure, die für das erfrischende Prickeln in Getränken sorgt. Im Körper ist sie wichtig für den Säure-Basen-Haushalt.'
|
||
},
|
||
{
|
||
id: 'fecl2',
|
||
ingredients: ['Cl', 'Cl', 'Fe'],
|
||
result: 'FeCl₂ (Eisenchlorid)',
|
||
color: '#2ecc71',
|
||
desc: 'Eisen(II)-chlorid ist ein blassgrünes Salz. Seine sauren Lösungen werden in der Industrie zur Abwasserreinigung (als Fällungsmittel) und zum präzisen Ätzen von Leiterplatten eingesetzt.'
|
||
}
|
||
];
|
||
|
||
// Fortgeschrittene Molekül-Reaktionen Datenbank (Phase 4: Säure-Base & komplexe Reaktionen)
|
||
const advancedRecipes = [
|
||
{
|
||
id: 'neutralisation',
|
||
ingredients: ['hcl', 'naoh'], // Salzsäure + Natronlauge
|
||
results: ['nacl', 'h2o'], // ergibt Kochsalz + Wasser
|
||
name: 'Säure-Base-Neutralisation',
|
||
desc: 'Eine der klassischsten Reaktionen der Chemie! Die stark saure Salzsäure (HCl) und die aggressive, basische Natronlauge (NaOH) neutralisieren sich gegenseitig vollständig zu harmlosem Kochsalz (NaCl) und reinem Wasser (H₂O). Die Lösung erwärmt sich dabei durch die freiwerdende Neutralisationswärme stark!'
|
||
},
|
||
{
|
||
id: 'methan_verbrennung',
|
||
ingredients: ['ch4', 'o2', 'o2'], // Methan + 2x Sauerstoff
|
||
results: ['co2', 'h2o', 'h2o'], // ergibt Kohlendioxid + 2x Wasser
|
||
name: 'Methangas-Verbrennung',
|
||
desc: 'Methan (CH₄), der Hauptbestandteil von gasförmigem Erdgas, reagiert intensiv mit Sauerstoff (O₂). Bei dieser stark exothermen Verbrennungsreaktion entstehen Kohlendioxid (CO₂) und Wasserdampf (H₂O). Dies ist die treibende Kraft hinter vielen häuslichen Gasheizungen!'
|
||
},
|
||
{
|
||
id: 'salmiak_rauch',
|
||
ingredients: ['nh3', 'hcl'], // Ammoniak + Salzsäure (Chlorwasserstoff)
|
||
results: ['nh4cl'], // ergibt Ammoniumchlorid
|
||
name: 'Ammoniumchlorid-Synthese (Salmiak-Rauch)',
|
||
desc: 'Wenn das stechend riechende Ammoniakgas (NH₃) und Chlorwasserstoffgas (HCl) direkt aufeinandertreffen, findet eine Gasphasenreaktion statt. Es bildet sich sofort ein dichter weißer Rauch aus feinsten Ammoniumchlorid-Salzkristallen (NH₄Cl), auch bekannt als Salmiak!'
|
||
},
|
||
{
|
||
id: 'saurer_regen',
|
||
ingredients: ['so2', 'h2o'], // Schwefeldioxid + Wasser
|
||
results: ['h2so3'], // ergibt Schweflige Säure
|
||
name: 'Saure-Regen-Bildung',
|
||
desc: 'Schwefeldioxid (SO₂) entsteht bei der Verbrennung fossiler Brennstoffe. Wenn dieses Gas in der Atmosphäre mit Regenwasser (H₂O) reagiert, bildet sich Schweflige Säure (H₂SO₃). Dies führt zum berüchtigten "sauren Regen", der Böden, Wälder und historische Bauten schädigt.'
|
||
},
|
||
{
|
||
id: 'kohlensaeure_bildung',
|
||
ingredients: ['co2', 'h2o'], // Kohlendioxid + Wasser
|
||
results: ['h2co3'], // ergibt Kohlensäure
|
||
name: 'Kohlensäure-Bildung',
|
||
desc: 'Kohlendioxid (CO₂) löst sich zu einem kleinen Prozentsatz physikalisch-chemisch in Wasser (H₂O) und bildet Kohlensäure (H₂CO₃). Sie sorgt für das spritzige Prickeln in Mineralwasser und sprudelnden Erfrischungsgetränken!'
|
||
},
|
||
{
|
||
id: 'eisen_saeure',
|
||
ingredients: ['Fe', 'hcl', 'hcl'], // Eisen + 2x Salzsäure
|
||
results: ['fecl2', 'h2'], // ergibt Eisenchlorid + Wasserstoff
|
||
name: 'Säureangriff auf Metalle',
|
||
desc: 'Unedle Metalle wie Eisen (Fe) werden von Säuren wie der Salzsäure (HCl) angegriffen. Dabei oxidiert das Eisen zu löslichem Eisenchlorid (FeCl₂), während der Wasserstoff der Säure als hochentzündliches Wasserstoff-Gas (H₂) entweicht. Es sprudelt!'
|
||
},
|
||
{
|
||
id: 'chlorknallgas',
|
||
ingredients: ['h2', 'cl2'], // Wasserstoff-Molekül + Chlor-Molekül
|
||
results: ['hcl', 'hcl'], // ergibt 2x Salzsäure-Gas
|
||
name: 'Chlorknallgas-Reaktion',
|
||
desc: 'Wenn reines Wasserstoff-Gas (H₂) und Chlor-Gas (Cl₂) gemischt werden, genügt ein kurzer Lichtblitz oder Funke, um eine extrem heftige, explosive Kettenreaktion auszulösen. Dabei entsteht gasförmiger Chlorwasserstoff (HCl), der in Wasser gelöst Salzsäure bildet!'
|
||
}
|
||
];
|
||
|
||
// Story-Modus Level-Datenbank
|
||
const levels = [
|
||
{
|
||
id: 1,
|
||
title: "Die salzige Suppe",
|
||
goalFormula: "nacl",
|
||
allowedAtoms: ['Na', 'Cl'],
|
||
intro: "Hallo Lehrling! Schön dich im Labor zu sehen. Der herzhafte Eintopf unseres Kantinenkochs schmeckt heute nach gar nichts... Er hat das Salz vergessen! Kannst du ihm helfen und 1x Kochsalz (NaCl) synthetisieren? Ziehe einfach ein Natrium-Atom (Na) und ein Chlor-Atom (Cl) auf dem Tisch zusammen!",
|
||
successText: "Großartig! Der Eintopf ist gerettet und schmeckt hervorragend. Du hast soeben gelernt, dass aus zwei gefährlichen Reinstoffen (explosives Metall und giftiges Gas) ein lebenswichtiges Gewürz entstehen kann. Bereit für die nächste Herausforderung?",
|
||
reward: "Schaltet Level 2 frei"
|
||
},
|
||
{
|
||
id: 2,
|
||
title: "Das Lebenselixier",
|
||
goalFormula: "h2o",
|
||
allowedAtoms: ['H', 'O'],
|
||
intro: "Hervorragend gemacht! Jetzt haben wir aber ein anderes Problem: Der Wasserspender im Labor ist leer und die Hitze heute ist unerträglich. Wir brauchen dringend frisches Wasser (H₂O)! Synthetisiere uns ein Wassermolekül aus zwei Wasserstoff-Atomen (H) und einem Sauerstoff-Atom (O).",
|
||
successText: "Ah, erfrischend! Kaltes, sauberes H₂O. Wusstest du, dass Wassermoleküle wegen ihrer Polarität so genial zusammenhalten? Du bist auf dem besten Weg zum Meister-Alchemisten!",
|
||
reward: "Schaltet Level 3 frei"
|
||
},
|
||
{
|
||
id: 3,
|
||
title: "Der Mülleimerbrand",
|
||
goalFormula: "co2",
|
||
allowedAtoms: ['C', 'O'],
|
||
intro: "Achtung! Ein Missgeschick ist passiert: Jemand hat ein heißes Streichholz in den Mülleimer geworfen und es brennt! Wasser nützt hier nichts, wir brauchen ein erstickendes Gas. Synthetisiere schnell 1x Kohlendioxid (CO₂) aus einem Kohlenstoff-Atom (C) und zwei Sauerstoff-Atomen (O), um die Flammen zu löschen!",
|
||
successText: "Puh, gerettet! Das CO₂ hat den Sauerstoff verdrängt und das Feuer im Handumdrehen erstickt. Ein echter Lebensretter-Einsatz! Aber lüfte danach gut durch.",
|
||
reward: "Schaltet Level 4 frei"
|
||
},
|
||
{
|
||
id: 4,
|
||
title: "Dünger für die Ernte",
|
||
goalFormula: "nh3",
|
||
allowedAtoms: ['N', 'H'],
|
||
intro: "Ein lokaler Bauer hat uns um Hilfe gebeten. Seine Nutzpflanzen wachsen schlecht und er benötigt dringend Dünger. Der wichtigste Rohstoff dafür ist Ammoniak (NH₃). Synthetisiere ein Ammoniak-Molekül aus einem Stickstoff-Atom (N) und drei Wasserstoff-Atomen (H)!",
|
||
successText: "Hervorragende Arbeit! Das NH₃ ist fertig. Es riecht zwar extrem stechend nach Urin, aber ohne diesen Kunstdünger (Haber-Bosch-Verfahren) könnten wir heute nicht die gesamte Weltbevölkerung ernähren!",
|
||
reward: "Schaltet Level 5 frei"
|
||
},
|
||
{
|
||
id: 5,
|
||
title: "Die Verdauungshilfe",
|
||
goalFormula: "hcl",
|
||
allowedAtoms: ['H', 'Cl'],
|
||
intro: "Unser Labor-Maskottchen (ein kleiner gefräßiger Drache) hat Bauchschmerzen, weil er zu viel gefressen hat. Wir müssen seine Verdauung ankurbeln! Synthetisiere Salzsäure (HCl) aus einem Wasserstoff-Atom (H) und einem Chlor-Atom (Cl), um seinen Magensaft zu verstärken!",
|
||
successText: "Perfekt! Dem kleinen Drachen geht es schon viel besser. Salzsäure ist extrem sauer und zersetzt selbst zähes Fleisch im Handumdrehen. Nun bist du bereit für fortgeschrittenere Synthesen!",
|
||
reward: "Schaltet Level 6 frei"
|
||
},
|
||
{
|
||
id: 6,
|
||
title: "Die weiße Rauchbombe",
|
||
goalFormula: "salmiak_rauch",
|
||
allowedAtoms: ['N', 'H', 'Cl'],
|
||
intro: "Hervorragend, Alchemist! Jetzt wird es spektakulär: Wir bereiten einen Show-Effekt für den Tag der offenen Tür vor. Wir wollen dichten, weißen Nebel erzeugen! Synthetisiere zuerst Ammoniak (NH₃) aus 1x N und 3x H, und Salzsäure (HCl) aus 1x H und 1x Cl. Ziehe dann beide Moleküle zusammen, um feinstes Ammoniumchlorid-Salz (NH₄Cl) als Nebel entstehen zu lassen!",
|
||
successText: "Sensationell! Ein dichter weißer Rauch steigt auf. Du hast soeben deine erste zweistufige Synthese gemeistert! Aus Stickstoff, Wasserstoff und Chlor hast du zuerst Gase erzeugt und diese dann zu feinem Ammoniumchlorid-Salz neutralisiert.",
|
||
reward: "Schaltet Level 7 frei"
|
||
},
|
||
{
|
||
id: 7,
|
||
title: "Das saure Umweltproblem",
|
||
goalFormula: "saurer_regen",
|
||
allowedAtoms: ['S', 'O', 'H'],
|
||
intro: "Sehr gut! Ein Naturschutzverein bittet uns um Hilfe: Sie vermuten sauren Regen als Ursache für ein Waldsterben. Kannst du im Labor demonstrieren, wie dieser gebildet wird? Synthetisiere Schwefeldioxid (SO₂) aus 1x S und 2x O, sowie Wasser (H₂O) aus 2x H und 1x O. Ziehe dann beide Moleküle zusammen, um Schweflige Säure (H₂SO₃) zu erzeugen!",
|
||
successText: "Perfekt demonstriert! Schwefeldioxid (SO₂) verbindet sich mit dem Regenwasser (H₂O) zu Schwefliger Säure (H₂SO₃). Dank deiner Veranschaulichung versteht das Team das Problem nun genau und kann gezielte Filteranlagen fordern!",
|
||
reward: "Schaltet Level 8 frei"
|
||
},
|
||
{
|
||
id: 8,
|
||
title: "Die Säure-Base-Schlacht",
|
||
goalFormula: "neutralisation",
|
||
allowedAtoms: ['H', 'Cl', 'Na', 'O'],
|
||
intro: "Willkommen zum großen Meisterstück der Alchemie! Ein wahrer Meister muss aggressive Säuren und Basen bändigen. Wir haben ätzende Salzsäure (HCl) und hochreaktive Natronlauge (NaOH). Neutralisiere sie! Synthetisiere zuerst HCl und NaOH, und bringe sie dann zusammen, um harmloses Kochsalz (NaCl) und reines Wasser (H₂O) zu erhalten!",
|
||
successText: "Unglaublich! Du hast es geschafft! Die heftige Reaktion hat Energie freigesetzt, aber am Ende blieben nur Speisesalz und klares Wasser übrig. Du hast die Kunst der Neutralisation gemeistert und bist nun ein anerkannter Großmeister der molekularen Alchemie!",
|
||
reward: "Story-Modus abgeschlossen! 🎉"
|
||
}
|
||
];
|
||
|
||
// Initiales Laden
|
||
updateStats();
|
||
renderDiscoveryBook();
|
||
|
||
// Button-Klicks registrieren für Spawnen in Sidebar
|
||
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();
|
||
});
|
||
|
||
// =========================================
|
||
// MODALS LOGIC
|
||
// =========================================
|
||
|
||
// Info-Modal (Entdeckungen) Schließen
|
||
modalCloseBtn.addEventListener('click', closeModal);
|
||
modalOverlay.addEventListener('click', closeModal);
|
||
modalCloseIcon.addEventListener('click', closeModal);
|
||
|
||
function closeModal() {
|
||
infoModal.classList.add('hidden');
|
||
}
|
||
|
||
// PSE Modal Steuerung
|
||
pseToggleBtn.addEventListener('click', () => {
|
||
pseModal.classList.remove('hidden');
|
||
renderPSEGrid();
|
||
});
|
||
|
||
pseCloseBtn.addEventListener('click', closePSE);
|
||
pseCloseIcon.addEventListener('click', closePSE);
|
||
pseOverlay.addEventListener('click', closePSE);
|
||
|
||
function closePSE() {
|
||
pseModal.classList.add('hidden');
|
||
}
|
||
|
||
// PSE Atom Spawnen Event
|
||
pseSpawnBtn.addEventListener('click', () => {
|
||
if (selectedPSEElement) {
|
||
spawnAtom(selectedPSEElement.symbol, elementDetails[selectedPSEElement.symbol].color);
|
||
closePSE();
|
||
}
|
||
});
|
||
|
||
// =========================================
|
||
// GAMEPLAY CORE LOGIC
|
||
// =========================================
|
||
|
||
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/kontrastreiche 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) {
|
||
// Reaktions-Keys extrahieren (Atome => Großbuchstaben-Symbol, Moleküle => Kleinbuchstaben-Rezept-ID)
|
||
const reactantKeys = [];
|
||
cluster.forEach(el => {
|
||
if (el.classList.contains('atom')) {
|
||
reactantKeys.push(el.dataset.symbol);
|
||
} else if (el.classList.contains('molecule')) {
|
||
reactantKeys.push(el.dataset.recipeId);
|
||
}
|
||
});
|
||
reactantKeys.sort();
|
||
|
||
// ZUERST: Fortgeschrittene Molekül-Reaktionen prüfen
|
||
let advancedReactionFound = false;
|
||
for (const advRecipe of advancedRecipes) {
|
||
const advIngredients = [...advRecipe.ingredients].sort();
|
||
|
||
if (JSON.stringify(reactantKeys) === JSON.stringify(advIngredients)) {
|
||
// FORTGESCHRITTENE REAKTION ERFOLGREICH!
|
||
advancedReactionFound = true;
|
||
const midX = parseFloat(draggingElement.style.left) + draggingElement.offsetWidth / 2;
|
||
const midY = parseFloat(draggingElement.style.top) + draggingElement.offsetHeight / 2;
|
||
|
||
// Lösche alle reactant-Elemente im Cluster vom Tisch
|
||
cluster.forEach(el => {
|
||
if (workspace.contains(el)) {
|
||
workspace.removeChild(el);
|
||
}
|
||
elementsOnDesk = elementsOnDesk.filter(e => e !== el);
|
||
});
|
||
|
||
// Dicke, bunte Explosion erzeugen
|
||
triggerExplosion(midX, midY, '#ff9f43');
|
||
|
||
// Erzeuge alle Produkt-Moleküle des fortgeschrittenen Rezepts
|
||
advRecipe.results.forEach((prodId, index) => {
|
||
const prodRecipe = recipes.find(r => r.id === prodId);
|
||
if (prodRecipe) {
|
||
// Platziere Produkte nebeneinander im Fusionszentrum
|
||
const offsetMultiplier = index - (advRecipe.results.length - 1) / 2;
|
||
const molX = midX - 60 + (offsetMultiplier * 80);
|
||
const molY = midY - 20;
|
||
createMolecule(prodRecipe, molX, molY);
|
||
|
||
// Entdeckung registrieren
|
||
const isNew = !discoveredMolecules.has(prodRecipe.id);
|
||
if (isNew) {
|
||
discoveredMolecules.add(prodRecipe.id);
|
||
localStorage.setItem('chem_lab_discoveries', JSON.stringify([...discoveredMolecules]));
|
||
}
|
||
}
|
||
});
|
||
|
||
renderDiscoveryBook();
|
||
|
||
// Story-Modus Ziel prüfen (für fortgeschrittene Reaktionen)
|
||
if (gameMode === 'story') {
|
||
const currentLevel = levels[currentLevelIndex];
|
||
if (advRecipe.id === currentLevel.goalFormula) {
|
||
handleLevelSuccess(currentLevel, advRecipe);
|
||
break;
|
||
}
|
||
}
|
||
|
||
// Info-Modal für die gesamte Reaktion anzeigen!
|
||
showAdvancedReactionModal(advRecipe);
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (advancedReactionFound) return;
|
||
|
||
// DANACH: Normale Atom-Fusion (Cluster darf KEINE Moleküle enthalten!)
|
||
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();
|
||
}
|
||
|
||
// Story-Modus Ziel prüfen (für atomare Fusionen)
|
||
if (gameMode === 'story') {
|
||
const currentLevel = levels[currentLevelIndex];
|
||
if (recipe.id === currentLevel.goalFormula) {
|
||
handleLevelSuccess(currentLevel, recipe);
|
||
break;
|
||
}
|
||
}
|
||
|
||
// 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.dataset.recipeId = recipe.id;
|
||
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
|
||
|
||
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('#info-modal .modal-emoji').innerText = "🎉";
|
||
} else {
|
||
modalTitle.innerText = "Molekül-Lexikon 📖";
|
||
document.querySelector('#info-modal .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 showAdvancedReactionModal(advRecipe) {
|
||
modalTitle.innerText = "Chemische Reaktivität! 💥";
|
||
document.querySelector('#info-modal .modal-emoji').innerText = "💥";
|
||
|
||
// Formel mit Tiefstellung formatieren (Sowohl Atome wie Fe als auch Moleküle wie h2o)
|
||
const formatFormula = (id) => {
|
||
const formulaText = id.toUpperCase();
|
||
return formulaText.replace(/\d/g, m => '<sub>' + m + '</sub>');
|
||
};
|
||
|
||
const reactantsText = advRecipe.ingredients.map(formatFormula).join(' + ');
|
||
const productsText = advRecipe.results.map(formatFormula).join(' + ');
|
||
|
||
modalFormula.innerHTML = `${reactantsText} → ${productsText}`;
|
||
modalFormula.style.backgroundColor = '#ff9f43'; // Schöne orange exotherme Reaktionsfarbe
|
||
|
||
modalName.innerText = advRecipe.name;
|
||
modalDesc.innerText = advRecipe.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;
|
||
}
|
||
|
||
// =========================================
|
||
// INTERACTIVE PSE LOGIC
|
||
// =========================================
|
||
|
||
function renderPSEGrid() {
|
||
const grid = document.getElementById('pse-grid');
|
||
grid.innerHTML = '';
|
||
|
||
pseElements.forEach(el => {
|
||
const div = document.createElement('div');
|
||
div.classList.add('pse-el');
|
||
div.style.gridRow = el.row;
|
||
div.style.gridColumn = el.col;
|
||
div.dataset.symbol = el.symbol;
|
||
|
||
div.innerHTML = `
|
||
<span class="pse-el-number">${el.number}</span>
|
||
<span class="pse-el-symbol">${el.symbol}</span>
|
||
<span class="pse-el-name">${el.name}</span>
|
||
`;
|
||
|
||
if (el.playable) {
|
||
div.classList.add('active-el');
|
||
div.classList.add(el.category);
|
||
div.addEventListener('click', () => {
|
||
selectElementSteckbrief(el);
|
||
});
|
||
} else {
|
||
div.classList.add('locked-el');
|
||
div.addEventListener('click', () => {
|
||
// Selektiert dekorative Elemente trotzdem für den Steckbrief (Lerneffekt!), aber deaktiviert Spawnen
|
||
selectElementSteckbrief(el, false);
|
||
});
|
||
}
|
||
grid.appendChild(div);
|
||
});
|
||
}
|
||
|
||
function selectElementSteckbrief(el, canSpawn = true) {
|
||
selectedPSEElement = el;
|
||
|
||
const emptyDiv = document.querySelector('.steckbrief-empty');
|
||
const cardDiv = document.querySelector('.steckbrief-card');
|
||
|
||
emptyDiv.classList.add('hidden');
|
||
cardDiv.classList.remove('hidden');
|
||
|
||
const box = document.getElementById('steckbrief-box');
|
||
box.innerText = el.symbol;
|
||
box.className = ''; // Zurücksetzen
|
||
box.classList.add('active-el', el.category);
|
||
|
||
// Spezieller Textkontrast für dunkle Elemente im Steckbrief
|
||
if (el.symbol === 'C' || el.symbol === 'Fe') {
|
||
box.style.color = '#fff';
|
||
} else {
|
||
box.style.color = '#333';
|
||
}
|
||
|
||
document.getElementById('steckbrief-name').innerText = el.name;
|
||
|
||
// Kategoriennamen lesbar übersetzen
|
||
const categoryNames = {
|
||
'nichtmetall': 'Nichtmetall',
|
||
'edelgas': 'Edelgas',
|
||
'alkalimetall': 'Alkalimetall',
|
||
'erdalkali': 'Erdalkalimetall',
|
||
'uebergang': 'Übergangsmetall',
|
||
'halogen': 'Halogen',
|
||
'metalloid': 'Halbmetall',
|
||
'metall': 'Metall'
|
||
};
|
||
document.getElementById('steckbrief-category').innerText = categoryNames[el.category] || el.category;
|
||
document.getElementById('steckbrief-category').className = '';
|
||
document.getElementById('steckbrief-category').classList.add(el.category);
|
||
|
||
document.getElementById('steckbrief-number').innerText = el.number;
|
||
document.getElementById('steckbrief-mass').innerText = el.mass;
|
||
document.getElementById('steckbrief-desc').innerText = el.desc;
|
||
|
||
// Spawnen-Button steuern
|
||
if (canSpawn && (gameMode === 'sandbox' || currentAllowedAtoms.includes(el.symbol))) {
|
||
pseSpawnBtn.classList.remove('hidden');
|
||
} else {
|
||
pseSpawnBtn.classList.add('hidden');
|
||
}
|
||
|
||
// Bohr-Modell Schalen visualisieren
|
||
renderBohrModel(el.electrons);
|
||
}
|
||
|
||
function renderBohrModel(electronsArray) {
|
||
const shellsContainer = document.getElementById('bohr-shells');
|
||
shellsContainer.innerHTML = '';
|
||
|
||
electronsArray.forEach((numElectrons, shellIndex) => {
|
||
const shellDiv = document.createElement('div');
|
||
shellDiv.classList.add('bohr-shell');
|
||
|
||
// Dynamischer Schalendurchmesser basierend auf Schalenindex (Concentric Circles)
|
||
const diameter = 40 + (shellIndex * 30);
|
||
shellDiv.style.width = diameter + 'px';
|
||
shellDiv.style.height = diameter + 'px';
|
||
|
||
// Geschwindigkeit variieren für coole Dynamik
|
||
shellDiv.style.animationDuration = (6 + shellIndex * 4) + 's';
|
||
|
||
// Elektronenpunkte auf der Kreisbahn berechnen & platzieren
|
||
for (let i = 0; i < numElectrons; i++) {
|
||
const electron = document.createElement('div');
|
||
electron.classList.add('bohr-electron');
|
||
|
||
// Winkel für Gleichverteilung berechnen
|
||
const angle = (i / numElectrons) * Math.PI * 2;
|
||
const radius = diameter / 2;
|
||
const x = Math.cos(angle) * radius;
|
||
const y = Math.sin(angle) * radius;
|
||
|
||
// Auf der CSS-Kreisbahn zentrieren
|
||
electron.style.left = `calc(50% + ${x}px - 3px)`;
|
||
electron.style.top = `calc(50% + ${y}px - 3px)`;
|
||
|
||
shellDiv.appendChild(electron);
|
||
}
|
||
|
||
shellsContainer.appendChild(shellDiv);
|
||
});
|
||
}
|
||
|
||
// =========================================
|
||
// STORY MODE LOGIC
|
||
// =========================================
|
||
|
||
// Dom Tabs switching
|
||
const tabSandboxBtn = document.getElementById('tab-sandbox-btn');
|
||
const tabStoryBtn = document.getElementById('tab-story-btn');
|
||
const sandboxContentEl = document.getElementById('sandbox-content');
|
||
const storyContentEl = document.getElementById('story-content');
|
||
|
||
tabSandboxBtn.addEventListener('click', () => {
|
||
switchMode('sandbox');
|
||
});
|
||
|
||
tabStoryBtn.addEventListener('click', () => {
|
||
switchMode('story');
|
||
});
|
||
|
||
function switchMode(mode) {
|
||
gameMode = mode;
|
||
workspace.innerHTML = '';
|
||
elementsOnDesk = [];
|
||
updateStats();
|
||
closeModal();
|
||
closeStorySuccessModal();
|
||
|
||
if (mode === 'sandbox') {
|
||
tabSandboxBtn.classList.add('active');
|
||
tabStoryBtn.classList.remove('active');
|
||
sandboxContentEl.classList.remove('hidden');
|
||
storyContentEl.classList.add('hidden');
|
||
|
||
deskTitle.innerText = "Labor-Tisch (Sandbox)";
|
||
deskSubtitle.innerText = "Ziehe Atome übereinander, um Reaktionen frei auszulösen!";
|
||
currentAllowedAtoms = Object.keys(elementDetails);
|
||
} else {
|
||
tabSandboxBtn.classList.remove('active');
|
||
tabStoryBtn.classList.add('active');
|
||
sandboxContentEl.classList.add('hidden');
|
||
storyContentEl.classList.remove('hidden');
|
||
|
||
loadStoryLevel(currentLevelIndex);
|
||
}
|
||
}
|
||
|
||
function loadStoryLevel(index) {
|
||
if (index >= levels.length) {
|
||
index = levels.length - 1;
|
||
}
|
||
currentLevelIndex = index;
|
||
localStorage.setItem('chem_lab_story_level', currentLevelIndex);
|
||
|
||
const level = levels[currentLevelIndex];
|
||
currentAllowedAtoms = level.allowedAtoms;
|
||
|
||
deskTitle.innerText = `Labor-Tisch (Story: ${level.title})`;
|
||
const recipe = recipes.find(r => r.id === level.goalFormula);
|
||
const cleanName = recipe.result.substring(recipe.result.indexOf('(') + 1, recipe.result.indexOf(')'));
|
||
deskSubtitle.innerText = `Ziel: Synthetisiere ${cleanName}`;
|
||
|
||
// Level Auswahldots zeichnen
|
||
renderLevelDots();
|
||
|
||
// Quest Panel updaten
|
||
document.getElementById('quest-level-title').innerText = `Level ${level.id}: ${level.title}`;
|
||
document.getElementById('quest-intro-text').innerText = level.intro;
|
||
|
||
const prettyFormula = recipe.id.toUpperCase().replace(/\d/g, m => '<sub>' + m + '</sub>');
|
||
document.getElementById('quest-goal-text').innerHTML = `Erzeuge 1x ${prettyFormula} (${cleanName})`;
|
||
|
||
// Checkbox zurücksetzen
|
||
const checkbox = document.querySelector('.objective-checkbox');
|
||
const objectiveItem = document.querySelector('.objective-item');
|
||
if (checkbox) checkbox.innerText = "⬜";
|
||
if (objectiveItem) objectiveItem.classList.remove('completed');
|
||
|
||
// Spawnbare Atome im Story-Gitter rendern
|
||
renderStorySpawnButtons();
|
||
}
|
||
|
||
function renderLevelDots() {
|
||
const container = document.querySelector('.level-dots-grid');
|
||
if (!container) return;
|
||
container.innerHTML = '';
|
||
|
||
levels.forEach((lvl, idx) => {
|
||
const dot = document.createElement('div');
|
||
dot.classList.add('level-dot');
|
||
dot.innerText = lvl.id;
|
||
|
||
const isCompleted = completedLevels.includes(lvl.id);
|
||
const isActive = idx === currentLevelIndex;
|
||
const isLocked = idx > 0 && !completedLevels.includes(levels[idx - 1].id);
|
||
|
||
if (isLocked) {
|
||
dot.classList.add('locked');
|
||
} else {
|
||
if (isCompleted) dot.classList.add('completed');
|
||
if (isActive) dot.classList.add('active');
|
||
|
||
dot.addEventListener('click', () => {
|
||
workspace.innerHTML = '';
|
||
elementsOnDesk = [];
|
||
updateStats();
|
||
loadStoryLevel(idx);
|
||
});
|
||
}
|
||
container.appendChild(dot);
|
||
});
|
||
|
||
const totalLevels = levels.length;
|
||
document.getElementById('story-overall-progress').innerText = `${completedLevels.includes(totalLevels) ? totalLevels : Math.min(totalLevels, completedLevels.length + 1)}/${totalLevels}`;
|
||
}
|
||
|
||
function renderStorySpawnButtons() {
|
||
const grid = document.getElementById('story-elements-grid');
|
||
if (!grid) return;
|
||
grid.innerHTML = '';
|
||
|
||
currentAllowedAtoms.forEach(symbol => {
|
||
const details = elementDetails[symbol];
|
||
if (details) {
|
||
const btn = document.createElement('div');
|
||
btn.classList.add('spawn-btn');
|
||
btn.dataset.element = symbol;
|
||
btn.dataset.color = details.color;
|
||
btn.innerText = symbol;
|
||
btn.title = details.name;
|
||
btn.style.backgroundColor = details.color;
|
||
|
||
if (symbol === 'C' || symbol === 'Fe') {
|
||
btn.style.color = '#fff';
|
||
} else {
|
||
btn.style.color = '#333';
|
||
}
|
||
|
||
btn.addEventListener('click', () => {
|
||
spawnAtom(symbol, details.color);
|
||
});
|
||
grid.appendChild(btn);
|
||
}
|
||
});
|
||
}
|
||
|
||
function completedMoleculesAdd(levelId) {
|
||
if (!completedLevels.includes(levelId)) {
|
||
completedLevels.push(levelId);
|
||
localStorage.setItem('chem_lab_story_completed', JSON.stringify(completedLevels));
|
||
}
|
||
}
|
||
|
||
function handleLevelSuccess(level, recipe) {
|
||
const checkbox = document.querySelector('.objective-checkbox');
|
||
const objectiveItem = document.querySelector('.objective-item');
|
||
if (checkbox) checkbox.innerText = "✅";
|
||
if (objectiveItem) objectiveItem.classList.add('completed');
|
||
|
||
completedMoleculesAdd(level.id);
|
||
renderLevelDots();
|
||
|
||
// Zeige das große Level-Erfolgs-Modal nach einer kurzen Verzögerung
|
||
setTimeout(() => {
|
||
showStorySuccessModal(level, recipe);
|
||
}, 600);
|
||
}
|
||
|
||
function showStorySuccessModal(level, recipe) {
|
||
storySuccessLevelName.innerText = `Level ${level.id} abgeschlossen!`;
|
||
storySuccessText.innerText = level.successText;
|
||
|
||
if (currentLevelIndex === levels.length - 1) {
|
||
storyNextLevelBtn.innerText = "🏆 Story-Modus beenden!";
|
||
} else {
|
||
storyNextLevelBtn.innerText = "Nächstes Level ➡️";
|
||
}
|
||
|
||
storySuccessModal.classList.remove('hidden');
|
||
}
|
||
|
||
function closeStorySuccessModal() {
|
||
storySuccessModal.classList.add('hidden');
|
||
}
|
||
|
||
// Story Next Level Button Event
|
||
storyNextLevelBtn.addEventListener('click', () => {
|
||
closeStorySuccessModal();
|
||
workspace.innerHTML = '';
|
||
elementsOnDesk = [];
|
||
updateStats();
|
||
|
||
if (currentLevelIndex < levels.length - 1) {
|
||
currentLevelIndex++;
|
||
localStorage.setItem('chem_lab_story_level', currentLevelIndex);
|
||
loadStoryLevel(currentLevelIndex);
|
||
} else {
|
||
alert("Glückwunsch! Du hast alle Level abgeschlossen. Der Sandkasten-Modus (Sandbox) steht dir weiterhin zur Verfügung!");
|
||
switchMode('sandbox');
|
||
}
|
||
});
|
||
|
||
// Zusätzliche Modal Close-Events
|
||
if (storySuccessCloseIcon) storySuccessCloseIcon.addEventListener('click', closeStorySuccessModal);
|
||
if (storySuccessOverlay) storySuccessOverlay.addEventListener('click', closeStorySuccessModal);
|