Code edit (1 edits merged)
Please save this source code
User prompt
Please fix the bug: 'Timeout.tick error: Cannot read properties of null (reading 'flipDown')' in or related to this line: 'firstCard.flipDown();' Line Number: 716
User prompt
Use cardSymbols2 instead of cardSymbols after level 20
Code edit (1 edits merged)
Please save this source code
User prompt
At level 50, if there is too much swapping at the same time, some cards don't fit to cell exactly when they finishing moving. Please fix this
User prompt
At level 50, randomly swap 2 cards per 2 seconds and also make swap per 1 move instead of 2 โช๐ก Consider importing and using the following plugins: @upit/tween.v1
User prompt
Please fix the bug: 'Timeout.tick error: Cannot read properties of null (reading 'flipDown')' in or related to this line: 'firstCard.flipDown();' Line Number: 870
User prompt
At level 50, some swapped cards don't move and exactly fit the cell. please fix it
User prompt
At level 50, randomly swap 6 cards per 5 seconds and also randomly swap 6 cards per 2 move โช๐ก Consider importing and using the following plugins: @upit/tween.v1
User prompt
after level 40, make random swapping 2 cards per 5 seconds and also make swap per 2 move instead of 4
User prompt
after level 40, make random swapping 2 cards per 10 seconds addition to swap per 4 moves โช๐ก Consider importing and using the following plugins: @upit/tween.v1
User prompt
after level 35, make random swapping 2 cards per 20 seconds addition to swap per 4 moves โช๐ก Consider importing and using the following plugins: @upit/tween.v1
User prompt
after level 30, make random swapping 2 cards per 4 move instead of 5 โช๐ก Consider importing and using the following plugins: @upit/tween.v1
User prompt
After level25, make random swapping 2 cards per 5 move instead of 10 โช๐ก Consider importing and using the following plugins: @upit/tween.v1
User prompt
Make card movement from swapping more slower โช๐ก Consider importing and using the following plugins: @upit/tween.v1
User prompt
Make card movement from swapping a bit slower โช๐ก Consider importing and using the following plugins: @upit/tween.v1
User prompt
When swapping cards, swapped cards should move towards the new cells โช๐ก Consider importing and using the following plugins: @upit/tween.v1
User prompt
After level 20, swap randomly 2 cards per 10 move
User prompt
Use โcardback8โ and โcardfront2โassets for cards after level45
User prompt
Use โcardback7โ and โcardfront2โassets for cards after level45
User prompt
Use โcardback7โ asset for cards after level35
User prompt
Use โcardback6โ asset for cards after level25
User prompt
Use โcardback5โ asset for cards after level20
User prompt
Use โcardback4โ asset for cards after level15
User prompt
Increase total card number after level 25
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ // Card class: represents a single card in the memory game var Card = Container.expand(function () { var self = Container.call(this); // Card properties self.symbol = null; // Symbol or id for matching self.isFaceUp = false; self.isMatched = false; self.index = -1; // Position in the grid // Card dimensions (will be set in game code) self.cardWidth = 0; self.cardHeight = 0; // Face-down asset (back of card) var backAssetId; var frontAssetId = 'cardFront'; if (typeof currentLevel !== "undefined" && currentLevel > 45) { backAssetId = 'CardBack8'; frontAssetId = 'CardFront2'; } else if (typeof currentLevel !== "undefined" && currentLevel > 35) { backAssetId = 'Cardback7'; } else if (typeof currentLevel !== "undefined" && currentLevel > 25) { backAssetId = 'CardBack6'; } else if (typeof currentLevel !== "undefined" && currentLevel > 20) { backAssetId = 'CardBack5'; } else if (typeof currentLevel !== "undefined" && currentLevel > 15) { backAssetId = 'CardBack4'; } else if (typeof currentLevel !== "undefined" && currentLevel > 10) { backAssetId = 'CardBack3'; } else if (typeof currentLevel !== "undefined" && currentLevel > 5) { backAssetId = 'CardBack2'; } else { backAssetId = 'cardBack'; } var back = self.attachAsset(backAssetId, { anchorX: 0.5, anchorY: 0.5 }); // Face-up asset (front of card, shows symbol) var front = self.attachAsset(frontAssetId, { anchorX: 0.5, anchorY: 0.5 }); // Symbol text (shows symbol when face up) var symbolText = new Text2('', { size: 90, fill: 0x222222 }); symbolText.anchor.set(0.5, 0.5); front.addChild(symbolText); // Set card size self.setSize = function (w, h) { self.cardWidth = w; self.cardHeight = h; back.width = w; back.height = h; front.width = w; front.height = h; }; // Set the symbol for this card self.setSymbol = function (symbol) { self.symbol = symbol; symbolText.setText(symbol); }; // Flip the card to face up (with animation) self.flipUp = function (_onFinish) { if (self.isFaceUp || self.isMatched) { return; } self.isFaceUp = true; // Animate flip: scaleX 1 -> 0, swap, 0 -> 1 tween(self, { scaleX: 0 }, { duration: 120, easing: tween.cubicIn, onFinish: function onFinish() { back.visible = false; front.visible = true; tween(self, { scaleX: 1 }, { duration: 120, easing: tween.cubicOut, onFinish: function onFinish() { if (_onFinish) { _onFinish(); } } }); } }); }; // Flip the card to face down (with animation) self.flipDown = function (_onFinish2) { if (!self.isFaceUp || self.isMatched) { return; } self.isFaceUp = false; // Animate flip: scaleX 1 -> 0, swap, 0 -> 1 tween(self, { scaleX: 0 }, { duration: 120, easing: tween.cubicIn, onFinish: function onFinish() { front.visible = false; back.visible = true; tween(self, { scaleX: 1 }, { duration: 120, easing: tween.cubicOut, onFinish: function onFinish() { if (_onFinish2) { _onFinish2(); } } }); } }); }; // Instantly show face up (no animation) self.showFaceUp = function () { self.isFaceUp = true; self.scaleX = 1; back.visible = false; front.visible = true; }; // Instantly show face down (no animation) self.showFaceDown = function () { self.isFaceUp = false; self.scaleX = 1; front.visible = false; back.visible = true; }; // Mark as matched (disable interaction, highlight) self.setMatched = function () { self.isMatched = true; // Subtle highlight tween(self, { tint: 0xA0FFA0 }, { duration: 300, easing: tween.linear }); }; // Handle tap/click self.down = function (x, y, obj) { if (self.isFaceUp || self.isMatched || game.lockInput) { return; } game.onCardTapped(self); }; // Initialize: show face down self.showFaceDown(); return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x2e3a4f // Deep blue background }); /**** * Game Code ****/ // --- Card symbols (use simple emojis for MVP) --- // Tween plugin for card flip and reveal animations var cardSymbols = ["๐", "๐", "๐", "๐", "๐", "๐", "๐", "๐", "๐ฅ", "๐ฅ", "๐", "๐", "๐", "๐", "๐ฅฅ", "๐ "]; var cardSymbols2 = ["๐ถ", "๐ฑ", "๐ญ", "๐ฐ", "๐ฆ", "๐ป", "๐ผ", "๐จ", "๐ฏ", "๐ฆ", "๐ฎ", "๐ท", "๐ธ", "๐ต", "๐", "๐ง", "๐ฆ", "๐ฆโโฌ", "๐ฅ", "๐ฆ", "๐ฆ ", "๐ฆ", "๐ฆ", "๐บ"]; // --- Game settings --- var maxLevel = 50; var currentLevel = 1; // Level configuration: grid size per level (increase difficulty) function getLevelConfig(level) { // Level 1-5: 4x4, 6-10: 5x4, 11-15: 6x5, 16-20: 6x6, 21-25: 7x6 if (level <= 5) { return { cols: 4, rows: 4 }; } if (level <= 10) { return { cols: 5, rows: 4 }; } if (level <= 15) { return { cols: 6, rows: 5 }; } if (level <= 20) { return { cols: 6, rows: 6 }; } if (level <= 35) { return { cols: 8, rows: 6 }; } if (level <= 45) { return { cols: 8, rows: 7 }; } return { cols: 9, rows: 7 }; } var gridCols = getLevelConfig(currentLevel).cols; var gridRows = getLevelConfig(currentLevel).rows; var totalPairs = gridCols * gridRows / 2; var cardSpacing = 36; // px between cards // --- Card assets (simple colored rectangles) --- // --- Game state --- var cards = []; // All card objects var firstCard = null; var secondCard = null; var lockInput = false; // Prevent input during animations var moves = 0; var matchesFound = 0; var timer = null; var elapsedTime = 0; // in seconds var timerText = null; var movesText = null; // --- GUI: Moves, Timer, Level --- var levelText = new Text2('Level: 1', { size: 80, fill: 0xFFFFFF }); levelText.anchor.set(0.5, 0); LK.gui.top.addChild(levelText); timerText = new Text2('Time: 0s', { size: 80, fill: 0xFFFFFF }); timerText.anchor.set(0.5, 0); LK.gui.top.addChild(timerText); // Position GUI elements (timer right, avoid top-left 100x100) // Align level indicator to top middle using LK.gui.top levelText.x = -10; levelText.y = 20; timerText.x = 500; // --- Timer for random swap after level 35 --- var randomSwapTimer = null; var randomSwapTimer10s = null; var randomSwapTimer5s = null; var randomSwapTimer2s = null; // --- Swap queue for level 50+ to prevent overlapping swaps --- var swapQueue = []; var swapInProgress = false; // Helper: Queue a swap (for level 50+) function queueCardSwap(cardA, cardB) { swapQueue.push({ cardA: cardA, cardB: cardB }); processSwapQueue(); } // Helper: Process swap queue (for level 50+) function processSwapQueue() { if (swapInProgress || swapQueue.length === 0) { return; } swapInProgress = true; var swap = swapQueue.shift(); var cardA = swap.cardA; var cardB = swap.cardB; var tempSymbol = cardA.symbol; cardA.setSymbol(cardB.symbol); cardB.setSymbol(tempSymbol); var tempX = cardA.x; var tempY = cardA.y; var swapsDone = 0; function onSwapDone() { swapsDone++; if (swapsDone === 2) { swapInProgress = false; // Snap to grid to avoid drift cardA.x = Math.round(cardA.x); cardA.y = Math.round(cardA.y); cardB.x = Math.round(cardB.x); cardB.y = Math.round(cardB.y); processSwapQueue(); } } tween(cardA, { x: cardB.x, y: cardB.y }, { duration: 1200, easing: tween.cubicInOut, onFinish: onSwapDone }); tween(cardB, { x: tempX, y: tempY }, { duration: 1200, easing: tween.cubicInOut, onFinish: onSwapDone }); } // --- Helper: Shuffle array (Fisher-Yates) --- function shuffleArray(arr) { for (var i = arr.length - 1; i > 0; i--) { var j = Math.floor(Math.random() * (i + 1)); var temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } } // --- Layout cards --- function layoutCards() { // Remove old cards if any for (var i = 0; i < cards.length; i++) { cards[i].destroy(); } cards = []; // Prepare symbols (duplicate and shuffle) var symbols = []; var useSymbols = currentLevel > 20 ? cardSymbols2 : cardSymbols; for (var i = 0; i < totalPairs; i++) { symbols.push(useSymbols[i]); symbols.push(useSymbols[i]); } shuffleArray(symbols); // Card size: fit grid to 2048x2732 with spacing var availableWidth = 2048 - cardSpacing * (gridCols + 1); var availableHeight = 1800 - cardSpacing * (gridRows + 1); // leave space for GUI var cardWidth = Math.floor(availableWidth / gridCols); var cardHeight = Math.floor(availableHeight / gridRows); // Center grid var gridPixelWidth = cardWidth * gridCols + cardSpacing * (gridCols - 1); var gridPixelHeight = cardHeight * gridRows + cardSpacing * (gridRows - 1); var startX = Math.floor((2048 - gridPixelWidth) / 2) + cardWidth / 2; var startY = 350 + cardHeight / 2; // leave space for GUI // Create cards for (var row = 0; row < gridRows; row++) { for (var col = 0; col < gridCols; col++) { var idx = row * gridCols + col; var card = new Card(); card.setSize(cardWidth, cardHeight); card.setSymbol(symbols[idx]); card.index = idx; card.x = startX + col * (cardWidth + cardSpacing); card.y = startY + row * (cardHeight + cardSpacing); card.showFaceDown(); game.addChild(card); cards.push(card); } } } // --- Reset game state --- function resetGame() { firstCard = null; secondCard = null; lockInput = false; moves = 0; matchesFound = 0; elapsedTime = 0; if (typeof levelText !== "undefined") { levelText.setText('Level: ' + currentLevel); } // Update grid size for this level var config = getLevelConfig(currentLevel); gridCols = config.cols; gridRows = config.rows; totalPairs = gridCols * gridRows / 2; timerText.setText('Time: 0s'); layoutCards(); if (timer) { LK.clearInterval(timer); } timer = LK.setInterval(function () { elapsedTime++; timerText.setText('Time: ' + elapsedTime + 's'); }, 1000); // --- Timer-based random swap after level 35 --- if (typeof randomSwapTimer !== "undefined" && randomSwapTimer) { LK.clearInterval(randomSwapTimer); randomSwapTimer = null; } if (currentLevel >= 50) { // At level 50, swap every 2s, 5s, 10s, and 20s using the swap queue swapQueue = []; swapInProgress = false; randomSwapTimer = LK.setInterval(function () { // 20s swap var swappable = []; for (var i = 0; i < cards.length; i++) { if (!cards[i].isMatched && !cards[i].isFaceUp) { swappable.push(cards[i]); } } if (swappable.length >= 2) { var idx1 = Math.floor(Math.random() * swappable.length); var idx2 = idx1; while (idx2 === idx1) { idx2 = Math.floor(Math.random() * swappable.length); } var cardA = swappable[idx1]; var cardB = swappable[idx2]; queueCardSwap(cardA, cardB); } }, 20000); // 20 seconds // Add a second timer for 10s swap if (typeof randomSwapTimer10s !== "undefined" && randomSwapTimer10s) { LK.clearInterval(randomSwapTimer10s); randomSwapTimer10s = null; } randomSwapTimer10s = LK.setInterval(function () { var swappable = []; for (var i = 0; i < cards.length; i++) { if (!cards[i].isMatched && !cards[i].isFaceUp) { swappable.push(cards[i]); } } if (swappable.length >= 2) { var idx1 = Math.floor(Math.random() * swappable.length); var idx2 = idx1; while (idx2 === idx1) { idx2 = Math.floor(Math.random() * swappable.length); } var cardA = swappable[idx1]; var cardB = swappable[idx2]; queueCardSwap(cardA, cardB); } }, 10000); // 10 seconds // Add a third timer for 5s swap if (typeof randomSwapTimer5s !== "undefined" && randomSwapTimer5s) { LK.clearInterval(randomSwapTimer5s); randomSwapTimer5s = null; } randomSwapTimer5s = LK.setInterval(function () { var swappable = []; for (var i = 0; i < cards.length; i++) { if (!cards[i].isMatched && !cards[i].isFaceUp) { swappable.push(cards[i]); } } if (swappable.length >= 2) { var idx1 = Math.floor(Math.random() * swappable.length); var idx2 = idx1; while (idx2 === idx1) { idx2 = Math.floor(Math.random() * swappable.length); } var cardA = swappable[idx1]; var cardB = swappable[idx2]; queueCardSwap(cardA, cardB); } }, 5000); // 5 seconds // Add a fourth timer for 2s swap if (typeof randomSwapTimer2s !== "undefined" && randomSwapTimer2s) { LK.clearInterval(randomSwapTimer2s); randomSwapTimer2s = null; } randomSwapTimer2s = LK.setInterval(function () { var swappable = []; for (var i = 0; i < cards.length; i++) { if (!cards[i].isMatched && !cards[i].isFaceUp) { swappable.push(cards[i]); } } if (swappable.length >= 2) { var idx1 = Math.floor(Math.random() * swappable.length); var idx2 = idx1; while (idx2 === idx1) { idx2 = Math.floor(Math.random() * swappable.length); } var cardA = swappable[idx1]; var cardB = swappable[idx2]; queueCardSwap(cardA, cardB); } }, 2000); // 2 seconds } else if (currentLevel > 40) { // Only 20s swap randomSwapTimer = LK.setInterval(function () { var swappable = []; for (var i = 0; i < cards.length; i++) { if (!cards[i].isMatched && !cards[i].isFaceUp) { swappable.push(cards[i]); } } if (swappable.length >= 2) { var idx1 = Math.floor(Math.random() * swappable.length); var idx2 = idx1; while (idx2 === idx1) { idx2 = Math.floor(Math.random() * swappable.length); } var cardA = swappable[idx1]; var cardB = swappable[idx2]; var tempSymbol = cardA.symbol; cardA.setSymbol(cardB.symbol); cardB.setSymbol(tempSymbol); var tempX = cardA.x; var tempY = cardA.y; tween(cardA, { x: cardB.x, y: cardB.y }, { duration: 1200, easing: tween.cubicInOut }); tween(cardB, { x: tempX, y: tempY }, { duration: 1200, easing: tween.cubicInOut }); } }, 20000); // 20 seconds // Clear 10s timer if it exists if (typeof randomSwapTimer10s !== "undefined" && randomSwapTimer10s) { LK.clearInterval(randomSwapTimer10s); randomSwapTimer10s = null; } } else { randomSwapTimer = null; if (typeof randomSwapTimer10s !== "undefined" && randomSwapTimer10s) { LK.clearInterval(randomSwapTimer10s); randomSwapTimer10s = null; } } } // --- Card tap handler --- game.onCardTapped = function (card) { if (lockInput || card.isFaceUp || card.isMatched) { return; } if (!firstCard) { firstCard = card; card.flipUp(); } else if (!secondCard && card !== firstCard) { secondCard = card; lockInput = true; card.flipUp(function () { // Check for match after both are face up LK.setTimeout(function () { checkMatch(); }, 350); }); } }; // --- Check for match --- function checkMatch() { moves++; // After level 40, swap 2 random cards every 2 moves; after level 30, every 4 moves; after level 25, every 5 moves; after level 20, every 10 moves var shouldSwap = false; if (currentLevel >= 50 && moves % 1 === 0 && cards.length > 1) { shouldSwap = true; } else if (currentLevel > 40 && moves % 2 === 0 && cards.length > 1) { shouldSwap = true; } else if (currentLevel > 30 && moves % 4 === 0 && cards.length > 1) { shouldSwap = true; } else if (currentLevel > 25 && moves % 5 === 0 && cards.length > 1) { shouldSwap = true; } else if (currentLevel > 20 && moves % 10 === 0 && cards.length > 1) { shouldSwap = true; } if (shouldSwap) { // Find all cards that are not matched and not currently face up var swappable = []; for (var i = 0; i < cards.length; i++) { if (!cards[i].isMatched && !cards[i].isFaceUp) { swappable.push(cards[i]); } } if (swappable.length >= 2) { var idx1 = Math.floor(Math.random() * swappable.length); var idx2 = idx1; while (idx2 === idx1) { idx2 = Math.floor(Math.random() * swappable.length); } var cardA = swappable[idx1]; var cardB = swappable[idx2]; if (currentLevel >= 50) { queueCardSwap(cardA, cardB); } else { // Old behavior for lower levels var tempSymbol = cardA.symbol; cardA.setSymbol(cardB.symbol); cardB.setSymbol(tempSymbol); var tempX = cardA.x; var tempY = cardA.y; tween(cardA, { x: cardB.x, y: cardB.y }, { duration: 1200, easing: tween.cubicInOut }); tween(cardB, { x: tempX, y: tempY }, { duration: 1200, easing: tween.cubicInOut }); } } } if (firstCard && secondCard && firstCard.symbol === secondCard.symbol) { // Match! firstCard.setMatched(); secondCard.setMatched(); matchesFound++; // Subtle scale animation for matched cards, then disappear if (firstCard && secondCard) { var matched1 = firstCard; var matched2 = secondCard; if (matched1) { tween(matched1, { scaleX: 1.15, scaleY: 1.15 }, { duration: 120, easing: tween.cubicOut, onFinish: function onFinish() { if (matched1) { tween(matched1, { scaleX: 0, scaleY: 0, alpha: 0 }, { duration: 180, onFinish: function onFinish() { if (matched1) { matched1.destroy(); } } }); } } }); } if (matched2) { tween(matched2, { scaleX: 1.15, scaleY: 1.15 }, { duration: 120, easing: tween.cubicOut, onFinish: function onFinish() { if (matched2) { tween(matched2, { scaleX: 0, scaleY: 0, alpha: 0 }, { duration: 180, onFinish: function onFinish() { if (matched2) { matched2.destroy(); } } }); } } }); } } // Check for win if (matchesFound === totalPairs) { LK.clearInterval(timer); LK.setScore(moves); // Use moves as score (lower is better) if (currentLevel < maxLevel) { currentLevel++; // Short delay before next level LK.setTimeout(function () { resetGame(); }, 1200); } else { LK.showYouWin(); } } // Reset selection firstCard = null; secondCard = null; lockInput = false; } else { // Not a match: flip both back after short delay LK.setTimeout(function () { if (firstCard && typeof firstCard.flipDown === "function") { firstCard.flipDown(); } if (secondCard && typeof secondCard.flipDown === "function") { secondCard.flipDown(function () { firstCard = null; secondCard = null; lockInput = false; }); } else { // If secondCard is missing, still reset state after delay firstCard = null; secondCard = null; lockInput = false; } }, 600); } } // --- Game update (not used for logic, but required) --- game.update = function () { // No per-frame logic needed for MVP }; // --- DEV Button: Center bottom, always visible, go to next level --- var devBtn = new Text2('DEV', { size: 90, fill: 0xFF00FF, font: "Impact" }); devBtn.anchor.set(0.5, 1); // Place DEV button slightly left of center devBtn.x = -120; devBtn.y = -50; LK.gui.bottom.addChild(devBtn); // DEV button handler: go to next level immediately (max 25) devBtn.down = function (x, y, obj) { if (currentLevel < maxLevel) { currentLevel++; resetGame(); } else { LK.showYouWin(); } }; // --- Reveal Button: Center bottom, always visible, reveals all cards --- var revealBtn = new Text2('Reveal', { size: 90, fill: 0x00CCFF, font: "Impact" }); revealBtn.anchor.set(0.5, 1); // Place Reveal button slightly right of center revealBtn.x = 120; revealBtn.y = -50; LK.gui.bottom.addChild(revealBtn); // Reveal button handler: flip all cards face up (if not matched) revealBtn.down = function (x, y, obj) { for (var i = 0; i < cards.length; i++) { var card = cards[i]; if (!card.isFaceUp && !card.isMatched) { card.flipUp(); } } }; // --- Start game --- resetGame(); // --- Game over handler (reset on game over) --- game.onGameOver = function () { if (timer) { LK.clearInterval(timer); } if (typeof randomSwapTimer !== "undefined" && randomSwapTimer) { LK.clearInterval(randomSwapTimer); randomSwapTimer = null; } if (typeof randomSwapTimer10s !== "undefined" && randomSwapTimer10s) { LK.clearInterval(randomSwapTimer10s); randomSwapTimer10s = null; } if (typeof randomSwapTimer5s !== "undefined" && randomSwapTimer5s) { LK.clearInterval(randomSwapTimer5s); randomSwapTimer5s = null; } if (typeof randomSwapTimer2s !== "undefined" && randomSwapTimer2s) { LK.clearInterval(randomSwapTimer2s); randomSwapTimer2s = null; } currentLevel = 1; resetGame(); }; // --- Win handler (reset on win) --- game.onYouWin = function () { if (timer) { LK.clearInterval(timer); } if (typeof randomSwapTimer !== "undefined" && randomSwapTimer) { LK.clearInterval(randomSwapTimer); randomSwapTimer = null; } if (typeof randomSwapTimer10s !== "undefined" && randomSwapTimer10s) { LK.clearInterval(randomSwapTimer10s); randomSwapTimer10s = null; } if (typeof randomSwapTimer5s !== "undefined" && randomSwapTimer5s) { LK.clearInterval(randomSwapTimer5s); randomSwapTimer5s = null; } if (typeof randomSwapTimer2s !== "undefined" && randomSwapTimer2s) { LK.clearInterval(randomSwapTimer2s); randomSwapTimer2s = null; } currentLevel = 1; // Game will reset automatically by LK };
===================================================================
--- original.js
+++ change.js