User prompt
Make the fruits go brighter when the fruit mover is used ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Make it so instead of changing darker when when the fruit mover is used the fruits turn whiter and bright ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Make all the fruits grey out more when the fruit mover is used ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Make it so using fruit mover turns the fruit you removed into the next fruit you drop
User prompt
Make all the power ups positioned to where they don’t overlap
User prompt
Move fruit mover to the left
User prompt
Move fruit mover to be behind randomize don’t move anything else
User prompt
Make the fruit mover and the beginning behind randomize and make it cost 125
User prompt
Add 1 last power up called “FRUIT MOVER!” Where using it makes all the fruits a little whiter and the first fruit you click will be deleted and the fruit you deleted will be you next fruit ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Move the fruit rain button a little more to the right
User prompt
Make the fruit rain the next power up after auto merge so you can see it
User prompt
Make the fruit rain button positioned like all the others
User prompt
Add another power up called “FRUIT RAIN!” That makes it rain all the lesser fruits randomly for 3 seconds ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Please fix this ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Please fix this ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Please fix this
User prompt
Make it so small movements like vibrating doesn’t count as movement and won’t make the fruits spin
User prompt
Make the fruits bounce there rotation back into place ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Make it so the speed threshold only applies when the fruit hits the ground
User prompt
Make it so if the threshold of speed is not reached the fruits will stop moving
User prompt
Make the rotation only activate once a high speed is reached
User prompt
Make the fruits stop vibrating when cramped together in a tight space
User prompt
Make it so while the fruits at touching they don’t rotate but store the speed until their not touching
User prompt
Can you make the outline white
User prompt
Please fix the bug: 'Script error.' in or related to this line: 'nameLabel.style.stroke = 0xffffff;' Line Number: 788
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var Fruit = Container.expand(function (type) {
var self = Container.call(this);
self.type = type;
self.velocityX = 0;
self.velocityY = 0;
self.angularVelocity = 0;
self.gravity = 1.2;
self.bounceX = 0.2;
self.bounceY = 0.1;
self.friction = 0.995;
self.rotationFriction = 0.85;
self.merged = false;
self.lastMergeCheck = false;
var fruitAssets = ['cherry', 'strawberry', 'grape', 'orange', 'apple', 'pear', 'peach', 'pineapple', 'melon', 'watermelon', 'lemon', 'coconut', 'dragon', 'kiwi', 'mango', 'papaya', 'durian'];
var fruitSizes = [120, 160, 200, 240, 280, 320, 360, 400, 440, 480, 520, 560, 600, 640, 580, 620, 660];
var fruitScores = [1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 66, 78, 91, 105, 120, 136, 153];
self.assetName = fruitAssets[type];
self.size = fruitSizes[type];
self.scoreValue = fruitScores[type];
var fruitGraphics = self.attachAsset(self.assetName, {
anchorX: 0.5,
anchorY: 0.5
});
self.getRadius = function () {
return self.size / 2;
};
self.update = function () {
if (typeof fruitsFrozen !== "undefined" && fruitsFrozen) {
return;
}
if (self.merged) {
return;
}
// Apply gravity
self.velocityY += self.gravity;
// Apply velocity
self.x += self.velocityX;
self.y += self.velocityY;
// Calculate current speed
var currentSpeed = Math.sqrt(self.velocityX * self.velocityX + self.velocityY * self.velocityY);
var speedThreshold = 3.0; // Only rotate when moving fast enough
// Always apply rotation
self.rotation += self.angularVelocity;
// Apply friction
self.velocityX *= self.friction;
self.velocityY *= self.friction;
self.angularVelocity *= self.rotationFriction;
// Count nearby fruits for stability calculations
var nearbyFruits = 0;
var minProximity = self.getRadius() * 2.5; // Within 2.5 radii
for (var f = 0; f < fruits.length; f++) {
if (fruits[f] !== self && !fruits[f].merged) {
var dx = self.x - fruits[f].x;
var dy = self.y - fruits[f].y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < minProximity) {
nearbyFruits++;
}
}
}
// Enhanced stability for cramped conditions
var totalSpeed = Math.sqrt(self.velocityX * self.velocityX + self.velocityY * self.velocityY);
var crampedCondition = nearbyFruits >= 3; // 3 or more nearby fruits = cramped
if (crampedCondition) {
// Much stronger damping when cramped
var crampedDamping = 0.6;
self.velocityX *= crampedDamping;
self.velocityY *= crampedDamping;
self.angularVelocity *= 0.4; // Extra rotational damping when cramped
} else if (totalSpeed < 1.0 && totalSpeed > 0) {
var stabilityDamping = 0.8;
self.velocityX *= stabilityDamping;
self.velocityY *= stabilityDamping;
}
// Cap maximum rotation speed - stricter limits when cramped
var maxAngularVelocity = crampedCondition ? 0.05 : 0.2;
if (self.angularVelocity > maxAngularVelocity) {
self.angularVelocity = maxAngularVelocity;
} else if (self.angularVelocity < -maxAngularVelocity) {
self.angularVelocity = -maxAngularVelocity;
}
// Prevent very small velocities that can cause sticking - increased threshold
if (Math.abs(self.velocityX) < 0.5) {
self.velocityX = 0;
}
if (Math.abs(self.velocityY) < 0.5) {
self.velocityY = 0;
}
if (Math.abs(self.angularVelocity) < 0.005) {
self.angularVelocity = 0;
// Bounce rotation back to neutral position when angular velocity stops
if (Math.abs(self.rotation) > 0.1) {
// Use shorter, gentler animation for cramped conditions
var animationDuration = crampedCondition ? 400 : 600;
var animationEasing = crampedCondition ? tween.easeOut : tween.bounceOut;
tween(self, {
rotation: 0
}, {
duration: animationDuration,
easing: animationEasing
});
}
}
var radius = self.getRadius();
var containerLeft = containerX - containerWidth / 2;
var containerRight = containerX + containerWidth / 2;
var containerBottom = containerY + containerHeight / 2;
// Collision with container walls
if (self.x - radius < containerLeft) {
self.x = containerLeft + radius;
self.velocityX = -self.velocityX * self.bounceX;
// Add rotational effect from wall collision only if movement is significant
if (Math.abs(self.velocityY) > 2.0) {
self.angularVelocity += self.velocityY * 0.01;
}
}
if (self.x + radius > containerRight) {
self.x = containerRight - radius;
self.velocityX = -self.velocityX * self.bounceX;
// Add rotational effect from wall collision only if movement is significant
if (Math.abs(self.velocityY) > 2.0) {
self.angularVelocity -= self.velocityY * 0.01;
}
}
// Collision with container bottom
if (self.y + radius > containerBottom) {
self.y = containerBottom - radius;
self.velocityY = -self.velocityY * self.bounceY;
// Add rotational effect from ground collision only if movement is significant
if (Math.abs(self.velocityX) > 2.0) {
self.angularVelocity += self.velocityX * 0.02;
}
if (Math.abs(self.velocityY) < 1) {
self.velocityY = 0;
}
// After ground collision, check speed threshold
var currentSpeed = Math.sqrt(self.velocityX * self.velocityX + self.velocityY * self.velocityY);
var speedThreshold = 3.0;
if (currentSpeed < speedThreshold) {
// Stop movement if below speed threshold after hitting ground
self.velocityX = 0;
self.velocityY = 0;
self.angularVelocity = 0;
// Bounce rotation back to neutral position
if (Math.abs(self.rotation) > 0.1) {
tween(self, {
rotation: 0
}, {
duration: 800,
easing: tween.elasticOut
});
}
}
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
// No backgroundColor, handled by asset
});
/****
* Game Code
****/
// Attach blue background asset, ensure it's at the back
// Blue background asset (full screen)
var blueBg = game.attachAsset('blueBg', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0,
width: 2048,
height: 2732
});
game.addChildAt(blueBg, 0); // Add as the bottom-most child
// Game state
var gameState = 'playing';
var fruits = [];
var currentFruit = null;
var nextFruitType = 0;
var gameRunning = true;
var dropLine = 400;
var gameOverLine = 300;
var isDragging = false;
var previewFruit = null;
// Freeze fruits when evolution menu is open
var fruitsFrozen = false;
// Track which fruits have been discovered by the player
var discoveredFruits = storage.discoveredFruits || [];
// Initialize array if not in storage
if (discoveredFruits.length === 0) {
for (var i = 0; i < 17; i++) {
discoveredFruits[i] = false;
}
}
// Container dimensions and position
var containerWidth = 1900;
var containerHeight = 2200;
var containerX = 2048 / 2;
var containerY = 1366;
// Create container
var container = game.attachAsset('container', {
anchorX: 0.5,
anchorY: 0.5,
x: containerX,
y: containerY
});
// Score display
var scoreTxt = new Text2('Score: 0', {
size: 80,
fill: 0x000000
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Merge counter (reset to 0 every game start)
var totalMerges = 0;
var mergeCounterTxt = new Text2('Merges: ' + totalMerges, {
size: 60,
fill: 0x000000
});
mergeCounterTxt.anchor.set(0.5, 0.5);
mergeCounterTxt.x = 2048 / 2;
mergeCounterTxt.y = 2732 / 2;
game.addChild(mergeCounterTxt);
// Load discovered fruits from storage, but ensure first 5 are always available
for (var i = 0; i < discoveredFruits.length; i++) {
if (i < 5) {
discoveredFruits[i] = true; // First 5 are always available to drop
} else if (storage.discoveredFruits && storage.discoveredFruits[i]) {
discoveredFruits[i] = true; // Keep unlocked fruits from storage
}
}
// Save to storage
storage.discoveredFruits = discoveredFruits;
// Next fruit preview
var nextFruitPreview = null;
var nextFruitLabel = new Text2('Next:', {
size: 60,
fill: 0x000000
});
nextFruitLabel.anchor.set(1, 0.5);
nextFruitLabel.x = 1580;
nextFruitLabel.y = 120;
game.addChild(nextFruitLabel);
// Drop line indicator
var dropLineIndicator = LK.getAsset('container', {
width: containerWidth,
height: 4,
anchorX: 0.5,
anchorY: 0.5
});
dropLineIndicator.x = containerX;
dropLineIndicator.y = dropLine;
dropLineIndicator.tint = 0x0000FF;
game.addChild(dropLineIndicator);
// Helper functions
function getRandomFruitType() {
return Math.floor(Math.random() * 5); // Only first 5 fruit types can be dropped
}
function createNextFruit() {
nextFruitType = getRandomFruitType();
if (nextFruitPreview) {
// Stop any existing tweens before destroying
tween.stop(nextFruitPreview);
nextFruitPreview.destroy();
}
var fruitAssets = ['cherry', 'strawberry', 'grape', 'orange', 'apple'];
var fruitSizes = [120, 160, 200, 240, 280];
nextFruitPreview = LK.getAsset(fruitAssets[nextFruitType], {
anchorX: 0.5,
anchorY: 0.5
});
nextFruitPreview.x = 1700;
nextFruitPreview.y = 120;
game.addChild(nextFruitPreview);
// Position the label based on fruit size to ensure it's always visible
var fruitRadius = fruitSizes[nextFruitType] / 2;
nextFruitLabel.x = nextFruitPreview.x - fruitRadius - 20; // 20px padding from fruit edge
// Add bobbing animation
var originalY = nextFruitPreview.y;
function bobUp() {
tween(nextFruitPreview, {
y: originalY - 15
}, {
duration: 1000,
easing: tween.easeInOut,
onFinish: bobDown
});
}
function bobDown() {
tween(nextFruitPreview, {
y: originalY + 15
}, {
duration: 1000,
easing: tween.easeInOut,
onFinish: bobUp
});
}
bobUp();
}
function dropFruit(x) {
if (!gameRunning || currentFruit) {
return;
}
currentFruit = new Fruit(nextFruitType);
currentFruit.x = Math.max(containerX - containerWidth / 2 + currentFruit.getRadius(), Math.min(x, containerX + containerWidth / 2 - currentFruit.getRadius()));
currentFruit.y = dropLine + currentFruit.getRadius() + 60; // Spawn lower below the line with more padding
// Mark this fruit as discovered
var newlyDiscovered = !discoveredFruits[nextFruitType];
discoveredFruits[nextFruitType] = true;
// Save to storage
storage.discoveredFruits = discoveredFruits;
if (newlyDiscovered) {
// Show notification for unlocking new fruit
var evoFruitNames = ['Cherry', 'Strawberry', 'Grape', 'Orange', 'Apple', 'Pear', 'Peach', 'Pineapple', 'Melon', 'Watermelon', 'Lemon', 'Coconut', 'Dragonfruit', 'Kiwi', 'Mango', 'Papaya', 'Durian'];
var unlockedName = evoFruitNames[nextFruitType];
var notification = new Text2("You've unlocked " + unlockedName + "!", {
size: 90,
fill: 0x222222
});
notification.anchor.set(0.5, 0.5);
notification.x = 2048 / 2;
notification.y = 900;
notification.alpha = 0.0;
game.addChild(notification);
// Fade in, hold, then fade out
tween(notification, {
alpha: 1
}, {
duration: 300,
easing: tween.easeIn,
onFinish: function onFinish() {
LK.setTimeout(function () {
tween(notification, {
alpha: 0
}, {
duration: 500,
easing: tween.easeOut,
onFinish: function onFinish() {
notification.destroy();
}
});
}, 1200);
}
});
}
// Add small random initial rotation
currentFruit.angularVelocity = (Math.random() - 0.5) * 0.05;
fruits.push(currentFruit);
game.addChild(currentFruit);
// Music and drop sound now start on tap/hold instead of drop
// Reset current fruit after a delay
LK.setTimeout(function () {
currentFruit = null;
}, 100);
createNextFruit();
}
function createPreviewFruit(x) {
if (previewFruit) {
previewFruit.destroy();
}
var fruitAssets = ['cherry', 'strawberry', 'grape', 'orange', 'apple', 'pear', 'peach', 'pineapple', 'melon', 'watermelon', 'lemon', 'coconut', 'dragon', 'kiwi', 'mango', 'papaya', 'durian'];
previewFruit = LK.getAsset(fruitAssets[nextFruitType], {
anchorX: 0.5,
anchorY: 0.5
});
previewFruit.alpha = 0.7; // Make it semi-transparent
previewFruit.x = x;
previewFruit.y = dropLine;
game.addChild(previewFruit);
}
function updatePreviewFruit(x) {
if (!previewFruit) {
return;
}
var fruitSizes = [120, 160, 200, 240, 280, 320, 360, 400, 440, 480, 520, 560, 600, 640, 580, 620, 660];
var radius = fruitSizes[nextFruitType] / 2;
previewFruit.x = Math.max(containerX - containerWidth / 2 + radius, Math.min(x, containerX + containerWidth / 2 - radius));
}
function destroyPreviewFruit() {
if (previewFruit) {
previewFruit.destroy();
previewFruit = null;
}
}
function checkCollision(fruit1, fruit2) {
var dx = fruit1.x - fruit2.x;
var dy = fruit1.y - fruit2.y;
var distance = Math.sqrt(dx * dx + dy * dy);
var minDistance = fruit1.getRadius() + fruit2.getRadius();
return distance < minDistance;
}
function resolveFruitCollision(fruit1, fruit2) {
var dx = fruit1.x - fruit2.x;
var dy = fruit1.y - fruit2.y;
var distance = Math.sqrt(dx * dx + dy * dy);
var minDistance = fruit1.getRadius() + fruit2.getRadius();
if (distance < minDistance && distance > 0) {
var overlap = minDistance - distance;
// Reduced separation force to prevent oscillation
var separationForce = 0.6;
var separationX = dx / distance * overlap * separationForce;
var separationY = dy / distance * overlap * separationForce;
fruit1.x += separationX;
fruit1.y += separationY;
fruit2.x -= separationX;
fruit2.y -= separationY;
// Apply strong damping when fruits are very close to prevent vibration
var dampingFactor = Math.max(0.1, Math.min(1.0, distance / minDistance));
fruit1.velocityX *= dampingFactor;
fruit1.velocityY *= dampingFactor;
fruit2.velocityX *= dampingFactor;
fruit2.velocityY *= dampingFactor;
// Realistic elastic collision response
var relativeVelX = fruit1.velocityX - fruit2.velocityX;
var relativeVelY = fruit1.velocityY - fruit2.velocityY;
var normalVelX = dx / distance;
var normalVelY = dy / distance;
var separatingVel = relativeVelX * normalVelX + relativeVelY * normalVelY;
// Only resolve if objects are moving toward each other with sufficient velocity
if (separatingVel < -0.5) {
var restitution = 0.05; // Reduced bounciness factor
var impulse = -(1 + restitution) * separatingVel * 0.5; // Reduced impulse strength
var impulseX = impulse * normalVelX;
var impulseY = impulse * normalVelY;
fruit1.velocityX += impulseX;
fruit1.velocityY += impulseY;
fruit2.velocityX -= impulseX;
fruit2.velocityY -= impulseY;
// Add rotational effects from collision only if movement is significant
var movementThreshold = 2.0; // Minimum movement to trigger rotation
var totalImpulse = Math.abs(impulseX) + Math.abs(impulseY);
// Count nearby fruits for both fruits to determine cramped conditions
var fruit1Nearby = 0;
var fruit2Nearby = 0;
var proximityCheck = Math.max(fruit1.getRadius(), fruit2.getRadius()) * 2.5;
for (var k = 0; k < fruits.length; k++) {
if (fruits[k] !== fruit1 && fruits[k] !== fruit2 && !fruits[k].merged) {
var dx1 = fruit1.x - fruits[k].x;
var dy1 = fruit1.y - fruits[k].y;
var distance1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
if (distance1 < proximityCheck) fruit1Nearby++;
var dx2 = fruit2.x - fruits[k].x;
var dy2 = fruit2.y - fruits[k].y;
var distance2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
if (distance2 < proximityCheck) fruit2Nearby++;
}
}
if (totalImpulse > movementThreshold) {
// Dramatically reduce rotation in cramped conditions
var baseRotationFactor = 0.015;
var fruit1RotationFactor = fruit1Nearby >= 2 ? baseRotationFactor * 0.1 : baseRotationFactor;
var fruit2RotationFactor = fruit2Nearby >= 2 ? baseRotationFactor * 0.1 : baseRotationFactor;
fruit1.angularVelocity += (impulseX + impulseY) * fruit1RotationFactor;
fruit2.angularVelocity -= (impulseX + impulseY) * fruit2RotationFactor;
}
}
}
}
function mergeFruits(fruit1, fruit2) {
if (fruit1.type !== fruit2.type || fruit1.merged || fruit2.merged) {
return false;
}
if (fruit1.type >= 16) {
return false;
} // Can't merge durian (highest fruit)
// Mark the new fruit type as discovered
var newlyDiscovered = !discoveredFruits[fruit1.type + 1];
discoveredFruits[fruit1.type + 1] = true;
// Save to storage
storage.discoveredFruits = discoveredFruits;
if (newlyDiscovered) {
// Show notification for unlocking new fruit
var evoFruitNames = ['Cherry', 'Strawberry', 'Grape', 'Orange', 'Apple', 'Pear', 'Peach', 'Pineapple', 'Melon', 'Watermelon', 'Lemon', 'Coconut', 'Dragonfruit', 'Kiwi', 'Mango', 'Papaya', 'Durian'];
var unlockedName = evoFruitNames[fruit1.type + 1];
var notification = new Text2("You've unlocked " + unlockedName + "!", {
size: 90,
fill: 0x222222
});
notification.anchor.set(0.5, 0.5);
notification.x = 2048 / 2;
notification.y = 900;
notification.alpha = 0.0;
game.addChild(notification);
// Fade in, hold, then fade out
tween(notification, {
alpha: 1
}, {
duration: 300,
easing: tween.easeIn,
onFinish: function onFinish() {
LK.setTimeout(function () {
tween(notification, {
alpha: 0
}, {
duration: 500,
easing: tween.easeOut,
onFinish: function onFinish() {
notification.destroy();
}
});
}, 1200);
}
});
}
// Create new fruit
var newFruit = new Fruit(fruit1.type + 1);
newFruit.x = (fruit1.x + fruit2.x) / 2;
newFruit.y = (fruit1.y + fruit2.y) / 2;
// Add score
LK.setScore(LK.getScore() + newFruit.scoreValue);
scoreTxt.setText('Score: ' + LK.getScore());
// Update merge counter
totalMerges++;
mergeCounterTxt.setText('Merges: ' + totalMerges);
storage.totalMerges = totalMerges;
// Mark old fruits as merged
fruit1.merged = true;
fruit2.merged = true;
// Remove old fruits
for (var i = fruits.length - 1; i >= 0; i--) {
if (fruits[i] === fruit1 || fruits[i] === fruit2) {
fruits[i].destroy();
fruits.splice(i, 1);
}
}
// Add new fruit
fruits.push(newFruit);
game.addChild(newFruit);
LK.getSound('merge').play();
// Flash effect
tween(newFruit, {
scaleX: 1.3,
scaleY: 1.3
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(newFruit, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 200,
easing: tween.easeIn
});
}
});
return true;
}
function performAutoMerge() {
var foundMerge = true;
// Keep merging until no more merges are possible
while (foundMerge) {
foundMerge = false;
// Check all fruit pairs for possible merges
for (var i = 0; i < fruits.length && !foundMerge; i++) {
for (var j = i + 1; j < fruits.length && !foundMerge; j++) {
var fruit1 = fruits[i];
var fruit2 = fruits[j];
// Skip if either fruit is already merged or different types
if (fruit1.merged || fruit2.merged || fruit1.type !== fruit2.type) {
continue;
}
// Skip if already at maximum fruit type (durian)
if (fruit1.type >= 16) {
continue;
}
// Auto merge merges ANY two fruits of the same type regardless of distance
// Perform the merge immediately
if (mergeFruits(fruit1, fruit2)) {
foundMerge = true;
break;
}
}
}
}
}
function checkGameOver() {
for (var i = 0; i < fruits.length; i++) {
var fruit = fruits[i];
if (fruit.y - fruit.getRadius() < gameOverLine) {
return true;
}
}
return false;
}
// Shake button
var shakeButton = new Text2('SHAKE!', {
size: 60,
fill: 0x000000
});
shakeButton.anchor.set(0.5, 0.5);
// Place at bottom middle, above the very bottom edge (e.g. 150px up)
shakeButton.x = 2048 / 2;
shakeButton.y = 2732 - 150;
game.addChild(shakeButton);
// Cost text under shake button
var shakeCostText = new Text2('Cost: 450', {
size: 40,
fill: 0x000000
});
shakeCostText.anchor.set(0.5, 0);
// Position just below the shake button (e.g. 20px below)
shakeCostText.x = shakeButton.x;
shakeCostText.y = shakeButton.y + 60;
game.addChild(shakeCostText);
// Randomize fruit button
var randomizeButton = new Text2('RANDOMIZE!', {
size: 60,
fill: 0x000000
});
randomizeButton.anchor.set(0.5, 0.5);
// Place to the left of shake button
randomizeButton.x = 2048 / 2 - 350;
randomizeButton.y = 2732 - 150;
game.addChild(randomizeButton);
// Cost text under randomize button
var randomizeCostText = new Text2('Cost: 300', {
size: 40,
fill: 0x000000
});
randomizeCostText.anchor.set(0.5, 0);
// Position just below the randomize button
randomizeCostText.x = randomizeButton.x;
randomizeCostText.y = randomizeButton.y + 60;
game.addChild(randomizeCostText);
// Auto merge button
var autoMergeButton = new Text2('AUTO MERGE!', {
size: 60,
fill: 0x000000
});
autoMergeButton.anchor.set(0.5, 0.5);
// Place to the right of shake button
autoMergeButton.x = 2048 / 2 + 350;
autoMergeButton.y = 2732 - 150;
game.addChild(autoMergeButton);
// Cost text under auto merge button
var autoMergeCostText = new Text2('Cost: 800', {
size: 40,
fill: 0x000000
});
autoMergeCostText.anchor.set(0.5, 0);
// Position just below the auto merge button
autoMergeCostText.x = autoMergeButton.x;
autoMergeCostText.y = autoMergeButton.y + 60;
game.addChild(autoMergeCostText);
// Add auto merge button click handler
autoMergeButton.down = function (x, y, obj) {
if (LK.getScore() >= 800 && fruits.length > 1) {
// Deduct score
LK.setScore(LK.getScore() - 800);
scoreTxt.setText('Score: ' + LK.getScore());
// Perform auto merge
performAutoMerge();
}
};
// Add randomize button click handler
randomizeButton.down = function (x, y, obj) {
if (LK.getScore() >= 300 && fruits.length > 1) {
// Deduct score
LK.setScore(LK.getScore() - 300);
scoreTxt.setText('Score: ' + LK.getScore());
// Store all current fruit positions
var positions = [];
for (var i = 0; i < fruits.length; i++) {
positions.push({
x: fruits[i].x,
y: fruits[i].y
});
}
// Shuffle the positions array
for (var i = positions.length - 1; i > 0; i--) {
var j = Math.floor(Math.random() * (i + 1));
var temp = positions[i];
positions[i] = positions[j];
positions[j] = temp;
}
// Apply shuffled positions to fruits
for (var i = 0; i < fruits.length; i++) {
fruits[i].x = positions[i].x;
fruits[i].y = positions[i].y;
// Reset velocities to prevent physics issues
fruits[i].velocityX = 0;
fruits[i].velocityY = 0;
fruits[i].angularVelocity = 0;
}
}
};
// Add shake button click handler
shakeButton.down = function (x, y, obj) {
if (LK.getScore() >= 450) {
// Deduct score
LK.setScore(LK.getScore() - 450);
scoreTxt.setText('Score: ' + LK.getScore());
// Move all fruits to random positions within container bounds
for (var i = 0; i < fruits.length; i++) {
var fruit = fruits[i];
var radius = fruit.getRadius();
var containerLeft = containerX - containerWidth / 2;
var containerRight = containerX + containerWidth / 2;
var containerTop = containerY - containerHeight / 2;
var containerBottom = containerY + containerHeight / 2;
// Calculate valid random position within container bounds
var randomX = containerLeft + radius + Math.random() * (containerRight - containerLeft - 2 * radius);
var randomY = containerTop + radius + Math.random() * (containerBottom - containerTop - 2 * radius);
// Animate fruit to new position with tween
tween(fruit, {
x: randomX,
y: randomY
}, {
duration: 500 + Math.random() * 500,
// Random duration between 500-1000ms
easing: tween.easeOut
});
// Reset velocities to prevent physics issues
fruit.velocityX = 0;
fruit.velocityY = 0;
fruit.angularVelocity = 0;
}
}
};
// Do not spawn a fruit at start; only spawn on click/hold
createNextFruit();
// --- Evolution Button and Menu ---
// Evolution button
var evolutionButton = new Text2('EVOLUTION', {
size: 60,
fill: 0x000000
});
evolutionButton.anchor.set(0.5, 0);
// Place directly under the score display, using LK.gui for vertical stacking
evolutionButton.y = scoreTxt.height + 20;
LK.gui.top.addChild(evolutionButton);
// Evolution menu container (hidden by default)
var evolutionMenu = new Container();
evolutionMenu.visible = false;
// Add evolution menu to the front (highest z-index)
game.addChild(evolutionMenu);
// Semi-transparent background for menu
var menuBg = LK.getAsset('container', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 2732 / 2,
width: 1400,
height: 1400
});
menuBg.alpha = 0.92;
menuBg.tint = 0xeeeeee;
evolutionMenu.addChild(menuBg);
// Title
var evolutionTitle = new Text2('Fruit Evolution', {
size: 90,
fill: 0x222222,
stroke: 0xffffff,
strokeThickness: 4
});
evolutionTitle.anchor.set(0.5, 0);
evolutionTitle.x = 2048 / 2;
evolutionTitle.y = 2732 / 2 - 650;
evolutionMenu.addChild(evolutionTitle);
// Fruit evolution data
var evoFruitAssets = ['cherry', 'strawberry', 'grape', 'orange', 'apple', 'pear', 'peach', 'pineapple', 'melon', 'watermelon', 'lemon', 'coconut', 'dragon', 'kiwi', 'mango', 'papaya', 'durian'];
var evoFruitNames = ['Cherry', 'Strawberry', 'Grape', 'Orange', 'Apple', 'Pear', 'Peach', 'Pineapple', 'Melon', 'Watermelon', 'Lemon', 'Coconut', 'Dragonfruit', 'Kiwi', 'Mango', 'Papaya', 'Durian'];
var evoFruitSizes = [120, 160, 200, 240, 280, 320, 360, 400, 440, 480, 520, 560, 600, 640, 580, 620, 660];
// Draw fruits in a circle
var centerX = 2048 / 2;
var centerY = 2732 / 2 + 80;
var radius = 500;
for (var i = 0; i < evoFruitAssets.length; i++) {
var angle = Math.PI * 2 / evoFruitAssets.length * i - Math.PI / 2;
var fruitAsset = LK.getAsset(evoFruitAssets[i], {
anchorX: 0.5,
anchorY: 0.5
});
// Make all fruits the same size as the strawberry (160x160)
var strawberrySize = 160;
var fruitRadius = strawberrySize / 2;
fruitAsset.width = strawberrySize;
fruitAsset.height = strawberrySize;
fruitAsset.x = centerX + Math.cos(angle) * radius;
fruitAsset.y = centerY + Math.sin(angle) * radius;
// Black out and hide text for undiscovered fruits
if (!discoveredFruits[i]) {
fruitAsset.tint = 0x222222;
fruitAsset.alpha = 0.7;
}
evolutionMenu.addChild(fruitAsset);
// Fruit name label
var nameLabel = new Text2(discoveredFruits[i] ? evoFruitNames[i] : '', {
size: 38,
fill: 0x333333,
stroke: 0xffffff,
strokeThickness: 3
});
nameLabel.anchor.set(0.5, 1);
nameLabel.x = fruitAsset.x;
nameLabel.y = fruitAsset.y - fruitRadius - 10;
evolutionMenu.addChild(nameLabel);
// Draw arrow to next fruit (except last)
if (i < evoFruitAssets.length - 1) {
var nextAngle = Math.PI * 2 / evoFruitAssets.length * (i + 1) - Math.PI / 2;
var arrowX1 = fruitAsset.x + Math.cos(angle) * (fruitRadius + 20);
var arrowY1 = fruitAsset.y + Math.sin(angle) * (fruitRadius + 20);
var arrowX2 = centerX + Math.cos(nextAngle) * (radius - 40);
var arrowY2 = centerY + Math.sin(nextAngle) * (radius - 40);
// Use a small fruit as an arrow "dot"
var arrowDot = LK.getAsset('nextFruit', {
anchorX: 0.5,
anchorY: 0.5,
x: (arrowX1 + arrowX2) / 2,
y: (arrowY1 + arrowY2) / 2,
width: 32,
height: 32
});
arrowDot.tint = 0xaaaaaa;
evolutionMenu.addChild(arrowDot);
}
}
// Show menu on button press
evolutionButton.down = function (x, y, obj) {
// If menu is open, close it
if (evolutionMenu.visible) {
evolutionMenu.visible = false;
gameRunning = true;
fruitsFrozen = false;
return;
}
// Otherwise, update and open the menu
var childIdx = 0;
for (var i = 0; i < evoFruitAssets.length; i++) {
// Fruit asset is first, label is second, then arrow dot (if not last)
var fruitAsset = evolutionMenu.children[childIdx + 2]; // skip bg and title
var nameLabel = evolutionMenu.children[childIdx + 3];
if (!discoveredFruits[i]) {
fruitAsset.tint = 0x222222;
fruitAsset.alpha = 0.7;
nameLabel.setText('');
} else {
fruitAsset.tint = 0xffffff;
fruitAsset.alpha = 1.0;
nameLabel.setText(evoFruitNames[i]);
// Text2 stroke properties are set directly, not through style object
nameLabel.stroke = 0xffffff;
nameLabel.strokeThickness = 3;
}
childIdx += 2;
if (i < evoFruitAssets.length - 1) childIdx++; // skip arrow dot
}
evolutionMenu.visible = true;
// Move evolution menu to front to ensure it's on top of all game elements
game.removeChild(evolutionMenu);
game.addChild(evolutionMenu);
// Optionally, darken background or disable gameplay while menu is open
gameRunning = false;
fruitsFrozen = true;
};
// Music will start when first fruit is dropped
var musicStarted = false;
// Event handlers
game.down = function (x, y, obj) {
if (!gameRunning || currentFruit) {
return;
}
// Only allow spawning if the press is inside the container bounds
var radius = 0;
if (x >= containerX - containerWidth / 2 + radius && x <= containerX + containerWidth / 2 - radius && y >= containerY - containerHeight / 2 + radius && y <= containerY + containerHeight / 2 - radius) {
isDragging = true;
createPreviewFruit(x);
// Start background music on first tap/hold if not already playing
if (!musicStarted) {
LK.playMusic('bgmusic');
musicStarted = true;
}
// Play drop sound when starting to aim
LK.getSound('drop').play();
}
};
game.move = function (x, y, obj) {
if (!gameRunning || !isDragging) {
return;
}
updatePreviewFruit(x);
};
game.up = function (x, y, obj) {
if (!gameRunning || !isDragging) {
return;
}
isDragging = false;
var dropX = previewFruit ? previewFruit.x : x;
destroyPreviewFruit();
dropFruit(dropX);
};
// Main game loop
game.update = function () {
if (!gameRunning) {
return;
}
// Update all fruits
if (!fruitsFrozen) {
for (var i = 0; i < fruits.length; i++) {
fruits[i].update();
}
}
// Check collisions and merges
for (var i = 0; i < fruits.length; i++) {
for (var j = i + 1; j < fruits.length; j++) {
var fruit1 = fruits[i];
var fruit2 = fruits[j];
if (fruit1.merged || fruit2.merged) {
continue;
}
if (checkCollision(fruit1, fruit2)) {
// Always apply physics first to prevent sticking
resolveFruitCollision(fruit1, fruit2);
// Then check for merge
var currentMerging = fruit1.type === fruit2.type;
if (!fruit1.lastMergeCheck && currentMerging) {
if (mergeFruits(fruit1, fruit2)) {
break; // Break out of inner loop as fruits array changed
}
}
fruit1.lastMergeCheck = currentMerging;
fruit2.lastMergeCheck = currentMerging;
} else {
fruit1.lastMergeCheck = false;
fruit2.lastMergeCheck = false;
}
}
}
// Check game over condition
if (checkGameOver() && LK.ticks % 60 === 0) {
// Check every second
gameRunning = false;
LK.showGameOver();
}
}; ===================================================================
--- original.js
+++ change.js
@@ -53,33 +53,37 @@
// Apply friction
self.velocityX *= self.friction;
self.velocityY *= self.friction;
self.angularVelocity *= self.rotationFriction;
- // Additional stability check - if fruit is moving very slowly, gradually bring it to rest
+ // Count nearby fruits for stability calculations
+ var nearbyFruits = 0;
+ var minProximity = self.getRadius() * 2.5; // Within 2.5 radii
+ for (var f = 0; f < fruits.length; f++) {
+ if (fruits[f] !== self && !fruits[f].merged) {
+ var dx = self.x - fruits[f].x;
+ var dy = self.y - fruits[f].y;
+ var distance = Math.sqrt(dx * dx + dy * dy);
+ if (distance < minProximity) {
+ nearbyFruits++;
+ }
+ }
+ }
+ // Enhanced stability for cramped conditions
var totalSpeed = Math.sqrt(self.velocityX * self.velocityX + self.velocityY * self.velocityY);
- if (totalSpeed < 1.0 && totalSpeed > 0) {
+ var crampedCondition = nearbyFruits >= 3; // 3 or more nearby fruits = cramped
+ if (crampedCondition) {
+ // Much stronger damping when cramped
+ var crampedDamping = 0.6;
+ self.velocityX *= crampedDamping;
+ self.velocityY *= crampedDamping;
+ self.angularVelocity *= 0.4; // Extra rotational damping when cramped
+ } else if (totalSpeed < 1.0 && totalSpeed > 0) {
var stabilityDamping = 0.8;
self.velocityX *= stabilityDamping;
self.velocityY *= stabilityDamping;
}
- // Check if fruit is in cramped conditions
- var nearbyFruits = 0;
- if (typeof fruits !== "undefined") {
- for (var k = 0; k < fruits.length; k++) {
- if (fruits[k] !== self) {
- var dist = Math.sqrt((self.x - fruits[k].x) * (self.x - fruits[k].x) + (self.y - fruits[k].y) * (self.y - fruits[k].y));
- if (dist < self.getRadius() + fruits[k].getRadius() + 30) {
- nearbyFruits++;
- }
- }
- }
- }
- // Apply stronger rotational damping in cramped conditions
- if (nearbyFruits >= 3) {
- self.angularVelocity *= 0.6; // Much stronger damping when cramped
- }
- // Cap maximum rotation speed - lower limit for cramped conditions
- var maxAngularVelocity = nearbyFruits >= 3 ? 0.05 : 0.1; // Reduced max rotation
+ // Cap maximum rotation speed - stricter limits when cramped
+ var maxAngularVelocity = crampedCondition ? 0.05 : 0.2;
if (self.angularVelocity > maxAngularVelocity) {
self.angularVelocity = maxAngularVelocity;
} else if (self.angularVelocity < -maxAngularVelocity) {
self.angularVelocity = -maxAngularVelocity;
@@ -90,20 +94,20 @@
}
if (Math.abs(self.velocityY) < 0.5) {
self.velocityY = 0;
}
- // Higher threshold for stopping rotation in cramped conditions
- var rotationStopThreshold = nearbyFruits >= 3 ? 0.02 : 0.005;
- if (Math.abs(self.angularVelocity) < rotationStopThreshold) {
+ if (Math.abs(self.angularVelocity) < 0.005) {
self.angularVelocity = 0;
// Bounce rotation back to neutral position when angular velocity stops
if (Math.abs(self.rotation) > 0.1) {
+ // Use shorter, gentler animation for cramped conditions
+ var animationDuration = crampedCondition ? 400 : 600;
+ var animationEasing = crampedCondition ? tween.easeOut : tween.bounceOut;
tween(self, {
rotation: 0
}, {
- duration: nearbyFruits >= 3 ? 400 : 600,
- // Faster correction when cramped
- easing: tween.bounceOut
+ duration: animationDuration,
+ easing: animationEasing
});
}
}
var radius = self.getRadius();
@@ -170,10 +174,10 @@
/****
* Game Code
****/
-// Blue background asset (full screen)
// Attach blue background asset, ensure it's at the back
+// Blue background asset (full screen)
var blueBg = game.attachAsset('blueBg', {
anchorX: 0,
anchorY: 0,
x: 0,
@@ -435,27 +439,34 @@
fruit1.velocityX += impulseX;
fruit1.velocityY += impulseY;
fruit2.velocityX -= impulseX;
fruit2.velocityY -= impulseY;
- // Add rotational effects from collision only if movement is significant and not in cramped conditions
- var movementThreshold = 5.0; // Increased threshold to prevent micro-rotation
+ // Add rotational effects from collision only if movement is significant
+ var movementThreshold = 2.0; // Minimum movement to trigger rotation
var totalImpulse = Math.abs(impulseX) + Math.abs(impulseY);
- // Check if fruits are in cramped conditions by counting nearby fruits
- var nearbyCount1 = 0;
- var nearbyCount2 = 0;
+ // Count nearby fruits for both fruits to determine cramped conditions
+ var fruit1Nearby = 0;
+ var fruit2Nearby = 0;
+ var proximityCheck = Math.max(fruit1.getRadius(), fruit2.getRadius()) * 2.5;
for (var k = 0; k < fruits.length; k++) {
- if (fruits[k] !== fruit1 && fruits[k] !== fruit2) {
- var dist1 = Math.sqrt((fruit1.x - fruits[k].x) * (fruit1.x - fruits[k].x) + (fruit1.y - fruits[k].y) * (fruit1.y - fruits[k].y));
- var dist2 = Math.sqrt((fruit2.x - fruits[k].x) * (fruit2.x - fruits[k].x) + (fruit2.y - fruits[k].y) * (fruit2.y - fruits[k].y));
- if (dist1 < fruit1.getRadius() + fruits[k].getRadius() + 50) nearbyCount1++;
- if (dist2 < fruit2.getRadius() + fruits[k].getRadius() + 50) nearbyCount2++;
+ if (fruits[k] !== fruit1 && fruits[k] !== fruit2 && !fruits[k].merged) {
+ var dx1 = fruit1.x - fruits[k].x;
+ var dy1 = fruit1.y - fruits[k].y;
+ var distance1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
+ if (distance1 < proximityCheck) fruit1Nearby++;
+ var dx2 = fruit2.x - fruits[k].x;
+ var dy2 = fruit2.y - fruits[k].y;
+ var distance2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
+ if (distance2 < proximityCheck) fruit2Nearby++;
}
}
- // Only apply rotation if not cramped and movement is significant
- if (totalImpulse > movementThreshold && nearbyCount1 < 3 && nearbyCount2 < 3) {
- var rotationFactor = 0.008; // Reduced rotation factor
- fruit1.angularVelocity += (impulseX + impulseY) * rotationFactor;
- fruit2.angularVelocity -= (impulseX + impulseY) * rotationFactor;
+ if (totalImpulse > movementThreshold) {
+ // Dramatically reduce rotation in cramped conditions
+ var baseRotationFactor = 0.015;
+ var fruit1RotationFactor = fruit1Nearby >= 2 ? baseRotationFactor * 0.1 : baseRotationFactor;
+ var fruit2RotationFactor = fruit2Nearby >= 2 ? baseRotationFactor * 0.1 : baseRotationFactor;
+ fruit1.angularVelocity += (impulseX + impulseY) * fruit1RotationFactor;
+ fruit2.angularVelocity -= (impulseX + impulseY) * fruit2RotationFactor;
}
}
}
}
Apple with cute face. In-Game asset. 2d. High contrast. No shadows
Cherry with cute face. In-Game asset. 2d. High contrast. No shadows
Greyish blue square. In-Game asset. 2d. High contrast. No shadows
Grapes with a cute face. In-Game asset. 2d. High contrast. No shadows
Circle water melon with cute face. In-Game asset. 2d. High contrast. No shadows
Circle cantaloupe with cute face. In-Game asset. 2d. High contrast. No shadows
Pear with cute face. In-Game asset. 2d. High contrast. No shadows
Strawberry with cute face. In-Game asset. 2d. High contrast. No shadows
Peach with cute face. In-Game asset. 2d. High contrast. No shadows
Pineapple with cute face. In-Game asset. 2d. High contrast. No shadows
Dragon fruit with cute face. In-Game asset. 2d. High contrast. No shadows
Lemon with cute face. In-Game asset. 2d. High contrast. No shadows
Coconut with cute face. In-Game asset. 2d. High contrast. No shadows
Kiwi with cute face. In-Game asset. 2d. High contrast. No shadows
Durian with cute face. In-Game asset. 2d. High contrast. No shadows
Mango with cute face. In-Game asset. 2d. High contrast. No shadows
Papaya with cute face. In-Game asset. 2d. High contrast. No shadows
Arrow pointing down. In-Game asset. 2d. High contrast. No shadows
Shine. In-Game asset. 2d. High contrast. No shadows
Purple galaxy. In-Game asset. 2d. High contrast. No shadows
Blueberry with cute face. In-Game asset. 2d. High contrast. No shadows
Open pomegranate with cute face. In-Game asset. 2d. High contrast. No shadows
A cut in half avocado with cute face. In-Game asset. 2d. High contrast. No shadows
Cranberry’s with cute face. In-Game asset. 2d. High contrast. No shadows