/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1", { highScore: 0, currentLevel: 1 }); /**** * Classes ****/ var AdElement = Container.expand(function (type, row, col) { var self = Container.call(this); self.type = type; self.row = row; self.col = col; self.isPowerup = false; var assetId; switch (type) { case 'social': assetId = 'adSocial'; break; case 'print': assetId = 'adPrint'; break; case 'tv': assetId = 'adTV'; break; case 'billboard': assetId = 'adBillboard'; break; case 'viral': assetId = 'powerupViral'; self.isPowerup = true; break; case 'targeted': assetId = 'powerupTargeted'; self.isPowerup = true; break; default: assetId = 'adSocial'; } var graphic = self.attachAsset(assetId, { anchorX: 0.5, anchorY: 0.5, scaleX: 0.9, scaleY: 0.9 }); self.setGridPosition = function (newRow, newCol) { self.row = newRow; self.col = newCol; self.x = GRID_OFFSET_X + newCol * CELL_SIZE + CELL_SIZE / 2; self.y = GRID_OFFSET_Y + newRow * CELL_SIZE + CELL_SIZE / 2; }; self.highlight = function () { tween(graphic, { scaleX: 1.1, scaleY: 1.1 }, { duration: 200 }); }; self.unhighlight = function () { tween(graphic, { scaleX: 0.9, scaleY: 0.9 }, { duration: 200 }); }; self.explode = function () { tween(graphic, { alpha: 0, scaleX: 1.5, scaleY: 1.5 }, { duration: 300, onFinish: function onFinish() { self.destroy(); } }); }; self.down = function (x, y, obj) { selectAdElement(self); }; return self; }); var ClientBrief = Container.expand(function (targetType, count) { var self = Container.call(this); self.targetType = targetType; self.requiredCount = count; self.currentCount = 0; var assetId; switch (targetType) { case 'social': assetId = 'adSocial'; break; case 'print': assetId = 'adPrint'; break; case 'tv': assetId = 'adTV'; break; case 'billboard': assetId = 'adBillboard'; break; default: assetId = 'adSocial'; } var icon = self.attachAsset(assetId, { anchorX: 0.5, anchorY: 0.5, x: 0, y: 0, scaleX: 0.6, scaleY: 0.6 }); var progressText = new Text2('0/' + count, { size: 40, fill: 0x333333 }); progressText.anchor.set(0, 0.5); progressText.x = 50; progressText.y = 0; self.addChild(progressText); self.updateProgress = function (matched) { if (matched && self.targetType === matched) { self.currentCount++; if (self.currentCount > self.requiredCount) { self.currentCount = self.requiredCount; } progressText.setText(self.currentCount + '/' + self.requiredCount); if (self.isComplete()) { tween(icon, { scaleX: 0.8, scaleY: 0.8 }, { duration: 200, easing: tween.easeOut, onFinish: function onFinish() { tween(icon, { scaleX: 0.6, scaleY: 0.6 }, { duration: 200, easing: tween.easeIn }); } }); } } }; self.isComplete = function () { return self.currentCount >= self.requiredCount; }; return self; }); var GridCell = Container.expand(function (row, col) { var self = Container.call(this); self.row = row; self.col = col; self.isHighlighted = false; var background = self.attachAsset('cellBackground', { anchorX: 0.5, anchorY: 0.5, alpha: 0.3 }); self.setPosition = function () { self.x = GRID_OFFSET_X + col * CELL_SIZE + CELL_SIZE / 2; self.y = GRID_OFFSET_Y + row * CELL_SIZE + CELL_SIZE / 2; }; self.highlight = function () { if (!self.isHighlighted) { self.isHighlighted = true; tween(background, { alpha: 0.6 }, { duration: 200 }); } }; self.unhighlight = function () { if (self.isHighlighted) { self.isHighlighted = false; tween(background, { alpha: 0.3 }, { duration: 200 }); } }; self.setPosition(); return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0xf5f5f5 }); /**** * Game Code ****/ // Constants var GRID_SIZE = 6; var CELL_SIZE = 180; var GRID_OFFSET_X = (2048 - GRID_SIZE * CELL_SIZE) / 2; var GRID_OFFSET_Y = 500; var MATCH_MIN = 3; var AD_TYPES = ['social', 'print', 'tv', 'billboard']; // Game variables var grid = []; var gridCells = []; var adElements = []; var selectedElement = null; var moveCount = 30; var score = 0; var level = storage.currentLevel || 1; var clientBriefs = []; var gameActive = true; // UI Elements var gridBackground = LK.getAsset('gridBackground', { anchorX: 0.5, anchorY: 0.5, scaleX: GRID_SIZE * CELL_SIZE / 1100, scaleY: GRID_SIZE * CELL_SIZE / 1100, alpha: 0.5 }); gridBackground.x = GRID_OFFSET_X + GRID_SIZE * CELL_SIZE / 2; gridBackground.y = GRID_OFFSET_Y + GRID_SIZE * CELL_SIZE / 2; game.addChild(gridBackground); var titleText = new Text2("Ad Story Blast", { size: 130, fill: 0x333333 }); titleText.anchor.set(0.5, 0); titleText.x = 2048 / 2; titleText.y = 80; game.addChild(titleText); var levelText = new Text2("Level: " + level, { size: 60, fill: 0x333333 }); levelText.anchor.set(0, 0); levelText.x = 150; levelText.y = 250; game.addChild(levelText); var scoreText = new Text2("Score: 0", { size: 60, fill: 0x333333 }); scoreText.anchor.set(0, 0); scoreText.x = 150; scoreText.y = 320; game.addChild(scoreText); var moveText = new Text2("Moves: " + moveCount, { size: 60, fill: 0x333333 }); moveText.anchor.set(1, 0); moveText.x = 2048 - 150; moveText.y = 250; game.addChild(moveText); var briefsTitle = new Text2("Client Briefs:", { size: 60, fill: 0x333333 }); briefsTitle.anchor.set(0, 0); briefsTitle.x = 150; briefsTitle.y = GRID_OFFSET_Y + GRID_SIZE * CELL_SIZE + 80; game.addChild(briefsTitle); var selectedOutline = LK.getAsset('selectedOutline', { anchorX: 0.5, anchorY: 0.5, alpha: 0.8 }); selectedOutline.visible = false; game.addChild(selectedOutline); // Functions function initializeGrid() { // Create grid cells for (var row = 0; row < GRID_SIZE; row++) { gridCells[row] = []; for (var col = 0; col < GRID_SIZE; col++) { var cell = new GridCell(row, col); gridCells[row][col] = cell; game.addChild(cell); } } // Create empty grid for (var row = 0; row < GRID_SIZE; row++) { grid[row] = []; for (var col = 0; col < GRID_SIZE; col++) { grid[row][col] = null; } } // Fill grid with ad elements fillGrid(); } function fillGrid() { for (var row = 0; row < GRID_SIZE; row++) { for (var col = 0; col < GRID_SIZE; col++) { if (grid[row][col] === null) { createAdElement(row, col); } } } // Check for initial matches and refill if needed var initialMatches = findAllMatches(); if (initialMatches.length > 0) { for (var i = 0; i < initialMatches.length; i++) { for (var j = 0; j < initialMatches[i].length; j++) { var element = initialMatches[i][j]; grid[element.row][element.col] = null; element.destroy(); } } fillGrid(); } } function createAdElement(row, col) { // Get random ad type var typeIndex = Math.floor(Math.random() * AD_TYPES.length); var type = AD_TYPES[typeIndex]; // Avoid creating three in a row initially if (row >= 2) { var prevType1 = grid[row - 1][col] ? grid[row - 1][col].type : null; var prevType2 = grid[row - 2][col] ? grid[row - 2][col].type : null; if (prevType1 === type && prevType2 === type) { // Choose a different type do { typeIndex = Math.floor(Math.random() * AD_TYPES.length); type = AD_TYPES[typeIndex]; } while (type === prevType1); } } if (col >= 2) { var prevType1 = grid[row][col - 1] ? grid[row][col - 1].type : null; var prevType2 = grid[row][col - 2] ? grid[row][col - 2].type : null; if (prevType1 === type && prevType2 === type) { // Choose a different type do { typeIndex = Math.floor(Math.random() * AD_TYPES.length); type = AD_TYPES[typeIndex]; } while (type === prevType1); } } var adElement = new AdElement(type, row, col); adElement.setGridPosition(row, col); grid[row][col] = adElement; adElements.push(adElement); game.addChild(adElement); return adElement; } function selectAdElement(element) { if (!gameActive) { return; } if (selectedElement === null) { // First selection selectedElement = element; selectedElement.highlight(); selectedOutline.x = selectedElement.x; selectedOutline.y = selectedElement.y; selectedOutline.visible = true; } else if (selectedElement === element) { // Deselect selectedElement.unhighlight(); selectedElement = null; selectedOutline.visible = false; } else { // Check if adjacent if (areAdjacent(selectedElement, element)) { // Swap elements swapElements(selectedElement, element); } else { // Not adjacent, change selection selectedElement.unhighlight(); selectedElement = element; selectedElement.highlight(); selectedOutline.x = selectedElement.x; selectedOutline.y = selectedElement.y; } } } function areAdjacent(elem1, elem2) { // Check if two elements are adjacent in the grid var rowDiff = Math.abs(elem1.row - elem2.row); var colDiff = Math.abs(elem1.col - elem2.col); return rowDiff === 1 && colDiff === 0 || rowDiff === 0 && colDiff === 1; } function swapElements(elem1, elem2) { // Store original positions var row1 = elem1.row; var col1 = elem1.col; var row2 = elem2.row; var col2 = elem2.col; // Update grid references grid[row1][col1] = elem2; grid[row2][col2] = elem1; // Update element positions elem1.setGridPosition(row2, col2); elem2.setGridPosition(row1, col1); // Animate the swap tween(elem1, { x: GRID_OFFSET_X + col2 * CELL_SIZE + CELL_SIZE / 2, y: GRID_OFFSET_Y + row2 * CELL_SIZE + CELL_SIZE / 2 }, { duration: 200 }); tween(elem2, { x: GRID_OFFSET_X + col1 * CELL_SIZE + CELL_SIZE / 2, y: GRID_OFFSET_Y + row1 * CELL_SIZE + CELL_SIZE / 2 }, { duration: 200, onFinish: function onFinish() { // Reset selection elem1.unhighlight(); selectedElement = null; selectedOutline.visible = false; // Check for matches var matches = findAllMatches(); if (matches.length > 0) { // Valid move decrementMoves(); processMatches(matches); } else { // Invalid move, swap back grid[row1][col1] = elem1; grid[row2][col2] = elem2; elem1.setGridPosition(row1, col1); elem2.setGridPosition(row2, col2); tween(elem1, { x: GRID_OFFSET_X + col1 * CELL_SIZE + CELL_SIZE / 2, y: GRID_OFFSET_Y + row1 * CELL_SIZE + CELL_SIZE / 2 }, { duration: 200 }); tween(elem2, { x: GRID_OFFSET_X + col2 * CELL_SIZE + CELL_SIZE / 2, y: GRID_OFFSET_Y + row2 * CELL_SIZE + CELL_SIZE / 2 }, { duration: 200 }); } } }); } function findAllMatches() { var allMatches = []; // Check horizontal matches for (var row = 0; row < GRID_SIZE; row++) { var col = 0; while (col < GRID_SIZE - 2) { var matchLength = 1; var currentType = grid[row][col] ? grid[row][col].type : null; if (currentType) { // Count matching adjacent elements while (col + matchLength < GRID_SIZE && grid[row][col + matchLength] && grid[row][col + matchLength].type === currentType) { matchLength++; } // If we have a match of at least 3 if (matchLength >= MATCH_MIN) { var match = []; for (var i = 0; i < matchLength; i++) { match.push(grid[row][col + i]); } allMatches.push(match); col += matchLength; } else { col++; } } else { col++; } } } // Check vertical matches for (var col = 0; col < GRID_SIZE; col++) { var row = 0; while (row < GRID_SIZE - 2) { var matchLength = 1; var currentType = grid[row][col] ? grid[row][col].type : null; if (currentType) { // Count matching adjacent elements while (row + matchLength < GRID_SIZE && grid[row + matchLength][col] && grid[row + matchLength][col].type === currentType) { matchLength++; } // If we have a match of at least 3 if (matchLength >= MATCH_MIN) { var match = []; for (var i = 0; i < matchLength; i++) { match.push(grid[row + i][col]); } allMatches.push(match); row += matchLength; } else { row++; } } else { row++; } } } return allMatches; } function processMatches(matches) { // Play sound LK.getSound('match').play(); // Calculate points and remove matched elements var pointsEarned = 0; var matchedTypes = {}; for (var i = 0; i < matches.length; i++) { var match = matches[i]; var matchType = match[0].type; var matchSize = match.length; // Keep track of matched types for client briefs if (!matchedTypes[matchType]) { matchedTypes[matchType] = 0; } matchedTypes[matchType] += matchSize; // Calculate points (more points for larger matches) var matchPoints = 10 * matchSize; pointsEarned += matchPoints; // Create powerup for matches of 4 or more var createPowerup = false; var powerupType = null; var powerupRow = null; var powerupCol = null; if (matchSize >= 4) { createPowerup = true; powerupType = matchSize >= 5 ? 'viral' : 'targeted'; // Use middle element position for powerup var middleIndex = Math.floor(matchSize / 2); powerupRow = match[middleIndex].row; powerupCol = match[middleIndex].col; } // Remove matched elements for (var j = 0; j < matchSize; j++) { var element = match[j]; var elementIndex = adElements.indexOf(element); if (elementIndex !== -1) { adElements.splice(elementIndex, 1); } grid[element.row][element.col] = null; element.explode(); } // Create powerup if needed if (createPowerup) { // Wait for animations to complete LK.setTimeout(function (row, col, type) { return function () { var powerup = new AdElement(type, row, col); powerup.setGridPosition(row, col); grid[row][col] = powerup; adElements.push(powerup); game.addChild(powerup); LK.getSound('powerup').play(); }; }(powerupRow, powerupCol, powerupType), 300); } } // Update score updateScore(pointsEarned); // Update client briefs for (var type in matchedTypes) { if (matchedTypes.hasOwnProperty(type) && AD_TYPES.indexOf(type) !== -1) { updateClientBriefs(type); } } // Check if all briefs are completed checkBriefsCompleted(); // After a delay, collapse the grid and refill LK.setTimeout(function () { collapseGrid(); }, 400); } function processPowerup(powerup, targetElement) { var elementsToRemove = []; if (powerup.type === 'viral') { // Viral powerup: clear entire row and column for (var col = 0; col < GRID_SIZE; col++) { if (grid[powerup.row][col] && grid[powerup.row][col] !== powerup) { elementsToRemove.push(grid[powerup.row][col]); } } for (var row = 0; row < GRID_SIZE; row++) { if (grid[row][powerup.col] && grid[row][powerup.col] !== powerup && elementsToRemove.indexOf(grid[row][powerup.col]) === -1) { elementsToRemove.push(grid[row][powerup.col]); } } } else if (powerup.type === 'targeted') { // Targeted powerup: remove all elements of the same type as the target var targetType = targetElement.type; for (var row = 0; row < GRID_SIZE; row++) { for (var col = 0; col < GRID_SIZE; col++) { if (grid[row][col] && grid[row][col].type === targetType && !grid[row][col].isPowerup) { elementsToRemove.push(grid[row][col]); } } } } // Remove powerup var powerupIndex = adElements.indexOf(powerup); if (powerupIndex !== -1) { adElements.splice(powerupIndex, 1); } grid[powerup.row][powerup.col] = null; powerup.explode(); // Remove affected elements for (var i = 0; i < elementsToRemove.length; i++) { var element = elementsToRemove[i]; var elementIndex = adElements.indexOf(element); if (elementIndex !== -1) { adElements.splice(elementIndex, 1); } // Track matched types for client briefs if (AD_TYPES.indexOf(element.type) !== -1) { updateClientBriefs(element.type); } grid[element.row][element.col] = null; element.explode(); } // Update score updateScore(elementsToRemove.length * 15); // Play powerup sound LK.getSound('powerup').play(); // Check if all briefs are completed checkBriefsCompleted(); // After a delay, collapse the grid and refill LK.setTimeout(function () { collapseGrid(); }, 400); } function collapseGrid() { var elementsMoved = false; // Move elements down to fill empty spaces for (var col = 0; col < GRID_SIZE; col++) { var emptySpaces = 0; for (var row = GRID_SIZE - 1; row >= 0; row--) { if (grid[row][col] === null) { emptySpaces++; } else if (emptySpaces > 0) { // Move element down var element = grid[row][col]; var newRow = row + emptySpaces; grid[newRow][col] = element; grid[row][col] = null; // Animate the movement element.setGridPosition(newRow, col); tween(element, { y: GRID_OFFSET_Y + newRow * CELL_SIZE + CELL_SIZE / 2 }, { duration: 300 }); elementsMoved = true; } } } // Refill the grid from the top LK.setTimeout(function () { // Create new elements for empty spaces at the top for (var col = 0; col < GRID_SIZE; col++) { for (var row = 0; row < GRID_SIZE; row++) { if (grid[row][col] === null) { var newElement = createAdElement(row, col); // Start from above the grid newElement.y = GRID_OFFSET_Y + (row - GRID_SIZE) * CELL_SIZE + CELL_SIZE / 2; // Animate falling in tween(newElement, { y: GRID_OFFSET_Y + row * CELL_SIZE + CELL_SIZE / 2 }, { duration: 300 }); } } } // Check for new matches after the grid is refilled LK.setTimeout(function () { var newMatches = findAllMatches(); if (newMatches.length > 0) { processMatches(newMatches); } else { // Check if there are possible moves if (!hasPossibleMoves()) { // No moves left, shuffle the grid shuffleGrid(); } } }, 400); }, elementsMoved ? 300 : 0); } function shuffleGrid() { // Collect all current elements var allElements = []; for (var row = 0; row < GRID_SIZE; row++) { for (var col = 0; col < GRID_SIZE; col++) { if (grid[row][col]) { allElements.push({ type: grid[row][col].type, isPowerup: grid[row][col].isPowerup }); // Remove from grid grid[row][col].destroy(); grid[row][col] = null; } } } // Clear adElements array adElements = []; // Shuffle the elements for (var i = allElements.length - 1; i > 0; i--) { var j = Math.floor(Math.random() * (i + 1)); var temp = allElements[i]; allElements[i] = allElements[j]; allElements[j] = temp; } // Refill the grid with shuffled elements var elementIndex = 0; for (var row = 0; row < GRID_SIZE; row++) { for (var col = 0; col < GRID_SIZE; col++) { if (elementIndex < allElements.length) { var element = new AdElement(allElements[elementIndex].type, row, col); element.setGridPosition(row, col); grid[row][col] = element; adElements.push(element); game.addChild(element); elementIndex++; } else { // In case we need more elements createAdElement(row, col); } } } // Check for matches after shuffling var newMatches = findAllMatches(); if (newMatches.length > 0) { // If we have matches, process them LK.setTimeout(function () { processMatches(newMatches); }, 500); } } function hasPossibleMoves() { // Check for possible moves horizontally for (var row = 0; row < GRID_SIZE; row++) { for (var col = 0; col < GRID_SIZE - 1; col++) { // Swap adjacent elements var temp = grid[row][col]; grid[row][col] = grid[row][col + 1]; grid[row][col + 1] = temp; // Check for matches var matches = findAllMatches(); // Swap back temp = grid[row][col]; grid[row][col] = grid[row][col + 1]; grid[row][col + 1] = temp; if (matches.length > 0) { return true; } } } // Check for possible moves vertically for (var row = 0; row < GRID_SIZE - 1; row++) { for (var col = 0; col < GRID_SIZE; col++) { // Swap adjacent elements var temp = grid[row][col]; grid[row][col] = grid[row + 1][col]; grid[row + 1][col] = temp; // Check for matches var matches = findAllMatches(); // Swap back temp = grid[row][col]; grid[row][col] = grid[row + 1][col]; grid[row + 1][col] = temp; if (matches.length > 0) { return true; } } } return false; } function updateScore(points) { score += points; scoreText.setText("Score: " + score); } function decrementMoves() { moveCount--; moveText.setText("Moves: " + moveCount); if (moveCount <= 0) { // Game over if no more moves gameActive = false; // Wait a bit before showing game over LK.setTimeout(function () { if (score > storage.highScore) { storage.highScore = score; } LK.setScore(score); LK.showGameOver(); }, 1000); } } function initializeClientBriefs() { // Clear existing briefs for (var i = 0; i < clientBriefs.length; i++) { clientBriefs[i].destroy(); } clientBriefs = []; // Create new client briefs based on level var briefsCount = Math.min(3, level + 1); var briefsComplexity = Math.floor(level * 1.5) + 5; var targetTypes = []; // Select random unique ad types for briefs var availableTypes = [].concat(AD_TYPES); for (var i = 0; i < briefsCount; i++) { if (availableTypes.length === 0) { break; } var index = Math.floor(Math.random() * availableTypes.length); targetTypes.push(availableTypes[index]); availableTypes.splice(index, 1); } // Create brief objects for (var i = 0; i < targetTypes.length; i++) { var count = briefsComplexity + Math.floor(Math.random() * 5); var brief = new ClientBrief(targetTypes[i], count); brief.x = 400 + i * 400; brief.y = GRID_OFFSET_Y + GRID_SIZE * CELL_SIZE + 160; clientBriefs.push(brief); game.addChild(brief); } } function updateClientBriefs(matchedType) { for (var i = 0; i < clientBriefs.length; i++) { clientBriefs[i].updateProgress(matchedType); } } function checkBriefsCompleted() { var allCompleted = true; for (var i = 0; i < clientBriefs.length; i++) { if (!clientBriefs[i].isComplete()) { allCompleted = false; break; } } if (allCompleted && clientBriefs.length > 0 && gameActive) { // Level completed gameActive = false; // Play celebration sound LK.getSound('briefComplete').play(); // Update level level++; storage.currentLevel = level; // Add bonus points var levelBonus = level * 100; updateScore(levelBonus); // Add move bonus var moveBonus = moveCount * 20; updateScore(moveBonus); // Show level complete and continue LK.setTimeout(function () { LK.setScore(score); LK.showYouWin(); }, 1200); } } // Initialize game initializeGrid(); initializeClientBriefs(); // Play background music LK.playMusic('bgmusic', { fade: { start: 0, end: 0.4, duration: 1000 } });
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
highScore: 0,
currentLevel: 1
});
/****
* Classes
****/
var AdElement = Container.expand(function (type, row, col) {
var self = Container.call(this);
self.type = type;
self.row = row;
self.col = col;
self.isPowerup = false;
var assetId;
switch (type) {
case 'social':
assetId = 'adSocial';
break;
case 'print':
assetId = 'adPrint';
break;
case 'tv':
assetId = 'adTV';
break;
case 'billboard':
assetId = 'adBillboard';
break;
case 'viral':
assetId = 'powerupViral';
self.isPowerup = true;
break;
case 'targeted':
assetId = 'powerupTargeted';
self.isPowerup = true;
break;
default:
assetId = 'adSocial';
}
var graphic = self.attachAsset(assetId, {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.9,
scaleY: 0.9
});
self.setGridPosition = function (newRow, newCol) {
self.row = newRow;
self.col = newCol;
self.x = GRID_OFFSET_X + newCol * CELL_SIZE + CELL_SIZE / 2;
self.y = GRID_OFFSET_Y + newRow * CELL_SIZE + CELL_SIZE / 2;
};
self.highlight = function () {
tween(graphic, {
scaleX: 1.1,
scaleY: 1.1
}, {
duration: 200
});
};
self.unhighlight = function () {
tween(graphic, {
scaleX: 0.9,
scaleY: 0.9
}, {
duration: 200
});
};
self.explode = function () {
tween(graphic, {
alpha: 0,
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 300,
onFinish: function onFinish() {
self.destroy();
}
});
};
self.down = function (x, y, obj) {
selectAdElement(self);
};
return self;
});
var ClientBrief = Container.expand(function (targetType, count) {
var self = Container.call(this);
self.targetType = targetType;
self.requiredCount = count;
self.currentCount = 0;
var assetId;
switch (targetType) {
case 'social':
assetId = 'adSocial';
break;
case 'print':
assetId = 'adPrint';
break;
case 'tv':
assetId = 'adTV';
break;
case 'billboard':
assetId = 'adBillboard';
break;
default:
assetId = 'adSocial';
}
var icon = self.attachAsset(assetId, {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: 0,
scaleX: 0.6,
scaleY: 0.6
});
var progressText = new Text2('0/' + count, {
size: 40,
fill: 0x333333
});
progressText.anchor.set(0, 0.5);
progressText.x = 50;
progressText.y = 0;
self.addChild(progressText);
self.updateProgress = function (matched) {
if (matched && self.targetType === matched) {
self.currentCount++;
if (self.currentCount > self.requiredCount) {
self.currentCount = self.requiredCount;
}
progressText.setText(self.currentCount + '/' + self.requiredCount);
if (self.isComplete()) {
tween(icon, {
scaleX: 0.8,
scaleY: 0.8
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(icon, {
scaleX: 0.6,
scaleY: 0.6
}, {
duration: 200,
easing: tween.easeIn
});
}
});
}
}
};
self.isComplete = function () {
return self.currentCount >= self.requiredCount;
};
return self;
});
var GridCell = Container.expand(function (row, col) {
var self = Container.call(this);
self.row = row;
self.col = col;
self.isHighlighted = false;
var background = self.attachAsset('cellBackground', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.3
});
self.setPosition = function () {
self.x = GRID_OFFSET_X + col * CELL_SIZE + CELL_SIZE / 2;
self.y = GRID_OFFSET_Y + row * CELL_SIZE + CELL_SIZE / 2;
};
self.highlight = function () {
if (!self.isHighlighted) {
self.isHighlighted = true;
tween(background, {
alpha: 0.6
}, {
duration: 200
});
}
};
self.unhighlight = function () {
if (self.isHighlighted) {
self.isHighlighted = false;
tween(background, {
alpha: 0.3
}, {
duration: 200
});
}
};
self.setPosition();
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0xf5f5f5
});
/****
* Game Code
****/
// Constants
var GRID_SIZE = 6;
var CELL_SIZE = 180;
var GRID_OFFSET_X = (2048 - GRID_SIZE * CELL_SIZE) / 2;
var GRID_OFFSET_Y = 500;
var MATCH_MIN = 3;
var AD_TYPES = ['social', 'print', 'tv', 'billboard'];
// Game variables
var grid = [];
var gridCells = [];
var adElements = [];
var selectedElement = null;
var moveCount = 30;
var score = 0;
var level = storage.currentLevel || 1;
var clientBriefs = [];
var gameActive = true;
// UI Elements
var gridBackground = LK.getAsset('gridBackground', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: GRID_SIZE * CELL_SIZE / 1100,
scaleY: GRID_SIZE * CELL_SIZE / 1100,
alpha: 0.5
});
gridBackground.x = GRID_OFFSET_X + GRID_SIZE * CELL_SIZE / 2;
gridBackground.y = GRID_OFFSET_Y + GRID_SIZE * CELL_SIZE / 2;
game.addChild(gridBackground);
var titleText = new Text2("Ad Story Blast", {
size: 130,
fill: 0x333333
});
titleText.anchor.set(0.5, 0);
titleText.x = 2048 / 2;
titleText.y = 80;
game.addChild(titleText);
var levelText = new Text2("Level: " + level, {
size: 60,
fill: 0x333333
});
levelText.anchor.set(0, 0);
levelText.x = 150;
levelText.y = 250;
game.addChild(levelText);
var scoreText = new Text2("Score: 0", {
size: 60,
fill: 0x333333
});
scoreText.anchor.set(0, 0);
scoreText.x = 150;
scoreText.y = 320;
game.addChild(scoreText);
var moveText = new Text2("Moves: " + moveCount, {
size: 60,
fill: 0x333333
});
moveText.anchor.set(1, 0);
moveText.x = 2048 - 150;
moveText.y = 250;
game.addChild(moveText);
var briefsTitle = new Text2("Client Briefs:", {
size: 60,
fill: 0x333333
});
briefsTitle.anchor.set(0, 0);
briefsTitle.x = 150;
briefsTitle.y = GRID_OFFSET_Y + GRID_SIZE * CELL_SIZE + 80;
game.addChild(briefsTitle);
var selectedOutline = LK.getAsset('selectedOutline', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.8
});
selectedOutline.visible = false;
game.addChild(selectedOutline);
// Functions
function initializeGrid() {
// Create grid cells
for (var row = 0; row < GRID_SIZE; row++) {
gridCells[row] = [];
for (var col = 0; col < GRID_SIZE; col++) {
var cell = new GridCell(row, col);
gridCells[row][col] = cell;
game.addChild(cell);
}
}
// Create empty grid
for (var row = 0; row < GRID_SIZE; row++) {
grid[row] = [];
for (var col = 0; col < GRID_SIZE; col++) {
grid[row][col] = null;
}
}
// Fill grid with ad elements
fillGrid();
}
function fillGrid() {
for (var row = 0; row < GRID_SIZE; row++) {
for (var col = 0; col < GRID_SIZE; col++) {
if (grid[row][col] === null) {
createAdElement(row, col);
}
}
}
// Check for initial matches and refill if needed
var initialMatches = findAllMatches();
if (initialMatches.length > 0) {
for (var i = 0; i < initialMatches.length; i++) {
for (var j = 0; j < initialMatches[i].length; j++) {
var element = initialMatches[i][j];
grid[element.row][element.col] = null;
element.destroy();
}
}
fillGrid();
}
}
function createAdElement(row, col) {
// Get random ad type
var typeIndex = Math.floor(Math.random() * AD_TYPES.length);
var type = AD_TYPES[typeIndex];
// Avoid creating three in a row initially
if (row >= 2) {
var prevType1 = grid[row - 1][col] ? grid[row - 1][col].type : null;
var prevType2 = grid[row - 2][col] ? grid[row - 2][col].type : null;
if (prevType1 === type && prevType2 === type) {
// Choose a different type
do {
typeIndex = Math.floor(Math.random() * AD_TYPES.length);
type = AD_TYPES[typeIndex];
} while (type === prevType1);
}
}
if (col >= 2) {
var prevType1 = grid[row][col - 1] ? grid[row][col - 1].type : null;
var prevType2 = grid[row][col - 2] ? grid[row][col - 2].type : null;
if (prevType1 === type && prevType2 === type) {
// Choose a different type
do {
typeIndex = Math.floor(Math.random() * AD_TYPES.length);
type = AD_TYPES[typeIndex];
} while (type === prevType1);
}
}
var adElement = new AdElement(type, row, col);
adElement.setGridPosition(row, col);
grid[row][col] = adElement;
adElements.push(adElement);
game.addChild(adElement);
return adElement;
}
function selectAdElement(element) {
if (!gameActive) {
return;
}
if (selectedElement === null) {
// First selection
selectedElement = element;
selectedElement.highlight();
selectedOutline.x = selectedElement.x;
selectedOutline.y = selectedElement.y;
selectedOutline.visible = true;
} else if (selectedElement === element) {
// Deselect
selectedElement.unhighlight();
selectedElement = null;
selectedOutline.visible = false;
} else {
// Check if adjacent
if (areAdjacent(selectedElement, element)) {
// Swap elements
swapElements(selectedElement, element);
} else {
// Not adjacent, change selection
selectedElement.unhighlight();
selectedElement = element;
selectedElement.highlight();
selectedOutline.x = selectedElement.x;
selectedOutline.y = selectedElement.y;
}
}
}
function areAdjacent(elem1, elem2) {
// Check if two elements are adjacent in the grid
var rowDiff = Math.abs(elem1.row - elem2.row);
var colDiff = Math.abs(elem1.col - elem2.col);
return rowDiff === 1 && colDiff === 0 || rowDiff === 0 && colDiff === 1;
}
function swapElements(elem1, elem2) {
// Store original positions
var row1 = elem1.row;
var col1 = elem1.col;
var row2 = elem2.row;
var col2 = elem2.col;
// Update grid references
grid[row1][col1] = elem2;
grid[row2][col2] = elem1;
// Update element positions
elem1.setGridPosition(row2, col2);
elem2.setGridPosition(row1, col1);
// Animate the swap
tween(elem1, {
x: GRID_OFFSET_X + col2 * CELL_SIZE + CELL_SIZE / 2,
y: GRID_OFFSET_Y + row2 * CELL_SIZE + CELL_SIZE / 2
}, {
duration: 200
});
tween(elem2, {
x: GRID_OFFSET_X + col1 * CELL_SIZE + CELL_SIZE / 2,
y: GRID_OFFSET_Y + row1 * CELL_SIZE + CELL_SIZE / 2
}, {
duration: 200,
onFinish: function onFinish() {
// Reset selection
elem1.unhighlight();
selectedElement = null;
selectedOutline.visible = false;
// Check for matches
var matches = findAllMatches();
if (matches.length > 0) {
// Valid move
decrementMoves();
processMatches(matches);
} else {
// Invalid move, swap back
grid[row1][col1] = elem1;
grid[row2][col2] = elem2;
elem1.setGridPosition(row1, col1);
elem2.setGridPosition(row2, col2);
tween(elem1, {
x: GRID_OFFSET_X + col1 * CELL_SIZE + CELL_SIZE / 2,
y: GRID_OFFSET_Y + row1 * CELL_SIZE + CELL_SIZE / 2
}, {
duration: 200
});
tween(elem2, {
x: GRID_OFFSET_X + col2 * CELL_SIZE + CELL_SIZE / 2,
y: GRID_OFFSET_Y + row2 * CELL_SIZE + CELL_SIZE / 2
}, {
duration: 200
});
}
}
});
}
function findAllMatches() {
var allMatches = [];
// Check horizontal matches
for (var row = 0; row < GRID_SIZE; row++) {
var col = 0;
while (col < GRID_SIZE - 2) {
var matchLength = 1;
var currentType = grid[row][col] ? grid[row][col].type : null;
if (currentType) {
// Count matching adjacent elements
while (col + matchLength < GRID_SIZE && grid[row][col + matchLength] && grid[row][col + matchLength].type === currentType) {
matchLength++;
}
// If we have a match of at least 3
if (matchLength >= MATCH_MIN) {
var match = [];
for (var i = 0; i < matchLength; i++) {
match.push(grid[row][col + i]);
}
allMatches.push(match);
col += matchLength;
} else {
col++;
}
} else {
col++;
}
}
}
// Check vertical matches
for (var col = 0; col < GRID_SIZE; col++) {
var row = 0;
while (row < GRID_SIZE - 2) {
var matchLength = 1;
var currentType = grid[row][col] ? grid[row][col].type : null;
if (currentType) {
// Count matching adjacent elements
while (row + matchLength < GRID_SIZE && grid[row + matchLength][col] && grid[row + matchLength][col].type === currentType) {
matchLength++;
}
// If we have a match of at least 3
if (matchLength >= MATCH_MIN) {
var match = [];
for (var i = 0; i < matchLength; i++) {
match.push(grid[row + i][col]);
}
allMatches.push(match);
row += matchLength;
} else {
row++;
}
} else {
row++;
}
}
}
return allMatches;
}
function processMatches(matches) {
// Play sound
LK.getSound('match').play();
// Calculate points and remove matched elements
var pointsEarned = 0;
var matchedTypes = {};
for (var i = 0; i < matches.length; i++) {
var match = matches[i];
var matchType = match[0].type;
var matchSize = match.length;
// Keep track of matched types for client briefs
if (!matchedTypes[matchType]) {
matchedTypes[matchType] = 0;
}
matchedTypes[matchType] += matchSize;
// Calculate points (more points for larger matches)
var matchPoints = 10 * matchSize;
pointsEarned += matchPoints;
// Create powerup for matches of 4 or more
var createPowerup = false;
var powerupType = null;
var powerupRow = null;
var powerupCol = null;
if (matchSize >= 4) {
createPowerup = true;
powerupType = matchSize >= 5 ? 'viral' : 'targeted';
// Use middle element position for powerup
var middleIndex = Math.floor(matchSize / 2);
powerupRow = match[middleIndex].row;
powerupCol = match[middleIndex].col;
}
// Remove matched elements
for (var j = 0; j < matchSize; j++) {
var element = match[j];
var elementIndex = adElements.indexOf(element);
if (elementIndex !== -1) {
adElements.splice(elementIndex, 1);
}
grid[element.row][element.col] = null;
element.explode();
}
// Create powerup if needed
if (createPowerup) {
// Wait for animations to complete
LK.setTimeout(function (row, col, type) {
return function () {
var powerup = new AdElement(type, row, col);
powerup.setGridPosition(row, col);
grid[row][col] = powerup;
adElements.push(powerup);
game.addChild(powerup);
LK.getSound('powerup').play();
};
}(powerupRow, powerupCol, powerupType), 300);
}
}
// Update score
updateScore(pointsEarned);
// Update client briefs
for (var type in matchedTypes) {
if (matchedTypes.hasOwnProperty(type) && AD_TYPES.indexOf(type) !== -1) {
updateClientBriefs(type);
}
}
// Check if all briefs are completed
checkBriefsCompleted();
// After a delay, collapse the grid and refill
LK.setTimeout(function () {
collapseGrid();
}, 400);
}
function processPowerup(powerup, targetElement) {
var elementsToRemove = [];
if (powerup.type === 'viral') {
// Viral powerup: clear entire row and column
for (var col = 0; col < GRID_SIZE; col++) {
if (grid[powerup.row][col] && grid[powerup.row][col] !== powerup) {
elementsToRemove.push(grid[powerup.row][col]);
}
}
for (var row = 0; row < GRID_SIZE; row++) {
if (grid[row][powerup.col] && grid[row][powerup.col] !== powerup && elementsToRemove.indexOf(grid[row][powerup.col]) === -1) {
elementsToRemove.push(grid[row][powerup.col]);
}
}
} else if (powerup.type === 'targeted') {
// Targeted powerup: remove all elements of the same type as the target
var targetType = targetElement.type;
for (var row = 0; row < GRID_SIZE; row++) {
for (var col = 0; col < GRID_SIZE; col++) {
if (grid[row][col] && grid[row][col].type === targetType && !grid[row][col].isPowerup) {
elementsToRemove.push(grid[row][col]);
}
}
}
}
// Remove powerup
var powerupIndex = adElements.indexOf(powerup);
if (powerupIndex !== -1) {
adElements.splice(powerupIndex, 1);
}
grid[powerup.row][powerup.col] = null;
powerup.explode();
// Remove affected elements
for (var i = 0; i < elementsToRemove.length; i++) {
var element = elementsToRemove[i];
var elementIndex = adElements.indexOf(element);
if (elementIndex !== -1) {
adElements.splice(elementIndex, 1);
}
// Track matched types for client briefs
if (AD_TYPES.indexOf(element.type) !== -1) {
updateClientBriefs(element.type);
}
grid[element.row][element.col] = null;
element.explode();
}
// Update score
updateScore(elementsToRemove.length * 15);
// Play powerup sound
LK.getSound('powerup').play();
// Check if all briefs are completed
checkBriefsCompleted();
// After a delay, collapse the grid and refill
LK.setTimeout(function () {
collapseGrid();
}, 400);
}
function collapseGrid() {
var elementsMoved = false;
// Move elements down to fill empty spaces
for (var col = 0; col < GRID_SIZE; col++) {
var emptySpaces = 0;
for (var row = GRID_SIZE - 1; row >= 0; row--) {
if (grid[row][col] === null) {
emptySpaces++;
} else if (emptySpaces > 0) {
// Move element down
var element = grid[row][col];
var newRow = row + emptySpaces;
grid[newRow][col] = element;
grid[row][col] = null;
// Animate the movement
element.setGridPosition(newRow, col);
tween(element, {
y: GRID_OFFSET_Y + newRow * CELL_SIZE + CELL_SIZE / 2
}, {
duration: 300
});
elementsMoved = true;
}
}
}
// Refill the grid from the top
LK.setTimeout(function () {
// Create new elements for empty spaces at the top
for (var col = 0; col < GRID_SIZE; col++) {
for (var row = 0; row < GRID_SIZE; row++) {
if (grid[row][col] === null) {
var newElement = createAdElement(row, col);
// Start from above the grid
newElement.y = GRID_OFFSET_Y + (row - GRID_SIZE) * CELL_SIZE + CELL_SIZE / 2;
// Animate falling in
tween(newElement, {
y: GRID_OFFSET_Y + row * CELL_SIZE + CELL_SIZE / 2
}, {
duration: 300
});
}
}
}
// Check for new matches after the grid is refilled
LK.setTimeout(function () {
var newMatches = findAllMatches();
if (newMatches.length > 0) {
processMatches(newMatches);
} else {
// Check if there are possible moves
if (!hasPossibleMoves()) {
// No moves left, shuffle the grid
shuffleGrid();
}
}
}, 400);
}, elementsMoved ? 300 : 0);
}
function shuffleGrid() {
// Collect all current elements
var allElements = [];
for (var row = 0; row < GRID_SIZE; row++) {
for (var col = 0; col < GRID_SIZE; col++) {
if (grid[row][col]) {
allElements.push({
type: grid[row][col].type,
isPowerup: grid[row][col].isPowerup
});
// Remove from grid
grid[row][col].destroy();
grid[row][col] = null;
}
}
}
// Clear adElements array
adElements = [];
// Shuffle the elements
for (var i = allElements.length - 1; i > 0; i--) {
var j = Math.floor(Math.random() * (i + 1));
var temp = allElements[i];
allElements[i] = allElements[j];
allElements[j] = temp;
}
// Refill the grid with shuffled elements
var elementIndex = 0;
for (var row = 0; row < GRID_SIZE; row++) {
for (var col = 0; col < GRID_SIZE; col++) {
if (elementIndex < allElements.length) {
var element = new AdElement(allElements[elementIndex].type, row, col);
element.setGridPosition(row, col);
grid[row][col] = element;
adElements.push(element);
game.addChild(element);
elementIndex++;
} else {
// In case we need more elements
createAdElement(row, col);
}
}
}
// Check for matches after shuffling
var newMatches = findAllMatches();
if (newMatches.length > 0) {
// If we have matches, process them
LK.setTimeout(function () {
processMatches(newMatches);
}, 500);
}
}
function hasPossibleMoves() {
// Check for possible moves horizontally
for (var row = 0; row < GRID_SIZE; row++) {
for (var col = 0; col < GRID_SIZE - 1; col++) {
// Swap adjacent elements
var temp = grid[row][col];
grid[row][col] = grid[row][col + 1];
grid[row][col + 1] = temp;
// Check for matches
var matches = findAllMatches();
// Swap back
temp = grid[row][col];
grid[row][col] = grid[row][col + 1];
grid[row][col + 1] = temp;
if (matches.length > 0) {
return true;
}
}
}
// Check for possible moves vertically
for (var row = 0; row < GRID_SIZE - 1; row++) {
for (var col = 0; col < GRID_SIZE; col++) {
// Swap adjacent elements
var temp = grid[row][col];
grid[row][col] = grid[row + 1][col];
grid[row + 1][col] = temp;
// Check for matches
var matches = findAllMatches();
// Swap back
temp = grid[row][col];
grid[row][col] = grid[row + 1][col];
grid[row + 1][col] = temp;
if (matches.length > 0) {
return true;
}
}
}
return false;
}
function updateScore(points) {
score += points;
scoreText.setText("Score: " + score);
}
function decrementMoves() {
moveCount--;
moveText.setText("Moves: " + moveCount);
if (moveCount <= 0) {
// Game over if no more moves
gameActive = false;
// Wait a bit before showing game over
LK.setTimeout(function () {
if (score > storage.highScore) {
storage.highScore = score;
}
LK.setScore(score);
LK.showGameOver();
}, 1000);
}
}
function initializeClientBriefs() {
// Clear existing briefs
for (var i = 0; i < clientBriefs.length; i++) {
clientBriefs[i].destroy();
}
clientBriefs = [];
// Create new client briefs based on level
var briefsCount = Math.min(3, level + 1);
var briefsComplexity = Math.floor(level * 1.5) + 5;
var targetTypes = [];
// Select random unique ad types for briefs
var availableTypes = [].concat(AD_TYPES);
for (var i = 0; i < briefsCount; i++) {
if (availableTypes.length === 0) {
break;
}
var index = Math.floor(Math.random() * availableTypes.length);
targetTypes.push(availableTypes[index]);
availableTypes.splice(index, 1);
}
// Create brief objects
for (var i = 0; i < targetTypes.length; i++) {
var count = briefsComplexity + Math.floor(Math.random() * 5);
var brief = new ClientBrief(targetTypes[i], count);
brief.x = 400 + i * 400;
brief.y = GRID_OFFSET_Y + GRID_SIZE * CELL_SIZE + 160;
clientBriefs.push(brief);
game.addChild(brief);
}
}
function updateClientBriefs(matchedType) {
for (var i = 0; i < clientBriefs.length; i++) {
clientBriefs[i].updateProgress(matchedType);
}
}
function checkBriefsCompleted() {
var allCompleted = true;
for (var i = 0; i < clientBriefs.length; i++) {
if (!clientBriefs[i].isComplete()) {
allCompleted = false;
break;
}
}
if (allCompleted && clientBriefs.length > 0 && gameActive) {
// Level completed
gameActive = false;
// Play celebration sound
LK.getSound('briefComplete').play();
// Update level
level++;
storage.currentLevel = level;
// Add bonus points
var levelBonus = level * 100;
updateScore(levelBonus);
// Add move bonus
var moveBonus = moveCount * 20;
updateScore(moveBonus);
// Show level complete and continue
LK.setTimeout(function () {
LK.setScore(score);
LK.showYouWin();
}, 1200);
}
}
// Initialize game
initializeGrid();
initializeClientBriefs();
// Play background music
LK.playMusic('bgmusic', {
fade: {
start: 0,
end: 0.4,
duration: 1000
}
});
Ad billboard. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
A ad Print. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
A social ad. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
TV ad. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows