// Research tab: labs produce points over time; points buy nodes of the tree. import { labCost, researchStatus } from '../engine/research.js'; import { t } from '../services/i18n.js'; import { h, icon } from './dom.js'; import { effectText, moneyFormatter, setText } from './text.js'; const BRANCHES = ['prod', 'mkt', 'log', 'fin', 'ins']; export function createResearchView(root, actions) { let ctx; let fmt; let money; let els; const nodeRows = new Map(); function nodeRow(node) { const status = h('p', { class: 'card__meta' }); const buy = h('button', { class: 'btn btn--buy', type: 'button', onclick: () => actions.buyNode(node.id), }); const li = h( 'li', { class: 'card' }, icon(`research.${node.id}`), h( 'div', { class: 'card__body' }, h('h3', { class: 'card__title', text: t(`research.node.${node.id}`) }), h('p', { class: 'card__desc', text: effectText(node.effect, fmt) }), status, ), buy, ); return { li, status, buy, node }; } function rebuild(newCtx, formatter) { ctx = newCtx; fmt = formatter; money = moneyFormatter(fmt); nodeRows.clear(); els = { rp: h('strong'), rate: h('span', { class: 'hint' }), labs: h('span', { class: 'hint' }), buyLab: h('button', { class: 'btn btn--wide', type: 'button', onclick: () => actions.buyLab() }), }; const branchPanels = BRANCHES.map((branch) => { const list = h('ul', { class: 'cards' }); for (const node of ctx.research.filter((n) => n.branch === branch)) { const row = nodeRow(node); nodeRows.set(node.id, row); list.append(row.li); } return h( 'section', { class: 'panel' }, h('h2', { class: 'panel__title', text: t(`research.branch.${branch}`) }), list, ); }); root.replaceChildren( h( 'section', { class: 'panel panel--wide' }, h('h2', { class: 'panel__title', text: t('tab.research') }), h('p', { class: 'hint', text: t('research.intro') }), h( 'div', { class: 'tile' }, icon('res.research'), h('div', {}, h('span', { class: 'tile__label', text: t('theme.res.research') }), els.rp, els.rate), ), els.labs, els.buyLab, ), ...branchPanels, ); } function update(state, d) { const r = ctx.rules.research; setText(els.rp, fmt.number(Math.floor(state.rp))); setText(els.rate, t('research.rate', { rate: fmt.number(Math.round(d.rpRate * 1000) / 1000) })); setText(els.labs, t('research.labs', { count: state.labs, perLab: fmt.number(r.rpPerLabPerSecond * d.mods.rp) })); const maxed = state.labs >= r.labMax; const cost = labCost(state, ctx); setText(els.buyLab, maxed ? t('research.labMax') : t('research.buyLab', { cost: money(cost) })); els.buyLab.disabled = maxed || state.funds < cost; for (const { li, status, buy, node } of nodeRows.values()) { const s = researchStatus(state, node); li.classList.toggle('card--done', s === 'owned'); li.classList.toggle('card--locked', s === 'locked'); buy.hidden = s !== 'available'; buy.disabled = state.rp < node.cost; setText(buy, t('research.cost', { cost: fmt.number(node.cost) })); if (s === 'owned') setText(status, t('research.owned')); else if (s === 'locked') { const names = node.requires.map((id) => t(`research.node.${id}`)).join(', '); setText(status, t('research.requires', { names })); } else setText(status, ''); } } return { rebuild, update }; }