115 lines
3.6 KiB
JavaScript
115 lines
3.6 KiB
JavaScript
// 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 };
|
|
}
|