User prompt
The text Next Level should be inside a prism and filled, visible and like a button.
User prompt
At the end of the episode, the episode score will flash on the screen and the next level button will appear on the screen.
User prompt
Sections must be passable .
User prompt
Improve the gameplay of the levels. Create new level designs. Make it more complicated and enjoyable.
User prompt
There is a bug in 5-way matchmaking
User prompt
When there was a 4-way match in the sections, the entire column disappeared. There is a bug.
User prompt
There must be a square-shaped match of 4. When that match occurs, the bomb should shine in the place where the match is and destroy the 8 squares next to it.
User prompt
The sections should be more complicated
User prompt
When the 4 explodes, there is a bug, solve that problem
User prompt
Each section should have a different design and gameplay. Design different levels. Don't play the same thing over and over.
User prompt
There should be next level and prev level buttons at the bottom of the game.
User prompt
Lower
User prompt
Level should not have too high goals.
User prompt
The level at the bottom left should be seen. There should be a difficult section in every 5 levels, like a boss section.
User prompt
The goals for each section should be achievable. The difficulty should increase as the level increases. The sections are easy at the beginning. The sections should be harder as they progress.
User prompt
I want you to do your calculations for each section and make it really passable. The game will progress level by level. Another feature should be a reset button at the bottom. The user should be able to restart the section.
User prompt
If the same boxes are placed side by side as a result of the change of location and there is no explosion, the boxes should return to their original place.
User prompt
I will decide which box to choose. I will swap places with the other box by holding it down. Swapping can only be done with adjacent squares and if there is no match, that is, if it does not comply with the rules, it is The box returns to its original place
User prompt
Boxes are not changing places again
User prompt
Make it like candy crush all gameplay and rules
User prompt
I should be able to move in all directions
User prompt
Some of my picks don't move
User prompt
Sync by phone. It doesn't select the box I clicked
User prompt
Fix the tile selection synchronization bug where clicking a tile incorrectly selects another adjacent tile. Debug the grid coordinate system by recalculating click/touch positions relative to the grid's top-left origin, add boundary checks to prevent out-of-grid selections, implement precise hitbox detection for each tile, and ensure tile positions update correctly during animations. Remove any offset miscalculations in the posToGrid() function and verify z-index layers to prevent overlapping elements from intercepting clicks. Add debug visuals to highlight the actually selected tile area during testing.
User prompt
Fix the tile movement bug in the match-3 game where players cannot swap adjacent tiles. Ensure tiles respond to clicks/taps by highlighting when selected, validate swaps (check if tiles are adjacent and create valid matches), animate smooth position transitions, and reset input states after animations. Debug grid coordinate calculations to prevent misaligned clicks, add proper event listeners for tile interaction, and ensure the swap logic doesn’t lock the game state. Include visual feedback for invalid swaps and re-enable input after animations complete
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ // Candy class var Candy = Container.expand(function () { var self = Container.call(this); // Properties self.type = getRandomCandyType(); self.special = SPECIAL_NONE; self.blocker = BLOCKER_NONE; self.row = 0; self.col = 0; self.isFalling = false; self.isMatched = false; self.isSelected = false; self.asset = null; // Attach asset function updateAsset() { if (self.asset) { self.removeChild(self.asset); self.asset.destroy(); } var assetId = self.type; if (self.special === SPECIAL_STRIPED) assetId = 'candy_striped'; if (self.special === SPECIAL_BOMB) assetId = 'candy_bomb'; if (self.special === SPECIAL_RAINBOW) assetId = 'candy_rainbow'; if (self.blocker === BLOCKER_CHOCOLATE) assetId = 'blocker_chocolate'; if (self.blocker === BLOCKER_ICE) assetId = 'blocker_ice'; self.asset = self.attachAsset(assetId, { anchorX: 0.5, anchorY: 0.5 }); if (self.isSelected) { self.asset.scaleX = 1.15; self.asset.scaleY = 1.15; } else { self.asset.scaleX = 1; self.asset.scaleY = 1; } } // Set type self.setType = function (type, special, blocker) { self.type = type || getRandomCandyType(); self.special = special || SPECIAL_NONE; self.blocker = blocker || BLOCKER_NONE; updateAsset(); }; // Set selected self.setSelected = function (selected) { self.isSelected = selected; updateAsset(); }; // Animate to position self.moveTo = function (x, y, duration, onFinish) { tween(self, { x: x, y: y }, { duration: duration || 200, easing: tween.easeInOut, onFinish: onFinish }); }; // Destroy self.destroyCandy = function () { if (self.asset) { self.removeChild(self.asset); self.asset.destroy(); self.asset = null; } self.destroy(); }; // Init updateAsset(); return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x222244 }); /**** * Game Code ****/ // Candy types // Music // Sounds // Blockers // Special candies // Candy shapes/colors // Board data var CANDY_TYPES = ['candy_red', 'candy_green', 'candy_blue', 'candy_yellow', 'candy_purple', 'candy_orange']; // Special types var SPECIAL_NONE = 0; var SPECIAL_STRIPED = 1; var SPECIAL_BOMB = 2; var SPECIAL_RAINBOW = 3; // Blocker types var BLOCKER_NONE = 0; var BLOCKER_CHOCOLATE = 1; var BLOCKER_ICE = 2; // Board size var BOARD_COLS = 7; var BOARD_ROWS = 9; var CELL_SIZE = 200; var BOARD_OFFSET_X = Math.floor((2048 - BOARD_COLS * CELL_SIZE) / 2); var BOARD_OFFSET_Y = 300; // Helper: get random candy type function getRandomCandyType() { return CANDY_TYPES[Math.floor(Math.random() * CANDY_TYPES.length)]; } // Helper: get random int function randInt(a, b) { return a + Math.floor(Math.random() * (b - a + 1)); } var board = []; var candies = []; var selectedCandy = null; var swapping = false; var animating = false; // Level system // Level design: early levels are easy, later levels are harder and require more skill var levels = [{ moves: 22, target: 1800, blockers: [{ row: 2, type: BLOCKER_CHOCOLATE }] }, { moves: 20, target: 2500, blockers: [{ row: 2, type: BLOCKER_CHOCOLATE }, { row: 6, type: BLOCKER_ICE }] }, { moves: 18, target: 3500, blockers: [{ row: 2, type: BLOCKER_CHOCOLATE }, { row: 4, type: BLOCKER_ICE }, { row: 6, type: BLOCKER_ICE }] }, { moves: 16, target: 4800, blockers: [{ row: 2, type: BLOCKER_CHOCOLATE }, { row: 4, type: BLOCKER_CHOCOLATE }, { row: 6, type: BLOCKER_ICE }] }, { moves: 15, target: 6000, blockers: [{ row: 2, type: BLOCKER_CHOCOLATE }, { row: 4, type: BLOCKER_ICE }, { row: 6, type: BLOCKER_CHOCOLATE }] }, { moves: 14, target: 7500, blockers: [{ row: 2, type: BLOCKER_CHOCOLATE }, { row: 4, type: BLOCKER_ICE }, { row: 6, type: BLOCKER_ICE }] }, { moves: 13, target: 9000, blockers: [{ row: 2, type: BLOCKER_CHOCOLATE }, { row: 3, type: BLOCKER_ICE }, { row: 4, type: BLOCKER_CHOCOLATE }, { row: 6, type: BLOCKER_ICE }] }, { moves: 12, target: 11000, blockers: [{ row: 1, type: BLOCKER_ICE }, { row: 2, type: BLOCKER_CHOCOLATE }, { row: 4, type: BLOCKER_ICE }, { row: 6, type: BLOCKER_CHOCOLATE }] }, { moves: 11, target: 13000, blockers: [{ row: 1, type: BLOCKER_ICE }, { row: 2, type: BLOCKER_CHOCOLATE }, { row: 3, type: BLOCKER_ICE }, { row: 4, type: BLOCKER_CHOCOLATE }, { row: 6, type: BLOCKER_ICE }] }, { moves: 10, target: 16000, blockers: [{ row: 0, type: BLOCKER_ICE }, { row: 1, type: BLOCKER_CHOCOLATE }, { row: 2, type: BLOCKER_ICE }, { row: 3, type: BLOCKER_CHOCOLATE }, { row: 4, type: BLOCKER_ICE }, { row: 5, type: BLOCKER_CHOCOLATE }, { row: 6, type: BLOCKER_ICE }] }]; var currentLevel = 0; var movesLeft = levels[0].moves; var targetScore = levels[0].target; var score = 0; var scoreTxt = null; var movesTxt = null; var targetTxt = null; var boardContainer = null; var matchQueue = []; var refillQueue = []; var isProcessing = false; // GUI scoreTxt = new Text2('Score: 0', { size: 90, fill: "#fff" }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); movesTxt = new Text2('Moves: 20', { size: 70, fill: "#fff" }); movesTxt.anchor.set(0.5, 0); LK.gui.top.addChild(movesTxt); movesTxt.y = 110; targetTxt = new Text2('Target: 5000', { size: 60, fill: "#fff" }); targetTxt.anchor.set(0.5, 0); LK.gui.top.addChild(targetTxt); targetTxt.y = 180; // Board container boardContainer = new Container(); game.addChild(boardContainer); boardContainer.x = BOARD_OFFSET_X; boardContainer.y = BOARD_OFFSET_Y; // Initialize board function initBoard() { // Clear previous for (var i = 0; i < candies.length; ++i) { if (candies[i]) candies[i].destroyCandy(); } candies = []; board = []; for (var row = 0; row < BOARD_ROWS; ++row) { board[row] = []; for (var col = 0; col < BOARD_COLS; ++col) { var candy = new Candy(); candy.row = row; candy.col = col; candy.x = col * CELL_SIZE + CELL_SIZE / 2; candy.y = row * CELL_SIZE + CELL_SIZE / 2; // Add blockers based on current level config var blockers = levels[currentLevel].blockers; var placedBlocker = false; for (var b = 0; b < blockers.length; ++b) { if (row === blockers[b].row) { // Alternate blockers on even/odd columns for variety if (blockers[b].type === BLOCKER_CHOCOLATE && col % 2 === 0 || blockers[b].type === BLOCKER_ICE && col % 2 === 1) { candy.setType(candy.type, SPECIAL_NONE, blockers[b].type); placedBlocker = true; } } } boardContainer.addChild(candy); candies.push(candy); board[row][col] = candy; } } // Remove initial matches removeInitialMatches(); } // Remove initial matches to avoid auto-matches at start function removeInitialMatches() { for (var row = 0; row < BOARD_ROWS; ++row) { for (var col = 0; col < BOARD_COLS; ++col) { var type = board[row][col].type; // Check left if (col >= 2 && board[row][col - 1].type === type && board[row][col - 2].type === type) { var newType = getRandomCandyType(); while (newType === type) newType = getRandomCandyType(); board[row][col].setType(newType); } // Check up if (row >= 2 && board[row - 1][col].type === type && board[row - 2][col].type === type) { var newType = getRandomCandyType(); while (newType === type) newType = getRandomCandyType(); board[row][col].setType(newType); } } } } // Get candy at board position function getCandyAt(row, col) { if (row < 0 || row >= BOARD_ROWS || col < 0 || col >= BOARD_COLS) return null; return board[row][col]; } // Swap two candies function swapCandies(c1, c2, cb) { swapping = true; var r1 = c1.row, c1c = c1.col, r2 = c2.row, c2c = c2.col; // Swap in board board[r1][c1c] = c2; board[r2][c2c] = c1; // Swap row/col var tmpRow = c1.row, tmpCol = c1.col; c1.row = r2; c1.col = c2c; c2.row = r1; c2.col = c1c; // Ensure board and candies are in sync after swap board[c1.row][c1.col] = c1; board[c2.row][c2.col] = c2; // Animate var done = 0; c1.moveTo(c1.col * CELL_SIZE + CELL_SIZE / 2, c1.row * CELL_SIZE + CELL_SIZE / 2, 180, function () { done++; if (done === 2 && cb) { swapping = false; cb(); } }); c2.moveTo(c2.col * CELL_SIZE + CELL_SIZE / 2, c2.row * CELL_SIZE + CELL_SIZE / 2, 180, function () { done++; if (done === 2 && cb) { swapping = false; cb(); } }); LK.getSound('swap').play(); } // Check if two candies are adjacent function areAdjacent(c1, c2) { var dr = Math.abs(c1.row - c2.row); var dc = Math.abs(c1.col - c2.col); // Allow swapping in all four directions (up, down, left, right) return dr === 1 && dc === 0 || dr === 0 && dc === 1; } // Find all matches on the board, including special candy creation function findMatches() { var matches = []; var matchGroups = []; // For special candy creation // Horizontal for (var row = 0; row < BOARD_ROWS; ++row) { var count = 1; var startCol = 0; for (var col = 1; col < BOARD_COLS; ++col) { var prev = board[row][col - 1]; var curr = board[row][col]; if (curr.type === prev.type && curr.blocker === BLOCKER_NONE && prev.blocker === BLOCKER_NONE) { count++; } else { if (count >= 3) { var group = []; for (var k = 0; k < count; ++k) { group.push(board[row][col - 1 - k]); } matches = matches.concat(group); matchGroups.push(group); } count = 1; startCol = col; } } if (count >= 3) { var group = []; for (var k = 0; k < count; ++k) { group.push(board[row][BOARD_COLS - 1 - k]); } matches = matches.concat(group); matchGroups.push(group); } } // Vertical for (var col = 0; col < BOARD_COLS; ++col) { var count = 1; var startRow = 0; for (var row = 1; row < BOARD_ROWS; ++row) { var prev = board[row - 1][col]; var curr = board[row][col]; if (curr.type === prev.type && curr.blocker === BLOCKER_NONE && prev.blocker === BLOCKER_NONE) { count++; } else { if (count >= 3) { var group = []; for (var k = 0; k < count; ++k) { group.push(board[row - 1 - k][col]); } matches = matches.concat(group); matchGroups.push(group); } count = 1; startRow = row; } } if (count >= 3) { var group = []; for (var k = 0; k < count; ++k) { group.push(board[BOARD_ROWS - 1 - k][col]); } matches = matches.concat(group); matchGroups.push(group); } } // Remove duplicates var unique = []; for (var i = 0; i < matches.length; ++i) { if (unique.indexOf(matches[i]) === -1) unique.push(matches[i]); } // Mark special candy creation (striped, bomb, rainbow) for (var g = 0; g < matchGroups.length; ++g) { var group = matchGroups[g]; if (group.length === 4) { // Striped candy: horizontal or vertical var isHorizontal = group[0].row === group[1].row; var specialCandy = group[1]; // Place special at second in group if (specialCandy.special === SPECIAL_NONE) { specialCandy.special = SPECIAL_STRIPED; specialCandy.setType(specialCandy.type, SPECIAL_STRIPED, specialCandy.blocker); } } if (group.length >= 5) { // Color bomb (rainbow) var specialCandy = group[Math.floor(group.length / 2)]; // Place in the middle if (specialCandy.special === SPECIAL_NONE) { specialCandy.special = SPECIAL_RAINBOW; specialCandy.setType(specialCandy.type, SPECIAL_RAINBOW, specialCandy.blocker); } } } // Bomb candy for T or L shape // Find intersections of horizontal and vertical matches for (var i = 0; i < matchGroups.length; ++i) { var groupA = matchGroups[i]; if (groupA.length !== 3) continue; for (var j = i + 1; j < matchGroups.length; ++j) { var groupB = matchGroups[j]; if (groupB.length !== 3) continue; // Check for intersection for (var a = 0; a < 3; ++a) { for (var b = 0; b < 3; ++b) { if (groupA[a] === groupB[b]) { // Place bomb at intersection var bombCandy = groupA[a]; if (bombCandy.special === SPECIAL_NONE) { bombCandy.special = SPECIAL_BOMB; bombCandy.setType(bombCandy.type, SPECIAL_BOMB, bombCandy.blocker); } } } } } } return unique; } // Remove matched candies and animate, including special candy activation and blockers function removeMatches(matches, cb) { if (!matches || matches.length === 0) { if (cb) cb(); return; } LK.getSound('match').play(); var done = 0; var toRemove = []; // Activate special candies in matches for (var i = 0; i < matches.length; ++i) { var candy = matches[i]; if (candy.special === SPECIAL_STRIPED) { // Clear row or column if (Math.random() < 0.5) { // Clear row for (var c = 0; c < BOARD_COLS; ++c) { var target = board[candy.row][c]; if (toRemove.indexOf(target) === -1) toRemove.push(target); } } else { // Clear column for (var r = 0; r < BOARD_ROWS; ++r) { var target = board[r][candy.col]; if (toRemove.indexOf(target) === -1) toRemove.push(target); } } } else if (candy.special === SPECIAL_RAINBOW) { // Clear all candies of a random type on board var colorType = getRandomCandyType(); for (var r = 0; r < BOARD_ROWS; ++r) { for (var c = 0; c < BOARD_COLS; ++c) { var target = board[r][c]; if (target.type === colorType && toRemove.indexOf(target) === -1) toRemove.push(target); } } } else if (candy.special === SPECIAL_BOMB) { // Clear 3x3 area for (var dr = -1; dr <= 1; ++dr) { for (var dc = -1; dc <= 1; ++dc) { var rr = candy.row + dr, cc = candy.col + dc; if (rr >= 0 && rr < BOARD_ROWS && cc >= 0 && cc < BOARD_COLS) { var target = board[rr][cc]; if (toRemove.indexOf(target) === -1) toRemove.push(target); } } } } else { if (toRemove.indexOf(candy) === -1) toRemove.push(candy); } } // Remove blockers if matched for (var i = 0; i < toRemove.length; ++i) { var candy = toRemove[i]; if (candy.blocker === BLOCKER_CHOCOLATE) { // Remove chocolate in one match candy.blocker = BLOCKER_NONE; candy.setType(candy.type, candy.special, BLOCKER_NONE); continue; } if (candy.blocker === BLOCKER_ICE) { // Remove ice in two matches: first match cracks, second removes if (!candy._iceCracked) { candy._iceCracked = true; // Tint or visually indicate cracked ice (optional) candy.setType(candy.type, candy.special, BLOCKER_ICE); continue; } else { candy.blocker = BLOCKER_NONE; candy.setType(candy.type, candy.special, BLOCKER_NONE); continue; } } candy.isMatched = true; // Animate scale down and fade tween(candy, { scaleX: 0, scaleY: 0, alpha: 0 }, { duration: 200, easing: tween.easeIn, onFinish: function (candy) { return function () { if (candy.asset) { candy.removeChild(candy.asset); candy.asset.destroy(); candy.asset = null; } candy.visible = false; done++; if (done === toRemove.length && cb) cb(); }; }(candy) }); // Add score score += 100; } } // Drop candies to fill empty spaces function dropCandies(cb) { var moved = false; for (var col = 0; col < BOARD_COLS; ++col) { for (var row = BOARD_ROWS - 1; row >= 0; --row) { var candy = board[row][col]; if (!candy.isMatched && candy.visible) continue; // Find nearest above for (var above = row - 1; above >= 0; --above) { var aboveCandy = board[above][col]; if (!aboveCandy.isMatched && aboveCandy.visible) { // Move aboveCandy down board[row][col] = aboveCandy; aboveCandy.row = row; aboveCandy.col = col; aboveCandy.moveTo(col * CELL_SIZE + CELL_SIZE / 2, row * CELL_SIZE + CELL_SIZE / 2, 180); board[above][col] = candy; moved = true; break; } } } } if (cb) LK.setTimeout(cb, moved ? 200 : 0); } // Fill empty spaces with new candies function refillBoard(cb) { var created = false; for (var col = 0; col < BOARD_COLS; ++col) { for (var row = 0; row < BOARD_ROWS; ++row) { var candy = board[row][col]; if (!candy.isMatched && candy.visible) continue; // Create new candy var newCandy = new Candy(); newCandy.row = row; newCandy.col = col; newCandy.x = col * CELL_SIZE + CELL_SIZE / 2; newCandy.y = -CELL_SIZE + CELL_SIZE / 2; boardContainer.addChild(newCandy); candies.push(newCandy); board[row][col] = newCandy; newCandy.moveTo(col * CELL_SIZE + CELL_SIZE / 2, row * CELL_SIZE + CELL_SIZE / 2, 220); created = true; } } if (cb) LK.setTimeout(cb, created ? 220 : 0); } // Remove matched candies from board function clearMatchedCandies() { for (var row = 0; row < BOARD_ROWS; ++row) { for (var col = 0; col < BOARD_COLS; ++col) { var candy = board[row][col]; if (candy.isMatched) { candy.destroyCandy(); // Remove from candies array for (var i = 0; i < candies.length; ++i) { if (candies[i] === candy) { candies.splice(i, 1); break; } } // Replace with dummy invisible candy for drop logic var dummy = new Candy(); dummy.row = row; dummy.col = col; dummy.visible = false; board[row][col] = dummy; } } } } // Deselect all candies function deselectAll() { for (var i = 0; i < candies.length; ++i) { candies[i].setSelected(false); } selectedCandy = null; } // Handle user tap function handleTap(x, y, obj) { if (swapping || animating || isProcessing) return; // Convert to board coordinates, ensuring correct offset and boundaries // Remove any offset miscalculations: boardContainer.x/y is already set, so local is relative to grid origin // For touch/click, always use the event's x/y relative to the game, then subtract boardContainer.x/y to get local grid coordinates var localX = x - boardContainer.x; var localY = y - boardContainer.y; // Clamp localX/localY to be within the grid area if (localX < 0 || localY < 0) return; var col = Math.floor(localX / CELL_SIZE); var row = Math.floor(localY / CELL_SIZE); // Clamp to grid bounds // Boundary check if (col < 0 || col >= BOARD_COLS || row < 0 || row >= BOARD_ROWS) return; var candy = board[row][col]; // Debug: Draw a highlight rectangle over the selected cell if (typeof handleTap._debugRect !== "undefined" && handleTap._debugRect) { boardContainer.removeChild(handleTap._debugRect); handleTap._debugRect.destroy(); handleTap._debugRect = null; } var debugRect = LK.getAsset('blocker_ice', { anchorX: 0, anchorY: 0, x: col * CELL_SIZE, y: row * CELL_SIZE, width: CELL_SIZE, height: CELL_SIZE, alpha: 0.25 }); // Insert debugRect at the bottom of boardContainer's children so it doesn't block candy input if (boardContainer.children && boardContainer.children.length > 0) { boardContainer.addChildAt(debugRect, 0); } else { boardContainer.addChild(debugRect); } handleTap._debugRect = debugRect; if (!candy || candy.blocker !== BLOCKER_NONE) return; // If nothing selected, select this candy if (!selectedCandy) { deselectAll(); selectedCandy = candy; candy.setSelected(true); return; } // If clicking the same candy, deselect if (selectedCandy === candy) { deselectAll(); return; } // If adjacent, try to swap if (areAdjacent(selectedCandy, candy)) { swapping = true; selectedCandy.setSelected(false); candy.setSelected(false); // Lock input during animation var c1 = selectedCandy; var c2 = candy; deselectAll(); swapCandies(c1, c2, function () { // Special candy swap logic var specialActivated = false; // Rainbow (color bomb) swap if (c1.special === SPECIAL_RAINBOW || c2.special === SPECIAL_RAINBOW) { var colorType = c1.special === SPECIAL_RAINBOW ? c2.type : c1.type; var toRemove = []; for (var r = 0; r < BOARD_ROWS; ++r) { for (var c = 0; c < BOARD_COLS; ++c) { var target = board[r][c]; if (target.type === colorType) toRemove.push(target); } } removeMatches(toRemove, function () { clearMatchedCandies(); dropCandies(function () { refillBoard(function () { processMatches(); }); }); }); movesLeft--; updateGUI(); swapping = false; specialActivated = true; } // Striped + striped: clear row and col else if (c1.special === SPECIAL_STRIPED && c2.special === SPECIAL_STRIPED) { var toRemove = []; for (var c = 0; c < BOARD_COLS; ++c) { var t1 = board[c1.row][c]; if (toRemove.indexOf(t1) === -1) toRemove.push(t1); } for (var r = 0; r < BOARD_ROWS; ++r) { var t2 = board[r][c2.col]; if (toRemove.indexOf(t2) === -1) toRemove.push(t2); } removeMatches(toRemove, function () { clearMatchedCandies(); dropCandies(function () { refillBoard(function () { processMatches(); }); }); }); movesLeft--; updateGUI(); swapping = false; specialActivated = true; } // Bomb + any: clear 3x3 around both else if (c1.special === SPECIAL_BOMB || c2.special === SPECIAL_BOMB) { var toRemove = []; var bombCandies = [c1, c2]; for (var b = 0; b < 2; ++b) { if (bombCandies[b].special === SPECIAL_BOMB) { for (var dr = -1; dr <= 1; ++dr) { for (var dc = -1; dc <= 1; ++dc) { var rr = bombCandies[b].row + dr, cc = bombCandies[b].col + dc; if (rr >= 0 && rr < BOARD_ROWS && cc >= 0 && cc < BOARD_COLS) { var target = board[rr][cc]; if (toRemove.indexOf(target) === -1) toRemove.push(target); } } } } } removeMatches(toRemove, function () { clearMatchedCandies(); dropCandies(function () { refillBoard(function () { processMatches(); }); }); }); movesLeft--; updateGUI(); swapping = false; specialActivated = true; } if (!specialActivated) { // After swap, check for matches var matches = findMatches(); if (matches.length > 0) { processMatches(); movesLeft--; updateGUI(); swapping = false; } else { // No match, swap back LK.getSound('fail').play(); // Animate a quick shake for both candies var origX1 = c1.col * CELL_SIZE + CELL_SIZE / 2; var origY1 = c1.row * CELL_SIZE + CELL_SIZE / 2; var origX2 = c2.col * CELL_SIZE + CELL_SIZE / 2; var origY2 = c2.row * CELL_SIZE + CELL_SIZE / 2; tween(c1, { x: origX1 + 20 }, { duration: 60, onFinish: function onFinish() { tween(c1, { x: origX1 }, { duration: 60 }); } }); tween(c2, { x: origX2 - 20 }, { duration: 60, onFinish: function onFinish() { tween(c2, { x: origX2 }, { duration: 60 }); } }); // Actually swap back the candies to their original positions in the board and update their row/col swapCandies(c1, c2, function () { swapping = false; deselectAll(); // Re-select the original candy for user feedback selectedCandy = c1; c1.setSelected(true); }); movesLeft--; updateGUI(); } } }); return; } // Not adjacent, select new candy deselectAll(); selectedCandy = candy; candy.setSelected(true); } // Process matches and refill function processMatches() { isProcessing = true; var matches = findMatches(); if (matches.length === 0) { swapping = false; isProcessing = false; deselectAll(); checkGameEnd(); return; } removeMatches(matches, function () { clearMatchedCandies(); dropCandies(function () { refillBoard(function () { // Chocolate spread: after all moves, if any chocolate exists, spread to adjacent var chocolateList = []; for (var row = 0; row < BOARD_ROWS; ++row) { for (var col = 0; col < BOARD_COLS; ++col) { var c = board[row][col]; if (c.blocker === BLOCKER_CHOCOLATE) chocolateList.push(c); } } if (chocolateList.length > 0) { // Try to spread chocolate to adjacent non-blocker, non-special, non-chocolate for (var i = 0; i < chocolateList.length; ++i) { var c = chocolateList[i]; var dirs = [[0, 1], [1, 0], [0, -1], [-1, 0]]; for (var d = 0; d < dirs.length; ++d) { var rr = c.row + dirs[d][0], cc = c.col + dirs[d][1]; if (rr >= 0 && rr < BOARD_ROWS && cc >= 0 && cc < BOARD_COLS) { var target = board[rr][cc]; if (target.blocker === BLOCKER_NONE && target.special === SPECIAL_NONE) { target.blocker = BLOCKER_CHOCOLATE; target.setType(target.type, target.special, BLOCKER_CHOCOLATE); break; } } } } } // After all refills, check for new matches var newMatches = findMatches(); if (newMatches.length > 0) { processMatches(); } else { swapping = false; isProcessing = false; deselectAll(); checkGameEnd(); } }); }); }); } // Update GUI function updateGUI() { scoreTxt.setText('Score: ' + score); movesTxt.setText('Moves: ' + movesLeft); targetTxt.setText('Target: ' + targetScore); // Show "Boss" for every 5th level (level 5, 10, 15, ...) var levelNum = currentLevel + 1; if (levelNum % 5 === 0) { levelTxt.setText('Level: ' + levelNum + ' (Boss!)'); } else { levelTxt.setText('Level: ' + levelNum); } } // Check for win/lose function checkGameEnd() { if (score >= targetScore) { // Next level if available, else show win if (currentLevel < levels.length - 1) { currentLevel++; // Reset board, score, moves for next level score = 0; movesLeft = levels[currentLevel].moves; targetScore = levels[currentLevel].target; updateGUI(); initBoard(); } else { LK.showYouWin(); } } else if (movesLeft <= 0) { LK.showGameOver(); } } // Game event handlers game.down = function (x, y, obj) { // Don't allow tap in top left 100x100 if (x < 100 && y < 100) return; handleTap(x, y, obj); }; // Drag-to-swap logic var dragStartCandy = null; var dragCurrentCandy = null; var dragActive = false; // Helper: get candy at pixel position (returns null if not valid) function getCandyAtPixel(x, y) { var localX = x - boardContainer.x; var localY = y - boardContainer.y; if (localX < 0 || localY < 0) return null; var col = Math.floor(localX / CELL_SIZE); var row = Math.floor(localY / CELL_SIZE); if (col < 0 || col >= BOARD_COLS || row < 0 || row >= BOARD_ROWS) return null; return board[row][col]; } // Override game.down for drag start game.down = function (x, y, obj) { // Don't allow tap in top left 100x100 if (x < 100 && y < 100) return; if (swapping || animating || isProcessing) return; var candy = getCandyAtPixel(x, y); if (!candy || candy.blocker !== BLOCKER_NONE) return; dragStartCandy = candy; dragCurrentCandy = candy; dragActive = true; deselectAll(); candy.setSelected(true); selectedCandy = candy; }; // Drag move handler game.move = function (x, y, obj) { if (!dragActive || swapping || animating || isProcessing) return; var candy = getCandyAtPixel(x, y); if (!candy || candy.blocker !== BLOCKER_NONE) return; if (candy === dragStartCandy) return; // Only allow swap with adjacent if (areAdjacent(dragStartCandy, candy)) { // Lock drag dragActive = false; dragStartCandy.setSelected(false); candy.setSelected(false); var c1 = dragStartCandy; var c2 = candy; deselectAll(); swapCandies(c1, c2, function () { // Special candy swap logic (same as tap) var specialActivated = false; if (c1.special === SPECIAL_RAINBOW || c2.special === SPECIAL_RAINBOW) { var colorType = c1.special === SPECIAL_RAINBOW ? c2.type : c1.type; var toRemove = []; for (var r = 0; r < BOARD_ROWS; ++r) { for (var c = 0; c < BOARD_COLS; ++c) { var target = board[r][c]; if (target.type === colorType) toRemove.push(target); } } removeMatches(toRemove, function () { clearMatchedCandies(); dropCandies(function () { refillBoard(function () { processMatches(); }); }); }); movesLeft--; updateGUI(); swapping = false; specialActivated = true; } else if (c1.special === SPECIAL_STRIPED && c2.special === SPECIAL_STRIPED) { var toRemove = []; for (var c = 0; c < BOARD_COLS; ++c) { var t1 = board[c1.row][c]; if (toRemove.indexOf(t1) === -1) toRemove.push(t1); } for (var r = 0; r < BOARD_ROWS; ++r) { var t2 = board[r][c2.col]; if (toRemove.indexOf(t2) === -1) toRemove.push(t2); } removeMatches(toRemove, function () { clearMatchedCandies(); dropCandies(function () { refillBoard(function () { processMatches(); }); }); }); movesLeft--; updateGUI(); swapping = false; specialActivated = true; } else if (c1.special === SPECIAL_BOMB || c2.special === SPECIAL_BOMB) { var toRemove = []; var bombCandies = [c1, c2]; for (var b = 0; b < 2; ++b) { if (bombCandies[b].special === SPECIAL_BOMB) { for (var dr = -1; dr <= 1; ++dr) { for (var dc = -1; dc <= 1; ++dc) { var rr = bombCandies[b].row + dr, cc = bombCandies[b].col + dc; if (rr >= 0 && rr < BOARD_ROWS && cc >= 0 && cc < BOARD_COLS) { var target = board[rr][cc]; if (toRemove.indexOf(target) === -1) toRemove.push(target); } } } } } removeMatches(toRemove, function () { clearMatchedCandies(); dropCandies(function () { refillBoard(function () { processMatches(); }); }); }); movesLeft--; updateGUI(); swapping = false; specialActivated = true; } if (!specialActivated) { var matches = findMatches(); if (matches.length > 0) { processMatches(); movesLeft--; updateGUI(); swapping = false; } else { // No match, swap back LK.getSound('fail').play(); var origX1 = c1.col * CELL_SIZE + CELL_SIZE / 2; var origY1 = c1.row * CELL_SIZE + CELL_SIZE / 2; var origX2 = c2.col * CELL_SIZE + CELL_SIZE / 2; var origY2 = c2.row * CELL_SIZE + CELL_SIZE / 2; tween(c1, { x: origX1 + 20 }, { duration: 60, onFinish: function onFinish() { tween(c1, { x: origX1 }, { duration: 60 }); } }); tween(c2, { x: origX2 - 20 }, { duration: 60, onFinish: function onFinish() { tween(c2, { x: origX2 }, { duration: 60 }); } }); // Actually swap back the candies to their original positions in the board and update their row/col swapCandies(c1, c2, function () { swapping = false; deselectAll(); // Re-select the original candy for user feedback selectedCandy = c1; c1.setSelected(true); }); movesLeft--; updateGUI(); } } }); } }; // Drag end handler game.up = function (x, y, obj) { dragActive = false; dragStartCandy = null; dragCurrentCandy = null; // Deselect all if not swapping if (!swapping && !animating && !isProcessing) { deselectAll(); } }; // Game update game.update = function () { // No per-frame logic needed for MVP }; // Start music LK.playMusic('bgmusic', { fade: { start: 0, end: 0.7, duration: 1000 } }); // Level label at bottom left var levelTxt = new Text2('Level: 1', { size: 70, fill: "#fff" }); levelTxt.anchor.set(0, 1); LK.gui.bottomLeft.addChild(levelTxt); levelTxt.x = 0; levelTxt.y = 0; // Add reset button at the bottom center var resetBtn = new Text2('Reset', { size: 90, fill: "#fff" }); resetBtn.anchor.set(0.5, 1); LK.gui.bottom.addChild(resetBtn); resetBtn.y = 0; // flush to bottom resetBtn.interactive = true; resetBtn.buttonMode = true; resetBtn.down = function (x, y, obj) { // Reset current level: reset score, moves, board score = 0; movesLeft = levels[currentLevel].moves; targetScore = levels[currentLevel].target; updateGUI(); initBoard(); }; // Start game initBoard(); updateGUI();
===================================================================
--- original.js
+++ change.js
@@ -973,8 +973,15 @@
function updateGUI() {
scoreTxt.setText('Score: ' + score);
movesTxt.setText('Moves: ' + movesLeft);
targetTxt.setText('Target: ' + targetScore);
+ // Show "Boss" for every 5th level (level 5, 10, 15, ...)
+ var levelNum = currentLevel + 1;
+ if (levelNum % 5 === 0) {
+ levelTxt.setText('Level: ' + levelNum + ' (Boss!)');
+ } else {
+ levelTxt.setText('Level: ' + levelNum);
+ }
}
// Check for win/lose
function checkGameEnd() {
if (score >= targetScore) {
@@ -1193,8 +1200,17 @@
end: 0.7,
duration: 1000
}
});
+// Level label at bottom left
+var levelTxt = new Text2('Level: 1', {
+ size: 70,
+ fill: "#fff"
+});
+levelTxt.anchor.set(0, 1);
+LK.gui.bottomLeft.addChild(levelTxt);
+levelTxt.x = 0;
+levelTxt.y = 0;
// Add reset button at the bottom center
var resetBtn = new Text2('Reset', {
size: 90,
fill: "#fff"