@vpmedia/phaser 1.124.0 → 1.126.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 (81) hide show
  1. package/dist/index.js +312 -309
  2. package/dist/index.js.map +1 -1
  3. package/dist/phaser/core/animation_parser.d.ts.map +1 -1
  4. package/dist/phaser/core/device_util.d.ts.map +1 -1
  5. package/dist/phaser/core/dom.d.ts.map +1 -1
  6. package/dist/phaser/core/frame_data.d.ts.map +1 -1
  7. package/dist/phaser/core/game.d.ts.map +1 -1
  8. package/dist/phaser/core/input.d.ts.map +1 -1
  9. package/dist/phaser/core/input_handler.d.ts.map +1 -1
  10. package/dist/phaser/core/loader.d.ts.map +1 -1
  11. package/dist/phaser/core/scale_manager.d.ts.map +1 -1
  12. package/dist/phaser/core/sound.d.ts.map +1 -1
  13. package/dist/phaser/core/sound_manager.d.ts.map +1 -1
  14. package/dist/phaser/core/tween.d.ts.map +1 -1
  15. package/dist/phaser/display/canvas/renderer.d.ts.map +1 -1
  16. package/dist/phaser/display/display_object.d.ts +2 -2
  17. package/dist/phaser/display/display_object.d.ts.map +1 -1
  18. package/dist/phaser/display/graphics.d.ts +1 -1
  19. package/dist/phaser/display/graphics.d.ts.map +1 -1
  20. package/dist/phaser/display/group.d.ts.map +1 -1
  21. package/dist/phaser/display/image.d.ts.map +1 -1
  22. package/dist/phaser/display/text.d.ts.map +1 -1
  23. package/dist/phaser/display/webgl/earcut.d.ts.map +1 -1
  24. package/dist/phaser/display/webgl/texture.d.ts.map +1 -1
  25. package/dist/phaser/geom/rectangle.d.ts.map +1 -1
  26. package/dist/phaser/geom/util/circle.d.ts.map +1 -1
  27. package/dist/phaser/geom/util/point.d.ts.map +1 -1
  28. package/dist/phaser/util/math.d.ts.map +1 -1
  29. package/package.json +1 -1
  30. package/src/phaser/core/animation.ts +7 -7
  31. package/src/phaser/core/animation_manager.ts +2 -2
  32. package/src/phaser/core/animation_parser.ts +11 -15
  33. package/src/phaser/core/array_set.ts +2 -2
  34. package/src/phaser/core/cache.ts +4 -4
  35. package/src/phaser/core/device_util.ts +7 -4
  36. package/src/phaser/core/dom.ts +24 -19
  37. package/src/phaser/core/frame_data.ts +2 -5
  38. package/src/phaser/core/game.test.ts +131 -0
  39. package/src/phaser/core/game.ts +17 -12
  40. package/src/phaser/core/input.ts +4 -3
  41. package/src/phaser/core/input_handler.ts +14 -13
  42. package/src/phaser/core/input_mouse.ts +4 -4
  43. package/src/phaser/core/input_pointer.ts +1 -1
  44. package/src/phaser/core/loader.ts +38 -50
  45. package/src/phaser/core/scale_manager.ts +10 -9
  46. package/src/phaser/core/scene_manager.ts +2 -2
  47. package/src/phaser/core/signal.ts +2 -2
  48. package/src/phaser/core/sound.ts +25 -32
  49. package/src/phaser/core/sound_manager.ts +12 -16
  50. package/src/phaser/core/sound_sprite.ts +1 -1
  51. package/src/phaser/core/stage.ts +3 -3
  52. package/src/phaser/core/timer.ts +1 -1
  53. package/src/phaser/core/tween.ts +6 -11
  54. package/src/phaser/display/bitmap_text.ts +3 -3
  55. package/src/phaser/display/button.ts +12 -12
  56. package/src/phaser/display/canvas/graphics.ts +2 -2
  57. package/src/phaser/display/canvas/pool.ts +3 -3
  58. package/src/phaser/display/canvas/renderer.ts +6 -5
  59. package/src/phaser/display/canvas/tinter.ts +1 -1
  60. package/src/phaser/display/canvas/util.ts +2 -2
  61. package/src/phaser/display/display_object.ts +13 -14
  62. package/src/phaser/display/graphics.test.ts +2 -2
  63. package/src/phaser/display/graphics.ts +9 -11
  64. package/src/phaser/display/group.ts +10 -9
  65. package/src/phaser/display/image.ts +7 -6
  66. package/src/phaser/display/sprite_util.ts +1 -1
  67. package/src/phaser/display/text.ts +35 -44
  68. package/src/phaser/display/webgl/abstract_filter.ts +1 -1
  69. package/src/phaser/display/webgl/earcut.ts +46 -42
  70. package/src/phaser/display/webgl/graphics.ts +2 -2
  71. package/src/phaser/display/webgl/renderer.ts +3 -3
  72. package/src/phaser/display/webgl/shader/normal.ts +6 -6
  73. package/src/phaser/display/webgl/shader_manager.ts +1 -1
  74. package/src/phaser/display/webgl/sprite_batch.ts +2 -2
  75. package/src/phaser/display/webgl/texture.ts +2 -1
  76. package/src/phaser/display/webgl/util.ts +3 -3
  77. package/src/phaser/geom/polygon.ts +2 -2
  78. package/src/phaser/geom/rectangle.ts +1 -2
  79. package/src/phaser/geom/util/circle.ts +6 -10
  80. package/src/phaser/geom/util/point.ts +5 -7
  81. package/src/phaser/util/math.ts +2 -3
