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 class="bomb-effect"></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>
|
||||
|
||||
<!-- Spiel-Kontrollen -->
|
||||
@@ -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();
|
||||
@@ -3558,6 +3777,49 @@ export default {
|
||||
}
|
||||
},
|
||||
|
||||
// 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);
|
||||
|
||||
Reference in New Issue
Block a user