From 665e86d4854446bdbbfec843c97dabb41d6f9d0f Mon Sep 17 00:00:00 2001 From: Thomas Date: Sun, 19 Apr 2026 14:13:47 +0200 Subject: [PATCH] Klima 3D: Musik-Reset, Szene 10% tiefer, Drohnen-Toggle MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - ggsMusicResetToDefault: neuer Helper springt auf den ersten Track im Dropdown. Wird in startGame aufgerufen, damit Drama-Musik aus dem Endscreen eines vorherigen Durchgangs nicht ins neue Level hinüber- läuft (erklärt, warum 'Drama im laufenden Spiel' lief). - camera.lookAt: von (0, 0.5, 0) auf (0, 2.8, 0) angehoben → Insel rutscht im Bild ~10 % nach unten, oben mehr Himmel/Dunst-Wolken. - Drohnen-Toggle 🚁 im Header: neuer droneEnabled-Flag. Aus = Auto-Orbit ist dauerhaft gestoppt (nur Maus-Drag bewegt Kamera). Ein = Idle-Timeout resumed wie bisher. Button zeigt aktiven Zustand über opacity + .active-Klasse. Co-Authored-By: Claude Opus 4.7 (1M context) --- App/sims/klima/game-3d.html | 42 +++++++++++++++++++++++++++++++++---- 1 file changed, 38 insertions(+), 4 deletions(-) diff --git a/App/sims/klima/game-3d.html b/App/sims/klima/game-3d.html index 7618698..ad2c468 100644 --- a/App/sims/klima/game-3d.html +++ b/App/sims/klima/game-3d.html @@ -609,6 +609,7 @@ 📚 + 🏠 @@ -2319,6 +2320,7 @@ let camTheta = Math.PI * 0.12; // Azimut (horizontale Drehung) let camPhi = Math.PI * 0.34; // Polar (Höhe; π/2 = Horizont, 0 = Vogelperspektive) let camRadius = 30; // Distanz vom Zentrum let droneActive = true; +let droneEnabled = true; // Header-Toggle: aus = nie auto-drehen let droneResumeAt = 0; // in Sekunden; nach pauseDrone()+Idle weiter let isDragging = false; let lastPointerX = 0, lastPointerY = 0; @@ -2331,7 +2333,9 @@ function applyCamera() { cosPhi * camRadius, Math.cos(camTheta) * sinPhi * camRadius ); - camera.lookAt(0, 0.5, 0); + // LookAt leicht erhöht → die Szene rutscht im Bild nach unten, oben + // mehr Himmel/Dunst-Wolken, Insel sitzt im mittleren Drittel. + camera.lookAt(0, 2.8, 0); } function pauseDrone() { droneActive = false; @@ -2556,8 +2560,10 @@ function sceneRender() { const dt = Math.max(0, tNow - _lastSceneMs); _lastSceneMs = tNow; - // Auto-Drohne: nach Idle-Timeout wieder sanft weiter-drehen - if (!droneActive && tNow >= droneResumeAt && !isDragging) droneActive = true; + // Auto-Drohne: nach Idle-Timeout wieder sanft weiter-drehen, aber nur + // wenn der Drohnen-Toggle im Header aktiv ist. + if (!droneEnabled) droneActive = false; + else if (!droneActive && tNow >= droneResumeAt && !isDragging) droneActive = true; if (droneActive) camTheta += dt * 0.06; // Meeresspiegel folgt state.seaLevelCm (0 cm = WATER_Y_BASE, 200 cm = +MAX_SEA_RISE) @@ -4572,6 +4578,23 @@ $$('.ggs-speed-btn').forEach(btn => { }); }); +// Drohnen-Toggle im Header: schaltet droneEnabled um. Wenn aus, pausiert +// die Auto-Kamera-Drehung dauerhaft (nur Maus-Drag bewegt noch). +const btnDrone = $('#btn-drone'); +if (btnDrone) { + const refreshDroneBtn = () => { + btnDrone.classList.toggle('active', droneEnabled); + btnDrone.style.opacity = droneEnabled ? '1' : '0.45'; + }; + refreshDroneBtn(); + btnDrone.addEventListener('click', () => { + droneEnabled = !droneEnabled; + if (!droneEnabled) { droneActive = false; droneResumeAt = Number.POSITIVE_INFINITY; } + else { droneResumeAt = (state.animMs || 0) / 1000 + 2; } + refreshDroneBtn(); + }); +} + // Pause-Zeichen zentral in der Szene: sichtbar bei speed=0, sonst aus. // Klick entspricht dem Druck auf den 1×-Speed-Button. const pauseCenter = $('#pause-center'); @@ -4692,7 +4715,8 @@ function startGame(levelId) { if (_glossarPopup) { _glossarPopup.remove(); _glossarPopup = null; } // Neues Level → Musik-Override aufheben, damit Auto-Switch (Drama) wieder // greift, falls der Spieler sich vorher manuell etwas ausgesucht hatte. - if (window.ggsMusicResetManualOverride) window.ggsMusicResetManualOverride(); + if (window.ggsMusicResetToDefault) window.ggsMusicResetToDefault(); + else if (window.ggsMusicResetManualOverride) window.ggsMusicResetManualOverride(); // --- 3. Visuelle Overlays schließen --- hideOverlay('level-select-overlay'); @@ -5402,6 +5426,16 @@ window.ggsMusicSetup = function(tracks) { _manualMusicOverride = false; _preAutoTrack = null; }; + // Setzt die Musik zurück auf den ersten Track im Dropdown (Default). + // Wird beim Level-Start aufgerufen, damit Drama-Musik aus dem Endscreen + // eines vorherigen Durchgangs nicht ins neue Level hinüberläuft. + window.ggsMusicResetToDefault = function() { + _manualMusicOverride = false; + _preAutoTrack = null; + if (!sel.options.length) return; + const defKey = sel.options[0].value; + if (sel.value !== defKey) switchTo(defKey); + }; btn.addEventListener('click', function() { if (audio.paused) {