From cfe8b025199ea1efc8f6409eec1cfb6c52e09d8d Mon Sep 17 00:00:00 2001 From: "Torsten Schulz (local)" Date: Wed, 27 Aug 2025 18:13:50 +0200 Subject: [PATCH] =?UTF-8?q?feat(match3):=20Erweiterung=20der=20Match-Logik?= =?UTF-8?q?=20und=20Einf=C3=BChrung=20neuer=20Power-Ups?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Hinzufügen von Regenbogen-Power-ups zur Spielmechanik, die durch 5er-Matches aktiviert werden. - Anpassung der Logik zur Erkennung von 5er-Matches und deren Priorisierung vor L-Form-Matches. - Verbesserung der Darstellung und Animationen für Bomben und Regenbogen-Power-ups. - Erweiterung der Debug-Ausgaben zur besseren Nachverfolgbarkeit von Power-ups und Matches. - Implementierung von neuen Methoden zur Aktivierung und Handhabung von Power-ups im Spiel. --- frontend/src/views/minigames/Match3Game.vue | 224 ++++++++++++++++++-- 1 file changed, 210 insertions(+), 14 deletions(-) diff --git a/frontend/src/views/minigames/Match3Game.vue b/frontend/src/views/minigames/Match3Game.vue index e08bb5b..a064d1b 100644 --- a/frontend/src/views/minigames/Match3Game.vue +++ b/frontend/src/views/minigames/Match3Game.vue @@ -1502,12 +1502,41 @@ export default { console.log(`🔧 Versuch ${attempts + 1}: ${initialMatches.length} Matches gefunden`); // Wähle einen zufälligen Match + if (initialMatches.length === 0) { + console.log('🔧 Keine Matches mehr gefunden, Level ist bereit'); + break; + } + const randomMatch = initialMatches[Math.floor(Math.random() * initialMatches.length)]; + + // Prüfe ob der Match gültig ist + if (!Array.isArray(randomMatch) || randomMatch.length === 0) { + console.warn('⚠️ Ungültiger Match gefunden, überspringe diesen Versuch'); + attempts++; + continue; + } + const randomTileIndex = randomMatch[Math.floor(Math.random() * randomMatch.length)]; + + // Prüfe ob der Index gültig ist und ein Tile existiert + if (randomTileIndex === null || randomTileIndex === undefined || + randomTileIndex < 0 || randomTileIndex >= this.board.length || + !this.board[randomTileIndex]) { + console.warn(`⚠️ Ungültiger Tile-Index: ${randomTileIndex}, überspringe diesen Versuch`); + attempts++; + continue; + } + const currentType = this.board[randomTileIndex].type; // Wähle einen anderen Tile-Typ const availableTypes = this.tileTypes.filter(type => type !== currentType); + if (availableTypes.length === 0) { + console.warn('⚠️ Keine alternativen Tile-Typen verfügbar, überspringe diesen Versuch'); + attempts++; + continue; + } + const newType = availableTypes[Math.floor(Math.random() * availableTypes.length)]; // Ändere den Tile-Typ @@ -1772,6 +1801,20 @@ export default { console.log('🔧 Erstelle Power-ups nach der Tile-Entfernung...'); const powerUpsCreated = await this.createPowerUpsForMatches(matches); + // Wenn Raketen erstellt wurden, lass sie im nächsten Zug starten + if (powerUpsCreated && powerUpsCreated.rockets && powerUpsCreated.rockets.length > 0) { + console.log(`🚀 ${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 + } + // Debug: Zeige alle Power-ups nach der Erstellung console.log('🔧 Debug: Alle Power-ups nach createPowerUpsForMatches:'); this.debugPowerUps(); @@ -1820,11 +1863,11 @@ export default { // Prüfe ob Power-ups erstellt wurden - wenn ja, keine Cascade-Matches prüfen // Verwende den Rückgabewert von createPowerUpsForMatches - if (!powerUpsCreated) { + if (!powerUpsCreated || powerUpsCreated.count === 0) { // Nur Cascade-Matches prüfen, wenn keine Power-ups erstellt wurden await this.checkForCascadeMatches(); } else { - console.log(`🔧 ${powerUpsCreated} Power-ups erstellt - überspringe Cascade-Match-Prüfung`); + console.log(`🔧 ${powerUpsCreated.count} Power-ups erstellt - überspringe Cascade-Match-Prüfung`); // Debug: Zeige alle Power-ups nach der Verarbeitung console.log('🔧 Debug: Alle Power-ups nach Power-up-Verarbeitung:'); @@ -2008,6 +2051,8 @@ export default { console.log('🔧 Prüfe auf Power-up-Erstellung...'); let powerUpsCreated = 0; + const bombs = []; + const rockets = []; matches.forEach(match => { // Prüfe auf L-Form-Matches (Bomben) @@ -2020,6 +2065,7 @@ export default { console.log(`💣 Bombe an Position ${match.corner} erstellt`); console.log(`🔧 Board[${match.corner}] = ${JSON.stringify(this.board[match.corner])}`); powerUpsCreated++; + bombs.push(match.corner); } // Prüfe auf normale 4er-Matches (Arrays) else if (Array.isArray(match) && match.length === 4) { @@ -2035,6 +2081,7 @@ export default { console.log(`🚀 Rakete ${rocketType} an Position ${rocketIndex} erstellt`); console.log(`🔧 Board[${rocketIndex}] = ${JSON.stringify(this.board[rocketIndex])}`); powerUpsCreated++; + rockets.push(rocketIndex); } }); @@ -2044,7 +2091,12 @@ export default { console.log(`🔧 ${powerUpsCreated} Power-ups erstellt und Board aktualisiert`); } - return powerUpsCreated; + // Gib detaillierte Informationen über erstellte Power-ups zurück + return { + count: powerUpsCreated, + bombs: bombs, + rockets: rockets + }; }, // Bestimme den Raketen-Typ basierend auf der Match-Richtung @@ -2513,6 +2565,12 @@ export default { return await this.activateRocket(fromIndex, toIndex); } + // Prüfe ob ein Tile eine Bombe ist + if (this.board[fromIndex].type === 'bomb' || this.board[toIndex].type === 'bomb') { + console.log('💣 Bombe wird aktiviert!'); + return await this.activateBomb(fromIndex, toIndex); + } + // Prüfe ob das Ziel ein leeres Feld ist if (!this.board[toIndex]) { console.log('⚠️ Verschieben auf leeres Feld nicht erlaubt'); @@ -2613,6 +2671,113 @@ export default { return false; }, + // Aktiviere eine Bombe (durch Verschieben oder Doppelklick) + async activateBomb(bombIndex, targetIndex) { + console.log(`💣 Aktiviere Bombe an Position ${bombIndex}`); + + // Bestimme das Zentrum der Bomben-Explosion + let explosionCenter; + + if (this.board[bombIndex].type === 'bomb') { + // Bombe wird auf ein Nachbarfeld verschoben + explosionCenter = targetIndex; + } else { + // Ein Tile wird auf die Bombe verschoben + explosionCenter = bombIndex; + } + + // Zeige Bomben-Effekt-Animation + await this.showBombEffectAnimation(explosionCenter); + + // Explodiere die Bombe mit 1 Ring (3x3 Bereich) - manuelle Aktivierung + this.explodeBomb(explosionCenter, 1, true); + + // Entferne die Bombe nach der Explosion + if (this.board[bombIndex].type === 'bomb') { + console.log(`💣 Entferne Bombe an Position ${bombIndex}`); + this.board[bombIndex] = null; + } + + // Aktualisiere die Anzeige + this.$forceUpdate(); + + // Führe Fall-Down-Logik aus + await this.fallTilesDown(); + + // Fülle leere Positionen mit neuen Tiles auf + await this.fillEmptyPositions(); + + // Erhöhe den Zug-Zähler + this.moves++; + this.movesLeft--; + + return true; + }, + + // Aktiviere eine Rakete automatisch (für sofortige Aktivierung nach der Erstellung) + async activateRocketAutomatically(rocketIndex) { + console.log(`🚀 Aktiviere Rakete automatisch an Position ${rocketIndex}`); + + // Wähle ein zufälliges Ziel für die Rakete + const availableTargets = []; + for (let i = 0; i < this.board.length; i++) { + if (i !== rocketIndex && this.board[i] && !this.isRocketTile(this.board[i].type)) { + availableTargets.push(i); + } + } + + if (availableTargets.length === 0) { + console.warn('⚠️ Keine Ziele für Rakete verfügbar'); + return false; + } + + // Wähle ein zufälliges Ziel + const randomTarget = availableTargets[Math.floor(Math.random() * availableTargets.length)]; + console.log(`🚀 Rakete startet auf zufälliges Ziel an Position ${randomTarget}`); + + // Zeige Raketen-Explosions-Animation (einfache Version ohne Flug) + await this.showRocketExplosionAnimationSimple(randomTarget); + + // Entferne die Tiles um das Explosionszentrum + const tilesToRemove = this.getRocketExplosionTiles(randomTarget); + + if (tilesToRemove.size > 0) { + console.log(`🚀 Entferne ${tilesToRemove.size} Tiles um Position ${randomTarget}`); + + // Starte Schrumpf-Animation für alle zu entfernenden Tiles + await this.animateTileRemoval(Array.from(tilesToRemove)); + + // Entferne alle Tiles nach der Animation + tilesToRemove.forEach(index => { + if (this.board[index]) { + console.log(`🚀 Entferne Tile ${this.board[index].type} an Position ${index}`); + this.board[index] = null; + } + }); + + // Entferne auch die Rakete selbst + console.log(`🚀 Entferne Rakete an Position ${rocketIndex}`); + this.board[rocketIndex] = null; + + // Aktualisiere die Anzeige + this.$forceUpdate(); + + // Führe Fall-Down-Logik aus + await this.fallTilesDown(); + + // Fülle leere Positionen mit neuen Tiles auf + await this.fillEmptyPositions(); + + // Erhöhe den Zug-Zähler + this.moves++; + this.movesLeft--; + + return true; + } + + return false; + }, + // Hole alle Tiles, die von der Raketen-Explosion betroffen sind getRocketExplosionTiles(centerIndex) { const tilesToRemove = new Set(); @@ -2660,9 +2825,27 @@ export default { // Verstecke Animation this.showRocketEffect = false; + }, + + // Zeige Raketen-Explosions-Animation für automatische Aktivierung (ohne Flug) + async showRocketExplosionAnimationSimple(centerIndex) { + const centerPos = this.indexToCoords(centerIndex); + const tileElement = document.querySelector(`[data-index="${centerIndex}"]`); - // Rakete fliegt zu einem zufälligen belegten Feld - await this.rocketFlightToRandomTile(); + if (tileElement) { + const rect = tileElement.getBoundingClientRect(); + this.rocketCenter = { x: rect.left + rect.width / 2, y: rect.top + rect.height / 2 }; + } + + // Zeige Explosions-Animation + this.showRocketEffect = true; + this.playSound('rocket'); + + // Warte auf Animation + await this.wait(1000); + + // Verstecke Animation + this.showRocketEffect = false; }, // Rakete fliegt zu einem zufälligen belegten Feld @@ -4213,7 +4396,7 @@ export default { this.removeAllTilesOfType(randomTileType); } else if (tile.type === 'bomb') { // Bomben-Tile: 9 Tiles rundherum entfernen - this.explodeBomb(index, 1); // 1 Ring = 3x3 Bereich + this.explodeBomb(index, 1, true); // 1 Ring = 3x3 Bereich - manuelle Aktivierung } else if (tile.type === 'rocket') { // Raketen-Tile: 4 Nachbarfelder löschen und Rakete starten this.handleRocketDoubleClick(index); @@ -4275,7 +4458,7 @@ export default { await this.showBombEffectAnimation(bombIndex); // Aktiviere die Bombe (3x3 Bereich) - this.explodeBomb(bombIndex, 1); + this.explodeBomb(bombIndex, 1, true); // Entferne die Bombe selbst console.log(`💣 Entferne Bombe an Position ${bombIndex}`); @@ -4351,7 +4534,7 @@ export default { // 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 + this.explodeBomb(bombIndex, 2, true); // 2 Ringe = 5x5 Bereich - manuelle Aktivierung } return true; } else if (originalTile1.type === 'bomb' || originalTile2.type === 'bomb') { @@ -4362,7 +4545,7 @@ export default { // Finde die neue Position der Bombe const newBombIndex = this.findTileIndex(bombTile); if (newBombIndex !== null) { - this.explodeBomb(newBombIndex, 1); // 1 Ring = 3x3 Bereich + this.explodeBomb(newBombIndex, 1, true); // 1 Ring = 3x3 Bereich - manuelle Aktivierung } return true; } else if ((originalTile1.type === 'bomb' && originalTile2.type === 'rainbow') || @@ -4460,7 +4643,7 @@ export default { }, // Neue Methode: Explodiere Bombe mit bestimmter Reichweite - explodeBomb(centerIndex, rings) { + explodeBomb(centerIndex, rings, isManualActivation = false) { const { row: centerRow, col: centerCol } = this.indexToCoords(centerIndex); const tilesToRemove = []; const powerUpsToTrigger = []; // Sammle Power-Ups für Kettenreaktionen @@ -4512,7 +4695,8 @@ export default { this.score += points; // Löse Kettenreaktionen nur für Power-Ups aus, die noch existieren - if (powerUpsToTrigger.length > 0) { + // UND nur wenn es sich um eine manuelle Aktivierung handelt + if (powerUpsToTrigger.length > 0 && isManualActivation) { setTimeout(() => { // Prüfe nochmal, ob die Power-Ups noch existieren, bevor sie ausgelöst werden const validPowerUps = powerUpsToTrigger.filter(powerUp => @@ -4533,7 +4717,7 @@ export default { powerUps.forEach(powerUp => { if (powerUp.type === 'bomb') { // Bombe explodiert mit 1 Ring (3x3 Bereich) - this.explodeBomb(powerUp.index, 1); + this.explodeBomb(powerUp.index, 1, true); } else if (powerUp.type === 'rocket') { // Rakete startet auf zufälliges Feld this.launchRocketToRandomField(powerUp.index); @@ -4596,7 +4780,7 @@ export default { // Löse alle Bomben aus bombIndices.forEach(index => { - this.explodeBomb(index, 1); + this.explodeBomb(index, 1, true); }); @@ -4720,7 +4904,7 @@ export default { const landingIndex = availableIndices[randomIndex]; // Explodiere am Landungsort (9 Felder = 1 Ring) - this.explodeBomb(landingIndex, 1); + this.explodeBomb(landingIndex, 1, true); } @@ -5857,6 +6041,8 @@ export default { .game-tile.bomb { background: linear-gradient(45deg, #ff6b6b, #ff8e53) !important; position: relative; + z-index: 10; + box-shadow: 0 0 15px rgba(255, 107, 107, 0.8); } /* Bomben-Symbol */ @@ -5865,6 +6051,16 @@ export default { color: white; text-shadow: 2px 2px 4px rgba(0,0,0,0.5); font-weight: bold; + animation: bombPulse 2s ease-in-out infinite; +} + +@keyframes bombPulse { + 0%, 100% { + transform: scale(1); + } + 50% { + transform: scale(1.1); + } } /* Raketen-Explosions-Effekt */