@vpmedia/phaser 1.117.0 → 1.119.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.
Files changed (48) hide show
  1. package/dist/index.js +163 -158
  2. package/dist/index.js.map +1 -1
  3. package/dist/phaser/core/animation_manager.d.ts +1 -1
  4. package/dist/phaser/core/animation_manager.d.ts.map +1 -1
  5. package/dist/phaser/core/cache.d.ts +70 -42
  6. package/dist/phaser/core/cache.d.ts.map +1 -1
  7. package/dist/phaser/core/game.d.ts +34 -8
  8. package/dist/phaser/core/game.d.ts.map +1 -1
  9. package/dist/phaser/core/input.d.ts +1 -1
  10. package/dist/phaser/core/input.d.ts.map +1 -1
  11. package/dist/phaser/core/input_mspointer.d.ts +8 -7
  12. package/dist/phaser/core/input_mspointer.d.ts.map +1 -1
  13. package/dist/phaser/core/loader.d.ts +30 -1
  14. package/dist/phaser/core/loader.d.ts.map +1 -1
  15. package/dist/phaser/core/loader_parser.d.ts +24 -4
  16. package/dist/phaser/core/loader_parser.d.ts.map +1 -1
  17. package/dist/phaser/core/scene.d.ts +3 -2
  18. package/dist/phaser/core/scene.d.ts.map +1 -1
  19. package/dist/phaser/core/scene_manager.d.ts +28 -17
  20. package/dist/phaser/core/scene_manager.d.ts.map +1 -1
  21. package/dist/phaser/core/sound.d.ts +13 -3
  22. package/dist/phaser/core/sound.d.ts.map +1 -1
  23. package/dist/phaser/core/tween.d.ts +3 -3
  24. package/dist/phaser/core/tween.d.ts.map +1 -1
  25. package/dist/phaser/core/tween_manager.d.ts +8 -6
  26. package/dist/phaser/core/tween_manager.d.ts.map +1 -1
  27. package/dist/phaser/display/bitmap_text.d.ts +13 -8
  28. package/dist/phaser/display/bitmap_text.d.ts.map +1 -1
  29. package/package.json +1 -1
  30. package/src/phaser/core/animation_manager.ts +1 -1
  31. package/src/phaser/core/cache.test.ts +201 -0
  32. package/src/phaser/core/cache.ts +126 -86
  33. package/src/phaser/core/game.ts +45 -22
  34. package/src/phaser/core/input.ts +1 -1
  35. package/src/phaser/core/input_mspointer.ts +10 -13
  36. package/src/phaser/core/loader.ts +82 -42
  37. package/src/phaser/core/loader_parser.test.ts +115 -0
  38. package/src/phaser/core/loader_parser.ts +64 -36
  39. package/src/phaser/core/scene.ts +4 -2
  40. package/src/phaser/core/scene_manager.test.ts +193 -0
  41. package/src/phaser/core/scene_manager.ts +51 -36
  42. package/src/phaser/core/sound.ts +29 -17
  43. package/src/phaser/core/sound_manager.ts +1 -1
  44. package/src/phaser/core/tween.ts +6 -6
  45. package/src/phaser/core/tween_manager.ts +42 -35
  46. package/src/phaser/display/bitmap_text.test.ts +212 -0
  47. package/src/phaser/display/bitmap_text.ts +39 -24
  48. package/src/phaser/display/webgl/renderer.ts +1 -1
