Ä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: {
|
methods: {
|
||||||
async fetchStatus() {
|
async fetchStatus() {
|
||||||
try {
|
try {
|
||||||
console.log('🔄 StatusBar: fetchStatus() startet...');
|
|
||||||
const response = await apiClient.get("/api/falukant/info");
|
const response = await apiClient.get("/api/falukant/info");
|
||||||
console.log('📊 StatusBar: API Response erhalten:', response.data);
|
|
||||||
const { money, character, events } = response.data;
|
const { money, character, events } = response.data;
|
||||||
const { age, health } = character;
|
const { age, health } = character;
|
||||||
const relationship = response.data.character.relationshipsAsCharacter1[0]?.relationshipType?.tr
|
const relationship = response.data.character.relationshipsAsCharacter1[0]?.relationshipType?.tr
|
||||||
@@ -112,27 +110,17 @@ export default {
|
|||||||
{ key: "events", icon: "📰", value: events || null, image: null },
|
{ key: "events", icon: "📰", value: events || null, image: null },
|
||||||
{ key: "children", icon: "👶", value: childrenDisplay },
|
{ key: "children", icon: "👶", value: childrenDisplay },
|
||||||
];
|
];
|
||||||
console.log('📊 StatusBar: statusItems aktualisiert:', this.statusItems);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error fetching status:", error);
|
// Error fetching status
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
handleEvent(eventData) {
|
handleEvent(eventData) {
|
||||||
console.log('🔄 StatusBar: handleEvent aufgerufen mit:', eventData);
|
|
||||||
console.log('🔄 StatusBar: Event-Typ:', eventData.event);
|
|
||||||
switch (eventData.event) {
|
switch (eventData.event) {
|
||||||
case 'falukantUpdateStatus':
|
case 'falukantUpdateStatus':
|
||||||
case 'stock_change':
|
case 'stock_change':
|
||||||
case 'familychanged':
|
case 'familychanged':
|
||||||
console.log('🔄 StatusBar: Rufe fetchStatus() auf...');
|
this.fetchStatus();
|
||||||
this.fetchStatus().then(() => {
|
|
||||||
console.log('✅ StatusBar: fetchStatus() erfolgreich abgeschlossen');
|
|
||||||
}).catch((error) => {
|
|
||||||
console.error('❌ StatusBar: fetchStatus() Fehler:', error);
|
|
||||||
});
|
|
||||||
break;
|
break;
|
||||||
default:
|
|
||||||
console.log('⚠️ StatusBar: Unbekanntes Event:', eventData.event);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
openPage(url, hasSubmenu = false) {
|
openPage(url, hasSubmenu = false) {
|
||||||
|
|||||||
@@ -145,15 +145,20 @@ const store = createStore({
|
|||||||
currentSocket.disconnect();
|
currentSocket.disconnect();
|
||||||
}
|
}
|
||||||
commit('setConnectionStatus', 'connecting');
|
commit('setConnectionStatus', 'connecting');
|
||||||
const socketIoUrl = import.meta.env.VITE_SOCKET_IO_URL || import.meta.env.VITE_API_BASE_URL;
|
// Socket.io URL für lokale Entwicklung und Produktion
|
||||||
console.log('🔌 Initializing Socket.io connection to:', socketIoUrl);
|
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, {
|
const socket = io(socketIoUrl, {
|
||||||
secure: true,
|
secure: true,
|
||||||
transports: ['websocket', 'polling']
|
transports: ['websocket', 'polling']
|
||||||
});
|
});
|
||||||
|
|
||||||
socket.on('connect', () => {
|
socket.on('connect', () => {
|
||||||
console.log('✅ Socket.io connected successfully');
|
|
||||||
retryCount = 0; // Reset retry counter on successful connection
|
retryCount = 0; // Reset retry counter on successful connection
|
||||||
commit('setConnectionStatus', 'connected');
|
commit('setConnectionStatus', 'connected');
|
||||||
const idForSocket = state.user?.hashedId || state.user?.id;
|
const idForSocket = state.user?.hashedId || state.user?.id;
|
||||||
@@ -161,14 +166,11 @@ const store = createStore({
|
|||||||
});
|
});
|
||||||
|
|
||||||
socket.on('disconnect', (reason) => {
|
socket.on('disconnect', (reason) => {
|
||||||
console.warn('❌ Socket.io disconnected:', reason);
|
|
||||||
commit('setConnectionStatus', 'disconnected');
|
commit('setConnectionStatus', 'disconnected');
|
||||||
retryConnection(connectSocket);
|
retryConnection(connectSocket);
|
||||||
});
|
});
|
||||||
|
|
||||||
socket.on('connect_error', (error) => {
|
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');
|
commit('setConnectionStatus', 'error');
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -179,30 +181,36 @@ const store = createStore({
|
|||||||
const maxRetries = 10;
|
const maxRetries = 10;
|
||||||
const retryConnection = (reconnectFn) => {
|
const retryConnection = (reconnectFn) => {
|
||||||
if (retryCount >= maxRetries) {
|
if (retryCount >= maxRetries) {
|
||||||
console.error('❌ Max retry attempts reached for Socket.io');
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
retryCount++;
|
retryCount++;
|
||||||
const delay = Math.min(1000 * Math.pow(1.5, retryCount - 1), 30000); // Exponential backoff, max 30s
|
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(() => {
|
setTimeout(() => {
|
||||||
reconnectFn();
|
reconnectFn();
|
||||||
}, delay);
|
}, delay);
|
||||||
};
|
};
|
||||||
|
|
||||||
connectSocket();
|
connectSocket();
|
||||||
} else {
|
|
||||||
console.log("User is not logged in or user data is not available.");
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
initializeDaemonSocket({ commit, state }) {
|
initializeDaemonSocket({ commit, state }) {
|
||||||
if (!state.isLoggedIn || !state.user) {
|
if (!state.isLoggedIn || !state.user) {
|
||||||
console.log("User is not logged in or user data is not available for Daemon WebSocket.");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const daemonUrl = import.meta.env.VITE_DAEMON_SOCKET || 'wss://www.your-part.de:4551';
|
// Daemon URL für lokale Entwicklung und Produktion
|
||||||
console.log('🔌 Initializing Daemon WebSocket connection to:', daemonUrl);
|
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 = () => {
|
const connectDaemonSocket = () => {
|
||||||
// Protokoll-Fallback: zuerst mit Subprotokoll, dann ohne
|
// Protokoll-Fallback: zuerst mit Subprotokoll, dann ohne
|
||||||
@@ -215,13 +223,11 @@ const store = createStore({
|
|||||||
const daemonSocket = currentProtocol
|
const daemonSocket = currentProtocol
|
||||||
? new WebSocket(daemonUrl, currentProtocol)
|
? new WebSocket(daemonUrl, currentProtocol)
|
||||||
: new WebSocket(daemonUrl);
|
: new WebSocket(daemonUrl);
|
||||||
console.log('🔌 Protocol:', currentProtocol ?? 'none (fallback)');
|
|
||||||
|
|
||||||
let opened = false;
|
let opened = false;
|
||||||
|
|
||||||
daemonSocket.onopen = () => {
|
daemonSocket.onopen = () => {
|
||||||
opened = true;
|
opened = true;
|
||||||
console.log('✅ Daemon WebSocket connected successfully');
|
|
||||||
retryCount = 0; // Reset retry counter on successful connection
|
retryCount = 0; // Reset retry counter on successful connection
|
||||||
const payload = JSON.stringify({
|
const payload = JSON.stringify({
|
||||||
user_id: state.user.id,
|
user_id: state.user.id,
|
||||||
@@ -232,17 +238,9 @@ const store = createStore({
|
|||||||
};
|
};
|
||||||
|
|
||||||
daemonSocket.onclose = (event) => {
|
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
|
// Falls Verbindungsaufbau nicht offen war und es noch einen Fallback gibt → nächsten Versuch ohne Subprotokoll
|
||||||
if (!opened && attemptIndex < protocols.length - 1) {
|
if (!opened && attemptIndex < protocols.length - 1) {
|
||||||
attemptIndex += 1;
|
attemptIndex += 1;
|
||||||
console.warn('🔄 Fallback: versuche erneut ohne Subprotokoll...');
|
|
||||||
tryConnectWithProtocol();
|
tryConnectWithProtocol();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -250,23 +248,9 @@ const store = createStore({
|
|||||||
};
|
};
|
||||||
|
|
||||||
daemonSocket.onerror = (error) => {
|
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
|
// Bei Fehler vor Open: Fallback versuchen
|
||||||
if (!opened && attemptIndex < protocols.length - 1) {
|
if (!opened && attemptIndex < protocols.length - 1) {
|
||||||
attemptIndex += 1;
|
attemptIndex += 1;
|
||||||
console.warn('🔄 Fallback: versuche erneut ohne Subprotokoll (onerror)...');
|
|
||||||
tryConnectWithProtocol();
|
tryConnectWithProtocol();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -281,21 +265,17 @@ const store = createStore({
|
|||||||
try {
|
try {
|
||||||
const data = JSON.parse(message);
|
const data = JSON.parse(message);
|
||||||
// Handle daemon messages here
|
// Handle daemon messages here
|
||||||
console.log('📨 Daemon message received:', data);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error parsing daemon message:", error);
|
// Error parsing daemon message
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
commit('setDaemonSocket', daemonSocket);
|
commit('setDaemonSocket', daemonSocket);
|
||||||
} catch (error) {
|
} 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
|
// Beim Konstruktionsfehler ebenfalls Fallback versuchen
|
||||||
if (attemptIndex < protocols.length - 1) {
|
if (attemptIndex < protocols.length - 1) {
|
||||||
attemptIndex += 1;
|
attemptIndex += 1;
|
||||||
console.warn('🔄 Fallback: Konstruktion ohne Subprotokoll...');
|
|
||||||
tryConnectWithProtocol();
|
tryConnectWithProtocol();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -310,18 +290,15 @@ const store = createStore({
|
|||||||
const maxRetries = 15; // Increased max retries
|
const maxRetries = 15; // Increased max retries
|
||||||
const retryConnection = (reconnectFn) => {
|
const retryConnection = (reconnectFn) => {
|
||||||
if (retryCount >= maxRetries) {
|
if (retryCount >= maxRetries) {
|
||||||
console.error('❌ Max retry attempts reached for Daemon WebSocket');
|
|
||||||
// Reset counter after a longer delay to allow for network recovery
|
// Reset counter after a longer delay to allow for network recovery
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
retryCount = 0;
|
retryCount = 0;
|
||||||
console.log('🔄 Resetting Daemon WebSocket retry counter - attempting reconnection');
|
|
||||||
reconnectFn();
|
reconnectFn();
|
||||||
}, 60000); // Wait 1 minute before resetting
|
}, 60000); // Wait 1 minute before resetting
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
retryCount++;
|
retryCount++;
|
||||||
const delay = Math.min(1000 * Math.pow(1.5, retryCount - 1), 30000); // Exponential backoff, max 30s
|
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(() => {
|
setTimeout(() => {
|
||||||
reconnectFn();
|
reconnectFn();
|
||||||
}, delay);
|
}, delay);
|
||||||
@@ -336,7 +313,6 @@ const store = createStore({
|
|||||||
const menu = await loadMenu();
|
const menu = await loadMenu();
|
||||||
commit('setMenu', menu);
|
commit('setMenu', menu);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
|
||||||
commit('setMenu', []);
|
commit('setMenu', []);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,19 +1,22 @@
|
|||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import store from '../store';
|
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 = () => {
|
const getApiBaseURL = () => {
|
||||||
// Wenn explizite Umgebungsvariable gesetzt ist, diese verwenden
|
// Wenn explizite Umgebungsvariable gesetzt ist, diese verwenden
|
||||||
if (import.meta.env.VITE_API_BASE_URL) {
|
if (import.meta.env.VITE_API_BASE_URL) {
|
||||||
return 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
|
// Für Produktion: Root-Pfad, da API-Endpunkte bereits mit /api beginnen
|
||||||
return '';
|
return '';
|
||||||
};
|
};
|
||||||
|
|
||||||
// Debug-Informationen
|
|
||||||
console.log('🔗 API Base URL:', getApiBaseURL());
|
|
||||||
|
|
||||||
const apiClient = axios.create({
|
const apiClient = axios.create({
|
||||||
baseURL: getApiBaseURL(),
|
baseURL: getApiBaseURL(),
|
||||||
@@ -28,8 +31,6 @@ apiClient.interceptors.request.use(config => {
|
|||||||
if (user && user.authCode) {
|
if (user && user.authCode) {
|
||||||
config.headers['userid'] = user.id;
|
config.headers['userid'] = user.id;
|
||||||
config.headers['authcode'] = user.authCode; // Kleinschreibung!
|
config.headers['authcode'] = user.authCode; // Kleinschreibung!
|
||||||
} else {
|
|
||||||
console.log('⚠️ Keine User-Daten verfügbar');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return config;
|
return config;
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="contenthidden">
|
<div class="contenthidden">
|
||||||
<StatusBar />
|
|
||||||
<div class="contentscroll">
|
<div class="contentscroll">
|
||||||
<!-- Spiel-Titel -->
|
<!-- Spiel-Titel -->
|
||||||
<div class="game-title">
|
<div class="game-title">
|
||||||
@@ -68,6 +67,7 @@
|
|||||||
<div class="tacho-speed">
|
<div class="tacho-speed">
|
||||||
<span class="lives" title="Verbleibende Fahrzeuge">❤️ {{ vehicleCount }}</span>
|
<span class="lives" title="Verbleibende Fahrzeuge">❤️ {{ vehicleCount }}</span>
|
||||||
<span class="fuel" title="Treibstoff">⛽ {{ Math.round(fuel) }}%</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="speed-violations" title="Geschwindigkeitsverstöße">📷 {{ speedViolations }}</span>
|
||||||
<span class="redlight-counter" title="Rote Ampeln überfahren">
|
<span class="redlight-counter" title="Rote Ampeln überfahren">
|
||||||
<span class="redlight-icon">🚦</span>
|
<span class="redlight-icon">🚦</span>
|
||||||
@@ -223,12 +223,9 @@
|
|||||||
import streetCoordinates from '../../utils/streetCoordinates.js';
|
import streetCoordinates from '../../utils/streetCoordinates.js';
|
||||||
import { MotorSound, NoiseGenerator } from '../../assets/motorSound.js';
|
import { MotorSound, NoiseGenerator } from '../../assets/motorSound.js';
|
||||||
import apiClient from '../../utils/axios.js';
|
import apiClient from '../../utils/axios.js';
|
||||||
import StatusBar from '../../components/falukant/StatusBar.vue';
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'TaxiGame',
|
name: 'TaxiGame',
|
||||||
components: {
|
components: {
|
||||||
StatusBar
|
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
@@ -242,6 +239,9 @@ export default {
|
|||||||
occupiedHouses: new Set(), // Verhindert doppelte Belegung von Häusern
|
occupiedHouses: new Set(), // Verhindert doppelte Belegung von Häusern
|
||||||
lastPassengerGeneration: 0,
|
lastPassengerGeneration: 0,
|
||||||
passengerGenerationInterval: 0,
|
passengerGenerationInterval: 0,
|
||||||
|
// Timeout-Referenzen für Cleanup
|
||||||
|
passengerGenerationTimeout: null,
|
||||||
|
crashDialogTimeout: null,
|
||||||
bonusMultiplier: 15, // Bonus pro Tile
|
bonusMultiplier: 15, // Bonus pro Tile
|
||||||
timePerTile: 8, // Sekunden pro Tile
|
timePerTile: 8, // Sekunden pro Tile
|
||||||
fuel: 100,
|
fuel: 100,
|
||||||
@@ -595,7 +595,6 @@ export default {
|
|||||||
|
|
||||||
async initializeMotorSound() {
|
async initializeMotorSound() {
|
||||||
// AudioContext wird erst bei erster Benutzerinteraktion erstellt
|
// 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() {
|
async initAudioOnUserInteraction() {
|
||||||
@@ -609,12 +608,9 @@ export default {
|
|||||||
this.audioContext = new (window.AudioContext || window.webkitAudioContext)();
|
this.audioContext = new (window.AudioContext || window.webkitAudioContext)();
|
||||||
window.__TaxiAudioContext = this.audioContext;
|
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
|
// Autoplay-Policy: Context sofort im User-Gesture fortsetzen
|
||||||
if (this.audioContext.state === 'suspended') {
|
if (this.audioContext.state === 'suspended') {
|
||||||
if (import.meta.env?.DEV) console.log('[Audio] init: resume() versuchen');
|
|
||||||
await this.audioContext.resume();
|
await this.audioContext.resume();
|
||||||
if (import.meta.env?.DEV) console.log('[Audio] init: state nach resume=', this.audioContext.state);
|
|
||||||
}
|
}
|
||||||
if (!this.motorSound) {
|
if (!this.motorSound) {
|
||||||
// Ggf. globalen Motor übernehmen
|
// Ggf. globalen Motor übernehmen
|
||||||
@@ -627,11 +623,9 @@ export default {
|
|||||||
window.__TaxiMotorSound = this.motorSound;
|
window.__TaxiMotorSound = this.motorSound;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (import.meta.env?.DEV) console.log('[Audio] init: MotorSound initialisiert');
|
|
||||||
// Falls bereits Bewegung anliegt, Sound direkt starten
|
// Falls bereits Bewegung anliegt, Sound direkt starten
|
||||||
const speedKmh = this.taxi.speed * 5;
|
const speedKmh = this.taxi.speed * 5;
|
||||||
if (speedKmh > 0) {
|
if (speedKmh > 0) {
|
||||||
if (import.meta.env?.DEV) console.log('[Audio] init: direkter Start. speedKmh=', speedKmh);
|
|
||||||
this.motorSound.start();
|
this.motorSound.start();
|
||||||
// Sofort Parameter setzen
|
// Sofort Parameter setzen
|
||||||
const speedFactor = Math.min(speedKmh / 120, 1);
|
const speedFactor = Math.min(speedKmh / 120, 1);
|
||||||
@@ -650,7 +644,6 @@ export default {
|
|||||||
|
|
||||||
const speedKmh = this.taxi.speed * 5; // Geschwindigkeit in km/h
|
const speedKmh = this.taxi.speed * 5; // Geschwindigkeit in km/h
|
||||||
const isMoving = speedKmh > 0;
|
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
|
// Start NICHT hier (kein User-Gesture). Stop mit 1s Verzögerung, wenn still
|
||||||
if (!isMoving) {
|
if (!isMoving) {
|
||||||
@@ -659,7 +652,6 @@ export default {
|
|||||||
// Nur stoppen, wenn weiterhin keine Bewegung
|
// Nur stoppen, wenn weiterhin keine Bewegung
|
||||||
const still = (this.taxi.speed * 5) <= 0;
|
const still = (this.taxi.speed * 5) <= 0;
|
||||||
if (still && this.motorSound && this.motorSound.isPlaying) {
|
if (still && this.motorSound && this.motorSound.isPlaying) {
|
||||||
if (import.meta.env?.DEV) console.log('[Audio] update: delayed motorSound.stop()');
|
|
||||||
this.motorSound.stop();
|
this.motorSound.stop();
|
||||||
}
|
}
|
||||||
this.motorStopTimeout = null;
|
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 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 motorSpeed = 0.3 + (speedFactor * 0.3); // 0.3 bis 1.5
|
||||||
const volume = 0.1 + (speedFactor * 0.4); // 0.1 bis 0.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.setSpeed(motorSpeed);
|
||||||
this.motorSound.setVolume(volume);
|
this.motorSound.setVolume(volume);
|
||||||
@@ -688,12 +679,22 @@ export default {
|
|||||||
cleanup() {
|
cleanup() {
|
||||||
if (this.gameLoop) {
|
if (this.gameLoop) {
|
||||||
cancelAnimationFrame(this.gameLoop);
|
cancelAnimationFrame(this.gameLoop);
|
||||||
|
this.gameLoop = null;
|
||||||
}
|
}
|
||||||
if (this.motorSound) { this.motorSound.stop(); }
|
if (this.motorSound) { this.motorSound.stop(); }
|
||||||
if (this.motorStopTimeout) {
|
if (this.motorStopTimeout) {
|
||||||
clearTimeout(this.motorStopTimeout);
|
clearTimeout(this.motorStopTimeout);
|
||||||
this.motorStopTimeout = null;
|
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
|
// AudioContext bleibt global erhalten, nicht schließen
|
||||||
document.removeEventListener('keydown', this.handleKeyDown);
|
document.removeEventListener('keydown', this.handleKeyDown);
|
||||||
document.removeEventListener('keyup', this.handleKeyUp);
|
document.removeEventListener('keyup', this.handleKeyUp);
|
||||||
@@ -703,6 +704,10 @@ export default {
|
|||||||
document.removeEventListener('keydown', this.audioUnlockHandler, { capture: true });
|
document.removeEventListener('keydown', this.audioUnlockHandler, { capture: true });
|
||||||
this.audioUnlockHandler = null;
|
this.audioUnlockHandler = null;
|
||||||
}
|
}
|
||||||
|
// Cleanup von Listen und Sets
|
||||||
|
this.waitingPassengersList = [];
|
||||||
|
this.loadedPassengersList = [];
|
||||||
|
this.occupiedHouses.clear();
|
||||||
},
|
},
|
||||||
|
|
||||||
generateLevel() {
|
generateLevel() {
|
||||||
@@ -736,7 +741,7 @@ export default {
|
|||||||
this.generateWaitingPassenger();
|
this.generateWaitingPassenger();
|
||||||
} else {
|
} else {
|
||||||
// Falls die Map noch nicht geladen ist, versuche es in 2 Sekunden erneut
|
// Falls die Map noch nicht geladen ist, versuche es in 2 Sekunden erneut
|
||||||
setTimeout(() => {
|
this.passengerGenerationTimeout = setTimeout(() => {
|
||||||
this.generateWaitingPassenger();
|
this.generateWaitingPassenger();
|
||||||
}, 2000);
|
}, 2000);
|
||||||
}
|
}
|
||||||
@@ -745,36 +750,39 @@ export default {
|
|||||||
generateWaitingPassenger() {
|
generateWaitingPassenger() {
|
||||||
if (!this.currentMap || !Array.isArray(this.currentMap.tileHouses)) {
|
if (!this.currentMap || !Array.isArray(this.currentMap.tileHouses)) {
|
||||||
// Versuche es in 2 Sekunden erneut
|
// Versuche es in 2 Sekunden erneut
|
||||||
setTimeout(() => {
|
this.passengerGenerationTimeout = setTimeout(() => {
|
||||||
this.generateWaitingPassenger();
|
this.generateWaitingPassenger();
|
||||||
}, 2000);
|
}, 2000);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Finde alle verfügbaren Häuser auf der Karte
|
// Erstelle Liste aller Tiles mit Häusern
|
||||||
const availableHouses = [];
|
const tilesWithHouses = this.getTilesWithHouses();
|
||||||
const houses = this.currentMap.tileHouses || [];
|
|
||||||
|
|
||||||
houses.forEach((house, houseIndex) => {
|
if (tilesWithHouses.length === 0) {
|
||||||
const houseId = `${house.x}-${house.y}-${houseIndex}`;
|
console.log('Keine Tiles mit Häusern gefunden');
|
||||||
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) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wähle zufälliges Haus
|
// Wähle zufälliges Tile mit Häusern
|
||||||
const selectedHouse = availableHouses[Math.floor(Math.random() * availableHouses.length)];
|
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
|
// Finde die Straße für dieses spezifische Haus
|
||||||
let streetName = "Unbekannte Straße";
|
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);
|
const houseTile = this.currentMap.tileStreets?.find(ts => ts.x === selectedHouse.x && ts.y === selectedHouse.y);
|
||||||
if (houseTile) {
|
if (houseTile) {
|
||||||
// Bestimme die Straße basierend auf der Haus-Ecke
|
// Bestimme die Straße basierend auf der Haus-Ecke
|
||||||
const corner = selectedHouse.house.corner;
|
const corner = selectedHouse.corner;
|
||||||
if (corner === 'lo' || corner === 'ro') {
|
if (corner === 'lo' || corner === 'ro') {
|
||||||
// Horizontale Straße
|
// Horizontale Straße
|
||||||
if (houseTile.streetNameH && houseTile.streetNameH.name) {
|
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
|
// 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;
|
houseNumber = this.houseNumbers[houseKey] || 1;
|
||||||
|
|
||||||
|
// Generiere Namen und Geschlecht
|
||||||
|
const nameData = this.generatePassengerName();
|
||||||
|
|
||||||
// Erstelle Passagier
|
// Erstelle Passagier
|
||||||
const passenger = {
|
const passenger = {
|
||||||
id: Date.now() + Math.random(), // Eindeutige ID
|
id: Date.now() + Math.random(), // Eindeutige ID
|
||||||
name: this.generatePassengerName(),
|
name: nameData.fullName,
|
||||||
|
gender: nameData.gender,
|
||||||
location: `${streetName} ${houseNumber}`,
|
location: `${streetName} ${houseNumber}`,
|
||||||
houseId: selectedHouse.houseId,
|
houseId: houseId,
|
||||||
tileX: selectedHouse.x,
|
tileX: selectedHouse.x,
|
||||||
tileY: selectedHouse.y,
|
tileY: selectedHouse.y,
|
||||||
houseIndex: selectedHouse.houseIndex,
|
houseIndex: houseIndex,
|
||||||
houseCorner: selectedHouse.house.corner, // Speichere auch die Ecke
|
houseCorner: selectedHouse.corner, // Speichere auch die Ecke
|
||||||
imageIndex: this.generatePassengerImageIndex(), // Zufälliger Bildindex 1-6
|
imageIndex: this.generatePassengerImageIndex(nameData.gender), // Bildindex basierend auf Geschlecht
|
||||||
createdAt: Date.now() // Zeitstempel der Erstellung
|
createdAt: Date.now() // Zeitstempel der Erstellung
|
||||||
};
|
};
|
||||||
|
|
||||||
// Füge Passagier zur Liste hinzu und markiere Haus als belegt
|
// Füge Passagier zur Liste hinzu und markiere Haus als belegt
|
||||||
this.waitingPassengersList.push(passenger);
|
this.waitingPassengersList.push(passenger);
|
||||||
this.occupiedHouses.add(selectedHouse.houseId);
|
this.occupiedHouses.add(houseId);
|
||||||
|
|
||||||
|
console.log('Neuer wartender Passagier generiert:', passenger);
|
||||||
},
|
},
|
||||||
|
|
||||||
generatePassengerName() {
|
generatePassengerName() {
|
||||||
const firstNames = [
|
const maleNames = [
|
||||||
'Anna', 'Max', 'Lisa', 'Tom', 'Sarah', 'Ben', 'Emma', 'Lukas', 'Julia', 'Paul',
|
'Max', 'Tom', 'Ben', 'Lukas', 'Paul', 'Felix', 'Jonas', 'Tim', 'Noah', 'Finn',
|
||||||
'Marie', 'Felix', 'Sophie', 'Jonas', 'Laura', 'Tim', 'Mia', 'Noah', 'Emma', '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 = [
|
const lastNames = [
|
||||||
'Müller', 'Schmidt', 'Schneider', 'Fischer', 'Weber', 'Meyer', 'Wagner', 'Becker',
|
'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 firstName = firstNames[Math.floor(Math.random() * firstNames.length)];
|
||||||
const lastName = lastNames[Math.floor(Math.random() * lastNames.length)];
|
const lastName = lastNames[Math.floor(Math.random() * lastNames.length)];
|
||||||
return `${firstName} ${lastName}`;
|
|
||||||
|
return {
|
||||||
|
fullName: `${firstName} ${lastName}`,
|
||||||
|
gender: isMale ? 'male' : 'female'
|
||||||
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
generatePassengerImageIndex() {
|
generatePassengerImageIndex(gender) {
|
||||||
// Zufälliger Bildindex 1-6 (1-3 männlich, 4-6 weiblich)
|
// Bildindex basierend auf Geschlecht (1-3 männlich, 4-6 weiblich)
|
||||||
return Math.floor(Math.random() * 6) + 1;
|
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) {
|
calculateShortestPath(startX, startY, endX, endY) {
|
||||||
// Berechne Manhattan-Distanz (kürzester Weg in einem Raster)
|
// Berechne Manhattan-Distanz (kürzester Weg in einem Raster)
|
||||||
const distance = Math.abs(endX - startX) + Math.abs(endY - startY);
|
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) {
|
calculateBonusAndTime(startX, startY, endX, endY) {
|
||||||
const shortestPath = this.calculateShortestPath(startX, startY, endX, endY);
|
const shortestPath = this.calculateShortestPath(startX, startY, endX, endY);
|
||||||
const bonus = shortestPath * this.bonusMultiplier;
|
const bonus = shortestPath * this.bonusMultiplier;
|
||||||
const maxTime = shortestPath * this.timePerTile;
|
const maxTime = shortestPath * this.timePerTile;
|
||||||
|
|
||||||
|
console.log(`Wegstrecke berechnet: Start(${startX},${startY}) -> Ziel(${endX},${endY}) = ${shortestPath} Tiles, Bonus: ${bonus}, Zeit: ${maxTime}s`);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
shortestPath,
|
shortestPath,
|
||||||
bonus,
|
bonus,
|
||||||
@@ -860,60 +939,66 @@ export default {
|
|||||||
|
|
||||||
generatePassengerDestination() {
|
generatePassengerDestination() {
|
||||||
// Generiere ein zufälliges Ziel für den Passagier
|
// 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
|
// Finde alle Straßen-Tiles auf der Karte
|
||||||
const streets = this.currentMap.tileStreets || [];
|
const streets = this.currentMap.tileStreets || [];
|
||||||
const houses = this.currentMap.tileHouses || [];
|
const houses = this.currentMap.tileHouses || [];
|
||||||
|
|
||||||
// Versuche maximal 10 Mal, ein gültiges Ziel zu finden
|
// Sammle alle gültigen Ziele vor der zufälligen Auswahl
|
||||||
for (let attempt = 0; attempt < 10; attempt++) {
|
const validDestinations = [];
|
||||||
// Wähle zufällige Straße
|
|
||||||
const randomStreet = streets[Math.floor(Math.random() * streets.length)];
|
|
||||||
|
|
||||||
// Prüfe ob diese Straße Häuser hat
|
for (const street of streets) {
|
||||||
const housesOnThisTile = houses.filter(house => house.x === randomStreet.x && house.y === randomStreet.y);
|
// Prüfe ob diese Straße Häuser hat
|
||||||
|
const housesOnThisTile = houses.filter(house => house.x === street.x && house.y === street.y);
|
||||||
|
|
||||||
if (housesOnThisTile.length > 0) {
|
if (housesOnThisTile.length > 0) {
|
||||||
// Wähle zufälliges Haus auf diesem Tile
|
for (const house of housesOnThisTile) {
|
||||||
const selectedHouse = housesOnThisTile[Math.floor(Math.random() * housesOnThisTile.length)];
|
const corner = house.corner;
|
||||||
|
let streetName = null;
|
||||||
|
|
||||||
// Bestimme die Straße basierend auf der Haus-Ecke
|
if (corner === 'lo' || corner === 'ro') {
|
||||||
let streetName = "Unbekannte Straße";
|
// Horizontale Straße
|
||||||
let houseNumber = 1;
|
if (street.streetNameH && street.streetNameH.name) {
|
||||||
|
streetName = street.streetNameH.name;
|
||||||
|
}
|
||||||
|
} else if (corner === 'lu' || corner === 'ru') {
|
||||||
|
// Vertikale Straße
|
||||||
|
if (street.streetNameV && street.streetNameV.name) {
|
||||||
|
streetName = street.streetNameV.name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const corner = selectedHouse.corner;
|
// 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];
|
||||||
|
|
||||||
if (corner === 'lo' || corner === 'ro') {
|
if (houseNumber != null) {
|
||||||
// Horizontale Straße
|
validDestinations.push({
|
||||||
if (randomStreet.streetNameH && randomStreet.streetNameH.name) {
|
streetName,
|
||||||
streetName = randomStreet.streetNameH.name;
|
houseNumber,
|
||||||
}
|
location: `${streetName} ${houseNumber}`,
|
||||||
} else if (corner === 'lu' || corner === 'ru') {
|
tileX: street.x,
|
||||||
// Vertikale Straße
|
tileY: street.y,
|
||||||
if (randomStreet.streetNameV && randomStreet.streetNameV.name) {
|
houseCorner: house.corner
|
||||||
streetName = randomStreet.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)];
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
if (validDestinations.length > 0) {
|
||||||
streetName,
|
// Wähle zufälliges gültiges Ziel
|
||||||
houseNumber,
|
const selectedDestination = validDestinations[Math.floor(Math.random() * validDestinations.length)];
|
||||||
location: `${streetName} ${houseNumber}`,
|
return selectedDestination;
|
||||||
tileX: randomStreet.x,
|
|
||||||
tileY: randomStreet.y,
|
|
||||||
houseCorner: selectedHouse.corner
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wenn nach 10 Versuchen kein Ziel gefunden wurde
|
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -941,7 +1026,6 @@ export default {
|
|||||||
if (timeSincePickup >= 2000) {
|
if (timeSincePickup >= 2000) {
|
||||||
passenger.timerActive = true;
|
passenger.timerActive = true;
|
||||||
passenger.lastUpdateTime = now;
|
passenger.lastUpdateTime = now;
|
||||||
console.log('Timer für Passagier', passenger.name, 'aktiviert nach', timeSincePickup, 'ms');
|
|
||||||
}
|
}
|
||||||
return; // Timer noch nicht aktiv
|
return; // Timer noch nicht aktiv
|
||||||
}
|
}
|
||||||
@@ -1106,6 +1190,10 @@ export default {
|
|||||||
|
|
||||||
startGame() {
|
startGame() {
|
||||||
this.gameRunning = true;
|
this.gameRunning = true;
|
||||||
|
// Stoppe bestehende Game-Loop falls vorhanden
|
||||||
|
if (this.gameLoop) {
|
||||||
|
cancelAnimationFrame(this.gameLoop);
|
||||||
|
}
|
||||||
this.gameLoop = requestAnimationFrame(this.update);
|
this.gameLoop = requestAnimationFrame(this.update);
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -1264,6 +1352,11 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
getHousePositionOnTile(tileX, tileY, corner) {
|
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
|
// Berechne die Position des Hauses basierend auf der Ecke
|
||||||
// Diese Methode muss exakt mit drawHousesOnMainCanvas übereinstimmen
|
// Diese Methode muss exakt mit drawHousesOnMainCanvas übereinstimmen
|
||||||
const tileSize = 500; // Tile-Größe
|
const tileSize = 500; // Tile-Größe
|
||||||
@@ -1306,19 +1399,53 @@ export default {
|
|||||||
const HOUSE_H = 150; // Haushöhe
|
const HOUSE_H = 150; // Haushöhe
|
||||||
const maxDistance = 100; // 100px Entfernung
|
const maxDistance = 100; // 100px Entfernung
|
||||||
|
|
||||||
// Einfache Distanzberechnung zum Hauszentrum
|
// Berechne die Entfernung zur Tile-Mitte
|
||||||
const houseCenterX = houseX + HOUSE_W / 2;
|
const tileCenterX = 250; // Mitte des 500px Canvas
|
||||||
const houseCenterY = houseY + HOUSE_H / 2;
|
const tileCenterY = 250; // Mitte des 500px Canvas
|
||||||
const distance = Math.sqrt(
|
const distanceToTileCenter = Math.sqrt(
|
||||||
Math.pow(taxiX - houseCenterX, 2) + Math.pow(taxiY - houseCenterY, 2)
|
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) {
|
pickupWaitingPassenger(passenger, index) {
|
||||||
console.log('Passagier wird eingeladen:', passenger.name);
|
|
||||||
|
|
||||||
// Entferne Passagier aus der Warteliste
|
// Entferne Passagier aus der Warteliste
|
||||||
this.waitingPassengersList.splice(index, 1);
|
this.waitingPassengersList.splice(index, 1);
|
||||||
@@ -1330,9 +1457,8 @@ export default {
|
|||||||
|
|
||||||
// Generiere ein Ziel für den Passagier
|
// Generiere ein Ziel für den Passagier
|
||||||
const destination = this.generatePassengerDestination();
|
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
|
// Berechne Bonus und Zeit basierend auf kürzestem Weg
|
||||||
const startX = passenger.tileX;
|
const startX = passenger.tileX;
|
||||||
const startY = passenger.tileY;
|
const startY = passenger.tileY;
|
||||||
@@ -1352,10 +1478,7 @@ export default {
|
|||||||
lastUpdateTime: 0, // Timer-Aktualisierung erst nach expliziter Aktivierung
|
lastUpdateTime: 0, // Timer-Aktualisierung erst nach expliziter Aktivierung
|
||||||
timerActive: false // Flag um Timer-Aktivierung zu kontrollieren
|
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 {
|
} else {
|
||||||
console.log('Kein Ziel generiert - Passagier wird nicht hinzugefügt');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Optional: Sound-Effekt oder Nachricht
|
// Optional: Sound-Effekt oder Nachricht
|
||||||
@@ -1369,40 +1492,26 @@ export default {
|
|||||||
// Prüfe ob geladene Passagiere abgesetzt werden können
|
// Prüfe ob geladene Passagiere abgesetzt werden können
|
||||||
if (this.loadedPassengersList.length === 0) return;
|
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
|
// Prüfe alle geladene Passagiere
|
||||||
for (let i = this.loadedPassengersList.length - 1; i >= 0; i--) {
|
for (let i = this.loadedPassengersList.length - 1; i >= 0; i--) {
|
||||||
const passenger = this.loadedPassengersList[i];
|
const passenger = this.loadedPassengersList[i];
|
||||||
const destination = passenger.destination;
|
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
|
// Prüfe ob das Taxi am Zielort ist
|
||||||
if (this.currentTile.col === destination.tileX && this.currentTile.row === destination.tileY) {
|
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
|
// Berechne die Position des Zielhauses auf dem aktuellen Tile
|
||||||
const housePosition = this.getHousePositionOnTile(destination.tileX, destination.tileY, destination.houseCorner);
|
const housePosition = this.getHousePositionOnTile(destination.tileX, destination.tileY, destination.houseCorner);
|
||||||
if (housePosition) {
|
if (housePosition) {
|
||||||
console.log('Haus-Position gefunden:', housePosition);
|
|
||||||
|
|
||||||
// Prüfe ob das Taxi in der Nähe des Zielhauses ist
|
// Prüfe ob das Taxi in der Nähe des Zielhauses ist
|
||||||
const isNear = this.isTaxiNearHouse(housePosition, destination.houseCorner);
|
const isNear = this.isTaxiNearHouse(housePosition, destination.houseCorner);
|
||||||
console.log('Ist Taxi nah genug?', isNear);
|
|
||||||
|
|
||||||
if (isNear) {
|
if (isNear) {
|
||||||
// Setze Passagier ab
|
// Setze Passagier ab
|
||||||
console.log('Setze Passagier ab:', passenger.name);
|
console.log('✅ Passagier abgesetzt:', passenger.name, 'an', destination.location);
|
||||||
this.dropoffLoadedPassenger(passenger, i);
|
this.dropoffLoadedPassenger(passenger, i);
|
||||||
break; // Nur einen Passagier pro Frame absetzen
|
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);
|
this.$root?.$refs?.messageDialog?.open?.(crashMessage, crashTitle, {}, this.handleCrashDialogClose);
|
||||||
|
|
||||||
// Test: Direkter Aufruf nach 3 Sekunden (falls Dialog-Callback nicht funktioniert)
|
// Test: Direkter Aufruf nach 3 Sekunden (falls Dialog-Callback nicht funktioniert)
|
||||||
setTimeout(() => {
|
this.crashDialogTimeout = setTimeout(() => {
|
||||||
console.log('Test: Automatischer Reset nach 3 Sekunden');
|
console.log('Test: Automatischer Reset nach 3 Sekunden');
|
||||||
this.handleCrashDialogClose();
|
this.handleCrashDialogClose();
|
||||||
}, 3000);
|
}, 3000);
|
||||||
@@ -2343,16 +2452,9 @@ export default {
|
|||||||
|
|
||||||
async handleCanvasClick(event) {
|
async handleCanvasClick(event) {
|
||||||
// AudioContext bei erster Benutzerinteraktion initialisieren
|
// AudioContext bei erster Benutzerinteraktion initialisieren
|
||||||
if (import.meta.env?.DEV) console.log('[Audio] CanvasClick: start, hasCtx=', !!this.audioContext);
|
|
||||||
this.ensureAudioUnlockedInEvent();
|
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)
|
// Motor ggf. direkt starten (Klick ist User-Geste)
|
||||||
if (this.motorSound && !this.motorSound.isPlaying) {
|
if (this.motorSound && !this.motorSound.isPlaying) {
|
||||||
if (import.meta.env?.DEV) console.log('[Audio] CanvasClick: motorSound.start()');
|
|
||||||
this.motorSound.start();
|
this.motorSound.start();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -2361,15 +2463,9 @@ export default {
|
|||||||
this.keys[event.key] = true;
|
this.keys[event.key] = true;
|
||||||
|
|
||||||
// AudioContext bei erster Benutzerinteraktion initialisieren
|
// AudioContext bei erster Benutzerinteraktion initialisieren
|
||||||
if (import.meta.env?.DEV) console.log('[Audio] KeyDown: key=', event.key, 'hasCtx=', !!this.audioContext);
|
|
||||||
this.ensureAudioUnlockedInEvent();
|
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)
|
// Bei Beschleunigungs-Key Motor starten (User-Geste garantiert)
|
||||||
if ((event.key === 'ArrowUp' || event.key === 'w' || event.key === 'W') && this.motorSound && !this.motorSound.isPlaying) {
|
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();
|
this.motorSound.start();
|
||||||
// Direkt Parameter setzen, um hörbares Feedback ohne Verzögerung zu bekommen
|
// Direkt Parameter setzen, um hörbares Feedback ohne Verzögerung zu bekommen
|
||||||
const speedKmh = Math.max(5, this.taxi.speed * 5);
|
const speedKmh = Math.max(5, this.taxi.speed * 5);
|
||||||
@@ -2384,6 +2480,11 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
ensureAudioUnlockedInEvent() {
|
ensureAudioUnlockedInEvent() {
|
||||||
|
// Nur einmal initialisieren
|
||||||
|
if (this.audioContext && this.motorSound) {
|
||||||
|
return; // Bereits initialisiert
|
||||||
|
}
|
||||||
|
|
||||||
// Muss synchron im Event-Handler laufen (ohne await), damit Policies greifen
|
// Muss synchron im Event-Handler laufen (ohne await), damit Policies greifen
|
||||||
if (!this.audioContext) {
|
if (!this.audioContext) {
|
||||||
this.audioContext = window.__TaxiAudioContext || new (window.AudioContext || window.webkitAudioContext)();
|
this.audioContext = window.__TaxiAudioContext || new (window.AudioContext || window.webkitAudioContext)();
|
||||||
@@ -2429,6 +2530,17 @@ export default {
|
|||||||
this.taxi.y = 250;
|
this.taxi.y = 250;
|
||||||
this.taxi.angle = 0;
|
this.taxi.angle = 0;
|
||||||
this.taxi.speed = 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.generateLevel();
|
||||||
this.initializePassengerGeneration();
|
this.initializePassengerGeneration();
|
||||||
},
|
},
|
||||||
@@ -3001,6 +3113,7 @@ export default {
|
|||||||
|
|
||||||
.lives { font-weight: 700; color: #d60000; }
|
.lives { font-weight: 700; color: #d60000; }
|
||||||
.fuel { font-weight: 600; color: #0a7c00; margin-left: 8px; }
|
.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-violations { font-weight: 700; color: #8a2be2; margin-left: 8px; }
|
||||||
|
|
||||||
.speed-value {
|
.speed-value {
|
||||||
|
|||||||
Reference in New Issue
Block a user