/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ var Arrow = Container.expand(function (colorType) { var self = Container.call(this); self.colorType = colorType; self.directionX = 0; self.directionY = 0; self.speed = 400; self.isDestroyed = false; self.hasProcessedBlocks = false; self.connectedBlocks = []; // Create arrow graphics - long white arrow (invisible) var arrowGraphics = self.attachAsset('arrow', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.5, scaleY: 0.8, alpha: 0 }); self.setDirection = function (targetX, targetY) { // Calculate normalized direction vector var dx = targetX - self.x; var dy = targetY - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance > 0) { self.directionX = dx / distance; self.directionY = dy / distance; arrowGraphics.rotation = Math.atan2(dy, dx); } }; self.update = function () { if (self.isDestroyed) { return; } // Check if we have enough connected blocks to stop if (self.connectedBlocks && self.connectedBlocks.length >= 3 && !self.hasProcessedBlocks) { self.hasProcessedBlocks = true; // Process connected blocks immediately clearSelection(); selectedBlocks = self.connectedBlocks; blastSelectedBlocks(); // Keep arrow visible for a moment before destroying LK.setTimeout(function () { self.destroyArrow(); }, 500); return; } // Move in straight line using direction vector only if we haven't processed blocks yet // Stop moving if we have any connected blocks to keep arrow visible if (!self.hasProcessedBlocks && (!self.connectedBlocks || self.connectedBlocks.length === 0)) { self.x += self.directionX * self.speed * (1 / 60); self.y += self.directionY * self.speed * (1 / 60); } // Arrow is invisible, no pulsing effect needed // Check collision with blocks only if we haven't processed blocks yet if (!self.hasProcessedBlocks) { self.checkBlockCollision(); } // Remove arrow if it goes off screen if (self.x < -200 || self.x > 2248 || self.y < -200 || self.y > 2932) { // Only destroy if we haven't processed blocks or if no blocks were connected if (!self.hasProcessedBlocks) { // Clear selection for any connected blocks if (self.connectedBlocks && self.connectedBlocks.length > 0) { for (var i = 0; i < self.connectedBlocks.length; i++) { self.connectedBlocks[i].setSelected(false); } } self.destroyArrow(); } } }; self.connectAdjacentBlocks = function (centerBlock) { if (!centerBlock || centerBlock.isDestroyed) { return; } var directions = [[0, -1], [0, 1], [-1, 0], [1, 0]]; // up, down, left, right for (var i = 0; i < directions.length; i++) { var newX = centerBlock.gridX + directions[i][0]; var newY = centerBlock.gridY + directions[i][1]; if (newX >= 0 && newX < GRID_WIDTH && newY >= 0 && newY < GRID_HEIGHT) { var adjacentBlock = grid[newX][newY]; if (adjacentBlock && adjacentBlock.colorType === self.colorType && !adjacentBlock.isDestroyed) { // Check if we already connected this block var alreadyConnected = false; for (var j = 0; j < self.connectedBlocks.length; j++) { if (self.connectedBlocks[j] === adjacentBlock) { alreadyConnected = true; break; } } if (!alreadyConnected) { self.connectedBlocks.push(adjacentBlock); adjacentBlock.setSelected(true); // Recursively connect blocks adjacent to this one self.connectAdjacentBlocks(adjacentBlock); } } } } }; self.checkBlockCollision = function () { var gridX = Math.floor((self.x - GRID_START_X) / BLOCK_SIZE); var gridY = Math.floor((self.y - GRID_START_Y) / BLOCK_SIZE); if (gridX >= 0 && gridX < GRID_WIDTH && gridY >= 0 && gridY < GRID_HEIGHT) { var hitBlock = grid[gridX][gridY]; if (hitBlock && hitBlock.colorType === self.colorType && !hitBlock.isDestroyed) { // Mark this block as connected by the arrow if (!self.connectedBlocks) { self.connectedBlocks = []; } // Check if we already connected this block var alreadyConnected = false; for (var i = 0; i < self.connectedBlocks.length; i++) { if (self.connectedBlocks[i] === hitBlock) { alreadyConnected = true; break; } } if (!alreadyConnected) { self.connectedBlocks.push(hitBlock); // Visual feedback for connected block hitBlock.setSelected(true); // Connect all adjacent blocks of the same color self.connectAdjacentBlocks(hitBlock); // If we have less than 3 blocks after connecting, set a timeout to destroy arrow if (self.connectedBlocks.length < 3) { LK.setTimeout(function () { if (!self.isDestroyed && !self.hasProcessedBlocks) { // Clear selection for connected blocks for (var i = 0; i < self.connectedBlocks.length; i++) { self.connectedBlocks[i].setSelected(false); } self.destroyArrow(); } }, 1500); // Give 1.5 seconds to potentially connect more blocks } } } } }; self.destroyArrow = function () { if (!self.isDestroyed) { self.isDestroyed = true; self.destroy(); var index = activeArrows.indexOf(self); if (index !== -1) { activeArrows.splice(index, 1); } } }; return self; }); var Block = Container.expand(function (colorType) { var self = Container.call(this); self.colorType = colorType; self.gridX = 0; self.gridY = 0; self.isSelected = false; self.isDragging = false; self.originalX = 0; self.originalY = 0; self.isDestroyed = false; var blockAssets = ['blockRed', 'blockBlue', 'blockGreen', 'blockYellow', 'blockPurple']; var blockGraphics = self.attachAsset(blockAssets[colorType], { anchorX: 0.5, anchorY: 0.5 }); self.setSelected = function (selected) { self.isSelected = selected; if (selected) { blockGraphics.alpha = 0.8; blockGraphics.scaleX = 1.05; blockGraphics.scaleY = 1.05; tween(blockGraphics, { tint: 0xFFFFAA }, { duration: 200 }); } else { blockGraphics.alpha = 1.0; blockGraphics.scaleX = 1.0; blockGraphics.scaleY = 1.0; tween(blockGraphics, { tint: 0xFFFFFF }, { duration: 200 }); } }; self.blast = function () { if (self.isDestroyed) { return; } self.isDestroyed = true; // No individual point effect - will be handled by blastSelectedBlocks tween(blockGraphics, { scaleX: 1.5, scaleY: 1.5, alpha: 0 }, { duration: 300, easing: tween.easeOut, onFinish: function onFinish() { self.destroy(); } }); }; return self; }); var PointEffect = Container.expand(function (points, x, y) { var self = Container.call(this); self.x = x; self.y = y; // Randomly choose between point effect assets var effectAssets = ['pointEffect', 'pointEffect2']; var randomAsset = effectAssets[Math.floor(Math.random() * effectAssets.length)]; // Create point effect graphics - bigger for combo effects var effectGraphics = self.attachAsset(randomAsset, { anchorX: 0.5, anchorY: 0.5, scaleX: 0.1, scaleY: 0.1, alpha: 0 }); // No text needed - just visual effect // Appear with bigger effect for combos var maxScale = Math.min(3.0, 1.0 + points / 500); tween(effectGraphics, { scaleX: maxScale, scaleY: maxScale, alpha: 1.0 }, { duration: 300, easing: tween.bounceOut, onFinish: function onFinish() { // Move up and fade out tween(self, { y: self.y - 150 }, { duration: 1000, easing: tween.easeOut }); tween(effectGraphics, { alpha: 0, scaleX: maxScale * 1.5, scaleY: maxScale * 1.5 }, { duration: 1000, easing: tween.easeOut }); // Destroy effect after animation completes LK.setTimeout(function () { self.destroy(); }, 1000); } }); return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x2c3e50 }); /**** * Game Code ****/ var GRID_WIDTH = 8; var GRID_HEIGHT = 10; var BLOCK_SIZE = 200; var GRID_START_X = (2048 - GRID_WIDTH * BLOCK_SIZE) / 2; var GRID_START_Y = 400; var grid = []; var selectedBlocks = []; var currentLevel = 1; var currentScore = 0; var targetScore = 1000; var gameActive = true; var activeArrows = []; var shootingBlock = null; // Initialize grid array for (var x = 0; x < GRID_WIDTH; x++) { grid[x] = []; for (var y = 0; y < GRID_HEIGHT; y++) { grid[x][y] = null; } } // Add white background - bigger scale to fill more screen var background = game.attachAsset('background', { anchorX: 0, anchorY: 0, x: 0, y: 0, scaleX: 1.2, scaleY: 1.2 }); // UI Elements // Add background shape for score text var scoreBackground = game.attachAsset('scoreBackground', { anchorX: 0.5, anchorY: 0.5, x: 1024, y: 180, alpha: 0.8 }); var scoreText = new Text2('Score: 0', { size: 80, fill: 0x000000 }); scoreText.anchor.set(0.5, 0.5); scoreText.x = 1024; scoreText.y = 180; game.addChild(scoreText); // Add background shape for target text var targetBackground = game.attachAsset('targetBackground', { anchorX: 0.5, anchorY: 0.5, x: 1024, y: 280, alpha: 0.8 }); var targetText = new Text2('Target: 1000', { size: 70, fill: 0x0066CC }); targetText.anchor.set(0.5, 0.5); targetText.x = 1024; targetText.y = 280; game.addChild(targetText); // Add background shape for level text var levelBackground = game.attachAsset('levelBackground', { anchorX: 0.5, anchorY: 0.5, x: 1024, y: 100, alpha: 0.8 }); var levelText = new Text2('Level 1', { size: 90, fill: 0x000000 }); levelText.anchor.set(0.5, 0.5); levelText.x = 1024; levelText.y = 100; game.addChild(levelText); function createBlock(x, y) { var colorType = Math.floor(Math.random() * 5); var block = new Block(colorType); block.gridX = x; block.gridY = y; block.x = GRID_START_X + x * BLOCK_SIZE + BLOCK_SIZE / 2; block.y = GRID_START_Y + y * BLOCK_SIZE + BLOCK_SIZE / 2; grid[x][y] = block; game.addChild(block); return block; } function initializeGrid() { for (var x = 0; x < GRID_WIDTH; x++) { for (var y = 0; y < GRID_HEIGHT; y++) { createBlock(x, y); } } } function getConnectedBlocks(startX, startY, colorType, visited) { if (!visited) { visited = []; } var key = startX + ',' + startY; if (visited.indexOf(key) !== -1) { return []; } if (startX < 0 || startX >= GRID_WIDTH || startY < 0 || startY >= GRID_HEIGHT) { return []; } if (!grid[startX][startY] || grid[startX][startY].colorType !== colorType) { return []; } visited.push(key); var connected = [grid[startX][startY]]; // Check adjacent blocks (up, down, left, right) var directions = [[0, -1], [0, 1], [-1, 0], [1, 0]]; for (var i = 0; i < directions.length; i++) { var newX = startX + directions[i][0]; var newY = startY + directions[i][1]; var adjacentBlocks = getConnectedBlocks(newX, newY, colorType, visited); connected = connected.concat(adjacentBlocks); } return connected; } function clearSelection() { for (var i = 0; i < selectedBlocks.length; i++) { selectedBlocks[i].setSelected(false); } selectedBlocks = []; } function selectBlockGroup(block) { clearSelection(); selectedBlocks = getConnectedBlocks(block.gridX, block.gridY, block.colorType); if (selectedBlocks.length >= 3) { for (var i = 0; i < selectedBlocks.length; i++) { selectedBlocks[i].setSelected(true); } return true; } else { selectedBlocks = []; return false; } } function blastSelectedBlocks() { if (selectedBlocks.length < 3 || !gameActive) { return; } var points = calculateScore(selectedBlocks.length); currentScore += points; LK.getSound('blockBlast').play(); LK.effects.flashScreen(0xffffff, 200); // Calculate center position of all selected blocks for big effect var centerX = 0; var centerY = 0; for (var i = 0; i < selectedBlocks.length; i++) { centerX += selectedBlocks[i].x; centerY += selectedBlocks[i].y; } centerX /= selectedBlocks.length; centerY /= selectedBlocks.length; // Create single big point effect at center var bigPointEffect = new PointEffect(points, centerX, centerY); game.addChild(bigPointEffect); // Remove blocks from grid and blast them for (var i = 0; i < selectedBlocks.length; i++) { var block = selectedBlocks[i]; grid[block.gridX][block.gridY] = null; block.blast(); } selectedBlocks = []; // Apply gravity after a short delay LK.setTimeout(function () { applyGravity(); updateUI(); checkGameState(); }, 400); } function calculateScore(blockCount) { return blockCount * blockCount * 10; } function applyGravity() { for (var x = 0; x < GRID_WIDTH; x++) { var writeY = GRID_HEIGHT - 1; // Move existing blocks down for (var y = GRID_HEIGHT - 1; y >= 0; y--) { if (grid[x][y] !== null) { if (writeY !== y) { grid[x][writeY] = grid[x][y]; grid[x][y] = null; grid[x][writeY].gridY = writeY; // Animate block falling var targetY = GRID_START_Y + writeY * BLOCK_SIZE + BLOCK_SIZE / 2; tween(grid[x][writeY], { y: targetY }, { duration: 300, easing: tween.easeOut }); } writeY--; } } // Fill empty spaces at top with new blocks for (var y = writeY; y >= 0; y--) { var newBlock = createBlock(x, y); newBlock.y = GRID_START_Y - (writeY - y + 1) * BLOCK_SIZE + BLOCK_SIZE / 2; var targetY = GRID_START_Y + y * BLOCK_SIZE + BLOCK_SIZE / 2; tween(newBlock, { y: targetY }, { duration: 500, easing: tween.bounceOut }); } } } function updateUI() { scoreText.setText('Score: ' + currentScore); LK.setScore(currentScore); } function checkGameState() { if (currentScore >= targetScore) { gameActive = false; LK.getSound('levelComplete').play(); LK.setTimeout(function () { nextLevel(); }, 1000); } } function nextLevel() { currentLevel++; targetScore = currentScore + 500 * currentLevel; gameActive = true; levelText.setText('Level ' + currentLevel); targetText.setText('Target: ' + targetScore); updateUI(); if (currentLevel >= 10) { LK.showYouWin(); } } game.down = function (x, y, obj) { if (!gameActive) { return; } var gridX = Math.floor((x - GRID_START_X) / BLOCK_SIZE); var gridY = Math.floor((y - GRID_START_Y) / BLOCK_SIZE); if (gridX >= 0 && gridX < GRID_WIDTH && gridY >= 0 && gridY < GRID_HEIGHT) { var block = grid[gridX][gridY]; if (block) { shootingBlock = block; } } }; game.move = function (x, y, obj) { // No dragging mechanics needed for arrow shooting }; game.up = function (x, y, obj) { if (!gameActive || !shootingBlock) { return; } // Shoot arrow in direction of target var arrow = new Arrow(shootingBlock.colorType); arrow.x = shootingBlock.x; arrow.y = shootingBlock.y; arrow.setDirection(x, y); activeArrows.push(arrow); game.addChild(arrow); // Arrow is invisible, no spawn animation needed // Clear shooting state shootingBlock = null; clearSelection(); }; game.update = function () { // Update all active arrows for (var i = activeArrows.length - 1; i >= 0; i--) { var arrow = activeArrows[i]; if (arrow.update) { arrow.update(); } } }; // Initialize the game initializeGrid(); updateUI();
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var Arrow = Container.expand(function (colorType) {
var self = Container.call(this);
self.colorType = colorType;
self.directionX = 0;
self.directionY = 0;
self.speed = 400;
self.isDestroyed = false;
self.hasProcessedBlocks = false;
self.connectedBlocks = [];
// Create arrow graphics - long white arrow (invisible)
var arrowGraphics = self.attachAsset('arrow', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 0.8,
alpha: 0
});
self.setDirection = function (targetX, targetY) {
// Calculate normalized direction vector
var dx = targetX - self.x;
var dy = targetY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
self.directionX = dx / distance;
self.directionY = dy / distance;
arrowGraphics.rotation = Math.atan2(dy, dx);
}
};
self.update = function () {
if (self.isDestroyed) {
return;
}
// Check if we have enough connected blocks to stop
if (self.connectedBlocks && self.connectedBlocks.length >= 3 && !self.hasProcessedBlocks) {
self.hasProcessedBlocks = true;
// Process connected blocks immediately
clearSelection();
selectedBlocks = self.connectedBlocks;
blastSelectedBlocks();
// Keep arrow visible for a moment before destroying
LK.setTimeout(function () {
self.destroyArrow();
}, 500);
return;
}
// Move in straight line using direction vector only if we haven't processed blocks yet
// Stop moving if we have any connected blocks to keep arrow visible
if (!self.hasProcessedBlocks && (!self.connectedBlocks || self.connectedBlocks.length === 0)) {
self.x += self.directionX * self.speed * (1 / 60);
self.y += self.directionY * self.speed * (1 / 60);
}
// Arrow is invisible, no pulsing effect needed
// Check collision with blocks only if we haven't processed blocks yet
if (!self.hasProcessedBlocks) {
self.checkBlockCollision();
}
// Remove arrow if it goes off screen
if (self.x < -200 || self.x > 2248 || self.y < -200 || self.y > 2932) {
// Only destroy if we haven't processed blocks or if no blocks were connected
if (!self.hasProcessedBlocks) {
// Clear selection for any connected blocks
if (self.connectedBlocks && self.connectedBlocks.length > 0) {
for (var i = 0; i < self.connectedBlocks.length; i++) {
self.connectedBlocks[i].setSelected(false);
}
}
self.destroyArrow();
}
}
};
self.connectAdjacentBlocks = function (centerBlock) {
if (!centerBlock || centerBlock.isDestroyed) {
return;
}
var directions = [[0, -1], [0, 1], [-1, 0], [1, 0]]; // up, down, left, right
for (var i = 0; i < directions.length; i++) {
var newX = centerBlock.gridX + directions[i][0];
var newY = centerBlock.gridY + directions[i][1];
if (newX >= 0 && newX < GRID_WIDTH && newY >= 0 && newY < GRID_HEIGHT) {
var adjacentBlock = grid[newX][newY];
if (adjacentBlock && adjacentBlock.colorType === self.colorType && !adjacentBlock.isDestroyed) {
// Check if we already connected this block
var alreadyConnected = false;
for (var j = 0; j < self.connectedBlocks.length; j++) {
if (self.connectedBlocks[j] === adjacentBlock) {
alreadyConnected = true;
break;
}
}
if (!alreadyConnected) {
self.connectedBlocks.push(adjacentBlock);
adjacentBlock.setSelected(true);
// Recursively connect blocks adjacent to this one
self.connectAdjacentBlocks(adjacentBlock);
}
}
}
}
};
self.checkBlockCollision = function () {
var gridX = Math.floor((self.x - GRID_START_X) / BLOCK_SIZE);
var gridY = Math.floor((self.y - GRID_START_Y) / BLOCK_SIZE);
if (gridX >= 0 && gridX < GRID_WIDTH && gridY >= 0 && gridY < GRID_HEIGHT) {
var hitBlock = grid[gridX][gridY];
if (hitBlock && hitBlock.colorType === self.colorType && !hitBlock.isDestroyed) {
// Mark this block as connected by the arrow
if (!self.connectedBlocks) {
self.connectedBlocks = [];
}
// Check if we already connected this block
var alreadyConnected = false;
for (var i = 0; i < self.connectedBlocks.length; i++) {
if (self.connectedBlocks[i] === hitBlock) {
alreadyConnected = true;
break;
}
}
if (!alreadyConnected) {
self.connectedBlocks.push(hitBlock);
// Visual feedback for connected block
hitBlock.setSelected(true);
// Connect all adjacent blocks of the same color
self.connectAdjacentBlocks(hitBlock);
// If we have less than 3 blocks after connecting, set a timeout to destroy arrow
if (self.connectedBlocks.length < 3) {
LK.setTimeout(function () {
if (!self.isDestroyed && !self.hasProcessedBlocks) {
// Clear selection for connected blocks
for (var i = 0; i < self.connectedBlocks.length; i++) {
self.connectedBlocks[i].setSelected(false);
}
self.destroyArrow();
}
}, 1500); // Give 1.5 seconds to potentially connect more blocks
}
}
}
}
};
self.destroyArrow = function () {
if (!self.isDestroyed) {
self.isDestroyed = true;
self.destroy();
var index = activeArrows.indexOf(self);
if (index !== -1) {
activeArrows.splice(index, 1);
}
}
};
return self;
});
var Block = Container.expand(function (colorType) {
var self = Container.call(this);
self.colorType = colorType;
self.gridX = 0;
self.gridY = 0;
self.isSelected = false;
self.isDragging = false;
self.originalX = 0;
self.originalY = 0;
self.isDestroyed = false;
var blockAssets = ['blockRed', 'blockBlue', 'blockGreen', 'blockYellow', 'blockPurple'];
var blockGraphics = self.attachAsset(blockAssets[colorType], {
anchorX: 0.5,
anchorY: 0.5
});
self.setSelected = function (selected) {
self.isSelected = selected;
if (selected) {
blockGraphics.alpha = 0.8;
blockGraphics.scaleX = 1.05;
blockGraphics.scaleY = 1.05;
tween(blockGraphics, {
tint: 0xFFFFAA
}, {
duration: 200
});
} else {
blockGraphics.alpha = 1.0;
blockGraphics.scaleX = 1.0;
blockGraphics.scaleY = 1.0;
tween(blockGraphics, {
tint: 0xFFFFFF
}, {
duration: 200
});
}
};
self.blast = function () {
if (self.isDestroyed) {
return;
}
self.isDestroyed = true;
// No individual point effect - will be handled by blastSelectedBlocks
tween(blockGraphics, {
scaleX: 1.5,
scaleY: 1.5,
alpha: 0
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
self.destroy();
}
});
};
return self;
});
var PointEffect = Container.expand(function (points, x, y) {
var self = Container.call(this);
self.x = x;
self.y = y;
// Randomly choose between point effect assets
var effectAssets = ['pointEffect', 'pointEffect2'];
var randomAsset = effectAssets[Math.floor(Math.random() * effectAssets.length)];
// Create point effect graphics - bigger for combo effects
var effectGraphics = self.attachAsset(randomAsset, {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.1,
scaleY: 0.1,
alpha: 0
});
// No text needed - just visual effect
// Appear with bigger effect for combos
var maxScale = Math.min(3.0, 1.0 + points / 500);
tween(effectGraphics, {
scaleX: maxScale,
scaleY: maxScale,
alpha: 1.0
}, {
duration: 300,
easing: tween.bounceOut,
onFinish: function onFinish() {
// Move up and fade out
tween(self, {
y: self.y - 150
}, {
duration: 1000,
easing: tween.easeOut
});
tween(effectGraphics, {
alpha: 0,
scaleX: maxScale * 1.5,
scaleY: maxScale * 1.5
}, {
duration: 1000,
easing: tween.easeOut
});
// Destroy effect after animation completes
LK.setTimeout(function () {
self.destroy();
}, 1000);
}
});
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x2c3e50
});
/****
* Game Code
****/
var GRID_WIDTH = 8;
var GRID_HEIGHT = 10;
var BLOCK_SIZE = 200;
var GRID_START_X = (2048 - GRID_WIDTH * BLOCK_SIZE) / 2;
var GRID_START_Y = 400;
var grid = [];
var selectedBlocks = [];
var currentLevel = 1;
var currentScore = 0;
var targetScore = 1000;
var gameActive = true;
var activeArrows = [];
var shootingBlock = null;
// Initialize grid array
for (var x = 0; x < GRID_WIDTH; x++) {
grid[x] = [];
for (var y = 0; y < GRID_HEIGHT; y++) {
grid[x][y] = null;
}
}
// Add white background - bigger scale to fill more screen
var background = game.attachAsset('background', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0,
scaleX: 1.2,
scaleY: 1.2
});
// UI Elements
// Add background shape for score text
var scoreBackground = game.attachAsset('scoreBackground', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 180,
alpha: 0.8
});
var scoreText = new Text2('Score: 0', {
size: 80,
fill: 0x000000
});
scoreText.anchor.set(0.5, 0.5);
scoreText.x = 1024;
scoreText.y = 180;
game.addChild(scoreText);
// Add background shape for target text
var targetBackground = game.attachAsset('targetBackground', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 280,
alpha: 0.8
});
var targetText = new Text2('Target: 1000', {
size: 70,
fill: 0x0066CC
});
targetText.anchor.set(0.5, 0.5);
targetText.x = 1024;
targetText.y = 280;
game.addChild(targetText);
// Add background shape for level text
var levelBackground = game.attachAsset('levelBackground', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 100,
alpha: 0.8
});
var levelText = new Text2('Level 1', {
size: 90,
fill: 0x000000
});
levelText.anchor.set(0.5, 0.5);
levelText.x = 1024;
levelText.y = 100;
game.addChild(levelText);
function createBlock(x, y) {
var colorType = Math.floor(Math.random() * 5);
var block = new Block(colorType);
block.gridX = x;
block.gridY = y;
block.x = GRID_START_X + x * BLOCK_SIZE + BLOCK_SIZE / 2;
block.y = GRID_START_Y + y * BLOCK_SIZE + BLOCK_SIZE / 2;
grid[x][y] = block;
game.addChild(block);
return block;
}
function initializeGrid() {
for (var x = 0; x < GRID_WIDTH; x++) {
for (var y = 0; y < GRID_HEIGHT; y++) {
createBlock(x, y);
}
}
}
function getConnectedBlocks(startX, startY, colorType, visited) {
if (!visited) {
visited = [];
}
var key = startX + ',' + startY;
if (visited.indexOf(key) !== -1) {
return [];
}
if (startX < 0 || startX >= GRID_WIDTH || startY < 0 || startY >= GRID_HEIGHT) {
return [];
}
if (!grid[startX][startY] || grid[startX][startY].colorType !== colorType) {
return [];
}
visited.push(key);
var connected = [grid[startX][startY]];
// Check adjacent blocks (up, down, left, right)
var directions = [[0, -1], [0, 1], [-1, 0], [1, 0]];
for (var i = 0; i < directions.length; i++) {
var newX = startX + directions[i][0];
var newY = startY + directions[i][1];
var adjacentBlocks = getConnectedBlocks(newX, newY, colorType, visited);
connected = connected.concat(adjacentBlocks);
}
return connected;
}
function clearSelection() {
for (var i = 0; i < selectedBlocks.length; i++) {
selectedBlocks[i].setSelected(false);
}
selectedBlocks = [];
}
function selectBlockGroup(block) {
clearSelection();
selectedBlocks = getConnectedBlocks(block.gridX, block.gridY, block.colorType);
if (selectedBlocks.length >= 3) {
for (var i = 0; i < selectedBlocks.length; i++) {
selectedBlocks[i].setSelected(true);
}
return true;
} else {
selectedBlocks = [];
return false;
}
}
function blastSelectedBlocks() {
if (selectedBlocks.length < 3 || !gameActive) {
return;
}
var points = calculateScore(selectedBlocks.length);
currentScore += points;
LK.getSound('blockBlast').play();
LK.effects.flashScreen(0xffffff, 200);
// Calculate center position of all selected blocks for big effect
var centerX = 0;
var centerY = 0;
for (var i = 0; i < selectedBlocks.length; i++) {
centerX += selectedBlocks[i].x;
centerY += selectedBlocks[i].y;
}
centerX /= selectedBlocks.length;
centerY /= selectedBlocks.length;
// Create single big point effect at center
var bigPointEffect = new PointEffect(points, centerX, centerY);
game.addChild(bigPointEffect);
// Remove blocks from grid and blast them
for (var i = 0; i < selectedBlocks.length; i++) {
var block = selectedBlocks[i];
grid[block.gridX][block.gridY] = null;
block.blast();
}
selectedBlocks = [];
// Apply gravity after a short delay
LK.setTimeout(function () {
applyGravity();
updateUI();
checkGameState();
}, 400);
}
function calculateScore(blockCount) {
return blockCount * blockCount * 10;
}
function applyGravity() {
for (var x = 0; x < GRID_WIDTH; x++) {
var writeY = GRID_HEIGHT - 1;
// Move existing blocks down
for (var y = GRID_HEIGHT - 1; y >= 0; y--) {
if (grid[x][y] !== null) {
if (writeY !== y) {
grid[x][writeY] = grid[x][y];
grid[x][y] = null;
grid[x][writeY].gridY = writeY;
// Animate block falling
var targetY = GRID_START_Y + writeY * BLOCK_SIZE + BLOCK_SIZE / 2;
tween(grid[x][writeY], {
y: targetY
}, {
duration: 300,
easing: tween.easeOut
});
}
writeY--;
}
}
// Fill empty spaces at top with new blocks
for (var y = writeY; y >= 0; y--) {
var newBlock = createBlock(x, y);
newBlock.y = GRID_START_Y - (writeY - y + 1) * BLOCK_SIZE + BLOCK_SIZE / 2;
var targetY = GRID_START_Y + y * BLOCK_SIZE + BLOCK_SIZE / 2;
tween(newBlock, {
y: targetY
}, {
duration: 500,
easing: tween.bounceOut
});
}
}
}
function updateUI() {
scoreText.setText('Score: ' + currentScore);
LK.setScore(currentScore);
}
function checkGameState() {
if (currentScore >= targetScore) {
gameActive = false;
LK.getSound('levelComplete').play();
LK.setTimeout(function () {
nextLevel();
}, 1000);
}
}
function nextLevel() {
currentLevel++;
targetScore = currentScore + 500 * currentLevel;
gameActive = true;
levelText.setText('Level ' + currentLevel);
targetText.setText('Target: ' + targetScore);
updateUI();
if (currentLevel >= 10) {
LK.showYouWin();
}
}
game.down = function (x, y, obj) {
if (!gameActive) {
return;
}
var gridX = Math.floor((x - GRID_START_X) / BLOCK_SIZE);
var gridY = Math.floor((y - GRID_START_Y) / BLOCK_SIZE);
if (gridX >= 0 && gridX < GRID_WIDTH && gridY >= 0 && gridY < GRID_HEIGHT) {
var block = grid[gridX][gridY];
if (block) {
shootingBlock = block;
}
}
};
game.move = function (x, y, obj) {
// No dragging mechanics needed for arrow shooting
};
game.up = function (x, y, obj) {
if (!gameActive || !shootingBlock) {
return;
}
// Shoot arrow in direction of target
var arrow = new Arrow(shootingBlock.colorType);
arrow.x = shootingBlock.x;
arrow.y = shootingBlock.y;
arrow.setDirection(x, y);
activeArrows.push(arrow);
game.addChild(arrow);
// Arrow is invisible, no spawn animation needed
// Clear shooting state
shootingBlock = null;
clearSelection();
};
game.update = function () {
// Update all active arrows
for (var i = activeArrows.length - 1; i >= 0; i--) {
var arrow = activeArrows[i];
if (arrow.update) {
arrow.update();
}
}
};
// Initialize the game
initializeGrid();
updateUI();