88e3c09e5c
Jede Sim liefert jetzt selbst leistung_pct in GGS_LIVE_STATE UND im Submit (Fundament fuer das einheitliche %-System) — kein Rueckrechnen aus Sternen mehr, keine 0-3-Sonderfaelle. Formeln je Sim wie vereinbart: - klima/fluss/kofferdetektiv/heli: = vorhandener 0-100-score - weltkueche: richtige/(richtige+falsche)*100 (Fehler in %) - busfahrt/fluggesellschaft: topoPunkte/(Auftraege*3)*100 - farmer: gewichteter totalScore; energiemanager: ausgegl./gesamt -15/Blackout - eu-werkstatt: firstTry/Schritte -5/Muelleimer; sonnensystem: Erstversuch-Quote - tourismustal: Ziele+Zufriedenheit+Natur gewichtet; tourismusregion: gewichteter score - logistik: Kontostand->% (sendet erstmals eine Kennzahl, auch an /api/assessment) Teacher _leistungPct + live.php extractScore bevorzugen bereits diesen Wert. Additive Aenderungen, bestehende Logik/Sterne unveraendert; alle 15 Dateien syntaktisch geprueft. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
770 lines
37 KiB
HTML
770 lines
37 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="de">
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, viewport-fit=cover">
|
||
<title>✈ Fluggesellschaft — GeoGraSim</title>
|
||
|
||
<link rel="stylesheet" href="../../assets/css/design-system.css">
|
||
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css"
|
||
integrity="sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY=" crossorigin="">
|
||
|
||
<style>
|
||
:root {
|
||
--fl-bg: #0a1d2e;
|
||
--fl-ink: #1f2937;
|
||
--fl-ink-2: #5b6b78;
|
||
--fl-good: #1b7e3b;
|
||
--fl-miss: #c0392b;
|
||
--fl-accent: #2563eb;
|
||
--fl-warm: #e8a247;
|
||
}
|
||
html, body {
|
||
margin: 0; padding: 0; height: 100dvh; overflow: hidden;
|
||
font-family: var(--ggs-font, system-ui, sans-serif);
|
||
background: var(--fl-bg);
|
||
color: var(--fl-ink);
|
||
}
|
||
#map { position: absolute; inset: 60px 0 0 0; z-index: 1; }
|
||
.leaflet-container { background: #0a1d2e; }
|
||
|
||
/* Flug-Status-Box: erscheint nur während Animation */
|
||
#flight-status {
|
||
position: absolute; top: 72px; left: 50%; transform: translateX(-50%);
|
||
z-index: 500; background: rgba(15, 28, 45, 0.92); color: #fff;
|
||
padding: 8px 16px; border-radius: 8px; font-size: 14px; font-weight: 600;
|
||
box-shadow: 0 4px 14px rgba(0,0,0,0.4); pointer-events: none;
|
||
border: 1px solid rgba(232, 162, 71, 0.5);
|
||
display: none; max-width: 90vw; text-align: center;
|
||
}
|
||
#flight-status.show { display: block; }
|
||
#flight-status .fs-progress { color: #e8a247; margin-left: 6px; }
|
||
|
||
.ggs-topbar {
|
||
position: relative; z-index: 100;
|
||
height: 60px; background: linear-gradient(135deg, #1f4b37, #2563eb);
|
||
color: #fff; display: flex; align-items: center; gap: 12px;
|
||
padding: 0 14px; box-shadow: 0 2px 8px rgba(0,0,0,0.2);
|
||
}
|
||
.ggs-topbar h1 { font-size: 17px; margin: 0; font-weight: 700; }
|
||
.ggs-topbar a { color: #fff; text-decoration: none; padding: 4px 8px;
|
||
background: rgba(255,255,255,0.15); border-radius: 6px; font-size: 18px; }
|
||
.fl-budget, .fl-counter, .fl-night-pill, .fl-co2 {
|
||
font-size: 14px; padding: 4px 10px; background: rgba(255,255,255,0.15); border-radius: 6px;
|
||
}
|
||
.fl-night-pill { display: none; background: #ff9800; }
|
||
.fl-night-pill.show { display: inline-flex; }
|
||
.fl-toggle {
|
||
background: rgba(255,255,255,0.18); border: none; color: #fff; cursor: pointer;
|
||
padding: 4px 8px; border-radius: 6px; font-size: 16px; margin-left: auto;
|
||
}
|
||
|
||
/* Order-Card */
|
||
.fl-order-card {
|
||
position: absolute; top: 76px; right: 12px; z-index: 60;
|
||
width: 320px; max-width: calc(100vw - 24px);
|
||
background: rgba(255,255,255,0.95); border-radius: 10px;
|
||
box-shadow: 0 6px 18px rgba(0,0,0,0.25); padding: 14px;
|
||
}
|
||
.fl-order-card.hidden { display: none; }
|
||
.fl-order-card.resolved { background: #f0f7f2; }
|
||
.fl-order-card h3 { margin: 0 0 6px; font-size: 17px; color: #1f4b37; }
|
||
.fl-order-card p { margin: 4px 0; font-size: 14px; line-height: 1.4; }
|
||
.fl-attempts { display: flex; gap: 6px; margin: 8px 0; }
|
||
.fl-dot { width: 14px; height: 14px; border-radius: 50%; background: #e0e0e0; border: 2px solid #ccc; }
|
||
.fl-dot.good { background: #4caf50; border-color: #2e7d32; }
|
||
.fl-dot.ok { background: #ff9800; border-color: #ef6c00; }
|
||
.fl-dot.miss { background: #f44336; border-color: #b71c1c; }
|
||
.fl-phase-label { font-size: 12px; color: var(--fl-ink-2); margin-top: 4px; }
|
||
.fl-night-hint { font-size: 12px; color: #ef6c00; margin-top: 6px; }
|
||
.fl-order-hint { font-size: 13px; color: var(--fl-warm); margin-top: 6px; font-weight: 600; }
|
||
|
||
/* Info-Card */
|
||
.fl-info {
|
||
position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
|
||
z-index: 200; max-width: 440px; width: calc(100vw - 24px); max-height: 86vh; overflow: auto;
|
||
background: #fff; border-radius: 14px; box-shadow: 0 12px 40px rgba(0,0,0,0.4);
|
||
padding: 18px;
|
||
}
|
||
.fl-info.hidden { display: none; }
|
||
.fl-info-close { position: absolute; top: 8px; right: 12px;
|
||
background: transparent; border: none; font-size: 22px; cursor: pointer; color: #888; }
|
||
.fl-info img { width: 100%; max-height: 160px; object-fit: cover; border-radius: 8px; margin: 8px 0; }
|
||
.fl-info-meta { display: flex; gap: 6px; flex-wrap: wrap; margin: 6px 0; }
|
||
.fl-info-meta .pill { background: #f0f0f0; padding: 2px 8px; border-radius: 12px; font-size: 12px; }
|
||
.fl-margin {
|
||
margin: 10px 0; padding: 10px 12px; border-radius: 10px;
|
||
background: #f7f5ee; border: 1px solid #e0dccf;
|
||
}
|
||
.fl-margin.won { background: #e8f5e9; border-color: #a5d6a7; }
|
||
.fl-margin.lost { background: #ffebee; border-color: #ef9a9a; }
|
||
.fl-margin__headline { display: flex; justify-content: space-between; font-weight: 700; font-size: 17px; }
|
||
.fl-margin__lesson { font-size: 13px; color: #5b6b78; margin: 6px 0; }
|
||
.bf-calc { display: grid; grid-template-columns: 1fr auto; gap: 4px 12px; font-family: monospace; font-size: 13px; }
|
||
.bf-calc .v { text-align: right; }
|
||
.bf-calc .pos { color: #1b7e3b; }
|
||
.bf-calc .neg { color: #c0392b; }
|
||
.bf-calc .total { font-weight: 700; }
|
||
.bf-calc__sep { grid-column: 1 / -1; border-bottom: 1px dashed #ccc; }
|
||
.fl-info-next {
|
||
margin-top: 10px; padding: 10px 14px; background: #2563eb; color: #fff;
|
||
border: none; border-radius: 8px; font-size: 15px; cursor: pointer; width: 100%;
|
||
}
|
||
.fl-info-text { font-size: 13px; line-height: 1.5; color: #555; margin-top: 8px; }
|
||
.fl-tz-info { font-size: 12px; color: #2563eb; background: #e3f2fd; padding: 6px 10px; border-radius: 6px; margin: 6px 0; }
|
||
.fl-co2-info { font-size: 12px; color: #5b6b78; background: #fff8e1; padding: 6px 10px; border-radius: 6px; margin: 6px 0; }
|
||
|
||
/* Choice-Modal (Quiz) */
|
||
.ggs-modal-overlay { display: none; position: fixed; inset: 0; z-index: 300;
|
||
background: rgba(10, 29, 46, 0.75); align-items: center; justify-content: center; }
|
||
.ggs-modal-overlay.show { display: flex; }
|
||
.ggs-modal { background: #fff; max-width: 480px; width: calc(100vw - 24px);
|
||
padding: 20px; border-radius: 12px; box-shadow: 0 12px 40px rgba(0,0,0,0.4); max-height: 90vh; overflow: auto; }
|
||
.ggs-modal h2 { margin: 0 0 8px; color: #1f4b37; }
|
||
.ggs-modal p { font-size: 15px; line-height: 1.5; margin: 6px 0; }
|
||
.ggs-modal-actions { display: flex; gap: 8px; margin-top: 14px; justify-content: flex-end; }
|
||
.ggs-modal-actions button {
|
||
padding: 10px 16px; background: #2563eb; color: #fff;
|
||
border: none; border-radius: 8px; font-size: 14px; cursor: pointer;
|
||
}
|
||
.ggs-modal-actions button.secondary { background: #5b6b78; }
|
||
|
||
.fl-choices { display: grid; gap: 10px; margin: 12px 0; }
|
||
.fl-choice {
|
||
background: #f1ede2; border: 2px solid #d4cfc0; border-radius: 10px;
|
||
padding: 14px 16px; font-size: 16px; font-weight: 600; cursor: pointer; text-align: left;
|
||
min-height: 52px; color: var(--fl-ink); transition: background 0.1s;
|
||
}
|
||
.fl-choice.right { background: #c8e6c9; border-color: var(--fl-good); }
|
||
.fl-choice.wrong { background: #ffcdd2; border-color: var(--fl-miss); }
|
||
.fl-choice[disabled] { opacity: 0.6; cursor: default; }
|
||
.fl-choice-feedback { font-size: 14px; line-height: 1.4; margin-top: 8px; padding: 8px 10px; border-radius: 6px; }
|
||
.fl-choice-feedback.right { background: #e8f5e9; color: #1b5e20; }
|
||
.fl-choice-feedback.wrong { background: #ffebee; color: #b71c1c; }
|
||
|
||
/* Endscreen */
|
||
.fl-stars { font-size: 36px; text-align: center; margin: 8px 0; }
|
||
.fl-end-row { display: flex; justify-content: space-between; padding: 4px 0; font-size: 14px; }
|
||
.fl-end-row.final { border-top: 1px solid #ccc; margin-top: 6px; padding-top: 8px; font-weight: 700; font-size: 16px; }
|
||
.fl-verdict { margin-top: 10px; padding: 10px; background: #f0f7f2; border-radius: 8px; font-size: 14px; line-height: 1.5; }
|
||
.fl-logbook { width: 100%; border-collapse: collapse; margin-top: 10px; font-size: 12px; }
|
||
.fl-logbook th, .fl-logbook td { padding: 4px 6px; border-bottom: 1px solid #eee; text-align: left; }
|
||
.fl-logbook .score-cell { text-align: center; }
|
||
.fl-logbook .margin-cell.pos { color: #1b7e3b; }
|
||
.fl-logbook .margin-cell.neg { color: #c0392b; }
|
||
.fl-logbook .night-row { background: #f5f5f5; }
|
||
.fl-logbook .lost-row td { color: #b71c1c; }
|
||
</style>
|
||
</head>
|
||
<body>
|
||
|
||
<div class="ggs-topbar">
|
||
<h1>✈ Fluggesellschaft</h1>
|
||
<a href="../../schueler.html" title="Zurück zur Schülerseite">🏠</a>
|
||
<span class="fl-budget" id="topbar-budget">💰 8.000 €</span>
|
||
<span class="fl-counter" id="topbar-counter">—</span>
|
||
<span class="fl-night-pill" id="topbar-night">🌙 Nachtflug</span>
|
||
<span class="fl-co2" id="topbar-co2">🌍 0 t CO₂</span>
|
||
<button class="fl-toggle" id="toggle-map" onclick="toggleMapStyle()" title="Karte umschalten">🛰️</button>
|
||
</div>
|
||
|
||
<div id="map"></div>
|
||
<div id="flight-status"><span class="fs-text">—</span><span class="fs-progress">0%</span></div>
|
||
|
||
<div class="fl-order-card hidden" id="order-card">
|
||
<h3 id="order-title">—</h3>
|
||
<p id="order-prompt">—</p>
|
||
<div class="fl-attempts">
|
||
<div class="fl-dot" id="dot-1"></div><div class="fl-dot" id="dot-2"></div><div class="fl-dot" id="dot-3"></div>
|
||
<span id="attempt-label" style="font-size:12px;color:#5b6b78">3 Versuche</span>
|
||
</div>
|
||
<div class="fl-phase-label" id="phase-label">Tippe den Flughafen auf der Karte</div>
|
||
<div class="fl-night-hint hidden" id="night-hint">🌙 Nachtflug — Karte ist abgedunkelt.</div>
|
||
<p class="fl-order-hint hidden" id="order-hint"></p>
|
||
</div>
|
||
|
||
<div class="fl-info hidden" id="info-card">
|
||
<button class="fl-info-close" onclick="closeInfo()">✕</button>
|
||
<h3 id="info-title">—</h3>
|
||
<img id="info-img" alt="" onerror="this.style.display='none'">
|
||
<div class="fl-info-meta">
|
||
<span class="pill" id="info-country">—</span>
|
||
<span class="pill" id="info-pop">—</span>
|
||
<span class="pill" id="info-continent">—</span>
|
||
<span class="pill" id="info-capital"></span>
|
||
</div>
|
||
<div class="fl-tz-info" id="info-tz">—</div>
|
||
<div class="fl-co2-info" id="info-co2">—</div>
|
||
<div class="fl-margin" id="info-margin">
|
||
<div class="fl-margin__headline">
|
||
<span id="margin-headline-label">Marge</span>
|
||
<span id="margin-headline-value">— €</span>
|
||
</div>
|
||
<div class="fl-margin__lesson" id="margin-lesson">—</div>
|
||
<div id="margin-rows"></div>
|
||
</div>
|
||
<p class="fl-info-text" id="info-text">—</p>
|
||
<button class="fl-info-next" onclick="nextOrder()" id="info-next-btn">Nächster Flug →</button>
|
||
</div>
|
||
|
||
<!-- Welcome -->
|
||
<div class="ggs-modal-overlay" id="welcome-overlay">
|
||
<div class="ggs-modal">
|
||
<h2>✈ Willkommen in der Flugleitung</h2>
|
||
<p>Du leitest eine Fluggesellschaft mit Hub am Frankfurter Flughafen (FRA).
|
||
Aufträge kommen rein — du musst Maschinen zu den richtigen Welt-Zielen schicken.</p>
|
||
<p><strong>So geht es:</strong></p>
|
||
<ul>
|
||
<li>Lies den Auftrag, beantworte die Zwischenfrage (Land / Kontinent).</li>
|
||
<li>Tippe den Ziel-Flughafen auf der Karte. 3 Versuche.</li>
|
||
<li>Daneben getippt = Umweg = Treibstoff = weniger Marge.</li>
|
||
<li>Treibstoff produziert CO₂ — wir zeigen die Bilanz mit.</li>
|
||
<li>Ziel: <strong id="welcome-target">— €</strong> am Tour-Ende.</li>
|
||
</ul>
|
||
<div class="ggs-modal-actions">
|
||
<button onclick="startGame()">Los arbeiten</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Quiz-Modal -->
|
||
<div class="ggs-modal-overlay" id="choice-overlay">
|
||
<div class="ggs-modal">
|
||
<h2 id="choice-title">Frage</h2>
|
||
<p id="choice-prompt">—</p>
|
||
<div class="fl-choices" id="choices-container"></div>
|
||
<div class="fl-choice-feedback" id="choice-feedback" style="display:none"></div>
|
||
<div class="ggs-modal-actions">
|
||
<button id="choice-continue" onclick="afterChoice()" style="display:none">Flughafen wählen</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Endscreen -->
|
||
<div class="ggs-modal-overlay" id="end-overlay">
|
||
<div class="ggs-modal">
|
||
<h2>✈ Tour beendet</h2>
|
||
<div class="fl-stars" id="end-stars">⭐⭐⭐</div>
|
||
<div>
|
||
<div class="fl-end-row"><span>Topografie-Punkte:</span><span id="end-topo-points">—</span></div>
|
||
<div class="fl-end-row"><span>Aufträge gewonnen:</span><span id="end-won-count">—</span></div>
|
||
<div class="fl-end-row"><span>Quiz richtig auf 1. Versuch:</span><span id="end-quiz-firsttry">—</span></div>
|
||
<div class="fl-end-row"><span>Startbudget:</span><span>8.000 €</span></div>
|
||
<div class="fl-end-row"><span>Verdienst aus Flügen:</span><span id="end-earnings">—</span></div>
|
||
<div class="fl-end-row" id="end-quiz-row" style="display:none"><span>Quiz-Bonus / -Strafe:</span><span id="end-quiz-delta">—</span></div>
|
||
<div class="fl-end-row final"><span>Endbudget:</span><span id="end-budget">— €</span></div>
|
||
<div class="fl-end-row"><span>🌍 Gesamt-CO₂ Tour:</span><span id="end-co2">—</span></div>
|
||
<div class="fl-verdict" id="end-verdict">—</div>
|
||
</div>
|
||
<table class="fl-logbook" id="end-logbook"></table>
|
||
<div class="ggs-modal-actions">
|
||
<button class="secondary" onclick="location.reload()">Nochmal arbeiten</button>
|
||
<button onclick="window.location.href='../../schueler.html'">Zur Übersicht</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"
|
||
integrity="sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo=" crossorigin=""></script>
|
||
<script src="engine.js"></script>
|
||
<script>
|
||
// ===================================================================
|
||
// Fluggesellschaft — UI
|
||
// ===================================================================
|
||
const FORCED_LEVEL = (window.FLUG_FORCED_LEVEL || 'l1');
|
||
const STUDENT_EASY = !!window.STUDENT_EASY;
|
||
function pickText(t) {
|
||
if (!t) return '';
|
||
if (typeof t === 'string') return t;
|
||
return STUDENT_EASY && t.easy ? t.easy : (t.de || t.easy || '');
|
||
}
|
||
function formatEur(n) { return (Math.abs(n) >= 1000 ? n.toLocaleString('de-AT') : String(Math.round(n))) + ' €'; }
|
||
function formatKm(n) { return Math.round(n).toLocaleString('de-AT') + ' km'; }
|
||
function formatPop(m) { return (m >= 1 ? m.toFixed(1) + ' Mio.' : (m * 1000).toFixed(0) + ' Tsd.') + ' Einw.'; }
|
||
function formatCO2t(kg) { return (kg / 1000).toFixed(1) + ' t'; }
|
||
function esc(s) { return String(s == null ? '' : s).replace(/[&<>"']/g, c => ({'&':'&','<':'<','>':'>','"':'"',"'":'''}[c])); }
|
||
|
||
// Leaflet-Karte
|
||
const tilesEsri = L.tileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}', {
|
||
attribution: 'Tiles © Esri', maxZoom: 18
|
||
});
|
||
const tilesOsm = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
||
attribution: '© OpenStreetMap', maxZoom: 18
|
||
});
|
||
const tilesDark = L.tileLayer('https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}.png', {
|
||
attribution: '© CARTO © OpenStreetMap', maxZoom: 18
|
||
});
|
||
let currentTileMode = 'esri'; // 'esri' | 'osm' | 'dark'
|
||
const map = L.map('map', { worldCopyJump: true, minZoom: 3, maxZoom: 8 }).setView([45, 15], 4);
|
||
L.control.scale({ metric: true, imperial: false, position: 'bottomleft', maxWidth: 200 }).addTo(map);
|
||
tilesEsri.addTo(map);
|
||
|
||
function toggleMapStyle() {
|
||
if (currentTileMode === 'esri') { map.removeLayer(tilesEsri); tilesOsm.addTo(map); currentTileMode = 'osm'; }
|
||
else if (currentTileMode === 'osm') { map.removeLayer(tilesOsm); tilesDark.addTo(map); currentTileMode = 'dark'; }
|
||
else { map.removeLayer(tilesDark); tilesEsri.addTo(map); currentTileMode = 'esri'; }
|
||
}
|
||
|
||
// Flughafen-Icon (✈) + Bus/Flug-Marker
|
||
const planeIcon = L.divIcon({ html: '<div style="font-size:24px;text-shadow:0 1px 3px #000">✈</div>',
|
||
iconSize: [24, 24], iconAnchor: [12, 12], className: '' });
|
||
const targetIcon = L.divIcon({ html: '<div style="font-size:28px;text-shadow:0 1px 3px #000">📍</div>',
|
||
iconSize: [28, 28], iconAnchor: [14, 28], className: '' });
|
||
const tapIcon = L.divIcon({ html: '<div style="font-size:22px">❓</div>',
|
||
iconSize: [22, 22], iconAnchor: [11, 11], className: '' });
|
||
|
||
let planeMarker = null, currentTapMarker = null, currentTargetMarker = null, currentLine = null;
|
||
let mapInteractive = false;
|
||
let game = null;
|
||
let totalOrders = 0;
|
||
|
||
(async function init() {
|
||
await window.FlugEngine.loadData();
|
||
const lvl = FORCED_LEVEL;
|
||
const seed = (window.FLUG_SESSION_ID ? hashStr(String(window.FLUG_SESSION_ID)) : 0) || Date.now();
|
||
game = window.FlugEngine.init(lvl, seed);
|
||
totalOrders = game.tourOrders.length;
|
||
const home = window.FlugEngine.getCity(game.lastCity);
|
||
if (home) {
|
||
planeMarker = L.marker([home.lat, home.lon], { icon: planeIcon }).addTo(map);
|
||
map.flyTo([home.lat, home.lon], 4, { duration: 0.6 });
|
||
}
|
||
updateTopbar();
|
||
|
||
const ECON = window.FlugEngine.ECON;
|
||
const target = (typeof ECON.targetBudget === 'object')
|
||
? (ECON.targetBudget[lvl] || 14000)
|
||
: ECON.targetBudget;
|
||
document.getElementById('welcome-target').textContent = formatEur(target);
|
||
document.getElementById('welcome-overlay').classList.add('show');
|
||
})();
|
||
|
||
function hashStr(s) { let h = 0; for (let i = 0; i < s.length; i++) h = ((h<<5)-h + s.charCodeAt(i))|0; return h>>>0; }
|
||
function haversineKmLocal(lat1, lon1, lat2, lon2) {
|
||
const R = 6371, toRad = d => d * Math.PI / 180;
|
||
const dLat = toRad(lat2 - lat1), dLon = toRad(lon2 - lon1);
|
||
const a = Math.sin(dLat/2)**2 + Math.cos(toRad(lat1)) * Math.cos(toRad(lat2)) * Math.sin(dLon/2)**2;
|
||
return 2 * R * Math.asin(Math.sqrt(a));
|
||
}
|
||
|
||
function updateTopbar() {
|
||
if (!game) return;
|
||
document.getElementById('topbar-budget').textContent = '💰 ' + formatEur(game.budget);
|
||
const idx = Math.min(game.orderIndex + 1, totalOrders);
|
||
document.getElementById('topbar-counter').textContent = `Flug ${idx} / ${totalOrders}`;
|
||
document.getElementById('topbar-co2').textContent = '🌍 ' + formatCO2t(game.co2Kg) + ' CO₂';
|
||
const order = game.currentOrder || (game.tourOrders[game.orderIndex] || null);
|
||
document.getElementById('topbar-night').classList.toggle('show', !!(order && order.isNight));
|
||
}
|
||
|
||
function startGame() {
|
||
document.getElementById('welcome-overlay').classList.remove('show');
|
||
if (game && !game._startedAt) game._startedAt = Date.now();
|
||
loadNextOrder();
|
||
}
|
||
|
||
function loadNextOrder() {
|
||
clearMapMarkers();
|
||
document.getElementById('order-hint').classList.add('hidden');
|
||
document.getElementById('info-card').classList.add('hidden');
|
||
resetAttemptDots();
|
||
const order = window.FlugEngine.nextOrder(game);
|
||
if (!order) { showEndScreen(); return; }
|
||
if (order.isNight) { /* Karte abdunkeln per Mode-Switch */ if (currentTileMode !== 'dark') { /* nicht erzwingen */ } }
|
||
renderOrderCard(order);
|
||
updateTopbar();
|
||
if (game.currentQuestion) { showChoiceModal(order); mapInteractive = false; }
|
||
else { document.getElementById('order-card').classList.remove('hidden'); mapInteractive = true; }
|
||
}
|
||
|
||
function renderOrderCard(order) {
|
||
const card = document.getElementById('order-card');
|
||
card.classList.remove('hidden', 'resolved');
|
||
const target = window.FlugEngine.getCity(order.cityId);
|
||
document.getElementById('order-title').textContent = target ? target.title : 'Ziel';
|
||
document.getElementById('order-prompt').textContent = pickText(order.prompt);
|
||
document.getElementById('attempt-label').textContent = '3 Versuche';
|
||
document.getElementById('phase-label').textContent = game.currentQuestion ? 'Erst Quiz beantworten' : 'Tippe den Flughafen';
|
||
document.getElementById('night-hint').classList.toggle('hidden', !order.isNight);
|
||
}
|
||
|
||
function resetAttemptDots() {
|
||
for (let i = 1; i <= 3; i++) document.getElementById('dot-' + i).classList.remove('miss', 'ok', 'good');
|
||
}
|
||
|
||
function showChoiceModal(order) {
|
||
const q = game.currentQuestion; if (!q) return;
|
||
const ov = document.getElementById('choice-overlay');
|
||
document.getElementById('choice-title').textContent = q.kind === 'capital' ? 'Hauptstadt-Frage' : (q.kind === 'continent' ? 'Kontinent-Frage' : 'Länder-Frage');
|
||
document.getElementById('choice-prompt').textContent = pickText(q.prompt);
|
||
const cont = document.getElementById('choices-container'); cont.innerHTML = '';
|
||
const fb = document.getElementById('choice-feedback'); fb.style.display = 'none';
|
||
document.getElementById('choice-continue').style.display = 'none';
|
||
q.choiceLabels.forEach((label, idx) => {
|
||
const btn = document.createElement('button');
|
||
btn.className = 'fl-choice'; btn.textContent = label;
|
||
btn.onclick = () => onChoicePick(btn, idx, cont);
|
||
cont.appendChild(btn);
|
||
});
|
||
ov.classList.add('show');
|
||
}
|
||
|
||
function onChoicePick(btn, idx, container) {
|
||
const result = window.FlugEngine.evaluateQuestion(game, idx);
|
||
if (!result) return;
|
||
updateTopbar();
|
||
const fb = document.getElementById('choice-feedback');
|
||
if (result.correct) {
|
||
btn.classList.add('right');
|
||
Array.from(container.children).forEach(b => b.disabled = true);
|
||
const q = game.currentQuestion;
|
||
const correctLabel = q.choiceLabels[q.correctIndex];
|
||
let msg = result.bonusEur > 0
|
||
? `Richtig! +${result.bonusEur} € Bonus für die erste richtige Antwort.`
|
||
: `Richtig: ${correctLabel}. ${result.penaltyEur > 0 ? `(${result.attempts.length-1} Fehlversuche haben ${result.penaltyEur} € gekostet.)` : ''}`;
|
||
fb.textContent = msg; fb.className = 'fl-choice-feedback right'; fb.style.display = '';
|
||
document.getElementById('choice-continue').style.display = 'inline-flex';
|
||
} else {
|
||
btn.classList.add('wrong'); btn.disabled = true;
|
||
const penaltyTbl = window.FlugEngine.ECON.quizPenaltyPerWrongEur;
|
||
const penalty = (typeof penaltyTbl === 'object') ? (penaltyTbl[game.levelId] || penaltyTbl.l1 || 100) : penaltyTbl;
|
||
fb.textContent = `Nicht ganz. −${penalty} € Strafe. Versuch noch eine Antwort.`;
|
||
fb.className = 'fl-choice-feedback wrong'; fb.style.display = '';
|
||
}
|
||
}
|
||
|
||
function afterChoice() {
|
||
document.getElementById('choice-overlay').classList.remove('show');
|
||
mapInteractive = true;
|
||
document.getElementById('phase-label').textContent = 'Tippe den Flughafen';
|
||
}
|
||
|
||
map.on('click', async (e) => {
|
||
if (!mapInteractive || !game || !game.currentOrder) return;
|
||
const tap = { lat: e.latlng.lat, lon: e.latlng.lng };
|
||
const result = window.FlugEngine.checkTap(game, tap);
|
||
if (!result) return;
|
||
paintAttempt(tap);
|
||
updateAttemptDot(game.attempts.length, result.score);
|
||
if (result.resolved) {
|
||
mapInteractive = false;
|
||
document.getElementById('attempt-label').textContent =
|
||
result.score === 3 ? '✓ Treffer!' : (result.score === 1 ? 'Ungenau' : 'Versuche aufgebraucht');
|
||
document.getElementById('order-hint').classList.add('hidden');
|
||
document.getElementById('phase-label').textContent = '✈ Flug läuft …';
|
||
await runResolution(tap);
|
||
} else {
|
||
showHint(result);
|
||
document.getElementById('attempt-label').textContent = `Versuch ${game.attempts.length + 1} / 3`;
|
||
setTimeout(() => clearTapMarker(), 1500);
|
||
}
|
||
});
|
||
|
||
function paintAttempt(tap) {
|
||
if (currentTapMarker) map.removeLayer(currentTapMarker);
|
||
currentTapMarker = L.marker([tap.lat, tap.lon], { icon: tapIcon }).addTo(map);
|
||
}
|
||
function updateAttemptDot(n, score) {
|
||
const d = document.getElementById('dot-' + n); if (!d) return;
|
||
if (score === 3) d.classList.add('good');
|
||
else if (score === 1) d.classList.add('ok');
|
||
else d.classList.add('miss');
|
||
}
|
||
function showHint(result) {
|
||
const h = document.getElementById('order-hint');
|
||
h.textContent = result.hint; h.classList.remove('hidden');
|
||
}
|
||
function clearTapMarker() { if (currentTapMarker) { map.removeLayer(currentTapMarker); currentTapMarker = null; } }
|
||
function clearMapMarkers() {
|
||
clearTapMarker();
|
||
if (currentTargetMarker) { map.removeLayer(currentTargetMarker); currentTargetMarker = null; }
|
||
if (currentLine) { map.removeLayer(currentLine); currentLine = null; }
|
||
}
|
||
|
||
async function runResolution(tap) {
|
||
const order = game.currentOrder;
|
||
const target = window.FlugEngine.getCity(order.cityId);
|
||
const fromCity = window.FlugEngine.getCity(game.lastCity);
|
||
if (!target || !fromCity) return;
|
||
|
||
// Großkreis-Polyline zeichnen (Plot der echten Flugbahn)
|
||
const pts = window.FlugEngine.greatCirclePoints(fromCity.lat, fromCity.lon, target.lat, target.lon, 64);
|
||
currentLine = L.polyline(pts, { color: '#e8a247', weight: 3, opacity: 0.9, dashArray: '6 4' }).addTo(map);
|
||
if (currentTargetMarker) map.removeLayer(currentTargetMarker);
|
||
currentTargetMarker = L.marker([target.lat, target.lon], { icon: targetIcon }).addTo(map);
|
||
map.flyToBounds(L.latLngBounds(pts).pad(0.2), { duration: 0.9, maxZoom: 4 });
|
||
|
||
// Flugzeug-Animation entlang der Linie — distanzbasiert.
|
||
// Bearbeiter:in soll die Route verfolgen können: kurze Flüge ~3s, lange ~12s.
|
||
const start = pts[0]; const end = pts[pts.length - 1];
|
||
const distanceKm = haversineKmLocal(start[0], start[1], end[0], end[1]);
|
||
const totalDurationMs = Math.max(3000, Math.min(12000, distanceKm * 1.5));
|
||
const stepMs = totalDurationMs / pts.length;
|
||
|
||
// Status-Box oberhalb der Karte einblenden
|
||
const statusEl = document.getElementById('flight-status');
|
||
const statusText = statusEl ? statusEl.querySelector('.fs-text') : null;
|
||
const statusProg = statusEl ? statusEl.querySelector('.fs-progress') : null;
|
||
if (statusEl) {
|
||
if (statusText) statusText.textContent = `${fromCity.title} → ${target.title} · ${Math.round(distanceKm).toLocaleString('de-AT')} km · Fortschritt`;
|
||
if (statusProg) statusProg.textContent = '0%';
|
||
statusEl.classList.add('show');
|
||
}
|
||
|
||
if (planeMarker) {
|
||
let i = 0;
|
||
await new Promise(res => {
|
||
const step = () => {
|
||
i++;
|
||
if (i >= pts.length) {
|
||
planeMarker.setLatLng(end);
|
||
if (statusProg) statusProg.textContent = '100%';
|
||
res();
|
||
return;
|
||
}
|
||
planeMarker.setLatLng(pts[i]);
|
||
if (statusProg) statusProg.textContent = Math.round((i / pts.length) * 100) + '%';
|
||
setTimeout(step, stepMs);
|
||
};
|
||
step();
|
||
});
|
||
}
|
||
if (statusEl) setTimeout(() => statusEl.classList.remove('show'), 700);
|
||
|
||
const report = window.FlugEngine.settleOrder(game);
|
||
updateTopbar();
|
||
showInfoCard(report);
|
||
}
|
||
|
||
function showInfoCard(report) {
|
||
const city = window.FlugEngine.getCity(report.cityId);
|
||
if (!city) return;
|
||
document.getElementById('info-title').textContent = city.title;
|
||
const img = document.getElementById('info-img');
|
||
if (city.imagePath) { img.src = (window.FLUG_BASE || '') + city.imagePath; img.style.display = ''; }
|
||
else img.style.display = 'none';
|
||
document.getElementById('info-country').textContent = '🌍 ' + city.country;
|
||
document.getElementById('info-pop').textContent = '👥 ' + formatPop(city.population_million || 0);
|
||
document.getElementById('info-continent').textContent = '🗺 ' + (city.continent || '');
|
||
const cap = document.getElementById('info-capital');
|
||
if (city.isCapital) { cap.textContent = '⭐ Hauptstadt'; cap.style.display = ''; } else { cap.style.display = 'none'; }
|
||
|
||
// Zeitzone-Info
|
||
const tz = document.getElementById('info-tz');
|
||
const tzDiff = report.tzDiffHours || 0;
|
||
if (tzDiff === 0) tz.textContent = `🕐 Selbe Zeitzone wie Startflughafen.`;
|
||
else if (tzDiff > 0) tz.textContent = `🕐 ${city.title} ist ${tzDiff}h VORAUS — wenn es bei dir 12:00 ist, dort ${(12+tzDiff)%24}:00.`;
|
||
else tz.textContent = `🕐 ${city.title} ist ${Math.abs(tzDiff)}h ZURÜCK — wenn es bei dir 12:00 ist, dort ${(12+tzDiff+24)%24}:00.`;
|
||
|
||
// CO2-Info
|
||
document.getElementById('info-co2').textContent =
|
||
`🌳 Dieser Flug verursachte ${report.co2Kg.toLocaleString('de-AT')} kg CO₂ (gerechnet auf ${window.FlugEngine.ECON.paxOnboard} Passagiere). Ein Hektar Wald bindet ca. 10 t CO₂/Jahr.`;
|
||
|
||
// Marge-Block analog Bus
|
||
const detourKm = Math.round(report.detourKm);
|
||
const quizBonus = report.quizBonus || 0;
|
||
const quizPenalty = report.quizPenalty || 0;
|
||
const quizDelta = quizBonus - quizPenalty;
|
||
const tourMargin = report.margin + quizDelta;
|
||
const tourMargClass = tourMargin >= 0 ? 'pos' : 'neg';
|
||
document.getElementById('margin-headline-value').textContent =
|
||
(tourMargin >= 0 ? '+' : '−') + formatEur(Math.abs(tourMargin));
|
||
document.getElementById('margin-headline-label').textContent = tourMargin >= 0 ? 'Marge' : 'Verlust';
|
||
document.getElementById('info-margin').className = 'fl-margin ' + (tourMargin >= 0 ? 'won' : 'lost');
|
||
|
||
let lesson;
|
||
if (detourKm <= 50) lesson = '✓ Punktlandung am Flughafen — volle Marge.';
|
||
else if (detourKm < 500) lesson = `Kleiner Umweg von ${detourKm} km — kostet ${formatEur(detourKm * 0.13 * 180)}, frisst aber noch nicht alles.`;
|
||
else lesson = `Großer Umweg von ${detourKm} km — Treibstoff frisst die Marge weg.`;
|
||
document.getElementById('margin-lesson').textContent = lesson;
|
||
|
||
const rows = document.getElementById('margin-rows');
|
||
const fmtSigned = n => (n >= 0 ? '+' : '−') + formatEur(Math.abs(n));
|
||
const subtotal = report.baseMargin;
|
||
const subClass = subtotal >= 0 ? 'pos' : 'neg';
|
||
let html = '<div class="bf-calc">';
|
||
html += `<div>Direkte Flugstrecke</div><div class="v">${formatKm(report.realDistanceKm)}</div>`;
|
||
html += `<div>Tatsächlich geflogen</div><div class="v">${formatKm(report.actualDistanceKm)}</div>`;
|
||
if (detourKm > 0) html += `<div>davon Umweg</div><div class="v">+${formatKm(detourKm)}</div>`;
|
||
html += '<div class="bf-calc__sep"></div>';
|
||
html += `<div>Einnahmen (180 Pax × Tarif)</div><div class="v pos">+${formatEur(report.playerPrice)}</div>`;
|
||
html += `<div>Ausgaben (Treibstoff + Flughafengeb.)</div><div class="v neg">−${formatEur(report.busCost)}</div>`;
|
||
html += '<div class="bf-calc__sep"></div>';
|
||
if (quizBonus > 0 || quizPenalty > 0) {
|
||
html += `<div class="total">Zwischensumme</div><div class="v total ${subClass}">${fmtSigned(subtotal)}</div>`;
|
||
if (quizBonus > 0) html += `<div>Quiz-Bonus (1. Versuch)</div><div class="v pos">+${formatEur(quizBonus)}</div>`;
|
||
if (quizPenalty > 0) html += `<div>Quiz-Strafe (Fehlversuche)</div><div class="v neg">−${formatEur(quizPenalty)}</div>`;
|
||
html += '<div class="bf-calc__sep"></div>';
|
||
}
|
||
html += `<div class="total">Marge</div><div class="v total ${tourMargClass}">${fmtSigned(tourMargin)}</div>`;
|
||
html += '</div>';
|
||
rows.innerHTML = html;
|
||
|
||
document.getElementById('info-text').textContent = pickText(city.infoText);
|
||
const isLast = game.orderIndex >= totalOrders;
|
||
document.getElementById('info-next-btn').textContent = isLast ? 'Auswertung →' : 'Nächster Flug →';
|
||
document.getElementById('info-card').classList.remove('hidden');
|
||
}
|
||
|
||
function closeInfo() {
|
||
document.getElementById('info-card').classList.add('hidden');
|
||
loadNextOrder();
|
||
}
|
||
function nextOrder() { closeInfo(); }
|
||
|
||
function showEndScreen() {
|
||
const ECON = window.FlugEngine.ECON;
|
||
const maxTopo = totalOrders * 3;
|
||
const ratio = game.score / maxTopo;
|
||
let stars;
|
||
if (ratio >= 0.85) stars = '⭐⭐⭐';
|
||
else if (ratio >= 0.55) stars = '⭐⭐';
|
||
else if (ratio >= 0.30) stars = '⭐';
|
||
else stars = '—';
|
||
document.getElementById('end-stars').textContent = stars;
|
||
|
||
const wonCount = game.reports.filter(r => r.won).length;
|
||
const quizFirstTry = game.reports.filter(r => r.quizFirstTry).length;
|
||
const quizBonusSum = game.reports.reduce((s, r) => s + (r.quizBonus || 0), 0);
|
||
const quizPenaltySum = game.reports.reduce((s, r) => s + (r.quizPenalty || 0), 0);
|
||
const quizDelta = quizBonusSum - quizPenaltySum;
|
||
document.getElementById('end-topo-points').textContent = `${game.score} / ${maxTopo}`;
|
||
document.getElementById('end-won-count').textContent = `${wonCount} / ${totalOrders}`;
|
||
document.getElementById('end-quiz-firsttry').textContent = `${quizFirstTry} / ${totalOrders}`;
|
||
document.getElementById('end-earnings').textContent = (game.earnings >= 0 ? '+' : '') + formatEur(game.earnings);
|
||
if (quizBonusSum > 0 || quizPenaltySum > 0) {
|
||
document.getElementById('end-quiz-row').style.display = '';
|
||
document.getElementById('end-quiz-delta').textContent = (quizDelta >= 0 ? '+' : '−') + formatEur(Math.abs(quizDelta));
|
||
}
|
||
document.getElementById('end-budget').textContent = formatEur(game.budget);
|
||
document.getElementById('end-co2').textContent = formatCO2t(game.co2Kg) + ' (= ' + Math.round(game.co2Kg / 10000) + ' Hektar Wald, 1 Jahr Bindung)';
|
||
|
||
const target = (typeof ECON.targetBudget === 'object') ? (ECON.targetBudget[game.levelId] || 14000) : ECON.targetBudget;
|
||
const verdEl = document.getElementById('end-verdict');
|
||
if (game.budget >= target) verdEl.innerHTML = `🎉 Super! Tour-Ziel von <strong>${formatEur(target)}</strong> erreicht.`;
|
||
else if (game.budget >= ECON.startBudget) verdEl.innerHTML = `Solide. Ziel war ${formatEur(target)}.`;
|
||
else verdEl.innerHTML = `🚧 Verlust. Tipp: Flughäfen genauer treffen, Quiz auf 1. Versuch lösen.`;
|
||
|
||
const tbl = document.getElementById('end-logbook');
|
||
tbl.innerHTML = '<tr><th>#</th><th>Flug</th><th>Topo</th><th>Marge</th></tr>';
|
||
game.reports.forEach((r, i) => {
|
||
const fromCity = window.FlugEngine.getCity(r.fromCityId);
|
||
const toCity = window.FlugEngine.getCity(r.cityId);
|
||
const fromName = fromCity ? fromCity.title : r.fromCityId;
|
||
const toName = toCity ? toCity.title : r.cityId;
|
||
const scoreEmoji = r.finalScore === 3 ? '🟢' : (r.finalScore === 1 ? '🟠' : '🔴');
|
||
const quizDeltaRow = (r.quizBonus || 0) - (r.quizPenalty || 0);
|
||
const tourMargin = r.margin + quizDeltaRow;
|
||
const margClass = tourMargin > 0 ? 'pos' : (tourMargin < 0 ? 'neg' : '');
|
||
const margTxt = (tourMargin >= 0 ? '+' : '') + formatEur(tourMargin);
|
||
let quizBadge = r.quizFirstTry ? ' · 🧠 ✓' : (r.quizAttemptsCount > 1 ? ` · 🧠 ${r.quizAttemptsCount}×` : '');
|
||
const tr = document.createElement('tr');
|
||
if (tourMargin < 0) tr.className = 'lost-row';
|
||
if (r.isNight) tr.classList.add('night-row');
|
||
tr.innerHTML =
|
||
`<td>${i + 1}</td>` +
|
||
`<td>${fromName} → <strong>${toName}</strong><br><small>${Math.round(r.realDistanceKm)} km · ${r.co2Kg.toLocaleString('de-AT')} kg CO₂${quizBadge}</small></td>` +
|
||
`<td class="score-cell">${scoreEmoji}</td>` +
|
||
`<td class="margin-cell ${margClass}">${margTxt}</td>`;
|
||
tbl.appendChild(tr);
|
||
});
|
||
document.getElementById('order-card').classList.add('hidden');
|
||
document.getElementById('info-card').classList.add('hidden');
|
||
clearMapMarkers();
|
||
document.getElementById('end-overlay').classList.add('show');
|
||
|
||
// Submit an die Plattform
|
||
submitTourToBackend();
|
||
}
|
||
|
||
// ===== Submit-Hook (Lehrer-Cockpit, Klassen-Vergleich) =====
|
||
function submitTourToBackend() {
|
||
if (!window.FLUG_SESSION_ID) return; // Free-Mode: nicht senden
|
||
const api = (window.FLUG_API_BASE || '/geograsim/App/php/api') + '/progress.php';
|
||
const total = game.tourOrders.length || 1;
|
||
const wonCount = game.reports.filter(r => r.won).length;
|
||
const quizFirstTry = game.reports.filter(r => r.quizFirstTry).length;
|
||
const stars = (() => {
|
||
const ratio = game.score / (total * 3);
|
||
return ratio >= 0.85 ? 3 : (ratio >= 0.55 ? 2 : (ratio >= 0.30 ? 1 : 0));
|
||
})();
|
||
try {
|
||
fetch(api, {
|
||
method: 'POST', headers: { 'Content-Type': 'application/json' },
|
||
credentials: 'same-origin',
|
||
body: JSON.stringify({
|
||
sim_id: 'fluggesellschaft',
|
||
action: 'submit_assessment',
|
||
data: {
|
||
level: game.levelId, stars,
|
||
score: Math.round(game.score),
|
||
duration_ms: Date.now() - (game._startedAt || Date.now()),
|
||
completed: true,
|
||
results: {
|
||
budget: game.budget,
|
||
earnings: game.earnings,
|
||
co2Kg: game.co2Kg,
|
||
wonOrders: wonCount,
|
||
totalOrders: total,
|
||
quizFirstTry,
|
||
quizFailCount: game.reports.reduce((s,r)=>s+Math.max(0,(r.quizAttemptsCount||1)-1), 0),
|
||
leistung_pct: (total*3)===0 ? null : Math.max(0,Math.min(100,Math.round(game.score/(total*3)*100))),
|
||
}
|
||
}
|
||
})
|
||
}).catch(()=>{});
|
||
} catch (_) {}
|
||
}
|
||
|
||
// ===== LIVE-State für Lehrer*innen-Cockpit =====
|
||
window.GGS_LIVE_STATE = function() {
|
||
if (!game) return null;
|
||
const reports = game.reports || [];
|
||
const wonCount = reports.filter(r => r.won).length;
|
||
const lastR = reports[reports.length - 1] || null;
|
||
const lastCity = window.FlugEngine.getCity(game.lastCity);
|
||
return {
|
||
simId: 'fluggesellschaft',
|
||
levelId: game.levelId,
|
||
orderIndex: game.orderIndex,
|
||
totalOrders: game.tourOrders.length,
|
||
budget: game.budget,
|
||
earnings: game.earnings,
|
||
co2Kg: game.co2Kg,
|
||
wonOrders: wonCount,
|
||
lostOrders: reports.length - wonCount,
|
||
lastCityId: game.lastCity,
|
||
lastCityTitle: lastCity ? lastCity.title : null,
|
||
lastMargin: lastR ? lastR.margin : null,
|
||
lastTzDiff: lastR ? lastR.tzDiffHours : null,
|
||
quizFirstTry: reports.filter(r => r.quizFirstTry).length,
|
||
leistung_pct: (game.tourOrders.length*3)===0 ? null : Math.max(0,Math.min(100,Math.round(game.score/(game.tourOrders.length*3)*100))),
|
||
timestamp: Date.now(),
|
||
};
|
||
};
|
||
</script>
|
||
|
||
<script src="../../assets/js/ggs-quick-intro.js" defer></script>
|
||
<script>
|
||
window.addEventListener('DOMContentLoaded', function () {
|
||
if (window.GgsQuickIntro) GgsQuickIntro.init({
|
||
simId: 'fluggesellschaft',
|
||
title: '✈️ Fluggesellschaft',
|
||
lead: 'Du fliegst Aufträge rund um den Globus. Hauptkostenpunkt: CO₂ und Zeitzonen. Wo lohnt sich die weite Reise, wo nicht?',
|
||
steps: [
|
||
'Auftrag wählen — du siehst Start, Ziel und Bezahlung.',
|
||
'Auf der Weltkarte das richtige Land/die richtige Stadt anklicken.',
|
||
'Quiz beantworten — Land und Kontinent richtig zuordnen.',
|
||
'CO₂-Bilanz im Blick — pro Flug + pro Sitzung.',
|
||
],
|
||
goals: [
|
||
'Globale Geografie sicher zuordnen',
|
||
'Zeitzonen verstehen und nutzen',
|
||
'CO₂-Wirkung von Fernreisen einschätzen',
|
||
],
|
||
});
|
||
});
|
||
</script>
|
||
</body>
|
||
</html>
|