Heli Routenplanung: Timing-basierte Wertung (3 Stufen) + Bonus vor rotem Hint

Pro Wegpunkt: 2=Treffer VOR der roten Einblendung (Bonus) · 1=waehrend der 10 s · 0=danach.
computePlanStars() aus Ø-Timing (5/4/3/2/1); Fehlklick-Abbruch bleibt 0. wpRevealAt merkt
Reveal-Zeitpunkt, onPlanClick klassifiziert. Bonus im Endbildschirm + Assessment (planBeforeRed).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-08-23 14:32:02 +02:00
parent bf5b026b46
commit 4976582400
+44 -6
View File
@@ -769,8 +769,9 @@ function submitHeliMission() {
var dt = heliAssessment.startMs ? (Date.now() - heliAssessment.startMs) : 0;
var planTotal = (game.route || []).length > 1 ? (game.route.length - 1) : 0;
var planPct = planTotal > 0 ? Math.round((game.planCorrect || 0) / planTotal * 100) : 0;
var planStars = planPct >= 90 ? 5 : planPct >= 70 ? 4 : planPct >= 50 ? 3 : planPct >= 30 ? 2 : 1;
if (game.planTakeover) planStars = 0;
var planStars = computePlanStars();
var _tim = game.wpTiming || [];
var planBeforeRed = _tim.filter(function(x){return x===2;}).length; // Treffer vor rotem Hint (Bonus)
var stars = game.totalStars || 0;
var score = stars * 20;
try {
@@ -802,6 +803,8 @@ function submitHeliMission() {
planCorrect: game.planCorrect || 0,
planTotal: planTotal,
planPct: planPct,
planBeforeRed: planBeforeRed, // Treffer VOR dem roten Hint (Bonus)
planTiming: _tim.join(''), // je WP: 2=vor · 1=während · 0=nach
startScore: game.startScore || 0,
landingScore: game.landingScore || 0,
startTakeover: !!game.startTakeover,
@@ -1099,6 +1102,8 @@ function initPlanPhase() {
game.planTakeover = false; // wird true bei 15 falschen Versuchen
game.planPhaseAudioDone = false; // wird true bei Engine phase-end-Event
hintedWaypoints = {}; // Reset der Marker-Hints fuer neue Mission
game.wpRevealAt = {}; // je Wegpunkt: Zeitpunkt, ab dem der rote Hint einblendet
game.wpTiming = []; // je gefundenem Wegpunkt: 2=vor Rot · 1=während · 0=nach Rot
// V3-Engine (2026-06-01): Audio-Pipe wird von Tool-generierter Timeline
// gesteuert. Sequenzielle Sprach-Pipe + parallel UI-Sound-Layer.
@@ -1433,6 +1438,17 @@ function onPlanClick(e) {
if (dist < 1.5) {
// Richtig!
game.planCorrect++;
// Timing-Stufe dieses Wegpunkts festhalten: 2 = VOR dem roten Hint (Bonus,
// reine Navigation), 1 = WÄHREND der 10-s-Einblendung, 0 = NACH voller Anzeige.
(function(){
var wpKey = game.route[game.planIndex];
var revealAt = (game.wpRevealAt || {})[wpKey];
var tier;
if (revealAt == null) tier = 2; // Hint noch nicht gestartet
else if (Date.now() - revealAt < 10000) tier = 1; // während der Einblendung
else tier = 0; // roter Punkt schon voll da
(game.wpTiming = game.wpTiming || []).push(tier);
})();
// Fehlversuche-Zaehler bleibt GLOBAL pro Mission (kein Reset bei richtig).
// Aber die Hinweisliste verschwindet — beim naechsten Wegpunkt sieht man
// einen sauberen Stand, alte Fail-Hinweise lenken nicht mehr ab.
@@ -1841,11 +1857,23 @@ function onLandingMessage(e) {
// =========================================================
// PHASE 6: AUSWERTUNG
// =========================================================
// Routenplanungs-Sterne aus dem TIMING (nicht mehr nur Genauigkeit):
// je Wegpunkt 2=vor rotem Hint (Bonus) · 1=während der 10-s-Einblendung · 0=danach.
// Ø-Punkte → 5/4/3/2/1★. Chefpilot-Übernahme (15 Fehlklicks) bleibt 0★.
// Fallback auf die alte Genauigkeits-Wertung, falls kein Timing erfasst wurde.
function computePlanStars() {
if (game.planTakeover) return 0;
var t = game.wpTiming || [];
if (!t.length) {
var pct = (game.route && game.route.length > 1) ? Math.round((game.planCorrect||0) / (game.route.length-1) * 100) : 100;
return pct >= 90 ? 5 : pct >= 70 ? 4 : pct >= 50 ? 3 : pct >= 30 ? 2 : 1;
}
var avg = t.reduce(function(a,b){ return a+b; }, 0) / t.length;
return avg >= 1.6 ? 5 : avg >= 1.1 ? 4 : avg >= 0.6 ? 3 : avg > 0 ? 2 : 1;
}
function initResultsPhase() {
var planPct = game.route.length > 1 ? Math.round(game.planCorrect / (game.route.length-1) * 100) : 100;
var planStars = planPct >= 90 ? 5 : planPct >= 70 ? 4 : planPct >= 50 ? 3 : planPct >= 30 ? 2 : 1;
// Pilot-Uebernahme nach 15 Fehlversuchen → keine Sterne fuer die Zielsuche
if (game.planTakeover) planStars = 0;
var planStars = computePlanStars();
var totalStars = Math.round((planStars + game.startScore + game.landingScore) / 3);
game.totalStars = totalStars;
// Snapshot des zuletzt abgeschlossenen Einsatzes — damit die Lehrer-Ansicht
@@ -1888,6 +1916,12 @@ function initResultsPhase() {
} else {
comment = baseComment;
}
// Bonus-Feedback: Ziele, die VOR der roten Einblendung getroffen wurden.
var planBeforeRed = (game.wpTiming||[]).filter(function(x){return x===2;}).length;
if (planBeforeRed > 0 && !game.planTakeover) {
comment += ' 🎯 Bonus: ' + planBeforeRed + ' Ziel' + (planBeforeRed>1?'e':'')
+ ' vor der roten Einblendung getroffen — top navigiert!';
}
} else {
cls = 'failed';
icon = '🧑‍✈️';
@@ -2065,6 +2099,10 @@ function startHeliAudioEngineForPlan() {
}
}
else if (ev.type === 'wp-marker-reveal') {
// Ab jetzt blendet der rote Zielpunkt ein → Zeitpunkt merken für die
// Timing-Wertung (Klick davor = Bonus, während der 10 s = mittel, danach = niedrig).
if (!game.wpRevealAt) game.wpRevealAt = {};
if (game.wpRevealAt[ev.waypoint] == null) game.wpRevealAt[ev.waypoint] = Date.now();
showWaypointHint(ev.waypoint, ev.fadeInMs || 10000);
}
else if (ev.type === 'phase-end') {