Änderung: Optimierung der Statusabfrage und Fehlerbehandlung im StatusBar-Komponenten
Änderungen: - Entfernen von Konsolenausgaben zur Verbesserung der Codequalität und Reduzierung von Debugging-Informationen in der Produktionsumgebung. - Anpassung der Fehlerbehandlung in der fetchStatus-Methode, um die Lesbarkeit zu erhöhen und die Fehlerprotokollierung zu vereinfachen. - Verbesserung der Socket.io- und Daemon-WebSocket-Verbindungslogik zur Unterstützung lokaler Entwicklungsumgebungen. Diese Anpassungen erhöhen die Effizienz und Benutzerfreundlichkeit der Anwendung, indem sie die Codebasis bereinigen und die Fehlerbehandlung optimieren.
This commit is contained in:
@@ -79,9 +79,7 @@ export default {
|
||||
methods: {
|
||||
async fetchStatus() {
|
||||
try {
|
||||
console.log('🔄 StatusBar: fetchStatus() startet...');
|
||||
const response = await apiClient.get("/api/falukant/info");
|
||||
console.log('📊 StatusBar: API Response erhalten:', response.data);
|
||||
const { money, character, events } = response.data;
|
||||
const { age, health } = character;
|
||||
const relationship = response.data.character.relationshipsAsCharacter1[0]?.relationshipType?.tr
|
||||
@@ -112,27 +110,17 @@ export default {
|
||||
{ key: "events", icon: "📰", value: events || null, image: null },
|
||||
{ key: "children", icon: "👶", value: childrenDisplay },
|
||||
];
|
||||
console.log('📊 StatusBar: statusItems aktualisiert:', this.statusItems);
|
||||
} catch (error) {
|
||||
console.error("Error fetching status:", error);
|
||||
// Error fetching status
|
||||
}
|
||||
},
|
||||
handleEvent(eventData) {
|
||||
console.log('🔄 StatusBar: handleEvent aufgerufen mit:', eventData);
|
||||
console.log('🔄 StatusBar: Event-Typ:', eventData.event);
|
||||
switch (eventData.event) {
|
||||
case 'falukantUpdateStatus':
|
||||
case 'stock_change':
|
||||
case 'familychanged':
|
||||
console.log('🔄 StatusBar: Rufe fetchStatus() auf...');
|
||||
this.fetchStatus().then(() => {
|
||||
console.log('✅ StatusBar: fetchStatus() erfolgreich abgeschlossen');
|
||||
}).catch((error) => {
|
||||
console.error('❌ StatusBar: fetchStatus() Fehler:', error);
|
||||
});
|
||||
this.fetchStatus();
|
||||
break;
|
||||
default:
|
||||
console.log('⚠️ StatusBar: Unbekanntes Event:', eventData.event);
|
||||
}
|
||||
},
|
||||
openPage(url, hasSubmenu = false) {
|
||||
|
||||
@@ -145,15 +145,20 @@ const store = createStore({
|
||||
currentSocket.disconnect();
|
||||
}
|
||||
commit('setConnectionStatus', 'connecting');
|
||||
const socketIoUrl = import.meta.env.VITE_SOCKET_IO_URL || import.meta.env.VITE_API_BASE_URL;
|
||||
console.log('🔌 Initializing Socket.io connection to:', socketIoUrl);
|
||||
// Socket.io URL für lokale Entwicklung und Produktion
|
||||
let socketIoUrl = import.meta.env.VITE_SOCKET_IO_URL || import.meta.env.VITE_API_BASE_URL;
|
||||
|
||||
// Für lokale Entwicklung: direkte Backend-Verbindung
|
||||
if (!socketIoUrl && (import.meta.env.DEV || window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1')) {
|
||||
socketIoUrl = 'http://localhost:3001';
|
||||
}
|
||||
|
||||
const socket = io(socketIoUrl, {
|
||||
secure: true,
|
||||
transports: ['websocket', 'polling']
|
||||
});
|
||||
|
||||
socket.on('connect', () => {
|
||||
console.log('✅ Socket.io connected successfully');
|
||||
retryCount = 0; // Reset retry counter on successful connection
|
||||
commit('setConnectionStatus', 'connected');
|
||||
const idForSocket = state.user?.hashedId || state.user?.id;
|
||||
@@ -161,14 +166,11 @@ const store = createStore({
|
||||
});
|
||||
|
||||
socket.on('disconnect', (reason) => {
|
||||
console.warn('❌ Socket.io disconnected:', reason);
|
||||
commit('setConnectionStatus', 'disconnected');
|
||||
retryConnection(connectSocket);
|
||||
});
|
||||
|
||||
socket.on('connect_error', (error) => {
|
||||
console.error('❌ Socket.io connection error:', error);
|
||||
console.error('❌ URL attempted:', import.meta.env.VITE_API_BASE_URL);
|
||||
commit('setConnectionStatus', 'error');
|
||||
});
|
||||
|
||||
@@ -179,30 +181,36 @@ const store = createStore({
|
||||
const maxRetries = 10;
|
||||
const retryConnection = (reconnectFn) => {
|
||||
if (retryCount >= maxRetries) {
|
||||
console.error('❌ Max retry attempts reached for Socket.io');
|
||||
return;
|
||||
}
|
||||
retryCount++;
|
||||
const delay = Math.min(1000 * Math.pow(1.5, retryCount - 1), 30000); // Exponential backoff, max 30s
|
||||
console.log(`🔄 Retrying Socket.io connection in ${delay}ms (attempt ${retryCount}/${maxRetries})`);
|
||||
setTimeout(() => {
|
||||
reconnectFn();
|
||||
}, delay);
|
||||
};
|
||||
|
||||
connectSocket();
|
||||
} else {
|
||||
console.log("User is not logged in or user data is not available.");
|
||||
}
|
||||
},
|
||||
initializeDaemonSocket({ commit, state }) {
|
||||
if (!state.isLoggedIn || !state.user) {
|
||||
console.log("User is not logged in or user data is not available for Daemon WebSocket.");
|
||||
return;
|
||||
}
|
||||
|
||||
const daemonUrl = import.meta.env.VITE_DAEMON_SOCKET || 'wss://www.your-part.de:4551';
|
||||
console.log('🔌 Initializing Daemon WebSocket connection to:', daemonUrl);
|
||||
// Daemon URL für lokale Entwicklung und Produktion
|
||||
let daemonUrl = import.meta.env.VITE_DAEMON_SOCKET;
|
||||
|
||||
// Für lokale Entwicklung: direkte Daemon-Verbindung
|
||||
if (!daemonUrl && (import.meta.env.DEV || window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1')) {
|
||||
daemonUrl = 'ws://localhost:4551';
|
||||
}
|
||||
|
||||
// Fallback für Produktion
|
||||
if (!daemonUrl) {
|
||||
daemonUrl = 'wss://www.your-part.de:4551';
|
||||
}
|
||||
|
||||
|
||||
const connectDaemonSocket = () => {
|
||||
// Protokoll-Fallback: zuerst mit Subprotokoll, dann ohne
|
||||
@@ -215,13 +223,11 @@ const store = createStore({
|
||||
const daemonSocket = currentProtocol
|
||||
? new WebSocket(daemonUrl, currentProtocol)
|
||||
: new WebSocket(daemonUrl);
|
||||
console.log('🔌 Protocol:', currentProtocol ?? 'none (fallback)');
|
||||
|
||||
let opened = false;
|
||||
|
||||
daemonSocket.onopen = () => {
|
||||
opened = true;
|
||||
console.log('✅ Daemon WebSocket connected successfully');
|
||||
retryCount = 0; // Reset retry counter on successful connection
|
||||
const payload = JSON.stringify({
|
||||
user_id: state.user.id,
|
||||
@@ -232,17 +238,9 @@ const store = createStore({
|
||||
};
|
||||
|
||||
daemonSocket.onclose = (event) => {
|
||||
console.warn('❌ Daemon WebSocket disconnected:', event.reason);
|
||||
console.warn('❌ Close details:', {
|
||||
code: event.code,
|
||||
reason: event.reason,
|
||||
wasClean: event.wasClean,
|
||||
readyState: daemonSocket.readyState
|
||||
});
|
||||
// Falls Verbindungsaufbau nicht offen war und es noch einen Fallback gibt → nächsten Versuch ohne Subprotokoll
|
||||
if (!opened && attemptIndex < protocols.length - 1) {
|
||||
attemptIndex += 1;
|
||||
console.warn('🔄 Fallback: versuche erneut ohne Subprotokoll...');
|
||||
tryConnectWithProtocol();
|
||||
return;
|
||||
}
|
||||
@@ -250,23 +248,9 @@ const store = createStore({
|
||||
};
|
||||
|
||||
daemonSocket.onerror = (error) => {
|
||||
console.error('❌ Daemon WebSocket error:', error);
|
||||
console.error('❌ Error details:', {
|
||||
type: error.type,
|
||||
target: error.target,
|
||||
readyState: daemonSocket.readyState,
|
||||
url: daemonSocket.url,
|
||||
protocol: daemonSocket.protocol
|
||||
});
|
||||
console.error('❌ Browser info:', {
|
||||
userAgent: navigator.userAgent,
|
||||
location: window.location.href,
|
||||
isSecure: window.location.protocol === 'https:'
|
||||
});
|
||||
// Bei Fehler vor Open: Fallback versuchen
|
||||
if (!opened && attemptIndex < protocols.length - 1) {
|
||||
attemptIndex += 1;
|
||||
console.warn('🔄 Fallback: versuche erneut ohne Subprotokoll (onerror)...');
|
||||
tryConnectWithProtocol();
|
||||
return;
|
||||
}
|
||||
@@ -281,21 +265,17 @@ const store = createStore({
|
||||
try {
|
||||
const data = JSON.parse(message);
|
||||
// Handle daemon messages here
|
||||
console.log('📨 Daemon message received:', data);
|
||||
} catch (error) {
|
||||
console.error("Error parsing daemon message:", error);
|
||||
// Error parsing daemon message
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
commit('setDaemonSocket', daemonSocket);
|
||||
} catch (error) {
|
||||
console.error('❌ Failed to create Daemon WebSocket:', error);
|
||||
console.error('❌ URL attempted:', import.meta.env.VITE_DAEMON_SOCKET);
|
||||
// Beim Konstruktionsfehler ebenfalls Fallback versuchen
|
||||
if (attemptIndex < protocols.length - 1) {
|
||||
attemptIndex += 1;
|
||||
console.warn('🔄 Fallback: Konstruktion ohne Subprotokoll...');
|
||||
tryConnectWithProtocol();
|
||||
return;
|
||||
}
|
||||
@@ -310,18 +290,15 @@ const store = createStore({
|
||||
const maxRetries = 15; // Increased max retries
|
||||
const retryConnection = (reconnectFn) => {
|
||||
if (retryCount >= maxRetries) {
|
||||
console.error('❌ Max retry attempts reached for Daemon WebSocket');
|
||||
// Reset counter after a longer delay to allow for network recovery
|
||||
setTimeout(() => {
|
||||
retryCount = 0;
|
||||
console.log('🔄 Resetting Daemon WebSocket retry counter - attempting reconnection');
|
||||
reconnectFn();
|
||||
}, 60000); // Wait 1 minute before resetting
|
||||
return;
|
||||
}
|
||||
retryCount++;
|
||||
const delay = Math.min(1000 * Math.pow(1.5, retryCount - 1), 30000); // Exponential backoff, max 30s
|
||||
console.log(`🔄 Retrying Daemon WebSocket connection in ${delay}ms (attempt ${retryCount}/${maxRetries})`);
|
||||
setTimeout(() => {
|
||||
reconnectFn();
|
||||
}, delay);
|
||||
@@ -336,7 +313,6 @@ const store = createStore({
|
||||
const menu = await loadMenu();
|
||||
commit('setMenu', menu);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
commit('setMenu', []);
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1,19 +1,22 @@
|
||||
import axios from 'axios';
|
||||
import store from '../store';
|
||||
|
||||
// API-Basis-URL - immer über Apache-Proxy für Produktion
|
||||
// API-Basis-URL - Apache-Proxy für Produktion, direkte Verbindung für lokale Entwicklung
|
||||
const getApiBaseURL = () => {
|
||||
// Wenn explizite Umgebungsvariable gesetzt ist, diese verwenden
|
||||
if (import.meta.env.VITE_API_BASE_URL) {
|
||||
return import.meta.env.VITE_API_BASE_URL;
|
||||
}
|
||||
|
||||
// Für lokale Entwicklung: direkte Backend-Verbindung
|
||||
if (import.meta.env.DEV || window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1') {
|
||||
return 'http://localhost:3001';
|
||||
}
|
||||
|
||||
// Für Produktion: Root-Pfad, da API-Endpunkte bereits mit /api beginnen
|
||||
return '';
|
||||
};
|
||||
|
||||
// Debug-Informationen
|
||||
console.log('🔗 API Base URL:', getApiBaseURL());
|
||||
|
||||
const apiClient = axios.create({
|
||||
baseURL: getApiBaseURL(),
|
||||
@@ -28,8 +31,6 @@ apiClient.interceptors.request.use(config => {
|
||||
if (user && user.authCode) {
|
||||
config.headers['userid'] = user.id;
|
||||
config.headers['authcode'] = user.authCode; // Kleinschreibung!
|
||||
} else {
|
||||
console.log('⚠️ Keine User-Daten verfügbar');
|
||||
}
|
||||
|
||||
return config;
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<template>
|
||||
<div class="contenthidden">
|
||||
<StatusBar />
|
||||
<div class="contentscroll">
|
||||
<!-- Spiel-Titel -->
|
||||
<div class="game-title">
|
||||
@@ -68,6 +67,7 @@
|
||||
<div class="tacho-speed">
|
||||
<span class="lives" title="Verbleibende Fahrzeuge">❤️ {{ vehicleCount }}</span>
|
||||
<span class="fuel" title="Treibstoff">⛽ {{ Math.round(fuel) }}%</span>
|
||||
<span class="score" title="Punkte">⭐ {{ score }}</span>
|
||||
<span class="speed-violations" title="Geschwindigkeitsverstöße">📷 {{ speedViolations }}</span>
|
||||
<span class="redlight-counter" title="Rote Ampeln überfahren">
|
||||
<span class="redlight-icon">🚦</span>
|
||||
@@ -223,12 +223,9 @@
|
||||
import streetCoordinates from '../../utils/streetCoordinates.js';
|
||||
import { MotorSound, NoiseGenerator } from '../../assets/motorSound.js';
|
||||
import apiClient from '../../utils/axios.js';
|
||||
import StatusBar from '../../components/falukant/StatusBar.vue';
|
||||
|
||||
export default {
|
||||
name: 'TaxiGame',
|
||||
components: {
|
||||
StatusBar
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@@ -242,6 +239,9 @@ export default {
|
||||
occupiedHouses: new Set(), // Verhindert doppelte Belegung von Häusern
|
||||
lastPassengerGeneration: 0,
|
||||
passengerGenerationInterval: 0,
|
||||
// Timeout-Referenzen für Cleanup
|
||||
passengerGenerationTimeout: null,
|
||||
crashDialogTimeout: null,
|
||||
bonusMultiplier: 15, // Bonus pro Tile
|
||||
timePerTile: 8, // Sekunden pro Tile
|
||||
fuel: 100,
|
||||
@@ -595,7 +595,6 @@ export default {
|
||||
|
||||
async initializeMotorSound() {
|
||||
// AudioContext wird erst bei erster Benutzerinteraktion erstellt
|
||||
if (import.meta.env?.DEV) console.log('[Audio] Lazy-Init: MotorSound wird bei erster Benutzerinteraktion initialisiert');
|
||||
},
|
||||
|
||||
async initAudioOnUserInteraction() {
|
||||
@@ -609,12 +608,9 @@ export default {
|
||||
this.audioContext = new (window.AudioContext || window.webkitAudioContext)();
|
||||
window.__TaxiAudioContext = this.audioContext;
|
||||
}
|
||||
if (import.meta.env?.DEV) console.log('[Audio] init: Context erstellt. state=', this.audioContext.state, 'rate=', this.audioContext.sampleRate);
|
||||
// Autoplay-Policy: Context sofort im User-Gesture fortsetzen
|
||||
if (this.audioContext.state === 'suspended') {
|
||||
if (import.meta.env?.DEV) console.log('[Audio] init: resume() versuchen');
|
||||
await this.audioContext.resume();
|
||||
if (import.meta.env?.DEV) console.log('[Audio] init: state nach resume=', this.audioContext.state);
|
||||
}
|
||||
if (!this.motorSound) {
|
||||
// Ggf. globalen Motor übernehmen
|
||||
@@ -627,11 +623,9 @@ export default {
|
||||
window.__TaxiMotorSound = this.motorSound;
|
||||
}
|
||||
}
|
||||
if (import.meta.env?.DEV) console.log('[Audio] init: MotorSound initialisiert');
|
||||
// Falls bereits Bewegung anliegt, Sound direkt starten
|
||||
const speedKmh = this.taxi.speed * 5;
|
||||
if (speedKmh > 0) {
|
||||
if (import.meta.env?.DEV) console.log('[Audio] init: direkter Start. speedKmh=', speedKmh);
|
||||
this.motorSound.start();
|
||||
// Sofort Parameter setzen
|
||||
const speedFactor = Math.min(speedKmh / 120, 1);
|
||||
@@ -650,7 +644,6 @@ export default {
|
||||
|
||||
const speedKmh = this.taxi.speed * 5; // Geschwindigkeit in km/h
|
||||
const isMoving = speedKmh > 0;
|
||||
if (import.meta.env?.DEV) console.log('[Audio] update:', { speedKmh, isMoving, ctx: this.audioContext?.state, playing: this.motorSound.isPlaying });
|
||||
|
||||
// Start NICHT hier (kein User-Gesture). Stop mit 1s Verzögerung, wenn still
|
||||
if (!isMoving) {
|
||||
@@ -659,7 +652,6 @@ export default {
|
||||
// Nur stoppen, wenn weiterhin keine Bewegung
|
||||
const still = (this.taxi.speed * 5) <= 0;
|
||||
if (still && this.motorSound && this.motorSound.isPlaying) {
|
||||
if (import.meta.env?.DEV) console.log('[Audio] update: delayed motorSound.stop()');
|
||||
this.motorSound.stop();
|
||||
}
|
||||
this.motorStopTimeout = null;
|
||||
@@ -678,7 +670,6 @@ export default {
|
||||
const speedFactor = Math.min(speedKmh / 120, 1); // 0-1 basierend auf 0-120 km/h
|
||||
const motorSpeed = 0.3 + (speedFactor * 0.3); // 0.3 bis 1.5
|
||||
const volume = 0.1 + (speedFactor * 0.4); // 0.1 bis 0.5
|
||||
if (import.meta.env?.DEV) console.log('[Audio] update: params', { motorSpeed, volume });
|
||||
|
||||
this.motorSound.setSpeed(motorSpeed);
|
||||
this.motorSound.setVolume(volume);
|
||||
@@ -688,12 +679,22 @@ export default {
|
||||
cleanup() {
|
||||
if (this.gameLoop) {
|
||||
cancelAnimationFrame(this.gameLoop);
|
||||
this.gameLoop = null;
|
||||
}
|
||||
if (this.motorSound) { this.motorSound.stop(); }
|
||||
if (this.motorStopTimeout) {
|
||||
clearTimeout(this.motorStopTimeout);
|
||||
this.motorStopTimeout = null;
|
||||
}
|
||||
// Cleanup aller Timeouts
|
||||
if (this.passengerGenerationTimeout) {
|
||||
clearTimeout(this.passengerGenerationTimeout);
|
||||
this.passengerGenerationTimeout = null;
|
||||
}
|
||||
if (this.crashDialogTimeout) {
|
||||
clearTimeout(this.crashDialogTimeout);
|
||||
this.crashDialogTimeout = null;
|
||||
}
|
||||
// AudioContext bleibt global erhalten, nicht schließen
|
||||
document.removeEventListener('keydown', this.handleKeyDown);
|
||||
document.removeEventListener('keyup', this.handleKeyUp);
|
||||
@@ -703,6 +704,10 @@ export default {
|
||||
document.removeEventListener('keydown', this.audioUnlockHandler, { capture: true });
|
||||
this.audioUnlockHandler = null;
|
||||
}
|
||||
// Cleanup von Listen und Sets
|
||||
this.waitingPassengersList = [];
|
||||
this.loadedPassengersList = [];
|
||||
this.occupiedHouses.clear();
|
||||
},
|
||||
|
||||
generateLevel() {
|
||||
@@ -736,7 +741,7 @@ export default {
|
||||
this.generateWaitingPassenger();
|
||||
} else {
|
||||
// Falls die Map noch nicht geladen ist, versuche es in 2 Sekunden erneut
|
||||
setTimeout(() => {
|
||||
this.passengerGenerationTimeout = setTimeout(() => {
|
||||
this.generateWaitingPassenger();
|
||||
}, 2000);
|
||||
}
|
||||
@@ -745,36 +750,39 @@ export default {
|
||||
generateWaitingPassenger() {
|
||||
if (!this.currentMap || !Array.isArray(this.currentMap.tileHouses)) {
|
||||
// Versuche es in 2 Sekunden erneut
|
||||
setTimeout(() => {
|
||||
this.passengerGenerationTimeout = setTimeout(() => {
|
||||
this.generateWaitingPassenger();
|
||||
}, 2000);
|
||||
return;
|
||||
}
|
||||
|
||||
// Finde alle verfügbaren Häuser auf der Karte
|
||||
const availableHouses = [];
|
||||
const houses = this.currentMap.tileHouses || [];
|
||||
// Erstelle Liste aller Tiles mit Häusern
|
||||
const tilesWithHouses = this.getTilesWithHouses();
|
||||
|
||||
houses.forEach((house, houseIndex) => {
|
||||
const houseId = `${house.x}-${house.y}-${houseIndex}`;
|
||||
if (!this.occupiedHouses.has(houseId)) {
|
||||
availableHouses.push({
|
||||
houseId,
|
||||
x: house.x,
|
||||
y: house.y,
|
||||
houseIndex,
|
||||
house
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Wenn keine freien Häuser verfügbar, generiere keinen Passagier
|
||||
if (availableHouses.length === 0) {
|
||||
if (tilesWithHouses.length === 0) {
|
||||
console.log('Keine Tiles mit Häusern gefunden');
|
||||
return;
|
||||
}
|
||||
|
||||
// Wähle zufälliges Haus
|
||||
const selectedHouse = availableHouses[Math.floor(Math.random() * availableHouses.length)];
|
||||
// Wähle zufälliges Tile mit Häusern
|
||||
const selectedTile = tilesWithHouses[Math.floor(Math.random() * tilesWithHouses.length)];
|
||||
|
||||
// Finde alle Häuser auf diesem Tile
|
||||
const housesOnTile = this.currentMap.tileHouses.filter(house =>
|
||||
house.x === selectedTile.x && house.y === selectedTile.y
|
||||
);
|
||||
|
||||
// Wähle zufälliges Haus auf diesem Tile
|
||||
const selectedHouse = housesOnTile[Math.floor(Math.random() * housesOnTile.length)];
|
||||
const houseIndex = this.currentMap.tileHouses.findIndex(h => h === selectedHouse);
|
||||
const houseId = `${selectedHouse.x}-${selectedHouse.y}-${houseIndex}`;
|
||||
|
||||
// Prüfe ob das Haus bereits belegt ist
|
||||
if (this.occupiedHouses.has(houseId)) {
|
||||
// Haus bereits belegt, versuche es erneut
|
||||
this.generateWaitingPassenger();
|
||||
return;
|
||||
}
|
||||
|
||||
// Finde die Straße für dieses spezifische Haus
|
||||
let streetName = "Unbekannte Straße";
|
||||
@@ -784,7 +792,7 @@ export default {
|
||||
const houseTile = this.currentMap.tileStreets?.find(ts => ts.x === selectedHouse.x && ts.y === selectedHouse.y);
|
||||
if (houseTile) {
|
||||
// Bestimme die Straße basierend auf der Haus-Ecke
|
||||
const corner = selectedHouse.house.corner;
|
||||
const corner = selectedHouse.corner;
|
||||
if (corner === 'lo' || corner === 'ro') {
|
||||
// Horizontale Straße
|
||||
if (houseTile.streetNameH && houseTile.streetNameH.name) {
|
||||
@@ -797,60 +805,131 @@ export default {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (streetName === 'Unbekannte Straße') {
|
||||
console.log(houseTile)
|
||||
}
|
||||
|
||||
// Finde die Hausnummer für dieses spezifische Haus
|
||||
const houseKey = `${selectedHouse.x},${selectedHouse.y},${selectedHouse.house.corner}`;
|
||||
const houseKey = `${selectedHouse.x},${selectedHouse.y},${selectedHouse.corner}`;
|
||||
houseNumber = this.houseNumbers[houseKey] || 1;
|
||||
|
||||
// Generiere Namen und Geschlecht
|
||||
const nameData = this.generatePassengerName();
|
||||
|
||||
// Erstelle Passagier
|
||||
const passenger = {
|
||||
id: Date.now() + Math.random(), // Eindeutige ID
|
||||
name: this.generatePassengerName(),
|
||||
name: nameData.fullName,
|
||||
gender: nameData.gender,
|
||||
location: `${streetName} ${houseNumber}`,
|
||||
houseId: selectedHouse.houseId,
|
||||
houseId: houseId,
|
||||
tileX: selectedHouse.x,
|
||||
tileY: selectedHouse.y,
|
||||
houseIndex: selectedHouse.houseIndex,
|
||||
houseCorner: selectedHouse.house.corner, // Speichere auch die Ecke
|
||||
imageIndex: this.generatePassengerImageIndex(), // Zufälliger Bildindex 1-6
|
||||
houseIndex: houseIndex,
|
||||
houseCorner: selectedHouse.corner, // Speichere auch die Ecke
|
||||
imageIndex: this.generatePassengerImageIndex(nameData.gender), // Bildindex basierend auf Geschlecht
|
||||
createdAt: Date.now() // Zeitstempel der Erstellung
|
||||
};
|
||||
|
||||
// Füge Passagier zur Liste hinzu und markiere Haus als belegt
|
||||
this.waitingPassengersList.push(passenger);
|
||||
this.occupiedHouses.add(selectedHouse.houseId);
|
||||
this.occupiedHouses.add(houseId);
|
||||
|
||||
console.log('Neuer wartender Passagier generiert:', passenger);
|
||||
},
|
||||
|
||||
generatePassengerName() {
|
||||
const firstNames = [
|
||||
'Anna', 'Max', 'Lisa', 'Tom', 'Sarah', 'Ben', 'Emma', 'Lukas', 'Julia', 'Paul',
|
||||
'Marie', 'Felix', 'Sophie', 'Jonas', 'Laura', 'Tim', 'Mia', 'Noah', 'Emma', 'Finn'
|
||||
const maleNames = [
|
||||
'Max', 'Tom', 'Ben', 'Lukas', 'Paul', 'Felix', 'Jonas', 'Tim', 'Noah', 'Finn',
|
||||
'Leon', 'Liam', 'Elias', 'Henry', 'Anton', 'Theo', 'Emil', 'Oskar', 'Mats', 'Luis',
|
||||
'Alexander', 'Sebastian', 'David', 'Daniel', 'Michael', 'Christian', 'Andreas', 'Stefan',
|
||||
'Markus', 'Thomas', 'Matthias', 'Martin', 'Peter', 'Klaus', 'Wolfgang', 'Jürgen',
|
||||
'Rainer', 'Uwe', 'Dieter', 'Hans', 'Günter', 'Karl', 'Walter', 'Helmut', 'Manfred',
|
||||
'Jens', 'Sven', 'Tobias', 'Nils', 'Jan', 'Marcel', 'Kevin', 'Dennis', 'Marco',
|
||||
'Philipp', 'Simon', 'Florian', 'Dominik', 'Patrick', 'Oliver', 'Timo', 'Björn'
|
||||
];
|
||||
const femaleNames = [
|
||||
'Anna', 'Lisa', 'Sarah', 'Emma', 'Julia', 'Marie', 'Sophie', 'Laura', 'Mia', 'Emma',
|
||||
'Lina', 'Mila', 'Hannah', 'Lea', 'Emilia', 'Clara', 'Leni', 'Marlene', 'Frieda', 'Ida',
|
||||
'Katharina', 'Christina', 'Nicole', 'Stefanie', 'Jennifer', 'Jessica', 'Melanie', 'Sandra',
|
||||
'Andrea', 'Petra', 'Monika', 'Ursula', 'Brigitte', 'Ingrid', 'Gisela', 'Elisabeth',
|
||||
'Maria', 'Barbara', 'Helga', 'Gertrud', 'Irmgard', 'Hildegard', 'Waltraud', 'Renate',
|
||||
'Sabine', 'Kerstin', 'Anja', 'Tanja', 'Nadine', 'Katrin', 'Silke', 'Birgit',
|
||||
'Susanne', 'Martina', 'Karin', 'Heike', 'Doris', 'Elke', 'Bärbel', 'Jutta',
|
||||
'Gabriele', 'Angelika', 'Christine', 'Annette', 'Beate', 'Cornelia', 'Diana', 'Eva'
|
||||
];
|
||||
const lastNames = [
|
||||
'Müller', 'Schmidt', 'Schneider', 'Fischer', 'Weber', 'Meyer', 'Wagner', 'Becker',
|
||||
'Schulz', 'Hoffmann', 'Schäfer', 'Koch', 'Bauer', 'Richter', 'Klein', 'Wolf'
|
||||
'Schulz', 'Hoffmann', 'Schäfer', 'Koch', 'Bauer', 'Richter', 'Klein', 'Wolf',
|
||||
'Schröder', 'Neumann', 'Schwarz', 'Zimmermann', 'Braun', 'Hofmann', 'Lange', 'Schmitt',
|
||||
'Werner', 'Schmitz', 'Krause', 'Meier', 'Lehmann', 'Köhler', 'Huber', 'Kaiser',
|
||||
'Fuchs', 'Peters', 'Lang', 'Scholz', 'Möller', 'Weiß', 'Jung', 'Hahn', 'Schubert',
|
||||
'Schuster', 'Winkler', 'Berger', 'Roth', 'Beck', 'Lorenz', 'Baumann', 'Franke',
|
||||
'Albrecht', 'Ludwig', 'Winter', 'Simon', 'Kraus', 'Vogt', 'Stein', 'Jäger',
|
||||
'Otto', 'Sommer', 'Groß', 'Seidel', 'Heinrich', 'Brandt', 'Haas', 'Schreiber',
|
||||
'Graf', 'Schulte', 'Dietrich', 'Ziegler', 'Kuhn', 'Pohl', 'Engel', 'Horn'
|
||||
];
|
||||
|
||||
// Wähle zufällig zwischen männlich und weiblich
|
||||
const isMale = Math.random() < 0.5;
|
||||
const firstNames = isMale ? maleNames : femaleNames;
|
||||
const firstName = firstNames[Math.floor(Math.random() * firstNames.length)];
|
||||
const lastName = lastNames[Math.floor(Math.random() * lastNames.length)];
|
||||
return `${firstName} ${lastName}`;
|
||||
|
||||
return {
|
||||
fullName: `${firstName} ${lastName}`,
|
||||
gender: isMale ? 'male' : 'female'
|
||||
};
|
||||
},
|
||||
|
||||
generatePassengerImageIndex() {
|
||||
// Zufälliger Bildindex 1-6 (1-3 männlich, 4-6 weiblich)
|
||||
return Math.floor(Math.random() * 6) + 1;
|
||||
generatePassengerImageIndex(gender) {
|
||||
// Bildindex basierend auf Geschlecht (1-3 männlich, 4-6 weiblich)
|
||||
if (gender === 'male') {
|
||||
return Math.floor(Math.random() * 3) + 1; // 1-3
|
||||
} else {
|
||||
return Math.floor(Math.random() * 3) + 4; // 4-6
|
||||
}
|
||||
},
|
||||
|
||||
getTilesWithHouses() {
|
||||
// Erstelle Liste aller Tiles, die Häuser haben
|
||||
const tilesWithHouses = [];
|
||||
const houses = this.currentMap.tileHouses || [];
|
||||
|
||||
// Sammle alle eindeutigen Tile-Koordinaten
|
||||
const tileSet = new Set();
|
||||
houses.forEach(house => {
|
||||
const tileKey = `${house.x}-${house.y}`;
|
||||
if (!tileSet.has(tileKey)) {
|
||||
tileSet.add(tileKey);
|
||||
tilesWithHouses.push({ x: house.x, y: house.y });
|
||||
}
|
||||
});
|
||||
|
||||
return tilesWithHouses;
|
||||
},
|
||||
|
||||
calculateShortestPath(startX, startY, endX, endY) {
|
||||
// Berechne Manhattan-Distanz (kürzester Weg in einem Raster)
|
||||
const distance = Math.abs(endX - startX) + Math.abs(endY - startY);
|
||||
return distance;
|
||||
|
||||
// Wenn Start und Ziel identisch sind, mindestens 1 Tile Distanz
|
||||
// (da das Taxi das Tile verlassen und wieder betreten muss)
|
||||
if (distance === 0) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Für unterschiedliche Tiles: Distanz + 1 (Start-Tile wird mitgezählt)
|
||||
return distance + 1;
|
||||
},
|
||||
|
||||
calculateBonusAndTime(startX, startY, endX, endY) {
|
||||
const shortestPath = this.calculateShortestPath(startX, startY, endX, endY);
|
||||
const bonus = shortestPath * this.bonusMultiplier;
|
||||
const maxTime = shortestPath * this.timePerTile;
|
||||
|
||||
console.log(`Wegstrecke berechnet: Start(${startX},${startY}) -> Ziel(${endX},${endY}) = ${shortestPath} Tiles, Bonus: ${bonus}, Zeit: ${maxTime}s`);
|
||||
|
||||
return {
|
||||
shortestPath,
|
||||
bonus,
|
||||
@@ -860,60 +939,66 @@ export default {
|
||||
|
||||
generatePassengerDestination() {
|
||||
// Generiere ein zufälliges Ziel für den Passagier
|
||||
if (!this.currentMap || !Array.isArray(this.currentMap.tileStreets) || !Array.isArray(this.currentMap.tileHouses)) return null;
|
||||
if (!this.currentMap || !Array.isArray(this.currentMap.tileStreets) || !Array.isArray(this.currentMap.tileHouses)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Finde alle Straßen-Tiles auf der Karte
|
||||
const streets = this.currentMap.tileStreets || [];
|
||||
const houses = this.currentMap.tileHouses || [];
|
||||
|
||||
// Versuche maximal 10 Mal, ein gültiges Ziel zu finden
|
||||
for (let attempt = 0; attempt < 10; attempt++) {
|
||||
// Wähle zufällige Straße
|
||||
const randomStreet = streets[Math.floor(Math.random() * streets.length)];
|
||||
// Sammle alle gültigen Ziele vor der zufälligen Auswahl
|
||||
const validDestinations = [];
|
||||
|
||||
for (const street of streets) {
|
||||
// Prüfe ob diese Straße Häuser hat
|
||||
const housesOnThisTile = houses.filter(house => house.x === randomStreet.x && house.y === randomStreet.y);
|
||||
const housesOnThisTile = houses.filter(house => house.x === street.x && house.y === street.y);
|
||||
|
||||
if (housesOnThisTile.length > 0) {
|
||||
// Wähle zufälliges Haus auf diesem Tile
|
||||
const selectedHouse = housesOnThisTile[Math.floor(Math.random() * housesOnThisTile.length)];
|
||||
|
||||
// Bestimme die Straße basierend auf der Haus-Ecke
|
||||
let streetName = "Unbekannte Straße";
|
||||
let houseNumber = 1;
|
||||
|
||||
const corner = selectedHouse.corner;
|
||||
for (const house of housesOnThisTile) {
|
||||
const corner = house.corner;
|
||||
let streetName = null;
|
||||
|
||||
if (corner === 'lo' || corner === 'ro') {
|
||||
// Horizontale Straße
|
||||
if (randomStreet.streetNameH && randomStreet.streetNameH.name) {
|
||||
streetName = randomStreet.streetNameH.name;
|
||||
if (street.streetNameH && street.streetNameH.name) {
|
||||
streetName = street.streetNameH.name;
|
||||
}
|
||||
} else if (corner === 'lu' || corner === 'ru') {
|
||||
// Vertikale Straße
|
||||
if (randomStreet.streetNameV && randomStreet.streetNameV.name) {
|
||||
streetName = randomStreet.streetNameV.name;
|
||||
if (street.streetNameV && street.streetNameV.name) {
|
||||
streetName = street.streetNameV.name;
|
||||
}
|
||||
}
|
||||
|
||||
// Finde die Hausnummer für diese Straße
|
||||
const houseNumbers = this.getHouseNumbersForStreet(streetName);
|
||||
if (houseNumbers.length > 0) {
|
||||
houseNumber = houseNumbers[Math.floor(Math.random() * houseNumbers.length)];
|
||||
}
|
||||
// Nur gültige Straßen hinzufügen
|
||||
if (streetName && streetName.trim() !== '') {
|
||||
// Hole Hausnummern nur für dieses spezifische Tile
|
||||
const key = `${street.x},${street.y},${house.corner}`;
|
||||
const houseNumber = this.houseNumbers[key];
|
||||
|
||||
return {
|
||||
if (houseNumber != null) {
|
||||
validDestinations.push({
|
||||
streetName,
|
||||
houseNumber,
|
||||
location: `${streetName} ${houseNumber}`,
|
||||
tileX: randomStreet.x,
|
||||
tileY: randomStreet.y,
|
||||
houseCorner: selectedHouse.corner
|
||||
};
|
||||
tileX: street.x,
|
||||
tileY: street.y,
|
||||
houseCorner: house.corner
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Wenn nach 10 Versuchen kein Ziel gefunden wurde
|
||||
|
||||
if (validDestinations.length > 0) {
|
||||
// Wähle zufälliges gültiges Ziel
|
||||
const selectedDestination = validDestinations[Math.floor(Math.random() * validDestinations.length)];
|
||||
return selectedDestination;
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
|
||||
@@ -941,7 +1026,6 @@ export default {
|
||||
if (timeSincePickup >= 2000) {
|
||||
passenger.timerActive = true;
|
||||
passenger.lastUpdateTime = now;
|
||||
console.log('Timer für Passagier', passenger.name, 'aktiviert nach', timeSincePickup, 'ms');
|
||||
}
|
||||
return; // Timer noch nicht aktiv
|
||||
}
|
||||
@@ -1106,6 +1190,10 @@ export default {
|
||||
|
||||
startGame() {
|
||||
this.gameRunning = true;
|
||||
// Stoppe bestehende Game-Loop falls vorhanden
|
||||
if (this.gameLoop) {
|
||||
cancelAnimationFrame(this.gameLoop);
|
||||
}
|
||||
this.gameLoop = requestAnimationFrame(this.update);
|
||||
},
|
||||
|
||||
@@ -1264,6 +1352,11 @@ export default {
|
||||
},
|
||||
|
||||
getHousePositionOnTile(tileX, tileY, corner) {
|
||||
// Prüfe ob das Taxi auf dem gleichen Tile ist wie das Haus
|
||||
if (this.currentTile.col !== tileX || this.currentTile.row !== tileY) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Berechne die Position des Hauses basierend auf der Ecke
|
||||
// Diese Methode muss exakt mit drawHousesOnMainCanvas übereinstimmen
|
||||
const tileSize = 500; // Tile-Größe
|
||||
@@ -1306,19 +1399,53 @@ export default {
|
||||
const HOUSE_H = 150; // Haushöhe
|
||||
const maxDistance = 100; // 100px Entfernung
|
||||
|
||||
// Einfache Distanzberechnung zum Hauszentrum
|
||||
const houseCenterX = houseX + HOUSE_W / 2;
|
||||
const houseCenterY = houseY + HOUSE_H / 2;
|
||||
const distance = Math.sqrt(
|
||||
Math.pow(taxiX - houseCenterX, 2) + Math.pow(taxiY - houseCenterY, 2)
|
||||
// Berechne die Entfernung zur Tile-Mitte
|
||||
const tileCenterX = 250; // Mitte des 500px Canvas
|
||||
const tileCenterY = 250; // Mitte des 500px Canvas
|
||||
const distanceToTileCenter = Math.sqrt(
|
||||
Math.pow(taxiX - tileCenterX, 2) + Math.pow(taxiY - tileCenterY, 2)
|
||||
);
|
||||
|
||||
// Wenn das Taxi zu weit von der Tile-Mitte entfernt ist, keine Aktion
|
||||
const maxDistanceFromCenter = 200; // 200px von der Mitte entfernt
|
||||
if (distanceToTileCenter > maxDistanceFromCenter) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return distance <= maxDistance;
|
||||
// Richtungsabhängige Erkennung basierend auf der Haus-Ecke
|
||||
let isNear = false;
|
||||
|
||||
if (corner === 'lo' || corner === 'ro') {
|
||||
// Horizontale Straße - Taxi muss links oder rechts vom Haus sein
|
||||
const houseCenterX = houseX + HOUSE_W / 2;
|
||||
const houseCenterY = houseY + HOUSE_H / 2;
|
||||
|
||||
// Prüfe horizontale Distanz (Taxi muss seitlich vom Haus sein)
|
||||
const horizontalDistance = Math.abs(taxiX - houseCenterX);
|
||||
const verticalDistance = Math.abs(taxiY - houseCenterY);
|
||||
|
||||
// Taxi muss horizontal nah genug sein, aber vertikal kann es weiter weg sein
|
||||
isNear = horizontalDistance <= maxDistance && verticalDistance <= maxDistance * 1.5;
|
||||
|
||||
} else if (corner === 'lu' || corner === 'ru') {
|
||||
// Vertikale Straße - Taxi muss oberhalb oder unterhalb vom Haus sein
|
||||
const houseCenterX = houseX + HOUSE_W / 2;
|
||||
const houseCenterY = houseY + HOUSE_H / 2;
|
||||
|
||||
// Prüfe vertikale Distanz (Taxi muss oberhalb/unterhalb vom Haus sein)
|
||||
const horizontalDistance = Math.abs(taxiX - houseCenterX);
|
||||
const verticalDistance = Math.abs(taxiY - houseCenterY);
|
||||
|
||||
// Taxi muss vertikal nah genug sein, aber horizontal kann es weiter weg sein
|
||||
isNear = verticalDistance <= maxDistance && horizontalDistance <= maxDistance * 1.5;
|
||||
}
|
||||
|
||||
console.log(`Haus-Erkennung: Corner=${corner}, Taxi(${taxiX},${taxiY}), Haus(${houseX},${houseY}), isNear=${isNear}`);
|
||||
|
||||
return isNear;
|
||||
},
|
||||
|
||||
pickupWaitingPassenger(passenger, index) {
|
||||
console.log('Passagier wird eingeladen:', passenger.name);
|
||||
|
||||
// Entferne Passagier aus der Warteliste
|
||||
this.waitingPassengersList.splice(index, 1);
|
||||
@@ -1330,9 +1457,8 @@ export default {
|
||||
|
||||
// Generiere ein Ziel für den Passagier
|
||||
const destination = this.generatePassengerDestination();
|
||||
console.log('Generiertes Ziel:', destination);
|
||||
|
||||
if (destination) {
|
||||
if (destination && destination.streetName && destination.streetName !== "Unbekannte Straße") {
|
||||
// Berechne Bonus und Zeit basierend auf kürzestem Weg
|
||||
const startX = passenger.tileX;
|
||||
const startY = passenger.tileY;
|
||||
@@ -1352,10 +1478,7 @@ export default {
|
||||
lastUpdateTime: 0, // Timer-Aktualisierung erst nach expliziter Aktivierung
|
||||
timerActive: false // Flag um Timer-Aktivierung zu kontrollieren
|
||||
});
|
||||
console.log('Passagier zur geladenen Liste hinzugefügt. Aktuelle Anzahl:', this.loadedPassengersList.length);
|
||||
console.log('Bonus-Daten:', bonusData);
|
||||
} else {
|
||||
console.log('Kein Ziel generiert - Passagier wird nicht hinzugefügt');
|
||||
}
|
||||
|
||||
// Optional: Sound-Effekt oder Nachricht
|
||||
@@ -1369,40 +1492,26 @@ export default {
|
||||
// Prüfe ob geladene Passagiere abgesetzt werden können
|
||||
if (this.loadedPassengersList.length === 0) return;
|
||||
|
||||
console.log('Prüfe Dropoff für', this.loadedPassengersList.length, 'geladene Passagiere');
|
||||
console.log('Aktuelle Tile-Position:', { col: this.currentTile.col, row: this.currentTile.row });
|
||||
|
||||
// Prüfe alle geladene Passagiere
|
||||
for (let i = this.loadedPassengersList.length - 1; i >= 0; i--) {
|
||||
const passenger = this.loadedPassengersList[i];
|
||||
const destination = passenger.destination;
|
||||
|
||||
console.log('Prüfe Passagier:', passenger.name, 'Ziel:', destination.location, 'Tile:', { x: destination.tileX, y: destination.tileY });
|
||||
|
||||
// Prüfe ob das Taxi am Zielort ist
|
||||
if (this.currentTile.col === destination.tileX && this.currentTile.row === destination.tileY) {
|
||||
console.log('Taxi ist am Zielort');
|
||||
|
||||
// Berechne die Position des Zielhauses auf dem aktuellen Tile
|
||||
const housePosition = this.getHousePositionOnTile(destination.tileX, destination.tileY, destination.houseCorner);
|
||||
if (housePosition) {
|
||||
console.log('Haus-Position gefunden:', housePosition);
|
||||
|
||||
// Prüfe ob das Taxi in der Nähe des Zielhauses ist
|
||||
const isNear = this.isTaxiNearHouse(housePosition, destination.houseCorner);
|
||||
console.log('Ist Taxi nah genug?', isNear);
|
||||
|
||||
if (isNear) {
|
||||
// Setze Passagier ab
|
||||
console.log('Setze Passagier ab:', passenger.name);
|
||||
console.log('✅ Passagier abgesetzt:', passenger.name, 'an', destination.location);
|
||||
this.dropoffLoadedPassenger(passenger, i);
|
||||
break; // Nur einen Passagier pro Frame absetzen
|
||||
}
|
||||
} else {
|
||||
console.log('Keine Haus-Position gefunden');
|
||||
}
|
||||
} else {
|
||||
console.log('Taxi ist nicht am Zielort');
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -1706,7 +1815,7 @@ export default {
|
||||
this.$root?.$refs?.messageDialog?.open?.(crashMessage, crashTitle, {}, this.handleCrashDialogClose);
|
||||
|
||||
// Test: Direkter Aufruf nach 3 Sekunden (falls Dialog-Callback nicht funktioniert)
|
||||
setTimeout(() => {
|
||||
this.crashDialogTimeout = setTimeout(() => {
|
||||
console.log('Test: Automatischer Reset nach 3 Sekunden');
|
||||
this.handleCrashDialogClose();
|
||||
}, 3000);
|
||||
@@ -2343,16 +2452,9 @@ export default {
|
||||
|
||||
async handleCanvasClick(event) {
|
||||
// AudioContext bei erster Benutzerinteraktion initialisieren
|
||||
if (import.meta.env?.DEV) console.log('[Audio] CanvasClick: start, hasCtx=', !!this.audioContext);
|
||||
this.ensureAudioUnlockedInEvent();
|
||||
if (this.audioContext && this.audioContext.state === 'suspended') {
|
||||
if (import.meta.env?.DEV) console.log('[Audio] CanvasClick: resume()');
|
||||
this.audioContext.resume().catch(() => {});
|
||||
if (import.meta.env?.DEV) console.log('[Audio] CanvasClick: state=', this.audioContext.state);
|
||||
}
|
||||
// Motor ggf. direkt starten (Klick ist User-Geste)
|
||||
if (this.motorSound && !this.motorSound.isPlaying) {
|
||||
if (import.meta.env?.DEV) console.log('[Audio] CanvasClick: motorSound.start()');
|
||||
this.motorSound.start();
|
||||
}
|
||||
},
|
||||
@@ -2361,15 +2463,9 @@ export default {
|
||||
this.keys[event.key] = true;
|
||||
|
||||
// AudioContext bei erster Benutzerinteraktion initialisieren
|
||||
if (import.meta.env?.DEV) console.log('[Audio] KeyDown: key=', event.key, 'hasCtx=', !!this.audioContext);
|
||||
this.ensureAudioUnlockedInEvent();
|
||||
if (this.audioContext && this.audioContext.state === 'suspended') {
|
||||
if (import.meta.env?.DEV) console.log('[Audio] KeyDown: resume()');
|
||||
this.audioContext.resume().catch(() => {});
|
||||
}
|
||||
// Bei Beschleunigungs-Key Motor starten (User-Geste garantiert)
|
||||
if ((event.key === 'ArrowUp' || event.key === 'w' || event.key === 'W') && this.motorSound && !this.motorSound.isPlaying) {
|
||||
if (import.meta.env?.DEV) console.log('[Audio] KeyDown: motorSound.start()');
|
||||
this.motorSound.start();
|
||||
// Direkt Parameter setzen, um hörbares Feedback ohne Verzögerung zu bekommen
|
||||
const speedKmh = Math.max(5, this.taxi.speed * 5);
|
||||
@@ -2384,6 +2480,11 @@ export default {
|
||||
},
|
||||
|
||||
ensureAudioUnlockedInEvent() {
|
||||
// Nur einmal initialisieren
|
||||
if (this.audioContext && this.motorSound) {
|
||||
return; // Bereits initialisiert
|
||||
}
|
||||
|
||||
// Muss synchron im Event-Handler laufen (ohne await), damit Policies greifen
|
||||
if (!this.audioContext) {
|
||||
this.audioContext = window.__TaxiAudioContext || new (window.AudioContext || window.webkitAudioContext)();
|
||||
@@ -2429,6 +2530,17 @@ export default {
|
||||
this.taxi.y = 250;
|
||||
this.taxi.angle = 0;
|
||||
this.taxi.speed = 0;
|
||||
|
||||
// Cleanup bestehender Timeouts
|
||||
if (this.passengerGenerationTimeout) {
|
||||
clearTimeout(this.passengerGenerationTimeout);
|
||||
this.passengerGenerationTimeout = null;
|
||||
}
|
||||
if (this.crashDialogTimeout) {
|
||||
clearTimeout(this.crashDialogTimeout);
|
||||
this.crashDialogTimeout = null;
|
||||
}
|
||||
|
||||
this.generateLevel();
|
||||
this.initializePassengerGeneration();
|
||||
},
|
||||
@@ -3001,6 +3113,7 @@ export default {
|
||||
|
||||
.lives { font-weight: 700; color: #d60000; }
|
||||
.fuel { font-weight: 600; color: #0a7c00; margin-left: 8px; }
|
||||
.score { font-weight: 700; color: #ffa500; margin-left: 8px; }
|
||||
.speed-violations { font-weight: 700; color: #8a2be2; margin-left: 8px; }
|
||||
|
||||
.speed-value {
|
||||
|
||||
Reference in New Issue
Block a user