/**** * Classes ****/ var Block = Container.expand(function (board) { var self = Container.call(this); var hsvToRgb = function hsvToRgb(h, s, v) { var r, g, b; var i = Math.floor(h * 6); var f = h * 6 - i; var p = v * (1 - s); var q = v * (1 - f * s); var t = v * (1 - (1 - f) * s); switch (i % 6) { case 0: r = v, g = t, b = p; break; case 1: r = q, g = v, b = p; break; case 2: r = p, g = v, b = t; break; case 3: r = p, g = q, b = v; break; case 4: r = t, g = p, b = v; break; case 5: r = v, g = p, b = q; break; } return (Math.round(r * 255) << 16) + (Math.round(g * 255) << 8) + Math.round(b * 255); }; var ShapeTypes = { SINGLE: [[1]], TRI: [[1, 1, 1]], QUAD: [[1, 1, 1, 1]], LSHAPE: [[1, 0, 0], [1, 0, 0], [1, 1, 1]], BLOCK: [[1, 1], [1, 1]], SMALLLSHAPE: [[1, 0], [1, 1]] }; var shapes = Object.values(ShapeTypes); var offset = Math.floor(Math.random() * shapes.length); self.shape = shapes[offset]; var hue = offset % shapes.length / shapes.length; var brightness = 0.8; // Default brightness // Adjust hue for bordeaux color - shift red (hue 0) towards purple-red if (offset === 0) { hue = 0.92; // Bordeaux hue (closer to purple-red) brightness = 0.6; // Make original colored blocks darker } // Make green blocks darker (green is around hue 0.33) if (hue >= 0.25 && hue <= 0.41) { brightness = 0.5; // Make green blocks darker } // Make L-shaped blocks darker if (self.shape === ShapeTypes.LSHAPE) { brightness = 0.5; // Make L-shaped blocks darker } // Make quad blocks dark blue if (self.shape === ShapeTypes.QUAD) { hue = 0.67; // Blue hue brightness = 0.55; // Brightened dark blue } self.color = hsvToRgb(hue, 0.7, brightness); self.rotateShapeRandomly = function () { var rotations = Math.floor(Math.random() * 4); for (var r = 0; r < rotations; r++) { self.shape = self.shape[0].map(function (val, index) { return self.shape.map(function (row) { return row[index]; }).reverse(); }); } }; self.rotateShapeRandomly(); self.blocks = []; var background = self.attachAsset('background', { anchorX: 0.5, anchorY: 0.5 }); background.alpha = 0; var blockSize = 160; background.width = 4 * blockSize; background.height = 4 * blockSize; self.addChild(background); self.offsetX = 0; self.offsetY = 0; var blockOffsetX = (background.width / 2 - self.shape[0].length * blockSize) / 2 - blockSize / 2; var blockOffsetY = (background.height / 2 - self.shape.length * blockSize) / 2 - blockSize / 2; for (var i = 0; i < self.shape.length; i++) { for (var j = 0; j < self.shape[i].length; j++) { if (self.shape[i][j] === 1) { var block = self.attachAsset('block', { anchorX: 0.5, anchorY: 0.5 }); block.tint = self.color; block.width = blockSize; block.height = blockSize; block.x = j * blockSize + blockOffsetX; block.y = i * blockSize + blockOffsetY; self.blocks.push(block); self.addChild(block); } } } self.startX = 0; self.startY = 0; self.moveTowardsHomePosition = function () { var dx = self.startX - self.x; var dy = self.startY - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance > 1) { self.x += dx * 0.3; self.y += dy * 0.3; } else { self.x = self.startX; self.y = self.startY; } }; var currentX = 0; var currentY = 0; self.moveToDragTarget = function () { var ox = -this.targetX; var oy = (LK.is.mobile ? 400 : 0) - this.targetY; this.targetX += ox / 5; this.targetY += oy / 5; this.x = currentX - this.targetX; this.y = currentY - this.targetY; }; self._move_migrated = function (x, y) { currentX = x; currentY = y; self.x = x - this.targetX; self.y = y - this.targetY; }; self.setStartPosition = function (x, y) { self.startX = x; self.startY = y; }; self.getOverlappingCells = function () { var cells = []; var boardPos = { x: -board.x + self.x + 160 * 4 + blockOffsetX + 160, y: -board.y + self.y + 160 * 4 + blockOffsetY + 160 }; var startX = Math.floor(boardPos.x / 160); var startY = Math.floor(boardPos.y / 160); for (var i = 0; i < self.shape.length; i++) { for (var j = 0; j < self.shape[i].length; j++) { if (self.shape[i][j] === 1) { var cell = board.grid && board.grid[startY + i] && board.grid[startY + i][startX + j]; if (cell && !cell.filled) { cells.push(cell); } else { return null; } } } } return cells; }; self.showOverlap = function () { var cells = self.getOverlappingCells(); if (cells) { for (var a = 0; a < cells.length; a++) { var cell = cells[a]; cell.setTint(self.color); } } }; self.rotateShapeRandomly = function () { var rotations = Math.floor(Math.random() * 4); for (var r = 0; r < rotations; r++) { self.shape = self.shape[0].map(function (val, index) { return self.shape.map(function (row) { return row[index]; }).reverse(); }); } }; }); var Board = Container.expand(function () { var self = Container.call(this); self.particles = []; Board.prototype.spawnParticles = function (x, y, tint) { for (var i = 0; i < 10; i++) { var particle = new Particle(tint); particle.x = x; particle.y = y; this.particles.push(particle); this.addChild(particle); } }; var background = self.attachAsset('background', { anchorX: 0.5, anchorY: 0.5 }); background.y = -20; background.alpha = .4; background.blendMode = 1; self.grid = new Array(10).fill(null).map(function () { return new Array(10).fill(null); }); var size = 158; var totalWidth = 10 * size; var totalHeight = 10 * size; for (var i = 0; i < 10; i++) { for (var j = 0; j < 10; j++) { var cell = new Cell(); cell.x = i * size - totalWidth / 2 + size / 2; cell.y = j * size - totalHeight / 2 + size / 2; self.grid[j][i] = cell; self.addChild(cell); } } self.removeTint = function () { for (var i = 0; i < 10; i++) { for (var j = 0; j < 10; j++) { if (!self.grid[i][j].filled) { self.grid[i][j].setTint(0xffffff); } } } }; self.checkLines = function () { var rowsRemoved = 0; for (var i = 0; i < 10; i++) { var rowFilled = true; var colFilled = true; for (var j = 0; j < 10; j++) { if (!self.grid[i][j].filled) { rowFilled = false; } if (!self.grid[j][i].filled) { colFilled = false; } } if (rowFilled || colFilled) { // Play elimination sound LK.getSound('elimination').play(); rowsRemoved += (rowFilled ? 1 : 0) + (colFilled ? 1 : 0); for (var j = 0; j < 10; j++) { if (rowFilled) { // Use the tint of the filled block for the particle color var particleTint = self.grid[i][j].getTint(); self.grid[i][j].setFill(false); self.spawnParticles(self.grid[i][j].x, self.grid[i][j].y, particleTint); } if (colFilled) { // Use the tint of the filled block for the particle color var particleTint = self.grid[j][i].getTint(); self.grid[j][i].setFill(false); self.spawnParticles(self.grid[j][i].x, self.grid[j][i].y, particleTint); } } } } return rowsRemoved; }; self.tick = function () { for (var i = self.particles.length - 1; i >= 0; i--) { var particle = self.particles[i]; if (particle) { particle.tick(); if (particle.alpha <= 0) { self.particles.splice(i, 1); } } } }; self.placeBlock = function () {}; }); var Cell = Container.expand(function () { var self = Container.call(this); self.filled = false; var empty = self.attachAsset('cell', { anchorX: 0.5, anchorY: 0.5 }); empty.alpha = .8; var filled = self.attachAsset('block', { anchorX: 0.5, anchorY: 0.5 }); empty.y = 2; self.setFill = function (isFilled) { self.filled = isFilled; empty.visible = !self.filled; filled.visible = self.filled; }; self.getTint = function () { return filled.tint; }; self.setTint = function (tint) { empty.tint = filled.tint = tint; }; self.setFill(false); }); var Particle = Container.expand(function (tint) { var self = Container.call(this); // Utility: Convert RGB to HSV function rgbToHsv(tint) { var r = (tint >> 16 & 0xFF) / 255; var g = (tint >> 8 & 0xFF) / 255; var b = (tint & 0xFF) / 255; var max = Math.max(r, g, b), min = Math.min(r, g, b); var h, s, v = max; var d = max - min; s = max === 0 ? 0 : d / max; if (max === min) { h = 0; } else { switch (max) { case r: h = (g - b) / d + (g < b ? 6 : 0); break; case g: h = (b - r) / d + 2; break; case b: h = (r - g) / d + 4; break; } h /= 6; } return [h, s, v]; } // Utility: Convert HSV to RGB function hsvToRgb(h, s, v) { var r, g, b; var i = Math.floor(h * 6); var f = h * 6 - i; var p = v * (1 - s); var q = v * (1 - f * s); var t = v * (1 - (1 - f) * s); switch (i % 6) { case 0: r = v, g = t, b = p; break; case 1: r = q, g = v, b = p; break; case 2: r = p, g = v, b = t; break; case 3: r = p, g = q, b = v; break; case 4: r = t, g = p, b = v; break; case 5: r = v, g = p, b = q; break; } return (Math.round(r * 255) << 16) + (Math.round(g * 255) << 8) + Math.round(b * 255); } // Make the particle color darker by reducing brightness var hsv = rgbToHsv(tint); hsv[2] = Math.max(0, hsv[2] * 0.5); // Reduce brightness by 50% var darkTint = hsvToRgb(hsv[0], hsv[1], hsv[2]); self.tint = darkTint; var particleGraphics = self.attachAsset('particle', { anchorX: 0.5, anchorY: 0.5 }); particleGraphics.rotation = Math.random() * Math.PI * 2; // Set the particle asset color to match the eliminated block's color, but darker particleGraphics.tint = darkTint; self.vx = Math.random() * 4 - 2; self.vy = Math.random() * 4 - 2; self.alpha = 1; self.lifetime = 60; self.tick = function () { self.x += self.vx; self.y += self.vy; self.alpha -= 1 / self.lifetime; if (self.alpha <= 0) { self.destroy(); } }; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x000000 }); /**** * Game Code ****/ var gameBackground = game.attachAsset('gameBackground', { anchorX: 0.5, anchorY: 0.5 }); gameBackground.x = 2048 / 2; gameBackground.y = 2732 / 2; var tilesBackground = game.attachAsset('tileBackground', { anchorX: 0.5, anchorY: 0.5 }); tilesBackground.y = -20; tilesBackground.alpha = .4; tilesBackground.blendMode = 1; var blocks = []; var dragTarget; var board = game.addChild(new Board()); board.x = 2048 / 2; board.y = 2732 / 2 - 250 + 30; tilesBackground.x = 2048 / 2; tilesBackground.y = 2732 - 300; var targetOffset; game.createBlock = function (index) { var block = new Block(board); block.x = 2048 / 2 + (index - 1) * (block.width + 30); block.y = 2732 + block.height; block.setStartPosition(block.x, 2732 - block.height / 2 - 30); blocks.push(block); game.addChild(block); block.on('down', function (x, y, obj) { dragTarget = this; var pos = this.toLocal(obj.global); var targetPos = game.toLocal(obj.global); this.targetX = pos.x; this.targetY = pos.y; dragTarget._move_migrated(targetPos.x, targetPos.y); }); }; game.on('move', function (x, y, obj) { if (dragTarget) { board.removeTint(); var pos = game.toLocal(obj.global); dragTarget._move_migrated(pos.x, pos.y); dragTarget.showOverlap(); } }); game.on('up', function (x, y, obj) { if (dragTarget) { var cells = dragTarget.getOverlappingCells(); if (cells) { for (var a = 0; a < cells.length; a++) { cells[a].setFill(true); cells[a].setTint(dragTarget.color); } blocks[blocks.indexOf(dragTarget)] = undefined; dragTarget.destroy(); if (!blocks.some(function (block) { return block; })) { game.createBlocks(); } var pointsToAdd = board.checkLines(); if (pointsToAdd) { // Count how many cells were just filled in this move var blocksEliminated = 0; for (var a = 0; a < cells.length; a++) { // Only count cells that were actually filled before this move if (cells[a].filled) { blocksEliminated++; } } // If blocksEliminated is 0 (shouldn't happen), fallback to cells.length if (blocksEliminated === 0) blocksEliminated = cells.length; // Multiply score by number of eliminations if more than one at once var multiplier = pointsToAdd > 1 ? pointsToAdd : 1; score += blocksEliminated * 10 * multiplier; scoreTxt.setText(score); // Give 1 refresh power-up if 2 line elimination is done in a single move if (pointsToAdd === 2) { if (typeof game.refreshPowerUps === "undefined") { game.refreshPowerUps = 0; } game.refreshPowerUps += 1; updateRefreshPowerupUI(); // Optionally, show a message or animation for the power-up here } // Give 1 bomb block if 3 line elimination is done in a single move if (pointsToAdd === 3) { if (typeof game.bombBlocks === "undefined") { game.bombBlocks = 0; } game.bombBlocks += 1; updateBombPowerupUI && updateBombPowerupUI(); // Optionally, update UI or show animation for bomb block here } // If more than 3 eliminations, give 1 bomb and +1 refresh for each extra elimination above 3 if (pointsToAdd > 3) { if (typeof game.bombBlocks === "undefined") { game.bombBlocks = 0; } if (typeof game.refreshPowerUps === "undefined") { game.refreshPowerUps = 0; } var extra = pointsToAdd - 3; game.bombBlocks += 1; game.refreshPowerUps += extra; updateBombPowerupUI && updateBombPowerupUI(); updateRefreshPowerupUI && updateRefreshPowerupUI(); // Optionally, show a message or animation for the extra power-ups here } } } board.removeTint(); dragTarget = undefined; // --- Refresh power-up logic --- // If player has refresh power-ups, allow refreshing the 3 blocks at the background if (typeof game.refreshPowerUps !== "undefined" && game.refreshPowerUps > 0) { // Example: call this function from a button or gesture in your UI game.useRefreshPowerUp = function () { if (game.refreshPowerUps > 0) { // Remove current blocks for (var i = 0; i < blocks.length; i++) { if (blocks[i]) { blocks[i].destroy(); blocks[i] = undefined; } } // Create new blocks game.createBlocks(); game.refreshPowerUps -= 1; updateRefreshPowerupUI(); // Optionally, update UI to show remaining power-ups } }; } // --- Bomb power-up logic --- game.activateBombPowerUp = function () { if (typeof game.bombBlocks === "undefined" || game.bombBlocks <= 0) return; // Set bomb mode active game.bombMode = true; // Optionally, visually indicate bomb mode (e.g. flash board or show message) // Next cell click on board will trigger bomb board.on('down', function (x, y, obj) { if (!game.bombMode) return; // Calculate the exact cell under the click, not just the nearest cell // Use board.toLocal to get the click position relative to the board var localPos = board.toLocal(obj.global); var size = 158; var targetJ = Math.round(localPos.x / size + 10 / 2 - 0.5); var targetI = Math.round(localPos.y / size + 10 / 2 - 0.5); // Only clear the 3x3 area centered on the exact cell clicked, and nowhere else if (targetI >= 0 && targetI < 10 && targetJ >= 0 && targetJ < 10) { for (var di = -1; di <= 1; di++) { for (var dj = -1; dj <= 1; dj++) { var ni = targetI + di; var nj = targetJ + dj; if (ni >= 0 && ni < 10 && nj >= 0 && nj < 10) { var cell = board.grid[ni][nj]; // Only clear cells in this 3x3 area, nowhere else var tint = cell.getTint(); cell.setFill(false); // Immediately reset tint to default so no hover/active color remains cell.setTint(0xffffff); board.spawnParticles(cell.x, cell.y, tint); } } } // Play elimination sound LK.getSound('elimination').play(); // After bomb clears, ensure all cells in 3x3 area have default tint (no hover effect) for (var di = -1; di <= 1; di++) { for (var dj = -1; dj <= 1; dj++) { var ni = targetI + di; var nj = targetJ + dj; if (ni >= 0 && ni < 10 && nj >= 0 && nj < 10) { var cell = board.grid[ni][nj]; cell.setTint(0xffffff); } } } game.bombBlocks -= 1; updateBombPowerupUI && updateBombPowerupUI(); game.bombMode = false; // Reset bomb icon color to normal if (typeof bombIcon !== "undefined") { bombIcon.tint = 0xffffff; } // Remove this event handler after use board.off('down'); } }); }; } }); game.createBlocks = function () { for (var i = 0; i < 3; i++) { game.createBlock(i); } }; var score = 0; game.createBlocks(); LK.playMusic('gamemusic'); var scoreTxt = new Text2('0', { size: 150, fill: 0xFFFFFF, font: 'Impact', dropShadow: true, dropShadowColor: '#2a636e' }); scoreTxt.anchor.set(.5, 0); LK.gui.top.addChild(scoreTxt); // --- Power-up indicator at top right --- var powerupContainer = new Container(); powerupContainer.x = -180; // Move more to the left from right edge powerupContainer.y = 30; // Offset from top edge // Add refresh icon var refreshIcon = LK.getAsset('refresh', { anchorX: 0.5, anchorY: 0.5 }); refreshIcon.width = 80; refreshIcon.height = 80; refreshIcon.x = 0; refreshIcon.y = 0; powerupContainer.addChild(refreshIcon); // Make refresh icon clickable to spend refresh power-up refreshIcon.on('down', function (x, y, obj) { if (typeof game.useRefreshPowerUp === "function") { game.useRefreshPowerUp(); } }); // Add bomb icon using Bomb asset var bombIcon = LK.getAsset('Bomb', { anchorX: 0.5, anchorY: 0.5 }); bombIcon.width = 80; bombIcon.height = 80; bombIcon.x = 0; bombIcon.y = 100; powerupContainer.addChild(bombIcon); // Make bomb icon clickable to toggle bomb mode and color bombIcon.on('down', function (x, y, obj) { // Only allow toggling if player has at least 1 bomb if (typeof game.bombBlocks === "undefined" || game.bombBlocks <= 0) return; // Toggle bomb mode game.bombMode = !game.bombMode; // Change bomb icon color to indicate mode if (game.bombMode) { // Bomb ON: tint to yellow bombIcon.tint = 0xffff00; // Activate bomb power-up (attach board click handler) if (typeof game.activateBombPowerUp === "function") { game.activateBombPowerUp(); } } else { // Bomb OFF: reset tint bombIcon.tint = 0xffffff; // Remove any board click handler if present if (typeof board.off === "function") { board.off('down'); } } }); // Add refresh count text var refreshCountTxt = new Text2('0', { size: 80, fill: 0xffffff, font: 'Impact', dropShadow: true, dropShadowColor: '#2a636e' }); refreshCountTxt.anchor.set(0, 0.5); refreshCountTxt.x = 50; refreshCountTxt.y = 0; powerupContainer.addChild(refreshCountTxt); // Add bomb count text var bombCountTxt = new Text2('0', { size: 80, fill: 0xffffff, font: 'Impact', dropShadow: true, dropShadowColor: '#2a636e' }); bombCountTxt.anchor.set(0, 0.5); bombCountTxt.x = 50; bombCountTxt.y = 100; powerupContainer.addChild(bombCountTxt); // Add to GUI topRight LK.gui.topRight.addChild(powerupContainer); // Helper to update refresh power-up count function updateRefreshPowerupUI() { var count = typeof game.refreshPowerUps !== "undefined" ? game.refreshPowerUps : 0; refreshCountTxt.setText('' + count); } updateRefreshPowerupUI(); // Helper to update bomb power-up count function updateBombPowerupUI() { var count = typeof game.bombBlocks !== "undefined" ? game.bombBlocks : 0; bombCountTxt.setText('' + count); } updateBombPowerupUI(); game.isMovePossible = function () { for (var a = 0; a < blocks.length; a++) { if (blocks[a]) { for (var i = 0; i < 10; i++) { for (var j = 0; j < 10; j++) { if (board.grid[i][j].filled) { continue; } var canPlace = true; for (var k = 0; k < blocks[a].shape.length; k++) { for (var l = 0; l < blocks[a].shape[k].length; l++) { if (blocks[a].shape[k][l] === 1) { if (i + k < 0 || i + k >= 10 || j + l < 0 || j + l >= 10 || board.grid[i + k][j + l].filled) { canPlace = false; break; } } } if (!canPlace) { break; } } if (canPlace) { return true; } } } } } return false; }; var isGameOver = false; LK.on('tick', function () { board.tick(); if (isGameOver || !game.isMovePossible()) { LK.effects.flashScreen(0xffffff, 1000); LK.showGameOver(); } for (var a = blocks.length - 1; a >= 0; a--) { if (blocks[a]) { if (blocks[a] != dragTarget) { blocks[a].moveTowardsHomePosition(); } else { blocks[a].moveToDragTarget(); } } } });
/****
* Classes
****/
var Block = Container.expand(function (board) {
var self = Container.call(this);
var hsvToRgb = function hsvToRgb(h, s, v) {
var r, g, b;
var i = Math.floor(h * 6);
var f = h * 6 - i;
var p = v * (1 - s);
var q = v * (1 - f * s);
var t = v * (1 - (1 - f) * s);
switch (i % 6) {
case 0:
r = v, g = t, b = p;
break;
case 1:
r = q, g = v, b = p;
break;
case 2:
r = p, g = v, b = t;
break;
case 3:
r = p, g = q, b = v;
break;
case 4:
r = t, g = p, b = v;
break;
case 5:
r = v, g = p, b = q;
break;
}
return (Math.round(r * 255) << 16) + (Math.round(g * 255) << 8) + Math.round(b * 255);
};
var ShapeTypes = {
SINGLE: [[1]],
TRI: [[1, 1, 1]],
QUAD: [[1, 1, 1, 1]],
LSHAPE: [[1, 0, 0], [1, 0, 0], [1, 1, 1]],
BLOCK: [[1, 1], [1, 1]],
SMALLLSHAPE: [[1, 0], [1, 1]]
};
var shapes = Object.values(ShapeTypes);
var offset = Math.floor(Math.random() * shapes.length);
self.shape = shapes[offset];
var hue = offset % shapes.length / shapes.length;
var brightness = 0.8; // Default brightness
// Adjust hue for bordeaux color - shift red (hue 0) towards purple-red
if (offset === 0) {
hue = 0.92; // Bordeaux hue (closer to purple-red)
brightness = 0.6; // Make original colored blocks darker
}
// Make green blocks darker (green is around hue 0.33)
if (hue >= 0.25 && hue <= 0.41) {
brightness = 0.5; // Make green blocks darker
}
// Make L-shaped blocks darker
if (self.shape === ShapeTypes.LSHAPE) {
brightness = 0.5; // Make L-shaped blocks darker
}
// Make quad blocks dark blue
if (self.shape === ShapeTypes.QUAD) {
hue = 0.67; // Blue hue
brightness = 0.55; // Brightened dark blue
}
self.color = hsvToRgb(hue, 0.7, brightness);
self.rotateShapeRandomly = function () {
var rotations = Math.floor(Math.random() * 4);
for (var r = 0; r < rotations; r++) {
self.shape = self.shape[0].map(function (val, index) {
return self.shape.map(function (row) {
return row[index];
}).reverse();
});
}
};
self.rotateShapeRandomly();
self.blocks = [];
var background = self.attachAsset('background', {
anchorX: 0.5,
anchorY: 0.5
});
background.alpha = 0;
var blockSize = 160;
background.width = 4 * blockSize;
background.height = 4 * blockSize;
self.addChild(background);
self.offsetX = 0;
self.offsetY = 0;
var blockOffsetX = (background.width / 2 - self.shape[0].length * blockSize) / 2 - blockSize / 2;
var blockOffsetY = (background.height / 2 - self.shape.length * blockSize) / 2 - blockSize / 2;
for (var i = 0; i < self.shape.length; i++) {
for (var j = 0; j < self.shape[i].length; j++) {
if (self.shape[i][j] === 1) {
var block = self.attachAsset('block', {
anchorX: 0.5,
anchorY: 0.5
});
block.tint = self.color;
block.width = blockSize;
block.height = blockSize;
block.x = j * blockSize + blockOffsetX;
block.y = i * blockSize + blockOffsetY;
self.blocks.push(block);
self.addChild(block);
}
}
}
self.startX = 0;
self.startY = 0;
self.moveTowardsHomePosition = function () {
var dx = self.startX - self.x;
var dy = self.startY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 1) {
self.x += dx * 0.3;
self.y += dy * 0.3;
} else {
self.x = self.startX;
self.y = self.startY;
}
};
var currentX = 0;
var currentY = 0;
self.moveToDragTarget = function () {
var ox = -this.targetX;
var oy = (LK.is.mobile ? 400 : 0) - this.targetY;
this.targetX += ox / 5;
this.targetY += oy / 5;
this.x = currentX - this.targetX;
this.y = currentY - this.targetY;
};
self._move_migrated = function (x, y) {
currentX = x;
currentY = y;
self.x = x - this.targetX;
self.y = y - this.targetY;
};
self.setStartPosition = function (x, y) {
self.startX = x;
self.startY = y;
};
self.getOverlappingCells = function () {
var cells = [];
var boardPos = {
x: -board.x + self.x + 160 * 4 + blockOffsetX + 160,
y: -board.y + self.y + 160 * 4 + blockOffsetY + 160
};
var startX = Math.floor(boardPos.x / 160);
var startY = Math.floor(boardPos.y / 160);
for (var i = 0; i < self.shape.length; i++) {
for (var j = 0; j < self.shape[i].length; j++) {
if (self.shape[i][j] === 1) {
var cell = board.grid && board.grid[startY + i] && board.grid[startY + i][startX + j];
if (cell && !cell.filled) {
cells.push(cell);
} else {
return null;
}
}
}
}
return cells;
};
self.showOverlap = function () {
var cells = self.getOverlappingCells();
if (cells) {
for (var a = 0; a < cells.length; a++) {
var cell = cells[a];
cell.setTint(self.color);
}
}
};
self.rotateShapeRandomly = function () {
var rotations = Math.floor(Math.random() * 4);
for (var r = 0; r < rotations; r++) {
self.shape = self.shape[0].map(function (val, index) {
return self.shape.map(function (row) {
return row[index];
}).reverse();
});
}
};
});
var Board = Container.expand(function () {
var self = Container.call(this);
self.particles = [];
Board.prototype.spawnParticles = function (x, y, tint) {
for (var i = 0; i < 10; i++) {
var particle = new Particle(tint);
particle.x = x;
particle.y = y;
this.particles.push(particle);
this.addChild(particle);
}
};
var background = self.attachAsset('background', {
anchorX: 0.5,
anchorY: 0.5
});
background.y = -20;
background.alpha = .4;
background.blendMode = 1;
self.grid = new Array(10).fill(null).map(function () {
return new Array(10).fill(null);
});
var size = 158;
var totalWidth = 10 * size;
var totalHeight = 10 * size;
for (var i = 0; i < 10; i++) {
for (var j = 0; j < 10; j++) {
var cell = new Cell();
cell.x = i * size - totalWidth / 2 + size / 2;
cell.y = j * size - totalHeight / 2 + size / 2;
self.grid[j][i] = cell;
self.addChild(cell);
}
}
self.removeTint = function () {
for (var i = 0; i < 10; i++) {
for (var j = 0; j < 10; j++) {
if (!self.grid[i][j].filled) {
self.grid[i][j].setTint(0xffffff);
}
}
}
};
self.checkLines = function () {
var rowsRemoved = 0;
for (var i = 0; i < 10; i++) {
var rowFilled = true;
var colFilled = true;
for (var j = 0; j < 10; j++) {
if (!self.grid[i][j].filled) {
rowFilled = false;
}
if (!self.grid[j][i].filled) {
colFilled = false;
}
}
if (rowFilled || colFilled) {
// Play elimination sound
LK.getSound('elimination').play();
rowsRemoved += (rowFilled ? 1 : 0) + (colFilled ? 1 : 0);
for (var j = 0; j < 10; j++) {
if (rowFilled) {
// Use the tint of the filled block for the particle color
var particleTint = self.grid[i][j].getTint();
self.grid[i][j].setFill(false);
self.spawnParticles(self.grid[i][j].x, self.grid[i][j].y, particleTint);
}
if (colFilled) {
// Use the tint of the filled block for the particle color
var particleTint = self.grid[j][i].getTint();
self.grid[j][i].setFill(false);
self.spawnParticles(self.grid[j][i].x, self.grid[j][i].y, particleTint);
}
}
}
}
return rowsRemoved;
};
self.tick = function () {
for (var i = self.particles.length - 1; i >= 0; i--) {
var particle = self.particles[i];
if (particle) {
particle.tick();
if (particle.alpha <= 0) {
self.particles.splice(i, 1);
}
}
}
};
self.placeBlock = function () {};
});
var Cell = Container.expand(function () {
var self = Container.call(this);
self.filled = false;
var empty = self.attachAsset('cell', {
anchorX: 0.5,
anchorY: 0.5
});
empty.alpha = .8;
var filled = self.attachAsset('block', {
anchorX: 0.5,
anchorY: 0.5
});
empty.y = 2;
self.setFill = function (isFilled) {
self.filled = isFilled;
empty.visible = !self.filled;
filled.visible = self.filled;
};
self.getTint = function () {
return filled.tint;
};
self.setTint = function (tint) {
empty.tint = filled.tint = tint;
};
self.setFill(false);
});
var Particle = Container.expand(function (tint) {
var self = Container.call(this);
// Utility: Convert RGB to HSV
function rgbToHsv(tint) {
var r = (tint >> 16 & 0xFF) / 255;
var g = (tint >> 8 & 0xFF) / 255;
var b = (tint & 0xFF) / 255;
var max = Math.max(r, g, b),
min = Math.min(r, g, b);
var h,
s,
v = max;
var d = max - min;
s = max === 0 ? 0 : d / max;
if (max === min) {
h = 0;
} else {
switch (max) {
case r:
h = (g - b) / d + (g < b ? 6 : 0);
break;
case g:
h = (b - r) / d + 2;
break;
case b:
h = (r - g) / d + 4;
break;
}
h /= 6;
}
return [h, s, v];
}
// Utility: Convert HSV to RGB
function hsvToRgb(h, s, v) {
var r, g, b;
var i = Math.floor(h * 6);
var f = h * 6 - i;
var p = v * (1 - s);
var q = v * (1 - f * s);
var t = v * (1 - (1 - f) * s);
switch (i % 6) {
case 0:
r = v, g = t, b = p;
break;
case 1:
r = q, g = v, b = p;
break;
case 2:
r = p, g = v, b = t;
break;
case 3:
r = p, g = q, b = v;
break;
case 4:
r = t, g = p, b = v;
break;
case 5:
r = v, g = p, b = q;
break;
}
return (Math.round(r * 255) << 16) + (Math.round(g * 255) << 8) + Math.round(b * 255);
}
// Make the particle color darker by reducing brightness
var hsv = rgbToHsv(tint);
hsv[2] = Math.max(0, hsv[2] * 0.5); // Reduce brightness by 50%
var darkTint = hsvToRgb(hsv[0], hsv[1], hsv[2]);
self.tint = darkTint;
var particleGraphics = self.attachAsset('particle', {
anchorX: 0.5,
anchorY: 0.5
});
particleGraphics.rotation = Math.random() * Math.PI * 2;
// Set the particle asset color to match the eliminated block's color, but darker
particleGraphics.tint = darkTint;
self.vx = Math.random() * 4 - 2;
self.vy = Math.random() * 4 - 2;
self.alpha = 1;
self.lifetime = 60;
self.tick = function () {
self.x += self.vx;
self.y += self.vy;
self.alpha -= 1 / self.lifetime;
if (self.alpha <= 0) {
self.destroy();
}
};
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000000
});
/****
* Game Code
****/
var gameBackground = game.attachAsset('gameBackground', {
anchorX: 0.5,
anchorY: 0.5
});
gameBackground.x = 2048 / 2;
gameBackground.y = 2732 / 2;
var tilesBackground = game.attachAsset('tileBackground', {
anchorX: 0.5,
anchorY: 0.5
});
tilesBackground.y = -20;
tilesBackground.alpha = .4;
tilesBackground.blendMode = 1;
var blocks = [];
var dragTarget;
var board = game.addChild(new Board());
board.x = 2048 / 2;
board.y = 2732 / 2 - 250 + 30;
tilesBackground.x = 2048 / 2;
tilesBackground.y = 2732 - 300;
var targetOffset;
game.createBlock = function (index) {
var block = new Block(board);
block.x = 2048 / 2 + (index - 1) * (block.width + 30);
block.y = 2732 + block.height;
block.setStartPosition(block.x, 2732 - block.height / 2 - 30);
blocks.push(block);
game.addChild(block);
block.on('down', function (x, y, obj) {
dragTarget = this;
var pos = this.toLocal(obj.global);
var targetPos = game.toLocal(obj.global);
this.targetX = pos.x;
this.targetY = pos.y;
dragTarget._move_migrated(targetPos.x, targetPos.y);
});
};
game.on('move', function (x, y, obj) {
if (dragTarget) {
board.removeTint();
var pos = game.toLocal(obj.global);
dragTarget._move_migrated(pos.x, pos.y);
dragTarget.showOverlap();
}
});
game.on('up', function (x, y, obj) {
if (dragTarget) {
var cells = dragTarget.getOverlappingCells();
if (cells) {
for (var a = 0; a < cells.length; a++) {
cells[a].setFill(true);
cells[a].setTint(dragTarget.color);
}
blocks[blocks.indexOf(dragTarget)] = undefined;
dragTarget.destroy();
if (!blocks.some(function (block) {
return block;
})) {
game.createBlocks();
}
var pointsToAdd = board.checkLines();
if (pointsToAdd) {
// Count how many cells were just filled in this move
var blocksEliminated = 0;
for (var a = 0; a < cells.length; a++) {
// Only count cells that were actually filled before this move
if (cells[a].filled) {
blocksEliminated++;
}
}
// If blocksEliminated is 0 (shouldn't happen), fallback to cells.length
if (blocksEliminated === 0) blocksEliminated = cells.length;
// Multiply score by number of eliminations if more than one at once
var multiplier = pointsToAdd > 1 ? pointsToAdd : 1;
score += blocksEliminated * 10 * multiplier;
scoreTxt.setText(score);
// Give 1 refresh power-up if 2 line elimination is done in a single move
if (pointsToAdd === 2) {
if (typeof game.refreshPowerUps === "undefined") {
game.refreshPowerUps = 0;
}
game.refreshPowerUps += 1;
updateRefreshPowerupUI();
// Optionally, show a message or animation for the power-up here
}
// Give 1 bomb block if 3 line elimination is done in a single move
if (pointsToAdd === 3) {
if (typeof game.bombBlocks === "undefined") {
game.bombBlocks = 0;
}
game.bombBlocks += 1;
updateBombPowerupUI && updateBombPowerupUI();
// Optionally, update UI or show animation for bomb block here
}
// If more than 3 eliminations, give 1 bomb and +1 refresh for each extra elimination above 3
if (pointsToAdd > 3) {
if (typeof game.bombBlocks === "undefined") {
game.bombBlocks = 0;
}
if (typeof game.refreshPowerUps === "undefined") {
game.refreshPowerUps = 0;
}
var extra = pointsToAdd - 3;
game.bombBlocks += 1;
game.refreshPowerUps += extra;
updateBombPowerupUI && updateBombPowerupUI();
updateRefreshPowerupUI && updateRefreshPowerupUI();
// Optionally, show a message or animation for the extra power-ups here
}
}
}
board.removeTint();
dragTarget = undefined;
// --- Refresh power-up logic ---
// If player has refresh power-ups, allow refreshing the 3 blocks at the background
if (typeof game.refreshPowerUps !== "undefined" && game.refreshPowerUps > 0) {
// Example: call this function from a button or gesture in your UI
game.useRefreshPowerUp = function () {
if (game.refreshPowerUps > 0) {
// Remove current blocks
for (var i = 0; i < blocks.length; i++) {
if (blocks[i]) {
blocks[i].destroy();
blocks[i] = undefined;
}
}
// Create new blocks
game.createBlocks();
game.refreshPowerUps -= 1;
updateRefreshPowerupUI();
// Optionally, update UI to show remaining power-ups
}
};
}
// --- Bomb power-up logic ---
game.activateBombPowerUp = function () {
if (typeof game.bombBlocks === "undefined" || game.bombBlocks <= 0) return;
// Set bomb mode active
game.bombMode = true;
// Optionally, visually indicate bomb mode (e.g. flash board or show message)
// Next cell click on board will trigger bomb
board.on('down', function (x, y, obj) {
if (!game.bombMode) return;
// Calculate the exact cell under the click, not just the nearest cell
// Use board.toLocal to get the click position relative to the board
var localPos = board.toLocal(obj.global);
var size = 158;
var targetJ = Math.round(localPos.x / size + 10 / 2 - 0.5);
var targetI = Math.round(localPos.y / size + 10 / 2 - 0.5);
// Only clear the 3x3 area centered on the exact cell clicked, and nowhere else
if (targetI >= 0 && targetI < 10 && targetJ >= 0 && targetJ < 10) {
for (var di = -1; di <= 1; di++) {
for (var dj = -1; dj <= 1; dj++) {
var ni = targetI + di;
var nj = targetJ + dj;
if (ni >= 0 && ni < 10 && nj >= 0 && nj < 10) {
var cell = board.grid[ni][nj];
// Only clear cells in this 3x3 area, nowhere else
var tint = cell.getTint();
cell.setFill(false);
// Immediately reset tint to default so no hover/active color remains
cell.setTint(0xffffff);
board.spawnParticles(cell.x, cell.y, tint);
}
}
}
// Play elimination sound
LK.getSound('elimination').play();
// After bomb clears, ensure all cells in 3x3 area have default tint (no hover effect)
for (var di = -1; di <= 1; di++) {
for (var dj = -1; dj <= 1; dj++) {
var ni = targetI + di;
var nj = targetJ + dj;
if (ni >= 0 && ni < 10 && nj >= 0 && nj < 10) {
var cell = board.grid[ni][nj];
cell.setTint(0xffffff);
}
}
}
game.bombBlocks -= 1;
updateBombPowerupUI && updateBombPowerupUI();
game.bombMode = false;
// Reset bomb icon color to normal
if (typeof bombIcon !== "undefined") {
bombIcon.tint = 0xffffff;
}
// Remove this event handler after use
board.off('down');
}
});
};
}
});
game.createBlocks = function () {
for (var i = 0; i < 3; i++) {
game.createBlock(i);
}
};
var score = 0;
game.createBlocks();
LK.playMusic('gamemusic');
var scoreTxt = new Text2('0', {
size: 150,
fill: 0xFFFFFF,
font: 'Impact',
dropShadow: true,
dropShadowColor: '#2a636e'
});
scoreTxt.anchor.set(.5, 0);
LK.gui.top.addChild(scoreTxt);
// --- Power-up indicator at top right ---
var powerupContainer = new Container();
powerupContainer.x = -180; // Move more to the left from right edge
powerupContainer.y = 30; // Offset from top edge
// Add refresh icon
var refreshIcon = LK.getAsset('refresh', {
anchorX: 0.5,
anchorY: 0.5
});
refreshIcon.width = 80;
refreshIcon.height = 80;
refreshIcon.x = 0;
refreshIcon.y = 0;
powerupContainer.addChild(refreshIcon);
// Make refresh icon clickable to spend refresh power-up
refreshIcon.on('down', function (x, y, obj) {
if (typeof game.useRefreshPowerUp === "function") {
game.useRefreshPowerUp();
}
});
// Add bomb icon using Bomb asset
var bombIcon = LK.getAsset('Bomb', {
anchorX: 0.5,
anchorY: 0.5
});
bombIcon.width = 80;
bombIcon.height = 80;
bombIcon.x = 0;
bombIcon.y = 100;
powerupContainer.addChild(bombIcon);
// Make bomb icon clickable to toggle bomb mode and color
bombIcon.on('down', function (x, y, obj) {
// Only allow toggling if player has at least 1 bomb
if (typeof game.bombBlocks === "undefined" || game.bombBlocks <= 0) return;
// Toggle bomb mode
game.bombMode = !game.bombMode;
// Change bomb icon color to indicate mode
if (game.bombMode) {
// Bomb ON: tint to yellow
bombIcon.tint = 0xffff00;
// Activate bomb power-up (attach board click handler)
if (typeof game.activateBombPowerUp === "function") {
game.activateBombPowerUp();
}
} else {
// Bomb OFF: reset tint
bombIcon.tint = 0xffffff;
// Remove any board click handler if present
if (typeof board.off === "function") {
board.off('down');
}
}
});
// Add refresh count text
var refreshCountTxt = new Text2('0', {
size: 80,
fill: 0xffffff,
font: 'Impact',
dropShadow: true,
dropShadowColor: '#2a636e'
});
refreshCountTxt.anchor.set(0, 0.5);
refreshCountTxt.x = 50;
refreshCountTxt.y = 0;
powerupContainer.addChild(refreshCountTxt);
// Add bomb count text
var bombCountTxt = new Text2('0', {
size: 80,
fill: 0xffffff,
font: 'Impact',
dropShadow: true,
dropShadowColor: '#2a636e'
});
bombCountTxt.anchor.set(0, 0.5);
bombCountTxt.x = 50;
bombCountTxt.y = 100;
powerupContainer.addChild(bombCountTxt);
// Add to GUI topRight
LK.gui.topRight.addChild(powerupContainer);
// Helper to update refresh power-up count
function updateRefreshPowerupUI() {
var count = typeof game.refreshPowerUps !== "undefined" ? game.refreshPowerUps : 0;
refreshCountTxt.setText('' + count);
}
updateRefreshPowerupUI();
// Helper to update bomb power-up count
function updateBombPowerupUI() {
var count = typeof game.bombBlocks !== "undefined" ? game.bombBlocks : 0;
bombCountTxt.setText('' + count);
}
updateBombPowerupUI();
game.isMovePossible = function () {
for (var a = 0; a < blocks.length; a++) {
if (blocks[a]) {
for (var i = 0; i < 10; i++) {
for (var j = 0; j < 10; j++) {
if (board.grid[i][j].filled) {
continue;
}
var canPlace = true;
for (var k = 0; k < blocks[a].shape.length; k++) {
for (var l = 0; l < blocks[a].shape[k].length; l++) {
if (blocks[a].shape[k][l] === 1) {
if (i + k < 0 || i + k >= 10 || j + l < 0 || j + l >= 10 || board.grid[i + k][j + l].filled) {
canPlace = false;
break;
}
}
}
if (!canPlace) {
break;
}
}
if (canPlace) {
return true;
}
}
}
}
}
return false;
};
var isGameOver = false;
LK.on('tick', function () {
board.tick();
if (isGameOver || !game.isMovePossible()) {
LK.effects.flashScreen(0xffffff, 1000);
LK.showGameOver();
}
for (var a = blocks.length - 1; a >= 0; a--) {
if (blocks[a]) {
if (blocks[a] != dragTarget) {
blocks[a].moveTowardsHomePosition();
} else {
blocks[a].moveToDragTarget();
}
}
}
});
White particle cloud. Cartoon. Bright outline. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.
block with a smile face. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
Dark grey square with round corners, flat shaded, hyper casual game. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.
Background for dark hell themed puzzle game. Pastel colors, flat shaded, vector art. Blocks. Depressed. dark prisons Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.
a dark colored refresh sign box. In-Game asset. 2d. High contrast. No shadows
A bomb. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat