User prompt
Add my image's that called daisy , rock and Water to the fruit's
User prompt
add my image's that called Coconut , kiwi , apple , pear , Pineapple , Avocado from the fruits
User prompt
Show fruits for a few seconds at each level and increase the time at each level
User prompt
remove the timer
User prompt
The duration of showing the fruits should be longer at each level and the number of seconds it will be shown should be written on the top of the cards.
User prompt
show how mony hearts left in left down
User prompt
How many lives are left should be shown with hearts at the bottom left and one heart should be lost for every mistake.
User prompt
level yükseldikçe can sayısı da artsın
User prompt
change the fruit colors by the image's in my assests that named Watermelon , Lemon , Cherry , Orange , Grape , Banana , Peach and Strawberry
User prompt
use the "watermelon" image for one fruit
Code edit (1 edits merged)
Please save this source code
User prompt
Fruit Memory Match
Initial prompt
Make a memory game with cards, have fruit pictures on the cards and have 2 of each fruit. First show all the cards for a while and then close them all. The card that the player clicks on is opened and the picture that appears must be the same as the picture of the second card that he opens. If they are the same, the cards are open. If they are different, the cards are closed and have a life system. For every mistake, 1 life is lost and there is a life right. Let the game be in 10 different levels. Each level will be harder than the previous one. You cannot pass to the next level without completing it. The number of lives should change according to the difficulty of the level.
/**** * 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 states: 'hidden', 'revealed', 'matched' self.state = 'hidden'; self.fruitId = null; // Will be set on creation // Card back (hidden state) var back = self.attachAsset('cardBack', { anchorX: 0.5, anchorY: 0.5 }); self.back = back; // Card front (fruit image, revealed state) // Default to watermelon image, will be replaced by setFruit var front = self.attachAsset('Watermelon', { anchorX: 0.5, anchorY: 0.5 }); self.front = front; self.front.visible = false; // Set fruit image for this card self.setFruit = function (fruitId) { self.fruitId = fruitId; // Map fruitId to asset name in assets section var assetMap = { 'banana': 'Banana', 'cherry': 'Cherry', 'orange': 'Orange', 'grape': 'grape', 'lemon': 'lemon', 'peach': 'Peach', 'strawberry': 'strawberry', 'watermelon': 'Watermelon', 'Coconut': 'Coconut', 'Kiwi': 'Kiwi', 'apple': 'apple', 'Pear': 'Pear', 'Pineapple': 'Pineapple', 'Avocado': 'Avocado', 'Daisy': 'Daisy', 'Rock': 'Rock', 'Water': 'Water' }; var assetName = assetMap[fruitId] || 'fruit'; var fruitAsset = LK.getAsset(assetName, { anchorX: 0.5, anchorY: 0.5 }); // Remove old front if present if (self.front && self.front.parent) self.removeChild(self.front); self.front = fruitAsset; self.addChild(self.front); self.front.visible = false; self.front.x = 0; self.front.y = 0; self.back.x = 0; self.back.y = 0; }; // Reveal the card (show fruit) self.reveal = function () { if (self.state !== 'hidden') return; self.state = 'revealed'; self.back.visible = false; self.front.visible = true; // Optional: flip animation tween(self, { scaleX: 0 }, { duration: 80, easing: tween.cubicIn, onFinish: function onFinish() { self.back.visible = false; self.front.visible = true; tween(self, { scaleX: 1 }, { duration: 80, easing: tween.cubicOut }); } }); }; // Hide the card (show back) self.hide = function () { if (self.state !== 'revealed') return; self.state = 'hidden'; // Optional: flip animation tween(self, { scaleX: 0 }, { duration: 80, easing: tween.cubicIn, onFinish: function onFinish() { self.front.visible = false; self.back.visible = true; tween(self, { scaleX: 1 }, { duration: 80, easing: tween.cubicOut }); } }); }; // Mark as matched (permanently revealed) self.match = function () { self.state = 'matched'; self.back.visible = false; self.front.visible = true; // Optional: small scale bounce tween(self, { scaleX: 1.15, scaleY: 1.15 }, { duration: 100, easing: tween.bounceOut, onFinish: function onFinish() { tween(self, { scaleX: 1, scaleY: 1 }, { duration: 100 }); } }); }; // Handle tap self.down = function (x, y, obj) { if (self.state === 'hidden' && !game.lockInput) { game.onCardTapped(self); } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x000000 }); /**** * Game Code ****/ // Fruit pool (all fruits with images in assets) var fruitIds = ['banana', 'cherry', 'orange', 'grape', 'lemon', 'peach', 'strawberry', 'watermelon', 'Coconut', 'Kiwi', 'apple', 'Pear', 'Pineapple', 'Avocado', 'Daisy', 'Rock', 'Water']; // No need to dynamically create fruit image assets, all are defined in assets section // Game variables var level = 1; var maxLevel = 10; var cards = []; var revealedCards = []; var matchedPairs = 0; var totalPairs = 0; var lives = 0; var maxLives = 0; var lockInput = false; var levelText, messageText; // Heart/lives display var heartNodes = []; var heartAssetName = 'heart'; // Use a heart image asset (add to assets if not present) var heartSize = 80; var heartSpacing = 20; var heartMargin = 30; // --- MENU SYSTEM --- // Menu container var menuContainer = new Container(); LK.gui.center.addChild(menuContainer); // Play button with ButtonBG var playBtnContainer = new Container(); var playBtn = new Text2('Play', { size: 140, fill: "#fff", font: "Impact" }); playBtn.anchor.set(0.5, 0.5); playBtn.y = 0; var playBtnBG = LK.getAsset('ButtonBG', { anchorX: 0.5, anchorY: 0.5 }); playBtnBG.width = playBtn.width + 80; playBtnBG.height = playBtn.height + 60; playBtnBG.x = 0; playBtnBG.y = 0; playBtnContainer.addChild(playBtnBG); playBtnContainer.addChild(playBtn); playBtnContainer.y = -120; menuContainer.addChild(playBtnContainer); // Settings button with ButtonBG var settingsBtnContainer = new Container(); var settingsBtn = new Text2('Settings', { size: 100, fill: "#fff", font: "Impact" }); settingsBtn.anchor.set(0.5, 0.5); settingsBtn.y = 0; var settingsBtnBG = LK.getAsset('ButtonBG', { anchorX: 0.5, anchorY: 0.5 }); settingsBtnBG.width = settingsBtn.width + 80; settingsBtnBG.height = settingsBtn.height + 60; settingsBtnBG.x = 0; settingsBtnBG.y = 0; settingsBtnContainer.addChild(settingsBtnBG); settingsBtnContainer.addChild(settingsBtn); settingsBtnContainer.y = 80; menuContainer.addChild(settingsBtnContainer); // Settings panel (hidden by default) var settingsPanel = new Container(); settingsPanel.visible = false; settingsPanel.y = 250; menuContainer.addChild(settingsPanel); // Sound toggle button with ButtonBG var soundOn = true; var soundBtnContainer = new Container(); var soundBtn = new Text2('Sound: On', { size: 80, fill: "#fff", font: "Impact" }); soundBtn.anchor.set(0.5, 0.5); soundBtn.y = 0; var soundBtnBG = LK.getAsset('ButtonBG', { anchorX: 0.5, anchorY: 0.5 }); soundBtnBG.width = soundBtn.width + 80; soundBtnBG.height = soundBtn.height + 60; soundBtnBG.x = 0; soundBtnBG.y = 0; soundBtnContainer.addChild(soundBtnBG); soundBtnContainer.addChild(soundBtn); soundBtnContainer.y = 0; settingsPanel.addChild(soundBtnContainer); // Music toggle button with ButtonBG var musicOn = true; var musicBtnContainer = new Container(); var musicBtn = new Text2('Music: On', { size: 80, fill: "#fff", font: "Impact" }); musicBtn.anchor.set(0.5, 0.5); musicBtn.y = 0; var musicBtnBG = LK.getAsset('ButtonBG', { anchorX: 0.5, anchorY: 0.5 }); musicBtnBG.width = musicBtn.width + 80; musicBtnBG.height = musicBtn.height + 60; musicBtnBG.x = 0; musicBtnBG.y = 0; musicBtnContainer.addChild(musicBtnBG); musicBtnContainer.addChild(musicBtn); musicBtnContainer.y = 120; settingsPanel.addChild(musicBtnContainer); // Menu logic var menuActive = true; function showMenu() { menuContainer.visible = true; levelText.visible = false; messageText.visible = false; pauseBtnContainer.visible = false; pauseMenuContainer.visible = false; // Hide all cards and hearts if present for (var i = 0; i < cards.length; i++) { cards[i].visible = false; } for (var i = 0; i < heartNodes.length; i++) { heartNodes[i].visible = false; } menuActive = true; lockInput = true; game.lockInput = true; } function hideMenu() { menuContainer.visible = false; levelText.visible = true; pauseBtnContainer.visible = true; pauseMenuContainer.visible = false; for (var i = 0; i < cards.length; i++) { cards[i].visible = true; } for (var i = 0; i < heartNodes.length; i++) { heartNodes[i].visible = true; } menuActive = false; lockInput = false; game.lockInput = false; } // Play button handler playBtnContainer.down = function (x, y, obj) { if (soundOn) LK.getSound('Click').play(); hideMenu(); // Start/restart game startLevel(1); }; // Settings button handler settingsBtnContainer.down = function (x, y, obj) { if (soundOn) LK.getSound('Click').play(); settingsPanel.visible = !settingsPanel.visible; }; // Sound toggle handler soundBtnContainer.down = function (x, y, obj) { if (soundOn) LK.getSound('Click').play(); soundOn = !soundOn; soundBtn.setText('Sound: ' + (soundOn ? 'On' : 'Off')); LK.setSoundEnabled && LK.setSoundEnabled(soundOn); }; // Music toggle handler musicBtnContainer.down = function (x, y, obj) { if (soundOn) LK.getSound('Click').play(); musicOn = !musicOn; musicBtn.setText('Music: ' + (musicOn ? 'On' : 'Off')); LK.setMusicEnabled && LK.setMusicEnabled(musicOn); }; // --- END MENU SYSTEM --- // GUI setup levelText = new Text2('Level 1', { size: 90, fill: "#fff" }); levelText.anchor.set(0.5, 0); // We'll position these below the cards after the board is created, so just add to game for now game.addChild(levelText); // Message text (centered, for "Level X", "You Win", etc) messageText = new Text2('', { size: 140, fill: "#fff" }); messageText.anchor.set(0.5, 0.5); LK.gui.center.addChild(messageText); messageText.visible = false; // --- PAUSE BUTTON & PAUSE MENU --- // Create a container for the pause button and its background var pauseBtnContainer = new Container(); // Add ButtonBG image as background var pauseBtnBG = LK.getAsset('ButtonBG', { anchorX: 0.5, anchorY: 0.5 }); pauseBtnContainer.addChild(pauseBtnBG); // Create the pause button text var pauseBtn = new Text2('Pause', { size: 80, fill: "#fff", font: "Impact" }); pauseBtn.anchor.set(0.5, 0.5); pauseBtnContainer.addChild(pauseBtn); // Place pause button just below the Level text, even further to the right of center pauseBtnContainer.x = 2048 / 2 + 260; // Move 260px to the right pauseBtnContainer.y = levelText.y + levelText.height + 10; // 10px below the Level text LK.gui.addChild(pauseBtnContainer); pauseBtnContainer.visible = false; // Pause menu container var pauseMenuContainer = new Container(); pauseMenuContainer.visible = false; LK.gui.center.addChild(pauseMenuContainer); // Resume button with ButtonBG var resumeBtnContainer = new Container(); var resumeBtn = new Text2('Resume', { size: 120, fill: "#fff", font: "Impact" }); resumeBtn.anchor.set(0.5, 0.5); resumeBtn.y = 0; var resumeBtnBG = LK.getAsset('ButtonBG', { anchorX: 0.5, anchorY: 0.5 }); resumeBtnBG.width = resumeBtn.width + 80; resumeBtnBG.height = resumeBtn.height + 60; resumeBtnBG.x = 0; resumeBtnBG.y = 0; resumeBtnContainer.addChild(resumeBtnBG); resumeBtnContainer.addChild(resumeBtn); resumeBtnContainer.y = -100; pauseMenuContainer.addChild(resumeBtnContainer); // Menu button (in pause menu) with ButtonBG var pauseMenuBtnContainer = new Container(); var pauseMenuBtn = new Text2('Menu', { size: 120, fill: "#fff", font: "Impact" }); pauseMenuBtn.anchor.set(0.5, 0.5); pauseMenuBtn.y = 0; var pauseMenuBtnBG = LK.getAsset('ButtonBG', { anchorX: 0.5, anchorY: 0.5 }); pauseMenuBtnBG.width = pauseMenuBtn.width + 80; pauseMenuBtnBG.height = pauseMenuBtn.height + 60; pauseMenuBtnBG.x = 0; pauseMenuBtnBG.y = 0; pauseMenuBtnContainer.addChild(pauseMenuBtnBG); pauseMenuBtnContainer.addChild(pauseMenuBtn); pauseMenuBtnContainer.y = 100; pauseMenuContainer.addChild(pauseMenuBtnContainer); // Pause button handler pauseBtn.down = function (x, y, obj) { if (soundOn) LK.getSound('Click').play(); pauseBtnContainer.visible = false; pauseMenuContainer.visible = true; lockInput = true; game.lockInput = true; // Hide cards and hearts visually, but keep state for (var i = 0; i < cards.length; i++) { cards[i].visible = false; } for (var i = 0; i < heartNodes.length; i++) { heartNodes[i].visible = false; } levelText.visible = false; messageText.visible = false; }; // Resume button handler resumeBtnContainer.down = function (x, y, obj) { if (soundOn) LK.getSound('Click').play(); pauseBtnContainer.visible = true; pauseMenuContainer.visible = false; lockInput = false; game.lockInput = false; for (var i = 0; i < cards.length; i++) { cards[i].visible = true; } for (var i = 0; i < heartNodes.length; i++) { heartNodes[i].visible = true; } levelText.visible = true; messageText.visible = false; }; // Menu button in pause menu handler pauseMenuBtnContainer.down = function (x, y, obj) { if (soundOn) LK.getSound('Click').play(); pauseMenuContainer.visible = false; showMenu(); pauseBtnContainer.visible = false; }; // Show menu at start showMenu(); playNextMusic(); // Layout parameters var boardMargin = 80; var cardSpacingX = 40; var cardSpacingY = 40; // Helper: shuffle array function shuffle(array) { for (var i = array.length - 1; i > 0; i--) { var j = Math.floor(Math.random() * (i + 1)); var t = array[i]; array[i] = array[j]; array[j] = t; } return array; } // Helper: get grid size for level function getGridForLevel(lvl) { // Level 1: 2x2 (4 cards, 2 pairs) // Level 2: 2x3 (6 cards, 3 pairs) // Level 3: 2x4 (8 cards, 4 pairs) // Level 4: 3x4 (12 cards, 6 pairs) // Level 5: 4x4 (16 cards, 8 pairs) // Level 6: 4x5 (20 cards, 10 pairs) // Level 7: 5x6 (30 cards, 15 pairs) // Level 8: 6x6 (36 cards, 18 pairs) // Level 9: 6x7 (42 cards, 21 pairs) // Level 10: 7x8 (56 cards, 28 pairs) var grids = [[2, 2], [2, 3], [2, 4], [3, 4], [4, 4], [4, 5], [5, 6], [6, 6], [6, 7], [7, 8]]; return grids[Math.min(lvl - 1, grids.length - 1)]; } // Helper: get lives for level function getLivesForLevel(lvl) { // Start with 3, increase by 1 every 2 levels, max 10 return Math.min(10, 3 + Math.floor((lvl - 1) / 2)); } // Start a new level function startLevel(lvl) { // Clean up for (var i = 0; i < cards.length; i++) { cards[i].destroy(); } cards = []; revealedCards = []; matchedPairs = 0; lockInput = true; game.lockInput = true; // Set level/lives level = lvl; var grid = getGridForLevel(level); var rows = grid[0], cols = grid[1]; totalPairs = Math.floor(rows * cols / 2); maxLives = getLivesForLevel(level); lives = maxLives; // Update GUI levelText.setText('Level ' + level); // Remove old hearts for (var i = 0; i < heartNodes.length; i++) { if (heartNodes[i].parent) heartNodes[i].parent.removeChild(heartNodes[i]); heartNodes[i].destroy && heartNodes[i].destroy(); } heartNodes = []; // Draw new hearts for current lives for (var i = 0; i < lives; i++) { var heart = LK.getAsset(heartAssetName, { anchorX: 0, anchorY: 1 }); // Place hearts at the bottom left, leaving a margin from the left and bottom heart.x = heartMargin + i * (heartSize + heartSpacing); heart.y = 2732 - heartMargin; // Add directly to the game (not GUI overlay) so it stays at the bottom left of the play area game.addChild(heart); heartNodes.push(heart); } // Prepare fruit pairs var fruitPool = []; for (var i = 0; i < totalPairs; i++) { var fruit = fruitIds[i % fruitIds.length]; fruitPool.push(fruit, fruit); } shuffle(fruitPool); // Card size var cardW = LK.getAsset('cardBack', {}).width; var cardH = LK.getAsset('cardBack', {}).height; // Board size var boardW = cols * cardW + (cols - 1) * cardSpacingX; var boardH = rows * cardH + (rows - 1) * cardSpacingY; var startX = (2048 - boardW) / 2 + cardW / 2; var startY = (2732 - boardH) / 2 + cardH / 2 + 60; // Position levelText and livesText below the cards levelText.x = 2048 / 2; levelText.y = startY + boardH / 2 + cardH / 2 + 40; // Create cards var idx = 0; for (var r = 0; r < rows; r++) { for (var c = 0; c < cols; c++) { if (idx >= fruitPool.length) continue; var card = new Card(); card.setFruit(fruitPool[idx]); card.x = startX + c * (cardW + cardSpacingX); card.y = startY + r * (cardH + cardSpacingY); card.scaleX = 1; card.scaleY = 1; game.addChild(card); cards.push(card); idx++; } } // Show all cards for a few seconds, increasing with level var showDuration = 1200 + (level - 1) * 500; // e.g. 1.2s + 0.5s per level for (var i = 0; i < cards.length; i++) { cards[i].front.visible = true; cards[i].back.visible = false; cards[i].state = 'revealed'; cards[i].scaleX = 1; cards[i].scaleY = 1; } // Move messageText below the cards, centered messageText.x = 2048 / 2; messageText.y = levelText.y + levelText.height + 40; messageText.setText('Level ' + level); messageText.visible = true; lockInput = true; game.lockInput = true; // After showDuration, hide all cards and allow input LK.setTimeout(function () { for (var i = 0; i < cards.length; i++) { cards[i].front.visible = false; cards[i].back.visible = true; cards[i].state = 'hidden'; cards[i].scaleX = 1; cards[i].scaleY = 1; } messageText.visible = false; lockInput = false; game.lockInput = false; }, showDuration); } // Card tap handler game.onCardTapped = function (card) { if (lockInput || card.state !== 'hidden') return; card.reveal(); revealedCards.push(card); if (revealedCards.length === 2) { lockInput = true; game.lockInput = true; var c1 = revealedCards[0], c2 = revealedCards[1]; if (c1.fruitId === c2.fruitId) { // Match! LK.setTimeout(function () { c1.match(); c2.match(); matchedPairs++; revealedCards = []; lockInput = false; game.lockInput = false; // Check win if (matchedPairs === totalPairs) { if (level === maxLevel) { // Game completed! messageText.setText('You Win!'); messageText.visible = true; LK.setTimeout(function () { messageText.visible = false; LK.showYouWin(); }, 1200); } else { // Next level messageText.setText('Level Complete!'); messageText.visible = true; LK.setTimeout(function () { messageText.visible = false; startLevel(level + 1); }, 1200); } } }, 350); } else { // Not a match LK.setTimeout(function () { c1.hide(); c2.hide(); revealedCards = []; lives--; // Update hearts: hide one for each lost life for (var i = 0; i < heartNodes.length; i++) { heartNodes[i].visible = i < lives; } lockInput = false; game.lockInput = false; if (lives <= 0) { // Game over messageText.setText('Game Over'); messageText.visible = true; LK.setTimeout(function () { messageText.visible = false; LK.showGameOver(); }, 1200); } }, 650); } } }; // Prevent interaction when locked game.down = function (x, y, obj) { // No-op: all input handled by Card.down }; game.move = function (x, y, obj) {}; game.up = function (x, y, obj) {}; // Start first level only after Play is clicked // (Do not start here; handled by playBtn.down) // No need for game.update, all logic is event-driven; // --- MUSIC LOOP LOGIC --- var musicTracks = ['BGMusic1', 'BGMusic2']; var currentMusicIndex = 0; var musicLoopTimeout = null; // Play the next music track in the sequence, looping back to start function playNextMusic() { if (!musicOn) return; // Defensive: check musicTracks is array and currentMusicIndex is valid if (!musicTracks || !Array.isArray(musicTracks) || musicTracks.length === 0) return; if (typeof currentMusicIndex !== "number" || currentMusicIndex < 0 || currentMusicIndex >= musicTracks.length) { currentMusicIndex = 0; } var track = musicTracks[currentMusicIndex]; if (!track) return; // Stop any currently playing music LK.stopMusic && LK.stopMusic(); // Play the current track, not looping LK.playMusic(track, { loop: false }); // Get duration in ms (LK.init.music uses start/end as 0-1, so we can't get duration directly, so hardcode fallback) // If you have access to duration, use it. Otherwise, use a safe default (e.g. 60000ms) var durations = { 'BGMusic1': 60000, 'BGMusic2': 60000 }; var duration = durations[track] || 60000; // Schedule next track musicLoopTimeout = LK.setTimeout(function () { currentMusicIndex = (currentMusicIndex + 1) % musicTracks.length; playNextMusic(); }, duration - 100); // Start next track just before current ends } // Stop the music loop and any scheduled next track function stopMusicLoop() { if (musicLoopTimeout) { LK.clearTimeout(musicLoopTimeout); musicLoopTimeout = null; } LK.stopMusic && LK.stopMusic(); } // When music is toggled, start/stop music loop accordingly musicBtnContainer.down = function (x, y, obj) { if (soundOn) LK.getSound('Click').play(); musicOn = !musicOn; musicBtn.setText('Music: ' + (musicOn ? 'On' : 'Off')); LK.setMusicEnabled && LK.setMusicEnabled(musicOn); if (musicOn) { playNextMusic(); } else { stopMusicLoop(); } }; // When menu is shown, stop music loop
/****
* 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 states: 'hidden', 'revealed', 'matched'
self.state = 'hidden';
self.fruitId = null; // Will be set on creation
// Card back (hidden state)
var back = self.attachAsset('cardBack', {
anchorX: 0.5,
anchorY: 0.5
});
self.back = back;
// Card front (fruit image, revealed state)
// Default to watermelon image, will be replaced by setFruit
var front = self.attachAsset('Watermelon', {
anchorX: 0.5,
anchorY: 0.5
});
self.front = front;
self.front.visible = false;
// Set fruit image for this card
self.setFruit = function (fruitId) {
self.fruitId = fruitId;
// Map fruitId to asset name in assets section
var assetMap = {
'banana': 'Banana',
'cherry': 'Cherry',
'orange': 'Orange',
'grape': 'grape',
'lemon': 'lemon',
'peach': 'Peach',
'strawberry': 'strawberry',
'watermelon': 'Watermelon',
'Coconut': 'Coconut',
'Kiwi': 'Kiwi',
'apple': 'apple',
'Pear': 'Pear',
'Pineapple': 'Pineapple',
'Avocado': 'Avocado',
'Daisy': 'Daisy',
'Rock': 'Rock',
'Water': 'Water'
};
var assetName = assetMap[fruitId] || 'fruit';
var fruitAsset = LK.getAsset(assetName, {
anchorX: 0.5,
anchorY: 0.5
});
// Remove old front if present
if (self.front && self.front.parent) self.removeChild(self.front);
self.front = fruitAsset;
self.addChild(self.front);
self.front.visible = false;
self.front.x = 0;
self.front.y = 0;
self.back.x = 0;
self.back.y = 0;
};
// Reveal the card (show fruit)
self.reveal = function () {
if (self.state !== 'hidden') return;
self.state = 'revealed';
self.back.visible = false;
self.front.visible = true;
// Optional: flip animation
tween(self, {
scaleX: 0
}, {
duration: 80,
easing: tween.cubicIn,
onFinish: function onFinish() {
self.back.visible = false;
self.front.visible = true;
tween(self, {
scaleX: 1
}, {
duration: 80,
easing: tween.cubicOut
});
}
});
};
// Hide the card (show back)
self.hide = function () {
if (self.state !== 'revealed') return;
self.state = 'hidden';
// Optional: flip animation
tween(self, {
scaleX: 0
}, {
duration: 80,
easing: tween.cubicIn,
onFinish: function onFinish() {
self.front.visible = false;
self.back.visible = true;
tween(self, {
scaleX: 1
}, {
duration: 80,
easing: tween.cubicOut
});
}
});
};
// Mark as matched (permanently revealed)
self.match = function () {
self.state = 'matched';
self.back.visible = false;
self.front.visible = true;
// Optional: small scale bounce
tween(self, {
scaleX: 1.15,
scaleY: 1.15
}, {
duration: 100,
easing: tween.bounceOut,
onFinish: function onFinish() {
tween(self, {
scaleX: 1,
scaleY: 1
}, {
duration: 100
});
}
});
};
// Handle tap
self.down = function (x, y, obj) {
if (self.state === 'hidden' && !game.lockInput) {
game.onCardTapped(self);
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000000
});
/****
* Game Code
****/
// Fruit pool (all fruits with images in assets)
var fruitIds = ['banana', 'cherry', 'orange', 'grape', 'lemon', 'peach', 'strawberry', 'watermelon', 'Coconut', 'Kiwi', 'apple', 'Pear', 'Pineapple', 'Avocado', 'Daisy', 'Rock', 'Water'];
// No need to dynamically create fruit image assets, all are defined in assets section
// Game variables
var level = 1;
var maxLevel = 10;
var cards = [];
var revealedCards = [];
var matchedPairs = 0;
var totalPairs = 0;
var lives = 0;
var maxLives = 0;
var lockInput = false;
var levelText, messageText;
// Heart/lives display
var heartNodes = [];
var heartAssetName = 'heart';
// Use a heart image asset (add to assets if not present)
var heartSize = 80;
var heartSpacing = 20;
var heartMargin = 30;
// --- MENU SYSTEM ---
// Menu container
var menuContainer = new Container();
LK.gui.center.addChild(menuContainer);
// Play button with ButtonBG
var playBtnContainer = new Container();
var playBtn = new Text2('Play', {
size: 140,
fill: "#fff",
font: "Impact"
});
playBtn.anchor.set(0.5, 0.5);
playBtn.y = 0;
var playBtnBG = LK.getAsset('ButtonBG', {
anchorX: 0.5,
anchorY: 0.5
});
playBtnBG.width = playBtn.width + 80;
playBtnBG.height = playBtn.height + 60;
playBtnBG.x = 0;
playBtnBG.y = 0;
playBtnContainer.addChild(playBtnBG);
playBtnContainer.addChild(playBtn);
playBtnContainer.y = -120;
menuContainer.addChild(playBtnContainer);
// Settings button with ButtonBG
var settingsBtnContainer = new Container();
var settingsBtn = new Text2('Settings', {
size: 100,
fill: "#fff",
font: "Impact"
});
settingsBtn.anchor.set(0.5, 0.5);
settingsBtn.y = 0;
var settingsBtnBG = LK.getAsset('ButtonBG', {
anchorX: 0.5,
anchorY: 0.5
});
settingsBtnBG.width = settingsBtn.width + 80;
settingsBtnBG.height = settingsBtn.height + 60;
settingsBtnBG.x = 0;
settingsBtnBG.y = 0;
settingsBtnContainer.addChild(settingsBtnBG);
settingsBtnContainer.addChild(settingsBtn);
settingsBtnContainer.y = 80;
menuContainer.addChild(settingsBtnContainer);
// Settings panel (hidden by default)
var settingsPanel = new Container();
settingsPanel.visible = false;
settingsPanel.y = 250;
menuContainer.addChild(settingsPanel);
// Sound toggle button with ButtonBG
var soundOn = true;
var soundBtnContainer = new Container();
var soundBtn = new Text2('Sound: On', {
size: 80,
fill: "#fff",
font: "Impact"
});
soundBtn.anchor.set(0.5, 0.5);
soundBtn.y = 0;
var soundBtnBG = LK.getAsset('ButtonBG', {
anchorX: 0.5,
anchorY: 0.5
});
soundBtnBG.width = soundBtn.width + 80;
soundBtnBG.height = soundBtn.height + 60;
soundBtnBG.x = 0;
soundBtnBG.y = 0;
soundBtnContainer.addChild(soundBtnBG);
soundBtnContainer.addChild(soundBtn);
soundBtnContainer.y = 0;
settingsPanel.addChild(soundBtnContainer);
// Music toggle button with ButtonBG
var musicOn = true;
var musicBtnContainer = new Container();
var musicBtn = new Text2('Music: On', {
size: 80,
fill: "#fff",
font: "Impact"
});
musicBtn.anchor.set(0.5, 0.5);
musicBtn.y = 0;
var musicBtnBG = LK.getAsset('ButtonBG', {
anchorX: 0.5,
anchorY: 0.5
});
musicBtnBG.width = musicBtn.width + 80;
musicBtnBG.height = musicBtn.height + 60;
musicBtnBG.x = 0;
musicBtnBG.y = 0;
musicBtnContainer.addChild(musicBtnBG);
musicBtnContainer.addChild(musicBtn);
musicBtnContainer.y = 120;
settingsPanel.addChild(musicBtnContainer);
// Menu logic
var menuActive = true;
function showMenu() {
menuContainer.visible = true;
levelText.visible = false;
messageText.visible = false;
pauseBtnContainer.visible = false;
pauseMenuContainer.visible = false;
// Hide all cards and hearts if present
for (var i = 0; i < cards.length; i++) {
cards[i].visible = false;
}
for (var i = 0; i < heartNodes.length; i++) {
heartNodes[i].visible = false;
}
menuActive = true;
lockInput = true;
game.lockInput = true;
}
function hideMenu() {
menuContainer.visible = false;
levelText.visible = true;
pauseBtnContainer.visible = true;
pauseMenuContainer.visible = false;
for (var i = 0; i < cards.length; i++) {
cards[i].visible = true;
}
for (var i = 0; i < heartNodes.length; i++) {
heartNodes[i].visible = true;
}
menuActive = false;
lockInput = false;
game.lockInput = false;
}
// Play button handler
playBtnContainer.down = function (x, y, obj) {
if (soundOn) LK.getSound('Click').play();
hideMenu();
// Start/restart game
startLevel(1);
};
// Settings button handler
settingsBtnContainer.down = function (x, y, obj) {
if (soundOn) LK.getSound('Click').play();
settingsPanel.visible = !settingsPanel.visible;
};
// Sound toggle handler
soundBtnContainer.down = function (x, y, obj) {
if (soundOn) LK.getSound('Click').play();
soundOn = !soundOn;
soundBtn.setText('Sound: ' + (soundOn ? 'On' : 'Off'));
LK.setSoundEnabled && LK.setSoundEnabled(soundOn);
};
// Music toggle handler
musicBtnContainer.down = function (x, y, obj) {
if (soundOn) LK.getSound('Click').play();
musicOn = !musicOn;
musicBtn.setText('Music: ' + (musicOn ? 'On' : 'Off'));
LK.setMusicEnabled && LK.setMusicEnabled(musicOn);
};
// --- END MENU SYSTEM ---
// GUI setup
levelText = new Text2('Level 1', {
size: 90,
fill: "#fff"
});
levelText.anchor.set(0.5, 0);
// We'll position these below the cards after the board is created, so just add to game for now
game.addChild(levelText);
// Message text (centered, for "Level X", "You Win", etc)
messageText = new Text2('', {
size: 140,
fill: "#fff"
});
messageText.anchor.set(0.5, 0.5);
LK.gui.center.addChild(messageText);
messageText.visible = false;
// --- PAUSE BUTTON & PAUSE MENU ---
// Create a container for the pause button and its background
var pauseBtnContainer = new Container();
// Add ButtonBG image as background
var pauseBtnBG = LK.getAsset('ButtonBG', {
anchorX: 0.5,
anchorY: 0.5
});
pauseBtnContainer.addChild(pauseBtnBG);
// Create the pause button text
var pauseBtn = new Text2('Pause', {
size: 80,
fill: "#fff",
font: "Impact"
});
pauseBtn.anchor.set(0.5, 0.5);
pauseBtnContainer.addChild(pauseBtn);
// Place pause button just below the Level text, even further to the right of center
pauseBtnContainer.x = 2048 / 2 + 260; // Move 260px to the right
pauseBtnContainer.y = levelText.y + levelText.height + 10; // 10px below the Level text
LK.gui.addChild(pauseBtnContainer);
pauseBtnContainer.visible = false;
// Pause menu container
var pauseMenuContainer = new Container();
pauseMenuContainer.visible = false;
LK.gui.center.addChild(pauseMenuContainer);
// Resume button with ButtonBG
var resumeBtnContainer = new Container();
var resumeBtn = new Text2('Resume', {
size: 120,
fill: "#fff",
font: "Impact"
});
resumeBtn.anchor.set(0.5, 0.5);
resumeBtn.y = 0;
var resumeBtnBG = LK.getAsset('ButtonBG', {
anchorX: 0.5,
anchorY: 0.5
});
resumeBtnBG.width = resumeBtn.width + 80;
resumeBtnBG.height = resumeBtn.height + 60;
resumeBtnBG.x = 0;
resumeBtnBG.y = 0;
resumeBtnContainer.addChild(resumeBtnBG);
resumeBtnContainer.addChild(resumeBtn);
resumeBtnContainer.y = -100;
pauseMenuContainer.addChild(resumeBtnContainer);
// Menu button (in pause menu) with ButtonBG
var pauseMenuBtnContainer = new Container();
var pauseMenuBtn = new Text2('Menu', {
size: 120,
fill: "#fff",
font: "Impact"
});
pauseMenuBtn.anchor.set(0.5, 0.5);
pauseMenuBtn.y = 0;
var pauseMenuBtnBG = LK.getAsset('ButtonBG', {
anchorX: 0.5,
anchorY: 0.5
});
pauseMenuBtnBG.width = pauseMenuBtn.width + 80;
pauseMenuBtnBG.height = pauseMenuBtn.height + 60;
pauseMenuBtnBG.x = 0;
pauseMenuBtnBG.y = 0;
pauseMenuBtnContainer.addChild(pauseMenuBtnBG);
pauseMenuBtnContainer.addChild(pauseMenuBtn);
pauseMenuBtnContainer.y = 100;
pauseMenuContainer.addChild(pauseMenuBtnContainer);
// Pause button handler
pauseBtn.down = function (x, y, obj) {
if (soundOn) LK.getSound('Click').play();
pauseBtnContainer.visible = false;
pauseMenuContainer.visible = true;
lockInput = true;
game.lockInput = true;
// Hide cards and hearts visually, but keep state
for (var i = 0; i < cards.length; i++) {
cards[i].visible = false;
}
for (var i = 0; i < heartNodes.length; i++) {
heartNodes[i].visible = false;
}
levelText.visible = false;
messageText.visible = false;
};
// Resume button handler
resumeBtnContainer.down = function (x, y, obj) {
if (soundOn) LK.getSound('Click').play();
pauseBtnContainer.visible = true;
pauseMenuContainer.visible = false;
lockInput = false;
game.lockInput = false;
for (var i = 0; i < cards.length; i++) {
cards[i].visible = true;
}
for (var i = 0; i < heartNodes.length; i++) {
heartNodes[i].visible = true;
}
levelText.visible = true;
messageText.visible = false;
};
// Menu button in pause menu handler
pauseMenuBtnContainer.down = function (x, y, obj) {
if (soundOn) LK.getSound('Click').play();
pauseMenuContainer.visible = false;
showMenu();
pauseBtnContainer.visible = false;
};
// Show menu at start
showMenu();
playNextMusic();
// Layout parameters
var boardMargin = 80;
var cardSpacingX = 40;
var cardSpacingY = 40;
// Helper: shuffle array
function shuffle(array) {
for (var i = array.length - 1; i > 0; i--) {
var j = Math.floor(Math.random() * (i + 1));
var t = array[i];
array[i] = array[j];
array[j] = t;
}
return array;
}
// Helper: get grid size for level
function getGridForLevel(lvl) {
// Level 1: 2x2 (4 cards, 2 pairs)
// Level 2: 2x3 (6 cards, 3 pairs)
// Level 3: 2x4 (8 cards, 4 pairs)
// Level 4: 3x4 (12 cards, 6 pairs)
// Level 5: 4x4 (16 cards, 8 pairs)
// Level 6: 4x5 (20 cards, 10 pairs)
// Level 7: 5x6 (30 cards, 15 pairs)
// Level 8: 6x6 (36 cards, 18 pairs)
// Level 9: 6x7 (42 cards, 21 pairs)
// Level 10: 7x8 (56 cards, 28 pairs)
var grids = [[2, 2], [2, 3], [2, 4], [3, 4], [4, 4], [4, 5], [5, 6], [6, 6], [6, 7], [7, 8]];
return grids[Math.min(lvl - 1, grids.length - 1)];
}
// Helper: get lives for level
function getLivesForLevel(lvl) {
// Start with 3, increase by 1 every 2 levels, max 10
return Math.min(10, 3 + Math.floor((lvl - 1) / 2));
}
// Start a new level
function startLevel(lvl) {
// Clean up
for (var i = 0; i < cards.length; i++) {
cards[i].destroy();
}
cards = [];
revealedCards = [];
matchedPairs = 0;
lockInput = true;
game.lockInput = true;
// Set level/lives
level = lvl;
var grid = getGridForLevel(level);
var rows = grid[0],
cols = grid[1];
totalPairs = Math.floor(rows * cols / 2);
maxLives = getLivesForLevel(level);
lives = maxLives;
// Update GUI
levelText.setText('Level ' + level);
// Remove old hearts
for (var i = 0; i < heartNodes.length; i++) {
if (heartNodes[i].parent) heartNodes[i].parent.removeChild(heartNodes[i]);
heartNodes[i].destroy && heartNodes[i].destroy();
}
heartNodes = [];
// Draw new hearts for current lives
for (var i = 0; i < lives; i++) {
var heart = LK.getAsset(heartAssetName, {
anchorX: 0,
anchorY: 1
});
// Place hearts at the bottom left, leaving a margin from the left and bottom
heart.x = heartMargin + i * (heartSize + heartSpacing);
heart.y = 2732 - heartMargin;
// Add directly to the game (not GUI overlay) so it stays at the bottom left of the play area
game.addChild(heart);
heartNodes.push(heart);
}
// Prepare fruit pairs
var fruitPool = [];
for (var i = 0; i < totalPairs; i++) {
var fruit = fruitIds[i % fruitIds.length];
fruitPool.push(fruit, fruit);
}
shuffle(fruitPool);
// Card size
var cardW = LK.getAsset('cardBack', {}).width;
var cardH = LK.getAsset('cardBack', {}).height;
// Board size
var boardW = cols * cardW + (cols - 1) * cardSpacingX;
var boardH = rows * cardH + (rows - 1) * cardSpacingY;
var startX = (2048 - boardW) / 2 + cardW / 2;
var startY = (2732 - boardH) / 2 + cardH / 2 + 60;
// Position levelText and livesText below the cards
levelText.x = 2048 / 2;
levelText.y = startY + boardH / 2 + cardH / 2 + 40;
// Create cards
var idx = 0;
for (var r = 0; r < rows; r++) {
for (var c = 0; c < cols; c++) {
if (idx >= fruitPool.length) continue;
var card = new Card();
card.setFruit(fruitPool[idx]);
card.x = startX + c * (cardW + cardSpacingX);
card.y = startY + r * (cardH + cardSpacingY);
card.scaleX = 1;
card.scaleY = 1;
game.addChild(card);
cards.push(card);
idx++;
}
}
// Show all cards for a few seconds, increasing with level
var showDuration = 1200 + (level - 1) * 500; // e.g. 1.2s + 0.5s per level
for (var i = 0; i < cards.length; i++) {
cards[i].front.visible = true;
cards[i].back.visible = false;
cards[i].state = 'revealed';
cards[i].scaleX = 1;
cards[i].scaleY = 1;
}
// Move messageText below the cards, centered
messageText.x = 2048 / 2;
messageText.y = levelText.y + levelText.height + 40;
messageText.setText('Level ' + level);
messageText.visible = true;
lockInput = true;
game.lockInput = true;
// After showDuration, hide all cards and allow input
LK.setTimeout(function () {
for (var i = 0; i < cards.length; i++) {
cards[i].front.visible = false;
cards[i].back.visible = true;
cards[i].state = 'hidden';
cards[i].scaleX = 1;
cards[i].scaleY = 1;
}
messageText.visible = false;
lockInput = false;
game.lockInput = false;
}, showDuration);
}
// Card tap handler
game.onCardTapped = function (card) {
if (lockInput || card.state !== 'hidden') return;
card.reveal();
revealedCards.push(card);
if (revealedCards.length === 2) {
lockInput = true;
game.lockInput = true;
var c1 = revealedCards[0],
c2 = revealedCards[1];
if (c1.fruitId === c2.fruitId) {
// Match!
LK.setTimeout(function () {
c1.match();
c2.match();
matchedPairs++;
revealedCards = [];
lockInput = false;
game.lockInput = false;
// Check win
if (matchedPairs === totalPairs) {
if (level === maxLevel) {
// Game completed!
messageText.setText('You Win!');
messageText.visible = true;
LK.setTimeout(function () {
messageText.visible = false;
LK.showYouWin();
}, 1200);
} else {
// Next level
messageText.setText('Level Complete!');
messageText.visible = true;
LK.setTimeout(function () {
messageText.visible = false;
startLevel(level + 1);
}, 1200);
}
}
}, 350);
} else {
// Not a match
LK.setTimeout(function () {
c1.hide();
c2.hide();
revealedCards = [];
lives--;
// Update hearts: hide one for each lost life
for (var i = 0; i < heartNodes.length; i++) {
heartNodes[i].visible = i < lives;
}
lockInput = false;
game.lockInput = false;
if (lives <= 0) {
// Game over
messageText.setText('Game Over');
messageText.visible = true;
LK.setTimeout(function () {
messageText.visible = false;
LK.showGameOver();
}, 1200);
}
}, 650);
}
}
};
// Prevent interaction when locked
game.down = function (x, y, obj) {
// No-op: all input handled by Card.down
};
game.move = function (x, y, obj) {};
game.up = function (x, y, obj) {};
// Start first level only after Play is clicked
// (Do not start here; handled by playBtn.down)
// No need for game.update, all logic is event-driven;
// --- MUSIC LOOP LOGIC ---
var musicTracks = ['BGMusic1', 'BGMusic2'];
var currentMusicIndex = 0;
var musicLoopTimeout = null;
// Play the next music track in the sequence, looping back to start
function playNextMusic() {
if (!musicOn) return;
// Defensive: check musicTracks is array and currentMusicIndex is valid
if (!musicTracks || !Array.isArray(musicTracks) || musicTracks.length === 0) return;
if (typeof currentMusicIndex !== "number" || currentMusicIndex < 0 || currentMusicIndex >= musicTracks.length) {
currentMusicIndex = 0;
}
var track = musicTracks[currentMusicIndex];
if (!track) return;
// Stop any currently playing music
LK.stopMusic && LK.stopMusic();
// Play the current track, not looping
LK.playMusic(track, {
loop: false
});
// Get duration in ms (LK.init.music uses start/end as 0-1, so we can't get duration directly, so hardcode fallback)
// If you have access to duration, use it. Otherwise, use a safe default (e.g. 60000ms)
var durations = {
'BGMusic1': 60000,
'BGMusic2': 60000
};
var duration = durations[track] || 60000;
// Schedule next track
musicLoopTimeout = LK.setTimeout(function () {
currentMusicIndex = (currentMusicIndex + 1) % musicTracks.length;
playNextMusic();
}, duration - 100); // Start next track just before current ends
}
// Stop the music loop and any scheduled next track
function stopMusicLoop() {
if (musicLoopTimeout) {
LK.clearTimeout(musicLoopTimeout);
musicLoopTimeout = null;
}
LK.stopMusic && LK.stopMusic();
}
// When music is toggled, start/stop music loop accordingly
musicBtnContainer.down = function (x, y, obj) {
if (soundOn) LK.getSound('Click').play();
musicOn = !musicOn;
musicBtn.setText('Music: ' + (musicOn ? 'On' : 'Off'));
LK.setMusicEnabled && LK.setMusicEnabled(musicOn);
if (musicOn) {
playNextMusic();
} else {
stopMusicLoop();
}
};
// When menu is shown, stop music loop
watermelon. In-Game asset. 2d. High contrast. No shadows
strawberry. In-Game asset. 2d. High contrast. No shadows
banana. In-Game asset. 2d. High contrast. No shadows
lemon. In-Game asset. 2d. High contrast. No shadows
Orange. In-Game asset. 2d. High contrast. No shadows
grape. In-Game asset. 2d. High contrast. No shadows
peach. In-Game asset. 2d. High contrast. No shadows
Cherry. In-Game asset. 2d. High contrast. No shadows
Heart. In-Game asset. 2d. High contrast. No shadows
avocado. In-Game asset. 2d. High contrast. No shadows
Coconut. In-Game asset. 2d. High contrast. No shadows
Pineapple. In-Game asset. 2d. High contrast. No shadows
Kiwi. In-Game asset. 2d. High contrast. No shadows
Apple. In-Game asset. 2d. High contrast. No shadows
Pear. In-Game asset. 2d. High contrast. No shadows
Daisy. In-Game asset. 2d. High contrast. No shadows
Bottle a water. In-Game asset. 2d. High contrast. No shadows
Rock (Gray). In-Game asset. 2d. High contrast. No shadows
Let it be a pink card, the corners are dark pink, getting lighter towards the middle. In-Game asset. 2d. High contrast. No shadows