@vpmedia/phaser 1.119.0 → 1.121.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 (156) hide show
  1. package/dist/index.d.ts +2 -0
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +499 -607
  4. package/dist/index.js.map +1 -1
  5. package/dist/phaser/core/animation.d.ts.map +1 -1
  6. package/dist/phaser/core/animation_manager.d.ts.map +1 -1
  7. package/dist/phaser/core/animation_parser.d.ts +1 -1
  8. package/dist/phaser/core/array_set.d.ts.map +1 -1
  9. package/dist/phaser/core/callback.d.ts +12 -0
  10. package/dist/phaser/core/callback.d.ts.map +1 -0
  11. package/dist/phaser/core/device.d.ts +1 -5
  12. package/dist/phaser/core/device.d.ts.map +1 -1
  13. package/dist/phaser/core/dom.d.ts.map +1 -1
  14. package/dist/phaser/core/factory.d.ts +2 -1
  15. package/dist/phaser/core/factory.d.ts.map +1 -1
  16. package/dist/phaser/core/game.d.ts +3 -0
  17. package/dist/phaser/core/game.d.ts.map +1 -1
  18. package/dist/phaser/core/input.d.ts +11 -9
  19. package/dist/phaser/core/input.d.ts.map +1 -1
  20. package/dist/phaser/core/input_event.d.ts +1 -1
  21. package/dist/phaser/core/input_event.d.ts.map +1 -1
  22. package/dist/phaser/core/input_handler.d.ts.map +1 -1
  23. package/dist/phaser/core/input_mouse.d.ts.map +1 -1
  24. package/dist/phaser/core/input_mspointer.d.ts.map +1 -1
  25. package/dist/phaser/core/input_pointer.d.ts +3 -2
  26. package/dist/phaser/core/input_pointer.d.ts.map +1 -1
  27. package/dist/phaser/core/input_touch.d.ts.map +1 -1
  28. package/dist/phaser/core/loader.d.ts +5 -4
  29. package/dist/phaser/core/loader.d.ts.map +1 -1
  30. package/dist/phaser/core/scale_manager.d.ts +5 -4
  31. package/dist/phaser/core/scale_manager.d.ts.map +1 -1
  32. package/dist/phaser/core/scene_manager.d.ts.map +1 -1
  33. package/dist/phaser/core/signal.d.ts +14 -7
  34. package/dist/phaser/core/signal.d.ts.map +1 -1
  35. package/dist/phaser/core/signal_binding.d.ts +10 -10
  36. package/dist/phaser/core/signal_binding.d.ts.map +1 -1
  37. package/dist/phaser/core/sound.d.ts +3 -3
  38. package/dist/phaser/core/sound.d.ts.map +1 -1
  39. package/dist/phaser/core/sound_manager.d.ts +9 -3
  40. package/dist/phaser/core/sound_manager.d.ts.map +1 -1
  41. package/dist/phaser/core/sound_sprite.d.ts +19 -7
  42. package/dist/phaser/core/sound_sprite.d.ts.map +1 -1
  43. package/dist/phaser/core/timer.d.ts +5 -4
  44. package/dist/phaser/core/timer.d.ts.map +1 -1
  45. package/dist/phaser/core/timer_event.d.ts +3 -2
  46. package/dist/phaser/core/timer_event.d.ts.map +1 -1
  47. package/dist/phaser/core/tween.d.ts +7 -6
  48. package/dist/phaser/core/tween.d.ts.map +1 -1
  49. package/dist/phaser/core/tween_data.d.ts +2 -1
  50. package/dist/phaser/core/tween_data.d.ts.map +1 -1
  51. package/dist/phaser/core/tween_easing.d.ts +31 -31
  52. package/dist/phaser/core/tween_manager.d.ts.map +1 -1
  53. package/dist/phaser/display/bitmap_text.d.ts.map +1 -1
  54. package/dist/phaser/display/button.d.ts +2 -1
  55. package/dist/phaser/display/button.d.ts.map +1 -1
  56. package/dist/phaser/display/canvas/pool.d.ts.map +1 -1
  57. package/dist/phaser/display/canvas/renderer.d.ts +1 -1
  58. package/dist/phaser/display/canvas/renderer.d.ts.map +1 -1
  59. package/dist/phaser/display/canvas/tinter.d.ts.map +1 -1
  60. package/dist/phaser/display/canvas/util.d.ts +2 -2
  61. package/dist/phaser/display/canvas/util.d.ts.map +1 -1
  62. package/dist/phaser/display/display_object.d.ts +8 -6
  63. package/dist/phaser/display/display_object.d.ts.map +1 -1
  64. package/dist/phaser/display/graphics.d.ts +7 -3
  65. package/dist/phaser/display/graphics.d.ts.map +1 -1
  66. package/dist/phaser/display/group.d.ts +1 -1
  67. package/dist/phaser/display/group.d.ts.map +1 -1
  68. package/dist/phaser/display/image.d.ts.map +1 -1
  69. package/dist/phaser/display/sprite_util.d.ts.map +1 -1
  70. package/dist/phaser/display/text.d.ts +5 -0
  71. package/dist/phaser/display/text.d.ts.map +1 -1
  72. package/dist/phaser/display/webgl/base_texture.d.ts.map +1 -1
  73. package/dist/phaser/display/webgl/earcut.d.ts.map +1 -1
  74. package/dist/phaser/display/webgl/fast_sprite_batch.d.ts.map +1 -1
  75. package/dist/phaser/display/webgl/filter_manager.d.ts.map +1 -1
  76. package/dist/phaser/display/webgl/graphics.d.ts.map +1 -1
  77. package/dist/phaser/display/webgl/graphics_data.d.ts.map +1 -1
  78. package/dist/phaser/display/webgl/render_texture.d.ts.map +1 -1
  79. package/dist/phaser/display/webgl/renderer.d.ts.map +1 -1
  80. package/dist/phaser/display/webgl/sprite_batch.d.ts.map +1 -1
  81. package/dist/phaser/display/webgl/texture.d.ts.map +1 -1
  82. package/dist/phaser/geom/polygon.d.ts +11 -5
  83. package/dist/phaser/geom/polygon.d.ts.map +1 -1
  84. package/dist/phaser/geom/rectangle.d.ts.map +1 -1
  85. package/dist/phaser/geom/rounded_rectangle.d.ts.map +1 -1
  86. package/dist/phaser/util/math.d.ts.map +1 -1
  87. package/package.json +1 -1
  88. package/src/index.ts +2 -0
  89. package/src/phaser/core/animation.ts +1 -2
  90. package/src/phaser/core/animation_manager.ts +3 -11
  91. package/src/phaser/core/animation_parser.ts +2 -2
  92. package/src/phaser/core/array_set.ts +8 -10
  93. package/src/phaser/core/cache.ts +2 -2
  94. package/src/phaser/core/callback.ts +14 -0
  95. package/src/phaser/core/device.ts +24 -53
  96. package/src/phaser/core/device_util.ts +6 -6
  97. package/src/phaser/core/dom.ts +10 -7
  98. package/src/phaser/core/factory.ts +3 -2
  99. package/src/phaser/core/game.ts +4 -5
  100. package/src/phaser/core/input.ts +16 -12
  101. package/src/phaser/core/input_event.ts +1 -1
  102. package/src/phaser/core/input_handler.ts +21 -22
  103. package/src/phaser/core/input_mouse.ts +12 -17
  104. package/src/phaser/core/input_mspointer.ts +8 -11
  105. package/src/phaser/core/input_pointer.ts +22 -21
  106. package/src/phaser/core/input_touch.ts +16 -19
  107. package/src/phaser/core/loader.ts +75 -86
  108. package/src/phaser/core/loader_parser.ts +6 -6
  109. package/src/phaser/core/scale_manager.ts +34 -42
  110. package/src/phaser/core/scene_manager.ts +2 -1
  111. package/src/phaser/core/signal.ts +21 -13
  112. package/src/phaser/core/signal_binding.ts +20 -21
  113. package/src/phaser/core/sound.ts +9 -11
  114. package/src/phaser/core/sound_manager.ts +53 -47
  115. package/src/phaser/core/sound_sprite.ts +34 -21
  116. package/src/phaser/core/stage.ts +4 -4
  117. package/src/phaser/core/timer.ts +13 -30
  118. package/src/phaser/core/timer_event.ts +3 -2
  119. package/src/phaser/core/tween.ts +9 -8
  120. package/src/phaser/core/tween_data.ts +15 -41
  121. package/src/phaser/core/tween_easing.test.ts +35 -35
  122. package/src/phaser/core/tween_easing.ts +33 -33
  123. package/src/phaser/core/tween_manager.ts +77 -75
  124. package/src/phaser/display/bitmap_text.ts +7 -11
  125. package/src/phaser/display/button.ts +3 -2
  126. package/src/phaser/display/canvas/graphics.ts +7 -7
  127. package/src/phaser/display/canvas/pool.ts +5 -6
  128. package/src/phaser/display/canvas/renderer.ts +7 -4
  129. package/src/phaser/display/canvas/tinter.ts +9 -11
  130. package/src/phaser/display/canvas/util.test.ts +122 -0
  131. package/src/phaser/display/canvas/util.ts +34 -35
  132. package/src/phaser/display/display_object.ts +30 -43
  133. package/src/phaser/display/graphics.ts +30 -45
  134. package/src/phaser/display/group.ts +1 -1
  135. package/src/phaser/display/image.ts +9 -16
  136. package/src/phaser/display/sprite_util.ts +0 -1
  137. package/src/phaser/display/text.ts +27 -43
  138. package/src/phaser/display/webgl/base_texture.ts +3 -3
  139. package/src/phaser/display/webgl/earcut.ts +2 -3
  140. package/src/phaser/display/webgl/fast_sprite_batch.ts +1 -2
  141. package/src/phaser/display/webgl/filter_manager.ts +6 -18
  142. package/src/phaser/display/webgl/filter_texture.ts +3 -3
  143. package/src/phaser/display/webgl/graphics.ts +28 -38
  144. package/src/phaser/display/webgl/graphics_data.ts +0 -1
  145. package/src/phaser/display/webgl/render_texture.ts +4 -13
  146. package/src/phaser/display/webgl/renderer.ts +0 -1
  147. package/src/phaser/display/webgl/sprite_batch.ts +2 -9
  148. package/src/phaser/display/webgl/texture.ts +12 -15
  149. package/src/phaser/geom/point.ts +1 -1
  150. package/src/phaser/geom/polygon.test.ts +59 -0
  151. package/src/phaser/geom/polygon.ts +43 -45
  152. package/src/phaser/geom/rectangle.ts +3 -15
  153. package/src/phaser/geom/rounded_rectangle.ts +2 -4
  154. package/src/phaser/geom/util/point.ts +1 -1
  155. package/src/phaser/geom/util/rectangle.ts +2 -2
  156. package/src/phaser/util/math.ts +1 -5
