diff --git a/frontend/src/views/minigames/TaxiGame.vue b/frontend/src/views/minigames/TaxiGame.vue index cbff4a6..27df5e9 100644 --- a/frontend/src/views/minigames/TaxiGame.vue +++ b/frontend/src/views/minigames/TaxiGame.vue @@ -359,7 +359,7 @@ export default { ,carImage: null // Geladenes Auto-Bild ,lastCarGeneration: 0 // Zeitstempel der letzten Autos-Generierung ,carGenerationInterval: 1000 // Autos-Generierung alle 1000ms (1 Sekunde) prüfen - ,carSpawnProbability: 0.2 // 20% Wahrscheinlichkeit pro Sekunde + ,carSpawnProbability: 1.0 // 100% Wahrscheinlichkeit pro Sekunde (temporär für Debugging) } }, computed: { @@ -455,6 +455,91 @@ export default { return item ? item.num : null; }, + // Prüfe, ob das Auto am Abbiegepunkt (Tile-Zentrumslinien) ist + // Gibt bevorzugte Zielrichtung zurück (string) oder null, wenn nicht abbiegen + shouldCarTurnAtIntersection(car) { + // Fahrzeugmitte verwenden + const cx = car.x + car.width / 2; + const cy = car.y + car.height / 2; + const rx = cx / this.tiles.size; + const ry = cy / this.tiles.size; + + const tileType = this.mapTileTypeToStreetCoordinates(this.getCurrentTileType()); + + // Toleranzfenster um die Mittellinien, damit der Trigger nicht frame-genau sein muss + const tol = 0.01; // ~5px bei 500px Tilegröße + const nearYCenter = ry > 0.5 - tol && ry < 0.5 + tol; + const nearXCenter = rx > 0.5 - tol && rx < 0.5 + tol; + + switch (tileType) { + case 'cornerBottomRight': + // unten -> rechts + if (car.direction === 'up' && nearYCenter) return 'right'; + if (car.direction === 'left' && nearXCenter) return 'down'; + return null; + case 'cornerBottomLeft': + // unten -> links + if (car.direction === 'up' && nearYCenter) return 'left'; + if (car.direction === 'right' && nearXCenter) return 'down'; + return null; + case 'cornerTopRight': + // oben -> rechts + if (car.direction === 'down' && nearYCenter) return 'right'; + if (car.direction === 'left' && nearXCenter) return 'up'; + return null; + case 'cornerTopLeft': + // oben -> links + if (car.direction === 'down' && nearYCenter) return 'left'; + if (car.direction === 'right' && nearXCenter) return 'up'; + return null; + default: + // Kreuzung: bei Zentrumslinien abbiegen erlaubt – Richtung später wählen + if (nearXCenter || nearYCenter) return this.getRandomTargetDirection(car.direction); + return null; + } + }, + + // Liefert eine sinnvolle Zielrichtung je Tile-Typ und aktueller Richtung (keine Kehrtwende) + getRandomTargetDirection(currentDirection) { + const tileType = this.mapTileTypeToStreetCoordinates(this.getCurrentTileType()); + const options = []; + + switch (tileType) { + case 'cornerBottomRight': + // unten -> rechts: von 'up' nach 'right', von 'left' nach 'down' + if (currentDirection === 'up') options.push('right'); + if (currentDirection === 'left') options.push('down'); + break; + case 'cornerBottomLeft': + // unten -> links: von 'up' nach 'left', von 'right' nach 'down' + if (currentDirection === 'up') options.push('left'); + if (currentDirection === 'right') options.push('down'); + break; + case 'cornerTopRight': + // oben -> rechts: von 'down' nach 'right', von 'left' nach 'up' + if (currentDirection === 'down') options.push('right'); + if (currentDirection === 'left') options.push('up'); + break; + case 'cornerTopLeft': + // oben -> links: von 'down' nach 'left', von 'right' nach 'up' + if (currentDirection === 'down') options.push('left'); + if (currentDirection === 'right') options.push('up'); + break; + case 'cross': + // Kreuzung: links/rechts bei vertikaler Bewegung, hoch/runter bei horizontaler Bewegung + if (currentDirection === 'up' || currentDirection === 'down') options.push('left', 'right'); + if (currentDirection === 'left' || currentDirection === 'right') options.push('up', 'down'); + break; + default: + // gerade Strecken: weiterfahren + options.push(currentDirection); + } + + // Fallback + if (options.length === 0) options.push(currentDirection); + return options[Math.floor(Math.random() * options.length)]; + }, + // Zeichnet die Straßen-Nr. auf die Minimap, je nach Tile-Typ und Position (pro Name nur einmal) drawStreetNumbersOnMinimap(ctx, x, y, size, tileType, col, row, drawnNames) { if (!this.currentMap || !Array.isArray(this.currentMap.tileStreets)) return; @@ -749,7 +834,7 @@ export default { // Game Loop stoppen if (this.gameLoop) { - cancelAnimationFrame(this.gameLoop); + clearTimeout(this.gameLoop); this.gameLoop = null; } @@ -1354,20 +1439,23 @@ export default { // 20% Wahrscheinlichkeit pro Sekunde if (!this.isPaused && Math.random() < this.carSpawnProbability) { + console.log('[🚗 GENERATION] Attempting to spawn car, current cars:', this.cars.length); this.spawnCar(now); } }, // Spawne ein neues Auto spawnCar(now = Date.now()) { - // Begrenze die Anzahl der Autos - if (this.cars.length >= 8) { + // Begrenze die Anzahl der Autos (TEST: nur 1 Auto) + if (this.cars.length >= 1) { + console.log('[🚗 SPAWN] Max cars reached:', this.cars.length); return; } // Spawne Auto auf einer befahrbaren Position const spawnPosition = this.getRandomCarSpawnPosition(); if (!spawnPosition) { + console.log('[🚗 SPAWN] No valid spawn position found'); return; // Keine gültige Spawn-Position gefunden } @@ -1390,39 +1478,44 @@ export default { targetLane: null // Zielspur basierend auf Zielrichtung }; + console.log(`[🚗 CREATE] New car created - ID: ${car.id}, Position: x=${car.x.toFixed(2)}, y=${car.y.toFixed(2)}, direction=${car.direction}, angle=${car.angle.toFixed(2)}, speed=${car.speed.toFixed(2)}`); + this.cars.push(car); + console.log(`[🚗 CREATE] Added car with ID: ${car.id}, Total cars now: ${this.cars.length}`); + console.log(`[🚗 CREATE] Car details: x=${car.x.toFixed(2)}, y=${car.y.toFixed(2)}, direction=${car.direction}, speed=${car.speed.toFixed(2)}`); }, // Ermittelt erlaubte Spawn-Richtungen basierend auf dem aktuellen Tile-Typ + // WICHTIG: Diese Methode gibt die BEWEGUNGSRICHTUNGEN zurück, nicht die Spawn-Seiten! getAllowedSpawnDirections() { const tileType = this.getCurrentTileType(); - // Basierend auf der allowedDirections Logik aus AdminTaxiToolsView.vue + // Basierend auf der tile-Struktur und welche Seiten offen sind switch (tileType) { case 'cornertopleft': - return ['left', 'up']; // Kann von links und oben spawnen + return ['left', 'up']; // Bewegt sich nach links (von rechts spawnen) und nach oben (von unten spawnen) case 'cornertopright': - return ['right', 'up']; // Kann von rechts und oben spawnen + return ['right', 'up']; // Bewegt sich nach rechts (von links spawnen) und nach oben (von unten spawnen) case 'cornerbottomleft': - return ['left', 'down']; // Kann von links und unten spawnen + return ['left', 'down']; // Bewegt sich nach links (von rechts spawnen) und nach unten (von oben spawnen) case 'cornerbottomright': - return ['right', 'down']; // Kann von rechts und unten spawnen + return ['left', 'up']; // Bewegt sich nach links (von rechts spawnen) und nach oben (von unten spawnen) case 'horizontal': case 'fuelhorizontal': - return ['left', 'right']; // Kann von links und rechts spawnen + return ['left', 'right']; // Bewegt sich nach links (von rechts spawnen) und nach rechts (von links spawnen) case 'vertical': case 'fuelvertical': - return ['up', 'down']; // Kann von oben und unten spawnen + return ['up', 'down']; // Bewegt sich nach oben (von unten spawnen) und nach unten (von oben spawnen) case 'cross': return ['left', 'right', 'up', 'down']; // Kann von allen Seiten spawnen case 'tup': - return ['up', 'left', 'right']; // Kann von oben, links und rechts spawnen + return ['up', 'left', 'right']; // Bewegt sich nach oben, links und rechts case 'tdown': - return ['down', 'left', 'right']; // Kann von unten, links und rechts spawnen + return ['down', 'left', 'right']; // Bewegt sich nach unten, links und rechts case 'tleft': - return ['left', 'up', 'down']; // Kann von links, oben und unten spawnen + return ['left', 'up', 'down']; // Bewegt sich nach links, oben und unten case 'tright': - return ['right', 'up', 'down']; // Kann von rechts, oben und unten spawnen + return ['right', 'up', 'down']; // Bewegt sich nach rechts, oben und unten default: return []; // Keine erlaubten Spawn-Richtungen } @@ -1431,87 +1524,97 @@ export default { // Finde eine zufällige befahrbare Spawn-Position für ein Auto getRandomCarSpawnPosition() { if (!this.currentMap || !this.currentMap.tiles) { + console.log('[🚗 SPAWN] No current map or tiles'); return null; } const tileSize = this.tiles.size; + const tileType = this.getCurrentTileType(); - // Definiere die erlaubten Spawn-Positionen (relativ 0-1) - // Strengere Spur-Bänder: links/oben bis 0.45, rechts/unten ab 0.55 - const spawnPositions = [ - // Links spawnen, nach rechts fahren, auf der rechten Straßenseite (y=0.55-0.625) - { relativeX: 0, relativeY: 0.55 + Math.random() * 0.075, angle: 0, direction: 'right' }, - // Rechts spawnen, nach links fahren, auf der linken Straßenseite (y=0.375-0.45) - { relativeX: 1, relativeY: 0.375 + Math.random() * 0.075, angle: Math.PI, direction: 'left' }, - // Oben spawnen, nach unten fahren, auf der linken Straßenseite (x=0.375-0.45) - { relativeX: 0.375 + Math.random() * 0.075, relativeY: 0, angle: Math.PI / 2, direction: 'down' }, - // Unten spawnen, nach oben fahren, auf der rechten Straßenseite (x=0.55-0.625) - // Direkt im befahrbaren Bereich starten (relY ≈ 0.78) - { relativeX: 0.55 + Math.random() * 0.075, relativeY: 1, angle: -Math.PI / 2, direction: 'up' } - ]; - - // Wähle eine zufällige Spawn-Position - const spawnPos = spawnPositions[Math.floor(Math.random() * spawnPositions.length)]; - - // Debug: Aktueller Tile-Typ und Wahl - try { - const tileRaw = this.getCurrentTileType(); - const streetType = this.mapTileTypeToStreetCoordinates(tileRaw); - if (spawnPos.direction === 'up') { - console.log('[Cars][debug] fromBottom on tile', { tileRaw, streetType }); - } - } catch (_) {} + // Erlaubte Spawn-Richtungen für den aktuellen Tile-Typ ermitteln + const allowedDirections = this.getAllowedSpawnDirections(); + console.log('[🚗 SPAWN] Tile type:', tileType, 'Allowed directions:', allowedDirections); + console.log('[🚗 SPAWN] Tile size:', tileSize); + + if (allowedDirections.length === 0) { + console.log('[🚗 SPAWN] No allowed spawn directions'); + return null; // Keine erlaubten Spawn-Richtungen + } + + // Definiere die Spawn-Positionen (relativ 0-1) basierend auf der Richtung + // Autos müssen von außerhalb des Tiles spawnen, um dann hineinzufahren + // Korrekte Fahrspuren: links (y=0.55-0.625), rechts (y=0.375-0.45), oben (x=0.375-0.45), unten (x=0.55-0.625) + // WICHTIG: direction ist die Bewegungsrichtung, nicht die Spawn-Seite! + // Auto-Dimensionen + const carWidth = 60; + const carHeight = 50; + + // Spawn-Positionen basierend auf den tatsächlichen Straßenlinien in streetCoordinates.json + // cornerBottomRight hat vertikale Linien bei x=0.375 (187.5px) und x=0.625 (312.5px) + // cornerBottomRight hat horizontale Linien bei y=0.375 (187.5px) und y=0.625 (312.5px) + // Spur-Bänder: innerer Rand (≈ 15px bei 500px → 0.03 rel) + const laneMargin = 0.03; + const spawnPositionsMap = { + 'right': { relativeX: -0.02, pickRelY: () => 0.5 + laneMargin + Math.random() * (0.625 - 0.5 - 2 * laneMargin), angle: 0, direction: 'right', laneAxis: 'H' }, + 'left': { relativeX: 1.02, pickRelY: () => 0.375 + laneMargin + Math.random() * (0.5 - 0.375 - 2 * laneMargin), angle: Math.PI, direction: 'left', laneAxis: 'H' }, + 'down': { pickRelX: () => 0.375 + laneMargin + Math.random() * (0.5 - 0.375 - 2 * laneMargin), relativeY: -0.02, angle: Math.PI / 2, direction: 'down', laneAxis: 'V' }, + 'up': { pickRelX: () => 0.5 + laneMargin + Math.random() * (0.625 - 0.5 - 2 * laneMargin), relativeY: 1.02, angle: -Math.PI / 2, direction: 'up', laneAxis: 'V' } + }; + + // Wähle eine zufällige erlaubte Richtung + const randomDirection = allowedDirections[Math.floor(Math.random() * allowedDirections.length)]; + const spawnPos = spawnPositionsMap[randomDirection]; + + if (!spawnPos) { + return null; + } + + console.log('[🚗 SPAWN] Selected direction:', randomDirection); + console.log('[🚗 SPAWN] Spawn position data:', spawnPos); + // Konvertiere relative Koordinaten zu absoluten Pixeln - const x = spawnPos.relativeX * tileSize; - const y = spawnPos.relativeY * tileSize; + let x = (spawnPos.relativeX != null ? spawnPos.relativeX : (spawnPos.pickRelX ? spawnPos.pickRelX() : 0.5)) * tileSize; + let y = (spawnPos.relativeY != null ? spawnPos.relativeY : (spawnPos.pickRelY ? spawnPos.pickRelY() : 0.5)) * tileSize; + + console.log('[🚗 SPAWN] Before adjustment - x:', x.toFixed(2), 'y:', y.toFixed(2)); + + // Passe Koordinaten an: Die Spawn-Position ist die Straßenlinie, aber wir brauchen die linke obere Ecke des Autos + // Das Auto muss auf der Linie zentriert sein + switch (randomDirection) { + case 'left': + case 'right': + // Horizontale Bewegung: Auto muss auf Y-Linie zentriert sein + y = y - carHeight / 2; // Zentriere Auto auf horizontaler Linie + if (x < 0) x = -carWidth; // Links außerhalb + if (x > tileSize) x = tileSize; // Rechts außerhalb + break; + case 'up': + case 'down': + // Vertikale Bewegung: Auto muss auf X-Linie zentriert sein + x = x - carWidth / 2; // Zentriere Auto auf vertikaler Linie + if (y < 0) y = -carHeight; // Oben außerhalb + if (y > tileSize) y = tileSize; // Unten außerhalb + break; + } + + console.log('[🚗 SPAWN] After adjustment - x:', x.toFixed(2), 'y:', y.toFixed(2)); + console.log('[🚗 SPAWN] Direction:', randomDirection, 'Absolute:', { x: x.toFixed(2), y: y.toFixed(2) }); + + // laneCoord speichert die Zielspur (Mitte) auf der Querachse + // Verwende die bereits berechneten absoluten x/y, um exakt dieselbe Spur zu halten + const laneCoord = spawnPos.laneAxis === 'H' + ? (y + carHeight / 2) // Y-Mitte + : (x + carWidth / 2); // X-Mitte - // Intensive Debug-Ausgaben für dir==='up' - if (spawnPos.direction === 'up') { - console.log('[Cars][debug] fromBottom candidate', { - relX: Number(spawnPos.relativeX.toFixed(3)), - relY: Number(spawnPos.relativeY.toFixed(3)), - x: Math.round(x), y: Math.round(y) - }); - } - - // Prüfe ob Position befahrbar ist (für Autos: Rand entspannt) - if (this.isPositionDriveableForCars(x, y, spawnPos.direction)) { - return { - x: x, - y: y, - angle: spawnPos.angle, - direction: spawnPos.direction - }; - } - - // Fallback: Versuche andere Positionen - for (let i = 0; i < spawnPositions.length; i++) { - const pos = spawnPositions[i]; - const testX = pos.relativeX * tileSize; - const testY = pos.relativeY * tileSize; - if (pos.direction === 'up') { - console.log('[Cars][debug] fromBottom fallback-candidate', { - relX: Number(pos.relativeX.toFixed(3)), - relY: Number(pos.relativeY.toFixed(3)), - x: Math.round(testX), y: Math.round(testY), ok: this.isPositionDriveable(testX, testY) - }); - } - - if (this.isPositionDriveableForCars(testX, testY, pos.direction)) { - return { - x: testX, - y: testY, - angle: pos.angle, - direction: pos.direction - }; - } - } - if (spawnPos.direction === 'up') { - console.warn('[Cars][debug] fromBottom: no driveable spawn found'); - } - - return null; // Keine gültige Position gefunden + return { + x: x, + y: y, + angle: spawnPos.angle, + direction: spawnPos.direction, + laneAxis: spawnPos.laneAxis, + laneCoord: laneCoord + }; }, // Prüfe ob eine Position befahrbar ist @@ -1526,21 +1629,83 @@ export default { return streetCoordinates.isPointDriveable(relativeX, relativeY, streetTileType, 1); }, - // Fahrzeuge (KI) dürfen in den Screen hineingleiten: Ränder als befahrbar behandeln - isPositionDriveableForCars(x, y, direction) { - // Toleranz: 1px über/unter dem Canvasrand als befahrbar werten - if (x < 0 || y < 0 || x > this.tiles.size || y > this.tiles.size) { - // Wenn wir am Rand nach innen fahren, erlauben - // up: y nimmt ab; down: y nimmt zu; right: x nimmt zu; left: x nimmt ab - if (direction === 'up' && y >= -1) return true; - if (direction === 'down' && y <= this.tiles.size + 1) return true; - if (direction === 'right' && x <= this.tiles.size + 1) return true; - if (direction === 'left' && x >= -1) return true; - // weit außerhalb: nicht befahrbar + // Prüfe ob Auto auf einer befahrbaren Straße ist (wie Taxi-Logik) + isCarOnRoad(car) { + const centerX = car.x + car.width / 2; + const centerY = car.y + car.height / 2; + + // Außerhalb des Tiles? Dann ist es okay (Autos dürfen rein/raus fahren) + if (centerX < 0 || centerY < 0 || centerX >= this.tiles.size || centerY >= this.tiles.size) { + return true; + } + + // Innerhalb des Tiles: Prüfe mehrere Punkte um das Auto herum (nicht nur Zentrum) + // Das Auto ist 60x50, also prüfe auch Ecken + const points = [ + { x: centerX, y: centerY, name: 'center' }, // Zentrum + { x: car.x + 10, y: car.y + 10, name: 'top-left' }, // Linke obere Ecke (mit Padding) + { x: car.x + car.width - 10, y: car.y + 10, name: 'top-right' }, // Rechte obere Ecke + { x: car.x + 10, y: car.y + car.height - 10, name: 'bottom-left' }, // Linke untere Ecke + { x: car.x + car.width - 10, y: car.y + car.height - 10, name: 'bottom-right' } // Rechte untere Ecke + ]; + + // Auto ist auf Straße, wenn mindestens einer der Punkte auf der Straße ist + let onRoadPoints = []; + for (const point of points) { + if (this.isPositionDriveable(point.x, point.y)) { + onRoadPoints.push(point.name); + } + } + + if (onRoadPoints.length > 0) { + console.log(`[🚗 ON_ROAD] Car at x=${car.x.toFixed(2)}, y=${car.y.toFixed(2)} - ON ROAD (points: ${onRoadPoints.join(', ')})`); + return true; + } else { + console.log(`[🚗 OFF_ROAD] Car at x=${car.x.toFixed(2)}, y=${car.y.toFixed(2)}, centerX=${centerX.toFixed(2)}, centerY=${centerY.toFixed(2)} - OFF ROAD (no points on road)`); return false; } - // Innerhalb des Tiles: normale Prüfung - return this.isPositionDriveable(x, y); + }, + + // Prüfe ob Auto nahe an einer Kreuzung/Abzweigung ist und Richtung ändern sollte + shouldCarChangeDirection(car) { + const centerX = car.x + car.width / 2; + const centerY = car.y + car.height / 2; + + // Zufälliger Kollisionsbereich: 5-20 Pixel vor der tatsächlichen Grenze + const randomOffset = 5 + Math.random() * 15; + + // Hole erlaubte Richtungen für aktuelles Tile + const allowedDirections = this.getAllowedSpawnDirections(); + + // Prüfe je nach aktueller Richtung, ob eine Änderung möglich ist + switch (car.direction) { + case 'up': + // Wenn nahe am oberen Rand und andere Richtungen möglich sind + if (centerY <= randomOffset && allowedDirections.length > 1) { + return allowedDirections.filter(d => d !== 'down' && d !== 'up'); // Nicht zurück, nicht weitergerade wenn am Rand + } + break; + case 'down': + // Wenn nahe am unteren Rand + if (centerY >= this.tiles.size - randomOffset && allowedDirections.length > 1) { + return allowedDirections.filter(d => d !== 'up' && d !== 'down'); + } + break; + case 'left': + // Wenn nahe am linken Rand + if (centerX <= randomOffset && allowedDirections.length > 1) { + return allowedDirections.filter(d => d !== 'right' && d !== 'left'); + } + break; + case 'right': + // Wenn nahe am rechten Rand + if (centerX >= this.tiles.size - randomOffset && allowedDirections.length > 1) { + return allowedDirections.filter(d => d !== 'left' && d !== 'right'); + } + break; + } + + return null; // Keine Richtungsänderung nötig }, @@ -1563,248 +1728,118 @@ export default { updateCars() { const now = Date.now(); const maxAge = 45000; // 45 Sekunden + const initialCarCount = this.cars.length; + + console.log(`[🚗 UPDATE] Starting with ${initialCarCount} cars`); - this.cars = this.cars.filter(car => { + this.cars = this.cars.filter((car, index) => { + console.log(`[🚗 UPDATE] Processing car ${index}: x=${car.x.toFixed(2)}, y=${car.y.toFixed(2)}, age=${(now - car.createdAt)}ms`); + // Entferne alte Autos - if (now - car.createdAt > maxAge) { - return false; - } - - // Bewege das Auto nur wenn es auf einer befahrbaren Position ist + // Bewege das Auto this.updateCarMovement(car); - // Entferne Autos, die außerhalb des Bildschirms sind - if (car.x < -50 || car.x > 550 || car.y < -50 || car.y > 550) { + // Entferne Autos mit speed=0 (off-road) + if (car.speed === 0) { + console.log(`[🚗 UPDATE] Removing car ${index} - speed=0 (off-road)`); return false; } + // Entferne Autos, die komplett außerhalb des sichtbaren Bereichs sind + // Berücksichtige die Auto-Größe: Auto wird erst entfernt, wenn es komplett außerhalb ist + const tileSize = this.tiles.size; // 500px + const carWidth = car.width || 60; + const carHeight = car.height || 50; + + // Auto ist komplett außerhalb wenn: + // - Rechte Kante links vom Canvas: x + carWidth < 0 + // - Linke Kante rechts vom Canvas: x > tileSize + // - Untere Kante über dem Canvas: y + carHeight < 0 + // - Obere Kante unter dem Canvas: y > tileSize + if (car.x + carWidth < 0 || car.x > tileSize || + car.y + carHeight < 0 || car.y > tileSize) { + console.log(`[🚗 UPDATE] Removing car ${index} - completely out of bounds (x=${car.x.toFixed(2)}, y=${car.y.toFixed(2)})`); + return false; + } + + console.log(`[🚗 UPDATE] Keeping car ${index}`); return true; }); + + console.log(`[🚗 UPDATE] Finished with ${this.cars.length} cars (removed ${initialCarCount - this.cars.length})`); }, - // Aktualisiere die Bewegung eines einzelnen Autos + // Aktualisiere die Bewegung eines einzelnen Autos (vereinfachte Taxi-Logik) updateCarMovement(car) { - // Speichere aktuelle Position - car.lastPosition = { x: car.x, y: car.y }; - - // Setze Zielrichtung beim ersten Betreten des Tiles - if (!car.targetDirection) { - car.targetDirection = this.getRandomTargetDirection(car.direction); - car.targetLane = this.getTargetLane(car.targetDirection); - } - - // Prüfe ob Auto an einer Kreuzung ist und zur Zielrichtung abbiegen soll - if (this.shouldCarTurnAtIntersection(car) && !car.hasTurnedAtIntersection) { - this.turnCarToTarget(car); - car.hasTurnedAtIntersection = true; - } - - // Berechne neue Position - const newX = car.x + Math.cos(car.angle) * car.speed; - const newY = car.y + Math.sin(car.angle) * car.speed; - - // Prüfe ob neue Position befahrbar ist (Autos dürfen an Rändern hinein gleiten) - if (this.isPositionDriveableForCars(newX, newY, car.direction)) { - // Position ist befahrbar - bewege Auto - car.x = newX; - car.y = newY; - } else { - // Vorzugsweise zur geplanten Zielrichtung abbiegen (z. B. Kurve unten->rechts) - if (car.targetDirection) { - const prevAngle = car.angle; - const prevDir = car.direction; - this.turnCarToTarget(car); - const tryX = car.x + Math.cos(car.angle) * car.speed; - const tryY = car.y + Math.sin(car.angle) * car.speed; - if (this.isPositionDriveableForCars(tryX, tryY, car.direction)) { - car.x = tryX; - car.y = tryY; + console.log(`[🚗 MOVE] Car ID: ${car.id} - Current: x=${car.x.toFixed(2)}, y=${car.y.toFixed(2)}, direction=${car.direction}`); + + // 1) An Abbiegepunkt? Dann (ggf.) vorgegebene Zielrichtung übernehmen + const forcedDir = this.shouldCarTurnAtIntersection(car); + if (forcedDir) { + const tileType = this.getCurrentTileType(); + // forcedDir hat Vorrang – NICHT durch allowedDirections einschränken + let preferred = forcedDir; + + if (preferred && preferred !== car.direction) { + console.log(`[🚗 MOVE] Car ID: ${car.id} - Turning on ${tileType} from ${car.direction} to ${preferred}`); + // Beim Abbiegen: neue Spur bestimmen + car.direction = preferred; + if (preferred === 'left' || preferred === 'right') { + car.laneAxis = 'H'; + const laneMargin = 0.03; + const rel = preferred === 'left' + ? (0.375 + laneMargin + Math.random() * (0.5 - 0.375 - 2 * laneMargin)) + : (0.5 + laneMargin + Math.random() * (0.625 - 0.5 - 2 * laneMargin)); + car.laneCoord = rel * this.tiles.size; // Y-Spur } else { - // Rückgängig machen, falls Zielrichtung auch nicht geht - car.angle = prevAngle; - car.direction = prevDir; - // Fallback: alternative Richtung suchen (ohne zurückzufahren) - this.adjustCarDirection(car); + car.laneAxis = 'V'; + const laneMargin = 0.03; + const rel = preferred === 'up' + ? (0.5 + laneMargin + Math.random() * (0.625 - 0.5 - 2 * laneMargin)) + : (0.375 + laneMargin + Math.random() * (0.5 - 0.375 - 2 * laneMargin)); + car.laneCoord = rel * this.tiles.size; // X-Spur + } + switch (preferred) { + case 'up': car.angle = -Math.PI / 2; break; + case 'down': car.angle = Math.PI / 2; break; + case 'left': car.angle = Math.PI; break; + case 'right':car.angle = 0; break; } - } else { - // Position ist nicht befahrbar - suche alternative Richtung (ohne zurückzufahren) - this.adjustCarDirection(car); } } - }, - // Bestimme zufällige Zielrichtung basierend auf Tile-Art und aktueller Richtung - getRandomTargetDirection(currentDirection) { - const tileType = this.getCurrentTileType(); - const possibleDirections = []; - - // Bestimme mögliche Richtungen basierend auf Tile-Typ - switch (tileType) { - case 'cornerTopLeft': - // L-förmige Kurve: oben-links - if (currentDirection === 'right') possibleDirections.push('up'); - else if (currentDirection === 'down') possibleDirections.push('left'); - break; - - case 'cornerTopRight': - // L-förmige Kurve: oben-rechts - if (currentDirection === 'left') possibleDirections.push('up'); - else if (currentDirection === 'down') possibleDirections.push('right'); - break; - - case 'cornerBottomLeft': - // L-förmige Kurve: unten-links - if (currentDirection === 'right') possibleDirections.push('down'); - else if (currentDirection === 'up') possibleDirections.push('left'); - break; - - case 'cornerBottomRight': - // L-förmige Kurve: unten-rechts - if (currentDirection === 'left') possibleDirections.push('down'); - else if (currentDirection === 'up') possibleDirections.push('right'); - break; - - case 'crossing': - // Kreuzung: alle Richtungen außer zurück - switch (currentDirection) { - case 'right': possibleDirections.push('up', 'down'); break; - case 'left': possibleDirections.push('up', 'down'); break; - case 'up': possibleDirections.push('left', 'right'); break; - case 'down': possibleDirections.push('left', 'right'); break; - } - break; - - default: - // Für andere Tile-Typen: nur geradeaus - possibleDirections.push(currentDirection); - break; - } - - // Fallback: wenn keine Richtungen gefunden, geradeaus - if (possibleDirections.length === 0) { - possibleDirections.push(currentDirection); - } - - return possibleDirections[Math.floor(Math.random() * possibleDirections.length)]; - }, + // 2) Bewege Auto in die aktuelle Richtung und halte es auf seiner Spur + let newX = car.x; + let newY = car.y; - // Bestimme Zielspur basierend auf Zielrichtung - getTargetLane(targetDirection) { - switch (targetDirection) { - case 'left': - return { y: 0.375 + Math.random() * 0.125 }; // y=0.375-0.5 - case 'right': - return { y: 0.5 + Math.random() * 0.125 }; // y=0.5-0.625 - case 'down': - return { x: 0.375 + Math.random() * 0.125 }; // x=0.375-0.5 - case 'up': - return { x: 0.5 + Math.random() * 0.125 }; // x=0.5-0.625 + switch (car.direction) { + case 'up': newY = car.y - car.speed; break; + case 'down': newY = car.y + car.speed; break; + case 'left': newX = car.x - car.speed; break; + case 'right': newX = car.x + car.speed; break; + } + + // Querachse fixieren + if (car.laneAxis === 'H') { + const centerY = car.laneCoord; + newY = centerY - car.height / 2; + } else if (car.laneAxis === 'V') { + const centerX = car.laneCoord; + newX = centerX - car.width / 2; + } + + // 3) Position übernehmen + car.x = newX; + car.y = newY; + + console.log(`[🚗 MOVE] Car ID: ${car.id} - Moved to: x=${car.x.toFixed(2)}, y=${car.y.toFixed(2)}`); + + // 4) Straße prüfen – bleibt es auf befahrbarer Fläche? + if (!this.isCarOnRoad(car)) { + console.log(`[🚗 MOVE] Car ID: ${car.id} - OFF ROAD! Stopping car.`); + car.speed = 0; } }, - - // Prüfe, ob am aktuellen Tile der Abbiegepunkt erreicht ist - shouldCarTurnAtIntersection(car) { - const rx = car.x / this.tiles.size; - const ry = car.y / this.tiles.size; - const tileType = this.mapTileTypeToStreetCoordinates(this.getCurrentTileType()); - // Engeres Toleranzfenster: 0.495..0.505 - const nearYCenter = ry > 0.495 && ry < 0.505; // vertikale Mitte erreicht - const nearXCenter = rx > 0.495 && rx < 0.505; // horizontale Mitte erreicht - - switch (tileType) { - case 'cornerBottomRight': - // Straße: unten -> rechts. Von unten kommend (up) am Y-Zentrum nach rechts abbiegen. - if (car.direction === 'up') return nearYCenter; - // Von links kommend (left) am X-Zentrum nach unten abbiegen. - if (car.direction === 'left') return nearXCenter; - return false; - case 'cornerBottomLeft': - // Straße: unten -> links - if (car.direction === 'up') return nearYCenter; // dann links abbiegen - if (car.direction === 'right') return nearXCenter; // dann runter abbiegen - return false; - case 'cornerTopRight': - // Straße: oben -> rechts - if (car.direction === 'down') return nearYCenter; // dann rechts - if (car.direction === 'left') return nearXCenter; // dann hoch - return false; - case 'cornerTopLeft': - // Straße: oben -> links - if (car.direction === 'down') return nearYCenter; // dann links - if (car.direction === 'right') return nearXCenter; // dann hoch - return false; - default: - // Für Kreuzungen etc. beide Zentren oder explizit angeforderte Zielrichtung - return nearXCenter && nearYCenter; - } - }, - - // Auto zur Zielrichtung abbiegen - turnCarToTarget(car) { - const targetDirection = car.targetDirection; - - switch (targetDirection) { - case 'left': - car.angle = Math.PI; - car.direction = 'left'; - break; - case 'right': - car.angle = 0; - car.direction = 'right'; - break; - case 'down': - car.angle = Math.PI / 2; - car.direction = 'down'; - break; - case 'up': - car.angle = -Math.PI / 2; - car.direction = 'up'; - break; - } - }, - - - // Passe die Richtung eines Autos an, wenn es nicht weiterfahren kann - adjustCarDirection(car) { - const directions = [ - { angle: 0, name: 'right' }, - { angle: Math.PI, name: 'left' }, - { angle: Math.PI / 2, name: 'down' }, - { angle: -Math.PI / 2, name: 'up' } - ]; - - // Gegengerichtete Richtung (zurückfahren) ausschließen - const opposite = { - right: 'left', - left: 'right', - up: 'down', - down: 'up' - }[car.direction]; - - // Versuche verschiedene Richtungen, zuerst die geplante Zielrichtung, dann andere (ohne zurück) - const preferredOrder = []; - if (car.targetDirection && car.targetDirection !== opposite) preferredOrder.push(car.targetDirection); - directions.forEach(d => { - if (d.name !== opposite && d.name !== (preferredOrder[0] || '')) preferredOrder.push(d.name); - }); - - for (let i = 0; i < preferredOrder.length; i++) { - const name = preferredOrder[i]; - const angle = name === 'right' ? 0 : (name === 'left' ? Math.PI : (name === 'down' ? Math.PI/2 : -Math.PI/2)); - const testX = car.x + Math.cos(angle) * car.speed; - const testY = car.y + Math.sin(angle) * car.speed; - if (this.isPositionDriveableForCars(testX, testY, name)) { - car.angle = angle; - car.direction = name; - return; - } - } - - // Keine befahrbare Richtung gefunden - bewege zurück zur letzten Position - car.x = car.lastPosition.x; - car.y = car.lastPosition.y; - }, - getPassengerTimeLeft(passenger) { const now = Date.now(); const age = now - passenger.createdAt; @@ -1917,15 +1952,20 @@ export default { startGame() { this.gameRunning = true; + + // Lösche alle alten Autos (falls welche existieren) + this.cars = []; + console.log('[🚗 GAME] Cleared all cars on game start'); + // Setze Spielstart-Zeit if (!this.gameStartTime) { this.gameStartTime = Date.now(); } // Stoppe bestehende Game-Loop falls vorhanden if (this.gameLoop) { - cancelAnimationFrame(this.gameLoop); + clearTimeout(this.gameLoop); } - this.gameLoop = requestAnimationFrame(this.update); + this.gameLoop = setTimeout(this.update, 62); }, update() { @@ -1934,6 +1974,13 @@ export default { return; } + // Debug: Log every 60 frames (about once per second) + if (!this._frameCount) this._frameCount = 0; + this._frameCount++; + if (this._frameCount % 60 === 0) { + console.log(`[🔄 UPDATE] Frame ${this._frameCount}, gameRunning: ${this.gameRunning}, isPaused: ${this.isPaused}, cars: ${this.cars.length}`); + } + // Ampelschaltung tick this.updateTrafficLights(); @@ -1971,7 +2018,8 @@ export default { this.lastMinimapDraw = nowTs; } - this.gameLoop = requestAnimationFrame(this.update); + // Update-Rate schneller: ~16x pro Sekunde (62ms) + this.gameLoop = setTimeout(this.update, 62); }, updateTaxi() { @@ -2604,7 +2652,7 @@ export default { // Game Loop stoppen if (this.gameLoop) { - cancelAnimationFrame(this.gameLoop); + clearTimeout(this.gameLoop); this.gameLoop = null; } @@ -2668,8 +2716,13 @@ export default { // Game Loop sicher neu starten (unabhängig vom vorherigen Zustand) this.gameRunning = true; - try { cancelAnimationFrame(this.gameLoop); } catch (_) {} - this.gameLoop = requestAnimationFrame(this.update); + + // Lösche alle alten Autos auch hier (falls welche existieren) + this.cars = []; + console.log('[🚗 GAME] Cleared all cars after dialog close'); + + try { clearTimeout(this.gameLoop); } catch (_) {} + this.gameLoop = setTimeout(this.update, 62); // Taxi bleibt auf dem aktuellen Tile, mittig platzieren this.taxi.speed = 0; @@ -3321,15 +3374,46 @@ export default { // Zeichne alle Autos drawCars() { - this.cars.forEach(car => { - this.ctx.save(); - this.ctx.translate(car.x + car.width/2, car.y + car.height/2); + console.log(`[🚗 DRAW] Drawing ${this.cars.length} cars`); + this.cars.forEach((car, index) => { + const centerX = car.x + car.width/2; + const centerY = car.y + car.height/2; - // Korrigiere die Rotation um 90° + weitere 180° - das Auto-Bild zeigt bereits in die richtige Richtung - this.ctx.rotate(car.angle + Math.PI / 2 + Math.PI); + // Auto ist sichtbar wenn IRGENDEIN Teil im Canvas ist (nicht nur das Zentrum) + const isVisible = !(car.x + car.width < 0 || car.x > 500 || car.y + car.height < 0 || car.y > 500); + + console.log(`[🚗 DRAW] Car ${index}: x=${car.x.toFixed(2)}, y=${car.y.toFixed(2)}, centerX=${centerX.toFixed(2)}, centerY=${centerY.toFixed(2)}, direction=${car.direction}, speed=${car.speed.toFixed(2)}, VISIBLE=${isVisible}`); + + this.ctx.save(); + + // Auto-Position als linke obere Ecke (car.x, car.y ist die linke obere Ecke) + // Transformiere zum Zentrum des Autos für Rotation + this.ctx.translate(centerX, centerY); + + // Rotiere basierend auf der Fahrtrichtung (nicht angle) + let rotationAngle = 0; + switch (car.direction) { + case 'up': + rotationAngle = -Math.PI / 2; + break; + case 'down': + rotationAngle = Math.PI / 2; + break; + case 'left': + rotationAngle = Math.PI; + break; + case 'right': + rotationAngle = 0; + break; + } + + // Auto-Bild korrigieren (90° + 180° für korrekte Ausrichtung) + const finalAngle = rotationAngle + Math.PI / 2 + Math.PI; + this.ctx.rotate(finalAngle); if (this.carImage) { - // Zeichne Auto-Bild + console.log(`[🚗 DRAW] Drawing car ${index} with image at x=${car.x.toFixed(2)}, y=${car.y.toFixed(2)}`); + // Zeichne Auto-Bild zentriert um den Transformationspunkt this.ctx.drawImage( this.carImage, -car.width/2, @@ -3338,6 +3422,7 @@ export default { car.height ); } else { + console.log(`[🚗 DRAW] Drawing car ${index} with fallback rectangle at x=${car.x.toFixed(2)}, y=${car.y.toFixed(2)}`); // Fallback: Zeichne farbiges Rechteck wenn Bild nicht geladen this.ctx.fillStyle = car.color; this.ctx.fillRect(-car.width/2, -car.height/2, car.width, car.height); @@ -4331,7 +4416,7 @@ export default { // Game Loop stoppen if (this.gameLoop) { - cancelAnimationFrame(this.gameLoop); + clearTimeout(this.gameLoop); this.gameLoop = null; } @@ -4349,7 +4434,7 @@ export default { // Game Loop neu starten if (this.gameRunning && !this.gameLoop) { - this.gameLoop = requestAnimationFrame(this.update); + this.gameLoop = setTimeout(this.update, 62); } // Motor startet automatisch bei der nächsten Beschleunigung diff --git a/google-chrome-stable_current_x86_64.rpm b/google-chrome-stable_current_x86_64.rpm new file mode 100644 index 0000000..ab55b2a Binary files /dev/null and b/google-chrome-stable_current_x86_64.rpm differ