From 53fe2280cb88ea40aceb5fd7be256ff14078730e Mon Sep 17 00:00:00 2001 From: Brian Fertig Date: Sat, 23 Aug 2025 14:42:08 -0600 Subject: [PATCH] ```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. ``` --- src/GameScene.js | 217 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 213 insertions(+), 4 deletions(-) diff --git a/src/GameScene.js b/src/GameScene.js index 89de80c..0d03bd3 100644 --- a/src/GameScene.js +++ b/src/GameScene.js @@ -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(); + }); + } + } + }); + }); + } + } \ No newline at end of file