like2d 2.12.1 → 2.13.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.
@@ -0,0 +1,368 @@
1
+ /**
2
+ * Scenes are a modular component of LÏKE based on setting `like.handleEvent`.
3
+ * The scene system is simple and powerful, once understood.
4
+ *
5
+ * For devs using the built-in callback pattern, they're an easy way
6
+ * to stack functionality on to the current project such as
7
+ * gamepad mapping or debug overlays.
8
+ *
9
+ * For multi-scene games, they codify a common state-management pattern based
10
+ * on switching between (or nesting) event handler callbacks. It's
11
+ * a lot better than switch-casing on each handler, or manually setting/clearing
12
+ * handler functions on each transition.
13
+ *
14
+ * Using scenes for your game also replaces the need to pass around global `like`
15
+ * or `sceneManager` wherever it is used.
16
+ *
17
+ * ## Jump in
18
+ *
19
+ * Get started: {@link SceneManager}
20
+ *
21
+ * Make your own scene: {@link Scene}
22
+ *
23
+ * Check out built-in utility scenes:
24
+ * - {@link scene/prefab/mapGamepad}
25
+ * - {@link scene/prefab/startScreen}
26
+ *
27
+ * @module scene
28
+ */
29
+ import type { Like, LikeHandlers } from '../like';
30
+ /**
31
+ * ## Creating your own scenes
32
+ *
33
+ * Scenes are a factory function that receives `Like` and `SceneManager`
34
+ * and returns a {@link LikeHandlers | scene instance with event handlers}.
35
+ *
36
+ * ## Examples
37
+ *
38
+ * Minimal usage:
39
+ * ```typescript
40
+ * const gameOver: Scene = (like, scenes) => ({
41
+ * titleCard: like.gfx.newImage(path);
42
+ * spawnTime: like.timer.getTime();
43
+ * draw() {
44
+ * // draw 'game over' over the parent scene
45
+ * like.gfx.draw(this.titleCard);
46
+ * scenes.get(-2)?.draw();
47
+ * }
48
+ * update() {
49
+ * // back to title screen after 3 seconds
50
+ // (assuming title screen is using callback pattern)
51
+ * if (like.timer.getTime() > spawnTime + 3) {
52
+ * while(scenes.pop());
53
+ * }
54
+ * }
55
+ * })
56
+ * ```
57
+ *
58
+ * For configurable scenes, it is reccommended to use a function
59
+ * that returns a Scene.
60
+ * ```typescript
61
+ * const myScene = (options: { speed: number }): Scene =>
62
+ * (like: Like, scenes: SceneManager) => {
63
+ *
64
+ * const playerImage = like.gfx.newImage('player.png');
65
+ * let x = 0, y = 0;
66
+ *
67
+ * return {
68
+ * update(dt) {
69
+ * x += options.speed * dt;
70
+ * },
71
+ * draw() {
72
+ * like.gfx.draw(playerImage, [x, y]);
73
+ * }
74
+ * mousepressed() {
75
+ * // exit this scene when user clicks
76
+ * scene.pop();
77
+ * }
78
+ * };
79
+ * };
80
+ * ```
81
+ *
82
+ * Of course, a class pattern is also possible.
83
+ * ```typescript
84
+ * class ThingDoer extends SceneInstance {
85
+ * constructor(like, scenes) {...}
86
+ * ...
87
+ * }
88
+ *
89
+ * const thingDoerScene: Scene =
90
+ * (like, scenes) => new ThingDoer(like, scenes);
91
+ * ```
92
+ * Or a configurable class:
93
+ * ```typescript
94
+ * class ThingDoer extends SceneInstance {
95
+ * constructor(like, scenes, options) {...}
96
+ * ...
97
+ * }
98
+ *
99
+ * const thingDoerScene = (options): Scene =>
100
+ * (like, scenes) => new ThingDoer(like, scenes, options);
101
+ * ```
102
+ *
103
+ * ## Converting from Callbacks
104
+ *
105
+ * When converting from global callbacks to a scene:
106
+ *
107
+ * ```typescript
108
+ * // Before (callbacks)
109
+ * like.update = function(dt) { player.update(dt); }
110
+ * like.draw = () => { player.draw(like.gfx); }
111
+ *
112
+ * // After (scene)
113
+ * scenes.set((like, scenes) => {
114
+ * const scene: SceneInstance = {}
115
+ * scene.update = function (dt) { player.update(dt); },
116
+ * scene.draw = () => { player.draw(like.gfx); }
117
+ * return scene;
118
+ * });
119
+ * ```
120
+ * ## Composing scenes
121
+ *
122
+ * Just like the `like` object, scenes have handleEvent on them.
123
+ * So, you could layer them like this, for example:
124
+ *
125
+ * ```typescript
126
+ * // Composing scenes lets us know about the children.
127
+ * // This allows communication, for example:
128
+ * type UISceneInstance = SceneInstance & {
129
+ * // Sending events to child scene
130
+ * buttonClicked(name: string): void;
131
+ * // Getting info from child scene
132
+ * getStatus(): string;
133
+ * };
134
+ * type UIScene = SceneEx<UISceneInstance>;
135
+ *
136
+ * const uiScene = (game: UIScene): Scene =>
137
+ * (like, scenes) => {
138
+ * const childScene = scenes.instantiate(game);
139
+ * return {
140
+ * handleEvent(event) {
141
+ * // Block mouse events in order to create a top bar.
142
+ * // Otherwise, propogate them.
143
+ * const mouseY = like.mouse.getPosition()[1];
144
+ * if (!event.type.startsWith('mouse') || mouseY > 100) {
145
+ * // Use likeDispatch so that nested handleEvent can fire,
146
+ * // if relevant.
147
+ * likeDispatch(childScene, event);
148
+ * }
149
+ * // Then, call my own callbacks.
150
+ * // Using likeDispatch here will result in an infinite loop.
151
+ * callOwnHandlers(this, event);
152
+ * },
153
+ * mousepressed(pos) {
154
+ * if (buttonClicked(pos)) {
155
+ * childScene.buttonClicked('statusbar')
156
+ * }
157
+ * },
158
+ * draw() {
159
+ * drawStatus(like, childScene.getStatus());
160
+ * }
161
+ * };
162
+ * }
163
+ *
164
+ * const gameScene = (level: number): UIScene =>
165
+ * (like, scene) => ({
166
+ * update() { ... },
167
+ * draw() { ... },
168
+ * // mandatory UI methods from interface
169
+ * buttonClicked(name) {
170
+ * doSomething(),
171
+ * },
172
+ * getStatus() {
173
+ * return 'all good!';
174
+ * }
175
+ * });
176
+ *
177
+ * like.pushScene(uiScene(gameScene);
178
+ * ```
179
+ *
180
+ * The main advance of composing scenes versus the stack-overlay
181
+ * technique is that the parent scene knows about its child.
182
+ * Because there's a **known interface**, the two scenes
183
+ * can communicate.
184
+ *
185
+ * This makes it perfect for reusable UI,
186
+ * level editors, debug viewers, and more.
187
+ *
188
+ * ## Overlay scenes
189
+ *
190
+ * You might assume that the purpose of a scene stack is
191
+ * visual: first push the BG, then the FG, etc.
192
+ *
193
+ * Actually, composing scenes (above) is a
194
+ * better pattern for that, since it's both explicit
195
+ * _and_ the parent can have a known interface on its child.
196
+ * Here, the **upper** scene only knows that the
197
+ * **lower** scene _is_ a scene.
198
+ *
199
+ * That's the tradeoff. Overlay scenes are good for things
200
+ * like pause screens or gamepad overlays. Anything where
201
+ * the upper doesn't care _what_ the lower is, and where
202
+ * the upper scene should be easily addable/removable.
203
+ *
204
+ * Using `like.getScene(-2)`, the overlay scene can see
205
+ * the lower scene and choose how to propagate events.
206
+ *
207
+ * The only technical difference between overlay and
208
+ * opaque is whether or not the scene we've pushed
209
+ * on top of stays loaded.
210
+ */
211
+ export type Scene = (like: Like, scenes: SceneManager) => SceneInstance;
212
+ /**
213
+ * A helper for extending Scenes as to have known interfaces beyond
214
+ * the generic ones. For example:
215
+ *
216
+ * ```ts
217
+ * type UISceneInstance = SceneInstance & {
218
+ * buttonClicked(name: string) => void;
219
+ * };
220
+ * type UIScene = SceneEx<UISceneInstance>;
221
+ * ```
222
+ *
223
+ * Now, a parent composing scene can take in UIScene rather than Scene,
224
+ * and it has no need to cast anything.
225
+ */
226
+ export type SceneEx<S> = (like: Like, scenes: SceneManager) => S & SceneInstance;
227
+ /**
228
+ * A scene instance is just an object with event handlers. It's
229
+ * the `like` object but without the modules -- just the event handling
230
+ * callbacks.
231
+ *
232
+ * See {@link Scene} for usage.
233
+ */
234
+ export type SceneInstance = LikeHandlers & {
235
+ /**
236
+ * Called when a scene is started or resumed (pop after a push w/o unload).
237
+ * Prefer to initialize in the scene constructor.
238
+ *
239
+ * Use case: This secene does a push without unload because we want to preserve
240
+ * its state, but we unload a few large assets before doing the push. When
241
+ * upper scene is popped, `load` fires so we can get those assets back.
242
+ *
243
+ * (Same signature as the one in {@link LikeHandlers}, just redeclared for docs)
244
+ */
245
+ load?: () => void;
246
+ /**
247
+ * Called when a scene is pushed with unload, or popped off.
248
+ *
249
+ * Use case: We want to clear out any native event handlers or global resource
250
+ * allocations (LÏKE has neither, but maybe you do?) in case another scene
251
+ * kicks this one off the stack.
252
+ *
253
+ * (Same signature as the one in {@link LikeHandlers}, just redeclared for docs)
254
+ */
255
+ quit?: () => void;
256
+ };
257
+ /** Goofy ahh Typescript thingy to avoid excess generics @private */
258
+ export type InstantiateReturn<F> = F extends SceneEx<infer S> ? S & SceneInstance : never;
259
+ /**
260
+ * Scenemanager is the entry point for the LÏKE scene system.
261
+ * Without it, there are no scene functions; it's entirely modular.
262
+ *
263
+ * Usage:
264
+ * ```typescript
265
+ * const like = createLike(document.body);
266
+ * const sceneMan = new SceneManager(like);
267
+ * sceneMan.push(myScene)
268
+ * ```
269
+ * For arbitrary scene management (non stack based),
270
+ * just use {@link SceneManager.set} which switches out the stack top.
271
+ *
272
+ * For stack-based, use {@link SceneManager.push} and {@link SceneManager.pop}.
273
+ * Note that for stack-based games, it is wise to put the first initialization in as
274
+ * a callback-based system rather than going straight to scene. This allows
275
+ * for easy resets / game overs.
276
+ *
277
+ * Note that the SceneManager sets {@link LikeHandlers.handleEvent | like.handleEvent}.
278
+ * To get rid of scene functionality entirely, simply set it back to default.
279
+ * ```typescript
280
+ * like.handleEvent = undefined;
281
+ * ```
282
+ * Otherwise, the `SceneManager` stays allocated even if the scene stack was cleared.
283
+ */
284
+ export declare class SceneManager {
285
+ private like;
286
+ private scenes;
287
+ constructor(like: Like);
288
+ /**
289
+ * Get the current scene, or a specific index.
290
+ *
291
+ * Uses `Array.at` under the hood, so -1 is the
292
+ * top scene, -2 is the parent scene, etc.
293
+ *
294
+ * During instantiation, the stack is not shifted
295
+ * relative to during event/lifecycle functions.
296
+ * The only difference is that during load,
297
+ * scene.get(-1) of course returns no value.
298
+ */
299
+ get(pos?: number): SceneInstance | undefined;
300
+ /**
301
+ * Set the current scene at the top of the scene stack.
302
+ * If the stack is empty, push it onto the stack.
303
+ *
304
+ * The new scene is instantiated after the old one is
305
+ * quit and removed from the stack.
306
+ *
307
+ * Set cannot clear the current scene; for that use {@link pop}.
308
+ *
309
+ * @param scene is a Scene (factory pattern).
310
+ * @param instance is an optional preloaded instance.
311
+ */
312
+ set(scene: Scene, instance?: SceneInstance): void;
313
+ /**
314
+ * Push a scene to the scene stack and run it.
315
+ *
316
+ * @param scene A function that creates and returns a scene instance, which is just event handlers.
317
+ *
318
+ * @param unload
319
+ *
320
+ * If a scene calls `scenes.push(nextScene, true)`, it will be unloaded
321
+ * and re-constructed upon the parent scene calling `scenes.pop()`.
322
+ * Good for resource-intensive
323
+ * scenes or ones that rely heavily on their lifecycle. If you do want
324
+ * the lower scene to know what happened in the upper while unloaded, (i.e. overworld
325
+ * updating with a battle), consider using scene composition instead, or
326
+ * using localStorage to track persistent game state.
327
+ *
328
+ * If a scene calls `scenes.push(nextScene, false)`, it will stay loaded:
329
+ * this means when we pop its parent, it will simply continue running, though
330
+ * `load` will be called. Assets will of course stay loaded in during that time.
331
+ *
332
+ * Further, with unload disabled the upper scene now has the ability to reference
333
+ * the instance that called `scene.push` and call down to it in a generic way
334
+ * via `scene.get(-2)`
335
+ *
336
+ * See {@link Scene} for more detail -- while stacking is good for certain
337
+ * things, you're likely looking for Scene Composition.
338
+ *
339
+ */
340
+ push(scene: Scene, unload: boolean): void;
341
+ /**
342
+ * Pop the current scene off the stack, calling `quit` on it and
343
+ * dropping the instance reference.
344
+ *
345
+ * If the lower scene had called `pushScene` with the second arg (unload)
346
+ * set to true, it will be re-loaded. Otherwise it will continue where it
347
+ * left off. Either way its `load` fill fire.
348
+ *
349
+ * To clear the stack, just run:
350
+ * ```ts
351
+ * while (like.popScene());
352
+ * ```
353
+ */
354
+ pop(): Scene | undefined;
355
+ /**
356
+ * Make a scene into an instance and dispatch `load` into it.
357
+ */
358
+ instantiate<T extends SceneEx<SceneInstance>>(scene: T): InstantiateReturn<T>;
359
+ /**
360
+ * Unload a parent scene. Only use this if the lower scene requested to be
361
+ * unloaded, or if you're certain that you want to reload the lower
362
+ * completely. Otherwise, this can easily lose state or break functions.
363
+ */
364
+ deinstance(pos: number): void;
365
+ debugDraw(): void;
366
+ private handleEvent;
367
+ }
368
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,204 @@
1
+ /**
2
+ * Scenes are a modular component of LÏKE based on setting `like.handleEvent`.
3
+ * The scene system is simple and powerful, once understood.
4
+ *
5
+ * For devs using the built-in callback pattern, they're an easy way
6
+ * to stack functionality on to the current project such as
7
+ * gamepad mapping or debug overlays.
8
+ *
9
+ * For multi-scene games, they codify a common state-management pattern based
10
+ * on switching between (or nesting) event handler callbacks. It's
11
+ * a lot better than switch-casing on each handler, or manually setting/clearing
12
+ * handler functions on each transition.
13
+ *
14
+ * Using scenes for your game also replaces the need to pass around global `like`
15
+ * or `sceneManager` wherever it is used.
16
+ *
17
+ * ## Jump in
18
+ *
19
+ * Get started: {@link SceneManager}
20
+ *
21
+ * Make your own scene: {@link Scene}
22
+ *
23
+ * Check out built-in utility scenes:
24
+ * - {@link scene/prefab/mapGamepad}
25
+ * - {@link scene/prefab/startScreen}
26
+ *
27
+ * @module scene
28
+ */
29
+ import { likeDispatch } from '../engine';
30
+ /**
31
+ * Scenemanager is the entry point for the LÏKE scene system.
32
+ * Without it, there are no scene functions; it's entirely modular.
33
+ *
34
+ * Usage:
35
+ * ```typescript
36
+ * const like = createLike(document.body);
37
+ * const sceneMan = new SceneManager(like);
38
+ * sceneMan.push(myScene)
39
+ * ```
40
+ * For arbitrary scene management (non stack based),
41
+ * just use {@link SceneManager.set} which switches out the stack top.
42
+ *
43
+ * For stack-based, use {@link SceneManager.push} and {@link SceneManager.pop}.
44
+ * Note that for stack-based games, it is wise to put the first initialization in as
45
+ * a callback-based system rather than going straight to scene. This allows
46
+ * for easy resets / game overs.
47
+ *
48
+ * Note that the SceneManager sets {@link LikeHandlers.handleEvent | like.handleEvent}.
49
+ * To get rid of scene functionality entirely, simply set it back to default.
50
+ * ```typescript
51
+ * like.handleEvent = undefined;
52
+ * ```
53
+ * Otherwise, the `SceneManager` stays allocated even if the scene stack was cleared.
54
+ */
55
+ export class SceneManager {
56
+ constructor(like) {
57
+ Object.defineProperty(this, "like", {
58
+ enumerable: true,
59
+ configurable: true,
60
+ writable: true,
61
+ value: like
62
+ });
63
+ Object.defineProperty(this, "scenes", {
64
+ enumerable: true,
65
+ configurable: true,
66
+ writable: true,
67
+ value: []
68
+ });
69
+ like.handleEvent = this.handleEvent.bind(this);
70
+ }
71
+ /**
72
+ * Get the current scene, or a specific index.
73
+ *
74
+ * Uses `Array.at` under the hood, so -1 is the
75
+ * top scene, -2 is the parent scene, etc.
76
+ *
77
+ * During instantiation, the stack is not shifted
78
+ * relative to during event/lifecycle functions.
79
+ * The only difference is that during load,
80
+ * scene.get(-1) of course returns no value.
81
+ */
82
+ get(pos = -1) {
83
+ return this.scenes.at(pos)?.instance;
84
+ }
85
+ /**
86
+ * Set the current scene at the top of the scene stack.
87
+ * If the stack is empty, push it onto the stack.
88
+ *
89
+ * The new scene is instantiated after the old one is
90
+ * quit and removed from the stack.
91
+ *
92
+ * Set cannot clear the current scene; for that use {@link pop}.
93
+ *
94
+ * @param scene is a Scene (factory pattern).
95
+ * @param instance is an optional preloaded instance.
96
+ */
97
+ set(scene, instance) {
98
+ const idx = Math.max(0, this.scenes.length - 1);
99
+ this.deinstance(-1);
100
+ this.scenes[idx] = { instance, factory: scene };
101
+ this.scenes[idx].instance = instance ?? this.instantiate(scene);
102
+ }
103
+ /**
104
+ * Push a scene to the scene stack and run it.
105
+ *
106
+ * @param scene A function that creates and returns a scene instance, which is just event handlers.
107
+ *
108
+ * @param unload
109
+ *
110
+ * If a scene calls `scenes.push(nextScene, true)`, it will be unloaded
111
+ * and re-constructed upon the parent scene calling `scenes.pop()`.
112
+ * Good for resource-intensive
113
+ * scenes or ones that rely heavily on their lifecycle. If you do want
114
+ * the lower scene to know what happened in the upper while unloaded, (i.e. overworld
115
+ * updating with a battle), consider using scene composition instead, or
116
+ * using localStorage to track persistent game state.
117
+ *
118
+ * If a scene calls `scenes.push(nextScene, false)`, it will stay loaded:
119
+ * this means when we pop its parent, it will simply continue running, though
120
+ * `load` will be called. Assets will of course stay loaded in during that time.
121
+ *
122
+ * Further, with unload disabled the upper scene now has the ability to reference
123
+ * the instance that called `scene.push` and call down to it in a generic way
124
+ * via `scene.get(-2)`
125
+ *
126
+ * See {@link Scene} for more detail -- while stacking is good for certain
127
+ * things, you're likely looking for Scene Composition.
128
+ *
129
+ */
130
+ push(scene, unload) {
131
+ if (unload) {
132
+ this.deinstance(-1);
133
+ }
134
+ const entry = { instance: undefined, factory: scene };
135
+ this.scenes.push(entry);
136
+ entry.instance = this.instantiate(entry.factory);
137
+ }
138
+ /**
139
+ * Pop the current scene off the stack, calling `quit` on it and
140
+ * dropping the instance reference.
141
+ *
142
+ * If the lower scene had called `pushScene` with the second arg (unload)
143
+ * set to true, it will be re-loaded. Otherwise it will continue where it
144
+ * left off. Either way its `load` fill fire.
145
+ *
146
+ * To clear the stack, just run:
147
+ * ```ts
148
+ * while (like.popScene());
149
+ * ```
150
+ */
151
+ pop() {
152
+ this.deinstance(-1);
153
+ const oldTop = this.scenes.pop();
154
+ const top = this.scenes.at(-1);
155
+ if (top) {
156
+ if (!top.instance) {
157
+ top.instance = this.instantiate(top.factory);
158
+ }
159
+ }
160
+ return oldTop?.factory;
161
+ }
162
+ /**
163
+ * Make a scene into an instance and dispatch `load` into it.
164
+ */
165
+ instantiate(scene) {
166
+ const inst = scene(this.like, this);
167
+ likeDispatch(inst, { type: 'load', args: [], timestamp: this.like.timer.getTime() });
168
+ return inst;
169
+ }
170
+ /**
171
+ * Unload a parent scene. Only use this if the lower scene requested to be
172
+ * unloaded, or if you're certain that you want to reload the lower
173
+ * completely. Otherwise, this can easily lose state or break functions.
174
+ */
175
+ deinstance(pos) {
176
+ const scene = this.scenes.at(pos);
177
+ if (scene) {
178
+ if (scene.instance) {
179
+ likeDispatch(scene.instance, { type: 'quit', args: [], timestamp: this.like.timer.getTime() });
180
+ }
181
+ scene.instance = undefined;
182
+ }
183
+ }
184
+ debugDraw() {
185
+ const g = this.like.gfx;
186
+ for (const si in this.scenes) {
187
+ const i = Number(si);
188
+ g.print('white', `${si}: hasInstance: ${!!this.scenes[i].instance}`, [50, i * 20 + 20]);
189
+ }
190
+ }
191
+ handleEvent(event) {
192
+ const top = this.scenes.at(-1);
193
+ if (top) {
194
+ if (!top.instance) {
195
+ throw new Error("expected top scene to be loaded");
196
+ }
197
+ likeDispatch(top.instance, event);
198
+ }
199
+ else {
200
+ this.like.callOwnHandlers(event);
201
+ }
202
+ //if (event.type == 'draw') this.debugDraw();
203
+ }
204
+ }
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Need some time to load?
3
+ *
4
+ * Or maybe, jump cuts look like garbage. Well, they do.
5
+ *
6
+ * Here's your remedy:
7
+ * ```ts
8
+ * sceneMan.push(fadeTransition(myNextScene, enableUnload), false);
9
+ * ```
10
+ */
11
+ import { Scene } from "..";
12
+ import { ColorNum } from "../../graphics";
13
+ export type FadeProps = Partial<{
14
+ color: ColorNum;
15
+ duration: number;
16
+ }>;
17
+ /**
18
+ * The fade scene factory.
19
+ *
20
+ * @param nextF The next scene (not sceneInstance)
21
+ * @param unload Set this rather than unload in `sceneMan.push`, which will cause early unload.`
22
+ * @options color and duration.
23
+ */
24
+ export declare const fadeTransition: (nextF: Scene, unload: boolean, options?: FadeProps) => Scene;
25
+ //# sourceMappingURL=fadeTransition.d.ts.map
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Need some time to load?
3
+ *
4
+ * Or maybe, jump cuts look like garbage. Well, they do.
5
+ *
6
+ * Here's your remedy:
7
+ * ```ts
8
+ * sceneMan.push(fadeTransition(myNextScene, enableUnload), false);
9
+ * ```
10
+ */
11
+ import { callOwnHandlers, likeDispatch } from "../..";
12
+ /**
13
+ * The fade scene factory.
14
+ *
15
+ * @param nextF The next scene (not sceneInstance)
16
+ * @param unload Set this rather than unload in `sceneMan.push`, which will cause early unload.`
17
+ * @options color and duration.
18
+ */
19
+ export const fadeTransition = (nextF, unload, options = {}) => (like, scenes) => {
20
+ const baseColor = options.color ? options.color.slice(0, 3) : [0, 0, 0];
21
+ const duration = options.duration ?? 1;
22
+ const prev = scenes.get(-2);
23
+ const next = scenes.instantiate(nextF); // load next scene early
24
+ let time = 0;
25
+ return {
26
+ // stack: [prev: -3, next: -2, this: -1]
27
+ load() { time = 0; },
28
+ update(dt) { time += dt; },
29
+ handleEvent(ev) {
30
+ let elapsed = time / duration * 2;
31
+ if (ev.type == 'draw') {
32
+ if (elapsed < 1) {
33
+ if (prev)
34
+ likeDispatch(prev, ev);
35
+ like.gfx.clear([...baseColor, elapsed]);
36
+ }
37
+ else if (elapsed < 2) {
38
+ if (unload) {
39
+ // if we're unloading, unload the prev scene now.
40
+ scenes.deinstance(-2);
41
+ unload = false;
42
+ }
43
+ if (next) {
44
+ likeDispatch(next, ev);
45
+ }
46
+ like.gfx.clear([...baseColor, 2 - elapsed]);
47
+ }
48
+ else {
49
+ scenes.set(nextF, next);
50
+ }
51
+ }
52
+ callOwnHandlers(this, ev);
53
+ }
54
+ };
55
+ };
@@ -0,0 +1,47 @@
1
+ /**
2
+ * An automagical gamepad mapper.
3
+ *
4
+ * ```ts
5
+ * like.gamepadconnected = (index) =>
6
+ * scenes.push(mapGamepad({ buttons: buttonSetGBA, sticks: 0 }, 0), false)
7
+ * ```
8
+ *
9
+ * Add this to your codebase and activating a gamepad causes a button mapping screen to pop up.
10
+ * It will request to map any buttons not already covered by the automapping database.
11
+ *
12
+ * If you're wondering what `scenes` refers to, check out {@link SceneManager} to
13
+ * get started.
14
+ *
15
+ * Note: many browsers only fire gamepadconnected on first button press, so always writing "P2: press any button" is a fine idea.
16
+ * @module scene/prefab/mapGamepad
17
+ */
18
+ import type { Scene } from "../";
19
+ import { type LikeButton } from "../../input";
20
+ /** All the buttons on an NES */
21
+ export declare const buttonSetNES: Set<LikeButton>;
22
+ /** All the buttons on a GBA -- Like an NES but with L+R */
23
+ export declare const buttonSetGBA: Set<LikeButton>;
24
+ /** All the buttons on a SNES */
25
+ export declare const buttonSetSNES: Set<LikeButton>;
26
+ /** All the buttons on a PS1 -- Like a SNES but with L2+R2 */
27
+ export declare const buttonSetPS1: Set<LikeButton>;
28
+ /** All the buttons -- including the stick buttons. */
29
+ export declare const buttonSetAll: Set<LikeButton>;
30
+ /** What gamepad buttons or sticks are we using?
31
+ *
32
+ * Set buttons to one of:
33
+ * - {@link buttonSetNES}
34
+ * - {@link buttonSetGBA}
35
+ * - {@link buttonSetSNES}
36
+ * - {@link buttonSetPS1}
37
+ * - {@link buttonSetAll}
38
+ * - Custom: `new Set<LikeButton>(...)`
39
+ *
40
+ */
41
+ export type MapMode = {
42
+ buttons: Set<LikeButton>;
43
+ stickCount: number;
44
+ };
45
+ /** The gamepad mapping scene factory. Call this and pass it into {@link SceneManager.push} */
46
+ export declare const mapGamepad: (mapMode: MapMode, targetPad: number) => Scene;
47
+ //# sourceMappingURL=mapGamepad.d.ts.map