User prompt
When you finish part 1, move on to part 2.
User prompt
If the target score is reached in the first section, move on to the second section and have 10 moves and the target score is 2500.
User prompt
Let's give 10 moves for the first section. Let's give the target score as 1800.
User prompt
Please fix the bug: 'TypeError: Cannot read properties of null (reading 'row')' in or related to this line: 'var r1 = c1.row,' Line Number: 152
User prompt
fnt music starts when game starts
User prompt
eliminate the spaces between the candies
User prompt
finish the sugars
User prompt
If you make a wrong move, your right to move will be reduced.
User prompt
assign candies to assets
Code edit (1 edits merged)
Please save this source code
User prompt
Candy Match Mania
Initial prompt
There will be 7 different candies and we will need to bring at least 3 of these candies together and explode them, and we will have a certain number of moves.
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
// Candy class: represents a single candy on the board
var Candy = Container.expand(function () {
var self = Container.call(this);
// Properties
self.type = 0; // 0-6, for 7 types
self.row = 0;
self.col = 0;
self.isMatched = false;
// Attach asset for candy, anchor center
self.candyAsset = null;
// Set candy type and update asset
self.setType = function (type) {
self.type = type;
// Remove previous asset if exists
if (self.candyAsset) {
self.removeChild(self.candyAsset);
}
// Assign each candy type to a unique asset for visual distinction
var assetIds = ['Sg1',
// type 0
'Sg2',
// type 1
'Sg3',
// type 2
'Sg4',
// type 3
'Sg5',
// type 4
'Sg6',
// type 5
'Sg7' // type 6
];
self.candyAsset = self.attachAsset(assetIds[type], {
anchorX: 0.5,
anchorY: 0.5
});
};
// Animate pop (match)
self.pop = function (_onFinish) {
tween(self, {
scaleX: 1.3,
scaleY: 1.3,
alpha: 0
}, {
duration: 250,
easing: tween.easeIn,
onFinish: function onFinish() {
self.alpha = 1;
self.scaleX = 1;
self.scaleY = 1;
if (_onFinish) _onFinish();
}
});
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x222244
});
/****
* Game Code
****/
// --- Game Constants ---
var boardCols = 7;
var boardRows = 9;
var candyTypes = 7;
var candySize = 200; // px, will be scaled to fit
var boardPadding = 30;
var moveLimit = 25;
var targetScore = 3000;
// --- Game State ---
var board = []; // 2D array [row][col] of Candy
var candies = []; // Flat array of all Candy objects
var selectedCandy = null;
var swappingCandy = null;
var isSwapping = false;
var isAnimating = false;
var movesLeft = moveLimit;
var score = 0;
var level = storage.level || 1;
// --- UI Elements ---
var scoreTxt = new Text2('0', {
size: 90,
fill: "#fff"
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
var movesTxt = new Text2('Moves: ' + movesLeft, {
size: 70,
fill: "#fff"
});
movesTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(movesTxt);
movesTxt.y = 110;
var targetTxt = new Text2('Target: ' + targetScore, {
size: 60,
fill: "#fff"
});
targetTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(targetTxt);
targetTxt.y = 190;
// --- Board Positioning ---
var boardWidth = boardCols * candySize + (boardCols - 1) * boardPadding;
var boardHeight = boardRows * candySize + (boardRows - 1) * boardPadding;
var boardOffsetX = Math.floor((2048 - boardWidth) / 2);
var boardOffsetY = Math.floor((2732 - boardHeight) / 2) + 60;
// --- Helper Functions ---
// Get board position (x, y) for a given row, col
function getBoardPos(row, col) {
return {
x: boardOffsetX + col * (candySize + boardPadding) + candySize / 2,
y: boardOffsetY + row * (candySize + boardPadding) + candySize / 2
};
}
// Get candy at (row, col)
function getCandy(row, col) {
if (row < 0 || row >= boardRows || col < 0 || col >= boardCols) return null;
return board[row][col];
}
// Swap two candies in board and update their row/col
function swapCandies(c1, c2) {
var r1 = c1.row,
c1c = c1.col,
r2 = c2.row,
c2c = c2.col;
board[r1][c1c] = c2;
board[r2][c2c] = c1;
c1.row = r2;
c1.col = c2c;
c2.row = r1;
c2.col = c1c;
}
// Animate swap between two candies
function animateSwap(c1, c2, _onFinish2) {
isAnimating = true;
var pos1 = getBoardPos(c1.row, c1.col);
var pos2 = getBoardPos(c2.row, c2.col);
tween(c1, {
x: pos2.x,
y: pos2.y
}, {
duration: 180,
easing: tween.cubicInOut
});
tween(c2, {
x: pos1.x,
y: pos1.y
}, {
duration: 180,
easing: tween.cubicInOut,
onFinish: function onFinish() {
isAnimating = false;
if (_onFinish2) _onFinish2();
}
});
}
// Animate candy falling to new position
function animateMoveTo(candy, newRow, newCol, onFinish) {
var pos = getBoardPos(newRow, newCol);
tween(candy, {
x: pos.x,
y: pos.y
}, {
duration: 180,
easing: tween.cubicInOut,
onFinish: onFinish
});
}
// Deselect all candies
function deselectAll() {
if (selectedCandy) {
tween(selectedCandy, {
scaleX: 1,
scaleY: 1
}, {
duration: 100
});
selectedCandy = null;
}
}
// Check if two candies are adjacent
function areAdjacent(c1, c2) {
return Math.abs(c1.row - c2.row) + Math.abs(c1.col - c2.col) === 1;
}
// Find all matches on the board, mark candies as isMatched
function findMatches() {
var found = false;
// Clear previous matches
for (var r = 0; r < boardRows; r++) {
for (var c = 0; c < boardCols; c++) {
board[r][c].isMatched = false;
}
}
// Horizontal matches
for (var r = 0; r < boardRows; r++) {
var matchLen = 1;
for (var c = 1; c < boardCols; c++) {
if (board[r][c].type === board[r][c - 1].type) {
matchLen++;
} else {
if (matchLen >= 3) {
for (var k = 0; k < matchLen; k++) {
board[r][c - 1 - k].isMatched = true;
}
found = true;
}
matchLen = 1;
}
}
if (matchLen >= 3) {
for (var k = 0; k < matchLen; k++) {
board[r][boardCols - 1 - k].isMatched = true;
}
found = true;
}
}
// Vertical matches
for (var c = 0; c < boardCols; c++) {
var matchLen = 1;
for (var r = 1; r < boardRows; r++) {
if (board[r][c].type === board[r - 1][c].type) {
matchLen++;
} else {
if (matchLen >= 3) {
for (var k = 0; k < matchLen; k++) {
board[r - 1 - k][c].isMatched = true;
}
found = true;
}
matchLen = 1;
}
}
if (matchLen >= 3) {
for (var k = 0; k < matchLen; k++) {
board[boardRows - 1 - k][c].isMatched = true;
}
found = true;
}
}
return found;
}
// Remove matched candies, return number removed
function removeMatches(onFinish) {
var removed = 0;
var toPop = [];
for (var r = 0; r < boardRows; r++) {
for (var c = 0; c < boardCols; c++) {
var candy = board[r][c];
if (candy.isMatched) {
toPop.push(candy);
removed++;
}
}
}
if (removed === 0) {
if (onFinish) onFinish();
return;
}
var popped = 0;
for (var i = 0; i < toPop.length; i++) {
toPop[i].pop(function () {
popped++;
if (popped === toPop.length) {
for (var j = 0; j < toPop.length; j++) {
var c = toPop[j];
game.removeChild(c);
candies.splice(candies.indexOf(c), 1);
board[c.row][c.col] = null;
}
if (onFinish) onFinish(removed);
}
});
}
}
// Drop candies down to fill empty spaces, return true if any moved
function dropCandies(onFinish) {
var moved = false;
var moves = [];
for (var c = 0; c < boardCols; c++) {
for (var r = boardRows - 1; r >= 0; r--) {
if (board[r][c] === null) {
// Find first non-null above
for (var k = r - 1; k >= 0; k--) {
if (board[k][c]) {
var candy = board[k][c];
board[r][c] = candy;
board[k][c] = null;
var oldRow = candy.row;
candy.row = r;
moves.push({
candy: candy,
from: oldRow,
to: r,
col: c
});
break;
}
}
}
}
}
if (moves.length === 0) {
if (onFinish) onFinish(false);
return;
}
var finished = 0;
for (var i = 0; i < moves.length; i++) {
var move = moves[i];
animateMoveTo(move.candy, move.to, move.col, function () {
finished++;
if (finished === moves.length) {
if (onFinish) onFinish(true);
}
});
}
}
// Fill empty spaces at the top with new candies
function fillBoard(onFinish) {
var filled = 0;
var toFill = [];
for (var c = 0; c < boardCols; c++) {
for (var r = 0; r < boardRows; r++) {
if (board[r][c] === null) {
var candy = new Candy();
var type = Math.floor(Math.random() * candyTypes);
candy.setType(type);
candy.row = r;
candy.col = c;
var pos = getBoardPos(r, c);
candy.x = pos.x;
candy.y = pos.y - 400; // Drop from above
game.addChild(candy);
candies.push(candy);
board[r][c] = candy;
toFill.push(candy);
}
}
}
if (toFill.length === 0) {
if (onFinish) onFinish();
return;
}
var finished = 0;
for (var i = 0; i < toFill.length; i++) {
var candy = toFill[i];
var pos = getBoardPos(candy.row, candy.col);
animateMoveTo(candy, candy.row, candy.col, function () {
finished++;
if (finished === toFill.length) {
if (onFinish) onFinish();
}
});
}
}
// Check if the board has any possible moves (for future: shuffle if not)
function hasPossibleMoves() {
// For MVP, skip shuffle, always assume possible moves
return true;
}
// Update UI
function updateUI() {
scoreTxt.setText(score);
movesTxt.setText('Moves: ' + movesLeft);
targetTxt.setText('Target: ' + targetScore);
}
// Handle end of turn: check for matches, drop, fill, repeat as needed
function resolveBoard(afterResolve) {
if (findMatches()) {
removeMatches(function (removed) {
score += removed * 100;
updateUI();
dropCandies(function () {
fillBoard(function () {
resolveBoard(afterResolve);
});
});
});
} else {
if (afterResolve) afterResolve();
}
}
// Handle win/lose
function checkEnd() {
if (score >= targetScore) {
storage.level = level + 1;
LK.showYouWin();
return true;
}
if (movesLeft <= 0) {
LK.showGameOver();
return true;
}
return false;
}
// --- Board Initialization ---
function createBoard() {
// Clear previous
for (var i = 0; i < candies.length; i++) {
game.removeChild(candies[i]);
}
candies = [];
board = [];
for (var r = 0; r < boardRows; r++) {
board[r] = [];
for (var c = 0; c < boardCols; c++) {
var candy = new Candy();
var type = Math.floor(Math.random() * candyTypes);
candy.setType(type);
candy.row = r;
candy.col = c;
var pos = getBoardPos(r, c);
candy.x = pos.x;
candy.y = pos.y;
game.addChild(candy);
candies.push(candy);
board[r][c] = candy;
}
}
// Remove any initial matches
while (findMatches()) {
for (var r = 0; r < boardRows; r++) {
for (var c = 0; c < boardCols; c++) {
if (board[r][c].isMatched) {
var newType;
do {
newType = Math.floor(Math.random() * candyTypes);
} while (r > 0 && board[r - 1][c].type === newType || c > 0 && board[r][c - 1].type === newType);
board[r][c].setType(newType);
board[r][c].isMatched = false;
}
}
}
}
}
// --- Input Handling ---
// Find candy at (x, y) in game coordinates
function findCandyAt(x, y) {
for (var i = 0; i < candies.length; i++) {
var c = candies[i];
var dx = x - c.x;
var dy = y - c.y;
if (Math.abs(dx) < candySize / 2 && Math.abs(dy) < candySize / 2) {
return c;
}
}
return null;
}
// Handle tap or drag
game.down = function (x, y, obj) {
if (isAnimating) return;
var c = findCandyAt(x, y);
if (!c) {
deselectAll();
return;
}
if (!selectedCandy) {
selectedCandy = c;
tween(selectedCandy, {
scaleX: 1.15,
scaleY: 1.15
}, {
duration: 100
});
} else if (selectedCandy === c) {
deselectAll();
} else if (areAdjacent(selectedCandy, c)) {
// Swap attempt
isSwapping = true;
swappingCandy = c;
// Animate swap
animateSwap(selectedCandy, swappingCandy, function () {
swapCandies(selectedCandy, swappingCandy);
// Check if swap creates a match
if (findMatches()) {
movesLeft--;
updateUI();
resolveBoard(function () {
deselectAll();
isSwapping = false;
swappingCandy = null;
checkEnd();
});
} else {
// No match, swap back and decrement moves
movesLeft--;
updateUI();
animateSwap(selectedCandy, swappingCandy, function () {
swapCandies(selectedCandy, swappingCandy);
deselectAll();
isSwapping = false;
swappingCandy = null;
checkEnd();
});
}
});
} else {
// Select new candy
tween(selectedCandy, {
scaleX: 1,
scaleY: 1
}, {
duration: 100
});
selectedCandy = c;
tween(selectedCandy, {
scaleX: 1.15,
scaleY: 1.15
}, {
duration: 100
});
}
};
// Drag to swap
game.move = function (x, y, obj) {
if (isAnimating || !selectedCandy) return;
var c = findCandyAt(x, y);
if (c && c !== selectedCandy && areAdjacent(selectedCandy, c)) {
game.down(x, y, obj);
}
};
game.up = function (x, y, obj) {
// No-op for now
};
// --- Game Start ---
function startGame() {
level = storage.level || 1;
score = 0;
movesLeft = moveLimit + (level - 1) * 2;
targetScore = 3000 + (level - 1) * 500;
updateUI();
createBoard();
deselectAll();
isAnimating = false;
isSwapping = false;
swappingCandy = null;
selectedCandy = null;
// Remove any matches at start
resolveBoard();
}
startGame();
// --- Game Tick ---
game.update = function () {
// No per-frame logic needed for MVP
}; ===================================================================
--- original.js
+++ change.js
@@ -494,14 +494,17 @@
swappingCandy = null;
checkEnd();
});
} else {
- // No match, swap back
+ // No match, swap back and decrement moves
+ movesLeft--;
+ updateUI();
animateSwap(selectedCandy, swappingCandy, function () {
swapCandies(selectedCandy, swappingCandy);
deselectAll();
isSwapping = false;
swappingCandy = null;
+ checkEnd();
});
}
});
} else {