/**** 
* 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);
	self.chargeNeededForRelease = GAME_CONSTANTS.CHARGE_NEEDED_FOR_RELEASE;
	self.currentCharge = 0;
	self.isReadyToRelease = false;
	self.pulseAnimationActive = false;
	self.initialize = function () {
		self.portalAsset = self.attachAsset('portal', {
			anchorX: 0.5,
			anchorY: 0.5
		});
		self.portalAsset.x = GAME_CONSTANTS.GAME_WIDTH / 2 + GAME_CONSTANTS.PORTAL_UI_X_OFFSET;
		self.portalAsset.alpha = 0;
		self.portalAsset.scaleX = 0;
		self.portalAsset.scaleY = 0;
		self.y = GAME_CONSTANTS.PORTAL_UI_Y;
	};
	self.updateChargeDisplay = function (chargeCount) {
		self.currentCharge = chargeCount;
		var remainingCount = Math.max(0, self.chargeNeededForRelease - self.currentCharge);
		var progressPercent = (self.chargeNeededForRelease - remainingCount) / self.chargeNeededForRelease;
		var targetScale = progressPercent;
		if (progressPercent > 0 && self.portalAsset.alpha === 0) {
			self.portalAsset.alpha = 1;
		}
		tween(self.portalAsset, {
			scaleX: targetScale,
			scaleY: targetScale,
			alpha: progressPercent
		}, {
			duration: GAME_CONSTANTS.PORTAL_TWEEN_DURATION,
			easing: tween.easeOut
		});
		if (remainingCount === 0 && !self.isReadyToRelease) {
			self.setReadyState(true);
		}
	};
	self.setReadyState = function (isReady) {
		self.isReadyToRelease = isReady;
		if (isReady) {
			tween(self.portalAsset, {
				scaleX: 1.0,
				scaleY: 1.0,
				alpha: 1.0,
				rotation: Math.PI * 2
			}, {
				duration: GAME_CONSTANTS.PORTAL_TWEEN_DURATION,
				easing: tween.easeOut
			});
			self.startPulseAnimation();
		}
	};
	self.startPulseAnimation = function () {
		if (self.pulseAnimationActive) return;
		self.pulseAnimationActive = true;
		self._pulseText();
	};
	self._pulseText = function () {
		if (!self.isReadyToRelease) {
			self.pulseAnimationActive = false;
			return;
		}
		tween(self.portalAsset, {
			scaleX: 1.3,
			scaleY: 1.3
		}, {
			duration: GAME_CONSTANTS.PORTAL_PULSE_DURATION,
			easing: tween.easeInOut,
			onFinish: function onFinish() {
				if (!self.isReadyToRelease) {
					self.pulseAnimationActive = false;
					return;
				}
				tween(self.portalAsset, {
					scaleX: 1.0,
					scaleY: 1.0
				}, {
					duration: GAME_CONSTANTS.PORTAL_PULSE_DURATION,
					easing: tween.easeInOut,
					onFinish: self._pulseText
				});
			}
		});
	};
	self.reset = function () {
		self.isReadyToRelease = false;
		self.currentCharge = 0;
		self.pulseAnimationActive = false;
		tween(self.portalAsset, {
			alpha: 0
		}, {
			duration: 200,
			easing: tween.easeOut
		});
		tween(self.portalAsset, {
			scaleX: 0,
			scaleY: 0
		}, {
			duration: 200,
			easing: tween.easeOut
		});
	};
	self.initialize();
	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 fruitLevel = getFruitLevel(fruit);
		var sizeReduction = Math.max(0, fruitLevel - 4) * 2;
		fruitHalfWidth = Math.max(10, fruitHalfWidth - sizeReduction / 2);
		fruitHalfHeight = Math.max(10, fruitHalfHeight - sizeReduction / 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;
		fruit._boundaryContacts = fruit._boundaryContacts || {
			left: false,
			right: false,
			floor: false
		};
		fruit._boundaryContacts.left = false;
		fruit._boundaryContacts.right = false;
		fruit._boundaryContacts.floor = false;
		self.checkLeftWallCollision(fruit, walls.left, effectiveWidth);
		self.checkRightWallCollision(fruit, walls.right, effectiveWidth);
		self.checkFloorCollision(fruit, floor, effectiveHeight);
		var isInContact = fruit._boundaryContacts.left || fruit._boundaryContacts.right || fruit._boundaryContacts.floor;
		if (isInContact) {
			fruit.wallContactFrames++;
			var progressiveFriction = Math.min(0.75, 0.55 + fruit.wallContactFrames * 0.015);
			fruit.angularVelocity *= progressiveFriction;
			if (fruit.wallContactFrames > 15) {
				fruit.vx *= 0.75;
				fruit.vy *= 0.75;
				fruit.angularVelocity *= 0.5;
			} else if (fruit.wallContactFrames > 10) {
				fruit.vx *= 0.8;
				fruit.vy *= 0.85;
				fruit.angularVelocity *= 0.7;
			} else if (fruit.wallContactFrames > 5) {
				fruit.vx *= 0.9;
				fruit.vy *= 0.9;
			}
		} else {
			fruit.wallContactFrames = Math.max(0, fruit.wallContactFrames - 1);
		}
	};
	self.checkLeftWallCollision = function (fruit, leftWall, effectiveWidth) {
		var leftBoundary = leftWall.x + leftWall.width / 2 + effectiveWidth;
		if (fruit.x < leftBoundary) {
			var incomingVx = fruit.vx;
			fruit.x = leftBoundary;
			fruit.vx = -incomingVx * fruit.elasticity * 0.7;
			if (Math.abs(incomingVx) > GAME_CONSTANTS.BOUNCE_SOUND_VELOCITY_THRESHOLD) {
				LK.getSound('bounce').play();
			}
			var angularImpactMultiplier = 0.0025 * (1 + (0.9 - fruit.elasticity) * 5);
			fruit.angularVelocity += fruit.vy * angularImpactMultiplier * 0.25;
			fruit.angularVelocity *= GAME_CONSTANTS.GROUND_ANGULAR_FRICTION * 0.4;
			fruit._boundaryContacts.left = true;
		} else if (fruit.x > leftWall.x + leftWall.width * 2) {
			if (fruit._boundaryContacts && !fruit._boundaryContacts.right && !fruit._boundaryContacts.floor) {
				fruit.wallContactFrames = Math.max(0, fruit.wallContactFrames - 1);
			}
		}
	};
	self.checkRightWallCollision = function (fruit, rightWall, effectiveWidth) {
		var rightBoundary = rightWall.x - rightWall.width / 2 - effectiveWidth;
		if (fruit.x > rightBoundary) {
			var incomingVx = fruit.vx;
			fruit.x = rightBoundary;
			fruit.vx = -incomingVx * fruit.elasticity * 0.7;
			if (Math.abs(incomingVx) > GAME_CONSTANTS.BOUNCE_SOUND_VELOCITY_THRESHOLD) {
				LK.getSound('bounce').play();
			}
			var angularImpactMultiplier = 0.0025 * (1 + (0.9 - fruit.elasticity) * 5);
			fruit.angularVelocity -= fruit.vy * angularImpactMultiplier * 0.25;
			fruit.angularVelocity *= GAME_CONSTANTS.GROUND_ANGULAR_FRICTION * 0.4;
			fruit._boundaryContacts.right = true;
		}
	};
	self.checkFloorCollision = function (fruit, floor, effectiveHeight) {
		var floorCollisionY = floor.y - floor.height / 2 - effectiveHeight;
		if (fruit.y > floorCollisionY) {
			var incomingVy = fruit.vy;
			fruit.y = floorCollisionY;
			fruit.vy = -incomingVy * fruit.elasticity * 0.5;
			if (Math.abs(incomingVy) > GAME_CONSTANTS.BOUNCE_SOUND_VELOCITY_THRESHOLD) {
				LK.getSound('bounce').play();
			}
			fruit._boundaryContacts.floor = true;
			if (Math.abs(fruit.vx) > 0.5) {
				fruit.angularVelocity = fruit.vx * 0.008;
			}
			fruit.angularVelocity *= GAME_CONSTANTS.GROUND_ANGULAR_FRICTION * 0.4;
			var restThreshold = fruit.wallContactFrames > 5 ? 1.5 : 2.5;
			if (Math.abs(fruit.vy) < restThreshold) {
				fruit.vy = 0;
				fruit.vx *= 0.6;
				if (fruit.wallContactFrames > 8) {
					fruit.vx *= 0.75;
				}
			}
			var angularRestThreshold = fruit.wallContactFrames > 5 ? 0.012 : 0.018;
			if (Math.abs(fruit.angularVelocity) < angularRestThreshold) {
				fruit.angularVelocity = 0;
			}
		}
	};
	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 EvolutionLine = Container.expand(function () {
	var self = Container.call(this);
	self.initialize = function () {
		self.y = GAME_CONSTANTS.EVOLUTION_LINE_Y;
		self.x = GAME_CONSTANTS.GAME_WIDTH / 2; // Remove the X_OFFSET to center properly
		var fruitTypes = [FruitTypes.CHERRY, FruitTypes.GRAPE, FruitTypes.APPLE, FruitTypes.ORANGE, FruitTypes.WATERMELON, FruitTypes.PINEAPPLE, FruitTypes.MELON, FruitTypes.PEACH, FruitTypes.COCONUT, FruitTypes.DURIAN];
		var totalWidth = 0;
		var fruitIcons = [];
		for (var i = 0; i < fruitTypes.length; i++) {
			var fruitType = fruitTypes[i];
			var fruitIcon = LK.getAsset(fruitType.id, {
				anchorX: 0.5,
				anchorY: 0.5
			});
			var scale = Math.min(GAME_CONSTANTS.EVOLUTION_ICON_MAX_SIZE / fruitIcon.width, GAME_CONSTANTS.EVOLUTION_ICON_MAX_SIZE / fruitIcon.height);
			fruitIcon.scaleX = scale;
			fruitIcon.scaleY = scale;
			totalWidth += fruitIcon.width * scale;
			if (i < fruitTypes.length - 1) {
				totalWidth += GAME_CONSTANTS.EVOLUTION_ICON_SPACING;
			}
			fruitIcons.push(fruitIcon);
		}
		var currentX = -totalWidth / 2;
		for (var i = 0; i < fruitIcons.length; i++) {
			var icon = fruitIcons[i];
			icon.x = currentX + icon.width * icon.scaleX / 2;
			icon.y = 0;
			self.addChild(icon);
			currentX += icon.width * icon.scaleX + GAME_CONSTANTS.EVOLUTION_ICON_SPACING;
		}
	};
	return self;
});
var FireElement = Container.expand(function (initX, initY, zIndex) {
	var self = Container.call(this);
	self.baseX = initX || 0;
	self.baseY = initY || 0;
	self.zIndex = zIndex || 0;
	self.movementRange = 30 + Math.random() * 20;
	self.movementSpeed = 0.3 + Math.random() * 0.4;
	self.direction = Math.random() > 0.5 ? 1 : -1;
	self.alphaMin = GAME_CONSTANTS.FIRE_ALPHA_MIN;
	self.alphaMax = GAME_CONSTANTS.FIRE_ALPHA_MAX;
	self.flickerSpeed = GAME_CONSTANTS.FIRE_FLICKER_SPEED_BASE + Math.random() * GAME_CONSTANTS.FIRE_FLICKER_SPEED_RANDOM;
	self.frameIndex = 0;
	self.frameTimer = null;
	self.frameDuration = GAME_CONSTANTS.FIRE_FRAME_DURATION;
	self.initialize = function () {
		self.fireAsset = self.attachAsset('fire', {
			anchorX: 0.5,
			anchorY: 1.0
		});
		self.fireAsset2 = self.attachAsset('fire_2', {
			anchorX: 0.5,
			anchorY: 1.0
		});
		self.fireAsset2.visible = false;
		self.x = self.baseX;
		self.y = self.baseY;
		self.startAlphaFlicker();
		self.startFrameAnimation();
	};
	self.update = function () {
		self.x += self.movementSpeed * self.direction;
		if (Math.abs(self.x - self.baseX) > self.movementRange) {
			self.direction *= -1;
		}
	};
	self.startFrameAnimation = function () {
		if (self.frameTimer) LK.clearInterval(self.frameTimer);
		self.frameTimer = LK.setInterval(function () {
			self.toggleFrame();
		}, self.frameDuration);
	};
	self.toggleFrame = function () {
		self.frameIndex = (self.frameIndex + 1) % 2;
		self.fireAsset.visible = self.frameIndex === 0;
		self.fireAsset2.visible = self.frameIndex === 1;
	};
	self.startAlphaFlicker = function () {
		if (self.flickerTween) self.flickerTween.stop();
		var startDelay = Math.random() * 500;
		LK.setTimeout(function () {
			self.flickerToMax();
		}, startDelay);
	};
	self.flickerToMax = function () {
		self.flickerTween = tween(self, {
			alpha: self.alphaMax
		}, {
			duration: self.flickerSpeed,
			easing: tween.easeInOut,
			onFinish: self.flickerToMin
		});
	};
	self.flickerToMin = function () {
		self.flickerTween = tween(self, {
			alpha: self.alphaMin
		}, {
			duration: self.flickerSpeed,
			easing: tween.easeInOut,
			onFinish: self.flickerToMax
		});
	};
	self.destroy = function () {
		if (self.flickerTween) self.flickerTween.stop();
		if (self.frameTimer) {
			LK.clearInterval(self.frameTimer);
			self.frameTimer = null;
		}
		Container.prototype.destroy.call(this);
	};
	self.initialize();
	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();
	var behaviorSystem = new FruitBehavior();
	self.vx = physics.vx;
	self.vy = physics.vy;
	self.rotation = physics.rotation;
	self.angularVelocity = physics.angularVelocity;
	self.angularFriction = GAME_CONSTANTS.ANGULAR_FRICTION;
	self.groundAngularFriction = GAME_CONSTANTS.GROUND_ANGULAR_FRICTION;
	var currentLevel = getFruitLevel(self);
	self.gravity = GAME_CONSTANTS.BASE_GRAVITY * (1 + (currentLevel - 1) * GAME_CONSTANTS.GRAVITY_LEVEL_MULTIPLIER);
	self.friction = GAME_CONSTANTS.FRICTION;
	self.rotationRestCounter = physics.rotationRestCounter;
	self.maxAngularVelocity = GAME_CONSTANTS.MAX_ANGULAR_VELOCITY;
	self.isStatic = physics.isStatic;
	self.elasticity = currentLevel < GAME_CONSTANTS.ELASTICITY_LOW_START_LEVEL ? GAME_CONSTANTS.ELASTICITY_HIGH : GAME_CONSTANTS.ELASTICITY_LOW_BASE - (currentLevel - (GAME_CONSTANTS.ELASTICITY_LOW_START_LEVEL - 1)) * GAME_CONSTANTS.ELASTICITY_DECREASE_FACTOR;
	self.elasticity = Math.max(0.1, self.elasticity); // Ensure minimum elasticity
	self.wallContactFrames = collision.wallContactFrames;
	self.merging = mergeHandler.merging;
	self.mergeGracePeriodActive = mergeHandler.mergeGracePeriodActive;
	self.fromChargedRelease = mergeHandler.fromChargedRelease;
	self.safetyPeriod = false;
	self.immuneToGameOver = false;
	self.isFullyStabilized = false;
	self.isSleeping = false;
	self._sleepCounter = 0;
	self.behavior = type && type.id ? behaviorSystem.getMergeHandler(type.id) : null;
	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;
		if (self.behavior && self.behavior.onSpawn) {
			self.behavior.onSpawn(self);
		}
	} else {
		console.log("Warning: Fruit type not available yet or missing required properties");
	}
	self.updatePhysics = function () {
		physics.apply(self);
	};
	self.checkBoundaries = function (walls, floor) {
		collision.checkBoundaryCollisions(self, walls, floor);
		if (self.safetyPeriod === false && self.vy <= 0.1) {
			self.safetyPeriod = undefined;
		}
	};
	self.merge = function (otherFruit) {
		mergeHandler.beginMerge(self, otherFruit);
	};
	return self;
});
var FruitBehavior = Container.expand(function () {
	var self = Container.call(this);
	self.behaviors = {
		CHERRY: {
			onMerge: function onMerge(f1, f2, x, y) {
				return self.standardMerge(f1, f2, x, y);
			}
		},
		GRAPE: {
			onMerge: function onMerge(f1, f2, x, y) {
				return self.standardMerge(f1, f2, x, y);
			}
		},
		APPLE: {
			onMerge: function onMerge(f1, f2, x, y) {
				return self.standardMerge(f1, f2, x, y);
			}
		},
		ORANGE: {
			onMerge: function onMerge(f1, f2, x, y) {
				return self.standardMerge(f1, f2, x, y);
			}
		},
		WATERMELON: {
			onMerge: function onMerge(f1, f2, x, y) {
				return self.standardMerge(f1, f2, x, y);
			}
		},
		PINEAPPLE: {
			onMerge: function onMerge(f1, f2, x, y) {
				return self.standardMerge(f1, f2, x, y);
			},
			onSpawn: function onSpawn() {}
		},
		MELON: {
			onMerge: function onMerge(f1, f2, x, y) {
				LK.getSound('Smartz').play();
				return self.standardMerge(f1, f2, x, y);
			}
		},
		PEACH: {
			onMerge: function onMerge(f1, f2, x, y) {
				LK.getSound('stonks').play();
				return self.standardMerge(f1, f2, x, y);
			}
		},
		COCONUT: {
			onMerge: function onMerge(f1, f2, x, y) {
				LK.getSound('ThisIsFine').play();
				return self.standardMerge(f1, f2, x, y);
			},
			onSpawn: function onSpawn() {
				LK.getSound('stonks').play();
			}
		},
		DURIAN: {
			onMerge: function onMerge(f1, f2, x, y) {
				LK.setScore(LK.getScore() + f1.type.points);
				updateScoreDisplay();
				removeFruitFromGame(f1);
				removeFruitFromGame(f2);
				releasePineappleOnMerge();
				return null;
			}
		}
	};
	self.getMergeHandler = function (fruitTypeId) {
		if (!fruitTypeId) return self.behaviors.CHERRY;
		var upperTypeId = fruitTypeId.toUpperCase();
		return self.behaviors[upperTypeId] || self.behaviors.CHERRY;
	};
	self.standardMerge = function (fruit1, fruit2, posX, posY) {
		var nextType = FruitTypes[fruit1.type.next.toUpperCase()];
		releasePineappleOnMerge();
		return self.createNextLevelFruit(fruit1, nextType, posX, posY);
	};
	self.createNextLevelFruit = function (sourceFruit, nextType, posX, posY) {
		var newFruit = new Fruit(nextType);
		newFruit.x = posX;
		newFruit.y = posY;
		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 newFruit;
	};
	self.playSoundEffect = function (soundId) {
		if (soundId) LK.getSound(soundId).play();
	};
	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.fruitBehavior = new FruitBehavior();
	self.beginMerge = function (fruit1, fruit2) {
		if (fruit1.merging || fruit2.merging) return; // Added check for fruit2
		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.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();
		self.trackMerge(fruit1, fruit2);
		var behaviorHandler = self.fruitBehavior.getMergeHandler(fruit1.type.id);
		if (behaviorHandler && behaviorHandler.onMerge) {
			// Added check
			behaviorHandler.onMerge(fruit1, fruit2, midX, midY);
		}
		// Ensure cleanup happens correctly, check if fruits still exist before removal
		if (fruit1 && fruit1.parent && fruit1.type.id.toUpperCase() !== 'DURIAN') removeFruitFromGame(fruit1);
		if (fruit2 && fruit2.parent) removeFruitFromGame(fruit2);
	};
	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;
		}
	};
	return self;
});
var PhysicsComponent = Container.expand(function () {
	var self = Container.call(this);
	self.vx = 0;
	self.vy = 0;
	self.gravity = GAME_CONSTANTS.BASE_GRAVITY;
	self.friction = GAME_CONSTANTS.FRICTION;
	self.isStatic = false;
	self.rotation = 0;
	self.angularVelocity = 0;
	self.maxAngularVelocity = GAME_CONSTANTS.MAX_ANGULAR_VELOCITY;
	self.rotationRestCounter = 0;
	self.lastVx = 0;
	self.lastVy = 0;
	self.isFullyStabilized = false; // Moved here
	self.stabilizeFruit = function (fruit, movementMagnitude, velocityChange) {
		var isMidAir = !fruit._boundaryContacts || !fruit._boundaryContacts.floor && !fruit._boundaryContacts.left && !fruit._boundaryContacts.right;
		if (isMidAir) {
			fruit.isFullyStabilized = false; // Ensure mid-air fruits are never marked stabilized
			return false;
		}
		var stabilizationLevel = 0;
		var fruitLevel = getFruitLevel(fruit);
		if (!fruit._stabilizeMetrics) {
			fruit._stabilizeMetrics = {
				consecutiveSmallMovements: 0,
				wallContactDuration: 0,
				surroundedDuration: 0,
				restingDuration: 0
			};
		}
		// Dynamic thresholds based only on fruitLevel - lower for higher-level fruits
		var movementThreshold = GAME_CONSTANTS.STABILIZATION_MOVEMENT_THRESHOLD_BASE - fruitLevel * GAME_CONSTANTS.STABILIZATION_MOVEMENT_THRESHOLD_LEVEL_FACTOR * 1.2;
		var angularThreshold = GAME_CONSTANTS.STABILIZATION_ANGULAR_THRESHOLD_BASE - fruitLevel * GAME_CONSTANTS.STABILIZATION_ANGULAR_THRESHOLD_LEVEL_FACTOR * 1.2;
		if (movementMagnitude < movementThreshold && Math.abs(fruit.angularVelocity) < angularThreshold) {
			var stabilizationRate = GAME_CONSTANTS.STABILIZATION_SCORE_RATE_BASE + fruitLevel * GAME_CONSTANTS.STABILIZATION_SCORE_LEVEL_FACTOR * 1.4;
			fruit._stabilizeMetrics.consecutiveSmallMovements += stabilizationRate;
			fruit._stabilizeMetrics.restingDuration += stabilizationRate;
		} else {
			fruit._stabilizeMetrics.consecutiveSmallMovements = 0;
			fruit._stabilizeMetrics.restingDuration = Math.max(0, fruit._stabilizeMetrics.restingDuration - 2);
		}
		if (fruit.wallContactFrames > 0) fruit._stabilizeMetrics.wallContactDuration += GAME_CONSTANTS.STABILIZATION_WALL_SCORE_RATE * 1.4 + fruitLevel * 0.15;else fruit._stabilizeMetrics.wallContactDuration = Math.max(0, fruit._stabilizeMetrics.wallContactDuration - 1);
		if (fruit.surroundedFrames > 0) fruit._stabilizeMetrics.surroundedDuration += GAME_CONSTANTS.STABILIZATION_SURROUNDED_SCORE_RATE * 1.4 + fruitLevel * 0.22;else fruit._stabilizeMetrics.surroundedDuration = Math.max(0, fruit._stabilizeMetrics.surroundedDuration - 1);
		var levelFactor = fruitLevel * GAME_CONSTANTS.STABILIZATION_SCORE_LEVEL_INFLUENCE;
		var totalStabilizationScore = fruit._stabilizeMetrics.consecutiveSmallMovements * (1.0 + levelFactor) + fruit._stabilizeMetrics.wallContactDuration * (0.8 + levelFactor) + fruit._stabilizeMetrics.surroundedDuration * (1.2 + levelFactor) + fruit._stabilizeMetrics.restingDuration * (0.5 + levelFactor);
		// Dynamic thresholds for stabilization based only on fruitLevel - lower for higher-level fruits
		var fullStabilizationThreshold = GAME_CONSTANTS.STABILIZATION_THRESHOLD_FULL_BASE - fruitLevel * GAME_CONSTANTS.STABILIZATION_THRESHOLD_FULL_LEVEL_FACTOR * 1.25;
		var partialStabilizationThreshold = GAME_CONSTANTS.STABILIZATION_THRESHOLD_PARTIAL_BASE - fruitLevel * GAME_CONSTANTS.STABILIZATION_THRESHOLD_PARTIAL_LEVEL_FACTOR * 1.25;
		if (totalStabilizationScore > fullStabilizationThreshold) stabilizationLevel = 2;else if (totalStabilizationScore > partialStabilizationThreshold) stabilizationLevel = 1;
		if (stabilizationLevel > 0) {
			// Make damping stronger for higher-level fruits
			var baseDamp = stabilizationLevel === 2 ? GAME_CONSTANTS.STABILIZATION_DAMP_FACTOR_FULL * 0.9 : GAME_CONSTANTS.STABILIZATION_DAMP_FACTOR_PARTIAL * 0.95;
			var dampFactor = Math.max(0.35, baseDamp - fruitLevel * GAME_CONSTANTS.STABILIZATION_DAMP_LEVEL_FACTOR * 1.1);
			fruit.vx *= dampFactor;
			fruit.vy *= dampFactor;
			fruit.angularVelocity *= dampFactor * 0.85;
			var stopThreshold = GAME_CONSTANTS.STABILIZATION_STOP_THRESHOLD_BASE - fruitLevel * GAME_CONSTANTS.STABILIZATION_STOP_THRESHOLD_LEVEL_FACTOR;
			// Unconditionally stop when fully stabilized (level 2) and below movement threshold
			if (stabilizationLevel === 2 && movementMagnitude < stopThreshold) {
				fruit.vx = 0;
				fruit.vy = 0;
				fruit.angularVelocity = 0;
				fruit.isFullyStabilized = true;
				// Track frames of being fully stabilized
				if (!fruit._sleepCounter) fruit._sleepCounter = 0;
				// Only increment sleep counter if both linear and angular velocities are near zero
				if (movementMagnitude < stopThreshold * 0.8 && Math.abs(fruit.angularVelocity) < angularThreshold * 0.7) {
					fruit._sleepCounter++;
					// After being fully stabilized for SLEEP_DELAY_FRAMES consecutive frames, put to sleep
					if (fruit._sleepCounter >= GAME_CONSTANTS.SLEEP_DELAY_FRAMES) {
						fruit.isSleeping = true;
						fruit.isFullyStabilized = true; // Ensure fully stabilized state when sleeping
					}
				} else {
					// Reset sleep counter if either velocity isn't near zero
					fruit._sleepCounter = 0;
					fruit.isSleeping = false; // Ensure fruit is awake if conditions aren't met
				}
				return true;
			}
		}
		fruit.isFullyStabilized = false;
		return false;
	};
	self.apply = function (fruit) {
		if (fruit.isStatic || fruit.merging) return;
		// Skip physics calculations for sleeping fruits, but keep collision detection
		if (fruit.isSleeping) {
			// Keep fruit in the exact same position - no physics updates
			fruit.vx = 0;
			fruit.vy = 0;
			fruit.angularVelocity = 0;
			return;
		}
		// Reset stabilization flag if significant external force applied (e.g., explosion)
		// Wake fruit up from potential stabilization if there's significant velocity change
		var velocityDeltaX = Math.abs(fruit.vx - fruit.lastVx);
		var velocityDeltaY = Math.abs(fruit.vy - fruit.lastVy);
		if ((fruit.isFullyStabilized || fruit._sleepCounter > 0) && (velocityDeltaX > 0.7 || velocityDeltaY > 0.7)) {
			fruit.isFullyStabilized = false;
			fruit._sleepCounter = 0; // Reset sleep counter on significant velocity change
		}
		// More aggressively ensure stability when at rest - use a tighter threshold
		if (fruit.isFullyStabilized && Math.abs(fruit.vx) < 0.3 && Math.abs(fruit.vy) < 0.3) {
			fruit.vx = 0;
			fruit.vy = 0;
			fruit.angularVelocity = 0; // Ensure zero angular velocity
			return;
		}
		fruit.lastVx = fruit.vx;
		fruit.lastVy = fruit.vy;
		var fruitLevel = getFruitLevel(fruit);
		var isMidAir = !fruit._boundaryContacts || !fruit._boundaryContacts.floor && !fruit._boundaryContacts.left && !fruit._boundaryContacts.right;
		// Use exponential scaling for gravity to make higher-level fruits dramatically heavier
		var gravityMultiplier = 1 + Math.pow(fruitLevel, 1.5) * 0.25; // Stronger influence
		fruit.vy += fruit.gravity * gravityMultiplier;
		var maxVelocity = GAME_CONSTANTS.MAX_VELOCITY_BASE - fruitLevel * GAME_CONSTANTS.MAX_VELOCITY_LEVEL_FACTOR;
		fruit.vx = Math.max(-maxVelocity, Math.min(maxVelocity, fruit.vx));
		fruit.vy = Math.max(-maxVelocity, Math.min(maxVelocity, fruit.vy));
		// Removed separate LEVEL_DOWNWARD_FORCE_FACTOR as it's now incorporated in the gravity calculation
		if (fruit.lastVx !== undefined && fruit.lastVy !== undefined) {
			var dvx = fruit.vx - fruit.lastVx;
			var dvy = fruit.vy - fruit.lastVy;
			// Make inertia scale dramatically with fruitLevel using power function
			var inertiaResistance = Math.min(0.95, Math.pow(fruitLevel, 1.8) * 0.01);
			fruit.vx = fruit.lastVx + dvx * (1 - inertiaResistance);
			fruit.vy = fruit.lastVy + dvy * (1 - inertiaResistance);
		}
		var movementMagnitude = Math.sqrt(fruit.vx * fruit.vx + fruit.vy * fruit.vy);
		var velocityChange = Math.abs(fruit.vx - fruit.lastVx) + Math.abs(fruit.vy - fruit.lastVy);
		// Apply strong damping when velocity is very low to help fruits stabilize faster
		if (movementMagnitude < 0.5) {
			fruit.vx *= 0.80;
			fruit.vy *= 0.80;
			fruit.angularVelocity *= 0.70;
		}
		var isFullyStabilizedByFunc = self.stabilizeFruit(fruit, movementMagnitude, velocityChange);
		fruit.isFullyStabilized = isFullyStabilizedByFunc; // Update fruit's state
		if (!fruit.isFullyStabilized) {
			// Only apply rolling rotation when in contact with a surface
			var isRolling = fruit._boundaryContacts && (fruit._boundaryContacts.floor || fruit._boundaryContacts.left || fruit._boundaryContacts.right);
			if (isRolling && (movementMagnitude > 0.5 || velocityChange > 0.3)) {
				var rotationFactor = 0.035 / (1 + fruitLevel * 0.08);
				var targetAngularVelocity = fruit.vx * rotationFactor;
				fruit.angularVelocity = fruit.angularVelocity * 0.4 + targetAngularVelocity * 0.6;
			} else {
				// Apply stronger damping when not rolling or in air
				fruit.angularVelocity *= isRolling ? 0.75 : 0.65; // More damping when not rolling
			}
			fruit.rotation += fruit.angularVelocity;
			// Make friction increase slightly with level (heavier objects have more surface interaction)
			var frictionModifier = 0.98 + fruitLevel * 0.005;
			// Apply more friction when nearly stopped to push toward stabilization
			var nearStopMultiplier = movementMagnitude < 1.0 ? 0.95 : 0.98; // More aggressive
			fruit.vx *= fruit.friction * frictionModifier * nearStopMultiplier;
			fruit.vy *= fruit.friction * frictionModifier * nearStopMultiplier * 0.98;
			var stopThreshold = GAME_CONSTANTS.STABILIZATION_STOP_THRESHOLD_BASE - fruitLevel * GAME_CONSTANTS.STABILIZATION_STOP_THRESHOLD_LEVEL_FACTOR;
			stopThreshold = Math.max(0.05, stopThreshold);
			if (Math.abs(fruit.vx) < stopThreshold) fruit.vx = 0;
			// Only stop vertical velocity if NOT in mid-air AND not fully stabilized by function
			if (Math.abs(fruit.vy) < stopThreshold && !isMidAir) fruit.vy = 0;
			self.handleRotationDamping(fruit);
		}
		fruit.x += fruit.vx;
		fruit.y += fruit.vy;
		if (isMidAir) {
			var currentMinFallSpeed = GAME_CONSTANTS.MIN_FALL_SPEED + fruitLevel * 0.02;
			if (fruit.vy < currentMinFallSpeed) {
				fruit.vy = currentMinFallSpeed;
			}
			fruit.isFullyStabilized = false; // Explicitly ensure mid-air is not stabilized
		}
	};
	self.handleRotationDamping = function (fruit) {
		var movementMagnitude = Math.sqrt(fruit.vx * fruit.vx + fruit.vy * fruit.vy);
		var fruitLevel = getFruitLevel(fruit);
		var isMidAir = !fruit._boundaryContacts || !fruit._boundaryContacts.floor && !fruit._boundaryContacts.left && !fruit._boundaryContacts.right;
		// Scale rotation factor inversely with fruitLevel - higher level = less rotation from rolling
		var rotationFactor = 0.04 / (1 + fruitLevel * 0.15); // Increased inverse scaling with level
		var targetAngularVelocity = fruit.vx * rotationFactor;
		var hasNeighbors = fruit.neighborContacts && fruit.neighborContacts.length > 0;
		var neighborCount = hasNeighbors ? fruit.neighborContacts.length : 0;
		// Only apply rotation forces if moving with sufficient velocity
		if (movementMagnitude > 0.3) {
			// Less influence when more neighbors present - scaled by neighbor count
			var blendRatio = Math.max(0.2, 0.6 - neighborCount * 0.08);
			fruit.angularVelocity = fruit.angularVelocity * (1 - blendRatio) + targetAngularVelocity * blendRatio;
			// Correct rotation direction if it's opposite of movement
			var targetDirection = fruit.vx > 0 ? 1 : -1;
			var currentDirection = fruit.angularVelocity > 0 ? 1 : -1;
			if (targetDirection !== currentDirection && Math.abs(fruit.vx) > 0.5) {
				fruit.angularVelocity *= 0.25; // Stronger correction when rotation direction opposite of movement
			}
		}
		// Base damping primarily on movement magnitude and neighbor count
		var movementFactor = Math.min(1, movementMagnitude * 2);
		var neighborFactor = Math.min(1, neighborCount * 0.2);
		var levelFactor = fruitLevel * 0.06; // Increased level factor influence
		// Calculate rotation damp factor based on these conditions
		var rotationDampFactor;
		// Apply much stronger damping when nearly stopped
		if (movementMagnitude < 0.05) {
			// Near-zero movement gets extreme damping
			rotationDampFactor = 0.12 - levelFactor * 0.6; // Increased extreme damping near zero
		} else if (isMidAir) {
			// Mid-air damping - moderately strong
			rotationDampFactor = 0.6 - levelFactor * 0.8;
		} else if (movementMagnitude < 0.3) {
			// Very slow movement - strong damping
			rotationDampFactor = 0.25 - levelFactor * 0.7 - neighborFactor * 0.15; // Stronger damping for slow movement
		} else if (neighborCount >= 3) {
			// Many neighbors - increased resistance
			rotationDampFactor = 0.35 - levelFactor * 0.6 - neighborFactor * 0.15;
		} else if (hasNeighbors) {
			// Some neighbors - moderate resistance
			rotationDampFactor = 0.45 - levelFactor * 0.5 - neighborFactor * 0.1;
		} else {
			// Default case - moving freely with no neighbors
			rotationDampFactor = 0.6 - levelFactor * 0.4;
		}
		// Check if fruit is rolling (in contact with surface with meaningful velocity)
		var isRolling = !isMidAir && movementMagnitude > 0.5 && fruit._boundaryContacts && (fruit._boundaryContacts.floor || fruit._boundaryContacts.left || fruit._boundaryContacts.right);
		if (isRolling) {
			// Fine-tune damping when rolling based on speed
			if (movementMagnitude < 0.5) {
				rotationDampFactor = 0.7 - levelFactor * 0.4;
			} else if (movementMagnitude < 1.0) {
				rotationDampFactor = 0.8 - levelFactor * 0.3;
			} else {
				// Allow more natural rotation when moving quickly
				rotationDampFactor = 0.85 - levelFactor * 0.2;
			}
		}
		// Apply the calculated damping factor
		fruit.angularVelocity *= rotationDampFactor;
		// Apply additional damping when velocity changes suddenly
		if (fruit.lastVx !== undefined && Math.abs(fruit.vx - fruit.lastVx) > 0.5) {
			fruit.angularVelocity -= (fruit.vx - fruit.lastVx) * 0.02;
		}
		// Dramatically increase damping when velocity is very low
		if (movementMagnitude < 0.2) {
			// Apply extreme angular damping when linear movement is minimal
			var linearDampingFactor = movementMagnitude < 0.08 ? 0.03 : 0.15; // More aggressive damping
			fruit.angularVelocity *= linearDampingFactor;
			// Directly zero out tiny angular velocities when there's almost no movement
			if (Math.abs(fruit.angularVelocity) < 0.002 || movementMagnitude < 0.02) {
				// Lower thresholds
				fruit.angularVelocity = 0;
			}
		}
		// Handling rolling physics when in contact with surfaces
		if (fruit._boundaryContacts) {
			// Apply rolling physics when in contact with floor or walls
			var isRolling = fruit._boundaryContacts.floor || fruit._boundaryContacts.left || fruit._boundaryContacts.right;
			if (isRolling && Math.abs(fruit.vx) > 0.2) {
				// Scale roll factor inversely with level - higher level fruits roll less
				var rollFactor = 0.025 / (1 + fruitLevel * 0.2); // Increased level scaling for rolling
				var idealRollingVelocity = fruit.vx * rollFactor;
				fruit.angularVelocity = fruit.angularVelocity * 0.7 + idealRollingVelocity * 0.3;
			} else if (isMidAir) {
				// Apply stronger damping when airborne with no contacts
				fruit.angularVelocity *= 0.85;
			}
		}
		// Increase wall damping
		if (fruit._boundaryContacts && (fruit._boundaryContacts.left || fruit._boundaryContacts.right) && Math.abs(fruit.angularVelocity) > 0.01) {
			fruit.angularVelocity *= 0.6; // Even stronger wall damping
		}
		// Lowered level-based thresholds for stopping rotation - significantly lowered
		var angularThreshold = GAME_CONSTANTS.ANGULAR_STOP_THRESHOLD - fruitLevel * 0.0005; // Lower threshold for higher level fruits
		var restFramesThreshold = Math.max(1, 2 - Math.floor(fruitLevel * 0.35)); // More aggressive frame requirement reduction
		if (Math.abs(fruit.angularVelocity) < angularThreshold) {
			fruit.rotationRestCounter++;
			if (fruit.rotationRestCounter > restFramesThreshold) {
				fruit.angularVelocity = 0;
			}
		} else {
			fruit.rotationRestCounter = 0;
		}
		// Scale max angular velocity based on level
		var maxAngularMultiplier = 1.2 - fruitLevel * 0.06; // Higher level fruits rotate even less
		fruit.angularVelocity = Math.min(Math.max(fruit.angularVelocity, -self.maxAngularVelocity * maxAngularMultiplier), self.maxAngularVelocity * maxAngularMultiplier);
	};
	return self;
});
var SpatialGrid = Container.expand(function (cellSize) {
	var self = Container.call(this);
	self.cellSize = cellSize || 200;
	self.grid = {};
	self.lastRebuildTime = Date.now();
	self.rebuildInterval = GAME_CONSTANTS.SPATIAL_GRID_REBUILD_INTERVAL_MS;
	self.insertObject = function (obj) {
		if (!obj || !obj.x || !obj.y || !obj.width || !obj.height || obj.merging || obj.isStatic) return;
		obj._currentCells = obj._currentCells || [];
		var cells = self.getCellsForObject(obj);
		obj._currentCells = cells.slice();
		for (var i = 0; i < cells.length; i++) {
			var cellKey = cells[i];
			if (!self.grid[cellKey]) self.grid[cellKey] = [];
			if (self.grid[cellKey].indexOf(obj) === -1) self.grid[cellKey].push(obj);
		}
	};
	self.removeObject = function (obj) {
		if (!obj || !obj.x || !obj.y || !obj.width || !obj.height) return;
		var cells = obj._currentCells || 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];
			}
		}
		obj._currentCells = [];
	};
	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) {
		if (!obj || !obj.x || !obj.y || !obj.width || !obj.height) return;
		var newCells = self.getCellsForObject(obj);
		var oldCells = obj._currentCells || [];
		var cellsChanged = false;
		if (oldCells.length !== newCells.length) cellsChanged = true;else {
			for (var i = 0; i < newCells.length; i++) {
				if (oldCells.indexOf(newCells[i]) === -1) {
					cellsChanged = true;
					break;
				}
			}
		}
		if (cellsChanged) {
			self.removeObject(obj);
			self.insertObject(obj);
		}
	};
	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];
					if (otherObj && otherObj !== obj && !addedObjects[otherObj.id]) {
						candidates.push(otherObj);
						addedObjects[otherObj.id] = true;
					}
				}
			}
		}
		return candidates;
	};
	self.clear = function () {
		self.grid = {};
		self.lastRebuildTime = Date.now();
	};
	self.rebuildGrid = function (allObjects) {
		self.grid = {};
		self.lastRebuildTime = Date.now();
		if (Array.isArray(allObjects)) {
			for (var i = 0; i < allObjects.length; i++) {
				if (allObjects[i] && !allObjects[i].merging && !allObjects[i].isStatic) {
					self.insertObject(allObjects[i]);
				}
			}
		}
	};
	return self;
});
var TrajectoryLine = Container.expand(function () {
	var self = Container.call(this);
	self.dotPool = new DotPool(100);
	self.activeDots = [];
	self.dots = [];
	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 activeFruitLevel = getFruitLevel(activeFruitObj);
		var targetFruitLevel = getFruitLevel(targetFruitObj);
		var activeFruitRadius = activeFruitObj.width / 2;
		var targetFruitRadius = targetFruitObj.width / 2;
		var hitboxReduction = (Math.max(0, activeFruitLevel - 4) + Math.max(0, targetFruitLevel - 4)) * 2;
		var combinedRadii = activeFruitRadius + targetFruitRadius - hitboxReduction;
		var minDistanceSquared = combinedRadii * combinedRadii;
		return distanceSquared < minDistanceSquared;
	};
	return self;
});
/**** 
* Initialize Game
****/ 
var game = new LK.Game({
	backgroundColor: 0xffe122
});
/**** 
* Game Code
****/ 
// --- Constants ---
var _GAME_CONSTANTS;
function _typeof(o) {
	"@babel/helpers - typeof";
	return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) {
		return typeof o;
	} : function (o) {
		return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o;
	}, _typeof(o);
}
function _defineProperty(e, r, t) {
	return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, {
		value: t,
		enumerable: !0,
		configurable: !0,
		writable: !0
	}) : e[r] = t, e;
}
function _toPropertyKey(t) {
	var i = _toPrimitive(t, "string");
	return "symbol" == _typeof(i) ? i : i + "";
}
function _toPrimitive(t, r) {
	if ("object" != _typeof(t) || !t) return t;
	var e = t[Symbol.toPrimitive];
	if (void 0 !== e) {
		var i = e.call(t, r || "default");
		if ("object" != _typeof(i)) return i;
		throw new TypeError("@@toPrimitive must return a primitive value.");
	}
	return ("string" === r ? String : Number)(t);
}
var GAME_CONSTANTS = (_GAME_CONSTANTS = {
	GAME_WIDTH: 2048,
	GAME_HEIGHT: 2732,
	DROP_POINT_Y: 200,
	DROP_START_Y_OFFSET: 200,
	CLICK_DELAY_MS: 300,
	FRUIT_IMMUNITY_MS: 1000,
	MERGE_GRACE_MS: 2000,
	GAME_OVER_LINE_Y: 550,
	GAME_OVER_COUNTDOWN_MS: 3000,
	// Physics
	BASE_GRAVITY: 5.0,
	GRAVITY_LEVEL_MULTIPLIER: 0.4,
	// Adjusted gravity scaling
	FRICTION: 0.90,
	// Increased base friction
	ANGULAR_FRICTION: 0.80,
	GROUND_ANGULAR_FRICTION: 0.60,
	// Further increased ground friction
	MAX_ANGULAR_VELOCITY: 0.15,
	ELASTICITY_HIGH: 0.85,
	// Sleep state constants
	SLEEP_DELAY_FRAMES: 15,
	WAKE_UP_IMPULSE_THRESHOLD: 1.5,
	ANGULAR_STOP_THRESHOLD: 0.003
}, _defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_GAME_CONSTANTS, "SLEEP_DELAY_FRAMES", 15), "WAKE_UP_IMPULSE_THRESHOLD", 1.5), "ELASTICITY_LOW_START_LEVEL", 4), "ELASTICITY_LOW_BASE", 0.8), "ELASTICITY_DECREASE_FACTOR", 0.2 / 9), "MIN_FALL_SPEED", 0.1), "MAX_VELOCITY_BASE", 65), "MAX_VELOCITY_LEVEL_FACTOR", 5), "LEVEL_INERTIA_FACTOR", 0.05), "LEVEL_DOWNWARD_FORCE_FACTOR", 0.1), _defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_GAME_CONSTANTS, "BOUNCE_SOUND_VELOCITY_THRESHOLD", 0.5), "COLLISION_SEPARATION_FACTOR", 1.01), "INTER_FRUIT_FRICTION", 0.1), "ROTATION_TRANSFER_FACTOR", 0.01), "STABILIZATION_MOVEMENT_THRESHOLD_BASE", 0.7), "STABILIZATION_MOVEMENT_THRESHOLD_LEVEL_FACTOR", 0.08), "STABILIZATION_ANGULAR_THRESHOLD_BASE", 0.07), "STABILIZATION_ANGULAR_THRESHOLD_LEVEL_FACTOR", 0.006), "STABILIZATION_SCORE_RATE_BASE", 1.5), "STABILIZATION_SCORE_LEVEL_FACTOR", 0.2), _defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_GAME_CONSTANTS, "STABILIZATION_WALL_SCORE_RATE", 1.2), "STABILIZATION_SURROUNDED_SCORE_RATE", 1.5), "STABILIZATION_SCORE_LEVEL_INFLUENCE", 0.1), "STABILIZATION_THRESHOLD_FULL_BASE", 15), "STABILIZATION_THRESHOLD_FULL_LEVEL_FACTOR", 0.8), "STABILIZATION_THRESHOLD_PARTIAL_BASE", 8), "STABILIZATION_THRESHOLD_PARTIAL_LEVEL_FACTOR", 0.4), "STABILIZATION_DAMP_FACTOR_FULL", 0.6), "STABILIZATION_DAMP_FACTOR_PARTIAL", 0.85), "STABILIZATION_DAMP_LEVEL_FACTOR", 0.03), _defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_GAME_CONSTANTS, "STABILIZATION_STOP_THRESHOLD_BASE", 0.15), "STABILIZATION_STOP_THRESHOLD_LEVEL_FACTOR", 0.01), "SPATIAL_GRID_REBUILD_INTERVAL_MS", 60000), "SPATIAL_GRID_CELL_SIZE_FACTOR", 1.1), "CHARGE_NEEDED_FOR_RELEASE", 15), "PORTAL_UI_Y", 120), "PORTAL_UI_X_OFFSET", 870), "PORTAL_TWEEN_DURATION", 300), "PORTAL_PULSE_DURATION", 500), "PINEAPPLE_MERGES_NEEDED", 15), _defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_GAME_CONSTANTS, "PINEAPPLE_START_Y", 200), "PINEAPPLE_END_POS_FACTOR", 0.16), "PINEAPPLE_TWEEN_DURATION", 300), "PINEAPPLE_IMMUNITY_MS", 3000), "FIRE_BASE_COUNT", 3), "FIRE_FRUIT_TYPE_THRESHOLD", 1), "FIRE_MAX_COUNT", 15), "FIRE_START_Y_OFFSET", 50), "FIRE_STACK_Y_OFFSET", 100), "FIRE_X_SPREAD", 500), _defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_GAME_CONSTANTS, "FIRE_FLICKER_SPEED_BASE", 500), "FIRE_FLICKER_SPEED_RANDOM", 300), "FIRE_ALPHA_MIN", 0.1), "FIRE_ALPHA_MAX", 0.5), "FIRE_FRAME_DURATION", 200), "COCONUT_SPAWN_SCORE_INTERVAL", 1000), "EVOLUTION_LINE_Y", 120), "EVOLUTION_LINE_X_OFFSET", 350), "EVOLUTION_ICON_MAX_SIZE", 150), "EVOLUTION_ICON_SPACING", 20), _defineProperty(_defineProperty(_GAME_CONSTANTS, "SCORE_TEXT_Y", 400), "SCORE_TEXT_SIZE", 120));
var gameOverLine;
var pineapple;
var pineappleActive = false;
var pineapplePushCount = 0;
var readyToReleaseCharged = false;
var trajectoryLine;
var isClickable = true;
var evolutionLine;
var fireContainer;
var activeFireElements = [];
var fruitLevels = {
	'CHERRY': 1,
	'GRAPE': 2,
	'APPLE': 3,
	'ORANGE': 4,
	'WATERMELON': 5,
	'PINEAPPLE': 6,
	'MELON': 7,
	'PEACH': 8,
	'COCONUT': 9,
	'DURIAN': 10
};
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 = GAME_CONSTANTS.GAME_WIDTH;
var gameHeight = GAME_CONSTANTS.GAME_HEIGHT;
var fruits = [];
var nextFruitType = null;
var activeFruit = null;
var wallLeft, wallRight, gameFloor;
var dropPointY = GAME_CONSTANTS.DROP_POINT_Y;
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;
var lastScoreCheckForCoconut = 0;
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 >= GAME_CONSTANTS.PINEAPPLE_MERGES_NEEDED && !pineappleActive && pineapple) {
		pineappleActive = true;
		pineapple.isStatic = false;
		pineapple.immuneToGameOver = true;
		applyDropPhysics(pineapple, 2.5);
		fruits.push(pineapple);
		if (spatialGrid) spatialGrid.insertObject(pineapple);
		LK.setTimeout(function () {
			if (pineapple && fruits.includes(pineapple)) {
				pineapple.immuneToGameOver = false;
			}
		}, GAME_CONSTANTS.PINEAPPLE_IMMUNITY_MS);
		setupPineapple();
		mergeCounter = 0;
	}
}
function setupBoundaries() {
	wallLeft = game.addChild(LK.getAsset('wall', {
		anchorX: 0.5,
		anchorY: 0.5
	}));
	wallLeft.x = 0;
	wallLeft.y = gameHeight / 2;
	wallLeft.alpha = 0;
	wallRight = game.addChild(LK.getAsset('wall', {
		anchorX: 0.5,
		anchorY: 0.5
	}));
	wallRight.x = gameWidth;
	wallRight.y = gameHeight / 2;
	wallRight.alpha = 0;
	gameFloor = game.addChild(LK.getAsset('floor', {
		anchorX: 0.5,
		anchorY: 0.5
	}));
	gameFloor.x = gameWidth / 2;
	gameFloor.y = gameHeight;
	gameFloor.alpha = 0;
	gameOverLine = game.addChild(new Line());
	gameOverLine.x = gameWidth / 2;
	gameOverLine.y = GAME_CONSTANTS.GAME_OVER_LINE_Y;
	gameOverLine.scaleX = gameWidth / 100; // Scale to full screen width (Line is 100px wide by default)
	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 + GAME_CONSTANTS.DROP_START_Y_OFFSET;
	activeFruit.isStatic = true;
	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;
	}, GAME_CONSTANTS.CLICK_DELAY_MS);
	activeFruit.isStatic = false;
	applyDropPhysics(activeFruit, 3.5);
	fruits.push(activeFruit);
	spatialGrid.insertObject(activeFruit);
	lastDroppedFruit = activeFruit;
	lastDroppedHasMerged = false;
	chargeCounter++;
	updateChargedBallDisplay();
	if (chargeCounter >= GAME_CONSTANTS.CHARGE_NEEDED_FOR_RELEASE && !readyToReleaseCharged) {
		releaseChargedBalls();
	}
	activeFruit.mergeGracePeriodActive = true;
	LK.setTimeout(function () {
		if (activeFruit && fruits.includes(activeFruit)) {
			activeFruit.mergeGracePeriodActive = false;
		}
	}, GAME_CONSTANTS.MERGE_GRACE_MS);
	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 >= GAME_CONSTANTS.CHARGE_NEEDED_FOR_RELEASE) {
		LK.getSound('pickleRick').play();
		var orange = new Fruit(FruitTypes.ORANGE);
		orange.hasBounced = false;
		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);
		chargeCounter = 0;
		resetChargedBalls();
		readyToReleaseCharged = false;
	}
	activeFruit = null;
	createNextFruit();
}
function applyDropPhysics(fruit, forceMultiplier) {
	var fruitLevel = getFruitLevel(fruit);
	forceMultiplier *= 1.4;
	var levelAdjustedForce = forceMultiplier * (1 - fruitLevel * 0.05);
	var angle = (Math.random() * 20 - 10) * (Math.PI / 180);
	fruit.vx = Math.sin(angle) * levelAdjustedForce;
	fruit.vy = Math.abs(Math.cos(angle) * levelAdjustedForce) * 1.5;
	fruit.angularVelocity = 0; // Explicitly reset angular velocity on drop
	fruit.gravity = GAME_CONSTANTS.BASE_GRAVITY * (1 + fruitLevel * GAME_CONSTANTS.GRAVITY_LEVEL_MULTIPLIER);
	fruit.safetyPeriod = false;
	fruit.immuneToGameOver = true;
	fruit.isFullyStabilized = false; // Reset stabilization state
	fruit.isSleeping = false; // Wake up the fruit when dropping
	fruit._sleepCounter = 0; // Reset sleep counter - ensure even if undefined
	LK.setTimeout(function () {
		if (fruit && fruits.includes(fruit)) {
			fruit.immuneToGameOver = false;
		}
	}, GAME_CONSTANTS.FRUIT_IMMUNITY_MS);
}
function updateScoreDisplay() {
	scoreText.setText(LK.getScore());
}
function setupUI() {
	scoreText = new Text2("0", {
		size: GAME_CONSTANTS.SCORE_TEXT_SIZE,
		fill: 0x000000
	});
	scoreText.anchor.set(0.5, 0);
	LK.gui.top.addChild(scoreText);
	scoreText.y = GAME_CONSTANTS.SCORE_TEXT_Y;
	setupChargedBallDisplay();
}
function setupChargedBallDisplay() {
	chargedBallUI = new ChargedBallUI();
	// Initialization moved to class constructor
	game.addChild(chargedBallUI);
}
function updateChargedBallDisplay() {
	chargedBallUI && chargedBallUI.updateChargeDisplay(chargeCounter);
}
function releaseChargedBalls() {
	readyToReleaseCharged = true;
	chargedBallUI && chargedBallUI.setReadyState(true);
}
function resetChargedBalls() {
	chargedBallUI && chargedBallUI.reset();
}
function checkFruitCollisions() {
	for (var k = 0; k < fruits.length; k++) {
		if (fruits[k]) {
			fruits[k].neighboringFruits = 0;
			fruits[k].neighborContacts = fruits[k].neighborContacts || [];
			fruits[k].neighborContacts = [];
			// Reset surroundedFrames here as it's primarily determined by collisions
			fruits[k].surroundedFrames = 0;
		}
	}
	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 level1 = getFruitLevel(fruit1);
			var level2 = getFruitLevel(fruit2);
			var hitboxReduction = (Math.max(0, level1 - 4) + Math.max(0, level2 - 4)) * 2; // Use simplified reduction
			var combinedHalfWidths = fruit1HalfWidth + fruit2HalfWidth - hitboxReduction / 2;
			var combinedHalfHeights = fruit1HalfHeight + fruit2HalfHeight - hitboxReduction / 2;
			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;
					}
					// Calculate precise overlap along the collision normal
					var overlap = combinedHalfWidths - absDistanceX;
					if (absDistanceY < combinedHalfHeights && overlap > 0) {
						// Use most accurate overlap calculation
						var verticalOverlap = combinedHalfHeights - absDistanceY;
						if (verticalOverlap < overlap) {
							overlap = verticalOverlap;
						}
						// Use exact normal vector from centers
						var normalizeX = dx / distance;
						var normalizeY = dy / distance;
						// Calculate proportional separation based on fruit mass
						var level1 = getFruitLevel(fruit1);
						var level2 = getFruitLevel(fruit2);
						// Use a strongly exponential formula for mass based on level - using power of 3.0
						var mass1 = Math.pow(level1, 3.0);
						var mass2 = Math.pow(level2, 3.0);
						var totalMass = mass1 + mass2;
						var moveRatio1 = totalMass > 0 ? mass2 / totalMass : 0.5;
						var moveRatio2 = totalMass > 0 ? mass1 / totalMass : 0.5;
						// Apply separation proportional to mass
						var moveX = overlap * normalizeX;
						var moveY = overlap * normalizeY;
						// Apply smaller separation factor for more stable stacking
						var separationFactor = GAME_CONSTANTS.COLLISION_SEPARATION_FACTOR;
						fruit1.x -= moveX * moveRatio1 * separationFactor;
						fruit1.y -= moveY * moveRatio1 * separationFactor;
						fruit2.x += moveX * moveRatio2 * separationFactor;
						fruit2.y += moveY * moveRatio2 * 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;
						// Use strongly exponential mass formula to exaggerate weight differences
						var mass1 = Math.pow(level1, 3.0); // Removed size dependency, using pure level exponent
						var mass2 = Math.pow(level2, 3.0);
						var totalMass = mass1 + mass2;
						// Let impulse ratios be calculated directly from the exaggerated masses
						var impulseRatio1 = totalMass > 0 ? mass2 / totalMass : 0.5;
						var impulseRatio2 = totalMass > 0 ? mass1 / totalMass : 0.5;
						var velocityMagnitude = Math.sqrt(rvX * rvX + rvY * rvY);
						var velocityDampening = velocityMagnitude > 5 ? 0.5 + 2.5 / velocityMagnitude : 1.0;
						var impulse1 = impulse * impulseRatio1 * velocityDampening;
						var impulse2 = impulse * impulseRatio2 * velocityDampening;
						// Apply impulses with sleeping state awareness but no special level-based adjustments
						var impactThreshold = 1.2; // Threshold to determine significant impacts
						// For first fruit: apply impulse and wake up if needed
						// Wake up sleeping fruits on significant impacts based on WAKE_UP_IMPULSE_THRESHOLD
						if (fruit1.isSleeping) {
							if (Math.abs(impulse1) > GAME_CONSTANTS.WAKE_UP_IMPULSE_THRESHOLD) {
								// Wake up if impact is very significant
								fruit1.isSleeping = false;
								fruit1.isFullyStabilized = false;
								fruit1._sleepCounter = 0;
								fruit1.vx -= impulse1 * normalizeX;
								fruit1.vy -= impulse1 * normalizeY;
							} else {
								// Keep sleeping but ensure proper state tracking
								fruit1.vx = 0;
								fruit1.vy = 0;
								fruit1.angularVelocity = 0;
							}
						} else if (fruit1.isFullyStabilized) {
							// Apply minimal impulse to stabilized fruits - just enough to handle edge cases
							if (Math.abs(impulse1) > impactThreshold) {
								// If impact is large, unstabilize and apply full impulse
								fruit1.isFullyStabilized = false;
								fruit1._sleepCounter = 0; // Reset sleep counter on destabilization
								fruit1.vx -= impulse1 * normalizeX;
								fruit1.vy -= impulse1 * normalizeY;
							} else {
								// For small impacts, apply minimal response that won't disturb stability
								fruit1.vx -= impulse1 * normalizeX * 0.1;
								fruit1.vy -= impulse1 * normalizeY * 0.1;
							}
						} else {
							fruit1.vx -= impulse1 * normalizeX;
							fruit1.vy -= impulse1 * normalizeY;
						}
						// For second fruit: apply impulse with the same sleep/stabilization logic
						if (fruit2.isSleeping) {
							if (Math.abs(impulse2) > GAME_CONSTANTS.WAKE_UP_IMPULSE_THRESHOLD) {
								// Wake up if impact is very significant
								fruit2.isSleeping = false;
								fruit2.isFullyStabilized = false;
								fruit2._sleepCounter = 0;
								fruit2.vx += impulse2 * normalizeX;
								fruit2.vy += impulse2 * normalizeY;
							} else {
								// Keep sleeping but ensure proper state tracking
								fruit2.vx = 0;
								fruit2.vy = 0;
								fruit2.angularVelocity = 0;
							}
						} else if (fruit2.isFullyStabilized) {
							// Apply minimal impulse to stabilized fruits - just enough to handle edge cases
							if (Math.abs(impulse2) > impactThreshold) {
								// If impact is large, unstabilize and apply full impulse
								fruit2.isFullyStabilized = false;
								fruit2._sleepCounter = 0; // Reset sleep counter on destabilization
								fruit2.vx += impulse2 * normalizeX;
								fruit2.vy += impulse2 * normalizeY;
							} else {
								// For small impacts, apply minimal response that won't disturb stability  
								fruit2.vx += impulse2 * normalizeX * 0.1;
								fruit2.vy += impulse2 * normalizeY * 0.1;
							}
						} else {
							fruit2.vx += impulse2 * normalizeX;
							fruit2.vy += impulse2 * normalizeY;
						}
						// Apply additional post-collision damping to dissipate energy more rapidly
						fruit1.vx *= 0.95; // Increased damping factor to reduce velocities after collision
						fruit1.vy *= 0.95;
						fruit2.vx *= 0.95;
						fruit2.vy *= 0.95;
						// Apply stronger damping for low-velocity collisions to promote settling
						if (velocityMagnitude < 3.0) {
							fruit1.vx *= 0.92;
							fruit1.vy *= 0.92;
							fruit2.vx *= 0.92;
							fruit2.vy *= 0.92;
						}
						var tangentX = -normalizeY;
						var tangentY = normalizeX;
						var tangentVelocity = rvX * tangentX + rvY * tangentY;
						var frictionImpulse = -tangentVelocity * GAME_CONSTANTS.INTER_FRUIT_FRICTION;
						// Apply tangential friction with same stabilization logic
						if (!fruit1.isFullyStabilized) {
							fruit1.vx -= frictionImpulse * tangentX * impulseRatio1;
							fruit1.vy -= frictionImpulse * tangentY * impulseRatio1;
						}
						if (!fruit2.isFullyStabilized) {
							fruit2.vx += frictionImpulse * tangentX * impulseRatio2;
							fruit2.vy += frictionImpulse * tangentY * impulseRatio2;
						}
						var tangentialComponent = rvX * tangentX + rvY * tangentY;
						var inertia1 = mass1 * Math.pow(fruit1.width / 2, 2);
						var inertia2 = mass2 * Math.pow(fruit2.width / 2, 2);
						// Only apply angular impulse if the tangential component or contact velocity is significant
						var tangentialMagnitude = Math.abs(tangentialComponent);
						var contactMagnitude = Math.abs(contactVelocity);
						var angularThreshold = 1.2; // Increased threshold for generating rotational force
						// Calculate angular impulse with adjustment for impact magnitude
						var angularImpulse = 0;
						if (tangentialMagnitude > angularThreshold || contactMagnitude > angularThreshold) {
							// Apply a curve that increases rotation for more significant impacts
							var impactFactor = Math.min(1.0, (Math.max(tangentialMagnitude, contactMagnitude) - angularThreshold) / 2.0);
							angularImpulse = tangentialComponent * GAME_CONSTANTS.ROTATION_TRANSFER_FACTOR * impactFactor;
						}
						// Apply angular velocity with same stabilization logic
						if (!fruit1.isFullyStabilized && angularImpulse !== 0) {
							fruit1.angularVelocity += inertia2 > 0 ? angularImpulse * (inertia1 / (inertia1 + inertia2)) : angularImpulse * 0.5;
							fruit1.angularVelocity *= 0.95;
						}
						if (!fruit2.isFullyStabilized && angularImpulse !== 0) {
							fruit2.angularVelocity -= inertia1 > 0 ? angularImpulse * (inertia2 / (inertia1 + inertia2)) : angularImpulse * 0.5;
							fruit2.angularVelocity *= 0.95;
						}
						// Only unstabilize on significantly large angular impacts
						if (Math.abs(angularImpulse) > 0.015) {
							// Unstabilize on significant angular impact
							if (fruit1.isFullyStabilized) fruit1.isFullyStabilized = false;
							if (fruit2.isFullyStabilized) fruit2.isFullyStabilized = false;
						}
						fruit1.angularVelocity = Math.min(Math.max(fruit1.angularVelocity, -fruit1.maxAngularVelocity), fruit1.maxAngularVelocity);
						fruit2.angularVelocity = Math.min(Math.max(fruit2.angularVelocity, -fruit2.maxAngularVelocity), fruit2.maxAngularVelocity);
						fruit1.neighboringFruits = (fruit1.neighboringFruits || 0) + 1;
						fruit2.neighboringFruits = (fruit2.neighboringFruits || 0) + 1;
						fruit1.surroundedFrames++; // Increment surrounded counter on collision
						fruit2.surroundedFrames++;
						fruit1.neighborContacts = fruit1.neighborContacts || [];
						fruit2.neighborContacts = fruit2.neighborContacts || [];
						if (fruit1.neighborContacts.indexOf(fruit2.id) === -1) fruit1.neighborContacts.push(fruit2.id);
						if (fruit2.neighborContacts.indexOf(fruit1.id) === -1) fruit2.neighborContacts.push(fruit1.id);
						// Removed bounce flag dependent damping
					}
				}
			}
		}
	}
}
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 fruitLevel = getFruitLevel(fruit);
		var sizeReduction = Math.max(0, fruitLevel - 4) * 2;
		fruitHalfHeight = Math.max(10, fruitHalfHeight - sizeReduction / 2);
		fruitHalfWidth = Math.max(10, fruitHalfWidth - sizeReduction / 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;
				// Game over if fruit is overlapping line AND not moving significantly OR fully stabilized
				var stableOrSlowing = Math.abs(fruit.vy) < 1.0 || fruit.isFullyStabilized || fruit._boundaryContacts && (fruit._boundaryContacts.left || fruit._boundaryContacts.right || fruit._boundaryContacts.floor);
				if (stableOrSlowing) {
					if (!fruit.gameOverTimer) {
						fruit.gameOverTimer = Date.now();
						tween(fruit, {
							alpha: 0.5
						}, {
							duration: 500,
							easing: tween.easeInOut,
							onFinish: function onFinish() {
								tween(fruit, {
									alpha: 1
								}, {
									duration: 500,
									easing: tween.easeInOut,
									repeat: 2
								});
							}
						});
						continue;
					}
					var currentTime = Date.now();
					if (currentTime - fruit.gameOverTimer >= GAME_CONSTANTS.GAME_OVER_COUNTDOWN_MS) {
						gameOver = true;
						LK.showGameOver();
						return;
					}
				} else {
					// Reset timer if fruit starts moving significantly while overlapping
					fruit.gameOverTimer = null;
					fruit.alpha = 1;
				}
			} else {
				// Reset timer if no longer overlapping horizontally
				if (fruit.gameOverTimer) {
					fruit.gameOverTimer = null;
					fruit.alpha = 1;
				}
			}
		} else {
			fruit.safetyPeriod = undefined;
			if (fruit.gameOverTimer) {
				fruit.gameOverTimer = null;
				fruit.alpha = 1;
			}
		}
	}
}
function setupPineapple() {
	pineapple = new Fruit(FruitTypes.PINEAPPLE);
	pineapple.x = -pineapple.width / 2;
	pineapple.y = GAME_CONSTANTS.PINEAPPLE_START_Y;
	pineapple.isStatic = true;
	pineappleActive = false;
	pineapplePushCount = 0;
	game.addChild(pineapple);
}
function pushPineapple() {
	if (!pineappleActive && pineapple) {
		var step = mergeCounter;
		var totalSteps = GAME_CONSTANTS.PINEAPPLE_MERGES_NEEDED;
		var percentage = Math.min(step / totalSteps, 1.0);
		var startPos = -pineapple.width / 2;
		var endPos = gameWidth * GAME_CONSTANTS.PINEAPPLE_END_POS_FACTOR;
		var newX = startPos + percentage * (endPos - startPos);
		tween(pineapple, {
			x: newX
		}, {
			duration: GAME_CONSTANTS.PINEAPPLE_TWEEN_DURATION,
			easing: tween.bounceOut
		});
	}
}
function initGame() {
	LK.setScore(0);
	gameOver = false;
	if (fruits && fruits.length > 0) {
		// Ensure cleanup only happens if needed
		for (var i = fruits.length - 1; i >= 0; i--) {
			if (fruits[i]) removeFruitFromGame(fruits[i]);
		}
	}
	fruits = [];
	chargeCounter = 0;
	if (chargedBallUI) chargedBallUI.destroy();
	chargedBallUI = null;
	readyToReleaseCharged = false;
	lastScoreCheckForCoconut = 0;
	lastDroppedFruit = null;
	lastDroppedHasMerged = false;
	mergeCounter = 0;
	isClickable = true;
	if (fireContainer) {
		for (var i = activeFireElements.length - 1; i >= 0; i--) {
			if (activeFireElements[i]) activeFireElements[i].destroy();
		}
		fireContainer.destroy();
	}
	fireContainer = new Container();
	game.addChildAt(fireContainer, 0);
	activeFireElements = [];
	if (spatialGrid) {
		spatialGrid.clear();
	} else {
		var avgFruitSize = 0;
		var fruitCount = 0;
		for (var fruitType in FruitTypes) {
			if (FruitTypes.hasOwnProperty(fruitType)) {
				avgFruitSize += FruitTypes[fruitType].size;
				fruitCount++;
			}
		}
		avgFruitSize = fruitCount > 0 ? avgFruitSize / fruitCount : 300;
		var optimalCellSize = Math.ceil(avgFruitSize * GAME_CONSTANTS.SPATIAL_GRID_CELL_SIZE_FACTOR);
		spatialGrid = new SpatialGrid(optimalCellSize);
	}
	// Destroy existing boundaries before setup
	if (wallLeft) wallLeft.destroy();
	if (wallRight) wallRight.destroy();
	if (gameFloor) gameFloor.destroy();
	if (gameOverLine) gameOverLine.destroy();
	if (pineapple) pineapple.destroy();
	if (trajectoryLine) trajectoryLine.destroy();
	if (evolutionLine) evolutionLine.destroy();
	LK.playMusic('bgmusic'); // Start music after potential cleanup
	setupBoundaries();
	setupUI();
	setupPineapple();
	updateFireBackground();
	trajectoryLine = game.addChild(new TrajectoryLine());
	trajectoryLine.createDots();
	evolutionLine = game.addChild(new EvolutionLine());
	evolutionLine.initialize();
	updateScoreDisplay();
	activeFruit = null;
	createNextFruit();
	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;
	game.addChild(coconut);
	fruits.push(coconut);
	coconut.safetyPeriod = false;
	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;
			coconut.isStatic = false;
			coconut.vy = -2;
			coconut.vx = (Math.random() * 2 - 1) * 1.5;
			spatialGrid.insertObject(coconut);
			LK.setTimeout(function () {
				if (coconut && fruits.includes(coconut)) coconut.immuneToGameOver = false;
			}, 1000);
		}
	});
}
game.down = function (x, y) {
	if (activeFruit && !gameOver) {
		isDragging = true;
		game.move(x, y);
	}
};
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));
		activeFruit.y = dropPointY + GAME_CONSTANTS.DROP_START_Y_OFFSET;
		if (trajectoryLine) trajectoryLine.updateTrajectory(activeFruit.x, activeFruit.y);
	}
};
game.up = function () {
	if (isDragging && activeFruit && isClickable && !gameOver) dropFruit();
	isDragging = false;
};
function updatePhysics() {
	if (spatialGrid && Date.now() - spatialGrid.lastRebuildTime > spatialGrid.rebuildInterval) {
		spatialGrid.rebuildGrid(fruits);
	}
	// First phase: Apply gravity and update velocities
	for (var i = fruits.length - 1; i >= 0; i--) {
		var fruit = fruits[i];
		if (!fruit || fruit.isStatic || fruit.merging) continue;
		// Apply physics without position update (updatePhysics handles this)
		fruit.updatePhysics();
	}
	// Second phase: Check collisions and resolve overlaps
	checkFruitCollisions();
	// Third phase: Apply boundary collisions and finalize positions
	for (var i = fruits.length - 1; i >= 0; i--) {
		var fruit = fruits[i];
		if (!fruit || fruit.isStatic || fruit.merging) continue;
		var walls = {
			left: wallLeft,
			right: wallRight
		};
		fruit.checkBoundaries(walls, gameFloor);
		// Final stabilization check - aggressively zero out extremely small velocity values
		if (fruit.isFullyStabilized) {
			// If fully stabilized, ensure complete zero velocity to prevent micro-movements
			fruit.vx = 0;
			fruit.vy = 0;
			fruit.angularVelocity = 0;
		} else if (!fruit.isSleeping && fruit._boundaryContacts && (fruit._boundaryContacts.floor || fruit._boundaryContacts.left || fruit._boundaryContacts.right)) {
			// For fruits touching boundaries but not fully stabilized, apply additional damping
			// to encourage them to come to a full stop more quickly
			if (Math.abs(fruit.vx) < 0.2) fruit.vx = 0;
			if (Math.abs(fruit.vy) < 0.2) fruit.vy = 0;
			if (Math.abs(fruit.angularVelocity) < 0.01) fruit.angularVelocity = 0;
		}
	}
	// Fourth phase: Handle environmental interactions and position adjustments
	for (var i = 0; i < fruits.length; i++) {
		var fruit = fruits[i];
		if (fruit && !fruit.isStatic && !fruit.merging) {
			if (fruit.surroundedFrames === undefined) fruit.surroundedFrames = 0;
			var boundaryContact = fruit.wallContactFrames > 0;
			var floorProximity = fruit.y + fruit.height / 2 >= gameFloor.y - gameFloor.height / 2 - 10;
			var hasMultipleNeighbors = fruit.neighborContacts && fruit.neighborContacts.length >= 2;
			var isSlowMoving = Math.abs(fruit.vx) < 0.3 && Math.abs(fruit.vy) < 0.3;
			var hasGapBelow = false;
			if (floorProximity || hasMultipleNeighbors) {
				var potentialNeighbors = spatialGrid.getPotentialCollisions({
					x: fruit.x,
					y: fruit.y + fruit.height / 2 + 20,
					width: fruit.width * 0.6,
					height: 20,
					id: 'gap_check_' + fruit.id
				});
				hasGapBelow = potentialNeighbors.length === 0 && !floorProximity;
				if (hasGapBelow && isSlowMoving) {
					var leftCount = 0,
						rightCount = 0;
					for (var j = 0; j < fruit.neighborContacts.length; j++) {
						var neighborId = fruit.neighborContacts[j];
						for (var k = 0; k < fruits.length; k++) {
							if (fruits[k] && fruits[k].id === neighborId) {
								if (fruits[k].x < fruit.x) leftCount++;else if (fruits[k].x > fruit.x) rightCount++;
								break;
							}
						}
					}
					if (leftCount < rightCount) fruit.vx -= 0.05;else if (rightCount < leftCount) fruit.vx += 0.05;else fruit.vx += Math.random() * 0.1 - 0.05;
				}
			}
			// Final position update in the spatial grid
			spatialGrid.updateObject(fruit);
		}
	}
}
game.update = function () {
	if (gameOver) return;
	var currentScore = LK.getScore();
	if (currentScore >= lastScoreCheckForCoconut + GAME_CONSTANTS.COCONUT_SPAWN_SCORE_INTERVAL) {
		lastScoreCheckForCoconut = Math.floor(currentScore / GAME_CONSTANTS.COCONUT_SPAWN_SCORE_INTERVAL) * GAME_CONSTANTS.COCONUT_SPAWN_SCORE_INTERVAL;
		spawnCoconut();
	} else {
		if (currentScore > lastScoreCheckForCoconut) {
			lastScoreCheckForCoconut = Math.floor(currentScore / GAME_CONSTANTS.COCONUT_SPAWN_SCORE_INTERVAL) * GAME_CONSTANTS.COCONUT_SPAWN_SCORE_INTERVAL;
		}
	}
	updatePhysics();
	checkGameOver();
	updateFireBackground();
	for (var i = 0; i < activeFireElements.length; i++) {
		if (activeFireElements[i]) activeFireElements[i].update();
	}
};
initGame();
function updateFireBackground() {
	var uniqueFruitTypes = {};
	for (var i = 0; i < fruits.length; i++) {
		var fruit = fruits[i];
		if (fruit && !fruit.isStatic && !fruit.merging && fruit.type && fruit.type.id) {
			uniqueFruitTypes[fruit.type.id] = true;
		}
	}
	var uniqueTypeCount = Object.keys(uniqueFruitTypes).length;
	// Updated fire count logic based on unique types present
	var targetFireCount = GAME_CONSTANTS.FIRE_BASE_COUNT + Math.floor(uniqueTypeCount / GAME_CONSTANTS.FIRE_FRUIT_TYPE_THRESHOLD);
	targetFireCount = Math.min(targetFireCount, GAME_CONSTANTS.FIRE_MAX_COUNT);
	if (targetFireCount > activeFireElements.length) {
		var newFiresCount = targetFireCount - activeFireElements.length;
		for (var i = 0; i < newFiresCount; i++) {
			createFireElement(activeFireElements.length);
		}
	} else if (targetFireCount < activeFireElements.length) {
		var fireToRemoveCount = activeFireElements.length - targetFireCount;
		for (var i = 0; i < fireToRemoveCount; i++) {
			var fire = activeFireElements.pop();
			if (fire) {
				fire.destroy();
				fireContainer.removeChild(fire);
			}
		}
	}
}
function createFireElement(index) {
	var yPos = gameHeight + GAME_CONSTANTS.FIRE_START_Y_OFFSET - index * GAME_CONSTANTS.FIRE_STACK_Y_OFFSET;
	var xPos = gameWidth / 2 + (Math.random() * GAME_CONSTANTS.FIRE_X_SPREAD - GAME_CONSTANTS.FIRE_X_SPREAD / 2);
	var newFire = new FireElement(xPos, yPos);
	if (index % 2 === 1) {
		newFire.fireAsset.scaleX = -1;
	}
	fireContainer.addChildAt(newFire, 0);
	activeFireElements.push(newFire);
}
function removeFruitFromGame(fruit) {
	// Before removing, wake up any sleeping neighbors that were resting on this fruit
	if (fruit.neighborContacts && fruit.neighborContacts.length > 0) {
		for (var i = 0; i < fruit.neighborContacts.length; i++) {
			var neighborId = fruit.neighborContacts[i];
			for (var j = 0; j < fruits.length; j++) {
				if (fruits[j] && fruits[j].id === neighborId) {
					// Wake up any sleeping neighbors
					if (fruits[j].isSleeping || fruits[j].isFullyStabilized) {
						fruits[j].isSleeping = false;
						fruits[j].isFullyStabilized = false;
						fruits[j]._sleepCounter = 0; // Always reset sleep counter (don't check if exists)
						// Apply a small impulse to ensure physics recalculation
						fruits[j].vx += Math.random() * 0.6 - 0.3;
						fruits[j].vy -= 0.5; // Small upward force
					}
				}
			}
		}
	}
	var index = fruits.indexOf(fruit);
	if (index !== -1) fruits.splice(index, 1);
	spatialGrid.removeObject(fruit);
	fruit.destroy();
} /**** 
* 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);
	self.chargeNeededForRelease = GAME_CONSTANTS.CHARGE_NEEDED_FOR_RELEASE;
	self.currentCharge = 0;
	self.isReadyToRelease = false;
	self.pulseAnimationActive = false;
	self.initialize = function () {
		self.portalAsset = self.attachAsset('portal', {
			anchorX: 0.5,
			anchorY: 0.5
		});
		self.portalAsset.x = GAME_CONSTANTS.GAME_WIDTH / 2 + GAME_CONSTANTS.PORTAL_UI_X_OFFSET;
		self.portalAsset.alpha = 0;
		self.portalAsset.scaleX = 0;
		self.portalAsset.scaleY = 0;
		self.y = GAME_CONSTANTS.PORTAL_UI_Y;
	};
	self.updateChargeDisplay = function (chargeCount) {
		self.currentCharge = chargeCount;
		var remainingCount = Math.max(0, self.chargeNeededForRelease - self.currentCharge);
		var progressPercent = (self.chargeNeededForRelease - remainingCount) / self.chargeNeededForRelease;
		var targetScale = progressPercent;
		if (progressPercent > 0 && self.portalAsset.alpha === 0) {
			self.portalAsset.alpha = 1;
		}
		tween(self.portalAsset, {
			scaleX: targetScale,
			scaleY: targetScale,
			alpha: progressPercent
		}, {
			duration: GAME_CONSTANTS.PORTAL_TWEEN_DURATION,
			easing: tween.easeOut
		});
		if (remainingCount === 0 && !self.isReadyToRelease) {
			self.setReadyState(true);
		}
	};
	self.setReadyState = function (isReady) {
		self.isReadyToRelease = isReady;
		if (isReady) {
			tween(self.portalAsset, {
				scaleX: 1.0,
				scaleY: 1.0,
				alpha: 1.0,
				rotation: Math.PI * 2
			}, {
				duration: GAME_CONSTANTS.PORTAL_TWEEN_DURATION,
				easing: tween.easeOut
			});
			self.startPulseAnimation();
		}
	};
	self.startPulseAnimation = function () {
		if (self.pulseAnimationActive) return;
		self.pulseAnimationActive = true;
		self._pulseText();
	};
	self._pulseText = function () {
		if (!self.isReadyToRelease) {
			self.pulseAnimationActive = false;
			return;
		}
		tween(self.portalAsset, {
			scaleX: 1.3,
			scaleY: 1.3
		}, {
			duration: GAME_CONSTANTS.PORTAL_PULSE_DURATION,
			easing: tween.easeInOut,
			onFinish: function onFinish() {
				if (!self.isReadyToRelease) {
					self.pulseAnimationActive = false;
					return;
				}
				tween(self.portalAsset, {
					scaleX: 1.0,
					scaleY: 1.0
				}, {
					duration: GAME_CONSTANTS.PORTAL_PULSE_DURATION,
					easing: tween.easeInOut,
					onFinish: self._pulseText
				});
			}
		});
	};
	self.reset = function () {
		self.isReadyToRelease = false;
		self.currentCharge = 0;
		self.pulseAnimationActive = false;
		tween(self.portalAsset, {
			alpha: 0
		}, {
			duration: 200,
			easing: tween.easeOut
		});
		tween(self.portalAsset, {
			scaleX: 0,
			scaleY: 0
		}, {
			duration: 200,
			easing: tween.easeOut
		});
	};
	self.initialize();
	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 fruitLevel = getFruitLevel(fruit);
		var sizeReduction = Math.max(0, fruitLevel - 4) * 2;
		fruitHalfWidth = Math.max(10, fruitHalfWidth - sizeReduction / 2);
		fruitHalfHeight = Math.max(10, fruitHalfHeight - sizeReduction / 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;
		fruit._boundaryContacts = fruit._boundaryContacts || {
			left: false,
			right: false,
			floor: false
		};
		fruit._boundaryContacts.left = false;
		fruit._boundaryContacts.right = false;
		fruit._boundaryContacts.floor = false;
		self.checkLeftWallCollision(fruit, walls.left, effectiveWidth);
		self.checkRightWallCollision(fruit, walls.right, effectiveWidth);
		self.checkFloorCollision(fruit, floor, effectiveHeight);
		var isInContact = fruit._boundaryContacts.left || fruit._boundaryContacts.right || fruit._boundaryContacts.floor;
		if (isInContact) {
			fruit.wallContactFrames++;
			var progressiveFriction = Math.min(0.75, 0.55 + fruit.wallContactFrames * 0.015);
			fruit.angularVelocity *= progressiveFriction;
			if (fruit.wallContactFrames > 15) {
				fruit.vx *= 0.75;
				fruit.vy *= 0.75;
				fruit.angularVelocity *= 0.5;
			} else if (fruit.wallContactFrames > 10) {
				fruit.vx *= 0.8;
				fruit.vy *= 0.85;
				fruit.angularVelocity *= 0.7;
			} else if (fruit.wallContactFrames > 5) {
				fruit.vx *= 0.9;
				fruit.vy *= 0.9;
			}
		} else {
			fruit.wallContactFrames = Math.max(0, fruit.wallContactFrames - 1);
		}
	};
	self.checkLeftWallCollision = function (fruit, leftWall, effectiveWidth) {
		var leftBoundary = leftWall.x + leftWall.width / 2 + effectiveWidth;
		if (fruit.x < leftBoundary) {
			var incomingVx = fruit.vx;
			fruit.x = leftBoundary;
			fruit.vx = -incomingVx * fruit.elasticity * 0.7;
			if (Math.abs(incomingVx) > GAME_CONSTANTS.BOUNCE_SOUND_VELOCITY_THRESHOLD) {
				LK.getSound('bounce').play();
			}
			var angularImpactMultiplier = 0.0025 * (1 + (0.9 - fruit.elasticity) * 5);
			fruit.angularVelocity += fruit.vy * angularImpactMultiplier * 0.25;
			fruit.angularVelocity *= GAME_CONSTANTS.GROUND_ANGULAR_FRICTION * 0.4;
			fruit._boundaryContacts.left = true;
		} else if (fruit.x > leftWall.x + leftWall.width * 2) {
			if (fruit._boundaryContacts && !fruit._boundaryContacts.right && !fruit._boundaryContacts.floor) {
				fruit.wallContactFrames = Math.max(0, fruit.wallContactFrames - 1);
			}
		}
	};
	self.checkRightWallCollision = function (fruit, rightWall, effectiveWidth) {
		var rightBoundary = rightWall.x - rightWall.width / 2 - effectiveWidth;
		if (fruit.x > rightBoundary) {
			var incomingVx = fruit.vx;
			fruit.x = rightBoundary;
			fruit.vx = -incomingVx * fruit.elasticity * 0.7;
			if (Math.abs(incomingVx) > GAME_CONSTANTS.BOUNCE_SOUND_VELOCITY_THRESHOLD) {
				LK.getSound('bounce').play();
			}
			var angularImpactMultiplier = 0.0025 * (1 + (0.9 - fruit.elasticity) * 5);
			fruit.angularVelocity -= fruit.vy * angularImpactMultiplier * 0.25;
			fruit.angularVelocity *= GAME_CONSTANTS.GROUND_ANGULAR_FRICTION * 0.4;
			fruit._boundaryContacts.right = true;
		}
	};
	self.checkFloorCollision = function (fruit, floor, effectiveHeight) {
		var floorCollisionY = floor.y - floor.height / 2 - effectiveHeight;
		if (fruit.y > floorCollisionY) {
			var incomingVy = fruit.vy;
			fruit.y = floorCollisionY;
			fruit.vy = -incomingVy * fruit.elasticity * 0.5;
			if (Math.abs(incomingVy) > GAME_CONSTANTS.BOUNCE_SOUND_VELOCITY_THRESHOLD) {
				LK.getSound('bounce').play();
			}
			fruit._boundaryContacts.floor = true;
			if (Math.abs(fruit.vx) > 0.5) {
				fruit.angularVelocity = fruit.vx * 0.008;
			}
			fruit.angularVelocity *= GAME_CONSTANTS.GROUND_ANGULAR_FRICTION * 0.4;
			var restThreshold = fruit.wallContactFrames > 5 ? 1.5 : 2.5;
			if (Math.abs(fruit.vy) < restThreshold) {
				fruit.vy = 0;
				fruit.vx *= 0.6;
				if (fruit.wallContactFrames > 8) {
					fruit.vx *= 0.75;
				}
			}
			var angularRestThreshold = fruit.wallContactFrames > 5 ? 0.012 : 0.018;
			if (Math.abs(fruit.angularVelocity) < angularRestThreshold) {
				fruit.angularVelocity = 0;
			}
		}
	};
	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 EvolutionLine = Container.expand(function () {
	var self = Container.call(this);
	self.initialize = function () {
		self.y = GAME_CONSTANTS.EVOLUTION_LINE_Y;
		self.x = GAME_CONSTANTS.GAME_WIDTH / 2; // Remove the X_OFFSET to center properly
		var fruitTypes = [FruitTypes.CHERRY, FruitTypes.GRAPE, FruitTypes.APPLE, FruitTypes.ORANGE, FruitTypes.WATERMELON, FruitTypes.PINEAPPLE, FruitTypes.MELON, FruitTypes.PEACH, FruitTypes.COCONUT, FruitTypes.DURIAN];
		var totalWidth = 0;
		var fruitIcons = [];
		for (var i = 0; i < fruitTypes.length; i++) {
			var fruitType = fruitTypes[i];
			var fruitIcon = LK.getAsset(fruitType.id, {
				anchorX: 0.5,
				anchorY: 0.5
			});
			var scale = Math.min(GAME_CONSTANTS.EVOLUTION_ICON_MAX_SIZE / fruitIcon.width, GAME_CONSTANTS.EVOLUTION_ICON_MAX_SIZE / fruitIcon.height);
			fruitIcon.scaleX = scale;
			fruitIcon.scaleY = scale;
			totalWidth += fruitIcon.width * scale;
			if (i < fruitTypes.length - 1) {
				totalWidth += GAME_CONSTANTS.EVOLUTION_ICON_SPACING;
			}
			fruitIcons.push(fruitIcon);
		}
		var currentX = -totalWidth / 2;
		for (var i = 0; i < fruitIcons.length; i++) {
			var icon = fruitIcons[i];
			icon.x = currentX + icon.width * icon.scaleX / 2;
			icon.y = 0;
			self.addChild(icon);
			currentX += icon.width * icon.scaleX + GAME_CONSTANTS.EVOLUTION_ICON_SPACING;
		}
	};
	return self;
});
var FireElement = Container.expand(function (initX, initY, zIndex) {
	var self = Container.call(this);
	self.baseX = initX || 0;
	self.baseY = initY || 0;
	self.zIndex = zIndex || 0;
	self.movementRange = 30 + Math.random() * 20;
	self.movementSpeed = 0.3 + Math.random() * 0.4;
	self.direction = Math.random() > 0.5 ? 1 : -1;
	self.alphaMin = GAME_CONSTANTS.FIRE_ALPHA_MIN;
	self.alphaMax = GAME_CONSTANTS.FIRE_ALPHA_MAX;
	self.flickerSpeed = GAME_CONSTANTS.FIRE_FLICKER_SPEED_BASE + Math.random() * GAME_CONSTANTS.FIRE_FLICKER_SPEED_RANDOM;
	self.frameIndex = 0;
	self.frameTimer = null;
	self.frameDuration = GAME_CONSTANTS.FIRE_FRAME_DURATION;
	self.initialize = function () {
		self.fireAsset = self.attachAsset('fire', {
			anchorX: 0.5,
			anchorY: 1.0
		});
		self.fireAsset2 = self.attachAsset('fire_2', {
			anchorX: 0.5,
			anchorY: 1.0
		});
		self.fireAsset2.visible = false;
		self.x = self.baseX;
		self.y = self.baseY;
		self.startAlphaFlicker();
		self.startFrameAnimation();
	};
	self.update = function () {
		self.x += self.movementSpeed * self.direction;
		if (Math.abs(self.x - self.baseX) > self.movementRange) {
			self.direction *= -1;
		}
	};
	self.startFrameAnimation = function () {
		if (self.frameTimer) LK.clearInterval(self.frameTimer);
		self.frameTimer = LK.setInterval(function () {
			self.toggleFrame();
		}, self.frameDuration);
	};
	self.toggleFrame = function () {
		self.frameIndex = (self.frameIndex + 1) % 2;
		self.fireAsset.visible = self.frameIndex === 0;
		self.fireAsset2.visible = self.frameIndex === 1;
	};
	self.startAlphaFlicker = function () {
		if (self.flickerTween) self.flickerTween.stop();
		var startDelay = Math.random() * 500;
		LK.setTimeout(function () {
			self.flickerToMax();
		}, startDelay);
	};
	self.flickerToMax = function () {
		self.flickerTween = tween(self, {
			alpha: self.alphaMax
		}, {
			duration: self.flickerSpeed,
			easing: tween.easeInOut,
			onFinish: self.flickerToMin
		});
	};
	self.flickerToMin = function () {
		self.flickerTween = tween(self, {
			alpha: self.alphaMin
		}, {
			duration: self.flickerSpeed,
			easing: tween.easeInOut,
			onFinish: self.flickerToMax
		});
	};
	self.destroy = function () {
		if (self.flickerTween) self.flickerTween.stop();
		if (self.frameTimer) {
			LK.clearInterval(self.frameTimer);
			self.frameTimer = null;
		}
		Container.prototype.destroy.call(this);
	};
	self.initialize();
	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();
	var behaviorSystem = new FruitBehavior();
	self.vx = physics.vx;
	self.vy = physics.vy;
	self.rotation = physics.rotation;
	self.angularVelocity = physics.angularVelocity;
	self.angularFriction = GAME_CONSTANTS.ANGULAR_FRICTION;
	self.groundAngularFriction = GAME_CONSTANTS.GROUND_ANGULAR_FRICTION;
	var currentLevel = getFruitLevel(self);
	self.gravity = GAME_CONSTANTS.BASE_GRAVITY * (1 + (currentLevel - 1) * GAME_CONSTANTS.GRAVITY_LEVEL_MULTIPLIER);
	self.friction = GAME_CONSTANTS.FRICTION;
	self.rotationRestCounter = physics.rotationRestCounter;
	self.maxAngularVelocity = GAME_CONSTANTS.MAX_ANGULAR_VELOCITY;
	self.isStatic = physics.isStatic;
	self.elasticity = currentLevel < GAME_CONSTANTS.ELASTICITY_LOW_START_LEVEL ? GAME_CONSTANTS.ELASTICITY_HIGH : GAME_CONSTANTS.ELASTICITY_LOW_BASE - (currentLevel - (GAME_CONSTANTS.ELASTICITY_LOW_START_LEVEL - 1)) * GAME_CONSTANTS.ELASTICITY_DECREASE_FACTOR;
	self.elasticity = Math.max(0.1, self.elasticity); // Ensure minimum elasticity
	self.wallContactFrames = collision.wallContactFrames;
	self.merging = mergeHandler.merging;
	self.mergeGracePeriodActive = mergeHandler.mergeGracePeriodActive;
	self.fromChargedRelease = mergeHandler.fromChargedRelease;
	self.safetyPeriod = false;
	self.immuneToGameOver = false;
	self.isFullyStabilized = false;
	self.isSleeping = false;
	self._sleepCounter = 0;
	self.behavior = type && type.id ? behaviorSystem.getMergeHandler(type.id) : null;
	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;
		if (self.behavior && self.behavior.onSpawn) {
			self.behavior.onSpawn(self);
		}
	} else {
		console.log("Warning: Fruit type not available yet or missing required properties");
	}
	self.updatePhysics = function () {
		physics.apply(self);
	};
	self.checkBoundaries = function (walls, floor) {
		collision.checkBoundaryCollisions(self, walls, floor);
		if (self.safetyPeriod === false && self.vy <= 0.1) {
			self.safetyPeriod = undefined;
		}
	};
	self.merge = function (otherFruit) {
		mergeHandler.beginMerge(self, otherFruit);
	};
	return self;
});
var FruitBehavior = Container.expand(function () {
	var self = Container.call(this);
	self.behaviors = {
		CHERRY: {
			onMerge: function onMerge(f1, f2, x, y) {
				return self.standardMerge(f1, f2, x, y);
			}
		},
		GRAPE: {
			onMerge: function onMerge(f1, f2, x, y) {
				return self.standardMerge(f1, f2, x, y);
			}
		},
		APPLE: {
			onMerge: function onMerge(f1, f2, x, y) {
				return self.standardMerge(f1, f2, x, y);
			}
		},
		ORANGE: {
			onMerge: function onMerge(f1, f2, x, y) {
				return self.standardMerge(f1, f2, x, y);
			}
		},
		WATERMELON: {
			onMerge: function onMerge(f1, f2, x, y) {
				return self.standardMerge(f1, f2, x, y);
			}
		},
		PINEAPPLE: {
			onMerge: function onMerge(f1, f2, x, y) {
				return self.standardMerge(f1, f2, x, y);
			},
			onSpawn: function onSpawn() {}
		},
		MELON: {
			onMerge: function onMerge(f1, f2, x, y) {
				LK.getSound('Smartz').play();
				return self.standardMerge(f1, f2, x, y);
			}
		},
		PEACH: {
			onMerge: function onMerge(f1, f2, x, y) {
				LK.getSound('stonks').play();
				return self.standardMerge(f1, f2, x, y);
			}
		},
		COCONUT: {
			onMerge: function onMerge(f1, f2, x, y) {
				LK.getSound('ThisIsFine').play();
				return self.standardMerge(f1, f2, x, y);
			},
			onSpawn: function onSpawn() {
				LK.getSound('stonks').play();
			}
		},
		DURIAN: {
			onMerge: function onMerge(f1, f2, x, y) {
				LK.setScore(LK.getScore() + f1.type.points);
				updateScoreDisplay();
				removeFruitFromGame(f1);
				removeFruitFromGame(f2);
				releasePineappleOnMerge();
				return null;
			}
		}
	};
	self.getMergeHandler = function (fruitTypeId) {
		if (!fruitTypeId) return self.behaviors.CHERRY;
		var upperTypeId = fruitTypeId.toUpperCase();
		return self.behaviors[upperTypeId] || self.behaviors.CHERRY;
	};
	self.standardMerge = function (fruit1, fruit2, posX, posY) {
		var nextType = FruitTypes[fruit1.type.next.toUpperCase()];
		releasePineappleOnMerge();
		return self.createNextLevelFruit(fruit1, nextType, posX, posY);
	};
	self.createNextLevelFruit = function (sourceFruit, nextType, posX, posY) {
		var newFruit = new Fruit(nextType);
		newFruit.x = posX;
		newFruit.y = posY;
		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 newFruit;
	};
	self.playSoundEffect = function (soundId) {
		if (soundId) LK.getSound(soundId).play();
	};
	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.fruitBehavior = new FruitBehavior();
	self.beginMerge = function (fruit1, fruit2) {
		if (fruit1.merging || fruit2.merging) return; // Added check for fruit2
		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.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();
		self.trackMerge(fruit1, fruit2);
		var behaviorHandler = self.fruitBehavior.getMergeHandler(fruit1.type.id);
		if (behaviorHandler && behaviorHandler.onMerge) {
			// Added check
			behaviorHandler.onMerge(fruit1, fruit2, midX, midY);
		}
		// Ensure cleanup happens correctly, check if fruits still exist before removal
		if (fruit1 && fruit1.parent && fruit1.type.id.toUpperCase() !== 'DURIAN') removeFruitFromGame(fruit1);
		if (fruit2 && fruit2.parent) removeFruitFromGame(fruit2);
	};
	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;
		}
	};
	return self;
});
var PhysicsComponent = Container.expand(function () {
	var self = Container.call(this);
	self.vx = 0;
	self.vy = 0;
	self.gravity = GAME_CONSTANTS.BASE_GRAVITY;
	self.friction = GAME_CONSTANTS.FRICTION;
	self.isStatic = false;
	self.rotation = 0;
	self.angularVelocity = 0;
	self.maxAngularVelocity = GAME_CONSTANTS.MAX_ANGULAR_VELOCITY;
	self.rotationRestCounter = 0;
	self.lastVx = 0;
	self.lastVy = 0;
	self.isFullyStabilized = false; // Moved here
	self.stabilizeFruit = function (fruit, movementMagnitude, velocityChange) {
		var isMidAir = !fruit._boundaryContacts || !fruit._boundaryContacts.floor && !fruit._boundaryContacts.left && !fruit._boundaryContacts.right;
		if (isMidAir) {
			fruit.isFullyStabilized = false; // Ensure mid-air fruits are never marked stabilized
			return false;
		}
		var stabilizationLevel = 0;
		var fruitLevel = getFruitLevel(fruit);
		if (!fruit._stabilizeMetrics) {
			fruit._stabilizeMetrics = {
				consecutiveSmallMovements: 0,
				wallContactDuration: 0,
				surroundedDuration: 0,
				restingDuration: 0
			};
		}
		// Dynamic thresholds based only on fruitLevel - lower for higher-level fruits
		var movementThreshold = GAME_CONSTANTS.STABILIZATION_MOVEMENT_THRESHOLD_BASE - fruitLevel * GAME_CONSTANTS.STABILIZATION_MOVEMENT_THRESHOLD_LEVEL_FACTOR * 1.2;
		var angularThreshold = GAME_CONSTANTS.STABILIZATION_ANGULAR_THRESHOLD_BASE - fruitLevel * GAME_CONSTANTS.STABILIZATION_ANGULAR_THRESHOLD_LEVEL_FACTOR * 1.2;
		if (movementMagnitude < movementThreshold && Math.abs(fruit.angularVelocity) < angularThreshold) {
			var stabilizationRate = GAME_CONSTANTS.STABILIZATION_SCORE_RATE_BASE + fruitLevel * GAME_CONSTANTS.STABILIZATION_SCORE_LEVEL_FACTOR * 1.4;
			fruit._stabilizeMetrics.consecutiveSmallMovements += stabilizationRate;
			fruit._stabilizeMetrics.restingDuration += stabilizationRate;
		} else {
			fruit._stabilizeMetrics.consecutiveSmallMovements = 0;
			fruit._stabilizeMetrics.restingDuration = Math.max(0, fruit._stabilizeMetrics.restingDuration - 2);
		}
		if (fruit.wallContactFrames > 0) fruit._stabilizeMetrics.wallContactDuration += GAME_CONSTANTS.STABILIZATION_WALL_SCORE_RATE * 1.4 + fruitLevel * 0.15;else fruit._stabilizeMetrics.wallContactDuration = Math.max(0, fruit._stabilizeMetrics.wallContactDuration - 1);
		if (fruit.surroundedFrames > 0) fruit._stabilizeMetrics.surroundedDuration += GAME_CONSTANTS.STABILIZATION_SURROUNDED_SCORE_RATE * 1.4 + fruitLevel * 0.22;else fruit._stabilizeMetrics.surroundedDuration = Math.max(0, fruit._stabilizeMetrics.surroundedDuration - 1);
		var levelFactor = fruitLevel * GAME_CONSTANTS.STABILIZATION_SCORE_LEVEL_INFLUENCE;
		var totalStabilizationScore = fruit._stabilizeMetrics.consecutiveSmallMovements * (1.0 + levelFactor) + fruit._stabilizeMetrics.wallContactDuration * (0.8 + levelFactor) + fruit._stabilizeMetrics.surroundedDuration * (1.2 + levelFactor) + fruit._stabilizeMetrics.restingDuration * (0.5 + levelFactor);
		// Dynamic thresholds for stabilization based only on fruitLevel - lower for higher-level fruits
		var fullStabilizationThreshold = GAME_CONSTANTS.STABILIZATION_THRESHOLD_FULL_BASE - fruitLevel * GAME_CONSTANTS.STABILIZATION_THRESHOLD_FULL_LEVEL_FACTOR * 1.25;
		var partialStabilizationThreshold = GAME_CONSTANTS.STABILIZATION_THRESHOLD_PARTIAL_BASE - fruitLevel * GAME_CONSTANTS.STABILIZATION_THRESHOLD_PARTIAL_LEVEL_FACTOR * 1.25;
		if (totalStabilizationScore > fullStabilizationThreshold) stabilizationLevel = 2;else if (totalStabilizationScore > partialStabilizationThreshold) stabilizationLevel = 1;
		if (stabilizationLevel > 0) {
			// Make damping stronger for higher-level fruits
			var baseDamp = stabilizationLevel === 2 ? GAME_CONSTANTS.STABILIZATION_DAMP_FACTOR_FULL * 0.9 : GAME_CONSTANTS.STABILIZATION_DAMP_FACTOR_PARTIAL * 0.95;
			var dampFactor = Math.max(0.35, baseDamp - fruitLevel * GAME_CONSTANTS.STABILIZATION_DAMP_LEVEL_FACTOR * 1.1);
			fruit.vx *= dampFactor;
			fruit.vy *= dampFactor;
			fruit.angularVelocity *= dampFactor * 0.85;
			var stopThreshold = GAME_CONSTANTS.STABILIZATION_STOP_THRESHOLD_BASE - fruitLevel * GAME_CONSTANTS.STABILIZATION_STOP_THRESHOLD_LEVEL_FACTOR;
			// Unconditionally stop when fully stabilized (level 2) and below movement threshold
			if (stabilizationLevel === 2 && movementMagnitude < stopThreshold) {
				fruit.vx = 0;
				fruit.vy = 0;
				fruit.angularVelocity = 0;
				fruit.isFullyStabilized = true;
				// Track frames of being fully stabilized
				if (!fruit._sleepCounter) fruit._sleepCounter = 0;
				// Only increment sleep counter if both linear and angular velocities are near zero
				if (movementMagnitude < stopThreshold * 0.8 && Math.abs(fruit.angularVelocity) < angularThreshold * 0.7) {
					fruit._sleepCounter++;
					// After being fully stabilized for SLEEP_DELAY_FRAMES consecutive frames, put to sleep
					if (fruit._sleepCounter >= GAME_CONSTANTS.SLEEP_DELAY_FRAMES) {
						fruit.isSleeping = true;
						fruit.isFullyStabilized = true; // Ensure fully stabilized state when sleeping
					}
				} else {
					// Reset sleep counter if either velocity isn't near zero
					fruit._sleepCounter = 0;
					fruit.isSleeping = false; // Ensure fruit is awake if conditions aren't met
				}
				return true;
			}
		}
		fruit.isFullyStabilized = false;
		return false;
	};
	self.apply = function (fruit) {
		if (fruit.isStatic || fruit.merging) return;
		// Skip physics calculations for sleeping fruits, but keep collision detection
		if (fruit.isSleeping) {
			// Keep fruit in the exact same position - no physics updates
			fruit.vx = 0;
			fruit.vy = 0;
			fruit.angularVelocity = 0;
			return;
		}
		// Reset stabilization flag if significant external force applied (e.g., explosion)
		// Wake fruit up from potential stabilization if there's significant velocity change
		var velocityDeltaX = Math.abs(fruit.vx - fruit.lastVx);
		var velocityDeltaY = Math.abs(fruit.vy - fruit.lastVy);
		if ((fruit.isFullyStabilized || fruit._sleepCounter > 0) && (velocityDeltaX > 0.7 || velocityDeltaY > 0.7)) {
			fruit.isFullyStabilized = false;
			fruit._sleepCounter = 0; // Reset sleep counter on significant velocity change
		}
		// More aggressively ensure stability when at rest - use a tighter threshold
		if (fruit.isFullyStabilized && Math.abs(fruit.vx) < 0.3 && Math.abs(fruit.vy) < 0.3) {
			fruit.vx = 0;
			fruit.vy = 0;
			fruit.angularVelocity = 0; // Ensure zero angular velocity
			return;
		}
		fruit.lastVx = fruit.vx;
		fruit.lastVy = fruit.vy;
		var fruitLevel = getFruitLevel(fruit);
		var isMidAir = !fruit._boundaryContacts || !fruit._boundaryContacts.floor && !fruit._boundaryContacts.left && !fruit._boundaryContacts.right;
		// Use exponential scaling for gravity to make higher-level fruits dramatically heavier
		var gravityMultiplier = 1 + Math.pow(fruitLevel, 1.5) * 0.25; // Stronger influence
		fruit.vy += fruit.gravity * gravityMultiplier;
		var maxVelocity = GAME_CONSTANTS.MAX_VELOCITY_BASE - fruitLevel * GAME_CONSTANTS.MAX_VELOCITY_LEVEL_FACTOR;
		fruit.vx = Math.max(-maxVelocity, Math.min(maxVelocity, fruit.vx));
		fruit.vy = Math.max(-maxVelocity, Math.min(maxVelocity, fruit.vy));
		// Removed separate LEVEL_DOWNWARD_FORCE_FACTOR as it's now incorporated in the gravity calculation
		if (fruit.lastVx !== undefined && fruit.lastVy !== undefined) {
			var dvx = fruit.vx - fruit.lastVx;
			var dvy = fruit.vy - fruit.lastVy;
			// Make inertia scale dramatically with fruitLevel using power function
			var inertiaResistance = Math.min(0.95, Math.pow(fruitLevel, 1.8) * 0.01);
			fruit.vx = fruit.lastVx + dvx * (1 - inertiaResistance);
			fruit.vy = fruit.lastVy + dvy * (1 - inertiaResistance);
		}
		var movementMagnitude = Math.sqrt(fruit.vx * fruit.vx + fruit.vy * fruit.vy);
		var velocityChange = Math.abs(fruit.vx - fruit.lastVx) + Math.abs(fruit.vy - fruit.lastVy);
		// Apply strong damping when velocity is very low to help fruits stabilize faster
		if (movementMagnitude < 0.5) {
			fruit.vx *= 0.80;
			fruit.vy *= 0.80;
			fruit.angularVelocity *= 0.70;
		}
		var isFullyStabilizedByFunc = self.stabilizeFruit(fruit, movementMagnitude, velocityChange);
		fruit.isFullyStabilized = isFullyStabilizedByFunc; // Update fruit's state
		if (!fruit.isFullyStabilized) {
			// Only apply rolling rotation when in contact with a surface
			var isRolling = fruit._boundaryContacts && (fruit._boundaryContacts.floor || fruit._boundaryContacts.left || fruit._boundaryContacts.right);
			if (isRolling && (movementMagnitude > 0.5 || velocityChange > 0.3)) {
				var rotationFactor = 0.035 / (1 + fruitLevel * 0.08);
				var targetAngularVelocity = fruit.vx * rotationFactor;
				fruit.angularVelocity = fruit.angularVelocity * 0.4 + targetAngularVelocity * 0.6;
			} else {
				// Apply stronger damping when not rolling or in air
				fruit.angularVelocity *= isRolling ? 0.75 : 0.65; // More damping when not rolling
			}
			fruit.rotation += fruit.angularVelocity;
			// Make friction increase slightly with level (heavier objects have more surface interaction)
			var frictionModifier = 0.98 + fruitLevel * 0.005;
			// Apply more friction when nearly stopped to push toward stabilization
			var nearStopMultiplier = movementMagnitude < 1.0 ? 0.95 : 0.98; // More aggressive
			fruit.vx *= fruit.friction * frictionModifier * nearStopMultiplier;
			fruit.vy *= fruit.friction * frictionModifier * nearStopMultiplier * 0.98;
			var stopThreshold = GAME_CONSTANTS.STABILIZATION_STOP_THRESHOLD_BASE - fruitLevel * GAME_CONSTANTS.STABILIZATION_STOP_THRESHOLD_LEVEL_FACTOR;
			stopThreshold = Math.max(0.05, stopThreshold);
			if (Math.abs(fruit.vx) < stopThreshold) fruit.vx = 0;
			// Only stop vertical velocity if NOT in mid-air AND not fully stabilized by function
			if (Math.abs(fruit.vy) < stopThreshold && !isMidAir) fruit.vy = 0;
			self.handleRotationDamping(fruit);
		}
		fruit.x += fruit.vx;
		fruit.y += fruit.vy;
		if (isMidAir) {
			var currentMinFallSpeed = GAME_CONSTANTS.MIN_FALL_SPEED + fruitLevel * 0.02;
			if (fruit.vy < currentMinFallSpeed) {
				fruit.vy = currentMinFallSpeed;
			}
			fruit.isFullyStabilized = false; // Explicitly ensure mid-air is not stabilized
		}
	};
	self.handleRotationDamping = function (fruit) {
		var movementMagnitude = Math.sqrt(fruit.vx * fruit.vx + fruit.vy * fruit.vy);
		var fruitLevel = getFruitLevel(fruit);
		var isMidAir = !fruit._boundaryContacts || !fruit._boundaryContacts.floor && !fruit._boundaryContacts.left && !fruit._boundaryContacts.right;
		// Scale rotation factor inversely with fruitLevel - higher level = less rotation from rolling
		var rotationFactor = 0.04 / (1 + fruitLevel * 0.15); // Increased inverse scaling with level
		var targetAngularVelocity = fruit.vx * rotationFactor;
		var hasNeighbors = fruit.neighborContacts && fruit.neighborContacts.length > 0;
		var neighborCount = hasNeighbors ? fruit.neighborContacts.length : 0;
		// Only apply rotation forces if moving with sufficient velocity
		if (movementMagnitude > 0.3) {
			// Less influence when more neighbors present - scaled by neighbor count
			var blendRatio = Math.max(0.2, 0.6 - neighborCount * 0.08);
			fruit.angularVelocity = fruit.angularVelocity * (1 - blendRatio) + targetAngularVelocity * blendRatio;
			// Correct rotation direction if it's opposite of movement
			var targetDirection = fruit.vx > 0 ? 1 : -1;
			var currentDirection = fruit.angularVelocity > 0 ? 1 : -1;
			if (targetDirection !== currentDirection && Math.abs(fruit.vx) > 0.5) {
				fruit.angularVelocity *= 0.25; // Stronger correction when rotation direction opposite of movement
			}
		}
		// Base damping primarily on movement magnitude and neighbor count
		var movementFactor = Math.min(1, movementMagnitude * 2);
		var neighborFactor = Math.min(1, neighborCount * 0.2);
		var levelFactor = fruitLevel * 0.06; // Increased level factor influence
		// Calculate rotation damp factor based on these conditions
		var rotationDampFactor;
		// Apply much stronger damping when nearly stopped
		if (movementMagnitude < 0.05) {
			// Near-zero movement gets extreme damping
			rotationDampFactor = 0.12 - levelFactor * 0.6; // Increased extreme damping near zero
		} else if (isMidAir) {
			// Mid-air damping - moderately strong
			rotationDampFactor = 0.6 - levelFactor * 0.8;
		} else if (movementMagnitude < 0.3) {
			// Very slow movement - strong damping
			rotationDampFactor = 0.25 - levelFactor * 0.7 - neighborFactor * 0.15; // Stronger damping for slow movement
		} else if (neighborCount >= 3) {
			// Many neighbors - increased resistance
			rotationDampFactor = 0.35 - levelFactor * 0.6 - neighborFactor * 0.15;
		} else if (hasNeighbors) {
			// Some neighbors - moderate resistance
			rotationDampFactor = 0.45 - levelFactor * 0.5 - neighborFactor * 0.1;
		} else {
			// Default case - moving freely with no neighbors
			rotationDampFactor = 0.6 - levelFactor * 0.4;
		}
		// Check if fruit is rolling (in contact with surface with meaningful velocity)
		var isRolling = !isMidAir && movementMagnitude > 0.5 && fruit._boundaryContacts && (fruit._boundaryContacts.floor || fruit._boundaryContacts.left || fruit._boundaryContacts.right);
		if (isRolling) {
			// Fine-tune damping when rolling based on speed
			if (movementMagnitude < 0.5) {
				rotationDampFactor = 0.7 - levelFactor * 0.4;
			} else if (movementMagnitude < 1.0) {
				rotationDampFactor = 0.8 - levelFactor * 0.3;
			} else {
				// Allow more natural rotation when moving quickly
				rotationDampFactor = 0.85 - levelFactor * 0.2;
			}
		}
		// Apply the calculated damping factor
		fruit.angularVelocity *= rotationDampFactor;
		// Apply additional damping when velocity changes suddenly
		if (fruit.lastVx !== undefined && Math.abs(fruit.vx - fruit.lastVx) > 0.5) {
			fruit.angularVelocity -= (fruit.vx - fruit.lastVx) * 0.02;
		}
		// Dramatically increase damping when velocity is very low
		if (movementMagnitude < 0.2) {
			// Apply extreme angular damping when linear movement is minimal
			var linearDampingFactor = movementMagnitude < 0.08 ? 0.03 : 0.15; // More aggressive damping
			fruit.angularVelocity *= linearDampingFactor;
			// Directly zero out tiny angular velocities when there's almost no movement
			if (Math.abs(fruit.angularVelocity) < 0.002 || movementMagnitude < 0.02) {
				// Lower thresholds
				fruit.angularVelocity = 0;
			}
		}
		// Handling rolling physics when in contact with surfaces
		if (fruit._boundaryContacts) {
			// Apply rolling physics when in contact with floor or walls
			var isRolling = fruit._boundaryContacts.floor || fruit._boundaryContacts.left || fruit._boundaryContacts.right;
			if (isRolling && Math.abs(fruit.vx) > 0.2) {
				// Scale roll factor inversely with level - higher level fruits roll less
				var rollFactor = 0.025 / (1 + fruitLevel * 0.2); // Increased level scaling for rolling
				var idealRollingVelocity = fruit.vx * rollFactor;
				fruit.angularVelocity = fruit.angularVelocity * 0.7 + idealRollingVelocity * 0.3;
			} else if (isMidAir) {
				// Apply stronger damping when airborne with no contacts
				fruit.angularVelocity *= 0.85;
			}
		}
		// Increase wall damping
		if (fruit._boundaryContacts && (fruit._boundaryContacts.left || fruit._boundaryContacts.right) && Math.abs(fruit.angularVelocity) > 0.01) {
			fruit.angularVelocity *= 0.6; // Even stronger wall damping
		}
		// Lowered level-based thresholds for stopping rotation - significantly lowered
		var angularThreshold = GAME_CONSTANTS.ANGULAR_STOP_THRESHOLD - fruitLevel * 0.0005; // Lower threshold for higher level fruits
		var restFramesThreshold = Math.max(1, 2 - Math.floor(fruitLevel * 0.35)); // More aggressive frame requirement reduction
		if (Math.abs(fruit.angularVelocity) < angularThreshold) {
			fruit.rotationRestCounter++;
			if (fruit.rotationRestCounter > restFramesThreshold) {
				fruit.angularVelocity = 0;
			}
		} else {
			fruit.rotationRestCounter = 0;
		}
		// Scale max angular velocity based on level
		var maxAngularMultiplier = 1.2 - fruitLevel * 0.06; // Higher level fruits rotate even less
		fruit.angularVelocity = Math.min(Math.max(fruit.angularVelocity, -self.maxAngularVelocity * maxAngularMultiplier), self.maxAngularVelocity * maxAngularMultiplier);
	};
	return self;
});
var SpatialGrid = Container.expand(function (cellSize) {
	var self = Container.call(this);
	self.cellSize = cellSize || 200;
	self.grid = {};
	self.lastRebuildTime = Date.now();
	self.rebuildInterval = GAME_CONSTANTS.SPATIAL_GRID_REBUILD_INTERVAL_MS;
	self.insertObject = function (obj) {
		if (!obj || !obj.x || !obj.y || !obj.width || !obj.height || obj.merging || obj.isStatic) return;
		obj._currentCells = obj._currentCells || [];
		var cells = self.getCellsForObject(obj);
		obj._currentCells = cells.slice();
		for (var i = 0; i < cells.length; i++) {
			var cellKey = cells[i];
			if (!self.grid[cellKey]) self.grid[cellKey] = [];
			if (self.grid[cellKey].indexOf(obj) === -1) self.grid[cellKey].push(obj);
		}
	};
	self.removeObject = function (obj) {
		if (!obj || !obj.x || !obj.y || !obj.width || !obj.height) return;
		var cells = obj._currentCells || 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];
			}
		}
		obj._currentCells = [];
	};
	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) {
		if (!obj || !obj.x || !obj.y || !obj.width || !obj.height) return;
		var newCells = self.getCellsForObject(obj);
		var oldCells = obj._currentCells || [];
		var cellsChanged = false;
		if (oldCells.length !== newCells.length) cellsChanged = true;else {
			for (var i = 0; i < newCells.length; i++) {
				if (oldCells.indexOf(newCells[i]) === -1) {
					cellsChanged = true;
					break;
				}
			}
		}
		if (cellsChanged) {
			self.removeObject(obj);
			self.insertObject(obj);
		}
	};
	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];
					if (otherObj && otherObj !== obj && !addedObjects[otherObj.id]) {
						candidates.push(otherObj);
						addedObjects[otherObj.id] = true;
					}
				}
			}
		}
		return candidates;
	};
	self.clear = function () {
		self.grid = {};
		self.lastRebuildTime = Date.now();
	};
	self.rebuildGrid = function (allObjects) {
		self.grid = {};
		self.lastRebuildTime = Date.now();
		if (Array.isArray(allObjects)) {
			for (var i = 0; i < allObjects.length; i++) {
				if (allObjects[i] && !allObjects[i].merging && !allObjects[i].isStatic) {
					self.insertObject(allObjects[i]);
				}
			}
		}
	};
	return self;
});
var TrajectoryLine = Container.expand(function () {
	var self = Container.call(this);
	self.dotPool = new DotPool(100);
	self.activeDots = [];
	self.dots = [];
	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 activeFruitLevel = getFruitLevel(activeFruitObj);
		var targetFruitLevel = getFruitLevel(targetFruitObj);
		var activeFruitRadius = activeFruitObj.width / 2;
		var targetFruitRadius = targetFruitObj.width / 2;
		var hitboxReduction = (Math.max(0, activeFruitLevel - 4) + Math.max(0, targetFruitLevel - 4)) * 2;
		var combinedRadii = activeFruitRadius + targetFruitRadius - hitboxReduction;
		var minDistanceSquared = combinedRadii * combinedRadii;
		return distanceSquared < minDistanceSquared;
	};
	return self;
});
/**** 
* Initialize Game
****/ 
var game = new LK.Game({
	backgroundColor: 0xffe122
});
/**** 
* Game Code
****/ 
// --- Constants ---
var _GAME_CONSTANTS;
function _typeof(o) {
	"@babel/helpers - typeof";
	return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) {
		return typeof o;
	} : function (o) {
		return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o;
	}, _typeof(o);
}
function _defineProperty(e, r, t) {
	return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, {
		value: t,
		enumerable: !0,
		configurable: !0,
		writable: !0
	}) : e[r] = t, e;
}
function _toPropertyKey(t) {
	var i = _toPrimitive(t, "string");
	return "symbol" == _typeof(i) ? i : i + "";
}
function _toPrimitive(t, r) {
	if ("object" != _typeof(t) || !t) return t;
	var e = t[Symbol.toPrimitive];
	if (void 0 !== e) {
		var i = e.call(t, r || "default");
		if ("object" != _typeof(i)) return i;
		throw new TypeError("@@toPrimitive must return a primitive value.");
	}
	return ("string" === r ? String : Number)(t);
}
var GAME_CONSTANTS = (_GAME_CONSTANTS = {
	GAME_WIDTH: 2048,
	GAME_HEIGHT: 2732,
	DROP_POINT_Y: 200,
	DROP_START_Y_OFFSET: 200,
	CLICK_DELAY_MS: 300,
	FRUIT_IMMUNITY_MS: 1000,
	MERGE_GRACE_MS: 2000,
	GAME_OVER_LINE_Y: 550,
	GAME_OVER_COUNTDOWN_MS: 3000,
	// Physics
	BASE_GRAVITY: 5.0,
	GRAVITY_LEVEL_MULTIPLIER: 0.4,
	// Adjusted gravity scaling
	FRICTION: 0.90,
	// Increased base friction
	ANGULAR_FRICTION: 0.80,
	GROUND_ANGULAR_FRICTION: 0.60,
	// Further increased ground friction
	MAX_ANGULAR_VELOCITY: 0.15,
	ELASTICITY_HIGH: 0.85,
	// Sleep state constants
	SLEEP_DELAY_FRAMES: 15,
	WAKE_UP_IMPULSE_THRESHOLD: 1.5,
	ANGULAR_STOP_THRESHOLD: 0.003
}, _defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_GAME_CONSTANTS, "SLEEP_DELAY_FRAMES", 15), "WAKE_UP_IMPULSE_THRESHOLD", 1.5), "ELASTICITY_LOW_START_LEVEL", 4), "ELASTICITY_LOW_BASE", 0.8), "ELASTICITY_DECREASE_FACTOR", 0.2 / 9), "MIN_FALL_SPEED", 0.1), "MAX_VELOCITY_BASE", 65), "MAX_VELOCITY_LEVEL_FACTOR", 5), "LEVEL_INERTIA_FACTOR", 0.05), "LEVEL_DOWNWARD_FORCE_FACTOR", 0.1), _defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_GAME_CONSTANTS, "BOUNCE_SOUND_VELOCITY_THRESHOLD", 0.5), "COLLISION_SEPARATION_FACTOR", 1.01), "INTER_FRUIT_FRICTION", 0.1), "ROTATION_TRANSFER_FACTOR", 0.01), "STABILIZATION_MOVEMENT_THRESHOLD_BASE", 0.7), "STABILIZATION_MOVEMENT_THRESHOLD_LEVEL_FACTOR", 0.08), "STABILIZATION_ANGULAR_THRESHOLD_BASE", 0.07), "STABILIZATION_ANGULAR_THRESHOLD_LEVEL_FACTOR", 0.006), "STABILIZATION_SCORE_RATE_BASE", 1.5), "STABILIZATION_SCORE_LEVEL_FACTOR", 0.2), _defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_GAME_CONSTANTS, "STABILIZATION_WALL_SCORE_RATE", 1.2), "STABILIZATION_SURROUNDED_SCORE_RATE", 1.5), "STABILIZATION_SCORE_LEVEL_INFLUENCE", 0.1), "STABILIZATION_THRESHOLD_FULL_BASE", 15), "STABILIZATION_THRESHOLD_FULL_LEVEL_FACTOR", 0.8), "STABILIZATION_THRESHOLD_PARTIAL_BASE", 8), "STABILIZATION_THRESHOLD_PARTIAL_LEVEL_FACTOR", 0.4), "STABILIZATION_DAMP_FACTOR_FULL", 0.6), "STABILIZATION_DAMP_FACTOR_PARTIAL", 0.85), "STABILIZATION_DAMP_LEVEL_FACTOR", 0.03), _defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_GAME_CONSTANTS, "STABILIZATION_STOP_THRESHOLD_BASE", 0.15), "STABILIZATION_STOP_THRESHOLD_LEVEL_FACTOR", 0.01), "SPATIAL_GRID_REBUILD_INTERVAL_MS", 60000), "SPATIAL_GRID_CELL_SIZE_FACTOR", 1.1), "CHARGE_NEEDED_FOR_RELEASE", 15), "PORTAL_UI_Y", 120), "PORTAL_UI_X_OFFSET", 870), "PORTAL_TWEEN_DURATION", 300), "PORTAL_PULSE_DURATION", 500), "PINEAPPLE_MERGES_NEEDED", 15), _defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_GAME_CONSTANTS, "PINEAPPLE_START_Y", 200), "PINEAPPLE_END_POS_FACTOR", 0.16), "PINEAPPLE_TWEEN_DURATION", 300), "PINEAPPLE_IMMUNITY_MS", 3000), "FIRE_BASE_COUNT", 3), "FIRE_FRUIT_TYPE_THRESHOLD", 1), "FIRE_MAX_COUNT", 15), "FIRE_START_Y_OFFSET", 50), "FIRE_STACK_Y_OFFSET", 100), "FIRE_X_SPREAD", 500), _defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_GAME_CONSTANTS, "FIRE_FLICKER_SPEED_BASE", 500), "FIRE_FLICKER_SPEED_RANDOM", 300), "FIRE_ALPHA_MIN", 0.1), "FIRE_ALPHA_MAX", 0.5), "FIRE_FRAME_DURATION", 200), "COCONUT_SPAWN_SCORE_INTERVAL", 1000), "EVOLUTION_LINE_Y", 120), "EVOLUTION_LINE_X_OFFSET", 350), "EVOLUTION_ICON_MAX_SIZE", 150), "EVOLUTION_ICON_SPACING", 20), _defineProperty(_defineProperty(_GAME_CONSTANTS, "SCORE_TEXT_Y", 400), "SCORE_TEXT_SIZE", 120));
var gameOverLine;
var pineapple;
var pineappleActive = false;
var pineapplePushCount = 0;
var readyToReleaseCharged = false;
var trajectoryLine;
var isClickable = true;
var evolutionLine;
var fireContainer;
var activeFireElements = [];
var fruitLevels = {
	'CHERRY': 1,
	'GRAPE': 2,
	'APPLE': 3,
	'ORANGE': 4,
	'WATERMELON': 5,
	'PINEAPPLE': 6,
	'MELON': 7,
	'PEACH': 8,
	'COCONUT': 9,
	'DURIAN': 10
};
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 = GAME_CONSTANTS.GAME_WIDTH;
var gameHeight = GAME_CONSTANTS.GAME_HEIGHT;
var fruits = [];
var nextFruitType = null;
var activeFruit = null;
var wallLeft, wallRight, gameFloor;
var dropPointY = GAME_CONSTANTS.DROP_POINT_Y;
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;
var lastScoreCheckForCoconut = 0;
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 >= GAME_CONSTANTS.PINEAPPLE_MERGES_NEEDED && !pineappleActive && pineapple) {
		pineappleActive = true;
		pineapple.isStatic = false;
		pineapple.immuneToGameOver = true;
		applyDropPhysics(pineapple, 2.5);
		fruits.push(pineapple);
		if (spatialGrid) spatialGrid.insertObject(pineapple);
		LK.setTimeout(function () {
			if (pineapple && fruits.includes(pineapple)) {
				pineapple.immuneToGameOver = false;
			}
		}, GAME_CONSTANTS.PINEAPPLE_IMMUNITY_MS);
		setupPineapple();
		mergeCounter = 0;
	}
}
function setupBoundaries() {
	wallLeft = game.addChild(LK.getAsset('wall', {
		anchorX: 0.5,
		anchorY: 0.5
	}));
	wallLeft.x = 0;
	wallLeft.y = gameHeight / 2;
	wallLeft.alpha = 0;
	wallRight = game.addChild(LK.getAsset('wall', {
		anchorX: 0.5,
		anchorY: 0.5
	}));
	wallRight.x = gameWidth;
	wallRight.y = gameHeight / 2;
	wallRight.alpha = 0;
	gameFloor = game.addChild(LK.getAsset('floor', {
		anchorX: 0.5,
		anchorY: 0.5
	}));
	gameFloor.x = gameWidth / 2;
	gameFloor.y = gameHeight;
	gameFloor.alpha = 0;
	gameOverLine = game.addChild(new Line());
	gameOverLine.x = gameWidth / 2;
	gameOverLine.y = GAME_CONSTANTS.GAME_OVER_LINE_Y;
	gameOverLine.scaleX = gameWidth / 100; // Scale to full screen width (Line is 100px wide by default)
	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 + GAME_CONSTANTS.DROP_START_Y_OFFSET;
	activeFruit.isStatic = true;
	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;
	}, GAME_CONSTANTS.CLICK_DELAY_MS);
	activeFruit.isStatic = false;
	applyDropPhysics(activeFruit, 3.5);
	fruits.push(activeFruit);
	spatialGrid.insertObject(activeFruit);
	lastDroppedFruit = activeFruit;
	lastDroppedHasMerged = false;
	chargeCounter++;
	updateChargedBallDisplay();
	if (chargeCounter >= GAME_CONSTANTS.CHARGE_NEEDED_FOR_RELEASE && !readyToReleaseCharged) {
		releaseChargedBalls();
	}
	activeFruit.mergeGracePeriodActive = true;
	LK.setTimeout(function () {
		if (activeFruit && fruits.includes(activeFruit)) {
			activeFruit.mergeGracePeriodActive = false;
		}
	}, GAME_CONSTANTS.MERGE_GRACE_MS);
	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 >= GAME_CONSTANTS.CHARGE_NEEDED_FOR_RELEASE) {
		LK.getSound('pickleRick').play();
		var orange = new Fruit(FruitTypes.ORANGE);
		orange.hasBounced = false;
		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);
		chargeCounter = 0;
		resetChargedBalls();
		readyToReleaseCharged = false;
	}
	activeFruit = null;
	createNextFruit();
}
function applyDropPhysics(fruit, forceMultiplier) {
	var fruitLevel = getFruitLevel(fruit);
	forceMultiplier *= 1.4;
	var levelAdjustedForce = forceMultiplier * (1 - fruitLevel * 0.05);
	var angle = (Math.random() * 20 - 10) * (Math.PI / 180);
	fruit.vx = Math.sin(angle) * levelAdjustedForce;
	fruit.vy = Math.abs(Math.cos(angle) * levelAdjustedForce) * 1.5;
	fruit.angularVelocity = 0; // Explicitly reset angular velocity on drop
	fruit.gravity = GAME_CONSTANTS.BASE_GRAVITY * (1 + fruitLevel * GAME_CONSTANTS.GRAVITY_LEVEL_MULTIPLIER);
	fruit.safetyPeriod = false;
	fruit.immuneToGameOver = true;
	fruit.isFullyStabilized = false; // Reset stabilization state
	fruit.isSleeping = false; // Wake up the fruit when dropping
	fruit._sleepCounter = 0; // Reset sleep counter - ensure even if undefined
	LK.setTimeout(function () {
		if (fruit && fruits.includes(fruit)) {
			fruit.immuneToGameOver = false;
		}
	}, GAME_CONSTANTS.FRUIT_IMMUNITY_MS);
}
function updateScoreDisplay() {
	scoreText.setText(LK.getScore());
}
function setupUI() {
	scoreText = new Text2("0", {
		size: GAME_CONSTANTS.SCORE_TEXT_SIZE,
		fill: 0x000000
	});
	scoreText.anchor.set(0.5, 0);
	LK.gui.top.addChild(scoreText);
	scoreText.y = GAME_CONSTANTS.SCORE_TEXT_Y;
	setupChargedBallDisplay();
}
function setupChargedBallDisplay() {
	chargedBallUI = new ChargedBallUI();
	// Initialization moved to class constructor
	game.addChild(chargedBallUI);
}
function updateChargedBallDisplay() {
	chargedBallUI && chargedBallUI.updateChargeDisplay(chargeCounter);
}
function releaseChargedBalls() {
	readyToReleaseCharged = true;
	chargedBallUI && chargedBallUI.setReadyState(true);
}
function resetChargedBalls() {
	chargedBallUI && chargedBallUI.reset();
}
function checkFruitCollisions() {
	for (var k = 0; k < fruits.length; k++) {
		if (fruits[k]) {
			fruits[k].neighboringFruits = 0;
			fruits[k].neighborContacts = fruits[k].neighborContacts || [];
			fruits[k].neighborContacts = [];
			// Reset surroundedFrames here as it's primarily determined by collisions
			fruits[k].surroundedFrames = 0;
		}
	}
	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 level1 = getFruitLevel(fruit1);
			var level2 = getFruitLevel(fruit2);
			var hitboxReduction = (Math.max(0, level1 - 4) + Math.max(0, level2 - 4)) * 2; // Use simplified reduction
			var combinedHalfWidths = fruit1HalfWidth + fruit2HalfWidth - hitboxReduction / 2;
			var combinedHalfHeights = fruit1HalfHeight + fruit2HalfHeight - hitboxReduction / 2;
			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;
					}
					// Calculate precise overlap along the collision normal
					var overlap = combinedHalfWidths - absDistanceX;
					if (absDistanceY < combinedHalfHeights && overlap > 0) {
						// Use most accurate overlap calculation
						var verticalOverlap = combinedHalfHeights - absDistanceY;
						if (verticalOverlap < overlap) {
							overlap = verticalOverlap;
						}
						// Use exact normal vector from centers
						var normalizeX = dx / distance;
						var normalizeY = dy / distance;
						// Calculate proportional separation based on fruit mass
						var level1 = getFruitLevel(fruit1);
						var level2 = getFruitLevel(fruit2);
						// Use a strongly exponential formula for mass based on level - using power of 3.0
						var mass1 = Math.pow(level1, 3.0);
						var mass2 = Math.pow(level2, 3.0);
						var totalMass = mass1 + mass2;
						var moveRatio1 = totalMass > 0 ? mass2 / totalMass : 0.5;
						var moveRatio2 = totalMass > 0 ? mass1 / totalMass : 0.5;
						// Apply separation proportional to mass
						var moveX = overlap * normalizeX;
						var moveY = overlap * normalizeY;
						// Apply smaller separation factor for more stable stacking
						var separationFactor = GAME_CONSTANTS.COLLISION_SEPARATION_FACTOR;
						fruit1.x -= moveX * moveRatio1 * separationFactor;
						fruit1.y -= moveY * moveRatio1 * separationFactor;
						fruit2.x += moveX * moveRatio2 * separationFactor;
						fruit2.y += moveY * moveRatio2 * 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;
						// Use strongly exponential mass formula to exaggerate weight differences
						var mass1 = Math.pow(level1, 3.0); // Removed size dependency, using pure level exponent
						var mass2 = Math.pow(level2, 3.0);
						var totalMass = mass1 + mass2;
						// Let impulse ratios be calculated directly from the exaggerated masses
						var impulseRatio1 = totalMass > 0 ? mass2 / totalMass : 0.5;
						var impulseRatio2 = totalMass > 0 ? mass1 / totalMass : 0.5;
						var velocityMagnitude = Math.sqrt(rvX * rvX + rvY * rvY);
						var velocityDampening = velocityMagnitude > 5 ? 0.5 + 2.5 / velocityMagnitude : 1.0;
						var impulse1 = impulse * impulseRatio1 * velocityDampening;
						var impulse2 = impulse * impulseRatio2 * velocityDampening;
						// Apply impulses with sleeping state awareness but no special level-based adjustments
						var impactThreshold = 1.2; // Threshold to determine significant impacts
						// For first fruit: apply impulse and wake up if needed
						// Wake up sleeping fruits on significant impacts based on WAKE_UP_IMPULSE_THRESHOLD
						if (fruit1.isSleeping) {
							if (Math.abs(impulse1) > GAME_CONSTANTS.WAKE_UP_IMPULSE_THRESHOLD) {
								// Wake up if impact is very significant
								fruit1.isSleeping = false;
								fruit1.isFullyStabilized = false;
								fruit1._sleepCounter = 0;
								fruit1.vx -= impulse1 * normalizeX;
								fruit1.vy -= impulse1 * normalizeY;
							} else {
								// Keep sleeping but ensure proper state tracking
								fruit1.vx = 0;
								fruit1.vy = 0;
								fruit1.angularVelocity = 0;
							}
						} else if (fruit1.isFullyStabilized) {
							// Apply minimal impulse to stabilized fruits - just enough to handle edge cases
							if (Math.abs(impulse1) > impactThreshold) {
								// If impact is large, unstabilize and apply full impulse
								fruit1.isFullyStabilized = false;
								fruit1._sleepCounter = 0; // Reset sleep counter on destabilization
								fruit1.vx -= impulse1 * normalizeX;
								fruit1.vy -= impulse1 * normalizeY;
							} else {
								// For small impacts, apply minimal response that won't disturb stability
								fruit1.vx -= impulse1 * normalizeX * 0.1;
								fruit1.vy -= impulse1 * normalizeY * 0.1;
							}
						} else {
							fruit1.vx -= impulse1 * normalizeX;
							fruit1.vy -= impulse1 * normalizeY;
						}
						// For second fruit: apply impulse with the same sleep/stabilization logic
						if (fruit2.isSleeping) {
							if (Math.abs(impulse2) > GAME_CONSTANTS.WAKE_UP_IMPULSE_THRESHOLD) {
								// Wake up if impact is very significant
								fruit2.isSleeping = false;
								fruit2.isFullyStabilized = false;
								fruit2._sleepCounter = 0;
								fruit2.vx += impulse2 * normalizeX;
								fruit2.vy += impulse2 * normalizeY;
							} else {
								// Keep sleeping but ensure proper state tracking
								fruit2.vx = 0;
								fruit2.vy = 0;
								fruit2.angularVelocity = 0;
							}
						} else if (fruit2.isFullyStabilized) {
							// Apply minimal impulse to stabilized fruits - just enough to handle edge cases
							if (Math.abs(impulse2) > impactThreshold) {
								// If impact is large, unstabilize and apply full impulse
								fruit2.isFullyStabilized = false;
								fruit2._sleepCounter = 0; // Reset sleep counter on destabilization
								fruit2.vx += impulse2 * normalizeX;
								fruit2.vy += impulse2 * normalizeY;
							} else {
								// For small impacts, apply minimal response that won't disturb stability  
								fruit2.vx += impulse2 * normalizeX * 0.1;
								fruit2.vy += impulse2 * normalizeY * 0.1;
							}
						} else {
							fruit2.vx += impulse2 * normalizeX;
							fruit2.vy += impulse2 * normalizeY;
						}
						// Apply additional post-collision damping to dissipate energy more rapidly
						fruit1.vx *= 0.95; // Increased damping factor to reduce velocities after collision
						fruit1.vy *= 0.95;
						fruit2.vx *= 0.95;
						fruit2.vy *= 0.95;
						// Apply stronger damping for low-velocity collisions to promote settling
						if (velocityMagnitude < 3.0) {
							fruit1.vx *= 0.92;
							fruit1.vy *= 0.92;
							fruit2.vx *= 0.92;
							fruit2.vy *= 0.92;
						}
						var tangentX = -normalizeY;
						var tangentY = normalizeX;
						var tangentVelocity = rvX * tangentX + rvY * tangentY;
						var frictionImpulse = -tangentVelocity * GAME_CONSTANTS.INTER_FRUIT_FRICTION;
						// Apply tangential friction with same stabilization logic
						if (!fruit1.isFullyStabilized) {
							fruit1.vx -= frictionImpulse * tangentX * impulseRatio1;
							fruit1.vy -= frictionImpulse * tangentY * impulseRatio1;
						}
						if (!fruit2.isFullyStabilized) {
							fruit2.vx += frictionImpulse * tangentX * impulseRatio2;
							fruit2.vy += frictionImpulse * tangentY * impulseRatio2;
						}
						var tangentialComponent = rvX * tangentX + rvY * tangentY;
						var inertia1 = mass1 * Math.pow(fruit1.width / 2, 2);
						var inertia2 = mass2 * Math.pow(fruit2.width / 2, 2);
						// Only apply angular impulse if the tangential component or contact velocity is significant
						var tangentialMagnitude = Math.abs(tangentialComponent);
						var contactMagnitude = Math.abs(contactVelocity);
						var angularThreshold = 1.2; // Increased threshold for generating rotational force
						// Calculate angular impulse with adjustment for impact magnitude
						var angularImpulse = 0;
						if (tangentialMagnitude > angularThreshold || contactMagnitude > angularThreshold) {
							// Apply a curve that increases rotation for more significant impacts
							var impactFactor = Math.min(1.0, (Math.max(tangentialMagnitude, contactMagnitude) - angularThreshold) / 2.0);
							angularImpulse = tangentialComponent * GAME_CONSTANTS.ROTATION_TRANSFER_FACTOR * impactFactor;
						}
						// Apply angular velocity with same stabilization logic
						if (!fruit1.isFullyStabilized && angularImpulse !== 0) {
							fruit1.angularVelocity += inertia2 > 0 ? angularImpulse * (inertia1 / (inertia1 + inertia2)) : angularImpulse * 0.5;
							fruit1.angularVelocity *= 0.95;
						}
						if (!fruit2.isFullyStabilized && angularImpulse !== 0) {
							fruit2.angularVelocity -= inertia1 > 0 ? angularImpulse * (inertia2 / (inertia1 + inertia2)) : angularImpulse * 0.5;
							fruit2.angularVelocity *= 0.95;
						}
						// Only unstabilize on significantly large angular impacts
						if (Math.abs(angularImpulse) > 0.015) {
							// Unstabilize on significant angular impact
							if (fruit1.isFullyStabilized) fruit1.isFullyStabilized = false;
							if (fruit2.isFullyStabilized) fruit2.isFullyStabilized = false;
						}
						fruit1.angularVelocity = Math.min(Math.max(fruit1.angularVelocity, -fruit1.maxAngularVelocity), fruit1.maxAngularVelocity);
						fruit2.angularVelocity = Math.min(Math.max(fruit2.angularVelocity, -fruit2.maxAngularVelocity), fruit2.maxAngularVelocity);
						fruit1.neighboringFruits = (fruit1.neighboringFruits || 0) + 1;
						fruit2.neighboringFruits = (fruit2.neighboringFruits || 0) + 1;
						fruit1.surroundedFrames++; // Increment surrounded counter on collision
						fruit2.surroundedFrames++;
						fruit1.neighborContacts = fruit1.neighborContacts || [];
						fruit2.neighborContacts = fruit2.neighborContacts || [];
						if (fruit1.neighborContacts.indexOf(fruit2.id) === -1) fruit1.neighborContacts.push(fruit2.id);
						if (fruit2.neighborContacts.indexOf(fruit1.id) === -1) fruit2.neighborContacts.push(fruit1.id);
						// Removed bounce flag dependent damping
					}
				}
			}
		}
	}
}
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 fruitLevel = getFruitLevel(fruit);
		var sizeReduction = Math.max(0, fruitLevel - 4) * 2;
		fruitHalfHeight = Math.max(10, fruitHalfHeight - sizeReduction / 2);
		fruitHalfWidth = Math.max(10, fruitHalfWidth - sizeReduction / 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;
				// Game over if fruit is overlapping line AND not moving significantly OR fully stabilized
				var stableOrSlowing = Math.abs(fruit.vy) < 1.0 || fruit.isFullyStabilized || fruit._boundaryContacts && (fruit._boundaryContacts.left || fruit._boundaryContacts.right || fruit._boundaryContacts.floor);
				if (stableOrSlowing) {
					if (!fruit.gameOverTimer) {
						fruit.gameOverTimer = Date.now();
						tween(fruit, {
							alpha: 0.5
						}, {
							duration: 500,
							easing: tween.easeInOut,
							onFinish: function onFinish() {
								tween(fruit, {
									alpha: 1
								}, {
									duration: 500,
									easing: tween.easeInOut,
									repeat: 2
								});
							}
						});
						continue;
					}
					var currentTime = Date.now();
					if (currentTime - fruit.gameOverTimer >= GAME_CONSTANTS.GAME_OVER_COUNTDOWN_MS) {
						gameOver = true;
						LK.showGameOver();
						return;
					}
				} else {
					// Reset timer if fruit starts moving significantly while overlapping
					fruit.gameOverTimer = null;
					fruit.alpha = 1;
				}
			} else {
				// Reset timer if no longer overlapping horizontally
				if (fruit.gameOverTimer) {
					fruit.gameOverTimer = null;
					fruit.alpha = 1;
				}
			}
		} else {
			fruit.safetyPeriod = undefined;
			if (fruit.gameOverTimer) {
				fruit.gameOverTimer = null;
				fruit.alpha = 1;
			}
		}
	}
}
function setupPineapple() {
	pineapple = new Fruit(FruitTypes.PINEAPPLE);
	pineapple.x = -pineapple.width / 2;
	pineapple.y = GAME_CONSTANTS.PINEAPPLE_START_Y;
	pineapple.isStatic = true;
	pineappleActive = false;
	pineapplePushCount = 0;
	game.addChild(pineapple);
}
function pushPineapple() {
	if (!pineappleActive && pineapple) {
		var step = mergeCounter;
		var totalSteps = GAME_CONSTANTS.PINEAPPLE_MERGES_NEEDED;
		var percentage = Math.min(step / totalSteps, 1.0);
		var startPos = -pineapple.width / 2;
		var endPos = gameWidth * GAME_CONSTANTS.PINEAPPLE_END_POS_FACTOR;
		var newX = startPos + percentage * (endPos - startPos);
		tween(pineapple, {
			x: newX
		}, {
			duration: GAME_CONSTANTS.PINEAPPLE_TWEEN_DURATION,
			easing: tween.bounceOut
		});
	}
}
function initGame() {
	LK.setScore(0);
	gameOver = false;
	if (fruits && fruits.length > 0) {
		// Ensure cleanup only happens if needed
		for (var i = fruits.length - 1; i >= 0; i--) {
			if (fruits[i]) removeFruitFromGame(fruits[i]);
		}
	}
	fruits = [];
	chargeCounter = 0;
	if (chargedBallUI) chargedBallUI.destroy();
	chargedBallUI = null;
	readyToReleaseCharged = false;
	lastScoreCheckForCoconut = 0;
	lastDroppedFruit = null;
	lastDroppedHasMerged = false;
	mergeCounter = 0;
	isClickable = true;
	if (fireContainer) {
		for (var i = activeFireElements.length - 1; i >= 0; i--) {
			if (activeFireElements[i]) activeFireElements[i].destroy();
		}
		fireContainer.destroy();
	}
	fireContainer = new Container();
	game.addChildAt(fireContainer, 0);
	activeFireElements = [];
	if (spatialGrid) {
		spatialGrid.clear();
	} else {
		var avgFruitSize = 0;
		var fruitCount = 0;
		for (var fruitType in FruitTypes) {
			if (FruitTypes.hasOwnProperty(fruitType)) {
				avgFruitSize += FruitTypes[fruitType].size;
				fruitCount++;
			}
		}
		avgFruitSize = fruitCount > 0 ? avgFruitSize / fruitCount : 300;
		var optimalCellSize = Math.ceil(avgFruitSize * GAME_CONSTANTS.SPATIAL_GRID_CELL_SIZE_FACTOR);
		spatialGrid = new SpatialGrid(optimalCellSize);
	}
	// Destroy existing boundaries before setup
	if (wallLeft) wallLeft.destroy();
	if (wallRight) wallRight.destroy();
	if (gameFloor) gameFloor.destroy();
	if (gameOverLine) gameOverLine.destroy();
	if (pineapple) pineapple.destroy();
	if (trajectoryLine) trajectoryLine.destroy();
	if (evolutionLine) evolutionLine.destroy();
	LK.playMusic('bgmusic'); // Start music after potential cleanup
	setupBoundaries();
	setupUI();
	setupPineapple();
	updateFireBackground();
	trajectoryLine = game.addChild(new TrajectoryLine());
	trajectoryLine.createDots();
	evolutionLine = game.addChild(new EvolutionLine());
	evolutionLine.initialize();
	updateScoreDisplay();
	activeFruit = null;
	createNextFruit();
	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;
	game.addChild(coconut);
	fruits.push(coconut);
	coconut.safetyPeriod = false;
	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;
			coconut.isStatic = false;
			coconut.vy = -2;
			coconut.vx = (Math.random() * 2 - 1) * 1.5;
			spatialGrid.insertObject(coconut);
			LK.setTimeout(function () {
				if (coconut && fruits.includes(coconut)) coconut.immuneToGameOver = false;
			}, 1000);
		}
	});
}
game.down = function (x, y) {
	if (activeFruit && !gameOver) {
		isDragging = true;
		game.move(x, y);
	}
};
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));
		activeFruit.y = dropPointY + GAME_CONSTANTS.DROP_START_Y_OFFSET;
		if (trajectoryLine) trajectoryLine.updateTrajectory(activeFruit.x, activeFruit.y);
	}
};
game.up = function () {
	if (isDragging && activeFruit && isClickable && !gameOver) dropFruit();
	isDragging = false;
};
function updatePhysics() {
	if (spatialGrid && Date.now() - spatialGrid.lastRebuildTime > spatialGrid.rebuildInterval) {
		spatialGrid.rebuildGrid(fruits);
	}
	// First phase: Apply gravity and update velocities
	for (var i = fruits.length - 1; i >= 0; i--) {
		var fruit = fruits[i];
		if (!fruit || fruit.isStatic || fruit.merging) continue;
		// Apply physics without position update (updatePhysics handles this)
		fruit.updatePhysics();
	}
	// Second phase: Check collisions and resolve overlaps
	checkFruitCollisions();
	// Third phase: Apply boundary collisions and finalize positions
	for (var i = fruits.length - 1; i >= 0; i--) {
		var fruit = fruits[i];
		if (!fruit || fruit.isStatic || fruit.merging) continue;
		var walls = {
			left: wallLeft,
			right: wallRight
		};
		fruit.checkBoundaries(walls, gameFloor);
		// Final stabilization check - aggressively zero out extremely small velocity values
		if (fruit.isFullyStabilized) {
			// If fully stabilized, ensure complete zero velocity to prevent micro-movements
			fruit.vx = 0;
			fruit.vy = 0;
			fruit.angularVelocity = 0;
		} else if (!fruit.isSleeping && fruit._boundaryContacts && (fruit._boundaryContacts.floor || fruit._boundaryContacts.left || fruit._boundaryContacts.right)) {
			// For fruits touching boundaries but not fully stabilized, apply additional damping
			// to encourage them to come to a full stop more quickly
			if (Math.abs(fruit.vx) < 0.2) fruit.vx = 0;
			if (Math.abs(fruit.vy) < 0.2) fruit.vy = 0;
			if (Math.abs(fruit.angularVelocity) < 0.01) fruit.angularVelocity = 0;
		}
	}
	// Fourth phase: Handle environmental interactions and position adjustments
	for (var i = 0; i < fruits.length; i++) {
		var fruit = fruits[i];
		if (fruit && !fruit.isStatic && !fruit.merging) {
			if (fruit.surroundedFrames === undefined) fruit.surroundedFrames = 0;
			var boundaryContact = fruit.wallContactFrames > 0;
			var floorProximity = fruit.y + fruit.height / 2 >= gameFloor.y - gameFloor.height / 2 - 10;
			var hasMultipleNeighbors = fruit.neighborContacts && fruit.neighborContacts.length >= 2;
			var isSlowMoving = Math.abs(fruit.vx) < 0.3 && Math.abs(fruit.vy) < 0.3;
			var hasGapBelow = false;
			if (floorProximity || hasMultipleNeighbors) {
				var potentialNeighbors = spatialGrid.getPotentialCollisions({
					x: fruit.x,
					y: fruit.y + fruit.height / 2 + 20,
					width: fruit.width * 0.6,
					height: 20,
					id: 'gap_check_' + fruit.id
				});
				hasGapBelow = potentialNeighbors.length === 0 && !floorProximity;
				if (hasGapBelow && isSlowMoving) {
					var leftCount = 0,
						rightCount = 0;
					for (var j = 0; j < fruit.neighborContacts.length; j++) {
						var neighborId = fruit.neighborContacts[j];
						for (var k = 0; k < fruits.length; k++) {
							if (fruits[k] && fruits[k].id === neighborId) {
								if (fruits[k].x < fruit.x) leftCount++;else if (fruits[k].x > fruit.x) rightCount++;
								break;
							}
						}
					}
					if (leftCount < rightCount) fruit.vx -= 0.05;else if (rightCount < leftCount) fruit.vx += 0.05;else fruit.vx += Math.random() * 0.1 - 0.05;
				}
			}
			// Final position update in the spatial grid
			spatialGrid.updateObject(fruit);
		}
	}
}
game.update = function () {
	if (gameOver) return;
	var currentScore = LK.getScore();
	if (currentScore >= lastScoreCheckForCoconut + GAME_CONSTANTS.COCONUT_SPAWN_SCORE_INTERVAL) {
		lastScoreCheckForCoconut = Math.floor(currentScore / GAME_CONSTANTS.COCONUT_SPAWN_SCORE_INTERVAL) * GAME_CONSTANTS.COCONUT_SPAWN_SCORE_INTERVAL;
		spawnCoconut();
	} else {
		if (currentScore > lastScoreCheckForCoconut) {
			lastScoreCheckForCoconut = Math.floor(currentScore / GAME_CONSTANTS.COCONUT_SPAWN_SCORE_INTERVAL) * GAME_CONSTANTS.COCONUT_SPAWN_SCORE_INTERVAL;
		}
	}
	updatePhysics();
	checkGameOver();
	updateFireBackground();
	for (var i = 0; i < activeFireElements.length; i++) {
		if (activeFireElements[i]) activeFireElements[i].update();
	}
};
initGame();
function updateFireBackground() {
	var uniqueFruitTypes = {};
	for (var i = 0; i < fruits.length; i++) {
		var fruit = fruits[i];
		if (fruit && !fruit.isStatic && !fruit.merging && fruit.type && fruit.type.id) {
			uniqueFruitTypes[fruit.type.id] = true;
		}
	}
	var uniqueTypeCount = Object.keys(uniqueFruitTypes).length;
	// Updated fire count logic based on unique types present
	var targetFireCount = GAME_CONSTANTS.FIRE_BASE_COUNT + Math.floor(uniqueTypeCount / GAME_CONSTANTS.FIRE_FRUIT_TYPE_THRESHOLD);
	targetFireCount = Math.min(targetFireCount, GAME_CONSTANTS.FIRE_MAX_COUNT);
	if (targetFireCount > activeFireElements.length) {
		var newFiresCount = targetFireCount - activeFireElements.length;
		for (var i = 0; i < newFiresCount; i++) {
			createFireElement(activeFireElements.length);
		}
	} else if (targetFireCount < activeFireElements.length) {
		var fireToRemoveCount = activeFireElements.length - targetFireCount;
		for (var i = 0; i < fireToRemoveCount; i++) {
			var fire = activeFireElements.pop();
			if (fire) {
				fire.destroy();
				fireContainer.removeChild(fire);
			}
		}
	}
}
function createFireElement(index) {
	var yPos = gameHeight + GAME_CONSTANTS.FIRE_START_Y_OFFSET - index * GAME_CONSTANTS.FIRE_STACK_Y_OFFSET;
	var xPos = gameWidth / 2 + (Math.random() * GAME_CONSTANTS.FIRE_X_SPREAD - GAME_CONSTANTS.FIRE_X_SPREAD / 2);
	var newFire = new FireElement(xPos, yPos);
	if (index % 2 === 1) {
		newFire.fireAsset.scaleX = -1;
	}
	fireContainer.addChildAt(newFire, 0);
	activeFireElements.push(newFire);
}
function removeFruitFromGame(fruit) {
	// Before removing, wake up any sleeping neighbors that were resting on this fruit
	if (fruit.neighborContacts && fruit.neighborContacts.length > 0) {
		for (var i = 0; i < fruit.neighborContacts.length; i++) {
			var neighborId = fruit.neighborContacts[i];
			for (var j = 0; j < fruits.length; j++) {
				if (fruits[j] && fruits[j].id === neighborId) {
					// Wake up any sleeping neighbors
					if (fruits[j].isSleeping || fruits[j].isFullyStabilized) {
						fruits[j].isSleeping = false;
						fruits[j].isFullyStabilized = false;
						fruits[j]._sleepCounter = 0; // Always reset sleep counter (don't check if exists)
						// Apply a small impulse to ensure physics recalculation
						fruits[j].vx += Math.random() * 0.6 - 0.3;
						fruits[j].vy -= 0.5; // Small upward force
					}
				}
			}
		}
	}
	var index = fruits.indexOf(fruit);
	if (index !== -1) fruits.splice(index, 1);
	spatialGrid.removeObject(fruit);
	fruit.destroy();
}