@@ -0,0 +1,193 @@
1
+ import { describe, expect, it, vi } from 'vitest';
2
+ import { Scene } from './scene.js';
3
+ import { SceneManager } from './scene_manager.js';
4
+ import type { Game } from './game.js';
5
+ import type { SceneState } from './scene_manager.js';
6
+
7
+ const createGame = (isBooted = true): Game =>
8
+ ({
9
+ isBooted,
10
+ isKickStart: false,
11
+ world: { x: 0, y: 0, destroy: vi.fn() },
12
+ tweens: { removeAll: vi.fn() },
13
+ input: { reset: vi.fn() },
14
+ time: { removeAll: vi.fn() },
15
+ scale: { reset: vi.fn() },
16
+ cache: { destroy: vi.fn() },
17
+ load: { reset: vi.fn(), start: vi.fn(), totalQueuedFiles: (): number => 0, totalQueuedPacks: (): number => 0 },
18
+ }) as unknown as Game;
19
+
20
+ const createManager = (pendingState: ConstructorParameters<typeof SceneManager>[1] = null): SceneManager =>
21
+ new SceneManager(createGame(), pendingState);
22
+
23
+ describe('SceneManager', () => {
24
+ describe('add', () => {
25
+ it('takes a plain hook object and links the game onto it', () => {
26
+ const manager = createManager();
27
+ const state: SceneState = { create: vi.fn<() => void>() };
28
+ expect(manager.add('menu', state)).toBe(state);
29
+ expect(state.game).toBeDefined();
30
+ });
31
+
32
+ it('builds a state from a constructor', () => {
33
+ const manager = createManager();
34
+ class Menu extends Scene {}
35
+ expect(manager.add('menu', Menu)).toBeInstanceOf(Menu);
36
+ });
37
+
38
+ it('takes a scene instance as-is', () => {
39
+ const manager = createManager();
40
+ const scene = new Scene();
41
+ expect(manager.add('menu', scene)).toBe(scene);
42
+ });
43
+
44
+ it('queues the state when the game has not booted', () => {
45
+ const manager = new SceneManager(createGame(false), null);
46
+ manager.add('menu', { create: vi.fn<() => void>() }, true);
47
+ expect(manager._pendingState).toBe('menu');
48
+ });
49
+
50
+ it('starts the state right away once the game has booted', () => {
51
+ const manager = createManager();
52
+ manager.add('menu', { create: vi.fn<() => void>() }, true);
53
+ expect(manager._pendingState).toBe('menu');
54
+ });
55
+ });
56
+
57
+ describe('boot', () => {
58
+ it('registers a state given as an object under the default key', () => {
59
+ const manager = createManager({ create: vi.fn<() => void>() });
60
+ manager.boot();
61
+ expect(manager.states['default']).toBeDefined();
62
+ });
63
+
64
+ it('leaves a state given by key alone', () => {
65
+ const manager = createManager('menu');
66
+ manager.boot();
67
+ expect(manager.states['default']).toBeUndefined();
68
+ });
69
+ });
70
+
71
+ describe('checkState', () => {
72
+ it('accepts a state carrying any of the lifecycle hooks', () => {
73
+ const manager = createManager();
74
+ manager.add('menu', { update: vi.fn<() => void>() });
75
+ expect(manager.checkState('menu')).toBe(true);
76
+ });
77
+
78
+ it('rejects a state with no hooks at all', () => {
79
+ const manager = createManager();
80
+ manager.add('menu', {});
81
+ expect(manager.checkState('menu')).toBe(false);
82
+ });
83
+
84
+ it('rejects a key it does not hold', () => {
85
+ expect(createManager().checkState('nope')).toBe(false);
86
+ });
87
+ });
88
+
89
+ describe('setCurrentState', () => {
90
+ it('binds the hooks the state provides and leaves the rest null', () => {
91
+ const manager = createManager();
92
+ const create = vi.fn<() => void>();
93
+ manager.add('menu', { create });
94
+ manager.setCurrentState('menu');
95
+ expect(manager.current).toBe('menu');
96
+ expect(manager.onCreateCallback).toBe(create);
97
+ expect(manager.onUpdateCallback).toBeNull();
98
+ expect(manager.onPreloadCallback).toBeNull();
99
+ });
100
+
101
+ it('falls back to a no-op for init and shutdown', () => {
102
+ const manager = createManager();
103
+ manager.add('menu', {});
104
+ manager.setCurrentState('menu');
105
+ expect(manager.onInitCallback).toBe(manager.dummy);
106
+ expect(manager.onShutDownCallback).toBe(manager.dummy);
107
+ });
108
+
109
+ it('calls init with the arguments start was given', () => {
110
+ const manager = createManager();
111
+ const init = vi.fn<() => void>();
112
+ manager.add('menu', { init, update: vi.fn<() => void>() });
113
+ manager.start('menu', true, false, 'a', 1);
114
+ manager.setCurrentState('menu');
115
+ expect(init).toHaveBeenCalledWith('a', 1);
116
+ });
117
+
118
+ it('stamps the key onto the state', () => {
119
+ const manager = createManager();
120
+ const state: SceneState = { create: vi.fn<() => void>() };
121
+ manager.add('menu', state);
122
+ manager.setCurrentState('menu');
123
+ expect(state.key).toBe('menu');
124
+ });
125
+ });
126
+
127
+ describe('start', () => {
128
+ it('ignores a key with no lifecycle hooks', () => {
129
+ const manager = createManager();
130
+ manager.add('menu', {});
131
+ manager.start('menu');
132
+ expect(manager._pendingState).toBeNull();
133
+ });
134
+ });
135
+
136
+ describe('update', () => {
137
+ it('runs the update hook only once the scene has been created', () => {
138
+ const manager = createManager();
139
+ const update = vi.fn<() => void>();
140
+ manager.add('menu', { update });
141
+ manager.setCurrentState('menu');
142
+ manager.update();
143
+ expect(update).not.toHaveBeenCalled();
144
+ manager.loadComplete();
145
+ manager.update();
146
+ expect(update).toHaveBeenCalledTimes(1);
147
+ });
148
+
149
+ it('runs the create hook exactly once', () => {
150
+ const manager = createManager();
151
+ const create = vi.fn<() => void>();
152
+ manager.add('menu', { create });
153
+ manager.setCurrentState('menu');
154
+ manager.loadComplete();
155
+ manager.loadComplete();
156
+ expect(create).toHaveBeenCalledTimes(1);
157
+ });
158
+ });
159
+
160
+ describe('resize', () => {
161
+ it('passes the new size to the scene', () => {
162
+ const manager = createManager();
163
+ const resize = vi.fn<(width: number, height: number) => void>();
164
+ manager.add('menu', { resize });
165
+ manager.setCurrentState('menu');
166
+ manager.resize(320, 240);
167
+ expect(resize).toHaveBeenCalledWith(320, 240);
168
+ });
169
+ });
170
+
171
+ describe('remove', () => {
172
+ it('drops the state and unbinds its hooks when it is current', () => {
173
+ const manager = createManager();
174
+ manager.add('menu', { create: vi.fn<() => void>() });
175
+ manager.setCurrentState('menu');
176
+ manager.remove('menu');
177
+ expect(manager.states['menu']).toBeUndefined();
178
+ expect(manager.onCreateCallback).toBeNull();
179
+ });
180
+ });
181
+
182
+ describe('destroy', () => {
183
+ it('empties the manager', () => {
184
+ const manager = createManager();
185
+ manager.add('menu', { create: vi.fn<() => void>(), shutdown: vi.fn<() => void>() });
186
+ manager.setCurrentState('menu');
187
+ manager.destroy();
188
+ expect(manager.states).toStrictEqual({});
189
+ expect(manager.current).toBe('');
190
+ expect(manager._pendingState).toBeNull();
191
+ });
192
+ });
193
+ });
@@ -7,34 +7,47 @@ export type SceneHooks = {
7
7
  preload?: () => void;
8
8
  create?: () => void;
9
9
  update?: () => void;
10
+ render?: () => void;
10
11
  resize?: (width: number, height: number) => void;
11
12
  pauseUpdate?: () => void;
12
13
  shutdown?: () => void;
13
14
  };
