/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ var Candy = Container.expand(function (type) { var self = Container.call(this); self.type = type; self.gridX = 0; self.gridY = 0; self.isMatched = false; var candyAsset = self.attachAsset('candy' + type, { anchorX: 0.5, anchorY: 0.5 }); self.setGridPosition = function (gridX, gridY) { self.gridX = gridX; self.gridY = gridY; self.x = GRID_START_X + gridX * CELL_SIZE + CELL_SIZE / 2; self.y = GRID_START_Y + gridY * CELL_SIZE + CELL_SIZE / 2; }; self.animateToGridPosition = function (gridX, gridY, duration) { self.gridX = gridX; self.gridY = gridY; var targetX = GRID_START_X + gridX * CELL_SIZE + CELL_SIZE / 2; var targetY = GRID_START_Y + gridY * CELL_SIZE + CELL_SIZE / 2; tween(self, { x: targetX, y: targetY }, { duration: duration || 300, easing: tween.easeOut }); }; self.highlight = function () { tween(self, { scaleX: 1.2, scaleY: 1.2 }, { duration: 200, easing: tween.easeOut }); }; self.unhighlight = function () { tween(self, { scaleX: 1, scaleY: 1 }, { duration: 200, easing: tween.easeOut }); }; self.markForRemoval = function () { self.isMatched = true; tween(self, { scaleX: 0, scaleY: 0, alpha: 0 }, { duration: 250, easing: tween.easeIn }); }; return self; }); var ExplosivePowerUp = Container.expand(function () { var self = Container.call(this); // Create explosive visual effect using Power asset with different tint var powerUpAsset = self.attachAsset('Power', { anchorX: 0.5, anchorY: 0.5 }); powerUpAsset.tint = 0xFF0000; // Red tint for explosive power-up // Add pulsing animation self.pulseAnimation = function () { tween(self, { scaleX: 1.4, scaleY: 1.4 }, { duration: 600, easing: tween.easeInOut, onFinish: function onFinish() { tween(self, { scaleX: 1, scaleY: 1 }, { duration: 600, easing: tween.easeInOut, onFinish: function onFinish() { if (self.parent) self.pulseAnimation(); } }); } }); }; // Start pulsing immediately self.pulseAnimation(); // Handle click on power-up self.down = function (x, y, obj) { self.activate(); }; self.activate = function () { // Create explosion effect at power-up location var explosion = game.addChild(LK.getAsset('Exp', { anchorX: 0.5, anchorY: 0.5 })); explosion.x = self.x; explosion.y = self.y; explosion.scaleX = 0.1; explosion.scaleY = 0.1; explosion.alpha = 0.8; // Animate explosion tween(explosion, { scaleX: 6, scaleY: 6, alpha: 0, rotation: Math.PI * 3 }, { duration: 600, easing: tween.easeOut, onFinish: function onFinish() { explosion.destroy(); } }); // Explode 5x5 random area self.explode5x5Area(); // Remove power-up var index = powerUps.indexOf(self); if (index > -1) { powerUps.splice(index, 1); } // Update power-up counter display powerUpCountText.setText('Power-ups: ' + powerUps.length); self.destroy(); }; self.explode5x5Area = function () { // Find a random 5x5 area on the grid var startX = Math.floor(Math.random() * (GRID_SIZE - 4)); // Ensure 5x5 fits var startY = Math.floor(Math.random() * (GRID_SIZE - 4)); var candiesToExplode = []; // Collect all candies in the 5x5 area for (var i = 0; i < 5; i++) { for (var j = 0; j < 5; j++) { var gridX = startX + i; var gridY = startY + j; if (grid[gridX][gridY] && !grid[gridX][gridY].isMatched) { candiesToExplode.push(grid[gridX][gridY]); } } } // Explode all candies in the area with staggered timing for (var i = 0; i < candiesToExplode.length; i++) { (function (candy, delay) { LK.setTimeout(function () { // Create explosion effect var explosion = game.addChild(LK.getAsset('Exp', { anchorX: 0.5, anchorY: 0.5 })); explosion.x = candy.x; explosion.y = candy.y; explosion.scaleX = 0.1; explosion.scaleY = 0.1; explosion.alpha = 0.8; // Animate explosion tween(explosion, { scaleX: 3, scaleY: 3, alpha: 0, rotation: Math.PI * 2 }, { duration: 300, easing: tween.easeOut, onFinish: function onFinish() { explosion.destroy(); } }); // Remove candy from grid candy.alpha = 0; grid[candy.gridX][candy.gridY] = null; // Remove from candies array var index = candies.indexOf(candy); if (index > -1) { candies.splice(index, 1); } // Destroy candy after explosion LK.setTimeout(function () { candy.destroy(); }, 300); }, delay); })(candiesToExplode[i], i * 50); } // Process gravity and refill after all explosions LK.setTimeout(function () { // Add score for power-up activation gameScore += 500; scoreText.setText('Score: ' + gameScore); LK.setScore(gameScore); // Process the board LK.setTimeout(function () { applyGravity(); fillEmptySpaces(); LK.setTimeout(function () { processMatches(); }, 300); }, 100); }, candiesToExplode.length * 50 + 300); }; return self; }); var FirePowerUp = Container.expand(function () { var self = Container.call(this); // Create fiery visual effect using Power asset var powerUpAsset = self.attachAsset('Power', { anchorX: 0.5, anchorY: 0.5 }); // Add pulsing animation self.pulseAnimation = function () { tween(self, { scaleX: 1.3, scaleY: 1.3 }, { duration: 800, easing: tween.easeInOut, onFinish: function onFinish() { tween(self, { scaleX: 1, scaleY: 1 }, { duration: 800, easing: tween.easeInOut, onFinish: function onFinish() { if (self.parent) self.pulseAnimation(); } }); } }); }; // Start pulsing immediately self.pulseAnimation(); // Handle click on power-up self.down = function (x, y, obj) { self.activate(); }; self.activate = function () { // Create explosion effect at power-up location var explosion = game.addChild(LK.getAsset('Exp', { anchorX: 0.5, anchorY: 0.5 })); explosion.x = self.x; explosion.y = self.y; explosion.scaleX = 0.1; explosion.scaleY = 0.1; explosion.alpha = 0.8; // Animate explosion tween(explosion, { scaleX: 4, scaleY: 4, alpha: 0, rotation: Math.PI * 2 }, { duration: 400, easing: tween.easeOut, onFinish: function onFinish() { explosion.destroy(); } }); // Explode 5 random candies self.explodeRandomCandies(); // Remove power-up var index = powerUps.indexOf(self); if (index > -1) { powerUps.splice(index, 1); } // Update power-up counter display powerUpCountText.setText('Power-ups: ' + powerUps.length); self.destroy(); }; self.explodeRandomCandies = function () { var availableCandies = []; for (var i = 0; i < candies.length; i++) { if (candies[i].parent && !candies[i].isMatched) { availableCandies.push(candies[i]); } } // Select up to 5 random candies var candiesToExplode = []; var numToExplode = Math.min(5, availableCandies.length); for (var i = 0; i < numToExplode; i++) { var randomIndex = Math.floor(Math.random() * availableCandies.length); candiesToExplode.push(availableCandies[randomIndex]); availableCandies.splice(randomIndex, 1); } // Explode selected candies with slight delay between each for (var i = 0; i < candiesToExplode.length; i++) { (function (candy, delay) { LK.setTimeout(function () { // Create explosion effect var explosion = game.addChild(LK.getAsset('Exp', { anchorX: 0.5, anchorY: 0.5 })); explosion.x = candy.x; explosion.y = candy.y; explosion.scaleX = 0.1; explosion.scaleY = 0.1; explosion.alpha = 0.8; // Animate explosion tween(explosion, { scaleX: 3, scaleY: 3, alpha: 0, rotation: Math.PI * 2 }, { duration: 300, easing: tween.easeOut, onFinish: function onFinish() { explosion.destroy(); } }); // Remove candy from grid candy.alpha = 0; grid[candy.gridX][candy.gridY] = null; // Remove from candies array var index = candies.indexOf(candy); if (index > -1) { candies.splice(index, 1); } // Destroy candy after explosion LK.setTimeout(function () { candy.destroy(); }, 300); }, delay); })(candiesToExplode[i], i * 100); } // Process gravity and refill after all explosions LK.setTimeout(function () { // Add score for power-up activation gameScore += 250; scoreText.setText('Score: ' + gameScore); LK.setScore(gameScore); // Process the board LK.setTimeout(function () { applyGravity(); fillEmptySpaces(); LK.setTimeout(function () { processMatches(); }, 300); }, 100); }, candiesToExplode.length * 100 + 300); }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0xFFC0CB }); /**** * Game Code ****/ var GRID_SIZE = 8; var CELL_SIZE = 140; var GRID_START_X = (2048 - GRID_SIZE * CELL_SIZE) / 2; var GRID_START_Y = (2732 - GRID_SIZE * CELL_SIZE) / 2 - 200; var grid = []; var candies = []; var selectedCandy = null; var isProcessing = false; var gameScore = 0; var comboMultiplier = 1; var powerUps = []; var hasSpawned1000PowerUp = false; var POWER_UP_AREA_Y = GRID_START_Y + GRID_SIZE * CELL_SIZE + 150; var SECOND_POWER_UP_AREA_Y = POWER_UP_AREA_Y + 200; var gameStarted = false; var countdownValue = 3; var countdownText = new Text2('3', { size: 200, fill: 0xFFD700 }); // Initialize grid background var gridBackground = game.addChild(LK.getAsset('gridBg', { anchorX: 0.5, anchorY: 0.5 })); gridBackground.x = 2048 / 2; gridBackground.y = GRID_START_Y + GRID_SIZE * CELL_SIZE / 2; // Create cell backgrounds for (var i = 0; i < GRID_SIZE; i++) { for (var j = 0; j < GRID_SIZE; j++) { var cellBg = game.addChild(LK.getAsset('cellBg', { anchorX: 0.5, anchorY: 0.5 })); cellBg.x = GRID_START_X + i * CELL_SIZE + CELL_SIZE / 2; cellBg.y = GRID_START_Y + j * CELL_SIZE + CELL_SIZE / 2; } } // Initialize score display var scoreText = new Text2('Score: 0', { size: 80, fill: 0xFFFFFF }); scoreText.anchor.set(0.5, 0); scoreText.y = 50; LK.gui.top.addChild(scoreText); // Initialize combo display var comboText = new Text2('', { size: 50, fill: 0xFFD700 }); comboText.anchor.set(0.5, 0); comboText.y = 140; LK.gui.top.addChild(comboText); // Initialize power-up counter display var powerUpCountText = new Text2('Power-ups: 0', { size: 60, fill: 0xFF4500 }); powerUpCountText.anchor.set(0.5, 0); powerUpCountText.y = 200; LK.gui.top.addChild(powerUpCountText); // Setup countdown display countdownText.anchor.set(0.5, 0.5); countdownText.x = 2048 / 2; countdownText.y = 2732 / 2 - 150; game.addChild(countdownText); // Create power-up area var powerUpArea = game.addChild(LK.getAsset('powerUpBg', { anchorX: 0.5, anchorY: 0.5 })); powerUpArea.x = 2048 / 2 - 250; powerUpArea.y = POWER_UP_AREA_Y; powerUpArea.alpha = 0.8; // Create second power-up area var secondPowerUpArea = game.addChild(LK.getAsset('powerUpBg', { anchorX: 0.5, anchorY: 0.5 })); secondPowerUpArea.x = 2048 / 2 + 250; secondPowerUpArea.y = POWER_UP_AREA_Y; secondPowerUpArea.alpha = 0.8; secondPowerUpArea.tint = 0xFF4444; // Red tint to distinguish from first area // Initialize grid array function initializeGrid() { grid = []; for (var i = 0; i < GRID_SIZE; i++) { grid[i] = []; for (var j = 0; j < GRID_SIZE; j++) { grid[i][j] = null; } } } // Start countdown timer function startCountdown() { if (countdownValue > 0) { countdownText.setText(countdownValue.toString()); tween(countdownText, { scaleX: 1.5, scaleY: 1.5 }, { duration: 200, easing: tween.easeOut, onFinish: function onFinish() { tween(countdownText, { scaleX: 1, scaleY: 1 }, { duration: 200, easing: tween.easeIn }); } }); countdownValue--; LK.setTimeout(startCountdown, 1000); } else { countdownText.setText('GO!'); tween(countdownText, { scaleX: 2, scaleY: 2, alpha: 0 }, { duration: 500, easing: tween.easeOut, onFinish: function onFinish() { countdownText.destroy(); gameStarted = true; // Play music when game starts LK.playMusic('music'); // Initialize game after countdown initializeGrid(); fillGrid(); // Remove initial matches LK.setTimeout(function () { processMatches(); }, 100); } }); } } // Generate random candy type function getRandomCandyType() { return Math.floor(Math.random() * 6) + 1; } // Create candy at grid position function createCandy(gridX, gridY) { var candyType = getRandomCandyType(); var candy = new Candy(candyType); candy.setGridPosition(gridX, gridY); game.addChild(candy); candies.push(candy); grid[gridX][gridY] = candy; return candy; } // Fill grid with initial candies function fillGrid() { for (var i = 0; i < GRID_SIZE; i++) { for (var j = 0; j < GRID_SIZE; j++) { if (!grid[i][j]) { createCandy(i, j); } } } } // Check if coordinates are valid function isValidPosition(x, y) { return x >= 0 && x < GRID_SIZE && y >= 0 && y < GRID_SIZE; } // Check if two positions are adjacent function areAdjacent(x1, y1, x2, y2) { var dx = Math.abs(x1 - x2); var dy = Math.abs(y1 - y2); return dx === 1 && dy === 0 || dx === 0 && dy === 1; } // Swap two candies function swapCandies(candy1, candy2) { if (!candy1 || !candy2) return; var tempX = candy1.gridX; var tempY = candy1.gridY; grid[candy1.gridX][candy1.gridY] = candy2; grid[candy2.gridX][candy2.gridY] = candy1; candy1.animateToGridPosition(candy2.gridX, candy2.gridY); candy2.animateToGridPosition(tempX, tempY); try { LK.getSound('swap').play(); } catch (e) { console.log('Error playing swap sound:', e); } } // Find matches in a line function findMatches(startX, startY, deltaX, deltaY) { var matches = []; var currentCandy = grid[startX][startY]; if (!currentCandy) return matches; var count = 1; matches.push(currentCandy); // Check forward var x = startX + deltaX; var y = startY + deltaY; while (isValidPosition(x, y) && grid[x][y] && grid[x][y].type === currentCandy.type) { matches.push(grid[x][y]); count++; x += deltaX; y += deltaY; } // Check backward x = startX - deltaX; y = startY - deltaY; while (isValidPosition(x, y) && grid[x][y] && grid[x][y].type === currentCandy.type) { matches.unshift(grid[x][y]); count++; x -= deltaX; y -= deltaY; } return count >= 3 ? matches : []; } // Find all matches on the board function findAllMatches() { var allMatches = []; var processed = []; for (var i = 0; i < GRID_SIZE; i++) { processed[i] = []; for (var j = 0; j < GRID_SIZE; j++) { processed[i][j] = false; } } for (var i = 0; i < GRID_SIZE; i++) { for (var j = 0; j < GRID_SIZE; j++) { if (grid[i][j] && !processed[i][j]) { // Check horizontal matches var horizontalMatches = findMatches(i, j, 1, 0); if (horizontalMatches.length >= 3) { for (var k = 0; k < horizontalMatches.length; k++) { var candy = horizontalMatches[k]; processed[candy.gridX][candy.gridY] = true; if (allMatches.indexOf(candy) === -1) { allMatches.push(candy); } } } // Check vertical matches var verticalMatches = findMatches(i, j, 0, 1); if (verticalMatches.length >= 3) { for (var k = 0; k < verticalMatches.length; k++) { var candy = verticalMatches[k]; processed[candy.gridX][candy.gridY] = true; if (allMatches.indexOf(candy) === -1) { allMatches.push(candy); } } } } } } return allMatches; } // Remove matched candies function removeMatches(matches) { for (var i = 0; i < matches.length; i++) { var candy = matches[i]; // Create explosion effect using "exp" asset var explosion = game.addChild(LK.getAsset('Exp', { anchorX: 0.5, anchorY: 0.5 })); explosion.x = candy.x; explosion.y = candy.y; explosion.scaleX = 0.1; explosion.scaleY = 0.1; explosion.alpha = 0.8; // Animate explosion growth and fade tween(explosion, { scaleX: 3, scaleY: 3, alpha: 0, rotation: Math.PI * 2 }, { duration: 300, easing: tween.easeOut, onFinish: function onFinish() { explosion.destroy(); } }); // Hide the candy immediately candy.alpha = 0; grid[candy.gridX][candy.gridY] = null; // Remove from candies array var index = candies.indexOf(candy); if (index > -1) { candies.splice(index, 1); } } // Check if we should create a power-up (4 or more candies matched) if (matches.length >= 4) { // Create fire power-up var firePowerUp = new FirePowerUp(); firePowerUp.x = 2048 / 2 - 250; firePowerUp.y = POWER_UP_AREA_Y; game.addChild(firePowerUp); powerUps.push(firePowerUp); // Update power-up counter display powerUpCountText.setText('Power-ups: ' + powerUps.length); // Flash screen to indicate power-up creation LK.effects.flashScreen(0xFF4500, 500); } // Check if we should create the special 1000-point explosive power-up if (gameScore >= 1000 && !hasSpawned1000PowerUp) { hasSpawned1000PowerUp = true; // Create explosive power-up var explosivePowerUp = new ExplosivePowerUp(); explosivePowerUp.x = 2048 / 2 + 250; explosivePowerUp.y = POWER_UP_AREA_Y; game.addChild(explosivePowerUp); powerUps.push(explosivePowerUp); // Update power-up counter display powerUpCountText.setText('Power-ups: ' + powerUps.length); // Flash screen with different color to indicate special power-up LK.effects.flashScreen(0xFF0000, 800); } // Calculate score var baseScore = matches.length * 10; var bonusScore = matches.length > 3 ? (matches.length - 3) * 20 : 0; var totalScore = (baseScore + bonusScore) * comboMultiplier; gameScore += totalScore; scoreText.setText('Score: ' + gameScore); LK.setScore(gameScore); // Show combo feedback if (comboMultiplier > 1) { comboText.setText('COMBO x' + comboMultiplier + ' (+' + totalScore + ')'); comboText.alpha = 1; tween(comboText, { alpha: 0 }, { duration: 1000, easing: tween.easeOut }); } else { comboText.setText(''); } // Flash screen effect for big matches if (matches.length >= 5) { LK.effects.flashScreen(0xFFD700, 300); } try { LK.getSound('match').play(); } catch (e) { console.log('Error playing match sound:', e); } // Schedule removal after explosion animation LK.setTimeout(function () { for (var i = 0; i < matches.length; i++) { matches[i].destroy(); } }, 300); } // Make candies fall down function applyGravity() { var moved = false; for (var i = 0; i < GRID_SIZE; i++) { for (var j = GRID_SIZE - 2; j >= 0; j--) { if (grid[i][j]) { var fallDistance = 0; var checkY = j + 1; while (checkY < GRID_SIZE && !grid[i][checkY]) { fallDistance++; checkY++; } if (fallDistance > 0) { var candy = grid[i][j]; grid[i][j] = null; grid[i][j + fallDistance] = candy; candy.animateToGridPosition(i, j + fallDistance); moved = true; } } } } return moved; } // Fill empty spaces with new candies function fillEmptySpaces() { var added = false; for (var i = 0; i < GRID_SIZE; i++) { for (var j = 0; j < GRID_SIZE; j++) { if (!grid[i][j]) { var candy = createCandy(i, j); // Start candy above the grid and animate down candy.y = GRID_START_Y - 100; candy.animateToGridPosition(i, j); added = true; } } } return added; } // Check if move creates matches function wouldCreateMatch(candy1, candy2) { // Store original positions var origX1 = candy1.gridX; var origY1 = candy1.gridY; var origX2 = candy2.gridX; var origY2 = candy2.gridY; // Temporarily swap in grid grid[candy1.gridX][candy1.gridY] = candy2; grid[candy2.gridX][candy2.gridY] = candy1; // Temporarily swap candy positions candy1.gridX = origX2; candy1.gridY = origY2; candy2.gridX = origX1; candy2.gridY = origY1; var matches1 = findMatches(candy1.gridX, candy1.gridY, 1, 0); var matches2 = findMatches(candy1.gridX, candy1.gridY, 0, 1); var matches3 = findMatches(candy2.gridX, candy2.gridY, 1, 0); var matches4 = findMatches(candy2.gridX, candy2.gridY, 0, 1); var hasMatch = matches1.length >= 3 || matches2.length >= 3 || matches3.length >= 3 || matches4.length >= 3; // Restore original positions candy1.gridX = origX1; candy1.gridY = origY1; candy2.gridX = origX2; candy2.gridY = origY2; grid[origX1][origY1] = candy1; grid[origX2][origY2] = candy2; return hasMatch; } // Process matches and cascades function processMatches() { if (isProcessing) return; isProcessing = true; var matches = findAllMatches(); if (matches.length > 0) { removeMatches(matches); comboMultiplier++; LK.setTimeout(function () { var gravityApplied = applyGravity(); var spacesAdded = fillEmptySpaces(); if (gravityApplied || spacesAdded) { LK.setTimeout(function () { isProcessing = false; processMatches(); // Check for new matches }, 300); } else { isProcessing = false; comboMultiplier = 1; } }, 300); } else { isProcessing = false; comboMultiplier = 1; } } // Get candy at screen position function getCandyAt(x, y) { // Calculate grid coordinates from screen position var gridX = Math.floor((x - GRID_START_X) / CELL_SIZE); var gridY = Math.floor((y - GRID_START_Y) / CELL_SIZE); // Check if coordinates are within grid bounds if (gridX >= 0 && gridX < GRID_SIZE && gridY >= 0 && gridY < GRID_SIZE) { return grid[gridX][gridY]; } return null; } var dragStartCandy = null; var dragCurrentCandy = null; // Game input handling game.down = function (x, y, obj) { if (!gameStarted || isProcessing) return; var candy = getCandyAt(x, y); if (candy) { dragStartCandy = candy; dragCurrentCandy = candy; candy.highlight(); } }; game.move = function (x, y, obj) { if (!gameStarted || isProcessing || !dragStartCandy) return; var candy = getCandyAt(x, y); if (candy && candy !== dragCurrentCandy) { if (dragCurrentCandy && dragCurrentCandy !== dragStartCandy) { dragCurrentCandy.unhighlight(); } dragCurrentCandy = candy; if (candy !== dragStartCandy) { candy.highlight(); } } }; game.up = function (x, y, obj) { if (!gameStarted || isProcessing || !dragStartCandy) return; var endCandy = getCandyAt(x, y); if (endCandy && endCandy !== dragStartCandy && areAdjacent(dragStartCandy.gridX, dragStartCandy.gridY, endCandy.gridX, endCandy.gridY)) { if (wouldCreateMatch(dragStartCandy, endCandy)) { swapCandies(dragStartCandy, endCandy); LK.setTimeout(function () { processMatches(); }, 400); } else { // Invalid move - swap back try { LK.getSound('Wro').play(); } catch (e) { console.log('Error playing wrong move sound:', e); } dragStartCandy.unhighlight(); if (endCandy) endCandy.unhighlight(); } } else { // No valid target - just unhighlight dragStartCandy.unhighlight(); if (dragCurrentCandy && dragCurrentCandy !== dragStartCandy) { dragCurrentCandy.unhighlight(); } } dragStartCandy = null; dragCurrentCandy = null; }; // Start countdown instead of immediate game initialization startCountdown(); game.update = function () { // Game runs automatically through event handling };
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var Candy = Container.expand(function (type) {
var self = Container.call(this);
self.type = type;
self.gridX = 0;
self.gridY = 0;
self.isMatched = false;
var candyAsset = self.attachAsset('candy' + type, {
anchorX: 0.5,
anchorY: 0.5
});
self.setGridPosition = function (gridX, gridY) {
self.gridX = gridX;
self.gridY = gridY;
self.x = GRID_START_X + gridX * CELL_SIZE + CELL_SIZE / 2;
self.y = GRID_START_Y + gridY * CELL_SIZE + CELL_SIZE / 2;
};
self.animateToGridPosition = function (gridX, gridY, duration) {
self.gridX = gridX;
self.gridY = gridY;
var targetX = GRID_START_X + gridX * CELL_SIZE + CELL_SIZE / 2;
var targetY = GRID_START_Y + gridY * CELL_SIZE + CELL_SIZE / 2;
tween(self, {
x: targetX,
y: targetY
}, {
duration: duration || 300,
easing: tween.easeOut
});
};
self.highlight = function () {
tween(self, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 200,
easing: tween.easeOut
});
};
self.unhighlight = function () {
tween(self, {
scaleX: 1,
scaleY: 1
}, {
duration: 200,
easing: tween.easeOut
});
};
self.markForRemoval = function () {
self.isMatched = true;
tween(self, {
scaleX: 0,
scaleY: 0,
alpha: 0
}, {
duration: 250,
easing: tween.easeIn
});
};
return self;
});
var ExplosivePowerUp = Container.expand(function () {
var self = Container.call(this);
// Create explosive visual effect using Power asset with different tint
var powerUpAsset = self.attachAsset('Power', {
anchorX: 0.5,
anchorY: 0.5
});
powerUpAsset.tint = 0xFF0000; // Red tint for explosive power-up
// Add pulsing animation
self.pulseAnimation = function () {
tween(self, {
scaleX: 1.4,
scaleY: 1.4
}, {
duration: 600,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(self, {
scaleX: 1,
scaleY: 1
}, {
duration: 600,
easing: tween.easeInOut,
onFinish: function onFinish() {
if (self.parent) self.pulseAnimation();
}
});
}
});
};
// Start pulsing immediately
self.pulseAnimation();
// Handle click on power-up
self.down = function (x, y, obj) {
self.activate();
};
self.activate = function () {
// Create explosion effect at power-up location
var explosion = game.addChild(LK.getAsset('Exp', {
anchorX: 0.5,
anchorY: 0.5
}));
explosion.x = self.x;
explosion.y = self.y;
explosion.scaleX = 0.1;
explosion.scaleY = 0.1;
explosion.alpha = 0.8;
// Animate explosion
tween(explosion, {
scaleX: 6,
scaleY: 6,
alpha: 0,
rotation: Math.PI * 3
}, {
duration: 600,
easing: tween.easeOut,
onFinish: function onFinish() {
explosion.destroy();
}
});
// Explode 5x5 random area
self.explode5x5Area();
// Remove power-up
var index = powerUps.indexOf(self);
if (index > -1) {
powerUps.splice(index, 1);
}
// Update power-up counter display
powerUpCountText.setText('Power-ups: ' + powerUps.length);
self.destroy();
};
self.explode5x5Area = function () {
// Find a random 5x5 area on the grid
var startX = Math.floor(Math.random() * (GRID_SIZE - 4)); // Ensure 5x5 fits
var startY = Math.floor(Math.random() * (GRID_SIZE - 4));
var candiesToExplode = [];
// Collect all candies in the 5x5 area
for (var i = 0; i < 5; i++) {
for (var j = 0; j < 5; j++) {
var gridX = startX + i;
var gridY = startY + j;
if (grid[gridX][gridY] && !grid[gridX][gridY].isMatched) {
candiesToExplode.push(grid[gridX][gridY]);
}
}
}
// Explode all candies in the area with staggered timing
for (var i = 0; i < candiesToExplode.length; i++) {
(function (candy, delay) {
LK.setTimeout(function () {
// Create explosion effect
var explosion = game.addChild(LK.getAsset('Exp', {
anchorX: 0.5,
anchorY: 0.5
}));
explosion.x = candy.x;
explosion.y = candy.y;
explosion.scaleX = 0.1;
explosion.scaleY = 0.1;
explosion.alpha = 0.8;
// Animate explosion
tween(explosion, {
scaleX: 3,
scaleY: 3,
alpha: 0,
rotation: Math.PI * 2
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
explosion.destroy();
}
});
// Remove candy from grid
candy.alpha = 0;
grid[candy.gridX][candy.gridY] = null;
// Remove from candies array
var index = candies.indexOf(candy);
if (index > -1) {
candies.splice(index, 1);
}
// Destroy candy after explosion
LK.setTimeout(function () {
candy.destroy();
}, 300);
}, delay);
})(candiesToExplode[i], i * 50);
}
// Process gravity and refill after all explosions
LK.setTimeout(function () {
// Add score for power-up activation
gameScore += 500;
scoreText.setText('Score: ' + gameScore);
LK.setScore(gameScore);
// Process the board
LK.setTimeout(function () {
applyGravity();
fillEmptySpaces();
LK.setTimeout(function () {
processMatches();
}, 300);
}, 100);
}, candiesToExplode.length * 50 + 300);
};
return self;
});
var FirePowerUp = Container.expand(function () {
var self = Container.call(this);
// Create fiery visual effect using Power asset
var powerUpAsset = self.attachAsset('Power', {
anchorX: 0.5,
anchorY: 0.5
});
// Add pulsing animation
self.pulseAnimation = function () {
tween(self, {
scaleX: 1.3,
scaleY: 1.3
}, {
duration: 800,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(self, {
scaleX: 1,
scaleY: 1
}, {
duration: 800,
easing: tween.easeInOut,
onFinish: function onFinish() {
if (self.parent) self.pulseAnimation();
}
});
}
});
};
// Start pulsing immediately
self.pulseAnimation();
// Handle click on power-up
self.down = function (x, y, obj) {
self.activate();
};
self.activate = function () {
// Create explosion effect at power-up location
var explosion = game.addChild(LK.getAsset('Exp', {
anchorX: 0.5,
anchorY: 0.5
}));
explosion.x = self.x;
explosion.y = self.y;
explosion.scaleX = 0.1;
explosion.scaleY = 0.1;
explosion.alpha = 0.8;
// Animate explosion
tween(explosion, {
scaleX: 4,
scaleY: 4,
alpha: 0,
rotation: Math.PI * 2
}, {
duration: 400,
easing: tween.easeOut,
onFinish: function onFinish() {
explosion.destroy();
}
});
// Explode 5 random candies
self.explodeRandomCandies();
// Remove power-up
var index = powerUps.indexOf(self);
if (index > -1) {
powerUps.splice(index, 1);
}
// Update power-up counter display
powerUpCountText.setText('Power-ups: ' + powerUps.length);
self.destroy();
};
self.explodeRandomCandies = function () {
var availableCandies = [];
for (var i = 0; i < candies.length; i++) {
if (candies[i].parent && !candies[i].isMatched) {
availableCandies.push(candies[i]);
}
}
// Select up to 5 random candies
var candiesToExplode = [];
var numToExplode = Math.min(5, availableCandies.length);
for (var i = 0; i < numToExplode; i++) {
var randomIndex = Math.floor(Math.random() * availableCandies.length);
candiesToExplode.push(availableCandies[randomIndex]);
availableCandies.splice(randomIndex, 1);
}
// Explode selected candies with slight delay between each
for (var i = 0; i < candiesToExplode.length; i++) {
(function (candy, delay) {
LK.setTimeout(function () {
// Create explosion effect
var explosion = game.addChild(LK.getAsset('Exp', {
anchorX: 0.5,
anchorY: 0.5
}));
explosion.x = candy.x;
explosion.y = candy.y;
explosion.scaleX = 0.1;
explosion.scaleY = 0.1;
explosion.alpha = 0.8;
// Animate explosion
tween(explosion, {
scaleX: 3,
scaleY: 3,
alpha: 0,
rotation: Math.PI * 2
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
explosion.destroy();
}
});
// Remove candy from grid
candy.alpha = 0;
grid[candy.gridX][candy.gridY] = null;
// Remove from candies array
var index = candies.indexOf(candy);
if (index > -1) {
candies.splice(index, 1);
}
// Destroy candy after explosion
LK.setTimeout(function () {
candy.destroy();
}, 300);
}, delay);
})(candiesToExplode[i], i * 100);
}
// Process gravity and refill after all explosions
LK.setTimeout(function () {
// Add score for power-up activation
gameScore += 250;
scoreText.setText('Score: ' + gameScore);
LK.setScore(gameScore);
// Process the board
LK.setTimeout(function () {
applyGravity();
fillEmptySpaces();
LK.setTimeout(function () {
processMatches();
}, 300);
}, 100);
}, candiesToExplode.length * 100 + 300);
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0xFFC0CB
});
/****
* Game Code
****/
var GRID_SIZE = 8;
var CELL_SIZE = 140;
var GRID_START_X = (2048 - GRID_SIZE * CELL_SIZE) / 2;
var GRID_START_Y = (2732 - GRID_SIZE * CELL_SIZE) / 2 - 200;
var grid = [];
var candies = [];
var selectedCandy = null;
var isProcessing = false;
var gameScore = 0;
var comboMultiplier = 1;
var powerUps = [];
var hasSpawned1000PowerUp = false;
var POWER_UP_AREA_Y = GRID_START_Y + GRID_SIZE * CELL_SIZE + 150;
var SECOND_POWER_UP_AREA_Y = POWER_UP_AREA_Y + 200;
var gameStarted = false;
var countdownValue = 3;
var countdownText = new Text2('3', {
size: 200,
fill: 0xFFD700
});
// Initialize grid background
var gridBackground = game.addChild(LK.getAsset('gridBg', {
anchorX: 0.5,
anchorY: 0.5
}));
gridBackground.x = 2048 / 2;
gridBackground.y = GRID_START_Y + GRID_SIZE * CELL_SIZE / 2;
// Create cell backgrounds
for (var i = 0; i < GRID_SIZE; i++) {
for (var j = 0; j < GRID_SIZE; j++) {
var cellBg = game.addChild(LK.getAsset('cellBg', {
anchorX: 0.5,
anchorY: 0.5
}));
cellBg.x = GRID_START_X + i * CELL_SIZE + CELL_SIZE / 2;
cellBg.y = GRID_START_Y + j * CELL_SIZE + CELL_SIZE / 2;
}
}
// Initialize score display
var scoreText = new Text2('Score: 0', {
size: 80,
fill: 0xFFFFFF
});
scoreText.anchor.set(0.5, 0);
scoreText.y = 50;
LK.gui.top.addChild(scoreText);
// Initialize combo display
var comboText = new Text2('', {
size: 50,
fill: 0xFFD700
});
comboText.anchor.set(0.5, 0);
comboText.y = 140;
LK.gui.top.addChild(comboText);
// Initialize power-up counter display
var powerUpCountText = new Text2('Power-ups: 0', {
size: 60,
fill: 0xFF4500
});
powerUpCountText.anchor.set(0.5, 0);
powerUpCountText.y = 200;
LK.gui.top.addChild(powerUpCountText);
// Setup countdown display
countdownText.anchor.set(0.5, 0.5);
countdownText.x = 2048 / 2;
countdownText.y = 2732 / 2 - 150;
game.addChild(countdownText);
// Create power-up area
var powerUpArea = game.addChild(LK.getAsset('powerUpBg', {
anchorX: 0.5,
anchorY: 0.5
}));
powerUpArea.x = 2048 / 2 - 250;
powerUpArea.y = POWER_UP_AREA_Y;
powerUpArea.alpha = 0.8;
// Create second power-up area
var secondPowerUpArea = game.addChild(LK.getAsset('powerUpBg', {
anchorX: 0.5,
anchorY: 0.5
}));
secondPowerUpArea.x = 2048 / 2 + 250;
secondPowerUpArea.y = POWER_UP_AREA_Y;
secondPowerUpArea.alpha = 0.8;
secondPowerUpArea.tint = 0xFF4444; // Red tint to distinguish from first area
// Initialize grid array
function initializeGrid() {
grid = [];
for (var i = 0; i < GRID_SIZE; i++) {
grid[i] = [];
for (var j = 0; j < GRID_SIZE; j++) {
grid[i][j] = null;
}
}
}
// Start countdown timer
function startCountdown() {
if (countdownValue > 0) {
countdownText.setText(countdownValue.toString());
tween(countdownText, {
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(countdownText, {
scaleX: 1,
scaleY: 1
}, {
duration: 200,
easing: tween.easeIn
});
}
});
countdownValue--;
LK.setTimeout(startCountdown, 1000);
} else {
countdownText.setText('GO!');
tween(countdownText, {
scaleX: 2,
scaleY: 2,
alpha: 0
}, {
duration: 500,
easing: tween.easeOut,
onFinish: function onFinish() {
countdownText.destroy();
gameStarted = true;
// Play music when game starts
LK.playMusic('music');
// Initialize game after countdown
initializeGrid();
fillGrid();
// Remove initial matches
LK.setTimeout(function () {
processMatches();
}, 100);
}
});
}
}
// Generate random candy type
function getRandomCandyType() {
return Math.floor(Math.random() * 6) + 1;
}
// Create candy at grid position
function createCandy(gridX, gridY) {
var candyType = getRandomCandyType();
var candy = new Candy(candyType);
candy.setGridPosition(gridX, gridY);
game.addChild(candy);
candies.push(candy);
grid[gridX][gridY] = candy;
return candy;
}
// Fill grid with initial candies
function fillGrid() {
for (var i = 0; i < GRID_SIZE; i++) {
for (var j = 0; j < GRID_SIZE; j++) {
if (!grid[i][j]) {
createCandy(i, j);
}
}
}
}
// Check if coordinates are valid
function isValidPosition(x, y) {
return x >= 0 && x < GRID_SIZE && y >= 0 && y < GRID_SIZE;
}
// Check if two positions are adjacent
function areAdjacent(x1, y1, x2, y2) {
var dx = Math.abs(x1 - x2);
var dy = Math.abs(y1 - y2);
return dx === 1 && dy === 0 || dx === 0 && dy === 1;
}
// Swap two candies
function swapCandies(candy1, candy2) {
if (!candy1 || !candy2) return;
var tempX = candy1.gridX;
var tempY = candy1.gridY;
grid[candy1.gridX][candy1.gridY] = candy2;
grid[candy2.gridX][candy2.gridY] = candy1;
candy1.animateToGridPosition(candy2.gridX, candy2.gridY);
candy2.animateToGridPosition(tempX, tempY);
try {
LK.getSound('swap').play();
} catch (e) {
console.log('Error playing swap sound:', e);
}
}
// Find matches in a line
function findMatches(startX, startY, deltaX, deltaY) {
var matches = [];
var currentCandy = grid[startX][startY];
if (!currentCandy) return matches;
var count = 1;
matches.push(currentCandy);
// Check forward
var x = startX + deltaX;
var y = startY + deltaY;
while (isValidPosition(x, y) && grid[x][y] && grid[x][y].type === currentCandy.type) {
matches.push(grid[x][y]);
count++;
x += deltaX;
y += deltaY;
}
// Check backward
x = startX - deltaX;
y = startY - deltaY;
while (isValidPosition(x, y) && grid[x][y] && grid[x][y].type === currentCandy.type) {
matches.unshift(grid[x][y]);
count++;
x -= deltaX;
y -= deltaY;
}
return count >= 3 ? matches : [];
}
// Find all matches on the board
function findAllMatches() {
var allMatches = [];
var processed = [];
for (var i = 0; i < GRID_SIZE; i++) {
processed[i] = [];
for (var j = 0; j < GRID_SIZE; j++) {
processed[i][j] = false;
}
}
for (var i = 0; i < GRID_SIZE; i++) {
for (var j = 0; j < GRID_SIZE; j++) {
if (grid[i][j] && !processed[i][j]) {
// Check horizontal matches
var horizontalMatches = findMatches(i, j, 1, 0);
if (horizontalMatches.length >= 3) {
for (var k = 0; k < horizontalMatches.length; k++) {
var candy = horizontalMatches[k];
processed[candy.gridX][candy.gridY] = true;
if (allMatches.indexOf(candy) === -1) {
allMatches.push(candy);
}
}
}
// Check vertical matches
var verticalMatches = findMatches(i, j, 0, 1);
if (verticalMatches.length >= 3) {
for (var k = 0; k < verticalMatches.length; k++) {
var candy = verticalMatches[k];
processed[candy.gridX][candy.gridY] = true;
if (allMatches.indexOf(candy) === -1) {
allMatches.push(candy);
}
}
}
}
}
}
return allMatches;
}
// Remove matched candies
function removeMatches(matches) {
for (var i = 0; i < matches.length; i++) {
var candy = matches[i];
// Create explosion effect using "exp" asset
var explosion = game.addChild(LK.getAsset('Exp', {
anchorX: 0.5,
anchorY: 0.5
}));
explosion.x = candy.x;
explosion.y = candy.y;
explosion.scaleX = 0.1;
explosion.scaleY = 0.1;
explosion.alpha = 0.8;
// Animate explosion growth and fade
tween(explosion, {
scaleX: 3,
scaleY: 3,
alpha: 0,
rotation: Math.PI * 2
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
explosion.destroy();
}
});
// Hide the candy immediately
candy.alpha = 0;
grid[candy.gridX][candy.gridY] = null;
// Remove from candies array
var index = candies.indexOf(candy);
if (index > -1) {
candies.splice(index, 1);
}
}
// Check if we should create a power-up (4 or more candies matched)
if (matches.length >= 4) {
// Create fire power-up
var firePowerUp = new FirePowerUp();
firePowerUp.x = 2048 / 2 - 250;
firePowerUp.y = POWER_UP_AREA_Y;
game.addChild(firePowerUp);
powerUps.push(firePowerUp);
// Update power-up counter display
powerUpCountText.setText('Power-ups: ' + powerUps.length);
// Flash screen to indicate power-up creation
LK.effects.flashScreen(0xFF4500, 500);
}
// Check if we should create the special 1000-point explosive power-up
if (gameScore >= 1000 && !hasSpawned1000PowerUp) {
hasSpawned1000PowerUp = true;
// Create explosive power-up
var explosivePowerUp = new ExplosivePowerUp();
explosivePowerUp.x = 2048 / 2 + 250;
explosivePowerUp.y = POWER_UP_AREA_Y;
game.addChild(explosivePowerUp);
powerUps.push(explosivePowerUp);
// Update power-up counter display
powerUpCountText.setText('Power-ups: ' + powerUps.length);
// Flash screen with different color to indicate special power-up
LK.effects.flashScreen(0xFF0000, 800);
}
// Calculate score
var baseScore = matches.length * 10;
var bonusScore = matches.length > 3 ? (matches.length - 3) * 20 : 0;
var totalScore = (baseScore + bonusScore) * comboMultiplier;
gameScore += totalScore;
scoreText.setText('Score: ' + gameScore);
LK.setScore(gameScore);
// Show combo feedback
if (comboMultiplier > 1) {
comboText.setText('COMBO x' + comboMultiplier + ' (+' + totalScore + ')');
comboText.alpha = 1;
tween(comboText, {
alpha: 0
}, {
duration: 1000,
easing: tween.easeOut
});
} else {
comboText.setText('');
}
// Flash screen effect for big matches
if (matches.length >= 5) {
LK.effects.flashScreen(0xFFD700, 300);
}
try {
LK.getSound('match').play();
} catch (e) {
console.log('Error playing match sound:', e);
}
// Schedule removal after explosion animation
LK.setTimeout(function () {
for (var i = 0; i < matches.length; i++) {
matches[i].destroy();
}
}, 300);
}
// Make candies fall down
function applyGravity() {
var moved = false;
for (var i = 0; i < GRID_SIZE; i++) {
for (var j = GRID_SIZE - 2; j >= 0; j--) {
if (grid[i][j]) {
var fallDistance = 0;
var checkY = j + 1;
while (checkY < GRID_SIZE && !grid[i][checkY]) {
fallDistance++;
checkY++;
}
if (fallDistance > 0) {
var candy = grid[i][j];
grid[i][j] = null;
grid[i][j + fallDistance] = candy;
candy.animateToGridPosition(i, j + fallDistance);
moved = true;
}
}
}
}
return moved;
}
// Fill empty spaces with new candies
function fillEmptySpaces() {
var added = false;
for (var i = 0; i < GRID_SIZE; i++) {
for (var j = 0; j < GRID_SIZE; j++) {
if (!grid[i][j]) {
var candy = createCandy(i, j);
// Start candy above the grid and animate down
candy.y = GRID_START_Y - 100;
candy.animateToGridPosition(i, j);
added = true;
}
}
}
return added;
}
// Check if move creates matches
function wouldCreateMatch(candy1, candy2) {
// Store original positions
var origX1 = candy1.gridX;
var origY1 = candy1.gridY;
var origX2 = candy2.gridX;
var origY2 = candy2.gridY;
// Temporarily swap in grid
grid[candy1.gridX][candy1.gridY] = candy2;
grid[candy2.gridX][candy2.gridY] = candy1;
// Temporarily swap candy positions
candy1.gridX = origX2;
candy1.gridY = origY2;
candy2.gridX = origX1;
candy2.gridY = origY1;
var matches1 = findMatches(candy1.gridX, candy1.gridY, 1, 0);
var matches2 = findMatches(candy1.gridX, candy1.gridY, 0, 1);
var matches3 = findMatches(candy2.gridX, candy2.gridY, 1, 0);
var matches4 = findMatches(candy2.gridX, candy2.gridY, 0, 1);
var hasMatch = matches1.length >= 3 || matches2.length >= 3 || matches3.length >= 3 || matches4.length >= 3;
// Restore original positions
candy1.gridX = origX1;
candy1.gridY = origY1;
candy2.gridX = origX2;
candy2.gridY = origY2;
grid[origX1][origY1] = candy1;
grid[origX2][origY2] = candy2;
return hasMatch;
}
// Process matches and cascades
function processMatches() {
if (isProcessing) return;
isProcessing = true;
var matches = findAllMatches();
if (matches.length > 0) {
removeMatches(matches);
comboMultiplier++;
LK.setTimeout(function () {
var gravityApplied = applyGravity();
var spacesAdded = fillEmptySpaces();
if (gravityApplied || spacesAdded) {
LK.setTimeout(function () {
isProcessing = false;
processMatches(); // Check for new matches
}, 300);
} else {
isProcessing = false;
comboMultiplier = 1;
}
}, 300);
} else {
isProcessing = false;
comboMultiplier = 1;
}
}
// Get candy at screen position
function getCandyAt(x, y) {
// Calculate grid coordinates from screen position
var gridX = Math.floor((x - GRID_START_X) / CELL_SIZE);
var gridY = Math.floor((y - GRID_START_Y) / CELL_SIZE);
// Check if coordinates are within grid bounds
if (gridX >= 0 && gridX < GRID_SIZE && gridY >= 0 && gridY < GRID_SIZE) {
return grid[gridX][gridY];
}
return null;
}
var dragStartCandy = null;
var dragCurrentCandy = null;
// Game input handling
game.down = function (x, y, obj) {
if (!gameStarted || isProcessing) return;
var candy = getCandyAt(x, y);
if (candy) {
dragStartCandy = candy;
dragCurrentCandy = candy;
candy.highlight();
}
};
game.move = function (x, y, obj) {
if (!gameStarted || isProcessing || !dragStartCandy) return;
var candy = getCandyAt(x, y);
if (candy && candy !== dragCurrentCandy) {
if (dragCurrentCandy && dragCurrentCandy !== dragStartCandy) {
dragCurrentCandy.unhighlight();
}
dragCurrentCandy = candy;
if (candy !== dragStartCandy) {
candy.highlight();
}
}
};
game.up = function (x, y, obj) {
if (!gameStarted || isProcessing || !dragStartCandy) return;
var endCandy = getCandyAt(x, y);
if (endCandy && endCandy !== dragStartCandy && areAdjacent(dragStartCandy.gridX, dragStartCandy.gridY, endCandy.gridX, endCandy.gridY)) {
if (wouldCreateMatch(dragStartCandy, endCandy)) {
swapCandies(dragStartCandy, endCandy);
LK.setTimeout(function () {
processMatches();
}, 400);
} else {
// Invalid move - swap back
try {
LK.getSound('Wro').play();
} catch (e) {
console.log('Error playing wrong move sound:', e);
}
dragStartCandy.unhighlight();
if (endCandy) endCandy.unhighlight();
}
} else {
// No valid target - just unhighlight
dragStartCandy.unhighlight();
if (dragCurrentCandy && dragCurrentCandy !== dragStartCandy) {
dragCurrentCandy.unhighlight();
}
}
dragStartCandy = null;
dragCurrentCandy = null;
};
// Start countdown instead of immediate game initialization
startCountdown();
game.update = function () {
// Game runs automatically through event handling
};