feat(match3): Hinzufügen von Raketen-Power-up-Mechaniken und Animationen

- 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.
This commit is contained in:
Torsten Schulz (local)
2025-08-25 22:35:56 +02:00
parent e85f402d11
commit b5f4e72de2

View File

@@ -143,6 +143,20 @@
<div v-if="showBombEffect" class="power-up-animation" :style="{ left: bombCenter.x + 'px', top: bombCenter.y + 'px' }"> <div v-if="showBombEffect" class="power-up-animation" :style="{ left: bombCenter.x + 'px', top: bombCenter.y + 'px' }">
<div class="bomb-effect"></div> <div class="bomb-effect"></div>
</div> </div>
<div v-if="showRocketEffect" class="power-up-animation" :style="{ left: rocketCenter.x + 'px', top: rocketCenter.y + 'px' }">
<div class="rocket-explosion">🚀💥</div>
</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>
<!-- Fliegende Rakete -->
<div v-if="showFlyingRocket" class="flying-rocket" :style="{ left: rocketCenter.x + 'px', top: rocketCenter.y + 'px' }">
<div class="rocket-icon">🚀</div>
</div>
</div> </div>
<!-- Spiel-Kontrollen --> <!-- Spiel-Kontrollen -->
@@ -326,6 +340,10 @@ export default {
rainbowCenter: { x: 0, y: 0 }, rainbowCenter: { x: 0, y: 0 },
showBombEffect: false, showBombEffect: false,
bombCenter: { x: 0, y: 0 }, bombCenter: { x: 0, y: 0 },
showRocketEffect: false,
rocketCenter: { x: 0, y: 0 },
rocketTarget: { x: -1, y: -1 },
showFlyingRocket: false,
// Sound-Effekte // Sound-Effekte
sounds: { sounds: {
@@ -2058,6 +2076,18 @@ export default {
return false; 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 // Führe den Tausch durch
const tempTile = { ...this.board[fromIndex] }; const tempTile = { ...this.board[fromIndex] };
this.board[fromIndex] = { ...this.board[toIndex] }; 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 // Hilfsmethode: Warte für eine bestimmte Zeit
wait(ms) { wait(ms) {
return new Promise(resolve => setTimeout(resolve, ms)); return new Promise(resolve => setTimeout(resolve, ms));
@@ -3530,13 +3741,21 @@ export default {
}, },
// Neue Methode: Behandle Doppelklick auf Power-Up Tiles // Neue Methode: Behandle Doppelklick auf Power-Up Tiles
handleDoubleClick(index, event) { async handleDoubleClick(index, event) {
event.preventDefault(); event.preventDefault();
event.stopPropagation(); event.stopPropagation();
const tile = this.board[index]; const tile = this.board[index];
if (!tile) return; 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) { if (tile.isSpecial) {
// Power-Up als Zug zählen // Power-Up als Zug zählen
this.countPowerUpMove(); this.countPowerUpMove();
@@ -3556,6 +3775,49 @@ export default {
this.handleRocketDoubleClick(index); 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 // Neue Methode: Entferne alle Tiles eines bestimmten Typs
@@ -5116,6 +5378,100 @@ export default {
font-weight: bold; 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 { @keyframes newTileAppear {
0% { 0% {
transform: scale(0.1); transform: scale(0.1);