/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Bomb class
var Bomb = Container.expand(function () {
var self = Container.call(this);
self.asset = self.attachAsset('bomb', {
anchorX: 0.5,
anchorY: 0.5
});
// Physics
self.vx = 0;
self.vy = 0;
self.gravity = 0.18 + Math.random() * 0.05;
self.sliced = false;
self.update = function () {
if (self.sliced) return;
self.x += self.vx;
self.y += self.vy;
self.vy += self.gravity;
// Off screen: bottom
if (self.y > 2732 + 100) {
self.destroy();
for (var i = bombs.length - 1; i >= 0; i--) {
if (bombs[i] === self) {
bombs.splice(i, 1);
break;
}
}
}
};
self.slice = function () {
if (self.sliced) return false;
self.sliced = true;
// Animate bomb
tween(self, {
alpha: 0,
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 250,
onFinish: function onFinish() {
self.destroy();
}
});
for (var i = bombs.length - 1; i >= 0; i--) {
if (bombs[i] === self) {
bombs.splice(i, 1);
break;
}
}
return true;
};
return self;
});
// Fruit class
var Fruit = Container.expand(function () {
var self = Container.call(this);
// Fruit type: {id, color, points}
self.fruitType = null;
// Attach asset
self.asset = null;
// Physics
self.vx = 0;
self.vy = 0;
self.gravity = 0.18 + Math.random() * 0.05;
// Sliced state
self.sliced = false;
// For combo detection
self.sliceId = null;
// Set fruit type and asset
self.setType = function (type) {
self.fruitType = type;
self.asset = self.attachAsset(type.id, {
anchorX: 0.5,
anchorY: 0.5
});
};
// Called every tick
self.update = function () {
if (self.sliced) return;
self.x += self.vx;
self.y += self.vy;
self.vy += self.gravity;
// Off screen: bottom
if (self.y > 2732 + 100) {
self.destroy();
// Remove from fruits array in main game
for (var i = fruits.length - 1; i >= 0; i--) {
if (fruits[i] === self) {
fruits.splice(i, 1);
break;
}
}
}
};
// Slice the fruit
self.slice = function (sliceId) {
if (self.sliced) return false;
self.sliced = true;
self.sliceId = sliceId;
// --- Blue fruit special effect: remove a bomb hit and show error ---
if (self.fruitType && self.fruitType.id === 'fruit_blue') {
if (typeof bombHits !== "undefined" && bombHits > 0) {
bombHits--;
if (typeof bombTxt !== "undefined") {
bombTxt.setText('x ' + bombHits + '/' + maxBombHits);
}
// Show error effect (flash screen blue)
if (typeof LK !== "undefined" && LK.effects && LK.effects.flashScreen) {
LK.effects.flashScreen(0x3399ff, 600);
}
// Show "-1 bomb" text at the top center
if (typeof comboTxt !== "undefined") {
comboTxt.setText('-1 bomb');
comboTxt.alpha = 1;
tween(comboTxt, {
alpha: 0
}, {
duration: 900,
onFinish: function onFinish() {
comboTxt.setText('');
}
});
}
}
}
// --- Purple fruit special effect: slow down fruits and blue edge effect ---
if (self.fruitType && self.fruitType.id === 'fruit_purple') {
// Slow down all fruits and bombs for 2.5 seconds
for (var i = 0; i < fruits.length; i++) {
if (!fruits[i].sliced) {
fruits[i].vx *= 0.4;
fruits[i].vy *= 0.4;
fruits[i].gravity *= 0.4;
}
}
for (var i = 0; i < bombs.length; i++) {
if (!bombs[i].sliced) {
bombs[i].vx *= 0.4;
bombs[i].vy *= 0.4;
bombs[i].gravity *= 0.4;
}
}
// After 2.5s, restore speed
LK.setTimeout(function () {
for (var i = 0; i < fruits.length; i++) {
if (!fruits[i].sliced) {
fruits[i].vx /= 0.4;
fruits[i].vy /= 0.4;
fruits[i].gravity /= 0.4;
}
}
for (var i = 0; i < bombs.length; i++) {
if (!bombs[i].sliced) {
bombs[i].vx /= 0.4;
bombs[i].vy /= 0.4;
bombs[i].gravity /= 0.4;
}
}
// Remove giant fruit_cyan if present
for (var i = fruits.length - 1; i >= 0; i--) {
if (fruits[i] && fruits[i].fruitType && fruits[i].fruitType.id === 'fruit_cyan') {
fruits[i].destroy();
fruits.splice(i, 1);
}
}
}, 2500);
// Blue edge effect
if (typeof game !== "undefined") {
if (!game._blueEdgeEffect) {
var edge = new Container();
edge.name = "_blueEdgeEffect";
// Four blue rectangles for each edge
var thickness = 80;
var color = 0x3399ff;
// Top
var topEdge = LK.getAsset('splat', {
anchorX: 0,
anchorY: 0,
width: 2048,
height: thickness,
x: 0,
y: 0
});
topEdge.tint = color;
topEdge.alpha = 0.7;
edge.addChild(topEdge);
// Bottom
var bottomEdge = LK.getAsset('splat', {
anchorX: 0,
anchorY: 0,
width: 2048,
height: thickness,
x: 0,
y: 2732 - thickness
});
bottomEdge.tint = color;
bottomEdge.alpha = 0.7;
edge.addChild(bottomEdge);
// Left
var leftEdge = LK.getAsset('splat', {
anchorX: 0,
anchorY: 0,
width: thickness,
height: 2732,
x: 0,
y: 0
});
leftEdge.tint = color;
leftEdge.alpha = 0.7;
edge.addChild(leftEdge);
// Right
var rightEdge = LK.getAsset('splat', {
anchorX: 0,
anchorY: 0,
width: thickness,
height: 2732,
x: 2048 - thickness,
y: 0
});
rightEdge.tint = color;
rightEdge.alpha = 0.7;
edge.addChild(rightEdge);
edge.alpha = 0;
game.addChild(edge);
game._blueEdgeEffect = edge;
}
var edge = game._blueEdgeEffect;
edge.alpha = 0.85;
// Animate in and out
tween(edge, {
alpha: 0.85
}, {
duration: 120,
onFinish: function onFinish() {
tween(edge, {
alpha: 0
}, {
duration: 600,
delay: 1800,
onFinish: function onFinish() {
if (edge && edge.parent) {
edge.parent.removeChild(edge);
}
game._blueEdgeEffect = null;
}
});
}
});
}
// --- SPECIAL: Spawn a giant fruit_cyan that covers the whole screen ---
if (typeof game !== "undefined") {
// Remove all existing fruits and bombs
for (var i = fruits.length - 1; i >= 0; i--) {
fruits[i].destroy();
}
fruits = [];
for (var i = bombs.length - 1; i >= 0; i--) {
bombs[i].destroy();
}
bombs = [];
// Create a giant cyan fruit
var giantFruit = new Fruit();
var cyanType = typeof fruitCyanType !== "undefined" ? fruitCyanType : {
id: 'fruit_cyan',
color: 0x00ffff,
points: 2
};
if (giantFruit && cyanType) {
giantFruit.setType(cyanType);
// Set size to cover the whole screen
var assetW = 2048;
var assetH = 2732;
if (giantFruit.asset) {
giantFruit.asset.width = assetW;
giantFruit.asset.height = assetH;
}
giantFruit.x = 2048 / 2;
giantFruit.y = 2732 / 2;
giantFruit.vx = 0;
giantFruit.vy = 0;
giantFruit.gravity = 0;
fruits.push(giantFruit);
game.addChild(giantFruit);
}
}
}
// Knife cut effect (white line) at fruit position
if (typeof lastSlicePoints !== "undefined" && lastSlicePoints.length >= 2) {
var p1 = lastSlicePoints[lastSlicePoints.length - 2];
var p2 = lastSlicePoints[lastSlicePoints.length - 1];
var dx = p2.x - p1.x;
var dy = p2.y - p1.y;
var angle = Math.atan2(dy, dx);
var knifeLine = new Container();
var knifeAsset = LK.getAsset('slice_trail', {
anchorX: 0.5,
anchorY: 0.5,
width: 220,
height: 22,
x: self.x,
y: self.y
});
knifeAsset.rotation = angle;
knifeAsset.alpha = 0.95;
knifeAsset.scaleX = 2.2;
knifeAsset.scaleY = 0.25;
knifeLine.addChild(knifeAsset);
game.addChild(knifeLine);
tween(knifeAsset, {
alpha: 0
}, {
duration: 220,
onFinish: function onFinish() {
knifeLine.destroy();
}
});
}
// --- Screen shake effect ---
if (typeof game !== "undefined") {
var _doShake = function doShake() {
if (shakeStep < shakeCount) {
var angle = Math.random() * Math.PI * 2;
var mag = shakeMagnitude * (1 - shakeStep / shakeCount);
game.x = originalX + Math.cos(angle) * mag;
game.y = originalY + Math.sin(angle) * mag;
LK.setTimeout(_doShake, 32);
shakeStep++;
} else {
game.x = originalX;
game.y = originalY;
}
};
var shakeMagnitude = 32;
var shakeDuration = 220;
var originalX = game.x || 0;
var originalY = game.y || 0;
var shakeCount = Math.floor(shakeDuration / 32);
var shakeStep = 0;
_doShake();
}
// --- Split fruit into two parts and animate separation ---
if (self.asset) {
// Calculate split direction
var splitAngle = 0;
if (typeof lastSlicePoints !== "undefined" && lastSlicePoints.length >= 2) {
var p1 = lastSlicePoints[lastSlicePoints.length - 2];
var p2 = lastSlicePoints[lastSlicePoints.length - 1];
splitAngle = Math.atan2(p2.y - p1.y, p2.x - p1.x);
}
// Create left and right fruit halves
var half1 = LK.getAsset(self.fruitType.id, {
anchorX: 0.5,
anchorY: 0.5,
width: self.asset.width * 0.5,
height: self.asset.height,
x: self.x,
y: self.y
});
var half2 = LK.getAsset(self.fruitType.id, {
anchorX: 0.5,
anchorY: 0.5,
width: self.asset.width * 0.5,
height: self.asset.height,
x: self.x,
y: self.y
});
// Masking simulation: offset halves
half1.x = self.x - Math.cos(splitAngle) * 16;
half1.y = self.y - Math.sin(splitAngle) * 16;
half2.x = self.x + Math.cos(splitAngle) * 16;
half2.y = self.y + Math.sin(splitAngle) * 16;
half1.rotation = splitAngle;
half2.rotation = splitAngle;
// Add to game
if (typeof game !== "undefined") {
game.addChild(half1);
game.addChild(half2);
// Animate halves moving apart and fading out
tween(half1, {
x: half1.x - Math.cos(splitAngle) * 120,
y: half1.y - Math.sin(splitAngle) * 120,
rotation: splitAngle - 0.5,
alpha: 0
}, {
duration: 420,
easing: tween.cubicOut,
onFinish: function onFinish() {
half1.destroy();
}
});
tween(half2, {
x: half2.x + Math.cos(splitAngle) * 120,
y: half2.y + Math.sin(splitAngle) * 120,
rotation: splitAngle + 0.5,
alpha: 0
}, {
duration: 420,
easing: tween.cubicOut,
onFinish: function onFinish() {
half2.destroy();
}
});
}
}
// Animate fruit disappearing
tween(self, {
alpha: 0,
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 250,
onFinish: function onFinish() {
self.destroy();
}
});
// Remove from fruits array in main game
for (var i = fruits.length - 1; i >= 0; i--) {
if (fruits[i] === self) {
fruits.splice(i, 1);
break;
}
}
return true;
};
// Slice bomb (should not be called, but for safety)
self.sliceBomb = function () {
if (self.sliced) return false;
self.sliced = true;
tween(self, {
alpha: 0,
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 250,
onFinish: function onFinish() {
self.destroy();
}
});
for (var i = fruits.length - 1; i >= 0; i--) {
if (fruits[i] === self) {
fruits.splice(i, 1);
break;
}
}
return true;
};
return self;
});
// Slice trail class
var SliceTrail = Container.expand(function () {
var self = Container.call(this);
self.asset = self.attachAsset('slice_trail', {
anchorX: 0.5,
anchorY: 0.5
});
self.asset.alpha = 0.5;
self.asset.scaleX = 1.2;
self.asset.scaleY = 0.5;
self.life = 18; // frames
self.update = function () {
self.life--;
self.asset.alpha *= 0.85;
if (self.life <= 0) {
self.destroy();
for (var i = sliceTrails.length - 1; i >= 0; i--) {
if (sliceTrails[i] === self) {
sliceTrails.splice(i, 1);
break;
}
}
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x1a1a1a
});
/****
* Game Code
****/
// Fruit types
// Fruit shapes (different colors for different fruits)
// Apple
// Lemon
// Kiwi
// Orange
// Plum
// Watermelon
// Bomb shape
// Slice trail
// Splat effect
// Sound effects
// Music
var fruitTypes = [{
id: 'fruit_red',
color: 0xff3b3b,
points: 1
}, {
id: 'fruit_yellow',
color: 0xffe14d,
points: 1
}, {
id: 'fruit_green',
color: 0x6be14d,
points: 1
}, {
id: 'fruit_orange',
color: 0xffa500,
points: 1
}, {
id: 'fruit_purple',
color: 0xb84dff,
points: 1
}, {
id: 'fruit_pink',
color: 0xff4da6,
points: 1
}, {
id: 'fruit_blue',
color: 0x4d9cff,
points: 2
}];
// Keep fruit_cyan definition for special use only (not in normal spawn)
var fruitCyanType = {
id: 'fruit_cyan',
color: 0x00ffff,
points: 2
};
// Game state
var fruits = [];
var bombs = [];
var sliceTrails = [];
var lastSlicePoints = [];
var comboCount = 0;
var comboTimer = 0;
var score = 0;
var timeLeft = 60; // seconds
var gameActive = true;
var lastSpawnTick = 0;
var spawnInterval = 60; // frames
var minSpawnInterval = 24;
var spawnAcceleration = 0.995; // gets faster
var sliceIdCounter = 1;
// Bomb hit state
var bombHits = 0;
var maxBombHits = 3;
// Score display
var scoreTxt = new Text2('0', {
size: 120,
fill: "#fff"
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Timer display
var timerTxt = new Text2('60', {
size: 90,
fill: "#fff"
});
timerTxt.anchor.set(0.5, 0);
LK.gui.topRight.addChild(timerTxt);
// Bomb hits display
var bombTxt = new Text2('💣 x 0/3', {
size: 90,
fill: "#fff"
});
bombTxt.anchor.set(0.5, 0);
LK.gui.bottom.addChild(bombTxt);
// Combo display
var comboTxt = new Text2('', {
size: 100,
fill: 0xFFE14D
});
comboTxt.anchor.set(0.5, 0.5);
LK.gui.center.addChild(comboTxt);
// Start music
LK.playMusic('bgmusic');
// Timer
var timerInterval = LK.setInterval(function () {
if (!gameActive) return;
timeLeft--;
if (timeLeft < 0) timeLeft = 0;
timerTxt.setText(timeLeft);
if (timeLeft <= 0) {
gameActive = false;
LK.showGameOver();
}
}, 1000);
// Helper: spawn a fruit at a random position with random velocity
function spawnFruit() {
var fruit = new Fruit();
var type = fruitTypes[Math.floor(Math.random() * fruitTypes.length)];
fruit.setType(type);
// Spawn from bottom, random X (avoid edges)
var margin = 180;
fruit.x = margin + Math.random() * (2048 - 2 * margin);
fruit.y = 2732 + 60;
// Arc velocity: up and sideways
var angle = -Math.PI / 2 + (Math.random() - 0.5) * Math.PI / 3; // mostly up
var speed = 22 + Math.random() * 6; // Reduced speed for slower fruits
fruit.vx = Math.cos(angle) * speed;
fruit.vy = Math.sin(angle) * speed;
fruit.scaleX = fruit.scaleY = 1 + (Math.random() - 0.5) * 0.2;
fruits.push(fruit);
game.addChild(fruit);
}
// Helper: spawn a bomb
function spawnBomb() {
var bomb = new Bomb();
var margin = 180;
bomb.x = margin + Math.random() * (2048 - 2 * margin);
bomb.y = 2732 + 60;
var angle = -Math.PI / 2 + (Math.random() - 0.5) * Math.PI / 3;
var speed = 20 + Math.random() * 5; // Reduced speed for slower bombs
bomb.vx = Math.cos(angle) * speed;
bomb.vy = Math.sin(angle) * speed;
bomb.scaleX = bomb.scaleY = 1 + (Math.random() - 0.5) * 0.2;
bombs.push(bomb);
game.addChild(bomb);
}
// Helper: spawn a wave (fruits + maybe bomb)
function spawnWave() {
var n = 2 + Math.floor(Math.random() * 3); // 2-4 fruits
for (var i = 0; i < n; i++) {
spawnFruit();
}
// 1 in 3 chance of bomb
if (Math.random() < 0.33) {
spawnBomb();
}
}
// Helper: check if a line segment (x1,y1)-(x2,y2) intersects a circle (cx,cy,r)
function lineCircleIntersect(x1, y1, x2, y2, cx, cy, r) {
// Closest point on line to circle center
var dx = x2 - x1,
dy = y2 - y1;
var fx = x1 - cx,
fy = y1 - cy;
var a = dx * dx + dy * dy;
var b = 2 * (fx * dx + fy * dy);
var c = fx * fx + fy * fy - r * r;
var discriminant = b * b - 4 * a * c;
if (discriminant < 0) return false;
discriminant = Math.sqrt(discriminant);
var t1 = (-b - discriminant) / (2 * a);
var t2 = (-b + discriminant) / (2 * a);
if (t1 >= 0 && t1 <= 1 || t2 >= 0 && t2 <= 1) return true;
return false;
}
// Helper: show combo text
function showCombo(count, x, y) {
if (count < 2) return;
comboTxt.setText('Combo x' + count + '!');
comboTxt.x = 0;
comboTxt.y = 0;
comboTxt.alpha = 1;
tween(comboTxt, {
alpha: 0
}, {
duration: 900,
onFinish: function onFinish() {
comboTxt.setText('');
}
});
LK.getSound('combo').play();
}
// Touch/mouse slice tracking
var isSlicing = false;
var slicePoints = [];
var lastSliceTime = 0;
// --- Touch hold line state ---
var holdLine = null;
var holdLineStart = {
x: 0,
y: 0
};
// Don't allow slices to start in top left 100x100 (menu area)
function isInMenuArea(x, y) {
return x < 100 && y < 100;
}
// Main move handler
function handleMove(x, y, obj) {
if (!gameActive) return;
if (!isSlicing) return;
// Clamp to game area
if (x < 0) x = 0;
if (x > 2048) x = 2048;
if (y < 0) y = 0;
if (y > 2732) y = 2732;
// --- Update hold line if present ---
if (holdLine && holdLine._lineAsset) {
var sx = holdLineStart.x;
var sy = holdLineStart.y;
var ex = x;
var ey = y;
var dx = ex - sx;
var dy = ey - sy;
var angle = Math.atan2(dy, dx);
var dist = Math.sqrt(dx * dx + dy * dy);
holdLine.x = (sx + ex) / 2;
holdLine.y = (sy + ey) / 2;
holdLine._lineAsset.rotation = angle;
holdLine._lineAsset.scaleX = dist / 220; // 220 is asset width
holdLine._lineAsset.scaleY = 0.5;
}
// Add to slice points
slicePoints.push({
x: x,
y: y,
t: LK.ticks
});
// Only keep last 8 points
if (slicePoints.length > 8) slicePoints.shift();
// Draw slice trail as a straight line between last two points
if (slicePoints.length >= 2) {
var p1 = slicePoints[slicePoints.length - 2];
var p2 = slicePoints[slicePoints.length - 1];
var dx = p2.x - p1.x;
var dy = p2.y - p1.y;
var angle = Math.atan2(dy, dx);
var dist = Math.sqrt(dx * dx + dy * dy);
var trail = new SliceTrail();
// Place at midpoint
trail.x = (p1.x + p2.x) / 2;
trail.y = (p1.y + p2.y) / 2;
// Set rotation and scale to match line
trail.asset.rotation = angle;
trail.asset.scaleX = dist / 72; // 72 is asset width
trail.asset.scaleY = 0.5;
sliceTrails.push(trail);
game.addChild(trail);
} else {
var trail = new SliceTrail();
trail.x = x;
trail.y = y;
sliceTrails.push(trail);
game.addChild(trail);
}
// Only check for slicing if moved enough
if (slicePoints.length >= 2) {
// Store last two slice points globally for knife effect in Fruit.slice
lastSlicePoints = [slicePoints[slicePoints.length - 2], slicePoints[slicePoints.length - 1]];
var p1 = slicePoints[slicePoints.length - 2];
var p2 = slicePoints[slicePoints.length - 1];
var slicedThisMove = [];
var sliceId = sliceIdCounter++;
// Check fruits
for (var i = fruits.length - 1; i >= 0; i--) {
var f = fruits[i];
if (!f || typeof f.sliced === "undefined") continue;
if (f.sliced) continue;
var r = f.asset.width * 0.5 * 0.95;
if (lineCircleIntersect(p1.x, p1.y, p2.x, p2.y, f.x, f.y, r)) {
if (f.slice(sliceId)) {
slicedThisMove.push(f);
LK.getSound('slice').play();
// Score
score += f.fruitType.points;
LK.setScore(score);
scoreTxt.setText(score);
// Notify when score ends with 0 (but not if score is 0)
if (score !== 0 && score % 10 === 0) {
// Show a quick flash or message with the number reached
comboTxt.setText('Ends with 0: ' + score);
comboTxt.alpha = 1;
tween(comboTxt, {
alpha: 0
}, {
duration: 600,
onFinish: function onFinish() {
comboTxt.setText('');
}
});
}
}
}
}
// Check bombs
for (var i = bombs.length - 1; i >= 0; i--) {
var b = bombs[i];
if (b.sliced) continue;
var r = b.asset.width * 0.5 * 0.95;
if (lineCircleIntersect(p1.x, p1.y, p2.x, p2.y, b.x, b.y, r)) {
b.slice();
LK.getSound('bomb').play();
// Flash screen
LK.effects.flashScreen(0xff0000, 900);
bombHits++;
if (bombHits > maxBombHits) bombHits = maxBombHits;
bombTxt.setText('💣 x ' + bombHits + '/' + maxBombHits);
if (bombHits >= maxBombHits) {
gameActive = false;
LK.showGameOver();
return;
}
}
}
// Combo detection
if (slicedThisMove.length > 1) {
showCombo(slicedThisMove.length, x, y);
score += (slicedThisMove.length - 1) * 2; // bonus
LK.setScore(score);
scoreTxt.setText(score);
}
}
}
// Down event: start slicing
game.down = function (x, y, obj) {
if (!gameActive) return;
if (isInMenuArea(x, y)) return;
isSlicing = true;
slicePoints = [{
x: x,
y: y,
t: LK.ticks
}];
// Draw trail
var trail = new SliceTrail();
trail.x = x;
trail.y = y;
sliceTrails.push(trail);
game.addChild(trail);
// --- Show persistent hold line ---
if (holdLine) {
holdLine.destroy();
holdLine = null;
}
holdLineStart.x = x;
holdLineStart.y = y;
holdLine = new Container();
var lineAsset = LK.getAsset('slice_trail', {
anchorX: 0.5,
anchorY: 0.5,
width: 220,
height: 22,
x: x,
y: y
});
lineAsset.alpha = 0.7;
holdLine.addChild(lineAsset);
holdLine._lineAsset = lineAsset;
holdLine._startX = x;
holdLine._startY = y;
holdLine._endX = x;
holdLine._endY = y;
game.addChild(holdLine);
};
// Up event: end slicing
game.up = function (x, y, obj) {
isSlicing = false;
slicePoints = [];
lastSlicePoints = [];
// Remove hold line
if (holdLine) {
holdLine.destroy();
holdLine = null;
}
};
// Move event
game.move = handleMove;
// Main update loop
game.update = function () {
if (!gameActive) return;
// Update all fruits, bombs, slice trails
for (var i = fruits.length - 1; i >= 0; i--) {
if (fruits[i].update) fruits[i].update();
}
for (var i = bombs.length - 1; i >= 0; i--) {
if (bombs[i].update) bombs[i].update();
}
for (var i = sliceTrails.length - 1; i >= 0; i--) {
if (sliceTrails[i].update) sliceTrails[i].update();
}
// Spawn new wave
if (LK.ticks - lastSpawnTick > spawnInterval) {
spawnWave();
lastSpawnTick = LK.ticks;
spawnInterval = Math.max(minSpawnInterval, Math.floor(spawnInterval * spawnAcceleration));
}
};
// Clean up on game over
function cleanup() {
for (var i = fruits.length - 1; i >= 0; i--) {
fruits[i].destroy();
}
fruits = [];
for (var i = bombs.length - 1; i >= 0; i--) {
bombs[i].destroy();
}
bombs = [];
for (var i = sliceTrails.length - 1; i >= 0; i--) {
sliceTrails[i].destroy();
}
sliceTrails = [];
comboTxt.setText('');
isSlicing = false;
slicePoints = [];
lastSlicePoints = [];
bombHits = 0;
bombTxt.setText('💣 x 0/3');
}
// Listen for game over to clean up
LK.on('gameover', function () {
cleanup();
LK.clearInterval(timerInterval);
LK.stopMusic();
});
// Listen for you win (if you want to add a win condition in future)
LK.on('youwin', function () {
cleanup();
LK.clearInterval(timerInterval);
LK.stopMusic();
}); /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Bomb class
var Bomb = Container.expand(function () {
var self = Container.call(this);
self.asset = self.attachAsset('bomb', {
anchorX: 0.5,
anchorY: 0.5
});
// Physics
self.vx = 0;
self.vy = 0;
self.gravity = 0.18 + Math.random() * 0.05;
self.sliced = false;
self.update = function () {
if (self.sliced) return;
self.x += self.vx;
self.y += self.vy;
self.vy += self.gravity;
// Off screen: bottom
if (self.y > 2732 + 100) {
self.destroy();
for (var i = bombs.length - 1; i >= 0; i--) {
if (bombs[i] === self) {
bombs.splice(i, 1);
break;
}
}
}
};
self.slice = function () {
if (self.sliced) return false;
self.sliced = true;
// Animate bomb
tween(self, {
alpha: 0,
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 250,
onFinish: function onFinish() {
self.destroy();
}
});
for (var i = bombs.length - 1; i >= 0; i--) {
if (bombs[i] === self) {
bombs.splice(i, 1);
break;
}
}
return true;
};
return self;
});
// Fruit class
var Fruit = Container.expand(function () {
var self = Container.call(this);
// Fruit type: {id, color, points}
self.fruitType = null;
// Attach asset
self.asset = null;
// Physics
self.vx = 0;
self.vy = 0;
self.gravity = 0.18 + Math.random() * 0.05;
// Sliced state
self.sliced = false;
// For combo detection
self.sliceId = null;
// Set fruit type and asset
self.setType = function (type) {
self.fruitType = type;
self.asset = self.attachAsset(type.id, {
anchorX: 0.5,
anchorY: 0.5
});
};
// Called every tick
self.update = function () {
if (self.sliced) return;
self.x += self.vx;
self.y += self.vy;
self.vy += self.gravity;
// Off screen: bottom
if (self.y > 2732 + 100) {
self.destroy();
// Remove from fruits array in main game
for (var i = fruits.length - 1; i >= 0; i--) {
if (fruits[i] === self) {
fruits.splice(i, 1);
break;
}
}
}
};
// Slice the fruit
self.slice = function (sliceId) {
if (self.sliced) return false;
self.sliced = true;
self.sliceId = sliceId;
// --- Blue fruit special effect: remove a bomb hit and show error ---
if (self.fruitType && self.fruitType.id === 'fruit_blue') {
if (typeof bombHits !== "undefined" && bombHits > 0) {
bombHits--;
if (typeof bombTxt !== "undefined") {
bombTxt.setText('x ' + bombHits + '/' + maxBombHits);
}
// Show error effect (flash screen blue)
if (typeof LK !== "undefined" && LK.effects && LK.effects.flashScreen) {
LK.effects.flashScreen(0x3399ff, 600);
}
// Show "-1 bomb" text at the top center
if (typeof comboTxt !== "undefined") {
comboTxt.setText('-1 bomb');
comboTxt.alpha = 1;
tween(comboTxt, {
alpha: 0
}, {
duration: 900,
onFinish: function onFinish() {
comboTxt.setText('');
}
});
}
}
}
// --- Purple fruit special effect: slow down fruits and blue edge effect ---
if (self.fruitType && self.fruitType.id === 'fruit_purple') {
// Slow down all fruits and bombs for 2.5 seconds
for (var i = 0; i < fruits.length; i++) {
if (!fruits[i].sliced) {
fruits[i].vx *= 0.4;
fruits[i].vy *= 0.4;
fruits[i].gravity *= 0.4;
}
}
for (var i = 0; i < bombs.length; i++) {
if (!bombs[i].sliced) {
bombs[i].vx *= 0.4;
bombs[i].vy *= 0.4;
bombs[i].gravity *= 0.4;
}
}
// After 2.5s, restore speed
LK.setTimeout(function () {
for (var i = 0; i < fruits.length; i++) {
if (!fruits[i].sliced) {
fruits[i].vx /= 0.4;
fruits[i].vy /= 0.4;
fruits[i].gravity /= 0.4;
}
}
for (var i = 0; i < bombs.length; i++) {
if (!bombs[i].sliced) {
bombs[i].vx /= 0.4;
bombs[i].vy /= 0.4;
bombs[i].gravity /= 0.4;
}
}
// Remove giant fruit_cyan if present
for (var i = fruits.length - 1; i >= 0; i--) {
if (fruits[i] && fruits[i].fruitType && fruits[i].fruitType.id === 'fruit_cyan') {
fruits[i].destroy();
fruits.splice(i, 1);
}
}
}, 2500);
// Blue edge effect
if (typeof game !== "undefined") {
if (!game._blueEdgeEffect) {
var edge = new Container();
edge.name = "_blueEdgeEffect";
// Four blue rectangles for each edge
var thickness = 80;
var color = 0x3399ff;
// Top
var topEdge = LK.getAsset('splat', {
anchorX: 0,
anchorY: 0,
width: 2048,
height: thickness,
x: 0,
y: 0
});
topEdge.tint = color;
topEdge.alpha = 0.7;
edge.addChild(topEdge);
// Bottom
var bottomEdge = LK.getAsset('splat', {
anchorX: 0,
anchorY: 0,
width: 2048,
height: thickness,
x: 0,
y: 2732 - thickness
});
bottomEdge.tint = color;
bottomEdge.alpha = 0.7;
edge.addChild(bottomEdge);
// Left
var leftEdge = LK.getAsset('splat', {
anchorX: 0,
anchorY: 0,
width: thickness,
height: 2732,
x: 0,
y: 0
});
leftEdge.tint = color;
leftEdge.alpha = 0.7;
edge.addChild(leftEdge);
// Right
var rightEdge = LK.getAsset('splat', {
anchorX: 0,
anchorY: 0,
width: thickness,
height: 2732,
x: 2048 - thickness,
y: 0
});
rightEdge.tint = color;
rightEdge.alpha = 0.7;
edge.addChild(rightEdge);
edge.alpha = 0;
game.addChild(edge);
game._blueEdgeEffect = edge;
}
var edge = game._blueEdgeEffect;
edge.alpha = 0.85;
// Animate in and out
tween(edge, {
alpha: 0.85
}, {
duration: 120,
onFinish: function onFinish() {
tween(edge, {
alpha: 0
}, {
duration: 600,
delay: 1800,
onFinish: function onFinish() {
if (edge && edge.parent) {
edge.parent.removeChild(edge);
}
game._blueEdgeEffect = null;
}
});
}
});
}
// --- SPECIAL: Spawn a giant fruit_cyan that covers the whole screen ---
if (typeof game !== "undefined") {
// Remove all existing fruits and bombs
for (var i = fruits.length - 1; i >= 0; i--) {
fruits[i].destroy();
}
fruits = [];
for (var i = bombs.length - 1; i >= 0; i--) {
bombs[i].destroy();
}
bombs = [];
// Create a giant cyan fruit
var giantFruit = new Fruit();
var cyanType = typeof fruitCyanType !== "undefined" ? fruitCyanType : {
id: 'fruit_cyan',
color: 0x00ffff,
points: 2
};
if (giantFruit && cyanType) {
giantFruit.setType(cyanType);
// Set size to cover the whole screen
var assetW = 2048;
var assetH = 2732;
if (giantFruit.asset) {
giantFruit.asset.width = assetW;
giantFruit.asset.height = assetH;
}
giantFruit.x = 2048 / 2;
giantFruit.y = 2732 / 2;
giantFruit.vx = 0;
giantFruit.vy = 0;
giantFruit.gravity = 0;
fruits.push(giantFruit);
game.addChild(giantFruit);
}
}
}
// Knife cut effect (white line) at fruit position
if (typeof lastSlicePoints !== "undefined" && lastSlicePoints.length >= 2) {
var p1 = lastSlicePoints[lastSlicePoints.length - 2];
var p2 = lastSlicePoints[lastSlicePoints.length - 1];
var dx = p2.x - p1.x;
var dy = p2.y - p1.y;
var angle = Math.atan2(dy, dx);
var knifeLine = new Container();
var knifeAsset = LK.getAsset('slice_trail', {
anchorX: 0.5,
anchorY: 0.5,
width: 220,
height: 22,
x: self.x,
y: self.y
});
knifeAsset.rotation = angle;
knifeAsset.alpha = 0.95;
knifeAsset.scaleX = 2.2;
knifeAsset.scaleY = 0.25;
knifeLine.addChild(knifeAsset);
game.addChild(knifeLine);
tween(knifeAsset, {
alpha: 0
}, {
duration: 220,
onFinish: function onFinish() {
knifeLine.destroy();
}
});
}
// --- Screen shake effect ---
if (typeof game !== "undefined") {
var _doShake = function doShake() {
if (shakeStep < shakeCount) {
var angle = Math.random() * Math.PI * 2;
var mag = shakeMagnitude * (1 - shakeStep / shakeCount);
game.x = originalX + Math.cos(angle) * mag;
game.y = originalY + Math.sin(angle) * mag;
LK.setTimeout(_doShake, 32);
shakeStep++;
} else {
game.x = originalX;
game.y = originalY;
}
};
var shakeMagnitude = 32;
var shakeDuration = 220;
var originalX = game.x || 0;
var originalY = game.y || 0;
var shakeCount = Math.floor(shakeDuration / 32);
var shakeStep = 0;
_doShake();
}
// --- Split fruit into two parts and animate separation ---
if (self.asset) {
// Calculate split direction
var splitAngle = 0;
if (typeof lastSlicePoints !== "undefined" && lastSlicePoints.length >= 2) {
var p1 = lastSlicePoints[lastSlicePoints.length - 2];
var p2 = lastSlicePoints[lastSlicePoints.length - 1];
splitAngle = Math.atan2(p2.y - p1.y, p2.x - p1.x);
}
// Create left and right fruit halves
var half1 = LK.getAsset(self.fruitType.id, {
anchorX: 0.5,
anchorY: 0.5,
width: self.asset.width * 0.5,
height: self.asset.height,
x: self.x,
y: self.y
});
var half2 = LK.getAsset(self.fruitType.id, {
anchorX: 0.5,
anchorY: 0.5,
width: self.asset.width * 0.5,
height: self.asset.height,
x: self.x,
y: self.y
});
// Masking simulation: offset halves
half1.x = self.x - Math.cos(splitAngle) * 16;
half1.y = self.y - Math.sin(splitAngle) * 16;
half2.x = self.x + Math.cos(splitAngle) * 16;
half2.y = self.y + Math.sin(splitAngle) * 16;
half1.rotation = splitAngle;
half2.rotation = splitAngle;
// Add to game
if (typeof game !== "undefined") {
game.addChild(half1);
game.addChild(half2);
// Animate halves moving apart and fading out
tween(half1, {
x: half1.x - Math.cos(splitAngle) * 120,
y: half1.y - Math.sin(splitAngle) * 120,
rotation: splitAngle - 0.5,
alpha: 0
}, {
duration: 420,
easing: tween.cubicOut,
onFinish: function onFinish() {
half1.destroy();
}
});
tween(half2, {
x: half2.x + Math.cos(splitAngle) * 120,
y: half2.y + Math.sin(splitAngle) * 120,
rotation: splitAngle + 0.5,
alpha: 0
}, {
duration: 420,
easing: tween.cubicOut,
onFinish: function onFinish() {
half2.destroy();
}
});
}
}
// Animate fruit disappearing
tween(self, {
alpha: 0,
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 250,
onFinish: function onFinish() {
self.destroy();
}
});
// Remove from fruits array in main game
for (var i = fruits.length - 1; i >= 0; i--) {
if (fruits[i] === self) {
fruits.splice(i, 1);
break;
}
}
return true;
};
// Slice bomb (should not be called, but for safety)
self.sliceBomb = function () {
if (self.sliced) return false;
self.sliced = true;
tween(self, {
alpha: 0,
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 250,
onFinish: function onFinish() {
self.destroy();
}
});
for (var i = fruits.length - 1; i >= 0; i--) {
if (fruits[i] === self) {
fruits.splice(i, 1);
break;
}
}
return true;
};
return self;
});
// Slice trail class
var SliceTrail = Container.expand(function () {
var self = Container.call(this);
self.asset = self.attachAsset('slice_trail', {
anchorX: 0.5,
anchorY: 0.5
});
self.asset.alpha = 0.5;
self.asset.scaleX = 1.2;
self.asset.scaleY = 0.5;
self.life = 18; // frames
self.update = function () {
self.life--;
self.asset.alpha *= 0.85;
if (self.life <= 0) {
self.destroy();
for (var i = sliceTrails.length - 1; i >= 0; i--) {
if (sliceTrails[i] === self) {
sliceTrails.splice(i, 1);
break;
}
}
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x1a1a1a
});
/****
* Game Code
****/
// Fruit types
// Fruit shapes (different colors for different fruits)
// Apple
// Lemon
// Kiwi
// Orange
// Plum
// Watermelon
// Bomb shape
// Slice trail
// Splat effect
// Sound effects
// Music
var fruitTypes = [{
id: 'fruit_red',
color: 0xff3b3b,
points: 1
}, {
id: 'fruit_yellow',
color: 0xffe14d,
points: 1
}, {
id: 'fruit_green',
color: 0x6be14d,
points: 1
}, {
id: 'fruit_orange',
color: 0xffa500,
points: 1
}, {
id: 'fruit_purple',
color: 0xb84dff,
points: 1
}, {
id: 'fruit_pink',
color: 0xff4da6,
points: 1
}, {
id: 'fruit_blue',
color: 0x4d9cff,
points: 2
}];
// Keep fruit_cyan definition for special use only (not in normal spawn)
var fruitCyanType = {
id: 'fruit_cyan',
color: 0x00ffff,
points: 2
};
// Game state
var fruits = [];
var bombs = [];
var sliceTrails = [];
var lastSlicePoints = [];
var comboCount = 0;
var comboTimer = 0;
var score = 0;
var timeLeft = 60; // seconds
var gameActive = true;
var lastSpawnTick = 0;
var spawnInterval = 60; // frames
var minSpawnInterval = 24;
var spawnAcceleration = 0.995; // gets faster
var sliceIdCounter = 1;
// Bomb hit state
var bombHits = 0;
var maxBombHits = 3;
// Score display
var scoreTxt = new Text2('0', {
size: 120,
fill: "#fff"
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Timer display
var timerTxt = new Text2('60', {
size: 90,
fill: "#fff"
});
timerTxt.anchor.set(0.5, 0);
LK.gui.topRight.addChild(timerTxt);
// Bomb hits display
var bombTxt = new Text2('💣 x 0/3', {
size: 90,
fill: "#fff"
});
bombTxt.anchor.set(0.5, 0);
LK.gui.bottom.addChild(bombTxt);
// Combo display
var comboTxt = new Text2('', {
size: 100,
fill: 0xFFE14D
});
comboTxt.anchor.set(0.5, 0.5);
LK.gui.center.addChild(comboTxt);
// Start music
LK.playMusic('bgmusic');
// Timer
var timerInterval = LK.setInterval(function () {
if (!gameActive) return;
timeLeft--;
if (timeLeft < 0) timeLeft = 0;
timerTxt.setText(timeLeft);
if (timeLeft <= 0) {
gameActive = false;
LK.showGameOver();
}
}, 1000);
// Helper: spawn a fruit at a random position with random velocity
function spawnFruit() {
var fruit = new Fruit();
var type = fruitTypes[Math.floor(Math.random() * fruitTypes.length)];
fruit.setType(type);
// Spawn from bottom, random X (avoid edges)
var margin = 180;
fruit.x = margin + Math.random() * (2048 - 2 * margin);
fruit.y = 2732 + 60;
// Arc velocity: up and sideways
var angle = -Math.PI / 2 + (Math.random() - 0.5) * Math.PI / 3; // mostly up
var speed = 22 + Math.random() * 6; // Reduced speed for slower fruits
fruit.vx = Math.cos(angle) * speed;
fruit.vy = Math.sin(angle) * speed;
fruit.scaleX = fruit.scaleY = 1 + (Math.random() - 0.5) * 0.2;
fruits.push(fruit);
game.addChild(fruit);
}
// Helper: spawn a bomb
function spawnBomb() {
var bomb = new Bomb();
var margin = 180;
bomb.x = margin + Math.random() * (2048 - 2 * margin);
bomb.y = 2732 + 60;
var angle = -Math.PI / 2 + (Math.random() - 0.5) * Math.PI / 3;
var speed = 20 + Math.random() * 5; // Reduced speed for slower bombs
bomb.vx = Math.cos(angle) * speed;
bomb.vy = Math.sin(angle) * speed;
bomb.scaleX = bomb.scaleY = 1 + (Math.random() - 0.5) * 0.2;
bombs.push(bomb);
game.addChild(bomb);
}
// Helper: spawn a wave (fruits + maybe bomb)
function spawnWave() {
var n = 2 + Math.floor(Math.random() * 3); // 2-4 fruits
for (var i = 0; i < n; i++) {
spawnFruit();
}
// 1 in 3 chance of bomb
if (Math.random() < 0.33) {
spawnBomb();
}
}
// Helper: check if a line segment (x1,y1)-(x2,y2) intersects a circle (cx,cy,r)
function lineCircleIntersect(x1, y1, x2, y2, cx, cy, r) {
// Closest point on line to circle center
var dx = x2 - x1,
dy = y2 - y1;
var fx = x1 - cx,
fy = y1 - cy;
var a = dx * dx + dy * dy;
var b = 2 * (fx * dx + fy * dy);
var c = fx * fx + fy * fy - r * r;
var discriminant = b * b - 4 * a * c;
if (discriminant < 0) return false;
discriminant = Math.sqrt(discriminant);
var t1 = (-b - discriminant) / (2 * a);
var t2 = (-b + discriminant) / (2 * a);
if (t1 >= 0 && t1 <= 1 || t2 >= 0 && t2 <= 1) return true;
return false;
}
// Helper: show combo text
function showCombo(count, x, y) {
if (count < 2) return;
comboTxt.setText('Combo x' + count + '!');
comboTxt.x = 0;
comboTxt.y = 0;
comboTxt.alpha = 1;
tween(comboTxt, {
alpha: 0
}, {
duration: 900,
onFinish: function onFinish() {
comboTxt.setText('');
}
});
LK.getSound('combo').play();
}
// Touch/mouse slice tracking
var isSlicing = false;
var slicePoints = [];
var lastSliceTime = 0;
// --- Touch hold line state ---
var holdLine = null;
var holdLineStart = {
x: 0,
y: 0
};
// Don't allow slices to start in top left 100x100 (menu area)
function isInMenuArea(x, y) {
return x < 100 && y < 100;
}
// Main move handler
function handleMove(x, y, obj) {
if (!gameActive) return;
if (!isSlicing) return;
// Clamp to game area
if (x < 0) x = 0;
if (x > 2048) x = 2048;
if (y < 0) y = 0;
if (y > 2732) y = 2732;
// --- Update hold line if present ---
if (holdLine && holdLine._lineAsset) {
var sx = holdLineStart.x;
var sy = holdLineStart.y;
var ex = x;
var ey = y;
var dx = ex - sx;
var dy = ey - sy;
var angle = Math.atan2(dy, dx);
var dist = Math.sqrt(dx * dx + dy * dy);
holdLine.x = (sx + ex) / 2;
holdLine.y = (sy + ey) / 2;
holdLine._lineAsset.rotation = angle;
holdLine._lineAsset.scaleX = dist / 220; // 220 is asset width
holdLine._lineAsset.scaleY = 0.5;
}
// Add to slice points
slicePoints.push({
x: x,
y: y,
t: LK.ticks
});
// Only keep last 8 points
if (slicePoints.length > 8) slicePoints.shift();
// Draw slice trail as a straight line between last two points
if (slicePoints.length >= 2) {
var p1 = slicePoints[slicePoints.length - 2];
var p2 = slicePoints[slicePoints.length - 1];
var dx = p2.x - p1.x;
var dy = p2.y - p1.y;
var angle = Math.atan2(dy, dx);
var dist = Math.sqrt(dx * dx + dy * dy);
var trail = new SliceTrail();
// Place at midpoint
trail.x = (p1.x + p2.x) / 2;
trail.y = (p1.y + p2.y) / 2;
// Set rotation and scale to match line
trail.asset.rotation = angle;
trail.asset.scaleX = dist / 72; // 72 is asset width
trail.asset.scaleY = 0.5;
sliceTrails.push(trail);
game.addChild(trail);
} else {
var trail = new SliceTrail();
trail.x = x;
trail.y = y;
sliceTrails.push(trail);
game.addChild(trail);
}
// Only check for slicing if moved enough
if (slicePoints.length >= 2) {
// Store last two slice points globally for knife effect in Fruit.slice
lastSlicePoints = [slicePoints[slicePoints.length - 2], slicePoints[slicePoints.length - 1]];
var p1 = slicePoints[slicePoints.length - 2];
var p2 = slicePoints[slicePoints.length - 1];
var slicedThisMove = [];
var sliceId = sliceIdCounter++;
// Check fruits
for (var i = fruits.length - 1; i >= 0; i--) {
var f = fruits[i];
if (!f || typeof f.sliced === "undefined") continue;
if (f.sliced) continue;
var r = f.asset.width * 0.5 * 0.95;
if (lineCircleIntersect(p1.x, p1.y, p2.x, p2.y, f.x, f.y, r)) {
if (f.slice(sliceId)) {
slicedThisMove.push(f);
LK.getSound('slice').play();
// Score
score += f.fruitType.points;
LK.setScore(score);
scoreTxt.setText(score);
// Notify when score ends with 0 (but not if score is 0)
if (score !== 0 && score % 10 === 0) {
// Show a quick flash or message with the number reached
comboTxt.setText('Ends with 0: ' + score);
comboTxt.alpha = 1;
tween(comboTxt, {
alpha: 0
}, {
duration: 600,
onFinish: function onFinish() {
comboTxt.setText('');
}
});
}
}
}
}
// Check bombs
for (var i = bombs.length - 1; i >= 0; i--) {
var b = bombs[i];
if (b.sliced) continue;
var r = b.asset.width * 0.5 * 0.95;
if (lineCircleIntersect(p1.x, p1.y, p2.x, p2.y, b.x, b.y, r)) {
b.slice();
LK.getSound('bomb').play();
// Flash screen
LK.effects.flashScreen(0xff0000, 900);
bombHits++;
if (bombHits > maxBombHits) bombHits = maxBombHits;
bombTxt.setText('💣 x ' + bombHits + '/' + maxBombHits);
if (bombHits >= maxBombHits) {
gameActive = false;
LK.showGameOver();
return;
}
}
}
// Combo detection
if (slicedThisMove.length > 1) {
showCombo(slicedThisMove.length, x, y);
score += (slicedThisMove.length - 1) * 2; // bonus
LK.setScore(score);
scoreTxt.setText(score);
}
}
}
// Down event: start slicing
game.down = function (x, y, obj) {
if (!gameActive) return;
if (isInMenuArea(x, y)) return;
isSlicing = true;
slicePoints = [{
x: x,
y: y,
t: LK.ticks
}];
// Draw trail
var trail = new SliceTrail();
trail.x = x;
trail.y = y;
sliceTrails.push(trail);
game.addChild(trail);
// --- Show persistent hold line ---
if (holdLine) {
holdLine.destroy();
holdLine = null;
}
holdLineStart.x = x;
holdLineStart.y = y;
holdLine = new Container();
var lineAsset = LK.getAsset('slice_trail', {
anchorX: 0.5,
anchorY: 0.5,
width: 220,
height: 22,
x: x,
y: y
});
lineAsset.alpha = 0.7;
holdLine.addChild(lineAsset);
holdLine._lineAsset = lineAsset;
holdLine._startX = x;
holdLine._startY = y;
holdLine._endX = x;
holdLine._endY = y;
game.addChild(holdLine);
};
// Up event: end slicing
game.up = function (x, y, obj) {
isSlicing = false;
slicePoints = [];
lastSlicePoints = [];
// Remove hold line
if (holdLine) {
holdLine.destroy();
holdLine = null;
}
};
// Move event
game.move = handleMove;
// Main update loop
game.update = function () {
if (!gameActive) return;
// Update all fruits, bombs, slice trails
for (var i = fruits.length - 1; i >= 0; i--) {
if (fruits[i].update) fruits[i].update();
}
for (var i = bombs.length - 1; i >= 0; i--) {
if (bombs[i].update) bombs[i].update();
}
for (var i = sliceTrails.length - 1; i >= 0; i--) {
if (sliceTrails[i].update) sliceTrails[i].update();
}
// Spawn new wave
if (LK.ticks - lastSpawnTick > spawnInterval) {
spawnWave();
lastSpawnTick = LK.ticks;
spawnInterval = Math.max(minSpawnInterval, Math.floor(spawnInterval * spawnAcceleration));
}
};
// Clean up on game over
function cleanup() {
for (var i = fruits.length - 1; i >= 0; i--) {
fruits[i].destroy();
}
fruits = [];
for (var i = bombs.length - 1; i >= 0; i--) {
bombs[i].destroy();
}
bombs = [];
for (var i = sliceTrails.length - 1; i >= 0; i--) {
sliceTrails[i].destroy();
}
sliceTrails = [];
comboTxt.setText('');
isSlicing = false;
slicePoints = [];
lastSlicePoints = [];
bombHits = 0;
bombTxt.setText('💣 x 0/3');
}
// Listen for game over to clean up
LK.on('gameover', function () {
cleanup();
LK.clearInterval(timerInterval);
LK.stopMusic();
});
// Listen for you win (if you want to add a win condition in future)
LK.on('youwin', function () {
cleanup();
LK.clearInterval(timerInterval);
LK.stopMusic();
});
red apple. In-Game asset. 2d. High contrast. No shadows
orange. In-Game asset. 2d. High contrast. No shadows
lemon. In-Game asset. 2d. High contrast. No shadows
ice . No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
straight and white slash thin at the tip and thick in the middle. In-Game asset. 2d. High contrast. No shadows
banana. In-Game asset. 2d. High contrast. No shadows
+. In-Game asset. 2d. High contrast. No shadows
kenarlardan gelen donma efekti. In-Game asset. 2d. High contrast. No shadows