```git commit message

Add match detection and jewel dropping functionality

This commit implements the core matching logic for the gem matching game:
- Added isDropping flag to prevent concurrent drop operations
- Implemented checkMatches() function to detect horizontal and vertical matches of 3+ jewels
- Added destroyMatchedJewels() function to animate and remove matched jewels
- Implemented dropJewels() function to handle jewel dropping after matches are cleared
- Integrated match checking into the swap completion callback
- Added proper animation sequences with tweens and delayed calls for smooth gameplay

The changes enable the core gem matching mechanics where matched jewels are destroyed, remaining jewels fall down to fill gaps, and new matches are automatically detected and processed.
```
This commit is contained in:
Brian Fertig 2025-08-23 14:42:08 -06:00
parent e9cb2a939d
commit 53fe2280cb
1 changed files with 213 additions and 4 deletions

View File

@ -15,6 +15,7 @@ export class GameScene extends Phaser.Scene {
this.startRows = 5;
this.level = 1;
this.levelTime = 60;
this.isDropping = false;
// Add selectedJewel property
this.selectedJewel = null;
@ -252,7 +253,10 @@ export class GameScene extends Phaser.Scene {
targets: this.swapWithJewel,
x: fromX,
y: fromY,
duration: 300
duration: 300,
onComplete: () => {
this.checkMatches();
}
});
this.selectedJewel = null;
this.swapWithJewel = null;
@ -268,8 +272,213 @@ export class GameScene extends Phaser.Scene {
this.isSwapping = false;
});
}
// Check for Matches
// Function to check for matches and destroy them
checkMatches() {
const matchedJewels = new Set();
// Check horizontal matches
for (let row = 1; row <= this.gridConfig.rows; row++) {
let count = 1;
let currentType = this.getJewelAtPosition(1, row);
for (let col = 2; col <= this.gridConfig.cols; col++) {
const jewelType = this.getJewelAtPosition(col, row);
if (jewelType === currentType && jewelType !== null) {
count++;
} else {
if (count >= 3) {
// Add all jewels in this match to the matched set
for (let i = col - count; i < col; i++) {
const key = `${i},${row}`;
matchedJewels.add(key);
}
}
count = 1;
currentType = jewelType;
}
}
// Check for match at the end of row
if (count >= 3) {
for (let i = this.gridConfig.cols - count + 1; i <= this.gridConfig.cols; i++) {
const key = `${i},${row}`;
matchedJewels.add(key);
}
}
}
// Check vertical matches
for (let col = 1; col <= this.gridConfig.cols; col++) {
let count = 1;
let currentType = this.getJewelAtPosition(col, 0);
for (let row = 1; row <= this.gridConfig.rows; row++) {
const jewelType = this.getJewelAtPosition(col, row);
if (jewelType === currentType && jewelType !== null) {
count++;
} else {
if (count >= 3) {
// Add all jewels in this match to the matched set
for (let i = row - count; i < row; i++) {
const key = `${col},${i}`;
matchedJewels.add(key);
}
}
count = 1;
currentType = jewelType;
}
}
// Check for match at the end of column
if (count >= 3) {
for (let i = this.gridConfig.rows - count + 1; i <= this.gridConfig.rows; i++) {
const key = `${col},${i}`;
matchedJewels.add(key);
}
}
}
// If we found matches, destroy them
if (matchedJewels.size > 0) {
this.destroyMatchedJewels(matchedJewels);
return true;
}
return false;
}
// Function to destroy matched jewels
destroyMatchedJewels(matchedJewels) {
// Create an array of jewels to destroy
const jewelsToDestroy = [];
this.jewels.children.iterate((jewel) => {
if (jewel) {
const col = Math.floor((jewel.x - this.gridConfig.leftPadding) / this.gridConfig.jewelWidth);
const row = Math.floor(jewel.y / this.gridConfig.jewelHeight);
const key = `${col},${row}`;
if (matchedJewels.has(key)) {
jewelsToDestroy.push(jewel);
}
}
});
// Animate destruction
jewelsToDestroy.forEach((jewel) => {
this.tweens.add({
targets: jewel,
scaleX: 0,
scaleY: 0,
alpha: 0,
duration: 200,
onComplete: () => {
jewel.destroy();
this.time.delayedCall(100, () => {
this.dropJewels();
});
}
});
});
}
dropJewels() {
if (this.isDropping) {
return;
}
this.isDropping = true;
console.log('drop');
// Create a grid representation to track jewel positions
const grid = [];
for (let row = 1; row <= this.gridConfig.rows; row++) {
grid[row] = [];
for (let col = 1; col <= this.gridConfig.cols; col++) {
grid[row][col] = null;
}
}
// Populate the grid with jewel positions
this.jewels.children.iterate((jewel) => {
if (jewel) {
const col = Math.floor((jewel.x - this.gridConfig.leftPadding) / this.gridConfig.jewelWidth);
const row = Math.floor(jewel.y / this.gridConfig.jewelHeight);
// Ensure we're within grid bounds
if (col >= 1 && col <= this.gridConfig.cols && row >= 1 && row <= this.gridConfig.rows) {
grid[row][col] = jewel;
}
}
});
// Find jewels that need to drop and their target positions
const jewelsToDrop = [];
for (let col = 1; col <= this.gridConfig.cols; col++) {
let emptySpaces = 0;
// Process from bottom to top
for (let row = this.gridConfig.rows; row >= 1; row--) {
if (grid[row][col] === null) {
emptySpaces++;
} else if (emptySpaces > 0) {
// This jewel needs to drop
const jewel = grid[row][col];
const targetRow = row + emptySpaces;
jewelsToDrop.push({
jewel: jewel,
fromRow: row,
toRow: targetRow
});
// Update grid reference
grid[row][col] = null;
grid[targetRow][col] = jewel;
}
}
}
// If no jewels need to drop, we're done
if (jewelsToDrop.length === 0) {
this.isDropping = false;
return;
}
// Animate the dropping
let droppedCount = 0;
jewelsToDrop.forEach((dropInfo) => {
const { jewel, fromRow, toRow } = dropInfo;
// Calculate target y position
const targetY = toRow * this.gridConfig.jewelHeight;
// Animate the jewel dropping
this.tweens.add({
targets: jewel,
y: targetY,
duration: 300,
ease: 'Linear',
onComplete: () => {
droppedCount++;
// When all jewels have dropped, check for new matches
if (droppedCount === jewelsToDrop.length) {
this.time.delayedCall(100, () => {
this.isDropping = false;
// Check for new matches after dropping
this.checkMatches();
});
}
}
});
});
}
}