fuzzyworld

explodorama / files / src / ui / padPlaces.ts

padPlaces.ts

2.1 KB · history · blame

1import { PAD_LAYOUTS, type PadDot, type PadLayout } from './padLayouts'
2import { faceOf, type FaceIndex, type PadFamily } from './padMap'
3
4/**
5 * Where each part of the demo's virtual controller (padOverlay.ts) goes for a pad family, from the
6 * measured layouts (padLayouts.ts). Pure, so the tests can check it without a DOM.
7 */
8export type PadSlot = 'dpad' | 'stick' | 'move' | 'bomb' | 'charge' | 'wait' | 'l1' | 'r1' | 'start'
9
10/** A part's picture: its centre and its size (a square), in the layout's units. */
11export interface PadPlace { x: number; y: number; size: number }
12
13/** The face position (a key of `layout.face`) of each raw face index: 0 bottom, 1 right, 2 left, 3 top. */
14const FACE_POSITION = ['bottom', 'right', 'left', 'top'] as const satisfies readonly (keyof PadLayout['face'])[]
15
16/** How much bigger than its button's circle a picture is drawn, so the picture covers the circle. */
17export const GLYPH_SCALE = 1.2
18
19/** A pad that is none of the three has the Xbox layout (W3C standard mapping). */
20export function layoutOf(family: PadFamily): PadLayout {
21 return PAD_LAYOUTS[family === 'generic' ? 'xbox' : family]
22}
23
24/** The face position of `action` on a pad of `family`, e.g. Nintendo's Move is 'right'. */
25export function facePosition(family: PadFamily, action: 'move' | 'bomb' | 'charge' | 'wait'): keyof PadLayout['face'] {
26 return FACE_POSITION[faceOf(family, action) as FaceIndex]
27}
28
29const place = (d: PadDot, scale = GLYPH_SCALE): PadPlace => ({ x: d.x, y: d.y, size: d.r * 2 * scale })
30
31/** Every lit part's place on the pad of `family`. */
32export function padPlaces(family: PadFamily): Record<PadSlot, PadPlace> {
33 const l = layoutOf(family)
34 const face = (a: 'move' | 'bomb' | 'charge' | 'wait'): PadPlace => place(l.face[facePosition(family, a)])
35 const shoulder = (b: PadLayout['l']): PadPlace => ({ x: b.x, y: b.y, size: b.h * 2.4 })
36 return {
37 dpad: place(l.dpad),
38 stick: place(l.rstick),
39 move: face('move'),
40 bomb: face('bomb'),
41 charge: face('charge'),
42 wait: face('wait'),
43 l1: shoulder(l.l),
44 r1: shoulder(l.r),
45 start: place(l.start, 1.5),
46 }
47}