Änderung: Erweiterung der Hausplatzierungslogik im Taxi-Spiel
Änderungen: - Hinzufügung einer neuen Logik zur Auswahl und Platzierung von Häusern auf der Karte, einschließlich der Auswahl von Ecken und Tür-Richtungen. - Implementierung einer Matrix zur Verwaltung erlaubter Haus-Tür-Richtungen basierend auf dem Tile-Typ. - Verbesserung der Benutzeroberfläche in TaxiToolsView.vue zur Anzeige von verfügbaren Ecken und Tür-Richtungen. - Anpassung der Datenstruktur zur Speicherung zusätzlicher Hausinformationen in den Tiles. Diese Anpassungen verbessern die Funktionalität und Benutzererfahrung im Taxi-Minispiel erheblich, indem sie eine detailliertere Verwaltung von Häusern auf der Karte ermöglichen.
This commit is contained in:
BIN
frontend/public/images/taxi/house_small.png
Normal file
BIN
frontend/public/images/taxi/house_small.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 91 KiB |
@@ -88,6 +88,16 @@
|
||||
:alt="getCellAtPosition(x, y).tileType"
|
||||
class="tile-image"
|
||||
/>
|
||||
<div v-if="getCellAtPosition(x, y)?.extraHouses" class="house-overlay">
|
||||
<div
|
||||
v-for="(rotation, corner) in getCellAtPosition(x, y).extraHouses"
|
||||
:key="corner"
|
||||
class="house-square"
|
||||
:class="cornerClass(corner)"
|
||||
>
|
||||
<div class="house-door" :class="doorClass(rotation)"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -139,7 +149,26 @@
|
||||
<!-- Zusätzliche Elemente (viertes Panel) -->
|
||||
<div v-if="showExtraElementsPanel" class="tool-panel extra-elements">
|
||||
<h4>{{ $t('admin.taxiTools.mapEditor.extraElements') }}</h4>
|
||||
<div class="panel-body placeholder">—</div>
|
||||
<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>
|
||||
</div>
|
||||
<h5 v-if="pendingCorner" class="extra-subtitle">Haus</h5>
|
||||
<div v-if="pendingCorner" class="extra-grid">
|
||||
<div
|
||||
v-for="dir in availableHouseDirections"
|
||||
:key="dir"
|
||||
class="extra-option"
|
||||
@click="startPlaceHouse(directionToRotation(dir))"
|
||||
:title="`Haus: Tür ${dir}`">
|
||||
<div class="house-square">
|
||||
<div class="house-door" :class="doorCssClass(dir)"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -210,6 +239,16 @@
|
||||
:alt="getCellAtPosition(x, y).tileType"
|
||||
class="tile-image"
|
||||
/>
|
||||
<div v-if="getCellAtPosition(x, y)?.extraHouses" class="house-overlay">
|
||||
<div
|
||||
v-for="(rotation, corner) in getCellAtPosition(x, y).extraHouses"
|
||||
:key="corner"
|
||||
class="house-square"
|
||||
:class="cornerClass(corner)"
|
||||
>
|
||||
<div class="house-door" :class="doorClass(rotation)"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -261,7 +300,36 @@
|
||||
<!-- Zusätzliche Elemente (viertes Panel) -->
|
||||
<div v-if="showExtraElementsPanel" class="tool-panel extra-elements">
|
||||
<h4>{{ $t('admin.taxiTools.mapEditor.extraElements') }}</h4>
|
||||
<div class="panel-body placeholder">—</div>
|
||||
<div class="panel-body">
|
||||
<div v-if="allowedHouseCorners.length" class="corner-chooser">
|
||||
<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>
|
||||
<div v-if="pendingCorner" class="extra-grid">
|
||||
<div
|
||||
v-for="dir in availableHouseDirections"
|
||||
:key="dir"
|
||||
class="extra-option"
|
||||
@click="startPlaceHouse(directionToRotation(dir))"
|
||||
:title="`Haus: Tür ${dir}`">
|
||||
<div class="house-square">
|
||||
<div class="house-door" :class="doorCssClass(dir)"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -289,6 +357,26 @@ import SimpleTabs from '../../components/SimpleTabs.vue';
|
||||
import MessageDialog from '../../dialogues/standard/MessageDialog.vue';
|
||||
import apiClient from '../../utils/axios.js';
|
||||
|
||||
// Matrix: erlaubte Haus-Tür-Richtungen je Tile-Typ und Ecke
|
||||
// Richtungen: bottom, right, top, left
|
||||
const HOUSE_TYPE_MATRIX = {
|
||||
horizontal: { lo: ['bottom'], ro: ['bottom'], lu: ['top'], ru: ['top'] },
|
||||
vertical: { lo: ['right'], ro: ['left'], lu: ['right'], ru: ['left'] },
|
||||
cross: { lo: ['bottom', 'right'], ro: ['bottom', 'left'], lu: ['top', 'right'], ru: ['top', 'left'] },
|
||||
cornertopleft: { lo: ['bottom', 'right'], ro: ['left'], lu: ['top'], ru: [] },
|
||||
cornertopright: { lo: ['right'], ro: ['bottom', 'right'], lu: [], ru: ['top'] },
|
||||
cornerbottomleft: { lo: ['bottom'], ro: [], lu: ['top', 'right'], ru: ['left'] },
|
||||
cornerbottomright: { lo: [], ro: ['bottom'], lu: ['right'], ru: ['top', 'left'] },
|
||||
tup: { lo: ['bottom', 'right'], ro: ['bottom', 'left'], lu: ['top'], ru: ['top'] },
|
||||
tdown: { lo: ['bottom'], ro: ['bottom'], lu: ['top', 'right'], ru: ['top', 'left'] },
|
||||
tleft: { lo: ['bottom', 'right'], ro: ['left'], lu: ['top', 'right'], ru: ['left'] },
|
||||
tright: { lo: ['right'], ro: ['bottom', 'left'], lu: ['right'], ru: ['top', 'left'] },
|
||||
fuelhorizontal: { lo: [], ro: [], lu: ['top'], ru: ['top'] },
|
||||
fuelvertical: { lo: ['right'], ro: [], lu: ['right'], ru: [] }
|
||||
};
|
||||
|
||||
const DIRECTION_TO_ROTATION = { bottom: 0, right: 90, top: 180, left: 270 };
|
||||
|
||||
export default {
|
||||
name: 'AdminTaxiToolsView',
|
||||
components: {
|
||||
@@ -325,6 +413,9 @@ export default {
|
||||
],
|
||||
selectedCellKey: null,
|
||||
selectedTileType: null
|
||||
,pendingHouse: null
|
||||
,allowedHouseCorners: []
|
||||
,pendingCorner: null
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
@@ -353,8 +444,8 @@ export default {
|
||||
return this.supportsHorizontal(type) || this.supportsVertical(type);
|
||||
},
|
||||
showExtraElementsPanel() {
|
||||
// Gleiche Logik wie Straßenpanel – nur sichtbar, wenn eine Zelle selektiert ist
|
||||
return !!this.selectedCell;
|
||||
// Nur sichtbar, wenn eine Zelle selektiert ist UND ein Tile-Typ gesetzt ist
|
||||
return !!(this.selectedCell && this.selectedCell.tileType);
|
||||
},
|
||||
supportsHorizontalName() {
|
||||
if (!this.selectedCell) return false;
|
||||
@@ -413,6 +504,15 @@ export default {
|
||||
gridTemplateRows: `repeat(${this.boardHeight}, 50px)`,
|
||||
gap: '2px'
|
||||
};
|
||||
},
|
||||
|
||||
// Verfügbare Haus-Tür-Richtungen anhand ausgewählter Ecke und Tile-Typ
|
||||
availableHouseDirections() {
|
||||
if (!this.pendingCorner || !this.selectedCell || !this.selectedCell.tileType) return [];
|
||||
const entry = HOUSE_TYPE_MATRIX[this.selectedCell.tileType];
|
||||
if (!entry) return [];
|
||||
const dirs = entry[this.pendingCorner] || [];
|
||||
return Array.isArray(dirs) ? dirs : [];
|
||||
}
|
||||
},
|
||||
|
||||
@@ -440,6 +540,18 @@ export default {
|
||||
},
|
||||
|
||||
methods: {
|
||||
directionToRotation(dir) {
|
||||
return DIRECTION_TO_ROTATION[dir] ?? 0;
|
||||
},
|
||||
doorCssClass(dir) {
|
||||
switch (dir) {
|
||||
case 'bottom': return 'door-bottom';
|
||||
case 'right': return 'door-right';
|
||||
case 'top': return 'door-top';
|
||||
case 'left': return 'door-left';
|
||||
default: return 'door-bottom';
|
||||
}
|
||||
},
|
||||
supportsHorizontal(tileType) {
|
||||
// Horizontalen Straßennamen erlauben bei: horizontal, cross, tleft, tright, tup, tdown, corns wo horizontaler Ast existiert
|
||||
const allowed = ['horizontal', 'cross', 'tleft', 'tright', 'tup', 'tdown', 'cornertopleft', 'cornerbottomleft', 'cornertopright', 'cornerbottomright', 'fuelhorizontal'];
|
||||
@@ -661,6 +773,16 @@ export default {
|
||||
for (const t of map.tiles) {
|
||||
const key = `${t.x},${t.y}`;
|
||||
this.boardCells[key] = { tileType: t.tileType, x: t.x, y: t.y };
|
||||
if (t.meta && t.meta.house) {
|
||||
// Legacy Einzel-Haus zu neuer Struktur migrieren
|
||||
const corner = t.meta.house.corner || 'lo';
|
||||
const rotation = t.meta.house.rotation || 0;
|
||||
this.boardCells[key].extraHouses = { [corner]: rotation };
|
||||
}
|
||||
if (t.meta && t.meta.houses) {
|
||||
// Neue Struktur: Mapping corner -> rotation
|
||||
this.boardCells[key].extraHouses = { ...t.meta.houses };
|
||||
}
|
||||
}
|
||||
// Straßennamen aus tileStreets übernehmen
|
||||
if (Array.isArray(map.tileStreets)) {
|
||||
@@ -688,10 +810,14 @@ export default {
|
||||
},
|
||||
|
||||
selectCell(key) {
|
||||
// Wähle Position aus (Platzhalter)
|
||||
// Wähle Position aus
|
||||
this.selectedCellKey = this.selectedCellKey === key ? null : key;
|
||||
// Setze Tile-Auswahl zurück
|
||||
// Reset temporärer Zustände
|
||||
this.selectedTileType = null;
|
||||
this.pendingHouse = null;
|
||||
this.pendingCorner = null;
|
||||
// Erlaubte Ecken vorberechnen
|
||||
this.computeAllowedHouseCorners();
|
||||
},
|
||||
|
||||
selectTileType(tileType) {
|
||||
@@ -725,6 +851,46 @@ export default {
|
||||
|
||||
// Erweitere das Board um benachbarte leere Zellen falls nötig
|
||||
this.expandBoardIfNeeded(x, y, tileType);
|
||||
// Erlaubte Haus-Ecken aktualisieren
|
||||
this.computeAllowedHouseCorners();
|
||||
},
|
||||
|
||||
startPlaceHouse(rotationDeg) {
|
||||
// Neuer Flow: Rotation wählen, nachdem Ecke gewählt wurde
|
||||
if (!this.selectedCellKey || !this.pendingCorner) return;
|
||||
const cell = this.boardCells[this.selectedCellKey];
|
||||
if (!cell || !cell.tileType) return;
|
||||
const existing = cell.extraHouses || {};
|
||||
const next = { ...existing, [this.pendingCorner]: rotationDeg };
|
||||
const updated = { ...cell, extraHouses: next };
|
||||
this.$set ? this.$set(this.boardCells, this.selectedCellKey, updated) : (this.boardCells[this.selectedCellKey] = updated);
|
||||
this.pendingCorner = null;
|
||||
},
|
||||
confirmHouseCorner(corner) {
|
||||
if (!this.selectedCellKey) return;
|
||||
const cell = this.boardCells[this.selectedCellKey];
|
||||
if (!cell || !cell.tileType) return;
|
||||
this.pendingCorner = corner;
|
||||
},
|
||||
computeAllowedHouseCorners() {
|
||||
if (!this.selectedCell || !this.selectedCell.tileType) {
|
||||
this.allowedHouseCorners = [];
|
||||
return;
|
||||
}
|
||||
this.allowedHouseCorners = this.getAllowedHouseCorners(this.selectedCell.tileType);
|
||||
},
|
||||
getAllowedHouseCorners(tileType) {
|
||||
// lo=links oben, ro=rechts oben, lu=links unten, ru=rechts unten
|
||||
switch (tileType) {
|
||||
case 'cornerbottomright': return ['ro','lu','ru'];
|
||||
case 'cornerbottomleft': return ['lo','lu','ru'];
|
||||
case 'cornertopright': return ['lo','ro','ru'];
|
||||
case 'cornertopleft': return ['lo','ro','lu'];
|
||||
case 'fuelhorizontal': return ['ru','lu'];
|
||||
case 'fuelvertical': return ['lo','lu'];
|
||||
default: // horizontal, vertical, cross, tup, tdown, tleft, tright
|
||||
return ['lo','ro','lu','ru'];
|
||||
}
|
||||
},
|
||||
|
||||
expandBoardIfNeeded(x, y, tileType) {
|
||||
@@ -864,6 +1030,25 @@ export default {
|
||||
return { tileType: null, x, y };
|
||||
},
|
||||
|
||||
doorClass(deg) {
|
||||
switch (deg) {
|
||||
case 0: return 'door-bottom';
|
||||
case 90: return 'door-right';
|
||||
case 180: return 'door-top';
|
||||
case 270: return 'door-left';
|
||||
default: return 'door-bottom';
|
||||
}
|
||||
},
|
||||
cornerClass(corner) {
|
||||
switch (corner) {
|
||||
case 'lo': return 'corner-lo';
|
||||
case 'ro': return 'corner-ro';
|
||||
case 'lu': return 'corner-lu';
|
||||
case 'ru': return 'corner-ru';
|
||||
default: return 'corner-lo';
|
||||
}
|
||||
},
|
||||
|
||||
range(start, end) {
|
||||
const result = [];
|
||||
for (let i = start; i <= end; i++) {
|
||||
@@ -954,7 +1139,12 @@ export default {
|
||||
// Tiles in Array-Form bringen
|
||||
const tiles = Object.values(this.boardCells)
|
||||
.filter(c => !!c.tileType)
|
||||
.map(c => ({ x: c.x, y: c.y, tileType: c.tileType }));
|
||||
.map(c => ({
|
||||
x: c.x,
|
||||
y: c.y,
|
||||
tileType: c.tileType,
|
||||
meta: c.extraHouses ? { houses: { ...c.extraHouses } } : null
|
||||
}));
|
||||
const tileStreetNames = this.collectTileStreetNames();
|
||||
const mapData = {
|
||||
...this.mapForm,
|
||||
@@ -1238,6 +1428,39 @@ export default {
|
||||
color: #F9A22C;
|
||||
font-size: 18px;
|
||||
}
|
||||
.extra-grid { display: flex; gap: 8px; align-items: center; }
|
||||
.extra-option {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 6px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: #fff;
|
||||
cursor: pointer;
|
||||
transition: transform 0.15s ease;
|
||||
}
|
||||
.extra-option:hover { transform: scale(1.05); }
|
||||
.house-square { width: 10px; height: 10px; background: #d0d0d0; border: 1px solid #aaa; position: relative; }
|
||||
.house-door { position: absolute; background: #000; }
|
||||
.door-top { top: -1px; left: calc(50% - 2.5px); width: 5px; height: 2px; }
|
||||
.door-bottom { bottom: -1px; left: calc(50% - 2.5px); width: 5px; height: 2px; }
|
||||
.door-left { left: -1px; top: calc(50% - 2.5px); width: 2px; height: 5px; }
|
||||
.door-right { right: -1px; top: calc(50% - 2.5px); width: 2px; height: 5px; }
|
||||
.house-overlay { position: absolute; inset: 0; pointer-events: none; }
|
||||
.house-square.corner-lo { position: absolute; top: 1px; left: 1px; }
|
||||
.house-square.corner-ro { position: absolute; top: 1px; right: 1px; }
|
||||
.house-square.corner-lu { position: absolute; bottom: 1px; left: 1px; }
|
||||
.house-square.corner-ru { position: absolute; bottom: 1px; right: 1px; }
|
||||
.corner-chooser { margin-top: 8px; display: flex; gap: 6px; }
|
||||
.corner-btn { padding: 3px; border: 1px solid #ccc; border-radius: 4px; background: #f7f7f7; cursor: pointer; }
|
||||
.corner-btn:hover { background: #eee; }
|
||||
|
||||
.corner-preview { width: 18px; height: 18px; display: grid; grid-template-columns: 1fr 1fr; grid-template-rows: 1fr 1fr; }
|
||||
.corner-preview .q { width: 100%; height: 100%; background: #000; display: block; }
|
||||
.corner-preview .q.active { background: #d60000; }
|
||||
|
||||
.panel-body.placeholder {
|
||||
border: 2px dashed #ddd;
|
||||
border-radius: 8px;
|
||||
|
||||
Reference in New Issue
Block a user