/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ // AnimatedBackground class: vibrant animated color waves and shapes var AnimatedBackground = Container.expand(function () { var self = Container.call(this); // Create several colored ellipses and rectangles that move and fade var shapes = []; var colorList = [0x4caf50, 0x42a5f5, 0xffb300, 0xffe082, 0xff5252, 0x7e57c2, 0x00bcd4, 0xff4081]; var numShapes = 8; for (var i = 0; i < numShapes; i++) { var isEllipse = Math.random() > 0.5; var color = colorList[i % colorList.length]; var assetId = isEllipse ? 'item_basic' : 'item_rare'; var shape = LK.getAsset(assetId, { anchorX: 0.5, anchorY: 0.5 }); shape.width = 900 + Math.random() * 600; shape.height = 320 + Math.random() * 400; shape.x = 2048 * Math.random(); shape.y = 2732 * Math.random(); shape.alpha = 0.13 + Math.random() * 0.18; shape.tint = color; self.addChild(shape); // Animation state shape._vx = (Math.random() - 0.5) * 0.7; shape._vy = (Math.random() - 0.5) * 0.5; shape._va = (Math.random() - 0.5) * 0.002; shape._baseAlpha = shape.alpha; shape._alphaPhase = Math.random() * Math.PI * 2; shapes.push(shape); } // Animate color waves and movement self.update = function () { for (var i = 0; i < shapes.length; i++) { var s = shapes[i]; // Move s.x += s._vx; s.y += s._vy; // Wrap around screen if (s.x < -s.width / 2) s.x = 2048 + s.width / 2; if (s.x > 2048 + s.width / 2) s.x = -s.width / 2; if (s.y < -s.height / 2) s.y = 2732 + s.height / 2; if (s.y > 2732 + s.height / 2) s.y = -s.height / 2; // Animate alpha for wave effect s.alpha = s._baseAlpha + Math.sin(LK.ticks * 0.008 + s._alphaPhase) * 0.09; // Animate tint for extra vibrancy var t = (Math.sin(LK.ticks * 0.004 + i) + 1) * 0.5; var base = s.tint; // Simple color shift: lerp toward white and back var r = base >> 16 & 0xFF, g = base >> 8 & 0xFF, b = base & 0xFF; var tr = Math.floor(r + (255 - r) * t * 0.18); var tg = Math.floor(g + (255 - g) * t * 0.18); var tb = Math.floor(b + (255 - b) * t * 0.18); s.tint = tr << 16 | tg << 8 | tb; } }; return self; }); // Item class for all falling items var Item = Container.expand(function () { var self = Container.call(this); // Asset id and score are set on creation self.assetId = 'item_basic'; self.scoreValue = 1; self.isBad = false; // true for bomb/spike self.isTapped = false; // Attach asset var itemGfx = self.attachAsset(self.assetId, { anchorX: 0.5, anchorY: 0.5 }); // Set up item visuals self.setType = function (type) { // type: {assetId, scoreValue, isBad} self.assetId = type.assetId; self.scoreValue = type.scoreValue; self.isBad = type.isBad; // Remove old asset if any if (itemGfx && itemGfx.parent) itemGfx.parent.removeChild(itemGfx); itemGfx = self.attachAsset(self.assetId, { anchorX: 0.5, anchorY: 0.5 }); // Add magical aura/glow for good items if (!self.isBad) { // Basic: faint green glow if (self.assetId === 'item_basic') { var aura = LK.getAsset('item_basic', { anchorX: 0.5, anchorY: 0.5 }); aura.width = itemGfx.width * 1.5; aura.height = itemGfx.height * 1.5; aura.alpha = 0.22; aura.tint = 0x7fffbe; self.addChild(aura); aura.zIndex = -1; } // Rare: blue aura, more intense, with a second layer if (self.assetId === 'item_rare') { var aura1 = LK.getAsset('item_rare', { anchorX: 0.5, anchorY: 0.5 }); aura1.width = itemGfx.width * 1.7; aura1.height = itemGfx.height * 1.7; aura1.alpha = 0.18; aura1.tint = 0x7ecbff; self.addChild(aura1); aura1.zIndex = -2; var aura2 = LK.getAsset('item_rare', { anchorX: 0.5, anchorY: 0.5 }); aura2.width = itemGfx.width * 1.25; aura2.height = itemGfx.height * 1.25; aura2.alpha = 0.32; aura2.tint = 0x42a5f5; self.addChild(aura2); aura2.zIndex = -1; } // Legend: golden aura, multi-layer if (self.assetId === 'item_legend') { var aura1 = LK.getAsset('item_legend', { anchorX: 0.5, anchorY: 0.5 }); aura1.width = itemGfx.width * 2.1; aura1.height = itemGfx.height * 2.1; aura1.alpha = 0.13; aura1.tint = 0xfff7b2; self.addChild(aura1); aura1.zIndex = -3; var aura2 = LK.getAsset('item_legend', { anchorX: 0.5, anchorY: 0.5 }); aura2.width = itemGfx.width * 1.5; aura2.height = itemGfx.height * 1.5; aura2.alpha = 0.22; aura2.tint = 0xffe082; self.addChild(aura2); aura2.zIndex = -2; var aura3 = LK.getAsset('item_legend', { anchorX: 0.5, anchorY: 0.5 }); aura3.width = itemGfx.width * 1.1; aura3.height = itemGfx.height * 1.1; aura3.alpha = 0.32; aura3.tint = 0xffc107; self.addChild(aura3); aura3.zIndex = -1; } } // For bomb, add a fuse and a little rotation if (self.assetId === 'item_bomb') { itemGfx.rotation = Math.PI / 8 * (Math.random() > 0.5 ? 1 : -1); // Add a fuse (small rectangle at top) var fuse = LK.getAsset('item_rare', { anchorX: 0.5, anchorY: 1.0 }); fuse.width = 18; fuse.height = 48; fuse.x = 0; fuse.y = -itemGfx.height / 2 + 10; fuse.alpha = 0.7; self.addChild(fuse); // Add a spark (small yellow ellipse) var spark = LK.getAsset('item_legend', { anchorX: 0.5, anchorY: 0.5 }); spark.width = 28; spark.height = 18; spark.x = 0; spark.y = fuse.y - 20; spark.alpha = 0.8; self.addChild(spark); } // For spike, make it a grey ball with spikes around it if (self.assetId === 'item_spike') { // Remove the box and use a light grey ellipse as the main body if (itemGfx && itemGfx.parent) itemGfx.parent.removeChild(itemGfx); itemGfx = self.attachAsset('item_bomb', { anchorX: 0.5, anchorY: 0.5 }); // Make it a light grey itemGfx.tint = 0xdddddd; // Add spikes around the ball var spikeCount = 8; var radius = itemGfx.width * 0.52; for (var s = 0; s < spikeCount; s++) { var angle = Math.PI * 2 / spikeCount * s; var spike = LK.getAsset('item_rare', { anchorX: 0.5, anchorY: 1.0 }); spike.width = itemGfx.width * 0.13; spike.height = itemGfx.height * 0.32; spike.x = Math.cos(angle) * radius; spike.y = Math.sin(angle) * radius; spike.rotation = angle; spike.alpha = 0.92; spike.tint = 0x888888; self.addChild(spike); } // Optionally, add a faint shadow for depth var shadow = LK.getAsset('item_bomb', { anchorX: 0.5, anchorY: 0.5 }); shadow.width = itemGfx.width * 1.12; shadow.height = itemGfx.height * 1.12; shadow.alpha = 0.13; shadow.tint = 0x222222; self.addChild(shadow); shadow.zIndex = -2; } }; // Called when tapped self.onTap = function () { if (self.isTapped) return; self.isTapped = true; // Animate: if bad, just remove immediately if (self.isBad) { if (self.parent) self.parent.removeChild(self); } else { // Good item: pop scale up, then shrink and fade out var origScaleX = self.scaleX; var origScaleY = self.scaleY; tween(self, { scaleX: origScaleX * 1.25, scaleY: origScaleY * 1.25 }, { duration: 90, easing: tween.cubicOut, onFinish: function onFinish() { tween(self, { scaleX: 0.2, scaleY: 0.2, alpha: 0 }, { duration: 180, easing: tween.cubicIn, onFinish: function onFinish() { if (self.parent) self.parent.removeChild(self); } }); } }); } }; // Called every tick self.update = function () { // Movement handled in game loop }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x181818 }); /**** * Game Code ****/ // Add AnimatedBackground behind everything var animatedBg = new AnimatedBackground(); game.addChild(animatedBg); game.setChildIndex(animatedBg, 0); // Ensure it's at the back // spike: keep as box, but will add visual in class // rare: blue, slightly smaller // legend: larger, gold // bomb: dark ellipse // Item types // Good items // Bad items // Pop effect // Sounds // Music var ITEM_TYPES = [{ assetId: 'item_basic', scoreValue: 1, isBad: false, weight: 55 }, { assetId: 'item_rare', scoreValue: 3, isBad: false, weight: 18 }, { assetId: 'item_legend', scoreValue: 7, isBad: false, weight: 5 }, { assetId: 'item_bomb', scoreValue: 0, isBad: true, weight: 10 }, { assetId: 'item_spike', scoreValue: 0, isBad: true, weight: 5 }]; // Weighted random selection function pickItemType() { var total = 0; for (var i = 0; i < ITEM_TYPES.length; i++) total += ITEM_TYPES[i].weight; var r = Math.random() * total; var acc = 0; for (var i = 0; i < ITEM_TYPES.length; i++) { acc += ITEM_TYPES[i].weight; if (r < acc) return ITEM_TYPES[i]; } return ITEM_TYPES[0]; } // Game state var items = []; var lives = 3; var score = 0; var spawnInterval = 1000; // ms var minSpawnInterval = 350; var spawnTimer = null; var itemFallSpeed = 7; // px per frame var itemSpeedInc = 0.15; var itemSpawnDec = 30; var lastTapTime = 0; // Difficulty menu var difficultyMenu = null; var difficultySettings = { Easy: { lives: 5, spawnInterval: 1200, minSpawnInterval: 500, itemFallSpeed: 5, itemSpeedInc: 0.10, itemSpawnDec: 18 }, Normal: { lives: 3, spawnInterval: 1000, minSpawnInterval: 350, itemFallSpeed: 7, itemSpeedInc: 0.15, itemSpawnDec: 30 }, Hard: { lives: 2, spawnInterval: 800, minSpawnInterval: 200, itemFallSpeed: 10, itemSpeedInc: 0.22, itemSpawnDec: 45 }, Insane: { lives: 1, spawnInterval: 500, minSpawnInterval: 100, itemFallSpeed: 16, itemSpeedInc: 0.38, itemSpawnDec: 80 } }; var selectedDifficulty = "Normal"; // Show difficulty menu at game start function showDifficultyMenu() { if (difficultyMenu && difficultyMenu.parent) difficultyMenu.parent.removeChild(difficultyMenu); difficultyMenu = new Container(); // Background overlay var bg = LK.getAsset('item_basic', { anchorX: 0.5, anchorY: 0.5 }); bg.width = 900; bg.height = 900; bg.alpha = 0.92; bg.x = 2048 / 2; bg.y = 2732 / 2; difficultyMenu.addChild(bg); // Title var title = new Text2("Select Difficulty", { size: 120, fill: "#fff" }); title.anchor.set(0.5, 0); title.x = 2048 / 2; title.y = 2732 / 2 - 320; difficultyMenu.addChild(title); // Buttons var btnY = 2732 / 2 - 80; var btns = ["Easy", "Normal", "Hard", "Insane"]; for (var i = 0; i < btns.length; i++) { (function (idx) { var diff = btns[idx]; var btn = LK.getAsset('item_legend', { anchorX: 0.5, anchorY: 0.5 }); btn.width = 520; btn.height = 160; btn.x = 2048 / 2; btn.y = btnY + idx * 200; difficultyMenu.addChild(btn); var label = new Text2(diff, { size: 90, fill: diff === "Insane" ? "#b71c1c" : "#222" }); label.anchor.set(0.5, 0.5); label.x = btn.x; label.y = btn.y; difficultyMenu.addChild(label); btn.interactive = true; btn.down = function (x, y, obj) { selectDifficulty(diff); }; })(i); } // Add to game game.addChild(difficultyMenu); // Pause spawn if (spawnTimer) LK.clearInterval(spawnTimer); } // Set difficulty and start game function selectDifficulty(diff) { selectedDifficulty = diff; var s = difficultySettings[diff]; lives = s.lives; spawnInterval = s.spawnInterval; minSpawnInterval = s.minSpawnInterval; itemFallSpeed = s.itemFallSpeed; itemSpeedInc = s.itemSpeedInc; itemSpawnDec = s.itemSpawnDec; updateLives(); score = 0; scoreTxt.setText(score); if (difficultyMenu && difficultyMenu.parent) difficultyMenu.parent.removeChild(difficultyMenu); startSpawnTimer(); } // Show menu at game start showDifficultyMenu(); // GUI var scoreTxt = new Text2('0', { size: 120, fill: "#fff" }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); var livesTxt = new Text2('♥♥♥', { size: 90, fill: 0xFF5252 }); livesTxt.anchor.set(1, 0); livesTxt.x = -40; // right padding LK.gui.topRight.addChild(livesTxt); // Prevent elements in top left 100x100 // (No elements added to gui.topLeft or at (0,0)) // Music LK.playMusic('bgmusic'); // Spawn a new item function spawnItem() { var type = pickItemType(); var item; item = new Item(); item.setType(type); // Assign per-item fall speed based on rarity/type // Default: base on global itemFallSpeed, then scale by rarity var speedMultiplier = 1; if (type.assetId === 'item_basic') speedMultiplier = 1;else if (type.assetId === 'item_rare') speedMultiplier = 1.18;else if (type.assetId === 'item_legend') speedMultiplier = 1.35;else if (type.assetId === 'item_bomb') speedMultiplier = 1.10;else if (type.assetId === 'item_spike') speedMultiplier = 1.22; item.fallSpeed = itemFallSpeed * speedMultiplier; // Random X, avoid edges, space out more as score increases var baseMargin = 120; var extraMargin = Math.min(260, Math.floor(score / 15) * 40); // +40px margin every 15 points, max +260 var margin = baseMargin + extraMargin; var x = margin + Math.random() * (2048 - 2 * margin); item.x = x; item.y = -100; // For touch detection item.width = LK.getAsset(type.assetId, { anchorX: 0.5, anchorY: 0.5 }).width; item.height = LK.getAsset(type.assetId, { anchorX: 0.5, anchorY: 0.5 }).height; // Make good items have a slightly larger hitbox (but smaller than before) if (!type.isBad) { item.width *= 1.15; item.height *= 1.15; } // Add to game game.addChild(item); items.push(item); // No pop-in animation item.scaleX = 1; item.scaleY = 1; // If this is a bomb, do nothing special for background } // Update lives display function updateLives() { var s = ''; for (var i = 0; i < lives; i++) s += '♥'; livesTxt.setText(s); } // Handle tap function handleTap(x, y, obj) { // Find topmost item under tap for (var i = items.length - 1; i >= 0; i--) { var item = items[i]; // Simple bounding box check var dx = x - item.x; var dy = y - item.y; var w = item.width / 2; var h = item.height / 2; if (dx * dx / (w * w) + dy * dy / (h * h) <= 1.1) { // Tapped this item if (item.isTapped) continue; item.onTap(); if (item.isBad) { // Lose a life lives--; updateLives(); LK.getSound('bad').play(); LK.effects.flashScreen(0xff0000, 350); if (lives <= 0) { LK.getSound('lose').play(); LK.showGameOver(); return; } } else { // Score! score += item.scoreValue; LK.setScore(score); scoreTxt.setText(score); LK.getSound('score').play(); if (item.parent) item.parent.removeChild(item); } // Remove item from array items.splice(i, 1); break; } } } // Game move/tap handler game.down = function (x, y, obj) { handleTap(x, y, obj); // End hold on all hold items when released game.up = function (x, y, obj) {}; // Main update loop game.update = function () { // Animate AnimatedBackground if (animatedBg && animatedBg.update) animatedBg.update(); // Move items for (var i = items.length - 1; i >= 0; i--) { var item = items[i]; item.y += typeof item.fallSpeed === "number" ? item.fallSpeed : itemFallSpeed; // If not tapped and off screen if (!item.isTapped && item.y > 2732 + 100) { // If good item missed, lose a life if (!item.isBad) { lives--; updateLives(); LK.getSound('life').play(); LK.effects.flashScreen(0xff9800, 250); if (lives <= 0) { LK.getSound('lose').play(); LK.showGameOver(); return; } } // Remove from game if (!item.isBad && !item.isTapped) { if (item.parent) item.parent.removeChild(item); } items.splice(i, 1); continue; } } }; // Difficulty ramp: every 10 points, increase speed and spawn rate var level = Math.floor(score / 10); var targetSpeed = 7 + level * itemSpeedInc; if (itemFallSpeed < targetSpeed) itemFallSpeed += 0.02; var targetInterval = Math.max(minSpawnInterval, 1000 - level * itemSpawnDec); if (spawnInterval > targetInterval) spawnInterval -= 1.5; }; // Spawn timer function startSpawnTimer() { if (spawnTimer) LK.clearInterval(spawnTimer); spawnTimer = LK.setInterval(function () { spawnItem(); // Adjust interval LK.clearInterval(spawnTimer); startSpawnTimer(); }, spawnInterval); } startSpawnTimer(); // Reset on game over LK.on('gameover', function () { // Clean up for (var i = 0; i < items.length; i++) { if (items[i].parent) items[i].parent.removeChild(items[i]); } items = []; // Show difficulty menu again showDifficultyMenu(); LK.playMusic('bgmusic'); }); // Reset on win (not used, but for completeness) LK.on('youwin', function () { for (var i = 0; i < items.length; i++) { if (items[i].parent) items[i].parent.removeChild(items[i]); } items = []; showDifficultyMenu(); LK.playMusic('bgmusic'); });
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// AnimatedBackground class: vibrant animated color waves and shapes
var AnimatedBackground = Container.expand(function () {
var self = Container.call(this);
// Create several colored ellipses and rectangles that move and fade
var shapes = [];
var colorList = [0x4caf50, 0x42a5f5, 0xffb300, 0xffe082, 0xff5252, 0x7e57c2, 0x00bcd4, 0xff4081];
var numShapes = 8;
for (var i = 0; i < numShapes; i++) {
var isEllipse = Math.random() > 0.5;
var color = colorList[i % colorList.length];
var assetId = isEllipse ? 'item_basic' : 'item_rare';
var shape = LK.getAsset(assetId, {
anchorX: 0.5,
anchorY: 0.5
});
shape.width = 900 + Math.random() * 600;
shape.height = 320 + Math.random() * 400;
shape.x = 2048 * Math.random();
shape.y = 2732 * Math.random();
shape.alpha = 0.13 + Math.random() * 0.18;
shape.tint = color;
self.addChild(shape);
// Animation state
shape._vx = (Math.random() - 0.5) * 0.7;
shape._vy = (Math.random() - 0.5) * 0.5;
shape._va = (Math.random() - 0.5) * 0.002;
shape._baseAlpha = shape.alpha;
shape._alphaPhase = Math.random() * Math.PI * 2;
shapes.push(shape);
}
// Animate color waves and movement
self.update = function () {
for (var i = 0; i < shapes.length; i++) {
var s = shapes[i];
// Move
s.x += s._vx;
s.y += s._vy;
// Wrap around screen
if (s.x < -s.width / 2) s.x = 2048 + s.width / 2;
if (s.x > 2048 + s.width / 2) s.x = -s.width / 2;
if (s.y < -s.height / 2) s.y = 2732 + s.height / 2;
if (s.y > 2732 + s.height / 2) s.y = -s.height / 2;
// Animate alpha for wave effect
s.alpha = s._baseAlpha + Math.sin(LK.ticks * 0.008 + s._alphaPhase) * 0.09;
// Animate tint for extra vibrancy
var t = (Math.sin(LK.ticks * 0.004 + i) + 1) * 0.5;
var base = s.tint;
// Simple color shift: lerp toward white and back
var r = base >> 16 & 0xFF,
g = base >> 8 & 0xFF,
b = base & 0xFF;
var tr = Math.floor(r + (255 - r) * t * 0.18);
var tg = Math.floor(g + (255 - g) * t * 0.18);
var tb = Math.floor(b + (255 - b) * t * 0.18);
s.tint = tr << 16 | tg << 8 | tb;
}
};
return self;
});
// Item class for all falling items
var Item = Container.expand(function () {
var self = Container.call(this);
// Asset id and score are set on creation
self.assetId = 'item_basic';
self.scoreValue = 1;
self.isBad = false; // true for bomb/spike
self.isTapped = false;
// Attach asset
var itemGfx = self.attachAsset(self.assetId, {
anchorX: 0.5,
anchorY: 0.5
});
// Set up item visuals
self.setType = function (type) {
// type: {assetId, scoreValue, isBad}
self.assetId = type.assetId;
self.scoreValue = type.scoreValue;
self.isBad = type.isBad;
// Remove old asset if any
if (itemGfx && itemGfx.parent) itemGfx.parent.removeChild(itemGfx);
itemGfx = self.attachAsset(self.assetId, {
anchorX: 0.5,
anchorY: 0.5
});
// Add magical aura/glow for good items
if (!self.isBad) {
// Basic: faint green glow
if (self.assetId === 'item_basic') {
var aura = LK.getAsset('item_basic', {
anchorX: 0.5,
anchorY: 0.5
});
aura.width = itemGfx.width * 1.5;
aura.height = itemGfx.height * 1.5;
aura.alpha = 0.22;
aura.tint = 0x7fffbe;
self.addChild(aura);
aura.zIndex = -1;
}
// Rare: blue aura, more intense, with a second layer
if (self.assetId === 'item_rare') {
var aura1 = LK.getAsset('item_rare', {
anchorX: 0.5,
anchorY: 0.5
});
aura1.width = itemGfx.width * 1.7;
aura1.height = itemGfx.height * 1.7;
aura1.alpha = 0.18;
aura1.tint = 0x7ecbff;
self.addChild(aura1);
aura1.zIndex = -2;
var aura2 = LK.getAsset('item_rare', {
anchorX: 0.5,
anchorY: 0.5
});
aura2.width = itemGfx.width * 1.25;
aura2.height = itemGfx.height * 1.25;
aura2.alpha = 0.32;
aura2.tint = 0x42a5f5;
self.addChild(aura2);
aura2.zIndex = -1;
}
// Legend: golden aura, multi-layer
if (self.assetId === 'item_legend') {
var aura1 = LK.getAsset('item_legend', {
anchorX: 0.5,
anchorY: 0.5
});
aura1.width = itemGfx.width * 2.1;
aura1.height = itemGfx.height * 2.1;
aura1.alpha = 0.13;
aura1.tint = 0xfff7b2;
self.addChild(aura1);
aura1.zIndex = -3;
var aura2 = LK.getAsset('item_legend', {
anchorX: 0.5,
anchorY: 0.5
});
aura2.width = itemGfx.width * 1.5;
aura2.height = itemGfx.height * 1.5;
aura2.alpha = 0.22;
aura2.tint = 0xffe082;
self.addChild(aura2);
aura2.zIndex = -2;
var aura3 = LK.getAsset('item_legend', {
anchorX: 0.5,
anchorY: 0.5
});
aura3.width = itemGfx.width * 1.1;
aura3.height = itemGfx.height * 1.1;
aura3.alpha = 0.32;
aura3.tint = 0xffc107;
self.addChild(aura3);
aura3.zIndex = -1;
}
}
// For bomb, add a fuse and a little rotation
if (self.assetId === 'item_bomb') {
itemGfx.rotation = Math.PI / 8 * (Math.random() > 0.5 ? 1 : -1);
// Add a fuse (small rectangle at top)
var fuse = LK.getAsset('item_rare', {
anchorX: 0.5,
anchorY: 1.0
});
fuse.width = 18;
fuse.height = 48;
fuse.x = 0;
fuse.y = -itemGfx.height / 2 + 10;
fuse.alpha = 0.7;
self.addChild(fuse);
// Add a spark (small yellow ellipse)
var spark = LK.getAsset('item_legend', {
anchorX: 0.5,
anchorY: 0.5
});
spark.width = 28;
spark.height = 18;
spark.x = 0;
spark.y = fuse.y - 20;
spark.alpha = 0.8;
self.addChild(spark);
}
// For spike, make it a grey ball with spikes around it
if (self.assetId === 'item_spike') {
// Remove the box and use a light grey ellipse as the main body
if (itemGfx && itemGfx.parent) itemGfx.parent.removeChild(itemGfx);
itemGfx = self.attachAsset('item_bomb', {
anchorX: 0.5,
anchorY: 0.5
});
// Make it a light grey
itemGfx.tint = 0xdddddd;
// Add spikes around the ball
var spikeCount = 8;
var radius = itemGfx.width * 0.52;
for (var s = 0; s < spikeCount; s++) {
var angle = Math.PI * 2 / spikeCount * s;
var spike = LK.getAsset('item_rare', {
anchorX: 0.5,
anchorY: 1.0
});
spike.width = itemGfx.width * 0.13;
spike.height = itemGfx.height * 0.32;
spike.x = Math.cos(angle) * radius;
spike.y = Math.sin(angle) * radius;
spike.rotation = angle;
spike.alpha = 0.92;
spike.tint = 0x888888;
self.addChild(spike);
}
// Optionally, add a faint shadow for depth
var shadow = LK.getAsset('item_bomb', {
anchorX: 0.5,
anchorY: 0.5
});
shadow.width = itemGfx.width * 1.12;
shadow.height = itemGfx.height * 1.12;
shadow.alpha = 0.13;
shadow.tint = 0x222222;
self.addChild(shadow);
shadow.zIndex = -2;
}
};
// Called when tapped
self.onTap = function () {
if (self.isTapped) return;
self.isTapped = true;
// Animate: if bad, just remove immediately
if (self.isBad) {
if (self.parent) self.parent.removeChild(self);
} else {
// Good item: pop scale up, then shrink and fade out
var origScaleX = self.scaleX;
var origScaleY = self.scaleY;
tween(self, {
scaleX: origScaleX * 1.25,
scaleY: origScaleY * 1.25
}, {
duration: 90,
easing: tween.cubicOut,
onFinish: function onFinish() {
tween(self, {
scaleX: 0.2,
scaleY: 0.2,
alpha: 0
}, {
duration: 180,
easing: tween.cubicIn,
onFinish: function onFinish() {
if (self.parent) self.parent.removeChild(self);
}
});
}
});
}
};
// Called every tick
self.update = function () {
// Movement handled in game loop
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x181818
});
/****
* Game Code
****/
// Add AnimatedBackground behind everything
var animatedBg = new AnimatedBackground();
game.addChild(animatedBg);
game.setChildIndex(animatedBg, 0); // Ensure it's at the back
// spike: keep as box, but will add visual in class
// rare: blue, slightly smaller
// legend: larger, gold
// bomb: dark ellipse
// Item types
// Good items
// Bad items
// Pop effect
// Sounds
// Music
var ITEM_TYPES = [{
assetId: 'item_basic',
scoreValue: 1,
isBad: false,
weight: 55
}, {
assetId: 'item_rare',
scoreValue: 3,
isBad: false,
weight: 18
}, {
assetId: 'item_legend',
scoreValue: 7,
isBad: false,
weight: 5
}, {
assetId: 'item_bomb',
scoreValue: 0,
isBad: true,
weight: 10
}, {
assetId: 'item_spike',
scoreValue: 0,
isBad: true,
weight: 5
}];
// Weighted random selection
function pickItemType() {
var total = 0;
for (var i = 0; i < ITEM_TYPES.length; i++) total += ITEM_TYPES[i].weight;
var r = Math.random() * total;
var acc = 0;
for (var i = 0; i < ITEM_TYPES.length; i++) {
acc += ITEM_TYPES[i].weight;
if (r < acc) return ITEM_TYPES[i];
}
return ITEM_TYPES[0];
}
// Game state
var items = [];
var lives = 3;
var score = 0;
var spawnInterval = 1000; // ms
var minSpawnInterval = 350;
var spawnTimer = null;
var itemFallSpeed = 7; // px per frame
var itemSpeedInc = 0.15;
var itemSpawnDec = 30;
var lastTapTime = 0;
// Difficulty menu
var difficultyMenu = null;
var difficultySettings = {
Easy: {
lives: 5,
spawnInterval: 1200,
minSpawnInterval: 500,
itemFallSpeed: 5,
itemSpeedInc: 0.10,
itemSpawnDec: 18
},
Normal: {
lives: 3,
spawnInterval: 1000,
minSpawnInterval: 350,
itemFallSpeed: 7,
itemSpeedInc: 0.15,
itemSpawnDec: 30
},
Hard: {
lives: 2,
spawnInterval: 800,
minSpawnInterval: 200,
itemFallSpeed: 10,
itemSpeedInc: 0.22,
itemSpawnDec: 45
},
Insane: {
lives: 1,
spawnInterval: 500,
minSpawnInterval: 100,
itemFallSpeed: 16,
itemSpeedInc: 0.38,
itemSpawnDec: 80
}
};
var selectedDifficulty = "Normal";
// Show difficulty menu at game start
function showDifficultyMenu() {
if (difficultyMenu && difficultyMenu.parent) difficultyMenu.parent.removeChild(difficultyMenu);
difficultyMenu = new Container();
// Background overlay
var bg = LK.getAsset('item_basic', {
anchorX: 0.5,
anchorY: 0.5
});
bg.width = 900;
bg.height = 900;
bg.alpha = 0.92;
bg.x = 2048 / 2;
bg.y = 2732 / 2;
difficultyMenu.addChild(bg);
// Title
var title = new Text2("Select Difficulty", {
size: 120,
fill: "#fff"
});
title.anchor.set(0.5, 0);
title.x = 2048 / 2;
title.y = 2732 / 2 - 320;
difficultyMenu.addChild(title);
// Buttons
var btnY = 2732 / 2 - 80;
var btns = ["Easy", "Normal", "Hard", "Insane"];
for (var i = 0; i < btns.length; i++) {
(function (idx) {
var diff = btns[idx];
var btn = LK.getAsset('item_legend', {
anchorX: 0.5,
anchorY: 0.5
});
btn.width = 520;
btn.height = 160;
btn.x = 2048 / 2;
btn.y = btnY + idx * 200;
difficultyMenu.addChild(btn);
var label = new Text2(diff, {
size: 90,
fill: diff === "Insane" ? "#b71c1c" : "#222"
});
label.anchor.set(0.5, 0.5);
label.x = btn.x;
label.y = btn.y;
difficultyMenu.addChild(label);
btn.interactive = true;
btn.down = function (x, y, obj) {
selectDifficulty(diff);
};
})(i);
}
// Add to game
game.addChild(difficultyMenu);
// Pause spawn
if (spawnTimer) LK.clearInterval(spawnTimer);
}
// Set difficulty and start game
function selectDifficulty(diff) {
selectedDifficulty = diff;
var s = difficultySettings[diff];
lives = s.lives;
spawnInterval = s.spawnInterval;
minSpawnInterval = s.minSpawnInterval;
itemFallSpeed = s.itemFallSpeed;
itemSpeedInc = s.itemSpeedInc;
itemSpawnDec = s.itemSpawnDec;
updateLives();
score = 0;
scoreTxt.setText(score);
if (difficultyMenu && difficultyMenu.parent) difficultyMenu.parent.removeChild(difficultyMenu);
startSpawnTimer();
}
// Show menu at game start
showDifficultyMenu();
// GUI
var scoreTxt = new Text2('0', {
size: 120,
fill: "#fff"
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
var livesTxt = new Text2('♥♥♥', {
size: 90,
fill: 0xFF5252
});
livesTxt.anchor.set(1, 0);
livesTxt.x = -40; // right padding
LK.gui.topRight.addChild(livesTxt);
// Prevent elements in top left 100x100
// (No elements added to gui.topLeft or at (0,0))
// Music
LK.playMusic('bgmusic');
// Spawn a new item
function spawnItem() {
var type = pickItemType();
var item;
item = new Item();
item.setType(type);
// Assign per-item fall speed based on rarity/type
// Default: base on global itemFallSpeed, then scale by rarity
var speedMultiplier = 1;
if (type.assetId === 'item_basic') speedMultiplier = 1;else if (type.assetId === 'item_rare') speedMultiplier = 1.18;else if (type.assetId === 'item_legend') speedMultiplier = 1.35;else if (type.assetId === 'item_bomb') speedMultiplier = 1.10;else if (type.assetId === 'item_spike') speedMultiplier = 1.22;
item.fallSpeed = itemFallSpeed * speedMultiplier;
// Random X, avoid edges, space out more as score increases
var baseMargin = 120;
var extraMargin = Math.min(260, Math.floor(score / 15) * 40); // +40px margin every 15 points, max +260
var margin = baseMargin + extraMargin;
var x = margin + Math.random() * (2048 - 2 * margin);
item.x = x;
item.y = -100;
// For touch detection
item.width = LK.getAsset(type.assetId, {
anchorX: 0.5,
anchorY: 0.5
}).width;
item.height = LK.getAsset(type.assetId, {
anchorX: 0.5,
anchorY: 0.5
}).height;
// Make good items have a slightly larger hitbox (but smaller than before)
if (!type.isBad) {
item.width *= 1.15;
item.height *= 1.15;
}
// Add to game
game.addChild(item);
items.push(item);
// No pop-in animation
item.scaleX = 1;
item.scaleY = 1;
// If this is a bomb, do nothing special for background
}
// Update lives display
function updateLives() {
var s = '';
for (var i = 0; i < lives; i++) s += '♥';
livesTxt.setText(s);
}
// Handle tap
function handleTap(x, y, obj) {
// Find topmost item under tap
for (var i = items.length - 1; i >= 0; i--) {
var item = items[i];
// Simple bounding box check
var dx = x - item.x;
var dy = y - item.y;
var w = item.width / 2;
var h = item.height / 2;
if (dx * dx / (w * w) + dy * dy / (h * h) <= 1.1) {
// Tapped this item
if (item.isTapped) continue;
item.onTap();
if (item.isBad) {
// Lose a life
lives--;
updateLives();
LK.getSound('bad').play();
LK.effects.flashScreen(0xff0000, 350);
if (lives <= 0) {
LK.getSound('lose').play();
LK.showGameOver();
return;
}
} else {
// Score!
score += item.scoreValue;
LK.setScore(score);
scoreTxt.setText(score);
LK.getSound('score').play();
if (item.parent) item.parent.removeChild(item);
}
// Remove item from array
items.splice(i, 1);
break;
}
}
}
// Game move/tap handler
game.down = function (x, y, obj) {
handleTap(x, y, obj);
// End hold on all hold items when released
game.up = function (x, y, obj) {};
// Main update loop
game.update = function () {
// Animate AnimatedBackground
if (animatedBg && animatedBg.update) animatedBg.update();
// Move items
for (var i = items.length - 1; i >= 0; i--) {
var item = items[i];
item.y += typeof item.fallSpeed === "number" ? item.fallSpeed : itemFallSpeed;
// If not tapped and off screen
if (!item.isTapped && item.y > 2732 + 100) {
// If good item missed, lose a life
if (!item.isBad) {
lives--;
updateLives();
LK.getSound('life').play();
LK.effects.flashScreen(0xff9800, 250);
if (lives <= 0) {
LK.getSound('lose').play();
LK.showGameOver();
return;
}
}
// Remove from game
if (!item.isBad && !item.isTapped) {
if (item.parent) item.parent.removeChild(item);
}
items.splice(i, 1);
continue;
}
}
};
// Difficulty ramp: every 10 points, increase speed and spawn rate
var level = Math.floor(score / 10);
var targetSpeed = 7 + level * itemSpeedInc;
if (itemFallSpeed < targetSpeed) itemFallSpeed += 0.02;
var targetInterval = Math.max(minSpawnInterval, 1000 - level * itemSpawnDec);
if (spawnInterval > targetInterval) spawnInterval -= 1.5;
};
// Spawn timer
function startSpawnTimer() {
if (spawnTimer) LK.clearInterval(spawnTimer);
spawnTimer = LK.setInterval(function () {
spawnItem();
// Adjust interval
LK.clearInterval(spawnTimer);
startSpawnTimer();
}, spawnInterval);
}
startSpawnTimer();
// Reset on game over
LK.on('gameover', function () {
// Clean up
for (var i = 0; i < items.length; i++) {
if (items[i].parent) items[i].parent.removeChild(items[i]);
}
items = [];
// Show difficulty menu again
showDifficultyMenu();
LK.playMusic('bgmusic');
});
// Reset on win (not used, but for completeness)
LK.on('youwin', function () {
for (var i = 0; i < items.length; i++) {
if (items[i].parent) items[i].parent.removeChild(items[i]);
}
items = [];
showDifficultyMenu();
LK.playMusic('bgmusic');
});