/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1"); /**** * Classes ****/ // Bomb class: Represents a bomb flying through the air var Bomb = Container.expand(function () { var self = Container.call(this); self.sliced = false; self.missed = false; // Attach bomb asset (black ellipse) var bombAsset = self.attachAsset('bomb', { anchorX: 0.5, anchorY: 0.5, width: 190, height: 190, color: 0x222222, shape: 'ellipse' }); // Add a white "fuse" (small rectangle) var fuse = self.attachAsset('fuse', { anchorX: 0.5, anchorY: 1, width: 20, height: 50, color: 0xffffff, shape: 'box', y: -80 }); // Set initial position and velocity (to be set by spawner) self.x = 0; self.y = 0; self.vx = 0; self.vy = 0; self.gravity = 0.35 + Math.random() * 0.1; self.lastY = self.y; self.update = function () { if (self.sliced) return; self.x += self.vx; self.y += self.vy; self.vy += self.gravity; bombAsset.rotation += 0.09; if (!self.missed && self.y > 2732 + 100) { self.missed = true; } }; self.slice = function () { if (self.sliced) return; self.sliced = true; // Animate: flash red and fade out tween(self, { scaleX: 1.7, scaleY: 1.7, alpha: 0 }, { duration: 250, easing: tween.easeOut, onFinish: function onFinish() { self.destroy(); } }); }; return self; }); // Fruit class: Represents a fruit flying through the air var Fruit = Container.expand(function () { var self = Container.call(this); // Fruit types and their properties var fruitTypes = [{ id: 'apple', color: 0xff2d2d, points: 1 }, { id: 'banana', color: 0xfff700, points: 2 }, { id: 'orange', color: 0xffa500, points: 3 }, { id: 'watermelon', color: 0x2ecc40, points: 5 }, { id: 'grape', color: 0x8e44ad, points: 4 }, { id: 'kiwi', color: 0x7ed957, points: 3 }, { id: 'lemon', color: 0xfff700, points: 2 }, { id: 'strawberry', color: 0xff3b6b, points: 4 }, { id: 'blueberry', color: 0x3b5fff, points: 4 }, { id: 'pineapple', color: 0xffe066, points: 5 }]; // Randomly pick a fruit type var typeIndex = Math.floor(Math.random() * fruitTypes.length); var type = fruitTypes[typeIndex]; self.fruitType = type.id; self.points = type.points; self.sliced = false; self.missed = false; // Attach fruit asset (ellipse for fruit) var fruitAsset = self.attachAsset(type.id, { anchorX: 0.5, anchorY: 0.5, width: 320, height: 320, color: type.color, shape: 'ellipse' }); // Give fruit a random initial rotation fruitAsset.rotation = Math.random() * Math.PI * 2; // Set initial position and velocity (to be set by spawner) self.x = 0; self.y = 0; self.vx = 0; self.vy = 0; self.gravity = 0.35 + Math.random() * 0.1; // For tracking if fruit is out of bounds self.lastY = self.y; // For combo detection self.sliceTick = -1; // Animate fruit (arc trajectory) self.update = function () { if (self.sliced) return; // Don't update if already sliced self.x += self.vx; self.y += self.vy; self.vy += self.gravity; // Spin fruit fruitAsset.rotation += 0.07; // If fruit falls below screen, mark as missed if (!self.missed && self.y > 2732 + 100) { self.missed = true; } }; // Slice the fruit self.slice = function () { if (self.sliced) return; self.sliced = true; // --- JUICE SPLASH EFFECT (more visible & dramatic) --- var splashCount = (self.fruitType === "strawberry" ? 22 : 14) + Math.floor(Math.random() * (self.fruitType === "strawberry" ? 8 : 4)); // more splashes for strawberry for (var i = 0; i < splashCount; i++) { // Spread splashes more evenly, but with some randomness var baseAngle = i / splashCount * Math.PI * 2; var angle = baseAngle + (Math.random() - 0.5) * (Math.PI / 8); var speed = (self.fruitType === "strawberry" ? 54 : 38) + Math.random() * (self.fruitType === "strawberry" ? 38 : 32); // faster for strawberry var splash = new Container(); // Use a colored ellipse for splash, color matches fruit var splashColor = 0xff2d2d; // default red if (self.fruitType === "lemon") { splashColor = 0xfff700; } else if (self.fruitType === "strawberry") { splashColor = 0xff1744; // bright strawberry red } else if (self.points > 3) { splashColor = 0xFFE066; } else if (self.points === 1) { splashColor = 0xff2d2d; } else if (self.points === 2) { splashColor = 0xfff700; } else if (self.points === 3) { splashColor = 0xffa500; } else if (self.points === 4) { splashColor = 0x8e44ad; } else { splashColor = 0x2ecc40; } var splashAsset = splash.attachAsset('slicingTrail', { anchorX: 0.5, anchorY: 0.5, width: 120 + Math.random() * 60, // much larger height: 32 + Math.random() * 18, // much taller color: splashColor, alpha: 0.95, // more opaque rotation: angle }); splash.x = self.x; splash.y = self.y; if (self.fruitType === "strawberry") { splash.scaleX = 1.7 + Math.random() * 1.1; splash.scaleY = 1.7 + Math.random() * 1.1; splash.alpha = 1; } else { splash.scaleX = 1.1 + Math.random() * 0.7; splash.scaleY = 1.1 + Math.random() * 0.7; } game.addChild(splash); // Animate splash: move outward, fade out, stretch and shrink (function (splash, splashAsset, angle, speed) { var dx = Math.cos(angle) * speed; var dy = Math.sin(angle) * speed; tween(splash, { x: splash.x + dx, y: splash.y + dy, alpha: 0, scaleX: splash.scaleX * (2.2 + Math.random() * 0.7), scaleY: splash.scaleY * (0.5 + Math.random() * 0.3) }, { duration: (self.fruitType === "strawberry" ? 1100 : 600) + Math.random() * (self.fruitType === "strawberry" ? 400 : 200), // longer duration for strawberry easing: tween.cubicOut, onFinish: function onFinish() { splash.destroy(); } }); })(splash, splashAsset, angle, speed); } // Animate: scale up and fade out tween(self, { scaleX: 1.5, scaleY: 1.5, alpha: 0 }, { duration: 350, easing: tween.easeOut, onFinish: function onFinish() { self.destroy(); } }); }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x222a36 }); /**** * Game Code ****/ // Slicing effect asset: a white ellipse, slightly transparent, for the slicing trail // --- MAIN MENU STATE --- var isGameStarted = false; // Main menu container var mainMenu = new Container(); // Top score asset and text (centered on screen) // Use persistent storage to get/set top score for this user var userTopScore = storage.userTopScore || 0; var topScoreAsset = LK.getAsset('missedX', { anchorX: 0.5, anchorY: 0.5, width: 90, height: 90, x: 2048 / 2, y: 2732 / 2 }); mainMenu.addChild(topScoreAsset); var topScoreTxt = new Text2('En Yüksek Skor: ' + userTopScore, { size: 110, fill: "#fff", font: "bold 900 110px Arial, 'GillSans-Bold', Impact, 'Arial Black', Tahoma" }); topScoreTxt.anchor.set(0.5, 0.5); // Place text below the asset, centered horizontally topScoreTxt.x = topScoreAsset.x; topScoreTxt.y = topScoreAsset.y + 120; mainMenu.addChild(topScoreTxt); // Update top score in storage and menu if needed function updateUserTopScore(newScore) { var prev = storage.userTopScore || 0; if (newScore > prev) { storage.userTopScore = newScore; if (typeof topScoreInGameTxt !== "undefined") { topScoreInGameTxt.setText('En Yüksek Skor: ' + newScore); } } } // --- GLOBALS --- var fruits = []; var bombs = []; var missedFruits = 0; var maxMissed = 5; // Increased from 3 to 5 // Add missedX asset for missed indicator var comboWindow = 20; // ticks for combo var lastSliceTick = -100; var comboCount = 0; var bestCombo = 0; var backgroundIndex = 0; var backgrounds = [{ id: 'bg1', color: 0x222a36 }, { id: 'bg2', color: 0x1a3d2f }, { id: 'bg3', color: 0x3d1a2f }, { id: 'bg4', color: 0x1e1e2f }]; var missedXNodes = []; // --- MAIN MENU STATE --- var isGameStarted = false; // Main menu container var mainMenu = new Container(); // Main menu background (reuse bg4) var menuBg = LK.getAsset('bg4', { anchorX: 0, anchorY: 0, x: 0, y: 0, width: 2048, height: 2732 }); mainMenu.addChild(menuBg); // "Play" button (now using 'by furkan' asset) var playBtn = LK.getAsset('byfurkan', { anchorX: 0.5, anchorY: 0.5, width: 400 * 2, height: 180 * 2 }); playBtn.x = 2048 / 2 + 70; playBtn.y = 2732 / 2 + 400; mainMenu.addChild(playBtn); var playBtnTxt = new Text2('PLAY', { size: 120, fill: "#fff", font: "bold 900 120px Arial, 'GillSans-Bold', Impact, 'Arial Black', Tahoma" }); playBtnTxt.anchor.set(0.5, 0.5); playBtnTxt.x = playBtn.x; playBtnTxt.y = playBtn.y; mainMenu.addChild(playBtnTxt); // Show main menu at start game.addChild(mainMenu); // Removed updateMenuTopScore function as top score text is no longer used // --- GUI Elements --- // Score text var scoreTxt = new Text2('0', { size: 120, fill: "#fff" }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); // Top score (user's best) text, always visible at the top center, above the score var topScoreInGameTxt = new Text2('En Yüksek Skor: ' + (storage.userTopScore || 0), { size: 1040, fill: "#000", font: "bold 900 1040px Arial, 'GillSans-Bold', Impact, 'Arial Black', Tahoma" }); topScoreInGameTxt.anchor.set(0.5, 0); // Move text further to the right (e.g. +500px) topScoreInGameTxt.x = 500; // x=0 in LK.gui.top is the center, so +500px to the right topScoreInGameTxt.y = 0; // top edge LK.gui.top.addChild(topScoreInGameTxt); // Move scoreTxt a bit lower so both are visible scoreTxt.y = topScoreInGameTxt.height + 10; // High score (best combo) text var bestComboTxt = new Text2('Best: 0', { size: 120, fill: "#000", font: "bold 900 120px Arial, 'GillSans-Bold', Impact, 'Arial Black', Tahoma" }); bestComboTxt.anchor.set(1, 0); // right edge, top bestComboTxt.x = 0; // x=0 in LK.gui.topRight is the right edge bestComboTxt.y = 0; LK.gui.topRight.addChild(bestComboTxt); // Missed fruits indicator (bottom left, red X's) var missedTxt = new Text2('', { size: 90, fill: 0xFF4444 }); missedTxt.anchor.set(0, 1); LK.gui.bottomLeft.addChild(missedTxt); // Combo text (center, fades out) var comboTxt = new Text2('', { size: 180, fill: 0xFFE066 }); comboTxt.anchor.set(0.5, 0.5); comboTxt.alpha = 0; LK.gui.center.addChild(comboTxt); // Background switch button (bottom right) var bgBtn = LK.getAsset('bgBtn', { anchorX: 1, anchorY: 1, width: 180, height: 100, color: 0x8888ff, shape: 'box' }); bgBtn.x = 0; bgBtn.y = 0; LK.gui.bottomRight.addChild(bgBtn); // Removed bgBtnTxt ('BG' text) as requested // --- BACKGROUND --- // Fullscreen background image node var bgImageNode = LK.getAsset('bg4', { anchorX: 0, anchorY: 0, x: 0, y: 0, width: 2048, height: 2732 }); game.addChild(bgImageNode); function setBackground(idx) { backgroundIndex = idx % backgrounds.length; // Always keep background image behind everything if (bgImageNode.parent) { game.setChildIndex(bgImageNode, 0); } // Optionally, you could swap image asset here if you want to support more backgrounds game.setBackgroundColor(backgrounds[backgroundIndex].color); } setBackground(0); // --- FRUIT & BOMB SPAWNING --- function spawnFruitOrBomb() { // Randomly decide: 80% fruit, 20% bomb var isBomb = Math.random() < 0.2; var obj; // Spawn X within safe bounds var x = 320 + Math.random() * (2048 - 640); var y = 2732 + 80; // Calculate angle so fruit always arcs toward center var centerX = 2048 / 2; var dx = centerX - x; var minAngle = -Math.PI / 2 - Math.PI / 6; // -120deg var maxAngle = -Math.PI / 2 + Math.PI / 6; // -60deg // Angle toward center, with a little random spread var baseAngle = Math.atan2(-1200, dx); var angle = baseAngle + (Math.random() - 0.5) * (Math.PI / 12); // small random spread // Clamp angle to safe range if (angle < minAngle) angle = minAngle; if (angle > maxAngle) angle = maxAngle; var speed = 38 + Math.random() * 10; if (isBomb) { obj = new Bomb(); bombs.push(obj); } else { obj = new Fruit(); fruits.push(obj); } obj.x = x; obj.y = y; obj.vx = Math.cos(angle) * speed; obj.vy = Math.sin(angle) * speed; game.addChild(obj); } // --- GAME LOGIC --- // Swiping: track move events, draw path, and slice var swipePath = []; var swipeMaxLength = 8; // Only keep last 8 points // --- Slicing Effect --- // Slicing effect node (a white line trail) var slicingEffectNodes = []; var slicingEffectVisible = false; // Helper to clear slicing effect function clearSlicingEffect() { for (var i = 0; i < slicingEffectNodes.length; i++) { if (slicingEffectNodes[i] && slicingEffectNodes[i].parent) { slicingEffectNodes[i].parent.removeChild(slicingEffectNodes[i]); } } slicingEffectNodes = []; } // Hide gameplay GUI until game starts scoreTxt.visible = false; bestComboTxt.visible = false; missedTxt.visible = false; comboTxt.visible = false; bgBtn.visible = false; // Show/hide GUI based on game state function updateGUIVisibility() { var show = isGameStarted; scoreTxt.visible = show; bestComboTxt.visible = show; missedTxt.visible = show; comboTxt.visible = show; bgBtn.visible = show; } // Call this after Play is pressed function startGame() { isGameStarted = true; mainMenu.visible = false; updateGUIVisibility(); resetGame(); } // Play button event playBtn.down = function (x, y, obj) { startGame(); }; function handleSlice(x, y) { if (!isGameStarted) return; // Check fruits for (var i = fruits.length - 1; i >= 0; i--) { var fruit = fruits[i]; if (fruit.sliced) continue; // If swipe is close to fruit center (distance < 120) var dx = fruit.x - x; var dy = fruit.y - y; if (dx * dx + dy * dy < 120 * 120) { fruit.slice(); // Combo logic if (lastSliceTick > 0 && LK.ticks - lastSliceTick < comboWindow) { comboCount++; } else { comboCount = 1; } lastSliceTick = LK.ticks; // Combo multiplier: 1x for single, 2x for double, 3x for triple or more var comboMultiplier = 1; if (comboCount === 2) comboMultiplier = 2; if (comboCount >= 3) comboMultiplier = 3; // Each fruit is worth 10 points, multiplied by combo var pointsEarned = 10 * comboMultiplier; LK.setScore(LK.getScore() + pointsEarned); scoreTxt.setText(LK.getScore()); // Show combo text if comboCount >= 2 if (comboCount >= 2) { showCombo(comboCount); } // Track best combo if (typeof bestCombo === "undefined") { bestCombo = 0; } if (comboCount > bestCombo) { bestCombo = comboCount; bestComboTxt.setText('Best: ' + bestCombo); // Removed updateMenuTopScore call as top score text is no longer used } fruit.sliceTick = LK.ticks; } } // Check bombs for (var j = bombs.length - 1; j >= 0; j--) { var bomb = bombs[j]; if (bomb.sliced) continue; var dx = bomb.x - x; var dy = bomb.y - y; if (dx * dx + dy * dy < 120 * 120) { bomb.slice(); // Game over updateUserTopScore(LK.getScore()); if (typeof topScoreInGameTxt !== "undefined") { var userTopScoreNow = storage.userTopScore || 0; topScoreInGameTxt.setText('En Yüksek Skor: ' + userTopScoreNow); } LK.effects.flashScreen(0xff0000, 800); LK.showGameOver(); return; } } } // Show combo text function showCombo(count) { if (count < 2) return; comboTxt.setText('Combo x' + count + '!'); comboTxt.alpha = 1; tween(comboTxt, { alpha: 0 }, { duration: 900, easing: tween.linear }); } // Update missed fruits display function updateMissed() { // Remove previous X assets if any if (typeof missedXNodes !== "undefined") { for (var i = 0; i < missedXNodes.length; i++) { if (missedXNodes[i] && missedXNodes[i].parent) { missedXNodes[i].parent.removeChild(missedXNodes[i]); } } } missedXNodes = []; // Draw up to maxMissed X's for (var i = 0; i < maxMissed; i++) { var xNode = LK.getAsset('missedX', { anchorX: 0, anchorY: 1, x: i * 100, // 90px width + 10px gap y: 0, alpha: i < missedFruits ? 1 : 0.25 // faded if not missed yet }); LK.gui.bottomLeft.addChild(xNode); missedXNodes.push(xNode); } // Hide the old text indicator missedTxt.setText(''); } // --- INPUT HANDLING --- // Swiping: track move events, draw path, and slice game.move = function (x, y, obj) { if (!isGameStarted) return; // Don't allow swipes in top left 100x100 if (x < 100 && y < 100) return; // Add to swipe path swipePath.push({ x: x, y: y, tick: LK.ticks }); if (swipePath.length > swipeMaxLength) swipePath.shift(); // --- Slicing Effect: Show and update --- slicingEffectVisible = true; clearSlicingEffect(); // Draw lines between swipe points for (var i = 1; i < swipePath.length; i++) { var p0 = swipePath[i - 1]; var p1 = swipePath[i]; // Use the custom slicingTrail asset as a "segment" between points var dx = p1.x - p0.x; var dy = p1.y - p0.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist > 0) { var seg = LK.getAsset('slicingTrail', { anchorX: 0.5, anchorY: 0.5, width: dist, height: 24, x: (p0.x + p1.x) / 2, y: (p0.y + p1.y) / 2, rotation: Math.atan2(dy, dx), alpha: 0.7 }); game.addChild(seg); slicingEffectNodes.push(seg); } } // For each point in swipe path, check for slicing for (var i = 0; i < swipePath.length; i++) { handleSlice(swipePath[i].x, swipePath[i].y); } }; // On down, start new swipe game.down = function (x, y, obj) { if (!isGameStarted) return; swipePath = [{ x: x, y: y, tick: LK.ticks }]; handleSlice(x, y); // --- Slicing Effect: Show on down --- slicingEffectVisible = true; clearSlicingEffect(); }; // On up, clear swipe path game.up = function (x, y, obj) { if (!isGameStarted) return; swipePath = []; // --- Slicing Effect: Hide on up --- slicingEffectVisible = false; clearSlicingEffect(); }; // Background button: tap to change background bgBtn.down = function (x, y, obj) { setBackground((backgroundIndex + 1) % backgrounds.length); }; // --- GAME UPDATE LOOP --- game.update = function () { if (!isGameStarted) { updateGUIVisibility(); return; } updateGUIVisibility(); // Spawn fruits/bombs every 35-55 ticks if (LK.ticks % (35 + Math.floor(Math.random() * 20)) === 0) { spawnFruitOrBomb(); } // Update fruits for (var i = fruits.length - 1; i >= 0; i--) { var fruit = fruits[i]; // Remove if destroyed if (fruit.destroyed) { fruits.splice(i, 1); continue; } // If missed if (fruit.missed && !fruit.sliced) { fruits.splice(i, 1); missedFruits++; updateMissed(); LK.effects.flashObject(missedTxt, 0xff0000, 400); // Check and update top score if needed before game over if (missedFruits >= maxMissed) { updateUserTopScore(LK.getScore()); if (typeof topScoreInGameTxt !== "undefined") { var userTopScoreNow = storage.userTopScore || 0; topScoreInGameTxt.setText('En Yüksek Skor: ' + userTopScoreNow); } LK.effects.flashScreen(0xff0000, 800); LK.showGameOver(); return; } } } // Update bombs for (var j = bombs.length - 1; j >= 0; j--) { var bomb = bombs[j]; if (bomb.destroyed) { bombs.splice(j, 1); continue; } } // Combo reset if (comboCount > 1 && LK.ticks - lastSliceTick > comboWindow) { comboCount = 0; } }; // --- GAME RESET HANDLING --- // On game reset, clear all arrays and reset state function resetGame() { fruits = []; bombs = []; missedFruits = 0; comboCount = 0; lastSliceTick = -100; scoreTxt.setText('0'); comboTxt.setText(''); comboTxt.alpha = 0; updateMissed(); setBackground(0); // --- Slicing Effect: Clear on reset --- clearSlicingEffect(); slicingEffectVisible = false; // On reset, update top score if needed updateUserTopScore(LK.getScore()); if (typeof topScoreInGameTxt !== "undefined") { var userTopScoreNow = storage.userTopScore || 0; topScoreInGameTxt.setText('En Yüksek Skor: ' + userTopScoreNow); } // Only reset bestCombo if not started, otherwise keep best score if (!isGameStarted) { if (typeof bestCombo === "undefined") bestCombo = 0; bestComboTxt.setText('Best: ' + bestCombo); // Removed updateMenuTopScore call as top score text is no longer used } else { bestComboTxt.setText('Best: ' + (typeof bestCombo !== "undefined" ? bestCombo : 0)); // Removed updateMenuTopScore call as top score text is no longer used } } resetGame(); // --- END OF GAME CODE ---
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
// Bomb class: Represents a bomb flying through the air
var Bomb = Container.expand(function () {
var self = Container.call(this);
self.sliced = false;
self.missed = false;
// Attach bomb asset (black ellipse)
var bombAsset = self.attachAsset('bomb', {
anchorX: 0.5,
anchorY: 0.5,
width: 190,
height: 190,
color: 0x222222,
shape: 'ellipse'
});
// Add a white "fuse" (small rectangle)
var fuse = self.attachAsset('fuse', {
anchorX: 0.5,
anchorY: 1,
width: 20,
height: 50,
color: 0xffffff,
shape: 'box',
y: -80
});
// Set initial position and velocity (to be set by spawner)
self.x = 0;
self.y = 0;
self.vx = 0;
self.vy = 0;
self.gravity = 0.35 + Math.random() * 0.1;
self.lastY = self.y;
self.update = function () {
if (self.sliced) return;
self.x += self.vx;
self.y += self.vy;
self.vy += self.gravity;
bombAsset.rotation += 0.09;
if (!self.missed && self.y > 2732 + 100) {
self.missed = true;
}
};
self.slice = function () {
if (self.sliced) return;
self.sliced = true;
// Animate: flash red and fade out
tween(self, {
scaleX: 1.7,
scaleY: 1.7,
alpha: 0
}, {
duration: 250,
easing: tween.easeOut,
onFinish: function onFinish() {
self.destroy();
}
});
};
return self;
});
// Fruit class: Represents a fruit flying through the air
var Fruit = Container.expand(function () {
var self = Container.call(this);
// Fruit types and their properties
var fruitTypes = [{
id: 'apple',
color: 0xff2d2d,
points: 1
}, {
id: 'banana',
color: 0xfff700,
points: 2
}, {
id: 'orange',
color: 0xffa500,
points: 3
}, {
id: 'watermelon',
color: 0x2ecc40,
points: 5
}, {
id: 'grape',
color: 0x8e44ad,
points: 4
}, {
id: 'kiwi',
color: 0x7ed957,
points: 3
}, {
id: 'lemon',
color: 0xfff700,
points: 2
}, {
id: 'strawberry',
color: 0xff3b6b,
points: 4
}, {
id: 'blueberry',
color: 0x3b5fff,
points: 4
}, {
id: 'pineapple',
color: 0xffe066,
points: 5
}];
// Randomly pick a fruit type
var typeIndex = Math.floor(Math.random() * fruitTypes.length);
var type = fruitTypes[typeIndex];
self.fruitType = type.id;
self.points = type.points;
self.sliced = false;
self.missed = false;
// Attach fruit asset (ellipse for fruit)
var fruitAsset = self.attachAsset(type.id, {
anchorX: 0.5,
anchorY: 0.5,
width: 320,
height: 320,
color: type.color,
shape: 'ellipse'
});
// Give fruit a random initial rotation
fruitAsset.rotation = Math.random() * Math.PI * 2;
// Set initial position and velocity (to be set by spawner)
self.x = 0;
self.y = 0;
self.vx = 0;
self.vy = 0;
self.gravity = 0.35 + Math.random() * 0.1;
// For tracking if fruit is out of bounds
self.lastY = self.y;
// For combo detection
self.sliceTick = -1;
// Animate fruit (arc trajectory)
self.update = function () {
if (self.sliced) return; // Don't update if already sliced
self.x += self.vx;
self.y += self.vy;
self.vy += self.gravity;
// Spin fruit
fruitAsset.rotation += 0.07;
// If fruit falls below screen, mark as missed
if (!self.missed && self.y > 2732 + 100) {
self.missed = true;
}
};
// Slice the fruit
self.slice = function () {
if (self.sliced) return;
self.sliced = true;
// --- JUICE SPLASH EFFECT (more visible & dramatic) ---
var splashCount = (self.fruitType === "strawberry" ? 22 : 14) + Math.floor(Math.random() * (self.fruitType === "strawberry" ? 8 : 4)); // more splashes for strawberry
for (var i = 0; i < splashCount; i++) {
// Spread splashes more evenly, but with some randomness
var baseAngle = i / splashCount * Math.PI * 2;
var angle = baseAngle + (Math.random() - 0.5) * (Math.PI / 8);
var speed = (self.fruitType === "strawberry" ? 54 : 38) + Math.random() * (self.fruitType === "strawberry" ? 38 : 32); // faster for strawberry
var splash = new Container();
// Use a colored ellipse for splash, color matches fruit
var splashColor = 0xff2d2d; // default red
if (self.fruitType === "lemon") {
splashColor = 0xfff700;
} else if (self.fruitType === "strawberry") {
splashColor = 0xff1744; // bright strawberry red
} else if (self.points > 3) {
splashColor = 0xFFE066;
} else if (self.points === 1) {
splashColor = 0xff2d2d;
} else if (self.points === 2) {
splashColor = 0xfff700;
} else if (self.points === 3) {
splashColor = 0xffa500;
} else if (self.points === 4) {
splashColor = 0x8e44ad;
} else {
splashColor = 0x2ecc40;
}
var splashAsset = splash.attachAsset('slicingTrail', {
anchorX: 0.5,
anchorY: 0.5,
width: 120 + Math.random() * 60,
// much larger
height: 32 + Math.random() * 18,
// much taller
color: splashColor,
alpha: 0.95,
// more opaque
rotation: angle
});
splash.x = self.x;
splash.y = self.y;
if (self.fruitType === "strawberry") {
splash.scaleX = 1.7 + Math.random() * 1.1;
splash.scaleY = 1.7 + Math.random() * 1.1;
splash.alpha = 1;
} else {
splash.scaleX = 1.1 + Math.random() * 0.7;
splash.scaleY = 1.1 + Math.random() * 0.7;
}
game.addChild(splash);
// Animate splash: move outward, fade out, stretch and shrink
(function (splash, splashAsset, angle, speed) {
var dx = Math.cos(angle) * speed;
var dy = Math.sin(angle) * speed;
tween(splash, {
x: splash.x + dx,
y: splash.y + dy,
alpha: 0,
scaleX: splash.scaleX * (2.2 + Math.random() * 0.7),
scaleY: splash.scaleY * (0.5 + Math.random() * 0.3)
}, {
duration: (self.fruitType === "strawberry" ? 1100 : 600) + Math.random() * (self.fruitType === "strawberry" ? 400 : 200),
// longer duration for strawberry
easing: tween.cubicOut,
onFinish: function onFinish() {
splash.destroy();
}
});
})(splash, splashAsset, angle, speed);
}
// Animate: scale up and fade out
tween(self, {
scaleX: 1.5,
scaleY: 1.5,
alpha: 0
}, {
duration: 350,
easing: tween.easeOut,
onFinish: function onFinish() {
self.destroy();
}
});
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x222a36
});
/****
* Game Code
****/
// Slicing effect asset: a white ellipse, slightly transparent, for the slicing trail
// --- MAIN MENU STATE ---
var isGameStarted = false;
// Main menu container
var mainMenu = new Container();
// Top score asset and text (centered on screen)
// Use persistent storage to get/set top score for this user
var userTopScore = storage.userTopScore || 0;
var topScoreAsset = LK.getAsset('missedX', {
anchorX: 0.5,
anchorY: 0.5,
width: 90,
height: 90,
x: 2048 / 2,
y: 2732 / 2
});
mainMenu.addChild(topScoreAsset);
var topScoreTxt = new Text2('En Yüksek Skor: ' + userTopScore, {
size: 110,
fill: "#fff",
font: "bold 900 110px Arial, 'GillSans-Bold', Impact, 'Arial Black', Tahoma"
});
topScoreTxt.anchor.set(0.5, 0.5);
// Place text below the asset, centered horizontally
topScoreTxt.x = topScoreAsset.x;
topScoreTxt.y = topScoreAsset.y + 120;
mainMenu.addChild(topScoreTxt);
// Update top score in storage and menu if needed
function updateUserTopScore(newScore) {
var prev = storage.userTopScore || 0;
if (newScore > prev) {
storage.userTopScore = newScore;
if (typeof topScoreInGameTxt !== "undefined") {
topScoreInGameTxt.setText('En Yüksek Skor: ' + newScore);
}
}
}
// --- GLOBALS ---
var fruits = [];
var bombs = [];
var missedFruits = 0;
var maxMissed = 5; // Increased from 3 to 5
// Add missedX asset for missed indicator
var comboWindow = 20; // ticks for combo
var lastSliceTick = -100;
var comboCount = 0;
var bestCombo = 0;
var backgroundIndex = 0;
var backgrounds = [{
id: 'bg1',
color: 0x222a36
}, {
id: 'bg2',
color: 0x1a3d2f
}, {
id: 'bg3',
color: 0x3d1a2f
}, {
id: 'bg4',
color: 0x1e1e2f
}];
var missedXNodes = [];
// --- MAIN MENU STATE ---
var isGameStarted = false;
// Main menu container
var mainMenu = new Container();
// Main menu background (reuse bg4)
var menuBg = LK.getAsset('bg4', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0,
width: 2048,
height: 2732
});
mainMenu.addChild(menuBg);
// "Play" button (now using 'by furkan' asset)
var playBtn = LK.getAsset('byfurkan', {
anchorX: 0.5,
anchorY: 0.5,
width: 400 * 2,
height: 180 * 2
});
playBtn.x = 2048 / 2 + 70;
playBtn.y = 2732 / 2 + 400;
mainMenu.addChild(playBtn);
var playBtnTxt = new Text2('PLAY', {
size: 120,
fill: "#fff",
font: "bold 900 120px Arial, 'GillSans-Bold', Impact, 'Arial Black', Tahoma"
});
playBtnTxt.anchor.set(0.5, 0.5);
playBtnTxt.x = playBtn.x;
playBtnTxt.y = playBtn.y;
mainMenu.addChild(playBtnTxt);
// Show main menu at start
game.addChild(mainMenu);
// Removed updateMenuTopScore function as top score text is no longer used
// --- GUI Elements ---
// Score text
var scoreTxt = new Text2('0', {
size: 120,
fill: "#fff"
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Top score (user's best) text, always visible at the top center, above the score
var topScoreInGameTxt = new Text2('En Yüksek Skor: ' + (storage.userTopScore || 0), {
size: 1040,
fill: "#000",
font: "bold 900 1040px Arial, 'GillSans-Bold', Impact, 'Arial Black', Tahoma"
});
topScoreInGameTxt.anchor.set(0.5, 0);
// Move text further to the right (e.g. +500px)
topScoreInGameTxt.x = 500; // x=0 in LK.gui.top is the center, so +500px to the right
topScoreInGameTxt.y = 0; // top edge
LK.gui.top.addChild(topScoreInGameTxt);
// Move scoreTxt a bit lower so both are visible
scoreTxt.y = topScoreInGameTxt.height + 10;
// High score (best combo) text
var bestComboTxt = new Text2('Best: 0', {
size: 120,
fill: "#000",
font: "bold 900 120px Arial, 'GillSans-Bold', Impact, 'Arial Black', Tahoma"
});
bestComboTxt.anchor.set(1, 0); // right edge, top
bestComboTxt.x = 0; // x=0 in LK.gui.topRight is the right edge
bestComboTxt.y = 0;
LK.gui.topRight.addChild(bestComboTxt);
// Missed fruits indicator (bottom left, red X's)
var missedTxt = new Text2('', {
size: 90,
fill: 0xFF4444
});
missedTxt.anchor.set(0, 1);
LK.gui.bottomLeft.addChild(missedTxt);
// Combo text (center, fades out)
var comboTxt = new Text2('', {
size: 180,
fill: 0xFFE066
});
comboTxt.anchor.set(0.5, 0.5);
comboTxt.alpha = 0;
LK.gui.center.addChild(comboTxt);
// Background switch button (bottom right)
var bgBtn = LK.getAsset('bgBtn', {
anchorX: 1,
anchorY: 1,
width: 180,
height: 100,
color: 0x8888ff,
shape: 'box'
});
bgBtn.x = 0;
bgBtn.y = 0;
LK.gui.bottomRight.addChild(bgBtn);
// Removed bgBtnTxt ('BG' text) as requested
// --- BACKGROUND ---
// Fullscreen background image node
var bgImageNode = LK.getAsset('bg4', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0,
width: 2048,
height: 2732
});
game.addChild(bgImageNode);
function setBackground(idx) {
backgroundIndex = idx % backgrounds.length;
// Always keep background image behind everything
if (bgImageNode.parent) {
game.setChildIndex(bgImageNode, 0);
}
// Optionally, you could swap image asset here if you want to support more backgrounds
game.setBackgroundColor(backgrounds[backgroundIndex].color);
}
setBackground(0);
// --- FRUIT & BOMB SPAWNING ---
function spawnFruitOrBomb() {
// Randomly decide: 80% fruit, 20% bomb
var isBomb = Math.random() < 0.2;
var obj;
// Spawn X within safe bounds
var x = 320 + Math.random() * (2048 - 640);
var y = 2732 + 80;
// Calculate angle so fruit always arcs toward center
var centerX = 2048 / 2;
var dx = centerX - x;
var minAngle = -Math.PI / 2 - Math.PI / 6; // -120deg
var maxAngle = -Math.PI / 2 + Math.PI / 6; // -60deg
// Angle toward center, with a little random spread
var baseAngle = Math.atan2(-1200, dx);
var angle = baseAngle + (Math.random() - 0.5) * (Math.PI / 12); // small random spread
// Clamp angle to safe range
if (angle < minAngle) angle = minAngle;
if (angle > maxAngle) angle = maxAngle;
var speed = 38 + Math.random() * 10;
if (isBomb) {
obj = new Bomb();
bombs.push(obj);
} else {
obj = new Fruit();
fruits.push(obj);
}
obj.x = x;
obj.y = y;
obj.vx = Math.cos(angle) * speed;
obj.vy = Math.sin(angle) * speed;
game.addChild(obj);
}
// --- GAME LOGIC ---
// Swiping: track move events, draw path, and slice
var swipePath = [];
var swipeMaxLength = 8; // Only keep last 8 points
// --- Slicing Effect ---
// Slicing effect node (a white line trail)
var slicingEffectNodes = [];
var slicingEffectVisible = false;
// Helper to clear slicing effect
function clearSlicingEffect() {
for (var i = 0; i < slicingEffectNodes.length; i++) {
if (slicingEffectNodes[i] && slicingEffectNodes[i].parent) {
slicingEffectNodes[i].parent.removeChild(slicingEffectNodes[i]);
}
}
slicingEffectNodes = [];
}
// Hide gameplay GUI until game starts
scoreTxt.visible = false;
bestComboTxt.visible = false;
missedTxt.visible = false;
comboTxt.visible = false;
bgBtn.visible = false;
// Show/hide GUI based on game state
function updateGUIVisibility() {
var show = isGameStarted;
scoreTxt.visible = show;
bestComboTxt.visible = show;
missedTxt.visible = show;
comboTxt.visible = show;
bgBtn.visible = show;
}
// Call this after Play is pressed
function startGame() {
isGameStarted = true;
mainMenu.visible = false;
updateGUIVisibility();
resetGame();
}
// Play button event
playBtn.down = function (x, y, obj) {
startGame();
};
function handleSlice(x, y) {
if (!isGameStarted) return;
// Check fruits
for (var i = fruits.length - 1; i >= 0; i--) {
var fruit = fruits[i];
if (fruit.sliced) continue;
// If swipe is close to fruit center (distance < 120)
var dx = fruit.x - x;
var dy = fruit.y - y;
if (dx * dx + dy * dy < 120 * 120) {
fruit.slice();
// Combo logic
if (lastSliceTick > 0 && LK.ticks - lastSliceTick < comboWindow) {
comboCount++;
} else {
comboCount = 1;
}
lastSliceTick = LK.ticks;
// Combo multiplier: 1x for single, 2x for double, 3x for triple or more
var comboMultiplier = 1;
if (comboCount === 2) comboMultiplier = 2;
if (comboCount >= 3) comboMultiplier = 3;
// Each fruit is worth 10 points, multiplied by combo
var pointsEarned = 10 * comboMultiplier;
LK.setScore(LK.getScore() + pointsEarned);
scoreTxt.setText(LK.getScore());
// Show combo text if comboCount >= 2
if (comboCount >= 2) {
showCombo(comboCount);
}
// Track best combo
if (typeof bestCombo === "undefined") {
bestCombo = 0;
}
if (comboCount > bestCombo) {
bestCombo = comboCount;
bestComboTxt.setText('Best: ' + bestCombo);
// Removed updateMenuTopScore call as top score text is no longer used
}
fruit.sliceTick = LK.ticks;
}
}
// Check bombs
for (var j = bombs.length - 1; j >= 0; j--) {
var bomb = bombs[j];
if (bomb.sliced) continue;
var dx = bomb.x - x;
var dy = bomb.y - y;
if (dx * dx + dy * dy < 120 * 120) {
bomb.slice();
// Game over
updateUserTopScore(LK.getScore());
if (typeof topScoreInGameTxt !== "undefined") {
var userTopScoreNow = storage.userTopScore || 0;
topScoreInGameTxt.setText('En Yüksek Skor: ' + userTopScoreNow);
}
LK.effects.flashScreen(0xff0000, 800);
LK.showGameOver();
return;
}
}
}
// Show combo text
function showCombo(count) {
if (count < 2) return;
comboTxt.setText('Combo x' + count + '!');
comboTxt.alpha = 1;
tween(comboTxt, {
alpha: 0
}, {
duration: 900,
easing: tween.linear
});
}
// Update missed fruits display
function updateMissed() {
// Remove previous X assets if any
if (typeof missedXNodes !== "undefined") {
for (var i = 0; i < missedXNodes.length; i++) {
if (missedXNodes[i] && missedXNodes[i].parent) {
missedXNodes[i].parent.removeChild(missedXNodes[i]);
}
}
}
missedXNodes = [];
// Draw up to maxMissed X's
for (var i = 0; i < maxMissed; i++) {
var xNode = LK.getAsset('missedX', {
anchorX: 0,
anchorY: 1,
x: i * 100,
// 90px width + 10px gap
y: 0,
alpha: i < missedFruits ? 1 : 0.25 // faded if not missed yet
});
LK.gui.bottomLeft.addChild(xNode);
missedXNodes.push(xNode);
}
// Hide the old text indicator
missedTxt.setText('');
}
// --- INPUT HANDLING ---
// Swiping: track move events, draw path, and slice
game.move = function (x, y, obj) {
if (!isGameStarted) return;
// Don't allow swipes in top left 100x100
if (x < 100 && y < 100) return;
// Add to swipe path
swipePath.push({
x: x,
y: y,
tick: LK.ticks
});
if (swipePath.length > swipeMaxLength) swipePath.shift();
// --- Slicing Effect: Show and update ---
slicingEffectVisible = true;
clearSlicingEffect();
// Draw lines between swipe points
for (var i = 1; i < swipePath.length; i++) {
var p0 = swipePath[i - 1];
var p1 = swipePath[i];
// Use the custom slicingTrail asset as a "segment" between points
var dx = p1.x - p0.x;
var dy = p1.y - p0.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist > 0) {
var seg = LK.getAsset('slicingTrail', {
anchorX: 0.5,
anchorY: 0.5,
width: dist,
height: 24,
x: (p0.x + p1.x) / 2,
y: (p0.y + p1.y) / 2,
rotation: Math.atan2(dy, dx),
alpha: 0.7
});
game.addChild(seg);
slicingEffectNodes.push(seg);
}
}
// For each point in swipe path, check for slicing
for (var i = 0; i < swipePath.length; i++) {
handleSlice(swipePath[i].x, swipePath[i].y);
}
};
// On down, start new swipe
game.down = function (x, y, obj) {
if (!isGameStarted) return;
swipePath = [{
x: x,
y: y,
tick: LK.ticks
}];
handleSlice(x, y);
// --- Slicing Effect: Show on down ---
slicingEffectVisible = true;
clearSlicingEffect();
};
// On up, clear swipe path
game.up = function (x, y, obj) {
if (!isGameStarted) return;
swipePath = [];
// --- Slicing Effect: Hide on up ---
slicingEffectVisible = false;
clearSlicingEffect();
};
// Background button: tap to change background
bgBtn.down = function (x, y, obj) {
setBackground((backgroundIndex + 1) % backgrounds.length);
};
// --- GAME UPDATE LOOP ---
game.update = function () {
if (!isGameStarted) {
updateGUIVisibility();
return;
}
updateGUIVisibility();
// Spawn fruits/bombs every 35-55 ticks
if (LK.ticks % (35 + Math.floor(Math.random() * 20)) === 0) {
spawnFruitOrBomb();
}
// Update fruits
for (var i = fruits.length - 1; i >= 0; i--) {
var fruit = fruits[i];
// Remove if destroyed
if (fruit.destroyed) {
fruits.splice(i, 1);
continue;
}
// If missed
if (fruit.missed && !fruit.sliced) {
fruits.splice(i, 1);
missedFruits++;
updateMissed();
LK.effects.flashObject(missedTxt, 0xff0000, 400);
// Check and update top score if needed before game over
if (missedFruits >= maxMissed) {
updateUserTopScore(LK.getScore());
if (typeof topScoreInGameTxt !== "undefined") {
var userTopScoreNow = storage.userTopScore || 0;
topScoreInGameTxt.setText('En Yüksek Skor: ' + userTopScoreNow);
}
LK.effects.flashScreen(0xff0000, 800);
LK.showGameOver();
return;
}
}
}
// Update bombs
for (var j = bombs.length - 1; j >= 0; j--) {
var bomb = bombs[j];
if (bomb.destroyed) {
bombs.splice(j, 1);
continue;
}
}
// Combo reset
if (comboCount > 1 && LK.ticks - lastSliceTick > comboWindow) {
comboCount = 0;
}
};
// --- GAME RESET HANDLING ---
// On game reset, clear all arrays and reset state
function resetGame() {
fruits = [];
bombs = [];
missedFruits = 0;
comboCount = 0;
lastSliceTick = -100;
scoreTxt.setText('0');
comboTxt.setText('');
comboTxt.alpha = 0;
updateMissed();
setBackground(0);
// --- Slicing Effect: Clear on reset ---
clearSlicingEffect();
slicingEffectVisible = false;
// On reset, update top score if needed
updateUserTopScore(LK.getScore());
if (typeof topScoreInGameTxt !== "undefined") {
var userTopScoreNow = storage.userTopScore || 0;
topScoreInGameTxt.setText('En Yüksek Skor: ' + userTopScoreNow);
}
// Only reset bestCombo if not started, otherwise keep best score
if (!isGameStarted) {
if (typeof bestCombo === "undefined") bestCombo = 0;
bestComboTxt.setText('Best: ' + bestCombo);
// Removed updateMenuTopScore call as top score text is no longer used
} else {
bestComboTxt.setText('Best: ' + (typeof bestCombo !== "undefined" ? bestCombo : 0));
// Removed updateMenuTopScore call as top score text is no longer used
}
}
resetGame();
// --- END OF GAME CODE ---
bomb. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
a chopping board with a kitchen background. In-Game asset. 2d. High contrast. No shadows
üzerinde by furkan yazan bir yazı. In-Game asset. 2d. High contrast. No shadows
double katana. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
applea. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
exploe. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
red. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
Tap to play button . No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat