Files
idle_clicker/public/js/ui/contractsView.js
T

165 lines
6.0 KiB
JavaScript

// Contracts tab: opportunities to seize, active effects, the active contract
// and the board of offers.
import { t } from '../services/i18n.js';
import { h } from './dom.js';
import { formatDuration, moneyFormatter, rewardText, setText } from './text.js';
const BUFF_LABEL = {
demand_mult: 'contracts.buff.demand',
global_mult: 'contracts.buff.production',
material_price_mult: 'contracts.buff.material',
};
export function createContractsView(root, actions) {
let ctx;
let fmt;
let els;
let offersSignature = '';
let buffsSignature = '';
function rebuild(newCtx, formatter) {
ctx = newCtx;
fmt = formatter;
offersSignature = '';
buffsSignature = '';
els = {
eventTitle: h('h3', { class: 'card__title' }),
eventText: h('p', { class: 'card__desc' }),
eventTime: h('p', { class: 'card__meta' }),
claim: h('button', { class: 'btn btn--primary', type: 'button', text: t('contracts.claim'), onclick: () => actions.claimEvent() }),
eventBox: h('div', { class: 'callout' }),
eventNone: h('p', { class: 'hint', text: t('contracts.eventNone') }),
buffs: h('ul', { class: 'cards' }),
activeBox: h('div', { class: 'callout' }),
activeTitle: h('h3', { class: 'card__title' }),
activeReward: h('p', { class: 'card__desc' }),
activeProgress: h('progress', { class: 'progress', max: 1, value: 0 }),
activeText: h('p', { class: 'card__meta' }),
abandon: h('button', { class: 'btn', type: 'button', text: t('contracts.abandon'), onclick: () => actions.abandon() }),
activeNone: h('p', { class: 'hint', text: t('contracts.activeNone') }),
offers: h('ul', { class: 'cards' }),
offersNone: h('p', { class: 'hint', text: t('contracts.offersNone') }),
};
els.eventBox.append(els.eventTitle, els.eventText, els.eventTime, els.claim);
els.activeBox.append(els.activeTitle, els.activeReward, els.activeProgress, els.activeText, els.abandon);
root.replaceChildren(
h(
'section',
{ class: 'panel' },
h('h2', { class: 'panel__title', text: t('contracts.events') }),
els.eventBox,
els.eventNone,
h('h3', { class: 'dialog__subtitle', text: t('contracts.effects') }),
els.buffs,
),
h(
'section',
{ class: 'panel' },
h('h2', { class: 'panel__title', text: t('contracts.active') }),
els.activeBox,
els.activeNone,
h('h3', { class: 'dialog__subtitle', text: t('contracts.board') }),
h('p', { class: 'hint', text: t('contracts.boardInfo', { seconds: ctx.rules.contracts.offerEverySeconds }) }),
els.offers,
els.offersNone,
),
);
}
function eventText(type) {
const cfg = ctx.rules.events.types.find((e) => e.id === type);
return t(`event.${type}.text`, {
value: cfg?.buff ? fmt.number(cfg.buff.value) : '',
seconds: cfg?.buff ? cfg.buff.seconds : '',
});
}
function updateEvent(state) {
const current = state.events.current;
els.eventBox.hidden = !current;
els.eventNone.hidden = Boolean(current);
if (!current) return;
setText(els.eventTitle, t(`event.${current.type}.title`));
setText(els.eventText, eventText(current.type));
setText(els.eventTime, t('contracts.timeLeft', { time: formatDuration(current.remaining) }));
}
function updateBuffs(state) {
const signature = state.events.buffs.map((b) => `${b.type}:${b.value}`).join();
if (signature !== buffsSignature) {
buffsSignature = signature;
els.buffs.replaceChildren(
...state.events.buffs.map((b) =>
h(
'li',
{ class: 'card' },
h('div', { class: 'card__body' }, h('h3', { class: 'card__title', text: t(BUFF_LABEL[b.type], { value: fmt.number(b.value) }) }), h('p', { class: 'card__meta' })),
),
),
);
}
[...els.buffs.children].forEach((li, i) => {
const buff = state.events.buffs[i];
if (buff) setText(li.querySelector('.card__meta'), t('contracts.timeLeft', { time: formatDuration(buff.remaining) }));
});
}
function updateActive(state) {
const active = state.contracts.active;
els.activeBox.hidden = !active;
els.activeNone.hidden = Boolean(active);
if (!active) return;
const { offer } = active;
setText(els.activeTitle, t(`contracts.template.${offer.template}`));
setText(els.activeReward, rewardText(offer.reward, fmt, offer.seconds));
els.activeProgress.max = offer.target;
els.activeProgress.value = active.progress;
setText(
els.activeText,
t('contracts.progress', {
done: fmt.number(Math.floor(active.progress)),
target: fmt.number(offer.target),
time: formatDuration(active.remaining),
}),
);
}
function updateOffers(state) {
const offers = state.contracts.offers;
const signature = offers.map((o) => `${o.template}:${o.target}:${o.reward.type}:${o.reward.amount}`).join();
if (signature !== offersSignature) {
offersSignature = signature;
els.offers.replaceChildren(
...offers.map((offer, index) =>
h(
'li',
{ class: 'card' },
h(
'div',
{ class: 'card__body' },
h('h3', { class: 'card__title', text: t(`contracts.template.${offer.template}`) }),
h('p', { class: 'card__desc', text: t('contracts.goal', { target: fmt.number(offer.target), duration: formatDuration(offer.seconds) }) }),
h('p', { class: 'card__meta', text: rewardText(offer.reward, fmt, offer.seconds) }),
),
h('button', { class: 'btn btn--buy', type: 'button', text: t('contracts.accept'), 'data-index': index, onclick: () => actions.accept(index) }),
),
),
);
}
els.offersNone.hidden = offers.length > 0;
for (const button of els.offers.querySelectorAll('button')) button.disabled = Boolean(state.contracts.active);
}
function update(state) {
updateEvent(state);
updateBuffs(state);
updateActive(state);
updateOffers(state);
}
return { rebuild, update };
}