Session 15: Buy Max Prestige Upgrades (bulk-buy for prestige upgrades) + fix missing Stellar Essence spend
This commit is contained in:
@@ -363,6 +363,23 @@ h2, h3 {
|
||||
opacity: 0.4;
|
||||
}
|
||||
|
||||
/* Prestige Upgrades header + Buy Max button */
|
||||
.prestige-upgrades-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.prestige-upgrades-header h3 {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.prestige-buy-all {
|
||||
background: var(--accent-gold);
|
||||
color: var(--bg-primary, #1a1035);
|
||||
}
|
||||
|
||||
/* Side Panel Tabs */
|
||||
.tabs {
|
||||
display: flex;
|
||||
|
||||
@@ -414,3 +414,31 @@ Wrote a headless Node.js test (`/opt/data/idle-game-test/buy-all-upgrades-test.m
|
||||
- Consider adding a "Buy Max" button to the prestige upgrades list as well.
|
||||
- Potential new building tiers or prestige milestones.
|
||||
- Sound variations or background music.
|
||||
|
||||
---
|
||||
|
||||
### Session 15 - 2026-08-24 (Morning)
|
||||
**Status:** ✅ Buy Max Prestige Upgrades (bulk-buy for prestige upgrades) Added
|
||||
|
||||
#### What was done:
|
||||
- Followed up on Session 14's own "next goal" by adding a **Buy Max** button to the **Prestige Upgrades** list. Previously one-time prestige upgrades could only be purchased individually (one click each), which was tedious once several were available — buildings had Buy Max (Session 10) and regular upgrades had Buy All (Session 14), but prestige upgrades did not. This brings prestige upgrades in line with that established bulk-buy convention.
|
||||
- Added `Game.buyMaxPrestigeUpgrades()` to `js/game.js`. It loops through every entry in `PRESTIGE_UPGRADES`, skips owned ones, respects each upgrade's (optional) `requirements` building-count gate, and buys everything affordable at once from **Stellar Essence**. It runs until a full pass purchases nothing (loop kept for symmetry with `buyAllUpgrades` and to stay safe if future prestige upgrades gain requirement gates), then decrements `stellarEssence` per purchase (mirroring `PRESTIGE.buyUpgrade`) and returns `{ bought: count, ids: [...] }`.
|
||||
- Wired the button into the Prestige tab in `index.html` (`#buy-all-prestige`, placed alongside a left-aligned header row mirroring the Upgrades tab's Buy All layout). Added a click listener in `PrestigeUI.setupEventListeners()`.
|
||||
- Added `PrestigeUI.updateBuyMaxPrestigeButton()` (called from `PrestigeUI.update()` each tick) which shows/hides and enables/disables the button based on whether any unowned, requirement-met prestige upgrade is currently affordable with Stellar Essence — a read-only state check, exactly mirroring `UI.updateBuyAllButton()`.
|
||||
- Added CSS for `.prestige-upgrades-header` (flex layout) and `.prestige-buy-all` (gold accent theme reusing the existing `.buy-all-btn` base, matching the Upgrades tab's Buy All button).
|
||||
- **Bug found & fixed during testing:** the first implementation of `buyMaxPrestigeUpgrades()` added upgrades to the list but never decremented `stellarEssence`, which would have let players prestige-buy unlimited upgrades for free. The headless test asserted essence depletion and caught it; added the missing `this.state.stellarEssence -= upgrade.cost` line so spend mirrors `PRESTIGE.buyUpgrade`.
|
||||
|
||||
#### Files modified:
|
||||
- `js/game.js` - added `buyMaxPrestigeUpgrades()` method (with correct Stellar Essence spend)
|
||||
- `js/prestigeUI.js` - added click listener for the Buy Max button + `updateBuyMaxPrestigeButton()`; called from `update()` each tick
|
||||
- `index.html` - added Buy Max button to the Prestige Upgrades header
|
||||
- `css/game.css` - `.prestige-upgrades-header`, `.prestige-buy-all` styles
|
||||
- `dev-journal.md` - appended Session 15 entry
|
||||
|
||||
#### Verification:
|
||||
Wrote a headless Node.js test (`/opt/data/idle-game-test/prestige-buy-max-test.mjs`) that loads the real `game.js`, `prestige.js`, and `prestigeUI.js` in-process with DOM/audio/particle stubs. **22/22 checks pass**, covering: no-op when nothing affordable, bulk purchase deducting Stellar Essence correctly, ungated-upgrade behavior (real prestige upgrades have no requirement gates), idempotent second call, already-owned upgrade skipping, notification-on-purchase vs silence on no-op, the missing-essence-spend regression (now fixed), and the real `PrestigeUI.updateBuyMaxPrestigeButton()` show/hide/enabled state against actual `PRESTIGE_UPGRADES` data. All existing suites re-ran clean with no regressions: buy-all-upgrades 24/24, buy-max 6/6, golden-bloom 22/22, slot-preview 22/22. All modified JS files pass `node --check`.
|
||||
|
||||
#### Next goals:
|
||||
- Potential new building tiers or prestige milestones / new prestige upgrades.
|
||||
- Sound variations or background music.
|
||||
- Consider a global "Buy Max" for buildings across all types at once (currently per-building).
|
||||
|
||||
@@ -99,6 +99,10 @@
|
||||
</button>
|
||||
</div>
|
||||
<h3 style="margin-top:15px">Prestige Upgrades</h3>
|
||||
<div class="prestige-upgrades-header">
|
||||
<span></span>
|
||||
<button id="buy-all-prestige" class="buy-all-btn prestige-buy-all" style="display:none" title="Buy every prestige upgrade you can currently afford with Stellar Essence">Buy Max</button>
|
||||
</div>
|
||||
<div id="prestige-upgrades-list"></div>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
+47
@@ -469,6 +469,53 @@ const Game = {
|
||||
return { bought: bought.length, ids: bought };
|
||||
},
|
||||
|
||||
/**
|
||||
* Buy every prestige upgrade the player can currently afford at once,
|
||||
* paid from Stellar Essence. Returns an object describing what happened:
|
||||
* { bought: count, ids: [...] }. Prestige upgrades are one-time purchases,
|
||||
* so we loop until a full pass buys nothing (purchasing one never unlocks
|
||||
* another, but the loop is kept for symmetry with buyAllUpgrades and to
|
||||
* stay safe should future prestige upgrades gain requirement gates).
|
||||
*/
|
||||
buyMaxPrestigeUpgrades() {
|
||||
const bought = [];
|
||||
let progress = true;
|
||||
while (progress) {
|
||||
progress = false;
|
||||
for (const [id, upgrade] of Object.entries(PRESTIGE_UPGRADES)) {
|
||||
if ((this.state.prestigeUpgrades || []).includes(id)) continue;
|
||||
// Respect requirements before attempting purchase, mirroring
|
||||
// PRESTIGE.buyUpgrade semantics.
|
||||
if (upgrade.requirements) {
|
||||
let met = true;
|
||||
for (const [reqId, reqCount] of Object.entries(upgrade.requirements)) {
|
||||
if ((this.state.buildings[reqId] || 0) < reqCount) {
|
||||
met = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!met) continue;
|
||||
}
|
||||
if ((this.state.stellarEssence || 0) >= upgrade.cost) {
|
||||
this.state.prestigeUpgrades = this.state.prestigeUpgrades || [];
|
||||
this.state.prestigeUpgrades.push(id);
|
||||
// Spend Stellar Essence, mirroring PRESTIGE.buyUpgrade.
|
||||
this.state.stellarEssence -= upgrade.cost;
|
||||
bought.push(id);
|
||||
progress = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (bought.length > 0) {
|
||||
UI.showNotification(`Bought ${bought.length} prestige upgrade(s)!`, 'achievement');
|
||||
Audio.playPurchase();
|
||||
UI.update();
|
||||
}
|
||||
|
||||
return { bought: bought.length, ids: bought };
|
||||
},
|
||||
|
||||
/**
|
||||
* Golden Starbloom - random event system.
|
||||
* A rare, time-limited bloom occasionally appears on screen. Clicking it
|
||||
|
||||
+47
-1
@@ -43,6 +43,15 @@ const PrestigeUI = {
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Buy Max prestige upgrades button - buy every affordable prestige
|
||||
// upgrade at once, paid from Stellar Essence.
|
||||
const buyMaxBtn = document.getElementById('buy-all-prestige');
|
||||
if (buyMaxBtn) {
|
||||
buyMaxBtn.addEventListener('click', () => {
|
||||
Game.buyMaxPrestigeUpgrades();
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -119,7 +128,44 @@ const PrestigeUI = {
|
||||
if (prestigeTab?.classList.contains('active')) {
|
||||
this.renderPrestigeUpgrades();
|
||||
}
|
||||
}
|
||||
|
||||
// Show/hide the "Buy Max" button based on whether any unowned prestige
|
||||
// upgrade is currently affordable with Stellar Essence. Read-only.
|
||||
this.updateBuyMaxPrestigeButton();
|
||||
},
|
||||
|
||||
/**
|
||||
* Show/hide and enable/disable the "Buy Max" prestige-upgrades button
|
||||
* depending on whether at least one unowned, requirement-met prestige
|
||||
* upgrade is currently affordable with Stellar Essence. Read of game state
|
||||
* only; never mutates it. Mirrors Game.buyMaxPrestigeUpgrades /
|
||||
* PRESTIGE.buyUpgrade semantics.
|
||||
*/
|
||||
updateBuyMaxPrestigeButton() {
|
||||
const btn = document.getElementById('buy-all-prestige');
|
||||
if (!btn) return;
|
||||
|
||||
let affordableAny = false;
|
||||
for (const [id, upgrade] of Object.entries(PRESTIGE_UPGRADES)) {
|
||||
if ((Game.state.prestigeUpgrades || []).includes(id)) continue;
|
||||
if ((Game.state.stellarEssence || 0) < upgrade.cost) continue;
|
||||
if (upgrade.requirements) {
|
||||
let met = true;
|
||||
for (const [reqId, reqCount] of Object.entries(upgrade.requirements)) {
|
||||
if ((Game.state.buildings[reqId] || 0) < reqCount) {
|
||||
met = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!met) continue;
|
||||
}
|
||||
affordableAny = true;
|
||||
break;
|
||||
}
|
||||
|
||||
btn.style.display = affordableAny ? '' : 'none';
|
||||
btn.disabled = !affordableAny;
|
||||
},
|
||||
};
|
||||
|
||||
window.PrestigeUI = PrestigeUI;
|
||||
|
||||
Reference in New Issue
Block a user