1e51ef7def
- Konzept/, didaktik_geografie/, didaktik_simulation/, v2-modules/, v2-platform/ - 12 code-workspace-Files - STATUS-*.md - viele M/D/R-Änderungen an bereits getrackten Files - .gitignore verstärkt: **/.humaninput/, **/secret_keys.txt Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
1039 lines
39 KiB
HTML
1039 lines
39 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="de">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
|
||
<title>Die große Wanderung — 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="">
|
||
<script src="../../assets/js/glossar-tooltip.js" defer></script>
|
||
<style>
|
||
/* =========================================================================
|
||
Wal-Sim · Sim-eigenes CSS
|
||
========================================================================= */
|
||
:root {
|
||
--wal-deep: #1a3949;
|
||
--wal-mid: #2e5b6e;
|
||
--wal-light: #5e8a9c;
|
||
--wal-sand: #e8e4d8;
|
||
--wal-sand-warm: #f4ede0;
|
||
--wal-coral: #b86840;
|
||
--wal-coral-dark: #8b4a2a;
|
||
--wal-moss: #4a7c4e;
|
||
--wal-honey: #e8c547;
|
||
--wal-ink: #1a2620;
|
||
--wal-ink-soft: #4a5550;
|
||
}
|
||
|
||
html, body { height: 100%; margin: 0; }
|
||
body {
|
||
font-family: 'Hanken Grotesk', system-ui, -apple-system, sans-serif;
|
||
background: var(--wal-sand);
|
||
color: var(--wal-ink);
|
||
overflow: hidden;
|
||
-webkit-tap-highlight-color: transparent;
|
||
}
|
||
|
||
#app { height: 100vh; display: flex; flex-direction: column; }
|
||
|
||
/* ---------- Era-Badge im Header ---------- */
|
||
#era-badge { background: var(--wal-coral); }
|
||
#era-badge.era-1850 { background: #8b4a2a; }
|
||
#era-badge.era-1990 { background: #4a7c8a; }
|
||
#era-badge.era-2026 { background: var(--wal-moss); }
|
||
|
||
/* ---------- Screens (Vollbild-Wechsel) ---------- */
|
||
.screen { flex: 1; display: none; flex-direction: column; min-height: 0; }
|
||
.screen.active { display: flex; }
|
||
|
||
/* =========================================================================
|
||
ERA PICKER
|
||
========================================================================= */
|
||
#screen-era {
|
||
background: linear-gradient(180deg, #2e5b6e 0%, #1a3949 100%);
|
||
color: var(--wal-sand-warm);
|
||
overflow-y: auto;
|
||
}
|
||
.era-head {
|
||
max-width: 880px; margin: 0 auto; padding: 36px 24px 16px; text-align: center;
|
||
}
|
||
.era-tag {
|
||
font-size: .72rem; letter-spacing: .22em; text-transform: uppercase;
|
||
color: var(--wal-honey); font-weight: 700; margin-bottom: 12px;
|
||
}
|
||
.era-head h1 {
|
||
font-size: clamp(1.6rem, 4vw, 2.4rem); font-weight: 800; margin: 0 0 10px;
|
||
letter-spacing: -.02em; line-height: 1.1;
|
||
}
|
||
.era-head p {
|
||
font-size: 1rem; line-height: 1.5; max-width: 640px; margin: 0 auto;
|
||
color: rgba(248, 244, 232, .85);
|
||
}
|
||
.era-grid {
|
||
max-width: 980px; margin: 0 auto; padding: 24px 16px 48px;
|
||
display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
|
||
gap: 16px;
|
||
}
|
||
.era-card {
|
||
background: var(--wal-sand-warm); color: var(--wal-ink);
|
||
border-radius: 10px; padding: 22px 22px 18px;
|
||
cursor: pointer; transition: transform .15s, box-shadow .15s;
|
||
border: 2px solid transparent;
|
||
display: flex; flex-direction: column; gap: 10px;
|
||
min-height: 280px;
|
||
}
|
||
.era-card:hover { transform: translateY(-3px); box-shadow: 0 12px 28px rgba(0,0,0,.25); }
|
||
.era-card:focus-visible { outline: 3px solid var(--wal-honey); outline-offset: 2px; }
|
||
.era-card-year {
|
||
font-size: 2.2rem; font-weight: 800; line-height: 1;
|
||
color: var(--wal-deep); letter-spacing: -.03em;
|
||
}
|
||
.era-card-sub {
|
||
font-size: .65rem; letter-spacing: .2em; text-transform: uppercase;
|
||
color: var(--wal-coral); font-weight: 700;
|
||
}
|
||
.era-card-lead { font-size: .92rem; line-height: 1.5; color: var(--wal-ink); flex: 1; }
|
||
.era-card-stats {
|
||
display: flex; gap: 12px; padding-top: 10px;
|
||
border-top: 1px solid rgba(0,0,0,.1); font-size: .78rem;
|
||
}
|
||
.era-card-stats span { color: var(--wal-ink-soft); }
|
||
.era-card-stats b { color: var(--wal-deep); }
|
||
.era-card-cta {
|
||
margin-top: auto; padding: 10px 14px;
|
||
background: var(--wal-coral); color: white;
|
||
border: 0; border-radius: 6px; font-weight: 700; cursor: pointer;
|
||
font-family: inherit; font-size: .9rem; letter-spacing: .04em;
|
||
}
|
||
.era-card-cta:hover { background: var(--wal-coral-dark); }
|
||
.era-foot {
|
||
max-width: 880px; margin: 0 auto; padding: 0 24px 32px;
|
||
font-size: .82rem; line-height: 1.55; color: rgba(248, 244, 232, .7);
|
||
text-align: center;
|
||
}
|
||
|
||
/* =========================================================================
|
||
GAME SCREEN — Stats-HUD + Map + Panel
|
||
========================================================================= */
|
||
#screen-game { background: var(--wal-sand); }
|
||
|
||
.hud {
|
||
background: var(--wal-deep); color: var(--wal-sand-warm);
|
||
padding: 8px 16px; display: flex; gap: 16px; align-items: center;
|
||
flex-wrap: wrap;
|
||
box-shadow: 0 2px 6px rgba(0,0,0,.15);
|
||
}
|
||
.hud-stage {
|
||
flex: 1; min-width: 200px;
|
||
font-weight: 700; font-size: .95rem;
|
||
}
|
||
.hud-stage small {
|
||
display: block; font-size: .68rem; letter-spacing: .15em;
|
||
text-transform: uppercase; color: rgba(248, 244, 232, .65);
|
||
font-weight: 600; margin-bottom: 2px;
|
||
}
|
||
.hud-day {
|
||
background: rgba(255,255,255,.1); padding: 4px 10px; border-radius: 99px;
|
||
font-size: .82rem; font-weight: 700; font-variant-numeric: tabular-nums;
|
||
}
|
||
.hud-stats { display: flex; gap: 8px; flex-wrap: wrap; }
|
||
.hud-stat {
|
||
min-width: 95px; background: rgba(255,255,255,.08);
|
||
padding: 5px 9px; border-radius: 6px;
|
||
}
|
||
.hud-stat-label {
|
||
font-size: .58rem; letter-spacing: .18em; text-transform: uppercase;
|
||
color: rgba(248, 244, 232, .7); font-weight: 700;
|
||
display: flex; justify-content: space-between;
|
||
}
|
||
.hud-stat-label .v {
|
||
font-variant-numeric: tabular-nums; color: var(--wal-sand-warm);
|
||
letter-spacing: .04em; font-size: .68rem;
|
||
}
|
||
.hud-stat-bar { height: 4px; background: rgba(0,0,0,.3); margin-top: 4px; border-radius: 2px; overflow: hidden; }
|
||
.hud-stat-fill { height: 100%; transition: width .25s linear, background .3s; border-radius: 2px; }
|
||
.hud-stat-fill.good { background: var(--wal-moss); }
|
||
.hud-stat-fill.ok { background: var(--wal-honey); }
|
||
.hud-stat-fill.bad { background: var(--wal-coral); }
|
||
.hud-calf {
|
||
font-size: 1.2rem; opacity: 0;
|
||
transition: opacity .35s;
|
||
}
|
||
.hud-calf.show { opacity: 1; }
|
||
|
||
#map-wrap {
|
||
flex: 1; min-height: 0; position: relative;
|
||
background: var(--wal-light);
|
||
}
|
||
#map { width: 100%; height: 100%; }
|
||
|
||
/* Leaflet whale marker (custom icon via class) */
|
||
.wal-marker {
|
||
font-size: 1.6rem; line-height: 1;
|
||
text-shadow: 0 1px 2px rgba(0,0,0,.4);
|
||
filter: drop-shadow(0 0 4px rgba(255,255,255,.6));
|
||
}
|
||
|
||
.panel {
|
||
background: var(--wal-sand-warm);
|
||
border-top: 3px solid var(--wal-coral);
|
||
padding: 14px 16px 18px; max-height: 38%; overflow-y: auto;
|
||
}
|
||
.panel-tag {
|
||
font-size: .65rem; letter-spacing: .22em; text-transform: uppercase;
|
||
color: var(--wal-coral); font-weight: 700; margin-bottom: 4px;
|
||
}
|
||
.panel-text { font-size: .95rem; line-height: 1.55; color: var(--wal-ink); margin: 0; }
|
||
.panel-text strong { color: var(--wal-deep); }
|
||
.panel-actions {
|
||
margin-top: 12px; display: flex; gap: 8px; flex-wrap: wrap;
|
||
}
|
||
|
||
/* =========================================================================
|
||
MODAL (Stage-Intro / Encounter / Tutorial)
|
||
========================================================================= */
|
||
.modal-back {
|
||
position: fixed; inset: 0; background: rgba(26, 38, 32, .72);
|
||
z-index: 9999; display: none; align-items: center; justify-content: center;
|
||
padding: 16px;
|
||
}
|
||
.modal-back.active { display: flex; }
|
||
.modal {
|
||
background: var(--wal-sand-warm); max-width: 560px; width: 100%;
|
||
max-height: 92vh; overflow-y: auto;
|
||
border-radius: 10px; display: flex; flex-direction: column;
|
||
box-shadow: 0 20px 40px rgba(0,0,0,.3);
|
||
}
|
||
.modal-head {
|
||
background: var(--wal-deep); color: var(--wal-sand-warm);
|
||
padding: 16px 22px;
|
||
border-radius: 10px 10px 0 0;
|
||
}
|
||
.modal-tag {
|
||
font-size: .65rem; letter-spacing: .25em; text-transform: uppercase;
|
||
color: var(--wal-honey); font-weight: 700; margin-bottom: 4px;
|
||
}
|
||
.modal-head h2 {
|
||
margin: 0; font-size: 1.4rem; font-weight: 800; line-height: 1.1;
|
||
letter-spacing: -.02em;
|
||
}
|
||
.modal-body {
|
||
padding: 20px 22px; display: flex; flex-direction: column; gap: 14px;
|
||
}
|
||
.modal-body p { margin: 0; font-size: .95rem; line-height: 1.55; }
|
||
.modal-body p strong { color: var(--wal-deep); }
|
||
.modal-foot {
|
||
display: flex; gap: 10px; padding: 16px 22px 18px;
|
||
border-top: 2px solid var(--wal-coral);
|
||
background: var(--wal-sand);
|
||
border-radius: 0 0 10px 10px;
|
||
position: sticky; bottom: 0;
|
||
}
|
||
.modal-foot .btn {
|
||
flex: 1; min-height: 50px; font-size: 1rem;
|
||
box-shadow: 0 2px 6px rgba(0,0,0,.12);
|
||
}
|
||
|
||
.choices { display: flex; flex-direction: column; gap: 8px; }
|
||
.choice {
|
||
text-align: left; padding: 12px 14px;
|
||
background: white; border: 2px solid #d9d3c4;
|
||
border-radius: 8px; cursor: pointer; font-family: inherit;
|
||
display: flex; flex-direction: column; gap: 3px;
|
||
transition: border-color .15s, background .15s;
|
||
min-height: 50px;
|
||
}
|
||
.choice:hover { border-color: var(--wal-coral); background: var(--wal-sand); }
|
||
.choice-label { font-weight: 700; color: var(--wal-deep); font-size: .94rem; }
|
||
.choice-hint { font-size: .8rem; color: var(--wal-ink-soft); }
|
||
|
||
.fact-box {
|
||
padding: 12px 14px; background: var(--wal-sand);
|
||
border-left: 4px solid var(--wal-moss);
|
||
border-radius: 0 6px 6px 0; font-size: .88rem; line-height: 1.55;
|
||
}
|
||
.fact-tag {
|
||
font-size: .58rem; letter-spacing: .25em; text-transform: uppercase;
|
||
color: var(--wal-moss); font-weight: 700; display: block; margin-bottom: 4px;
|
||
}
|
||
|
||
.effect-chips { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 4px; }
|
||
.effect-chip {
|
||
font-size: .78rem; padding: 3px 8px; border-radius: 99px;
|
||
font-weight: 700; font-variant-numeric: tabular-nums;
|
||
}
|
||
.effect-chip.pos { background: rgba(74, 124, 78, .15); color: var(--wal-moss); }
|
||
.effect-chip.neg { background: rgba(184, 104, 64, .15); color: var(--wal-coral-dark); }
|
||
.effect-chip.time { background: rgba(46, 91, 110, .15); color: var(--wal-deep); }
|
||
|
||
/* =========================================================================
|
||
BUTTONS
|
||
========================================================================= */
|
||
.btn {
|
||
font-family: inherit; font-size: .95rem; font-weight: 700;
|
||
padding: 12px 20px; border: 0; border-radius: 6px; cursor: pointer;
|
||
background: var(--wal-coral); color: white;
|
||
letter-spacing: .03em; transition: background .15s, transform .08s;
|
||
display: inline-flex; align-items: center; justify-content: center;
|
||
min-height: 44px;
|
||
}
|
||
.btn:hover { background: var(--wal-coral-dark); }
|
||
.btn:active { transform: translateY(1px); }
|
||
.btn-secondary { background: var(--wal-deep); color: white; }
|
||
.btn-secondary:hover { background: var(--wal-ink); }
|
||
.btn-ghost {
|
||
background: white; color: var(--wal-deep);
|
||
border: 2px solid var(--wal-deep);
|
||
}
|
||
.btn-ghost:hover { background: var(--wal-sand); }
|
||
|
||
/* =========================================================================
|
||
END SCREEN
|
||
========================================================================= */
|
||
#screen-end {
|
||
background: linear-gradient(180deg, #1a3949 0%, #2e5b6e 100%);
|
||
color: var(--wal-sand-warm); overflow-y: auto;
|
||
padding: 36px 16px;
|
||
}
|
||
.end-inner { max-width: 640px; margin: 0 auto; }
|
||
.end-tag {
|
||
font-size: .68rem; letter-spacing: .25em; text-transform: uppercase;
|
||
color: var(--wal-honey); font-weight: 700; margin-bottom: 8px;
|
||
}
|
||
.end-inner h1 {
|
||
font-size: clamp(1.5rem, 4vw, 2.1rem); margin: 0 0 14px;
|
||
letter-spacing: -.02em; line-height: 1.1;
|
||
}
|
||
.end-text { font-size: 1.02rem; line-height: 1.55; margin: 0 0 20px; color: rgba(248,244,232,.92); }
|
||
.end-stats {
|
||
display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
|
||
gap: 8px; margin-bottom: 20px;
|
||
background: rgba(255,255,255,.08); border-radius: 8px; padding: 4px;
|
||
}
|
||
.end-stat { padding: 12px; text-align: center; }
|
||
.end-stat-label {
|
||
font-size: .58rem; letter-spacing: .18em; text-transform: uppercase;
|
||
color: rgba(248,244,232,.65); font-weight: 700; margin-bottom: 4px;
|
||
}
|
||
.end-stat-value {
|
||
font-size: 1.35rem; font-weight: 800; font-variant-numeric: tabular-nums;
|
||
color: var(--wal-honey);
|
||
}
|
||
|
||
/* =========================================================================
|
||
Mobile
|
||
========================================================================= */
|
||
@media (max-width: 720px) {
|
||
.hud { padding: 6px 10px; gap: 8px; }
|
||
.hud-stats { flex: 1; }
|
||
.hud-stat { min-width: 78px; flex: 1; padding: 4px 7px; }
|
||
.panel { max-height: 44%; padding: 12px 14px 14px; }
|
||
}
|
||
@media (max-width: 480px) {
|
||
.hud-stage { flex-basis: 100%; min-width: 0; }
|
||
.hud-stat-label .v { font-size: .62rem; }
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
|
||
<header class="ggs-header">
|
||
<a class="ggs-header-logo" id="cockpit-link" href="/schueler" title="Zurück zum Cockpit">
|
||
<img src="../../assets/img/bildLogo.png" alt="" class="ggs-header-logo-icon">
|
||
<img src="../../assets/img/textlogo_geograsim.svg" alt="GeoGraSim" class="ggs-header-logo-text">
|
||
</a>
|
||
<div class="ggs-header-separator"></div>
|
||
<span class="ggs-header-module">Die große Wanderung</span>
|
||
<span class="ggs-header-badge" id="era-badge">—</span>
|
||
|
||
<div class="ggs-header-spacer"></div>
|
||
|
||
<div class="ggs-header-actions">
|
||
<button class="ggs-btn-ghost" id="btn-info"
|
||
title="Anleitung nochmal zeigen"
|
||
onclick="if(window.GGS_TUTORIAL&&typeof window.GGS_TUTORIAL.replay==='function')window.GGS_TUTORIAL.replay();">ℹ</button>
|
||
<a class="ggs-btn-ghost" id="ggs-lehrplan-link" href="/lehrplan#wal" target="_blank" rel="noopener" title="Lehrplan-Bezug">📚</a>
|
||
<button class="ggs-btn-ghost" id="btn-reset" title="Fortschritt zurücksetzen" onclick="resetWal()">🔄</button>
|
||
<a class="ggs-btn-ghost" id="cockpit-home" href="/schueler" title="Zurück zum Cockpit">🏠</a>
|
||
</div>
|
||
</header>
|
||
|
||
<main id="app">
|
||
<!-- ========= ERA PICKER ========= -->
|
||
<section class="screen" id="screen-era">
|
||
<div class="era-head">
|
||
<div class="era-tag">Welt-Auswahl</div>
|
||
<h1>Drei Welten – derselbe Wal</h1>
|
||
<p>Du leitest einen Buckelwal auf der 6.000 km langen Wanderung von Alaska nach Hawaii. Du kannst wählen, in welcher Zeit deine Reise spielt. Die Geografie ist immer die gleiche – aber die Welt um dich herum verändert sich.</p>
|
||
</div>
|
||
|
||
<div class="era-grid">
|
||
<div class="era-card" role="button" tabindex="0" data-era="1850">
|
||
<div class="era-card-sub">Walfangzeit</div>
|
||
<div class="era-card-year">1850</div>
|
||
<div class="era-card-lead">Walfänger-Schiffe segeln durch alle Meere. Fischschwärme sind so dicht wie nie wieder. Auf der Welt leben rund 125.000 Buckelwale – aber die Zahl sinkt schnell.</div>
|
||
<div class="era-card-stats">
|
||
<span><b>~125.000</b> Wale</span>
|
||
<span><b>kein</b> Plastik</span>
|
||
<span><b>Harpune</b> als Gefahr</span>
|
||
</div>
|
||
<button class="era-card-cta" data-era-cta="1850">In dieser Welt starten →</button>
|
||
</div>
|
||
|
||
<div class="era-card" role="button" tabindex="0" data-era="1990">
|
||
<div class="era-card-sub">Erholung beginnt</div>
|
||
<div class="era-card-year">1990</div>
|
||
<div class="era-card-lead">Seit vier Jahren ist Walfang weltweit verboten. Nur noch rund 12.000 Buckelwale leben – einen anderen zu treffen, ist seltenes Glück. Erste große Frachtschiffe queren den Pazifik.</div>
|
||
<div class="era-card-stats">
|
||
<span><b>~12.000</b> Wale</span>
|
||
<span><b>wenig</b> Plastik</span>
|
||
<span><b>Fischer-Netze</b> häufig</span>
|
||
</div>
|
||
<button class="era-card-cta" data-era-cta="1990">In dieser Welt starten →</button>
|
||
</div>
|
||
|
||
<div class="era-card" role="button" tabindex="0" data-era="2026">
|
||
<div class="era-card-sub">Heute</div>
|
||
<div class="era-card-year">2026</div>
|
||
<div class="era-card-lead">Rund 80.000 Buckelwale weltweit – die Erholung läuft. Aber: dichter Schiffsverkehr, viel Plastik im Meer, militärisches Sonar, und das Wasser wird wärmer.</div>
|
||
<div class="era-card-stats">
|
||
<span><b>~80.000</b> Wale</span>
|
||
<span><b>viel</b> Plastik</span>
|
||
<span><b>Frachter</b> als Gefahr</span>
|
||
</div>
|
||
<button class="era-card-cta" data-era-cta="2026">In dieser Welt starten →</button>
|
||
</div>
|
||
</div>
|
||
|
||
<p class="era-foot">
|
||
Tipp für Lehrpersonen: Klasse in drei Gruppen aufteilen, jede Gruppe arbeitet in einer Welt. Der Vergleich in der Reflexion zeigt, was internationale Schutzabkommen bewegt haben – und welche Probleme heute neu sind.
|
||
</p>
|
||
</section>
|
||
|
||
<!-- ========= GAME ========= -->
|
||
<section class="screen" id="screen-game">
|
||
<div class="hud">
|
||
<div class="hud-stage">
|
||
<small id="hudStageHead">Etappe 1 von 4</small>
|
||
<span id="hudStageName">—</span>
|
||
</div>
|
||
<div class="hud-day">Tag <span id="hudDay">1</span></div>
|
||
<div class="hud-stats">
|
||
<div class="hud-stat">
|
||
<div class="hud-stat-label"><span>⚡ Energie</span><span class="v" id="hudEnergyV">100</span></div>
|
||
<div class="hud-stat-bar"><div class="hud-stat-fill good" id="hudEnergyB" style="width:100%"></div></div>
|
||
</div>
|
||
<div class="hud-stat">
|
||
<div class="hud-stat-label"><span>❤️ Gesund.</span><span class="v" id="hudHealthV">100</span></div>
|
||
<div class="hud-stat-bar"><div class="hud-stat-fill good" id="hudHealthB" style="width:100%"></div></div>
|
||
</div>
|
||
<div class="hud-stat">
|
||
<div class="hud-stat-label"><span>😖 Stress</span><span class="v" id="hudStressV">0</span></div>
|
||
<div class="hud-stat-bar"><div class="hud-stat-fill good" id="hudStressB" style="width:0%"></div></div>
|
||
</div>
|
||
</div>
|
||
<div class="hud-calf" id="hudCalf" title="Kalb geboren">🐳</div>
|
||
</div>
|
||
|
||
<div id="map-wrap">
|
||
<div id="map"></div>
|
||
</div>
|
||
|
||
<div class="panel" id="panel">
|
||
<div class="panel-tag" id="panelTag">Position</div>
|
||
<p class="panel-text" id="panelText">—</p>
|
||
<div class="panel-actions" id="panelActions"></div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- ========= END SCREEN ========= -->
|
||
<section class="screen" id="screen-end">
|
||
<div class="end-inner">
|
||
<div class="end-tag" id="endTag">— Ende der Reise —</div>
|
||
<h1 id="endTitle">—</h1>
|
||
<p class="end-text" id="endText">—</p>
|
||
<div class="end-stats" id="endStats"></div>
|
||
<div class="fact-box" id="endFact" style="display:none">
|
||
<span class="fact-tag">Wusstest du?</span>
|
||
<span id="endFactText"></span>
|
||
</div>
|
||
<div style="display:flex; gap:8px; flex-wrap:wrap; margin-top: 20px;">
|
||
<button class="btn" onclick="returnToEraPick()">Andere Welt erkunden</button>
|
||
<button class="btn btn-secondary" onclick="resetCurrentEra()">Dieselbe Welt nochmal</button>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
</main>
|
||
|
||
<!-- ========= MODAL ========= -->
|
||
<div class="modal-back" id="modal">
|
||
<div class="modal">
|
||
<div class="modal-head">
|
||
<div class="modal-tag" id="modalTag">—</div>
|
||
<h2 id="modalTitle">—</h2>
|
||
</div>
|
||
<div class="modal-body" id="modalBody"></div>
|
||
<div class="modal-foot" id="modalFoot"></div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Plattform-Wrapper-Injection (Atlas-Wrapper setzt diese Vars) -->
|
||
<script>/*__WAL_INJECTION__*/</script>
|
||
|
||
<!-- Leaflet -->
|
||
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"
|
||
integrity="sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo=" crossorigin=""></script>
|
||
|
||
<!-- Sim-Engine + UI -->
|
||
<script src="engine.js"></script>
|
||
|
||
<script>
|
||
// =============================================================================
|
||
// WAL · UI-Controller
|
||
// =============================================================================
|
||
(function () {
|
||
'use strict';
|
||
|
||
// ---- Shorthand ----
|
||
const $ = (id) => document.getElementById(id);
|
||
const clamp = (v, lo, hi) => Math.max(lo, Math.min(hi, v));
|
||
const escapeHtml = (s) => String(s == null ? '' : s).replace(/[<>&"]/g, c => ({'<':'<','>':'>','&':'&','"':'"'})[c]);
|
||
|
||
// ---- State ----
|
||
let state = null;
|
||
let map = null;
|
||
let walMarker = null;
|
||
let routeLine = null;
|
||
let booted = false;
|
||
|
||
// ---- Helpers ----
|
||
function lsKey(era) { return 'ggs-save-wal-' + era; }
|
||
function autoSave() {
|
||
if (!state) return;
|
||
try { localStorage.setItem(lsKey(state.era), JSON.stringify(state)); } catch (_) {}
|
||
}
|
||
function loadSave(era) {
|
||
try {
|
||
const raw = localStorage.getItem(lsKey(era));
|
||
if (!raw) return null;
|
||
const s = JSON.parse(raw);
|
||
return (s && typeof s === 'object' && s.era === era) ? s : null;
|
||
} catch (_) { return null; }
|
||
}
|
||
function clearSave(era) { try { localStorage.removeItem(lsKey(era)); } catch (_) {} }
|
||
|
||
function setBadge(era) {
|
||
const b = $('era-badge');
|
||
if (!era) { b.textContent = '—'; b.className = 'ggs-header-badge'; return; }
|
||
b.textContent = String(era);
|
||
b.className = 'ggs-header-badge era-' + era;
|
||
}
|
||
|
||
function showScreen(id) {
|
||
document.querySelectorAll('.screen').forEach(s => s.classList.remove('active'));
|
||
$(id).classList.add('active');
|
||
}
|
||
|
||
// ---- Stats-HUD ----
|
||
function updateHud() {
|
||
$('hudDay').textContent = state.day;
|
||
$('hudStageHead').textContent = 'Etappe ' + (state.stage + 1) + ' von ' + WalEngine.STAGE_COUNT;
|
||
const stage = WalEngine.getStage(state.era, state.stage);
|
||
$('hudStageName').textContent = stage ? stage.name : '—';
|
||
setStat('Energy', state.energy, 'high');
|
||
setStat('Health', state.health, 'high');
|
||
setStat('Stress', state.stress, 'low');
|
||
$('hudCalf').classList.toggle('show', !!state.hasCalf);
|
||
}
|
||
function setStat(key, val, goodWhen) {
|
||
const v = $('hud' + key + 'V');
|
||
const b = $('hud' + key + 'B');
|
||
v.textContent = Math.round(val);
|
||
b.style.width = val + '%';
|
||
const cls = (goodWhen === 'low')
|
||
? (val <= 33 ? 'good' : val <= 66 ? 'ok' : 'bad')
|
||
: (val >= 66 ? 'good' : val >= 33 ? 'ok' : 'bad');
|
||
b.className = 'hud-stat-fill ' + cls;
|
||
}
|
||
|
||
// ---- Modal ----
|
||
function showModal(tag, title, bodyHtml, footHtml) {
|
||
$('modalTag').textContent = tag;
|
||
$('modalTitle').textContent = title;
|
||
$('modalBody').innerHTML = bodyHtml;
|
||
$('modalFoot').innerHTML = footHtml || '';
|
||
$('modal').classList.add('active');
|
||
}
|
||
function closeModal() { $('modal').classList.remove('active'); }
|
||
|
||
// ---- Map ----
|
||
function initMap() {
|
||
if (map) return;
|
||
map = L.map('map', {
|
||
zoomControl: true,
|
||
attributionControl: true,
|
||
minZoom: 3,
|
||
maxZoom: 6,
|
||
scrollWheelZoom: true,
|
||
}).setView([40, -145], 4);
|
||
|
||
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
||
attribution: '© OpenStreetMap',
|
||
maxZoom: 12,
|
||
}).addTo(map);
|
||
|
||
routeLine = L.polyline(WalEngine.routePoints, {
|
||
color: '#b86840', weight: 3, opacity: .75, dashArray: '6,6',
|
||
}).addTo(map);
|
||
|
||
const startIcon = L.divIcon({
|
||
className: 'wal-marker', html: '🐋', iconSize: [32, 32], iconAnchor: [16, 16],
|
||
});
|
||
walMarker = L.marker(WalEngine.routePoints[0], { icon: startIcon }).addTo(map);
|
||
|
||
L.marker([57.0, -133.8]).bindPopup('Frederick Sound · Start').addTo(map);
|
||
L.marker([20.85, -156.5]).bindPopup('Maui · Ziel').addTo(map);
|
||
}
|
||
function moveWhale(routeIdx) {
|
||
if (!walMarker || !WalEngine.routePoints[routeIdx]) return;
|
||
const pt = WalEngine.routePoints[routeIdx];
|
||
walMarker.setLatLng(pt);
|
||
map.panTo(pt, { animate: true, duration: .8 });
|
||
}
|
||
|
||
// ---- Tutorial ----
|
||
const TUTORIAL_STEPS = [
|
||
{ tag: 'Anleitung', title: 'Du bist eine Buckelwal-Kuh',
|
||
body: '<p>Du steuerst einen großen Buckelwal – ein Meeres-Säugetier, das jedes Jahr eine sehr weite Reise macht.</p><p>Vor dir liegt die längste Reise deines Lebens: 6.000 km von Alaska nach Hawaii. Du arbeitest dich durch <strong>4 Etappen</strong>. In jeder Etappe wartet eine Begegnung, bei der du entscheiden musst.</p>' },
|
||
{ tag: 'Anleitung', title: 'Drei Werte zeigen, wie es dir geht',
|
||
body: '<p><strong>⚡ Energie</strong> ist dein Speck – fällt sie auf 0, schaffst du es nicht mehr nach Hawaii.</p><p><strong>❤️ Gesundheit</strong> sinkt bei Verletzungen – fällt sie auf 0, ist die Reise vorbei.</p><p><strong>😖 Stress</strong> steigt bei bedrohlichen Situationen. Zu viel Stress kostet dich Energie über die Zeit.</p>' },
|
||
{ tag: 'Anleitung', title: 'Jede Entscheidung hat Folgen',
|
||
body: '<p>Bei jeder Begegnung wählst du zwischen zwei oder drei Wegen. Der sichere Weg kostet oft mehr Energie. Der riskante Weg kann gut ausgehen – oder Gesundheit kosten.</p><p>Nach jeder Entscheidung siehst du, was passiert, und eine <strong>Faktbox</strong> erklärt den Hintergrund.</p>' },
|
||
{ tag: 'Anleitung', title: 'Dein Ziel: Maui erreichen',
|
||
body: '<p>Auf Hawaii wirst du im Winter dein Kalb zur Welt bringen. Ankunft mit Kalb ist das Hauptziel.</p><p>Die Reise dauert ungefähr <strong>90 Tage</strong>. Wörter mit gestrichelter Unterlinie kannst du antippen – dann öffnet sich eine kurze Erklärung.</p>' },
|
||
];
|
||
let tutorialStep = 0;
|
||
let postTutorialCallback = null; // wird nach Tutorial-Close gefeuert
|
||
|
||
function startTutorial(onAfter) {
|
||
tutorialStep = 0;
|
||
postTutorialCallback = (typeof onAfter === 'function') ? onAfter : null;
|
||
renderTutorialStep();
|
||
}
|
||
function renderTutorialStep() {
|
||
const t = TUTORIAL_STEPS[tutorialStep];
|
||
const isLast = tutorialStep >= TUTORIAL_STEPS.length - 1;
|
||
const counter = '<div style="text-align:center;font-size:.7rem;color:var(--wal-ink-soft);letter-spacing:.15em;margin-top:8px">' + (tutorialStep+1) + ' / ' + TUTORIAL_STEPS.length + '</div>';
|
||
const foot = isLast
|
||
? '<button class="btn" onclick="closeTutorial()">Los geht\'s</button>'
|
||
: '<button class="btn btn-ghost" onclick="closeTutorial()">Überspringen</button>'
|
||
+ '<button class="btn" onclick="nextTutorialStep()">Weiter →</button>';
|
||
showModal(t.tag, t.title, t.body + counter, foot);
|
||
}
|
||
function nextTutorialStep() {
|
||
tutorialStep++;
|
||
if (tutorialStep >= TUTORIAL_STEPS.length) return closeTutorial();
|
||
renderTutorialStep();
|
||
}
|
||
function closeTutorial() {
|
||
closeModal();
|
||
try { localStorage.setItem('wal.tutorial.seen', '1'); } catch (_) {}
|
||
const cb = postTutorialCallback;
|
||
postTutorialCallback = null;
|
||
if (typeof cb === 'function') {
|
||
// Mini-Delay, damit Modal-Schließ-Animation nicht mit nächstem Modal kollidiert
|
||
setTimeout(cb, 60);
|
||
}
|
||
}
|
||
window.startTutorial = startTutorial;
|
||
window.closeTutorial = closeTutorial;
|
||
window.nextTutorialStep = nextTutorialStep;
|
||
|
||
// ---- Plattform-Convention: GGS_TUTORIAL ----
|
||
window.GGS_TUTORIAL = {
|
||
simId: 'wal',
|
||
title: '🐋 Die große Wanderung',
|
||
replay: function () { startTutorial(null); },
|
||
};
|
||
|
||
// ---- Era-Picker ----
|
||
function bindEraPicker() {
|
||
document.querySelectorAll('[data-era-cta]').forEach(btn => {
|
||
btn.addEventListener('click', (e) => {
|
||
e.stopPropagation();
|
||
startEra(parseInt(btn.dataset.eraCta, 10));
|
||
});
|
||
});
|
||
document.querySelectorAll('.era-card[data-era]').forEach(card => {
|
||
card.addEventListener('click', (e) => {
|
||
if (e.target.tagName === 'BUTTON') return;
|
||
startEra(parseInt(card.dataset.era, 10));
|
||
});
|
||
card.addEventListener('keydown', (e) => {
|
||
if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); startEra(parseInt(card.dataset.era, 10)); }
|
||
});
|
||
});
|
||
}
|
||
|
||
function startEra(era) {
|
||
setBadge(era);
|
||
// Existierenden Save laden?
|
||
const saved = loadSave(era);
|
||
if (saved && saved.status === 'playing' && (saved.stage > 0 || saved.eventIndex > 0)) {
|
||
if (confirm('Du hast in dieser Welt schon angefangen (Etappe ' + (saved.stage+1) + '). Weitermachen?\n\nAbbrechen = neu starten.')) {
|
||
state = saved;
|
||
enterGame();
|
||
return;
|
||
}
|
||
clearSave(era);
|
||
}
|
||
state = WalEngine.newState(era);
|
||
showScreen('screen-game');
|
||
setTimeout(() => {
|
||
initMap();
|
||
if (map) map.invalidateSize();
|
||
updateHud();
|
||
autoSave();
|
||
// Tutorial bei Erststart, sonst direkt Stage-Intro
|
||
const seen = (() => { try { return localStorage.getItem('wal.tutorial.seen') === '1'; } catch (_) { return false; } })();
|
||
if (!seen) {
|
||
// Pending-Callback feuert nach Tutorial-Close (Skip oder „Los geht's")
|
||
startTutorial(showStageIntro);
|
||
} else {
|
||
showStageIntro();
|
||
}
|
||
}, 80);
|
||
}
|
||
|
||
function enterGame() {
|
||
showScreen('screen-game');
|
||
setTimeout(() => {
|
||
initMap();
|
||
if (map) map.invalidateSize();
|
||
moveWhale(WalEngine.pointIndexFor(state.era, state.stage, state.eventIndex));
|
||
updateHud();
|
||
setPanel('Etappe ' + (state.stage+1) + ' · Begegnung ' + (state.eventIndex+1),
|
||
'Du setzt deine Reise fort.',
|
||
'<button class="btn" onclick="nextEvent()">Weiter →</button>');
|
||
}, 80);
|
||
}
|
||
|
||
// ---- Stage / Event Flow ----
|
||
function showStageIntro() {
|
||
const s = WalEngine.getStage(state.era, state.stage);
|
||
if (!s) return;
|
||
closeModal();
|
||
setBadge(state.era);
|
||
moveWhale(WalEngine.pointIndexFor(state.era, state.stage, 0) - 1);
|
||
showModal(
|
||
'Etappe ' + (state.stage + 1) + ' von ' + WalEngine.STAGE_COUNT + ' · ' + s.season,
|
||
s.name,
|
||
'<p class="panel-text">' + s.intro.text + '</p>'
|
||
+ '<div class="fact-box"><span class="fact-tag">Hintergrund</span>' + s.intro.fact + '</div>',
|
||
'<button class="btn" onclick="closeModal(); nextEvent();">Weiterreisen →</button>'
|
||
);
|
||
autoSave();
|
||
}
|
||
|
||
function setPanel(tag, text, actionsHtml) {
|
||
$('panelTag').textContent = tag;
|
||
$('panelText').innerHTML = text;
|
||
$('panelActions').innerHTML = actionsHtml || '';
|
||
}
|
||
|
||
function nextEvent() {
|
||
// Tages-Drift zwischen Begegnungen
|
||
state.energy = clamp(state.energy - 3, 0, 100);
|
||
state.stress = clamp(state.stress - 2, 0, 100);
|
||
state.day += 5 + Math.floor(Math.random() * 4);
|
||
updateHud();
|
||
autoSave();
|
||
|
||
if (state.energy <= 0) return endRun('starvation');
|
||
if (state.health <= 0) return endRun('injury');
|
||
|
||
const stage = WalEngine.getStage(state.era, state.stage);
|
||
// Etappenende?
|
||
if (state.eventIndex >= stage.events.length) {
|
||
state.stage++;
|
||
state.eventIndex = 0;
|
||
autoSave();
|
||
if (state.stage >= WalEngine.STAGE_COUNT) return endRun('arrival');
|
||
return showStageIntro();
|
||
}
|
||
|
||
const ev = stage.events[state.eventIndex];
|
||
moveWhale(WalEngine.pointIndexFor(state.era, state.stage, state.eventIndex));
|
||
setPanel(
|
||
'Etappe ' + (state.stage + 1) + ' · Begegnung ' + (state.eventIndex + 1),
|
||
'Du näherst dich einer neuen Situation: <strong>' + ev.title + '</strong>',
|
||
'<button class="btn" onclick="openEvent()">Hinsehen →</button>'
|
||
);
|
||
}
|
||
|
||
function openEvent() {
|
||
const stage = WalEngine.getStage(state.era, state.stage);
|
||
const ev = stage.events[state.eventIndex];
|
||
const choicesHtml = '<div class="choices">'
|
||
+ ev.choices.map((c, i) =>
|
||
'<button class="choice" onclick="chooseOption(' + i + ')">'
|
||
+ '<span class="choice-label">' + c.label + '</span>'
|
||
+ (c.hint ? '<span class="choice-hint">' + c.hint + '</span>' : '')
|
||
+ '</button>'
|
||
).join('')
|
||
+ '</div>';
|
||
showModal(
|
||
ev.tag, ev.title,
|
||
'<p class="panel-text">' + ev.text + '</p>' + choicesHtml,
|
||
''
|
||
);
|
||
}
|
||
window.openEvent = openEvent;
|
||
|
||
function chooseOption(i) {
|
||
const stage = WalEngine.getStage(state.era, state.stage);
|
||
const ev = stage.events[state.eventIndex];
|
||
const choice = ev.choices[i];
|
||
|
||
// Effekte anwenden
|
||
const eff = choice.effects || {};
|
||
if (typeof eff.energy === 'number') state.energy = clamp(state.energy + eff.energy, 0, 100);
|
||
if (typeof eff.health === 'number') state.health = clamp(state.health + eff.health, 0, 100);
|
||
if (typeof eff.stress === 'number') state.stress = clamp(state.stress + eff.stress, 0, 100);
|
||
if (choice.sideEffect && typeof choice.sideEffect.day === 'number') state.day += choice.sideEffect.day;
|
||
if (choice.calf) state.hasCalf = true;
|
||
state.stressPeak = Math.max(state.stressPeak, state.stress);
|
||
state.encountersDone++;
|
||
state.choicesLog.push({stage: state.stage, event_id: ev.id || ev.title, choice_label: choice.label});
|
||
if (ev.fact) { state.factsReadList.push(ev.id || ev.title); state.factsRead++; }
|
||
updateHud();
|
||
moveWhale(WalEngine.pointIndexFor(state.era, state.stage, state.eventIndex) + 1);
|
||
autoSave();
|
||
|
||
// Auflösung + Fakt
|
||
const factHtml = ev.fact
|
||
? '<div class="fact-box"><span class="fact-tag">Wusstest du?</span>' + ev.fact + '</div>'
|
||
: '';
|
||
showModal(
|
||
ev.tag, ev.title,
|
||
'<p class="panel-text">' + choice.result + '</p>'
|
||
+ buildEffectChips(eff, choice.sideEffect)
|
||
+ factHtml,
|
||
'<button class="btn" onclick="afterChoice()">Weiter →</button>'
|
||
);
|
||
}
|
||
window.chooseOption = chooseOption;
|
||
|
||
function buildEffectChips(eff, sideEffect) {
|
||
const chips = [];
|
||
if (typeof eff.energy === 'number' && eff.energy !== 0) chips.push(['energy', eff.energy, eff.energy>0?'pos':'neg']);
|
||
if (typeof eff.health === 'number' && eff.health !== 0) chips.push(['health', eff.health, eff.health>0?'pos':'neg']);
|
||
if (typeof eff.stress === 'number' && eff.stress !== 0) chips.push(['stress', eff.stress, eff.stress<0?'pos':'neg']);
|
||
if (sideEffect && typeof sideEffect.day === 'number' && sideEffect.day !== 0) chips.push(['day', sideEffect.day, 'time']);
|
||
if (!chips.length) return '';
|
||
const map = {energy:'⚡', health:'❤️', stress:'😖', day:'🗓'};
|
||
return '<div class="effect-chips">' + chips.map(([k, v, cls]) =>
|
||
'<span class="effect-chip ' + cls + '">' + map[k] + ' ' + (v>0?'+':'') + v + (k==='day'?' Tage':'') + '</span>'
|
||
).join('') + '</div>';
|
||
}
|
||
|
||
function afterChoice() {
|
||
state.eventIndex++;
|
||
closeModal();
|
||
autoSave();
|
||
if (state.energy <= 0) return endRun('starvation');
|
||
if (state.health <= 0) return endRun('injury');
|
||
nextEvent();
|
||
}
|
||
window.afterChoice = afterChoice;
|
||
window.nextEvent = nextEvent;
|
||
window.closeModal = closeModal;
|
||
|
||
// ---- Reise-Ende ----
|
||
function endRun(reason) {
|
||
state.status = (reason === 'arrival') ? 'won' : 'lost';
|
||
state.endReason = reason;
|
||
autoSave();
|
||
closeModal();
|
||
showScreen('screen-end');
|
||
|
||
const meta = WalEngine.endMeta(state, reason);
|
||
$('endTag').textContent = meta.tag;
|
||
$('endTitle').textContent = meta.title;
|
||
$('endText').textContent = meta.text;
|
||
$('endFactText').textContent = meta.fact || '';
|
||
$('endFact').style.display = meta.fact ? 'block' : 'none';
|
||
$('endStats').innerHTML = ''
|
||
+ statBlock('Welt', state.era)
|
||
+ statBlock('Tage', state.day)
|
||
+ statBlock('Etappen', state.stage)
|
||
+ statBlock('Energie', Math.round(state.energy))
|
||
+ statBlock('Gesund.', Math.round(state.health))
|
||
+ statBlock('Stress-Max', Math.round(state.stressPeak));
|
||
|
||
submitAssessment(reason);
|
||
}
|
||
function statBlock(label, val) {
|
||
return '<div class="end-stat"><div class="end-stat-label">' + label + '</div><div class="end-stat-value">' + val + '</div></div>';
|
||
}
|
||
|
||
// ---- Submit (Plattform-Endpoint) ----
|
||
function submitAssessment(reason) {
|
||
if (!window.WAL_SESSION_ID || !window.WAL_API_BASE) return; // Direkt-URL / Gast → kein Submit
|
||
const outcome = (reason === 'arrival')
|
||
? (state.hasCalf ? 'arrival_with_calf' : 'arrival_no_calf')
|
||
: reason; // starvation | injury | whaling
|
||
const level = (state.era === 1850) ? 1 : (state.era === 1990) ? 2 : 3;
|
||
const stars = computeStars(reason);
|
||
const score = computeScore(reason);
|
||
|
||
fetch(window.WAL_API_BASE + '/progress.php', {
|
||
method: 'POST',
|
||
headers: {'Content-Type': 'application/json'},
|
||
credentials: 'same-origin',
|
||
body: JSON.stringify({
|
||
sim_id: 'wal',
|
||
action: 'submit_assessment',
|
||
data: {
|
||
level,
|
||
stars,
|
||
score,
|
||
duration_ms: (window.__GGS_LIVE__ && window.__GGS_LIVE__.activeMs) || 0,
|
||
completed: outcome.startsWith('arrival'),
|
||
results: {
|
||
era: state.era,
|
||
days_total: state.day,
|
||
outcome,
|
||
energy_end: Math.round(state.energy),
|
||
health_end: Math.round(state.health),
|
||
stress_peak: Math.round(state.stressPeak),
|
||
stages_completed: state.stage,
|
||
encounters_total: state.encountersDone,
|
||
choices_log: state.choicesLog,
|
||
facts_seen: state.factsRead,
|
||
},
|
||
},
|
||
}),
|
||
}).catch(() => {});
|
||
}
|
||
function computeStars(reason) {
|
||
if (reason === 'arrival' && state.hasCalf) return 5;
|
||
if (reason === 'arrival') return 4;
|
||
if (state.stage >= 2) return 2;
|
||
return 1;
|
||
}
|
||
function computeScore(reason) {
|
||
const outcome = (reason === 'arrival') ? (state.hasCalf ? 'arrival_with_calf' : 'arrival_no_calf') : reason;
|
||
const base = outcome === 'arrival_with_calf' ? 70
|
||
: outcome === 'arrival_no_calf' ? 50
|
||
: 0;
|
||
const bonus = Math.round(state.health * 0.12) + Math.round(state.energy * 0.08);
|
||
const malus = Math.round(state.stressPeak * 0.05);
|
||
const timePen = Math.max(0, state.day - 90) * 0.3;
|
||
return clamp(0, 100, Math.round(base + bonus - malus - timePen));
|
||
}
|
||
|
||
// ---- Live-Snapshot fürs Cockpit (Plattform-Convention) ----
|
||
window.GGS_LIVE_STATE = function () {
|
||
if (!state || !booted) return null;
|
||
const stage = WalEngine.getStage(state.era, state.stage);
|
||
const total = WalEngine.totalEvents(state.era);
|
||
const seen = WalEngine.eventsSeenBefore(state.era, state.stage) + state.eventIndex;
|
||
const pct = Math.round((seen / Math.max(1, total)) * 100);
|
||
const healthStatus = (state.energy + state.health) / 2 >= 60 ? 'good'
|
||
: (state.energy + state.health) / 2 >= 30 ? 'ok'
|
||
: 'struggle';
|
||
return {
|
||
simId: 'wal',
|
||
era: state.era,
|
||
stage: state.stage + 1,
|
||
stageName: stage ? stage.name : '—',
|
||
day: state.day,
|
||
energy: Math.round(state.energy),
|
||
health: Math.round(state.health),
|
||
stress: Math.round(state.stress),
|
||
stressPeak: Math.round(state.stressPeak),
|
||
hasCalf: !!state.hasCalf,
|
||
progressPct: pct,
|
||
health_status: healthStatus,
|
||
encountersDone: state.encountersDone,
|
||
};
|
||
};
|
||
|
||
// ---- Reset + Back to Era Picker ----
|
||
function resetWal() {
|
||
if (!state) { showScreen('screen-era'); setBadge(null); return; }
|
||
if (!confirm('Aktuelle Reise abbrechen und neu starten?\nDer Fortschritt in dieser Welt geht verloren.')) return;
|
||
clearSave(state.era);
|
||
const era = state.era;
|
||
state = WalEngine.newState(era);
|
||
closeModal();
|
||
showScreen('screen-game');
|
||
setTimeout(() => {
|
||
if (map) map.invalidateSize();
|
||
moveWhale(0);
|
||
updateHud();
|
||
showStageIntro();
|
||
}, 60);
|
||
}
|
||
window.resetWal = resetWal;
|
||
|
||
function resetCurrentEra() {
|
||
const era = state.era;
|
||
clearSave(era);
|
||
state = WalEngine.newState(era);
|
||
showScreen('screen-game');
|
||
setTimeout(() => {
|
||
if (map) map.invalidateSize();
|
||
moveWhale(0);
|
||
updateHud();
|
||
showStageIntro();
|
||
}, 60);
|
||
}
|
||
window.resetCurrentEra = resetCurrentEra;
|
||
|
||
function returnToEraPick() {
|
||
state = null;
|
||
setBadge(null);
|
||
showScreen('screen-era');
|
||
}
|
||
window.returnToEraPick = returnToEraPick;
|
||
|
||
// =============================================================================
|
||
// BOOT
|
||
// =============================================================================
|
||
function boot() {
|
||
// Glossar-Tooltip mit lokalen Fallback-Snippets initialisieren.
|
||
// Sobald in DB Wal-Begriffe angelegt sind, greift die API automatisch.
|
||
if (window.GlossarTooltip) {
|
||
window.GlossarTooltip.init({
|
||
apiBase: (window.WAL_API_BASE || '/php/api'),
|
||
moduleId: 'wal',
|
||
fallback: WalEngine.glossarFallback || {},
|
||
});
|
||
}
|
||
|
||
bindEraPicker();
|
||
booted = true;
|
||
|
||
// Falls Lehrperson Era erzwingt → direkt los
|
||
const forcedEra = window.WAL_FORCED_ERA;
|
||
if (forcedEra && [1850, 1990, 2026].includes(forcedEra)) {
|
||
startEra(forcedEra);
|
||
return;
|
||
}
|
||
|
||
// Sonst Era-Picker zeigen
|
||
showScreen('screen-era');
|
||
setBadge(null);
|
||
}
|
||
|
||
window.addEventListener('DOMContentLoaded', boot);
|
||
|
||
})();
|
||
</script>
|
||
|
||
</body>
|
||
</html>
|