/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var Gem = Container.expand(function () {
var self = Container.call(this);
self.init = function (type, row, col) {
self.gemType = type;
self.row = row;
self.col = col;
self.isMatched = false;
self.isAnimating = false;
var gemAssets = ['gem_red', 'gem_blue', 'gem_green', 'gem_yellow', 'gem_purple', 'gem_orange'];
var gemGraphics = self.attachAsset(gemAssets[type], {
anchorX: 0.5,
anchorY: 0.5
});
self.x = col * GEM_SIZE + GEM_SIZE / 2 + BOARD_OFFSET_X;
self.y = row * GEM_SIZE + GEM_SIZE / 2 + BOARD_OFFSET_Y;
};
self.animateTo = function (targetX, targetY, duration, onComplete) {
self.isAnimating = true;
tween(self, {
x: targetX,
y: targetY
}, {
duration: duration || 300,
easing: tween.easeOut,
onFinish: function onFinish() {
self.isAnimating = false;
if (onComplete) onComplete();
}
});
};
self.explode = function () {
// Create more particles for dramatic effect
var particleCount = 12 + Math.floor(Math.random() * 8);
for (var i = 0; i < particleCount; i++) {
var particle = new Particle();
particle.x = self.x;
particle.y = self.y;
particle.explode(Math.random() * Math.PI * 2, 150 + Math.random() * 200);
game.addChild(particle);
particles.push(particle);
}
// Add dramatic gem explosion animation with bounce
tween(self, {
scaleX: 2.5,
scaleY: 2.5,
rotation: Math.PI * 2
}, {
duration: 100,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(self, {
scaleX: 0,
scaleY: 0,
alpha: 0
}, {
duration: 150,
easing: tween.easeIn
});
}
});
// Add color flash effect
var originalTint = self.children[0].tint || 0xFFFFFF;
tween(self.children[0], {
tint: 0xFFFFFF
}, {
duration: 50,
onFinish: function onFinish() {
tween(self.children[0], {
tint: originalTint
}, {
duration: 100
});
}
});
};
return self;
});
var Particle = Container.expand(function () {
var self = Container.call(this);
var particleGraphics = self.attachAsset('particle', {
anchorX: 0.5,
anchorY: 0.5
});
self.velocityX = 0;
self.velocityY = 0;
self.life = 60;
self.maxLife = 60;
self.explode = function (angle, speed) {
self.velocityX = Math.cos(angle) * speed;
self.velocityY = Math.sin(angle) * speed;
};
self.update = function () {
self.x += self.velocityX;
self.y += self.velocityY;
self.velocityX *= 0.95;
self.velocityY *= 0.95;
self.velocityY += 2; // gravity
self.life--;
self.alpha = self.life / self.maxLife;
if (self.life <= 0) {
self.destroy();
for (var i = particles.length - 1; i >= 0; i--) {
if (particles[i] === self) {
particles.splice(i, 1);
break;
}
}
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x1a1a2e
});
/****
* Game Code
****/
var GRID_ROWS = 10;
var GRID_COLS = 8;
var GEM_SIZE = 256;
var BOARD_OFFSET_X = (2048 - GRID_COLS * GEM_SIZE) / 2;
var BOARD_OFFSET_Y = 100;
var grid = [];
var selectedGem = null;
var isProcessing = false;
var particles = [];
var comboMultiplier = 1;
var shakeIntensity = 0;
var shakeDecay = 0.9;
// Initialize score display
var scoreTxt = new Text2('0', {
size: 120,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
scoreTxt.y = 50;
// Initialize combo display
var comboTxt = new Text2('', {
size: 80,
fill: 0xFFFF00
});
comboTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(comboTxt);
comboTxt.y = 180;
function initializeGrid() {
for (var row = 0; row < GRID_ROWS; row++) {
grid[row] = [];
for (var col = 0; col < GRID_COLS; col++) {
var gemType;
do {
gemType = Math.floor(Math.random() * 4); // Start with 4 gem types
} while (wouldCreateMatch(row, col, gemType));
var gem = new Gem();
gem.init(gemType, row, col);
grid[row][col] = gem;
game.addChild(gem);
}
}
}
function wouldCreateMatch(row, col, gemType) {
// Check horizontal match
var horizontalCount = 1;
if (col >= 2 && grid[row][col - 1] && grid[row][col - 2] && grid[row][col - 1].gemType === gemType && grid[row][col - 2].gemType === gemType) {
return true;
}
// Check vertical match
if (row >= 2 && grid[row - 1][col] && grid[row - 2][col] && grid[row - 1][col].gemType === gemType && grid[row - 2][col].gemType === gemType) {
return true;
}
return false;
}
function getGemAt(x, y) {
var col = Math.floor((x - BOARD_OFFSET_X) / GEM_SIZE);
var row = Math.floor((y - BOARD_OFFSET_Y) / GEM_SIZE);
if (row >= 0 && row < GRID_ROWS && col >= 0 && col < GRID_COLS) {
return grid[row][col];
}
return null;
}
function areAdjacent(gem1, gem2) {
var rowDiff = Math.abs(gem1.row - gem2.row);
var colDiff = Math.abs(gem1.col - gem2.col);
return rowDiff === 1 && colDiff === 0 || rowDiff === 0 && colDiff === 1;
}
function swapGems(gem1, gem2) {
var tempRow = gem1.row;
var tempCol = gem1.col;
var tempX = gem1.x;
var tempY = gem1.y;
gem1.row = gem2.row;
gem1.col = gem2.col;
gem2.row = tempRow;
gem2.col = tempCol;
grid[gem1.row][gem1.col] = gem1;
grid[gem2.row][gem2.col] = gem2;
gem1.animateTo(gem2.x, gem2.y, 200);
gem2.animateTo(tempX, tempY, 200);
}
function findMatches() {
var matches = [];
// Check horizontal matches
for (var row = 0; row < GRID_ROWS; row++) {
var count = 1;
var currentType = grid[row][0].gemType;
for (var col = 1; col < GRID_COLS; col++) {
if (grid[row][col].gemType === currentType) {
count++;
} else {
if (count >= 3) {
for (var i = col - count; i < col; i++) {
matches.push(grid[row][i]);
}
}
count = 1;
currentType = grid[row][col].gemType;
}
}
if (count >= 3) {
for (var i = col - count; i < col; i++) {
matches.push(grid[row][i]);
}
}
}
// Check vertical matches
for (var col = 0; col < GRID_COLS; col++) {
var count = 1;
var currentType = grid[0][col].gemType;
for (var row = 1; row < GRID_ROWS; row++) {
if (grid[row][col].gemType === currentType) {
count++;
} else {
if (count >= 3) {
for (var i = row - count; i < row; i++) {
if (matches.indexOf(grid[i][col]) === -1) {
matches.push(grid[i][col]);
}
}
}
count = 1;
currentType = grid[row][col].gemType;
}
}
if (count >= 3) {
for (var i = row - count; i < row; i++) {
if (matches.indexOf(grid[i][col]) === -1) {
matches.push(grid[i][col]);
}
}
}
}
return matches;
}
function removeMatches(matches) {
var points = matches.length * 10 * comboMultiplier;
LK.setScore(LK.getScore() + points);
scoreTxt.setText(LK.getScore());
// Update combo display with dramatic effects
if (comboMultiplier > 1) {
comboTxt.setText('COMBO x' + comboMultiplier);
// Scale up dramatically based on combo multiplier
var maxScale = Math.min(1 + comboMultiplier * 0.2, 2.5);
comboTxt.scaleX = 0.1;
comboTxt.scaleY = 0.1;
tween(comboTxt, {
scaleX: maxScale,
scaleY: maxScale,
tint: comboMultiplier > 5 ? 0xFF0000 : comboMultiplier > 3 ? 0xFF8800 : 0xFFFF00
}, {
duration: 300,
easing: tween.elasticOut,
onFinish: function onFinish() {
tween(comboTxt, {
scaleX: 1,
scaleY: 1,
alpha: 0,
tint: 0xFFFFFF
}, {
duration: 1200,
easing: tween.easeOut,
onFinish: function onFinish() {
comboTxt.setText('');
comboTxt.alpha = 1;
}
});
}
});
}
// Screen shake based on match size with dramatic scaling
shakeIntensity = Math.min(matches.length * 5, 40);
// Add brief screen flash effect for large matches
if (matches.length >= 5) {
LK.effects.flashScreen(0xFFFFFF, 500);
} else if (matches.length >= 4) {
LK.effects.flashScreen(0xFFFF00, 300);
}
// Add zoom effect for large combos
if (comboMultiplier > 3) {
tween(game, {
scaleX: 1.05,
scaleY: 1.05
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(game, {
scaleX: 1,
scaleY: 1
}, {
duration: 300,
easing: tween.elasticOut
});
}
});
}
// Play sound
if (comboMultiplier > 1) {
LK.getSound('combo').play();
} else {
LK.getSound('match').play();
}
for (var i = 0; i < matches.length; i++) {
var gem = matches[i];
gem.explode();
gem.isMatched = true;
LK.setTimeout(function (gemToRemove) {
return function () {
gemToRemove.destroy();
grid[gemToRemove.row][gemToRemove.col] = null;
};
}(gem), 200);
}
comboMultiplier++;
}
function applyGravity() {
var hasMoved = false;
for (var col = 0; col < GRID_COLS; col++) {
for (var row = GRID_ROWS - 2; row >= 0; row--) {
if (grid[row][col] && !grid[row][col].isMatched) {
var fallDistance = 0;
for (var checkRow = row + 1; checkRow < GRID_ROWS; checkRow++) {
if (grid[checkRow][col] === null) {
fallDistance++;
} else {
break;
}
}
if (fallDistance > 0) {
var gem = grid[row][col];
grid[row][col] = null;
gem.row = row + fallDistance;
grid[gem.row][col] = gem;
var targetY = gem.row * GEM_SIZE + GEM_SIZE / 2 + BOARD_OFFSET_Y;
// Add bouncy fall animation with trail effect
gem.animateTo(gem.x, targetY, 400 + fallDistance * 50);
// Add falling particle trail
if (fallDistance > 2) {
for (var p = 0; p < 3; p++) {
LK.setTimeout(function (gemRef) {
return function () {
var trailParticle = new Particle();
trailParticle.x = gemRef.x + (Math.random() - 0.5) * 50;
trailParticle.y = gemRef.y;
trailParticle.explode(Math.PI / 2 + (Math.random() - 0.5) * 0.5, 50 + Math.random() * 50);
game.addChild(trailParticle);
particles.push(trailParticle);
};
}(gem), p * 50);
}
}
hasMoved = true;
}
}
}
}
return hasMoved;
}
function fillEmptySpaces() {
for (var col = 0; col < GRID_COLS; col++) {
for (var row = 0; row < GRID_ROWS; row++) {
if (grid[row][col] === null) {
var gemTypes = Math.min(4 + Math.floor(LK.getScore() / 500), 6); // Progressive difficulty
var gemType = Math.floor(Math.random() * gemTypes);
var gem = new Gem();
gem.init(gemType, row, col);
gem.y = -GEM_SIZE;
grid[row][col] = gem;
game.addChild(gem);
var targetY = row * GEM_SIZE + GEM_SIZE / 2 + BOARD_OFFSET_Y;
gem.animateTo(gem.x, targetY, 400);
}
}
}
}
function processMatches() {
if (isProcessing) return;
isProcessing = true;
LK.setTimeout(function () {
var matches = findMatches();
if (matches.length > 0) {
removeMatches(matches);
LK.setTimeout(function () {
var moved = applyGravity();
fillEmptySpaces();
LK.setTimeout(function () {
isProcessing = false;
processMatches(); // Check for new matches
}, 500);
}, 300);
} else {
comboMultiplier = 1;
isProcessing = false;
}
}, 100);
}
game.down = function (x, y, obj) {
if (isProcessing) return;
var gem = getGemAt(x, y);
if (gem && !gem.isAnimating) {
if (selectedGem === null) {
selectedGem = gem;
// Add pulsing glow effect for selected gem
tween(gem, {
scaleX: 1.2,
scaleY: 1.2,
tint: 0xFFFFFF
}, {
duration: 300,
easing: tween.easeInOut
});
// Create continuous pulsing effect
var _pulseGem = function pulseGem() {
if (selectedGem === gem) {
tween(gem, {
scaleX: 1.3,
scaleY: 1.3
}, {
duration: 400,
easing: tween.easeInOut,
onFinish: function onFinish() {
if (selectedGem === gem) {
tween(gem, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 400,
easing: tween.easeInOut,
onFinish: _pulseGem
});
}
}
});
}
};
_pulseGem();
} else if (selectedGem === gem) {
tween(gem, {
scaleX: 1,
scaleY: 1
}, {
duration: 200,
easing: tween.easeOut
});
selectedGem = null;
} else if (areAdjacent(selectedGem, gem)) {
var firstGem = selectedGem;
tween(firstGem, {
scaleX: 1,
scaleY: 1
}, {
duration: 200,
easing: tween.easeOut
});
swapGems(firstGem, gem);
selectedGem = null;
LK.setTimeout(function () {
var matches = findMatches();
if (matches.length === 0) {
// Invalid move, swap back
swapGems(firstGem, gem);
} else {
processMatches();
}
}, 250);
} else {
tween(selectedGem, {
scaleX: 1,
scaleY: 1
}, {
duration: 200,
easing: tween.easeOut
});
selectedGem = gem;
tween(gem, {
scaleX: 1.1,
scaleY: 1.1
}, {
duration: 200,
easing: tween.easeOut
});
}
}
};
game.update = function () {
// Update particles
for (var i = particles.length - 1; i >= 0; i--) {
particles[i].update();
}
// Apply screen shake
if (shakeIntensity > 0.1) {
var shakeX = (Math.random() - 0.5) * shakeIntensity;
var shakeY = (Math.random() - 0.5) * shakeIntensity;
game.x = shakeX;
game.y = shakeY;
shakeIntensity *= shakeDecay;
} else {
game.x = 0;
game.y = 0;
shakeIntensity = 0;
}
};
// Initialize the game
initializeGrid(); /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var Gem = Container.expand(function () {
var self = Container.call(this);
self.init = function (type, row, col) {
self.gemType = type;
self.row = row;
self.col = col;
self.isMatched = false;
self.isAnimating = false;
var gemAssets = ['gem_red', 'gem_blue', 'gem_green', 'gem_yellow', 'gem_purple', 'gem_orange'];
var gemGraphics = self.attachAsset(gemAssets[type], {
anchorX: 0.5,
anchorY: 0.5
});
self.x = col * GEM_SIZE + GEM_SIZE / 2 + BOARD_OFFSET_X;
self.y = row * GEM_SIZE + GEM_SIZE / 2 + BOARD_OFFSET_Y;
};
self.animateTo = function (targetX, targetY, duration, onComplete) {
self.isAnimating = true;
tween(self, {
x: targetX,
y: targetY
}, {
duration: duration || 300,
easing: tween.easeOut,
onFinish: function onFinish() {
self.isAnimating = false;
if (onComplete) onComplete();
}
});
};
self.explode = function () {
// Create more particles for dramatic effect
var particleCount = 12 + Math.floor(Math.random() * 8);
for (var i = 0; i < particleCount; i++) {
var particle = new Particle();
particle.x = self.x;
particle.y = self.y;
particle.explode(Math.random() * Math.PI * 2, 150 + Math.random() * 200);
game.addChild(particle);
particles.push(particle);
}
// Add dramatic gem explosion animation with bounce
tween(self, {
scaleX: 2.5,
scaleY: 2.5,
rotation: Math.PI * 2
}, {
duration: 100,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(self, {
scaleX: 0,
scaleY: 0,
alpha: 0
}, {
duration: 150,
easing: tween.easeIn
});
}
});
// Add color flash effect
var originalTint = self.children[0].tint || 0xFFFFFF;
tween(self.children[0], {
tint: 0xFFFFFF
}, {
duration: 50,
onFinish: function onFinish() {
tween(self.children[0], {
tint: originalTint
}, {
duration: 100
});
}
});
};
return self;
});
var Particle = Container.expand(function () {
var self = Container.call(this);
var particleGraphics = self.attachAsset('particle', {
anchorX: 0.5,
anchorY: 0.5
});
self.velocityX = 0;
self.velocityY = 0;
self.life = 60;
self.maxLife = 60;
self.explode = function (angle, speed) {
self.velocityX = Math.cos(angle) * speed;
self.velocityY = Math.sin(angle) * speed;
};
self.update = function () {
self.x += self.velocityX;
self.y += self.velocityY;
self.velocityX *= 0.95;
self.velocityY *= 0.95;
self.velocityY += 2; // gravity
self.life--;
self.alpha = self.life / self.maxLife;
if (self.life <= 0) {
self.destroy();
for (var i = particles.length - 1; i >= 0; i--) {
if (particles[i] === self) {
particles.splice(i, 1);
break;
}
}
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x1a1a2e
});
/****
* Game Code
****/
var GRID_ROWS = 10;
var GRID_COLS = 8;
var GEM_SIZE = 256;
var BOARD_OFFSET_X = (2048 - GRID_COLS * GEM_SIZE) / 2;
var BOARD_OFFSET_Y = 100;
var grid = [];
var selectedGem = null;
var isProcessing = false;
var particles = [];
var comboMultiplier = 1;
var shakeIntensity = 0;
var shakeDecay = 0.9;
// Initialize score display
var scoreTxt = new Text2('0', {
size: 120,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
scoreTxt.y = 50;
// Initialize combo display
var comboTxt = new Text2('', {
size: 80,
fill: 0xFFFF00
});
comboTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(comboTxt);
comboTxt.y = 180;
function initializeGrid() {
for (var row = 0; row < GRID_ROWS; row++) {
grid[row] = [];
for (var col = 0; col < GRID_COLS; col++) {
var gemType;
do {
gemType = Math.floor(Math.random() * 4); // Start with 4 gem types
} while (wouldCreateMatch(row, col, gemType));
var gem = new Gem();
gem.init(gemType, row, col);
grid[row][col] = gem;
game.addChild(gem);
}
}
}
function wouldCreateMatch(row, col, gemType) {
// Check horizontal match
var horizontalCount = 1;
if (col >= 2 && grid[row][col - 1] && grid[row][col - 2] && grid[row][col - 1].gemType === gemType && grid[row][col - 2].gemType === gemType) {
return true;
}
// Check vertical match
if (row >= 2 && grid[row - 1][col] && grid[row - 2][col] && grid[row - 1][col].gemType === gemType && grid[row - 2][col].gemType === gemType) {
return true;
}
return false;
}
function getGemAt(x, y) {
var col = Math.floor((x - BOARD_OFFSET_X) / GEM_SIZE);
var row = Math.floor((y - BOARD_OFFSET_Y) / GEM_SIZE);
if (row >= 0 && row < GRID_ROWS && col >= 0 && col < GRID_COLS) {
return grid[row][col];
}
return null;
}
function areAdjacent(gem1, gem2) {
var rowDiff = Math.abs(gem1.row - gem2.row);
var colDiff = Math.abs(gem1.col - gem2.col);
return rowDiff === 1 && colDiff === 0 || rowDiff === 0 && colDiff === 1;
}
function swapGems(gem1, gem2) {
var tempRow = gem1.row;
var tempCol = gem1.col;
var tempX = gem1.x;
var tempY = gem1.y;
gem1.row = gem2.row;
gem1.col = gem2.col;
gem2.row = tempRow;
gem2.col = tempCol;
grid[gem1.row][gem1.col] = gem1;
grid[gem2.row][gem2.col] = gem2;
gem1.animateTo(gem2.x, gem2.y, 200);
gem2.animateTo(tempX, tempY, 200);
}
function findMatches() {
var matches = [];
// Check horizontal matches
for (var row = 0; row < GRID_ROWS; row++) {
var count = 1;
var currentType = grid[row][0].gemType;
for (var col = 1; col < GRID_COLS; col++) {
if (grid[row][col].gemType === currentType) {
count++;
} else {
if (count >= 3) {
for (var i = col - count; i < col; i++) {
matches.push(grid[row][i]);
}
}
count = 1;
currentType = grid[row][col].gemType;
}
}
if (count >= 3) {
for (var i = col - count; i < col; i++) {
matches.push(grid[row][i]);
}
}
}
// Check vertical matches
for (var col = 0; col < GRID_COLS; col++) {
var count = 1;
var currentType = grid[0][col].gemType;
for (var row = 1; row < GRID_ROWS; row++) {
if (grid[row][col].gemType === currentType) {
count++;
} else {
if (count >= 3) {
for (var i = row - count; i < row; i++) {
if (matches.indexOf(grid[i][col]) === -1) {
matches.push(grid[i][col]);
}
}
}
count = 1;
currentType = grid[row][col].gemType;
}
}
if (count >= 3) {
for (var i = row - count; i < row; i++) {
if (matches.indexOf(grid[i][col]) === -1) {
matches.push(grid[i][col]);
}
}
}
}
return matches;
}
function removeMatches(matches) {
var points = matches.length * 10 * comboMultiplier;
LK.setScore(LK.getScore() + points);
scoreTxt.setText(LK.getScore());
// Update combo display with dramatic effects
if (comboMultiplier > 1) {
comboTxt.setText('COMBO x' + comboMultiplier);
// Scale up dramatically based on combo multiplier
var maxScale = Math.min(1 + comboMultiplier * 0.2, 2.5);
comboTxt.scaleX = 0.1;
comboTxt.scaleY = 0.1;
tween(comboTxt, {
scaleX: maxScale,
scaleY: maxScale,
tint: comboMultiplier > 5 ? 0xFF0000 : comboMultiplier > 3 ? 0xFF8800 : 0xFFFF00
}, {
duration: 300,
easing: tween.elasticOut,
onFinish: function onFinish() {
tween(comboTxt, {
scaleX: 1,
scaleY: 1,
alpha: 0,
tint: 0xFFFFFF
}, {
duration: 1200,
easing: tween.easeOut,
onFinish: function onFinish() {
comboTxt.setText('');
comboTxt.alpha = 1;
}
});
}
});
}
// Screen shake based on match size with dramatic scaling
shakeIntensity = Math.min(matches.length * 5, 40);
// Add brief screen flash effect for large matches
if (matches.length >= 5) {
LK.effects.flashScreen(0xFFFFFF, 500);
} else if (matches.length >= 4) {
LK.effects.flashScreen(0xFFFF00, 300);
}
// Add zoom effect for large combos
if (comboMultiplier > 3) {
tween(game, {
scaleX: 1.05,
scaleY: 1.05
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(game, {
scaleX: 1,
scaleY: 1
}, {
duration: 300,
easing: tween.elasticOut
});
}
});
}
// Play sound
if (comboMultiplier > 1) {
LK.getSound('combo').play();
} else {
LK.getSound('match').play();
}
for (var i = 0; i < matches.length; i++) {
var gem = matches[i];
gem.explode();
gem.isMatched = true;
LK.setTimeout(function (gemToRemove) {
return function () {
gemToRemove.destroy();
grid[gemToRemove.row][gemToRemove.col] = null;
};
}(gem), 200);
}
comboMultiplier++;
}
function applyGravity() {
var hasMoved = false;
for (var col = 0; col < GRID_COLS; col++) {
for (var row = GRID_ROWS - 2; row >= 0; row--) {
if (grid[row][col] && !grid[row][col].isMatched) {
var fallDistance = 0;
for (var checkRow = row + 1; checkRow < GRID_ROWS; checkRow++) {
if (grid[checkRow][col] === null) {
fallDistance++;
} else {
break;
}
}
if (fallDistance > 0) {
var gem = grid[row][col];
grid[row][col] = null;
gem.row = row + fallDistance;
grid[gem.row][col] = gem;
var targetY = gem.row * GEM_SIZE + GEM_SIZE / 2 + BOARD_OFFSET_Y;
// Add bouncy fall animation with trail effect
gem.animateTo(gem.x, targetY, 400 + fallDistance * 50);
// Add falling particle trail
if (fallDistance > 2) {
for (var p = 0; p < 3; p++) {
LK.setTimeout(function (gemRef) {
return function () {
var trailParticle = new Particle();
trailParticle.x = gemRef.x + (Math.random() - 0.5) * 50;
trailParticle.y = gemRef.y;
trailParticle.explode(Math.PI / 2 + (Math.random() - 0.5) * 0.5, 50 + Math.random() * 50);
game.addChild(trailParticle);
particles.push(trailParticle);
};
}(gem), p * 50);
}
}
hasMoved = true;
}
}
}
}
return hasMoved;
}
function fillEmptySpaces() {
for (var col = 0; col < GRID_COLS; col++) {
for (var row = 0; row < GRID_ROWS; row++) {
if (grid[row][col] === null) {
var gemTypes = Math.min(4 + Math.floor(LK.getScore() / 500), 6); // Progressive difficulty
var gemType = Math.floor(Math.random() * gemTypes);
var gem = new Gem();
gem.init(gemType, row, col);
gem.y = -GEM_SIZE;
grid[row][col] = gem;
game.addChild(gem);
var targetY = row * GEM_SIZE + GEM_SIZE / 2 + BOARD_OFFSET_Y;
gem.animateTo(gem.x, targetY, 400);
}
}
}
}
function processMatches() {
if (isProcessing) return;
isProcessing = true;
LK.setTimeout(function () {
var matches = findMatches();
if (matches.length > 0) {
removeMatches(matches);
LK.setTimeout(function () {
var moved = applyGravity();
fillEmptySpaces();
LK.setTimeout(function () {
isProcessing = false;
processMatches(); // Check for new matches
}, 500);
}, 300);
} else {
comboMultiplier = 1;
isProcessing = false;
}
}, 100);
}
game.down = function (x, y, obj) {
if (isProcessing) return;
var gem = getGemAt(x, y);
if (gem && !gem.isAnimating) {
if (selectedGem === null) {
selectedGem = gem;
// Add pulsing glow effect for selected gem
tween(gem, {
scaleX: 1.2,
scaleY: 1.2,
tint: 0xFFFFFF
}, {
duration: 300,
easing: tween.easeInOut
});
// Create continuous pulsing effect
var _pulseGem = function pulseGem() {
if (selectedGem === gem) {
tween(gem, {
scaleX: 1.3,
scaleY: 1.3
}, {
duration: 400,
easing: tween.easeInOut,
onFinish: function onFinish() {
if (selectedGem === gem) {
tween(gem, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 400,
easing: tween.easeInOut,
onFinish: _pulseGem
});
}
}
});
}
};
_pulseGem();
} else if (selectedGem === gem) {
tween(gem, {
scaleX: 1,
scaleY: 1
}, {
duration: 200,
easing: tween.easeOut
});
selectedGem = null;
} else if (areAdjacent(selectedGem, gem)) {
var firstGem = selectedGem;
tween(firstGem, {
scaleX: 1,
scaleY: 1
}, {
duration: 200,
easing: tween.easeOut
});
swapGems(firstGem, gem);
selectedGem = null;
LK.setTimeout(function () {
var matches = findMatches();
if (matches.length === 0) {
// Invalid move, swap back
swapGems(firstGem, gem);
} else {
processMatches();
}
}, 250);
} else {
tween(selectedGem, {
scaleX: 1,
scaleY: 1
}, {
duration: 200,
easing: tween.easeOut
});
selectedGem = gem;
tween(gem, {
scaleX: 1.1,
scaleY: 1.1
}, {
duration: 200,
easing: tween.easeOut
});
}
}
};
game.update = function () {
// Update particles
for (var i = particles.length - 1; i >= 0; i--) {
particles[i].update();
}
// Apply screen shake
if (shakeIntensity > 0.1) {
var shakeX = (Math.random() - 0.5) * shakeIntensity;
var shakeY = (Math.random() - 0.5) * shakeIntensity;
game.x = shakeX;
game.y = shakeY;
shakeIntensity *= shakeDecay;
} else {
game.x = 0;
game.y = 0;
shakeIntensity = 0;
}
};
// Initialize the game
initializeGrid();