explodorama / files / src / ui / chargeOffer.ts
chargeOffer.ts
| 1 | import { fireableHeld } from '../sim/actions' |
| 2 | import { salePrice } from '../sim/charges' |
| 3 | import type { GameState } from '../sim/types' |
| 4 | import { CHARGE_ICONS, spriteUrl } from '../render/sprites' |
| 5 | import { openDialog } from './dialog' |
| 6 | import { CHARGE_TEXT } from './info' |
| 7 | |
| 8 | /** |
| 9 | * A charge picked up while carrying another (PLAN.md §19.7; user request, 2026-10-24): the player |
| 10 | * carries one at a time. Keep the one held and sell the new one, or swap: take the new one, and |
| 11 | * the one held is sold for what is left of it; or, for a charge that needs no aiming (Freeze), |
| 12 | * use the one held now and then take the new one (user request, 2026-10-31). It must be answered; |
| 13 | * it takes no turn. |
| 14 | */ |
| 15 | export function openChargeOffer(s: GameState, answer: (choice: 'keep' | 'swap' | 'fire') => void): void { |
| 16 | const offered = s.chargeOffer?.kind |
| 17 | const offeredLeft = s.chargeOffer?.left ?? 0 |
| 18 | const held = s.charge |
| 19 | if (!offered || !held) return |
| 20 | const card = (kind: typeof offered, note: string) => |
| 21 | `<div class="almanac-entry"><img src="${spriteUrl(CHARGE_ICONS[kind])}" alt="" draggable="false">` + |
| 22 | `<div><strong>${CHARGE_TEXT[kind].name}</strong> <small>${note}</small><p>${CHARGE_TEXT[kind].text}</p></div></div>` |
| 23 | const newPrice = salePrice(offered, offeredLeft) |
| 24 | const oldPrice = salePrice(held.kind, held.left) |
| 25 | // Fired first, a charge with uses left sells what remains. |
| 26 | const fireLeft = held.left > 1 ? salePrice(held.kind, held.left - 1) : 0 |
| 27 | openDialog({ |
| 28 | icon: spriteUrl(CHARGE_ICONS[offered]), |
| 29 | title: `${CHARGE_TEXT[offered].name}!`, |
| 30 | body: |
| 31 | `<p>You can carry one charge at a time. The one you don’t keep is sold for coins.</p>` + |
| 32 | card(offered, `new · ${offeredLeft === 1 ? '1 use' : `${offeredLeft} uses`}`) + |
| 33 | card(held.kind, `yours · ${held.left} ${held.left === 1 ? 'use' : 'uses'} left`), |
| 34 | buttons: [ |
| 35 | { label: `Keep ${CHARGE_TEXT[held.kind].name} · +${newPrice}`, kind: 'plain', onClick: () => answer('keep') }, |
| 36 | { label: `Take ${CHARGE_TEXT[offered].name} · +${oldPrice}`, kind: 'primary', onClick: () => answer('swap') }, |
| 37 | ...(fireableHeld(s) |
| 38 | ? [{ label: `Use ${CHARGE_TEXT[held.kind].name}, then take ${CHARGE_TEXT[offered].name}${fireLeft ? ` · +${fireLeft}` : ''}`, kind: 'primary' as const, onClick: () => answer('fire') }] |
| 39 | : []), |
| 40 | ], |
| 41 | required: true, |
| 42 | }) |
| 43 | } |