joymap
Version:
A Gamepad API wrapper and mapping tool.
213 lines (173 loc) • 5.44 kB
text/typescript
import memoize from 'fast-memoize';
import { assignIn, mapValues, omit, forEach } from 'lodash/fp';
import { buttonMap, stickMap } from '../common/utils';
import createBaseModule from '../baseModule/base';
import { StickResult, ButtonResult } from '../types';
// the following definition allows for the circular Mapper type
// eslint-disable-next-line @typescript-eslint/no-empty-interface
export interface QueryModule extends ReturnType<typeof createQueryModule> {}
export type MapperResult = any;
export type Mapper = (module: QueryModule) => MapperResult;
export const emptyMapper: MapperResult = null;
export const emptyStick: StickResult = {
type: 'stick',
value: [0, 0],
pressed: false,
justChanged: false,
inverts: [false, false],
};
export const emptyButton: ButtonResult = {
type: 'button',
value: 0,
pressed: false,
justChanged: false,
};
export default function createQueryModule(params = {}) {
const { state, module: baseModule } = createBaseModule(params);
let mappers: Record<string, Mapper> = {};
const buttonMapMemoized = memoize(buttonMap);
const stickMapMemoized = memoize(stickMap);
const module = assignIn(baseModule, {
getButton: (inputName: string) => {
if (!module.isConnected()) {
return emptyButton;
}
return buttonMapMemoized(
state.pad,
state.prevPad,
state.buttons[inputName],
state.threshold,
state.clampThreshold,
);
},
getButtons: (...inputNames: string[]) => {
if (!module.isConnected()) {
const result: Record<string, ButtonResult> = {};
forEach((mapperName) => {
result[mapperName] = emptyButton;
}, inputNames);
return result;
}
const result: Record<string, ButtonResult> = {};
forEach((inputName) => {
result[inputName] = buttonMapMemoized(
state.pad,
state.prevPad,
state.buttons[inputName],
state.threshold,
state.clampThreshold,
);
}, inputNames);
return result;
},
getAllButtons: (): Record<string, ButtonResult> => {
if (!module.isConnected()) {
return mapValues(state.buttons, () => emptyButton);
}
return mapValues(
(button) =>
buttonMapMemoized(
state.pad,
state.prevPad,
button,
state.threshold,
state.clampThreshold,
),
state.buttons,
);
},
getStick: (inputName: string) => {
if (!module.isConnected()) {
return emptyStick;
}
const { indexes, inverts } = state.sticks[inputName];
return stickMapMemoized(
state.pad,
state.prevPad,
indexes,
inverts,
state.threshold,
state.clampThreshold,
);
},
getSticks: (...inputNames: string[]) => {
if (!module.isConnected()) {
const result: Record<string, StickResult> = {};
forEach((mapperName) => {
result[mapperName] = emptyStick;
}, inputNames);
return result;
}
const result: Record<string, StickResult> = {};
forEach((inputName) => {
const { indexes, inverts } = state.sticks[inputName];
result[inputName] = stickMapMemoized(
state.pad,
state.prevPad,
indexes,
inverts,
state.threshold,
state.clampThreshold,
);
}, inputNames);
return result;
},
getAllSticks: (): Record<string, StickResult> => {
if (!module.isConnected()) {
return mapValues(state.sticks, () => emptyStick);
}
return mapValues((stick) => {
const { indexes, inverts } = stick;
return stickMapMemoized(
state.pad,
state.prevPad,
indexes,
inverts,
state.threshold,
state.clampThreshold,
);
}, state.sticks);
},
getMapper: (mapperName: string) => {
if (!module.isConnected()) {
const emptyMapper = null;
return emptyMapper;
}
return mappers[mapperName](module);
},
getMappers: (...mapperNames: string[]) => {
if (!module.isConnected()) {
const result: Record<string, MapperResult> = {};
forEach((mapperName) => {
result[mapperName] = emptyMapper;
}, mapperNames);
return result;
}
const result: Record<string, Mapper> = {};
forEach((mapperName) => {
result[mapperName] = mappers[mapperName](module);
}, mapperNames);
return result;
},
getAllMappers: (): Record<string, MapperResult> => {
if (!module.isConnected()) {
return mapValues(() => emptyMapper, mappers);
}
return mapValues((mapper) => mapper(module), mappers);
},
setMapper: (mapperName: string, mapper: Mapper) => {
mappers[mapperName] = mapper;
},
removeMapper: (mapperName: string) => {
mappers = omit([mapperName], mappers);
},
clearMappers: () => {
mappers = {};
},
destroy: () => {
baseModule.destroy();
module.clearMappers();
},
});
return module;
}