diff --git a/frontend/src/views/minigames/TaxiGame.vue b/frontend/src/views/minigames/TaxiGame.vue index b645404..b2db69d 100644 --- a/frontend/src/views/minigames/TaxiGame.vue +++ b/frontend/src/views/minigames/TaxiGame.vue @@ -309,6 +309,7 @@ export default { ,houseNumbers: {} ,prevTaxiX: 250 ,prevTaxiY: 250 + ,skipRedLightOneFrame: false } }, computed: { @@ -489,6 +490,10 @@ export default { this.taxi.y = 250 - this.taxi.height/2; // Zentriert in der Mitte this.taxi.angle = 0; // Fahrtrichtung nach oben this.taxi.speed = 0; // Geschwindigkeit zurücksetzen + // Vorherige Position und Skip-Flag setzen, damit erste Prüfung nicht triggert + this.prevTaxiX = this.taxi.x; + this.prevTaxiY = this.taxi.y; + this.skipRedLightOneFrame = true; // Aktuelle Tile-Position setzen this.currentTile.row = 0; @@ -551,6 +556,11 @@ export default { if (moved) { this.currentTile.row = newRow; this.currentTile.col = newCol; + // Nach Tile-Wechsel: eine Frame lang Rotlicht-Prüfung aussetzen + // und prev-Position auf aktuelle setzen, um false positives zu vermeiden + this.prevTaxiX = this.taxi.x; + this.prevTaxiY = this.taxi.y; + this.skipRedLightOneFrame = true; // Radar mit 12% Wahrscheinlichkeit aktivieren beim Betreten des neuen Tiles this.activeRadar = Math.random() < 0.12; // Achse und Position festlegen: Messung findet bei 150px vom Rand statt @@ -1585,7 +1595,11 @@ export default { } // Rotlichtverstöße prüfen if (!this.isPaused) { - this.checkRedLightViolation(); + if (!this.skipRedLightOneFrame) { + this.checkRedLightViolation(); + } + // Einmal-Flag nach der Prüfung zurücksetzen + if (this.skipRedLightOneFrame) this.skipRedLightOneFrame = false; } // Prüfe Hindernisse nur wenn das Spiel nicht pausiert ist @@ -1608,6 +1622,8 @@ export default { const phase = this.getTrafficLightPhase(this.currentTile.col + (this.currentMap.offsetX||0), this.currentTile.row + (this.currentMap.offsetY||0)); const rects = this.getVirtualStopLineRects(tileSize, approaches); + // Kleine Toleranz für Segmenttreffer (dick=5px, daher +-4px ausreichend) + const tol = 4; // Einseitige Prüfung je Richtung und Phasen-Logik // Zu prüfende Ecken laut Vorgabe: @@ -1627,19 +1643,41 @@ export default { const vx = currCX - prevCX; const vy = currCY - prevCY; - // WICHTIG: Nur prüfen wenn das Taxi tatsächlich bewegt wurde (mindestens 1 Pixel) - const movementThreshold = 1; - if (Math.abs(vx) < movementThreshold && Math.abs(vy) < movementThreshold) { - return; // Keine Bewegung, keine Verletzung möglich - } + // DEBUG: Umfassendes Logging + console.log('=== ROTLICHT DEBUG ==='); + console.log('Tile:', this.currentTile.col, this.currentTile.row); + console.log('TileType:', tileType); + console.log('Approaches:', approaches); + console.log('Phase:', phase, 'isHorRed:', isHorRed, 'isVerRed:', isVerRed); + console.log('Taxi Position: prev(' + prevCX.toFixed(1) + ',' + prevCY.toFixed(1) + ') curr(' + currCX.toFixed(1) + ',' + currCY.toFixed(1) + ')'); + console.log('Movement: vx=' + vx.toFixed(1) + ', vy=' + vy.toFixed(1)); + console.log('StopLine Rects:', rects); + + // Hinweis: Keine globale Bewegungsschwelle mehr. + // Die Richtungsbedingungen (vy/vx > 0 bzw. < 0) in den jeweiligen Fällen + // stellen bereits sicher, dass nur bei tatsächlicher Bewegung geprüft wird. + // Top-Band: von oben nach unten (prev oben, curr unten), Eintritt über obere Bandkante y0 if (rects.top && approaches.top) { const x0 = rects.top.x, x1 = rects.top.x + rects.top.width; const y0 = rects.top.y, y1 = rects.top.y + rects.top.height; - const inX = (prevCX >= x0 && prevCX <= x1) || (currCX >= x0 && currCX <= x1); - // Nur verletzen wenn das Taxi tatsächlich die Linie überquert (von außerhalb zu innerhalb) - if (vy > 0 && inX && prevCY < y0 && currCY >= y0 && isVerRed) { + // Für horizontale Haltelinien: Y-Position muss in der Nähe der Haltelinie sein + const isNearStopLine = currCY >= (y0 - 50) && currCY <= (y1 + 50); + // Zusätzlich: innerhalb des horizontalen Liniensegments (X-Span) sein + const withinXSpan = ( + (prevCX >= x0 - tol && prevCX <= x1 + tol) || + (currCX >= x0 - tol && currCX <= x1 + tol) || + // oder Strecke zwischen prevCX und currCX schneidet [x0, x1] + (Math.min(prevCX, currCX) <= x1 + tol && Math.max(prevCX, currCX) >= x0 - tol) + ); + + console.log('TOP-Band: x0=' + x0 + ', x1=' + x1 + ', y0=' + y0 + ', y1=' + y1); + console.log('TOP: vy=' + vy + ', prevCY=' + prevCY.toFixed(1) + ', currCY=' + currCY.toFixed(1) + ', isVerRed=' + isVerRed + ', isNearStopLine=' + isNearStopLine + ', withinXSpan=' + withinXSpan); + console.log('TOP: prevCY < y0 =', prevCY < y0, ', currCY >= y0 =', currCY >= y0); + + if (vy > 0 && prevCY < y0 && currCY >= y0 && isVerRed && isNearStopLine && withinXSpan) { + console.log('🚨 TOP-Band VIOLATION!'); violated = true; } } @@ -1647,9 +1685,21 @@ export default { if (rects.bottom && approaches.bottom) { const x0 = rects.bottom.x, x1 = rects.bottom.x + rects.bottom.width; const y0 = rects.bottom.y, y1 = rects.bottom.y + rects.bottom.height; - const inX = (prevCX >= x0 && prevCX <= x1) || (currCX >= x0 && currCX <= x1); - // Nur verletzen wenn das Taxi tatsächlich die Linie überquert (von außerhalb zu innerhalb) - if (vy < 0 && inX && prevCY > y1 && currCY <= y1 && isVerRed) { + // Für horizontale Haltelinien: Y-Position muss in der Nähe der Haltelinie sein + const isNearStopLine = currCY >= (y0 - 50) && currCY <= (y1 + 50); + // Zusätzlich: innerhalb des horizontalen Liniensegments (X-Span) sein + const withinXSpan = ( + (prevCX >= x0 - tol && prevCX <= x1 + tol) || + (currCX >= x0 - tol && currCX <= x1 + tol) || + (Math.min(prevCX, currCX) <= x1 + tol && Math.max(prevCX, currCX) >= x0 - tol) + ); + + console.log('BOTTOM-Band: x0=' + x0 + ', x1=' + x1 + ', y0=' + y0 + ', y1=' + y1); + console.log('BOTTOM: vy=' + vy + ', prevCY=' + prevCY.toFixed(1) + ', currCY=' + currCY.toFixed(1) + ', isVerRed=' + isVerRed + ', isNearStopLine=' + isNearStopLine + ', withinXSpan=' + withinXSpan); + console.log('BOTTOM: prevCY > y1 =', prevCY > y1, ', currCY <= y1 =', currCY <= y1); + + if (vy < 0 && prevCY > y1 && currCY <= y1 && isVerRed && isNearStopLine && withinXSpan) { + console.log('🚨 BOTTOM-Band VIOLATION!'); violated = true; } } @@ -1657,9 +1707,21 @@ export default { if (rects.left && approaches.left) { const x0 = rects.left.x, x1 = rects.left.x + rects.left.width; const y0 = rects.left.y, y1 = rects.left.y + rects.left.height; - const inY = (prevCY >= y0 && prevCY <= y1) || (currCY >= y0 && currCY <= y1); - // Nur verletzen wenn das Taxi tatsächlich die Linie überquert (von außerhalb zu innerhalb) - if (vx > 0 && inY && prevCX < x0 && currCX >= x0 && isHorRed) { + // Für vertikale Haltelinien: X-Position muss in der Nähe der Haltelinie sein + const isNearStopLine = currCX >= (x0 - 50) && currCX <= (x1 + 50); + // Zusätzlich: innerhalb des vertikalen Liniensegments (Y-Span) sein + const withinYSpan = ( + (prevCY >= y0 - tol && prevCY <= y1 + tol) || + (currCY >= y0 - tol && currCY <= y1 + tol) || + (Math.min(prevCY, currCY) <= y1 + tol && Math.max(prevCY, currCY) >= y0 - tol) + ); + + console.log('LEFT-Band: x0=' + x0 + ', x1=' + x1 + ', y0=' + y0 + ', y1=' + y1); + console.log('LEFT: vx=' + vx + ', prevCX=' + prevCX.toFixed(1) + ', currCX=' + currCX.toFixed(1) + ', isHorRed=' + isHorRed + ', isNearStopLine=' + isNearStopLine + ', withinYSpan=' + withinYSpan); + console.log('LEFT: prevCX < x0 =', prevCX < x0, ', currCX >= x0 =', currCX >= x0); + + if (vx > 0 && prevCX < x0 && currCX >= x0 && isHorRed && isNearStopLine && withinYSpan) { + console.log('🚨 LEFT-Band VIOLATION!'); violated = true; } } @@ -1667,19 +1729,37 @@ export default { if (rects.right && approaches.right) { const x0 = rects.right.x, x1 = rects.right.x + rects.right.width; const y0 = rects.right.y, y1 = rects.right.y + rects.right.height; - const inY = (prevCY >= y0 && prevCY <= y1) || (currCY >= y0 && currCY <= y1); - // Nur verletzen wenn das Taxi tatsächlich die Linie überquert (von außerhalb zu innerhalb) - if (vx < 0 && inY && prevCX > x1 && currCX <= x1 && isHorRed) { + // Für vertikale Haltelinien: X-Position muss in der Nähe der Haltelinie sein + const isNearStopLine = currCX >= (x0 - 50) && currCX <= (x1 + 50); + // Zusätzlich: innerhalb des vertikalen Liniensegments (Y-Span) sein + const withinYSpan = ( + (prevCY >= y0 - tol && prevCY <= y1 + tol) || + (currCY >= y0 - tol && currCY <= y1 + tol) || + (Math.min(prevCY, currCY) <= y1 + tol && Math.max(prevCY, currCY) >= y0 - tol) + ); + + console.log('RIGHT-Band: x0=' + x0 + ', x1=' + x1 + ', y0=' + y0 + ', y1=' + y1); + console.log('RIGHT: vx=' + vx + ', prevCX=' + prevCX.toFixed(1) + ', currCX=' + currCX.toFixed(1) + ', isHorRed=' + isHorRed + ', isNearStopLine=' + isNearStopLine + ', withinYSpan=' + withinYSpan); + console.log('RIGHT: prevCX < x0 =', prevCX < x0, ', currCX >= x0 =', currCX >= x0); + + if (vx < 0 && prevCX > x1 && currCX <= x1 && isHorRed && isNearStopLine && withinYSpan) { + console.log('🚨 RIGHT-Band VIOLATION!'); violated = true; } } + console.log('=== FINAL RESULT ==='); + console.log('Violated:', violated); + if (violated) { // Entprellen: pro Tile nur einmal pro tatsächlichem Übertritt zählen const key = `${this.currentTile.col},${this.currentTile.row}`; const now = Date.now(); const last = this.redLightLastCount[key] || 0; + console.log('Violation detected! Key:', key, 'Last:', last, 'Now:', now, 'Diff:', now - last); + if (now - last > 500) { // 0.5s Sperrzeit + console.log('✅ VIOLATION REGISTERED! Count:', this.redLightViolations + 1); this.redLightLastCount[key] = now; this.redLightViolations += 1; this.playRedLightSound(); @@ -1697,8 +1777,12 @@ export default { } }); } + } else { + console.log('❌ VIOLATION IGNORED (too soon)'); } } + + console.log('=== END ROTLICHT DEBUG ==='); }, checkRadarMeasurement() { @@ -1892,6 +1976,10 @@ export default { this.taxi.y = 250 - this.taxi.height/2; // Mitte des Canvas (500px / 2) this.taxi.speed = 0; this.taxi.angle = 0; + // Prev-Position synchronisieren und erste Prüfung aussetzen + this.prevTaxiX = this.taxi.x; + this.prevTaxiY = this.taxi.y; + this.skipRedLightOneFrame = true; }, isTaxiOnRoad() { @@ -2360,41 +2448,45 @@ export default { ); }, - // Haltelinien: 120px vom Rand, 5px dick, 40px breit, an allen vier Zufahrten + // Haltelinien: Basierend auf den korrigierten Koordinaten drawStopLinesOnMainCanvas(ctx, size, approaches = { top:true, right:true, bottom:true, left:true }) { - const margin = 120; // Basis-Abstand vom Rand - const extra = 30; // 10px zurück (von +40 auf +30) - const m = margin + extra; const thickness = 5; - const width = 60; // +20px breiter (vorher 40) ctx.save(); ctx.fillStyle = '#ffffff'; - // Oben (horizontale Linie, zentriert) - if (approaches.top) ctx.fillRect((size - width) / 2 - 30, m - thickness, width, thickness); - // Unten - if (approaches.bottom) ctx.fillRect((size - width) / 2 + 30, size - m, width, thickness); - // Links (vertikale Linie, zentriert) - if (approaches.left) ctx.fillRect(m - thickness, (size - width) / 2 + 30, thickness, width); - // Rechts - if (approaches.right) ctx.fillRect(size - m, (size - width) / 2 - 30, thickness, width); + + // Basierend auf den korrigierten Koordinaten: + // Links: sichtbar: 145/294 - 150/313 + if (approaches.left) ctx.fillRect(145, 294, thickness, 19); + + // Rechts: sichtbar: 150/186 - 355/293 + if (approaches.right) ctx.fillRect(350, 186, thickness, 107); + + // Unten: sichtbar: 250/360 - 312/365 + if (approaches.bottom) ctx.fillRect(250, 360, 62, thickness); + + // Oben: berechnet aus den anderen Angaben (symmetrisch zu unten) + if (approaches.top) ctx.fillRect(250, 5, 62, thickness); + ctx.restore(); }, // Liefert die virtuellen (breit verdoppelten) Haltelinienrechtecke pro Seite, ohne zu zeichnen getVirtualStopLineRects(size, approaches = { top:true, right:true, bottom:true, left:true }) { - const margin = 120; - const extra = 30; - const m = margin + extra; const thickness = 5 * 2; // doppelte Breite für Erkennung - const width = 60; // Länge der Linie unverändert const rects = {}; - // Oben - if (approaches.top) rects.top = { x: (size - width) / 2 - 30, y: m - thickness, width: width, height: thickness }; - // Unten - if (approaches.bottom) rects.bottom = { x: (size - width) / 2 + 30, y: size - m, width: width, height: thickness }; - // Links - if (approaches.left) rects.left = { x: m - thickness, y: (size - width) / 2 + 30, width: thickness, height: width }; - // Rechts - if (approaches.right) rects.right = { x: size - m, y: (size - width) / 2 - 30, width: thickness, height: width }; + + // Basierend auf den korrigierten Koordinaten: + // Links: virtuell: 145/186 - 150/313; sichtbar: 145/294 - 150/313 + if (approaches.left) rects.left = { x: 145, y: 186, width: thickness, height: 127 }; + + // Rechts: virtuell: 350/186 - 355/313; sichtbar: 150/186 - 355/293 + if (approaches.right) rects.right = { x: 350, y: 186, width: thickness, height: 127 }; + + // Unten: virtuell: 187/360- 312/365; sichtbar: 250/360 - 312/365 + if (approaches.bottom) rects.bottom = { x: 187, y: 360, width: 125, height: thickness }; + + // Oben: berechnet aus den anderen Angaben (symmetrisch zu unten) + if (approaches.top) rects.top = { x: 187, y: 5, width: 125, height: thickness }; + return rects; }, // Liefert, von welchen Seiten eine Straße an dieses Tile anbindet