// Legacy (prestige): restart the run for permanent points. Points are spent on // repeatable perks, and every point ever earned also gives a small passive bonus. // What survives a restart: see META_KEYS in state.js. import { modifiersFor } from './modifiers.js'; import { createState, META_KEYS } from './state.js'; /** * How many points a lifetime production deserves in total. Diminishing returns * (a power below 1) on EVERYTHING ever produced, not on a single run: each * further point needs far more production than the last, so restarting more and * more often cannot snowball. */ export function pointsFor(lifetimeProduced, ctx, mods) { const { threshold, exponent } = ctx.rules.legacy; if (!(lifetimeProduced >= threshold)) return 0; return Math.floor((lifetimeProduced / threshold) ** exponent * mods.legacyGain); } /** Lifetime production at which the next point is earned (ignoring bonuses to point gains). */ export function nextPointAt(state, ctx) { const { threshold, exponent } = ctx.rules.legacy; return threshold * (state.legacy.earned + 1) ** (1 / exponent); } /** Points a restart would grant right now (0 until the next point is within reach). */ export function legacyGain(state, ctx, mods) { return Math.max(0, pointsFor(state.lifetimeProduced, ctx, mods) - state.legacy.earned); } export function perkLevel(state, perk) { return state.legacy.perks[perk.id] ?? 0; } export function perkCost(perk, level) { return Math.ceil(perk.baseCost * perk.costGrowth ** level); } export function buyPerk(state, ctx, id) { const perk = ctx.perksById.get(id); if (!perk) return false; const level = perkLevel(state, perk); const cost = perkCost(perk, level); if (level >= perk.maxLevel || state.legacy.points < cost) return false; state.legacy.points -= cost; state.legacy.perks[id] = level + 1; return true; } /** Restart the run in place. Returns the points gained, or 0 if not allowed. */ export function prestige(state, ctx, now = Date.now()) { const gain = legacyGain(state, ctx, modifiersFor(state, ctx)); if (gain < 1) return 0; const fresh = createState(ctx, now); const kept = Object.fromEntries(META_KEYS.map((key) => [key, state[key]])); kept.legacy = { ...state.legacy, points: state.legacy.points + gain, earned: state.legacy.earned + gain, prestiges: state.legacy.prestiges + 1, }; Object.assign(state, fresh, kept); // Perks like "head start" shape the new run. state.funds += modifiersFor(state, ctx).startFunds; return gain; }