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

168 lines
5.2 KiB
JavaScript

// Gamble tab: chips are hard to buy and hard to keep. Stake them on a cycle.
import { chipCost, odds, reliabilityCost } from '../engine/gamble.js';
import { chipDemandBonus } from '../engine/formulas.js';
import { t } from '../services/i18n.js';
import { h, icon } from './dom.js';
import { moneyFormatter, setText } from './text.js';
const STAKES = [
{ id: 'one', count: () => 1 },
{ id: 'tenth', fraction: 0.1 },
{ id: 'half', fraction: 0.5 },
{ id: 'all', fraction: 1 },
];
const LOG_SIZE = 6;
export function createGambleView(root, actions) {
let ctx;
let fmt;
let money;
let stake = STAKES[0];
let els;
const log = [];
const stakeCount = (chips) => (stake.count ? stake.count() : Math.max(1, Math.ceil(chips * stake.fraction)));
function stakeLabel(option) {
if (option.id === 'one') return t('gamble.stakeOne');
if (option.id === 'all') return t('gamble.stakeAll');
return t('gamble.stakePercent', { percent: option.fraction * 100 });
}
function renderLog() {
els.log.replaceChildren(
...log.map((entry) =>
h('li', { class: `log__entry log__entry--${entry.outcome}`, text: t(`gamble.result.${entry.outcome}`, { amount: fmt.number(Math.abs(entry.delta)) }) }),
),
);
els.logEmpty.hidden = log.length > 0;
}
function run(state) {
const result = actions.run(stakeCount(state.chips));
if (!result) return;
log.unshift(result);
log.length = Math.min(log.length, LOG_SIZE);
renderLog();
}
let lastState;
function rebuild(newCtx, formatter) {
ctx = newCtx;
fmt = formatter;
money = moneyFormatter(fmt);
els = {
chips: h('strong'),
bonus: h('span', { class: 'hint' }),
buyChip: h('button', { class: 'btn btn--wide', type: 'button', onclick: () => actions.buyChip() }),
reliability: h('button', { class: 'btn btn--wide', type: 'button', onclick: () => actions.buyReliability() }),
odds: h('p', { class: 'hint' }),
run: h('button', { class: 'btn btn--primary btn--big', type: 'button', onclick: () => run(lastState) }),
log: h('ul', { class: 'log' }),
logEmpty: h('p', { class: 'hint', text: t('gamble.logEmpty') }),
};
root.replaceChildren(
h(
'section',
{ class: 'panel' },
h('h2', { class: 'panel__title', text: t('theme.tab.gamble') }),
h('p', { class: 'hint', text: t('gamble.intro') }),
h(
'div',
{ class: 'tile' },
icon('res.chips'),
h('div', {}, h('span', { class: 'tile__label', text: t('theme.res.chips') }), els.chips, els.bonus),
),
els.buyChip,
els.reliability,
),
h(
'section',
{ class: 'panel' },
h('h2', { class: 'panel__title', text: t('gamble.play') }),
h('p', { class: 'field__label', text: t('gamble.stake') }),
h(
'fieldset',
{ class: 'segmented' },
h('legend', { class: 'sr-only', text: t('gamble.stake') }),
...STAKES.map((option) =>
h(
'label',
{},
h('input', {
type: 'radio',
name: 'gamble-stake',
value: option.id,
checked: option === stake,
onchange: () => {
stake = option;
},
}),
h('span', { text: stakeLabel(option) }),
),
),
),
els.odds,
els.run,
h('h3', { class: 'dialog__subtitle', text: t('gamble.log') }),
els.logEmpty,
els.log,
),
);
renderLog();
}
function update(state, d) {
lastState = state;
const g = ctx.rules.gamble;
setText(els.chips, fmt.number(state.chips));
setText(els.bonus, t('gamble.bonus', { bonus: fmt.number(chipDemandBonus(state.chips, g)) }));
const cost = chipCost(state, ctx);
setText(els.buyChip, t('gamble.buyChip', { cost: money(cost) }));
els.buyChip.disabled = state.funds < cost || state.chips >= g.maxChips;
const maxed = state.reliabilityLevel >= g.reliability.maxLevel;
setText(
els.reliability,
maxed
? t('gamble.reliabilityMax', { level: state.reliabilityLevel })
: t('gamble.reliability', {
level: state.reliabilityLevel,
max: g.reliability.maxLevel,
cost: money(reliabilityCost(state, ctx)),
}),
);
els.reliability.disabled = maxed || state.funds < reliabilityCost(state, ctx);
const p = odds(state, ctx, d.mods);
const percent = (x) => fmt.number(Math.round(x * 1000) / 10);
setText(
els.odds,
t('gamble.odds', {
win: percent(p.win),
jackpot: percent(p.jackpot),
multiplier: g.jackpotMultiplier,
crash: percent(p.crash),
lose: percent(p.lose),
}),
);
const cooling = state.gambleCooldown > 0;
setText(
els.run,
cooling
? t('gamble.cooldown', { seconds: fmt.number(Math.ceil(state.gambleCooldown * 10) / 10) })
: `${t('theme.action.gamble')} (${fmt.number(Math.min(stakeCount(state.chips), state.chips))})`,
);
els.run.disabled = cooling || state.chips < 1;
}
return { rebuild, update };
}