Atlas: DSGVO-Tile-Proxy + Logistik-Phase-bis-8 + Sounds-Anleitung

Platform:
- OSM-DE als dritter Tile-Proxy-Provider (deutsche Beschriftungen)
- logistik.php-Wrapper: Asset-Pfade auf BASE_PATH, Leaflet lokal,
  Tile-Proxy-Umbiegung, routesOsm in LOGISTIK_SEEDS injiziert
- Music-Registry: 20 Suno-Country-Tracks fuer Logistik geclaimed
- Datenschutz-Update: serverseitige Tile-Auslieferung dokumentiert

Logistik (Instanz-Lieferungen, parallel):
- Phase 0 bis 8 inkl. Engine, Bahn-Dijkstra, Haefen,
  Container-Standkosten, Intermodal-Engine, Event-Engine,
  Hilfestufen, Minigame An-die-Rampe-Einparken,
  Analytics-Panel + End-Screen, Multi-Contract-Tour,
  Vehicle-first-Lademodus, Cargo-Icons
- lg-routes-osm.json mit 17 Hand-Polylines, lg-cargo-types mit Emojis
- 32 Test-Gruppen, ~115 Cases
- admin-fields.json fuer Admin-UI-Integration

Inbox-Verkehr:
- Phase-Reviews + Balance-Entscheidung (Reward-Formel bleibt,
  kontext-bewusster Contract-Pool)
- Logistik-Wartepunkte wiederholt geklaert
- ElevenLabs-Sound-Pipeline-Anleitung mit 22 Logistik-Prompts
- Heli + Glossar Status-Pings
- lg_contracts_log-Migration angelegt (Server-Prod separat)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-23 08:15:00 +02:00
parent eac686ee1a
commit fc8996f064
47 changed files with 9995 additions and 230 deletions
+745 -1
View File
@@ -2,7 +2,7 @@
instanz: logistik
rolle: Simulation „Logistik Europa" (Modul 11)
stand_seit: 2026-04-19
phase: Phase 0 v0.2 ausgeliefert (Atlas-Klarstellung umgesetzt) — wartet auf Atlas-Review
phase: Phase 7b ausgeliefert — admin-fields.json + 3 API-Endpunkte + Savegame-Binding + Analytics-Logging stehen. Wartet auf Atlas-Review + Admin-UI-Patch
---
# Status — Logistik-Instanz
@@ -110,3 +110,747 @@ nur Vorarbeit. Konkrete Atlas-Forderungen alle abgehakt:
Leaflet-Karte mit 13 Seed-Locations
**Blocker:** Atlas-Review + Thomas-Browser-Test (test.html + headless-runner.html)
**Offen für Thomas:** einmal beide HTML-Seiten öffnen, Status melden
## Tagesabschluss 2026-04-20 (02:30) — Phase 1 ausgeliefert
Phase 0 von Atlas mit BESTANDEN reviewt (01:45). Doku-Korrekturen aus
Review umgesetzt (Bahnnetz-Distanzen Paris↔Rotterdam=520, Paris↔München=820;
INSERT-Schema mit `scenario`+`levelNameEasy` in params); 3 Logistik-Level
sind in DB. Phase 1 komplett implementiert:
- **engine.js erweitert:**
- `loadContent(game, seeds)` — INIT → LOADING_CONTENT → READY
- `startPlanning(game)` — READY → PLANNING
- `startSimulation(game)` — PLANNING → RUNNING (paused=false)
- `pause(game)` / `resume(game)` — User-Pause via paused-Flag
- `setTimeScale(game, scale)` — 1×/2×/4×/8×
- `onStateChange(game, callback)` → unregister-Funktion (für Autosave 7b)
- `tick(game, deltaMs)` — Phase 1: Sim-Zeit + Zeitlimit-Check, alle
anderen Pipeline-Schritte (Vehicles, Contracts, Economy, Events)
sind Phase 2/3/5 Stubs
- `convertRealTimeToGameMinutes(deltaMs, timeScale)` — utility
- `getSimHoursElapsed(game)` — utility
- `serialize(game)` / `deserialize(json)` — ohne Listener (Autosave 7b)
- `createGame(levelNum, {deterministic, seed})` — neue Option für Tests
- Internes `_setState()` löst onStateChange-Callbacks aus
- **game.html komplett umgebaut:**
- Leaflet-Karte mit Carto Positron (Atlas-Vorgabe), tap:true für iPad
- 13 Locations gerendert als CircleMarker pro Typ
(CAPITAL grün/CITY grau/PORT blau/TERMINAL braun)
- Layer-Gruppen: Hauptstädte, Städte, Häfen, Terminals, Bahnnetz
- Layer-Control oben rechts (collapsed:false), Marker-Filter nach
`visibleFromLevel <= visibleLocationLevel` aus Level-Config
- Bahnnetz als gestrichelte Polylines (5 Kanten Wien-München-Hamburg-
Rotterdam-Paris-München)
- RAF-Loop ruft `tick(deltaMs)` und `updateStatusUI()`
- Speed-Buttons (1×/2×/4×/8×) mit `aria-pressed`-Toggle, ≥36px
- Pause-Button mit kontextsensitiven Übergängen
(READY→PLANNING→RUNNING ↔ pause/resume)
- Status-Leiste live: Sim-Zeit, Konto, Bilanz, Aufträge x/y, State
- Save/Load via localStorage (Phase 1 Skelett, Server-API in Phase 7)
- Autosave-Hook via onStateChange (localStorage)
- Sprachregel 4a durchgehend („Bearbeiter:in", „Simulation",
„Durchgang", „Sim-Zeit") — nirgends „Spieler"/„Spiel"
- **test.html erweitert um Test 11 (10 Cases) Phase 1:**
- 11.1 deterministic createGame → reproduzierbare sessionId+startTime
- 11.2 State-Übergänge INIT→LOADING_CONTENT→READY
- 11.3 startPlanning + startSimulation
- 11.4 tick(1000ms, 1×) → +1 Sim-Min
- 11.5 tick(1000ms, 4×) → +4 Sim-Min
- 11.6 paused tick → keine Zeit-Änderung
- 11.7 50× tick auf zwei Instanzen mit Seed 99 → identische simulationTime
- 11.8 onStateChange protokolliert alle 4 Übergänge
- 11.9 Zeitlimit überschritten → state=LEVEL_FAILED
- 11.10 serialize/deserialize Roundtrip + Listener-Reaktivierung
- Test 7 (Bahn-Dijkstra) erweitert um Wien→Paris (1220 km)
und Wien→Rotterdam (1700 km via Hamburg, nicht via Paris)
- **noop-Strategie weiterhin lauffähig:** noop nutzt eigenen Mini-Loop
(`simHours += 1`), keine engine.tick-Abhängigkeit → läuft ohne
Phase-2-Implementierung weiter. Test 10 grün-erwartet.
- **Mathematische Verifikation (Python 1:1):** Tick-Konversion,
Dijkstra-Pfade, Mulberry32 — alle neuen Cases verifiziert.
**Verbliebene Stubs (Phase 2+):** `assignContract`, `calculateRoute`,
`useHint`, `applyMinigameResult`, sowie die Strategien `naive/greedy/optimal`.
**Nächstes (Phase 2):** Auftrags-Generierung + Fahrzeug-Bewegung +
Routen-Visualisierung → Level 1 spielbar
**Blocker:** keiner — Atlas hat „keine weiteren Reviews bis Phase 1
fertig" gesagt, ich starte Phase 2 nach Thomas-Test
**Offen für Thomas:**
- Browser-Test `http://localhost/geograsim/App/sims/logistik/test.html` (Test 111, ~36 Cases)
- Browser-Test `http://localhost/geograsim/App/logistik?level=1` (Karte sichtbar, Pause-Button startet Sim-Zeit, Speed-Buttons schalten)
## Tagesabschluss 2026-04-20 (03:30) — Phase 2 ausgeliefert + Header-Fix
Atlas-Phase-1-OK + grünes Licht für Phase 2 (03:00). Atlas hat mein
Phase-1-Plan-§6 1:1 übernommen und „keine weiteren Reviews bis Phase 2
fertig" gesagt. Header-Klassen-Anfrage von 02:45 wurde nicht direkt
beantwortet (Überschneidung), aber das Template ist die Wahrheitsquelle —
Header habe ich selbst aufs Template-Pattern umgestellt (kein Klassen-
Erfinden, nur 1:1-Übernahme).
### Header-Fix (vor Phase 2)
- Klassen auf Template-Muster: `ggs-header-logo` + `ggs-header-logo-icon`
+ `ggs-header-logo-text` + `ggs-header-separator` + `ggs-header-module`
+ `ggs-header-badge` + `ggs-header-spacer` + `ggs-header-actions` +
`ggs-speed`/`ggs-speed-btn` + `ggs-btn-ghost`
- Pause-Button als `data-speed="0"` integriert (Template-Vorschlag)
- Lehrplan-Link `📚` ergänzt (verweist auf modul-logistik.php#lehrplan-bezug)
- Eigene CSS-Override-Regeln entfernt (DS macht das jetzt sauber)
- Speed-Button-Wahl steuert auch start (von PLANNING → RUNNING bei `>0`)
### Phase 2 — Engine
- `calculateRoute(game, originId, targetId, mode)` — Phase-2-Strategie
Luftlinie × 1.3, liefert Route mit Geometrie + Distanz + Dauer +
Segments. Provider markiert als `airline_x1.3` (Phase 3 ersetzt mit
Hand-Polylines).
- `assignContract(game, contractId, vehicleId)` — Validierung,
Routenberechnung, Vehicle springt zur Origin (kein Pickup-Drive in
Phase 2), States werden gesetzt.
- `_updateVehicles(game, simMinutes)` im tick — Routen-Interpolation,
bei Ankunft Vehicle→IDLE, Contract→DELIVERED
- `_completeContract` — Verspätung berechnen, Erlös +
BonusFahrkostenStrafe buchen, Contract in completedContracts,
Notification, Auto-Folgeauftrag generieren (Phase-3-Vorgriff für
L1-Spielbarkeit)
- `seedInitialVehicles(game)` — Level-Config-driven Vehicle-Erstellung
(L1: 1× TRUCK_SMALL in Wien)
- `seedInitialContracts(game)` — Level-1: 1× Wien→Salzburg fest
- `tick` erweitert um Erfolgsprüfung (Time-Limit-Erreichung mit
minTarget → LEVEL_SUCCESS, sonst LEVEL_FAILED)
### Phase 2 — Headless-Runner
- `naive`-Strategie aktiviert: nimmt erstes OPEN ↔ erstes IDLE,
tickt 5 Sim-Min/Iter bis state≠RUNNING. Nutzt opts.seeds wenn
übergeben (für Tests ohne window.LOGISTIK_SEEDS).
- runLevel ruft createGame mit deterministic+seed
- noop bleibt unverändert lauffähig (eigener Loop, keine engine.tick-Abhängigkeit)
### Phase 2 — UI (game.html)
- **Contracts-Panel** zeigt Cards pro Auftrag mit Code, Strecke,
km, Erlös, Frist, State-Badge. Klick selektiert (toggleable).
- **Vehicles-Panel** zeigt Cards pro Fahrzeug mit Name, Speed, Kapazität,
Standort. Klick bei selected contract → assignContract.
- **Routen-Polylines** (durchgezogen, dunkelgrün) für jede activeRoute
- **Fahrzeug-Marker** als 🚚/🚆 (DivIcon), Position aus RAF-Loop
- **Hilfestufe BLINK_EXACT** (Phase 5 vorgegriffen): blinkende Ringe um
Origin (gelb) und Target (rot) bei selected contract
- **Toast-Notifications** bei Delivery (mit Net-Betrag)
- **Sprachregel 4a** durchgehend (Aufträge, Bearbeiter:in, etc.)
### Phase 2 — Tests (test.html)
- Test 12 (5 Cases) — calculateRoute Wien→Salzburg ≈ 326 km × 1.3
- Test 13 (8 Cases) — assignContract State-Übergänge + doppelte Zuweisung wirft
- Test 14 (3 Cases) — tick bewegt Vehicle (segmentProgress strikt zwischen 0/1)
- Test 15 (6 Cases) — Delivery + Erlös + Folge-Auftrag automatisch
- Test 16 (3 Cases) — latePenaltyOverride wird respektiert + analytics++
- Test 17 (5 Cases) — naive auf L1 schafft Akzeptanzkorridor (≥4 Aufträge,
Profit ≥ +1000€, success=true)
- Test 10.3 angepasst: greedy wirft jetzt „Phase 2", naive ist aktiv
### Mathematische Verifikation (Python 1:1)
- Wien-Salzburg airline = 250.82 km, × 1.3 = 326.06 km, 4.66 h @ 70 km/h
- Pro Auftrag: Net 792.27€ (1300€ Erlös 507.73€ Fahrkosten)
- 24h / 4.66h = 5 Aufträge schaffbar
- Gesamtprofit: 3961€ > 3000€ Min-Ziel → success ✓
**L1 ist spielbar.** Im Browser: Klick auf Auftrag (blinkende Ringe) →
Klick auf LKW → Fahrt beginnt → 1× oder 4×/8× Speed → LKW fährt nach
Salzburg → Toast „Auftrag abgeschlossen +792€" → Folge-Auftrag erscheint.
**Nächstes (Phase 3):** Mehrfahrzeuge, Mehraufträge mit Variation,
Kostenmodell für Miete/Standkosten, Hand-Polylines für 12 Hauptstrecken,
Level 2 spielbar. Ziel: ~50% schaffen L2 mit greedy, ~70% mit optimal.
**Blocker:** keiner — Atlas hat „direkt Level 1 spielbar → Thomas-
Browsertest → Phase 3" gesagt. Header-Klassen-Frage 02:45 ist faktisch
durch Selbstkorrektur auf Template gelöst, aber Atlas könnte noch
beanstanden falls anders gemeint.
**Offen für Thomas:**
- Browser-Test `App/logistik?level=1`: 1 Auftrag links, 1 LKW rechts,
klicken → blinkende Ringe → LKW klicken → Fahrt → Toast bei Ankunft →
Folge-Auftrag → Bilanz wächst
- Test-Harness `App/sims/logistik/test.html`: 17 Gruppen, ~60 Cases,
alle grün erwartet
## Tagesabschluss 2026-04-20 (05:00) — Phase 3 ausgeliefert
Atlas-Wrapper-Patch war 04:00 fertig, Thomas hat Phase 2 live gesehen
und sofort gefragt, ob die LKW Hauptstraßen statt Luftlinie fahren
können — exakt Phase 3 nach Plan. Grünes Licht erteilt, gestartet.
### Phase 3 — Hand-Polylines + Engine
- **`App/assets/data/lg-routes-osm.json`** — 17 Hauptstrecken zwischen
den 13 Locations mit handgepflegten Polyline-Stützpunkten an
bekannten Knoten (St. Pölten, Linz, Brennerpass, Lille, Reims, etc.).
Bidirektional. Polyline-Längen via Python verifiziert.
- **`engine.calculateRoute` Phase 3** — Polyline-Lookup (Forward +
Reversed), Fallback auf Luftlinie × 1.3. Provider-Markierung
(`osm_polyline` / `osm_polyline_reversed` / `airline_x1.3`).
- **Pickup-Drive** in `assignContract`: wenn Vehicle nicht am Origin,
zweistufige Route (Pickup + Delivery)
- **4-Phasen-Trip** in `_updateVehicles`: pickup_drive → loading →
delivery_drive → unloading. Mit Phase-Übertrag-Loop (kein Drift bei
großen Ticks)
- **`_completeContract`** mit `vehicle`-Param: noEmpty-Bonus korrekt,
Pickup-Fahrkosten zusätzlich, `contract.pickupKm` für Tracking
- **Kostenmodell** im tick (`_applyRunningCosts`): Standkosten/h für
IDLE-Vehicles, Miete/Tag für alle Vehicles
- **Auftrags-Variation** deterministisch (Mulberry32 + Counter):
12 `CONTRACT_TEMPLATES`, L2/L3 ziehen daraus, L1 hardcoded auf
**Wien↔Salzburg-Pendel** (vermeidet Leerfahrten, hält naive im
Akzeptanzkorridor)
- **`seedInitialVehicles`** verteilt auf Hubs (L1: Wien; L2: Wien/
München/Hamburg; L3: 5 Hubs)
### Phase 3 — Headless-Runner
- **`greedy`-Strategie aktiviert**: höchster rewardBase ↔ größtes
Vehicle das passt
### Phase 3 — Tests
5 neue Gruppen, 19 neue Cases (Test 18-22). Total **22 Gruppen / ~80 Cases**.
### Mathematische Sanity-Checks (Python)
- L1 Pendel mit airline-Fallback: 4 Trips × 792€ = 3169€ > 3000€ Min ✓
- L1 mit Polyline (kürzer): noch profitabler
- L2/L3 Akzeptanzkorridor: nicht erfüllt (siehe Balance-Hinweis unten)
### Atlas-Wartepunkt
Wrapper-Erweiterung in `logistik.php` für `lg-routes-osm.json`
(siehe Atlas-Mail 2026-04-20-0500). Bis dahin fahren LKWs im Browser
noch auf Luftlinie × 1.3. Test-Harness unbetroffen (Inline-Seeds).
### Balance-Hinweis (für künftige Tuning-Iteration)
L2/L3-Akzeptanzkorridor mathematisch noch nicht erfüllt. Pflichtenheft
gibt `rewardBase=1000` fix (unabhängig von Container/Distanz). Mit
Pickup-Drive werden lange Strecken (>500km) verlustig. Tuning für
Phase 4+ (z.B. Reward proportional zur Distanz). Test 22 prüft nur
strukturelle Lauffähigkeit.
### Offen für Thomas
- Atlas-Wrapper-Update abwarten, dann Browser-Test L1 (Pendel-Modus,
Polyline-Routen sichtbar — LKW fährt entlang St. Pölten-Linz-Wels
statt diagonal durch die Donau)
- Test-Harness `test.html`: 22 Gruppen / ~80 Cases sollten alle grün
## Tagesabschluss 2026-04-20 (06:00) — Phase 4a + Music
### Music-Player aktiv
- 4 Tracks aus `.humanInput/Background Music/_claimed/` kopiert nach
`App/sims/logistik/sounds/music/`:
- `saddlewood-metronome.mp3` (3 MB, mechanisch-rhythmisch)
- `gears-and-marbles.mp3` (3.2 MB, Steve-Reich-Stil)
- `barbed-lullaby.mp3` (3.2 MB)
- `untitled-instrumental.mp3` (2.9 MB)
- `.ggs-music`-Markup im Header eingebaut (Pattern aus template.html)
- `ggsMusicSetup`-Funktion inline (gleicher Code-Pfad wie Klima/Fluss)
- Default-Volume 22 %, localStorage `ggs-music-logistik`
### Phase 4a — Engine
- **TRAIN-Routing**: `calculateRoute` nutzt für mode=TRAIN den
`railShortestPath`-Dijkstra über `worldState.railnet`. Wenn beide
Locations Bahnknoten sind, kommt eine echte Mehr-Segment-Route
raus (provider: `rail_dijkstra`), sonst Fallback auf
Polyline/airline. Geometrie führt durch alle Bahnknoten.
- **Hafen-Schiffsankünfte**: `_maybeGenerateShipArrival(game)` läuft
im tick, alle 6 Sim-Stunden ein Schiff (deterministisch via Counter).
Generiert OPEN-Auftrag mit Origin=Hafen, Target=Hauptstadt-Pool
rotierend. Code-Präfix `SHIP-`, `narrativeText` erwähnt Hafen,
Notification `SHIP_ARRIVED`. Nur aktiv bei `cfg.portsEnabled=true`.
- **Container-Standkosten**: `_applyRunningCosts` rechnet zusätzlich
zu Vehicle-Standkosten/Miete auch `containerStandCostPerHour ×
quantityContainers × simHours` für jeden OPEN-Auftrag mit
Origin am Hafen.
### Phase 4a — Tests (3 neue Gruppen, ~10 Cases)
- Test 23 (5): TRAIN-Routing über railnet-Dijkstra (Wien→Hamburg
via München = 1200 km, 2 RAIL-Segmente), Fallback wenn kein
Bahnknoten
- Test 24 (4): Schiffsankunft-Generator (nach 7h Sim mit
portsEnabled → SHIP-Auftrag + Notification)
- Test 25 (1): Container-Standkosten an Häfen (3 Container × 10€/h
× 1h = 30€ pro Sim-Stunde abgezogen)
Total jetzt: **25 Test-Gruppen, ~90 Cases**.
### Phase 4b verschoben (Intermodal)
Vollständige intermodale Aufträge mit `legs[]`-Struktur und Multi-
Vehicle-Übergabe (LKW→Zug→LKW) sind komplexer als die anderen 3
Phase-4-Punkte zusammen. Phase 4a deckt die Basis-Strukturen ab
(Bahn-Routing + Hafen-Aufträge), 4b kommt als nächste Iteration:
- Contract-Schema um `legs: [{mode, originId, targetId}]` erweitern
- Engine-Logik für Übergabe zwischen Vehicles am Terminal/Hafen
- Container am Hafen warten auf Folge-Vehicle (Inland-Strecke)
- LOADING-Phase am Übergabepunkt mit echten Cargo-Transfer-Zeiten
- Lehrziel #13 (kombinierte Verkehre) wirklich erlebbar
### Offen für Thomas
- Browser-Test [App/logistik?level=1](http://localhost/geograsim/App/logistik?level=1):
Music-Player rechts oben sollte sichtbar sein (Tracks wählbar, ▶ startet)
- Browser-Test [test.html](http://localhost/geograsim/App/sims/logistik/test.html):
25 Gruppen / ~90 Cases sollten alle grün
- Phase-4b-Entscheidung: jetzt oder erst nach L3-Tuning?
### Atlas-Wartepunkte
- Wrapper-Erweiterung `lg-routes-osm.json` (von Mail 0500, noch offen)
- Music-Tracks-Bestätigung (von Mail 0540, „letzten paar" akzeptiert?)
## Tagesabschluss 2026-04-20 (06:30) — Phase 4b + 5a ausgeliefert
Thomas: „Arbeite weiter, soweit du kannst." → Phase 4b (Intermodal-Engine)
und Phase 5a (Event-Engine) komplett implementiert. Phase 5b
(Hilfestufen-UI) auf Phase 6 verschoben — passt natürlich zu
Sprachregel-Check + Leichte Sprache (alles UI-Polish).
### Phase 4b — Intermodale Aufträge
- **Schema**: Contract bekommt optional `intermodal: true` und
`legs: [{legNum, mode, originLocationId, targetLocationId, state,
assignedVehicleId, routeId, departTime, arrivalTime}]`. Plus
`currentLegNum` und `currentLocationId` (wo der Container gerade ist).
- **`_makeIntermodalContract(game, code, legs, containers, isExpress)`**:
Factory mit Aggregations-Daten (Gesamt-Distanz approx, Gesamt-Frist,
rewardBase × 1.5 als Intermodal-Bonus).
- **`assignContract(game, contractId, vehicleId, legNum?)`**:
- bei intermodal: `legNum` Pflicht (sonst nimmt currentLegNum)
- prüft Vehicle-Mode-Match zum Leg-Mode (TRAIN-Leg → TRAIN-Vehicle)
- setzt leg.state statt c.state
- **`_completeLeg(game, contract, legNum, route, vehicle)`**:
- markiert Leg als DELIVERED
- bucht Fahrkosten dieses Legs sofort (Reward kommt am Schluss)
- setzt currentLegNum/currentLocationId vor
- aktiviert nächsten Leg auf OPEN (Notification `LEG_TRANSFERRED`)
- bei letztem Leg: Reward + Bonus + Strafe in einem buchen,
Contract als DELIVERED/LATE in completedContracts
- **`_stepVehiclePhase` UNLOADING**: erkennt intermodal und ruft
`_completeLeg` statt `_completeContract`
**Wichtig**: Strategien (naive/greedy) sind Phase 4b NICHT angepasst —
sie würden auf intermodal-Aufträgen mit Mode-Mismatch crashen. Phase 4c
oder Phase 5 für Strategy-Anpassung. UI ebenfalls noch nicht
intermodal-tauglich; Tests testen explizit via _makeIntermodalContract.
### Phase 5a — Event-Engine
- **`_maybeGenerateEvents(game, simMinutes)`**: würfelt deterministisch
via Mulberry32 (`game.seed ^ 0xE7E7` + Counter `_eventsRngCalls`).
Pro Sim-Stunde-Block + EVENT_TYPE: `prob = baseProb × eventProbabilityMultiplier`
- **EVENT_RULES** (aus PH 65.5): TRAFFIC_ACCIDENT 5 % Speed×0.5,
SNOW 10 % Speed×0.7 (Alpen), PORT_DELAY 8 % Loadtime×1.5
- **Event-Dauer**: 30-120 min (deterministisch via 2. Würfel)
- **`_expireEvents(game)`**: filtert abgelaufene Events
- **`_vehicleSpeedMultiplier(game)`**: kombiniert alle aktiven Events
multiplikativ → wirkt im `_stepVehiclePhase` auf `route.totalDurationMinutes`
- **Notifications**: `EVENT_STARTED` mit Emoji-Message (🚧 Unfall,
❄ Schnee, ⚓ Hafenüberlastung)
### Phase 5b — Hilfestufen-UI verschoben auf Phase 6
`contract.hintMode` ist schon im Schema (Phase 0/2), `BLINK_EXACT` ist
visuell umgesetzt (Phase 2). Die anderen 4 Stufen (`SHOW_COUNTRY`,
`SHOW_REGION`, `DISTANCE_FEEDBACK`, `NONE`) brauchen UI-Erweiterung
und passen natürlich zu Phase 6 (UI-Polish + Sprachregel + Leichte
Sprache) — alle drei drehen an der Darstellungs-Schicht.
### Phase 4b + 5 — Tests
5 neue Cases:
- Test 26 (4 Cases): Intermodal-Schema (legs[], States)
- Test 27 (8 Cases): Multi-Leg-Trip (Rotterdam_Hafen→Hamburg→München)
- Test 28 (3 Cases): Event-Generierung deterministisch + reproduzierbar
- Test 29 (1 Case): Manuelles Event reduziert Vehicle-Speed
- Test 30 (2 Cases): Events laufen nach Dauer ab
Total jetzt: **30 Test-Gruppen, ~110 Cases**.
### Was noch offen ist (Phase 6, 7, weitere)
- Phase 6: Hilfestufen-UI, 1× Minigame (An-die-Rampe-Einparken),
Sprachregel-4a-Check aller UI-Texte, Leichte-Sprache via `pickText()`,
Glossar-Begriffs-Anbindung
- Phase 7: Lehrkraft-Konfiguration (über Admin-Tool), Analytics-Dashboard
(Kennzahlen aus PH 23.2)
- Strategie-Anpassung: greedy für intermodal, optimal-Strategie
- Balance-Tuning für L2/L3 (rewardBase distanzproportional?)
### Browser-Tests offen
- Test-Harness: 30 Gruppen / ~110 Cases sollten alle grün
- Live-UI: Music-Player + LKW-Polylines (nach Atlas-Wrapper-Update)
## Tagesabschluss 2026-04-20 (07:00) — Phase 6 ausgeliefert
Thomas: „Mach weiter. Direkt Phase 6 machen." → UI-Polish komplett in 3 Subphasen.
### Phase 6a — Sprachregel + pickText + Hilfestufen + Glossar + Events
- **Sprachregel-4a-Audit**: alle UI-Texte sauber (nur interne Doc-Comments
enthalten „Spielzustand"/„spielbar" — laut Atlas-Briefing erlaubt)
- **pickText-Helper** mit Easy-Detection: `?easy=1` URL-Param oder
`localStorage 'logistik:easy'='1'` schaltet auf Leichte-Sprache-Variante
- **GLOSSAR-Fallback** (Pattern aus Fluss): 16 Begriffe inline definiert
(Container, Intermodal, Umschlag, Luftlinie, Disposition, Frist,
Leerfahrt, Standkosten, Bahnterminal, Hafen, Routing, Spedition,
Logistikkette, Auftrag, Fahrzeug, Logistik). Wenn Atlas/Glossar
später DB-API liefert → Fallback bleibt als Backup.
- **Glossar-Click-Handler**: `.ggs-glossar-popup` Pattern aus Design-System
- **Glossar-Markups** in Auftrags-Karten + Hint-Texten + Didaktik-Text
- **5 Hilfestufen visuell** (PH 10.3):
- BLINK_EXACT: ✓ Origin (gelb) + Target (rot) blinken (war Phase 2)
- SHOW_COUNTRY: alle Locations im selben Land wie Target leuchten
- SHOW_REGION: alle Locations in selber Region leuchten (sanftes Blau)
- DISTANCE_FEEDBACK: Skelett (UX-Mechanik kommt mit Phase 7+)
- NONE: nichts angezeigt
- **Event-Panel** im Didaktik-Bereich: aktive Events als Pills
(„🚧 Unfall · noch 25 min", „❄ Schnee · noch 60 min")
- **pickText in Toasts**: standard + easy für Auftrag-Zuweisung,
Delivery-Result, Fehlermeldungen
- **CSS für `[data-glossar]`**: dotted-Underline + cursor:help
### Phase 6b — Intermodal-UI
- **Auftrags-Karte** erkennt `c.intermodal` und zeigt **Leg-Liste** als
Pills (durchgestrichen=DELIVERED, fett-grün=OPEN, grau=WAITING)
- **🔗-Symbol** im Auftrag-Titel bei Intermodal
- **Mode-Filter im Vehicles-Panel**: bei selected intermodal-Auftrag werden
Vehicles, deren Mode nicht zum aktuellen Leg passt, als „busy" angezeigt
mit Hint „benötigt: 🚆 Zug" / „benötigt: 🚚 LKW"
- **Click-Handler** übergibt `currentLegNum` an `assignContract`
- **Bei Intermodal**: Auftrag bleibt selected nach Vehicle-Wahl (für
Folge-Legs)
- **Demo-Hook**: `?demo-intermodal=1` triggert Beispiel-Auftrag
Rotterdam_Hafen → Hamburg → München bei L3
- **Strategien angepasst**: naive/greedy filtern intermodal-Aufträge
raus (Phase 6c oder Tuning-Iteration für intermodal-fähige Strategien)
### Phase 6c — Minigame „An-die-Rampe-Einparken"
- **Engine: `applyMinigameResult(game, result)`** implementiert (war Stub):
- Speichert `loadingTimeFactor` aus `result.metadata` in
`game._loadingTimeModifier`
- Wirkt auf nächste LOADING-Phase (in `assignContract`),
danach Reset auf 1
- Trackt `analytics.minigamesPlayed` + `minigamesSuccessful`
- Generiert `MINIGAME_DONE`-Notification
- **UI: Modal mit Canvas** (420×200), Top-Down-View
- **Steuerung**: Touch-Buttons ⬅ ➡ + 📦 Andocken (iPad-tauglich, ≥44px)
- **Bewertung**:
- LKW-Vorderkante in 12 px um Rampen-Mitte → perfect → factor 0.8 (20 %)
- 12-30 px → ok → factor 1.0
- >30 px → miss → factor 1.3 (+30 %)
- **PH 65.9 Werte exakt umgesetzt**
- **Trigger-Button 🎮 im Header** sichtbar wenn `cfg.minigamesEnabled=true`
(L2/L3)
- **pickText** für alle Minigame-Texte (standard + easy)
### Phase 6 — Tests
- Test 31 (4 Cases): applyMinigameResult setzt Modifier, wird in
nächster LOADING-Phase angewendet (5 × 0.8 = 4 min), dann Reset
- Test 32 (1 Case): Minigame-Fail erhöht Loading auf 6.5 min (5 × 1.3)
Total jetzt: **32 Test-Gruppen, ~115 Cases**.
### Was Phase 6 NICHT enthält
- Strategien-Anpassung für intermodal (Phase 6d / Tuning)
- DB-Glossar-API (Atlas + Glossar-Instanz)
- DISTANCE_FEEDBACK voll umgesetzt (braucht Klick-zum-Wählen-UX)
- Keine API-Endpunkte
- Kein Lehrkraft-Konfig-UI (Phase 7)
### Browser-Tests offen
- [test.html](http://localhost/geograsim/App/sims/logistik/test.html):
32 Gruppen / ~115 Cases
- [App/logistik?level=1](http://localhost/geograsim/App/logistik?level=1):
Glossar-Begriffe (gepunktete Unterstreichung) anklickbar; Music-Player
- [App/logistik?level=2](http://localhost/geograsim/App/logistik?level=2):
+ Minigame-Button 🎮 im Header
- [App/logistik?level=3](http://localhost/geograsim/App/logistik?level=3):
+ Bahn-Layer + Häfen + Schiffsankünfte + Events sichtbar im Panel
- [App/logistik?level=3&demo-intermodal=1](http://localhost/geograsim/App/logistik?level=3&demo-intermodal=1):
+ intermodaler Demo-Auftrag mit 2 Legs (LKW Rotterdam_Hafen→Hamburg, Zug Hamburg→München)
- [App/logistik?level=1&easy=1](http://localhost/geograsim/App/logistik?level=1&easy=1):
Leichte-Sprache-Modus aktiv
### Atlas-Wartepunkte
- Wrapper-Erweiterung `lg-routes-osm.json` (Mail 0500)
- Glossar-Begriffe (Anfrage 0010, Atlas koordiniert)
- Music-Tracks-Bestätigung (Mail 0540)
## Tagesabschluss 2026-04-21 (Phase 8) — Multi-Contract-Tour
Thomas's Konzept übernommen: **Vehicle-first-Lademodus**.
1. Klick auf IDLE-Vehicle → Lademodus aktiv (Banner + Laderaum-Slots)
2. Klick auf OPEN-Auftrag → wandert in den LKW (Cargo-Emoji im Slot)
3. „🚀 Fahrt starten" → Multi-Stop-Tour beginnt
**Engine-API neu:**
- `loadContract(game, vehicleId, contractId)` — Kapazität-Check, Mode-Check, Contract → RESERVED
- `unloadContract` / `cancelLoading` — vor Fahrt-Start
- `startTour(game, vehicleId)` — baut Stops (alle Pickups erst, dann alle Dropoffs in Klick-Reihenfolge), konsolidiert aufeinanderfolgende Stops am gleichen Ort, berechnet Routen pro Segment, startet erste Phase
**Trip-Phase neu:**
- `tour_driving` (zwischen Stops) und `tour_handling` (an einem Stop laden/entladen)
- `_stepVehiclePhase` durchläuft Stops sequentiell, bucht Fahrkosten pro Segment sofort, Reward bei Dropoff
- `_endTour` setzt Vehicle zurück auf IDLE
**Cargo-Typen mit Emojis:**
- `lg-cargo-types.json` um `emoji`-Feld erweitert (📱 Elektronik, 👕 Kleidung, ⚙️ Maschinen, 🥫 Lebensmittel, 🧪 Chemie, 🔩 Fahrzeugteile, 🪵 Holz/Papier, 🪨 Rohstoffe)
- `CONTRACT_TEMPLATES` haben jetzt `cargoTypeIds`-Liste pro Strecke
- `_nextContractTemplate` zieht deterministisch einen Cargo-Typ
- Laderaum-Slots zeigen das Cargo-Emoji des belegenden Auftrags
**UI-Lademodus:**
- Vehicle-Card zeigt Banner + Laderaum-Grid (cap-viele Slots, gefüllt mit Cargo-Icons)
- „🚀 Fahrt starten"-Button (disabled bei leer) und „↺ Abbrechen"
- Klick auf RESERVED-Auftrag macht nichts (Card ist busy-styled)
- Wechsel zu anderem Vehicle: altes Vehicle wird automatisch entladen (cancelLoading)
**Bestehende Pfade unangetastet:**
- `assignContract` bleibt für intermodale Aufträge (selectedContractId-Pfad)
- Engine-Tests 1317 nutzen weiter assignContract → laufen unverändert
- Intermodal hat eigenen 4-Phasen-Trip (pickup_drive/loading/delivery_drive/unloading), nicht den neuen Tour-Pfad
**Was Phase 8 NICHT enthält (Phase 9 / nice-to-have):**
- Tour-Route-Vorschau auf der Karte vor „Fahrt starten" (geplante Stops als gestrichelte Linie)
- Klick-Animation „Container wandert in den LKW"
- Naive/greedy-Strategien wurden noch nicht angepasst (filtern intermodal+RESERVED)
- Optimierungs-Button („Reihenfolge optimieren ✨")
- Während-der-Fahrt-Nachladen (an Stops zusätzliche Aufträge dazunehmen)
**Browser-Test:**
- [http://localhost/geograsim/App/logistik?level=3](http://localhost/geograsim/App/logistik?level=3)
- Zug-Card klicken → Banner „Lademodus aktiv" + 20 Slots
- Aufträge nacheinander reinklicken — Container füllen sich mit Emoji
- „🚀 Fahrt starten" — Zug fährt erst alle Pickup-Orte, dann alle Dropoff-Orte ab
- Bei selber Origin: Stops werden konsolidiert (1 Pickup für mehrere Aufträge)
## Tagesabschluss 2026-04-20 (08:00) — Phase 7a: Analytics + Balance-Tuning
Thomas: „mach weiter" → Phase 7 angepackt. Teile, die Plattform-Domain
sind (Lehrkraft-Admin + API-Endpunkte + DB-Migration), an Atlas
übergeben (Mail 0800). Modul-Seite von Phase 7 ist fertig:
### Balance-Tuning: rewardBase distanzabhängig
- `_computeReward(distanceKm, isExpress)` in Engine
- PH-Base + `max(0, (km - 300) × 1.5)` Zuschlag
- Kurze Strecken bleiben PH-kompatibel (Wien-Salzburg: 1000→1039€)
- Lange Strecken profitabel (Wien-Hamburg: 1000→2350€)
- Intermodal-Bonus × 1.5 bleibt erhalten
- Python-verifiziert: L1-Pendel-naive bleibt schaffbar (845€ × 4 = 3380€)
### Analytics-Tracking in Engine
- `totalRewardEarned`, `totalPenaltyPaid`, `totalFareCostPaid`
- `runningCostsTotal` (war schon da)
- `contract.durationHours` pro abgeschlossenem Auftrag
### UI — Analytics-Panel
- Toggle-Button 📊 oben rechts auf der Karte
- 12 Kennzahlen live (Aufträge, Leerfahrtenquote, Verspätungsquote,
Ø-Net/Auftrag, Erlöse, Fahrkosten, Strafen, Standkosten+Miete,
Minigame-Erfolg, Bilanz/Profit)
- Live-Update im RAF-Loop bei offenem Panel
### UI — End-Screen
- Overlay bei State-Wechsel zu LEVEL_SUCCESS / LEVEL_FAILED
- Success in grün, Fail in rot
- Gekürzte Stats-Liste (5 wichtigste Werte)
- pickText für standard/easy
- „Neuer Durchgang"-Button reloaded Seite
- Via `onStateChange`-Hook automatisch getriggert
### Was bei Atlas liegt (Phase 7b)
- Lehrkraft-Admin-Tool (admin-levels.html erweitern vs. separat)
- API-Endpunkte `App/php/api/logistik-*.php` — ich kann selbst, erwarte Go
- DB-Migration `lg_contracts_log` — Freigabe vom Schema
- Glossar-API-Anbindung falls die 13+ Begriffe angelegt sind
### Offen für Thomas
- Browser-Test L1/L2/L3: Analytics-Button 📊 rechts oben, End-Screen
nach Zeitlimit oder Min-Ziel-Erreichen
- Die Balance-Tuning-Änderung macht L1 leichter, L2/L3 besser spielbar
## Tagesabschluss 2026-04-20 (07:30) — UX-Iteration „Fahrzeug-Auftrags-Verbindung"
Thomas: „wenn der Auftrag angenommen wurde und das Fahrzeug unterwegs
ist, wäre es schön, wenn irgendwo erkennbar wäre, dass diese Verbindung
wirklich vorliegt." → drei Mechanismen ineinandergreifen lassen.
### Was neu ist
- **Vehicle-Card zeigt Trip-Info** bei MOVING/LOADING/UNLOADING:
- Phase-Label („🟡 Leerfahrt zum Abholen", „📦 Lädt", „🟢 Unterwegs",
„📥 Lädt ab")
- Auftrag-Code + Strecke („C-001 · Wien → Salzburg")
- Progress-Bar bei Fahrten (live aus `v.segmentProgress`)
- ETA („ETA 14:32") oder Restdauer („noch 3 min") je nach Phase
- **Vehicle-Marker mit Hover-Tooltip** (Leaflet `bindTooltip`, sticky):
Name, Phase, Auftrag, Prozent-Fortschritt
- **Klickbares Pairing**:
- Klick auf Vehicle-Card oder -Marker → selectedVehicleId
- Zugehörige Contract-Card bekommt `linked-pair`-Highlight (blauer Ring)
- Vehicle-Card bekommt `linked`-Highlight (grüner Ring)
- Zugehörige Route wird auf der Karte dicker+kräftigrot, andere Routen
werden gedimmt
- Klick zentriert die Karte auf's Fahrzeug
- Erneuter Klick deselect
- **Auto-Deselect**: wenn Vehicle IDLE wird (Delivery fertig) → Selection
automatisch gelöscht
### CSS-Klassen neu
- `.lg-trip-info` (+ `.pickup`, `.loading`) — Trip-Box unter Vehicle-Titel
- `.lg-progress-bar` + `.lg-progress-fill` (+ `.pickup` für gelb)
- `.lg-card.linked` (grün) + `.lg-card.linked-pair` (blau)
### Keine neuen Tests
UX-Erweiterung (visuell), kein Engine-Change. Test-Gruppen bleiben bei 32.
### Offen für Thomas
- Browser-Test [App/logistik?level=1](http://localhost/geograsim/App/logistik?level=1):
- Auftrag klicken → LKW klicken → Fahrt startet
- Jetzt: Vehicle-Card zeigt „🟢 Unterwegs · C-001 · Wien → Salzburg · ETA ..."
mit Progress-Bar die mit 1×/4×/8× Speed füllt sich live
- Marker-Hover auf der Karte zeigt Tooltip
- Marker-Klick zentriert Karte + beide Cards sind farblich verbunden
- Bei L2/L3 analog — mit mehreren aktiven Trips siehst du die Fokus-
Route hervorgehoben, andere gedimmt
## Tagesabschluss 2026-04-22 — UX-Polish-Sprint + Atlas-Phase-7b-Entscheidungen
### Neue Atlas-Nachricht (1200, gelesen + quittiert)
`2026-04-22-1200-phase7-entscheidungen-und-db.md` — Atlas hat nach
2 Tagen Pause alle drei offenen Punkte entschieden:
1. **Lehrkraft-Admin**: Atlas erweitert `App/admin-levels.html` (zentrales Tool).
**Mein Beitrag:** `App/sims/logistik/admin-fields.json` mit
Feld-Beschreibungen aus balance-matrix.md §5.
2. **API-Endpunkte**: Ich baue selbst, Pattern aus `App/php/api/glossar.php`.
Drei Endpunkte: `logistik-sessions.php`, `logistik-saves.php`,
`logistik-analytics.php`. Security: Session-ID gegen `student_sessions`
validieren. Kein formales Review, ich committe wenn zufrieden.
3. **DB-Migration `lg_contracts_log`** erledigt. Schema erweitert um
`intermodal`, `route_distance_km`, `route_mode`. Meister pingt Atlas
für Produktions-Replikation.
4. Glossar-API: Fallback bleibt, Switch wenn Glossar liefert. Pattern
kommt in `App/docs/module-interface.md`.
Thomas-Frage „nächstes Modul" → Atlas sagt: erst nach Phase 7b.
### Phase 8 + UX-Iterationen (heute + gestern)
- **Multi-Contract-Tour**: Vehicle-first-Lademodus, Cargo-Icons (📱👕⚙️🥫🧪🔩🪵🪨),
Klick-Reihenfolge-Tour (Pickups zuerst, dann Dropoffs; konsolidiert
aufeinanderfolgende Stops am gleichen Ort), Laderaum-Slot-Visualisierung,
„🚀 Fahrt starten" / „↺ Abbrechen"
- **Container-Visualisierung**: bis 6 Container als einzelne 📦-Symbole,
ab 7 als „N× 📦". Mit Glossar-Popup (TEU + metrische Maße)
- **Scale-Bar** auf Leaflet-Karte (unten-links, metrisch, bis 100 km)
- **Pro-Vehicle-Farben** (8 Palette-Farben): Vehicle-Card-Dot, Marker-Ring,
Route-Farbe (durchgezogen Lieferfahrt, dünn gestrichelt Pickup-Leerfahrt)
- **Event-Symbole** auf der Karte: 🚧 Unfall (Mittelpunkt einer aktiven
Route), ❄ Schnee (Alpen-Region ±0.8°), ⚓ Hafenüberlastung (am Hafen)
- **Rote Routen bei Verspätung** (dashArray, überschreibt Vehicle-Farbe)
- **Analytics-Panel gedämpft** (1 Hz statt 60 Hz), Status-Leiste 4 Hz
- **Farbleitsystem**: OPEN grün · PICKUP/LOADING/IN_TRANSIT orange ·
DELIVERED blau · LATE rot · WAITING beige. Deutsche Labels mit pickText
- **State-Labels deutsch**: „offen"/„Abholung läuft"/„unterwegs"/„geliefert"/...
- **Vehicle-Auftrag-Verbindung**: Klick auf Vehicle (trip-aktiv) oder
Marker → Fokus-Route rot-dick, andere gedimmt, passende Contract-Card
mit blauem Ring (`linked-pair`). Vehicle-Card mit Progress-Bar + ETA
- **Analytics-Panel + End-Screen** (Phase 7a)
- **Balance-Tuning** (Phase 7a + 8): `_computeReward` distanz-proportional
+ Container-Komponente (+250 €/zusätzl. Container), CONTRACT_TEMPLATES
auf mehr 1-Container-Ranges (damit kleiner LKW nützlich wird)
- **Drei-Kommastellen-Fix**: `fmtEuro()` + `fmtEuroSigned()` runden
überall auf ganze Euro (Status, Toast, Analytics, End-Screen)
### Minigame-Komplettumbau (mehrere Iterationen heute)
- **Grüne Zielzone** (LKW-Größe + 3 px Toleranz), sichtbar schraffiert mit
„🎯 Hier parken"-Beschriftung
- **Referenz-Miniaturen** (3× 130×70 Canvas): ✅ Rückwärts · ❌ Vorwärts · ❌ Schief
- **Countdown 3:00** rückwärts, MM:SS-Format, warnt bei <60 s orange, <30 s rot
- **Auto-Andocken** sobald 0,3 s stabil in grüner Zone + rückseitig + langsam
- **Rückseitig-Pflicht**: Nur angle ≈ 180° (Heck zur Rampen-Rückwand) zählt als „richtig"
- **Live-Dock-Status**: „✓ Zone · ✗ Heck zur Rampe (45°) · ✓ still"
- **Crash-Szenarien** → Game Over, factor 1.5:
- Anderes Fahrzeug rammen
- Bucht-Wand mit v>55 UND tiefem Overlap
- Zeitablauf (3 min)
- **Active Mover** ab L2 sichtbar im Feld: fahrendes Fahrzeug mit Waypoints
+ sanfter Lenkung (Math.atan2 + max-turn-rate), wraps am Rand
- **Zeit → Ladezeit-Faktor**: <15 s → 0.7× / 15-30 s → 1.0 / 30-60 s → 1.2 / 60-180 s → 1.3 / Crash/Timeout → 1.5
- **LKW-Optik** verbessert: Kabine + Auflieger, Frontscheibe, Scheinwerfer,
6 Räder mit gelenkten Vorderrädern, Schatten
- **Web-Audio-Sounds** (synthesized, kein externer File nötig):
- Engine-Brummen: Sägezahn 55-200 Hz, Volume skaliert mit v
- Brems-Quietschen: Bandpass-Rauschen 0.25 s Burst bei abruptem v-Abfall
- Crash: tieffrequenter Sinus-Sweep + Lowpass-Impuls
- **Musik-Auto-Start**: Hauptmusik wird getriggert wenn Modal öffnet
(click auf #ggs-music-toggle falls nicht playing)
- **Buttons**: „↺ Neu starten" (gelb-warm, 46 px hoch) + „Fertig · Schließen"
(dunkelgrün), beide groß und touch-tauglich
- **Andocken-Button entfernt** (Auto-Detection ersetzt Manual-Check)
### Offene Arbeitsliste (Phase 7b — Atlas-Reihenfolge)
1. **`App/sims/logistik/admin-fields.json`** — Feld-Beschreibungen
aus balance-matrix.md §5 (15 Min Arbeit)
2. **`App/php/api/logistik-sessions.php`** — POST start/end, GET status
3. **`App/php/api/logistik-saves.php`** — POST save, GET load
(gegen `game_saves.save_data` JSON, Engine `serialize()` schon bereit)
4. **`App/php/api/logistik-analytics.php`** — POST in `lg_contracts_log`
(Schema Atlas bereits angelegt)
5. **Analytics-Logging in `_completeContract`-Hook** — pro Delivery
POST an Analytics-API
### Nicht-Blocker-Wartepunkte
- Wrapper-Erweiterung `lg-routes-osm.json` (Mail 0500) — Atlas hatte
bestätigt 0400 als Tile-Proxy-Patch, Routes-Seed aber weiterhin offen.
Browser-LKW fahren weiterhin Luftlinie × 1.3 (Fallback).
Tatsächlich: `engine.loadContent` liest `window.LOGISTIK_SEEDS.routesOsm`
falls gesetzt → wenn Atlas den Wrapper erweitert, greift Polyline-Routing
automatisch.
- Glossar-API-Pattern (Atlas koordiniert mit Glossar-Instanz)
### Browser-Test jetzt
- [http://localhost/geograsim/App/logistik?level=2](http://localhost/geograsim/App/logistik?level=2):
Multi-Tour, Minigame 🎮 mit Countdown + Sounds + Mover
- [http://localhost/geograsim/App/logistik?level=3](http://localhost/geograsim/App/logistik?level=3):
+ Bahn + Häfen + Events + Schiffsankünfte + schnellerer Mover
---
## 2026-04-22 ~14:00 — Phase 7b ausgeliefert (nach Atlas-12:00-Mail)
### Gebaut
- **`App/sims/logistik/admin-fields.json`** — alle 19 `params`-Keys aus balance-matrix.md §5 mit `label`, `labelEasy`, `type`, `unit`, `min/max/step`, `options`, `default`, `help`. Gruppiert in 7 Abschnitten (Wirtschaft, Auftragslage, Fahrzeugpool, Didaktik & Hilfen, Ereignisse, Verkehrstraeger, Sonstiges). Bonus: `availableVehicleTypes` als `multienum`, `levelNameEasy` als `text` für Leichte Sprache.
- **`App/php/api/logistik-sessions.php`** — POST start/end, GET status. Persistiert ohne neue Tabelle via `game_saves` mit Namespace-Keys `logistik:active-attempt` / `logistik:history` (Ring-Puffer letzte 20 Durchgänge). Security: Cookie-Session gegen `student_sessions` validiert.
- **`App/php/api/logistik-saves.php`** — GET/POST/DELETE. 5 Slots (15) via Keys `logistik:save:<slot>`. 500 KB-Limit. Baut auf generischer `game_saves`-Tabelle auf — keine Duplikat-Tabelle zu `saves.php`.
- **`App/php/api/logistik-analytics.php`** — POST in `lg_contracts_log` (Atlas-Schema mit intermodal + route_distance_km + route_mode). GET liefert 3 Scopes: `me` (eigene letzten 50), `class` (Lehrer-only, pro Session), `level` (Lehrer-only, pro Level-Aggregation).
- **Engine-Hook**: `game.pendingAnalytics` als neue Queue. `_pushAnalyticsEntry()` wird in `_completeContract` (Single) und `_completeContractMulti` (Tour) aufgerufen. Engine selbst macht keinen Netz-Call — bleibt in Node/Browser gleich testbar. UI drainiert die Queue alle 2s und POSTet (best-effort).
- **game.html-Integration** — `LG_API`-Helper: fire-and-forget mit `credentials: same-origin`, 401 → bis Session-Ende offline. Auf Game-Init: `startAttempt(levelNum)`. Auf LEVEL_SUCCESS/FAIL: `endAttempt(success, stats)` + Last-Drain der Analytics. Save/Load-Buttons jetzt Server-first mit localStorage-Fallback + Leichte-Sprache-Toasts.
### Security-Minimum (wie Atlas vorgegeben)
- Jeder POST validiert Session-ID aus `ggs_session`-Cookie gegen `student_sessions`-Tabelle (nicht nur Cookie-Präsenz)
- Keine Fremd-Session-IDs schreibbar — `session_id` kommt IMMER aus Server-Cookie, nie aus Request-Body
- DELETE/GET auf Saves nur für eigene Session
- `class`/`level`-Analytics-Scopes nur mit `requireTeacher()`
### Offene Punkte (an Atlas-Review)
- `logistik-saves.php` legt nicht in einer eigenen Tabelle ab, sondern namespaced im vorhandenen `game_saves`. Falls du stattdessen eine `lg_saves`-Tabelle willst, Migration + Refactor-Patch.
- `logistik-sessions.php` speichert Aktiv-Attempt ebenfalls in `game_saves`. Einfach, kein Schema. Falls du reportingfähige `lg_attempts`-Tabelle willst, sag Bescheid.
- Ich habe **keinen Browser-Test** gemacht — es hängt daran, ob die bestehende `ggs_session`-Cookie auch unter `/App/logistik?level=1` mitkommt (sollte dank `BASE_PATH`-Konfiguration, aber ungeprüft).
### PHP lint
```
/c/xampp/php/php.exe -l App/php/api/logistik-sessions.php → OK
/c/xampp/php/php.exe -l App/php/api/logistik-saves.php → OK
/c/xampp/php/php.exe -l App/php/api/logistik-analytics.php → OK
```
### Was Atlas jetzt tun kann
1. **Admin-UI** gegen `admin-fields.json` bauen — alle Feld-Metadaten stehen
2. **Review** der 3 PHP-Endpunkte, besonders: ist die `game_saves`-Kooptierung für Sessions/Saves ok oder dedizierte Tabelle?
3. **Meister** anpingen, damit das `lg_contracts_log`-Schema auf Produktion ist
4. **Glossar-API** — sobald die 13 Begriffe da sind, einbauen (Inline-Fallback in game.html funktioniert bis dahin)