@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.
- package/dist/index.js +312 -309
- package/dist/index.js.map +1 -1
- package/dist/phaser/core/animation_parser.d.ts.map +1 -1
- package/dist/phaser/core/device_util.d.ts.map +1 -1
- package/dist/phaser/core/dom.d.ts.map +1 -1
- package/dist/phaser/core/frame_data.d.ts.map +1 -1
- package/dist/phaser/core/game.d.ts.map +1 -1
- package/dist/phaser/core/input.d.ts.map +1 -1
- package/dist/phaser/core/input_handler.d.ts.map +1 -1
- package/dist/phaser/core/loader.d.ts.map +1 -1
- package/dist/phaser/core/scale_manager.d.ts.map +1 -1
- package/dist/phaser/core/sound.d.ts.map +1 -1
- package/dist/phaser/core/sound_manager.d.ts.map +1 -1
- package/dist/phaser/core/tween.d.ts.map +1 -1
- package/dist/phaser/display/canvas/renderer.d.ts.map +1 -1
- package/dist/phaser/display/display_object.d.ts +2 -2
- package/dist/phaser/display/display_object.d.ts.map +1 -1
- package/dist/phaser/display/graphics.d.ts +1 -1
- package/dist/phaser/display/graphics.d.ts.map +1 -1
- package/dist/phaser/display/group.d.ts.map +1 -1
- package/dist/phaser/display/image.d.ts.map +1 -1
- package/dist/phaser/display/text.d.ts.map +1 -1
- package/dist/phaser/display/webgl/earcut.d.ts.map +1 -1
- package/dist/phaser/display/webgl/texture.d.ts.map +1 -1
- package/dist/phaser/geom/rectangle.d.ts.map +1 -1
- package/dist/phaser/geom/util/circle.d.ts.map +1 -1
- package/dist/phaser/geom/util/point.d.ts.map +1 -1
- package/dist/phaser/util/math.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/phaser/core/animation.ts +7 -7
- package/src/phaser/core/animation_manager.ts +2 -2
- package/src/phaser/core/animation_parser.ts +11 -15
- package/src/phaser/core/array_set.ts +2 -2
- package/src/phaser/core/cache.ts +4 -4
- package/src/phaser/core/device_util.ts +7 -4
- package/src/phaser/core/dom.ts +24 -19
- package/src/phaser/core/frame_data.ts +2 -5
- package/src/phaser/core/game.test.ts +131 -0
- package/src/phaser/core/game.ts +17 -12
- package/src/phaser/core/input.ts +4 -3
- package/src/phaser/core/input_handler.ts +14 -13
- package/src/phaser/core/input_mouse.ts +4 -4
- package/src/phaser/core/input_pointer.ts +1 -1
- package/src/phaser/core/loader.ts +38 -50
- package/src/phaser/core/scale_manager.ts +10 -9
- package/src/phaser/core/scene_manager.ts +2 -2
- package/src/phaser/core/signal.ts +2 -2
- package/src/phaser/core/sound.ts +25 -32
- package/src/phaser/core/sound_manager.ts +12 -16
- package/src/phaser/core/sound_sprite.ts +1 -1
- package/src/phaser/core/stage.ts +3 -3
- package/src/phaser/core/timer.ts +1 -1
- package/src/phaser/core/tween.ts +6 -11
- package/src/phaser/display/bitmap_text.ts +3 -3
- package/src/phaser/display/button.ts +12 -12
- package/src/phaser/display/canvas/graphics.ts +2 -2
- package/src/phaser/display/canvas/pool.ts +3 -3
- package/src/phaser/display/canvas/renderer.ts +6 -5
- package/src/phaser/display/canvas/tinter.ts +1 -1
- package/src/phaser/display/canvas/util.ts +2 -2
- package/src/phaser/display/display_object.ts +13 -14
- package/src/phaser/display/graphics.test.ts +2 -2
- package/src/phaser/display/graphics.ts +9 -11
- package/src/phaser/display/group.ts +10 -9
- package/src/phaser/display/image.ts +7 -6
- package/src/phaser/display/sprite_util.ts +1 -1
- package/src/phaser/display/text.ts +35 -44
- package/src/phaser/display/webgl/abstract_filter.ts +1 -1
- package/src/phaser/display/webgl/earcut.ts +46 -42
- package/src/phaser/display/webgl/graphics.ts +2 -2
- package/src/phaser/display/webgl/renderer.ts +3 -3
- package/src/phaser/display/webgl/shader/normal.ts +6 -6
- package/src/phaser/display/webgl/shader_manager.ts +1 -1
- package/src/phaser/display/webgl/sprite_batch.ts +2 -2
- package/src/phaser/display/webgl/texture.ts +2 -1
- package/src/phaser/display/webgl/util.ts +3 -3
- package/src/phaser/geom/polygon.ts +2 -2
- package/src/phaser/geom/rectangle.ts +1 -2
- package/src/phaser/geom/util/circle.ts +6 -10
- package/src/phaser/geom/util/point.ts +5 -7
- 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
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
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 <
|
|
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
|
|
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,
|
|
68
|
-
x +=
|
|
69
|
-
if (x +
|
|
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 +=
|
|
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?.();
|
package/src/phaser/core/cache.ts
CHANGED
|
@@ -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 (
|
|
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 =
|
|
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 (
|
|
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 (
|
|
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;
|
package/src/phaser/core/dom.ts
CHANGED
|
@@ -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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
202
|
+
'pageXOffset' in globalThis
|
|
203
203
|
? (): number => window.pageXOffset
|
|
204
204
|
: (): number => document.documentElement.scrollLeft;
|
|
205
205
|
this.scrollYProvider =
|
|
206
|
-
|
|
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
|
-
|
|
237
|
-
|
|
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(
|
|
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
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
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
|
-
|
|
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
|
|
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
|
+
});
|
package/src/phaser/core/game.ts
CHANGED
|
@@ -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 (
|
|
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
|
-
|
|
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
|
}
|
package/src/phaser/core/input.ts
CHANGED
|
@@ -443,8 +443,9 @@ export class Input {
|
|
|
443
443
|
}
|
|
444
444
|
this.getLocalPosition(displayObject, pointer, this._localPoint);
|
|
445
445
|
localPoint.copyFrom(this._localPoint);
|
|
446
|
-
|
|
447
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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 ||
|
|
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
|
|
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
|
|
591
|
+
if (!silent && (this.sprite?.events as EventManager | undefined)) {
|
|
591
592
|
this.sprite.events.onInputOut$dispatch(this.sprite, pointer);
|
|
592
|
-
if (this.sprite
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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;
|