/****
* Classes
****/
var PointsText = Container.expand(function (points, x, y) {
var self = Container.call(this);
var text = new Text2('+' + points, {
size: 180,
fill: '#ffffff',
weight: '800',
align: 'center',
stroke: '#000000',
strokeThickness: 12
});
text.anchor.set(.5, .5);
text.x = x;
text.y = y;
self.addChild(text);
self.getText = function () {
return text;
};
self.alpha = 1;
self.fadeOutDuration = 30;
self.fadeOutStartTime = LK.ticks + 20;
self.tick = function () {
var elapsedTime = LK.ticks - self.fadeOutStartTime;
if (elapsedTime < 0) {
return;
}
if (elapsedTime < self.fadeOutDuration) {
self.alpha = 1 - elapsedTime / self.fadeOutDuration;
self.y -= 2;
} else {
self.alpha = 0;
self.destroy();
}
};
});
var ClockIndicator = Container.expand(function () {
var self = Container.call(this);
var indicatorGraphics = self.attachAsset('clockIndicator', {
anchorX: 0.5,
anchorY: 0.5
});
return self;
});
var Tile = Container.expand(function (type) {
var self = Container.call(this);
var tileGraphics = self.attachAsset('tile', {
anchorX: 0.5,
anchorY: 0.5
});
tileGraphics.alpha = 0;
self.targetX = 0;
self.targetY = 0;
self.isMoving = false;
self.totalTypes = 3;
self.type = type || Math.floor(Math.random() * self.totalTypes);
var hue = self.type / self.totalTypes;
var color = hsvToRgb(hue, 0.5, 1);
tileGraphics.tint = color;
self.cells = [];
var cellWidth = 0;
var cellHeight = 0;
self.structure = [];
if (self.type === 0) {
self.structure = [[1, 1]];
var cell1 = new Cell();
var cell2 = new Cell();
cellWidth = cell1.width;
cellHeight = cell1.height;
self.cells.push(cell1);
self.cells.push(cell2);
} else if (self.type === 1) {
self.structure = [[1], [1]];
var cell1 = new Cell();
var cell2 = new Cell();
cellWidth = cell1.width;
cellHeight = cell1.height;
self.cells.push(cell1);
self.cells.push(cell2);
} else if (self.type === 2) {
self.structure = [[1]];
var cell1 = new Cell();
cellWidth = cell1.width;
cellHeight = cell1.height;
self.cells.push(cell1);
}
self.cells.forEach(function (cell, index) {
if (self.type === 0) {
cell.x = index === 0 ? self.x - cellWidth / 2 : self.x + cellWidth / 2;
cell.y = self.y;
} else if (self.type === 1) {
cell.x = self.x;
cell.y = index === 0 ? self.y - cellHeight / 2 : self.y + cellHeight / 2;
}
self.addChild(cell);
});
self.move = function (x, y, instant) {
self.targetX = x;
self.targetY = y;
if (instant) {
self.x = x;
self.y = y;
}
};
self.tick = function () {
var dx = self.targetX - self.x;
var dy = self.targetY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 1) {
self.x += dx * 0.15;
self.y += dy * 0.15;
self.isMoving = true;
} else {
self.x = self.targetX;
self.y = self.targetY;
self.isMoving = false;
}
};
});
var GridBackgroundCell = Container.expand(function () {
var self = Container.call(this);
var bgCellGraphics = self.attachAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5
});
bgCellGraphics.alpha = 0.5;
self.setAlphaAndTint = function (alpha, tint) {
bgCellGraphics.alpha = alpha;
bgCellGraphics.tint = tint;
};
});
var Cell = Container.expand(function (type) {
var self = Container.call(this);
var cellGraphics = self.attachAsset('cell', {
anchorX: 0.5,
anchorY: 0.5
});
self.indicators = [];
var clockGraphics = self.attachAsset('clock', {
anchorX: 0.5,
anchorY: 0.5
});
clockGraphics.y = -8;
clockGraphics.x = -4;
clockGraphics.blendMode = 0;
var indicatorAngles = [270, 330, 30, 90, 150, 210];
var indicatorDistance = 100;
indicatorAngles.forEach(function (angle) {
var radian = angle * (Math.PI / 180);
var indicatorX = clockGraphics.x + Math.cos(radian) * indicatorDistance;
var indicatorY = clockGraphics.y + Math.sin(radian) * indicatorDistance;
var indicator = self.addChildAt(new ClockIndicator(), 1);
indicator.x = indicatorX;
indicator.y = indicatorY;
indicator.rotation = radian + Math.PI / 2;
self.indicators.push(indicator);
});
self.targetX = 0;
self.targetY = 0;
self.targetAlpha = 1;
self.isMoving = false;
self.totalTypes = 6;
self.type = type !== undefined ? type : Math.floor(Math.random() * self.totalTypes);
self.realType = self.type;
self.currentHue = self.type / self.totalTypes;
self.targetHue = self.type / self.totalTypes;
self.targetRotation = self.type * (2 * Math.PI / self.totalTypes);
var color = hsvToRgb(self.targetHue, 0.25, 1);
cellGraphics.tint = color;
clockGraphics.rotation = self.targetRotation;
self.changeType = function (newType) {
self.type = newType % self.totalTypes;
self.realType = newType;
self.targetHue = self.realType / self.totalTypes;
self.targetRotation = self.realType * (2 * Math.PI / self.totalTypes);
};
self.move = function (x, y, instant) {
self.targetX = x;
self.targetY = y;
if (instant) {
self.x = x;
self.y = y;
} else {
var steps = 20;
self.speedX = (x - self.x) / steps;
self.speedY = (y - self.y) / steps;
}
};
self.tick = function (allNotMoving) {
if (allNotMoving) {
if (self.currentHue !== self.targetHue) {
var hueDifference = self.targetHue - self.currentHue;
var hueStep = hueDifference * 0.05;
self.currentHue += hueStep;
if (Math.abs(hueDifference) < 0.01) {
self.currentHue = self.targetHue;
}
var color = hsvToRgb(self.currentHue, 0.25, 1);
cellGraphics.tint = color;
}
var rotationDifference = self.targetRotation - clockGraphics.rotation;
if (Math.abs(rotationDifference) > 0.01) {
clockGraphics.rotation += rotationDifference * 0.05;
} else {
clockGraphics.rotation = self.targetRotation;
}
}
var alphaDifference = self.targetAlpha - self.alpha;
if (Math.abs(alphaDifference) > 0.01) {
self.alpha += alphaDifference * 0.1;
} else {
self.alpha = self.targetAlpha;
}
var acceleration = 1;
self.speedX = self.speedX || 0;
self.speedY = self.speedY || 0;
var threshold = 1;
var dx = self.targetX - self.x;
var dy = self.targetY - self.y;
if (Math.abs(dx) < threshold && Math.abs(dy) < threshold) {
self.x = self.targetX;
self.y = self.targetY;
self.isMoving = false;
self.speedX = 0;
self.speedY = 0;
} else {
var nextX = self.x + self.speedX;
var nextY = self.y + self.speedY;
if (self.speedX > 0 && nextX > self.targetX || self.speedX < 0 && nextX < self.targetX) {
nextX = self.targetX;
self.speedX = 0;
}
if (self.speedY > 0 && nextY > self.targetY || self.speedY < 0 && nextY < self.targetY) {
nextY = self.targetY;
self.speedY = 0;
}
self.x = nextX;
self.y = nextY;
self.isMoving = self.x !== self.targetX || self.y !== self.targetY;
}
};
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000000
});
/****
* Game Code
****/
function hsvToRgb(h, s, v) {
var i = Math.floor(h * 6),
f = h * 6 - i,
p = v * (1 - s),
q = v * (1 - f * s),
t = v * (1 - (1 - f) * s),
mod = i % 6,
r = [v, q, p, p, t, v][mod],
g = [t, v, v, q, p, p][mod],
b = [p, p, t, v, v, q][mod];
return (r * 255 << 16) + (g * 255 << 8) + b * 255;
}
var effectsArray = [];
game.processMergeAndGameOver = function (force) {
force = force || false;
var isAnyRowMoving = grid.some(function (row) {
return row.some(function (cell) {
return cell && cell.isMoving;
});
});
if (!isAnyRowMoving && !game.mergeMode || force) {
grid.forEach(function (row) {
row.forEach(function (cell) {
if (cell) {
var colRow = game.findCellColRow(cell);
var connectedNeighbors = game.findConnectedNeighbors(cell);
if (connectedNeighbors.length >= 3) {
toMerge.push(connectedNeighbors);
game.setMergeMode(true);
}
}
});
});
if (game.mergeMode) {
grid.forEach(function (row) {
row.forEach(function (cell) {
if (cell && !toMerge.some(function (mergeGroup) {
return mergeGroup.includes(cell);
})) {
cell.targetAlpha = 0.5;
}
});
});
game.scoreMultiplier += 1;
game.updateScoreMultiplier(game.scoreMultiplier);
} else {
game.scoreMultiplier = -1;
game.updateScoreMultiplier(game.scoreMultiplier);
}
var isAnyTileMoving = bottomTiles.some(function (tile) {
return tile.isMoving;
});
isAnyRowMoving = grid.some(function (row) {
return row.some(function (cell) {
return cell && cell.isMoving;
});
});
if (!isAnyTileMoving && !game.canPlaceAnyTile() && !isAnyRowMoving && toDelete.length === 0 && !game.mergeMode) {
LK.showGameOver();
}
}
};
game.addAndHandleCell = function (targetCell) {
gridContainer.addChild(targetCell);
targetCell.on('down', function () {
if (game.mergeMode) {
game.mergeConnectedNeighbors(targetCell);
}
});
};
game.mergeConnectedNeighbors = function (cell) {
var connectedNeighbors = game.findConnectedNeighbors(cell);
if (connectedNeighbors.length >= 3) {
var colRow = game.findCellColRow(cell);
var newType = cell.realType + 1;
var pointsEarned = connectedNeighbors.length * Math.max(game.scoreMultiplier * 3, 1);
var pointsText = new PointsText(pointsEarned, cell.x, cell.y - 300);
gridContainer.addChild(pointsText);
effectsArray.push(pointsText);
game.score += pointsEarned;
LK.setScore(game.score);
scoreTxt.setText(game.score.toString());
connectedNeighbors.forEach(function (neighborCell) {
var neighborColRow = game.findCellColRow(neighborCell);
if (neighborColRow) {
if (neighborCell === cell) {
cell.changeType(newType);
gridContainer.removeChild(cell);
gridContainer.addChild(cell);
return;
}
grid[neighborColRow.col][neighborColRow.row] = null;
neighborCell.isMoving = true;
toDelete.push(neighborCell);
neighborCell.move(cell.x, cell.y);
}
}, this);
}
};
var bottomTilesContainer = new Container();
game.setMergeMode = function (mode) {
game.mergeMode = mode;
bottomTilesContainer.alpha = mode ? 0.4 : 1;
if (!mode && game.mergeModeText) {
game.mergeModeText.destroy();
game.mergeModeText = null;
}
if (mode && !game.mergeModeTextShown) {
var mergeModeText = new Text2('Tap a tile to merge\nall tiles into it', {
size: 130,
fill: '#ffffff',
weight: '800',
align: 'center',
dropShadow: true,
dropShadowColor: '#000000',
dropShadowBlur: 4,
dropShadowAngle: Math.PI / 6,
dropShadowDistance: 12,
stroke: '#000000',
strokeThickness: 12
});
mergeModeText.anchor.set(0.5, 1);
mergeModeText.y = -75;
LK.gui.bottom.addChild(mergeModeText);
game.mergeModeText = mergeModeText;
game.mergeModeTextShown = true;
}
};
game.setMergeMode(false);
game.score = 0;
game.scoreMultiplier = -1;
game.setBackgroundColor(0xffffff);
var backgroundGraphics = game.attachAsset('background', {
anchorX: 0.5,
anchorY: 0.5
});
game.addChild(backgroundGraphics);
backgroundGraphics.x = 2048 / 2 - 50 + 3;
backgroundGraphics.y = 2732 / 2 - 205 + 3;
backgroundGraphics.alpha = 1;
var bottomTileBackground = game.attachAsset('bottomTileBackground', {
anchorX: 0.5,
anchorY: 1
});
game.addChild(bottomTileBackground);
bottomTileBackground.x = 2048 / 2;
bottomTileBackground.y = 2732 + 150 + 130;
bottomTileBackground.alpha = 1;
var bottomTileBackground2 = game.attachAsset('bottomTileBackground2', {
anchorX: 0.5,
anchorY: 1
});
game.addChild(bottomTileBackground2);
bottomTileBackground2.x = 2048 / 2;
bottomTileBackground2.y = bottomTileBackground.y + bottomTileBackground.height + 10;
bottomTileBackground2.alpha = 0.7;
game.findCellColRow = function (cell) {
for (var col = 0; col < gridWidth; col++) {
for (var row = 0; row < gridHeight; row++) {
if (grid[col][row] === cell) {
return {
col: col,
row: row
};
}
}
}
return null;
};
game.canPlaceAnyTile = function () {
for (var i = 0; i < bottomTiles.length; i++) {
var tile = bottomTiles[i];
for (var col = 0; col < gridWidth; col++) {
for (var row = 0; row < gridHeight; row++) {
if (game.canPlaceTile(tile, col, row)) {
return true;
}
}
}
}
return false;
};
game.canPlaceTile = function (tile, gridX, gridY) {
for (var row = 0; row < tile.structure.length; row++) {
for (var col = 0; col < tile.structure[row].length; col++) {
if (tile.structure[row][col] === 1) {
var targetCol = gridX + col;
var targetRow = gridY + row;
if (targetCol < 0 || targetCol >= gridWidth || targetRow < 0 || targetRow >= gridHeight) {
return false;
}
if (grid[targetCol][targetRow]) {
return false;
}
}
}
}
return true;
};
game.toTest = [];
game.getOverlappingCells = function (tile) {
var overlappingCells = [];
var shouldReturnEmpty = false;
tile.cells.forEach(function (cell) {
var cellBounds = cell.getBounds();
for (var i = 0; i < gridWidth; i++) {
for (var j = 0; j < gridHeight; j++) {
var bgCell = bgGrid[i][j];
var bgCellBounds = bgCell.getBounds();
if (cellBounds.contains(bgCellBounds.x + bgCellBounds.width / 2, bgCellBounds.y + bgCellBounds.height / 2)) {
if (grid[i][j]) {
shouldReturnEmpty = true;
break;
}
overlappingCells.push(bgCell);
}
}
if (shouldReturnEmpty) {
break;
}
}
});
if (shouldReturnEmpty) {
return [];
}
return overlappingCells;
};
game.resetBackgroundGridCells = function () {
for (var i = 0; i < gridWidth; i++) {
for (var j = 0; j < gridHeight; j++) {
var bgCell = bgGrid[i][j];
bgCell.setAlphaAndTint(0.5, 0xFFFFFF);
}
}
};
var gridWidth = 5;
var gridHeight = 5;
var gridSpacing = 2;
var tempTile = new Tile();
var tileWidth = tempTile.width;
var tileHeight = tempTile.height;
tempTile.destroy();
var tempCell = new Cell();
var cellWidth = 320;
var cellHeight = 320;
tempCell.destroy();
var gridContainer = new Container();
game.addChild(gridContainer);
var totalGridWidth = gridWidth * (cellWidth + gridSpacing) - gridSpacing;
var totalGridHeight = gridHeight * (cellHeight + gridSpacing) - gridSpacing;
gridContainer.x = (2048 - totalGridWidth) / 2 + cellWidth / 2;
gridContainer.y = (2732 - totalGridHeight) / 2 + cellHeight / 2 - 290;
game.calculateTargetPosition = function (col, row) {
return {
x: col * (cellWidth + gridSpacing),
y: row * (cellHeight + gridSpacing)
};
};
game.findConnectedNeighbors = function (cell, connectedNeighbors) {
connectedNeighbors = connectedNeighbors || [];
if (!cell) {
return [];
}
var cellType = cell.type;
var cellColRow = game.findCellColRow(cell);
if (cellColRow) {
var directions = [[-1, 0], [1, 0], [0, -1], [0, 1]];
directions.forEach(function (dir) {
var newRow = cellColRow.row + dir[0];
var newCol = cellColRow.col + dir[1];
if (newRow >= 0 && newRow < gridHeight && newCol >= 0 && newCol < gridWidth) {
var neighborCell = grid[newCol][newRow];
if (neighborCell && neighborCell.visible && neighborCell.type === cellType && connectedNeighbors.indexOf(neighborCell) === -1) {
connectedNeighbors.push(neighborCell);
game.findConnectedNeighbors(neighborCell, connectedNeighbors);
}
}
});
}
return connectedNeighbors;
};
game.findBgCellColRow = function (bgCell) {
for (var col = 0; col < gridWidth; col++) {
for (var row = 0; row < gridHeight; row++) {
if (bgGrid[col][row] === bgCell) {
return {
col: col,
row: row
};
}
}
}
return null;
};
var grid = Array(5).fill().map(function () {
return Array(5).fill(null);
});
var bgGrid = Array(5).fill().map(function () {
return Array(5).fill(null);
});
for (var i = 0; i < gridWidth; i++) {
for (var j = 0; j < gridHeight; j++) {
var bgCell = new GridBackgroundCell();
var targetPos = game.calculateTargetPosition(i, j);
bgCell.x = targetPos.x;
bgCell.y = targetPos.y;
bgGrid[i][j] = bgCell;
gridContainer.addChild(bgCell);
}
}
for (var k = 0; k < 2; k++) {
var randomCol = Math.floor(Math.random() * gridWidth);
var randomRow = Math.floor(Math.random() * gridHeight);
while (grid[randomCol][randomRow]) {
randomCol = Math.floor(Math.random() * gridWidth);
randomRow = Math.floor(Math.random() * gridHeight);
}
var cell = new Cell();
var targetPos = game.calculateTargetPosition(randomCol, randomRow);
cell.move(targetPos.x, targetPos.y, true);
grid[randomCol][randomRow] = cell;
game.addAndHandleCell(cell);
}
game.on('move', function (obj) {
if (draggedTile) {
var pos = obj.event.getLocalPosition(game);
draggedTile.x = pos.x;
draggedTile.y = pos.y;
game.highlightOverlappingCells(draggedTile);
}
});
game.highlightOverlappingCells = function (tile) {
game.resetBackgroundGridCells();
var overlappingCells = game.getOverlappingCells(tile);
if (overlappingCells.length !== tile.cells.length) {
return;
}
overlappingCells.forEach(function (bgCell, index) {
var cell = tile.cells[index];
var color = hsvToRgb(cell.currentHue, 0.25, 1);
bgCell.setAlphaAndTint(1, color);
});
};
LK.on('tick', function () {
for (var i = effectsArray.length - 1; i >= 0; i--) {
var effectNode = effectsArray[i];
effectNode.tick();
if (effectNode.alpha <= 0) {
effectNode.destroy();
effectsArray.splice(i, 1);
}
}
var allNotMoving = toDelete.every(function (cell) {
return !cell.isMoving;
});
if (toDelete.length > 0) {
if (allNotMoving) {
if (toDelete.length > 0) {
game.setMergeMode(false);
grid.forEach(function (row) {
row.forEach(function (cell) {
if (cell) {
cell.targetAlpha = 1;
}
});
});
toMerge.forEach(function (group) {
group.forEach(function (cell) {
game.toTest.push(cell);
});
});
toMerge = [];
game.processMergeAndGameOver(true);
}
toDelete.forEach(function (cell) {
cell.destroy();
});
toDelete = [];
for (var i = 0; i < gridWidth; i++) {
for (var j = 0; j < gridHeight; j++) {
if (grid[i][j] && !grid[i][j].visible) {
grid[i][j].visible = true;
game.toTest.push(grid[i][j]);
}
}
}
} else {
for (var i = 0; i < toDelete.length; i++) {
toDelete[i].tick();
}
}
}
for (var i = 0; i < gridWidth; i++) {
for (var j = 0; j < gridHeight; j++) {
if (grid[i][j]) {
grid[i][j].tick(allNotMoving);
}
}
}
for (var i = 0; i < bottomTiles.length; i++) {
if (bottomTiles[i] !== draggedTile) {
bottomTiles[i].tick();
}
}
var isAnyRowMoving = grid.some(function (row) {
return row.some(function (cell) {
return cell && cell.isMoving;
});
});
game.processMergeAndGameOver();
});
var bottomTiles = [];
var toDelete = [];
var toMerge = [];
var draggedTile = null;
game.addBottomTiles = function () {
var numberOfTiles = 3;
var totalTilesWidth = numberOfTiles * tileWidth + (numberOfTiles - 1) * gridSpacing;
var margin = (2048 - totalTilesWidth) / 2;
var spacing = margin + tileWidth / 2;
var posY = 2732 - tileHeight / 2 - margin;
for (var i = 0; i < numberOfTiles; i++) {
var tile = new Tile();
var posX = spacing + i * (tileWidth + gridSpacing);
tile.move(posX, 2732 + tileHeight, true);
tile.move(posX, posY);
bottomTiles[i] = tile;
tile.on('down', function () {
if (game.mergeMode) {
return;
}
bottomTilesContainer.removeChild(this);
bottomTilesContainer.addChild(this);
draggedTile = this;
});
bottomTilesContainer.addChild(tile);
}
};
var scoreTxt = new Text2('0', {
size: 150,
fill: '#ffffff',
font: 'Impact',
stroke: '#000000',
strokeThickness: 6,
dropShadow: true,
dropShadowColor: '#000000',
dropShadowBlur: 4,
dropShadowAngle: Math.PI / 6,
dropShadowDistance: 6
});
scoreTxt.anchor.set(.5, 0);
scoreTxt.y -= 25;
LK.gui.topCenter.addChild(scoreTxt);
game.addChild(bottomTilesContainer);
var scoreMultiplierTxt = new Text2('+1', {
size: 100,
fill: '#ffffff',
font: 'Impact',
stroke: '#000000',
strokeThickness: 6,
dropShadow: true,
dropShadowColor: '#000000',
dropShadowBlur: 4,
dropShadowAngle: Math.PI / 6,
dropShadowDistance: 6
});
scoreMultiplierTxt.anchor.set(1, 0);
scoreMultiplierTxt.x = -20;
scoreMultiplierTxt.y = 5;
LK.gui.topRight.addChild(scoreMultiplierTxt);
game.updateScoreMultiplier = function (multiplier) {
scoreMultiplierTxt.setText('x' + Math.max(1, multiplier * 3));
};
game.updateScoreMultiplier(game.scoreMultiplier);
game.on('up', function (obj) {
game.resetBackgroundGridCells();
if (draggedTile) {
var overlappingCells = game.getOverlappingCells(draggedTile);
if (overlappingCells.length === draggedTile.cells.length) {
overlappingCells.forEach(function (bgCell, index) {
var colRow = game.findBgCellColRow(bgCell);
if (colRow && draggedTile.cells[index]) {
var cell = draggedTile.cells[index];
var targetPos = game.calculateTargetPosition(colRow.col, colRow.row);
cell.move(targetPos.x, targetPos.y, true);
grid[colRow.col][colRow.row] = cell;
game.addAndHandleCell(cell);
game.toTest.push(cell);
}
});
var tileIndex = bottomTiles.indexOf(draggedTile);
if (tileIndex !== -1) {
bottomTiles.splice(tileIndex, 1);
}
draggedTile.destroy();
if (bottomTiles.length === 0) {
game.addBottomTiles();
}
draggedTile = null;
}
}
draggedTile = null;
});
game.addBottomTiles();
Simple White square round corners. Vector. No details. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.
Outdoor, nighttime Christmas Background for puzzle game. Cartoon. Pastel colors, flat shaded, vector art. Full width blank square in center. Sharp corners.
Simple Snow dunes cartoon. Christmas gifts at bottom
Simple white arrow pointing up. Mouse cursor like. Black outline. No shadow