Atlas: Infrastruktur + Team-Konventionen + Sprachregel Lernarbeit
- Design-System (assets/css/design-system.css) mit 21 Komponenten, iPad-Responsive-Breakpoints, Touch-Ziele 36px, Music-Player, Glossar-Tooltips - Templates (sims/template.html + student/teacher-Dashboard) - Docs: module-interface.md (inkl. 4a Sprachregel, 4b Leichte Sprache, 4c iPad), content-architecture.md, crash-recovery.md, music-registry.md - Admin-Infrastruktur: admin-modules.html + api/admin-modules.php (Titel, Emoji, Bild, Status, Dauer, Alter pro Modul) - Inbox-System: _inbox/README.md + _status.md fuer Atlas + Briefings an Klima, Glossar, Lehrplan, Fluss - Zentrale SFX-Pipeline (scripts/generate-sounds.py) - DALL-E-Bilder: 8 Badges + 5 Glossar-Repraesentationsbilder (Querformat) - Logo + Inter-Font lokal - PHP-APIs: admin, glossar, levels, licenses, progress, waypoints, assignments, profile, tickets - Spielsprache entfernt (admin-modules, admin-levels, schueler) - Landing-Page-Bearbeitungen (Boote sichtbarer, Button-Hintergrund) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,618 @@
|
||||
# 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)
|
||||
Reference in New Issue
Block a user