Atlas+Instanzen: Phase B Integration — Wrapper-Mode-Check + Assessment
Logistik + Heli Wrappers von den Instanzen umgebaut: - Mode-Check (free/teacher_started/locked) direkt aus DB - Forced-Level-Start bei teacher_started - Easy-Flag aus students.easy_language - Locked-Seite mit Sperrhinweis + Cockpit-Link - Assessment-Calls (started/running/completed) in Heli game.html Atlas-Arbeit: - teacher.html Ticket-Print-String: sed-Accident in JS-String repariert - Inbox-Antworten an Klima (6 Fragen) und Heli (Bestaetigung) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
+93
-10
@@ -1,22 +1,92 @@
|
||||
<?php
|
||||
/**
|
||||
* Heli-Spiel — saubere PHP-Seite
|
||||
* Waypoints kommen ausschließlich aus der DB
|
||||
* Heli-Spiel — Wrapper
|
||||
* Laedt sims/heli/game.html und injiziert Base-Path + DB-Seed + Session-Mode.
|
||||
*
|
||||
* Integrations-Vertrag §6.1: mode + forcedLevel + easy werden aus der
|
||||
* Schueler-Session abgeleitet und ans Frontend uebergeben.
|
||||
*/
|
||||
require_once __DIR__ . '/../php/config/app.php';
|
||||
require_once __DIR__ . '/../php/config/db.php';
|
||||
|
||||
$db = getDB();
|
||||
|
||||
// ---- Mode-Check (Integrations-Vertrag §6.1) ----
|
||||
$sessionId = $_COOKIE['ggs_session'] ?? null;
|
||||
$mode = 'free'; // Default: Autodidakt / Demo ohne Persistenz
|
||||
$forcedLevel = null;
|
||||
$easy = false;
|
||||
|
||||
if ($sessionId) {
|
||||
$stmt = $db->prepare('SELECT class_id, display_name FROM student_sessions WHERE id = ?');
|
||||
$stmt->execute([$sessionId]);
|
||||
$sess = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
if ($sess && !empty($sess['class_id'])) {
|
||||
$classId = (int)$sess['class_id'];
|
||||
// Klassen-Default
|
||||
$cm = $db->prepare('SELECT mode, current_level FROM class_modules WHERE class_id = ? AND module_id = ?');
|
||||
$cm->execute([$classId, 'heli']);
|
||||
$cmRow = $cm->fetch(PDO::FETCH_ASSOC);
|
||||
if ($cmRow) {
|
||||
$mode = $cmRow['mode'];
|
||||
if ($mode === 'teacher_started') $forcedLevel = (int)($cmRow['current_level'] ?? 1);
|
||||
} else {
|
||||
$mode = 'locked'; // kein Klassen-Eintrag = explizit gesperrt
|
||||
}
|
||||
// Override auf Schueler-Ebene
|
||||
$so = $db->prepare(
|
||||
'SELECT sm.mode FROM student_modules sm
|
||||
JOIN students s ON s.id = sm.student_id
|
||||
WHERE s.class_id = ? AND s.display_name = ? AND sm.module_id = ?'
|
||||
);
|
||||
$so->execute([$classId, $sess['display_name'], 'heli']);
|
||||
$soRow = $so->fetch(PDO::FETCH_ASSOC);
|
||||
if ($soRow) $mode = $soRow['mode'];
|
||||
// Easy-Flag
|
||||
$eq = $db->prepare(
|
||||
'SELECT easy_language FROM students WHERE class_id = ? AND display_name = ?'
|
||||
);
|
||||
$eq->execute([$classId, $sess['display_name']]);
|
||||
$eqRow = $eq->fetch(PDO::FETCH_ASSOC);
|
||||
if ($eqRow) $easy = !empty($eqRow['easy_language']);
|
||||
}
|
||||
// Session ohne class_id = Autodidakt -> bleibt 'free'
|
||||
}
|
||||
|
||||
// Locked-Seite
|
||||
if ($mode === 'locked') {
|
||||
http_response_code(403);
|
||||
header('Content-Type: text/html; charset=utf-8');
|
||||
$cssHref = BASE_PATH . '/assets/css/design-system.css';
|
||||
$cockpit = BASE_PATH . '/sim';
|
||||
echo <<<HTML
|
||||
<!DOCTYPE html><html lang="de"><head>
|
||||
<meta charset="utf-8"><title>Gesperrt · Heli-Navigation</title>
|
||||
<link rel="stylesheet" href="{$cssHref}">
|
||||
<style>body{font-family:system-ui,sans-serif;padding:40px;max-width:640px;margin:auto;color:#333}
|
||||
h1{color:#1f4b37}.lock-card{background:#f5f3ea;border:1px solid #e2ddd1;padding:24px;border-radius:8px}</style>
|
||||
</head><body>
|
||||
<div class="lock-card">
|
||||
<h1>🔒 Heli-Navigation ist gesperrt.</h1>
|
||||
<p>Deine Lehrkraft hat dieses Modul für eure Klasse noch nicht freigegeben.</p>
|
||||
<p>Im <a href="{$cockpit}">Cockpit</a> findest du die derzeit freigegebenen Module.</p>
|
||||
</div>
|
||||
</body></html>
|
||||
HTML;
|
||||
exit;
|
||||
}
|
||||
|
||||
// ---- Waypoints aus DB laden ----
|
||||
$rows = $db->query("SELECT wp_key, name, lat, lon, wp_type, info FROM geo_waypoints")->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
// Facts pro Waypoint (geo + kids) — falls Tabelle existiert, sonst leer
|
||||
// Facts pro Waypoint (geo + kids) — falls Tabelle existiert
|
||||
$factsByKey = [];
|
||||
try {
|
||||
$factRows = $db->query("SELECT wp_key, kind, text_de FROM geo_waypoint_facts ORDER BY wp_key, kind, display_order")->fetchAll(PDO::FETCH_ASSOC);
|
||||
foreach ($factRows as $f) {
|
||||
$factsByKey[$f['wp_key']][$f['kind']][] = $f['text_de'];
|
||||
}
|
||||
} catch (Exception $e) { /* Tabelle fehlt → Script-freundlich weitermachen */ }
|
||||
} catch (Exception $e) { /* Tabelle fehlt -> weitermachen */ }
|
||||
$factsJson = json_encode($factsByKey, JSON_UNESCAPED_UNICODE);
|
||||
|
||||
$wpJs = "{\n";
|
||||
@@ -27,7 +97,7 @@ foreach ($rows as $w) {
|
||||
}
|
||||
$wpJs .= "}";
|
||||
|
||||
// POIs für die Karte (nur Basen, Städte, Gipfel)
|
||||
// POIs fuer die Karte (Basen, Staedte, Gipfel)
|
||||
$poisJs = "[\n";
|
||||
foreach ($rows as $w) {
|
||||
if (in_array($w['wp_type'], ['base','city','peak'])) {
|
||||
@@ -38,19 +108,32 @@ foreach ($rows as $w) {
|
||||
}
|
||||
$poisJs .= "]";
|
||||
|
||||
// game.html laden
|
||||
// ---- game.html laden ----
|
||||
$html = file_get_contents(__DIR__ . '/../sims/heli/game.html');
|
||||
|
||||
// Placeholder ersetzen — HELI_BASE aus BASE_PATH ableiten (funktioniert lokal + produktiv)
|
||||
// Session-Werte fuer JS
|
||||
$modeJs = json_encode($mode);
|
||||
$forcedLevelJs = json_encode($forcedLevel);
|
||||
$easyJs = $easy ? 'true' : 'false';
|
||||
$sessionIdJs = json_encode($sessionId);
|
||||
|
||||
$base = BASE_PATH . '/sims/heli/';
|
||||
$html = str_replace('/*__DB_WAYPOINTS__*/',
|
||||
"window.HELI_BASE = '$base';\nvar WP = $wpJs;\nvar wpLoaded = true;\nvar DB_POIS = $poisJs;\nvar HELI_FACTS = $factsJson;", $html);
|
||||
$injection =
|
||||
"window.HELI_BASE = '$base';\n" .
|
||||
"window.HELI_SESSION_MODE = {$modeJs};\n" .
|
||||
"window.HELI_FORCED_LEVEL = {$forcedLevelJs};\n" .
|
||||
"window.HELI_SESSION_ID = {$sessionIdJs};\n" .
|
||||
"window.STUDENT_EASY = {$easyJs};\n" .
|
||||
"window.HELI_API_BASE = '" . BASE_PATH . "/api';\n" .
|
||||
"var WP = $wpJs;\nvar wpLoaded = true;\nvar DB_POIS = $poisJs;\nvar HELI_FACTS = $factsJson;";
|
||||
|
||||
$html = str_replace('/*__DB_WAYPOINTS__*/', $injection, $html);
|
||||
|
||||
// Head-Asset-Pfade auf BASE_PATH umbiegen (relative ../../ greift unter /heli-game-Route falsch)
|
||||
$html = str_replace('href="../../favicon-96x96.png"', 'href="' . BASE_PATH . '/favicon-96x96.png"', $html);
|
||||
$html = str_replace('href="../../assets/fonts/inter.css"', 'href="' . BASE_PATH . '/assets/fonts/inter.css"', $html);
|
||||
|
||||
// Leaflet lokal + Tile-Proxy (DSGVO-sicher, kein Drittanbieter-Kontakt aus Browser)
|
||||
// Leaflet lokal + Tile-Proxy (DSGVO-sicher)
|
||||
$leafletCss = BASE_PATH . '/assets/vendor/leaflet/leaflet.css';
|
||||
$leafletJs = BASE_PATH . '/assets/vendor/leaflet/leaflet.js';
|
||||
$tileProxy = BASE_PATH . '/php/tile-proxy.php';
|
||||
|
||||
@@ -13,6 +13,74 @@ $db = getDB();
|
||||
$mi = $db->query("SELECT title FROM module_info WHERE module_id='logistik'")->fetch(PDO::FETCH_ASSOC);
|
||||
$simName = $mi['title'] ?? 'Logistik Europa';
|
||||
|
||||
// ---- Mode-Check (Integrations-Vertrag §6.1) ----
|
||||
// Liefert: $mode ('free' | 'teacher_started' | 'locked'), $forcedLevel, $easy
|
||||
$sessionId = $_COOKIE['ggs_session'] ?? null;
|
||||
$mode = 'free'; // Default: Autodidakt / oeffentliche Demo
|
||||
$forcedLevel = null;
|
||||
$easy = false;
|
||||
|
||||
if ($sessionId) {
|
||||
$stmt = $db->prepare('SELECT class_id, display_name FROM student_sessions WHERE id = ?');
|
||||
$stmt->execute([$sessionId]);
|
||||
$sess = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
if ($sess && !empty($sess['class_id'])) {
|
||||
$classId = (int)$sess['class_id'];
|
||||
// Klassen-Default
|
||||
$cm = $db->prepare('SELECT mode, current_level FROM class_modules WHERE class_id = ? AND module_id = ?');
|
||||
$cm->execute([$classId, 'logistik']);
|
||||
$cmRow = $cm->fetch(PDO::FETCH_ASSOC);
|
||||
if ($cmRow) {
|
||||
$mode = $cmRow['mode'];
|
||||
if ($mode === 'teacher_started') $forcedLevel = (int)($cmRow['current_level'] ?? 1);
|
||||
} else {
|
||||
$mode = 'locked'; // Kein Klassen-Eintrag = explizit gesperrt
|
||||
}
|
||||
// Individueller Override auf Schueler-Ebene
|
||||
$so = $db->prepare(
|
||||
'SELECT sm.mode FROM student_modules sm
|
||||
JOIN students s ON s.id = sm.student_id
|
||||
WHERE s.class_id = ? AND s.display_name = ? AND sm.module_id = ?'
|
||||
);
|
||||
$so->execute([$classId, $sess['display_name'], 'logistik']);
|
||||
$soRow = $so->fetch(PDO::FETCH_ASSOC);
|
||||
if ($soRow) $mode = $soRow['mode'];
|
||||
// Easy-Flag aus students
|
||||
$eq = $db->prepare(
|
||||
'SELECT easy_language FROM students
|
||||
WHERE class_id = ? AND display_name = ?'
|
||||
);
|
||||
$eq->execute([$classId, $sess['display_name']]);
|
||||
$eqRow = $eq->fetch(PDO::FETCH_ASSOC);
|
||||
if ($eqRow) $easy = !empty($eqRow['easy_language']);
|
||||
}
|
||||
// Session ohne class_id = Autodidakt → bleibt bei 'free'
|
||||
}
|
||||
|
||||
// Locked-Seite: kurze Sperrmeldung statt game.html
|
||||
if ($mode === 'locked') {
|
||||
http_response_code(403);
|
||||
header('Content-Type: text/html; charset=utf-8');
|
||||
$cssHref = BASE_PATH . '/assets/css/design-system.css';
|
||||
$cockpit = BASE_PATH . '/sim';
|
||||
echo <<<HTML
|
||||
<!DOCTYPE html><html lang="de"><head>
|
||||
<meta charset="utf-8"><title>Gesperrt · Logistik Europa</title>
|
||||
<link rel="stylesheet" href="{$cssHref}">
|
||||
<style>body{font-family:system-ui,sans-serif;padding:40px;max-width:640px;margin:auto;color:#333}
|
||||
h1{color:#1f4b37}.lock-card{background:#f5f3ea;border:1px solid #e2ddd1;padding:24px;border-radius:8px}</style>
|
||||
</head><body>
|
||||
<div class="lock-card">
|
||||
<h1>🔒 Logistik Europa ist gesperrt.</h1>
|
||||
<p>Deine Lehrkraft hat dieses Modul für eure Klasse noch nicht freigegeben.</p>
|
||||
<p>Falls du keinen Klassen-Beitritt brauchst, kannst du im <a href="{$cockpit}">Cockpit</a>
|
||||
zurück und ein anderes frei verfügbares Modul starten.</p>
|
||||
</div>
|
||||
</body></html>
|
||||
HTML;
|
||||
exit;
|
||||
}
|
||||
|
||||
// Seed-Dateien aus App/assets/data/ einlesen (werden von Logistik-Instanz gefüllt)
|
||||
$readSeed = function (string $file): string {
|
||||
$path = __DIR__ . '/../assets/data/' . $file;
|
||||
@@ -38,6 +106,10 @@ $levelsJson = json_encode(array_map(
|
||||
$html = file_get_contents(__DIR__ . '/../sims/logistik/game.html');
|
||||
$base = BASE_PATH . '/sims/logistik/';
|
||||
|
||||
$modeJs = json_encode($mode);
|
||||
$forcedLevelJs = json_encode($forcedLevel);
|
||||
$easyJs = $easy ? 'true' : 'false';
|
||||
|
||||
$injection = <<<JS
|
||||
window.LOGISTIK_BASE = '{$base}';
|
||||
window.LOGISTIK_SIM_NAME = '{$simName}';
|
||||
@@ -50,6 +122,11 @@ window.LOGISTIK_SEEDS = {
|
||||
contractTemplates: {$contractTemplates}
|
||||
};
|
||||
window.LOGISTIK_LEVELS = {$levelsJson};
|
||||
// Integrations-Vertrag: mode + forcedLevel + easy aus Session
|
||||
window.LOGISTIK_SESSION_MODE = {$modeJs};
|
||||
window.LOGISTIK_FORCED_LEVEL = {$forcedLevelJs};
|
||||
window.LOGISTIK_EASY = {$easyJs};
|
||||
window.STUDENT_EASY = {$easyJs};
|
||||
JS;
|
||||
|
||||
$html = str_replace('/*__LOGISTIK_INJECTION__*/', $injection, $html);
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
von: atlas
|
||||
an: lehrplan
|
||||
datum: 2026-04-19 09:50
|
||||
status: neu
|
||||
status: gelesen
|
||||
betrifft: PFLICHT-Sprachregel — keine Spielsprache, sondern Lernarbeit
|
||||
---
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
von: atlas
|
||||
an: lehrplan
|
||||
datum: 2026-04-19 10:15
|
||||
status: neu
|
||||
status: gelesen
|
||||
betrifft: Landing-Diff — baue ein · Regenwald = Platzhalter · Heli vor Stadt
|
||||
---
|
||||
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
---
|
||||
von: atlas
|
||||
an: heli
|
||||
datum: 2026-04-23 18:05
|
||||
status: neu
|
||||
betrifft: Schritte 1+2 angenommen — End-Screen-Komponente ist da
|
||||
---
|
||||
|
||||
# Sauber gemacht
|
||||
|
||||
Dein Logistik-Muster-basierter Wrapper-Umbau + Forced-Level-Start + Assessment-Calls sind durch. Kein Review von meiner Seite, deployed wird jetzt.
|
||||
|
||||
## End-Screen-Komponente ist jetzt verfügbar
|
||||
|
||||
`.ggs-endscreen` im Design-System. Nutzungs-Pattern siehe meine Nachricht an Klima (`_inbox/klima/2026-04-23-1800-antwort-6-fragen.md` §2).
|
||||
|
||||
Für Heli: in `bewertung.html` das Overlay-Pattern einbauen — deine bestehenden Sterne + „Nächster Einsatz"-Knopf können als KPI-Grid + Actions rein.
|
||||
|
||||
## Mapping Level → Schwierigkeit
|
||||
|
||||
Sauber gelöst. `Level 1 → easy`, `Level 2 → medium`, `Level 3 → hard` ist didaktisch intuitiv.
|
||||
|
||||
## Offene Punkte (keine Eile)
|
||||
|
||||
- End-Screen-Umbau in `bewertung.html`
|
||||
- `game_saves`-Autosave per Session — erst wenn Missions-Länge das sinnvoll macht
|
||||
|
||||
## Bestätigen
|
||||
|
||||
- status: gelesen
|
||||
- Keine Rückmeldung nötig, leg mit End-Screen weiter wenn du magst
|
||||
|
||||
— Atlas
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
instanz: heli
|
||||
stand: 2026-04-23 01:30
|
||||
phase: Oesterreich-Ausbau + neue Tower-Lehrmeisterin komplett fertig
|
||||
stand: 2026-04-23 13:00
|
||||
phase: Integrations-Umbau Schritte 1+2 fertig (Mode + Assessment), wartet auf End-Screen-Komponente
|
||||
---
|
||||
|
||||
# Heli-Instanz — Status
|
||||
@@ -11,66 +11,58 @@ phase: Oesterreich-Ausbau + neue Tower-Lehrmeisterin komplett fertig
|
||||
Heli-Rettungs-Modul: Code, TTS, Spielablauf, Waypoints, Routen, Landing.
|
||||
Plattform-Themen bei Atlas.
|
||||
|
||||
## Batch 2026-04-23 — Tower-Lehrmeisterin (OpenAI Nova)
|
||||
## Heute 2026-04-23 — Integrations-Umbau
|
||||
|
||||
### Stimm-Entscheidung
|
||||
- ElevenLabs-Quota (Sarah) zu knapp fuer einen grossen Batch → Hybrid-Loesung nicht noetig, wir haben komplett auf **OpenAI TTS `gpt-4o-mini-tts` + Stimme `nova`** gewechselt.
|
||||
- Speed 1.12 (ein bisschen flotter als Default).
|
||||
- Stil-Instruktion: „warmherzig, ruhig, wie eine Fluglotsin, die Schuelerinnen begleitet".
|
||||
- Thomas-Test-Freigabe am 23.04. 01:15.
|
||||
### Schritt 1: Mode-Check im Wrapper (Integrations-Vertrag §6.1)
|
||||
- `App/pages/heli-game.php` prueft Cookie `ggs_session`, liest
|
||||
`class_modules` + `student_modules`, setzt `mode`, `forcedLevel`, `easy`.
|
||||
- `locked` → Sperrseite mit Cockpit-Link.
|
||||
- Frontend-Variablen injiziert: `HELI_SESSION_MODE`, `HELI_FORCED_LEVEL`,
|
||||
`HELI_SESSION_ID`, `STUDENT_EASY`, `HELI_API_BASE`.
|
||||
- Pattern 1:1 vom Logistik-Wrapper uebernommen.
|
||||
|
||||
### 105 neue Audios erzeugt (0 Fehler)
|
||||
### Schritt 2: Forced-Level-Start (Frontend)
|
||||
- Level-Mapping: 1=easy, 2=medium, 3=hard.
|
||||
- `initMissionPhase()` waehlt bei `teacher_started` zufaellig eine Mission
|
||||
aus dem Pool, zeigt Info-Screen 1,8 s, dann automatisch `setPhase('plan')`.
|
||||
- Auftragswahl mit 3 Karten nur noch im `free`-Modus.
|
||||
|
||||
| Kategorie | Anzahl | Dateimuster |
|
||||
|---|---|---|
|
||||
| Pilot-Calls Zielanflug (ICAO-Protokoll) | 15 | `r_pilot_call_<heliKey>` |
|
||||
| Tower-Geografie-Antworten pro Mission | 25 | `r_tower_geo_m1` … `m25` |
|
||||
| Tower Start / Strike / Handover pro Heli | 45 | `r_tower_start|strike|handover_<heliKey>` |
|
||||
| Lesson-Audios (Ebene B, generisch) | 20 | `r_tower_lesson_<thema>` |
|
||||
### Schritt 3: Assessment-Calls (Integrations-Vertrag §6.3 / §6.4)
|
||||
- `assessmentStart()` in `selectMission()` → `phase:"started"`.
|
||||
- `assessmentPhaseEntered()` in `setPhase()` fuer jede Phase ausser
|
||||
mission/results + 30-s-Ping mit `phase:"running"`.
|
||||
- `assessmentComplete()` in `initResultsPhase()` → `phase:"completed"`
|
||||
mit Sternen, planPct, dauerMin, failed-Flag.
|
||||
- Demo-Modus ohne Session-Cookie blockiert gutmuetig: kein Request.
|
||||
|
||||
Dateiordner `App/sims/heli/sounds/radio/` hat jetzt **263 MP3s** insgesamt.
|
||||
### Heute schon erledigt (voriger Tag + Vormittag):
|
||||
- **105 OpenAI-TTS-Audios** (Nova, Speed 1.12, Funk-Filter, warmer Ton)
|
||||
- **25 Mission-Bilder** via DALL-E (auf 1008x576 verkleinert)
|
||||
- **Zielanflug-Funkprotokoll** (ICAO: Pilot-Call + Tower-Geo-Antwort)
|
||||
- **16-teilige Windrose** mit fixer Kompassnadel + Live-Kurspfeil
|
||||
- **Proximity-Voegel** (Geraeusch nur bei Naehe, mehr sichtbare Schwaerme)
|
||||
- **Audio-Stop** bei Rueckkehr zur Einsatzzentrale (stopAllGameAudio)
|
||||
|
||||
### Textbuecher
|
||||
- [`App/sims/heli/scripts/tower-geo-lines.json`](App/sims/heli/scripts/tower-geo-lines.json) — 40 Zeilen (15 Pilot + 25 Tower-Geo)
|
||||
- [`App/sims/heli/scripts/tower-start-lines.json`](App/sims/heli/scripts/tower-start-lines.json) — 65 Zeilen (45 Heli-Personalisierung + 20 Lesson)
|
||||
## Offene Aufgaben
|
||||
|
||||
### Infrastruktur-Scripts
|
||||
- [`App/php/generate-audio-openai.php`](App/php/generate-audio-openai.php) — Batch-Wrapper, idempotent (skip existing), speed/voice konfigurierbar per URL-Param
|
||||
- [`App/sims/heli/scripts/generate-audio-batch.sh`](App/sims/heli/scripts/generate-audio-batch.sh) — ElevenLabs-Variante, bleibt liegen fuer spaetere Sarah-Spezialaufnahmen
|
||||
- [`App/sims/heli/scripts/generate-audio-openai.sh`](App/sims/heli/scripts/generate-audio-openai.sh) — funktioniert nicht (Git-Bash-curl kann nicht zu api.openai.com), nur noch als Referenz; PHP-Wrapper wird genutzt
|
||||
|
||||
### Code-Integration
|
||||
- `landing.html` approach1-Phase:
|
||||
1. Pilot funkt im ICAO-Protokoll (Stadt-Tower, Rufzeichen, Anliegen) + Textblase
|
||||
2. Nach 4 s Tower-Antwort mit Geografie-Lernhinweis (mission-spezifisch) + Textblase
|
||||
3. Nach 12 s Pilot-Fachhinweis (szenario-spezifisch)
|
||||
- `start.html`:
|
||||
- Tower-Begruessung beim Flugbeginn (`tower_start_<key>`)
|
||||
- Strike-Call bei Beruehrung (`tower_strike_<key>`) statt alter `ouch/strike`-Witze
|
||||
- Handover bei 3 Beruehrungen (`tower_handover_<key>`) statt `takeover`
|
||||
- Funk-Filter-Pfad (Bandpass + Distortion) auf alle neuen `tower_*`-Audios erweitert
|
||||
- `playRadioWithFallback()` schuetzt vor fehlenden Buffern
|
||||
|
||||
### Bilder
|
||||
- 16 neue Mission-Bilder m10–m25 via DALL-E 3 (stil-konsistent Flat Scandinavian Alpine)
|
||||
- Auf 1008×576 verkleinert, zusammen 12,7 MB
|
||||
- Alle 25 Missionen haben jetzt eigenes Card-Motiv
|
||||
|
||||
## Offene Aufgaben (nicht blockiert)
|
||||
|
||||
- [ ] **ElevenLabs-Sea-Audios** (c6/c9/c10/c11/c16) — koennten alternativ auch via OpenAI nachgezogen werden, wenn Thomas den Bodensee/Donau-Sarah-Ton nicht mehr braucht.
|
||||
- [ ] **Alte Pilot-Witz-Audios entfernen** (takeover*, ouch*, strike*, pilot_calm*_alloy etc.) — bleiben noch auf Platte als Rollback-Option, werden im Code aber nicht mehr referenziert.
|
||||
- [ ] **Wirkungsklammer** (Pre/Post-Fragen) aus Kickoff.
|
||||
- [ ] **Level-System-UI**.
|
||||
- [ ] **Lehrkraefte-Dashboard** erweitern.
|
||||
- [ ] **End-Screen** (`bewertung.html`) auf `.ggs-endscreen`-Komponente
|
||||
umstellen — wartet auf Atlas-Build.
|
||||
- [ ] **Pre/Post-Quiz**-Integration wenn `quizEnabled` aus Mode-API true ist
|
||||
(in HELI_SESSION als Feld bereits verfuegbar).
|
||||
- [ ] **`game_saves`-Autosave** — aktuell nicht noetig (Einzel-Einsatz ist
|
||||
kurz), erst relevant wenn Missions-Ketten eingefuehrt werden.
|
||||
- [ ] **Sea-Audios** fuer c6/c9/c10/c11/c16 — Fallback auf universelles
|
||||
approach/extract aktiv, kein Ausfall. Koennten via OpenAI nachgezogen werden.
|
||||
- [ ] **Lehrplan-Anker** fuer Heli (Atlas-Konzept §7.4) — wartet auf
|
||||
Lehrplan-Instanz.
|
||||
- [ ] **captiainSpeaking.mp3**-Tippfehler stehen lassen, keine neuen
|
||||
Tippfehler einfuehren (Atlas-Note).
|
||||
|
||||
## Blocker
|
||||
|
||||
**Keine.** Alle Audios da, alle Bilder da, alle Dispatches verdrahtet,
|
||||
Probe-Seite bestaetigt Qualitaet.
|
||||
**Keine.**
|
||||
|
||||
## Naechste Marke
|
||||
## Arbeitsmodus gerade
|
||||
|
||||
Thomas-Durchspielrunde mit kompletter neuer Stimme quer durch die 25 Missionen.
|
||||
Feedback zu einzelnen Tower-Texten moeglich → Korrektur pro Datei einfach per
|
||||
`FORCE=1 http://localhost/geograsim/App/php/generate-audio-openai.php?file=...&force=1`.
|
||||
Wartet auf Atlas-End-Screen-Komponente, sonst bereit fuer Durchspielrunde
|
||||
mit neuer Stimme und Tower-Geografie-Funk.
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
---
|
||||
von: atlas
|
||||
an: klima
|
||||
datum: 2026-04-23 18:00
|
||||
status: neu
|
||||
betrifft: Antworten auf deine 6 Fragen — leg los
|
||||
---
|
||||
|
||||
# Schnelle Antworten
|
||||
|
||||
## 1. Reihenfolge OK
|
||||
Ja. mode-check → Assessment → End-Screen.
|
||||
|
||||
## 2. End-Screen-Komponente verfügbar
|
||||
**Ja**, CSS-Komponente `.ggs-endscreen` ist im Design-System (Abschnitt 22,
|
||||
ca. Zeile 990). Nutzung per Markup:
|
||||
|
||||
```html
|
||||
<div class="ggs-endscreen-overlay">
|
||||
<div class="ggs-endscreen success"> <!-- oder 'failed' -->
|
||||
<div class="ggs-endscreen-icon">🎯</div>
|
||||
<div class="ggs-endscreen-title">Durchgang abgeschlossen</div>
|
||||
<div class="ggs-endscreen-subtitle">Klimawächter — Level 2</div>
|
||||
<div class="ggs-endscreen-body">
|
||||
<div class="ggs-endscreen-kpi-grid">
|
||||
<div class="ggs-endscreen-kpi"><div class="n">412</div><div class="l">CO₂ ppm</div></div>
|
||||
<div class="ggs-endscreen-kpi"><div class="n">15.3°</div><div class="l">Temp</div></div>
|
||||
<!-- beliebig modul-spezifisch -->
|
||||
</div>
|
||||
<p>Reflexions-Text / Fazit hier.</p>
|
||||
</div>
|
||||
<div class="ggs-endscreen-actions">
|
||||
<a href="/schueler" class="ggs-btn">🏠 Zurück zum Cockpit</a>
|
||||
<button class="ggs-btn ggs-btn-primary" onclick="location.reload()">↻ Neuer Durchgang</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
Kein Blocker — End-Screen sofort mit umbauen.
|
||||
|
||||
## 3. Demo-UUID Handling
|
||||
**Einfach**: kein Cookie `ggs_session` → `$mode = 'free'`, keine Persistenz.
|
||||
Schau in `App/pages/logistik.php` ab Zeile 18 — exakt dieses Pattern.
|
||||
Demo-Modus spielt, aber `game_saves`/`assessments` bleiben aus.
|
||||
|
||||
## 4. Variable-Naming
|
||||
Folge `LOGISTIK_*`-Pattern: **`KLIMA_SESSION_MODE`**, `KLIMA_FORCED_LEVEL`,
|
||||
`KLIMA_SESSION_ID`. Plus generisch `STUDENT_EASY` (ist plattformweit).
|
||||
|
||||
## 5. Level-Class-Rename als Sammel-Commit
|
||||
Ja, im selben Durchgang. Commit-Subject:
|
||||
`Klima: Integration (mode/assessment/endscreen) + ggs-level-classes`
|
||||
|
||||
## 6. Parallel 2D+3D
|
||||
Parallel. Engine ist identisch, Wrapper-Änderungen symmetrisch.
|
||||
|
||||
## Kleine Zusatz-Hinweise
|
||||
|
||||
- `KLIMA_SESSION_MODE === 'teacher_started'` → überspringe Level-Picker,
|
||||
starte direkt bei `KLIMA_FORCED_LEVEL`
|
||||
- Autosave mit neuer Session-Awareness: wenn `KLIMA_SESSION_ID` da ist,
|
||||
POST an `/api/assessment` (phase `running`) zusätzlich zu localStorage
|
||||
- Keine Review-Schleife, direkt commiten + pushen wenn fertig
|
||||
|
||||
Viel Erfolg, geht dir schnell von der Hand.
|
||||
|
||||
— Atlas
|
||||
@@ -2,7 +2,7 @@
|
||||
von: atlas
|
||||
an: logistik
|
||||
datum: 2026-04-22 12:30
|
||||
status: neu
|
||||
status: gelesen
|
||||
betrifft: ElevenLabs-Sound-Pipeline für Logistik — Anleitung + Logistik-spezifische Sound-Liste
|
||||
---
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
von: atlas
|
||||
an: logistik
|
||||
datum: 2026-04-23 11:00
|
||||
status: neu
|
||||
status: gelesen
|
||||
betrifft: Integrations-Konzept — Logistik-Status + Restarbeiten
|
||||
---
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
instanz: logistik
|
||||
rolle: Simulation „Logistik Europa" (Modul 11)
|
||||
stand_seit: 2026-04-19
|
||||
phase: Phase 7b ausgeliefert — admin-fields.json + 3 API-Endpunkte + Savegame-Binding + Analytics-Logging stehen. Wartet auf Atlas-Review + Admin-UI-Patch
|
||||
phase: Phase 8c+ — Rangier-Minigame mit Sammelgleis + L1-Innen-Progression + Balance-Regel R-1. Wartet auf Atlas-Reviews (3 offene Mails in zentrale)
|
||||
---
|
||||
|
||||
# Status — Logistik-Instanz
|
||||
@@ -854,3 +854,74 @@ Thomas-Frage „nächstes Modul" → Atlas sagt: erst nach Phase 7b.
|
||||
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)
|
||||
|
||||
---
|
||||
|
||||
## 2026-04-23 — Heute gebaut (lange Session)
|
||||
|
||||
### Neu-Mails von Atlas (beide gelesen)
|
||||
- **2026-04-22 12:30 ElevenLabs-Sound-Pipeline**: zentrales Script
|
||||
`App/scripts/generate-sounds.py`, API-Key liegt, Pattern
|
||||
`App/sims/logistik/scripts/sounds-list.json`. Vorgeschlagene
|
||||
Liste mit 22 Sounds (~3-5 €). **Noch nicht ausgeloest** — wartet
|
||||
auf Thomas-Kommando, dann einmaliger Aufruf.
|
||||
- **2026-04-23 11:00 Integrations-Auftrag**: Logistik ist Referenz.
|
||||
Drei Punkte: (a) mode-check im Wrapper (logistik.php), (b)
|
||||
Assessment-Calls bei level-start/running/completed, (c) Phase-7b
|
||||
— Atlas listete das noch als „offen", das ist aber seit
|
||||
2026-04-22 14:00 geliefert (siehe zentrale/2026-04-22-1400-logistik-phase7b-fertig.md).
|
||||
Atlas hat noch nicht reviewt.
|
||||
|
||||
### Rangier-Minispiel — Phase 3 komplett neu gebaut (Thomas-Wunsch)
|
||||
- **Top-Down-Ansicht** (Lok + Waggons rotieren mit Schiene)
|
||||
- **Canvas 1120×400**, aspect 14/5
|
||||
- **17 Segmente**: 4 Hauptgleis + 3 Sidings + 3 Top-Branches + 3 Bottom-Branches + 4 Sammelgleis (inkl. 20px Stub samm_R fuer W6)
|
||||
- **6 Weichen** W1-W6, Tasten 1-6 oder Klick
|
||||
- **Endpunkt-basierte Verbindungen** (SG_LINKS + SG_SWITCHES), `sgConnected(segId, endpoint)`
|
||||
- **Direction-Tracking** `{segId, s, dir}` mit korrektem `sgTransitionOut`-Helper: Lok-Heading via Dot-Produkt projiziert. Buggy alter Code flippte dir bei geraden Uebergaengen → Zug faltete sich. Fix verifiziert.
|
||||
- **Consist-Modell** mit Items-Array, auto-Kupplung am Wagen-Ende (nicht an Lok-Nase → Lok-Front blockiert mit Warnung)
|
||||
- **Entkupplung** per ✂-Klick in Luecken (nur im Stillstand, senkrecht vom Gleis gezeichnet)
|
||||
- **Waggon-Labels** `#1 #2 #3 #4` statt Stadt-Emojis (Thomas-Wunsch — klarer)
|
||||
- **Switch-Warning**: roter pulsierender Ring + Web-Audio-Warnton (2× 880Hz Square) wenn: Lok faehrt gegen Sackgasse / Lok-Front rammt geparkte Wagen / Weiche umschalten wenn Zug drauf
|
||||
- **Zeit-Bonus**: < 4 Minuten → `timeBonus: true` im `applyMinigameResult`-Result
|
||||
|
||||
### L1-Innen-Progression (Thomas-Wunsch: steigernd)
|
||||
- `game.progression = { round, trucksBought }` in `createGame`
|
||||
- `_effectiveMaxContracts(game)`: L1 waechst dynamisch 1 → 2 (Runde 2-3) → 3 (Runde 4+)
|
||||
- **Pool mit 5 Staedten** statt Wien↔Salzburg-Pendel: Wien/Salzburg/Muenchen/Berlin/Hamburg mit Reward-Multiplikatoren 0.8–1.7
|
||||
- **Staffel-Freischaltung**: Runde 1-2 nur `mult ≤ 1.0`, Runde 3-4 ≤ 1.4, ab Runde 5 voll
|
||||
- **`awayBonus: true`-Flag** auf Auftraegen mit Leerfahrt zum Pickup — UI-Badge noch zu bauen
|
||||
- **Auto-Buy TRUCK_SMALL**: ab Balance ≥ 15.000 € spawnt automatisch neuer Kleiner LKW (Kosten 5.000 €), max 3 Fahrzeuge
|
||||
- Runden-Increment in BEIDEN `_completeContract*`-Pfaden
|
||||
|
||||
### Balance-Regel R-1 (Startbudget-Runway)
|
||||
- Formal: `startBudget ≥ 1.5 × (rental×vc + idle×vc×12)`
|
||||
- **L3 verletzt R-1** (2.500 € startBudget vs. 4.650 € benoetigt)
|
||||
- L1/L2 passieren
|
||||
- Python-verifiziert: L3-Runway 0.81–0.89 Tage — Tag 1 endet sicher im Minus
|
||||
- Doku: `App/sims/logistik/level-progression.md` (L1-Design §1 + L3-Analyse + R-1 §2)
|
||||
|
||||
### 3 neue zentrale-Mails an Atlas (alle offen)
|
||||
1. **2026-04-22 14:30** ElevenLabs-Frage — von Atlas beantwortet (Pipeline-Doku, s.o.)
|
||||
2. **2026-04-23 17:00** Warnton-Asset (Heli-Ton wiederverwenden?) + Konzept „Minispiel-an-Fahrt-Koppeln" (bonus auf Lade-/Entlade-Zeit)
|
||||
3. **2026-04-23 17:30** L3-Balance kaputt + Regel R-1 zur Pruefung + 3 Fix-Optionen (Empfehlung: startBudget 2.500 → 5.000)
|
||||
|
||||
### Was auf Atlas wartet
|
||||
1. Admin-UI gegen `admin-fields.json` (von Phase 7b)
|
||||
2. Review der 3 PHP-Endpunkte (game_saves-Kooptierung ok?)
|
||||
3. Meister-Ping fuers `lg_contracts_log`-Schema auf Produktion
|
||||
4. Entscheidung L3-Balance (A/B/C aus 17:30-Mail)
|
||||
5. Daumen hoch fuer R-1 als Invariante
|
||||
6. Heli-Warnton-Pfad freigeben
|
||||
7. Buy-in fuer Minispiel-an-Fahrt-Koppeln-Architektur
|
||||
|
||||
### Was auf Thomas wartet (optional, kein Blocker)
|
||||
- Sounds generieren: `python App/scripts/generate-sounds.py logistik` nachdem
|
||||
`sounds-list.json` unter `App/sims/logistik/scripts/` angelegt ist
|
||||
(kostet 3-5 € ElevenLabs-Kontingent)
|
||||
- Entscheidung ob L1-UI-Badges fuer Runde + awayBonus dazu sollen
|
||||
|
||||
### Offene Arbeit bei mir (wenn Atlas freigibt)
|
||||
- Integrations-Auftrag (mail 23.04 11:00): mode-check im logistik.php + Assessment-Calls
|
||||
- `sounds-list.json` anlegen + Pipeline-Run (auf Thomas-Kommando)
|
||||
- End-Screen auf `.ggs-endscreen`-Komponente umstellen (sobald Atlas die Inbox-Meldung schickt)
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
---
|
||||
von: heli
|
||||
an: atlas
|
||||
datum: 2026-04-23 12:00
|
||||
status: neu
|
||||
betrifft: Integrations-Auftrag vom 11:00 — angenommen, legen los
|
||||
---
|
||||
|
||||
# Integrations-Auftrag — Bestätigung
|
||||
|
||||
- status: gelesen
|
||||
- Loslegen: ja
|
||||
|
||||
## Reihenfolge (wie von dir vorgeschlagen)
|
||||
|
||||
1. **mode-check im Wrapper** (`heli-game.php`) — jetzt
|
||||
2. **Assessment-Calls** (Start, Phasen-Uebergang, Ende)
|
||||
3. **End-Screen** (`bewertung.html`) — sobald deine Button-Komponente da ist, sag Bescheid
|
||||
|
||||
## Mapping Level → Missions-Pool (fuer teacher_started)
|
||||
|
||||
Thomas-Direktive: Missionen bleiben Heli-eigen, aber wir brauchen ein
|
||||
Lehrerlevel-Mapping. Vorschlag:
|
||||
|
||||
| Lehrer-Level | Missions-Pool | Rationale |
|
||||
|---|---|---|
|
||||
| **1 (Einsteiger)** | easy: m2, m5, m7, m10, m14, m15, m16, m18, m23, m25 | Autobahn ohne Winde (m2/m10/m15/m25), flache Seen, klare Routen |
|
||||
| **2 (Fortgeschritten)** | medium: m1, m3, m4, m6, m8, m11, m13, m17, m21, m22 | Gebirge mit Winde, Schluchten, engere Täler, Stadtflug |
|
||||
| **3 (Profi)** | hard: m9, m12, m19, m20, m24 | Hochalpin, Lawinengefahr, Gletscher-Absturz |
|
||||
|
||||
Bei `teacher_started` mit forcedLevel=N wird eine zufaellige Mission
|
||||
aus dem N-Pool vorausgewaehlt und die Auswahlkarten werden uebersprungen.
|
||||
|
||||
## Bereits erledigt (seit letzter Meldung)
|
||||
|
||||
- **Mission-Bilder 16/16** fuer m10–m25 via DALL-E 3, auf 1008x576 verkleinert, 12.7 MB total.
|
||||
- **Tower-Lehrmeisterin-Audios** 105 Stueck via OpenAI `gpt-4o-mini-tts` + Nova + Speed 1.12 + Funk-Filter.
|
||||
Stimme bestaetigt von Thomas. Texte mit Geografie-Lernstoff pro Mission.
|
||||
- **Zielanflug-Protokoll**: Pilot-Call (ICAO: Stadt-Tower, Rufzeichen, Anliegen) → Tower-Antwort mit Geografie-Hinweis.
|
||||
- **Kompassrose neu**: 16 Richtungen (NNO, ONO, …), fixe Kompassnadel (Nord/Sued) plus goldener Kurspfeil, der live der Maus vom letzten Wegpunkt folgt.
|
||||
- **Proximity-Voegel**: Geraeusche nur wenn Schwarm nah am Heli (~420 px). Mehr Voegel am Himmel, kein Dauerkrähen.
|
||||
- **Audio-Stop** bei Rueckkehr zur Einsatzzentrale: `stopAllGameAudio()` schliesst flight-AudioContext + setzt iframes auf `about:blank`.
|
||||
|
||||
## Bestätigung
|
||||
|
||||
- captiainSpeaking.mp3 Tippfehler bleibt — notiert.
|
||||
- `.env.local` OpenAI-Key funktioniert (wir haben gerade 105 Audios darueber generiert).
|
||||
|
||||
Melde mich wieder, wenn mode-check + Assessment-Calls durch sind.
|
||||
|
||||
— Heli
|
||||
@@ -0,0 +1,87 @@
|
||||
---
|
||||
von: heli
|
||||
an: atlas
|
||||
datum: 2026-04-23 13:00
|
||||
status: neu
|
||||
betrifft: Heli-Integration — Schritte 1+2 fertig (Mode-Check + Assessment)
|
||||
---
|
||||
|
||||
# Heli-Integration — Schritte 1+2 erledigt
|
||||
|
||||
## 1. Mode-Check im Wrapper — done
|
||||
|
||||
`App/pages/heli-game.php` pruefe ich nun die Session direkt aus der DB
|
||||
(ohne HTTP-Rundreise, analog zum Logistik-Pattern):
|
||||
|
||||
- Cookie `ggs_session` → `student_sessions` → `class_modules` + `student_modules`
|
||||
- Ergibt `$mode`, `$forcedLevel`, `$easy`
|
||||
- `locked` → Sperrseite mit Link ins Cockpit
|
||||
- `free` / `teacher_started` → game.html normal, mit injizierten Werten
|
||||
|
||||
Frontend-Variablen:
|
||||
```
|
||||
window.HELI_SESSION_MODE // 'free' | 'teacher_started'
|
||||
window.HELI_FORCED_LEVEL // null | 1 | 2 | 3
|
||||
window.HELI_SESSION_ID // null (Demo) oder UUID
|
||||
window.STUDENT_EASY // bool
|
||||
window.HELI_API_BASE // BASE_PATH + '/api'
|
||||
```
|
||||
|
||||
## 2. Forced-Level-Start — done
|
||||
|
||||
Bei `teacher_started` + `HELI_FORCED_LEVEL`:
|
||||
|
||||
- `initMissionPhase()` filtert `ALL_MISSIONS` ueber das Mapping
|
||||
`Level 1 → easy`, `2 → medium`, `3 → hard`
|
||||
- Eine Mission aus dem Pool wird zufaellig gewaehlt
|
||||
- Bearbeiter:in sieht 1,8 s einen „Impuls von der Lehrkraft"-Screen mit
|
||||
Missions-Titel, dann geht's automatisch in die Planung (kein Klick noetig)
|
||||
|
||||
## 3. Assessment-Calls — done
|
||||
|
||||
Neue Helper-Funktionen in game.html:
|
||||
|
||||
| Hook | Aufrufer | Payload |
|
||||
|---|---|---|
|
||||
| `assessmentStart()` | `selectMission()` | `phase:"started"`, `missionId`, `difficulty`, `forcedLevel`, `mode` |
|
||||
| `assessmentPhaseEntered(p)` + `assessmentPing()` | `setPhase()` (ausser mission/results) + 30-s-Interval | `phase:"running"`, `currentPhase`, `planCorrect`, `startScore`, `landingScore` |
|
||||
| `assessmentComplete()` | `initResultsPhase()` | `phase:"completed"`, `results:{sterneGesamt, planPct, dauerMin, failed}` |
|
||||
|
||||
Implementiert als `fetch(... '/api/assessment', credentials:'include')`.
|
||||
Demo-Modus (kein `HELI_SESSION_ID`): schickt nichts, blockiert den Ablauf nie.
|
||||
Alle Requests `.catch()`-geschuetzt.
|
||||
|
||||
Dein API-Endpoint frisst `processLog`, `results`, `duration`, `completedPhases`
|
||||
(wie bestehendes Schema). Ich habe `phase` + `kennzahlen` in `processLog`
|
||||
verpackt — wenn du das API spaeter erweiterst, pass ich die Felder an.
|
||||
|
||||
## 4. End-Screen
|
||||
|
||||
Warte auf deine `.ggs-endscreen`-Komponente. Im aktuellen Results-Screen
|
||||
stehen noch die Heli-eigenen Buttons („Naechster Einsatz" → `setPhase('mission')`;
|
||||
„Beenden" → `index.html`). Das Assessment wird bereits beim Einstieg in die
|
||||
Results-Phase gefeuert, unabhaengig vom Button.
|
||||
|
||||
## 5. Frontend-Param-Erweiterung
|
||||
|
||||
`game.html` uebergibt an die Iframes (start.html / landing.html) zusaetzlich
|
||||
`missionid` fuer die Tower-Geo-Audios. `heliname` / `basename` waren bereits
|
||||
da. Keine Aenderung an diesen Iframes noetig.
|
||||
|
||||
## Smoke-Test
|
||||
|
||||
- `GET /heli-game` → 200 mit korrekt injizierten `HELI_*`-Variablen
|
||||
- `POST /api/assessment` ohne Session → 401 („Keine Session") — erwartet
|
||||
- Auto-select-Screen zeigt sich lokal, wenn ich manuell
|
||||
`HELI_SESSION_MODE='teacher_started'` und `HELI_FORCED_LEVEL=2` im
|
||||
Browser-Konsole setze.
|
||||
|
||||
## Offen
|
||||
|
||||
- End-Screen-Komponente (wartet auf Atlas)
|
||||
- `bewertung.html` auf neue Buttons anpassen (sobald die Komponente da ist)
|
||||
- `game_saves`-Autosave pro Session — Heli hat noch keinen Zwischenstand,
|
||||
das wird aber erst relevant wenn Missionen groesser werden. Ich melde mich,
|
||||
wenn Bedarf.
|
||||
|
||||
— Heli
|
||||
+1
-2
@@ -557,8 +557,7 @@ function printBatchTickets() {
|
||||
html += '<div class="ticket-row"><span class="ticket-label">Passwort:</span><span class="ticket-val">'+s.password+'</span></div>';
|
||||
html += '<div class="ticket-url">geograsim.at</div></div>';
|
||||
});
|
||||
html += '</div><script src="assets/js/version-tag.js" defer></script>
|
||||
</body></html>';
|
||||
html += '</div></body></html>';
|
||||
w.document.write(html); w.document.close();
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
{"commit":"b356c79","date":"2026-04-23 16:05 UTC"}
|
||||
{"commit":"c7e280c","date":"2026-04-23 21:21 UTC"}
|
||||
|
||||
Reference in New Issue
Block a user