excalibur 0.26.0-alpha.310 → 0.26.0-alpha.314

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 (104) hide show
  1. package/CHANGELOG.md +9 -2
  2. package/build/dist/Actor.js +3 -1
  3. package/build/dist/Actor.js.map +1 -1
  4. package/build/dist/Collision/Detection/DynamicTreeCollisionProcessor.d.ts +2 -2
  5. package/build/dist/Collision/Detection/DynamicTreeCollisionProcessor.js +9 -26
  6. package/build/dist/Collision/Detection/DynamicTreeCollisionProcessor.js.map +1 -1
  7. package/build/dist/Collision/Detection/Pair.d.ts +5 -0
  8. package/build/dist/Collision/Detection/Pair.js +19 -0
  9. package/build/dist/Collision/Detection/Pair.js.map +1 -1
  10. package/build/dist/Debug/Debug.d.ts +16 -0
  11. package/build/dist/Debug/Debug.js +35 -0
  12. package/build/dist/Debug/Debug.js.map +1 -1
  13. package/build/dist/Drawing/Animation.js +10 -1
  14. package/build/dist/Drawing/Animation.js.map +1 -1
  15. package/build/dist/Drawing/Polygon.js +11 -1
  16. package/build/dist/Drawing/Polygon.js.map +1 -1
  17. package/build/dist/Drawing/Sprite.js +24 -25
  18. package/build/dist/Drawing/Sprite.js.map +1 -1
  19. package/build/dist/Drawing/Texture.js +25 -36
  20. package/build/dist/Drawing/Texture.js.map +1 -1
  21. package/build/dist/Engine.d.ts +34 -1
  22. package/build/dist/Engine.js +114 -60
  23. package/build/dist/Engine.js.map +1 -1
  24. package/build/dist/EntityComponentSystem/SystemManager.d.ts +6 -0
  25. package/build/dist/EntityComponentSystem/SystemManager.js +12 -5
  26. package/build/dist/EntityComponentSystem/SystemManager.js.map +1 -1
  27. package/build/dist/Graphics/Animation.js +7 -1
  28. package/build/dist/Graphics/Animation.js.map +1 -1
  29. package/build/dist/Graphics/Canvas.js +5 -1
  30. package/build/dist/Graphics/Canvas.js.map +1 -1
  31. package/build/dist/Graphics/Circle.js +5 -1
  32. package/build/dist/Graphics/Circle.js.map +1 -1
  33. package/build/dist/Graphics/Context/ExcaliburGraphicsContextWebGL.js +4 -4
  34. package/build/dist/Graphics/Context/ExcaliburGraphicsContextWebGL.js.map +1 -1
  35. package/build/dist/Graphics/Font.js +14 -2
  36. package/build/dist/Graphics/Font.js.map +1 -1
  37. package/build/dist/Graphics/GraphicsComponent.js +6 -3
  38. package/build/dist/Graphics/GraphicsComponent.js.map +1 -1
  39. package/build/dist/Graphics/GraphicsGroup.js +4 -1
  40. package/build/dist/Graphics/GraphicsGroup.js.map +1 -1
  41. package/build/dist/Graphics/ImageSource.js +25 -36
  42. package/build/dist/Graphics/ImageSource.js.map +1 -1
  43. package/build/dist/Graphics/Polygon.js +5 -1
  44. package/build/dist/Graphics/Polygon.js.map +1 -1
  45. package/build/dist/Graphics/Rectangle.js +6 -1
  46. package/build/dist/Graphics/Rectangle.js.map +1 -1
  47. package/build/dist/Graphics/Sprite.js +6 -1
  48. package/build/dist/Graphics/Sprite.js.map +1 -1
  49. package/build/dist/Graphics/SpriteSheet.js +2 -2
  50. package/build/dist/Graphics/SpriteSheet.js.map +1 -1
  51. package/build/dist/Loader.d.ts +4 -1
  52. package/build/dist/Loader.js +47 -45
  53. package/build/dist/Loader.js.map +1 -1
  54. package/build/dist/Particles.js +1 -1
  55. package/build/dist/Particles.js.map +1 -1
  56. package/build/dist/Resources/Gif.js +8 -19
  57. package/build/dist/Resources/Gif.js.map +1 -1
  58. package/build/dist/Resources/Sound/Sound.js +42 -59
  59. package/build/dist/Resources/Sound/Sound.js.map +1 -1
  60. package/build/dist/Scene.js +1 -0
  61. package/build/dist/Scene.js.map +1 -1
  62. package/build/dist/Screen.js +3 -3
  63. package/build/dist/Screen.js.map +1 -1
  64. package/build/dist/ScreenElement.js +1 -1
  65. package/build/dist/ScreenElement.js.map +1 -1
  66. package/build/dist/Trigger.js +4 -1
  67. package/build/dist/Trigger.js.map +1 -1
  68. package/build/dist/Util/Clock.d.ts +117 -0
  69. package/build/dist/Util/Clock.js +209 -0
  70. package/build/dist/Util/Clock.js.map +1 -0
  71. package/build/dist/Util/Decorators.js +7 -2
  72. package/build/dist/Util/Decorators.js.map +1 -1
  73. package/build/dist/Util/Fps.d.ts +40 -0
  74. package/build/dist/Util/Fps.js +47 -0
  75. package/build/dist/Util/Fps.js.map +1 -0
  76. package/build/dist/Util/Util.d.ts +6 -1
  77. package/build/dist/Util/Util.js +9 -3
  78. package/build/dist/Util/Util.js.map +1 -1
  79. package/build/dist/Util/WebAudio.js.map +1 -1
  80. package/build/dist/excalibur.js +743 -346
  81. package/build/dist/excalibur.js.map +1 -1
  82. package/build/dist/excalibur.min.js +1 -1
  83. package/build/dist/excalibur.min.js.LICENSE.txt +1 -1
  84. package/build/dist/excalibur.min.js.map +1 -1
  85. package/build/dist/index.d.ts +3 -0
  86. package/build/dist/index.js +5 -2
  87. package/build/dist/index.js.map +1 -1
  88. package/build/esm/Collision/Detection/DynamicTreeCollisionProcessor.d.ts +2 -2
  89. package/build/esm/Collision/Detection/Pair.d.ts +5 -0
  90. package/build/esm/Debug/Debug.d.ts +16 -0
  91. package/build/esm/Engine.d.ts +34 -1
  92. package/build/esm/EntityComponentSystem/SystemManager.d.ts +6 -0
  93. package/build/esm/Loader.d.ts +4 -1
  94. package/build/esm/Util/Clock.d.ts +117 -0
  95. package/build/esm/Util/Fps.d.ts +40 -0
  96. package/build/esm/Util/Util.d.ts +6 -1
  97. package/build/esm/excalibur.js +749 -347
  98. package/build/esm/excalibur.js.map +1 -1
  99. package/build/esm/excalibur.min.js +1 -1
  100. package/build/esm/excalibur.min.js.LICENSE.txt +1 -1
  101. package/build/esm/excalibur.min.js.map +1 -1
  102. package/build/esm/index.d.ts +3 -0
  103. package/package.json +4 -4
  104. package/wallaby.js +1 -0
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * excalibur - 0.26.0-alpha.310+5f94c2f - 2021-11-27
2
+ * excalibur - 0.26.0-alpha.314+ff3f7ae - 2021-11-28
3
3
  * https://github.com/excaliburjs/Excalibur
4
4
  * Copyright (c) 2021 Excalibur.js <https://github.com/excaliburjs/Excalibur/graphs/contributors>
5
5
  * Licensed BSD-2-Clause
