// Research: labs (bought with funds) produce research points slowly over time; // points buy nodes of a small tree. Research survives a legacy restart. export function labCost(state, ctx) { const r = ctx.rules.research; return Math.ceil(r.labBaseCost * r.labCostGrowth ** state.labs); } export function rpPerSecond(state, ctx, mods) { return state.labs * ctx.rules.research.rpPerLabPerSecond * mods.rp; } export function buyLab(state, ctx) { const { labMax } = ctx.rules.research; const cost = labCost(state, ctx); if (state.labs >= labMax || state.funds < cost) return false; state.funds -= cost; state.labs += 1; return true; } /** 'owned' | 'available' | 'locked' */ export function researchStatus(state, node) { if (state.research.includes(node.id)) return 'owned'; return node.requires.every((id) => state.research.includes(id)) ? 'available' : 'locked'; } export function buyResearch(state, ctx, id) { const node = ctx.researchById.get(id); if (!node || researchStatus(state, node) !== 'available' || state.rp < node.cost) return false; state.rp -= node.cost; state.research.push(id); return true; } export function stepResearch(state, ctx, dt, mods) { state.rp += rpPerSecond(state, ctx, mods) * dt; }