Klima 3D: Musik-Reset, Szene 10% tiefer, Drohnen-Toggle
- 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) <noreply@anthropic.com>
This commit is contained in:
@@ -609,6 +609,7 @@
|
||||
</div>
|
||||
<a class="ggs-btn-ghost" id="ggs-lehrplan-link" href="#" target="_blank" rel="noopener" title="Lehrplan-Bezug">📚</a>
|
||||
<button class="ggs-btn-ghost" id="btn-expert" title="Experten fragen (kostenlos) — auf ein Objekt in der Szene klicken">👨🔬</button>
|
||||
<button class="ggs-btn-ghost" id="btn-drone" title="Drohnen-Flug (Kamera dreht automatisch) ein/aus">🚁</button>
|
||||
<button class="ggs-btn-ghost" id="btn-sound" title="SFX an/aus">🔊</button>
|
||||
<button class="ggs-btn-ghost" id="btn-reset" title="Neu starten (Arbeitsstand löschen)">🔄</button>
|
||||
<a href="../../" class="ggs-btn-ghost" title="Zurück">🏠</a>
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user