dfb9e80664
- 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
76 lines
2.9 KiB
Markdown
76 lines
2.9 KiB
Markdown
---
|
||
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
|