/****
* 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');
});