@@ -1918,6 +1918,7 @@ __webpack_require__.d(__webpack_exports__, {
1918
1918
  "Circle": () => (/* reexport */ Circle),
1919
1919
  "CircleCollider": () => (/* reexport */ CircleCollider),
1920
1920
  "Class": () => (/* reexport */ Class),
1921
+ "Clock": () => (/* reexport */ Clock),
1921
1922
  "ClosestLine": () => (/* reexport */ ClosestLine),
1922
1923
  "ClosestLineJumpTable": () => (/* reexport */ ClosestLineJumpTable),
1923
1924
  "Collider": () => (/* reexport */ Collider),
@@ -1984,6 +1985,7 @@ __webpack_require__.d(__webpack_exports__, {
1984
1985
  "Font": () => (/* reexport */ Font),
1985
1986
  "FontStyle": () => (/* reexport */ FontStyle),
1986
1987
  "FontUnit": () => (/* reexport */ FontUnit),
1988
+ "FpsSampler": () => (/* reexport */ FpsSampler),
1987
1989
  "FrameStats": () => (/* reexport */ FrameStats),
1988
1990
  "GameEvent": () => (/* reexport */ GameEvent),
1989
1991
  "GameStartEvent": () => (/* reexport */ GameStartEvent),
@@ -2084,6 +2086,7 @@ __webpack_require__.d(__webpack_exports__, {
2084
2086
  "Sprite": () => (/* reexport */ Sprite),
2085
2087
  "SpriteFont": () => (/* reexport */ SpriteFont_SpriteFont),
2086
2088
  "SpriteSheet": () => (/* reexport */ SpriteSheet),
2089
+ "StandardClock": () => (/* reexport */ StandardClock),
2087
2090
  "StrategyContainer": () => (/* reexport */ StrategyContainer),
2088
2091
  "Stream": () => (/* reexport */ Stream),
2089
2092
  "SubscribeEvent": () => (/* reexport */ SubscribeEvent),
@@ -2091,6 +2094,7 @@ __webpack_require__.d(__webpack_exports__, {
2091
2094
  "SystemManager": () => (/* reexport */ SystemManager),
2092
2095
  "SystemType": () => (/* reexport */ SystemType),
2093
2096
  "TagComponent": () => (/* reexport */ TagComponent),
2097
+ "TestClock": () => (/* reexport */ TestClock),
2094
2098
  "Text": () => (/* reexport */ Text),
2095
2099
  "TextAlign": () => (/* reexport */ TextAlign),
2096
2100
  "TileMap": () => (/* reexport */ TileMap),
@@ -2104,6 +2108,7 @@ __webpack_require__.d(__webpack_exports__, {
2104
2108
  "Vector": () => (/* reexport */ Vector),
2105
2109
  "VectorView": () => (/* reexport */ VectorView),
2106
2110
  "VisibleEvent": () => (/* reexport */ VisibleEvent),
2111
+ "WebAudio": () => (/* reexport */ WebAudio),
2107
2112
  "WebAudioInstance": () => (/* reexport */ WebAudioInstance),
2108
2113
  "World": () => (/* reexport */ World),
2109
2114
  "canonicalizeAngle": () => (/* reexport */ canonicalizeAngle),
@@ -2664,7 +2669,12 @@ const logMessage = (message, options) => {
2664
2669
  * method do the deprecated one. Inspired by https://github.com/jayphelps/core-decorators.js
2665
2670
  */
2666
2671
  function obsolete(options) {
2667
- options = Object.assign({ message: 'This feature will be removed in future versions of Excalibur.', alternateMethod: null, showStackTrace: false }, options);
2672
+ options = {
2673
+ message: 'This feature will be removed in future versions of Excalibur.',
2674
+ alternateMethod: null,
2675
+ showStackTrace: false,
2676
+ ...options
2677
+ };
2668
2678
  return function (target, property, descriptor) {
2669
2679
  if (descriptor &&
2670
2680
  !(typeof descriptor.value === 'function' || typeof descriptor.get === 'function' || typeof descriptor.set === 'function')) {
@@ -2677,7 +2687,7 @@ function obsolete(options) {
2677
2687
  obsoleteMessage[message] = 0;
2678
2688
  }
2679
2689
  // If descriptor is null it is a class
2680
- const method = descriptor ? Object.assign({}, descriptor) : target;
2690
+ const method = descriptor ? { ...descriptor } : target;
2681
2691
  if (!descriptor) {
2682
2692
  // with es2015 classes we need to change our decoration tactic
2683
2693
  class DecoratedClass extends method {
@@ -4491,11 +4501,17 @@ function fail(message) {
4491
4501
  const range = (from, to) => Array.from(new Array(to - from + 1), (_x, i) => i + from);
4492
4502
  /**
4493
4503
  * Create a promise that resolves after a certain number of milliseconds
4504
+ *
4505
+ * It is strongly recommended you pass the excalibur clock so delays are bound to the
4506
+ * excalibur clock which would be unaffected by stop/pause.
4494
4507
  * @param milliseconds
4508
+ * @param clock
4495
4509
  */
4496
- function delay(milliseconds) {
4497
- return new Promise((resolve) => {
4498
- setTimeout(() => {
4510
+ function delay(milliseconds, clock) {
4511
+ var _a;
4512
+ const schedule = (_a = clock === null || clock === void 0 ? void 0 : clock.schedule.bind(clock)) !== null && _a !== void 0 ? _a : setTimeout;
4513
+ return new Promise(resolve => {
4514
+ schedule(() => {
4499
4515
  resolve();
4500
4516
  }, milliseconds);
4501
4517
  });
@@ -6547,7 +6563,11 @@ class Canvas extends Raster {
6547
6563
  return this._ctx;
6548
6564
  }
6549
6565
  clone() {
6550
- return new Canvas(Object.assign(Object.assign(Object.assign({}, this._options), this.cloneGraphicOptions()), this.cloneRasterOptions()));
6566
+ return new Canvas({
6567
+ ...this._options,
6568
+ ...this.cloneGraphicOptions(),
6569
+ ...this.cloneRasterOptions()
6570
+ });
6551
6571
  }
6552
6572
  execute(ctx) {
6553
6573
  var _a, _b;
@@ -7496,15 +7516,6 @@ Fill = SpriteEffects_decorate([
7496
7516
 
7497
7517
 
7498
7518
  ;// CONCATENATED MODULE: ./Drawing/Texture.ts
7499
- var __awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {
7500
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
7501
- return new (P || (P = Promise))(function (resolve, reject) {
7502
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
7503
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7504
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7505
- step((generator = generator.apply(thisArg, _arguments || [])).next());
7506
- });
7507
- };
7508
7519
 
7509
7520
 
7510
7521
  /**
@@ -7541,35 +7552,33 @@ class Texture {
7541
7552
  /**
7542
7553
  * Begins loading the texture and returns a promise to be resolved on completion
7543
7554
  */
7544
- load() {
7545
- return __awaiter(this, void 0, void 0, function* () {
7546
- try {
7547
- // Load base64 or blob if needed
7548
- let url;
7549
- if (!this.path.includes('data:image/')) {
7550
- const blob = yield this._resource.load();
7551
- url = URL.createObjectURL(blob);
7552
- }
7553
- else {
7554
- url = this.path;
7555
- }
7556
- // Decode the image
7557
- const image = new Image();
7558
- image.src = url;
7559
- yield image.decode();
7560
- // Set results
7561
- this.data = image;
7562
- this.width = this._sprite.width = image.naturalWidth;
7563
- this.height = this._sprite.height = image.naturalHeight;
7564
- this._sprite = new Sprite_Sprite(this, 0, 0, this.width, this.height);
7565
- }
7566
- catch (_a) {
7567
- yield Promise.reject('Error loading texture');
7555
+ async load() {
7556
+ try {
7557
+ // Load base64 or blob if needed
7558
+ let url;
7559
+ if (!this.path.includes('data:image/')) {
7560
+ const blob = await this._resource.load();
7561
+ url = URL.createObjectURL(blob);
7568
7562
  }
7569
- // todo emit complete
7570
- this._loadedResolve(this.data);
7571
- return this.data;
7572
- });
7563
+ else {
7564
+ url = this.path;
7565
+ }
7566
+ // Decode the image
7567
+ const image = new Image();
7568
+ image.src = url;
7569
+ await image.decode();
7570
+ // Set results
7571
+ this.data = image;
7572
+ this.width = this._sprite.width = image.naturalWidth;
7573
+ this.height = this._sprite.height = image.naturalHeight;
7574
+ this._sprite = new Sprite_Sprite(this, 0, 0, this.width, this.height);
7575
+ }
7576
+ catch (_a) {
7577
+ await Promise.reject('Error loading texture');
7578
+ }
7579
+ // todo emit complete
7580
+ this._loadedResolve(this.data);
7581
+ return this.data;
7573
7582
  }
7574
7583
  asSprite() {
7575
7584
  return this._sprite;
@@ -7616,15 +7625,6 @@ var Sprite_decorate = (undefined && undefined.__decorate) || function (decorator
7616
7625
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
7617
7626
  return c > 3 && r && Object.defineProperty(target, key, r), r;
7618
7627
  };
7619
- var Sprite_awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {
7620
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
7621
- return new (P || (P = Promise))(function (resolve, reject) {
7622
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
7623
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7624
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7625
- step((generator = generator.apply(thisArg, _arguments || [])).next());
7626
- });
7627
- };
7628
7628
 
7629
7629
 
7630
7630
 
@@ -7704,21 +7704,19 @@ class SpriteImpl {
7704
7704
  get drawHeight() {
7705
7705
  return Math.abs(this.height * this.scale.y);
7706
7706
  }
7707
- _initPixelsFromTexture() {
7708
- return Sprite_awaiter(this, void 0, void 0, function* () {
7709
- try {
7710
- const image = yield this.texture.loaded;
7711
- this.width = this.width || image.naturalWidth;
7712
- this.height = this.height || image.naturalHeight;
7713
- this._spriteCanvas.width = this._spriteCanvas.width || image.naturalWidth;
7714
- this._spriteCanvas.height = this._spriteCanvas.height || image.naturalHeight;
7715
- this._loadPixels();
7716
- this._dirtyEffect = true;
7717
- }
7718
- catch (e) {
7719
- this.logger.error('Error loading texture ', this.texture.path, e);
7720
- }
7721
- });
7707
+ async _initPixelsFromTexture() {
7708
+ try {
7709
+ const image = await this.texture.loaded;
7710
+ this.width = this.width || image.naturalWidth;
7711
+ this.height = this.height || image.naturalHeight;
7712
+ this._spriteCanvas.width = this._spriteCanvas.width || image.naturalWidth;
7713
+ this._spriteCanvas.height = this._spriteCanvas.height || image.naturalHeight;
7714
+ this._loadPixels();
7715
+ this._dirtyEffect = true;
7716
+ }
7717
+ catch (e) {
7718
+ this.logger.error('Error loading texture ', this.texture.path, e);
7719
+ }
7722
7720
  }
7723
7721
  _loadPixels() {
7724
7722
  if (this.texture.isLoaded() && !this._pixelsLoaded) {
@@ -7890,7 +7888,17 @@ class SpriteImpl {
7890
7888
  }
7891
7889
  _drawWithOptions(options) {
7892
7890
  var _a, _b, _c, _d, _e, _f, _g, _h, _j;
7893
- const { ctx, x, y, rotation, drawWidth, drawHeight, anchor, offset, opacity, flipHorizontal, flipVertical } = Object.assign(Object.assign({}, options), { rotation: (_a = options.rotation) !== null && _a !== void 0 ? _a : this.rotation, drawWidth: (_b = options.drawWidth) !== null && _b !== void 0 ? _b : this.width, drawHeight: (_c = options.drawHeight) !== null && _c !== void 0 ? _c : this.height, flipHorizontal: (_d = options.flipHorizontal) !== null && _d !== void 0 ? _d : this.flipHorizontal, flipVertical: (_e = options.flipVertical) !== null && _e !== void 0 ? _e : this.flipVertical, anchor: (_f = options.anchor) !== null && _f !== void 0 ? _f : this.anchor, offset: (_g = options.offset) !== null && _g !== void 0 ? _g : this.offset, opacity: ((_h = options.opacity) !== null && _h !== void 0 ? _h : 1) * ((_j = this._opacity) !== null && _j !== void 0 ? _j : 1) });
7891
+ const { ctx, x, y, rotation, drawWidth, drawHeight, anchor, offset, opacity, flipHorizontal, flipVertical } = {
7892
+ ...options,
7893
+ rotation: (_a = options.rotation) !== null && _a !== void 0 ? _a : this.rotation,
7894
+ drawWidth: (_b = options.drawWidth) !== null && _b !== void 0 ? _b : this.width,
7895
+ drawHeight: (_c = options.drawHeight) !== null && _c !== void 0 ? _c : this.height,
7896
+ flipHorizontal: (_d = options.flipHorizontal) !== null && _d !== void 0 ? _d : this.flipHorizontal,
7897
+ flipVertical: (_e = options.flipVertical) !== null && _e !== void 0 ? _e : this.flipVertical,
7898
+ anchor: (_f = options.anchor) !== null && _f !== void 0 ? _f : this.anchor,
7899
+ offset: (_g = options.offset) !== null && _g !== void 0 ? _g : this.offset,
7900
+ opacity: ((_h = options.opacity) !== null && _h !== void 0 ? _h : 1) * ((_j = this._opacity) !== null && _j !== void 0 ? _j : 1)
7901
+ };
7894
7902
  if (this._dirtyEffect) {
7895
7903
  this._applyEffects();
7896
7904
  }
@@ -8058,20 +8066,16 @@ class Sprite extends Graphic {
8058
8066
  return new Sprite_Sprite(tex, sprite.sourceView.x, sprite.sourceView.y, sprite.sourceView.width, sprite.sourceView.height);
8059
8067
  }
8060
8068
  clone() {
8061
- return new Sprite(Object.assign({ image: this.image, sourceView: Object.assign({}, this.sourceView), destSize: Object.assign({}, this.destSize) }, this.cloneGraphicOptions()));
8069
+ return new Sprite({
8070
+ image: this.image,
8071
+ sourceView: { ...this.sourceView },
8072
+ destSize: { ...this.destSize },
8073
+ ...this.cloneGraphicOptions()
8074
+ });
8062
8075
  }
8063
8076
  }
8064
8077
 
8065
8078
  ;// CONCATENATED MODULE: ./Graphics/ImageSource.ts
8066
- var ImageSource_awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {
8067
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
8068
- return new (P || (P = Promise))(function (resolve, reject) {
8069
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
8070
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
8071
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8072
- step((generator = generator.apply(thisArg, _arguments || [])).next());
8073
- });
8074
- };
8075
8079
 
8076
8080
 
8077
8081
 
@@ -8120,35 +8124,33 @@ class ImageSource {
8120
8124
  /**
8121
8125
  * Begins loading the image and returns a promise that resolves when the image is loaded
8122
8126
  */
8123
- load() {
8124
- return ImageSource_awaiter(this, void 0, void 0, function* () {
8125
- if (this.isLoaded()) {
8126
- return this.data;
8127
- }
8128
- try {
8129
- // Load base64 or blob if needed
8130
- let url;
8131
- if (!this.path.includes('data:image/')) {
8132
- const blob = yield this._resource.load();
8133
- url = URL.createObjectURL(blob);
8134
- }
8135
- else {
8136
- url = this.path;
8137
- }
8138
- // Decode the image
8139
- const image = new Image();
8140
- image.src = url;
8141
- yield image.decode();
8142
- // Set results
8143
- this.data = image;
8144
- }
8145
- catch (error) {
8146
- throw `Error loading ImageSource from path '${this.path}' with error [${error.message}]`;
8147
- }
8148
- // todo emit complete
8149
- this._loadedResolve(this.data);
8127
+ async load() {
8128
+ if (this.isLoaded()) {
8150
8129
  return this.data;
8151
- });
8130
+ }
8131
+ try {
8132
+ // Load base64 or blob if needed
8133
+ let url;
8134
+ if (!this.path.includes('data:image/')) {
8135
+ const blob = await this._resource.load();
8136
+ url = URL.createObjectURL(blob);
8137
+ }
8138
+ else {
8139
+ url = this.path;
8140
+ }
8141
+ // Decode the image
8142
+ const image = new Image();
8143
+ image.src = url;
8144
+ await image.decode();
8145
+ // Set results
8146
+ this.data = image;
8147
+ }
8148
+ catch (error) {
8149
+ throw `Error loading ImageSource from path '${this.path}' with error [${error.message}]`;
8150
+ }
8151
+ // todo emit complete
8152
+ this._loadedResolve(this.data);
8153
+ return this.data;
8152
8154
  }
8153
8155
  /**
8154
8156
  * Build a sprite from this ImageSource
@@ -8421,7 +8423,16 @@ class AnimationImpl {
8421
8423
  }
8422
8424
  _drawWithOptions(options) {
8423
8425
  var _a, _b, _c, _d, _e, _f, _g, _h;
8424
- const animOptions = Object.assign(Object.assign({}, options), { rotation: (_a = options.rotation) !== null && _a !== void 0 ? _a : this.rotation, drawWidth: (_b = options.drawWidth) !== null && _b !== void 0 ? _b : this.drawWidth, drawHeight: (_c = options.drawHeight) !== null && _c !== void 0 ? _c : this.drawHeight, flipHorizontal: (_d = options.flipHorizontal) !== null && _d !== void 0 ? _d : this.flipHorizontal, flipVertical: (_e = options.flipVertical) !== null && _e !== void 0 ? _e : this.flipVertical, anchor: (_f = options.anchor) !== null && _f !== void 0 ? _f : this.anchor, opacity: ((_g = options.opacity) !== null && _g !== void 0 ? _g : 1) * ((_h = this._opacity) !== null && _h !== void 0 ? _h : 1) });
8426
+ const animOptions = {
8427
+ ...options,
8428
+ rotation: (_a = options.rotation) !== null && _a !== void 0 ? _a : this.rotation,
8429
+ drawWidth: (_b = options.drawWidth) !== null && _b !== void 0 ? _b : this.drawWidth,
8430
+ drawHeight: (_c = options.drawHeight) !== null && _c !== void 0 ? _c : this.drawHeight,
8431
+ flipHorizontal: (_d = options.flipHorizontal) !== null && _d !== void 0 ? _d : this.flipHorizontal,
8432
+ flipVertical: (_e = options.flipVertical) !== null && _e !== void 0 ? _e : this.flipVertical,
8433
+ anchor: (_f = options.anchor) !== null && _f !== void 0 ? _f : this.anchor,
8434
+ opacity: ((_g = options.opacity) !== null && _g !== void 0 ? _g : 1) * ((_h = this._opacity) !== null && _h !== void 0 ? _h : 1)
8435
+ };
8425
8436
  this._updateValues();
8426
8437
  let currSprite;
8427
8438
  if (this.currentFrame < this.sprites.length) {
@@ -9080,8 +9091,8 @@ class SpriteSheet {
9080
9091
  const sprites = [];
9081
9092
  options.spacing = (_a = options.spacing) !== null && _a !== void 0 ? _a : {};
9082
9093
  const { image, grid: { rows, columns: cols, spriteWidth, spriteHeight }, spacing: { originOffset, margin } } = options;
9083
- const offsetDefaults = Object.assign({ x: 0, y: 0 }, originOffset);
9084
- const marginDefaults = Object.assign({ x: 0, y: 0 }, margin);
9094
+ const offsetDefaults = { x: 0, y: 0, ...originOffset };
9095
+ const marginDefaults = { x: 0, y: 0, ...margin };
9085
9096
  for (let x = 0; x < cols; x++) {
9086
9097
  for (let y = 0; y < rows; y++) {
9087
9098
  sprites[x + y * cols] = new Sprite({
@@ -9297,10 +9308,10 @@ class ExcaliburGraphicsContextWebGLDebug {
9297
9308
  * @param height
9298
9309
  */
9299
9310
  drawRect(x, y, width, height, rectOptions = { color: Color.Black }) {
9300
- this.drawLine(vec(x, y), vec(x + width, y), Object.assign({}, rectOptions));
9301
- this.drawLine(vec(x + width, y), vec(x + width, y + height), Object.assign({}, rectOptions));
9302
- this.drawLine(vec(x + width, y + height), vec(x, y + height), Object.assign({}, rectOptions));
9303
- this.drawLine(vec(x, y + height), vec(x, y), Object.assign({}, rectOptions));
9311
+ this.drawLine(vec(x, y), vec(x + width, y), { ...rectOptions });
9312
+ this.drawLine(vec(x + width, y), vec(x + width, y + height), { ...rectOptions });
9313
+ this.drawLine(vec(x + width, y + height), vec(x, y + height), { ...rectOptions });
9314
+ this.drawLine(vec(x, y + height), vec(x, y), { ...rectOptions });
9304
9315
  }
9305
9316
  /**
9306
9317
  * Draw a debugging line to the context
@@ -9606,7 +9617,7 @@ class Screen {
9606
9617
  this._devicePixelRatio = this._calculateDevicePixelRatio();
9607
9618
  this._alreadyWarned = false;
9608
9619
  this.viewport = options.viewport;
9609
- this.resolution = (_a = options.resolution) !== null && _a !== void 0 ? _a : Object.assign({}, this.viewport);
9620
+ this.resolution = (_a = options.resolution) !== null && _a !== void 0 ? _a : { ...this.viewport };
9610
9621
  this._displayMode = (_b = options.displayMode) !== null && _b !== void 0 ? _b : DisplayMode.Fixed;
9611
9622
  this._canvas = options.canvas;
9612
9623
  this._ctx = options.context;
@@ -9702,8 +9713,8 @@ class Screen {
9702
9713
  pushResolutionAndViewport() {
9703
9714
  this._resolutionStack.push(this.resolution);
9704
9715
  this._viewportStack.push(this.viewport);
9705
- this.resolution = Object.assign({}, this.resolution);
9706
- this.viewport = Object.assign({}, this.viewport);
9716
+ this.resolution = { ...this.resolution };
9717
+ this.viewport = { ...this.viewport };
9707
9718
  }
9708
9719
  peekViewport() {
9709
9720
  return this._viewportStack[this._viewportStack.length - 1];
@@ -10371,15 +10382,6 @@ function circle(ctx, x, y, radius, stroke = Color.White, fill = null) {
10371
10382
  // EXTERNAL MODULE: ./Loader.css
10372
10383
  var Loader_0 = __webpack_require__(1388);
10373
10384
  ;// CONCATENATED MODULE: ./Loader.ts
10374
- var Loader_awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {
10375
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
10376
- return new (P || (P = Promise))(function (resolve, reject) {
10377
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
10378
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
10379
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
10380
- step((generator = generator.apply(thisArg, _arguments || [])).next());
10381
- });
10382
- };
10383
10385
 
10384
10386
 
10385
10387
 
@@ -10503,6 +10505,9 @@ class Loader extends Class {
10503
10505
  buttonElement.style.display = 'none';
10504
10506
  return buttonElement;
10505
10507
  };
10508
+ this._isLoadedPromise = new Promise(resolve => {
10509
+ this._isLoadedResolve = resolve;
10510
+ });
10506
10511
  if (loadables) {
10507
10512
  this.addResources(loadables);
10508
10513
  }
@@ -10578,17 +10583,18 @@ class Loader extends Class {
10578
10583
  /**
10579
10584
  * Shows the play button and returns a promise that resolves when clicked
10580
10585
  */
10581
- showPlayButton() {
10582
- var _a;
10586
+ async showPlayButton() {
10587
+ var _a, _b;
10583
10588
  if (this.suppressPlayButton) {
10584
10589
  this.hidePlayButton();
10585
- return Promise.resolve();
10590
+ // Delay is to give the logo a chance to show, otherwise don't delay
10591
+ await delay(500, (_a = this._engine) === null || _a === void 0 ? void 0 : _a.clock);
10586
10592
  }
10587
10593
  else {
10588
10594
  const resizeHandler = () => {
10589
10595
  this._positionPlayButton();
10590
10596
  };
10591
- if ((_a = this._engine) === null || _a === void 0 ? void 0 : _a.browser) {
10597
+ if ((_b = this._engine) === null || _b === void 0 ? void 0 : _b.browser) {
10592
10598
  this._engine.browser.window.on('resize', resizeHandler);
10593
10599
  }
10594
10600
  this._playButtonShown = true;
@@ -10598,7 +10604,8 @@ class Loader extends Class {
10598
10604
  this._playButton.click();
10599
10605
  }
10600
10606
  });
10601
- const promise = new Promise((resolve) => {
10607
+ this._positionPlayButton();
10608
+ const playButtonClicked = new Promise((resolve) => {
10602
10609
  const startButtonHandler = (e) => {
10603
10610
  var _a;
10604
10611
  // We want to stop propogation to keep bubbling to the engine pointer handlers
@@ -10614,7 +10621,7 @@ class Loader extends Class {
10614
10621
  this._playButton.addEventListener('touchend', startButtonHandler);
10615
10622
  this._playButton.addEventListener('pointerup', startButtonHandler);
10616
10623
  });
10617
- return promise;
10624
+ return await playButtonClicked;
10618
10625
  }
10619
10626
  }
10620
10627
  hidePlayButton() {
@@ -10637,25 +10644,29 @@ class Loader extends Class {
10637
10644
  update(_engine, _delta) {
10638
10645
  // override me
10639
10646
  }
10647
+ areResourcesLoaded() {
10648
+ return this._isLoadedPromise;
10649
+ }
10640
10650
  /**
10641
10651
  * Begin loading all of the supplied resources, returning a promise
10642
- * that resolves when loading of all is complete
10652
+ * that resolves when loading of all is complete AND the user has clicked the "Play button"
10643
10653
  */
10644
- load() {
10645
- return Loader_awaiter(this, void 0, void 0, function* () {
10646
- yield Promise.all(this._resourceList.map((r) => r.load().finally(() => {
10647
- // capture progress
10648
- this._numLoaded++;
10649
- })));
10650
- // short delay in showing the button for aesthetics
10651
- yield delay(200);
10652
- yield this.showPlayButton();
10653
- // Unlock browser AudioContext in after user gesture
10654
- // See: https://github.com/excaliburjs/Excalibur/issues/262
10655
- // See: https://github.com/excaliburjs/Excalibur/issues/1031
10656
- yield WebAudio.unlock();
10657
- return (this.data = this._resourceList);
10658
- });
10654
+ async load() {
10655
+ var _a, _b;
10656
+ await ((_a = this._image) === null || _a === void 0 ? void 0 : _a.decode()); // decode logo if it exists
10657
+ await Promise.all(this._resourceList.map((r) => r.load().finally(() => {
10658
+ // capture progress
10659
+ this._numLoaded++;
10660
+ })));
10661
+ this._isLoadedResolve();
10662
+ // short delay in showing the button for aesthetics
10663
+ await delay(200, (_b = this._engine) === null || _b === void 0 ? void 0 : _b.clock);
10664
+ await this.showPlayButton();
10665
+ // Unlock browser AudioContext in after user gesture
10666
+ // See: https://github.com/excaliburjs/Excalibur/issues/262
10667
+ // See: https://github.com/excaliburjs/Excalibur/issues/1031
10668
+ await WebAudio.unlock();
10669
+ return (this.data = this._resourceList);
10659
10670
  }
10660
10671
  markResourceComplete() {
10661
10672
  this._numLoaded++;
@@ -10667,20 +10678,22 @@ class Loader extends Class {
10667
10678
  return this._resourceCount > 0 ? clamp(this._numLoaded, 0, this._resourceCount) / this._resourceCount : 1;
10668
10679
  }
10669
10680
  _positionPlayButton() {
10670
- const screenHeight = this._engine.screen.viewport.height;
10671
- const screenWidth = this._engine.screen.viewport.width;
10672
- if (this._playButtonRootElement) {
10673
- const left = this._engine.canvas.offsetLeft;
10674
- const top = this._engine.canvas.offsetTop;
10675
- const buttonWidth = this._playButton.clientWidth;
10676
- const buttonHeight = this._playButton.clientHeight;
10677
- if (this.playButtonPosition) {
10678
- this._playButtonRootElement.style.left = `${this.playButtonPosition.x}px`;
10679
- this._playButtonRootElement.style.top = `${this.playButtonPosition.y}px`;
10680
- }
10681
- else {
10682
- this._playButtonRootElement.style.left = `${left + screenWidth / 2 - buttonWidth / 2}px`;
10683
- this._playButtonRootElement.style.top = `${top + screenHeight / 2 - buttonHeight / 2 + 100}px`;
10681
+ if (this._engine) {
10682
+ const screenHeight = this._engine.screen.viewport.height;
10683
+ const screenWidth = this._engine.screen.viewport.width;
10684
+ if (this._playButtonRootElement) {
10685
+ const left = this._engine.canvas.offsetLeft;
10686
+ const top = this._engine.canvas.offsetTop;
10687
+ const buttonWidth = this._playButton.clientWidth;
10688
+ const buttonHeight = this._playButton.clientHeight;
10689
+ if (this.playButtonPosition) {
10690
+ this._playButtonRootElement.style.left = `${this.playButtonPosition.x}px`;
10691
+ this._playButtonRootElement.style.top = `${this.playButtonPosition.y}px`;
10692
+ }
10693
+ else {
10694
+ this._playButtonRootElement.style.left = `${left + screenWidth / 2 - buttonWidth / 2}px`;
10695
+ this._playButtonRootElement.style.top = `${top + screenHeight / 2 - buttonHeight / 2 + 100}px`;
10696
+ }
10684
10697
  }
10685
10698
  }
10686
10699
  }
@@ -11665,10 +11678,29 @@ class Pair {
11665
11678
  this.id = null;
11666
11679
  this.id = Pair.calculatePairHash(colliderA.id, colliderB.id);
11667
11680
  }
11681
+ /**
11682
+ * Returns whether a it is allowed for 2 colliders in a Pair to collide
11683
+ * @param colliderA
11684
+ * @param colliderB
11685
+ */
11668
11686
  static canCollide(colliderA, colliderB) {
11669
11687
  var _a, _b;
11670
11688
  const bodyA = (_a = colliderA === null || colliderA === void 0 ? void 0 : colliderA.owner) === null || _a === void 0 ? void 0 : _a.get(BodyComponent);
11671
11689
  const bodyB = (_b = colliderB === null || colliderB === void 0 ? void 0 : colliderB.owner) === null || _b === void 0 ? void 0 : _b.get(BodyComponent);
11690
+ // Prevent self collision
11691
+ if (colliderA.id === colliderB.id) {
11692
+ return false;
11693
+ }
11694
+ // Colliders with the same owner do not collide (composite colliders)
11695
+ if (colliderA.owner &&
11696
+ colliderB.owner &&
11697
+ colliderA.owner.id === colliderB.owner.id) {
11698
+ return false;
11699
+ }
11700
+ // if the pair has a member with zero dimension don't collide
11701
+ if (colliderA.localBounds.hasZeroDimensions() || colliderB.localBounds.hasZeroDimensions()) {
11702
+ return false;
11703
+ }
11672
11704
  // Body's needed for collision in the current state
11673
11705
  // TODO can we collide without a body?
11674
11706
  if (!bodyA || !bodyB) {
@@ -12265,7 +12297,7 @@ class Ray {
12265
12297
  class DynamicTreeCollisionProcessor {
12266
12298
  constructor() {
12267
12299
  this._dynamicCollisionTree = new DynamicTree();
12268
- this._collisions = new Set();
12300
+ this._pairs = new Set();
12269
12301
  this._collisionPairCache = [];
12270
12302
  this._colliders = [];
12271
12303
  }
@@ -12319,27 +12351,10 @@ class DynamicTreeCollisionProcessor {
12319
12351
  this._dynamicCollisionTree.untrackCollider(target);
12320
12352
  }
12321
12353
  }
12322
- _shouldGenerateCollisionPair(colliderA, colliderB) {
12323
- // if the collision pair must be 2 separate colliders
12324
- // Also separate owners for composite colliders
12325
- if ((colliderA.id !== null &&
12326
- colliderB.id !== null &&
12327
- colliderA.id === colliderB.id) ||
12328
- (colliderA.owner !== null &&
12329
- colliderB.owner !== null &&
12330
- colliderA.owner === colliderB.owner)) {
12331
- return false;
12332
- }
12354
+ _pairExists(colliderA, colliderB) {
12333
12355
  // if the collision pair has been calculated already short circuit
12334
12356
  const hash = Pair.calculatePairHash(colliderA.id, colliderB.id);
12335
- if (this._collisions.has(hash)) {
12336
- return false; // pair exists easy exit return false
12337
- }
12338
- // if the pair has a member with zero dimension
12339
- if (colliderA.localBounds.hasZeroDimensions() || colliderB.localBounds.hasZeroDimensions()) {
12340
- return false;
12341
- }
12342
- return Pair.canCollide(colliderA, colliderB);
12357
+ return this._pairs.has(hash);
12343
12358
  }
12344
12359
  /**
12345
12360
  * Detects potential collision pairs in a broadphase approach with the dynamic aabb tree strategy
@@ -12354,16 +12369,16 @@ class DynamicTreeCollisionProcessor {
12354
12369
  });
12355
12370
  // clear old list of collision pairs
12356
12371
  this._collisionPairCache = [];
12357
- this._collisions.clear();
12372
+ this._pairs.clear();
12358
12373
  // check for normal collision pairs
12359
12374
  let collider;
12360
12375
  for (let j = 0, l = potentialColliders.length; j < l; j++) {
12361
12376
  collider = potentialColliders[j];
12362
12377
  // Query the collision tree for potential colliders
12363
12378
  this._dynamicCollisionTree.query(collider, (other) => {
12364
- if (this._shouldGenerateCollisionPair(collider, other)) {
12379
+ if (!this._pairExists(collider, other) && Pair.canCollide(collider, other)) {
12365
12380
  const pair = new Pair(collider, other);
12366
- this._collisions.add(pair.id);
12381
+ this._pairs.add(pair.id);
12367
12382
  this._collisionPairCache.push(pair);
12368
12383
  }
12369
12384
  // Always return false, to query whole tree. Returning true in the query method stops searching
@@ -12403,7 +12418,7 @@ class DynamicTreeCollisionProcessor {
12403
12418
  let minCollider;
12404
12419
  let minTranslate = new Vector(Infinity, Infinity);
12405
12420
  this._dynamicCollisionTree.rayCastQuery(ray, updateDistance + Physics.surfaceEpsilon * 2, (other) => {
12406
- if (collider !== other && Pair.canCollide(collider, other)) {
12421
+ if (!this._pairExists(collider, other) && Pair.canCollide(collider, other)) {
12407
12422
  const hitPoint = other.rayCast(ray, updateDistance + Physics.surfaceEpsilon * 10);
12408
12423
  if (hitPoint) {
12409
12424
  const translate = hitPoint.sub(origin);
@@ -12417,8 +12432,8 @@ class DynamicTreeCollisionProcessor {
12417
12432
  });
12418
12433
  if (minCollider && Vector.isValid(minTranslate)) {
12419
12434
  const pair = new Pair(collider, minCollider);
12420
- if (!this._collisions.has(pair.id)) {
12421
- this._collisions.add(pair.id);
12435
+ if (!this._pairs.has(pair.id)) {
12436
+ this._pairs.add(pair.id);
12422
12437
  this._collisionPairCache.push(pair);
12423
12438
  }
12424
12439
  // move the fast moving object to the other body
@@ -15730,7 +15745,7 @@ class GraphicsLayer {
15730
15745
  * @param options
15731
15746
  */
15732
15747
  show(nameOrGraphic, options) {
15733
- options = Object.assign({}, options);
15748
+ options = { ...options };
15734
15749
  let gfx;
15735
15750
  if (nameOrGraphic instanceof Graphic) {
15736
15751
  gfx = this._graphics.copyGraphics ? nameOrGraphic.clone() : nameOrGraphic;
@@ -15755,7 +15770,7 @@ class GraphicsLayer {
15755
15770
  * @param options
15756
15771
  */
15757
15772
  use(nameOrGraphic, options) {
15758
- options = Object.assign({}, options);
15773
+ options = { ...options };
15759
15774
  this.hide();
15760
15775
  return this.show(nameOrGraphic, options);
15761
15776
  }
@@ -15862,7 +15877,10 @@ class GraphicsComponent extends Component {
15862
15877
  this.copyGraphics = false;
15863
15878
  this._bounds = null;
15864
15879
  // Defaults
15865
- options = Object.assign({ visible: this.visible }, options);
15880
+ options = {
15881
+ visible: this.visible,
15882
+ ...options
15883
+ };
15866
15884
  const { current, anchor, opacity, visible, graphics, offset, copyGraphics, onPreDraw, onPostDraw } = options;
15867
15885
  this._graphics = graphics || {};
15868
15886
  this.offset = offset !== null && offset !== void 0 ? offset : this.offset;
@@ -15986,7 +16004,12 @@ class Rectangle extends Raster {
15986
16004
  this.rasterize();
15987
16005
  }
15988
16006
  clone() {
15989
- return new Rectangle(Object.assign(Object.assign({ width: this.width, height: this.height }, this.cloneGraphicOptions()), this.cloneRasterOptions()));
16007
+ return new Rectangle({
16008
+ width: this.width,
16009
+ height: this.height,
16010
+ ...this.cloneGraphicOptions(),
16011
+ ...this.cloneRasterOptions()
16012
+ });
15990
16013
  }
15991
16014
  execute(ctx) {
15992
16015
  if (this.color) {
@@ -16022,7 +16045,11 @@ class Circle extends Raster {
16022
16045
  this.flagDirty();
16023
16046
  }
16024
16047
  clone() {
16025
- return new Circle(Object.assign(Object.assign({ radius: this.radius }, this.cloneGraphicOptions()), this.cloneRasterOptions()));
16048
+ return new Circle({
16049
+ radius: this.radius,
16050
+ ...this.cloneGraphicOptions(),
16051
+ ...this.cloneRasterOptions()
16052
+ });
16026
16053
  }
16027
16054
  execute(ctx) {
16028
16055
  if (this.radius > 0) {
@@ -17706,7 +17733,9 @@ class Actor extends Entity {
17706
17733
  * @deprecated will be removed in v0.26.0
17707
17734
  */
17708
17735
  this.traits = [];
17709
- const { name, x, y, pos, scale, width, height, radius, collider, vel, acc, rotation, angularVelocity, z, color, visible, anchor, collisionType, collisionGroup } = Object.assign({}, config);
17736
+ const { name, x, y, pos, scale, width, height, radius, collider, vel, acc, rotation, angularVelocity, z, color, visible, anchor, collisionType, collisionGroup } = {
17737
+ ...config
17738
+ };
17710
17739
  this._setName(name);
17711
17740
  this.anchor = anchor !== null && anchor !== void 0 ? anchor : Actor.defaults.anchor.clone();
17712
17741
  this.addComponent(new TransformComponent());
@@ -18425,7 +18454,7 @@ Actor_decorate([
18425
18454
  */
18426
18455
  class ScreenElement_ScreenElement extends Actor {
18427
18456
  constructor(config) {
18428
- super(Object.assign({}, config));
18457
+ super({ ...config });
18429
18458
  this.get(TransformComponent).coordPlane = CoordPlane.Screen;
18430
18459
  this.anchor = vec(0, 0);
18431
18460
  this.body.collisionType = CollisionType.PreventCollision;
@@ -19809,7 +19838,10 @@ class Trigger_Trigger extends Actor {
19809
19838
  * Number of times to repeat before killing the trigger,
19810
19839
  */
19811
19840
  this.repeat = -1;
19812
- opts = Object.assign(Object.assign({}, triggerDefaults), opts);
19841
+ opts = {
19842
+ ...triggerDefaults,
19843
+ ...opts
19844
+ };
19813
19845
  this.filter = opts.filter || this.filter;
19814
19846
  this.repeat = opts.repeat || this.repeat;
19815
19847
  this.action = opts.action || this.action;
@@ -20319,12 +20351,11 @@ class SystemManager {
20319
20351
  }
20320
20352
  }
20321
20353
  /**
20322
- * Updates all systems
20323
- * @param type whether this is an update or draw system
20324
- * @param context context reference
20325
- * @param delta time in milliseconds
20354
+ * Initialize all systems in the manager
20355
+ *
20356
+ * Systems added after initialize() will be initialized on add
20326
20357
  */
20327
- updateSystems(type, context, delta) {
20358
+ initialize() {
20328
20359
  if (!this.initialized) {
20329
20360
  this.initialized = true;
20330
20361
  for (const s of this.systems) {
@@ -20333,6 +20364,14 @@ class SystemManager {
20333
20364
  }
20334
20365
  }
20335
20366
  }
20367
+ }
20368
+ /**
20369
+ * Updates all systems
20370
+ * @param type whether this is an update or draw system
20371
+ * @param context context reference
20372
+ * @param delta time in milliseconds
20373
+ */
20374
+ updateSystems(type, context, delta) {
20336
20375
  const systems = this.systems.filter((s) => s.systemType === type);
20337
20376
  for (const s of systems) {
20338
20377
  if (s.preupdate) {
@@ -21128,7 +21167,13 @@ class Animation extends Graphic {
21128
21167
  this.goToFrame(0);
21129
21168
  }
21130
21169
  clone() {
21131
- return new Animation(Object.assign({ frames: this.frames.map((f) => (Object.assign({}, f))), frameDuration: this.frameDuration, reverse: this._reversed, strategy: this.strategy }, this.cloneGraphicOptions()));
21170
+ return new Animation({
21171
+ frames: this.frames.map((f) => ({ ...f })),
21172
+ frameDuration: this.frameDuration,
21173
+ reverse: this._reversed,
21174
+ strategy: this.strategy,
21175
+ ...this.cloneGraphicOptions()
21176
+ });
21132
21177
  }
21133
21178
  /**
21134
21179
  * Create an Animation from a [[SpriteSheet]], a list of indices into the sprite sheet, a duration per frame
@@ -21374,7 +21419,10 @@ class GraphicsGroup extends Graphic {
21374
21419
  this._updateDimensions();
21375
21420
  }
21376
21421
  clone() {
21377
- return new GraphicsGroup(Object.assign({ members: [...this.members] }, this.cloneGraphicOptions()));
21422
+ return new GraphicsGroup({
21423
+ members: [...this.members],
21424
+ ...this.cloneGraphicOptions()
21425
+ });
21378
21426
  }
21379
21427
  _updateDimensions() {
21380
21428
  let bb = new BoundingBox();
@@ -21715,7 +21763,7 @@ class ParticleEmitter extends Actor {
21715
21763
  * Indicates whether particles should start with a random rotation
21716
21764
  */
21717
21765
  this.randomRotation = false;
21718
- const { x, y, pos, isEmitting, minVel, maxVel, acceleration, minAngle, maxAngle, emitRate, particleLife, opacity, fadeFlag, focus, focusAccel, startSize, endSize, minSize, maxSize, beginColor, endColor, particleSprite, emitterType, radius, particleRotationalVelocity, randomRotation, random } = Object.assign({}, config);
21766
+ const { x, y, pos, isEmitting, minVel, maxVel, acceleration, minAngle, maxAngle, emitRate, particleLife, opacity, fadeFlag, focus, focusAccel, startSize, endSize, minSize, maxSize, beginColor, endColor, particleSprite, emitterType, radius, particleRotationalVelocity, randomRotation, random } = { ...config };
21719
21767
  this.pos = pos !== null && pos !== void 0 ? pos : vec(x !== null && x !== void 0 ? x : 0, y !== null && y !== void 0 ? y : 0);
21720
21768
  this.isEmitting = isEmitting !== null && isEmitting !== void 0 ? isEmitting : this.isEmitting;
21721
21769
  this.minVel = minVel !== null && minVel !== void 0 ? minVel : this.minVel;
@@ -22785,6 +22833,7 @@ class Scene extends Class {
22785
22833
  this.engine = engine;
22786
22834
  // Initialize camera first
22787
22835
  this.camera._initialize(engine);
22836
+ this.world.systemManager.initialize();
22788
22837
  // This order is important! we want to be sure any custom init that add actors
22789
22838
  // fire before the actor init
22790
22839
  this.onInitialize.call(this, engine);
@@ -23374,6 +23423,41 @@ class Debug {
23374
23423
  this._engine = engine;
23375
23424
  this.colorBlindMode = new ColorBlindFlags(this._engine);
23376
23425
  }
23426
+ /**
23427
+ * Switch the current excalibur clock with the [[TestClock]] and return
23428
+ * it in the same running state.
23429
+ *
23430
+ * This is useful when you need to debug frame by frame.
23431
+ */
23432
+ useTestClock() {
23433
+ const clock = this._engine.clock;
23434
+ const wasRunning = clock.isRunning();
23435
+ clock.stop();
23436
+ const testClock = clock.toTestClock();
23437
+ if (wasRunning) {
23438
+ testClock.start();
23439
+ }
23440
+ this._engine.clock = testClock;
23441
+ return testClock;
23442
+ }
23443
+ /**
23444
+ * Switch the current excalibur clock with the [[StandardClock]] and
23445
+ * return it in the same runnign state.
23446
+ *
23447
+ * This is useful when you need to switch back to normal mode after
23448
+ * debugging.
23449
+ */
23450
+ useStandardClock() {
23451
+ const currentClock = this._engine.clock;
23452
+ const wasRunning = currentClock.isRunning();
23453
+ currentClock.stop();
23454
+ const standardClock = currentClock.toStandardClock();
23455
+ if (wasRunning) {
23456
+ standardClock.start();
23457
+ }
23458
+ this._engine.clock = standardClock;
23459
+ return standardClock;
23460
+ }
23377
23461
  }
23378
23462
  /**
23379
23463
  * Implementation of a frame's stats. Meant to have values copied via [[FrameStats.reset]], avoid
@@ -25048,6 +25132,264 @@ class PointerEventReceiver extends Class {
25048
25132
  }
25049
25133
  }
25050
25134
 
25135
+ ;// CONCATENATED MODULE: ./Util/Fps.ts
25136
+ class FpsSampler {
25137
+ constructor(options) {
25138
+ var _a;
25139
+ this._samplePeriod = 100;
25140
+ this._currentFrameTime = 0;
25141
+ this._frames = 0;
25142
+ this._previousSampleTime = 0;
25143
+ this._beginFrameTime = 0;
25144
+ this._fps = options.initialFps;
25145
+ this._samplePeriod = (_a = options.samplePeriod) !== null && _a !== void 0 ? _a : this._samplePeriod;
25146
+ this._currentFrameTime = 1000 / options.initialFps;
25147
+ this._nowFn = options.nowFn;
25148
+ this._previousSampleTime = this._nowFn();
25149
+ }
25150
+ /**
25151
+ * Start of code block to sample FPS for
25152
+ */
25153
+ start() {
25154
+ this._beginFrameTime = this._nowFn();
25155
+ }
25156
+ /**
25157
+ * End of code block to sample FPS for
25158
+ */
25159
+ end() {
25160
+ this._frames++;
25161
+ const time = this._nowFn();
25162
+ this._currentFrameTime = time - this._beginFrameTime;
25163
+ if (time >= this._previousSampleTime + this._samplePeriod) {
25164
+ this._fps = (this._frames * 1000) / (time - this._previousSampleTime);
25165
+ this._previousSampleTime = time;
25166
+ this._frames = 0;
25167
+ }
25168
+ }
25169
+ /**
25170
+ * Return the currenty sampled fps over the last sample period, by default every 100ms
25171
+ */
25172
+ get fps() {
25173
+ return this._fps;
25174
+ }
25175
+ /**
25176
+ * Return the instantanteous fps, this can be less useful because it will fluctuate given the current frames time
25177
+ */
25178
+ get instant() {
25179
+ return 1000 / this._currentFrameTime;
25180
+ }
25181
+ }
25182
+
25183
+ ;// CONCATENATED MODULE: ./Util/Clock.ts
25184
+
25185
+
25186
+ /**
25187
+ * Abstract Clock is the base type of all Clocks
25188
+ *
25189
+ * It has a few opinions
25190
+ * 1. It manages the calculation of what "elapsed" time means and thus maximum fps
25191
+ * 2. The default timing api is implemented in now()
25192
+ *
25193
+ * To implement your own clock, extend Clock and override start/stop to start and stop the clock, then call update() with whatever
25194
+ * method is unique to your clock implementation.
25195
+ */
25196
+ class Clock {
25197
+ constructor(options) {
25198
+ var _a, _b, _c;
25199
+ this._onFatalException = () => { };
25200
+ this._maxFps = Infinity;
25201
+ this._lastTime = 0;
25202
+ this._elapsed = 1;
25203
+ this._scheduledCbs = [];
25204
+ this._totalElapsed = 0;
25205
+ this._options = options;
25206
+ this.tick = options.tick;
25207
+ this._lastTime = (_a = this.now()) !== null && _a !== void 0 ? _a : 0;
25208
+ this._maxFps = (_b = options.maxFps) !== null && _b !== void 0 ? _b : this._maxFps;
25209
+ this._onFatalException = (_c = options.onFatalException) !== null && _c !== void 0 ? _c : this._onFatalException;
25210
+ this.fpsSampler = new FpsSampler({
25211
+ initialFps: 60,
25212
+ nowFn: () => this.now()
25213
+ });
25214
+ }
25215
+ /**
25216
+ * Get the elapsed time for the last completed frame
25217
+ */
25218
+ elapsed() {
25219
+ return this._elapsed;
25220
+ }
25221
+ /**
25222
+ * Get the current time in milliseconds
25223
+ */
25224
+ now() {
25225
+ return performance.now();
25226
+ }
25227
+ toTestClock() {
25228
+ const testClock = new TestClock({
25229
+ ...this._options,
25230
+ defaultUpdateMs: 16.6
25231
+ });
25232
+ return testClock;
25233
+ }
25234
+ toStandardClock() {
25235
+ const clock = new StandardClock({
25236
+ ...this._options
25237
+ });
25238
+ return clock;
25239
+ }
25240
+ /**
25241
+ * Schedule a callback to fire given a timeout in milliseconds using the excalibur [[Clock]]
25242
+ *
25243
+ * This is useful to use over the built in browser `setTimeout` because callbacks will be tied to the
25244
+ * excalibur update clock, instead of browser time, this means that callbacks wont fire if the game is
25245
+ * stopped or paused.
25246
+ *
25247
+ * @param cb callback to fire
25248
+ * @param timeoutMs Optionally specify a timeout in milliseconds from now, default is 0ms which means the next possible tick
25249
+ */
25250
+ schedule(cb, timeoutMs = 0) {
25251
+ const scheduledTime = this.now() + timeoutMs;
25252
+ this._scheduledCbs.push([cb, scheduledTime]);
25253
+ }
25254
+ _runScheduledCbs() {
25255
+ // walk backwards to delete items as we loop
25256
+ for (let i = this._scheduledCbs.length - 1; i > -1; i--) {
25257
+ if (this._scheduledCbs[i][1] <= this._totalElapsed) {
25258
+ this._scheduledCbs[i][0]();
25259
+ this._scheduledCbs.splice(i, 1);
25260
+ }
25261
+ }
25262
+ }
25263
+ update(overrideUpdateMs) {
25264
+ try {
25265
+ this.fpsSampler.start();
25266
+ // Get the time to calculate time-elapsed
25267
+ const now = this.now();
25268
+ let elapsed = now - this._lastTime || 1; // first frame
25269
+ // Constrain fps
25270
+ const fpsInterval = this._maxFps === Infinity ? 0 : 1000 / this._maxFps;
25271
+ if (elapsed <= fpsInterval) {
25272
+ return; // too fast 😎 skip this frame
25273
+ }
25274
+ // Resolves issue #138 if the game has been paused, or blurred for
25275
+ // more than a 200 milliseconds, reset elapsed time to 1. This improves reliability
25276
+ // and provides more expected behavior when the engine comes back
25277
+ // into focus
25278
+ if (elapsed > 200) {
25279
+ elapsed = 1;
25280
+ }
25281
+ // tick the mainloop and run scheduled callbacks
25282
+ this._elapsed = overrideUpdateMs || elapsed;
25283
+ this._totalElapsed += this._elapsed;
25284
+ this._runScheduledCbs();
25285
+ this.tick(overrideUpdateMs || elapsed);
25286
+ // if fps interval is not a multple
25287
+ if (fpsInterval > 0) {
25288
+ this._lastTime = now - (elapsed % fpsInterval);
25289
+ }
25290
+ else {
25291
+ this._lastTime = now;
25292
+ }
25293
+ this.fpsSampler.end();
25294
+ }
25295
+ catch (e) {
25296
+ this._onFatalException(e);
25297
+ this.stop();
25298
+ }
25299
+ }
25300
+ }
25301
+ /**
25302
+ * The [[StandardClock]] implements the requestAnimationFrame browser api to run the tick()
25303
+ */
25304
+ class StandardClock extends Clock {
25305
+ constructor(options) {
25306
+ super(options);
25307
+ this._running = false;
25308
+ }
25309
+ isRunning() {
25310
+ return this._running;
25311
+ }
25312
+ start() {
25313
+ this._running = true;
25314
+ const mainloop = () => {
25315
+ // stop the loop
25316
+ if (!this._running) {
25317
+ return;
25318
+ }
25319
+ try {
25320
+ // request next loop
25321
+ this._requestId = window.requestAnimationFrame(mainloop);
25322
+ this.update();
25323
+ }
25324
+ catch (e) {
25325
+ window.cancelAnimationFrame(this._requestId);
25326
+ throw e;
25327
+ }
25328
+ };
25329
+ // begin the first frame
25330
+ mainloop();
25331
+ }
25332
+ stop() {
25333
+ this._running = false;
25334
+ }
25335
+ }
25336
+ /**
25337
+ * The TestClock is meant for debugging interactions in excalibur that require precise timing to replicate or test
25338
+ */
25339
+ class TestClock extends Clock {
25340
+ constructor(options) {
25341
+ super({
25342
+ ...options
25343
+ });
25344
+ this._logger = Logger.getInstance();
25345
+ this._running = false;
25346
+ this._currentTime = 0;
25347
+ this._updateMs = options.defaultUpdateMs;
25348
+ }
25349
+ /**
25350
+ * Get the current time in milliseconds
25351
+ */
25352
+ now() {
25353
+ var _a;
25354
+ return (_a = this._currentTime) !== null && _a !== void 0 ? _a : 0;
25355
+ }
25356
+ isRunning() {
25357
+ return this._running;
25358
+ }
25359
+ start() {
25360
+ this._running = true;
25361
+ }
25362
+ stop() {
25363
+ this._running = false;
25364
+ }
25365
+ /**
25366
+ * Manually step the clock forward 1 tick, optionally specify an elapsed time in milliseconds
25367
+ * @param overrideUpdateMs
25368
+ */
25369
+ step(overrideUpdateMs) {
25370
+ const time = overrideUpdateMs !== null && overrideUpdateMs !== void 0 ? overrideUpdateMs : this._updateMs;
25371
+ if (this._running) {
25372
+ // to be comparable to RAF this needs to be a full blown Task
25373
+ // For example, images cannot decode syncronously in a single step
25374
+ this.update(time);
25375
+ this._currentTime += time;
25376
+ }
25377
+ else {
25378
+ this._logger.warn('The clock is not running, no step will be performed');
25379
+ }
25380
+ }
25381
+ /**
25382
+ * Run a number of steps that tick the clock, optionally specify an elapsed time in milliseconds
25383
+ * @param numberOfSteps
25384
+ * @param overrideUpdateMs
25385
+ */
25386
+ run(numberOfSteps, overrideUpdateMs) {
25387
+ for (let i = 0; i < numberOfSteps; i++) {
25388
+ this.step(overrideUpdateMs !== null && overrideUpdateMs !== void 0 ? overrideUpdateMs : this._updateMs);
25389
+ }
25390
+ }
25391
+ }
25392
+
25051
25393
  ;// CONCATENATED MODULE: ./Engine.ts
25052
25394
  var Engine_decorate = (undefined && undefined.__decorate) || function (decorators, target, key, desc) {
25053
25395
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
@@ -25074,6 +25416,8 @@ polyfill();
25074
25416
 
25075
25417
 
25076
25418
 
25419
+
25420
+
25077
25421
  /**
25078
25422
  * Enum representing the different mousewheel event bubble prevention
25079
25423
  */
@@ -25125,8 +25469,15 @@ class Engine extends Class {
25125
25469
  * ```
25126
25470
  */
25127
25471
  constructor(options) {
25128
- var _a, _b, _c;
25472
+ var _a, _b, _c, _d;
25129
25473
  super();
25474
+ /**
25475
+ * Optionally set the maximum fps if not set Excalibur will go as fast as the device allows.
25476
+ *
25477
+ * You may want to constrain max fps if your game cannot maintain fps consistenly, it can look and feel better to have a 30fps game than
25478
+ * one that bounces between 30fps and 60fps
25479
+ */
25480
+ this.maxFps = Number.POSITIVE_INFINITY;
25130
25481
  this._hasStarted = false;
25131
25482
  /**
25132
25483
  * Gets or sets the list of post processors to apply at the end of drawing a frame (such as [[ColorBlindCorrector]])
@@ -25162,11 +25513,14 @@ class Engine extends Class {
25162
25513
  Logger.getInstance().fatal(e);
25163
25514
  };
25164
25515
  this._timescale = 1.0;
25165
- this._isLoading = false;
25166
25516
  this._isInitialized = false;
25167
25517
  this._deferredGoTo = null;
25168
25518
  this._loadingComplete = false;
25169
- options = Object.assign(Object.assign({}, Engine._DEFAULT_ENGINE_OPTIONS), options);
25519
+ this._isReady = false;
25520
+ this._isReadyPromise = new Promise(resolve => {
25521
+ this._isReadyResolve = resolve;
25522
+ });
25523
+ options = { ...Engine._DEFAULT_ENGINE_OPTIONS, ...options };
25170
25524
  Flags.freeze();
25171
25525
  // Initialize browser events facade
25172
25526
  this.browser = new BrowserEvents(window, document);
@@ -25275,6 +25629,12 @@ O|===|* >________________>\n\
25275
25629
  if (options.backgroundColor) {
25276
25630
  this.backgroundColor = options.backgroundColor.clone();
25277
25631
  }
25632
+ this.maxFps = (_d = options.maxFps) !== null && _d !== void 0 ? _d : this.maxFps;
25633
+ this.clock = new StandardClock({
25634
+ maxFps: this.maxFps,
25635
+ tick: this._mainloop.bind(this),
25636
+ onFatalException: (e) => this.onFatalException(e)
25637
+ });
25278
25638
  this.enableCanvasTransparency = options.enableCanvasTransparency;
25279
25639
  this._loader = new Loader();
25280
25640
  this.debug = new Debug(this);
@@ -25628,7 +25988,7 @@ O|===|* >________________>\n\
25628
25988
  * @param delta Number of milliseconds elapsed since the last update.
25629
25989
  */
25630
25990
  _update(delta) {
25631
- if (this._isLoading) {
25991
+ if (!this.ready) {
25632
25992
  // suspend updates until loading is finished
25633
25993
  this._loader.update(this, delta);
25634
25994
  // Update input listeners
@@ -25636,7 +25996,6 @@ O|===|* >________________>\n\
25636
25996
  this.input.gamepads.update();
25637
25997
  return;
25638
25998
  }
25639
- this._overrideInitialize(this);
25640
25999
  // Publish preupdate events
25641
26000
  this._preupdate(delta);
25642
26001
  // process engine level events
@@ -25679,10 +26038,10 @@ O|===|* >________________>\n\
25679
26038
  _draw(delta) {
25680
26039
  const ctx = this.ctx;
25681
26040
  this._predraw(ctx, delta);
25682
- if (this._isLoading) {
26041
+ // Drawing nothing else while loading
26042
+ if (!this._isReady) {
25683
26043
  this._loader.canvas.draw(this.graphicsContext, 0, 0);
25684
26044
  this.graphicsContext.flush();
25685
- // Drawing nothing else while loading
25686
26045
  return;
25687
26046
  }
25688
26047
  // TODO move to graphics systems?
@@ -25751,64 +26110,105 @@ O|===|* >________________>\n\
25751
26110
  get loadingComplete() {
25752
26111
  return this._loadingComplete;
25753
26112
  }
26113
+ get ready() {
26114
+ return this._isReady;
26115
+ }
26116
+ isReady() {
26117
+ return this._isReadyPromise;
26118
+ }
25754
26119
  /**
25755
26120
  * Starts the internal game loop for Excalibur after loading
25756
26121
  * any provided assets.
25757
26122
  * @param loader Optional [[Loader]] to use to load resources. The default loader is [[Loader]], override to provide your own
25758
26123
  * custom loader.
26124
+ *
26125
+ * Note: start() only resolves AFTER the user has clicked the play button
25759
26126
  */
25760
- start(loader) {
26127
+ async start(loader) {
25761
26128
  if (!this._compatible) {
25762
- return Promise.reject('Excalibur is incompatible with your browser');
25763
- }
25764
- let loadingComplete;
25765
- // Push the current user entered resolution/viewport
25766
- this.screen.pushResolutionAndViewport();
25767
- // Configure resolution for loader
25768
- this.screen.resolution = this.screen.viewport;
25769
- this.screen.applyResolutionAndViewport();
25770
- this.graphicsContext.updateViewport();
26129
+ throw new Error('Excalibur is incompatible with your browser');
26130
+ }
26131
+ // Wire loader if we have it
25771
26132
  if (loader) {
26133
+ // Push the current user entered resolution/viewport
26134
+ this.screen.pushResolutionAndViewport();
26135
+ // Configure resolution for loader, it expects resolution === viewport
26136
+ this.screen.resolution = this.screen.viewport;
26137
+ this.screen.applyResolutionAndViewport();
26138
+ this.graphicsContext.updateViewport();
25772
26139
  this._loader = loader;
25773
26140
  this._loader.suppressPlayButton = this._suppressPlayButton || this._loader.suppressPlayButton;
25774
26141
  this._loader.wireEngine(this);
25775
- loadingComplete = this.load(this._loader);
25776
- }
25777
- else {
25778
- loadingComplete = Promise.resolve();
25779
26142
  }
25780
- loadingComplete.then(() => {
26143
+ // Start the excalibur clock which drives the mainloop
26144
+ // has started is a slight misnomer, it's really mainloop started
26145
+ this._logger.debug('Starting game clock...');
26146
+ this.browser.resume();
26147
+ this.clock.start();
26148
+ this._logger.debug('Game clock started');
26149
+ if (loader) {
26150
+ await this.load(this._loader);
26151
+ this._loadingComplete = true;
26152
+ // reset back to previous user resolution/viewport
25781
26153
  this.screen.popResolutionAndViewport();
25782
26154
  this.screen.applyResolutionAndViewport();
25783
26155
  this.graphicsContext.updateViewport();
25784
- this.emit('start', new GameStartEvent(this));
25785
- this._loadingComplete = true;
25786
- });
25787
- if (!this._hasStarted) {
25788
- // has started is a slight misnomer, it's really mainloop started
25789
- this._hasStarted = true;
25790
- this._logger.debug('Starting game...');
25791
- this.browser.resume();
25792
- Engine.createMainLoop(this, window.requestAnimationFrame, Date.now)();
25793
- this._logger.debug('Game started');
25794
- }
25795
- else {
25796
- // Game already started;
25797
26156
  }
25798
- return loadingComplete;
26157
+ this._loadingComplete = true;
26158
+ // Initialize before ready
26159
+ this._overrideInitialize(this);
26160
+ this._isReady = true;
26161
+ this._isReadyResolve();
26162
+ this.emit('start', new GameStartEvent(this));
26163
+ return this._isReadyPromise;
26164
+ }
26165
+ _mainloop(elapsed) {
26166
+ this.emit('preframe', new PreFrameEvent(this, this.stats.prevFrame));
26167
+ const delta = elapsed * this.timescale;
26168
+ // reset frame stats (reuse existing instances)
26169
+ const frameId = this.stats.prevFrame.id + 1;
26170
+ this.stats.currFrame.reset();
26171
+ this.stats.currFrame.id = frameId;
26172
+ this.stats.currFrame.delta = delta;
26173
+ this.stats.currFrame.fps = this.clock.fpsSampler.fps;
26174
+ const beforeUpdate = this.clock.now();
26175
+ this._update(delta);
26176
+ const afterUpdate = this.clock.now();
26177
+ this._draw(delta);
26178
+ const afterDraw = this.clock.now();
26179
+ this.stats.currFrame.duration.update = afterUpdate - beforeUpdate;
26180
+ this.stats.currFrame.duration.draw = afterDraw - afterUpdate;
26181
+ this.emit('postframe', new PostFrameEvent(this, this.stats.currFrame));
25799
26182
  }
26183
+ /**
26184
+ *
26185
+ * @param game
26186
+ * @param raf
26187
+ * @param nowFn
26188
+ * @deprecated Use [[Clock]] to run the mainloop, will be removed in v0.26.0
26189
+ */
25800
26190
  static createMainLoop(game, raf, nowFn) {
25801
26191
  let lastTime = nowFn();
26192
+ const fpsSampler = new FpsSampler({
26193
+ nowFn,
26194
+ initialFps: game.maxFps === Infinity ? 60 : game.maxFps
26195
+ });
25802
26196
  return function mainloop() {
25803
26197
  if (!game._hasStarted) {
25804
26198
  return;
25805
26199
  }
25806
26200
  try {
25807
26201
  game._requestId = raf(mainloop);
26202
+ fpsSampler.start();
25808
26203
  game.emit('preframe', new PreFrameEvent(game, game.stats.prevFrame));
25809
26204
  // Get the time to calculate time-elapsed
25810
26205
  const now = nowFn();
25811
- let elapsed = Math.floor(now - lastTime) || 1;
26206
+ let elapsed = now - lastTime || 1; // first frame
26207
+ // Constrain fps
26208
+ const fpsInterval = game.maxFps === Number.POSITIVE_INFINITY ? 0 : 1000 / game.maxFps;
26209
+ if (elapsed <= fpsInterval) {
26210
+ return; // too fast 😎 skip this frame
26211
+ }
25812
26212
  // Resolves issue #138 if the game has been paused, or blurred for
25813
26213
  // more than a 200 milliseconds, reset elapsed time to 1. This improves reliability
25814
26214
  // and provides more expected behavior when the engine comes back
@@ -25822,7 +26222,7 @@ O|===|* >________________>\n\
25822
26222
  game.stats.currFrame.reset();
25823
26223
  game.stats.currFrame.id = frameId;
25824
26224
  game.stats.currFrame.delta = delta;
25825
- game.stats.currFrame.fps = 1.0 / (delta / 1000);
26225
+ game.stats.currFrame.fps = fpsSampler.fps;
25826
26226
  const beforeUpdate = nowFn();
25827
26227
  game._update(delta);
25828
26228
  const afterUpdate = nowFn();
@@ -25830,8 +26230,15 @@ O|===|* >________________>\n\
25830
26230
  const afterDraw = nowFn();
25831
26231
  game.stats.currFrame.duration.update = afterUpdate - beforeUpdate;
25832
26232
  game.stats.currFrame.duration.draw = afterDraw - afterUpdate;
25833
- lastTime = now;
26233
+ // if fps interval is not a multple
26234
+ if (fpsInterval > 0) {
26235
+ lastTime = now - (elapsed % fpsInterval);
26236
+ }
26237
+ else {
26238
+ lastTime = now;
26239
+ }
25834
26240
  game.emit('postframe', new PostFrameEvent(game, game.stats.currFrame));
26241
+ fpsSampler.end();
25835
26242
  game.stats.prevFrame.reset(game.stats.currFrame);
25836
26243
  }
25837
26244
  catch (e) {
@@ -25845,10 +26252,10 @@ O|===|* >________________>\n\
25845
26252
  * Stops Excalibur's main loop, useful for pausing the game.
25846
26253
  */
25847
26254
  stop() {
25848
- if (this._hasStarted) {
26255
+ if (this.clock.isRunning()) {
25849
26256
  this.emit('stop', new GameStopEvent(this));
25850
26257
  this.browser.pause();
25851
- this._hasStarted = false;
26258
+ this.clock.stop();
25852
26259
  this._logger.debug('Game stopped');
25853
26260
  }
25854
26261
  }
@@ -25856,7 +26263,7 @@ O|===|* >________________>\n\
25856
26263
  * Returns the Engine's Running status, Useful for checking whether engine is running or paused.
25857
26264
  */
25858
26265
  isPaused() {
25859
- return !this._hasStarted;
26266
+ return !this.clock.isRunning();
25860
26267
  }
25861
26268
  /**
25862
26269
  * Takes a screen shot of the current viewport and returns it as an
@@ -25874,24 +26281,13 @@ O|===|* >________________>\n\
25874
26281
  * will appear.
25875
26282
  * @param loader Some [[Loadable]] such as a [[Loader]] collection, [[Sound]], or [[Texture]].
25876
26283
  */
25877
- load(loader) {
25878
- const complete = new Promise((resolve) => {
25879
- this._isLoading = true;
25880
- loader.load().then(() => {
25881
- if (this._suppressPlayButton) {
25882
- setTimeout(() => {
25883
- this._isLoading = false;
25884
- resolve();
25885
- // Delay is to give the logo a chance to show, otherwise don't delay
25886
- }, 500);
25887
- }
25888
- else {
25889
- this._isLoading = false;
25890
- resolve();
25891
- }
25892
- });
25893
- });
25894
- return complete;
26284
+ async load(loader) {
26285
+ try {
26286
+ await loader.load();
26287
+ }
26288
+ catch (_a) {
26289
+ await Promise.resolve();
26290
+ }
25895
26291
  }
25896
26292
  }
25897
26293
  /**
@@ -26070,13 +26466,25 @@ class Font extends Raster {
26070
26466
  this.flagDirty();
26071
26467
  }
26072
26468
  clone() {
26073
- return new Font(Object.assign(Object.assign(Object.assign({}, this.cloneGraphicOptions()), this.cloneRasterOptions()), { size: this.size, unit: this.unit, family: this.family, style: this.style, bold: this.bold, textAlign: this.textAlign, baseAlign: this.baseAlign, direction: this.direction, shadow: this.shadow
26469
+ return new Font({
26470
+ ...this.cloneGraphicOptions(),
26471
+ ...this.cloneRasterOptions(),
26472
+ size: this.size,
26473
+ unit: this.unit,
26474
+ family: this.family,
26475
+ style: this.style,
26476
+ bold: this.bold,
26477
+ textAlign: this.textAlign,
26478
+ baseAlign: this.baseAlign,
26479
+ direction: this.direction,
26480
+ shadow: this.shadow
26074
26481
  ? {
26075
26482
  blur: this.shadow.blur,
26076
26483
  offset: this.shadow.offset,
26077
26484
  color: this.shadow.color
26078
26485
  }
26079
- : null }));
26486
+ : null
26487
+ });
26080
26488
  }
26081
26489
  get fontString() {
26082
26490
  return `${this.style} ${this.bold ? 'bold' : ''} ${this.size}${this.unit} ${this.family}`;
@@ -26766,7 +27174,17 @@ let Polygon = class Polygon {
26766
27174
  }
26767
27175
  _drawWithOptions(options) {
26768
27176
  var _a, _b, _c, _d, _e, _f, _g, _h, _j;
26769
- const { ctx, x, y, rotation, drawWidth, drawHeight, anchor, offset, opacity, flipHorizontal, flipVertical } = Object.assign(Object.assign({}, options), { rotation: (_a = options.rotation) !== null && _a !== void 0 ? _a : this.rotation, drawWidth: (_b = options.drawWidth) !== null && _b !== void 0 ? _b : this.drawWidth, drawHeight: (_c = options.drawHeight) !== null && _c !== void 0 ? _c : this.drawHeight, flipHorizontal: (_d = options.flipHorizontal) !== null && _d !== void 0 ? _d : this.flipHorizontal, flipVertical: (_e = options.flipVertical) !== null && _e !== void 0 ? _e : this.flipVertical, anchor: (_f = options.anchor) !== null && _f !== void 0 ? _f : this.anchor, offset: (_g = options.offset) !== null && _g !== void 0 ? _g : this.offset, opacity: ((_h = options.opacity) !== null && _h !== void 0 ? _h : 1) * ((_j = this.opacity) !== null && _j !== void 0 ? _j : 1) });
27177
+ const { ctx, x, y, rotation, drawWidth, drawHeight, anchor, offset, opacity, flipHorizontal, flipVertical } = {
27178
+ ...options,
27179
+ rotation: (_a = options.rotation) !== null && _a !== void 0 ? _a : this.rotation,
27180
+ drawWidth: (_b = options.drawWidth) !== null && _b !== void 0 ? _b : this.drawWidth,
27181
+ drawHeight: (_c = options.drawHeight) !== null && _c !== void 0 ? _c : this.drawHeight,
27182
+ flipHorizontal: (_d = options.flipHorizontal) !== null && _d !== void 0 ? _d : this.flipHorizontal,
27183
+ flipVertical: (_e = options.flipVertical) !== null && _e !== void 0 ? _e : this.flipVertical,
27184
+ anchor: (_f = options.anchor) !== null && _f !== void 0 ? _f : this.anchor,
27185
+ offset: (_g = options.offset) !== null && _g !== void 0 ? _g : this.offset,
27186
+ opacity: ((_h = options.opacity) !== null && _h !== void 0 ? _h : 1) * ((_j = this.opacity) !== null && _j !== void 0 ? _j : 1)
27187
+ };
26770
27188
  const xpoint = drawWidth * anchor.x + offset.x + x;
26771
27189
  const ypoint = drawHeight * anchor.y + offset.y + y;
26772
27190
  ctx.save();
@@ -27088,15 +27506,6 @@ function canPlayFile(file) {
27088
27506
  }
27089
27507
 
27090
27508
  ;// CONCATENATED MODULE: ./Resources/Sound/Sound.ts
27091
- var Sound_awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {
27092
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
27093
- return new (P || (P = Promise))(function (resolve, reject) {
27094
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
27095
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
27096
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
27097
- step((generator = generator.apply(thisArg, _arguments || [])).next());
27098
- });
27099
- };
27100
27509
 
27101
27510
 
27102
27511
 
@@ -27187,30 +27596,26 @@ class Sound extends Class {
27187
27596
  isLoaded() {
27188
27597
  return !!this.data;
27189
27598
  }
27190
- load() {
27191
- return Sound_awaiter(this, void 0, void 0, function* () {
27192
- if (this.data) {
27193
- return this.data;
27194
- }
27195
- const arraybuffer = yield this._resource.load();
27196
- const audiobuffer = yield this.decodeAudio(arraybuffer.slice(0));
27197
- this._duration = typeof audiobuffer === 'object' ? audiobuffer.duration : undefined;
27198
- this.emit('processed', new NativeSoundProcessedEvent(this, audiobuffer));
27199
- return this.data = audiobuffer;
27200
- });
27599
+ async load() {
27600
+ if (this.data) {
27601
+ return this.data;
27602
+ }
27603
+ const arraybuffer = await this._resource.load();
27604
+ const audiobuffer = await this.decodeAudio(arraybuffer.slice(0));
27605
+ this._duration = typeof audiobuffer === 'object' ? audiobuffer.duration : undefined;
27606
+ this.emit('processed', new NativeSoundProcessedEvent(this, audiobuffer));
27607
+ return this.data = audiobuffer;
27201
27608
  }
27202
- decodeAudio(data) {
27203
- return Sound_awaiter(this, void 0, void 0, function* () {
27204
- try {
27205
- return yield this._audioContext.decodeAudioData(data.slice(0));
27206
- }
27207
- catch (e) {
27208
- this.logger.error('Unable to decode ' +
27209
- ' this browser may not fully support this format, or the file may be corrupt, ' +
27210
- 'if this is an mp3 try removing id3 tags and album art from the file.');
27211
- return yield Promise.reject();
27212
- }
27213
- });
27609
+ async decodeAudio(data) {
27610
+ try {
27611
+ return await this._audioContext.decodeAudioData(data.slice(0));
27612
+ }
27613
+ catch (e) {
27614
+ this.logger.error('Unable to decode ' +
27615
+ ' this browser may not fully support this format, or the file may be corrupt, ' +
27616
+ 'if this is an mp3 try removing id3 tags and album art from the file.');
27617
+ return await Promise.reject();
27618
+ }
27214
27619
  }
27215
27620
  wireEngine(engine) {
27216
27621
  if (engine) {
@@ -27302,38 +27707,34 @@ class Sound extends Class {
27302
27707
  getTrackId(track) {
27303
27708
  return this._tracks.indexOf(track);
27304
27709
  }
27305
- _resumePlayback() {
27306
- return Sound_awaiter(this, void 0, void 0, function* () {
27307
- if (this._isPaused) {
27308
- const resumed = [];
27309
- // ensure we resume *current* tracks (if paused)
27310
- for (const track of this._tracks) {
27311
- resumed.push(track.play());
27312
- }
27313
- this._isPaused = false;
27314
- this.emit('resume', new NativeSoundEvent(this));
27315
- this.logger.debug('Resuming paused instances for sound', this.path, this._tracks);
27316
- // resolve when resumed tracks are done
27317
- yield Promise.all(resumed);
27710
+ async _resumePlayback() {
27711
+ if (this._isPaused) {
27712
+ const resumed = [];
27713
+ // ensure we resume *current* tracks (if paused)
27714
+ for (const track of this._tracks) {
27715
+ resumed.push(track.play());
27318
27716
  }
27319
- return true;
27320
- });
27717
+ this._isPaused = false;
27718
+ this.emit('resume', new NativeSoundEvent(this));
27719
+ this.logger.debug('Resuming paused instances for sound', this.path, this._tracks);
27720
+ // resolve when resumed tracks are done
27721
+ await Promise.all(resumed);
27722
+ }
27723
+ return true;
27321
27724
  }
27322
27725
  /**
27323
27726
  * Starts playback, returns a promise that resolves when playback is complete
27324
27727
  */
27325
- _startPlayback() {
27326
- return Sound_awaiter(this, void 0, void 0, function* () {
27327
- const track = yield this._getTrackInstance(this.data);
27328
- const complete = yield track.play(() => {
27329
- this.emit('playbackstart', new NativeSoundEvent(this, track));
27330
- this.logger.debug('Playing new instance for sound', this.path);
27331
- });
27332
- // when done, remove track
27333
- this.emit('playbackend', new NativeSoundEvent(this, track));
27334
- this._tracks.splice(this.getTrackId(track), 1);
27335
- return complete;
27728
+ async _startPlayback() {
27729
+ const track = await this._getTrackInstance(this.data);
27730
+ const complete = await track.play(() => {
27731
+ this.emit('playbackstart', new NativeSoundEvent(this, track));
27732
+ this.logger.debug('Playing new instance for sound', this.path);
27336
27733
  });
27734
+ // when done, remove track
27735
+ this.emit('playbackend', new NativeSoundEvent(this, track));
27736
+ this._tracks.splice(this.getTrackId(track), 1);
27737
+ return complete;
27337
27738
  }
27338
27739
  _getTrackInstance(data) {
27339
27740
  const newTrack = new WebAudioInstance(data);
@@ -27351,15 +27752,6 @@ class Sound extends Class {
27351
27752
 
27352
27753
 
27353
27754
  ;// CONCATENATED MODULE: ./Resources/Gif.ts
27354
- var Gif_awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {
27355
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
27356
- return new (P || (P = Promise))(function (resolve, reject) {
27357
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
27358
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
27359
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
27360
- step((generator = generator.apply(thisArg, _arguments || [])).next());
27361
- });
27362
- };
27363
27755
 
27364
27756
 
27365
27757
 
@@ -27393,16 +27785,14 @@ class Gif {
27393
27785
  /**
27394
27786
  * Begins loading the texture and returns a promise to be resolved on completion
27395
27787
  */
27396
- load() {
27397
- return Gif_awaiter(this, void 0, void 0, function* () {
27398
- const arraybuffer = yield this._resource.load();
27399
- this._stream = new Stream(arraybuffer);
27400
- this._gif = new ParseGif(this._stream, this._transparentColor);
27401
- const images = this._gif.images.map(i => new ImageSource(i.src, false));
27402
- // Load all textures
27403
- yield Promise.all(images.map(t => t.load()));
27404
- return this.data = this._textures = images;
27405
- });
27788
+ async load() {
27789
+ const arraybuffer = await this._resource.load();
27790
+ this._stream = new Stream(arraybuffer);
27791
+ this._gif = new ParseGif(this._stream, this._transparentColor);
27792
+ const images = this._gif.images.map(i => new ImageSource(i.src, false));
27793
+ // Load all textures
27794
+ await Promise.all(images.map(t => t.load()));
27795
+ return this.data = this._textures = images;
27406
27796
  }
27407
27797
  isLoaded() {
27408
27798
  return !!this.data;
@@ -27886,7 +28276,11 @@ class Polygon_Polygon extends Raster {
27886
28276
  return vec(minX, minY);
27887
28277
  }
27888
28278
  clone() {
27889
- return new Polygon_Polygon(Object.assign(Object.assign({ points: this.points.map((p) => p.clone()) }, this.cloneGraphicOptions()), this.cloneRasterOptions()));
28279
+ return new Polygon_Polygon({
28280
+ points: this.points.map((p) => p.clone()),
28281
+ ...this.cloneGraphicOptions(),
28282
+ ...this.cloneRasterOptions()
28283
+ });
27890
28284
  }
27891
28285
  execute(ctx) {
27892
28286
  if (this.points && this.points.length) {
@@ -28446,7 +28840,7 @@ Promises_Promise = Promise_1 = Promises_decorate([
28446
28840
  * The current Excalibur version string
28447
28841
  * @description `process.env.__EX_VERSION` gets replaced by Webpack on build
28448
28842
  */
28449
- const EX_VERSION = "0.26.0-alpha.310+5f94c2f";
28843
+ const EX_VERSION = "0.26.0-alpha.314+ff3f7ae";
28450
28844
 
28451
28845
  polyfill();
28452
28846
  // This file is used as the bundle entry point and exports everything
@@ -28508,6 +28902,9 @@ polyfill();
28508
28902
 
28509
28903
 
28510
28904
 
28905
+
28906
+
28907
+
28511
28908
  // ex.Deprecated
28512
28909
 
28513
28910
  // import * as deprecated from './Deprecated';