/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1", { money: 0, strongBombUnlocked: false }); /**** * Classes ****/ var Candy = Container.expand(function (colorType) { var self = Container.call(this); self.colorType = colorType; self.isSelected = false; self.isExploding = false; var candyAssets = ['candy_red', 'candy_blue', 'candy_green', 'candy_yellow', 'candy_purple', 'candy_orange', 'candy_bomb', 'candy_money']; var candyGraphics = self.attachAsset(candyAssets[colorType], { anchorX: 0.5, anchorY: 0.5 }); self.setSelected = function (selected) { self.isSelected = selected; if (selected) { candyGraphics.scaleX = 1.2; candyGraphics.scaleY = 1.2; } else { candyGraphics.scaleX = 1.0; candyGraphics.scaleY = 1.0; } }; self.explode = function () { self.isExploding = true; // Cut effect: rotate and scale down with fade tween(candyGraphics, { rotation: Math.PI * 2, scaleX: 0.1, scaleY: 0.1, alpha: 0 }, { duration: 300, easing: tween.easeOut, onFinish: function onFinish() { self.destroy(); } }); }; return self; }); var Grid = Container.expand(function () { var self = Container.call(this); self.gridWidth = 8; self.gridHeight = 12; self.cellSize = 130; self.candies = []; self.selectedGroup = []; self.isAnimating = false; // Initialize grid array for (var i = 0; i < self.gridHeight; i++) { self.candies[i] = []; for (var j = 0; j < self.gridWidth; j++) { self.candies[i][j] = null; } } self.getGridPosition = function (x, y) { var startX = (2048 - self.gridWidth * self.cellSize) / 2; var startY = 300; var col = Math.floor((x - startX) / self.cellSize); var row = Math.floor((y - startY) / self.cellSize); return { row: row, col: col }; }; self.getWorldPosition = function (row, col) { var startX = (2048 - self.gridWidth * self.cellSize) / 2; var startY = 300; return { x: startX + col * self.cellSize + self.cellSize / 2, y: startY + row * self.cellSize + self.cellSize / 2 }; }; self.createCandy = function (row, col) { var colorType; // 5% chance for money candy if (Math.random() < 0.05) { colorType = 7; // Money candy } else { colorType = Math.floor(Math.random() * 6); } var candy = new Candy(colorType); var pos = self.getWorldPosition(row, col); candy.x = pos.x; candy.y = pos.y; self.candies[row][col] = candy; self.addChild(candy); return candy; }; self.fillGrid = function () { for (var i = 0; i < self.gridHeight; i++) { for (var j = 0; j < self.gridWidth; j++) { if (!self.candies[i][j]) { self.createCandy(i, j); } } } }; self.findConnectedGroup = function (row, col, colorType, visited) { if (row < 0 || row >= self.gridHeight || col < 0 || col >= self.gridWidth) { return []; } if (visited[row][col] || !self.candies[row][col] || self.candies[row][col].colorType !== colorType) { return []; } visited[row][col] = true; var group = [{ row: row, col: col, candy: self.candies[row][col] }]; // Check adjacent cells (up, down, left, right) var directions = [[-1, 0], [1, 0], [0, -1], [0, 1]]; for (var d = 0; d < directions.length; d++) { var newRow = row + directions[d][0]; var newCol = col + directions[d][1]; var connected = self.findConnectedGroup(newRow, newCol, colorType, visited); group = group.concat(connected); } return group; }; self.selectGroup = function (row, col) { if (self.isAnimating || !self.candies[row][col]) return; // Clear previous selection self.clearSelection(); var visited = []; for (var i = 0; i < self.gridHeight; i++) { visited[i] = []; for (var j = 0; j < self.gridWidth; j++) { visited[i][j] = false; } } var colorType = self.candies[row][col].colorType; self.selectedGroup = self.findConnectedGroup(row, col, colorType, visited); if (self.selectedGroup.length >= 3) { for (var i = 0; i < self.selectedGroup.length; i++) { self.selectedGroup[i].candy.setSelected(true); } return true; } else { self.selectedGroup = []; return false; } }; self.clearSelection = function () { for (var i = 0; i < self.selectedGroup.length; i++) { if (self.selectedGroup[i].candy && !self.selectedGroup[i].candy.isExploding) { self.selectedGroup[i].candy.setSelected(false); } } self.selectedGroup = []; }; self.explodeBomb = function (row, col) { if (!self.candies[row][col] || self.candies[row][col].colorType !== 6) return 0; self.isAnimating = true; var points = 0; var bombsToExplode = []; // Explode 3x3 area around bomb for (var i = -1; i <= 1; i++) { for (var j = -1; j <= 1; j++) { var newRow = row + i; var newCol = col + j; if (newRow >= 0 && newRow < self.gridHeight && newCol >= 0 && newCol < self.gridWidth) { if (self.candies[newRow][newCol]) { bombsToExplode.push({ row: newRow, col: newCol, candy: self.candies[newRow][newCol] }); points += 10; } } } } // Add explosion flash effect to bomb area for (var i = 0; i < bombsToExplode.length; i++) { var item = bombsToExplode[i]; // Flash effect before exploding tween(item.candy, { tint: 0xFF0000, scaleX: 1.3, scaleY: 1.3 }, { duration: 150, easing: tween.easeOut, onFinish: function onFinish() { tween(item.candy, { tint: 0xFFFFFF, scaleX: 1.0, scaleY: 1.0 }, { duration: 100, easing: tween.easeIn }); } }); } // Explode all candies in the bomb area LK.setTimeout(function () { for (var i = 0; i < bombsToExplode.length; i++) { var item = bombsToExplode[i]; self.candies[item.row][item.col] = null; item.candy.explode(); } }, 250); LK.getSound('explode').play(); // Apply gravity and spawn new candies after explosion animation LK.setTimeout(function () { self.applyGravity(); }, 600); return points; }; self.explodeStrongBomb = function (row, col) { self.isAnimating = true; var points = 0; var bombsToExplode = []; // Explode 5x5 area around selected position for (var i = -2; i <= 2; i++) { for (var j = -2; j <= 2; j++) { var newRow = row + i; var newCol = col + j; if (newRow >= 0 && newRow < self.gridHeight && newCol >= 0 && newCol < self.gridWidth) { if (self.candies[newRow][newCol]) { bombsToExplode.push({ row: newRow, col: newCol, candy: self.candies[newRow][newCol] }); points += 15; } } } } // Explode all candies in the bomb area for (var i = 0; i < bombsToExplode.length; i++) { var item = bombsToExplode[i]; self.candies[item.row][item.col] = null; item.candy.explode(); } LK.getSound('explode').play(); // Apply gravity and spawn new candies after explosion animation LK.setTimeout(function () { self.applyGravity(); }, 350); return points; }; self.explodeSelectedGroup = function () { if (self.selectedGroup.length < 3) return 0; self.isAnimating = true; var points = self.selectedGroup.length * 10; var shouldCreateBomb = false; // Bonus points for larger groups if (self.selectedGroup.length >= 5) { points += 50; } if (self.selectedGroup.length >= 4) { shouldCreateBomb = true; } if (self.selectedGroup.length >= 7) { points += 100; } // Apply combo multiplier points = Math.floor(points * comboMultiplier); // Increment combo count comboCount++; comboMultiplier = 1 + (comboCount - 1) * 0.5; // 1x, 1.5x, 2x, 2.5x, etc. comboTimer = comboTimeout; // Update combo display if (comboCount > 1) { comboTxt.setText('COMBO x' + comboCount + '!'); comboTxt.fill = comboCount >= 5 ? 0xFF0000 : 0xFFD700; // Flash combo text tween(comboTxt, { scaleX: 1.3, scaleY: 1.3 }, { duration: 200, easing: tween.easeOut, onFinish: function onFinish() { tween(comboTxt, { scaleX: 1.0, scaleY: 1.0 }, { duration: 200, easing: tween.easeIn }); } }); } // Clear candy references from grid first for (var i = 0; i < self.selectedGroup.length; i++) { var item = self.selectedGroup[i]; self.candies[item.row][item.col] = null; } // Sound will be played when knife touches candies // Create and animate knife to cut candies var knife = new Knife(); self.addChild(knife); knife.cutCandies(self.selectedGroup, function () { // Remove knife after cutting knife.destroy(); // Create sugar bomb if 4 or more sugar candies exploded var sugarCount = 0; for (var i = 0; i < self.selectedGroup.length; i++) { if (self.selectedGroup[i].candy.colorType === 3) { // Yellow candy (sugar) sugarCount++; } } if (sugarCount >= 4) { var bombRow = self.selectedGroup[0].row; var bombCol = self.selectedGroup[0].col; LK.setTimeout(function () { var sugarBomb = new Candy(3); // Using candy_yellow asset (index 3 in candyAssets array) for sugar bomb var pos = self.getWorldPosition(bombRow, bombCol); sugarBomb.x = pos.x; sugarBomb.y = pos.y; self.candies[bombRow][bombCol] = sugarBomb; self.addChild(sugarBomb); // Add visual effect to indicate it's a sugar bomb tween(sugarBomb, { scaleX: 1.3, scaleY: 1.3 }, { duration: 300, easing: tween.easeOut, onFinish: function onFinish() { tween(sugarBomb, { scaleX: 1.0, scaleY: 1.0 }, { duration: 300, easing: tween.easeIn }); } }); }, 100); } // Apply gravity and spawn new candies after cutting animation LK.setTimeout(function () { self.applyGravity(); }, 350); }); self.selectedGroup = []; return points; }; self.applyGravity = function () { var moved = false; // Move candies down for (var col = 0; col < self.gridWidth; col++) { for (var row = self.gridHeight - 1; row >= 0; row--) { if (!self.candies[row][col]) { // Find candy above to move down for (var searchRow = row - 1; searchRow >= 0; searchRow--) { if (self.candies[searchRow][col]) { self.candies[row][col] = self.candies[searchRow][col]; self.candies[searchRow][col] = null; var newPos = self.getWorldPosition(row, col); tween(self.candies[row][col], { x: newPos.x, y: newPos.y }, { duration: 200, easing: tween.easeIn }); moved = true; break; } } } } } // Fill empty spaces at top for (var col = 0; col < self.gridWidth; col++) { for (var row = 0; row < self.gridHeight; row++) { if (!self.candies[row][col]) { var candy = self.createCandy(row, col); // Start candy above screen and animate down candy.y = self.getWorldPosition(row, col).y - 500; var targetPos = self.getWorldPosition(row, col); tween(candy, { y: targetPos.y }, { duration: 300, easing: tween.easeOut }); } } } if (moved) { LK.getSound('cascade').play(); } // Check for chain reactions LK.setTimeout(function () { self.checkChainReactions(); }, 400); }; self.checkChainReactions = function () { // Simply finish the animation process without auto-exploding self.isAnimating = false; // Reset combo if no more valid moves after timeout LK.setTimeout(function () { if (comboTimer > 0) { comboTimer = 0; comboCount = 0; comboMultiplier = 1; comboTxt.setText(''); } }, 100); self.checkGameOver(); }; self.checkGameOver = function () { var visited = []; for (var i = 0; i < self.gridHeight; i++) { visited[i] = []; for (var j = 0; j < self.gridWidth; j++) { visited[i][j] = false; } } for (var row = 0; row < self.gridHeight; row++) { for (var col = 0; col < self.gridWidth; col++) { if (!visited[row][col] && self.candies[row][col]) { var group = self.findConnectedGroup(row, col, self.candies[row][col].colorType, visited); if (group.length >= 3) { return; // Game can continue } } } } // No valid moves left LK.setTimeout(function () { LK.showGameOver(); }, 1000); }; self.explodeAreaBomb = function (row, col) { self.isAnimating = true; var points = 0; var bombsToExplode = []; // Explode 3x3 area around selected position for (var i = -1; i <= 1; i++) { for (var j = -1; j <= 1; j++) { var newRow = row + i; var newCol = col + j; if (newRow >= 0 && newRow < self.gridHeight && newCol >= 0 && newCol < self.gridWidth) { if (self.candies[newRow][newCol]) { bombsToExplode.push({ row: newRow, col: newCol, candy: self.candies[newRow][newCol] }); points += 10; } } } } // Add explosion flash effect to bomb area for (var i = 0; i < bombsToExplode.length; i++) { var item = bombsToExplode[i]; // Flash effect before exploding tween(item.candy, { tint: 0xFF0000, scaleX: 1.3, scaleY: 1.3 }, { duration: 150, easing: tween.easeOut, onFinish: function onFinish() { tween(item.candy, { tint: 0xFFFFFF, scaleX: 1.0, scaleY: 1.0 }, { duration: 100, easing: tween.easeIn }); } }); } // Explode all candies in the bomb area LK.setTimeout(function () { for (var i = 0; i < bombsToExplode.length; i++) { var item = bombsToExplode[i]; self.candies[item.row][item.col] = null; item.candy.explode(); } }, 250); LK.getSound('explode').play(); // Apply gravity and spawn new candies after explosion animation LK.setTimeout(function () { self.applyGravity(); }, 600); return points; }; self.explodeFirework = function (row, col) { self.isAnimating = true; var points = 0; var bombsToExplode = []; // Explode entire row for (var j = 0; j < self.gridWidth; j++) { if (self.candies[row][j]) { bombsToExplode.push({ row: row, col: j, candy: self.candies[row][j] }); points += 12; } } // Explode entire column for (var i = 0; i < self.gridHeight; i++) { if (i !== row && self.candies[i][col]) { bombsToExplode.push({ row: i, col: col, candy: self.candies[i][col] }); points += 12; } } // Add firework flash effect with colorful animation for (var i = 0; i < bombsToExplode.length; i++) { var item = bombsToExplode[i]; // Colorful flash effect before exploding tween(item.candy, { tint: 0xFFD700, scaleX: 1.4, scaleY: 1.4 }, { duration: 200, easing: tween.easeOut, onFinish: function onFinish() { tween(item.candy, { tint: 0xFF69B4, scaleX: 1.2, scaleY: 1.2 }, { duration: 150, easing: tween.easeIn, onFinish: function onFinish() { tween(item.candy, { tint: 0x00FFFF, scaleX: 1.0, scaleY: 1.0 }, { duration: 100, easing: tween.easeIn }); } }); } }); } // Explode all candies in the firework pattern LK.setTimeout(function () { for (var i = 0; i < bombsToExplode.length; i++) { var item = bombsToExplode[i]; self.candies[item.row][item.col] = null; item.candy.explode(); } }, 450); LK.getSound('explode').play(); // Apply gravity and spawn new candies after explosion animation LK.setTimeout(function () { self.applyGravity(); }, 800); return points; }; return self; }); var Knife = Container.expand(function () { var self = Container.call(this); var knifeGraphics = self.attachAsset('knife', { anchorX: 0.5, anchorY: 0.5 }); self.cutCandies = function (targetCandies, onComplete) { if (targetCandies.length === 0) { if (onComplete) onComplete(); return; } // Position knife at start position (off screen left) self.x = -200; self.y = targetCandies[0].candy.y; self.alpha = 1; // Calculate end position (off screen right) var endX = 2248; // Animate knife cutting across screen tween(self, { x: endX }, { duration: 500, easing: tween.easeInOut, onFinish: function onFinish() { // Fade out knife tween(self, { alpha: 0 }, { duration: 200, onFinish: function onFinish() { if (onComplete) onComplete(); } }); } }); // Trigger candy cutting effect when knife reaches each candy for (var i = 0; i < targetCandies.length; i++) { var candy = targetCandies[i].candy; var candyX = candy.x; var delay = (candyX + 200) / (endX + 200) * 500; // Calculate when knife reaches this candy (function (candyTocut) { LK.setTimeout(function () { if (candyTocut && !candyTocut.isExploding) { // Play sound when knife touches candy LK.getSound('explode').play(); candyTocut.explode(); } }, delay); })(candy); } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x2E1065 }); /**** * Game Code ****/ // Add visual background var background = game.addChild(LK.getAsset('Bg', { anchorX: 0, anchorY: 0, scaleX: 20.48, scaleY: 27.32 })); background.x = 0; background.y = 0; var grid = game.addChild(new Grid()); var scoreTxt = new Text2('0', { size: 120, fill: 0xFFFFFF }); scoreTxt.anchor.set(0, 0); scoreTxt.x = 50; scoreTxt.y = 400; game.addChild(scoreTxt); // Initialize score LK.setScore(0); scoreTxt.setText(LK.getScore().toString()); // Fill initial grid grid.fillGrid(); // Instructions text var instructionsTxt = new Text2('Tap groups of 3+ same colored candies!', { size: 60, fill: 0xFFFFFF }); instructionsTxt.anchor.set(0.5, 0); instructionsTxt.x = 2048 / 2; instructionsTxt.y = 120; game.addChild(instructionsTxt); // Set money to 0 when game starts storage.money = 0; // Money counter var moneyTxt = new Text2('Money: ' + storage.money, { size: 80, fill: 0xFFD700 }); moneyTxt.anchor.set(0, 0); moneyTxt.x = 50; moneyTxt.y = 300; game.addChild(moneyTxt); // Shop background var shopBackground = game.addChild(LK.getAsset('gridBackground', { anchorX: 0.5, anchorY: 0, scaleX: 20, scaleY: 4 })); shopBackground.x = 2048 / 2; shopBackground.y = 2180; // Shop UI var shopTxt = new Text2('SHOP', { size: 80, fill: 0xFFFFFF }); shopTxt.anchor.set(0.5, 0); shopTxt.x = 2048 / 2; shopTxt.y = 2200; game.addChild(shopTxt); var areaBombTxt = new Text2('3x3 Area Bomb (Cost: 15)', { size: 60, fill: 0xFFFFFF }); areaBombTxt.anchor.set(0.5, 0); areaBombTxt.x = 2048 / 2; areaBombTxt.y = 2300; game.addChild(areaBombTxt); var strongBombTxt = new Text2('5x5 Area Bomb (Cost: 25)', { size: 60, fill: storage.strongBombUnlocked ? 0x00FF00 : 0xFFFFFF }); strongBombTxt.anchor.set(0.5, 0); strongBombTxt.x = 2048 / 2; strongBombTxt.y = 2400; game.addChild(strongBombTxt); var fireworkTxt = new Text2('Firework Line Exploder (Cost: 20)', { size: 60, fill: 0xFFFFFF }); fireworkTxt.anchor.set(0.5, 0); fireworkTxt.x = 2048 / 2; fireworkTxt.y = 2500; game.addChild(fireworkTxt); // Combo display var comboTxt = new Text2('', { size: 100, fill: 0xFFD700 }); comboTxt.anchor.set(0.5, 0.5); comboTxt.x = 2048 / 2; comboTxt.y = 200; game.addChild(comboTxt); var usingAreaBomb = false; var usingStrongBomb = false; var usingFirework = false; var strongBombTimer = 0; var strongBombCooldown = 45000; // 45 seconds in milliseconds var comboCount = 0; var comboMultiplier = 1; var comboTimer = 0; var comboTimeout = 3000; // 3 seconds to maintain combo game.down = function (x, y, obj) { if (grid.isAnimating) return; // Check if clicked on shop area if (y >= 2200) { // Check if clicked on area bomb purchase if (y >= 2300 && y <= 2400) { if (!usingAreaBomb && storage.money >= 15) { storage.money -= 15; usingAreaBomb = true; moneyTxt.setText('Money: ' + storage.money); areaBombTxt.setText('Select area to explode!'); areaBombTxt.fill = 0xFF0000; } } // Check if clicked on strong bomb purchase if (y >= 2400 && y <= 2500) { if (!storage.strongBombUnlocked && storage.money >= 25) { storage.money -= 25; storage.strongBombUnlocked = true; moneyTxt.setText('Money: ' + storage.money); strongBombTxt.setText('5x5 Area Bomb (Unlocked!)'); strongBombTxt.fill = 0x00FF00; } else if (storage.strongBombUnlocked && !usingStrongBomb) { usingStrongBomb = true; strongBombTxt.setText('Select area to explode!'); strongBombTxt.fill = 0xFF0000; } } // Check if clicked on firework purchase if (y >= 2500 && y <= 2600) { if (!usingFirework && storage.money >= 20) { storage.money -= 20; usingFirework = true; moneyTxt.setText('Money: ' + storage.money); fireworkTxt.setText('Select line to explode!'); fireworkTxt.fill = 0xFF0000; } } return; } var gridPos = grid.getGridPosition(x, y); if (gridPos.row >= 0 && gridPos.row < grid.gridHeight && gridPos.col >= 0 && gridPos.col < grid.gridWidth) { // Check if using area bomb if (usingAreaBomb) { var points = grid.explodeAreaBomb(gridPos.row, gridPos.col); LK.setScore(LK.getScore() + points); scoreTxt.setText(LK.getScore().toString()); usingAreaBomb = false; areaBombTxt.setText('3x3 Area Bomb (Cost: 15)'); areaBombTxt.fill = 0xFFFFFF; return; } // Check if using strong bomb if (usingStrongBomb && storage.strongBombUnlocked) { var points = grid.explodeStrongBomb(gridPos.row, gridPos.col); LK.setScore(LK.getScore() + points); scoreTxt.setText(LK.getScore().toString()); usingStrongBomb = false; // Reset to purchasable state storage.strongBombUnlocked = false; strongBombTxt.setText('5x5 Area Bomb (Cost: 25)'); strongBombTxt.fill = 0xFFFFFF; return; } // Check if using firework if (usingFirework) { var points = grid.explodeFirework(gridPos.row, gridPos.col); LK.setScore(LK.getScore() + points); scoreTxt.setText(LK.getScore().toString()); usingFirework = false; fireworkTxt.setText('Firework Line Exploder (Cost: 20)'); fireworkTxt.fill = 0xFFFFFF; return; } // Check if clicked candy is money candy if (grid.candies[gridPos.row][gridPos.col] && grid.candies[gridPos.row][gridPos.col].colorType === 7) { // Give money and remove candy storage.money += 5; moneyTxt.setText('Money: ' + storage.money); grid.candies[gridPos.row][gridPos.col].explode(); grid.candies[gridPos.row][gridPos.col] = null; LK.setTimeout(function () { grid.applyGravity(); }, 350); return; } // Check if clicked candy is a bomb if (grid.candies[gridPos.row][gridPos.col] && grid.candies[gridPos.row][gridPos.col].colorType === 6) { // Explode bomb immediately var points = grid.explodeBomb(gridPos.row, gridPos.col); LK.setScore(LK.getScore() + points); scoreTxt.setText(LK.getScore().toString()); } else if (grid.selectGroup(gridPos.row, gridPos.col)) { // Valid group selected, explode on next tap LK.setTimeout(function () { if (grid.selectedGroup.length >= 3) { var points = grid.explodeSelectedGroup(); LK.setScore(LK.getScore() + points); scoreTxt.setText(LK.getScore().toString()); } }, 100); } else { // Invalid move - reset combo comboCount = 0; comboMultiplier = 1; comboTimer = 0; comboTxt.setText(''); } } }; // Start background music LK.playMusic('bgMusic'); game.update = function () { // Timer logic removed - no cooldown needed // Update combo timer if (comboTimer > 0) { comboTimer -= 16; // Approximately 60 FPS if (comboTimer <= 0) { // Reset combo comboCount = 0; comboMultiplier = 1; comboTxt.setText(''); } } };
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
money: 0,
strongBombUnlocked: false
});
/****
* Classes
****/
var Candy = Container.expand(function (colorType) {
var self = Container.call(this);
self.colorType = colorType;
self.isSelected = false;
self.isExploding = false;
var candyAssets = ['candy_red', 'candy_blue', 'candy_green', 'candy_yellow', 'candy_purple', 'candy_orange', 'candy_bomb', 'candy_money'];
var candyGraphics = self.attachAsset(candyAssets[colorType], {
anchorX: 0.5,
anchorY: 0.5
});
self.setSelected = function (selected) {
self.isSelected = selected;
if (selected) {
candyGraphics.scaleX = 1.2;
candyGraphics.scaleY = 1.2;
} else {
candyGraphics.scaleX = 1.0;
candyGraphics.scaleY = 1.0;
}
};
self.explode = function () {
self.isExploding = true;
// Cut effect: rotate and scale down with fade
tween(candyGraphics, {
rotation: Math.PI * 2,
scaleX: 0.1,
scaleY: 0.1,
alpha: 0
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
self.destroy();
}
});
};
return self;
});
var Grid = Container.expand(function () {
var self = Container.call(this);
self.gridWidth = 8;
self.gridHeight = 12;
self.cellSize = 130;
self.candies = [];
self.selectedGroup = [];
self.isAnimating = false;
// Initialize grid array
for (var i = 0; i < self.gridHeight; i++) {
self.candies[i] = [];
for (var j = 0; j < self.gridWidth; j++) {
self.candies[i][j] = null;
}
}
self.getGridPosition = function (x, y) {
var startX = (2048 - self.gridWidth * self.cellSize) / 2;
var startY = 300;
var col = Math.floor((x - startX) / self.cellSize);
var row = Math.floor((y - startY) / self.cellSize);
return {
row: row,
col: col
};
};
self.getWorldPosition = function (row, col) {
var startX = (2048 - self.gridWidth * self.cellSize) / 2;
var startY = 300;
return {
x: startX + col * self.cellSize + self.cellSize / 2,
y: startY + row * self.cellSize + self.cellSize / 2
};
};
self.createCandy = function (row, col) {
var colorType;
// 5% chance for money candy
if (Math.random() < 0.05) {
colorType = 7; // Money candy
} else {
colorType = Math.floor(Math.random() * 6);
}
var candy = new Candy(colorType);
var pos = self.getWorldPosition(row, col);
candy.x = pos.x;
candy.y = pos.y;
self.candies[row][col] = candy;
self.addChild(candy);
return candy;
};
self.fillGrid = function () {
for (var i = 0; i < self.gridHeight; i++) {
for (var j = 0; j < self.gridWidth; j++) {
if (!self.candies[i][j]) {
self.createCandy(i, j);
}
}
}
};
self.findConnectedGroup = function (row, col, colorType, visited) {
if (row < 0 || row >= self.gridHeight || col < 0 || col >= self.gridWidth) {
return [];
}
if (visited[row][col] || !self.candies[row][col] || self.candies[row][col].colorType !== colorType) {
return [];
}
visited[row][col] = true;
var group = [{
row: row,
col: col,
candy: self.candies[row][col]
}];
// Check adjacent cells (up, down, left, right)
var directions = [[-1, 0], [1, 0], [0, -1], [0, 1]];
for (var d = 0; d < directions.length; d++) {
var newRow = row + directions[d][0];
var newCol = col + directions[d][1];
var connected = self.findConnectedGroup(newRow, newCol, colorType, visited);
group = group.concat(connected);
}
return group;
};
self.selectGroup = function (row, col) {
if (self.isAnimating || !self.candies[row][col]) return;
// Clear previous selection
self.clearSelection();
var visited = [];
for (var i = 0; i < self.gridHeight; i++) {
visited[i] = [];
for (var j = 0; j < self.gridWidth; j++) {
visited[i][j] = false;
}
}
var colorType = self.candies[row][col].colorType;
self.selectedGroup = self.findConnectedGroup(row, col, colorType, visited);
if (self.selectedGroup.length >= 3) {
for (var i = 0; i < self.selectedGroup.length; i++) {
self.selectedGroup[i].candy.setSelected(true);
}
return true;
} else {
self.selectedGroup = [];
return false;
}
};
self.clearSelection = function () {
for (var i = 0; i < self.selectedGroup.length; i++) {
if (self.selectedGroup[i].candy && !self.selectedGroup[i].candy.isExploding) {
self.selectedGroup[i].candy.setSelected(false);
}
}
self.selectedGroup = [];
};
self.explodeBomb = function (row, col) {
if (!self.candies[row][col] || self.candies[row][col].colorType !== 6) return 0;
self.isAnimating = true;
var points = 0;
var bombsToExplode = [];
// Explode 3x3 area around bomb
for (var i = -1; i <= 1; i++) {
for (var j = -1; j <= 1; j++) {
var newRow = row + i;
var newCol = col + j;
if (newRow >= 0 && newRow < self.gridHeight && newCol >= 0 && newCol < self.gridWidth) {
if (self.candies[newRow][newCol]) {
bombsToExplode.push({
row: newRow,
col: newCol,
candy: self.candies[newRow][newCol]
});
points += 10;
}
}
}
}
// Add explosion flash effect to bomb area
for (var i = 0; i < bombsToExplode.length; i++) {
var item = bombsToExplode[i];
// Flash effect before exploding
tween(item.candy, {
tint: 0xFF0000,
scaleX: 1.3,
scaleY: 1.3
}, {
duration: 150,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(item.candy, {
tint: 0xFFFFFF,
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 100,
easing: tween.easeIn
});
}
});
}
// Explode all candies in the bomb area
LK.setTimeout(function () {
for (var i = 0; i < bombsToExplode.length; i++) {
var item = bombsToExplode[i];
self.candies[item.row][item.col] = null;
item.candy.explode();
}
}, 250);
LK.getSound('explode').play();
// Apply gravity and spawn new candies after explosion animation
LK.setTimeout(function () {
self.applyGravity();
}, 600);
return points;
};
self.explodeStrongBomb = function (row, col) {
self.isAnimating = true;
var points = 0;
var bombsToExplode = [];
// Explode 5x5 area around selected position
for (var i = -2; i <= 2; i++) {
for (var j = -2; j <= 2; j++) {
var newRow = row + i;
var newCol = col + j;
if (newRow >= 0 && newRow < self.gridHeight && newCol >= 0 && newCol < self.gridWidth) {
if (self.candies[newRow][newCol]) {
bombsToExplode.push({
row: newRow,
col: newCol,
candy: self.candies[newRow][newCol]
});
points += 15;
}
}
}
}
// Explode all candies in the bomb area
for (var i = 0; i < bombsToExplode.length; i++) {
var item = bombsToExplode[i];
self.candies[item.row][item.col] = null;
item.candy.explode();
}
LK.getSound('explode').play();
// Apply gravity and spawn new candies after explosion animation
LK.setTimeout(function () {
self.applyGravity();
}, 350);
return points;
};
self.explodeSelectedGroup = function () {
if (self.selectedGroup.length < 3) return 0;
self.isAnimating = true;
var points = self.selectedGroup.length * 10;
var shouldCreateBomb = false;
// Bonus points for larger groups
if (self.selectedGroup.length >= 5) {
points += 50;
}
if (self.selectedGroup.length >= 4) {
shouldCreateBomb = true;
}
if (self.selectedGroup.length >= 7) {
points += 100;
}
// Apply combo multiplier
points = Math.floor(points * comboMultiplier);
// Increment combo count
comboCount++;
comboMultiplier = 1 + (comboCount - 1) * 0.5; // 1x, 1.5x, 2x, 2.5x, etc.
comboTimer = comboTimeout;
// Update combo display
if (comboCount > 1) {
comboTxt.setText('COMBO x' + comboCount + '!');
comboTxt.fill = comboCount >= 5 ? 0xFF0000 : 0xFFD700;
// Flash combo text
tween(comboTxt, {
scaleX: 1.3,
scaleY: 1.3
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(comboTxt, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 200,
easing: tween.easeIn
});
}
});
}
// Clear candy references from grid first
for (var i = 0; i < self.selectedGroup.length; i++) {
var item = self.selectedGroup[i];
self.candies[item.row][item.col] = null;
}
// Sound will be played when knife touches candies
// Create and animate knife to cut candies
var knife = new Knife();
self.addChild(knife);
knife.cutCandies(self.selectedGroup, function () {
// Remove knife after cutting
knife.destroy();
// Create sugar bomb if 4 or more sugar candies exploded
var sugarCount = 0;
for (var i = 0; i < self.selectedGroup.length; i++) {
if (self.selectedGroup[i].candy.colorType === 3) {
// Yellow candy (sugar)
sugarCount++;
}
}
if (sugarCount >= 4) {
var bombRow = self.selectedGroup[0].row;
var bombCol = self.selectedGroup[0].col;
LK.setTimeout(function () {
var sugarBomb = new Candy(3); // Using candy_yellow asset (index 3 in candyAssets array) for sugar bomb
var pos = self.getWorldPosition(bombRow, bombCol);
sugarBomb.x = pos.x;
sugarBomb.y = pos.y;
self.candies[bombRow][bombCol] = sugarBomb;
self.addChild(sugarBomb);
// Add visual effect to indicate it's a sugar bomb
tween(sugarBomb, {
scaleX: 1.3,
scaleY: 1.3
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(sugarBomb, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 300,
easing: tween.easeIn
});
}
});
}, 100);
}
// Apply gravity and spawn new candies after cutting animation
LK.setTimeout(function () {
self.applyGravity();
}, 350);
});
self.selectedGroup = [];
return points;
};
self.applyGravity = function () {
var moved = false;
// Move candies down
for (var col = 0; col < self.gridWidth; col++) {
for (var row = self.gridHeight - 1; row >= 0; row--) {
if (!self.candies[row][col]) {
// Find candy above to move down
for (var searchRow = row - 1; searchRow >= 0; searchRow--) {
if (self.candies[searchRow][col]) {
self.candies[row][col] = self.candies[searchRow][col];
self.candies[searchRow][col] = null;
var newPos = self.getWorldPosition(row, col);
tween(self.candies[row][col], {
x: newPos.x,
y: newPos.y
}, {
duration: 200,
easing: tween.easeIn
});
moved = true;
break;
}
}
}
}
}
// Fill empty spaces at top
for (var col = 0; col < self.gridWidth; col++) {
for (var row = 0; row < self.gridHeight; row++) {
if (!self.candies[row][col]) {
var candy = self.createCandy(row, col);
// Start candy above screen and animate down
candy.y = self.getWorldPosition(row, col).y - 500;
var targetPos = self.getWorldPosition(row, col);
tween(candy, {
y: targetPos.y
}, {
duration: 300,
easing: tween.easeOut
});
}
}
}
if (moved) {
LK.getSound('cascade').play();
}
// Check for chain reactions
LK.setTimeout(function () {
self.checkChainReactions();
}, 400);
};
self.checkChainReactions = function () {
// Simply finish the animation process without auto-exploding
self.isAnimating = false;
// Reset combo if no more valid moves after timeout
LK.setTimeout(function () {
if (comboTimer > 0) {
comboTimer = 0;
comboCount = 0;
comboMultiplier = 1;
comboTxt.setText('');
}
}, 100);
self.checkGameOver();
};
self.checkGameOver = function () {
var visited = [];
for (var i = 0; i < self.gridHeight; i++) {
visited[i] = [];
for (var j = 0; j < self.gridWidth; j++) {
visited[i][j] = false;
}
}
for (var row = 0; row < self.gridHeight; row++) {
for (var col = 0; col < self.gridWidth; col++) {
if (!visited[row][col] && self.candies[row][col]) {
var group = self.findConnectedGroup(row, col, self.candies[row][col].colorType, visited);
if (group.length >= 3) {
return; // Game can continue
}
}
}
}
// No valid moves left
LK.setTimeout(function () {
LK.showGameOver();
}, 1000);
};
self.explodeAreaBomb = function (row, col) {
self.isAnimating = true;
var points = 0;
var bombsToExplode = [];
// Explode 3x3 area around selected position
for (var i = -1; i <= 1; i++) {
for (var j = -1; j <= 1; j++) {
var newRow = row + i;
var newCol = col + j;
if (newRow >= 0 && newRow < self.gridHeight && newCol >= 0 && newCol < self.gridWidth) {
if (self.candies[newRow][newCol]) {
bombsToExplode.push({
row: newRow,
col: newCol,
candy: self.candies[newRow][newCol]
});
points += 10;
}
}
}
}
// Add explosion flash effect to bomb area
for (var i = 0; i < bombsToExplode.length; i++) {
var item = bombsToExplode[i];
// Flash effect before exploding
tween(item.candy, {
tint: 0xFF0000,
scaleX: 1.3,
scaleY: 1.3
}, {
duration: 150,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(item.candy, {
tint: 0xFFFFFF,
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 100,
easing: tween.easeIn
});
}
});
}
// Explode all candies in the bomb area
LK.setTimeout(function () {
for (var i = 0; i < bombsToExplode.length; i++) {
var item = bombsToExplode[i];
self.candies[item.row][item.col] = null;
item.candy.explode();
}
}, 250);
LK.getSound('explode').play();
// Apply gravity and spawn new candies after explosion animation
LK.setTimeout(function () {
self.applyGravity();
}, 600);
return points;
};
self.explodeFirework = function (row, col) {
self.isAnimating = true;
var points = 0;
var bombsToExplode = [];
// Explode entire row
for (var j = 0; j < self.gridWidth; j++) {
if (self.candies[row][j]) {
bombsToExplode.push({
row: row,
col: j,
candy: self.candies[row][j]
});
points += 12;
}
}
// Explode entire column
for (var i = 0; i < self.gridHeight; i++) {
if (i !== row && self.candies[i][col]) {
bombsToExplode.push({
row: i,
col: col,
candy: self.candies[i][col]
});
points += 12;
}
}
// Add firework flash effect with colorful animation
for (var i = 0; i < bombsToExplode.length; i++) {
var item = bombsToExplode[i];
// Colorful flash effect before exploding
tween(item.candy, {
tint: 0xFFD700,
scaleX: 1.4,
scaleY: 1.4
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(item.candy, {
tint: 0xFF69B4,
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 150,
easing: tween.easeIn,
onFinish: function onFinish() {
tween(item.candy, {
tint: 0x00FFFF,
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 100,
easing: tween.easeIn
});
}
});
}
});
}
// Explode all candies in the firework pattern
LK.setTimeout(function () {
for (var i = 0; i < bombsToExplode.length; i++) {
var item = bombsToExplode[i];
self.candies[item.row][item.col] = null;
item.candy.explode();
}
}, 450);
LK.getSound('explode').play();
// Apply gravity and spawn new candies after explosion animation
LK.setTimeout(function () {
self.applyGravity();
}, 800);
return points;
};
return self;
});
var Knife = Container.expand(function () {
var self = Container.call(this);
var knifeGraphics = self.attachAsset('knife', {
anchorX: 0.5,
anchorY: 0.5
});
self.cutCandies = function (targetCandies, onComplete) {
if (targetCandies.length === 0) {
if (onComplete) onComplete();
return;
}
// Position knife at start position (off screen left)
self.x = -200;
self.y = targetCandies[0].candy.y;
self.alpha = 1;
// Calculate end position (off screen right)
var endX = 2248;
// Animate knife cutting across screen
tween(self, {
x: endX
}, {
duration: 500,
easing: tween.easeInOut,
onFinish: function onFinish() {
// Fade out knife
tween(self, {
alpha: 0
}, {
duration: 200,
onFinish: function onFinish() {
if (onComplete) onComplete();
}
});
}
});
// Trigger candy cutting effect when knife reaches each candy
for (var i = 0; i < targetCandies.length; i++) {
var candy = targetCandies[i].candy;
var candyX = candy.x;
var delay = (candyX + 200) / (endX + 200) * 500; // Calculate when knife reaches this candy
(function (candyTocut) {
LK.setTimeout(function () {
if (candyTocut && !candyTocut.isExploding) {
// Play sound when knife touches candy
LK.getSound('explode').play();
candyTocut.explode();
}
}, delay);
})(candy);
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x2E1065
});
/****
* Game Code
****/
// Add visual background
var background = game.addChild(LK.getAsset('Bg', {
anchorX: 0,
anchorY: 0,
scaleX: 20.48,
scaleY: 27.32
}));
background.x = 0;
background.y = 0;
var grid = game.addChild(new Grid());
var scoreTxt = new Text2('0', {
size: 120,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0, 0);
scoreTxt.x = 50;
scoreTxt.y = 400;
game.addChild(scoreTxt);
// Initialize score
LK.setScore(0);
scoreTxt.setText(LK.getScore().toString());
// Fill initial grid
grid.fillGrid();
// Instructions text
var instructionsTxt = new Text2('Tap groups of 3+ same colored candies!', {
size: 60,
fill: 0xFFFFFF
});
instructionsTxt.anchor.set(0.5, 0);
instructionsTxt.x = 2048 / 2;
instructionsTxt.y = 120;
game.addChild(instructionsTxt);
// Set money to 0 when game starts
storage.money = 0;
// Money counter
var moneyTxt = new Text2('Money: ' + storage.money, {
size: 80,
fill: 0xFFD700
});
moneyTxt.anchor.set(0, 0);
moneyTxt.x = 50;
moneyTxt.y = 300;
game.addChild(moneyTxt);
// Shop background
var shopBackground = game.addChild(LK.getAsset('gridBackground', {
anchorX: 0.5,
anchorY: 0,
scaleX: 20,
scaleY: 4
}));
shopBackground.x = 2048 / 2;
shopBackground.y = 2180;
// Shop UI
var shopTxt = new Text2('SHOP', {
size: 80,
fill: 0xFFFFFF
});
shopTxt.anchor.set(0.5, 0);
shopTxt.x = 2048 / 2;
shopTxt.y = 2200;
game.addChild(shopTxt);
var areaBombTxt = new Text2('3x3 Area Bomb (Cost: 15)', {
size: 60,
fill: 0xFFFFFF
});
areaBombTxt.anchor.set(0.5, 0);
areaBombTxt.x = 2048 / 2;
areaBombTxt.y = 2300;
game.addChild(areaBombTxt);
var strongBombTxt = new Text2('5x5 Area Bomb (Cost: 25)', {
size: 60,
fill: storage.strongBombUnlocked ? 0x00FF00 : 0xFFFFFF
});
strongBombTxt.anchor.set(0.5, 0);
strongBombTxt.x = 2048 / 2;
strongBombTxt.y = 2400;
game.addChild(strongBombTxt);
var fireworkTxt = new Text2('Firework Line Exploder (Cost: 20)', {
size: 60,
fill: 0xFFFFFF
});
fireworkTxt.anchor.set(0.5, 0);
fireworkTxt.x = 2048 / 2;
fireworkTxt.y = 2500;
game.addChild(fireworkTxt);
// Combo display
var comboTxt = new Text2('', {
size: 100,
fill: 0xFFD700
});
comboTxt.anchor.set(0.5, 0.5);
comboTxt.x = 2048 / 2;
comboTxt.y = 200;
game.addChild(comboTxt);
var usingAreaBomb = false;
var usingStrongBomb = false;
var usingFirework = false;
var strongBombTimer = 0;
var strongBombCooldown = 45000; // 45 seconds in milliseconds
var comboCount = 0;
var comboMultiplier = 1;
var comboTimer = 0;
var comboTimeout = 3000; // 3 seconds to maintain combo
game.down = function (x, y, obj) {
if (grid.isAnimating) return;
// Check if clicked on shop area
if (y >= 2200) {
// Check if clicked on area bomb purchase
if (y >= 2300 && y <= 2400) {
if (!usingAreaBomb && storage.money >= 15) {
storage.money -= 15;
usingAreaBomb = true;
moneyTxt.setText('Money: ' + storage.money);
areaBombTxt.setText('Select area to explode!');
areaBombTxt.fill = 0xFF0000;
}
}
// Check if clicked on strong bomb purchase
if (y >= 2400 && y <= 2500) {
if (!storage.strongBombUnlocked && storage.money >= 25) {
storage.money -= 25;
storage.strongBombUnlocked = true;
moneyTxt.setText('Money: ' + storage.money);
strongBombTxt.setText('5x5 Area Bomb (Unlocked!)');
strongBombTxt.fill = 0x00FF00;
} else if (storage.strongBombUnlocked && !usingStrongBomb) {
usingStrongBomb = true;
strongBombTxt.setText('Select area to explode!');
strongBombTxt.fill = 0xFF0000;
}
}
// Check if clicked on firework purchase
if (y >= 2500 && y <= 2600) {
if (!usingFirework && storage.money >= 20) {
storage.money -= 20;
usingFirework = true;
moneyTxt.setText('Money: ' + storage.money);
fireworkTxt.setText('Select line to explode!');
fireworkTxt.fill = 0xFF0000;
}
}
return;
}
var gridPos = grid.getGridPosition(x, y);
if (gridPos.row >= 0 && gridPos.row < grid.gridHeight && gridPos.col >= 0 && gridPos.col < grid.gridWidth) {
// Check if using area bomb
if (usingAreaBomb) {
var points = grid.explodeAreaBomb(gridPos.row, gridPos.col);
LK.setScore(LK.getScore() + points);
scoreTxt.setText(LK.getScore().toString());
usingAreaBomb = false;
areaBombTxt.setText('3x3 Area Bomb (Cost: 15)');
areaBombTxt.fill = 0xFFFFFF;
return;
}
// Check if using strong bomb
if (usingStrongBomb && storage.strongBombUnlocked) {
var points = grid.explodeStrongBomb(gridPos.row, gridPos.col);
LK.setScore(LK.getScore() + points);
scoreTxt.setText(LK.getScore().toString());
usingStrongBomb = false;
// Reset to purchasable state
storage.strongBombUnlocked = false;
strongBombTxt.setText('5x5 Area Bomb (Cost: 25)');
strongBombTxt.fill = 0xFFFFFF;
return;
}
// Check if using firework
if (usingFirework) {
var points = grid.explodeFirework(gridPos.row, gridPos.col);
LK.setScore(LK.getScore() + points);
scoreTxt.setText(LK.getScore().toString());
usingFirework = false;
fireworkTxt.setText('Firework Line Exploder (Cost: 20)');
fireworkTxt.fill = 0xFFFFFF;
return;
}
// Check if clicked candy is money candy
if (grid.candies[gridPos.row][gridPos.col] && grid.candies[gridPos.row][gridPos.col].colorType === 7) {
// Give money and remove candy
storage.money += 5;
moneyTxt.setText('Money: ' + storage.money);
grid.candies[gridPos.row][gridPos.col].explode();
grid.candies[gridPos.row][gridPos.col] = null;
LK.setTimeout(function () {
grid.applyGravity();
}, 350);
return;
}
// Check if clicked candy is a bomb
if (grid.candies[gridPos.row][gridPos.col] && grid.candies[gridPos.row][gridPos.col].colorType === 6) {
// Explode bomb immediately
var points = grid.explodeBomb(gridPos.row, gridPos.col);
LK.setScore(LK.getScore() + points);
scoreTxt.setText(LK.getScore().toString());
} else if (grid.selectGroup(gridPos.row, gridPos.col)) {
// Valid group selected, explode on next tap
LK.setTimeout(function () {
if (grid.selectedGroup.length >= 3) {
var points = grid.explodeSelectedGroup();
LK.setScore(LK.getScore() + points);
scoreTxt.setText(LK.getScore().toString());
}
}, 100);
} else {
// Invalid move - reset combo
comboCount = 0;
comboMultiplier = 1;
comboTimer = 0;
comboTxt.setText('');
}
}
};
// Start background music
LK.playMusic('bgMusic');
game.update = function () {
// Timer logic removed - no cooldown needed
// Update combo timer
if (comboTimer > 0) {
comboTimer -= 16; // Approximately 60 FPS
if (comboTimer <= 0) {
// Reset combo
comboCount = 0;
comboMultiplier = 1;
comboTxt.setText('');
}
}
};
uncut watermelon. In-Game asset. 2d. High contrast. No shadows
Coin. In-Game asset. 2d. High contrast. No shadows
uncut lemon. In-Game asset. 2d. High contrast. No shadows
Uncut orange. In-Game asset. 2d. High contrast. No shadows
Uncut dragonfruit. In-Game asset. 2d. High contrast. No shadows
Strawberry. In-Game asset. 2d. High contrast. No shadows
Blueberry. In-Game asset. 2d. High contrast. No shadows
Bomb. In-Game asset. 2d. High contrast. No shadows
HORIZONTAL BLADE. In-Game asset. 2d. High contrast. No shadows