/****
* 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