/** * Touch control scheme derived from a game's existing action bindings: the * capture layer renders a virtual joystick, on-screen buttons, and a gesture * surface, and feeds synthetic `touch:` codes into the same * ActionStateTracker the keyboard uses, so games gain touch play without any * per-game input code. Session-lifecycle actions (start/restart/pause/menu/…) * are left off the derived dock — they live in the start screen, pause menu, or * settings sheet, never as a chip over live play. Games refine the derived * defaults through TouchControlsConfig (gesture bindings, button curation) or * opt out entirely with `touch: false`. */ import { type ActionCodesMap } from "./actionBindings.js"; export declare const TOUCH_CODE_PREFIX = "touch:"; export declare function touchCode(action: string): string; /** Every action gains a synthetic touch code alongside its physical codes. */ export declare function withTouchCodes(map: ActionCodesMap | undefined): ActionCodesMap; export interface TouchDragBinding { /** Action fired once per `stepPx` of travel toward that side. */ left?: string; right?: string; up?: string; down?: string; stepPx?: number; } export interface TouchGestureBindings { /** Action fired by a quick still tap on the play surface. */ tap?: string; /** Actions fired by a fast flick past the swipe threshold. */ swipeUp?: string; swipeDown?: string; swipeLeft?: string; swipeRight?: string; /** Continuous drag: repeats the directional action every `stepPx` of travel. Wins over swipes on its axis, except a flick can still fire the swipe binding for a direction the drag leaves unbound. */ drag?: TouchDragBinding; } /** * Physical silhouette a touch button wears. The capture layer draws each as its * own shape — a `pedal` reads as a foot pedal, a `lever` as a pull handle, a * `trigger` as a firing paddle — so a control looks like the thing it does * instead of a labelled circle. `circle`/`square` are the neutral fallbacks. */ export type TouchButtonShape = "circle" | "square" | "pedal" | "lever" | "trigger" | "wheel" | "tab"; /** * Screen zone a touch cluster or button docks to. The four corners plus the * mid `left`/`right` rails (vertical stacks, MMO-style hotbars) and the * `bottom-center` / `top-center` strips let controls use the whole viewport * instead of piling into one bottom bar. */ export type TouchAnchor = "bottom-left" | "bottom-center" | "bottom-right" | "left" | "right" | "top-left" | "top-center" | "top-right"; /** * Player-selectable skin for the whole touch layer. A style is a material + * geometry preset (not just colours), chosen in Settings → Controls and * persisted; `glass` is the translucent default, the rest are opt-in looks. */ export type TouchStyle = "glass" | "arcade" | "mechanical" | "minimal"; /** Every touch skin id, in menu order. */ export declare const TOUCH_STYLES: readonly TouchStyle[]; /** Touch skins as `{ value, label }` rows for the Settings → Controls selector. */ export declare const TOUCH_STYLE_OPTIONS: readonly { value: TouchStyle; label: string; }[]; /** Skin used when neither the game nor the player picks one. */ export declare const DEFAULT_TOUCH_STYLE: TouchStyle; /** * How the movement joystick behaves — the two shipped variants, player-selectable in * Settings → Controls. `fixed` is the classic always-visible stick in its dock corner; * `floating` keeps the corner as an empty capture zone and spawns the stick centered under * wherever the thumb lands, so the hand never has to find a fixed target mid-play. */ export type TouchJoystickVariant = "fixed" | "floating"; /** Every joystick variant id, in menu order. */ export declare const TOUCH_JOYSTICK_VARIANTS: readonly TouchJoystickVariant[]; /** Joystick variants as `{ value, label }` rows for the Settings → Controls selector. */ export declare const TOUCH_JOYSTICK_VARIANT_OPTIONS: readonly { value: TouchJoystickVariant; label: string; }[]; /** Variant used when the player has not picked one. */ export declare const DEFAULT_TOUCH_JOYSTICK_VARIANT: TouchJoystickVariant; /** Where each touch cluster docks; unset falls back to the classic bottom layout. */ export interface TouchLayoutConfig { /** Virtual joystick / movement zone. Default `bottom-left`. */ movement?: TouchAnchor; /** Primary action cluster. Default `bottom-right`. */ actions?: TouchAnchor; /** Utility chip row. Default `bottom-center`. */ utility?: TouchAnchor; } /** The dock zone each cluster resolved to, after applying game config over the bottom-layout defaults. */ export interface ResolvedTouchLayout { movement: TouchAnchor; actions: TouchAnchor; utility: TouchAnchor; } export interface TouchButtonSpec { action: string; label?: string; /** Glyph name from the capture layer's icon catalog (`@jgengine/react/gameIcons` `GameIconName`); unset derives one from the action name, `false` forces the text label. */ icon?: string | false; /** Overrides the derived classification: `primary` renders as a large thumb button, `utility` as a small chip away from the thumb zones. */ kind?: TouchButtonKind; /** Physical silhouette; unset derives one from the action name (`brake`→pedal, `handbrake`→lever, `fire`→trigger, …). */ shape?: TouchButtonShape; /** Docks this button on its own instead of joining its cluster — e.g. a brake pedal pinned to the right rail. */ anchor?: TouchAnchor; /** Custom art drawn as the button face instead of the built-in silhouette — any image URL or `data:image/svg+xml` URI (a slot frame, spell plate, …); the icon/label still sits on top. */ image?: string; } /** Restricts the virtual joystick to one axis — a `horizontal` zone reads as a steering control, freeing throttle/brake to become pedal buttons. */ export interface TouchMovementConfig { axis?: "both" | "horizontal" | "vertical"; } /** One named control context's touch config — the same shape as the base config, minus nested modes. */ export type TouchControlsModeConfig = Omit; /** Game-authored refinement of the derived touch scheme on `defineGame({ touch })` — gestures, curated buttons, hidden actions, cluster layout, skin, and per-context `modes`. */ export interface TouchControlsConfig { /** `false` removes the virtual joystick even when movement actions are bound; an object restricts it to one axis. */ movement?: false | TouchMovementConfig; /** Curated on-screen buttons (order preserved); omit to derive one button per remaining action. Derivation skips session-lifecycle actions (start/restart/pause/menu/…); list one here to dock it anyway. */ buttons?: readonly (string | TouchButtonSpec)[]; /** Play-surface gestures; actions consumed here stop appearing as derived buttons. */ gestures?: TouchGestureBindings; /** Drag-to-look on the play surface; defaults to true for first-person games. */ look?: boolean; /** Radians of look rotation per pixel of touch travel. Default 0.005. */ lookSensitivity?: number; /** Actions to leave off the derived button cluster. */ hidden?: readonly string[]; /** Dock each cluster to a screen zone; omit for the classic bottom layout. */ layout?: TouchLayoutConfig; /** Suggested default skin; the player's Settings choice overrides it. Default `glass`. */ style?: TouchStyle; /** * Per-context control sets for games whose verbs change at runtime — on foot vs driving vs * flying. Each key is a mode name whose fields *replace* the matching base fields while that * mode is active (base config = the default mode). Gameplay switches with * `setTouchControlsMode(ctx, "car")` and back with `null`; only the actions a context actually * uses should be visible in it, so a runner on foot never sees flight or vehicle chrome. */ modes?: Record; } export interface TouchJoystick { up: string | null; down: string | null; left: string | null; right: string | null; } export type TouchButtonKind = "primary" | "utility"; export interface TouchButton { action: string; label: string; /** Glyph hint for the capture layer: a name to render, `false` to force text, null to derive from the action name. */ icon: string | false | null; /** `primary` = frequent gameplay verb, rendered as a large thumb button; `utility` = meta action (start, restart, pause, toggles), rendered as a small chip. */ kind: TouchButtonKind; /** Physical silhouette to draw. */ shape: TouchButtonShape; /** Per-button dock override; null joins the cluster's anchor. */ anchor: TouchAnchor | null; /** Custom face art (image/SVG URL); null draws the built-in silhouette. */ image: string | null; } export interface TouchScheme { joystick: TouchJoystick | null; buttons: readonly TouchButton[]; gestures: TouchGestureBindings | null; look: boolean; lookSensitivity: number; /** Resolved dock zones for the three clusters. */ layout: ResolvedTouchLayout; /** Game-suggested default skin; the shell swaps in the player's choice when set. */ style: TouchStyle; } /** * Default silhouette for an action; `circle` when nothing more specific fits. * * @capability touch-controls default on-screen button silhouette for a touch action */ export declare function touchButtonShape(action: string): TouchButtonShape; export declare function touchButtonKind(action: string): TouchButtonKind; export declare function touchActionLabel(action: string): string; export interface DeriveTouchSchemeOptions { reserved: ReadonlySet; firstPerson: boolean; config?: TouchControlsConfig | false; /** Active control context (see {@link TouchControlsConfig.modes}); null/unknown falls back to the base config. */ mode?: string | null; } /** * Null means "render no touch controls" — either the game opted out or there * is nothing to synthesize. */ export declare function deriveTouchScheme(input: ActionCodesMap | undefined, { reserved, firstPerson, config: rawConfig, mode }: DeriveTouchSchemeOptions): TouchScheme | null;