fuzzyworld

explodorama / files / src / ui / targetLabel.ts

targetLabel.ts

Blame: the commit that last changed each line · file · history

ebcd0a8f 2026-11-02 Pavel Safronov1import { isDeadly } from '../sim/danger'
2import { pickChoices, resolvePick } from '../sim/picks'
3import { enemyAt } from '../sim/enemies'
4import { bombAt, oilSlide, playerCanPass } from '../sim/state'
5import type { ActionId, GameState, Vec } from '../sim/types'
6import { CHARGE_TEXT } from './info'
7
8/**
9 * The controller's one-line label (user request, 2026-11-01): what confirming the current target
10 * will do, in the player's words. `picked` is the action button (Move, Bomb, Charge, Wait); the
11 * tile decides which of its actions it is (picks.ts `resolvePick`), as it does for a tap. `target`
12 * is null for Wait, which has no tile. An action that would cost a life this turn says so.
13 */
14export function targetLabel(s: GameState, picked: ActionId, target: Vec | null): string {
15 if (!target) return picked === 'wait' ? withDanger('Wait a turn', isDeadly(s, { id: 'wait' })) : 'Nothing to do here'
16 // A bomb two of the button's actions could deal with: the choice dialog asks which.
17 if (pickChoices(s, picked, target).length) return 'Choose what to do with the bomb on this tile'
18 const id = resolvePick(s, picked, target)
19 if (!id) return 'Nothing to do here'
20 return withDanger(labelOf(s, id, target), isDeadly(s, { id, target }))
21}
22
23function withDanger(text: string, deadly: boolean): string {
24 return deadly ? `${text} (costs a life!)` : text
25}
26
27/** A step or run onto oil goes on sliding along the spill (turn.ts), so it is more than a move there. */
28function slidesOnOil(s: GameState, target: Vec): boolean {
29 const d = { x: Math.sign(target.x - s.player.x), y: Math.sign(target.y - s.player.y) }
30 return oilSlide(s, target, d, (q) => playerCanPass(s, q) && !enemyAt(s, q)).length > 0
31}
32
33function labelOf(s: GameState, id: ActionId, target: Vec): string {
34 switch (id) {
35 case 'move': return slidesOnOil(s, target) ? 'Slide to the end of the oil' : bombAt(s, target) ? 'Walk onto the bomb on this tile' : 'Move to this tile'
36 case 'run': return slidesOnOil(s, target) ? 'Slide to the end of the oil' : 'Run to this tile'
37 case 'diagonal': return 'Step diagonally to this tile'
38 case 'slide': return 'Slide to this tile'
39 case 'kick': return 'Kick the bomb on this tile'
40 case 'bomb': return 'Place a bomb on this tile'
41 case 'throw': return 'Throw a bomb to this tile'
42 case 'rpg': return 'Fire an RPG bomb to this tile'
43 case 'detonate': return 'Set off the bomb on this tile'
44 case 'wait': return 'Wait a turn'
45 case 'icbm': return 'Launch an ICBM to this tile'
46 case 'freeze': return 'Freeze every enemy'
47 case 'decoy': return 'Put a decoy on this tile'
48 case 'teleport': return 'Teleport to this tile'
49 case 'charged': return `Place a ${s.charge ? CHARGE_TEXT[s.charge.kind].name : 'charged'} bomb on this tile`
50 }
51}