@@ -40,22 +40,18 @@ export const spriteSheet = (
40
40
  }
41
41
  const { width } = img;
42
42
  const { height } = img;
43
- if (frameWidth <= 0) {
44
- frameWidth = Math.floor(-width / Math.min(-1, frameWidth));
45
- }
46
- if (frameHeight <= 0) {
47
- frameHeight = Math.floor(-height / Math.min(-1, frameHeight));
48
- }
49
- const row = Math.floor((width - margin) / (frameWidth + spacing));
50
- 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));
51
47
  let total = row * column;
52
48
  if (frameMax !== -1) {
53
49
  total = frameMax;
54
50
  }
55
51
  // Zero or smaller than frame sizes?
56
- if (width === 0 || height === 0 || width < frameWidth || height < frameHeight || total === 0) {
52
+ if (width === 0 || height === 0 || width < cellWidth || height < cellHeight || total === 0) {
57
53
  game.logger.warn(
58
- `AnimationParser.spriteSheet: '${typeof key === 'string' ? key : 'image'}'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`
59
55
  );
60
56
  return null;
61
57
  }
@@ -64,11 +60,11 @@ export const spriteSheet = (
64
60
  let x = margin;
65
61
  let y = margin;
66
62
  for (let i = 0; i < total; i += 1) {
67
- data.addFrame(new Frame(i, x, y, frameWidth, frameHeight, ''));
68
- x += frameWidth + spacing;
69
- if (x + frameWidth > width) {
63
+ data.addFrame(new Frame(i, x, y, cellWidth, cellHeight, ''));
64
+ x += cellWidth + spacing;
65
+ if (x + cellWidth > width) {
70
66
  x = margin;
71
- y += frameHeight + spacing;
67
+ y += cellHeight + spacing;
72
68
  }
73
69
  }
74
70
  return data;
@@ -93,7 +89,7 @@ export const jsonDataHash = (game: Game, json: unknown, _key?: string): FrameDat
93
89
  let i = 0;
94
90
  for (const [key, entry] of Object.entries(frames)) {
95
91
  const newFrame = data.addFrame(new Frame(i, entry.frame.x, entry.frame.y, entry.frame.w, entry.frame.h, key));
96
- if (entry.trimmed && entry.sourceSize && entry.spriteSourceSize) {
92
+ if (entry.trimmed === true && entry.sourceSize !== undefined && entry.spriteSourceSize !== undefined) {
97
93
  newFrame.setTrim(
98
94
  entry.trimmed,
99
95
  entry.sourceSize.w,
@@ -86,7 +86,7 @@ export class ArraySet<T> {
86
86
  */
87
87
  public setAll(key: string, value: unknown): void {
88
88
  for (const item of this.list) {
89
- if (item) {
89
+ if (item !== undefined) {
90
90
  (item as Record<string, unknown>)[key] = value;
91
91
  }
92
92
  }
@@ -114,7 +114,7 @@ export class ArraySet<T> {
114
114
  public removeAll(destroy = false): void {
115
115
  for (let i = this.list.length - 1; i >= 0; i -= 1) {
116
116
  const entry = this.list[i];
117
- if (entry) {
117
+ if (entry !== undefined) {
118
118
  const item = this.remove(entry) as { destroy?: () => void } | null;
119
119
  if (destroy) {
120
120
  item?.destroy?.();
@@ -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];
@@ -705,7 +705,7 @@ export class Cache {
705
705
  */
706
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?.();
@@ -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,16 @@ 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 (
115
+ (globalThis.Element as typeof Element | undefined) &&
116
+ (Element as unknown as { ALLOW_KEYBOARD_INPUT?: number }).ALLOW_KEYBOARD_INPUT !== undefined
117
+ ) {
115
118
  device.fullscreenKeyboard = true;
116
119
  }
117
120
  }
@@ -190,7 +193,7 @@ export const checkAudio = (device: Device): void => {
190
193
  for (const format of formats) {
191
194
  const { type, codecs } = format;
192
195
  for (const codec of codecs) {
193
- if (!device.supportedAudioFormats[type]) {
196
+ if (device.supportedAudioFormats[type] !== true) {
194
197
  const isSupported = isMediaSourceTypeSupported(codec) || canPlayType(audioElement, codec);
195
198
  if (isSupported) {
196
199
  device.supportedAudioFormats[type] = isSupported;
@@ -17,7 +17,7 @@ class VisualBoundsDesktopRectangle {
17
17
  * @returns {number} The horizontal scroll position in pixels.
18
18
  */
19
19
  public get x(): number {
20
- return globalThis && 'pageXOffset' in globalThis ? window.pageXOffset : document.documentElement.scrollLeft;
20
+ return 'pageXOffset' in globalThis ? window.pageXOffset : document.documentElement.scrollLeft;
21
21
  }
22
22
 
23
23
  /**
@@ -25,7 +25,7 @@ class VisualBoundsDesktopRectangle {
25
25
  * @returns {number} The vertical scroll position in pixels.
26
26
  */
27
27
  public get y(): number {
28
- return globalThis && 'pageYOffset' in globalThis ? window.pageYOffset : document.documentElement.scrollTop;
28
+ return 'pageYOffset' in globalThis ? window.pageYOffset : document.documentElement.scrollTop;
29
29
  }
30
30
 
31
31
  /**
@@ -81,7 +81,7 @@ class VisualBoundsRectangle {
81
81
  * @returns {number} The horizontal scroll position in pixels.
82
82
  */
83
83
  public get x(): number {
84
- return globalThis && 'pageXOffset' in globalThis ? window.pageXOffset : document.documentElement.scrollLeft;
84
+ return 'pageXOffset' in globalThis ? window.pageXOffset : document.documentElement.scrollLeft;
85
85
  }
86
86
 
87
87
  /**
@@ -89,7 +89,7 @@ class VisualBoundsRectangle {
89
89
  * @returns {number} The vertical scroll position in pixels.
90
90
  */
91
91
  public get y(): number {
92
- return globalThis && 'pageYOffset' in globalThis ? window.pageYOffset : document.documentElement.scrollTop;
92
+ return 'pageYOffset' in globalThis ? window.pageYOffset : document.documentElement.scrollTop;
93
93
  }
94
94
 
95
95
  /**
@@ -199,13 +199,11 @@ export class DOM {
199
199
  this.layoutBounds = this.treatAsDesktop ? new LayoutBoundsDesktopRectangle() : new LayoutBoundsRectangle();
200
200
  this.documentBounds = new DocumentBoundsRectangle();
201
201
  this.scrollXProvider =
202
- globalThis && 'pageXOffset' in globalThis
202
+ 'pageXOffset' in globalThis
203
203
  ? (): number => window.pageXOffset
204
204
  : (): number => document.documentElement.scrollLeft;
205
205
  this.scrollYProvider =
206
- globalThis && 'pageYOffset' in globalThis
207
- ? (): number => window.pageYOffset
208
- : (): number => document.documentElement.scrollTop;
206
+ 'pageYOffset' in globalThis ? (): number => window.pageYOffset : (): number => document.documentElement.scrollTop;
209
207
  }
210
208
 
211
209
  /**
@@ -233,11 +231,13 @@ export class DOM {
233
231
  * @returns {boolean} True if bounds were successfully retrieved, false otherwise.
234
232
  */
235
233
  public getBounds(element: HTMLCanvasElement, cushion = 0): CalibratedBounds | false {
236
- element = element && !element.nodeType ? (element as unknown as HTMLCanvasElement[])[0]! : element;
237
- if (!element || element.nodeType !== 1) {
234
+ // a jQuery-style wrapper was accepted here historically, so unwrap the first entry of one
235
+ const wrapped = element as HTMLCanvasElement | undefined;
236
+ const target = wrapped && !wrapped.nodeType ? (element as unknown as HTMLCanvasElement[])[0] : wrapped;
237
+ if (target?.nodeType !== 1) {
238
238
  return false;
239
239
  }
240
- return this.calibrate(element.getBoundingClientRect(), cushion);
240
+ return this.calibrate(target.getBoundingClientRect(), cushion);
241
241
  }
242
242
 
243
243
  /**
@@ -266,16 +266,21 @@ export class DOM {
266
266
  * @returns {string} The screen orientation ('portrait-primary', 'landscape-primary', etc.).
267
267
  */
268
268
  public getScreenOrientation(primaryFallback: string | null = null): OrientationType {
269
- const { screen } = globalThis;
270
- // @ts-expect-error the prefixed orientation properties predate the standard one and are absent from lib.dom
271
- const orientation = screen.orientation || screen.mozOrientation || screen.msOrientation;
272
- if (orientation && typeof orientation.type === 'string') {
273
- // Screen Orientation API specification
274
- return orientation.type;
275
- } else if (typeof orientation === 'string') {
269
+ // the prefixed orientation properties predate the standard one and are absent from lib.dom
270
+ const screen = globalThis.screen as Screen & {
271
+ mozOrientation?: string;
272
+ msOrientation?: string;
273
+ };
274
+ const orientation: ScreenOrientation | string | undefined =
275
+ screen.orientation ?? screen.mozOrientation ?? screen.msOrientation;
276
+ if (typeof orientation === 'string') {
276
277
  // moz/ms-orientation are strings
277
278
  return orientation;
278
279
  }
280
+ if (typeof orientation?.type === 'string') {
281
+ // Screen Orientation API specification
282
+ return orientation.type;
283
+ }
279
284
  const PORTRAIT = 'portrait-primary';
280
285
  const LANDSCAPE = 'landscape-primary';
281
286
  if (primaryFallback === 'screen') {
@@ -288,7 +293,7 @@ export class DOM {
288
293
  if (primaryFallback === 'window.orientation' && typeof legacyOrientation === 'number') {
289
294
  // This may change by device based on "natural" orientation.
290
295
  return legacyOrientation === 0 || legacyOrientation === 180 ? PORTRAIT : LANDSCAPE;
291
- } else if (globalThis.matchMedia) {
296
+ } else if (typeof globalThis.matchMedia === 'function') {
292
297
  if (globalThis.matchMedia('(orientation: portrait)').matches) {
293
298
  return PORTRAIT;
294
299
  } else if (globalThis.matchMedia('(orientation: landscape)').matches) {
@@ -20,10 +20,7 @@ export class FrameData {
20
20
  }
21
21
 
22
22
  public getFrame(index = 0): Frame {
23
- if (index >= this._frames.length) {
24
- index = 0;
25
- }
26
- return this._frames[index]!;
23
+ return this._frames[index >= this._frames.length ? 0 : index]!;
27
24
  }
28
25
 
29
26
  public getFrameByName(name: string): Frame | null {
@@ -59,7 +56,7 @@ export class FrameData {
59
56
  output: number[] | null = null
60
57
  ): number[] {
61
58
  const result = output ?? [];
62
- if (frames && frames.length > 0) {
59
+ if (((frames as (string | number)[] | undefined)?.length ?? 0) > 0) {
63
60
  for (const f of frames) {
64
61
  if (useNumericIndex && this._frames[f as number]) {
65
62
  result.push(this._frames[f as number]!.index);
@@ -0,0 +1,131 @@
1
+ import { afterAll, afterEach, beforeAll, describe, expect, it, vi } from 'vitest';
2
+ import { RENDER_CANVAS } from './const.js';
3
+ import { Game } from './game.js';
4
+
5
+ // happy-dom has no canvas implementation, so booting needs the smallest 2D context the canvas
6
+ // renderer touches. Booting is the one path that wires every subsystem together, which makes it the
7
+ // regression net for guards that only fire while the game is half-built.
8
+ const createContext2D = (): CanvasRenderingContext2D =>
9
+ ({
10
+ font: '',
11
+ fillStyle: '',
12
+ strokeStyle: '',
13
+ globalAlpha: 1,
14
+ globalCompositeOperation: 'source-over',
15
+ imageSmoothingEnabled: true,
16
+ clearRect: vi.fn(),
17
+ fillRect: vi.fn(),
18
+ drawImage: vi.fn(),
19
+ setTransform: vi.fn(),
20
+ save: vi.fn(),
21
+ restore: vi.fn(),
22
+ translate: vi.fn(),
23
+ getImageData: (): ImageData => ({ data: new Uint8ClampedArray(4) }) as ImageData,
24
+ putImageData: vi.fn(),
25
+ measureText: (value: string) => ({ width: value.length * 8 }),
26
+ }) as unknown as CanvasRenderingContext2D;
27
+
28
+ const createGame = (): Game =>
29
+ new Game({
30
+ width: 320,
31
+ height: 240,
32
+ renderType: RENDER_CANVAS,
33
+ isSkipTicker: true,
34
+ });
35
+
36
+ describe('Game', () => {
37
+ const originalGetContext = HTMLCanvasElement.prototype.getContext;
38
+
39
+ beforeAll(() => {
40
+ HTMLCanvasElement.prototype.getContext = function getContext(
41
+ this: HTMLCanvasElement,
42
+ id: string
43
+ ): CanvasRenderingContext2D | null {
44
+ return id === '2d' ? createContext2D() : null;
45
+ } as typeof HTMLCanvasElement.prototype.getContext;
46
+ });
47
+
48
+ afterEach(() => {
49
+ document.body.innerHTML = '';
50
+ });
51
+
52
+ afterAll(() => {
53
+ HTMLCanvasElement.prototype.getContext = originalGetContext;
54
+ });
55
+
56
+ describe('boot', () => {
57
+ it('brings the game up', () => {
58
+ expect(createGame().isBooted).toBe(true);
59
+ });
60
+
61
+ it('wires every subsystem', () => {
62
+ const game = createGame();
63
+ expect(game.scale).toBeDefined();
64
+ expect(game.stage).toBeDefined();
65
+ expect(game.world).toBeDefined();
66
+ expect(game.add).toBeDefined();
67
+ expect(game.cache).toBeDefined();
68
+ expect(game.load).toBeDefined();
69
+ expect(game.time).toBeDefined();
70
+ expect(game.tweens).toBeDefined();
71
+ expect(game.input).toBeDefined();
72
+ expect(game.sound).toBeDefined();
73
+ expect(game.renderer).toBeDefined();
74
+ });
75
+
76
+ it('takes the size from the config', () => {
77
+ const game = createGame();
78
+ expect(game.width).toBe(320);
79
+ expect(game.height).toBe(240);
80
+ expect(game.scale.width).toBe(320);
81
+ expect(game.scale.height).toBe(240);
82
+ });
83
+
84
+ it('gives the scale manager an input scale once input is up', () => {
85
+ expect(createGame().input.scale.x).toBeGreaterThan(0);
86
+ });
87
+
88
+ it('attaches the canvas to the document', () => {
89
+ const game = createGame();
90
+ expect(game.canvas.parentElement).not.toBeNull();
91
+ });
92
+
93
+ it('skips the ticker when the config asks it to', () => {
94
+ expect(createGame().raf).toBeUndefined();
95
+ });
96
+
97
+ it('defaults the size when the config gives none', () => {
98
+ const game = new Game({ renderType: RENDER_CANVAS, isSkipTicker: true });
99
+ expect(game.width).toBe(800);
100
+ expect(game.height).toBe(600);
101
+ });
102
+ });
103
+
104
+ describe('update', () => {
105
+ it('advances the clock and the world without throwing', () => {
106
+ const game = createGame();
107
+ expect(() => {
108
+ game.update(16);
109
+ game.update(32);
110
+ }).not.toThrow();
111
+ });
112
+ });
113
+
114
+ describe('config', () => {
115
+ it('takes a canvas supplied by the config instead of creating one', () => {
116
+ const canvas = document.createElement('canvas');
117
+ const game = new Game({ width: 320, height: 240, renderType: RENDER_CANVAS, isSkipTicker: true, canvas });
118
+ expect(game.canvas).toBe(canvas);
119
+ });
120
+ });
121
+
122
+ describe('destroy', () => {
123
+ it('detaches the canvas and drops it', () => {
124
+ const game = createGame();
125
+ const { canvas } = game;
126
+ game.destroy();
127
+ expect(canvas.parentElement).toBeNull();
128
+ expect(game.canvas).toBeNull();
129
+ });
130
+ });
131
+ });
@@ -165,10 +165,10 @@ export class Game {
165
165
  this.input.boot();
166
166
  this.sound.boot();
167
167
  this.state.boot();
168
- if (window.focus) {
168
+ if (typeof window.focus === 'function') {
169
169
  window.focus();
170
170
  }
171
- if (!this.config.isSkipTicker) {
171
+ if (this.config.isSkipTicker !== true) {
172
172
  this.raf = new RequestAnimationFrame(this);
173
173
  this.raf.start();
174
174
  }
@@ -180,7 +180,7 @@ export class Game {
180
180
  */
181
181
  public createRendererCanvas(): void {
182
182
  this.logger.info('createRendererCanvas');
183
- if (this.canvas) {
183
+ if (this.canvas as HTMLCanvasElement | undefined) {
184
184
  removeFromDOM(this.canvas);
185
185
  }
186
186
  this.canvas = this.config.canvas ?? create(this, this.width, this.height, this.config.canvasID, true);
@@ -212,10 +212,10 @@ export class Game {
212
212
  isWebGlReady = true;
213
213
  } catch (error) {
214
214
  isWebGlReady = false;
215
- if (globalThis.PhaserRegistry?.GL_PROGRAM_INFO_LOG) {
215
+ if (globalThis.PhaserRegistry?.GL_PROGRAM_INFO_LOG != null) {
216
216
  this.logger.warn('WebGL program info', { log: globalThis.PhaserRegistry.GL_PROGRAM_INFO_LOG });
217
217
  }
218
- if (globalThis.PhaserRegistry?.GL_SHADER_INFO_LOG) {
218
+ if (globalThis.PhaserRegistry?.GL_SHADER_INFO_LOG != null) {
219
219
  this.logger.warn('WebGL shader info', { log: globalThis.PhaserRegistry.GL_SHADER_INFO_LOG });
220
220
  }
221
221
  const typedError = error instanceof Error ? error : new Error(String(error));
@@ -229,7 +229,7 @@ export class Game {
229
229
  if (this.contextRestoredBinded) {
230
230
  this.canvas.removeEventListener('webglcontextlost', this.contextRestoredBinded, false);
231
231
  }
232
- if (this.renderer) {
232
+ if (this.renderer as CanvasRenderer | WebGLRenderer | undefined) {
233
233
  this.renderer.destroy();
234
234
  }
235
235
  this.createRendererCanvas();
@@ -278,7 +278,12 @@ export class Game {
278
278
  this.parseConfigElement(config, 'renderType', RENDER_AUTO);
279
279
  this.parseConfigElement(config, 'isForceDisabledAudio', false);
280
280
  this.parseConfigElement(config, 'maxParallelDownloads', 16);
281
- if (config.parent) {
281
+ this.parseConfigElement(config, 'canvas');
282
+ this.parseConfigElement(config, 'isSkipTicker');
283
+ this.parseConfigElement(config, 'scaleMode');
284
+ this.parseConfigElement(config, 'fullScreenScaleMode');
285
+ this.parseConfigElement(config, 'fullScreenTarget');
286
+ if (config.parent !== undefined) {
282
287
  this.parent = config.parent;
283
288
  }
284
289
  this.state = new SceneManager(this, config.state ?? null);
@@ -291,7 +296,7 @@ export class Game {
291
296
  public contextLost(event: Event): void {
292
297
  this.logger.info('contextLost', { type: event.type });
293
298
  event.preventDefault();
294
- if (this.renderer) {
299
+ if (this.renderer as CanvasRenderer | WebGLRenderer | undefined) {
295
300
  this.renderer.contextLost = true;
296
301
  }
297
302
  }
@@ -302,7 +307,7 @@ export class Game {
302
307
  */
303
308
  public contextRestored(event: Event): void {
304
309
  this.logger.info('contextRestored', { type: event.type });
305
- if (this.renderer) {
310
+ if (this.renderer as CanvasRenderer | WebGLRenderer | undefined) {
306
311
  this.renderer.initContext(this);
307
312
  // This.cache.clearGLTextures();
308
313
  this.renderer.contextLost = false;
@@ -340,10 +345,10 @@ export class Game {
340
345
  this.logger.info('destroy');
341
346
  this.isPaused = true;
342
347
 
343
- if (!this.cache) {
348
+ if (!(this.cache as Cache | undefined)) {
344
349
  return;
345
350
  }
346
- if (this.raf) {
351
+ if (this.raf as RequestAnimationFrame | undefined) {
347
352
  this.raf.stop();
348
353
  }
349
354
 
@@ -354,7 +359,7 @@ export class Game {
354
359
  this.stage.destroy();
355
360
  this.input.destroy();
356
361
 
357
- if (this.canvas) {
362
+ if (this.canvas as HTMLCanvasElement | undefined) {
358
363
  if (this.contextLostBinded) {
359
364
  this.canvas.removeEventListener('webglcontextlost', this.contextLostBinded, false);
360
365
  }
@@ -443,8 +443,9 @@ export class Input {
443
443
  }
444
444
  this.getLocalPosition(displayObject, pointer, this._localPoint);
445
445
  localPoint.copyFrom(this._localPoint);
446
- if (displayObject.hitArea && displayObject.hitArea.contains) {
447
- return displayObject.hitArea.contains(this._localPoint.x, this._localPoint.y);
446
+ const { hitArea } = displayObject;
447
+ if (hitArea && (hitArea.contains as ((x: number, y: number) => boolean) | undefined)) {
448
+ return hitArea.contains(this._localPoint.x, this._localPoint.y);
448
449
  } else if (displayObject instanceof Image) {
449
450
  const { width } = displayObject.texture.frame;
450
451
  const { height } = displayObject.texture.frame;
@@ -457,7 +458,7 @@ export class Input {
457
458
  }
458
459
  } else if (displayObject instanceof Graphics) {
459
460
  for (const data of displayObject.graphicsData) {
460
- if (data.fill && data.shape && data.shape.contains(this._localPoint.x, this._localPoint.y)) {
461
+ if (data.fill && (data.shape?.contains(this._localPoint.x, this._localPoint.y) ?? false)) {
461
462
  // Only deal with fills..
462
463
  return true;
463
464
  }
@@ -6,6 +6,7 @@ import type { Image } from '../display/image.js';
6
6
  import type { Pointer } from './input_pointer.js';
7
7
  import type { Rectangle } from '../geom/rectangle.js';
8
8
  import type { Game } from './game.js';
9
+ import type { EventManager } from './event_manager.js';
9
10
 
10
11
  interface PointerData {
11
12
  id: number;
@@ -223,7 +224,7 @@ export class InputHandler {
223
224
  * TBD.
224
225
  */
225
226
  public destroy(): void {
226
- if (this.sprite) {
227
+ if (this.sprite !== null) {
227
228
  if (this._setHandCursor) {
228
229
  this.game.canvas.style.cursor = 'default';
229
230
  this._setHandCursor = false;
@@ -402,7 +403,7 @@ export class InputHandler {
402
403
  if (
403
404
  !pointer.isDown ||
404
405
  !this.enabled ||
405
- !this.sprite ||
406
+ this.sprite === null ||
406
407
  !this.sprite.parent ||
407
408
  !this.sprite.visible ||
408
409
  !this.sprite.parent.visible ||
@@ -430,7 +431,7 @@ export class InputHandler {
430
431
  public checkPointerOver(pointer: Pointer, fastTest = false): boolean {
431
432
  if (
432
433
  !this.enabled ||
433
- !this.sprite ||
434
+ this.sprite === null ||
434
435
  !this.sprite.parent ||
435
436
  !this.sprite.visible ||
436
437
  !this.sprite.parent.visible ||
@@ -511,7 +512,7 @@ export class InputHandler {
511
512
  // Abort. We've been destroyed.
512
513
  return false;
513
514
  }
514
- if (!this.enabled || !this.sprite.visible || !this.sprite.parent?.visible) {
515
+ if (!this.enabled || !this.sprite.visible || this.sprite.parent?.visible !== true) {
515
516
  this._pointerOutHandler(pointer);
516
517
  return false;
517
518
  }
@@ -560,7 +561,7 @@ export class InputHandler {
560
561
  this.game.canvas.style.cursor = 'pointer';
561
562
  this._setHandCursor = true;
562
563
  }
563
- if (!silent && sendEvent && this.sprite && this.sprite.events) {
564
+ if (!silent && sendEvent && (this.sprite?.events as EventManager | undefined)) {
564
565
  this.sprite.events.onInputOver$dispatch(this.sprite, pointer);
565
566
  }
566
567
  if (this.sprite.parent && this.sprite.parent.type === GROUP) {
@@ -587,9 +588,9 @@ export class InputHandler {
587
588
  this.game.canvas.style.cursor = 'default';
588
589
  this._setHandCursor = false;
589
590
  }
590
- if (!silent && this.sprite && this.sprite.events) {
591
+ if (!silent && (this.sprite?.events as EventManager | undefined)) {
591
592
  this.sprite.events.onInputOut$dispatch(this.sprite, pointer);
592
- if (this.sprite && this.sprite.parent && this.sprite.parent.type === GROUP) {
593
+ if (this.sprite?.parent?.type === GROUP) {
593
594
  (this.sprite.parent as Group).onChildInputOut.dispatch(this.sprite, pointer);
594
595
  }
595
596
  }
@@ -615,10 +616,10 @@ export class InputHandler {
615
616
  this.downPoint.setTo(pointer.x, pointer.y);
616
617
  // It's possible the onInputDown event creates a new Sprite that is on-top of this one, so we ought to force a Pointer update
617
618
  pointer.dirty = true;
618
- if (this.sprite && this.sprite.events) {
619
+ if (this.sprite?.events as EventManager | undefined) {
619
620
  this.sprite.events.onInputDown$dispatch(this.sprite, pointer);
620
621
  // The event above might have destroyed this sprite.
621
- if (this.sprite && this.sprite.parent && this.sprite.parent.type === GROUP) {
622
+ if (this.sprite?.parent?.type === GROUP) {
622
623
  (this.sprite.parent as Group).onChildInputDown.dispatch(this.sprite, pointer);
623
624
  }
624
625
  // The events might have destroyed this sprite.
@@ -650,7 +651,7 @@ export class InputHandler {
650
651
  */
651
652
  public dragTimeElapsed(pointer: Pointer): void {
652
653
  this._dragTimePass = true;
653
- if (this._pendingDrag && this.sprite && this._dragDistancePass) {
654
+ if (this._pendingDrag && this.sprite !== null && this._dragDistancePass) {
654
655
  this.startDrag(pointer);
655
656
  }
656
657
  }
@@ -673,14 +674,14 @@ export class InputHandler {
673
674
  data.downDuration = data.timeUp - data.timeDown;
674
675
  // Only release the InputUp signal if the pointer is still over this sprite
675
676
  let isOver = this.checkPointerOver(pointer);
676
- if (this.sprite && this.sprite.events) {
677
+ if (this.sprite?.events as EventManager | undefined) {
677
678
  if (
678
679
  !this.dragStopBlocksInputUp ||
679
680
  (this.dragStopBlocksInputUp && !(this.draggable && this.isDragged && this._draggedPointerID === pointer.id))
680
681
  ) {
681
682
  this.sprite.events.onInputUp$dispatch(this.sprite, pointer, isOver);
682
683
  }
683
- if (this.sprite && this.sprite.parent && this.sprite.parent.type === GROUP) {
684
+ if (this.sprite?.parent?.type === GROUP) {
684
685
  (this.sprite.parent as Group).onChildInputUp.dispatch(this.sprite, pointer, isOver);
685
686
  }
686
687
  // The onInputUp event may have changed the sprite so that checkPointerOver is no longer true, so update it.
@@ -843,7 +844,7 @@ export class InputHandler {
843
844
  * TBD.
844
845
  */
845
846
  public disableDrag(): void {
846
- if (this._pointerData) {
847
+ if (this._pointerData.length > 0) {
847
848
  for (let i = 0; i < 10; i += 1) {
848
849
  this.pointerData(i).isDragged = false;
849
850
  }
@@ -106,7 +106,7 @@ export class Mouse {
106
106
  canvas.addEventListener('mouseover', this._onMouseOver, true);
107
107
  canvas.addEventListener('mouseout', this._onMouseOut, true);
108
108
  const { wheelEvent } = this.game.device;
109
- if (wheelEvent) {
109
+ if (wheelEvent !== null) {
110
110
  canvas.addEventListener(wheelEvent, this._onMouseWheel, true);
111
111
  }
112
112
  }
@@ -132,13 +132,13 @@ export class Mouse {
132
132
  canvas.removeEventListener('mouseout', this._onMouseOut, true);
133
133
  }
134
134
  const { wheelEvent } = this.game.device;
135
- if (wheelEvent && this._onMouseWheel) {
135
+ if (wheelEvent !== null && this._onMouseWheel) {
136
136
  canvas.removeEventListener(wheelEvent, this._onMouseWheel, true);
137
137
  }
138
- if (this._onMouseUpGlobal) {
138
+ if (this._onMouseUpGlobal !== null) {
139
139
  globalThis.removeEventListener('mouseup', this._onMouseUpGlobal, true);
140
140
  }
141
- if (this._onMouseOutGlobal) {
141
+ if (this._onMouseOutGlobal !== null) {
142
142
  globalThis.removeEventListener('mouseout', this._onMouseOutGlobal, true);
143
143
  }
144
144
  }
@@ -149,7 +149,7 @@ export class Pointer {
149
149
  */
150
150
  public start(event: InputEvent): this {
151
151
  const { input } = this.game;
152
- if (event.pointerId) {
152
+ if (event.pointerId !== undefined) {
153
153
  this.pointerId = event.pointerId;
154
154
  }
155
155
  this.identifier = event.identifier ?? null;