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