/**** 
* Plugins
****/ 
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/**** 
* Classes
****/ 
var Background = Container.expand(function () {
	var self = Container.call(this);
	// Attach the background_1 asset to the class
	self.backgrounds = [];
	for (var i = 0; i <= 10; i++) {
		var background = self.attachAsset('background_' + i, {
			anchorX: 0.5,
			anchorY: 0.5,
			index: i,
			visible: false
		});
		self.backgrounds.push(background);
	}
	self.backgrounds[0].visible = true; // Set the initial background visible
	// Position the background at the center of the screen
	self.x = 2048 / 2;
	self.y = 2732 / 2;
	// Function to change the background based on the index
	self.changeBackground = function (index) {
		var currentBg = self.backgrounds.find(function (bg) {
			return bg.visible;
		});
		if (index > 10) {
			// Ignore changes on background 10
			return;
		}
		var newBg = self.backgrounds[index];
		if (newBg && currentBg !== newBg) {
			tween(currentBg, {
				alpha: 0
			}, {
				duration: 500,
				easing: tween.easeOut,
				onFinish: function onFinish() {
					currentBg.visible = false;
					newBg.alpha = 0;
					newBg.visible = true;
					tween(newBg, {
						alpha: 1
					}, {
						duration: 500,
						easing: tween.easeIn
					});
				}
			});
		}
	};
});
// Create a class for bigHeart
var BigHeart = Container.expand(function () {
	var self = Container.call(this);
	self.currentGraphic = null;
	self.nextGraphic = null;
	self.tapLimit = TAPS_PER_LEVEL[0]; // Initialize tap limit using TAPS_PER_LEVEL
	self.nbTapsPerFrame = self.tapLimit / 6; // Initialize number of taps per frame
	self.heartType = 0; // Initialize tap counter
	self.explosionTriggered = false; // Initialize explosion flag
	self.highestScore = 0; // Initialize highestScore property
	// Attach the bigHeart asset to the class
	var bigHeartGraphics = self.attachAsset('bigHeart', {
		anchorX: 0.5,
		anchorY: 0.5,
		alpha: 0.1
	});
	self.heartFrames = {}; // Initialize heartFrames as a property of BigHeart class
	for (var type = 9; type >= 0; type--) {
		self.heartFrames[type] = [];
		for (var i = 5; i >= 0; i--) {
			self.heartFrames[type][5 - i] = self.attachAsset('heart_' + type + '_frame_' + i, {
				anchorX: 0.5,
				anchorY: 0.5,
				scaleX: 0.9,
				scaleY: 0.9,
				heartType: type,
				index: 5 - i,
				visible: true //!i
			});
		}
	}
	log("Setting frames in constructor...");
	self.currentGraphic = self.heartFrames[self.heartType][5];
	self.nextGraphic = self.heartFrames[self.heartType][4];
	if (self.currentGraphic) {
		if (self.currentGraphic && self.currentGraphic.scaleX !== undefined) {
			self.currentGraphic.scaleX = 1.1;
		}
		if (self.currentGraphic && self.currentGraphic.scaleY !== undefined) {
			self.currentGraphic.scaleY = 1.1;
		}
		//self.currentGraphic.visible = true;
	}
	if (self.nextGraphic) {
		if (self.nextGraphic.scaleX !== undefined) {
			self.nextGraphic.scaleX = 1.1;
		}
		if (self.nextGraphic.scaleY !== undefined) {
			self.nextGraphic.scaleY = 1.1;
		}
		//self.nextGraphic.visible = true;
	}
	// Position the bigHeart at the center of the screen
	self.x = 2048 / 2;
	self.y = 2732 / 2 - 300;
	// Define baseWidth and baseHeight
	var baseWidth = bigHeartGraphics.width;
	var baseHeight = bigHeartGraphics.height;
	// Event handler called when a press happens on element. This is automatically called on press if bigHeart is attached.
	self.down = function (x, y, obj) {
		// Log the down event
		log("Down event triggered on BigHeart");
		if (self.currentGraphic && self.nextGraphic) {
			log("Current indexes:: ", self.currentGraphic.index, ',', self.nextGraphic.index); // Log the tap count
		} else {
			log("CurrentGraphic or NextGraphic is not initialized.");
		}
		// Increment tap counter
		progressManager.manualGeneration();
		self.highestScore = Math.max(self.highestScore, parseInt(tapCount)); // Update highestScore
		self.animateBeat();
		self.animateFrames();
		// Create a new heart projection using the current frame index
		if (self.currentGraphic) {
			projectionsManager.popHearts(self.currentGraphic.heartType);
		} else {
			log("CurrentGraphic is not initialized.");
		}
		shakeMiddleground();
	};
	// Beat Animation
	self.animateBeat = function () {
		if (self.explosionTriggered) {
			return;
		}
		// Play beat sound
		LK.getSound('bump').play();
		if (self.currentGraphic) {
			self.currentGraphic._activeTween = tween(self.currentGraphic, {
				scaleX: 1.2,
				scaleY: 1.2,
				x: 0
			}, {
				duration: 100,
				onFinish: function onFinish() {
					if (self.currentGraphic) {
						delete self.currentGraphic._activeTween;
						tween(self.currentGraphic, {
							scaleX: 1.1,
							scaleY: 1.1,
							x: 0
						}, {
							duration: 100
						});
					}
				}
			});
		}
		if (self.nextGraphic && !self.nextGraphic._activeTween) {
			self.nextGraphic._activeTween = tween(self.nextGraphic, {
				duration: 250,
				onFinish: function onComplete() {
					if (self.nextGraphic) {
						delete self.nextGraphic._activeTween;
						tween(self.nextGraphic, {
							scaleX: 1.2,
							scaleY: 1.2,
							x: 0
						}, {
							duration: 100,
							onFinish: function onFinish() {
								if (self.nextGraphic) {
									delete self.nextGraphic._activeTween;
									tween(self.nextGraphic, {
										scaleX: 1.1,
										scaleY: 1.1,
										x: 0
									}, {
										duration: 100
									});
								}
							}
						});
					}
				}
			});
		}
	};
	// Frames Animation
	self.animateFrames = function () {
		if (self.explosionTriggered || self.highestScore >= TAPS_PER_LEVEL[self.heartType + 1]) {
			return;
		}
		// Calculate progress in current level (0 to 1)
		var startTap = self.heartType === 0 ? 0 : TAPS_PER_LEVEL[self.heartType - 1];
		var endTap = TAPS_PER_LEVEL[self.heartType];
		var progress = (self.highestScore - startTap) / (endTap - startTap);
		// Map progress to frame indices (frames go from 5 to 0)
		var frameProgress = progress * 5;
		var currentFrame = 5 - Math.floor(frameProgress);
		var nextFrame = Math.max(0, currentFrame - 1);
		var alpha = frameProgress - Math.floor(frameProgress);
		// Update frame visibility only if current frame changed
		if (self.currentGraphic !== self.heartFrames[self.heartType][currentFrame] || self.nextGraphic !== self.heartFrames[self.heartType][nextFrame]) {
			// Hide all frames
			self.heartFrames[self.heartType].forEach(function (frame) {
				return frame.visible = false;
			});
			// Setup current and next frames
			self.currentGraphic = self.heartFrames[self.heartType][currentFrame];
			self.nextGraphic = self.heartFrames[self.heartType][nextFrame];
			[self.currentGraphic, self.nextGraphic].forEach(function (frame) {
				if (frame) {
					frame.visible = true;
					frame.scaleX = frame.scaleY = 1.1;
				}
			});
		}
		// Update alpha for smooth transition
		if (self.currentGraphic) {
			self.currentGraphic.alpha = 1 - alpha;
		}
	};
	// Explosion Animation
	self.animateExplosion = function (callback) {
		if (!self.explosionTriggered) {
			self.explosionTriggered = true;
			LK.getSound('boom').play();
			shakeScreen();
			var cloneGraphic;
			if (self.nextGraphic) {
				cloneGraphic = LK.getAsset('heart_' + self.nextGraphic.heartType + '_frame_' + self.nextGraphic.index, {
					anchorX: 0.5,
					anchorY: 0.5,
					scaleX: self.nextGraphic.scaleX,
					scaleY: self.nextGraphic.scaleY,
					alpha: 1
				});
				self.addChild(cloneGraphic);
			}
			LK.setTimeout(function () {
				LK.effects.flashScreen(0xffffff, 2000); // Flash the screen white for 500ms
				if (cloneGraphic) {
					log("nextGraphic for explosion!", cloneGraphic);
					tween(cloneGraphic, {
						scaleX: 45,
						scaleY: 45,
						alpha: 0
					}, {
						duration: 3000,
						easing: tween.easeOut,
						onFinish: function onFinish() {
							// Make all frames of the current heartType invisible
							self.heartFrames[self.heartType].forEach(function (frame) {
								frame.visible = false;
							});
							self.explosionTriggered = false; // Reset explosion flag
							cloneGraphic.destroy(); // Remove the clone after animation
							if (callback) {
								callback();
							}
						}
					});
					// Pre scale next heart
					if (self.heartFrames[self.heartType + 1]) {
						tween(self.heartFrames[self.heartType + 1][5], {
							scaleX: 1.1,
							scaleY: 1.1
						}, {
							duration: 300,
							easing: tween.easeOut
						});
						tween(self.heartFrames[self.heartType + 1][4], {
							scaleX: 1.1,
							scaleY: 1.1
						}, {
							duration: 300,
							easing: tween.easeOut
						});
					}
				} else {
					log("No nextGraphic for explosion!");
				}
			}, 205);
		}
	};
	// Initialize heart based on tap count
	self.initHeart = function () {
		var newHeartType = 0;
		self.highestScore = Math.max(tapCount, storage.highestScore || 0);
		// Determine heart type based on tapCount
		for (var level = 9; level > 0; level--) {
			// 0 < 50  < 99 => 0
			// 99 < 110 < 999 => 1
			if (self.highestScore >= TAPS_PER_LEVEL[level - 1]) {
				newHeartType = level;
				break;
			}
		}
		log("initHeart - Selected heartType: " + newHeartType);
		// Hide frames up to current heart type
		for (var type = 0; type < newHeartType; type++) {
			var frames = self.heartFrames[type] || [];
			frames.forEach(function (frame) {
				frame.visible = false;
			});
		}
		// Update heart type and graphics
		self.heartType = newHeartType;
		self.tapLimit = TAPS_PER_LEVEL[newHeartType];
		self.nbTapsPerFrame = self.tapLimit / 6;
		// Calculate progress in current level (0 to 1)
		var startTap = self.heartType === 0 ? 0 : TAPS_PER_LEVEL[self.heartType - 1];
		var endTap = TAPS_PER_LEVEL[self.heartType];
		var progress = Math.min(1, (self.highestScore - startTap) / (endTap - startTap));
		log("initHeart - Progress calculation: " + progress + " (tapCount: " + self.highestScore + ", startTap: " + startTap + ", endTap: " + endTap + ")");
		// Map progress to frame indices (frames go from 5 to 0)
		var frameProgress = Math.min(5, progress * 5);
		var currentFrame = Math.max(0, Math.min(5, Math.floor(frameProgress)));
		var nextFrame = Math.max(0, currentFrame + 1);
		var alpha = frameProgress - Math.floor(frameProgress);
		log("initHeart - Frame indices: current=" + currentFrame + ", next=" + nextFrame + ", alpha=" + alpha);
		// Check if heartFrames exists for this type
		if (!self.heartFrames[self.heartType]) {
			log("ERROR: No heartFrames found for type " + self.heartType);
			return;
		}
		log("initHeart - Number of frames for type " + self.heartType + ": " + self.heartFrames[self.heartType].length);
		// Set up current and next frames
		self.currentGraphic = self.heartFrames[self.heartType][currentFrame];
		self.nextGraphic = self.heartFrames[self.heartType][nextFrame];
		log("initHeart - Current graphic: " + (self.currentGraphic ? "exists" : "null") + ", Next graphic: " + (self.nextGraphic ? "exists" : "null"));
		// Set visibility and alpha values
		if (self.currentGraphic) {
			self.currentGraphic.visible = true;
			if (isDebug) {
				self.currentGraphic.tint = 0x00FF00;
			}
			self.currentGraphic.alpha = 1 - alpha;
			log("initHeart - Set current graphic alpha: " + (1 - alpha));
		} else {
			log("ERROR: currentGraphic is null");
			return;
		}
		if (self.nextGraphic) {
			self.nextGraphic.visible = true;
			if (isDebug) {
				self.nextGraphic.tint = 0xFF0000;
			}
			//self.nextGraphic.alpha = alpha;
			log("initHeart - Set next graphic alpha: " + alpha);
		} else {
			log("ERROR: nextGraphic is null");
			return;
		}
		log("initHeart - Starting tweens");
		tween(self.currentGraphic, {
			scaleX: 1.1,
			scaleY: 1.1
		}, {
			duration: 300,
			easing: tween.easeOut
		});
		tween(self.nextGraphic, {
			scaleX: 1.1,
			scaleY: 1.1
		}, {
			duration: 300,
			easing: tween.easeOut
		});
		shakeMiddleground();
		log("initHeart - Completed");
	};
});
// Create a class for bigHeart
var GeneratorButton = Container.expand(function (index) {
	var self = Container.call(this);
	// Attach a button asset to the class
	var buttonGraphics = self.attachAsset('generatorButton', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	// Ensure rightBoard is initialized before accessing its properties
	self.index = Math.min(Math.max(0, index), maxGenerators);
	self.isAnimating = false; // Add isAnimating flag at instance level
	var generatorAsset = self.attachAsset('generator_' + self.index, {
		anchorX: 0.5,
		anchorY: 0.5,
		y: -10
	});
	if (typeof GENERATORS !== 'undefined') {
		self.config = Object.values(GENERATORS).find(function (g) {
			return g.id === self.index;
		});
	} else {
		console.error("GENERATORS is not defined");
		self.config = {
			cost: 0
		}; // Default to prevent undefined error
	}
	self.costText = new Text2(self.config.cost.toString(), {
		size: 50,
		fill: 0x043515,
		dropShadow: true,
		align: 'right'
	});
	self.countText = new Text2(' ', {
		size: 50,
		fill: 0x043515,
		dropShadow: true,
		align: 'center'
	});
	self.countText.x = -buttonGraphics.width / 2 + 10;
	self.countText.y = -buttonGraphics.height / 2 + 10;
	self.addChild(self.countText);
	self.costText.x = 30;
	self.costText.y = 75;
	self.visible = false;
	self.addChild(self.costText);
	if (typeof GENERATORS !== 'undefined') {
		self.config = Object.values(GENERATORS).find(function (g) {
			return g.id === self.index;
		});
	} else {
		console.error("GENERATORS is not defined");
	}
	// Position the button at the center of the screen
	// self.x = 2048 / 2;
	// self.y = 2732 / 2;
	// Event handler called when a press happens on the button
	var isSwipingButtons = false; // Global flag to track swipe/animation state
	var dragNode = null;
	var globalStartY = 0;
	self.down = function (x, y, obj) {
		if (isSwipingButtons) {
			return;
		} // Prevent interaction during animation
		log("Generator button pressed", y);
		globalStartY = y;
		self.startTime = Date.now();
		self.startX = x;
		dragNode = rightBoard;
	};
	self.up = function (x, y, obj) {
		if (isSwipingButtons) {
			dragNode = null;
			return;
		}
		var endTime = Date.now();
		var timeDiff = endTime - self.startTime;
		var distance = Math.sqrt(Math.pow(x - self.startX, 2) + Math.pow(y - globalStartY, 2));
		if (timeDiff < TAP_DETECT_DELAY && distance < SWIPE_THRESHOLD) {
			// Detected as a tap
			log("Detected tap on Generator button");
			// Check if tapCount is less than the cost of the generator
			if (tapCount < self.config.cost) {
				log("No enough love");
				LK.getSound('cantBuyGenerator').play(); // Play sound when player can't buy
				// Store original position
				var originalX = self.x;
				var originalY = self.y;
				isSwipingButtons = true;
				dragNode = null; // Reset dragNode immediately when starting animation
				tween(self, {
					x: originalX + 10,
					y: originalY + 10
				}, {
					duration: 100,
					easing: tween.easeInOut,
					onFinish: function onFinish() {
						if (!isSwipingButtons) {
							return;
						} // Prevent animation continuation if interrupted
						tween(self, {
							x: originalX - 10,
							y: originalY - 10
						}, {
							duration: 100,
							easing: tween.easeInOut,
							onFinish: function onFinish() {
								if (!isSwipingButtons) {
									return;
								}
								tween(self, {
									x: originalX,
									y: originalY
								}, {
									duration: 100,
									easing: tween.easeInOut,
									onFinish: function onFinish() {
										isSwipingButtons = false;
									}
								});
							}
						});
					}
				});
				return; // Exit if not enough taps
			}
			// Buy the corresponding generator using progressManager
			if (progressManager.buyGenerator(self.index)) {
				log("Generator purchased successfully");
				tween(self, {
					scaleX: 1.2,
					// Increase scale for bump effect
					scaleY: 1.2
				}, {
					duration: 100,
					// Duration of the bump
					easing: tween.easeOut,
					// Easing function for smooth effect
					onFinish: function onFinish() {
						tween(self, {
							// Return to original scale
							scaleX: 1,
							scaleY: 1
						}, {
							duration: 100,
							easing: tween.easeIn
						});
					}
				});
			} else {
				log("Failed to purchase generator");
			}
		} else {
			// Only handle swipe if not animating
			if (!isSwipingButtons && dragNode === rightBoard) {
				log("Detected swipe on Generator button");
				rightBoard.handleSwipe(y - globalStartY);
			}
			dragNode = null; // Reset dragNode when mouse is up
		}
	};
});
var GiftBox = Container.expand(function () {
	var self = Container.call(this);
	var giftBoxGraphics = self.attachAsset('giftBox', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	self.speed = Math.random() * 2 + 1; // Random speed for each gift box
	self.visible = false; // Start invisible
	// Function to make the gift box fall
	self.fall = function () {
		self.x = 100 + Math.random() * 1548; // Random x position
		self.y = -100; // Start above the screen
		self.visible = true;
		log("GiftBox is falling from position: ", self.x, self.y);
		tween(self, {
			y: 3000 // Fall to below the screen
		}, {
			duration: Math.random() * 3000 + 3000,
			// Random duration for fall
			easing: tween.easeInOut,
			onFinish: function onFinish() {
				self.visible = false;
				self.destroy();
			}
		});
	};
	// Add tap event to destroy the gift box
	self.down = function (x, y, obj) {
		if (isSpinWheelGamePlaying) {
			return;
		}
		LK.getSound('bonus').play(); // Play bonus sound when giftbox is tapped
		isSpinWheelGamePlaying = true;
		var spinwheelGame = new SpinwheelGame();
		spinwheelGame.x = self.x;
		spinwheelGame.y = self.y;
		spinwheelGame.scaleX = 0;
		spinwheelGame.scaleY = 0;
		foregroundContainer.addChild(spinwheelGame);
		tween(spinwheelGame, {
			x: 1024,
			y: 1366,
			scaleX: 1,
			scaleY: 1
		}, {
			duration: 1000,
			easing: tween.easeOut,
			onFinish: function onFinish() {
				tween(spinwheelGame.backdrop, {
					width: 2048,
					height: 2732
				}, {
					duration: 1000,
					easing: tween.easeOut
				});
				spinwheelGame.start();
			}
		});
		self.destroy();
	};
});
var GiftBoxManager = Container.expand(function () {
	var self = Container.call(this);
	self.giftBox;
	self.isStarted = false;
	// Function to spawn a gift box
	self.spawnGiftBox = function () {
		if (isSpinWheelGamePlaying) {
			log("SpinWheelGame is playing, skipping gift box spawn.");
			return; // Stop spawning gift boxes if SpinwheelGame is playing
		}
		log("Spawning a new gift box.");
		if (self.giftBox) {
			self.removeChild(self.giftBox);
			self.giftBox.destroy();
			self.giftBox = null;
		}
		self.giftBox = new GiftBox();
		self.addChild(self.giftBox);
		self.giftBox.fall();
	};
	// Update function to manage gift boxes
	self.start = function () {
		self.isStarted = true;
		function scheduleNextGiftBox() {
			var intervalDuration = 30000 + 40000 * Math.random();
			LK.setTimeout(function () {
				self.spawnGiftBox();
				scheduleNextGiftBox(); // Schedule next gift box with a new random interval
			}, intervalDuration);
		}
		scheduleNextGiftBox(); // Start the first scheduling
	};
});
var GiftRain = Container.expand(function () {
	var self = Container.call(this);
	self.giftPool = [];
	self.activeGifts = [];
	self.generatorCount = 0;
	// Initialize gift pool
	for (var i = 0; i < 50; i++) {
		var gift = new RainDrop();
		gift.updateAsset(self.generatorCount);
		gift.speed = Math.random() * 3 + 5; // Random speed for each gift
		gift.visible = false; // Start invisible
		self.addChild(gift); // Add to container immediately
		self.giftPool.push(gift);
	}
	// Function to spawn a gift
	self.spawnGift = function (generatorId) {
		log("Spawning gift. Active gifts count: ", self.activeGifts.length);
		if (self.giftPool.length > 0) {
			var gift = self.giftPool.pop();
			gift.x = Math.random() * (1948 - 100) + 100;
			gift.y = -300; // Start above the screen
			gift.rotation = Math.random() * (Math.PI / 2) - Math.PI / 4; // Set random rotation between -PI/4 and +PI/4
			gift.alpha = 0.8;
			gift.visible = true;
			gift.updateAsset(generatorId); // Update asset when spawning
			self.activeGifts.push(gift);
			log("Gift spawned at position: ", gift.x, gift.y);
		}
	};
	// Update function to move gifts
	self.update = function () {
		if (isSpinWheelGamePlaying || isInfiniteMode) {
			return;
		}
		for (var i = self.activeGifts.length - 1; i >= 0; i--) {
			var gift = self.activeGifts[i];
			gift.y += gift.speed;
			if (gift.y > 3000) {
				log("Gift moved out of bounds and will be recycled. Position: ", gift.x, gift.y);
				gift.x = Math.random() * (1948 - 100) + 100;
				gift.y = -300; // Start above the screen
				gift.rotation = Math.random() * (Math.PI / 2) - Math.PI / 4; // Set random rotation between -PI/4 and +PI/4
				gift.visible = true;
			}
		}
	};
	// Function to increase gift spawn rate
	self.increaseSpawnRate = function (generatorId) {
		self.generatorCount++;
		self.spawnGift(generatorId);
		log("Increasing spawn rate. Generator count: ", self.generatorCount);
		// for (var i = 0; i < self.generatorCount; i++) {
		// 	self.spawnGift(generatorId);
		// }
	};
});
var LoveField = Container.expand(function () {
	var self = Container.call(this);
	self.hearts = [];
	self.maxHearts = 30;
	// Initialize heart assets
	for (var i = 4; i <= 9; i++) {
		for (var j = 0; j < self.maxHearts / 6; j++) {
			var heart = self.attachAsset('heart_' + i + '_frame_0', {
				anchorX: 0.5,
				anchorY: 0.5,
				scaleX: 0.5,
				scaleY: 0.5,
				heartType: i,
				visible: false
			});
			heart.vx = (Math.random() - 0.5) * 2;
			heart.vy = Math.random() * 2 + 1;
			heart.down = function (x, y, obj) {
				// Create a clone heart
				var cloneHeart = LK.getAsset('heart_' + this.heartType + '_frame_0', {
					anchorX: 0.5,
					anchorY: 0.5,
					scaleX: this.scaleX,
					scaleY: this.scaleY,
					x: this.x,
					y: this.y,
					rotation: this.rotation,
					alpha: 1
				});
				self.addChild(cloneHeart);
				// Scale clone heart to 50 and fade out
				tween(cloneHeart, {
					scaleX: 50,
					scaleY: 50,
					alpha: 0
				}, {
					duration: 1000,
					onFinish: function onFinish() {
						// Destroy the clone heart after animation
						cloneHeart.destroy();
					}
				});
				LK.getSound('endHeartTap').play(); // Play pop sound for manualGeneration
				LK.setTimeout(function () {
					LK.getSound('pop2').play();
				}, 666);
			};
			self.hearts.push(heart);
		}
	}
	// Update function to animate hearts
	self.update = function () {
		self.hearts.forEach(function (heart) {
			if (!heart.visible) {
				if (heart._activeTween) {
					heart._activeTween.stop();
					heart._activeTween = null;
				}
				var targetX, targetY; // Define targetX and targetY variables
				// Determine if the heart should start from the bottom corners 
				if (Math.random() > 0.5) {
					heart.x = Math.random() > 0.5 ? 0 : 2048; // Start from left or right bottom corner 
					heart.y = 2732 + heart.height; // Start below the screen 
					targetX = heart.x === 0 ? Math.random() * 1024 + 1024 : Math.random() * 1024;
					targetY = -heart.height; // Move to above the screen 
				} else {
					// Start from one of the top corners 
					heart.x = Math.random() > 0.5 ? 0 : 2048; // Start from left or right corner 
					heart.y = -heart.height; // Start above the screen 
					targetX = heart.x === 0 ? Math.random() * 1024 + 1024 : Math.random() * 1024;
					targetY = 2732 + heart.height; // Fall to below the screen 
				}
				heart.scaleX = 0.5; // Initial scale 
				heart.scaleY = 0.5;
				var delay = Math.random() * 5000; // Random delay up to 2 seconds 
				heart.visible = true;
				tween(heart, {
					x: targetX,
					y: targetY,
					rotation: (Math.random() - 0.5) * Math.PI / 4 // Random rotation like a leaf 
				}, {
					duration: Math.random() * 3000 + 4000,
					// Longer duration for a gentle fall 
					easing: tween.easeInOut,
					// Smooth easing 
					delay: delay,
					onFinish: function onFinish() {
						heart.visible = false; // Hide after animation
						heart.x = Math.random() > 0.5 ? 0 : 2048; // Reset position to start from left or right corner
						heart.y = heart.y < 0 ? -heart.height : 2732 + heart.height; // Reset position to start above or below the screen
					}
				});
			}
		});
	};
});
var ProgressBar = Container.expand(function () {
	var self = Container.call(this);
	// Attach the progressBack asset
	var progressBack = self.attachAsset('progressBack', {
		anchorX: 0,
		anchorY: 0.5,
		width: 2048,
		height: 40,
		alpha: 0.8
	});
	// Attach the progressBar asset
	var progressBar = self.attachAsset('progressBar', {
		anchorX: 0,
		anchorY: 0.5,
		width: 2048,
		height: 32,
		alpha: 0.8,
		y: 4
	});
	// Set initial progress to 0
	self.progress = 0;
	self.visible = !isInfiniteMode;
	// Method to update the progress
	self.updateProgress = function (value) {
		if (isInfiniteMode) {
			return;
		}
		self.progress = Math.max(0, Math.min(1, value)); // Clamp value between 0 and 1
		progressBar.scaleX = self.progress; // Scale the progressBar based on progress
	};
});
// Create a class for Projections
var Projections = Container.expand(function () {
	var self = Container.call(this);
	var nbProjections = 10;
	var heartSpeed = 20;
	var gravity = 0.5;
	var initialScale = 0.25;
	var scaleVariation = 0.5;
	var alphaDecay = 0.002;
	self.heartPool = [];
	self.preloadedAssets = {}; // Preload assets for each heart type
	for (var type = 0; type <= 9; type++) {
		self.preloadedAssets[type] = LK.getAsset('heart_' + type + '_frame_0', {
			anchorX: 0.5,
			anchorY: 0.5,
			scaleX: 0.5,
			scaleY: 0.5
		});
	}
	// Initialize heart pool
	for (var i = 0; i < nbProjections * 5; i++) {
		var heart = new Container();
		heart.vx = 0;
		heart.vy = 0;
		heart.alpha = 0;
		heart.update = function () {
			this.x += this.vx;
			this.y += this.vy;
			this.vy += gravity; // Add gravity effect 
			this.alpha -= alphaDecay;
			if (this.alpha <= 0 || this.y > 2900) {
				this.alpha = 0;
				self.heartPool.push(this);
			}
		};
		// Add all heart type assets to the heart Container
		for (var type = 0; type <= 9; type++) {
			var heartAsset = heart.attachAsset('heart_' + type + '_frame_0', {
				anchorX: 0.5,
				anchorY: 0.5,
				scaleX: 0.5,
				scaleY: 0.5,
				heartType: type,
				visible: type === 0 // Only make the current heartType visible
			});
		}
		self.heartPool.push(heart);
	}
	self.x = 2048 / 2;
	self.y = 2732 / 2 - 400;
	// Function to pop hearts
	self.popHearts = function (heartType) {
		if (self.isUpdatingHeartType) {
			return;
		} // Exit if updateHeartType is running
		for (var i = 0; i < nbProjections; i++) {
			if (self.heartPool.length > 0) {
				var heart = self.heartPool.pop();
				if (heart.alpha <= 0) {
					// Ensure heart has finished its previous animation
					heart.x = 0;
					heart.y = 0;
					heart.vx = (Math.random() - 0.5) * heartSpeed;
					heart.vy = (Math.random() - 1.5) * heartSpeed;
					heart.alpha = 0.8;
					heart.scaleX = initialScale + Math.random() * scaleVariation; // Randomize scale between initialScale and initialScale + scaleVariation
					heart.scaleY = heart.scaleX; // Keep aspect ratio consistent
					heart.rotation = Math.random() * Math.PI * 2; // Randomize rotation between 0 and 2Ο
					self.addChild(heart);
				} else {
					self.heartPool.push(heart); // Return heart to pool if it hasn't finished animation
				}
			}
		}
	};
	self.updateHeartType = function (heartType) {
		// Update the heart type for all hearts in the pool
		self.isUpdatingHeartType = true; // Set flag to indicate updateHeartType is running
		self.heartPool.forEach(function (heart) {
			heart.children.forEach(function (child) {
				// Iterate over all children
				child.visible = child.heartType === heartType; // Set visibility based on heartType
			});
		});
		self.isUpdatingHeartType = false; // Reset flag after updateHeartType completes
	};
});
var RainDrop = Container.expand(function () {
	var self = Container.call(this);
	self.assets = [];
	// Initialize assets for RainDrop
	for (var i = 0; i <= maxGenerators; i++) {
		var asset = self.attachAsset('generator_' + i, {
			anchorX: 0.5,
			anchorY: 0.5,
			visible: true
		});
		self.assets.push(asset);
	}
	// Function to update asset visibility based on index
	self.updateAsset = function (index) {
		log("Updating asset visibility for index: ", index);
		if (index >= 0 && index < self.assets.length) {
			self.assets.forEach(function (asset, i) {
				asset.visible = i === index;
				if (asset.visible) {
					log("Asset at index ", i, " is now visible.");
				}
			});
		}
	};
});
var ResetButton = Container.expand(function () {
	var self = Container.call(this);
	// Attach the resetButton asset to the class
	self.resetButtonGraphics = self.attachAsset('resetButton', {
		anchorX: 0.5,
		anchorY: 0.5,
		alpha: 0.5
	});
	// Event handler called when a press happens on the reset button
	self.down = function (x, y, obj) {
		log("Reset button pressed");
		if (resetTappedOnce) {
			// Call the resetProgress function to reset the game
			resetProgress();
		} else {
			popMessage("RESTART FROM ZERO ?\r\nTap to confirm");
			resetTappedOnce = true;
			self.resetButtonGraphics.alpha = 1;
			LK.setTimeout(function () {
				resetTappedOnce = false;
				if (self.resetButtonGraphics) {
					self.resetButtonGraphics.alpha = 0.5;
				}
			}, 2000);
		}
	};
});
var RightBoard = Container.expand(function () {
	var self = Container.call(this);
	// Attach the rightBoard asset to the class
	var rightBoardGraphics = self.attachAsset('rightBoard', {
		anchorX: 0.5,
		anchorY: 0.5,
		width: 400,
		height: 3000,
		alpha: 0
	});
	self.lastY = 0;
	self.velocity = 0;
	self.isAnimating = false;
	self.lastTime = Date.now();
	self.swipeOffset = 0;
	// Add momentum animation
	self.applyMomentum = function () {
		if (Math.abs(self.velocity) < 0.1) {
			self.velocity = 0;
			self.isAnimating = false;
			return;
		}
		var currentTime = Date.now();
		var deltaTime = (currentTime - self.lastTime) / 16; // Normalize to ~60fps
		self.lastTime = currentTime;
		// Apply friction
		self.velocity *= Math.pow(0.95, deltaTime);
		// Calculate movement with velocity
		var deltaY = self.velocity * deltaTime;
		// Check boundaries
		var firstButtonNewY = self.y + self.generatorButtons[0].y + deltaY;
		var lastButtonNewY = self.y + self.generatorButtons[self.generatorButtons.length - 1].y + deltaY;
		// Bounce effect at boundaries
		if (firstButtonNewY > 1000) {
			self.velocity *= -0.5;
			deltaY = 1000 - (self.y + self.generatorButtons[0].y);
		} else if (lastButtonNewY < 1366) {
			self.velocity *= -0.5;
			deltaY = 1366 - (self.y + self.generatorButtons[self.generatorButtons.length - 1].y);
		}
		// Move all generator buttons
		self.generatorButtons.forEach(function (button) {
			button.y += deltaY;
		});
		// Continue animation
		if (self.isAnimating) {
			LK.setTimeout(self.applyMomentum, 16);
		}
	};
	// Position the rightBoard at the right side of the screen
	self.x = 1848;
	self.y = 1366;
	self.buttonsOffsetX = 50;
	self.buttonsOffsetY = -300;
	self.generatorButtons = []; // Initialize an array to hold generator buttons
	// Create and position generator buttons
	for (var i = 0; i <= 9; i++) {
		// Todo : use maxGenerators
		var generatorButton = new GeneratorButton(i);
		generatorButton.x = self.buttonsOffsetX; //0 * self.x + i * 100 - 100; // Position buttons with some spacing
		generatorButton.y = self.buttonsOffsetY + i * 300 + i * 300 * 0.1; //self.y;
		self.generatorButtons.push(generatorButton);
		self.addChild(generatorButton);
	}
	self.cleanUp = function () {
		// Re-calculate positions for all buttons while maintaining the swipe offset
		self.generatorButtons.forEach(function (button, index) {
			button.y = self.buttonsOffsetY + index * 300 + index * 300 * 0.1 + self.swipeOffset;
		});
	};
	self.down = function (x, y, obj) {
		log("RightBoard pressed", y);
		globalStartY = y;
		self.lastY = y;
		self.velocity = 0;
		self.isAnimating = false;
		dragNode = self;
		self.startTime = Date.now();
		self.lastTime = Date.now();
	};
	self.move = function (x, y, obj) {
		// Check if any generator button is animating
		var isAnyButtonAnimating = self.generatorButtons.some(function (button) {
			return button.isAnimating;
		});
		if (isAnyButtonAnimating) {
			return; // Prevent movement if any button is animating
		}
		if (dragNode === self || dragNode instanceof GeneratorButton) {
			// Check if a button or rightBoard is being dragged
			var deltaY = y - globalStartY; // Calculate deltaY based on movement
			// Only apply movement if the delta exceeds the swipe threshold
			if (Math.abs(deltaY) > SWIPE_THRESHOLD) {
				log("Move detected with deltaY:", deltaY);
				// Calculate new positions for the first and last buttons
				var firstButtonNewY = self.y + self.generatorButtons[0].y + deltaY;
				var lastButtonNewY = self.y + self.generatorButtons[self.generatorButtons.length - 1].y + deltaY;
				log("firstButtonNewY:", firstButtonNewY, "lastButtonNewY:", lastButtonNewY);
				// Check if the first button stays above the screen center and the last button stays below the screen center
				// Adjust deltaY to prevent moving beyond boundaries
				if (deltaY > 0) {
					deltaY = Math.min(deltaY, 1000 - (self.y + self.generatorButtons[0].y));
				}
				if (deltaY < 0) {
					deltaY = Math.max(deltaY, 1366 - (self.y + self.generatorButtons[self.generatorButtons.length - 1].y));
				}
				// Move all generator buttons vertically based on swipe direction
				self.generatorButtons.forEach(function (button) {
					button.y += deltaY;
				});
				self.swipeOffset += deltaY; // Update the total swipe offset
				globalStartY = y; // Update globalStartY for continuous movement
			}
		}
	};
	self.up = function (x, y, obj) {
		if (!allowSwipe) {
			return;
		}
		var endTime = Date.now();
		var timeDiff = endTime - self.startTime;
		var distance = Math.sqrt(Math.pow(x - self.startX, 2) + Math.pow(y - globalStartY, 2));
		if (timeDiff < TAP_DETECT_DELAY && distance < SWIPE_THRESHOLD) {
			// Detected as a tap
			log("Detected tap on RightBoard");
		} else {
			// Start momentum animation
			self.isAnimating = true;
			LK.setTimeout(function () {
				self.applyMomentum();
				// Clean up after momentum animation
				self.cleanUp();
			}, 16);
		}
		dragNode = null;
	};
	self.handleSwipe = function (deltaY, y) {
		if (!allowSwipe) {
			return;
		}
		// Calculate new positions for the first and last buttons
		var firstButtonNewY = self.y + self.generatorButtons[0].y + deltaY;
		var lastButtonNewY = self.y + self.generatorButtons[self.generatorButtons.length - 1].y + deltaY;
		log("firstButtonNewY:", firstButtonNewY, "lastButtonNewY:", lastButtonNewY);
		// Check if the first button stays above the screen center and the last button stays below the screen center
		// Adjust deltaY to prevent moving beyond boundaries
		if (deltaY > 0) {
			deltaY = Math.min(deltaY, 1000 - (self.y + self.generatorButtons[0].y));
		}
		if (deltaY < 0) {
			deltaY = Math.max(deltaY, 1366 - (self.y + self.generatorButtons[unlockedGeneratorIndex].y));
		}
		// Move all generator buttons vertically based on swipe direction
		self.generatorButtons.forEach(function (button) {
			button.y += deltaY;
		});
		self.swipeOffset += deltaY; // Update the total swipe offset
		globalStartY = y; // Update globalStartY for continuous movement
	};
	self.move = function (x, y, obj) {
		if (!allowSwipe) {
			return;
		}
		if (dragNode === self || dragNode instanceof GeneratorButton) {
			// Check if a button or rightBoard is being dragged
			var deltaY = y - globalStartY; // Calculate deltaY based on movement
			// Only apply movement if the delta exceeds the swipe threshold
			if (Math.abs(deltaY) > SWIPE_THRESHOLD) {
				log("Move detected with deltaY:", deltaY);
				// Calculate new positions for the first and last buttons
				var firstButtonNewY = self.y + self.generatorButtons[0].y + deltaY;
				var lastButtonNewY = self.y + self.generatorButtons[self.generatorButtons.length - 1].y + deltaY;
				log("firstButtonNewY:", firstButtonNewY, "lastButtonNewY:", lastButtonNewY);
				// Adjust deltaY to prevent moving beyond boundaries
				if (deltaY > 0) {
					deltaY = Math.min(deltaY, 1000 - (self.y + self.generatorButtons[0].y));
				}
				if (deltaY < 0) {
					deltaY = Math.max(deltaY, 1366 - (self.y + self.generatorButtons[self.generatorButtons.length - 1].y));
				}
				// Move all generator buttons vertically based on swipe direction
				self.generatorButtons.forEach(function (button) {
					button.y += deltaY;
				});
				self.swipeOffset += deltaY; // Update the total swipe offset
				globalStartY = y; // Update globalStartY for continuous movement
			}
		}
	};
});
var SpinwheelGame = Container.expand(function () {
	var self = Container.call(this);
	self.bonuses = [];
	self.wheelSpinning = false;
	self.wheelStopped = false;
	self.nbGeneratorsOnWheel = 0;
	self.wonBonusIndex = 0;
	self.isSlowingDown = false;
	self.slowDownStartTime = 0;
	self.nextTickTime = 0;
	self.tickSoundComplete = false;
	// Attach the rightBoard asset to the class
	self.backdrop = self.attachAsset('backdrop', {
		anchorX: 0.5,
		anchorY: 0.5,
		width: 1000,
		height: 1000,
		alpha: 0.5 // Set semi-transparency
	});
	// Add event handler to catch clicks
	self.backdrop.down = function (x, y, obj) {
		// Prevent interaction with objects behind the backdrop
		log("Backdrop clicked, preventing interaction with objects behind it.");
		if (isSpinWheelGamePlaying) {
			LK.getSound('pop').play();
			self.stopWheel();
		}
	};
	self.addChild(self.backdrop);
	self.rightBoardGraphics = self.attachAsset('rightBoard', {
		anchorX: 0.5,
		anchorY: 0.5,
		width: 1480,
		height: 1480
	});
	// Attach the selector asset to the SpinwheelGame
	self.selector = self.attachAsset('selector', {
		anchorX: 0.5,
		anchorY: 0.5,
		x: 0,
		y: -650 // Position it at the top of the wheel
	});
	self.addChild(self.selector);
	// Attach the bigHeart asset to the SpinwheelGame
	self.centerHeartShadow = self.attachAsset('bigHeart', {
		anchorX: 0.5,
		anchorY: 0.5,
		scaleX: 0.75,
		scaleY: 0.75,
		x: 10,
		y: 10,
		alpha: 0.3,
		tint: 0x000000
	});
	self.addChild(self.centerHeartShadow);
	self.centerHeart = self.attachAsset('bigHeart', {
		anchorX: 0.5,
		anchorY: 0.5,
		scaleX: 0.75,
		scaleY: 0.75,
		x: 0,
		y: 0 // Center it in the SpinwheelGame
	});
	self.addChild(self.centerHeart);
	// Create a wheel container
	self.wheel = new Container();
	self.addChild(self.wheel);
	self.start = function () {
		// Iterate over each unlocked generator
		self.wheelSpinning = true;
		self.wheelStopped = false;
		self.wonBonusIndex = 0;
		self.wheel.rotation = Math.random() * Math.PI * 2;
		giftRain.visible = false; // Pause giftrain by making it invisible
		self.nbGeneratorsOnWheel = Math.min(8, unlockedGeneratorIndex);
		for (var i = 0; i <= self.nbGeneratorsOnWheel; i++) {
			// Calculate angle for each bonus
			var angle = 2 * Math.PI / (self.nbGeneratorsOnWheel + 1) * i;
			// Calculate x and y positions based on angle and radius
			var xPos = 500 * Math.cos(angle);
			var yPos = 500 * Math.sin(angle);
			// Create a new gift asset for each unlocked generator
			var giftAsset = self.attachAsset('generator_' + i, {
				anchorX: 0.5,
				anchorY: 0.5,
				x: xPos,
				y: yPos,
				visible: true
			});
			// Add the gift asset to the wheel container
			self.wheel.addChild(giftAsset);
			// Store the gift asset in the bonuses array
			self.bonuses.push(giftAsset);
			// Start rotating the wheel indefinitely
			var _rotateWheel = function rotateWheel() {
				if (!self.wheelSpinning) {
					return;
				}
				tween(self.wheel, {
					rotation: Math.PI * 2
				}, {
					duration: 1000,
					easing: tween.linear,
					onFinish: function onFinish() {
						self.wheel.rotation = 0;
						_rotateWheel();
					}
				});
			};
			_rotateWheel();
		}
		;
	};
	self.update = function () {
		if (self.wheelSpinning && LK.ticks % 10 === 0) {
			LK.getSound('tick').play();
		}
		// Handle slowing down tick sounds when the wheel is stopping
		if (self.isSlowingDown && !self.tickSoundComplete) {
			var elapsedTime = Date.now() - self.slowDownStartTime;
			// Only play tick if we've reached the next scheduled tick time
			if (Date.now() >= self.nextTickTime && elapsedTime < 3000) {
				LK.getSound('tick').play();
				// Calculate the next tick delay - gradually increasing as we approach 3 seconds
				// Start with 100ms between ticks, end with around 500ms between ticks
				var progress = elapsedTime / 3000; // 0 to 1 over 3 seconds
				var nextDelay = 100 + 400 * progress;
				// Schedule the next tick
				self.nextTickTime = Date.now() + nextDelay;
			}
			// Mark as complete after 3 seconds
			if (elapsedTime >= 3000) {
				self.tickSoundComplete = true;
			}
		}
	};
	self.stopWheel = function () {
		if (self.wheelStopped) {
			return;
		}
		log("stopWheel...");
		self.wheelStopped = true; // Set spinning flag to false after stop
		self.wheelSpinning = false; // Set spinning flag to false after stop
		// // Stop the wheel rotation by clearing any active tweens
		// if (self.wheel._activeTween) {
		// 	self.wheel._activeTween.stop();
		// 	self.wheel._activeTween = null;
		// }
		// Initialize variables for the slowing down tick effect
		self.isSlowingDown = true;
		self.slowDownStartTime = Date.now();
		self.nextTickTime = Date.now(); // Start first tick immediately
		self.tickSoundComplete = false;
		// // Optionally, you can set the wheel's rotation to a specific angle if needed
		// self.wheel.rotation = 0;
		tween.stop(self.wheel, {
			rotation: true
		}); // Stop any active rotation tween
		self.wonBonusIndex = Math.floor(Math.random() * (self.nbGeneratorsOnWheel + 1)); // Randomly select a bonus index
		// Calculate the angle for each segment
		var segmentAngle = 2 * Math.PI / (self.nbGeneratorsOnWheel + 1);
		// Calculate the current normalized rotation (between 0 and 2Ο)
		var currentRotation = self.wheel.rotation % (Math.PI * 2);
		if (currentRotation < 0) {
			currentRotation += Math.PI * 2;
		}
		// Calculate the target position for the selected bonus (top position is at 270 degrees or 3Ο/2)
		var targetPosition = Math.PI * 1.5;
		// Calculate the current position of the selected bonus
		var currentBonusPosition = (segmentAngle * self.wonBonusIndex + currentRotation) % (Math.PI * 2);
		// Calculate how much we need to rotate to align the bonus with the top
		var rotationNeeded = targetPosition - currentBonusPosition;
		// Ensure we always rotate forward (add 2Ο if needed)
		if (rotationNeeded < 0) {
			rotationNeeded += Math.PI * 2;
		}
		// Add additional full rotations for a nice effect (2 full rotations)
		rotationNeeded += Math.PI * 4;
		// Calculate the final angle
		var finalAngle = self.wheel.rotation + rotationNeeded;
		log("wonBonusIndex:", self.wonBonusIndex, "segmentAngle:", segmentAngle, "currentRotation:", currentRotation, "currentBonusPosition:", currentBonusPosition, "targetPosition:", targetPosition, "rotationNeeded:", rotationNeeded, "finalAngle:", finalAngle);
		tween(self.wheel, {
			rotation: finalAngle
		}, {
			duration: 3000,
			easing: tween.easeOut,
			onFinish: function onFinish() {
				self.animateWheelWin();
			}
		});
		tween(self.centerHeart, {
			scaleX: 0.7,
			scaleY: 0.7
		}, {
			duration: 300,
			easing: tween.easeIn
		});
		tween(self.centerHeartShadow, {
			scaleX: 0.6,
			scaleY: 0.6
		}, {
			duration: 300,
			easing: tween.easeIn
		});
	};
	self.animateWheelWin = function () {
		if (!self.wheelStopped) {
			return;
		}
		log("animateWheelWin..." + self.wonBonusIndex);
		LK.getSound('tada').play();
		var bonus = self.bonuses[self.wonBonusIndex];
		if (bonus) {
			tween(bonus, {
				scaleX: 40,
				scaleY: 40,
				rotation: -self.wheel.rotation,
				alpha: 0
			}, {
				duration: 3000,
				easing: tween.easeOut,
				onFinish: function onFinish() {
					bonus.visible = false;
					// Offer the cost and buy the generator
					progressManager.buyGenerator(self.wonBonusIndex, true);
					giftRain.visible = true; // Set giftRain visible again
					self.destroy();
				}
			});
			tween(self.backdrop, {
				alpha: 0
			}, {
				duration: 2800,
				easing: tween.easeOut,
				onFinish: function onFinish() {
					isSpinWheelGamePlaying = false;
				}
			});
			// Hide all other bonuses except the one with self.wonBonusIndex
			self.bonuses.forEach(function (b, index) {
				if (index !== self.wonBonusIndex) {
					b.visible = false;
				}
			});
			// Flash the screen white for 500ms
			LK.effects.flashScreen(0xffffff, 500);
			// Hide bigHeart, selector, and rightBoardGraphics
			self.centerHeart.visible = false;
			self.selector.visible = false;
			self.rightBoardGraphics.visible = false;
		}
	};
});
/**** 
* Initialize Game
****/ 
var game = new LK.Game({
	backgroundColor: 0xa16e9f //Init game with black background 
});
/**** 
* Game Code
****/ 
var resetTappedOnce = false;
function resetProgress() {
	// Reset tap count and displayed tap count
	tapCount = "0";
	displayedTapCount = tapCount;
	targetTapCount = tapCount;
	// Reset progress manager properties
	progressManager.money = tapCount;
	progressManager.currentLevel = 0;
	// Reset generators using initializeGenerators
	var resetGenerators = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
	var initResult = initializeGenerators(resetGenerators);
	progressManager.generatorCounts = initResult.generatorCounts;
	progressManager.generators = initResult.generators;
	progressManager.upgrades = {};
	// Destroy and re-create the rightBoard
	if (rightBoard) {
		rightBoard.destroy();
		rightBoard = new RightBoard();
		foregroundContainer.addChild(rightBoard);
		rightBoard.generatorButtons.forEach(function (button, index) {
			if (!button.config) {
				button.config = {
					cost: GENERATORS[Object.keys(GENERATORS)[index]].cost
				};
			}
		});
	}
	// Reset bigHeart properties
	bigHeart.highestScore = 0;
	bigHeart.heartType = 0;
	bigHeart.explosionTriggered = false;
	// Reset storage
	storage.tapCount = tapCount;
	storage.isInfiniteMode = false;
	storage.lastUpdateTime = Date.now();
	storage.highestScore = 0;
	storage.generators = Array.from({
		length: 10
	}, function () {
		return 0;
	});
	// Reset UI elements
	updateTapCountText();
	progressBar.updateProgress(0);
	// Reset game state flags
	isInfiniteMode = false;
	if (resetButton) {
		resetButton.destroy();
	}
	allowSwipe = false;
	unlockedGeneratorIndex = 0;
	// Re-initialize game elements
	bigHeart.initHeart();
	background.changeBackground(0);
	projectionsManager.updateHeartType(bigHeart.heartType);
	bigHeart.visible = true;
	// Reset loveField if it exists
	if (loveField) {
		loveField.destroy();
		loveField = null;
	}
	// Reset giftRain
	if (giftRain) {
		// Clear active gifts and reset generator count
		giftRain.activeGifts = [];
		giftRain.generatorCount = 0;
		// Return all gifts to the pool
		while (giftRain.activeGifts.length > 0) {
			var gift = giftRain.activeGifts.pop();
			gift.visible = false;
			giftRain.giftPool.push(gift);
		}
		// Make giftRain visible again
		giftRain.visible = true;
	}
	background.changeBackground(0);
}
function popMessage(text) {
	messageText.setText(text);
	messageText.visible = true;
	messageText.y = 2850;
	tween(messageText, {
		alpha: 1,
		y: 2500
	}, {
		duration: 500,
		easing: tween.easeInOut,
		onFinish: function onFinish() {
			tween(messageText, {
				alpha: 0,
				y: 2850
			}, {
				duration: 500,
				easing: tween.easeInOut,
				delay: 3000,
				onFinish: function onFinish() {
					messageText.visible = false;
				}
			});
		}
	});
}
// Start updtes 
var messageText; // Global variable for displaying messages
function formatGeneratorPrice(value) {
	if (value >= 1e12) {
		return Math.floor(value / 1e12) + 'T';
	} else if (value >= 1e9) {
		return Math.floor(value / 1e9) + 'B';
	} else if (value >= 1e6) {
		return Math.floor(value / 1e6) + 'M';
	} else if (value >= 1e3) {
		return Math.floor(value / 1e3) + 'K';
	} else {
		return value.toString();
	}
}
// Global event listener for mouse or touch up
game.up = function (x, y, obj) {
	dragNode = null; // Reset dragNode when mouse is up
};
function shakeMiddleground() {
	tween(middlegroundContainer, {
		x: 10,
		y: 10
	}, {
		duration: 100,
		easing: tween.easeInOut,
		onFinish: function onFinish() {
			tween(middlegroundContainer, {
				x: -10,
				y: -10
			}, {
				duration: 100,
				easing: tween.easeInOut,
				onFinish: function onFinish() {
					tween(middlegroundContainer, {
						x: 0,
						y: 0
					}, {
						duration: 100,
						easing: tween.easeInOut
					});
				}
			});
		}
	});
}
function shakeScreen() {
	var shakeCount = 0;
	function performShake() {
		if (shakeCount >= 10) {
			return;
		} // Stop after 10 loops
		tween(game, {
			x: Math.random() * 20 - 10,
			y: 0 //Math.random() * 20 - 10
		}, {
			duration: 100,
			easing: tween.easeInOut,
			onFinish: function onFinish() {
				tween(game, {
					x: Math.random() * 20 - 10,
					y: 0 //Math.random() * 20 - 10
				}, {
					duration: 100,
					easing: tween.easeInOut,
					onFinish: function onFinish() {
						tween(game, {
							x: 0,
							y: 0
						}, {
							duration: 100,
							easing: tween.easeInOut,
							onFinish: function onFinish() {
								shakeCount++;
								performShake(); // Recursive call for next shake
							}
						});
					}
				});
			}
		});
	}
	performShake();
}
var isSpinWheelGamePlaying = false;
var isDebug = false;
var SWIPE_THRESHOLD = 10; // Threshold in pixels to distinguish between taps and swipes
var TAP_DETECT_DELAY = 600;
var TAPS_PER_LEVEL = {
	0: 99,
	1: 999,
	2: 9999,
	3: 99999,
	4: 999999,
	5: 9999999,
	6: 99999999,
	7: 999999999,
	8: 9999999999,
	9: 99999999999
};
if (isDebug) {
	TAPS_PER_LEVEL = {
		0: 12,
		1: 50,
		2: 100,
		3: 200,
		4: 300,
		5: 400,
		6: 500,
		7: 600,
		8: 700,
		9: 10000000000
	};
}
// Constants for Generators and Upgrades
var GENERATORS = {
	ROSE: {
		id: 0,
		name: "Rose",
		description: "A charming rose that generates a few love beats",
		autoClick: true,
		clickRate: 1,
		cost: 10,
		upgradeLevel: 0
	},
	CHOCLATE: {
		id: 1,
		name: "Choclate",
		description: "A sweet choclate that generates love faster",
		autoClick: true,
		clickRate: 5,
		cost: 50,
		upgradeLevel: 0
	},
	CHAIN_BRACELET: {
		id: 2,
		name: "Chain Bracelet",
		description: "A beautiful chain bracelet that generates love even faster",
		autoClick: true,
		clickRate: 10,
		cost: 200,
		upgradeLevel: 0
	},
	PERFUME: {
		id: 3,
		name: "Perfume",
		description: "A delightful fragrance that enchants love",
		autoClick: true,
		clickRate: 20,
		cost: 500,
		upgradeLevel: 0
	},
	WATCH: {
		id: 4,
		name: "Watch",
		description: "A timeless piece that speeds up love generation",
		autoClick: true,
		clickRate: 50,
		cost: 1000,
		upgradeLevel: 0
	},
	JEWELRY_SET: {
		id: 5,
		name: "Jewelry Set",
		description: "A luxurious set that dazzles with love",
		autoClick: true,
		clickRate: 100,
		cost: 10000,
		upgradeLevel: 0
	},
	SPORTS_CAR: {
		id: 6,
		name: "Sport Car",
		description: "A fast car that accelerates love generation",
		autoClick: true,
		clickRate: 200,
		cost: 100000,
		upgradeLevel: 0
	},
	VILLA: {
		id: 7,
		name: "Villa",
		description: "A grand villa that generates love in abundance",
		autoClick: true,
		clickRate: 500,
		cost: 1000000,
		upgradeLevel: 0
	},
	PRIVATE_JET: {
		id: 8,
		name: "Private Jet",
		description: "A jet that takes love to new heights",
		autoClick: true,
		clickRate: 1000,
		cost: 1000000000,
		upgradeLevel: 0
	},
	INFINITE_LOVE: {
		id: 9,
		name: "Infinite Love",
		description: "An endless source of love",
		autoClick: true,
		clickRate: 654321,
		cost: 1000000000000,
		upgradeLevel: 0
	}
};
if (isDebug) {
	// Set debug cost for Generators
	GENERATORS.ROSE.cost = 10;
	GENERATORS.CHOCLATE.cost = 110;
	GENERATORS.CHAIN_BRACELET.cost = 220;
	GENERATORS.PERFUME.cost = 330;
	GENERATORS.WATCH.cost = 440;
	GENERATORS.JEWELRY_SET.cost = 550;
	GENERATORS.SPORTS_CAR.cost = 660;
	GENERATORS.VILLA.cost = 770;
	GENERATORS.PRIVATE_JET.cost = 880;
	GENERATORS.INFINITE_LOVE.cost = 1000;
}
var UPGRADES = {
	LOVE_FILTER: {
		id: 0,
		name: "Love Filter",
		description: "A powerful love filter that make you irresistible",
		targetGenerator: 1,
		// Targets Generator #1 (Me)
		multipliers: [2, 4, 8],
		// Levels of multiplier
		cost: 12
	}
};
var MAX_DISPLAY_NUMBER = "9999999999999999"; //9 999 999 999 999 999
///////////////////////////////////////////////////////// GLOBAL VARIABLES //////////////////////////////////////////////////////////// 
///////////////////////////////////////////////////////// GLOBAL VARIABLES //////////////////////////////////////////////////////////// 
///////////////////////////////////////////////////////// GLOBAL VARIABLES //////////////////////////////////////////////////////////// 
var maxGenerators = 9;
var nbHearts = 10;
var tapCount;
var displayedTapCount; // For smooth animation
var targetTapCount; // Target value for animation
var isAnimatingCount = false; // Flag to track if count animation is in progress
var background;
var backgroundContainer;
var giftsContainer;
var middlegroundContainer;
var foregroundContainer;
var giftRain;
var tapCountText;
var progressManager;
var projectionsManager;
var rightBoard;
var bigHeart;
var loveField;
var progressBar;
var resetButton;
var isInfiniteMode = false;
var fisrtBonusDeliveryDelay = 20000; // Fisrt giftbox delay
var allowSwipe = false; // Global flag to control swipe functionality
// Declare a global variable to store the startY position for GeneratorButton events
var globalStartY = 0;
// Declare a global variable to track the node being dragged
var dragNode = null;
var isSwipingButtons = false; // Global flag to track swipe/animation state
var unlockedGeneratorIndex = 0; // Track the index of the last unlocked generator
function log() {
	if (isDebug) {
		console.log.apply(console, arguments);
	}
}
function updateTapCountText() {
	tapCountText.setText('LOVE\r\n' + displayedTapCount);
}
// Function to animate the tap count smoothly
function animateTapCount() {
	if (!isAnimatingCount || displayedTapCount === targetTapCount) {
		isAnimatingCount = false;
		return;
	}
	// Determine if we need to increase or decrease the displayed count
	var isIncreasing = compareNumbers(targetTapCount, displayedTapCount) > 0;
	// Calculate the difference between current and target
	var diff = isIncreasing ? subtractLargeNumbers(targetTapCount, displayedTapCount) : subtractLargeNumbers(displayedTapCount, targetTapCount);
	if (diff === "0") {
		displayedTapCount = targetTapCount; // Ensure exact match at the end
		updateTapCountText();
		isAnimatingCount = false;
		return;
	}
	// Determine step size based on difference magnitude
	var stepSize = "1";
	var diffLength = diff.length;
	// Use a more gradual step size calculation for smoother animation
	if (diffLength > 5) {
		// For very large differences
		stepSize = "1" + "0".repeat(diffLength - 3);
	} else if (diffLength > 3) {
		// For medium differences
		stepSize = "1" + "0".repeat(diffLength - 3);
	} else if (diffLength > 2) {
		// For hundreds
		stepSize = "5";
	} else {
		// For small differences (1-99)
		stepSize = "1";
	}
	// Update the displayed count
	if (isIncreasing) {
		displayedTapCount = addLargeNumbers(displayedTapCount, stepSize);
	} else {
		displayedTapCount = subtractLargeNumbers(displayedTapCount, stepSize);
	}
	// Update the text
	updateTapCountText();
	// Continue animation in the next frame - use a faster frame rate for smoother animation
	LK.setTimeout(animateTapCount, 10); // ~100fps for smoother animation
}
function compareNumbers(a, b) {
	a = a.toString();
	b = b.toString();
	if (a.length < b.length) {
		return -1;
	}
	if (a.length > b.length) {
		return 1;
	}
	return a < b ? -1 : a > b ? 1 : 0;
}
function addLargeNumbers(a, b) {
	// Convert numbers to strings if they aren't already
	a = a.toString();
	b = b.toString();
	var result = '';
	var carry = 0;
	// Pad the shorter number with zeros
	while (a.length < b.length) {
		a = '0' + a;
	}
	while (b.length < a.length) {
		b = '0' + b;
	}
	// Add digits from right to left
	for (var i = a.length - 1; i >= 0; i--) {
		var sum = parseInt(a[i]) + parseInt(b[i]) + carry;
		carry = Math.floor(sum / 10);
		result = sum % 10 + result;
	}
	if (carry) {
		result = carry + result;
	}
	return result;
}
// Helper function to subtract large numbers as strings
function subtractLargeNumbers(a, b) {
	// Convert numbers to strings if they aren't already
	a = a.toString();
	b = b.toString();
	// If b is bigger than a, result would be negative
	if (b.length > a.length || b.length === a.length && b > a) {
		return "0";
	}
	var result = '';
	var borrow = 0;
	// Pad b with leading zeros to match a's length
	while (b.length < a.length) {
		b = '0' + b;
	}
	// Subtract digits from right to left
	for (var i = a.length - 1; i >= 0; i--) {
		var digitA = parseInt(a[i]);
		var digitB = parseInt(b[i]);
		// Handle borrowing
		if (borrow) {
			digitA--;
			borrow = 0;
		}
		// Need to borrow from next digit
		if (digitA < digitB) {
			digitA += 10;
			borrow = 1;
		}
		result = digitA - digitB + result;
	}
	// Remove leading zeros
	result = result.replace(/^0+/, '');
	return result || "0";
}
// Global ProgressManager
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 _classCallCheck(a, n) {
	if (!(a instanceof n)) {
		throw new TypeError("Cannot call a class as a function");
	}
}
function _defineProperties(e, r) {
	for (var t = 0; t < r.length; t++) {
		var o = r[t];
		o.enumerable = o.enumerable || !1, o.configurable = !0, "value" in o && (o.writable = !0), Object.defineProperty(e, _toPropertyKey(o.key), o);
	}
}
function _createClass(e, r, t) {
	return r && _defineProperties(e.prototype, r), t && _defineProperties(e, t), Object.defineProperty(e, "prototype", {
		writable: !1
	}), 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);
}
// Progress Management
function ProgressManager() {
	var self = this;
	self.maxHeartBeatsPerSecond = 8; // Add maxHeartBeatsPerSecond property
	self.tapMultiplier = 1; // Initialize tapMultiplier property
	self.priceIncreaseRate = 1.33; // Extracted price increase rate
	self.money = tapCount;
	self.generators = {};
	self.generatorCounts = {}; // Add a counter for each generator
	self.upgrades = {};
	self.currentTime = Date.now();
	self.currentLevel = 0;
	self.tapsPerLevel = TAPS_PER_LEVEL;
	self.lastUpdateTime = self.currentTime;
	self.updateGame = function () {
		if (isSpinWheelGamePlaying) {
			return;
		}
		if (isInfiniteMode) {
			return;
		}
		//log("ProgressManager updateGame...");
		var now = Date.now();
		var deltaTime = now - self.lastUpdateTime;
		var tempGenerated = 0;
		// Update generators
		Object.values(self.generators).forEach(function (generator) {
			var generated = generator.generate(deltaTime) * self.generatorCounts[generator.id];
			//log("generator => +" + generated);
			tempGenerated += Math.ceil(generated);
		});
		// Calculate beats based on generation rate
		var beatsToGenerate = Math.floor(tempGenerated / 2); // One beat every 10 taps generated
		beatsToGenerate = Math.min(beatsToGenerate, self.maxHeartBeatsPerSecond); // Cap at 5 beats per update to avoid overwhelming
		if (tempGenerated > 0) {
			self.money = addLargeNumbers(self.money, tempGenerated.toString());
			tapCount = self.money;
			targetTapCount = tapCount; // Update target for animation
			if (!isAnimatingCount) {
				isAnimatingCount = true;
				animateTapCount();
			}
		}
		bigHeart.highestScore = Math.max(bigHeart.highestScore, parseInt(tapCount)); // Update highestScore
		self.lastUpdateTime = now;
		// Save game status
		storage.tapCount = tapCount;
		storage.isInfiniteMode = isInfiniteMode;
		storage.lastUpdateTime = self.lastUpdateTime;
		storage.highestScore = bigHeart.highestScore; // Store highestScore
		storage.generators = Array.from({
			length: 10
		}, function (_, i) {
			return self.generatorCounts[i] || 0;
		});
		// Update progress bar based on current level progress
		if (!isInfiniteMode) {
			var startTap = self.currentLevel === 0 ? 0 : TAPS_PER_LEVEL[self.currentLevel - 1];
			var endTap = TAPS_PER_LEVEL[self.currentLevel];
			var progress = Math.min(1, (parseInt(tapCount) - startTap) / (endTap - startTap));
			progressBar.updateProgress(progress);
		}
		self.updateGeneratorsUi();
		// Trigger multiple beats based on generation rate
		if (tempGenerated > 0) {
			for (var i = 0; i < beatsToGenerate; i++) {
				LK.setTimeout(function () {
					bigHeart.animateBeat();
				}, i * 300);
			}
			bigHeart.animateFrames();
		}
		self.checkProgress(); // Check the progress
	};
	self.manualGeneration = function () {
		tapCount = addLargeNumbers(tapCount, self.tapMultiplier.toString());
		self.money = addLargeNumbers(self.money, self.tapMultiplier.toString());
		LK.getSound('pop').play(); // Play pop sound for manualGeneration
		log("manualGeneration Tap count: ", tapCount); // Log the tap count
		targetTapCount = tapCount; // Update target for animation
		if (!isAnimatingCount) {
			isAnimatingCount = true;
			animateTapCount();
		}
		bigHeart.highestScore = Math.max(bigHeart.highestScore, parseInt(tapCount)); // Update highestScore
		self.updateGeneratorsUi();
		self.checkProgress(); // Check for level up immediately
		// Update progress bar based on current level progress
		var startTap = self.currentLevel === 0 ? 0 : TAPS_PER_LEVEL[self.currentLevel - 1];
		var endTap = TAPS_PER_LEVEL[self.currentLevel];
		var progress = Math.min(1, (parseInt(tapCount) - startTap) / (endTap - startTap));
		progressBar.updateProgress(progress);
		if (!giftBoxManager.isStarted && unlockedGeneratorIndex >= 1) {
			giftBoxManager.isStarted = true;
			LK.setTimeout(function () {
				giftBoxManager.spawnGiftBox();
				giftBoxManager.start();
			}, fisrtBonusDeliveryDelay);
		}
	};
	self.checkProgress = function () {
		if (bigHeart.highestScore >= self.tapsPerLevel[self.currentLevel]) {
			if (self.currentLevel < 9) {
				// Ensure we don't exceed level 9
				var previousLevel = self.currentLevel;
				var previousFrames = bigHeart.heartFrames[previousLevel] || [];
				previousFrames.forEach(function (frame) {
					if (frame && frame._activeTween) {
						frame._activeTween.stop();
						frame._activeTween = null;
					}
					frame.visible = false;
				});
				background.changeBackground(progressManager.currentLevel + 1); // Change background when level changes
				bigHeart.animateExplosion(function () {
					self.currentLevel++;
					bigHeart.heartType = self.currentLevel;
					bigHeart.currentGraphic = bigHeart.heartFrames[bigHeart.heartType][5];
					bigHeart.nextGraphic = bigHeart.heartFrames[bigHeart.heartType][4];
					bigHeart.currentGraphic.visible = true;
					bigHeart.nextGraphic.visible = true;
					projectionsManager.updateHeartType(bigHeart.heartType);
					tween(bigHeart.currentGraphic, {
						scaleX: 1.1,
						scaleY: 1.1
					}, {
						duration: 300,
						easing: tween.easeOut
					});
					tween(bigHeart.nextGraphic, {
						scaleX: 1.1,
						scaleY: 1.1
					}, {
						duration: 300,
						easing: tween.easeOut
					});
				});
			} else {
				isInfiniteMode = true;
				bigHeart.visible = false; // Hide the BigHeart
				giftRain.visible = false; // Hide the GiftRain
				progressBar.visible = false;
				// Play tada sound
				LK.getSound('tada').play();
				LK.playMusic('infiniteMusic');
				// Animate rightBoard to the right border of the screen
				tween(rightBoard, {
					x: 2048 + rightBoard.width // Move to the right border
				}, {
					duration: 500,
					// Duration of the animation
					easing: tween.easeOut // Easing function for smooth effect
				});
				// Change background to 10
				background.changeBackground(10);
				loveField = new LoveField();
				middlegroundContainer.addChild(loveField);
				if (!resetButton) {
					LK.setTimeout(function () {
						resetButton = new ResetButton();
						resetButton.x = 120;
						resetButton.y = 2650;
						foregroundContainer.addChild(resetButton);
					}, 10000);
				}
			}
		}
	};
	self.computeIdleGeneration = function () {
		if (isInfiniteMode) {
			return;
		}
		var now = Date.now();
		var deltaTime = now - storage.lastUpdateTime; // Use storage lastUpdateTime here
		var tempGenerated = 0;
		// Update generators
		Object.values(self.generators).forEach(function (generator) {
			var generated = generator.generate(deltaTime) * self.generatorCounts[generator.id];
			log("generator => +" + generated);
			tempGenerated += Math.ceil(generated);
		});
		if (tempGenerated > 0) {
			self.money = addLargeNumbers(self.money, tempGenerated.toString());
			tapCount = self.money;
		}
		self.lastUpdateTime = now;
		storage.lastUpdateTime = now;
	};
	self.buyGenerator = function (generatorId, isFree) {
		var generatorConfig = Object.values(GENERATORS).find(function (g) {
			return g.id === generatorId;
		});
		if (!generatorConfig) {
			log("Generator not found");
			return false;
		}
		// Check if player has enough money
		if (!isFree && parseInt(tapCount) < generatorConfig.cost) {
			log("Not enough money");
			return false;
		}
		// Initialize generator if it doesn't exist
		if (!self.generators[generatorId]) {
			self.generators[generatorId] = new Generator(generatorConfig);
			self.generatorCounts[generatorId] = 0;
		}
		// Subtract cost using subtractLargeNumbers
		if (!isFree) {
			self.money = subtractLargeNumbers(self.money, generatorConfig.cost.toString());
			tapCount = self.money;
			// Update displayed count immediately without animation for price reductions
			displayedTapCount = tapCount;
			targetTapCount = tapCount;
			updateTapCountText();
		}
		// Calculate new cost
		var currentCount = self.generatorCounts[generatorId] || 0;
		var baseCost = GENERATORS[Object.keys(GENERATORS)[generatorId]].cost;
		var newCost = Math.floor(baseCost * Math.pow(self.priceIncreaseRate, currentCount + 1));
		log("Updating costs - Base:", baseCost, "Count:", currentCount, "New Cost:", newCost);
		// Update costs
		//generatorConfig.cost = newCost;
		if (!isFree) {
			rightBoard.generatorButtons[generatorId].config.cost = newCost;
		}
		self.generatorCounts[generatorId]++; // Increment the count for the generator 
		rightBoard.generatorButtons[generatorId].countText.setText(self.generatorCounts[generatorId].toString());
		log("Generator", generatorId, "Count:", self.generatorCounts[generatorId], "New Cost:", newCost);
		LK.getSound('buyGenerator').play();
		popMessage(generatorConfig.name + "\r\n+" + generatorConfig.clickRate + " love/sec");
		giftRain.increaseSpawnRate(generatorId);
		if (generatorId === GENERATORS.INFINITE_LOVE.id) {
			isInfiniteMode = true;
			bigHeart.visible = false; // Hide the BigHeart
			giftRain.visible = false; // Hide the GiftRain
			// Play tada sound
			LK.getSound('tada').play();
			LK.playMusic('infiniteMusic');
			// Animate rightBoard to the right border of the screen
			tween(rightBoard, {
				x: 2048 + rightBoard.width // Move to the right border
			}, {
				duration: 500,
				// Duration of the animation
				easing: tween.easeOut // Easing function for smooth effect
			});
			// Change background to 10
			background.changeBackground(10);
			loveField = new LoveField();
			middlegroundContainer.addChild(loveField);
		}
		// Update the tapMultiplier based on the generator index
		if (!isFree) {
			self.tapMultiplier = Math.max(self.tapMultiplier, Math.pow(2, generatorId + 1));
		}
		return true;
	};
	self.buyUpgrade = function (upgradeId, generatorId) {
		var upgradeConfig = Object.values(UPGRADES).find(function (u) {
			return u.id === upgradeId;
		});
		var targetGenerator = self.generators[generatorId];
		if (!upgradeConfig || !targetGenerator) {
			throw new Error("Upgrade or Generator not found");
		}
		if (parseInt(tapCount) < upgradeConfig.cost) {
			return false;
		}
		// Subtract cost using subtractLargeNumbers
		self.money = subtractLargeNumbers(self.money, upgradeConfig.cost.toString());
		tapCount = self.money;
		// Update displayed count immediately without animation for price reductions
		displayedTapCount = tapCount;
		targetTapCount = tapCount;
		updateTapCountText();
		var upgrade = new Upgrade(upgradeConfig);
		upgrade.apply(targetGenerator);
		self.upgrades[upgradeId] = upgrade;
		return true;
	};
	self.updateGeneratorsUi = function () {
		// Update generator button visibility based on money
		//log("Updating Generators UI");
		rightBoard.generatorButtons.forEach(function (button, index) {
			var generatorConfig = rightBoard.generatorButtons[index].config;
			rightBoard.generatorButtons[index].costText.setText(formatGeneratorPrice(generatorConfig.cost)); // Update the cost text to reflect the new exponential cost
			if (generatorConfig) {
				// Store a flag 'wasShown' in generatorButton when displayed
				if (parseInt(tapCount) >= generatorConfig.cost * 0.75) {
					if (!button.wasShown) {
						button.x = 2048 + button.width; // Start from the right border
						tween(button, {
							x: 0
						}, {
							duration: 500,
							easing: tween.easeOut
						}); // Animate entrance
						// Enable swipe when generator 5 becomes visible
						if (!allowSwipe && index === 5) {
							allowSwipe = true;
						}
						unlockedGeneratorIndex = Math.max(index, unlockedGeneratorIndex);
					}
					// Show generator if player has at least 75% of its cost
					button.wasShown = true;
					button.visible = true;
					// Set alpha to 0.75 if player can't buy the generator
					button.alpha = parseInt(tapCount) >= generatorConfig.cost ? 1 : 0.6;
				} else {
					button.visible = button.wasShown;
					button.alpha = 0.6;
					// If the button has been moved out of its normal position, tween it back to x = 0
					if (button.x !== 0) {
						tween(button, {
							x: 0
						}, {
							duration: 200,
							easing: tween.easeOut
						});
					}
				}
			} else {
				button.visible = false;
			}
		});
	};
}
function initializeGenerators(storedGenerators) {
	var result = {
		generatorCounts: {},
		generators: {}
	};
	if (Array.isArray(storedGenerators)) {
		for (var index = 0; index < storedGenerators.length; index++) {
			log("Adding loaded generator ", index, " => ", storedGenerators[index]);
			var count = storedGenerators[index];
			result.generatorCounts[index] = count;
			// Initialize generator if count > 0
			if (count > 0) {
				var generatorKey = Object.keys(GENERATORS)[index];
				var config = GENERATORS[generatorKey];
				config.id = index;
				result.generators[index] = new Generator(config);
			}
		}
	} else {
		log("No previous generators");
	}
	return result;
}
function Generator(config) {
	var self = this;
	self.id = config.id;
	self.name = config.name;
	self.description = config.description;
	self.autoClick = config.autoClick;
	self.clickRate = config.clickRate;
	self.cost = config.cost;
	self.upgradeLevel = config.upgradeLevel;
	self.generate = function (deltaTime) {
		if (!self.autoClick) {
			return 0;
		}
		var clickAmount = self.clickRate * deltaTime / 1000;
		return clickAmount * Math.pow(2, self.upgradeLevel);
	};
	self.currentMultiplier = Math.pow(2, self.upgradeLevel);
	self.manualGenerate = function () {
		return 1 * self.currentMultiplier;
	};
	self.upgrade = function (upgradeMultiplier) {
		self.upgradeLevel++;
	};
}
function Upgrade(config) {
	var self = this;
	self.id = config.id;
	self.name = config.name;
	self.description = config.description;
	self.targetGenerator = config.targetGenerator;
	self.multipliers = config.multipliers;
	self.cost = config.cost;
	self.currentLevel = 0;
	self.apply = function (generator) {
		if (self.currentLevel < self.multipliers.length) {
			generator.upgrade(self.multipliers[self.currentLevel]);
			self.currentLevel++;
		}
	};
}
function initializeGame() {
	tapCount = storage.tapCount || "0";
	tapCount = isDebug ? 0 : tapCount;
	displayedTapCount = tapCount; // Initialize displayed count
	targetTapCount = tapCount; // Initialize target count
	isInfiniteMode = storage.isInfiniteMode || false;
	//storage.isInfiniteMode = isInfiniteMode = false;
	//storage.isInfiniteMode = isInfiniteMode = true;
	var storedGenerators = storage.generators || [0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
	log("Loaded tapCount from storage:", tapCount);
	log("Loaded isInfiniteMode from storage:", isInfiniteMode);
	log("Loaded generators from storage:", storedGenerators);
	bigHeart = new BigHeart();
	backgroundContainer = new Container();
	giftsContainer = new Container();
	middlegroundContainer = new Container();
	foregroundContainer = new Container();
	game.addChild(backgroundContainer);
	game.addChild(giftsContainer);
	game.addChild(middlegroundContainer);
	game.addChild(foregroundContainer);
	background = new Background(); // Create a Background instance
	backgroundContainer.addChild(background); // Add Background instance to the backgroundContainer
	projectionsManager = backgroundContainer.addChild(new Projections()); // Place projectionsManager in backgroundContainer
	progressManager = new ProgressManager();
	//progressManager.lastUpdateTime = storage.lastUpdateTime || Date.now();
	var initResult = initializeGenerators(storedGenerators);
	progressManager.generatorCounts = initResult.generatorCounts;
	progressManager.generators = initResult.generators;
	progressManager.computeIdleGeneration(); // Calculate offline earnings
	// Determine heart type based on tapCount
	for (var level = 9; level > 0; level--) {
		// 0 < 50  < 99 => 0
		// 99 < 110 < 999 => 1
		if (tapCount >= TAPS_PER_LEVEL[level - 1]) {
			progressManager.currentLevel = level;
			break;
		}
	}
	// Initialize gift rain with existing generator counts
	giftRain = new GiftRain();
	giftsContainer.addChild(giftRain);
	giftBoxManager = new GiftBoxManager();
	log("unlockedGeneratorIndex=", unlockedGeneratorIndex);
	if (unlockedGeneratorIndex >= 1) {
		LK.setTimeout(function () {
			giftBoxManager.isStarted = true;
			giftBoxManager.spawnGiftBox();
			giftBoxManager.start();
		}, fisrtBonusDeliveryDelay);
	}
	middlegroundContainer.addChild(giftBoxManager);
	for (var generatorId in progressManager.generatorCounts) {
		var count = progressManager.generatorCounts[generatorId];
		log("Initializing gifts for generator", generatorId, "count:", count);
		for (var i = 0; i < count; i++) {
			giftRain.increaseSpawnRate(parseInt(generatorId));
		}
	}
	background.changeBackground(progressManager.currentLevel);
	// Add a big heart at the center of the screen
	bigHeart.initHeart();
	if (!isInfiniteMode) {
		LK.playMusic('bgMusic');
	} else {
		if (tapCountText) {
			tapCountText.setText('LOVE\\r\\nβ');
		}
	}
	middlegroundContainer.addChild(bigHeart);
	projectionsManager.updateHeartType(bigHeart.heartType);
	progressBar = new ProgressBar();
	foregroundContainer.addChild(progressBar);
	// Add a RightBoard instance to the foreground container
	rightBoard = new RightBoard();
	foregroundContainer.addChild(rightBoard);
	rightBoard.generatorButtons.forEach(function (button, index) {
		var count = progressManager.generatorCounts[index] || 0;
		var baseCost = GENERATORS[Object.keys(GENERATORS)[index]].cost;
		var newCost = Math.floor(baseCost * Math.pow(progressManager.priceIncreaseRate, count));
		button.config = {
			count: count,
			cost: newCost
		};
	});
	// Update generator counts in UI
	for (var genId in progressManager.generatorCounts) {
		if (progressManager.generatorCounts[genId] > 0) {
			rightBoard.generatorButtons[genId].countText.setText(progressManager.generatorCounts[genId].toString());
		}
	}
	// Create a text object to display tapCount
	tapCountText = new Text2('LOVE\r\n ', {
		size: 140,
		fill: 0xFFFFFF,
		dropShadow: true,
		align: 'center'
	});
	// Center the text horizontally, anchor point set at the middle of its top edge.
	tapCountText.anchor.set(0.5, 0);
	// Position the text at the top-center of the screen.
	LK.gui.top.addChild(tapCountText);
	if (isInfiniteMode) {
		tapCountText.setText('LOVE\r\nβ');
	}
	// Create a text object to display messages
	messageText = new Text2('', {
		size: 100,
		fill: 0xFFFFFF,
		dropShadow: true,
		align: 'center'
	});
	messageText.anchor.set(0.5, 0);
	messageText.x = 1024;
	messageText.y = 2500;
	foregroundContainer.addChild(messageText);
	if (isInfiniteMode) {
		resetButton = new ResetButton();
		resetButton.x = 120;
		resetButton.y = 2650;
		foregroundContainer.addChild(resetButton);
	}
	// Start updtes 
	bigHeart.down();
	if (tapCount >= 10) {
		popMessage("Welcome back my love!");
	} else {
		popMessage("Welcome my love!");
	}
	var intervalId = LK.setInterval(function () {
		progressManager.updateGame();
	}, 1000);
	checkInfiniteMode();
}
function checkInfiniteMode() {
	if (isInfiniteMode) {
		bigHeart.visible = false; // Hide the BigHeart
		giftRain.visible = false; // Hide the GiftRain
		// Play tada sound
		LK.getSound('tada').play();
		LK.playMusic('infiniteMusic');
		// Animate rightBoard to the right border of the screen
		tween(rightBoard, {
			x: 2048 + rightBoard.width // Move to the right border
		}, {
			duration: 500,
			// Duration of the animation
			easing: tween.easeOut // Easing function for smooth effect
		});
		// Change background to 10
		background.changeBackground(10);
		loveField = new LoveField();
		middlegroundContainer.addChild(loveField);
		// Update tapCountText to display 'LOVE\\r\\nβ'
		tapCountText.setText('LOVE\r\nβ');
	}
}
initializeGame(); /**** 
* Plugins
****/ 
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/**** 
* Classes
****/ 
var Background = Container.expand(function () {
	var self = Container.call(this);
	// Attach the background_1 asset to the class
	self.backgrounds = [];
	for (var i = 0; i <= 10; i++) {
		var background = self.attachAsset('background_' + i, {
			anchorX: 0.5,
			anchorY: 0.5,
			index: i,
			visible: false
		});
		self.backgrounds.push(background);
	}
	self.backgrounds[0].visible = true; // Set the initial background visible
	// Position the background at the center of the screen
	self.x = 2048 / 2;
	self.y = 2732 / 2;
	// Function to change the background based on the index
	self.changeBackground = function (index) {
		var currentBg = self.backgrounds.find(function (bg) {
			return bg.visible;
		});
		if (index > 10) {
			// Ignore changes on background 10
			return;
		}
		var newBg = self.backgrounds[index];
		if (newBg && currentBg !== newBg) {
			tween(currentBg, {
				alpha: 0
			}, {
				duration: 500,
				easing: tween.easeOut,
				onFinish: function onFinish() {
					currentBg.visible = false;
					newBg.alpha = 0;
					newBg.visible = true;
					tween(newBg, {
						alpha: 1
					}, {
						duration: 500,
						easing: tween.easeIn
					});
				}
			});
		}
	};
});
// Create a class for bigHeart
var BigHeart = Container.expand(function () {
	var self = Container.call(this);
	self.currentGraphic = null;
	self.nextGraphic = null;
	self.tapLimit = TAPS_PER_LEVEL[0]; // Initialize tap limit using TAPS_PER_LEVEL
	self.nbTapsPerFrame = self.tapLimit / 6; // Initialize number of taps per frame
	self.heartType = 0; // Initialize tap counter
	self.explosionTriggered = false; // Initialize explosion flag
	self.highestScore = 0; // Initialize highestScore property
	// Attach the bigHeart asset to the class
	var bigHeartGraphics = self.attachAsset('bigHeart', {
		anchorX: 0.5,
		anchorY: 0.5,
		alpha: 0.1
	});
	self.heartFrames = {}; // Initialize heartFrames as a property of BigHeart class
	for (var type = 9; type >= 0; type--) {
		self.heartFrames[type] = [];
		for (var i = 5; i >= 0; i--) {
			self.heartFrames[type][5 - i] = self.attachAsset('heart_' + type + '_frame_' + i, {
				anchorX: 0.5,
				anchorY: 0.5,
				scaleX: 0.9,
				scaleY: 0.9,
				heartType: type,
				index: 5 - i,
				visible: true //!i
			});
		}
	}
	log("Setting frames in constructor...");
	self.currentGraphic = self.heartFrames[self.heartType][5];
	self.nextGraphic = self.heartFrames[self.heartType][4];
	if (self.currentGraphic) {
		if (self.currentGraphic && self.currentGraphic.scaleX !== undefined) {
			self.currentGraphic.scaleX = 1.1;
		}
		if (self.currentGraphic && self.currentGraphic.scaleY !== undefined) {
			self.currentGraphic.scaleY = 1.1;
		}
		//self.currentGraphic.visible = true;
	}
	if (self.nextGraphic) {
		if (self.nextGraphic.scaleX !== undefined) {
			self.nextGraphic.scaleX = 1.1;
		}
		if (self.nextGraphic.scaleY !== undefined) {
			self.nextGraphic.scaleY = 1.1;
		}
		//self.nextGraphic.visible = true;
	}
	// Position the bigHeart at the center of the screen
	self.x = 2048 / 2;
	self.y = 2732 / 2 - 300;
	// Define baseWidth and baseHeight
	var baseWidth = bigHeartGraphics.width;
	var baseHeight = bigHeartGraphics.height;
	// Event handler called when a press happens on element. This is automatically called on press if bigHeart is attached.
	self.down = function (x, y, obj) {
		// Log the down event
		log("Down event triggered on BigHeart");
		if (self.currentGraphic && self.nextGraphic) {
			log("Current indexes:: ", self.currentGraphic.index, ',', self.nextGraphic.index); // Log the tap count
		} else {
			log("CurrentGraphic or NextGraphic is not initialized.");
		}
		// Increment tap counter
		progressManager.manualGeneration();
		self.highestScore = Math.max(self.highestScore, parseInt(tapCount)); // Update highestScore
		self.animateBeat();
		self.animateFrames();
		// Create a new heart projection using the current frame index
		if (self.currentGraphic) {
			projectionsManager.popHearts(self.currentGraphic.heartType);
		} else {
			log("CurrentGraphic is not initialized.");
		}
		shakeMiddleground();
	};
	// Beat Animation
	self.animateBeat = function () {
		if (self.explosionTriggered) {
			return;
		}
		// Play beat sound
		LK.getSound('bump').play();
		if (self.currentGraphic) {
			self.currentGraphic._activeTween = tween(self.currentGraphic, {
				scaleX: 1.2,
				scaleY: 1.2,
				x: 0
			}, {
				duration: 100,
				onFinish: function onFinish() {
					if (self.currentGraphic) {
						delete self.currentGraphic._activeTween;
						tween(self.currentGraphic, {
							scaleX: 1.1,
							scaleY: 1.1,
							x: 0
						}, {
							duration: 100
						});
					}
				}
			});
		}
		if (self.nextGraphic && !self.nextGraphic._activeTween) {
			self.nextGraphic._activeTween = tween(self.nextGraphic, {
				duration: 250,
				onFinish: function onComplete() {
					if (self.nextGraphic) {
						delete self.nextGraphic._activeTween;
						tween(self.nextGraphic, {
							scaleX: 1.2,
							scaleY: 1.2,
							x: 0
						}, {
							duration: 100,
							onFinish: function onFinish() {
								if (self.nextGraphic) {
									delete self.nextGraphic._activeTween;
									tween(self.nextGraphic, {
										scaleX: 1.1,
										scaleY: 1.1,
										x: 0
									}, {
										duration: 100
									});
								}
							}
						});
					}
				}
			});
		}
	};
	// Frames Animation
	self.animateFrames = function () {
		if (self.explosionTriggered || self.highestScore >= TAPS_PER_LEVEL[self.heartType + 1]) {
			return;
		}
		// Calculate progress in current level (0 to 1)
		var startTap = self.heartType === 0 ? 0 : TAPS_PER_LEVEL[self.heartType - 1];
		var endTap = TAPS_PER_LEVEL[self.heartType];
		var progress = (self.highestScore - startTap) / (endTap - startTap);
		// Map progress to frame indices (frames go from 5 to 0)
		var frameProgress = progress * 5;
		var currentFrame = 5 - Math.floor(frameProgress);
		var nextFrame = Math.max(0, currentFrame - 1);
		var alpha = frameProgress - Math.floor(frameProgress);
		// Update frame visibility only if current frame changed
		if (self.currentGraphic !== self.heartFrames[self.heartType][currentFrame] || self.nextGraphic !== self.heartFrames[self.heartType][nextFrame]) {
			// Hide all frames
			self.heartFrames[self.heartType].forEach(function (frame) {
				return frame.visible = false;
			});
			// Setup current and next frames
			self.currentGraphic = self.heartFrames[self.heartType][currentFrame];
			self.nextGraphic = self.heartFrames[self.heartType][nextFrame];
			[self.currentGraphic, self.nextGraphic].forEach(function (frame) {
				if (frame) {
					frame.visible = true;
					frame.scaleX = frame.scaleY = 1.1;
				}
			});
		}
		// Update alpha for smooth transition
		if (self.currentGraphic) {
			self.currentGraphic.alpha = 1 - alpha;
		}
	};
	// Explosion Animation
	self.animateExplosion = function (callback) {
		if (!self.explosionTriggered) {
			self.explosionTriggered = true;
			LK.getSound('boom').play();
			shakeScreen();
			var cloneGraphic;
			if (self.nextGraphic) {
				cloneGraphic = LK.getAsset('heart_' + self.nextGraphic.heartType + '_frame_' + self.nextGraphic.index, {
					anchorX: 0.5,
					anchorY: 0.5,
					scaleX: self.nextGraphic.scaleX,
					scaleY: self.nextGraphic.scaleY,
					alpha: 1
				});
				self.addChild(cloneGraphic);
			}
			LK.setTimeout(function () {
				LK.effects.flashScreen(0xffffff, 2000); // Flash the screen white for 500ms
				if (cloneGraphic) {
					log("nextGraphic for explosion!", cloneGraphic);
					tween(cloneGraphic, {
						scaleX: 45,
						scaleY: 45,
						alpha: 0
					}, {
						duration: 3000,
						easing: tween.easeOut,
						onFinish: function onFinish() {
							// Make all frames of the current heartType invisible
							self.heartFrames[self.heartType].forEach(function (frame) {
								frame.visible = false;
							});
							self.explosionTriggered = false; // Reset explosion flag
							cloneGraphic.destroy(); // Remove the clone after animation
							if (callback) {
								callback();
							}
						}
					});
					// Pre scale next heart
					if (self.heartFrames[self.heartType + 1]) {
						tween(self.heartFrames[self.heartType + 1][5], {
							scaleX: 1.1,
							scaleY: 1.1
						}, {
							duration: 300,
							easing: tween.easeOut
						});
						tween(self.heartFrames[self.heartType + 1][4], {
							scaleX: 1.1,
							scaleY: 1.1
						}, {
							duration: 300,
							easing: tween.easeOut
						});
					}
				} else {
					log("No nextGraphic for explosion!");
				}
			}, 205);
		}
	};
	// Initialize heart based on tap count
	self.initHeart = function () {
		var newHeartType = 0;
		self.highestScore = Math.max(tapCount, storage.highestScore || 0);
		// Determine heart type based on tapCount
		for (var level = 9; level > 0; level--) {
			// 0 < 50  < 99 => 0
			// 99 < 110 < 999 => 1
			if (self.highestScore >= TAPS_PER_LEVEL[level - 1]) {
				newHeartType = level;
				break;
			}
		}
		log("initHeart - Selected heartType: " + newHeartType);
		// Hide frames up to current heart type
		for (var type = 0; type < newHeartType; type++) {
			var frames = self.heartFrames[type] || [];
			frames.forEach(function (frame) {
				frame.visible = false;
			});
		}
		// Update heart type and graphics
		self.heartType = newHeartType;
		self.tapLimit = TAPS_PER_LEVEL[newHeartType];
		self.nbTapsPerFrame = self.tapLimit / 6;
		// Calculate progress in current level (0 to 1)
		var startTap = self.heartType === 0 ? 0 : TAPS_PER_LEVEL[self.heartType - 1];
		var endTap = TAPS_PER_LEVEL[self.heartType];
		var progress = Math.min(1, (self.highestScore - startTap) / (endTap - startTap));
		log("initHeart - Progress calculation: " + progress + " (tapCount: " + self.highestScore + ", startTap: " + startTap + ", endTap: " + endTap + ")");
		// Map progress to frame indices (frames go from 5 to 0)
		var frameProgress = Math.min(5, progress * 5);
		var currentFrame = Math.max(0, Math.min(5, Math.floor(frameProgress)));
		var nextFrame = Math.max(0, currentFrame + 1);
		var alpha = frameProgress - Math.floor(frameProgress);
		log("initHeart - Frame indices: current=" + currentFrame + ", next=" + nextFrame + ", alpha=" + alpha);
		// Check if heartFrames exists for this type
		if (!self.heartFrames[self.heartType]) {
			log("ERROR: No heartFrames found for type " + self.heartType);
			return;
		}
		log("initHeart - Number of frames for type " + self.heartType + ": " + self.heartFrames[self.heartType].length);
		// Set up current and next frames
		self.currentGraphic = self.heartFrames[self.heartType][currentFrame];
		self.nextGraphic = self.heartFrames[self.heartType][nextFrame];
		log("initHeart - Current graphic: " + (self.currentGraphic ? "exists" : "null") + ", Next graphic: " + (self.nextGraphic ? "exists" : "null"));
		// Set visibility and alpha values
		if (self.currentGraphic) {
			self.currentGraphic.visible = true;
			if (isDebug) {
				self.currentGraphic.tint = 0x00FF00;
			}
			self.currentGraphic.alpha = 1 - alpha;
			log("initHeart - Set current graphic alpha: " + (1 - alpha));
		} else {
			log("ERROR: currentGraphic is null");
			return;
		}
		if (self.nextGraphic) {
			self.nextGraphic.visible = true;
			if (isDebug) {
				self.nextGraphic.tint = 0xFF0000;
			}
			//self.nextGraphic.alpha = alpha;
			log("initHeart - Set next graphic alpha: " + alpha);
		} else {
			log("ERROR: nextGraphic is null");
			return;
		}
		log("initHeart - Starting tweens");
		tween(self.currentGraphic, {
			scaleX: 1.1,
			scaleY: 1.1
		}, {
			duration: 300,
			easing: tween.easeOut
		});
		tween(self.nextGraphic, {
			scaleX: 1.1,
			scaleY: 1.1
		}, {
			duration: 300,
			easing: tween.easeOut
		});
		shakeMiddleground();
		log("initHeart - Completed");
	};
});
// Create a class for bigHeart
var GeneratorButton = Container.expand(function (index) {
	var self = Container.call(this);
	// Attach a button asset to the class
	var buttonGraphics = self.attachAsset('generatorButton', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	// Ensure rightBoard is initialized before accessing its properties
	self.index = Math.min(Math.max(0, index), maxGenerators);
	self.isAnimating = false; // Add isAnimating flag at instance level
	var generatorAsset = self.attachAsset('generator_' + self.index, {
		anchorX: 0.5,
		anchorY: 0.5,
		y: -10
	});
	if (typeof GENERATORS !== 'undefined') {
		self.config = Object.values(GENERATORS).find(function (g) {
			return g.id === self.index;
		});
	} else {
		console.error("GENERATORS is not defined");
		self.config = {
			cost: 0
		}; // Default to prevent undefined error
	}
	self.costText = new Text2(self.config.cost.toString(), {
		size: 50,
		fill: 0x043515,
		dropShadow: true,
		align: 'right'
	});
	self.countText = new Text2(' ', {
		size: 50,
		fill: 0x043515,
		dropShadow: true,
		align: 'center'
	});
	self.countText.x = -buttonGraphics.width / 2 + 10;
	self.countText.y = -buttonGraphics.height / 2 + 10;
	self.addChild(self.countText);
	self.costText.x = 30;
	self.costText.y = 75;
	self.visible = false;
	self.addChild(self.costText);
	if (typeof GENERATORS !== 'undefined') {
		self.config = Object.values(GENERATORS).find(function (g) {
			return g.id === self.index;
		});
	} else {
		console.error("GENERATORS is not defined");
	}
	// Position the button at the center of the screen
	// self.x = 2048 / 2;
	// self.y = 2732 / 2;
	// Event handler called when a press happens on the button
	var isSwipingButtons = false; // Global flag to track swipe/animation state
	var dragNode = null;
	var globalStartY = 0;
	self.down = function (x, y, obj) {
		if (isSwipingButtons) {
			return;
		} // Prevent interaction during animation
		log("Generator button pressed", y);
		globalStartY = y;
		self.startTime = Date.now();
		self.startX = x;
		dragNode = rightBoard;
	};
	self.up = function (x, y, obj) {
		if (isSwipingButtons) {
			dragNode = null;
			return;
		}
		var endTime = Date.now();
		var timeDiff = endTime - self.startTime;
		var distance = Math.sqrt(Math.pow(x - self.startX, 2) + Math.pow(y - globalStartY, 2));
		if (timeDiff < TAP_DETECT_DELAY && distance < SWIPE_THRESHOLD) {
			// Detected as a tap
			log("Detected tap on Generator button");
			// Check if tapCount is less than the cost of the generator
			if (tapCount < self.config.cost) {
				log("No enough love");
				LK.getSound('cantBuyGenerator').play(); // Play sound when player can't buy
				// Store original position
				var originalX = self.x;
				var originalY = self.y;
				isSwipingButtons = true;
				dragNode = null; // Reset dragNode immediately when starting animation
				tween(self, {
					x: originalX + 10,
					y: originalY + 10
				}, {
					duration: 100,
					easing: tween.easeInOut,
					onFinish: function onFinish() {
						if (!isSwipingButtons) {
							return;
						} // Prevent animation continuation if interrupted
						tween(self, {
							x: originalX - 10,
							y: originalY - 10
						}, {
							duration: 100,
							easing: tween.easeInOut,
							onFinish: function onFinish() {
								if (!isSwipingButtons) {
									return;
								}
								tween(self, {
									x: originalX,
									y: originalY
								}, {
									duration: 100,
									easing: tween.easeInOut,
									onFinish: function onFinish() {
										isSwipingButtons = false;
									}
								});
							}
						});
					}
				});
				return; // Exit if not enough taps
			}
			// Buy the corresponding generator using progressManager
			if (progressManager.buyGenerator(self.index)) {
				log("Generator purchased successfully");
				tween(self, {
					scaleX: 1.2,
					// Increase scale for bump effect
					scaleY: 1.2
				}, {
					duration: 100,
					// Duration of the bump
					easing: tween.easeOut,
					// Easing function for smooth effect
					onFinish: function onFinish() {
						tween(self, {
							// Return to original scale
							scaleX: 1,
							scaleY: 1
						}, {
							duration: 100,
							easing: tween.easeIn
						});
					}
				});
			} else {
				log("Failed to purchase generator");
			}
		} else {
			// Only handle swipe if not animating
			if (!isSwipingButtons && dragNode === rightBoard) {
				log("Detected swipe on Generator button");
				rightBoard.handleSwipe(y - globalStartY);
			}
			dragNode = null; // Reset dragNode when mouse is up
		}
	};
});
var GiftBox = Container.expand(function () {
	var self = Container.call(this);
	var giftBoxGraphics = self.attachAsset('giftBox', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	self.speed = Math.random() * 2 + 1; // Random speed for each gift box
	self.visible = false; // Start invisible
	// Function to make the gift box fall
	self.fall = function () {
		self.x = 100 + Math.random() * 1548; // Random x position
		self.y = -100; // Start above the screen
		self.visible = true;
		log("GiftBox is falling from position: ", self.x, self.y);
		tween(self, {
			y: 3000 // Fall to below the screen
		}, {
			duration: Math.random() * 3000 + 3000,
			// Random duration for fall
			easing: tween.easeInOut,
			onFinish: function onFinish() {
				self.visible = false;
				self.destroy();
			}
		});
	};
	// Add tap event to destroy the gift box
	self.down = function (x, y, obj) {
		if (isSpinWheelGamePlaying) {
			return;
		}
		LK.getSound('bonus').play(); // Play bonus sound when giftbox is tapped
		isSpinWheelGamePlaying = true;
		var spinwheelGame = new SpinwheelGame();
		spinwheelGame.x = self.x;
		spinwheelGame.y = self.y;
		spinwheelGame.scaleX = 0;
		spinwheelGame.scaleY = 0;
		foregroundContainer.addChild(spinwheelGame);
		tween(spinwheelGame, {
			x: 1024,
			y: 1366,
			scaleX: 1,
			scaleY: 1
		}, {
			duration: 1000,
			easing: tween.easeOut,
			onFinish: function onFinish() {
				tween(spinwheelGame.backdrop, {
					width: 2048,
					height: 2732
				}, {
					duration: 1000,
					easing: tween.easeOut
				});
				spinwheelGame.start();
			}
		});
		self.destroy();
	};
});
var GiftBoxManager = Container.expand(function () {
	var self = Container.call(this);
	self.giftBox;
	self.isStarted = false;
	// Function to spawn a gift box
	self.spawnGiftBox = function () {
		if (isSpinWheelGamePlaying) {
			log("SpinWheelGame is playing, skipping gift box spawn.");
			return; // Stop spawning gift boxes if SpinwheelGame is playing
		}
		log("Spawning a new gift box.");
		if (self.giftBox) {
			self.removeChild(self.giftBox);
			self.giftBox.destroy();
			self.giftBox = null;
		}
		self.giftBox = new GiftBox();
		self.addChild(self.giftBox);
		self.giftBox.fall();
	};
	// Update function to manage gift boxes
	self.start = function () {
		self.isStarted = true;
		function scheduleNextGiftBox() {
			var intervalDuration = 30000 + 40000 * Math.random();
			LK.setTimeout(function () {
				self.spawnGiftBox();
				scheduleNextGiftBox(); // Schedule next gift box with a new random interval
			}, intervalDuration);
		}
		scheduleNextGiftBox(); // Start the first scheduling
	};
});
var GiftRain = Container.expand(function () {
	var self = Container.call(this);
	self.giftPool = [];
	self.activeGifts = [];
	self.generatorCount = 0;
	// Initialize gift pool
	for (var i = 0; i < 50; i++) {
		var gift = new RainDrop();
		gift.updateAsset(self.generatorCount);
		gift.speed = Math.random() * 3 + 5; // Random speed for each gift
		gift.visible = false; // Start invisible
		self.addChild(gift); // Add to container immediately
		self.giftPool.push(gift);
	}
	// Function to spawn a gift
	self.spawnGift = function (generatorId) {
		log("Spawning gift. Active gifts count: ", self.activeGifts.length);
		if (self.giftPool.length > 0) {
			var gift = self.giftPool.pop();
			gift.x = Math.random() * (1948 - 100) + 100;
			gift.y = -300; // Start above the screen
			gift.rotation = Math.random() * (Math.PI / 2) - Math.PI / 4; // Set random rotation between -PI/4 and +PI/4
			gift.alpha = 0.8;
			gift.visible = true;
			gift.updateAsset(generatorId); // Update asset when spawning
			self.activeGifts.push(gift);
			log("Gift spawned at position: ", gift.x, gift.y);
		}
	};
	// Update function to move gifts
	self.update = function () {
		if (isSpinWheelGamePlaying || isInfiniteMode) {
			return;
		}
		for (var i = self.activeGifts.length - 1; i >= 0; i--) {
			var gift = self.activeGifts[i];
			gift.y += gift.speed;
			if (gift.y > 3000) {
				log("Gift moved out of bounds and will be recycled. Position: ", gift.x, gift.y);
				gift.x = Math.random() * (1948 - 100) + 100;
				gift.y = -300; // Start above the screen
				gift.rotation = Math.random() * (Math.PI / 2) - Math.PI / 4; // Set random rotation between -PI/4 and +PI/4
				gift.visible = true;
			}
		}
	};
	// Function to increase gift spawn rate
	self.increaseSpawnRate = function (generatorId) {
		self.generatorCount++;
		self.spawnGift(generatorId);
		log("Increasing spawn rate. Generator count: ", self.generatorCount);
		// for (var i = 0; i < self.generatorCount; i++) {
		// 	self.spawnGift(generatorId);
		// }
	};
});
var LoveField = Container.expand(function () {
	var self = Container.call(this);
	self.hearts = [];
	self.maxHearts = 30;
	// Initialize heart assets
	for (var i = 4; i <= 9; i++) {
		for (var j = 0; j < self.maxHearts / 6; j++) {
			var heart = self.attachAsset('heart_' + i + '_frame_0', {
				anchorX: 0.5,
				anchorY: 0.5,
				scaleX: 0.5,
				scaleY: 0.5,
				heartType: i,
				visible: false
			});
			heart.vx = (Math.random() - 0.5) * 2;
			heart.vy = Math.random() * 2 + 1;
			heart.down = function (x, y, obj) {
				// Create a clone heart
				var cloneHeart = LK.getAsset('heart_' + this.heartType + '_frame_0', {
					anchorX: 0.5,
					anchorY: 0.5,
					scaleX: this.scaleX,
					scaleY: this.scaleY,
					x: this.x,
					y: this.y,
					rotation: this.rotation,
					alpha: 1
				});
				self.addChild(cloneHeart);
				// Scale clone heart to 50 and fade out
				tween(cloneHeart, {
					scaleX: 50,
					scaleY: 50,
					alpha: 0
				}, {
					duration: 1000,
					onFinish: function onFinish() {
						// Destroy the clone heart after animation
						cloneHeart.destroy();
					}
				});
				LK.getSound('endHeartTap').play(); // Play pop sound for manualGeneration
				LK.setTimeout(function () {
					LK.getSound('pop2').play();
				}, 666);
			};
			self.hearts.push(heart);
		}
	}
	// Update function to animate hearts
	self.update = function () {
		self.hearts.forEach(function (heart) {
			if (!heart.visible) {
				if (heart._activeTween) {
					heart._activeTween.stop();
					heart._activeTween = null;
				}
				var targetX, targetY; // Define targetX and targetY variables
				// Determine if the heart should start from the bottom corners 
				if (Math.random() > 0.5) {
					heart.x = Math.random() > 0.5 ? 0 : 2048; // Start from left or right bottom corner 
					heart.y = 2732 + heart.height; // Start below the screen 
					targetX = heart.x === 0 ? Math.random() * 1024 + 1024 : Math.random() * 1024;
					targetY = -heart.height; // Move to above the screen 
				} else {
					// Start from one of the top corners 
					heart.x = Math.random() > 0.5 ? 0 : 2048; // Start from left or right corner 
					heart.y = -heart.height; // Start above the screen 
					targetX = heart.x === 0 ? Math.random() * 1024 + 1024 : Math.random() * 1024;
					targetY = 2732 + heart.height; // Fall to below the screen 
				}
				heart.scaleX = 0.5; // Initial scale 
				heart.scaleY = 0.5;
				var delay = Math.random() * 5000; // Random delay up to 2 seconds 
				heart.visible = true;
				tween(heart, {
					x: targetX,
					y: targetY,
					rotation: (Math.random() - 0.5) * Math.PI / 4 // Random rotation like a leaf 
				}, {
					duration: Math.random() * 3000 + 4000,
					// Longer duration for a gentle fall 
					easing: tween.easeInOut,
					// Smooth easing 
					delay: delay,
					onFinish: function onFinish() {
						heart.visible = false; // Hide after animation
						heart.x = Math.random() > 0.5 ? 0 : 2048; // Reset position to start from left or right corner
						heart.y = heart.y < 0 ? -heart.height : 2732 + heart.height; // Reset position to start above or below the screen
					}
				});
			}
		});
	};
});
var ProgressBar = Container.expand(function () {
	var self = Container.call(this);
	// Attach the progressBack asset
	var progressBack = self.attachAsset('progressBack', {
		anchorX: 0,
		anchorY: 0.5,
		width: 2048,
		height: 40,
		alpha: 0.8
	});
	// Attach the progressBar asset
	var progressBar = self.attachAsset('progressBar', {
		anchorX: 0,
		anchorY: 0.5,
		width: 2048,
		height: 32,
		alpha: 0.8,
		y: 4
	});
	// Set initial progress to 0
	self.progress = 0;
	self.visible = !isInfiniteMode;
	// Method to update the progress
	self.updateProgress = function (value) {
		if (isInfiniteMode) {
			return;
		}
		self.progress = Math.max(0, Math.min(1, value)); // Clamp value between 0 and 1
		progressBar.scaleX = self.progress; // Scale the progressBar based on progress
	};
});
// Create a class for Projections
var Projections = Container.expand(function () {
	var self = Container.call(this);
	var nbProjections = 10;
	var heartSpeed = 20;
	var gravity = 0.5;
	var initialScale = 0.25;
	var scaleVariation = 0.5;
	var alphaDecay = 0.002;
	self.heartPool = [];
	self.preloadedAssets = {}; // Preload assets for each heart type
	for (var type = 0; type <= 9; type++) {
		self.preloadedAssets[type] = LK.getAsset('heart_' + type + '_frame_0', {
			anchorX: 0.5,
			anchorY: 0.5,
			scaleX: 0.5,
			scaleY: 0.5
		});
	}
	// Initialize heart pool
	for (var i = 0; i < nbProjections * 5; i++) {
		var heart = new Container();
		heart.vx = 0;
		heart.vy = 0;
		heart.alpha = 0;
		heart.update = function () {
			this.x += this.vx;
			this.y += this.vy;
			this.vy += gravity; // Add gravity effect 
			this.alpha -= alphaDecay;
			if (this.alpha <= 0 || this.y > 2900) {
				this.alpha = 0;
				self.heartPool.push(this);
			}
		};
		// Add all heart type assets to the heart Container
		for (var type = 0; type <= 9; type++) {
			var heartAsset = heart.attachAsset('heart_' + type + '_frame_0', {
				anchorX: 0.5,
				anchorY: 0.5,
				scaleX: 0.5,
				scaleY: 0.5,
				heartType: type,
				visible: type === 0 // Only make the current heartType visible
			});
		}
		self.heartPool.push(heart);
	}
	self.x = 2048 / 2;
	self.y = 2732 / 2 - 400;
	// Function to pop hearts
	self.popHearts = function (heartType) {
		if (self.isUpdatingHeartType) {
			return;
		} // Exit if updateHeartType is running
		for (var i = 0; i < nbProjections; i++) {
			if (self.heartPool.length > 0) {
				var heart = self.heartPool.pop();
				if (heart.alpha <= 0) {
					// Ensure heart has finished its previous animation
					heart.x = 0;
					heart.y = 0;
					heart.vx = (Math.random() - 0.5) * heartSpeed;
					heart.vy = (Math.random() - 1.5) * heartSpeed;
					heart.alpha = 0.8;
					heart.scaleX = initialScale + Math.random() * scaleVariation; // Randomize scale between initialScale and initialScale + scaleVariation
					heart.scaleY = heart.scaleX; // Keep aspect ratio consistent
					heart.rotation = Math.random() * Math.PI * 2; // Randomize rotation between 0 and 2Ο
					self.addChild(heart);
				} else {
					self.heartPool.push(heart); // Return heart to pool if it hasn't finished animation
				}
			}
		}
	};
	self.updateHeartType = function (heartType) {
		// Update the heart type for all hearts in the pool
		self.isUpdatingHeartType = true; // Set flag to indicate updateHeartType is running
		self.heartPool.forEach(function (heart) {
			heart.children.forEach(function (child) {
				// Iterate over all children
				child.visible = child.heartType === heartType; // Set visibility based on heartType
			});
		});
		self.isUpdatingHeartType = false; // Reset flag after updateHeartType completes
	};
});
var RainDrop = Container.expand(function () {
	var self = Container.call(this);
	self.assets = [];
	// Initialize assets for RainDrop
	for (var i = 0; i <= maxGenerators; i++) {
		var asset = self.attachAsset('generator_' + i, {
			anchorX: 0.5,
			anchorY: 0.5,
			visible: true
		});
		self.assets.push(asset);
	}
	// Function to update asset visibility based on index
	self.updateAsset = function (index) {
		log("Updating asset visibility for index: ", index);
		if (index >= 0 && index < self.assets.length) {
			self.assets.forEach(function (asset, i) {
				asset.visible = i === index;
				if (asset.visible) {
					log("Asset at index ", i, " is now visible.");
				}
			});
		}
	};
});
var ResetButton = Container.expand(function () {
	var self = Container.call(this);
	// Attach the resetButton asset to the class
	self.resetButtonGraphics = self.attachAsset('resetButton', {
		anchorX: 0.5,
		anchorY: 0.5,
		alpha: 0.5
	});
	// Event handler called when a press happens on the reset button
	self.down = function (x, y, obj) {
		log("Reset button pressed");
		if (resetTappedOnce) {
			// Call the resetProgress function to reset the game
			resetProgress();
		} else {
			popMessage("RESTART FROM ZERO ?\r\nTap to confirm");
			resetTappedOnce = true;
			self.resetButtonGraphics.alpha = 1;
			LK.setTimeout(function () {
				resetTappedOnce = false;
				if (self.resetButtonGraphics) {
					self.resetButtonGraphics.alpha = 0.5;
				}
			}, 2000);
		}
	};
});
var RightBoard = Container.expand(function () {
	var self = Container.call(this);
	// Attach the rightBoard asset to the class
	var rightBoardGraphics = self.attachAsset('rightBoard', {
		anchorX: 0.5,
		anchorY: 0.5,
		width: 400,
		height: 3000,
		alpha: 0
	});
	self.lastY = 0;
	self.velocity = 0;
	self.isAnimating = false;
	self.lastTime = Date.now();
	self.swipeOffset = 0;
	// Add momentum animation
	self.applyMomentum = function () {
		if (Math.abs(self.velocity) < 0.1) {
			self.velocity = 0;
			self.isAnimating = false;
			return;
		}
		var currentTime = Date.now();
		var deltaTime = (currentTime - self.lastTime) / 16; // Normalize to ~60fps
		self.lastTime = currentTime;
		// Apply friction
		self.velocity *= Math.pow(0.95, deltaTime);
		// Calculate movement with velocity
		var deltaY = self.velocity * deltaTime;
		// Check boundaries
		var firstButtonNewY = self.y + self.generatorButtons[0].y + deltaY;
		var lastButtonNewY = self.y + self.generatorButtons[self.generatorButtons.length - 1].y + deltaY;
		// Bounce effect at boundaries
		if (firstButtonNewY > 1000) {
			self.velocity *= -0.5;
			deltaY = 1000 - (self.y + self.generatorButtons[0].y);
		} else if (lastButtonNewY < 1366) {
			self.velocity *= -0.5;
			deltaY = 1366 - (self.y + self.generatorButtons[self.generatorButtons.length - 1].y);
		}
		// Move all generator buttons
		self.generatorButtons.forEach(function (button) {
			button.y += deltaY;
		});
		// Continue animation
		if (self.isAnimating) {
			LK.setTimeout(self.applyMomentum, 16);
		}
	};
	// Position the rightBoard at the right side of the screen
	self.x = 1848;
	self.y = 1366;
	self.buttonsOffsetX = 50;
	self.buttonsOffsetY = -300;
	self.generatorButtons = []; // Initialize an array to hold generator buttons
	// Create and position generator buttons
	for (var i = 0; i <= 9; i++) {
		// Todo : use maxGenerators
		var generatorButton = new GeneratorButton(i);
		generatorButton.x = self.buttonsOffsetX; //0 * self.x + i * 100 - 100; // Position buttons with some spacing
		generatorButton.y = self.buttonsOffsetY + i * 300 + i * 300 * 0.1; //self.y;
		self.generatorButtons.push(generatorButton);
		self.addChild(generatorButton);
	}
	self.cleanUp = function () {
		// Re-calculate positions for all buttons while maintaining the swipe offset
		self.generatorButtons.forEach(function (button, index) {
			button.y = self.buttonsOffsetY + index * 300 + index * 300 * 0.1 + self.swipeOffset;
		});
	};
	self.down = function (x, y, obj) {
		log("RightBoard pressed", y);
		globalStartY = y;
		self.lastY = y;
		self.velocity = 0;
		self.isAnimating = false;
		dragNode = self;
		self.startTime = Date.now();
		self.lastTime = Date.now();
	};
	self.move = function (x, y, obj) {
		// Check if any generator button is animating
		var isAnyButtonAnimating = self.generatorButtons.some(function (button) {
			return button.isAnimating;
		});
		if (isAnyButtonAnimating) {
			return; // Prevent movement if any button is animating
		}
		if (dragNode === self || dragNode instanceof GeneratorButton) {
			// Check if a button or rightBoard is being dragged
			var deltaY = y - globalStartY; // Calculate deltaY based on movement
			// Only apply movement if the delta exceeds the swipe threshold
			if (Math.abs(deltaY) > SWIPE_THRESHOLD) {
				log("Move detected with deltaY:", deltaY);
				// Calculate new positions for the first and last buttons
				var firstButtonNewY = self.y + self.generatorButtons[0].y + deltaY;
				var lastButtonNewY = self.y + self.generatorButtons[self.generatorButtons.length - 1].y + deltaY;
				log("firstButtonNewY:", firstButtonNewY, "lastButtonNewY:", lastButtonNewY);
				// Check if the first button stays above the screen center and the last button stays below the screen center
				// Adjust deltaY to prevent moving beyond boundaries
				if (deltaY > 0) {
					deltaY = Math.min(deltaY, 1000 - (self.y + self.generatorButtons[0].y));
				}
				if (deltaY < 0) {
					deltaY = Math.max(deltaY, 1366 - (self.y + self.generatorButtons[self.generatorButtons.length - 1].y));
				}
				// Move all generator buttons vertically based on swipe direction
				self.generatorButtons.forEach(function (button) {
					button.y += deltaY;
				});
				self.swipeOffset += deltaY; // Update the total swipe offset
				globalStartY = y; // Update globalStartY for continuous movement
			}
		}
	};
	self.up = function (x, y, obj) {
		if (!allowSwipe) {
			return;
		}
		var endTime = Date.now();
		var timeDiff = endTime - self.startTime;
		var distance = Math.sqrt(Math.pow(x - self.startX, 2) + Math.pow(y - globalStartY, 2));
		if (timeDiff < TAP_DETECT_DELAY && distance < SWIPE_THRESHOLD) {
			// Detected as a tap
			log("Detected tap on RightBoard");
		} else {
			// Start momentum animation
			self.isAnimating = true;
			LK.setTimeout(function () {
				self.applyMomentum();
				// Clean up after momentum animation
				self.cleanUp();
			}, 16);
		}
		dragNode = null;
	};
	self.handleSwipe = function (deltaY, y) {
		if (!allowSwipe) {
			return;
		}
		// Calculate new positions for the first and last buttons
		var firstButtonNewY = self.y + self.generatorButtons[0].y + deltaY;
		var lastButtonNewY = self.y + self.generatorButtons[self.generatorButtons.length - 1].y + deltaY;
		log("firstButtonNewY:", firstButtonNewY, "lastButtonNewY:", lastButtonNewY);
		// Check if the first button stays above the screen center and the last button stays below the screen center
		// Adjust deltaY to prevent moving beyond boundaries
		if (deltaY > 0) {
			deltaY = Math.min(deltaY, 1000 - (self.y + self.generatorButtons[0].y));
		}
		if (deltaY < 0) {
			deltaY = Math.max(deltaY, 1366 - (self.y + self.generatorButtons[unlockedGeneratorIndex].y));
		}
		// Move all generator buttons vertically based on swipe direction
		self.generatorButtons.forEach(function (button) {
			button.y += deltaY;
		});
		self.swipeOffset += deltaY; // Update the total swipe offset
		globalStartY = y; // Update globalStartY for continuous movement
	};
	self.move = function (x, y, obj) {
		if (!allowSwipe) {
			return;
		}
		if (dragNode === self || dragNode instanceof GeneratorButton) {
			// Check if a button or rightBoard is being dragged
			var deltaY = y - globalStartY; // Calculate deltaY based on movement
			// Only apply movement if the delta exceeds the swipe threshold
			if (Math.abs(deltaY) > SWIPE_THRESHOLD) {
				log("Move detected with deltaY:", deltaY);
				// Calculate new positions for the first and last buttons
				var firstButtonNewY = self.y + self.generatorButtons[0].y + deltaY;
				var lastButtonNewY = self.y + self.generatorButtons[self.generatorButtons.length - 1].y + deltaY;
				log("firstButtonNewY:", firstButtonNewY, "lastButtonNewY:", lastButtonNewY);
				// Adjust deltaY to prevent moving beyond boundaries
				if (deltaY > 0) {
					deltaY = Math.min(deltaY, 1000 - (self.y + self.generatorButtons[0].y));
				}
				if (deltaY < 0) {
					deltaY = Math.max(deltaY, 1366 - (self.y + self.generatorButtons[self.generatorButtons.length - 1].y));
				}
				// Move all generator buttons vertically based on swipe direction
				self.generatorButtons.forEach(function (button) {
					button.y += deltaY;
				});
				self.swipeOffset += deltaY; // Update the total swipe offset
				globalStartY = y; // Update globalStartY for continuous movement
			}
		}
	};
});
var SpinwheelGame = Container.expand(function () {
	var self = Container.call(this);
	self.bonuses = [];
	self.wheelSpinning = false;
	self.wheelStopped = false;
	self.nbGeneratorsOnWheel = 0;
	self.wonBonusIndex = 0;
	self.isSlowingDown = false;
	self.slowDownStartTime = 0;
	self.nextTickTime = 0;
	self.tickSoundComplete = false;
	// Attach the rightBoard asset to the class
	self.backdrop = self.attachAsset('backdrop', {
		anchorX: 0.5,
		anchorY: 0.5,
		width: 1000,
		height: 1000,
		alpha: 0.5 // Set semi-transparency
	});
	// Add event handler to catch clicks
	self.backdrop.down = function (x, y, obj) {
		// Prevent interaction with objects behind the backdrop
		log("Backdrop clicked, preventing interaction with objects behind it.");
		if (isSpinWheelGamePlaying) {
			LK.getSound('pop').play();
			self.stopWheel();
		}
	};
	self.addChild(self.backdrop);
	self.rightBoardGraphics = self.attachAsset('rightBoard', {
		anchorX: 0.5,
		anchorY: 0.5,
		width: 1480,
		height: 1480
	});
	// Attach the selector asset to the SpinwheelGame
	self.selector = self.attachAsset('selector', {
		anchorX: 0.5,
		anchorY: 0.5,
		x: 0,
		y: -650 // Position it at the top of the wheel
	});
	self.addChild(self.selector);
	// Attach the bigHeart asset to the SpinwheelGame
	self.centerHeartShadow = self.attachAsset('bigHeart', {
		anchorX: 0.5,
		anchorY: 0.5,
		scaleX: 0.75,
		scaleY: 0.75,
		x: 10,
		y: 10,
		alpha: 0.3,
		tint: 0x000000
	});
	self.addChild(self.centerHeartShadow);
	self.centerHeart = self.attachAsset('bigHeart', {
		anchorX: 0.5,
		anchorY: 0.5,
		scaleX: 0.75,
		scaleY: 0.75,
		x: 0,
		y: 0 // Center it in the SpinwheelGame
	});
	self.addChild(self.centerHeart);
	// Create a wheel container
	self.wheel = new Container();
	self.addChild(self.wheel);
	self.start = function () {
		// Iterate over each unlocked generator
		self.wheelSpinning = true;
		self.wheelStopped = false;
		self.wonBonusIndex = 0;
		self.wheel.rotation = Math.random() * Math.PI * 2;
		giftRain.visible = false; // Pause giftrain by making it invisible
		self.nbGeneratorsOnWheel = Math.min(8, unlockedGeneratorIndex);
		for (var i = 0; i <= self.nbGeneratorsOnWheel; i++) {
			// Calculate angle for each bonus
			var angle = 2 * Math.PI / (self.nbGeneratorsOnWheel + 1) * i;
			// Calculate x and y positions based on angle and radius
			var xPos = 500 * Math.cos(angle);
			var yPos = 500 * Math.sin(angle);
			// Create a new gift asset for each unlocked generator
			var giftAsset = self.attachAsset('generator_' + i, {
				anchorX: 0.5,
				anchorY: 0.5,
				x: xPos,
				y: yPos,
				visible: true
			});
			// Add the gift asset to the wheel container
			self.wheel.addChild(giftAsset);
			// Store the gift asset in the bonuses array
			self.bonuses.push(giftAsset);
			// Start rotating the wheel indefinitely
			var _rotateWheel = function rotateWheel() {
				if (!self.wheelSpinning) {
					return;
				}
				tween(self.wheel, {
					rotation: Math.PI * 2
				}, {
					duration: 1000,
					easing: tween.linear,
					onFinish: function onFinish() {
						self.wheel.rotation = 0;
						_rotateWheel();
					}
				});
			};
			_rotateWheel();
		}
		;
	};
	self.update = function () {
		if (self.wheelSpinning && LK.ticks % 10 === 0) {
			LK.getSound('tick').play();
		}
		// Handle slowing down tick sounds when the wheel is stopping
		if (self.isSlowingDown && !self.tickSoundComplete) {
			var elapsedTime = Date.now() - self.slowDownStartTime;
			// Only play tick if we've reached the next scheduled tick time
			if (Date.now() >= self.nextTickTime && elapsedTime < 3000) {
				LK.getSound('tick').play();
				// Calculate the next tick delay - gradually increasing as we approach 3 seconds
				// Start with 100ms between ticks, end with around 500ms between ticks
				var progress = elapsedTime / 3000; // 0 to 1 over 3 seconds
				var nextDelay = 100 + 400 * progress;
				// Schedule the next tick
				self.nextTickTime = Date.now() + nextDelay;
			}
			// Mark as complete after 3 seconds
			if (elapsedTime >= 3000) {
				self.tickSoundComplete = true;
			}
		}
	};
	self.stopWheel = function () {
		if (self.wheelStopped) {
			return;
		}
		log("stopWheel...");
		self.wheelStopped = true; // Set spinning flag to false after stop
		self.wheelSpinning = false; // Set spinning flag to false after stop
		// // Stop the wheel rotation by clearing any active tweens
		// if (self.wheel._activeTween) {
		// 	self.wheel._activeTween.stop();
		// 	self.wheel._activeTween = null;
		// }
		// Initialize variables for the slowing down tick effect
		self.isSlowingDown = true;
		self.slowDownStartTime = Date.now();
		self.nextTickTime = Date.now(); // Start first tick immediately
		self.tickSoundComplete = false;
		// // Optionally, you can set the wheel's rotation to a specific angle if needed
		// self.wheel.rotation = 0;
		tween.stop(self.wheel, {
			rotation: true
		}); // Stop any active rotation tween
		self.wonBonusIndex = Math.floor(Math.random() * (self.nbGeneratorsOnWheel + 1)); // Randomly select a bonus index
		// Calculate the angle for each segment
		var segmentAngle = 2 * Math.PI / (self.nbGeneratorsOnWheel + 1);
		// Calculate the current normalized rotation (between 0 and 2Ο)
		var currentRotation = self.wheel.rotation % (Math.PI * 2);
		if (currentRotation < 0) {
			currentRotation += Math.PI * 2;
		}
		// Calculate the target position for the selected bonus (top position is at 270 degrees or 3Ο/2)
		var targetPosition = Math.PI * 1.5;
		// Calculate the current position of the selected bonus
		var currentBonusPosition = (segmentAngle * self.wonBonusIndex + currentRotation) % (Math.PI * 2);
		// Calculate how much we need to rotate to align the bonus with the top
		var rotationNeeded = targetPosition - currentBonusPosition;
		// Ensure we always rotate forward (add 2Ο if needed)
		if (rotationNeeded < 0) {
			rotationNeeded += Math.PI * 2;
		}
		// Add additional full rotations for a nice effect (2 full rotations)
		rotationNeeded += Math.PI * 4;
		// Calculate the final angle
		var finalAngle = self.wheel.rotation + rotationNeeded;
		log("wonBonusIndex:", self.wonBonusIndex, "segmentAngle:", segmentAngle, "currentRotation:", currentRotation, "currentBonusPosition:", currentBonusPosition, "targetPosition:", targetPosition, "rotationNeeded:", rotationNeeded, "finalAngle:", finalAngle);
		tween(self.wheel, {
			rotation: finalAngle
		}, {
			duration: 3000,
			easing: tween.easeOut,
			onFinish: function onFinish() {
				self.animateWheelWin();
			}
		});
		tween(self.centerHeart, {
			scaleX: 0.7,
			scaleY: 0.7
		}, {
			duration: 300,
			easing: tween.easeIn
		});
		tween(self.centerHeartShadow, {
			scaleX: 0.6,
			scaleY: 0.6
		}, {
			duration: 300,
			easing: tween.easeIn
		});
	};
	self.animateWheelWin = function () {
		if (!self.wheelStopped) {
			return;
		}
		log("animateWheelWin..." + self.wonBonusIndex);
		LK.getSound('tada').play();
		var bonus = self.bonuses[self.wonBonusIndex];
		if (bonus) {
			tween(bonus, {
				scaleX: 40,
				scaleY: 40,
				rotation: -self.wheel.rotation,
				alpha: 0
			}, {
				duration: 3000,
				easing: tween.easeOut,
				onFinish: function onFinish() {
					bonus.visible = false;
					// Offer the cost and buy the generator
					progressManager.buyGenerator(self.wonBonusIndex, true);
					giftRain.visible = true; // Set giftRain visible again
					self.destroy();
				}
			});
			tween(self.backdrop, {
				alpha: 0
			}, {
				duration: 2800,
				easing: tween.easeOut,
				onFinish: function onFinish() {
					isSpinWheelGamePlaying = false;
				}
			});
			// Hide all other bonuses except the one with self.wonBonusIndex
			self.bonuses.forEach(function (b, index) {
				if (index !== self.wonBonusIndex) {
					b.visible = false;
				}
			});
			// Flash the screen white for 500ms
			LK.effects.flashScreen(0xffffff, 500);
			// Hide bigHeart, selector, and rightBoardGraphics
			self.centerHeart.visible = false;
			self.selector.visible = false;
			self.rightBoardGraphics.visible = false;
		}
	};
});
/**** 
* Initialize Game
****/ 
var game = new LK.Game({
	backgroundColor: 0xa16e9f //Init game with black background 
});
/**** 
* Game Code
****/ 
var resetTappedOnce = false;
function resetProgress() {
	// Reset tap count and displayed tap count
	tapCount = "0";
	displayedTapCount = tapCount;
	targetTapCount = tapCount;
	// Reset progress manager properties
	progressManager.money = tapCount;
	progressManager.currentLevel = 0;
	// Reset generators using initializeGenerators
	var resetGenerators = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
	var initResult = initializeGenerators(resetGenerators);
	progressManager.generatorCounts = initResult.generatorCounts;
	progressManager.generators = initResult.generators;
	progressManager.upgrades = {};
	// Destroy and re-create the rightBoard
	if (rightBoard) {
		rightBoard.destroy();
		rightBoard = new RightBoard();
		foregroundContainer.addChild(rightBoard);
		rightBoard.generatorButtons.forEach(function (button, index) {
			if (!button.config) {
				button.config = {
					cost: GENERATORS[Object.keys(GENERATORS)[index]].cost
				};
			}
		});
	}
	// Reset bigHeart properties
	bigHeart.highestScore = 0;
	bigHeart.heartType = 0;
	bigHeart.explosionTriggered = false;
	// Reset storage
	storage.tapCount = tapCount;
	storage.isInfiniteMode = false;
	storage.lastUpdateTime = Date.now();
	storage.highestScore = 0;
	storage.generators = Array.from({
		length: 10
	}, function () {
		return 0;
	});
	// Reset UI elements
	updateTapCountText();
	progressBar.updateProgress(0);
	// Reset game state flags
	isInfiniteMode = false;
	if (resetButton) {
		resetButton.destroy();
	}
	allowSwipe = false;
	unlockedGeneratorIndex = 0;
	// Re-initialize game elements
	bigHeart.initHeart();
	background.changeBackground(0);
	projectionsManager.updateHeartType(bigHeart.heartType);
	bigHeart.visible = true;
	// Reset loveField if it exists
	if (loveField) {
		loveField.destroy();
		loveField = null;
	}
	// Reset giftRain
	if (giftRain) {
		// Clear active gifts and reset generator count
		giftRain.activeGifts = [];
		giftRain.generatorCount = 0;
		// Return all gifts to the pool
		while (giftRain.activeGifts.length > 0) {
			var gift = giftRain.activeGifts.pop();
			gift.visible = false;
			giftRain.giftPool.push(gift);
		}
		// Make giftRain visible again
		giftRain.visible = true;
	}
	background.changeBackground(0);
}
function popMessage(text) {
	messageText.setText(text);
	messageText.visible = true;
	messageText.y = 2850;
	tween(messageText, {
		alpha: 1,
		y: 2500
	}, {
		duration: 500,
		easing: tween.easeInOut,
		onFinish: function onFinish() {
			tween(messageText, {
				alpha: 0,
				y: 2850
			}, {
				duration: 500,
				easing: tween.easeInOut,
				delay: 3000,
				onFinish: function onFinish() {
					messageText.visible = false;
				}
			});
		}
	});
}
// Start updtes 
var messageText; // Global variable for displaying messages
function formatGeneratorPrice(value) {
	if (value >= 1e12) {
		return Math.floor(value / 1e12) + 'T';
	} else if (value >= 1e9) {
		return Math.floor(value / 1e9) + 'B';
	} else if (value >= 1e6) {
		return Math.floor(value / 1e6) + 'M';
	} else if (value >= 1e3) {
		return Math.floor(value / 1e3) + 'K';
	} else {
		return value.toString();
	}
}
// Global event listener for mouse or touch up
game.up = function (x, y, obj) {
	dragNode = null; // Reset dragNode when mouse is up
};
function shakeMiddleground() {
	tween(middlegroundContainer, {
		x: 10,
		y: 10
	}, {
		duration: 100,
		easing: tween.easeInOut,
		onFinish: function onFinish() {
			tween(middlegroundContainer, {
				x: -10,
				y: -10
			}, {
				duration: 100,
				easing: tween.easeInOut,
				onFinish: function onFinish() {
					tween(middlegroundContainer, {
						x: 0,
						y: 0
					}, {
						duration: 100,
						easing: tween.easeInOut
					});
				}
			});
		}
	});
}
function shakeScreen() {
	var shakeCount = 0;
	function performShake() {
		if (shakeCount >= 10) {
			return;
		} // Stop after 10 loops
		tween(game, {
			x: Math.random() * 20 - 10,
			y: 0 //Math.random() * 20 - 10
		}, {
			duration: 100,
			easing: tween.easeInOut,
			onFinish: function onFinish() {
				tween(game, {
					x: Math.random() * 20 - 10,
					y: 0 //Math.random() * 20 - 10
				}, {
					duration: 100,
					easing: tween.easeInOut,
					onFinish: function onFinish() {
						tween(game, {
							x: 0,
							y: 0
						}, {
							duration: 100,
							easing: tween.easeInOut,
							onFinish: function onFinish() {
								shakeCount++;
								performShake(); // Recursive call for next shake
							}
						});
					}
				});
			}
		});
	}
	performShake();
}
var isSpinWheelGamePlaying = false;
var isDebug = false;
var SWIPE_THRESHOLD = 10; // Threshold in pixels to distinguish between taps and swipes
var TAP_DETECT_DELAY = 600;
var TAPS_PER_LEVEL = {
	0: 99,
	1: 999,
	2: 9999,
	3: 99999,
	4: 999999,
	5: 9999999,
	6: 99999999,
	7: 999999999,
	8: 9999999999,
	9: 99999999999
};
if (isDebug) {
	TAPS_PER_LEVEL = {
		0: 12,
		1: 50,
		2: 100,
		3: 200,
		4: 300,
		5: 400,
		6: 500,
		7: 600,
		8: 700,
		9: 10000000000
	};
}
// Constants for Generators and Upgrades
var GENERATORS = {
	ROSE: {
		id: 0,
		name: "Rose",
		description: "A charming rose that generates a few love beats",
		autoClick: true,
		clickRate: 1,
		cost: 10,
		upgradeLevel: 0
	},
	CHOCLATE: {
		id: 1,
		name: "Choclate",
		description: "A sweet choclate that generates love faster",
		autoClick: true,
		clickRate: 5,
		cost: 50,
		upgradeLevel: 0
	},
	CHAIN_BRACELET: {
		id: 2,
		name: "Chain Bracelet",
		description: "A beautiful chain bracelet that generates love even faster",
		autoClick: true,
		clickRate: 10,
		cost: 200,
		upgradeLevel: 0
	},
	PERFUME: {
		id: 3,
		name: "Perfume",
		description: "A delightful fragrance that enchants love",
		autoClick: true,
		clickRate: 20,
		cost: 500,
		upgradeLevel: 0
	},
	WATCH: {
		id: 4,
		name: "Watch",
		description: "A timeless piece that speeds up love generation",
		autoClick: true,
		clickRate: 50,
		cost: 1000,
		upgradeLevel: 0
	},
	JEWELRY_SET: {
		id: 5,
		name: "Jewelry Set",
		description: "A luxurious set that dazzles with love",
		autoClick: true,
		clickRate: 100,
		cost: 10000,
		upgradeLevel: 0
	},
	SPORTS_CAR: {
		id: 6,
		name: "Sport Car",
		description: "A fast car that accelerates love generation",
		autoClick: true,
		clickRate: 200,
		cost: 100000,
		upgradeLevel: 0
	},
	VILLA: {
		id: 7,
		name: "Villa",
		description: "A grand villa that generates love in abundance",
		autoClick: true,
		clickRate: 500,
		cost: 1000000,
		upgradeLevel: 0
	},
	PRIVATE_JET: {
		id: 8,
		name: "Private Jet",
		description: "A jet that takes love to new heights",
		autoClick: true,
		clickRate: 1000,
		cost: 1000000000,
		upgradeLevel: 0
	},
	INFINITE_LOVE: {
		id: 9,
		name: "Infinite Love",
		description: "An endless source of love",
		autoClick: true,
		clickRate: 654321,
		cost: 1000000000000,
		upgradeLevel: 0
	}
};
if (isDebug) {
	// Set debug cost for Generators
	GENERATORS.ROSE.cost = 10;
	GENERATORS.CHOCLATE.cost = 110;
	GENERATORS.CHAIN_BRACELET.cost = 220;
	GENERATORS.PERFUME.cost = 330;
	GENERATORS.WATCH.cost = 440;
	GENERATORS.JEWELRY_SET.cost = 550;
	GENERATORS.SPORTS_CAR.cost = 660;
	GENERATORS.VILLA.cost = 770;
	GENERATORS.PRIVATE_JET.cost = 880;
	GENERATORS.INFINITE_LOVE.cost = 1000;
}
var UPGRADES = {
	LOVE_FILTER: {
		id: 0,
		name: "Love Filter",
		description: "A powerful love filter that make you irresistible",
		targetGenerator: 1,
		// Targets Generator #1 (Me)
		multipliers: [2, 4, 8],
		// Levels of multiplier
		cost: 12
	}
};
var MAX_DISPLAY_NUMBER = "9999999999999999"; //9 999 999 999 999 999
///////////////////////////////////////////////////////// GLOBAL VARIABLES //////////////////////////////////////////////////////////// 
///////////////////////////////////////////////////////// GLOBAL VARIABLES //////////////////////////////////////////////////////////// 
///////////////////////////////////////////////////////// GLOBAL VARIABLES //////////////////////////////////////////////////////////// 
var maxGenerators = 9;
var nbHearts = 10;
var tapCount;
var displayedTapCount; // For smooth animation
var targetTapCount; // Target value for animation
var isAnimatingCount = false; // Flag to track if count animation is in progress
var background;
var backgroundContainer;
var giftsContainer;
var middlegroundContainer;
var foregroundContainer;
var giftRain;
var tapCountText;
var progressManager;
var projectionsManager;
var rightBoard;
var bigHeart;
var loveField;
var progressBar;
var resetButton;
var isInfiniteMode = false;
var fisrtBonusDeliveryDelay = 20000; // Fisrt giftbox delay
var allowSwipe = false; // Global flag to control swipe functionality
// Declare a global variable to store the startY position for GeneratorButton events
var globalStartY = 0;
// Declare a global variable to track the node being dragged
var dragNode = null;
var isSwipingButtons = false; // Global flag to track swipe/animation state
var unlockedGeneratorIndex = 0; // Track the index of the last unlocked generator
function log() {
	if (isDebug) {
		console.log.apply(console, arguments);
	}
}
function updateTapCountText() {
	tapCountText.setText('LOVE\r\n' + displayedTapCount);
}
// Function to animate the tap count smoothly
function animateTapCount() {
	if (!isAnimatingCount || displayedTapCount === targetTapCount) {
		isAnimatingCount = false;
		return;
	}
	// Determine if we need to increase or decrease the displayed count
	var isIncreasing = compareNumbers(targetTapCount, displayedTapCount) > 0;
	// Calculate the difference between current and target
	var diff = isIncreasing ? subtractLargeNumbers(targetTapCount, displayedTapCount) : subtractLargeNumbers(displayedTapCount, targetTapCount);
	if (diff === "0") {
		displayedTapCount = targetTapCount; // Ensure exact match at the end
		updateTapCountText();
		isAnimatingCount = false;
		return;
	}
	// Determine step size based on difference magnitude
	var stepSize = "1";
	var diffLength = diff.length;
	// Use a more gradual step size calculation for smoother animation
	if (diffLength > 5) {
		// For very large differences
		stepSize = "1" + "0".repeat(diffLength - 3);
	} else if (diffLength > 3) {
		// For medium differences
		stepSize = "1" + "0".repeat(diffLength - 3);
	} else if (diffLength > 2) {
		// For hundreds
		stepSize = "5";
	} else {
		// For small differences (1-99)
		stepSize = "1";
	}
	// Update the displayed count
	if (isIncreasing) {
		displayedTapCount = addLargeNumbers(displayedTapCount, stepSize);
	} else {
		displayedTapCount = subtractLargeNumbers(displayedTapCount, stepSize);
	}
	// Update the text
	updateTapCountText();
	// Continue animation in the next frame - use a faster frame rate for smoother animation
	LK.setTimeout(animateTapCount, 10); // ~100fps for smoother animation
}
function compareNumbers(a, b) {
	a = a.toString();
	b = b.toString();
	if (a.length < b.length) {
		return -1;
	}
	if (a.length > b.length) {
		return 1;
	}
	return a < b ? -1 : a > b ? 1 : 0;
}
function addLargeNumbers(a, b) {
	// Convert numbers to strings if they aren't already
	a = a.toString();
	b = b.toString();
	var result = '';
	var carry = 0;
	// Pad the shorter number with zeros
	while (a.length < b.length) {
		a = '0' + a;
	}
	while (b.length < a.length) {
		b = '0' + b;
	}
	// Add digits from right to left
	for (var i = a.length - 1; i >= 0; i--) {
		var sum = parseInt(a[i]) + parseInt(b[i]) + carry;
		carry = Math.floor(sum / 10);
		result = sum % 10 + result;
	}
	if (carry) {
		result = carry + result;
	}
	return result;
}
// Helper function to subtract large numbers as strings
function subtractLargeNumbers(a, b) {
	// Convert numbers to strings if they aren't already
	a = a.toString();
	b = b.toString();
	// If b is bigger than a, result would be negative
	if (b.length > a.length || b.length === a.length && b > a) {
		return "0";
	}
	var result = '';
	var borrow = 0;
	// Pad b with leading zeros to match a's length
	while (b.length < a.length) {
		b = '0' + b;
	}
	// Subtract digits from right to left
	for (var i = a.length - 1; i >= 0; i--) {
		var digitA = parseInt(a[i]);
		var digitB = parseInt(b[i]);
		// Handle borrowing
		if (borrow) {
			digitA--;
			borrow = 0;
		}
		// Need to borrow from next digit
		if (digitA < digitB) {
			digitA += 10;
			borrow = 1;
		}
		result = digitA - digitB + result;
	}
	// Remove leading zeros
	result = result.replace(/^0+/, '');
	return result || "0";
}
// Global ProgressManager
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 _classCallCheck(a, n) {
	if (!(a instanceof n)) {
		throw new TypeError("Cannot call a class as a function");
	}
}
function _defineProperties(e, r) {
	for (var t = 0; t < r.length; t++) {
		var o = r[t];
		o.enumerable = o.enumerable || !1, o.configurable = !0, "value" in o && (o.writable = !0), Object.defineProperty(e, _toPropertyKey(o.key), o);
	}
}
function _createClass(e, r, t) {
	return r && _defineProperties(e.prototype, r), t && _defineProperties(e, t), Object.defineProperty(e, "prototype", {
		writable: !1
	}), 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);
}
// Progress Management
function ProgressManager() {
	var self = this;
	self.maxHeartBeatsPerSecond = 8; // Add maxHeartBeatsPerSecond property
	self.tapMultiplier = 1; // Initialize tapMultiplier property
	self.priceIncreaseRate = 1.33; // Extracted price increase rate
	self.money = tapCount;
	self.generators = {};
	self.generatorCounts = {}; // Add a counter for each generator
	self.upgrades = {};
	self.currentTime = Date.now();
	self.currentLevel = 0;
	self.tapsPerLevel = TAPS_PER_LEVEL;
	self.lastUpdateTime = self.currentTime;
	self.updateGame = function () {
		if (isSpinWheelGamePlaying) {
			return;
		}
		if (isInfiniteMode) {
			return;
		}
		//log("ProgressManager updateGame...");
		var now = Date.now();
		var deltaTime = now - self.lastUpdateTime;
		var tempGenerated = 0;
		// Update generators
		Object.values(self.generators).forEach(function (generator) {
			var generated = generator.generate(deltaTime) * self.generatorCounts[generator.id];
			//log("generator => +" + generated);
			tempGenerated += Math.ceil(generated);
		});
		// Calculate beats based on generation rate
		var beatsToGenerate = Math.floor(tempGenerated / 2); // One beat every 10 taps generated
		beatsToGenerate = Math.min(beatsToGenerate, self.maxHeartBeatsPerSecond); // Cap at 5 beats per update to avoid overwhelming
		if (tempGenerated > 0) {
			self.money = addLargeNumbers(self.money, tempGenerated.toString());
			tapCount = self.money;
			targetTapCount = tapCount; // Update target for animation
			if (!isAnimatingCount) {
				isAnimatingCount = true;
				animateTapCount();
			}
		}
		bigHeart.highestScore = Math.max(bigHeart.highestScore, parseInt(tapCount)); // Update highestScore
		self.lastUpdateTime = now;
		// Save game status
		storage.tapCount = tapCount;
		storage.isInfiniteMode = isInfiniteMode;
		storage.lastUpdateTime = self.lastUpdateTime;
		storage.highestScore = bigHeart.highestScore; // Store highestScore
		storage.generators = Array.from({
			length: 10
		}, function (_, i) {
			return self.generatorCounts[i] || 0;
		});
		// Update progress bar based on current level progress
		if (!isInfiniteMode) {
			var startTap = self.currentLevel === 0 ? 0 : TAPS_PER_LEVEL[self.currentLevel - 1];
			var endTap = TAPS_PER_LEVEL[self.currentLevel];
			var progress = Math.min(1, (parseInt(tapCount) - startTap) / (endTap - startTap));
			progressBar.updateProgress(progress);
		}
		self.updateGeneratorsUi();
		// Trigger multiple beats based on generation rate
		if (tempGenerated > 0) {
			for (var i = 0; i < beatsToGenerate; i++) {
				LK.setTimeout(function () {
					bigHeart.animateBeat();
				}, i * 300);
			}
			bigHeart.animateFrames();
		}
		self.checkProgress(); // Check the progress
	};
	self.manualGeneration = function () {
		tapCount = addLargeNumbers(tapCount, self.tapMultiplier.toString());
		self.money = addLargeNumbers(self.money, self.tapMultiplier.toString());
		LK.getSound('pop').play(); // Play pop sound for manualGeneration
		log("manualGeneration Tap count: ", tapCount); // Log the tap count
		targetTapCount = tapCount; // Update target for animation
		if (!isAnimatingCount) {
			isAnimatingCount = true;
			animateTapCount();
		}
		bigHeart.highestScore = Math.max(bigHeart.highestScore, parseInt(tapCount)); // Update highestScore
		self.updateGeneratorsUi();
		self.checkProgress(); // Check for level up immediately
		// Update progress bar based on current level progress
		var startTap = self.currentLevel === 0 ? 0 : TAPS_PER_LEVEL[self.currentLevel - 1];
		var endTap = TAPS_PER_LEVEL[self.currentLevel];
		var progress = Math.min(1, (parseInt(tapCount) - startTap) / (endTap - startTap));
		progressBar.updateProgress(progress);
		if (!giftBoxManager.isStarted && unlockedGeneratorIndex >= 1) {
			giftBoxManager.isStarted = true;
			LK.setTimeout(function () {
				giftBoxManager.spawnGiftBox();
				giftBoxManager.start();
			}, fisrtBonusDeliveryDelay);
		}
	};
	self.checkProgress = function () {
		if (bigHeart.highestScore >= self.tapsPerLevel[self.currentLevel]) {
			if (self.currentLevel < 9) {
				// Ensure we don't exceed level 9
				var previousLevel = self.currentLevel;
				var previousFrames = bigHeart.heartFrames[previousLevel] || [];
				previousFrames.forEach(function (frame) {
					if (frame && frame._activeTween) {
						frame._activeTween.stop();
						frame._activeTween = null;
					}
					frame.visible = false;
				});
				background.changeBackground(progressManager.currentLevel + 1); // Change background when level changes
				bigHeart.animateExplosion(function () {
					self.currentLevel++;
					bigHeart.heartType = self.currentLevel;
					bigHeart.currentGraphic = bigHeart.heartFrames[bigHeart.heartType][5];
					bigHeart.nextGraphic = bigHeart.heartFrames[bigHeart.heartType][4];
					bigHeart.currentGraphic.visible = true;
					bigHeart.nextGraphic.visible = true;
					projectionsManager.updateHeartType(bigHeart.heartType);
					tween(bigHeart.currentGraphic, {
						scaleX: 1.1,
						scaleY: 1.1
					}, {
						duration: 300,
						easing: tween.easeOut
					});
					tween(bigHeart.nextGraphic, {
						scaleX: 1.1,
						scaleY: 1.1
					}, {
						duration: 300,
						easing: tween.easeOut
					});
				});
			} else {
				isInfiniteMode = true;
				bigHeart.visible = false; // Hide the BigHeart
				giftRain.visible = false; // Hide the GiftRain
				progressBar.visible = false;
				// Play tada sound
				LK.getSound('tada').play();
				LK.playMusic('infiniteMusic');
				// Animate rightBoard to the right border of the screen
				tween(rightBoard, {
					x: 2048 + rightBoard.width // Move to the right border
				}, {
					duration: 500,
					// Duration of the animation
					easing: tween.easeOut // Easing function for smooth effect
				});
				// Change background to 10
				background.changeBackground(10);
				loveField = new LoveField();
				middlegroundContainer.addChild(loveField);
				if (!resetButton) {
					LK.setTimeout(function () {
						resetButton = new ResetButton();
						resetButton.x = 120;
						resetButton.y = 2650;
						foregroundContainer.addChild(resetButton);
					}, 10000);
				}
			}
		}
	};
	self.computeIdleGeneration = function () {
		if (isInfiniteMode) {
			return;
		}
		var now = Date.now();
		var deltaTime = now - storage.lastUpdateTime; // Use storage lastUpdateTime here
		var tempGenerated = 0;
		// Update generators
		Object.values(self.generators).forEach(function (generator) {
			var generated = generator.generate(deltaTime) * self.generatorCounts[generator.id];
			log("generator => +" + generated);
			tempGenerated += Math.ceil(generated);
		});
		if (tempGenerated > 0) {
			self.money = addLargeNumbers(self.money, tempGenerated.toString());
			tapCount = self.money;
		}
		self.lastUpdateTime = now;
		storage.lastUpdateTime = now;
	};
	self.buyGenerator = function (generatorId, isFree) {
		var generatorConfig = Object.values(GENERATORS).find(function (g) {
			return g.id === generatorId;
		});
		if (!generatorConfig) {
			log("Generator not found");
			return false;
		}
		// Check if player has enough money
		if (!isFree && parseInt(tapCount) < generatorConfig.cost) {
			log("Not enough money");
			return false;
		}
		// Initialize generator if it doesn't exist
		if (!self.generators[generatorId]) {
			self.generators[generatorId] = new Generator(generatorConfig);
			self.generatorCounts[generatorId] = 0;
		}
		// Subtract cost using subtractLargeNumbers
		if (!isFree) {
			self.money = subtractLargeNumbers(self.money, generatorConfig.cost.toString());
			tapCount = self.money;
			// Update displayed count immediately without animation for price reductions
			displayedTapCount = tapCount;
			targetTapCount = tapCount;
			updateTapCountText();
		}
		// Calculate new cost
		var currentCount = self.generatorCounts[generatorId] || 0;
		var baseCost = GENERATORS[Object.keys(GENERATORS)[generatorId]].cost;
		var newCost = Math.floor(baseCost * Math.pow(self.priceIncreaseRate, currentCount + 1));
		log("Updating costs - Base:", baseCost, "Count:", currentCount, "New Cost:", newCost);
		// Update costs
		//generatorConfig.cost = newCost;
		if (!isFree) {
			rightBoard.generatorButtons[generatorId].config.cost = newCost;
		}
		self.generatorCounts[generatorId]++; // Increment the count for the generator 
		rightBoard.generatorButtons[generatorId].countText.setText(self.generatorCounts[generatorId].toString());
		log("Generator", generatorId, "Count:", self.generatorCounts[generatorId], "New Cost:", newCost);
		LK.getSound('buyGenerator').play();
		popMessage(generatorConfig.name + "\r\n+" + generatorConfig.clickRate + " love/sec");
		giftRain.increaseSpawnRate(generatorId);
		if (generatorId === GENERATORS.INFINITE_LOVE.id) {
			isInfiniteMode = true;
			bigHeart.visible = false; // Hide the BigHeart
			giftRain.visible = false; // Hide the GiftRain
			// Play tada sound
			LK.getSound('tada').play();
			LK.playMusic('infiniteMusic');
			// Animate rightBoard to the right border of the screen
			tween(rightBoard, {
				x: 2048 + rightBoard.width // Move to the right border
			}, {
				duration: 500,
				// Duration of the animation
				easing: tween.easeOut // Easing function for smooth effect
			});
			// Change background to 10
			background.changeBackground(10);
			loveField = new LoveField();
			middlegroundContainer.addChild(loveField);
		}
		// Update the tapMultiplier based on the generator index
		if (!isFree) {
			self.tapMultiplier = Math.max(self.tapMultiplier, Math.pow(2, generatorId + 1));
		}
		return true;
	};
	self.buyUpgrade = function (upgradeId, generatorId) {
		var upgradeConfig = Object.values(UPGRADES).find(function (u) {
			return u.id === upgradeId;
		});
		var targetGenerator = self.generators[generatorId];
		if (!upgradeConfig || !targetGenerator) {
			throw new Error("Upgrade or Generator not found");
		}
		if (parseInt(tapCount) < upgradeConfig.cost) {
			return false;
		}
		// Subtract cost using subtractLargeNumbers
		self.money = subtractLargeNumbers(self.money, upgradeConfig.cost.toString());
		tapCount = self.money;
		// Update displayed count immediately without animation for price reductions
		displayedTapCount = tapCount;
		targetTapCount = tapCount;
		updateTapCountText();
		var upgrade = new Upgrade(upgradeConfig);
		upgrade.apply(targetGenerator);
		self.upgrades[upgradeId] = upgrade;
		return true;
	};
	self.updateGeneratorsUi = function () {
		// Update generator button visibility based on money
		//log("Updating Generators UI");
		rightBoard.generatorButtons.forEach(function (button, index) {
			var generatorConfig = rightBoard.generatorButtons[index].config;
			rightBoard.generatorButtons[index].costText.setText(formatGeneratorPrice(generatorConfig.cost)); // Update the cost text to reflect the new exponential cost
			if (generatorConfig) {
				// Store a flag 'wasShown' in generatorButton when displayed
				if (parseInt(tapCount) >= generatorConfig.cost * 0.75) {
					if (!button.wasShown) {
						button.x = 2048 + button.width; // Start from the right border
						tween(button, {
							x: 0
						}, {
							duration: 500,
							easing: tween.easeOut
						}); // Animate entrance
						// Enable swipe when generator 5 becomes visible
						if (!allowSwipe && index === 5) {
							allowSwipe = true;
						}
						unlockedGeneratorIndex = Math.max(index, unlockedGeneratorIndex);
					}
					// Show generator if player has at least 75% of its cost
					button.wasShown = true;
					button.visible = true;
					// Set alpha to 0.75 if player can't buy the generator
					button.alpha = parseInt(tapCount) >= generatorConfig.cost ? 1 : 0.6;
				} else {
					button.visible = button.wasShown;
					button.alpha = 0.6;
					// If the button has been moved out of its normal position, tween it back to x = 0
					if (button.x !== 0) {
						tween(button, {
							x: 0
						}, {
							duration: 200,
							easing: tween.easeOut
						});
					}
				}
			} else {
				button.visible = false;
			}
		});
	};
}
function initializeGenerators(storedGenerators) {
	var result = {
		generatorCounts: {},
		generators: {}
	};
	if (Array.isArray(storedGenerators)) {
		for (var index = 0; index < storedGenerators.length; index++) {
			log("Adding loaded generator ", index, " => ", storedGenerators[index]);
			var count = storedGenerators[index];
			result.generatorCounts[index] = count;
			// Initialize generator if count > 0
			if (count > 0) {
				var generatorKey = Object.keys(GENERATORS)[index];
				var config = GENERATORS[generatorKey];
				config.id = index;
				result.generators[index] = new Generator(config);
			}
		}
	} else {
		log("No previous generators");
	}
	return result;
}
function Generator(config) {
	var self = this;
	self.id = config.id;
	self.name = config.name;
	self.description = config.description;
	self.autoClick = config.autoClick;
	self.clickRate = config.clickRate;
	self.cost = config.cost;
	self.upgradeLevel = config.upgradeLevel;
	self.generate = function (deltaTime) {
		if (!self.autoClick) {
			return 0;
		}
		var clickAmount = self.clickRate * deltaTime / 1000;
		return clickAmount * Math.pow(2, self.upgradeLevel);
	};
	self.currentMultiplier = Math.pow(2, self.upgradeLevel);
	self.manualGenerate = function () {
		return 1 * self.currentMultiplier;
	};
	self.upgrade = function (upgradeMultiplier) {
		self.upgradeLevel++;
	};
}
function Upgrade(config) {
	var self = this;
	self.id = config.id;
	self.name = config.name;
	self.description = config.description;
	self.targetGenerator = config.targetGenerator;
	self.multipliers = config.multipliers;
	self.cost = config.cost;
	self.currentLevel = 0;
	self.apply = function (generator) {
		if (self.currentLevel < self.multipliers.length) {
			generator.upgrade(self.multipliers[self.currentLevel]);
			self.currentLevel++;
		}
	};
}
function initializeGame() {
	tapCount = storage.tapCount || "0";
	tapCount = isDebug ? 0 : tapCount;
	displayedTapCount = tapCount; // Initialize displayed count
	targetTapCount = tapCount; // Initialize target count
	isInfiniteMode = storage.isInfiniteMode || false;
	//storage.isInfiniteMode = isInfiniteMode = false;
	//storage.isInfiniteMode = isInfiniteMode = true;
	var storedGenerators = storage.generators || [0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
	log("Loaded tapCount from storage:", tapCount);
	log("Loaded isInfiniteMode from storage:", isInfiniteMode);
	log("Loaded generators from storage:", storedGenerators);
	bigHeart = new BigHeart();
	backgroundContainer = new Container();
	giftsContainer = new Container();
	middlegroundContainer = new Container();
	foregroundContainer = new Container();
	game.addChild(backgroundContainer);
	game.addChild(giftsContainer);
	game.addChild(middlegroundContainer);
	game.addChild(foregroundContainer);
	background = new Background(); // Create a Background instance
	backgroundContainer.addChild(background); // Add Background instance to the backgroundContainer
	projectionsManager = backgroundContainer.addChild(new Projections()); // Place projectionsManager in backgroundContainer
	progressManager = new ProgressManager();
	//progressManager.lastUpdateTime = storage.lastUpdateTime || Date.now();
	var initResult = initializeGenerators(storedGenerators);
	progressManager.generatorCounts = initResult.generatorCounts;
	progressManager.generators = initResult.generators;
	progressManager.computeIdleGeneration(); // Calculate offline earnings
	// Determine heart type based on tapCount
	for (var level = 9; level > 0; level--) {
		// 0 < 50  < 99 => 0
		// 99 < 110 < 999 => 1
		if (tapCount >= TAPS_PER_LEVEL[level - 1]) {
			progressManager.currentLevel = level;
			break;
		}
	}
	// Initialize gift rain with existing generator counts
	giftRain = new GiftRain();
	giftsContainer.addChild(giftRain);
	giftBoxManager = new GiftBoxManager();
	log("unlockedGeneratorIndex=", unlockedGeneratorIndex);
	if (unlockedGeneratorIndex >= 1) {
		LK.setTimeout(function () {
			giftBoxManager.isStarted = true;
			giftBoxManager.spawnGiftBox();
			giftBoxManager.start();
		}, fisrtBonusDeliveryDelay);
	}
	middlegroundContainer.addChild(giftBoxManager);
	for (var generatorId in progressManager.generatorCounts) {
		var count = progressManager.generatorCounts[generatorId];
		log("Initializing gifts for generator", generatorId, "count:", count);
		for (var i = 0; i < count; i++) {
			giftRain.increaseSpawnRate(parseInt(generatorId));
		}
	}
	background.changeBackground(progressManager.currentLevel);
	// Add a big heart at the center of the screen
	bigHeart.initHeart();
	if (!isInfiniteMode) {
		LK.playMusic('bgMusic');
	} else {
		if (tapCountText) {
			tapCountText.setText('LOVE\\r\\nβ');
		}
	}
	middlegroundContainer.addChild(bigHeart);
	projectionsManager.updateHeartType(bigHeart.heartType);
	progressBar = new ProgressBar();
	foregroundContainer.addChild(progressBar);
	// Add a RightBoard instance to the foreground container
	rightBoard = new RightBoard();
	foregroundContainer.addChild(rightBoard);
	rightBoard.generatorButtons.forEach(function (button, index) {
		var count = progressManager.generatorCounts[index] || 0;
		var baseCost = GENERATORS[Object.keys(GENERATORS)[index]].cost;
		var newCost = Math.floor(baseCost * Math.pow(progressManager.priceIncreaseRate, count));
		button.config = {
			count: count,
			cost: newCost
		};
	});
	// Update generator counts in UI
	for (var genId in progressManager.generatorCounts) {
		if (progressManager.generatorCounts[genId] > 0) {
			rightBoard.generatorButtons[genId].countText.setText(progressManager.generatorCounts[genId].toString());
		}
	}
	// Create a text object to display tapCount
	tapCountText = new Text2('LOVE\r\n ', {
		size: 140,
		fill: 0xFFFFFF,
		dropShadow: true,
		align: 'center'
	});
	// Center the text horizontally, anchor point set at the middle of its top edge.
	tapCountText.anchor.set(0.5, 0);
	// Position the text at the top-center of the screen.
	LK.gui.top.addChild(tapCountText);
	if (isInfiniteMode) {
		tapCountText.setText('LOVE\r\nβ');
	}
	// Create a text object to display messages
	messageText = new Text2('', {
		size: 100,
		fill: 0xFFFFFF,
		dropShadow: true,
		align: 'center'
	});
	messageText.anchor.set(0.5, 0);
	messageText.x = 1024;
	messageText.y = 2500;
	foregroundContainer.addChild(messageText);
	if (isInfiniteMode) {
		resetButton = new ResetButton();
		resetButton.x = 120;
		resetButton.y = 2650;
		foregroundContainer.addChild(resetButton);
	}
	// Start updtes 
	bigHeart.down();
	if (tapCount >= 10) {
		popMessage("Welcome back my love!");
	} else {
		popMessage("Welcome my love!");
	}
	var intervalId = LK.setInterval(function () {
		progressManager.updateGame();
	}, 1000);
	checkInfiniteMode();
}
function checkInfiniteMode() {
	if (isInfiniteMode) {
		bigHeart.visible = false; // Hide the BigHeart
		giftRain.visible = false; // Hide the GiftRain
		// Play tada sound
		LK.getSound('tada').play();
		LK.playMusic('infiniteMusic');
		// Animate rightBoard to the right border of the screen
		tween(rightBoard, {
			x: 2048 + rightBoard.width // Move to the right border
		}, {
			duration: 500,
			// Duration of the animation
			easing: tween.easeOut // Easing function for smooth effect
		});
		// Change background to 10
		background.changeBackground(10);
		loveField = new LoveField();
		middlegroundContainer.addChild(loveField);
		// Update tapCountText to display 'LOVE\\r\\nβ'
		tapCountText.setText('LOVE\r\nβ');
	}
}
initializeGame();
:quality(85)/https://cdn.frvr.ai/6795575b3e37286b7983d8ac.png%3F3) 
 a big lovely heart
:quality(85)/https://cdn.frvr.ai/679608d4b242d70a84f2bb06.png%3F3) 
 a big stone heart
:quality(85)/https://cdn.frvr.ai/67960abbb242d70a84f2bb12.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/6796547cd63d184aa47a9fd2.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67965648b242d70a84f2bba2.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/679656e4b242d70a84f2bbab.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/679657b8b242d70a84f2bbae.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/679972141f2a754c4618425f.png%3F3) 
 a big used copper heart
:quality(85)/https://cdn.frvr.ai/679972ff1f2a754c46184275.png%3F3) 
 face view of a big bronze heart
:quality(85)/https://cdn.frvr.ai/679973ca1f2a754c46184297.png%3F3) 
 face view of a big silver heart
:quality(85)/https://cdn.frvr.ai/679975c71f2a754c461842dc.png%3F3) 
 Big shining gold heart verly slightly ornate. face view.
:quality(85)/https://cdn.frvr.ai/6799770a1f2a754c461842fe.png%3F3) 
 Big precious shiny porcelain heart slightly ornate. face view.
:quality(85)/https://cdn.frvr.ai/679978441f2a754c46184320.png%3F3) 
 Large precious heart in mother-of-pearl, lightly ornate. Front view.
:quality(85)/https://cdn.frvr.ai/679979061f2a754c4618432d.png%3F3) 
 Large heart in precious ruby, very lightly decorated. Front view.
:quality(85)/https://cdn.frvr.ai/6799799f1f2a754c4618433b.png%3F3) 
 The most precious large heart in diamond, Front view.
:quality(85)/https://cdn.frvr.ai/679aa2340aab9af650c0f77a.png%3F3) 
 clean pink enamel board witha very thin border
:quality(85)/https://cdn.frvr.ai/679aba2fc311aaae2198e5ef.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67a8a18b68c182ca2db2ab01.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67a8d8928cabef597a6d193b.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67a8d8ef8cabef597a6d1941.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67a8d90a8cabef597a6d1946.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67a8d9458cabef597a6d194b.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67a8d9848cabef597a6d1951.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67a8dae18cabef597a6d1960.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67a8dafa8cabef597a6d196a.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67a8db3f8cabef597a6d1979.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67a8db768cabef597a6d197e.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67a8ec3dbadf5001415a6126.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67a925e8badf5001415a61ab.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67a9283fbadf5001415a61b3.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67a92880badf5001415a61b6.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67a928b2badf5001415a61ba.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67a928cbbadf5001415a61be.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67a92b80badf5001415a6205.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67a92bbfbadf5001415a620a.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67a932d7badf5001415a6238.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67a99decfea36315b70304ce.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67aa5fb1766195a6aae38f18.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67aa600a766195a6aae38f1b.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67aa607e766195a6aae38f20.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67aa6598766195a6aae38f23.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67aa67cb766195a6aae38f26.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67aced0aec046e6574e00711.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67aced7eec046e6574e00717.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67acee3eec046e6574e0071d.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67aceea3ec046e6574e00720.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67acef19ec046e6574e00723.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67acef97ec046e6574e00726.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67acfa32ec046e6574e00742.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67acfbaeec046e6574e00745.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67acfc0fec046e6574e00748.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67acfc89ec046e6574e0074b.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67acfcd0ec046e6574e0074e.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67acfd17ec046e6574e00751.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67acfd8dec046e6574e00756.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67acfdf3ec046e6574e00759.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67acfe5aec046e6574e0075c.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67ad193ae366fb4797fc375d.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67ad199fe366fb4797fc3761.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67ad1d78e366fb4797fc379e.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67ad1de1e366fb4797fc37ac.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67ad1e70e366fb4797fc37c3.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67ae16b0752b0d388fcd9444.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67ae16ef752b0d388fcd9447.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67ae1735752b0d388fcd944d.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67ae17a5752b0d388fcd9450.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67ae1804752b0d388fcd9453.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67ae1833752b0d388fcd9456.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67ae189e752b0d388fcd9459.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67ae18ea752b0d388fcd945c.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67ae193c752b0d388fcd945f.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67ae199c752b0d388fcd9462.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67ae19d7752b0d388fcd9465.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67ae1a09752b0d388fcd9468.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67ae1a64752b0d388fcd946e.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67ae1abc752b0d388fcd9471.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67ae1b58752b0d388fcd9474.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67ae1b86752b0d388fcd9477.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67ae1bb7752b0d388fcd947a.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67ae1c23752b0d388fcd9480.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67ae1c72752b0d388fcd9483.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67ae1ced752b0d388fcd9486.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67d2f1ee249940bb44cc026a.png%3F3) 
 beautifull red gift box.
:quality(85)/https://cdn.frvr.ai/67d57da988248ab681d8fb6f.png%3F3) 
 black plastic 3d triangle. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
:quality(85)/https://cdn.frvr.ai/67d668f253da059024b02edb.png%3F3) 
 basic red horizontal rectangle button with white text "RESET".