User prompt
please rename all fruits so their format is 1.Cherry and 2.Apple and so on. start each fruit with it's level number so I can easily see each fruit level numerically
User prompt
apply this scoring formula when merging balls. so when two level one balls merge, they award a point. this is the points for each level. Ball Level Merging Score 1 1 2 2 3 3 4 5 5 8 6 13 7 21 8 34 9 55 10 89
User prompt
Please fix the bug: 'fruit is not defined' in or related to this line: 'if (Math.abs(fruit.vx) < 0.5 && Math.abs(fruit.vy) < 0.5) {' Line Number: 59
User prompt
the balls rotation is still bugged, especially for balls on top of other balls that don't touch the ground, they seem to keep spinning, never coming to a rest
User prompt
it seems the spinning of the ball is not correlated to the force of impact. they do rotate, but the rotation doesn't look natural. they either rotate too much from a small itneraction, or sometimes stopp too abruptly
User prompt
balls reverted back to spinning uncontrolably, as if there's no friction. balls should have friction when interacting with the ground or other balls, so they dont keep spinning forever
User prompt
great, but now some ball lost collision and dont collide with each other
User prompt
nice, it works great now! amazing job! but curently I need to hold the actuall ball for it to move, when instead I should be able to press anywhere for that to happen
User prompt
we need to simplify things. remove the preview that shows the next ball and thenextfruitbg and instead of showing the next ball as a static icon in a fixed place, place that at the top of the screen, as the next dropping fruit. it remains in the same Y axis, but it now becomes part of the gameplay rather than of UI. while the player keeps the finger on the screen, the active ball follows it, but only moving on the X axis, and when the figner is released, the ball drops and then the next one pawns in it's place, so there's always an active ball that is about to be dropped
User prompt
upon release, the balls dont drop, they simply diappear
User prompt
we need to simplify things. remove the preview that shows the next ball and thenextfruitbg and instead of showing the next ball as a static icon in a fixed place, place that at the top of the screen, as the next dropping fruit. it remains in the same Y axis, but it now becomes part of the gameplay rather than of UI. while the player keeps the finger on the screen, the active ball follows it, but only moving on the X axis, and when the figner is released, the ball drops and then the next one pawns in it's place, so there's always an active ball that is about to be dropped
User prompt
make the balls drop even faster
User prompt
balls can still rotate exponentially. ground applies friction, but collision with other balls doesnt, making balls on top spin uncontrolably. fix this please
User prompt
the rotation seems to be too much and looks unrealistic. ensure ground adds friction which decreases the rotation
User prompt
balls should also rotate when colliding with other balls. the rotation should follow physics rules, based on the point of impact āŖš” Consider importing and using the following plugins: @upit/tween.v1
User prompt
remove the numerical value from the balls
User prompt
balls numbers are wrng, smallest fruit should be 1, next one 2 instead of 3, next one 3 instead of 6 and so on, keep it simple, chronological
User prompt
the smallest starting fruit size should be 100 and each next evolution is 100 pixels higher, so first is 100, next 200, next 300 and so on. change not the values in the code, but also the actual assets sizes
User prompt
the smallest starting fruit size should be 150 and each next evolution is 150 pixels higher, so first is 150, next 300, next 450 and so on
User prompt
the game seems to only have 5 fruits, I need another 5, so we have 10 total
User prompt
there seems to b a lag before identical balls merge, the merge should hapen instantly, as soon as the balls touch
User prompt
only balls of different evolutions should colide, identical ones need to merge
User prompt
balls currently overlap and intersect each other, that should not be possible, they need to collide with each other, and never overlap
User prompt
upon merging, balls should evolve to the next level, right now they dont, they just disappear. that's ok, but also create a new higher level ball upon merging
User prompt
increase the balls drop speed
/****
* 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);
// FruitTypes is being used before it's defined, so we need to handle this case
self.type = type;
self.vx = 0;
self.vy = 0;
self.gravity = 0.9;
self.friction = 0.98;
self.elasticity = 0.7;
self.merging = false;
self.isStatic = false;
// Only attempt to attach the asset if self.type exists and has necessary properties
if (self.type && self.type.id && self.type.points && self.type.size) {
var fruitGraphics = self.attachAsset(self.type.id, {
anchorX: 0.5,
anchorY: 0.5
});
// Show point value on fruit - only if we have a valid type
var pointsText = new Text2(self.type.points.toString(), {
size: self.type.size * 0.4,
fill: 0xFFFFFF
});
pointsText.anchor.set(0.5, 0.5);
self.addChild(pointsText);
} else {
// This will be initialized properly when the game is fully loaded
console.log("Warning: Fruit type not available yet or missing required properties");
}
self.update = function () {
if (self.isStatic || self.merging) {
return;
}
// Apply gravity
self.vy += self.gravity;
// Apply velocity
self.x += self.vx;
self.y += self.vy;
// Apply friction
self.vx *= self.friction;
self.vy *= self.friction;
// Check collisions with other fruits
for (var i = 0; i < fruits.length; i++) {
var other = fruits[i];
if (other === self || other.merging || self.merging) {
continue;
}
// Skip collision resolution if same fruit type (they should merge instead)
if (self.type === other.type) {
continue;
}
// Calculate distance between centers
var dx = other.x - self.x;
var dy = other.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
// Calculate minimum distance before collision (sum of radii)
var minDistance = (self.type.size + other.type.size) / 2;
// If collision detected
if (distance < minDistance) {
// Normalize direction vector
var nx = dx / distance;
var ny = dy / distance;
// Calculate penetration depth
var penetration = minDistance - distance;
// Move fruits apart based on penetration
self.x -= nx * penetration * 0.5;
self.y -= ny * penetration * 0.5;
other.x += nx * penetration * 0.5;
other.y += ny * penetration * 0.5;
// Calculate relative velocity
var relVelocityX = other.vx - self.vx;
var relVelocityY = other.vy - self.vy;
// Calculate velocity along normal
var velAlongNormal = relVelocityX * nx + relVelocityY * ny;
// Only resolve if objects are moving toward each other
if (velAlongNormal > 0) {
continue;
}
// Calculate collision impulse
var e = self.elasticity; // coefficient of restitution
var j = -(1 + e) * velAlongNormal;
j /= 2; // Assume equal mass for simplicity
// Apply impulse
self.vx -= j * nx;
self.vy -= j * ny;
other.vx += j * nx;
other.vy += j * ny;
// Play bounce sound if significant collision
if (Math.abs(j) > 2) {
LK.getSound('bounce').play();
}
}
}
// Wall collision
if (self.x < wallLeft.x + wallLeft.width / 2 + self.type.size / 2) {
self.x = wallLeft.x + wallLeft.width / 2 + self.type.size / 2;
self.vx = -self.vx * self.elasticity;
if (Math.abs(self.vx) > 1) {
LK.getSound('bounce').play();
}
}
if (self.x > wallRight.x - wallRight.width / 2 - self.type.size / 2) {
self.x = wallRight.x - wallRight.width / 2 - self.type.size / 2;
self.vx = -self.vx * self.elasticity;
if (Math.abs(self.vx) > 1) {
LK.getSound('bounce').play();
}
}
// Floor collision
if (self.y > gameFloor.y - gameFloor.height / 2 - self.type.size / 2) {
self.y = gameFloor.y - gameFloor.height / 2 - self.type.size / 2;
self.vy = -self.vy * self.elasticity;
// If we're barely moving, stop completely
if (Math.abs(self.vy) < 2) {
self.vy = 0;
}
if (Math.abs(self.vy) > 1) {
LK.getSound('bounce').play();
}
}
};
self.merge = function (otherFruit) {
if (self.merging || !self.type.next) {
return;
}
self.merging = true;
otherFruit.merging = true;
// Calculate midpoint between fruits for new fruit position
var midX = (self.x + otherFruit.x) / 2;
var midY = (self.y + otherFruit.y) / 2;
// Create merge animation
tween(self, {
alpha: 0,
scaleX: 0.5,
scaleY: 0.5
}, {
duration: 200,
easing: tween.easeOut
});
tween(otherFruit, {
alpha: 0,
scaleX: 0.5,
scaleY: 0.5
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
// Create new merged fruit
var nextType = FruitTypes[self.type.next.toUpperCase()];
var newFruit = new Fruit(nextType);
newFruit.x = midX;
newFruit.y = midY;
newFruit.scaleX = 0.5;
newFruit.scaleY = 0.5;
game.addChild(newFruit);
fruits.push(newFruit);
// Add merge points
LK.setScore(LK.getScore() + nextType.points);
updateScoreDisplay();
// Animate new fruit growing
tween(newFruit, {
scaleX: 1,
scaleY: 1
}, {
duration: 300,
easing: tween.bounceOut
});
// Remove old fruits
var indexSelf = fruits.indexOf(self);
if (indexSelf !== -1) {
fruits.splice(indexSelf, 1);
}
self.destroy();
var indexOther = fruits.indexOf(otherFruit);
if (indexOther !== -1) {
fruits.splice(indexOther, 1);
}
otherFruit.destroy();
// Play merge sound
LK.getSound('merge').play();
}
});
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0xf6e58d
});
/****
* Game Code
****/
// Game variables
var FruitTypes = {
CHERRY: {
id: 'cherry',
size: 100,
points: 1,
next: 'grape'
},
GRAPE: {
id: 'grape',
size: 200,
points: 2,
next: 'apple'
},
APPLE: {
id: 'apple',
size: 300,
points: 3,
next: 'orange'
},
ORANGE: {
id: 'orange',
size: 400,
points: 4,
next: 'watermelon'
},
WATERMELON: {
id: 'watermelon',
size: 500,
points: 5,
next: 'pineapple'
},
PINEAPPLE: {
id: 'pineapple',
size: 600,
points: 6,
next: 'melon'
},
MELON: {
id: 'melon',
size: 700,
points: 7,
next: 'peach'
},
PEACH: {
id: 'peach',
size: 800,
points: 8,
next: 'coconut'
},
COCONUT: {
id: 'coconut',
size: 900,
points: 9,
next: 'durian'
},
DURIAN: {
id: 'durian',
size: 1000,
points: 10,
next: null
}
};
var gameWidth = 2048;
var gameHeight = 2732;
var fruits = [];
var nextFruit = null;
var nextFruitDisplay = null;
var canDropFruit = true;
var wallLeft, wallRight, gameFloor;
var dropPoint = {
x: gameWidth / 2,
y: 200
};
var gameOver = false;
var scoreText;
// Setup game boundaries
function setupBoundaries() {
// Left wall
wallLeft = game.addChild(LK.getAsset('wall', {
anchorX: 0.5,
anchorY: 0.5
}));
wallLeft.x = 0;
wallLeft.y = gameHeight / 2;
// Right wall
wallRight = game.addChild(LK.getAsset('wall', {
anchorX: 0.5,
anchorY: 0.5
}));
wallRight.x = gameWidth;
wallRight.y = gameHeight / 2;
// Floor
gameFloor = game.addChild(LK.getAsset('floor', {
anchorX: 0.5,
anchorY: 0.5
}));
gameFloor.x = gameWidth / 2;
gameFloor.y = gameHeight;
}
// Create new next fruit
function createNextFruit() {
// Determine which fruit to spawn (for now just start with smaller fruits)
var fruitProbability = Math.random();
var fruitType;
if (fruitProbability < 0.6) {
fruitType = FruitTypes.CHERRY;
} else if (fruitProbability < 0.85) {
fruitType = FruitTypes.GRAPE;
} else {
fruitType = FruitTypes.APPLE;
}
nextFruit = fruitType;
// Update display
if (nextFruitDisplay) {
nextFruitDisplay.destroy();
}
// Background for next fruit
var nextBg = LK.getAsset('nextFruitBg', {
anchorX: 0.5,
anchorY: 0.5
});
LK.gui.top.addChild(nextBg);
nextBg.x = 150;
nextBg.y = 150;
// The next fruit preview
var fruitPreview = LK.getAsset(fruitType.id, {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.7,
scaleY: 0.7
});
// Next label
var nextLabel = new Text2("NEXT", {
size: 40,
fill: 0xFFFFFF
});
nextLabel.anchor.set(0.5, 0.5);
nextLabel.y = -100;
nextFruitDisplay = new Container();
nextFruitDisplay.addChild(nextBg);
nextFruitDisplay.addChild(fruitPreview);
nextFruitDisplay.addChild(nextLabel);
LK.gui.topLeft.addChild(nextFruitDisplay);
nextFruitDisplay.x = 150;
nextFruitDisplay.y = 150;
}
// Drop fruit at specified position
function dropFruit(x) {
if (!canDropFruit || gameOver) {
return;
}
// Create new fruit
var newFruit = new Fruit(nextFruit);
newFruit.x = x;
newFruit.y = dropPoint.y;
// Add small random x velocity to make it interesting
newFruit.vx = (Math.random() - 0.5) * 3;
game.addChild(newFruit);
fruits.push(newFruit);
// Play drop sound
LK.getSound('drop').play();
// Prepare next fruit
createNextFruit();
// Prevent spam dropping
canDropFruit = false;
LK.setTimeout(function () {
canDropFruit = true;
}, 500);
}
// Update score display
function updateScoreDisplay() {
scoreText.setText("SCORE: " + LK.getScore());
}
// Setup UI
function setupUI() {
// Score display
scoreText = new Text2("SCORE: 0", {
size: 80,
fill: 0xFFFFFF
});
scoreText.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreText);
scoreText.y = 30;
}
// Check for fruit collisions
function checkFruitCollisions() {
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];
// Skip if either fruit is already merging
if (fruit1.merging || fruit2.merging) {
continue;
}
// Calculate distance between centers
var dx = fruit2.x - fruit1.x;
var dy = fruit2.y - fruit1.y;
var distance = Math.sqrt(dx * dx + dy * dy);
// Check if they are overlapping
var combinedRadius = (fruit1.type.size + fruit2.type.size) / 2;
// Use a smaller threshold for merging to prevent overlap issues
if (distance < combinedRadius * 0.9) {
// Only proceed with merge if fruits are the same type
if (fruit1.type === fruit2.type) {
// Merge fruits immediately upon contact, regardless of velocity
fruit1.merge(fruit2);
break;
}
}
}
}
}
// Check if game is over (too many fruits on screen or stacked too high)
function checkGameOver() {
if (gameOver) {
return;
}
if (fruits.length > 30) {
gameOver = true;
LK.showGameOver();
return;
}
// Check if any fruits are too high
for (var i = 0; i < fruits.length; i++) {
// Only check for game over if fruit is high AND has had time to settle
if (fruits[i].y < 300 && !fruits[i].merging) {
var isFruitMoving = Math.abs(fruits[i].vx) > 0.5 || Math.abs(fruits[i].vy) > 0.5;
// Add a countdown to ensure the fruit has truly stopped moving
if (!isFruitMoving) {
// Initialize the stable timer if not already set
if (fruits[i].stableTimer === undefined) {
fruits[i].stableTimer = 60; // Give 1 second (60 frames) to confirm stability
} else {
fruits[i].stableTimer--;
if (fruits[i].stableTimer <= 0) {
gameOver = true;
LK.showGameOver();
return;
}
}
} else {
// Reset timer if the fruit starts moving again
fruits[i].stableTimer = undefined;
}
}
}
}
// Initialize game
function initGame() {
LK.setScore(0);
gameOver = false;
fruits = [];
// Start background music
LK.playMusic('bgmusic');
// Setup game elements
setupBoundaries();
setupUI();
updateScoreDisplay();
createNextFruit();
}
// Event handlers
game.down = function (x, y) {
// Don't allow drops too close to the edges
var minX = wallLeft.x + wallLeft.width / 2 + 100;
var maxX = wallRight.x - wallRight.width / 2 - 100;
if (x > minX && x < maxX) {
dropFruit(x);
}
};
// Game update loop
game.update = function () {
// Check for fruit collisions
checkFruitCollisions();
// Check game over conditions
checkGameOver();
};
// Initialize the game
initGame(); ===================================================================
--- original.js
+++ change.js
@@ -212,57 +212,57 @@
},
GRAPE: {
id: 'grape',
size: 200,
- points: 3,
+ points: 2,
next: 'apple'
},
APPLE: {
id: 'apple',
size: 300,
- points: 6,
+ points: 3,
next: 'orange'
},
ORANGE: {
id: 'orange',
size: 400,
- points: 10,
+ points: 4,
next: 'watermelon'
},
WATERMELON: {
id: 'watermelon',
size: 500,
- points: 15,
+ points: 5,
next: 'pineapple'
},
PINEAPPLE: {
id: 'pineapple',
size: 600,
- points: 21,
+ points: 6,
next: 'melon'
},
MELON: {
id: 'melon',
size: 700,
- points: 28,
+ points: 7,
next: 'peach'
},
PEACH: {
id: 'peach',
size: 800,
- points: 36,
+ points: 8,
next: 'coconut'
},
COCONUT: {
id: 'coconut',
size: 900,
- points: 45,
+ points: 9,
next: 'durian'
},
DURIAN: {
id: 'durian',
size: 1000,
- points: 55,
+ points: 10,
next: null
}
};
var gameWidth = 2048;