Änderungen: - Integration des neuen Modells TaxiMapTileHouse zur Verwaltung von Häusern auf der Karte. - Anpassung der TaxiMap- und TaxiMapService-Logik zur Unterstützung der Hausplatzierung und -verwaltung. - Erweiterung der Benutzeroberfläche in TaxiToolsView.vue zur Erfassung und Anzeige von Hausinformationen. - Implementierung von Methoden zur Speicherung und Aktualisierung von Hausdaten in der Datenbank. Diese Anpassungen verbessern die Funktionalität und Benutzererfahrung im Taxi-Minispiel, indem sie eine detaillierte Verwaltung von Häusern auf der Karte ermöglichen.
327 lines
9.7 KiB
JavaScript
327 lines
9.7 KiB
JavaScript
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 }
|
||
});
|
||
await row.update({ tileType, meta: meta || null });
|
||
}
|
||
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;
|