Files
yourpart3/backend/services/taxiMapService.js
Torsten Schulz (local) 5142243a88 Änderung: Hinzufügung der Ampel-Logik zur Taxi-Map
Änderungen:
- Erweiterung des TaxiMapTile-Modells um die Spalte trafficLight zur Verwaltung von Ampelzuständen.
- Anpassung der TaxiMapService-Logik zur Unterstützung der Ampel-Updates und -Zustände.
- Implementierung von Methoden zur Steuerung und Anzeige von Ampeln in der Benutzeroberfläche, einschließlich der neuen Funktionen in TaxiToolsView.vue und TaxiGame.vue.
- Verbesserung der Darstellung und Logik zur Ampelsteuerung im Spiel, einschließlich der visuellen Darstellung und der Interaktion mit Ampeln.

Diese Anpassungen verbessern die Funktionalität und Benutzererfahrung im Taxi-Minispiel erheblich, indem sie eine realistischere Verkehrssteuerung ermöglichen.
2025-09-18 18:48:36 +02:00

329 lines
9.9 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import BaseService from './BaseService.js';
import TaxiMap from '../models/taxi/taxiMap.js';
import TaxiMapType from '../models/taxi/taxiMapType.js';
import TaxiMapTile from '../models/taxi/taxiMapTile.js';
import TaxiStreetName from '../models/taxi/taxiStreetName.js';
import TaxiMapTileStreet from '../models/taxi/taxiMapTileStreet.js';
import TaxiMapTileHouse from '../models/taxi/taxiMapTileHouse.js';
class TaxiMapService extends BaseService {
constructor() {
super();
}
/**
* Holt alle verfügbaren Map-Typen
*/
async getMapTypes() {
try {
const mapTypes = await TaxiMapType.findAll({
where: { isActive: true },
order: [['name', 'ASC']]
});
return mapTypes;
} catch (error) {
console.error('Error getting map types:', error);
throw error;
}
}
/**
* Holt alle verfügbaren Maps
*/
async getMaps() {
try {
const maps = await TaxiMap.findAll({
where: { isActive: true },
include: [
{ model: TaxiMapType, as: 'mapType' },
{ model: TaxiMapTile, as: 'tiles' },
{
model: TaxiMapTileStreet,
as: 'tileStreets',
include: [
{ model: TaxiStreetName, as: 'streetNameH' },
{ model: TaxiStreetName, as: 'streetNameV' }
]
},
{ model: TaxiMapTileHouse, as: 'tileHouses' }
],
order: [['name', 'ASC']]
});
return maps;
} catch (error) {
console.error('Error getting maps:', error);
throw error;
}
}
/**
* Holt eine spezifische Map
*/
async getMapById(mapId) {
try {
const map = await TaxiMap.findOne({
where: {
id: mapId,
isActive: true
},
include: [
{ model: TaxiMapType, as: 'mapType' },
{ model: TaxiMapTile, as: 'tiles' },
{
model: TaxiMapTileStreet,
as: 'tileStreets',
include: [
{ model: TaxiStreetName, as: 'streetNameH' },
{ model: TaxiStreetName, as: 'streetNameV' }
]
},
{ model: TaxiMapTileHouse, as: 'tileHouses' }
]
});
return map;
} catch (error) {
console.error('Error getting map by ID:', error);
throw error;
}
}
/**
* Holt die Standard-Map
*/
async getDefaultMap() {
try {
const map = await TaxiMap.findOne({
where: {
isDefault: true,
isActive: true
},
include: [{
model: TaxiMapType,
as: 'mapType'
}]
});
return map;
} catch (error) {
console.error('Error getting default map:', error);
throw error;
}
}
/**
* Erstellt eine neue Map
*/
async createMap(mapData) {
try {
const { tiles, tileStreetNames, tileHouses, ...mapFields } = mapData;
// mapData JSON ist entfernt map erstellen ohne mapData
const map = await TaxiMap.create(mapFields);
// Tiles upsert (optional)
if (Array.isArray(tiles) && tiles.length > 0) {
await this.upsertTiles(map.id, tiles);
}
// Street names (optional)
if (tileStreetNames && Object.keys(tileStreetNames).length > 0) {
await this.upsertTileStreetNames(map.id, tileStreetNames);
}
if (tileHouses && Object.keys(tileHouses).length > 0) {
await this.upsertTileHouses(map.id, tileHouses);
}
return await this.getMapById(map.id);
} catch (error) {
console.error('Error creating map:', error);
throw error;
}
}
/**
* Aktualisiert eine Map
*/
async updateMap(mapId, updateData) {
try {
const { tiles, tileStreetNames, tileHouses, ...mapFields } = updateData;
const [updatedRowsCount] = await TaxiMap.update(mapFields, {
where: { id: mapId }
});
if (updatedRowsCount === 0) {
throw new Error('Map not found');
}
// Tiles upsert (optional)
if (Array.isArray(tiles)) {
await this.upsertTiles(mapId, tiles);
}
if (tileStreetNames && Object.keys(tileStreetNames).length > 0) {
await this.upsertTileStreetNames(mapId, tileStreetNames);
}
if (tileHouses && Object.keys(tileHouses).length > 0) {
await this.upsertTileHouses(mapId, tileHouses);
}
return await this.getMapById(mapId);
} catch (error) {
console.error('Error updating map:', error);
throw error;
}
}
/**
* Speichert Straßennamen-Belegungen für eine Map
* @param {number} mapId
* @param {{[cellKey:string]:{streetNameH?:string, streetNameV?:string}}} tileNames
*/
async upsertTileStreetNames(mapId, tileNames) {
const entries = Object.entries(tileNames || {});
for (const [cellKey, data] of entries) {
const [x, y] = cellKey.split(',').map(Number);
const record = { mapId, x, y };
// Street names: findOrCreate
if (data.streetNameH) {
const [snH] = await TaxiStreetName.findOrCreate({ where: { name: data.streetNameH }, defaults: { name: data.streetNameH } });
record.streetNameHId = snH.id;
} else {
record.streetNameHId = null;
}
if (data.streetNameV) {
const [snV] = await TaxiStreetName.findOrCreate({ where: { name: data.streetNameV }, defaults: { name: data.streetNameV } });
record.streetNameVId = snV.id;
} else {
record.streetNameVId = null;
}
const [row] = await TaxiMapTileStreet.findOrCreate({ where: { mapId, x, y }, defaults: record });
await row.update(record);
}
return { success: true };
}
/**
* Upsert Tiles (x,y,tileType,meta?) pro Map
* Erwartet: tiles: Array<{x:number,y:number,tileType:string, meta?:object}>
*/
async upsertTiles(mapId, tiles) {
for (const tile of tiles) {
const { x, y, tileType, meta } = tile;
if (typeof x !== 'number' || typeof y !== 'number' || !tileType) continue;
const [row] = await TaxiMapTile.findOrCreate({
where: { mapId, x, y },
defaults: { mapId, x, y, tileType, meta: meta || null }
});
// trafficLight kann in meta.trafficLight oder künftig in eigener Spalte liegen
const trafficLight = !!(meta && meta.trafficLight);
await row.update({ tileType, meta: meta && Object.keys(meta).length ? meta : null, trafficLight });
}
return { success: true };
}
/**
* Speichert Häuser pro Tile (je Ecke eine Zeile)
* @param {number} mapId
* @param {{[cellKey:string]: {[corner:string]: number}}} tileHouses
*/
async upsertTileHouses(mapId, tileHouses) {
// Löschen und neu anlegen pro Zelle ist am einfachsten und robust
const entries = Object.entries(tileHouses || {});
for (const [cellKey, cornerMap] of entries) {
const [x, y] = cellKey.split(',').map(Number);
await TaxiMapTileHouse.destroy({ where: { map_id: mapId, x, y } });
for (const [corner, rotation] of Object.entries(cornerMap || {})) {
await TaxiMapTileHouse.create({ mapId, x, y, corner, rotation: Number(rotation) || 0 });
}
}
return { success: true };
}
/**
* Löscht eine Map (soft delete)
*/
async deleteMap(mapId) {
try {
const [updatedRowsCount] = await TaxiMap.update(
{ isActive: false },
{ where: { id: mapId } }
);
if (updatedRowsCount === 0) {
throw new Error('Map not found');
}
return { success: true };
} catch (error) {
console.error('Error deleting map:', error);
throw error;
}
}
/**
* Setzt eine Map als Standard
*/
async setDefaultMap(mapId) {
try {
// Entferne Standard-Status von allen anderen Maps
await TaxiMap.update(
{ isDefault: false },
{ where: { isDefault: true } }
);
// Setze neue Standard-Map
const [updatedRowsCount] = await TaxiMap.update(
{ isDefault: true },
{ where: { id: mapId } }
);
if (updatedRowsCount === 0) {
throw new Error('Map not found');
}
return { success: true };
} catch (error) {
console.error('Error setting default map:', error);
throw error;
}
}
/**
* Initialisiert Standard-Map-Typen
*/
async initializeMapTypes() {
try {
const mapTypes = [
{ name: 'Corner Bottom Left', tileType: 'cornerBottomLeft', description: 'Bottom left corner tile' },
{ name: 'Corner Bottom Right', tileType: 'cornerBottomRight', description: 'Bottom right corner tile' },
{ name: 'Corner Top Left', tileType: 'cornerTopLeft', description: 'Top left corner tile' },
{ name: 'Corner Top Right', tileType: 'cornerTopRight', description: 'Top right corner tile' },
{ name: 'Horizontal', tileType: 'horizontal', description: 'Horizontal road tile' },
{ name: 'Vertical', tileType: 'vertical', description: 'Vertical road tile' },
{ name: 'Cross', tileType: 'cross', description: 'Cross intersection tile' },
{ name: 'Fuel Horizontal', tileType: 'fuelHorizontal', description: 'Horizontal road with fuel station' },
{ name: 'Fuel Vertical', tileType: 'fuelVertical', description: 'Vertical road with fuel station' },
{ name: 'T-Left', tileType: 'tLeft', description: 'T-junction facing left' },
{ name: 'T-Right', tileType: 'tRight', description: 'T-junction facing right' },
{ name: 'T-Up', tileType: 'tUp', description: 'T-junction facing up' },
{ name: 'T-Down', tileType: 'tDown', description: 'T-junction facing down' }
];
for (const mapType of mapTypes) {
await TaxiMapType.findOrCreate({
where: { name: mapType.name },
defaults: mapType
});
}
console.log('Taxi map types initialized');
} catch (error) {
console.error('Error initializing map types:', error);
throw error;
}
}
/**
* Erstellt eine Standard-Map
*/
async createDefaultMap() {
}
}
export default TaxiMapService;