Skip to main content

Step 5: Add Multiple Upgrades

Let's expand the upgrade system! Instead of just auto-clickers, we'll add multiple types of upgrades that enhance your clicking power or generate cookies in different ways.

Understanding the Upgrade System

An upgrade system needs to track:

  • What upgrades exist - Their names, costs, effects
  • How many of each - Player's current quantity of each upgrade
  • Visual representation - Buttons to display and purchase upgrades
  • Active effects - How each upgrade changes gameplay

Setting Up the Upgrade Object

Instead of separate variables for each upgrade, let's use an object to organize upgrades efficiently:

let upgrades = {
autoClicker: {
name: 'Auto-Clicker',
cost: 10,
owned: 0,
baseCost: 10,
effect: 1 // Cookies per second
},
clickBoost: {
name: 'Click Boost',
cost: 50,
owned: 0,
baseCost: 50,
effect: 1 // Extra cookies per click
},
speedBoost: {
name: 'Speed Boost',
cost: 100,
owned: 0,
baseCost: 100,
effect: 0.5 // Multiplier for auto-clicker speed
}
};

let cookiesPerClick = 1; // Affected by Click Boost
let clickMultiplier = 1; // Affected by Speed Boost

What this does:

  • name - Display name for the upgrade
  • cost - Current price to buy one
  • owned - How many player has purchased
  • baseCost - Original price (for calculating new prices)
  • effect - How much value this upgrade provides

Creating a Button Layout for Upgrades

Add button tracking for the upgrade buttons:

let upgradeButtons = {
autoClicker: { x: 50, y: 450, width: 150, height: 60 },
clickBoost: { x: 250, y: 450, width: 150, height: 60 },
speedBoost: { x: 450, y: 450, width: 150, height: 60 }
};

function setup() {
createCanvas(800, 600);
}

Drawing Upgrade Buttons

Add this code to your draw() function to display all upgrade buttons:

function draw() {
background(240, 230, 220);

// ... auto-generate and draw cookie code ...

// Draw all upgrade buttons
drawUpgradeButtons();

// ... score display code ...
}

function drawUpgradeButtons() {
// Loop through each upgrade and draw its button
for (let upgradeKey in upgrades) {
let upgrade = upgrades[upgradeKey];
let button = upgradeButtons[upgradeKey];

// Check if player can afford this upgrade
let canAfford = score >= upgrade.cost;

// Draw button background
if (canAfford) {
fill(100, 200, 100); // Green if affordable
} else {
fill(150); // Gray if too expensive
}

// Draw button
rect(button.x, button.y, button.width, button.height, 8);

// Draw text on button
fill(255);
textSize(14);
textAlign(CENTER, CENTER);
text(upgrade.name, button.x + button.width/2, button.y + 15);
text('Cost: ' + upgrade.cost, button.x + button.width/2, button.y + 35);
text('Owned: ' + upgrade.owned, button.x + button.width/2, button.y + 50);
}
}

Breaking it down:

  • for (let upgradeKey in upgrades) loops through each upgrade
  • We check if the player can afford it with score >= upgrade.cost
  • Different colors provide visual feedback (green = affordable, gray = too expensive)
  • We display the upgrade name, cost, and quantity owned

Handling Upgrade Purchases

In your mousePressed() function, add code to handle upgrade button clicks:

function mousePressed() {
// Check if cookie is clicked
let d = dist(mouseX, mouseY, width/2, height/2 - 50);

if (d < 75) {
// Click on cookie - apply click boost
score += cookiesPerClick;
cookieSize = 135;
// ... floating text and particle effects ...
}

// Check upgrade button clicks
for (let upgradeKey in upgrades) {
let button = upgradeButtons[upgradeKey];

if (mouseX > button.x && mouseX < button.x + button.width &&
mouseY > button.y && mouseY < button.y + button.height) {
purchaseUpgrade(upgradeKey);
}
}
}

function purchaseUpgrade(upgradeKey) {
let upgrade = upgrades[upgradeKey];

// Check if player can afford it
if (score >= upgrade.cost) {
score -= upgrade.cost;
upgrade.owned++;

// Apply upgrade effects based on type
if (upgradeKey === 'autoClicker') {
// Auto-clickers add to passive income
} else if (upgradeKey === 'clickBoost') {
cookiesPerClick += upgrade.effect;
} else if (upgradeKey === 'speedBoost') {
clickMultiplier += upgrade.effect;
}

// Calculate new cost (increases with exponential growth)
upgrade.cost = Math.floor(upgrade.baseCost * Math.pow(1.15, upgrade.owned));
}
}

Breaking it down:

  • Math.pow(1.15, upgrade.owned) calculates exponential cost growth
  • Each upgrade owned multiplies the cost by 1.15 (15% increase)
  • This prevents the game from becoming too easy as the player progresses
  • Different upgrade types have different effects

Applying Upgrade Effects in the Main Loop

Modify your auto-generation code to account for upgrades:

function draw() {
background(240, 230, 220);

// Auto-generate cookies from auto-clickers
if (frameCount % 60 === 0 && upgrades.autoClicker.owned > 0) {
let autoClickerIncome = upgrades.autoClicker.owned * upgrades.autoClicker.effect;
// Apply speed boost multiplier
autoClickerIncome *= (1 + upgrades.speedBoost.owned * 0.5);
score += autoClickerIncome;
}

// ... rest of your code ...
}

Displaying Upgrade Statistics

Add this code to show the player their current upgrade benefits:

function draw() {
background(240, 230, 220);

// ... auto-generate and draw cookie code ...

// Display upgrade stats
fill(0);
textSize(16);
textAlign(LEFT);

let statsX = 20;
let statsY = 150;

text('Cookies per click: ' + cookiesPerClick, statsX, statsY);
text('Speed multiplier: ' + (1 + upgrades.speedBoost.owned * 0.5).toFixed(2) + 'x',
statsX, statsY + 25);

let totalPassiveIncome = upgrades.autoClicker.owned * upgrades.autoClicker.effect *
(1 + upgrades.speedBoost.owned * 0.5);
text('Passive income: +' + Math.floor(totalPassiveIncome) + '/sec',
statsX, statsY + 50);

// ... rest of your code ...
}

Complete Example: All Upgrades Together

Here's a complete working example with three upgrades:

// Global variables
let score = 0;
let cookieSize = 150;
let cookieImage;
let floatingTexts = [];
let particles = [];
let cookiesPerClick = 1;

let upgrades = {
autoClicker: {
name: 'Auto-Clicker',
cost: 10,
owned: 0,
baseCost: 10,
effect: 1
},
clickBoost: {
name: 'Click Boost',
cost: 50,
owned: 0,
baseCost: 50,
effect: 1
},
speedBoost: {
name: 'Speed Boost',
cost: 100,
owned: 0,
baseCost: 100,
effect: 0.5
}
};

let upgradeButtons = {
autoClicker: { x: 50, y: 450, width: 150, height: 60 },
clickBoost: { x: 250, y: 450, width: 150, height: 60 },
speedBoost: { x: 450, y: 450, width: 150, height: 60 }
};

function preload() {
cookieImage = loadImage('https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcT-0fda82kJu_k4skgYTqbZFam6J6zaK6bw2A&s');
}

function setup() {
createCanvas(800, 600);
}

function draw() {
background(240, 230, 220);

// Auto-generate cookies
if (frameCount % 60 === 0 && upgrades.autoClicker.owned > 0) {
let autoClickerIncome = upgrades.autoClicker.owned * upgrades.autoClicker.effect;
autoClickerIncome *= (1 + upgrades.speedBoost.owned * 0.5);
score += autoClickerIncome;
}

// Cookie animation
if (cookieSize < 150) {
cookieSize += 2;
}

// Draw the cookie
push();
imageMode(CENTER);
image(cookieImage, width/2, height/2 - 50, cookieSize, cookieSize);
pop();

// Update and draw floating texts
for (let i = floatingTexts.length - 1; i >= 0; i--) {
let txt = floatingTexts[i];
txt.yOffset += 2;
txt.alpha -= 5;

fill(50, 200, 50, txt.alpha);
textSize(24);
textAlign(CENTER);
text('+' + txt.amount, txt.x, txt.y - txt.yOffset);

if (txt.alpha <= 0) {
floatingTexts.splice(i, 1);
}
}

// Update and draw particles
for (let i = particles.length - 1; i >= 0; i--) {
let p = particles[i];
p.x += p.vx;
p.y += p.vy;
p.alpha -= 8;

fill(218, 165, 32, p.alpha);
noStroke();
circle(p.x, p.y, p.size);

if (p.alpha <= 0) {
particles.splice(i, 1);
}
}

// Draw upgrade buttons
drawUpgradeButtons();

// Display score
fill(0);
textSize(36);
textAlign(CENTER);
text(score + ' cookies', width/2, 50);

// Display upgrade stats
textSize(16);
textAlign(LEFT);
text('Per click: ' + cookiesPerClick, 20, 150);
text('Speed: ' + (1 + upgrades.speedBoost.owned * 0.5).toFixed(2) + 'x', 20, 180);
}

