Session 14: Buy All Upgrades (bulk-buy for upgrades) + empty-slot boost leak fix

- Added Game.buyAllUpgrades(): buys every affordable upgrade at once,
  respecting per-upgrade requirements and looping until nothing remains.
- Added a 'Buy All' button to the Upgrades tab header; UI.updateBuyAllButton()
  shows/hides & enables/disables it live each tick based on affordability.
- Fixed empty-slot branch of switchSlot(): added productionBoostUntil: 0 so a
  temporary golden-bloom boost cannot leak into a fresh slot.
- Added .upgrades-header / .buy-all-btn CSS (gold accent theme).

Verified: headless test buy-all-upgrades-test.mjs 24/24 pass; existing
golden-bloom (22/22) and buy-max (6/6) tests re-run clean, no regressions.
This commit is contained in:
2026-08-24 01:30:10 +00:00
parent b532f5a15c
commit 34074b8e65
5 changed files with 160 additions and 3 deletions
+39 -2
View File
@@ -189,15 +189,52 @@ const UI = {
for (const [id, upgrade] of Object.entries(UPGRADES)) {
const card = document.getElementById(`upgrade-${id}`);
if (!card) continue;
const purchased = Game.state.upgrades.includes(id);
const costEl = card.querySelector('.upgrade-cost');
if (costEl && !purchased) {
const canAfford = Game.state.stardust >= upgrade.cost;
costEl.textContent = canAfford ? `${Game.formatNumber(upgrade.cost)} ✨` : `❌ ${Game.formatNumber(upgrade.cost)} ✨`;
}
}
// Toggle the "Buy All" button: show it once at least one unowned,
// requirement-met upgrade exists, and disable it when nothing is
// affordable. Its enabled state is refreshed live each tick.
this.updateBuyAllButton();
},
/**
* Show/hide and enable/disable the "Buy All upgrades" button based on
* whether any unowned upgrade (whose requirements are met) is currently
* affordable. Read of game state only; never mutates it.
*/
updateBuyAllButton() {
const btn = document.getElementById('buy-all-upgrades');
if (!btn) return;
let affordableAny = false;
for (const [id, upgrade] of Object.entries(UPGRADES)) {
if (Game.state.upgrades.includes(id)) continue;
if (Game.state.stardust < upgrade.cost) continue;
// Requirement check mirrors Game.buyUpgrade / buyAllUpgrades.
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;
},
/**