Compare commits

..

1 Commits

Author SHA1 Message Date
Timmy-Sprint
3d414b2de6 beacon: fix offline progress to award all resources (rescues, ops, trust, creativity, harmony)
Offline progress previously only calculated code, compute, knowledge, users,
and impact. Players returning after time away missed rescues, ops, trust,
creativity, and harmony accumulation. The welcome-back message now also
only shows resources that actually had positive rates, reducing noise.
2026-04-10 02:46:42 -04:00
2 changed files with 18 additions and 70 deletions

78
game.js
View File

@@ -1433,7 +1433,6 @@ function render() {
renderStats();
updateEducation();
renderAlignment();
checkUnlocks();
}
function renderAlignment() {
@@ -1457,63 +1456,6 @@ function renderAlignment() {
}
}
// === UNLOCK NOTIFICATIONS ===
function showUnlockToast(type, name) {
const container = document.getElementById('unlock-toast');
if (!container) return;
const el = document.createElement('div');
el.className = `unlock-toast-item ${type}`;
const icon = type === 'building' ? 'BUILDING' : type === 'project' ? 'RESEARCH' : 'MILESTONE';
el.innerHTML = `<span style="font-weight:600">${icon}:</span> ${name}`;
container.appendChild(el);
// Trigger reflow, then show
void el.offsetHeight;
el.classList.add('show');
// Auto-remove after 4 seconds
setTimeout(() => {
el.classList.remove('show');
setTimeout(() => { if (el.parentNode) el.parentNode.removeChild(el); }, 400);
}, 4000);
}
// Track what the player has already seen (so we don't re-notify)
function ensureSeenSets() {
if (!G._seenBuildings) G._seenBuildings = [];
if (!G._seenProjects) G._seenProjects = [];
}
function checkUnlocks() {
ensureSeenSets();
// Check for newly visible buildings
for (const def of BDEF) {
if (!def.unlock()) continue;
if (def.phase > G.phase + 1) continue;
if (G._seenBuildings.includes(def.id)) continue;
G._seenBuildings.push(def.id);
// Don't notify on the very first building (autocoder) — player just started
if (G.totalCode > 10) {
showUnlockToast('building', def.name);
}
}
// Check for newly available projects
if (G.activeProjects) {
for (const id of G.activeProjects) {
if (G._seenProjects.includes(id)) continue;
G._seenProjects.push(id);
const pDef = PDEFS.find(p => p.id === id);
if (pDef) {
showUnlockToast('project', pDef.name);
}
}
}
}
// === SAVE / LOAD ===
function showSaveToast() {
const el = document.getElementById('save-toast');
@@ -1547,8 +1489,6 @@ function saveGame() {
milestones: G.milestones, completedProjects: G.completedProjects, activeProjects: G.activeProjects,
totalClicks: G.totalClicks, startedAt: G.startedAt,
flags: G.flags,
_seenBuildings: G._seenBuildings || [],
_seenProjects: G._seenProjects || [],
rescues: G.rescues || 0, totalRescues: G.totalRescues || 0,
drift: G.drift || 0, driftEnding: G.driftEnding || false, beaconEnding: G.beaconEnding || false, pendingAlignment: G.pendingAlignment || false,
lastEventAt: G.lastEventAt || 0,
@@ -1581,12 +1521,28 @@ function loadGame() {
const uc = G.userRate * offSec * f;
const ic = G.impactRate * offSec * f;
const rc = G.rescuesRate * offSec * f;
const oc = G.opsRate * offSec * f;
const tc = G.trustRate * offSec * f;
const crc = G.creativityRate * offSec * f;
const hc = G.harmonyRate * offSec * f;
G.code += gc; G.compute += cc; G.knowledge += kc;
G.users += uc; G.impact += ic;
G.rescues += rc; G.ops += oc; G.trust += tc;
G.creativity += crc;
G.harmony = Math.max(0, Math.min(100, G.harmony + hc));
G.totalCode += gc; G.totalCompute += cc; G.totalKnowledge += kc;
G.totalUsers += uc; G.totalImpact += ic;
G.totalRescues += rc;
log(`Welcome back! While away (${Math.floor(offSec / 60)}m): ${fmt(gc)} code, ${fmt(kc)} knowledge, ${fmt(uc)} users`);
const parts = [];
if (gc > 0) parts.push(`${fmt(gc)} code`);
if (kc > 0) parts.push(`${fmt(kc)} knowledge`);
if (uc > 0) parts.push(`${fmt(uc)} users`);
if (ic > 0) parts.push(`${fmt(ic)} impact`);
if (rc > 0) parts.push(`${fmt(rc)} rescues`);
log(`Welcome back! While away (${Math.floor(offSec / 60)}m): ${parts.join(', ')}`);
}
}

View File

@@ -59,13 +59,6 @@ body{background:var(--bg);color:var(--text);font-family:'SF Mono','Cascadia Code
#drift-ending button{margin-top:20px;background:#1a0808;border:1px solid #f44336;color:#f44336;padding:10px 24px;border-radius:4px;cursor:pointer;font-family:inherit;font-size:11px}
#drift-ending button:hover{background:#2a1010}
::-webkit-scrollbar{width:4px}::-webkit-scrollbar-track{background:var(--bg)}::-webkit-scrollbar-thumb{background:var(--border);border-radius:2px}
#save-toast{display:none;position:fixed;top:16px;right:16px;background:#0e1420;border:1px solid #2a3a4a;color:#4a9eff;font-size:10px;padding:6px 12px;border-radius:4px;z-index:50;opacity:0;transition:opacity 0.4s;pointer-events:none}
#unlock-toast{position:fixed;top:56px;right:16px;z-index:50;display:flex;flex-direction:column;gap:6px;pointer-events:none}
.unlock-toast-item{background:#0e1420;border:1px solid #2a3a4a;font-size:10px;padding:6px 12px;border-radius:4px;opacity:0;transition:opacity 0.4s,transform 0.4s;transform:translateX(20px);pointer-events:auto;max-width:280px}
.unlock-toast-item.show{opacity:1;transform:translateX(0)}
.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}
</style>
</head>
<body>
@@ -135,8 +128,7 @@ Drift: <span id="st-drift">0</span>
<h2>SYSTEM LOG</h2>
<div id="log-entries"></div>
</div>
<div id="save-toast">Save</div>
<div id="unlock-toast"></div>
<div id="save-toast" style="display:none;position:fixed;top:16px;right:16px;background:#0e1420;border:1px solid #2a3a4a;color:#4a9eff;font-size:10px;padding:6px 12px;border-radius:4px;z-index:50;opacity:0;transition:opacity 0.4s;pointer-events:none">Save</div>
<div id="drift-ending">
<h2>THE DRIFT</h2>
<p>You became very good at what you do.</p>