Enhance usability and localization across components: Update USABILITY_CONCEPT.md with new focus areas, improve user feedback in AppFooter and FamilyView components, and refine text in various UI elements for better clarity and consistency. Replace console logs with user-friendly messages, correct German translations, and streamline interaction logic in multiple components.
This commit is contained in:
@@ -129,7 +129,9 @@
|
||||
@mouseenter="onTileMouseEnter($event, index)"
|
||||
@mouseleave="onTileMouseLeave($event, index)"
|
||||
@touchstart="onTileMouseDown($event, index)"
|
||||
@touchmove.prevent="onTileMouseMove($event)"
|
||||
@touchend="onTileMouseUp($event, index)"
|
||||
@touchcancel="endDrag($event)"
|
||||
@dblclick="handleDoubleClick(index, $event)">
|
||||
<span v-if="tile" class="tile-symbol">{{ getTileSymbol(tile.type) }}</span>
|
||||
</div>
|
||||
@@ -148,7 +150,9 @@
|
||||
</div>
|
||||
|
||||
<div v-if="showRocketFlight" class="power-up-animation" :style="{ left: rocketStartPos.x + 'px', top: rocketStartPos.y + 'px' }">
|
||||
<div class="rocket-flight" :style="{ '--dx': (rocketEndPos.x - rocketStartPos.x) + 'px', '--dy': (rocketEndPos.y - rocketStartPos.y) + 'px' }"></div>
|
||||
<div class="rocket-flight-path" :style="{ '--dx': (rocketEndPos.x - rocketStartPos.x) + 'px', '--dy': (rocketEndPos.y - rocketStartPos.y) + 'px' }">
|
||||
<div class="rocket-flight-icon">🚀</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="showRainbowEffect" class="power-up-animation" :style="{ left: rainbowCenter.x + 'px', top: rainbowCenter.y + 'px' }">
|
||||
@@ -164,8 +168,8 @@
|
||||
</div>
|
||||
|
||||
<!-- Raketen-Flug-Animation -->
|
||||
<div v-if="rocketTarget.x > 0 && rocketTarget.y > 0" class="rocket-flight-animation" :style="{ left: rocketTarget.x + 'px', top: rocketTarget.y + 'px' }">
|
||||
<div class="rocket-flight">🚀</div>
|
||||
<div v-if="rocketTarget.x > 0 && rocketTarget.y > 0" class="rocket-target-marker" :style="{ left: rocketTarget.x + 'px', top: rocketTarget.y + 'px' }">
|
||||
<div class="rocket-target-marker__icon">🎯</div>
|
||||
</div>
|
||||
|
||||
<!-- Fliegende Rakete -->
|
||||
@@ -383,14 +387,110 @@ export default {
|
||||
// Füge globale Event-Listener hinzu
|
||||
document.addEventListener('mousemove', this.onGlobalMouseMove);
|
||||
document.addEventListener('mouseup', this.onGlobalMouseUp);
|
||||
document.addEventListener('touchmove', this.onGlobalMouseMove, { passive: false });
|
||||
document.addEventListener('touchend', this.onGlobalMouseUp);
|
||||
document.addEventListener('touchcancel', this.onGlobalMouseUp);
|
||||
},
|
||||
|
||||
beforeUnmount() {
|
||||
// Entferne globale Event-Listener
|
||||
document.removeEventListener('mousemove', this.onGlobalMouseMove);
|
||||
document.removeEventListener('mouseup', this.onGlobalMouseUp);
|
||||
document.removeEventListener('touchmove', this.onGlobalMouseMove);
|
||||
document.removeEventListener('touchend', this.onGlobalMouseUp);
|
||||
document.removeEventListener('touchcancel', this.onGlobalMouseUp);
|
||||
},
|
||||
methods: {
|
||||
createTile(type, extra = {}) {
|
||||
return {
|
||||
type,
|
||||
id: extra.id ?? `${Date.now()}-${Math.random().toString(36).slice(2, 10)}`,
|
||||
...extra
|
||||
};
|
||||
},
|
||||
|
||||
getTileType(tileOrType) {
|
||||
if (!tileOrType) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return typeof tileOrType === 'string' ? tileOrType : tileOrType.type;
|
||||
},
|
||||
|
||||
getPowerUpKind(tileOrType) {
|
||||
const tileType = this.getTileType(tileOrType);
|
||||
|
||||
if (this.isRocketTile(tileType)) {
|
||||
return 'rocket';
|
||||
}
|
||||
|
||||
if (tileType === 'bomb' || tileType === 'rainbow') {
|
||||
return tileType;
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
|
||||
getPointerCoordinates(event) {
|
||||
const pointer = event?.touches?.[0] || event?.changedTouches?.[0];
|
||||
const x = pointer?.clientX ?? event?.clientX ?? null;
|
||||
const y = pointer?.clientY ?? event?.clientY ?? null;
|
||||
|
||||
if (x === null || y === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return { x, y };
|
||||
},
|
||||
|
||||
getSwipeDirection(clientX, clientY, threshold = 18) {
|
||||
if (this.dragStartX === null || this.dragStartY === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const deltaX = clientX - this.dragStartX;
|
||||
const deltaY = clientY - this.dragStartY;
|
||||
|
||||
if (Math.abs(deltaX) < threshold && Math.abs(deltaY) < threshold) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return Math.abs(deltaX) >= Math.abs(deltaY)
|
||||
? (deltaX > 0 ? 'right' : 'left')
|
||||
: (deltaY > 0 ? 'down' : 'up');
|
||||
},
|
||||
|
||||
resolveDragTargetIndex(event, fallbackTileIndex = null) {
|
||||
if (this.currentlyAnimatingTile !== null && this.currentlyAnimatingTile !== this.draggedTileIndex) {
|
||||
return this.currentlyAnimatingTile;
|
||||
}
|
||||
|
||||
if (
|
||||
fallbackTileIndex !== null &&
|
||||
fallbackTileIndex !== this.draggedTileIndex &&
|
||||
this.areTilesAdjacent(this.draggedTileIndex, fallbackTileIndex)
|
||||
) {
|
||||
return fallbackTileIndex;
|
||||
}
|
||||
|
||||
const pointer = this.getPointerCoordinates(event);
|
||||
if (!pointer) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const pointerTargetIndex = this.findTileAtPosition(pointer.x, pointer.y);
|
||||
if (
|
||||
pointerTargetIndex !== null &&
|
||||
pointerTargetIndex !== this.draggedTileIndex &&
|
||||
this.areTilesAdjacent(this.draggedTileIndex, pointerTargetIndex)
|
||||
) {
|
||||
return pointerTargetIndex;
|
||||
}
|
||||
|
||||
const swipeDirection = this.getSwipeDirection(pointer.x, pointer.y);
|
||||
return swipeDirection ? this.getAdjacentIndex(this.draggedTileIndex, swipeDirection) : null;
|
||||
},
|
||||
|
||||
// Initialisiere Sound-Effekte
|
||||
initializeSounds() {
|
||||
try {
|
||||
@@ -1467,18 +1567,20 @@ export default {
|
||||
|
||||
// Hilfsmethode: Prüfe ob ein Tile ein Raketen-Power-up ist
|
||||
isRocketTile(tileType) {
|
||||
return tileType === 'rocket' || tileType === 'rocket-horizontal' || tileType === 'rocket-vertical';
|
||||
const normalizedType = this.getTileType(tileType);
|
||||
return normalizedType === 'rocket' || normalizedType === 'rocket-horizontal' || normalizedType === 'rocket-vertical';
|
||||
},
|
||||
|
||||
// Hilfsmethode: Prüfe ob ein Tile ein Regenbogen-Power-up ist
|
||||
isRainbowTile(tileType) {
|
||||
return tileType === 'rainbow';
|
||||
return this.getTileType(tileType) === 'rainbow';
|
||||
},
|
||||
|
||||
// Hilfsmethode: Prüfe ob ein Tile ein Power-up ist
|
||||
isPowerUpTile(tileType) {
|
||||
if (!tileType) return false;
|
||||
return this.isRocketTile(tileType) || tileType === 'bomb' || tileType === 'rocket-horizontal' || tileType === 'rocket-vertical' || this.isRainbowTile(tileType);
|
||||
const normalizedType = this.getTileType(tileType);
|
||||
if (!normalizedType) return false;
|
||||
return this.isRocketTile(normalizedType) || normalizedType === 'bomb' || this.isRainbowTile(normalizedType);
|
||||
},
|
||||
|
||||
// Hilfsmethode: Debug-Ausgabe für Power-ups
|
||||
@@ -1932,21 +2034,11 @@ export default {
|
||||
|
||||
// JETZT erst Power-ups erstellen, nachdem die Tiles entfernt wurden
|
||||
debugLog('🔧 Erstelle Power-ups nach der Tile-Entfernung...');
|
||||
const powerUpsCreated = await this.createPowerUpsForMatches(matches);
|
||||
await this.createPowerUpsForMatches(matches);
|
||||
|
||||
// Wenn Raketen erstellt wurden, lass sie im nächsten Zug starten
|
||||
if (powerUpsCreated && powerUpsCreated.rockets && powerUpsCreated.rockets.length > 0) {
|
||||
debugLog(`🚀 ${powerUpsCreated.rockets.length} Raketen erstellt - werden im nächsten Zug aktiviert`);
|
||||
|
||||
// Aktualisiere die Anzeige
|
||||
this.$forceUpdate();
|
||||
|
||||
// Warte kurz, damit die Rakete sichtbar wird
|
||||
await this.wait(300);
|
||||
|
||||
// KEINE automatische Aktivierung - Raketen bleiben auf dem Board
|
||||
// und werden erst durch Spieler-Aktionen aktiviert
|
||||
}
|
||||
this.$forceUpdate();
|
||||
await this.wait(120);
|
||||
|
||||
// Debug: Zeige alle Power-ups nach der Erstellung
|
||||
debugLog('🔧 Debug: Alle Power-ups nach createPowerUpsForMatches:');
|
||||
@@ -1994,36 +2086,12 @@ export default {
|
||||
debugLog('🔧 Debug: Alle Power-ups nach dem Füllen:');
|
||||
this.debugPowerUps();
|
||||
|
||||
// Prüfe ob Power-ups erstellt wurden - wenn ja, keine Cascade-Matches prüfen
|
||||
// Verwende den Rückgabewert von createPowerUpsForMatches
|
||||
if (!powerUpsCreated || powerUpsCreated.count === 0) {
|
||||
// Nur Cascade-Matches prüfen, wenn keine Power-ups erstellt wurden
|
||||
await this.checkForCascadeMatches();
|
||||
} else {
|
||||
debugLog(`🔧 ${powerUpsCreated.count} Power-ups erstellt - überspringe Cascade-Match-Prüfung`);
|
||||
|
||||
// Debug: Zeige alle Power-ups nach der Verarbeitung
|
||||
debugLog('🔧 Debug: Alle Power-ups nach Power-up-Verarbeitung:');
|
||||
this.debugPowerUps();
|
||||
|
||||
// Debug: Zeige alle Power-ups im Template nach der Verarbeitung
|
||||
debugLog('🔧 Debug: Power-ups im Template nach Verarbeitung:');
|
||||
for (let i = 0; i < this.board.length; i++) {
|
||||
if (this.board[i]) {
|
||||
const isPowerUp = this.isPowerUpTile(this.board[i].type);
|
||||
debugLog(`🔧 Position ${i}: Tile ${this.board[i].type}, isPowerUpTile: ${isPowerUp}`);
|
||||
if (isPowerUp) {
|
||||
debugLog(`🔧 ✅ Power-up im Template: ${this.board[i].type} an Position ${i}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await this.checkForCascadeMatches();
|
||||
|
||||
// WICHTIG: Prüfe Level-Objekte nach dem Verarbeiten der Matches
|
||||
if (isPlayerMove && !this.isInitializingLevel) {
|
||||
debugLog('🎯 Prüfe Level-Objekte nach Match-Verarbeitung...');
|
||||
this.checkLevelObjectives();
|
||||
}
|
||||
|
||||
this.checkLevelObjectives();
|
||||
}
|
||||
},
|
||||
|
||||
@@ -2051,15 +2119,9 @@ export default {
|
||||
|
||||
// Wenn Position leer ist, prüfe ob sie im Layout gültig ist
|
||||
if (!this.board[index]) {
|
||||
if (this.currentLevelData && this.currentLevelData.boardLayout) {
|
||||
const layoutRows = this.currentLevelData.boardLayout.split('\n');
|
||||
if (row < layoutRows.length && col < layoutRows[row].length) {
|
||||
const targetChar = layoutRows[row][col];
|
||||
if (targetChar !== 'x') {
|
||||
debugLog(`🔧 Position [${row}, ${col}] ist ungültig im Layout (${targetChar}) - überspringe`);
|
||||
continue; // Überspringe ungültige Positionen
|
||||
}
|
||||
}
|
||||
if (!this.isPlayableBoardCell(row, col)) {
|
||||
debugLog(`🔧 Position [${row}, ${col}] ist ein Design-Leerfeld - überspringe`);
|
||||
continue;
|
||||
}
|
||||
|
||||
debugLog(`🔧 Leere Position gefunden: [${row}, ${col}] -> Index ${index}`);
|
||||
@@ -2069,7 +2131,7 @@ export default {
|
||||
for (let searchRow = row - 1; searchRow >= 0; searchRow--) {
|
||||
const searchIndex = this.coordsToIndex(searchRow, col);
|
||||
|
||||
if (this.board[searchIndex] && !this.isPowerUpTile(this.board[searchIndex].type)) {
|
||||
if (this.board[searchIndex]) {
|
||||
debugLog(`🔧 Tile ${this.board[searchIndex].type} gefunden an [${searchRow}, ${col}] -> verschiebe nach [${row}, ${col}]`);
|
||||
|
||||
// Verschiebe Tile nach unten
|
||||
@@ -2079,8 +2141,8 @@ export default {
|
||||
// Aktualisiere DOM
|
||||
this.$forceUpdate();
|
||||
|
||||
// Warte kurz für Animation
|
||||
await this.wait(500);
|
||||
// Kurze Wartezeit, damit die Bewegung sichtbar bleibt ohne das Spiel zu bremsen
|
||||
await this.wait(120);
|
||||
|
||||
hasChanges = true;
|
||||
tileFound = true;
|
||||
@@ -2104,15 +2166,9 @@ export default {
|
||||
|
||||
// Wenn oberste Position leer ist, prüfe ob sie im Layout gültig ist
|
||||
if (!this.board[index]) {
|
||||
if (this.currentLevelData && this.currentLevelData.boardLayout) {
|
||||
const layoutRows = this.currentLevelData.boardLayout.split('\n');
|
||||
if (0 < layoutRows.length && col < layoutRows[0].length) {
|
||||
const targetChar = layoutRows[0][col];
|
||||
if (targetChar !== 'x') {
|
||||
debugLog(`🔧 Oberste Position [0, ${col}] ist ungültig im Layout (${targetChar}) - überspringe`);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (!this.isPlayableBoardCell(0, col)) {
|
||||
debugLog(`🔧 Oberste Position [0, ${col}] ist ein Design-Leerfeld - überspringe`);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Erstelle neues Tile
|
||||
@@ -2125,7 +2181,7 @@ export default {
|
||||
|
||||
// Aktualisiere DOM nach dem Auffüllen
|
||||
this.$forceUpdate();
|
||||
await this.wait(100);
|
||||
await this.wait(40);
|
||||
}
|
||||
|
||||
debugLog(`🔧 Iteration ${iteration} abgeschlossen - Änderungen: ${hasChanges}`);
|
||||
@@ -2137,6 +2193,37 @@ export default {
|
||||
|
||||
debugLog('🔧 Fall-Down-Logik abgeschlossen');
|
||||
},
|
||||
|
||||
isPlayableBoardCell(row, col) {
|
||||
if (row < 0 || row >= this.boardHeight || col < 0 || col >= this.boardWidth) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.boardLayout?.[row]?.[col]) {
|
||||
return this.boardLayout[row][col].type !== 'empty';
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
collectEmptyPlayableFields() {
|
||||
const emptyPlayableFields = [];
|
||||
|
||||
for (let row = 0; row < this.boardHeight; row++) {
|
||||
for (let col = 0; col < this.boardWidth; col++) {
|
||||
if (!this.isPlayableBoardCell(row, col)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const index = this.coordsToIndex(row, col);
|
||||
if (index !== null && !this.board[index]) {
|
||||
emptyPlayableFields.push({ index, row, col });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return emptyPlayableFields;
|
||||
},
|
||||
|
||||
// Hilfsfunktion: Zeige den aktuellen Board-Zustand in der Konsole
|
||||
printBoardState() {
|
||||
@@ -2226,8 +2313,8 @@ export default {
|
||||
if (newTilesAdded > 0) {
|
||||
debugLog('🔍 Überprüfe das Brett auf Matches nach dem Füllen der leeren Positionen...');
|
||||
|
||||
// Warte kurz, damit die neuen Tiles vollständig angezeigt werden
|
||||
await this.wait(300);
|
||||
// Nur kurz warten, damit das Brett aktualisiert ist
|
||||
await this.wait(80);
|
||||
|
||||
// Prüfe auf Matches auf dem aktuellen Board
|
||||
const matchesAfterFill = this.findMatchesOnBoard(this.board, false);
|
||||
@@ -2285,7 +2372,7 @@ export default {
|
||||
match.forEach(pos => usedPositions.add(pos));
|
||||
|
||||
// Erstelle Regenbogen-Tile
|
||||
this.board[rainbowIndex] = { type: 'rainbow' };
|
||||
this.board[rainbowIndex] = this.createTile('rainbow');
|
||||
|
||||
debugLog(`🌈 Regenbogen-Tile an Position ${rainbowIndex} erstellt`);
|
||||
debugLog(`🔧 Board[${rainbowIndex}] = ${JSON.stringify(this.board[rainbowIndex])}`);
|
||||
@@ -2309,7 +2396,7 @@ export default {
|
||||
usedPositions.add(match.corner);
|
||||
|
||||
// Bombe an der Ecke erstellen
|
||||
this.board[match.corner] = { type: 'bomb' };
|
||||
this.board[match.corner] = this.createTile('bomb');
|
||||
|
||||
debugLog(`💣 Bombe an Position ${match.corner} erstellt`);
|
||||
debugLog(`🔧 Board[${match.corner}] = ${JSON.stringify(this.board[match.corner])}`);
|
||||
@@ -2337,7 +2424,7 @@ export default {
|
||||
|
||||
// Erstelle Rakete basierend auf der Richtung des Matches
|
||||
const rocketType = this.determineRocketType(match);
|
||||
this.board[rocketIndex] = { type: rocketType };
|
||||
this.board[rocketIndex] = this.createTile(rocketType);
|
||||
|
||||
debugLog(`🚀 Rakete ${rocketType} an Position ${rocketIndex} erstellt`);
|
||||
debugLog(`🔧 Board[${rocketIndex}] = ${JSON.stringify(this.board[rocketIndex])}`);
|
||||
@@ -2380,99 +2467,45 @@ export default {
|
||||
async checkAndFillEmptyValidFields() {
|
||||
debugLog('🔧 Prüfe alle leeren gültigen Felder...');
|
||||
|
||||
let hasEmptyValidFields = false;
|
||||
const emptyValidFields = [];
|
||||
|
||||
// Gehe durch alle Positionen im Board
|
||||
for (let row = 0; row < this.boardHeight; row++) {
|
||||
for (let col = 0; col < this.boardWidth; col++) {
|
||||
const index = this.coordsToIndex(row, col);
|
||||
|
||||
// Wenn Position leer ist, prüfe ob sie im Layout gültig ist
|
||||
if (!this.board[index]) {
|
||||
if (this.currentLevelData && this.currentLevelData.boardLayout) {
|
||||
const layout = this.currentLevelData.boardLayout;
|
||||
const layoutRows = layout.split('\n');
|
||||
|
||||
// Prüfe, ob das Feld im Layout gültig ist (nicht 'o')
|
||||
if (row < layoutRows.length && col < layoutRows[row].length) {
|
||||
const targetChar = layoutRows[row][col];
|
||||
|
||||
if (targetChar === 'x') {
|
||||
// Gültiges Feld ist leer - muss gefüllt werden
|
||||
emptyValidFields.push({ index, row, col });
|
||||
hasEmptyValidFields = true;
|
||||
debugLog(`🔧 Gültiges Feld [${row}, ${col}] ist leer und muss gefüllt werden`);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (this.board[index] && this.isPowerUpTile(this.board[index].type)) {
|
||||
// Position enthält bereits ein Power-up - nicht überschreiben
|
||||
debugLog(`🔧 Position [${row}, ${col}] enthält bereits Power-up ${this.board[index].type} - wird nicht überschrieben`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hasEmptyValidFields) {
|
||||
debugLog(`🔧 ${emptyValidFields.length} leere gültige Felder gefunden - starte erneuten Fall-Prozess`);
|
||||
|
||||
// Fülle alle leeren gültigen Felder mit neuen Tiles
|
||||
let safetyCounter = 0;
|
||||
let emptyValidFields = this.collectEmptyPlayableFields();
|
||||
|
||||
while (emptyValidFields.length > 0 && safetyCounter < 8) {
|
||||
safetyCounter++;
|
||||
debugLog(`🔧 ${emptyValidFields.length} leere gültige Felder gefunden - Füllrunde ${safetyCounter}`);
|
||||
|
||||
for (const field of emptyValidFields) {
|
||||
const newTile = this.createRandomTile();
|
||||
this.board[field.index] = newTile;
|
||||
debugLog(`🔧 Neues Tile ${newTile.type} an Position [${field.row}, ${field.col}] hinzugefügt`);
|
||||
}
|
||||
|
||||
// Aktualisiere die Anzeige
|
||||
|
||||
this.$forceUpdate();
|
||||
|
||||
// Führe Animation für neue Tiles aus
|
||||
await this.animateNewTilesAppearing(emptyValidFields.map(field => ({
|
||||
index: field.index,
|
||||
row: field.row,
|
||||
col: field.col,
|
||||
type: this.board[field.index].type
|
||||
|
||||
await this.animateNewTilesAppearing(emptyValidFields.map(field => ({
|
||||
index: field.index,
|
||||
row: field.row,
|
||||
col: field.col,
|
||||
type: this.board[field.index].type
|
||||
})));
|
||||
|
||||
debugLog(`🔧 Alle leeren gültigen Felder gefüllt`);
|
||||
|
||||
// WICHTIG: Nach dem Füllen der leeren gültigen Felder das Brett auf Matches überprüfen
|
||||
debugLog('🔍 Überprüfe das Brett auf Matches nach dem Füllen der leeren gültigen Felder...');
|
||||
|
||||
// Warte kurz, damit die neuen Tiles vollständig angezeigt werden
|
||||
await this.wait(300);
|
||||
|
||||
// Prüfe auf Matches auf dem aktuellen Board
|
||||
const matchesAfterFill = this.findMatchesOnBoard(this.board, false);
|
||||
|
||||
if (matchesAfterFill.length > 0) {
|
||||
debugLog(`🔍 ${matchesAfterFill.length} Match(es) nach dem Füllen der leeren gültigen Felder gefunden - starte automatische Behandlung`);
|
||||
|
||||
// Behandle die gefundenen Matches automatisch (kein Spieler-Move)
|
||||
await this.handleMatches(matchesAfterFill, false);
|
||||
|
||||
// WICHTIG: Rekursiver Aufruf, falls durch die Matches neue leere Positionen entstehen
|
||||
// Das verhindert Endlosschleifen durch max. 3 Rekursionen
|
||||
if (this.recursionDepth === undefined) {
|
||||
this.recursionDepth = 0;
|
||||
}
|
||||
|
||||
if (this.recursionDepth < 3) {
|
||||
this.recursionDepth++;
|
||||
debugLog(`🔄 Rekursiver Aufruf ${this.recursionDepth}/3 - prüfe auf weitere leere gültige Felder`);
|
||||
|
||||
// Prüfe erneut auf leere gültige Felder und fülle sie auf
|
||||
await this.checkAndFillEmptyValidFields();
|
||||
|
||||
this.recursionDepth--;
|
||||
} else {
|
||||
debugLog('⚠️ Maximale Rekursionstiefe erreicht - stoppe automatische Match-Behandlung');
|
||||
}
|
||||
} else {
|
||||
debugLog('✅ Keine Matches nach dem Füllen der leeren gültigen Felder gefunden - Board ist bereit');
|
||||
}
|
||||
} else {
|
||||
debugLog('🔧 Alle gültigen Felder enthalten Tiles - Board ist vollständig');
|
||||
|
||||
await this.wait(40);
|
||||
await this.fallTilesDown();
|
||||
emptyValidFields = this.collectEmptyPlayableFields();
|
||||
}
|
||||
|
||||
if (safetyCounter >= 8 && emptyValidFields.length > 0) {
|
||||
debugLog(`⚠️ Brett konnte nach ${safetyCounter} Füllrunden nicht vollständig stabilisiert werden`);
|
||||
}
|
||||
|
||||
if (emptyValidFields.length === 0) {
|
||||
debugLog('🔧 Alle spielbaren Felder enthalten Tiles - Board ist vollständig');
|
||||
}
|
||||
|
||||
const matchesAfterFill = this.findMatchesOnBoard(this.board, false);
|
||||
if (matchesAfterFill.length > 0) {
|
||||
debugLog(`🔍 ${matchesAfterFill.length} Match(es) nach der Stabilisierung gefunden - starte automatische Behandlung`);
|
||||
await this.handleMatches(matchesAfterFill, false);
|
||||
}
|
||||
},
|
||||
|
||||
@@ -2498,8 +2531,8 @@ export default {
|
||||
|
||||
debugLog(`🎬 ${tileElements.length} DOM-Elemente für Animation vorbereitet`);
|
||||
|
||||
// Warte auf die Animation (0,75 Sekunden)
|
||||
await this.wait(750);
|
||||
// Warte auf die Animation
|
||||
await this.wait(280);
|
||||
|
||||
// Entferne die CSS-Klassen
|
||||
tileElements.forEach(element => {
|
||||
@@ -2525,7 +2558,7 @@ export default {
|
||||
|
||||
// Setze das Tile an seine ursprüngliche Position (oben) mit transform
|
||||
element.style.transform = `translateY(-${fallPixels}px)`;
|
||||
element.style.transition = 'transform 0.4s ease-out';
|
||||
element.style.transition = 'transform 0.18s ease-out';
|
||||
|
||||
// Füge CSS-Klasse für die Fall-Animation hinzu
|
||||
element.classList.add('falling');
|
||||
@@ -2553,8 +2586,8 @@ export default {
|
||||
// Spiele Fall-Sound ab
|
||||
this.playSound('falling');
|
||||
|
||||
// Warte auf die Fall-Animation (0,4 Sekunden)
|
||||
await this.wait(400);
|
||||
// Warte auf die Fall-Animation
|
||||
await this.wait(180);
|
||||
|
||||
// Entferne die CSS-Klassen und transform-Eigenschaften
|
||||
tileElements.forEach(element => {
|
||||
@@ -2588,8 +2621,8 @@ export default {
|
||||
|
||||
debugLog(`🎬 ${tileElements.length} DOM-Elemente für Erscheinungs-Animation vorbereitet`);
|
||||
|
||||
// Warte auf die Erscheinungs-Animation (0,5 Sekunden)
|
||||
await this.wait(500);
|
||||
// Warte auf die Erscheinungs-Animation
|
||||
await this.wait(180);
|
||||
|
||||
// Entferne die CSS-Klassen
|
||||
tileElements.forEach(element => {
|
||||
@@ -2606,12 +2639,12 @@ export default {
|
||||
const randomType = this.currentLevelData.tileTypes[
|
||||
Math.floor(Math.random() * this.currentLevelData.tileTypes.length)
|
||||
];
|
||||
return { type: randomType };
|
||||
return this.createTile(randomType);
|
||||
} else {
|
||||
// Fallback: Verwende Standard-Tile-Typen
|
||||
const defaultTypes = ['red', 'blue', 'green', 'yellow', 'purple'];
|
||||
const randomType = defaultTypes[Math.floor(Math.random() * defaultTypes.length)];
|
||||
return { type: randomType };
|
||||
return this.createTile(randomType);
|
||||
}
|
||||
},
|
||||
|
||||
@@ -2802,28 +2835,15 @@ export default {
|
||||
async checkForCascadeMatches() {
|
||||
debugLog('🔧 Prüfe auf Cascade-Matches...');
|
||||
|
||||
// Warte kurz, damit alle Animationen abgeschlossen sind
|
||||
await this.wait(200);
|
||||
// Nur kurze Synchronisierung nach dem Fallen
|
||||
await this.wait(60);
|
||||
|
||||
// Prüfe ob neue Matches entstanden sind
|
||||
const newMatches = this.findMatchesOnBoard(this.board, false);
|
||||
|
||||
// Filtere Power-up-Matches heraus (diese sollen nicht als Cascade-Matches behandelt werden)
|
||||
const filteredMatches = newMatches.filter(match => {
|
||||
if (match.type === 'l-shape') {
|
||||
debugLog('🔧 L-Form Match in Cascade gefunden - überspringe');
|
||||
return false;
|
||||
}
|
||||
if (Array.isArray(match) && match.length === 4) {
|
||||
debugLog('🔧 4er-Match in Cascade gefunden - überspringe');
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
if (filteredMatches.length > 0) {
|
||||
debugLog(`🔧 ${filteredMatches.length} neue Cascade-Matches gefunden`);
|
||||
await this.handleMatches(filteredMatches, false);
|
||||
if (newMatches.length > 0) {
|
||||
debugLog(`🔧 ${newMatches.length} neue Cascade-Matches gefunden`);
|
||||
await this.handleMatches(newMatches, false);
|
||||
} else {
|
||||
debugLog('🔧 Keine neuen Cascade-Matches gefunden');
|
||||
}
|
||||
@@ -3411,14 +3431,19 @@ export default {
|
||||
}
|
||||
|
||||
debugLog(`🔧 Starte Drag für Tile ${tileIndex}`);
|
||||
|
||||
const pointer = this.getPointerCoordinates(event);
|
||||
if (!pointer) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Setze Drag-Status
|
||||
this.draggedTileIndex = tileIndex;
|
||||
this.isDragging = true;
|
||||
|
||||
// Speichere Start-Position für Drag-Offset
|
||||
this.dragStartX = event.clientX;
|
||||
this.dragStartY = event.clientY;
|
||||
this.dragStartX = pointer.x;
|
||||
this.dragStartY = pointer.y;
|
||||
|
||||
// WICHTIG: Speichere die ursprüngliche Position des gedraggten Tiles
|
||||
const tileElement = event.target.closest('.game-tile');
|
||||
@@ -3483,12 +3508,21 @@ export default {
|
||||
if (!this.isDragging || this.draggedTileIndex === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
const pointer = this.getPointerCoordinates(event);
|
||||
if (!pointer) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (event?.cancelable) {
|
||||
event.preventDefault();
|
||||
}
|
||||
|
||||
debugLog(`🔧 onTileMouseMove: clientX=${event.clientX}, clientY=${event.clientY}`);
|
||||
debugLog(`🔧 onTileMouseMove: clientX=${pointer.x}, clientY=${pointer.y}`);
|
||||
|
||||
// Berechne Drag-Offset
|
||||
const deltaX = event.clientX - this.dragStartX;
|
||||
const deltaY = event.clientY - this.dragStartY;
|
||||
const deltaX = pointer.x - this.dragStartX;
|
||||
const deltaY = pointer.y - this.dragStartY;
|
||||
|
||||
debugLog(`🔧 Drag-Offset: deltaX=${deltaX}px, deltaY=${deltaY}px`);
|
||||
|
||||
@@ -3724,7 +3758,7 @@ export default {
|
||||
onGlobalMouseUp(event) {
|
||||
if (this.isDragging) {
|
||||
debugLog(`🔧 Globaler MouseUp während Drag - beende Drag`);
|
||||
this.endDrag();
|
||||
this.endDrag(event);
|
||||
}
|
||||
},
|
||||
|
||||
@@ -3744,23 +3778,11 @@ export default {
|
||||
debugLog(`🔧 Beende Drag: draggedTileIndex=${this.draggedTileIndex}, targetTile=${tileIndex}`);
|
||||
|
||||
// WICHTIG: Prüfe ob ein Tile tatsächlich animiert wurde
|
||||
let shouldPerformMove = false;
|
||||
let targetTileIndex = null;
|
||||
|
||||
if (this.draggedTileIndex !== tileIndex) {
|
||||
// Verschiedene Tiles - prüfe ob das Ziel-Tile animiert wurde
|
||||
if (this.currentlyAnimatingTile === tileIndex) {
|
||||
// Das Ziel-Tile ist animiert - führe Move durch
|
||||
shouldPerformMove = true;
|
||||
targetTileIndex = tileIndex;
|
||||
debugLog(`🔧 Ziel-Tile ${tileIndex} ist animiert - führe Move durch`);
|
||||
} else {
|
||||
// Das Ziel-Tile ist nicht animiert - kein Move
|
||||
debugLog(`🔧 Ziel-Tile ${tileIndex} ist nicht animiert - kein Move`);
|
||||
}
|
||||
} else {
|
||||
debugLog(`🔧 Gleiches Tile, kein Move erforderlich`);
|
||||
}
|
||||
const targetTileIndex = this.resolveDragTargetIndex(event, tileIndex);
|
||||
const shouldPerformMove =
|
||||
targetTileIndex !== null &&
|
||||
targetTileIndex !== this.draggedTileIndex &&
|
||||
this.areTilesAdjacent(this.draggedTileIndex, targetTileIndex);
|
||||
|
||||
// Setze alle Animationen zurück
|
||||
this.resetAllTileAnimations();
|
||||
@@ -3786,7 +3808,7 @@ export default {
|
||||
},
|
||||
|
||||
// Beende den Drag korrekt
|
||||
endDrag() {
|
||||
endDrag(event = null) {
|
||||
debugLog(`🔧 endDrag aufgerufen`);
|
||||
|
||||
if (!this.isDragging) {
|
||||
@@ -3795,20 +3817,11 @@ export default {
|
||||
}
|
||||
|
||||
// Prüfe ob ein Tile tatsächlich animiert wurde UND ob es sich um ein anderes Tile handelt
|
||||
let shouldPerformMove = false;
|
||||
let targetTileIndex = null;
|
||||
|
||||
if (this.currentlyAnimatingTile !== null && this.currentlyAnimatingTile !== this.draggedTileIndex) {
|
||||
// Ein anderes Tile ist animiert - führe Move durch
|
||||
shouldPerformMove = true;
|
||||
targetTileIndex = this.currentlyAnimatingTile;
|
||||
debugLog(`🔧 Anderes Tile ${targetTileIndex} ist animiert - führe Move durch`);
|
||||
} else if (this.currentlyAnimatingTile === this.draggedTileIndex) {
|
||||
// Das gedraggte Tile ist auf sich selbst animiert - kein Move
|
||||
debugLog(`🔧 Gedraggtes Tile ist auf sich selbst animiert - kein Move`);
|
||||
} else {
|
||||
debugLog(`🔧 Kein Tile animiert - kein Move`);
|
||||
}
|
||||
let targetTileIndex = this.resolveDragTargetIndex(event);
|
||||
let shouldPerformMove =
|
||||
targetTileIndex !== null &&
|
||||
targetTileIndex !== this.draggedTileIndex &&
|
||||
this.areTilesAdjacent(this.draggedTileIndex, targetTileIndex);
|
||||
|
||||
// Zusätzliche Prüfung: Wenn das gedraggte Tile fast an seiner ursprünglichen Position ist, kein Move
|
||||
if (shouldPerformMove && this.originalTilePosition) {
|
||||
@@ -4396,7 +4409,8 @@ export default {
|
||||
|
||||
// Hilfsmethode: Prüfe ob ein Tile ein Power-Up ist
|
||||
isPowerUpTile(tile) {
|
||||
return tile && this.powerUpTypes.includes(tile.type);
|
||||
const tileType = this.getTileType(tile);
|
||||
return this.getPowerUpKind(tileType) !== null;
|
||||
},
|
||||
|
||||
// Hilfsmethode: Prüfe ob zwei Tiles matchen können
|
||||
@@ -4992,8 +5006,17 @@ export default {
|
||||
// Power-Up Tausch als Zug zählen (wird auch über swapTiles aufgerufen)
|
||||
// WICHTIG: Zähle den Zug nur einmal hier, nicht in den nachfolgenden Funktionen
|
||||
this.countPowerUpMove();
|
||||
|
||||
const tile1Type = this.getTileType(originalTile1);
|
||||
const tile2Type = this.getTileType(originalTile2);
|
||||
const tile1IsRocket = this.isRocketTile(tile1Type);
|
||||
const tile2IsRocket = this.isRocketTile(tile2Type);
|
||||
const tile1IsBomb = tile1Type === 'bomb';
|
||||
const tile2IsBomb = tile2Type === 'bomb';
|
||||
const tile1IsRainbow = tile1Type === 'rainbow';
|
||||
const tile2IsRainbow = tile2Type === 'rainbow';
|
||||
|
||||
if (originalTile1.type === 'rainbow' && originalTile2.type === 'rainbow') {
|
||||
if (tile1IsRainbow && tile2IsRainbow) {
|
||||
// Spiele Regenbogen-Sound
|
||||
this.playSound('rainbow');
|
||||
|
||||
@@ -5001,29 +5024,48 @@ export default {
|
||||
debugLog('🌈 Zwei Regenbogen-Tiles kombiniert - entferne alle Tiles vom Board!');
|
||||
await this.removeAllTilesFromBoardIncludingRainbows();
|
||||
return true;
|
||||
} else if (originalTile1.type === 'rainbow' || originalTile2.type === 'rainbow') {
|
||||
} else if ((tile1IsBomb && tile2IsRainbow) || (tile1IsRainbow && tile2IsBomb)) {
|
||||
this.playSound('rainbow');
|
||||
this.createRandomBombs(20);
|
||||
setTimeout(() => {
|
||||
this.detonateAllBombs();
|
||||
}, 500);
|
||||
return true;
|
||||
} else if ((tile1IsRocket && tile2IsRainbow) || (tile1IsRainbow && tile2IsRocket)) {
|
||||
this.playSound('rainbow');
|
||||
this.createRandomRockets(10);
|
||||
setTimeout(() => {
|
||||
this.launchAllRockets();
|
||||
}, 500);
|
||||
return true;
|
||||
} else if (tile1IsRainbow || tile2IsRainbow) {
|
||||
// Spiele Regenbogen-Sound
|
||||
this.playSound('rainbow');
|
||||
|
||||
// Ein Regenbogen-Tile mit normalem Tile getauscht: Entferne alle Tiles des normalen Typs
|
||||
const normalTile = originalTile1.type === 'rainbow' ? originalTile2 : originalTile1;
|
||||
const rainbowIndex = originalTile1.type === 'rainbow' ? this.findTileIndex(originalTile1) : this.findTileIndex(originalTile2);
|
||||
const normalTile = tile1IsRainbow ? originalTile2 : originalTile1;
|
||||
const rainbowIndex = tile1IsRainbow ? this.findTileIndex(originalTile1) : this.findTileIndex(originalTile2);
|
||||
|
||||
if (rainbowIndex !== null) {
|
||||
await this.activateRainbowByType(rainbowIndex, normalTile.type);
|
||||
}
|
||||
return true;
|
||||
} else if (originalTile1.type === 'bomb' && originalTile2.type === 'bomb') {
|
||||
} else if (tile1IsBomb && tile2IsBomb) {
|
||||
// Zwei Bomben-Tiles getauscht: Entferne 2 Ringe (5x5 Bereich)
|
||||
const bombIndex = this.findBombIndex(originalTile1, originalTile2);
|
||||
if (bombIndex !== null) {
|
||||
this.explodeBomb(bombIndex, 2, true); // 2 Ringe = 5x5 Bereich - manuelle Aktivierung
|
||||
}
|
||||
return true;
|
||||
} else if (originalTile1.type === 'bomb' || originalTile2.type === 'bomb') {
|
||||
} else if ((tile1IsRocket && tile2IsBomb) || (tile1IsBomb && tile2IsRocket)) {
|
||||
this.handleRocketBombCombination(originalTile1, originalTile2);
|
||||
return true;
|
||||
} else if (tile1IsRocket && tile2IsRocket) {
|
||||
this.handleRocketConnection(originalTile1, originalTile2);
|
||||
return true;
|
||||
} else if (tile1IsBomb || tile2IsBomb) {
|
||||
// Ein Bomben-Tile mit normalem Tile getauscht: Entferne 9 Tiles rund um das Ziel
|
||||
const bombTile = originalTile1.type === 'bomb' ? originalTile1 : originalTile2;
|
||||
const targetTile = originalTile1.type === 'bomb' ? originalTile2 : originalTile1;
|
||||
const bombTile = tile1IsBomb ? originalTile1 : originalTile2;
|
||||
|
||||
// Finde die neue Position der Bombe
|
||||
const newBombIndex = this.findTileIndex(bombTile);
|
||||
@@ -5031,41 +5073,10 @@ export default {
|
||||
this.explodeBomb(newBombIndex, 1, true); // 1 Ring = 3x3 Bereich - manuelle Aktivierung
|
||||
}
|
||||
return true;
|
||||
} else if ((originalTile1.type === 'bomb' && originalTile2.type === 'rainbow') ||
|
||||
(originalTile1.type === 'rainbow' && originalTile2.type === 'bomb')) {
|
||||
// Spiele Regenbogen-Sound
|
||||
this.playSound('rainbow');
|
||||
|
||||
// Bombe + Regenbogen: Erstelle zufällige Bomben und löse sie aus
|
||||
this.createRandomBombs(20);
|
||||
setTimeout(() => {
|
||||
this.detonateAllBombs();
|
||||
}, 500); // Kurze Verzögerung für visuellen Effekt
|
||||
return true;
|
||||
} else if (originalTile1.type === 'rocket' && originalTile2.type === 'rocket') {
|
||||
// Zwei Raketen verbunden: Lösche Nachbarfelder beider Raketen und starte 3 Raketen
|
||||
this.handleRocketConnection(originalTile1, originalTile2);
|
||||
return true;
|
||||
} else if ((originalTile1.type === 'rocket' && originalTile2.type === 'rainbow') ||
|
||||
(originalTile1.type === 'rainbow' && originalTile2.type === 'rocket')) {
|
||||
// Spiele Regenbogen-Sound
|
||||
this.playSound('rainbow');
|
||||
|
||||
// Rakete + Regenbogen: Erstelle zufällige Raketen und starte sie
|
||||
this.createRandomRockets(10);
|
||||
setTimeout(() => {
|
||||
this.launchAllRockets();
|
||||
}, 500); // Kurze Verzögerung für visuellen Effekt
|
||||
return true;
|
||||
} else if ((originalTile1.type === 'rocket' && originalTile2.type === 'bomb') ||
|
||||
(originalTile1.type === 'bomb' && originalTile2.type === 'rocket')) {
|
||||
// Rakete + Bombe: Lösche 4 Nachbarfelder und starte Bomben-Rakete
|
||||
this.handleRocketBombCombination(originalTile1, originalTile2);
|
||||
return true;
|
||||
} else if (originalTile1.type === 'rocket' || originalTile2.type === 'rocket') {
|
||||
} else if (tile1IsRocket || tile2IsRocket) {
|
||||
// Eine Rakete mit normalem Tile: Rakete fliegt auf zufälliges Feld
|
||||
const rocketTile = originalTile1.type === 'rocket' ? originalTile1 : originalTile2;
|
||||
const targetTile = originalTile1.type === 'rocket' ? originalTile2 : originalTile1;
|
||||
const rocketTile = tile1IsRocket ? originalTile1 : originalTile2;
|
||||
const targetTile = tile1IsRocket ? originalTile2 : originalTile1;
|
||||
this.handleRocketLaunch(rocketTile, targetTile);
|
||||
return true;
|
||||
}
|
||||
@@ -5087,7 +5098,10 @@ export default {
|
||||
findTileIndex(originalTile) {
|
||||
// Suche nach dem Tile auf dem Board
|
||||
for (let i = 0; i < this.board.length; i++) {
|
||||
if (this.board[i] && this.board[i].id === originalTile.id) {
|
||||
if (this.board[i] === originalTile) {
|
||||
return i;
|
||||
}
|
||||
if (this.board[i] && originalTile && this.board[i].id && originalTile.id && this.board[i].id === originalTile.id) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
@@ -5291,7 +5305,7 @@ export default {
|
||||
if (powerUp.type === 'bomb') {
|
||||
// Bombe explodiert mit 1 Ring (3x3 Bereich)
|
||||
this.explodeBomb(powerUp.index, 1, true);
|
||||
} else if (powerUp.type === 'rocket') {
|
||||
} else if (this.isRocketTile(powerUp.type)) {
|
||||
// Rakete startet auf zufälliges Feld
|
||||
this.launchRocketToRandomField(powerUp.index);
|
||||
}
|
||||
@@ -5401,7 +5415,7 @@ export default {
|
||||
|
||||
// Finde alle Raketen auf dem Board
|
||||
for (let i = 0; i < this.board.length; i++) {
|
||||
if (this.board[i] && this.board[i].type === 'rocket') {
|
||||
if (this.board[i] && this.isRocketTile(this.board[i].type)) {
|
||||
rocketIndices.push(i);
|
||||
}
|
||||
}
|
||||
@@ -5424,7 +5438,7 @@ export default {
|
||||
// Neue Methode: Behandle Rakete + Bombe Kombination
|
||||
handleRocketBombCombination(originalTile1, originalTile2) {
|
||||
// Finde die Position einer der Power-Ups (für die 4 Nachbarfelder)
|
||||
const rocketTile = originalTile1.type === 'rocket' ? originalTile1 : originalTile2;
|
||||
const rocketTile = this.isRocketTile(originalTile1.type) ? originalTile1 : originalTile2;
|
||||
const bombTile = originalTile1.type === 'bomb' ? originalTile1 : originalTile2;
|
||||
|
||||
// Finde die Position auf dem Board
|
||||
@@ -5562,12 +5576,18 @@ export default {
|
||||
x: endRect.left - boardRect.left + endRect.width / 2 - 20,
|
||||
y: endRect.top - boardRect.top + endRect.height / 2 - 20
|
||||
};
|
||||
|
||||
this.rocketTarget = {
|
||||
x: endRect.left - boardRect.left + endRect.width / 2,
|
||||
y: endRect.top - boardRect.top + endRect.height / 2
|
||||
};
|
||||
|
||||
this.showRocketFlight = true;
|
||||
|
||||
// Verstecke Animation nach der Dauer
|
||||
setTimeout(() => {
|
||||
this.showRocketFlight = false;
|
||||
this.rocketTarget = { x: -1, y: -1 };
|
||||
}, 1200);
|
||||
}
|
||||
});
|
||||
@@ -6410,6 +6430,7 @@ export default {
|
||||
border-radius: 8px;
|
||||
box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
position: relative; /* Wichtig für absolute Positionierung der animierten Tiles */
|
||||
touch-action: none;
|
||||
}
|
||||
|
||||
.game-tile {
|
||||
@@ -6427,6 +6448,7 @@ export default {
|
||||
pointer-events: auto;
|
||||
user-select: none;
|
||||
position: relative;
|
||||
touch-action: none;
|
||||
}
|
||||
|
||||
/* Vergrößere den klickbaren Bereich für besseres Drag&Drop */
|
||||
@@ -6478,7 +6500,7 @@ export default {
|
||||
|
||||
/* Schrumpf-Animation für das Entfernen von Tiles */
|
||||
.game-tile.removing {
|
||||
transition: all 0.75s ease-out;
|
||||
transition: all 0.28s ease-out;
|
||||
transform: scale(0.1) rotate(360deg);
|
||||
opacity: 0;
|
||||
z-index: 200;
|
||||
@@ -6498,12 +6520,12 @@ export default {
|
||||
|
||||
/* Fall-Animation */
|
||||
.game-tile.falling {
|
||||
transition: transform 0.5s ease-in, opacity 0.3s ease-out;
|
||||
transition: transform 0.18s ease-in, opacity 0.18s ease-out;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.game-tile.new-tile {
|
||||
transition: opacity 0.5s ease-in;
|
||||
transition: opacity 0.18s ease-in;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
@@ -6741,12 +6763,25 @@ export default {
|
||||
animation: explosion 2s ease-out forwards;
|
||||
}
|
||||
|
||||
.rocket-flight {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
background: linear-gradient(45deg, #ff6b6b, #ffd93d);
|
||||
clip-path: polygon(50% 0%, 0% 100%, 100% 100%);
|
||||
animation: rocketFlight 3s ease-in-out forwards;
|
||||
.rocket-flight-path {
|
||||
position: relative;
|
||||
width: 0;
|
||||
height: 0;
|
||||
animation: rocketTravel 0.9s ease-in-out forwards;
|
||||
}
|
||||
|
||||
.rocket-flight-icon {
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
margin-left: -22px;
|
||||
margin-top: -22px;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 24px;
|
||||
background: linear-gradient(135deg, #fff7e8, #ffd7a8);
|
||||
box-shadow: 0 10px 24px rgba(181, 94, 21, 0.28);
|
||||
}
|
||||
|
||||
.rainbow-effect {
|
||||
@@ -6895,35 +6930,51 @@ export default {
|
||||
}
|
||||
|
||||
/* Raketen-Flug-Animation */
|
||||
.rocket-flight-animation {
|
||||
position: fixed;
|
||||
z-index: 2000;
|
||||
.rocket-target-marker {
|
||||
position: absolute;
|
||||
z-index: 1400;
|
||||
pointer-events: none;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
|
||||
.rocket-flight {
|
||||
.rocket-target-marker__icon {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
background: white;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 20px;
|
||||
animation: rocketFlight 1s ease-in-out forwards;
|
||||
font-size: 22px;
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
box-shadow: 0 0 0 6px rgba(255, 107, 107, 0.18);
|
||||
animation: rocketTargetPulse 1.2s ease-in-out forwards;
|
||||
}
|
||||
|
||||
@keyframes rocketFlight {
|
||||
@keyframes rocketTravel {
|
||||
0% {
|
||||
transform: scale(0.5);
|
||||
transform: translate(0, 0) scale(0.7) rotate(-10deg);
|
||||
opacity: 0;
|
||||
}
|
||||
50% {
|
||||
transform: scale(1);
|
||||
15% {
|
||||
opacity: 1;
|
||||
}
|
||||
100% {
|
||||
transform: scale(0.8);
|
||||
transform: translate(var(--dx), var(--dy)) scale(1) rotate(8deg);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes rocketTargetPulse {
|
||||
0% {
|
||||
transform: scale(0.6);
|
||||
opacity: 0;
|
||||
}
|
||||
35% {
|
||||
transform: scale(1.08);
|
||||
opacity: 1;
|
||||
}
|
||||
100% {
|
||||
transform: scale(1);
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -411,7 +411,6 @@ export default {
|
||||
}, 2 * 60 * 1000); // alle 2 Minuten
|
||||
},
|
||||
beforeUnmount() {
|
||||
console.log('🚪 Component unmounting, cleaning up...');
|
||||
this.cleanup();
|
||||
},
|
||||
methods: {
|
||||
@@ -854,8 +853,6 @@ export default {
|
||||
},
|
||||
|
||||
cleanup() {
|
||||
console.log('🧹 Starting cleanup...');
|
||||
|
||||
// Game Loop stoppen
|
||||
if (this.gameLoop) {
|
||||
clearTimeout(this.gameLoop);
|
||||
@@ -929,20 +926,15 @@ export default {
|
||||
this.passengerImages = {};
|
||||
this.carImage = null; // Auto-Bild bereinigen
|
||||
this.tiles = null;
|
||||
|
||||
console.log('🧹 Cleanup completed');
|
||||
},
|
||||
|
||||
// Regelmäßige Memory-Cleanup-Methode
|
||||
performMemoryCleanup() {
|
||||
console.log('🧹 Performing memory cleanup...');
|
||||
|
||||
// Canvas NICHT leeren – das verursacht sichtbares Flackern / Grau
|
||||
// Wir verlassen uns auf das reguläre render() zum Überschreiben des Frames
|
||||
|
||||
// Traffic Light States aggressiver bereinigen
|
||||
if (this.trafficLightStates && Object.keys(this.trafficLightStates).length > 20) {
|
||||
console.log('🧹 Cleaning up traffic light states');
|
||||
// Nur States für aktuelle Map behalten
|
||||
if (this.currentMap && this.currentMap.tiles) {
|
||||
const currentTileKeys = new Set();
|
||||
@@ -966,12 +958,10 @@ export default {
|
||||
|
||||
// Passagier-Listen aggressiver begrenzen
|
||||
if (this.waitingPassengersList && this.waitingPassengersList.length > 20) {
|
||||
console.log('🧹 Trimming waiting passengers list');
|
||||
this.waitingPassengersList = this.waitingPassengersList.slice(-10);
|
||||
}
|
||||
|
||||
if (this.loadedPassengersList && this.loadedPassengersList.length > 20) {
|
||||
console.log('🧹 Trimming loaded passengers list');
|
||||
this.loadedPassengersList = this.loadedPassengersList.slice(-10);
|
||||
}
|
||||
|
||||
@@ -989,8 +979,6 @@ export default {
|
||||
if (window.gc) {
|
||||
window.gc();
|
||||
}
|
||||
|
||||
console.log('🧹 Memory cleanup completed');
|
||||
},
|
||||
|
||||
generateLevel() {
|
||||
|
||||
Reference in New Issue
Block a user