@@ -1,9 +1,10 @@
1
1
  import { Point } from '../geom/point.js';
2
2
  import { Rectangle } from '../geom/rectangle.js';
3
3
  import { RENDER_CANVAS, SCALE_EXACT_FIT, SCALE_OFF, SCALE_RESIZE, SCALE_SHOW_ALL, SCALE_USER } from './const.js';
4
- import type { Game } from './game.js';
4
+ import type { Game, GameConfig } from './game.js';
5
5
  import { DOM } from './dom.js';
6
6
  import { Signal } from './signal.js';
7
+ import type { AppliedCallback, Callback } from './callback.js';
7
8
 
8
9
  /** The gap the canvas leaves around itself inside its parent. */
9
10
  export type ScaleMargin = { left: number; top: number; right: number; bottom: number; x: number; y: number };
@@ -61,7 +62,7 @@ export class ScaleManager {
61
62
  public parentScaleFactor!: Point;
62
63
  public trackParentInterval!: number;
63
64
  public onSizeChange!: Signal;
64
- public onResize!: Function | null;
65
+ public onResize!: Callback | null;
65
66
  public onResizeContext!: unknown;
66
67
  public _pendingScaleMode!: number | null;
67
68
  public _fullScreenRestore!: { targetWidth: string; targetHeight: string } | null;
@@ -179,22 +180,21 @@ export class ScaleManager {
179
180
  compat.clickTrampoline = '';
180
181
  }
181
182
  // Configure event listeners
182
- const scope = this;
183
183
  this._orientationChange = (event): void => {
184
- scope.orientationChange(event);
184
+ this.orientationChange(event);
185
185
  };
186
186
  this._windowResize = (event): void => {
187
- scope.windowResize(event);
187
+ this.windowResize(event);
188
188
  };
189
189
  // This does not appear to be on the standards track
190
190
  globalThis.addEventListener('orientationchange', this._orientationChange, false);
191
191
  window.addEventListener('resize', this._windowResize, false);
192
192
  if (this.compatibility.supportsFullScreen) {
193
193
  this._fullScreenChange = (event): void => {
194
- scope.fullScreenChange(event);
194
+ this.fullScreenChange(event);
195
195
  };
196
196
  this._fullScreenError = (event): void => {
197
- scope.fullScreenError(event);
197
+ this.fullScreenError(event);
198
198
  };
199
199
  document.addEventListener('webkitfullscreenchange', this._fullScreenChange, false);
200
200
  document.addEventListener('mozfullscreenchange', this._fullScreenChange, false);
@@ -223,7 +223,7 @@ export class ScaleManager {
223
223
  * TBD.
224
224
  * @param {object} config - TBD.
225
225
  */
226
- public parseConfig(config: any): void {
226
+ public parseConfig(config: GameConfig): void {
227
227
  if (config.scaleMode !== undefined) {
228
228
  if (this._booted) {
229
229
  this.scaleMode = config.scaleMode;
@@ -234,11 +234,7 @@ export class ScaleManager {
234
234
  if (config.fullScreenScaleMode !== undefined) {
235
235
  this.fullScreenScaleMode = config.fullScreenScaleMode;
236
236
  }
237
- if (config.fullScreenTarget) {
238
- this.fullScreenTarget = config.fullScreenTarget;
239
- } else {
240
- this.fullScreenTarget = document.documentElement;
241
- }
237
+ this.fullScreenTarget = config.fullScreenTarget ?? document.documentElement;
242
238
  }
243
239
 
244
240
  /**
@@ -252,7 +248,7 @@ export class ScaleManager {
252
248
  if (this.game.parent !== '') {
253
249
  if (typeof this.game.parent === 'string') {
254
250
  // hopefully an element ID
255
- target = document.getElementById(this.game.parent);
251
+ target = document.querySelector<HTMLElement>(`#${CSS.escape(this.game.parent)}`);
256
252
  } else if (this.game.parent && this.game.parent.nodeType === 1) {
257
253
  // quick test for a HTMLelement
258
254
  target = this.game.parent;
@@ -334,7 +330,7 @@ export class ScaleManager {
334
330
  * @param {Function} callback - TBD.
335
331
  * @param {object} context - TBD.
336
332
  */
337
- public setResizeCallback(callback: Function, context: unknown): void {
333
+ public setResizeCallback(callback: Callback, context: unknown): void {
338
334
  this.onResize = callback;
339
335
  this.onResizeContext = context;
340
336
  }
@@ -398,7 +394,7 @@ export class ScaleManager {
398
394
  const orientationChanged = this.updateOrientationState();
399
395
  if (boundsChanged || orientationChanged) {
400
396
  if (this.onResize) {
401
- this.onResize.call(this.onResizeContext, this, bounds);
397
+ (this.onResize as AppliedCallback).call(this.onResizeContext, this, bounds);
402
398
  }
403
399
  this.updateLayout();
404
400
  this.signalSizeChange();
@@ -572,8 +568,8 @@ export class ScaleManager {
572
568
  this.height = Math.min(this.height, bounds.height);
573
569
  }
574
570
  // Always truncate / force to integer
575
- this.width |= 0;
576
- this.height |= 0;
571
+ this.width = Math.trunc(this.width);
572
+ this.height = Math.trunc(this.height);
577
573
  this.reflowCanvas();
578
574
  }
579
575
 
@@ -743,12 +739,9 @@ export class ScaleManager {
743
739
  const bounds = this.getParentBounds(this._tempBounds);
744
740
  const { width } = bounds;
745
741
  const { height } = bounds;
746
- let multiplier;
747
- if (expanding) {
748
- multiplier = Math.max(height / this.game.height, width / this.game.width);
749
- } else {
750
- multiplier = Math.min(height / this.game.height, width / this.game.width);
751
- }
742
+ const multiplier = expanding
743
+ ? Math.max(height / this.game.height, width / this.game.width)
744
+ : Math.min(height / this.game.height, width / this.game.width);
752
745
  this.width = Math.round(this.game.width * multiplier);
753
746
  this.height = Math.round(this.game.height * multiplier);
754
747
  }
@@ -796,9 +789,8 @@ export class ScaleManager {
796
789
  }
797
790
  if (!this.compatibility.supportsFullScreen) {
798
791
  // Error is called in timeout to emulate the real fullscreenerror event better
799
- const scope = this;
800
792
  setTimeout((): void => {
801
- scope.fullScreenError({} as any);
793
+ this.fullScreenError(new Event('fullscreenerror'));
802
794
  }, 10);
803
795
  return false;
804
796
  }
@@ -863,9 +855,8 @@ export class ScaleManager {
863
855
  if (fsTarget && fsTarget.parentNode) {
864
856
  // Make sure to cleanup synthetic target for sure;
865
857
  // swap the canvas back to the parent.
866
- const parent = fsTarget.parentNode;
867
- parent.insertBefore(this.game.canvas, fsTarget);
868
- parent.removeChild(fsTarget);
858
+ fsTarget.before(this.game.canvas);
859
+ fsTarget.remove();
869
860
  }
870
861
  this._createdFullScreenTarget = null;
871
862
  }
@@ -880,16 +871,17 @@ export class ScaleManager {
880
871
  return;
881
872
  }
882
873
  if (enteringFullscreen) {
883
- if (this._createdFullScreenTarget || this.fullScreenScaleMode === SCALE_EXACT_FIT) {
884
- // Resize target, as long as it's not the canvas
885
- if (fsTarget !== this.game.canvas) {
886
- this._fullScreenRestore = {
887
- targetWidth: fsTarget.style.width,
888
- targetHeight: fsTarget.style.height,
889
- };
890
- fsTarget.style.width = '100%';
891
- fsTarget.style.height = '100%';
892
- }
874
+ // Resize target, as long as it's not the canvas
875
+ if (
876
+ (this._createdFullScreenTarget || this.fullScreenScaleMode === SCALE_EXACT_FIT) &&
877
+ fsTarget !== this.game.canvas
878
+ ) {
879
+ this._fullScreenRestore = {
880
+ targetWidth: fsTarget.style.width,
881
+ targetHeight: fsTarget.style.height,
882
+ };
883
+ fsTarget.style.width = '100%';
884
+ fsTarget.style.height = '100%';
893
885
  }
894
886
  } else {
895
887
  // Have restore information
@@ -1044,9 +1036,9 @@ export class ScaleManager {
1044
1036
  public get isFullScreen(): boolean {
1045
1037
  return Boolean(
1046
1038
  document.fullscreenElement ??
1047
- (document as any).webkitFullscreenElement ??
1048
- (document as any).mozFullScreenElement ??
1049
- (document as any).msFullscreenElement
1039
+ document.webkitFullscreenElement ??
1040
+ document.mozFullScreenElement ??
1041
+ document.msFullscreenElement
1050
1042
  );
1051
1043
  }
1052
1044
 
@@ -88,7 +88,8 @@ export class SceneManager {
88
88
  public add(key: string, state: SceneDefinition, autoStart = false): SceneState {
89
89
  let newState: SceneState;
90
90
  if (typeof state === 'function') {
91
- newState = new state(this.game);
91
+ const SceneClass = state;
92
+ newState = new SceneClass(this.game);
92
93
  } else {
93
94
  newState = state;
94
95
  if (!(state instanceof Scene)) {
@@ -1,5 +1,14 @@
1
1
  import { SignalBinding } from './signal_binding.js';
2
2
 
3
+ /**
4
+ * A signal listener. The rest parameter is `never[]` so a handler with concrete parameters is
5
+ * assignable; the dispatcher widens it before applying the arguments.
6
+ */
7
+ export type SignalListener = (...args: never[]) => unknown;
8
+
9
+ /** The listener as the dispatcher calls it, once the concrete parameter types are behind us. */
10
+ export type DispatchedListener = (...args: unknown[]) => unknown;
11
+
3
12
  export class Signal {
4
13
  public _bindings: SignalBinding[] | null = null;
5
14
  public _prevParams: unknown[] | null = null;
@@ -19,7 +28,7 @@ export class Signal {
19
28
  * @param {string} fnName - The name of the function this validation is for.
20
29
  * @throws {Error} If the listener is not a function.
21
30
  */
22
- public validateListener(listener: Function, fnName: string): void {
31
+ public validateListener(listener: SignalListener, fnName: string): void {
23
32
  if (typeof listener !== 'function') {
24
33
  throw new TypeError(
25
34
  'Signal: listener is a required param of {fn}() and should be a Function.'.replace('{fn}', fnName)
@@ -38,11 +47,11 @@ export class Signal {
38
47
  * @throws {Error} If the listener is already registered with a different once setting.
39
48
  */
40
49
  public _registerListener(
41
- listener: Function,
50
+ listener: SignalListener,
42
51
  isOnce = false,
43
- listenerContext: unknown | null = null,
52
+ listenerContext: unknown = null,
44
53
  priority = 0,
45
- args: any = null
54
+ args: unknown[] | null = null
46
55
  ): SignalBinding {
47
56
  const prevIndex = this._indexOfListener(listener, listenerContext);
48
57
  let binding;
@@ -86,7 +95,7 @@ export class Signal {
86
95
  * @param {object} context - The context of the listener.
87
96
  * @returns {number} The index of the listener in the bindings array, or -1 if not found.
88
97
  */
89
- public _indexOfListener(listener: Function, context: unknown = null): number {
98
+ public _indexOfListener(listener: SignalListener, context: unknown = null): number {
90
99
  if (!this._bindings) {
91
100
  return -1;
92
101
  }
@@ -108,7 +117,7 @@ export class Signal {
108
117
  * @param {object} context - The context of the listener.
109
118
  * @returns {boolean} True if the listener is registered, false otherwise.
110
119
  */
111
- public has(listener: Function, context: unknown = null): boolean {
120
+ public has(listener: SignalListener, context: unknown = null): boolean {
112
121
  return this._indexOfListener(listener, context) !== -1;
113
122
  }
114
123
 
@@ -121,8 +130,8 @@ export class Signal {
121
130
  * @returns {SignalBinding} The binding for this listener.
122
131
  */
123
132
  public add(
124
- listener: Function,
125
- listenerContext: unknown | null = null,
133
+ listener: SignalListener,
134
+ listenerContext: unknown = null,
126
135
  priority = 0,
127
136
  ...args: unknown[]
128
137
  ): SignalBinding {
@@ -139,8 +148,8 @@ export class Signal {
139
148
  * @returns {SignalBinding} The binding for this listener.
140
149
  */
141
150
  public addOnce(
142
- listener: Function,
143
- listenerContext: unknown | null = null,
151
+ listener: SignalListener,
152
+ listenerContext: unknown = null,
144
153
  priority = 0,
145
154
  ...args: unknown[]
146
155
  ): SignalBinding {
@@ -154,7 +163,7 @@ export class Signal {
154
163
  * @param {object} context - The context of the listener.
155
164
  * @returns {Function} The removed listener function.
156
165
  */
157
- public remove(listener: Function, context: unknown = null): Function {
166
+ public remove(listener: SignalListener, context: unknown = null): SignalListener {
158
167
  this.validateListener(listener, 'remove');
159
168
  const i = this._indexOfListener(listener, context);
160
169
  if (i !== -1 && this._bindings) {
@@ -267,9 +276,8 @@ export class Signal {
267
276
  * @returns {Function} A function that will dispatch the signal with the correct context.
268
277
  */
269
278
  public get boundDispatch() {
270
- const _this = this;
271
279
  this._boundDispatch ??= (...rest: unknown[]): void => {
272
- _this.dispatch(...rest);
280
+ this.dispatch(...rest);
273
281
  };
274
282
  return this._boundDispatch;
275
283
  }
@@ -1,14 +1,14 @@
1
- import type { Signal } from './signal.js';
1
+ import type { DispatchedListener, Signal, SignalListener } from './signal.js';
2
2
  export class SignalBinding {
3
- public _signal: any;
4
- public _listener: any;
5
- public _args: any;
3
+ public _signal: Signal | null;
4
+ public _listener: SignalListener | null;
5
+ public _args: unknown[] | null;
6
6
  public _priority: number;
7
7
  public _isOnce: boolean;
8
8
  public context: unknown;
9
9
  public callCount: number;
10
10
  public active: boolean;
11
- public params: any;
11
+ public params: unknown[] | null;
12
12
 
13
13
  /**
14
14
  * TBD.
@@ -21,11 +21,11 @@ export class SignalBinding {
21
21
  */
22
22
  public constructor(
23
23
  signal: Signal,
24
- listener: Function,
24
+ listener: SignalListener,
25
25
  isOnce = false,
26
- listenerContext: unknown | null = null,
26
+ listenerContext: unknown = null,
27
27
  priority = 0,
28
- args: any = null
28
+ args: unknown[] | null = null
29
29
  ) {
30
30
  this._signal = signal;
31
31
  this._listener = listener;
@@ -43,15 +43,14 @@ export class SignalBinding {
43
43
  * @param {any[]} paramsArr - TBD.
44
44
  * @returns {Function} TBD.
45
45
  */
46
- public execute(paramsArr: any[]) {
46
+ public execute(paramsArr: unknown[]): unknown {
47
47
  let handlerReturn;
48
- let params;
49
- if (this.active && Boolean(this._listener)) {
50
- params = this.params ? this.params.concat(paramsArr) : paramsArr;
48
+ if (this.active && this._listener) {
49
+ let params = this.params ? [...this.params, ...paramsArr] : paramsArr;
51
50
  if (this._args) {
52
- params = params.concat(this._args);
51
+ params = [...params, ...this._args];
53
52
  }
54
- handlerReturn = this._listener.apply(this.context, params);
53
+ handlerReturn = (this._listener as DispatchedListener).apply(this.context, params);
55
54
  this.callCount += 1;
56
55
  if (this._isOnce) {
57
56
  this.detach();
@@ -64,8 +63,8 @@ export class SignalBinding {
64
63
  * TBD.
65
64
  * @returns {Function} TBD.
66
65
  */
67
- public detach() {
68
- return this.isBound() ? this._signal.remove(this._listener, this.context) : null;
66
+ public detach(): SignalListener | null {
67
+ return this.isBound() ? this._signal!.remove(this._listener!, this.context) : null;
69
68
  }
70
69
 
71
70
  /**
@@ -88,7 +87,7 @@ export class SignalBinding {
88
87
  * TBD.
89
88
  * @returns {Function} TBD.
90
89
  */
91
- public getListener() {
90
+ public getListener(): SignalListener | null {
92
91
  return this._listener;
93
92
  }
94
93
 
@@ -96,7 +95,7 @@ export class SignalBinding {
96
95
  * TBD.
97
96
  * @returns {Signal} TBD.
98
97
  */
99
- public getSignal() {
98
+ public getSignal(): Signal | null {
100
99
  return this._signal;
101
100
  }
102
101
 
@@ -104,9 +103,9 @@ export class SignalBinding {
104
103
  * TBD.
105
104
  */
106
105
  public _destroy(): void {
107
- delete this._signal;
108
- delete this._listener;
109
- delete this.context;
106
+ this._signal = null;
107
+ this._listener = null;
108
+ this.context = null;
110
109
  }
111
110
 
112
111
  /**
@@ -70,7 +70,6 @@ export class Sound {
70
70
  * @param {boolean} connect - Whether to connect to the master gain node.
71
71
  */
72
72
  public constructor(game: Game, key: string, volume = 1, loop = false, connect: boolean | null = null) {
73
- // TODO
74
73
  // https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API/Migrating_from_webkitAudioContext
75
74
  connect ??= game.sound.connectToMaster;
76
75
  this.game = game;
@@ -173,8 +172,7 @@ export class Sound {
173
172
  /**
174
173
  * Handler for when the sound ends.
175
174
  */
176
- public onEndedHandler(): void {
177
- this._sound.onended = null;
175
+ public onEndedHandler = (): void => {
178
176
  this.isPlaying = false;
179
177
  this.currentTime = this.durationMS;
180
178
  this.stop();
@@ -202,7 +200,7 @@ export class Sound {
202
200
  this.onMarkerComplete.dispose();
203
201
  this.onFadeComplete.dispose();
204
202
  }
205
- }
203
+ };
206
204
 
207
205
  /**
208
206
  * Updates the sound state.
@@ -341,7 +339,7 @@ export class Sound {
341
339
  this._sound.loop = true;
342
340
  }
343
341
  if (!this.loop && marker === '') {
344
- this._sound.onended = this.onEndedHandler.bind(this);
342
+ this._sound.addEventListener('ended', this.onEndedHandler, { once: true });
345
343
  }
346
344
  this.totalDuration = this._sound.buffer?.duration ?? 0;
347
345
  if (this.duration === 0) {
@@ -364,7 +362,7 @@ export class Sound {
364
362
  } else {
365
363
  this.pendingPlayback = true;
366
364
  if (this.game.cache.getSound(this.key)?.isDecoding === false) {
367
- this.game.sound.decode(this.key);
365
+ void this.game.sound.decode(this.key);
368
366
  }
369
367
  }
370
368
  return this;
@@ -412,7 +410,7 @@ export class Sound {
412
410
  this._sound.loop = true;
413
411
  }
414
412
  if (!this.loop && this.currentMarker === '') {
415
- this._sound.onended = this.onEndedHandler.bind(this);
413
+ this._sound.addEventListener('ended', this.onEndedHandler, { once: true });
416
414
  }
417
415
  const duration = this.duration - this.pausedPosition / 1000;
418
416
  if (this._sound.start === undefined) {
@@ -540,12 +538,12 @@ export class Sound {
540
538
  /**
541
539
  * Sets the muted state of the sound.
542
540
  */
543
- public set mute(value) {
544
- value = value ?? false;
545
- if (value === this._muted) {
541
+ public set mute(value: boolean | null) {
542
+ const muted = value ?? false;
543
+ if (muted === this._muted) {
546
544
  return;
547
545
  }
548
- if (value) {
546
+ if (muted) {
549
547
  this._muted = true;
550
548
  this._muteVolume = this._tempVolume;
551
549
  this.gainNode.gain.value = 0;
@@ -11,6 +11,7 @@ import { ENGINE_ERROR_CREATING_AUDIO_CONTEXT } from './error_code.js';
11
11
  import { Signal } from './signal.js';
12
12
  import { Sound } from './sound.js';
13
13
  import { SoundSprite } from './sound_sprite.js';
14
+ import type { Callback } from './callback.js';
14
15
 
15
16
  export class SoundManager {
16
17
  public game!: Game;
@@ -28,7 +29,7 @@ export class SoundManager {
28
29
  public _sounds!: Sound[];
29
30
  public _watchList!: ArraySet;
30
31
  public _watching!: boolean;
31
- public _watchCallback!: Function | null;
32
+ public _watchCallback!: Callback | null;
32
33
  public _watchContext!: unknown;
33
34
  public masterGain!: GainNode;
34
35
  public _muteVolume!: number;
@@ -83,10 +84,11 @@ export class SoundManager {
83
84
  const typedError = error instanceof Error ? error : new Error(String(error));
84
85
  this.game.logger.fatal('SoundManager', { error: typedError });
85
86
  }
86
- } else if ((globalThis as any).webkitAudioContext) {
87
+ } else if (globalThis.webkitAudioContext) {
87
88
  try {
88
89
  this.game.logger.info('initWebkitAudioContext');
89
- this.context = new (globalThis as any).webkitAudioContext();
90
+ const WebkitAudioContext = globalThis.webkitAudioContext;
91
+ this.context = new WebkitAudioContext();
90
92
  this.type = AUDIO_WEBKIT;
91
93
  } catch (error) {
92
94
  this.context = null;
@@ -101,11 +103,10 @@ export class SoundManager {
101
103
  return;
102
104
  }
103
105
  this.baseLatency = this.context.baseLatency ?? 256 / (this.context.sampleRate ?? 44_100);
104
- if (this.context.createGain === undefined) {
105
- this.masterGain = (this.context as unknown as { createGainNode: () => GainNode }).createGainNode();
106
- } else {
107
- this.masterGain = this.context.createGain();
108
- }
106
+ this.masterGain =
107
+ this.context.createGain === undefined
108
+ ? (this.context as unknown as { createGainNode: () => GainNode }).createGainNode()
109
+ : this.context.createGain();
109
110
  this.masterGain.gain.value = 1;
110
111
  this.masterGain.connect(this.context.destination);
111
112
  // handle audio state unlock
@@ -185,6 +186,14 @@ export class SoundManager {
185
186
  * @param {Event} event - The DOM event that triggered the unlock.
186
187
  */
187
188
  public onUnlockEvent = (event: Event): void => {
189
+ void this.resumeContext(event);
190
+ };
191
+
192
+ /**
193
+ * Resumes the audio context a user gesture has just unlocked.
194
+ * @param {Event} event - The DOM event that triggered the unlock.
195
+ */
196
+ public async resumeContext(event: Event): Promise<void> {
188
197
  const initialState = this.context!.state;
189
198
  if (initialState !== 'suspended' && initialState !== 'interrupted') {
190
199
  this.game.logger.info('onUnlockResumeDenied', {
@@ -200,29 +209,28 @@ export class SoundManager {
200
209
  isLocked: this.isLocked,
201
210
  event,
202
211
  });
203
- this.context!.resume()
204
- .then((): void => {
205
- this.game.logger.info('onContextResumeResult', {
206
- initialState,
207
- state: this.context!.state,
208
- isLocked: this.isLocked,
209
- });
210
- this.removeUnlockHandlers();
211
- })
212
- .catch((error: unknown): void => {
213
- this.game.logger.info('onContextResumeReject', {
214
- initialState,
215
- state: this.context!.state,
216
- isLocked: this.isLocked,
217
- error,
218
- });
219
- this.removeUnlockHandlers();
220
- this.game.logger.fatal('SoundManager', {
221
- error,
222
- tags: { 'audio.initialState': initialState, 'audio.state': this.context!.state },
223
- });
212
+ try {
213
+ await this.context!.resume();
214
+ this.game.logger.info('onContextResumeResult', {
215
+ initialState,
216
+ state: this.context!.state,
217
+ isLocked: this.isLocked,
224
218
  });
225
- };
219
+ this.removeUnlockHandlers();
220
+ } catch (error: unknown) {
221
+ this.game.logger.info('onContextResumeReject', {
222
+ initialState,
223
+ state: this.context!.state,
224
+ isLocked: this.isLocked,
225
+ error,
226
+ });
227
+ this.removeUnlockHandlers();
228
+ this.game.logger.fatal('SoundManager', {
229
+ error,
230
+ tags: { 'audio.initialState': initialState, 'audio.state': this.context!.state },
231
+ });
232
+ }
233
+ }
226
234
 
227
235
  /**
228
236
  * Stops all sounds in the manager.
@@ -270,7 +278,7 @@ export class SoundManager {
270
278
  * Decodes an audio file for playback.
271
279
  * @param {string} key - The key of the sound to decode.
272
280
  */
273
- public decode(key: string): void {
281
+ public async decode(key: string): Promise<void> {
274
282
  const soundData = this.game.cache.getSoundData(key);
275
283
  if (!(soundData instanceof ArrayBuffer)) {
276
284
  return;
@@ -279,21 +287,19 @@ export class SoundManager {
279
287
  return;
280
288
  }
281
289
  this.game.cache.updateSound(key, 'isDecoding', true);
282
- this.context!.decodeAudioData(soundData)
283
- .then((buffer: AudioBuffer): void => {
284
- this.game.cache.decodedSound(key, buffer);
285
- })
286
- .catch((error: unknown): void => {
287
- const typedError = error instanceof Error ? error : new Error(String(error));
288
- this.game.logger.fatal('SoundManager', { error: typedError, tags: { 'asset.key': key } });
289
- if (typedError.name === 'InvalidStateError') {
290
- addPageLifecycleCallback(PAGE_LIFECYCLE_STATE_ACTIVE, (): void => {
291
- this.decode(key);
292
- });
293
- } else if (typedError.name === 'EncodingError') {
294
- this._watchList.remove(key);
295
- }
296
- });
290
+ try {
291
+ this.game.cache.decodedSound(key, await this.context!.decodeAudioData(soundData));
292
+ } catch (error: unknown) {
293
+ const typedError = error instanceof Error ? error : new Error(String(error));
294
+ this.game.logger.fatal('SoundManager', { error: typedError, tags: { 'asset.key': key } });
295
+ if (typedError.name === 'InvalidStateError') {
296
+ addPageLifecycleCallback(PAGE_LIFECYCLE_STATE_ACTIVE, (): void => {
297
+ void this.decode(key);
298
+ });
299
+ } else if (typedError.name === 'EncodingError') {
300
+ this._watchList.remove(key);
301
+ }
302
+ }
297
303
  }
298
304
 
299
305
  /**
@@ -304,7 +310,7 @@ export class SoundManager {
304
310
  */
305
311
  public setDecodedCallback(
306
312
  files: string | string[] | Sound | Sound[],
307
- callback: Function,
313
+ callback: Callback,
308
314
  callbackContext: unknown
309
315
  ): void {
310
316
  const candidates: (string | Sound)[] = typeof files === 'string' ? [files] : Array.isArray(files) ? files : [files];