diff --git a/assets/alarm.mp3 b/assets/alarm.mp3 new file mode 100644 index 0000000..988cc6d Binary files /dev/null and b/assets/alarm.mp3 differ diff --git a/assets/countdown.mp3 b/assets/countdown.mp3 new file mode 100644 index 0000000..9ade184 Binary files /dev/null and b/assets/countdown.mp3 differ diff --git a/assets/game-over-boom.mp3 b/assets/game-over-boom.mp3 new file mode 100644 index 0000000..41584c2 Binary files /dev/null and b/assets/game-over-boom.mp3 differ diff --git a/assets/goblin-background.mp4 b/assets/goblin-background.mp4 new file mode 100644 index 0000000..a794d7f Binary files /dev/null and b/assets/goblin-background.mp4 differ diff --git a/src/GameScene.js b/src/GameScene.js index d274d12..6c037ce 100644 --- a/src/GameScene.js +++ b/src/GameScene.js @@ -15,11 +15,12 @@ export class GameScene extends Phaser.Scene { // Stats this.numberOfJewels = 4; - this.startRows = 3; + this.startRows = 5; this.level = 1; this.matchesNeeded = 8; this.score = 0; this.ally = 'goblin'; + this.gameStatus = true; // Status Indication this.isDropping = false; @@ -37,6 +38,7 @@ export class GameScene extends Phaser.Scene { this.moveTimer = 0; this.moveInterval = 12000; this.timerText = null; + this.countdownTimer = false; } preload() { @@ -56,6 +58,9 @@ export class GameScene extends Phaser.Scene { this.load.audio('clear', 'assets/clear.mp3'); this.load.audio('level-up', 'assets/level-up.mp3'); this.load.audio('switch', 'assets/switch.mp3'); + this.load.audio('game-over', 'assets/game-over-boom.mp3'); + this.load.audio('countdown', 'assets/countdown.mp3'); + this.load.audio('alarm', 'assets/alarm.mp3'); // Ally Assets const allys = ['goblin']; @@ -64,6 +69,7 @@ export class GameScene extends Phaser.Scene { this.load.video(`${ally}-excited`, 'assets/goblin-excited.mp4'); this.load.video(`${ally}-pleased`, 'assets/goblin-pleased.mp4'); this.load.video(`${ally}-match`, 'assets/goblin-match.mp4'); + this.load.video(`${ally}-background`, `assets/${ally}-background.mp4`); this.load.audio(`${ally}-a1`, 'assets/goblin-a1.mp3'); this.load.audio(`${ally}-a2`, 'assets/goblin-a2.mp3'); this.load.audio(`${ally}-a3`, 'assets/goblin-a3.mp3'); @@ -75,6 +81,16 @@ export class GameScene extends Phaser.Scene { } create() { + // Background Video + this.bgVideo = this.add.video(0, 0, `${this.ally}-background`); + this.bgVideo.setOrigin(0); + console.log("width",this.scale.width); + this.bgVideo.scaleX = this.scale.width / 848; + this.bgVideo.scaleY = this.scale.height / 480; + console.log(this.bgVideo); + this.bgVideo.play(true); + + // Create the Game Grid this.makeGrid(); this.physics.world.setBounds(this.grid.getBounds().x - 50, this.grid.getBounds().y - 50, this.grid.getBounds().width + 100, this.grid.getBounds().height + 100); this.jewels = this.physics.add.group({ @@ -88,6 +104,17 @@ export class GameScene extends Phaser.Scene { this.bgMusic.loop = true; this.bgMusic.play(); + // Create Ally Video + this.allyVideo = this.add.video(350, 610, `${this.ally}-resting`).setOrigin(0.5); + this.allyVideo.play(true); + this.allyVideo.postFX.addGlow(); + this.time.delayedCall(500, () => { + this.sound.play(`${this.ally}-intro`); + }); + + // Create Text Background + this.add.rectangle(10, 40, 650, 270, 0x000000, .5).setOrigin(0); + // Create the score text this.scoreText = this.add.text(20, 50, 'Score: 0', { fontFamily: 'cruiser, arial', @@ -139,12 +166,6 @@ export class GameScene extends Phaser.Scene { bottom: 5 } }).setOrigin(0); - - this.allyVideo = this.add.video(350, 610, `${this.ally}-resting`).setOrigin(0.5); - this.allyVideo.play(true); - this.time.delayedCall(500, () => { - this.sound.play(`${this.ally}-intro`); - }); } update(time, delta) { @@ -152,10 +173,20 @@ export class GameScene extends Phaser.Scene { 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) { + if ( + this.moveTimer >= this.moveInterval && + this.isDropping === false && + this.isSwapping === false && + this.isDestroying === false && + this.gameStatus === true + ) { this.moveAllJewelsUp(); this.moveTimer = 0; // Reset the timer } + + if (this.moveInterval - this.moveTimer <= 3000) { + this.startCountdownTimer(); + } // Update the timer display const timeRemaining = (this.moveInterval - this.moveTimer) / 1000; @@ -165,6 +196,18 @@ export class GameScene extends Phaser.Scene { } } + startCountdownTimer() { + if (this.countdownTimer === true || this.gameStatus === false) { + return; + } + + this.countdownTimer = true; + this.sound.play('countdown'); + this.time.delayedCall(6000, () => { + this.countdownTimer = false; + }); + } + makeGrid() { this.grid = this.add.rectangle( this.gridConfig.leftPadding + this.gridConfig.allPadding, @@ -174,6 +217,10 @@ export class GameScene extends Phaser.Scene { 0x000000, .5 ).setOrigin(0); + this.grid.setInteractive(); + this.grid.on('pointerdown', (pointer) => { + this.handleGridClick(pointer.x, pointer.y); + }); } createJewel(type, col, row) { @@ -250,12 +297,98 @@ export class GameScene extends Phaser.Scene { // Return the jewel type or null if not found return jewelAtPosition ? jewelAtPosition.jewelType : null; } + + // Handle clicks on the grid + handleGridClick(x, y) { + if (!this.selectedJewel) { + return; + } + + const selectedX = this.selectedJewel.x; + const selectedY = this.selectedJewel.y; + + // Calculate which grid tile was clicked + const clickCol = Math.floor((x+50) / this.gridConfig.jewelWidth); + const clickRow = Math.floor((y+50) / this.gridConfig.jewelHeight); + + // Calculate selected jewel's grid position + const selectedCol = Math.floor(selectedX / this.gridConfig.jewelWidth); + const selectedRow = Math.floor(selectedY / this.gridConfig.jewelHeight); + + // Check if click is adjacent (up, down, left, right) + const isAdjacent = + (clickCol === selectedCol && Math.abs(clickRow - selectedRow) === 1) || // Up or down + (clickRow === selectedRow && Math.abs(clickCol - selectedCol) === 1); // Left or right + + if (isAdjacent) { + this.tweens.add({ + targets: this.selectedJewel, + scale: 1, + duration: 200 + }); + this.moveJewel(clickCol * this.gridConfig.jewelWidth, clickRow * this.gridConfig.jewelHeight); + } + } + + // Modified swap function to disable clicks during execution + moveJewel(x ,y) { + if (this.isMovingUp === true) { + this.time.delayedCall(1000, ()=> { + this.moveJewel(x, y); + }) + return; + } + // Set swapping flag to prevent new clicks from being processed + this.isSwapping = true; + this.sound.play('switch'); + + // Disable all jewel interactivity temporarily + this.jewels.children.iterate((jewel) => { + if (jewel) { + jewel.disableInteractive(); + } + }); + + // Perform the swap animation here - currently empty + const fromX = this.selectedJewel.x; + const fromY = this.selectedJewel.y; + const toX = x; + const toY = y; + this.tweens.add({ + targets: this.selectedJewel, + x: toX, + y: toY, + duration: 300, + onComplete: () => { + this.dropJewels(); + this.time.delayedCall(300, () => { + this.checkMatches(); + }) + } + }); + this.selectedJewel = null; + this.swapWithJewel = null; + + + // After swap completes, re-enable interactivity and reset flag + this.time.delayedCall(300, () => { // Adjust delay as needed for animation duration + this.jewels.children.iterate((jewel) => { + if (jewel) { + jewel.setInteractive(); + } + }); + + this.isSwapping = false; + }); + } // New function to handle jewel clicks handleJewelClick(x, y) { // Find which jewel was clicked const clickedJewel = this.getJewelAtWorldPosition(x, y); + //const isAdjacentButEmpty = this.isAdjacentButEmtpy(this.selectedJewel, x, y); + if (!clickedJewel || this.isSwapping) { return; } @@ -351,7 +484,7 @@ export class GameScene extends Phaser.Scene { // Modified swap function to disable clicks during execution swapJewel() { if (this.isMovingUp === true) { - this.time.delayedCall(300, ()=> { + this.time.delayedCall(500, ()=> { this.swapJewel(); }) return; @@ -876,9 +1009,80 @@ export class GameScene extends Phaser.Scene { // Game over function gameOver() { console.log('Game Over!'); - // Add game over logic here (e.g., show game over screen, reset game, etc.) + this.gameStatus = false; + this.sound.play('game-over'); - // For now, just restart the scene - this.scene.restart(); + // Make all jewels bounce off screen + this.jewels.children.iterate((jewel) => { + if (jewel) { + // Enable physics for the jewel if not already enabled + if (!jewel.body) { + this.physics.add.existing(jewel); + } + + // Remove world bounds collision + jewel.body.setCollideWorldBounds(false); + + // Set random velocity for bouncing effect + const angle = Phaser.Math.Between(0, 360); + const speed = Phaser.Math.Between(100, 300); + + jewel.body.setVelocity( + Math.cos(angle) * speed, + Math.sin(angle) * speed + ); + + // Add some rotation for visual effect + jewel.body.angularVelocity = Phaser.Math.Between(-200, 200); + } + }); + + // Create the New Level text + const gameOverText = this.add.text(1150, 250, `Game Over`, { + 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: gameOverText, + scale: { from: 0.5, to: 1.5 }, + alpha: { from: 0, to: 1 }, + duration: 1000, + ease: 'Back.easeOut', + }); + + // Fade out and destroy after 2000ms + this.time.delayedCall(10200, () => { + this.tweens.add({ + targets: gameOverText, + angle: 360, + scale: 0, + alpha: 0, + duration: 500, + onComplete: () => { + gameOverText.destroy(); + this.time.delayedCall(3000, () => { + // Fade out camera + this.tweens.add({ + targets: this.cameras.main, + opacity: 0, + duration: 1500, + ease: 'Linear', + onComplete: () => { + this.scene.restart(); + } + }); + }); + } + }); + }); } } \ No newline at end of file