Big Update

This commit is contained in:
Brian Fertig 2025-08-23 21:15:20 -06:00
parent 53fe2280cb
commit f409413e8d
10 changed files with 367 additions and 12 deletions

BIN
assets/2015 Cruiser.ttf Normal file

Binary file not shown.

Binary file not shown.

BIN
assets/NEUROPOL.ttf Normal file

Binary file not shown.

BIN
assets/cyberGoblin.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 MiB

BIN
assets/goblin-excited.mp4 Normal file

Binary file not shown.

BIN
assets/goblin-match.mp4 Normal file

Binary file not shown.

BIN
assets/goblin-pleased.mp4 Normal file

Binary file not shown.

BIN
assets/goblin-resting.mp4 Normal file

Binary file not shown.

View File

@ -1,3 +1,5 @@
import { LEVEL_CONFIG } from './config.js';
export class GameScene extends Phaser.Scene { export class GameScene extends Phaser.Scene {
constructor() { constructor() {
super({ key: 'GameScene' }); super({ key: 'GameScene' });
@ -11,15 +13,29 @@ export class GameScene extends Phaser.Scene {
jewelHeight: 100, jewelHeight: 100,
}; };
this.numberOfJewels = 5; // Stats
this.startRows = 5; this.numberOfJewels = 4;
this.startRows = 3;
this.level = 1; this.level = 1;
this.levelTime = 60; this.matchesNeeded = 10;
this.score = 0;
this.ally = 'goblin';
// Status Indication
this.isDropping = false; this.isDropping = false;
this.isSwapping = false;
this.isDestroying = false;
this.isMovingUp = false;
this.isPlayingVideo = false;
// Add selectedJewel property // Add selectedJewel property
this.selectedJewel = null; this.selectedJewel = null;
this.swapWithJewel = null; this.swapWithJewel = null;
// Timer properties
this.moveTimer = 0;
this.moveInterval = 12000;
this.timerText = null;
} }
preload() { preload() {
@ -27,6 +43,13 @@ export class GameScene extends Phaser.Scene {
frameWidth: 100, frameWidth: 100,
frameHeight: 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() { create() {
@ -37,10 +60,79 @@ export class GameScene extends Phaser.Scene {
}); });
this.physics.add.collider(this.jewels, this.jewels); this.physics.add.collider(this.jewels, this.jewels);
this.createStart(); 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(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() { makeGrid() {
@ -228,6 +320,12 @@ export class GameScene extends Phaser.Scene {
// Modified swap function to disable clicks during execution // Modified swap function to disable clicks during execution
swapJewel() { swapJewel() {
if (this.isMovingUp === true) {
this.time.delayedCall(300, ()=> {
this.swapJewel();
})
return;
}
// Set swapping flag to prevent new clicks from being processed // Set swapping flag to prevent new clicks from being processed
this.isSwapping = true; this.isSwapping = true;
@ -263,7 +361,7 @@ export class GameScene extends Phaser.Scene {
// After swap completes, re-enable interactivity and reset flag // 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) => { this.jewels.children.iterate((jewel) => {
if (jewel) { if (jewel) {
jewel.setInteractive(); 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 // Function to check for matches and destroy them
checkMatches() { checkMatches() {
const matchedJewels = new Set(); const matchedJewels = new Set();
@ -290,6 +442,7 @@ export class GameScene extends Phaser.Scene {
count++; count++;
} else { } else {
if (count >= 3) { if (count >= 3) {
this.reduceMatches(1);
// Add all jewels in this match to the matched set // Add all jewels in this match to the matched set
for (let i = col - count; i < col; i++) { for (let i = col - count; i < col; i++) {
const key = `${i},${row}`; const key = `${i},${row}`;
@ -303,6 +456,7 @@ export class GameScene extends Phaser.Scene {
// Check for match at the end of row // Check for match at the end of row
if (count >= 3) { if (count >= 3) {
this.reduceMatches(1);
for (let i = this.gridConfig.cols - count + 1; i <= this.gridConfig.cols; i++) { for (let i = this.gridConfig.cols - count + 1; i <= this.gridConfig.cols; i++) {
const key = `${i},${row}`; const key = `${i},${row}`;
matchedJewels.add(key); matchedJewels.add(key);
@ -322,6 +476,7 @@ export class GameScene extends Phaser.Scene {
count++; count++;
} else { } else {
if (count >= 3) { if (count >= 3) {
this.reduceMatches(1);
// Add all jewels in this match to the matched set // Add all jewels in this match to the matched set
for (let i = row - count; i < row; i++) { for (let i = row - count; i < row; i++) {
const key = `${col},${i}`; const key = `${col},${i}`;
@ -335,6 +490,7 @@ export class GameScene extends Phaser.Scene {
// Check for match at the end of column // Check for match at the end of column
if (count >= 3) { if (count >= 3) {
this.reduceMatches(1);
for (let i = this.gridConfig.rows - count + 1; i <= this.gridConfig.rows; i++) { for (let i = this.gridConfig.rows - count + 1; i <= this.gridConfig.rows; i++) {
const key = `${col},${i}`; const key = `${col},${i}`;
matchedJewels.add(key); 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) { 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); this.destroyMatchedJewels(matchedJewels);
return true; return true;
} }
@ -351,8 +513,37 @@ export class GameScene extends Phaser.Scene {
return false; 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 // Function to destroy matched jewels
destroyMatchedJewels(matchedJewels) { destroyMatchedJewels(matchedJewels) {
if (this.isDestroying) {
return;
}
this.isDestroying = true;
// Create an array of jewels to destroy // Create an array of jewels to destroy
const jewelsToDestroy = []; const jewelsToDestroy = [];
@ -379,6 +570,7 @@ export class GameScene extends Phaser.Scene {
onComplete: () => { onComplete: () => {
jewel.destroy(); jewel.destroy();
this.time.delayedCall(100, () => { this.time.delayedCall(100, () => {
this.isDestroying = false;
this.dropJewels(); this.dropJewels();
}); });
} }
@ -392,7 +584,6 @@ export class GameScene extends Phaser.Scene {
} }
this.isDropping = true; this.isDropping = true;
console.log('drop');
// Create a grid representation to track jewel positions // Create a grid representation to track jewel positions
const grid = []; 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();
}
} }

View File

@ -1,6 +1,17 @@
export const GRID_CONFIG = { export const LEVEL_CONFIG = {
rows: 8, 1: {
cols: 8, numberOfJewels: 4,
jewelWidth: 100, matchesNeeded: 12,
jewelHeight: 100, moveInterval: 12000
},
2: {
numberOfJewels: 5,
matchesNeeded: 12,
moveInterval: 12000
},
3: {
numberOfJewels: 5,
matchesNeeded: 12,
moveInterval: 11000
}
}; };