From 4976582400f2f29e43156f5531a701e04e9e2d6c Mon Sep 17 00:00:00 2001 From: Thomas Date: Sun, 23 Aug 2026 14:32:02 +0200 Subject: [PATCH] Heli Routenplanung: Timing-basierte Wertung (3 Stufen) + Bonus vor rotem Hint MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- App/sims/heli/game.html | 50 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 44 insertions(+), 6 deletions(-) diff --git a/App/sims/heli/game.html b/App/sims/heli/game.html index 6432060..1edb569 100644 --- a/App/sims/heli/game.html +++ b/App/sims/heli/game.html @@ -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') {