69 lines
2.5 KiB
JavaScript
69 lines
2.5 KiB
JavaScript
// 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;
|
|
}
|