diff --git a/assets/2015 Cruiser.ttf b/assets/2015 Cruiser.ttf new file mode 100644 index 0000000..4384a1e Binary files /dev/null and b/assets/2015 Cruiser.ttf differ diff --git a/assets/CodePredators-Regular.otf b/assets/CodePredators-Regular.otf new file mode 100644 index 0000000..103e01d Binary files /dev/null and b/assets/CodePredators-Regular.otf differ diff --git a/assets/NEUROPOL.ttf b/assets/NEUROPOL.ttf new file mode 100644 index 0000000..d703e02 Binary files /dev/null and b/assets/NEUROPOL.ttf differ diff --git a/assets/cyberGoblin.png b/assets/cyberGoblin.png new file mode 100644 index 0000000..2aea640 Binary files /dev/null and b/assets/cyberGoblin.png differ diff --git a/assets/goblin-excited.mp4 b/assets/goblin-excited.mp4 new file mode 100644 index 0000000..97cbe07 Binary files /dev/null and b/assets/goblin-excited.mp4 differ diff --git a/assets/goblin-match.mp4 b/assets/goblin-match.mp4 new file mode 100644 index 0000000..b281dbc Binary files /dev/null and b/assets/goblin-match.mp4 differ diff --git a/assets/goblin-pleased.mp4 b/assets/goblin-pleased.mp4 new file mode 100644 index 0000000..20bbffb Binary files /dev/null and b/assets/goblin-pleased.mp4 differ diff --git a/assets/goblin-resting.mp4 b/assets/goblin-resting.mp4 new file mode 100644 index 0000000..06b5a96 Binary files /dev/null and b/assets/goblin-resting.mp4 differ diff --git a/src/GameScene.js b/src/GameScene.js index 0d03bd3..7c18c14 100644 --- a/src/GameScene.js +++ b/src/GameScene.js @@ -1,3 +1,5 @@ +import { LEVEL_CONFIG } from './config.js'; + export class GameScene extends Phaser.Scene { constructor() { super({ key: 'GameScene' }); @@ -11,15 +13,29 @@ export class GameScene extends Phaser.Scene { jewelHeight: 100, }; - this.numberOfJewels = 5; - this.startRows = 5; + // Stats + this.numberOfJewels = 4; + this.startRows = 3; this.level = 1; - this.levelTime = 60; + this.matchesNeeded = 10; + this.score = 0; + this.ally = 'goblin'; + + // Status Indication this.isDropping = false; + this.isSwapping = false; + this.isDestroying = false; + this.isMovingUp = false; + this.isPlayingVideo = false; // Add selectedJewel property this.selectedJewel = null; this.swapWithJewel = null; + + // Timer properties + this.moveTimer = 0; + this.moveInterval = 12000; + this.timerText = null; } preload() { @@ -27,6 +43,13 @@ export class GameScene extends Phaser.Scene { frameWidth: 100, frameHeight: 100 }); + this.load.font('cruiser', 'assets/NEUROPOL.ttf'); + this.load.font('code', 'assets/CodePredators-Regular.otf'); + + this.load.video('goblin-resting', 'assets/goblin-resting.mp4'); + this.load.video('goblin-excited', 'assets/goblin-excited.mp4'); + this.load.video('goblin-pleased', 'assets/goblin-pleased.mp4'); + this.load.video('goblin-match', 'assets/goblin-match.mp4'); } create() { @@ -37,10 +60,79 @@ export class GameScene extends Phaser.Scene { }); this.physics.add.collider(this.jewels, this.jewels); this.createStart(); + + // Create the score text + this.scoreText = this.add.text(20, 50, 'Score: 0', { + fontFamily: 'cruiser, arial', + fontSize: '36px', + fill: '#ffffff', + padding: { + left: 10, + right: 10, + top: 5, + bottom: 5 + } + }).setOrigin(0); + + // Create the Level text + this.LevelText = this.add.text(20, 116, `Level: ${this.level}`, { + fontFamily: 'cruiser, arial', + fontSize: '36px', + fill: '#ffffff', + padding: { + left: 10, + right: 10, + top: 5, + bottom: 5 + } + }).setOrigin(0); + + // Create the timer text + this.timerText = this.add.text(20, 182, 'Next Row:', { + fontFamily: 'cruiser, arial', + fontSize: '36px', + fill: '#ffffff', + padding: { + left: 10, + right: 10, + top: 5, + bottom: 5 + } + }).setOrigin(0); + + // Create the Matches Needed text + this.matchesText = this.add.text(20, 248, `Remaining Matches: ${this.matchesNeeded}`, { + fontFamily: 'cruiser, arial', + fontSize: '36px', + fill: '#ffffff', + padding: { + left: 10, + right: 10, + top: 5, + bottom: 5 + } + }).setOrigin(0); + + this.allyVideo = this.add.video(350, 610, `${this.ally}-resting`).setOrigin(0.5); + this.allyVideo.play(true); } update(time, delta) { - + // Update the move timer + this.moveTimer += delta; + + // Check if it's time to move all jewels up + if (this.moveTimer >= this.moveInterval && this.isDropping === false && this.isSwapping === false && this.isDestroying === false) { + this.moveAllJewelsUp(); + this.moveTimer = 0; // Reset the timer + } + + // Update the timer display + const timeRemaining = (this.moveInterval - this.moveTimer) / 1000; + const displayTime = timeRemaining.toFixed(2); + if (this.timerText) { + this.timerText.setText(`Next Row: ${displayTime}`); + } } makeGrid() { @@ -228,6 +320,12 @@ export class GameScene extends Phaser.Scene { // Modified swap function to disable clicks during execution swapJewel() { + if (this.isMovingUp === true) { + this.time.delayedCall(300, ()=> { + this.swapJewel(); + }) + return; + } // Set swapping flag to prevent new clicks from being processed this.isSwapping = true; @@ -263,7 +361,7 @@ export class GameScene extends Phaser.Scene { // After swap completes, re-enable interactivity and reset flag - this.time.delayedCall(500, () => { // Adjust delay as needed for animation duration + this.time.delayedCall(300, () => { // Adjust delay as needed for animation duration this.jewels.children.iterate((jewel) => { if (jewel) { jewel.setInteractive(); @@ -274,6 +372,60 @@ export class GameScene extends Phaser.Scene { }); } + reduceMatches(amount) { + this.matchesNeeded -= amount; + if (this.matchesNeeded <= 0) { + this.levelUp(); + } + this.matchesText.setText(`Matches Needed: ${this.matchesNeeded}`); + } + + levelUp() { + this.level ++; + const newLevel = LEVEL_CONFIG[this.level]; + this.numberOfJewels = newLevel.numberOfJewels; + this.matchesNeeded = newLevel.matchesNeeded; + this.moveInterval = newLevel.moveInterval; + this.LevelText.setText(`Level: ${this.level}`); + + // Create the New Level text + const newLevelText = this.add.text(1150, 250, `Level ${this.level}`, { + fontFamily: 'code, arial', + fontSize: '100px', + fill: '#ea00ffff', + padding: { + left: 10, + right: 10, + top: 5, + bottom: 5 + } + }).setOrigin(0.5); + + // Add cool eye-catching effect + this.tweens.add({ + targets: newLevelText, + scale: { from: 0.5, to: 1.5 }, + alpha: { from: 0, to: 1 }, + duration: 1000, + ease: 'Back.easeOut', + yoyo: true + }); + + // Fade out and destroy after 500ms + this.time.delayedCall(1200, () => { + this.tweens.add({ + targets: newLevelText, + angle: 360, + scale: 0, + alpha: 0, + duration: 500, + onComplete: () => { + newLevelText.destroy(); + } + }); + }); + } + // Function to check for matches and destroy them checkMatches() { const matchedJewels = new Set(); @@ -290,6 +442,7 @@ export class GameScene extends Phaser.Scene { count++; } else { if (count >= 3) { + this.reduceMatches(1); // Add all jewels in this match to the matched set for (let i = col - count; i < col; i++) { const key = `${i},${row}`; @@ -303,6 +456,7 @@ export class GameScene extends Phaser.Scene { // Check for match at the end of row if (count >= 3) { + this.reduceMatches(1); for (let i = this.gridConfig.cols - count + 1; i <= this.gridConfig.cols; i++) { const key = `${i},${row}`; matchedJewels.add(key); @@ -322,6 +476,7 @@ export class GameScene extends Phaser.Scene { count++; } else { if (count >= 3) { + this.reduceMatches(1); // Add all jewels in this match to the matched set for (let i = row - count; i < row; i++) { const key = `${col},${i}`; @@ -335,6 +490,7 @@ export class GameScene extends Phaser.Scene { // Check for match at the end of column if (count >= 3) { + this.reduceMatches(1); for (let i = this.gridConfig.rows - count + 1; i <= this.gridConfig.rows; i++) { const key = `${col},${i}`; matchedJewels.add(key); @@ -342,8 +498,14 @@ export class GameScene extends Phaser.Scene { } } - // If we found matches, destroy them + // If we found matches, destroy them and play video if (matchedJewels.size > 0) { + this.playVideo(matchedJewels.size); + const scoreMatches = matchedJewels.size / 3; + const scoreAdd = Math.ceil(scoreMatches ** 2) * 10; + this.score += scoreAdd; + this.scoreText.setText(`Score: ${this.score}`); + // ADD SCORE this.destroyMatchedJewels(matchedJewels); return true; } @@ -351,8 +513,37 @@ export class GameScene extends Phaser.Scene { return false; } + playVideo(amount) { + if (this.isPlayingVideo === true) { + return; + } + + this.isPlayingVideo = true; + let video = null; + + if (amount >= 5) { + video = `${this.ally}-excited`; + } else if (amount >= 4) { + video = `${this.ally}-pleased`; + } else { + video = `${this.ally}-match`; + } + const showVideo = this.add.video(350, 610, video).setOrigin(0.5).setDepth(100); + showVideo.play(false); + this.time.delayedCall(5000, () => { + this.isPlayingVideo = false; + showVideo.destroy(); + console.log('end video'); + }); + } + // Function to destroy matched jewels destroyMatchedJewels(matchedJewels) { + if (this.isDestroying) { + return; + } + + this.isDestroying = true; // Create an array of jewels to destroy const jewelsToDestroy = []; @@ -379,6 +570,7 @@ export class GameScene extends Phaser.Scene { onComplete: () => { jewel.destroy(); this.time.delayedCall(100, () => { + this.isDestroying = false; this.dropJewels(); }); } @@ -392,7 +584,6 @@ export class GameScene extends Phaser.Scene { } this.isDropping = true; - console.log('drop'); // Create a grid representation to track jewel positions const grid = []; @@ -481,4 +672,157 @@ export class GameScene extends Phaser.Scene { }); } + // Function to move all jewels up one row + moveAllJewelsUp() { + this.isMovingUp = true; + // Check if any jewel is at row 1 (topmost) that would go to row 0 + let gameOver = false; + + 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); + + // If jewel is at row 1 and would move up to row 0, game over + if (row === 1 && col >= 1 && col <= this.gridConfig.cols) { + gameOver = true; + } + } + }); + + // If game over, end the game + if (gameOver) { + this.gameOver(); + return; + } + + // Move all jewels up one row + 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); + + // Only move jewels that are not at the top row + if (row > 1 && col >= 1 && col <= this.gridConfig.cols) { + const targetRow = row - 1; + const targetY = targetRow * this.gridConfig.jewelHeight; + + // Animate the jewel moving up + this.tweens.add({ + targets: jewel, + y: targetY, + duration: 300, + ease: 'Linear' + }); + } + } + }); + + // Create new bottom row after moving all jewels up + this.time.delayedCall(300, () => { + this.isMovingUp = false; + this.createBottomRow(); + }); + } + + // Function to create a new row of jewels at the bottom + createBottomRow() { + const newRow = 8; + let jewelDelay = 0; + + for (let col = 1; col <= this.gridConfig.cols; col++) { + let type = null; + let match = false; + + do { + type = Phaser.Math.Between(0, this.numberOfJewels - 1); + match = false; + + // Check horizontal matches in the new row + if (col >= 3) { + // Check if this would create a horizontal match of 3 or more + const leftType = this.getJewelAtPosition(col-1, newRow); + const leftLeftType = this.getJewelAtPosition(col-2, newRow); + + if (leftType === type && leftLeftType === type) { + match = true; + } + } + + // Check vertical matches in the columns above + if (newRow <= this.gridConfig.rows) { + const aboveType = this.getJewelAtPosition(col, newRow - 1); + + if (aboveType === type) { + // Check if there's a match of 3 or more by looking at additional positions + let count = 1; + + // Look up to see how many matching jewels we have in the column + for (let row = newRow - 2; row < this.gridConfig.rows; row++) { + const jewelType = this.getJewelAtPosition(col, row); + if (jewelType === type) { + count++; + } else { + break; + } + } + + // If we have 3 or more in a vertical line, it's a match + if (count >= 3) { + match = true; + } + } + } + + // Also check for matches with jewels above the new row (if they exist) + if (newRow < this.gridConfig.rows && col <= this.gridConfig.cols) { + const jewelAbove = this.getJewelAtPosition(col, newRow - 1); + + if (jewelAbove !== null) { + // Check horizontal match with jewels in the same row + let count = 0; + + // Look left to see how many matching jewels we have in that row + for (let c = col - 1; c >= 1; c--) { + const jewelType = this.getJewelAtPosition(c, newRow); + if (jewelType === type) { + count++; + } else { + break; + } + } + + // Look right to see how many matching jewels we have in that row + for (let c = col + 1; c <= this.gridConfig.cols; c++) { + const jewelType = this.getJewelAtPosition(c, newRow); + if (jewelType === type) { + count++; + } else { + break; + } + } + + // If we have at least 2 matching jewels in the row and one more above, + // or any other match scenario that creates a 3-in-a-row + if (count >= 2 && jewelAbove === type) { + match = true; + } + } + } + + } while (match === true); + + // Create the jewel at the correct position + this.createJewel(type, col, newRow); + } + } + + // Game over function + gameOver() { + console.log('Game Over!'); + // Add game over logic here (e.g., show game over screen, reset game, etc.) + + // For now, just restart the scene + this.scene.restart(); + } } \ No newline at end of file diff --git a/src/config.js b/src/config.js index 8163c13..9eeeb96 100644 --- a/src/config.js +++ b/src/config.js @@ -1,6 +1,17 @@ -export const GRID_CONFIG = { - rows: 8, - cols: 8, - jewelWidth: 100, - jewelHeight: 100, +export const LEVEL_CONFIG = { + 1: { + numberOfJewels: 4, + matchesNeeded: 12, + moveInterval: 12000 + }, + 2: { + numberOfJewels: 5, + matchesNeeded: 12, + moveInterval: 12000 + }, + 3: { + numberOfJewels: 5, + matchesNeeded: 12, + moveInterval: 11000 + } };