fuzzyworld

explodorama / files / src / ui / padGlyph.ts

padGlyph.ts

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

92cc4ee3 2026-11-02 Pavel Safronov1import { faceOf, type FaceIndex, type PadFamily } from './padMap'
ebcd0a8f 2026-11-02 Pavel Safronov2
3/**
4 * Which controller picture goes on an action-bar button (controller support; Kenney Input
5 * Prompts, assets/kenney-input-prompts). The buttons are the board mapping of the face buttons
6 * (padMap.ts): Move/Go on Cross or A, Bomb on Square or X, Charge on Triangle or Y, Wait on Circle
92cc4ee3 2026-11-02 Pavel Safronov7 * or B, by position on the pad (bottom, left, top, right). A Nintendo pad is the exception: its A
8 * (right) is Move/Go and its B (bottom) is Wait, Bomb Y and Charge X as before. A pad that is none
9 * of the three has the same layout (W3C standard mapping), so it gets the Xbox letters.
ebcd0a8f 2026-11-02 Pavel Safronov10 */
11export type GlyphButton = 'move' | 'bomb' | 'charge' | 'wait'
12
92cc4ee3 2026-11-02 Pavel Safronov13/** The picture of the face button at each raw index (0 bottom, 1 right, 2 left, 3 top), by family. */
14const FACE_NAMES = {
15 playstation: ['cross', 'circle', 'square', 'triangle'],
16 xbox: ['a', 'b', 'x', 'y'],
17 // Nintendo's letters sit in other places: bottom B, right A, left Y, top X.
18 nintendo: ['b', 'a', 'y', 'x'],
ebcd0a8f 2026-11-02 Pavel Safronov19} as const
20
92cc4ee3 2026-11-02 Pavel Safronov21/** The picture for a raw face button (0 bottom .. 3 top), as dialogs use them. */
22export function faceGlyph(family: PadFamily, face: FaceIndex): string {
23 if (family === 'playstation') return `playstation_button_color_${FACE_NAMES.playstation[face]}`
24 if (family === 'nintendo') return `switch_button_${FACE_NAMES.nintendo[face]}`
25 return `xbox_button_color_${FACE_NAMES.xbox[face]}`
26}
27
28/** The sprite's name (the file without `.png`) for `button` on a pad of `family`: the picture of the face button that does it there. */
ebcd0a8f 2026-11-02 Pavel Safronov29export function glyphFor(family: PadFamily, button: GlyphButton): string {
92cc4ee3 2026-11-02 Pavel Safronov30 return faceGlyph(family, faceOf(family, button))
ebcd0a8f 2026-11-02 Pavel Safronov31}
32
aef53511 2026-11-02 Pavel Safronov33/** The shoulder buttons' pictures (the board's hint): L1 / LB / L and R1 / RB / R. A pad that is none of the three gets the Xbox ones. */
34export function shoulderGlyph(family: PadFamily, side: 'l' | 'r'): string {
35 if (family === 'playstation') return `playstation_trigger_${side}1`
36 if (family === 'nintendo') return `switch_button_${side}`
37 return `xbox_${side}b`
38}
39
92cc4ee3 2026-11-02 Pavel Safronov40/** The d-pad and right-stick pictures, for the demo's virtual controller (padOverlay.ts). */
41export type DpadDir = 'none' | 'up' | 'down' | 'left' | 'right'
42export type StickDir = 'none' | 'left' | 'right' | 'up' | 'down' | 'press'
43
44/** The file-name prefix of a family's d-pad and stick pictures; a pad that is none of the three gets the Xbox ones. */
45function prefix(family: PadFamily): string {
46 return family === 'playstation' ? 'playstation' : family === 'nintendo' ? 'switch' : 'xbox'
47}
48
49/**
50 * The d-pad with `dir` lit (`none`: the plain, all-white d-pad). The plain one and the lit ones are
51 * all the filled style, so pressing only turns one arm red and the picture does not change shape
52 * (Pavel, 2026-11-02; Kenney's `_dpad_none` is an outline, which looked like a different picture).
53 */
54export function dpadGlyph(family: PadFamily, dir: DpadDir): string {
55 return dir === 'none' ? `${prefix(family)}_dpad` : `${prefix(family)}_dpad_${dir}`
56}
57
58/** The right stick pushed towards `dir`, or pressed in (`none`: at rest). */
59export function stickGlyph(family: PadFamily, dir: StickDir): string {
60 return dir === 'none' ? `${prefix(family)}_stick_r` : `${prefix(family)}_stick_r_${dir}`
61}
ebcd0a8f 2026-11-02 Pavel Safronov62
92cc4ee3 2026-11-02 Pavel Safronov63/** The Start button's picture: Options / Menu / Plus. */
64export function startGlyph(family: PadFamily): string {
65 return family === 'playstation' ? 'playstation5_button_options' : family === 'nintendo' ? 'switch_button_plus' : 'xbox_button_menu'
66}
67
68/**
69 * A demo caption with its `{token}`s turned into the controller's pictures, as inline `<img
70 * class="pad-glyph">` (`url` turns a sprite name into its address: `spriteUrl`). Tokens: {dpad},
71 * {move}, {bomb}, {charge}, {wait}, {l1}, {r1}, {stick}, {start}. An unknown token throws, so a
72 * typo in a caption shows up in the tests. The text itself is not escaped: captions are ours.
73 */
74export function padCaptionHtml(text: string, family: PadFamily, url: (name: string) => string): string {
75 return text.replace(/\{(\w+)\}/g, (_, token: string) => {
76 let name: string
77 switch (token) {
78 case 'dpad': name = dpadGlyph(family, 'none'); break
79 case 'move': case 'bomb': case 'charge': case 'wait': name = glyphFor(family, token); break
80 case 'l1': name = shoulderGlyph(family, 'l'); break
81 case 'r1': name = shoulderGlyph(family, 'r'); break
82 case 'stick': name = stickGlyph(family, 'none'); break
83 case 'start': name = startGlyph(family); break
84 default: throw new Error(`Unknown caption token {${token}}`)
85 }
86 return `<img class="pad-glyph" src="${url(name)}" alt="">`
87 })
ebcd0a8f 2026-11-02 Pavel Safronov88}