14
15
 
16
+ /** A scene once the manager holds it: its hooks plus the wiring the manager adds. */
17
+ export type SceneState = SceneHooks & {
18
+ game?: Game | null;
19
+ key?: string;
20
+ };
21
+
22
+ /** What the manager accepts as a scene: an instance, a plain hook object, or a class to build one. */
23
+ export type SceneDefinition = SceneState | (new (game: Game) => SceneState);
24
+
25
+ /** A hook invoked with the scene as its receiver and the game as its argument. */
26
+ type SceneCallback = (game: Game) => void;
27
+
15
28
  export class SceneManager {
16
29
  public game!: Game;
17
- public states!: any;
18
- public _pendingState!: any;
19
- public _clearWorld!: any;
20
- public _clearCache!: any;
21
- public _created!: any;
22
- public _args!: any;
30
+ public states!: Record<string, SceneState>;
31
+ public _pendingState!: SceneDefinition | string | null;
32
+ public _clearWorld!: boolean;
33
+ public _clearCache!: boolean;
34
+ public _created!: boolean;
35
+ public _args!: unknown[];
23
36
  public current!: string;
24
- public onInitCallback!: any;
25
- public onPreloadCallback!: any;
26
- public onCreateCallback!: any;
27
- public onUpdateCallback!: any;
28
- public onResizeCallback!: any;
29
- public onPauseUpdateCallback!: any;
30
- public onShutDownCallback!: any;
31
- public callbackContext!: SceneHooks;
37
+ public onInitCallback!: ((...args: unknown[]) => void) | null;
38
+ public onPreloadCallback!: SceneCallback | null;
39
+ public onCreateCallback!: SceneCallback | null;
40
+ public onUpdateCallback!: SceneCallback | null;
41
+ public onResizeCallback!: ((width: number, height: number) => void) | null;
42
+ public onPauseUpdateCallback!: SceneCallback | null;
43
+ public onShutDownCallback!: SceneCallback | null;
44
+ public callbackContext!: SceneState;
32
45
  /**
33
46
  * Creates a new SceneManager instance.
34
47
  * @param {Game} game - The game instance this manager belongs to.
35
48
  * @param {string} pendingState - The state to load when the game boots.
36
49
  */
37
- public constructor(game: Game, pendingState: string) {
50
+ public constructor(game: Game, pendingState: SceneDefinition | string | null) {
38
51
  this.game = game;
39
52
  this.states = {};
40
53
  this._pendingState = null;
@@ -72,15 +85,15 @@ export class SceneManager {
72
85
  * @param {boolean} autoStart - Whether to start this state immediately.
73
86
  * @returns {Scene|object} The created scene or state object.
74
87
  */
75
- public add(key: string, state: any, autoStart = false) {
76
- let newState = null;
77
- if (state instanceof Scene) {
78
- newState = state;
79
- } else if (typeof state === 'object') {
80
- newState = state;
81
- newState.game = this.game;
82
- } else if (typeof state === 'function') {
88
+ public add(key: string, state: SceneDefinition, autoStart = false): SceneState {
89
+ let newState: SceneState;
90
+ if (typeof state === 'function') {
83
91
  newState = new state(this.game);
92
+ } else {
93
+ newState = state;
94
+ if (!(state instanceof Scene)) {
95
+ newState.game = this.game;
96
+ }
84
97
  }
85
98
  this.states[key] = newState;
86
99
  if (autoStart) {
@@ -149,7 +162,7 @@ export class SceneManager {
149
162
  * This method is called before the game loop updates.
150
163
  */
151
164
  public preUpdate(): void {
152
- if (this._pendingState && this.game.isBooted) {
165
+ if (typeof this._pendingState === 'string' && this._pendingState && this.game.isBooted) {
153
166
  // var previousStateKey = this.current;
154
167
  // Already got a state running?
155
168
  this.clearCurrentState();
@@ -207,11 +220,9 @@ export class SceneManager {
207
220
  * @returns {boolean} True if the scene exists, false otherwise.
208
221
  */
209
222
  public checkState(key: string): boolean {
210
- if (this.states[key]) {
211
- if (this.states[key].preload || this.states[key].create || this.states[key].update || this.states[key].render) {
212
- return true;
213
- }
214
- return false;
223
+ const state = this.states[key];
224
+ if (state) {
225
+ return Boolean(state.preload ?? state.create ?? state.update ?? state.render);
215
226
  }
216
227
  return false;
217
228
  }
@@ -221,8 +232,11 @@ export class SceneManager {
221
232
  * @param {string} key - The unique key for the state to link.
222
233
  */
223
234
  public link(key: string): void {
224
- this.states[key].game = this.game;
225
- this.states[key].key = key;
235
+ const state = this.states[key];
236
+ if (state) {
237
+ state.game = this.game;
238
+ state.key = key;
239
+ }
226
240
  }
227
241
 
228
242
  /**
@@ -230,8 +244,9 @@ export class SceneManager {
230
244
  * @param {string} key - The unique key for the state to unlink.
231
245
  */
232
246
  public unlink(key: string): void {
233
- if (this.states[key]) {
234
- this.states[key].game = null;
247
+ const state = this.states[key];
248
+ if (state) {
249
+ state.game = null;
235
250
  }
236
251
  }
237
252
 
@@ -240,7 +255,7 @@ export class SceneManager {
240
255
  * @param {string} key - The unique key for the state to set as current.
241
256
  */
242
257
  public setCurrentState(key: string): void {
243
- this.callbackContext = this.states[key];
258
+ this.callbackContext = this.states[key]!;
244
259
  this.link(key);
245
260
  // Used when the state is set as being the current active state
246
261
  this.onInitCallback = this.callbackContext.init ?? this.dummy;
@@ -265,7 +280,7 @@ export class SceneManager {
265
280
  * @returns {T} The current scene state.
266
281
  */
267
282
  public getCurrentState<T = Partial<Scene>>(): T {
268
- return this.states[this.current];
283
+ return this.states[this.current] as T;
269
284
  }
270
285
 
271
286
  /**
@@ -273,7 +288,7 @@ export class SceneManager {
273
288
  * This method is called when scene loading is complete.
274
289
  */
275
290
  public loadComplete(): void {
276
- if (this._created === false && this.onCreateCallback) {
291
+ if (!this._created && this.onCreateCallback) {
277
292
  this._created = true;
278
293
  this.onCreateCallback.call(this.callbackContext, this.game);
279
294
  } else {
@@ -2,13 +2,24 @@ import { Signal } from './signal.js';
2
2
  import type { Game } from './game.js';
3
3
  import type { Tween } from './tween.js';
4
4
 
5
+ /** A named span within a sound file. */
6
+ export type SoundMarker = {
7
+ name: string;
8
+ start: number;
9
+ stop: number;
10
+ volume: number;
11
+ duration: number;
12
+ durationMS: number;
13
+ loop: boolean;
14
+ };
15
+
5
16
  export class Sound {
6
- public _paused!: any;
17
+ public _paused!: boolean;
7
18
  public game!: Game;
8
19
  public name!: string;
9
20
  public key!: string;
10
21
  public loop!: boolean;
11
- public markers!: any;
22
+ public markers!: Record<string, SoundMarker>;
12
23
  public context!: AudioContext | null;
13
24
  public autoplay!: boolean;
14
25
  public totalDuration!: number;
@@ -124,8 +135,10 @@ export class Sound {
124
135
  */
125
136
  public soundHasUnlocked(key: string): void {
126
137
  if (key === this.key) {
127
- this._sound = this.game.cache.getSoundData(this.key);
128
- this.totalDuration = (this._sound as any).duration;
138
+ // Unlocking parks the decoded buffer in _sound; play() replaces it with a real source node.
139
+ const buffer = this.game.cache.getSoundData(this.key) as AudioBuffer;
140
+ this._sound = buffer as unknown as AudioBufferSourceNode;
141
+ this.totalDuration = buffer.duration;
129
142
  }
130
143
  }
131
144
 
@@ -246,7 +259,7 @@ export class Sound {
246
259
  * @param {boolean} forceRestart - Whether to force restarting the sound even if it's already playing.
247
260
  * @returns {Sound} This Sound instance for chaining.
248
261
  */
249
- public play(marker: any = '', position: any = 0, volume: any = 1, loop: any = false, forceRestart = true): this {
262
+ public play(marker: string | false | null = '', position = 0, volume = 1, loop = false, forceRestart = true): this {
250
263
  if (marker === undefined || marker === false || marker === null) {
251
264
  marker = '';
252
265
  }
@@ -274,15 +287,16 @@ export class Sound {
274
287
  // We should never play the entire thing
275
288
  return this;
276
289
  }
290
+ const markerData = marker === '' ? undefined : this.markers[marker];
277
291
  if (marker !== '') {
278
- if (this.markers[marker]) {
292
+ if (markerData) {
279
293
  this.currentMarker = marker;
280
294
  // Playing a marker? Then we default to the marker values
281
- this.position = this.markers[marker].start;
282
- this.volume = this.markers[marker].volume;
283
- this.loop = this.markers[marker].loop;
284
- this.duration = this.markers[marker].duration;
285
- this.durationMS = this.markers[marker].durationMS;
295
+ this.position = markerData.start;
296
+ this.volume = markerData.volume;
297
+ this.loop = markerData.loop;
298
+ this.duration = markerData.duration;
299
+ this.durationMS = markerData.durationMS;
286
300
  if (volume !== undefined) {
287
301
  this.volume = volume;
288
302
  }
@@ -298,10 +312,8 @@ export class Sound {
298
312
  return this;
299
313
  }
300
314
  } else {
301
- position = position ?? 0;
302
- if (volume === undefined) {
303
- volume = this._volume;
304
- }
315
+ position ??= 0;
316
+ volume ??= this._volume;
305
317
  if (loop === undefined) {
306
318
  ({ loop } = this);
307
319
  }
@@ -323,7 +335,7 @@ export class Sound {
323
335
  } else {
324
336
  this._sound.connect(this.gainNode);
325
337
  }
326
- this._buffer = this.game.cache.getSoundData(this.key);
338
+ this._buffer = this.game.cache.getSoundData(this.key) as AudioBuffer | null;
327
339
  this._sound.buffer = this._buffer;
328
340
  if (this.loop && marker === '') {
329
341
  this._sound.loop = true;
@@ -351,7 +363,7 @@ export class Sound {
351
363
  this.onPlay.dispatch(this);
352
364
  } else {
353
365
  this.pendingPlayback = true;
354
- if (this.game.cache.getSound(this.key) && this.game.cache.getSound(this.key).isDecoding === false) {
366
+ if (this.game.cache.getSound(this.key)?.isDecoding === false) {
355
367
  this.game.sound.decode(this.key);
356
368
  }
357
369
  }
@@ -272,7 +272,7 @@ export class SoundManager {
272
272
  */
273
273
  public decode(key: string): void {
274
274
  const soundData = this.game.cache.getSoundData(key);
275
- if (!soundData) {
275
+ if (!(soundData instanceof ArrayBuffer)) {
276
276
  return;
277
277
  }
278
278
  if (this.game.cache.isSoundDecoded(key) === true) {
@@ -1,14 +1,14 @@
1
+ import type { TweenTarget } from './tween_manager.js';
1
2
  import * as MathUtils from '../util/math.js';
2
3
  import { TWEEN_COMPLETE, TWEEN_LOOPED, TWEEN_PENDING, TWEEN_RUNNING } from './const.js';
3
4
  import { Signal } from './signal.js';
4
5
  import { TweenData } from './tween_data.js';
5
6
  import type { Game } from './game.js';
6
- import type { DisplayObject } from '../display/display_object.js';
7
7
  import type { TweenManager } from './tween_manager.js';
8
8
 
9
9
  export class Tween {
10
10
  public game!: Game;
11
- public target!: DisplayObject;
11
+ public target!: TweenTarget;
12
12
  public manager!: TweenManager;
13
13
  public timeline!: TweenData[];
14
14
  public reverse!: boolean;
@@ -36,7 +36,7 @@ export class Tween {
36
36
  * @param {Game} game - Reference to the Phaser Game instance.
37
37
  * @param {TweenManager} manager - Reference to the Tween Manager.
38
38
  */
39
- public constructor(target: DisplayObject, game: Game, manager: TweenManager) {
39
+ public constructor(target: TweenTarget, game: Game, manager: TweenManager) {
40
40
  this.game = game;
41
41
  this.target = target;
42
42
  /** @type {TweenManager} */
@@ -100,7 +100,7 @@ export class Tween {
100
100
  yoyo = false
101
101
  ): this {
102
102
  if (typeof ease === 'string' && this.manager.easeMap[ease]) {
103
- ease = this.manager.easeMap[ease];
103
+ ease = this.manager.easeMap[ease]!;
104
104
  }
105
105
  if (this.isRunning) {
106
106
  return this;
@@ -133,7 +133,7 @@ export class Tween {
133
133
  yoyo = false
134
134
  ): this {
135
135
  if (typeof ease === 'string' && this.manager.easeMap[ease]) {
136
- ease = this.manager.easeMap[ease];
136
+ ease = this.manager.easeMap[ease]!;
137
137
  }
138
138
  if (this.isRunning) {
139
139
  this.game.logger.warn('Tween.from cannot be called after Tween.start');
@@ -284,7 +284,7 @@ export class Tween {
284
284
  */
285
285
  public easing(ease: string | Function, index: number): this {
286
286
  if (typeof ease === 'string' && this.manager.easeMap[ease]) {
287
- ease = this.manager.easeMap[ease];
287
+ ease = this.manager.easeMap[ease]!;
288
288
  }
289
289
  return this.updateTweenData('easingFunction', ease, index);
290
290
  }
@@ -35,11 +35,14 @@ import {
35
35
  SinusoidalOut,
36
36
  } from './tween_easing.js';
37
37
 
38
+ /** What a tween can be attached to: any object, or a list of them. */
39
+ export type TweenTarget = object | object[];
40
+
38
41
  export class TweenManager {
39
42
  public game!: Game;
40
- public _tweens!: any;
41
- public _add!: any;
42
- public easeMap!: any;
43
+ public _tweens!: Tween[];
44
+ public _add!: Tween[];
45
+ public easeMap!: Record<string, (k: number) => number>;
43
46
  /**
44
47
  * Creates a new TweenManager instance.
45
48
  * @param {Game} game - The game instance this manager belongs to.
@@ -112,8 +115,8 @@ export class TweenManager {
112
115
  * This method removes all active and pending tweens.
113
116
  */
114
117
  public removeAll(): void {
115
- for (let i = 0; i < this._tweens.length; i += 1) {
116
- this._tweens[i].pendingDelete = true;
118
+ for (const tween of this._tweens) {
119
+ tween.pendingDelete = true;
117
120
  }
118
121
  this._add = [];
119
122
  }
@@ -123,27 +126,28 @@ export class TweenManager {
123
126
  * @param {object} obj - The object to remove tweens from.
124
127
  * @param {object[]} children - Optional array of child objects to remove tweens from.
125
128
  */
126
- public removeFrom(obj: any, children: any[] | null = null): void {
127
- let i;
128
- let len;
129
+ public removeFrom(obj: TweenTarget, children: object[] | null = null): void {
129
130
  if (Array.isArray(obj)) {
130
- for (i = 0, len = obj.length; i < len; i += 1) {
131
- this.removeFrom(obj[i]);
131
+ for (const entry of obj) {
132
+ this.removeFrom(entry);
132
133
  }
133
- } else if (obj.type === GROUP && children) {
134
- for (i = 0, len = obj.children.length; i < len; i += 1) {
135
- this.removeFrom(obj.children[i]);
134
+ return;
135
+ }
136
+ const group = obj as { type?: number; children?: TweenTarget[] };
137
+ if (group.type === GROUP && children && group.children) {
138
+ for (const child of group.children) {
139
+ this.removeFrom(child);
136
140
  }
137
- } else {
138
- for (i = 0, len = this._tweens.length; i < len; i += 1) {
139
- if (obj === this._tweens[i].target) {
140
- this.remove(this._tweens[i]);
141
- }
141
+ return;
142
+ }
143
+ for (const tween of this._tweens.slice()) {
144
+ if (obj === tween.target) {
145
+ this.remove(tween);
142
146
  }
143
- for (i = 0, len = this._add.length; i < len; i += 1) {
144
- if (obj === this._add[i].target) {
145
- this.remove(this._add[i]);
146
- }
147
+ }
148
+ for (const tween of this._add.slice()) {
149
+ if (obj === tween.target) {
150
+ this.remove(tween);
147
151
  }
148
152
  }
149
153
  }
@@ -162,7 +166,7 @@ export class TweenManager {
162
166
  * @param {object} object - The object to create a tween for.
163
167
  * @returns {Tween} The created Tween object.
164
168
  */
165
- public create(object: any): Tween {
169
+ public create(object: TweenTarget): Tween {
166
170
  return new Tween(object, this.game, this);
167
171
  }
168
172
 
@@ -171,13 +175,16 @@ export class TweenManager {
171
175
  * @param {Tween | null | undefined} tween - The tween to remove.
172
176
  */
173
177
  public remove(tween: Tween | null | undefined): void {
178
+ if (!tween) {
179
+ return;
180
+ }
174
181
  let i = this._tweens.indexOf(tween);
175
182
  if (i !== -1) {
176
- this._tweens[i].pendingDelete = true;
183
+ this._tweens[i]!.pendingDelete = true;
177
184
  } else {
178
185
  i = this._add.indexOf(tween);
179
186
  if (i !== -1) {
180
- this._add[i].pendingDelete = true;
187
+ this._add[i]!.pendingDelete = true;
181
188
  }
182
189
  }
183
190
  }
@@ -194,7 +201,7 @@ export class TweenManager {
194
201
  }
195
202
  let i = 0;
196
203
  while (i < numTweens) {
197
- if (this._tweens[i].update(this.game.time.time)) {
204
+ if (this._tweens[i]!.update(this.game.time.time)) {
198
205
  i += 1;
199
206
  } else {
200
207
  this._tweens.splice(i, 1);
@@ -215,7 +222,7 @@ export class TweenManager {
215
222
  * @returns {boolean} True if the object is being tweened, false otherwise.
216
223
  */
217
224
  public isTweening(object: unknown): boolean {
218
- return (this._tweens as Tween[]).some((tween: Tween): boolean => tween.target === object);
225
+ return this._tweens.some((tween: Tween): boolean => tween.target === object);
219
226
  }
220
227
 
221
228
  /**
@@ -223,8 +230,8 @@ export class TweenManager {
223
230
  * This method pauses all active tweens.
224
231
  */
225
232
  public _pauseAll(): void {
226
- for (let i = this._tweens.length - 1; i >= 0; i -= 1) {
227
- this._tweens[i]._pause();
233
+ for (const tween of this._tweens) {
234
+ tween._pause();
228
235
  }
229
236
  }
230
237
 
@@ -233,8 +240,8 @@ export class TweenManager {
233
240
  * This method resumes all paused tweens.
234
241
  */
235
242
  public _resumeAll(): void {
236
- for (let i = this._tweens.length - 1; i >= 0; i -= 1) {
237
- this._tweens[i]._resume();
243
+ for (const tween of this._tweens) {
244
+ tween._resume();
238
245
  }
239
246
  }
240
247
 
@@ -243,8 +250,8 @@ export class TweenManager {
243
250
  * This method pauses all active tweens.
244
251
  */
245
252
  public pauseAll(): void {
246
- for (let i = this._tweens.length - 1; i >= 0; i -= 1) {
247
- this._tweens[i].pause();
253
+ for (const tween of this._tweens) {
254
+ tween.pause();
248
255
  }
249
256
  }
250
257
 
@@ -253,8 +260,8 @@ export class TweenManager {
253
260
  * This method resumes all paused tweens.
254
261
  */
255
262
  public resumeAll(): void {
256
- for (let i = this._tweens.length - 1; i >= 0; i -= 1) {
257
- this._tweens[i].resume(true);
263
+ for (const tween of this._tweens) {
264
+ tween.resume();
258
265
  }
259
266
  }
260
267
  }