Ä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:
@@ -151,8 +151,18 @@
|
||||
<h4>{{ $t('admin.taxiTools.mapEditor.extraElements') }}</h4>
|
||||
<div class="panel-body">
|
||||
<div v-if="allowedHouseCorners.length" class="corner-chooser">
|
||||
<button v-for="pos in allowedHouseCorners" :key="pos" class="corner-btn" @click="confirmHouseCorner(pos)">
|
||||
{{ pos.toUpperCase() }}
|
||||
<button
|
||||
v-for="pos in allowedHouseCorners"
|
||||
:key="pos"
|
||||
class="corner-btn"
|
||||
:title="pos.toUpperCase()"
|
||||
@click="confirmHouseCorner(pos)">
|
||||
<div class="corner-preview">
|
||||
<span class="q" :class="{ active: pos === 'lo' }"></span>
|
||||
<span class="q" :class="{ active: pos === 'ro' }"></span>
|
||||
<span class="q" :class="{ active: pos === 'lu' }"></span>
|
||||
<span class="q" :class="{ active: pos === 'ru' }"></span>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
<h5 v-if="pendingCorner" class="extra-subtitle">Haus</h5>
|
||||
@@ -375,7 +385,9 @@ const HOUSE_TYPE_MATRIX = {
|
||||
fuelvertical: { lo: ['right'], ro: [], lu: ['right'], ru: [] }
|
||||
};
|
||||
|
||||
const DIRECTION_TO_ROTATION = { bottom: 0, right: 90, top: 180, left: 270 };
|
||||
// Canvas-Rotation ist im Browser positiv im Uhrzeigersinn (y-Achse nach unten).
|
||||
// Bild zeigt Tür unten (0°). Für "rechts" benötigen wir -90° (= 270°), für "links" +90°.
|
||||
const DIRECTION_TO_ROTATION = { bottom: 0, right: 270, top: 180, left: 90 };
|
||||
|
||||
export default {
|
||||
name: 'AdminTaxiToolsView',
|
||||
@@ -1033,9 +1045,9 @@ export default {
|
||||
doorClass(deg) {
|
||||
switch (deg) {
|
||||
case 0: return 'door-bottom';
|
||||
case 90: return 'door-right';
|
||||
case 90: return 'door-left';
|
||||
case 180: return 'door-top';
|
||||
case 270: return 'door-left';
|
||||
case 270: return 'door-right';
|
||||
default: return 'door-bottom';
|
||||
}
|
||||
},
|
||||
@@ -1146,6 +1158,7 @@ export default {
|
||||
meta: c.extraHouses ? { houses: { ...c.extraHouses } } : null
|
||||
}));
|
||||
const tileStreetNames = this.collectTileStreetNames();
|
||||
const tileHouses = this.collectTileHouses();
|
||||
const mapData = {
|
||||
...this.mapForm,
|
||||
width: this.boardWidth,
|
||||
@@ -1153,7 +1166,8 @@ export default {
|
||||
tileSize: 50,
|
||||
mapTypeId: 1,
|
||||
tiles,
|
||||
tileStreetNames
|
||||
tileStreetNames,
|
||||
tileHouses
|
||||
};
|
||||
|
||||
let savedMap;
|
||||
@@ -1196,6 +1210,17 @@ export default {
|
||||
return result;
|
||||
},
|
||||
|
||||
collectTileHouses() {
|
||||
const result = {};
|
||||
for (const [key, cell] of Object.entries(this.boardCells)) {
|
||||
if (!cell.tileType) continue;
|
||||
if (cell.extraHouses && Object.keys(cell.extraHouses).length > 0) {
|
||||
result[key] = { ...cell.extraHouses };
|
||||
}
|
||||
}
|
||||
return result;
|
||||
},
|
||||
|
||||
async deleteMap(mapId) {
|
||||
if (confirm('Möchtest du diese Map wirklich löschen?')) {
|
||||
try {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user