/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1"); /**** * Classes ****/ var FoodItem = Container.expand(function (foodType) { var self = Container.call(this); self.foodType = foodType; self.gridX = -1; self.gridY = -1; self.isMatched = false; self.isAnimating = false; var foodGraphics = self.attachAsset(foodType, { anchorX: 0.5, anchorY: 0.5 }); self.setGridPosition = function (gridX, gridY) { self.gridX = gridX; self.gridY = gridY; self.x = GRID_START_X + gridX * CELL_SIZE + CELL_SIZE / 2; self.y = GRID_START_Y + gridY * CELL_SIZE + CELL_SIZE / 2; }; self.animateToPosition = function (targetX, targetY, callback) { self.isAnimating = true; tween(self, { x: targetX, y: targetY }, { duration: 300, easing: tween.easeOut, onFinish: function onFinish() { self.isAnimating = false; if (callback) callback(); } }); }; self.animateMatch = function (callback) { self.isMatched = true; // First make it bigger and sparkly tween(self, { scaleX: 1.3, scaleY: 1.3, tint: 0xFFD700 }, { duration: 150, easing: tween.bounceOut }); // Then shrink away with stars effect tween(self, { scaleX: 0, scaleY: 0, alpha: 0, rotation: Math.PI * 2 }, { duration: 300, easing: tween.easeIn, onFinish: callback }); }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x87CEEB }); /**** * Game Code ****/ var GRID_SIZE = 8; var CELL_SIZE = 240; var GRID_START_X = (2048 - GRID_SIZE * CELL_SIZE) / 2; var GRID_START_Y = 300; var FOOD_TYPES = ['apple', 'banana', 'orange', 'grape', 'lemon', 'hamburger', 'hotdog', 'pizza']; var grid = []; var selectedFood = null; var isProcessingMatches = false; var movesLeft = 30; var currentScore = 0; var targetMatches = 10; var matchesCleared = 0; var swipeStartX = 0; var swipeStartY = 0; var swipeFood = null; var isSwipeActive = false; var SWIPE_THRESHOLD = 50; var currentLevel = 1; var maxUnlockedLevel = 1; var totalScore = 0; var gameState = 'playing'; var levelCompleteShown = false; // Text input variables var currentInputField = null; var usernameText = ''; var passwordText = ''; var keyboardContainer = null; var inputDisplayText = null; var isKeyboardVisible = false; var usernameDisplayText = null; var passwordDisplayText = null; var createUsernameDisplayText = null; var createPasswordDisplayText = null; // Initialize grid array function getInputText() { if (currentInputField === 'username') { return usernameText; } else if (currentInputField === 'password') { // Show asterisks for password var asterisks = ''; for (var i = 0; i < passwordText.length; i++) { asterisks += '*'; } return asterisks; } return ''; } function showKeyboard(inputType) { if (isKeyboardVisible) return; isKeyboardVisible = true; currentInputField = inputType; // Create keyboard container keyboardContainer = new Container(); game.addChild(keyboardContainer); // Keyboard popup without background // Create input display var inputBg = LK.getAsset('inputField', { anchorX: 0.5, anchorY: 0.5, scaleX: 10, scaleY: 2 }); inputBg.x = 2048 / 2; inputBg.y = 1600; keyboardContainer.addChild(inputBg); // Create input text display inputDisplayText = new Text2(getInputText(), { size: 80, fill: 0xFFFFFF }); inputDisplayText.anchor.set(0.5, 0.5); inputDisplayText.x = 2048 / 2; inputDisplayText.y = 1600; keyboardContainer.addChild(inputDisplayText); // Create keyboard layout var keys = [['Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P'], ['A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L'], ['Z', 'X', 'C', 'V', 'B', 'N', 'M']]; var startY = 1800; var keySize = 160; var keySpacing = 200; // Create letter keys for (var row = 0; row < keys.length; row++) { var rowKeys = keys[row]; var totalWidth = rowKeys.length * keySpacing; var startX = (2048 - totalWidth) / 2 + keySpacing / 2; for (var col = 0; col < rowKeys.length; col++) { createKey(rowKeys[col], startX + col * keySpacing, startY + row * keySpacing); } } // Create number row var numbers = ['1', '2', '3', '4', '5', '6', '7', '8', '9', '0']; var numberStartY = startY - keySpacing; var numberTotalWidth = numbers.length * keySpacing; var numberStartX = (2048 - numberTotalWidth) / 2 + keySpacing / 2; for (var i = 0; i < numbers.length; i++) { createKey(numbers[i], numberStartX + i * keySpacing, numberStartY); } // Create special keys createSpecialKey('SPACE', 2048 / 2, startY + 3 * keySpacing, 4, function () { addCharacter(' '); }); createSpecialKey('DELETE', 2048 / 2 - 400, startY + 3 * keySpacing, 2, function () { deleteCharacter(); }); createSpecialKey('DONE', 2048 / 2 + 400, startY + 3 * keySpacing, 2, function () { hideKeyboard(); }); // Create close button var closeButton = LK.getAsset('xButton', { anchorX: 0.5, anchorY: 0.5 }); closeButton.x = 2048 / 2 + 500; closeButton.y = 1500; closeButton.down = function () { hideKeyboard(); }; keyboardContainer.addChild(closeButton); // Start keyboard below screen and animate up keyboardContainer.y = 800; tween(keyboardContainer, { y: 0 }, { duration: 300, easing: tween.easeOut }); } function createKey(letter, x, y) { var keyButton = new Container(); var keyBg = LK.getAsset('gridCell', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.2, scaleY: 1.2 }); keyBg.tint = 0x4CAF50; keyButton.addChild(keyBg); var keyText = new Text2(letter, { size: 70, fill: 0xFFFFFF }); keyText.anchor.set(0.5, 0.5); keyButton.addChild(keyText); keyButton.x = x; keyButton.y = y; keyButton.down = function () { LK.getSound('buttonClick').play(); tween(keyButton, { scaleX: 0.9, scaleY: 0.9 }, { duration: 100, easing: tween.easeOut }); tween(keyButton, { scaleX: 1, scaleY: 1 }, { duration: 150, easing: tween.bounceOut }); addCharacter(letter); }; keyboardContainer.addChild(keyButton); } function createSpecialKey(text, x, y, widthScale, action) { var keyButton = new Container(); var keyBg = LK.getAsset('gridCell', { anchorX: 0.5, anchorY: 0.5, scaleX: widthScale * 0.8, scaleY: 1.2 }); keyBg.tint = 0xFF9800; keyButton.addChild(keyBg); var keyText = new Text2(text, { size: 60, fill: 0xFFFFFF }); keyText.anchor.set(0.5, 0.5); keyButton.addChild(keyText); keyButton.x = x; keyButton.y = y; keyButton.down = function () { LK.getSound('buttonClick').play(); tween(keyButton, { scaleX: 0.9, scaleY: 0.9 }, { duration: 100, easing: tween.easeOut }); tween(keyButton, { scaleX: 1, scaleY: 1 }, { duration: 150, easing: tween.bounceOut }); action(); }; keyboardContainer.addChild(keyButton); } function addCharacter(_char) { if (currentInputField === 'username') { if (usernameText.length < 15) { usernameText += _char; } } else if (currentInputField === 'password') { if (passwordText.length < 15) { passwordText += _char; } } updateInputDisplay(); } function deleteCharacter() { if (currentInputField === 'username') { if (usernameText.length > 0) { usernameText = usernameText.slice(0, -1); } } else if (currentInputField === 'password') { if (passwordText.length > 0) { passwordText = passwordText.slice(0, -1); } } updateInputDisplay(); } function updateInputDisplay() { if (inputDisplayText) { inputDisplayText.setText(getInputText()); } // Update field displays on login screen if (gameState === 'loginScreen') { if (usernameDisplayText) { usernameDisplayText.setText(usernameText); } if (passwordDisplayText) { var asterisks = ''; for (var i = 0; i < passwordText.length; i++) { asterisks += '*'; } passwordDisplayText.setText(asterisks); } } // Update field displays on create account screen if (gameState === 'createAccountScreen') { if (createUsernameDisplayText) { createUsernameDisplayText.setText(usernameText); } if (createPasswordDisplayText) { var asterisks = ''; for (var i = 0; i < passwordText.length; i++) { asterisks += '*'; } createPasswordDisplayText.setText(asterisks); } } } function hideKeyboard() { if (keyboardContainer) { tween(keyboardContainer, { y: 800 }, { duration: 300, easing: tween.easeIn, onFinish: function onFinish() { keyboardContainer.destroy(); keyboardContainer = null; inputDisplayText = null; isKeyboardVisible = false; currentInputField = null; } }); } } var _loop = function _loop() { grid[x] = []; for (y = 0; y < GRID_SIZE; y++) { grid[x][y] = null; } }, y; for (var x = 0; x < GRID_SIZE; x++) { _loop(); } // Create grid background var gridBackground = new Container(); game.addChild(gridBackground); for (var x = 0; x < GRID_SIZE; x++) { for (var y = 0; y < GRID_SIZE; y++) { var cell = LK.getAsset('gridCell', { anchorX: 0.5, anchorY: 0.5 }); cell.x = GRID_START_X + x * CELL_SIZE + CELL_SIZE / 2; cell.y = GRID_START_Y + y * CELL_SIZE + CELL_SIZE / 2; cell.alpha = 0.3; gridBackground.addChild(cell); } } // Create UI var scoreText = new Text2('Score: 0', { size: 100, fill: 0xFFFFFF }); scoreText.anchor.set(0.5, 0); LK.gui.top.addChild(scoreText); scoreText.y = 120; var movesText = new Text2('Moves: 30', { size: 80, fill: 0xFFFFFF }); movesText.anchor.set(0, 0); LK.gui.topLeft.addChild(movesText); movesText.x = 150; movesText.y = 50; var tutorialText = new Text2('Lvl' + currentLevel, { size: 120, fill: 0xFFFFFF }); tutorialText.anchor.set(0.5, 0); LK.gui.top.addChild(tutorialText); tutorialText.y = 20; var targetText = new Text2('Target: ' + targetMatches, { size: 100, fill: 0xFFFFFF }); targetText.anchor.set(0, 0); LK.gui.top.addChild(targetText); targetText.x = tutorialText.x + tutorialText.width / 2 + 50; targetText.y = tutorialText.y; function getRandomFoodType(x, y) { // If position is provided, try to create squares by matching nearby foods if (x !== undefined && y !== undefined) { // Collect nearby food types that could form squares var nearbyTypes = []; // Check adjacent positions for potential square formation var positions = [{ dx: -1, dy: 0 }, { dx: 1, dy: 0 }, { dx: 0, dy: -1 }, { dx: 0, dy: 1 }, // Adjacent { dx: -1, dy: -1 }, { dx: 1, dy: -1 }, { dx: -1, dy: 1 }, { dx: 1, dy: 1 } // Diagonal ]; for (var i = 0; i < positions.length; i++) { var checkX = x + positions[i].dx; var checkY = y + positions[i].dy; if (isValidPosition(checkX, checkY) && grid[checkX] && grid[checkX][checkY]) { nearbyTypes.push(grid[checkX][checkY].foodType); } } // 40% chance to match a nearby type if any exist if (nearbyTypes.length > 0 && Math.random() < 0.4) { return nearbyTypes[Math.floor(Math.random() * nearbyTypes.length)]; } } return FOOD_TYPES[Math.floor(Math.random() * FOOD_TYPES.length)]; } function createFoodItem(x, y) { var foodType = getRandomFoodType(x, y); var food = new FoodItem(foodType); food.setGridPosition(x, y); grid[x][y] = food; game.addChild(food); // Add fun spawn animation with rainbow colors food.scaleX = 0; food.scaleY = 0; food.alpha = 0; var rainbowColors = [0xFF6B6B, 0x4ECDC4, 0x45B7D1, 0x96CEB4, 0xFECA57, 0xFF9FF3, 0x54A0FF]; var randomColor = rainbowColors[Math.floor(Math.random() * rainbowColors.length)]; food.tint = randomColor; tween(food, { scaleX: 1, scaleY: 1, alpha: 1 }, { duration: 500, easing: tween.bounceOut }); // Reset to normal color after spawn tween(food, { tint: 0xFFFFFF }, { duration: 800, easing: tween.easeOut }); return food; } function initializeGrid() { for (var x = 0; x < GRID_SIZE; x++) { for (var y = 0; y < GRID_SIZE; y++) { createFoodItem(x, y); } } // Remove initial matches var foundMatches = true; while (foundMatches) { var matches = findAllMatches(); if (matches.length > 0) { for (var i = 0; i < matches.length; i++) { var match = matches[i]; if (grid[match.x] && grid[match.x][match.y]) { grid[match.x][match.y].destroy(); grid[match.x][match.y] = null; } } fillEmptySpaces(); } else { foundMatches = false; } } } function isValidPosition(x, y) { return x >= 0 && x < GRID_SIZE && y >= 0 && y < GRID_SIZE; } function isAdjacent(x1, y1, x2, y2) { var dx = Math.abs(x1 - x2); var dy = Math.abs(y1 - y2); return dx === 1 && dy === 0 || dx === 0 && dy === 1; } function swapFoods(food1, food2) { if (!food1 || !food2) return false; var x1 = food1.gridX; var y1 = food1.gridY; var x2 = food2.gridX; var y2 = food2.gridY; // Swap in grid grid[x1][y1] = food2; grid[x2][y2] = food1; // Update grid positions food1.setGridPosition(x2, y2); food2.setGridPosition(x1, y1); // Animate swap var targetX1 = GRID_START_X + x2 * CELL_SIZE + CELL_SIZE / 2; var targetY1 = GRID_START_Y + y2 * CELL_SIZE + CELL_SIZE / 2; var targetX2 = GRID_START_X + x1 * CELL_SIZE + CELL_SIZE / 2; var targetY2 = GRID_START_Y + y1 * CELL_SIZE + CELL_SIZE / 2; food1.animateToPosition(targetX1, targetY1); food2.animateToPosition(targetX2, targetY2); return true; } function findMatches(x, y) { var matches = []; if (!grid[x] || !grid[x][y]) return matches; var foodType = grid[x][y].foodType; // Check horizontal matches var horizontalMatches = [{ x: x, y: y }]; // Check left for (var i = x - 1; i >= 0; i--) { if (grid[i] && grid[i][y] && grid[i][y].foodType === foodType) { horizontalMatches.push({ x: i, y: y }); } else { break; } } // Check right for (var i = x + 1; i < GRID_SIZE; i++) { if (grid[i] && grid[i][y] && grid[i][y].foodType === foodType) { horizontalMatches.push({ x: i, y: y }); } else { break; } } if (horizontalMatches.length >= 3) { matches = matches.concat(horizontalMatches); } // Check vertical matches var verticalMatches = [{ x: x, y: y }]; // Check up for (var i = y - 1; i >= 0; i--) { if (grid[x] && grid[x][i] && grid[x][i].foodType === foodType) { verticalMatches.push({ x: x, y: i }); } else { break; } } // Check down for (var i = y + 1; i < GRID_SIZE; i++) { if (grid[x] && grid[x][i] && grid[x][i].foodType === foodType) { verticalMatches.push({ x: x, y: i }); } else { break; } } if (verticalMatches.length >= 3) { matches = matches.concat(verticalMatches); } return matches; } function findSquares() { var allSquares = []; // Check for 2x2 squares for (var x = 0; x < GRID_SIZE - 1; x++) { for (var y = 0; y < GRID_SIZE - 1; y++) { // Check if all positions have valid food items if (grid[x] && grid[x][y] && grid[x + 1] && grid[x + 1][y] && grid[x] && grid[x][y + 1] && grid[x + 1] && grid[x + 1][y + 1]) { var topLeft = grid[x][y]; var topRight = grid[x + 1][y]; var bottomLeft = grid[x][y + 1]; var bottomRight = grid[x + 1][y + 1]; // Check if all 4 foods exist, are the same type and not already matched if (topLeft && topRight && bottomLeft && bottomRight && topLeft.foodType === topRight.foodType && topLeft.foodType === bottomLeft.foodType && topLeft.foodType === bottomRight.foodType && !topLeft.isMatched && !topRight.isMatched && !bottomLeft.isMatched && !bottomRight.isMatched) { allSquares.push([{ x: x, y: y }, { x: x + 1, y: y }, { x: x, y: y + 1 }, { x: x + 1, y: y + 1 }]); } } } } return allSquares; } function findAllMatches() { var allMatches = []; var processed = {}; for (var x = 0; x < GRID_SIZE; x++) { for (var y = 0; y < GRID_SIZE; y++) { if (grid[x] && grid[x][y] && !processed[x + ',' + y]) { var matches = findMatches(x, y); for (var i = 0; i < matches.length; i++) { var match = matches[i]; var key = match.x + ',' + match.y; if (!processed[key]) { allMatches.push(match); processed[key] = true; } } } } } return allMatches; } function processMatches() { if (isProcessingMatches) return; // First check for squares var squares = findSquares(); var matches = findAllMatches(); if (squares.length === 0 && matches.length === 0) return; isProcessingMatches = true; LK.getSound('match').play(); var totalClearedCount = 0; var finalMatches = []; // Process squares first - clear entire rows if (squares.length > 0) { var rowsToClear = []; // Collect all unique rows that contain squares for (var s = 0; s < squares.length; s++) { var square = squares[s]; for (var p = 0; p < square.length; p++) { var rowY = square[p].y; if (rowsToClear.indexOf(rowY) === -1) { rowsToClear.push(rowY); } } } // Clear all foods in the identified rows for (var r = 0; r < rowsToClear.length; r++) { var row = rowsToClear[r]; for (var x = 0; x < GRID_SIZE; x++) { if (grid[x] && grid[x][row] && !grid[x][row].isMatched) { finalMatches.push({ x: x, y: row }); grid[x][row].isMatched = true; // Mark as matched immediately } } } totalClearedCount = finalMatches.length; // Special celebration for square clearing LK.effects.flashScreen(0x00FF00, 800); currentScore += totalClearedCount * 20; // Bonus points for row clearing } else { // Regular match processing finalMatches = matches; totalClearedCount = matches.length; currentScore += totalClearedCount * 10; } matchesCleared += totalClearedCount; targetMatches = Math.max(0, targetMatches - Math.floor(totalClearedCount / 3)); updateUI(); // Update total score totalScore += squares.length > 0 ? totalClearedCount * 20 : totalClearedCount * 10; // Save high score to storage var highScore = storage.highScore || 0; if (totalScore > highScore) { storage.highScore = totalScore; } // Auto-save user progress saveUserProgress(); // Play squeaky voice when getting points LK.getSound('goodJobBooby').play(); // Add celebration effects for bigger matches if (totalClearedCount >= 4 || squares.length > 0) { // Big match celebration! if (squares.length === 0) { LK.effects.flashScreen(0xFFD700, 500); } // Make score text dance tween(scoreText, { scaleX: 1.3, scaleY: 1.3 }, { duration: 300, easing: tween.bounceOut }); tween(scoreText, { scaleX: 1, scaleY: 1 }, { duration: 300, easing: tween.bounceOut }); } var animationsCompleted = 0; for (var i = 0; i < finalMatches.length; i++) { var match = finalMatches[i]; if (grid[match.x] && grid[match.x][match.y]) { // Mark as matched before animation grid[match.x][match.y].isMatched = true; grid[match.x][match.y].animateMatch(function () { animationsCompleted++; if (animationsCompleted === totalClearedCount) { // Remove matched foods for (var j = 0; j < finalMatches.length; j++) { var m = finalMatches[j]; if (grid[m.x] && grid[m.x][m.y]) { grid[m.x][m.y].destroy(); grid[m.x][m.y] = null; } } fillEmptySpaces(); LK.setTimeout(function () { isProcessingMatches = false; processMatches(); // Check for cascade matches }, 300); } }); } } if (totalClearedCount === 0) { isProcessingMatches = false; } } function fillEmptySpaces() { // Drop existing foods for (var x = 0; x < GRID_SIZE; x++) { var writeIndex = GRID_SIZE - 1; for (var y = GRID_SIZE - 1; y >= 0; y--) { if (grid[x][y] !== null) { if (y !== writeIndex) { grid[x][writeIndex] = grid[x][y]; grid[x][y] = null; grid[x][writeIndex].setGridPosition(x, writeIndex); var targetY = GRID_START_Y + writeIndex * CELL_SIZE + CELL_SIZE / 2; grid[x][writeIndex].animateToPosition(grid[x][writeIndex].x, targetY); } writeIndex--; } } // Create new foods for empty spaces at top for (var y = 0; y <= writeIndex; y++) { var food = new FoodItem(getRandomFoodType(x, y)); food.x = GRID_START_X + x * CELL_SIZE + CELL_SIZE / 2; food.y = GRID_START_Y - (writeIndex - y + 1) * CELL_SIZE + CELL_SIZE / 2; food.gridX = x; food.gridY = y; grid[x][y] = food; game.addChild(food); var targetY = GRID_START_Y + y * CELL_SIZE + CELL_SIZE / 2; food.animateToPosition(food.x, targetY); } } } function updateUI() { if (scoreText) { scoreText.setText('Score: ' + currentScore); } if (movesText) { movesText.setText('Moves: ' + movesLeft); } if (targetText) { targetText.setText('Target: ' + targetMatches); } } function checkGameEnd() { if (targetMatches <= 1 && targetMatches > 0) { // Create guaranteed 2x2 square at front of board createGuaranteedSquare(); } if (targetMatches <= 0) { if (currentLevel === 1 && !levelCompleteShown) { showLevelComplete(); } else { completeLevel(); } } else if (movesLeft <= 0) { LK.showGameOver(); } } function createGuaranteedSquare() { // Choose a random food type for the square var squareFoodType = FOOD_TYPES[Math.floor(Math.random() * FOOD_TYPES.length)]; // Find the best 2x2 position at the front (top rows) of the board var bestX = 0; var bestY = 0; // Try to place in top-left area first for visibility for (var x = 0; x <= GRID_SIZE - 2; x++) { for (var y = 0; y <= 2; y++) { // Focus on top 3 rows if (isValidPosition(x, y) && isValidPosition(x + 1, y) && isValidPosition(x, y + 1) && isValidPosition(x + 1, y + 1)) { bestX = x; bestY = y; break; } } if (bestY <= 2) break; } // Create the 2x2 square by replacing existing foods var positions = [{ x: bestX, y: bestY }, { x: bestX + 1, y: bestY }, { x: bestX, y: bestY + 1 }, { x: bestX + 1, y: bestY + 1 }]; for (var i = 0; i < positions.length; i++) { var pos = positions[i]; // Remove existing food if any if (grid[pos.x] && grid[pos.x][pos.y]) { grid[pos.x][pos.y].destroy(); } // Create new food of the same type var newFood = new FoodItem(squareFoodType); newFood.setGridPosition(pos.x, pos.y); grid[pos.x][pos.y] = newFood; game.addChild(newFood); // Add special visual effect to highlight the square newFood.tint = 0xFFD700; // Gold tint // Add pulsing animation to make it obvious tween(newFood, { scaleX: 1.2, scaleY: 1.2 }, { duration: 500, easing: tween.easeInOut }); tween(newFood, { scaleX: 1, scaleY: 1 }, { duration: 500, easing: tween.easeInOut }); } // Flash screen to draw attention LK.effects.flashScreen(0xFFD700, 1000); } function showLevelComplete() { levelCompleteShown = true; gameState = 'levelComplete'; // Play celebration music LK.playMusic('celebrationMusic', { loop: false }); // Play level complete sound LK.getSound('levelComplete').play(); // Add celebration effects LK.effects.flashScreen(0xFFD700, 1000); // Make all UI elements dance tween(scoreText, { scaleX: 1.5, scaleY: 1.5, rotation: 0.2 }, { duration: 500, easing: tween.bounceOut }); tween(tutorialText, { scaleX: 1.3, scaleY: 1.3, rotation: -0.1 }, { duration: 600, easing: tween.bounceOut }); tween(targetText, { scaleX: 1.2, scaleY: 1.2, rotation: 0.15 }, { duration: 700, easing: tween.bounceOut }); LK.setTimeout(function () { showWhiteScreen(); }, 3000); } function showWhiteScreen() { gameState = 'whiteScreen'; // Clear all game elements game.removeChildren(); // Clear tutorial UI elements if (tutorialText && tutorialText.parent) { tutorialText.destroy(); tutorialText = null; } if (targetText && targetText.parent) { targetText.destroy(); targetText = null; } if (scoreText && scoreText.parent) { scoreText.destroy(); scoreText = null; } if (movesText && movesText.parent) { movesText.destroy(); movesText = null; } // Set white background game.setBackgroundColor(0xFFFFFF); // Show white screen for 2 seconds then show home screen LK.setTimeout(function () { showHomeScreen(); }, 2000); } function showHomeScreen() { gameState = 'homeScreen'; // Load user progress from storage loadUserProgress(); // Clear all game elements game.removeChildren(); // Set game background color game.setBackgroundColor(0x87CEEB); // Start playing happy background music LK.playMusic('happyBgMusic', { loop: true }); // Clear all UI elements from the screen if (tutorialText && tutorialText.parent) { tutorialText.destroy(); tutorialText = null; } if (targetText && targetText.parent) { targetText.destroy(); targetText = null; } if (scoreText && scoreText.parent) { scoreText.destroy(); scoreText = null; } if (movesText && movesText.parent) { movesText.destroy(); movesText = null; } // Get user data from storage var currentUser = storage.currentUser || null; var isLoggedIn = currentUser !== null; // Create home screen UI var titleText = new Text2('FOOD MATCH', { size: 200, fill: 0xFFFFFF }); titleText.anchor.set(0.5, 0.5); titleText.x = 2048 / 2; titleText.y = 300; game.addChild(titleText); // Show user status if (isLoggedIn) { var welcomeText = new Text2('Welcome back, ' + currentUser + '!', { size: 80, fill: 0xFFD700 }); welcomeText.anchor.set(0.5, 0.5); welcomeText.x = 2048 / 2; welcomeText.y = 450; game.addChild(welcomeText); } else { var guestText = new Text2('Playing as Guest', { size: 80, fill: 0xFFFFFF }); guestText.anchor.set(0.5, 0.5); guestText.x = 2048 / 2; guestText.y = 450; game.addChild(guestText); } // Create play button var playButton = new Container(); var playBg = LK.getAsset('gridCell', { anchorX: 0.5, anchorY: 0.5, scaleX: 2, scaleY: 0.8 }); playBg.tint = 0x4CAF50; // Green tint for play button playButton.addChild(playBg); var playText = new Text2('PLAY', { size: 80, fill: 0xFFFFFF }); playText.anchor.set(0.5, 0.5); playButton.addChild(playText); playButton.x = 2048 / 2; playButton.y = 800; playButton.down = function () { // Play button click sound LK.getSound('buttonClick').play(); // Add button press animation tween(playButton, { scaleX: 0.9, scaleY: 0.9 }, { duration: 100, easing: tween.easeOut }); tween(playButton, { scaleX: 1, scaleY: 1 }, { duration: 150, easing: tween.bounceOut }); // Start the game startLevel(1); }; game.addChild(playButton); // Create leaderboard button var leaderboardButton = new Container(); var leaderboardBg = LK.getAsset('gridCell', { anchorX: 0.5, anchorY: 0.5, scaleX: 2, scaleY: 0.8 }); leaderboardBg.tint = isLoggedIn ? 0x2196F3 : 0x666666; // Blue for logged in, gray for guests leaderboardButton.addChild(leaderboardBg); var leaderboardText = new Text2('LEADERBOARD', { size: 60, fill: isLoggedIn ? 0xFFFFFF : 0x999999 }); leaderboardText.anchor.set(0.5, 0.5); leaderboardButton.addChild(leaderboardText); leaderboardButton.x = 2048 / 2; leaderboardButton.y = 950; leaderboardButton.down = function () { LK.getSound('buttonClick').play(); tween(leaderboardButton, { scaleX: 0.9, scaleY: 0.9 }, { duration: 100, easing: tween.easeOut }); tween(leaderboardButton, { scaleX: 1, scaleY: 1 }, { duration: 150, easing: tween.bounceOut }); if (isLoggedIn) { // Show leaderboard for logged-in users showLeaderboard(); } else { // Show message that login is required var loginRequiredText = new Text2('Login required for leaderboards', { size: 60, fill: 0xFF0000 }); loginRequiredText.anchor.set(0.5, 0.5); loginRequiredText.x = 2048 / 2; loginRequiredText.y = 1200; game.addChild(loginRequiredText); // Remove message after 3 seconds LK.setTimeout(function () { if (loginRequiredText && loginRequiredText.parent) { loginRequiredText.destroy(); } }, 3000); } }; game.addChild(leaderboardButton); // Authentication buttons if (isLoggedIn) { // Show logout button var logoutButton = new Container(); var logoutBg = LK.getAsset('gridCell', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.5, scaleY: 0.6 }); logoutBg.tint = 0xFF9800; // Orange tint for logout button logoutButton.addChild(logoutBg); var logoutText = new Text2('LOGOUT', { size: 50, fill: 0xFFFFFF }); logoutText.anchor.set(0.5, 0.5); logoutButton.addChild(logoutText); logoutButton.x = 2048 / 2; logoutButton.y = 1100; logoutButton.down = function () { LK.getSound('buttonClick').play(); tween(logoutButton, { scaleX: 0.9, scaleY: 0.9 }, { duration: 100, easing: tween.easeOut }); tween(logoutButton, { scaleX: 1, scaleY: 1 }, { duration: 150, easing: tween.bounceOut }); // Save current progress before logout saveUserProgress(); // Logout user storage.currentUser = null; showHomeScreen(); // Refresh home screen }; game.addChild(logoutButton); } else { // Show login button var loginButton = new Container(); var loginBg = LK.getAsset('gridCell', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.5, scaleY: 0.6 }); loginBg.tint = 0x9C27B0; // Purple tint for login button loginButton.addChild(loginBg); var loginText = new Text2('LOGIN', { size: 50, fill: 0xFFFFFF }); loginText.anchor.set(0.5, 0.5); loginButton.addChild(loginText); loginButton.x = 2048 / 2 - 200; loginButton.y = 1100; loginButton.down = function () { LK.getSound('buttonClick').play(); tween(loginButton, { scaleX: 0.9, scaleY: 0.9 }, { duration: 100, easing: tween.easeOut }); tween(loginButton, { scaleX: 1, scaleY: 1 }, { duration: 150, easing: tween.bounceOut }); showLoginScreen(); }; game.addChild(loginButton); // Show create account button var createAccountButton = new Container(); var createAccountBg = LK.getAsset('gridCell', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.5, scaleY: 0.6 }); createAccountBg.tint = 0x4CAF50; // Green tint for create account button createAccountButton.addChild(createAccountBg); var createAccountText = new Text2('SIGN UP', { size: 50, fill: 0xFFFFFF }); createAccountText.anchor.set(0.5, 0.5); createAccountButton.addChild(createAccountText); createAccountButton.x = 2048 / 2 + 200; createAccountButton.y = 1100; createAccountButton.down = function () { LK.getSound('buttonClick').play(); tween(createAccountButton, { scaleX: 0.9, scaleY: 0.9 }, { duration: 100, easing: tween.easeOut }); tween(createAccountButton, { scaleX: 1, scaleY: 1 }, { duration: 150, easing: tween.bounceOut }); showCreateAccountScreen(); }; game.addChild(createAccountButton); } // Show high score if available var highScore = storage.highScore || 0; if (highScore > 0) { var highScoreText = new Text2('High Score: ' + highScore, { size: 80, fill: 0xFFFFFF }); highScoreText.anchor.set(0.5, 0.5); highScoreText.x = 2048 / 2; highScoreText.y = 600; game.addChild(highScoreText); } } function completeLevel() { // Add current score to total score totalScore += currentScore; // Save high score to storage var highScore = storage.highScore || 0; if (totalScore > highScore) { storage.highScore = totalScore; } // Auto-save user progress saveUserProgress(); // Show leaderboard showLeaderboard(); // Show home screen to restart the game showHomeScreen(); } function startLevel(levelNum) { currentLevel = levelNum; gameState = 'playing'; levelCompleteShown = false; // Auto-save progress when starting level saveUserProgress(); // Continue playing background music during gameplay LK.playMusic('happyBgMusic', { loop: true }); // Adjust difficulty based on level targetMatches = Math.min(10 + (levelNum - 1) * 5, 100); movesLeft = Math.max(30 - Math.floor((levelNum - 1) / 3), 15); currentScore = 0; matchesCleared = 0; // Clear existing UI game.removeChildren(); // Recreate game setupGame(); } function setupGame() { // Recreate grid background gridBackground = new Container(); game.addChild(gridBackground); for (var x = 0; x < GRID_SIZE; x++) { for (var y = 0; y < GRID_SIZE; y++) { var cell = LK.getAsset('gridCell', { anchorX: 0.5, anchorY: 0.5 }); cell.x = GRID_START_X + x * CELL_SIZE + CELL_SIZE / 2; cell.y = GRID_START_Y + y * CELL_SIZE + CELL_SIZE / 2; cell.alpha = 0.3; gridBackground.addChild(cell); } } // Clear existing UI elements if (tutorialText && tutorialText.parent) { tutorialText.destroy(); } if (targetText && targetText.parent) { targetText.destroy(); } // Recreate UI elements if they don't exist if (!scoreText) { scoreText = new Text2('Score: 0', { size: 100, fill: 0xFFFFFF }); scoreText.anchor.set(0.5, 0); LK.gui.top.addChild(scoreText); scoreText.y = 120; } if (!movesText) { movesText = new Text2('Moves: ' + movesLeft, { size: 80, fill: 0xFFFFFF }); movesText.anchor.set(0, 0); LK.gui.topLeft.addChild(movesText); movesText.x = 150; movesText.y = 50; } // Update UI scoreText.setText('Score: 0'); movesText.setText('Moves: ' + movesLeft); tutorialText = new Text2('Lvl' + currentLevel, { size: 120, fill: 0xFFFFFF }); tutorialText.anchor.set(0.5, 0); LK.gui.top.addChild(tutorialText); tutorialText.y = 20; targetText = new Text2('Target: ' + targetMatches, { size: 100, fill: 0xFFFFFF }); targetText.anchor.set(0, 0); LK.gui.top.addChild(targetText); targetText.x = tutorialText.x + tutorialText.width / 2 + 50; targetText.y = tutorialText.y; // Add exit button below the game grid var exitButton = new Container(); var exitBg = LK.getAsset('gridCell', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.5, scaleY: 0.6 }); exitBg.tint = 0xFF4444; // Red tint for exit button exitButton.addChild(exitBg); var exitText = new Text2('EXIT', { size: 60, fill: 0xFFFFFF }); exitText.anchor.set(0.5, 0.5); exitButton.addChild(exitText); // Position button below the game grid, centered exitButton.x = 2048 / 2; exitButton.y = GRID_START_Y + GRID_SIZE * CELL_SIZE + 100; exitButton.down = function () { // Play button click sound LK.getSound('buttonClick').play(); // Add button press animation tween(exitButton, { scaleX: 0.9, scaleY: 0.9 }, { duration: 100, easing: tween.easeOut }); tween(exitButton, { scaleX: 1, scaleY: 1 }, { duration: 150, easing: tween.bounceOut }); // Teleport to home screen showHomeScreen(); }; game.addChild(exitButton); // Initialize grid initializeGrid(); updateUI(); } function getFoodAtPosition(x, y) { var gridX = Math.floor((x - GRID_START_X) / CELL_SIZE); var gridY = Math.floor((y - GRID_START_Y) / CELL_SIZE); if (isValidPosition(gridX, gridY) && grid[gridX] && grid[gridX][gridY]) { return grid[gridX][gridY]; } return null; } game.down = function (x, y, obj) { if (isProcessingMatches) return; var food = getFoodAtPosition(x, y); if (food && !food.isAnimating) { swipeStartX = x; swipeStartY = y; swipeFood = food; isSwipeActive = true; swipeFood.alpha = 0.7; // Add fun wobble animation when selected tween(swipeFood, { scaleX: 1.1, scaleY: 1.1 }, { duration: 200, easing: tween.bounceOut }); } }; game.up = function (x, y, obj) { if (!isSwipeActive || !swipeFood) return; var deltaX = x - swipeStartX; var deltaY = y - swipeStartY; var swipeDistance = Math.sqrt(deltaX * deltaX + deltaY * deltaY); if (swipeDistance < SWIPE_THRESHOLD) { // Not a swipe, reset swipeFood.alpha = 1; isSwipeActive = false; swipeFood = null; return; } // Determine swipe direction var targetGridX = swipeFood.gridX; var targetGridY = swipeFood.gridY; var absDeltaX = Math.abs(deltaX); var absDeltaY = Math.abs(deltaY); if (absDeltaX > absDeltaY) { // Horizontal swipe if (deltaX > 0) { targetGridX++; // Swipe right } else { targetGridX--; // Swipe left } } else { // Vertical swipe if (deltaY > 0) { targetGridY++; // Swipe down } else { targetGridY--; // Swipe up } } // Check if target position is valid if (isValidPosition(targetGridX, targetGridY) && grid[targetGridX] && grid[targetGridX][targetGridY]) { var targetFood = grid[targetGridX][targetGridY]; if (!targetFood.isAnimating) { LK.getSound('swap').play(); // Store original positions BEFORE any swap var tempFood1 = swipeFood; var tempFood2 = targetFood; var originalX1 = tempFood1.gridX; var originalY1 = tempFood1.gridY; var originalX2 = tempFood2.gridX; var originalY2 = tempFood2.gridY; // Try swap swapFoods(tempFood1, tempFood2); LK.setTimeout(function () { var matches = findAllMatches(); if (matches.length === 0) { // Invalid move, animate swap back to original positions var targetX1 = GRID_START_X + originalX1 * CELL_SIZE + CELL_SIZE / 2; var targetY1 = GRID_START_Y + originalY1 * CELL_SIZE + CELL_SIZE / 2; var targetX2 = GRID_START_X + originalX2 * CELL_SIZE + CELL_SIZE / 2; var targetY2 = GRID_START_Y + originalY2 * CELL_SIZE + CELL_SIZE / 2; tempFood1.animateToPosition(targetX1, targetY1); tempFood2.animateToPosition(targetX2, targetY2); // Restore original grid positions grid[originalX1][originalY1] = tempFood1; grid[originalX2][originalY2] = tempFood2; tempFood1.gridX = originalX1; tempFood1.gridY = originalY1; tempFood2.gridX = originalX2; tempFood2.gridY = originalY2; } else { // Valid move movesLeft--; updateUI(); processMatches(); checkGameEnd(); } }, 350); } } // Reset swipe state with fun bounce back animation tween(swipeFood, { alpha: 1, scaleX: 1, scaleY: 1 }, { duration: 200, easing: tween.bounceOut }); isSwipeActive = false; swipeFood = null; }; // Start background music when game loads LK.playMusic('happyBgMusic', { loop: true }); // Initialize the game - start with home screen showHomeScreen(); function showLoginScreen() { gameState = 'loginScreen'; // Clear all game elements game.removeChildren(); // Set game background color game.setBackgroundColor(0x87CEEB); // Create login screen UI var titleText = new Text2('LOGIN', { size: 150, fill: 0xFFFFFF }); titleText.anchor.set(0.5, 0.5); titleText.x = 2048 / 2; titleText.y = 300; game.addChild(titleText); // Username input field var usernameField = LK.getAsset('inputField', { anchorX: 0.5, anchorY: 0.5, scaleX: 8, scaleY: 1.2 }); usernameField.x = 2048 / 2; usernameField.y = 600; usernameField.down = function () { showKeyboard('username'); }; game.addChild(usernameField); usernameDisplayText = new Text2('', { size: 50, fill: 0x000000 }); usernameDisplayText.anchor.set(0.5, 0.5); usernameDisplayText.x = 2048 / 2; usernameDisplayText.y = 600; game.addChild(usernameDisplayText); var usernameLabel = new Text2('Username', { size: 60, fill: 0xFFFFFF }); usernameLabel.anchor.set(0.5, 0.5); usernameLabel.x = 2048 / 2; usernameLabel.y = 550; game.addChild(usernameLabel); // Password input field var passwordField = LK.getAsset('passwordField', { anchorX: 0.5, anchorY: 0.5, scaleX: 8, scaleY: 1.2 }); passwordField.x = 2048 / 2; passwordField.y = 800; passwordField.down = function () { showKeyboard('password'); }; game.addChild(passwordField); passwordDisplayText = new Text2('', { size: 50, fill: 0x000000 }); passwordDisplayText.anchor.set(0.5, 0.5); passwordDisplayText.x = 2048 / 2; passwordDisplayText.y = 800; game.addChild(passwordDisplayText); var passwordLabel = new Text2('Password', { size: 60, fill: 0xFFFFFF }); passwordLabel.anchor.set(0.5, 0.5); passwordLabel.x = 2048 / 2; passwordLabel.y = 750; game.addChild(passwordLabel); // Show keyboard automatically for username input showKeyboard('username'); // Login button var loginButton = new Container(); var loginBg = LK.getAsset('gridCell', { anchorX: 0.5, anchorY: 0.5, scaleX: 2, scaleY: 0.8 }); loginBg.tint = 0x4CAF50; // Green tint for login button loginButton.addChild(loginBg); var loginText = new Text2('LOGIN', { size: 80, fill: 0xFFFFFF }); loginText.anchor.set(0.5, 0.5); loginButton.addChild(loginText); loginButton.x = 2048 / 2; loginButton.y = 1000; loginButton.down = function () { LK.getSound('buttonClick').play(); tween(loginButton, { scaleX: 0.9, scaleY: 0.9 }, { duration: 100, easing: tween.easeOut }); tween(loginButton, { scaleX: 1, scaleY: 1 }, { duration: 150, easing: tween.bounceOut }); // Validate input fields if (usernameText.length === 0 || passwordText.length === 0) { // Show error message for empty fields var errorText = new Text2('Please fill all fields', { size: 60, fill: 0xFF0000 }); errorText.anchor.set(0.5, 0.5); errorText.x = 2048 / 2; errorText.y = 900; game.addChild(errorText); // Remove error message after 2 seconds LK.setTimeout(function () { if (errorText && errorText.parent) { errorText.destroy(); } }, 2000); return; } // Validate credentials var existingUsers = storage.users || {}; var username = usernameText; if (!existingUsers[username]) { // Username doesn't exist var errorText = new Text2('Username not found', { size: 60, fill: 0xFF0000 }); errorText.anchor.set(0.5, 0.5); errorText.x = 2048 / 2; errorText.y = 900; game.addChild(errorText); // Remove error message after 2 seconds LK.setTimeout(function () { if (errorText && errorText.parent) { errorText.destroy(); } }, 2000); return; } if (existingUsers[username].password !== passwordText) { // Wrong password var errorText = new Text2('Incorrect password', { size: 60, fill: 0xFF0000 }); errorText.anchor.set(0.5, 0.5); errorText.x = 2048 / 2; errorText.y = 900; game.addChild(errorText); // Remove error message after 2 seconds LK.setTimeout(function () { if (errorText && errorText.parent) { errorText.destroy(); } }, 2000); return; } // Valid credentials - login successful storage.currentUser = username; // Auto-save user data on login saveUserProgress(); // Clear input fields usernameText = ''; passwordText = ''; showHomeScreen(); }; game.addChild(loginButton); // Back button var backButton = new Container(); var backBg = LK.getAsset('gridCell', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.5, scaleY: 0.6 }); backBg.tint = 0xFF4444; // Red tint for back button backButton.addChild(backBg); var backText = new Text2('BACK', { size: 50, fill: 0xFFFFFF }); backText.anchor.set(0.5, 0.5); backButton.addChild(backText); backButton.x = 2048 / 2; backButton.y = 1150; backButton.down = function () { LK.getSound('buttonClick').play(); tween(backButton, { scaleX: 0.9, scaleY: 0.9 }, { duration: 100, easing: tween.easeOut }); tween(backButton, { scaleX: 1, scaleY: 1 }, { duration: 150, easing: tween.bounceOut }); showHomeScreen(); }; game.addChild(backButton); } function showLeaderboard() { gameState = 'leaderboard'; // Clear all game elements game.removeChildren(); // Set game background color game.setBackgroundColor(0x87CEEB); // Create leaderboard screen UI var titleText = new Text2('LEADERBOARD', { size: 150, fill: 0xFFFFFF }); titleText.anchor.set(0.5, 0.5); titleText.x = 2048 / 2; titleText.y = 300; game.addChild(titleText); // Get user-specific high score from storage var currentUser = storage.currentUser || 'Guest'; var highScore = 0; if (currentUser !== 'Guest' && storage.users && storage.users[currentUser]) { highScore = storage.users[currentUser].highScore || 0; } else { highScore = storage.highScore || 0; } // Display high score var highScoreText = new Text2('High Score: ' + highScore, { size: 120, fill: 0xFFD700 }); highScoreText.anchor.set(0.5, 0.5); highScoreText.x = 2048 / 2; highScoreText.y = 600; game.addChild(highScoreText); // Display current user var userText = new Text2('Player: ' + currentUser, { size: 80, fill: 0xFFFFFF }); userText.anchor.set(0.5, 0.5); userText.x = 2048 / 2; userText.y = 750; game.addChild(userText); // Show additional user stats for logged-in users if (currentUser !== 'Guest' && storage.users && storage.users[currentUser]) { var userData = storage.users[currentUser]; var levelText = new Text2('Level Reached: ' + (userData.level || 1), { size: 70, fill: 0xFFFFFF }); levelText.anchor.set(0.5, 0.5); levelText.x = 2048 / 2; levelText.y = 850; game.addChild(levelText); var totalScoreText = new Text2('Total Score: ' + (userData.totalScore || 0), { size: 70, fill: 0xFFFFFF }); totalScoreText.anchor.set(0.5, 0.5); totalScoreText.x = 2048 / 2; totalScoreText.y = 920; game.addChild(totalScoreText); } else { var loginPromptText = new Text2('Login to track your progress!', { size: 60, fill: 0xFFD700 }); loginPromptText.anchor.set(0.5, 0.5); loginPromptText.x = 2048 / 2; loginPromptText.y = 850; game.addChild(loginPromptText); } // Back button var backButton = new Container(); var backBg = LK.getAsset('gridCell', { anchorX: 0.5, anchorY: 0.5, scaleX: 2, scaleY: 0.8 }); backBg.tint = 0xFF4444; // Red tint for back button backButton.addChild(backBg); var backText = new Text2('BACK', { size: 80, fill: 0xFFFFFF }); backText.anchor.set(0.5, 0.5); backButton.addChild(backText); backButton.x = 2048 / 2; backButton.y = 1000; backButton.down = function () { LK.getSound('buttonClick').play(); tween(backButton, { scaleX: 0.9, scaleY: 0.9 }, { duration: 100, easing: tween.easeOut }); tween(backButton, { scaleX: 1, scaleY: 1 }, { duration: 150, easing: tween.bounceOut }); showHomeScreen(); }; game.addChild(backButton); } function showCreateAccountScreen() { gameState = 'createAccountScreen'; // Clear all game elements game.removeChildren(); // Set game background color game.setBackgroundColor(0x87CEEB); // Create account screen UI var titleText = new Text2('CREATE ACCOUNT', { size: 120, fill: 0xFFFFFF }); titleText.anchor.set(0.5, 0.5); titleText.x = 2048 / 2; titleText.y = 300; game.addChild(titleText); // Username input field var usernameField = LK.getAsset('inputField', { anchorX: 0.5, anchorY: 0.5, scaleX: 8, scaleY: 1.2 }); usernameField.x = 2048 / 2; usernameField.y = 600; usernameField.down = function () { showKeyboard('username'); }; game.addChild(usernameField); createUsernameDisplayText = new Text2('', { size: 50, fill: 0x000000 }); createUsernameDisplayText.anchor.set(0.5, 0.5); createUsernameDisplayText.x = 2048 / 2; createUsernameDisplayText.y = 600; game.addChild(createUsernameDisplayText); var usernameLabel = new Text2('Choose Username', { size: 60, fill: 0xFFFFFF }); usernameLabel.anchor.set(0.5, 0.5); usernameLabel.x = 2048 / 2; usernameLabel.y = 550; game.addChild(usernameLabel); // Password input field var passwordField = LK.getAsset('passwordField', { anchorX: 0.5, anchorY: 0.5, scaleX: 8, scaleY: 1.2 }); passwordField.x = 2048 / 2; passwordField.y = 800; passwordField.down = function () { showKeyboard('password'); }; game.addChild(passwordField); createPasswordDisplayText = new Text2('', { size: 50, fill: 0x000000 }); createPasswordDisplayText.anchor.set(0.5, 0.5); createPasswordDisplayText.x = 2048 / 2; createPasswordDisplayText.y = 800; game.addChild(createPasswordDisplayText); var passwordLabel = new Text2('Choose Password', { size: 60, fill: 0xFFFFFF }); passwordLabel.anchor.set(0.5, 0.5); passwordLabel.x = 2048 / 2; passwordLabel.y = 750; game.addChild(passwordLabel); // Show keyboard automatically for username input showKeyboard('username'); // Create account button var createButton = new Container(); var createBg = LK.getAsset('gridCell', { anchorX: 0.5, anchorY: 0.5, scaleX: 2, scaleY: 0.8 }); createBg.tint = 0x4CAF50; // Green tint for create button createButton.addChild(createBg); var createText = new Text2('CREATE', { size: 70, fill: 0xFFFFFF }); createText.anchor.set(0.5, 0.5); createButton.addChild(createText); createButton.x = 2048 / 2; createButton.y = 1000; createButton.down = function () { LK.getSound('buttonClick').play(); tween(createButton, { scaleX: 0.9, scaleY: 0.9 }, { duration: 100, easing: tween.easeOut }); tween(createButton, { scaleX: 1, scaleY: 1 }, { duration: 150, easing: tween.bounceOut }); // Validate input fields if (usernameText.length === 0 || passwordText.length === 0) { // Show error message for empty fields var errorText = new Text2('Please fill all fields', { size: 60, fill: 0xFF0000 }); errorText.anchor.set(0.5, 0.5); errorText.x = 2048 / 2; errorText.y = 900; game.addChild(errorText); // Remove error message after 2 seconds LK.setTimeout(function () { if (errorText && errorText.parent) { errorText.destroy(); } }, 2000); return; } // Check if username already exists var existingUsers = storage.users || {}; if (existingUsers[usernameText]) { // Show error message for existing username var errorText = new Text2('Username already exists', { size: 60, fill: 0xFF0000 }); errorText.anchor.set(0.5, 0.5); errorText.x = 2048 / 2; errorText.y = 900; game.addChild(errorText); // Remove error message after 2 seconds LK.setTimeout(function () { if (errorText && errorText.parent) { errorText.destroy(); } }, 2000); return; } // Create account with password var username = usernameText; // Store user credentials in storage if (!storage.users) { storage.users = {}; } // Ensure the storage.users object is properly initialized before assignment var newUserData = { password: passwordText, level: 1, totalScore: 0, highScore: 0 }; // Double-check storage.users exists before assignment if (!storage.users) { storage.users = {}; } // Ensure storage.users is properly initialized before assignment if (!storage.users) { storage.users = {}; } storage.users[username] = newUserData; storage.currentUser = username; // Auto-save new user data saveUserProgress(); // Clear input fields usernameText = ''; passwordText = ''; showHomeScreen(); }; game.addChild(createButton); // Back button var backButton = new Container(); var backBg = LK.getAsset('gridCell', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.5, scaleY: 0.6 }); backBg.tint = 0xFF4444; // Red tint for back button backButton.addChild(backBg); var backText = new Text2('BACK', { size: 50, fill: 0xFFFFFF }); backText.anchor.set(0.5, 0.5); backButton.addChild(backText); backButton.x = 2048 / 2; backButton.y = 1150; backButton.down = function () { LK.getSound('buttonClick').play(); tween(backButton, { scaleX: 0.9, scaleY: 0.9 }, { duration: 100, easing: tween.easeOut }); tween(backButton, { scaleX: 1, scaleY: 1 }, { duration: 150, easing: tween.bounceOut }); showHomeScreen(); }; game.addChild(backButton); } function saveUserProgress() { // Save current user data if (storage.currentUser) { // Initialize users storage if it doesn't exist if (!storage.users) { storage.users = {}; } // Initialize user data if it doesn't exist if (!storage.users[storage.currentUser]) { storage.users[storage.currentUser] = { password: '', level: 1, totalScore: 0, highScore: 0 }; } // Update user-specific progress storage.users[storage.currentUser].level = currentLevel; storage.users[storage.currentUser].totalScore = totalScore; storage.users[storage.currentUser].currentScore = currentScore; storage.users[storage.currentUser].movesLeft = movesLeft; storage.users[storage.currentUser].targetMatches = targetMatches; storage.users[storage.currentUser].matchesCleared = matchesCleared; storage.users[storage.currentUser].maxUnlockedLevel = maxUnlockedLevel; storage.users[storage.currentUser].gameState = gameState; // Update high score if current total score is higher var userHighScore = storage.users[storage.currentUser].highScore || 0; if (totalScore > userHighScore) { storage.users[storage.currentUser].highScore = totalScore; } // Also save to legacy storage locations for backward compatibility storage.userLevel = currentLevel; storage.userTotalScore = totalScore; storage.userCurrentScore = currentScore; storage.userMovesLeft = movesLeft; storage.userTargetMatches = targetMatches; storage.userMatchesCleared = matchesCleared; storage.userMaxUnlockedLevel = maxUnlockedLevel; storage.gameState = gameState; } // Save general game progress storage.lastPlayedLevel = currentLevel; storage.lastTotalScore = totalScore; } function loadUserProgress() { // Load user-specific progress if logged in if (storage.currentUser) { // Initialize users storage if it doesn't exist if (!storage.users) { storage.users = {}; } // Load from user account if it exists if (storage.users[storage.currentUser]) { var userData = storage.users[storage.currentUser]; currentLevel = userData.level || 1; totalScore = userData.totalScore || 0; currentScore = userData.currentScore || 0; movesLeft = userData.movesLeft || 30; targetMatches = userData.targetMatches || 10; matchesCleared = userData.matchesCleared || 0; maxUnlockedLevel = userData.maxUnlockedLevel || 1; } else { // Fallback to legacy storage for existing users currentLevel = storage.userLevel || 1; totalScore = storage.userTotalScore || 0; currentScore = storage.userCurrentScore || 0; movesLeft = storage.userMovesLeft || 30; targetMatches = storage.userTargetMatches || 10; matchesCleared = storage.userMatchesCleared || 0; maxUnlockedLevel = storage.userMaxUnlockedLevel || 1; } } else { // Load general progress for guests currentLevel = storage.lastPlayedLevel || 1; totalScore = storage.lastTotalScore || 0; } }
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var FoodItem = Container.expand(function (foodType) {
var self = Container.call(this);
self.foodType = foodType;
self.gridX = -1;
self.gridY = -1;
self.isMatched = false;
self.isAnimating = false;
var foodGraphics = self.attachAsset(foodType, {
anchorX: 0.5,
anchorY: 0.5
});
self.setGridPosition = function (gridX, gridY) {
self.gridX = gridX;
self.gridY = gridY;
self.x = GRID_START_X + gridX * CELL_SIZE + CELL_SIZE / 2;
self.y = GRID_START_Y + gridY * CELL_SIZE + CELL_SIZE / 2;
};
self.animateToPosition = function (targetX, targetY, callback) {
self.isAnimating = true;
tween(self, {
x: targetX,
y: targetY
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
self.isAnimating = false;
if (callback) callback();
}
});
};
self.animateMatch = function (callback) {
self.isMatched = true;
// First make it bigger and sparkly
tween(self, {
scaleX: 1.3,
scaleY: 1.3,
tint: 0xFFD700
}, {
duration: 150,
easing: tween.bounceOut
});
// Then shrink away with stars effect
tween(self, {
scaleX: 0,
scaleY: 0,
alpha: 0,
rotation: Math.PI * 2
}, {
duration: 300,
easing: tween.easeIn,
onFinish: callback
});
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x87CEEB
});
/****
* Game Code
****/
var GRID_SIZE = 8;
var CELL_SIZE = 240;
var GRID_START_X = (2048 - GRID_SIZE * CELL_SIZE) / 2;
var GRID_START_Y = 300;
var FOOD_TYPES = ['apple', 'banana', 'orange', 'grape', 'lemon', 'hamburger', 'hotdog', 'pizza'];
var grid = [];
var selectedFood = null;
var isProcessingMatches = false;
var movesLeft = 30;
var currentScore = 0;
var targetMatches = 10;
var matchesCleared = 0;
var swipeStartX = 0;
var swipeStartY = 0;
var swipeFood = null;
var isSwipeActive = false;
var SWIPE_THRESHOLD = 50;
var currentLevel = 1;
var maxUnlockedLevel = 1;
var totalScore = 0;
var gameState = 'playing';
var levelCompleteShown = false;
// Text input variables
var currentInputField = null;
var usernameText = '';
var passwordText = '';
var keyboardContainer = null;
var inputDisplayText = null;
var isKeyboardVisible = false;
var usernameDisplayText = null;
var passwordDisplayText = null;
var createUsernameDisplayText = null;
var createPasswordDisplayText = null;
// Initialize grid array
function getInputText() {
if (currentInputField === 'username') {
return usernameText;
} else if (currentInputField === 'password') {
// Show asterisks for password
var asterisks = '';
for (var i = 0; i < passwordText.length; i++) {
asterisks += '*';
}
return asterisks;
}
return '';
}
function showKeyboard(inputType) {
if (isKeyboardVisible) return;
isKeyboardVisible = true;
currentInputField = inputType;
// Create keyboard container
keyboardContainer = new Container();
game.addChild(keyboardContainer);
// Keyboard popup without background
// Create input display
var inputBg = LK.getAsset('inputField', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 10,
scaleY: 2
});
inputBg.x = 2048 / 2;
inputBg.y = 1600;
keyboardContainer.addChild(inputBg);
// Create input text display
inputDisplayText = new Text2(getInputText(), {
size: 80,
fill: 0xFFFFFF
});
inputDisplayText.anchor.set(0.5, 0.5);
inputDisplayText.x = 2048 / 2;
inputDisplayText.y = 1600;
keyboardContainer.addChild(inputDisplayText);
// Create keyboard layout
var keys = [['Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P'], ['A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L'], ['Z', 'X', 'C', 'V', 'B', 'N', 'M']];
var startY = 1800;
var keySize = 160;
var keySpacing = 200;
// Create letter keys
for (var row = 0; row < keys.length; row++) {
var rowKeys = keys[row];
var totalWidth = rowKeys.length * keySpacing;
var startX = (2048 - totalWidth) / 2 + keySpacing / 2;
for (var col = 0; col < rowKeys.length; col++) {
createKey(rowKeys[col], startX + col * keySpacing, startY + row * keySpacing);
}
}
// Create number row
var numbers = ['1', '2', '3', '4', '5', '6', '7', '8', '9', '0'];
var numberStartY = startY - keySpacing;
var numberTotalWidth = numbers.length * keySpacing;
var numberStartX = (2048 - numberTotalWidth) / 2 + keySpacing / 2;
for (var i = 0; i < numbers.length; i++) {
createKey(numbers[i], numberStartX + i * keySpacing, numberStartY);
}
// Create special keys
createSpecialKey('SPACE', 2048 / 2, startY + 3 * keySpacing, 4, function () {
addCharacter(' ');
});
createSpecialKey('DELETE', 2048 / 2 - 400, startY + 3 * keySpacing, 2, function () {
deleteCharacter();
});
createSpecialKey('DONE', 2048 / 2 + 400, startY + 3 * keySpacing, 2, function () {
hideKeyboard();
});
// Create close button
var closeButton = LK.getAsset('xButton', {
anchorX: 0.5,
anchorY: 0.5
});
closeButton.x = 2048 / 2 + 500;
closeButton.y = 1500;
closeButton.down = function () {
hideKeyboard();
};
keyboardContainer.addChild(closeButton);
// Start keyboard below screen and animate up
keyboardContainer.y = 800;
tween(keyboardContainer, {
y: 0
}, {
duration: 300,
easing: tween.easeOut
});
}
function createKey(letter, x, y) {
var keyButton = new Container();
var keyBg = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.2,
scaleY: 1.2
});
keyBg.tint = 0x4CAF50;
keyButton.addChild(keyBg);
var keyText = new Text2(letter, {
size: 70,
fill: 0xFFFFFF
});
keyText.anchor.set(0.5, 0.5);
keyButton.addChild(keyText);
keyButton.x = x;
keyButton.y = y;
keyButton.down = function () {
LK.getSound('buttonClick').play();
tween(keyButton, {
scaleX: 0.9,
scaleY: 0.9
}, {
duration: 100,
easing: tween.easeOut
});
tween(keyButton, {
scaleX: 1,
scaleY: 1
}, {
duration: 150,
easing: tween.bounceOut
});
addCharacter(letter);
};
keyboardContainer.addChild(keyButton);
}
function createSpecialKey(text, x, y, widthScale, action) {
var keyButton = new Container();
var keyBg = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: widthScale * 0.8,
scaleY: 1.2
});
keyBg.tint = 0xFF9800;
keyButton.addChild(keyBg);
var keyText = new Text2(text, {
size: 60,
fill: 0xFFFFFF
});
keyText.anchor.set(0.5, 0.5);
keyButton.addChild(keyText);
keyButton.x = x;
keyButton.y = y;
keyButton.down = function () {
LK.getSound('buttonClick').play();
tween(keyButton, {
scaleX: 0.9,
scaleY: 0.9
}, {
duration: 100,
easing: tween.easeOut
});
tween(keyButton, {
scaleX: 1,
scaleY: 1
}, {
duration: 150,
easing: tween.bounceOut
});
action();
};
keyboardContainer.addChild(keyButton);
}
function addCharacter(_char) {
if (currentInputField === 'username') {
if (usernameText.length < 15) {
usernameText += _char;
}
} else if (currentInputField === 'password') {
if (passwordText.length < 15) {
passwordText += _char;
}
}
updateInputDisplay();
}
function deleteCharacter() {
if (currentInputField === 'username') {
if (usernameText.length > 0) {
usernameText = usernameText.slice(0, -1);
}
} else if (currentInputField === 'password') {
if (passwordText.length > 0) {
passwordText = passwordText.slice(0, -1);
}
}
updateInputDisplay();
}
function updateInputDisplay() {
if (inputDisplayText) {
inputDisplayText.setText(getInputText());
}
// Update field displays on login screen
if (gameState === 'loginScreen') {
if (usernameDisplayText) {
usernameDisplayText.setText(usernameText);
}
if (passwordDisplayText) {
var asterisks = '';
for (var i = 0; i < passwordText.length; i++) {
asterisks += '*';
}
passwordDisplayText.setText(asterisks);
}
}
// Update field displays on create account screen
if (gameState === 'createAccountScreen') {
if (createUsernameDisplayText) {
createUsernameDisplayText.setText(usernameText);
}
if (createPasswordDisplayText) {
var asterisks = '';
for (var i = 0; i < passwordText.length; i++) {
asterisks += '*';
}
createPasswordDisplayText.setText(asterisks);
}
}
}
function hideKeyboard() {
if (keyboardContainer) {
tween(keyboardContainer, {
y: 800
}, {
duration: 300,
easing: tween.easeIn,
onFinish: function onFinish() {
keyboardContainer.destroy();
keyboardContainer = null;
inputDisplayText = null;
isKeyboardVisible = false;
currentInputField = null;
}
});
}
}
var _loop = function _loop() {
grid[x] = [];
for (y = 0; y < GRID_SIZE; y++) {
grid[x][y] = null;
}
},
y;
for (var x = 0; x < GRID_SIZE; x++) {
_loop();
}
// Create grid background
var gridBackground = new Container();
game.addChild(gridBackground);
for (var x = 0; x < GRID_SIZE; x++) {
for (var y = 0; y < GRID_SIZE; y++) {
var cell = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5
});
cell.x = GRID_START_X + x * CELL_SIZE + CELL_SIZE / 2;
cell.y = GRID_START_Y + y * CELL_SIZE + CELL_SIZE / 2;
cell.alpha = 0.3;
gridBackground.addChild(cell);
}
}
// Create UI
var scoreText = new Text2('Score: 0', {
size: 100,
fill: 0xFFFFFF
});
scoreText.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreText);
scoreText.y = 120;
var movesText = new Text2('Moves: 30', {
size: 80,
fill: 0xFFFFFF
});
movesText.anchor.set(0, 0);
LK.gui.topLeft.addChild(movesText);
movesText.x = 150;
movesText.y = 50;
var tutorialText = new Text2('Lvl' + currentLevel, {
size: 120,
fill: 0xFFFFFF
});
tutorialText.anchor.set(0.5, 0);
LK.gui.top.addChild(tutorialText);
tutorialText.y = 20;
var targetText = new Text2('Target: ' + targetMatches, {
size: 100,
fill: 0xFFFFFF
});
targetText.anchor.set(0, 0);
LK.gui.top.addChild(targetText);
targetText.x = tutorialText.x + tutorialText.width / 2 + 50;
targetText.y = tutorialText.y;
function getRandomFoodType(x, y) {
// If position is provided, try to create squares by matching nearby foods
if (x !== undefined && y !== undefined) {
// Collect nearby food types that could form squares
var nearbyTypes = [];
// Check adjacent positions for potential square formation
var positions = [{
dx: -1,
dy: 0
}, {
dx: 1,
dy: 0
}, {
dx: 0,
dy: -1
}, {
dx: 0,
dy: 1
},
// Adjacent
{
dx: -1,
dy: -1
}, {
dx: 1,
dy: -1
}, {
dx: -1,
dy: 1
}, {
dx: 1,
dy: 1
} // Diagonal
];
for (var i = 0; i < positions.length; i++) {
var checkX = x + positions[i].dx;
var checkY = y + positions[i].dy;
if (isValidPosition(checkX, checkY) && grid[checkX] && grid[checkX][checkY]) {
nearbyTypes.push(grid[checkX][checkY].foodType);
}
}
// 40% chance to match a nearby type if any exist
if (nearbyTypes.length > 0 && Math.random() < 0.4) {
return nearbyTypes[Math.floor(Math.random() * nearbyTypes.length)];
}
}
return FOOD_TYPES[Math.floor(Math.random() * FOOD_TYPES.length)];
}
function createFoodItem(x, y) {
var foodType = getRandomFoodType(x, y);
var food = new FoodItem(foodType);
food.setGridPosition(x, y);
grid[x][y] = food;
game.addChild(food);
// Add fun spawn animation with rainbow colors
food.scaleX = 0;
food.scaleY = 0;
food.alpha = 0;
var rainbowColors = [0xFF6B6B, 0x4ECDC4, 0x45B7D1, 0x96CEB4, 0xFECA57, 0xFF9FF3, 0x54A0FF];
var randomColor = rainbowColors[Math.floor(Math.random() * rainbowColors.length)];
food.tint = randomColor;
tween(food, {
scaleX: 1,
scaleY: 1,
alpha: 1
}, {
duration: 500,
easing: tween.bounceOut
});
// Reset to normal color after spawn
tween(food, {
tint: 0xFFFFFF
}, {
duration: 800,
easing: tween.easeOut
});
return food;
}
function initializeGrid() {
for (var x = 0; x < GRID_SIZE; x++) {
for (var y = 0; y < GRID_SIZE; y++) {
createFoodItem(x, y);
}
}
// Remove initial matches
var foundMatches = true;
while (foundMatches) {
var matches = findAllMatches();
if (matches.length > 0) {
for (var i = 0; i < matches.length; i++) {
var match = matches[i];
if (grid[match.x] && grid[match.x][match.y]) {
grid[match.x][match.y].destroy();
grid[match.x][match.y] = null;
}
}
fillEmptySpaces();
} else {
foundMatches = false;
}
}
}
function isValidPosition(x, y) {
return x >= 0 && x < GRID_SIZE && y >= 0 && y < GRID_SIZE;
}
function isAdjacent(x1, y1, x2, y2) {
var dx = Math.abs(x1 - x2);
var dy = Math.abs(y1 - y2);
return dx === 1 && dy === 0 || dx === 0 && dy === 1;
}
function swapFoods(food1, food2) {
if (!food1 || !food2) return false;
var x1 = food1.gridX;
var y1 = food1.gridY;
var x2 = food2.gridX;
var y2 = food2.gridY;
// Swap in grid
grid[x1][y1] = food2;
grid[x2][y2] = food1;
// Update grid positions
food1.setGridPosition(x2, y2);
food2.setGridPosition(x1, y1);
// Animate swap
var targetX1 = GRID_START_X + x2 * CELL_SIZE + CELL_SIZE / 2;
var targetY1 = GRID_START_Y + y2 * CELL_SIZE + CELL_SIZE / 2;
var targetX2 = GRID_START_X + x1 * CELL_SIZE + CELL_SIZE / 2;
var targetY2 = GRID_START_Y + y1 * CELL_SIZE + CELL_SIZE / 2;
food1.animateToPosition(targetX1, targetY1);
food2.animateToPosition(targetX2, targetY2);
return true;
}
function findMatches(x, y) {
var matches = [];
if (!grid[x] || !grid[x][y]) return matches;
var foodType = grid[x][y].foodType;
// Check horizontal matches
var horizontalMatches = [{
x: x,
y: y
}];
// Check left
for (var i = x - 1; i >= 0; i--) {
if (grid[i] && grid[i][y] && grid[i][y].foodType === foodType) {
horizontalMatches.push({
x: i,
y: y
});
} else {
break;
}
}
// Check right
for (var i = x + 1; i < GRID_SIZE; i++) {
if (grid[i] && grid[i][y] && grid[i][y].foodType === foodType) {
horizontalMatches.push({
x: i,
y: y
});
} else {
break;
}
}
if (horizontalMatches.length >= 3) {
matches = matches.concat(horizontalMatches);
}
// Check vertical matches
var verticalMatches = [{
x: x,
y: y
}];
// Check up
for (var i = y - 1; i >= 0; i--) {
if (grid[x] && grid[x][i] && grid[x][i].foodType === foodType) {
verticalMatches.push({
x: x,
y: i
});
} else {
break;
}
}
// Check down
for (var i = y + 1; i < GRID_SIZE; i++) {
if (grid[x] && grid[x][i] && grid[x][i].foodType === foodType) {
verticalMatches.push({
x: x,
y: i
});
} else {
break;
}
}
if (verticalMatches.length >= 3) {
matches = matches.concat(verticalMatches);
}
return matches;
}
function findSquares() {
var allSquares = [];
// Check for 2x2 squares
for (var x = 0; x < GRID_SIZE - 1; x++) {
for (var y = 0; y < GRID_SIZE - 1; y++) {
// Check if all positions have valid food items
if (grid[x] && grid[x][y] && grid[x + 1] && grid[x + 1][y] && grid[x] && grid[x][y + 1] && grid[x + 1] && grid[x + 1][y + 1]) {
var topLeft = grid[x][y];
var topRight = grid[x + 1][y];
var bottomLeft = grid[x][y + 1];
var bottomRight = grid[x + 1][y + 1];
// Check if all 4 foods exist, are the same type and not already matched
if (topLeft && topRight && bottomLeft && bottomRight && topLeft.foodType === topRight.foodType && topLeft.foodType === bottomLeft.foodType && topLeft.foodType === bottomRight.foodType && !topLeft.isMatched && !topRight.isMatched && !bottomLeft.isMatched && !bottomRight.isMatched) {
allSquares.push([{
x: x,
y: y
}, {
x: x + 1,
y: y
}, {
x: x,
y: y + 1
}, {
x: x + 1,
y: y + 1
}]);
}
}
}
}
return allSquares;
}
function findAllMatches() {
var allMatches = [];
var processed = {};
for (var x = 0; x < GRID_SIZE; x++) {
for (var y = 0; y < GRID_SIZE; y++) {
if (grid[x] && grid[x][y] && !processed[x + ',' + y]) {
var matches = findMatches(x, y);
for (var i = 0; i < matches.length; i++) {
var match = matches[i];
var key = match.x + ',' + match.y;
if (!processed[key]) {
allMatches.push(match);
processed[key] = true;
}
}
}
}
}
return allMatches;
}
function processMatches() {
if (isProcessingMatches) return;
// First check for squares
var squares = findSquares();
var matches = findAllMatches();
if (squares.length === 0 && matches.length === 0) return;
isProcessingMatches = true;
LK.getSound('match').play();
var totalClearedCount = 0;
var finalMatches = [];
// Process squares first - clear entire rows
if (squares.length > 0) {
var rowsToClear = [];
// Collect all unique rows that contain squares
for (var s = 0; s < squares.length; s++) {
var square = squares[s];
for (var p = 0; p < square.length; p++) {
var rowY = square[p].y;
if (rowsToClear.indexOf(rowY) === -1) {
rowsToClear.push(rowY);
}
}
}
// Clear all foods in the identified rows
for (var r = 0; r < rowsToClear.length; r++) {
var row = rowsToClear[r];
for (var x = 0; x < GRID_SIZE; x++) {
if (grid[x] && grid[x][row] && !grid[x][row].isMatched) {
finalMatches.push({
x: x,
y: row
});
grid[x][row].isMatched = true; // Mark as matched immediately
}
}
}
totalClearedCount = finalMatches.length;
// Special celebration for square clearing
LK.effects.flashScreen(0x00FF00, 800);
currentScore += totalClearedCount * 20; // Bonus points for row clearing
} else {
// Regular match processing
finalMatches = matches;
totalClearedCount = matches.length;
currentScore += totalClearedCount * 10;
}
matchesCleared += totalClearedCount;
targetMatches = Math.max(0, targetMatches - Math.floor(totalClearedCount / 3));
updateUI();
// Update total score
totalScore += squares.length > 0 ? totalClearedCount * 20 : totalClearedCount * 10;
// Save high score to storage
var highScore = storage.highScore || 0;
if (totalScore > highScore) {
storage.highScore = totalScore;
}
// Auto-save user progress
saveUserProgress();
// Play squeaky voice when getting points
LK.getSound('goodJobBooby').play();
// Add celebration effects for bigger matches
if (totalClearedCount >= 4 || squares.length > 0) {
// Big match celebration!
if (squares.length === 0) {
LK.effects.flashScreen(0xFFD700, 500);
}
// Make score text dance
tween(scoreText, {
scaleX: 1.3,
scaleY: 1.3
}, {
duration: 300,
easing: tween.bounceOut
});
tween(scoreText, {
scaleX: 1,
scaleY: 1
}, {
duration: 300,
easing: tween.bounceOut
});
}
var animationsCompleted = 0;
for (var i = 0; i < finalMatches.length; i++) {
var match = finalMatches[i];
if (grid[match.x] && grid[match.x][match.y]) {
// Mark as matched before animation
grid[match.x][match.y].isMatched = true;
grid[match.x][match.y].animateMatch(function () {
animationsCompleted++;
if (animationsCompleted === totalClearedCount) {
// Remove matched foods
for (var j = 0; j < finalMatches.length; j++) {
var m = finalMatches[j];
if (grid[m.x] && grid[m.x][m.y]) {
grid[m.x][m.y].destroy();
grid[m.x][m.y] = null;
}
}
fillEmptySpaces();
LK.setTimeout(function () {
isProcessingMatches = false;
processMatches(); // Check for cascade matches
}, 300);
}
});
}
}
if (totalClearedCount === 0) {
isProcessingMatches = false;
}
}
function fillEmptySpaces() {
// Drop existing foods
for (var x = 0; x < GRID_SIZE; x++) {
var writeIndex = GRID_SIZE - 1;
for (var y = GRID_SIZE - 1; y >= 0; y--) {
if (grid[x][y] !== null) {
if (y !== writeIndex) {
grid[x][writeIndex] = grid[x][y];
grid[x][y] = null;
grid[x][writeIndex].setGridPosition(x, writeIndex);
var targetY = GRID_START_Y + writeIndex * CELL_SIZE + CELL_SIZE / 2;
grid[x][writeIndex].animateToPosition(grid[x][writeIndex].x, targetY);
}
writeIndex--;
}
}
// Create new foods for empty spaces at top
for (var y = 0; y <= writeIndex; y++) {
var food = new FoodItem(getRandomFoodType(x, y));
food.x = GRID_START_X + x * CELL_SIZE + CELL_SIZE / 2;
food.y = GRID_START_Y - (writeIndex - y + 1) * CELL_SIZE + CELL_SIZE / 2;
food.gridX = x;
food.gridY = y;
grid[x][y] = food;
game.addChild(food);
var targetY = GRID_START_Y + y * CELL_SIZE + CELL_SIZE / 2;
food.animateToPosition(food.x, targetY);
}
}
}
function updateUI() {
if (scoreText) {
scoreText.setText('Score: ' + currentScore);
}
if (movesText) {
movesText.setText('Moves: ' + movesLeft);
}
if (targetText) {
targetText.setText('Target: ' + targetMatches);
}
}
function checkGameEnd() {
if (targetMatches <= 1 && targetMatches > 0) {
// Create guaranteed 2x2 square at front of board
createGuaranteedSquare();
}
if (targetMatches <= 0) {
if (currentLevel === 1 && !levelCompleteShown) {
showLevelComplete();
} else {
completeLevel();
}
} else if (movesLeft <= 0) {
LK.showGameOver();
}
}
function createGuaranteedSquare() {
// Choose a random food type for the square
var squareFoodType = FOOD_TYPES[Math.floor(Math.random() * FOOD_TYPES.length)];
// Find the best 2x2 position at the front (top rows) of the board
var bestX = 0;
var bestY = 0;
// Try to place in top-left area first for visibility
for (var x = 0; x <= GRID_SIZE - 2; x++) {
for (var y = 0; y <= 2; y++) {
// Focus on top 3 rows
if (isValidPosition(x, y) && isValidPosition(x + 1, y) && isValidPosition(x, y + 1) && isValidPosition(x + 1, y + 1)) {
bestX = x;
bestY = y;
break;
}
}
if (bestY <= 2) break;
}
// Create the 2x2 square by replacing existing foods
var positions = [{
x: bestX,
y: bestY
}, {
x: bestX + 1,
y: bestY
}, {
x: bestX,
y: bestY + 1
}, {
x: bestX + 1,
y: bestY + 1
}];
for (var i = 0; i < positions.length; i++) {
var pos = positions[i];
// Remove existing food if any
if (grid[pos.x] && grid[pos.x][pos.y]) {
grid[pos.x][pos.y].destroy();
}
// Create new food of the same type
var newFood = new FoodItem(squareFoodType);
newFood.setGridPosition(pos.x, pos.y);
grid[pos.x][pos.y] = newFood;
game.addChild(newFood);
// Add special visual effect to highlight the square
newFood.tint = 0xFFD700; // Gold tint
// Add pulsing animation to make it obvious
tween(newFood, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 500,
easing: tween.easeInOut
});
tween(newFood, {
scaleX: 1,
scaleY: 1
}, {
duration: 500,
easing: tween.easeInOut
});
}
// Flash screen to draw attention
LK.effects.flashScreen(0xFFD700, 1000);
}
function showLevelComplete() {
levelCompleteShown = true;
gameState = 'levelComplete';
// Play celebration music
LK.playMusic('celebrationMusic', {
loop: false
});
// Play level complete sound
LK.getSound('levelComplete').play();
// Add celebration effects
LK.effects.flashScreen(0xFFD700, 1000);
// Make all UI elements dance
tween(scoreText, {
scaleX: 1.5,
scaleY: 1.5,
rotation: 0.2
}, {
duration: 500,
easing: tween.bounceOut
});
tween(tutorialText, {
scaleX: 1.3,
scaleY: 1.3,
rotation: -0.1
}, {
duration: 600,
easing: tween.bounceOut
});
tween(targetText, {
scaleX: 1.2,
scaleY: 1.2,
rotation: 0.15
}, {
duration: 700,
easing: tween.bounceOut
});
LK.setTimeout(function () {
showWhiteScreen();
}, 3000);
}
function showWhiteScreen() {
gameState = 'whiteScreen';
// Clear all game elements
game.removeChildren();
// Clear tutorial UI elements
if (tutorialText && tutorialText.parent) {
tutorialText.destroy();
tutorialText = null;
}
if (targetText && targetText.parent) {
targetText.destroy();
targetText = null;
}
if (scoreText && scoreText.parent) {
scoreText.destroy();
scoreText = null;
}
if (movesText && movesText.parent) {
movesText.destroy();
movesText = null;
}
// Set white background
game.setBackgroundColor(0xFFFFFF);
// Show white screen for 2 seconds then show home screen
LK.setTimeout(function () {
showHomeScreen();
}, 2000);
}
function showHomeScreen() {
gameState = 'homeScreen';
// Load user progress from storage
loadUserProgress();
// Clear all game elements
game.removeChildren();
// Set game background color
game.setBackgroundColor(0x87CEEB);
// Start playing happy background music
LK.playMusic('happyBgMusic', {
loop: true
});
// Clear all UI elements from the screen
if (tutorialText && tutorialText.parent) {
tutorialText.destroy();
tutorialText = null;
}
if (targetText && targetText.parent) {
targetText.destroy();
targetText = null;
}
if (scoreText && scoreText.parent) {
scoreText.destroy();
scoreText = null;
}
if (movesText && movesText.parent) {
movesText.destroy();
movesText = null;
}
// Get user data from storage
var currentUser = storage.currentUser || null;
var isLoggedIn = currentUser !== null;
// Create home screen UI
var titleText = new Text2('FOOD MATCH', {
size: 200,
fill: 0xFFFFFF
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 2048 / 2;
titleText.y = 300;
game.addChild(titleText);
// Show user status
if (isLoggedIn) {
var welcomeText = new Text2('Welcome back, ' + currentUser + '!', {
size: 80,
fill: 0xFFD700
});
welcomeText.anchor.set(0.5, 0.5);
welcomeText.x = 2048 / 2;
welcomeText.y = 450;
game.addChild(welcomeText);
} else {
var guestText = new Text2('Playing as Guest', {
size: 80,
fill: 0xFFFFFF
});
guestText.anchor.set(0.5, 0.5);
guestText.x = 2048 / 2;
guestText.y = 450;
game.addChild(guestText);
}
// Create play button
var playButton = new Container();
var playBg = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2,
scaleY: 0.8
});
playBg.tint = 0x4CAF50; // Green tint for play button
playButton.addChild(playBg);
var playText = new Text2('PLAY', {
size: 80,
fill: 0xFFFFFF
});
playText.anchor.set(0.5, 0.5);
playButton.addChild(playText);
playButton.x = 2048 / 2;
playButton.y = 800;
playButton.down = function () {
// Play button click sound
LK.getSound('buttonClick').play();
// Add button press animation
tween(playButton, {
scaleX: 0.9,
scaleY: 0.9
}, {
duration: 100,
easing: tween.easeOut
});
tween(playButton, {
scaleX: 1,
scaleY: 1
}, {
duration: 150,
easing: tween.bounceOut
});
// Start the game
startLevel(1);
};
game.addChild(playButton);
// Create leaderboard button
var leaderboardButton = new Container();
var leaderboardBg = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2,
scaleY: 0.8
});
leaderboardBg.tint = isLoggedIn ? 0x2196F3 : 0x666666; // Blue for logged in, gray for guests
leaderboardButton.addChild(leaderboardBg);
var leaderboardText = new Text2('LEADERBOARD', {
size: 60,
fill: isLoggedIn ? 0xFFFFFF : 0x999999
});
leaderboardText.anchor.set(0.5, 0.5);
leaderboardButton.addChild(leaderboardText);
leaderboardButton.x = 2048 / 2;
leaderboardButton.y = 950;
leaderboardButton.down = function () {
LK.getSound('buttonClick').play();
tween(leaderboardButton, {
scaleX: 0.9,
scaleY: 0.9
}, {
duration: 100,
easing: tween.easeOut
});
tween(leaderboardButton, {
scaleX: 1,
scaleY: 1
}, {
duration: 150,
easing: tween.bounceOut
});
if (isLoggedIn) {
// Show leaderboard for logged-in users
showLeaderboard();
} else {
// Show message that login is required
var loginRequiredText = new Text2('Login required for leaderboards', {
size: 60,
fill: 0xFF0000
});
loginRequiredText.anchor.set(0.5, 0.5);
loginRequiredText.x = 2048 / 2;
loginRequiredText.y = 1200;
game.addChild(loginRequiredText);
// Remove message after 3 seconds
LK.setTimeout(function () {
if (loginRequiredText && loginRequiredText.parent) {
loginRequiredText.destroy();
}
}, 3000);
}
};
game.addChild(leaderboardButton);
// Authentication buttons
if (isLoggedIn) {
// Show logout button
var logoutButton = new Container();
var logoutBg = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 0.6
});
logoutBg.tint = 0xFF9800; // Orange tint for logout button
logoutButton.addChild(logoutBg);
var logoutText = new Text2('LOGOUT', {
size: 50,
fill: 0xFFFFFF
});
logoutText.anchor.set(0.5, 0.5);
logoutButton.addChild(logoutText);
logoutButton.x = 2048 / 2;
logoutButton.y = 1100;
logoutButton.down = function () {
LK.getSound('buttonClick').play();
tween(logoutButton, {
scaleX: 0.9,
scaleY: 0.9
}, {
duration: 100,
easing: tween.easeOut
});
tween(logoutButton, {
scaleX: 1,
scaleY: 1
}, {
duration: 150,
easing: tween.bounceOut
});
// Save current progress before logout
saveUserProgress();
// Logout user
storage.currentUser = null;
showHomeScreen(); // Refresh home screen
};
game.addChild(logoutButton);
} else {
// Show login button
var loginButton = new Container();
var loginBg = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 0.6
});
loginBg.tint = 0x9C27B0; // Purple tint for login button
loginButton.addChild(loginBg);
var loginText = new Text2('LOGIN', {
size: 50,
fill: 0xFFFFFF
});
loginText.anchor.set(0.5, 0.5);
loginButton.addChild(loginText);
loginButton.x = 2048 / 2 - 200;
loginButton.y = 1100;
loginButton.down = function () {
LK.getSound('buttonClick').play();
tween(loginButton, {
scaleX: 0.9,
scaleY: 0.9
}, {
duration: 100,
easing: tween.easeOut
});
tween(loginButton, {
scaleX: 1,
scaleY: 1
}, {
duration: 150,
easing: tween.bounceOut
});
showLoginScreen();
};
game.addChild(loginButton);
// Show create account button
var createAccountButton = new Container();
var createAccountBg = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 0.6
});
createAccountBg.tint = 0x4CAF50; // Green tint for create account button
createAccountButton.addChild(createAccountBg);
var createAccountText = new Text2('SIGN UP', {
size: 50,
fill: 0xFFFFFF
});
createAccountText.anchor.set(0.5, 0.5);
createAccountButton.addChild(createAccountText);
createAccountButton.x = 2048 / 2 + 200;
createAccountButton.y = 1100;
createAccountButton.down = function () {
LK.getSound('buttonClick').play();
tween(createAccountButton, {
scaleX: 0.9,
scaleY: 0.9
}, {
duration: 100,
easing: tween.easeOut
});
tween(createAccountButton, {
scaleX: 1,
scaleY: 1
}, {
duration: 150,
easing: tween.bounceOut
});
showCreateAccountScreen();
};
game.addChild(createAccountButton);
}
// Show high score if available
var highScore = storage.highScore || 0;
if (highScore > 0) {
var highScoreText = new Text2('High Score: ' + highScore, {
size: 80,
fill: 0xFFFFFF
});
highScoreText.anchor.set(0.5, 0.5);
highScoreText.x = 2048 / 2;
highScoreText.y = 600;
game.addChild(highScoreText);
}
}
function completeLevel() {
// Add current score to total score
totalScore += currentScore;
// Save high score to storage
var highScore = storage.highScore || 0;
if (totalScore > highScore) {
storage.highScore = totalScore;
}
// Auto-save user progress
saveUserProgress();
// Show leaderboard
showLeaderboard();
// Show home screen to restart the game
showHomeScreen();
}
function startLevel(levelNum) {
currentLevel = levelNum;
gameState = 'playing';
levelCompleteShown = false;
// Auto-save progress when starting level
saveUserProgress();
// Continue playing background music during gameplay
LK.playMusic('happyBgMusic', {
loop: true
});
// Adjust difficulty based on level
targetMatches = Math.min(10 + (levelNum - 1) * 5, 100);
movesLeft = Math.max(30 - Math.floor((levelNum - 1) / 3), 15);
currentScore = 0;
matchesCleared = 0;
// Clear existing UI
game.removeChildren();
// Recreate game
setupGame();
}
function setupGame() {
// Recreate grid background
gridBackground = new Container();
game.addChild(gridBackground);
for (var x = 0; x < GRID_SIZE; x++) {
for (var y = 0; y < GRID_SIZE; y++) {
var cell = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5
});
cell.x = GRID_START_X + x * CELL_SIZE + CELL_SIZE / 2;
cell.y = GRID_START_Y + y * CELL_SIZE + CELL_SIZE / 2;
cell.alpha = 0.3;
gridBackground.addChild(cell);
}
}
// Clear existing UI elements
if (tutorialText && tutorialText.parent) {
tutorialText.destroy();
}
if (targetText && targetText.parent) {
targetText.destroy();
}
// Recreate UI elements if they don't exist
if (!scoreText) {
scoreText = new Text2('Score: 0', {
size: 100,
fill: 0xFFFFFF
});
scoreText.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreText);
scoreText.y = 120;
}
if (!movesText) {
movesText = new Text2('Moves: ' + movesLeft, {
size: 80,
fill: 0xFFFFFF
});
movesText.anchor.set(0, 0);
LK.gui.topLeft.addChild(movesText);
movesText.x = 150;
movesText.y = 50;
}
// Update UI
scoreText.setText('Score: 0');
movesText.setText('Moves: ' + movesLeft);
tutorialText = new Text2('Lvl' + currentLevel, {
size: 120,
fill: 0xFFFFFF
});
tutorialText.anchor.set(0.5, 0);
LK.gui.top.addChild(tutorialText);
tutorialText.y = 20;
targetText = new Text2('Target: ' + targetMatches, {
size: 100,
fill: 0xFFFFFF
});
targetText.anchor.set(0, 0);
LK.gui.top.addChild(targetText);
targetText.x = tutorialText.x + tutorialText.width / 2 + 50;
targetText.y = tutorialText.y;
// Add exit button below the game grid
var exitButton = new Container();
var exitBg = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 0.6
});
exitBg.tint = 0xFF4444; // Red tint for exit button
exitButton.addChild(exitBg);
var exitText = new Text2('EXIT', {
size: 60,
fill: 0xFFFFFF
});
exitText.anchor.set(0.5, 0.5);
exitButton.addChild(exitText);
// Position button below the game grid, centered
exitButton.x = 2048 / 2;
exitButton.y = GRID_START_Y + GRID_SIZE * CELL_SIZE + 100;
exitButton.down = function () {
// Play button click sound
LK.getSound('buttonClick').play();
// Add button press animation
tween(exitButton, {
scaleX: 0.9,
scaleY: 0.9
}, {
duration: 100,
easing: tween.easeOut
});
tween(exitButton, {
scaleX: 1,
scaleY: 1
}, {
duration: 150,
easing: tween.bounceOut
});
// Teleport to home screen
showHomeScreen();
};
game.addChild(exitButton);
// Initialize grid
initializeGrid();
updateUI();
}
function getFoodAtPosition(x, y) {
var gridX = Math.floor((x - GRID_START_X) / CELL_SIZE);
var gridY = Math.floor((y - GRID_START_Y) / CELL_SIZE);
if (isValidPosition(gridX, gridY) && grid[gridX] && grid[gridX][gridY]) {
return grid[gridX][gridY];
}
return null;
}
game.down = function (x, y, obj) {
if (isProcessingMatches) return;
var food = getFoodAtPosition(x, y);
if (food && !food.isAnimating) {
swipeStartX = x;
swipeStartY = y;
swipeFood = food;
isSwipeActive = true;
swipeFood.alpha = 0.7;
// Add fun wobble animation when selected
tween(swipeFood, {
scaleX: 1.1,
scaleY: 1.1
}, {
duration: 200,
easing: tween.bounceOut
});
}
};
game.up = function (x, y, obj) {
if (!isSwipeActive || !swipeFood) return;
var deltaX = x - swipeStartX;
var deltaY = y - swipeStartY;
var swipeDistance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
if (swipeDistance < SWIPE_THRESHOLD) {
// Not a swipe, reset
swipeFood.alpha = 1;
isSwipeActive = false;
swipeFood = null;
return;
}
// Determine swipe direction
var targetGridX = swipeFood.gridX;
var targetGridY = swipeFood.gridY;
var absDeltaX = Math.abs(deltaX);
var absDeltaY = Math.abs(deltaY);
if (absDeltaX > absDeltaY) {
// Horizontal swipe
if (deltaX > 0) {
targetGridX++; // Swipe right
} else {
targetGridX--; // Swipe left
}
} else {
// Vertical swipe
if (deltaY > 0) {
targetGridY++; // Swipe down
} else {
targetGridY--; // Swipe up
}
}
// Check if target position is valid
if (isValidPosition(targetGridX, targetGridY) && grid[targetGridX] && grid[targetGridX][targetGridY]) {
var targetFood = grid[targetGridX][targetGridY];
if (!targetFood.isAnimating) {
LK.getSound('swap').play();
// Store original positions BEFORE any swap
var tempFood1 = swipeFood;
var tempFood2 = targetFood;
var originalX1 = tempFood1.gridX;
var originalY1 = tempFood1.gridY;
var originalX2 = tempFood2.gridX;
var originalY2 = tempFood2.gridY;
// Try swap
swapFoods(tempFood1, tempFood2);
LK.setTimeout(function () {
var matches = findAllMatches();
if (matches.length === 0) {
// Invalid move, animate swap back to original positions
var targetX1 = GRID_START_X + originalX1 * CELL_SIZE + CELL_SIZE / 2;
var targetY1 = GRID_START_Y + originalY1 * CELL_SIZE + CELL_SIZE / 2;
var targetX2 = GRID_START_X + originalX2 * CELL_SIZE + CELL_SIZE / 2;
var targetY2 = GRID_START_Y + originalY2 * CELL_SIZE + CELL_SIZE / 2;
tempFood1.animateToPosition(targetX1, targetY1);
tempFood2.animateToPosition(targetX2, targetY2);
// Restore original grid positions
grid[originalX1][originalY1] = tempFood1;
grid[originalX2][originalY2] = tempFood2;
tempFood1.gridX = originalX1;
tempFood1.gridY = originalY1;
tempFood2.gridX = originalX2;
tempFood2.gridY = originalY2;
} else {
// Valid move
movesLeft--;
updateUI();
processMatches();
checkGameEnd();
}
}, 350);
}
}
// Reset swipe state with fun bounce back animation
tween(swipeFood, {
alpha: 1,
scaleX: 1,
scaleY: 1
}, {
duration: 200,
easing: tween.bounceOut
});
isSwipeActive = false;
swipeFood = null;
};
// Start background music when game loads
LK.playMusic('happyBgMusic', {
loop: true
});
// Initialize the game - start with home screen
showHomeScreen();
function showLoginScreen() {
gameState = 'loginScreen';
// Clear all game elements
game.removeChildren();
// Set game background color
game.setBackgroundColor(0x87CEEB);
// Create login screen UI
var titleText = new Text2('LOGIN', {
size: 150,
fill: 0xFFFFFF
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 2048 / 2;
titleText.y = 300;
game.addChild(titleText);
// Username input field
var usernameField = LK.getAsset('inputField', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 8,
scaleY: 1.2
});
usernameField.x = 2048 / 2;
usernameField.y = 600;
usernameField.down = function () {
showKeyboard('username');
};
game.addChild(usernameField);
usernameDisplayText = new Text2('', {
size: 50,
fill: 0x000000
});
usernameDisplayText.anchor.set(0.5, 0.5);
usernameDisplayText.x = 2048 / 2;
usernameDisplayText.y = 600;
game.addChild(usernameDisplayText);
var usernameLabel = new Text2('Username', {
size: 60,
fill: 0xFFFFFF
});
usernameLabel.anchor.set(0.5, 0.5);
usernameLabel.x = 2048 / 2;
usernameLabel.y = 550;
game.addChild(usernameLabel);
// Password input field
var passwordField = LK.getAsset('passwordField', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 8,
scaleY: 1.2
});
passwordField.x = 2048 / 2;
passwordField.y = 800;
passwordField.down = function () {
showKeyboard('password');
};
game.addChild(passwordField);
passwordDisplayText = new Text2('', {
size: 50,
fill: 0x000000
});
passwordDisplayText.anchor.set(0.5, 0.5);
passwordDisplayText.x = 2048 / 2;
passwordDisplayText.y = 800;
game.addChild(passwordDisplayText);
var passwordLabel = new Text2('Password', {
size: 60,
fill: 0xFFFFFF
});
passwordLabel.anchor.set(0.5, 0.5);
passwordLabel.x = 2048 / 2;
passwordLabel.y = 750;
game.addChild(passwordLabel);
// Show keyboard automatically for username input
showKeyboard('username');
// Login button
var loginButton = new Container();
var loginBg = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2,
scaleY: 0.8
});
loginBg.tint = 0x4CAF50; // Green tint for login button
loginButton.addChild(loginBg);
var loginText = new Text2('LOGIN', {
size: 80,
fill: 0xFFFFFF
});
loginText.anchor.set(0.5, 0.5);
loginButton.addChild(loginText);
loginButton.x = 2048 / 2;
loginButton.y = 1000;
loginButton.down = function () {
LK.getSound('buttonClick').play();
tween(loginButton, {
scaleX: 0.9,
scaleY: 0.9
}, {
duration: 100,
easing: tween.easeOut
});
tween(loginButton, {
scaleX: 1,
scaleY: 1
}, {
duration: 150,
easing: tween.bounceOut
});
// Validate input fields
if (usernameText.length === 0 || passwordText.length === 0) {
// Show error message for empty fields
var errorText = new Text2('Please fill all fields', {
size: 60,
fill: 0xFF0000
});
errorText.anchor.set(0.5, 0.5);
errorText.x = 2048 / 2;
errorText.y = 900;
game.addChild(errorText);
// Remove error message after 2 seconds
LK.setTimeout(function () {
if (errorText && errorText.parent) {
errorText.destroy();
}
}, 2000);
return;
}
// Validate credentials
var existingUsers = storage.users || {};
var username = usernameText;
if (!existingUsers[username]) {
// Username doesn't exist
var errorText = new Text2('Username not found', {
size: 60,
fill: 0xFF0000
});
errorText.anchor.set(0.5, 0.5);
errorText.x = 2048 / 2;
errorText.y = 900;
game.addChild(errorText);
// Remove error message after 2 seconds
LK.setTimeout(function () {
if (errorText && errorText.parent) {
errorText.destroy();
}
}, 2000);
return;
}
if (existingUsers[username].password !== passwordText) {
// Wrong password
var errorText = new Text2('Incorrect password', {
size: 60,
fill: 0xFF0000
});
errorText.anchor.set(0.5, 0.5);
errorText.x = 2048 / 2;
errorText.y = 900;
game.addChild(errorText);
// Remove error message after 2 seconds
LK.setTimeout(function () {
if (errorText && errorText.parent) {
errorText.destroy();
}
}, 2000);
return;
}
// Valid credentials - login successful
storage.currentUser = username;
// Auto-save user data on login
saveUserProgress();
// Clear input fields
usernameText = '';
passwordText = '';
showHomeScreen();
};
game.addChild(loginButton);
// Back button
var backButton = new Container();
var backBg = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 0.6
});
backBg.tint = 0xFF4444; // Red tint for back button
backButton.addChild(backBg);
var backText = new Text2('BACK', {
size: 50,
fill: 0xFFFFFF
});
backText.anchor.set(0.5, 0.5);
backButton.addChild(backText);
backButton.x = 2048 / 2;
backButton.y = 1150;
backButton.down = function () {
LK.getSound('buttonClick').play();
tween(backButton, {
scaleX: 0.9,
scaleY: 0.9
}, {
duration: 100,
easing: tween.easeOut
});
tween(backButton, {
scaleX: 1,
scaleY: 1
}, {
duration: 150,
easing: tween.bounceOut
});
showHomeScreen();
};
game.addChild(backButton);
}
function showLeaderboard() {
gameState = 'leaderboard';
// Clear all game elements
game.removeChildren();
// Set game background color
game.setBackgroundColor(0x87CEEB);
// Create leaderboard screen UI
var titleText = new Text2('LEADERBOARD', {
size: 150,
fill: 0xFFFFFF
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 2048 / 2;
titleText.y = 300;
game.addChild(titleText);
// Get user-specific high score from storage
var currentUser = storage.currentUser || 'Guest';
var highScore = 0;
if (currentUser !== 'Guest' && storage.users && storage.users[currentUser]) {
highScore = storage.users[currentUser].highScore || 0;
} else {
highScore = storage.highScore || 0;
}
// Display high score
var highScoreText = new Text2('High Score: ' + highScore, {
size: 120,
fill: 0xFFD700
});
highScoreText.anchor.set(0.5, 0.5);
highScoreText.x = 2048 / 2;
highScoreText.y = 600;
game.addChild(highScoreText);
// Display current user
var userText = new Text2('Player: ' + currentUser, {
size: 80,
fill: 0xFFFFFF
});
userText.anchor.set(0.5, 0.5);
userText.x = 2048 / 2;
userText.y = 750;
game.addChild(userText);
// Show additional user stats for logged-in users
if (currentUser !== 'Guest' && storage.users && storage.users[currentUser]) {
var userData = storage.users[currentUser];
var levelText = new Text2('Level Reached: ' + (userData.level || 1), {
size: 70,
fill: 0xFFFFFF
});
levelText.anchor.set(0.5, 0.5);
levelText.x = 2048 / 2;
levelText.y = 850;
game.addChild(levelText);
var totalScoreText = new Text2('Total Score: ' + (userData.totalScore || 0), {
size: 70,
fill: 0xFFFFFF
});
totalScoreText.anchor.set(0.5, 0.5);
totalScoreText.x = 2048 / 2;
totalScoreText.y = 920;
game.addChild(totalScoreText);
} else {
var loginPromptText = new Text2('Login to track your progress!', {
size: 60,
fill: 0xFFD700
});
loginPromptText.anchor.set(0.5, 0.5);
loginPromptText.x = 2048 / 2;
loginPromptText.y = 850;
game.addChild(loginPromptText);
}
// Back button
var backButton = new Container();
var backBg = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2,
scaleY: 0.8
});
backBg.tint = 0xFF4444; // Red tint for back button
backButton.addChild(backBg);
var backText = new Text2('BACK', {
size: 80,
fill: 0xFFFFFF
});
backText.anchor.set(0.5, 0.5);
backButton.addChild(backText);
backButton.x = 2048 / 2;
backButton.y = 1000;
backButton.down = function () {
LK.getSound('buttonClick').play();
tween(backButton, {
scaleX: 0.9,
scaleY: 0.9
}, {
duration: 100,
easing: tween.easeOut
});
tween(backButton, {
scaleX: 1,
scaleY: 1
}, {
duration: 150,
easing: tween.bounceOut
});
showHomeScreen();
};
game.addChild(backButton);
}
function showCreateAccountScreen() {
gameState = 'createAccountScreen';
// Clear all game elements
game.removeChildren();
// Set game background color
game.setBackgroundColor(0x87CEEB);
// Create account screen UI
var titleText = new Text2('CREATE ACCOUNT', {
size: 120,
fill: 0xFFFFFF
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 2048 / 2;
titleText.y = 300;
game.addChild(titleText);
// Username input field
var usernameField = LK.getAsset('inputField', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 8,
scaleY: 1.2
});
usernameField.x = 2048 / 2;
usernameField.y = 600;
usernameField.down = function () {
showKeyboard('username');
};
game.addChild(usernameField);
createUsernameDisplayText = new Text2('', {
size: 50,
fill: 0x000000
});
createUsernameDisplayText.anchor.set(0.5, 0.5);
createUsernameDisplayText.x = 2048 / 2;
createUsernameDisplayText.y = 600;
game.addChild(createUsernameDisplayText);
var usernameLabel = new Text2('Choose Username', {
size: 60,
fill: 0xFFFFFF
});
usernameLabel.anchor.set(0.5, 0.5);
usernameLabel.x = 2048 / 2;
usernameLabel.y = 550;
game.addChild(usernameLabel);
// Password input field
var passwordField = LK.getAsset('passwordField', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 8,
scaleY: 1.2
});
passwordField.x = 2048 / 2;
passwordField.y = 800;
passwordField.down = function () {
showKeyboard('password');
};
game.addChild(passwordField);
createPasswordDisplayText = new Text2('', {
size: 50,
fill: 0x000000
});
createPasswordDisplayText.anchor.set(0.5, 0.5);
createPasswordDisplayText.x = 2048 / 2;
createPasswordDisplayText.y = 800;
game.addChild(createPasswordDisplayText);
var passwordLabel = new Text2('Choose Password', {
size: 60,
fill: 0xFFFFFF
});
passwordLabel.anchor.set(0.5, 0.5);
passwordLabel.x = 2048 / 2;
passwordLabel.y = 750;
game.addChild(passwordLabel);
// Show keyboard automatically for username input
showKeyboard('username');
// Create account button
var createButton = new Container();
var createBg = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2,
scaleY: 0.8
});
createBg.tint = 0x4CAF50; // Green tint for create button
createButton.addChild(createBg);
var createText = new Text2('CREATE', {
size: 70,
fill: 0xFFFFFF
});
createText.anchor.set(0.5, 0.5);
createButton.addChild(createText);
createButton.x = 2048 / 2;
createButton.y = 1000;
createButton.down = function () {
LK.getSound('buttonClick').play();
tween(createButton, {
scaleX: 0.9,
scaleY: 0.9
}, {
duration: 100,
easing: tween.easeOut
});
tween(createButton, {
scaleX: 1,
scaleY: 1
}, {
duration: 150,
easing: tween.bounceOut
});
// Validate input fields
if (usernameText.length === 0 || passwordText.length === 0) {
// Show error message for empty fields
var errorText = new Text2('Please fill all fields', {
size: 60,
fill: 0xFF0000
});
errorText.anchor.set(0.5, 0.5);
errorText.x = 2048 / 2;
errorText.y = 900;
game.addChild(errorText);
// Remove error message after 2 seconds
LK.setTimeout(function () {
if (errorText && errorText.parent) {
errorText.destroy();
}
}, 2000);
return;
}
// Check if username already exists
var existingUsers = storage.users || {};
if (existingUsers[usernameText]) {
// Show error message for existing username
var errorText = new Text2('Username already exists', {
size: 60,
fill: 0xFF0000
});
errorText.anchor.set(0.5, 0.5);
errorText.x = 2048 / 2;
errorText.y = 900;
game.addChild(errorText);
// Remove error message after 2 seconds
LK.setTimeout(function () {
if (errorText && errorText.parent) {
errorText.destroy();
}
}, 2000);
return;
}
// Create account with password
var username = usernameText;
// Store user credentials in storage
if (!storage.users) {
storage.users = {};
}
// Ensure the storage.users object is properly initialized before assignment
var newUserData = {
password: passwordText,
level: 1,
totalScore: 0,
highScore: 0
};
// Double-check storage.users exists before assignment
if (!storage.users) {
storage.users = {};
}
// Ensure storage.users is properly initialized before assignment
if (!storage.users) {
storage.users = {};
}
storage.users[username] = newUserData;
storage.currentUser = username;
// Auto-save new user data
saveUserProgress();
// Clear input fields
usernameText = '';
passwordText = '';
showHomeScreen();
};
game.addChild(createButton);
// Back button
var backButton = new Container();
var backBg = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 0.6
});
backBg.tint = 0xFF4444; // Red tint for back button
backButton.addChild(backBg);
var backText = new Text2('BACK', {
size: 50,
fill: 0xFFFFFF
});
backText.anchor.set(0.5, 0.5);
backButton.addChild(backText);
backButton.x = 2048 / 2;
backButton.y = 1150;
backButton.down = function () {
LK.getSound('buttonClick').play();
tween(backButton, {
scaleX: 0.9,
scaleY: 0.9
}, {
duration: 100,
easing: tween.easeOut
});
tween(backButton, {
scaleX: 1,
scaleY: 1
}, {
duration: 150,
easing: tween.bounceOut
});
showHomeScreen();
};
game.addChild(backButton);
}
function saveUserProgress() {
// Save current user data
if (storage.currentUser) {
// Initialize users storage if it doesn't exist
if (!storage.users) {
storage.users = {};
}
// Initialize user data if it doesn't exist
if (!storage.users[storage.currentUser]) {
storage.users[storage.currentUser] = {
password: '',
level: 1,
totalScore: 0,
highScore: 0
};
}
// Update user-specific progress
storage.users[storage.currentUser].level = currentLevel;
storage.users[storage.currentUser].totalScore = totalScore;
storage.users[storage.currentUser].currentScore = currentScore;
storage.users[storage.currentUser].movesLeft = movesLeft;
storage.users[storage.currentUser].targetMatches = targetMatches;
storage.users[storage.currentUser].matchesCleared = matchesCleared;
storage.users[storage.currentUser].maxUnlockedLevel = maxUnlockedLevel;
storage.users[storage.currentUser].gameState = gameState;
// Update high score if current total score is higher
var userHighScore = storage.users[storage.currentUser].highScore || 0;
if (totalScore > userHighScore) {
storage.users[storage.currentUser].highScore = totalScore;
}
// Also save to legacy storage locations for backward compatibility
storage.userLevel = currentLevel;
storage.userTotalScore = totalScore;
storage.userCurrentScore = currentScore;
storage.userMovesLeft = movesLeft;
storage.userTargetMatches = targetMatches;
storage.userMatchesCleared = matchesCleared;
storage.userMaxUnlockedLevel = maxUnlockedLevel;
storage.gameState = gameState;
}
// Save general game progress
storage.lastPlayedLevel = currentLevel;
storage.lastTotalScore = totalScore;
}
function loadUserProgress() {
// Load user-specific progress if logged in
if (storage.currentUser) {
// Initialize users storage if it doesn't exist
if (!storage.users) {
storage.users = {};
}
// Load from user account if it exists
if (storage.users[storage.currentUser]) {
var userData = storage.users[storage.currentUser];
currentLevel = userData.level || 1;
totalScore = userData.totalScore || 0;
currentScore = userData.currentScore || 0;
movesLeft = userData.movesLeft || 30;
targetMatches = userData.targetMatches || 10;
matchesCleared = userData.matchesCleared || 0;
maxUnlockedLevel = userData.maxUnlockedLevel || 1;
} else {
// Fallback to legacy storage for existing users
currentLevel = storage.userLevel || 1;
totalScore = storage.userTotalScore || 0;
currentScore = storage.userCurrentScore || 0;
movesLeft = storage.userMovesLeft || 30;
targetMatches = storage.userTargetMatches || 10;
matchesCleared = storage.userMatchesCleared || 0;
maxUnlockedLevel = storage.userMaxUnlockedLevel || 1;
}
} else {
// Load general progress for guests
currentLevel = storage.lastPlayedLevel || 1;
totalScore = storage.lastTotalScore || 0;
}
}