/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ var Bomb = Container.expand(function () { var self = Container.call(this); self.isSliced = false; var bombGraphics = self.attachAsset('bomb', { anchorX: 0.5, anchorY: 0.5 }); // Random movement self.velocityX = (Math.random() - 0.5) * 8; self.velocityY = Math.random() * -20 - 10; self.gravity = 0.5; self.update = function () { if (self.isSliced) { return; } self.x += self.velocityX; self.y += self.velocityY; self.velocityY += self.gravity; // Limit maximum height - if bomb goes too high, start falling faster if (self.y < 500) { self.velocityY = Math.max(self.velocityY, 2); } // Add invisible barriers on the sides if (self.x < 100) { self.x = 100; self.velocityX = Math.abs(self.velocityX); // Bounce back } if (self.x > 1948) { self.x = 1948; self.velocityX = -Math.abs(self.velocityX); // Bounce back } }; self.slice = function () { if (self.isSliced) { return; } self.isSliced = true; LK.getSound('bomb').play(); // Explosion effect with tween tween(bombGraphics, { scaleX: 3, scaleY: 3, alpha: 0, tint: 0xff0000 }, { duration: 500, easing: tween.easeOut, onFinish: function onFinish() { // Take all 3 lives when bomb is sliced missedFruits = maxMisses; // Hide all hearts for (var h = 0; h < hearts.length; h++) { hearts[h].alpha = 0.3; } // Flash screen red and end game after explosion LK.effects.flashScreen(0xff0000, 1000); LK.showGameOver(); } }); }; return self; }); var Fruit = Container.expand(function (type) { var self = Container.call(this); self.fruitType = type; self.isSliced = false; self.points = 10; // Different point values for different fruits if (type === 'apple') { self.points = 10; } else if (type === 'orange') { self.points = 15; } else if (type === 'banana') { self.points = 20; } else if (type === 'watermelon') { self.points = 30; } else if (type === 'strawberry') { self.points = 25; } var fruitGraphics = self.attachAsset(type, { anchorX: 0.5, anchorY: 0.5 }); // Random rotation and movement self.rotationSpeed = (Math.random() - 0.5) * 0.2; self.velocityX = (Math.random() - 0.5) * 8; self.velocityY = Math.random() * -20 - 10; self.gravity = 0.3; self.update = function () { if (self.isSliced) { return; } self.x += self.velocityX; self.y += self.velocityY; self.velocityY += self.gravity; self.rotation += self.rotationSpeed; // Limit maximum height - if fruit goes too high, start falling faster if (self.y < 500) { self.velocityY = Math.max(self.velocityY, 2); } // Add invisible barriers on the sides if (self.x < 100) { self.x = 100; self.velocityX = Math.abs(self.velocityX); // Bounce back } if (self.x > 1948) { self.x = 1948; self.velocityX = -Math.abs(self.velocityX); // Bounce back } }; self.slice = function () { if (self.isSliced) { return; } self.isSliced = true; LK.getSound('slice').play(); // Add points and update combo var pointsEarned = self.points * comboMultiplier; LK.setScore(LK.getScore() + pointsEarned); comboCount++; comboMultiplier = Math.min(5, Math.floor(comboCount / 3) + 1); // Visual effect tween(fruitGraphics, { alpha: 0, scaleX: 1.5, scaleY: 1.5 }, { duration: 300 }); // Update score display scoreTxt.setText(LK.getScore()); comboTxt.setText('x' + comboMultiplier); }; return self; }); var SliceTrail = Container.expand(function () { var self = Container.call(this); var sliceGraphics = self.attachAsset('slicetrail', { anchorX: 0.5, anchorY: 0.5 }); sliceGraphics.alpha = 0.8; // Fade out over time tween(sliceGraphics, { alpha: 0 }, { duration: 200, onFinish: function onFinish() { self.destroy(); } }); return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x87CEEB }); /**** * Game Code ****/ // Add background var background = game.attachAsset('background', { anchorX: 0, anchorY: 0, x: 0, y: 0 }); var fruits = []; var bombs = []; var sliceTrails = []; var missedFruits = 0; var maxMisses = 3; var comboCount = 0; var comboMultiplier = 1; var gameSpeed = 1; var lastSliceX = 0; var lastSliceY = 0; var isSlicing = false; // UI Elements var scoreTxt = new Text2('0', { size: 80, fill: 0xFFFFFF }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); var comboTxt = new Text2('x1', { size: 60, fill: 0xFFFF00 }); comboTxt.anchor.set(1, 0); LK.gui.topRight.addChild(comboTxt); // Heart lives display var hearts = []; for (var h = 0; h < 3; h++) { var heart = LK.getAsset('heart', { anchorX: 0.5, anchorY: 0.5 }); heart.x = 150 + h * 80; // Avoid platform menu, space hearts out heart.y = 80; hearts.push(heart); LK.gui.addChild(heart); } // Spawn timer var spawnTimer = 0; var spawnRate = 45; // frames between spawns (increased rate) // Game input handling game.down = function (x, y, obj) { isSlicing = true; lastSliceX = x; lastSliceY = y; }; game.move = function (x, y, obj) { if (!isSlicing) { return; } // Create slice trail var trail = new SliceTrail(); trail.x = x; trail.y = y; // Calculate angle between last position and current var dx = x - lastSliceX; var dy = y - lastSliceY; var angle = Math.atan2(dy, dx); trail.rotation = angle; game.addChild(trail); sliceTrails.push(trail); // Check for fruit/bomb slicing checkSlicing(lastSliceX, lastSliceY, x, y); lastSliceX = x; lastSliceY = y; }; game.up = function (x, y, obj) { isSlicing = false; // Reset combo if no slicing for a while LK.setTimeout(function () { if (!isSlicing) { comboCount = 0; comboMultiplier = 1; comboTxt.setText('x1'); } }, 1000); }; function checkSlicing(x1, y1, x2, y2) { // Check fruits for (var i = 0; i < fruits.length; i++) { var fruit = fruits[i]; if (fruit.isSliced) { continue; } // Simple line-circle intersection with bigger collision radius var fruitRadius = 100; // Bigger collision for larger fruits if (fruit.fruitType === 'watermelon') { fruitRadius = 200; // Extra large for watermelon } if (lineIntersectsCircle(x1, y1, x2, y2, fruit.x, fruit.y, fruitRadius)) { fruit.slice(); } } // Check bombs for (var i = 0; i < bombs.length; i++) { var bomb = bombs[i]; if (bomb.isSliced) { continue; } if (lineIntersectsCircle(x1, y1, x2, y2, bomb.x, bomb.y, 90)) { bomb.slice(); } } } function lineIntersectsCircle(x1, y1, x2, y2, cx, cy, radius) { var dx = x2 - x1; var dy = y2 - y1; var fx = x1 - cx; var fy = y1 - cy; var a = dx * dx + dy * dy; var b = 2 * (fx * dx + fy * dy); var c = fx * fx + fy * fy - radius * radius; var discriminant = b * b - 4 * a * c; if (discriminant < 0) { return false; } var sqrt = Math.sqrt(discriminant); var t1 = (-b - sqrt) / (2 * a); var t2 = (-b + sqrt) / (2 * a); return t1 >= 0 && t1 <= 1 || t2 >= 0 && t2 <= 1; } function spawnFruit() { var fruitTypes = ['apple', 'orange', 'banana', 'watermelon', 'strawberry']; var randomType = fruitTypes[Math.floor(Math.random() * fruitTypes.length)]; var fruit = new Fruit(randomType); // Spawn from bottom of screen fruit.x = Math.random() * 2048; fruit.y = 2832; // Upward velocity to make fruits fly up much higher fruit.velocityY = Math.random() * -60 - 30; // Initialize lastY for tracking fruit.lastY = fruit.y; fruits.push(fruit); game.addChild(fruit); } function spawnBomb() { var bomb = new Bomb(); // Spawn from bottom of screen bomb.x = Math.random() * 2048; bomb.y = 2832; // Upward velocity to make bombs fly up much higher bomb.velocityY = Math.random() * -50 - 25; bombs.push(bomb); game.addChild(bomb); } game.update = function () { spawnTimer++; // Adjust spawn rate based on score gameSpeed = 1 + LK.getScore() / 1000; var adjustedSpawnRate = Math.max(30, spawnRate / gameSpeed); // Spawn fruits if (spawnTimer >= adjustedSpawnRate) { spawnTimer = 0; if (Math.random() < 0.1) { // 10% chance for bomb spawnBomb(); } else { spawnFruit(); } } // Update and clean up fruits for (var i = fruits.length - 1; i >= 0; i--) { var fruit = fruits[i]; // Check if fruit just hit the ground (transition from above to below ground level) if (fruit.lastY <= 2832 && fruit.y > 2832 && !fruit.isSliced) { missedFruits++; // Hide a heart when life is lost if (missedFruits <= hearts.length) { hearts[hearts.length - missedFruits].alpha = 0.3; // Dim the heart } if (missedFruits >= maxMisses) { LK.effects.flashScreen(0xff0000, 1000); LK.showGameOver(); } } // Update lastY for next frame fruit.lastY = fruit.y; // Remove off-screen or sliced fruits if (fruit.y > 2900 || fruit.isSliced && fruit.alpha <= 0) { fruit.destroy(); fruits.splice(i, 1); } } // Update and clean up bombs for (var i = bombs.length - 1; i >= 0; i--) { var bomb = bombs[i]; // Remove off-screen bombs if (bomb.y > 2900) { bomb.destroy(); bombs.splice(i, 1); } } // Clean up slice trails for (var i = sliceTrails.length - 1; i >= 0; i--) { var trail = sliceTrails[i]; if (trail.alpha <= 0) { sliceTrails.splice(i, 1); } } };
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var Bomb = Container.expand(function () {
var self = Container.call(this);
self.isSliced = false;
var bombGraphics = self.attachAsset('bomb', {
anchorX: 0.5,
anchorY: 0.5
});
// Random movement
self.velocityX = (Math.random() - 0.5) * 8;
self.velocityY = Math.random() * -20 - 10;
self.gravity = 0.5;
self.update = function () {
if (self.isSliced) {
return;
}
self.x += self.velocityX;
self.y += self.velocityY;
self.velocityY += self.gravity;
// Limit maximum height - if bomb goes too high, start falling faster
if (self.y < 500) {
self.velocityY = Math.max(self.velocityY, 2);
}
// Add invisible barriers on the sides
if (self.x < 100) {
self.x = 100;
self.velocityX = Math.abs(self.velocityX); // Bounce back
}
if (self.x > 1948) {
self.x = 1948;
self.velocityX = -Math.abs(self.velocityX); // Bounce back
}
};
self.slice = function () {
if (self.isSliced) {
return;
}
self.isSliced = true;
LK.getSound('bomb').play();
// Explosion effect with tween
tween(bombGraphics, {
scaleX: 3,
scaleY: 3,
alpha: 0,
tint: 0xff0000
}, {
duration: 500,
easing: tween.easeOut,
onFinish: function onFinish() {
// Take all 3 lives when bomb is sliced
missedFruits = maxMisses;
// Hide all hearts
for (var h = 0; h < hearts.length; h++) {
hearts[h].alpha = 0.3;
}
// Flash screen red and end game after explosion
LK.effects.flashScreen(0xff0000, 1000);
LK.showGameOver();
}
});
};
return self;
});
var Fruit = Container.expand(function (type) {
var self = Container.call(this);
self.fruitType = type;
self.isSliced = false;
self.points = 10;
// Different point values for different fruits
if (type === 'apple') {
self.points = 10;
} else if (type === 'orange') {
self.points = 15;
} else if (type === 'banana') {
self.points = 20;
} else if (type === 'watermelon') {
self.points = 30;
} else if (type === 'strawberry') {
self.points = 25;
}
var fruitGraphics = self.attachAsset(type, {
anchorX: 0.5,
anchorY: 0.5
});
// Random rotation and movement
self.rotationSpeed = (Math.random() - 0.5) * 0.2;
self.velocityX = (Math.random() - 0.5) * 8;
self.velocityY = Math.random() * -20 - 10;
self.gravity = 0.3;
self.update = function () {
if (self.isSliced) {
return;
}
self.x += self.velocityX;
self.y += self.velocityY;
self.velocityY += self.gravity;
self.rotation += self.rotationSpeed;
// Limit maximum height - if fruit goes too high, start falling faster
if (self.y < 500) {
self.velocityY = Math.max(self.velocityY, 2);
}
// Add invisible barriers on the sides
if (self.x < 100) {
self.x = 100;
self.velocityX = Math.abs(self.velocityX); // Bounce back
}
if (self.x > 1948) {
self.x = 1948;
self.velocityX = -Math.abs(self.velocityX); // Bounce back
}
};
self.slice = function () {
if (self.isSliced) {
return;
}
self.isSliced = true;
LK.getSound('slice').play();
// Add points and update combo
var pointsEarned = self.points * comboMultiplier;
LK.setScore(LK.getScore() + pointsEarned);
comboCount++;
comboMultiplier = Math.min(5, Math.floor(comboCount / 3) + 1);
// Visual effect
tween(fruitGraphics, {
alpha: 0,
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 300
});
// Update score display
scoreTxt.setText(LK.getScore());
comboTxt.setText('x' + comboMultiplier);
};
return self;
});
var SliceTrail = Container.expand(function () {
var self = Container.call(this);
var sliceGraphics = self.attachAsset('slicetrail', {
anchorX: 0.5,
anchorY: 0.5
});
sliceGraphics.alpha = 0.8;
// Fade out over time
tween(sliceGraphics, {
alpha: 0
}, {
duration: 200,
onFinish: function onFinish() {
self.destroy();
}
});
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x87CEEB
});
/****
* Game Code
****/
// Add background
var background = game.attachAsset('background', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0
});
var fruits = [];
var bombs = [];
var sliceTrails = [];
var missedFruits = 0;
var maxMisses = 3;
var comboCount = 0;
var comboMultiplier = 1;
var gameSpeed = 1;
var lastSliceX = 0;
var lastSliceY = 0;
var isSlicing = false;
// UI Elements
var scoreTxt = new Text2('0', {
size: 80,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
var comboTxt = new Text2('x1', {
size: 60,
fill: 0xFFFF00
});
comboTxt.anchor.set(1, 0);
LK.gui.topRight.addChild(comboTxt);
// Heart lives display
var hearts = [];
for (var h = 0; h < 3; h++) {
var heart = LK.getAsset('heart', {
anchorX: 0.5,
anchorY: 0.5
});
heart.x = 150 + h * 80; // Avoid platform menu, space hearts out
heart.y = 80;
hearts.push(heart);
LK.gui.addChild(heart);
}
// Spawn timer
var spawnTimer = 0;
var spawnRate = 45; // frames between spawns (increased rate)
// Game input handling
game.down = function (x, y, obj) {
isSlicing = true;
lastSliceX = x;
lastSliceY = y;
};
game.move = function (x, y, obj) {
if (!isSlicing) {
return;
}
// Create slice trail
var trail = new SliceTrail();
trail.x = x;
trail.y = y;
// Calculate angle between last position and current
var dx = x - lastSliceX;
var dy = y - lastSliceY;
var angle = Math.atan2(dy, dx);
trail.rotation = angle;
game.addChild(trail);
sliceTrails.push(trail);
// Check for fruit/bomb slicing
checkSlicing(lastSliceX, lastSliceY, x, y);
lastSliceX = x;
lastSliceY = y;
};
game.up = function (x, y, obj) {
isSlicing = false;
// Reset combo if no slicing for a while
LK.setTimeout(function () {
if (!isSlicing) {
comboCount = 0;
comboMultiplier = 1;
comboTxt.setText('x1');
}
}, 1000);
};
function checkSlicing(x1, y1, x2, y2) {
// Check fruits
for (var i = 0; i < fruits.length; i++) {
var fruit = fruits[i];
if (fruit.isSliced) {
continue;
}
// Simple line-circle intersection with bigger collision radius
var fruitRadius = 100; // Bigger collision for larger fruits
if (fruit.fruitType === 'watermelon') {
fruitRadius = 200; // Extra large for watermelon
}
if (lineIntersectsCircle(x1, y1, x2, y2, fruit.x, fruit.y, fruitRadius)) {
fruit.slice();
}
}
// Check bombs
for (var i = 0; i < bombs.length; i++) {
var bomb = bombs[i];
if (bomb.isSliced) {
continue;
}
if (lineIntersectsCircle(x1, y1, x2, y2, bomb.x, bomb.y, 90)) {
bomb.slice();
}
}
}
function lineIntersectsCircle(x1, y1, x2, y2, cx, cy, radius) {
var dx = x2 - x1;
var dy = y2 - y1;
var fx = x1 - cx;
var fy = y1 - cy;
var a = dx * dx + dy * dy;
var b = 2 * (fx * dx + fy * dy);
var c = fx * fx + fy * fy - radius * radius;
var discriminant = b * b - 4 * a * c;
if (discriminant < 0) {
return false;
}
var sqrt = Math.sqrt(discriminant);
var t1 = (-b - sqrt) / (2 * a);
var t2 = (-b + sqrt) / (2 * a);
return t1 >= 0 && t1 <= 1 || t2 >= 0 && t2 <= 1;
}
function spawnFruit() {
var fruitTypes = ['apple', 'orange', 'banana', 'watermelon', 'strawberry'];
var randomType = fruitTypes[Math.floor(Math.random() * fruitTypes.length)];
var fruit = new Fruit(randomType);
// Spawn from bottom of screen
fruit.x = Math.random() * 2048;
fruit.y = 2832;
// Upward velocity to make fruits fly up much higher
fruit.velocityY = Math.random() * -60 - 30;
// Initialize lastY for tracking
fruit.lastY = fruit.y;
fruits.push(fruit);
game.addChild(fruit);
}
function spawnBomb() {
var bomb = new Bomb();
// Spawn from bottom of screen
bomb.x = Math.random() * 2048;
bomb.y = 2832;
// Upward velocity to make bombs fly up much higher
bomb.velocityY = Math.random() * -50 - 25;
bombs.push(bomb);
game.addChild(bomb);
}
game.update = function () {
spawnTimer++;
// Adjust spawn rate based on score
gameSpeed = 1 + LK.getScore() / 1000;
var adjustedSpawnRate = Math.max(30, spawnRate / gameSpeed);
// Spawn fruits
if (spawnTimer >= adjustedSpawnRate) {
spawnTimer = 0;
if (Math.random() < 0.1) {
// 10% chance for bomb
spawnBomb();
} else {
spawnFruit();
}
}
// Update and clean up fruits
for (var i = fruits.length - 1; i >= 0; i--) {
var fruit = fruits[i];
// Check if fruit just hit the ground (transition from above to below ground level)
if (fruit.lastY <= 2832 && fruit.y > 2832 && !fruit.isSliced) {
missedFruits++;
// Hide a heart when life is lost
if (missedFruits <= hearts.length) {
hearts[hearts.length - missedFruits].alpha = 0.3; // Dim the heart
}
if (missedFruits >= maxMisses) {
LK.effects.flashScreen(0xff0000, 1000);
LK.showGameOver();
}
}
// Update lastY for next frame
fruit.lastY = fruit.y;
// Remove off-screen or sliced fruits
if (fruit.y > 2900 || fruit.isSliced && fruit.alpha <= 0) {
fruit.destroy();
fruits.splice(i, 1);
}
}
// Update and clean up bombs
for (var i = bombs.length - 1; i >= 0; i--) {
var bomb = bombs[i];
// Remove off-screen bombs
if (bomb.y > 2900) {
bomb.destroy();
bombs.splice(i, 1);
}
}
// Clean up slice trails
for (var i = sliceTrails.length - 1; i >= 0; i--) {
var trail = sliceTrails[i];
if (trail.alpha <= 0) {
sliceTrails.splice(i, 1);
}
}
};