User prompt
if I close the target play this track "endlevel" and I won the level change the track "main_menu"1
User prompt
if I won level, stop this track:"endlevel" and play this track "main_menu"
User prompt
increase the level target level 1: 100 level 30: 3000
User prompt
if I click any level button, first play this track:"startlevel" If i have a target/2 play this track:"levelmid" if i have a target play this track "endlevel"
User prompt
if I clicked any button play this sound in collect>"click"
User prompt
if music:on play this track:"main_menu"
User prompt
settings tab>Music:on/off settings tab>Sound:on/off
User prompt
if I in the main menu play this track:"main_menu"
User prompt
add a "setting" button in the main menu
User prompt
play "happy_and_lo-fi" track and loop
User prompt
update the "Assets" tab
User prompt
if i click the regular upit play button, play game "happy_and_lo-fi" track
User prompt
add background in the levels
User prompt
if I click regular upit play button, reset the progress
User prompt
reset the player progress
User prompt
level targets "Level 1: 25 score... Level 30 100 score
User prompt
if I won don't show regular "you win!" tab
User prompt
lock the levels, and put a target for levels "example if i have a 50 score in level 1, unlock level 2
User prompt
Please fix the bug: 'TypeError: storage.save is not a function' in or related to this line: 'storage.save();' Line Number: 661 ↪💡 Consider importing and using the following plugins: @upit/storage.v1
User prompt
add "5" perks
User prompt
if heart 0<3 return the main menu
User prompt
add a 3 heart for loss
User prompt
remake the game with main menu, "play>select levels 1-30" "shop>Character&perks"
User prompt
remake the game
User prompt
remake
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
coins: 0,
unlockedSkins: ["candyRed"],
selectedSkin: "candyRed"
});
/****
* Classes
****/
// Candy class
var Candy = Container.expand(function () {
var self = Container.call(this);
// Use selected skin or random unlocked skin
var skin = storage.selectedSkin || '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;
// For future: coin drop
self.hasCoin = Math.random() < 0.15; // 15% chance to drop a coin
// Add coin visual if hasCoin
if (self.hasCoin) {
var coinAsset = self.attachAsset('coin', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.5,
scaleY: 0.5,
y: 30
});
coinAsset.alpha = 0.8;
}
// 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 collectorAsset = self.attachAsset('collector', {
anchorX: 0.5,
anchorY: 0.5
});
collectorAsset.alpha = 0.18;
self.width = collectorAsset.width;
self.height = collectorAsset.height;
self.update = function () {};
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
****/
// Tabs: 0=Game, 1=Shop, 2=Leaderboard
// Candies (different colors/shapes for future shop unlocks)
// Coin
// Collector (player's swipe area)
// Shop item placeholder
// Sounds
// Music
var currentTab = 0;
// GUI tab buttons
var tabBtnGame, tabBtnShop, tabBtnLeaderboard;
// GUI overlays
var coinTxt,
scoreTxt,
shopCoinTxt,
shopTitleTxt,
shopItems = [],
shopGroup,
leaderboardGroup;
// Game state
var candies = [];
var collector;
var isDragging = false;
var dragOffsetX = 0,
dragOffsetY = 0;
var score = 0;
var coins = storage.coins || 0;
var level = 1;
var candiesToCollect = 10;
var candiesCollected = 0;
var gameActive = true;
var candySpawnTimer = 0;
var candySpawnInterval = 36; // frames
// --- Health ---
var health = 3;
var healthTxt = new Text2('❤❤❤', {
size: 90,
fill: '#ff3b3b'
});
healthTxt.anchor.set(0, 0);
healthTxt.x = 120;
healthTxt.y = 0;
LK.gui.top.addChild(healthTxt);
// Shop data
var shopSkins = [{
id: 'candyRed',
price: 0
}, {
id: 'candyGreen',
price: 20
}, {
id: 'candyBlue',
price: 40
}, {
id: 'candyYellow',
price: 60
}, {
id: 'candyPurple',
price: 100
}];
// --- GUI Setup ---
// Coin display (top right)
coinTxt = new Text2(coins + '', {
size: 90,
fill: '#ffe23b'
});
coinTxt.anchor.set(1, 0);
LK.gui.topRight.addChild(coinTxt);
// Score display (top center)
scoreTxt = new Text2('0', {
size: 110,
fill: '#fff'
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Tab buttons (bottom)
tabBtnGame = new Text2('Game', {
size: 80,
fill: '#fff'
});
tabBtnGame.anchor.set(0.5, 1);
tabBtnGame.x = 2048 / 6;
tabBtnGame.y = 0;
tabBtnGame.interactive = true;
tabBtnGame.buttonMode = true;
LK.gui.bottom.addChild(tabBtnGame);
tabBtnShop = new Text2('Shop', {
size: 80,
fill: '#fff'
});
tabBtnShop.anchor.set(0.5, 1);
tabBtnShop.x = 2048 / 2;
tabBtnShop.y = 0;
tabBtnShop.interactive = true;
tabBtnShop.buttonMode = true;
LK.gui.bottom.addChild(tabBtnShop);
tabBtnLeaderboard = new Text2('Leaderboard', {
size: 80,
fill: '#fff'
});
tabBtnLeaderboard.anchor.set(0.5, 1);
tabBtnLeaderboard.x = 2048 * 5 / 6;
tabBtnLeaderboard.y = 0;
tabBtnLeaderboard.interactive = true;
tabBtnLeaderboard.buttonMode = true;
LK.gui.bottom.addChild(tabBtnLeaderboard);
// Shop overlay group
shopGroup = new Container();
shopGroup.visible = false;
game.addChild(shopGroup);
// Shop background panel for visual separation
var shopBgPanel = LK.getAsset('shopItem', {
anchorX: 0.5,
anchorY: 0,
scaleX: 11,
scaleY: 8.5,
x: 2048 / 2,
y: 40
});
shopBgPanel.alpha = 0.10;
shopGroup.addChild(shopBgPanel);
// Shop title
shopTitleTxt = new Text2('Shop', {
size: 150,
fill: '#ffe23b'
});
shopTitleTxt.anchor.set(0.5, 0);
shopTitleTxt.x = 2048 / 2;
shopTitleTxt.y = 80;
shopGroup.addChild(shopTitleTxt);
// Shop coin display (top right in shop)
shopCoinTxt = new Text2('Coins: ' + coins, {
size: 100,
fill: '#ffe23b'
});
shopCoinTxt.anchor.set(1, 0);
shopCoinTxt.x = 2048 - 80;
shopCoinTxt.y = 80;
shopGroup.addChild(shopCoinTxt);
// Shop return button (top left in shop)
var shopReturnBtn = new Text2('← Back', {
size: 90,
fill: '#ffe23b'
});
shopReturnBtn.anchor.set(0, 0);
shopReturnBtn.x = 100;
shopReturnBtn.y = 80;
shopReturnBtn.interactive = true;
shopReturnBtn.buttonMode = true;
shopReturnBtn.down = function (x, y, obj) {
shopGroup.visible = false;
mainMenuGroup.visible = true;
shopCurrentTab = 0;
updateShopTab();
setTab(0);
};
shopGroup.addChild(shopReturnBtn);
// Shop tab buttons (centered below title, visually grouped)
var shopTabBar = new Container();
shopTabBar.x = 2048 / 2;
shopTabBar.y = 260;
shopGroup.addChild(shopTabBar);
var shopTabCharacter = new Text2('Characters', {
size: 90,
fill: '#fff'
});
shopTabCharacter.anchor.set(1, 0);
shopTabCharacter.x = -60;
shopTabCharacter.y = 0;
shopTabCharacter.interactive = true;
shopTabCharacter.buttonMode = true;
var shopTabItem = new Text2('Items', {
size: 90,
fill: '#fff'
});
shopTabItem.anchor.set(0, 0);
shopTabItem.x = 60;
shopTabItem.y = 0;
shopTabItem.interactive = true;
shopTabItem.buttonMode = true;
shopTabBar.addChild(shopTabCharacter);
shopTabBar.addChild(shopTabItem);
// Shop tab state: 0 = Characters, 1 = Items
var shopCurrentTab = 0;
// Shop tab switching logic
function updateShopTab() {
// Defensive: ensure shopItemItems is always an array
if (typeof shopItemItems === "undefined" || !shopItemItems) {
shopItemItems = [];
}
// Characters tab
if (shopCurrentTab === 0) {
for (var i = 0; i < shopItems.length; i++) {
shopItems[i].visible = true;
// Center vertically for character tab
shopItems[i].y = 700;
}
for (var i = 0; i < shopItemItems.length; i++) {
shopItemItems[i].visible = false;
}
shopTabCharacter.fill = '#ffe23b';
shopTabItem.fill = '#fff';
shopTabCharacter.scaleX = shopTabCharacter.scaleY = 1.15;
shopTabItem.scaleX = shopTabItem.scaleY = 1.0;
}
// Items tab
if (shopCurrentTab === 1) {
for (var i = 0; i < shopItems.length; i++) {
shopItems[i].visible = false;
}
for (var i = 0; i < shopItemItems.length; i++) {
shopItemItems[i].visible = true;
// Center vertically for item tab
shopItemItems[i].y = 700;
}
// Ensure item shop items are brought to front for visibility
for (var i = 0; i < shopItemItems.length; i++) {
if (shopGroup.children.indexOf(shopItemItems[i]) !== -1) {
shopGroup.removeChild(shopItemItems[i]);
shopGroup.addChild(shopItemItems[i]);
}
}
shopTabCharacter.fill = '#fff';
shopTabItem.fill = '#ffe23b';
shopTabCharacter.scaleX = shopTabCharacter.scaleY = 1.0;
shopTabItem.scaleX = shopTabItem.scaleY = 1.15;
}
}
shopTabCharacter.down = function (x, y, obj) {
shopCurrentTab = 0;
updateShopTab();
};
shopTabItem.down = function (x, y, obj) {
shopCurrentTab = 1;
updateShopTab();
};
// Initialize tab highlight
updateShopTab();
// Shop items
shopItems = [];
for (var i = 0; i < shopSkins.length; i++) {
var item = new ShopItem();
var skin = shopSkins[i];
var owned = storage.unlockedSkins.indexOf(skin.id) !== -1;
item.setSkin(skin.id, skin.price, owned);
item.x = 2048 / 2 + (i - 2) * 320;
item.y = 600;
// Price/owned label
var label = new Text2(owned ? 'Owned' : skin.price + ' coins', {
size: 60,
fill: owned ? '#3bff6e' : '#fff'
});
label.anchor.set(0.5, 0);
label.y = 120;
item.addChild(label);
item.label = label;
// Selection highlight
var selectTxt = new Text2('Selected', {
size: 50,
fill: '#ffe23b'
});
selectTxt.anchor.set(0.5, 0);
selectTxt.y = 170;
selectTxt.visible = storage.selectedSkin === skin.id;
item.addChild(selectTxt);
item.selectTxt = selectTxt;
// Touch handler
(function (item, skin, label, selectTxt) {
item.down = function (x, y, obj) {
if (item.owned) {
storage.selectedSkin = skin.id;
for (var j = 0; j < shopItems.length; j++) {
shopItems[j].selectTxt.visible = shopItems[j].skinId === skin.id;
}
} else if (coins >= skin.price) {
coins -= skin.price;
storage.coins = coins;
storage.unlockedSkins.push(skin.id);
item.owned = true;
item.label.setText('Owned');
item.label.fill = '#3bff6e';
item.selectTxt.visible = true;
storage.selectedSkin = skin.id;
for (var j = 0; j < shopItems.length; j++) {
if (shopItems[j] !== item) shopItems[j].selectTxt.visible = false;
}
shopCoinTxt.setText('Coins: ' + coins);
coinTxt.setText(coins);
LK.getSound('shopBuy').play();
}
};
})(item, skin, label, selectTxt);
shopGroup.addChild(item);
shopItems.push(item);
}
// --- Item Shop Items ---
var shopItemItems = [];
// Item shop data
var itemShopData = [{
id: 'healthBooster',
label: '',
desc: '+1 Heart (per game)',
price: 10
}, {
id: 'slowMotion',
label: '',
desc: '-Y: Slow candies (10s)',
price: 15
}, {
id: 'autoClicker',
label: '',
desc: 'Auto collect (10s)',
price: 25
}];
// Track item inventory (permanent for health, consumable for others)
if (!storage.itemInventory) storage.itemInventory = {
healthBooster: 0,
slowMotion: 0,
autoClicker: 0
};
for (var i = 0; i < itemShopData.length; i++) {
(function (i) {
var itemData = itemShopData[i];
var item = new ShopItem();
// Use 'shopItem' asset for all items
item.setSkin('shopItem', itemData.price, false);
item.x = 2048 / 2 + (i - 1) * 320;
item.y = 600;
// --- Visual Item Icon ---
var iconAssetId = null;
if (itemData.id === "healthBooster") iconAssetId = "candyRed";
if (itemData.id === "slowMotion") iconAssetId = "candyBlue";
if (itemData.id === "autoClicker") iconAssetId = "coin";
var iconVisual = null;
if (iconAssetId) {
iconVisual = LK.getAsset(iconAssetId, {
anchorX: 0.5,
anchorY: 0.5,
scaleX: itemData.id === "autoClicker" ? 1.1 : 0.8,
scaleY: itemData.id === "autoClicker" ? 1.1 : 0.8,
y: -60
});
item.addChild(iconVisual);
}
// Icon/label
// Removed item name label (iconTxt)
// Description
var descTxt = new Text2(itemData.desc, {
size: 38,
fill: '#fff'
});
descTxt.anchor.set(0.5, 0);
descTxt.y = 60;
item.addChild(descTxt);
// Price/owned label
var ownedCount = storage.itemInventory[itemData.id] || 0;
var priceLabel = new Text2('x' + ownedCount + ' ' + itemData.price + ' coins', {
size: 48,
fill: '#fff'
});
priceLabel.anchor.set(0.5, 0);
priceLabel.y = 120;
item.addChild(priceLabel);
item.priceLabel = priceLabel;
// Touch handler
item.down = function (x, y, obj) {
if (coins >= itemData.price) {
coins -= itemData.price;
storage.coins = coins;
storage.itemInventory[itemData.id] = (storage.itemInventory[itemData.id] || 0) + 1;
item.priceLabel.setText('x' + storage.itemInventory[itemData.id] + ' ' + itemData.price + ' coins');
shopCoinTxt.setText('Coins: ' + coins);
coinTxt.setText(coins);
LK.getSound('shopBuy').play();
}
};
shopGroup.addChild(item);
item.visible = false;
shopItemItems.push(item);
})(i);
}
// Leaderboard overlay group (placeholder)
leaderboardGroup = new Container();
leaderboardGroup.visible = false;
game.addChild(leaderboardGroup);
var leaderboardTitle = new Text2('Leaderboard', {
size: 120,
fill: '#fff'
});
leaderboardTitle.anchor.set(0.5, 0);
leaderboardTitle.x = 2048 / 2;
leaderboardTitle.y = 180;
leaderboardGroup.addChild(leaderboardTitle);
// Leaderboard return button
var leaderboardReturnBtn = new Text2('Return', {
size: 80,
fill: '#ffe23b'
});
leaderboardReturnBtn.anchor.set(0, 0);
leaderboardReturnBtn.x = 80;
leaderboardReturnBtn.y = 180;
leaderboardReturnBtn.interactive = true;
leaderboardReturnBtn.buttonMode = true;
leaderboardReturnBtn.down = function (x, y, obj) {
leaderboardGroup.visible = false;
mainMenuGroup.visible = true;
setTab(0);
};
leaderboardGroup.addChild(leaderboardReturnBtn);
var leaderboardInfo = new Text2('Check your high score in the system leaderboard!', {
size: 70,
fill: '#fff'
});
leaderboardInfo.anchor.set(0.5, 0);
leaderboardInfo.x = 2048 / 2;
leaderboardInfo.y = 350;
leaderboardGroup.addChild(leaderboardInfo);
// --- Main Menu Overlay ---
var mainMenuGroup = new Container();
mainMenuGroup.visible = true;
game.addChild(mainMenuGroup);
var mainMenuTitle = new Text2('Candy Collector Deluxe', {
size: 160,
fill: '#ffe23b'
});
mainMenuTitle.anchor.set(0.5, 0);
mainMenuTitle.x = 2048 / 2;
mainMenuTitle.y = 400;
mainMenuGroup.addChild(mainMenuTitle);
var mainMenuPlayBtn = new Text2('Play', {
size: 120,
fill: '#fff'
});
mainMenuPlayBtn.anchor.set(0.5, 0.5);
mainMenuPlayBtn.x = 2048 / 2;
mainMenuPlayBtn.y = 900;
mainMenuPlayBtn.interactive = true;
mainMenuPlayBtn.buttonMode = true;
mainMenuGroup.addChild(mainMenuPlayBtn);
var mainMenuShopBtn = new Text2('Shop', {
size: 100,
fill: '#fff'
});
mainMenuShopBtn.anchor.set(0.5, 0.5);
mainMenuShopBtn.x = 2048 / 2;
mainMenuShopBtn.y = 1100;
mainMenuShopBtn.interactive = true;
mainMenuShopBtn.buttonMode = true;
mainMenuGroup.addChild(mainMenuShopBtn);
var mainMenuLeaderboardBtn = new Text2('Leaderboard', {
size: 100,
fill: '#fff'
});
mainMenuLeaderboardBtn.anchor.set(0.5, 0.5);
mainMenuLeaderboardBtn.x = 2048 / 2;
mainMenuLeaderboardBtn.y = 1250;
mainMenuLeaderboardBtn.interactive = true;
mainMenuLeaderboardBtn.buttonMode = true;
mainMenuGroup.addChild(mainMenuLeaderboardBtn);
mainMenuPlayBtn.down = function (x, y, obj) {
mainMenuGroup.visible = false;
setTab(0);
};
mainMenuShopBtn.down = function (x, y, obj) {
mainMenuGroup.visible = false;
setTab(1);
};
mainMenuLeaderboardBtn.down = function (x, y, obj) {
mainMenuGroup.visible = false;
setTab(2);
};
// --- Main Game Setup ---
function resetGame() {
// Remove candies
for (var i = candies.length - 1; i >= 0; i--) {
candies[i].destroy();
candies.splice(i, 1);
}
score = 0;
candiesCollected = 0;
candiesToCollect = 10 + (level - 1) * 2;
candySpawnTimer = 0;
scoreTxt.setText('0');
gameActive = true;
// Reset health, add +1 if health booster owned
health = 3 + (storage.itemInventory && storage.itemInventory.healthBooster ? storage.itemInventory.healthBooster : 0);
var hearts = '';
for (var h = 0; h < health; h++) hearts += '❤';
healthTxt.setText(hearts);
// Collector position
collector.x = 2048 / 2;
collector.y = 2732 - 220;
// Reset item effect timers
slowMotionActive = false;
slowMotionTimer = 0;
autoClickerActive = false;
autoClickerTimer = 0;
}
// Collector (player's swipe area)
collector = new Collector();
collector.x = 2048 / 2;
collector.y = 2732 - 220;
game.addChild(collector);
// --- Tab Switching ---
function setTab(tabIdx) {
currentTab = tabIdx;
// Game tab
if (tabIdx === 0) {
game.visible = true;
shopGroup.visible = false;
leaderboardGroup.visible = false;
coinTxt.visible = true;
scoreTxt.visible = true;
collector.visible = true;
}
// Shop tab
if (tabIdx === 1) {
game.visible = true;
shopGroup.visible = true;
leaderboardGroup.visible = false;
coinTxt.visible = false;
scoreTxt.visible = false;
collector.visible = false;
shopCoinTxt.setText('Coins: ' + coins);
for (var i = 0; i < shopItems.length; i++) {
var owned = storage.unlockedSkins.indexOf(shopItems[i].skinId) !== -1;
shopItems[i].owned = owned;
shopItems[i].label.setText(owned ? 'Owned' : shopSkins[i].price + ' coins');
shopItems[i].label.fill = owned ? '#3bff6e' : '#fff';
shopItems[i].selectTxt.visible = storage.selectedSkin === shopItems[i].skinId;
// Show/hide based on tab
shopItems[i].visible = shopCurrentTab === 0;
}
updateShopTab();
}
// Leaderboard tab
if (tabIdx === 2) {
game.visible = true;
shopGroup.visible = false;
leaderboardGroup.visible = true;
coinTxt.visible = false;
scoreTxt.visible = false;
collector.visible = false;
}
}
// Tab button handlers
tabBtnGame.down = function (x, y, obj) {
setTab(0);
};
tabBtnShop.down = function (x, y, obj) {
setTab(1);
};
tabBtnLeaderboard.down = function (x, y, obj) {
setTab(2);
};
// --- Item Usage Buttons ---
var itemBtnSlow = new Text2('Slow', {
size: 70,
fill: '#3b9bff'
});
itemBtnSlow.anchor.set(0.5, 0);
itemBtnSlow.x = 2048 / 2 - 200;
itemBtnSlow.y = 120;
itemBtnSlow.interactive = true;
itemBtnSlow.buttonMode = true;
LK.gui.top.addChild(itemBtnSlow);
var itemBtnAuto = new Text2('Auto', {
size: 70,
fill: '#3b9bff'
});
itemBtnAuto.anchor.set(0.5, 0);
itemBtnAuto.x = 2048 / 2 + 200;
itemBtnAuto.y = 120;
itemBtnAuto.interactive = true;
itemBtnAuto.buttonMode = true;
LK.gui.top.addChild(itemBtnAuto);
// Effect state
var slowMotionActive = false;
var slowMotionTimer = 0;
var autoClickerActive = false;
var autoClickerTimer = 0;
// Button handlers
itemBtnSlow.down = function (x, y, obj) {
if (!gameActive || mainMenuGroup.visible || currentTab !== 0) return;
if (storage.itemInventory && storage.itemInventory.slowMotion > 0 && !slowMotionActive) {
storage.itemInventory.slowMotion--;
slowMotionActive = true;
slowMotionTimer = 600; // 10 seconds at 60fps
itemBtnSlow.setText('Slow (' + storage.itemInventory.slowMotion + ')');
itemBtnSlow.fill = '#ffe23b';
}
};
itemBtnAuto.down = function (x, y, obj) {
if (!gameActive || mainMenuGroup.visible || currentTab !== 0) return;
if (storage.itemInventory && storage.itemInventory.autoClicker > 0 && !autoClickerActive) {
storage.itemInventory.autoClicker--;
autoClickerActive = true;
autoClickerTimer = 600; // 10 seconds at 60fps
itemBtnAuto.setText('Auto (' + storage.itemInventory.autoClicker + ')');
itemBtnAuto.fill = '#ffe23b';
}
};
// Update button text on game start
itemBtnSlow.setText('Slow (' + (storage.itemInventory && storage.itemInventory.slowMotion || 0) + ')');
itemBtnAuto.setText('Auto (' + (storage.itemInventory && storage.itemInventory.autoClicker || 0) + ')');
itemBtnSlow.fill = '#3b9bff';
itemBtnAuto.fill = '#3b9bff';
// --- Touch Controls ---
// Drag collector
game.down = function (x, y, obj) {
if (mainMenuGroup.visible || currentTab !== 0 || !gameActive) return;
// Only drag if touch is on collector
var local = collector.toLocal({
x: x,
y: y
});
if (local.x > -collector.width / 2 && local.x < collector.width / 2 && local.y > -collector.height / 2 && local.y < collector.height / 2) {
isDragging = true;
dragOffsetX = collector.x - x;
dragOffsetY = collector.y - y;
}
};
game.up = function (x, y, obj) {
if (mainMenuGroup.visible || currentTab !== 0 || !gameActive) return;
isDragging = false;
};
game.move = function (x, y, obj) {
if (mainMenuGroup.visible || currentTab !== 0 || !gameActive) return;
if (isDragging) {
collector.x = Math.max(collector.width / 2, Math.min(2048 - collector.width / 2, x + dragOffsetX));
collector.y = Math.max(collector.height / 2 + 200, Math.min(2732 - collector.height / 2, y + dragOffsetY));
}
// Swipe detection: check for candies under finger
for (var i = candies.length - 1; i >= 0; i--) {
var candy = candies[i];
if (!candy.collected && candy.visible) {
var dx = candy.x - x;
var dy = candy.y - y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist < candy.width / 2 + 40) {
collectCandy(candy);
}
}
}
};
// --- Candy Collection ---
function collectCandy(candy) {
if (candy.collected) return;
candy.collected = true;
candiesCollected++;
score++;
scoreTxt.setText(score);
// Animate
tween(candy, {
scaleX: 1.4,
scaleY: 1.4,
alpha: 0
}, {
duration: 220,
easing: tween.cubicIn,
onFinish: function onFinish() {
candy.destroy();
}
});
LK.getSound('collect').play();
// Coin drop
if (candy.hasCoin) {
coins++;
storage.coins = coins;
coinTxt.setText(coins);
shopCoinTxt.setText('Coins: ' + coins);
// Coin fly animation
var coinFly = new CoinFly();
coinFly.x = candy.x;
coinFly.y = candy.y;
game.addChild(coinFly);
tween(coinFly, {
x: 2048 - 120,
y: 120,
scaleX: 0.5,
scaleY: 0.5,
alpha: 0
}, {
duration: 600,
easing: tween.cubicIn,
onFinish: function onFinish() {
coinFly.destroy();
}
});
LK.getSound('coin').play();
}
// Remove from candies array
for (var i = 0; i < candies.length; i++) {
if (candies[i] === candy) {
candies.splice(i, 1);
break;
}
}
// Win condition
if (candiesCollected >= candiesToCollect) {
// Save high score
if (!storage.highScore || score > storage.highScore) {
storage.highScore = score;
}
// Loop: advance to next level and reset game state
level++;
resetGame();
setTab(0);
return;
}
}
// --- Game Update Loop ---
game.update = function () {
if (mainMenuGroup.visible || currentTab !== 0 || !gameActive) return;
// --- Item Effects ---
if (slowMotionActive) {
slowMotionTimer--;
if (slowMotionTimer <= 0) {
slowMotionActive = false;
itemBtnSlow.fill = '#3b9bff';
itemBtnSlow.setText('Slow (' + (storage.itemInventory && storage.itemInventory.slowMotion || 0) + ')');
}
}
if (autoClickerActive) {
autoClickerTimer--;
if (autoClickerTimer <= 0) {
autoClickerActive = false;
itemBtnAuto.fill = '#3b9bff';
itemBtnAuto.setText('Auto (' + (storage.itemInventory && storage.itemInventory.autoClicker || 0) + ')');
}
}
// Spawn candies
candySpawnTimer++;
var spawnInterval = slowMotionActive ? Math.round(candySpawnInterval * 1.5) : candySpawnInterval;
if (candies.length < candiesToCollect && candySpawnTimer >= spawnInterval) {
candySpawnTimer = 0;
var candy = new Candy();
// Use selected skin
candy.skin = storage.selectedSkin || 'candyRed';
// Random X, avoid edges
candy.x = 120 + Math.random() * (2048 - 240);
candy.y = -80;
// Randomize speed a bit
candy.speed = (slowMotionActive ? 0.5 : 1) * (10 + Math.random() * 6 + level * 0.5);
candies.push(candy);
game.addChild(candy);
}
// Update candies
for (var i = candies.length - 1; i >= 0; i--) {
var candy = candies[i];
if (slowMotionActive) {
// Slow down falling speed
candy.y += (candy.speed || 12) * 0.5;
} else {
candy.update();
}
// Collector collision
if (!candy.collected && collector.visible) {
var dx = candy.x - collector.x;
var dy = candy.y - collector.y;
if (Math.abs(dx) < candy.width / 2 + collector.width / 2 - 30 && Math.abs(dy) < candy.height / 2 + collector.height / 2 - 30) {
collectCandy(candy);
}
}
// Off-screen (missed)
if (!candy.collected && candy.y > 2732 + 80 && !candy.missed) {
candy.missed = true;
// Animate out
tween(candy, {
alpha: 0
}, {
duration: 200,
onFinish: function onFinish() {
candy.destroy();
}
});
// Remove from candies array
candies.splice(i, 1);
// Decrease health and update display
health--;
var hearts = '';
for (var h = 0; h < health; h++) hearts += '❤';
healthTxt.setText(hearts);
LK.effects.flashScreen(0xff0000, 600);
if (health <= 0) {
LK.showGameOver();
gameActive = false;
}
}
}
// --- Auto Clicker effect: auto collect nearest candy ---
if (autoClickerActive && candies.length > 0) {
// Find lowest (closest to bottom) visible, uncollected candy
var best = null,
bestY = -Infinity;
for (var i = 0; i < candies.length; i++) {
if (!candies[i].collected && candies[i].visible && candies[i].y > bestY) {
best = candies[i];
bestY = candies[i].y;
}
}
if (best) {
collectCandy(best);
}
}
};
// --- Music ---
LK.playMusic('bgmusic');
// --- Game Over/Win Handlers ---
LK.on('gameover', function () {
// Reset game state
resetGame();
mainMenuGroup.visible = true;
setTab(0);
});
LK.on('youwin', function () {
// Next level
level++;
resetGame();
mainMenuGroup.visible = true;
setTab(0);
});
// --- Initial State ---
resetGame();
mainMenuGroup.visible = true;
setTab(0); /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
coins: 0,
unlockedSkins: ["candyRed"],
selectedSkin: "candyRed"
});
/****
* Classes
****/
// Candy class
var Candy = Container.expand(function () {
var self = Container.call(this);
// Use selected skin or random unlocked skin
var skin = storage.selectedSkin || '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;
// For future: coin drop
self.hasCoin = Math.random() < 0.15; // 15% chance to drop a coin
// Add coin visual if hasCoin
if (self.hasCoin) {
var coinAsset = self.attachAsset('coin', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.5,
scaleY: 0.5,
y: 30
});
coinAsset.alpha = 0.8;
}
// 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 collectorAsset = self.attachAsset('collector', {
anchorX: 0.5,
anchorY: 0.5
});
collectorAsset.alpha = 0.18;
self.width = collectorAsset.width;
self.height = collectorAsset.height;
self.update = function () {};
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
****/
// Tabs: 0=Game, 1=Shop, 2=Leaderboard
// Candies (different colors/shapes for future shop unlocks)
// Coin
// Collector (player's swipe area)
// Shop item placeholder
// Sounds
// Music
var currentTab = 0;
// GUI tab buttons
var tabBtnGame, tabBtnShop, tabBtnLeaderboard;
// GUI overlays
var coinTxt,
scoreTxt,
shopCoinTxt,
shopTitleTxt,
shopItems = [],
shopGroup,
leaderboardGroup;
// Game state
var candies = [];
var collector;
var isDragging = false;
var dragOffsetX = 0,
dragOffsetY = 0;
var score = 0;
var coins = storage.coins || 0;
var level = 1;
var candiesToCollect = 10;
var candiesCollected = 0;
var gameActive = true;
var candySpawnTimer = 0;
var candySpawnInterval = 36; // frames
// --- Health ---
var health = 3;
var healthTxt = new Text2('❤❤❤', {
size: 90,
fill: '#ff3b3b'
});
healthTxt.anchor.set(0, 0);
healthTxt.x = 120;
healthTxt.y = 0;
LK.gui.top.addChild(healthTxt);
// Shop data
var shopSkins = [{
id: 'candyRed',
price: 0
}, {
id: 'candyGreen',
price: 20
}, {
id: 'candyBlue',
price: 40
}, {
id: 'candyYellow',
price: 60
}, {
id: 'candyPurple',
price: 100
}];
// --- GUI Setup ---
// Coin display (top right)
coinTxt = new Text2(coins + '', {
size: 90,
fill: '#ffe23b'
});
coinTxt.anchor.set(1, 0);
LK.gui.topRight.addChild(coinTxt);
// Score display (top center)
scoreTxt = new Text2('0', {
size: 110,
fill: '#fff'
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Tab buttons (bottom)
tabBtnGame = new Text2('Game', {
size: 80,
fill: '#fff'
});
tabBtnGame.anchor.set(0.5, 1);
tabBtnGame.x = 2048 / 6;
tabBtnGame.y = 0;
tabBtnGame.interactive = true;
tabBtnGame.buttonMode = true;
LK.gui.bottom.addChild(tabBtnGame);
tabBtnShop = new Text2('Shop', {
size: 80,
fill: '#fff'
});
tabBtnShop.anchor.set(0.5, 1);
tabBtnShop.x = 2048 / 2;
tabBtnShop.y = 0;
tabBtnShop.interactive = true;
tabBtnShop.buttonMode = true;
LK.gui.bottom.addChild(tabBtnShop);
tabBtnLeaderboard = new Text2('Leaderboard', {
size: 80,
fill: '#fff'
});
tabBtnLeaderboard.anchor.set(0.5, 1);
tabBtnLeaderboard.x = 2048 * 5 / 6;
tabBtnLeaderboard.y = 0;
tabBtnLeaderboard.interactive = true;
tabBtnLeaderboard.buttonMode = true;
LK.gui.bottom.addChild(tabBtnLeaderboard);
// Shop overlay group
shopGroup = new Container();
shopGroup.visible = false;
game.addChild(shopGroup);
// Shop background panel for visual separation
var shopBgPanel = LK.getAsset('shopItem', {
anchorX: 0.5,
anchorY: 0,
scaleX: 11,
scaleY: 8.5,
x: 2048 / 2,
y: 40
});
shopBgPanel.alpha = 0.10;
shopGroup.addChild(shopBgPanel);
// Shop title
shopTitleTxt = new Text2('Shop', {
size: 150,
fill: '#ffe23b'
});
shopTitleTxt.anchor.set(0.5, 0);
shopTitleTxt.x = 2048 / 2;
shopTitleTxt.y = 80;
shopGroup.addChild(shopTitleTxt);
// Shop coin display (top right in shop)
shopCoinTxt = new Text2('Coins: ' + coins, {
size: 100,
fill: '#ffe23b'
});
shopCoinTxt.anchor.set(1, 0);
shopCoinTxt.x = 2048 - 80;
shopCoinTxt.y = 80;
shopGroup.addChild(shopCoinTxt);
// Shop return button (top left in shop)
var shopReturnBtn = new Text2('← Back', {
size: 90,
fill: '#ffe23b'
});
shopReturnBtn.anchor.set(0, 0);
shopReturnBtn.x = 100;
shopReturnBtn.y = 80;
shopReturnBtn.interactive = true;
shopReturnBtn.buttonMode = true;
shopReturnBtn.down = function (x, y, obj) {
shopGroup.visible = false;
mainMenuGroup.visible = true;
shopCurrentTab = 0;
updateShopTab();
setTab(0);
};
shopGroup.addChild(shopReturnBtn);
// Shop tab buttons (centered below title, visually grouped)
var shopTabBar = new Container();
shopTabBar.x = 2048 / 2;
shopTabBar.y = 260;
shopGroup.addChild(shopTabBar);
var shopTabCharacter = new Text2('Characters', {
size: 90,
fill: '#fff'
});
shopTabCharacter.anchor.set(1, 0);
shopTabCharacter.x = -60;
shopTabCharacter.y = 0;
shopTabCharacter.interactive = true;
shopTabCharacter.buttonMode = true;
var shopTabItem = new Text2('Items', {
size: 90,
fill: '#fff'
});
shopTabItem.anchor.set(0, 0);
shopTabItem.x = 60;
shopTabItem.y = 0;
shopTabItem.interactive = true;
shopTabItem.buttonMode = true;
shopTabBar.addChild(shopTabCharacter);
shopTabBar.addChild(shopTabItem);
// Shop tab state: 0 = Characters, 1 = Items
var shopCurrentTab = 0;
// Shop tab switching logic
function updateShopTab() {
// Defensive: ensure shopItemItems is always an array
if (typeof shopItemItems === "undefined" || !shopItemItems) {
shopItemItems = [];
}
// Characters tab
if (shopCurrentTab === 0) {
for (var i = 0; i < shopItems.length; i++) {
shopItems[i].visible = true;
// Center vertically for character tab
shopItems[i].y = 700;
}
for (var i = 0; i < shopItemItems.length; i++) {
shopItemItems[i].visible = false;
}
shopTabCharacter.fill = '#ffe23b';
shopTabItem.fill = '#fff';
shopTabCharacter.scaleX = shopTabCharacter.scaleY = 1.15;
shopTabItem.scaleX = shopTabItem.scaleY = 1.0;
}
// Items tab
if (shopCurrentTab === 1) {
for (var i = 0; i < shopItems.length; i++) {
shopItems[i].visible = false;
}
for (var i = 0; i < shopItemItems.length; i++) {
shopItemItems[i].visible = true;
// Center vertically for item tab
shopItemItems[i].y = 700;
}
// Ensure item shop items are brought to front for visibility
for (var i = 0; i < shopItemItems.length; i++) {
if (shopGroup.children.indexOf(shopItemItems[i]) !== -1) {
shopGroup.removeChild(shopItemItems[i]);
shopGroup.addChild(shopItemItems[i]);
}
}
shopTabCharacter.fill = '#fff';
shopTabItem.fill = '#ffe23b';
shopTabCharacter.scaleX = shopTabCharacter.scaleY = 1.0;
shopTabItem.scaleX = shopTabItem.scaleY = 1.15;
}
}
shopTabCharacter.down = function (x, y, obj) {
shopCurrentTab = 0;
updateShopTab();
};
shopTabItem.down = function (x, y, obj) {
shopCurrentTab = 1;
updateShopTab();
};
// Initialize tab highlight
updateShopTab();
// Shop items
shopItems = [];
for (var i = 0; i < shopSkins.length; i++) {
var item = new ShopItem();
var skin = shopSkins[i];
var owned = storage.unlockedSkins.indexOf(skin.id) !== -1;
item.setSkin(skin.id, skin.price, owned);
item.x = 2048 / 2 + (i - 2) * 320;
item.y = 600;
// Price/owned label
var label = new Text2(owned ? 'Owned' : skin.price + ' coins', {
size: 60,
fill: owned ? '#3bff6e' : '#fff'
});
label.anchor.set(0.5, 0);
label.y = 120;
item.addChild(label);
item.label = label;
// Selection highlight
var selectTxt = new Text2('Selected', {
size: 50,
fill: '#ffe23b'
});
selectTxt.anchor.set(0.5, 0);
selectTxt.y = 170;
selectTxt.visible = storage.selectedSkin === skin.id;
item.addChild(selectTxt);
item.selectTxt = selectTxt;
// Touch handler
(function (item, skin, label, selectTxt) {
item.down = function (x, y, obj) {
if (item.owned) {
storage.selectedSkin = skin.id;
for (var j = 0; j < shopItems.length; j++) {
shopItems[j].selectTxt.visible = shopItems[j].skinId === skin.id;
}
} else if (coins >= skin.price) {
coins -= skin.price;
storage.coins = coins;
storage.unlockedSkins.push(skin.id);
item.owned = true;
item.label.setText('Owned');
item.label.fill = '#3bff6e';
item.selectTxt.visible = true;
storage.selectedSkin = skin.id;
for (var j = 0; j < shopItems.length; j++) {
if (shopItems[j] !== item) shopItems[j].selectTxt.visible = false;
}
shopCoinTxt.setText('Coins: ' + coins);
coinTxt.setText(coins);
LK.getSound('shopBuy').play();
}
};
})(item, skin, label, selectTxt);
shopGroup.addChild(item);
shopItems.push(item);
}
// --- Item Shop Items ---
var shopItemItems = [];
// Item shop data
var itemShopData = [{
id: 'healthBooster',
label: '',
desc: '+1 Heart (per game)',
price: 10
}, {
id: 'slowMotion',
label: '',
desc: '-Y: Slow candies (10s)',
price: 15
}, {
id: 'autoClicker',
label: '',
desc: 'Auto collect (10s)',
price: 25
}];
// Track item inventory (permanent for health, consumable for others)
if (!storage.itemInventory) storage.itemInventory = {
healthBooster: 0,
slowMotion: 0,
autoClicker: 0
};
for (var i = 0; i < itemShopData.length; i++) {
(function (i) {
var itemData = itemShopData[i];
var item = new ShopItem();
// Use 'shopItem' asset for all items
item.setSkin('shopItem', itemData.price, false);
item.x = 2048 / 2 + (i - 1) * 320;
item.y = 600;
// --- Visual Item Icon ---
var iconAssetId = null;
if (itemData.id === "healthBooster") iconAssetId = "candyRed";
if (itemData.id === "slowMotion") iconAssetId = "candyBlue";
if (itemData.id === "autoClicker") iconAssetId = "coin";
var iconVisual = null;
if (iconAssetId) {
iconVisual = LK.getAsset(iconAssetId, {
anchorX: 0.5,
anchorY: 0.5,
scaleX: itemData.id === "autoClicker" ? 1.1 : 0.8,
scaleY: itemData.id === "autoClicker" ? 1.1 : 0.8,
y: -60
});
item.addChild(iconVisual);
}
// Icon/label
// Removed item name label (iconTxt)
// Description
var descTxt = new Text2(itemData.desc, {
size: 38,
fill: '#fff'
});
descTxt.anchor.set(0.5, 0);
descTxt.y = 60;
item.addChild(descTxt);
// Price/owned label
var ownedCount = storage.itemInventory[itemData.id] || 0;
var priceLabel = new Text2('x' + ownedCount + ' ' + itemData.price + ' coins', {
size: 48,
fill: '#fff'
});
priceLabel.anchor.set(0.5, 0);
priceLabel.y = 120;
item.addChild(priceLabel);
item.priceLabel = priceLabel;
// Touch handler
item.down = function (x, y, obj) {
if (coins >= itemData.price) {
coins -= itemData.price;
storage.coins = coins;
storage.itemInventory[itemData.id] = (storage.itemInventory[itemData.id] || 0) + 1;
item.priceLabel.setText('x' + storage.itemInventory[itemData.id] + ' ' + itemData.price + ' coins');
shopCoinTxt.setText('Coins: ' + coins);
coinTxt.setText(coins);
LK.getSound('shopBuy').play();
}
};
shopGroup.addChild(item);
item.visible = false;
shopItemItems.push(item);
})(i);
}
// Leaderboard overlay group (placeholder)
leaderboardGroup = new Container();
leaderboardGroup.visible = false;
game.addChild(leaderboardGroup);
var leaderboardTitle = new Text2('Leaderboard', {
size: 120,
fill: '#fff'
});
leaderboardTitle.anchor.set(0.5, 0);
leaderboardTitle.x = 2048 / 2;
leaderboardTitle.y = 180;
leaderboardGroup.addChild(leaderboardTitle);
// Leaderboard return button
var leaderboardReturnBtn = new Text2('Return', {
size: 80,
fill: '#ffe23b'
});
leaderboardReturnBtn.anchor.set(0, 0);
leaderboardReturnBtn.x = 80;
leaderboardReturnBtn.y = 180;
leaderboardReturnBtn.interactive = true;
leaderboardReturnBtn.buttonMode = true;
leaderboardReturnBtn.down = function (x, y, obj) {
leaderboardGroup.visible = false;
mainMenuGroup.visible = true;
setTab(0);
};
leaderboardGroup.addChild(leaderboardReturnBtn);
var leaderboardInfo = new Text2('Check your high score in the system leaderboard!', {
size: 70,
fill: '#fff'
});
leaderboardInfo.anchor.set(0.5, 0);
leaderboardInfo.x = 2048 / 2;
leaderboardInfo.y = 350;
leaderboardGroup.addChild(leaderboardInfo);
// --- Main Menu Overlay ---
var mainMenuGroup = new Container();
mainMenuGroup.visible = true;
game.addChild(mainMenuGroup);
var mainMenuTitle = new Text2('Candy Collector Deluxe', {
size: 160,
fill: '#ffe23b'
});
mainMenuTitle.anchor.set(0.5, 0);
mainMenuTitle.x = 2048 / 2;
mainMenuTitle.y = 400;
mainMenuGroup.addChild(mainMenuTitle);
var mainMenuPlayBtn = new Text2('Play', {
size: 120,
fill: '#fff'
});
mainMenuPlayBtn.anchor.set(0.5, 0.5);
mainMenuPlayBtn.x = 2048 / 2;
mainMenuPlayBtn.y = 900;
mainMenuPlayBtn.interactive = true;
mainMenuPlayBtn.buttonMode = true;
mainMenuGroup.addChild(mainMenuPlayBtn);
var mainMenuShopBtn = new Text2('Shop', {
size: 100,
fill: '#fff'
});
mainMenuShopBtn.anchor.set(0.5, 0.5);
mainMenuShopBtn.x = 2048 / 2;
mainMenuShopBtn.y = 1100;
mainMenuShopBtn.interactive = true;
mainMenuShopBtn.buttonMode = true;
mainMenuGroup.addChild(mainMenuShopBtn);
var mainMenuLeaderboardBtn = new Text2('Leaderboard', {
size: 100,
fill: '#fff'
});
mainMenuLeaderboardBtn.anchor.set(0.5, 0.5);
mainMenuLeaderboardBtn.x = 2048 / 2;
mainMenuLeaderboardBtn.y = 1250;
mainMenuLeaderboardBtn.interactive = true;
mainMenuLeaderboardBtn.buttonMode = true;
mainMenuGroup.addChild(mainMenuLeaderboardBtn);
mainMenuPlayBtn.down = function (x, y, obj) {
mainMenuGroup.visible = false;
setTab(0);
};
mainMenuShopBtn.down = function (x, y, obj) {
mainMenuGroup.visible = false;
setTab(1);
};
mainMenuLeaderboardBtn.down = function (x, y, obj) {
mainMenuGroup.visible = false;
setTab(2);
};
// --- Main Game Setup ---
function resetGame() {
// Remove candies
for (var i = candies.length - 1; i >= 0; i--) {
candies[i].destroy();
candies.splice(i, 1);
}
score = 0;
candiesCollected = 0;
candiesToCollect = 10 + (level - 1) * 2;
candySpawnTimer = 0;
scoreTxt.setText('0');
gameActive = true;
// Reset health, add +1 if health booster owned
health = 3 + (storage.itemInventory && storage.itemInventory.healthBooster ? storage.itemInventory.healthBooster : 0);
var hearts = '';
for (var h = 0; h < health; h++) hearts += '❤';
healthTxt.setText(hearts);
// Collector position
collector.x = 2048 / 2;
collector.y = 2732 - 220;
// Reset item effect timers
slowMotionActive = false;
slowMotionTimer = 0;
autoClickerActive = false;
autoClickerTimer = 0;
}
// Collector (player's swipe area)
collector = new Collector();
collector.x = 2048 / 2;
collector.y = 2732 - 220;
game.addChild(collector);
// --- Tab Switching ---
function setTab(tabIdx) {
currentTab = tabIdx;
// Game tab
if (tabIdx === 0) {
game.visible = true;
shopGroup.visible = false;
leaderboardGroup.visible = false;
coinTxt.visible = true;
scoreTxt.visible = true;
collector.visible = true;
}
// Shop tab
if (tabIdx === 1) {
game.visible = true;
shopGroup.visible = true;
leaderboardGroup.visible = false;
coinTxt.visible = false;
scoreTxt.visible = false;
collector.visible = false;
shopCoinTxt.setText('Coins: ' + coins);
for (var i = 0; i < shopItems.length; i++) {
var owned = storage.unlockedSkins.indexOf(shopItems[i].skinId) !== -1;
shopItems[i].owned = owned;
shopItems[i].label.setText(owned ? 'Owned' : shopSkins[i].price + ' coins');
shopItems[i].label.fill = owned ? '#3bff6e' : '#fff';
shopItems[i].selectTxt.visible = storage.selectedSkin === shopItems[i].skinId;
// Show/hide based on tab
shopItems[i].visible = shopCurrentTab === 0;
}
updateShopTab();
}
// Leaderboard tab
if (tabIdx === 2) {
game.visible = true;
shopGroup.visible = false;
leaderboardGroup.visible = true;
coinTxt.visible = false;
scoreTxt.visible = false;
collector.visible = false;
}
}
// Tab button handlers
tabBtnGame.down = function (x, y, obj) {
setTab(0);
};
tabBtnShop.down = function (x, y, obj) {
setTab(1);
};
tabBtnLeaderboard.down = function (x, y, obj) {
setTab(2);
};
// --- Item Usage Buttons ---
var itemBtnSlow = new Text2('Slow', {
size: 70,
fill: '#3b9bff'
});
itemBtnSlow.anchor.set(0.5, 0);
itemBtnSlow.x = 2048 / 2 - 200;
itemBtnSlow.y = 120;
itemBtnSlow.interactive = true;
itemBtnSlow.buttonMode = true;
LK.gui.top.addChild(itemBtnSlow);
var itemBtnAuto = new Text2('Auto', {
size: 70,
fill: '#3b9bff'
});
itemBtnAuto.anchor.set(0.5, 0);
itemBtnAuto.x = 2048 / 2 + 200;
itemBtnAuto.y = 120;
itemBtnAuto.interactive = true;
itemBtnAuto.buttonMode = true;
LK.gui.top.addChild(itemBtnAuto);
// Effect state
var slowMotionActive = false;
var slowMotionTimer = 0;
var autoClickerActive = false;
var autoClickerTimer = 0;
// Button handlers
itemBtnSlow.down = function (x, y, obj) {
if (!gameActive || mainMenuGroup.visible || currentTab !== 0) return;
if (storage.itemInventory && storage.itemInventory.slowMotion > 0 && !slowMotionActive) {
storage.itemInventory.slowMotion--;
slowMotionActive = true;
slowMotionTimer = 600; // 10 seconds at 60fps
itemBtnSlow.setText('Slow (' + storage.itemInventory.slowMotion + ')');
itemBtnSlow.fill = '#ffe23b';
}
};
itemBtnAuto.down = function (x, y, obj) {
if (!gameActive || mainMenuGroup.visible || currentTab !== 0) return;
if (storage.itemInventory && storage.itemInventory.autoClicker > 0 && !autoClickerActive) {
storage.itemInventory.autoClicker--;
autoClickerActive = true;
autoClickerTimer = 600; // 10 seconds at 60fps
itemBtnAuto.setText('Auto (' + storage.itemInventory.autoClicker + ')');
itemBtnAuto.fill = '#ffe23b';
}
};
// Update button text on game start
itemBtnSlow.setText('Slow (' + (storage.itemInventory && storage.itemInventory.slowMotion || 0) + ')');
itemBtnAuto.setText('Auto (' + (storage.itemInventory && storage.itemInventory.autoClicker || 0) + ')');
itemBtnSlow.fill = '#3b9bff';
itemBtnAuto.fill = '#3b9bff';
// --- Touch Controls ---
// Drag collector
game.down = function (x, y, obj) {
if (mainMenuGroup.visible || currentTab !== 0 || !gameActive) return;
// Only drag if touch is on collector
var local = collector.toLocal({
x: x,
y: y
});
if (local.x > -collector.width / 2 && local.x < collector.width / 2 && local.y > -collector.height / 2 && local.y < collector.height / 2) {
isDragging = true;
dragOffsetX = collector.x - x;
dragOffsetY = collector.y - y;
}
};
game.up = function (x, y, obj) {
if (mainMenuGroup.visible || currentTab !== 0 || !gameActive) return;
isDragging = false;
};
game.move = function (x, y, obj) {
if (mainMenuGroup.visible || currentTab !== 0 || !gameActive) return;
if (isDragging) {
collector.x = Math.max(collector.width / 2, Math.min(2048 - collector.width / 2, x + dragOffsetX));
collector.y = Math.max(collector.height / 2 + 200, Math.min(2732 - collector.height / 2, y + dragOffsetY));
}
// Swipe detection: check for candies under finger
for (var i = candies.length - 1; i >= 0; i--) {
var candy = candies[i];
if (!candy.collected && candy.visible) {
var dx = candy.x - x;
var dy = candy.y - y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist < candy.width / 2 + 40) {
collectCandy(candy);
}
}
}
};
// --- Candy Collection ---
function collectCandy(candy) {
if (candy.collected) return;
candy.collected = true;
candiesCollected++;
score++;
scoreTxt.setText(score);
// Animate
tween(candy, {
scaleX: 1.4,
scaleY: 1.4,
alpha: 0
}, {
duration: 220,
easing: tween.cubicIn,
onFinish: function onFinish() {
candy.destroy();
}
});
LK.getSound('collect').play();
// Coin drop
if (candy.hasCoin) {
coins++;
storage.coins = coins;
coinTxt.setText(coins);
shopCoinTxt.setText('Coins: ' + coins);
// Coin fly animation
var coinFly = new CoinFly();
coinFly.x = candy.x;
coinFly.y = candy.y;
game.addChild(coinFly);
tween(coinFly, {
x: 2048 - 120,
y: 120,
scaleX: 0.5,
scaleY: 0.5,
alpha: 0
}, {
duration: 600,
easing: tween.cubicIn,
onFinish: function onFinish() {
coinFly.destroy();
}
});
LK.getSound('coin').play();
}
// Remove from candies array
for (var i = 0; i < candies.length; i++) {
if (candies[i] === candy) {
candies.splice(i, 1);
break;
}
}
// Win condition
if (candiesCollected >= candiesToCollect) {
// Save high score
if (!storage.highScore || score > storage.highScore) {
storage.highScore = score;
}
// Loop: advance to next level and reset game state
level++;
resetGame();
setTab(0);
return;
}
}
// --- Game Update Loop ---
game.update = function () {
if (mainMenuGroup.visible || currentTab !== 0 || !gameActive) return;
// --- Item Effects ---
if (slowMotionActive) {
slowMotionTimer--;
if (slowMotionTimer <= 0) {
slowMotionActive = false;
itemBtnSlow.fill = '#3b9bff';
itemBtnSlow.setText('Slow (' + (storage.itemInventory && storage.itemInventory.slowMotion || 0) + ')');
}
}
if (autoClickerActive) {
autoClickerTimer--;
if (autoClickerTimer <= 0) {
autoClickerActive = false;
itemBtnAuto.fill = '#3b9bff';
itemBtnAuto.setText('Auto (' + (storage.itemInventory && storage.itemInventory.autoClicker || 0) + ')');
}
}
// Spawn candies
candySpawnTimer++;
var spawnInterval = slowMotionActive ? Math.round(candySpawnInterval * 1.5) : candySpawnInterval;
if (candies.length < candiesToCollect && candySpawnTimer >= spawnInterval) {
candySpawnTimer = 0;
var candy = new Candy();
// Use selected skin
candy.skin = storage.selectedSkin || 'candyRed';
// Random X, avoid edges
candy.x = 120 + Math.random() * (2048 - 240);
candy.y = -80;
// Randomize speed a bit
candy.speed = (slowMotionActive ? 0.5 : 1) * (10 + Math.random() * 6 + level * 0.5);
candies.push(candy);
game.addChild(candy);
}
// Update candies
for (var i = candies.length - 1; i >= 0; i--) {
var candy = candies[i];
if (slowMotionActive) {
// Slow down falling speed
candy.y += (candy.speed || 12) * 0.5;
} else {
candy.update();
}
// Collector collision
if (!candy.collected && collector.visible) {
var dx = candy.x - collector.x;
var dy = candy.y - collector.y;
if (Math.abs(dx) < candy.width / 2 + collector.width / 2 - 30 && Math.abs(dy) < candy.height / 2 + collector.height / 2 - 30) {
collectCandy(candy);
}
}
// Off-screen (missed)
if (!candy.collected && candy.y > 2732 + 80 && !candy.missed) {
candy.missed = true;
// Animate out
tween(candy, {
alpha: 0
}, {
duration: 200,
onFinish: function onFinish() {
candy.destroy();
}
});
// Remove from candies array
candies.splice(i, 1);
// Decrease health and update display
health--;
var hearts = '';
for (var h = 0; h < health; h++) hearts += '❤';
healthTxt.setText(hearts);
LK.effects.flashScreen(0xff0000, 600);
if (health <= 0) {
LK.showGameOver();
gameActive = false;
}
}
}
// --- Auto Clicker effect: auto collect nearest candy ---
if (autoClickerActive && candies.length > 0) {
// Find lowest (closest to bottom) visible, uncollected candy
var best = null,
bestY = -Infinity;
for (var i = 0; i < candies.length; i++) {
if (!candies[i].collected && candies[i].visible && candies[i].y > bestY) {
best = candies[i];
bestY = candies[i].y;
}
}
if (best) {
collectCandy(best);
}
}
};
// --- Music ---
LK.playMusic('bgmusic');
// --- Game Over/Win Handlers ---
LK.on('gameover', function () {
// Reset game state
resetGame();
mainMenuGroup.visible = true;
setTab(0);
});
LK.on('youwin', function () {
// Next level
level++;
resetGame();
mainMenuGroup.visible = true;
setTab(0);
});
// --- Initial State ---
resetGame();
mainMenuGroup.visible = true;
setTab(0);
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