1e51ef7def
- Konzept/, didaktik_geografie/, didaktik_simulation/, v2-modules/, v2-platform/ - 12 code-workspace-Files - STATUS-*.md - viele M/D/R-Änderungen an bereits getrackten Files - .gitignore verstärkt: **/.humaninput/, **/secret_keys.txt Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
619 lines
22 KiB
Markdown
619 lines
22 KiB
Markdown
# GeoGraSim — Module Interface Specification
|
||
|
||
Dieses Dokument definiert die Schnittstellen zwischen der Lernplattform und den einzelnen Simulationsmodulen. Jede Modul-Instanz (Claude Code Session) bekommt dieses Dokument als Kontext.
|
||
|
||
---
|
||
|
||
## 1. Architektur-Überblick
|
||
|
||
```
|
||
Lernplattform (PHP + MySQL) Simulationsmodul (HTML/JS/Canvas)
|
||
───────────────────────────── ─────────────────────────────────
|
||
Authentifizierung & Sessions → Spieler-Kontext (wer, welches Level)
|
||
Modul-Freigabe pro Klasse → Zugang ja/nein
|
||
Level-Konfiguration → Schwierigkeitsparameter
|
||
← Live-Fortschritt (während Spiel)
|
||
← Achievements (Badges, Sterne)
|
||
← Reflexionsantworten
|
||
← Abschluss-Assessment
|
||
Detail-View (Schüler) ← Modulspezifische Ergebnis-Daten
|
||
Detail-View (Lehrer) ← Aktionslog + Metriken
|
||
Summary-Kachel ← Standardisierte Zusammenfassung
|
||
```
|
||
|
||
---
|
||
|
||
## 2. Einbettung: PHP-Page pro Modul
|
||
|
||
Jedes Modul wird über eine PHP-Seite ausgeliefert: `App/pages/{modul-id}.php`
|
||
|
||
Die PHP-Seite:
|
||
1. Prüft die Session (Schüler oder Gast)
|
||
2. Lädt Level-Konfiguration aus der DB
|
||
3. Injiziert den Spieler-Kontext als JavaScript-Objekt
|
||
4. Lädt die HTML-Datei des Moduls (Template-Replacement oder direktes PHP)
|
||
|
||
### Spieler-Kontext (PHP → JS)
|
||
|
||
```html
|
||
<script>
|
||
window.__GGS__ = {
|
||
// Session
|
||
sessionId: '<?= $_SESSION["student_session"] ?? "" ?>',
|
||
studentId: <?= $_SESSION['student_id'] ?? 'null' ?>,
|
||
studentName: '<?= addslashes($_SESSION["student_name"] ?? "Gast") ?>',
|
||
classId: <?= $_SESSION['class_id'] ?? 'null' ?>,
|
||
|
||
// Modul
|
||
simId: 'klima', // eindeutige Modul-ID
|
||
simName: 'Klimawächter', // Anzeigename
|
||
level: 1, // aktuelles Level (1, 2, 3)
|
||
|
||
// Level-Konfiguration (aus DB: game_levels)
|
||
levelConfig: {
|
||
difficulty: 'easy',
|
||
timeLimit: 75, // Runden/Ticks
|
||
startBudget: 200,
|
||
eventFrequency: 0.3,
|
||
// ... modulspezifische Parameter
|
||
},
|
||
|
||
// Vorheriges Ergebnis (falls vorhanden)
|
||
previousScore: null, // oder {stars: 3, score: 72, ...}
|
||
|
||
// URLs
|
||
baseUrl: '/geograsim/App', // oder '' bei geograsim.at
|
||
apiUrl: '/geograsim/App/php/api'
|
||
};
|
||
</script>
|
||
```
|
||
|
||
---
|
||
|
||
## 3. API-Endpunkte (JS → PHP)
|
||
|
||
Alle API-Calls gehen an `{apiUrl}/{endpoint}.php` via `fetch()` mit JSON body.
|
||
|
||
### 3.1 Live-Fortschritt melden
|
||
|
||
```
|
||
POST /php/api/progress.php
|
||
{
|
||
"session_id": "uuid",
|
||
"sim_id": "klima",
|
||
"action": "update",
|
||
"data": {
|
||
"level": 1,
|
||
"phase": "playing", // "tutorial", "playing", "paused", "finished"
|
||
"tick": 34, // aktueller Spielfortschritt
|
||
"tick_max": 75, // maximale Ticks
|
||
"score_so_far": 45, // Zwischenstand (0–100)
|
||
"metrics": { // modulspezifische Metriken
|
||
"co2": 780,
|
||
"temperature": 1.1,
|
||
"budget": 145
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
**Wann aufrufen:** Alle 10 Ticks oder bei wichtigen Ereignissen. Nicht bei jedem Tick — das wäre zu viel Traffic.
|
||
|
||
### 3.2 Achievement melden
|
||
|
||
```
|
||
POST /php/api/progress.php
|
||
{
|
||
"session_id": "uuid",
|
||
"sim_id": "klima",
|
||
"action": "achievement",
|
||
"data": {
|
||
"level": 1,
|
||
"badge_key": "first_windpark", // eindeutiger Key
|
||
"badge_name": "Windkraft-Pionier", // Anzeigename
|
||
"badge_icon": "🌬️", // Emoji
|
||
"badge_desc": "Ersten Windpark gebaut",
|
||
"xp": 50 // XP-Belohnung
|
||
}
|
||
}
|
||
```
|
||
|
||
### 3.3 Reflexionsantwort speichern
|
||
|
||
```
|
||
POST /php/api/progress.php
|
||
{
|
||
"session_id": "uuid",
|
||
"sim_id": "klima",
|
||
"action": "reflection",
|
||
"data": {
|
||
"level": 1,
|
||
"question": "Was war deine wirksamste Entscheidung?",
|
||
"answer": "Windpark gebaut",
|
||
"answer_index": 0, // Index der gewählten Option
|
||
"options": ["Windpark gebaut", "Küstenschutz ausgebaut", "Verkehr reduziert", "Ich bin mir nicht sicher"]
|
||
}
|
||
}
|
||
```
|
||
|
||
### 3.4 Abschluss-Assessment
|
||
|
||
```
|
||
POST /php/api/progress.php
|
||
{
|
||
"session_id": "uuid",
|
||
"sim_id": "klima",
|
||
"action": "submit_assessment",
|
||
"data": {
|
||
"level": 1,
|
||
"stars": 3, // 1–5
|
||
"score": 72, // 0–100 (Gesamtbewertung)
|
||
"duration_ms": 324000, // Spielzeit in Millisekunden
|
||
"completed": true, // Level geschafft?
|
||
|
||
// Modulspezifische Ergebnisse
|
||
"results": {
|
||
"final_co2": 580,
|
||
"final_temperature": 1.3,
|
||
"final_budget": 42,
|
||
"final_flooded_pct": 12,
|
||
"renewable_pct": 78,
|
||
"measures_bought": 8,
|
||
"events_survived": 5
|
||
},
|
||
|
||
// Aktionslog (für Lehrer-Detailansicht)
|
||
"action_log": [
|
||
{"tick": 3, "action": "buy", "item": "windpark", "cost": 25},
|
||
{"tick": 5, "action": "buy", "item": "solar", "cost": 18},
|
||
{"tick": 8, "action": "buy", "item": "deich", "cost": 30},
|
||
{"tick": 12, "action": "event", "type": "sturmflut", "result": "survived"},
|
||
// ...
|
||
],
|
||
|
||
// Entscheidungseffizienz (automatisch berechenbar)
|
||
"efficiency": {
|
||
"score_per_action": 9.0, // score / anzahl_aktionen
|
||
"budget_efficiency": 0.71 // ergebnis / ausgegebenes_budget
|
||
},
|
||
|
||
// Badges verdient in diesem Level
|
||
"badges_earned": ["first_windpark", "coastal_defense"]
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 4. Summary-Format (für Klassenübersicht)
|
||
|
||
Jedes Modul liefert nach Abschluss eines Levels eine standardisierte Zusammenfassung. Die Plattform rendert daraus einheitliche Kacheln.
|
||
|
||
```json
|
||
{
|
||
"sim_id": "klima",
|
||
"sim_name": "Klimawächter",
|
||
"sim_icon": "🌍",
|
||
"level": 1,
|
||
"stars": 3,
|
||
"score": 72,
|
||
"completed": true,
|
||
"duration_formatted": "5:24",
|
||
"key_metric": {
|
||
"label": "Endtemperatur",
|
||
"value": "+1.3 °C",
|
||
"status": "warning"
|
||
},
|
||
"badges_count": 2
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 4a. Sprache — Lernarbeit statt Spielsprache (PFLICHT)
|
||
|
||
GeoGraSim ist kein Spiel, sondern eine **Simulations-Lernumgebung**.
|
||
Alle sichtbaren UI-Texte folgen dem Prinzip: **Lernarbeit**, nicht
|
||
Unterhaltung.
|
||
|
||
### Bildungstheoretischer Hintergrund
|
||
|
||
- **Wygotski**: Lernen als aktive Konstruktion in der Zone der nächsten
|
||
Entwicklung
|
||
- Sprache formt Haltung: "Spiel" → abschalten dürfen. "Arbeit" →
|
||
ernstgenommen werden.
|
||
- Gegenüber Eltern, Schulleitung, Lehrplan-Koordinator*innen:
|
||
"Simulation" ist verteidigungsfähig, "Spiel" nicht.
|
||
|
||
### Lexikon (verbindlich)
|
||
|
||
| NICHT verwenden | STATTDESSEN |
|
||
|-------------------------|---------------------------------------------|
|
||
| spielen, Spiel | arbeiten mit, Simulation, bearbeiten |
|
||
| Weiterspielen | Fortsetzen, weiterarbeiten |
|
||
| "Jetzt spielen" | "Simulation starten", "Los arbeiten" |
|
||
| Spielstand | Arbeitsstand, Fortschritt |
|
||
| Spieler*in | Lernende, Bearbeiter*in |
|
||
| Spielrunde | Durchgang, Arbeitsphase |
|
||
| Gameplay | Simulationsablauf, Arbeitsweise |
|
||
| "verloren", "Game Over" | "Ziel verfehlt", "Durchgang beendet" |
|
||
| Highscore | Beste Leistung, Bestwert |
|
||
| Level | OK (pädagogisch vertretbar), besser "Schwierigkeit" oder "Stufe" |
|
||
|
||
### Ausnahmen
|
||
|
||
- **Interne Variablen-/Tabellennamen** (`game.tick`, `gameState`,
|
||
`game_saves`, `game_levels`) sind weiterhin OK — nicht sichtbar,
|
||
keine pädagogische Wirkung.
|
||
- **Sichtbare UI-Texte**, **Marketing-Inhalte**, **Dokumentation für
|
||
Endnutzer** folgen strikt der Lexikon-Regel.
|
||
|
||
### Anwendung bei deinem Modul
|
||
|
||
- Prüfe bei jeder neuen UI-Zeile, bei jedem Overlay-Titel, bei jedem
|
||
Button-Text: Kommt "spielen" oder ein verwandtes Wort vor?
|
||
- Beim Übernehmen von V1-Texten: V1-Wortwahl korrigieren, nicht
|
||
1:1 kopieren.
|
||
- Tutorial-Karten, Achievements, Endscreen, Reflexionsfragen: besonders
|
||
sensibel, weil prominent sichtbar.
|
||
|
||
### Beispiele aus der Praxis
|
||
|
||
| Vorher (typisch V1) | Nachher (V2) |
|
||
|----------------------------------------|---------------------------------------|
|
||
| "Willkommen zum Klimaspiel!" | "Willkommen zur Klima-Simulation." |
|
||
| "Spiel starten" | "Simulation starten" |
|
||
| "Speichern & weiterspielen" | "Speichern & fortsetzen" |
|
||
| "Du hast gewonnen! 🏆" | "Ziel erreicht! 🏆" |
|
||
| "Game Over" | "Durchgang beendet" |
|
||
| "Dein Spielstand wird geladen ..." | "Dein Arbeitsstand wird geladen ..." |
|
||
| "Schwierigkeitsgrad wählen" | ok, oder "Stufe wählen" |
|
||
| "Dein Highscore" | "Deine beste Leistung" |
|
||
|
||
---
|
||
|
||
## 4b. Leichte Sprache (Barrierefreiheit, Inklusion)
|
||
|
||
Die Lehrperson kann pro Schüler*in in der Klassenliste den Flag
|
||
**"Leichte Sprache"** aktivieren. Wenn gesetzt, werden Texte im gesamten
|
||
System (Glossar-Tooltips, Beispiele, Modul-Beschreibungen, Info-Topics)
|
||
in vereinfachter Sprache ausgeliefert.
|
||
|
||
### Schüler-Flag in der DB
|
||
|
||
```sql
|
||
students.easy_language BOOLEAN NOT NULL DEFAULT 0
|
||
```
|
||
|
||
Setzen via:
|
||
```
|
||
POST /api/students {action: "update", studentId: N, easyLanguage: true}
|
||
```
|
||
|
||
### Was automatisch in Easy übersetzt wird (Backend-API)
|
||
|
||
| Tabelle | Normal-Spalte | Easy-Spalte |
|
||
|---|---|---|
|
||
| `glossar` | `short`, `text` | `short_easy`, `text_easy` |
|
||
| `glossar_examples` | `text` | `text_easy` |
|
||
| `module_info` | `short_desc`, `long_desc`, `learning_goals` | `short_desc_easy`, `long_desc_easy`, `learning_goals_easy` |
|
||
|
||
**Fallback-Logik:** Wenn die Easy-Spalte leer ist, liefert die API die
|
||
Normal-Version. So bricht nichts, wenn noch nicht alles übersetzt wurde.
|
||
|
||
### API-Verhalten
|
||
|
||
Die Glossar-API (`/api/glossar.php`) erkennt den aktiven Schüler automatisch
|
||
über die Session und liefert dann die Easy-Variante. Keine zusätzlichen
|
||
Parameter nötig.
|
||
|
||
Optional zum Testen / expliziten Überschreiben:
|
||
- `?easy=1` → erzwingt Easy
|
||
- `?easy=0` → erzwingt Normal
|
||
|
||
Response enthält ein Meta-Feld `_easy: true|false`, damit das Frontend
|
||
optional einen Hinweis "In leichter Sprache" einblenden kann.
|
||
|
||
### Wer schreibt die Easy-Versionen
|
||
|
||
- **Glossar-Instanz** schreibt `short_easy`, `text_easy`, `text_easy`
|
||
(Examples) für alle Glossar-Einträge
|
||
- **Lehrplan-Instanz** schreibt `*_easy` für `module_info`
|
||
- **Modul-Instanzen** schreiben eigene Event-Info-Topics direkt mit einer
|
||
Easy-Variante (wenn sie Info-Topics haben)
|
||
|
||
### Merkregeln für "Leichte Sprache"
|
||
|
||
- Kurze Sätze (max. ~10–12 Wörter)
|
||
- Häufige, einfache Wörter
|
||
- Aktiv statt Passiv
|
||
- Fremdwörter vermeiden oder erklären
|
||
- Zahlen als Ziffern, nicht ausgeschrieben
|
||
- Keine Metaphern, keine Ironie
|
||
- Hauptaussage an den Anfang
|
||
|
||
### Richtlinien als Inspiration
|
||
|
||
- Netzwerk Leichte Sprache: https://www.leichte-sprache.org/
|
||
- capito Leichte Sprache Standards (A1 / A2 / B1)
|
||
|
||
---
|
||
|
||
## 4c. Referenzgerät — iPad Landscape (1180×820)
|
||
|
||
**iPad ist unser Hauptreferenzgerät.** Schulen in Österreich und DACH setzen
|
||
überwiegend iPads ein. Jedes Modul muss auf iPad Landscape wunderbar funktionieren.
|
||
|
||
### Layout-Breiten (aus design-system.css, automatisch)
|
||
|
||
| Viewport | Links | Canvas | Rechts |
|
||
|---|---|---|---|
|
||
| Desktop ≥1200px | 240px | 1fr | 280px |
|
||
| **iPad Landscape 900–1199px** | **210px** | **1fr** | **250px** |
|
||
| iPad Portrait / Mobil <900px | einspaltig, Panels unter Canvas | | |
|
||
|
||
Die Breakpoints sind im Design-System gesetzt — Module müssen nichts dafür tun,
|
||
wenn sie `.ggs-sim-layout` verwenden.
|
||
|
||
### Pflichten für alle Module
|
||
|
||
**Touch-Ziele:** Alle Buttons, Cards und klickbaren Elemente mindestens
|
||
36×36px (idealerweise 40×40). Das ist in `.ggs-speed-btn`, `.ggs-btn-*`,
|
||
`.ggs-card` bereits so gesetzt. Eigene klickbare Elemente bitte auch so
|
||
dimensionieren.
|
||
|
||
**Kein Hover-Abhängigkeit:** Hover-Effekte sind schön, aber auf iPad gibt es
|
||
keinen Cursor. Jede Funktion, die per Hover erreichbar ist, muss auch per
|
||
Tap funktionieren. Hover ist Bonus, nicht Pflicht-UX. Im Design-System sind
|
||
Hover-Effekte schon in `@media (hover: hover)` gekapselt — Cards zeigen auf
|
||
Touch einen `:active`-Zustand statt klebendem Hover.
|
||
|
||
**Scrolling im Canvas:** Wenn das Canvas per Drag interagiert (3D-Orbit, Map-
|
||
Pan), muss verhindert werden, dass iPad Safari stattdessen die Page scrollt:
|
||
`touch-action: none` auf dem Canvas-Element setzen. Für Canvas-Zonen ohne
|
||
Drag-Interaktion nicht nötig.
|
||
|
||
**Kein Double-Tap-Zoom im Body:** Bereits im Design-System gesetzt
|
||
(`touch-action: manipulation` auf body). Kein Bedarf, das pro Modul zu
|
||
ändern — ausser du willst Pinch-to-zoom für ein spezielles Element
|
||
erlauben.
|
||
|
||
**Formular-Eingaben (Reflexion, Textfelder):** `font-size: 16px` mindestens,
|
||
sonst zoomt iPad Safari beim Fokus rein. Das Design-System setzt das für
|
||
Standardfelder bereits.
|
||
|
||
### Was noch zu testen ist
|
||
|
||
- Graph-Zoom per Tap: funktioniert
|
||
- Card-Tap: funktioniert, kein Hover-Kleber
|
||
- Speed-Buttons: gross genug
|
||
- Overlay-Schließen per Tap außerhalb: funktioniert
|
||
- Glossar-Tooltip per Tap öffnet, zweiter Tap außerhalb schließt: ✓
|
||
- Dropdown-Menüs (z.B. Country-Picker, falls im Header): Select statt
|
||
Custom-Dropdown, weil iPad native Selects perfekt rendert
|
||
|
||
---
|
||
|
||
## 5. Design-System
|
||
|
||
Alle Module binden `assets/css/design-system.css` ein. Diese Datei definiert:
|
||
|
||
### Farb-Variablen (Pflicht)
|
||
```css
|
||
--ggs-fjord /* Primärblau */
|
||
--ggs-moss /* Erfolg/Grün */
|
||
--ggs-sand /* Kosten/Neutral */
|
||
--ggs-coral /* Gefahr/Rot */
|
||
--ggs-orange /* Warnung */
|
||
```
|
||
|
||
### Layout-Zonen (verwende diese Klassen)
|
||
```
|
||
.ggs-header → App-Header (Logo + Textlogo + Separator + Modulname)
|
||
.ggs-sim-layout → 3-Spalten Grid (Standard: Canvas in der Mitte)
|
||
.ggs-sim-layout.fullscreen → Canvas randlos, Panels als Glass-Overlays darüber
|
||
.ggs-zone-status → Links: Parameter, Ziele, Badges
|
||
.ggs-zone-sim → Mitte: Canvas/3D/Karte
|
||
.ggs-zone-actions → Rechts: Maßnahmen/Werkzeuge/Missionen
|
||
.ggs-zone-graphs → Unten: Zeitverlaufs-Graphen (klickbar zum Vergrössern)
|
||
.ggs-event-viewport → In-Canvas Notifications (erscheinen gross, verschwinden)
|
||
.ggs-event-stack → Event-Stapel links unten (aufklappbar)
|
||
.ggs-loading → Ladebildschirm mit drehendem Logo
|
||
```
|
||
|
||
### UI-Komponenten (verwende diese Klassen)
|
||
```
|
||
.ggs-card → Auswahl-Card (Maßnahme, Werkzeug, Mission)
|
||
.ggs-param → Parameter-Anzeige mit Balken
|
||
.ggs-graph-card → Zeitverlaufs-Graph (SVG)
|
||
.ggs-event → Event-Pill im Feed
|
||
.ggs-speed → Geschwindigkeits-Buttons
|
||
.ggs-overlay → Modal/Overlay (Tutorial, Level-Ende, Endscreen)
|
||
.ggs-stars → Sterne-Bewertung
|
||
.ggs-toast → Achievement-Toast
|
||
.ggs-btn → Button (primary, secondary, ghost, success, danger)
|
||
.ggs-badge → Mini-Badge (cost, effect, protect, level-easy/medium/hard)
|
||
.ggs-phases → Phasen-Dots im Header
|
||
```
|
||
|
||
### Template-Datei
|
||
`App/sims/template.html` enthält alle Zonen mit Platzhalter-Inhalten und minimalen JS-Helfern:
|
||
- `showOverlay(id)` / `hideOverlay(id)`
|
||
- `showAchievement(icon, title, subtitle)` — Toast oben rechts
|
||
- `showEvent(icon, text, type)` — Notification im Canvas + Stapel (type: 'good'/'bad'/'neutral')
|
||
- `toggleEventStack()` — Event-Stapel auf/zuklappen
|
||
- `updateGraph(index, points, currentValue)` — Klick auf Graph vergrössert
|
||
- `updateParam(index, value, percent, trend)`
|
||
- `reportProgress(data)`
|
||
- `submitAssessment(results)`
|
||
- `submitReflection(level, question, answer)`
|
||
|
||
---
|
||
|
||
## 6. Modul-IDs
|
||
|
||
| ID | Name | Status |
|
||
|----|------|--------|
|
||
| `klima` | Klimawächter (2D + 3D) | Aktiv |
|
||
| `fluss` | Flussmanagement | Aktiv |
|
||
| `heli` | Heli-Navigation | Aktiv |
|
||
| `stadt` | Stadt & Raumplanung | In Entwicklung |
|
||
| `regenwald` | Regenwald-Expedition | Überarbeitung nötig |
|
||
|
||
---
|
||
|
||
## 7. Datenbank-Tabellen
|
||
|
||
### Gemeinsam (Plattform verwaltet)
|
||
- `teachers`, `classes`, `students` — Benutzerverwaltung
|
||
- `student_sessions` — Anonyme Sessions
|
||
- `game_levels` — Level-Konfiguration pro Modul × Schwierigkeit
|
||
- `player_progress` — XP, Level-Stand, Badges
|
||
- `assessment_answers` — Reflexionsantworten
|
||
- `class_modules` — Modul-Freigabe pro Klasse
|
||
- `licenses` — Lizenz-System
|
||
|
||
### Modulspezifisch (Modul verwaltet)
|
||
- `game_saves` — Spielstände (session_id + save_key)
|
||
- `assessments` — Detaillierte Ergebnisdaten (JSON-Felder)
|
||
- Module können eigene Tabellen anlegen (z.B. `geo_waypoints` für Heli)
|
||
|
||
---
|
||
|
||
## 7b. Persistenz-Pflicht: Autosave und Resume
|
||
|
||
**Kernprinzip:** Ein Browser-Reload (F5, Tab-Wechsel, Stromausfall, Schüler schliesst aus Versehen das Fenster) darf **niemals** zum Neustart des Spiels führen. Der Schüler muss dort weitermachen können, wo er aufgehört hat.
|
||
|
||
### Was das bedeutet
|
||
|
||
- Beim **Seitenaufruf / Reload** lädt das Modul den letzten gespeicherten Stand und setzt genau dort fort (gleiche Runde, gleiche Werte, gleiche platzierte Massnahmen, gleicher CO₂-Stand, etc.).
|
||
- Bei **Klick auf Reset** (und nur dann!) wird der Spielstand gelöscht und das Spiel beginnt von vorne.
|
||
- **Autosave** nach jeder wichtigen Aktion (Tick-Ende, Kauf einer Massnahme, Level-Übergang). Kein manuelles "Speichern"-Knopfdrücken nötig.
|
||
|
||
### Wie
|
||
|
||
Zwei Speicher, in dieser Reihenfolge:
|
||
|
||
1. **localStorage** (Primär): Sofortiges, synchrones Speichern im Browser. Funktioniert auch offline.
|
||
- Schlüssel-Format: `ggs-save-{modul-id}-{level}` (z.B. `ggs-save-klima-2`)
|
||
- Wert: JSON-Serialisierung des Spielzustands
|
||
2. **Server-Backup** (Sekundär, bei vorhandener Session): Zusätzlich per POST an `/api/saves.php` schicken, damit der Fortschritt auf anderen Geräten verfügbar ist. Fällt das Netz aus, ist localStorage der Fallback.
|
||
|
||
### Konkrete Implementierung
|
||
|
||
```javascript
|
||
// Speichern
|
||
function autoSave() {
|
||
const state = game.serialize(); // dein kompletter Spielzustand
|
||
const key = `ggs-save-klima-${game.level}`;
|
||
try { localStorage.setItem(key, JSON.stringify(state)); } catch {}
|
||
// Optional: Server-Spiegelung
|
||
if (window.__GGS__.sessionId) {
|
||
fetch(window.__GGS__.apiUrl + '/saves.php', {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({
|
||
sim_id: 'klima',
|
||
save_key: `level-${game.level}`,
|
||
save_data: state
|
||
})
|
||
}).catch(() => {});
|
||
}
|
||
}
|
||
|
||
// Laden beim Start
|
||
function loadOrInit() {
|
||
const key = `ggs-save-klima-${level}`;
|
||
const raw = localStorage.getItem(key);
|
||
if (raw) {
|
||
try {
|
||
game.deserialize(JSON.parse(raw));
|
||
showEvent('💾', 'Spielstand geladen — weiter bei Runde ' + game.tick, 'neutral');
|
||
return;
|
||
} catch {}
|
||
}
|
||
// Fallback: neues Spiel
|
||
game.init();
|
||
}
|
||
|
||
// Reset-Knopf
|
||
function resetGame() {
|
||
if (!confirm('Fortschritt wirklich löschen und neu starten?')) return;
|
||
localStorage.removeItem(`ggs-save-klima-${game.level}`);
|
||
// Auch Server-Save löschen (DELETE), falls Session vorhanden
|
||
game.init();
|
||
}
|
||
```
|
||
|
||
### Wann speichern?
|
||
|
||
- Nach jedem Tick/Runden-Ende (automatisch)
|
||
- Nach jedem Kauf/Aktion (automatisch)
|
||
- Nach jeder Reflexionsantwort (automatisch)
|
||
- Bei Phasenwechsel (Tutorial → Spiel, Level → Level)
|
||
- Vor `beforeunload` als Sicherheit (falls Tab geschlossen wird)
|
||
|
||
### Welche Daten gehören in den Save
|
||
|
||
Alles, was der Spieler getan hat oder was sich durch Spielverlauf geändert hat:
|
||
- Aktuelle Runde / Tick
|
||
- Aktuelles Level
|
||
- Alle Spielparameter (Budget, CO₂, Temperatur, etc.)
|
||
- Gekaufte / platzierte Massnahmen inkl. Position
|
||
- Absolvierte Achievements / Badges
|
||
- Tutorial-Fortschritt (welche Schritte sind durch)
|
||
- Event-Historie (falls didaktisch relevant)
|
||
|
||
### UI-Hinweise
|
||
|
||
- Beim Laden eines bestehenden Stands: kurze Info einblenden ("Spielstand geladen — weiter bei Runde 34")
|
||
- Reset-Button (🔄) sichtbar im Header, mit Bestätigungsdialog ("Fortschritt löschen?")
|
||
- Save-/Load-Buttons im Header sind optional (für explizites Speichern) — Autosave passiert ohnehin
|
||
|
||
### Warum das wichtig ist
|
||
|
||
Didaktisch: Schüler*innen arbeiten in 45-Minuten-Einheiten. Wird das Modul aus Zeitgründen unterbrochen, soll in der nächsten Stunde dort weitergespielt werden können. Ohne Persistenz wäre das Modul unterrichtstauglich nur bei 100%-Fertigstellung in einer Einheit — unrealistisch.
|
||
|
||
Technisch: Browser-Crashs, versehentliche Reloads, Netzausfälle — alles passiert. Ohne Autosave verliert der Schüler seine Arbeit und frustriert sich. Ein Tabubruch im Lernkontext.
|
||
|
||
---
|
||
|
||
## 8. Datei-Struktur pro Modul
|
||
|
||
```
|
||
App/sims/{modul-id}/
|
||
├── game.html ← Hauptdatei (oder Template für PHP-Injection)
|
||
├── start.html ← Optional: Mini-Game (iframe)
|
||
├── landing.html ← Optional: Mini-Game (iframe)
|
||
└── assets/ ← Modul-eigene Assets (Sounds, Bilder)
|
||
|
||
App/pages/
|
||
├── {modul-id}.php ← PHP-Wrapper (Session, DB, Kontext-Injection)
|
||
└── {modul-id}-game.php ← Optional: Wenn Hauptseite DB-Daten braucht
|
||
```
|
||
|
||
---
|
||
|
||
## 9. Checkliste für neue Module
|
||
|
||
- [ ] Design-System CSS eingebunden
|
||
- [ ] Einheitlicher Header mit Logo, Modulname, Level-Badge
|
||
- [ ] Layout nutzt `ggs-sim-layout` Zonen
|
||
- [ ] Action-Cards nutzen `ggs-card` Klasse
|
||
- [ ] Parameter nutzen `ggs-param` mit Farbstufen
|
||
- [ ] Graphen nutzen `ggs-graph-card` (mindestens 1)
|
||
- [ ] Event-Feed zeigt Spielereignisse
|
||
- [ ] Speed-Control vorhanden (falls zeitbasiert)
|
||
- [ ] Tutorial-Overlay beim ersten Start
|
||
- [ ] Between-Level Screen mit Sterne + Reflexionsfrage
|
||
- [ ] Endscreen mit Stats-Grid
|
||
- [ ] Achievement-Toasts bei Meilensteinen
|
||
- [ ] API: `reportProgress()` alle 10 Ticks
|
||
- [ ] API: `showAchievement()` + POST bei Badge
|
||
- [ ] API: `submitAssessment()` bei Level-Ende
|
||
- [ ] API: `submitReflection()` bei Level-Übergang
|
||
- [ ] PHP-Page erstellt mit `window.__GGS__` Kontext
|
||
- [ ] **Autosave nach jeder Aktion** (localStorage + optional Server)
|
||
- [ ] **Resume beim Reload** (lädt letzten Stand, kein Neustart)
|
||
- [ ] **Reset-Button** löscht Save und startet neu (mit Bestätigungsdialog)
|