/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1", { highScore: 0, currentLevel: 1 }); /**** * Classes ****/ var Button = Container.expand(function (text) { var self = Container.call(this); self.background = self.attachAsset('button', { anchorX: 0.5, anchorY: 0.5 }); self.label = new Text2(text || "Button", { size: 50, fill: 0xFFFFFF }); self.label.anchor.set(0.5, 0.5); self.addChild(self.label); // Set interactive self.interactive = true; return self; }); // Define prototype methods for Button var Card = Container.expand(function (cardNumber) { var self = Container.call(this); self.cardNumber = cardNumber || 0; self.revealed = false; self.matched = false; self.canFlip = false; // Flag to control when cards can be flipped // Card back (shown initially) self.back = self.attachAsset('cardBack', { anchorX: 0.5, anchorY: 0.5 }); // Card front (shown when flipped) self.front = self.attachAsset('cardFront', { anchorX: 0.5, anchorY: 0.5, visible: false }); // Card number text self.numberText = new Text2(self.cardNumber.toString(), { size: 100, fill: 0x000000 }); self.numberText.anchor.set(0.5, 0.5); self.numberText.visible = false; self.addChild(self.numberText); // Make card interactive self.interactive = true; // Set size based on the card self.width = self.back.width; self.height = self.back.height; // Flag to indicate if animation is in progress self.animating = false; // Flip card to show front with animation self.showFront = function () { if (!self.revealed && !self.animating) { self.animating = true; // First half of flip: scale horizontally to 0 tween(self, { scaleX: 0 }, { duration: 150, onFinish: function onFinish() { // Switch cards at the middle of animation self.back.visible = false; self.front.visible = true; self.numberText.visible = true; // Second half of flip: scale back from 0 to 1 tween(self, { scaleX: 1 }, { duration: 150, onFinish: function onFinish() { self.revealed = true; self.animating = false; LK.getSound('flip').play(); } }); } }); } }; // Flip card to show back with animation self.showBack = function () { if (self.revealed && !self.animating) { self.animating = true; // First half of flip: scale horizontally to 0 tween(self, { scaleX: 0 }, { duration: 150, onFinish: function onFinish() { // Switch cards at the middle of animation self.back.visible = true; self.front.visible = false; self.numberText.visible = false; // Second half of flip: scale back from 0 to 1 tween(self, { scaleX: 1 }, { duration: 150, onFinish: function onFinish() { self.revealed = false; self.animating = false; } }); } }); } }; self.down = function (x, y, obj) { // Handle card click if (gameState.gamePhase === "memorize" && self.canFlip && !self.animating) { // Create star burst effect at click location var localPos = { x: x, y: y }; // Fix: use the clicked object's position directly since parent.toGlobal is causing an error var gamePos = { x: self.x, y: self.y }; // Create and track the particles var particles = createStarBurst(gamePos.x, gamePos.y); activeParticles.push(particles); if (!self.revealed) { self.showFront(); // Start the countdown timer when the first card is flipped if (!timerStarted) { timerStarted = true; countdownTimer.visible = true; countdownTimer.reset(); countdownTimer.start(); } } else { self.showBack(); } } }; return self; }); var CountdownTimer = Container.expand(function (duration) { var self = Container.call(this); // Create circle background self.circle = LK.getAsset('centerCircle', { anchorX: 0.5, anchorY: 0.5, tint: 0x4CAF50 }); self.addChild(self.circle); // Create text for countdown self.timeText = new Text2("15", { size: 80, fill: 0xFFFFFF }); self.timeText.anchor.set(0.5, 0.5); self.addChild(self.timeText); // Timer properties self.duration = duration || 15; // Default 15 seconds self.timeRemaining = self.duration; self.active = false; self.timerInterval = null; // Start the countdown self.start = function () { if (self.active) return; self.active = true; self.timeRemaining = self.duration; self.updateDisplay(); self.timerInterval = LK.setInterval(function () { self.timeRemaining--; self.updateDisplay(); // Update circle scale based on time remaining var progress = self.timeRemaining / self.duration; self.circle.scale.set(progress); // Change color as time runs out if (self.timeRemaining <= 5) { self.circle.tint = 0xFF5252; // Red when time is running out } if (self.timeRemaining <= 0) { self.stop(); if (typeof self.onComplete === 'function') { self.onComplete(); } } }, 1000); }; // Stop the countdown self.stop = function () { if (!self.active) return; self.active = false; if (self.timerInterval) { LK.clearInterval(self.timerInterval); self.timerInterval = null; } }; // Reset the countdown self.reset = function () { self.stop(); self.timeRemaining = self.duration; self.updateDisplay(); self.circle.tint = 0x4CAF50; // Reset to green self.circle.scale.set(1); }; // Update the display self.updateDisplay = function () { self.timeText.setText(Math.max(0, self.timeRemaining).toString()); }; return self; }); var QuestionDisplay = Container.expand(function () { var self = Container.call(this); self.questionText = new Text2("", { size: 60, fill: 0xFFFFFF }); // Set anchor to center for proper horizontal centering self.questionText.anchor.set(0.5, 0.5); self.addChild(self.questionText); self.setQuestion = function (question) { // Set the text content self.questionText.setText(question); // Reset position to ensure it's centered self.questionText.x = 0; self.questionText.y = 0; }; return self; }); var StarParticle = Container.expand(function () { var self = Container.call(this); // Create a star shape var star = LK.getAsset('star', { anchorX: 0.5, anchorY: 0.5, tint: 0xFFD700, // Gold color scaleX: 0.5 + Math.random() * 0.5, // Random size for variety scaleY: 0.5 + Math.random() * 0.5 }); self.addChild(star); // Add rotation to star star.rotation = Math.random() * Math.PI * 2; // Random initial rotation // Set random properties for the particle self.vx = (Math.random() - 0.5) * 12; // Random x velocity - increased for more dramatic effect self.vy = (Math.random() - 0.5) * 12; // Random y velocity - increased for more dramatic effect self.lifespan = 30 + Math.random() * 60; // Random lifespan (0.5-1.5 seconds) self.rotationSpeed = (Math.random() - 0.5) * 0.2; // Random rotation speed // Update function for particle movement self.update = function () { // Move particle self.x += self.vx; self.y += self.vy; // Apply slight gravity self.vy += 0.1; // Rotate star star.rotation += self.rotationSpeed; // Decrease lifespan self.lifespan--; // Fade out as lifespan decreases self.alpha = self.lifespan / 90; // Remove when lifespan is over if (self.lifespan <= 0) { if (self.parent) { self.parent.removeChild(self); self.destroy(); // Properly destroy the particle } return true; // Return true to indicate particle should be removed } return false; // Particle should continue to exist }; return self; }); /**** * Initialize Game ****/ // Define prototype methods for Button var game = new LK.Game({ backgroundColor: 0x1A1A2E }); /**** * Game Code ****/ // Game configuration // Define prototype methods for Button Button.prototype.over = function () { tween(this.background, { scaleX: 1.05, scaleY: 1.05 }, { duration: 100 }); }; Button.prototype.out = function () { tween(this.background, { scaleX: 1, scaleY: 1 }, { duration: 100 }); }; Button.prototype.down = function () { tween(this.background, { scaleX: 0.95, scaleY: 0.95 }, { duration: 100 }); }; Button.prototype.up = function () { tween(this.background, { scaleX: 1, scaleY: 1 }, { duration: 100 }); }; var config = { totalCards: 52, cardsPerRound: 4, initialViewTime: 5000, // 5 seconds to view cards initially viewTimeDecrement: 500, // Reduced by 500ms each level minViewTime: 1000, // Minimum viewing time of 1 second questionTypes: ["highestCard", "lowestCard", "sumOfCards", "specificPosition"] }; // Game state var gameState = { level: storage.currentLevel || 1, score: 0, highScore: storage.highScore || 0, currentDeck: [], selectedCards: [], currentQuestion: "", currentAnswer: null, gamePhase: "ready", // ready, memorize, question, result viewTime: config.initialViewTime }; // Game elements var cards = []; var buttons = []; var questionDisplay; var overlay; var scoreText; var levelText; var messageText; var countdownTimer; var timerStarted = false; // Initialize the game function initGame() { // Create deck createDeck(); // Create UI elements createUI(); // Play background music LK.playMusic('bgmusic'); // Reset timer state timerStarted = false; if (countdownTimer) { countdownTimer.reset(); countdownTimer.visible = false; } // Reset question display if (questionDisplay) { questionDisplay.visible = false; } // Show welcome screen showWelcomeScreen(); } // Create the full deck of cards function createDeck() { gameState.currentDeck = []; for (var i = 1; i <= config.totalCards; i++) { gameState.currentDeck.push(i); } } // Create UI elements function createUI() { // Add background image var background = LK.getAsset('background', { anchorX: 0.5, anchorY: 0.5 }); background.x = 2048 / 2; background.y = 2732 / 2; game.addChild(background); // Create overlay for transitions overlay = LK.getAsset('overlay', { anchorX: 0.5, anchorY: 0.5, alpha: 0 }); overlay.x = 2048 / 2; overlay.y = 2732 / 2; game.addChild(overlay); // Score display removed // Create level display levelText = new Text2("Level: 1", { size: 50, fill: 0xFFFFFF }); levelText.anchor.set(0.5, 0); levelText.x = 2048 * 3 / 4; levelText.y = 50; LK.gui.addChild(levelText); // Create message text messageText = new Text2("", { size: 70, fill: 0xFFFFFF }); // Set anchor to center for proper horizontal centering messageText.anchor.set(0.5, 0.5); // Position text in the center horizontally and near the top messageText.x = 2048 / 2; messageText.y = 400; LK.gui.addChild(messageText); // Create question display questionDisplay = new QuestionDisplay(); questionDisplay.x = 2048 / 2; questionDisplay.y = 800; game.addChild(questionDisplay); questionDisplay.visible = false; // Create countdown timer countdownTimer = new CountdownTimer(10); countdownTimer.x = 2048 / 2; countdownTimer.y = 2732 - 200; // Position at bottom of screen countdownTimer.visible = false; countdownTimer.onComplete = function () { hideCards(); generateQuestion(); }; game.addChild(countdownTimer); } // Show welcome screen function showWelcomeScreen() { // Clear UI elements to prevent text overlap on restart if (levelText && levelText.parent) LK.gui.removeChild(levelText); if (messageText && messageText.parent) LK.gui.removeChild(messageText); // Clear any previous game elements for (var i = 0; i < cards.length; i++) { if (cards[i] && cards[i].parent) { cards[i].parent.removeChild(cards[i]); } } for (var j = 0; j < buttons.length; j++) { if (buttons[j] && buttons[j].parent) { buttons[j].parent.removeChild(buttons[j]); } } // Reset arrays cards = []; buttons = []; // Reset game state gameState.score = 0; gameState.level = 1; // Reset level to 1 instead of using stored level storage.currentLevel = 1; // Also reset the stored level gameState.gamePhase = "ready"; // Reset game phase to avoid carrying over wrong answer messages // Hide question display if visible if (questionDisplay) { questionDisplay.visible = false; } // Recreate UI elements // Score display removed // Create level display levelText = new Text2("Level: " + gameState.level, { size: 50, fill: 0xFFFFFF }); levelText.anchor.set(0.5, 0); levelText.x = 2048 * 3 / 4; levelText.y = 50; LK.gui.addChild(levelText); // Create message text messageText = new Text2("", { size: 70, fill: 0xFFFFFF }); // Set anchor to center for proper horizontal centering messageText.anchor.set(0.5, 0.5); // Position text in the center horizontally and near the top messageText.x = 2048 / 2; messageText.y = 400; LK.gui.addChild(messageText); // Clear any previous message messageText.setText(""); // Create title text with proper size and center it messageText.setText("Mind Deck\nTest your memory!"); // Calculate the position for perfect centering near the top messageText.y = 300; // Create start button var startButton = new Button("Start Game"); startButton.x = 2048 / 2; startButton.y = 1200; game.addChild(startButton); startButton.down = function () { Button.prototype.down.call(this); }; startButton.up = function () { Button.prototype.up.call(this); game.removeChild(startButton); startGame(); }; // Update UI updateUI(); } // Start a new game function startGame() { // Reset game state if needed resetRound(); // Start the first round startRound(); } // Reset the current round function resetRound() { // Clear previous cards for (var i = 0; i < cards.length; i++) { if (cards[i].parent) { cards[i].parent.removeChild(cards[i]); } } cards = []; // Clear previous buttons for (var j = 0; j < buttons.length; j++) { if (buttons[j].parent) { buttons[j].parent.removeChild(buttons[j]); } } buttons = []; // Reset game phase gameState.gamePhase = "ready"; // Calculate view time based on level gameState.viewTime = Math.max(config.initialViewTime - (gameState.level - 1) * config.viewTimeDecrement, config.minViewTime); // Clear message text messageText.setText(""); // Hide question display questionDisplay.visible = false; // Reset timer state timerStarted = false; countdownTimer.reset(); countdownTimer.visible = false; // Update UI updateUI(); } // Start a new round function startRound() { // Clear any previous messages messageText.setText(""); // Set new message messageText.setText("Get Ready!"); // Reset timer state to ensure it's properly initialized for the new round timerStarted = false; countdownTimer.reset(); countdownTimer.visible = false; // Fade in tween(overlay, { alpha: 0 }, { duration: 500, onFinish: function onFinish() { // Draw cards for this round drawCards(); // Show cards after a short delay LK.setTimeout(function () { messageText.setText("Memorize the cards!"); showCards(); }, 1000); } }); } // Draw cards for the round function drawCards() { // Shuffle the deck shuffleDeck(); // Select cards for this round gameState.selectedCards = gameState.currentDeck.slice(0, config.cardsPerRound); // Create card objects var cardWidth = 300; var spacing = 50; var totalWidth = cardWidth * config.cardsPerRound + spacing * (config.cardsPerRound - 1); var startX = (2048 - totalWidth) / 2 + cardWidth / 2; for (var i = 0; i < config.cardsPerRound; i++) { var card = new Card(gameState.selectedCards[i]); card.x = startX + i * (cardWidth + spacing); card.y = 1200; game.addChild(card); cards.push(card); } } // Show cards to memorize function showCards() { gameState.gamePhase = "memorize"; // Show message to tell player to click cards messageText.setText("Click on cards to reveal them!\nCards will disappear in 10 seconds"); // Move message to the left to fit on screen messageText.x = 2048 / 2 - 200; // Make all cards clickable but don't flip them automatically for (var i = 0; i < cards.length; i++) { cards[i].canFlip = true; } // Timer for cards to disappear after 15 seconds LK.setTimeout(function () { // Only execute this timeout if the timer hasn't been started (no cards flipped) if (!timerStarted) { // Remove cards from game for (var i = 0; i < cards.length; i++) { // Create fade out animation for each card tween(cards[i], { alpha: 0 }, { duration: 500, onFinish: function onFinish() { // After animation is complete, ensure cards are removed from game for (var j = 0; j < cards.length; j++) { if (cards[j].parent) { cards[j].parent.removeChild(cards[j]); } } } }); } // Update message to inform player messageText.setText("Time's up! Answer the question from memory"); // Automatically transition to question screen if no cards were flipped hideCards(); generateQuestion(); } }, 15000); // 15 seconds } // Hide cards and prepare question function hideCards() { // Stop the countdown timer if it's running if (timerStarted) { countdownTimer.stop(); countdownTimer.visible = false; } // Disable card flipping for all cards for (var i = 0; i < cards.length; i++) { cards[i].canFlip = false; } // Animate all cards flipping back with a cascade effect var allAnimationsComplete = 0; var totalAnimations = 0; // Count cards that need to be flipped for (var i = 0; i < cards.length; i++) { if (cards[i].revealed) { totalAnimations++; } } // If no cards are revealed, move directly to question if (totalAnimations === 0) { LK.getSound('flip').play(); generateQuestion(); return; } // Flip all revealed cards with animation for (var i = 0; i < cards.length; i++) { // Use a closure to preserve the i value for each card (function (index) { // Only animate revealed cards if (cards[index].revealed && !cards[index].animating) { // Set a small delay for each card var delay = index * 100; LK.setTimeout(function () { cards[index].animating = true; // First half of flip tween(cards[index], { scaleX: 0 }, { duration: 150, onFinish: function onFinish() { // Switch cards at the middle of animation cards[index].back.visible = true; cards[index].front.visible = false; cards[index].numberText.visible = false; // Second half of flip tween(cards[index], { scaleX: 1 }, { duration: 150, onFinish: function onFinish() { cards[index].revealed = false; cards[index].animating = false; // Track animation completion allAnimationsComplete++; // Only play sound once for the last card if (allAnimationsComplete === totalAnimations) { LK.getSound('flip').play(); // Generate and display question after all animations complete generateQuestion(); } } }); } }); }, delay); } })(i); } // If no cards needed animation, ensure we still generate a question if (totalAnimations === 0) { generateQuestion(); } } // Generate a question about the cards function generateQuestion() { gameState.gamePhase = "question"; // Clear the canvas of any previous questions or elements for (var i = 0; i < buttons.length; i++) { if (buttons[i] && buttons[i].parent) { buttons[i].parent.removeChild(buttons[i]); } } buttons = []; // Clear previous question display questionDisplay.setQuestion(""); // Make sure question display is visible and reset questionDisplay.visible = true; // Choose random question type var questionType = config.questionTypes[Math.floor(Math.random() * config.questionTypes.length)]; switch (questionType) { case "highestCard": gameState.currentQuestion = "What was the highest card?"; gameState.currentAnswer = Math.max.apply(null, gameState.selectedCards); createAnswerButtons(gameState.currentAnswer); break; case "lowestCard": gameState.currentQuestion = "What was the lowest card?"; gameState.currentAnswer = Math.min.apply(null, gameState.selectedCards); createAnswerButtons(gameState.currentAnswer); break; case "sumOfCards": gameState.currentQuestion = "What was the sum of all cards?"; var sum = 0; for (var i = 0; i < gameState.selectedCards.length; i++) { sum += gameState.selectedCards[i]; } gameState.currentAnswer = sum; createAnswerButtons(gameState.currentAnswer); break; case "specificPosition": var position = Math.floor(Math.random() * config.cardsPerRound); var positionNames = ["first", "second", "third", "fourth"]; gameState.currentQuestion = "What was the " + positionNames[position] + " card?"; gameState.currentAnswer = gameState.selectedCards[position]; createAnswerButtons(gameState.currentAnswer); break; } // Show question questionDisplay.setQuestion(gameState.currentQuestion); questionDisplay.visible = true; messageText.setText("Answer the question:"); } // Create answer buttons function createAnswerButtons(correctAnswer) { // Generate wrong answers var answers = [correctAnswer]; while (answers.length < 4) { var wrongAnswer; // Generate a reasonable wrong answer based on correct answer if (correctAnswer <= 10) { wrongAnswer = Math.floor(Math.random() * 20) + 1; } else if (correctAnswer <= 52) { wrongAnswer = Math.floor(Math.random() * 52) + 1; } else { // For sums or other large numbers var variance = Math.floor(correctAnswer * 0.3); wrongAnswer = correctAnswer + Math.floor(Math.random() * variance * 2) - variance; wrongAnswer = Math.max(1, wrongAnswer); } // Make sure we don't add duplicates if (answers.indexOf(wrongAnswer) === -1) { answers.push(wrongAnswer); } } // Shuffle answers shuffleArray(answers); // Create buttons var buttonWidth = 400; var spacing = 50; var totalWidth = buttonWidth * 2 + spacing; var startX = (2048 - totalWidth) / 2 + buttonWidth / 2; for (var i = 0; i < answers.length; i++) { var button = new Button(answers[i].toString()); // Position buttons in a 2x2 grid var row = Math.floor(i / 2); var col = i % 2; button.x = startX + col * (buttonWidth + spacing); button.y = 1500 + row * 150; // Store the answer value button.answerValue = answers[i]; // Set up button handlers button.down = function () { Button.prototype.down.call(this); }; button.up = function () { Button.prototype.up.call(this); checkAnswer(this.answerValue); }; game.addChild(button); buttons.push(button); } } // Check if the answer is correct function checkAnswer(answer) { gameState.gamePhase = "result"; // Hide question questionDisplay.visible = false; // Clear all current elements from the canvas for (var i = 0; i < cards.length; i++) { if (cards[i] && cards[i].parent) { cards[i].parent.removeChild(cards[i]); } } cards = []; // Clear cards array completely for (var j = 0; j < buttons.length; j++) { if (buttons[j] && buttons[j].parent) { buttons[j].parent.removeChild(buttons[j]); } } buttons = []; // Clear buttons array completely // Reset all active particles for (var k = activeParticles.length - 1; k >= 0; k--) { var particles = activeParticles[k]; for (var l = particles.length - 1; l >= 0; l--) { if (particles[l] && particles[l].parent) { particles[l].parent.removeChild(particles[l]); } } } activeParticles = []; // Clear all particles // Check if correct if (answer === gameState.currentAnswer) { // Correct answer! messageText.setText("Correct!"); // Update score gameState.score += gameState.level * 10; // Update UI immediately to reflect score change updateUI(); // Play correct sound LK.getSound('correct').play(); // Move to next level after delay LK.setTimeout(function () { gameState.level++; storage.currentLevel = gameState.level; // Update high score if needed if (gameState.score > gameState.highScore) { gameState.highScore = gameState.score; storage.highScore = gameState.highScore; } // Play level up sound LK.getSound('levelUp').play(); // Ensure all state variables are properly reset timerStarted = false; countdownTimer.reset(); countdownTimer.visible = false; // Clear any previous wrong answer messages messageText.setText(""); // Update UI again after level change updateUI(); // Start next round resetRound(); startRound(); }, 1500); } else { // Wrong answer messageText.setText("Wrong! The correct answer was " + gameState.currentAnswer); // Play wrong sound LK.getSound('wrong').play(); // Auto-hide wrong answer message after 1 second LK.setTimeout(function () { messageText.setText(""); }, 1000); // Show game over after delay LK.setTimeout(function () { LK.showGameOver(); }, 2000); } } // Update UI elements function updateUI() { levelText.setText("Level: " + gameState.level); } // Shuffle the deck function shuffleDeck() { shuffleArray(gameState.currentDeck); } // Fisher-Yates shuffle algorithm function shuffleArray(array) { for (var i = array.length - 1; i > 0; i--) { var j = Math.floor(Math.random() * (i + 1)); var temp = array[i]; array[i] = array[j]; array[j] = temp; } return array; } // Function to create a burst of stars at a specific position function createStarBurst(x, y) { var particles = []; var numParticles = 15 + Math.floor(Math.random() * 10); // 15-25 particles for more impressive effect var colors = [0xFFD700, 0xFFA500, 0xFF8C00, 0xFFFF00, 0xFAFAD2]; // Gold, orange, dark orange, yellow, light yellow // Create particles for (var i = 0; i < numParticles; i++) { var particle = new StarParticle(); particle.x = x; particle.y = y; // Apply random color from our palette var randomColor = colors[Math.floor(Math.random() * colors.length)]; // Get the star child and tint it if (particle.children && particle.children[0]) { particle.children[0].tint = randomColor; } game.addChild(particle); particles.push(particle); } return particles; } // Array to store active particle systems var activeParticles = []; // Game update loop game.update = function () { // Update all active particles for (var i = activeParticles.length - 1; i >= 0; i--) { var particleSystem = activeParticles[i]; var removeCount = 0; for (var j = particleSystem.length - 1; j >= 0; j--) { var particle = particleSystem[j]; if (particle.update()) { particleSystem.splice(j, 1); removeCount++; } } // Remove empty particle systems if (particleSystem.length === 0) { activeParticles.splice(i, 1); } } }; // Handle drag and drop game.down = function (x, y, obj) { // Nothing needed for this game }; game.up = function (x, y, obj) { // Nothing needed for this game }; game.move = function (x, y, obj) { // Nothing needed for this game }; // Initialize the game initGame();
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
highScore: 0,
currentLevel: 1
});
/****
* Classes
****/
var Button = Container.expand(function (text) {
var self = Container.call(this);
self.background = self.attachAsset('button', {
anchorX: 0.5,
anchorY: 0.5
});
self.label = new Text2(text || "Button", {
size: 50,
fill: 0xFFFFFF
});
self.label.anchor.set(0.5, 0.5);
self.addChild(self.label);
// Set interactive
self.interactive = true;
return self;
});
// Define prototype methods for Button
var Card = Container.expand(function (cardNumber) {
var self = Container.call(this);
self.cardNumber = cardNumber || 0;
self.revealed = false;
self.matched = false;
self.canFlip = false; // Flag to control when cards can be flipped
// Card back (shown initially)
self.back = self.attachAsset('cardBack', {
anchorX: 0.5,
anchorY: 0.5
});
// Card front (shown when flipped)
self.front = self.attachAsset('cardFront', {
anchorX: 0.5,
anchorY: 0.5,
visible: false
});
// Card number text
self.numberText = new Text2(self.cardNumber.toString(), {
size: 100,
fill: 0x000000
});
self.numberText.anchor.set(0.5, 0.5);
self.numberText.visible = false;
self.addChild(self.numberText);
// Make card interactive
self.interactive = true;
// Set size based on the card
self.width = self.back.width;
self.height = self.back.height;
// Flag to indicate if animation is in progress
self.animating = false;
// Flip card to show front with animation
self.showFront = function () {
if (!self.revealed && !self.animating) {
self.animating = true;
// First half of flip: scale horizontally to 0
tween(self, {
scaleX: 0
}, {
duration: 150,
onFinish: function onFinish() {
// Switch cards at the middle of animation
self.back.visible = false;
self.front.visible = true;
self.numberText.visible = true;
// Second half of flip: scale back from 0 to 1
tween(self, {
scaleX: 1
}, {
duration: 150,
onFinish: function onFinish() {
self.revealed = true;
self.animating = false;
LK.getSound('flip').play();
}
});
}
});
}
};
// Flip card to show back with animation
self.showBack = function () {
if (self.revealed && !self.animating) {
self.animating = true;
// First half of flip: scale horizontally to 0
tween(self, {
scaleX: 0
}, {
duration: 150,
onFinish: function onFinish() {
// Switch cards at the middle of animation
self.back.visible = true;
self.front.visible = false;
self.numberText.visible = false;
// Second half of flip: scale back from 0 to 1
tween(self, {
scaleX: 1
}, {
duration: 150,
onFinish: function onFinish() {
self.revealed = false;
self.animating = false;
}
});
}
});
}
};
self.down = function (x, y, obj) {
// Handle card click
if (gameState.gamePhase === "memorize" && self.canFlip && !self.animating) {
// Create star burst effect at click location
var localPos = {
x: x,
y: y
};
// Fix: use the clicked object's position directly since parent.toGlobal is causing an error
var gamePos = {
x: self.x,
y: self.y
};
// Create and track the particles
var particles = createStarBurst(gamePos.x, gamePos.y);
activeParticles.push(particles);
if (!self.revealed) {
self.showFront();
// Start the countdown timer when the first card is flipped
if (!timerStarted) {
timerStarted = true;
countdownTimer.visible = true;
countdownTimer.reset();
countdownTimer.start();
}
} else {
self.showBack();
}
}
};
return self;
});
var CountdownTimer = Container.expand(function (duration) {
var self = Container.call(this);
// Create circle background
self.circle = LK.getAsset('centerCircle', {
anchorX: 0.5,
anchorY: 0.5,
tint: 0x4CAF50
});
self.addChild(self.circle);
// Create text for countdown
self.timeText = new Text2("15", {
size: 80,
fill: 0xFFFFFF
});
self.timeText.anchor.set(0.5, 0.5);
self.addChild(self.timeText);
// Timer properties
self.duration = duration || 15; // Default 15 seconds
self.timeRemaining = self.duration;
self.active = false;
self.timerInterval = null;
// Start the countdown
self.start = function () {
if (self.active) return;
self.active = true;
self.timeRemaining = self.duration;
self.updateDisplay();
self.timerInterval = LK.setInterval(function () {
self.timeRemaining--;
self.updateDisplay();
// Update circle scale based on time remaining
var progress = self.timeRemaining / self.duration;
self.circle.scale.set(progress);
// Change color as time runs out
if (self.timeRemaining <= 5) {
self.circle.tint = 0xFF5252; // Red when time is running out
}
if (self.timeRemaining <= 0) {
self.stop();
if (typeof self.onComplete === 'function') {
self.onComplete();
}
}
}, 1000);
};
// Stop the countdown
self.stop = function () {
if (!self.active) return;
self.active = false;
if (self.timerInterval) {
LK.clearInterval(self.timerInterval);
self.timerInterval = null;
}
};
// Reset the countdown
self.reset = function () {
self.stop();
self.timeRemaining = self.duration;
self.updateDisplay();
self.circle.tint = 0x4CAF50; // Reset to green
self.circle.scale.set(1);
};
// Update the display
self.updateDisplay = function () {
self.timeText.setText(Math.max(0, self.timeRemaining).toString());
};
return self;
});
var QuestionDisplay = Container.expand(function () {
var self = Container.call(this);
self.questionText = new Text2("", {
size: 60,
fill: 0xFFFFFF
});
// Set anchor to center for proper horizontal centering
self.questionText.anchor.set(0.5, 0.5);
self.addChild(self.questionText);
self.setQuestion = function (question) {
// Set the text content
self.questionText.setText(question);
// Reset position to ensure it's centered
self.questionText.x = 0;
self.questionText.y = 0;
};
return self;
});
var StarParticle = Container.expand(function () {
var self = Container.call(this);
// Create a star shape
var star = LK.getAsset('star', {
anchorX: 0.5,
anchorY: 0.5,
tint: 0xFFD700,
// Gold color
scaleX: 0.5 + Math.random() * 0.5,
// Random size for variety
scaleY: 0.5 + Math.random() * 0.5
});
self.addChild(star);
// Add rotation to star
star.rotation = Math.random() * Math.PI * 2; // Random initial rotation
// Set random properties for the particle
self.vx = (Math.random() - 0.5) * 12; // Random x velocity - increased for more dramatic effect
self.vy = (Math.random() - 0.5) * 12; // Random y velocity - increased for more dramatic effect
self.lifespan = 30 + Math.random() * 60; // Random lifespan (0.5-1.5 seconds)
self.rotationSpeed = (Math.random() - 0.5) * 0.2; // Random rotation speed
// Update function for particle movement
self.update = function () {
// Move particle
self.x += self.vx;
self.y += self.vy;
// Apply slight gravity
self.vy += 0.1;
// Rotate star
star.rotation += self.rotationSpeed;
// Decrease lifespan
self.lifespan--;
// Fade out as lifespan decreases
self.alpha = self.lifespan / 90;
// Remove when lifespan is over
if (self.lifespan <= 0) {
if (self.parent) {
self.parent.removeChild(self);
self.destroy(); // Properly destroy the particle
}
return true; // Return true to indicate particle should be removed
}
return false; // Particle should continue to exist
};
return self;
});
/****
* Initialize Game
****/
// Define prototype methods for Button
var game = new LK.Game({
backgroundColor: 0x1A1A2E
});
/****
* Game Code
****/
// Game configuration
// Define prototype methods for Button
Button.prototype.over = function () {
tween(this.background, {
scaleX: 1.05,
scaleY: 1.05
}, {
duration: 100
});
};
Button.prototype.out = function () {
tween(this.background, {
scaleX: 1,
scaleY: 1
}, {
duration: 100
});
};
Button.prototype.down = function () {
tween(this.background, {
scaleX: 0.95,
scaleY: 0.95
}, {
duration: 100
});
};
Button.prototype.up = function () {
tween(this.background, {
scaleX: 1,
scaleY: 1
}, {
duration: 100
});
};
var config = {
totalCards: 52,
cardsPerRound: 4,
initialViewTime: 5000,
// 5 seconds to view cards initially
viewTimeDecrement: 500,
// Reduced by 500ms each level
minViewTime: 1000,
// Minimum viewing time of 1 second
questionTypes: ["highestCard", "lowestCard", "sumOfCards", "specificPosition"]
};
// Game state
var gameState = {
level: storage.currentLevel || 1,
score: 0,
highScore: storage.highScore || 0,
currentDeck: [],
selectedCards: [],
currentQuestion: "",
currentAnswer: null,
gamePhase: "ready",
// ready, memorize, question, result
viewTime: config.initialViewTime
};
// Game elements
var cards = [];
var buttons = [];
var questionDisplay;
var overlay;
var scoreText;
var levelText;
var messageText;
var countdownTimer;
var timerStarted = false;
// Initialize the game
function initGame() {
// Create deck
createDeck();
// Create UI elements
createUI();
// Play background music
LK.playMusic('bgmusic');
// Reset timer state
timerStarted = false;
if (countdownTimer) {
countdownTimer.reset();
countdownTimer.visible = false;
}
// Reset question display
if (questionDisplay) {
questionDisplay.visible = false;
}
// Show welcome screen
showWelcomeScreen();
}
// Create the full deck of cards
function createDeck() {
gameState.currentDeck = [];
for (var i = 1; i <= config.totalCards; i++) {
gameState.currentDeck.push(i);
}
}
// Create UI elements
function createUI() {
// Add background image
var background = LK.getAsset('background', {
anchorX: 0.5,
anchorY: 0.5
});
background.x = 2048 / 2;
background.y = 2732 / 2;
game.addChild(background);
// Create overlay for transitions
overlay = LK.getAsset('overlay', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0
});
overlay.x = 2048 / 2;
overlay.y = 2732 / 2;
game.addChild(overlay);
// Score display removed
// Create level display
levelText = new Text2("Level: 1", {
size: 50,
fill: 0xFFFFFF
});
levelText.anchor.set(0.5, 0);
levelText.x = 2048 * 3 / 4;
levelText.y = 50;
LK.gui.addChild(levelText);
// Create message text
messageText = new Text2("", {
size: 70,
fill: 0xFFFFFF
});
// Set anchor to center for proper horizontal centering
messageText.anchor.set(0.5, 0.5);
// Position text in the center horizontally and near the top
messageText.x = 2048 / 2;
messageText.y = 400;
LK.gui.addChild(messageText);
// Create question display
questionDisplay = new QuestionDisplay();
questionDisplay.x = 2048 / 2;
questionDisplay.y = 800;
game.addChild(questionDisplay);
questionDisplay.visible = false;
// Create countdown timer
countdownTimer = new CountdownTimer(10);
countdownTimer.x = 2048 / 2;
countdownTimer.y = 2732 - 200; // Position at bottom of screen
countdownTimer.visible = false;
countdownTimer.onComplete = function () {
hideCards();
generateQuestion();
};
game.addChild(countdownTimer);
}
// Show welcome screen
function showWelcomeScreen() {
// Clear UI elements to prevent text overlap on restart
if (levelText && levelText.parent) LK.gui.removeChild(levelText);
if (messageText && messageText.parent) LK.gui.removeChild(messageText);
// Clear any previous game elements
for (var i = 0; i < cards.length; i++) {
if (cards[i] && cards[i].parent) {
cards[i].parent.removeChild(cards[i]);
}
}
for (var j = 0; j < buttons.length; j++) {
if (buttons[j] && buttons[j].parent) {
buttons[j].parent.removeChild(buttons[j]);
}
}
// Reset arrays
cards = [];
buttons = [];
// Reset game state
gameState.score = 0;
gameState.level = 1; // Reset level to 1 instead of using stored level
storage.currentLevel = 1; // Also reset the stored level
gameState.gamePhase = "ready"; // Reset game phase to avoid carrying over wrong answer messages
// Hide question display if visible
if (questionDisplay) {
questionDisplay.visible = false;
}
// Recreate UI elements
// Score display removed
// Create level display
levelText = new Text2("Level: " + gameState.level, {
size: 50,
fill: 0xFFFFFF
});
levelText.anchor.set(0.5, 0);
levelText.x = 2048 * 3 / 4;
levelText.y = 50;
LK.gui.addChild(levelText);
// Create message text
messageText = new Text2("", {
size: 70,
fill: 0xFFFFFF
});
// Set anchor to center for proper horizontal centering
messageText.anchor.set(0.5, 0.5);
// Position text in the center horizontally and near the top
messageText.x = 2048 / 2;
messageText.y = 400;
LK.gui.addChild(messageText);
// Clear any previous message
messageText.setText("");
// Create title text with proper size and center it
messageText.setText("Mind Deck\nTest your memory!");
// Calculate the position for perfect centering near the top
messageText.y = 300;
// Create start button
var startButton = new Button("Start Game");
startButton.x = 2048 / 2;
startButton.y = 1200;
game.addChild(startButton);
startButton.down = function () {
Button.prototype.down.call(this);
};
startButton.up = function () {
Button.prototype.up.call(this);
game.removeChild(startButton);
startGame();
};
// Update UI
updateUI();
}
// Start a new game
function startGame() {
// Reset game state if needed
resetRound();
// Start the first round
startRound();
}
// Reset the current round
function resetRound() {
// Clear previous cards
for (var i = 0; i < cards.length; i++) {
if (cards[i].parent) {
cards[i].parent.removeChild(cards[i]);
}
}
cards = [];
// Clear previous buttons
for (var j = 0; j < buttons.length; j++) {
if (buttons[j].parent) {
buttons[j].parent.removeChild(buttons[j]);
}
}
buttons = [];
// Reset game phase
gameState.gamePhase = "ready";
// Calculate view time based on level
gameState.viewTime = Math.max(config.initialViewTime - (gameState.level - 1) * config.viewTimeDecrement, config.minViewTime);
// Clear message text
messageText.setText("");
// Hide question display
questionDisplay.visible = false;
// Reset timer state
timerStarted = false;
countdownTimer.reset();
countdownTimer.visible = false;
// Update UI
updateUI();
}
// Start a new round
function startRound() {
// Clear any previous messages
messageText.setText("");
// Set new message
messageText.setText("Get Ready!");
// Reset timer state to ensure it's properly initialized for the new round
timerStarted = false;
countdownTimer.reset();
countdownTimer.visible = false;
// Fade in
tween(overlay, {
alpha: 0
}, {
duration: 500,
onFinish: function onFinish() {
// Draw cards for this round
drawCards();
// Show cards after a short delay
LK.setTimeout(function () {
messageText.setText("Memorize the cards!");
showCards();
}, 1000);
}
});
}
// Draw cards for the round
function drawCards() {
// Shuffle the deck
shuffleDeck();
// Select cards for this round
gameState.selectedCards = gameState.currentDeck.slice(0, config.cardsPerRound);
// Create card objects
var cardWidth = 300;
var spacing = 50;
var totalWidth = cardWidth * config.cardsPerRound + spacing * (config.cardsPerRound - 1);
var startX = (2048 - totalWidth) / 2 + cardWidth / 2;
for (var i = 0; i < config.cardsPerRound; i++) {
var card = new Card(gameState.selectedCards[i]);
card.x = startX + i * (cardWidth + spacing);
card.y = 1200;
game.addChild(card);
cards.push(card);
}
}
// Show cards to memorize
function showCards() {
gameState.gamePhase = "memorize";
// Show message to tell player to click cards
messageText.setText("Click on cards to reveal them!\nCards will disappear in 10 seconds");
// Move message to the left to fit on screen
messageText.x = 2048 / 2 - 200;
// Make all cards clickable but don't flip them automatically
for (var i = 0; i < cards.length; i++) {
cards[i].canFlip = true;
}
// Timer for cards to disappear after 15 seconds
LK.setTimeout(function () {
// Only execute this timeout if the timer hasn't been started (no cards flipped)
if (!timerStarted) {
// Remove cards from game
for (var i = 0; i < cards.length; i++) {
// Create fade out animation for each card
tween(cards[i], {
alpha: 0
}, {
duration: 500,
onFinish: function onFinish() {
// After animation is complete, ensure cards are removed from game
for (var j = 0; j < cards.length; j++) {
if (cards[j].parent) {
cards[j].parent.removeChild(cards[j]);
}
}
}
});
}
// Update message to inform player
messageText.setText("Time's up! Answer the question from memory");
// Automatically transition to question screen if no cards were flipped
hideCards();
generateQuestion();
}
}, 15000); // 15 seconds
}
// Hide cards and prepare question
function hideCards() {
// Stop the countdown timer if it's running
if (timerStarted) {
countdownTimer.stop();
countdownTimer.visible = false;
}
// Disable card flipping for all cards
for (var i = 0; i < cards.length; i++) {
cards[i].canFlip = false;
}
// Animate all cards flipping back with a cascade effect
var allAnimationsComplete = 0;
var totalAnimations = 0;
// Count cards that need to be flipped
for (var i = 0; i < cards.length; i++) {
if (cards[i].revealed) {
totalAnimations++;
}
}
// If no cards are revealed, move directly to question
if (totalAnimations === 0) {
LK.getSound('flip').play();
generateQuestion();
return;
}
// Flip all revealed cards with animation
for (var i = 0; i < cards.length; i++) {
// Use a closure to preserve the i value for each card
(function (index) {
// Only animate revealed cards
if (cards[index].revealed && !cards[index].animating) {
// Set a small delay for each card
var delay = index * 100;
LK.setTimeout(function () {
cards[index].animating = true;
// First half of flip
tween(cards[index], {
scaleX: 0
}, {
duration: 150,
onFinish: function onFinish() {
// Switch cards at the middle of animation
cards[index].back.visible = true;
cards[index].front.visible = false;
cards[index].numberText.visible = false;
// Second half of flip
tween(cards[index], {
scaleX: 1
}, {
duration: 150,
onFinish: function onFinish() {
cards[index].revealed = false;
cards[index].animating = false;
// Track animation completion
allAnimationsComplete++;
// Only play sound once for the last card
if (allAnimationsComplete === totalAnimations) {
LK.getSound('flip').play();
// Generate and display question after all animations complete
generateQuestion();
}
}
});
}
});
}, delay);
}
})(i);
}
// If no cards needed animation, ensure we still generate a question
if (totalAnimations === 0) {
generateQuestion();
}
}
// Generate a question about the cards
function generateQuestion() {
gameState.gamePhase = "question";
// Clear the canvas of any previous questions or elements
for (var i = 0; i < buttons.length; i++) {
if (buttons[i] && buttons[i].parent) {
buttons[i].parent.removeChild(buttons[i]);
}
}
buttons = [];
// Clear previous question display
questionDisplay.setQuestion("");
// Make sure question display is visible and reset
questionDisplay.visible = true;
// Choose random question type
var questionType = config.questionTypes[Math.floor(Math.random() * config.questionTypes.length)];
switch (questionType) {
case "highestCard":
gameState.currentQuestion = "What was the highest card?";
gameState.currentAnswer = Math.max.apply(null, gameState.selectedCards);
createAnswerButtons(gameState.currentAnswer);
break;
case "lowestCard":
gameState.currentQuestion = "What was the lowest card?";
gameState.currentAnswer = Math.min.apply(null, gameState.selectedCards);
createAnswerButtons(gameState.currentAnswer);
break;
case "sumOfCards":
gameState.currentQuestion = "What was the sum of all cards?";
var sum = 0;
for (var i = 0; i < gameState.selectedCards.length; i++) {
sum += gameState.selectedCards[i];
}
gameState.currentAnswer = sum;
createAnswerButtons(gameState.currentAnswer);
break;
case "specificPosition":
var position = Math.floor(Math.random() * config.cardsPerRound);
var positionNames = ["first", "second", "third", "fourth"];
gameState.currentQuestion = "What was the " + positionNames[position] + " card?";
gameState.currentAnswer = gameState.selectedCards[position];
createAnswerButtons(gameState.currentAnswer);
break;
}
// Show question
questionDisplay.setQuestion(gameState.currentQuestion);
questionDisplay.visible = true;
messageText.setText("Answer the question:");
}
// Create answer buttons
function createAnswerButtons(correctAnswer) {
// Generate wrong answers
var answers = [correctAnswer];
while (answers.length < 4) {
var wrongAnswer;
// Generate a reasonable wrong answer based on correct answer
if (correctAnswer <= 10) {
wrongAnswer = Math.floor(Math.random() * 20) + 1;
} else if (correctAnswer <= 52) {
wrongAnswer = Math.floor(Math.random() * 52) + 1;
} else {
// For sums or other large numbers
var variance = Math.floor(correctAnswer * 0.3);
wrongAnswer = correctAnswer + Math.floor(Math.random() * variance * 2) - variance;
wrongAnswer = Math.max(1, wrongAnswer);
}
// Make sure we don't add duplicates
if (answers.indexOf(wrongAnswer) === -1) {
answers.push(wrongAnswer);
}
}
// Shuffle answers
shuffleArray(answers);
// Create buttons
var buttonWidth = 400;
var spacing = 50;
var totalWidth = buttonWidth * 2 + spacing;
var startX = (2048 - totalWidth) / 2 + buttonWidth / 2;
for (var i = 0; i < answers.length; i++) {
var button = new Button(answers[i].toString());
// Position buttons in a 2x2 grid
var row = Math.floor(i / 2);
var col = i % 2;
button.x = startX + col * (buttonWidth + spacing);
button.y = 1500 + row * 150;
// Store the answer value
button.answerValue = answers[i];
// Set up button handlers
button.down = function () {
Button.prototype.down.call(this);
};
button.up = function () {
Button.prototype.up.call(this);
checkAnswer(this.answerValue);
};
game.addChild(button);
buttons.push(button);
}
}
// Check if the answer is correct
function checkAnswer(answer) {
gameState.gamePhase = "result";
// Hide question
questionDisplay.visible = false;
// Clear all current elements from the canvas
for (var i = 0; i < cards.length; i++) {
if (cards[i] && cards[i].parent) {
cards[i].parent.removeChild(cards[i]);
}
}
cards = []; // Clear cards array completely
for (var j = 0; j < buttons.length; j++) {
if (buttons[j] && buttons[j].parent) {
buttons[j].parent.removeChild(buttons[j]);
}
}
buttons = []; // Clear buttons array completely
// Reset all active particles
for (var k = activeParticles.length - 1; k >= 0; k--) {
var particles = activeParticles[k];
for (var l = particles.length - 1; l >= 0; l--) {
if (particles[l] && particles[l].parent) {
particles[l].parent.removeChild(particles[l]);
}
}
}
activeParticles = []; // Clear all particles
// Check if correct
if (answer === gameState.currentAnswer) {
// Correct answer!
messageText.setText("Correct!");
// Update score
gameState.score += gameState.level * 10;
// Update UI immediately to reflect score change
updateUI();
// Play correct sound
LK.getSound('correct').play();
// Move to next level after delay
LK.setTimeout(function () {
gameState.level++;
storage.currentLevel = gameState.level;
// Update high score if needed
if (gameState.score > gameState.highScore) {
gameState.highScore = gameState.score;
storage.highScore = gameState.highScore;
}
// Play level up sound
LK.getSound('levelUp').play();
// Ensure all state variables are properly reset
timerStarted = false;
countdownTimer.reset();
countdownTimer.visible = false;
// Clear any previous wrong answer messages
messageText.setText("");
// Update UI again after level change
updateUI();
// Start next round
resetRound();
startRound();
}, 1500);
} else {
// Wrong answer
messageText.setText("Wrong! The correct answer was " + gameState.currentAnswer);
// Play wrong sound
LK.getSound('wrong').play();
// Auto-hide wrong answer message after 1 second
LK.setTimeout(function () {
messageText.setText("");
}, 1000);
// Show game over after delay
LK.setTimeout(function () {
LK.showGameOver();
}, 2000);
}
}
// Update UI elements
function updateUI() {
levelText.setText("Level: " + gameState.level);
}
// Shuffle the deck
function shuffleDeck() {
shuffleArray(gameState.currentDeck);
}
// Fisher-Yates shuffle algorithm
function shuffleArray(array) {
for (var i = array.length - 1; i > 0; i--) {
var j = Math.floor(Math.random() * (i + 1));
var temp = array[i];
array[i] = array[j];
array[j] = temp;
}
return array;
}
// Function to create a burst of stars at a specific position
function createStarBurst(x, y) {
var particles = [];
var numParticles = 15 + Math.floor(Math.random() * 10); // 15-25 particles for more impressive effect
var colors = [0xFFD700, 0xFFA500, 0xFF8C00, 0xFFFF00, 0xFAFAD2]; // Gold, orange, dark orange, yellow, light yellow
// Create particles
for (var i = 0; i < numParticles; i++) {
var particle = new StarParticle();
particle.x = x;
particle.y = y;
// Apply random color from our palette
var randomColor = colors[Math.floor(Math.random() * colors.length)];
// Get the star child and tint it
if (particle.children && particle.children[0]) {
particle.children[0].tint = randomColor;
}
game.addChild(particle);
particles.push(particle);
}
return particles;
}
// Array to store active particle systems
var activeParticles = [];
// Game update loop
game.update = function () {
// Update all active particles
for (var i = activeParticles.length - 1; i >= 0; i--) {
var particleSystem = activeParticles[i];
var removeCount = 0;
for (var j = particleSystem.length - 1; j >= 0; j--) {
var particle = particleSystem[j];
if (particle.update()) {
particleSystem.splice(j, 1);
removeCount++;
}
}
// Remove empty particle systems
if (particleSystem.length === 0) {
activeParticles.splice(i, 1);
}
}
};
// Handle drag and drop
game.down = function (x, y, obj) {
// Nothing needed for this game
};
game.up = function (x, y, obj) {
// Nothing needed for this game
};
game.move = function (x, y, obj) {
// Nothing needed for this game
};
// Initialize the game
initGame();