Heli: Treibstoff kann leer werden (1★-Takeover) + Live-Score fuer Tag-Farbe + Aktuell-Status/Phase

- start.html: Sprit-Floor 0.05->0; Treibstoff-leer loest Chefpilotin-Takeover aus (stars:1)
- game.html: GGS_LIVE_STATE liefert progressiven score (Routing/Start/Landung/Gesamt) -> farbige Tag-Balken
- teacher.html: Phasen-Map + Status-Spalte fuer mission/briefing/unterwegs (kein leeres "...")

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-08-23 12:48:26 +02:00
parent 6dfcf4d57f
commit 7986bb2eb6
3 changed files with 32 additions and 3 deletions
+13
View File
@@ -480,6 +480,19 @@ window.GGS_LIVE_STATE = function () {
totalStars: landingDone ? (game.totalStars || 0) : null,
failed: !!game.missionFailed,
missionStartedAt: game.missionStartedAt || null,
// Laufender 0100-Score für die Ampelfarbe im Lehrer-Live (Tag-Balken).
// Progressiv: Routing-Genauigkeit, dann Ø mit Start-/Landungssternen; am Ende
// die Gesamtsterne. In der Auftragswahl (mission/briefing) kein Score.
score: (function(){
if (!phase || phase === 'mission' || phase === 'briefing') return null;
if (landingDone && game.totalStars != null) return Math.round(Math.max(0,Math.min(5,game.totalStars))/5*100);
var parts = [];
if (routeLen > 1) parts.push(Math.max(0, Math.min(100, (game.planTotal?(game.planCorrect||0)/game.planTotal:0)*100 - (game.planAttempts||0)*4)));
if (startDone && game.startScore != null) parts.push(Math.max(0,Math.min(5,game.startScore))/5*100);
if (landingDone && game.landingScore != null) parts.push(Math.max(0,Math.min(5,game.landingScore))/5*100);
if (!parts.length) return null;
return Math.round(parts.reduce(function(a,b){return a+b;},0)/parts.length);
})(),
};
};
+16 -1
View File
@@ -1723,7 +1723,22 @@
if (wantFlap) thrustMult += 0.8;
if (h.vy < -40) thrustMult += Math.min(1.0, -h.vy / 160);
else if (h.vy > 60) thrustMult = Math.max(0.4, thrustMult - 0.5);
h.fuel = Math.max(0.05, h.fuel - 0.005 * altFactor * thrustMult * dt);
h.fuel = Math.max(0, h.fuel - 0.005 * altFactor * thrustMult * dt);
// Treibstoff leer → Chefpilotin übernimmt (wie 3 Berührungen): schlechteste
// Start-Bewertung (1★). Nur einmal auslösen und nicht, wenn ohnehin schon
// ein Takeover läuft.
if (h.fuel <= 0 && state.mode !== 'autopilot' && !state._fuelEmpty) {
state._fuelEmpty = true;
state.mode = 'autopilot';
stopAllAmbient();
state.autopilotT = 0;
h.invul = 999;
state.takeoverReason = 'Treibstoff leer — die Chefpilotin bringt den Helikopter sicher zurück. Beim nächsten Start früher auf den Verbrauch achten und nicht zu hoch steigen (dünne Luft = ×8 Verbrauch).';
state.takeoverShort = 'Treibstoff leer';
state.radio.current = 'Pilot: Tank leer! Ich übernehme.';
state.radio.shown = 0; state.radio.nextAt = 99;
try { playRadio('warn_fuel'); } catch(_){}
}
// Visuelle Höhenwarnung: roter Pulse oben + Fuel-HUD blinkt
// Greift sobald altRatio > 0.9 (= im obersten 10 % der Bildhöhe).
var inDangerZone = altRatio > 0.9;
+3 -2
View File
@@ -856,7 +856,7 @@ var LIVE_PRIMARY_FIELDS = {
],
'heli': [
{k:'phase', label:'Phase', align:'center', fmt:function(v){
var m = {plan:'🗺️ Routenplan', start:'🛫 Start', flight:'✈️ Flug', approach:'🎯 Anflug', landing:'🛬 Landung', results:'🏁 Ende'};
var m = {mission:'📋 Auftragswahl', plan:'🗺️ Routenplan', briefing:'📋 Briefing', start:'🛫 Start', flight:'✈️ Flug', approach:'🎯 Anflug', landing:'🛬 Landung', results:'🏁 Ende'};
return m[v] || (v ? v : '—');
}},
{k:'missionId', label:'Auftrag', align:'left', fmt:function(v, row){
@@ -905,7 +905,8 @@ var LIVE_PRIMARY_FIELDS = {
var s = row.state||{};
if (v === true) return '<span style="color:#c07a6b;font-weight:700">⚠ Chefpilot</span>';
if (s.phase === 'results') return '<span style="color:#5a8a5e;font-weight:700">✓ Erfolg</span>';
return '<span style="color:#8a8a8a"></span>';
if (s.phase === 'mission' || !s.phase) return '<span style="color:#8a8a8a">wählt Auftrag</span>';
return '<span style="color:#4a7c8a;font-weight:600">🟢 unterwegs</span>';
}},
],
'fluss': [