Code edit (1 edits merged)
Please save this source code
User prompt
when the 6 apples get released, they spawn a bit higher then the actual location they're intended, fix this, so the spawn location coincides with the same y position they start from. spawn them 100 pixels lower
User prompt
when apples get released, they spawn a bit higher then the actual location they're intended, fix this, so the spawn location coincides with the same y position they start from
Code edit (2 edits merged)
Please save this source code
User prompt
consolidate the y position of the apples. right now their starting position and their spawning position differs, so they charge in a position but spawn a bit higher, ensure this position is used for both loading and spawning
User prompt
all new generated fruits should have one second delay before interacting with the game over trigger so that if I release fruits super fast they don't trigger a game over
User prompt
after releasing the pineapple from the left side onto the bboard, disabble its contact with the game over for 2 seconds after release. this delay is meant to not cause a gameover immediately after releasing it, allowing it to fall inside the board first. after the 2 seconds delay, then it becomes active ↪💡 Consider importing and using the following plugins: @upit/tween.v1
Code edit (1 edits merged)
Please save this source code
User prompt
move the pineapple that slides from the left, 200 pixels higher
User prompt
mak the coconut appear once every 500 points instead of every 200 please.
User prompt
make the coconut sldie in a bit faster on the board
User prompt
create a sound named Stonks and play it when a new coconut appears every 200 points
User prompt
make a coconut only appear after very 200 points instead of 100
User prompt
the coconut should slide from below the screen, much slower, like increase it's speed upwards as it slides. right now it snaps into place, it doesn't slide, make sure the speed is much slower so it takes a while for it to actully fully enter the screen, giving players a chance to see it coming from below ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
the coconut should slide in slower in the board ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
for every 100 points gained, make a coconut come from under the screen. it slides from under the screen and upwards onto the board. it's active ever since inception, so as it enters the board, it interacts with the other balls, pusshing them away. it stops when it no longer touches the floor, meaning it entered fully in the board. it can appear from any horizontal part of the screen as long as it's inside the bounds of the playing area, but one instance can appear more toward the left, anothermore to the right or another towards the middle
Code edit (4 edits merged)
Please save this source code
User prompt
move the apples 100 pixels higher pls
Code edit (1 edits merged)
Please save this source code
User prompt
make there be 6 apples instead of 4 please, mighty Ava
User prompt
let's change how the 4 apples charge up. instead of filling them when a fruit is dropped, remove that logic and change it to refill one icon when a merge occurs. every merge charges one icon, and when all 4 have been filled, they are ready to be released. the next merge releases them, and then the cycle resets
Code edit (4 edits merged)
Please save this source code
User prompt
dont push the pineapple as much to the left, decrease that pushed distance please
User prompt
actually, instead of just 3 pushes, let's do 7 and release it on the 8th
User prompt
actually, instead of dropping the pineapple after no longer touching the left wall, remove that rule and change it to simply release it after the 3rd push. so it's pushed a total of 3 times, and on the 4th drop, also release the pineapple. on the 5th drop, resetart the process, and push it again from the left
/**** * 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.rotation = 0; self.angularVelocity = 0; self.angularFriction = 0.92; // Increased friction for faster angular velocity reduction self.groundAngularFriction = 0.75; // Stronger ground friction to stop spinning faster self.gravity = 1.8; // Doubled gravity to make fruits drop faster self.friction = 0.98; // Calculate elasticity based on fruit level // The biggest fruit (DURIAN) has elasticity of 0.7 // Smaller fruits are more bouncy with elasticity closer to 1.0 var fruitLevels = { 'CHERRY': 1, 'GRAPE': 2, 'APPLE': 3, 'ORANGE': 4, 'WATERMELON': 5, 'PINEAPPLE': 6, 'MELON': 7, 'PEACH': 8, 'COCONUT': 9, 'DURIAN': 10 }; var currentLevel = self.type ? fruitLevels[self.type.id.toUpperCase()] || 10 : 10; // Scale elasticity from 0.9 (most bouncy) for level 1 to 0.7 (least bouncy) for level 10 self.elasticity = 0.9 - (currentLevel - 1) * (0.2 / 9); self.merging = false; self.isStatic = false; self.maxAngularVelocity = 0.15; // Add maximum angular velocity cap // 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 }); // Set width and height directly from the actual asset for accurate hitbox self.width = fruitGraphics.width; self.height = fruitGraphics.height; // No point value shown on fruit } 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; } // Track safety period state changes if (self.safetyPeriod === false && self.vy <= 0) { // When a fruit that was in safety period starts moving upward or stops, // it means it has hit something, so it's no longer in safety period self.safetyPeriod = true; } // Add damping when velocity is low if (Math.abs(self.vx) < 0.5 && Math.abs(self.vy) < 0.5) { self.angularVelocity *= 0.9; // Apply damping when fruit is almost at rest } // Check for contact with walls to apply wall friction var fruitRadius = self.width / 2; var isContactingLeftWall = self.x <= wallLeft.x + wallLeft.width / 2 + fruitRadius + 2; var isContactingRightWall = self.x >= wallRight.x - wallRight.width / 2 - fruitRadius - 2; if (isContactingLeftWall || isContactingRightWall) { // Apply progressive wall friction based on how long the fruit has been in contact if (!self.wallContactFrames) { self.wallContactFrames = 1; } else { self.wallContactFrames++; } // Increase wall friction the longer the fruit stays in contact var progressiveFriction = Math.min(0.85, 0.65 + self.wallContactFrames * 0.01); self.angularVelocity *= progressiveFriction; } else { // Reset wall contact frames when not touching walls self.wallContactFrames = 0; } // Check for contact with other fruits to apply additional friction var isContactingOtherFruit = false; for (var i = 0; i < fruits.length; i++) { var otherFruit = fruits[i]; if (otherFruit !== this && !otherFruit.merging && !otherFruit.isStatic) { var dx = otherFruit.x - this.x; var dy = otherFruit.y - this.y; var distance = Math.sqrt(dx * dx + dy * dy); var combinedRadius = (this.type.size + otherFruit.type.size) / 2; if (distance < combinedRadius + 2) { // Small buffer for contact detection isContactingOtherFruit = true; break; } } } // Apply stronger friction when in contact with other fruits if (isContactingOtherFruit) { self.angularVelocity *= 0.8; // Stronger friction when touching other fruits } // Apply extreme damping when almost stopped rotating if (Math.abs(self.angularVelocity) < 0.01) { self.angularVelocity = 0; } }; 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 based on the new fruit's level LK.setScore(LK.getScore() + nextType.points); updateScoreDisplay(); // Play merge sound LK.getSound('merge').play(); // 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(); // Charge one ball icon per merge chargeCounter++; updateChargedBallDisplay(); // Check if we've reached 6 charged balls if (chargeCounter > 6) { // Release all charged balls on the next merge releaseChargedBalls(); } } }); }; return self; }); var Line = Container.expand(function () { var self = Container.call(this); var lineGraphics = self.attachAsset('floor', { anchorX: 0.5, anchorY: 0.5 }); // Make it visually distinct from the floor lineGraphics.tint = 0xff0000; // Ensure full width is used for collision but visual is thin lineGraphics.height = 20; // Make the visual thinner return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0xf6e58d }); /**** * Game Code ****/ // Game variables var gameOverLine; // Game over line var pineapple; // The pineapple that pushes in from left var pineappleActive = false; // Track if pineapple is active in gameplay var pineapplePushCount = 0; // Count how many times pineapple has been pushed var FruitTypes = { CHERRY: { id: 'cherry', size: 150, points: 1, next: 'grape' }, GRAPE: { id: 'grape', size: 200, points: 2, next: 'apple' }, APPLE: { id: 'apple', size: 250, points: 3, next: 'orange' }, ORANGE: { id: 'orange', size: 300, points: 5, next: 'watermelon' }, WATERMELON: { id: 'watermelon', size: 350, points: 8, next: 'pineapple' }, PINEAPPLE: { id: 'pineapple', size: 400, points: 13, next: 'melon' }, MELON: { id: 'melon', size: 450, points: 21, next: 'peach' }, PEACH: { id: 'peach', size: 500, points: 34, next: 'coconut' }, COCONUT: { id: 'coconut', size: 550, points: 55, next: 'durian' }, DURIAN: { id: 'durian', size: 600, points: 89, next: null } }; var gameWidth = 2048; var gameHeight = 2732; var fruits = []; var nextFruitType = null; var activeFruit = null; // The fruit currently controlled by the player var wallLeft, wallRight, gameFloor; var dropPointY = 200; // Y coordinate where new fruits appear var gameOver = false; var scoreText; var isDragging = false; // Flag to check if the player is currently dragging var chargedBalls = []; // Array to hold charged ball icons var chargedBallContainer = null; // Container for charged ball icons var chargeCounter = 0; // Counter to track dropped balls for charging // 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; // Game over line gameOverLine = game.addChild(new Line()); gameOverLine.x = gameWidth / 2; gameOverLine.y = 450; // Position above the drop zone gameOverLine.scaleX = 1; // Make it stretch across the entire width of the screen gameOverLine.scaleY = 0.5; // Make it thinner } // 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; } nextFruitType = fruitType; // Update display // No explicit preview display needed, the next fruit will be the one the player controls // Create the active fruit activeFruit = new Fruit(nextFruitType); // Position at the top center initially, but 100px lower than before activeFruit.x = gameWidth / 2; activeFruit.y = dropPointY + 100; // Make it static while the player controls it activeFruit.isStatic = true; game.addChild(activeFruit); } // Drop fruit at specified position function dropFruit() { if (gameOver || !activeFruit) { return; } // Make the active fruit dynamic so it drops activeFruit.isStatic = false; // Add angle variation - random angle between -10 and +10 degrees var angle = (Math.random() * 20 - 10) * (Math.PI / 180); // Convert to radians var force = 3.5; // Force magnitude // Apply velocity based on angle activeFruit.vx = Math.sin(angle) * force; activeFruit.vy = Math.abs(Math.cos(angle) * force); // Make sure initial Y velocity is downward // Mark this fruit as in safety period since it's newly dropped activeFruit.safetyPeriod = false; // Make it immune to game over for a second activeFruit.immuneToGameOver = true; // Start 1-second timer to enable game over contact LK.setTimeout(function () { if (activeFruit && fruits.includes(activeFruit)) { activeFruit.immuneToGameOver = false; } }, 1000); // Add the fruit to the main fruits array fruits.push(activeFruit); // Play drop sound LK.getSound('drop').play(); // Update charged ball display updateChargedBallDisplay(); // Removed charge counter increment from here // Handle pineapple logic if (pineappleActive) { // Pineapple is ready to drop after 3 pushes pineapple.isStatic = false; // Add angle variation var pineappleAngle = (Math.random() * 10 - 5) * (Math.PI / 180); var pineappleForce = 2.5; // Less force for bigger fruit // Apply velocity pineapple.vx = Math.sin(pineappleAngle) * pineappleForce; pineapple.vy = Math.abs(Math.cos(pineappleAngle) * pineappleForce); // Mark as in safety period and disable game over contact for 2 seconds pineapple.safetyPeriod = false; // Make a property to track if pineapple is immune to game over pineapple.immuneToGameOver = true; // Add to fruits array fruits.push(pineapple); // Start 2-second timer to enable game over contact LK.setTimeout(function () { pineapple.immuneToGameOver = false; }, 2000); // Setup a new pineapple for next cycle setupPineapple(); } else { // Push the pineapple further in pushPineapple(); } // Clear active fruit activeFruit = null; // Create the next fruit immediately createNextFruit(); } // 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; // Create charged ball grid setupChargedBallDisplay(); } // Create charged ball grid function setupChargedBallDisplay() { // Create container for charged balls chargedBallContainer = new Container(); game.addChild(chargedBallContainer); // Position the container at the top of the screen, 100 pixels higher than before chargedBallContainer.y = 700; // Calculate total width needed for all balls var ballType = FruitTypes.APPLE; // Level 3 ball type var ballSize = ballType.size; // Get actual size of apple var spacing = (gameWidth - 200) / 5; // Evenly distribute 6 balls // Horizontal position to start the row var startX = 100; // Starting position from left edge // Create 6 balls in a single row for (var i = 0; i < 6; i++) { var ball = new Container(); // Use apple as charged balls (level 3) with actual size var ballGraphics = ball.attachAsset('apple', { anchorX: 0.5, anchorY: 0.5 }); // Position in single row with equal spacing ball.x = startX + i * spacing; // Set to semi-transparent initially ball.alpha = 0.5; chargedBallContainer.addChild(ball); chargedBalls.push(ball); } // Center the container horizontally chargedBallContainer.x = 0; } // Function to update charged ball display function updateChargedBallDisplay() { // Update the visibility of balls based on chargeCounter for (var i = 0; i < chargedBalls.length; i++) { if (i < chargeCounter) { // Balls that are charged are fully visible if (chargedBalls[i].alpha !== 1) { tween(chargedBalls[i], { alpha: 1 }, { duration: 300, easing: tween.easeOut }); } // Ensure correct size is maintained chargedBalls[i].scaleX = 1; chargedBalls[i].scaleY = 1; } else { // Balls that are not yet charged are semi-transparent if (chargedBalls[i].alpha !== 0.5) { chargedBalls[i].alpha = 0.5; } // Ensure correct size is maintained chargedBalls[i].scaleX = 1; chargedBalls[i].scaleY = 1; } } } // Function to release all charged balls function releaseChargedBalls() { // Create and drop 6 level 3 (apple) balls for (var i = 0; i < 6; i++) { var apple = new Fruit(FruitTypes.APPLE); // Distribute the balls across the width of the game var offset = i * (gameWidth - 300) / 5 + 150; apple.x = offset; apple.y = dropPointY - 100; // Position 100 pixels lower // Make it dynamic so it drops apple.isStatic = false; // Add random horizontal velocity and downward vertical velocity var angle = (Math.random() * 20 - 10) * (Math.PI / 180); var force = 3.5; apple.vx = Math.sin(angle) * force; apple.vy = Math.abs(Math.cos(angle) * force); // Mark this fruit as in safety period since it's newly dropped apple.safetyPeriod = false; // Make it immune to game over for a second apple.immuneToGameOver = true; // Start 1-second timer to enable game over contact LK.setTimeout(function () { if (apple && fruits.includes(apple)) { apple.immuneToGameOver = false; } }, 1000); // Add to game and fruits array game.addChild(apple); fruits.push(apple); // Add animation to make it look like the charged balls are dropping tween(chargedBalls[i], { alpha: 0, scaleX: 0.1, scaleY: 0.1 }, { duration: 300, easing: tween.easeIn }); } // Play drop sound LK.getSound('drop').play(); // Reset charge counter chargeCounter = 0; // Update the charged ball display after a delay - ensure complete reset LK.setTimeout(function () { for (var i = 0; i < chargedBalls.length; i++) { // Fully reset all properties chargedBalls[i].alpha = 0.5; // Remove any scaling to ensure original size chargedBalls[i].scaleX = 1; chargedBalls[i].scaleY = 1; } updateChargedBallDisplay(); }, 300); } // Check for fruit collisions // Check for fruit collisions function checkFruitCollisions() { for (var i = 0; i < fruits.length; i++) { // Skip collision for the active fruit if (fruits[i] === activeFruit) { continue; } for (var j = i + 1; j < fruits.length; j++) { var fruit1 = fruits[i]; var fruit2 = fruits[j]; // Skip collision for the active fruit if (fruits[j] === activeFruit) { continue; } // 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 - use actual asset dimensions for more accurate hitboxes var fruit1Radius = fruit1.width / 2; // Use actual width of the asset var fruit2Radius = fruit2.width / 2; // Use actual width of the asset var combinedRadius = fruit1Radius + fruit2Radius; if (distance < combinedRadius) { // Resolve collision (simple separation and velocity adjustment) var overlap = combinedRadius - distance; var normalizeX = dx / distance; var normalizeY = dy / distance; var moveX = overlap / 2 * normalizeX; var moveY = overlap / 2 * normalizeY; fruit1.x -= moveX; fruit1.y -= moveY; fruit2.x += moveX; fruit2.y += moveY; // Calculate relative velocity var rvX = fruit2.vx - fruit1.vx; var rvY = fruit2.vy - fruit1.vy; var contactVelocity = rvX * normalizeX + rvY * normalizeY; // Only resolve if velocities are separating if (contactVelocity < 0) { // Use the higher elasticity for the collision (smaller fruits bounce more) var collisionElasticity = Math.max(fruit1.elasticity, fruit2.elasticity); var impulse = -(1 + collisionElasticity) * contactVelocity; var totalMass = fruit1.type.size + fruit2.type.size; // Using size as a proxy for mass var impulse1 = impulse * (fruit2.type.size / totalMass); var impulse2 = impulse * (fruit1.type.size / totalMass); // Apply impact scaling for smaller fruits against bigger ones // Smaller fruits should bounce away more from larger fruits var sizeDifference = Math.abs(fruit1.type.size - fruit2.type.size) / Math.max(fruit1.type.size, fruit2.type.size); if (fruit1.type.size < fruit2.type.size) { impulse1 *= 1 + sizeDifference * 0.5; // Smaller fruit gets extra bounce } else if (fruit2.type.size < fruit1.type.size) { impulse2 *= 1 + sizeDifference * 0.5; // Smaller fruit gets extra bounce } fruit1.vx -= impulse1 * normalizeX; fruit1.vy -= impulse1 * normalizeY; fruit2.vx += impulse2 * normalizeX; fruit2.vy += impulse2 * normalizeY; // Apply friction between colliding fruits var tangentX = -normalizeY; var tangentY = normalizeX; var tangentVelocity = rvX * tangentX + rvY * tangentY; var frictionImpulse = -tangentVelocity * 0.2; // Increased friction factor fruit1.vx -= frictionImpulse * tangentX; fruit1.vy -= frictionImpulse * tangentY; fruit2.vx += frictionImpulse * tangentX; fruit2.vy += frictionImpulse * tangentY; // Apply angular velocity change based on collision with proportional damping var fruit1AngularImpulse = impulse1 * (tangentX * normalizeY - tangentY * normalizeX) * 0.0005; var fruit2AngularImpulse = impulse2 * (tangentX * normalizeY - tangentY * normalizeX) * 0.0005; fruit1.angularVelocity += fruit1AngularImpulse; fruit2.angularVelocity -= fruit2AngularImpulse; // Apply additional angular damping during collisions fruit1.angularVelocity *= 0.9; fruit2.angularVelocity *= 0.9; // Cap angular velocity fruit1.angularVelocity = Math.min(Math.max(fruit1.angularVelocity, -fruit1.maxAngularVelocity), fruit1.maxAngularVelocity); fruit2.angularVelocity = Math.min(Math.max(fruit2.angularVelocity, -fruit2.maxAngularVelocity), fruit2.maxAngularVelocity); if (Math.abs(contactVelocity) > 1) { // Bounce sound removed } } // Only proceed with merge if fruits are the same type if (fruit1.type === fruit2.type) { // Merge fruits if they are close enough and of the same type 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++) { // Don't check game over for the active fruit if (fruits[i] === activeFruit) { continue; } // Check if fruit touches the game over line if (!fruits[i].merging && fruits[i].intersects(gameOverLine)) { // Skip game over if the fruit is immune (any fruit in grace period) if (fruits[i].immuneToGameOver) { continue; } // Initialize the safetyPeriod property if not already set if (fruits[i].safetyPeriod === undefined) { // If the fruit is still moving downward, it's probably just spawned if (fruits[i].vy > 0) { // Mark this fruit as in safety period - it has just been dropped fruits[i].safetyPeriod = false; continue; // Skip game over check for freshly dropped fruits } // If the fruit has hit something and bounced back or is stable, it's no longer in safety period if (fruits[i].vy <= 0) { fruits[i].safetyPeriod = true; // Mark that we've checked and it's now unsafe } } // Only trigger game over if the fruit is not in safety period if (fruits[i].safetyPeriod) { // Trigger game over when a fruit touches the line after having bounced/settled gameOver = true; LK.showGameOver(); return; } } // Only check for game over if fruit is high AND has had time to settle // Use the fruit's height to determine proper position check var fruitRadius = fruits[i].height / 2; if (fruits[i].y < 300 + fruitRadius && !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; } } } } // Create and setup the pineapple function setupPineapple() { pineapple = new Fruit(FruitTypes.PINEAPPLE); pineapple.x = -pineapple.width / 2; // Start completely off screen pineapple.y = 200; // Position 200 pixels higher than before pineapple.isStatic = true; // Make it static until it's dropped pineappleActive = false; // Not active in gameplay yet pineapplePushCount = 0; // Reset push count game.addChild(pineapple); } // Function to push the pineapple function pushPineapple() { // Only push if not already active in gameplay if (!pineappleActive) { pineapplePushCount++; // Calculate new x position (100px per push instead of 200px) var newX = -pineapple.width / 2 + pineapplePushCount * 70; // Animate the push tween(pineapple, { x: newX }, { duration: 300, easing: tween.bounceOut }); // Check if we've pushed the pineapple 7 times if (pineapplePushCount >= 7) { pineappleActive = true; // Mark as ready to drop on next fruit release } } } // Initialize game function initGame() { LK.setScore(0); gameOver = false; fruits = []; chargeCounter = 0; chargedBalls = []; lastScoreCheckForCoconut = 0; // Start background music LK.playMusic('bgmusic'); // Setup game elements setupBoundaries(); setupUI(); setupPineapple(); // Setup the pineapple updateScoreDisplay(); createNextFruit(); } // Function to spawn coconut from bottom of screen function spawnCoconut() { var coconut = new Fruit(FruitTypes.COCONUT); // Randomly position coconut horizontally within game area var minX = wallLeft.x + wallLeft.width / 2 + coconut.width / 2 + 50; var maxX = wallRight.x - wallRight.width / 2 - coconut.width / 2 - 50; coconut.x = minX + Math.random() * (maxX - minX); // Position below the screen coconut.y = gameHeight + coconut.height / 2; // Make it static while animating into place coconut.isStatic = true; // Play the Stonks sound when coconut appears LK.getSound('stonks').play(); // Add to game and fruits array game.addChild(coconut); fruits.push(coconut); // Mark as in safety period coconut.safetyPeriod = false; // Make it immune to game over for a second coconut.immuneToGameOver = true; // Use tween to smoothly animate the coconut entering the screen from below // Calculate target Y position where the coconut is fully in the board var targetY = gameHeight - gameFloor.height / 2 - coconut.height / 2 - 10; // Animate entry with an easeOut effect and gradually increasing speed tween(coconut, { y: targetY }, { duration: 1200, // 1.2 seconds for a faster entry easing: tween.easeIn, // Start slow and speed up onFinish: function onFinish() { // Once fully entered, make it dynamic so it can interact with other fruits coconut.isStatic = false; // Give it a small upward push to make it bounce slightly when it enters coconut.vy = -2; // Add random horizontal velocity for natural movement coconut.vx = (Math.random() * 2 - 1) * 1.5; // Start 1-second timer to enable game over contact LK.setTimeout(function () { if (coconut && fruits.includes(coconut)) { coconut.immuneToGameOver = false; } }, 1000); } }); } // Track last score checked for coconut spawn var lastScoreCheckForCoconut = 0; // Event handlers game.down = function (x, y) { // We don't need to check specific boundaries to start dragging. // As long as there's an active fruit, we can start dragging. if (activeFruit) { isDragging = true; // Update active fruit position immediately game.move(x, y); } }; // Mouse or touch move on game object game.move = function (x, y) { if (isDragging && activeFruit) { // Only move the active fruit on the X axis - use actual fruit width var fruitRadius = activeFruit.width / 2; var minX = wallLeft.x + wallLeft.width / 2 + fruitRadius; var maxX = wallRight.x - wallRight.width / 2 - fruitRadius; activeFruit.x = Math.max(minX, Math.min(maxX, x)); } }; // Mouse or touch up on game object game.up = function () { if (isDragging && activeFruit) { dropFruit(); } isDragging = false; }; // Game update loop game.update = function () { // Check if we've reached a new 500-point threshold var currentScore = LK.getScore(); if (Math.floor(currentScore / 500) > Math.floor(lastScoreCheckForCoconut / 500)) { // Spawn a coconut for every 500 points spawnCoconut(); } lastScoreCheckForCoconut = currentScore; // We no longer need to check if pineapple is in the board // as we now use push count to determine when it's ready // Apply physics and check collisions for each fruit for (var i = fruits.length - 1; i >= 0; i--) { var fruit = fruits[i]; if (fruit.isStatic || fruit.merging) { continue; } // Store last position for boundary checks if (fruit.lastY === undefined) { fruit.lastY = fruit.y; } if (fruit.lastX === undefined) { fruit.lastX = fruit.x; } // Apply gravity fruit.vy += fruit.gravity; // Apply velocity fruit.x += fruit.vx; fruit.y += fruit.vy; // Apply rotation fruit.rotation += fruit.angularVelocity; // Apply friction fruit.vx *= fruit.friction; fruit.vy *= fruit.friction; // Apply angular friction fruit.angularVelocity *= fruit.angularFriction; // Force fruits to stop rotating when they're barely moving if (Math.abs(fruit.vx) < 0.1 && Math.abs(fruit.vy) < 0.1 && Math.abs(fruit.angularVelocity) < 0.03) { fruit.angularVelocity = 0; } // Apply stronger angular friction when moving slowly if (Math.abs(fruit.vx) < 0.8 && Math.abs(fruit.vy) < 0.8) { fruit.angularVelocity *= 0.9; } // Clamp angular velocity fruit.angularVelocity = Math.min(Math.max(fruit.angularVelocity, -fruit.maxAngularVelocity), fruit.maxAngularVelocity); // Wall collision - use actual fruit width for accurate collision var fruitRadius = fruit.width / 2; // Use actual width of the asset if (fruit.x < wallLeft.x + wallLeft.width / 2 + fruitRadius) { fruit.x = wallLeft.x + wallLeft.width / 2 + fruitRadius; fruit.vx = -fruit.vx * fruit.elasticity; // Smaller fruits get more angular velocity from impacts var angularImpactMultiplier = 0.005 * (1 + (0.9 - fruit.elasticity) * 5); fruit.angularVelocity += fruit.vy * angularImpactMultiplier * (fruit.vx / Math.abs(fruit.vx || 1)); // Apply angular velocity based on vertical velocity and direction of impact // Apply wall friction - stronger when in wall contact and proportional to fruit size var wallFriction = 0.65 + (fruit.elasticity - 0.7) * 0.5; // More elastic (smaller) fruits get less wall friction fruit.angularVelocity *= wallFriction; fruit.angularVelocity *= fruit.groundAngularFriction; if (Math.abs(fruit.vx) > 1) { // Bounce sound removed } } else if (fruit.x > wallRight.x - wallRight.width / 2 - fruitRadius) { fruit.x = wallRight.x - wallRight.width / 2 - fruitRadius; fruit.vx = -fruit.vx * fruit.elasticity; // Smaller fruits get more angular velocity from impacts var angularImpactMultiplier = 0.005 * (1 + (0.9 - fruit.elasticity) * 5); fruit.angularVelocity -= fruit.vy * angularImpactMultiplier * (fruit.vx / Math.abs(fruit.vx || 1)); // Apply angular velocity based on vertical velocity and direction of impact // Apply wall friction - stronger when in wall contact and proportional to fruit size var wallFriction = 0.65 + (fruit.elasticity - 0.7) * 0.5; // More elastic (smaller) fruits get less wall friction fruit.angularVelocity *= wallFriction; fruit.angularVelocity *= fruit.groundAngularFriction; if (Math.abs(fruit.vx) > 1) { // Bounce sound removed } } // Floor collision - use actual fruit height for accurate collision var fruitRadius = fruit.height / 2; // Use actual height of the asset if (fruit.y > gameFloor.y - gameFloor.height / 2 - fruitRadius) { fruit.y = gameFloor.y - gameFloor.height / 2 - fruitRadius; fruit.vy = -fruit.vy * fruit.elasticity; if (Math.abs(fruit.vx) > 0.5) { // Smaller fruits get more angular velocity from impacts var angularImpactMultiplier = 0.01 * (1 + (0.9 - fruit.elasticity) * 5); fruit.angularVelocity += fruit.vx * angularImpactMultiplier * (fruit.vy / Math.abs(fruit.vy || 1)); // Apply angular velocity based on horizontal velocity and direction of impact } // Smaller fruits should spin longer after impact var angularDamping = fruit.elasticity > 0.85 ? 0.85 : fruit.groundAngularFriction; fruit.angularVelocity *= angularDamping; // Smaller fruits take more time to come to rest var restThreshold = 1 + (fruit.elasticity - 0.7) * 10; if (Math.abs(fruit.vy) < restThreshold) { fruit.vy = 0; } // Angular rest threshold should also scale with elasticity var angularRestThreshold = 0.03 * (1 - (fruit.elasticity - 0.7) * 2); if (Math.abs(fruit.angularVelocity) < angularRestThreshold) { fruit.angularVelocity = 0; } if (Math.abs(fruit.vy) > 1) { // Bounce sound removed } } // Update last positions fruit.lastX = fruit.x; fruit.lastY = fruit.y; } // Check for fruit collisions checkFruitCollisions(); // Check game over conditions checkGameOver(); }; // Initialize the game initGame();
===================================================================
--- original.js
+++ change.js
@@ -486,9 +486,9 @@
var apple = new Fruit(FruitTypes.APPLE);
// Distribute the balls across the width of the game
var offset = i * (gameWidth - 300) / 5 + 150;
apple.x = offset;
- apple.y = dropPointY + 100; // Position 100 pixels lower
+ apple.y = dropPointY - 100; // Position 100 pixels lower
// Make it dynamic so it drops
apple.isStatic = false;
// Add random horizontal velocity and downward vertical velocity
var angle = (Math.random() * 20 - 10) * (Math.PI / 180);