/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Candy class
var Candy = Container.expand(function () {
var self = Container.call(this);
// Color index: 0-4
self.colorIndex = 0;
self.row = 0;
self.col = 0;
self.isMoving = false; // Used for animation
// Attach asset
self.setColor = function (idx) {
self.colorIndex = idx;
if (self.candyAsset) {
self.removeChild(self.candyAsset);
}
var colorIds = ['candyRed', 'candyGreen', 'candyBlue', 'candyYellow', 'candyPurple'];
self.candyAsset = self.attachAsset(colorIds[idx], {
anchorX: 0.5,
anchorY: 0.5
});
};
// Animate to (x, y)
self.animateTo = function (x, y, _onFinish) {
self.isMoving = true;
tween(self, {
x: x,
y: y
}, {
duration: 180,
easing: tween.cubicInOut,
onFinish: function onFinish() {
self.isMoving = false;
if (_onFinish) _onFinish();
}
});
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x222244
});
/****
* Game Code
****/
// --- Game constants ---
// 5 candy colors as ellipses
// Simple pop sound for matches
var BOARD_COLS = 7;
var BOARD_ROWS = 9;
var CANDY_SIZE = 200; // px, including margin
var CANDY_MARGIN = 10;
var COLORS = 5;
var MOVES_LIMIT = 30;
// --- Game state ---
var board = []; // 2D array [row][col] of Candy
var selectedCandy = null;
var swappingCandy = null;
var isSwapping = false;
var isAnimating = false;
var movesLeft = MOVES_LIMIT;
var score = 0;
// --- UI elements ---
var scoreTxt = new Text2('Score: 0', {
size: 90,
fill: "#fff"
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
var movesTxt = new Text2('Moves: ' + MOVES_LIMIT, {
size: 70,
fill: "#fff"
});
movesTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(movesTxt);
movesTxt.y = 110;
// --- Board positioning ---
var boardWidth = BOARD_COLS * CANDY_SIZE;
var boardHeight = BOARD_ROWS * CANDY_SIZE;
var boardOffsetX = Math.floor((2048 - boardWidth) / 2) + CANDY_SIZE / 2;
var boardOffsetY = Math.floor((2732 - boardHeight) / 2) + CANDY_SIZE / 2;
// --- Helper functions ---
function getCandyPos(row, col) {
return {
x: boardOffsetX + col * CANDY_SIZE,
y: boardOffsetY + row * CANDY_SIZE
};
}
function randomColor() {
return Math.floor(Math.random() * COLORS);
}
// Returns true if (r1,c1) and (r2,c2) are adjacent
function areAdjacent(r1, c1, r2, c2) {
return Math.abs(r1 - r2) + Math.abs(c1 - c2) === 1;
}
// Returns array of {row, col} for all matches (3+ in row or col)
function findMatches() {
var matches = [];
// Horizontal
for (var r = 0; r < BOARD_ROWS; r++) {
var run = 1;
for (var c = 1; c < BOARD_COLS; c++) {
var prev = board[r][c - 1];
var curr = board[r][c];
if (prev && curr && prev.colorIndex === curr.colorIndex) {
run++;
} else {
if (run >= 3) {
for (var k = 0; k < run; k++) {
matches.push({
row: r,
col: c - 1 - k
});
}
}
run = 1;
}
}
if (run >= 3) {
for (var k = 0; k < run; k++) {
matches.push({
row: r,
col: BOARD_COLS - 1 - k
});
}
}
}
// Vertical
for (var c = 0; c < BOARD_COLS; c++) {
var run = 1;
for (var r = 1; r < BOARD_ROWS; r++) {
var prev = board[r - 1][c];
var curr = board[r][c];
if (prev && curr && prev.colorIndex === curr.colorIndex) {
run++;
} else {
if (run >= 3) {
for (var k = 0; k < run; k++) {
matches.push({
row: r - 1 - k,
col: c
});
}
}
run = 1;
}
}
if (run >= 3) {
for (var k = 0; k < run; k++) {
matches.push({
row: BOARD_ROWS - 1 - k,
col: c
});
}
}
}
// Remove duplicates
var seen = {};
var unique = [];
for (var i = 0; i < matches.length; i++) {
var key = matches[i].row + ',' + matches[i].col;
if (!seen[key]) {
unique.push(matches[i]);
seen[key] = true;
}
}
return unique;
}
// Remove candies at given positions, return number removed
function removeMatches(matches) {
for (var i = 0; i < matches.length; i++) {
var r = matches[i].row,
c = matches[i].col;
var candy = board[r][c];
if (candy) {
(function (candy) {
tween(candy, {
alpha: 0,
scaleX: 1.3,
scaleY: 1.3
}, {
duration: 180,
easing: tween.cubicIn,
onFinish: function onFinish() {
if (candy.parent) candy.parent.removeChild(candy);
}
});
})(candy);
board[r][c] = null;
}
}
if (matches.length > 0) {
LK.getSound('pop').play();
}
return matches.length;
}
// Drop candies down to fill empty spaces, return true if any moved
function dropCandies(onFinish) {
var anyMoved = false;
var animCount = 0;
for (var c = 0; c < BOARD_COLS; c++) {
for (var r = BOARD_ROWS - 1; r >= 0; r--) {
if (!board[r][c]) {
// Find first non-null above
for (var rr = r - 1; rr >= 0; rr--) {
if (board[rr][c]) {
// Move candy down
var candy = board[rr][c];
board[r][c] = candy;
board[rr][c] = null;
candy.row = r;
candy.col = c;
var pos = getCandyPos(r, c);
animCount++;
candy.animateTo(pos.x, pos.y, function () {
animCount--;
if (animCount === 0 && onFinish) onFinish();
});
anyMoved = true;
break;
}
}
}
}
}
// If no candies moved, call onFinish immediately
if (!anyMoved && onFinish) {
onFinish();
}
return anyMoved;
}
// Fill empty spaces at top with new candies, return true if any added
function fillCandies(onFinish) {
var anyAdded = false;
var animCount = 0;
for (var c = 0; c < BOARD_COLS; c++) {
for (var r = 0; r < BOARD_ROWS; r++) {
if (!board[r][c]) {
var candy = new Candy();
var color = randomColor();
candy.setColor(color);
candy.row = r;
candy.col = c;
var pos = getCandyPos(r, c);
// Start above board
var startY = boardOffsetY - CANDY_SIZE;
candy.x = pos.x;
candy.y = startY;
candy.alpha = 0.7;
candy.scaleX = 0.7;
candy.scaleY = 0.7;
game.addChild(candy);
board[r][c] = candy;
animCount++;
(function (candy, pos) {
candy.animateTo(pos.x, pos.y, function () {
tween(candy, {
alpha: 1,
scaleX: 1,
scaleY: 1
}, {
duration: 100
});
animCount--;
if (animCount === 0 && onFinish) onFinish();
});
})(candy, pos);
anyAdded = true;
}
}
}
if (!anyAdded && onFinish) {
onFinish();
}
return anyAdded;
}
// After a move, process all matches, drops, fills, recursively
function processBoard(afterAll) {
isAnimating = true;
var matches = findMatches();
if (matches.length === 0) {
isAnimating = false;
if (afterAll) afterAll();
return;
}
score += matches.length * 10;
scoreTxt.setText('Score: ' + score);
removeMatches(matches);
LK.setTimeout(function () {
dropCandies(function () {
fillCandies(function () {
LK.setTimeout(function () {
processBoard(afterAll);
}, 80);
});
});
}, 200);
}
// Swap two candies (row1,col1) <-> (row2,col2)
function swapCandies(r1, c1, r2, c2, animate, onFinish) {
var candyA = board[r1][c1];
var candyB = board[r2][c2];
if (!candyA || !candyB) return;
// Swap in board
board[r1][c1] = candyB;
board[r2][c2] = candyA;
// Update row/col
candyA.row = r2;
candyA.col = c2;
candyB.row = r1;
candyB.col = c1;
// Animate
if (animate) {
var posA = getCandyPos(r2, c2);
var posB = getCandyPos(r1, c1);
var doneA = false,
doneB = false;
candyA.animateTo(posA.x, posA.y, function () {
doneA = true;
if (doneB && onFinish) onFinish();
});
candyB.animateTo(posB.x, posB.y, function () {
doneB = true;
if (doneA && onFinish) onFinish();
});
} else {
var posA = getCandyPos(r2, c2);
var posB = getCandyPos(r1, c1);
candyA.x = posA.x;
candyA.y = posA.y;
candyB.x = posB.x;
candyB.y = posB.y;
if (onFinish) onFinish();
}
}
// --- Board setup ---
function noInitialMatches(r, c, colorIdx) {
// Check left 2
if (c >= 2 && board[r][c - 1] && board[r][c - 2]) {
if (board[r][c - 1].colorIndex === colorIdx && board[r][c - 2].colorIndex === colorIdx) {
return false;
}
}
// Check up 2
if (r >= 2 && board[r - 1][c] && board[r - 2][c]) {
if (board[r - 1][c].colorIndex === colorIdx && board[r - 2][c].colorIndex === colorIdx) {
return false;
}
}
return true;
}
// Initialize board
for (var r = 0; r < BOARD_ROWS; r++) {
board[r] = [];
for (var c = 0; c < BOARD_COLS; c++) {
var candy = new Candy();
var color = randomColor();
// Avoid initial matches
while (!noInitialMatches(r, c, color)) {
color = randomColor();
}
candy.setColor(color);
candy.row = r;
candy.col = c;
var pos = getCandyPos(r, c);
candy.x = pos.x;
candy.y = pos.y;
game.addChild(candy);
board[r][c] = candy;
}
}
// --- Input handling ---
// Convert (x, y) to (row, col) on board, or null if outside
function posToCell(x, y) {
var col = Math.floor((x - boardOffsetX + CANDY_SIZE / 2) / CANDY_SIZE);
var row = Math.floor((y - boardOffsetY + CANDY_SIZE / 2) / CANDY_SIZE);
if (row < 0 || row >= BOARD_ROWS || col < 0 || col >= BOARD_COLS) return null;
return {
row: row,
col: col
};
}
// Highlight selected candy (simple scale up)
function highlightCandy(candy) {
if (!candy) return;
tween(candy, {
scaleX: 1.18,
scaleY: 1.18
}, {
duration: 100,
easing: tween.cubicOut
});
}
function unhighlightCandy(candy) {
if (!candy) return;
tween(candy, {
scaleX: 1,
scaleY: 1
}, {
duration: 100,
easing: tween.cubicIn
});
}
// Touch/drag logic
var dragStartCell = null;
var dragStartCandy = null;
var dragActive = false;
game.down = function (x, y, obj) {
if (isAnimating) return;
var cell = posToCell(x, y);
if (!cell) return;
var candy = board[cell.row][cell.col];
if (!candy || candy.isMoving) return;
dragStartCell = cell;
dragStartCandy = candy;
dragActive = true;
highlightCandy(candy);
};
game.move = function (x, y, obj) {
if (!dragActive || isAnimating) return;
var cell = posToCell(x, y);
if (!cell) return;
if (!dragStartCell) return;
if (cell.row === dragStartCell.row && cell.col === dragStartCell.col) return;
if (!areAdjacent(cell.row, cell.col, dragStartCell.row, dragStartCell.col)) return;
var targetCandy = board[cell.row][cell.col];
if (!targetCandy || targetCandy.isMoving) return;
dragActive = false;
unhighlightCandy(dragStartCandy);
isSwapping = true;
movesLeft--;
movesTxt.setText('Moves: ' + movesLeft);
// Save original positions for rollback
var origA = {
row: dragStartCell.row,
col: dragStartCell.col
};
var origB = {
row: cell.row,
col: cell.col
};
swapCandies(origA.row, origA.col, origB.row, origB.col, true, function () {
// After swap, check for matches
var matches = findMatches();
if (matches.length === 0) {
// No match: swap back with animation
swapCandies(origB.row, origB.col, origA.row, origA.col, true, function () {
isSwapping = false;
if (movesLeft <= 0) {
LK.showGameOver();
}
});
} else {
// Match: process board
processBoard(function () {
isSwapping = false;
if (movesLeft <= 0) {
LK.showGameOver();
}
});
}
});
};
game.up = function (x, y, obj) {
if (dragActive) {
unhighlightCandy(dragStartCandy);
}
dragActive = false;
dragStartCell = null;
dragStartCandy = null;
};
// --- Game update loop ---
game.update = function () {
// No per-frame logic needed; all handled by events and tweens
};
// --- End of file --- /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Candy class
var Candy = Container.expand(function () {
var self = Container.call(this);
// Color index: 0-4
self.colorIndex = 0;
self.row = 0;
self.col = 0;
self.isMoving = false; // Used for animation
// Attach asset
self.setColor = function (idx) {
self.colorIndex = idx;
if (self.candyAsset) {
self.removeChild(self.candyAsset);
}
var colorIds = ['candyRed', 'candyGreen', 'candyBlue', 'candyYellow', 'candyPurple'];
self.candyAsset = self.attachAsset(colorIds[idx], {
anchorX: 0.5,
anchorY: 0.5
});
};
// Animate to (x, y)
self.animateTo = function (x, y, _onFinish) {
self.isMoving = true;
tween(self, {
x: x,
y: y
}, {
duration: 180,
easing: tween.cubicInOut,
onFinish: function onFinish() {
self.isMoving = false;
if (_onFinish) _onFinish();
}
});
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x222244
});
/****
* Game Code
****/
// --- Game constants ---
// 5 candy colors as ellipses
// Simple pop sound for matches
var BOARD_COLS = 7;
var BOARD_ROWS = 9;
var CANDY_SIZE = 200; // px, including margin
var CANDY_MARGIN = 10;
var COLORS = 5;
var MOVES_LIMIT = 30;
// --- Game state ---
var board = []; // 2D array [row][col] of Candy
var selectedCandy = null;
var swappingCandy = null;
var isSwapping = false;
var isAnimating = false;
var movesLeft = MOVES_LIMIT;
var score = 0;
// --- UI elements ---
var scoreTxt = new Text2('Score: 0', {
size: 90,
fill: "#fff"
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
var movesTxt = new Text2('Moves: ' + MOVES_LIMIT, {
size: 70,
fill: "#fff"
});
movesTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(movesTxt);
movesTxt.y = 110;
// --- Board positioning ---
var boardWidth = BOARD_COLS * CANDY_SIZE;
var boardHeight = BOARD_ROWS * CANDY_SIZE;
var boardOffsetX = Math.floor((2048 - boardWidth) / 2) + CANDY_SIZE / 2;
var boardOffsetY = Math.floor((2732 - boardHeight) / 2) + CANDY_SIZE / 2;
// --- Helper functions ---
function getCandyPos(row, col) {
return {
x: boardOffsetX + col * CANDY_SIZE,
y: boardOffsetY + row * CANDY_SIZE
};
}
function randomColor() {
return Math.floor(Math.random() * COLORS);
}
// Returns true if (r1,c1) and (r2,c2) are adjacent
function areAdjacent(r1, c1, r2, c2) {
return Math.abs(r1 - r2) + Math.abs(c1 - c2) === 1;
}
// Returns array of {row, col} for all matches (3+ in row or col)
function findMatches() {
var matches = [];
// Horizontal
for (var r = 0; r < BOARD_ROWS; r++) {
var run = 1;
for (var c = 1; c < BOARD_COLS; c++) {
var prev = board[r][c - 1];
var curr = board[r][c];
if (prev && curr && prev.colorIndex === curr.colorIndex) {
run++;
} else {
if (run >= 3) {
for (var k = 0; k < run; k++) {
matches.push({
row: r,
col: c - 1 - k
});
}
}
run = 1;
}
}
if (run >= 3) {
for (var k = 0; k < run; k++) {
matches.push({
row: r,
col: BOARD_COLS - 1 - k
});
}
}
}
// Vertical
for (var c = 0; c < BOARD_COLS; c++) {
var run = 1;
for (var r = 1; r < BOARD_ROWS; r++) {
var prev = board[r - 1][c];
var curr = board[r][c];
if (prev && curr && prev.colorIndex === curr.colorIndex) {
run++;
} else {
if (run >= 3) {
for (var k = 0; k < run; k++) {
matches.push({
row: r - 1 - k,
col: c
});
}
}
run = 1;
}
}
if (run >= 3) {
for (var k = 0; k < run; k++) {
matches.push({
row: BOARD_ROWS - 1 - k,
col: c
});
}
}
}
// Remove duplicates
var seen = {};
var unique = [];
for (var i = 0; i < matches.length; i++) {
var key = matches[i].row + ',' + matches[i].col;
if (!seen[key]) {
unique.push(matches[i]);
seen[key] = true;
}
}
return unique;
}
// Remove candies at given positions, return number removed
function removeMatches(matches) {
for (var i = 0; i < matches.length; i++) {
var r = matches[i].row,
c = matches[i].col;
var candy = board[r][c];
if (candy) {
(function (candy) {
tween(candy, {
alpha: 0,
scaleX: 1.3,
scaleY: 1.3
}, {
duration: 180,
easing: tween.cubicIn,
onFinish: function onFinish() {
if (candy.parent) candy.parent.removeChild(candy);
}
});
})(candy);
board[r][c] = null;
}
}
if (matches.length > 0) {
LK.getSound('pop').play();
}
return matches.length;
}
// Drop candies down to fill empty spaces, return true if any moved
function dropCandies(onFinish) {
var anyMoved = false;
var animCount = 0;
for (var c = 0; c < BOARD_COLS; c++) {
for (var r = BOARD_ROWS - 1; r >= 0; r--) {
if (!board[r][c]) {
// Find first non-null above
for (var rr = r - 1; rr >= 0; rr--) {
if (board[rr][c]) {
// Move candy down
var candy = board[rr][c];
board[r][c] = candy;
board[rr][c] = null;
candy.row = r;
candy.col = c;
var pos = getCandyPos(r, c);
animCount++;
candy.animateTo(pos.x, pos.y, function () {
animCount--;
if (animCount === 0 && onFinish) onFinish();
});
anyMoved = true;
break;
}
}
}
}
}
// If no candies moved, call onFinish immediately
if (!anyMoved && onFinish) {
onFinish();
}
return anyMoved;
}
// Fill empty spaces at top with new candies, return true if any added
function fillCandies(onFinish) {
var anyAdded = false;
var animCount = 0;
for (var c = 0; c < BOARD_COLS; c++) {
for (var r = 0; r < BOARD_ROWS; r++) {
if (!board[r][c]) {
var candy = new Candy();
var color = randomColor();
candy.setColor(color);
candy.row = r;
candy.col = c;
var pos = getCandyPos(r, c);
// Start above board
var startY = boardOffsetY - CANDY_SIZE;
candy.x = pos.x;
candy.y = startY;
candy.alpha = 0.7;
candy.scaleX = 0.7;
candy.scaleY = 0.7;
game.addChild(candy);
board[r][c] = candy;
animCount++;
(function (candy, pos) {
candy.animateTo(pos.x, pos.y, function () {
tween(candy, {
alpha: 1,
scaleX: 1,
scaleY: 1
}, {
duration: 100
});
animCount--;
if (animCount === 0 && onFinish) onFinish();
});
})(candy, pos);
anyAdded = true;
}
}
}
if (!anyAdded && onFinish) {
onFinish();
}
return anyAdded;
}
// After a move, process all matches, drops, fills, recursively
function processBoard(afterAll) {
isAnimating = true;
var matches = findMatches();
if (matches.length === 0) {
isAnimating = false;
if (afterAll) afterAll();
return;
}
score += matches.length * 10;
scoreTxt.setText('Score: ' + score);
removeMatches(matches);
LK.setTimeout(function () {
dropCandies(function () {
fillCandies(function () {
LK.setTimeout(function () {
processBoard(afterAll);
}, 80);
});
});
}, 200);
}
// Swap two candies (row1,col1) <-> (row2,col2)
function swapCandies(r1, c1, r2, c2, animate, onFinish) {
var candyA = board[r1][c1];
var candyB = board[r2][c2];
if (!candyA || !candyB) return;
// Swap in board
board[r1][c1] = candyB;
board[r2][c2] = candyA;
// Update row/col
candyA.row = r2;
candyA.col = c2;
candyB.row = r1;
candyB.col = c1;
// Animate
if (animate) {
var posA = getCandyPos(r2, c2);
var posB = getCandyPos(r1, c1);
var doneA = false,
doneB = false;
candyA.animateTo(posA.x, posA.y, function () {
doneA = true;
if (doneB && onFinish) onFinish();
});
candyB.animateTo(posB.x, posB.y, function () {
doneB = true;
if (doneA && onFinish) onFinish();
});
} else {
var posA = getCandyPos(r2, c2);
var posB = getCandyPos(r1, c1);
candyA.x = posA.x;
candyA.y = posA.y;
candyB.x = posB.x;
candyB.y = posB.y;
if (onFinish) onFinish();
}
}
// --- Board setup ---
function noInitialMatches(r, c, colorIdx) {
// Check left 2
if (c >= 2 && board[r][c - 1] && board[r][c - 2]) {
if (board[r][c - 1].colorIndex === colorIdx && board[r][c - 2].colorIndex === colorIdx) {
return false;
}
}
// Check up 2
if (r >= 2 && board[r - 1][c] && board[r - 2][c]) {
if (board[r - 1][c].colorIndex === colorIdx && board[r - 2][c].colorIndex === colorIdx) {
return false;
}
}
return true;
}
// Initialize board
for (var r = 0; r < BOARD_ROWS; r++) {
board[r] = [];
for (var c = 0; c < BOARD_COLS; c++) {
var candy = new Candy();
var color = randomColor();
// Avoid initial matches
while (!noInitialMatches(r, c, color)) {
color = randomColor();
}
candy.setColor(color);
candy.row = r;
candy.col = c;
var pos = getCandyPos(r, c);
candy.x = pos.x;
candy.y = pos.y;
game.addChild(candy);
board[r][c] = candy;
}
}
// --- Input handling ---
// Convert (x, y) to (row, col) on board, or null if outside
function posToCell(x, y) {
var col = Math.floor((x - boardOffsetX + CANDY_SIZE / 2) / CANDY_SIZE);
var row = Math.floor((y - boardOffsetY + CANDY_SIZE / 2) / CANDY_SIZE);
if (row < 0 || row >= BOARD_ROWS || col < 0 || col >= BOARD_COLS) return null;
return {
row: row,
col: col
};
}
// Highlight selected candy (simple scale up)
function highlightCandy(candy) {
if (!candy) return;
tween(candy, {
scaleX: 1.18,
scaleY: 1.18
}, {
duration: 100,
easing: tween.cubicOut
});
}
function unhighlightCandy(candy) {
if (!candy) return;
tween(candy, {
scaleX: 1,
scaleY: 1
}, {
duration: 100,
easing: tween.cubicIn
});
}
// Touch/drag logic
var dragStartCell = null;
var dragStartCandy = null;
var dragActive = false;
game.down = function (x, y, obj) {
if (isAnimating) return;
var cell = posToCell(x, y);
if (!cell) return;
var candy = board[cell.row][cell.col];
if (!candy || candy.isMoving) return;
dragStartCell = cell;
dragStartCandy = candy;
dragActive = true;
highlightCandy(candy);
};
game.move = function (x, y, obj) {
if (!dragActive || isAnimating) return;
var cell = posToCell(x, y);
if (!cell) return;
if (!dragStartCell) return;
if (cell.row === dragStartCell.row && cell.col === dragStartCell.col) return;
if (!areAdjacent(cell.row, cell.col, dragStartCell.row, dragStartCell.col)) return;
var targetCandy = board[cell.row][cell.col];
if (!targetCandy || targetCandy.isMoving) return;
dragActive = false;
unhighlightCandy(dragStartCandy);
isSwapping = true;
movesLeft--;
movesTxt.setText('Moves: ' + movesLeft);
// Save original positions for rollback
var origA = {
row: dragStartCell.row,
col: dragStartCell.col
};
var origB = {
row: cell.row,
col: cell.col
};
swapCandies(origA.row, origA.col, origB.row, origB.col, true, function () {
// After swap, check for matches
var matches = findMatches();
if (matches.length === 0) {
// No match: swap back with animation
swapCandies(origB.row, origB.col, origA.row, origA.col, true, function () {
isSwapping = false;
if (movesLeft <= 0) {
LK.showGameOver();
}
});
} else {
// Match: process board
processBoard(function () {
isSwapping = false;
if (movesLeft <= 0) {
LK.showGameOver();
}
});
}
});
};
game.up = function (x, y, obj) {
if (dragActive) {
unhighlightCandy(dragStartCandy);
}
dragActive = false;
dragStartCell = null;
dragStartCandy = null;
};
// --- Game update loop ---
game.update = function () {
// No per-frame logic needed; all handled by events and tweens
};
// --- End of file ---