Compare commits
1 Commits
fix/offlin
...
feature/bu
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3aa1c36c88 |
89
game.js
89
game.js
@@ -85,6 +85,7 @@ const G = {
|
||||
tick: 0,
|
||||
saveTimer: 0,
|
||||
secTimer: 0,
|
||||
buyMode: 1, // 1, 10, or -1 (max)
|
||||
|
||||
// Systems
|
||||
projects: [],
|
||||
@@ -808,6 +809,42 @@ function getBuildingCost(id) {
|
||||
return cost;
|
||||
}
|
||||
|
||||
function getBuildingBatchCost(id, count) {
|
||||
const def = BDEF.find(b => b.id === id);
|
||||
if (!def || count <= 0) return {};
|
||||
const currentCount = G.buildings[id] || 0;
|
||||
const cost = {};
|
||||
for (let i = 0; i < count; i++) {
|
||||
for (const [resource, amount] of Object.entries(def.baseCost)) {
|
||||
cost[resource] = (cost[resource] || 0) + Math.floor(amount * Math.pow(def.costMult, currentCount + i));
|
||||
}
|
||||
}
|
||||
return cost;
|
||||
}
|
||||
|
||||
function getMaxBuyable(id) {
|
||||
const def = BDEF.find(b => b.id === id);
|
||||
if (!def) return 0;
|
||||
// Simulate buying one at a time until we can't afford
|
||||
let count = 0;
|
||||
const tempResources = {};
|
||||
for (const r of Object.keys(def.baseCost)) tempResources[r] = G[r] || 0;
|
||||
const currentCount = G.buildings[id] || 0;
|
||||
for (let i = 0; i < 1000; i++) { // cap at 1000
|
||||
let canAfford = true;
|
||||
for (const [resource, amount] of Object.entries(def.baseCost)) {
|
||||
const cost = Math.floor(amount * Math.pow(def.costMult, currentCount + i));
|
||||
if (tempResources[resource] < cost) { canAfford = false; break; }
|
||||
}
|
||||
if (!canAfford) break;
|
||||
for (const [resource, amount] of Object.entries(def.baseCost)) {
|
||||
tempResources[resource] -= Math.floor(amount * Math.pow(def.costMult, currentCount + i));
|
||||
}
|
||||
count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
function canAffordBuilding(id) {
|
||||
const cost = getBuildingCost(id);
|
||||
for (const [resource, amount] of Object.entries(cost)) {
|
||||
@@ -816,6 +853,16 @@ function canAffordBuilding(id) {
|
||||
return true;
|
||||
}
|
||||
|
||||
function canAffordBatch(id) {
|
||||
const count = G.buyMode === -1 ? getMaxBuyable(id) : G.buyMode;
|
||||
if (count <= 0) return false;
|
||||
const cost = getBuildingBatchCost(id, count);
|
||||
for (const [resource, amount] of Object.entries(cost)) {
|
||||
if ((G[resource] || 0) < amount) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function spendBuilding(id) {
|
||||
const cost = getBuildingCost(id);
|
||||
for (const [resource, amount] of Object.entries(cost)) {
|
||||
@@ -1045,15 +1092,26 @@ function checkProjects() {
|
||||
function buyBuilding(id) {
|
||||
const def = BDEF.find(b => b.id === id);
|
||||
if (!def || !def.unlock()) return;
|
||||
|
||||
if (def.phase > G.phase + 1) return;
|
||||
|
||||
if (!canAffordBuilding(id)) return;
|
||||
const buyCount = G.buyMode === -1 ? getMaxBuyable(id) : G.buyMode;
|
||||
if (buyCount <= 0) return;
|
||||
|
||||
spendBuilding(id);
|
||||
G.buildings[id] = (G.buildings[id] || 0) + 1;
|
||||
if (buyCount === 1) {
|
||||
if (!canAffordBuilding(id)) return;
|
||||
spendBuilding(id);
|
||||
G.buildings[id] = (G.buildings[id] || 0) + 1;
|
||||
log(`Built ${def.name} (total: ${G.buildings[id]})`);
|
||||
} else {
|
||||
if (!canAffordBatch(id)) return;
|
||||
const cost = getBuildingBatchCost(id, buyCount);
|
||||
for (const [resource, amount] of Object.entries(cost)) {
|
||||
G[resource] -= amount;
|
||||
}
|
||||
G.buildings[id] = (G.buildings[id] || 0) + buyCount;
|
||||
log(`Built ${buyCount}x ${def.name} (total: ${G.buildings[id]})`);
|
||||
}
|
||||
updateRates();
|
||||
log(`Built ${def.name} (total: ${G.buildings[id]})`);
|
||||
render();
|
||||
}
|
||||
|
||||
@@ -1273,6 +1331,15 @@ function resolveAlignment(accept) {
|
||||
}
|
||||
|
||||
// === ACTIONS ===
|
||||
function setBuyMode(mode) {
|
||||
G.buyMode = mode;
|
||||
// Update active button highlight
|
||||
document.querySelectorAll('.buy-mode-btn').forEach(btn => {
|
||||
btn.classList.toggle('active', parseInt(btn.dataset.mode) === mode);
|
||||
});
|
||||
renderBuildings();
|
||||
}
|
||||
|
||||
function writeCode() {
|
||||
const base = 1;
|
||||
const bonus = Math.floor(G.buildings.autocoder * 0.5);
|
||||
@@ -1643,7 +1710,7 @@ function saveGame() {
|
||||
branchProtectionFlag: G.branchProtectionFlag || 0, nightlyWatchFlag: G.nightlyWatchFlag || 0,
|
||||
nostrFlag: G.nostrFlag || 0,
|
||||
milestones: G.milestones, completedProjects: G.completedProjects, activeProjects: G.activeProjects,
|
||||
totalClicks: G.totalClicks, startedAt: G.startedAt,
|
||||
totalClicks: G.totalClicks, startedAt: G.startedAt, buyMode: G.buyMode,
|
||||
flags: G.flags,
|
||||
_seenBuildings: G._seenBuildings || [],
|
||||
_seenProjects: G._seenProjects || [],
|
||||
@@ -1719,6 +1786,7 @@ function initGame() {
|
||||
|
||||
log('The screen is blank. Write your first line of code.', true);
|
||||
log('Click WRITE CODE or press SPACE to start.');
|
||||
log('Press B to toggle buy mode (x1 / x10 / MAX).');
|
||||
log('Build AutoCode for passive production.');
|
||||
log('Watch for Research Projects to appear.');
|
||||
}
|
||||
@@ -1740,6 +1808,9 @@ window.addEventListener('load', function () {
|
||||
}
|
||||
}
|
||||
|
||||
// Restore buy mode button highlight
|
||||
setBuyMode(G.buyMode || 1);
|
||||
|
||||
// Game loop at 10Hz (100ms tick)
|
||||
setInterval(tick, 100);
|
||||
|
||||
@@ -1756,4 +1827,10 @@ window.addEventListener('keydown', function (e) {
|
||||
e.preventDefault();
|
||||
writeCode();
|
||||
}
|
||||
if (e.code === 'KeyB' && e.target === document.body) {
|
||||
e.preventDefault();
|
||||
// Cycle: x1 -> x10 -> MAX -> x1
|
||||
const next = G.buyMode === 1 ? 10 : G.buyMode === 10 ? -1 : 1;
|
||||
setBuyMode(next);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -66,6 +66,8 @@ body{background:var(--bg);color:var(--text);font-family:'SF Mono','Cascadia Code
|
||||
.unlock-toast-item.building{border-color:#4a9eff;color:#4a9eff}
|
||||
.unlock-toast-item.project{border-color:#ffd700;color:#ffd700}
|
||||
.unlock-toast-item.milestone{border-color:#4caf50;color:#4caf50}
|
||||
.buy-mode-btn{min-width:36px}
|
||||
.buy-mode-btn.active{border-color:#ffd700!important;color:#ffd700!important;background:#1a1a08!important}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
@@ -103,6 +105,11 @@ body{background:var(--bg);color:var(--text);font-family:'SF Mono','Cascadia Code
|
||||
</div>
|
||||
<div id="alignment-ui" style="display:none"></div>
|
||||
<div id="events-ui" style="display:none"></div>
|
||||
<div id="buy-mode-toggle" style="display:flex;gap:4px;margin-bottom:8px">
|
||||
<button class="ops-btn buy-mode-btn active" onclick="setBuyMode(1)" data-mode="1">x1</button>
|
||||
<button class="ops-btn buy-mode-btn" onclick="setBuyMode(10)" data-mode="10">x10</button>
|
||||
<button class="ops-btn buy-mode-btn" onclick="setBuyMode(-1)" data-mode="-1">MAX</button>
|
||||
</div>
|
||||
<button class="save-btn" onclick="saveGame()">Save Game</button>
|
||||
<button class="reset-btn" onclick="if(confirm('Reset all progress?')){localStorage.removeItem('the-beacon-v2');location.reload()}">Reset Progress</button>
|
||||
<h2>BUILDINGS</h2>
|
||||
|
||||
Reference in New Issue
Block a user