/****
* 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