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;
|
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 */
|
/* Side Panel Tabs */
|
||||||
.tabs {
|
.tabs {
|
||||||
display: flex;
|
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.
|
- Consider adding a "Buy Max" button to the prestige upgrades list as well.
|
||||||
- Potential new building tiers or prestige milestones.
|
- Potential new building tiers or prestige milestones.
|
||||||
- Sound variations or background music.
|
- 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>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<h3 style="margin-top:15px">Prestige Upgrades</h3>
|
<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 id="prestige-upgrades-list"></div>
|
||||||
</div>
|
</div>
|
||||||
</aside>
|
</aside>
|
||||||
|
|||||||
+47
@@ -469,6 +469,53 @@ const Game = {
|
|||||||
return { bought: bought.length, ids: bought };
|
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.
|
* Golden Starbloom - random event system.
|
||||||
* A rare, time-limited bloom occasionally appears on screen. Clicking it
|
* A rare, time-limited bloom occasionally appears on screen. Clicking it
|
||||||
|
|||||||
@@ -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')) {
|
if (prestigeTab?.classList.contains('active')) {
|
||||||
this.renderPrestigeUpgrades();
|
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;
|
window.PrestigeUI = PrestigeUI;
|
||||||
|
|||||||
Reference in New Issue
Block a user