User prompt
Update with: if (category === 'colors' && upgrade.currentLevel > 0) { // Toggle this color as active if (UPGRADE_CONFIG.gameSettings.activeColor === key) { // If already active, switch to auto UPGRADE_CONFIG.gameSettings.activeColor = "auto"; game.showMessage("Auto color mode"); } else { // Otherwise activate this color UPGRADE_CONFIG.gameSettings.activeColor = key; game.showMessage(upgrade.name + " activated"); } // Refresh the tab to update the text displays refreshUpgradeTab('colors'); return true; }
User prompt
Update with: var cost = getUpgradeCost(upgrade); var costText; // Check if this is an active color first var isActiveColor = false; if (category === 'colors' && upgrade.currentLevel > 0) { var activeColorKey = getActiveColorKey(); if (key === activeColorKey || (UPGRADE_CONFIG.gameSettings.activeColor === "auto" && key === activeColorKey)) { isActiveColor = true; } } if (isActiveColor) { // Create "ACTIVE" text for active color costText = new Text2("ACTIVE", { size: 96, fill: 0x00FF00, // Green color stroke: 0x000000, strokeThickness: 2, font: "Impact" }); } else if (category === 'colors' && upgrade.currentLevel >= upgrade.maxLevel) { // Create "SOLD OUT" text for maxed out color upgrades // (rest of the existing conditions stay the same) }
User prompt
Update as needed with: if (tabName === 'colors') { var activeColorKey = getActiveColorKey(); tabColumns[tabName].left.concat(tabColumns[tabName].right).forEach(function (upgrade) { var category = upgrade[0]; var key = upgrade[1]; var upgradeConfig = UPGRADE_CONFIG[category][key]; if (upgradeConfig.currentLevel > 0) { // Find the corresponding cost text for this upgrade tabContainers[tabName].children.forEach(function (child) { // Identify the cost text by its position below the name if (child.text && child.y > 0) { var nameText = tabContainers[tabName].children.find(function (c) { return c.text === upgradeConfig.name; }); if (nameText && child.y > nameText.y && child.y < nameText.y + 150) { // Check if this color is active if (activeColorKey === key || (UPGRADE_CONFIG.gameSettings.activeColor === "auto" && key === activeColorKey)) { child.setText("ACTIVE"); child.fill = 0x00FF00; // Green } else { // Otherwise show SOLD OUT if maxed if (upgradeConfig.currentLevel >= upgradeConfig.maxLevel) { child.setText("SOLD OUT"); child.fill = 0x888888; // Gray } else { var cost = getUpgradeCost(upgradeConfig); child.setText(cost + " BP"); child.fill = 0xFFFF00; // Yellow } } } } }); } }); }
User prompt
Update with: // Add this for colors when they're first purchased: if (category === 'colors' && upgrade.currentLevel === 1) { // If this is the first color purchased or auto is selected, make it active if (UPGRADE_CONFIG.gameSettings.activeColor === "auto" || UPGRADE_CONFIG.colors[UPGRADE_CONFIG.gameSettings.activeColor].currentLevel === 0) { UPGRADE_CONFIG.gameSettings.activeColor = key; game.showMessage(upgrade.name + " activated"); } refreshUpgradeTab('colors'); return true; }
User prompt
Update with: if (category === 'colors' && upgrade.currentLevel > 0) { // Toggle this color as active if (UPGRADE_CONFIG.gameSettings.activeColor === key) { // If already active, switch to auto UPGRADE_CONFIG.gameSettings.activeColor = "auto"; game.showMessage("Auto color mode"); // Update color indicator refreshUpgradeTab('colors'); } else { // Otherwise activate this color UPGRADE_CONFIG.gameSettings.activeColor = key; game.showMessage(upgrade.name + " activated"); // Update color indicator refreshUpgradeTab('colors'); } return true; }
User prompt
Please fix the bug: 'Script error.' in or related to this line: 'costText.setFill(0x00FF00); // Change to green' Line Number: 1682
User prompt
Update with: // In refreshUpgradeTab function, replace the "ACTIVE" text creation logic // Around line 3028 in the provided code if (tabName === 'colors') { var activeColorKey = getActiveColorKey(); tabColumns[tabName].left.concat(tabColumns[tabName].right).forEach(function (upgrade) { var category = upgrade[0]; var key = upgrade[1]; var upgradeConfig = UPGRADE_CONFIG[category][key]; if (upgradeConfig.currentLevel > 0) { // Find the cost text for this upgrade tabContainers[tabName].children.forEach(function (child) { // Find the cost text (the text below the upgrade name) if (child.text && (child.text.includes("BP") || child.text === "SOLD OUT") && child.y > tabContainers[tabName].children.find(function (c) { return c.text === upgradeConfig.name; }).y) { // If this color is active, change cost text to "ACTIVE" if (activeColorKey === key || (UPGRADE_CONFIG.gameSettings.activeColor === "auto" && key === activeColorKey)) { child.setText("ACTIVE"); child.setFill(0x00FF00); // Green } else { // Otherwise show "SOLD OUT" or the BP cost if (upgradeConfig.currentLevel >= upgradeConfig.maxLevel) { child.setText("SOLD OUT"); child.setFill(0x888888); // Gray } else { child.setText(getUpgradeCost(upgradeConfig) + " BP"); child.setFill(0xFFFF00); // Yellow } } } }); } }); }
User prompt
Update with: // Change inside the hitContainer.down function in createUpgradeText // (Around line 3090, within the color upgrade section) if (category === 'colors' && upgrade.currentLevel > 0) { // Toggle this color as active if (UPGRADE_CONFIG.gameSettings.activeColor === key) { // If already active, switch to auto UPGRADE_CONFIG.gameSettings.activeColor = "auto"; game.showMessage("Auto color mode"); costText.setText(cost + " BP"); // Reset to cost costText.setFill(0xFFFF00); // Reset to yellow } else { // Otherwise activate this color UPGRADE_CONFIG.gameSettings.activeColor = key; game.showMessage(upgrade.name + " activated"); costText.setText("ACTIVE"); // Change to active costText.setFill(0x00FF00); // Change to green } // No need to add a separate active indicator text return true; }
User prompt
Update with: if (UPGRADE_CONFIG.gameSettings.activeColor === key) { // If already active, switch to auto UPGRADE_CONFIG.gameSettings.activeColor = "auto"; game.showMessage("Auto color mode"); } else { // Otherwise activate this color UPGRADE_CONFIG.gameSettings.activeColor = key; game.showMessage(upgrade.name + " activated"); } // Fix indicators after toggling LK.setTimeout(function () { fixColorIndicators(); }, 50);
User prompt
Update with: if (category === 'colors') { var wasFirstPurchase = upgrade.currentLevel === 0; upgrade.currentLevel++; game.bp -= cost; bpText.setText(formatBP(game.bp) + " BP"); // Auto-activate the first color purchased if (wasFirstPurchase && (UPGRADE_CONFIG.gameSettings.activeColor === "auto" || !UPGRADE_CONFIG.colors[UPGRADE_CONFIG.gameSettings.activeColor] || UPGRADE_CONFIG.colors[UPGRADE_CONFIG.gameSettings.activeColor].currentLevel === 0)) { UPGRADE_CONFIG.gameSettings.activeColor = key; game.showMessage(upgrade.name + " activated"); } // Refresh the tab with a slight delay to ensure elements are created refreshUpgradeTab('colors'); // Fix the indicators with an explicit longer delay LK.setTimeout(function () { fixColorIndicators(); }, 50); return true; }
User prompt
Update with: // For colors tab, set up active indicator with a delay if (tabName === 'colors') { // Call after a slight delay to ensure all elements are created LK.setTimeout(function () { fixColorIndicators(); }, 30); // Longer delay for safety }
User prompt
Add this: // Add this function after the addActiveColorIndicator function function fixColorIndicators() { // First remove all active indicators var indicatorsToRemove = []; tabContainers.colors.children.forEach(function (child) { if (child.text && child.text === "ACTIVE") { indicatorsToRemove.push(child); } }); indicatorsToRemove.forEach(function (indicator) { indicator.destroy(); }); // Now add just one indicator for the active color var activeColorKey = getActiveColorKey(); if (activeColorKey === "auto" || !UPGRADE_CONFIG.colors[activeColorKey]) { return; } // Find the name element for this color var targetName = UPGRADE_CONFIG.colors[activeColorKey].name; var nameElement = null; // Search through all children to find the name text tabContainers.colors.children.forEach(function (child) { if (child.text === targetName) { nameElement = child; } }); if (nameElement) { var activeText = new Text2("ACTIVE", { size: 64, fill: 0x00FF00, stroke: 0x000000, strokeThickness: 2, font: "Impact" }); activeText.x = nameElement.x + 150; activeText.y = nameElement.y; tabContainers.colors.addChild(activeText); } }
User prompt
Replace with: function addActiveColorIndicator() { var activeColorKey = getActiveColorKey(); // Only proceed if we have a valid color if (activeColorKey === "auto" || !UPGRADE_CONFIG.colors[activeColorKey]) { return; } // Remove any existing ACTIVE indicators first var indicatorsToRemove = []; tabContainers.colors.children.forEach(function (child) { if (child.text && child.text === "ACTIVE") { indicatorsToRemove.push(child); } }); // Remove indicators in separate loop to avoid modification during iteration indicatorsToRemove.forEach(function (indicator) { indicator.destroy(); }); // Use a more direct approach to find the target element var targetName = UPGRADE_CONFIG.colors[activeColorKey].name; var nameElements = []; tabContainers.colors.children.forEach(function (child) { if (child.text === targetName) { nameElements.push(child); } }); if (nameElements.length > 0) { // Use the first found element var activeText = new Text2("ACTIVE", { size: 64, fill: 0x00FF00, stroke: 0x000000, strokeThickness: 2, font: "Impact" }); activeText.x = nameElements[0].x + 150; activeText.y = nameElements[0].y; tabContainers.colors.addChild(activeText); console.log("Added ACTIVE indicator for", activeColorKey); } else { console.log("Could not find element for", targetName); } }
Code edit (1 edits merged)
Please save this source code
User prompt
Update with: // For colors tab, set up active indicator with a delay if (tabName === 'colors') { // Call after a slight delay to ensure all elements are created LK.setTimeout(function() { addActiveColorIndicator(); }, 20); // Increase from 1 to 20 }
User prompt
Replace with: function addActiveColorIndicator() { // Delay execution to ensure all UI elements are fully created LK.setTimeout(function() { var activeColorKey = getActiveColorKey(); // Don't proceed for auto mode if (activeColorKey === "auto" || !UPGRADE_CONFIG.colors[activeColorKey]) { return; } console.log("Adding indicator for", activeColorKey); // First remove ALL existing ACTIVE indicators var indicatorsToRemove = []; tabContainers.colors.children.forEach(function(child) { if (child.text && child.text === "ACTIVE") { indicatorsToRemove.push(child); } }); indicatorsToRemove.forEach(function(indicator) { indicator.destroy(); }); // Get the target name for the active color var targetName = UPGRADE_CONFIG.colors[activeColorKey].name; // Find all name elements - look for exact textual match var nameElement = null; tabContainers.colors.children.forEach(function(child) { if (child.text === targetName) { nameElement = child; } }); if (nameElement) { var activeText = new Text2("ACTIVE", { size: 64, fill: 0x00FF00, stroke: 0x000000, strokeThickness: 2, font: "Impact" }); activeText.x = nameElement.x + 150; activeText.y = nameElement.y; tabContainers.colors.addChild(activeText); console.log("Added ACTIVE indicator for", targetName, "at", nameElement.x, nameElement.y); } else { console.log("Could not find element for", targetName); } }, 20); // Increased delay for safer execution }
User prompt
Update with: // Inside hitContainer.down function in the createUpgradeText function, // after refreshUpgradeTab('colors') call: // Clear any existing ACTIVE indicators first var indicatorsToRemove = []; tabContainers.colors.children.forEach(function(child) { if (child.text && child.text === "ACTIVE") { indicatorsToRemove.push(child); } }); indicatorsToRemove.forEach(function(indicator) { indicator.destroy(); }); // Add active indicator with sufficient delay LK.setTimeout(function() { var targetKey = getActiveColorKey(); if (targetKey && targetKey !== "auto") { var targetName = UPGRADE_CONFIG.colors[targetKey].name; var nameElement = null; tabContainers.colors.children.forEach(function(child) { if (child.text === targetName) { nameElement = child; } }); if (nameElement) { var activeText = new Text2("ACTIVE", { size: 64, fill: 0x00FF00, stroke: 0x000000, strokeThickness: 2, font: "Impact" }); activeText.x = nameElement.x + 150; activeText.y = nameElement.y; tabContainers.colors.addChild(activeText); } } }, 10); // Increased delay to ensure UI elements are ready
User prompt
update with: function refreshUpgradeTab(tabName) { // Clear all children from the tab container while (tabContainers[tabName].children.length > 0) { tabContainers[tabName].children[0].destroy(); } // Recreate all upgrades for the tab if (tabColumns[tabName] && tabColumns[tabName].left) { tabColumns[tabName].left.forEach(function(upgrade, index) { createUpgradeText(upgrade[0], upgrade[1], index, true, tabName); }); } if (tabColumns[tabName] && tabColumns[tabName].right) { tabColumns[tabName].right.forEach(function(upgrade, index) { createUpgradeText(upgrade[0], upgrade[1], index, false, tabName); }); } // For colors tab, set up active indicator with a delay if (tabName === 'colors') { // Call after a slight delay to ensure all elements are created LK.setTimeout(function() { addActiveColorIndicator(); }, 1); } }
User prompt
update with: // In the hitContainer.down function part of createUpgradeText: // Replace the purchase logic for colors with: if (category === 'colors') { var wasFirstPurchase = upgrade.currentLevel === 0; upgrade.currentLevel++; game.bp -= cost; bpText.setText(formatBP(game.bp) + " BP"); // Auto-activate the first color purchased if (wasFirstPurchase && (UPGRADE_CONFIG.gameSettings.activeColor === "auto" || !UPGRADE_CONFIG.colors[UPGRADE_CONFIG.gameSettings.activeColor] || UPGRADE_CONFIG.colors[UPGRADE_CONFIG.gameSettings.activeColor].currentLevel === 0)) { UPGRADE_CONFIG.gameSettings.activeColor = key; game.showMessage(upgrade.name + " activated"); } // Refresh the tab with a slight delay to ensure elements are created refreshUpgradeTab('colors'); // Add a longer timeout to ensure elements are fully created LK.setTimeout(function() { addActiveColorIndicator(); }, 5); return true; }
User prompt
update with: function addActiveColorIndicator() { var activeColorKey = getActiveColorKey(); // Only proceed if we have a valid color if (activeColorKey === "auto" || !UPGRADE_CONFIG.colors[activeColorKey]) { return; } // Remove any existing ACTIVE indicators first var indicatorsToRemove = []; tabContainers.colors.children.forEach(function(child) { if (child.text && child.text === "ACTIVE") { indicatorsToRemove.push(child); } }); // Remove indicators in separate loop to avoid modification during iteration indicatorsToRemove.forEach(function(indicator) { indicator.destroy(); }); // Use a more direct approach to find the target element var targetName = UPGRADE_CONFIG.colors[activeColorKey].name; var nameElements = []; tabContainers.colors.children.forEach(function(child) { if (child.text === targetName) { nameElements.push(child); } }); if (nameElements.length > 0) { // Use the first found element var activeText = new Text2("ACTIVE", { size: 64, fill: 0x00FF00, stroke: 0x000000, strokeThickness: 2, font: "Impact" }); activeText.x = nameElements[0].x + 150; activeText.y = nameElements[0].y; tabContainers.colors.addChild(activeText); console.log("Added ACTIVE indicator for", activeColorKey); } else { console.log("Could not find element for", targetName); } }
User prompt
Update with: function addActiveColorIndicator() { var activeColorKey = getActiveColorKey(); // Only proceed if we have a valid color if (activeColorKey === "auto" || !UPGRADE_CONFIG.colors[activeColorKey]) return; // Remove any existing ACTIVE indicators first var indicatorsToRemove = []; tabContainers.colors.children.forEach(function(child) { if (child.text === "ACTIVE") { indicatorsToRemove.push(child); } }); // Remove indicators in separate loop to avoid modification during iteration indicatorsToRemove.forEach(function(indicator) { indicator.destroy(); }); // Use a more direct approach to find the target element var targetName = UPGRADE_CONFIG.colors[activeColorKey].name; var nameElements = []; tabContainers.colors.children.forEach(function(child) { if (child.text === targetName) { nameElements.push(child); } }); if (nameElements.length > 0) { // Use the first found element var activeText = new Text2("ACTIVE", { size: 64, fill: 0x00FF00, stroke: 0x000000, strokeThickness: 2, font: "Impact" }); activeText.x = nameElements[0].x + 150; activeText.y = nameElements[0].y; tabContainers.colors.addChild(activeText); console.log("Added ACTIVE indicator for", activeColorKey); } else { console.log("Could not find element for", targetName); } }
User prompt
Update with: // In the color purchase section: if (category === 'colors') { var wasFirstPurchase = (upgrade.currentLevel === 0); upgrade.currentLevel++; game.bp -= cost; bpText.setText(formatBP(game.bp) + " BP"); // Auto-activate the first color purchased if (wasFirstPurchase && (UPGRADE_CONFIG.gameSettings.activeColor === "auto" || !UPGRADE_CONFIG.colors[UPGRADE_CONFIG.gameSettings.activeColor] || UPGRADE_CONFIG.colors[UPGRADE_CONFIG.gameSettings.activeColor].currentLevel === 0)) { UPGRADE_CONFIG.gameSettings.activeColor = key; game.showMessage(upgrade.name + " activated"); } // Refresh and update with a slightly longer delay refreshUpgradeTab('colors'); // Add a longer timeout to ensure elements are fully created LK.setTimeout(function() { addActiveColorIndicator(); }, 5); // Increased from 1 to 5 return true; }
User prompt
Update with: function refreshUpgradeTab(tabName) { // Keep existing code that rebuilds the tab // ... // Add at the end: if (tabName === 'colors') { // Call after a slight delay to ensure all elements are created LK.setTimeout(function() { addActiveColorIndicator(); }, 1); } }
User prompt
Update with: function setActiveColor(colorKey) { if (colorKey === "auto" || UPGRADE_CONFIG.colors[colorKey].currentLevel > 0) { UPGRADE_CONFIG.gameSettings.activeColor = colorKey; // Update visual indicator addActiveColorIndicator(); } else { game.showError("Unlock this color first!"); } }
User prompt
Add: function addActiveColorIndicator() { var activeColorKey = getActiveColorKey(); // Remove any existing ACTIVE indicators first tabContainers.colors.children.forEach(function(child) { if (child.text === "ACTIVE") { child.destroy(); } }); // Find the active color's element tabColumns.colors.left.concat(tabColumns.colors.right).forEach(function(upgrade) { var category = upgrade[0]; var key = upgrade[1]; if (key === activeColorKey) { // Find the reference to this upgrade's name text var nameText = null; tabContainers.colors.children.forEach(function(child) { if (child.text === UPGRADE_CONFIG[category][key].name) { nameText = child; } }); if (nameText) { // Create and add "ACTIVE" text var activeText = new Text2("ACTIVE", { size: 64, fill: 0x00FF00, stroke: 0x000000, strokeThickness: 2, font: "Impact" }); activeText.x = nameText.x + 150; activeText.y = nameText.y; tabContainers.colors.addChild(activeText); } } }); }
/**** 
* Plugins
****/ 
var tween = LK.import("@upit/tween.v1");
var facekit = LK.import("@upit/facekit.v1");
/**** 
* Classes
****/ 
// Bubble class to represent each bubble in the game
var Bubble = Container.expand(function () {
	var self = Container.call(this);
	self.lifetime = 0;
	self.hasSplit = false;
	self.splitHeight = null;
	self.AUTO_POP_SIZE = 40;
	self.MIN_SPLIT_SIZE = 30;
	self.lastPopTime = 0;
	self.visible = false; // Start invisible in pool
	var sprite = self.attachAsset('bubble', {
		anchorX: 0.5,
		anchorY: 0.5,
		alpha: 0.8
	});
	self.colorTint = 0xFFFFFF; // Default white tint
	self.colorMultiplier = 1.0; // Default multiplier
	self.colorPhase = Math.random() * Math.PI * 2; // Random starting phase
	self.size = 100;
	self.updateColor = function () {
		// Check if color upgrades are active
		if (UPGRADE_CONFIG.colors.blueBubbles.currentLevel > 0) {
			var color = 0xFFFFFF;
			var multiplier = 1.0;
			if (UPGRADE_CONFIG.colors.prismaticBubbles.currentLevel > 0) {
				// Prismatic - constantly shifting color with variable multiplier
				self.colorPhase += 0.02;
				var colorValue = Math.sin(self.colorPhase) * 0.5 + 0.5; // 0 to 1
				multiplier = 0.8 + colorValue * 1.2; // 0.8x to 2.0x
				// Generate rainbow color
				var r = Math.sin(self.colorPhase) * 127 + 128;
				var g = Math.sin(self.colorPhase + 2) * 127 + 128;
				var b = Math.sin(self.colorPhase + 4) * 127 + 128;
				color = (Math.floor(r) << 16) + (Math.floor(g) << 8) + Math.floor(b);
			} else if (UPGRADE_CONFIG.colors.rainbowBubbles.currentLevel > 0) {
				// Rainbow - randomly changes color on activation
				var colorChoice = Math.floor(Math.random() * 6);
				switch (colorChoice) {
					case 0:
						color = 0xFF0000;
						multiplier = 1.5;
						break;
					// Red
					case 1:
						color = 0xFFAA00;
						multiplier = 1.4;
						break;
					// Orange
					case 2:
						color = 0xFFFF00;
						multiplier = 1.3;
						break;
					// Yellow
					case 3:
						color = 0x00FF00;
						multiplier = 1.2;
						break;
					// Green
					case 4:
						color = 0x0000FF;
						multiplier = 1.1;
						break;
					// Blue
					case 5:
						color = 0xFF00FF;
						multiplier = 1.6;
						break;
					// Purple
				}
			} else if (UPGRADE_CONFIG.colors.pinkBubbles.currentLevel > 0) {
				color = 0xFF80C0; // Pink
				multiplier = 1.3;
			} else if (UPGRADE_CONFIG.colors.greenBubbles.currentLevel > 0) {
				color = 0x00FF80; // Green
				multiplier = 1.2;
			} else {
				// Blue bubbles
				color = 0x80C0FF; // Blue
				multiplier = 1.1;
			}
			self.colorTint = color;
			self.colorMultiplier = multiplier;
			// Apply tint to the sprite
			sprite.tint = color; // Try direct assignment
			// If that doesn't work, try:
			// sprite.color = color;
		}
	};
	self.activate = function (x, y, size, isPlayerBlown) {
		// Existing reset code
		self.x = x;
		self.y = y;
		self.size = size;
		self.lifetime = 0;
		self.hasSplit = false;
		self.splitHeight = null;
		self.justSplit = false;
		self.autoPopDisplayed = false;
		self.lastPopTime = 0;
		self.verticalVelocity = 0;
		self.driftX = (Math.random() * 20 - 10) / 60;
		self.floatSpeed = 50 * (120 / size * (0.9 + Math.random() * 0.2)) / 60;
		self.initLifetime(); // Always get fresh lifetime
		self.visible = true;
		// Reset color properties
		self.colorTint = 0xFFFFFF; // Reset to default white
		self.colorMultiplier = 1.0; // Reset multiplier
		self.colorPhase = Math.random() * Math.PI * 2; // Fresh random phase
		sprite.tint = 0xFFFFFF; // Reset the sprite tint directly
		// Check if color upgrades are active and apply colors
		self.applyColorUpgrade();
		// Add some debug logs
		console.log('Activating bubble:', {
			size: self.size,
			maxLifetime: self.maxLifetime,
			isPlayerBlown: isPlayerBlown
		});
	};
	self.applyColorUpgrade = function () {
		// Only apply if any color upgrade is active
		if (UPGRADE_CONFIG.colors.blueBubbles.currentLevel > 0) {
			var color = 0xFFFFFF;
			var multiplier = 1.0;
			// Get active color setting
			var activeColorKey = getActiveColorKey();
			// Apply the selected color
			if (activeColorKey === "prismaticBubbles") {
				// Prismatic - constantly shifting color with variable multiplier
				self.colorPhase += 0.02;
				var colorValue = Math.sin(self.colorPhase) * 0.5 + 0.5; // 0 to 1
				multiplier = 0.8 + colorValue * 1.2; // 0.8x to 2.0x
				// Generate rainbow color
				var r = Math.sin(self.colorPhase) * 127 + 128;
				var g = Math.sin(self.colorPhase + 2) * 127 + 128;
				var b = Math.sin(self.colorPhase + 4) * 127 + 128;
				color = (Math.floor(r) << 16) + (Math.floor(g) << 8) + Math.floor(b);
			} else if (activeColorKey === "rainbowBubbles") {
				// Rainbow - randomly changes color
				var rainbowColors = [0xFF0000,
				// Red
				0xFFA500,
				// Orange
				0xFFFF00,
				// Yellow
				0x00FF00,
				// Green
				0x0000FF,
				// Blue
				0xFF00FF // Purple
				];
				var multipliers = [1.5, 1.4, 1.3, 1.2, 1.1, 1.6];
				var index = Math.floor(Math.random() * rainbowColors.length);
				color = rainbowColors[index];
				multiplier = multipliers[index];
			} else {
				// Check all single colors in a more maintainable way
				var colorKeys = ["silverBubbles", "crimsonBubbles", "goldBubbles", "tealBubbles", "pinkBubbles", "orangeBubbles", "greenBubbles", "purpleBubbles", "blueBubbles"];
				// Find the active color or highest unlocked
				for (var i = 0; i < colorKeys.length; i++) {
					var key = colorKeys[i];
					if (activeColorKey === key || activeColorKey === "auto" && UPGRADE_CONFIG.colors[key].currentLevel > 0) {
						color = UPGRADE_CONFIG.colors[key].color;
						multiplier = UPGRADE_CONFIG.colors[key].multiplier;
						break;
					}
				}
			}
			// Apply the color and multiplier
			sprite.tint = color;
			self.colorTint = color;
			self.colorMultiplier = multiplier;
		}
	};
	self.deactivate = function () {
		self.visible = false;
		var index = game.activeBubbles.indexOf(self);
		if (index > -1) {
			game.activeBubbles.splice(index, 1);
		}
		// Don't award points here - let the calling function handle it
	};
	self.initLifetime = function () {
		self.maxLifetime = Math.floor(Math.random() * 960 + 1440);
		self.maxLifetime *= Math.min(1, self.size / 100);
	};
	self.initLifetime();
	// Subtle size-based variance plus small random factor
	var speedMultiplier = 120 / self.size * (0.9 + Math.random() * 0.2); // Just 10% variance
	self.floatSpeed = 50 * speedMultiplier / 60;
	self.driftX = (Math.random() * 20 - 10) / 60; // Normal drift variance
	self.verticalVelocity = 0;
	self.down = function (e) {
		var currentTime = Date.now();
		if (currentTime - self.lastPopTime < 100) {
			return true;
		}
		self.lastPopTime = currentTime;
		var points = self.getBP();
		game.addBP(points, self.x, self.y, false);
		if (self.size > 60 && !self.justSplit) {
			var splitCount = 2 + UPGRADE_CONFIG.machine.bubbleDurability.currentLevel;
			var newSize = Math.max(self.MIN_SPLIT_SIZE, self.size * 0.6);
			for (var i = 0; i < splitCount; i++) {
				var angle = i / splitCount * Math.PI * 2;
				// Reduce the direction multiplier to 0.5 to make movement more gentle
				spawnBubble(self.x, self.y, newSize, Math.cos(angle) * 0.5, false);
			}
		}
		// Check if this bubble is part of twin pair
		var twinPair = game.twinBubbles.find(function (pair) {
			return pair.bubble1 === self && !pair.popped || pair.bubble2 === self && !pair.popped;
		});
		if (twinPair) {
			// Mark as popped
			twinPair.popped = true;
			twinPair.timestamp = LK.ticks;
			// Find the other bubble in the pair
			var otherBubble = twinPair.bubble1 === self ? twinPair.bubble2 : twinPair.bubble1;
			// Add bonus for twin pop
			var twinLevel = UPGRADE_CONFIG.player.twinBubbles.currentLevel;
			var bonusMultiplier = 0.5 + 0.25 * twinLevel; // 50/75/100% bonus
			// Pop the other bubble automatically and add bonus points
			if (otherBubble.visible) {
				var bonusPoints = Math.floor(otherBubble.getBP() * bonusMultiplier);
				game.addBP(bonusPoints, otherBubble.x, otherBubble.y, false);
				otherBubble.deactivate();
			}
		}
		self.deactivate();
		return true;
	};
	self.getBP = function () {
		var baseValue = Math.max(1, Math.floor(Math.pow(self.size, 1.4) * 0.02 * 100));
		// Apply Bubble Refinement upgrade
		var refinementLevel = UPGRADE_CONFIG.player.bubbleRefinement.currentLevel;
		if (refinementLevel > 0) {
			baseValue *= 1 + 0.25 * refinementLevel; // +25% per level
		}
		if (self.fromClam && UPGRADE_CONFIG.machine.bubbleQuality.currentLevel > 0) {
			var qualityLevel = UPGRADE_CONFIG.machine.bubbleQuality.currentLevel;
			baseValue *= 1 + 0.4 * qualityLevel; // +40% per level
		}
		// Apply color multiplier
		baseValue = Math.floor(baseValue * self.colorMultiplier);
		// Apply treasure zone bonus if applicable
		baseValue = Math.floor(baseValue * getTreasureBonusMultiplier(self.x, self.y));
		return baseValue;
	};
	self.update = function () {
		if (UPGRADE_CONFIG.colors.prismaticBubbles.currentLevel > 0) {
			self.colorPhase += 0.02;
			var r = Math.sin(self.colorPhase) * 127 + 128;
			var g = Math.sin(self.colorPhase + 2) * 127 + 128;
			var b = Math.sin(self.colorPhase + 4) * 127 + 128;
			var color = (Math.floor(r) << 16) + (Math.floor(g) << 8) + Math.floor(b);
			// Update the color value
			sprite.tint = color;
			self.colorTint = color;
			// Make the multiplier oscillate for prismatic
			var colorValue = Math.sin(self.colorPhase) * 0.5 + 0.5;
			self.colorMultiplier = 0.8 + colorValue * 1.2; // 0.8x to 2.0x
		}
		// Only increment lifetime if not being actively blown
		if (game.growingBubble !== self) {
			self.lifetime++;
			if (self.lifetime % 60 === 0) {
				self.driftX += (Math.random() - 0.5) * 0.8;
			}
		}
		self.x += self.driftX * 1.2;
		if (self.lifetime > self.maxLifetime) {
			console.log('Bubble dying - lifetime:', self.lifetime, 'maxLifetime:', self.maxLifetime);
			if (self.size > 60 && !self.hasSplit) {
				self.hasSplit = true;
				var newSize = Math.max(self.MIN_SPLIT_SIZE, self.size * 0.6);
				for (var i = 0; i < 2; i++) {
					var split = spawnBubble(self.x, self.y, newSize, i === 0 ? -1 : 1, true);
					if (split) {
						split.maxLifetime *= 0.7;
					}
				}
				self.deactivate();
				return;
			}
			self.autoPop();
			return;
		}
		if (self.y < -self.size) {
			// Just deactivate, no points
			self.visible = false;
			var index = game.activeBubbles.indexOf(self);
			if (index > -1) {
				game.activeBubbles.splice(index, 1);
			}
			return;
		}
		self.justSplit = false;
		if (self.verticalVelocity < self.floatSpeed) {
			self.verticalVelocity += 0.08;
		}
		self.y -= self.verticalVelocity;
		if (Math.abs(self.driftX) > (Math.random() * 20 - 10) / 60) {
			self.driftX *= 0.98;
		}
		self.x += self.driftX;
		if (self.x < self.size) {
			self.x = self.size;
			self.driftX = Math.abs(self.driftX);
		} else if (self.x > game.width - self.size) {
			self.x = game.width - self.size;
			self.driftX = -Math.abs(self.driftX);
		}
		var scale = self.size / sprite.width;
		sprite.scaleX = scale;
		sprite.scaleY = scale;
	};
	self.autoPop = function () {
		// Only award points if bubble is on screen
		if (!self.autoPopDisplayed && self.y > -self.size) {
			var points = Math.floor(self.getBP() * 0.5);
			game.addBP(points, self.x, self.y, true);
			self.autoPopDisplayed = true;
		}
		self.deactivate();
		return;
	};
	self.updateColor = function () {
		// Check if color upgrades are active
		if (UPGRADE_CONFIG.colors.blueBubbles.currentLevel > 0) {
			var color = 0xFFFFFF;
			var multiplier = 1.0;
			if (UPGRADE_CONFIG.colors.prismaticBubbles.currentLevel > 0) {
				// Prismatic - constantly shifting color with variable multiplier
				self.colorPhase += 0.02;
				var colorValue = Math.sin(self.colorPhase) * 0.5 + 0.5; // 0 to 1
				multiplier = 0.8 + colorValue * 1.2; // 0.8x to 2.0x
				// Generate rainbow color
				var r = Math.sin(self.colorPhase) * 127 + 128;
				var g = Math.sin(self.colorPhase + 2) * 127 + 128;
				var b = Math.sin(self.colorPhase + 4) * 127 + 128;
				color = (Math.floor(r) << 16) + (Math.floor(g) << 8) + Math.floor(b);
			} else if (UPGRADE_CONFIG.colors.rainbowBubbles.currentLevel > 0) {
				// Rainbow - randomly changes color on activation
				var colorChoice = Math.floor(Math.random() * 6);
				switch (colorChoice) {
					case 0:
						color = 0xFF0000;
						multiplier = 1.5;
						break;
					// Red
					case 1:
						color = 0xFFAA00;
						multiplier = 1.4;
						break;
					// Orange
					case 2:
						color = 0xFFFF00;
						multiplier = 1.3;
						break;
					// Yellow
					case 3:
						color = 0x00FF00;
						multiplier = 1.2;
						break;
					// Green
					case 4:
						color = 0x0000FF;
						multiplier = 1.1;
						break;
					// Blue
					case 5:
						color = 0xFF00FF;
						multiplier = 1.6;
						break;
					// Purple
				}
			} else if (UPGRADE_CONFIG.colors.pinkBubbles.currentLevel > 0) {
				color = 0xFF80C0; // Pink
				multiplier = 1.3;
			} else if (UPGRADE_CONFIG.colors.greenBubbles.currentLevel > 0) {
				color = 0x00FF80; // Green
				multiplier = 1.2;
			} else {
				// Blue bubbles
				color = 0x80C0FF; // Blue
				multiplier = 1.1;
			}
			self.colorTint = color;
			self.colorMultiplier = multiplier;
			// Apply tint to the sprite
			sprite.tint = self.colorTint;
		}
	};
	return self;
});
var Fish = Container.expand(function () {
	var self = Container.call(this);
	var fishTypes = ['redfish', 'bluefish', 'yellowfish'];
	var fishType = fishTypes[Math.floor(Math.random() * fishTypes.length)];
	// Create fish sprite
	var sprite = self.attachAsset(fishType, {
		anchorX: 0.5,
		anchorY: 0.5
	});
	// Initialize position and movement
	self.fromLeft = Math.random() < 0.5;
	self.x = self.fromLeft ? -100 : game.width + 100;
	self.y = Math.random() * (game.height * 0.7) + game.height * 0.1;
	sprite.scaleX = self.fromLeft ? 1 : -1;
	self.speed = 8; // Changed from 4
	self.update = function () {
		self.x += self.fromLeft ? self.speed : -self.speed;
		// Add bubble collision check
		game.activeBubbles.forEach(function (bubble) {
			if (bubble.visible) {
				var dx = self.x - bubble.x;
				var dy = self.y - bubble.y;
				var distance = Math.sqrt(dx * dx + dy * dy);
				if (distance <= bubble.size / 2 + 50) {
					// Increased from 30 to 50 
					// 30px collision radius
					var points = bubble.getBP();
					game.addBP(points, bubble.x, bubble.y, false); // false = manual pop points
					bubble.deactivate();
				}
			}
		});
		// Remove when off screen
		if (self.fromLeft && self.x > game.width + 100 || !self.fromLeft && self.x < -100) {
			self.destroy();
		}
	};
	return self;
});
// Pufferfish mask that follows face
var pufferMask = Container.expand(function () {
	var self = Container.call(this);
	var sprite = self.attachAsset('pufferfish', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	var targetX = 0;
	var targetY = 0;
	var smoothingFactor = 0.12;
	var prevX = null;
	var prevY = null;
	var targetRotation = 0;
	var rotationSmoothingFactor = 0.1;
	var targetTilt = 0;
	var tiltSmoothingFactor = 0.11; // Reduced from 0.08 for smoother movement
	var tiltScaleFactor = 0.09; // Reduced from 0.15 for less tilt
	var scaleHistory = new Array(5).fill(0); // Keep last 5 scale values
	var scaleIndex = 0;
	var baseScale = 1;
	var minScale = 0.1;
	var maxScale = 3;
	self.update = function () {
		// Adjust scale based on face size
		if (facekit.leftEye && facekit.rightEye && facekit.mouthCenter) {
			var eyeDistance = Math.abs(facekit.rightEye.x - facekit.leftEye.x);
			var newScale = eyeDistance / 500;
			// Update rolling average
			scaleHistory[scaleIndex] = newScale;
			scaleIndex = (scaleIndex + 1) % scaleHistory.length;
			// Calculate average scale
			var avgScale = scaleHistory.reduce(function (a, b) {
				return a + b;
			}, 0) / scaleHistory.length;
			// More gentle smoothing
			sprite.scaleX = sprite.scaleX * 0.85 + avgScale * 0.15;
			sprite.scaleY = sprite.scaleY * 0.85 + avgScale * 0.15;
		}
		// Follow nose position for main face tracking
		if (facekit.noseTip) {
			targetX = facekit.noseTip.x;
			targetY = facekit.noseTip.y;
			// Initialize previous positions if not set
			if (prevX === null) {
				prevX = targetX;
				prevY = targetY;
			}
			// Weighted average between previous and target position
			var newX = prevX * (1 - smoothingFactor) + targetX * smoothingFactor;
			var newY = prevY * (1 - smoothingFactor) + targetY * smoothingFactor;
			self.x = newX;
			self.y = newY;
			// Update previous positions
			prevX = newX;
			prevY = newY;
		}
		if (facekit.leftEye && facekit.rightEye) {
			targetTilt = calculateFaceTilt() * tiltScaleFactor; // Scale down the tilt
			// Reduce max rotation to Β±15 degrees
			targetTilt = Math.max(-15, Math.min(15, targetTilt));
			self.rotation += (targetTilt - self.rotation) * tiltSmoothingFactor;
		}
	};
	function calculateFaceTilt() {
		if (facekit.leftEye && facekit.rightEye && facekit.mouthCenter) {
			// Calculate midpoint between eyes
			var eyeMidX = (facekit.leftEye.x + facekit.rightEye.x) / 2;
			var eyeMidY = (facekit.leftEye.y + facekit.rightEye.y) / 2;
			// Calculate angle between eye midpoint and mouth, negated to fix direction
			var dx = facekit.mouthCenter.x - eyeMidX;
			var dy = facekit.mouthCenter.y - eyeMidY;
			var angle = -(Math.atan2(dx, dy) * (180 / Math.PI));
			// Reduced max angle to Β±15 degrees and lowered multiplier
			return Math.max(-15, Math.min(15, angle * 0.15));
		}
		return 0; // Default to straight when face points aren't available
	}
	return self;
});
/**** 
* Initialize Game
****/ 
var game = new LK.Game({
	backgroundColor: 0x87CEEB // Light blue background to represent the sky
});
/**** 
* Game Code
****/ 
if (typeof UPGRADE_CONFIG === 'undefined') {
	var UPGRADE_CONFIG = {};
}
if (!UPGRADE_CONFIG.gameSettings) {
	UPGRADE_CONFIG.gameSettings = {
		activeColor: "auto" // Default to automatic progression
	};
}
// Add function to get current active color
function getActiveColorKey() {
	if (UPGRADE_CONFIG.gameSettings.activeColor === "auto") {
		// Find highest unlocked color in order of value
		var colorKeys = ["prismaticBubbles", "rainbowBubbles", "silverBubbles", "crimsonBubbles", "goldBubbles", "tealBubbles", "pinkBubbles", "orangeBubbles", "greenBubbles", "purpleBubbles", "blueBubbles"];
		for (var i = 0; i < colorKeys.length; i++) {
			if (UPGRADE_CONFIG.colors[colorKeys[i]].currentLevel > 0) {
				return colorKeys[i];
			}
		}
	}
	return UPGRADE_CONFIG.gameSettings.activeColor;
}
// Add a function to set active color
function setActiveColor(colorKey) {
	if (colorKey === "auto" || UPGRADE_CONFIG.colors[colorKey].currentLevel > 0) {
		UPGRADE_CONFIG.gameSettings.activeColor = colorKey;
		// Update visual indicator in UI
		updateColorSelectionUI();
	} else {
		game.showError("Unlock this color first!");
	}
}
function getTreasureBonusMultiplier(x, y) {
	if (!game.treasureZones || game.treasureZones.length === 0) {
		return 1.0; // No bonus if no treasures
	}
	// Start with no bonus
	var totalBonus = 0;
	// Check each treasure zone
	game.treasureZones.forEach(function (zone) {
		var dx = x - zone.x;
		var dy = y - zone.y;
		var distance = Math.sqrt(dx * dx + dy * dy);
		// If bubble is in zone, add 50% bonus
		if (distance <= zone.radius) {
			totalBonus += 0.5; // +50% per overlapping zone as specified
		}
	});
	// Return multiplier (1.0 = no bonus, 1.5 = one zone, 2.0 = two zones, etc.)
	return 1.0 + totalBonus;
}
// Function to update treasure decorations
function updateTreasureDecorations() {
	// Clear existing treasures
	while (treasureContainer.children.length) {
		treasureContainer.children[0].destroy();
	}
	// Clear zone tracking
	game.treasureZones = [];
	var treasureCount = UPGRADE_CONFIG.decorations.sunkenTreasures.amount;
	if (treasureCount <= 0) {
		return;
	}
	// Available treasure types
	var treasureTypes = ['treasure1', 'treasure2', 'treasure3'];
	// Position treasures at specific spots in the bottom half of screen
	var positions = [
	// Left side
	{
		x: game.width * 0.25,
		y: game.height * 0.65
	},
	// Right side
	{
		x: game.width * 0.75,
		y: game.height * 0.65
	},
	// Middle bottom
	{
		x: game.width * 0.5,
		y: game.height * 0.8
	}];
	// Place treasures at predetermined spots
	for (var i = 0; i < treasureCount; i++) {
		// Don't exceed available positions
		if (i >= positions.length) {
			break;
		}
		// Choose treasure type based on position
		var treasureType = treasureTypes[i % treasureTypes.length];
		var pos = positions[i];
		// Create circular zone indicator
		var zoneRadius = game.width * 0.15;
		var zoneIndicator = LK.getAsset('blower', {
			width: zoneRadius * 2,
			height: zoneRadius * 2,
			shape: 'circle',
			// Specify circle shape
			color: 0xFFFFFF,
			alpha: 0.15,
			anchorX: 0.5,
			anchorY: 0.5,
			x: pos.x,
			y: pos.y
		});
		// Add zone to tracking for bonus calculation
		game.treasureZones.push({
			id: 'treasure_' + i,
			x: pos.x,
			y: pos.y,
			radius: zoneRadius
		});
		// Create treasure sprite on top of zone
		var treasure = LK.getAsset(treasureType, {
			anchorX: 0.5,
			anchorY: 0.5,
			x: pos.x,
			y: pos.y,
			scaleX: 1.8,
			scaleY: 1.8,
			alpha: 0.9
		});
		// Add zone first (so it's behind treasure)
		treasureContainer.addChild(zoneIndicator);
		treasureContainer.addChild(treasure);
	}
}
function updateCoralBubbles() {
	if (UPGRADE_CONFIG.decorations.bubbleCoral.amount <= 0) {
		return;
	}
	coralContainer.children.forEach(function (coral) {
		// 5% chance per second to spawn bubble (0.083% per frame at 60fps)
		if (Math.random() < 0.00083 || LK.ticks - coral.lastBubbleTime > 600) {
			// Force spawn after 10 seconds
			coral.lastBubbleTime = LK.ticks;
			// Calculate bubble size based on player's max size
			var bubbleSize = game.maxBubbleSize * (0.5 + Math.random() * 0.3);
			// Spawn position slightly above coral
			var bubbleX = coral.x + (Math.random() * 30 - 15);
			var bubbleY = coral.y - coral.height * 0.3;
			var bubble = spawnBubble(bubbleX, bubbleY, bubbleSize, 0, false);
			if (bubble) {
				// Set moderate upward velocity
				bubble.verticalVelocity = -(2 + Math.random() * 3);
				bubble.driftX = Math.random() * 2 - 1;
			}
		}
	});
}
function updateCoralDecorations() {
	// Clear existing corals
	while (coralContainer.children.length) {
		coralContainer.children[0].destroy();
	}
	// Position corals along bottom of screen
	var coralCount = UPGRADE_CONFIG.decorations.bubbleCoral.amount;
	if (coralCount <= 0) {
		return;
	}
	// Available coral types
	var coralTypes = ['coral1', 'coral2', 'coral3', 'coral4'];
	// Space corals evenly along bottom
	var spacing = game.width / (coralCount + 1);
	for (var i = 0; i < coralCount; i++) {
		// Choose random coral type
		var coralType = coralTypes[Math.floor(Math.random() * coralTypes.length)];
		// Create coral sprite
		var coral = LK.getAsset(coralType, {
			anchorX: 0.5,
			anchorY: 1,
			x: spacing * (i + 1),
			y: game.height - 20,
			// Slightly above bottom
			scaleX: 1.5 + Math.random() * 0.5,
			scaleY: 1.5 + Math.random() * 0.5
		});
		// Store last bubble spawn time
		coral.lastBubbleTime = 0;
		coralContainer.addChild(coral);
	}
}
function refreshUpgradeTab(tabName) {
	// Clear all children from the tab container
	while (tabContainers[tabName].children.length > 0) {
		tabContainers[tabName].children[0].destroy();
	}
	// Recreate all upgrades for the tab
	if (tabColumns[tabName] && tabColumns[tabName].left) {
		tabColumns[tabName].left.forEach(function (upgrade, index) {
			createUpgradeText(upgrade[0], upgrade[1], index, true, tabName);
		});
	}
	if (tabColumns[tabName] && tabColumns[tabName].right) {
		tabColumns[tabName].right.forEach(function (upgrade, index) {
			createUpgradeText(upgrade[0], upgrade[1], index, false, tabName);
		});
	}
	// Add this at the end of the function:
	if (tabName === 'colors') {
		var activeColorKey = getActiveColorKey();
		tabColumns[tabName].left.concat(tabColumns[tabName].right).forEach(function (upgrade) {
			var category = upgrade[0];
			var key = upgrade[1];
			var upgradeConfig = UPGRADE_CONFIG[category][key];
			if (upgradeConfig.currentLevel > 0) {
				// Find the corresponding cost text for this upgrade 
				tabContainers[tabName].children.forEach(function (child) {
					// Identify the cost text by its position below the name 
					if (child.text && child.y > 0) {
						var nameText = tabContainers[tabName].children.find(function (c) {
							return c.text === upgradeConfig.name;
						});
						if (nameText && child.y > nameText.y && child.y < nameText.y + 150) {
							// Check if this color is active 
							if (activeColorKey === key || UPGRADE_CONFIG.gameSettings.activeColor === "auto" && key === activeColorKey) {
								child.setText("ACTIVE");
								child.fill = 0x00FF00; // Green 
							} else if (category === 'colors' && upgradeConfig.currentLevel >= upgradeConfig.maxLevel) {
								// Create "SOLD OUT" text for maxed out color upgrades 
								child.setText("SOLD OUT");
								child.fill = 0x888888; // Gray 
							} else {
								var cost = getUpgradeCost(upgradeConfig);
								child.setText(cost + " BP");
								child.fill = 0xFFFF00; // Yellow 
							}
						}
					}
				});
			}
		});
	}
}
game.twinBubbles = [];
game.treasureZones = [];
// Define column assignments for each tab
var tabColumns = {
	bubbles: {
		left: [['player', 'lungCapacity'], ['player', 'quickBreath'], ['player', 'bubbleRefinement'], ['player', 'twinBubbles']],
		right: [['player', 'autoPop'], ['player', 'sizeVariance'], ['player', 'bubbleBreath']]
	},
	clams: {
		left: [['machines', 'basicClam'], ['machines', 'advancedClam'], ['machines', 'premiumClam']],
		right: [['machine', 'bubbleDurability'], ['machine', 'autoBubbleSpeed'], ['machine', 'bubbleQuality']]
	},
	colors: {
		left: [['colors', 'blueBubbles'], ['colors', 'purpleBubbles'], ['colors', 'greenBubbles'], ['colors', 'orangeBubbles'], ['colors', 'pinkBubbles']],
		right: [['colors', 'tealBubbles'], ['colors', 'goldBubbles'], ['colors', 'crimsonBubbles'], ['colors', 'silverBubbles'], ['colors', 'rainbowBubbles'], ['colors', 'prismaticBubbles']]
	},
	decorations: {
		left: [['decorations', 'bubbleCoral']],
		right: [['decorations', 'sunkenTreasures']]
	}
};
var UPGRADE_EFFECTS = {
	lungCapacity: {
		baseValue: 160,
		// Base max bubble size
		incrementPercent: 25 // +25% per level
	},
	quickBreath: {
		baseValue: 1.6,
		// Base growth rate
		incrementPercent: 25 // +25% per level
	},
	autoBubbleSpeed: {
		decrementPercent: 10 // -10% production time per level
	},
	bubbleDurability: {
		extraSplits: 1 // +1 split per level
	},
	autoPop: {
		timeReduction: 0.8 // Reduces lifetime by 20% per level
	}
};
function _slicedToArray3(r, e) {
	return _arrayWithHoles3(r) || _iterableToArrayLimit3(r, e) || _unsupportedIterableToArray3(r, e) || _nonIterableRest3();
}
function _nonIterableRest3() {
	throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
}
function _unsupportedIterableToArray3(r, a) {
	if (r) {
		if ("string" == typeof r) {
			return _arrayLikeToArray3(r, a);
		}
		var t = {}.toString.call(r).slice(8, -1);
		return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray3(r, a) : void 0;
	}
}
function _arrayLikeToArray3(r, a) {
	(null == a || a > r.length) && (a = r.length);
	for (var e = 0, n = Array(a); e < a; e++) {
		n[e] = r[e];
	}
	return n;
}
function _iterableToArrayLimit3(r, l) {
	var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"];
	if (null != t) {
		var e,
			n,
			i,
			u,
			a = [],
			f = !0,
			o = !1;
		try {
			if (i = (t = t.call(r)).next, 0 === l) {
				if (Object(t) !== t) {
					return;
				}
				f = !1;
			} else {
				for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0) {
					;
				}
			}
		} catch (r) {
			o = !0, n = r;
		} finally {
			try {
				if (!f && null != t["return"] && (u = t["return"](), Object(u) !== u)) {
					return;
				}
			} finally {
				if (o) {
					throw n;
				}
			}
		}
		return a;
	}
}
function _arrayWithHoles3(r) {
	if (Array.isArray(r)) {
		return r;
	}
}
function getUpgradeCost(upgrade) {
	if (upgrade.amount !== undefined) {
		// For clams
		return Math.floor(upgrade.baseCost * Math.pow(upgrade.costScale, upgrade.amount));
	} else {
		// For regular upgrades
		return Math.floor(upgrade.baseCost * Math.pow(upgrade.costScale, upgrade.currentLevel));
	}
}
function _slicedToArray2(r, e) {
	return _arrayWithHoles2(r) || _iterableToArrayLimit2(r, e) || _unsupportedIterableToArray2(r, e) || _nonIterableRest2();
}
function _nonIterableRest2() {
	throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
}
function _unsupportedIterableToArray2(r, a) {
	if (r) {
		if ("string" == typeof r) {
			return _arrayLikeToArray2(r, a);
		}
		var t = {}.toString.call(r).slice(8, -1);
		return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray2(r, a) : void 0;
	}
}
function _arrayLikeToArray2(r, a) {
	(null == a || a > r.length) && (a = r.length);
	for (var e = 0, n = Array(a); e < a; e++) {
		n[e] = r[e];
	}
	return n;
}
function _iterableToArrayLimit2(r, l) {
	var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"];
	if (null != t) {
		var e,
			n,
			i,
			u,
			a = [],
			f = !0,
			o = !1;
		try {
			if (i = (t = t.call(r)).next, 0 === l) {
				if (Object(t) !== t) {
					return;
				}
				f = !1;
			} else {
				for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0) {
					;
				}
			}
		} catch (r) {
			o = !0, n = r;
		} finally {
			try {
				if (!f && null != t["return"] && (u = t["return"](), Object(u) !== u)) {
					return;
				}
			} finally {
				if (o) {
					throw n;
				}
			}
		}
		return a;
	}
}
function _arrayWithHoles2(r) {
	if (Array.isArray(r)) {
		return r;
	}
}
function updateClams() {
	if (!game.clamSpawnPoints) {
		return;
	}
	game.clamSpawnPoints.forEach(function (spawnPoint) {
		var config = UPGRADE_CONFIG.machines[spawnPoint.type];
		// Calculate production time with speed upgrade
		var baseTime = config.production * 150; // Convert to frames and further increase base time
		var speedMultiplier = Math.pow(1 - UPGRADE_EFFECTS.autoBubbleSpeed.decrementPercent / 100, UPGRADE_CONFIG.machine.autoBubbleSpeed.currentLevel);
		var adjustedTime = Math.max(1, Math.floor(baseTime * speedMultiplier));
		var qualityLevel = UPGRADE_CONFIG.machine.bubbleQuality.currentLevel;
		if (qualityLevel > 0) {
			// -10% production rate per level
			adjustedTime = Math.floor(adjustedTime * (1 + 0.1 * qualityLevel));
		}
		if (LK.ticks % adjustedTime === 0) {
			// Find first available bubble in pool
			var bubble = game.bubblePool.find(function (b) {
				return !b.visible;
			});
			if (bubble && game.activeBubbles.length < game.MAX_BUBBLES) {
				bubble.activate(spawnPoint.x, spawnPoint.y, config.bubbleSize, false // not player blown
				);
				bubble.fromClam = true;
				if (UPGRADE_CONFIG.player.sizeVariance.currentLevel > 0) {
					var variance = UPGRADE_CONFIG.player.sizeVariance.currentLevel;
					var minIncrease = 0.1 * variance; // +10% per level to min size
					var maxIncrease = 0.15 * variance; // +15% per level to max size
					// Apply size variance
					var sizeMultiplier = 1 - minIncrease + Math.random() * (minIncrease + maxIncrease);
					bubble.size *= sizeMultiplier;
				}
				// Set initial velocities for clam bubbles
				bubble.verticalVelocity = 0;
				bubble.driftX = (spawnPoint.isRight ? -1 : 1) * (Math.random() * 1.5 + 2);
				game.activeBubbles.push(bubble);
			}
		}
	});
}
function _slicedToArray(r, e) {
	return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest();
}
function _nonIterableRest() {
	throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
}
function _unsupportedIterableToArray(r, a) {
	if (r) {
		if ("string" == typeof r) {
			return _arrayLikeToArray(r, a);
		}
		var t = {}.toString.call(r).slice(8, -1);
		return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0;
	}
}
function _arrayLikeToArray(r, a) {
	(null == a || a > r.length) && (a = r.length);
	for (var e = 0, n = Array(a); e < a; e++) {
		n[e] = r[e];
	}
	return n;
}
function _iterableToArrayLimit(r, l) {
	var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"];
	if (null != t) {
		var e,
			n,
			i,
			u,
			a = [],
			f = !0,
			o = !1;
		try {
			if (i = (t = t.call(r)).next, 0 === l) {
				if (Object(t) !== t) {
					return;
				}
				f = !1;
			} else {
				for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0) {
					;
				}
			}
		} catch (r) {
			o = !0, n = r;
		} finally {
			try {
				if (!f && null != t["return"] && (u = t["return"](), Object(u) !== u)) {
					return;
				}
			} finally {
				if (o) {
					throw n;
				}
			}
		}
		return a;
	}
}
function _arrayWithHoles(r) {
	if (Array.isArray(r)) {
		return r;
	}
}
var background = LK.getAsset('background', {
	anchorX: 0.5,
	anchorY: 0.5,
	x: game.width / 2,
	y: game.height / 2
});
game.addChild(background);
// Add coral container here
var coralContainer = new Container();
game.addChild(coralContainer);
// Reposition in display list
game.setChildIndex(coralContainer, 1);
var treasureContainer = new Container();
// Add after coralContainer but before clams for proper layering
game.addChild(treasureContainer);
game.setChildIndex(treasureContainer, 2);
var clamContainer = new Container();
game.addChild(clamContainer);
var playerMask = new pufferMask();
game.addChild(playerMask);
var UPGRADE_CONFIG = {
	gameSettings: {
		activeColor: "auto" // Default to automatic progression (highest unlocked)
	},
	player: {
		lungCapacity: {
			name: "Lung Capacity",
			baseCost: 100,
			// About 1-2 max bubbles 
			costScale: 3.0,
			// Steeper scaling 
			maxLevel: 10,
			currentLevel: 0
		},
		quickBreath: {
			name: "Quick Breath",
			baseCost: 100,
			// 2-3 max bubbles 
			costScale: 3.0,
			// Steeper scaling 
			maxLevel: 10,
			currentLevel: 0
		},
		autoPop: {
			// Moved here from machine section 
			name: "Fish Friends",
			// Updated name 
			baseCost: 400,
			// Changed from 1000 
			costScale: 3,
			maxLevel: 6,
			// Changed from 5 
			currentLevel: 0
		},
		bubbleRefinement: {
			// New upgrade
			name: "Bubble Refinement",
			baseCost: 2500,
			costScale: 2.5,
			maxLevel: 5,
			currentLevel: 0
		},
		twinBubbles: {
			// New upgrade
			name: "Twin Bubbles",
			baseCost: 5000,
			costScale: 2.3,
			maxLevel: 3,
			currentLevel: 0
		},
		sizeVariance: {
			// New upgrade
			name: "Size Variance",
			baseCost: 4000,
			costScale: 1.8,
			maxLevel: 5,
			currentLevel: 0
		},
		bubbleBreath: {
			// New upgrade
			name: "Bubble Breath",
			baseCost: 12000,
			costScale: 2.2,
			maxLevel: 4,
			currentLevel: 0
		}
	},
	machines: {
		basicClam: {
			name: "Basic Clam",
			baseCost: 300,
			// 6-7 max bubbles 
			costScale: 2,
			// Slightly steeper scaling 
			amount: 0,
			production: 3,
			bubbleSize: 80
		},
		advancedClam: {
			name: "Advanced Clam",
			baseCost: 3000,
			// Requires running basic clams for a while 
			costScale: 3.0,
			amount: 0,
			production: 2,
			bubbleSize: 100,
			unlockCost: 3000
		},
		premiumClam: {
			name: "Premium Clam",
			baseCost: 20000,
			// True end-game content 
			costScale: 3,
			amount: 0,
			production: 1,
			bubbleSize: 150,
			unlockCost: 20000
		}
	},
	machine: {
		bubbleDurability: {
			name: "Bubble Splitting",
			baseCost: 500,
			costScale: 5,
			// Significant scaling 
			maxLevel: 3,
			currentLevel: 0
		},
		autoBubbleSpeed: {
			name: "Clam Speed",
			baseCost: 500,
			costScale: 3.5,
			maxLevel: 8,
			currentLevel: 0
		},
		bubbleQuality: {
			// New upgrade
			name: "Bubble Quality",
			baseCost: 3000,
			costScale: 2.2,
			maxLevel: 5,
			currentLevel: 0
		}
	},
	colors: {
		// New section
		blueBubbles: {
			name: "Blue Bubbles",
			baseCost: 1000,
			costScale: 1.0,
			maxLevel: 1,
			currentLevel: 0,
			multiplier: 1.1,
			color: 0x80C0FF
		},
		purpleBubbles: {
			name: "Purple Bubbles",
			baseCost: 2500,
			costScale: 1.0,
			maxLevel: 1,
			currentLevel: 0,
			requires: "blueBubbles",
			multiplier: 1.15,
			color: 0x8A2BE2
		},
		greenBubbles: {
			name: "Green Bubbles",
			baseCost: 5000,
			costScale: 1.0,
			maxLevel: 1,
			currentLevel: 0,
			requires: "purpleBubbles",
			multiplier: 1.2,
			color: 0x00FF80
		},
		orangeBubbles: {
			name: "Orange Bubbles",
			baseCost: 8000,
			costScale: 1.0,
			maxLevel: 1,
			currentLevel: 0,
			requires: "greenBubbles",
			multiplier: 1.25,
			color: 0xFFA500
		},
		pinkBubbles: {
			name: "Pink Bubbles",
			baseCost: 12000,
			costScale: 1.0,
			maxLevel: 1,
			currentLevel: 0,
			requires: "orangeBubbles",
			multiplier: 1.3,
			color: 0xFF80C0
		},
		tealBubbles: {
			name: "Teal Bubbles",
			baseCost: 18000,
			costScale: 1.0,
			maxLevel: 1,
			currentLevel: 0,
			requires: "pinkBubbles",
			multiplier: 1.35,
			color: 0x00CED1
		},
		goldBubbles: {
			name: "Gold Bubbles",
			baseCost: 25000,
			costScale: 1.0,
			maxLevel: 1,
			currentLevel: 0,
			requires: "tealBubbles",
			multiplier: 1.4,
			color: 0xFFD700
		},
		crimsonBubbles: {
			name: "Crimson Bubbles",
			baseCost: 35000,
			costScale: 1.0,
			maxLevel: 1,
			currentLevel: 0,
			requires: "goldBubbles",
			multiplier: 1.45,
			color: 0xDC143C
		},
		silverBubbles: {
			name: "Silver Bubbles",
			baseCost: 45000,
			costScale: 1.0,
			maxLevel: 1,
			currentLevel: 0,
			requires: "crimsonBubbles",
			multiplier: 1.5,
			color: 0xC0C0C0
		},
		rainbowBubbles: {
			name: "Rainbow Bubbles",
			baseCost: 75000,
			costScale: 1.0,
			maxLevel: 1,
			currentLevel: 0,
			requires: "silverBubbles",
			multiplier: 1.6
		},
		prismaticBubbles: {
			name: "Prismatic Bubbles",
			baseCost: 150000,
			costScale: 1.0,
			maxLevel: 1,
			currentLevel: 0,
			requires: "rainbowBubbles",
			multiplier: 1.8
		}
	},
	decorations: {
		// New section
		bubbleCoral: {
			name: "Bubble Coral",
			baseCost: 10000,
			costScale: 2.0,
			amount: 0,
			maxAmount: 5
		},
		sunkenTreasures: {
			name: "Sunken Treasures",
			baseCost: 25000,
			costScale: 2.5,
			amount: 0,
			maxAmount: 3
		}
	}
};
game.showError = function (message) {
	var errorText = new Text2(message, {
		size: 120,
		fill: 0xFF0000,
		stroke: 0x000000,
		strokeThickness: 5,
		font: "Impact"
	});
	// Set the anchor point to center the text
	errorText.anchor = {
		x: 0.5,
		y: 0.5
	};
	// Position at the center of the screen
	errorText.x = game.width / 2;
	errorText.y = game.height / 2;
	game.addChild(errorText);
	// Animate the text
	tween(errorText, {
		alpha: 0,
		y: errorText.y - 50
	}, {
		duration: 1200,
		onFinish: function onFinish() {
			errorText.destroy();
		}
	});
};
game.showMessage = function (message) {
	var messageText = new Text2(message, {
		size: 80,
		fill: 0xFFFFFF,
		stroke: 0x000000,
		strokeThickness: 3,
		font: "Impact"
	});
	// Set the anchor point to center the text
	messageText.anchor = {
		x: 0.5,
		y: 0.5
	};
	// Position at the center of the screen
	messageText.x = game.width / 2;
	messageText.y = game.height / 2;
	game.addChild(messageText);
	// Animate the text
	tween(messageText, {
		alpha: 0,
		y: messageText.y - 30
	}, {
		duration: 1000,
		onFinish: function onFinish() {
			messageText.destroy();
		}
	});
};
var currentTab = 'bubbles'; // Default tab
var menuTabs = ['bubbles', 'clams', 'colors', 'decorations'];
var tabButtons = {}; // Will hold references to tab buttons
// Create upgrade menu elements
// Menu tab (handle)
// First position the panel relative to container at y=0
// Menu panel should be below the tab in the container
var menuTab = LK.getAsset('upgradetab', {
	anchorX: 0.5,
	anchorY: 1,
	// Change to bottom anchor
	y: 0,
	// Will be at container's position
	scaleX: 3,
	scaleY: 0.8,
	alpha: 0.9
});
var menuPanel = LK.getAsset('upgradetab', {
	anchorX: 0.5,
	anchorY: 0,
	y: -570,
	alpha: 0.9,
	scaleX: 2048 / 200,
	// Use the width of the asset directly
	scaleY: game.height * 0.4 / 100.3
});
// Initialize menu structure
// Initialize menu container at the right position
var menuContainer = new Container();
menuContainer.x = game.width / 2;
menuContainer.y = game.height; // Position at bottom
var menuPanel = LK.getAsset('upgradetab', {
	anchorX: 0.5,
	anchorY: 0,
	y: -570,
	alpha: 0.9,
	scaleX: 2048 / 200,
	scaleY: game.height * 0.4 / 100.3
});
var menuTab = LK.getAsset('upgradetab', {
	anchorX: 0.5,
	anchorY: 1,
	y: 0,
	scaleX: 3,
	scaleY: 0.8,
	alpha: 0.9
});
// Add panel first (so it's behind tab)
menuContainer.addChild(menuPanel);
menuContainer.addChild(menuTab);
// Menu text setup - should be good as is
var menuText = new Text2("Upgrades", {
	size: 90,
	fill: 0xFFFFFF,
	stroke: 0x000000,
	strokeThickness: 3,
	font: "Impact"
});
menuText.anchor = {
	x: 0.5,
	y: 0.5
};
menuText.x = 0; // Relative to container
menuText.y = -menuTab.height / 2; // Position relative to container bottom
menuContainer.addChild(menuText);
// Add to game
game.addChild(menuContainer);
// Add this after the menuPanel is created but before upgradeTexts are created
// Create text container AFTER panel scaling
var menuTextContainer = new Container();
menuContainer.addChild(menuTextContainer);
var tabContainers = {};
menuTabs.forEach(function (tab) {
	var contentContainer = new Container();
	contentContainer.visible = tab === currentTab;
	tabContainers[tab] = contentContainer;
	menuTextContainer.addChild(contentContainer);
});
// Create text container AFTER panel scaling
var menuTextContainer = new Container();
menuContainer.addChild(menuTextContainer);
// Create tab container (only visible when menu is open)
var tabsContainer = new Container();
// Position tabsContainer at the bottom of the panel
tabsContainer.x = menuPanel.width * menuPanel.scaleX / 2 - 850; // Center horizontally
tabsContainer.y = 0;
menuContainer.addChild(tabsContainer);
// Define tab dimensions
var tabWidth = menuPanel.width * 0.8 * menuPanel.scaleX / menuTabs.length;
var tabHeight = 120;
// Create tabs
menuTabs.forEach(function (tab, index) {
	// Create tab button
	var tabButton = LK.getAsset('upgradetab', {
		anchorX: 0.5,
		anchorY: 0,
		// Anchor to top
		// Position evenly across panel width
		x: -menuPanel.width * menuPanel.scaleX / 2 + (index + 0.5) * tabWidth,
		y: 0,
		// Start at the container's position
		scaleX: tabWidth / 200,
		scaleY: tabHeight / 299,
		// Updated for new height
		alpha: tab === currentTab ? 1.0 : 0.7
	});
	// Add hit detection to the tab button
	tabButton.down = function () {
		// Only do something if this isn't already the current tab
		if (tab !== currentTab) {
			// Update tab appearance
			Object.keys(tabButtons).forEach(function (t) {
				if (tabButtons[t]) {
					tabButtons[t].alpha = t === tab ? 1.0 : 0.7;
				}
			});
			// Hide current tab content, show new tab content
			if (tabContainers[currentTab]) {
				tabContainers[currentTab].visible = false;
			}
			if (tabContainers[tab]) {
				tabContainers[tab].visible = true;
			}
			// Update current tab
			currentTab = tab;
		}
		// Return true to indicate the event was handled
		return true;
	};
	// Store reference to the button
	tabButtons[tab] = tabButton;
	// Add text to tab
	var tabText = new Text2(tab.charAt(0).toUpperCase() + tab.slice(1), {
		// Capitalize first letter
		size: 80,
		// Increased from 50
		fill: 0xFFFFFF,
		stroke: 0x000000,
		strokeThickness: 3,
		// Slightly thicker for larger text
		font: "Impact"
	});
	tabText.anchor = {
		x: 0.5,
		y: 0.5
	};
	tabText.x = tabButton.x;
	tabText.y = tabHeight / 2;
	// Add to container
	tabsContainer.addChild(tabButton);
	tabsContainer.addChild(tabText);
});
// Function to switch between tabs
function switchTab(newTab) {
	// Hide old tab content, show new tab content
	tabContainers[currentTab].visible = false;
	tabContainers[newTab].visible = true;
	// Update tab button appearance
	tabButtons[currentTab].alpha = 0.7;
	tabButtons[newTab].alpha = 1.0;
	currentTab = newTab;
}
// Add upgrade texts to text container instead of panel
var upgradeTexts = [];
var startY = 150;
var upgradeSpacing = 250;
var columnWidth = 1024;
// Create arrays to hold upgrade categories in desired order
var leftColumnUpgrades = [['player', 'lungCapacity'], ['player', 'quickBreath'], ['player', 'autoPop']];
var rightColumnUpgrades = [['machines', 'basicClam'], ['machines', 'advancedClam'], ['machines', 'premiumClam'], ['machine', 'bubbleDurability'], ['machine', 'autoBubbleSpeed']];
// Function to determine which tab an upgrade belongs to
function getTabForUpgrade(category, key) {
	if (category === 'player') {
		return 'bubbles';
	}
	if (category === 'machines' || category === 'machine') {
		return 'clams';
	}
	if (category === 'colors') {
		return 'colors';
	}
	if (category === 'decorations') {
		return 'decorations';
	}
	return 'bubbles'; // Default
}
// Function to create upgrade text
function createUpgradeText(category, key, index, isLeftColumn, tab) {
	var upgrade = UPGRADE_CONFIG[category][key];
	if (!upgrade) {
		return;
	} // Skip if upgrade doesn't exist
	var xOffset = isLeftColumn ? -750 : 200; // Changed from -550 and 100
	var yPos = startY + index * upgradeSpacing + 120; // Added 120 to move everything down
	// Create hit container
	var hitContainer = new Container();
	var hitArea = LK.getAsset('blower', {
		width: 400,
		height: 150,
		color: 0xFFFFFF,
		alpha: 0.0
	});
	hitContainer.addChild(hitArea);
	hitContainer.x = xOffset;
	hitContainer.y = yPos;
	hitArea.x = 0;
	hitArea.y = -40;
	// Create name text
	var nameText = new Text2(upgrade.name, {
		size: 96,
		fill: 0xFFFFFF,
		stroke: 0x000000,
		strokeThickness: 2,
		font: "Impact"
	});
	nameText.x = xOffset;
	nameText.y = yPos;
	// Create cost text
	var cost = getUpgradeCost(upgrade);
	var costText;
	if (category === 'colors' && upgrade.currentLevel >= upgrade.maxLevel) {
		// Create "SOLD OUT" text for maxed out color upgrades
		costText = new Text2("SOLD OUT", {
			size: 96,
			fill: 0x888888,
			// Gray color
			stroke: 0x000000,
			strokeThickness: 2,
			font: "Impact"
		});
	} else if (category === 'colors' && upgrade.requires) {
		var required = UPGRADE_CONFIG.colors[upgrade.requires];
		if (required && required.currentLevel === 0) {
			// Create "LOCKED" text for locked upgrades
			costText = new Text2("LOCKED", {
				size: 96,
				fill: 0x888888,
				// Gray color
				stroke: 0x000000,
				strokeThickness: 2,
				font: "Impact"
			});
		} else {
			// Normal cost text
			costText = new Text2(cost + " BP", {
				size: 96,
				fill: 0xFFFF00,
				stroke: 0x000000,
				strokeThickness: 2,
				font: "Impact"
			});
		}
	} else {
		// Normal cost text for non-color upgrades
		costText = new Text2(cost + " BP", {
			size: 96,
			fill: 0xFFFF00,
			stroke: 0x000000,
			strokeThickness: 2,
			font: "Impact"
		});
	}
	costText.x = xOffset;
	costText.y = yPos + 100;
	// Add click handler
	hitContainer.down = function () {
		var cost = getUpgradeCost(upgrade);
		// Special handling for colors that are already purchased
		if (category === 'colors' && upgrade.currentLevel > 0) {
			// Toggle this color as active
			if (UPGRADE_CONFIG.gameSettings.activeColor === key) {
				// If already active, switch to auto
				UPGRADE_CONFIG.gameSettings.activeColor = "auto";
				game.showMessage("Auto color mode");
			} else {
				// Otherwise activate this color
				UPGRADE_CONFIG.gameSettings.activeColor = key;
				game.showMessage(upgrade.name + " activated");
			}
			// Refresh the tab to update the text displays
			refreshUpgradeTab('colors');
			return true;
		}
		// Regular upgrade purchase logic follows...
		if (category === 'colors' && upgrade.requires) {
			var required = UPGRADE_CONFIG.colors[upgrade.requires];
			if (required && required.currentLevel === 0) {
				game.showError("Unlock " + required.name + " first!");
				return true;
			}
		}
		if (game.bp >= cost) {
			if (upgrade.amount !== undefined) {
				// For clams and decorations with amount
				if (upgrade.amount < (upgrade.maxAmount || 999)) {
					upgrade.amount++;
					game.bp -= cost;
					bpText.setText(formatBP(game.bp) + " BP");
					costText.setText(getUpgradeCost(upgrade) + " BP");
					// Update clam visuals if needed
					if (category === 'machines') {
						updateClamVisuals();
					}
					// Update decoration visuals if implemented
					if (category === 'decorations') {
						if (key === 'bubbleCoral') {
							updateCoralDecorations();
						} else if (key === 'sunkenTreasures') {
							updateTreasureDecorations();
						}
						// Future decoration handlers would go here
					}
				}
			} else if (upgrade.currentLevel < upgrade.maxLevel) {
				// For regular upgrades with levels
				upgrade.currentLevel++;
				game.bp -= cost;
				bpText.setText(formatBP(game.bp) + " BP");
				// If this is a color upgrade, update the UI to reflect dependency status
				if (category === 'colors') {
					refreshUpgradeTab('colors');
					return true;
				}
				// Update cost text
				if (upgrade.currentLevel >= upgrade.maxLevel) {
					costText.setText("SOLD OUT");
				} else {
					costText.setText(getUpgradeCost(upgrade) + " BP");
				}
				// Handle specific upgrade effects
				if (category === 'player') {
					if (key === 'lungCapacity') {
						var baseSize = UPGRADE_EFFECTS.lungCapacity.baseValue;
						var increasePercent = UPGRADE_EFFECTS.lungCapacity.incrementPercent;
						var multiplier = 1 + increasePercent / 100 * upgrade.currentLevel;
						game.maxBubbleSize = baseSize * multiplier;
					} else if (key === 'quickBreath') {
						game.growthRate = UPGRADE_EFFECTS.quickBreath.baseValue * (1 + UPGRADE_EFFECTS.quickBreath.incrementPercent / 100 * upgrade.currentLevel);
					}
					// Other upgrade effects will be implemented later
				}
			}
		} else {
			game.showError("Not enough BP!");
		}
		return true;
	};
	// Add elements to the appropriate tab container
	tabContainers[tab].addChild(hitContainer);
	tabContainers[tab].addChild(nameText);
	tabContainers[tab].addChild(costText);
}
// Replace the existing code for creating upgrade texts with this:
Object.keys(tabContainers).forEach(function (tab) {
	// Clear any existing content
	while (tabContainers[tab].children.length > 0) {
		tabContainers[tab].children[0].destroy();
	}
	// Create left column for this tab
	if (tabColumns[tab] && tabColumns[tab].left) {
		tabColumns[tab].left.forEach(function (upgrade, index) {
			createUpgradeText(upgrade[0], upgrade[1], index, true, tab);
		});
	}
	// Create right column for this tab
	if (tabColumns[tab] && tabColumns[tab].right) {
		tabColumns[tab].right.forEach(function (upgrade, index) {
			createUpgradeText(upgrade[0], upgrade[1], index, false, tab);
		});
	}
});
// Move the entire text container down by adjusting its Y position
menuTextContainer.y = 0; // This should align it with the top of the panel instead of being above it
menuTextContainer.x = 0; // Center in panel
// Menu state and animation
var menuOpen = false;
var menuTargetY = game.height;
// Update selection UI when colors change
function updateClamVisuals() {
	while (clamContainer.children.length) {
		clamContainer.children[0].destroy();
	}
	var leftStart = game.width * 0.1; // Moved further left from 0.15
	var rightStart = game.width * 0.9; // Moved further right from 0.85
	var spacing = 250; // Increased from 150
	var y = game.height - 100;
	// We'll store type of each clam position (0-3)
	var clamTypes = [];
	// Fill with basic clams first
	for (var i = 0; i < UPGRADE_CONFIG.machines.basicClam.amount; i++) {
		clamTypes.push('basicClam');
	}
	// Replace some with advanced
	for (var i = 0; i < UPGRADE_CONFIG.machines.advancedClam.amount; i++) {
		if (clamTypes[i]) {
			clamTypes[i] = 'advancedClam';
		}
	}
	// Replace some with premium
	for (var i = 0; i < UPGRADE_CONFIG.machines.premiumClam.amount; i++) {
		if (clamTypes[i]) {
			clamTypes[i] = 'premiumClam';
		}
	}
	// Place clams
	game.clamSpawnPoints = [];
	clamTypes.forEach(function (type, i) {
		var isRight = i % 2 === 1;
		var baseX = isRight ? rightStart : leftStart;
		var direction = isRight ? -1 : 1;
		var position = Math.floor(i / 2);
		var x = baseX + direction * position * spacing;
		var sprite = LK.getAsset(type, {
			anchorX: 0.5,
			anchorY: 1,
			x: x,
			y: y,
			scaleX: isRight ? -0.5 : 0.5,
			// Flip right-side clams
			scaleY: 0.5
		});
		// Store spawn point for this clam
		game.clamSpawnPoints.push({
			x: x + (isRight ? -75 : 75),
			// 25% from edge
			y: y - 50,
			// Slightly above clam
			type: type,
			isRight: isRight
		});
		clamContainer.addChild(sprite);
	});
}
updateClamVisuals();
updateCoralDecorations();
updateTreasureDecorations();
// Add this to the end of the game initialization code, after updating the clam visuals
function updateAllUpgradeTexts() {
	// Process left column upgrades
	leftColumnUpgrades.forEach(function (upgrade) {
		var category = upgrade[0];
		var key = upgrade[1];
		var upgradeConfig = UPGRADE_CONFIG[category][key];
		// Find the cost text for this upgrade
		Object.values(tabContainers).forEach(function (tabContainer) {
			tabContainer.children.forEach(function (child) {
				if (child.text && child.text.includes("BP") && child.y > tabContainer.children.find(function (c) {
					return c.text === upgradeConfig.name;
				}).y) {
					// Check if max level reached
					if (upgradeConfig.currentLevel >= upgradeConfig.maxLevel) {
						child.setText("SOLD OUT");
					}
				}
			});
		});
	});
	// Process right column upgrades
	rightColumnUpgrades.forEach(function (upgrade) {
		var category = upgrade[0];
		var key = upgrade[1];
		if (category === 'machines') {
			var upgradeConfig = UPGRADE_CONFIG[category][key];
			var totalClams = UPGRADE_CONFIG.machines.basicClam.amount + UPGRADE_CONFIG.machines.advancedClam.amount + UPGRADE_CONFIG.machines.premiumClam.amount;
			// Find the cost text for this upgrade
			Object.values(tabContainers).forEach(function (tabContainer) {
				tabContainer.children.forEach(function (child) {
					if (child.text && child.text.includes("BP") && child.y > tabContainer.children.find(function (c) {
						return c.text === upgradeConfig.name;
					}).y) {
						// Check if sold out based on clam logic
						if (totalClams >= 4 && key === 'basicClam' || UPGRADE_CONFIG.machines.basicClam.amount <= UPGRADE_CONFIG.machines.advancedClam.amount && key === 'advancedClam' || UPGRADE_CONFIG.machines.advancedClam.amount <= UPGRADE_CONFIG.machines.premiumClam.amount && key === 'premiumClam') {
							child.setText("SOLD OUT");
						}
					}
				});
			});
		} else if (category === 'machine') {
			var upgradeConfig = UPGRADE_CONFIG[category][key];
			// Find the cost text for this upgrade
			Object.values(tabContainers).forEach(function (tabContainer) {
				tabContainer.children.forEach(function (child) {
					if (child.text && child.text.includes("BP") && child.y > tabContainer.children.find(function (c) {
						return c.text === upgradeConfig.name;
					}).y) {
						// Check if max level reached
						if (upgradeConfig.currentLevel >= upgradeConfig.maxLevel) {
							child.setText("SOLD OUT");
						}
					}
				});
			});
		}
	});
}
// Call the function after creating all upgrades
updateAllUpgradeTexts();
// Initialize game variables 
game.growingBubble = null;
game.lastMouthState = false; // Track previous mouth state
game.mouthOpenDuration = 0; // Track how long mouth has been open
game.MOUTH_OPEN_THRESHOLD = 10; // Frames required with mouth open to start bubble
game.MIN_SPAWN_SIZE = 25; // Minimum initial bubble size
game.blowCooldown = 0; // Cooldown timer between bubble starts
game.BLOW_COOLDOWN_TIME = 15; // Frames to wait between new bubbles
game.maxBubbleSize = 120; // Increased by 30% 
game.growthRate = UPGRADE_EFFECTS.quickBreath.baseValue * (1 + UPGRADE_EFFECTS.quickBreath.incrementPercent / 100 * UPGRADE_CONFIG.player.quickBreath.currentLevel);
game.bubblePool = Array(250).fill(null).map(function () {
	return new Bubble();
});
game.activeBubbles = [];
game.MAX_BUBBLES = 200; // Active bubble limit
game.bubblePool.forEach(function (bubble) {
	game.addChild(bubble);
});
game.baseSpawnRate = 180; // Every 3 seconds
function spawnBubble(x, y, size) {
	var direction = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 0;
	var isAutoPop = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : false;
	if (game.activeBubbles.length >= game.MAX_BUBBLES) {
		return null;
	}
	// Find first available bubble in pool
	var bubble = game.bubblePool.find(function (b) {
		return !b.visible;
	});
	if (!bubble) {
		return null;
	}
	bubble.activate(x, y, size);
	if (isAutoPop) {
		bubble.verticalVelocity = bubble.floatSpeed;
		bubble.driftX = direction * (Math.random() * 0.8 + 0.5);
	} else {
		bubble.verticalVelocity = -(Math.random() * 2 + 4);
		bubble.driftX = direction * (Math.random() * 1.5 + 2);
	}
	game.activeBubbles.push(bubble);
	return bubble;
}
// Initialize game variables
//<Assets used in the game will automatically appear here>
game.bp = 0; // Track total BP 
game.combo = 0;
game.lastPopTime = 0;
game.COMBO_WINDOW = 60; // 1 second in frames
function formatBP(value) {
	var units = ['', 'K', 'M', 'B', 'T'];
	var unitIndex = 0;
	while (value >= 1000 && unitIndex < units.length - 1) {
		value /= 1000;
		unitIndex++;
	}
	return Math.floor(value * 10) / 10 + units[unitIndex];
}
// Create BP display text (add near game initialization)
var bpText = new Text2("0 BP", {
	size: 120,
	fill: 0xFFFFFF,
	stroke: 0x33caf8,
	strokeThickness: 4,
	font: "Impact",
	fontWeight: "bold"
});
bpText.anchor.set(1, 0);
bpText.x = game.width - 20;
bpText.y = 20;
game.addChild(bpText);
game.addBP = function (points, x, y, isAutoPop) {
	var currentTime = LK.ticks;
	// Only update combo if it's not an auto-pop
	if (!isAutoPop) {
		if (currentTime - game.lastPopTime < game.COMBO_WINDOW) {
			game.combo++;
			points *= 1 + game.combo * 0.1; // 10% bonus per combo
		} else {
			game.combo = 0;
		}
		game.lastPopTime = currentTime;
	}
	// Ensure points is at least 1
	points = Math.max(1, Math.floor(points));
	game.bp += points;
	bpText.setText(formatBP(game.bp) + " BP");
	// Set size and color based on point value
	var textSize = 96;
	var textColor = 0xFFFF00; // Default yellow
	if (points > 100) {
		textSize = 120;
		textColor = 0xFF0000; // Red for >100
	} else if (points > 50) {
		textSize = 108;
		textColor = 0xFFA500; // Orange for 51-100
	}
	// Always show point text regardless of auto or manual pop
	var pointText = new Text2("+" + points, {
		size: textSize,
		fill: textColor,
		font: "Impact",
		fontWeight: 'bold'
	});
	pointText.anchorX = 0.5;
	pointText.anchorY = 0.5;
	pointText.x = x;
	pointText.y = y;
	game.addChild(pointText);
	tween(pointText, {
		y: pointText.y - 100,
		alpha: 0
	}, {
		duration: 1200,
		onFinish: function onFinish() {
			pointText.destroy();
		}
	});
	// Only show combo text if it's a manual pop and we have a combo
	if (!isAutoPop && game.combo > 0) {
		var comboText = new Text2("x" + (game.combo + 1), {
			size: 96,
			fill: 0xFFA500,
			stroke: 0x000000,
			strokeThickness: 4,
			fontWeight: 'bold'
		});
		comboText.anchorX = 0.5;
		comboText.anchorY = 0;
		comboText.x = game.width / 2;
		comboText.y = 20;
		game.addChild(comboText);
		tween(comboText, {
			alpha: 0
		}, {
			duration: 500,
			onFinish: function onFinish() {
				comboText.destroy();
			}
		});
	}
};
game.update = function () {
	updateCoralBubbles();
	// Update mouth state and duration
	if (!game.lastMouthState) {
		game.mouthOpenDuration = 0;
	}
	if (facekit.mouthOpen) {
		game.mouthOpenDuration++;
	} else {
		game.mouthOpenDuration = 0;
	}
	// Only allow bubble creation if menu is closed and mouth has been open long enough
	if (!menuOpen && facekit.mouthOpen && game.mouthOpenDuration >= game.MOUTH_OPEN_THRESHOLD) {
		// Only allow new bubbles after cooldown
		if (!game.growingBubble && game.blowCooldown <= 0) {
			var spawnX = playerMask.x;
			var spawnY = playerMask.y + playerMask.height * 0.15;
			var sizeVarianceLevel = UPGRADE_CONFIG.player.sizeVariance.currentLevel;
			var minSizeMultiplier = 1 + 0.1 * sizeVarianceLevel;
			var adjustedMinSize = game.MIN_SPAWN_SIZE * minSizeMultiplier;
			game.growingBubble = spawnBubble(spawnX, spawnY, adjustedMinSize, 0, false);
			if (game.growingBubble) {
				game.blowCooldown = game.BLOW_COOLDOWN_TIME;
			}
		}
		if (game.growingBubble) {
			game.growingBubble.x = playerMask.x;
			game.growingBubble.y = playerMask.y + playerMask.height * 0.15;
			game.growingBubble.size = Math.min(game.growingBubble.size + game.growthRate, game.maxBubbleSize);
			game.growingBubble.verticalVelocity = 0;
			game.growingBubble.driftX = 0;
		}
	} else {
		if (game.growingBubble) {
			// Recalculate float speed and lifetime based on final size
			game.growingBubble.floatSpeed = 50 * (120 / game.growingBubble.size * (0.9 + Math.random() * 0.2)) / 60;
			game.growingBubble.initLifetime();
			// Then apply the release velocity
			game.growingBubble.verticalVelocity = -10;
			game.growingBubble.driftX = (Math.random() * 2 - 1) * 2.5;
			var twinLevel = UPGRADE_CONFIG.player.twinBubbles.currentLevel;
			if (twinLevel > 0 && Math.random() < twinLevel * 0.05) {
				// 5/10/15% chance based on level
				var twinBubble = spawnBubble(game.growingBubble.x + (Math.random() * 40 - 20), game.growingBubble.y + (Math.random() * 40 - 20), game.growingBubble.size * 0.9, game.growingBubble.driftX * 0.8, false);
				if (twinBubble) {
					// Link the bubbles
					game.twinBubbles.push({
						bubble1: game.growingBubble,
						bubble2: twinBubble,
						popped: false,
						timestamp: LK.ticks
					});
				}
			}
			game.growingBubble = null;
			var breathLevel = UPGRADE_CONFIG.player.bubbleBreath.currentLevel;
			if (breathLevel > 0) {
				// Calculate chances for extra bubbles
				var extraBubbles = 0;
				// Level 1: 20% for +1
				// Level 2: 40% for +1
				// Level 3: 60% for +1, 20% for +2
				// Level 4: 80% for +1, 40% for +2
				var rand = Math.random();
				if (rand < breathLevel * 0.2) {
					extraBubbles++;
					// Check for 2nd extra bubble at level 3-4
					if (breathLevel >= 3 && rand < (breathLevel - 2) * 0.2) {
						extraBubbles++;
					}
				}
				// Spawn extra bubbles
				for (var i = 0; i < extraBubbles; i++) {
					var angle = Math.random() * Math.PI * 2;
					var distance = 20 + Math.random() * 30;
					var extraX = playerMask.x + Math.cos(angle) * distance;
					var extraY = playerMask.y + playerMask.height * 0.15 + Math.sin(angle) * distance;
					var extraSize = Math.max(game.MIN_SPAWN_SIZE, game.growingBubble.size * (0.6 + Math.random() * 0.2));
					var extraBubble = spawnBubble(extraX, extraY, extraSize, 0, false);
					if (extraBubble) {
						extraBubble.verticalVelocity = -8 - Math.random() * 4;
						extraBubble.driftX = Math.random() * 4 - 2;
					}
				}
			}
			game.mouthOpenDuration = 0;
		}
	}
	// Update cooldown timer
	if (game.blowCooldown > 0) {
		game.blowCooldown--;
	}
	game.lastMouthState = facekit.mouthOpen;
	updateClams();
	// Inside game.update, after other updates
	if (UPGRADE_CONFIG.player.autoPop.currentLevel > 0) {
		// 8 seconds (480 frames) base, reduced by 2 seconds (120 frames) per level
		if (LK.ticks % Math.max(60, 660 - UPGRADE_CONFIG.player.autoPop.currentLevel * 120) === 0) {
			var fish = new Fish();
			game.addChild(fish);
		}
	}
	if (game.activeBubbles.length < game.MAX_BUBBLES) {
		if (LK.ticks % game.baseSpawnRate == 0) {
			var x = Math.random() * (game.width - 200) + 100;
			spawnBubble(x, game.height + 100, 100, 0, true);
		}
	}
	// Clean up old twin bubble pairs
	game.twinBubbles = game.twinBubbles.filter(function (pair) {
		// Remove pairs where both bubbles are gone or time expired (60 frames = 1 second)
		if (!pair.bubble1.visible || !pair.bubble2.visible || pair.popped && LK.ticks - pair.timestamp > 60) {
			return false;
		}
		return true;
	});
	game.activeBubbles.forEach(function (bubble) {
		if (bubble.update) {
			bubble.update();
		}
	});
};
// Handle touch/mouse events for the game
game.down = function (x, y, obj) {
	var localX = x - menuContainer.x;
	var localY = y - menuContainer.y;
	// Tab click handling
	var tabBounds = {
		x: -menuTab.width * menuTab.scaleX / 2,
		y: -menuTab.height * menuTab.scaleY,
		width: menuTab.width * menuTab.scaleX,
		height: menuTab.height * menuTab.scaleY
	};
	if (localX >= tabBounds.x && localX <= tabBounds.x + tabBounds.width && localY >= tabBounds.y && localY <= tabBounds.y + tabBounds.height) {
		menuOpen = !menuOpen;
		var targetY = menuOpen ? menuTab.height : game.height;
		if (menuOpen) {
			game.setChildIndex(menuContainer, game.children.length - 1);
		}
		tween(menuContainer, {
			y: targetY //{9O.1}
		}, {
			//{9O.2}
			duration: 300,
			//{9O.3}
			easing: tween.easeOutBack,
			//{9O.4}
			onFinish: function onFinish() {
				tabsContainer.visible = menuOpen; // Show/hide tabs based on menu state //{9Q.1}
				if (!menuOpen) {
					//{9Q.2}
					game.setChildIndex(menuContainer, 1); //{9Q.3}
				} //{9Q.4}
			} //{9Q.5}
		});
		return true;
	}
	if (menuOpen) {
		return true; // Let containers handle their own clicks
	}
	// Bubble popping logic remains the same
	var popped = false;
	// NEW:
	for (var i = game.activeBubbles.length - 1; i >= 0; i--) {
		var bubble = game.activeBubbles[i];
		var dx = x - bubble.x;
		var dy = y - bubble.y;
		var distance = Math.sqrt(dx * dx + dy * dy);
		if (!popped && distance <= bubble.size / 2 + 30 && bubble.down) {
			bubble.down();
			popped = true;
			break;
		}
	}
}; ===================================================================
--- original.js
+++ change.js
@@ -1643,32 +1643,20 @@
 	hitContainer.down = function () {
 		var cost = getUpgradeCost(upgrade);
 		// Special handling for colors that are already purchased
 		if (category === 'colors' && upgrade.currentLevel > 0) {
-			// Add this for colors when they're first purchased:
-			if (upgrade.currentLevel === 1) {
-				// If this is the first color purchased or auto is selected, make it active
-				if (UPGRADE_CONFIG.gameSettings.activeColor === "auto" || UPGRADE_CONFIG.colors[UPGRADE_CONFIG.gameSettings.activeColor].currentLevel === 0) {
-					UPGRADE_CONFIG.gameSettings.activeColor = key;
-					game.showMessage(upgrade.name + " activated");
-				}
-				refreshUpgradeTab('colors');
-				return true;
-			}
 			// Toggle this color as active
 			if (UPGRADE_CONFIG.gameSettings.activeColor === key) {
 				// If already active, switch to auto
 				UPGRADE_CONFIG.gameSettings.activeColor = "auto";
 				game.showMessage("Auto color mode");
-				// Update color indicator
-				refreshUpgradeTab('colors');
 			} else {
 				// Otherwise activate this color
 				UPGRADE_CONFIG.gameSettings.activeColor = key;
 				game.showMessage(upgrade.name + " activated");
-				// Update color indicator
-				refreshUpgradeTab('colors');
 			}
+			// Refresh the tab to update the text displays
+			refreshUpgradeTab('colors');
 			return true;
 		}
 		// Regular upgrade purchase logic follows...
 		if (category === 'colors' && upgrade.requires) {
:quality(85)/https://cdn.frvr.ai/67ba4b7d38fe57e0d4bf9475.png%3F3) 
 A white bubble with a black outline Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
:quality(85)/https://cdn.frvr.ai/67bfab127754338fc503f264.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67c22f097033e124bf42ebe7.png%3F3) 
 A filled in white circle.. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
:quality(85)/https://cdn.frvr.ai/67c240d2f2b58146864d0ea7.png%3F3) 
 A yellow star. Cartoon.. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
:quality(85)/https://cdn.frvr.ai/67c33edae3ef98e836e2092f.png%3F3) 
 a game logo for a game called 'Bubble Blower Tycoon' about a happy purple pufferfish with yellow fins and spines that builds an underwater empire of bubbles. Cartoon. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
:quality(85)/https://cdn.frvr.ai/67c34097e3ef98e836e2095a.png%3F3) 
 an SVG of the word 'Start'. word should be yellow and the font should look like its made out of bubbles. cartoon. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
:quality(85)/https://cdn.frvr.ai/67c52c6b3591963c82f8bf83.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67cb3ceb2dbb4136c61192f7.png%3F3) 
 A outstretched straight octopus tentacle. Green with purple suckers. Cartoon.. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
:quality(85)/https://cdn.frvr.ai/67cb782f9a1a3019c1ad978e.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67cb789e9a1a3019c1ad9792.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67cb790e9a1a3019c1ad9796.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67e08245e50564a967e16f72.png%3F3) 
 A colorful underwater coral reef background. Cartoon Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
:quality(85)/https://cdn.frvr.ai/67e0b732e50564a967e16fe5.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67e0b774e50564a967e16fe9.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67e0b7b4e50564a967e16fed.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67e0b83be50564a967e16ff5.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67e0b8b7e50564a967e17001.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67e0b8fee7971efb59b948a3.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67e0b906e50564a967e17005.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67e0b94ae50564a967e17009.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67e0b987e50564a967e1700d.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67e0b9b1e50564a967e17011.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67e0b9e5e50564a967e17015.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67e0ba09e50564a967e17019.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67e0ba34e50564a967e1701d.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67e0ba60e50564a967e17021.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67e0ba8de50564a967e17025.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67e0bbe2e50564a967e17062.png%3F3) 
 A white bubble with a black outline. Pixel art.. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
:quality(85)/https://cdn.frvr.ai/67e0bc03e50564a967e17067.png%3F3) 
 bubblelow
Sound effect
backgroundmusic
Music
bubblehigh
Sound effect
bubble1
Sound effect
bubble2
Sound effect
bubble3
Sound effect
bubble4
Sound effect
blowing
Sound effect
bubbleshoot
Sound effect
fishtank
Sound effect
menuopen
Sound effect
upgrade
Sound effect
jellyfish
Sound effect
titlemusic
Music
startbutton
Sound effect