From b5f4e72de2e1d2d2c5e83328745601c2c3bbc4c1 Mon Sep 17 00:00:00 2001 From: "Torsten Schulz (local)" Date: Mon, 25 Aug 2025 22:35:56 +0200 Subject: [PATCH] =?UTF-8?q?feat(match3):=20Hinzuf=C3=BCgen=20von=20Raketen?= =?UTF-8?q?-Power-up-Mechaniken=20und=20Animationen?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Implementierung neuer Raketen-Power-ups mit Logik zur Aktivierung durch Doppelklick oder Verschieben. - Einführung von Animationen für Raketenexplosionen und -flüge zur Verbesserung der visuellen Effekte im Spiel. - Erweiterung der Spielmechanik zur Handhabung von Raketen-Tiles und deren Interaktionen mit anderen Tiles. - Anpassungen an der Benutzeroberfläche zur Anzeige von Raketen-Animationen und -Effekten. --- frontend/src/views/minigames/Match3Game.vue | 360 +++++++++++++++++++- 1 file changed, 358 insertions(+), 2 deletions(-) diff --git a/frontend/src/views/minigames/Match3Game.vue b/frontend/src/views/minigames/Match3Game.vue index dd0610a..e2e5697 100644 --- a/frontend/src/views/minigames/Match3Game.vue +++ b/frontend/src/views/minigames/Match3Game.vue @@ -143,6 +143,20 @@
+ +
+
🚀💥
+
+ + +
+
🚀
+
+ + +
+
🚀
+
@@ -326,6 +340,10 @@ export default { rainbowCenter: { x: 0, y: 0 }, showBombEffect: false, bombCenter: { x: 0, y: 0 }, + showRocketEffect: false, + rocketCenter: { x: 0, y: 0 }, + rocketTarget: { x: -1, y: -1 }, + showFlyingRocket: false, // Sound-Effekte sounds: { @@ -2058,6 +2076,18 @@ export default { return false; } + // Prüfe ob ein Tile eine Rakete ist + if (this.isRocketTile(this.board[fromIndex].type) || this.isRocketTile(this.board[toIndex].type)) { + console.log('🚀 Rakete wird aktiviert!'); + return await this.activateRocket(fromIndex, toIndex); + } + + // Prüfe ob das Ziel ein leeres Feld ist + if (!this.board[toIndex]) { + console.log('⚠️ Verschieben auf leeres Feld nicht erlaubt'); + return false; + } + // Führe den Tausch durch const tempTile = { ...this.board[fromIndex] }; this.board[fromIndex] = { ...this.board[toIndex] }; @@ -2088,6 +2118,187 @@ export default { } }, + // Aktiviere eine Rakete (durch Verschieben oder Doppelklick) + async activateRocket(rocketIndex, targetIndex) { + console.log(`🚀 Aktiviere Rakete an Position ${rocketIndex}`); + + // Bestimme das Zentrum der Raketen-Explosion + let explosionCenter; + let tilesToRemove = new Set(); + + if (this.isRocketTile(this.board[rocketIndex].type)) { + // Rakete wird auf ein Nachbarfeld verschoben + explosionCenter = targetIndex; + tilesToRemove = this.getRocketExplosionTiles(targetIndex); + } else { + // Ein Tile wird auf die Rakete verschoben + explosionCenter = rocketIndex; + tilesToRemove = this.getRocketExplosionTiles(rocketIndex); + } + + // Entferne die Tiles um das Explosionszentrum + if (tilesToRemove.size > 0) { + console.log(`🚀 Entferne ${tilesToRemove.size} Tiles um Position ${explosionCenter}`); + + // 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 + if (this.isRocketTile(this.board[rocketIndex].type)) { + console.log(`🚀 Entferne Rakete an Position ${rocketIndex}`); + this.board[rocketIndex] = null; + } + + // Aktualisiere die Anzeige + this.$forceUpdate(); + + // Zeige Raketen-Explosions-Animation + await this.showRocketExplosionAnimation(explosionCenter); + + // 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(); + const centerPos = this.indexToCoords(centerIndex); + + // Prüfe alle 4 Richtungen: oben, unten, links, rechts + const directions = [ + { row: -1, col: 0 }, // oben + { row: 1, col: 0 }, // unten + { row: 0, col: -1 }, // links + { row: 0, col: 1 } // rechts + ]; + + directions.forEach(dir => { + const newRow = centerPos.row + dir.row; + const newCol = centerPos.col + dir.col; + + if (this.isValidPosition(newRow, newCol)) { + const newIndex = this.coordsToIndex(newRow, newCol); + if (newIndex !== null && this.board[newIndex] && !this.isRocketTile(this.board[newIndex].type)) { + tilesToRemove.add(newIndex); + } + } + }); + + return tilesToRemove; + }, + + // Zeige Raketen-Explosions-Animation + async showRocketExplosionAnimation(centerIndex) { + const centerPos = this.indexToCoords(centerIndex); + const tileElement = document.querySelector(`[data-index="${centerIndex}"]`); + + 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 + await this.rocketFlightToRandomTile(); + }, + + // Rakete fliegt zu einem zufälligen belegten Feld + async rocketFlightToRandomTile() { + // Finde alle belegten Felder + const occupiedTiles = []; + for (let i = 0; i < this.board.length; i++) { + if (this.board[i] && !this.isRocketTile(this.board[i].type)) { + occupiedTiles.push(i); + } + } + + if (occupiedTiles.length > 0) { + // Wähle ein zufälliges belegtes Feld + const randomTargetIndex = occupiedTiles[Math.floor(Math.random() * occupiedTiles.length)]; + const targetPos = this.indexToCoords(randomTargetIndex); + + console.log(`🚀 Rakete fliegt zu Position [${targetPos.row}, ${targetPos.col}]`); + + // Zeige Flug-Animation + await this.showRocketFlightAnimation(randomTargetIndex); + + // Entferne das Ziel-Tile nach der Animation + this.board[randomTargetIndex] = null; + console.log(`🚀 Ziel-Tile an Position ${randomTargetIndex} entfernt`); + + // Aktualisiere die Anzeige + this.$forceUpdate(); + } + }, + + // Zeige Raketen-Flug-Animation + async showRocketFlightAnimation(targetIndex) { + const targetPos = this.indexToCoords(targetIndex); + const targetElement = document.querySelector(`[data-index="${targetIndex}"]`); + + if (targetElement) { + const rect = targetElement.getBoundingClientRect(); + this.rocketTarget = { x: rect.left + rect.width / 2, y: rect.top + rect.height / 2 }; + } + + // Zeige Flug-Animation + console.log(`🚀 Rakete fliegt zu Ziel...`); + + // Zeige die fliegende Rakete + this.showFlyingRocket = true; + + // Berechne die Flug-Distanz + const flyX = this.rocketTarget.x - this.rocketCenter.x; + const flyY = this.rocketTarget.y - this.rocketCenter.y; + + // Setze CSS-Variablen für die Flug-Animation + const flyingRocketElement = document.querySelector('.flying-rocket'); + if (flyingRocketElement) { + flyingRocketElement.style.setProperty('--fly-x', `${flyX}px`); + flyingRocketElement.style.setProperty('--fly-y', `${flyY}px`); + } + + // Warte auf Flug-Animation (1 Sekunde) + await this.wait(1000); + + // Verstecke die fliegende Rakete + this.showFlyingRocket = false; + + console.log(`🚀 Rakete erreicht Ziel und zerstört es`); + + // Aktualisiere die Anzeige + this.$forceUpdate(); + }, + // Hilfsmethode: Warte für eine bestimmte Zeit wait(ms) { return new Promise(resolve => setTimeout(resolve, ms)); @@ -3530,13 +3741,21 @@ export default { }, // Neue Methode: Behandle Doppelklick auf Power-Up Tiles - handleDoubleClick(index, event) { + async handleDoubleClick(index, event) { event.preventDefault(); event.stopPropagation(); const tile = this.board[index]; if (!tile) return; + // Prüfe auf Raketen-Tiles (neue Power-ups) + if (this.isRocketTile(tile.type)) { + console.log(`🚀 Doppelklick auf Rakete ${tile.type} an Position ${index}`); + // Rakete durch Doppelklick aktivieren + await this.activateRocketByDoubleClick(index); + return; + } + if (tile.isSpecial) { // Power-Up als Zug zählen this.countPowerUpMove(); @@ -3556,8 +3775,51 @@ export default { this.handleRocketDoubleClick(index); } } + }, + + // Aktiviere Rakete durch Doppelklick + async activateRocketByDoubleClick(rocketIndex) { + console.log(`🚀 Aktiviere Rakete durch Doppelklick an Position ${rocketIndex}`); + + // Hole alle Tiles um die Rakete herum + const tilesToRemove = this.getRocketExplosionTiles(rocketIndex); + + if (tilesToRemove.size > 0) { + console.log(`🚀 Entferne ${tilesToRemove.size} Tiles um Rakete herum`); + + // 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(); + + // Zeige Raketen-Explosions-Animation + await this.showRocketExplosionAnimation(rocketIndex); + + // 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--; + } }, - + // Neue Methode: Entferne alle Tiles eines bestimmten Typs removeAllTilesOfType(tileType) { const tilesToRemove = []; @@ -5116,6 +5378,100 @@ export default { font-weight: bold; } +/* Raketen-Explosions-Effekt */ +.rocket-explosion { + width: 80px; + height: 80px; + background: radial-gradient(circle, #ff6b6b, #ffd93d, #ff6b6b); + border-radius: 50%; + animation: rocketExplosion 1s ease-out forwards; + position: relative; + display: flex; + align-items: center; + justify-content: center; + font-size: 24px; +} + +@keyframes rocketExplosion { + 0% { + transform: scale(0.1); + opacity: 0; + } + 50% { + transform: scale(1.2); + opacity: 1; + } + 100% { + transform: scale(1); + opacity: 0; + } +} + +/* Raketen-Flug-Animation */ +.rocket-flight-animation { + position: fixed; + z-index: 2000; + pointer-events: none; +} + +.rocket-flight { + 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; +} + +@keyframes rocketFlight { + 0% { + transform: scale(0.5); + opacity: 0; + } + 50% { + transform: scale(1); + opacity: 1; + } + 100% { + transform: scale(0.8); + opacity: 0; + } +} + +/* Fliegende Rakete */ +.flying-rocket { + position: fixed; + z-index: 2000; + pointer-events: none; + animation: rocketFly 1s ease-in-out forwards; +} + +.rocket-icon { + width: 40px; + height: 40px; + background: white; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + font-size: 20px; + box-shadow: 0 0 10px rgba(255, 107, 107, 0.8); +} + +@keyframes rocketFly { + 0% { + transform: translate(0, 0) scale(1); + opacity: 1; + } + 100% { + transform: translate(var(--fly-x), var(--fly-y)) scale(0.8); + opacity: 0; + } +} + @keyframes newTileAppear { 0% { transform: scale(0.1);