@vpmedia/phaser 1.123.0 → 1.125.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 (221) hide show
  1. package/dist/index.js +635 -623
  2. package/dist/index.js.map +1 -1
  3. package/dist/phaser/core/animation.d.ts +3 -3
  4. package/dist/phaser/core/animation.d.ts.map +1 -1
  5. package/dist/phaser/core/animation_manager.d.ts.map +1 -1
  6. package/dist/phaser/core/animation_parser.d.ts +27 -2
  7. package/dist/phaser/core/animation_parser.d.ts.map +1 -1
  8. package/dist/phaser/core/array_set.d.ts +11 -11
  9. package/dist/phaser/core/array_set.d.ts.map +1 -1
  10. package/dist/phaser/core/cache.d.ts.map +1 -1
  11. package/dist/phaser/core/callback.d.ts.map +1 -1
  12. package/dist/phaser/core/dom.d.ts +125 -19
  13. package/dist/phaser/core/dom.d.ts.map +1 -1
  14. package/dist/phaser/core/factory.d.ts +3 -2
  15. package/dist/phaser/core/factory.d.ts.map +1 -1
  16. package/dist/phaser/core/frame_data.d.ts.map +1 -1
  17. package/dist/phaser/core/input.d.ts +3 -2
  18. package/dist/phaser/core/input.d.ts.map +1 -1
  19. package/dist/phaser/core/input_handler.d.ts.map +1 -1
  20. package/dist/phaser/core/input_mouse.d.ts +5 -1
  21. package/dist/phaser/core/input_mouse.d.ts.map +1 -1
  22. package/dist/phaser/core/input_pointer.d.ts +3 -3
  23. package/dist/phaser/core/input_pointer.d.ts.map +1 -1
  24. package/dist/phaser/core/input_touch.d.ts +8 -8
  25. package/dist/phaser/core/input_touch.d.ts.map +1 -1
  26. package/dist/phaser/core/loader.d.ts +11 -2
  27. package/dist/phaser/core/loader.d.ts.map +1 -1
  28. package/dist/phaser/core/loader_parser.d.ts +14 -1
  29. package/dist/phaser/core/loader_parser.d.ts.map +1 -1
  30. package/dist/phaser/core/raf.d.ts +1 -1
  31. package/dist/phaser/core/raf.d.ts.map +1 -1
  32. package/dist/phaser/core/scale_manager.d.ts.map +1 -1
  33. package/dist/phaser/core/scene_manager.d.ts.map +1 -1
  34. package/dist/phaser/core/signal.d.ts.map +1 -1
  35. package/dist/phaser/core/sound.d.ts.map +1 -1
  36. package/dist/phaser/core/sound_manager.d.ts +1 -1
  37. package/dist/phaser/core/sound_manager.d.ts.map +1 -1
  38. package/dist/phaser/core/stage.d.ts +16 -6
  39. package/dist/phaser/core/stage.d.ts.map +1 -1
  40. package/dist/phaser/core/time.d.ts.map +1 -1
  41. package/dist/phaser/core/timer.d.ts +1 -1
  42. package/dist/phaser/core/timer.d.ts.map +1 -1
  43. package/dist/phaser/core/tween.d.ts +13 -5
  44. package/dist/phaser/core/tween.d.ts.map +1 -1
  45. package/dist/phaser/core/tween_data.d.ts +9 -7
  46. package/dist/phaser/core/tween_data.d.ts.map +1 -1
  47. package/dist/phaser/core/tween_manager.d.ts.map +1 -1
  48. package/dist/phaser/display/button.d.ts +6 -5
  49. package/dist/phaser/display/button.d.ts.map +1 -1
  50. package/dist/phaser/display/canvas/buffer.d.ts +1 -1
  51. package/dist/phaser/display/canvas/buffer.d.ts.map +1 -1
  52. package/dist/phaser/display/canvas/masker.d.ts +1 -1
  53. package/dist/phaser/display/canvas/masker.d.ts.map +1 -1
  54. package/dist/phaser/display/canvas/pool.d.ts +2 -2
  55. package/dist/phaser/display/canvas/pool.d.ts.map +1 -1
  56. package/dist/phaser/display/canvas/renderer.d.ts +9 -8
  57. package/dist/phaser/display/canvas/renderer.d.ts.map +1 -1
  58. package/dist/phaser/display/display_object.d.ts +5 -3
  59. package/dist/phaser/display/display_object.d.ts.map +1 -1
  60. package/dist/phaser/display/graphics.d.ts +1 -1
  61. package/dist/phaser/display/graphics.d.ts.map +1 -1
  62. package/dist/phaser/display/group.d.ts +1 -1
  63. package/dist/phaser/display/group.d.ts.map +1 -1
  64. package/dist/phaser/display/image.d.ts +6 -5
  65. package/dist/phaser/display/image.d.ts.map +1 -1
  66. package/dist/phaser/display/sprite_util.d.ts +3 -2
  67. package/dist/phaser/display/sprite_util.d.ts.map +1 -1
  68. package/dist/phaser/display/text.d.ts +2 -16
  69. package/dist/phaser/display/text.d.ts.map +1 -1
  70. package/dist/phaser/display/webgl/abstract_filter.d.ts +14 -8
  71. package/dist/phaser/display/webgl/abstract_filter.d.ts.map +1 -1
  72. package/dist/phaser/display/webgl/blend_manager.d.ts +2 -3
  73. package/dist/phaser/display/webgl/blend_manager.d.ts.map +1 -1
  74. package/dist/phaser/display/webgl/earcut.d.ts.map +1 -1
  75. package/dist/phaser/display/webgl/fast_sprite_batch.d.ts +11 -5
  76. package/dist/phaser/display/webgl/fast_sprite_batch.d.ts.map +1 -1
  77. package/dist/phaser/display/webgl/filter_manager.d.ts +6 -4
  78. package/dist/phaser/display/webgl/filter_manager.d.ts.map +1 -1
  79. package/dist/phaser/display/webgl/filter_texture.d.ts +4 -4
  80. package/dist/phaser/display/webgl/filter_texture.d.ts.map +1 -1
  81. package/dist/phaser/display/webgl/graphics.d.ts +2 -2
  82. package/dist/phaser/display/webgl/graphics.d.ts.map +1 -1
  83. package/dist/phaser/display/webgl/mask_manager.d.ts +3 -2
  84. package/dist/phaser/display/webgl/mask_manager.d.ts.map +1 -1
  85. package/dist/phaser/display/webgl/render_texture.d.ts +11 -8
  86. package/dist/phaser/display/webgl/render_texture.d.ts.map +1 -1
  87. package/dist/phaser/display/webgl/renderer.d.ts +8 -4
  88. package/dist/phaser/display/webgl/renderer.d.ts.map +1 -1
  89. package/dist/phaser/display/webgl/shader/normal.d.ts +28 -3
  90. package/dist/phaser/display/webgl/shader/normal.d.ts.map +1 -1
  91. package/dist/phaser/display/webgl/sprite_batch.d.ts +6 -5
  92. package/dist/phaser/display/webgl/sprite_batch.d.ts.map +1 -1
  93. package/dist/phaser/display/webgl/stencil_manager.d.ts +3 -3
  94. package/dist/phaser/display/webgl/stencil_manager.d.ts.map +1 -1
  95. package/dist/phaser/display/webgl/texture.d.ts.map +1 -1
  96. package/dist/phaser/display/webgl/texture_util.d.ts +2 -2
  97. package/dist/phaser/display/webgl/texture_util.d.ts.map +1 -1
  98. package/dist/phaser/display/webgl/util.d.ts +1 -1
  99. package/dist/phaser/display/webgl/util.d.ts.map +1 -1
  100. package/dist/phaser/geom/circle.d.ts.map +1 -1
  101. package/dist/phaser/geom/line.d.ts +10 -1
  102. package/dist/phaser/geom/line.d.ts.map +1 -1
  103. package/dist/phaser/geom/rectangle.d.ts.map +1 -1
  104. package/dist/phaser/geom/util/circle.d.ts +2 -1
  105. package/dist/phaser/geom/util/circle.d.ts.map +1 -1
  106. package/dist/phaser/geom/util/point.d.ts +1 -1
  107. package/dist/phaser/geom/util/point.d.ts.map +1 -1
  108. package/dist/phaser/geom/util/rectangle.d.ts +1 -1
  109. package/dist/phaser/geom/util/rectangle.d.ts.map +1 -1
  110. package/dist/phaser/util/math.d.ts.map +1 -1
  111. package/package.json +1 -1
  112. package/src/phaser/core/animation.ts +11 -11
  113. package/src/phaser/core/animation_manager.ts +12 -12
  114. package/src/phaser/core/animation_parser.ts +39 -37
  115. package/src/phaser/core/array_set.ts +28 -28
  116. package/src/phaser/core/cache.test.ts +33 -33
  117. package/src/phaser/core/cache.ts +7 -7
  118. package/src/phaser/core/callback.ts +1 -0
  119. package/src/phaser/core/callback_context.test.ts +8 -8
  120. package/src/phaser/core/device_util.ts +4 -4
  121. package/src/phaser/core/dom.ts +38 -24
  122. package/src/phaser/core/factory.ts +9 -2
  123. package/src/phaser/core/frame_data.test.ts +23 -23
  124. package/src/phaser/core/frame_data.ts +2 -5
  125. package/src/phaser/core/game.ts +12 -12
  126. package/src/phaser/core/input.ts +11 -10
  127. package/src/phaser/core/input_handler.test.ts +18 -18
  128. package/src/phaser/core/input_handler.ts +14 -14
  129. package/src/phaser/core/input_mouse.ts +13 -12
  130. package/src/phaser/core/input_pointer.test.ts +26 -26
  131. package/src/phaser/core/input_pointer.ts +4 -4
  132. package/src/phaser/core/input_touch.ts +8 -8
  133. package/src/phaser/core/loader.test.ts +26 -26
  134. package/src/phaser/core/loader.ts +63 -75
  135. package/src/phaser/core/loader_parser.test.ts +16 -16
  136. package/src/phaser/core/loader_parser.ts +16 -6
  137. package/src/phaser/core/raf.ts +1 -1
  138. package/src/phaser/core/registry.test.ts +6 -6
  139. package/src/phaser/core/scale_manager.ts +12 -11
  140. package/src/phaser/core/scene_manager.test.ts +30 -30
  141. package/src/phaser/core/scene_manager.ts +3 -3
  142. package/src/phaser/core/signal.test.ts +8 -8
  143. package/src/phaser/core/signal.ts +3 -3
  144. package/src/phaser/core/signal_dispatch.test.ts +30 -30
  145. package/src/phaser/core/sound.ts +45 -40
  146. package/src/phaser/core/sound_manager.ts +18 -22
  147. package/src/phaser/core/sound_sprite.ts +1 -1
  148. package/src/phaser/core/stage.ts +22 -12
  149. package/src/phaser/core/time.ts +1 -1
  150. package/src/phaser/core/timer.test.ts +47 -47
  151. package/src/phaser/core/timer.ts +2 -2
  152. package/src/phaser/core/tween.ts +34 -36
  153. package/src/phaser/core/tween_data.test.ts +119 -0
  154. package/src/phaser/core/tween_data.ts +54 -39
  155. package/src/phaser/core/tween_easing.test.ts +5 -5
  156. package/src/phaser/core/tween_manager.ts +2 -2
  157. package/src/phaser/display/bitmap_text.test.ts +30 -30
  158. package/src/phaser/display/bitmap_text.ts +3 -3
  159. package/src/phaser/display/button.ts +20 -18
  160. package/src/phaser/display/canvas/buffer.ts +2 -2
  161. package/src/phaser/display/canvas/graphics.ts +2 -2
  162. package/src/phaser/display/canvas/masker.ts +1 -1
  163. package/src/phaser/display/canvas/pool.test.ts +20 -20
  164. package/src/phaser/display/canvas/pool.ts +6 -6
  165. package/src/phaser/display/canvas/renderer.ts +18 -16
  166. package/src/phaser/display/canvas/tinter.ts +1 -1
  167. package/src/phaser/display/canvas/util.test.ts +25 -25
  168. package/src/phaser/display/canvas/util.ts +2 -2
  169. package/src/phaser/display/display_object.ts +16 -15
  170. package/src/phaser/display/graphics.test.ts +70 -20
  171. package/src/phaser/display/graphics.ts +46 -42
  172. package/src/phaser/display/group.ts +13 -12
  173. package/src/phaser/display/image.ts +35 -31
  174. package/src/phaser/display/sprite_util.ts +4 -3
  175. package/src/phaser/display/text.test.ts +28 -25
  176. package/src/phaser/display/text.ts +56 -62
  177. package/src/phaser/display/webgl/abstract_filter.ts +21 -11
  178. package/src/phaser/display/webgl/blend_manager.ts +4 -5
  179. package/src/phaser/display/webgl/earcut.test.ts +20 -20
  180. package/src/phaser/display/webgl/earcut.ts +46 -42
  181. package/src/phaser/display/webgl/fast_sprite_batch.ts +21 -16
  182. package/src/phaser/display/webgl/filter_manager.ts +7 -4
  183. package/src/phaser/display/webgl/filter_texture.ts +5 -5
  184. package/src/phaser/display/webgl/graphics.ts +3 -3
  185. package/src/phaser/display/webgl/mask_manager.ts +9 -14
  186. package/src/phaser/display/webgl/render_texture.ts +15 -14
  187. package/src/phaser/display/webgl/renderer.ts +10 -9
  188. package/src/phaser/display/webgl/shader/normal.ts +45 -35
  189. package/src/phaser/display/webgl/shader/shader.test.ts +24 -24
  190. package/src/phaser/display/webgl/shader_manager.test.ts +18 -18
  191. package/src/phaser/display/webgl/shader_manager.ts +1 -1
  192. package/src/phaser/display/webgl/sprite_batch.ts +10 -10
  193. package/src/phaser/display/webgl/stencil_manager.ts +4 -4
  194. package/src/phaser/display/webgl/texture.ts +2 -1
  195. package/src/phaser/display/webgl/texture_util.ts +2 -2
  196. package/src/phaser/display/webgl/util.ts +4 -4
  197. package/src/phaser/geom/circle.test.ts +38 -38
  198. package/src/phaser/geom/circle.ts +7 -7
  199. package/src/phaser/geom/ellipse.test.ts +12 -12
  200. package/src/phaser/geom/line.test.ts +15 -15
  201. package/src/phaser/geom/line.ts +5 -2
  202. package/src/phaser/geom/matrix.test.ts +31 -31
  203. package/src/phaser/geom/point.test.ts +31 -31
  204. package/src/phaser/geom/polygon.test.ts +27 -27
  205. package/src/phaser/geom/polygon.ts +2 -2
  206. package/src/phaser/geom/rectangle.test.ts +87 -87
  207. package/src/phaser/geom/rectangle.ts +12 -13
  208. package/src/phaser/geom/rounded_rectangle.test.ts +9 -9
  209. package/src/phaser/geom/util/circle.test.ts +5 -5
  210. package/src/phaser/geom/util/circle.ts +8 -11
  211. package/src/phaser/geom/util/ellipse.test.ts +2 -2
  212. package/src/phaser/geom/util/line.test.ts +2 -2
  213. package/src/phaser/geom/util/matrix.test.ts +4 -4
  214. package/src/phaser/geom/util/point.test.ts +2 -2
  215. package/src/phaser/geom/util/point.ts +6 -8
  216. package/src/phaser/geom/util/polygon.test.ts +2 -2
  217. package/src/phaser/geom/util/rectangle.test.ts +2 -2
  218. package/src/phaser/geom/util/rectangle.ts +1 -1
  219. package/src/phaser/geom/util/rounded_rectangle.test.ts +2 -2
  220. package/src/phaser/util/math.test.ts +41 -41
  221. package/src/phaser/util/math.ts +7 -5
