@vpmedia/phaser 1.117.0 → 1.119.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/dist/index.js +163 -158
  2. package/dist/index.js.map +1 -1
  3. package/dist/phaser/core/animation_manager.d.ts +1 -1
  4. package/dist/phaser/core/animation_manager.d.ts.map +1 -1
  5. package/dist/phaser/core/cache.d.ts +70 -42
  6. package/dist/phaser/core/cache.d.ts.map +1 -1
  7. package/dist/phaser/core/game.d.ts +34 -8
  8. package/dist/phaser/core/game.d.ts.map +1 -1
  9. package/dist/phaser/core/input.d.ts +1 -1
  10. package/dist/phaser/core/input.d.ts.map +1 -1
  11. package/dist/phaser/core/input_mspointer.d.ts +8 -7
  12. package/dist/phaser/core/input_mspointer.d.ts.map +1 -1
  13. package/dist/phaser/core/loader.d.ts +30 -1
  14. package/dist/phaser/core/loader.d.ts.map +1 -1
  15. package/dist/phaser/core/loader_parser.d.ts +24 -4
  16. package/dist/phaser/core/loader_parser.d.ts.map +1 -1
  17. package/dist/phaser/core/scene.d.ts +3 -2
  18. package/dist/phaser/core/scene.d.ts.map +1 -1
  19. package/dist/phaser/core/scene_manager.d.ts +28 -17
  20. package/dist/phaser/core/scene_manager.d.ts.map +1 -1
  21. package/dist/phaser/core/sound.d.ts +13 -3
  22. package/dist/phaser/core/sound.d.ts.map +1 -1
  23. package/dist/phaser/core/tween.d.ts +3 -3
  24. package/dist/phaser/core/tween.d.ts.map +1 -1
  25. package/dist/phaser/core/tween_manager.d.ts +8 -6
  26. package/dist/phaser/core/tween_manager.d.ts.map +1 -1
  27. package/dist/phaser/display/bitmap_text.d.ts +13 -8
  28. package/dist/phaser/display/bitmap_text.d.ts.map +1 -1
  29. package/package.json +1 -1
  30. package/src/phaser/core/animation_manager.ts +1 -1
  31. package/src/phaser/core/cache.test.ts +201 -0
  32. package/src/phaser/core/cache.ts +126 -86
  33. package/src/phaser/core/game.ts +45 -22
  34. package/src/phaser/core/input.ts +1 -1
  35. package/src/phaser/core/input_mspointer.ts +10 -13
  36. package/src/phaser/core/loader.ts +82 -42
  37. package/src/phaser/core/loader_parser.test.ts +115 -0
  38. package/src/phaser/core/loader_parser.ts +64 -36
  39. package/src/phaser/core/scene.ts +4 -2
  40. package/src/phaser/core/scene_manager.test.ts +193 -0
  41. package/src/phaser/core/scene_manager.ts +51 -36
  42. package/src/phaser/core/sound.ts +29 -17
  43. package/src/phaser/core/sound_manager.ts +1 -1
  44. package/src/phaser/core/tween.ts +6 -6
  45. package/src/phaser/core/tween_manager.ts +42 -35
  46. package/src/phaser/display/bitmap_text.test.ts +212 -0
  47. package/src/phaser/display/bitmap_text.ts +39 -24
  48. package/src/phaser/display/webgl/renderer.ts +1 -1
@@ -8,6 +8,37 @@ import { Signal } from './signal.js';
8
8
 
9
9
  const TEXTURE_ATLAS_JSON_HASH = 1;
10
10
 
11
+ /**
12
+ * What a completed load carries. Which member applies is decided by the file's type, so the
13
+ * completion handlers narrow before handing it to the cache.
14
+ */
15
+ /** One entry in a pack manifest: the arguments for the loader call it stands for. */
16
+ export type PackEntry = {
17
+ type: string;
18
+ key: string;
19
+ url?: string;
20
+ overwrite?: boolean;
21
+ frameWidth?: number;
22
+ frameHeight?: number;
23
+ frameMax?: number;
24
+ margin?: number;
25
+ spacing?: number;
26
+ urls?: string[] | string;
27
+ autoDecode?: boolean;
28
+ textureURL?: string;
29
+ atlasURL?: string;
30
+ atlasData?: unknown;
31
+ audioURL?: string;
32
+ jsonURL?: string;
33
+ jsonData?: unknown;
34
+ fontDataURL?: string;
35
+ xSpacing?: number;
36
+ ySpacing?: number;
37
+ format?: unknown;
38
+ };
39
+
40
+ export type LoaderFileData = HTMLImageElement | XMLDocument | ArrayBuffer | string | Record<string, unknown> | null;
41
+
11
42
  /** An audio source, either a plain URL or a URL paired with its format. */
