User prompt
Fix bugs
User prompt
- Players receive a random colored block to place on a 7x9 grid. - Drag and drop the block onto any empty cell. - When three or more adjacent blocks of the same color connect, they merge into a new color and clear, scoring points. - Merges can trigger chain reactions for bonus points. - The game ends when the grid is full and no moves remain. - Aim for high scores by planning merges and creating cascades.
User prompt
Can not see the game, fix it
User prompt
Please fix the bug: 'Uncaught TypeError: Cannot read properties of null (reading 'grid')' in or related to this line: 'var bdata = prevState.grid[c] && prevState.grid[c][r] ? prevState.grid[c][r] : null;' Line Number: 252
Code edit (1 edits merged)
Please save this source code
User prompt
Please fix the bug: 'Uncaught TypeError: Cannot read properties of null (reading 'grid')' in or related to this line: 'var bdata = prevState.grid[c][r];' Line Number: 249
Code edit (1 edits merged)
Please save this source code
User prompt
Please fix the bug: 'Uncaught ReferenceError: copyNextBlocksState is not defined' in or related to this line: 'return arr;' Line Number: 210
User prompt
Please fix the bug: 'Uncaught ReferenceError: copyNextBlocksState is not defined' in or related to this line: 'return arr;' Line Number: 210
User prompt
Please fix the bug: 'Uncaught ReferenceError: copyNextBlocksState is not defined' in or related to this line: 'return arr;' Line Number: 210
User prompt
Please fix the bug: 'Uncaught ReferenceError: copyNextBlocksState is not defined' in or related to this line: 'return arr;' Line Number: 210
User prompt
Please fix the bug: 'Uncaught ReferenceError: copyNextBlocksState is not defined' in or related to this line: 'return arr;' Line Number: 210
User prompt
Please fix the bug: 'Uncaught ReferenceError: copyNextBlocksState is not defined' in or related to this line: 'return arr;' Line Number: 210
User prompt
Please fix the bug: 'Uncaught ReferenceError: copyNextBlocksState is not defined' in or related to this line: 'return arr;' Line Number: 210
User prompt
Please fix the bug: 'Uncaught ReferenceError: copyNextBlocksState is not defined' in or related to this line: 'return arr;' Line Number: 210
User prompt
Please fix the bug: 'Uncaught ReferenceError: copyNextBlocksState is not defined' in or related to this line: 'return arr;' Line Number: 210
User prompt
Fix the game
User prompt
Please fix the bug: 'Uncaught ReferenceError: copyNextBlocksState is not defined' in or related to this line: 'return arr;' Line Number: 210
User prompt
Please fix the bug: 'Uncaught ReferenceError: copyNextBlocksState is not defined' in or related to this line: 'return arr;' Line Number: 210
User prompt
Please fix the bug: 'Uncaught ReferenceError: copyNextBlocksState is not defined' in or related to this line: 'return arr;' Line Number: 210
User prompt
Reset game codes to its functional
User prompt
Fix errors
User prompt
Undo deleted asset
User prompt
Please fix the bug: 'Uncaught ReferenceError: copyNextBlocksState is not defined' in or related to this line: 'return arr;' Line Number: 209
User prompt
Please fix the bug: 'Uncaught ReferenceError: copyNextBlocksState is not defined' in or related to this line: 'return arr;' Line Number: 209
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// --- Game Over / Reset handled by LK engine ---
// --- Block Class ---
var Block = Container.expand(function () {
var self = Container.call(this);
// Default color index (0=red, 1=orange, 2=yellow, 3=green, 4=blue, 5=purple, 6=pink, 7=white, 8=rainbow)
self.colorIndex = 0;
self.isLocked = false;
self.isOnGrid = false;
self.gridX = -1;
self.gridY = -1;
// Attach block asset (default, will be set by setColor)
self.blockAsset = self.attachAsset('Hd', {
anchorX: 0.5,
anchorY: 0.5
});
self.scaleX = 1;
self.scaleY = 1;
// Set color and locked state
self.setColor = function (colorIdx) {
self.colorIndex = colorIdx;
self.isLocked = false; // No more locked blocks
// Remove old asset if exists
if (self.blockAsset) {
self.removeChild(self.blockAsset);
}
self.blockAsset = self.attachAsset('Hd', {
anchorX: 0.5,
anchorY: 0.5
});
self.scaleX = 1;
self.scaleY = 1;
// Assign each block a unique, visually distinct color using the Hd asset for all
// 0=yellow, 1=red, 2=blue, 3=green, 4=purple, 5=rainbow/white, 6=pink, 7=black, 8=orange
var tints = [0xFFF600,
// 0 Yellow
0xFF6666,
// 1 Red
0x3ec1ff,
// 2 Blue
0x7cff6b,
// 3 Green (matches face_green)
0xd08cff,
// 4 Purple
0xffffff,
// 5 Rainbow/white (special)
0xffb7e5,
// 6 Pink
0x222222,
// 7 Black
0xffa500 // 8 Orange
];
// Map colorIndex to correct tint
if (colorIdx === 0) {
self.blockAsset.tint = tints[0]; // yellow
} else if (colorIdx === 1) {
self.blockAsset.tint = tints[1]; // red
} else if (colorIdx === 2) {
self.blockAsset.tint = tints[2]; // blue
} else if (colorIdx === 3) {
self.blockAsset.tint = tints[3]; // green
} else if (colorIdx === 4) {
self.blockAsset.tint = tints[4]; // purple
} else if (colorIdx === 5) {
self.blockAsset.tint = tints[5]; // rainbow/white
} else if (colorIdx === 6) {
self.blockAsset.tint = tints[6]; // pink
} else if (colorIdx === 7) {
self.blockAsset.tint = tints[7]; // black
} else if (colorIdx === 8) {
self.blockAsset.tint = tints[8]; // orange
} else {
self.blockAsset.tint = tints[0]; // fallback to yellow
}
// Add a lightful flash effect when color is set
LK.effects.flashObject(self.blockAsset, 0xffffff, 220);
};
// Animate merge (scale up and back)
self.animateMerge = function (_onFinish) {
tween(self, {
scaleX: 1.25,
scaleY: 1.25
}, {
duration: 80,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(self, {
scaleX: 1,
scaleY: 1
}, {
duration: 100,
easing: tween.easeIn,
onFinish: function onFinish() {
if (_onFinish) {
_onFinish();
}
}
});
}
});
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x222233
});
/****
* Game Code
****/
// Flower and honey jar assets
// Face assets in different colors
// Removed block_gray asset initialization (no more grey squares)
// locked block
// --- Game Constants ---
// Define block shapes for each color and merged color
// We'll use 4 colors: red, blue, yellow, purple (no green)
// Merged block (final color)
// Sound for merge
// visually white, but will be rainbow in logic
// More defined, fun, and clear block shapes for each color
// Cheerful blue
// Cheerful green
// Cheerful purple
// Bright yellow for rainbow
// Cheerful red
// Pure white
// Cheerful yellow
// Cheerful pink
// Fill nextBlocks with random colors (original mechanic)
var GRID_COLS = 6;
var GRID_ROWS = 7;
var CELL_SIZE = 260; // px (was 200, now bigger for larger squares)
var GRID_OFFSET_X = Math.floor((2048 - GRID_COLS * CELL_SIZE) / 2);
var GRID_OFFSET_Y = 350;
var COLORS = [0, 1, 2, 3, 4, 6, 7, 8]; // 0=yellow, 1=red, 2=blue, 3=green, 4=purple, 6=pink, 7=black, 8=orange
var COLOR_NAMES = ['yellow', 'red', 'blue', 'green', 'purple', 'rainbow', 'pink', 'black', 'orange'];
var MAX_COLOR_INDEX = 8; // 0-8 normal, 5=rainbow (special, can't merge further)
var RAINBOW_COLOR_INDEX = 5; // special rainbow block (if needed)
var BLOCK_DROP_Y = 2200; // y position for blocks that fall off
// --- Game State ---
var grid = []; // 2D array [col][row] of Block or null
for (var c = 0; c < GRID_COLS; c++) {
grid[c] = [];
for (var r = 0; r < GRID_ROWS; r++) {
grid[c][r] = null;
}
}
var draggingBlock = null;
var dragOffsetX = 0;
var dragOffsetY = 0;
var nextBlocks = []; // Array of next blocks to place
var score = 0;
var scoreTxt = null;
var isProcessing = false; // Prevent input during merges
// --- Undo/Föregående State ---
var prevState = null;
// Helper: Deep copy grid state (only colorIndex, isLocked, isOnGrid, gridX, gridY)
function copyGridState(srcGrid) {
var arr = [];
for (var c = 0; c < GRID_COLS; c++) {
arr[c] = [];
for (var r = 0; r < GRID_ROWS; r++) {
var b = srcGrid[c][r];
if (b) {
// Remove pink and violet cubes if present
if (b.colorIndex === 4 || b.colorIndex === 6) {
arr[c][r] = {
colorIndex: b.colorIndex,
isLocked: b.isLocked,
isOnGrid: b.isOnGrid,
gridX: b.gridX,
gridY: b.gridY
};
} else {
arr[c][r] = null;
}
}
}
}
// Helper: Deep copy nextBlocks state (only colorIndex)
function copyNextBlocksState(srcNextBlocks) {
var arr = [];
for (var i = 0; i < srcNextBlocks.length; i++) {
// Defensive: handle both Block objects and plain objects
var colorIndex = srcNextBlocks[i] && typeof srcNextBlocks[i].colorIndex !== "undefined" ? srcNextBlocks[i].colorIndex : null;
arr[i] = {
colorIndex: colorIndex
};
}
return arr;
}
// Save current state for undo
function savePrevState() {
prevState = {
grid: copyGridState(grid),
nextBlocks: copyNextBlocksState(nextBlocks),
score: score
};
}
// Restore previous state (undo)
function restorePrevState() {
if (!prevState) {
return;
}
// Remove all blocks from game
for (var c = 0; c < GRID_COLS; c++) {
for (var r = 0; r < GRID_ROWS; r++) {
if (grid[c][r]) {
grid[c][r].destroy();
grid[c][r] = null;
}
}
}
}
// Restore grid
if (!prevState || !prevState.grid) {
// Defensive: If prevState or prevState.grid is null, skip resto
}
for (var c = 0; c < GRID_COLS; c++) {
for (var r = 0; r < GRID_ROWS; r++) {
var bdata = prevState.grid && prevState.grid[c] && prevState.grid[c][r] ? prevState.grid[c][r] : null;
if (bdata) {
var b = new Block();
b.setColor(bdata.colorIndex);
b.isOnGrid = bdata.isOnGrid;
b.gridX = bdata.gridX;
b.gridY = bdata.gridY;
var pos = getPosForCell(c, r);
b.x = pos.x;
b.y = pos.y;
grid[c][r] = b;
game.addChild(b);
} else {
grid[c][r] = null;
}
}
}
// Remove all nextBlocks from game
for (var i = 0; i < nextBlocks.length; i++) {
if (nextBlocks[i]) {
nextBlocks[i].destroy();
}
}
nextBlocks = [];
// Restore nextBlocks
for (var i = 0; i < prevState.nextBlocks.length; i++) {
var nb = new Block();
nb.setColor(prevState.nextBlocks[i].colorIndex);
nb.x = 2048 / 2 + (nextBlocks.length - 1) * 220;
nb.y = 220;
nb.scaleX = nb.scaleY = 1;
nb.isOnGrid = false;
nb.gridX = -1;
nb.gridY = -1;
game.addChild(nb);
nextBlocks.push(nb);
}
// Reposition nextBlocks
for (var i = 0; i < nextBlocks.length; i++) {
var bx = 2048 / 2 + (i - 1) * 220;
nextBlocks[i].x = bx;
nextBlocks[i].y = 220;
}
// Restore score
score = prevState.score;
scoreTxt.setText(score);
prevState = null;
}
// Add a button for undo/föregående
var undoBtn = new Text2('⟲', {
size: 110,
fill: "#fff"
});
undoBtn.anchor.set(0.5, 0.5);
undoBtn.x = 2048 - 120;
undoBtn.y = 120;
undoBtn.interactive = true;
undoBtn.buttonMode = true;
undoBtn.down = function () {
restorePrevState();
};
LK.gui.top.addChild(undoBtn);
// --- GUI Elements ---
scoreTxt = new Text2('0', {
size: 120,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// --- Draw Background Image ---
var background = LK.getAsset('Background', {
width: 2048,
height: 2732,
anchorX: 0,
anchorY: 0,
x: 0,
y: 0,
alpha: 1
});
game.addChild(background);
// --- Draw Grid Background ---
for (var c = 0; c < GRID_COLS; c++) {
for (var r = 0; r < GRID_ROWS; r++) {
var cell = LK.getAsset('block_white', {
anchorX: 0.5,
anchorY: 0.5,
x: GRID_OFFSET_X + c * CELL_SIZE + CELL_SIZE / 2,
y: GRID_OFFSET_Y + r * CELL_SIZE + CELL_SIZE / 2,
alpha: 0.08
});
game.addChild(cell);
}
}
// --- Draw Grid Lines ---
for (var c = 0; c <= GRID_COLS; c++) {
var x = GRID_OFFSET_X + c * CELL_SIZE;
var y1 = GRID_OFFSET_Y;
var y2 = GRID_OFFSET_Y + GRID_ROWS * CELL_SIZE;
// Draw vertical line as a thick, high-contrast rectangle
var vline = LK.getAsset('block_black', {
width: 14,
height: GRID_ROWS * CELL_SIZE,
anchorX: 0.5,
anchorY: 0,
x: x,
y: y1,
alpha: 0.38
});
game.addChild(vline);
}
for (var r = 0; r <= GRID_ROWS; r++) {
var y = GRID_OFFSET_Y + r * CELL_SIZE;
var x1 = GRID_OFFSET_X;
var x2 = GRID_OFFSET_X + GRID_COLS * CELL_SIZE;
// Draw horizontal line as a thick, high-contrast rectangle
var hline = LK.getAsset('block_black', {
width: GRID_COLS * CELL_SIZE,
height: 14,
anchorX: 0,
anchorY: 0.5,
x: x1,
y: y,
alpha: 0.38
});
game.addChild(hline);
}
// --- Helper Functions ---
// Get grid cell from x,y (game coordinates)
function getGridCellFromPos(x, y) {
var gx = Math.floor((x - GRID_OFFSET_X) / CELL_SIZE);
var gy = Math.floor((y - GRID_OFFSET_Y) / CELL_SIZE);
if (gx < 0 || gx >= GRID_COLS || gy < 0 || gy >= GRID_ROWS) {
return null;
}
return {
col: gx,
row: gy
};
}
// Get position (x,y) for grid cell
function getPosForCell(col, row) {
return {
x: GRID_OFFSET_X + col * CELL_SIZE + CELL_SIZE / 2,
y: GRID_OFFSET_Y + row * CELL_SIZE + CELL_SIZE / 2
};
}
// Generate a random color index (0-4)
function randomColorIndex() {
return COLORS[Math.floor(Math.random() * COLORS.length)];
}
// Create a new block for the "next" area
function createNextBlock(idx) {
var block = new Block();
block.setColor(idx);
block.x = 2048 / 2 + (nextBlocks.length - 1) * 220;
block.y = 220;
block.scaleX = block.scaleY = 1;
block.isOnGrid = false;
block.gridX = -1;
block.gridY = -1;
game.addChild(block);
return block;
}
// Fill nextBlocks to always have 3 blocks
function refillNextBlocks() {
// Limit: Only allow new blocks if there is at least one empty cell on the grid
var emptyCells = 0;
for (var c = 0; c < GRID_COLS; c++) {
for (var r = 0; r < GRID_ROWS; r++) {
if (!grid[c][r]) {
emptyCells++;
}
}
}
// Only allow up to 3 next blocks, but never more than empty cells
var maxNext = Math.min(3, emptyCells);
// Fill nextBlocks with random colors (original mechanic)
while (nextBlocks.length < maxNext) {
// 5% chance to spawn a rainbow block, otherwise random color
var idx;
var rand = Math.random();
if (rand < 0.05) {
idx = RAINBOW_COLOR_INDEX; // rainbow
} else {
// Allow all defined COLORS except rainbow (5) as next blocks
var allowedColors = COLORS.filter(function (cidx) {
return cidx !== RAINBOW_COLOR_INDEX;
});
idx = allowedColors[Math.floor(Math.random() * allowedColors.length)];
}
var block = createNextBlock(idx);
nextBlocks.push(block);
}
// Remove excess nextBlocks if grid is almost full
while (nextBlocks.length > maxNext) {
var b = nextBlocks.pop();
if (b) {
b.destroy();
}
}
// Position them nicely
for (var i = 0; i < nextBlocks.length; i++) {
var bx = 2048 / 2 + (i - 1) * 220;
tween(nextBlocks[i], {
x: bx,
y: 220
}, {
duration: 180,
easing: tween.easeInOut
});
}
}
// Remove a block from nextBlocks and shift others
function popNextBlock(block) {
var idx = nextBlocks.indexOf(block);
if (idx >= 0) {
nextBlocks.splice(idx, 1);
}
refillNextBlocks();
}
// Place block on grid
function placeBlockOnGrid(block, col, row) {
block.isOnGrid = true;
block.gridX = col;
block.gridY = row;
grid[col][row] = block;
var pos = getPosForCell(col, row);
tween(block, {
x: pos.x,
y: pos.y,
scaleX: 1,
scaleY: 1
}, {
duration: 120,
easing: tween.easeOut
});
}
// Remove block from grid
function removeBlockFromGrid(col, row) {
var block = grid[col][row];
if (block) {
grid[col][row] = null;
}
}
// Find all connected blocks of the same color (DFS)
function findConnectedBlocks(col, row, colorIndex, visited) {
if (col < 0 || col >= GRID_COLS || row < 0 || row >= GRID_ROWS) {
return [];
}
if (visited[col + "," + row]) {
return [];
}
var block = grid[col][row];
if (!block || block.colorIndex !== colorIndex) {
return [];
}
visited[col + "," + row] = true;
var result = [{
col: col,
row: row,
block: block
}];
// 4 directions
var dirs = [[1, 0], [-1, 0], [0, 1], [0, -1]];
for (var i = 0; i < dirs.length; i++) {
var nc = col + dirs[i][0];
var nr = row + dirs[i][1];
result = result.concat(findConnectedBlocks(nc, nr, colorIndex, visited));
}
return result;
}
// Check if any moves are possible (empty cell exists)
function hasMoves() {
for (var c = 0; c < GRID_COLS; c++) {
for (var r = 0; r < GRID_ROWS; r++) {
if (!grid[c][r]) {
return true;
}
}
}
return false;
}
// --- Game Logic ---
// Handle block drop onto grid
function tryPlaceBlock(block, x, y) {
if (isProcessing) {
return false;
}
var cell = getGridCellFromPos(x, y);
if (!cell) {
return false;
}
var col = cell.col,
row = cell.row;
if (grid[col][row]) {
return false;
} // occupied
// No locked blocks, so no need to check
if (grid[col][row]) {
return false;
} // cell is occupied
if (block.colorIndex === RAINBOW_COLOR_INDEX) {
// Rainbow block special checks
}
// Save state for undo before placing
function savePrevState() {
prevState = {
grid: copyGridState(grid),
nextBlocks: copyNextBlocksState(nextBlocks),
score: score
};
}
savePrevState();
if (block.colorIndex === RAINBOW_COLOR_INDEX) {
// Place rainbow block and trigger effect
placeBlockOnGrid(block, col, row);
block.isOnGrid = true;
popNextBlock(block);
// Pick a random color present on the grid (not locked, not white, not rainbow)
var presentColors = {};
for (var c2 = 0; c2 < GRID_COLS; c2++) {
for (var r2 = 0; r2 < GRID_ROWS; r2++) {
var b2 = grid[c2][r2];
if (b2 && (b2.colorIndex === 0 || b2.colorIndex === 1 || b2.colorIndex === 2 || b2.colorIndex === 4)) {
presentColors[b2.colorIndex] = true;
}
}
}
var colorList = [];
for (var k in presentColors) {
if (presentColors.hasOwnProperty(k)) {
colorList.push(parseInt(k));
}
}
if (colorList.length > 0) {
var colorToClear = colorList[Math.floor(Math.random() * colorList.length)];
// Animate and remove all blocks of that color
for (var c2 = 0; c2 < GRID_COLS; c2++) {
for (var r2 = 0; r2 < GRID_ROWS; r2++) {
var b2 = grid[c2][r2];
if (b2 && b2.colorIndex === colorToClear) {
var tmpBlock = b2; // Store reference before nulling the grid position
grid[c2][r2] = null;
(function (bref) {
tween(bref, {
scaleX: 0,
scaleY: 0,
alpha: 0
}, {
duration: 180,
easing: tween.easeIn,
onFinish: function onFinish() {
bref.destroy();
}
});
})(tmpBlock);
}
}
}
// Add bonus score for rainbow clear
score += 200;
scoreTxt.setText(score);
}
// Play a sound for rainbow effect
LK.getSound('merge').play();
// After effect, check for game over
LK.setTimeout(function () {
if (!hasMoves()) {
LK.showGameOver();
}
}, 220);
return true;
}
// Place block
placeBlockOnGrid(block, col, row);
popNextBlock(block);
// After placement, check for merges
processMerges(col, row, function () {
// After all merges and cascades, check for game over
if (!hasMoves()) {
LK.showGameOver();
}
});
return true;
}
// Process merges and cascades starting from (col,row)
function processMerges(col, row, onFinish) {
if (isProcessing) {
return;
}
isProcessing = true;
var block = grid[col][row];
if (!block || block.isLocked) {
isProcessing = false;
if (onFinish) {
onFinish();
}
return;
}
var colorIdx = block.colorIndex;
if (colorIdx >= MAX_COLOR_INDEX) {
isProcessing = false;
if (onFinish) {
onFinish();
}
return;
}
// Remove pink (6) and violet (4) cubes immediately if present at this position
if (block.colorIndex === 4 || block.colorIndex === 6) {
removeBlockFromGrid(col, row);
tween(block, {
scaleX: 0,
scaleY: 0,
alpha: 0
}, {
duration: 120,
easing: tween.easeIn,
onFinish: function onFinish() {
block.destroy();
isProcessing = false;
if (onFinish) {
onFinish();
}
}
});
return;
}
// Find all connected blocks of same color
var connected = findConnectedBlocks(col, row, colorIdx, {});
if (connected.length >= 3) {
// Play happy merge sound only on actual merge
LK.getSound('merge').play();
// Find center position for merged block
var sumX = 0,
sumY = 0;
for (var i = 0; i < connected.length; i++) {
sumX += connected[i].col;
sumY += connected[i].row;
}
var centerCol = Math.round(sumX / connected.length);
var centerRow = Math.round(sumY / connected.length);
// Pick a new color for the merged block: must be a color not present in the merged group
var presentColors = {};
for (var i = 0; i < connected.length; i++) {
presentColors[connected[i].block.colorIndex] = true;
}
// Try to find a color not present in the merged group
var allPossibleColors = [0, 1, 2, 3, 4, 6, 7, 8, 5]; // all colors, rainbow last
var newColorIdx = -1;
for (var i = 0; i < allPossibleColors.length; i++) {
if (!presentColors[allPossibleColors[i]]) {
newColorIdx = allPossibleColors[i];
break;
}
}
// If all colors are present, fallback to rainbow/white
if (newColorIdx === -1) {
newColorIdx = RAINBOW_COLOR_INDEX;
}
// Remove all blocks in connected, but delay the destruction until after the merged block is created
var blocksToRemove = [];
for (var i = 0; i < connected.length; i++) {
var b = connected[i].block;
blocksToRemove.push(b);
removeBlockFromGrid(b.gridX, b.gridY);
}
// Animate and destroy the old blocks, then create the merged block after all are gone
var destroyCount = 0;
for (var i = 0; i < blocksToRemove.length; i++) {
(function (bref) {
tween(bref, {
scaleX: 0,
scaleY: 0,
alpha: 0
}, {
duration: 120,
easing: tween.easeIn,
onFinish: function onFinish() {
bref.destroy();
destroyCount++;
// When all blocks are destroyed, create the merged block and animate it
if (destroyCount === blocksToRemove.length) {
// Add delay for purple (4) and pink (6) merges
var doTransform = function doTransform() {
// Create merged block at center position
var mergedBlock = new Block();
mergedBlock.setColor(newColorIdx);
mergedBlock.isOnGrid = true;
mergedBlock.gridX = centerCol;
mergedBlock.gridY = centerRow;
var pos = getPosForCell(centerCol, centerRow);
mergedBlock.x = pos.x;
mergedBlock.y = pos.y;
grid[centerCol][centerRow] = mergedBlock;
game.addChild(mergedBlock);
mergedBlock.animateMerge(function () {
// Update score
var points = 10 * connected.length * (colorIdx + 1);
score += points;
scoreTxt.setText(score);
// After animation, finish processing
isProcessing = false;
// After merge, check if the new merged block itself can merge again (chain reaction)
processMerges(centerCol, centerRow, onFinish);
});
};
// If merging to purple (4) or pink (6), add a delay before transform
if (newColorIdx === 4 || newColorIdx === 6) {
LK.setTimeout(doTransform, 320);
} else {
doTransform();
}
}
}
});
})(blocksToRemove[i]);
}
// If no blocks to remove (should not happen), just finish
if (blocksToRemove.length === 0) {
mergedBlock.animateMerge(function () {
var points = 10 * connected.length * (colorIdx + 1);
score += points;
scoreTxt.setText(score);
isProcessing = false;
processMerges(centerCol, centerRow, onFinish);
});
}
} else {
isProcessing = false;
if (onFinish) {
onFinish();
}
}
}
// --- Input Handling ---
// Only allow dragging from nextBlocks
game.down = function (x, y, obj) {
if (isProcessing) {
return;
}
for (var i = 0; i < nextBlocks.length; i++) {
var block = nextBlocks[i];
// Check if touch is inside block
var dx = x - block.x;
var dy = y - block.y;
if (Math.abs(dx) < 90 && Math.abs(dy) < 90 && !block.isLocked && !block.isOnGrid) {
draggingBlock = block;
dragOffsetX = dx;
dragOffsetY = dy;
// Play a sound when picking up a block
LK.getSound('merge').play();
// Bring to front
game.addChild(block);
tween(block, {
scaleX: 1.15,
scaleY: 1.15
}, {
duration: 80,
easing: tween.easeOut
});
break;
}
}
};
game.move = function (x, y, obj) {
if (!draggingBlock) {
return;
}
draggingBlock.x = x - dragOffsetX;
draggingBlock.y = y - dragOffsetY;
};
game.up = function (x, y, obj) {
if (!draggingBlock) {
return;
}
// Try to place on grid
var placed = tryPlaceBlock(draggingBlock, draggingBlock.x, draggingBlock.y);
if (!placed) {
// Snap back to next area
var idx = nextBlocks.indexOf(draggingBlock);
var bx = 2048 / 2 + (idx - 1) * 220;
tween(draggingBlock, {
x: bx,
y: 220,
scaleX: 1,
scaleY: 1
}, {
duration: 120,
easing: tween.easeInOut
});
} else {
// Play a sound when block is placed on the grid
LK.getSound('merge').play();
// Block is now on grid, don't need to do anything
}
draggingBlock = null;
};
// --- Game Update Loop ---
game.update = function () {
// Animate blocks falling off (if any)
for (var c = 0; c < GRID_COLS; c++) {
for (var r = 0; r < GRID_ROWS; r++) {
var block = grid[c][r];
// Remove pink and violet cubes if present
if (block && (block.colorIndex === 4 || block.colorIndex === 6)) {
block.destroy();
grid[c][r] = null;
} else if (block && block.y > BLOCK_DROP_Y) {
block.destroy();
grid[c][r] = null;
}
}
}
// Occasionally spawn a locked block in a random empty cell (every 10 turns)
if (typeof game.turnsSinceLock === "undefined") {
game.turnsSinceLock = 0;
}
if (typeof game.lastBlocksOnGrid === "undefined") {
game.lastBlocksOnGrid = 0;
}
var blocksOnGrid = 0;
for (var c = 0; c < GRID_COLS; c++) {
for (var r = 0; r < GRID_ROWS; r++) {
if (grid[c][r]) {
blocksOnGrid++;
}
}
}
// Removed logic for spawning gray/locked blocks every 10 turns (no more grey squares)
if (blocksOnGrid > game.lastBlocksOnGrid) {
game.turnsSinceLock++;
game.lastBlocksOnGrid = blocksOnGrid;
// No locked block spawn
}
};
// --- Game Start ---
score = 0;
scoreTxt.setText(score);
refillNextBlocks(); ===================================================================
--- original.js
+++ change.js
@@ -229,9 +229,9 @@
// Defensive: If prevState or prevState.grid is null, skip resto
}
for (var c = 0; c < GRID_COLS; c++) {
for (var r = 0; r < GRID_ROWS; r++) {
- var bdata = prevState.grid[c] && prevState.grid[c][r] ? prevState.grid[c][r] : null;
+ var bdata = prevState.grid && prevState.grid[c] && prevState.grid[c][r] ? prevState.grid[c][r] : null;
if (bdata) {
var b = new Block();
b.setColor(bdata.colorIndex);
b.isOnGrid = bdata.isOnGrid;