173 lines
6.2 KiB
JavaScript
173 lines
6.2 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, makeBuyable, setBuyable } from './dom.js';
|
|
import { purchased } from './fx.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) => {
|
|
const li = 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('div', { class: 'card__price' }, h('strong', { text: t('contracts.accept') })),
|
|
);
|
|
makeBuyable(li, () => {
|
|
if (actions.accept(index)) purchased(li, '✓', null);
|
|
});
|
|
return li;
|
|
}),
|
|
);
|
|
}
|
|
els.offersNone.hidden = offers.length > 0;
|
|
for (const li of els.offers.children) setBuyable(li, !state.contracts.active);
|
|
}
|
|
|
|
function update(state) {
|
|
updateEvent(state);
|
|
updateBuffs(state);
|
|
updateActive(state);
|
|
updateOffers(state);
|
|
}
|
|
|
|
return { rebuild, update };
|
|
}
|