User prompt
update with: // Add near game.update function updateClams() { Object.entries(UPGRADE_CONFIG.machines).forEach(([type, machine]) => { if(machine.amount > 0 && (LK.ticks % (machine.production * 60)) === 0) { let speedMultiplier = Math.pow(0.9, UPGRADE_CONFIG.machine.autoBubbleSpeed.currentLevel); let amount = machine.amount; for(let i = 0; i < amount; i++) { if(game.bubbles.length < game.MAX_BUBBLES) { let x = Math.random() * (game.width - 200) + 100; spawnSplitBubble(x, game.height + 100, machine.bubbleSize, 0); } } } }); } // Add this line inside game.update: updateClams();
User prompt
update as needed with: var UPGRADE_CONFIG = { player: { lungCapacity: { name: "Lung Capacity", baseCost: 50, costScale: 2, maxLevel: 10, currentLevel: 0 }, quickBreath: { name: "Quick Breath", baseCost: 75, costScale: 2, maxLevel: 10, currentLevel: 0 } }, machines: { // Moved up before machine upgrades basicClam: { name: "Basic Clam", baseCost: 100, costScale: 1.15, amount: 0, production: 3, bubbleSize: 25 }, advancedClam: { name: "Advanced Clam", baseCost: 1000, costScale: 1.18, amount: 0, production: 2, bubbleSize: 35, unlockCost: 1000 }, premiumClam: { name: "Premium Clam", baseCost: 10000, costScale: 1.20, amount: 0, production: 1, bubbleSize: 50, unlockCost: 10000 } }, machine: { bubbleDurability: { name: "Bubble Durability", baseCost: 200, costScale: 3, maxLevel: 5, currentLevel: 0 }, autoBubbleSpeed: { name: "Auto-Bubble Speed", baseCost: 150, costScale: 2.5, maxLevel: 10, currentLevel: 0 }, autoPop: { name: "Auto-Pop", baseCost: 500, costScale: 2, maxLevel: 5, currentLevel: 0 } } }; // Add this helper function to calculate costs 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)); } } // Then update the cost text creation in the menu setup: Object.entries(UPGRADE_CONFIG).forEach(function(category, categoryIndex) { Object.entries(category[1]).forEach(function(upgrade, index) { // Create name text (unchanged) var nameText = new Text2(upgrade[1].name, { size: 96, fill: 0xFFFFFF, stroke: 0x000000, strokeThickness: 2, font: "Impact" }); // Update cost text to use the helper function var cost = getUpgradeCost(upgrade[1]); var costText = new Text2(cost + " BP", { size: 96, fill: 0xFFFF00, stroke: 0x000000, strokeThickness: 2, font: "Impact" }); // Rest of the positioning code remains the same nameText.x = categoryIndex * columnWidth - 924; nameText.y = startY + index * upgradeSpacing; costText.x = categoryIndex * columnWidth - 924; costText.y = startY + index * upgradeSpacing + 100; nameText.upgrade = upgrade[0]; upgradeTexts.push(nameText); upgradeTexts.push(costText); menuTextContainer.addChild(nameText); menuTextContainer.addChild(costText); }); });
User prompt
Please fix the bug: 'TypeError: can't convert undefined to object' in or related to this line: 'Object.entries(UPGRADE_CONFIG.machine.machines).forEach(function (_ref) {' Line Number: 299
User prompt
update with: function updateClams() { Object.entries(UPGRADE_CONFIG.machines).forEach(([type, machine]) => { if(machine.amount > 0 && (LK.ticks % (machine.production * 60)) === 0) { let speedMultiplier = Math.pow(0.9, UPGRADE_CONFIG.machine.autoBubbleSpeed.currentLevel); let amount = machine.amount; for(let i = 0; i < amount; i++) { if(game.bubbles.length < game.MAX_BUBBLES) { let x = Math.random() * (game.width - 200) + 100; spawnSplitBubble(x, game.height + 100, machine.bubbleSize, 0); } } } }); }
User prompt
update with: var startY = 150; var upgradeSpacing = 250; var columnWidth = 1024; // Create arrays to hold upgrade categories in desired order var leftColumnUpgrades = [ ['player', 'lungCapacity'], ['player', 'quickBreath'] ]; var rightColumnUpgrades = [ ['machines', 'basicClam'], ['machines', 'advancedClam'], ['machines', 'premiumClam'], ['machine', 'bubbleDurability'], ['machine', 'autoBubbleSpeed'], ['machine', 'autoPop'] ]; // Function to create upgrade text function createUpgradeText(category, key, index, isLeftColumn) { var upgrade = UPGRADE_CONFIG[category][key]; // Create name text var nameText = new Text2(upgrade.name, { size: 96, fill: 0xFFFFFF, stroke: 0x000000, strokeThickness: 2, font: "Impact" }); // Create cost text var cost = getUpgradeCost(upgrade); var costText = new Text2(cost + " BP", { size: 96, fill: 0xFFFF00, stroke: 0x000000, strokeThickness: 2, font: "Impact" }); // Position texts var xOffset = isLeftColumn ? -924 : 100; // Adjust these values as needed nameText.x = xOffset; nameText.y = startY + index * upgradeSpacing; costText.x = xOffset; costText.y = startY + index * upgradeSpacing + 100; nameText.upgrade = key; nameText.category = category; upgradeTexts.push(nameText); upgradeTexts.push(costText); menuTextContainer.addChild(nameText); menuTextContainer.addChild(costText); } // Clear existing texts upgradeTexts.forEach(text => text.destroy()); upgradeTexts = []; // Create left column leftColumnUpgrades.forEach((upgrade, index) => { createUpgradeText(upgrade[0], upgrade[1], index, true); }); // Create right column rightColumnUpgrades.forEach((upgrade, index) => { createUpgradeText(upgrade[0], upgrade[1], index, false); });
User prompt
Updated as needed with: if (!menuOpen && facekit.mouthOpen && facekit.mouthCenter) { // Get vertical distance between top and bottom lip const mouthOpenness = Math.abs(facekit.mouthTop.y - facekit.mouthBottom.y); // Only spawn bubble if mouth is open beyond threshold // Adjust threshold value (20) to tune sensitivity if (mouthOpenness > 20) { // Calculate spawn position relative to pufferfish mask var spawnX = playerMask.x; var spawnY = playerMask.y + playerMask.height * 0.15; if (!game.growingBubble) { game.growingBubble = new Bubble(); game.growingBubble.size = 25; game.addChild(game.growingBubble); game.bubbles.push(game.growingBubble); } // Rest of bubble growth code... } } ↪💡 Consider importing and using the following plugins: @upit/facekit.v1
User prompt
Please fix the bug: 'TypeError: undefined is not an object (evaluating 'facekit.mouthTop.y')' in or related to this line: 'var mouthOpenness = Math.abs(facekit.mouthTop.y - facekit.mouthBottom.y);' Line Number: 752
User prompt
Update with: game.lastBubbleTime = 0; game.BUBBLE_COOLDOWN = 8; // Frames between bubble starts (adjust between 5-10)
User prompt
Update with: if (!menuOpen && facekit.mouthOpen && LK.ticks - game.lastBubbleTime > game.BUBBLE_COOLDOWN) { game.lastBubbleTime = LK.ticks;
User prompt
Update with: if (!menuOpen && facekit.mouthOpen && LK.ticks - game.lastBubbleTime > game.BUBBLE_COOLDOWN) { // Only start a new bubble if we don't have one growing if (!game.growingBubble) { game.lastBubbleTime = LK.ticks; // Calculate spawn position relative to pufferfish mask var spawnX = playerMask.x; var spawnY = playerMask.y + playerMask.height * 0.15; game.growingBubble = new Bubble(); game.growingBubble.size = 25; game.addChild(game.growingBubble); game.bubbles.push(game.growingBubble); } if (game.growingBubble) { game.growingBubble.x = spawnX; game.growingBubble.y = spawnY; game.growingBubble.size = Math.min(game.growingBubble.size + game.growthRate, game.maxBubbleSize); game.growingBubble.verticalVelocity = 0; game.growingBubble.driftX = 0; } } ↪💡 Consider importing and using the following plugins: @upit/facekit.v1
User prompt
Update with: // In Bubble class, modify getBP: getBP: function() { // Reduce multiplier significantly and adjust power to make size matter more return Math.floor(Math.pow(self.size, 1.6) * 0.003); } // Now a max size bubble (160px) gives: 160^1.6 * 0.003 ≈ 45 BP
User prompt
Update with: player: { lungCapacity: { baseCost: 50, // About 1-2 max bubbles costScale: 2.0 // Steeper scaling }, quickBreath: { baseCost: 100, // 2-3 max bubbles costScale: 2.0 } }, machines: { basicClam: { baseCost: 300, // 6-7 max bubbles costScale: 1.25 // Slightly steeper scaling }, advancedClam: { baseCost: 2000, // Requires running basic clams for a while costScale: 1.3, unlockCost: 2000 }, premiumClam: { baseCost: 15000, // True end-game content costScale: 1.35, unlockCost: 15000 } }, machine: { bubbleDurability: { baseCost: 250, costScale: 2.8 // Significant scaling }, autoBubbleSpeed: { baseCost: 500, costScale: 2.5 }, autoPop: { baseCost: 1000, costScale: 2.3 } }
User prompt
Update with: getBP: function() { // More appropriate scaling for these size ranges (25-160) return Math.max(1, Math.floor(Math.pow(self.size, 1.4) * 0.02)); }
User prompt
Update with: game.MIN_MOUTH_OPENING = 0.2; // Minimum mouth opening threshold (0-1) game.MIN_SPAWN_SIZE = 25; // Minimum initial bubble size game.lastMouthState = false; // Track previous mouth state
User prompt
Update with: if (!menuOpen && facekit.mouthOpen) { // Get mouth opening amount (assuming facekit provides this) const mouthOpening = facekit.mouthOpenAmount || 0; // Only create/grow bubbles if mouth is opened enough if (mouthOpening >= game.MIN_MOUTH_OPENING) { // Calculate spawn position relative to pufferfish mask const spawnX = playerMask.x; const spawnY = playerMask.y + playerMask.height * 0.15; // Only create new bubble if we don't have one and weren't just blowing if (!game.growingBubble && !game.lastMouthState) { game.growingBubble = new Bubble(); game.growingBubble.size = game.MIN_SPAWN_SIZE; game.addChild(game.growingBubble); game.bubbles.push(game.growingBubble); } if (game.growingBubble) { game.growingBubble.x = spawnX; game.growingBubble.y = spawnY; // Scale growth rate by how open the mouth is const growthMultiplier = Math.min(1, mouthOpening / 0.5); game.growingBubble.size = Math.min( game.growingBubble.size + (game.growthRate * growthMultiplier), game.maxBubbleSize ); game.growingBubble.verticalVelocity = 0; game.growingBubble.driftX = 0; } } game.lastMouthState = true; } else { if (game.growingBubble) { game.growingBubble.verticalVelocity = -12; game.growingBubble.driftX = (Math.random() * 2 - 1) * 2.5; game.growingBubble = null; } game.lastMouthState = false; } ↪💡 Consider importing and using the following plugins: @upit/facekit.v1
User prompt
Update with: 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
User prompt
Update with: if (!menuOpen && facekit.mouthOpen) { // Only allow new bubbles after cooldown if (!game.growingBubble && game.blowCooldown <= 0) { // Calculate spawn position relative to pufferfish mask const spawnX = playerMask.x; const spawnY = playerMask.y + playerMask.height * 0.15; game.growingBubble = new Bubble(); game.growingBubble.size = game.MIN_SPAWN_SIZE; game.addChild(game.growingBubble); game.bubbles.push(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) { game.growingBubble.verticalVelocity = -12; game.growingBubble.driftX = (Math.random() * 2 - 1) * 2.5; game.growingBubble = null; } } // Update cooldown timer if (game.blowCooldown > 0) { game.blowCooldown--; } ↪💡 Consider importing and using the following plugins: @upit/facekit.v1
User prompt
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 Update with:
User prompt
Update only as needed with: // Modify the if statement to: if (!menuOpen && facekit.mouthOpen) { if (!game.lastMouthState) { game.mouthOpenDuration = 0; } game.mouthOpenDuration++; if (game.mouthOpenDuration >= game.MOUTH_OPEN_THRESHOLD) { // Existing bubble creation code stays the same ``` Then at the very end of the else block where it releases the bubble, add: ```javascript game.mouthOpenDuration = 0; ``` And finally before the updateClams() call: ```javascript game.lastMouthState = facekit.mouthOpen;
User prompt
Update with: // Near the top of the game code, add maximum upgrade effects: 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 } };
User prompt
Update with: // Modify Bubble class to handle durability splits: 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; spawnSplitBubble( self.x, self.y, newSize, Math.cos(angle), // More even distribution false ); } }
User prompt
Update with: // Modify updateClams to use autoBubbleSpeed: function updateClams() { Object.entries(UPGRADE_CONFIG.machines).forEach(([type, machine]) => { if(machine.amount > 0) { // Calculate production time with speed upgrade let baseTime = machine.production * 60; // Convert to frames let speedMultiplier = Math.pow( (1 - UPGRADE_EFFECTS.autoBubbleSpeed.decrementPercent / 100), UPGRADE_CONFIG.machine.autoBubbleSpeed.currentLevel ); let adjustedTime = Math.max(1, Math.floor(baseTime * speedMultiplier)); if(LK.ticks % adjustedTime === 0) { for(let i = 0; i < machine.amount; i++) { if(game.bubbles.length < game.MAX_BUBBLES) { let x = Math.random() * (game.width - 200) + 100; spawnSplitBubble(x, game.height + 100, machine.bubbleSize, 0); } } } } }); }
User prompt
Update game.update with: game.maxBubbleSize = UPGRADE_EFFECTS.lungCapacity.baseValue * (1 + (UPGRADE_CONFIG.player.lungCapacity.currentLevel * UPGRADE_EFFECTS.lungCapacity.incrementPercent / 100)); // Calculate growth rate with upgrades game.growthRate = UPGRADE_EFFECTS.quickBreath.baseValue * (1 + (UPGRADE_CONFIG.player.quickBreath.currentLevel * UPGRADE_EFFECTS.quickBreath.incrementPercent / 100));
Code edit (2 edits merged)
Please save this source code
User prompt
Update with: var leftColumnUpgrades = [ ['player', 'lungCapacity'], ['player', 'quickBreath'], ['player', 'autoPop'] // Added here ]; var rightColumnUpgrades = [ ['machines', 'basicClam'], ['machines', 'advancedClam'], ['machines', 'premiumClam'], ['machine', 'bubbleDurability'], ['machine', 'autoBubbleSpeed'] // autoPop removed from here ];
/****
* 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; // Add timestamp tracking
var sprite = self.attachAsset('bubble', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.9
});
self.size = 100;
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) {
// Add cooldown check (100ms)
var currentTime = Date.now();
if (currentTime - self.lastPopTime < 100) {
return true; // Ignore clicks too close together
}
self.lastPopTime = currentTime;
var index = game.bubbles.indexOf(self);
if (index > -1) {
game.bubbles.splice(index, 1);
}
var points = self.getBP();
game.addBP(points, self.x, self.y, false); // Pass position and flag for manual pop
// Only split if manually popped and large enough
if (self.size > 60 && !self.justSplit) {
var newSize = Math.max(self.MIN_SPLIT_SIZE, self.size * 0.6);
for (var i = 0; i < 2; i++) {
spawnSplitBubble(self.x, self.y, newSize, i === 0 ? -1 : 1);
}
}
self.destroy();
return true; // Stop event propagation
};
self.getBP = function () {
return Math.floor(Math.pow(self.size, 2) * 0.1);
};
self.update = function () {
self.lifetime++;
// Add subtle drift variation
if (self.lifetime % 60 === 0) {
// Every second
self.driftX += (Math.random() - 0.5) * 0.3; // Add small random drift
}
// Increase overall horizontal movement
self.x += self.driftX * 1.2; // 20% more horizontal movement
// Auto-pop or split when lifetime exceeded
if (self.lifetime > self.maxLifetime) {
// Just check size and not already split
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 = spawnSplitBubble(self.x, self.y, newSize, i === 0 ? -1 : 1, true);
split.maxLifetime *= 0.7; // Shorter lifetime for split bubbles
}
self.destroy();
return;
}
// If too small to split, just pop
self.autoPop();
return;
}
// Clear off-screen bubbles
if (self.y < -self.size) {
self.destroy();
return;
}
self.justSplit = false; // Clear the flag after first update
// More gradual vertical speed transition
if (self.verticalVelocity < self.floatSpeed) {
self.verticalVelocity += 0.08; // More gentle transition
}
self.y -= self.verticalVelocity;
// Gradually reduce horizontal speed after split
if (Math.abs(self.driftX) > (Math.random() * 20 - 10) / 60) {
self.driftX *= 0.98; // Slowly return to normal drift speed
}
self.x += self.driftX;
// Bounce off edges
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 () {
if (!self.autoPopDisplayed) {
var points = Math.floor(self.getBP() * 0.5); // Half points for auto-pop
game.addBP(points, self.x, self.y, true); // Added true flag for autoPop
self.autoPopDisplayed = true;
}
self.destroy();
return;
};
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
****/
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() {
Object.entries(UPGRADE_CONFIG.machine.machines).forEach(function (_ref) {
var _ref2 = _slicedToArray2(_ref, 2),
type = _ref2[0],
machine = _ref2[1];
if (machine.amount > 0 && LK.ticks % (machine.production * 60) === 0) {
var speedMultiplier = Math.pow(0.9, UPGRADE_CONFIG.machine.autoBubbleSpeed.currentLevel);
var amount = machine.amount;
for (var i = 0; i < amount; i++) {
if (game.bubbles.length < game.MAX_BUBBLES) {
var x = Math.random() * (game.width - 200) + 100;
spawnSplitBubble(x, game.height + 100, machine.bubbleSize, 0);
}
}
}
});
}
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);
var playerMask = new pufferMask();
game.addChild(playerMask);
var UPGRADE_CONFIG = {
player: {
lungCapacity: {
name: "Lung Capacity",
baseCost: 50,
costScale: 2,
maxLevel: 10,
currentLevel: 0
},
quickBreath: {
name: "Quick Breath",
baseCost: 75,
costScale: 2,
maxLevel: 10,
currentLevel: 0
}
},
machine: {
bubbleDurability: {
name: "Bubble Durability",
baseCost: 200,
costScale: 3,
maxLevel: 5,
currentLevel: 0
},
machines: {
basicClam: {
name: "Basic Clam",
baseCost: 100,
costScale: 1.15,
amount: 0,
production: 3,
// seconds per bubble
bubbleSize: 25
},
advancedClam: {
name: "Advanced Clam",
baseCost: 1000,
costScale: 1.18,
amount: 0,
production: 2,
bubbleSize: 35,
unlockCost: 1000
},
premiumClam: {
name: "Premium Clam",
baseCost: 10000,
costScale: 1.20,
amount: 0,
production: 1,
bubbleSize: 50,
unlockCost: 10000
}
},
autoBubbleSpeed: {
// Changed from machineSpeed
name: "Auto-Bubble Speed",
// Updated name
baseCost: 150,
costScale: 2.5,
maxLevel: 10,
currentLevel: 0
},
autoPop: {
name: "Auto-Pop",
baseCost: 500,
costScale: 2,
maxLevel: 5,
currentLevel: 0
}
}
};
// 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
// 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);
// Create text container AFTER panel scaling
var menuTextContainer = new Container();
menuContainer.addChild(menuTextContainer);
// Add upgrade texts to text container instead of panel
var upgradeTexts = [];
var startY = 150; // Increase initial offset
var upgradeSpacing = 250; // Increase space between upgrade groups
var columnWidth = 1024; // Half of 2048 for two columns
Object.entries(UPGRADE_CONFIG).forEach(function (category, categoryIndex) {
Object.entries(category[1]).forEach(function (upgrade, index) {
// Create name text
var nameText = new Text2(upgrade[1].name, {
size: 96,
fill: 0xFFFFFF,
stroke: 0x000000,
strokeThickness: 2,
font: "Impact"
});
// Create cost text
var costText = new Text2(upgrade[1].baseCost + " BP", {
size: 96,
fill: 0xFFFF00,
// Gold color for cost
stroke: 0x000000,
strokeThickness: 2,
font: "Impact"
});
// Position texts
nameText.x = categoryIndex * columnWidth - 924;
nameText.y = startY + index * upgradeSpacing; // Use new spacing
costText.x = categoryIndex * columnWidth - 924;
costText.y = startY + index * upgradeSpacing + 100;
nameText.upgrade = upgrade[0];
upgradeTexts.push(nameText);
upgradeTexts.push(costText);
menuTextContainer.addChild(nameText);
menuTextContainer.addChild(costText);
});
});
// 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;
// Initialize game variables
game.growingBubble = null;
game.maxBubbleSize = 160; // Increased by 30%
game.growthRate = 1.6; // Slightly increased to match new max size
game.MAX_BUBBLES = 125;
game.baseSpawnRate = 180; // Every 3 seconds
function spawnSplitBubble(parentX, parentY, size, direction) {
var isAutoPop = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : false;
var bubble = new Bubble();
bubble.x = parentX;
bubble.y = parentY;
bubble.size = size;
bubble.initLifetime(); // Recalculate after size is set
bubble.justSplit = true;
var speedMultiplier = 120 / size * (0.9 + Math.random() * 0.2);
if (isAutoPop) {
// Pure upward/sideways motion for natural splits
bubble.verticalVelocity = bubble.floatSpeed; // Start at float speed
bubble.driftX = direction * (Math.random() * 0.8 + 0.5);
} else {
// Manual pop physics
bubble.verticalVelocity = -(Math.random() * 2 + 4);
bubble.driftX = direction * (Math.random() * 1.5 + 2);
}
game.addChild(bubble);
game.bubbles.push(bubble);
return bubble;
}
game.bubbles = []; // Track bubble array
// 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;
}
game.bp += Math.floor(points);
bpText.setText(formatBP(game.bp) + " BP");
// Always show point text regardless of auto or manual pop
var pointText = new Text2("+" + Math.floor(points), {
size: 96,
fill: 0xFFFF00,
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 () {
// Only allow bubble creation if menu is closed
if (!menuOpen && facekit.mouthOpen) {
// Calculate spawn position relative to pufferfish mask
var spawnX = playerMask.x;
var spawnY = playerMask.y + playerMask.height * 0.15;
if (!game.growingBubble) {
game.growingBubble = new Bubble();
game.growingBubble.size = 25;
game.addChild(game.growingBubble);
game.bubbles.push(game.growingBubble);
}
if (game.growingBubble) {
game.growingBubble.x = spawnX;
game.growingBubble.y = spawnY;
game.growingBubble.size = Math.min(game.growingBubble.size + game.growthRate, game.maxBubbleSize);
game.growingBubble.verticalVelocity = 0;
game.growingBubble.driftX = 0;
}
} else if (game.growingBubble) {
// Release any growing bubble when menu opens
game.growingBubble.verticalVelocity = -12;
game.growingBubble.driftX = (Math.random() * 2 - 1) * 2.5;
game.growingBubble = null;
}
updateClams();
// Update all children (bubbles)
// Only spawn if under max bubbles
if (game.bubbles.length < game.MAX_BUBBLES) {
if (LK.ticks % game.baseSpawnRate == 0) {
var x = Math.random() * (game.width - 200) + 100;
spawnSplitBubble(x, game.height + 100, 100, 0);
}
}
// Clean up destroyed bubbles from array
game.bubbles = game.bubbles.filter(function (bubble) {
return !bubble.destroyed;
});
// Update all children (bubbles)
for (var i = game.bubbles.length - 1; i >= 0; i--) {
var bubble = game.bubbles[i];
if (bubble.update) {
bubble.update();
}
}
};
// Handle touch/mouse events for the game
game.down = function (x, y, obj) {
// Convert to local coordinates for container
var localX = x - menuContainer.x;
var localY = y - menuContainer.y;
// Check if clicked on menu tab
var tabBounds = {
x: -menuTab.width * menuTab.scaleX / 2,
// Account for scale
y: -menuTab.height * menuTab.scaleY,
// Account for scale
width: menuTab.width * menuTab.scaleX,
// Account for scale
height: menuTab.height * menuTab.scaleY // Account for scale
};
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;
// Move menu to top when opening
if (menuOpen) {
game.setChildIndex(menuContainer, game.children.length - 1);
}
tween(menuContainer, {
y: targetY
}, {
duration: 300,
easing: tween.easeOutBack,
onFinish: function onFinish() {
// Move menu back down in z-index when closed
if (!menuOpen) {
game.setChildIndex(menuContainer, 1); // Above background
}
}
});
return true;
}
// Check for clicks outside when menu is open
if (menuOpen) {
var menuBounds = {
x: -menuPanel.width / 2,
y: -menuPanel.height,
width: menuPanel.width,
height: menuPanel.height + menuTab.height
};
if (!(localX >= menuBounds.x && localX <= menuBounds.x + menuBounds.width && localY >= menuBounds.y && localY <= menuBounds.y + menuBounds.height)) {
menuOpen = false;
tween(menuContainer, {
y: game.height
}, {
duration: 300,
easing: tween.easeInBack
});
return true;
}
}
// Rest of click handling for bubbles...
};
var popped = false; // Track if we've popped any bubble
for (var i = game.bubbles.length - 1; i >= 0; i--) {
var bubble = game.bubbles[i];
// Calculate distance between click and bubble center
var dx = x - bubble.x;
var dy = y - bubble.y;
var distance = Math.sqrt(dx * dx + dy * dy);
// Only pop if we haven't popped anything this click
if (!popped && distance <= bubble.size / 2 + 10 && bubble.down) {
bubble.down();
popped = true;
break; // Exit loop after first pop
}
}
;
A treasure chest with gold coins. Cartoon.. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
A golden skull with diamonds for eyes. Cartoon.. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
A golden necklace with a ruby pendant. Cartoon.. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
A filled in white circle.. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
A yellow star. Cartoon.. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
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
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
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