mirror of
https://github.com/legop3/MultiRoombaRover.git
synced 2026-09-16 01:21:20 -04:00
136 lines
5.5 KiB
JavaScript
136 lines
5.5 KiB
JavaScript
// Controller Prompt Labels
|
||
// Purpose: Converts persisted controller bindings into compact prompts for the connected hardware.
|
||
// Scope: Delegates hardware identification and standard button naming to gamepad-helper while
|
||
// keeping rover action aliases and compact presentation local to the controller input layer.
|
||
import GamepadHelper from '@lizardbyte/gamepad-helper/src/js/gamepad-helper.js';
|
||
|
||
const gamepadHelper = new GamepadHelper();
|
||
|
||
const ACTION_ALIASES = {
|
||
driveForward: { bindingId: 'drive', direction: 'up' },
|
||
driveBackward: { bindingId: 'drive', direction: 'down' },
|
||
driveLeft: { bindingId: 'drive', direction: 'left' },
|
||
driveRight: { bindingId: 'drive', direction: 'right' },
|
||
cameraUp: { bindingId: 'cameraTilt', direction: 'up' },
|
||
cameraDown: { bindingId: 'cameraTilt', direction: 'down' },
|
||
auxMainForward: { bindingId: 'mainBrush', direction: 'forward' },
|
||
auxMainReverse: { bindingId: 'mainBrush', direction: 'reverse' },
|
||
auxSideForward: { bindingId: 'sideBrush', direction: 'forward' },
|
||
auxSideReverse: { bindingId: 'sideBrush', direction: 'reverse' },
|
||
auxVacuumFast: { bindingId: 'vacuum' },
|
||
auxVacuumSlow: { bindingId: 'vacuum' },
|
||
auxAllForward: { bindingId: 'allAux' },
|
||
};
|
||
|
||
const DIRECTION_GLYPHS = {
|
||
up: '↑',
|
||
down: '↓',
|
||
left: '←',
|
||
right: '→',
|
||
forward: '+',
|
||
reverse: '−',
|
||
};
|
||
|
||
const TANK_DIRECTION_GLYPHS = {
|
||
driveForward: ['up', 'up'],
|
||
driveBackward: ['down', 'down'],
|
||
driveLeft: ['down', 'up'],
|
||
driveRight: ['up', 'down'],
|
||
};
|
||
|
||
const COMPACT_BUTTON_NAMES = {
|
||
DUp: 'D↑',
|
||
DDown: 'D↓',
|
||
DLeft: 'D←',
|
||
DRight: 'D→',
|
||
TouchPad: 'Touchpad',
|
||
};
|
||
|
||
function controllerType(controller, promptStyle) {
|
||
/* Manual prompt selection is a direct library controller type, not a model-name imitation.
|
||
Automatic mode gives the complete browser ID to gamepad-helper unchanged; in particular,
|
||
its vendor/product lookup directly recognizes Linux's 054c-0ce6 DualSense identifier. */
|
||
if (promptStyle && promptStyle !== 'auto') return promptStyle;
|
||
return gamepadHelper.detectControllerType(controller?.id ?? '');
|
||
}
|
||
|
||
export function describeController(controller) {
|
||
const info = gamepadHelper.getGamepadInfo(controller?.id ?? '');
|
||
return {
|
||
model: info.type,
|
||
brand: info.type === gamepadHelper.CONTROLLER_TYPES.PLAYSTATION ? 'Sony' : null,
|
||
description: info.name,
|
||
};
|
||
}
|
||
|
||
function compactButtonName(source, type) {
|
||
const name = gamepadHelper.getButtonName(type, source.index);
|
||
return COMPACT_BUTTON_NAMES[name] ?? name;
|
||
}
|
||
|
||
function compactAxisName(source) {
|
||
/* Standard browser mappings place sticks in adjacent pairs. Showing the stick instead of its
|
||
raw component keeps prompts short; the action and optional arrow already convey the axis. */
|
||
if (source.index === 0 || source.index === 1) return 'LS';
|
||
if (source.index === 2 || source.index === 3) return 'RS';
|
||
return `A${source.index}`;
|
||
}
|
||
|
||
function compactAxisPairName(source) {
|
||
if (source.x === 0 && source.y === 1) return 'LS';
|
||
if (source.x === 2 && source.y === 3) return 'RS';
|
||
return `A${source.x}/${source.y}`;
|
||
}
|
||
|
||
function compactSourceName(source, type) {
|
||
if (!source) return '—';
|
||
if (source.kind === 'chord') {
|
||
return (source.inputs ?? []).map((input) => compactSourceName(input, type)).join('+');
|
||
}
|
||
if (source.kind === 'axisPair') return compactAxisPairName(source);
|
||
if (source.kind === 'button' || source.kind === 'buttonAxis') {
|
||
return compactButtonName(source, type);
|
||
}
|
||
if (source.kind === 'axis' || source.kind === 'axisButton') {
|
||
return compactAxisName(source);
|
||
}
|
||
return '—';
|
||
}
|
||
|
||
export function bindingForControllerAction(profile, actionId) {
|
||
const direct = profile?.bindings?.[actionId];
|
||
if (direct?.sources?.length) return { binding: direct, direction: null };
|
||
if (profile?.calibration?.driveMode === 'tank' && actionId === 'cameraUp') {
|
||
return { binding: profile?.bindings?.tankCameraUp ?? null, direction: null };
|
||
}
|
||
if (profile?.calibration?.driveMode === 'tank' && actionId === 'cameraDown') {
|
||
return { binding: profile?.bindings?.tankCameraDown ?? null, direction: null };
|
||
}
|
||
const alias = ACTION_ALIASES[actionId];
|
||
if (!alias) return { binding: direct ?? null, direction: null };
|
||
return {
|
||
binding: profile?.bindings?.[alias.bindingId] ?? null,
|
||
direction: alias.direction ?? null,
|
||
};
|
||
}
|
||
|
||
export function formatControllerBinding(profile, actionId, controller) {
|
||
if (profile?.calibration?.driveMode === 'tank' && TANK_DIRECTION_GLYPHS[actionId]) {
|
||
const leftSource = profile?.bindings?.tankLeft?.sources?.[0];
|
||
const rightSource = profile?.bindings?.tankRight?.sources?.[0];
|
||
if (!leftSource || !rightSource) return '—';
|
||
const type = controllerType(controller, profile?.promptStyle);
|
||
const [leftDirection, rightDirection] = TANK_DIRECTION_GLYPHS[actionId];
|
||
/* A tank movement is inherently a two-input gesture. Showing both compact stick directions
|
||
makes help labels accurate without spelling out controller model names or raw axis numbers. */
|
||
return `${compactSourceName(leftSource, type)} ${DIRECTION_GLYPHS[leftDirection]} + ${compactSourceName(rightSource, type)} ${DIRECTION_GLYPHS[rightDirection]}`;
|
||
}
|
||
const { binding, direction } = bindingForControllerAction(profile, actionId);
|
||
const source = binding?.sources?.[0];
|
||
if (!source) return '—';
|
||
const type = controllerType(controller, profile?.promptStyle);
|
||
const label = compactSourceName(source, type);
|
||
const directionLabel = direction ? DIRECTION_GLYPHS[direction] : null;
|
||
return directionLabel ? `${label} ${directionLabel}` : label;
|
||
}
|