Änderung: Implementierung der Passagier-Generierung und -verwaltung im Taxi-Spiel

Änderungen:
- Einführung einer neuen Logik zur Generierung und Verwaltung wartender Passagiere.
- Anpassung der Benutzeroberfläche zur Anzeige wartender Passagiere mit Informationen zu Namen und Standorten.
- Implementierung von Methoden zur Aktualisierung und Entfernung abgelaufener Passagiere aus der Warteliste.
- Verbesserung der Spielmechanik durch dynamische Passagier-Generierung basierend auf verfügbaren Häusern.

Diese Anpassungen erhöhen die Interaktivität und Komplexität des Spiels, indem sie eine realistischere Passagierverwaltung ermöglichen.
This commit is contained in:
Torsten Schulz (local)
2025-09-19 17:26:42 +02:00
parent 65b80c37b4
commit 71f25596e9

View File

@@ -122,33 +122,30 @@
<!-- Sidebar (rechts) --> <!-- Sidebar (rechts) -->
<div class="sidebar-section"> <div class="sidebar-section">
<!-- Statistiken --> <!-- Wartende Passagiere -->
<div class="stats-card"> <div class="waiting-passengers-card">
<div class="stats-header"> <div class="waiting-passengers-header">
<div class="stats-header-content"> <h3 class="waiting-passengers-title">Wartende Passagiere</h3>
<h3 class="stats-title">{{ $t('minigames.taxi.gameStats') }}</h3> </div>
<button class="toggle-button" @click="toggleStats"> <div class="waiting-passengers-list">
<span class="toggle-icon">{{ toggleIcon }}</span> <div v-if="waitingPassengersList.length === 0" class="no-passengers">
</button> Keine wartenden Passagiere
</div>
<div v-else>
<div
v-for="(passenger, index) in waitingPassengersList"
:key="index"
class="passenger-item"
>
<div class="passenger-info">
<span class="passenger-name">
{{ passenger.name }}
<span v-if="getPassengerTimeLeft(passenger) <= 5" class="passenger-timer">({{ getPassengerTimeLeft(passenger) }}s)</span>
</span>
<span class="passenger-location">{{ passenger.location }}</span>
</div> </div>
</div> </div>
<div class="stats-list" v-if="isStatsExpanded">
<div class="stat-row">
<span class="stat-value score-value">{{ score }}</span>
<span class="stat-label">{{ $t('minigames.taxi.score') }}</span>
</div> </div>
<div class="stat-row">
<span class="stat-value money-value">{{ money }}</span>
<span class="stat-label">{{ $t('minigames.taxi.money') }}</span>
</div>
<div class="stat-row">
<span class="stat-value passengers-value">{{ passengersDelivered }}</span>
<span class="stat-label">{{ $t('minigames.taxi.passengers') }}</span>
</div>
</div> </div>
</div> </div>
@@ -203,6 +200,10 @@ export default {
score: 0, score: 0,
money: 0, money: 0,
passengersDelivered: 0, passengersDelivered: 0,
waitingPassengersList: [],
occupiedHouses: new Set(), // Verhindert doppelte Belegung von Häusern
lastPassengerGeneration: 0,
passengerGenerationInterval: 0,
fuel: 100, fuel: 100,
currentLevel: 1, currentLevel: 1,
gameRunning: false, gameRunning: false,
@@ -216,7 +217,6 @@ export default {
ctx: null, ctx: null,
minimapCanvas: null, minimapCanvas: null,
minimapCtx: null, minimapCtx: null,
isStatsExpanded: true,
taxi: { taxi: {
x: 250, // Mitte des 400x400px Tiles x: 250, // Mitte des 400x400px Tiles
y: 250, // Mitte des 400x400px Tiles y: 250, // Mitte des 400x400px Tiles
@@ -453,6 +453,7 @@ export default {
this.currentTile.col = 0; this.currentTile.col = 0;
this.generateLevel(); this.generateLevel();
this.initializePassengerGeneration();
this.startGame(); this.startGame();
}, },
@@ -682,6 +683,225 @@ export default {
} }
}, },
initializePassengerGeneration() {
// Setze zufälliges Intervall für erste Passagier-Generierung (10-35 Sekunden)
this.passengerGenerationInterval = Math.floor(Math.random() * 25000) + 10000; // 10-35 Sekunden
this.lastPassengerGeneration = Date.now();
this.occupiedHouses.clear();
console.log('Passagier-Generierung initialisiert:', {
interval: this.passengerGenerationInterval,
currentMap: !!this.currentMap,
tileHouses: this.currentMap?.tileHouses?.length || 0
});
},
generateWaitingPassenger() {
if (!this.currentMap || !Array.isArray(this.currentMap.tileHouses)) return;
// Finde alle verfügbaren Häuser auf der Karte
const availableHouses = [];
const houses = this.currentMap.tileHouses || [];
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) {
console.log('Keine freien Häuser verfügbar für Passagier-Generierung');
return;
}
// Wähle zufälliges Haus
const selectedHouse = availableHouses[Math.floor(Math.random() * availableHouses.length)];
// Finde verfügbare Straßennamen aus der Map
const availableStreets = [];
if (this.currentMap.tileStreets) {
this.currentMap.tileStreets.forEach(ts => {
if (ts.streetNameH && ts.streetNameH.name) {
availableStreets.push(ts.streetNameH.name);
}
if (ts.streetNameV && ts.streetNameV.name) {
availableStreets.push(ts.streetNameV.name);
}
});
}
// Wenn keine Straßen in der Map vorhanden, verwende Fallback
let streetName, houseNumber;
if (availableStreets.length > 0) {
// Wähle zufällige Straße aus der Map
streetName = availableStreets[Math.floor(Math.random() * availableStreets.length)];
// Finde alle Hausnummern für diese Straße
const houseNumbersForStreet = this.getHouseNumbersForStreet(streetName);
if (houseNumbersForStreet.length > 0) {
// Wähle zufällige Hausnummer aus den existierenden
houseNumber = houseNumbersForStreet[Math.floor(Math.random() * houseNumbersForStreet.length)];
} else {
// Fallback falls keine Hausnummern für diese Straße gefunden
houseNumber = Math.floor(Math.random() * 200) + 1;
}
console.log('Verfügbare Straßen:', availableStreets);
console.log('Gewählte Straße:', streetName);
console.log('Hausnummern für Straße:', houseNumbersForStreet);
console.log('Gewählte Hausnummer:', houseNumber);
} else {
// Fallback falls keine Straßen in der Map
streetName = "Unbekannte Straße";
houseNumber = Math.floor(Math.random() * 200) + 1;
console.log('Keine Straßen in der Map gefunden, verwende Fallback');
}
// Erstelle Passagier
const passenger = {
id: Date.now() + Math.random(), // Eindeutige ID
name: this.generatePassengerName(),
location: `${streetName} ${houseNumber}`,
houseId: selectedHouse.houseId,
tileX: selectedHouse.x,
tileY: selectedHouse.y,
houseIndex: selectedHouse.houseIndex,
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);
console.log('Neuer 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 lastNames = [
'Müller', 'Schmidt', 'Schneider', 'Fischer', 'Weber', 'Meyer', 'Wagner', 'Becker',
'Schulz', 'Hoffmann', 'Schäfer', 'Koch', 'Bauer', 'Richter', 'Klein', 'Wolf'
];
const firstName = firstNames[Math.floor(Math.random() * firstNames.length)];
const lastName = lastNames[Math.floor(Math.random() * lastNames.length)];
return `${firstName} ${lastName}`;
},
updatePassengerGeneration() {
const now = Date.now();
const timeSinceLastGeneration = now - this.lastPassengerGeneration;
if (timeSinceLastGeneration >= this.passengerGenerationInterval) {
console.log('Generiere neuen Passagier...', {
timeSinceLastGeneration,
interval: this.passengerGenerationInterval,
currentMap: !!this.currentMap,
tileHouses: this.currentMap?.tileHouses?.length || 0
});
this.generateWaitingPassenger();
this.lastPassengerGeneration = now;
// Setze nächstes Intervall (10-35 Sekunden)
this.passengerGenerationInterval = Math.floor(Math.random() * 25000) + 10000;
console.log('Nächstes Intervall:', this.passengerGenerationInterval);
}
},
removePassengerFromWaitingList() {
if (this.waitingPassengersList.length > 0) {
// Entferne den ersten Passagier aus der Liste
const removedPassenger = this.waitingPassengersList.shift();
if (removedPassenger && removedPassenger.houseId) {
// Gib das Haus wieder frei
this.occupiedHouses.delete(removedPassenger.houseId);
}
}
},
removeExpiredPassengers() {
const now = Date.now();
const maxAge = 30000; // 30 Sekunden in Millisekunden
// Filtere abgelaufene Passagiere heraus
const expiredPassengers = [];
this.waitingPassengersList = this.waitingPassengersList.filter(passenger => {
const age = now - passenger.createdAt;
if (age > maxAge) {
expiredPassengers.push(passenger);
// Gib das Haus wieder frei
if (passenger.houseId) {
this.occupiedHouses.delete(passenger.houseId);
}
return false; // Entferne aus der Liste
}
return true; // Behalte in der Liste
});
if (expiredPassengers.length > 0) {
console.log(`${expiredPassengers.length} Passagiere entfernt (abgelaufen):`, expiredPassengers);
}
},
getPassengerTimeLeft(passenger) {
const now = Date.now();
const age = now - passenger.createdAt;
const maxAge = 30000; // 30 Sekunden
const timeLeft = Math.max(0, Math.ceil((maxAge - age) / 1000));
return timeLeft;
},
getHouseNumbersForStreet(streetName) {
const houseNumbers = [];
if (!this.currentMap || !this.currentMap.tileStreets || !this.houseNumbers) {
return houseNumbers;
}
// Finde alle Tiles mit dieser Straße
const tilesWithStreet = this.currentMap.tileStreets.filter(ts =>
(ts.streetNameH && ts.streetNameH.name === streetName) ||
(ts.streetNameV && ts.streetNameV.name === streetName)
);
// Sammle nur Hausnummern, die tatsächlich zu dieser Straße gehören
tilesWithStreet.forEach(ts => {
const isHorizontal = ts.streetNameH && ts.streetNameH.name === streetName;
const isVertical = ts.streetNameV && ts.streetNameV.name === streetName;
// Prüfe nur die Ecken, die zu dieser Straße gehören
const relevantCorners = [];
if (isHorizontal) {
// Für horizontale Straßen: linke und rechte Ecken
relevantCorners.push('lo', 'ro'); // left-outer, right-outer
}
if (isVertical) {
// Für vertikale Straßen: obere und untere Ecken
relevantCorners.push('lu', 'ru'); // left-upper, right-upper
}
// Sammle Hausnummern nur von den relevanten Ecken
relevantCorners.forEach(corner => {
const key = `${ts.x},${ts.y},${corner}`;
const houseNumber = this.houseNumbers[key];
if (houseNumber != null && !houseNumbers.includes(houseNumber)) {
houseNumbers.push(houseNumber);
}
});
});
return houseNumbers.sort((a, b) => a - b); // Sortiere aufsteigend
},
getRandomRoadPosition() { getRandomRoadPosition() {
const tileSize = this.tiles.size; const tileSize = this.tiles.size;
const cols = 8; const cols = 8;
@@ -754,6 +974,12 @@ export default {
// Ampelschaltung tick // Ampelschaltung tick
this.updateTrafficLights(); this.updateTrafficLights();
// Passagier-Generierung prüfen
this.updatePassengerGeneration();
// Abgelaufene Passagiere entfernen
this.removeExpiredPassengers();
this.updateTaxi(); this.updateTaxi();
this.handlePassengerActions(); this.handlePassengerActions();
this.checkCollisions(); this.checkCollisions();
@@ -879,6 +1105,8 @@ export default {
this.passengersDelivered++; this.passengersDelivered++;
this.score += 50; this.score += 50;
this.money += 25; this.money += 25;
// Entferne Passagier aus der Warteliste und gib Haus frei
this.removePassengerFromWaitingList();
break; break;
} }
} }
@@ -1796,21 +2024,21 @@ export default {
this.score = 0; this.score = 0;
this.money = 0; this.money = 0;
this.passengersDelivered = 0; this.passengersDelivered = 0;
this.waitingPassengersList = [];
this.occupiedHouses.clear();
this.fuel = 100; this.fuel = 100;
this.taxi.x = 250; this.taxi.x = 250;
this.taxi.y = 250; this.taxi.y = 250;
this.taxi.angle = 0; this.taxi.angle = 0;
this.taxi.speed = 0; this.taxi.speed = 0;
this.generateLevel(); this.generateLevel();
this.initializePassengerGeneration();
}, },
goBack() { goBack() {
this.$router.push('/minigames'); this.$router.push('/minigames');
}, },
toggleStats() {
this.isStatsExpanded = !this.isStatsExpanded;
},
initializeMinimap() { initializeMinimap() {
this.minimapCanvas = this.$refs.minimapCanvas; this.minimapCanvas = this.$refs.minimapCanvas;
@@ -2400,78 +2628,74 @@ export default {
top: 20px; top: 20px;
} }
/* Stats Card */ /* Waiting Passengers Card */
.stats-card { .waiting-passengers-card {
background: #fff; background: #fff;
border: 1px solid #ddd; border: 1px solid #ddd;
border-radius: 8px; border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1); box-shadow: 0 2px 4px rgba(0,0,0,0.1);
overflow: hidden; overflow: hidden;
margin-bottom: 20px;
} }
.stats-header { .waiting-passengers-header {
background: #f8f9fa; background: #f8f9fa;
border-bottom: 1px solid #ddd; border-bottom: 1px solid #ddd;
padding: 15px 20px; padding: 15px 20px;
} }
.stats-header-content { .waiting-passengers-title {
display: flex;
justify-content: space-between;
align-items: center;
}
.stats-title {
margin: 0; margin: 0;
font-size: 1.1rem; font-size: 1.1rem;
font-weight: 600; font-weight: 600;
color: #333; color: #333;
} }
.toggle-button { .waiting-passengers-list {
background: none;
border: none;
font-size: 1.2rem;
cursor: pointer;
padding: 5px;
color: #666;
}
.toggle-button:hover {
color: #333;
}
.stats-list {
padding: 20px; padding: 20px;
max-height: 300px;
overflow-y: auto;
} }
.stat-row { .no-passengers {
display: flex; text-align: center;
justify-content: space-between; color: #666;
align-items: center; font-style: italic;
padding: 8px 0; padding: 20px 0;
}
.passenger-item {
padding: 10px 0;
border-bottom: 1px solid #f0f0f0; border-bottom: 1px solid #f0f0f0;
} }
.stat-row:last-child { .passenger-item:last-child {
border-bottom: none; border-bottom: none;
} }
.stat-value { .passenger-info {
font-size: 1.2rem; display: flex;
font-weight: bold; flex-direction: column;
color: #333; gap: 4px;
} }
.stat-label { .passenger-name {
font-size: 0.9rem; font-weight: 600;
color: #333;
font-size: 0.95rem;
}
.passenger-location {
font-size: 0.85rem;
color: #666; color: #666;
} }
.score-value { color: #4CAF50; } .passenger-timer {
.money-value { color: #FF9800; } font-size: 0.8rem;
.passengers-value { color: #2196F3; } color: #ff5722;
.fuel-value { color: #9C27B0; } font-weight: bold;
margin-left: 4px;
}
/* Minimap Card */ /* Minimap Card */
.minimap-card { .minimap-card {