User prompt
remake shop taab
User prompt
reforged the shop tab
User prompt
items description are not touch the gray square
User prompt
I can't read items
User prompt
Make the items visible.
User prompt
change -y shop item desc.
User prompt
delete items names
User prompt
put the "health booster", "slow motion" and "automatic clicker" under
User prompt
fix the items tab
User prompt
add a visual items
User prompt
Please fix the bug: 'Cannot read properties of undefined (reading 'length')' in or related to this line: 'for (var i = 0; i < shopItemItems.length; i++) {' Line Number: 295
User prompt
add this items "health booster", "slow motion" and "automatic clicker"
User prompt
add shop character and item tabs
User prompt
add a return button for shop and leaderboard
User prompt
pause the game in main menu
User prompt
reforge the game with main menu
User prompt
add a health "3 healt"
User prompt
add a loop
User prompt
pause the game tabs
Code edit (1 edits merged)
Please save this source code
User prompt
Candy Collector Deluxe
Initial prompt
candy, collect all, menu tab, shop tab, high resulation
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
coins: 0,
unlockedSkins: ["candyRed"],
selectedSkin: "candyRed",
perks: {
extraLife: false,
slowMotion: false,
magnetCollector: false,
doubleCoins: false,
bonusPoints: false
},
unlockedLevels: [1],
levelScores: {}
});
/****
* Classes
****/
// Candy class
var Candy = Container.expand(function () {
var self = Container.call(this);
// Choose a random skin from the unlocked skins for candy variety
var skin;
if (storage.unlockedSkins && storage.unlockedSkins.length > 0) {
var randomIndex = Math.floor(Math.random() * storage.unlockedSkins.length);
skin = storage.unlockedSkins[randomIndex];
} else {
// Fallback, though unlockedSkins should always have at least 'candyRed'
skin = 'candyRed';
}
var candyAsset = self.attachAsset(skin, {
anchorX: 0.5,
anchorY: 0.5
});
self.width = candyAsset.width;
self.height = candyAsset.height;
// Falling speed
self.speed = 10 + Math.random() * 6;
// For swipe detection
self.collected = false;
// For tracking last Y for off-screen detection
self.lastY = self.y;
// For tracking if already counted as missed
self.missed = false;
// For future: skin id
self.skin = skin;
// Animate in
candyAsset.scaleX = 0.2;
candyAsset.scaleY = 0.2;
tween(candyAsset, {
scaleX: 1,
scaleY: 1
}, {
duration: 300,
easing: tween.elasticOut
});
// Update method
self.update = function () {
if (self.collected) return;
self.y += self.speed;
};
return self;
});
// Coin collect animation
var CoinFly = Container.expand(function () {
var self = Container.call(this);
var coin = self.attachAsset('coin', {
anchorX: 0.5,
anchorY: 0.5
});
self.width = coin.width;
self.height = coin.height;
self.update = function () {};
return self;
});
// Collector class (player's swipe area)
var Collector = Container.expand(function () {
var self = Container.call(this);
var collectorId = storage.selectedCollector || "collector";
var collectorAsset = self.attachAsset(collectorId, {
anchorX: 0.5,
anchorY: 0.5
});
// collectorAsset.alpha = 0.18;
// Apply magnet perk to collector size
var magnetScale = storage.perks.magnetCollector === true ? 1.5 : 1; // Apply Magnet Collector perk
collectorAsset.scaleX = magnetScale;
self.width = collectorAsset.width * magnetScale;
self.height = collectorAsset.height;
self.update = function () {};
return self;
});
// PowerUp class for special candies
var PowerUp = Container.expand(function () {
var self = Container.call(this);
// Random power-up type
var types = ['speed', 'magnet', 'shield', 'multi'];
self.type = types[Math.floor(Math.random() * types.length)];
// Visual based on type
var colors = {
speed: 0x00ffff,
magnet: 0xff00ff,
shield: 0xffff00,
multi: 0x00ff00
};
// Use 'effect' asset for magnet, shield, and slow-motion (speed) types
var assetId = self.type === 'magnet' || self.type === 'shield' || self.type === 'speed' ? 'effect' : 'candyBlue';
var powerAsset = self.attachAsset(assetId, {
anchorX: 0.5,
anchorY: 0.5,
tint: colors[self.type],
scaleX: 1.3,
scaleY: 1.3
});
// Add glow effect
tween(powerAsset, {
scaleX: 1.5,
scaleY: 1.5,
alpha: 0.7
}, {
duration: 500,
yoyo: true,
repeat: -1
});
self.width = powerAsset.width;
self.height = powerAsset.height;
self.speed = 8;
self.collected = false;
self.lastY = self.y;
self.update = function () {
if (self.collected) return;
self.y += self.speed;
};
return self;
});
// Shield collectable class
var Shield = Container.expand(function () {
var self = Container.call(this);
var shieldAsset = self.attachAsset('effect', {
anchorX: 0.5,
anchorY: 0.5,
tint: 0x00ffff,
scaleX: 1.2,
scaleY: 1.2
});
// Add pulsing effect
tween(shieldAsset, {
scaleX: 1.4,
scaleY: 1.4,
alpha: 0.8
}, {
duration: 500,
yoyo: true,
repeat: -1
});
self.width = shieldAsset.width;
self.height = shieldAsset.height;
self.speed = 8;
self.collected = false;
self.lastY = self.y;
self.update = function () {
if (self.collected) return;
self.y += self.speed;
};
return self;
});
// Shop item class
var ShopItem = Container.expand(function () {
var self = Container.call(this);
var box = self.attachAsset('shopItem', {
anchorX: 0.5,
anchorY: 0.5
});
self.icon = null;
self.price = 0;
self.skinId = '';
self.owned = false;
self.setSkin = function (skinId, price, owned) {
self.skinId = skinId;
self.price = price;
self.owned = owned;
if (self.icon) self.removeChild(self.icon);
self.icon = self.attachAsset(skinId, {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.1,
scaleY: 1.1
});
self.icon.y = -20;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x222244
});
/****
* Game Code
****/
// Background music
// Sound effects
// Shop item - gray box for UI buttons and shop items
// Collector - white rectangle for player's swipe area
// Coin - golden circle for currency
// Candy shapes - colorful circles for collectible items
// Reset all progress when game reinitializes
storage.coins = 0;
storage.unlockedSkins = ["candyRed"];
storage.selectedSkin = "candyRed";
storage.perks = {
extraLife: false,
slowMotion: false,
magnetCollector: false,
doubleCoins: false,
bonusPoints: false
};
storage.unlockedLevels = [1];
storage.levelScores = {};
storage.unlockedCollectors = ["collector"];
storage.selectedCollector = "collector";
// Game states
var gameState = 'mainMenu'; // mainMenu, levelSelect, shop, playing, settings
var currentLevel = 1;
// Audio settings
var musicEnabled = true;
var soundEnabled = true;
// UI containers
var mainMenuContainer = null;
var levelSelectContainer = null;
var shopContainer = null;
var gameplayContainer = null;
var settingsContainer = null;
var perksDisplayContainer = null; // Container for the active perks list
// Game variables
var score = 0;
var candies = [];
var shields = [];
var collector = null;
var candySpawnTimer = 0;
var levelConfig = {};
var lives = 3;
var heartsContainer = null;
// Power-up system
var powerUps = [];
var powerUpTimer = 0;
var activePowerUp = null;
var powerUpDuration = 0;
// Combo system
var combo = 0;
var maxCombo = 0;
var comboTimer = 0;
var comboDisplay = null;
// World tab filtering removed - all levels always visible
// Power-up indicator
var powerUpIndicator = null;
// Shield perk variables
var shieldActive = false;
var shieldDuration = 0;
// Music transition flags
var endlevelMusicPlayed = false;
LK.currentMusic = null; // Track current playing music
// Game stats
var perfectRun = true;
var totalCandiesCaught = 0;
// Perks system
var perks = [{
id: 'extraLife',
name: 'Extra Life',
description: '+1 starting life',
price: 250,
effect: function effect() {
return storage.perks.extraLife === true ? 1 : 0;
}
}, {
id: 'slowMotion',
name: 'Slow Motion',
description: '20% slower candies',
price: 400,
effect: function effect() {
return storage.perks.slowMotion === true ? 0.8 : 1;
}
}, {
id: 'magnetCollector',
name: 'Magnet',
description: 'Wider collection area',
price: 600,
effect: function effect() {
return storage.perks.magnetCollector === true ? 1.5 : 1;
}
}, {
id: 'doubleCoins',
name: 'Double Coins',
description: '2x coin drops',
price: 800,
effect: function effect() {
return storage.perks.doubleCoins === true ? 2 : 1;
}
}, {
id: 'bonusPoints',
name: 'Score Boost',
description: '+1 point per candy',
price: 1000,
effect: function effect() {
return storage.perks.bonusPoints === true ? 1 : 0;
}
}];
// Score display
var scoreTxt = new Text2('0', {
size: 120,
fill: '#fff'
});
scoreTxt.anchor.set(0.5, 0);
scoreTxt.visible = false;
LK.gui.top.addChild(scoreTxt);
// Coins display
var coinsTxt = new Text2('Coins: ' + storage.coins, {
size: 80,
fill: '#ffd700'
});
coinsTxt.anchor.set(1, 0);
coinsTxt.x = -20;
LK.gui.topRight.addChild(coinsTxt);
// Create main menu
function createMainMenu() {
if (mainMenuContainer) mainMenuContainer.destroy();
mainMenuContainer = new Container();
// Title
var title = new Text2('Candy Catch', {
size: 200,
fill: '#ffffff'
});
title.anchor.set(0.5, 0.5);
title.x = 1024;
title.y = 600;
mainMenuContainer.addChild(title);
// Play button
var playBtn = mainMenuContainer.addChild(new Container());
var playBg = playBtn.attachAsset('shopItem', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2,
scaleY: 0.8,
tint: 0x3bff6e
});
playBtn.x = 1024;
playBtn.y = 1200;
var playTxt = new Text2('PLAY', {
size: 100,
fill: '#ffffff'
});
playTxt.anchor.set(0.5, 0.5);
playBtn.addChild(playTxt);
playBtn.down = function () {
if (soundEnabled) {
LK.getSound('collect').play();
}
gameState = 'levelSelect';
updateGameState();
};
// Shop button
var shopBtn = mainMenuContainer.addChild(new Container());
var shopBg = shopBtn.attachAsset('shopItem', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2,
scaleY: 0.8,
tint: 0xffd700
});
shopBtn.x = 1024;
shopBtn.y = 1500;
var shopTxt = new Text2('SHOP', {
size: 100,
fill: '#ffffff'
});
shopTxt.anchor.set(0.5, 0.5);
shopBtn.addChild(shopTxt);
shopBtn.down = function () {
if (soundEnabled) {
LK.getSound('collect').play();
}
gameState = 'shop';
updateGameState();
};
// Settings button
var settingsBtn = mainMenuContainer.addChild(new Container());
var settingsBg = settingsBtn.attachAsset('shopItem', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2,
scaleY: 0.8,
tint: 0x8888ff
});
settingsBtn.x = 1024;
settingsBtn.y = 1800;
var settingsTxt = new Text2('SETTINGS', {
size: 100,
fill: '#ffffff'
});
settingsTxt.anchor.set(0.5, 0.5);
settingsBtn.addChild(settingsTxt);
settingsBtn.down = function () {
if (soundEnabled) {
LK.getSound('collect').play();
}
gameState = 'settings';
updateGameState();
};
game.addChild(mainMenuContainer);
}
// Create level select
function createLevelSelect() {
if (levelSelectContainer) levelSelectContainer.destroy();
levelSelectContainer = new Container();
// Title
var title = new Text2('Select Level', {
size: 120,
fill: '#ffffff'
});
title.anchor.set(0.5, 0.5);
title.x = 1024;
title.y = 200;
levelSelectContainer.addChild(title);
// Back button
var backBtn = levelSelectContainer.addChild(new Container());
var backBg = backBtn.attachAsset('shopItem', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 0.6,
tint: 0xff6666
});
backBtn.x = 200;
backBtn.y = 200;
var backTxt = new Text2('BACK', {
size: 60,
fill: '#ffffff'
});
backTxt.anchor.set(0.5, 0.5);
backBtn.addChild(backTxt);
backBtn.down = function () {
if (soundEnabled) {
LK.getSound('collect').play();
}
gameState = 'mainMenu';
updateGameState();
};
// World tabs removed
// Level buttons grid
var startX = 300;
var startY = 400; // Position levels higher since no world tabs
var spacing = 250;
var cols = 6;
for (var i = 0; i < 45; i++) {
var levelBtn = levelSelectContainer.addChild(new Container());
var col = i % cols;
var row = Math.floor(i / cols);
var levelNum = i + 1;
var isUnlocked = storage.unlockedLevels.indexOf(levelNum) !== -1;
var highScore = storage.levelScores[levelNum] || 0;
var targetScore = 200; // All levels target 200 score
var lvlBg = levelBtn.attachAsset('shopItem', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.8,
scaleY: 0.8,
tint: isUnlocked ? i < 10 ? 0x66ff66 : i < 20 ? 0xffff66 : i < 30 ? 0xff6666 : 0xff66ff : 0x444444
});
levelBtn.x = startX + col * spacing;
levelBtn.y = startY + row * spacing;
var lvlTxt = new Text2(isUnlocked ? levelNum.toString() : '🔒', {
size: isUnlocked ? 80 : 60,
fill: isUnlocked ? '#ffffff' : '#888888'
});
lvlTxt.anchor.set(0.5, 0.5);
lvlTxt.y = isUnlocked ? -10 : 0;
levelBtn.addChild(lvlTxt);
// Show score info for unlocked levels
if (isUnlocked && highScore > 0) {
var scoreTxt = new Text2(highScore + '/' + targetScore, {
size: 30,
fill: '#cccccc'
});
scoreTxt.anchor.set(0.5, 0.5);
scoreTxt.y = 50;
levelBtn.addChild(scoreTxt);
}
// Show unlock requirement for locked levels
if (!isUnlocked && levelNum > 1) {
var prevTargetScore = 200; // Previous level target score is 200
var reqTxt = new Text2('Need ' + prevTargetScore, {
size: 25,
fill: '#ff8888'
});
reqTxt.anchor.set(0.5, 0.5);
reqTxt.y = 50;
levelBtn.addChild(reqTxt);
}
levelBtn.levelNum = levelNum;
levelBtn.isUnlocked = isUnlocked;
levelBtn.targetScore = targetScore;
levelBtn.down = function () {
if (this.isUnlocked) {
if (soundEnabled) {
LK.getSound('collect').play();
}
// Play startlevel music when level is selected
if (musicEnabled) {
LK.currentMusic = 'startlevel';
LK.playMusic('startlevel', {
loop: true
});
}
currentLevel = this.levelNum;
gameState = 'playing';
updateGameState();
}
};
levelBtn.over = function () {
// Increase size by 5%
var currentScaleX = this.scaleX || 1;
var currentScaleY = this.scaleY || 1;
tween(this, {
scaleX: currentScaleX * 1.05,
scaleY: currentScaleY * 1.05
}, {
duration: 100
});
};
levelBtn.out = function () {
// Decrease size back to original (remove the 5% increase)
var currentScaleX = this.scaleX || 1;
var currentScaleY = this.scaleY || 1;
tween(this, {
scaleX: currentScaleX / 1.05,
scaleY: currentScaleY / 1.05
}, {
duration: 100
});
};
}
game.addChild(levelSelectContainer);
// No filtering needed - show all levels
}
// Mouse move listener for level buttons
game.move = function (x, y, obj) {
if (gameState === 'levelSelect' && levelSelectContainer) {
for (var i = 0; i < levelSelectContainer.children.length; i++) {
var child = levelSelectContainer.children[i];
if (child.isUnlocked !== undefined && child.containsPoint(game.toLocal({
x: x,
y: y
}))) {
if (!child.isHovered) {
child.isHovered = true;
child.over();
}
} else if (child.isHovered) {
child.isHovered = false;
child.out();
}
}
} else if (gameState === 'playing' && collector) {
collector.x = x;
}
};
// Create settings
function createSettings() {
if (settingsContainer) settingsContainer.destroy();
settingsContainer = new Container();
// Title
var title = new Text2('Settings', {
size: 120,
fill: '#ffffff'
});
title.anchor.set(0.5, 0.5);
title.x = 1024;
title.y = 200;
settingsContainer.addChild(title);
// Back button
var backBtn = settingsContainer.addChild(new Container());
var backBg = backBtn.attachAsset('shopItem', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 0.6,
tint: 0xff6666
});
backBtn.x = 200;
backBtn.y = 200;
var backTxt = new Text2('BACK', {
size: 60,
fill: '#ffffff'
});
backTxt.anchor.set(0.5, 0.5);
backBtn.addChild(backTxt);
backBtn.down = function () {
if (soundEnabled) {
LK.getSound('collect').play();
}
gameState = 'mainMenu';
updateGameState();
};
// Music volume control
var musicLabel = new Text2('Music Volume', {
size: 80,
fill: '#ffffff'
});
musicLabel.anchor.set(0.5, 0.5);
musicLabel.x = 1024;
musicLabel.y = 600;
settingsContainer.addChild(musicLabel);
// Music toggle button
var musicBtn = settingsContainer.addChild(new Container());
var musicBg = musicBtn.attachAsset('shopItem', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 3,
scaleY: 0.8,
tint: musicEnabled ? 0x66ff66 : 0xff6666
});
musicBtn.x = 1024;
musicBtn.y = 750;
var musicTxt = new Text2(musicEnabled ? 'MUSIC: ON' : 'MUSIC: OFF', {
size: 70,
fill: '#ffffff'
});
musicTxt.anchor.set(0.5, 0.5);
musicBtn.addChild(musicTxt);
musicBtn.musicTxt = musicTxt;
musicBtn.musicBg = musicBg;
musicBtn.down = function () {
if (soundEnabled) {
LK.getSound('collect').play();
}
musicEnabled = !musicEnabled;
this.musicTxt.setText(musicEnabled ? 'MUSIC: ON' : 'MUSIC: OFF');
this.musicBg.tint = musicEnabled ? 0x66ff66 : 0xff6666;
if (!musicEnabled) {
LK.stopMusic();
} else {
// Play main_menu track when music is enabled
LK.playMusic('main_menu', {
loop: true
});
}
};
// Sound effects volume control
var soundLabel = new Text2('Sound Effects', {
size: 80,
fill: '#ffffff'
});
soundLabel.anchor.set(0.5, 0.5);
soundLabel.x = 1024;
soundLabel.y = 1000;
settingsContainer.addChild(soundLabel);
// Sound toggle button
var soundBtn = settingsContainer.addChild(new Container());
var soundBg = soundBtn.attachAsset('shopItem', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 3,
scaleY: 0.8,
tint: soundEnabled ? 0x66ff66 : 0xff6666
});
soundBtn.x = 1024;
soundBtn.y = 1150;
var soundTxt = new Text2(soundEnabled ? 'SOUND: ON' : 'SOUND: OFF', {
size: 70,
fill: '#ffffff'
});
soundTxt.anchor.set(0.5, 0.5);
soundBtn.addChild(soundTxt);
soundBtn.soundTxt = soundTxt;
soundBtn.soundBg = soundBg;
soundBtn.down = function () {
if (soundEnabled) {
LK.getSound('collect').play();
}
soundEnabled = !soundEnabled;
this.soundTxt.setText(soundEnabled ? 'SOUND: ON' : 'SOUND: OFF');
this.soundBg.tint = soundEnabled ? 0x66ff66 : 0xff6666;
};
// Info text
var infoTxt = new Text2('Audio controls are managed by the game platform', {
size: 40,
fill: '#888888'
});
infoTxt.anchor.set(0.5, 0.5);
infoTxt.x = 1024;
infoTxt.y = 1400;
settingsContainer.addChild(infoTxt);
game.addChild(settingsContainer);
}
// Create shop
function createShop() {
if (shopContainer) shopContainer.destroy();
shopContainer = new Container();
// Title
var title = new Text2('Shop', {
size: 120,
fill: '#ffffff'
});
title.anchor.set(0.5, 0.5);
title.x = 1024;
title.y = 200;
shopContainer.addChild(title);
// Shop Tabs
var tabNames = ['Candies', 'Perks', 'Collector bar'];
var tabContainers = [];
var activeTab = 0;
var tabStartX = 700;
var tabSpacing = 350;
for (var t = 0; t < tabNames.length; t++) {
(function (tabIndex) {
var tabBtn = shopContainer.addChild(new Container());
var tabBg = tabBtn.attachAsset('shopItem', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.2,
scaleY: 0.6,
tint: tabIndex === activeTab ? 0x66ff66 : 0xcccccc
});
tabBtn.x = tabStartX + tabIndex * tabSpacing;
tabBtn.y = 350;
var tabTxt = new Text2(tabNames[tabIndex], {
size: 60,
fill: '#222'
});
tabTxt.anchor.set(0.5, 0.5);
tabBtn.addChild(tabTxt);
tabBtn.tabIndex = tabIndex;
tabBtn.down = function () {
activeTab = this.tabIndex;
// Hide all tab containers
for (var j = 0; j < tabContainers.length; j++) {
tabContainers[j].visible = false;
}
// Show selected tab
tabContainers[activeTab].visible = true;
// Update tab button tints
for (var j = 0; j < shopContainer.children.length; j++) {
var child = shopContainer.children[j];
if (child.tabIndex !== undefined) {
child.children[0].tint = child.tabIndex === activeTab ? 0x66ff66 : 0xcccccc;
}
}
};
})(t);
}
// Candies Tab Container
var candiesTab = new Container();
candiesTab.visible = true;
shopContainer.addChild(candiesTab);
tabContainers.push(candiesTab);
// Perks Tab Container
var perksTab = new Container();
perksTab.visible = false;
shopContainer.addChild(perksTab);
tabContainers.push(perksTab);
// Collector Bar Tab Container
var collectorTab = new Container();
collectorTab.visible = false;
shopContainer.addChild(collectorTab);
tabContainers.push(collectorTab);
// Back button
var backBtn = shopContainer.addChild(new Container());
var backBg = backBtn.attachAsset('shopItem', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 0.6,
tint: 0xff6666
});
backBtn.x = 200;
backBtn.y = 200;
var backTxt = new Text2('BACK', {
size: 60,
fill: '#ffffff'
});
backTxt.anchor.set(0.5, 0.5);
backBtn.addChild(backTxt);
backBtn.down = function () {
if (soundEnabled) {
LK.getSound('collect').play();
}
gameState = 'mainMenu';
updateGameState();
};
// Character skins
var skins = [{
id: 'candyRed',
price: 0,
name: 'Red'
}, {
id: 'candyBlue',
price: 100,
name: 'Blue'
}, {
id: 'candyGreen',
price: 200,
name: 'Green'
}, {
id: 'candyPurple',
price: 300,
name: 'Purple'
}, {
id: 'candyYellow',
price: 500,
name: 'Yellow'
}];
var skinsTxt = new Text2('Candies', {
size: 80,
fill: '#ffffff'
});
skinsTxt.anchor.set(0.5, 0.5);
skinsTxt.x = 1024;
skinsTxt.y = 500;
candiesTab.addChild(skinsTxt);
// Skin items
var startX = 400;
var startY = 700;
var spacing = 300;
for (var i = 0; i < skins.length; i++) {
var skin = skins[i];
var owned = storage.unlockedSkins.indexOf(skin.id) !== -1;
var skinItem = candiesTab.addChild(new Container());
skinItem.x = startX + i * spacing;
skinItem.y = startY;
var itemBg = skinItem.attachAsset('shopItem', {
anchorX: 0.5,
anchorY: 0.5,
tint: owned ? storage.selectedSkin === skin.id ? 0x66ff66 : 0xcccccc : 0x888888
});
var icon = skinItem.attachAsset(skin.id, {
anchorX: 0.5,
anchorY: 0.5,
y: -30
});
var priceTxt = new Text2(owned ? storage.selectedSkin === skin.id ? 'SELECTED' : 'OWNED' : skin.price + ' coins', {
size: 40,
fill: '#ffffff'
});
priceTxt.anchor.set(0.5, 0.5);
priceTxt.y = 60;
skinItem.addChild(priceTxt);
skinItem.skinData = skin;
skinItem.owned = owned;
skinItem.down = function () {
if (soundEnabled) {
LK.getSound('collect').play();
}
if (this.owned) {
storage.selectedSkin = this.skinData.id;
createShop();
} else if (storage.coins >= this.skinData.price) {
storage.coins -= this.skinData.price;
storage.unlockedSkins.push(this.skinData.id);
storage.selectedSkin = this.skinData.id;
coinsTxt.setText('Coins: ' + storage.coins);
createShop();
}
};
}
// Perks section
var perksTxt = new Text2('Perks', {
size: 80,
fill: '#ffffff'
});
perksTxt.anchor.set(0.5, 0.5);
perksTxt.x = 1024;
perksTxt.y = 500;
perksTab.addChild(perksTxt);
// Perk items
var perkStartY = 700;
var perkSpacing = 180;
for (var p = 0; p < perks.length; p++) {
var perk = perks[p];
var owned = storage.perks[perk.id];
var perkItem = perksTab.addChild(new Container());
perkItem.x = 1024;
perkItem.y = perkStartY + p * perkSpacing;
var perkBg = perkItem.attachAsset('shopItem', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 4,
scaleY: 0.7,
tint: owned ? 0x66ff66 : 0x555555
});
var perkName = new Text2(perk.name, {
size: 60,
fill: '#ffffff'
});
perkName.anchor.set(0, 0.5);
perkName.x = -350;
perkItem.addChild(perkName);
var perkDesc = new Text2(perk.description, {
size: 40,
fill: '#cccccc'
});
perkDesc.anchor.set(0, 0.5);
perkDesc.x = -350;
perkDesc.y = 40;
perkItem.addChild(perkDesc);
var perkPrice = new Text2(owned ? 'ACTIVE' : perk.price + ' coins', {
size: 50,
fill: owned ? '#66ff66' : '#ffd700'
});
perkPrice.anchor.set(1, 0.5);
perkPrice.x = 350;
perkItem.addChild(perkPrice);
perkItem.perkData = perk;
perkItem.owned = owned;
perkItem.down = function () {
if (soundEnabled) {
LK.getSound('collect').play();
}
if (!this.owned && storage.coins >= this.perkData.price) {
storage.coins -= this.perkData.price;
storage.perks[this.perkData.id] = true;
coinsTxt.setText('Coins: ' + storage.coins);
createShop();
}
};
}
// Collector bar tab content
var collectorTxt = new Text2('Collector bar', {
size: 80,
fill: '#ffffff'
});
collectorTxt.anchor.set(0.5, 0.5);
collectorTxt.x = 1024;
collectorTxt.y = 500;
collectorTab.addChild(collectorTxt);
// --- Collector bar shop logic ---
if (!storage.unlockedCollectors) {
storage.unlockedCollectors = ["collector"];
}
if (!storage.selectedCollector) {
storage.selectedCollector = "collector";
}
var collectors = [{
id: "collector",
name: "White",
price: 0
}, {
id: "collector_b",
name: "Pink",
price: 100
}];
var collectorStartX = 800;
var collectorStartY = 800;
var collectorSpacing = 400;
for (var i = 0; i < collectors.length; i++) {
var col = collectors[i];
var owned = storage.unlockedCollectors.indexOf(col.id) !== -1;
var selected = storage.selectedCollector === col.id;
var colItem = collectorTab.addChild(new Container());
colItem.x = collectorStartX + i * collectorSpacing;
colItem.y = collectorStartY;
var colBg = colItem.attachAsset('shopItem', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.2,
scaleY: 1.2,
tint: owned ? selected ? 0x66ff66 : 0xcccccc : 0x888888
});
var colIcon = colItem.attachAsset(col.id, {
anchorX: 0.5,
anchorY: 0.5,
y: -30
});
var colPriceTxt = new Text2(owned ? selected ? 'SELECTED' : 'OWNED' : col.price + ' coins', {
size: 40,
fill: '#ffffff'
});
colPriceTxt.anchor.set(0.5, 0.5);
colPriceTxt.y = 60;
colItem.addChild(colPriceTxt);
colItem.collectorData = col;
colItem.owned = owned;
colItem.selected = selected;
colItem.down = function () {
if (soundEnabled) {
LK.getSound('collect').play();
}
if (this.owned) {
storage.selectedCollector = this.collectorData.id;
createShop();
// If in gameplay, update collector bar immediately
if (gameState === 'playing' && typeof gameplayContainer !== 'undefined' && collector) {
var oldX = collector.x;
var oldY = collector.y;
collector.destroy();
collector = new Collector();
collector.x = oldX;
collector.y = oldY;
gameplayContainer.addChild(collector);
}
} else if (storage.coins >= this.collectorData.price) {
storage.coins -= this.collectorData.price;
storage.unlockedCollectors.push(this.collectorData.id);
storage.selectedCollector = this.collectorData.id;
coinsTxt.setText('Coins: ' + storage.coins);
createShop();
// If in gameplay, update collector bar immediately
if (gameState === 'playing' && typeof gameplayContainer !== 'undefined' && collector) {
var oldX = collector.x;
var oldY = collector.y;
collector.destroy();
collector = new Collector();
collector.x = oldX;
collector.y = oldY;
gameplayContainer.addChild(collector);
}
}
};
}
// Reset Progress button
var resetBtn = shopContainer.addChild(new Container());
var resetBg = resetBtn.attachAsset('shopItem', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.5,
scaleY: 0.8,
tint: 0xff4444
});
resetBtn.x = 1024;
resetBtn.y = 2400;
var resetTxt = new Text2('RESET PROGRESS', {
size: 70,
fill: '#ffffff'
});
resetTxt.anchor.set(0.5, 0.5);
resetBtn.addChild(resetTxt);
var confirmReset = false;
resetBtn.down = function () {
if (soundEnabled) {
LK.getSound('collect').play();
}
if (!confirmReset) {
confirmReset = true;
resetTxt.setText('CONFIRM RESET?');
resetBg.tint = 0xff0000;
// Reset confirmation after 3 seconds
LK.setTimeout(function () {
if (confirmReset) {
confirmReset = false;
resetTxt.setText('RESET PROGRESS');
resetBg.tint = 0xff4444;
}
}, 3000);
} else {
// Reset all progress
storage.coins = 0;
storage.unlockedSkins = ["candyRed"];
storage.selectedSkin = "candyRed";
storage.perks = {
extraLife: false,
slowMotion: false,
magnetCollector: false,
doubleCoins: false,
bonusPoints: false
};
storage.unlockedLevels = [1];
storage.levelScores = {};
// Update UI
coinsTxt.setText('Coins: 0');
confirmReset = false;
resetTxt.setText('PROGRESS RESET!');
resetBg.tint = 0x44ff44;
// Refresh shop after a moment
LK.setTimeout(function () {
createShop();
}, 1000);
}
};
game.addChild(shopContainer);
}
// Start gameplay
function startGameplay() {
if (gameplayContainer) gameplayContainer.destroy();
gameplayContainer = new Container();
game.addChild(gameplayContainer);
// Reset game variables
score = 0;
candies = [];
candySpawnTimer = 0;
lives = 3 + (storage.perks.extraLife === true ? 1 : 0); // Apply Extra Life perk
scoreTxt.setText('0');
scoreTxt.visible = true;
shields = [];
shieldSpawnTimer = 900; // First shield after 15 seconds
// Reset power-up system
powerUps = [];
powerUpTimer = 300; // First power-up after 5 seconds
activePowerUp = null;
powerUpDuration = 0;
// Reset combo system
combo = 0;
maxCombo = 0;
comboTimer = 0;
if (comboDisplay) comboDisplay.visible = false;
// Reset shield state
shieldActive = false;
shieldDuration = 0;
// Reset music flags
endlevelMusicPlayed = false;
// Reset stats
perfectRun = true;
totalCandiesCaught = 0;
// Level configuration
levelConfig = {
spawnRate: Math.max(30 - currentLevel, 10),
candySpeed: 8 + currentLevel * 0.5,
targetScore: 200 // All levels target 200 score
};
// Set level-specific background colors
var bgColor;
if (currentLevel <= 10) {
// Levels 1-10: Blue gradient
bgColor = 0x2255aa + currentLevel * 0x000505;
} else if (currentLevel <= 20) {
// Levels 11-20: Purple gradient
bgColor = 0x6633aa + (currentLevel - 10) * 0x050005;
} else if (currentLevel <= 30) {
// Levels 21-30: Red gradient
bgColor = 0xaa3355 + (currentLevel - 20) * 0x050000;
} else {
// Levels 31-45: Green gradient
bgColor = 0x33aa55 + (currentLevel - 30) * 0x000505;
}
game.setBackgroundColor(bgColor);
// Add background decorative elements
var bgElements = new Container();
gameplayContainer.addChild(bgElements);
// Add subtle background circles for depth
for (var b = 0; b < 8; b++) {
var bgCircle = bgElements.attachAsset('candyBlue', {
anchorX: 0.5,
anchorY: 0.5,
x: 200 + Math.random() * 1648,
y: 400 + Math.random() * 2000,
scaleX: 2 + Math.random() * 3,
scaleY: 2 + Math.random() * 3,
alpha: 0.05 + Math.random() * 0.05,
tint: 0xffffff
});
}
// Create collector
collector = new Collector();
collector.x = 1024;
collector.y = 2400;
gameplayContainer.addChild(collector);
// Level info
var levelTxt = new Text2('Level ' + currentLevel + ' - Collect ' + levelConfig.targetScore, {
size: 60,
fill: '#ffffff'
});
levelTxt.anchor.set(0.5, 0.5);
levelTxt.x = 1024;
levelTxt.y = 300;
gameplayContainer.addChild(levelTxt);
// Create hearts display
heartsContainer = new Container();
heartsContainer.x = 1024;
heartsContainer.y = 150;
gameplayContainer.addChild(heartsContainer);
// Add hearts based on lives
for (var h = 0; h < lives; h++) {
var heart = heartsContainer.attachAsset('candyRed', {
anchorX: 0.5,
anchorY: 0.5,
x: (h - lives / 2 + 0.5) * 140,
scaleX: 0.8,
scaleY: 0.8,
tint: 0xff6666
});
}
// Combo display
comboDisplay = new Container();
comboDisplay.x = 1024;
comboDisplay.y = 450;
gameplayContainer.addChild(comboDisplay);
var comboText = new Text2('', {
size: 80,
fill: '#ffff00'
});
comboText.anchor.set(0.5, 0.5);
comboDisplay.addChild(comboText);
comboDisplay.comboText = comboText;
comboDisplay.visible = false;
// Power-up indicator
powerUpIndicator = new Container();
powerUpIndicator.x = 1024;
powerUpIndicator.y = 600;
gameplayContainer.addChild(powerUpIndicator);
var powerUpText = new Text2('', {
size: 60,
fill: '#ffffff'
});
powerUpText.anchor.set(0.5, 0.5);
powerUpIndicator.addChild(powerUpText);
powerUpIndicator.powerUpText = powerUpText;
powerUpIndicator.visible = false;
// Perks display
if (perksDisplayContainer) {
perksDisplayContainer.destroy();
perksDisplayContainer = null;
}
perksDisplayContainer = new Container();
perksDisplayContainer.x = 150; // Positioned to avoid top-left menu icon and provide left padding
perksDisplayContainer.y = 2732 - 400; // Positioned towards the bottom-left
gameplayContainer.addChild(perksDisplayContainer);
perksDisplayContainer.visible = false; // Default to hidden
var displayedPerkY = 0;
var hasActivePerks = false;
// First, check if any perks are active to determine if the title is needed
for (var k_check = 0; k_check < perks.length; k_check++) {
if (storage.perks[perks[k_check].id] === true) {
hasActivePerks = true;
break;
}
}
if (hasActivePerks) {
var perksListTitle = new Text2('Active Perks:', {
size: 45,
// Readable size for a title
fill: '#ffffff'
});
perksListTitle.anchor.set(0, 0); // Top-left anchor
perksListTitle.y = displayedPerkY;
perksDisplayContainer.addChild(perksListTitle);
displayedPerkY += perksListTitle.height + 10; // Add spacing below title
for (var k = 0; k < perks.length; k++) {
if (storage.perks[perks[k].id] === true) {
// Explicitly check for true
// Check if the perk is active
var perkNameText = new Text2('- ' + perks[k].name, {
size: 35,
// Slightly smaller size for perk names
fill: '#aaffaa' // A light green color to indicate active status
});
perkNameText.anchor.set(0, 0); // Top-left anchor
perkNameText.y = displayedPerkY;
perksDisplayContainer.addChild(perkNameText);
displayedPerkY += perkNameText.height + 5; // Spacing between perk names
}
}
perksDisplayContainer.visible = true; // Show perks list if active perks exist
}
// Back button removed - no exit button during gameplay
}
// Update game state
function updateGameState() {
// Hide all containers
if (mainMenuContainer) mainMenuContainer.visible = false;
if (levelSelectContainer) levelSelectContainer.visible = false;
if (shopContainer) shopContainer.visible = false;
if (gameplayContainer) gameplayContainer.visible = false;
if (settingsContainer) settingsContainer.visible = false;
if (perksDisplayContainer) perksDisplayContainer.visible = false; // Hide perks list
scoreTxt.visible = false;
// Show current state
switch (gameState) {
case 'mainMenu':
game.setBackgroundColor(0x222244); // Reset to original background
createMainMenu();
if (musicEnabled) {
LK.currentMusic = 'main_menu';
LK.playMusic('main_menu', {
loop: true
});
}
break;
case 'levelSelect':
game.setBackgroundColor(0x222244); // Reset to original background
createLevelSelect();
break;
case 'shop':
game.setBackgroundColor(0x222244); // Reset to original background
createShop();
break;
case 'playing':
startGameplay();
break;
case 'settings':
game.setBackgroundColor(0x222244); // Reset to original background
createSettings();
break;
}
}
// Game update
game.update = function () {
if (gameState !== 'playing') return;
// Spawn candies
candySpawnTimer--;
if (candySpawnTimer <= 0) {
var candy = new Candy();
candy.x = 200 + Math.random() * 1648;
candy.y = -100;
candy.speed = levelConfig.candySpeed * (storage.perks.slowMotion === true ? 0.8 : 1); // Apply Slow Motion perk
if (activePowerUp === 'speed') {
candy.speed *= 0.5; // Halve the speed during power-up
}
candies.push(candy);
gameplayContainer.addChild(candy);
candySpawnTimer = levelConfig.spawnRate;
}
// Spawn power-ups
powerUpTimer--;
if (powerUpTimer <= 0) {
var powerUp = new PowerUp();
powerUp.x = 200 + Math.random() * 1648;
powerUp.y = -100;
powerUps.push(powerUp);
gameplayContainer.addChild(powerUp);
powerUpTimer = 600 + Math.random() * 600; // Next power-up in 10-20 seconds
}
// Spawn shields
shieldSpawnTimer--;
if (shieldSpawnTimer <= 0) {
var shield = new Shield();
shield.x = 200 + Math.random() * 1648;
shield.y = -100;
shields.push(shield);
gameplayContainer.addChild(shield);
shieldSpawnTimer = 1200 + Math.random() * 600; // Next shield in 20-30 seconds
}
// Update power-ups
for (var p = powerUps.length - 1; p >= 0; p--) {
var powerUp = powerUps[p];
powerUp.update();
// Check collection
if (!powerUp.collected && powerUp.intersects(collector)) {
powerUp.collected = true;
// Activate power-up
activePowerUp = powerUp.type;
powerUpDuration = 300; // 5 seconds
// Show power-up indicator
var powerUpNames = {
speed: 'SLOW MOTION!',
magnet: 'MAGNET MODE!',
shield: 'SHIELD ACTIVE!',
multi: '2X POINTS!'
};
powerUpIndicator.powerUpText.setText(powerUpNames[powerUp.type]);
powerUpIndicator.visible = true;
// Apply immediate effects
if (powerUp.type === 'magnet') {
tween(collector, {
scaleX: 2,
scaleY: 1
}, {
duration: 300
});
}
// If slow-motion, after 10 seconds, collect all candies
if (powerUp.type === 'speed') {
LK.setTimeout(function () {
// Only apply if still in gameplay and candies exist
if (gameState === 'playing' && candies && candies.length > 0) {
// Copy candies array to avoid mutation issues
var candiesToCollect = candies.slice();
for (var i = candiesToCollect.length - 1; i >= 0; i--) {
var candy = candiesToCollect[i];
if (!candy.collected) {
// Simulate collection as in normal gameplay
candy.collected = true;
totalCandiesCaught++;
combo++;
comboTimer = 120;
if (combo > maxCombo) maxCombo = combo;
var points = 1 + (storage.perks.bonusPoints === true ? 1 : 0);
if (activePowerUp === 'multi') points *= 2;
if (combo >= 5) points += Math.floor(combo / 5);
score += points;
scoreTxt.setText(score.toString());
if (combo >= 3) {
comboDisplay.visible = true;
comboDisplay.comboText.setText('COMBO x' + combo + '!');
tween(comboDisplay, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 200,
yoyo: true
});
}
if (combo === 10 || combo === 20 || combo === 30) {
LK.effects.flashScreen(0xffff00, 300);
}
if (soundEnabled) {
LK.getSound('collect').play();
}
// Normal collection animation
tween(candy, {
scaleX: 1.5,
scaleY: 1.5,
alpha: 0
}, {
duration: 200,
onComplete: function (c) {
return function () {
c.destroy();
};
}(candy)
});
// Award coins based on candy skin
var minCoins, maxCoins;
switch (candy.skin) {
case 'candyRed':
minCoins = 1;
maxCoins = 5;
break;
case 'candyBlue':
minCoins = 5;
maxCoins = 10;
break;
case 'candyGreen':
minCoins = 10;
maxCoins = 20;
break;
case 'candyPurple':
minCoins = 20;
maxCoins = 40;
break;
case 'candyYellow':
minCoins = 40;
maxCoins = 80;
break;
default:
minCoins = 1;
maxCoins = 5;
break;
}
var coinAmount = Math.floor(Math.random() * (maxCoins - minCoins + 1)) + minCoins;
coinAmount *= storage.perks.doubleCoins === true ? 2 : 1;
storage.coins += coinAmount;
coinsTxt.setText('Coins: ' + storage.coins);
var coin = new CoinFly();
coin.x = candy.x;
coin.y = candy.y;
gameplayContainer.addChild(coin);
tween(coin, {
x: 2048 - 100,
y: 50,
alpha: 0
}, {
duration: 800,
onComplete: function onComplete() {
coin.destroy();
}
});
// Remove candy from candies array
var idx = candies.indexOf(candy);
if (idx !== -1) {
candies.splice(idx, 1);
}
}
}
}
}, 10000); // 10 seconds
}
powerUp.destroy();
powerUps.splice(p, 1);
}
// Remove off-screen
if (powerUp.y > 2832) {
powerUp.destroy();
powerUps.splice(p, 1);
}
}
// Update active power-up
if (powerUpDuration > 0) {
powerUpDuration--;
// Show remaining time
if (powerUpDuration % 60 === 0) {
powerUpIndicator.powerUpText.setText(Math.floor(powerUpDuration / 60) + 's');
}
if (powerUpDuration === 0) {
// Deactivate power-up
powerUpIndicator.visible = false;
activePowerUp = null;
// Reset collector size if magnet was active
tween(collector, {
scaleX: 1,
scaleY: 1
}, {
duration: 300
});
}
}
// Update combo timer
if (comboTimer > 0) {
comboTimer--;
if (comboTimer === 0) {
combo = 0;
comboDisplay.visible = false;
}
}
// Update shields
for (var s = shields.length - 1; s >= 0; s--) {
var shield = shields[s];
shield.update();
// Check collection
if (!shield.collected && shield.intersects(collector)) {
shield.collected = true;
// Shield effect explicitly disabled as per request regarding a "green shield perk".
// shieldActive = true; // Protective effect disabled
// shieldDuration = 300; // Protective effect disabled
// Optional: Provide minimal feedback that something was collected but had no protective effect.
// LK.effects.flashScreen(0x888888, 100); // A dim flash for an ineffective shield.
// Keep the collection sound for consistency, or change/remove if it's misleading.
if (soundEnabled) {
LK.getSound('collect').play();
}
// The powerUpIndicator will not show 'SHIELD ACTIVE!' from this collection.
// Animation
tween(shield, {
scaleX: 2,
scaleY: 2,
alpha: 0
}, {
duration: 300,
onComplete: function onComplete() {
shield.destroy();
}
});
shields.splice(s, 1);
}
// Remove off-screen
if (shield.y > 2832) {
shield.destroy();
shields.splice(s, 1);
}
}
// Update shield duration
if (shieldActive && shieldDuration > 0) {
shieldDuration--;
// Show remaining time
if (shieldDuration % 60 === 0 && powerUpIndicator) {
powerUpIndicator.powerUpText.setText('SHIELD: ' + Math.floor(shieldDuration / 60) + 's');
}
if (shieldDuration === 0) {
// Deactivate shield
shieldActive = false;
if (powerUpIndicator && activePowerUp === null) {
powerUpIndicator.visible = false;
}
}
}
// Update candies
for (var i = candies.length - 1; i >= 0; i--) {
var candy = candies[i];
candy.update();
// Check collection
var collectRadius = activePowerUp === 'magnet' ? 300 : 0;
var dx = candy.x - collector.x;
var dy = candy.y - collector.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (!candy.collected && (candy.intersects(collector) || activePowerUp === 'magnet' && distance < collectRadius)) {
candy.collected = true;
totalCandiesCaught++;
// Update combo
combo++;
comboTimer = 120; // 2 seconds to maintain combo
if (combo > maxCombo) maxCombo = combo;
// Calculate points with power-up and combo
var points = 1 + (storage.perks.bonusPoints === true ? 1 : 0); // Apply Bonus Points perk
if (activePowerUp === 'multi') points *= 2; // Double points power-up
if (combo >= 5) points += Math.floor(combo / 5); // Bonus for combo
score += points;
scoreTxt.setText(score.toString());
// Show combo display
if (combo >= 3) {
comboDisplay.visible = true;
comboDisplay.comboText.setText('COMBO x' + combo + '!');
// Animate combo text
tween(comboDisplay, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 200,
yoyo: true
});
}
// Special effects for high combos
if (combo === 10 || combo === 20 || combo === 30) {
LK.effects.flashScreen(0xffff00, 300);
}
if (soundEnabled) {
LK.getSound('collect').play();
}
// Magnet effect animation
if (activePowerUp === 'magnet' && distance > 100) {
tween(candy, {
x: collector.x,
y: collector.y,
alpha: 0,
scaleX: 0.1,
scaleY: 0.1
}, {
duration: 200,
onComplete: function onComplete() {
candy.destroy();
}
});
} else {
// Normal collection animation
tween(candy, {
scaleX: 1.5,
scaleY: 1.5,
alpha: 0
}, {
duration: 200,
onComplete: function onComplete() {
candy.destroy();
}
});
}
// Award coins based on candy skin
var minCoins, maxCoins;
switch (candy.skin) {
case 'candyRed':
minCoins = 1;
maxCoins = 5;
break;
case 'candyBlue':
minCoins = 5;
maxCoins = 10;
break;
case 'candyGreen':
minCoins = 10;
maxCoins = 20;
break;
case 'candyPurple':
minCoins = 20;
maxCoins = 40;
break;
case 'candyYellow':
minCoins = 40;
maxCoins = 80;
break;
default:
minCoins = 1;
maxCoins = 5;
break;
// Fallback for any other skins
}
var coinAmount = Math.floor(Math.random() * (maxCoins - minCoins + 1)) + minCoins;
coinAmount *= storage.perks.doubleCoins === true ? 2 : 1; // Apply Double Coins perk
storage.coins += coinAmount;
coinsTxt.setText('Coins: ' + storage.coins);
// Coin animation
var coin = new CoinFly();
coin.x = candy.x;
coin.y = candy.y;
gameplayContainer.addChild(coin);
tween(coin, {
x: 2048 - 100,
// Target near the coin display
y: 50,
alpha: 0
}, {
duration: 800,
onComplete: function onComplete() {
coin.destroy();
}
});
// Remove candy
candy.destroy();
candies.splice(i, 1);
// Check music transitions based on score
var progress = score / levelConfig.targetScore;
// Music transitions at 15% and 35%
if (progress >= 0.15 && progress < 0.35 && musicEnabled) {
// Play levelmid at 15%
if (LK.currentMusic !== 'levelmid') {
LK.currentMusic = 'levelmid';
LK.playMusic('levelmid', {
loop: true
});
}
} else if (progress >= 0.35 && musicEnabled && !endlevelMusicPlayed) {
// Play endlevel at 35%
endlevelMusicPlayed = true;
LK.currentMusic = 'endlevel';
LK.playMusic('endlevel', {
loop: false
});
// Increase candy speed when at 35% progress
levelConfig.candySpeed *= 1.5; // Increase speed by 50%
}
// Check win
if (score >= levelConfig.targetScore) {
// Calculate bonus coins
var bonusCoins = currentLevel * 10; // Base level bonus
if (perfectRun) bonusCoins += 50; // Perfect run bonus
if (maxCombo >= 10) bonusCoins += maxCombo * 2; // Combo bonus
if (totalCandiesCaught >= levelConfig.targetScore * 1.5) bonusCoins += 30; // Efficiency bonus
storage.coins += bonusCoins;
coinsTxt.setText('Coins: ' + storage.coins);
// Show bonus notification
var bonusText = new Text2('+' + bonusCoins + ' coins!', {
size: 100,
fill: '#ffd700'
});
bonusText.anchor.set(0.5, 0.5);
bonusText.x = 1024;
bonusText.y = 1366;
gameplayContainer.addChild(bonusText);
tween(bonusText, {
y: 1100,
alpha: 0
}, {
duration: 2000,
onComplete: function onComplete() {
bonusText.destroy();
}
});
// Save high score for current level
if (!storage.levelScores[currentLevel] || score > storage.levelScores[currentLevel]) {
storage.levelScores[currentLevel] = score;
}
// Unlock next level if not already unlocked
var nextLevel = currentLevel + 1;
if (nextLevel <= 45 && storage.unlockedLevels.indexOf(nextLevel) === -1) {
storage.unlockedLevels.push(nextLevel);
storage.unlockedLevels.sort(function (a, b) {
return a - b;
}); // Keep levels sorted
}
// Stop any playing music
if (musicEnabled) {
LK.stopMusic();
LK.currentMusic = null;
}
// Return to levels tab
gameState = 'levelSelect';
updateGameState();
// Don't show regular "you win!" tab
// LK.showYouWin();
}
// Check progress - return to levels tab at 100%
else if (progress >= 1.0) {
// Stop any playing music
if (musicEnabled) {
LK.stopMusic();
LK.currentMusic = null;
}
// Return to levels tab
gameState = 'levelSelect';
updateGameState();
return; // Exit early to prevent further processing
}
}
// Remove off-screen
if (candy.y > 2832) {
if (!candy.missed) {
candy.missed = true;
// Shield power-up prevents life loss
if (activePowerUp !== 'shield' && !shieldActive) {
lives--;
perfectRun = false;
combo = 0; // Reset combo on miss
comboDisplay.visible = false;
} else if (activePowerUp === 'shield') {
// Shield power-up absorbs the hit
LK.effects.flashScreen(0xffff00, 200);
activePowerUp = null;
powerUpDuration = 0;
powerUpIndicator.visible = false;
} else if (shieldActive) {
// Shield perk absorbs the hit
LK.effects.flashScreen(0x00ffff, 200);
shieldActive = false;
shieldDuration = 0;
if (powerUpIndicator && activePowerUp === null) {
powerUpIndicator.visible = false;
}
}
// Update hearts display
if (heartsContainer && heartsContainer.children.length > lives) {
var heartToRemove = heartsContainer.children[lives];
if (heartToRemove && heartToRemove.visible) {
tween(heartToRemove, {
alpha: 0,
scaleX: 0.1,
scaleY: 0.1
}, {
duration: 300,
onComplete: function onComplete() {
heartToRemove.visible = false;
}
});
}
}
// Check game over
if (lives <= 0) {
// Stop any playing music
if (musicEnabled) {
LK.stopMusic();
LK.currentMusic = null;
}
// Return to levels tab
gameState = 'levelSelect';
updateGameState();
// Don't show regular game over screen
}
}
candy.destroy();
candies.splice(i, 1);
}
}
};
// Touch controls for collector
game.down = function (x, y, obj) {
if (gameState === 'playing' && collector) {
collector.x = x;
}
};
game.move = function (x, y, obj) {
if (gameState === 'playing' && collector) {
collector.x = x;
}
};
// Initialize game
updateGameState(); /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
coins: 0,
unlockedSkins: ["candyRed"],
selectedSkin: "candyRed",
perks: {
extraLife: false,
slowMotion: false,
magnetCollector: false,
doubleCoins: false,
bonusPoints: false
},
unlockedLevels: [1],
levelScores: {}
});
/****
* Classes
****/
// Candy class
var Candy = Container.expand(function () {
var self = Container.call(this);
// Choose a random skin from the unlocked skins for candy variety
var skin;
if (storage.unlockedSkins && storage.unlockedSkins.length > 0) {
var randomIndex = Math.floor(Math.random() * storage.unlockedSkins.length);
skin = storage.unlockedSkins[randomIndex];
} else {
// Fallback, though unlockedSkins should always have at least 'candyRed'
skin = 'candyRed';
}
var candyAsset = self.attachAsset(skin, {
anchorX: 0.5,
anchorY: 0.5
});
self.width = candyAsset.width;
self.height = candyAsset.height;
// Falling speed
self.speed = 10 + Math.random() * 6;
// For swipe detection
self.collected = false;
// For tracking last Y for off-screen detection
self.lastY = self.y;
// For tracking if already counted as missed
self.missed = false;
// For future: skin id
self.skin = skin;
// Animate in
candyAsset.scaleX = 0.2;
candyAsset.scaleY = 0.2;
tween(candyAsset, {
scaleX: 1,
scaleY: 1
}, {
duration: 300,
easing: tween.elasticOut
});
// Update method
self.update = function () {
if (self.collected) return;
self.y += self.speed;
};
return self;
});
// Coin collect animation
var CoinFly = Container.expand(function () {
var self = Container.call(this);
var coin = self.attachAsset('coin', {
anchorX: 0.5,
anchorY: 0.5
});
self.width = coin.width;
self.height = coin.height;
self.update = function () {};
return self;
});
// Collector class (player's swipe area)
var Collector = Container.expand(function () {
var self = Container.call(this);
var collectorId = storage.selectedCollector || "collector";
var collectorAsset = self.attachAsset(collectorId, {
anchorX: 0.5,
anchorY: 0.5
});
// collectorAsset.alpha = 0.18;
// Apply magnet perk to collector size
var magnetScale = storage.perks.magnetCollector === true ? 1.5 : 1; // Apply Magnet Collector perk
collectorAsset.scaleX = magnetScale;
self.width = collectorAsset.width * magnetScale;
self.height = collectorAsset.height;
self.update = function () {};
return self;
});
// PowerUp class for special candies
var PowerUp = Container.expand(function () {
var self = Container.call(this);
// Random power-up type
var types = ['speed', 'magnet', 'shield', 'multi'];
self.type = types[Math.floor(Math.random() * types.length)];
// Visual based on type
var colors = {
speed: 0x00ffff,
magnet: 0xff00ff,
shield: 0xffff00,
multi: 0x00ff00
};
// Use 'effect' asset for magnet, shield, and slow-motion (speed) types
var assetId = self.type === 'magnet' || self.type === 'shield' || self.type === 'speed' ? 'effect' : 'candyBlue';
var powerAsset = self.attachAsset(assetId, {
anchorX: 0.5,
anchorY: 0.5,
tint: colors[self.type],
scaleX: 1.3,
scaleY: 1.3
});
// Add glow effect
tween(powerAsset, {
scaleX: 1.5,
scaleY: 1.5,
alpha: 0.7
}, {
duration: 500,
yoyo: true,
repeat: -1
});
self.width = powerAsset.width;
self.height = powerAsset.height;
self.speed = 8;
self.collected = false;
self.lastY = self.y;
self.update = function () {
if (self.collected) return;
self.y += self.speed;
};
return self;
});
// Shield collectable class
var Shield = Container.expand(function () {
var self = Container.call(this);
var shieldAsset = self.attachAsset('effect', {
anchorX: 0.5,
anchorY: 0.5,
tint: 0x00ffff,
scaleX: 1.2,
scaleY: 1.2
});
// Add pulsing effect
tween(shieldAsset, {
scaleX: 1.4,
scaleY: 1.4,
alpha: 0.8
}, {
duration: 500,
yoyo: true,
repeat: -1
});
self.width = shieldAsset.width;
self.height = shieldAsset.height;
self.speed = 8;
self.collected = false;
self.lastY = self.y;
self.update = function () {
if (self.collected) return;
self.y += self.speed;
};
return self;
});
// Shop item class
var ShopItem = Container.expand(function () {
var self = Container.call(this);
var box = self.attachAsset('shopItem', {
anchorX: 0.5,
anchorY: 0.5
});
self.icon = null;
self.price = 0;
self.skinId = '';
self.owned = false;
self.setSkin = function (skinId, price, owned) {
self.skinId = skinId;
self.price = price;
self.owned = owned;
if (self.icon) self.removeChild(self.icon);
self.icon = self.attachAsset(skinId, {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.1,
scaleY: 1.1
});
self.icon.y = -20;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x222244
});
/****
* Game Code
****/
// Background music
// Sound effects
// Shop item - gray box for UI buttons and shop items
// Collector - white rectangle for player's swipe area
// Coin - golden circle for currency
// Candy shapes - colorful circles for collectible items
// Reset all progress when game reinitializes
storage.coins = 0;
storage.unlockedSkins = ["candyRed"];
storage.selectedSkin = "candyRed";
storage.perks = {
extraLife: false,
slowMotion: false,
magnetCollector: false,
doubleCoins: false,
bonusPoints: false
};
storage.unlockedLevels = [1];
storage.levelScores = {};
storage.unlockedCollectors = ["collector"];
storage.selectedCollector = "collector";
// Game states
var gameState = 'mainMenu'; // mainMenu, levelSelect, shop, playing, settings
var currentLevel = 1;
// Audio settings
var musicEnabled = true;
var soundEnabled = true;
// UI containers
var mainMenuContainer = null;
var levelSelectContainer = null;
var shopContainer = null;
var gameplayContainer = null;
var settingsContainer = null;
var perksDisplayContainer = null; // Container for the active perks list
// Game variables
var score = 0;
var candies = [];
var shields = [];
var collector = null;
var candySpawnTimer = 0;
var levelConfig = {};
var lives = 3;
var heartsContainer = null;
// Power-up system
var powerUps = [];
var powerUpTimer = 0;
var activePowerUp = null;
var powerUpDuration = 0;
// Combo system
var combo = 0;
var maxCombo = 0;
var comboTimer = 0;
var comboDisplay = null;
// World tab filtering removed - all levels always visible
// Power-up indicator
var powerUpIndicator = null;
// Shield perk variables
var shieldActive = false;
var shieldDuration = 0;
// Music transition flags
var endlevelMusicPlayed = false;
LK.currentMusic = null; // Track current playing music
// Game stats
var perfectRun = true;
var totalCandiesCaught = 0;
// Perks system
var perks = [{
id: 'extraLife',
name: 'Extra Life',
description: '+1 starting life',
price: 250,
effect: function effect() {
return storage.perks.extraLife === true ? 1 : 0;
}
}, {
id: 'slowMotion',
name: 'Slow Motion',
description: '20% slower candies',
price: 400,
effect: function effect() {
return storage.perks.slowMotion === true ? 0.8 : 1;
}
}, {
id: 'magnetCollector',
name: 'Magnet',
description: 'Wider collection area',
price: 600,
effect: function effect() {
return storage.perks.magnetCollector === true ? 1.5 : 1;
}
}, {
id: 'doubleCoins',
name: 'Double Coins',
description: '2x coin drops',
price: 800,
effect: function effect() {
return storage.perks.doubleCoins === true ? 2 : 1;
}
}, {
id: 'bonusPoints',
name: 'Score Boost',
description: '+1 point per candy',
price: 1000,
effect: function effect() {
return storage.perks.bonusPoints === true ? 1 : 0;
}
}];
// Score display
var scoreTxt = new Text2('0', {
size: 120,
fill: '#fff'
});
scoreTxt.anchor.set(0.5, 0);
scoreTxt.visible = false;
LK.gui.top.addChild(scoreTxt);
// Coins display
var coinsTxt = new Text2('Coins: ' + storage.coins, {
size: 80,
fill: '#ffd700'
});
coinsTxt.anchor.set(1, 0);
coinsTxt.x = -20;
LK.gui.topRight.addChild(coinsTxt);
// Create main menu
function createMainMenu() {
if (mainMenuContainer) mainMenuContainer.destroy();
mainMenuContainer = new Container();
// Title
var title = new Text2('Candy Catch', {
size: 200,
fill: '#ffffff'
});
title.anchor.set(0.5, 0.5);
title.x = 1024;
title.y = 600;
mainMenuContainer.addChild(title);
// Play button
var playBtn = mainMenuContainer.addChild(new Container());
var playBg = playBtn.attachAsset('shopItem', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2,
scaleY: 0.8,
tint: 0x3bff6e
});
playBtn.x = 1024;
playBtn.y = 1200;
var playTxt = new Text2('PLAY', {
size: 100,
fill: '#ffffff'
});
playTxt.anchor.set(0.5, 0.5);
playBtn.addChild(playTxt);
playBtn.down = function () {
if (soundEnabled) {
LK.getSound('collect').play();
}
gameState = 'levelSelect';
updateGameState();
};
// Shop button
var shopBtn = mainMenuContainer.addChild(new Container());
var shopBg = shopBtn.attachAsset('shopItem', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2,
scaleY: 0.8,
tint: 0xffd700
});
shopBtn.x = 1024;
shopBtn.y = 1500;
var shopTxt = new Text2('SHOP', {
size: 100,
fill: '#ffffff'
});
shopTxt.anchor.set(0.5, 0.5);
shopBtn.addChild(shopTxt);
shopBtn.down = function () {
if (soundEnabled) {
LK.getSound('collect').play();
}
gameState = 'shop';
updateGameState();
};
// Settings button
var settingsBtn = mainMenuContainer.addChild(new Container());
var settingsBg = settingsBtn.attachAsset('shopItem', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2,
scaleY: 0.8,
tint: 0x8888ff
});
settingsBtn.x = 1024;
settingsBtn.y = 1800;
var settingsTxt = new Text2('SETTINGS', {
size: 100,
fill: '#ffffff'
});
settingsTxt.anchor.set(0.5, 0.5);
settingsBtn.addChild(settingsTxt);
settingsBtn.down = function () {
if (soundEnabled) {
LK.getSound('collect').play();
}
gameState = 'settings';
updateGameState();
};
game.addChild(mainMenuContainer);
}
// Create level select
function createLevelSelect() {
if (levelSelectContainer) levelSelectContainer.destroy();
levelSelectContainer = new Container();
// Title
var title = new Text2('Select Level', {
size: 120,
fill: '#ffffff'
});
title.anchor.set(0.5, 0.5);
title.x = 1024;
title.y = 200;
levelSelectContainer.addChild(title);
// Back button
var backBtn = levelSelectContainer.addChild(new Container());
var backBg = backBtn.attachAsset('shopItem', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 0.6,
tint: 0xff6666
});
backBtn.x = 200;
backBtn.y = 200;
var backTxt = new Text2('BACK', {
size: 60,
fill: '#ffffff'
});
backTxt.anchor.set(0.5, 0.5);
backBtn.addChild(backTxt);
backBtn.down = function () {
if (soundEnabled) {
LK.getSound('collect').play();
}
gameState = 'mainMenu';
updateGameState();
};
// World tabs removed
// Level buttons grid
var startX = 300;
var startY = 400; // Position levels higher since no world tabs
var spacing = 250;
var cols = 6;
for (var i = 0; i < 45; i++) {
var levelBtn = levelSelectContainer.addChild(new Container());
var col = i % cols;
var row = Math.floor(i / cols);
var levelNum = i + 1;
var isUnlocked = storage.unlockedLevels.indexOf(levelNum) !== -1;
var highScore = storage.levelScores[levelNum] || 0;
var targetScore = 200; // All levels target 200 score
var lvlBg = levelBtn.attachAsset('shopItem', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.8,
scaleY: 0.8,
tint: isUnlocked ? i < 10 ? 0x66ff66 : i < 20 ? 0xffff66 : i < 30 ? 0xff6666 : 0xff66ff : 0x444444
});
levelBtn.x = startX + col * spacing;
levelBtn.y = startY + row * spacing;
var lvlTxt = new Text2(isUnlocked ? levelNum.toString() : '🔒', {
size: isUnlocked ? 80 : 60,
fill: isUnlocked ? '#ffffff' : '#888888'
});
lvlTxt.anchor.set(0.5, 0.5);
lvlTxt.y = isUnlocked ? -10 : 0;
levelBtn.addChild(lvlTxt);
// Show score info for unlocked levels
if (isUnlocked && highScore > 0) {
var scoreTxt = new Text2(highScore + '/' + targetScore, {
size: 30,
fill: '#cccccc'
});
scoreTxt.anchor.set(0.5, 0.5);
scoreTxt.y = 50;
levelBtn.addChild(scoreTxt);
}
// Show unlock requirement for locked levels
if (!isUnlocked && levelNum > 1) {
var prevTargetScore = 200; // Previous level target score is 200
var reqTxt = new Text2('Need ' + prevTargetScore, {
size: 25,
fill: '#ff8888'
});
reqTxt.anchor.set(0.5, 0.5);
reqTxt.y = 50;
levelBtn.addChild(reqTxt);
}
levelBtn.levelNum = levelNum;
levelBtn.isUnlocked = isUnlocked;
levelBtn.targetScore = targetScore;
levelBtn.down = function () {
if (this.isUnlocked) {
if (soundEnabled) {
LK.getSound('collect').play();
}
// Play startlevel music when level is selected
if (musicEnabled) {
LK.currentMusic = 'startlevel';
LK.playMusic('startlevel', {
loop: true
});
}
currentLevel = this.levelNum;
gameState = 'playing';
updateGameState();
}
};
levelBtn.over = function () {
// Increase size by 5%
var currentScaleX = this.scaleX || 1;
var currentScaleY = this.scaleY || 1;
tween(this, {
scaleX: currentScaleX * 1.05,
scaleY: currentScaleY * 1.05
}, {
duration: 100
});
};
levelBtn.out = function () {
// Decrease size back to original (remove the 5% increase)
var currentScaleX = this.scaleX || 1;
var currentScaleY = this.scaleY || 1;
tween(this, {
scaleX: currentScaleX / 1.05,
scaleY: currentScaleY / 1.05
}, {
duration: 100
});
};
}
game.addChild(levelSelectContainer);
// No filtering needed - show all levels
}
// Mouse move listener for level buttons
game.move = function (x, y, obj) {
if (gameState === 'levelSelect' && levelSelectContainer) {
for (var i = 0; i < levelSelectContainer.children.length; i++) {
var child = levelSelectContainer.children[i];
if (child.isUnlocked !== undefined && child.containsPoint(game.toLocal({
x: x,
y: y
}))) {
if (!child.isHovered) {
child.isHovered = true;
child.over();
}
} else if (child.isHovered) {
child.isHovered = false;
child.out();
}
}
} else if (gameState === 'playing' && collector) {
collector.x = x;
}
};
// Create settings
function createSettings() {
if (settingsContainer) settingsContainer.destroy();
settingsContainer = new Container();
// Title
var title = new Text2('Settings', {
size: 120,
fill: '#ffffff'
});
title.anchor.set(0.5, 0.5);
title.x = 1024;
title.y = 200;
settingsContainer.addChild(title);
// Back button
var backBtn = settingsContainer.addChild(new Container());
var backBg = backBtn.attachAsset('shopItem', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 0.6,
tint: 0xff6666
});
backBtn.x = 200;
backBtn.y = 200;
var backTxt = new Text2('BACK', {
size: 60,
fill: '#ffffff'
});
backTxt.anchor.set(0.5, 0.5);
backBtn.addChild(backTxt);
backBtn.down = function () {
if (soundEnabled) {
LK.getSound('collect').play();
}
gameState = 'mainMenu';
updateGameState();
};
// Music volume control
var musicLabel = new Text2('Music Volume', {
size: 80,
fill: '#ffffff'
});
musicLabel.anchor.set(0.5, 0.5);
musicLabel.x = 1024;
musicLabel.y = 600;
settingsContainer.addChild(musicLabel);
// Music toggle button
var musicBtn = settingsContainer.addChild(new Container());
var musicBg = musicBtn.attachAsset('shopItem', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 3,
scaleY: 0.8,
tint: musicEnabled ? 0x66ff66 : 0xff6666
});
musicBtn.x = 1024;
musicBtn.y = 750;
var musicTxt = new Text2(musicEnabled ? 'MUSIC: ON' : 'MUSIC: OFF', {
size: 70,
fill: '#ffffff'
});
musicTxt.anchor.set(0.5, 0.5);
musicBtn.addChild(musicTxt);
musicBtn.musicTxt = musicTxt;
musicBtn.musicBg = musicBg;
musicBtn.down = function () {
if (soundEnabled) {
LK.getSound('collect').play();
}
musicEnabled = !musicEnabled;
this.musicTxt.setText(musicEnabled ? 'MUSIC: ON' : 'MUSIC: OFF');
this.musicBg.tint = musicEnabled ? 0x66ff66 : 0xff6666;
if (!musicEnabled) {
LK.stopMusic();
} else {
// Play main_menu track when music is enabled
LK.playMusic('main_menu', {
loop: true
});
}
};
// Sound effects volume control
var soundLabel = new Text2('Sound Effects', {
size: 80,
fill: '#ffffff'
});
soundLabel.anchor.set(0.5, 0.5);
soundLabel.x = 1024;
soundLabel.y = 1000;
settingsContainer.addChild(soundLabel);
// Sound toggle button
var soundBtn = settingsContainer.addChild(new Container());
var soundBg = soundBtn.attachAsset('shopItem', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 3,
scaleY: 0.8,
tint: soundEnabled ? 0x66ff66 : 0xff6666
});
soundBtn.x = 1024;
soundBtn.y = 1150;
var soundTxt = new Text2(soundEnabled ? 'SOUND: ON' : 'SOUND: OFF', {
size: 70,
fill: '#ffffff'
});
soundTxt.anchor.set(0.5, 0.5);
soundBtn.addChild(soundTxt);
soundBtn.soundTxt = soundTxt;
soundBtn.soundBg = soundBg;
soundBtn.down = function () {
if (soundEnabled) {
LK.getSound('collect').play();
}
soundEnabled = !soundEnabled;
this.soundTxt.setText(soundEnabled ? 'SOUND: ON' : 'SOUND: OFF');
this.soundBg.tint = soundEnabled ? 0x66ff66 : 0xff6666;
};
// Info text
var infoTxt = new Text2('Audio controls are managed by the game platform', {
size: 40,
fill: '#888888'
});
infoTxt.anchor.set(0.5, 0.5);
infoTxt.x = 1024;
infoTxt.y = 1400;
settingsContainer.addChild(infoTxt);
game.addChild(settingsContainer);
}
// Create shop
function createShop() {
if (shopContainer) shopContainer.destroy();
shopContainer = new Container();
// Title
var title = new Text2('Shop', {
size: 120,
fill: '#ffffff'
});
title.anchor.set(0.5, 0.5);
title.x = 1024;
title.y = 200;
shopContainer.addChild(title);
// Shop Tabs
var tabNames = ['Candies', 'Perks', 'Collector bar'];
var tabContainers = [];
var activeTab = 0;
var tabStartX = 700;
var tabSpacing = 350;
for (var t = 0; t < tabNames.length; t++) {
(function (tabIndex) {
var tabBtn = shopContainer.addChild(new Container());
var tabBg = tabBtn.attachAsset('shopItem', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.2,
scaleY: 0.6,
tint: tabIndex === activeTab ? 0x66ff66 : 0xcccccc
});
tabBtn.x = tabStartX + tabIndex * tabSpacing;
tabBtn.y = 350;
var tabTxt = new Text2(tabNames[tabIndex], {
size: 60,
fill: '#222'
});
tabTxt.anchor.set(0.5, 0.5);
tabBtn.addChild(tabTxt);
tabBtn.tabIndex = tabIndex;
tabBtn.down = function () {
activeTab = this.tabIndex;
// Hide all tab containers
for (var j = 0; j < tabContainers.length; j++) {
tabContainers[j].visible = false;
}
// Show selected tab
tabContainers[activeTab].visible = true;
// Update tab button tints
for (var j = 0; j < shopContainer.children.length; j++) {
var child = shopContainer.children[j];
if (child.tabIndex !== undefined) {
child.children[0].tint = child.tabIndex === activeTab ? 0x66ff66 : 0xcccccc;
}
}
};
})(t);
}
// Candies Tab Container
var candiesTab = new Container();
candiesTab.visible = true;
shopContainer.addChild(candiesTab);
tabContainers.push(candiesTab);
// Perks Tab Container
var perksTab = new Container();
perksTab.visible = false;
shopContainer.addChild(perksTab);
tabContainers.push(perksTab);
// Collector Bar Tab Container
var collectorTab = new Container();
collectorTab.visible = false;
shopContainer.addChild(collectorTab);
tabContainers.push(collectorTab);
// Back button
var backBtn = shopContainer.addChild(new Container());
var backBg = backBtn.attachAsset('shopItem', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 0.6,
tint: 0xff6666
});
backBtn.x = 200;
backBtn.y = 200;
var backTxt = new Text2('BACK', {
size: 60,
fill: '#ffffff'
});
backTxt.anchor.set(0.5, 0.5);
backBtn.addChild(backTxt);
backBtn.down = function () {
if (soundEnabled) {
LK.getSound('collect').play();
}
gameState = 'mainMenu';
updateGameState();
};
// Character skins
var skins = [{
id: 'candyRed',
price: 0,
name: 'Red'
}, {
id: 'candyBlue',
price: 100,
name: 'Blue'
}, {
id: 'candyGreen',
price: 200,
name: 'Green'
}, {
id: 'candyPurple',
price: 300,
name: 'Purple'
}, {
id: 'candyYellow',
price: 500,
name: 'Yellow'
}];
var skinsTxt = new Text2('Candies', {
size: 80,
fill: '#ffffff'
});
skinsTxt.anchor.set(0.5, 0.5);
skinsTxt.x = 1024;
skinsTxt.y = 500;
candiesTab.addChild(skinsTxt);
// Skin items
var startX = 400;
var startY = 700;
var spacing = 300;
for (var i = 0; i < skins.length; i++) {
var skin = skins[i];
var owned = storage.unlockedSkins.indexOf(skin.id) !== -1;
var skinItem = candiesTab.addChild(new Container());
skinItem.x = startX + i * spacing;
skinItem.y = startY;
var itemBg = skinItem.attachAsset('shopItem', {
anchorX: 0.5,
anchorY: 0.5,
tint: owned ? storage.selectedSkin === skin.id ? 0x66ff66 : 0xcccccc : 0x888888
});
var icon = skinItem.attachAsset(skin.id, {
anchorX: 0.5,
anchorY: 0.5,
y: -30
});
var priceTxt = new Text2(owned ? storage.selectedSkin === skin.id ? 'SELECTED' : 'OWNED' : skin.price + ' coins', {
size: 40,
fill: '#ffffff'
});
priceTxt.anchor.set(0.5, 0.5);
priceTxt.y = 60;
skinItem.addChild(priceTxt);
skinItem.skinData = skin;
skinItem.owned = owned;
skinItem.down = function () {
if (soundEnabled) {
LK.getSound('collect').play();
}
if (this.owned) {
storage.selectedSkin = this.skinData.id;
createShop();
} else if (storage.coins >= this.skinData.price) {
storage.coins -= this.skinData.price;
storage.unlockedSkins.push(this.skinData.id);
storage.selectedSkin = this.skinData.id;
coinsTxt.setText('Coins: ' + storage.coins);
createShop();
}
};
}
// Perks section
var perksTxt = new Text2('Perks', {
size: 80,
fill: '#ffffff'
});
perksTxt.anchor.set(0.5, 0.5);
perksTxt.x = 1024;
perksTxt.y = 500;
perksTab.addChild(perksTxt);
// Perk items
var perkStartY = 700;
var perkSpacing = 180;
for (var p = 0; p < perks.length; p++) {
var perk = perks[p];
var owned = storage.perks[perk.id];
var perkItem = perksTab.addChild(new Container());
perkItem.x = 1024;
perkItem.y = perkStartY + p * perkSpacing;
var perkBg = perkItem.attachAsset('shopItem', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 4,
scaleY: 0.7,
tint: owned ? 0x66ff66 : 0x555555
});
var perkName = new Text2(perk.name, {
size: 60,
fill: '#ffffff'
});
perkName.anchor.set(0, 0.5);
perkName.x = -350;
perkItem.addChild(perkName);
var perkDesc = new Text2(perk.description, {
size: 40,
fill: '#cccccc'
});
perkDesc.anchor.set(0, 0.5);
perkDesc.x = -350;
perkDesc.y = 40;
perkItem.addChild(perkDesc);
var perkPrice = new Text2(owned ? 'ACTIVE' : perk.price + ' coins', {
size: 50,
fill: owned ? '#66ff66' : '#ffd700'
});
perkPrice.anchor.set(1, 0.5);
perkPrice.x = 350;
perkItem.addChild(perkPrice);
perkItem.perkData = perk;
perkItem.owned = owned;
perkItem.down = function () {
if (soundEnabled) {
LK.getSound('collect').play();
}
if (!this.owned && storage.coins >= this.perkData.price) {
storage.coins -= this.perkData.price;
storage.perks[this.perkData.id] = true;
coinsTxt.setText('Coins: ' + storage.coins);
createShop();
}
};
}
// Collector bar tab content
var collectorTxt = new Text2('Collector bar', {
size: 80,
fill: '#ffffff'
});
collectorTxt.anchor.set(0.5, 0.5);
collectorTxt.x = 1024;
collectorTxt.y = 500;
collectorTab.addChild(collectorTxt);
// --- Collector bar shop logic ---
if (!storage.unlockedCollectors) {
storage.unlockedCollectors = ["collector"];
}
if (!storage.selectedCollector) {
storage.selectedCollector = "collector";
}
var collectors = [{
id: "collector",
name: "White",
price: 0
}, {
id: "collector_b",
name: "Pink",
price: 100
}];
var collectorStartX = 800;
var collectorStartY = 800;
var collectorSpacing = 400;
for (var i = 0; i < collectors.length; i++) {
var col = collectors[i];
var owned = storage.unlockedCollectors.indexOf(col.id) !== -1;
var selected = storage.selectedCollector === col.id;
var colItem = collectorTab.addChild(new Container());
colItem.x = collectorStartX + i * collectorSpacing;
colItem.y = collectorStartY;
var colBg = colItem.attachAsset('shopItem', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.2,
scaleY: 1.2,
tint: owned ? selected ? 0x66ff66 : 0xcccccc : 0x888888
});
var colIcon = colItem.attachAsset(col.id, {
anchorX: 0.5,
anchorY: 0.5,
y: -30
});
var colPriceTxt = new Text2(owned ? selected ? 'SELECTED' : 'OWNED' : col.price + ' coins', {
size: 40,
fill: '#ffffff'
});
colPriceTxt.anchor.set(0.5, 0.5);
colPriceTxt.y = 60;
colItem.addChild(colPriceTxt);
colItem.collectorData = col;
colItem.owned = owned;
colItem.selected = selected;
colItem.down = function () {
if (soundEnabled) {
LK.getSound('collect').play();
}
if (this.owned) {
storage.selectedCollector = this.collectorData.id;
createShop();
// If in gameplay, update collector bar immediately
if (gameState === 'playing' && typeof gameplayContainer !== 'undefined' && collector) {
var oldX = collector.x;
var oldY = collector.y;
collector.destroy();
collector = new Collector();
collector.x = oldX;
collector.y = oldY;
gameplayContainer.addChild(collector);
}
} else if (storage.coins >= this.collectorData.price) {
storage.coins -= this.collectorData.price;
storage.unlockedCollectors.push(this.collectorData.id);
storage.selectedCollector = this.collectorData.id;
coinsTxt.setText('Coins: ' + storage.coins);
createShop();
// If in gameplay, update collector bar immediately
if (gameState === 'playing' && typeof gameplayContainer !== 'undefined' && collector) {
var oldX = collector.x;
var oldY = collector.y;
collector.destroy();
collector = new Collector();
collector.x = oldX;
collector.y = oldY;
gameplayContainer.addChild(collector);
}
}
};
}
// Reset Progress button
var resetBtn = shopContainer.addChild(new Container());
var resetBg = resetBtn.attachAsset('shopItem', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.5,
scaleY: 0.8,
tint: 0xff4444
});
resetBtn.x = 1024;
resetBtn.y = 2400;
var resetTxt = new Text2('RESET PROGRESS', {
size: 70,
fill: '#ffffff'
});
resetTxt.anchor.set(0.5, 0.5);
resetBtn.addChild(resetTxt);
var confirmReset = false;
resetBtn.down = function () {
if (soundEnabled) {
LK.getSound('collect').play();
}
if (!confirmReset) {
confirmReset = true;
resetTxt.setText('CONFIRM RESET?');
resetBg.tint = 0xff0000;
// Reset confirmation after 3 seconds
LK.setTimeout(function () {
if (confirmReset) {
confirmReset = false;
resetTxt.setText('RESET PROGRESS');
resetBg.tint = 0xff4444;
}
}, 3000);
} else {
// Reset all progress
storage.coins = 0;
storage.unlockedSkins = ["candyRed"];
storage.selectedSkin = "candyRed";
storage.perks = {
extraLife: false,
slowMotion: false,
magnetCollector: false,
doubleCoins: false,
bonusPoints: false
};
storage.unlockedLevels = [1];
storage.levelScores = {};
// Update UI
coinsTxt.setText('Coins: 0');
confirmReset = false;
resetTxt.setText('PROGRESS RESET!');
resetBg.tint = 0x44ff44;
// Refresh shop after a moment
LK.setTimeout(function () {
createShop();
}, 1000);
}
};
game.addChild(shopContainer);
}
// Start gameplay
function startGameplay() {
if (gameplayContainer) gameplayContainer.destroy();
gameplayContainer = new Container();
game.addChild(gameplayContainer);
// Reset game variables
score = 0;
candies = [];
candySpawnTimer = 0;
lives = 3 + (storage.perks.extraLife === true ? 1 : 0); // Apply Extra Life perk
scoreTxt.setText('0');
scoreTxt.visible = true;
shields = [];
shieldSpawnTimer = 900; // First shield after 15 seconds
// Reset power-up system
powerUps = [];
powerUpTimer = 300; // First power-up after 5 seconds
activePowerUp = null;
powerUpDuration = 0;
// Reset combo system
combo = 0;
maxCombo = 0;
comboTimer = 0;
if (comboDisplay) comboDisplay.visible = false;
// Reset shield state
shieldActive = false;
shieldDuration = 0;
// Reset music flags
endlevelMusicPlayed = false;
// Reset stats
perfectRun = true;
totalCandiesCaught = 0;
// Level configuration
levelConfig = {
spawnRate: Math.max(30 - currentLevel, 10),
candySpeed: 8 + currentLevel * 0.5,
targetScore: 200 // All levels target 200 score
};
// Set level-specific background colors
var bgColor;
if (currentLevel <= 10) {
// Levels 1-10: Blue gradient
bgColor = 0x2255aa + currentLevel * 0x000505;
} else if (currentLevel <= 20) {
// Levels 11-20: Purple gradient
bgColor = 0x6633aa + (currentLevel - 10) * 0x050005;
} else if (currentLevel <= 30) {
// Levels 21-30: Red gradient
bgColor = 0xaa3355 + (currentLevel - 20) * 0x050000;
} else {
// Levels 31-45: Green gradient
bgColor = 0x33aa55 + (currentLevel - 30) * 0x000505;
}
game.setBackgroundColor(bgColor);
// Add background decorative elements
var bgElements = new Container();
gameplayContainer.addChild(bgElements);
// Add subtle background circles for depth
for (var b = 0; b < 8; b++) {
var bgCircle = bgElements.attachAsset('candyBlue', {
anchorX: 0.5,
anchorY: 0.5,
x: 200 + Math.random() * 1648,
y: 400 + Math.random() * 2000,
scaleX: 2 + Math.random() * 3,
scaleY: 2 + Math.random() * 3,
alpha: 0.05 + Math.random() * 0.05,
tint: 0xffffff
});
}
// Create collector
collector = new Collector();
collector.x = 1024;
collector.y = 2400;
gameplayContainer.addChild(collector);
// Level info
var levelTxt = new Text2('Level ' + currentLevel + ' - Collect ' + levelConfig.targetScore, {
size: 60,
fill: '#ffffff'
});
levelTxt.anchor.set(0.5, 0.5);
levelTxt.x = 1024;
levelTxt.y = 300;
gameplayContainer.addChild(levelTxt);
// Create hearts display
heartsContainer = new Container();
heartsContainer.x = 1024;
heartsContainer.y = 150;
gameplayContainer.addChild(heartsContainer);
// Add hearts based on lives
for (var h = 0; h < lives; h++) {
var heart = heartsContainer.attachAsset('candyRed', {
anchorX: 0.5,
anchorY: 0.5,
x: (h - lives / 2 + 0.5) * 140,
scaleX: 0.8,
scaleY: 0.8,
tint: 0xff6666
});
}
// Combo display
comboDisplay = new Container();
comboDisplay.x = 1024;
comboDisplay.y = 450;
gameplayContainer.addChild(comboDisplay);
var comboText = new Text2('', {
size: 80,
fill: '#ffff00'
});
comboText.anchor.set(0.5, 0.5);
comboDisplay.addChild(comboText);
comboDisplay.comboText = comboText;
comboDisplay.visible = false;
// Power-up indicator
powerUpIndicator = new Container();
powerUpIndicator.x = 1024;
powerUpIndicator.y = 600;
gameplayContainer.addChild(powerUpIndicator);
var powerUpText = new Text2('', {
size: 60,
fill: '#ffffff'
});
powerUpText.anchor.set(0.5, 0.5);
powerUpIndicator.addChild(powerUpText);
powerUpIndicator.powerUpText = powerUpText;
powerUpIndicator.visible = false;
// Perks display
if (perksDisplayContainer) {
perksDisplayContainer.destroy();
perksDisplayContainer = null;
}
perksDisplayContainer = new Container();
perksDisplayContainer.x = 150; // Positioned to avoid top-left menu icon and provide left padding
perksDisplayContainer.y = 2732 - 400; // Positioned towards the bottom-left
gameplayContainer.addChild(perksDisplayContainer);
perksDisplayContainer.visible = false; // Default to hidden
var displayedPerkY = 0;
var hasActivePerks = false;
// First, check if any perks are active to determine if the title is needed
for (var k_check = 0; k_check < perks.length; k_check++) {
if (storage.perks[perks[k_check].id] === true) {
hasActivePerks = true;
break;
}
}
if (hasActivePerks) {
var perksListTitle = new Text2('Active Perks:', {
size: 45,
// Readable size for a title
fill: '#ffffff'
});
perksListTitle.anchor.set(0, 0); // Top-left anchor
perksListTitle.y = displayedPerkY;
perksDisplayContainer.addChild(perksListTitle);
displayedPerkY += perksListTitle.height + 10; // Add spacing below title
for (var k = 0; k < perks.length; k++) {
if (storage.perks[perks[k].id] === true) {
// Explicitly check for true
// Check if the perk is active
var perkNameText = new Text2('- ' + perks[k].name, {
size: 35,
// Slightly smaller size for perk names
fill: '#aaffaa' // A light green color to indicate active status
});
perkNameText.anchor.set(0, 0); // Top-left anchor
perkNameText.y = displayedPerkY;
perksDisplayContainer.addChild(perkNameText);
displayedPerkY += perkNameText.height + 5; // Spacing between perk names
}
}
perksDisplayContainer.visible = true; // Show perks list if active perks exist
}
// Back button removed - no exit button during gameplay
}
// Update game state
function updateGameState() {
// Hide all containers
if (mainMenuContainer) mainMenuContainer.visible = false;
if (levelSelectContainer) levelSelectContainer.visible = false;
if (shopContainer) shopContainer.visible = false;
if (gameplayContainer) gameplayContainer.visible = false;
if (settingsContainer) settingsContainer.visible = false;
if (perksDisplayContainer) perksDisplayContainer.visible = false; // Hide perks list
scoreTxt.visible = false;
// Show current state
switch (gameState) {
case 'mainMenu':
game.setBackgroundColor(0x222244); // Reset to original background
createMainMenu();
if (musicEnabled) {
LK.currentMusic = 'main_menu';
LK.playMusic('main_menu', {
loop: true
});
}
break;
case 'levelSelect':
game.setBackgroundColor(0x222244); // Reset to original background
createLevelSelect();
break;
case 'shop':
game.setBackgroundColor(0x222244); // Reset to original background
createShop();
break;
case 'playing':
startGameplay();
break;
case 'settings':
game.setBackgroundColor(0x222244); // Reset to original background
createSettings();
break;
}
}
// Game update
game.update = function () {
if (gameState !== 'playing') return;
// Spawn candies
candySpawnTimer--;
if (candySpawnTimer <= 0) {
var candy = new Candy();
candy.x = 200 + Math.random() * 1648;
candy.y = -100;
candy.speed = levelConfig.candySpeed * (storage.perks.slowMotion === true ? 0.8 : 1); // Apply Slow Motion perk
if (activePowerUp === 'speed') {
candy.speed *= 0.5; // Halve the speed during power-up
}
candies.push(candy);
gameplayContainer.addChild(candy);
candySpawnTimer = levelConfig.spawnRate;
}
// Spawn power-ups
powerUpTimer--;
if (powerUpTimer <= 0) {
var powerUp = new PowerUp();
powerUp.x = 200 + Math.random() * 1648;
powerUp.y = -100;
powerUps.push(powerUp);
gameplayContainer.addChild(powerUp);
powerUpTimer = 600 + Math.random() * 600; // Next power-up in 10-20 seconds
}
// Spawn shields
shieldSpawnTimer--;
if (shieldSpawnTimer <= 0) {
var shield = new Shield();
shield.x = 200 + Math.random() * 1648;
shield.y = -100;
shields.push(shield);
gameplayContainer.addChild(shield);
shieldSpawnTimer = 1200 + Math.random() * 600; // Next shield in 20-30 seconds
}
// Update power-ups
for (var p = powerUps.length - 1; p >= 0; p--) {
var powerUp = powerUps[p];
powerUp.update();
// Check collection
if (!powerUp.collected && powerUp.intersects(collector)) {
powerUp.collected = true;
// Activate power-up
activePowerUp = powerUp.type;
powerUpDuration = 300; // 5 seconds
// Show power-up indicator
var powerUpNames = {
speed: 'SLOW MOTION!',
magnet: 'MAGNET MODE!',
shield: 'SHIELD ACTIVE!',
multi: '2X POINTS!'
};
powerUpIndicator.powerUpText.setText(powerUpNames[powerUp.type]);
powerUpIndicator.visible = true;
// Apply immediate effects
if (powerUp.type === 'magnet') {
tween(collector, {
scaleX: 2,
scaleY: 1
}, {
duration: 300
});
}
// If slow-motion, after 10 seconds, collect all candies
if (powerUp.type === 'speed') {
LK.setTimeout(function () {
// Only apply if still in gameplay and candies exist
if (gameState === 'playing' && candies && candies.length > 0) {
// Copy candies array to avoid mutation issues
var candiesToCollect = candies.slice();
for (var i = candiesToCollect.length - 1; i >= 0; i--) {
var candy = candiesToCollect[i];
if (!candy.collected) {
// Simulate collection as in normal gameplay
candy.collected = true;
totalCandiesCaught++;
combo++;
comboTimer = 120;
if (combo > maxCombo) maxCombo = combo;
var points = 1 + (storage.perks.bonusPoints === true ? 1 : 0);
if (activePowerUp === 'multi') points *= 2;
if (combo >= 5) points += Math.floor(combo / 5);
score += points;
scoreTxt.setText(score.toString());
if (combo >= 3) {
comboDisplay.visible = true;
comboDisplay.comboText.setText('COMBO x' + combo + '!');
tween(comboDisplay, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 200,
yoyo: true
});
}
if (combo === 10 || combo === 20 || combo === 30) {
LK.effects.flashScreen(0xffff00, 300);
}
if (soundEnabled) {
LK.getSound('collect').play();
}
// Normal collection animation
tween(candy, {
scaleX: 1.5,
scaleY: 1.5,
alpha: 0
}, {
duration: 200,
onComplete: function (c) {
return function () {
c.destroy();
};
}(candy)
});
// Award coins based on candy skin
var minCoins, maxCoins;
switch (candy.skin) {
case 'candyRed':
minCoins = 1;
maxCoins = 5;
break;
case 'candyBlue':
minCoins = 5;
maxCoins = 10;
break;
case 'candyGreen':
minCoins = 10;
maxCoins = 20;
break;
case 'candyPurple':
minCoins = 20;
maxCoins = 40;
break;
case 'candyYellow':
minCoins = 40;
maxCoins = 80;
break;
default:
minCoins = 1;
maxCoins = 5;
break;
}
var coinAmount = Math.floor(Math.random() * (maxCoins - minCoins + 1)) + minCoins;
coinAmount *= storage.perks.doubleCoins === true ? 2 : 1;
storage.coins += coinAmount;
coinsTxt.setText('Coins: ' + storage.coins);
var coin = new CoinFly();
coin.x = candy.x;
coin.y = candy.y;
gameplayContainer.addChild(coin);
tween(coin, {
x: 2048 - 100,
y: 50,
alpha: 0
}, {
duration: 800,
onComplete: function onComplete() {
coin.destroy();
}
});
// Remove candy from candies array
var idx = candies.indexOf(candy);
if (idx !== -1) {
candies.splice(idx, 1);
}
}
}
}
}, 10000); // 10 seconds
}
powerUp.destroy();
powerUps.splice(p, 1);
}
// Remove off-screen
if (powerUp.y > 2832) {
powerUp.destroy();
powerUps.splice(p, 1);
}
}
// Update active power-up
if (powerUpDuration > 0) {
powerUpDuration--;
// Show remaining time
if (powerUpDuration % 60 === 0) {
powerUpIndicator.powerUpText.setText(Math.floor(powerUpDuration / 60) + 's');
}
if (powerUpDuration === 0) {
// Deactivate power-up
powerUpIndicator.visible = false;
activePowerUp = null;
// Reset collector size if magnet was active
tween(collector, {
scaleX: 1,
scaleY: 1
}, {
duration: 300
});
}
}
// Update combo timer
if (comboTimer > 0) {
comboTimer--;
if (comboTimer === 0) {
combo = 0;
comboDisplay.visible = false;
}
}
// Update shields
for (var s = shields.length - 1; s >= 0; s--) {
var shield = shields[s];
shield.update();
// Check collection
if (!shield.collected && shield.intersects(collector)) {
shield.collected = true;
// Shield effect explicitly disabled as per request regarding a "green shield perk".
// shieldActive = true; // Protective effect disabled
// shieldDuration = 300; // Protective effect disabled
// Optional: Provide minimal feedback that something was collected but had no protective effect.
// LK.effects.flashScreen(0x888888, 100); // A dim flash for an ineffective shield.
// Keep the collection sound for consistency, or change/remove if it's misleading.
if (soundEnabled) {
LK.getSound('collect').play();
}
// The powerUpIndicator will not show 'SHIELD ACTIVE!' from this collection.
// Animation
tween(shield, {
scaleX: 2,
scaleY: 2,
alpha: 0
}, {
duration: 300,
onComplete: function onComplete() {
shield.destroy();
}
});
shields.splice(s, 1);
}
// Remove off-screen
if (shield.y > 2832) {
shield.destroy();
shields.splice(s, 1);
}
}
// Update shield duration
if (shieldActive && shieldDuration > 0) {
shieldDuration--;
// Show remaining time
if (shieldDuration % 60 === 0 && powerUpIndicator) {
powerUpIndicator.powerUpText.setText('SHIELD: ' + Math.floor(shieldDuration / 60) + 's');
}
if (shieldDuration === 0) {
// Deactivate shield
shieldActive = false;
if (powerUpIndicator && activePowerUp === null) {
powerUpIndicator.visible = false;
}
}
}
// Update candies
for (var i = candies.length - 1; i >= 0; i--) {
var candy = candies[i];
candy.update();
// Check collection
var collectRadius = activePowerUp === 'magnet' ? 300 : 0;
var dx = candy.x - collector.x;
var dy = candy.y - collector.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (!candy.collected && (candy.intersects(collector) || activePowerUp === 'magnet' && distance < collectRadius)) {
candy.collected = true;
totalCandiesCaught++;
// Update combo
combo++;
comboTimer = 120; // 2 seconds to maintain combo
if (combo > maxCombo) maxCombo = combo;
// Calculate points with power-up and combo
var points = 1 + (storage.perks.bonusPoints === true ? 1 : 0); // Apply Bonus Points perk
if (activePowerUp === 'multi') points *= 2; // Double points power-up
if (combo >= 5) points += Math.floor(combo / 5); // Bonus for combo
score += points;
scoreTxt.setText(score.toString());
// Show combo display
if (combo >= 3) {
comboDisplay.visible = true;
comboDisplay.comboText.setText('COMBO x' + combo + '!');
// Animate combo text
tween(comboDisplay, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 200,
yoyo: true
});
}
// Special effects for high combos
if (combo === 10 || combo === 20 || combo === 30) {
LK.effects.flashScreen(0xffff00, 300);
}
if (soundEnabled) {
LK.getSound('collect').play();
}
// Magnet effect animation
if (activePowerUp === 'magnet' && distance > 100) {
tween(candy, {
x: collector.x,
y: collector.y,
alpha: 0,
scaleX: 0.1,
scaleY: 0.1
}, {
duration: 200,
onComplete: function onComplete() {
candy.destroy();
}
});
} else {
// Normal collection animation
tween(candy, {
scaleX: 1.5,
scaleY: 1.5,
alpha: 0
}, {
duration: 200,
onComplete: function onComplete() {
candy.destroy();
}
});
}
// Award coins based on candy skin
var minCoins, maxCoins;
switch (candy.skin) {
case 'candyRed':
minCoins = 1;
maxCoins = 5;
break;
case 'candyBlue':
minCoins = 5;
maxCoins = 10;
break;
case 'candyGreen':
minCoins = 10;
maxCoins = 20;
break;
case 'candyPurple':
minCoins = 20;
maxCoins = 40;
break;
case 'candyYellow':
minCoins = 40;
maxCoins = 80;
break;
default:
minCoins = 1;
maxCoins = 5;
break;
// Fallback for any other skins
}
var coinAmount = Math.floor(Math.random() * (maxCoins - minCoins + 1)) + minCoins;
coinAmount *= storage.perks.doubleCoins === true ? 2 : 1; // Apply Double Coins perk
storage.coins += coinAmount;
coinsTxt.setText('Coins: ' + storage.coins);
// Coin animation
var coin = new CoinFly();
coin.x = candy.x;
coin.y = candy.y;
gameplayContainer.addChild(coin);
tween(coin, {
x: 2048 - 100,
// Target near the coin display
y: 50,
alpha: 0
}, {
duration: 800,
onComplete: function onComplete() {
coin.destroy();
}
});
// Remove candy
candy.destroy();
candies.splice(i, 1);
// Check music transitions based on score
var progress = score / levelConfig.targetScore;
// Music transitions at 15% and 35%
if (progress >= 0.15 && progress < 0.35 && musicEnabled) {
// Play levelmid at 15%
if (LK.currentMusic !== 'levelmid') {
LK.currentMusic = 'levelmid';
LK.playMusic('levelmid', {
loop: true
});
}
} else if (progress >= 0.35 && musicEnabled && !endlevelMusicPlayed) {
// Play endlevel at 35%
endlevelMusicPlayed = true;
LK.currentMusic = 'endlevel';
LK.playMusic('endlevel', {
loop: false
});
// Increase candy speed when at 35% progress
levelConfig.candySpeed *= 1.5; // Increase speed by 50%
}
// Check win
if (score >= levelConfig.targetScore) {
// Calculate bonus coins
var bonusCoins = currentLevel * 10; // Base level bonus
if (perfectRun) bonusCoins += 50; // Perfect run bonus
if (maxCombo >= 10) bonusCoins += maxCombo * 2; // Combo bonus
if (totalCandiesCaught >= levelConfig.targetScore * 1.5) bonusCoins += 30; // Efficiency bonus
storage.coins += bonusCoins;
coinsTxt.setText('Coins: ' + storage.coins);
// Show bonus notification
var bonusText = new Text2('+' + bonusCoins + ' coins!', {
size: 100,
fill: '#ffd700'
});
bonusText.anchor.set(0.5, 0.5);
bonusText.x = 1024;
bonusText.y = 1366;
gameplayContainer.addChild(bonusText);
tween(bonusText, {
y: 1100,
alpha: 0
}, {
duration: 2000,
onComplete: function onComplete() {
bonusText.destroy();
}
});
// Save high score for current level
if (!storage.levelScores[currentLevel] || score > storage.levelScores[currentLevel]) {
storage.levelScores[currentLevel] = score;
}
// Unlock next level if not already unlocked
var nextLevel = currentLevel + 1;
if (nextLevel <= 45 && storage.unlockedLevels.indexOf(nextLevel) === -1) {
storage.unlockedLevels.push(nextLevel);
storage.unlockedLevels.sort(function (a, b) {
return a - b;
}); // Keep levels sorted
}
// Stop any playing music
if (musicEnabled) {
LK.stopMusic();
LK.currentMusic = null;
}
// Return to levels tab
gameState = 'levelSelect';
updateGameState();
// Don't show regular "you win!" tab
// LK.showYouWin();
}
// Check progress - return to levels tab at 100%
else if (progress >= 1.0) {
// Stop any playing music
if (musicEnabled) {
LK.stopMusic();
LK.currentMusic = null;
}
// Return to levels tab
gameState = 'levelSelect';
updateGameState();
return; // Exit early to prevent further processing
}
}
// Remove off-screen
if (candy.y > 2832) {
if (!candy.missed) {
candy.missed = true;
// Shield power-up prevents life loss
if (activePowerUp !== 'shield' && !shieldActive) {
lives--;
perfectRun = false;
combo = 0; // Reset combo on miss
comboDisplay.visible = false;
} else if (activePowerUp === 'shield') {
// Shield power-up absorbs the hit
LK.effects.flashScreen(0xffff00, 200);
activePowerUp = null;
powerUpDuration = 0;
powerUpIndicator.visible = false;
} else if (shieldActive) {
// Shield perk absorbs the hit
LK.effects.flashScreen(0x00ffff, 200);
shieldActive = false;
shieldDuration = 0;
if (powerUpIndicator && activePowerUp === null) {
powerUpIndicator.visible = false;
}
}
// Update hearts display
if (heartsContainer && heartsContainer.children.length > lives) {
var heartToRemove = heartsContainer.children[lives];
if (heartToRemove && heartToRemove.visible) {
tween(heartToRemove, {
alpha: 0,
scaleX: 0.1,
scaleY: 0.1
}, {
duration: 300,
onComplete: function onComplete() {
heartToRemove.visible = false;
}
});
}
}
// Check game over
if (lives <= 0) {
// Stop any playing music
if (musicEnabled) {
LK.stopMusic();
LK.currentMusic = null;
}
// Return to levels tab
gameState = 'levelSelect';
updateGameState();
// Don't show regular game over screen
}
}
candy.destroy();
candies.splice(i, 1);
}
}
};
// Touch controls for collector
game.down = function (x, y, obj) {
if (gameState === 'playing' && collector) {
collector.x = x;
}
};
game.move = function (x, y, obj) {
if (gameState === 'playing' && collector) {
collector.x = x;
}
};
// Initialize game
updateGameState();
red shiny candy. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
chocolate cupcake, blue detail, cherry. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
wood stick, green&white stripe apple candy. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
golden coin . No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
chocolate ice-cream, cherry. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
chocolate bar, yellow packet. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
sparkle, rainbow. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat