/**** * Classes ****/ // Candy class var Candy = Container.expand(function () { var self = Container.call(this); // Properties self.type = null; // 'red', 'yellow', etc. self.special = null; // null, 'striped', 'bomb' self.row = 0; self.col = 0; self.isMoving = false; // Asset var asset = null; // Set candy type and special self.setType = function (type, special) { self.type = type; self.special = special || null; if (asset) { asset.destroy(); } var assetId; if (special === 'bomb') { assetId = 'candy_bomb'; } else { assetId = 'candy_' + (special ? special : type); } asset = self.attachAsset(assetId, { anchorX: 0.5, anchorY: 0.5 }); }; // Animate to (x, y) self.moveTo = function (x, y, cb) { self.isMoving = true; self.x = x; self.y = y; self.isMoving = false; if (cb) { cb(); } }; // Pop animation self.pop = function (cb) { self.destroy(); if (cb) { cb(); } }; // Reset visual self.resetVisual = function () { self.scaleX = 1; self.scaleY = 1; self.alpha = 1; }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ // Set background to solid gray backgroundColor: 0x888888 }); /**** * Game Code ****/ // --- Constants --- // 6 candy types, each a colored ellipse // Special candies // Simple sound for match var GRID_SIZE = 8; var CELL_SIZE = 220; // px, fits 8x8 in 2048x2048 var GRID_OFFSET_X = Math.floor((2048 - GRID_SIZE * CELL_SIZE) / 2); var GRID_OFFSET_Y = 320; // leave space for GUI at top var CANDY_TYPES = ['red', 'yellow', 'green', 'blue', 'orange', 'purple']; var SPECIALS = ['striped', 'bomb']; // --- State --- var grid = []; // 2D array [row][col] of Candy or null var selectedCandy = null; var swappingCandy = null; var isAnimating = false; var movesLeft = 40; var targetScore = 20000; var score = 0; var comboMultiplier = 1; var isProcessing = false; var currentLevel = 1; // --- GUI --- var scoreTxt = new Text2('Score: 0', { size: 90, fill: "#fff" }); scoreTxt.anchor.set(1, 0); // Move scoreTxt to the right side of the top GUI bar scoreTxt.x = LK.gui.top.width - 120; LK.gui.top.addChild(scoreTxt); var movesTxt = new Text2('Moves: 550', { size: 90, fill: "#fff" }); movesTxt.anchor.set(1, 0); LK.gui.topRight.addChild(movesTxt); var targetTxt = new Text2('Target: 20000', { size: 70, fill: "#fff" }); targetTxt.anchor.set(0.5, 0); LK.gui.top.addChild(targetTxt); targetTxt.y = 100; var levelTxt = new Text2('Level: 1', { size: 70, fill: "#fff" }); levelTxt.anchor.set(0.5, 0); LK.gui.top.addChild(levelTxt); levelTxt.y = 200; // --- Helper Functions --- function getCandyPos(row, col) { return { x: GRID_OFFSET_X + col * CELL_SIZE + CELL_SIZE / 2, y: GRID_OFFSET_Y + row * CELL_SIZE + CELL_SIZE / 2 }; } function randomCandyType() { return CANDY_TYPES[Math.floor(Math.random() * CANDY_TYPES.length)]; } // Returns true if (r1,c1) and (r2,c2) are adjacent function isAdjacent(r1, c1, r2, c2) { return Math.abs(r1 - r2) + Math.abs(c1 - c2) === 1; } // --- Board Setup --- function createBoard() { // Remove previous grid lines if any if (typeof gridLines !== "undefined" && gridLines) { gridLines.destroy(); gridLines = null; } // Draw grid lines (black) for ızgıra görünümü gridLines = new Container(); var lineWidth = 12; // px, thick black lines var gridW = GRID_SIZE * CELL_SIZE; var gridH = GRID_SIZE * CELL_SIZE; // Draw vertical lines for (var c = 0; c <= GRID_SIZE; c++) { var x = GRID_OFFSET_X + c * CELL_SIZE; var line = LK.getAsset('candy_bomb', { width: lineWidth, height: gridH + lineWidth, color: 0x000000, anchorX: 0.5, anchorY: 0 }); line.x = x; line.y = GRID_OFFSET_Y - lineWidth / 2; gridLines.addChild(line); } // Draw horizontal lines for (var r = 0; r <= GRID_SIZE; r++) { var y = GRID_OFFSET_Y + r * CELL_SIZE; var line = LK.getAsset('candy_bomb', { width: gridW + lineWidth, height: lineWidth, color: 0x000000, anchorX: 0, anchorY: 0.5 }); line.x = GRID_OFFSET_X - lineWidth / 2; line.y = y; gridLines.addChild(line); } game.addChild(gridLines); // Clear previous for (var r = 0; r < GRID_SIZE; r++) { grid[r] = []; for (var c = 0; c < GRID_SIZE; c++) { var candy = new Candy(); var type = randomCandyType(); candy.setType(type); candy.row = r; candy.col = c; // Place each candy in a grid layout var pos = getCandyPos(r, c); candy.x = pos.x; candy.y = pos.y; candy.resetVisual(); grid[r][c] = candy; game.addChild(candy); } } // Remove any initial matches removeInitialMatches(); } function removeInitialMatches() { var changed = false; for (var r = 0; r < GRID_SIZE; r++) { for (var c = 0; c < GRID_SIZE; c++) { var type = grid[r][c].type; // Check left 2 if (c >= 2 && grid[r][c - 1].type === type && grid[r][c - 2].type === type) { do { type = randomCandyType(); } while (type === grid[r][c - 1].type || type === grid[r][c - 2].type); grid[r][c].setType(type); changed = true; } // Check up 2 if (r >= 2 && grid[r - 1][c].type === type && grid[r - 2][c].type === type) { do { type = randomCandyType(); } while (type === grid[r - 1][c].type || type === grid[r - 2][c].type); grid[r][c].setType(type); changed = true; } } } if (changed) { removeInitialMatches(); } } // --- Input Handling --- // Convert (x,y) to (row,col) or null if outside grid function posToCell(x, y) { var col = Math.floor((x - GRID_OFFSET_X) / CELL_SIZE); var row = Math.floor((y - GRID_OFFSET_Y) / CELL_SIZE); if (row < 0 || row >= GRID_SIZE || col < 0 || col >= GRID_SIZE) { return null; } return { row: row, col: col }; } // Highlight selected candy function highlightCandy(candy) { if (!candy) { return; } candy.scaleX = 1.2; candy.scaleY = 1.2; } function unhighlightCandy(candy) { if (!candy) { return; } candy.scaleX = 1; candy.scaleY = 1; } // Touch/drag logic var dragStartCell = null; var dragCurrentCell = null; game.down = function (x, y, obj) { if (isAnimating || isProcessing || movesLeft <= 0) { return; } var cell = posToCell(x, y); if (!cell) { return; } var candy = grid[cell.row][cell.col]; if (!candy) { return; } selectedCandy = candy; dragStartCell = cell; highlightCandy(candy); }; game.move = function (x, y, obj) { if (!selectedCandy || isAnimating || isProcessing) { return; } var cell = posToCell(x, y); if (!cell) { return; } dragCurrentCell = cell; // If drag to adjacent cell, swap if (isAdjacent(dragStartCell.row, dragStartCell.col, cell.row, cell.col)) { var targetCandy = grid[cell.row][cell.col]; if (!targetCandy) { return; } swapCandies(selectedCandy, targetCandy, true); unhighlightCandy(selectedCandy); selectedCandy = null; dragStartCell = null; dragCurrentCell = null; } }; game.up = function (x, y, obj) { if (selectedCandy) { unhighlightCandy(selectedCandy); selectedCandy = null; dragStartCell = null; dragCurrentCell = null; } }; // --- Swap and Match Logic --- function swapCandies(c1, c2, isPlayerMove) { if (isAnimating || isProcessing) { return; } isAnimating = true; // Swap in grid var r1 = c1.row, c1c = c1.col, r2 = c2.row, c2c = c2.col; grid[r1][c1c] = c2; grid[r2][c2c] = c1; // Swap row/col var tmpRow = c1.row, tmpCol = c1.col; c1.row = c2.row; c1.col = c2.col; c2.row = tmpRow; c2.col = tmpCol; // Animate var pos1 = getCandyPos(c1.row, c1.col); var pos2 = getCandyPos(c2.row, c2.col); var done = 0; c1.moveTo(pos1.x, pos1.y, function () { done++; if (done === 2) { afterSwap(); } }); c2.moveTo(pos2.x, pos2.y, function () { done++; if (done === 2) { afterSwap(); } }); function afterSwap() { // Check for matches var matches = findAllMatches(); if (matches.length > 0) { if (isPlayerMove) { movesLeft--; updateGUI(); } processMatches(matches); } else { // No match: swap back if player move if (isPlayerMove) { var finish = function finish() { isAnimating = false; }; // Swap back grid[r1][c1c] = c1; grid[r2][c2c] = c2; c1.row = r1; c1.col = c1c; c2.row = r2; c2.col = c2c; var pos1b = getCandyPos(c1.row, c1.col); var pos2b = getCandyPos(c2.row, c2.col); var done2 = 0; c1.moveTo(pos1b.x, pos1b.y, function () { done2++; if (done2 === 2) { finish(); } }); c2.moveTo(pos2b.x, pos2b.y, function () { done2++; if (done2 === 2) { finish(); } }); } else { isAnimating = false; } } } } // Find all matches on board. Returns array of arrays of candies. function findAllMatches() { var matches = []; var visited = []; for (var r = 0; r < GRID_SIZE; r++) { visited[r] = []; for (var c = 0; c < GRID_SIZE; c++) { visited[r][c] = false; } } // Horizontal for (var r = 0; r < GRID_SIZE; r++) { var count = 1; for (var c = 1; c < GRID_SIZE; c++) { var prev = grid[r][c - 1], curr = grid[r][c]; if (curr && prev && curr.type === prev.type && !curr.special && !prev.special) { count++; } else { if (count >= 3) { var arr = []; for (var k = 0; k < count; k++) { if (!visited[r][c - 1 - k]) { arr.push(grid[r][c - 1 - k]); visited[r][c - 1 - k] = true; } } if (arr.length) { matches.push(arr); } } count = 1; } } if (count >= 3) { var arr = []; for (var k = 0; k < count; k++) { if (!visited[r][GRID_SIZE - 1 - k]) { arr.push(grid[r][GRID_SIZE - 1 - k]); visited[r][GRID_SIZE - 1 - k] = true; } } if (arr.length) { matches.push(arr); } } } // Vertical for (var c = 0; c < GRID_SIZE; c++) { var count = 1; for (var r = 1; r < GRID_SIZE; r++) { var prev = grid[r - 1][c], curr = grid[r][c]; if (curr && prev && curr.type === prev.type && !curr.special && !prev.special) { count++; } else { if (count >= 3) { var arr = []; for (var k = 0; k < count; k++) { if (!visited[r - 1 - k][c]) { arr.push(grid[r - 1 - k][c]); visited[r - 1 - k][c] = true; } } if (arr.length) { matches.push(arr); } } count = 1; } } if (count >= 3) { var arr = []; for (var k = 0; k < count; k++) { if (!visited[GRID_SIZE - 1 - k][c]) { arr.push(grid[GRID_SIZE - 1 - k][c]); visited[GRID_SIZE - 1 - k][c] = true; } } if (arr.length) { matches.push(arr); } } } return matches; } // --- Match Processing --- function processMatches(matches) { isProcessing = true; // Play sound LK.getSound('match').play(); // Score var points = 0; for (var i = 0; i < matches.length; i++) { points += matches[i].length * 100 * comboMultiplier; } score += points; updateGUI(); // Special candy creation (only for 4+ matches) for (var i = 0; i < matches.length; i++) { if (matches[i].length === 4) { // Make one of them striped var c = matches[i][Math.floor(Math.random() * 4)]; c.setType(c.type, 'striped'); } // No bomb creation for 5+ matches } // Remove matched candies (except special ones just created) var toRemove = []; for (var i = 0; i < matches.length; i++) { for (var j = 0; j < matches[i].length; j++) { var c = matches[i][j]; if (!c.special) { toRemove.push(c); } } } // Remove from grid for (var i = 0; i < toRemove.length; i++) { var c = toRemove[i]; grid[c.row][c.col] = null; c.pop(); } // After animation, drop candies and refill LK.setTimeout(function () { dropCandies(function () { refillCandies(function () { // Check for new matches (cascade) var newMatches = findAllMatches(); if (newMatches.length > 0) { comboMultiplier++; processMatches(newMatches); } else { comboMultiplier = 1; isProcessing = false; isAnimating = false; checkEndConditions(); } }); }); }, 220); } // Drop candies to fill empty spaces function dropCandies(cb) { var moved = false; for (var c = 0; c < GRID_SIZE; c++) { for (var r = GRID_SIZE - 1; r >= 0; r--) { if (!grid[r][c]) { // Find first non-null above for (var k = r - 1; k >= 0; k--) { if (grid[k][c]) { // Move down var candy = grid[k][c]; grid[r][c] = candy; grid[k][c] = null; candy.row = r; candy.col = c; var pos = getCandyPos(r, c); candy.moveTo(pos.x, pos.y); moved = true; break; } } } } } // Wait for animation LK.setTimeout(function () { if (moved) { dropCandies(cb); } else { cb(); } }, 200); } // Fill empty spaces at top with new candies function refillCandies(cb) { var created = false; for (var c = 0; c < GRID_SIZE; c++) { for (var r = 0; r < GRID_SIZE; r++) { if (!grid[r][c]) { var candy = new Candy(); var type = randomCandyType(); candy.setType(type); candy.row = r; candy.col = c; var pos = getCandyPos(r, c); // Start above board candy.x = pos.x; candy.y = pos.y - 400; candy.resetVisual(); grid[r][c] = candy; game.addChild(candy); candy.moveTo(pos.x, pos.y); created = true; } } } LK.setTimeout(cb, created ? 200 : 0); } // --- GUI Update --- function updateGUI() { scoreTxt.setText('Score: ' + score); movesTxt.setText('Moves: ' + movesLeft); targetTxt.setText('Target: ' + targetScore); if (typeof levelTxt !== "undefined") { levelTxt.setText('Level: ' + currentLevel); } } // --- End Conditions --- function checkEndConditions() { if (score >= targetScore) { // Advance to next level currentLevel++; score = 0; movesLeft = 40; targetScore = 20000; comboMultiplier = 1; updateGUI(); // Remove all candies from board for (var r = 0; r < GRID_SIZE; r++) { for (var c = 0; c < GRID_SIZE; c++) { if (grid[r][c]) { grid[r][c].destroy(); grid[r][c] = null; } } } createBoard(); } else if (movesLeft <= 0) { LK.showGameOver(); } } // --- Game Start --- function startGame() { currentLevel = 1; score = 0; movesLeft = 40; targetScore = 20000; comboMultiplier = 1; updateGUI(); createBoard(); } startGame(); // Play calm background music (loops by default) LK.playMusic('calm_bg_music'); // --- Game Tick --- game.update = function () { // No per-frame logic needed for MVP };
/****
* Classes
****/
// Candy class
var Candy = Container.expand(function () {
var self = Container.call(this);
// Properties
self.type = null; // 'red', 'yellow', etc.
self.special = null; // null, 'striped', 'bomb'
self.row = 0;
self.col = 0;
self.isMoving = false;
// Asset
var asset = null;
// Set candy type and special
self.setType = function (type, special) {
self.type = type;
self.special = special || null;
if (asset) {
asset.destroy();
}
var assetId;
if (special === 'bomb') {
assetId = 'candy_bomb';
} else {
assetId = 'candy_' + (special ? special : type);
}
asset = self.attachAsset(assetId, {
anchorX: 0.5,
anchorY: 0.5
});
};
// Animate to (x, y)
self.moveTo = function (x, y, cb) {
self.isMoving = true;
self.x = x;
self.y = y;
self.isMoving = false;
if (cb) {
cb();
}
};
// Pop animation
self.pop = function (cb) {
self.destroy();
if (cb) {
cb();
}
};
// Reset visual
self.resetVisual = function () {
self.scaleX = 1;
self.scaleY = 1;
self.alpha = 1;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
// Set background to solid gray
backgroundColor: 0x888888
});
/****
* Game Code
****/
// --- Constants ---
// 6 candy types, each a colored ellipse
// Special candies
// Simple sound for match
var GRID_SIZE = 8;
var CELL_SIZE = 220; // px, fits 8x8 in 2048x2048
var GRID_OFFSET_X = Math.floor((2048 - GRID_SIZE * CELL_SIZE) / 2);
var GRID_OFFSET_Y = 320; // leave space for GUI at top
var CANDY_TYPES = ['red', 'yellow', 'green', 'blue', 'orange', 'purple'];
var SPECIALS = ['striped', 'bomb'];
// --- State ---
var grid = []; // 2D array [row][col] of Candy or null
var selectedCandy = null;
var swappingCandy = null;
var isAnimating = false;
var movesLeft = 40;
var targetScore = 20000;
var score = 0;
var comboMultiplier = 1;
var isProcessing = false;
var currentLevel = 1;
// --- GUI ---
var scoreTxt = new Text2('Score: 0', {
size: 90,
fill: "#fff"
});
scoreTxt.anchor.set(1, 0);
// Move scoreTxt to the right side of the top GUI bar
scoreTxt.x = LK.gui.top.width - 120;
LK.gui.top.addChild(scoreTxt);
var movesTxt = new Text2('Moves: 550', {
size: 90,
fill: "#fff"
});
movesTxt.anchor.set(1, 0);
LK.gui.topRight.addChild(movesTxt);
var targetTxt = new Text2('Target: 20000', {
size: 70,
fill: "#fff"
});
targetTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(targetTxt);
targetTxt.y = 100;
var levelTxt = new Text2('Level: 1', {
size: 70,
fill: "#fff"
});
levelTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(levelTxt);
levelTxt.y = 200;
// --- Helper Functions ---
function getCandyPos(row, col) {
return {
x: GRID_OFFSET_X + col * CELL_SIZE + CELL_SIZE / 2,
y: GRID_OFFSET_Y + row * CELL_SIZE + CELL_SIZE / 2
};
}
function randomCandyType() {
return CANDY_TYPES[Math.floor(Math.random() * CANDY_TYPES.length)];
}
// Returns true if (r1,c1) and (r2,c2) are adjacent
function isAdjacent(r1, c1, r2, c2) {
return Math.abs(r1 - r2) + Math.abs(c1 - c2) === 1;
}
// --- Board Setup ---
function createBoard() {
// Remove previous grid lines if any
if (typeof gridLines !== "undefined" && gridLines) {
gridLines.destroy();
gridLines = null;
}
// Draw grid lines (black) for ızgıra görünümü
gridLines = new Container();
var lineWidth = 12; // px, thick black lines
var gridW = GRID_SIZE * CELL_SIZE;
var gridH = GRID_SIZE * CELL_SIZE;
// Draw vertical lines
for (var c = 0; c <= GRID_SIZE; c++) {
var x = GRID_OFFSET_X + c * CELL_SIZE;
var line = LK.getAsset('candy_bomb', {
width: lineWidth,
height: gridH + lineWidth,
color: 0x000000,
anchorX: 0.5,
anchorY: 0
});
line.x = x;
line.y = GRID_OFFSET_Y - lineWidth / 2;
gridLines.addChild(line);
}
// Draw horizontal lines
for (var r = 0; r <= GRID_SIZE; r++) {
var y = GRID_OFFSET_Y + r * CELL_SIZE;
var line = LK.getAsset('candy_bomb', {
width: gridW + lineWidth,
height: lineWidth,
color: 0x000000,
anchorX: 0,
anchorY: 0.5
});
line.x = GRID_OFFSET_X - lineWidth / 2;
line.y = y;
gridLines.addChild(line);
}
game.addChild(gridLines);
// Clear previous
for (var r = 0; r < GRID_SIZE; r++) {
grid[r] = [];
for (var c = 0; c < GRID_SIZE; c++) {
var candy = new Candy();
var type = randomCandyType();
candy.setType(type);
candy.row = r;
candy.col = c;
// Place each candy in a grid layout
var pos = getCandyPos(r, c);
candy.x = pos.x;
candy.y = pos.y;
candy.resetVisual();
grid[r][c] = candy;
game.addChild(candy);
}
}
// Remove any initial matches
removeInitialMatches();
}
function removeInitialMatches() {
var changed = false;
for (var r = 0; r < GRID_SIZE; r++) {
for (var c = 0; c < GRID_SIZE; c++) {
var type = grid[r][c].type;
// Check left 2
if (c >= 2 && grid[r][c - 1].type === type && grid[r][c - 2].type === type) {
do {
type = randomCandyType();
} while (type === grid[r][c - 1].type || type === grid[r][c - 2].type);
grid[r][c].setType(type);
changed = true;
}
// Check up 2
if (r >= 2 && grid[r - 1][c].type === type && grid[r - 2][c].type === type) {
do {
type = randomCandyType();
} while (type === grid[r - 1][c].type || type === grid[r - 2][c].type);
grid[r][c].setType(type);
changed = true;
}
}
}
if (changed) {
removeInitialMatches();
}
}
// --- Input Handling ---
// Convert (x,y) to (row,col) or null if outside grid
function posToCell(x, y) {
var col = Math.floor((x - GRID_OFFSET_X) / CELL_SIZE);
var row = Math.floor((y - GRID_OFFSET_Y) / CELL_SIZE);
if (row < 0 || row >= GRID_SIZE || col < 0 || col >= GRID_SIZE) {
return null;
}
return {
row: row,
col: col
};
}
// Highlight selected candy
function highlightCandy(candy) {
if (!candy) {
return;
}
candy.scaleX = 1.2;
candy.scaleY = 1.2;
}
function unhighlightCandy(candy) {
if (!candy) {
return;
}
candy.scaleX = 1;
candy.scaleY = 1;
}
// Touch/drag logic
var dragStartCell = null;
var dragCurrentCell = null;
game.down = function (x, y, obj) {
if (isAnimating || isProcessing || movesLeft <= 0) {
return;
}
var cell = posToCell(x, y);
if (!cell) {
return;
}
var candy = grid[cell.row][cell.col];
if (!candy) {
return;
}
selectedCandy = candy;
dragStartCell = cell;
highlightCandy(candy);
};
game.move = function (x, y, obj) {
if (!selectedCandy || isAnimating || isProcessing) {
return;
}
var cell = posToCell(x, y);
if (!cell) {
return;
}
dragCurrentCell = cell;
// If drag to adjacent cell, swap
if (isAdjacent(dragStartCell.row, dragStartCell.col, cell.row, cell.col)) {
var targetCandy = grid[cell.row][cell.col];
if (!targetCandy) {
return;
}
swapCandies(selectedCandy, targetCandy, true);
unhighlightCandy(selectedCandy);
selectedCandy = null;
dragStartCell = null;
dragCurrentCell = null;
}
};
game.up = function (x, y, obj) {
if (selectedCandy) {
unhighlightCandy(selectedCandy);
selectedCandy = null;
dragStartCell = null;
dragCurrentCell = null;
}
};
// --- Swap and Match Logic ---
function swapCandies(c1, c2, isPlayerMove) {
if (isAnimating || isProcessing) {
return;
}
isAnimating = true;
// Swap in grid
var r1 = c1.row,
c1c = c1.col,
r2 = c2.row,
c2c = c2.col;
grid[r1][c1c] = c2;
grid[r2][c2c] = c1;
// Swap row/col
var tmpRow = c1.row,
tmpCol = c1.col;
c1.row = c2.row;
c1.col = c2.col;
c2.row = tmpRow;
c2.col = tmpCol;
// Animate
var pos1 = getCandyPos(c1.row, c1.col);
var pos2 = getCandyPos(c2.row, c2.col);
var done = 0;
c1.moveTo(pos1.x, pos1.y, function () {
done++;
if (done === 2) {
afterSwap();
}
});
c2.moveTo(pos2.x, pos2.y, function () {
done++;
if (done === 2) {
afterSwap();
}
});
function afterSwap() {
// Check for matches
var matches = findAllMatches();
if (matches.length > 0) {
if (isPlayerMove) {
movesLeft--;
updateGUI();
}
processMatches(matches);
} else {
// No match: swap back if player move
if (isPlayerMove) {
var finish = function finish() {
isAnimating = false;
};
// Swap back
grid[r1][c1c] = c1;
grid[r2][c2c] = c2;
c1.row = r1;
c1.col = c1c;
c2.row = r2;
c2.col = c2c;
var pos1b = getCandyPos(c1.row, c1.col);
var pos2b = getCandyPos(c2.row, c2.col);
var done2 = 0;
c1.moveTo(pos1b.x, pos1b.y, function () {
done2++;
if (done2 === 2) {
finish();
}
});
c2.moveTo(pos2b.x, pos2b.y, function () {
done2++;
if (done2 === 2) {
finish();
}
});
} else {
isAnimating = false;
}
}
}
}
// Find all matches on board. Returns array of arrays of candies.
function findAllMatches() {
var matches = [];
var visited = [];
for (var r = 0; r < GRID_SIZE; r++) {
visited[r] = [];
for (var c = 0; c < GRID_SIZE; c++) {
visited[r][c] = false;
}
}
// Horizontal
for (var r = 0; r < GRID_SIZE; r++) {
var count = 1;
for (var c = 1; c < GRID_SIZE; c++) {
var prev = grid[r][c - 1],
curr = grid[r][c];
if (curr && prev && curr.type === prev.type && !curr.special && !prev.special) {
count++;
} else {
if (count >= 3) {
var arr = [];
for (var k = 0; k < count; k++) {
if (!visited[r][c - 1 - k]) {
arr.push(grid[r][c - 1 - k]);
visited[r][c - 1 - k] = true;
}
}
if (arr.length) {
matches.push(arr);
}
}
count = 1;
}
}
if (count >= 3) {
var arr = [];
for (var k = 0; k < count; k++) {
if (!visited[r][GRID_SIZE - 1 - k]) {
arr.push(grid[r][GRID_SIZE - 1 - k]);
visited[r][GRID_SIZE - 1 - k] = true;
}
}
if (arr.length) {
matches.push(arr);
}
}
}
// Vertical
for (var c = 0; c < GRID_SIZE; c++) {
var count = 1;
for (var r = 1; r < GRID_SIZE; r++) {
var prev = grid[r - 1][c],
curr = grid[r][c];
if (curr && prev && curr.type === prev.type && !curr.special && !prev.special) {
count++;
} else {
if (count >= 3) {
var arr = [];
for (var k = 0; k < count; k++) {
if (!visited[r - 1 - k][c]) {
arr.push(grid[r - 1 - k][c]);
visited[r - 1 - k][c] = true;
}
}
if (arr.length) {
matches.push(arr);
}
}
count = 1;
}
}
if (count >= 3) {
var arr = [];
for (var k = 0; k < count; k++) {
if (!visited[GRID_SIZE - 1 - k][c]) {
arr.push(grid[GRID_SIZE - 1 - k][c]);
visited[GRID_SIZE - 1 - k][c] = true;
}
}
if (arr.length) {
matches.push(arr);
}
}
}
return matches;
}
// --- Match Processing ---
function processMatches(matches) {
isProcessing = true;
// Play sound
LK.getSound('match').play();
// Score
var points = 0;
for (var i = 0; i < matches.length; i++) {
points += matches[i].length * 100 * comboMultiplier;
}
score += points;
updateGUI();
// Special candy creation (only for 4+ matches)
for (var i = 0; i < matches.length; i++) {
if (matches[i].length === 4) {
// Make one of them striped
var c = matches[i][Math.floor(Math.random() * 4)];
c.setType(c.type, 'striped');
}
// No bomb creation for 5+ matches
}
// Remove matched candies (except special ones just created)
var toRemove = [];
for (var i = 0; i < matches.length; i++) {
for (var j = 0; j < matches[i].length; j++) {
var c = matches[i][j];
if (!c.special) {
toRemove.push(c);
}
}
}
// Remove from grid
for (var i = 0; i < toRemove.length; i++) {
var c = toRemove[i];
grid[c.row][c.col] = null;
c.pop();
}
// After animation, drop candies and refill
LK.setTimeout(function () {
dropCandies(function () {
refillCandies(function () {
// Check for new matches (cascade)
var newMatches = findAllMatches();
if (newMatches.length > 0) {
comboMultiplier++;
processMatches(newMatches);
} else {
comboMultiplier = 1;
isProcessing = false;
isAnimating = false;
checkEndConditions();
}
});
});
}, 220);
}
// Drop candies to fill empty spaces
function dropCandies(cb) {
var moved = false;
for (var c = 0; c < GRID_SIZE; c++) {
for (var r = GRID_SIZE - 1; r >= 0; r--) {
if (!grid[r][c]) {
// Find first non-null above
for (var k = r - 1; k >= 0; k--) {
if (grid[k][c]) {
// Move down
var candy = grid[k][c];
grid[r][c] = candy;
grid[k][c] = null;
candy.row = r;
candy.col = c;
var pos = getCandyPos(r, c);
candy.moveTo(pos.x, pos.y);
moved = true;
break;
}
}
}
}
}
// Wait for animation
LK.setTimeout(function () {
if (moved) {
dropCandies(cb);
} else {
cb();
}
}, 200);
}
// Fill empty spaces at top with new candies
function refillCandies(cb) {
var created = false;
for (var c = 0; c < GRID_SIZE; c++) {
for (var r = 0; r < GRID_SIZE; r++) {
if (!grid[r][c]) {
var candy = new Candy();
var type = randomCandyType();
candy.setType(type);
candy.row = r;
candy.col = c;
var pos = getCandyPos(r, c);
// Start above board
candy.x = pos.x;
candy.y = pos.y - 400;
candy.resetVisual();
grid[r][c] = candy;
game.addChild(candy);
candy.moveTo(pos.x, pos.y);
created = true;
}
}
}
LK.setTimeout(cb, created ? 200 : 0);
}
// --- GUI Update ---
function updateGUI() {
scoreTxt.setText('Score: ' + score);
movesTxt.setText('Moves: ' + movesLeft);
targetTxt.setText('Target: ' + targetScore);
if (typeof levelTxt !== "undefined") {
levelTxt.setText('Level: ' + currentLevel);
}
}
// --- End Conditions ---
function checkEndConditions() {
if (score >= targetScore) {
// Advance to next level
currentLevel++;
score = 0;
movesLeft = 40;
targetScore = 20000;
comboMultiplier = 1;
updateGUI();
// Remove all candies from board
for (var r = 0; r < GRID_SIZE; r++) {
for (var c = 0; c < GRID_SIZE; c++) {
if (grid[r][c]) {
grid[r][c].destroy();
grid[r][c] = null;
}
}
}
createBoard();
} else if (movesLeft <= 0) {
LK.showGameOver();
}
}
// --- Game Start ---
function startGame() {
currentLevel = 1;
score = 0;
movesLeft = 40;
targetScore = 20000;
comboMultiplier = 1;
updateGUI();
createBoard();
}
startGame();
// Play calm background music (loops by default)
LK.playMusic('calm_bg_music');
// --- Game Tick ---
game.update = function () {
// No per-frame logic needed for MVP
};
kare şeker. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
candy. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
candy. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
candy. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
candy ball. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
candy ball white. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
ball green candy. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
black. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat