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:
@@ -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);
|
||||||
|
|||||||
Reference in New Issue
Block a user