122 lines
3.9 KiB
JavaScript
122 lines
3.9 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, makeBuyable, setBuyable } from './dom.js';
|
|
import { purchased } from './fx.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 cost = h('strong');
|
|
const price = h('div', { class: 'card__price' }, cost);
|
|
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,
|
|
),
|
|
price,
|
|
);
|
|
makeBuyable(li, () => {
|
|
if (actions.buyNode(node.id)) purchased(li, '✓', null);
|
|
});
|
|
return { li, status, price, cost, 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: () => {
|
|
if (actions.buyLab()) purchased(els.buyLab, '+1');
|
|
},
|
|
}),
|
|
};
|
|
|
|
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')} ${t('ui.clickToBuy')}` }),
|
|
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, price, cost, node } of nodeRows.values()) {
|
|
const s = researchStatus(state, node);
|
|
li.classList.toggle('card--done', s === 'owned');
|
|
li.classList.toggle('card--locked', s === 'locked');
|
|
price.hidden = s !== 'available';
|
|
setText(cost, t('research.cost', { cost: fmt.number(node.cost) }));
|
|
setBuyable(li, s === 'available' && state.rp >= 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 };
|
|
}
|