diff --git a/backend/utils/falukant/initializeFalukantTypes.js b/backend/utils/falukant/initializeFalukantTypes.js
index 22507e7..e9daa35 100644
--- a/backend/utils/falukant/initializeFalukantTypes.js
+++ b/backend/utils/falukant/initializeFalukantTypes.js
@@ -14,6 +14,7 @@ import MusicType from "../../models/falukant/type/music.js";
import BanquetteType from "../../models/falukant/type/banquette.js";
import LearnRecipient from "../../models/falukant/type/learn_recipient.js";
import PoliticalOfficeType from "../../models/falukant/type/political_office_type.js";
+import PoliticalOfficeBenefitType from "../../models/falukant/type/political_office_benefit_type.js";
import PoliticalOfficePrerequisite from "../../models/falukant/predefine/political_office_prerequisite.js";
import UndergroundType from "../../models/falukant/type/underground.js";
@@ -32,6 +33,7 @@ export const initializeFalukantTypes = async () => {
await initializeFalukantMusicTypes();
await initializeFalukantBanquetteTypes();
await initializeLearnerTypes();
+ await initializePoliticalOfficeBenefitTypes();
await initializePoliticalOfficeTypes();
await initializePoliticalOfficePrerequisites();
await initializeUndergroundTypes();
@@ -267,6 +269,17 @@ const learnerTypes = [
{ tr: 'director', },
];
+const politicalOfficeBenefitTypes = [
+ { tr: 'salary' },
+ { tr: 'reputation' },
+ { tr: 'influence' },
+ { tr: 'access_level' },
+ { tr: 'housing_allowance' },
+ { tr: 'tax_exemption' },
+ { tr: 'guard_protection' },
+ { tr: 'court_immunity' },
+];
+
const politicalOffices = [
{ tr: "assessor", seatsPerRegion: 10, regionType: "city", termLength: 5 },
{ tr: "councillor", seatsPerRegion: 7, regionType: "city", termLength: 7 },
@@ -847,7 +860,16 @@ export const initializeLearnerTypes = async () => {
}
});
}
-}
+};
+
+export const initializePoliticalOfficeBenefitTypes = async () => {
+ for (const benefitType of politicalOfficeBenefitTypes) {
+ await PoliticalOfficeBenefitType.findOrCreate({
+ where: { tr: benefitType.tr },
+ });
+ }
+ console.log(`[Falukant] PoliticalOfficeBenefitTypes initialisiert: ${politicalOfficeBenefitTypes.length} Typen`);
+};
export const initializePoliticalOfficeTypes = async () => {
for (const po of politicalOffices) {
diff --git a/frontend/src/store/index.js b/frontend/src/store/index.js
index 8e1d327..df2609a 100644
--- a/frontend/src/store/index.js
+++ b/frontend/src/store/index.js
@@ -121,7 +121,6 @@ const store = createStore({
const daemonSocket = new WebSocket(import.meta.env.VITE_DAEMON_SOCKET);
daemonSocket.onopen = () => {
- console.log('Daemon WebSocket connected');
const payload = JSON.stringify({
event: 'setUserId',
data: { userId: state.user.id }
diff --git a/frontend/src/utils/axios.js b/frontend/src/utils/axios.js
index 5f46d1a..d84fae1 100644
--- a/frontend/src/utils/axios.js
+++ b/frontend/src/utils/axios.js
@@ -10,15 +10,10 @@ const apiClient = axios.create({
apiClient.interceptors.request.use(config => {
const user = store.getters.user;
- console.log('đ Axios Interceptor - User:', user);
if (user && user.authCode) {
config.headers['userid'] = user.id;
config.headers['authcode'] = user.authCode; // Kleinschreibung!
- console.log('đĄ Setze Headers:', {
- userid: user.id,
- authcode: user.authCode
- });
} else {
console.log('â ïž Keine User-Daten verfĂŒgbar');
}
diff --git a/frontend/src/views/minigames/Match3Game.vue b/frontend/src/views/minigames/Match3Game.vue
index 8f121b4..f2c0c2a 100644
--- a/frontend/src/views/minigames/Match3Game.vue
+++ b/frontend/src/views/minigames/Match3Game.vue
@@ -90,11 +90,9 @@
+ @mousedown="startDrag(index, $event)"
+ @touchstart="startDrag(index, $event)"
+ @dblclick="handleDoubleClick(index, $event)">
{{ getTileSymbol(tile.type) }}
@@ -113,6 +111,23 @@
Board Layout: {{ boardLayout ? 'Ja' : 'Nein' }}
+
+
+
+
+
+
+
+
+
@@ -206,6 +221,8 @@
+
+
@@ -236,7 +253,8 @@ export default {
boardLayout: [], // Neue: Array der Level-Form
boardWidth: 6, // Standardwert fĂŒr 6x6
boardHeight: 6, // Standardwert fĂŒr 6x6
- tileTypes: ['gem', 'star', 'heart'],
+ tileTypes: ['gem', 'star', 'heart'], // Normale Tiles (ohne Power-Ups)
+ powerUpTypes: ['rocket', 'bomb', 'rainbow'], // Power-Up Tiles (werden nur durch Matches erstellt)
// Game progress
score: 0,
@@ -277,7 +295,18 @@ export default {
apiCallCooldown: 1000, // 1 Sekunde Cooldown zwischen API-Aufrufen
// WICHTIG: Verhindere mehrfache Level-Initialisierung
- isInitializingLevel: false
+ isInitializingLevel: false,
+
+ // Animation states
+ showExplosion: false,
+ explosionPosition: { x: 0, y: 0 },
+ showRocketFlight: false,
+ rocketStartPos: { x: 0, y: 0 },
+ rocketEndPos: { x: 0, y: 0 },
+ showRainbowEffect: false,
+ rainbowCenter: { x: 0, y: 0 },
+ showBombEffect: false,
+ bombCenter: { x: 0, y: 0 }
};
return initialData;
@@ -631,17 +660,25 @@ export default {
// ZusĂ€tzliche PrĂŒfung: Stelle sicher, dass keine initialen Matches vorhanden sind
// WICHTIG: Reduziere Debug-Ausgaben fĂŒr bessere Performance
- const finalCheck = this.findMatchesOnBoard(this.board, false); // false = keine Debug-Ausgaben
- if (finalCheck.length > 0) {
- // WICHTIG: Generiere das Board erneut, anstatt die Matches zu verarbeiten
- // Bei Level-Initialisierung sollen keine Punkte vergeben oder Level-Objekte geprĂŒft werden
- this.board = this.generateBoardFromLayout();
-
- // PrĂŒfe erneut auf Matches (rekursiv, aber mit Limit)
- const secondCheck = this.findMatchesOnBoard(this.board, false);
- if (secondCheck.length > 0) {
- // Board hat immer noch initiale Matches - verwende es wie es ist
+ let attempts = 0;
+ const maxAttempts = 10; // Verhindere Endlosschleifen
+
+ while (attempts < maxAttempts) {
+ const matches = this.findMatchesOnBoard(this.board, false); // false = keine Debug-Ausgaben
+ if (matches.length === 0) {
+ break; // Keine Matches mehr vorhanden
}
+
+ console.log(`đ Initiale Matches gefunden (Versuch ${attempts + 1}):`, matches.length);
+
+ // RĂ€ume die Matches auf, aber ohne Punkte zu vergeben
+ this.cleanupInitialMatches(matches);
+
+ attempts++;
+ }
+
+ if (attempts >= maxAttempts) {
+ console.warn('â ïž Konnte initiale Matches nicht vollstĂ€ndig aufrĂ€umen nach', maxAttempts, 'Versuchen');
}
// WICHTIG: Setze das Spiel als aktiv, aber prĂŒfe NICHT sofort die Level-Objekte
@@ -654,7 +691,12 @@ export default {
},
startDrag(index, event) {
- if (!this.gameActive) {
+ if (!this.gameActive || this.isDragging) {
+ return;
+ }
+
+ // Verhindere Drag wÀhrend der Fall-Animation
+ if (this.isFalling) {
return;
}
@@ -665,6 +707,14 @@ export default {
// Drag-Effekt starten
this.startDragEffect(event, index);
+
+ // Verhindere Standard-Browser-Verhalten
+ event.preventDefault();
+ event.stopPropagation();
+
+ // Registriere Document-Level Event-Handler fĂŒr bessere Kontrolle
+ document.addEventListener('mouseup', this.handleDocumentMouseUp, true);
+ document.addEventListener('touchend', this.handleDocumentTouchEnd, true);
},
endDrag(event) {
@@ -689,16 +739,12 @@ export default {
const endY = event.clientY || event.changedTouches?.[0]?.clientY;
if (endX && endY) {
- const deltaX = endX - this.dragStartX;
- const deltaY = endY - this.dragStartY;
+ // Finde das Tile, ĂŒber dem der Drag endet
+ const targetIndex = this.findTileAtPosition(endX, endY);
- // Bestimme die Richtung des Drags
- const direction = this.getDragDirection(deltaX, deltaY);
-
- if (direction) {
- const targetIndex = this.getAdjacentIndex(this.dragStartIndex, direction);
-
- if (targetIndex !== null) {
+ if (targetIndex !== null && targetIndex !== this.dragStartIndex) {
+ // PrĂŒfe ob die Tiles benachbart sind
+ if (this.areTilesAdjacent(this.dragStartIndex, targetIndex)) {
this.swapTiles(this.dragStartIndex, targetIndex);
}
}
@@ -711,22 +757,70 @@ export default {
this.isDragging = false;
},
- getDragDirection(deltaX, deltaY) {
- const minDragDistance = 30; // Minimale Drag-Distanz fĂŒr einen gĂŒltigen Zug
-
- if (Math.abs(deltaX) < minDragDistance && Math.abs(deltaY) < minDragDistance) {
- return null; // Zu kurzer Drag
+ // Neue Methode: Drag abbrechen (z.B. bei mouseleave)
+ cancelDrag() {
+ if (!this.isDragging || this.dragStartIndex === null) {
+ return;
}
- if (Math.abs(deltaX) > Math.abs(deltaY)) {
- // Horizontale Bewegung
- return deltaX > 0 ? 'right' : 'left';
- } else {
- // Vertikale Bewegung
- return deltaY > 0 ? 'down' : 'up';
+ // Drag-Effekt beenden ohne Swap
+ this.endDragEffect();
+
+ // Reset drag state
+ this.dragStartIndex = null;
+ this.dragStartX = null;
+ this.dragStartY = null;
+ this.isDragging = false;
+ },
+
+ // Document-Level Event-Handler fĂŒr bessere Kontrolle
+ handleDocumentMouseUp(event) {
+ if (this.isDragging) {
+ this.endDrag(event);
}
},
+ handleDocumentTouchEnd(event) {
+ if (this.isDragging) {
+ this.endDrag(event);
+ }
+ },
+
+
+
+ // Neue Methode: Finde das Tile an einer bestimmten Bildschirm-Position
+ findTileAtPosition(clientX, clientY) {
+ // Durchsuche alle Tiles und finde das, das an der Position liegt
+ for (let i = 0; i < this.board.length; i++) {
+ if (this.board[i] && !this.board[i].isSpecial) {
+ const tileElement = document.querySelector(`[data-index="${i}"]`);
+ if (tileElement) {
+ const rect = tileElement.getBoundingClientRect();
+ if (clientX >= rect.left && clientX <= rect.right &&
+ clientY >= rect.top && clientY <= rect.bottom) {
+ return i;
+ }
+ }
+ }
+ }
+ return null;
+ },
+
+ // Neue Methode: PrĂŒfe ob zwei Tiles benachbart sind
+ areTilesAdjacent(index1, index2) {
+ if (index1 === null || index2 === null) return false;
+
+ const { row: row1, col: col1 } = this.indexToCoords(index1);
+ const { row: row2, col: col2 } = this.indexToCoords(index2);
+
+ // Tiles sind benachbart wenn sie sich in der gleichen Zeile oder Spalte befinden
+ // und nur 1 Position voneinander entfernt sind
+ const rowDiff = Math.abs(row1 - row2);
+ const colDiff = Math.abs(col1 - col2);
+
+ return (rowDiff === 1 && colDiff === 0) || (rowDiff === 0 && colDiff === 1);
+ },
+
getAdjacentIndex(index, direction) {
if (index === null) return null;
@@ -773,8 +867,8 @@ export default {
if (index1 !== null && index2 !== null && index3 !== null &&
this.isValidPosition(row, col) && this.isValidPosition(row, col + 1) && this.isValidPosition(row, col + 2) &&
board[index1] && board[index2] && board[index3] &&
- board[index1].type === board[index2].type &&
- board[index2].type === board[index3].type) {
+ this.canTilesMatch(board[index1], board[index2]) &&
+ this.canTilesMatch(board[index2], board[index3])) {
// Erweitere das Match nach links und rechts
let startCol = col;
@@ -783,7 +877,7 @@ export default {
while (startCol > 0) {
const leftIndex = this.coordsToIndex(row, startCol - 1);
if (leftIndex !== null && this.isValidPosition(row, startCol - 1) &&
- board[leftIndex] && board[leftIndex].type === board[index1].type) {
+ board[leftIndex] && this.canTilesMatch(board[leftIndex], board[index1])) {
startCol--;
} else {
break;
@@ -793,7 +887,7 @@ export default {
while (endCol < this.boardWidth - 1) {
const rightIndex = this.coordsToIndex(row, endCol + 1);
if (rightIndex !== null && this.isValidPosition(row, endCol + 1) &&
- board[rightIndex] && board[rightIndex].type === board[rightIndex].type) {
+ board[rightIndex] && this.canTilesMatch(board[rightIndex], board[index1])) {
endCol++;
} else {
break;
@@ -827,8 +921,8 @@ export default {
if (index1 !== null && index2 !== null && index3 !== null &&
this.isValidPosition(row, col) && this.isValidPosition(row + 1, col) && this.isValidPosition(row + 2, col) &&
board[index1] && board[index2] && board[index3] &&
- board[index1].type === board[index2].type &&
- board[index2].type === board[index3].type) {
+ this.canTilesMatch(board[index1], board[index2]) &&
+ this.canTilesMatch(board[index2], board[index3])) {
// Erweitere das Match nach oben und unten
let startRow = row;
@@ -837,7 +931,7 @@ export default {
while (startRow > 0) {
const upIndex = this.coordsToIndex(startRow - 1, col);
if (upIndex !== null && this.isValidPosition(startRow - 1, col) &&
- board[upIndex] && board[upIndex].type === board[index1].type) {
+ board[upIndex] && this.canTilesMatch(board[upIndex], board[index1])) {
startRow--;
} else {
break;
@@ -847,7 +941,7 @@ export default {
while (endRow < this.boardHeight - 1) {
const downIndex = this.coordsToIndex(endRow + 1, col);
if (downIndex !== null && this.isValidPosition(endRow + 1, col) &&
- board[downIndex] && board[downIndex].type === board[downIndex].type) {
+ board[downIndex] && this.canTilesMatch(board[downIndex], board[index1])) {
endRow++;
} else {
break;
@@ -870,9 +964,126 @@ export default {
}
}
+ // L-Form Matches finden
+ const lShapeMatches = this.findLShapesOnBoard(board);
+ matches.push(...lShapeMatches);
+
return matches;
},
+ // Neue Methode: Finde L-Form Matches auf dem Brett
+ findLShapesOnBoard(board) {
+ const lShapeMatches = [];
+
+ // Durchsuche das Brett nach L-Formen
+ for (let row = 0; row < this.boardHeight - 2; row++) {
+ for (let col = 0; col < this.boardWidth - 2; col++) {
+ // PrĂŒfe verschiedene L-Form Muster
+ const patterns = this.checkLShapePatterns(board, row, col);
+ lShapeMatches.push(...patterns);
+ }
+ }
+
+ return lShapeMatches;
+ },
+
+ // Hilfsmethode: PrĂŒfe L-Form Muster an einer bestimmten Position
+ checkLShapePatterns(board, startRow, startCol) {
+ const patterns = [];
+
+ // Pattern 1: Horizontale Linie nach rechts + vertikale Linie nach unten
+ // xxx
+ // x
+ // x
+ const horizontalRight = this.checkHorizontalLine(board, startRow, startCol, 3);
+ if (horizontalRight.length >= 3) {
+ const verticalDown = this.checkVerticalLine(board, startRow, startCol, 3);
+ if (verticalDown.length >= 2) {
+ const lShape = [...horizontalRight, ...verticalDown];
+ if (lShape.length >= 5) {
+ patterns.push(lShape);
+ }
+ }
+ }
+
+ // Pattern 2: Horizontale Linie nach rechts + vertikale Linie nach oben
+ // x
+ // x
+ // xxx
+ const horizontalRight2 = this.checkHorizontalLine(board, startRow + 2, startCol, 3);
+ if (horizontalRight2.length >= 3) {
+ const verticalUp = this.checkVerticalLine(board, startRow, startCol, 3);
+ if (verticalUp.length >= 2) {
+ const lShape = [...horizontalRight2, ...verticalUp];
+ if (lShape.length >= 5) {
+ patterns.push(lShape);
+ }
+ }
+ }
+
+ // Pattern 3: Horizontale Linie nach links + vertikale Linie nach unten
+ // xxx
+ // x
+ // x
+ const horizontalLeft = this.checkHorizontalLine(board, startRow, startCol - 2, 3);
+ if (horizontalLeft.length >= 3) {
+ const verticalDown2 = this.checkVerticalLine(board, startRow, startCol, 3);
+ if (verticalDown2.length >= 2) {
+ const lShape = [...horizontalLeft, ...verticalDown2];
+ if (lShape.length >= 5) {
+ patterns.push(lShape);
+ }
+ }
+ }
+
+ // Pattern 4: Horizontale Linie nach links + vertikale Linie nach oben
+ // x
+ // x
+ // xxx
+ const horizontalLeft2 = this.checkHorizontalLine(board, startRow + 2, startCol - 2, 3);
+ if (horizontalLeft2.length >= 3) {
+ const verticalUp2 = this.checkVerticalLine(board, startRow, startCol, 3);
+ if (verticalUp2.length >= 2) {
+ const lShape = [...horizontalLeft2, ...verticalUp2];
+ if (lShape.length >= 5) {
+ patterns.push(lShape);
+ }
+ }
+ }
+
+ return patterns;
+ },
+
+ // Hilfsmethode: PrĂŒfe horizontale Linie
+ checkHorizontalLine(board, row, startCol, minLength) {
+ const line = [];
+ for (let col = startCol; col < startCol + minLength && col < this.boardWidth; col++) {
+ const index = this.coordsToIndex(row, col);
+ if (index !== null && this.isValidPosition(row, col) && board[index] &&
+ (line.length === 0 || this.canTilesMatch(board[line[0]], board[index]))) {
+ line.push(index);
+ } else {
+ break;
+ }
+ }
+ return line;
+ },
+
+ // Hilfsmethode: PrĂŒfe vertikale Linie
+ checkVerticalLine(board, startRow, col, minLength) {
+ const line = [];
+ for (let row = startRow; row < startRow + minLength && row < this.boardHeight; row++) {
+ const index = this.coordsToIndex(row, col);
+ if (index !== null && this.isValidPosition(row, col) && board[index] &&
+ (line.length === 0 || this.canTilesMatch(board[line[0]], board[index]))) {
+ line.push(index);
+ } else {
+ break;
+ }
+ }
+ return line;
+ },
+
fixInitialMatches() {
let attempts = 0;
const maxAttempts = 50;
@@ -942,6 +1153,8 @@ export default {
// UrsprĂŒngliches Tile ausblenden
originalTile.style.opacity = '0.3';
+
+
},
updateDragEffect(event) {
@@ -963,11 +1176,16 @@ export default {
document.removeEventListener('mousemove', this.boundMouseMoveHandler);
this.boundMouseMoveHandler = null;
}
- if (this.boundTouchMoveHandler) {
+ if (this.boundTouchMoveHandler) {
document.removeEventListener('touchmove', this.boundTouchMoveHandler);
this.boundTouchMoveHandler = null;
}
+ // Document-Level Event-Handler entfernen
+ document.removeEventListener('mouseup', this.handleDocumentMouseUp, true);
+ document.removeEventListener('touchend', this.handleDocumentTouchEnd, true);
+
+
// Drag-Element entfernen
if (this.dragElement) {
document.body.removeChild(this.dragElement);
@@ -988,23 +1206,27 @@ export default {
if (this.isFalling) {
return;
}
-
-
// Tausche die Tiles
const temp = this.board[index1];
this.board[index1] = this.board[index2];
this.board[index2] = temp;
- // Erhöhe den Zug-ZÀhler
+ // WICHTIG: PrĂŒfe auf Regenbogen-Tile Tausch (vor normalen Matches)
+ if (this.handleRainbowSwap(temp, this.board[index1])) {
+ // Regenbogen-Tausch erfolgreich - Zug-ZÀhler wird bereits in handleRainbowSwap erhöht
+ return; // Beende hier, da Regenbogen-Tausch bereits verarbeitet wurde
+ }
+
+ // Erhöhe den Zug-ZĂ€hler nur fĂŒr normale Swaps
this.moves++;
this.movesLeft--;
- // PrĂŒfe auf Matches nach dem Swap
+ // PrĂŒfe auf normale Matches nach dem Swap
const matches = this.findMatchesOnBoard(this.board, false); // Reduziere Debug-Ausgaben
if (matches.length > 0) {
- this.handleMatches(matches);
+ this.handleMatches(matches, true); // true = Spieler-Zug
} else {
// Keine Matches - tausche zurĂŒck
const tempBack = this.board[index1];
@@ -1024,24 +1246,43 @@ export default {
}
},
- handleMatches(matches) {
+ handleMatches(matches, isPlayerMove = false) {
const matchedIndices = new Set();
+ const powerUpIndices = new Set(); // Sammle Power-Up Tiles separat
+
matches.forEach(match => {
- match.forEach(index => matchedIndices.add(index));
+ match.forEach(index => {
+ // PrĂŒfe ob es sich um ein Power-Up Tile handelt
+ if (this.isPowerUpTile(this.board[index])) {
+ powerUpIndices.add(index);
+ } else {
+ matchedIndices.add(index);
+ }
+ });
});
- // Matched tiles markieren
+ // Matched tiles markieren (nur normale Tiles)
this.matchedTiles = Array.from(matchedIndices);
- // Matches zÀhlen (jeder Match erhöht den ZÀhler)
- this.matchesMade += 1;
+ // Matches zĂ€hlen (nur bei Spieler-ZĂŒgen, nicht bei automatischen Kaskaden)
+ if (isPlayerMove) {
+ this.matchesMade += 1;
+ }
- // Punkte hinzufĂŒgen
+ // Punkte hinzufĂŒgen (nur fĂŒr normale Tiles)
const matchPoints = matchedIndices.size * 10 * this.currentLevel;
this.levelScore += matchPoints;
this.score += matchPoints;
- // Nach kurzer Verzögerung Fall-Animation starten
+ // WICHTIG: Erstelle spezielle Items basierend auf der Match-Anordnung
+ const powerUpPositions = this.createSpecialItems(matches);
+
+ // Entferne Power-Up Positionen aus den gematchten Indizes
+ powerUpPositions.forEach(pos => {
+ matchedIndices.delete(pos);
+ });
+
+ // Nach kurzer Verzögerung Fall-Animation starten (nur fĂŒr normale Tiles)
setTimeout(() => {
this.startFallAnimation(Array.from(matchedIndices));
}, 500);
@@ -1240,7 +1481,7 @@ export default {
if (newMatches.length > 0) {
// Starte neue Match-Verarbeitung
// Diese wird automatisch wieder checkForCascadeMatches aufrufen
- this.handleMatches(newMatches);
+ this.handleMatches(newMatches, false); // false = automatischer Match
} else {
// Wichtig: Auch ohne neue Matches Level-Objekte prĂŒfen
// (falls der Spieler bereits alle Ziele erreicht hat)
@@ -1272,8 +1513,16 @@ export default {
// PrĂŒfe ob an dieser Position ein Feld existiert
if (currentIndex !== null && this.isValidPosition(row, col)) {
// Suche nach dem nÀchsten freien Platz von unten
- while (targetRow >= 0 && !this.isValidPosition(targetRow, col)) {
- targetRow--;
+ while (targetRow >= 0) {
+ // WICHTIG: PrĂŒfe ob es sich um ein freies Feld (o) handelt
+ if (!this.isValidPosition(targetRow, col) ||
+ (this.boardLayout[targetRow] && this.boardLayout[targetRow][col] &&
+ this.boardLayout[targetRow][col].type === 'empty')) {
+ // Freies Feld oder ungĂŒltige Position - ĂŒberspringe es
+ targetRow--;
+ continue;
+ }
+ break;
}
if (targetRow >= 0) {
@@ -1305,6 +1554,12 @@ export default {
if (this.isValidPosition(row, col)) {
const index = this.coordsToIndex(row, col);
if (index !== null && !this.board[index]) {
+ // WICHTIG: PrĂŒfe ob es sich um ein freies Feld (o) handelt
+ if (this.boardLayout[row] && this.boardLayout[row][col] &&
+ this.boardLayout[row][col].type === 'empty') {
+ // Freies Feld - ĂŒberspringe es
+ continue;
+ }
firstEmptyRow = row;
break;
}
@@ -1317,6 +1572,13 @@ export default {
if (this.isValidPosition(row, col)) {
const index = this.coordsToIndex(row, col);
if (index !== null && !this.board[index]) {
+ // WICHTIG: PrĂŒfe ob es sich um ein freies Feld (o) handelt
+ if (this.boardLayout[row] && this.boardLayout[row][col] &&
+ this.boardLayout[row][col].type === 'empty') {
+ // Freies Feld - ĂŒberspringe es
+ continue;
+ }
+
// Erstelle ein neues Tile
const tileType = this.tileTypes[Math.floor(Math.random() * this.tileTypes.length)];
const newTile = {
@@ -1693,7 +1955,10 @@ export default {
circle: 'â',
square: 'đŠ',
crown: 'đ',
- rainbow: 'đ'
+ rainbow: 'đ',
+ // Spezielle Items
+ rocket: 'đ',
+ bomb: 'đŁ'
};
return symbols[type] || 'â';
},
@@ -1750,7 +2015,7 @@ export default {
// Kein Feld - null hinzufĂŒgen
board.push(null);
} else if (layoutCell.type === 'tile') {
- // Tile-Feld - zufÀlliger Tile-Typ
+ // Tile-Feld - zufÀlliger Tile-Typ (nur normale Tiles, keine Power-Ups)
const tileType = this.tileTypes[Math.floor(Math.random() * this.tileTypes.length)];
const tile = {
type: tileType,
@@ -1830,6 +2095,21 @@ export default {
return true;
},
+ // Hilfsmethode: PrĂŒfe ob ein Tile ein Power-Up ist
+ isPowerUpTile(tile) {
+ return tile && this.powerUpTypes.includes(tile.type);
+ },
+
+ // Hilfsmethode: PrĂŒfe ob zwei Tiles matchen können
+ canTilesMatch(tile1, tile2) {
+ // Power-Up Tiles können nicht mit anderen Tiles matchen (auch nicht mit sich selbst)
+ if (this.isPowerUpTile(tile1) || this.isPowerUpTile(tile2)) {
+ return false;
+ }
+ // Normale Tiles können nur mit dem gleichen Typ matchen
+ return tile1 && tile2 && tile1.type === tile2.type;
+ },
+
// Hilfsmethode: Zeige Fortschritt fĂŒr Level-Objekte
getObjectiveProgress(objective) {
switch (objective.type) {
@@ -1842,6 +2122,1069 @@ export default {
default:
return 'N/A';
}
+ },
+
+ // Neue Methode: Erstelle spezielle Items basierend auf der Match-Anordnung
+ createSpecialItems(matches) {
+ const powerUpPositions = new Set(); // Sammle Positionen, wo Power-Ups erstellt werden
+
+ matches.forEach(match => {
+ if (match.length >= 4) {
+ // Mindestens 4 Felder gematcht - prĂŒfe auf spezielle Anordnungen
+ const specialItem = this.detectSpecialItemPattern(match);
+ if (specialItem) {
+ // Erstelle das spezielle Item an der ersten Position des Matches
+ const centerIndex = match[Math.floor(match.length / 2)];
+ this.createSpecialItem(centerIndex, specialItem);
+ powerUpPositions.add(centerIndex);
+ }
+ }
+ });
+
+ // Entferne Power-Up Positionen aus den Matches, damit sie nicht als normale Matches behandelt werden
+ return powerUpPositions;
+ },
+
+ // Neue Methode: Erkenne spezielle Item-Muster
+ detectSpecialItemPattern(match) {
+ if (match.length < 4) return null;
+
+ // Konvertiere Match-Indizes zu Koordinaten
+ const coords = match.map(index => this.indexToCoords(index));
+
+ console.log('đ Pattern-Erkennung fĂŒr Match:', coords);
+ console.log('đ Anzahl Tiles:', coords.length);
+
+ // WICHTIG: PrĂŒfe zuerst die komplexesten Muster (höchste PrioritĂ€t)
+ // UND stelle sicher, dass nur EIN Pattern erkannt wird
+
+ // PrĂŒfe auf I-Form (Regenbogen) - höchste PrioritĂ€t, mindestens 5 Tiles
+ if (coords.length >= 5 && this.isIRainbow(coords)) {
+ console.log('â
Rainbow Pattern erkannt');
+ return 'rainbow';
+ }
+
+ // PrĂŒfe auf L-Form (Bombe) - zweite PrioritĂ€t, mindestens 5 Tiles
+ if (coords.length >= 5 && this.isLBomb(coords)) {
+ console.log('đŁ L-Bomb Pattern erkannt');
+ return 'bomb';
+ }
+
+ // PrĂŒfe auf T-Form (Bombe) - dritte PrioritĂ€t, mindestens 5 Tiles
+ if (coords.length >= 5 && this.isTBomb(coords)) {
+ console.log('đŁ T-Bomb Pattern erkannt');
+ return 'bomb';
+ }
+
+ // PrĂŒfe auf Kreuz-Form (Bombe) - vierte PrioritĂ€t, mindestens 5 Tiles
+ if (coords.length >= 5 && this.isCrossBomb(coords)) {
+ console.log('đŁ Cross-Bomb Pattern erkannt');
+ return 'bomb';
+ }
+
+ // PrĂŒfe auf horizontale Anordnung (Rakete) - niedrigste PrioritĂ€t, mindestens 4 Tiles
+ if (coords.length >= 4 && this.isHorizontalRocket(coords)) {
+ console.log('đ Horizontal Rocket Pattern erkannt');
+ return 'rocket';
+ }
+
+ // PrĂŒfe auf vertikale Anordnung (Rakete) - niedrigste PrioritĂ€t, mindestens 4 Tiles
+ if (coords.length >= 4 && this.isVerticalRocket(coords)) {
+ console.log('đ Vertical Rocket Pattern erkannt');
+ return 'rocket';
+ }
+
+ console.log('â Kein spezielles Pattern erkannt');
+ return null;
+ },
+
+ // Hilfsmethode: PrĂŒfe auf horizontale Rakete (4+ Felder in einer Reihe)
+ isHorizontalRocket(coords) {
+ if (coords.length < 4) return false;
+
+ // Alle Koordinaten mĂŒssen in der gleichen Zeile sein
+ const row = coords[0].row;
+ return coords.every(coord => coord.row === row);
+ },
+
+ // Hilfsmethode: PrĂŒfe auf vertikale Rakete (4+ Felder in einer Spalte)
+ isVerticalRocket(coords) {
+ if (coords.length < 4) return false;
+
+ // Alle Koordinaten mĂŒssen in der gleichen Spalte sein
+ const col = coords[0].col;
+ return coords.every(coord => coord.col === col);
+ },
+
+ // Hilfsmethode: PrĂŒfe auf L-Form Bombe
+ isLBomb(coords) {
+ // L-Form Bombe benötigt mindestens 5 Tiles
+ if (coords.length < 5) {
+ return false;
+ }
+
+ // FĂŒr 5+ Tiles: Suche nach L-Form mit langen Zeilen/Spalten
+ const rows = new Set(coords.map(c => c.row));
+ const cols = new Set(coords.map(c => c.col));
+
+ // L-Form: Mindestens 3 Zeilen UND 3 Spalten
+ if (rows.size >= 3 && cols.size >= 3) {
+ // PrĂŒfe ob es eine echte L-Form gibt
+ return this.hasLShape(coords);
+ }
+
+ return false;
+ },
+
+ // Hilfsmethode: PrĂŒfe auf T-Form Bombe
+ isTBomb(coords) {
+ if (coords.length < 5) return false; // Mindestens 5 Tiles fĂŒr T-Form Bombe
+
+ // Suche nach T-Form: 3+ horizontal + 3+ vertikal
+ const rows = new Set(coords.map(c => c.row));
+ const cols = new Set(coords.map(c => c.col));
+
+ // Mindestens 3 Zeilen und 3 Spalten
+ if (rows.size >= 3 && cols.size >= 3) {
+ // PrĂŒfe ob es eine T-Form gibt
+ return this.hasTShape(coords);
+ }
+
+ return false;
+ },
+
+ // Hilfsmethode: PrĂŒfe auf Kreuz-Form Bombe
+ isCrossBomb(coords) {
+ if (coords.length < 5) return false; // Mindestens 5 Tiles fĂŒr Kreuz-Form Bombe
+
+ // Suche nach Kreuz-Form: 3+ horizontal + 3+ vertikal
+ const rows = new Set(coords.map(c => c.row));
+ const cols = new Set(coords.map(c => c.col));
+
+ // Mindestens 3 Zeilen und 3 Spalten
+ if (rows.size >= 3 && cols.size >= 3) {
+ // PrĂŒfe ob es eine Kreuz-Form gibt
+ return this.hasCrossShape(coords);
+ }
+
+ return false;
+ },
+
+ // Hilfsmethode: PrĂŒfe auf I-Form Regenbogen (5+ Felder in einer Reihe/Spalte)
+ isIRainbow(coords) {
+ if (coords.length < 5) return false;
+
+ // Alle Koordinaten mĂŒssen in der gleichen Zeile ODER Spalte sein
+ const row = coords[0].row;
+ const col = coords[0].col;
+
+ return coords.every(coord => coord.row === row) ||
+ coords.every(coord => coord.col === col);
+ },
+
+
+
+ // Hilfsmethode: PrĂŒfe auf L-Form
+ hasLShape(coords) {
+ if (coords.length < 5) return false; // Mindestens 5 Tiles fĂŒr L-Form
+
+ // Gruppiere nach Zeilen und Spalten
+ const rowGroups = {};
+ const colGroups = {};
+
+ coords.forEach(coord => {
+ if (!rowGroups[coord.row]) rowGroups[coord.row] = [];
+ if (!colGroups[coord.col]) colGroups[coord.col] = [];
+ rowGroups[coord.row].push(coord);
+ colGroups[coord.col].push(coord);
+ });
+
+ // PrĂŒfe auf L-Form: Eine Zeile mit 3+ Tiles UND eine Spalte mit 3+ Tiles
+ // und sie mĂŒssen sich an einer Ecke treffen
+ const rows = Object.keys(rowGroups).map(r => parseInt(r));
+ const cols = Object.keys(colGroups).map(c => parseInt(c));
+
+ // Finde Zeilen mit mindestens 3 Tiles
+ const longRows = rows.filter(row => rowGroups[row].length >= 3);
+ // Finde Spalten mit mindestens 2 Tiles (fĂŒr L-Form reicht das)
+ const longCols = cols.filter(col => colGroups[col].length >= 2);
+
+ // PrĂŒfe ob eine lange Zeile und eine lange Spalte sich schneiden
+ for (const row of longRows) {
+ for (const col of longCols) {
+ // PrĂŒfe ob es ein Tile an der Kreuzung gibt
+ const intersection = coords.find(coord => coord.row === row && coord.col === col);
+ if (intersection) {
+ // PrĂŒfe ob es wirklich eine L-Form ist
+ const rowTiles = rowGroups[row];
+ const colTiles = colGroups[col];
+
+ // L-Form: horizontale Linie + vertikale Linie, die sich treffen
+ if (rowTiles.length >= 3 && colTiles.length >= 2) {
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+ },
+
+ // Hilfsmethode: PrĂŒfe auf T-Form
+ hasTShape(coords) {
+ if (coords.length < 5) return false; // Mindestens 5 Tiles fĂŒr T-Form
+
+ // Gruppiere nach Zeilen und Spalten
+ const rowGroups = {};
+ const colGroups = {};
+
+ coords.forEach(coord => {
+ if (!rowGroups[coord.row]) rowGroups[coord.row] = [];
+ if (!colGroups[coord.col]) colGroups[coord.col] = [];
+ rowGroups[coord.row].push(coord);
+ colGroups[coord.col].push(coord);
+ });
+
+ // Finde Zeilen und Spalten mit mindestens 3 Tiles
+ const longRows = Object.values(rowGroups).filter(group => group.length >= 3);
+ const longCols = Object.values(colGroups).filter(group => group.length >= 3);
+
+ // T-Form: mindestens eine lange Zeile UND eine lange Spalte
+ // ZusÀtzlich: mindestens ein Tile muss an der Kreuzung sein
+ if (longRows.length > 0 && longCols.length > 0) {
+ // PrĂŒfe ob es eine Kreuzung gibt
+ const intersection = coords.filter(coord =>
+ longRows.some(row => row.some(r => r.row === coord.row)) &&
+ longCols.some(col => col.some(c => c.col === coord.col))
+ );
+
+ return intersection.length > 0;
+ }
+ return false;
+ },
+
+ // Hilfsmethode: PrĂŒfe auf Kreuz-Form
+ hasCrossShape(coords) {
+ if (coords.length < 5) return false; // Mindestens 5 Tiles fĂŒr Kreuz-Form
+
+ // Gruppiere nach Zeilen und Spalten
+ const rowGroups = {};
+ const colGroups = {};
+
+ coords.forEach(coord => {
+ if (!rowGroups[coord.row]) rowGroups[coord.row] = [];
+ if (!colGroups[coord.col]) colGroups[coord.col] = [];
+ rowGroups[coord.row].push(coord);
+ colGroups[coord.col].push(coord);
+ });
+
+ // Finde Zeilen und Spalten mit mindestens 3 Tiles
+ const longRows = Object.values(rowGroups).filter(group => group.length >= 3);
+ const longCols = Object.values(colGroups).filter(group => group.length >= 3);
+
+ // Kreuz-Form: mindestens eine lange Zeile UND eine lange Spalte
+ // ZusÀtzlich: mindestens ein Tile muss an der Kreuzung sein
+ if (longRows.length > 0 && longCols.length > 0) {
+ // PrĂŒfe ob es eine Kreuzung gibt
+ const intersection = coords.filter(coord =>
+ longRows.some(row => row.some(r => r.row === coord.row)) &&
+ longCols.some(col => col.some(c => c.col === coord.col))
+ );
+
+ return intersection.length > 0;
+ }
+ return false;
+ },
+
+ // Neue Methode: Erstelle ein spezielles Item
+ createSpecialItem(index, itemType) {
+ if (!this.board[index]) return;
+
+ // Erstelle das spezielle Item
+ const specialItem = {
+ type: itemType,
+ id: Date.now() + index + Math.random(),
+ row: this.indexToCoords(index).row,
+ col: this.indexToCoords(index).col,
+ isSpecial: true,
+ specialType: itemType
+ };
+
+ // Ersetze das normale Tile mit dem speziellen Item
+ this.board[index] = specialItem;
+
+ // Zeige eine visuelle BestÀtigung
+ this.showSpecialItemCreated(index, itemType);
+
+ console.log(`đŻ Spezielles Item erstellt: ${itemType} an Position ${index}`);
+ },
+
+ // Neue Methode: Zeige visuelle BestĂ€tigung fĂŒr erstelltes spezielles Item
+ showSpecialItemCreated(index, itemType) {
+ const tileElement = document.querySelector(`[data-index="${index}"]`);
+ if (tileElement) {
+ // FĂŒge eine spezielle Klasse hinzu
+ tileElement.classList.add('special-item-created');
+
+ // Zeige eine kurze Animation
+ setTimeout(() => {
+ tileElement.classList.remove('special-item-created');
+ }, 1000);
+ }
+ },
+
+ // Neue Methode: ZĂ€hle Power-Up als Zug
+ countPowerUpMove() {
+ this.moves++;
+ this.movesLeft--;
+ console.log('đą Power-Up als Zug gezĂ€hlt:', 'moves:', this.moves, 'movesLeft:', this.movesLeft);
+ },
+
+ // Neue Methode: Behandle Doppelklick auf Power-Up Tiles
+ handleDoubleClick(index, event) {
+ event.preventDefault();
+ event.stopPropagation();
+
+ const tile = this.board[index];
+ if (!tile) return;
+
+ if (tile.isSpecial) {
+ // Power-Up als Zug zÀhlen
+ this.countPowerUpMove();
+
+ if (tile.type === 'rainbow') {
+ // Regenbogen-Tile: ZufÀlligen Tile-Typ entfernen
+ const randomTileType = this.tileTypes[Math.floor(Math.random() * this.tileTypes.length)];
+ this.removeAllTilesOfType(randomTileType);
+ console.log(`đ Regenbogen-Doppelklick: Alle ${randomTileType} Tiles entfernt`);
+ } else if (tile.type === 'bomb') {
+ // Bomben-Tile: 9 Tiles rundherum entfernen
+ this.explodeBomb(index, 1); // 1 Ring = 3x3 Bereich
+ console.log(`đŁ Bomben-Doppelklick: 9 Tiles rundherum entfernt`);
+ } else if (tile.type === 'rocket') {
+ // Raketen-Tile: 4 Nachbarfelder löschen und Rakete starten
+ this.handleRocketDoubleClick(index);
+ console.log(`đ Raketen-Doppelklick: 4 Nachbarfelder gelöscht und Rakete gestartet`);
+ }
+ }
+ },
+
+ // Neue Methode: Entferne alle Tiles eines bestimmten Typs
+ removeAllTilesOfType(tileType) {
+ const tilesToRemove = [];
+
+ // Sammle alle Indizes der zu entfernenden Tiles
+ for (let i = 0; i < this.board.length; i++) {
+ if (this.board[i] && this.board[i].type === tileType) {
+ tilesToRemove.push(i);
+ }
+ }
+
+ if (tilesToRemove.length > 0) {
+ // Zeige Regenbogen-Effekt-Animation fĂŒr das erste Tile
+ if (tilesToRemove.length > 0) {
+ this.showRainbowEffectAnimation(tilesToRemove[0]);
+ }
+
+ // Entferne die Tiles
+ tilesToRemove.forEach(index => {
+ this.board[index] = null;
+ });
+
+ // Starte Fall-Animation
+ this.startFallAnimation(tilesToRemove);
+
+ // Punkte hinzufĂŒgen
+ const points = tilesToRemove.length * 20 * this.currentLevel;
+ this.levelScore += points;
+ this.score += points;
+ }
+ },
+
+ // Neue Methode: Behandle Power-Up Tile Tausch
+ handleRainbowSwap(originalTile1, originalTile2) {
+ // Power-Up Tausch als Zug zĂ€hlen (wird auch ĂŒber swapTiles aufgerufen)
+ this.countPowerUpMove();
+
+ if (originalTile1.type === 'rainbow' && originalTile2.type === 'rainbow') {
+ // Zwei Regenbogen-Tiles getauscht: Entferne alle Tiles
+ this.removeAllTilesFromBoard();
+ return true;
+ } else if (originalTile1.type === 'rainbow' || originalTile2.type === 'rainbow') {
+ // Ein Regenbogen-Tile mit normalem Tile getauscht: Entferne alle Tiles des normalen Typs
+ const normalTile = originalTile1.type === 'rainbow' ? originalTile2 : originalTile1;
+ this.removeAllTilesOfType(normalTile.type);
+ return true;
+ } else if (originalTile1.type === 'bomb' && originalTile2.type === 'bomb') {
+ // Zwei Bomben-Tiles getauscht: Entferne 2 Ringe (5x5 Bereich)
+ const bombIndex = this.findBombIndex(originalTile1, originalTile2);
+ if (bombIndex !== null) {
+ this.explodeBomb(bombIndex, 2); // 2 Ringe = 5x5 Bereich
+ }
+ return true;
+ } else if (originalTile1.type === 'bomb' || originalTile2.type === 'bomb') {
+ // 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;
+
+ // Finde die neue Position der Bombe
+ const newBombIndex = this.findTileIndex(bombTile);
+ if (newBombIndex !== null) {
+ this.explodeBomb(newBombIndex, 1); // 1 Ring = 3x3 Bereich
+ }
+ return true;
+ } else if ((originalTile1.type === 'bomb' && originalTile2.type === 'rainbow') ||
+ (originalTile1.type === 'rainbow' && originalTile2.type === 'bomb')) {
+ // Bombe + Regenbogen: Erstelle zufÀllige Bomben und löse sie aus
+ this.createRandomBombs(20);
+ setTimeout(() => {
+ this.detonateAllBombs();
+ }, 100); // 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')) {
+ // Rakete + Regenbogen: Erstelle zufÀllige Raketen und starte sie
+ this.createRandomRockets(10);
+ setTimeout(() => {
+ this.launchAllRockets();
+ }, 100); // 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') {
+ // 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;
+ this.handleRocketLaunch(rocketTile, targetTile);
+ return true;
+ }
+ return false; // Kein Power-Up Tausch
+ },
+
+ // Hilfsmethode: Finde den Index einer Bombe nach dem Tausch
+ findBombIndex(originalBomb1, originalBomb2) {
+ // Suche nach der Bombe auf dem Board
+ for (let i = 0; i < this.board.length; i++) {
+ if (this.board[i] && this.board[i].type === 'bomb') {
+ return i;
+ }
+ }
+ return null;
+ },
+
+ // Hilfsmethode: Finde den Index eines Tiles nach dem Tausch
+ 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) {
+ return i;
+ }
+ }
+ return null;
+ },
+
+ // Neue Methode: Entferne alle Tiles vom Board
+ removeAllTilesFromBoard() {
+ const allTileIndices = [];
+
+ // Sammle alle Tile-Indizes
+ for (let i = 0; i < this.board.length; i++) {
+ if (this.board[i]) {
+ allTileIndices.push(i);
+ }
+ }
+
+ if (allTileIndices.length > 0) {
+ // Zeige Regenbogen-Effekt-Animation in der Mitte des Boards
+ const centerIndex = Math.floor(this.board.length / 2);
+ this.showRainbowEffectAnimation(centerIndex);
+
+ // Entferne alle Tiles
+ allTileIndices.forEach(index => {
+ this.board[index] = null;
+ });
+
+ // Starte Fall-Animation
+ this.startFallAnimation(allTileIndices);
+
+ // Punkte hinzufĂŒgen
+ const points = allTileIndices.length * 30 * this.currentLevel;
+ this.levelScore += points;
+ this.score += points;
+
+ console.log(`đ Alle Tiles vom Board entfernt!`);
+ }
+ },
+
+ // Neue Methode: Explodiere Bombe mit bestimmter Reichweite
+ explodeBomb(centerIndex, rings) {
+ const { row: centerRow, col: centerCol } = this.indexToCoords(centerIndex);
+ const tilesToRemove = [];
+
+ // Sammle alle Tiles in den angegebenen Ringen
+ for (let ring = 0; ring <= rings; ring++) {
+ for (let r = centerRow - ring; r <= centerRow + ring; r++) {
+ for (let c = centerCol - ring; c <= centerCol + ring; c++) {
+ if (r >= 0 && r < this.boardHeight && c >= 0 && c < this.boardWidth) {
+ const index = this.coordsToIndex(r, c);
+ if (index !== null && this.board[index]) {
+ tilesToRemove.push(index);
+ }
+ }
+ }
+ }
+ }
+
+ if (tilesToRemove.length > 0) {
+ // Zeige Explosions-Animation
+ this.showExplosionAnimation(centerIndex);
+
+ // Entferne die Tiles
+ tilesToRemove.forEach(index => {
+ this.board[index] = null;
+ });
+
+ // Starte Fall-Animation
+ this.startFallAnimation(tilesToRemove);
+
+ // Punkte hinzufĂŒgen
+ const points = tilesToRemove.length * 25 * this.currentLevel;
+ this.levelScore += points;
+ this.score += points;
+
+ console.log(`đŁ Bombe explodiert: ${tilesToRemove.length} Tiles entfernt`);
+ }
+ },
+
+ // Neue Methode: Erstelle zufÀllige Bomben auf 20% der Tiles
+ createRandomBombs(percentage = 20) {
+ const totalTiles = this.board.filter(tile => tile !== null).length;
+ const bombCount = Math.floor(totalTiles * percentage / 100);
+ const bombIndices = [];
+
+ // Sammle alle verfĂŒgbaren Tile-Indizes
+ const availableIndices = [];
+ for (let i = 0; i < this.board.length; i++) {
+ if (this.board[i] && this.board[i].type !== 'bomb') {
+ availableIndices.push(i);
+ }
+ }
+
+ // WÀhle zufÀllige Tiles aus und verwandle sie in Bomben
+ for (let i = 0; i < Math.min(bombCount, availableIndices.length); i++) {
+ const randomIndex = Math.floor(Math.random() * availableIndices.length);
+ const tileIndex = availableIndices.splice(randomIndex, 1)[0];
+
+ // Verwandle das Tile in eine Bombe
+ this.board[tileIndex] = {
+ type: 'bomb',
+ id: Date.now() + tileIndex + Math.random(),
+ row: this.indexToCoords(tileIndex).row,
+ col: this.indexToCoords(tileIndex).col,
+ isSpecial: true,
+ specialType: 'bomb'
+ };
+
+ bombIndices.push(tileIndex);
+ }
+
+ console.log(`đŁ ${bombIndices.length} zufĂ€llige Bomben erstellt`);
+ return bombIndices;
+ },
+
+ // Neue Methode: Löse alle Bomben auf dem Feld aus
+ detonateAllBombs() {
+ const bombIndices = [];
+
+ // Sammle alle Bomben-Indizes
+ for (let i = 0; i < this.board.length; i++) {
+ if (this.board[i] && this.board[i].type === 'bomb') {
+ bombIndices.push(i);
+ }
+ }
+
+ // Löse alle Bomben aus
+ bombIndices.forEach(index => {
+ this.explodeBomb(index, 1);
+ });
+
+ console.log(`đŁ Alle ${bombIndices.length} Bomben wurden ausgelöst!`);
+ },
+
+ // Neue Methode: Erstelle zufÀllige Raketen
+ createRandomRockets(percentage = 10) {
+ const totalTiles = this.board.filter(tile => tile !== null).length;
+ const rocketCount = Math.floor(totalTiles * percentage / 100);
+ const availableIndices = [];
+
+ // Sammle alle verfĂŒgbaren Positionen (keine Power-Ups)
+ for (let i = 0; i < this.board.length; i++) {
+ if (this.board[i] && !this.isPowerUpTile(this.board[i])) {
+ availableIndices.push(i);
+ }
+ }
+
+ // WĂ€hle zufĂ€llige Positionen fĂŒr Raketen
+ for (let i = 0; i < Math.min(rocketCount, availableIndices.length); i++) {
+ const randomIndex = Math.floor(Math.random() * availableIndices.length);
+ const boardIndex = availableIndices.splice(randomIndex, 1)[0];
+
+ // Ersetze das Tile durch eine Rakete
+ this.board[boardIndex] = {
+ id: Math.random().toString(36).substr(2, 9),
+ type: 'rocket',
+ isSpecial: true
+ };
+ }
+
+ console.log(`đ ${Math.min(rocketCount, availableIndices.length)} zufĂ€llige Raketen erstellt`);
+ },
+
+ // Neue Methode: Starte alle Raketen
+ launchAllRockets() {
+ const rocketIndices = [];
+
+ // Finde alle Raketen auf dem Board
+ for (let i = 0; i < this.board.length; i++) {
+ if (this.board[i] && this.board[i].type === 'rocket') {
+ rocketIndices.push(i);
+ }
+ }
+
+ // Starte jede Rakete
+ rocketIndices.forEach(rocketIndex => {
+ this.handleRocketDoubleClick(rocketIndex);
+ });
+
+ console.log(`đ Alle ${rocketIndices.length} Raketen wurden gestartet!`);
+ },
+
+ // 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 bombTile = originalTile1.type === 'bomb' ? originalTile1 : originalTile2;
+
+ // Finde die Position auf dem Board
+ const rocketIndex = this.findTileIndex(rocketTile);
+ if (rocketIndex !== null) {
+ // Lösche die 4 Nachbarfelder der Rakete
+ const { row: rocketRow, col: rocketCol } = this.indexToCoords(rocketIndex);
+ const tilesToRemove = [];
+
+ // Sammle die 4 Nachbarfelder (oben, unten, links, rechts)
+ const directions = [
+ { row: rocketRow - 1, col: rocketCol }, // Oben
+ { row: rocketRow + 1, col: rocketCol }, // Unten
+ { row: rocketRow, col: rocketCol - 1 }, // Links
+ { row: rocketRow, col: rocketCol + 1 } // Rechts
+ ];
+
+ directions.forEach(({ row, col }) => {
+ if (row >= 0 && row < this.boardHeight && col >= 0 && col < this.boardWidth) {
+ const index = this.coordsToIndex(row, col);
+ if (this.board[index]) {
+ tilesToRemove.push(index);
+ }
+ }
+ });
+
+ // Entferne die Nachbarfelder
+ if (tilesToRemove.length > 0) {
+ tilesToRemove.forEach(index => {
+ this.board[index] = null;
+ });
+
+ // Starte Fall-Animation
+ this.startFallAnimation(tilesToRemove);
+
+ // Punkte hinzufĂŒgen
+ const points = tilesToRemove.length * 25 * this.currentLevel;
+ this.levelScore += points;
+ this.score += points;
+ }
+ }
+
+ // Starte die Bomben-Rakete nach kurzer Verzögerung
+ setTimeout(() => {
+ this.launchBombRocket();
+ }, 200);
+
+ console.log(`đđŁ Rakete + Bombe Kombination: 4 Nachbarfelder gelöscht, Bomben-Rakete gestartet`);
+ },
+
+ // Neue Methode: Starte eine Bomben-Rakete
+ launchBombRocket() {
+ // Finde alle verfĂŒgbaren Positionen fĂŒr die Landung
+ const availableIndices = [];
+ for (let i = 0; i < this.board.length; i++) {
+ if (this.board[i]) {
+ availableIndices.push(i);
+ }
+ }
+
+ if (availableIndices.length > 0) {
+ // WÀhle zufÀllige Landungsposition
+ const randomIndex = Math.floor(Math.random() * availableIndices.length);
+ const landingIndex = availableIndices[randomIndex];
+
+ // Explodiere am Landungsort (9 Felder = 1 Ring)
+ this.explodeBomb(landingIndex, 1);
+
+ console.log(`đđŁ Bomben-Rakete landete bei Index ${landingIndex} und explodierte!`);
+ }
+ },
+
+ // Neue Methode: Zeige Explosions-Animation
+ showExplosionAnimation(centerIndex) {
+ // Warte bis das DOM gerendert ist
+ this.$nextTick(() => {
+ const tileElement = document.querySelector(`[data-index="${centerIndex}"]`);
+ const gameBoard = document.querySelector('.game-board-container');
+
+ if (tileElement && gameBoard) {
+ const tileRect = tileElement.getBoundingClientRect();
+ const boardRect = gameBoard.getBoundingClientRect();
+
+ // Berechne relative Position zum Game-Board-Container
+ this.explosionPosition = {
+ x: tileRect.left - boardRect.left + tileRect.width / 2 - 40,
+ y: tileRect.top - boardRect.top + tileRect.height / 2 - 40
+ };
+
+ this.showExplosion = true;
+
+ // Verstecke Animation nach der Dauer
+ setTimeout(() => {
+ this.showExplosion = false;
+ }, 800);
+ }
+ });
+ },
+
+ // Neue Methode: Zeige Raketen-Flug-Animation
+ showRocketFlightAnimation(startIndex, endIndex) {
+ // Warte bis das DOM gerendert ist
+ this.$nextTick(() => {
+ const startElement = document.querySelector(`[data-index="${startIndex}"]`);
+ const endElement = document.querySelector(`[data-index="${endIndex}"]`);
+ const gameBoard = document.querySelector('.game-board-container');
+
+ if (startElement && endElement && gameBoard) {
+ const startRect = startElement.getBoundingClientRect();
+ const endRect = endElement.getBoundingClientRect();
+ const boardRect = gameBoard.getBoundingClientRect();
+
+ this.rocketStartPos = {
+ x: startRect.left - boardRect.left + startRect.width / 2 - 20,
+ y: startRect.top - boardRect.top + startRect.height / 2 - 20
+ };
+
+ this.rocketEndPos = {
+ x: endRect.left - boardRect.left + endRect.width / 2 - 20,
+ y: endRect.top - boardRect.top + endRect.height / 2 - 20
+ };
+
+ this.showRocketFlight = true;
+
+ // Verstecke Animation nach der Dauer
+ setTimeout(() => {
+ this.showRocketFlight = false;
+ }, 1200);
+ }
+ });
+ },
+
+ // Neue Methode: Zeige Regenbogen-Effekt-Animation
+ showRainbowEffectAnimation(centerIndex) {
+ // Warte bis das DOM gerendert ist
+ this.$nextTick(() => {
+ const tileElement = document.querySelector(`[data-index="${centerIndex}"]`);
+ const gameBoard = document.querySelector('.game-board-container');
+
+ if (tileElement && gameBoard) {
+ const tileRect = tileElement.getBoundingClientRect();
+ const boardRect = gameBoard.getBoundingClientRect();
+
+ this.rainbowCenter = {
+ x: tileRect.left - boardRect.left + tileRect.width / 2 - 50,
+ y: tileRect.top - boardRect.top + tileRect.height / 2 - 50
+ };
+
+ this.showRainbowEffect = true;
+
+ // Verstecke Animation nach der Dauer
+ setTimeout(() => {
+ this.showRainbowEffect = false;
+ }, 1500);
+ }
+ });
+ },
+
+ // Neue Methode: Zeige Bomben-Effekt-Animation
+ showBombEffectAnimation(centerIndex) {
+ // Warte bis das DOM gerendert ist
+ this.$nextTick(() => {
+ const tileElement = document.querySelector(`[data-index="${centerIndex}"]`);
+ const gameBoard = document.querySelector('.game-board-container');
+
+ if (tileElement && gameBoard) {
+ const tileRect = tileElement.getBoundingClientRect();
+ const boardRect = gameBoard.getBoundingClientRect();
+
+ this.bombCenter = {
+ x: tileRect.left - boardRect.left + tileRect.width / 2 - 30,
+ y: tileRect.top - boardRect.top + tileRect.height / 2 - 30
+ };
+
+ this.showBombEffect = true;
+
+ // Verstecke Animation nach der Dauer
+ setTimeout(() => {
+ this.showBombEffect = false;
+ }, 300);
+ }
+ });
+ },
+
+ // Neue Methode: Behandle Doppelklick auf Rakete
+ handleRocketDoubleClick(rocketIndex) {
+ const { row: rocketRow, col: rocketCol } = this.indexToCoords(rocketIndex);
+ const tilesToRemove = [];
+
+ // Sammle die 4 Nachbarfelder (oben, unten, links, rechts)
+ const directions = [
+ { row: rocketRow - 1, col: rocketCol }, // Oben
+ { row: rocketRow + 1, col: rocketCol }, // Unten
+ { row: rocketRow, col: rocketCol - 1 }, // Links
+ { row: rocketRow, col: rocketCol + 1 } // Rechts
+ ];
+
+ directions.forEach(({ row, col }) => {
+ if (row >= 0 && row < this.boardHeight && col >= 0 && col < this.boardWidth) {
+ const index = this.coordsToIndex(row, col);
+ if (index !== null && this.board[index]) {
+ tilesToRemove.push(index);
+ }
+ }
+ });
+
+ if (tilesToRemove.length > 0) {
+ // Zeige Bomben-Effekt-Animation fĂŒr die Rakete
+ this.showBombEffectAnimation(rocketIndex);
+
+ // Entferne die Nachbar-Tiles
+ tilesToRemove.forEach(index => {
+ this.board[index] = null;
+ });
+
+ // Starte Fall-Animation
+ this.startFallAnimation(tilesToRemove);
+
+ // Punkte hinzufĂŒgen
+ const points = tilesToRemove.length * 15 * this.currentLevel;
+ this.levelScore += points;
+ this.score += points;
+
+ // Starte Rakete auf zufÀlliges Feld
+ this.launchRocketToRandomField(rocketIndex);
+ }
+ },
+
+ // Neue Methode: Rakete auf zufÀlliges Feld starten
+ launchRocketToRandomField(rocketIndex) {
+ // Finde alle verfĂŒgbaren Felder (auĂer der Rakete selbst)
+ const availableFields = [];
+ for (let i = 0; i < this.board.length; i++) {
+ if (i !== rocketIndex && this.board[i] && this.isValidPosition(this.indexToCoords(i).row, this.indexToCoords(i).col)) {
+ availableFields.push(i);
+ }
+ }
+
+ if (availableFields.length > 0) {
+ // WÀhle ein zufÀlliges Feld aus
+ const randomIndex = availableFields[Math.floor(Math.random() * availableFields.length)];
+
+ // Zeige visuellen Effekt auf dem Ziel-Tile (kurzer Farbwechsel)
+ this.showRocketTargetEffect(randomIndex);
+
+ // Kurze Verzögerung, damit der Effekt sichtbar ist
+ setTimeout(() => {
+ // Entferne die Rakete vom ursprĂŒnglichen Feld
+ this.board[rocketIndex] = null;
+
+ // Lösche das zufÀllige Feld
+ this.board[randomIndex] = null;
+
+ // Zeige Raketen-Flug-Animation
+ this.showRocketFlightAnimation(rocketIndex, randomIndex);
+
+ // Starte Fall-Animation fĂŒr beide Felder
+ this.startFallAnimation([rocketIndex, randomIndex]);
+ }, 200); // 200ms Verzögerung fĂŒr den visuellen Effekt
+
+ // Punkte hinzufĂŒgen
+ const points = 20 * this.currentLevel;
+ this.levelScore += points;
+ this.score += points;
+
+ console.log(`đ Rakete gestartet und auf Feld ${randomIndex} gelandet!`);
+ }
+ },
+
+ // Neue Methode: Zeige visuellen Effekt auf dem Raketen-Ziel
+ showRocketTargetEffect(targetIndex) {
+ const targetTile = document.querySelector(`[data-index="${targetIndex}"]`);
+ if (targetTile) {
+ // FĂŒge eine CSS-Klasse fĂŒr den Raketen-Treffer-Effekt hinzu
+ targetTile.classList.add('rocket-target-hit');
+
+ // Entferne die Klasse nach der Animation
+ setTimeout(() => {
+ targetTile.classList.remove('rocket-target-hit');
+ }, 300);
+ }
+ },
+
+ // Neue Methode: Behandle Verbindung zweier Raketen
+ handleRocketConnection(rocket1, rocket2) {
+ const rocket1Index = this.findTileIndex(rocket1);
+ const rocket2Index = this.findTileIndex(rocket2);
+
+ if (rocket1Index === null || rocket2Index === null) return;
+
+ const tilesToRemove = [];
+
+ // Sammle Nachbarfelder beider Raketen
+ [rocket1Index, rocket2Index].forEach(rocketIndex => {
+ const { row: rocketRow, col: rocketCol } = this.indexToCoords(rocketIndex);
+
+ const directions = [
+ { row: rocketRow - 1, col: rocketCol }, // Oben
+ { row: rocketRow + 1, col: rocketCol }, // Unten
+ { row: rocketRow, col: rocketCol - 1 }, // Links
+ { row: rocketRow, col: rocketCol + 1 } // Rechts
+ ];
+
+ directions.forEach(({ row, col }) => {
+ if (row >= 0 && row < this.boardHeight && col >= 0 && col < this.boardWidth) {
+ const index = this.coordsToIndex(row, col);
+ if (index !== null && this.board[index]) {
+ tilesToRemove.push(index);
+ }
+ }
+ });
+ });
+
+ if (tilesToRemove.length > 0) {
+ // Entferne die Nachbar-Tiles
+ tilesToRemove.forEach(index => {
+ this.board[index] = null;
+ });
+
+ // Starte Fall-Animation
+ this.startFallAnimation(tilesToRemove);
+
+ // Punkte hinzufĂŒgen
+ const points = tilesToRemove.length * 20 * this.currentLevel;
+ this.levelScore += points;
+ this.score += points;
+
+ // Starte 3 Raketen auf zufÀllige Felder
+ this.launchThreeRockets([rocket1Index, rocket2Index]);
+ }
+ },
+
+ // Neue Methode: Starte 3 Raketen auf zufÀllige Felder
+ launchThreeRockets(rocketIndices) {
+ // Entferne die ursprĂŒnglichen Raketen
+ rocketIndices.forEach(index => {
+ this.board[index] = null;
+ });
+
+ // Finde alle verfĂŒgbaren Felder
+ const availableFields = [];
+ for (let i = 0; i < this.board.length; i++) {
+ if (!rocketIndices.includes(i) && this.board[i] && this.isValidPosition(this.indexToCoords(i).row, this.indexToCoords(i).col)) {
+ availableFields.push(i);
+ }
+ }
+
+ if (availableFields.length > 0) {
+ // WÀhle 3 zufÀllige Felder aus
+ const selectedFields = [];
+ for (let i = 0; i < Math.min(3, availableFields.length); i++) {
+ const randomIndex = Math.floor(Math.random() * availableFields.length);
+ const fieldIndex = availableFields.splice(randomIndex, 1)[0];
+ selectedFields.push(fieldIndex);
+ }
+
+ // Lösche die ausgewÀhlten Felder
+ selectedFields.forEach(index => {
+ this.board[index] = null;
+ });
+
+ // Starte Fall-Animation fĂŒr alle betroffenen Felder
+ this.startFallAnimation([...rocketIndices, ...selectedFields]);
+
+ // Punkte hinzufĂŒgen
+ const points = (rocketIndices.length + selectedFields.length) * 25 * this.currentLevel;
+ this.levelScore += points;
+ this.score += points;
+
+ console.log(`đđđ 3 Raketen gestartet und auf Felder ${selectedFields.join(', ')} gelandet!`);
+ }
+ },
+
+ // Neue Methode: Rakete mit normalem Tile tauschen
+ handleRocketLaunch(rocketTile, targetTile) {
+ const targetIndex = this.findTileIndex(targetTile);
+ if (targetIndex === null) return;
+
+ const { row: targetRow, col: targetCol } = this.indexToCoords(targetIndex);
+ const tilesToRemove = [];
+
+ // Sammle die 4 Nachbarfelder des Ziel-Tiles
+ const directions = [
+ { row: targetRow - 1, col: targetCol }, // Oben
+ { row: targetRow + 1, col: targetCol }, // Unten
+ { row: targetRow, col: targetCol - 1 }, // Links
+ { row: targetRow, col: targetCol + 1 } // Rechts
+ ];
+
+ directions.forEach(({ row, col }) => {
+ if (row >= 0 && row < this.boardHeight && col >= 0 && col < this.boardWidth) {
+ const index = this.coordsToIndex(row, col);
+ if (index !== null && this.board[index]) {
+ tilesToRemove.push(index);
+ }
+ }
+ });
+
+ if (tilesToRemove.length > 0) {
+ // Entferne die Nachbar-Tiles
+ tilesToRemove.forEach(index => {
+ this.board[index] = null;
+ });
+
+ // Starte Fall-Animation
+ this.startFallAnimation(tilesToRemove);
+
+ // Punkte hinzufĂŒgen
+ const points = tilesToRemove.length * 15 * this.currentLevel;
+ this.levelScore += points;
+ this.score += points;
+
+ // Starte Rakete auf zufÀlliges Feld
+ this.launchRocketToRandomField(targetIndex);
+ }
}
},
computed: {
@@ -2122,6 +3465,7 @@ export default {
border: 1px solid #ddd;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
margin-bottom: 20px;
+ position: relative; /* FĂŒr absolute Positionierung der Animationen */
}
.game-board {
@@ -2134,8 +3478,8 @@ export default {
}
.game-tile {
- width: 50px;
- height: 50px;
+ width: 30px;
+ height: 30px;
background: linear-gradient(135deg, #eeeded, #f1eeee);
border: 2px solid #c0c0c0;
border-radius: 8px;
@@ -2147,16 +3491,30 @@ export default {
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
pointer-events: auto;
user-select: none;
+ position: relative;
}
+/* VergröĂere den klickbaren Bereich fĂŒr besseres Drag&Drop */
+.game-tile::before {
+ content: '';
+ position: absolute;
+ top: -5px;
+ left: -5px;
+ right: -5px;
+ bottom: -5px;
+ z-index: -1;
+}
+
+
+
.game-tile.empty {
background: transparent;
border: 2px solid transparent;
box-shadow: none;
pointer-events: none;
/* Wichtig: Behalte die gleiche GröĂe wie normale Tiles */
- width: 50px;
- height: 50px;
+ width: 30px;
+ height: 30px;
/* Optional: Zeige einen subtilen Rahmen fĂŒr bessere Sichtbarkeit */
background: rgba(0, 0, 0, 0.05);
}
@@ -2183,6 +3541,18 @@ export default {
z-index: 200;
}
+/* Spezielle Items */
+.game-tile.special-item-created {
+ animation: specialItemCreated 1s ease-in-out;
+ box-shadow: 0 0 20px rgba(255, 215, 0, 0.8);
+}
+
+@keyframes specialItemCreated {
+ 0% { transform: scale(1); }
+ 50% { transform: scale(1.3) rotate(10deg); }
+ 100% { transform: scale(1); }
+}
+
/* Fall-Animation */
.game-tile.falling {
transition: transform 0.6s ease-in;
@@ -2216,12 +3586,12 @@ export default {
}
.drag-tile .tile-icon {
- font-size: 24px;
+ font-size: 18px;
user-select: none;
}
.tile-icon {
- font-size: 24px;
+ font-size: 18px;
user-select: none;
}
@@ -2274,6 +3644,135 @@ export default {
100% { transform: scale(1); }
}
+/* Power-Up Animationen */
+@keyframes explosion {
+ 0% {
+ transform: scale(0.1);
+ opacity: 1;
+ }
+ 50% {
+ transform: scale(1.5);
+ opacity: 0.8;
+ }
+ 100% {
+ transform: scale(2.5);
+ opacity: 0;
+ }
+}
+
+@keyframes rocketFlight {
+ 0% {
+ transform: translate(0, 0) rotate(0deg);
+ opacity: 1;
+ }
+ 50% {
+ transform: translate(var(--dx), var(--dy)) rotate(45deg);
+ opacity: 0.8;
+ }
+ 100% {
+ transform: translate(calc(var(--dx) * 2), calc(var(--dy) * 2)) rotate(90deg);
+ opacity: 0;
+ }
+}
+
+@keyframes rainbowPulse {
+ 0% {
+ transform: scale(1) rotate(0deg);
+ filter: hue-rotate(0deg);
+ }
+ 25% {
+ transform: scale(1.2) rotate(90deg);
+ filter: hue-rotate(90deg);
+ }
+ 50% {
+ transform: scale(1.4) rotate(180deg);
+ filter: hue-rotate(180deg);
+ }
+ 75% {
+ transform: scale(1.2) rotate(270deg);
+ filter: hue-rotate(270deg);
+ }
+ 100% {
+ transform: scale(1) rotate(360deg);
+ filter: hue-rotate(360deg);
+ }
+}
+
+@keyframes bombShake {
+ 0%, 100% { transform: translateX(0) rotate(0deg); }
+ 25% { transform: translateX(-2px) rotate(-1deg); }
+ 75% { transform: translateX(2px) rotate(1deg); }
+}
+
+@keyframes rocketTargetHit {
+ 0% {
+ background: linear-gradient(135deg, #eeeded, #f1eeee);
+ transform: scale(1);
+ }
+ 25% {
+ background: linear-gradient(135deg, #ff6b6b, #ff8e53);
+ transform: scale(1.1);
+ }
+ 50% {
+ background: linear-gradient(135deg, #ffd93d, #ff6b6b);
+ transform: scale(1.2);
+ }
+ 75% {
+ background: linear-gradient(135deg, #ff8e53, #ffd93d);
+ transform: scale(1.1);
+ }
+ 100% {
+ background: linear-gradient(135deg, #eeeded, #f1eeee);
+ transform: scale(1);
+ }
+}
+
+/* Power-Up Animation Container */
+.power-up-animation {
+ position: absolute;
+ pointer-events: none;
+ z-index: 1000;
+ transform: translate(-50%, -50%); /* Zentriere die Animation */
+}
+
+/* Raketen-Treffer-Effekt */
+.game-tile.rocket-target-hit {
+ animation: rocketTargetHit 0.3s ease-in-out;
+ z-index: 100;
+}
+
+.explosion-effect {
+ width: 80px;
+ height: 80px;
+ background: radial-gradient(circle, #ff6b6b, #ff8e53, #ffd93d, #6bcf7f);
+ border-radius: 50%;
+ animation: explosion 0.8s 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 1.2s ease-in-out forwards;
+}
+
+.rainbow-effect {
+ width: 100px;
+ height: 100px;
+ background: conic-gradient(from 0deg, #ff6b6b, #ffd93d, #6bcf7f, #4ecdc4, #45b7d1, #96ceb4, #feca57, #ff9ff3, #ff6b6b);
+ border-radius: 50%;
+ animation: rainbowPulse 1.5s ease-in-out forwards;
+}
+
+.bomb-effect {
+ width: 60px;
+ height: 60px;
+ background: radial-gradient(circle, #ff6b6b, #ff8e53);
+ border-radius: 50%;
+ animation: bombShake 0.3s ease-in-out infinite;
+}
+
/* Loading State */
.game-board-loading {
display: flex;
@@ -2304,8 +3803,8 @@ export default {
}
.game-tile {
- width: 45px;
- height: 45px;
+ width: 25px;
+ height: 25px;
}
.game-board {
@@ -2314,7 +3813,7 @@ export default {
}
.tile-icon {
- font-size: 18px;
+ font-size: 14px;
}
.moves-left-display {