function drawUpgradeButtons() {
for (let upgradeKey in upgrades) {
let upgrade = upgrades[upgradeKey];
let button = upgradeButtons[upgradeKey];

let canAfford = score >= upgrade.cost;

if (canAfford) {
fill(100, 200, 100);
} else {
fill(150);
}

rect(button.x, button.y, button.width, button.height, 8);

fill(255);
textSize(14);
textAlign(CENTER, CENTER);
text(upgrade.name, button.x + button.width/2, button.y + 15);
text('Cost: ' + upgrade.cost, button.x + button.width/2, button.y + 35);
text('Owned: ' + upgrade.owned, button.x + button.width/2, button.y + 50);
}
}

function mousePressed() {
// Check if cookie is clicked
let d = dist(mouseX, mouseY, width/2, height/2 - 50);

if (d < 75) {
score += cookiesPerClick;
cookieSize = 135;

floatingTexts.push({
x: mouseX,
y: mouseY,
amount: cookiesPerClick,
alpha: 255,
yOffset: 0
});

for (let i = 0; i < 8; i++) {
let angle = (i / 8) * TWO_PI;
particles.push({
x: mouseX,
y: mouseY,
vx: cos(angle) * 3,
vy: sin(angle) * 3,
alpha: 255,
size: random(4, 8)
});
}
}

// Check upgrade button clicks
for (let upgradeKey in upgrades) {
let button = upgradeButtons[upgradeKey];

if (mouseX > button.x && mouseX < button.x + button.width &&
mouseY > button.y && mouseY < button.y + button.height) {
purchaseUpgrade(upgradeKey);
}
}
}

function purchaseUpgrade(upgradeKey) {
let upgrade = upgrades[upgradeKey];

if (score >= upgrade.cost) {
score -= upgrade.cost;
upgrade.owned++;

if (upgradeKey === 'clickBoost') {
cookiesPerClick += upgrade.effect;
}

upgrade.cost = Math.floor(upgrade.baseCost * Math.pow(1.15, upgrade.owned));
}
}

Tips for Extending This System

Add More Upgrades

Add new upgrades by extending the upgrades object:

let upgrades = {
// ... existing upgrades ...
goldenCookie: {
name: 'Golden Cookie',
cost: 500,
owned: 0,
baseCost: 500,
effect: 0.1 // 10% score boost chance
}
};

Create a Shop Layout

Use a scrollable menu or grid layout for many upgrades:

let upgradeButtons = {
// Row 1
autoClicker: { x: 50, y: 450, width: 150, height: 60 },
// Row 2
clickBoost: { x: 50, y: 520, width: 150, height: 60 },
// And so on...
};

Synergies Between Upgrades

Make upgrades interact:

function purchaseUpgrade(upgradeKey) {
let upgrade = upgrades[upgradeKey];

if (score >= upgrade.cost) {
score -= upgrade.cost;
upgrade.owned++;

// Synergy: speedBoost makes autoClickers more efficient
if (upgradeKey === 'speedBoost' && upgrades.autoClicker.owned > 0) {
score += 10; // Bonus cookies when synergy is triggered
}

upgrade.cost = Math.floor(upgrade.baseCost * Math.pow(1.15, upgrade.owned));
}
}

Next Steps

Once you have the basic upgrade system working:

  • Experiment with costs - Adjust baseCost and growth rates
  • Add visual effects - Highlight recently purchased upgrades
  • Create descriptions - Add tooltips explaining what each upgrade does
  • Balance gameplay - Adjust effects so progression feels rewarding