User prompt
update with: // Handle decoration upgrades else if (category === 'decorations') { if (upgrade.amount >= upgrade.maxAmount) { costText.setText("SOLD OUT"); costText.fill = 0x888888; } else { costText.setText(getUpgradeCost(upgrade) + " BP"); costText.fill = 0xFFFF00; } }
Code edit (2 edits merged)
Please save this source code
User prompt
add this: // Add near the top of the game initialization game.faceTrackingEnabled = false;
User prompt
update with: // Find the pufferMask update function and replace it with this: self.update = function () { // Only use face tracking when enabled if (game.faceTrackingEnabled) { // 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; } } };
User prompt
update with: game.startGame = function () { // Remove title screen if (game.titleContainer) { game.titleContainer.destroy(); game.titleContainer = null; } // Exit title mode game.titleMode = false; // Initialize BP display bpText.visible = true; // Initialize pufferfish for animation (not visible yet) playerMask.visible = true; playerMask.scaleX = 0.2; playerMask.scaleY = 0.2; playerMask.alpha = 0.8; playerMask.x = game.width / 2; playerMask.y = game.height + 100; // Start animation sequence tween(playerMask, { x: game.width / 2, y: game.height / 2, scaleX: 0.8, scaleY: 0.8, alpha: 1 }, { duration: 1500, easing: tween.easeOutBack, onFinish: function() { // Enable face tracking after animation game.faceTrackingEnabled = true; // Make menu visible menuContainer.visible = true; // Update clam visuals updateClamVisuals(); updateTreasureDecorations(); // Start background music LK.playMusic('backgroundmusic', { fade: { start: 0, end: 0.5, duration: 2000 } }); } }); }; ↪💡 Consider importing and using the following plugins: @upit/tween.v1
Code edit (4 edits merged)
Please save this source code
User prompt
update with: if (game.tutorial && game.tutorial.stage === 2 && popped) { game.tutorial.poppedBubble = true; LK.setTimeout(function() { showTutorialPopup(3); }, 30); // Short delay to ensure pop animation completes return true; }
Code edit (2 edits merged)
Please save this source code
User prompt
reduce value of bubbles by 100 times
Code edit (3 edits merged)
Please save this source code
User prompt
update with: f (menuOpen) { game.setChildIndex(menuContainer, game.children.length - 1); // If we're in tutorial, make sure tutorial popup is above menu if (game.tutorialContainer) { LK.setTimeout(function() { game.setChildIndex(game.tutorialContainer, game.children.length - 1); }, 1); // Use tiny delay to run after current frame completes } }
Code edit (1 edits merged)
Please save this source code
User prompt
update with: // Find the tabButton.down function in your code (in the part where tabs are created) // It should look something like this: tabButton.down = function () { if (tab !== currentTab) { // Update tab appearance Object.keys(tabButtons).forEach(function (t) { if (tabButtons[t]) { tabButtons[t].alpha = t === tab ? 1.0 : 0.7; } }); // Remove old indicator if (tabsContainer.currentIndicator) { tabsContainer.removeChild(tabsContainer.currentIndicator); tabsContainer.currentIndicator.destroy(); } // Create new indicator var newIndicator = LK.getAsset('blower', { width: tabWidth, height: 10, color: 0xFFFF00, alpha: 1.0 }); // Position at the bottom of this tab newIndicator.x = tabButton.x - tabWidth / 2; newIndicator.y = tabHeight - 5; // Add to container and track it tabsContainer.addChild(newIndicator); tabsContainer.currentIndicator = newIndicator; // 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; // ADD THIS NEW CODE: // Check if this is the clams tab during tutorial if (game.tutorial && game.tutorial.stage === 5 && tab === 'clams') { LK.setTimeout(function() { showTutorialPopup(6); if (game.tutorialContainer) { game.setChildIndex(game.tutorialContainer, game.children.length - 1); } }, 1); } } return true; };
Code edit (5 edits merged)
Please save this source code
Code edit (4 edits merged)
Please save this source code
User prompt
import storage plugin ↪💡 Consider importing and using the following plugins: @upit/storage.v1
Code edit (3 edits merged)
Please save this source code
User prompt
Please fix the bug: 'Error: Invalid value. Only literals or 1-level deep objects/arrays containing literals are allowed.' in or related to this line: 'storage.gameData = gameData;' Line Number: 669
User prompt
Please fix the bug: 'TypeError: JSON is undefined' in or related to this line: 'storage.gameData = JSON.parse(JSON.stringify(gameData));' Line Number: 669
Code edit (1 edits merged)
Please save this source code
User prompt
update as needed with: function calculateOfflineProgress(timeDiff) { // Calculate basic BP as before... // Apply a stronger offline penalty (25% of normal production) totalBP *= 0.25; // Apply a hard cap totalBP = Math.min(totalBP, 8000); // Or apply diminishing returns var hoursMult = Math.min(1, 1 / Math.sqrt(secondsAway / 3600)); totalBP *= hoursMult; // Round to integer totalBP = Math.floor(totalBP); // Add the BP... }
Code edit (2 edits merged)
Please save this source code
User prompt
update with: function saveGame() { storage.bp = game.bp; try { // Store upgrades as JSON string storage.upgrades = JSON.stringify(UPGRADE_CONFIG); console.log("Saved upgrades: " + storage.upgrades); // Add debug output } catch (e) { console.log("Error saving upgrades: ", e); } storage.lastPlayTime = Date.now(); storage.tutorialComplete = game.tutorial.stage >= 7; }
User prompt
update with: // In loadGame function if (storage.upgrades) { try { var loadedUpgrades = JSON.parse(storage.upgrades); console.log("Loaded upgrades: ", loadedUpgrades); // Debug output // Merge the loaded config with default config for (var category in loadedUpgrades) { if (UPGRADE_CONFIG[category]) { for (var key in loadedUpgrades[category]) { if (UPGRADE_CONFIG[category][key]) { // For objects like machines if (typeof loadedUpgrades[category][key] === 'object' && loadedUpgrades[category][key] !== null) { for (var prop in loadedUpgrades[category][key]) { UPGRADE_CONFIG[category][key][prop] = loadedUpgrades[category][key][prop]; } } else { // For simple values UPGRADE_CONFIG[category][key] = loadedUpgrades[category][key]; } } } } } } catch (e) { console.log("Error loading upgrades: ", e); } }
User prompt
update as needed with: game.startGame = function () { // Remove title screen if (game.titleContainer) { game.titleContainer.destroy(); game.titleContainer = null; } // Start background music LK.playMusic('backgroundmusic', { fade: { start: 0, end: 0.5, duration: 2000 } }); // Exit title mode game.titleMode = false; // Load saved game data FIRST loadGame(); // Initialize BP display bpText.visible = true; // Initialize pufferfish for animation (not visible yet) playerMask.visible = true; // ... rest of the function ... // Only show tutorial if not completed if (!storage.tutorialComplete) { LK.setTimeout(function () { showTutorialPopup(1); }, 60); } }
===================================================================
--- original.js
+++ change.js
@@ -1378,8 +1378,12 @@
}
// Standard purchase logic
if (game.bp >= cost) {
upgrade.amount++;
+ if (category === 'machines' && key === 'basicClam' && upgrade.amount === 1 && game.tutorial.stage === 6) {
+ game.tutorial.boughtClam = true;
+ showTutorialPopup(7);
+ }
game.bp -= cost;
LK.getSound('upgrade').play();
bpText.setText(formatBP(game.bp) + " BP");
// Update cost display
@@ -1630,8 +1634,11 @@
// Update tab button appearance
tabButtons[currentTab].alpha = 0.7;
tabButtons[newTab].alpha = 1.0;
currentTab = newTab;
+ if (game.tutorial.stage === 5 && newTab === 'clams') {
+ showTutorialPopup(6);
+ }
}
// Function to update treasure decorations
function updateTreasureDecorations() {
// Clear existing treasures
@@ -1706,8 +1713,92 @@
treasureContainer.addChild(zoneIndicator);
treasureContainer.addChild(treasure);
}
}
+function showTutorialPopup(stage) {
+ // Set current stage
+ game.tutorial.stage = stage;
+ // Remove existing tutorial popup if any
+ if (game.tutorialContainer) {
+ game.tutorialContainer.destroy();
+ }
+ // Create new popup container
+ game.tutorialContainer = new Container();
+ game.addChild(game.tutorialContainer);
+ game.setChildIndex(game.tutorialContainer, game.children.length - 1);
+ // Create background
+ var bg = LK.getAsset('blower', {
+ width: game.width * 0.8,
+ height: 300,
+ color: 0x000066,
+ shape: 'box',
+ alpha: 0.9,
+ anchorX: 0.5,
+ anchorY: 0.5,
+ tint: 0x000000 // Use tint to make it black
+ });
+ // Position above the menu tab
+ bg.x = game.width / 2;
+ bg.y = game.height - 400;
+ game.tutorialContainer.addChild(bg);
+ // Create tutorial text
+ var message = "";
+ switch (stage) {
+ case 1:
+ // Welcome
+ message = "Welcome to Bubble Blower Tycoon! You are an ambitious little pufferfish looking to expand your bubble empire. Open your mouth to start growing a bubble and close to release it. Go ahead and try!";
+ break;
+ case 2:
+ // After blowing bubble
+ message = "Great! Now go ahead and pop it to collect Bubble Points (BP).";
+ break;
+ case 3:
+ // After popping
+ message = "That's all there is to it! Now get popping!";
+ break;
+ case 4:
+ // Can afford clam
+ message = "It looks like you've saved enough for your first clam. Go ahead and open up the 'Upgrades' menu at the bottom.";
+ break;
+ case 5:
+ // Menu opened
+ message = "Click on the clams tab.";
+ break;
+ case 6:
+ // Clams tab
+ message = "Click on 'Basic Clam' to buy your first clam. They will create bubbles for you.";
+ break;
+ case 7:
+ // After buying clam
+ message = "Great! There's many other upgrades to explore that will help you on your way. Good luck on becoming the Bubble Blower Tycoon!";
+ break;
+ }
+ var tutorialText = new Text2(message, {
+ size: 60,
+ fill: 0xFFFFFF,
+ stroke: 0x000000,
+ strokeThickness: 3,
+ font: "Impact",
+ wordWrap: true,
+ wordWrapWidth: game.width * 0.75
+ });
+ tutorialText.anchor = {
+ x: 0.5,
+ y: 0.5
+ };
+ tutorialText.x = bg.x;
+ tutorialText.y = bg.y;
+ game.tutorialContainer.addChild(tutorialText);
+ // If final message, add a timer to close
+ if (stage === 3 || stage === 7) {
+ LK.setTimeout(function () {
+ if (game.tutorialContainer) {
+ game.tutorialContainer.destroy();
+ game.tutorialContainer = null;
+ }
+ }, 180);
+ }
+}
// Function to update coral bubbles
// Function to update coral decorations
// Function to update clam visuals
function updateClamVisuals() {
@@ -1844,8 +1935,16 @@
if (game.titleContainer) {
game.titleContainer.destroy();
game.titleContainer = null;
}
+ // Start background music
+ LK.playMusic('backgroundmusic', {
+ fade: {
+ start: 0,
+ end: 0.5,
+ duration: 2000
+ }
+ });
// Exit title mode
game.titleMode = false;
// Initialize BP display
bpText.visible = true;
@@ -1859,10 +1958,10 @@
// Start animation sequence
tween(playerMask, {
x: game.width / 2,
y: game.height / 2,
- scaleX: 0.8,
- scaleY: 0.8,
+ scaleX: 0.7,
+ scaleY: 0.7,
alpha: 1
}, {
duration: 1500,
easing: tween.easeOutBack,
@@ -1871,27 +1970,46 @@
game.faceTrackingEnabled = true;
// Make menu visible
menuContainer.visible = true;
// Update clam visuals
+ LK.setTimeout(function () {
+ showTutorialPopup(1);
+ }, 60);
updateClamVisuals();
updateTreasureDecorations();
- // Start background music
- LK.playMusic('backgroundmusic', {
- fade: {
- start: 0,
- end: 0.5,
- duration: 2000
- }
- });
}
});
};
+game.tutorial = {
+ stage: 0,
+ // 0=none, 1=welcome, 2=blow bubble, 3=pop bubble, 4=first clam, 5=open menu, 6=clams tab, 7=buy clam, 8=final
+ blownBubble: false,
+ poppedBubble: false,
+ boughtClam: false,
+ tutorialBubble: null,
+ minBubbleSize: 60 // Minimum size to consider a proper blown bubble
+};
// Game update function
game.update = function () {
// Update sound cooldowns
if (game.autoPopSoundCooldown > 0) {
game.autoPopSoundCooldown--;
}
+ if (!game.titleMode) {
+ // Tutorial progression logic
+ if (game.tutorial.stage === 1 && game.growingBubble === null && game.tutorial.blownBubble) {
+ // Player released a bubble
+ showTutorialPopup(2);
+ }
+ // Track if player has blown a proper bubble
+ if (game.tutorial.stage === 1 && game.growingBubble && game.growingBubble.size >= game.tutorial.minBubbleSize) {
+ game.tutorial.blownBubble = true;
+ }
+ // Check if we can afford first clam and haven't bought one yet
+ if (game.tutorial.stage === 3 && game.bp >= UPGRADE_CONFIG.machines.basicClam.baseCost && UPGRADE_CONFIG.machines.basicClam.amount === 0) {
+ showTutorialPopup(4);
+ }
+ }
if (game.titleMode) {
// Just handle bubble spawning and updates during title
// Random bubble spawning
if (game.activeBubbles.length < game.MAX_BUBBLES) {
@@ -2023,8 +2141,13 @@
});
};
// Handle touch/mouse events for the game
game.down = function (x, y, obj) {
+ if (game.tutorial.stage === 2 && popped) {
+ game.tutorial.poppedBubble = true;
+ showTutorialPopup(3);
+ return true;
+ }
if (game.titleMode) {
return false; // Let containers handle their own clicks
}
var localX = x - menuContainer.x;
@@ -2038,8 +2161,11 @@
};
if (localX >= tabBounds.x && localX <= tabBounds.x + tabBounds.width && localY >= tabBounds.y && localY <= tabBounds.y + tabBounds.height) {
LK.getSound('menuopen').play();
menuOpen = !menuOpen;
+ if (game.tutorial.stage === 4 && menuOpen) {
+ showTutorialPopup(5);
+ }
var targetY = menuOpen ? menuTab.height : game.height;
if (menuOpen) {
game.setChildIndex(menuContainer, game.children.length - 1);
}
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