/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
coins: 0,
currentLevel: 1,
bombCount: 0,
hintCount: 0,
clockCount: 0,
timerCount: 0,
skipCount: 0,
mansionCount: 0,
hutCount: 0,
patchCount: 0,
towerCount: 0,
castleCount: 0,
ghostCount: 0,
wizardCount: 0,
catCount: 0,
buildingPositions: {}
});
/****
* Classes
****/
var Card = Container.expand(function (symbolType) {
var self = Container.call(this);
self.symbolType = symbolType;
self.isFlipped = false;
self.isMatched = false;
self.canFlip = true;
// Card back (always visible when not flipped)
var cardBack = LK.getAsset('cardBack', {
anchorX: 0.5,
anchorY: 0.5
});
self.addChild(cardBack);
// Card front (white background)
var cardFront = LK.getAsset('cardFront', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0
});
self.addChild(cardFront);
// Symbol on the card
var symbol = LK.getAsset('symbol' + symbolType, {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0
});
self.addChild(symbol);
self.flip = function () {
if (!self.canFlip || self.isMatched) {
return;
}
LK.getSound('cardFlip').play();
self.isFlipped = !self.isFlipped;
if (self.isFlipped) {
// Show front and symbol
tween(cardFront, {
alpha: 1
}, {
duration: 200
});
tween(symbol, {
alpha: 1
}, {
duration: 200
});
} else {
// Hide front and symbol
tween(cardFront, {
alpha: 0
}, {
duration: 200
});
tween(symbol, {
alpha: 0
}, {
duration: 200
});
}
};
self.setMatched = function () {
self.isMatched = true;
self.canFlip = false;
// Add a subtle scale effect for matched cards
tween(self, {
scaleX: 1.1,
scaleY: 1.1
}, {
duration: 300,
easing: tween.easeOut
});
};
self.down = function (x, y, obj) {
if (self.canFlip && !self.isFlipped && !self.isMatched) {
handleCardTap(self);
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000000
});
/****
* Game Code
****/
// Game variables
// Game state variables
function _typeof(o) {
"@babel/helpers - typeof";
return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) {
return typeof o;
} : function (o) {
return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o;
}, _typeof(o);
}
var cards = [];
var flippedCards = [];
var moves = 0;
var gameCompleted = false;
var canFlipCards = true;
var gameOver = false;
var showMenu = true;
// Level and timing variables
var currentLevel = storage.currentLevel || 1;
var startTime = Date.now();
var gameStartTime = Date.now();
var LEVEL_TIME_LIMIT = 120; // 2 minutes in seconds
var MOVE_LIMIT = 15;
// Grid configuration
var GRID_COLS = 4;
var GRID_ROWS = 4;
var CARD_SIZE = 200;
var CARD_SPACING = 20;
// UI elements
var movesTxt = new Text2('Moves: 0', {
size: 60,
fill: 0x000000,
fontWeight: 'bold'
});
movesTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(movesTxt);
movesTxt.y = 100;
var timerTxt = new Text2('Time: 0:00', {
size: 60,
fill: 0x000000,
fontWeight: 'bold'
});
timerTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(timerTxt);
timerTxt.y = 180;
// Initialize storage with defaults to prevent undefined access errors
// Initialize coins from storage
var coins = storage.coins || 0;
var coinsTxt = new Text2('Coins: ' + coins, {
size: 60,
fill: 0xFFD700,
fontWeight: 'bold'
});
coinsTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(coinsTxt);
coinsTxt.y = 260;
// Initialize saved games from individual storage slots with validation
var savedGames = [];
for (var i = 0; i < 5; i++) {
var slotData = storage['savedGame' + i] || null;
// Validate slotData structure
if (slotData && _typeof(slotData) === 'object' && typeof slotData.level === 'number' && typeof slotData.coins === 'number') {
savedGames.push(slotData);
} else {
savedGames.push(null);
}
}
if (!Array.isArray(savedGames)) {
savedGames = [];
}
while (savedGames.length < 5) {
savedGames.push(null);
}
var levelTxt = new Text2('Level: ' + currentLevel, {
size: 60,
fill: 0x000000,
fontWeight: 'bold'
});
levelTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(levelTxt);
levelTxt.y = 340;
// Level completion notification text
var levelCompleteTxt = new Text2('LEVEL COMPLETE!', {
size: 100,
fill: 0x00FF00,
fontWeight: 'bold'
});
levelCompleteTxt.anchor.set(0.5, 0.5);
levelCompleteTxt.x = 2048 / 2;
levelCompleteTxt.y = 2732 / 2;
levelCompleteTxt.visible = false;
game.addChild(levelCompleteTxt);
// Power-up UI elements
var bombTxt = new Text2('Bomb: ' + (storage.bombCount || 0), {
size: 40,
fill: 0xFF6B6B,
fontWeight: 'bold'
});
bombTxt.anchor.set(0, 0);
LK.gui.topLeft.addChild(bombTxt);
bombTxt.x = 120;
bombTxt.y = 100;
var lightbulbTxt = new Text2('Hint: ' + (storage.hintCount || 0), {
size: 40,
fill: 0xFFD700,
fontWeight: 'bold'
});
lightbulbTxt.anchor.set(0, 0);
LK.gui.topLeft.addChild(lightbulbTxt);
lightbulbTxt.x = 120;
lightbulbTxt.y = 160;
var clockTxt = new Text2('Clock: ' + (storage.clockCount || 0), {
size: 40,
fill: 0x4ECDC4,
fontWeight: 'bold'
});
clockTxt.anchor.set(0, 0);
LK.gui.topLeft.addChild(clockTxt);
clockTxt.x = 120;
clockTxt.y = 220;
var timerPowerTxt = new Text2('Timer: ' + (storage.timerCount || 0), {
size: 40,
fill: 0x96CEB4,
fontWeight: 'bold'
});
timerPowerTxt.anchor.set(0, 0);
LK.gui.topLeft.addChild(timerPowerTxt);
timerPowerTxt.x = 120;
timerPowerTxt.y = 280;
var skipTxt = new Text2('Skip: ' + (storage.skipCount || 0), {
size: 40,
fill: 0xFF9FF3,
fontWeight: 'bold'
});
skipTxt.anchor.set(0, 0);
LK.gui.topLeft.addChild(skipTxt);
skipTxt.x = 120;
skipTxt.y = 340;
// Menu screen elements
var menuContainer = new Container();
game.addChild(menuContainer);
var titleTxt = new Text2('SPOOKY MEMORY MATCH', {
size: 120,
fill: 0xFF6600,
fontWeight: 'bold'
});
titleTxt.anchor.set(0.5, 0.5);
titleTxt.x = 2048 / 2;
titleTxt.y = 500;
menuContainer.addChild(titleTxt);
var levelInfoTxt = new Text2('Current Level: ' + currentLevel, {
size: 80,
fill: 0xFFD700,
fontWeight: 'bold'
});
levelInfoTxt.anchor.set(0.5, 0.5);
levelInfoTxt.x = 2048 / 2;
levelInfoTxt.y = 700;
menuContainer.addChild(levelInfoTxt);
var coinsInfoTxt = new Text2('Coins: ' + coins, {
size: 80,
fill: 0xFFD700,
fontWeight: 'bold'
});
coinsInfoTxt.anchor.set(0.5, 0.5);
coinsInfoTxt.x = 2048 / 2;
coinsInfoTxt.y = 850;
menuContainer.addChild(coinsInfoTxt);
var playBtn = new Text2('PLAY', {
size: 100,
fill: 0x4ECDC4,
fontWeight: 'bold'
});
playBtn.anchor.set(0.5, 0.5);
playBtn.x = 2048 / 2;
playBtn.y = 1050;
menuContainer.addChild(playBtn);
var instructionsTxt = new Text2('Match spooky pairs to advance levels!\nEarn coins to buy magical power-ups!', {
size: 60,
fill: 0xFF6600,
fontWeight: 'bold'
});
instructionsTxt.anchor.set(0.5, 0.5);
instructionsTxt.x = 2048 / 2;
instructionsTxt.y = 2200;
menuContainer.addChild(instructionsTxt);
var progressTxt = new Text2('Progress: ' + currentLevel + '/500', {
size: 70,
fill: 0x96CEB4,
fontWeight: 'bold'
});
progressTxt.anchor.set(0.5, 0.5);
progressTxt.x = 2048 / 2;
progressTxt.y = 2400;
menuContainer.addChild(progressTxt);
// Shop button for buying power-ups
var shopBtn = new Text2('SHOP', {
size: 100,
fill: 0xFFD700,
fontWeight: 'bold'
});
shopBtn.anchor.set(0.5, 0.5);
shopBtn.x = 2048 / 2;
shopBtn.y = 1250;
menuContainer.addChild(shopBtn);
// Shop screen elements
var shopContainer = new Container();
shopContainer.visible = false;
game.addChild(shopContainer);
var shopTitleTxt = new Text2('SHOP', {
size: 120,
fill: 0xFFD700,
fontWeight: 'bold'
});
shopTitleTxt.anchor.set(0.5, 0.5);
shopTitleTxt.x = 2048 / 2;
shopTitleTxt.y = 400;
shopContainer.addChild(shopTitleTxt);
var shopCoinsTxt = new Text2('Coins: ' + coins, {
size: 80,
fill: 0xFFD700,
fontWeight: 'bold'
});
shopCoinsTxt.anchor.set(0.5, 0.5);
shopCoinsTxt.x = 2048 / 2;
shopCoinsTxt.y = 500;
shopContainer.addChild(shopCoinsTxt);
// Tab buttons
var powerUpTabBtn = new Text2('POWER-UPS', {
size: 80,
fill: 0x4ECDC4,
fontWeight: 'bold'
});
powerUpTabBtn.anchor.set(0.5, 0.5);
powerUpTabBtn.x = 2048 / 2 - 200;
powerUpTabBtn.y = 600;
shopContainer.addChild(powerUpTabBtn);
var buildingTabBtn = new Text2('BUILDINGS', {
size: 80,
fill: 0x666666,
fontWeight: 'bold'
});
buildingTabBtn.anchor.set(0.5, 0.5);
buildingTabBtn.x = 2048 / 2 + 200;
buildingTabBtn.y = 600;
shopContainer.addChild(buildingTabBtn);
// Power-ups tab content
var powerUpTabContainer = new Container();
powerUpTabContainer.visible = true; // Start with power-ups tab visible
shopContainer.addChild(powerUpTabContainer);
var shopBombBtn = new Text2('BOMB - 10 Coins\n(Removes 6 random cards)', {
size: 60,
fill: 0xFF6B6B,
fontWeight: 'bold'
});
shopBombBtn.anchor.set(0.5, 0.5);
shopBombBtn.x = 2048 / 2;
shopBombBtn.y = 800;
powerUpTabContainer.addChild(shopBombBtn);
var shopHintBtn = new Text2('HINT - 15 Coins\n(Reveals a matching pair)', {
size: 60,
fill: 0xFFD700,
fontWeight: 'bold'
});
shopHintBtn.anchor.set(0.5, 0.5);
shopHintBtn.x = 2048 / 2;
shopHintBtn.y = 1000;
powerUpTabContainer.addChild(shopHintBtn);
var shopClockBtn = new Text2('CLOCK - 20 Coins\n(Adds 5 minutes)', {
size: 60,
fill: 0x4ECDC4,
fontWeight: 'bold'
});
shopClockBtn.anchor.set(0.5, 0.5);
shopClockBtn.x = 2048 / 2;
shopClockBtn.y = 1200;
powerUpTabContainer.addChild(shopClockBtn);
var shopTimerBtn = new Text2('TIMER - 5 Coins\n(Adds 1 minute)', {
size: 60,
fill: 0x96CEB4,
fontWeight: 'bold'
});
shopTimerBtn.anchor.set(0.5, 0.5);
shopTimerBtn.x = 2048 / 2;
shopTimerBtn.y = 1400;
powerUpTabContainer.addChild(shopTimerBtn);
var shopSkipBtn = new Text2('SKIP - 100 Coins\n(Skip current level)', {
size: 60,
fill: 0xFF9FF3,
fontWeight: 'bold'
});
shopSkipBtn.anchor.set(0.5, 0.5);
shopSkipBtn.x = 2048 / 2;
shopSkipBtn.y = 1600;
powerUpTabContainer.addChild(shopSkipBtn);
// Buildings tab content
var buildingTabContainer = new Container();
buildingTabContainer.visible = false;
shopContainer.addChild(buildingTabContainer);
var hauntedMansionBtn = new Text2('HAUNTED MANSION - 50 Coins\n(Spooky decoration)', {
size: 60,
fill: 0x4B0082,
fontWeight: 'bold'
});
hauntedMansionBtn.anchor.set(0.5, 0.5);
hauntedMansionBtn.x = 2048 / 2;
hauntedMansionBtn.y = 800;
buildingTabContainer.addChild(hauntedMansionBtn);
var witchHutBtn = new Text2('WITCH HUT - 75 Coins\n(Magical dwelling)', {
size: 60,
fill: 0x228B22,
fontWeight: 'bold'
});
witchHutBtn.anchor.set(0.5, 0.5);
witchHutBtn.x = 2048 / 2;
witchHutBtn.y = 950;
buildingTabContainer.addChild(witchHutBtn);
var pumpkinPatchBtn = new Text2('PUMPKIN PATCH - 30 Coins\n(Halloween decoration)', {
size: 60,
fill: 0xFF8C00,
fontWeight: 'bold'
});
pumpkinPatchBtn.anchor.set(0.5, 0.5);
pumpkinPatchBtn.x = 2048 / 2;
pumpkinPatchBtn.y = 1100;
buildingTabContainer.addChild(pumpkinPatchBtn);
var ghostlyTowerBtn = new Text2('GHOSTLY TOWER - 100 Coins\n(Spectral building)', {
size: 60,
fill: 0xF5F5F5,
fontWeight: 'bold'
});
ghostlyTowerBtn.anchor.set(0.5, 0.5);
ghostlyTowerBtn.x = 2048 / 2;
ghostlyTowerBtn.y = 1250;
buildingTabContainer.addChild(ghostlyTowerBtn);
var vampireCastleBtn = new Text2('VAMPIRE CASTLE - 200 Coins\n(Dark fortress)', {
size: 60,
fill: 0x8B0000,
fontWeight: 'bold'
});
vampireCastleBtn.anchor.set(0.5, 0.5);
vampireCastleBtn.x = 2048 / 2;
vampireCastleBtn.y = 1400;
buildingTabContainer.addChild(vampireCastleBtn);
// NPC characters
var friendlyGhostBtn = new Text2('FRIENDLY GHOST - 40 Coins\n(Helpful NPC)', {
size: 60,
fill: 0xD3D3D3,
fontWeight: 'bold'
});
friendlyGhostBtn.anchor.set(0.5, 0.5);
friendlyGhostBtn.x = 2048 / 2;
friendlyGhostBtn.y = 1550;
buildingTabContainer.addChild(friendlyGhostBtn);
var wizardBtn = new Text2('WIZARD - 60 Coins\n(Wise NPC)', {
size: 60,
fill: 0x191970,
fontWeight: 'bold'
});
wizardBtn.anchor.set(0.5, 0.5);
wizardBtn.x = 2048 / 2;
wizardBtn.y = 1700;
buildingTabContainer.addChild(wizardBtn);
var blackCatBtn = new Text2('BLACK CAT - 25 Coins\n(Mystical companion)', {
size: 60,
fill: 0x000000,
fontWeight: 'bold'
});
blackCatBtn.anchor.set(0.5, 0.5);
blackCatBtn.x = 2048 / 2;
blackCatBtn.y = 1850;
buildingTabContainer.addChild(blackCatBtn);
var shopBackBtn = new Text2('BACK TO MENU', {
size: 80,
fill: 0xCCCCCC,
fontWeight: 'bold'
});
shopBackBtn.anchor.set(0.5, 0.5);
shopBackBtn.x = 2048 / 2;
shopBackBtn.y = 2000;
shopContainer.addChild(shopBackBtn);
// Saved Games button
var savedGamesBtn = new Text2('SAVED GAMES', {
size: 100,
fill: 0x96CEB4,
fontWeight: 'bold'
});
savedGamesBtn.anchor.set(0.5, 0.5);
savedGamesBtn.x = 2048 / 2;
savedGamesBtn.y = 1450;
menuContainer.addChild(savedGamesBtn);
// World button
var worldBtn = new Container();
worldBtn.x = 2048 / 2;
worldBtn.y = 1650;
// Add background image
var worldBackground = LK.getAsset('worldbackground', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2,
scaleY: 2
});
worldBtn.addChild(worldBackground);
// Add text on top of background
var worldBtnText = new Text2('WORLD', {
size: 100,
fill: 0x4B0082,
fontWeight: 'bold'
});
worldBtnText.anchor.set(0.5, 0.5);
worldBtn.addChild(worldBtnText);
menuContainer.addChild(worldBtn);
// Saved Games screen container
var savedGamesContainer = new Container();
savedGamesContainer.visible = false;
game.addChild(savedGamesContainer);
var savedGamesTitleTxt = new Text2('SAVED GAMES', {
size: 120,
fill: 0x96CEB4,
fontWeight: 'bold'
});
savedGamesTitleTxt.anchor.set(0.5, 0.5);
savedGamesTitleTxt.x = 2048 / 2;
savedGamesTitleTxt.y = 400;
savedGamesContainer.addChild(savedGamesTitleTxt);
var savedGamesInstructionTxt = new Text2('Click a slot to load or save your game progress', {
size: 60,
fill: 0x000000,
fontWeight: 'bold'
});
savedGamesInstructionTxt.anchor.set(0.5, 0.5);
savedGamesInstructionTxt.x = 2048 / 2;
savedGamesInstructionTxt.y = 550;
savedGamesContainer.addChild(savedGamesInstructionTxt);
// Create 5 save game slot buttons in saved games container
var saveSlots = [];
for (var i = 0; i < 5; i++) {
var slotData = savedGames[i];
var slotText = 'Slot ' + (i + 1);
if (slotData) {
slotText += ' - Level ' + slotData.level + ' (' + slotData.coins + ' coins)';
if (slotData.result) {
slotText += ' - ' + slotData.result;
}
} else {
slotText += ' - Empty';
}
var slotBtn = new Text2(slotText, {
size: 60,
fill: slotData ? 0x4ECDC4 : 0x666666,
fontWeight: 'bold'
});
slotBtn.anchor.set(0.5, 0.5);
slotBtn.x = 2048 / 2;
slotBtn.y = 750 + i * 150;
slotBtn.slotIndex = i;
savedGamesContainer.addChild(slotBtn);
saveSlots.push(slotBtn);
}
// Back to menu button for saved games
var savedGamesBackBtn = new Text2('BACK TO MENU', {
size: 80,
fill: 0xCCCCCC,
fontWeight: 'bold'
});
savedGamesBackBtn.anchor.set(0.5, 0.5);
savedGamesBackBtn.x = 2048 / 2;
savedGamesBackBtn.y = 1500;
savedGamesContainer.addChild(savedGamesBackBtn);
// Create card types array based on level (4 tiles for first level + 6 tiles per additional level)
var totalTiles;
if (currentLevel === 1) {
totalTiles = 4; // First level has exactly 4 tiles
} else {
totalTiles = 4 + (currentLevel - 1) * 6; // 4 base tiles + 6 per level
}
var pairCount = totalTiles / 2;
var cardTypes = [];
for (var i = 1; i <= pairCount; i++) {
// Ensure symbol index stays within valid range (1-25)
var symbolIndex = (i - 1) % 25 + 1; // Cycle through 25 available symbols
cardTypes.push(symbolIndex);
cardTypes.push(symbolIndex); // Add pair
}
// Shuffle function
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;
}
// Initialize cards
function initializeCards() {
var shuffledTypes = shuffleArray(cardTypes);
// Calculate dynamic grid size based on total tiles
var totalTiles = cardTypes.length;
GRID_COLS = Math.ceil(Math.sqrt(totalTiles));
GRID_ROWS = Math.ceil(totalTiles / GRID_COLS);
// Adjust card size based on grid size
if (totalTiles > 16) {
CARD_SIZE = Math.max(120, 200 - (totalTiles - 16) * 5);
CARD_SPACING = Math.max(10, 20 - (totalTiles - 16) * 1);
}
// Calculate grid positioning
var gridWidth = GRID_COLS * CARD_SIZE + (GRID_COLS - 1) * CARD_SPACING;
var gridHeight = GRID_ROWS * CARD_SIZE + (GRID_ROWS - 1) * CARD_SPACING;
var startX = (2048 - gridWidth) / 2 + CARD_SIZE / 2;
var startY = (2732 - gridHeight) / 2 + CARD_SIZE / 2;
for (var i = 0; i < totalTiles; i++) {
var row = Math.floor(i / GRID_COLS);
var col = i % GRID_COLS;
var card = new Card(shuffledTypes[i]);
card.x = startX + col * (CARD_SIZE + CARD_SPACING);
card.y = startY + row * (CARD_SIZE + CARD_SPACING);
cards.push(card);
game.addChild(card);
}
}
// Handle card tap
function handleCardTap(card) {
if (!canFlipCards || flippedCards.length >= 2 || gameOver) {
return;
}
card.flip();
flippedCards.push(card);
if (flippedCards.length === 2) {
moves++;
movesTxt.setText('Moves: ' + moves);
// Check if move limit exceeded
if (moves >= MOVE_LIMIT) {
gameOver = true;
// Save current level progress before game over
storage.currentLevel = currentLevel;
// Auto-save failed attempt
autoSaveGame('Failed - Move Limit');
LK.setTimeout(function () {
LK.showGameOver();
}, 1000);
return;
}
canFlipCards = false;
// Check for match after a short delay
LK.setTimeout(function () {
checkForMatch();
}, 1000);
}
}
// Check if two flipped cards match
function checkForMatch() {
var card1 = flippedCards[0];
var card2 = flippedCards[1];
if (card1.symbolType === card2.symbolType) {
// Match found
LK.getSound('match').play();
card1.setMatched();
card2.setMatched();
// Award 5 coins for matching pair
coins += 5;
storage.coins = coins;
coinsTxt.setText('Coins: ' + coins);
// Check if game is complete
var matchedCount = 0;
for (var i = 0; i < cards.length; i++) {
if (cards[i] && typeof cards[i].isMatched !== 'undefined' && cards[i].isMatched) {
matchedCount++;
}
}
if (matchedCount === cards.length) {
gameCompleted = true;
// Save current level completion
storage.currentLevel = currentLevel;
// Auto-save completed level
autoSaveGame('Completed');
// Award bonus coins for completing level
coins += 10;
storage.coins = coins;
coinsTxt.setText('Coins: ' + coins);
// Check if we've completed all 500 levels
if (currentLevel >= 500) {
// Show you win when all levels are completed
LK.setTimeout(function () {
LK.showYouWin();
}, 1000);
} else {
// Advance to next level
currentLevel++;
storage.currentLevel = currentLevel; // Save current level to storage
levelTxt.setText('Level: ' + currentLevel);
// Update menu progress display
levelInfoTxt.setText('Current Level: ' + currentLevel);
progressTxt.setText('Progress: ' + currentLevel + '/500');
// Show level completion message
LK.effects.flashScreen(0x00FF00, 1000);
// Reset for next level
LK.setTimeout(function () {
resetLevel();
}, 1500);
}
}
} else {
// No match
LK.getSound('noMatch').play();
card1.flip();
card2.flip();
}
flippedCards = [];
canFlipCards = true;
}
// Reset level for progression
function resetLevel() {
// Show level completion message briefly if advancing
if (currentLevel > 1) {
levelCompleteTxt.setText('LEVEL ' + (currentLevel - 1) + ' COMPLETE!');
levelCompleteTxt.visible = true;
LK.setTimeout(function () {
levelCompleteTxt.visible = false;
}, 2000);
}
// Clear existing cards
for (var i = 0; i < cards.length; i++) {
if (cards[i] && typeof cards[i].destroy === 'function') {
cards[i].destroy();
}
}
cards = [];
flippedCards = [];
moves = 0;
gameCompleted = false;
canFlipCards = true;
gameOver = false;
gameStartTime = Date.now();
startTime = Date.now();
// Update UI
movesTxt.setText('Moves: 0');
timerTxt.setText('Time: 0:00');
// Recreate card types for new level
var totalTiles;
if (currentLevel === 1) {
totalTiles = 4; // First level has exactly 4 tiles
} else {
totalTiles = 4 + (currentLevel - 1) * 6; // 4 base tiles + 6 per level
}
var pairCount = totalTiles / 2;
cardTypes = [];
for (var i = 1; i <= pairCount; i++) {
// Ensure symbol index stays within valid range (1-25)
var symbolIndex = (i - 1) % 25 + 1; // Cycle through 25 available symbols
cardTypes.push(symbolIndex);
cardTypes.push(symbolIndex);
}
// Initialize new cards
initializeCards();
}
// Format time display
function formatTime(seconds) {
var minutes = Math.floor(seconds / 60);
var remainingSeconds = seconds % 60;
return minutes + ':' + (remainingSeconds < 10 ? '0' : '') + remainingSeconds;
}
// Building purchase functions
function buyHauntedMansion() {
if (coins >= 50) {
coins = Math.max(0, coins - 50);
storage.coins = coins;
shopCoinsTxt.setText('Coins: ' + coins);
coinsInfoTxt.setText('Coins: ' + coins);
var mansionCount = storage.mansionCount || 0;
storage.mansionCount = mansionCount + 1;
LK.effects.flashScreen(0x4B0082, 500);
}
}
function buyWitchHut() {
if (coins >= 75) {
coins = Math.max(0, coins - 75);
storage.coins = coins;
shopCoinsTxt.setText('Coins: ' + coins);
coinsInfoTxt.setText('Coins: ' + coins);
var hutCount = storage.hutCount || 0;
storage.hutCount = hutCount + 1;
LK.effects.flashScreen(0x228B22, 500);
}
}
function buyPumpkinPatch() {
if (coins >= 30) {
coins = Math.max(0, coins - 30);
storage.coins = coins;
shopCoinsTxt.setText('Coins: ' + coins);
coinsInfoTxt.setText('Coins: ' + coins);
var patchCount = storage.patchCount || 0;
storage.patchCount = patchCount + 1;
LK.effects.flashScreen(0xFF8C00, 500);
}
}
function buyGhostlyTower() {
if (coins >= 100) {
coins = Math.max(0, coins - 100);
storage.coins = coins;
shopCoinsTxt.setText('Coins: ' + coins);
coinsInfoTxt.setText('Coins: ' + coins);
var towerCount = storage.towerCount || 0;
storage.towerCount = towerCount + 1;
LK.effects.flashScreen(0xF5F5F5, 500);
}
}
function buyVampireCastle() {
if (coins >= 200) {
coins = Math.max(0, coins - 200);
storage.coins = coins;
shopCoinsTxt.setText('Coins: ' + coins);
coinsInfoTxt.setText('Coins: ' + coins);
var castleCount = storage.castleCount || 0;
storage.castleCount = castleCount + 1;
LK.effects.flashScreen(0x8B0000, 500);
}
}
function buyFriendlyGhost() {
if (coins >= 40) {
coins = Math.max(0, coins - 40);
storage.coins = coins;
shopCoinsTxt.setText('Coins: ' + coins);
coinsInfoTxt.setText('Coins: ' + coins);
var ghostCount = storage.ghostCount || 0;
storage.ghostCount = ghostCount + 1;
LK.effects.flashScreen(0xD3D3D3, 500);
}
}
function buyWizard() {
if (coins >= 60) {
coins = Math.max(0, coins - 60);
storage.coins = coins;
shopCoinsTxt.setText('Coins: ' + coins);
coinsInfoTxt.setText('Coins: ' + coins);
var wizardCount = storage.wizardCount || 0;
storage.wizardCount = wizardCount + 1;
LK.effects.flashScreen(0x191970, 500);
}
}
function buyBlackCat() {
if (coins >= 25) {
coins = Math.max(0, coins - 25);
storage.coins = coins;
shopCoinsTxt.setText('Coins: ' + coins);
coinsInfoTxt.setText('Coins: ' + coins);
var catCount = storage.catCount || 0;
storage.catCount = catCount + 1;
LK.effects.flashScreen(0x000000, 500);
}
}
// Shop power-up purchase functions
function buyBomb() {
if (coins >= 10) {
coins = Math.max(0, coins - 10);
storage.coins = coins;
shopCoinsTxt.setText('Coins: ' + coins);
coinsInfoTxt.setText('Coins: ' + coins);
// Add bomb to inventory (stored in storage)
var bombCount = storage.bombCount || 0;
storage.bombCount = bombCount + 1;
bombTxt.setText('Bomb: ' + (storage.bombCount || 0));
LK.effects.flashScreen(0x00FF00, 500);
}
}
function buyHint() {
if (coins >= 15) {
coins = Math.max(0, coins - 15);
storage.coins = coins;
shopCoinsTxt.setText('Coins: ' + coins);
coinsInfoTxt.setText('Coins: ' + coins);
// Add hint to inventory
var hintCount = storage.hintCount || 0;
storage.hintCount = hintCount + 1;
lightbulbTxt.setText('Hint: ' + (storage.hintCount || 0));
LK.effects.flashScreen(0x00FF00, 500);
}
}
function buyClock() {
if (coins >= 20) {
coins = Math.max(0, coins - 20);
storage.coins = coins;
shopCoinsTxt.setText('Coins: ' + coins);
coinsInfoTxt.setText('Coins: ' + coins);
// Add clock to inventory
var clockCount = storage.clockCount || 0;
storage.clockCount = clockCount + 1;
clockTxt.setText('Clock: ' + (storage.clockCount || 0));
LK.effects.flashScreen(0x00FF00, 500);
}
}
function buyTimer() {
if (coins >= 5) {
coins = Math.max(0, coins - 5);
storage.coins = coins;
shopCoinsTxt.setText('Coins: ' + coins);
coinsInfoTxt.setText('Coins: ' + coins);
// Add timer to inventory
var timerCount = storage.timerCount || 0;
storage.timerCount = timerCount + 1;
timerPowerTxt.setText('Timer: ' + (storage.timerCount || 0));
LK.effects.flashScreen(0x00FF00, 500);
}
}
function buySkip() {
if (coins >= 100) {
coins = Math.max(0, coins - 100);
storage.coins = coins;
shopCoinsTxt.setText('Coins: ' + coins);
coinsInfoTxt.setText('Coins: ' + coins);
// Add skip to inventory
var skipCount = storage.skipCount || 0;
storage.skipCount = skipCount + 1;
skipTxt.setText('Skip: ' + (storage.skipCount || 0));
LK.effects.flashScreen(0x00FF00, 500);
}
}
// Power-up functions
function useBomb() {
var bombCount = storage.bombCount || 0;
if (bombCount > 0) {
storage.bombCount = bombCount - 1;
// Update display
bombTxt.setText('Bomb: ' + (storage.bombCount || 0));
// Delete 6 random non-matched cards
var availableCards = [];
for (var i = 0; i < cards.length; i++) {
if (cards[i] && typeof cards[i].isMatched !== 'undefined' && !cards[i].isMatched) {
availableCards.push(cards[i]);
}
}
var cardsToRemove = Math.min(6, availableCards.length);
for (var i = 0; i < cardsToRemove; i++) {
if (availableCards.length > 0) {
var randomIndex = Math.floor(Math.random() * availableCards.length);
var cardToRemove = availableCards[randomIndex];
if (cardToRemove && cardToRemove.destroy) {
cardToRemove.destroy();
var cardIndex = cards.indexOf(cardToRemove);
if (cardIndex !== -1) {
cards.splice(cardIndex, 1);
}
availableCards.splice(randomIndex, 1);
}
}
}
}
}
function useLightbulb() {
var hintCount = storage.hintCount || 0;
if (hintCount > 0) {
storage.hintCount = hintCount - 1;
// Update display
lightbulbTxt.setText('Hint: ' + (storage.hintCount || 0));
// Find a matching pair that's not matched yet
var unmatchedCards = [];
for (var i = 0; i < cards.length; i++) {
if (cards[i] && typeof cards[i].isMatched !== 'undefined' && !cards[i].isMatched) {
unmatchedCards.push(cards[i]);
}
}
// Group by symbol type
var symbolGroups = {};
for (var i = 0; i < unmatchedCards.length; i++) {
var card = unmatchedCards[i];
if (!symbolGroups[card.symbolType]) {
symbolGroups[card.symbolType] = [];
}
symbolGroups[card.symbolType].push(card);
}
// Find first pair and show them
for (var symbolType in symbolGroups) {
if (symbolGroups[symbolType].length >= 2) {
var pair = symbolGroups[symbolType].slice(0, 2);
for (var i = 0; i < pair.length; i++) {
if (!pair[i].isFlipped) {
pair[i].flip();
}
}
break;
}
}
}
}
function useClock() {
var clockCount = storage.clockCount || 0;
if (clockCount > 0) {
storage.clockCount = clockCount - 1;
// Update display
clockTxt.setText('Clock: ' + (storage.clockCount || 0));
// Add 5 minutes (300 seconds) to game time
gameStartTime -= 300000; // Subtract 5 minutes from start time to effectively add time
}
}
function useTimer() {
var timerCount = storage.timerCount || 0;
if (timerCount > 0) {
storage.timerCount = timerCount - 1;
// Update display
timerPowerTxt.setText('Timer: ' + (storage.timerCount || 0));
// Add 1 minute (60 seconds) to game time
gameStartTime -= 60000; // Subtract 1 minute from start time to effectively add time
}
}
function useSkip() {
var skipCount = storage.skipCount || 0;
if (skipCount > 0) {
storage.skipCount = skipCount - 1;
// Update display
skipTxt.setText('Skip: ' + (storage.skipCount || 0));
// Save current level before skipping
storage.currentLevel = currentLevel;
// Check if we've completed all 500 levels
if (currentLevel >= 500) {
// Show you win when all levels are completed
LK.setTimeout(function () {
LK.showYouWin();
}, 1000);
} else {
// Advance to next level
currentLevel++;
storage.currentLevel = currentLevel; // Save current level to storage
levelTxt.setText('Level: ' + currentLevel);
// Reset for next level
LK.setTimeout(function () {
resetLevel();
}, 1000);
}
}
}
// Function to save current game state
function saveGame(slotIndex) {
// Create flattened gameData object with only literals
var gameData = {
level: currentLevel,
coins: coins,
timestamp: Date.now()
};
// Ensure savedGames is initialized as an array
if (!Array.isArray(savedGames)) {
savedGames = [];
}
// Ensure we have enough slots
while (savedGames.length <= slotIndex) {
savedGames.push(null);
}
savedGames[slotIndex] = gameData;
storage['savedGame' + slotIndex] = gameData;
// Update the slot button text only if saveSlots exists and has the slot
if (saveSlots && saveSlots[slotIndex]) {
var slotBtn = saveSlots[slotIndex];
slotBtn.setText('Slot ' + (slotIndex + 1) + ' - Level ' + gameData.level + ' (' + gameData.coins + ' coins)');
slotBtn.tint = 0x4ECDC4;
}
}
// Function to auto-save to circular buffer of latest 5 games
function autoSaveGame(gameResult) {
// Create flattened gameData object with only literals
var gameData = {
level: currentLevel,
coins: coins,
timestamp: Date.now(),
result: gameResult
};
// Ensure savedGames is initialized as an array
if (!Array.isArray(savedGames)) {
savedGames = [];
}
// Create a new array to avoid reference issues
var newSavedGames = [];
// Add new game to the beginning
newSavedGames.push(gameData);
// Add existing games (up to 4 more)
for (var i = 0; i < Math.min(4, savedGames.length); i++) {
if (savedGames[i]) {
newSavedGames.push(savedGames[i]);
}
}
savedGames = newSavedGames;
// Keep only the latest 5 games
if (savedGames.length > 5) {
savedGames = savedGames.slice(0, 5);
}
// Save each slot individually to storage with only literals
for (var i = 0; i < 5; i++) {
if (i < savedGames.length && savedGames[i]) {
storage['savedGame' + i] = {
level: savedGames[i].level,
coins: savedGames[i].coins,
timestamp: savedGames[i].timestamp,
result: savedGames[i].result || ''
};
} else {
storage['savedGame' + i] = null;
}
}
// Update all slot buttons if they exist
if (saveSlots) {
for (var i = 0; i < 5; i++) {
if (saveSlots[i]) {
var slotData = savedGames[i];
var slotText = 'Slot ' + (i + 1);
if (slotData) {
slotText += ' - Level ' + slotData.level + ' (' + slotData.coins + ' coins)';
if (slotData.result) {
slotText += ' - ' + slotData.result;
}
} else {
slotText += ' - Empty';
}
saveSlots[i].setText(slotText);
saveSlots[i].tint = slotData ? 0x4ECDC4 : 0x666666;
}
}
}
}
// Function to load saved game
function loadGame(slotIndex) {
if (slotIndex >= 0 && slotIndex < savedGames.length && savedGames[slotIndex]) {
var gameData = savedGames[slotIndex];
if (gameData && typeof gameData.level === 'number' && typeof gameData.coins === 'number') {
currentLevel = Math.max(1, Math.min(500, gameData.level));
coins = Math.max(0, gameData.coins);
storage.currentLevel = currentLevel;
storage.coins = coins;
// Update UI
levelInfoTxt.setText('Current Level: ' + currentLevel);
coinsInfoTxt.setText('Coins: ' + coins);
progressTxt.setText('Progress: ' + currentLevel + '/500');
}
}
}
// Function to update power-up inventory display
function updatePowerUpDisplay() {
bombTxt.setText('Bomb: ' + (storage.bombCount || 0));
lightbulbTxt.setText('Hint: ' + (storage.hintCount || 0));
clockTxt.setText('Clock: ' + (storage.clockCount || 0));
timerPowerTxt.setText('Timer: ' + (storage.timerCount || 0));
skipTxt.setText('Skip: ' + (storage.skipCount || 0));
}
// Function to start the game
function startGame() {
showMenu = false;
menuContainer.visible = false;
// Show game UI elements
movesTxt.visible = true;
timerTxt.visible = true;
coinsTxt.visible = true;
levelTxt.visible = true;
bombTxt.visible = true;
lightbulbTxt.visible = true;
clockTxt.visible = true;
timerPowerTxt.visible = true;
skipTxt.visible = true;
playBackBtn.visible = true;
// Update power-up display
updatePowerUpDisplay();
// Initialize game
initializeCards();
gameStartTime = Date.now();
startTime = Date.now();
}
// Add click handlers for power-ups
bombTxt.down = function (x, y, obj) {
useBomb();
};
lightbulbTxt.down = function (x, y, obj) {
useLightbulb();
};
clockTxt.down = function (x, y, obj) {
useClock();
};
timerPowerTxt.down = function (x, y, obj) {
useTimer();
};
skipTxt.down = function (x, y, obj) {
useSkip();
};
// Saved Games button click handler
savedGamesBtn.down = function (x, y, obj) {
menuContainer.visible = false;
savedGamesContainer.visible = true;
// Update saved games display
for (var i = 0; i < saveSlots.length; i++) {
var slotData = savedGames[i];
var slotText = 'Slot ' + (i + 1);
if (slotData) {
slotText += ' - Level ' + slotData.level + ' (' + slotData.coins + ' coins)';
if (slotData.result) {
slotText += ' - ' + slotData.result;
}
} else {
slotText += ' - Empty';
}
saveSlots[i].setText(slotText);
saveSlots[i].tint = slotData ? 0x4ECDC4 : 0x666666;
}
};
// World screen container
var worldContainer = new Container();
worldContainer.visible = false;
// Add worldbackground as background
var worldContainerBackground = LK.getAsset('worldbackground', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 20,
scaleY: 27
});
worldContainerBackground.x = 2048 / 2;
worldContainerBackground.y = 2732 / 2;
worldContainer.addChild(worldContainerBackground);
game.addChild(worldContainer);
var worldTitleTxt = new Text2('YOUR SPOOKY WORLD', {
size: 120,
fill: 0x4B0082,
fontWeight: 'bold'
});
worldTitleTxt.anchor.set(0.5, 0.5);
worldTitleTxt.x = 2048 / 2;
worldTitleTxt.y = 400;
worldContainer.addChild(worldTitleTxt);
var worldInstructionTxt = new Text2('Drag buildings to place them in your world', {
size: 60,
fill: 0x000000,
fontWeight: 'bold'
});
worldInstructionTxt.anchor.set(0.5, 0.5);
worldInstructionTxt.x = 2048 / 2;
worldInstructionTxt.y = 500;
worldContainer.addChild(worldInstructionTxt);
// World back button
var worldBackBtn = new Text2('BACK TO MENU', {
size: 80,
fill: 0xCCCCCC,
fontWeight: 'bold'
});
worldBackBtn.anchor.set(0.5, 0.5);
worldBackBtn.x = 2048 / 2;
worldBackBtn.y = 2400;
worldContainer.addChild(worldBackBtn);
// Array to store placed buildings
var placedBuildings = [];
// World button click handler
worldBtn.down = function (x, y, obj) {
menuContainer.visible = false;
worldContainer.visible = true;
createWorldBuildings();
};
// World back button handler
worldBackBtn.down = function (x, y, obj) {
worldContainer.visible = false;
menuContainer.visible = true;
saveWorldState();
};
// Function to create world buildings based on purchased items
function createWorldBuildings() {
// Clear existing buildings
for (var i = 0; i < placedBuildings.length; i++) {
placedBuildings[i].destroy();
}
placedBuildings = [];
// Load saved positions from flattened storage structure
var savedPositions = {};
for (var i = 0; i < placedBuildings.length; i++) {
var building = placedBuildings[i];
var x = storage['buildingPos_' + building.buildingId + '_x'];
var y = storage['buildingPos_' + building.buildingId + '_y'];
if (x !== undefined && y !== undefined) {
savedPositions[building.buildingId] = {
x: x,
y: y
};
}
}
var buildingY = 700;
var buildingX = 150;
var buildingSpacing = 300;
// Create buildings based on purchased counts
var mansionCount = storage.mansionCount || 0;
for (var i = 0; i < mansionCount; i++) {
var mansion = createBuilding('Haunted Mansion', 0x4B0082, buildingX, buildingY, 'mansion_' + i);
placedBuildings.push(mansion);
worldContainer.addChild(mansion);
buildingX += buildingSpacing;
if (buildingX > 1800) {
buildingX = 150;
buildingY += 250;
}
}
var hutCount = storage.hutCount || 0;
for (var i = 0; i < hutCount; i++) {
var hut = createBuilding('Witch Hut', 0x228B22, buildingX, buildingY, 'hut_' + i);
placedBuildings.push(hut);
worldContainer.addChild(hut);
buildingX += buildingSpacing;
if (buildingX > 1800) {
buildingX = 150;
buildingY += 250;
}
}
var patchCount = storage.patchCount || 0;
for (var i = 0; i < patchCount; i++) {
var patch = createBuilding('Pumpkin Patch', 0xFF8C00, buildingX, buildingY, 'patch_' + i);
placedBuildings.push(patch);
worldContainer.addChild(patch);
buildingX += buildingSpacing;
if (buildingX > 1800) {
buildingX = 150;
buildingY += 250;
}
}
var towerCount = storage.towerCount || 0;
for (var i = 0; i < towerCount; i++) {
var tower = createBuilding('Ghostly Tower', 0xF5F5F5, buildingX, buildingY, 'tower_' + i);
placedBuildings.push(tower);
worldContainer.addChild(tower);
buildingX += buildingSpacing;
if (buildingX > 1800) {
buildingX = 150;
buildingY += 250;
}
}
var castleCount = storage.castleCount || 0;
for (var i = 0; i < castleCount; i++) {
var castle = createBuilding('Vampire Castle', 0x8B0000, buildingX, buildingY, 'castle_' + i);
placedBuildings.push(castle);
worldContainer.addChild(castle);
buildingX += buildingSpacing;
if (buildingX > 1800) {
buildingX = 150;
buildingY += 250;
}
}
var ghostCount = storage.ghostCount || 0;
for (var i = 0; i < ghostCount; i++) {
var ghost = createBuilding('Friendly Ghost', 0xD3D3D3, buildingX, buildingY, 'ghost_' + i);
placedBuildings.push(ghost);
worldContainer.addChild(ghost);
buildingX += buildingSpacing;
if (buildingX > 1800) {
buildingX = 150;
buildingY += 250;
}
}
var wizardCount = storage.wizardCount || 0;
for (var i = 0; i < wizardCount; i++) {
var wizard = createBuilding('Wizard', 0x191970, buildingX, buildingY, 'wizard_' + i);
placedBuildings.push(wizard);
worldContainer.addChild(wizard);
buildingX += buildingSpacing;
if (buildingX > 1800) {
buildingX = 150;
buildingY += 250;
}
}
var catCount = storage.catCount || 0;
for (var i = 0; i < catCount; i++) {
var cat = createBuilding('Black Cat', 0x000000, buildingX, buildingY, 'cat_' + i);
placedBuildings.push(cat);
worldContainer.addChild(cat);
buildingX += buildingSpacing;
if (buildingX > 1800) {
buildingX = 150;
buildingY += 250;
}
}
// Apply saved positions
for (var i = 0; i < placedBuildings.length; i++) {
var building = placedBuildings[i];
if (savedPositions[building.buildingId]) {
building.x = savedPositions[building.buildingId].x;
building.y = savedPositions[building.buildingId].y;
}
}
}
// Function to create a draggable building
function createBuilding(name, color, x, y, buildingId) {
var building = new Container();
building.buildingId = buildingId;
// Create building visual - use appropriate asset based on building type
var assetName = 'hauntedMansion'; // default fallback
if (name === 'Haunted Mansion') assetName = 'hauntedMansion';else if (name === 'Witch Hut') assetName = 'witchHut';else if (name === 'Pumpkin Patch') assetName = 'pumpkinPatch';else if (name === 'Ghostly Tower') assetName = 'ghostlyTower';else if (name === 'Vampire Castle') assetName = 'vampireCastle';else if (name === 'Friendly Ghost') assetName = 'friendlyGhost';else if (name === 'Wizard') assetName = 'wizard';else if (name === 'Black Cat') assetName = 'blackCat';
var buildingShape = LK.getAsset(assetName, {
anchorX: 0.5,
anchorY: 0.5
});
building.addChild(buildingShape);
// Create building label
var buildingLabel = new Text2(name, {
size: 30,
fill: 0x000000,
fontWeight: 'bold'
});
buildingLabel.anchor.set(0.5, 0.5);
buildingLabel.y = 100;
building.addChild(buildingLabel);
building.x = x;
building.y = y;
// Make building draggable
building.isDragging = false;
building.down = function (x, y, obj) {
if (building && typeof building.x !== 'undefined' && typeof building.y !== 'undefined') {
building.isDragging = true;
// Convert coordinates to local space for proper offset calculation
var localPos = worldContainer.toLocal({
x: x,
y: y
});
building.dragOffsetX = localPos.x - building.x;
building.dragOffsetY = localPos.y - building.y;
}
};
building.up = function (x, y, obj) {
building.isDragging = false;
};
return building;
}
// Function to save world state
function saveWorldState() {
var positions = {};
for (var i = 0; i < placedBuildings.length; i++) {
var building = placedBuildings[i];
positions[building.buildingId] = {
x: building.x,
y: building.y
};
}
// Store positions as individual key-value pairs to avoid object nesting issues
for (var buildingId in positions) {
storage['buildingPos_' + buildingId + '_x'] = positions[buildingId].x;
storage['buildingPos_' + buildingId + '_y'] = positions[buildingId].y;
}
}
// Handle building dragging
var draggedBuilding = null;
// World container mouse handlers
worldContainer.move = function (x, y, obj) {
for (var i = 0; i < placedBuildings.length; i++) {
var building = placedBuildings[i];
if (building && building.isDragging) {
// Convert coordinates to world container local space
var localPos = worldContainer.toLocal({
x: x,
y: y
});
// Keep buildings within world bounds
var newX = Math.max(100, Math.min(1948, localPos.x - (building.dragOffsetX || 0)));
var newY = Math.max(600, Math.min(2300, localPos.y - (building.dragOffsetY || 0)));
building.x = newX;
building.y = newY;
break;
}
}
};
worldContainer.up = function (x, y, obj) {
for (var i = 0; i < placedBuildings.length; i++) {
var building = placedBuildings[i];
if (building.isDragging) {
building.isDragging = false;
break;
}
}
};
// Saved Games back button click handler
savedGamesBackBtn.down = function (x, y, obj) {
savedGamesContainer.visible = false;
menuContainer.visible = true;
};
// Save game slot click handlers
for (var i = 0; i < saveSlots.length; i++) {
saveSlots[i].down = function (x, y, obj) {
var slotIndex = obj.slotIndex;
var gameData = savedGames[slotIndex];
if (gameData) {
// Load existing save and go back to menu
loadGame(slotIndex);
savedGamesContainer.visible = false;
menuContainer.visible = true;
} else {
// Save current game to empty slot
saveGame(slotIndex);
}
};
}
// Shop button click handler
shopBtn.down = function (x, y, obj) {
menuContainer.visible = false;
shopContainer.visible = true;
shopCoinsTxt.setText('Coins: ' + coins);
};
// Tab switching handlers
powerUpTabBtn.down = function (x, y, obj) {
powerUpTabContainer.visible = true;
buildingTabContainer.visible = false;
powerUpTabBtn.fill = 0x4ECDC4;
buildingTabBtn.fill = 0x666666;
};
buildingTabBtn.down = function (x, y, obj) {
powerUpTabContainer.visible = false;
buildingTabContainer.visible = true;
powerUpTabBtn.fill = 0x666666;
buildingTabBtn.fill = 0x8B4513;
};
// Building purchase handlers
hauntedMansionBtn.down = function (x, y, obj) {
buyHauntedMansion();
};
witchHutBtn.down = function (x, y, obj) {
buyWitchHut();
};
pumpkinPatchBtn.down = function (x, y, obj) {
buyPumpkinPatch();
};
ghostlyTowerBtn.down = function (x, y, obj) {
buyGhostlyTower();
};
vampireCastleBtn.down = function (x, y, obj) {
buyVampireCastle();
};
friendlyGhostBtn.down = function (x, y, obj) {
buyFriendlyGhost();
};
wizardBtn.down = function (x, y, obj) {
buyWizard();
};
blackCatBtn.down = function (x, y, obj) {
buyBlackCat();
};
// Shop power-up purchase handlers
shopBombBtn.down = function (x, y, obj) {
buyBomb();
};
shopHintBtn.down = function (x, y, obj) {
buyHint();
};
shopClockBtn.down = function (x, y, obj) {
buyClock();
};
shopTimerBtn.down = function (x, y, obj) {
buyTimer();
};
shopSkipBtn.down = function (x, y, obj) {
buySkip();
};
// Back to menu button handler
shopBackBtn.down = function (x, y, obj) {
shopContainer.visible = false;
menuContainer.visible = true;
coinsInfoTxt.setText('Coins: ' + coins);
};
// Create back button for play function
var playBackBtn = new Text2('BACK TO MENU', {
size: 60,
fill: 0xCCCCCC,
fontWeight: 'bold'
});
playBackBtn.anchor.set(0.5, 0);
LK.gui.topRight.addChild(playBackBtn);
playBackBtn.x = -100;
playBackBtn.y = 100;
playBackBtn.visible = false;
// Back button click handler for play function
playBackBtn.down = function (x, y, obj) {
// Return to menu
showMenu = true;
menuContainer.visible = true;
// Hide game UI elements
movesTxt.visible = false;
timerTxt.visible = false;
coinsTxt.visible = false;
levelTxt.visible = false;
bombTxt.visible = false;
lightbulbTxt.visible = false;
clockTxt.visible = false;
timerPowerTxt.visible = false;
skipTxt.visible = false;
playBackBtn.visible = false;
// Clear existing cards
for (var i = 0; i < cards.length; i++) {
cards[i].destroy();
}
cards = [];
flippedCards = [];
// Reset game state variables
moves = 0;
gameCompleted = false;
canFlipCards = true;
gameOver = false;
// Hide level complete text if visible
levelCompleteTxt.visible = false;
// Update menu display with current values
levelInfoTxt.setText('Current Level: ' + currentLevel);
coinsInfoTxt.setText('Coins: ' + coins);
progressTxt.setText('Progress: ' + currentLevel + '/500');
};
// Play button click handler
playBtn.down = function (x, y, obj) {
// Always start from level 1 when play button is pressed
currentLevel = 1;
storage.currentLevel = currentLevel;
// Reset coin count when play button is pressed
coins = 0;
storage.coins = coins;
coinsTxt.setText('Coins: ' + coins);
coinsInfoTxt.setText('Coins: ' + coins);
levelTxt.setText('Level: ' + currentLevel);
levelInfoTxt.setText('Current Level: ' + currentLevel);
progressTxt.setText('Progress: ' + currentLevel + '/500');
// Reset game state variables for fresh start
moves = 0;
gameCompleted = false;
canFlipCards = true;
gameOver = false;
flippedCards = [];
// Clear existing cards if any
for (var i = 0; i < cards.length; i++) {
cards[i].destroy();
}
cards = [];
startGame();
};
// Hide game UI elements initially (show only in game)
movesTxt.visible = false;
timerTxt.visible = false;
coinsTxt.visible = false;
levelTxt.visible = false;
bombTxt.visible = false;
lightbulbTxt.visible = false;
clockTxt.visible = false;
timerPowerTxt.visible = false;
skipTxt.visible = false;
// Don't initialize cards immediately - wait for menu
// initializeCards(); // This will be called when starting the game
// Main game update loop
game.update = function () {
// Only update game logic if not in menu
if (!showMenu) {
// Update timer
if (!gameCompleted && !gameOver) {
var elapsedTime = Math.floor((Date.now() - gameStartTime) / 1000);
var remainingTime = Math.max(0, LEVEL_TIME_LIMIT - elapsedTime);
timerTxt.setText('Time: ' + formatTime(remainingTime));
// Check if time is up
if (remainingTime <= 0) {
gameOver = true;
// Save current level progress before game over
storage.currentLevel = currentLevel;
// Auto-save failed attempt
autoSaveGame('Failed - Time Limit');
LK.showGameOver();
}
}
}
};
// Start background music
LK.playMusic('bgmusic'); /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
coins: 0,
currentLevel: 1,
bombCount: 0,
hintCount: 0,
clockCount: 0,
timerCount: 0,
skipCount: 0,
mansionCount: 0,
hutCount: 0,
patchCount: 0,
towerCount: 0,
castleCount: 0,
ghostCount: 0,
wizardCount: 0,
catCount: 0,
buildingPositions: {}
});
/****
* Classes
****/
var Card = Container.expand(function (symbolType) {
var self = Container.call(this);
self.symbolType = symbolType;
self.isFlipped = false;
self.isMatched = false;
self.canFlip = true;
// Card back (always visible when not flipped)
var cardBack = LK.getAsset('cardBack', {
anchorX: 0.5,
anchorY: 0.5
});
self.addChild(cardBack);
// Card front (white background)
var cardFront = LK.getAsset('cardFront', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0
});
self.addChild(cardFront);
// Symbol on the card
var symbol = LK.getAsset('symbol' + symbolType, {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0
});
self.addChild(symbol);
self.flip = function () {
if (!self.canFlip || self.isMatched) {
return;
}
LK.getSound('cardFlip').play();
self.isFlipped = !self.isFlipped;
if (self.isFlipped) {
// Show front and symbol
tween(cardFront, {
alpha: 1
}, {
duration: 200
});
tween(symbol, {
alpha: 1
}, {
duration: 200
});
} else {
// Hide front and symbol
tween(cardFront, {
alpha: 0
}, {
duration: 200
});
tween(symbol, {
alpha: 0
}, {
duration: 200
});
}
};
self.setMatched = function () {
self.isMatched = true;
self.canFlip = false;
// Add a subtle scale effect for matched cards
tween(self, {
scaleX: 1.1,
scaleY: 1.1
}, {
duration: 300,
easing: tween.easeOut
});
};
self.down = function (x, y, obj) {
if (self.canFlip && !self.isFlipped && !self.isMatched) {
handleCardTap(self);
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000000
});
/****
* Game Code
****/
// Game variables
// Game state variables
function _typeof(o) {
"@babel/helpers - typeof";
return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) {
return typeof o;
} : function (o) {
return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o;
}, _typeof(o);
}
var cards = [];
var flippedCards = [];
var moves = 0;
var gameCompleted = false;
var canFlipCards = true;
var gameOver = false;
var showMenu = true;
// Level and timing variables
var currentLevel = storage.currentLevel || 1;
var startTime = Date.now();
var gameStartTime = Date.now();
var LEVEL_TIME_LIMIT = 120; // 2 minutes in seconds
var MOVE_LIMIT = 15;
// Grid configuration
var GRID_COLS = 4;
var GRID_ROWS = 4;
var CARD_SIZE = 200;
var CARD_SPACING = 20;
// UI elements
var movesTxt = new Text2('Moves: 0', {
size: 60,
fill: 0x000000,
fontWeight: 'bold'
});
movesTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(movesTxt);
movesTxt.y = 100;
var timerTxt = new Text2('Time: 0:00', {
size: 60,
fill: 0x000000,
fontWeight: 'bold'
});
timerTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(timerTxt);
timerTxt.y = 180;
// Initialize storage with defaults to prevent undefined access errors
// Initialize coins from storage
var coins = storage.coins || 0;
var coinsTxt = new Text2('Coins: ' + coins, {
size: 60,
fill: 0xFFD700,
fontWeight: 'bold'
});
coinsTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(coinsTxt);
coinsTxt.y = 260;
// Initialize saved games from individual storage slots with validation
var savedGames = [];
for (var i = 0; i < 5; i++) {
var slotData = storage['savedGame' + i] || null;
// Validate slotData structure
if (slotData && _typeof(slotData) === 'object' && typeof slotData.level === 'number' && typeof slotData.coins === 'number') {
savedGames.push(slotData);
} else {
savedGames.push(null);
}
}
if (!Array.isArray(savedGames)) {
savedGames = [];
}
while (savedGames.length < 5) {
savedGames.push(null);
}
var levelTxt = new Text2('Level: ' + currentLevel, {
size: 60,
fill: 0x000000,
fontWeight: 'bold'
});
levelTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(levelTxt);
levelTxt.y = 340;
// Level completion notification text
var levelCompleteTxt = new Text2('LEVEL COMPLETE!', {
size: 100,
fill: 0x00FF00,
fontWeight: 'bold'
});
levelCompleteTxt.anchor.set(0.5, 0.5);
levelCompleteTxt.x = 2048 / 2;
levelCompleteTxt.y = 2732 / 2;
levelCompleteTxt.visible = false;
game.addChild(levelCompleteTxt);
// Power-up UI elements
var bombTxt = new Text2('Bomb: ' + (storage.bombCount || 0), {
size: 40,
fill: 0xFF6B6B,
fontWeight: 'bold'
});
bombTxt.anchor.set(0, 0);
LK.gui.topLeft.addChild(bombTxt);
bombTxt.x = 120;
bombTxt.y = 100;
var lightbulbTxt = new Text2('Hint: ' + (storage.hintCount || 0), {
size: 40,
fill: 0xFFD700,
fontWeight: 'bold'
});
lightbulbTxt.anchor.set(0, 0);
LK.gui.topLeft.addChild(lightbulbTxt);
lightbulbTxt.x = 120;
lightbulbTxt.y = 160;
var clockTxt = new Text2('Clock: ' + (storage.clockCount || 0), {
size: 40,
fill: 0x4ECDC4,
fontWeight: 'bold'
});
clockTxt.anchor.set(0, 0);
LK.gui.topLeft.addChild(clockTxt);
clockTxt.x = 120;
clockTxt.y = 220;
var timerPowerTxt = new Text2('Timer: ' + (storage.timerCount || 0), {
size: 40,
fill: 0x96CEB4,
fontWeight: 'bold'
});
timerPowerTxt.anchor.set(0, 0);
LK.gui.topLeft.addChild(timerPowerTxt);
timerPowerTxt.x = 120;
timerPowerTxt.y = 280;
var skipTxt = new Text2('Skip: ' + (storage.skipCount || 0), {
size: 40,
fill: 0xFF9FF3,
fontWeight: 'bold'
});
skipTxt.anchor.set(0, 0);
LK.gui.topLeft.addChild(skipTxt);
skipTxt.x = 120;
skipTxt.y = 340;
// Menu screen elements
var menuContainer = new Container();
game.addChild(menuContainer);
var titleTxt = new Text2('SPOOKY MEMORY MATCH', {
size: 120,
fill: 0xFF6600,
fontWeight: 'bold'
});
titleTxt.anchor.set(0.5, 0.5);
titleTxt.x = 2048 / 2;
titleTxt.y = 500;
menuContainer.addChild(titleTxt);
var levelInfoTxt = new Text2('Current Level: ' + currentLevel, {
size: 80,
fill: 0xFFD700,
fontWeight: 'bold'
});
levelInfoTxt.anchor.set(0.5, 0.5);
levelInfoTxt.x = 2048 / 2;
levelInfoTxt.y = 700;
menuContainer.addChild(levelInfoTxt);
var coinsInfoTxt = new Text2('Coins: ' + coins, {
size: 80,
fill: 0xFFD700,
fontWeight: 'bold'
});
coinsInfoTxt.anchor.set(0.5, 0.5);
coinsInfoTxt.x = 2048 / 2;
coinsInfoTxt.y = 850;
menuContainer.addChild(coinsInfoTxt);
var playBtn = new Text2('PLAY', {
size: 100,
fill: 0x4ECDC4,
fontWeight: 'bold'
});
playBtn.anchor.set(0.5, 0.5);
playBtn.x = 2048 / 2;
playBtn.y = 1050;
menuContainer.addChild(playBtn);
var instructionsTxt = new Text2('Match spooky pairs to advance levels!\nEarn coins to buy magical power-ups!', {
size: 60,
fill: 0xFF6600,
fontWeight: 'bold'
});
instructionsTxt.anchor.set(0.5, 0.5);
instructionsTxt.x = 2048 / 2;
instructionsTxt.y = 2200;
menuContainer.addChild(instructionsTxt);
var progressTxt = new Text2('Progress: ' + currentLevel + '/500', {
size: 70,
fill: 0x96CEB4,
fontWeight: 'bold'
});
progressTxt.anchor.set(0.5, 0.5);
progressTxt.x = 2048 / 2;
progressTxt.y = 2400;
menuContainer.addChild(progressTxt);
// Shop button for buying power-ups
var shopBtn = new Text2('SHOP', {
size: 100,
fill: 0xFFD700,
fontWeight: 'bold'
});
shopBtn.anchor.set(0.5, 0.5);
shopBtn.x = 2048 / 2;
shopBtn.y = 1250;
menuContainer.addChild(shopBtn);
// Shop screen elements
var shopContainer = new Container();
shopContainer.visible = false;
game.addChild(shopContainer);
var shopTitleTxt = new Text2('SHOP', {
size: 120,
fill: 0xFFD700,
fontWeight: 'bold'
});
shopTitleTxt.anchor.set(0.5, 0.5);
shopTitleTxt.x = 2048 / 2;
shopTitleTxt.y = 400;
shopContainer.addChild(shopTitleTxt);
var shopCoinsTxt = new Text2('Coins: ' + coins, {
size: 80,
fill: 0xFFD700,
fontWeight: 'bold'
});
shopCoinsTxt.anchor.set(0.5, 0.5);
shopCoinsTxt.x = 2048 / 2;
shopCoinsTxt.y = 500;
shopContainer.addChild(shopCoinsTxt);
// Tab buttons
var powerUpTabBtn = new Text2('POWER-UPS', {
size: 80,
fill: 0x4ECDC4,
fontWeight: 'bold'
});
powerUpTabBtn.anchor.set(0.5, 0.5);
powerUpTabBtn.x = 2048 / 2 - 200;
powerUpTabBtn.y = 600;
shopContainer.addChild(powerUpTabBtn);
var buildingTabBtn = new Text2('BUILDINGS', {
size: 80,
fill: 0x666666,
fontWeight: 'bold'
});
buildingTabBtn.anchor.set(0.5, 0.5);
buildingTabBtn.x = 2048 / 2 + 200;
buildingTabBtn.y = 600;
shopContainer.addChild(buildingTabBtn);
// Power-ups tab content
var powerUpTabContainer = new Container();
powerUpTabContainer.visible = true; // Start with power-ups tab visible
shopContainer.addChild(powerUpTabContainer);
var shopBombBtn = new Text2('BOMB - 10 Coins\n(Removes 6 random cards)', {
size: 60,
fill: 0xFF6B6B,
fontWeight: 'bold'
});
shopBombBtn.anchor.set(0.5, 0.5);
shopBombBtn.x = 2048 / 2;
shopBombBtn.y = 800;
powerUpTabContainer.addChild(shopBombBtn);
var shopHintBtn = new Text2('HINT - 15 Coins\n(Reveals a matching pair)', {
size: 60,
fill: 0xFFD700,
fontWeight: 'bold'
});
shopHintBtn.anchor.set(0.5, 0.5);
shopHintBtn.x = 2048 / 2;
shopHintBtn.y = 1000;
powerUpTabContainer.addChild(shopHintBtn);
var shopClockBtn = new Text2('CLOCK - 20 Coins\n(Adds 5 minutes)', {
size: 60,
fill: 0x4ECDC4,
fontWeight: 'bold'
});
shopClockBtn.anchor.set(0.5, 0.5);
shopClockBtn.x = 2048 / 2;
shopClockBtn.y = 1200;
powerUpTabContainer.addChild(shopClockBtn);
var shopTimerBtn = new Text2('TIMER - 5 Coins\n(Adds 1 minute)', {
size: 60,
fill: 0x96CEB4,
fontWeight: 'bold'
});
shopTimerBtn.anchor.set(0.5, 0.5);
shopTimerBtn.x = 2048 / 2;
shopTimerBtn.y = 1400;
powerUpTabContainer.addChild(shopTimerBtn);
var shopSkipBtn = new Text2('SKIP - 100 Coins\n(Skip current level)', {
size: 60,
fill: 0xFF9FF3,
fontWeight: 'bold'
});
shopSkipBtn.anchor.set(0.5, 0.5);
shopSkipBtn.x = 2048 / 2;
shopSkipBtn.y = 1600;
powerUpTabContainer.addChild(shopSkipBtn);
// Buildings tab content
var buildingTabContainer = new Container();
buildingTabContainer.visible = false;
shopContainer.addChild(buildingTabContainer);
var hauntedMansionBtn = new Text2('HAUNTED MANSION - 50 Coins\n(Spooky decoration)', {
size: 60,
fill: 0x4B0082,
fontWeight: 'bold'
});
hauntedMansionBtn.anchor.set(0.5, 0.5);
hauntedMansionBtn.x = 2048 / 2;
hauntedMansionBtn.y = 800;
buildingTabContainer.addChild(hauntedMansionBtn);
var witchHutBtn = new Text2('WITCH HUT - 75 Coins\n(Magical dwelling)', {
size: 60,
fill: 0x228B22,
fontWeight: 'bold'
});
witchHutBtn.anchor.set(0.5, 0.5);
witchHutBtn.x = 2048 / 2;
witchHutBtn.y = 950;
buildingTabContainer.addChild(witchHutBtn);
var pumpkinPatchBtn = new Text2('PUMPKIN PATCH - 30 Coins\n(Halloween decoration)', {
size: 60,
fill: 0xFF8C00,
fontWeight: 'bold'
});
pumpkinPatchBtn.anchor.set(0.5, 0.5);
pumpkinPatchBtn.x = 2048 / 2;
pumpkinPatchBtn.y = 1100;
buildingTabContainer.addChild(pumpkinPatchBtn);
var ghostlyTowerBtn = new Text2('GHOSTLY TOWER - 100 Coins\n(Spectral building)', {
size: 60,
fill: 0xF5F5F5,
fontWeight: 'bold'
});
ghostlyTowerBtn.anchor.set(0.5, 0.5);
ghostlyTowerBtn.x = 2048 / 2;
ghostlyTowerBtn.y = 1250;
buildingTabContainer.addChild(ghostlyTowerBtn);
var vampireCastleBtn = new Text2('VAMPIRE CASTLE - 200 Coins\n(Dark fortress)', {
size: 60,
fill: 0x8B0000,
fontWeight: 'bold'
});
vampireCastleBtn.anchor.set(0.5, 0.5);
vampireCastleBtn.x = 2048 / 2;
vampireCastleBtn.y = 1400;
buildingTabContainer.addChild(vampireCastleBtn);
// NPC characters
var friendlyGhostBtn = new Text2('FRIENDLY GHOST - 40 Coins\n(Helpful NPC)', {
size: 60,
fill: 0xD3D3D3,
fontWeight: 'bold'
});
friendlyGhostBtn.anchor.set(0.5, 0.5);
friendlyGhostBtn.x = 2048 / 2;
friendlyGhostBtn.y = 1550;
buildingTabContainer.addChild(friendlyGhostBtn);
var wizardBtn = new Text2('WIZARD - 60 Coins\n(Wise NPC)', {
size: 60,
fill: 0x191970,
fontWeight: 'bold'
});
wizardBtn.anchor.set(0.5, 0.5);
wizardBtn.x = 2048 / 2;
wizardBtn.y = 1700;
buildingTabContainer.addChild(wizardBtn);
var blackCatBtn = new Text2('BLACK CAT - 25 Coins\n(Mystical companion)', {
size: 60,
fill: 0x000000,
fontWeight: 'bold'
});
blackCatBtn.anchor.set(0.5, 0.5);
blackCatBtn.x = 2048 / 2;
blackCatBtn.y = 1850;
buildingTabContainer.addChild(blackCatBtn);
var shopBackBtn = new Text2('BACK TO MENU', {
size: 80,
fill: 0xCCCCCC,
fontWeight: 'bold'
});
shopBackBtn.anchor.set(0.5, 0.5);
shopBackBtn.x = 2048 / 2;
shopBackBtn.y = 2000;
shopContainer.addChild(shopBackBtn);
// Saved Games button
var savedGamesBtn = new Text2('SAVED GAMES', {
size: 100,
fill: 0x96CEB4,
fontWeight: 'bold'
});
savedGamesBtn.anchor.set(0.5, 0.5);
savedGamesBtn.x = 2048 / 2;
savedGamesBtn.y = 1450;
menuContainer.addChild(savedGamesBtn);
// World button
var worldBtn = new Container();
worldBtn.x = 2048 / 2;
worldBtn.y = 1650;
// Add background image
var worldBackground = LK.getAsset('worldbackground', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2,
scaleY: 2
});
worldBtn.addChild(worldBackground);
// Add text on top of background
var worldBtnText = new Text2('WORLD', {
size: 100,
fill: 0x4B0082,
fontWeight: 'bold'
});
worldBtnText.anchor.set(0.5, 0.5);
worldBtn.addChild(worldBtnText);
menuContainer.addChild(worldBtn);
// Saved Games screen container
var savedGamesContainer = new Container();
savedGamesContainer.visible = false;
game.addChild(savedGamesContainer);
var savedGamesTitleTxt = new Text2('SAVED GAMES', {
size: 120,
fill: 0x96CEB4,
fontWeight: 'bold'
});
savedGamesTitleTxt.anchor.set(0.5, 0.5);
savedGamesTitleTxt.x = 2048 / 2;
savedGamesTitleTxt.y = 400;
savedGamesContainer.addChild(savedGamesTitleTxt);
var savedGamesInstructionTxt = new Text2('Click a slot to load or save your game progress', {
size: 60,
fill: 0x000000,
fontWeight: 'bold'
});
savedGamesInstructionTxt.anchor.set(0.5, 0.5);
savedGamesInstructionTxt.x = 2048 / 2;
savedGamesInstructionTxt.y = 550;
savedGamesContainer.addChild(savedGamesInstructionTxt);
// Create 5 save game slot buttons in saved games container
var saveSlots = [];
for (var i = 0; i < 5; i++) {
var slotData = savedGames[i];
var slotText = 'Slot ' + (i + 1);
if (slotData) {
slotText += ' - Level ' + slotData.level + ' (' + slotData.coins + ' coins)';
if (slotData.result) {
slotText += ' - ' + slotData.result;
}
} else {
slotText += ' - Empty';
}
var slotBtn = new Text2(slotText, {
size: 60,
fill: slotData ? 0x4ECDC4 : 0x666666,
fontWeight: 'bold'
});
slotBtn.anchor.set(0.5, 0.5);
slotBtn.x = 2048 / 2;
slotBtn.y = 750 + i * 150;
slotBtn.slotIndex = i;
savedGamesContainer.addChild(slotBtn);
saveSlots.push(slotBtn);
}
// Back to menu button for saved games
var savedGamesBackBtn = new Text2('BACK TO MENU', {
size: 80,
fill: 0xCCCCCC,
fontWeight: 'bold'
});
savedGamesBackBtn.anchor.set(0.5, 0.5);
savedGamesBackBtn.x = 2048 / 2;
savedGamesBackBtn.y = 1500;
savedGamesContainer.addChild(savedGamesBackBtn);
// Create card types array based on level (4 tiles for first level + 6 tiles per additional level)
var totalTiles;
if (currentLevel === 1) {
totalTiles = 4; // First level has exactly 4 tiles
} else {
totalTiles = 4 + (currentLevel - 1) * 6; // 4 base tiles + 6 per level
}
var pairCount = totalTiles / 2;
var cardTypes = [];
for (var i = 1; i <= pairCount; i++) {
// Ensure symbol index stays within valid range (1-25)
var symbolIndex = (i - 1) % 25 + 1; // Cycle through 25 available symbols
cardTypes.push(symbolIndex);
cardTypes.push(symbolIndex); // Add pair
}
// Shuffle function
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;
}
// Initialize cards
function initializeCards() {
var shuffledTypes = shuffleArray(cardTypes);
// Calculate dynamic grid size based on total tiles
var totalTiles = cardTypes.length;
GRID_COLS = Math.ceil(Math.sqrt(totalTiles));
GRID_ROWS = Math.ceil(totalTiles / GRID_COLS);
// Adjust card size based on grid size
if (totalTiles > 16) {
CARD_SIZE = Math.max(120, 200 - (totalTiles - 16) * 5);
CARD_SPACING = Math.max(10, 20 - (totalTiles - 16) * 1);
}
// Calculate grid positioning
var gridWidth = GRID_COLS * CARD_SIZE + (GRID_COLS - 1) * CARD_SPACING;
var gridHeight = GRID_ROWS * CARD_SIZE + (GRID_ROWS - 1) * CARD_SPACING;
var startX = (2048 - gridWidth) / 2 + CARD_SIZE / 2;
var startY = (2732 - gridHeight) / 2 + CARD_SIZE / 2;
for (var i = 0; i < totalTiles; i++) {
var row = Math.floor(i / GRID_COLS);
var col = i % GRID_COLS;
var card = new Card(shuffledTypes[i]);
card.x = startX + col * (CARD_SIZE + CARD_SPACING);
card.y = startY + row * (CARD_SIZE + CARD_SPACING);
cards.push(card);
game.addChild(card);
}
}
// Handle card tap
function handleCardTap(card) {
if (!canFlipCards || flippedCards.length >= 2 || gameOver) {
return;
}
card.flip();
flippedCards.push(card);
if (flippedCards.length === 2) {
moves++;
movesTxt.setText('Moves: ' + moves);
// Check if move limit exceeded
if (moves >= MOVE_LIMIT) {
gameOver = true;
// Save current level progress before game over
storage.currentLevel = currentLevel;
// Auto-save failed attempt
autoSaveGame('Failed - Move Limit');
LK.setTimeout(function () {
LK.showGameOver();
}, 1000);
return;
}
canFlipCards = false;
// Check for match after a short delay
LK.setTimeout(function () {
checkForMatch();
}, 1000);
}
}
// Check if two flipped cards match
function checkForMatch() {
var card1 = flippedCards[0];
var card2 = flippedCards[1];
if (card1.symbolType === card2.symbolType) {
// Match found
LK.getSound('match').play();
card1.setMatched();
card2.setMatched();
// Award 5 coins for matching pair
coins += 5;
storage.coins = coins;
coinsTxt.setText('Coins: ' + coins);
// Check if game is complete
var matchedCount = 0;
for (var i = 0; i < cards.length; i++) {
if (cards[i] && typeof cards[i].isMatched !== 'undefined' && cards[i].isMatched) {
matchedCount++;
}
}
if (matchedCount === cards.length) {
gameCompleted = true;
// Save current level completion
storage.currentLevel = currentLevel;
// Auto-save completed level
autoSaveGame('Completed');
// Award bonus coins for completing level
coins += 10;
storage.coins = coins;
coinsTxt.setText('Coins: ' + coins);
// Check if we've completed all 500 levels
if (currentLevel >= 500) {
// Show you win when all levels are completed
LK.setTimeout(function () {
LK.showYouWin();
}, 1000);
} else {
// Advance to next level
currentLevel++;
storage.currentLevel = currentLevel; // Save current level to storage
levelTxt.setText('Level: ' + currentLevel);
// Update menu progress display
levelInfoTxt.setText('Current Level: ' + currentLevel);
progressTxt.setText('Progress: ' + currentLevel + '/500');
// Show level completion message
LK.effects.flashScreen(0x00FF00, 1000);
// Reset for next level
LK.setTimeout(function () {
resetLevel();
}, 1500);
}
}
} else {
// No match
LK.getSound('noMatch').play();
card1.flip();
card2.flip();
}
flippedCards = [];
canFlipCards = true;
}
// Reset level for progression
function resetLevel() {
// Show level completion message briefly if advancing
if (currentLevel > 1) {
levelCompleteTxt.setText('LEVEL ' + (currentLevel - 1) + ' COMPLETE!');
levelCompleteTxt.visible = true;
LK.setTimeout(function () {
levelCompleteTxt.visible = false;
}, 2000);
}
// Clear existing cards
for (var i = 0; i < cards.length; i++) {
if (cards[i] && typeof cards[i].destroy === 'function') {
cards[i].destroy();
}
}
cards = [];
flippedCards = [];
moves = 0;
gameCompleted = false;
canFlipCards = true;
gameOver = false;
gameStartTime = Date.now();
startTime = Date.now();
// Update UI
movesTxt.setText('Moves: 0');
timerTxt.setText('Time: 0:00');
// Recreate card types for new level
var totalTiles;
if (currentLevel === 1) {
totalTiles = 4; // First level has exactly 4 tiles
} else {
totalTiles = 4 + (currentLevel - 1) * 6; // 4 base tiles + 6 per level
}
var pairCount = totalTiles / 2;
cardTypes = [];
for (var i = 1; i <= pairCount; i++) {
// Ensure symbol index stays within valid range (1-25)
var symbolIndex = (i - 1) % 25 + 1; // Cycle through 25 available symbols
cardTypes.push(symbolIndex);
cardTypes.push(symbolIndex);
}
// Initialize new cards
initializeCards();
}
// Format time display
function formatTime(seconds) {
var minutes = Math.floor(seconds / 60);
var remainingSeconds = seconds % 60;
return minutes + ':' + (remainingSeconds < 10 ? '0' : '') + remainingSeconds;
}
// Building purchase functions
function buyHauntedMansion() {
if (coins >= 50) {
coins = Math.max(0, coins - 50);
storage.coins = coins;
shopCoinsTxt.setText('Coins: ' + coins);
coinsInfoTxt.setText('Coins: ' + coins);
var mansionCount = storage.mansionCount || 0;
storage.mansionCount = mansionCount + 1;
LK.effects.flashScreen(0x4B0082, 500);
}
}
function buyWitchHut() {
if (coins >= 75) {
coins = Math.max(0, coins - 75);
storage.coins = coins;
shopCoinsTxt.setText('Coins: ' + coins);
coinsInfoTxt.setText('Coins: ' + coins);
var hutCount = storage.hutCount || 0;
storage.hutCount = hutCount + 1;
LK.effects.flashScreen(0x228B22, 500);
}
}
function buyPumpkinPatch() {
if (coins >= 30) {
coins = Math.max(0, coins - 30);
storage.coins = coins;
shopCoinsTxt.setText('Coins: ' + coins);
coinsInfoTxt.setText('Coins: ' + coins);
var patchCount = storage.patchCount || 0;
storage.patchCount = patchCount + 1;
LK.effects.flashScreen(0xFF8C00, 500);
}
}
function buyGhostlyTower() {
if (coins >= 100) {
coins = Math.max(0, coins - 100);
storage.coins = coins;
shopCoinsTxt.setText('Coins: ' + coins);
coinsInfoTxt.setText('Coins: ' + coins);
var towerCount = storage.towerCount || 0;
storage.towerCount = towerCount + 1;
LK.effects.flashScreen(0xF5F5F5, 500);
}
}
function buyVampireCastle() {
if (coins >= 200) {
coins = Math.max(0, coins - 200);
storage.coins = coins;
shopCoinsTxt.setText('Coins: ' + coins);
coinsInfoTxt.setText('Coins: ' + coins);
var castleCount = storage.castleCount || 0;
storage.castleCount = castleCount + 1;
LK.effects.flashScreen(0x8B0000, 500);
}
}
function buyFriendlyGhost() {
if (coins >= 40) {
coins = Math.max(0, coins - 40);
storage.coins = coins;
shopCoinsTxt.setText('Coins: ' + coins);
coinsInfoTxt.setText('Coins: ' + coins);
var ghostCount = storage.ghostCount || 0;
storage.ghostCount = ghostCount + 1;
LK.effects.flashScreen(0xD3D3D3, 500);
}
}
function buyWizard() {
if (coins >= 60) {
coins = Math.max(0, coins - 60);
storage.coins = coins;
shopCoinsTxt.setText('Coins: ' + coins);
coinsInfoTxt.setText('Coins: ' + coins);
var wizardCount = storage.wizardCount || 0;
storage.wizardCount = wizardCount + 1;
LK.effects.flashScreen(0x191970, 500);
}
}
function buyBlackCat() {
if (coins >= 25) {
coins = Math.max(0, coins - 25);
storage.coins = coins;
shopCoinsTxt.setText('Coins: ' + coins);
coinsInfoTxt.setText('Coins: ' + coins);
var catCount = storage.catCount || 0;
storage.catCount = catCount + 1;
LK.effects.flashScreen(0x000000, 500);
}
}
// Shop power-up purchase functions
function buyBomb() {
if (coins >= 10) {
coins = Math.max(0, coins - 10);
storage.coins = coins;
shopCoinsTxt.setText('Coins: ' + coins);
coinsInfoTxt.setText('Coins: ' + coins);
// Add bomb to inventory (stored in storage)
var bombCount = storage.bombCount || 0;
storage.bombCount = bombCount + 1;
bombTxt.setText('Bomb: ' + (storage.bombCount || 0));
LK.effects.flashScreen(0x00FF00, 500);
}
}
function buyHint() {
if (coins >= 15) {
coins = Math.max(0, coins - 15);
storage.coins = coins;
shopCoinsTxt.setText('Coins: ' + coins);
coinsInfoTxt.setText('Coins: ' + coins);
// Add hint to inventory
var hintCount = storage.hintCount || 0;
storage.hintCount = hintCount + 1;
lightbulbTxt.setText('Hint: ' + (storage.hintCount || 0));
LK.effects.flashScreen(0x00FF00, 500);
}
}
function buyClock() {
if (coins >= 20) {
coins = Math.max(0, coins - 20);
storage.coins = coins;
shopCoinsTxt.setText('Coins: ' + coins);
coinsInfoTxt.setText('Coins: ' + coins);
// Add clock to inventory
var clockCount = storage.clockCount || 0;
storage.clockCount = clockCount + 1;
clockTxt.setText('Clock: ' + (storage.clockCount || 0));
LK.effects.flashScreen(0x00FF00, 500);
}
}
function buyTimer() {
if (coins >= 5) {
coins = Math.max(0, coins - 5);
storage.coins = coins;
shopCoinsTxt.setText('Coins: ' + coins);
coinsInfoTxt.setText('Coins: ' + coins);
// Add timer to inventory
var timerCount = storage.timerCount || 0;
storage.timerCount = timerCount + 1;
timerPowerTxt.setText('Timer: ' + (storage.timerCount || 0));
LK.effects.flashScreen(0x00FF00, 500);
}
}
function buySkip() {
if (coins >= 100) {
coins = Math.max(0, coins - 100);
storage.coins = coins;
shopCoinsTxt.setText('Coins: ' + coins);
coinsInfoTxt.setText('Coins: ' + coins);
// Add skip to inventory
var skipCount = storage.skipCount || 0;
storage.skipCount = skipCount + 1;
skipTxt.setText('Skip: ' + (storage.skipCount || 0));
LK.effects.flashScreen(0x00FF00, 500);
}
}
// Power-up functions
function useBomb() {
var bombCount = storage.bombCount || 0;
if (bombCount > 0) {
storage.bombCount = bombCount - 1;
// Update display
bombTxt.setText('Bomb: ' + (storage.bombCount || 0));
// Delete 6 random non-matched cards
var availableCards = [];
for (var i = 0; i < cards.length; i++) {
if (cards[i] && typeof cards[i].isMatched !== 'undefined' && !cards[i].isMatched) {
availableCards.push(cards[i]);
}
}
var cardsToRemove = Math.min(6, availableCards.length);
for (var i = 0; i < cardsToRemove; i++) {
if (availableCards.length > 0) {
var randomIndex = Math.floor(Math.random() * availableCards.length);
var cardToRemove = availableCards[randomIndex];
if (cardToRemove && cardToRemove.destroy) {
cardToRemove.destroy();
var cardIndex = cards.indexOf(cardToRemove);
if (cardIndex !== -1) {
cards.splice(cardIndex, 1);
}
availableCards.splice(randomIndex, 1);
}
}
}
}
}
function useLightbulb() {
var hintCount = storage.hintCount || 0;
if (hintCount > 0) {
storage.hintCount = hintCount - 1;
// Update display
lightbulbTxt.setText('Hint: ' + (storage.hintCount || 0));
// Find a matching pair that's not matched yet
var unmatchedCards = [];
for (var i = 0; i < cards.length; i++) {
if (cards[i] && typeof cards[i].isMatched !== 'undefined' && !cards[i].isMatched) {
unmatchedCards.push(cards[i]);
}
}
// Group by symbol type
var symbolGroups = {};
for (var i = 0; i < unmatchedCards.length; i++) {
var card = unmatchedCards[i];
if (!symbolGroups[card.symbolType]) {
symbolGroups[card.symbolType] = [];
}
symbolGroups[card.symbolType].push(card);
}
// Find first pair and show them
for (var symbolType in symbolGroups) {
if (symbolGroups[symbolType].length >= 2) {
var pair = symbolGroups[symbolType].slice(0, 2);
for (var i = 0; i < pair.length; i++) {
if (!pair[i].isFlipped) {
pair[i].flip();
}
}
break;
}
}
}
}
function useClock() {
var clockCount = storage.clockCount || 0;
if (clockCount > 0) {
storage.clockCount = clockCount - 1;
// Update display
clockTxt.setText('Clock: ' + (storage.clockCount || 0));
// Add 5 minutes (300 seconds) to game time
gameStartTime -= 300000; // Subtract 5 minutes from start time to effectively add time
}
}
function useTimer() {
var timerCount = storage.timerCount || 0;
if (timerCount > 0) {
storage.timerCount = timerCount - 1;
// Update display
timerPowerTxt.setText('Timer: ' + (storage.timerCount || 0));
// Add 1 minute (60 seconds) to game time
gameStartTime -= 60000; // Subtract 1 minute from start time to effectively add time
}
}
function useSkip() {
var skipCount = storage.skipCount || 0;
if (skipCount > 0) {
storage.skipCount = skipCount - 1;
// Update display
skipTxt.setText('Skip: ' + (storage.skipCount || 0));
// Save current level before skipping
storage.currentLevel = currentLevel;
// Check if we've completed all 500 levels
if (currentLevel >= 500) {
// Show you win when all levels are completed
LK.setTimeout(function () {
LK.showYouWin();
}, 1000);
} else {
// Advance to next level
currentLevel++;
storage.currentLevel = currentLevel; // Save current level to storage
levelTxt.setText('Level: ' + currentLevel);
// Reset for next level
LK.setTimeout(function () {
resetLevel();
}, 1000);
}
}
}
// Function to save current game state
function saveGame(slotIndex) {
// Create flattened gameData object with only literals
var gameData = {
level: currentLevel,
coins: coins,
timestamp: Date.now()
};
// Ensure savedGames is initialized as an array
if (!Array.isArray(savedGames)) {
savedGames = [];
}
// Ensure we have enough slots
while (savedGames.length <= slotIndex) {
savedGames.push(null);
}
savedGames[slotIndex] = gameData;
storage['savedGame' + slotIndex] = gameData;
// Update the slot button text only if saveSlots exists and has the slot
if (saveSlots && saveSlots[slotIndex]) {
var slotBtn = saveSlots[slotIndex];
slotBtn.setText('Slot ' + (slotIndex + 1) + ' - Level ' + gameData.level + ' (' + gameData.coins + ' coins)');
slotBtn.tint = 0x4ECDC4;
}
}
// Function to auto-save to circular buffer of latest 5 games
function autoSaveGame(gameResult) {
// Create flattened gameData object with only literals
var gameData = {
level: currentLevel,
coins: coins,
timestamp: Date.now(),
result: gameResult
};
// Ensure savedGames is initialized as an array
if (!Array.isArray(savedGames)) {
savedGames = [];
}
// Create a new array to avoid reference issues
var newSavedGames = [];
// Add new game to the beginning
newSavedGames.push(gameData);
// Add existing games (up to 4 more)
for (var i = 0; i < Math.min(4, savedGames.length); i++) {
if (savedGames[i]) {
newSavedGames.push(savedGames[i]);
}
}
savedGames = newSavedGames;
// Keep only the latest 5 games
if (savedGames.length > 5) {
savedGames = savedGames.slice(0, 5);
}
// Save each slot individually to storage with only literals
for (var i = 0; i < 5; i++) {
if (i < savedGames.length && savedGames[i]) {
storage['savedGame' + i] = {
level: savedGames[i].level,
coins: savedGames[i].coins,
timestamp: savedGames[i].timestamp,
result: savedGames[i].result || ''
};
} else {
storage['savedGame' + i] = null;
}
}
// Update all slot buttons if they exist
if (saveSlots) {
for (var i = 0; i < 5; i++) {
if (saveSlots[i]) {
var slotData = savedGames[i];
var slotText = 'Slot ' + (i + 1);
if (slotData) {
slotText += ' - Level ' + slotData.level + ' (' + slotData.coins + ' coins)';
if (slotData.result) {
slotText += ' - ' + slotData.result;
}
} else {
slotText += ' - Empty';
}
saveSlots[i].setText(slotText);
saveSlots[i].tint = slotData ? 0x4ECDC4 : 0x666666;
}
}
}
}
// Function to load saved game
function loadGame(slotIndex) {
if (slotIndex >= 0 && slotIndex < savedGames.length && savedGames[slotIndex]) {
var gameData = savedGames[slotIndex];
if (gameData && typeof gameData.level === 'number' && typeof gameData.coins === 'number') {
currentLevel = Math.max(1, Math.min(500, gameData.level));
coins = Math.max(0, gameData.coins);
storage.currentLevel = currentLevel;
storage.coins = coins;
// Update UI
levelInfoTxt.setText('Current Level: ' + currentLevel);
coinsInfoTxt.setText('Coins: ' + coins);
progressTxt.setText('Progress: ' + currentLevel + '/500');
}
}
}
// Function to update power-up inventory display
function updatePowerUpDisplay() {
bombTxt.setText('Bomb: ' + (storage.bombCount || 0));
lightbulbTxt.setText('Hint: ' + (storage.hintCount || 0));
clockTxt.setText('Clock: ' + (storage.clockCount || 0));
timerPowerTxt.setText('Timer: ' + (storage.timerCount || 0));
skipTxt.setText('Skip: ' + (storage.skipCount || 0));
}
// Function to start the game
function startGame() {
showMenu = false;
menuContainer.visible = false;
// Show game UI elements
movesTxt.visible = true;
timerTxt.visible = true;
coinsTxt.visible = true;
levelTxt.visible = true;
bombTxt.visible = true;
lightbulbTxt.visible = true;
clockTxt.visible = true;
timerPowerTxt.visible = true;
skipTxt.visible = true;
playBackBtn.visible = true;
// Update power-up display
updatePowerUpDisplay();
// Initialize game
initializeCards();
gameStartTime = Date.now();
startTime = Date.now();
}
// Add click handlers for power-ups
bombTxt.down = function (x, y, obj) {
useBomb();
};
lightbulbTxt.down = function (x, y, obj) {
useLightbulb();
};
clockTxt.down = function (x, y, obj) {
useClock();
};
timerPowerTxt.down = function (x, y, obj) {
useTimer();
};
skipTxt.down = function (x, y, obj) {
useSkip();
};
// Saved Games button click handler
savedGamesBtn.down = function (x, y, obj) {
menuContainer.visible = false;
savedGamesContainer.visible = true;
// Update saved games display
for (var i = 0; i < saveSlots.length; i++) {
var slotData = savedGames[i];
var slotText = 'Slot ' + (i + 1);
if (slotData) {
slotText += ' - Level ' + slotData.level + ' (' + slotData.coins + ' coins)';
if (slotData.result) {
slotText += ' - ' + slotData.result;
}
} else {
slotText += ' - Empty';
}
saveSlots[i].setText(slotText);
saveSlots[i].tint = slotData ? 0x4ECDC4 : 0x666666;
}
};
// World screen container
var worldContainer = new Container();
worldContainer.visible = false;
// Add worldbackground as background
var worldContainerBackground = LK.getAsset('worldbackground', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 20,
scaleY: 27
});
worldContainerBackground.x = 2048 / 2;
worldContainerBackground.y = 2732 / 2;
worldContainer.addChild(worldContainerBackground);
game.addChild(worldContainer);
var worldTitleTxt = new Text2('YOUR SPOOKY WORLD', {
size: 120,
fill: 0x4B0082,
fontWeight: 'bold'
});
worldTitleTxt.anchor.set(0.5, 0.5);
worldTitleTxt.x = 2048 / 2;
worldTitleTxt.y = 400;
worldContainer.addChild(worldTitleTxt);
var worldInstructionTxt = new Text2('Drag buildings to place them in your world', {
size: 60,
fill: 0x000000,
fontWeight: 'bold'
});
worldInstructionTxt.anchor.set(0.5, 0.5);
worldInstructionTxt.x = 2048 / 2;
worldInstructionTxt.y = 500;
worldContainer.addChild(worldInstructionTxt);
// World back button
var worldBackBtn = new Text2('BACK TO MENU', {
size: 80,
fill: 0xCCCCCC,
fontWeight: 'bold'
});
worldBackBtn.anchor.set(0.5, 0.5);
worldBackBtn.x = 2048 / 2;
worldBackBtn.y = 2400;
worldContainer.addChild(worldBackBtn);
// Array to store placed buildings
var placedBuildings = [];
// World button click handler
worldBtn.down = function (x, y, obj) {
menuContainer.visible = false;
worldContainer.visible = true;
createWorldBuildings();
};
// World back button handler
worldBackBtn.down = function (x, y, obj) {
worldContainer.visible = false;
menuContainer.visible = true;
saveWorldState();
};
// Function to create world buildings based on purchased items
function createWorldBuildings() {
// Clear existing buildings
for (var i = 0; i < placedBuildings.length; i++) {
placedBuildings[i].destroy();
}
placedBuildings = [];
// Load saved positions from flattened storage structure
var savedPositions = {};
for (var i = 0; i < placedBuildings.length; i++) {
var building = placedBuildings[i];
var x = storage['buildingPos_' + building.buildingId + '_x'];
var y = storage['buildingPos_' + building.buildingId + '_y'];
if (x !== undefined && y !== undefined) {
savedPositions[building.buildingId] = {
x: x,
y: y
};
}
}
var buildingY = 700;
var buildingX = 150;
var buildingSpacing = 300;
// Create buildings based on purchased counts
var mansionCount = storage.mansionCount || 0;
for (var i = 0; i < mansionCount; i++) {
var mansion = createBuilding('Haunted Mansion', 0x4B0082, buildingX, buildingY, 'mansion_' + i);
placedBuildings.push(mansion);
worldContainer.addChild(mansion);
buildingX += buildingSpacing;
if (buildingX > 1800) {
buildingX = 150;
buildingY += 250;
}
}
var hutCount = storage.hutCount || 0;
for (var i = 0; i < hutCount; i++) {
var hut = createBuilding('Witch Hut', 0x228B22, buildingX, buildingY, 'hut_' + i);
placedBuildings.push(hut);
worldContainer.addChild(hut);
buildingX += buildingSpacing;
if (buildingX > 1800) {
buildingX = 150;
buildingY += 250;
}
}
var patchCount = storage.patchCount || 0;
for (var i = 0; i < patchCount; i++) {
var patch = createBuilding('Pumpkin Patch', 0xFF8C00, buildingX, buildingY, 'patch_' + i);
placedBuildings.push(patch);
worldContainer.addChild(patch);
buildingX += buildingSpacing;
if (buildingX > 1800) {
buildingX = 150;
buildingY += 250;
}
}
var towerCount = storage.towerCount || 0;
for (var i = 0; i < towerCount; i++) {
var tower = createBuilding('Ghostly Tower', 0xF5F5F5, buildingX, buildingY, 'tower_' + i);
placedBuildings.push(tower);
worldContainer.addChild(tower);
buildingX += buildingSpacing;
if (buildingX > 1800) {
buildingX = 150;
buildingY += 250;
}
}
var castleCount = storage.castleCount || 0;
for (var i = 0; i < castleCount; i++) {
var castle = createBuilding('Vampire Castle', 0x8B0000, buildingX, buildingY, 'castle_' + i);
placedBuildings.push(castle);
worldContainer.addChild(castle);
buildingX += buildingSpacing;
if (buildingX > 1800) {
buildingX = 150;
buildingY += 250;
}
}
var ghostCount = storage.ghostCount || 0;
for (var i = 0; i < ghostCount; i++) {
var ghost = createBuilding('Friendly Ghost', 0xD3D3D3, buildingX, buildingY, 'ghost_' + i);
placedBuildings.push(ghost);
worldContainer.addChild(ghost);
buildingX += buildingSpacing;
if (buildingX > 1800) {
buildingX = 150;
buildingY += 250;
}
}
var wizardCount = storage.wizardCount || 0;
for (var i = 0; i < wizardCount; i++) {
var wizard = createBuilding('Wizard', 0x191970, buildingX, buildingY, 'wizard_' + i);
placedBuildings.push(wizard);
worldContainer.addChild(wizard);
buildingX += buildingSpacing;
if (buildingX > 1800) {
buildingX = 150;
buildingY += 250;
}
}
var catCount = storage.catCount || 0;
for (var i = 0; i < catCount; i++) {
var cat = createBuilding('Black Cat', 0x000000, buildingX, buildingY, 'cat_' + i);
placedBuildings.push(cat);
worldContainer.addChild(cat);
buildingX += buildingSpacing;
if (buildingX > 1800) {
buildingX = 150;
buildingY += 250;
}
}
// Apply saved positions
for (var i = 0; i < placedBuildings.length; i++) {
var building = placedBuildings[i];
if (savedPositions[building.buildingId]) {
building.x = savedPositions[building.buildingId].x;
building.y = savedPositions[building.buildingId].y;
}
}
}
// Function to create a draggable building
function createBuilding(name, color, x, y, buildingId) {
var building = new Container();
building.buildingId = buildingId;
// Create building visual - use appropriate asset based on building type
var assetName = 'hauntedMansion'; // default fallback
if (name === 'Haunted Mansion') assetName = 'hauntedMansion';else if (name === 'Witch Hut') assetName = 'witchHut';else if (name === 'Pumpkin Patch') assetName = 'pumpkinPatch';else if (name === 'Ghostly Tower') assetName = 'ghostlyTower';else if (name === 'Vampire Castle') assetName = 'vampireCastle';else if (name === 'Friendly Ghost') assetName = 'friendlyGhost';else if (name === 'Wizard') assetName = 'wizard';else if (name === 'Black Cat') assetName = 'blackCat';
var buildingShape = LK.getAsset(assetName, {
anchorX: 0.5,
anchorY: 0.5
});
building.addChild(buildingShape);
// Create building label
var buildingLabel = new Text2(name, {
size: 30,
fill: 0x000000,
fontWeight: 'bold'
});
buildingLabel.anchor.set(0.5, 0.5);
buildingLabel.y = 100;
building.addChild(buildingLabel);
building.x = x;
building.y = y;
// Make building draggable
building.isDragging = false;
building.down = function (x, y, obj) {
if (building && typeof building.x !== 'undefined' && typeof building.y !== 'undefined') {
building.isDragging = true;
// Convert coordinates to local space for proper offset calculation
var localPos = worldContainer.toLocal({
x: x,
y: y
});
building.dragOffsetX = localPos.x - building.x;
building.dragOffsetY = localPos.y - building.y;
}
};
building.up = function (x, y, obj) {
building.isDragging = false;
};
return building;
}
// Function to save world state
function saveWorldState() {
var positions = {};
for (var i = 0; i < placedBuildings.length; i++) {
var building = placedBuildings[i];
positions[building.buildingId] = {
x: building.x,
y: building.y
};
}
// Store positions as individual key-value pairs to avoid object nesting issues
for (var buildingId in positions) {
storage['buildingPos_' + buildingId + '_x'] = positions[buildingId].x;
storage['buildingPos_' + buildingId + '_y'] = positions[buildingId].y;
}
}
// Handle building dragging
var draggedBuilding = null;
// World container mouse handlers
worldContainer.move = function (x, y, obj) {
for (var i = 0; i < placedBuildings.length; i++) {
var building = placedBuildings[i];
if (building && building.isDragging) {
// Convert coordinates to world container local space
var localPos = worldContainer.toLocal({
x: x,
y: y
});
// Keep buildings within world bounds
var newX = Math.max(100, Math.min(1948, localPos.x - (building.dragOffsetX || 0)));
var newY = Math.max(600, Math.min(2300, localPos.y - (building.dragOffsetY || 0)));
building.x = newX;
building.y = newY;
break;
}
}
};
worldContainer.up = function (x, y, obj) {
for (var i = 0; i < placedBuildings.length; i++) {
var building = placedBuildings[i];
if (building.isDragging) {
building.isDragging = false;
break;
}
}
};
// Saved Games back button click handler
savedGamesBackBtn.down = function (x, y, obj) {
savedGamesContainer.visible = false;
menuContainer.visible = true;
};
// Save game slot click handlers
for (var i = 0; i < saveSlots.length; i++) {
saveSlots[i].down = function (x, y, obj) {
var slotIndex = obj.slotIndex;
var gameData = savedGames[slotIndex];
if (gameData) {
// Load existing save and go back to menu
loadGame(slotIndex);
savedGamesContainer.visible = false;
menuContainer.visible = true;
} else {
// Save current game to empty slot
saveGame(slotIndex);
}
};
}
// Shop button click handler
shopBtn.down = function (x, y, obj) {
menuContainer.visible = false;
shopContainer.visible = true;
shopCoinsTxt.setText('Coins: ' + coins);
};
// Tab switching handlers
powerUpTabBtn.down = function (x, y, obj) {
powerUpTabContainer.visible = true;
buildingTabContainer.visible = false;
powerUpTabBtn.fill = 0x4ECDC4;
buildingTabBtn.fill = 0x666666;
};
buildingTabBtn.down = function (x, y, obj) {
powerUpTabContainer.visible = false;
buildingTabContainer.visible = true;
powerUpTabBtn.fill = 0x666666;
buildingTabBtn.fill = 0x8B4513;
};
// Building purchase handlers
hauntedMansionBtn.down = function (x, y, obj) {
buyHauntedMansion();
};
witchHutBtn.down = function (x, y, obj) {
buyWitchHut();
};
pumpkinPatchBtn.down = function (x, y, obj) {
buyPumpkinPatch();
};
ghostlyTowerBtn.down = function (x, y, obj) {
buyGhostlyTower();
};
vampireCastleBtn.down = function (x, y, obj) {
buyVampireCastle();
};
friendlyGhostBtn.down = function (x, y, obj) {
buyFriendlyGhost();
};
wizardBtn.down = function (x, y, obj) {
buyWizard();
};
blackCatBtn.down = function (x, y, obj) {
buyBlackCat();
};
// Shop power-up purchase handlers
shopBombBtn.down = function (x, y, obj) {
buyBomb();
};
shopHintBtn.down = function (x, y, obj) {
buyHint();
};
shopClockBtn.down = function (x, y, obj) {
buyClock();
};
shopTimerBtn.down = function (x, y, obj) {
buyTimer();
};
shopSkipBtn.down = function (x, y, obj) {
buySkip();
};
// Back to menu button handler
shopBackBtn.down = function (x, y, obj) {
shopContainer.visible = false;
menuContainer.visible = true;
coinsInfoTxt.setText('Coins: ' + coins);
};
// Create back button for play function
var playBackBtn = new Text2('BACK TO MENU', {
size: 60,
fill: 0xCCCCCC,
fontWeight: 'bold'
});
playBackBtn.anchor.set(0.5, 0);
LK.gui.topRight.addChild(playBackBtn);
playBackBtn.x = -100;
playBackBtn.y = 100;
playBackBtn.visible = false;
// Back button click handler for play function
playBackBtn.down = function (x, y, obj) {
// Return to menu
showMenu = true;
menuContainer.visible = true;
// Hide game UI elements
movesTxt.visible = false;
timerTxt.visible = false;
coinsTxt.visible = false;
levelTxt.visible = false;
bombTxt.visible = false;
lightbulbTxt.visible = false;
clockTxt.visible = false;
timerPowerTxt.visible = false;
skipTxt.visible = false;
playBackBtn.visible = false;
// Clear existing cards
for (var i = 0; i < cards.length; i++) {
cards[i].destroy();
}
cards = [];
flippedCards = [];
// Reset game state variables
moves = 0;
gameCompleted = false;
canFlipCards = true;
gameOver = false;
// Hide level complete text if visible
levelCompleteTxt.visible = false;
// Update menu display with current values
levelInfoTxt.setText('Current Level: ' + currentLevel);
coinsInfoTxt.setText('Coins: ' + coins);
progressTxt.setText('Progress: ' + currentLevel + '/500');
};
// Play button click handler
playBtn.down = function (x, y, obj) {
// Always start from level 1 when play button is pressed
currentLevel = 1;
storage.currentLevel = currentLevel;
// Reset coin count when play button is pressed
coins = 0;
storage.coins = coins;
coinsTxt.setText('Coins: ' + coins);
coinsInfoTxt.setText('Coins: ' + coins);
levelTxt.setText('Level: ' + currentLevel);
levelInfoTxt.setText('Current Level: ' + currentLevel);
progressTxt.setText('Progress: ' + currentLevel + '/500');
// Reset game state variables for fresh start
moves = 0;
gameCompleted = false;
canFlipCards = true;
gameOver = false;
flippedCards = [];
// Clear existing cards if any
for (var i = 0; i < cards.length; i++) {
cards[i].destroy();
}
cards = [];
startGame();
};
// Hide game UI elements initially (show only in game)
movesTxt.visible = false;
timerTxt.visible = false;
coinsTxt.visible = false;
levelTxt.visible = false;
bombTxt.visible = false;
lightbulbTxt.visible = false;
clockTxt.visible = false;
timerPowerTxt.visible = false;
skipTxt.visible = false;
// Don't initialize cards immediately - wait for menu
// initializeCards(); // This will be called when starting the game
// Main game update loop
game.update = function () {
// Only update game logic if not in menu
if (!showMenu) {
// Update timer
if (!gameCompleted && !gameOver) {
var elapsedTime = Math.floor((Date.now() - gameStartTime) / 1000);
var remainingTime = Math.max(0, LEVEL_TIME_LIMIT - elapsedTime);
timerTxt.setText('Time: ' + formatTime(remainingTime));
// Check if time is up
if (remainingTime <= 0) {
gameOver = true;
// Save current level progress before game over
storage.currentLevel = currentLevel;
// Auto-save failed attempt
autoSaveGame('Failed - Time Limit');
LK.showGameOver();
}
}
}
};
// Start background music
LK.playMusic('bgmusic');