@@ -1,6 +1,18 @@
1
1
  import { Frame } from './frame.js';
2
+
3
+ /** One frame in a texture atlas, in the shape the packers emit. */
4
+ export type AtlasFrame = {
5
+ frame: { x: number; y: number; w: number; h: number };
6
+ trimmed?: boolean;
7
+ sourceSize?: { w: number; h: number };
8
+ spriteSourceSize?: { x: number; y: number; w: number; h: number };
9
+ };
10
+
11
+ /** A texture atlas descriptor, keyed by frame name. */
12
+ export type AtlasJson = { frames: Record<string, AtlasFrame> };
2
13
  import { FrameData } from './frame_data.js';
3
14
  import type { Game } from './game.js';
15
+ import type { TextureSource } from '../display/webgl/base_texture.js';
4
16
 
5
17
  /**
6
18
  * TBD.
@@ -15,38 +27,31 @@ import type { Game } from './game.js';
15
27
  */
16
28
  export const spriteSheet = (
17
29
  game: Game,
18
- key: any,
30
+ key: string | TextureSource,
19
31
  frameWidth: number,
20
32
  frameHeight: number,
21
33
  frameMax: number,
22
34
  margin: number,
23
35
  spacing: number
24
36
  ): FrameData | null => {
25
- let img: any = key;
26
- if (typeof key === 'string') {
27
- img = game.cache.getImage(key);
28
- }
37
+ const img = typeof key === 'string' ? game.cache.getImage(key) : key;
29
38
  if (img === null) {
30
39
  return null;
31
40
  }
32
41
  const { width } = img;
33
42
  const { height } = img;
34
- if (frameWidth <= 0) {
35
- frameWidth = Math.floor(-width / Math.min(-1, frameWidth));
36
- }
37
- if (frameHeight <= 0) {
38
- frameHeight = Math.floor(-height / Math.min(-1, frameHeight));
39
- }
40
- const row = Math.floor((width - margin) / (frameWidth + spacing));
41
- const column = Math.floor((height - margin) / (frameHeight + spacing));
43
+ const cellWidth = frameWidth <= 0 ? Math.floor(-width / Math.min(-1, frameWidth)) : frameWidth;
44
+ const cellHeight = frameHeight <= 0 ? Math.floor(-height / Math.min(-1, frameHeight)) : frameHeight;
45
+ const row = Math.floor((width - margin) / (cellWidth + spacing));
46
+ const column = Math.floor((height - margin) / (cellHeight + spacing));
42
47
  let total = row * column;
43
48
  if (frameMax !== -1) {
44
49
  total = frameMax;
45
50
  }
46
51
  // Zero or smaller than frame sizes?
47
- if (width === 0 || height === 0 || width < frameWidth || height < frameHeight || total === 0) {
52
+ if (width === 0 || height === 0 || width < cellWidth || height < cellHeight || total === 0) {
48
53
  game.logger.warn(
49
- `AnimationParser.spriteSheet: '${key}'s width/height zero or width/height < given frameWidth/frameHeight`
54
+ `AnimationParser.spriteSheet: '${typeof key === 'string' ? key : 'image'}'s width/height zero or width/height < given cellWidth/cellHeight`
50
55
  );
51
56
  return null;
52
57
  }
@@ -55,11 +60,11 @@ export const spriteSheet = (
55
60
  let x = margin;
56
61
  let y = margin;
57
62
  for (let i = 0; i < total; i += 1) {
58
- data.addFrame(new Frame(i, x, y, frameWidth, frameHeight, ''));
59
- x += frameWidth + spacing;
60
- if (x + frameWidth > width) {
63
+ data.addFrame(new Frame(i, x, y, cellWidth, cellHeight, ''));
64
+ x += cellWidth + spacing;
65
+ if (x + cellWidth > width) {
61
66
  x = margin;
62
- y += frameHeight + spacing;
67
+ y += cellHeight + spacing;
63
68
  }
64
69
  }
65
70
  return data;
@@ -71,31 +76,28 @@ export const spriteSheet = (
71
76
  * @param {object} json - TBD.
72
77
  * @returns {FrameData} TBD.
73
78
  */
74
- export const jsonDataHash = (game: Game, json: any, _key?: any): FrameData | null => {
75
- if (!json.frames) {
76
- game.logger.warn('jsonDataHash: Invalid Texture Atlas JSON given, missing frames object', json);
79
+ export const jsonDataHash = (game: Game, json: unknown, _key?: string): FrameData | null => {
80
+ const atlas = json as AtlasJson | null;
81
+ if (!atlas?.frames) {
82
+ game.logger.warn('jsonDataHash: Invalid Texture Atlas JSON given, missing frames object');
77
83
  return null;
78
84
  }
79
85
  // Let's create some frames then
80
86
  const data = new FrameData();
81
87
  // By this stage frames is a fully parsed array
82
- const { frames } = json;
83
- let newFrame;
88
+ const { frames } = atlas;
84
89
  let i = 0;
85
- const keys = Object.keys(frames);
86
- for (const key of keys) {
87
- newFrame = data.addFrame(
88
- new Frame(i, frames[key].frame.x, frames[key].frame.y, frames[key].frame.w, frames[key].frame.h, key)
89
- );
90
- if (frames[key].trimmed) {
90
+ for (const [key, entry] of Object.entries(frames)) {
91
+ const newFrame = data.addFrame(new Frame(i, entry.frame.x, entry.frame.y, entry.frame.w, entry.frame.h, key));
92
+ if (entry.trimmed === true && entry.sourceSize !== undefined && entry.spriteSourceSize !== undefined) {
91
93
  newFrame.setTrim(
92
- frames[key].trimmed,
93
- frames[key].sourceSize.w,
94
- frames[key].sourceSize.h,
95
- frames[key].spriteSourceSize.x,
96
- frames[key].spriteSourceSize.y,
97
- frames[key].spriteSourceSize.w,
98
- frames[key].spriteSourceSize.h
94
+ entry.trimmed,
95
+ entry.sourceSize.w,
96
+ entry.sourceSize.h,
97
+ entry.spriteSourceSize.x,
98
+ entry.spriteSourceSize.y,
99
+ entry.spriteSourceSize.w,
100
+ entry.spriteSourceSize.h
99
101
  );
100
102
  }
101
103
  i += 1;
@@ -1,12 +1,14 @@
1
- export class ArraySet {
1
+ import type { AppliedCallback } from './callback.js';
2
+
3
+ export class ArraySet<T> {
2
4
  public position = 0;
3
- public list: any[];
5
+ public list: T[];
4
6
 
5
7
  /**
6
8
  * TBD.
7
9
  * @param {object[]} list - TBD.
8
10
  */
9
- public constructor(list: any[] = []) {
11
+ public constructor(list: T[] = []) {
10
12
  this.list = list;
11
13
  }
12
14
 
@@ -15,7 +17,7 @@ export class ArraySet {
15
17
  * @param {object} item - TBD.
16
18
  * @returns {object} TBD.
17
19
  */
18
- public add(item: any) {
20
+ public add(item: T): T {
19
21
  if (!this.exists(item)) {
20
22
  this.list.push(item);
21
23
  }
@@ -27,7 +29,7 @@ export class ArraySet {
27
29
  * @param {object} item - TBD.
28
30
  * @returns {number} TBD.
29
31
  */
30
- public getIndex(item: any): number {
32
+ public getIndex(item: T): number {
31
33
  return this.list.indexOf(item);
32
34
  }
33
35
 
@@ -37,12 +39,11 @@ export class ArraySet {
37
39
  * @param {object} value - TBD.
38
40
  * @returns {object} TBD.
39
41
  */
40
- public getByKey(property: string, value: any) {
41
- let i = this.list.length;
42
- while (i) {
43
- i -= 1;
44
- if (this.list[i][property] === value) {
45
- return this.list[i];
42
+ public getByKey(property: string, value: unknown): T | null {
43
+ for (let i = this.list.length - 1; i >= 0; i -= 1) {
44
+ const item = this.list[i] as Record<string, unknown> | undefined;
45
+ if (item?.[property] === value) {
46
+ return this.list[i]!;
46
47
  }
47
48
  }
48
49
  return null;
@@ -53,7 +54,7 @@ export class ArraySet {
53
54
  * @param {object} item - TBD.
54
55
  * @returns {boolean} TBD.
55
56
  */
56
- public exists(item: any): boolean {
57
+ public exists(item: T): boolean {
57
58
  return this.list.includes(item);
58
59
  }
59
60
 
@@ -69,7 +70,7 @@ export class ArraySet {
69
70
  * @param {object} item - TBD.
70
71
  * @returns {object} TBD.
71
72
  */
72
- public remove(item: any) {
73
+ public remove(item: T): T | null {
73
74
  const idx = this.list.indexOf(item);
74
75
  if (idx !== -1) {
75
76
  this.list.splice(idx, 1);
@@ -83,10 +84,10 @@ export class ArraySet {
83
84
  * @param {string} key - TBD.
84
85
  * @param {object} value - TBD.
85
86
  */
86
- public setAll(key: string, value: any): void {
87
+ public setAll(key: string, value: unknown): void {
87
88
  for (const item of this.list) {
88
- if (item) {
89
- item[key] = value;
89
+ if (item !== undefined) {
90
+ (item as Record<string, unknown>)[key] = value;
90
91
  }
91
92
  }
92
93
  }
@@ -99,8 +100,8 @@ export class ArraySet {
99
100
  public callAll(key: string, ...args: unknown[]): void {
100
101
  // walked backwards so a callback that removes its own entry does not skip the next one
101
102
  for (let i = this.list.length - 1; i >= 0; i -= 1) {
102
- const item = this.list[i];
103
- if (item && item[key]) {
103
+ const item = this.list[i] as Record<string, AppliedCallback> | undefined;
104
+ if (item?.[key]) {
104
105
  item[key](...args);
105
106
  }
106
107
  }
@@ -111,13 +112,12 @@ export class ArraySet {
111
112
  * @param {boolean} destroy - TBD.
112
113
  */
113
114
  public removeAll(destroy = false): void {
114
- let i = this.list.length;
115
- while (i) {
116
- i -= 1;
117
- if (this.list[i]) {
118
- const item = this.remove(this.list[i]);
115
+ for (let i = this.list.length - 1; i >= 0; i -= 1) {
116
+ const entry = this.list[i];
117
+ if (entry !== undefined) {
118
+ const item = this.remove(entry) as { destroy?: () => void } | null;
119
119
  if (destroy) {
120
- item.destroy();
120
+ item?.destroy?.();
121
121
  }
122
122
  }
123
123
  }
@@ -137,10 +137,10 @@ export class ArraySet {
137
137
  * TBD.
138
138
  * @returns {object} TBD.
139
139
  */
140
- public get first() {
140
+ public get first(): T | null {
141
141
  this.position = 0;
142
142
  if (this.list.length > 0) {
143
- return this.list[0];
143
+ return this.list[0]!;
144
144
  }
145
145
  return null;
146
146
  }
@@ -149,10 +149,10 @@ export class ArraySet {
149
149
  * TBD.
150
150
  * @returns {object} TBD.
151
151
  */
152
- public get next() {
152
+ public get next(): T | null {
153
153
  if (this.position < this.list.length) {
154
154
  this.position += 1;
155
- return this.list[this.position];
155
+ return this.list[this.position] ?? null;
156
156
  }
157
157
  return null;
158
158
  }
@@ -27,36 +27,36 @@ const FONT_XML = `<?xml version="1.0"?>
27
27
  </chars>
28
28
  </font>`;
29
29
 
30
- describe('Cache', () => {
30
+ describe('Cache', (): void => {
31
31
  let cache: Cache;
32
32
 
33
- beforeEach(() => {
33
+ beforeEach((): void => {
34
34
  getRegistry().TEXTURE_SCALE_MODE = SCALE_LINEAR;
35
35
  cache = new Cache(createGame());
36
36
  });
37
37
 
38
- describe('images', () => {
39
- it('seeds the default and missing images', () => {
38
+ describe('images', (): void => {
39
+ it('seeds the default and missing images', (): void => {
40
40
  expect(cache.checkImageKey('__default')).toBe(true);
41
41
  expect(cache.checkImageKey('__missing')).toBe(true);
42
42
  });
43
43
 
44
- it('keeps the default image out of the reported keys', () => {
44
+ it('keeps the default image out of the reported keys', (): void => {
45
45
  expect(cache.getKeys(IMAGE)).toStrictEqual([]);
46
46
  });
47
47
 
48
- it('returns the source bitmap by default and the whole entry when asked', () => {
48
+ it('returns the source bitmap by default and the whole entry when asked', (): void => {
49
49
  const source = createImage();
50
50
  cache.addImage('logo', 'logo.png', source);
51
51
  expect(cache.getImage('logo')).toBe(source);
52
52
  expect(cache.getImage('logo', true).key).toBe('logo');
53
53
  });
54
54
 
55
- it('falls back to the missing image for an unknown key', () => {
55
+ it('falls back to the missing image for an unknown key', (): void => {
56
56
  expect(cache.getImage('nope', true).key).toBe('__missing');
57
57
  });
58
58
 
59
- it('gives an image one frame covering the whole bitmap', () => {
59
+ it('gives an image one frame covering the whole bitmap', (): void => {
60
60
  cache.addImage('logo', 'logo.png', createImage(64, 32));
61
61
  const frame = cache.getFrameByIndex('logo', 0);
62
62
  expect(frame?.width).toBe(64);
@@ -64,26 +64,26 @@ describe('Cache', () => {
64
64
  expect(cache.getFrameCount('logo')).toBe(1);
65
65
  });
66
66
 
67
- it('reports no frames for a key it does not hold', () => {
67
+ it('reports no frames for a key it does not hold', (): void => {
68
68
  expect(cache.getFrameData('nope')).toBeNull();
69
69
  expect(cache.getFrameCount('nope')).toBe(0);
70
70
  expect(cache.hasFrameData('nope')).toBe(false);
71
71
  });
72
72
 
73
- it('drops an image when it is removed', () => {
73
+ it('drops an image when it is removed', (): void => {
74
74
  cache.addImage('logo', 'logo.png', createImage());
75
75
  cache.removeImage('logo');
76
76
  expect(cache.checkImageKey('logo')).toBe(false);
77
77
  });
78
78
 
79
- it('replaces an image registered twice under the same key', () => {
79
+ it('replaces an image registered twice under the same key', (): void => {
80
80
  cache.addImage('logo', 'a.png', createImage(4, 4));
81
81
  const second = createImage(8, 8);
82
82
  cache.addImage('logo', 'b.png', second);
83
83
  expect(cache.getImage('logo')).toBe(second);
84
84
  });
85
85
 
86
- it('clears the GL textures of every cached image', () => {
86
+ it('clears the GL textures of every cached image', (): void => {
87
87
  cache.addImage('logo', 'logo.png', createImage());
88
88
  const entry = cache.getImage('logo', true);
89
89
  entry.base._glTextures = [null];
@@ -92,25 +92,25 @@ describe('Cache', () => {
92
92
  });
93
93
  });
94
94
 
95
- describe('sounds', () => {
96
- it('stores a sound as undecoded', () => {
95
+ describe('sounds', (): void => {
96
+ it('stores a sound as undecoded', (): void => {
97
97
  cache.addSound('shot', 'shot.ogg', new ArrayBuffer(8));
98
98
  expect(cache.isSoundDecoded('shot')).toBe(false);
99
99
  expect(cache.isSoundReady('shot')).toBe(false);
100
100
  });
101
101
 
102
- it('reports an unknown sound as neither decoded nor ready', () => {
102
+ it('reports an unknown sound as neither decoded nor ready', (): void => {
103
103
  expect(cache.isSoundDecoded('nope')).toBeNull();
104
104
  expect(cache.isSoundReady('nope')).toBe(false);
105
105
  });
106
106
 
107
- it('updates a single property of a cached sound', () => {
107
+ it('updates a single property of a cached sound', (): void => {
108
108
  cache.addSound('shot', 'shot.ogg', new ArrayBuffer(8));
109
109
  cache.updateSound('shot', 'isDecoding', true);
110
110
  expect(cache.getSound('shot')?.isDecoding).toBe(true);
111
111
  });
112
112
 
113
- it('swaps in the decoded buffer and marks the sound ready', () => {
113
+ it('swaps in the decoded buffer and marks the sound ready', (): void => {
114
114
  cache.addSound('shot', 'shot.ogg', new ArrayBuffer(8));
115
115
  const buffer = { duration: 1.5 } as AudioBuffer;
116
116
  cache.decodedSound('shot', buffer);
@@ -119,33 +119,33 @@ describe('Cache', () => {
119
119
  expect(cache.isSoundReady('shot')).toBe(true);
120
120
  });
121
121
 
122
- it('ignores a decode result for a sound it never held', () => {
123
- expect(() => {
122
+ it('ignores a decode result for a sound it never held', (): void => {
123
+ expect((): void => {
124
124
  cache.decodedSound('nope', {} as AudioBuffer);
125
125
  }).not.toThrow();
126
126
  });
127
127
 
128
- it('drops a sound when it is removed', () => {
128
+ it('drops a sound when it is removed', (): void => {
129
129
  cache.addSound('shot', 'shot.ogg', new ArrayBuffer(8));
130
130
  cache.removeSound('shot');
131
131
  expect(cache.getSound('shot')).toBeNull();
132
132
  });
133
133
  });
134
134
 
135
- describe('data', () => {
136
- it('round-trips text', () => {
135
+ describe('data', (): void => {
136
+ it('round-trips text', (): void => {
137
137
  cache.addText('note', 'note.txt', 'hello');
138
138
  expect(cache.getText('note')).toBe('hello');
139
139
  cache.removeText('note');
140
140
  expect(cache.getText('note')).toBeNull();
141
141
  });
142
142
 
143
- it('round-trips JSON', () => {
143
+ it('round-trips JSON', (): void => {
144
144
  cache.addJSON('config', 'config.json', { a: 1 });
145
145
  expect(cache.getJSON('config')).toStrictEqual({ a: 1 });
146
146
  });
147
147
 
148
- it('hands back a detached copy when a clone is asked for', () => {
148
+ it('hands back a detached copy when a clone is asked for', (): void => {
149
149
  const data = { a: 1 };
150
150
  cache.addJSON('config', 'config.json', data);
151
151
  const clone = cache.getJSON<{ a: number }>('config', true);
@@ -153,15 +153,15 @@ describe('Cache', () => {
153
153
  expect(clone).not.toBe(data);
154
154
  });
155
155
 
156
- it('round-trips XML', () => {
156
+ it('round-trips XML', (): void => {
157
157
  const xml = new DOMParser().parseFromString('<root/>', 'text/xml');
158
158
  cache.addXML('doc', 'doc.xml', xml);
159
159
  expect(cache.getXML('doc')).toBe(xml);
160
160
  });
161
161
  });
162
162
 
163
- describe('bitmap fonts', () => {
164
- it('parses the descriptor and keeps it beside the atlas', () => {
163
+ describe('bitmap fonts', (): void => {
164
+ it('parses the descriptor and keeps it beside the atlas', (): void => {
165
165
  const xml = new DOMParser().parseFromString(FONT_XML, 'text/xml');
166
166
  cache.addBitmapFont('arial', 'arial.png', createImage(256, 256), xml, 'xml');
167
167
  const entry = cache.getBitmapFont('arial');
@@ -170,26 +170,26 @@ describe('Cache', () => {
170
170
  expect(entry?.base).toBeDefined();
171
171
  });
172
172
 
173
- it('reports a font it does not hold as absent', () => {
173
+ it('reports a font it does not hold as absent', (): void => {
174
174
  expect(cache.checkBitmapFontKey('nope')).toBe(false);
175
175
  expect(cache.getBitmapFont('nope')).toBeNull();
176
176
  });
177
177
  });
178
178
 
179
- describe('getItem', () => {
180
- it('returns the whole entry when no property is named', () => {
179
+ describe('getItem', (): void => {
180
+ it('returns the whole entry when no property is named', (): void => {
181
181
  cache.addText('note', 'note.txt', 'hello');
182
182
  expect(cache.getItem('note', 4, 'test')).toStrictEqual({ url: 'note.txt', data: 'hello' });
183
183
  });
184
184
 
185
- it('returns null for a key the bucket does not hold', () => {
185
+ it('returns null for a key the bucket does not hold', (): void => {
186
186
  expect(cache.getItem('nope', SOUND, 'test')).toBeNull();
187
187
  expect(cache.getItem('nope', BITMAPFONT, 'test', 'font')).toBeNull();
188
188
  });
189
189
  });
190
190
 
191
- describe('destroy', () => {
192
- it('empties every bucket but the built-in images', () => {
191
+ describe('destroy', (): void => {
192
+ it('empties every bucket but the built-in images', (): void => {
193
193
  cache.addText('note', 'note.txt', 'hello');
194
194
  cache.addSound('shot', 'shot.ogg', new ArrayBuffer(8));
195
195
  cache.destroy();
@@ -6,7 +6,7 @@ import { Frame } from './frame.js';
6
6
  import { FrameData } from './frame_data.js';
7
7
  import { jsonBitmapFont, xmlBitmapFont } from './loader_parser.js';
8
8
  import { Signal } from './signal.js';
9
- import type { BitmapFontData } from './loader_parser.js';
9
+ import type { BitmapFontData, JsonBitmapFontDescriptor } from './loader_parser.js';
10
10
  import type { Game } from './game.js';
11
11
 
12
12
  export const CANVAS = 0;
@@ -272,7 +272,7 @@ export class Cache {
272
272
  const base = new BaseTexture(data);
273
273
  const font =
274
274
  atlasType === 'json'
275
- ? jsonBitmapFont(atlasData, base, xSpacing, ySpacing)
275
+ ? jsonBitmapFont(atlasData as JsonBitmapFontDescriptor, base, xSpacing, ySpacing)
276
276
  : xmlBitmapFont(atlasData as XMLDocument, base, xSpacing, ySpacing);
277
277
  const obj: BitmapFontCacheEntry = { url, data, font, base };
278
278
  this._cache.bitmapFont[key] = obj;
@@ -381,7 +381,7 @@ export class Cache {
381
381
  */
382
382
  public checkURL(url: string): boolean {
383
383
  const resolved = this._resolveURL(url);
384
- if (resolved && this._urlMap?.[resolved]) {
384
+ if (resolved !== null && this._urlMap?.[resolved] !== undefined) {
385
385
  return true;
386
386
  }
387
387
  return false;
@@ -481,7 +481,7 @@ export class Cache {
481
481
  public getItem(key: string, cache: number, _method: string, property: string | null = null): unknown {
482
482
  if (this.checkKey(cache, key)) {
483
483
  const entry = this._cacheMap[cache]![key]!;
484
- if (!property) {
484
+ if (property === null) {
485
485
  return entry;
486
486
  }
487
487
  return (entry as Record<string, unknown>)[property];
@@ -703,9 +703,9 @@ export class Cache {
703
703
  * @param {string} url - The original URL to resolve.
704
704
  * @returns {string} The resolved URL or null if not found.
705
705
  */
706
- public getURL(url: string) {
706
+ public getURL(url: string): object | null {
707
707
  const resolvedURL = this._resolveURL(url);
708
- if (resolvedURL) {
708
+ if (resolvedURL !== null) {
709
709
  return this._urlMap?.[resolvedURL] ?? null;
710
710
  }
711
711
  this.game.logger.warn('Cache invalid url', { resolvedURL });
@@ -864,7 +864,7 @@ export class Cache {
864
864
  */
865
865
  public destroy(): void {
866
866
  for (const cache of this._cacheMap) {
867
- const keys = cache ? Object.keys(cache) : [];
867
+ const keys = Object.keys(cache);
868
868
  for (const key of keys) {
869
869
  if (key !== '__default' && key !== '__missing') {
870
870
  (cache[key] as { destroy?: () => void }).destroy?.();
@@ -31,4 +31,5 @@ export type InterpolationFunction = (v: number[], k: number) => number;
31
31
  * and consumers narrow it by declaring their own display-object subtype — which only holds while
32
32
  * the property stays assignable in both directions, so this one bag has no shape.
33
33
  */
34
+ // eslint-disable-next-line typescript/no-explicit-any -- the whole point of this bag is that it has no shape
34
35
  export type UserData = any;
@@ -15,9 +15,9 @@ class Consumer {
15
15
  }
16
16
  }
17
17
 
18
- describe('callback contexts', () => {
19
- describe('Signal', () => {
20
- it('accepts an arbitrary receiver on add', () => {
18
+ describe('callback contexts', (): void => {
19
+ describe('Signal', (): void => {
20
+ it('accepts an arbitrary receiver on add', (): void => {
21
21
  const signal = new Signal();
22
22
  const consumer = new Consumer();
23
23
  signal.add(consumer.handler, consumer);
@@ -25,7 +25,7 @@ describe('callback contexts', () => {
25
25
  expect(consumer.seen).toBe('payload');
26
26
  });
27
27
 
28
- it('accepts an arbitrary receiver on addOnce', () => {
28
+ it('accepts an arbitrary receiver on addOnce', (): void => {
29
29
  const signal = new Signal();
30
30
  const consumer = new Consumer();
31
31
  signal.addOnce(consumer.handler, consumer);
@@ -33,7 +33,7 @@ describe('callback contexts', () => {
33
33
  expect(consumer.seen).toBe('once');
34
34
  });
35
35
 
36
- it('matches the receiver on remove', () => {
36
+ it('matches the receiver on remove', (): void => {
37
37
  const signal = new Signal();
38
38
  const consumer = new Consumer();
39
39
  signal.add(consumer.handler, consumer);
@@ -41,7 +41,7 @@ describe('callback contexts', () => {
41
41
  expect(signal.getNumListeners()).toBe(0);
42
42
  });
43
43
 
44
- it('matches the receiver on removeAll', () => {
44
+ it('matches the receiver on removeAll', (): void => {
45
45
  const signal = new Signal();
46
46
  const consumer = new Consumer();
47
47
  signal.add(consumer.handler, consumer);
@@ -50,7 +50,7 @@ describe('callback contexts', () => {
50
50
  expect(signal.getNumListeners()).toBe(1);
51
51
  });
52
52
 
53
- it('reports whether a listener is bound to a receiver', () => {
53
+ it('reports whether a listener is bound to a receiver', (): void => {
54
54
  const signal = new Signal();
55
55
  const consumer = new Consumer();
56
56
  signal.add(consumer.handler, consumer);
@@ -58,7 +58,7 @@ describe('callback contexts', () => {
58
58
  expect(signal.has(consumer.handler, new Consumer())).toBe(false);
59
59
  });
60
60
 
61
- it('keeps separate bindings per receiver', () => {
61
+ it('keeps separate bindings per receiver', (): void => {
62
62
  const signal = new Signal();
63
63
  const first = new Consumer();
64
64
  const second = new Consumer();
@@ -96,7 +96,7 @@ export const checkFullScreenSupport = (device: Device): void => {
96
96
  const element = document.createElement('div');
97
97
  const elementApi = element as unknown as Record<string, unknown>;
98
98
  for (const name of fs) {
99
- if (elementApi[name]) {
99
+ if (elementApi[name] !== undefined) {
100
100
  device.fullscreen = true;
101
101
  device.requestFullscreen = name;
102
102
  break;
@@ -105,13 +105,13 @@ export const checkFullScreenSupport = (device: Device): void => {
105
105
  if (device.fullscreen) {
106
106
  const documentApi = document as unknown as Record<string, unknown>;
107
107
  for (const name of cfs) {
108
- if (documentApi[name]) {
108
+ if (documentApi[name] !== undefined) {
109
109
  device.cancelFullscreen = name;
110
110
  break;
111
111
  }
112
112
  }
113
113
  // ALLOW_KEYBOARD_INPUT is a legacy webkit fullscreen flag, absent from lib.dom
114
- if (globalThis.Element && (Element as unknown as { ALLOW_KEYBOARD_INPUT?: number }).ALLOW_KEYBOARD_INPUT) {
114
+ if ((Element as unknown as { ALLOW_KEYBOARD_INPUT?: number }).ALLOW_KEYBOARD_INPUT !== undefined) {
115
115
  device.fullscreenKeyboard = true;
116
116
  }
117
117
  }
@@ -190,7 +190,7 @@ export const checkAudio = (device: Device): void => {
190
190
  for (const format of formats) {
191
191
  const { type, codecs } = format;
192
192
  for (const codec of codecs) {
193
- if (!device.supportedAudioFormats[type]) {
193
+ if (device.supportedAudioFormats[type] !== true) {
194
194
  const isSupported = isMediaSourceTypeSupported(codec) || canPlayType(audioElement, codec);
195
195
  if (isSupported) {
196
196
  device.supportedAudioFormats[type] = isSupported;