- engine.js als headless Single Source of Truth (KlimaEngine) - game-2d.html nutzt engine (-515 Zeilen Duplikation) - rising-pressure.mp3 als Drama-Slot, Auto-Switch bei kritischem State - state.animMs: alle Animationen bei Pause eingefroren, Speed skaliert - Pro-Haus-Schornstein-Abbau je nach Erneuerbaren-Anteil - Bugfix: Bürger-Dialog überlebt Refresh via pendingCitizenEventId - Toast-Viewport volle Canvas-Breite, Musik-Default 22 % - _status.md Konvention eingeführt, Inbox-Nachrichten erhalten
7.4 KiB
von, an, datum, status, betrifft
| von | an | datum | status | betrifft |
|---|---|---|---|---|
| klima (Session vom 18.04. abends) | klima (folgende Session) | 2026-04-18 22:40 | uebergabe | Engine-Refactor — Schritt 1 + 2 fertig, Schritt 3-5 offen |
Übergabe-Notiz: Klima-Engine-Refactor
Auftrag aus 2026-04-18-2145-auftrag-engine-und-3d-v2.md. Schritt 1 (Engine
extrahieren) und Schritt 2 (2D auf Engine umstellen) sind durch. Schritt 3
(3D V2) wurde bewusst noch NICHT angefangen, weil der 4-6h-Block mehr
Kontext braucht als in dieser Session verfügbar war.
Was fertig ist
Schritt 1 — App/sims/klima/engine.js
Neues, headless JS-Modul, ESM-frei (window.KlimaEngine). Enthält:
- Konstanten:
MEASURES,DIFFICULTY,ACHIEVEMENTS,CITIZEN_EVENTS,FIRST_BUY_HINTS,CO2_FLOOR,TEMP_FLOOR— genau 1:1 aus der alten 2D-Version übernommen. - Lebenszyklus:
createGame(levelId, { maxTick, startYear, levelConfig }),serialize(game),deserialize(data). - Simulation:
recalcEffects,computeClimate,computeTemperatureFromCO2,getEmissionsThisYear,getPowerDemand,getIncomeThisYear,getYearlyBalance,computePopulationGrowth. - Aktionen:
buyMeasure(game, id, { resolveMeta, forceNegBalance }),demolishMeasure(game, id).buyMeasureliefert beireason: 'negative-balance'eine Signal-Struktur — die View zeigtconfirm(), und ruftbuyMeasureerneut mitforceNegBalance: trueauf. - Tick:
tick(game)— simuliert ein Jahr, gibt{ newEvents, unlockedAchievements, citizenEvent, endReason, timelineSample }zurück.endReason ∈ {null, 'won', 'pleite', 'ueberflutet'}. - Bürger-Events:
maybeCitizenEvent,applyCitizenChoice,getCitizenEvent. Choices sindapply(game, ctx)-Funktionen,ctx = { resolveMeta, pushEvent }. - Achievements:
tryUnlockAchievement(game, key)— gibt Achievement-Objekt zurück, wenn frisch freigeschaltet, sonst null.checkRenewable100(game)wird intern inbuyMeasureundtickaufgerufen.
Wichtig: Engine-Events puffern sich in game._pendingEvents und
game._pendingAchievements; _beginEvents / _drainEvents werden in jeder
mutierenden Aktion aufgerufen, sodass der Return nur die DIESE-Aktion-Events
enthält.
Schritt 2 — App/sims/klima/game-2d.html
Alle zuvor inline definierten Regel-Konstanten durch const MEASURES = KlimaEngine.MEASURES; etc. ersetzt. Alle Logik-Funktionen sind jetzt dünne
Wrapper:
stateist jetztlet state = KlimaEngine.createGame(1)+Object.assign(state, { phase, speed, MS_PER_TICK, tutorial*, pendingCitizenEvent }).startGame(lvl)ersetztstatedurch eine frische Engine-Instanz (dankletgreifen alle View-Funktionen auf die neue Instanz zu).simulateTick()ruftKlimaEngine.tick(state)auf und reicht das Resultat anconsumeEngineEvents,consumeEngineAchievements,triggerCitizenDialog,endLevelweiter.buyMeasure(id)/demolishMeasure(id)sind UI-Wrapper um die Engine; negative Bilanz wird weiterhin als confirm()-Dialog angezeigt.showEventwurde inshowEventUI(ev)(reines UI, für Engine-Events) undshowEvent(icon,text,type,infoKey)(Legacy-Einstieg, pusht + UI) aufgespalten, um doppelte Event-Stapel-Einträge zu vermeiden.unlockAchievement(key)nutztKlimaEngine.tryUnlockAchievement,notifyAchievementUI(a)macht Toast + Sound + Server-POST.serializeState/deserializeStatedelegieren an die Engine und hängen View-Flags (phase, tutorial) an/lesen sie wieder aus.pickBuildPositionbleibt View-seitig.resolveMeta: () => ({ pos: pickBuildPosition(id) })spreizt die Position in das Instance-Objekt, damit der bestehende Canvas-Draw-Code (inst.pos.x/y) unverändert läuft.
Build-Positionen in ownedMeasures[id].instances[i].pos.{x,y} — das Format
der Saves ist rückwärtskompatibel (alte Saves der V2-2D laden ohne
Migration).
Weitere Fixes / Hausaufgaben, die im selben Zug reingefallen sind
.ggs-event-viewportvolle Canvas-Breite (10px Rand links/rechts), Toasts mit 8px/16px Innenabstand (flacher).- Hintergrundmusik: Default-Volume 22 %, Auto-Start bei erster User-Geste, wenn state.playing !== false.
- Schornstein + Rauchfahne bei Häusern (in
drawHouse): Rauch verblasst proportional zustate.renewablePower / powerDemand, volles 100 % erneuerbar → kein Rauch. - Helikopter in
drawAirportfliegt nach seed-basiertem Zyklus (ca. 26-34 s): 8 % Start, 64 % Flug, 8 % Anflug, 20 % Pad-Parken.
Gesendet: Start-Meldung
_inbox/zentrale/2026-04-18-2200-klima-engine-start.md ging an Atlas raus
("Starte jetzt Schritt 1"). Eine Fertig-Meldung für Schritt 1+2 sollte
raus, sobald die neue Session die 2D-Refactor-Regression-Tests durch hat
(Level starten, Maßnahme kaufen, Bürger-Event beantworten, Save/Load,
Level gewinnen/verlieren).
Was noch offen ist
Schritt 3 — App/sims/klima/game-3d.html (V2)
Der dickste Brocken. Neue Datei, analoges Pattern wie 2D V2:
<script src="engine.js"></script>+<script src="audio.js"></script><link rel="stylesheet" href="../../assets/css/design-system.css">- Header mit Speed-Control, 📚-Button, 🔊 Mute, 🔄 Reset, 🎵 Music-Widget
- Status-Panels links (Status, Bilanz), Action-Cards rechts, Graphen-Tabs, Event-Stapel wie in 2D
- Glossar-Tooltips via
data-glossar - Autosave localStorage-Key:
ggs-save-klima-3d-L{n} ggsMusicSetup([...])mit den 4 Klima-Tracks
Three.js per CDN:
<script src="https://unpkg.com/three@0.155.0/build/three.min.js"></script>
Szenen-Baustellen aus App/src/sims/sim-05-treibhaus-3d/*.ts als
Vorlage — NICHT importieren, sondern in Vanilla JS portieren.
Übernehmen:
- Scene + Camera + Lights + Insel-Geometrie
- 3D-Modelle der Maßnahmen (Windrad, Solar, Deich, Mangrove …)
- Drag-Orbit + Scroll-Zoom + Auto-Drohnen-Flug
- Raycaster für Platzierung, Placement-Ghost (grün/rot)
- Arrow-Keys für Rotation
resolveMeta liefert in 3D etwas wie { pos: {x,y,z}, rotation, zoneId } —
Engine ist dafür agnostisch (spreizt nur in die Instance).
Schritt 4 — App/pages/klima-3d.php
Analog zu klima-2d.php, lädt App/sims/klima/game-3d.html.
Level-Config kommt aus game_levels mit game_id='klima' (gleiche Quelle
wie 2D — 2D und 3D teilen sich die Admin-Einstellungen).
Schritt 5 — module_info / Lehrplan
Nachricht an _inbox/lehrplan/ schicken:
module_info-Eintragklimaum 2D/3D-Toggle erweiternmodul-klima.php-Detailseite: "▶ 2D spielen" / "▶ 3D spielen" Buttons
Regression-Tests (für neue Session, bevor Fertig-Meldung)
http://localhost/geograsim/App/pages/klima-2d.php?level=1aufrufen- Level-Select → "Lernen" wählen → Spiel startet mit Budget 600, Pop 6000
- Eine Windkraft kaufen → Toast,
🌬️ Windkraft-Pionier-Achievement - Kohlekraftwerk kaufen → First-Buy-Hint "Kohle liefert …"
- Einige Ticks laufen lassen → Timeline + Graphen füllen sich, Bürger-Event bei Tick 30 (Lina, Fischerin) → eine Option wählen
- Reload → Spielstand wird wiederhergestellt
- Reset → Speicher gelöscht
- Level 2/3 durchlaufen, Events + Achievements vergleichen
Bestätigen
- Diese Notiz steht als
status: uebergabein klima-Inbox - Wenn neue Session anfängt: Schritt 3 starten, vorher in dieser Inbox checken, ob Atlas oder Thomas noch was drangelegt haben
- Erwartete Kontext-Größe für Schritt 3 allein: vermutlich eine komplette Session (Three.js-Szenenaufbau + 12 Maßnahmen-Modelle + Platzierung)