User prompt
Meyvelerin kesilme animasyonları olsun.
User prompt
Meyve kesince puan artmıyor.
User prompt
Meyveler her 2 saniyede bir çıksın
User prompt
Meyveler her 3 saniyede bir çıksın. Bombalar ise her 5 saniyede bir çıksın.
User prompt
Meyvelerin boyutları baya büyüt.
User prompt
Meyveler daha fazla fırlasın ama ekranın sağ ve solundan geçemesinler
User prompt
Meyveler geri düşünce x artmasın sadece bombaya dokunduğumuzda x artsın.
User prompt
Meyve çıkma olasılığını düşür.
User prompt
Meyveler çok azıcık daha fırlasın
User prompt
Meyveler daha az fırlasın
User prompt
Meyvelerin çıkma süresini azalat. Meyveler teker teker çıksın.
User prompt
Hepsinin asetini assets kısmına ekls
User prompt
Meyveleri baya fırlat
User prompt
Meyveler daha yükseğe çıksın ama daha yavaş olsunlar.
User prompt
Meyveler daha yavaş çıksın
User prompt
Meyveler ekranın sağ ve solu çok hızlı bir şekilde gidiyor bunu düzelt.
Code edit (1 edits merged)
Please save this source code
User prompt
Fruit Ninja Slice
Initial prompt
Fruit Ninja
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1"); /**** * Classes ****/ // Bomb class var Bomb = Container.expand(function () { var self = Container.call(this); // Bomb asset: black circle with red border var assetId = 'bomb'; var bombGfx = self.attachAsset(assetId, { anchorX: 0.5, anchorY: 0.5 }); // Add a red border by overlaying a slightly larger ellipse var borderId = 'bomb_border'; var borderGfx = self.attachAsset(borderId, { anchorX: 0.5, anchorY: 0.5, alpha: 0.3 }); // Physics self.vx = 0; self.vy = 0; self.gravity = 0.35 + Math.random() * 0.1; self.sliced = false; self.slice = function () { if (self.sliced) return; self.sliced = true; // Animate: flash and fade out tween(self, { scaleX: 1.5, scaleY: 1.5, alpha: 0 }, { duration: 250, easing: tween.easeOut, onFinish: function onFinish() { self.destroy(); } }); }; self.update = function () { if (self.sliced) return; self.x += self.vx; self.y += self.vy; self.vy += self.gravity; // Prevent from leaving left/right screen edges if (self.x < 0 + self.width * 0.5) { self.x = self.width * 0.5; self.vx = -self.vx * 0.55; } if (self.x > 2048 - self.width * 0.5) { self.x = 2048 - self.width * 0.5; self.vx = -self.vx * 0.55; } }; return self; }); // Fruit class var Fruit = Container.expand(function () { var self = Container.call(this); // Fruit types: apple, banana, orange, watermelon, etc. // Each type has a color and shape // We'll use 'ellipse' for round fruits, 'box' for banana // We'll randomize type on creation var types = [{ id: 'apple', color: 0xff2d2d, shape: 'ellipse', width: 320, height: 320, points: 1 }, { id: 'orange', color: 0xffa500, shape: 'ellipse', width: 300, height: 300, points: 1 }, { id: 'banana', color: 0xfff700, shape: 'box', width: 420, height: 160, points: 2 }, { id: 'watermelon', color: 0x2ecc40, shape: 'ellipse', width: 420, height: 420, points: 3 }, { id: 'kiwi', color: 0x8ee53f, shape: 'ellipse', width: 270, height: 270, points: 2 }]; var type = types[Math.floor(Math.random() * types.length)]; // Asset id is unique per type var assetId = 'fruit_' + type.id; // Initialize asset if not already var fruitGfx = self.attachAsset(assetId, { anchorX: 0.5, anchorY: 0.5 }); // Physics self.vx = 0; self.vy = 0; self.gravity = 0.35 + Math.random() * 0.1; self.sliced = false; self.points = type.points; // For arc motion, we set vx, vy on spawn // Slicing animation self.slice = function () { if (self.sliced) return; self.sliced = true; // Play slicing sound LK.getSound('Ninjasword').play(); // --- Split fruit into two visually distinct halves animation --- // Top half (slightly lighter, with a white highlight) var halfTop = LK.getAsset(assetId, { anchorX: 0.5, anchorY: 1.0, x: self.x, y: self.y, rotation: self.rotation, scaleY: 0.52, tint: 0xffffff // white tint for highlight }); var halfBottom = LK.getAsset(assetId, { anchorX: 0.5, anchorY: 0.0, x: self.x, y: self.y, rotation: self.rotation, scaleY: 0.52, tint: type.color // original fruit color for bottom }); game.addChild(halfTop); game.addChild(halfBottom); // Animate halves flying apart, rotating and fading out tween(halfTop, { x: self.x - 60, y: self.y - 80, rotation: self.rotation - 0.32, alpha: 0 }, { duration: 420, easing: tween.easeOut, onFinish: function onFinish() { halfTop.destroy(); } }); tween(halfBottom, { x: self.x + 60, y: self.y + 80, rotation: self.rotation + 0.32, alpha: 0 }, { duration: 420, easing: tween.easeOut, onFinish: function onFinish() { halfBottom.destroy(); } }); // Juice splash effect (ellipse, color based on fruit) // More vivid color for splash (slightly saturated) var vividJuiceColor = function (c) { // Slightly brighten and saturate the color var r = Math.min(255, (c >> 16 & 0xff) * 1.15 + 32) | 0; var g = Math.min(255, (c >> 8 & 0xff) * 1.15 + 32) | 0; var b = Math.min(255, (c & 0xff) * 1.15 + 32) | 0; return r << 16 | g << 8 | b; }(type.color || 0xff2d2d); // Create multiple juice particles for a juicier effect var juiceParticles = []; var juiceParticleCount = 12 + Math.floor(Math.random() * 7); // 12-18 particles for (var jp = 0; jp < juiceParticleCount; jp++) { var angle = Math.random() * Math.PI * 2; var dist = 40 + Math.random() * 60; var scale = 0.22 + Math.random() * 0.22; var alpha = 0.55 + Math.random() * 0.25; var splash = LK.getAsset('juice_splash_' + type.id, { anchorX: 0.5, anchorY: 0.5, x: self.x, y: self.y, scaleX: scale * (0.8 + Math.random() * 0.7), scaleY: scale * (0.5 + Math.random() * 1.1), alpha: alpha, tint: vividJuiceColor }); game.addChild(splash); juiceParticles.push(splash); // Animate each particle outward and fade tween(splash, { x: self.x + Math.cos(angle) * dist, y: self.y + Math.sin(angle) * dist, scaleX: scale * (1.2 + Math.random() * 1.2), scaleY: scale * (1.1 + Math.random() * 1.2), alpha: 0 }, { duration: 320 + Math.random() * 120, easing: tween.easeOut, onFinish: function (s) { return function () { s.destroy(); }; }(splash) }); } // --- Add stain to background --- if (typeof background !== "undefined" && background && background.parent) { // Create a stain asset (ellipse, color based on fruit, random rotation/scale) var stain = LK.getAsset('juice_stain_' + type.id, { anchorX: 0.5, anchorY: 0.5, x: self.x, y: self.y, scaleX: 0.7 + Math.random() * 0.7, scaleY: 0.5 + Math.random() * 0.7, alpha: 0.32 + Math.random() * 0.18, rotation: Math.random() * Math.PI * 2, tint: vividJuiceColor }); // Insert stain just above the background (so it stays under fruit halves/splashes) var bgParent = background.parent; var bgIndex = bgParent.getChildIndex(background); bgParent.addChildAt(stain, bgIndex + 1); } // --- End stain --- // Hide original fruit immediately self.alpha = 0; tween(self, { alpha: 0 }, { duration: 1, onFinish: function onFinish() { self.destroy(); } }); }; // Update per frame self.update = function () { if (self.sliced) return; self.x += self.vx; self.y += self.vy; self.vy += self.gravity; // Prevent from leaving left/right screen edges if (self.x < 0 + self.width * 0.5) { self.x = self.width * 0.5; self.vx = -self.vx * 0.55; // bounce back with some energy loss } if (self.x > 2048 - self.width * 0.5) { self.x = 2048 - self.width * 0.5; self.vx = -self.vx * 0.55; } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x1a1a1a }); /**** * Game Code ****/ // Add background image // Game variables var background = LK.getAsset('background', { anchorX: 0, anchorY: 0, x: 0, y: 0 }); game.addChild(background); var fruits = []; var bombs = []; var fruitSpawnTimer = 0; var bombSpawnTimer = 0; var fruitSpawnInterval = 120; // 2 seconds at 60fps var bombSpawnInterval = 300; // 5 seconds at 60fps var score = 0; var highScore = storage.highScore || 0; var missed = 0; var maxMissed = 3; var isGameOver = false; // Score display var scoreTxt = new Text2('0', { size: 120, fill: "#fff" }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); // Combo system var comboCount = 0; var comboTimer = 0; var comboTimeout = 36; // 0.6s at 60fps var maxCombo = 0; var comboTxt = new Text2('', { size: 90, fill: 0xFFFA00 }); comboTxt.anchor.set(0.5, 0); LK.gui.top.addChild(comboTxt); // High score display (smaller, top right) var highScoreTxt = new Text2('Best: ' + highScore, { size: 60, fill: "#fff" }); highScoreTxt.anchor.set(1, 0); LK.gui.topRight.addChild(highScoreTxt); // Missed fruits display (bottom left, as X icons) var missTxt = new Text2('', { size: 90, fill: 0xFF4444 }); missTxt.anchor.set(0, 1); LK.gui.bottomLeft.addChild(missTxt); // Helper: update score function updateScore(val) { score = val; scoreTxt.setText(score); if (score > highScore) { highScore = score; storage.highScore = highScore; highScoreTxt.setText('Best: ' + highScore); } } // Helper: update missed display function updateMissed() { var s = ''; for (var i = 0; i < missed; i++) s += '✗ '; missTxt.setText(s); } // (spawning is now handled by fruit/bomb timers in game.update) // Slicing logic var swipePath = []; var swipeMaxLength = 12; // Number of points to keep in swipe trail var swipeRadius = 90; // How close swipe must be to slice // Draw swipe trail (for visual feedback) var swipeTrail = []; function drawSwipeTrail() { // Remove old for (var i = 0; i < swipeTrail.length; i++) { swipeTrail[i].destroy(); } swipeTrail = []; // Draw new for (var i = 1; i < swipePath.length; i++) { var p1 = swipePath[i - 1]; var p2 = swipePath[i]; // Draw a short white ellipse between points var dx = p2.x - p1.x, dy = p2.y - p1.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist < 10) continue; var angle = Math.atan2(dy, dx); var id = 'swipe_' + i; var seg = LK.getAsset(id, { anchorX: 0, anchorY: 0.5, x: p1.x, y: p1.y, rotation: angle, alpha: 0.22 }); game.addChild(seg); swipeTrail.push(seg); } } // Handle swipe start game.down = function (x, y, obj) { if (isGameOver) return; swipePath = [{ x: x, y: y }]; drawSwipeTrail(); }; // Handle swipe move game.move = function (x, y, obj) { if (isGameOver) return; swipePath.push({ x: x, y: y }); if (swipePath.length > swipeMaxLength) swipePath.shift(); drawSwipeTrail(); // For each segment in swipe, check for intersection with fruits/bombs for (var i = 1; i < swipePath.length; i++) { var p1 = swipePath[i - 1], p2 = swipePath[i]; // Fruits for (var f = fruits.length - 1; f >= 0; f--) { var fruit = fruits[f]; if (fruit.sliced) continue; // If fruit already off screen, skip if (fruit.y > 2732 + 120) continue; // Check if swipe segment is close to fruit center var cx = fruit.x, cy = fruit.y; var dist = pointToSegmentDistance(cx, cy, p1.x, p1.y, p2.x, p2.y); if (dist < swipeRadius) { fruit.slice(); updateScore(score + fruit.points); // Increase score by fruit's point value fruits.splice(f, 1); // Flash effect LK.effects.flashObject(fruit, 0xffffff, 120); // --- Combo logic --- comboCount++; comboTimer = comboTimeout; if (comboCount > maxCombo) maxCombo = comboCount; if (comboCount > 1) { comboTxt.setText(comboCount + " Combo!"); comboTxt.alpha = 1; // Animate combo text alpha in case it was faded out tween(comboTxt, { alpha: 1 }, { duration: 60 }); // Bonus points for combos (e.g. +2 for 3-combo, +4 for 4-combo, etc) if (comboCount >= 3) { var bonus = (comboCount - 2) * 2; updateScore(score + bonus); // Animate combo text tween(comboTxt, { scaleX: 1.3, scaleY: 1.3 }, { duration: 120, yoyo: true, repeat: 1 }); } } } } // Bombs for (var b = bombs.length - 1; b >= 0; b--) { var bomb = bombs[b]; if (bomb.sliced) continue; if (bomb.y > 2732 + 120) continue; var cx = bomb.x, cy = bomb.y; var dist = pointToSegmentDistance(cx, cy, p1.x, p1.y, p2.x, p2.y); if (dist < swipeRadius) { bomb.slice(); bombs.splice(b, 1); // Play bomb sound LK.getSound('Bomb').play(); // Increase score by 1 when bomb is sliced updateScore(score + 1); // Flash screen red, then game over LK.effects.flashScreen(0xff2222, 800); isGameOver = true; LK.setTimeout(function () { LK.showGameOver(); }, 600); return; } } } }; // Handle swipe end game.up = function (x, y, obj) { swipePath = []; drawSwipeTrail(); }; // Helper: distance from point to segment function pointToSegmentDistance(px, py, x1, y1, x2, y2) { var dx = x2 - x1, dy = y2 - y1; if (dx === 0 && dy === 0) { dx = px - x1; dy = py - y1; return Math.sqrt(dx * dx + dy * dy); } var t = ((px - x1) * dx + (py - y1) * dy) / (dx * dx + dy * dy); t = Math.max(0, Math.min(1, t)); var lx = x1 + t * dx, ly = y1 + t * dy; dx = px - lx; dy = py - ly; return Math.sqrt(dx * dx + dy * dy); } // Main game update game.update = function () { if (isGameOver) return; // --- Dynamic difficulty: speed up game as score increases --- var minFruitInterval = 36; // minimum interval (0.6s) var minBombInterval = 120; // minimum interval (2s) var fruitIntervalStep = Math.max(0, fruitSpawnInterval - Math.floor(score / 10) * 10); fruitSpawnInterval = Math.max(minFruitInterval, 120 - Math.floor(score / 10) * 10); bombSpawnInterval = Math.max(minBombInterval, 300 - Math.floor(score / 15) * 15); // Spawn logic fruitSpawnTimer++; bombSpawnTimer++; if (fruitSpawnTimer >= fruitSpawnInterval) { // Spawn a fruit only var obj = new Fruit(); // All fruits spawn at the same Y (bottom), but random X across the screen (leaving margin for fruit width) var fruitMargin = 200; var x = fruitMargin + Math.random() * (2048 - 2 * fruitMargin); var y = 2732 + 60; // All fruits spawn just below the bottom of the screen // Launch angle is always straight up var angle = -Math.PI / 2; var speed = 17 + Math.random() * 5.5 + score * 0.055; // vx is 0 (no side velocity), vy is always up var vx = 0; var vy = -Math.abs(Math.sin(angle) * speed) * 1.32; obj.x = x; obj.y = y; obj.vx = vx; obj.vy = vy; obj.scaleX = obj.scaleY = 1; obj.alpha = 1; // Increase gravity for fruit as score increases obj.gravity += Math.min(0.18, score * 0.012); fruits.push(obj); game.addChild(obj); fruitSpawnTimer = 0; } if (bombSpawnTimer >= bombSpawnInterval) { // Spawn a bomb only var obj = new Bomb(); // All bombs spawn at the same Y (bottom), but random X across the screen (leaving margin for bomb width) var bombMargin = 200; var x = bombMargin + Math.random() * (2048 - 2 * bombMargin); var y = 2732 + 60; // All bombs spawn just below the bottom of the screen // Launch angle is always straight up var angle = -Math.PI / 2; var speed = 17 + Math.random() * 5.5 + score * 0.055; // vx is 0 (no side velocity), vy is always up var vx = 0; var vy = -Math.abs(Math.sin(angle) * speed) * 1.32; obj.x = x; obj.y = y; obj.vx = vx; obj.vy = vy; obj.scaleX = obj.scaleY = 1; obj.alpha = 1; // Increase gravity for bomb as score increases obj.gravity += Math.min(0.18, score * 0.012); bombs.push(obj); game.addChild(obj); bombSpawnTimer = 0; } // Update fruits for (var i = fruits.length - 1; i >= 0; i--) { var fruit = fruits[i]; fruit.update(); // Remove if off screen if (!fruit.sliced && fruit.y > 2732 + 120) { // Play lose sound when fruit is missed LK.getSound('Lose').play(); fruit.destroy(); fruits.splice(i, 1); missed++; updateMissed(); // Flash screen if missed LK.effects.flashScreen(0x2222ff, 200); if (missed >= maxMissed) { isGameOver = true; LK.setTimeout(function () { LK.showGameOver(); }, 500); } // Do NOT increase score here; only bomb slice increases score now } } // Update bombs for (var i = bombs.length - 1; i >= 0; i--) { var bomb = bombs[i]; bomb.update(); if (bomb.y > 2732 + 120) { bomb.destroy(); bombs.splice(i, 1); } } // Fade out swipe trail for (var i = 0; i < swipeTrail.length; i++) { swipeTrail[i].alpha *= 0.85; if (swipeTrail[i].alpha < 0.05) { swipeTrail[i].destroy(); swipeTrail.splice(i, 1); i--; } } // --- Combo timer logic --- if (comboTimer > 0) { comboTimer--; if (comboTimer === 0) { // Combo ended comboCount = 0; tween(comboTxt, { alpha: 0 }, { duration: 320 }); } } }; // Reset game state on new game game.on('reset', function () { // Remove all fruits and bombs for (var i = 0; i < fruits.length; i++) fruits[i].destroy(); for (var i = 0; i < bombs.length; i++) bombs[i].destroy(); fruits = []; bombs = []; fruitSpawnTimer = 0; bombSpawnTimer = 0; score = 0; missed = 0; isGameOver = false; updateScore(0); updateMissed(); swipePath = []; drawSwipeTrail(); comboCount = 0; comboTimer = 0; comboTxt.setText(''); comboTxt.alpha = 0; maxCombo = 0; highScore = storage.highScore || 0; highScoreTxt.setText('Best: ' + highScore); }); // Initial state updateScore(0); updateMissed(); // Fruit assets // Bomb asset: black circle // Bomb border: red, slightly larger, semi-transparent
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
// Bomb class
var Bomb = Container.expand(function () {
var self = Container.call(this);
// Bomb asset: black circle with red border
var assetId = 'bomb';
var bombGfx = self.attachAsset(assetId, {
anchorX: 0.5,
anchorY: 0.5
});
// Add a red border by overlaying a slightly larger ellipse
var borderId = 'bomb_border';
var borderGfx = self.attachAsset(borderId, {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.3
});
// Physics
self.vx = 0;
self.vy = 0;
self.gravity = 0.35 + Math.random() * 0.1;
self.sliced = false;
self.slice = function () {
if (self.sliced) return;
self.sliced = true;
// Animate: flash and fade out
tween(self, {
scaleX: 1.5,
scaleY: 1.5,
alpha: 0
}, {
duration: 250,
easing: tween.easeOut,
onFinish: function onFinish() {
self.destroy();
}
});
};
self.update = function () {
if (self.sliced) return;
self.x += self.vx;
self.y += self.vy;
self.vy += self.gravity;
// Prevent from leaving left/right screen edges
if (self.x < 0 + self.width * 0.5) {
self.x = self.width * 0.5;
self.vx = -self.vx * 0.55;
}
if (self.x > 2048 - self.width * 0.5) {
self.x = 2048 - self.width * 0.5;
self.vx = -self.vx * 0.55;
}
};
return self;
});
// Fruit class
var Fruit = Container.expand(function () {
var self = Container.call(this);
// Fruit types: apple, banana, orange, watermelon, etc.
// Each type has a color and shape
// We'll use 'ellipse' for round fruits, 'box' for banana
// We'll randomize type on creation
var types = [{
id: 'apple',
color: 0xff2d2d,
shape: 'ellipse',
width: 320,
height: 320,
points: 1
}, {
id: 'orange',
color: 0xffa500,
shape: 'ellipse',
width: 300,
height: 300,
points: 1
}, {
id: 'banana',
color: 0xfff700,
shape: 'box',
width: 420,
height: 160,
points: 2
}, {
id: 'watermelon',
color: 0x2ecc40,
shape: 'ellipse',
width: 420,
height: 420,
points: 3
}, {
id: 'kiwi',
color: 0x8ee53f,
shape: 'ellipse',
width: 270,
height: 270,
points: 2
}];
var type = types[Math.floor(Math.random() * types.length)];
// Asset id is unique per type
var assetId = 'fruit_' + type.id;
// Initialize asset if not already
var fruitGfx = self.attachAsset(assetId, {
anchorX: 0.5,
anchorY: 0.5
});
// Physics
self.vx = 0;
self.vy = 0;
self.gravity = 0.35 + Math.random() * 0.1;
self.sliced = false;
self.points = type.points;
// For arc motion, we set vx, vy on spawn
// Slicing animation
self.slice = function () {
if (self.sliced) return;
self.sliced = true;
// Play slicing sound
LK.getSound('Ninjasword').play();
// --- Split fruit into two visually distinct halves animation ---
// Top half (slightly lighter, with a white highlight)
var halfTop = LK.getAsset(assetId, {
anchorX: 0.5,
anchorY: 1.0,
x: self.x,
y: self.y,
rotation: self.rotation,
scaleY: 0.52,
tint: 0xffffff // white tint for highlight
});
var halfBottom = LK.getAsset(assetId, {
anchorX: 0.5,
anchorY: 0.0,
x: self.x,
y: self.y,
rotation: self.rotation,
scaleY: 0.52,
tint: type.color // original fruit color for bottom
});
game.addChild(halfTop);
game.addChild(halfBottom);
// Animate halves flying apart, rotating and fading out
tween(halfTop, {
x: self.x - 60,
y: self.y - 80,
rotation: self.rotation - 0.32,
alpha: 0
}, {
duration: 420,
easing: tween.easeOut,
onFinish: function onFinish() {
halfTop.destroy();
}
});
tween(halfBottom, {
x: self.x + 60,
y: self.y + 80,
rotation: self.rotation + 0.32,
alpha: 0
}, {
duration: 420,
easing: tween.easeOut,
onFinish: function onFinish() {
halfBottom.destroy();
}
});
// Juice splash effect (ellipse, color based on fruit)
// More vivid color for splash (slightly saturated)
var vividJuiceColor = function (c) {
// Slightly brighten and saturate the color
var r = Math.min(255, (c >> 16 & 0xff) * 1.15 + 32) | 0;
var g = Math.min(255, (c >> 8 & 0xff) * 1.15 + 32) | 0;
var b = Math.min(255, (c & 0xff) * 1.15 + 32) | 0;
return r << 16 | g << 8 | b;
}(type.color || 0xff2d2d);
// Create multiple juice particles for a juicier effect
var juiceParticles = [];
var juiceParticleCount = 12 + Math.floor(Math.random() * 7); // 12-18 particles
for (var jp = 0; jp < juiceParticleCount; jp++) {
var angle = Math.random() * Math.PI * 2;
var dist = 40 + Math.random() * 60;
var scale = 0.22 + Math.random() * 0.22;
var alpha = 0.55 + Math.random() * 0.25;
var splash = LK.getAsset('juice_splash_' + type.id, {
anchorX: 0.5,
anchorY: 0.5,
x: self.x,
y: self.y,
scaleX: scale * (0.8 + Math.random() * 0.7),
scaleY: scale * (0.5 + Math.random() * 1.1),
alpha: alpha,
tint: vividJuiceColor
});
game.addChild(splash);
juiceParticles.push(splash);
// Animate each particle outward and fade
tween(splash, {
x: self.x + Math.cos(angle) * dist,
y: self.y + Math.sin(angle) * dist,
scaleX: scale * (1.2 + Math.random() * 1.2),
scaleY: scale * (1.1 + Math.random() * 1.2),
alpha: 0
}, {
duration: 320 + Math.random() * 120,
easing: tween.easeOut,
onFinish: function (s) {
return function () {
s.destroy();
};
}(splash)
});
}
// --- Add stain to background ---
if (typeof background !== "undefined" && background && background.parent) {
// Create a stain asset (ellipse, color based on fruit, random rotation/scale)
var stain = LK.getAsset('juice_stain_' + type.id, {
anchorX: 0.5,
anchorY: 0.5,
x: self.x,
y: self.y,
scaleX: 0.7 + Math.random() * 0.7,
scaleY: 0.5 + Math.random() * 0.7,
alpha: 0.32 + Math.random() * 0.18,
rotation: Math.random() * Math.PI * 2,
tint: vividJuiceColor
});
// Insert stain just above the background (so it stays under fruit halves/splashes)
var bgParent = background.parent;
var bgIndex = bgParent.getChildIndex(background);
bgParent.addChildAt(stain, bgIndex + 1);
}
// --- End stain ---
// Hide original fruit immediately
self.alpha = 0;
tween(self, {
alpha: 0
}, {
duration: 1,
onFinish: function onFinish() {
self.destroy();
}
});
};
// Update per frame
self.update = function () {
if (self.sliced) return;
self.x += self.vx;
self.y += self.vy;
self.vy += self.gravity;
// Prevent from leaving left/right screen edges
if (self.x < 0 + self.width * 0.5) {
self.x = self.width * 0.5;
self.vx = -self.vx * 0.55; // bounce back with some energy loss
}
if (self.x > 2048 - self.width * 0.5) {
self.x = 2048 - self.width * 0.5;
self.vx = -self.vx * 0.55;
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x1a1a1a
});
/****
* Game Code
****/
// Add background image
// Game variables
var background = LK.getAsset('background', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0
});
game.addChild(background);
var fruits = [];
var bombs = [];
var fruitSpawnTimer = 0;
var bombSpawnTimer = 0;
var fruitSpawnInterval = 120; // 2 seconds at 60fps
var bombSpawnInterval = 300; // 5 seconds at 60fps
var score = 0;
var highScore = storage.highScore || 0;
var missed = 0;
var maxMissed = 3;
var isGameOver = false;
// Score display
var scoreTxt = new Text2('0', {
size: 120,
fill: "#fff"
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Combo system
var comboCount = 0;
var comboTimer = 0;
var comboTimeout = 36; // 0.6s at 60fps
var maxCombo = 0;
var comboTxt = new Text2('', {
size: 90,
fill: 0xFFFA00
});
comboTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(comboTxt);
// High score display (smaller, top right)
var highScoreTxt = new Text2('Best: ' + highScore, {
size: 60,
fill: "#fff"
});
highScoreTxt.anchor.set(1, 0);
LK.gui.topRight.addChild(highScoreTxt);
// Missed fruits display (bottom left, as X icons)
var missTxt = new Text2('', {
size: 90,
fill: 0xFF4444
});
missTxt.anchor.set(0, 1);
LK.gui.bottomLeft.addChild(missTxt);
// Helper: update score
function updateScore(val) {
score = val;
scoreTxt.setText(score);
if (score > highScore) {
highScore = score;
storage.highScore = highScore;
highScoreTxt.setText('Best: ' + highScore);
}
}
// Helper: update missed display
function updateMissed() {
var s = '';
for (var i = 0; i < missed; i++) s += '✗ ';
missTxt.setText(s);
}
// (spawning is now handled by fruit/bomb timers in game.update)
// Slicing logic
var swipePath = [];
var swipeMaxLength = 12; // Number of points to keep in swipe trail
var swipeRadius = 90; // How close swipe must be to slice
// Draw swipe trail (for visual feedback)
var swipeTrail = [];
function drawSwipeTrail() {
// Remove old
for (var i = 0; i < swipeTrail.length; i++) {
swipeTrail[i].destroy();
}
swipeTrail = [];
// Draw new
for (var i = 1; i < swipePath.length; i++) {
var p1 = swipePath[i - 1];
var p2 = swipePath[i];
// Draw a short white ellipse between points
var dx = p2.x - p1.x,
dy = p2.y - p1.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist < 10) continue;
var angle = Math.atan2(dy, dx);
var id = 'swipe_' + i;
var seg = LK.getAsset(id, {
anchorX: 0,
anchorY: 0.5,
x: p1.x,
y: p1.y,
rotation: angle,
alpha: 0.22
});
game.addChild(seg);
swipeTrail.push(seg);
}
}
// Handle swipe start
game.down = function (x, y, obj) {
if (isGameOver) return;
swipePath = [{
x: x,
y: y
}];
drawSwipeTrail();
};
// Handle swipe move
game.move = function (x, y, obj) {
if (isGameOver) return;
swipePath.push({
x: x,
y: y
});
if (swipePath.length > swipeMaxLength) swipePath.shift();
drawSwipeTrail();
// For each segment in swipe, check for intersection with fruits/bombs
for (var i = 1; i < swipePath.length; i++) {
var p1 = swipePath[i - 1],
p2 = swipePath[i];
// Fruits
for (var f = fruits.length - 1; f >= 0; f--) {
var fruit = fruits[f];
if (fruit.sliced) continue;
// If fruit already off screen, skip
if (fruit.y > 2732 + 120) continue;
// Check if swipe segment is close to fruit center
var cx = fruit.x,
cy = fruit.y;
var dist = pointToSegmentDistance(cx, cy, p1.x, p1.y, p2.x, p2.y);
if (dist < swipeRadius) {
fruit.slice();
updateScore(score + fruit.points); // Increase score by fruit's point value
fruits.splice(f, 1);
// Flash effect
LK.effects.flashObject(fruit, 0xffffff, 120);
// --- Combo logic ---
comboCount++;
comboTimer = comboTimeout;
if (comboCount > maxCombo) maxCombo = comboCount;
if (comboCount > 1) {
comboTxt.setText(comboCount + " Combo!");
comboTxt.alpha = 1;
// Animate combo text alpha in case it was faded out
tween(comboTxt, {
alpha: 1
}, {
duration: 60
});
// Bonus points for combos (e.g. +2 for 3-combo, +4 for 4-combo, etc)
if (comboCount >= 3) {
var bonus = (comboCount - 2) * 2;
updateScore(score + bonus);
// Animate combo text
tween(comboTxt, {
scaleX: 1.3,
scaleY: 1.3
}, {
duration: 120,
yoyo: true,
repeat: 1
});
}
}
}
}
// Bombs
for (var b = bombs.length - 1; b >= 0; b--) {
var bomb = bombs[b];
if (bomb.sliced) continue;
if (bomb.y > 2732 + 120) continue;
var cx = bomb.x,
cy = bomb.y;
var dist = pointToSegmentDistance(cx, cy, p1.x, p1.y, p2.x, p2.y);
if (dist < swipeRadius) {
bomb.slice();
bombs.splice(b, 1);
// Play bomb sound
LK.getSound('Bomb').play();
// Increase score by 1 when bomb is sliced
updateScore(score + 1);
// Flash screen red, then game over
LK.effects.flashScreen(0xff2222, 800);
isGameOver = true;
LK.setTimeout(function () {
LK.showGameOver();
}, 600);
return;
}
}
}
};
// Handle swipe end
game.up = function (x, y, obj) {
swipePath = [];
drawSwipeTrail();
};
// Helper: distance from point to segment
function pointToSegmentDistance(px, py, x1, y1, x2, y2) {
var dx = x2 - x1,
dy = y2 - y1;
if (dx === 0 && dy === 0) {
dx = px - x1;
dy = py - y1;
return Math.sqrt(dx * dx + dy * dy);
}
var t = ((px - x1) * dx + (py - y1) * dy) / (dx * dx + dy * dy);
t = Math.max(0, Math.min(1, t));
var lx = x1 + t * dx,
ly = y1 + t * dy;
dx = px - lx;
dy = py - ly;
return Math.sqrt(dx * dx + dy * dy);
}
// Main game update
game.update = function () {
if (isGameOver) return;
// --- Dynamic difficulty: speed up game as score increases ---
var minFruitInterval = 36; // minimum interval (0.6s)
var minBombInterval = 120; // minimum interval (2s)
var fruitIntervalStep = Math.max(0, fruitSpawnInterval - Math.floor(score / 10) * 10);
fruitSpawnInterval = Math.max(minFruitInterval, 120 - Math.floor(score / 10) * 10);
bombSpawnInterval = Math.max(minBombInterval, 300 - Math.floor(score / 15) * 15);
// Spawn logic
fruitSpawnTimer++;
bombSpawnTimer++;
if (fruitSpawnTimer >= fruitSpawnInterval) {
// Spawn a fruit only
var obj = new Fruit();
// All fruits spawn at the same Y (bottom), but random X across the screen (leaving margin for fruit width)
var fruitMargin = 200;
var x = fruitMargin + Math.random() * (2048 - 2 * fruitMargin);
var y = 2732 + 60; // All fruits spawn just below the bottom of the screen
// Launch angle is always straight up
var angle = -Math.PI / 2;
var speed = 17 + Math.random() * 5.5 + score * 0.055;
// vx is 0 (no side velocity), vy is always up
var vx = 0;
var vy = -Math.abs(Math.sin(angle) * speed) * 1.32;
obj.x = x;
obj.y = y;
obj.vx = vx;
obj.vy = vy;
obj.scaleX = obj.scaleY = 1;
obj.alpha = 1;
// Increase gravity for fruit as score increases
obj.gravity += Math.min(0.18, score * 0.012);
fruits.push(obj);
game.addChild(obj);
fruitSpawnTimer = 0;
}
if (bombSpawnTimer >= bombSpawnInterval) {
// Spawn a bomb only
var obj = new Bomb();
// All bombs spawn at the same Y (bottom), but random X across the screen (leaving margin for bomb width)
var bombMargin = 200;
var x = bombMargin + Math.random() * (2048 - 2 * bombMargin);
var y = 2732 + 60; // All bombs spawn just below the bottom of the screen
// Launch angle is always straight up
var angle = -Math.PI / 2;
var speed = 17 + Math.random() * 5.5 + score * 0.055;
// vx is 0 (no side velocity), vy is always up
var vx = 0;
var vy = -Math.abs(Math.sin(angle) * speed) * 1.32;
obj.x = x;
obj.y = y;
obj.vx = vx;
obj.vy = vy;
obj.scaleX = obj.scaleY = 1;
obj.alpha = 1;
// Increase gravity for bomb as score increases
obj.gravity += Math.min(0.18, score * 0.012);
bombs.push(obj);
game.addChild(obj);
bombSpawnTimer = 0;
}
// Update fruits
for (var i = fruits.length - 1; i >= 0; i--) {
var fruit = fruits[i];
fruit.update();
// Remove if off screen
if (!fruit.sliced && fruit.y > 2732 + 120) {
// Play lose sound when fruit is missed
LK.getSound('Lose').play();
fruit.destroy();
fruits.splice(i, 1);
missed++;
updateMissed();
// Flash screen if missed
LK.effects.flashScreen(0x2222ff, 200);
if (missed >= maxMissed) {
isGameOver = true;
LK.setTimeout(function () {
LK.showGameOver();
}, 500);
}
// Do NOT increase score here; only bomb slice increases score now
}
}
// Update bombs
for (var i = bombs.length - 1; i >= 0; i--) {
var bomb = bombs[i];
bomb.update();
if (bomb.y > 2732 + 120) {
bomb.destroy();
bombs.splice(i, 1);
}
}
// Fade out swipe trail
for (var i = 0; i < swipeTrail.length; i++) {
swipeTrail[i].alpha *= 0.85;
if (swipeTrail[i].alpha < 0.05) {
swipeTrail[i].destroy();
swipeTrail.splice(i, 1);
i--;
}
}
// --- Combo timer logic ---
if (comboTimer > 0) {
comboTimer--;
if (comboTimer === 0) {
// Combo ended
comboCount = 0;
tween(comboTxt, {
alpha: 0
}, {
duration: 320
});
}
}
};
// Reset game state on new game
game.on('reset', function () {
// Remove all fruits and bombs
for (var i = 0; i < fruits.length; i++) fruits[i].destroy();
for (var i = 0; i < bombs.length; i++) bombs[i].destroy();
fruits = [];
bombs = [];
fruitSpawnTimer = 0;
bombSpawnTimer = 0;
score = 0;
missed = 0;
isGameOver = false;
updateScore(0);
updateMissed();
swipePath = [];
drawSwipeTrail();
comboCount = 0;
comboTimer = 0;
comboTxt.setText('');
comboTxt.alpha = 0;
maxCombo = 0;
highScore = storage.highScore || 0;
highScoreTxt.setText('Best: ' + highScore);
});
// Initial state
updateScore(0);
updateMissed();
// Fruit assets
// Bomb asset: black circle
// Bomb border: red, slightly larger, semi-transparent