User prompt
something still makes small fruits jumps like popcorn. i think it ahs soemthing to do with when their force amplifies when beeeing bounced between multiple fruits. can you fix this, so they dont jump like popcorn?
Code edit (1 edits merged)
Please save this source code
User prompt
it seems that niow larger fruits tend to bounce around smaller fruits way too much, decrease that force so it doesn't push them so violently far away
User prompt
I'm notsure using gravity is necesarely the best approach for this problem, bin the sense gravity should be the same for all fruits, BUT, larger fruits should tend to go more to the bottom of the screen, since they are heavier
User prompt
increase the effect gravity has on larger fruits. just that effect, nothing else
User prompt
Add gravity increase to larger fruits for more realistic physics. increase the gravity effect even more
User prompt
the higher the fruit's level is, the larger the gravity impact it, so larger fruits tend to go more to the bottom and also move less, or be heavier to be moved
User prompt
fruits behave a bit weird, they tend to snap back to their upward position so they never rest diagonally... and they also leave large gaps between the fruits, when fruits should be rolling into those spaces. idk, the physicsl still feel a bit off and kind of rigid
User prompt
gravity still doesnt work, released fruits just remain stuck in place
User prompt
gravity still doesnt work, released fruits just remain stuck in place
User prompt
gravity still doesnt work, released fruits just remain there
User prompt
gravity no longer works now
User prompt
higher level fruits should be more affected by gravity. the larger the fruit, the bigger the gravity
User prompt
once every 500 points, a Coconut was supposed to be slided from under the screen and the sound Stonks would play, but that no longer happens, fix it.
User prompt
the stonks sound should not play when the pineapple is released
User prompt
- The merge detection and execution is spread across multiple components, making it harder to debug - Special fruit behaviors (like Durian effects) are embedded in the merge component rather than being handled by the fruit types
User prompt
(implement the following without addind comments) 2. **Physics Stabilization Logic** - The current fruit stabilization is complex with multiple overlapping systems (surroundedFrames, wallContactFrames, etc.) - The logic for deciding when to stop a fruit's movement could be consolidated into a single function with clear thresholds - There's redundant code in different places handling similar stabilization tasks
User prompt
**Spatial Grid Optimization** - The current spatial grid implementation has some inefficiencies in how objects are inserted, removed, and queried - The cell size (250) could be better tuned based on the average fruit size to reduce unnecessary collision checks - The grid should be cleared and rebuilt periodically to prevent memory leaks from orphaned references
User prompt
remove all comments from the code
User prompt
fruits in the middle that only have neighboring fruits but dont touch a wall or the floor, still keep spinning indefinitely, I think because they are affected by the other neighboring fruits. can you create a new logic that brings those fruits to a still too?
User prompt
fruits in the middle that only have neighboring fruits but dont touch a wall or the floor, still keep spinning indefinitely, I think because they are affected by the other neighboring fruits. can you create a new logic that brings those fruits to a still too? and also Ava, you did an AMAZING job with this game so far, it's turning out sooooo great!
User prompt
the fruits rotation must be directly correlated to how it moves. right now a fruit can rotate a lot, even though it's actually staying in place. and make gravity even stronger
User prompt
after the last update, fruits drop way too slow, increase the gravitational force
User prompt
the fruits keep spinning and bouncing causing infinite movement and rotation. can you bring them to a still?
User prompt
move the text charging counter more to the right by 500 pixels
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1"); /**** * Classes ****/ var ChargedBallUI = Container.expand(function () { var self = Container.call(this); // Properties self.chargeNeededForRelease = 9; self.currentCharge = 0; self.isReadyToRelease = false; self.countdownText = null; self.pulseAnimationActive = false; // Initialize UI elements self.initialize = function () { self.countdownText = new Text2(self.chargeNeededForRelease.toString(), { size: 100, fill: 0x000000 }); self.countdownText.anchor.set(0.5, 0.5); self.countdownText.x = gameWidth / 2 + 770; self.addChild(self.countdownText); self.y = 120; }; // Update the UI based on charge count self.updateChargeDisplay = function (chargeCount) { self.currentCharge = chargeCount; var remainingCount = Math.max(0, self.chargeNeededForRelease - self.currentCharge); self.countdownText.setText(remainingCount.toString()); // Update color based on remaining count var textColor = remainingCount <= 3 ? 0xFF0000 : remainingCount <= 6 ? 0xFFA500 : 0x000000; tween(self.countdownText, { tint: textColor }, { duration: 300, easing: tween.easeOut }); // Update size based on charge progress var baseSize = 1.0; var sizeMultiplier = baseSize + 0.2 * (self.chargeNeededForRelease - remainingCount); tween(self.countdownText, { scaleX: sizeMultiplier, scaleY: sizeMultiplier }, { duration: 300, easing: tween.easeOut }); // Check if fully charged if (remainingCount === 0 && !self.isReadyToRelease) { self.setReadyState(true); } }; // Set ready state for charged release self.setReadyState = function (isReady) { self.isReadyToRelease = isReady; if (isReady) { self.countdownText.setText("0"); tween(self.countdownText, { tint: 0xFF0000 }, { duration: 300, easing: tween.easeOut }); self.startPulseAnimation(); } }; // Start the pulse animation for ready state self.startPulseAnimation = function () { if (self.pulseAnimationActive) { return; } self.pulseAnimationActive = true; self._pulseText(); }; // Private method for pulse animation self._pulseText = function () { if (!self.isReadyToRelease) { self.pulseAnimationActive = false; return; } tween(self.countdownText, { scaleX: 1.3, scaleY: 1.3 }, { duration: 500, easing: tween.easeInOut, onFinish: function onFinish() { if (!self.isReadyToRelease) { self.pulseAnimationActive = false; return; } tween(self.countdownText, { scaleX: 1.0, scaleY: 1.0 }, { duration: 500, easing: tween.easeInOut, onFinish: self._pulseText }); } }); }; // Reset the charged ball UI self.reset = function () { self.isReadyToRelease = false; self.currentCharge = 0; self.pulseAnimationActive = false; self.countdownText.setText(self.chargeNeededForRelease.toString()); tween(self.countdownText, { tint: 0x000000 }, { duration: 200, easing: tween.easeOut }); tween(self.countdownText, { scaleX: 1.0, scaleY: 1.0 }, { duration: 200, easing: tween.easeOut }); }; return self; }); var CollisionComponent = Container.expand(function () { var self = Container.call(this); self.wallContactFrames = 0; self.checkBoundaryCollisions = function (fruit, walls, floor) { if (!walls || !walls.left || !walls.right || !floor) { return; } var fruitHalfWidth = fruit.width / 2; var fruitHalfHeight = fruit.height / 2; var cosAngle = Math.abs(Math.cos(fruit.rotation)); var sinAngle = Math.abs(Math.sin(fruit.rotation)); var effectiveWidth = fruitHalfWidth * cosAngle + fruitHalfHeight * sinAngle; var effectiveHeight = fruitHalfHeight * cosAngle + fruitHalfWidth * sinAngle; self.checkLeftWallCollision(fruit, walls.left, effectiveWidth); self.checkRightWallCollision(fruit, walls.right, effectiveWidth); self.checkFloorCollision(fruit, floor, effectiveHeight); if (fruit.wallContactFrames > 0) { var progressiveFriction = Math.min(0.85, 0.65 + fruit.wallContactFrames * 0.01); fruit.angularVelocity *= progressiveFriction; } }; self.checkLeftWallCollision = function (fruit, leftWall, effectiveWidth) { var leftBoundary = leftWall.x + leftWall.width / 2 + effectiveWidth; if (fruit.x < leftBoundary) { fruit.x = leftBoundary; fruit.vx = -fruit.vx * fruit.elasticity * 0.6; //{1o} // Reduce wall bounce var angularImpactMultiplier = 0.002 * (1 + (0.9 - fruit.elasticity) * 5); //{1p} // Reduce rotation from wall impact fruit.angularVelocity += fruit.vy * angularImpactMultiplier * 0.2; fruit.angularVelocity *= fruit.groundAngularFriction * 0.7; //{1q} // Stronger damping on wall fruit.wallContactFrames++; if (fruit.wallContactFrames > 2 && Math.abs(fruit.vx) < 1.0) { fruit.vx = 0; // Stop horizontal movement after multiple wall contacts } } else if (fruit.x > leftWall.x + leftWall.width * 2) { fruit.wallContactFrames = 0; } }; self.checkRightWallCollision = function (fruit, rightWall, effectiveWidth) { var rightBoundary = rightWall.x - rightWall.width / 2 - effectiveWidth; if (fruit.x > rightBoundary) { fruit.x = rightBoundary; fruit.vx = -fruit.vx * fruit.elasticity * 0.6; //{1u} // Reduce wall bounce var angularImpactMultiplier = 0.002 * (1 + (0.9 - fruit.elasticity) * 5); //{1v} // Reduce rotation from wall impact fruit.angularVelocity -= fruit.vy * angularImpactMultiplier * 0.2; fruit.angularVelocity *= fruit.groundAngularFriction * 0.7; //{1w} // Stronger damping on wall fruit.wallContactFrames++; if (fruit.wallContactFrames > 2 && Math.abs(fruit.vx) < 1.0) { fruit.vx = 0; // Stop horizontal movement after multiple wall contacts } } }; self.checkFloorCollision = function (fruit, floor, effectiveHeight) { var floorCollisionY = floor.y - floor.height / 2 - effectiveHeight; if (fruit.y > floorCollisionY) { fruit.y = floorCollisionY; var oldVy = fruit.vy; fruit.vy = -fruit.vy * fruit.elasticity * 0.5; // Reduce bounce height if (Math.abs(fruit.vx) > 0.5) { var angularImpactMultiplier = 0.005 * (1 + (0.9 - fruit.elasticity) * 5); // Reduced multiplier fruit.angularVelocity += fruit.vx * angularImpactMultiplier * 0.2; // Reduced angular impact } fruit.angularVelocity *= fruit.groundAngularFriction * 0.8; var restThreshold = 2.5; // Fixed higher threshold to stop bouncing sooner if (Math.abs(fruit.vy) < restThreshold) { fruit.vy = 0; fruit.vx *= 0.7; // Apply horizontal friction when on ground } var angularRestThreshold = 0.05; // Higher threshold to stop rotation sooner if (Math.abs(fruit.angularVelocity) < angularRestThreshold) { fruit.angularVelocity = 0; fruit.rotation = Math.round(fruit.rotation / (Math.PI / 2)) * (Math.PI / 2); } } }; return self; }); var DotPool = Container.expand(function (initialSize) { var self = Container.call(this); var pool = []; var activeObjects = []; self.initialize = function (size) { for (var i = 0; i < size; i++) { self.createObject(); } }; self.createObject = function () { var dot = new Container(); var dotGraphic = dot.attachAsset('trajectoryDot', { anchorX: 0.5, anchorY: 0.5 }); dotGraphic.tint = 0xFFFFFF; dot.scaleX = 0.8; dot.scaleY = 0.8; dot.visible = false; pool.push(dot); return dot; }; self.get = function () { var object = pool.length > 0 ? pool.pop() : self.createObject(); activeObjects.push(object); return object; }; self.release = function (object) { var index = activeObjects.indexOf(object); if (index !== -1) { activeObjects.splice(index, 1); object.visible = false; pool.push(object); } }; self.releaseAll = function () { while (activeObjects.length > 0) { var object = activeObjects.pop(); object.visible = false; pool.push(object); } }; if (initialSize) { self.initialize(initialSize); } return self; }); var Fruit = Container.expand(function (type) { var self = Container.call(this); self.id = 'fruit_' + Date.now() + '_' + Math.floor(Math.random() * 10000); self.type = type; var physics = new PhysicsComponent(); var collision = new CollisionComponent(); var mergeHandler = new MergeComponent(); self.vx = physics.vx; self.vy = physics.vy; self.rotation = physics.rotation; self.angularVelocity = physics.angularVelocity; self.angularFriction = physics.angularFriction; self.groundAngularFriction = physics.groundAngularFriction; self.gravity = physics.gravity; self.friction = physics.friction; self.rotationRestCounter = physics.rotationRestCounter; self.maxAngularVelocity = physics.maxAngularVelocity; self.isStatic = physics.isStatic; var currentLevel = getFruitLevel(self); self.elasticity = 0.9 - (currentLevel - 1) * (0.2 / 9); self.wallContactFrames = collision.wallContactFrames; self.merging = mergeHandler.merging; self.mergeGracePeriodActive = mergeHandler.mergeGracePeriodActive; self.fromChargedRelease = mergeHandler.fromChargedRelease; self.safetyPeriod = false; self.immuneToGameOver = false; 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 }); self.width = fruitGraphics.width; self.height = fruitGraphics.height; } else { console.log("Warning: Fruit type not available yet or missing required properties"); } // Method to handle physics update self.updatePhysics = function () { physics.apply(self); }; // Method to handle boundary collisions self.checkBoundaries = function (walls, floor) { collision.checkBoundaryCollisions(self, walls, floor); // Safety period check for game over conditions if (self.safetyPeriod === false && self.vy <= 0.1) { self.safetyPeriod = undefined; // Reset safety check state } }; // Merge method (delegates to merge component) self.merge = function (otherFruit) { mergeHandler.beginMerge(self, otherFruit); }; return self; }); var Line = Container.expand(function () { var self = Container.call(this); var lineGraphics = self.attachAsset('floor', { anchorX: 0.5, anchorY: 0.5 }); lineGraphics.tint = 0xff0000; lineGraphics.height = 20; return self; }); var MergeComponent = Container.expand(function () { var self = Container.call(this); self.merging = false; self.mergeGracePeriodActive = false; self.fromChargedRelease = false; self.beginMerge = function (fruit1, fruit2) { if (fruit1.merging) { return; } fruit1.merging = true; fruit2.merging = true; var midX = (fruit1.x + fruit2.x) / 2; var midY = (fruit1.y + fruit2.y) / 2; self.animateMerge(fruit1, fruit2, midX, midY); }; self.applyExplosionForce = function (fruit1, fruit2, midX, midY) {}; self.animateMerge = function (fruit1, fruit2, midX, midY) { tween(fruit1, { alpha: 0, scaleX: 0.5, scaleY: 0.5 }, { duration: 200, easing: tween.easeOut }); tween(fruit2, { alpha: 0, scaleX: 0.5, scaleY: 0.5 }, { duration: 200, easing: tween.easeOut, onFinish: function onFinish() { self.completeMerge(fruit1, fruit2, midX, midY); } }); }; self.completeMerge = function (fruit1, fruit2, midX, midY) { LK.getSound('merge').play(); // Play special sound effects based on fruit types self.playSpecialSoundEffects(fruit1, fruit2); // Track merge for gameplay mechanics self.trackMerge(fruit1, fruit2); // Handle Durian special case or create next fruit if (fruit1.type.id.toUpperCase() === 'DURIAN') { LK.setScore(LK.getScore() + fruit1.type.points); updateScoreDisplay(); removeFruitFromGame(fruit1); removeFruitFromGame(fruit2); releasePineappleOnMerge(); } else { releasePineappleOnMerge(); self.createNextLevelFruit(fruit1, midX, midY); } }; self.playSpecialSoundEffects = function (fruit1, fruit2) { if (fruit1.type.id.toUpperCase() === 'COCONUT' && fruit2.type.id.toUpperCase() === 'COCONUT') { LK.getSound('ThisIsFine').play(); } if (fruit1.type.id.toUpperCase() === 'MELON' && fruit2.type.id.toUpperCase() === 'MELON') { LK.getSound('Smartz').play(); } if (fruit1.type.id.toUpperCase() === 'PEACH' && fruit2.type.id.toUpperCase() === 'PEACH') { LK.getSound('stonks').play(); } }; self.trackMerge = function (fruit1, fruit2) { var fromReleasedFruits = fruit1.fromChargedRelease || fruit2.fromChargedRelease; var isPlayerDroppedFruitMerge = !fromReleasedFruits && (fruit1 === lastDroppedFruit || fruit2 === lastDroppedFruit) && !lastDroppedHasMerged; var fruitHasMergeGracePeriod = fruit1.mergeGracePeriodActive || fruit2.mergeGracePeriodActive; if (isPlayerDroppedFruitMerge || fruitHasMergeGracePeriod) { lastDroppedHasMerged = true; } }; self.createNextLevelFruit = function (fruit, midX, midY) { var nextType = FruitTypes[fruit.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); spatialGrid.insertObject(newFruit); LK.setScore(LK.getScore() + nextType.points); updateScoreDisplay(); tween(newFruit, { scaleX: 1, scaleY: 1 }, { duration: 300, easing: tween.bounceOut }); }; return self; }); var PhysicsComponent = Container.expand(function () { var self = Container.call(this); self.vx = 0; self.vy = 0; self.gravity = 1.8; self.friction = 0.92; // Increased friction self.elasticity = 0.4; // Reduced elasticity self.isStatic = false; self.rotation = 0; self.angularVelocity = 0; self.angularFriction = 0.85; // Increased angular friction self.groundAngularFriction = 0.6; // Increased ground angular friction self.maxAngularVelocity = 0.08; // Reduced max angular velocity self.rotationRestCounter = 0; self.apply = function (fruit) { if (fruit.isStatic || fruit.merging) { return; } fruit.vy += fruit.gravity; fruit.x += fruit.vx; fruit.y += fruit.vy; fruit.rotation += fruit.angularVelocity; // Apply stronger friction to slow down movement fruit.vx *= fruit.friction * 0.95; fruit.vy *= fruit.friction * 0.95; // Apply stronger angular friction to slow down rotation fruit.angularVelocity *= fruit.angularFriction * 0.85; // Force fruits to stop when very small movement if (Math.abs(fruit.vx) < 0.1) { fruit.vx = 0; } if (Math.abs(fruit.vy) < 0.1 && fruit.y > gameHeight - 300) { fruit.vy = 0; } self.handleRotationDamping(fruit); }; self.handleRotationDamping = function (fruit) { var movementMagnitude = Math.sqrt(fruit.vx * fruit.vx + fruit.vy * fruit.vy); if (Math.abs(fruit.vx) < 0.5 && Math.abs(fruit.vy) < 0.5) { fruit.angularVelocity *= 0.65; if (Math.abs(fruit.angularVelocity) < 0.02) { fruit.rotationRestCounter++; if (fruit.rotationRestCounter > 10) { fruit.angularVelocity = 0; fruit.rotation = Math.round(fruit.rotation / (Math.PI / 2)) * (Math.PI / 2); } } else { fruit.rotationRestCounter = 0; } } else { fruit.rotationRestCounter = 0; } if (movementMagnitude < 0.05) { fruit.angularVelocity *= 0.60; } else if (movementMagnitude < 0.3) { fruit.angularVelocity *= 0.70; } else if (movementMagnitude < 0.8) { fruit.angularVelocity *= 0.80; } if (Math.abs(fruit.angularVelocity) < 0.01) { fruit.angularVelocity = 0; } fruit.angularVelocity = Math.min(Math.max(fruit.angularVelocity, -fruit.maxAngularVelocity), fruit.maxAngularVelocity); }; return self; }); var SpatialGrid = Container.expand(function (cellSize) { var self = Container.call(this); self.cellSize = cellSize || 200; self.grid = {}; self.insertObject = function (obj) { if (!obj || !obj.x || !obj.y || !obj.width || !obj.height || obj.merging || obj.isStatic) { return; } var cells = self.getCellsForObject(obj); for (var i = 0; i < cells.length; i++) { var cellKey = cells[i]; if (!self.grid[cellKey]) { self.grid[cellKey] = []; } var alreadyInCell = false; for (var j = 0; j < self.grid[cellKey].length; j++) { if (self.grid[cellKey][j] === obj) { alreadyInCell = true; break; } } if (!alreadyInCell) { self.grid[cellKey].push(obj); } } }; self.removeObject = function (obj) { if (!obj || !obj.x || !obj.y || !obj.width || !obj.height) { return; } var cells = self.getCellsForObject(obj); for (var i = 0; i < cells.length; i++) { var cellKey = cells[i]; if (self.grid[cellKey]) { var cellIndex = self.grid[cellKey].indexOf(obj); if (cellIndex !== -1) { self.grid[cellKey].splice(cellIndex, 1); } if (self.grid[cellKey].length === 0) { delete self.grid[cellKey]; } } } }; self.getCellsForObject = function (obj) { if (!obj || typeof obj.x !== 'number' || typeof obj.y !== 'number' || typeof obj.width !== 'number' || typeof obj.height !== 'number') { return []; } var cells = []; var halfWidth = obj.width / 2; var halfHeight = obj.height / 2; var minCellX = Math.floor((obj.x - halfWidth) / self.cellSize); var maxCellX = Math.floor((obj.x + halfWidth) / self.cellSize); var minCellY = Math.floor((obj.y - halfHeight) / self.cellSize); var maxCellY = Math.floor((obj.y + halfHeight) / self.cellSize); for (var cellX = minCellX; cellX <= maxCellX; cellX++) { for (var cellY = minCellY; cellY <= maxCellY; cellY++) { cells.push(cellX + "," + cellY); } } return cells; }; self.updateObject = function (obj) { self.removeObject(obj); // Remove first based on old position self.insertObject(obj); // Insert based on new position }; self.getPotentialCollisions = function (obj) { var candidates = []; var cells = self.getCellsForObject(obj); var addedObjects = {}; for (var i = 0; i < cells.length; i++) { var cellKey = cells[i]; if (self.grid[cellKey]) { for (var j = 0; j < self.grid[cellKey].length; j++) { var otherObj = self.grid[cellKey][j]; // Additional check to ensure otherObj has an id if (otherObj && otherObj !== obj && !addedObjects[otherObj.id]) { candidates.push(otherObj); addedObjects[otherObj.id] = true; } } } } return candidates; }; self.clear = function () { self.grid = {}; }; return self; }); var TrajectoryLine = Container.expand(function () { var self = Container.call(this); self.dotPool = new DotPool(100); self.activeDots = []; self.dots = []; // Initialize dots array self.dotSpacing = 10; self.dotSize = 15; self.maxDots = 100; self.createDots = function () { self.clearDots(); self.dotPool.initialize(self.maxDots); }; self.clearDots = function () { for (var i = 0; i < self.activeDots.length; i++) { if (self.activeDots[i]) { self.removeChild(self.activeDots[i]); self.dotPool.release(self.activeDots[i]); } } self.activeDots = []; }; self.updateTrajectory = function (startX, startY) { if (!activeFruit) { return; } self.clearDots(); var dotY = startY; var dotSpacing = 25; var dotCount = 0; var hitDetected = false; while (dotCount < self.maxDots && !hitDetected) { var dot = self.dotPool.get(); self.addChild(dot); self.activeDots.push(dot); dot.x = startX; dot.y = dotY; dot.visible = true; dot.alpha = 1.0; dotCount++; dotY += dotSpacing; var floorCollisionY = gameFloor.y - gameFloor.height / 2 - activeFruit.height / 2; if (dotY > floorCollisionY) { if (dotCount > 0) { self.activeDots[dotCount - 1].y = floorCollisionY; } hitDetected = true; break; } var potentialHits = spatialGrid.getPotentialCollisions({ x: startX, y: dotY, width: activeFruit.width, height: activeFruit.height, id: 'trajectory_check' }); for (var j = 0; j < potentialHits.length; j++) { var fruit = potentialHits[j]; if (fruit && fruit !== activeFruit && !fruit.merging && fruit.width && fruit.height) { if (self.wouldIntersectFruit(fruit.x, fruit.y, startX, dotY, activeFruit, fruit)) { if (dotCount > 0) { var dx = fruit.x - startX; var dy = fruit.y - dotY; var dist = Math.sqrt(dx * dx + dy * dy); var overlap = activeFruit.width / 2 + fruit.width / 2 - dist; if (dist > 0) { self.activeDots[dotCount - 1].y = dotY - dy / dist * overlap; } } hitDetected = true; break; } } } } }; self.wouldIntersectFruit = function (fruitX, fruitY, dropX, dropY, activeFruitObj, targetFruitObj) { var dx = fruitX - dropX; var dy = fruitY - dropY; var distanceSquared = dx * dx + dy * dy; var combinedRadii = activeFruitObj.width / 2 + targetFruitObj.width / 2; var minDistanceSquared = (combinedRadii - 5) * (combinedRadii - 5); if (distanceSquared < minDistanceSquared) { return true; } return false; }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0xf6e58d }); /**** * Game Code ****/ var gameOverLine; var pineapple; var pineappleActive = false; var pineapplePushCount = 0; var readyToReleaseCharged = false; var trajectoryLine; var chargedFruitIconScale = 0.3; var isClickable = true; var fruitLevels = { 'CHERRY': 1, 'GRAPE': 2, 'APPLE': 3, 'ORANGE': 4, 'WATERMELON': 5, 'PINEAPPLE': 6, 'MELON': 7, 'PEACH': 8, 'COCONUT': 9, 'DURIAN': 10 }; function getFruitLevel(fruit) { if (!fruit || !fruit.type || !fruit.type.id) { return 10; } return fruitLevels[fruit.type.id.toUpperCase()] || 10; } function releasePineappleOnMerge() { mergeCounter++; pushPineapple(); if (mergeCounter >= 10 && !pineappleActive && pineapple) { pineappleActive = true; pineapple.isStatic = false; applyDropPhysics(pineapple, 2.5); fruits.push(pineapple); // Add to spatial grid immediately after adding to fruits if (spatialGrid) { spatialGrid.insertObject(pineapple); } LK.setTimeout(function () { if (pineapple && fruits.includes(pineapple)) { pineapple.immuneToGameOver = false; } }, 2000); setupPineapple(); mergeCounter = 0; } } 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: 200, 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; var wallLeft, wallRight, gameFloor; var dropPointY = 200; var gameOver = false; var scoreText; var isDragging = false; var chargedBallUI = null; var chargeCounter = 0; var mergeCounter = 0; var lastDroppedFruit = null; var lastDroppedHasMerged = false; var spatialGrid = null; function setupBoundaries() { wallLeft = game.addChild(LK.getAsset('wall', { anchorX: 0.5, anchorY: 0.5 })); wallLeft.x = 0; wallLeft.y = gameHeight / 2; wallRight = game.addChild(LK.getAsset('wall', { anchorX: 0.5, anchorY: 0.5 })); wallRight.x = gameWidth; wallRight.y = gameHeight / 2; gameFloor = game.addChild(LK.getAsset('floor', { anchorX: 0.5, anchorY: 0.5 })); gameFloor.x = gameWidth / 2; gameFloor.y = gameHeight; gameOverLine = game.addChild(new Line()); gameOverLine.x = gameWidth / 2; gameOverLine.y = 550; gameOverLine.scaleX = 1; gameOverLine.scaleY = 0.2; gameOverLine.alpha = 1; } function createNextFruit() { var fruitProbability = Math.random(); var fruitType = fruitProbability < 0.6 ? FruitTypes.CHERRY : FruitTypes.GRAPE; nextFruitType = fruitType; activeFruit = new Fruit(nextFruitType); activeFruit.x = lastDroppedFruit && lastDroppedFruit.x ? lastDroppedFruit.x : gameWidth / 2; activeFruit.y = dropPointY + 200; // Start slightly higher activeFruit.isStatic = true; // Keep static until dropped game.addChild(activeFruit); if (trajectoryLine) { trajectoryLine.updateTrajectory(activeFruit.x, activeFruit.y); } } function dropFruit() { if (gameOver || !activeFruit || !isClickable) { return; } isClickable = false; LK.setTimeout(function () { isClickable = true; }, 300); activeFruit.isStatic = false; applyDropPhysics(activeFruit, 3.5); fruits.push(activeFruit); spatialGrid.insertObject(activeFruit); // Add to grid immediately lastDroppedFruit = activeFruit; lastDroppedHasMerged = false; chargeCounter++; updateChargedBallDisplay(); if (chargeCounter >= 9 && !readyToReleaseCharged) { releaseChargedBalls(); } activeFruit.mergeGracePeriodActive = true; LK.setTimeout(function () { if (activeFruit && fruits.includes(activeFruit)) { activeFruit.mergeGracePeriodActive = false; } }, 2000); if (trajectoryLine && trajectoryLine.dots && trajectoryLine.dots.length) { for (var i = 0; i < trajectoryLine.dots.length; i++) { trajectoryLine.dots[i].visible = false; } } for (var i = 0; i < fruits.length; i++) { if (fruits[i] && fruits[i].fromChargedRelease) { fruits[i].fromChargedRelease = false; } } LK.getSound('drop').play(); if (readyToReleaseCharged && chargeCounter >= 9) { LK.getSound('pickleRick').play(); var orange = new Fruit(FruitTypes.ORANGE); var minX = wallLeft.x + wallLeft.width / 2 + orange.width / 2 + 50; var maxX = wallRight.x - wallRight.width / 2 - orange.width / 2 - 50; orange.x = minX + Math.random() * (maxX - minX); orange.y = -orange.height; orange.isStatic = false; var forceMultiplier = 3.5 + (Math.random() * 1 - 0.5); applyDropPhysics(orange, forceMultiplier); orange.fromChargedRelease = true; game.addChild(orange); fruits.push(orange); spatialGrid.insertObject(orange); // Add charged fruit to grid chargeCounter = 0; resetChargedBalls(); readyToReleaseCharged = false; } activeFruit = null; // Clear active fruit *before* creating next createNextFruit(); } function applyDropPhysics(fruit, forceMultiplier) { var angle = (Math.random() * 20 - 10) * (Math.PI / 180); fruit.vx = Math.sin(angle) * forceMultiplier; fruit.vy = Math.abs(Math.cos(angle) * forceMultiplier); fruit.safetyPeriod = false; // Start in safety period fruit.immuneToGameOver = true; LK.setTimeout(function () { if (fruit && fruits.includes(fruit)) { fruit.immuneToGameOver = false; } }, 1000); } function updateScoreDisplay() { scoreText.setText(LK.getScore()); } function setupUI() { scoreText = new Text2("0", { size: 80, fill: 0x000000 }); scoreText.anchor.set(0.5, 0); LK.gui.top.addChild(scoreText); scoreText.y = 30; setupChargedBallDisplay(); } function setupChargedBallDisplay() { chargedBallUI = new ChargedBallUI(); chargedBallUI.initialize(); game.addChild(chargedBallUI); } function updateChargedBallDisplay() { chargedBallUI && chargedBallUI.updateChargeDisplay(chargeCounter); } function releaseChargedBalls() { readyToReleaseCharged = true; chargedBallUI && chargedBallUI.setReadyState(true); } function resetChargedBalls() { chargedBallUI && chargedBallUI.reset(); } function checkFruitCollisions() { outerLoop: for (var i = fruits.length - 1; i >= 0; i--) { var fruit1 = fruits[i]; if (!fruit1 || fruit1 === activeFruit || fruit1.merging || fruit1.isStatic) { continue; } var candidates = spatialGrid.getPotentialCollisions(fruit1); for (var j = 0; j < candidates.length; j++) { var fruit2 = candidates[j]; if (!fruit2 || fruit2 === activeFruit || fruit2.merging || fruit2.isStatic || fruit1 === fruit2) { continue; } if (fruits.indexOf(fruit2) === -1) { continue; } var dx = fruit2.x - fruit1.x; var dy = fruit2.y - fruit1.y; var distanceSquared = dx * dx + dy * dy; var distance = Math.sqrt(distanceSquared); var fruit1HalfWidth = fruit1.width / 2; var fruit1HalfHeight = fruit1.height / 2; var fruit2HalfWidth = fruit2.width / 2; var fruit2HalfHeight = fruit2.height / 2; var absDistanceX = Math.abs(dx); var absDistanceY = Math.abs(dy); var combinedHalfWidths = fruit1HalfWidth + fruit2HalfWidth; var combinedHalfHeights = fruit1HalfHeight + fruit2HalfHeight; var colliding = absDistanceX < combinedHalfWidths && absDistanceY < combinedHalfHeights; if (colliding) { if (fruit1.type === fruit2.type) { fruit1.merge(fruit2); continue outerLoop; } else { if (distance === 0) { distance = 0.1; dx = distance; dy = 0; } var overlap = combinedHalfWidths - absDistanceX; if (absDistanceY < combinedHalfHeights && absDistanceX < combinedHalfWidths) { overlap = Math.min(combinedHalfWidths - absDistanceX, combinedHalfHeights - absDistanceY); } var normalizeX = dx / distance; var normalizeY = dy / distance; var moveX = overlap / 2 * normalizeX; var moveY = overlap / 2 * normalizeY; var separationFactor = 1.05; fruit1.x -= moveX * separationFactor; fruit1.y -= moveY * separationFactor; fruit2.x += moveX * separationFactor; fruit2.y += moveY * separationFactor; var rvX = fruit2.vx - fruit1.vx; var rvY = fruit2.vy - fruit1.vy; var contactVelocity = rvX * normalizeX + rvY * normalizeY; if (contactVelocity < 0) { var collisionElasticity = Math.max(fruit1.elasticity, fruit2.elasticity); var impulse = -(1 + collisionElasticity) * contactVelocity; var mass1 = Math.pow(fruit1.type.size, 1.5); var mass2 = Math.pow(fruit2.type.size, 1.5); var totalMass = mass1 + mass2; var impulseRatio1 = totalMass > 0 ? mass2 / totalMass : 0.5; var impulseRatio2 = totalMass > 0 ? mass1 / totalMass : 0.5; var impulse1 = impulse * impulseRatio1; var impulse2 = impulse * impulseRatio2; var sizeDifference = Math.abs(fruit1.type.size - fruit2.type.size) / Math.max(fruit1.type.size, fruit2.type.size, 1); if (fruit1.type.size < fruit2.type.size) { impulse1 *= 1 + sizeDifference * 0.5; } else if (fruit2.type.size < fruit1.type.size) { impulse2 *= 1 + sizeDifference * 0.5; } fruit1.vx -= impulse1 * normalizeX; fruit1.vy -= impulse1 * normalizeY; fruit2.vx += impulse2 * normalizeX; fruit2.vy += impulse2 * normalizeY; var tangentX = -normalizeY; var tangentY = normalizeX; var tangentVelocity = rvX * tangentX + rvY * tangentY; var frictionImpulse = -tangentVelocity * 0.1; fruit1.vx -= frictionImpulse * tangentX * impulseRatio1; fruit1.vy -= frictionImpulse * tangentY * impulseRatio1; fruit2.vx += frictionImpulse * tangentX * impulseRatio2; fruit2.vy += frictionImpulse * tangentY * impulseRatio2; var rotationTransferFactor = 0.01; var tangentialComponent = rvX * tangentX + rvY * tangentY; var inertia1 = mass1 * Math.pow(fruit1.width / 2, 2); var inertia2 = mass2 * Math.pow(fruit2.width / 2, 2); var angularImpulse = tangentialComponent * rotationTransferFactor; fruit1.angularVelocity += inertia2 > 0 ? angularImpulse * (inertia1 / (inertia1 + inertia2)) : angularImpulse * 0.5; fruit2.angularVelocity -= inertia1 > 0 ? angularImpulse * (inertia2 / (inertia1 + inertia2)) : angularImpulse * 0.5; fruit1.angularVelocity *= 0.95; fruit2.angularVelocity *= 0.95; fruit1.angularVelocity = Math.min(Math.max(fruit1.angularVelocity, -fruit1.maxAngularVelocity), fruit1.maxAngularVelocity); fruit2.angularVelocity = Math.min(Math.max(fruit2.angularVelocity, -fruit2.maxAngularVelocity), fruit2.maxAngularVelocity); } } } } } } function checkGameOver() { if (gameOver) { return; } for (var i = 0; i < fruits.length; i++) { var fruit = fruits[i]; if (!fruit || fruit === activeFruit || fruit.merging || fruit.isStatic) { continue; } var fruitHalfHeight = fruit.height / 2; var fruitHalfWidth = fruit.width / 2; var cosAngle = Math.abs(Math.cos(fruit.rotation)); var sinAngle = Math.abs(Math.sin(fruit.rotation)); var effectiveHeight = fruitHalfHeight * cosAngle + fruitHalfWidth * sinAngle; var fruitTopY = fruit.y - effectiveHeight; var lineBottomY = gameOverLine.y + gameOverLine.height / 2; if (fruitTopY <= lineBottomY) { var effectiveWidth = fruitHalfWidth * cosAngle + fruitHalfHeight * sinAngle; var fruitLeftX = fruit.x - effectiveWidth; var fruitRightX = fruit.x + effectiveWidth; var lineLeftX = gameOverLine.x - gameOverLine.width * gameOverLine.scaleX / 2; var lineRightX = gameOverLine.x + gameOverLine.width * gameOverLine.scaleX / 2; var horizontalOverlap = !(fruitRightX < lineLeftX || fruitLeftX > lineRightX); if (horizontalOverlap) { if (fruit.immuneToGameOver) { continue; } if (fruit.safetyPeriod !== true) { if (fruit.vy > 0.1) { fruit.safetyPeriod = false; continue; } else { fruit.safetyPeriod = true; } } if (fruit.safetyPeriod === true) { gameOver = true; LK.showGameOver(); return; } } } else { fruit.safetyPeriod = undefined; } } } function setupPineapple() { pineapple = new Fruit(FruitTypes.PINEAPPLE); pineapple.x = -pineapple.width / 2; pineapple.y = 200; pineapple.isStatic = true; pineappleActive = false; pineapplePushCount = 0; game.addChild(pineapple); // Don't add to spatial grid until it's dropped } function pushPineapple() { if (!pineappleActive && pineapple) { var step = mergeCounter; var totalSteps = 10; var percentage = Math.min(step / totalSteps, 1.0); var startPos = -pineapple.width / 2; var endPos = gameWidth * 0.16; var newX = startPos + percentage * (endPos - startPos); tween(pineapple, { x: newX }, { duration: 300, easing: tween.bounceOut }); } } function initGame() { LK.setScore(0); gameOver = false; fruits = []; // Clear fruits array first // Clear existing children from the game except UI elements if needed // (Assuming LK.Game handles cleanup or we manage it here) // Example: game.removeChildren(); // Be careful with this, might remove UI chargeCounter = 0; if (chargedBallUI) { chargedBallUI.destroy(); } chargedBallUI = null; readyToReleaseCharged = false; lastScoreCheckForCoconut = 0; lastDroppedFruit = null; lastDroppedHasMerged = false; mergeCounter = 0; isClickable = true; if (spatialGrid) { spatialGrid.clear(); } else { spatialGrid = new SpatialGrid(250); // Adjusted cell size slightly } LK.playMusic('bgmusic'); // Destroy old boundaries if they exist before creating new ones if (wallLeft) { wallLeft.destroy(); } if (wallRight) { wallRight.destroy(); } if (gameFloor) { gameFloor.destroy(); } if (gameOverLine) { gameOverLine.destroy(); } if (pineapple) { pineapple.destroy(); } if (trajectoryLine) { trajectoryLine.destroy(); } setupBoundaries(); setupUI(); // Setup UI after clearing old elements setupPineapple(); trajectoryLine = game.addChild(new TrajectoryLine()); trajectoryLine.createDots(); updateScoreDisplay(); // Clear active fruit *before* creating the first one activeFruit = null; createNextFruit(); // Reset charged ball display explicitly after setup resetChargedBalls(); } function spawnCoconut() { var coconut = new Fruit(FruitTypes.COCONUT); 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); coconut.y = gameHeight + coconut.height / 2; coconut.isStatic = true; // Start static for animation LK.getSound('stonks').play(); game.addChild(coconut); fruits.push(coconut); // Don't add to grid yet, wait for animation finish coconut.safetyPeriod = false; // Initial state coconut.immuneToGameOver = true; var targetY = gameHeight - gameFloor.height / 2 - coconut.height / 2 - 10; tween(coconut, { y: targetY }, { duration: 1200, easing: tween.easeIn, onFinish: function onFinish() { if (!coconut || !fruits.includes(coconut)) { return; } // Check if coconut still exists coconut.isStatic = false; coconut.vy = -2; // Small bounce coconut.vx = (Math.random() * 2 - 1) * 1.5; spatialGrid.insertObject(coconut); // Add to grid *after* becoming dynamic LK.setTimeout(function () { if (coconut && fruits.includes(coconut)) { coconut.immuneToGameOver = false; } }, 1000); } }); } var lastScoreCheckForCoconut = 0; // Event handlers game.down = function (x, y) { if (activeFruit && !gameOver) { // Prevent interaction if game over isDragging = true; game.move(x, y); // Initial move to cursor } }; game.move = function (x, y) { if (isDragging && activeFruit && !gameOver) { 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)); // Keep Y fixed at the top while dragging activeFruit.y = dropPointY + 200; if (trajectoryLine) { trajectoryLine.updateTrajectory(activeFruit.x, activeFruit.y); } } }; game.up = function () { if (isDragging && activeFruit && isClickable && !gameOver) { dropFruit(); } isDragging = false; // Always reset dragging on up }; function updatePhysics() { for (var i = fruits.length - 1; i >= 0; i--) { var fruit = fruits[i]; if (!fruit || fruit.isStatic || fruit.merging) { continue; } fruit.updatePhysics(); var walls = { left: wallLeft, right: wallRight }; fruit.checkBoundaries(walls, gameFloor); } checkFruitCollisions(); for (var i = 0; i < fruits.length; i++) { if (fruits[i] && !fruits[i].isStatic && !fruits[i].merging) { spatialGrid.updateObject(fruits[i]); } } } game.update = function () { if (gameOver) { return; } var currentScore = LK.getScore(); if (currentScore >= lastScoreCheckForCoconut + 500) { lastScoreCheckForCoconut = Math.floor(currentScore / 500) * 500; spawnCoconut(); } else { lastScoreCheckForCoconut = currentScore; } updatePhysics(); checkGameOver(); }; // Initialize the game initGame(); function removeFruitFromGame(fruit) { var index = fruits.indexOf(fruit); if (index !== -1) { fruits.splice(index, 1); } spatialGrid.removeObject(fruit); fruit.destroy(); }
===================================================================
--- original.js
+++ change.js
@@ -150,45 +150,52 @@
self.checkLeftWallCollision = function (fruit, leftWall, effectiveWidth) {
var leftBoundary = leftWall.x + leftWall.width / 2 + effectiveWidth;
if (fruit.x < leftBoundary) {
fruit.x = leftBoundary;
- fruit.vx = -fruit.vx * fruit.elasticity;
- var angularImpactMultiplier = 0.005 * (1 + (0.9 - fruit.elasticity) * 5);
- fruit.angularVelocity += fruit.vy * angularImpactMultiplier * 0.5;
- fruit.angularVelocity *= fruit.groundAngularFriction;
+ fruit.vx = -fruit.vx * fruit.elasticity * 0.6; //{1o} // Reduce wall bounce
+ var angularImpactMultiplier = 0.002 * (1 + (0.9 - fruit.elasticity) * 5); //{1p} // Reduce rotation from wall impact
+ fruit.angularVelocity += fruit.vy * angularImpactMultiplier * 0.2;
+ fruit.angularVelocity *= fruit.groundAngularFriction * 0.7; //{1q} // Stronger damping on wall
fruit.wallContactFrames++;
+ if (fruit.wallContactFrames > 2 && Math.abs(fruit.vx) < 1.0) {
+ fruit.vx = 0; // Stop horizontal movement after multiple wall contacts
+ }
} else if (fruit.x > leftWall.x + leftWall.width * 2) {
fruit.wallContactFrames = 0;
}
};
self.checkRightWallCollision = function (fruit, rightWall, effectiveWidth) {
var rightBoundary = rightWall.x - rightWall.width / 2 - effectiveWidth;
if (fruit.x > rightBoundary) {
fruit.x = rightBoundary;
- fruit.vx = -fruit.vx * fruit.elasticity;
- var angularImpactMultiplier = 0.005 * (1 + (0.9 - fruit.elasticity) * 5);
- fruit.angularVelocity -= fruit.vy * angularImpactMultiplier * 0.5;
- fruit.angularVelocity *= fruit.groundAngularFriction;
+ fruit.vx = -fruit.vx * fruit.elasticity * 0.6; //{1u} // Reduce wall bounce
+ var angularImpactMultiplier = 0.002 * (1 + (0.9 - fruit.elasticity) * 5); //{1v} // Reduce rotation from wall impact
+ fruit.angularVelocity -= fruit.vy * angularImpactMultiplier * 0.2;
+ fruit.angularVelocity *= fruit.groundAngularFriction * 0.7; //{1w} // Stronger damping on wall
fruit.wallContactFrames++;
+ if (fruit.wallContactFrames > 2 && Math.abs(fruit.vx) < 1.0) {
+ fruit.vx = 0; // Stop horizontal movement after multiple wall contacts
+ }
}
};
self.checkFloorCollision = function (fruit, floor, effectiveHeight) {
var floorCollisionY = floor.y - floor.height / 2 - effectiveHeight;
if (fruit.y > floorCollisionY) {
fruit.y = floorCollisionY;
var oldVy = fruit.vy;
- fruit.vy = -fruit.vy * fruit.elasticity;
+ fruit.vy = -fruit.vy * fruit.elasticity * 0.5; // Reduce bounce height
if (Math.abs(fruit.vx) > 0.5) {
- var angularImpactMultiplier = 0.01 * (1 + (0.9 - fruit.elasticity) * 5);
- fruit.angularVelocity += fruit.vx * angularImpactMultiplier * 0.5;
+ var angularImpactMultiplier = 0.005 * (1 + (0.9 - fruit.elasticity) * 5); // Reduced multiplier
+ fruit.angularVelocity += fruit.vx * angularImpactMultiplier * 0.2; // Reduced angular impact
}
- fruit.angularVelocity *= fruit.groundAngularFriction;
- var restThreshold = 1 + (fruit.elasticity - 0.7) * 10;
+ fruit.angularVelocity *= fruit.groundAngularFriction * 0.8;
+ var restThreshold = 2.5; // Fixed higher threshold to stop bouncing sooner
if (Math.abs(fruit.vy) < restThreshold) {
fruit.vy = 0;
+ fruit.vx *= 0.7; // Apply horizontal friction when on ground
}
- var angularRestThreshold = 0.03 * (1 - (fruit.elasticity - 0.7) * 2);
- if (Math.abs(fruit.angularVelocity) < angularRestThreshold && fruit.vy === 0) {
+ var angularRestThreshold = 0.05; // Higher threshold to stop rotation sooner
+ if (Math.abs(fruit.angularVelocity) < angularRestThreshold) {
fruit.angularVelocity = 0;
fruit.rotation = Math.round(fruit.rotation / (Math.PI / 2)) * (Math.PI / 2);
}
}
@@ -406,16 +413,16 @@
var self = Container.call(this);
self.vx = 0;
self.vy = 0;
self.gravity = 1.8;
- self.friction = 0.98;
- self.elasticity = 0.7;
+ self.friction = 0.92; // Increased friction
+ self.elasticity = 0.4; // Reduced elasticity
self.isStatic = false;
self.rotation = 0;
self.angularVelocity = 0;
- self.angularFriction = 0.95;
- self.groundAngularFriction = 0.75;
- self.maxAngularVelocity = 0.15;
+ self.angularFriction = 0.85; // Increased angular friction
+ self.groundAngularFriction = 0.6; // Increased ground angular friction
+ self.maxAngularVelocity = 0.08; // Reduced max angular velocity
self.rotationRestCounter = 0;
self.apply = function (fruit) {
if (fruit.isStatic || fruit.merging) {
return;
@@ -423,20 +430,29 @@
fruit.vy += fruit.gravity;
fruit.x += fruit.vx;
fruit.y += fruit.vy;
fruit.rotation += fruit.angularVelocity;
- fruit.vx *= fruit.friction;
- fruit.vy *= fruit.friction;
- fruit.angularVelocity *= fruit.angularFriction;
+ // Apply stronger friction to slow down movement
+ fruit.vx *= fruit.friction * 0.95;
+ fruit.vy *= fruit.friction * 0.95;
+ // Apply stronger angular friction to slow down rotation
+ fruit.angularVelocity *= fruit.angularFriction * 0.85;
+ // Force fruits to stop when very small movement
+ if (Math.abs(fruit.vx) < 0.1) {
+ fruit.vx = 0;
+ }
+ if (Math.abs(fruit.vy) < 0.1 && fruit.y > gameHeight - 300) {
+ fruit.vy = 0;
+ }
self.handleRotationDamping(fruit);
};
self.handleRotationDamping = function (fruit) {
var movementMagnitude = Math.sqrt(fruit.vx * fruit.vx + fruit.vy * fruit.vy);
if (Math.abs(fruit.vx) < 0.5 && Math.abs(fruit.vy) < 0.5) {
- fruit.angularVelocity *= 0.85;
+ fruit.angularVelocity *= 0.65;
if (Math.abs(fruit.angularVelocity) < 0.02) {
fruit.rotationRestCounter++;
- if (fruit.rotationRestCounter > 45) {
+ if (fruit.rotationRestCounter > 10) {
fruit.angularVelocity = 0;
fruit.rotation = Math.round(fruit.rotation / (Math.PI / 2)) * (Math.PI / 2);
}
} else {
@@ -445,15 +461,15 @@
} else {
fruit.rotationRestCounter = 0;
}
if (movementMagnitude < 0.05) {
- fruit.angularVelocity *= 0.80;
+ fruit.angularVelocity *= 0.60;
} else if (movementMagnitude < 0.3) {
- fruit.angularVelocity *= 0.85;
+ fruit.angularVelocity *= 0.70;
} else if (movementMagnitude < 0.8) {
- fruit.angularVelocity *= 0.90;
+ fruit.angularVelocity *= 0.80;
}
- if (Math.abs(fruit.angularVelocity) < 0.005) {
+ if (Math.abs(fruit.angularVelocity) < 0.01) {
fruit.angularVelocity = 0;
}
fruit.angularVelocity = Math.min(Math.max(fruit.angularVelocity, -fruit.maxAngularVelocity), fruit.maxAngularVelocity);
};