like2d 2.8.0 → 2.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +44 -41
- package/dist/__benchmarks__/vector2.bench.d.ts +2 -0
- package/dist/__benchmarks__/vector2.bench.d.ts.map +1 -0
- package/dist/__benchmarks__/vector2.bench.js +74 -0
- package/dist/{core → audio}/audio.d.ts +21 -9
- package/dist/audio/audio.d.ts.map +1 -0
- package/dist/{core → audio}/audio.js +15 -4
- package/dist/audio/index.d.ts +2 -0
- package/dist/audio/index.d.ts.map +1 -0
- package/dist/engine.d.ts +30 -15
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +79 -160
- package/dist/events.d.ts +86 -0
- package/dist/events.d.ts.map +1 -0
- package/dist/events.js +5 -0
- package/dist/gamecontrollerdb.txt +9 -8
- package/dist/graphics/canvas.d.ts +65 -0
- package/dist/graphics/canvas.d.ts.map +1 -0
- package/dist/graphics/canvas.js +224 -0
- package/dist/graphics/drawing.d.ts +203 -0
- package/dist/graphics/drawing.d.ts.map +1 -0
- package/dist/graphics/drawing.js +388 -0
- package/dist/graphics/index.d.ts +19 -0
- package/dist/graphics/index.d.ts.map +1 -0
- package/dist/graphics/index.js +13 -0
- package/dist/index.d.ts +18 -24
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +15 -15
- package/dist/input/gamepad-mapping.d.ts +134 -0
- package/dist/input/gamepad-mapping.d.ts.map +1 -0
- package/dist/input/gamepad-mapping.js +146 -0
- package/dist/input/gamepad.d.ts +74 -0
- package/dist/input/gamepad.d.ts.map +1 -0
- package/dist/input/gamepad.js +288 -0
- package/dist/input/index.d.ts +6 -0
- package/dist/input/index.d.ts.map +1 -0
- package/dist/input/index.js +1 -0
- package/dist/input/input.d.ts +76 -0
- package/dist/input/input.d.ts.map +1 -0
- package/dist/input/input.js +164 -0
- package/dist/input/keyboard.d.ts +15 -0
- package/dist/input/keyboard.d.ts.map +1 -0
- package/dist/input/keyboard.js +53 -0
- package/dist/input/mouse.d.ts +108 -0
- package/dist/input/mouse.d.ts.map +1 -0
- package/dist/input/mouse.js +241 -0
- package/dist/like.d.ts +80 -0
- package/dist/like.d.ts.map +1 -0
- package/dist/like.js +5 -0
- package/dist/math/index.d.ts +18 -0
- package/dist/math/index.d.ts.map +1 -0
- package/dist/math/index.js +17 -0
- package/dist/math/rect.d.ts +68 -0
- package/dist/math/rect.d.ts.map +1 -0
- package/dist/{core → math}/rect.js +48 -66
- package/dist/math/vector2.d.ts +133 -0
- package/dist/math/vector2.d.ts.map +1 -0
- package/dist/math/vector2.js +111 -0
- package/dist/prefab-scenes/index.d.ts +8 -0
- package/dist/prefab-scenes/index.d.ts.map +1 -0
- package/dist/prefab-scenes/index.js +7 -0
- package/dist/prefab-scenes/mapGamepad.d.ts +28 -0
- package/dist/prefab-scenes/mapGamepad.d.ts.map +1 -0
- package/dist/prefab-scenes/mapGamepad.js +181 -0
- package/dist/prefab-scenes/startScreen.d.ts +59 -0
- package/dist/prefab-scenes/startScreen.d.ts.map +1 -0
- package/dist/{scenes/startup.js → prefab-scenes/startScreen.js} +48 -8
- package/dist/scene.d.ts +104 -6
- package/dist/scene.d.ts.map +1 -1
- package/dist/scene.js +28 -1
- package/dist/timer/index.d.ts +2 -0
- package/dist/timer/index.d.ts.map +1 -0
- package/dist/timer/timer.d.ts +32 -0
- package/dist/timer/timer.d.ts.map +1 -0
- package/dist/{core → timer}/timer.js +19 -14
- package/package.json +27 -2
- package/dist/core/audio.d.ts.map +0 -1
- package/dist/core/canvas-config.d.ts +0 -22
- package/dist/core/canvas-config.d.ts.map +0 -1
- package/dist/core/canvas-config.js +0 -14
- package/dist/core/canvas-manager.d.ts +0 -32
- package/dist/core/canvas-manager.d.ts.map +0 -1
- package/dist/core/canvas-manager.js +0 -179
- package/dist/core/events.d.ts +0 -31
- package/dist/core/events.d.ts.map +0 -1
- package/dist/core/gamepad-buttons.d.ts +0 -23
- package/dist/core/gamepad-buttons.d.ts.map +0 -1
- package/dist/core/gamepad-buttons.js +0 -36
- package/dist/core/gamepad-mapping.d.ts +0 -19
- package/dist/core/gamepad-mapping.d.ts.map +0 -1
- package/dist/core/gamepad-mapping.js +0 -223
- package/dist/core/gamepad.d.ts +0 -61
- package/dist/core/gamepad.d.ts.map +0 -1
- package/dist/core/gamepad.js +0 -237
- package/dist/core/graphics.d.ts +0 -93
- package/dist/core/graphics.d.ts.map +0 -1
- package/dist/core/graphics.js +0 -289
- package/dist/core/input-state.d.ts +0 -14
- package/dist/core/input-state.d.ts.map +0 -1
- package/dist/core/input-state.js +0 -50
- package/dist/core/input.d.ts +0 -33
- package/dist/core/input.d.ts.map +0 -1
- package/dist/core/input.js +0 -117
- package/dist/core/keyboard.d.ts +0 -16
- package/dist/core/keyboard.d.ts.map +0 -1
- package/dist/core/keyboard.js +0 -83
- package/dist/core/like.d.ts +0 -59
- package/dist/core/like.d.ts.map +0 -1
- package/dist/core/mouse.d.ts +0 -45
- package/dist/core/mouse.d.ts.map +0 -1
- package/dist/core/mouse.js +0 -182
- package/dist/core/rect.d.ts +0 -26
- package/dist/core/rect.d.ts.map +0 -1
- package/dist/core/timer.d.ts +0 -18
- package/dist/core/timer.d.ts.map +0 -1
- package/dist/core/vector2.d.ts +0 -26
- package/dist/core/vector2.d.ts.map +0 -1
- package/dist/core/vector2.js +0 -105
- package/dist/scenes/startup.d.ts +0 -18
- package/dist/scenes/startup.d.ts.map +0 -1
- /package/dist/{core/events.js → audio/index.js} +0 -0
- /package/dist/{core/like.js → timer/index.js} +0 -0
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module GamepadMapping
|
|
3
|
+
*
|
|
4
|
+
* A database, generated on module load,
|
|
5
|
+
* which uses SDL's database to coerce
|
|
6
|
+
* browser APIs into physical gamepad button
|
|
7
|
+
* mappings.
|
|
8
|
+
*
|
|
9
|
+
* Browser API shortcomings:
|
|
10
|
+
* - No standard way of exposing vendor and product
|
|
11
|
+
* - Almost nothing is standard
|
|
12
|
+
* - Vendor and product alone doesn't suffice for GUID -- Different controllers have the same.
|
|
13
|
+
* - D-pads either get mapped to an axis (last pair of axes in Chromium) or buttons (Firefox).
|
|
14
|
+
* - Analog axes get mapped differently in Firefox and Chromium.
|
|
15
|
+
*
|
|
16
|
+
* How we overcome them:
|
|
17
|
+
* - We parse out vendor and product based on currently known formats.
|
|
18
|
+
* - We go with best-match and always fall back on manual mapping.
|
|
19
|
+
*/
|
|
20
|
+
/**
|
|
21
|
+
* ref: https://www.w3.org/TR/gamepad/#dfn-standard-gamepad
|
|
22
|
+
* note: `num` is only the corresponding number on standard mapping above.
|
|
23
|
+
*
|
|
24
|
+
* The point of the mapping system is to apply that _or_ non-standard mappings,
|
|
25
|
+
* Which are exceedingly common.
|
|
26
|
+
*/
|
|
27
|
+
const buttonMap = [
|
|
28
|
+
{ sdl: "a", like: "BBottom", num: 0, name: "Bottom Face Button" },
|
|
29
|
+
{ sdl: "b", like: "BRight", num: 1, name: "Right Face Button" },
|
|
30
|
+
{ sdl: "x", like: "BLeft", num: 2, name: "Left Face Button" },
|
|
31
|
+
{ sdl: "y", like: "BTop", num: 3, name: "Top Face Button" },
|
|
32
|
+
{ sdl: "leftshoulder", like: "L1", num: 4, name: "Left shoulder (front)" },
|
|
33
|
+
{ sdl: "rightshoulder", like: "R1", num: 5, name: "Right shoulder (front)" },
|
|
34
|
+
{ sdl: "lefttrigger", like: "L2", num: 6, name: "Left shoulder (rear)" },
|
|
35
|
+
{ sdl: "righttrigger", like: "R2", num: 7, name: "Right shoulder (rear)" },
|
|
36
|
+
{ sdl: "back", like: "MenuLeft", num: 8, name: "Left Menu Button" },
|
|
37
|
+
{ sdl: "start", like: "MenuRight", num: 9, name: "Right Menu Button" },
|
|
38
|
+
{ sdl: "leftstick", like: "LeftStick", num: 10, name: "Left Stick Button" },
|
|
39
|
+
{
|
|
40
|
+
sdl: "rightstick",
|
|
41
|
+
like: "RightStick",
|
|
42
|
+
num: 11,
|
|
43
|
+
name: "Right Stick Button",
|
|
44
|
+
},
|
|
45
|
+
{ sdl: "dpup", like: "Up", num: 12, name: "D-Pad Up" },
|
|
46
|
+
{ sdl: "dpdown", like: "Down", num: 13, name: "D-Pad Down" },
|
|
47
|
+
{ sdl: "dpleft", like: "Left", num: 14, name: "D-Pad Left" },
|
|
48
|
+
{ sdl: "dpright", like: "Right", num: 15, name: "D-Pad right" },
|
|
49
|
+
];
|
|
50
|
+
export const defaultMapping = (stickCount) => ({
|
|
51
|
+
buttons: {},
|
|
52
|
+
sticks: Array(stickCount / 2)
|
|
53
|
+
.fill(0)
|
|
54
|
+
.map((_, i) => [
|
|
55
|
+
{ index: i * 2, invert: false },
|
|
56
|
+
{ index: i * 2 + 1, invert: false },
|
|
57
|
+
]),
|
|
58
|
+
});
|
|
59
|
+
export const standardButtonMapping = () => Object.fromEntries(buttonMap.map(({ like, num }) => [num, like]));
|
|
60
|
+
export const allButtons = new Set(buttonMap.map(({ like }) => like));
|
|
61
|
+
export const fullButtonName = new Map(buttonMap.map(({ like, name }) => [like, name]));
|
|
62
|
+
export const mapStick = (gp, mapping) => {
|
|
63
|
+
return mapping.map((axis) => (axis.invert ? -1 : 1) * (gp.axes[axis.index] ?? 0));
|
|
64
|
+
};
|
|
65
|
+
//// ************* SDL Gamepad auto-binding system ******************* ////
|
|
66
|
+
// @ts-ignore
|
|
67
|
+
import gamecontrollerdb from "../gamecontrollerdb.txt?raw";
|
|
68
|
+
const sdlButtonSet = new Set(buttonMap.map(({ sdl }) => sdl));
|
|
69
|
+
const sdlToLikeMap = new Map(buttonMap.map(({ sdl, like }) => [sdl, like]));
|
|
70
|
+
const mappingDb = generateMappingDb(gamecontrollerdb);
|
|
71
|
+
function generateMappingDb(db) {
|
|
72
|
+
const dbEntries = db
|
|
73
|
+
.split("\n")
|
|
74
|
+
.map((l) => l.trim())
|
|
75
|
+
.filter((line) => line !== "" && !line.startsWith("#"))
|
|
76
|
+
.map(parseDbLine);
|
|
77
|
+
console.log(`[Gamepad] Parsed ${dbEntries.length} entries from DB`);
|
|
78
|
+
const mappingDb = Object.fromEntries(dbEntries);
|
|
79
|
+
console.log(`[Gamepad] Final entry count: ${Object.keys(mappingDb).length}`);
|
|
80
|
+
return mappingDb;
|
|
81
|
+
}
|
|
82
|
+
function parseDbLine(line) {
|
|
83
|
+
const [guid, name, ...mappings] = line.split(",");
|
|
84
|
+
mappings.pop();
|
|
85
|
+
const vpNum = 0x1 * parseInt(guid.substring(16, 18), 16) +
|
|
86
|
+
0x100 * parseInt(guid.substring(18, 20), 16) +
|
|
87
|
+
0x10000 * parseInt(guid.substring(8, 10), 16) +
|
|
88
|
+
0x1000000 * parseInt(guid.substring(10, 12), 16);
|
|
89
|
+
const mapping = Object.fromEntries(mappings
|
|
90
|
+
.map((s) => {
|
|
91
|
+
const [sdl, bname] = s.split(":");
|
|
92
|
+
const browserIndex = sdlRawButtonToBrowser(bname);
|
|
93
|
+
return (browserIndex !== undefined &&
|
|
94
|
+
sdlButtonSet.has(sdl) && [browserIndex, sdlToLikeMap.get(sdl)]);
|
|
95
|
+
})
|
|
96
|
+
.filter((v) => !!v));
|
|
97
|
+
return [vpNum, { name, mapping }];
|
|
98
|
+
}
|
|
99
|
+
function sdlRawButtonToBrowser(btn) {
|
|
100
|
+
return btn.startsWith("b")
|
|
101
|
+
? Number(btn.substring(1))
|
|
102
|
+
: btn.startsWith("h")
|
|
103
|
+
? { 1: 12, 2: 13, 4: 14, 8: 15 }[Number(btn.substring(3))]
|
|
104
|
+
: undefined;
|
|
105
|
+
}
|
|
106
|
+
export function getSdlMapping(gamepad) {
|
|
107
|
+
const parsed = tryParseId(gamepad.id);
|
|
108
|
+
if (parsed) {
|
|
109
|
+
const [vendorStr, productStr, nameStr] = parsed;
|
|
110
|
+
const vendor = parseInt(vendorStr, 16);
|
|
111
|
+
const product = parseInt(productStr, 16);
|
|
112
|
+
const name = nameStr.trim();
|
|
113
|
+
const mapping = mappingDb[vendor * 0x10000 + product];
|
|
114
|
+
if (mapping) {
|
|
115
|
+
console.log(`[Gamepad] Found SDL db mapping for '${name}'`);
|
|
116
|
+
return {
|
|
117
|
+
vendor,
|
|
118
|
+
product,
|
|
119
|
+
name,
|
|
120
|
+
sdlName: mapping.name,
|
|
121
|
+
mapping: mapping.mapping,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
console.log(`[Gamepad] No SDL db mapping found for '${gamepad.id}.`);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
console.log(`[Gamepad] Failed to parse id: ${gamepad.id}. Please report this bug with the name of your web browser.`);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
function tryParseId(id) {
|
|
133
|
+
const infoC = id.match(/^([^(]+)\(Vendor: ([0-9a-f]+) Product: ([0-9a-f]+)/i);
|
|
134
|
+
if (infoC) {
|
|
135
|
+
// chrome pattern: Name(Vendor: VEND Product: PROD)
|
|
136
|
+
const [, name, vendor, product] = infoC;
|
|
137
|
+
return [vendor, product, name.trim()];
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
// firefox pattern: VEND-PROD-Name
|
|
141
|
+
const infoF = id.split("-");
|
|
142
|
+
if (infoF.length == 3) {
|
|
143
|
+
return infoF;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { GamepadMapping, LikeButton } from './gamepad-mapping';
|
|
2
|
+
import { type LikeGamepadEvent } from '../events';
|
|
3
|
+
import { Vector2 } from '../math/vector2';
|
|
4
|
+
import { EngineProps } from '../engine';
|
|
5
|
+
/** A selector for a gamepad. */
|
|
6
|
+
export type GamepadTarget = number | "any";
|
|
7
|
+
/** LIKE Gamepad Wrapper
|
|
8
|
+
*
|
|
9
|
+
* - Allows events/callbacks to be sent from joy buttons.
|
|
10
|
+
* - Can track if any gamepad has a button pressed / just pressed.
|
|
11
|
+
* - Remaps raw input numbers to readable strings -- by default using SDL database.
|
|
12
|
+
*
|
|
13
|
+
* If you're planning on supporting gamepads, please include a
|
|
14
|
+
* way to generate {@link GamepadMapping} and set it with {@link Gamepad.setMapping}.
|
|
15
|
+
*
|
|
16
|
+
* If you don't want to make your own, take a look at `prefab-scenes/mapGamepad`.
|
|
17
|
+
*/
|
|
18
|
+
export declare class Gamepad {
|
|
19
|
+
private dispatch;
|
|
20
|
+
private gamepads;
|
|
21
|
+
private autoLoadMapping;
|
|
22
|
+
constructor(props: EngineProps<LikeGamepadEvent>);
|
|
23
|
+
private onGamepadConnected;
|
|
24
|
+
/**
|
|
25
|
+
* @private
|
|
26
|
+
* Called by the engine every frame.
|
|
27
|
+
*/
|
|
28
|
+
update(): void;
|
|
29
|
+
/**
|
|
30
|
+
*
|
|
31
|
+
* @param target Which controller?
|
|
32
|
+
* @returns all of the sticks. Convention is 0 = left, 1 = right.
|
|
33
|
+
*/
|
|
34
|
+
getSticks(target: number): Vector2[];
|
|
35
|
+
fullButtonName(name: LikeButton): string;
|
|
36
|
+
private checkButton;
|
|
37
|
+
/** Check if a gamepad button is down. */
|
|
38
|
+
isDown(target: GamepadTarget, button: LikeButton | number): boolean | undefined;
|
|
39
|
+
/**
|
|
40
|
+
* Returns true for only one frame/update if a button is pressed.
|
|
41
|
+
* Considered an alternative to `like.gamepadpressed`.
|
|
42
|
+
*/
|
|
43
|
+
justPressed(target: GamepadTarget, button: LikeButton | number): boolean | undefined;
|
|
44
|
+
/**
|
|
45
|
+
* Get a controller mapping.
|
|
46
|
+
* Note that modifying this mapping in place will modify the target controller.
|
|
47
|
+
* However, use `setMapping` to finalize the mapping.
|
|
48
|
+
*/
|
|
49
|
+
getMapping(index: number): GamepadMapping | undefined;
|
|
50
|
+
/**
|
|
51
|
+
* Set the mapping for a particular controller.
|
|
52
|
+
*
|
|
53
|
+
* Set `save = false` if you don't want this written into localstorage.
|
|
54
|
+
*/
|
|
55
|
+
setMapping(index: number, mapping: GamepadMapping, save?: boolean): void;
|
|
56
|
+
/**
|
|
57
|
+
* Get saved mapping from db, if it exists.
|
|
58
|
+
*/
|
|
59
|
+
loadMapping(index: number): GamepadMapping | undefined;
|
|
60
|
+
/**
|
|
61
|
+
* Save a mapping to persistant storage
|
|
62
|
+
*/
|
|
63
|
+
saveMapping(index: number, mapping: GamepadMapping): void;
|
|
64
|
+
/**
|
|
65
|
+
* Enable automatically loading mappings.
|
|
66
|
+
*
|
|
67
|
+
* When a gamepad with a known (to this system) ID is plugged in,
|
|
68
|
+
* this will load the previously saved mapping.
|
|
69
|
+
*
|
|
70
|
+
* @param enable
|
|
71
|
+
*/
|
|
72
|
+
enableAutoLoadMapping(enable: boolean): void;
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=gamepad.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gamepad.d.ts","sourceRoot":"","sources":["../../src/input/gamepad.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkC,cAAc,EAAiB,UAAU,EAAY,MAAM,mBAAmB,CAAC;AACxH,OAAO,EAAmB,KAAK,gBAAgB,EAAE,MAAM,WAAW,CAAC;AACnE,OAAO,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAC1C,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAExC,gCAAgC;AAChC,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,KAAK,CAAC;AAE3C;;;;;;;;;;GAUG;AACH,qBAAa,OAAO;IAClB,OAAO,CAAC,QAAQ,CAA+B;IAC/C,OAAO,CAAC,QAAQ,CAAoC;IACpD,OAAO,CAAC,eAAe,CAAQ;gBAEnB,KAAK,EAAE,WAAW,CAAC,gBAAgB,CAAC;IAgChD,OAAO,CAAC,kBAAkB;IA+B1B;;;OAGG;IACH,MAAM,IAAI,IAAI;IAId;;;;OAIG;IACH,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,EAAE;IAQpC,cAAc,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM;IAIxC,OAAO,CAAC,WAAW;IAYnB,yCAAyC;IACzC,MAAM,CACJ,MAAM,EAAE,aAAa,EACrB,MAAM,EAAE,UAAU,GAAG,MAAM,GAC1B,OAAO,GAAG,SAAS;IAItB;;;OAGG;IACH,WAAW,CACT,MAAM,EAAE,aAAa,EACrB,MAAM,EAAE,UAAU,GAAG,MAAM,GAC1B,OAAO,GAAG,SAAS;IAItB;;;;OAIG;IACH,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS;IAIrD;;;;OAIG;IACH,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,EAAE,IAAI,UAAO;IAU9D;;OAEG;IACH,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS;IAYtD;;OAEG;IACH,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc;IASlD;;;;;;;OAOG;IACH,qBAAqB,CAAC,MAAM,EAAE,OAAO;CAGtC"}
|
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
import { defaultMapping, fullButtonName, getSdlMapping, mapStick } from './gamepad-mapping';
|
|
2
|
+
/** LIKE Gamepad Wrapper
|
|
3
|
+
*
|
|
4
|
+
* - Allows events/callbacks to be sent from joy buttons.
|
|
5
|
+
* - Can track if any gamepad has a button pressed / just pressed.
|
|
6
|
+
* - Remaps raw input numbers to readable strings -- by default using SDL database.
|
|
7
|
+
*
|
|
8
|
+
* If you're planning on supporting gamepads, please include a
|
|
9
|
+
* way to generate {@link GamepadMapping} and set it with {@link Gamepad.setMapping}.
|
|
10
|
+
*
|
|
11
|
+
* If you don't want to make your own, take a look at `prefab-scenes/mapGamepad`.
|
|
12
|
+
*/
|
|
13
|
+
export class Gamepad {
|
|
14
|
+
constructor(props) {
|
|
15
|
+
Object.defineProperty(this, "dispatch", {
|
|
16
|
+
enumerable: true,
|
|
17
|
+
configurable: true,
|
|
18
|
+
writable: true,
|
|
19
|
+
value: void 0
|
|
20
|
+
});
|
|
21
|
+
Object.defineProperty(this, "gamepads", {
|
|
22
|
+
enumerable: true,
|
|
23
|
+
configurable: true,
|
|
24
|
+
writable: true,
|
|
25
|
+
value: {}
|
|
26
|
+
});
|
|
27
|
+
Object.defineProperty(this, "autoLoadMapping", {
|
|
28
|
+
enumerable: true,
|
|
29
|
+
configurable: true,
|
|
30
|
+
writable: true,
|
|
31
|
+
value: true
|
|
32
|
+
});
|
|
33
|
+
this.dispatch = props.dispatch;
|
|
34
|
+
const { abort } = props;
|
|
35
|
+
// Register event listeners
|
|
36
|
+
window.addEventListener("gamepadconnected", this.onGamepadConnected.bind(this), {
|
|
37
|
+
signal: abort,
|
|
38
|
+
});
|
|
39
|
+
window.addEventListener("gamepaddisconnected", (ev) => {
|
|
40
|
+
console.log(`[Gamepad] Disconnected ${ev.gamepad.id}`);
|
|
41
|
+
delete this.gamepads[ev.gamepad.index];
|
|
42
|
+
this.dispatch("gamepaddisconnected", [ev.gamepad.index]);
|
|
43
|
+
}, { signal: abort });
|
|
44
|
+
window.addEventListener("blur", () => {
|
|
45
|
+
for (const gps of Object.values(this.gamepads)) {
|
|
46
|
+
gps.clear();
|
|
47
|
+
}
|
|
48
|
+
}, { signal: abort });
|
|
49
|
+
}
|
|
50
|
+
onGamepadConnected(ev) {
|
|
51
|
+
const gps = new GamepadState(ev.gamepad.index);
|
|
52
|
+
this.gamepads[ev.gamepad.index] = gps;
|
|
53
|
+
console.log(`[Gamepad] Connected ${ev.gamepad.id}. buttons: ${ev.gamepad.buttons.length}, axes: ${ev.gamepad.axes.length}`);
|
|
54
|
+
const mapping = this.loadMapping(ev.gamepad.index);
|
|
55
|
+
if (this.autoLoadMapping && mapping) {
|
|
56
|
+
gps.mapping = mapping;
|
|
57
|
+
console.log(`[Gamepad] Applied presaved mapping.`);
|
|
58
|
+
}
|
|
59
|
+
else if (ev.gamepad.mapping == 'standard') {
|
|
60
|
+
gps.mapping = defaultMapping(ev.gamepad.axes.length / 2);
|
|
61
|
+
console.log(`Loaded standard mapping.`);
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
const sdlMapping = getSdlMapping(ev.gamepad);
|
|
65
|
+
if (sdlMapping) {
|
|
66
|
+
gps.mapping.buttons = sdlMapping.mapping;
|
|
67
|
+
console.log(`[Gamepad] Connected, applied SDL database mapping.`);
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
console.log(`[Gamepad] Could not find mapping for gamepad. Consider remapping it.`);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
this.dispatch("gamepadconnected", [ev.gamepad.index]);
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* @private
|
|
77
|
+
* Called by the engine every frame.
|
|
78
|
+
*/
|
|
79
|
+
update() {
|
|
80
|
+
Object.values(this.gamepads).forEach((gp) => gp.update(this.dispatch));
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
*
|
|
84
|
+
* @param target Which controller?
|
|
85
|
+
* @returns all of the sticks. Convention is 0 = left, 1 = right.
|
|
86
|
+
*/
|
|
87
|
+
getSticks(target) {
|
|
88
|
+
const gp = this.gamepads[target];
|
|
89
|
+
if (gp) {
|
|
90
|
+
return gp.getSticks();
|
|
91
|
+
}
|
|
92
|
+
return [];
|
|
93
|
+
}
|
|
94
|
+
fullButtonName(name) {
|
|
95
|
+
return fullButtonName.get(name) ?? name;
|
|
96
|
+
}
|
|
97
|
+
checkButton(target, button, mode) {
|
|
98
|
+
if (target == "any") {
|
|
99
|
+
return Object.values(this.gamepads).some((gp) => gp[mode](button));
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
return this.gamepads[target]?.[mode](button);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
/** Check if a gamepad button is down. */
|
|
106
|
+
isDown(target, button) {
|
|
107
|
+
return this.checkButton(target, button, "isDown");
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Returns true for only one frame/update if a button is pressed.
|
|
111
|
+
* Considered an alternative to `like.gamepadpressed`.
|
|
112
|
+
*/
|
|
113
|
+
justPressed(target, button) {
|
|
114
|
+
return this.checkButton(target, button, "justPressed");
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Get a controller mapping.
|
|
118
|
+
* Note that modifying this mapping in place will modify the target controller.
|
|
119
|
+
* However, use `setMapping` to finalize the mapping.
|
|
120
|
+
*/
|
|
121
|
+
getMapping(index) {
|
|
122
|
+
return this.gamepads[index]?.mapping;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Set the mapping for a particular controller.
|
|
126
|
+
*
|
|
127
|
+
* Set `save = false` if you don't want this written into localstorage.
|
|
128
|
+
*/
|
|
129
|
+
setMapping(index, mapping, save = true) {
|
|
130
|
+
const gp = this.gamepads[index];
|
|
131
|
+
if (gp) {
|
|
132
|
+
gp.mapping = mapping;
|
|
133
|
+
if (save) {
|
|
134
|
+
this.saveMapping(index, mapping);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Get saved mapping from db, if it exists.
|
|
140
|
+
*/
|
|
141
|
+
loadMapping(index) {
|
|
142
|
+
const gp = navigator.getGamepads()?.[index];
|
|
143
|
+
if (gp) {
|
|
144
|
+
const path = getLocalstoragePath(gp);
|
|
145
|
+
console.log(`[Gamepad] Found saved mapping for ${gp.id}.`);
|
|
146
|
+
const item = localStorage.getItem(path);
|
|
147
|
+
if (item) {
|
|
148
|
+
return JSON.parse(item);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Save a mapping to persistant storage
|
|
154
|
+
*/
|
|
155
|
+
saveMapping(index, mapping) {
|
|
156
|
+
const gp = navigator.getGamepads()?.[index];
|
|
157
|
+
if (gp) {
|
|
158
|
+
const path = getLocalstoragePath(gp);
|
|
159
|
+
console.log(`[Gamepad] Saved mapping ${path} to localStorage.`);
|
|
160
|
+
localStorage.setItem(path, JSON.stringify(mapping));
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Enable automatically loading mappings.
|
|
165
|
+
*
|
|
166
|
+
* When a gamepad with a known (to this system) ID is plugged in,
|
|
167
|
+
* this will load the previously saved mapping.
|
|
168
|
+
*
|
|
169
|
+
* @param enable
|
|
170
|
+
*/
|
|
171
|
+
enableAutoLoadMapping(enable) {
|
|
172
|
+
this.autoLoadMapping = enable;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
function getLocalstoragePath(gamepad) {
|
|
176
|
+
return `${gamepad.axes.length}A${gamepad.buttons.length}B${gamepad.id}`;
|
|
177
|
+
}
|
|
178
|
+
const maxButtons = 64;
|
|
179
|
+
/** Internal class: Using it externally could result
|
|
180
|
+
* in a gamepad being disconnected and still trying to maintain
|
|
181
|
+
* its state.
|
|
182
|
+
*/
|
|
183
|
+
class GamepadState {
|
|
184
|
+
constructor(index) {
|
|
185
|
+
Object.defineProperty(this, "index", {
|
|
186
|
+
enumerable: true,
|
|
187
|
+
configurable: true,
|
|
188
|
+
writable: true,
|
|
189
|
+
value: index
|
|
190
|
+
});
|
|
191
|
+
Object.defineProperty(this, "mapping", {
|
|
192
|
+
enumerable: true,
|
|
193
|
+
configurable: true,
|
|
194
|
+
writable: true,
|
|
195
|
+
value: void 0
|
|
196
|
+
});
|
|
197
|
+
Object.defineProperty(this, "downNums", {
|
|
198
|
+
enumerable: true,
|
|
199
|
+
configurable: true,
|
|
200
|
+
writable: true,
|
|
201
|
+
value: []
|
|
202
|
+
});
|
|
203
|
+
Object.defineProperty(this, "lastDownNums", {
|
|
204
|
+
enumerable: true,
|
|
205
|
+
configurable: true,
|
|
206
|
+
writable: true,
|
|
207
|
+
value: []
|
|
208
|
+
});
|
|
209
|
+
Object.defineProperty(this, "down", {
|
|
210
|
+
enumerable: true,
|
|
211
|
+
configurable: true,
|
|
212
|
+
writable: true,
|
|
213
|
+
value: {}
|
|
214
|
+
});
|
|
215
|
+
Object.defineProperty(this, "lastDown", {
|
|
216
|
+
enumerable: true,
|
|
217
|
+
configurable: true,
|
|
218
|
+
writable: true,
|
|
219
|
+
value: {}
|
|
220
|
+
});
|
|
221
|
+
const gp = navigator.getGamepads()[this.index];
|
|
222
|
+
this.mapping = defaultMapping(gp.axes.length);
|
|
223
|
+
}
|
|
224
|
+
isDown(button) {
|
|
225
|
+
return typeof (button) == "number" ? !!this.downNums[button] : !!this.down[button];
|
|
226
|
+
}
|
|
227
|
+
justPressed(button) {
|
|
228
|
+
return typeof (button) == "number" ?
|
|
229
|
+
(!!this.downNums[button] && !this.lastDownNums[button]) :
|
|
230
|
+
(!!this.down[button] && !this.lastDown[button]);
|
|
231
|
+
}
|
|
232
|
+
map(button) {
|
|
233
|
+
return (this.mapping.buttons[button] ??
|
|
234
|
+
(button < maxButtons
|
|
235
|
+
? `Button${button}`
|
|
236
|
+
: `Axis${Math.floor((button - maxButtons) / 2)}${button % 2 ? "-" : "+"}`));
|
|
237
|
+
}
|
|
238
|
+
update(dispatch) {
|
|
239
|
+
const gp = navigator.getGamepads()[this.index];
|
|
240
|
+
if (!gp)
|
|
241
|
+
return;
|
|
242
|
+
[this.downNums, this.lastDownNums] = [this.lastDownNums, this.downNums];
|
|
243
|
+
for (const k in this.downNums) {
|
|
244
|
+
delete this.downNums[Number(k)];
|
|
245
|
+
}
|
|
246
|
+
gp.buttons.forEach((btn, i) => {
|
|
247
|
+
if (btn.pressed) {
|
|
248
|
+
this.downNums[i] = true;
|
|
249
|
+
}
|
|
250
|
+
});
|
|
251
|
+
gp.axes.forEach((axis, i) => {
|
|
252
|
+
const pos = Math.round(axis);
|
|
253
|
+
if (pos == 0)
|
|
254
|
+
return;
|
|
255
|
+
const index = 64 + i * 2 + (pos == -1 ? 1 : 0);
|
|
256
|
+
this.downNums[index] = true;
|
|
257
|
+
});
|
|
258
|
+
[this.down, this.lastDown] = [this.lastDown, this.down];
|
|
259
|
+
for (const k in this.down) {
|
|
260
|
+
delete this.down[k];
|
|
261
|
+
}
|
|
262
|
+
for (const i in this.downNums) {
|
|
263
|
+
const name = this.map(Number(i));
|
|
264
|
+
this.down[name] = true;
|
|
265
|
+
if (!this.lastDownNums[Number(i)]) {
|
|
266
|
+
dispatch("gamepadpressed", [this.index, name, Number(i)]);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
for (const i in this.lastDownNums) {
|
|
270
|
+
if (!this.downNums[Number(i)]) {
|
|
271
|
+
const name = this.map(Number(i));
|
|
272
|
+
dispatch("gamepadreleased", [this.index, name, Number(i)]);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
getSticks() {
|
|
277
|
+
const gp = navigator.getGamepads()[this.index];
|
|
278
|
+
return this.mapping.sticks.map((stick) => mapStick(gp, stick));
|
|
279
|
+
}
|
|
280
|
+
clear() {
|
|
281
|
+
for (const k in this.lastDown) {
|
|
282
|
+
delete this.lastDown[k];
|
|
283
|
+
}
|
|
284
|
+
for (const k in this.down) {
|
|
285
|
+
delete this.down[k];
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export type { Input, InputType, InputBinding, } from "./input";
|
|
2
|
+
export type { GamepadTarget, Gamepad, } from "./gamepad";
|
|
3
|
+
export type { LikeButton, GamepadMapping, StickMapping, StickAxisMapping, } from "./gamepad-mapping";
|
|
4
|
+
export { defaultMapping, allButtons } from "./gamepad-mapping";
|
|
5
|
+
export type { MouseSetMode, Mouse, } from "./mouse";
|
|
6
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/input/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,KAAK,EACL,SAAS,EACT,YAAY,GACb,MAAM,SAAS,CAAC;AAEjB,YAAY,EACV,aAAa,EACb,OAAO,GACR,MAAM,WAAW,CAAA;AAElB,YAAY,EACV,UAAU,EACV,cAAc,EACd,YAAY,EACZ,gBAAgB,GACjB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAE/D,YAAY,EACV,YAAY,EACZ,KAAK,GACN,MAAM,SAAS,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { defaultMapping, allButtons } from "./gamepad-mapping";
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import type { Keyboard } from './keyboard';
|
|
2
|
+
import type { Mouse } from './mouse';
|
|
3
|
+
import { Gamepad, GamepadTarget } from './gamepad';
|
|
4
|
+
import { LikeButton } from './gamepad-mapping';
|
|
5
|
+
import { MouseButton, Dispatcher } from '../events';
|
|
6
|
+
export type InputType = InputBinding['type'];
|
|
7
|
+
export type InputBinding = {
|
|
8
|
+
type: 'keyboard';
|
|
9
|
+
scancode: string;
|
|
10
|
+
} | {
|
|
11
|
+
type: 'mouse';
|
|
12
|
+
button: MouseButton;
|
|
13
|
+
} | {
|
|
14
|
+
type: 'gamepad';
|
|
15
|
+
gamepad: GamepadTarget;
|
|
16
|
+
button: LikeButton;
|
|
17
|
+
};
|
|
18
|
+
export declare class Input {
|
|
19
|
+
private dispatch;
|
|
20
|
+
private currState;
|
|
21
|
+
private prevState;
|
|
22
|
+
private actionTable;
|
|
23
|
+
private keyboard;
|
|
24
|
+
private mouse;
|
|
25
|
+
private gamepad;
|
|
26
|
+
constructor(dispatch: Dispatcher<'actionpressed' | 'actionreleased'>, deps: {
|
|
27
|
+
keyboard: Keyboard;
|
|
28
|
+
mouse: Mouse;
|
|
29
|
+
gamepad: Gamepad;
|
|
30
|
+
});
|
|
31
|
+
/**
|
|
32
|
+
* This is the easiest way to set-and-forget input => action mapping.
|
|
33
|
+
*
|
|
34
|
+
* Or, it's a helper to remove actions -- `setAction(action)`
|
|
35
|
+
* will simply clear the action away.
|
|
36
|
+
*
|
|
37
|
+
* For input strings:
|
|
38
|
+
* - Mouse is `MouseLeft`, `MouseRight`, or `MouseMiddle`.
|
|
39
|
+
* - Joypad is 'Left', 'L1', or any joypad button. {@link LikeButton}
|
|
40
|
+
* - Keyboard is the name of scancodes, which are based on key positions. Choose from a subset of portable, web-safe scancodes:
|
|
41
|
+
* - Alphabetical: `KeyA`, `KeyB`, ...
|
|
42
|
+
* - Numeric: `Digit0`, `Digit1`, ...
|
|
43
|
+
* - `ArrowLeft`, `ArrowRight`, `ArrowUp`, `ArrowDown`
|
|
44
|
+
* - `ShiftLeft`, `ShiftRight`
|
|
45
|
+
* - `Minus`
|
|
46
|
+
* - `Equal` (also has a plus sign)
|
|
47
|
+
* - `BracketLeft` and `BracketRight`
|
|
48
|
+
* - `Semicolon`
|
|
49
|
+
* - `Quote`
|
|
50
|
+
* - `Backquote` (also has tilde)
|
|
51
|
+
* - `Backslash`
|
|
52
|
+
* - `Comma`
|
|
53
|
+
* - `Period`
|
|
54
|
+
* - `Slash`
|
|
55
|
+
* - `Backspace`
|
|
56
|
+
* - `Enter`
|
|
57
|
+
*
|
|
58
|
+
* @param action
|
|
59
|
+
* @param inputs
|
|
60
|
+
*/
|
|
61
|
+
setAction(action: string, inputs?: (string | InputBinding)[]): void;
|
|
62
|
+
/**
|
|
63
|
+
* Map a single input onto an action.
|
|
64
|
+
*/
|
|
65
|
+
appendToAction(action: string, input: InputBinding | string): void;
|
|
66
|
+
/**
|
|
67
|
+
* Get the mapping entry for a specific action.
|
|
68
|
+
*/
|
|
69
|
+
getActionMapping(action: string): InputBinding[];
|
|
70
|
+
isDown(action: string): boolean;
|
|
71
|
+
justPressed(action: string): boolean;
|
|
72
|
+
justReleased(action: string): boolean;
|
|
73
|
+
update(): void;
|
|
74
|
+
private isBindingActive;
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=input.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"input.d.ts","sourceRoot":"","sources":["../../src/input/input.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AACnD,OAAO,EAAc,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC3D,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAEpD,MAAM,MAAM,SAAS,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC;AAC7C,MAAM,MAAM,YAAY,GACpB;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GACtC;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,WAAW,CAAA;CAAE,GACtC;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,OAAO,EAAE,aAAa,CAAC;IAAC,MAAM,EAAE,UAAU,CAAA;CAAE,CAAC;AAEpE,qBAAa,KAAK;IASd,OAAO,CAAC,QAAQ;IARlB,OAAO,CAAC,SAAS,CAAqB;IACtC,OAAO,CAAC,SAAS,CAAqB;IACtC,OAAO,CAAC,WAAW,CAAsC;IACzD,OAAO,CAAC,QAAQ,CAAW;IAC3B,OAAO,CAAC,KAAK,CAAQ;IACrB,OAAO,CAAC,OAAO,CAAU;gBAGf,QAAQ,EAAE,UAAU,CAAC,eAAe,GAAG,gBAAgB,CAAC,EAChE,IAAI,EAAE;QACJ,QAAQ,EAAE,QAAQ,CAAC;QACnB,KAAK,EAAE,KAAK,CAAC;QACb,OAAO,EAAE,OAAO,CAAC;KAClB;IAOH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA6BG;IACH,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,GAAE,CAAC,MAAM,GAAG,YAAY,CAAC,EAAO,GAAG,IAAI;IAUvE;;OAEG;IACH,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,GAAG,MAAM;IAM3D;;OAEG;IACH,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,YAAY,EAAE;IAIhD,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO;IAM/B,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO;IAIpC,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO;IAIrC,MAAM;IAiBN,OAAO,CAAC,eAAe;CAaxB"}
|