/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ // Apple class: can be good or bad, and have different colors var Apple = Container.expand(function () { var self = Container.call(this); // Randomly decide if this apple is good or bad self.isGood = Math.random() < 0.7; // 70% good, 30% bad // Pick a random color for good and bad apples var goodAppleTypes = ['goodApple', 'goodAppleGreen', 'goodAppleYellow', 'goodAppleBlue', 'goodApplePurple']; var badAppleTypes = ['badApple', 'badApplePurple', 'badAppleBlue', 'badAppleOrange', 'badApplePink']; var assetId; if (self.isGood) { assetId = goodAppleTypes[Math.floor(Math.random() * goodAppleTypes.length)]; } else { assetId = badAppleTypes[Math.floor(Math.random() * badAppleTypes.length)]; } // Attach correct asset var appleAsset = self.attachAsset(assetId, { anchorX: 0.5, anchorY: 0.5 }); // Set falling speed (randomize a bit), increase as game progresses, but more slowly var minSpeed = 12; var maxSpeed = 32; // Progress increases more slowly: use sqrt for slower ramp-up var progress = Math.min(1, Math.sqrt(LK.ticks / (60 * 60))); // Slower acceleration var baseSpeed = minSpeed + (maxSpeed - minSpeed) * progress; self.speed = baseSpeed + Math.random() * 6; // Mix: 50% apples fall straight, 50% sloped and bounce if (Math.random() < 0.5) { // Flat: straight down self.xSpeed = 0; } else { // Sloping: random left/right, random speed self.xSpeed = (Math.random() < 0.5 ? -1 : 1) * (6 + Math.random() * 8); } // For edge bounce, need to know lastX self.lastX = self.x; // For intersection tracking self.lastIntersecting = false; // Update method called every tick self.update = function () { // Save lastX for edge bounce detection self.lastX = self.x; self.y += self.speed; self.x += self.xSpeed; // Bounce off left edge if (self.lastX >= 60 && self.x < 60) { self.x = 60; self.xSpeed *= -1; } // Bounce off right edge if (self.lastX <= GAME_WIDTH - 60 && self.x > GAME_WIDTH - 60) { self.x = GAME_WIDTH - 60; self.xSpeed *= -1; } }; return self; }); // Basket class var Basket = Container.expand(function () { var self = Container.call(this); var basketAsset = self.attachAsset('basket', { anchorX: 0.5, anchorY: 0.5 }); // Optionally, add a little "lip" to the basket for visual // (not required for MVP) // No update needed for basket return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x87ceeb // Sky blue }); /**** * Game Code ****/ // Music (optional, not used in MVP) // Sound for miss (optional, not used in MVP) // Sound for catching bad apple // Sound for catching good apple // Basket // Apple (rotten) // Apple (good) // Game constants var GAME_WIDTH = 2048; var GAME_HEIGHT = 2732; var BASKET_Y = GAME_HEIGHT - 200; // Basket near bottom var APPLE_SPAWN_INTERVAL = 45; // Ticks between apples (~0.75s) var TARGET_SCORE = 20; // Win condition // Game state var apples = []; var basket = null; var score = 1; var scoreTxt = null; var dragNode = null; var lastMoveX = null; // Add background image (centered, covers full game area) var background = LK.getAsset('background', { anchorX: 0.5, anchorY: 0.5, x: GAME_WIDTH / 2, y: GAME_HEIGHT / 2, width: GAME_WIDTH, height: GAME_HEIGHT }); game.addChild(background); // Create and position basket basket = new Basket(); basket.x = GAME_WIDTH / 2; basket.y = BASKET_Y; game.addChild(basket); // Score text scoreTxt = new Text2('0', { size: 120, fill: 0xFFFFFF }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); // Helper: update score display function updateScoreText() { scoreTxt.setText(score); } // Move handler for dragging basket function handleMove(x, y, obj) { if (dragNode) { // Clamp basket within screen var halfWidth = dragNode.width / 2; var newX = x; if (newX < halfWidth) newX = halfWidth; if (newX > GAME_WIDTH - halfWidth) newX = GAME_WIDTH - halfWidth; dragNode.x = newX; } } // Touch/mouse down: start dragging basket if touch is on basket or anywhere game.down = function (x, y, obj) { // Only allow drag if not in top left 100x100 (reserved for menu) if (x > 100 || y > 100) { dragNode = basket; handleMove(x, y, obj); } }; // Touch/mouse up: stop dragging game.up = function (x, y, obj) { dragNode = null; }; // Touch/mouse move: move basket if dragging game.move = function (x, y, obj) { handleMove(x, y, obj); }; // Main game update loop game.update = function () { // Dynamically decrease spawn interval as time passes (min 12 ticks, max 45) var minInterval = 12; var maxInterval = 45; var progress = Math.min(1, LK.ticks / (60 * 60)); // Progress over 60 seconds, clamp to 1 var dynamicInterval = Math.round(maxInterval - (maxInterval - minInterval) * progress); if (LK.ticks % dynamicInterval === 0) { var apple = new Apple(); // Random x, but keep fully on screen var minX = 80; var maxX = GAME_WIDTH - 80; apple.x = minX + Math.random() * (maxX - minX); apple.y = -80; // Start above screen apples.push(apple); game.addChild(apple); } // Update apples for (var i = apples.length - 1; i >= 0; i--) { var apple = apples[i]; // Track last intersecting state var currentIntersecting = apple.intersects(basket); // If apple just started intersecting with basket if (!apple.lastIntersecting && currentIntersecting) { if (apple.isGood) { score += 1; updateScoreText(); LK.getSound('catchGood').play(); // Flash basket green LK.effects.flashObject(basket, 0x00FF00, 300); } else { // Penalty increases as game progresses var minPenalty = 2; var maxPenalty = 10; var progress = Math.min(1, LK.ticks / (60 * 60)); // Progress over 60 seconds, clamp to 1 var penalty = Math.round(minPenalty + (maxPenalty - minPenalty) * progress); score -= penalty; if (score < 0) score = 0; updateScoreText(); LK.getSound('catchBad').play(); // Flash basket red LK.effects.flashObject(basket, 0xFF0000, 400); // Game over if score reaches 0 if (score === 0) { LK.showGameOver(); return; } } // Remove apple apple.destroy(); apples.splice(i, 1); continue; } // If apple falls below screen, remove it if (apple.y > GAME_HEIGHT + 100) { apple.destroy(); apples.splice(i, 1); continue; } // Update lastIntersecting apple.lastIntersecting = currentIntersecting; } // Endless game: no win condition }; // Initial score/timer display updateScoreText();
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Apple class: can be good or bad, and have different colors
var Apple = Container.expand(function () {
var self = Container.call(this);
// Randomly decide if this apple is good or bad
self.isGood = Math.random() < 0.7; // 70% good, 30% bad
// Pick a random color for good and bad apples
var goodAppleTypes = ['goodApple', 'goodAppleGreen', 'goodAppleYellow', 'goodAppleBlue', 'goodApplePurple'];
var badAppleTypes = ['badApple', 'badApplePurple', 'badAppleBlue', 'badAppleOrange', 'badApplePink'];
var assetId;
if (self.isGood) {
assetId = goodAppleTypes[Math.floor(Math.random() * goodAppleTypes.length)];
} else {
assetId = badAppleTypes[Math.floor(Math.random() * badAppleTypes.length)];
}
// Attach correct asset
var appleAsset = self.attachAsset(assetId, {
anchorX: 0.5,
anchorY: 0.5
});
// Set falling speed (randomize a bit), increase as game progresses, but more slowly
var minSpeed = 12;
var maxSpeed = 32;
// Progress increases more slowly: use sqrt for slower ramp-up
var progress = Math.min(1, Math.sqrt(LK.ticks / (60 * 60))); // Slower acceleration
var baseSpeed = minSpeed + (maxSpeed - minSpeed) * progress;
self.speed = baseSpeed + Math.random() * 6;
// Mix: 50% apples fall straight, 50% sloped and bounce
if (Math.random() < 0.5) {
// Flat: straight down
self.xSpeed = 0;
} else {
// Sloping: random left/right, random speed
self.xSpeed = (Math.random() < 0.5 ? -1 : 1) * (6 + Math.random() * 8);
}
// For edge bounce, need to know lastX
self.lastX = self.x;
// For intersection tracking
self.lastIntersecting = false;
// Update method called every tick
self.update = function () {
// Save lastX for edge bounce detection
self.lastX = self.x;
self.y += self.speed;
self.x += self.xSpeed;
// Bounce off left edge
if (self.lastX >= 60 && self.x < 60) {
self.x = 60;
self.xSpeed *= -1;
}
// Bounce off right edge
if (self.lastX <= GAME_WIDTH - 60 && self.x > GAME_WIDTH - 60) {
self.x = GAME_WIDTH - 60;
self.xSpeed *= -1;
}
};
return self;
});
// Basket class
var Basket = Container.expand(function () {
var self = Container.call(this);
var basketAsset = self.attachAsset('basket', {
anchorX: 0.5,
anchorY: 0.5
});
// Optionally, add a little "lip" to the basket for visual
// (not required for MVP)
// No update needed for basket
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x87ceeb // Sky blue
});
/****
* Game Code
****/
// Music (optional, not used in MVP)
// Sound for miss (optional, not used in MVP)
// Sound for catching bad apple
// Sound for catching good apple
// Basket
// Apple (rotten)
// Apple (good)
// Game constants
var GAME_WIDTH = 2048;
var GAME_HEIGHT = 2732;
var BASKET_Y = GAME_HEIGHT - 200; // Basket near bottom
var APPLE_SPAWN_INTERVAL = 45; // Ticks between apples (~0.75s)
var TARGET_SCORE = 20; // Win condition
// Game state
var apples = [];
var basket = null;
var score = 1;
var scoreTxt = null;
var dragNode = null;
var lastMoveX = null;
// Add background image (centered, covers full game area)
var background = LK.getAsset('background', {
anchorX: 0.5,
anchorY: 0.5,
x: GAME_WIDTH / 2,
y: GAME_HEIGHT / 2,
width: GAME_WIDTH,
height: GAME_HEIGHT
});
game.addChild(background);
// Create and position basket
basket = new Basket();
basket.x = GAME_WIDTH / 2;
basket.y = BASKET_Y;
game.addChild(basket);
// Score text
scoreTxt = new Text2('0', {
size: 120,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Helper: update score display
function updateScoreText() {
scoreTxt.setText(score);
}
// Move handler for dragging basket
function handleMove(x, y, obj) {
if (dragNode) {
// Clamp basket within screen
var halfWidth = dragNode.width / 2;
var newX = x;
if (newX < halfWidth) newX = halfWidth;
if (newX > GAME_WIDTH - halfWidth) newX = GAME_WIDTH - halfWidth;
dragNode.x = newX;
}
}
// Touch/mouse down: start dragging basket if touch is on basket or anywhere
game.down = function (x, y, obj) {
// Only allow drag if not in top left 100x100 (reserved for menu)
if (x > 100 || y > 100) {
dragNode = basket;
handleMove(x, y, obj);
}
};
// Touch/mouse up: stop dragging
game.up = function (x, y, obj) {
dragNode = null;
};
// Touch/mouse move: move basket if dragging
game.move = function (x, y, obj) {
handleMove(x, y, obj);
};
// Main game update loop
game.update = function () {
// Dynamically decrease spawn interval as time passes (min 12 ticks, max 45)
var minInterval = 12;
var maxInterval = 45;
var progress = Math.min(1, LK.ticks / (60 * 60)); // Progress over 60 seconds, clamp to 1
var dynamicInterval = Math.round(maxInterval - (maxInterval - minInterval) * progress);
if (LK.ticks % dynamicInterval === 0) {
var apple = new Apple();
// Random x, but keep fully on screen
var minX = 80;
var maxX = GAME_WIDTH - 80;
apple.x = minX + Math.random() * (maxX - minX);
apple.y = -80; // Start above screen
apples.push(apple);
game.addChild(apple);
}
// Update apples
for (var i = apples.length - 1; i >= 0; i--) {
var apple = apples[i];
// Track last intersecting state
var currentIntersecting = apple.intersects(basket);
// If apple just started intersecting with basket
if (!apple.lastIntersecting && currentIntersecting) {
if (apple.isGood) {
score += 1;
updateScoreText();
LK.getSound('catchGood').play();
// Flash basket green
LK.effects.flashObject(basket, 0x00FF00, 300);
} else {
// Penalty increases as game progresses
var minPenalty = 2;
var maxPenalty = 10;
var progress = Math.min(1, LK.ticks / (60 * 60)); // Progress over 60 seconds, clamp to 1
var penalty = Math.round(minPenalty + (maxPenalty - minPenalty) * progress);
score -= penalty;
if (score < 0) score = 0;
updateScoreText();
LK.getSound('catchBad').play();
// Flash basket red
LK.effects.flashObject(basket, 0xFF0000, 400);
// Game over if score reaches 0
if (score === 0) {
LK.showGameOver();
return;
}
}
// Remove apple
apple.destroy();
apples.splice(i, 1);
continue;
}
// If apple falls below screen, remove it
if (apple.y > GAME_HEIGHT + 100) {
apple.destroy();
apples.splice(i, 1);
continue;
}
// Update lastIntersecting
apple.lastIntersecting = currentIntersecting;
}
// Endless game: no win condition
};
// Initial score/timer display
updateScoreText();
banana. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
rotten banana. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
apple. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
rotten apple. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
pear. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
rotten pear. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
kiwi . No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
rotten kiwi. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
strawberry. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
rotten strawberry. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
hollow fruit basket. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat