/****
* Classes
****/
var PointsText = Container.expand(function (points, x, y) {
var self = Container.call(this);
var text = new Text2('+' + points, {
size: 180,
fill: '#000000',
weight: '800',
align: 'center',
stroke: '#ffffff',
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;
bgCellGraphics.x += 10;
bgCellGraphics.y += 15;
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
});
var clockGraphics = self.attachAsset('clock', {
anchorX: 0.5,
anchorY: 0.5
});
clockGraphics.y = -8;
clockGraphics.x = -4;
clockGraphics.blendMode = 2;
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.addChild(new ClockIndicator());
indicator.x = indicatorX;
indicator.y = indicatorY;
indicator.rotation = radian;
});
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.4;
}
});
});
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);
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: '#000000',
weight: '800',
align: 'center',
dropShadow: true,
dropShadowColor: '#ffffff',
dropShadowBlur: 4,
dropShadowAngle: Math.PI / 6,
dropShadowDistance: 6
});
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;
backgroundGraphics.y = 2732 / 2;
backgroundGraphics.alpha = 0.5;
var bottomTileBackground = game.attachAsset('bottomTileBackground', {
anchorX: 0.5,
anchorY: 1
});
game.addChild(bottomTileBackground);
bottomTileBackground.x = 2048 / 2;
bottomTileBackground.y = 2732;
bottomTileBackground.alpha = 0.5;
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: '#24272b',
font: 'Impact',
dropShadow: true,
dropShadowColor: '#ffffff',
dropShadowBlur: 4,
dropShadowAngle: Math.PI / 6,
dropShadowDistance: 6
});
scoreTxt.anchor.set(.5, 0);
LK.gui.topCenter.addChild(scoreTxt);
game.addChild(bottomTilesContainer);
var scoreMultiplierTxt = new Text2('+1', {
size: 100,
fill: '#24272b',
font: 'Impact',
dropShadow: true,
dropShadowColor: '#ffffff',
dropShadowBlur: 4,
dropShadowAngle: Math.PI / 6,
dropShadowDistance: 6
});
scoreMultiplierTxt.anchor.set(1, 0);
scoreMultiplierTxt.x = -20;
scoreMultiplierTxt.y = 20;
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.
Background for relaxing puzzle game. Pastel colors, flat shaded, vector art. Flowers. Blocks. Relaxing. Clouds Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.
Simple White square round corners. Vector. No details. Single Game Texture. In-Game asset. 2d. White background. High contrast. No shadows.
Simple black arrow pointing up. Mouse cursor like.