127 lines
3.7 KiB
JavaScript
127 lines
3.7 KiB
JavaScript
// Endless upgrades: families of upgrades that continue forever, level after level.
|
||
|
||
import { endlessQuote, lineAvailable, lineLevel } from '../engine/endless.js';
|
||
import { t } from '../services/i18n.js';
|
||
import { h, icon, makeBuyable, setBuyable } from './dom.js';
|
||
import { bump, purchased } from './fx.js';
|
||
import { effectText, moneyFormatter, setText } from './text.js';
|
||
|
||
const AMOUNTS = [1, 10, 100, 'max'];
|
||
|
||
export function createEndlessView(root, actions) {
|
||
let ctx;
|
||
let fmt;
|
||
let money;
|
||
let amount = 1;
|
||
const rows = new Map();
|
||
|
||
const lineName = (line) =>
|
||
line.effect.target
|
||
? t('endless.name.generator', { target: t(`theme.gen.${line.effect.target}.name`) })
|
||
: t(`endless.name.${line.id}`);
|
||
|
||
function rowFor(line) {
|
||
const level = h('span', { class: 'badge' });
|
||
const total = h('p', { class: 'card__meta' });
|
||
const label = h('strong');
|
||
const cost = h('small');
|
||
const li = h(
|
||
'li',
|
||
{ class: 'card', hidden: true },
|
||
icon(`endless.${line.id}`),
|
||
h(
|
||
'div',
|
||
{ class: 'card__body' },
|
||
h('h3', { class: 'card__title' }, h('span', { text: lineName(line) }), level),
|
||
h('p', { class: 'card__desc', text: t('endless.perLevel', { effect: effectText(line.effect, fmt) }) }),
|
||
total,
|
||
),
|
||
h('div', { class: 'card__price' }, label, cost),
|
||
);
|
||
const row = { li, level, total, label, cost, line, shown: 1 };
|
||
makeBuyable(li, () => {
|
||
if (!actions.buy(line.id, amount)) return;
|
||
purchased(li, `+${fmt.number(row.shown)}`);
|
||
bump(level);
|
||
});
|
||
return row;
|
||
}
|
||
|
||
function rebuild(newCtx, formatter) {
|
||
ctx = newCtx;
|
||
fmt = formatter;
|
||
money = moneyFormatter(fmt);
|
||
rows.clear();
|
||
|
||
const list = h('ul', { class: 'cards cards--grid' });
|
||
for (const line of ctx.endless) {
|
||
const row = rowFor(line);
|
||
rows.set(line.id, row);
|
||
list.append(row.li);
|
||
}
|
||
const amountGroup = h(
|
||
'fieldset',
|
||
{ class: 'segmented' },
|
||
h('legend', { class: 'sr-only', text: t('endless.amount') }),
|
||
...AMOUNTS.map((a) =>
|
||
h(
|
||
'label',
|
||
{},
|
||
h('input', {
|
||
type: 'radio',
|
||
name: 'endless-amount',
|
||
value: String(a),
|
||
checked: a === amount,
|
||
onchange: () => {
|
||
amount = a;
|
||
},
|
||
}),
|
||
h('span', { text: a === 'max' ? t('ui.max') : `×${a}` }),
|
||
),
|
||
),
|
||
);
|
||
|
||
root.replaceChildren(
|
||
h(
|
||
'div',
|
||
{ class: 'panel__head' },
|
||
h('h2', { class: 'panel__title', text: t('endless.title') }),
|
||
amountGroup,
|
||
),
|
||
h('p', { class: 'hint', text: `${t('endless.intro')} ${t('ui.clickToBuy')}` }),
|
||
list,
|
||
);
|
||
}
|
||
|
||
function update(state) {
|
||
let any = false;
|
||
for (const row of rows.values()) {
|
||
const { li, level, total, label, cost, line } = row;
|
||
const open = lineAvailable(state, line);
|
||
li.hidden = !open;
|
||
if (!open) continue;
|
||
any = true;
|
||
|
||
const lv = lineLevel(state, line.id);
|
||
const maxed = lv >= line.maxLevel;
|
||
let quote = endlessQuote(state, ctx, line.id, amount);
|
||
if (quote.count <= 0) quote = endlessQuote(state, ctx, line.id, 1);
|
||
|
||
setText(level, t('endless.level', { level: fmt.number(lv) }));
|
||
setText(
|
||
total,
|
||
lv > 0
|
||
? t('endless.total', { effect: effectText({ ...line.effect, value: line.effect.value ** lv }, fmt) })
|
||
: t('endless.none'),
|
||
);
|
||
row.shown = quote.count;
|
||
setText(label, maxed ? t('endless.maxed') : `×${fmt.number(quote.count)}`);
|
||
setText(cost, maxed ? '' : money(quote.cost));
|
||
setBuyable(li, !maxed && quote.count > 0 && state.funds >= quote.cost);
|
||
}
|
||
root.hidden = !any;
|
||
}
|
||
|
||
return { rebuild, update };
|
||
}
|