- 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
2.9 KiB
von, an, datum, status, betrifft
| von | an | datum | status | betrifft |
|---|---|---|---|---|
| klima | atlas | 2026-04-18 22:45 | neu | Engine-Refactor — Schritt 1 + 2 durch, Schritt 3-5 in Übergabe |
Zwischenstand: Engine + 2D-Umstellung fertig
Fertig
Schritt 1 — App/sims/klima/engine.js
Headless-Engine als window.KlimaEngine — keine DOM-Aufrufe, keine
kwAudio, kein fetch. Enthält alle Regel-Konstanten (MEASURES,
DIFFICULTY, ACHIEVEMENTS, CITIZEN_EVENTS, FIRST_BUY_HINTS,
CO2_FLOOR, TEMP_FLOOR) und die komplette Spiellogik (Tick, Kauf/
Abriss, Klima-Physik, Bürger-Events, Achievements, Serialize/
Deserialize).
API:
const game = KlimaEngine.createGame(levelId, { levelConfig });
const result = KlimaEngine.tick(game);
// result = { newEvents, unlockedAchievements, citizenEvent, endReason, timelineSample }
KlimaEngine.buyMeasure(game, id, { resolveMeta, forceNegBalance });
KlimaEngine.applyCitizenChoice(game, eventId, choiceIdx, { resolveMeta });
KlimaEngine.serialize(game); KlimaEngine.deserialize(data);
Event-Emission läuft über game._pendingEvents / _pendingAchievements,
pro Aufruf gedraint — die View bekommt genau die Events dieses Aufrufs
zurück (für Toast, Sound, Overlay) und kann sie rendern, ohne eine
zweite state.events-Mutation zu triggern.
Schritt 2 — App/sims/klima/game-2d.html
Alle Regel-Konstanten sind jetzt Aliases auf KlimaEngine.*. Alle
Logik-Funktionen sind dünne UI-Wrapper:
stateist eine Engine-Instanz (let state = KlimaEngine.createGame(1))- View-Flags via Object.assign
simulateTick,buyMeasure,demolishMeasure,triggerCitizenDialog→ Engine-WrapperserializeState/deserializeStatedelegierenshowEventwurde geteilt inshowEventUI(nur Toast/Sound, für Engine-Events) undshowEvent(pusht + UI, für Legacy-Pfade wie "Budget zu niedrig" Toast und "Save geladen" Hinweis)resolveMeta: () => ({ pos: pickBuildPosition(id) })— Engine spreizt die Extra-Daten in das Instance-Objekt, sodass der Canvas-Draw-Code (inst.pos.x/y) unverändert läuft und alte Saves rückwärtskompatibel geladen werden
Lines game-2d.html: 4552 → 4037 (−515 durch entfernte Duplikate).
Sonstige 2D-Verbesserungen im selben Zug
- Toast-Viewport volle Canvas-Breite (10px Rand), kompakteres Padding
- Hintergrundmusik: Default 22 %, Auto-Start bei erster User-Geste
- Häuser mit Schornstein-Rauchfahne, abhängig von
renewablePower / powerDemand(100 % erneuerbar → kein Rauch) - Flughafen-Helikopter fliegt jetzt (statt zu parken): 26-34 s Zyklus pro Airport, mehrheitlich Flug + gelegentliches Pad-Parken
Offen: Schritt 3-5
In _inbox/klima/2026-04-18-2240-handover-engine-steps3-5.md liegt die
Übergabe-Notiz mit Detail-Plan für die nächste Session. Schritt 3 (3D V2)
ist der dicke Brocken und will eine eigene Session.
Bestätigen
- status: gelesen
- Keine Rückmeldung nötig — Thomas testet parallel, wenn etwas in 2D regrediert ist, meldet er es direkt