Änderung: Hinzufügung der Haus-Logik zur Taxi-Map

Ä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.
This commit is contained in:
Torsten Schulz (local)
2025-09-18 14:27:14 +02:00
parent ab8e12cbcd
commit 7207274ab5
8 changed files with 290 additions and 13 deletions

View File

@@ -235,6 +235,7 @@ export default {
size: 500, // 400x400px pro Tile
images: {}
},
houseImage: null,
maps: [], // Geladene Maps aus der Datenbank
currentMap: null, // Aktuell verwendete Map
selectedMapId: null, // ID der ausgewählten Map
@@ -246,6 +247,7 @@ export default {
audioContext: null,
audioUnlockHandler: null,
selectedStreetName: null
,houseNumbers: {}
}
},
computed: {
@@ -285,6 +287,7 @@ export default {
this.initializeMinimap();
this.loadTiles();
this.loadTaxiImage();
this.loadHouseImage();
this.loadMaps();
this.setupEventListeners();
await this.initializeMotorSound();
@@ -1116,9 +1119,11 @@ export default {
if (this.tiles.images[tileType]) {
this.ctx.drawImage(this.tiles.images[tileType], 0, 0, tileSize, tileSize);
}
// Straßennamen auf Haupt-Canvas zeichnen
// Häuser zeichnen (aus tileHouses)
const absCol = this.currentTile.col + (this.currentMap.offsetX || 0);
const absRow = this.currentTile.row + (this.currentMap.offsetY || 0);
this.drawHousesOnMainCanvas(this.ctx, tileSize, absCol, absRow);
// Straßennamen auf Haupt-Canvas zeichnen
this.drawStreetNamesOnMainCanvas(this.ctx, tileSize, tileType, absCol, absRow);
}
}
@@ -1176,6 +1181,55 @@ export default {
drawText(hName, size - fontPx, cy, -Math.PI / 2, sel);
}
},
drawHousesOnMainCanvas(ctx, size, col, row) {
if (!this.currentMap) return;
const houses = Array.isArray(this.currentMap.tileHouses) ? this.currentMap.tileHouses : [];
if (!houses.length || !this.houseImage) return;
// Finde alle Häuser für diese Zelle
const list = houses.filter(h => h.x === col && h.y === row);
if (!list.length) return;
// Hausgröße 150x150px; an Ecken platzieren mit Abstand 3px
const HOUSE_W = 150;
const HOUSE_H = 150;
const pad = 30;
for (const h of list) {
const rot = (Number(h.rotation) || 0) % 360;
const isPortrait = (rot % 180) !== 0; // 90 oder 270 Grad
const w = isPortrait ? HOUSE_H : HOUSE_W;
const hgt = isPortrait ? HOUSE_W : HOUSE_H;
let px = 0, py = 0;
switch (h.corner) {
case 'lo': px = pad; py = pad; break;
case 'ro': px = size - w - pad; py = pad; break;
case 'lu': px = pad; py = size - hgt - pad; break;
case 'ru': px = size - w - pad; py = size - hgt - pad; break;
default: continue;
}
// Rotation: Bild zeigt Tür unten (0°). 90/180/270 drehen um Zentrum.
const cx = px + w / 2;
const cy = py + hgt / 2;
ctx.save();
ctx.translate(cx, cy);
const rad = rot * Math.PI / 180;
ctx.rotate(rad);
ctx.drawImage(this.houseImage, -HOUSE_W/2, -HOUSE_H/2, HOUSE_W, HOUSE_H);
// Hausnummer zeichnen (über dem Bild zentriert)
const key = `${h.x},${h.y},${h.corner}`;
const num = this.houseNumbers[key];
if (num != null) {
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.font = 'bold 20px sans-serif';
ctx.lineWidth = 4;
ctx.strokeStyle = 'rgba(255,255,255,0.9)';
ctx.strokeText(String(num), 0, 0);
ctx.fillStyle = '#000';
ctx.fillText(String(num), 0, 0);
}
ctx.restore();
}
},
getTileType(row, col, rows, cols) {
// Ecken
@@ -1336,6 +1390,16 @@ export default {
img.src = '/images/taxi/taxi.svg';
// console.log('Lade Taxi-Bild von:', '/images/taxi/taxi.svg');
},
loadHouseImage() {
const img = new Image();
img.onload = () => {
this.houseImage = img;
};
img.onerror = () => {
console.warn('Fehler beim Laden von house_small.png');
};
img.src = '/images/taxi/house_small.png';
},
async loadMaps() {
try {
@@ -1352,6 +1416,7 @@ export default {
// Minimap neu zeichnen nach dem Laden der Map
this.$nextTick(() => {
this.computeHouseNumbers();
this.drawMinimap();
});
}
@@ -1382,6 +1447,7 @@ export default {
// Minimap neu zeichnen
this.$nextTick(() => {
this.computeHouseNumbers();
this.drawMinimap();
});
}
@@ -1542,6 +1608,8 @@ export default {
map.mapData = grid;
map.offsetX = minX;
map.offsetY = minY;
// tileHouses vom Backend übernehmen (Array von Zeilen)
map.tileHouses = Array.isArray(map.tileHouses) ? map.tileHouses : [];
return map;
},
onSelectStreet(name) {
@@ -1595,6 +1663,92 @@ export default {
}
ctx.restore();
},
// ---- Hausnummern-Berechnung ----
computeHouseNumbers() {
this.houseNumbers = {};
if (!this.currentMap || !this.currentMap.tileHouses || !this.currentMap.mapData) return;
const map = this.currentMap;
const offsetX = map.offsetX || 0;
const offsetY = map.offsetY || 0;
const streetIndex = new Map();
const getEntry = (name) => { if (!streetIndex.has(name)) streetIndex.set(name, { tiles: new Map() }); return streetIndex.get(name); };
const tileStreets = Array.isArray(map.tileStreets) ? map.tileStreets : [];
for (const ts of tileStreets) {
const x = ts.x; const y = ts.y; const gridX = x - offsetX; const gridY = y - offsetY;
const tileType = (map.mapData[gridY] && map.mapData[gridY][gridX]) || null; if (!tileType) continue;
const makeTileObj = (name) => { const key = `${x},${y}`; const e = getEntry(name); const t = e.tiles.get(key) || { x, y, tileType, h: false, v: false }; e.tiles.set(key, t); return t; };
if (ts.streetNameH && ts.streetNameH.name) { makeTileObj(ts.streetNameH.name).h = true; }
if (ts.streetNameV && ts.streetNameV.name) { makeTileObj(ts.streetNameV.name).v = true; }
}
const allowedDirections = (tileType) => {
switch (tileType) {
case 'cornertopleft': return { left: true, up: true, right: false, down: false };
case 'cornertopright': return { right: true, up: true, left: false, down: false };
case 'cornerbottomleft': return { left: true, down: true, right: false, up: false };
case 'cornerbottomright': return { right: true, down: true, left: false, up: false };
case 'horizontal':
case 'fuelhorizontal': return { left: true, right: true, up: false, down: false };
case 'vertical':
case 'fuelvertical': return { up: true, down: true, left: false, right: false };
case 'cross': return { left: true, right: true, up: true, down: true };
case 'tup': return { up: true, left: true, right: true, down: false };
case 'tdown': return { down: true, left: true, right: true, up: false };
case 'tleft': return { left: true, up: true, down: true, right: false };
case 'tright': return { right: true, up: true, down: true, left: false };
default: return { left: false, right: false, up: false, down: false };
}
};
const getTile = (entry, x, y) => entry.tiles.get(`${x},${y}`);
for (const [name, entry] of streetIndex.entries()) {
const nodes = new Map();
const addNode = (x, y, axis) => nodes.set(`${x},${y},${axis}`, { x, y, axis });
for (const t of entry.tiles.values()) { if (t.h) addNode(t.x, t.y, 'h'); if (t.v) addNode(t.x, t.y, 'v'); }
const adj = new Map();
const addEdge = (a, b) => { if (!nodes.has(a) || !nodes.has(b)) return; if (!adj.has(a)) adj.set(a, new Set()); if (!adj.has(b)) adj.set(b, new Set()); adj.get(a).add(b); adj.get(b).add(a); };
for (const t of entry.tiles.values()) {
const dirs = allowedDirections(t.tileType); const keyH = `${t.x},${t.y},h`; const keyV = `${t.x},${t.y},v`;
if (t.h) { if (dirs.left) { const n = getTile(entry, t.x - 1, t.y); if (n && n.h && allowedDirections(n.tileType).right) addEdge(keyH, `${n.x},${n.y},h`); } if (dirs.right) { const n = getTile(entry, t.x + 1, t.y); if (n && n.h && allowedDirections(n.tileType).left) addEdge(keyH, `${n.x},${n.y},h`); } }
if (t.v) { if (dirs.up) { const n = getTile(entry, t.x, t.y - 1); if (n && n.v && allowedDirections(n.tileType).down) addEdge(keyV, `${n.x},${n.y},v`); } if (dirs.down) { const n = getTile(entry, t.x, t.y + 1); if (n && n.v && allowedDirections(n.tileType).up) addEdge(keyV, `${n.x},${n.y},v`); } }
if (t.h && t.v) { addEdge(keyH, keyV); }
}
if (nodes.size === 0) continue;
let startKey = null; for (const k of nodes.keys()) { if ((adj.get(k)?.size || 0) === 1) { startKey = k; break; } } if (!startKey) startKey = Array.from(nodes.keys()).sort()[0];
const visited = new Set(); let odd = 1, even = 2; let prev = null; let curr = startKey; let currOddSide = (nodes.get(curr).axis === 'h') ? 'top' : 'left';
while (curr) {
visited.add(curr);
const { x, y, axis } = nodes.get(curr);
const neighbors = Array.from(adj.get(curr) || []); const next = neighbors.find(n => !visited.has(n));
let dir = { dx: 0, dy: 0 }; if (next) { const nNode = nodes.get(next); dir = { dx: nNode.x - x, dy: nNode.y - y }; } else if (prev) { const pNode = nodes.get(prev); dir = { dx: x - pNode.x, dy: y - pNode.y }; }
if (prev) { const prevAxis = nodes.get(prev).axis; if (prevAxis !== axis) { if (prevAxis === 'v' && axis === 'h') currOddSide = 'bottom'; else if (prevAxis === 'h' && axis === 'v') currOddSide = 'left'; } }
const houseMap = this.currentMap.tileHouses || []; const housesHere = houseMap.filter(hh => hh.x === x && hh.y === y);
if (housesHere.length) {
const doorSideFromRotation = (rot) => {
const r = ((Number(rot) || 0) % 360 + 360) % 360;
if (r === 0) return 'bottom';
if (r === 90) return 'left';
if (r === 180) return 'top';
if (r === 270) return 'right';
return 'bottom';
};
// Nur Häuser berücksichtigen, deren Tür zur aktuellen Straßenachse passt
const housesFiltered = housesHere.filter(hh => {
const side = doorSideFromRotation(hh.rotation);
return axis === 'h' ? (side === 'top' || side === 'bottom') : (side === 'left' || side === 'right');
});
if (!housesFiltered.length) { prev = curr; curr = next || null; continue; }
const orderCorners = (list) => {
if (axis === 'h') { const movingRight = dir.dx > 0 || (dir.dx === 0 && !prev); const seq = movingRight ? ['lo','ro','lu','ru'] : ['ro','lo','ru','lu']; return list.slice().sort((a,b)=> seq.indexOf(a) - seq.indexOf(b)); }
else { const movingDown = dir.dy > 0 || (dir.dy === 0 && !prev); const seq = movingDown ? ['lo','lu','ro','ru'] : ['lu','lo','ru','ro']; return list.slice().sort((a,b)=> seq.indexOf(a) - seq.indexOf(b)); }
};
const oddCorners = (axis === 'h') ? (currOddSide === 'top' ? ['lo','ro'] : ['lu','ru']) : (currOddSide === 'left' ? ['lo','lu'] : ['ro','ru']);
const evenCorners = (axis === 'h') ? (currOddSide === 'top' ? ['lu','ru'] : ['lo','ro']) : (currOddSide === 'left' ? ['ro','ru'] : ['lo','lu']);
for (const c of orderCorners(oddCorners)) { if (housesFiltered.find(hh => hh.corner === c)) { const k = `${x},${y},${c}`; if (this.houseNumbers[k] == null) { this.houseNumbers[k] = odd; odd += 2; } } }
for (const c of orderCorners(evenCorners)) { if (housesFiltered.find(hh => hh.corner === c)) { const k = `${x},${y},${c}`; if (this.houseNumbers[k] == null) { this.houseNumbers[k] = even; even += 2; } } }
}
prev = curr; curr = next || null;
}
}
}
}
}