12
43
  export type AudioSource = string | { uri: string; type?: string };
13
44
 
@@ -21,7 +52,7 @@ export type LoaderFile = {
21
52
  path: string;
22
53
  url: string | AudioSource[];
23
54
  syncPoint: boolean;
24
- data: any;
55
+ data: LoaderFileData;
25
56
  loading: boolean;
26
57
  loaded: boolean;
27
58
  error: boolean;
@@ -311,7 +342,7 @@ export class Loader {
311
342
  url,
312
343
  path: this.path,
313
344
  syncPoint: true,
314
- data: null as unknown,
345
+ data: null as LoaderFileData,
315
346
  loading: false,
316
347
  loaded: false,
317
348
  error: false,
@@ -321,7 +352,7 @@ export class Loader {
321
352
  if (typeof data === 'string') {
322
353
  data = JSON.parse(data);
323
354
  }
324
- pack.data = data ?? {};
355
+ pack.data = (data ?? {}) as LoaderFileData;
325
356
  pack.loaded = true;
326
357
  }
327
358
  for (let i = 0; i < this._fileList.length + 1; i += 1) {
@@ -755,52 +786,53 @@ export class Loader {
755
786
  * @param {object} pack - The pack file object to process.
756
787
  */
757
788
  public processPack(pack: LoaderFile): void {
758
- const packData = pack.data[pack.key];
789
+ const manifest = (pack.data ?? {}) as Record<string, unknown>;
790
+ const packData = manifest[pack.key] as { files?: unknown[] } | unknown[] | undefined;
759
791
  if (!packData) {
760
792
  this.game.logger.warn('Missing loader pack key', { key: pack.key });
761
793
  return;
762
794
  }
763
- const packDataCompat = Array.isArray(packData) ? packData : packData.files;
795
+ const packDataCompat = (Array.isArray(packData) ? packData : (packData.files ?? [])) as PackEntry[];
764
796
  for (const file of packDataCompat) {
765
797
  switch (file.type) {
766
798
  case 'image': {
767
- this.image(file.key, file.url, file.overwrite);
799
+ this.image(file.key, file.url ?? file.key, file.overwrite);
768
800
  break;
769
801
  }
770
802
  case 'text': {
771
- this.text(file.key, file.url, file.overwrite);
803
+ this.text(file.key, file.url ?? file.key, file.overwrite);
772
804
  break;
773
805
  }
774
806
  case 'json': {
775
- this.json(file.key, file.url, file.overwrite);
807
+ this.json(file.key, file.url ?? file.key, file.overwrite);
776
808
  break;
777
809
  }
778
810
  case 'xml': {
779
- this.xml(file.key, file.url, file.overwrite);
811
+ this.xml(file.key, file.url ?? file.key, file.overwrite);
780
812
  break;
781
813
  }
782
814
  case 'spritesheet': {
783
815
  this.spritesheet(
784
816
  file.key,
785
- file.url,
786
- file.frameWidth,
787
- file.frameHeight,
788
- file.frameMax,
817
+ file.url ?? file.key,
818
+ file.frameWidth ?? 0,
819
+ file.frameHeight ?? 0,
820
+ file.frameMax ?? -1,
789
821
  file.margin,
790
822
  file.spacing
791
823
  );
792
824
  break;
793
825
  }
794
826
  case 'audio': {
795
- this.audio(file.key, file.urls ?? file.url);
827
+ this.audio(file.key, file.urls ?? file.url ?? file.key);
796
828
  break;
797
829
  }
798
830
  case 'audiosprite': {
799
- this.audioSprite(file.key, file.urls ?? file.url, file.jsonURL, file.jsonData);
831
+ this.audioSprite(file.key, String(file.urls ?? file.url ?? file.key), file.jsonURL ?? '', file.jsonData);
800
832
  break;
801
833
  }
802
834
  case 'audioSprite': {
803
- this.audioSprite(file.key, file.audioURL, file.jsonURL, file.jsonData);
835
+ this.audioSprite(file.key, file.audioURL ?? file.key, file.jsonURL ?? '', file.jsonData);
804
836
  break;
805
837
  }
806
838
  case 'bitmapFont': {
@@ -815,7 +847,7 @@ export class Loader {
815
847
  break;
816
848
  }
817
849
  case 'atlas': {
818
- this.atlas(file.key, file.textureURL, file.atlasURL, file.atlasData, TEXTURE_ATLAS_JSON_HASH);
850
+ this.atlas(file.key, file.textureURL ?? file.key, file.atlasURL, file.atlasData, TEXTURE_ATLAS_JSON_HASH);
819
851
  break;
820
852
  }
821
853
  }
@@ -898,31 +930,37 @@ export class Loader {
898
930
  public loadImageTag(file: LoaderFile): void {
899
931
  this.log('loadImageTag', file);
900
932
  const scope = this;
901
- file.data = new globalThis.Image();
902
- file.data.name = file.key;
903
- if (this.crossOrigin) {
904
- file.data.crossOrigin = this.crossOrigin;
933
+ const image = new globalThis.Image();
934
+ file.data = image;
935
+ image.name = file.key;
936
+ if (typeof this.crossOrigin === 'string' && this.crossOrigin) {
937
+ image.crossOrigin = this.crossOrigin;
905
938
  }
906
- file.data.onload = (): void => {
907
- if (file.data.onload) {
908
- file.data.onload = null;
909
- file.data.onerror = null;
939
+ image.onload = (): void => {
940
+ if (image.onload) {
941
+ image.onload = null;
942
+ image.onerror = null;
910
943
  scope.fileComplete(file);
911
944
  }
912
945
  };
913
- file.data.onerror = (): void => {
946
+ image.onerror = (): void => {
914
947
  if (scope.isUseRetry && (!file.numRetry || file.numRetry < scope.maxRetry)) {
915
948
  setTimeout((): void => {
916
949
  file.numRetry = !file.numRetry ? 1 : (file.numRetry += 1);
917
950
  scope.loadImageTag(file);
918
951
  }, 1000);
919
- } else if (file.data.onload) {
920
- file.data.onload = null;
921
- file.data.onerror = null;
952
+ } else if (image.onload) {
953
+ image.onload = null;
954
+ image.onerror = null;
922
955
  scope.fileError(file);
923
956
  }
924
957
  };
925
- file.data.src = this.transformUrl(file.url, file);
958
+ const src = this.transformUrl(file.url, file);
959
+ if (src === false) {
960
+ this.fileError(file, null, 'Could not resolve the file URL');
961
+ return;
962
+ }
963
+ image.src = src;
926
964
  }
927
965
 
928
966
  /**
@@ -1091,14 +1129,14 @@ export class Loader {
1091
1129
  break;
1092
1130
  }
1093
1131
  case 'image': {
1094
- this.cache.addImage(file.key, url, file.data);
1132
+ this.cache.addImage(file.key, url, file.data as HTMLImageElement);
1095
1133
  break;
1096
1134
  }
1097
1135
  case 'spritesheet': {
1098
1136
  this.cache.addSpriteSheet(
1099
1137
  file.key,
1100
1138
  url,
1101
- file.data,
1139
+ file.data as HTMLImageElement,
1102
1140
  file.frameWidth ?? 0,
1103
1141
  file.frameHeight ?? 0,
1104
1142
  file.frameMax ?? -1,
@@ -1109,7 +1147,7 @@ export class Loader {
1109
1147
  }
1110
1148
  case 'textureatlas': {
1111
1149
  if (file.atlasURL == null) {
1112
- this.cache.addTextureAtlas(file.key, url, file.data, file.atlasData);
1150
+ this.cache.addTextureAtlas(file.key, url, file.data as HTMLImageElement, file.atlasData);
1113
1151
  } else {
1114
1152
  loadNext = false;
1115
1153
  if (file.format === TEXTURE_ATLAS_JSON_HASH) {
@@ -1125,7 +1163,7 @@ export class Loader {
1125
1163
  this.cache.addBitmapFont(
1126
1164
  file.key,
1127
1165
  url,
1128
- file.data,
1166
+ file.data as HTMLImageElement,
1129
1167
  file.atlasData,
1130
1168
  file.atlasType ?? 'xml',
1131
1169
  file.xSpacing ?? 0,
@@ -1159,16 +1197,18 @@ export class Loader {
1159
1197
  break;
1160
1198
  }
1161
1199
  case 'audio': {
1162
- file.data = response.response;
1163
- this.cache.addSound(file.key, url, file.data);
1200
+ const audio = response.response as ArrayBuffer;
1201
+ file.data = audio;
1202
+ this.cache.addSound(file.key, url, audio);
1164
1203
  if (file.autoDecode) {
1165
1204
  this.game.sound.decode(file.key);
1166
1205
  }
1167
1206
  break;
1168
1207
  }
1169
1208
  case 'text': {
1170
- file.data = response.responseText;
1171
- this.cache.addText(file.key, url, file.data);
1209
+ const { responseText } = response;
1210
+ file.data = responseText;
1211
+ this.cache.addText(file.key, url, responseText);
1172
1212
  break;
1173
1213
  }
1174
1214
  default: {
@@ -1193,7 +1233,7 @@ export class Loader {
1193
1233
  this.cache.addBitmapFont(
1194
1234
  file.key,
1195
1235
  url,
1196
- file.data,
1236
+ file.data as HTMLImageElement,
1197
1237
  data,
1198
1238
  file.atlasType ?? 'json',
1199
1239
  file.xSpacing ?? 0,
@@ -1202,7 +1242,7 @@ export class Loader {
1202
1242
  } else if (file.type === 'json') {
1203
1243
  this.cache.addJSON(file.key, url, data);
1204
1244
  } else {
1205
- this.cache.addTextureAtlas(file.key, url, file.data, data);
1245
+ this.cache.addTextureAtlas(file.key, url, file.data as HTMLImageElement, data);
1206
1246
  }
1207
1247
  this.asyncComplete(file);
1208
1248
  }
@@ -1235,14 +1275,14 @@ export class Loader {
1235
1275
  this.cache.addBitmapFont(
1236
1276
  file.key,
1237
1277
  url,
1238
- file.data,
1278
+ file.data as HTMLImageElement,
1239
1279
  xml,
1240
1280
  file.atlasType ?? 'xml',
1241
1281
  file.xSpacing ?? 0,
1242
1282
  file.ySpacing ?? 0
1243
1283
  );
1244
1284
  } else if (file.type === 'textureatlas') {
1245
- this.cache.addTextureAtlas(file.key, url, file.data, xml);
1285
+ this.cache.addTextureAtlas(file.key, url, file.data as HTMLImageElement, xml);
1246
1286
  } else if (file.type === 'xml') {
1247
1287
  this.cache.addXML(file.key, url, xml);
1248
1288
  }
@@ -0,0 +1,115 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import type { BaseTexture } from '../display/webgl/base_texture.js';
3
+ import { bitmapFont, jsonBitmapFont, xmlBitmapFont } from './loader_parser.js';
4
+
5
+ const baseTexture = { width: 256, height: 256 } as unknown as BaseTexture;
6
+
7
+ const parseXml = (markup: string): XMLDocument => new DOMParser().parseFromString(markup, 'text/xml');
8
+
9
+ const FONT_XML = `<?xml version="1.0"?>
10
+ <font>
11
+ <info face="Arial" size="32" />
12
+ <common lineHeight="40" />
13
+ <chars>
14
+ <char id="65" x="0" y="0" width="20" height="30" xoffset="1" yoffset="2" xadvance="21" />
15
+ <char id="66" x="20" y="0" width="22" height="30" xoffset="0" yoffset="2" xadvance="23" />
16
+ </chars>
17
+ <kernings>
18
+ <kerning first="65" second="66" amount="-3" />
19
+ </kernings>
20
+ </font>`;
21
+
22
+ const FONT_JSON = {
23
+ font: {
24
+ info: { _face: 'Arial', _size: '32' },
25
+ common: { _lineHeight: '40' },
26
+ chars: {
27
+ char: [
28
+ { _id: '65', _x: '0', _y: '0', _width: '20', _height: '30', _xoffset: '1', _yoffset: '2', _xadvance: '21' },
29
+ { _id: '66', _x: '20', _y: '0', _width: '22', _height: '30', _xoffset: '0', _yoffset: '2', _xadvance: '23' },
30
+ ],
31
+ },
32
+ kernings: { kerning: [{ _first: '65', _second: '66', _amount: '-3' }] },
33
+ },
34
+ };
35
+
36
+ describe('bitmap font parsing', () => {
37
+ describe('xmlBitmapFont', () => {
38
+ it('reads the font metrics', () => {
39
+ const data = xmlBitmapFont(parseXml(FONT_XML), baseTexture, 0, 0);
40
+ expect(data.font).toBe('Arial');
41
+ expect(data.size).toBe(32);
42
+ expect(data.lineHeight).toBe(40);
43
+ });
44
+
45
+ it('reads every glyph, keyed by character code', () => {
46
+ const data = xmlBitmapFont(parseXml(FONT_XML), baseTexture, 0, 0);
47
+ expect(Object.keys(data.chars)).toStrictEqual(['65', '66']);
48
+ expect(data.chars[65]).toMatchObject({ x: 0, y: 0, width: 20, height: 30, xOffset: 1, yOffset: 2 });
49
+ });
50
+
51
+ it('adds the spacing arguments to advance and line height', () => {
52
+ const data = xmlBitmapFont(parseXml(FONT_XML), baseTexture, 5, 7);
53
+ expect(data.chars[65]?.xAdvance).toBe(26);
54
+ expect(data.lineHeight).toBe(47);
55
+ });
56
+
57
+ it('records kerning against the second character of the pair', () => {
58
+ const data = xmlBitmapFont(parseXml(FONT_XML), baseTexture, 0, 0);
59
+ expect(data.chars[66]?.kerning[65]).toBe(-3);
60
+ });
61
+
62
+ it('attaches a texture to every glyph', () => {
63
+ const data = xmlBitmapFont(parseXml(FONT_XML), baseTexture, 0, 0);
64
+ expect(data.chars[65]?.texture).toBeDefined();
65
+ expect(data.chars[66]?.texture).toBeDefined();
66
+ });
67
+
68
+ it('survives a font with no kerning table', () => {
69
+ const markup = FONT_XML.replace(/<kernings>[\s\S]*<\/kernings>/, '');
70
+ expect(() => xmlBitmapFont(parseXml(markup), baseTexture, 0, 0)).not.toThrow();
71
+ });
72
+
73
+ it('ignores kerning that names a character the font does not have', () => {
74
+ const markup = FONT_XML.replace('second="66"', 'second="999"');
75
+ const data = xmlBitmapFont(parseXml(markup), baseTexture, 0, 0);
76
+ expect(data.chars[66]?.kerning).toStrictEqual({});
77
+ });
78
+
79
+ it('falls back to an empty face when the info element is missing', () => {
80
+ const markup = FONT_XML.replace(/<info[^>]*\/>/, '');
81
+ expect(xmlBitmapFont(parseXml(markup), baseTexture, 0, 0).font).toBe('');
82
+ });
83
+ });
84
+
85
+ describe('jsonBitmapFont', () => {
86
+ it('reads the same metrics as the xml form', () => {
87
+ const data = jsonBitmapFont(FONT_JSON, baseTexture, 0, 0);
88
+ expect(data.font).toBe('Arial');
89
+ expect(data.size).toBe(32);
90
+ expect(data.lineHeight).toBe(40);
91
+ });
92
+
93
+ it('produces the same glyphs as the xml form', () => {
94
+ const fromJson = jsonBitmapFont(FONT_JSON, baseTexture, 0, 0);
95
+ const fromXml = xmlBitmapFont(parseXml(FONT_XML), baseTexture, 0, 0);
96
+ for (const code of [65, 66]) {
97
+ expect(fromJson.chars[code]).toMatchObject({
98
+ x: fromXml.chars[code]!.x,
99
+ width: fromXml.chars[code]!.width,
100
+ xAdvance: fromXml.chars[code]!.xAdvance,
101
+ });
102
+ }
103
+ });
104
+
105
+ it('records the same kerning as the xml form', () => {
106
+ expect(jsonBitmapFont(FONT_JSON, baseTexture, 0, 0).chars[66]?.kerning[65]).toBe(-3);
107
+ });
108
+ });
109
+
110
+ describe('bitmapFont', () => {
111
+ it('parses the xml form', () => {
112
+ expect(bitmapFont(parseXml(FONT_XML), baseTexture, 0, 0).font).toBe('Arial');
113
+ });
114
+ });
115
+ });
@@ -2,17 +2,37 @@ import { Texture } from '../display/webgl/texture.js';
2
2
  import { Rectangle } from '../geom/rectangle.js';
3
3
  import type { BaseTexture } from '../display/webgl/base_texture.js';
4
4
 
5
+ /** One glyph in a bitmap font, as parsed from the font descriptor. */
6
+ export type BitmapFontChar = {
7
+ x: number;
8
+ y: number;
9
+ width: number;
10
+ height: number;
11
+ xOffset: number;
12
+ yOffset: number;
13
+ xAdvance: number;
14
+ kerning: Record<number, number>;
15
+ texture?: Texture;
16
+ };
17
+
18
+ /** A parsed bitmap font: its metrics and the glyphs it can draw. */
19
+ export type BitmapFontData = {
20
+ font: string;
21
+ size: number;
22
+ lineHeight: number;
23
+ chars: Record<number, BitmapFontChar>;
24
+ };
25
+
5
26
  /**
6
27
  * Finalizes bitmap font data by attaching textures to characters.
7
28
  * @param {BaseTexture} baseTexture - The base texture for the font.
8
29
  * @param {object} bitmapFontData - The bitmap font data to finalize.
9
30
  * @returns {object} The finalized bitmap font data.
10
31
  */
11
- export const finalizeBitmapFont = (baseTexture: BaseTexture, bitmapFontData: any) => {
12
- Object.keys(bitmapFontData.chars).forEach((charCode): void => {
13
- const letter = bitmapFontData.chars[charCode];
32
+ export const finalizeBitmapFont = (baseTexture: BaseTexture, bitmapFontData: BitmapFontData): BitmapFontData => {
33
+ for (const letter of Object.values(bitmapFontData.chars)) {
14
34
  letter.texture = new Texture(baseTexture, new Rectangle(letter.x, letter.y, letter.width, letter.height));
15
- });
35
+ }
16
36
  return bitmapFontData;
17
37
  };
18
38
 
@@ -24,34 +44,38 @@ export const finalizeBitmapFont = (baseTexture: BaseTexture, bitmapFontData: any
24
44
  * @param {number} ySpacing - Vertical spacing between characters.
25
45
  * @returns {object} The parsed bitmap font data.
26
46
  */
27
- export const xmlBitmapFont = (xml: any, baseTexture: BaseTexture, xSpacing: number, ySpacing: number) => {
28
- const data: any = {};
47
+ export const xmlBitmapFont = (
48
+ xml: XMLDocument,
49
+ baseTexture: BaseTexture,
50
+ xSpacing: number,
51
+ ySpacing: number
52
+ ): BitmapFontData => {
29
53
  const info = xml.querySelectorAll('info')[0];
30
54
  const common = xml.querySelectorAll('common')[0];
31
- data.font = info.getAttribute('face');
32
- data.size = Math.trunc(Number(info.getAttribute('size')));
33
- data.lineHeight = Math.trunc(Number(common.getAttribute('lineHeight'))) + ySpacing;
34
- data.chars = {};
35
- const letters = xml.querySelectorAll('char');
36
- for (let i = 0; i < letters.length; i += 1) {
37
- const charCode = Math.trunc(Number(letters[i].getAttribute('id')));
38
- data.chars[charCode] = {
39
- x: Math.trunc(Number(letters[i].getAttribute('x'))),
40
- y: Math.trunc(Number(letters[i].getAttribute('y'))),
41
- width: Math.trunc(Number(letters[i].getAttribute('width'))),
42
- height: Math.trunc(Number(letters[i].getAttribute('height'))),
43
- xOffset: Math.trunc(Number(letters[i].getAttribute('xoffset'))),
44
- yOffset: Math.trunc(Number(letters[i].getAttribute('yoffset'))),
45
- xAdvance: Math.trunc(Number(letters[i].getAttribute('xadvance'))) + xSpacing,
55
+ const attr = (element: Element | undefined, name: string): number => Math.trunc(Number(element?.getAttribute(name)));
56
+ const data: BitmapFontData = {
57
+ font: info?.getAttribute('face') ?? '',
58
+ size: attr(info, 'size'),
59
+ lineHeight: attr(common, 'lineHeight') + ySpacing,
60
+ chars: {},
61
+ };
62
+ for (const letter of xml.querySelectorAll('char')) {
63
+ data.chars[attr(letter, 'id')] = {
64
+ x: attr(letter, 'x'),
65
+ y: attr(letter, 'y'),
66
+ width: attr(letter, 'width'),
67
+ height: attr(letter, 'height'),
68
+ xOffset: attr(letter, 'xoffset'),
69
+ yOffset: attr(letter, 'yoffset'),
70
+ xAdvance: attr(letter, 'xadvance') + xSpacing,
46
71
  kerning: {},
47
72
  };
48
73
  }
49
- const kernings = xml.querySelectorAll('kerning');
50
- for (let i = 0; i < kernings.length; i += 1) {
51
- const first = Math.trunc(Number(kernings[i].getAttribute('first')));
52
- const second = Math.trunc(Number(kernings[i].getAttribute('second')));
53
- const amount = Math.trunc(Number(kernings[i].getAttribute('amount')));
54
- data.chars[second].kerning[first] = amount;
74
+ for (const kerning of xml.querySelectorAll('kerning')) {
75
+ const char = data.chars[attr(kerning, 'second')];
76
+ if (char) {
77
+ char.kerning[attr(kerning, 'first')] = attr(kerning, 'amount');
78
+ }
55
79
  }
56
80
  return finalizeBitmapFont(baseTexture, data);
57
81
  };
@@ -64,8 +88,12 @@ export const xmlBitmapFont = (xml: any, baseTexture: BaseTexture, xSpacing: numb
64
88
  * @param {number} ySpacing - Vertical spacing between characters.
65
89
  * @returns {object} The parsed bitmap font data.
66
90
  */
67
- export const bitmapFont = (xml: any, baseTexture: BaseTexture, xSpacing: number, ySpacing: number) =>
68
- xmlBitmapFont(xml, baseTexture, xSpacing, ySpacing);
91
+ export const bitmapFont = (
92
+ xml: XMLDocument,
93
+ baseTexture: BaseTexture,
94
+ xSpacing: number,
95
+ ySpacing: number
96
+ ): BitmapFontData => xmlBitmapFont(xml, baseTexture, xSpacing, ySpacing);
69
97
 
70
98
  /**
71
99
  * Parses JSON bitmap font data.
@@ -75,13 +103,13 @@ export const bitmapFont = (xml: any, baseTexture: BaseTexture, xSpacing: number,
75
103
  * @param {number} ySpacing - Vertical spacing between characters.
76
104
  * @returns {object} The parsed bitmap font data.
77
105
  */
78
- export const jsonBitmapFont = (json: any, baseTexture: BaseTexture, xSpacing: number, ySpacing: number) => {
79
- const data: {
80
- font: string;
81
- size: number;
82
- lineHeight: number;
83
- chars: Record<number, { kerning: Record<number, number>; [key: string]: unknown }>;
84
- } = {
106
+ export const jsonBitmapFont = (
107
+ json: any,
108
+ baseTexture: BaseTexture,
109
+ xSpacing: number,
110
+ ySpacing: number
111
+ ): BitmapFontData => {
112
+ const data: BitmapFontData = {
85
113
  font: json.font.info._face,
86
114
  size: Math.trunc(Number(json.font.info._size)),
87
115
  lineHeight: Math.trunc(Number(json.font.common._lineHeight)) + ySpacing,
@@ -1,6 +1,8 @@
1
+ import type { Game } from './game.js';
2
+
1
3
  export class Scene {
2
- public game: any = null;
3
- public key: any = '';
4
+ public game: Game | null = null;
5
+ public key = '';
4
6
  /**
5
7
  * Creates a new Scene instance.
6
8
  */