From 1577c68564804f3cc60fd31904bf0e0197e5b6a9 Mon Sep 17 00:00:00 2001 From: kreidler90 Date: Sat, 4 Jul 2026 12:51:41 +0000 Subject: [PATCH] feat: implement Story-Modus with chapter selector, Professor Atomus dialogs, level constraints, success modals, and localStorage saves --- app.js | 292 +++++++++++++++++++++++++++++++++++++++++++++++++++-- index.html | 114 ++++++++++++++++----- style.css | 269 ++++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 632 insertions(+), 43 deletions(-) diff --git a/app.js b/app.js index 8e52d08..b4e2293 100644 --- a/app.js +++ b/app.js @@ -5,6 +5,8 @@ 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'); @@ -16,6 +18,14 @@ 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'); @@ -28,6 +38,14 @@ 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 = { @@ -179,7 +197,7 @@ const recipes = [ 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.' + 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', @@ -193,13 +211,58 @@ const recipes = [ ingredients: ['C', 'C', 'H', 'H', 'H', 'H'], // 2x Kohlenstoff, 4x Wasserstoff result: 'C₂H₄ (Ethen)', color: '#ffeaa7', - desc: 'Ethen (Ethylen) is ein gasförmiges Pflanzenhormon, das Früchte reifen lässt. Industriell ist es der absolut wichtigste Baustein zur Herstellung des Kunststoffs Polyethylen (PE).' + 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).' } ]; -// Geladene Entdeckungen aus dem LocalStorage -let discoveredMolecules = new Set(JSON.parse(localStorage.getItem('chem_lab_discoveries') || '[]')); -let selectedPSEElement = null; +// 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. Du hast den Story-Modus abgeschlossen! Du bist jetzt ein zertifizierter Labor-Meister!", + reward: "Story-Modus abgeschlossen! 🎉" + } +]; // Initiales Laden updateStats(); @@ -403,6 +466,15 @@ function checkForReactions() { renderDiscoveryBook(); } + // Story-Modus Ziel prüfen + 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); @@ -483,7 +555,7 @@ function showModal(recipe, isNew = true) { modalFormula.innerHTML = prettyFormula; modalFormula.style.backgroundColor = recipe.color; - // Reiner Name (Text in Klammern) + // 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; @@ -613,7 +685,7 @@ function selectElementSteckbrief(el, canSpawn = true) { 'metall': 'Metall' }; document.getElementById('steckbrief-category').innerText = categoryNames[el.category] || el.category; - document.getElementById('steckbrief-category').className = ''; // Reset + document.getElementById('steckbrief-category').className = ''; document.getElementById('steckbrief-category').classList.add(el.category); document.getElementById('steckbrief-number').innerText = el.number; @@ -621,7 +693,7 @@ function selectElementSteckbrief(el, canSpawn = true) { document.getElementById('steckbrief-desc').innerText = el.desc; // Spawnen-Button steuern - if (canSpawn) { + if (canSpawn && (gameMode === 'sandbox' || currentAllowedAtoms.includes(el.symbol))) { pseSpawnBtn.classList.remove('hidden'); } else { pseSpawnBtn.classList.add('hidden'); @@ -668,3 +740,207 @@ function renderBohrModel(electronsArray) { 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 => '' + m + ''); + 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 completedCount = completedLevels.length; + document.getElementById('story-overall-progress').innerText = `${completedLevels.includes(5) ? 5 : Math.max(1, completedLevels.length + 1)}/5`; +} + +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); diff --git a/index.html b/index.html index 782b574..eae7a7d 100644 --- a/index.html +++ b/index.html @@ -9,32 +9,81 @@