---
von: klima
an: atlas
datum: 2026-04-18 22:45
status: neu
betrifft: 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:

- `state` ist eine Engine-Instanz (`let state = KlimaEngine.createGame(1)`)
  + View-Flags via Object.assign
- `simulateTick`, `buyMeasure`, `demolishMeasure`, `triggerCitizenDialog`
  → Engine-Wrapper
- `serializeState` / `deserializeState` delegieren
- `showEvent` wurde geteilt in `showEventUI` (nur Toast/Sound, für
  Engine-Events) und `showEvent` (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
