melonjs 18.1.0 → 18.2.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 (94) hide show
  1. package/build/application/application.d.ts +29 -37
  2. package/build/application/application.d.ts.map +1 -1
  3. package/build/application/header.d.ts +3 -7
  4. package/build/application/header.d.ts.map +1 -1
  5. package/build/application/resize.d.ts +3 -4
  6. package/build/application/resize.d.ts.map +1 -1
  7. package/build/audio/audio.d.ts +145 -125
  8. package/build/audio/audio.d.ts.map +1 -1
  9. package/build/camera/camera2d.d.ts +192 -102
  10. package/build/camera/camera2d.d.ts.map +1 -1
  11. package/build/geometries/roundrect.d.ts +82 -17
  12. package/build/geometries/roundrect.d.ts.map +1 -1
  13. package/build/index.d.ts +12 -12
  14. package/build/index.js +2266 -840
  15. package/build/index.js.map +4 -4
  16. package/build/input/gamepad.d.ts +110 -61
  17. package/build/input/gamepad.d.ts.map +1 -1
  18. package/build/input/input.d.ts +3 -7
  19. package/build/input/input.d.ts.map +1 -1
  20. package/build/input/pointer.d.ts +25 -51
  21. package/build/input/pointer.d.ts.map +1 -1
  22. package/build/input/pointerevent.d.ts +51 -79
  23. package/build/input/pointerevent.d.ts.map +1 -1
  24. package/build/level/tiled/TMXGroup.d.ts +5 -0
  25. package/build/level/tiled/TMXGroup.d.ts.map +1 -1
  26. package/build/level/tiled/TMXLayer.d.ts.map +1 -1
  27. package/build/level/tiled/TMXObject.d.ts +21 -16
  28. package/build/level/tiled/TMXObject.d.ts.map +1 -1
  29. package/build/level/tiled/TMXTile.d.ts +16 -5
  30. package/build/level/tiled/TMXTile.d.ts.map +1 -1
  31. package/build/level/tiled/TMXTileMap.d.ts +15 -3
  32. package/build/level/tiled/TMXTileMap.d.ts.map +1 -1
  33. package/build/level/tiled/TMXTileset.d.ts +136 -15
  34. package/build/level/tiled/TMXTileset.d.ts.map +1 -1
  35. package/build/level/tiled/TMXUtils.d.ts +31 -0
  36. package/build/level/tiled/TMXUtils.d.ts.map +1 -1
  37. package/build/level/tiled/constants.d.ts +1 -1
  38. package/build/level/tiled/constants.d.ts.map +1 -1
  39. package/build/level/tiled/renderer/TMXObliqueRenderer.d.ts +42 -0
  40. package/build/level/tiled/renderer/TMXObliqueRenderer.d.ts.map +1 -0
  41. package/build/level/tiled/renderer/autodetect.d.ts +2 -1
  42. package/build/level/tiled/renderer/autodetect.d.ts.map +1 -1
  43. package/build/particles/emitter.d.ts +38 -121
  44. package/build/particles/emitter.d.ts.map +1 -1
  45. package/build/particles/particle.d.ts +1 -4
  46. package/build/particles/particle.d.ts.map +1 -1
  47. package/build/physics/bounds.d.ts +1 -1
  48. package/build/physics/bounds.d.ts.map +1 -1
  49. package/build/physics/detector.d.ts.map +1 -1
  50. package/build/physics/world.d.ts +2 -2
  51. package/build/plugin/plugin.d.ts +36 -53
  52. package/build/plugin/plugin.d.ts.map +1 -1
  53. package/build/polyfill/ellipse.d.ts +2 -0
  54. package/build/polyfill/ellipse.d.ts.map +1 -0
  55. package/build/polyfill/index.d.ts +1 -0
  56. package/build/polyfill/index.d.ts.map +1 -1
  57. package/build/renderable/colorlayer.d.ts +2 -2
  58. package/build/renderable/container.d.ts +6 -6
  59. package/build/renderable/container.d.ts.map +1 -1
  60. package/build/renderable/imagelayer.d.ts.map +1 -1
  61. package/build/renderable/renderable.d.ts +11 -2
  62. package/build/renderable/renderable.d.ts.map +1 -1
  63. package/build/renderable/ui/uibaseelement.d.ts +46 -33
  64. package/build/renderable/ui/uibaseelement.d.ts.map +1 -1
  65. package/build/renderable/ui/uispriteelement.d.ts +53 -43
  66. package/build/renderable/ui/uispriteelement.d.ts.map +1 -1
  67. package/build/renderable/ui/uitextbutton.d.ts +68 -74
  68. package/build/renderable/ui/uitextbutton.d.ts.map +1 -1
  69. package/build/state/stage.d.ts +1 -1
  70. package/build/state/state.d.ts +93 -90
  71. package/build/state/state.d.ts.map +1 -1
  72. package/build/system/event.d.ts +2 -2
  73. package/build/system/event.d.ts.map +1 -1
  74. package/build/utils/decode.d.ts +1 -0
  75. package/build/utils/decode.d.ts.map +1 -1
  76. package/build/utils/function.d.ts +1 -1
  77. package/build/utils/function.d.ts.map +1 -1
  78. package/build/utils/utils.d.ts +1 -1
  79. package/build/utils/utils.d.ts.map +1 -1
  80. package/build/video/canvas/canvas_renderer.d.ts +40 -13
  81. package/build/video/canvas/canvas_renderer.d.ts.map +1 -1
  82. package/build/video/renderer.d.ts +12 -0
  83. package/build/video/renderer.d.ts.map +1 -1
  84. package/build/video/webgl/batchers/batcher.d.ts +43 -8
  85. package/build/video/webgl/batchers/batcher.d.ts.map +1 -1
  86. package/build/video/webgl/batchers/quad_batcher.d.ts +0 -2
  87. package/build/video/webgl/batchers/quad_batcher.d.ts.map +1 -1
  88. package/build/video/webgl/buffer/index.d.ts +33 -8
  89. package/build/video/webgl/buffer/index.d.ts.map +1 -1
  90. package/build/video/webgl/buffer/vertex.d.ts +9 -1
  91. package/build/video/webgl/buffer/vertex.d.ts.map +1 -1
  92. package/build/video/webgl/webgl_renderer.d.ts +22 -20
  93. package/build/video/webgl/webgl_renderer.d.ts.map +1 -1
  94. package/package.json +1 -1
package/build/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * melonJS Game Engine - 18.1.0
2
+ * melonJS Game Engine - 18.2.0
3
3
  * http://www.melonjs.org
4
4
  * melonjs is licensed under the MIT License.
5
5
  * http://www.opensource.org/licenses/mit-license
@@ -153,10 +153,10 @@ var require_classof_raw = __commonJS({
153
153
  "../../node_modules/.pnpm/core-js@3.49.0/node_modules/core-js/internals/classof-raw.js"(exports, module) {
154
154
  "use strict";
155
155
  var uncurryThis = require_function_uncurry_this();
156
- var toString2 = uncurryThis({}.toString);
156
+ var toString = uncurryThis({}.toString);
157
157
  var stringSlice = uncurryThis("".slice);
158
158
  module.exports = function(it) {
159
- return stringSlice(toString2(it), 8, -1);
159
+ return stringSlice(toString(it), 8, -1);
160
160
  };
161
161
  }
162
162
  });
@@ -489,9 +489,9 @@ var require_uid = __commonJS({
489
489
  var uncurryThis = require_function_uncurry_this();
490
490
  var id = 0;
491
491
  var postfix = Math.random();
492
- var toString2 = uncurryThis(1.1.toString);
492
+ var toString = uncurryThis(1.1.toString);
493
493
  module.exports = function(key) {
494
- return "Symbol(" + (key === void 0 ? "" : key) + ")_" + toString2(++id + postfix, 36);
494
+ return "Symbol(" + (key === void 0 ? "" : key) + ")_" + toString(++id + postfix, 36);
495
495
  };
496
496
  }
497
497
  });
@@ -900,7 +900,7 @@ var require_make_built_in = __commonJS({
900
900
  }
901
901
  return value;
902
902
  };
903
- Function.prototype.toString = makeBuiltIn(function toString2() {
903
+ Function.prototype.toString = makeBuiltIn(function toString() {
904
904
  return isCallable(this) && getInternalState(this).source || inspectSource(this);
905
905
  }, "toString");
906
906
  }
@@ -1299,14 +1299,14 @@ var require_string_trim = __commonJS({
1299
1299
  "use strict";
1300
1300
  var uncurryThis = require_function_uncurry_this();
1301
1301
  var requireObjectCoercible = require_require_object_coercible();
1302
- var toString2 = require_to_string();
1302
+ var toString = require_to_string();
1303
1303
  var whitespaces = require_whitespaces();
1304
1304
  var replace = uncurryThis("".replace);
1305
1305
  var ltrim = RegExp("^[" + whitespaces + "]+");
1306
1306
  var rtrim = RegExp("(^|[^" + whitespaces + "])[" + whitespaces + "]+$");
1307
1307
  var createMethod = function(TYPE) {
1308
1308
  return function($this) {
1309
- var string = toString2(requireObjectCoercible($this));
1309
+ var string = toString(requireObjectCoercible($this));
1310
1310
  if (TYPE & 1) string = replace(string, ltrim, "");
1311
1311
  if (TYPE & 2) string = replace(string, rtrim, "$1");
1312
1312
  return string;
@@ -3681,6 +3681,62 @@ if (typeof globalThis !== "undefined") {
3681
3681
  }
3682
3682
  }
3683
3683
 
3684
+ // src/polyfill/ellipse.ts
3685
+ function ellipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle, counterclockwise = false) {
3686
+ if (radiusX < 0 || radiusY < 0) {
3687
+ throw new RangeError("Radius values must be non-negative.");
3688
+ }
3689
+ if (!Number.isFinite(x) || !Number.isFinite(y) || !Number.isFinite(radiusX) || !Number.isFinite(radiusY) || !Number.isFinite(rotation) || !Number.isFinite(startAngle) || !Number.isFinite(endAngle)) {
3690
+ return;
3691
+ }
3692
+ const ctx = this;
3693
+ const isFullEllipse = rotation === 0 && startAngle === 0 && (Math.abs(endAngle - Math.PI * 2) < 1e-6 || Math.abs(endAngle - 360) < 1e-6);
3694
+ if (isFullEllipse && !counterclockwise) {
3695
+ const kappa = 0.5522848;
3696
+ const kx = radiusX * kappa;
3697
+ const ky = radiusY * kappa;
3698
+ ctx.lineTo(x + radiusX, y);
3699
+ ctx.bezierCurveTo(x + radiusX, y + ky, x + kx, y + radiusY, x, y + radiusY);
3700
+ ctx.bezierCurveTo(x - kx, y + radiusY, x - radiusX, y + ky, x - radiusX, y);
3701
+ ctx.bezierCurveTo(x - radiusX, y - ky, x - kx, y - radiusY, x, y - radiusY);
3702
+ ctx.bezierCurveTo(x + kx, y - radiusY, x + radiusX, y - ky, x + radiusX, y);
3703
+ } else {
3704
+ const step = Math.PI / 36;
3705
+ const dir = counterclockwise ? -1 : 1;
3706
+ let end = endAngle;
3707
+ if (counterclockwise && endAngle > startAngle) {
3708
+ end -= Math.PI * 2;
3709
+ } else if (!counterclockwise && endAngle < startAngle) {
3710
+ end += Math.PI * 2;
3711
+ }
3712
+ const maxSegments = Math.ceil(Math.abs(end - startAngle) / step) + 1;
3713
+ const cos = Math.cos(rotation);
3714
+ const sin = Math.sin(rotation);
3715
+ for (let i = 0; i <= maxSegments; i++) {
3716
+ const angle = i === maxSegments ? end : startAngle + i * step * dir;
3717
+ if (dir > 0 && angle > end || dir < 0 && angle < end) {
3718
+ break;
3719
+ }
3720
+ const px2 = x + radiusX * Math.cos(angle) * cos - radiusY * Math.sin(angle) * sin;
3721
+ const py2 = y + radiusX * Math.cos(angle) * sin + radiusY * Math.sin(angle) * cos;
3722
+ ctx.lineTo(px2, py2);
3723
+ }
3724
+ const px = x + radiusX * Math.cos(endAngle) * cos - radiusY * Math.sin(endAngle) * sin;
3725
+ const py = y + radiusX * Math.cos(endAngle) * sin + radiusY * Math.sin(endAngle) * cos;
3726
+ ctx.lineTo(px, py);
3727
+ }
3728
+ }
3729
+ if (globalThis.CanvasRenderingContext2D) {
3730
+ if (typeof globalThis.CanvasRenderingContext2D.prototype.ellipse === "undefined") {
3731
+ globalThis.CanvasRenderingContext2D.prototype.ellipse = ellipse;
3732
+ }
3733
+ }
3734
+ if (globalThis.OffscreenCanvasRenderingContext2D) {
3735
+ if (typeof globalThis.OffscreenCanvasRenderingContext2D.prototype.ellipse === "undefined") {
3736
+ globalThis.OffscreenCanvasRenderingContext2D.prototype.ellipse = ellipse;
3737
+ }
3738
+ }
3739
+
3684
3740
  // src/polyfill/roundrect.ts
3685
3741
  function roundRect(x, y, w, h, radii) {
3686
3742
  if (![x, y, w, h].every((input) => {
@@ -7081,7 +7137,7 @@ function hasVideoFormat(codec) {
7081
7137
  return result;
7082
7138
  }
7083
7139
 
7084
- // src/audio/audio.js
7140
+ // src/audio/audio.ts
7085
7141
  var audio_exports = {};
7086
7142
  __export(audio_exports, {
7087
7143
  disable: () => disable,
@@ -7124,11 +7180,11 @@ var retry_counter = 0;
7124
7180
  var audioExts = [];
7125
7181
  var soundLoadError = function(sound_name, onerror_cb) {
7126
7182
  if (retry_counter++ > 3) {
7127
- const errmsg = "melonJS: failed loading " + sound_name;
7128
- if (stopOnAudioError === false) {
7183
+ const errmsg = `melonJS: failed loading ${sound_name}`;
7184
+ if (!stopOnAudioError) {
7129
7185
  disable();
7130
7186
  onerror_cb?.();
7131
- console.log(errmsg + ", disabling audio");
7187
+ console.log(`${errmsg}, disabling audio`);
7132
7188
  } else {
7133
7189
  onerror_cb?.();
7134
7190
  throw new Error(errmsg);
@@ -7161,11 +7217,13 @@ function load(sound2, onloadcb, onerrorcb, settings = {}) {
7161
7217
  "target audio extension(s) should be set through me.audio.init() before calling the preloader."
7162
7218
  );
7163
7219
  }
7164
- if (isDataUrl(sound2.src) === true) {
7220
+ if (isDataUrl(sound2.src)) {
7165
7221
  urls.push(sound2.src);
7166
7222
  } else {
7167
7223
  for (let i = 0; i < audioExts.length; i++) {
7168
- urls.push(sound2.src + sound2.name + "." + audioExts[i] + settings.nocache);
7224
+ urls.push(
7225
+ `${sound2.src + sound2.name}.${audioExts[i]}${settings.nocache ?? ""}`
7226
+ );
7169
7227
  }
7170
7228
  }
7171
7229
  audioTracks[sound2.name] = new import_howler.Howl({
@@ -7174,6 +7232,7 @@ function load(sound2, onloadcb, onerrorcb, settings = {}) {
7174
7232
  autoplay: sound2.autoplay === true,
7175
7233
  loop: sound2.loop = true,
7176
7234
  html5: sound2.stream === true || sound2.html5 === true,
7235
+ // @ts-expect-error xhrWithCredentials is a valid Howl option but not in the type definitions
7177
7236
  xhrWithCredentials: settings.withCredentials,
7178
7237
  onloaderror() {
7179
7238
  soundLoadError.call(this, sound2.name, onerrorcb);
@@ -7199,7 +7258,7 @@ function play(sound_name, loop = false, onend, volume) {
7199
7258
  id
7200
7259
  );
7201
7260
  if (typeof onend === "function") {
7202
- if (loop === true) {
7261
+ if (loop) {
7203
7262
  sound2.on("end", onend, id);
7204
7263
  } else {
7205
7264
  sound2.once("end", onend, id);
@@ -7207,7 +7266,7 @@ function play(sound_name, loop = false, onend, volume) {
7207
7266
  }
7208
7267
  return id;
7209
7268
  } else {
7210
- throw new Error("audio clip " + sound_name + " does not exist");
7269
+ throw new Error(`audio clip ${sound_name} does not exist`);
7211
7270
  }
7212
7271
  }
7213
7272
  function fade(sound_name, from, to, duration, id) {
@@ -7215,7 +7274,7 @@ function fade(sound_name, from, to, duration, id) {
7215
7274
  if (sound2 && typeof sound2 !== "undefined") {
7216
7275
  sound2.fade(from, to, duration, id);
7217
7276
  } else {
7218
- throw new Error("audio clip " + sound_name + " does not exist");
7277
+ throw new Error(`audio clip ${sound_name} does not exist`);
7219
7278
  }
7220
7279
  }
7221
7280
  function seek(sound_name, ...args) {
@@ -7223,7 +7282,7 @@ function seek(sound_name, ...args) {
7223
7282
  if (sound2 && typeof sound2 !== "undefined") {
7224
7283
  return sound2.seek(...args);
7225
7284
  } else {
7226
- throw new Error("audio clip " + sound_name + " does not exist");
7285
+ throw new Error(`audio clip ${sound_name} does not exist`);
7227
7286
  }
7228
7287
  }
7229
7288
  function rate(sound_name, ...args) {
@@ -7231,15 +7290,15 @@ function rate(sound_name, ...args) {
7231
7290
  if (sound2 && typeof sound2 !== "undefined") {
7232
7291
  return sound2.rate(...args);
7233
7292
  } else {
7234
- throw new Error("audio clip " + sound_name + " does not exist");
7293
+ throw new Error(`audio clip ${sound_name} does not exist`);
7235
7294
  }
7236
7295
  }
7237
7296
  function stereo(sound_name, pan, id) {
7238
7297
  const sound2 = audioTracks[sound_name];
7239
7298
  if (sound2 && typeof sound2 !== "undefined") {
7240
- return sound2.stereo(pan, id);
7299
+ return pan !== void 0 ? sound2.stereo(pan, id) : sound2.stereo();
7241
7300
  } else {
7242
- throw new Error("audio clip " + sound_name + " does not exist");
7301
+ throw new Error(`audio clip ${sound_name} does not exist`);
7243
7302
  }
7244
7303
  }
7245
7304
  function position(sound_name, x, y, z, id) {
@@ -7247,7 +7306,7 @@ function position(sound_name, x, y, z, id) {
7247
7306
  if (sound2 && typeof sound2 !== "undefined") {
7248
7307
  return sound2.pos(x, y, z, id);
7249
7308
  } else {
7250
- throw new Error("audio clip " + sound_name + " does not exist");
7309
+ throw new Error(`audio clip ${sound_name} does not exist`);
7251
7310
  }
7252
7311
  }
7253
7312
  function orientation(sound_name, x, y, z, id) {
@@ -7255,15 +7314,18 @@ function orientation(sound_name, x, y, z, id) {
7255
7314
  if (sound2 && typeof sound2 !== "undefined") {
7256
7315
  return sound2.orientation(x, y, z, id);
7257
7316
  } else {
7258
- throw new Error("audio clip " + sound_name + " does not exist");
7317
+ throw new Error(`audio clip ${sound_name} does not exist`);
7259
7318
  }
7260
7319
  }
7261
7320
  function panner(sound_name, attributes, id) {
7262
7321
  const sound2 = audioTracks[sound_name];
7263
7322
  if (sound2 && typeof sound2 !== "undefined") {
7264
- return sound2.pannerAttr(attributes, id);
7323
+ return sound2.pannerAttr(
7324
+ attributes,
7325
+ id
7326
+ );
7265
7327
  } else {
7266
- throw new Error("audio clip " + sound_name + " does not exist");
7328
+ throw new Error(`audio clip ${sound_name} does not exist`);
7267
7329
  }
7268
7330
  }
7269
7331
  function stop(sound_name, id) {
@@ -7273,7 +7335,7 @@ function stop(sound_name, id) {
7273
7335
  sound2.stop(id);
7274
7336
  sound2.off("end", void 0, id);
7275
7337
  } else {
7276
- throw new Error("audio clip " + sound_name + " does not exist");
7338
+ throw new Error(`audio clip ${sound_name} does not exist`);
7277
7339
  }
7278
7340
  } else {
7279
7341
  import_howler.Howler.stop();
@@ -7284,7 +7346,7 @@ function pause(sound_name, id) {
7284
7346
  if (sound2 && typeof sound2 !== "undefined") {
7285
7347
  sound2.pause(id);
7286
7348
  } else {
7287
- throw new Error("audio clip " + sound_name + " does not exist");
7349
+ throw new Error(`audio clip ${sound_name} does not exist`);
7288
7350
  }
7289
7351
  }
7290
7352
  function resume(sound_name, id) {
@@ -7292,7 +7354,7 @@ function resume(sound_name, id) {
7292
7354
  if (sound2 && typeof sound2 !== "undefined") {
7293
7355
  sound2.play(id);
7294
7356
  } else {
7295
- throw new Error("audio clip " + sound_name + " does not exist");
7357
+ throw new Error(`audio clip ${sound_name} does not exist`);
7296
7358
  }
7297
7359
  }
7298
7360
  function playTrack(sound_name, volume) {
@@ -7324,12 +7386,12 @@ function setVolume(volume) {
7324
7386
  function getVolume() {
7325
7387
  return import_howler.Howler.volume();
7326
7388
  }
7327
- function mute(sound_name, id, mute2 = true) {
7389
+ function mute(sound_name, id, shouldMute = true) {
7328
7390
  const sound2 = audioTracks[sound_name];
7329
7391
  if (sound2 && typeof sound2 !== "undefined") {
7330
- sound2.mute(mute2, id);
7392
+ sound2.mute(shouldMute, id);
7331
7393
  } else {
7332
- throw new Error("audio clip " + sound_name + " does not exist");
7394
+ throw new Error(`audio clip ${sound_name} does not exist`);
7333
7395
  }
7334
7396
  }
7335
7397
  function unmute(sound_name, id) {
@@ -7354,7 +7416,7 @@ function unload(sound_name) {
7354
7416
  }
7355
7417
  function unloadAll() {
7356
7418
  for (const sound_name in audioTracks) {
7357
- if (audioTracks.hasOwnProperty(sound_name)) {
7419
+ if (Object.prototype.hasOwnProperty.call(audioTracks, sound_name)) {
7358
7420
  unload(sound_name);
7359
7421
  }
7360
7422
  }
@@ -7471,7 +7533,7 @@ var ObservablePoint = class _ObservablePoint {
7471
7533
  }
7472
7534
  };
7473
7535
 
7474
- // src/input/input.js
7536
+ // src/input/input.ts
7475
7537
  var input_exports = {};
7476
7538
  __export(input_exports, {
7477
7539
  GAMEPAD: () => GAMEPAD,
@@ -7606,15 +7668,15 @@ function unbindKey(keyCode) {
7606
7668
  delete _preventDefaultForKeys[keyCode];
7607
7669
  }
7608
7670
 
7609
- // src/input/gamepad.js
7671
+ // src/input/gamepad.ts
7610
7672
  var deadzone = 0.1;
7611
- function wiredXbox360NormalizeFn(value, axis, button) {
7673
+ function wiredXbox360NormalizeFn(value, _axis, button) {
7612
7674
  if (button === this.GAMEPAD.BUTTONS.L2 || button === this.GAMEPAD.BUTTONS.R2) {
7613
7675
  return (value + 1) / 2;
7614
7676
  }
7615
7677
  return value;
7616
7678
  }
7617
- function ouyaNormalizeFn(value, axis, button) {
7679
+ function ouyaNormalizeFn(value, _axis, button) {
7618
7680
  if (value > 0) {
7619
7681
  if (button === this.GAMEPAD.BUTTONS.L2) {
7620
7682
  value = Math.max(0, value - 2e4) / 111070;
@@ -7629,12 +7691,18 @@ function ouyaNormalizeFn(value, axis, button) {
7629
7691
  var vendorProductRE = /^([0-9a-f]{1,4})-([0-9a-f]{1,4})-/i;
7630
7692
  var leadingZeroRE = /^0+/;
7631
7693
  function addMapping(id, mapping) {
7632
- const expanded_id = id.replace(vendorProductRE, (_, a, b) => {
7633
- return "000".slice(a.length - 1) + a + "-" + "000".slice(b.length - 1) + b + "-";
7634
- });
7635
- const sparse_id = id.replace(vendorProductRE, (_, a, b) => {
7636
- return a.replace(leadingZeroRE, "") + "-" + b.replace(leadingZeroRE, "") + "-";
7637
- });
7694
+ const expanded_id = id.replace(
7695
+ vendorProductRE,
7696
+ (_, a, b) => {
7697
+ return `${"000".slice(a.length - 1) + a}-${"000".slice(b.length - 1)}${b}-`;
7698
+ }
7699
+ );
7700
+ const sparse_id = id.replace(
7701
+ vendorProductRE,
7702
+ (_, a, b) => {
7703
+ return `${a.replace(leadingZeroRE, "")}-${b.replace(leadingZeroRE, "")}-`;
7704
+ }
7705
+ );
7638
7706
  mapping.analog = mapping.analog || mapping.buttons.map(() => {
7639
7707
  return -1;
7640
7708
  });
@@ -7757,7 +7825,7 @@ var updateGamepads = function() {
7757
7825
  }
7758
7826
  let mapping = null;
7759
7827
  if (gamepad.mapping !== "standard") {
7760
- mapping = remap.get(gamepad.id);
7828
+ mapping = remap.get(gamepad.id) || null;
7761
7829
  }
7762
7830
  const binding = bindings[index];
7763
7831
  Object.keys(binding.buttons).forEach((button) => {
@@ -7771,7 +7839,7 @@ var updateGamepads = function() {
7771
7839
  return;
7772
7840
  }
7773
7841
  }
7774
- let current = gamepad.buttons[mapped_button] || {};
7842
+ let current = gamepad.buttons[mapped_button] || { value: 0, pressed: false };
7775
7843
  if (mapping) {
7776
7844
  if (mapped_axis >= 0) {
7777
7845
  const value = mapping.normalize_fn(
@@ -7787,9 +7855,9 @@ var updateGamepads = function() {
7787
7855
  }
7788
7856
  eventEmitter.emit(GAMEPAD_UPDATE, index, "buttons", +button, current);
7789
7857
  if (!last.pressed && current.pressed) {
7790
- triggerKeyEvent(last.keyCode, true, mapped_button + 256);
7858
+ triggerKeyEvent(last.keyCode, true, Number(mapped_button) + 256);
7791
7859
  } else if (last.pressed && !current.pressed) {
7792
- triggerKeyEvent(last.keyCode, false, mapped_button + 256);
7860
+ triggerKeyEvent(last.keyCode, false, Number(mapped_button) + 256);
7793
7861
  }
7794
7862
  last.value = current.value;
7795
7863
  last.pressed = current.pressed;
@@ -7814,18 +7882,22 @@ var updateGamepads = function() {
7814
7882
  if (last[range].keyCode === 0) {
7815
7883
  return;
7816
7884
  }
7817
- const pressed = Math.abs(value) >= deadzone + Math.abs(last[range].threshold);
7885
+ const pressed = Math.abs(value) >= deadzone + Math.abs(last[range].threshold || 0);
7818
7886
  eventEmitter.emit(GAMEPAD_UPDATE, index, "axes", +axis, value);
7819
7887
  if (!last[range].pressed && pressed) {
7820
7888
  if (last[-range].pressed) {
7821
- triggerKeyEvent(last[-range].keyCode, false, mapped_axis + 256);
7889
+ triggerKeyEvent(
7890
+ last[-range].keyCode,
7891
+ false,
7892
+ Number(mapped_axis) + 256
7893
+ );
7822
7894
  last[-range].value = 0;
7823
7895
  last[-range].pressed = false;
7824
7896
  }
7825
- triggerKeyEvent(last[range].keyCode, true, mapped_axis + 256);
7897
+ triggerKeyEvent(last[range].keyCode, true, Number(mapped_axis) + 256);
7826
7898
  } else if ((last[range].pressed || last[-range].pressed) && !pressed) {
7827
7899
  range = last[range].pressed ? range : -range;
7828
- triggerKeyEvent(last[range].keyCode, false, mapped_axis + 256);
7900
+ triggerKeyEvent(last[range].keyCode, false, Number(mapped_axis) + 256);
7829
7901
  }
7830
7902
  last[range].value = value;
7831
7903
  last[range].pressed = pressed;
@@ -7908,7 +7980,7 @@ var GAMEPAD = {
7908
7980
  };
7909
7981
  function bindGamepad(index, button, keyCode) {
7910
7982
  if (!getBindingKey(keyCode)) {
7911
- throw new Error("no action defined for keycode " + keyCode);
7983
+ throw new Error(`no action defined for keycode ${keyCode}`);
7912
7984
  }
7913
7985
  if (!eventEmitter.hasListener(GAME_BEFORE_UPDATE, updateGamepads) && typeof navigator.getGamepads === "function") {
7914
7986
  eventEmitter.addListener(GAME_BEFORE_UPDATE, updateGamepads);
@@ -7948,7 +8020,7 @@ function bindGamepad(index, button, keyCode) {
7948
8020
  }
7949
8021
  function unbindGamepad(index, button) {
7950
8022
  if (!bindings[index]) {
7951
- throw new Error("no bindings for gamepad " + index);
8023
+ throw new Error(`no bindings for gamepad ${index}`);
7952
8024
  }
7953
8025
  bindings[index].buttons[button] = {};
7954
8026
  }
@@ -8060,7 +8132,7 @@ var KEY = {
8060
8132
  SINGLE_QUOTE: 222
8061
8133
  };
8062
8134
 
8063
- // src/input/input.js
8135
+ // src/input/input.ts
8064
8136
  var preventDefault = true;
8065
8137
 
8066
8138
  // src/math/color.ts
@@ -10828,11 +10900,71 @@ var linePool = createPool((x, y, points) => {
10828
10900
 
10829
10901
  // src/geometries/roundrect.ts
10830
10902
  var DEFAULT_RADIUS = 20;
10831
- var RoundRect = class _RoundRect extends Rect {
10903
+ var CORNER_SEGMENTS = 8;
10904
+ var ROUNDED_VERTEX_COUNT = 4 * (CORNER_SEGMENTS + 1);
10905
+ var CORNER_COS = new Array(CORNER_SEGMENTS + 1);
10906
+ var CORNER_SIN = new Array(CORNER_SEGMENTS + 1);
10907
+ for (let i = 0; i <= CORNER_SEGMENTS; i++) {
10908
+ const angle = i / CORNER_SEGMENTS * (Math.PI / 2);
10909
+ CORNER_COS[i] = Math.cos(angle);
10910
+ CORNER_SIN[i] = Math.sin(angle);
10911
+ }
10912
+ function updateRoundRectVertices(points, w, h, r) {
10913
+ if (r <= 0) {
10914
+ if (points.length === 4) {
10915
+ points[0].set(0, 0);
10916
+ points[1].set(w, 0);
10917
+ points[2].set(w, h);
10918
+ points[3].set(0, h);
10919
+ return points;
10920
+ }
10921
+ return [
10922
+ new Vector2d(0, 0),
10923
+ new Vector2d(w, 0),
10924
+ new Vector2d(w, h),
10925
+ new Vector2d(0, h)
10926
+ ];
10927
+ }
10928
+ const canReuse = points.length === ROUNDED_VERTEX_COUNT;
10929
+ let idx = 0;
10930
+ function setOrPush(x, y) {
10931
+ if (canReuse) {
10932
+ points[idx].set(x, y);
10933
+ } else {
10934
+ points[idx] = new Vector2d(x, y);
10935
+ }
10936
+ idx++;
10937
+ }
10938
+ if (!canReuse) {
10939
+ points.length = ROUNDED_VERTEX_COUNT;
10940
+ }
10941
+ for (let i = 0; i <= CORNER_SEGMENTS; i++) {
10942
+ setOrPush(w - r + r * CORNER_SIN[i], r - r * CORNER_COS[i]);
10943
+ }
10944
+ for (let i = 0; i <= CORNER_SEGMENTS; i++) {
10945
+ setOrPush(w - r + r * CORNER_COS[i], h - r + r * CORNER_SIN[i]);
10946
+ }
10947
+ for (let i = 0; i <= CORNER_SEGMENTS; i++) {
10948
+ setOrPush(r - r * CORNER_SIN[i], h - r + r * CORNER_COS[i]);
10949
+ }
10950
+ for (let i = 0; i <= CORNER_SEGMENTS; i++) {
10951
+ setOrPush(r - r * CORNER_COS[i], r - r * CORNER_SIN[i]);
10952
+ }
10953
+ return points;
10954
+ }
10955
+ var RoundRect = class _RoundRect extends Polygon {
10832
10956
  /**
10833
10957
  * Corner radius.
10834
10958
  */
10835
10959
  _radius;
10960
+ /**
10961
+ * stored width
10962
+ */
10963
+ _width;
10964
+ /**
10965
+ * stored height
10966
+ */
10967
+ _height;
10836
10968
  /**
10837
10969
  * the shape type (used internally)
10838
10970
  */
@@ -10845,10 +10977,73 @@ var RoundRect = class _RoundRect extends Rect {
10845
10977
  * @param [radius=20] - the radius of the rounded corner
10846
10978
  */
10847
10979
  constructor(x, y, width, height, radius = DEFAULT_RADIUS) {
10848
- super(x, y, width, height);
10980
+ super(x, y, [
10981
+ new Vector2d(0, 0),
10982
+ new Vector2d(width, 0),
10983
+ new Vector2d(width, height),
10984
+ new Vector2d(0, height)
10985
+ ]);
10849
10986
  this.type = "RoundRect";
10987
+ this._width = width;
10988
+ this._height = height;
10850
10989
  this.radius = radius;
10851
10990
  }
10991
+ /**
10992
+ * width of the RoundRect
10993
+ */
10994
+ get width() {
10995
+ return this._width;
10996
+ }
10997
+ set width(value) {
10998
+ this._width = value;
10999
+ this.radius = this._radius;
11000
+ }
11001
+ /**
11002
+ * height of the RoundRect
11003
+ */
11004
+ get height() {
11005
+ return this._height;
11006
+ }
11007
+ set height(value) {
11008
+ this._height = value;
11009
+ this.radius = this._radius;
11010
+ }
11011
+ /**
11012
+ * left coordinate of the RoundRect
11013
+ */
11014
+ get left() {
11015
+ return this.pos.x;
11016
+ }
11017
+ /**
11018
+ * right coordinate of the RoundRect
11019
+ */
11020
+ get right() {
11021
+ return this.pos.x + this._width;
11022
+ }
11023
+ /**
11024
+ * top coordinate of the RoundRect
11025
+ */
11026
+ get top() {
11027
+ return this.pos.y;
11028
+ }
11029
+ /**
11030
+ * bottom coordinate of the RoundRect
11031
+ */
11032
+ get bottom() {
11033
+ return this.pos.y + this._height;
11034
+ }
11035
+ /**
11036
+ * absolute center of this RoundRect on the horizontal axis
11037
+ */
11038
+ get centerX() {
11039
+ return this.pos.x + this._width / 2;
11040
+ }
11041
+ /**
11042
+ * absolute center of this RoundRect on the vertical axis
11043
+ */
11044
+ get centerY() {
11045
+ return this.pos.y + this._height / 2;
11046
+ }
10852
11047
  /**
10853
11048
  * the radius of the rounded corner
10854
11049
  * @default 20
@@ -10857,18 +11052,61 @@ var RoundRect = class _RoundRect extends Rect {
10857
11052
  return this._radius;
10858
11053
  }
10859
11054
  set radius(value) {
10860
- if (this.width < 2 * value) {
10861
- value = this.width / 2;
11055
+ value = Math.max(0, value);
11056
+ if (this._width < 2 * value) {
11057
+ value = this._width / 2;
10862
11058
  }
10863
- if (this.height < 2 * value) {
10864
- value = this.height / 2;
11059
+ if (this._height < 2 * value) {
11060
+ value = this._height / 2;
10865
11061
  }
10866
11062
  this._radius = value;
11063
+ this._updateVertices();
11064
+ }
11065
+ /**
11066
+ * Rebuild polygon vertices to approximate the rounded corners.
11067
+ * Reuses existing Vector2d instances when the vertex count matches.
11068
+ * @ignore
11069
+ */
11070
+ _updateVertices() {
11071
+ const updated = updateRoundRectVertices(
11072
+ this.points,
11073
+ this._width,
11074
+ this._height,
11075
+ this._radius
11076
+ );
11077
+ if (updated !== this.points) {
11078
+ this.setVertices(
11079
+ updated
11080
+ );
11081
+ } else {
11082
+ this.recalc();
11083
+ this.updateBounds();
11084
+ }
11085
+ }
11086
+ /**
11087
+ * Set new dimensions for the rounded rectangle.
11088
+ * @param width - The new width.
11089
+ * @param height - The new height.
11090
+ */
11091
+ setSize(width, height) {
11092
+ this._width = width;
11093
+ this._height = height;
11094
+ this.radius = this._radius;
11095
+ return this;
11096
+ }
11097
+ /**
11098
+ * resize the rounded rectangle
11099
+ * @param w - new width
11100
+ * @param h - new height
11101
+ * @returns this rounded rectangle
11102
+ */
11103
+ resize(w, h) {
11104
+ return this.setSize(w, h);
10867
11105
  }
10868
11106
  /**
10869
11107
  * copy the position, size and radius of the given rounded rectangle into this one
10870
11108
  * @param rrect - source rounded rectangle
10871
- * @returns new rectangle
11109
+ * @returns this rounded rectangle
10872
11110
  */
10873
11111
  copy(rrect) {
10874
11112
  this.pos.set(rrect.pos.x, rrect.pos.y);
@@ -10894,8 +11132,8 @@ var RoundRect = class _RoundRect extends Rect {
10894
11132
  }
10895
11133
  let tx;
10896
11134
  let ty;
10897
- const radiusX = Math.max(0, Math.min(this.radius, this.width / 2));
10898
- const radiusY = Math.max(0, Math.min(this.radius, this.height / 2));
11135
+ const radiusX = Math.max(0, Math.min(this.radius, this._width / 2));
11136
+ const radiusY = Math.max(0, Math.min(this.radius, this._height / 2));
10899
11137
  if (_x < this.left + radiusX && _y < this.top + radiusY) {
10900
11138
  tx = _x - this.left - radiusX;
10901
11139
  ty = _y - this.top - radiusY;
@@ -10913,13 +11151,25 @@ var RoundRect = class _RoundRect extends Rect {
10913
11151
  }
10914
11152
  return tx * tx + ty * ty <= radiusX * radiusY;
10915
11153
  }
11154
+ /**
11155
+ * Returns true if the rounded rectangle contains the given rectangle
11156
+ * @param rectangle - rectangle to test
11157
+ * @param rectangle.left - left coordinate
11158
+ * @param rectangle.right - right coordinate
11159
+ * @param rectangle.top - top coordinate
11160
+ * @param rectangle.bottom - bottom coordinate
11161
+ * @returns true if contained
11162
+ */
11163
+ containsRectangle(rectangle) {
11164
+ return rectangle.left >= this.left && rectangle.right <= this.right && rectangle.top >= this.top && rectangle.bottom <= this.bottom;
11165
+ }
10916
11166
  /**
10917
11167
  * check if this RoundRect is identical to the specified one
10918
11168
  * @param rrect - Other rounded rectangle.
10919
11169
  * @returns true if equals
10920
11170
  */
10921
11171
  equals(rrect) {
10922
- return super.equals(rrect) && this.radius === rrect.radius;
11172
+ return this.left === rrect.left && this.right === rrect.right && this.top === rrect.top && this.bottom === rrect.bottom && this.radius === rrect.radius;
10923
11173
  }
10924
11174
  /**
10925
11175
  * clone this RoundRect
@@ -10929,9 +11179,9 @@ var RoundRect = class _RoundRect extends Rect {
10929
11179
  return new _RoundRect(
10930
11180
  this.pos.x,
10931
11181
  this.pos.y,
10932
- this.width,
10933
- this.height,
10934
- this.radius
11182
+ this._width,
11183
+ this._height,
11184
+ this._radius
10935
11185
  );
10936
11186
  }
10937
11187
  };
@@ -12553,6 +12803,7 @@ var Renderable = class _Renderable extends Rect {
12553
12803
  this.updateWhenPaused = false;
12554
12804
  this.isPersistent = false;
12555
12805
  this.floating = false;
12806
+ this.visibleInAllCameras = false;
12556
12807
  this.autoTransform = true;
12557
12808
  this.alpha = 1;
12558
12809
  this.ancestor = void 0;
@@ -14474,19 +14725,29 @@ var Container = class _Container extends Renderable {
14474
14725
  if (this.backgroundColor.alpha > 1 / 255) {
14475
14726
  renderer2.clearColor(this.backgroundColor);
14476
14727
  }
14728
+ const isNonDefaultCamera = !viewport.isDefault;
14477
14729
  const children = this.getChildren();
14478
14730
  for (let i = children.length, obj; i--, obj = children[i]; ) {
14479
14731
  if (obj.isRenderable) {
14480
14732
  isFloating = obj.floating === true;
14481
14733
  if (obj.inViewport || isFloating) {
14734
+ if (isFloating && isNonDefaultCamera && !obj.visibleInAllCameras) {
14735
+ continue;
14736
+ }
14482
14737
  if (isFloating) {
14483
14738
  renderer2.save();
14484
14739
  renderer2.resetTransform();
14740
+ if (isNonDefaultCamera) {
14741
+ renderer2.setProjection(viewport.screenProjection);
14742
+ }
14485
14743
  }
14486
14744
  obj.preDraw(renderer2);
14487
14745
  obj.draw(renderer2, viewport);
14488
14746
  obj.postDraw(renderer2);
14489
14747
  if (isFloating) {
14748
+ if (isNonDefaultCamera) {
14749
+ renderer2.setProjection(viewport.worldProjection);
14750
+ }
14490
14751
  renderer2.restore();
14491
14752
  }
14492
14753
  this.drawCount++;
@@ -14497,7 +14758,7 @@ var Container = class _Container extends Renderable {
14497
14758
  };
14498
14759
 
14499
14760
  // src/level/tiled/constants.js
14500
- var TILED_SUPPORTED_VERSION = "1.9";
14761
+ var TILED_SUPPORTED_VERSION = "1.12";
14501
14762
  var TMX_FLIP_H = 2147483648;
14502
14763
  var TMX_FLIP_V = 1073741824;
14503
14764
  var TMX_FLIP_AD = 536870912;
@@ -15450,6 +15711,7 @@ var Renderer = class {
15450
15711
  this.scaleRatio = new Vector2d(this.settings.scale, this.settings.scale);
15451
15712
  this.isContextValid = true;
15452
15713
  this.depthTest = "sorting";
15714
+ this.GPURenderer = void 0;
15453
15715
  this.path2D = new path2d_default();
15454
15716
  this.type = "Generic";
15455
15717
  this.backgroundColor = new Color(
@@ -15571,6 +15833,15 @@ var Renderer = class {
15571
15833
  getBlendMode() {
15572
15834
  return this.currentBlendMode;
15573
15835
  }
15836
+ /**
15837
+ * set the current blend mode.
15838
+ * Subclasses (CanvasRenderer, WebGLRenderer) implement the actual GL/Canvas logic.
15839
+ * @param {string} [mode="normal"] - blend mode
15840
+ * @param {boolean} [premultipliedAlpha=true] - whether textures use premultiplied alpha (WebGL only)
15841
+ */
15842
+ setBlendMode(mode = "normal") {
15843
+ this.currentBlendMode = mode;
15844
+ }
15574
15845
  /**
15575
15846
  * get the current fill & stroke style color.
15576
15847
  * @returns {Color} current global color
@@ -16675,39 +16946,89 @@ var CanvasRenderer = class extends Renderer {
16675
16946
  this.getContext().setTransform(1, 0, 0, 1, 0, 0);
16676
16947
  }
16677
16948
  /**
16678
- * set a blend mode for the given context. <br>
16679
- * Supported blend mode between Canvas and WebGL renderer : <br>
16680
- * - "normal" : this is the default mode and draws new content on top of the existing content <br>
16681
- * <img src="../images/normal-blendmode.png" width="510"/> <br>
16682
- * - "multiply" : the pixels of the top layer are multiplied with the corresponding pixel of the bottom layer. A darker picture is the result. <br>
16683
- * <img src="../images/multiply-blendmode.png" width="510"/> <br>
16684
- * - "additive or lighter" : where both content overlap the color is determined by adding color values. <br>
16685
- * <img src="../images/lighter-blendmode.png" width="510"/> <br>
16686
- * - "screen" : The pixels are inverted, multiplied, and inverted again. A lighter picture is the result (opposite of multiply) <br>
16687
- * <img src="../images/screen-blendmode.png" width="510"/> <br>
16949
+ * set/change the current projection matrix.
16950
+ * In Canvas mode, this applies the ortho projection as a canvas 2D transform
16951
+ * (translate + scale) to map world coordinates to screen coordinates.
16952
+ * @param {Matrix3d} matrix - the new projection matrix
16953
+ */
16954
+ setProjection(matrix) {
16955
+ super.setProjection(matrix);
16956
+ const val = matrix.val;
16957
+ const w = this.getCanvas().width;
16958
+ const h = this.getCanvas().height;
16959
+ const sx = val[0] * w * 0.5;
16960
+ const sy = -val[5] * h * 0.5;
16961
+ const tx = (val[12] + 1) * w * 0.5;
16962
+ const ty = (1 - val[13]) * h * 0.5;
16963
+ this.getContext().setTransform(sx, 0, 0, sy, tx, ty);
16964
+ }
16965
+ /**
16966
+ * set the current blend mode for this renderer. <br>
16967
+ * All renderers support: <br>
16968
+ * - "normal" : draws new content on top of the existing content <br>
16969
+ * <img src="../images/normal-blendmode.png" width="180"/> <br>
16970
+ * - "add", "additive", or "lighter" : color values are added together <br>
16971
+ * <img src="../images/add-blendmode.png" width="180"/> <br>
16972
+ * - "multiply" : pixels are multiplied, resulting in a darker picture <br>
16973
+ * <img src="../images/multiply-blendmode.png" width="180"/> <br>
16974
+ * - "screen" : pixels are inverted, multiplied, and inverted again (opposite of multiply) <br>
16975
+ * <img src="../images/screen-blendmode.png" width="180"/> <br>
16976
+ * Canvas (browser-dependent) and WebGL2: <br>
16977
+ * - "darken" : retains the darkest pixels of both layers <br>
16978
+ * <img src="../images/darken-blendmode.png" width="180"/> <br>
16979
+ * - "lighten" : retains the lightest pixels of both layers <br>
16980
+ * <img src="../images/lighten-blendmode.png" width="180"/> <br>
16981
+ * Canvas only, browser-dependent (falls back to "normal" if unsupported or in WebGL): <br>
16982
+ * - "overlay" <br>
16983
+ * <img src="../images/overlay-blendmode.png" width="180"/> <br>
16984
+ * - "color-dodge" <br>
16985
+ * <img src="../images/color-dodge-blendmode.png" width="180"/> <br>
16986
+ * - "color-burn" <br>
16987
+ * <img src="../images/color-burn-blendmode.png" width="180"/> <br>
16988
+ * - "hard-light" <br>
16989
+ * <img src="../images/hard-light-blendmode.png" width="180"/> <br>
16990
+ * - "soft-light" <br>
16991
+ * <img src="../images/soft-light-blendmode.png" width="180"/> <br>
16992
+ * - "difference" <br>
16993
+ * <img src="../images/difference-blendmode.png" width="180"/> <br>
16994
+ * - "exclusion" <br>
16995
+ * <img src="../images/exclusion-blendmode.png" width="180"/> <br>
16688
16996
  * @see https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/globalCompositeOperation
16689
- * @param {string} [mode="normal"] - blend mode : "normal", "multiply", "lighter, "additive", "screen"
16690
- * @param {CanvasRenderingContext2D} [context]
16997
+ * @param {string} [mode="normal"] - blend mode
16998
+ * @returns {string} the blend mode actually applied (may differ if the requested mode is unsupported)
16691
16999
  */
16692
- setBlendMode(mode = "normal", context) {
16693
- context = context || this.getContext();
17000
+ setBlendMode(mode = "normal") {
17001
+ const context = this.getContext();
16694
17002
  this.currentBlendMode = mode;
16695
17003
  switch (mode) {
16696
- case "screen":
16697
- context.globalCompositeOperation = "screen";
16698
- break;
16699
17004
  case "lighter":
16700
17005
  case "additive":
17006
+ case "add":
16701
17007
  context.globalCompositeOperation = "lighter";
16702
17008
  break;
16703
17009
  case "multiply":
16704
- context.globalCompositeOperation = "multiply";
17010
+ case "screen":
17011
+ case "overlay":
17012
+ case "darken":
17013
+ case "lighten":
17014
+ case "color-dodge":
17015
+ case "color-burn":
17016
+ case "hard-light":
17017
+ case "soft-light":
17018
+ case "difference":
17019
+ case "exclusion":
17020
+ context.globalCompositeOperation = mode;
17021
+ if (context.globalCompositeOperation !== mode) {
17022
+ context.globalCompositeOperation = "source-over";
17023
+ this.currentBlendMode = "normal";
17024
+ }
16705
17025
  break;
16706
17026
  default:
16707
17027
  context.globalCompositeOperation = "source-over";
16708
17028
  this.currentBlendMode = "normal";
16709
17029
  break;
16710
17030
  }
17031
+ return this.currentBlendMode;
16711
17032
  }
16712
17033
  /**
16713
17034
  * prepare the framebuffer for drawing a new frame
@@ -16975,24 +17296,8 @@ var CanvasRenderer = class extends Renderer {
16975
17296
  return;
16976
17297
  }
16977
17298
  const context = this.getContext();
16978
- const hw = w;
16979
- const hh = h;
16980
- const lx = x - hw;
16981
- const rx = x + hw;
16982
- const ty = y - hh;
16983
- const by = y + hh;
16984
- const xmagic = hw * 0.551784;
16985
- const ymagic = hh * 0.551784;
16986
- const xmin = x - xmagic;
16987
- const xmax = x + xmagic;
16988
- const ymin = y - ymagic;
16989
- const ymax = y + ymagic;
16990
17299
  context.beginPath();
16991
- context.moveTo(x, ty);
16992
- context.bezierCurveTo(xmax, ty, rx, ymin, rx, y);
16993
- context.bezierCurveTo(rx, ymax, xmax, by, x, by);
16994
- context.bezierCurveTo(xmin, by, lx, ymax, lx, y);
16995
- context.bezierCurveTo(lx, ymin, xmin, ty, x, ty);
17300
+ context.ellipse(x, y, w, h, 0, 0, Math.PI * 2);
16996
17301
  context[fill === true ? "fill" : "stroke"]();
16997
17302
  context.closePath();
16998
17303
  }
@@ -17441,13 +17746,13 @@ var Tile = class extends Bounds {
17441
17746
  * @param {TMXTileset} tileset - the corresponding tileset object
17442
17747
  */
17443
17748
  constructor(x, y, gid, tileset) {
17749
+ super();
17444
17750
  let width;
17445
17751
  let height;
17446
- super();
17447
17752
  if (tileset.isCollection) {
17448
- const image = tileset.getTileImage(gid & TMX_CLEAR_BIT_MASK);
17449
- width = image.width;
17450
- height = image.height;
17753
+ const tileImage = tileset.getTileImage(gid & TMX_CLEAR_BIT_MASK);
17754
+ width = tileImage.width;
17755
+ height = tileImage.height;
17451
17756
  } else {
17452
17757
  width = tileset.tilewidth;
17453
17758
  height = tileset.tileheight;
@@ -17462,20 +17767,21 @@ var Tile = class extends Bounds {
17462
17767
  this.flippedY = (this.tileId & TMX_FLIP_V) !== 0;
17463
17768
  this.flippedAD = (this.tileId & TMX_FLIP_AD) !== 0;
17464
17769
  this.flipped = this.flippedX || this.flippedY || this.flippedAD;
17465
- if (this.flipped === true) {
17466
- if (this.currentTransform === null) {
17467
- this.currentTransform = new Matrix2d();
17468
- }
17770
+ if (this.flipped) {
17771
+ this.currentTransform = new Matrix2d();
17469
17772
  this.setTileTransform(this.currentTransform.identity());
17470
17773
  }
17471
17774
  this.tileId &= TMX_CLEAR_BIT_MASK;
17472
17775
  }
17473
17776
  /**
17474
17777
  * set the transformation matrix for this tile
17778
+ * @param {Matrix2d} transform - the transformation matrix to apply
17475
17779
  * @ignore
17476
17780
  */
17477
17781
  setTileTransform(transform) {
17478
- transform.translate(this.width / 2, this.height / 2);
17782
+ const halfW = this.width / 2;
17783
+ const halfH = this.height / 2;
17784
+ transform.translate(halfW, halfH);
17479
17785
  if (this.flippedAD) {
17480
17786
  transform.rotate(degToRad(-90));
17481
17787
  transform.scale(-1, 1);
@@ -17486,7 +17792,7 @@ var Tile = class extends Bounds {
17486
17792
  if (this.flippedY) {
17487
17793
  transform.scale(this.flippedAD ? -1 : 1, this.flippedAD ? 1 : -1);
17488
17794
  }
17489
- transform.translate(-this.width / 2, -this.height / 2);
17795
+ transform.translate(-halfW, -halfH);
17490
17796
  }
17491
17797
  /**
17492
17798
  * return a renderable object for this Tile object
@@ -17496,51 +17802,43 @@ var Tile = class extends Bounds {
17496
17802
  getRenderable(settings) {
17497
17803
  let renderable;
17498
17804
  const tileset = this.tileset;
17499
- if (tileset.animations.has(this.tileId)) {
17805
+ const localId = this.tileId - tileset.firstgid;
17806
+ if (tileset.animations.has(localId)) {
17500
17807
  const frames = [];
17501
17808
  const frameId = [];
17502
- tileset.animations.get(this.tileId).frames.forEach((frame) => {
17809
+ for (const frame of tileset.animations.get(localId).frames) {
17503
17810
  frameId.push(frame.tileid);
17504
17811
  frames.push({
17505
17812
  name: "" + frame.tileid,
17506
17813
  delay: frame.duration
17507
17814
  });
17508
- });
17815
+ }
17509
17816
  renderable = tileset.texture.createAnimationFromName(frameId, settings);
17510
- renderable.addAnimation(this.tileId - tileset.firstgid, frames);
17511
- renderable.setCurrentAnimation(this.tileId - tileset.firstgid);
17512
- } else {
17513
- if (tileset.isCollection === true) {
17514
- const image = tileset.getTileImage(this.tileId);
17515
- renderable = new Sprite(
17516
- 0,
17517
- 0,
17518
- Object.assign({
17519
- image
17520
- })
17521
- //, settings)
17522
- );
17523
- renderable.anchorPoint.set(0, 0);
17524
- renderable.scale(
17525
- settings.width / this.width,
17526
- settings.height / this.height
17527
- );
17528
- if (typeof settings.rotation !== "undefined") {
17529
- renderable.anchorPoint.set(0.5, 0.5);
17530
- renderable.currentTransform.rotate(settings.rotation);
17531
- renderable.currentTransform.translate(
17532
- settings.width / 2,
17533
- settings.height / 2
17534
- );
17535
- settings.rotation = void 0;
17536
- }
17537
- } else {
17538
- renderable = tileset.texture.createSpriteFromName(
17539
- this.tileId - tileset.firstgid,
17540
- settings
17817
+ renderable.addAnimation(localId, frames);
17818
+ renderable.setCurrentAnimation(localId);
17819
+ } else if (tileset.isCollection) {
17820
+ const image = tileset.getTileImage(this.tileId);
17821
+ renderable = new Sprite(0, 0, { image });
17822
+ renderable.anchorPoint.set(0, 0);
17823
+ renderable.scale(
17824
+ settings.width / this.width,
17825
+ settings.height / this.height
17826
+ );
17827
+ if (settings.rotation !== void 0) {
17828
+ renderable.anchorPoint.set(0.5, 0.5);
17829
+ renderable.currentTransform.rotate(settings.rotation);
17830
+ renderable.currentTransform.translate(
17831
+ settings.width / 2,
17832
+ settings.height / 2
17541
17833
  );
17542
- renderable.anchorPoint.set(0, 0);
17834
+ settings.rotation = void 0;
17543
17835
  }
17836
+ } else {
17837
+ renderable = tileset.texture.createSpriteFromName(
17838
+ this.tileId - tileset.firstgid,
17839
+ settings
17840
+ );
17841
+ renderable.anchorPoint.set(0, 0);
17544
17842
  }
17545
17843
  this.setTileTransform(renderable.currentTransform);
17546
17844
  return renderable;
@@ -17551,9 +17849,12 @@ var Tile = class extends Bounds {
17551
17849
  var TMXUtils_exports = {};
17552
17850
  __export(TMXUtils_exports, {
17553
17851
  applyTMXProperties: () => applyTMXProperties,
17852
+ cacheEmbeddedImage: () => cacheEmbeddedImage,
17554
17853
  decode: () => decode,
17555
17854
  parse: () => parse,
17556
- setInflateFunction: () => setInflateFunction
17855
+ resolveEmbeddedImage: () => resolveEmbeddedImage,
17856
+ setInflateFunction: () => setInflateFunction,
17857
+ tiledBlendMode: () => tiledBlendMode
17557
17858
  });
17558
17859
 
17559
17860
  // src/utils/decode.ts
@@ -17615,6 +17916,22 @@ function decodeBase64AsArray(input, bytes = 1) {
17615
17916
  }
17616
17917
  return ar;
17617
17918
  }
17919
+ var mimeTypes = {
17920
+ jpg: "image/jpeg",
17921
+ svg: "image/svg+xml"
17922
+ };
17923
+ function decodeBase64Image(base64, format = "png", width, height) {
17924
+ const img = new Image();
17925
+ if (width != null) {
17926
+ img.width = width;
17927
+ }
17928
+ if (height != null) {
17929
+ img.height = height;
17930
+ }
17931
+ const mime = mimeTypes[format] || `image/${format}`;
17932
+ img.src = `data:${mime};base64,${base64}`;
17933
+ return img;
17934
+ }
17618
17935
  function decode(data2, encoding, compression) {
17619
17936
  const comp = compression || "none";
17620
17937
  const enc = encoding || "none";
@@ -17670,10 +17987,35 @@ function xmlToObject(element, normalizer) {
17670
17987
  }
17671
17988
 
17672
17989
  // src/level/tiled/TMXUtils.js
17990
+ var embeddedImageId = 0;
17991
+ function cacheEmbeddedImage(base64, format = "png", width, height) {
17992
+ const name = `__embedded_${embeddedImageId++}`;
17993
+ imgList[name] = decodeBase64Image(base64, format, width, height);
17994
+ return `${name}.${format}`;
17995
+ }
17996
+ function resolveEmbeddedImage(data2) {
17997
+ if (data2.imagedata) {
17998
+ const format = data2.imageformat || "png";
17999
+ data2.image = cacheEmbeddedImage(
18000
+ data2.imagedata,
18001
+ format,
18002
+ data2.imagewidth ? +data2.imagewidth : void 0,
18003
+ data2.imageheight ? +data2.imageheight : void 0
18004
+ );
18005
+ delete data2.imagedata;
18006
+ delete data2.imageformat;
18007
+ }
18008
+ }
18009
+ function tiledBlendMode(mode) {
18010
+ if (typeof mode === "undefined" || mode === "normal") {
18011
+ return "normal";
18012
+ }
18013
+ return mode === "add" ? "lighter" : mode;
18014
+ }
17673
18015
  var SHORT_ARGB = /^#([\da-fA-F])([\da-fA-F]{3})$/;
17674
18016
  var LONG_ARGB = /^#([\da-fA-F]{2})([\da-fA-F]{6})$/;
17675
18017
  function coerceTMXValue(name, type, raw) {
17676
- if (typeof raw !== "string") {
18018
+ if (typeof raw !== "string" && type !== "list") {
17677
18019
  return raw;
17678
18020
  }
17679
18021
  switch (type) {
@@ -17682,6 +18024,13 @@ function coerceTMXValue(name, type, raw) {
17682
18024
  return Number(raw);
17683
18025
  case "bool":
17684
18026
  return raw === "true";
18027
+ case "list":
18028
+ if (Array.isArray(raw)) {
18029
+ return raw.map((item) => {
18030
+ return coerceTMXValue("", item.type || "string", item.value);
18031
+ });
18032
+ }
18033
+ return raw;
17685
18034
  default:
17686
18035
  break;
17687
18036
  }
@@ -17730,7 +18079,20 @@ function flattenImage(obj) {
17730
18079
  if (obj.image) {
17731
18080
  obj.imagewidth = obj.image.width;
17732
18081
  obj.imageheight = obj.image.height;
17733
- obj.image = obj.image.source;
18082
+ if (obj.image.source) {
18083
+ obj.image = obj.image.source;
18084
+ } else if (obj.image.data) {
18085
+ const format = obj.image.format || "png";
18086
+ const base64 = obj.image.data.text || obj.image.data;
18087
+ obj.image = cacheEmbeddedImage(
18088
+ base64,
18089
+ format,
18090
+ obj.image.width ? +obj.image.width : void 0,
18091
+ obj.image.height ? +obj.image.height : void 0
18092
+ );
18093
+ } else {
18094
+ obj.image = obj.image.source;
18095
+ }
17734
18096
  }
17735
18097
  }
17736
18098
  function normalizeTMX(obj, item, parse2) {
@@ -17773,7 +18135,18 @@ function normalizeTMX(obj, item, parse2) {
17773
18135
  const layer = parse2(item);
17774
18136
  layer.type = nodeName === "layer" ? "tilelayer" : nodeName;
17775
18137
  if (layer.image) {
17776
- layer.image = layer.image.source;
18138
+ if (layer.image.source) {
18139
+ layer.image = layer.image.source;
18140
+ } else if (layer.image.data) {
18141
+ const format = layer.image.format || "png";
18142
+ const base64 = layer.image.data.text || layer.image.data;
18143
+ layer.image = cacheEmbeddedImage(
18144
+ base64,
18145
+ format,
18146
+ layer.image.width ? +layer.image.width : void 0,
18147
+ layer.image.height ? +layer.image.height : void 0
18148
+ );
18149
+ }
17777
18150
  }
17778
18151
  const layers = obj.layers || (obj.layers = []);
17779
18152
  layers.push(layer);
@@ -17821,16 +18194,37 @@ function normalizeTMX(obj, item, parse2) {
17821
18194
  case "properties":
17822
18195
  obj.properties = parse2(item);
17823
18196
  break;
18197
+ case "item": {
18198
+ const itemData = parse2(item);
18199
+ const items = obj.items || (obj.items = []);
18200
+ items.push(
18201
+ coerceTMXValue(
18202
+ "",
18203
+ itemData.type || "string",
18204
+ itemData.value !== void 0 ? itemData.value : itemData.text
18205
+ )
18206
+ );
18207
+ break;
18208
+ }
17824
18209
  case "property": {
17825
18210
  const prop = parse2(item);
17826
- obj[prop.name] = coerceTMXValue(
17827
- prop.name,
17828
- // in XML, type is undefined for "string" values
17829
- prop.type || "string",
17830
- prop.value !== void 0 ? prop.value : prop.text
17831
- );
18211
+ if (prop.type === "class") {
18212
+ obj[prop.name] = prop.properties || {};
18213
+ } else if (prop.type === "list") {
18214
+ obj[prop.name] = prop.items || [];
18215
+ } else {
18216
+ obj[prop.name] = coerceTMXValue(
18217
+ prop.name,
18218
+ // in XML, type is undefined for "string" values
18219
+ prop.type || "string",
18220
+ prop.value !== void 0 ? prop.value : prop.text
18221
+ );
18222
+ }
17832
18223
  break;
17833
18224
  }
18225
+ case "image":
18226
+ obj.image = xmlToObject(item);
18227
+ break;
17834
18228
  default:
17835
18229
  obj[nodeName] = parse2(item);
17836
18230
  break;
@@ -17860,7 +18254,7 @@ function applyTMXProperties(obj, data2) {
17860
18254
  for (let i = 0, len = keys.length; i < len; i++) {
17861
18255
  const key = keys[i];
17862
18256
  const prop = properties[key];
17863
- if (prop !== null && typeof prop === "object" && prop.name !== void 0) {
18257
+ if (prop !== null && typeof prop === "object" && !isNaN(key) && prop.name !== void 0 && prop.type !== void 0) {
17864
18258
  obj[prop.name] = coerceTMXValue(
17865
18259
  prop.name,
17866
18260
  prop.type || "string",
@@ -17950,6 +18344,7 @@ var TMXLayer = class extends Renderable {
17950
18344
  this.rows = +data2.height;
17951
18345
  const visible = typeof data2.visible !== "undefined" ? +data2.visible : 1;
17952
18346
  this.setOpacity(visible ? +data2.opacity : 0);
18347
+ this.blendMode = tiledBlendMode(data2.mode);
17953
18348
  if (typeof data2.tintcolor === "string") {
17954
18349
  this.tint.parseHex(data2.tintcolor, true);
17955
18350
  }
@@ -18899,60 +19294,211 @@ var TMXOrthogonalRenderer = class extends TMXRenderer {
18899
19294
  }
18900
19295
  };
18901
19296
 
18902
- // src/level/tiled/renderer/TMXStaggeredRenderer.js
18903
- var TMXStaggeredRenderer = class extends TMXHexagonalRenderer {
19297
+ // src/level/tiled/renderer/TMXObliqueRenderer.js
19298
+ var TMXObliqueRenderer = class extends TMXOrthogonalRenderer {
19299
+ /**
19300
+ * @param {TMXTileMap} map - the TMX map
19301
+ */
19302
+ constructor(map) {
19303
+ super(map);
19304
+ this.skewX = map.skewx;
19305
+ this.skewY = map.skewy;
19306
+ this.shearX = this.tileheight !== 0 ? this.skewX / this.tileheight : 0;
19307
+ this.shearY = this.tilewidth !== 0 ? this.skewY / this.tilewidth : 0;
19308
+ this._det = 1 - this.shearX * this.shearY;
19309
+ }
18904
19310
  /**
18905
19311
  * return true if the renderer can render the specified layer
18906
19312
  * @ignore
18907
19313
  */
18908
19314
  canRender(layer) {
18909
- return layer.orientation === "staggered" && super.canRender(layer);
19315
+ return layer.orientation === "oblique" && this.tilewidth === layer.tilewidth && this.tileheight === layer.tileheight;
19316
+ }
19317
+ /**
19318
+ * return the bounding rect for this map renderer
19319
+ * @param {TMXLayer} [layer] - calculate the bounding rect for a specific layer
19320
+ * @returns {Bounds}
19321
+ */
19322
+ getBounds(layer) {
19323
+ const bounds = layer instanceof TMXLayer ? boundsPool.get() : this.bounds;
19324
+ const cols = layer instanceof TMXLayer ? layer.cols : this.cols;
19325
+ const rows = layer instanceof TMXLayer ? layer.rows : this.rows;
19326
+ const w = cols * this.tilewidth;
19327
+ const h = rows * this.tileheight;
19328
+ const sx = this.skewX * rows;
19329
+ const sy = this.skewY * cols;
19330
+ const minX = Math.min(0, sx, sy * this.shearX, w + sx);
19331
+ const minY = Math.min(0, sy, sx * this.shearY, h + sy);
19332
+ const maxX = Math.max(0, w, w + sx, sx);
19333
+ const maxY = Math.max(0, h, h + sy, sy);
19334
+ bounds.setMinMax(minX, minY, maxX, maxY);
19335
+ return bounds;
18910
19336
  }
18911
19337
  /**
18912
19338
  * return the tile position corresponding to the specified pixel
18913
19339
  * @ignore
18914
19340
  */
18915
19341
  pixelToTileCoords(x, y, v) {
18916
- let ret = v || vector2dPool.get();
18917
- let alignedX = x;
18918
- let alignedY = y;
18919
- if (this.staggerX) {
18920
- alignedX -= this.staggerEven ? this.sideoffsetx : 0;
18921
- } else {
18922
- alignedY -= this.staggerEven ? this.sideoffsety : 0;
18923
- }
18924
- let referencePoint = vector2dPool.get(
18925
- Math.floor(alignedX / this.tilewidth),
18926
- Math.floor(alignedY / this.tileheight)
18927
- );
18928
- if (this.staggerX) {
18929
- referencePoint.x = referencePoint.x * 2;
18930
- if (this.staggerEven) {
18931
- ++referencePoint.x;
19342
+ const ret = v || vector2dPool.get();
19343
+ const px = (x - this.shearX * y) / this._det;
19344
+ const py = (y - this.shearY * x) / this._det;
19345
+ return ret.set(px / this.tilewidth, py / this.tileheight);
19346
+ }
19347
+ /**
19348
+ * return the pixel position corresponding of the specified tile
19349
+ * @ignore
19350
+ */
19351
+ tileToPixelCoords(x, y, v) {
19352
+ const ret = v || vector2dPool.get();
19353
+ const px = x * this.tilewidth;
19354
+ const py = y * this.tileheight;
19355
+ return ret.set(px + this.shearX * py, this.shearY * px + py);
19356
+ }
19357
+ /**
19358
+ * draw the tile map
19359
+ * @ignore
19360
+ */
19361
+ drawTile(renderer2, x, y, tmxTile) {
19362
+ const tileset = tmxTile.tileset;
19363
+ const dx = tileset.tileoffset.x + x * this.tilewidth + this.skewX * y;
19364
+ const dy = tileset.tileoffset.y + (y + 1) * this.tileheight - tileset.tileheight + this.skewY * x;
19365
+ tileset.drawTile(renderer2, dx, dy, tmxTile);
19366
+ }
19367
+ /**
19368
+ * draw the given TMX Layer for the given area
19369
+ * @ignore
19370
+ */
19371
+ drawTileLayer(renderer2, layer, rect) {
19372
+ let incX = 1;
19373
+ let incY = 1;
19374
+ const rxMin = rect.pos.x;
19375
+ const ryMin = rect.pos.y;
19376
+ const rxMax = rect.pos.x + rect.width;
19377
+ const ryMax = rect.pos.y + rect.height;
19378
+ const invCorners = [
19379
+ this.pixelToTileCoords(rxMin, ryMin, vector2dPool.get()),
19380
+ this.pixelToTileCoords(rxMax, ryMin, vector2dPool.get()),
19381
+ this.pixelToTileCoords(rxMin, ryMax, vector2dPool.get()),
19382
+ this.pixelToTileCoords(rxMax, ryMax, vector2dPool.get())
19383
+ ];
19384
+ let startX = Infinity;
19385
+ let startY = Infinity;
19386
+ let endX = -Infinity;
19387
+ let endY = -Infinity;
19388
+ for (const c of invCorners) {
19389
+ if (c.x < startX) {
19390
+ startX = c.x;
18932
19391
  }
18933
- } else {
18934
- referencePoint.y = referencePoint.y * 2;
18935
- if (this.staggerEven) {
18936
- ++referencePoint.y;
19392
+ if (c.y < startY) {
19393
+ startY = c.y;
19394
+ }
19395
+ if (c.x > endX) {
19396
+ endX = c.x;
19397
+ }
19398
+ if (c.y > endY) {
19399
+ endY = c.y;
18937
19400
  }
18938
19401
  }
18939
- const rel = vector2dPool.get(
18940
- alignedX - referencePoint.x * this.tilewidth,
18941
- alignedY - referencePoint.y * this.tileheight
18942
- );
18943
- const y_pos = rel.x * (this.tileheight / this.tilewidth);
18944
- if (this.sideoffsety - y_pos > rel.y) {
18945
- referencePoint = this.topLeft(
18946
- referencePoint.x,
18947
- referencePoint.y,
18948
- referencePoint
18949
- );
19402
+ for (const c of invCorners) {
19403
+ vector2dPool.release(c);
18950
19404
  }
18951
- if (-this.sideoffsety + y_pos > rel.y) {
18952
- referencePoint = this.topRight(
18953
- referencePoint.x,
18954
- referencePoint.y,
18955
- referencePoint
19405
+ startX = Math.max(
19406
+ Math.floor(
19407
+ startX - (layer.maxTileSize.width - layer.tilewidth) / this.tilewidth
19408
+ ),
19409
+ 0
19410
+ );
19411
+ startY = Math.max(
19412
+ Math.floor(
19413
+ startY - (layer.maxTileSize.height - layer.tileheight) / this.tileheight
19414
+ ),
19415
+ 0
19416
+ );
19417
+ endX = Math.min(Math.ceil(endX) + 1, this.cols);
19418
+ endY = Math.min(Math.ceil(endY) + 1, this.rows);
19419
+ switch (layer.renderorder) {
19420
+ case "right-up":
19421
+ endY = startY + (startY = endY) - endY;
19422
+ incY = -1;
19423
+ break;
19424
+ case "left-down":
19425
+ endX = startX + (startX = endX) - endX;
19426
+ incX = -1;
19427
+ break;
19428
+ case "left-up":
19429
+ endX = startX + (startX = endX) - endX;
19430
+ endY = startY + (startY = endY) - endY;
19431
+ incX = -1;
19432
+ incY = -1;
19433
+ break;
19434
+ default:
19435
+ break;
19436
+ }
19437
+ for (let y = startY; y !== endY; y += incY) {
19438
+ for (let x = startX; x !== endX; x += incX) {
19439
+ const tmxTile = layer.cellAt(x, y, false);
19440
+ if (tmxTile) {
19441
+ this.drawTile(renderer2, x, y, tmxTile);
19442
+ }
19443
+ }
19444
+ }
19445
+ }
19446
+ };
19447
+
19448
+ // src/level/tiled/renderer/TMXStaggeredRenderer.js
19449
+ var TMXStaggeredRenderer = class extends TMXHexagonalRenderer {
19450
+ /**
19451
+ * return true if the renderer can render the specified layer
19452
+ * @ignore
19453
+ */
19454
+ canRender(layer) {
19455
+ return layer.orientation === "staggered" && super.canRender(layer);
19456
+ }
19457
+ /**
19458
+ * return the tile position corresponding to the specified pixel
19459
+ * @ignore
19460
+ */
19461
+ pixelToTileCoords(x, y, v) {
19462
+ let ret = v || vector2dPool.get();
19463
+ let alignedX = x;
19464
+ let alignedY = y;
19465
+ if (this.staggerX) {
19466
+ alignedX -= this.staggerEven ? this.sideoffsetx : 0;
19467
+ } else {
19468
+ alignedY -= this.staggerEven ? this.sideoffsety : 0;
19469
+ }
19470
+ let referencePoint = vector2dPool.get(
19471
+ Math.floor(alignedX / this.tilewidth),
19472
+ Math.floor(alignedY / this.tileheight)
19473
+ );
19474
+ if (this.staggerX) {
19475
+ referencePoint.x = referencePoint.x * 2;
19476
+ if (this.staggerEven) {
19477
+ ++referencePoint.x;
19478
+ }
19479
+ } else {
19480
+ referencePoint.y = referencePoint.y * 2;
19481
+ if (this.staggerEven) {
19482
+ ++referencePoint.y;
19483
+ }
19484
+ }
19485
+ const rel = vector2dPool.get(
19486
+ alignedX - referencePoint.x * this.tilewidth,
19487
+ alignedY - referencePoint.y * this.tileheight
19488
+ );
19489
+ const y_pos = rel.x * (this.tileheight / this.tilewidth);
19490
+ if (this.sideoffsety - y_pos > rel.y) {
19491
+ referencePoint = this.topLeft(
19492
+ referencePoint.x,
19493
+ referencePoint.y,
19494
+ referencePoint
19495
+ );
19496
+ }
19497
+ if (-this.sideoffsety + y_pos > rel.y) {
19498
+ referencePoint = this.topRight(
19499
+ referencePoint.x,
19500
+ referencePoint.y,
19501
+ referencePoint
18956
19502
  );
18957
19503
  }
18958
19504
  if (this.sideoffsety + y_pos < rel.y) {
@@ -18993,6 +19539,8 @@ function getNewTMXRenderer(map) {
18993
19539
  return new TMXHexagonalRenderer(map);
18994
19540
  case "staggered":
18995
19541
  return new TMXStaggeredRenderer(map);
19542
+ case "oblique":
19543
+ return new TMXObliqueRenderer(map);
18996
19544
  // if none found, throw an exception
18997
19545
  default:
18998
19546
  throw new Error(`${map.orientation} type TMX Tile Map not supported!`);
@@ -19000,6 +19548,24 @@ function getNewTMXRenderer(map) {
19000
19548
  }
19001
19549
 
19002
19550
  // src/level/tiled/TMXObject.js
19551
+ function detectShape(settings) {
19552
+ if (typeof settings.ellipse !== "undefined") {
19553
+ return "ellipse";
19554
+ }
19555
+ if (typeof settings.capsule !== "undefined") {
19556
+ return "capsule";
19557
+ }
19558
+ if (typeof settings.point !== "undefined") {
19559
+ return "point";
19560
+ }
19561
+ if (typeof settings.polygon !== "undefined") {
19562
+ return "polygon";
19563
+ }
19564
+ if (typeof settings.polyline !== "undefined") {
19565
+ return "polyline";
19566
+ }
19567
+ return "rectangle";
19568
+ }
19003
19569
  var TMXObject = class {
19004
19570
  constructor(map, settings, z) {
19005
19571
  this.points = void 0;
@@ -19015,28 +19581,25 @@ var TMXObject = class {
19015
19581
  this.class = settings.class ?? settings.type;
19016
19582
  this.text = void 0;
19017
19583
  this.rotation = degToRad(+settings.rotation || 0);
19584
+ this.opacity = +(settings.opacity ?? 1);
19585
+ this.visible = +(settings.visible ?? 1) !== 0;
19018
19586
  this.id = +settings.id || void 0;
19019
19587
  this.orientation = map.orientation;
19020
19588
  this.shapes = void 0;
19021
- this.isEllipse = false;
19022
- this.isPoint = false;
19023
- this.isPolygon = false;
19024
- this.isPolyLine = false;
19589
+ this.shapeType = "rectangle";
19025
19590
  if (typeof this.gid === "number") {
19026
19591
  this.setTile(map.tilesets);
19027
19592
  } else {
19028
- if (typeof settings.ellipse !== "undefined") {
19029
- this.isEllipse = true;
19030
- } else if (typeof settings.point !== "undefined") {
19031
- this.isPoint = true;
19032
- } else if (typeof settings.polygon !== "undefined") {
19033
- this.points = settings.polygon;
19034
- this.isPolygon = true;
19035
- } else if (typeof settings.polyline !== "undefined") {
19036
- this.points = settings.polyline;
19037
- this.isPolyLine = true;
19593
+ this.shapeType = detectShape(settings);
19594
+ if (this.shapeType === "polygon" || this.shapeType === "polyline") {
19595
+ this.points = settings[this.shapeType];
19038
19596
  }
19039
19597
  }
19598
+ this.isEllipse = this.shapeType === "ellipse";
19599
+ this.isCapsule = this.shapeType === "capsule";
19600
+ this.isPoint = this.shapeType === "point";
19601
+ this.isPolygon = this.shapeType === "polygon";
19602
+ this.isPolyLine = this.shapeType === "polyline";
19040
19603
  if (typeof settings.text !== "undefined") {
19041
19604
  this.text = settings.text;
19042
19605
  this.text.font = settings.text.fontfamily || "sans-serif";
@@ -19070,58 +19633,75 @@ var TMXObject = class {
19070
19633
  this.tile = new Tile(this.x, this.y, this.gid, tileset);
19071
19634
  }
19072
19635
  /**
19073
- * parses the TMX shape definition and returns a corresponding array of me.Shape object
19636
+ * parses the TMX shape definition and returns a corresponding array of shape objects
19074
19637
  * @private
19075
- * @returns {Polygon[]|Line[]|Ellipse[]} an array of shape objects
19638
+ * @returns {Polygon[]|Line[]|Ellipse[]|RoundRect[]} an array of shape objects
19076
19639
  */
19077
19640
  parseTMXShapes() {
19078
19641
  const shapes = [];
19079
- if (this.isEllipse === true) {
19080
- shapes.push(
19081
- ellipsePool.get(this.width / 2, this.height / 2, this.width, this.height).rotate(this.rotation)
19082
- );
19083
- } else if (this.isPoint === true) {
19084
- shapes.push(pointPool.get(this.x, this.y));
19085
- } else {
19086
- if (this.isPolygon === true) {
19087
- const _polygon = polygonPool.get(0, 0, this.points);
19088
- const isConvex = _polygon.isConvex();
19642
+ switch (this.shapeType) {
19643
+ case "ellipse":
19644
+ shapes.push(
19645
+ ellipsePool.get(this.width / 2, this.height / 2, this.width, this.height).rotate(this.rotation)
19646
+ );
19647
+ break;
19648
+ case "capsule": {
19649
+ const radius = Math.min(this.width, this.height) / 2;
19650
+ shapes.push(
19651
+ roundedRectanglePool.get(0, 0, this.width, this.height, radius).rotate(this.rotation)
19652
+ );
19653
+ break;
19654
+ }
19655
+ case "point":
19656
+ shapes.push(pointPool.get(this.x, this.y));
19657
+ break;
19658
+ case "polygon": {
19659
+ const polygon = polygonPool.get(0, 0, this.points);
19660
+ const isConvex = polygon.isConvex();
19089
19661
  if (isConvex === true) {
19090
- shapes.push(_polygon.rotate(this.rotation));
19662
+ shapes.push(polygon.rotate(this.rotation));
19091
19663
  } else if (isConvex === false) {
19092
19664
  console.warn(
19093
19665
  "melonJS: concave collision polygon detected, decomposing into convex triangles"
19094
19666
  );
19095
- const indices = _polygon.getIndices();
19096
- const pts = _polygon.points;
19667
+ const indices = polygon.getIndices();
19668
+ const pts = polygon.points;
19097
19669
  for (let t = 0; t < indices.length; t += 3) {
19098
- const tri = polygonPool.get(0, 0, [
19099
- vector2dPool.get(pts[indices[t]].x, pts[indices[t]].y),
19100
- vector2dPool.get(pts[indices[t + 1]].x, pts[indices[t + 1]].y),
19101
- vector2dPool.get(pts[indices[t + 2]].x, pts[indices[t + 2]].y)
19102
- ]);
19103
- shapes.push(tri.rotate(this.rotation));
19670
+ shapes.push(
19671
+ polygonPool.get(0, 0, [
19672
+ vector2dPool.get(pts[indices[t]].x, pts[indices[t]].y),
19673
+ vector2dPool.get(
19674
+ pts[indices[t + 1]].x,
19675
+ pts[indices[t + 1]].y
19676
+ ),
19677
+ vector2dPool.get(
19678
+ pts[indices[t + 2]].x,
19679
+ pts[indices[t + 2]].y
19680
+ )
19681
+ ]).rotate(this.rotation)
19682
+ );
19104
19683
  }
19105
- polygonPool.release(_polygon);
19684
+ polygonPool.release(polygon);
19106
19685
  } else {
19107
19686
  console.warn("melonJS: invalid polygon definition, skipping");
19108
- polygonPool.release(_polygon);
19687
+ polygonPool.release(polygon);
19109
19688
  }
19110
- } else if (this.isPolyLine === true) {
19689
+ break;
19690
+ }
19691
+ case "polyline": {
19111
19692
  const p = this.points;
19112
- let p1;
19113
- let p2;
19114
- const segments = p.length - 1;
19115
- for (let i = 0; i < segments; i++) {
19116
- p1 = vector2dPool.get(p[i].x, p[i].y);
19117
- p2 = vector2dPool.get(p[i + 1].x, p[i + 1].y);
19693
+ for (let i = 0, len = p.length - 1; i < len; i++) {
19694
+ let p1 = vector2dPool.get(p[i].x, p[i].y);
19695
+ let p2 = vector2dPool.get(p[i + 1].x, p[i + 1].y);
19118
19696
  if (this.rotation !== 0) {
19119
19697
  p1 = p1.rotate(this.rotation);
19120
19698
  p2 = p2.rotate(this.rotation);
19121
19699
  }
19122
19700
  shapes.push(linePool.get(0, 0, [p1, p2]));
19123
19701
  }
19124
- } else {
19702
+ break;
19703
+ }
19704
+ default:
19125
19705
  shapes.push(
19126
19706
  polygonPool.get(0, 0, [
19127
19707
  vector2dPool.get(),
@@ -19130,7 +19710,7 @@ var TMXObject = class {
19130
19710
  vector2dPool.get(0, this.height)
19131
19711
  ]).rotate(this.rotation)
19132
19712
  );
19133
- }
19713
+ break;
19134
19714
  }
19135
19715
  if (this.orientation === "isometric") {
19136
19716
  for (let i = 0; i < shapes.length; i++) {
@@ -19162,6 +19742,7 @@ var TMXGroup = class {
19162
19742
  this.objects = [];
19163
19743
  const visible = data2.visible ?? true;
19164
19744
  this.opacity = visible === true ? clamp(+data2.opacity || 1, 0, 1) : 0;
19745
+ this.blendMode = tiledBlendMode(data2.mode);
19165
19746
  applyTMXProperties(this, data2);
19166
19747
  if (data2.objects) {
19167
19748
  const objects = data2.objects;
@@ -19217,12 +19798,15 @@ var TMXGroup = class {
19217
19798
  var TMXTileset = class {
19218
19799
  /**
19219
19800
  * @param {object} tileset - tileset data in JSON format ({@link http://docs.mapeditor.org/en/stable/reference/tmx-map-format/#tileset})
19220
- */
19221
- constructor(tileset) {
19222
- this.TileProperties = [];
19223
- this.imageCollection = [];
19224
- this.firstgid = this.lastgid = +tileset.firstgid;
19225
- if (typeof tileset.source !== "undefined") {
19801
+ * @param {number} [mapTilewidth] - the map's tile grid width in pixels
19802
+ * @param {number} [mapTileheight] - the map's tile grid height in pixels
19803
+ */
19804
+ constructor(tileset, mapTilewidth, mapTileheight) {
19805
+ this.tileProperties = /* @__PURE__ */ new Map();
19806
+ this.imageCollection = /* @__PURE__ */ new Map();
19807
+ this.firstgid = +tileset.firstgid;
19808
+ this.lastgid = this.firstgid;
19809
+ if (tileset.source !== void 0) {
19226
19810
  const src = tileset.source;
19227
19811
  const ext = getExtension(src);
19228
19812
  if (ext === "tsx" || ext === "xml" || ext === "json" || ext === "tsj") {
@@ -19235,54 +19819,45 @@ var TMXTileset = class {
19235
19819
  this.name = tileset.name;
19236
19820
  this.tilewidth = +tileset.tilewidth;
19237
19821
  this.tileheight = +tileset.tileheight;
19238
- this.spacing = +tileset.spacing || 0;
19239
- this.margin = +tileset.margin || 0;
19822
+ this.spacing = +(tileset.spacing ?? 0);
19823
+ this.margin = +(tileset.margin ?? 0);
19240
19824
  this.tileoffset = new Vector2d();
19241
19825
  this.isAnimated = false;
19242
19826
  this.isCollection = false;
19243
- this.class = tileset.class;
19827
+ this.class = tileset.class ?? tileset.type;
19828
+ this.tilerendersize = tileset.tilerendersize ?? "tile";
19829
+ this.fillmode = tileset.fillmode ?? "stretch";
19830
+ this.mapTilewidth = mapTilewidth ?? this.tilewidth;
19831
+ this.mapTileheight = mapTileheight ?? this.tileheight;
19832
+ this._renderScaleX = 1;
19833
+ this._renderScaleY = 1;
19834
+ this._renderDw = this.tilewidth;
19835
+ this._renderDh = this.tileheight;
19836
+ this._renderDyOffset = 0;
19837
+ this._renderDxCenter = 0;
19838
+ this._renderDyCenter = 0;
19839
+ if (this.tilerendersize === "grid") {
19840
+ this._renderScaleX = this.mapTilewidth / this.tilewidth;
19841
+ this._renderScaleY = this.mapTileheight / this.tileheight;
19842
+ if (this.fillmode === "preserve-aspect-fit") {
19843
+ const minScale = Math.min(this._renderScaleX, this._renderScaleY);
19844
+ this._renderScaleX = minScale;
19845
+ this._renderScaleY = minScale;
19846
+ }
19847
+ this._renderDw = this.tilewidth * this._renderScaleX;
19848
+ this._renderDh = this.tileheight * this._renderScaleY;
19849
+ this._renderDyOffset = this.tileheight - this._renderDh;
19850
+ if (this.fillmode === "preserve-aspect-fit") {
19851
+ this._renderDxCenter = (this.mapTilewidth - this._renderDw) / 2;
19852
+ this._renderDyCenter = -(this.mapTileheight - this._renderDh) / 2;
19853
+ }
19854
+ }
19855
+ this.tileSubRects = /* @__PURE__ */ new Map();
19244
19856
  this.animations = /* @__PURE__ */ new Map();
19245
19857
  this._lastUpdate = 0;
19246
- const tiles = tileset.tiles;
19247
- if (tiles) {
19248
- const tileEntries = Array.isArray(tiles) ? tiles : Object.values(tiles);
19249
- for (let t = 0, tLen = tileEntries.length; t < tLen; t++) {
19250
- const tile = tileEntries[t];
19251
- const tileId = +tile.id;
19252
- if (tile.animation) {
19253
- this.isAnimated = true;
19254
- const anim = tile.animation;
19255
- this.animations.set(anim[0].tileid, {
19256
- dt: 0,
19257
- idx: 0,
19258
- frames: anim,
19259
- cur: anim[0]
19260
- });
19261
- }
19262
- if (tile.properties) {
19263
- if (Array.isArray(tile.properties)) {
19264
- const tileProperty = {};
19265
- const props = tile.properties;
19266
- for (let j = 0, pLen = props.length; j < pLen; j++) {
19267
- tileProperty[props[j].name] = props[j].value;
19268
- }
19269
- this.setTileProperty(tileId + this.firstgid, tileProperty);
19270
- } else {
19271
- this.setTileProperty(tileId + this.firstgid, tile.properties);
19272
- }
19273
- }
19274
- if (tile.image) {
19275
- const image = getImage(tile.image);
19276
- if (!image) {
19277
- throw new Error(
19278
- `melonJS: '${tile.image}' file for tile '${tileId + this.firstgid}' not found!`
19279
- );
19280
- }
19281
- this.imageCollection[tileId + this.firstgid] = image;
19282
- }
19283
- }
19284
- }
19285
- this.isCollection = this.imageCollection.length > 0;
19858
+ resolveEmbeddedImage(tileset);
19859
+ this._parseTiles(tileset.tiles);
19860
+ this.isCollection = tileset.isCollection ?? this.imageCollection.size > 0;
19286
19861
  const offset = tileset.tileoffset;
19287
19862
  if (offset) {
19288
19863
  this.tileoffset.x = +offset.x;
@@ -19290,55 +19865,138 @@ var TMXTileset = class {
19290
19865
  }
19291
19866
  const tileInfo = tileset.tileproperties;
19292
19867
  if (tileInfo) {
19293
- const tileIds = Object.keys(tileInfo);
19294
- for (let i = 0, len = tileIds.length; i < len; i++) {
19295
- const id = tileIds[i];
19296
- this.setTileProperty(+id + this.firstgid, tileInfo[id]);
19868
+ for (const [id, props] of Object.entries(tileInfo)) {
19869
+ this.setTileProperty(+id + this.firstgid, props);
19297
19870
  }
19298
19871
  }
19299
- if (this.isCollection === false) {
19300
- this.image = getImage(tileset.image);
19301
- if (!this.image) {
19302
- throw new Error(
19303
- `melonJS: '${tileset.image}' file for tileset '${this.name}' not found!`
19304
- );
19305
- }
19306
- this.texture = renderer.cache.get(this.image, {
19307
- framewidth: this.tilewidth,
19308
- frameheight: this.tileheight,
19309
- margin: this.margin,
19310
- spacing: this.spacing
19872
+ if (!this.isCollection) {
19873
+ this._initAtlas(tileset);
19874
+ }
19875
+ }
19876
+ /**
19877
+ * Parse individual tile entries for animations, properties, and images.
19878
+ * @param {object[]|object} [tiles] - tile entries (array in JSON, object in XML)
19879
+ * @ignore
19880
+ */
19881
+ _parseTiles(tiles) {
19882
+ if (!tiles) {
19883
+ return;
19884
+ }
19885
+ let tileEntries;
19886
+ if (Array.isArray(tiles)) {
19887
+ tileEntries = tiles;
19888
+ } else {
19889
+ tileEntries = Object.entries(tiles).map(([key, value]) => {
19890
+ return { id: key, ...value };
19311
19891
  });
19312
- this.atlas = this.texture.getAtlas();
19313
- const hTileCount = +tileset.columns || Math.round(this.image.width / (this.tilewidth + this.spacing));
19314
- let vTileCount = Math.round(
19315
- this.image.height / (this.tileheight + this.spacing)
19316
- );
19317
- if (tileset.tilecount % hTileCount > 0) {
19318
- ++vTileCount;
19892
+ }
19893
+ for (const tile of tileEntries) {
19894
+ const tileId = +tile.id;
19895
+ if (tile.animation) {
19896
+ this.isAnimated = true;
19897
+ const anim = tile.animation;
19898
+ this.animations.set(tileId, {
19899
+ dt: 0,
19900
+ idx: 0,
19901
+ frames: anim,
19902
+ cur: anim[0]
19903
+ });
19319
19904
  }
19320
- this.lastgid = this.firstgid + (hTileCount * vTileCount - 1 || 0);
19321
- if (tileset.tilecount && this.lastgid - this.firstgid + 1 !== +tileset.tilecount) {
19322
- console.warn(
19323
- `Computed tilecount (${this.lastgid - this.firstgid + 1}) does not match expected tilecount (${tileset.tilecount})`
19324
- );
19905
+ if (tile.properties) {
19906
+ if (Array.isArray(tile.properties)) {
19907
+ const tileProperty = {};
19908
+ for (const prop of tile.properties) {
19909
+ tileProperty[prop.name] = prop.value;
19910
+ }
19911
+ this.setTileProperty(tileId + this.firstgid, tileProperty);
19912
+ } else {
19913
+ this.setTileProperty(tileId + this.firstgid, tile.properties);
19914
+ }
19915
+ }
19916
+ resolveEmbeddedImage(tile);
19917
+ if (tile.image) {
19918
+ const image = getImage(tile.image);
19919
+ if (!image) {
19920
+ throw new Error(
19921
+ `melonJS: '${tile.image}' file for tile '${tileId + this.firstgid}' not found!`
19922
+ );
19923
+ }
19924
+ const hasSubRect = typeof tile.x !== "undefined" || typeof tile.y !== "undefined" || typeof tile.width !== "undefined" || typeof tile.height !== "undefined";
19925
+ if (hasSubRect) {
19926
+ const sx = +(tile.x ?? 0);
19927
+ const sy = +(tile.y ?? 0);
19928
+ const sw = typeof tile.width !== "undefined" ? +tile.width : image.width;
19929
+ const sh = typeof tile.height !== "undefined" ? +tile.height : image.height;
19930
+ this.tileSubRects.set(tileId, {
19931
+ x: sx,
19932
+ y: sy,
19933
+ width: sw,
19934
+ height: sh
19935
+ });
19936
+ if (sx !== 0 || sy !== 0 || sw !== image.width || sh !== image.height) {
19937
+ const canvas = document.createElement("canvas");
19938
+ canvas.width = sw;
19939
+ canvas.height = sh;
19940
+ canvas.getContext("2d").drawImage(image, sx, sy, sw, sh, 0, 0, sw, sh);
19941
+ this.imageCollection.set(tileId + this.firstgid, canvas);
19942
+ } else {
19943
+ this.imageCollection.set(tileId + this.firstgid, image);
19944
+ }
19945
+ } else {
19946
+ this.imageCollection.set(tileId + this.firstgid, image);
19947
+ }
19325
19948
  }
19326
19949
  }
19327
19950
  }
19951
+ /**
19952
+ * Initialize the texture atlas for a spritesheet tileset.
19953
+ * @param {object} tileset - tileset data
19954
+ * @ignore
19955
+ */
19956
+ _initAtlas(tileset) {
19957
+ this.image = getImage(tileset.image);
19958
+ if (!this.image) {
19959
+ throw new Error(
19960
+ `melonJS: '${tileset.image}' file for tileset '${this.name}' not found!`
19961
+ );
19962
+ }
19963
+ this.texture = renderer.cache.get(this.image, {
19964
+ framewidth: this.tilewidth,
19965
+ frameheight: this.tileheight,
19966
+ margin: this.margin,
19967
+ spacing: this.spacing
19968
+ });
19969
+ this.atlas = this.texture.getAtlas();
19970
+ const hTileCount = +tileset.columns || Math.round(this.image.width / (this.tilewidth + this.spacing));
19971
+ let vTileCount = Math.round(
19972
+ this.image.height / (this.tileheight + this.spacing)
19973
+ );
19974
+ if (tileset.tilecount % hTileCount > 0) {
19975
+ ++vTileCount;
19976
+ }
19977
+ this.lastgid = this.firstgid + (hTileCount * vTileCount - 1 || 0);
19978
+ if (tileset.tilecount && this.lastgid - this.firstgid + 1 !== +tileset.tilecount) {
19979
+ console.warn(
19980
+ `Computed tilecount (${this.lastgid - this.firstgid + 1}) does not match expected tilecount (${tileset.tilecount})`
19981
+ );
19982
+ }
19983
+ }
19328
19984
  /**
19329
19985
  * return the tile image from a "Collection of Image" tileset
19330
19986
  * @param {number} gid
19331
- * @returns {Image} corresponding image or undefined
19987
+ * @returns {HTMLImageElement|HTMLCanvasElement|undefined} corresponding image or undefined
19332
19988
  */
19333
19989
  getTileImage(gid) {
19334
- return this.imageCollection[gid];
19990
+ return this.imageCollection.get(gid);
19335
19991
  }
19336
19992
  /**
19337
19993
  * set the tile properties
19994
+ * @param {number} gid - global tile ID
19995
+ * @param {object} prop - property object
19338
19996
  * @ignore
19339
19997
  */
19340
19998
  setTileProperty(gid, prop) {
19341
- this.TileProperties[gid] = prop;
19999
+ this.tileProperties.set(gid, prop);
19342
20000
  }
19343
20001
  /**
19344
20002
  * return true if the gid belongs to the tileset
@@ -19355,29 +20013,34 @@ var TMXTileset = class {
19355
20013
  */
19356
20014
  getViewTileId(gid) {
19357
20015
  const localId = gid - this.firstgid;
19358
- if (this.animations.has(localId)) {
19359
- return this.animations.get(localId).cur.tileid;
20016
+ const anim = this.animations.get(localId);
20017
+ if (anim !== void 0) {
20018
+ return anim.cur.tileid;
19360
20019
  }
19361
20020
  return localId;
19362
20021
  }
19363
20022
  /**
19364
20023
  * return the properties of the specified tile
19365
- * @param {number} tileId
19366
- * @returns {object}
20024
+ * @param {number} tileId - global tile ID
20025
+ * @returns {object|undefined} tile properties or undefined
19367
20026
  */
19368
20027
  getTileProperties(tileId) {
19369
- return this.TileProperties[tileId];
20028
+ return this.tileProperties.get(tileId);
19370
20029
  }
19371
- // update tile animations
20030
+ /**
20031
+ * update tile animations
20032
+ * @param {number} dt - time delta in milliseconds
20033
+ * @returns {boolean} true if any animation frame changed
20034
+ * @ignore
20035
+ */
19372
20036
  update(dt) {
19373
- let duration = 0;
19374
20037
  const now = timer_default.getTime();
19375
20038
  let result = false;
19376
20039
  if (this._lastUpdate !== now) {
19377
20040
  this._lastUpdate = now;
19378
- this.animations.forEach((anim) => {
20041
+ for (const anim of this.animations.values()) {
19379
20042
  anim.dt += dt;
19380
- duration = anim.cur.duration;
20043
+ let duration = anim.cur.duration;
19381
20044
  while (anim.dt >= duration) {
19382
20045
  anim.dt -= duration;
19383
20046
  anim.idx = (anim.idx + 1) % anim.frames.length;
@@ -19385,29 +20048,64 @@ var TMXTileset = class {
19385
20048
  duration = anim.cur.duration;
19386
20049
  result = true;
19387
20050
  }
19388
- });
20051
+ }
19389
20052
  }
19390
20053
  return result;
19391
20054
  }
19392
- // draw the x,y tile
20055
+ /**
20056
+ * draw a tile at the specified position
20057
+ * @param {CanvasRenderer|WebGLRenderer} renderer - a renderer instance
20058
+ * @param {number} dx - destination x position
20059
+ * @param {number} dy - destination y position
20060
+ * @param {Tile} tmxTile - the tile object to draw
20061
+ * @ignore
20062
+ */
19393
20063
  drawTile(renderer2, dx, dy, tmxTile) {
20064
+ let dw, dh;
20065
+ if (this.isCollection) {
20066
+ if (this.tilerendersize === "grid") {
20067
+ let scaleX = this.mapTilewidth / tmxTile.width;
20068
+ let scaleY = this.mapTileheight / tmxTile.height;
20069
+ if (this.fillmode === "preserve-aspect-fit") {
20070
+ const scale2 = Math.min(scaleX, scaleY);
20071
+ scaleX = scale2;
20072
+ scaleY = scale2;
20073
+ }
20074
+ dw = tmxTile.width * scaleX;
20075
+ dh = tmxTile.height * scaleY;
20076
+ dy += this.tileheight - dh;
20077
+ if (this.fillmode === "preserve-aspect-fit") {
20078
+ dx += (this.mapTilewidth - dw) / 2;
20079
+ dy -= (this.mapTileheight - dh) / 2;
20080
+ }
20081
+ } else {
20082
+ dw = tmxTile.width;
20083
+ dh = tmxTile.height;
20084
+ }
20085
+ } else {
20086
+ dw = this._renderDw;
20087
+ dh = this._renderDh;
20088
+ dy += this._renderDyOffset;
20089
+ dx += this._renderDxCenter;
20090
+ dy += this._renderDyCenter;
20091
+ }
19394
20092
  if (tmxTile.flipped) {
19395
20093
  renderer2.save();
19396
20094
  renderer2.translate(dx, dy);
19397
20095
  renderer2.transform(tmxTile.currentTransform);
19398
20096
  dx = dy = 0;
19399
20097
  }
19400
- if (this.isCollection === true) {
20098
+ if (this.isCollection) {
19401
20099
  renderer2.drawImage(
19402
- this.imageCollection[tmxTile.tileId],
20100
+ this.imageCollection.get(tmxTile.tileId),
19403
20101
  0,
19404
20102
  0,
19405
20103
  tmxTile.width,
19406
20104
  tmxTile.height,
19407
20105
  dx,
19408
20106
  dy,
19409
- tmxTile.width,
19410
- tmxTile.height
20107
+ dw,
20108
+ dh
19411
20109
  );
19412
20110
  } else {
19413
20111
  const offset = this.atlas[this.getViewTileId(tmxTile.tileId)].offset;
@@ -19419,8 +20117,8 @@ var TMXTileset = class {
19419
20117
  this.tileheight,
19420
20118
  dx,
19421
20119
  dy,
19422
- this.tilewidth + renderer2.uvOffset,
19423
- this.tileheight + renderer2.uvOffset
20120
+ dw + renderer2.uvOffset,
20121
+ dh + renderer2.uvOffset
19424
20122
  );
19425
20123
  }
19426
20124
  if (tmxTile.flipped) {
@@ -19498,30 +20196,50 @@ function readLayer(map, data2, z) {
19498
20196
  );
19499
20197
  }
19500
20198
  function readImageLayer(map, data2, z) {
20199
+ resolveEmbeddedImage(data2);
19501
20200
  applyTMXProperties(data2.properties, data2);
20201
+ const repX = data2.repeatx === true || data2.repeatx === "1";
20202
+ const repY = data2.repeaty === true || data2.repeaty === "1";
20203
+ let repeat = data2.properties?.repeat;
20204
+ if (typeof repeat === "undefined" && (repX || repY)) {
20205
+ if (repX && repY) {
20206
+ repeat = "repeat";
20207
+ } else if (repX) {
20208
+ repeat = "repeat-x";
20209
+ } else {
20210
+ repeat = "repeat-y";
20211
+ }
20212
+ }
20213
+ const pox = +(map.data.parallaxoriginx ?? 0);
20214
+ const poy = +(map.data.parallaxoriginy ?? 0);
20215
+ const ratioX = +(data2.parallaxx ?? 1);
20216
+ const ratioY = +(data2.parallaxy ?? 1);
20217
+ const ox = +(data2.offsetx ?? data2.x ?? 0) + pox * ratioX;
20218
+ const oy = +(data2.offsety ?? data2.y ?? 0) + poy * ratioY;
19502
20219
  const imageLayer = legacy_pool_default.pull(
19503
20220
  "ImageLayer",
19504
- // x/y is deprecated since 0.15 and replace by offsetx/y
19505
- +data2.offsetx || +data2.x || 0,
19506
- +data2.offsety || +data2.y || 0,
20221
+ ox,
20222
+ oy,
19507
20223
  Object.assign(
19508
20224
  {
19509
20225
  name: data2.name,
19510
20226
  image: data2.image,
19511
- ratio: vector2dPool.get(+data2.parallaxx || 1, +data2.parallaxy || 1),
20227
+ ratio: vector2dPool.get(ratioX, ratioY),
19512
20228
  // convert to melonJS color format (note: this should be done earlier when parsing data)
19513
20229
  tint: typeof data2.tintcolor !== "undefined" ? colorPool.get().parseHex(data2.tintcolor, true) : void 0,
19514
- z
20230
+ z,
20231
+ repeat
19515
20232
  },
19516
20233
  data2.properties
19517
20234
  )
19518
20235
  );
19519
20236
  const visible = data2.visible ?? true;
19520
20237
  imageLayer.setOpacity(visible ? +data2.opacity : 0);
20238
+ imageLayer.blendMode = tiledBlendMode(data2.mode);
19521
20239
  return imageLayer;
19522
20240
  }
19523
- function readTileset(data2) {
19524
- return new TMXTileset(data2);
20241
+ function readTileset(data2, mapTilewidth, mapTileheight) {
20242
+ return new TMXTileset(data2, mapTilewidth, mapTileheight);
19525
20243
  }
19526
20244
  function readObjectGroup(map, data2, z) {
19527
20245
  return new TMXGroup(map, data2, z);
@@ -19545,6 +20263,8 @@ var TMXTileMap = class {
19545
20263
  this.tileheight = +data2.tileheight;
19546
20264
  this.infinite = +data2.infinite || 0;
19547
20265
  this.orientation = data2.orientation;
20266
+ this.skewx = +(data2.skewx ?? 0);
20267
+ this.skewy = +(data2.skewy ?? 0);
19548
20268
  this.renderorder = data2.renderorder || "right-down";
19549
20269
  this.version = "" + data2.version;
19550
20270
  this.tiledversion = "" + data2.tiledversion;
@@ -19614,7 +20334,9 @@ var TMXTileMap = class {
19614
20334
  if (typeof data2.tilesets !== "undefined") {
19615
20335
  const tilesets = data2.tilesets;
19616
20336
  for (let i = 0, len = tilesets.length; i < len; i++) {
19617
- this.tilesets.add(readTileset(tilesets[i]));
20337
+ this.tilesets.add(
20338
+ readTileset(tilesets[i], this.tilewidth, this.tileheight)
20339
+ );
19618
20340
  }
19619
20341
  }
19620
20342
  if (this.background_image) {
@@ -19717,6 +20439,7 @@ var TMXTileMap = class {
19717
20439
  targetContainer.name = group.name;
19718
20440
  targetContainer.pos.z = group.z;
19719
20441
  targetContainer.setOpacity(group.opacity);
20442
+ targetContainer.blendMode = group.blendMode;
19720
20443
  targetContainer.autoSort = false;
19721
20444
  targetContainer.autoDepth = false;
19722
20445
  }
@@ -19795,8 +20518,29 @@ var TMXTileMap = class {
19795
20518
  obj.body.collisionType = collision.types.WORLD_SHAPE;
19796
20519
  obj.body.isStatic = true;
19797
20520
  }
20521
+ if (obj.isRenderable === true && !(settings instanceof TMXLayer)) {
20522
+ if (!settings.visible) {
20523
+ obj.setOpacity(0);
20524
+ if (typeof obj.renderable !== "undefined" && obj.renderable.isRenderable === true) {
20525
+ obj.renderable.setOpacity(0);
20526
+ }
20527
+ } else if (settings.opacity < 1) {
20528
+ obj.setOpacity(obj.getOpacity() * settings.opacity);
20529
+ if (typeof obj.renderable !== "undefined" && obj.renderable.isRenderable === true) {
20530
+ obj.renderable.setOpacity(
20531
+ obj.renderable.getOpacity() * settings.opacity
20532
+ );
20533
+ }
20534
+ }
20535
+ }
19798
20536
  if (flatten !== false) {
19799
20537
  if (obj.isRenderable === true) {
20538
+ if (group.blendMode !== "normal" && obj.blendMode === "normal") {
20539
+ obj.blendMode = group.blendMode;
20540
+ if (typeof obj.renderable !== "undefined" && obj.renderable.isRenderable === true && obj.renderable.blendMode === "normal") {
20541
+ obj.renderable.blendMode = group.blendMode;
20542
+ }
20543
+ }
19800
20544
  obj.setOpacity(obj.getOpacity() * group.opacity);
19801
20545
  if (typeof obj.renderable !== "undefined" && obj.renderable.isRenderable === true) {
19802
20546
  obj.renderable.setOpacity(
@@ -19806,6 +20550,12 @@ var TMXTileMap = class {
19806
20550
  }
19807
20551
  objects.push(obj);
19808
20552
  } else {
20553
+ if (group.blendMode !== "normal" && obj.isRenderable === true && obj.blendMode === "normal") {
20554
+ obj.blendMode = group.blendMode;
20555
+ if (typeof obj.renderable !== "undefined" && obj.renderable.isRenderable === true && obj.renderable.blendMode === "normal") {
20556
+ obj.renderable.blendMode = group.blendMode;
20557
+ }
20558
+ }
19809
20559
  targetContainer.addChild(obj);
19810
20560
  }
19811
20561
  }
@@ -21071,14 +21821,117 @@ var Sprite = class extends Renderable {
21071
21821
  }
21072
21822
  };
21073
21823
 
21074
- // src/camera/camera2d.js
21824
+ // src/camera/camera2d.ts
21075
21825
  var targetV = new Vector2d();
21076
21826
  var Camera2d = class extends Renderable {
21077
21827
  /**
21078
- * @param {number} minX - start x offset
21079
- * @param {number} minY - start y offset
21080
- * @param {number} maxX - end x offset
21081
- * @param {number} maxY - end y offset
21828
+ * Axis definition
21829
+ * NONE no axis
21830
+ * HORIZONTAL horizontal axis only
21831
+ * VERTICAL vertical axis only
21832
+ * BOTH both axis
21833
+ */
21834
+ AXIS;
21835
+ /**
21836
+ * Camera bounds
21837
+ */
21838
+ bounds;
21839
+ /**
21840
+ * enable or disable damping
21841
+ * @default true
21842
+ */
21843
+ smoothFollow;
21844
+ /**
21845
+ * Camera damping for smooth transition [0 .. 1].
21846
+ * 1 being the maximum value and will snap the camera to the target position
21847
+ * @default 1.0
21848
+ */
21849
+ damping;
21850
+ /**
21851
+ * the closest point relative to the camera
21852
+ * @default -1000
21853
+ */
21854
+ near;
21855
+ /**
21856
+ * the furthest point relative to the camera.
21857
+ * @default 1000
21858
+ */
21859
+ far;
21860
+ /**
21861
+ * the x position on the screen where this camera viewport is rendered.
21862
+ * @default 0
21863
+ */
21864
+ screenX;
21865
+ /**
21866
+ * the y position on the screen where this camera viewport is rendered.
21867
+ * @default 0
21868
+ */
21869
+ screenY;
21870
+ /**
21871
+ * @ignore
21872
+ */
21873
+ _zoom;
21874
+ /**
21875
+ * the world-space projection matrix for non-default cameras (offset/zoomed).
21876
+ * Maps world coordinates to the camera's screen viewport.
21877
+ */
21878
+ worldProjection;
21879
+ /**
21880
+ * the screen-space projection matrix for non-default cameras.
21881
+ * Maps coordinates so that (0,0) aligns with the camera's screenX/screenY position.
21882
+ * Used for rendering floating elements (e.g. background layers) in the correct screen area.
21883
+ */
21884
+ screenProjection;
21885
+ /**
21886
+ * Whether this camera should automatically resize when the canvas resizes.
21887
+ * Set to false for non-default cameras with fixed dimensions
21888
+ * (e.g. minimap, split-screen viewports).
21889
+ * @default true
21890
+ */
21891
+ autoResize;
21892
+ /**
21893
+ * cached world view bounds
21894
+ * @ignore
21895
+ */
21896
+ _worldView;
21897
+ /**
21898
+ * the default camera projection matrix
21899
+ * (2d cameras use an orthographic projection by default).
21900
+ */
21901
+ projectionMatrix;
21902
+ /**
21903
+ * the invert camera transform used to unproject points
21904
+ * @ignore
21905
+ */
21906
+ invCurrentTransform;
21907
+ /** offset for shake effect */
21908
+ offset;
21909
+ /** target to follow */
21910
+ target;
21911
+ /** default value follow */
21912
+ follow_axis;
21913
+ /**
21914
+ * shake variables
21915
+ * @ignore
21916
+ */
21917
+ _shake;
21918
+ /**
21919
+ * flash variables
21920
+ * @ignore
21921
+ */
21922
+ _fadeOut;
21923
+ /**
21924
+ * fade variables
21925
+ * @ignore
21926
+ */
21927
+ _fadeIn;
21928
+ /** the camera deadzone */
21929
+ deadzone;
21930
+ /**
21931
+ * @param minX - start x offset
21932
+ * @param minY - start y offset
21933
+ * @param maxX - end x offset
21934
+ * @param maxY - end y offset
21082
21935
  */
21083
21936
  constructor(minX, minY, maxX, maxY) {
21084
21937
  super(minX, minY, maxX - minX, maxY - minY);
@@ -21112,6 +21965,13 @@ var Camera2d = class extends Renderable {
21112
21965
  color: null,
21113
21966
  tween: null
21114
21967
  };
21968
+ this.screenX = 0;
21969
+ this.screenY = 0;
21970
+ this.zoom = 1;
21971
+ this.worldProjection = new Matrix3d();
21972
+ this.screenProjection = new Matrix3d();
21973
+ this._worldView = new Bounds();
21974
+ this.autoResize = true;
21115
21975
  this.name = "default";
21116
21976
  this.setDeadzone(this.width / 6, this.height / 6);
21117
21977
  this.anchorPoint.set(0, 0);
@@ -21161,10 +22021,64 @@ var Camera2d = class extends Renderable {
21161
22021
  return targetY;
21162
22022
  }
21163
22023
  // -- public function ---
22024
+ /**
22025
+ * the zoom level of this camera.
22026
+ * Values less than 1 zoom out (show more of the world),
22027
+ * values greater than 1 zoom in (show less of the world).
22028
+ * @default 1
22029
+ * @example
22030
+ * // zoom out to show the full level in a 180x100 minimap
22031
+ * camera.zoom = Math.min(180 / levelWidth, 100 / levelHeight);
22032
+ */
22033
+ get zoom() {
22034
+ return this._zoom;
22035
+ }
22036
+ set zoom(value) {
22037
+ this._zoom = value > 0 ? value : 1;
22038
+ }
22039
+ /**
22040
+ * Whether this camera is using default settings (no screen offset, no zoom).
22041
+ * Non-default cameras use custom projections for viewport positioning and scaling.
22042
+ * @returns true if this camera has no screen offset and zoom is 1
22043
+ */
22044
+ get isDefault() {
22045
+ return this.screenX === 0 && this.screenY === 0 && this.zoom === 1;
22046
+ }
22047
+ /**
22048
+ * The world-space bounds currently visible through this camera,
22049
+ * taking into account position and zoom level.
22050
+ * @returns the visible world area
22051
+ */
22052
+ get worldView() {
22053
+ this._worldView.setMinMax(
22054
+ this.pos.x,
22055
+ this.pos.y,
22056
+ this.pos.x + this.width / this.zoom,
22057
+ this.pos.y + this.height / this.zoom
22058
+ );
22059
+ return this._worldView;
22060
+ }
22061
+ /**
22062
+ * Set the camera screen position and size in a single call.
22063
+ * @param x - x position on screen
22064
+ * @param y - y position on screen
22065
+ * @param [w] - width (defaults to current width)
22066
+ * @param [h] - height (defaults to current height)
22067
+ * @returns this camera for chaining
22068
+ */
22069
+ setViewport(x, y, w, h) {
22070
+ this.screenX = x;
22071
+ this.screenY = y;
22072
+ if (typeof w !== "undefined" && typeof h !== "undefined") {
22073
+ super.resize(w, h);
22074
+ this._updateProjectionMatrix();
22075
+ }
22076
+ return this;
22077
+ }
21164
22078
  /**
21165
22079
  * reset the camera position to specified coordinates
21166
- * @param {number} [x=0] - initial position of the camera on the x axis
21167
- * @param {number} [y=0] - initial position of the camera on the y axis
22080
+ * @param [x=0] - initial position of the camera on the x axis
22081
+ * @param [y=0] - initial position of the camera on the y axis
21168
22082
  */
21169
22083
  reset(x = 0, y = 0) {
21170
22084
  this.pos.x = x;
@@ -21174,6 +22088,8 @@ var Camera2d = class extends Renderable {
21174
22088
  this.damping = 1;
21175
22089
  this.currentTransform.identity();
21176
22090
  this.invCurrentTransform.identity().invert();
22091
+ this.worldProjection.identity();
22092
+ this.screenProjection.identity();
21177
22093
  this._updateProjectionMatrix();
21178
22094
  }
21179
22095
  /**
@@ -21181,8 +22097,8 @@ var Camera2d = class extends Renderable {
21181
22097
  * the "deadzone" defines an area within the current camera in which
21182
22098
  * the followed renderable can move without scrolling the camera.
21183
22099
  * @see {@link follow}
21184
- * @param {number} w - deadzone width
21185
- * @param {number} h - deadzone height
22100
+ * @param w - deadzone width
22101
+ * @param h - deadzone height
21186
22102
  */
21187
22103
  setDeadzone(w, h) {
21188
22104
  if (typeof this.deadzone === "undefined") {
@@ -21199,11 +22115,14 @@ var Camera2d = class extends Renderable {
21199
22115
  }
21200
22116
  /**
21201
22117
  * resize the camera
21202
- * @param {number} w - new width of the camera
21203
- * @param {number} h - new height of the camera
21204
- * @returns {Camera2d} this camera
22118
+ * @param w - new width of the camera
22119
+ * @param h - new height of the camera
22120
+ * @returns this camera
21205
22121
  */
21206
22122
  resize(w, h) {
22123
+ if (!this.autoResize) {
22124
+ return this;
22125
+ }
21207
22126
  super.resize(w, h);
21208
22127
  this.smoothFollow = false;
21209
22128
  this.setBounds(0, 0, w, h);
@@ -21217,10 +22136,10 @@ var Camera2d = class extends Renderable {
21217
22136
  /**
21218
22137
  * set the camera boundaries (set to the world limit by default).
21219
22138
  * the camera is bound to the given coordinates and cannot move/be scrolled outside of it.
21220
- * @param {number} x - world left limit
21221
- * @param {number} y - world top limit
21222
- * @param {number} w - world width limit
21223
- * @param {number} h - world height limit
22139
+ * @param x - world left limit
22140
+ * @param y - world top limit
22141
+ * @param w - world width limit
22142
+ * @param h - world height limit
21224
22143
  */
21225
22144
  setBounds(x, y, w, h) {
21226
22145
  this.smoothFollow = false;
@@ -21232,9 +22151,9 @@ var Camera2d = class extends Renderable {
21232
22151
  /**
21233
22152
  * set the camera to follow the specified renderable. <br>
21234
22153
  * (this will put the camera center around the given target)
21235
- * @param {Renderable|Vector2d} target - renderable or position vector to follow
21236
- * @param {number} [axis=me.game.viewport.AXIS.BOTH] - Which axis to follow (see {@link Camera2d.AXIS})
21237
- * @param {number} [damping=1] - default damping value
22154
+ * @param target - renderable or position vector to follow
22155
+ * @param [axis=me.game.viewport.AXIS.BOTH] - Which axis to follow (see {@link Camera2d.AXIS})
22156
+ * @param [damping=1] - default damping value
21238
22157
  * @example
21239
22158
  * // set the camera to follow this renderable on both axis, and enable damping
21240
22159
  * me.game.viewport.follow(this, me.game.viewport.AXIS.BOTH, 0.1);
@@ -21267,8 +22186,8 @@ var Camera2d = class extends Renderable {
21267
22186
  /**
21268
22187
  * move the camera upper-left position by the specified offset.
21269
22188
  * @see {@link focusOn}
21270
- * @param {number} x - horizontal offset
21271
- * @param {number} y - vertical offset
22189
+ * @param x - horizontal offset
22190
+ * @param y - vertical offset
21272
22191
  * @example
21273
22192
  * // Move the camera up by four pixels
21274
22193
  * me.game.viewport.move(0, -4);
@@ -21279,8 +22198,8 @@ var Camera2d = class extends Renderable {
21279
22198
  /**
21280
22199
  * move the camera upper-left position to the specified coordinates
21281
22200
  * @see {@link focusOn}
21282
- * @param {number} x
21283
- * @param {number} y
22201
+ * @param x - horizontal position
22202
+ * @param y - vertical position
21284
22203
  */
21285
22204
  moveTo(x, y) {
21286
22205
  const _x = this.pos.x;
@@ -21292,7 +22211,8 @@ var Camera2d = class extends Renderable {
21292
22211
  }
21293
22212
  }
21294
22213
  /** @ignore */
21295
- updateTarget() {
22214
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
22215
+ updateTarget(_dt) {
21296
22216
  if (this.target) {
21297
22217
  targetV.setV(this.pos);
21298
22218
  switch (this.follow_axis) {
@@ -21312,7 +22232,7 @@ var Camera2d = class extends Renderable {
21312
22232
  break;
21313
22233
  }
21314
22234
  if (!this.pos.equals(targetV)) {
21315
- if (this.smoothFollow === true && this.damping < 1) {
22235
+ if (this.smoothFollow && this.damping < 1) {
21316
22236
  if (toBeCloseTo(targetV.x, this.pos.x, 2) && toBeCloseTo(targetV.y, this.pos.y, 2)) {
21317
22237
  this.pos.setV(targetV);
21318
22238
  return;
@@ -21330,7 +22250,7 @@ var Camera2d = class extends Renderable {
21330
22250
  update(dt) {
21331
22251
  this.updateTarget(dt);
21332
22252
  if (this._shake.duration > 0) {
21333
- this._shake.duration -= dt;
22253
+ this._shake.duration -= dt ?? 0;
21334
22254
  if (this._shake.duration <= 0) {
21335
22255
  this._shake.duration = 0;
21336
22256
  this.offset.setZero();
@@ -21347,7 +22267,7 @@ var Camera2d = class extends Renderable {
21347
22267
  }
21348
22268
  this.isDirty = true;
21349
22269
  }
21350
- if (this.isDirty === true) {
22270
+ if (this.isDirty) {
21351
22271
  eventEmitter.emit(VIEWPORT_ONCHANGE, this.pos);
21352
22272
  }
21353
22273
  if (this._fadeIn.tween != null || this._fadeOut.tween != null) {
@@ -21358,16 +22278,16 @@ var Camera2d = class extends Renderable {
21358
22278
  } else {
21359
22279
  this.invCurrentTransform.identity();
21360
22280
  }
21361
- return super.update(dt);
22281
+ return super.update(dt ?? 0);
21362
22282
  }
21363
22283
  /**
21364
22284
  * shake the camera
21365
- * @param {number} intensity - maximum offset that the screen can be moved
22285
+ * @param intensity - maximum offset that the screen can be moved
21366
22286
  * while shaking
21367
- * @param {number} duration - expressed in milliseconds
21368
- * @param {number} [axis=me.game.viewport.AXIS.BOTH] - specify on which axis to apply the shake effect (see {@link Camera2d.AXIS})
21369
- * @param {Function} [onComplete] - callback once shaking effect is over
21370
- * @param {boolean} [force] - if true this will override the current effect
22287
+ * @param duration - expressed in milliseconds
22288
+ * @param [axis=me.game.viewport.AXIS.BOTH] - specify on which axis to apply the shake effect (see {@link Camera2d.AXIS})
22289
+ * @param [onComplete] - callback once shaking effect is over
22290
+ * @param [force] - if true this will override the current effect
21371
22291
  * @example
21372
22292
  * // shake it baby !
21373
22293
  * me.game.viewport.shake(10, 500, me.game.viewport.AXIS.BOTH);
@@ -21383,9 +22303,9 @@ var Camera2d = class extends Renderable {
21383
22303
  /**
21384
22304
  * fadeOut(flash) effect<p>
21385
22305
  * screen is filled with the specified color and slowly goes back to normal
21386
- * @param {Color|string} color - a CSS color value
21387
- * @param {number} [duration=1000] - expressed in milliseconds
21388
- * @param {Function} [onComplete] - callback once effect is over
22306
+ * @param color - a CSS color value
22307
+ * @param [duration=1000] - expressed in milliseconds
22308
+ * @param [onComplete] - callback once effect is over
21389
22309
  * @example
21390
22310
  * // fade the camera to white upon dying, reload the level, and then fade out back
21391
22311
  * me.game.viewport.fadeIn("#fff", 150, function() {
@@ -21396,16 +22316,19 @@ var Camera2d = class extends Renderable {
21396
22316
  */
21397
22317
  fadeOut(color, duration = 1e3, onComplete) {
21398
22318
  this._fadeOut.color = colorPool.get(color);
21399
- this._fadeOut.tween = tweenPool.get(this._fadeOut.color).to({ alpha: 0 }, { duration }).onComplete(onComplete || null);
22319
+ this._fadeOut.tween = tweenPool.get(this._fadeOut.color).to({ alpha: 0 }, { duration });
22320
+ if (onComplete) {
22321
+ this._fadeOut.tween.onComplete(onComplete);
22322
+ }
21400
22323
  this._fadeOut.tween.isPersistent = true;
21401
22324
  this._fadeOut.tween.start();
21402
22325
  }
21403
22326
  /**
21404
22327
  * fadeIn effect <p>
21405
22328
  * fade to the specified color
21406
- * @param {Color|string} color - a CSS color value
21407
- * @param {number} [duration=1000] - expressed in milliseconds
21408
- * @param {Function} [onComplete] - callback once effect is over
22329
+ * @param color - a CSS color value
22330
+ * @param [duration=1000] - expressed in milliseconds
22331
+ * @param [onComplete] - callback once effect is over
21409
22332
  * @example
21410
22333
  * // flash the camera to white for 75ms
21411
22334
  * me.game.viewport.fadeIn("#FFFFFF", 75);
@@ -21414,13 +22337,16 @@ var Camera2d = class extends Renderable {
21414
22337
  this._fadeIn.color = colorPool.get(color);
21415
22338
  const _alpha = this._fadeIn.color.alpha;
21416
22339
  this._fadeIn.color.alpha = 0;
21417
- this._fadeIn.tween = tweenPool.get(this._fadeIn.color).to({ alpha: _alpha }, { duration }).onComplete(onComplete || null);
22340
+ this._fadeIn.tween = tweenPool.get(this._fadeIn.color).to({ alpha: _alpha }, { duration });
22341
+ if (onComplete) {
22342
+ this._fadeIn.tween.onComplete(onComplete);
22343
+ }
21418
22344
  this._fadeIn.tween.isPersistent = true;
21419
22345
  this._fadeIn.tween.start();
21420
22346
  }
21421
22347
  /**
21422
22348
  * set the camera position around the specified object
21423
- * @param {Renderable|Entity|Sprite|NineSliceSprite} target - the renderable to focus the camera on
22349
+ * @param target - the renderable to focus the camera on
21424
22350
  */
21425
22351
  focusOn(target) {
21426
22352
  const bounds = target.getBounds();
@@ -21431,23 +22357,22 @@ var Camera2d = class extends Renderable {
21431
22357
  }
21432
22358
  /**
21433
22359
  * check if the specified renderable is in the camera
21434
- * @param {Renderable|Entity|Sprite|NineSliceSprite} obj - to be checked against
21435
- * @param {boolean} [floating = obj.floating] - if visibility check should be done against screen coordinates
21436
- * @returns {boolean} true if within the viewport
22360
+ * @param obj - to be checked against
22361
+ * @param [floating = obj.floating] - if visibility check should be done against screen coordinates
22362
+ * @returns true if within the viewport
21437
22363
  */
21438
22364
  isVisible(obj, floating = obj.floating) {
21439
- if (floating === true || obj.floating === true) {
22365
+ if (floating || obj.floating) {
21440
22366
  return renderer.overlaps(obj.getBounds());
21441
- } else {
21442
- return obj.getBounds().overlaps(this);
21443
22367
  }
22368
+ return obj.getBounds().overlaps(this.worldView);
21444
22369
  }
21445
22370
  /**
21446
22371
  * convert the given "local" (screen) coordinates into world coordinates
21447
- * @param {number} x - the x coordinate of the local point to be converted
21448
- * @param {number} y - the y coordinate of the local point to be converted
21449
- * @param {Vector2d} [v] - an optional vector object where to set the converted value
21450
- * @returns {Vector2d}
22372
+ * @param x - the x coordinate of the local point to be converted
22373
+ * @param y - the y coordinate of the local point to be converted
22374
+ * @param [v] - an optional vector object where to set the converted value
22375
+ * @returns the converted world coordinates as a Vector2d
21451
22376
  */
21452
22377
  localToWorld(x, y, v) {
21453
22378
  v = v || vector2dPool.get();
@@ -21459,10 +22384,10 @@ var Camera2d = class extends Renderable {
21459
22384
  }
21460
22385
  /**
21461
22386
  * convert the given world coordinates into "local" (screen) coordinates
21462
- * @param {number} x
21463
- * @param {number} y
21464
- * @param {number} [v] - an optional vector object where to set the converted value
21465
- * @returns {Vector2d} a vector with the converted local coordinates
22387
+ * @param x - the x world coordinate to be converted
22388
+ * @param y - the y world coordinate to be converted
22389
+ * @param [v] - an optional vector object where to set the converted value
22390
+ * @returns a vector with the converted local coordinates
21466
22391
  */
21467
22392
  worldToLocal(x, y, v) {
21468
22393
  v = v || vector2dPool.get();
@@ -21477,12 +22402,13 @@ var Camera2d = class extends Renderable {
21477
22402
  * @ignore
21478
22403
  */
21479
22404
  drawFX(renderer2) {
22405
+ const r = renderer2;
21480
22406
  if (this._fadeIn.tween) {
21481
- renderer2.save();
21482
- renderer2.resetTransform();
21483
- renderer2.setColor(this._fadeIn.color);
21484
- renderer2.fillRect(0, 0, this.width, this.height);
21485
- renderer2.restore();
22407
+ r.save();
22408
+ r.resetTransform();
22409
+ r.setColor(this._fadeIn.color);
22410
+ r.fillRect(0, 0, this.width, this.height);
22411
+ r.restore();
21486
22412
  if (this._fadeIn.color.alpha === 1) {
21487
22413
  this._fadeIn.tween = null;
21488
22414
  colorPool.release(this._fadeIn.color);
@@ -21490,11 +22416,11 @@ var Camera2d = class extends Renderable {
21490
22416
  }
21491
22417
  }
21492
22418
  if (this._fadeOut.tween) {
21493
- renderer2.save();
21494
- renderer2.resetTransform();
21495
- renderer2.setColor(this._fadeOut.color);
21496
- renderer2.fillRect(0, 0, this.width, this.height);
21497
- renderer2.restore();
22419
+ r.save();
22420
+ r.resetTransform();
22421
+ r.setColor(this._fadeOut.color);
22422
+ r.fillRect(0, 0, this.width, this.height);
22423
+ r.restore();
21498
22424
  if (this._fadeOut.color.alpha === 0) {
21499
22425
  this._fadeOut.tween = null;
21500
22426
  colorPool.release(this._fadeOut.color);
@@ -21507,17 +22433,56 @@ var Camera2d = class extends Renderable {
21507
22433
  * @ignore
21508
22434
  */
21509
22435
  draw(renderer2, container) {
21510
- const translateX = this.pos.x + this.offset.x;
21511
- const translateY = this.pos.y + this.offset.y;
22436
+ const r = renderer2;
22437
+ const isNonDefault = !this.isDefault;
22438
+ const containerOffsetX = isNonDefault ? container.pos.x : 0;
22439
+ const containerOffsetY = isNonDefault ? container.pos.y : 0;
22440
+ const translateX = this.pos.x + this.offset.x + containerOffsetX;
22441
+ const translateY = this.pos.y + this.offset.y + containerOffsetY;
21512
22442
  container.currentTransform.translate(-translateX, -translateY);
21513
- renderer2.setProjection(this.projectionMatrix);
21514
- renderer2.clipRect(0, 0, this.width, this.height);
21515
- this.preDraw(renderer2);
21516
- container.preDraw(renderer2, this);
21517
- container.draw(renderer2, this);
22443
+ this.preDraw(r);
22444
+ r.clipRect(this.screenX, this.screenY, this.width, this.height);
22445
+ if (isNonDefault) {
22446
+ const left = -this.screenX / this.zoom;
22447
+ const top = -this.screenY / this.zoom;
22448
+ const rw = renderer2.width / this.zoom;
22449
+ const rh = renderer2.height / this.zoom;
22450
+ this.worldProjection.ortho(
22451
+ left,
22452
+ left + rw,
22453
+ top + rh,
22454
+ top,
22455
+ this.near,
22456
+ this.far
22457
+ );
22458
+ renderer2.setProjection(this.worldProjection);
22459
+ this.screenProjection.ortho(
22460
+ -this.screenX,
22461
+ -this.screenX + renderer2.width,
22462
+ -this.screenY + renderer2.height,
22463
+ -this.screenY,
22464
+ this.near,
22465
+ this.far
22466
+ );
22467
+ } else {
22468
+ renderer2.setProjection(this.projectionMatrix);
22469
+ }
22470
+ container.preDraw(r);
22471
+ if (isNonDefault) {
22472
+ const view = this.worldView;
22473
+ const savedWidth = this.width;
22474
+ const savedHeight = this.height;
22475
+ this.width = view.width;
22476
+ this.height = view.height;
22477
+ container.draw(r, this);
22478
+ this.width = savedWidth;
22479
+ this.height = savedHeight;
22480
+ } else {
22481
+ container.draw(r, this);
22482
+ }
21518
22483
  this.drawFX(renderer2);
21519
- container.postDraw(renderer2, this);
21520
- this.postDraw(renderer2);
22484
+ container.postDraw(r);
22485
+ this.postDraw(r);
21521
22486
  container.currentTransform.translate(translateX, translateY);
21522
22487
  }
21523
22488
  };
@@ -21756,7 +22721,7 @@ var DefaultLoadingScreen = class extends Stage {
21756
22721
  };
21757
22722
  var loadingscreen_default = DefaultLoadingScreen;
21758
22723
 
21759
- // src/state/state.js
22724
+ // src/state/state.ts
21760
22725
  var _state = -1;
21761
22726
  var _animFrameId = -1;
21762
22727
  var _isPaused = false;
@@ -21791,14 +22756,18 @@ function _stopRunLoop() {
21791
22756
  globalThis.cancelAnimationFrame(_animFrameId);
21792
22757
  _animFrameId = -1;
21793
22758
  }
21794
- function _switchState(state2) {
22759
+ function _switchState(stateId) {
21795
22760
  _stopRunLoop();
21796
22761
  if (_stages[_state]) {
21797
22762
  _stages[_state].stage.destroy();
21798
22763
  }
21799
- if (_stages[state2]) {
21800
- _state = state2;
21801
- _stages[_state].stage.reset.apply(_stages[_state].stage, _extraArgs);
22764
+ if (_stages[stateId]) {
22765
+ _state = stateId;
22766
+ if (_extraArgs) {
22767
+ _stages[_state].stage.reset(..._extraArgs);
22768
+ } else {
22769
+ _stages[_state].stage.reset();
22770
+ }
21802
22771
  _startRunLoop();
21803
22772
  eventEmitter.emit(STATE_CHANGE);
21804
22773
  if (_onSwitchComplete) {
@@ -21816,80 +22785,47 @@ eventEmitter.addListenerOnce(BOOT, () => {
21816
22785
  var state = {
21817
22786
  /**
21818
22787
  * default state ID for Loading Stage
21819
- * @constant
21820
- * @name LOADING
21821
- * @memberof state
21822
22788
  */
21823
22789
  LOADING: 0,
21824
22790
  /**
21825
22791
  * default state ID for Menu Stage
21826
- * @constant
21827
- * @name MENU
21828
- * @memberof state
21829
22792
  */
21830
22793
  MENU: 1,
21831
22794
  /**
21832
22795
  * default state ID for "Ready" Stage
21833
- * @constant
21834
- * @name READY
21835
- * @memberof state
21836
22796
  */
21837
22797
  READY: 2,
21838
22798
  /**
21839
22799
  * default state ID for Play Stage
21840
- * @constant
21841
- * @name PLAY
21842
- * @memberof state
21843
22800
  */
21844
22801
  PLAY: 3,
21845
22802
  /**
21846
22803
  * default state ID for Game Over Stage
21847
- * @constant
21848
- * @name GAMEOVER
21849
- * @memberof state
21850
22804
  */
21851
22805
  GAMEOVER: 4,
21852
22806
  /**
21853
22807
  * default state ID for Game End Stage
21854
- * @constant
21855
- * @name GAME_END
21856
- * @memberof state
21857
22808
  */
21858
22809
  GAME_END: 5,
21859
22810
  /**
21860
22811
  * default state ID for High Score Stage
21861
- * @constant
21862
- * @name SCORE
21863
- * @memberof state
21864
22812
  */
21865
22813
  SCORE: 6,
21866
22814
  /**
21867
22815
  * default state ID for Credits Stage
21868
- * @constant
21869
- * @name CREDITS
21870
- * @memberof state
21871
22816
  */
21872
22817
  CREDITS: 7,
21873
22818
  /**
21874
22819
  * default state ID for Settings Stage
21875
- * @constant
21876
- * @name SETTINGS
21877
- * @memberof state
21878
22820
  */
21879
22821
  SETTINGS: 8,
21880
22822
  /**
21881
22823
  * default state ID for the default Stage
21882
22824
  * (the default stage is the one running as soon as melonJS is started)
21883
- * @constant
21884
- * @name DEFAULT
21885
- * @memberof state
21886
22825
  */
21887
22826
  DEFAULT: 9,
21888
22827
  /**
21889
22828
  * default state ID for user defined constants<br>
21890
- * @constant
21891
- * @name USER
21892
- * @memberof state
21893
22829
  * @example
21894
22830
  * let STATE_INFO = me.state.USER + 0;
21895
22831
  * let STATE_WARN = me.state.USER + 1;
@@ -21899,16 +22835,13 @@ var state = {
21899
22835
  USER: 100,
21900
22836
  /**
21901
22837
  * Stop the current stage.
21902
- * @name stop
21903
- * @memberof state
21904
- * @public
21905
- * @param {boolean} [pauseTrack=false] - pause current track on screen stop.
22838
+ * @param [shouldPauseTrack=false] - pause current track on screen stop.
21906
22839
  */
21907
- stop(pauseTrack2 = false) {
22840
+ stop(shouldPauseTrack = false) {
21908
22841
  if (_state !== this.LOADING && this.isRunning()) {
21909
22842
  _stopRunLoop();
21910
- if (pauseTrack2 === true) {
21911
- pauseTrack2();
22843
+ if (shouldPauseTrack) {
22844
+ pauseTrack();
21912
22845
  }
21913
22846
  _pauseTime = globalThis.performance.now();
21914
22847
  eventEmitter.emit(STATE_STOP);
@@ -21916,15 +22849,12 @@ var state = {
21916
22849
  },
21917
22850
  /**
21918
22851
  * pause the current stage
21919
- * @name pause
21920
- * @memberof state
21921
- * @public
21922
- * @param {boolean} [music=false] - pause current music track on screen pause
22852
+ * @param [music=false] - pause current music track on screen pause
21923
22853
  */
21924
22854
  pause(music = false) {
21925
22855
  if (_state !== this.LOADING && !this.isPaused()) {
21926
22856
  _pauseRunLoop();
21927
- if (music === true) {
22857
+ if (music) {
21928
22858
  pauseTrack();
21929
22859
  }
21930
22860
  _pauseTime = globalThis.performance.now();
@@ -21933,15 +22863,12 @@ var state = {
21933
22863
  },
21934
22864
  /**
21935
22865
  * Restart the current stage from a full stop.
21936
- * @name restart
21937
- * @memberof state
21938
- * @public
21939
- * @param {boolean} [music=false] - resume current music track on screen resume
22866
+ * @param [music=false] - resume current music track on screen resume
21940
22867
  */
21941
22868
  restart(music = false) {
21942
22869
  if (!this.isRunning()) {
21943
22870
  _startRunLoop();
21944
- if (music === true) {
22871
+ if (music) {
21945
22872
  resumeTrack();
21946
22873
  }
21947
22874
  _pauseTime = globalThis.performance.now() - _pauseTime;
@@ -21950,15 +22877,12 @@ var state = {
21950
22877
  },
21951
22878
  /**
21952
22879
  * resume the current stage
21953
- * @name resume
21954
- * @memberof state
21955
- * @public
21956
- * @param {boolean} [music=false] - resume current music track on screen resume
22880
+ * @param [music=false] - resume current music track on screen resume
21957
22881
  */
21958
22882
  resume(music = false) {
21959
22883
  if (this.isPaused()) {
21960
22884
  _resumeRunLoop();
21961
- if (music === true) {
22885
+ if (music) {
21962
22886
  resumeTrack();
21963
22887
  }
21964
22888
  _pauseTime = globalThis.performance.now() - _pauseTime;
@@ -21967,32 +22891,23 @@ var state = {
21967
22891
  },
21968
22892
  /**
21969
22893
  * return the running state of the state manager
21970
- * @name isRunning
21971
- * @memberof state
21972
- * @public
21973
- * @returns {boolean} true if a "process is running"
22894
+ * @returns true if a "process is running"
21974
22895
  */
21975
22896
  isRunning() {
21976
22897
  return _animFrameId !== -1;
21977
22898
  },
21978
22899
  /**
21979
22900
  * Return the pause state of the state manager
21980
- * @name isPaused
21981
- * @memberof state
21982
- * @public
21983
- * @returns {boolean} true if the game is paused
22901
+ * @returns true if the game is paused
21984
22902
  */
21985
22903
  isPaused() {
21986
22904
  return _isPaused;
21987
22905
  },
21988
22906
  /**
21989
22907
  * associate the specified state with a Stage
21990
- * @name set
21991
- * @memberof state
21992
- * @public
21993
- * @param {number} state - State ID (see constants)
21994
- * @param {Stage} stage - Instantiated Stage to associate with state ID
21995
- * @param {boolean} [start = false] - if true the state will be changed immediately after adding it.
22908
+ * @param stateId - State ID (see constants)
22909
+ * @param stage - Instantiated Stage to associate with state ID
22910
+ * @param [start = false] - if true the state will be changed immediately after adding it.
21996
22911
  * @example
21997
22912
  * class MenuButton extends me.GUI_Object {
21998
22913
  * onClick() {
@@ -22030,29 +22945,27 @@ var state = {
22030
22945
  *
22031
22946
  * me.state.set(me.state.MENU, new MenuScreen());
22032
22947
  */
22033
- set(state2, stage, start = false) {
22948
+ set(stateId, stage, start = false) {
22034
22949
  if (!(stage instanceof Stage)) {
22035
- throw new Error(stage + " is not an instance of me.Stage");
22950
+ throw new Error(`${String(stage)} is not an instance of me.Stage`);
22036
22951
  }
22037
- _stages[state2] = {};
22038
- _stages[state2].stage = stage;
22039
- _stages[state2].transition = true;
22040
- if (start === true) {
22041
- this.change(state2);
22952
+ _stages[stateId] = {
22953
+ stage,
22954
+ transition: true
22955
+ };
22956
+ if (start) {
22957
+ this.change(stateId);
22042
22958
  }
22043
22959
  },
22044
22960
  /**
22045
22961
  * returns the stage associated with the specified state
22046
22962
  * (or the current one if none is specified)
22047
- * @name set
22048
- * @memberof state
22049
- * @public
22050
- * @param {number} [state] - State ID (see constants)
22051
- * @returns {Stage}
22963
+ * @param [stateId] - State ID (see constants)
22964
+ * @returns the Stage instance associated with the given state ID, or undefined
22052
22965
  */
22053
- get(state2 = _state) {
22054
- if (typeof _stages[state2] !== "undefined") {
22055
- return _stages[state2].stage;
22966
+ get(stateId = _state) {
22967
+ if (typeof _stages[stateId] !== "undefined") {
22968
+ return _stages[stateId].stage;
22056
22969
  } else {
22057
22970
  return void 0;
22058
22971
  }
@@ -22060,22 +22973,16 @@ var state = {
22060
22973
  /**
22061
22974
  * return a reference to the current stage<br>
22062
22975
  * useful to call a object specific method
22063
- * @name current
22064
- * @memberof state
22065
- * @public
22066
- * @returns {Stage}
22976
+ * @returns the current Stage instance, or undefined if no stage is active
22067
22977
  */
22068
22978
  current() {
22069
22979
  return this.get();
22070
22980
  },
22071
22981
  /**
22072
22982
  * specify a global transition effect
22073
- * @name transition
22074
- * @memberof state
22075
- * @public
22076
- * @param {string} effect - (only "fade" is supported for now)
22077
- * @param {Color|string} color - a CSS color value
22078
- * @param {number} [duration=1000] - expressed in milliseconds
22983
+ * @param effect - (only "fade" is supported for now)
22984
+ * @param color - a CSS color value
22985
+ * @param [duration=1000] - expressed in milliseconds
22079
22986
  */
22080
22987
  transition(effect, color, duration) {
22081
22988
  if (effect === "fade") {
@@ -22085,63 +22992,63 @@ var state = {
22085
22992
  },
22086
22993
  /**
22087
22994
  * enable/disable the transition to a particular state (by default enabled for all)
22088
- * @name setTransition
22089
- * @memberof state
22090
- * @public
22091
- * @param {number} state - State ID (see constants)
22092
- * @param {boolean} enable
22995
+ * @param stateId - State ID (see constants)
22996
+ * @param enable - true to enable transition, false to disable
22093
22997
  */
22094
- setTransition(state2, enable2) {
22095
- _stages[state2].transition = enable2;
22998
+ setTransition(stateId, enable2) {
22999
+ _stages[stateId].transition = enable2;
22096
23000
  },
22097
23001
  /**
22098
23002
  * change the game/app state
22099
- * @name change
22100
- * @memberof state
22101
- * @public
22102
- * @param {number} state - State ID (see constants)
22103
- * @param {boolean} [forceChange=false] - if true the state will be changed immediately (without waiting for the next frame)
22104
- * @param {...*} [args] - extra arguments to be passed to the reset functions
23003
+ * @param stateId - State ID (see constants)
23004
+ * @param [forceChange=false] - if true the state will be changed immediately (without waiting for the next frame)
23005
+ * @param extraArgs - extra arguments to be passed to the reset functions
22105
23006
  * @example
22106
23007
  * // The onResetEvent method on the play screen will receive two args:
22107
23008
  * // "level_1" and the number 3
22108
23009
  * me.state.change(me.state.PLAY, "level_1", 3);
22109
23010
  */
22110
- change(state2, forceChange = false) {
22111
- if (typeof _stages[state2] === "undefined") {
22112
- throw new Error("Undefined Stage for state '" + state2 + "'");
23011
+ change(stateId, forceChange = false, ...extraArgs) {
23012
+ if (typeof _stages[stateId] === "undefined") {
23013
+ throw new Error(`Undefined Stage for state '${stateId}'`);
22113
23014
  }
22114
- if (!this.isCurrent(state2)) {
22115
- _extraArgs = null;
22116
- if (arguments.length > 1) {
22117
- _extraArgs = Array.prototype.slice.call(arguments, 1);
22118
- }
22119
- if (_fade.duration && _stages[state2].transition) {
23015
+ if (!this.isCurrent(stateId)) {
23016
+ _extraArgs = extraArgs.length > 0 ? extraArgs : null;
23017
+ if (_fade.duration && _stages[stateId].transition) {
22120
23018
  _onSwitchComplete = () => {
22121
23019
  game.viewport.fadeOut(_fade.color, _fade.duration);
22122
23020
  };
22123
- game.viewport.fadeIn(_fade.color, _fade.duration, function() {
22124
- defer(_switchState, this, state2);
22125
- });
23021
+ game.viewport.fadeIn(
23022
+ _fade.color,
23023
+ _fade.duration,
23024
+ function() {
23025
+ defer(
23026
+ _switchState,
23027
+ this,
23028
+ stateId
23029
+ );
23030
+ }
23031
+ );
22126
23032
  } else {
22127
- if (forceChange === true) {
22128
- _switchState(state2);
23033
+ if (forceChange) {
23034
+ _switchState(stateId);
22129
23035
  } else {
22130
- defer(_switchState, this, state2);
23036
+ defer(
23037
+ _switchState,
23038
+ this,
23039
+ stateId
23040
+ );
22131
23041
  }
22132
23042
  }
22133
23043
  }
22134
23044
  },
22135
23045
  /**
22136
23046
  * return true if the specified state is the current one
22137
- * @name isCurrent
22138
- * @memberof state
22139
- * @public
22140
- * @param {number} state - State ID (see constants)
22141
- * @returns {boolean} true if the specified state is the current one
23047
+ * @param stateId - State ID (see constants)
23048
+ * @returns true if the specified state is the current one
22142
23049
  */
22143
- isCurrent(state2) {
22144
- return _state === state2;
23050
+ isCurrent(stateId) {
23051
+ return _state === stateId;
22145
23052
  }
22146
23053
  };
22147
23054
  var state_default = state;
@@ -22357,9 +23264,142 @@ var Timer = class {
22357
23264
  var timer = new Timer();
22358
23265
  var timer_default = timer;
22359
23266
 
22360
- // src/input/pointer.js
23267
+ // src/input/pointer.ts
22361
23268
  var tmpVec = new Vector2d();
22362
23269
  var Pointer = class extends Bounds {
23270
+ /**
23271
+ * constant for left button
23272
+ */
23273
+ LEFT;
23274
+ /**
23275
+ * constant for middle button
23276
+ */
23277
+ MIDDLE;
23278
+ /**
23279
+ * constant for right button
23280
+ */
23281
+ RIGHT;
23282
+ /**
23283
+ * the originating Event Object
23284
+ * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent}
23285
+ * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/TouchEvent}
23286
+ * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent}
23287
+ */
23288
+ event;
23289
+ /**
23290
+ * a string containing the event's type.
23291
+ * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Event/type}
23292
+ */
23293
+ type = "";
23294
+ /**
23295
+ * the button property indicates which button was pressed on the mouse to trigger the event.
23296
+ * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/button}
23297
+ */
23298
+ button;
23299
+ /**
23300
+ * indicates whether or not the pointer device that created the event is the primary pointer.
23301
+ * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/isPrimary}
23302
+ */
23303
+ isPrimary;
23304
+ /**
23305
+ * the horizontal coordinate at which the event occurred, relative to the left edge of the entire document.
23306
+ * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/pageX}
23307
+ */
23308
+ pageX;
23309
+ /**
23310
+ * the vertical coordinate at which the event occurred, relative to the left edge of the entire document.
23311
+ * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/pageY}
23312
+ */
23313
+ pageY;
23314
+ /**
23315
+ * the horizontal coordinate within the application's client area at which the event occurred
23316
+ * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/clientX}
23317
+ */
23318
+ clientX;
23319
+ /**
23320
+ * the vertical coordinate within the application's client area at which the event occurred
23321
+ * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/clientY}
23322
+ */
23323
+ clientY;
23324
+ /**
23325
+ * the difference in the X coordinate of the pointer since the previous move event
23326
+ * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/movementX}
23327
+ */
23328
+ movementX;
23329
+ /**
23330
+ * the difference in the Y coordinate of the pointer since the previous move event
23331
+ * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/movementY}
23332
+ */
23333
+ movementY;
23334
+ /**
23335
+ * an unsigned long representing the unit of the delta values scroll amount
23336
+ * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/WheelEvent/deltaMode}
23337
+ */
23338
+ deltaMode;
23339
+ /**
23340
+ * a double representing the horizontal scroll amount in the Wheel Event deltaMode unit.
23341
+ * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/WheelEvent/deltaX}
23342
+ */
23343
+ deltaX;
23344
+ /**
23345
+ * a double representing the vertical scroll amount in the Wheel Event deltaMode unit.
23346
+ * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/WheelEvent/deltaY}
23347
+ */
23348
+ deltaY;
23349
+ /**
23350
+ * a double representing the scroll amount in the z-axis, in the Wheel Event deltaMode unit.
23351
+ * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/WheelEvent/deltaZ}
23352
+ */
23353
+ deltaZ;
23354
+ /**
23355
+ * Event normalized X coordinate within the game canvas itself<br>
23356
+ * <img src="images/event_coord.png"/>
23357
+ */
23358
+ gameX;
23359
+ /**
23360
+ * Event normalized Y coordinate within the game canvas itself<br>
23361
+ * <img src="images/event_coord.png"/>
23362
+ */
23363
+ gameY;
23364
+ /**
23365
+ * Event X coordinate relative to the viewport
23366
+ */
23367
+ gameScreenX;
23368
+ /**
23369
+ * Event Y coordinate relative to the viewport
23370
+ */
23371
+ gameScreenY;
23372
+ /**
23373
+ * Event X coordinate relative to the map
23374
+ */
23375
+ gameWorldX;
23376
+ /**
23377
+ * Event Y coordinate relative to the map
23378
+ */
23379
+ gameWorldY;
23380
+ /**
23381
+ * Event X coordinate relative to the holding container
23382
+ */
23383
+ gameLocalX;
23384
+ /**
23385
+ * Event Y coordinate relative to the holding container
23386
+ */
23387
+ gameLocalY;
23388
+ /**
23389
+ * The unique identifier of the contact for a touch, mouse or pen
23390
+ * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/pointerId}
23391
+ */
23392
+ pointerId;
23393
+ /**
23394
+ * true if not originally a pointer event
23395
+ */
23396
+ isNormalized;
23397
+ /**
23398
+ * true if the pointer is currently locked
23399
+ */
23400
+ locked;
23401
+ // bind list for mouse buttons
23402
+ bind;
22363
23403
  /**
22364
23404
  * @ignore
22365
23405
  */
@@ -22370,7 +23410,7 @@ var Pointer = class extends Bounds {
22370
23410
  this.MIDDLE = 1;
22371
23411
  this.RIGHT = 2;
22372
23412
  this.event = void 0;
22373
- this.type = void 0;
23413
+ this.type = "";
22374
23414
  this.button = 0;
22375
23415
  this.isPrimary = false;
22376
23416
  this.pageX = 0;
@@ -22398,13 +23438,12 @@ var Pointer = class extends Bounds {
22398
23438
  }
22399
23439
  /**
22400
23440
  * initialize the Pointer object using the given Event Object
22401
- * @private
22402
- * @param {Event} event - the original Event object
22403
- * @param {number} [pageX=0] - the horizontal coordinate at which the event occurred, relative to the left edge of the entire document
22404
- * @param {number} [pageY=0] - the vertical coordinate at which the event occurred, relative to the left edge of the entire document
22405
- * @param {number} [clientX=0] - the horizontal coordinate within the application's client area at which the event occurred
22406
- * @param {number} [clientY=0] - the vertical coordinate within the application's client area at which the event occurred
22407
- * @param {number} [pointerId=1] - the Pointer, Touch or Mouse event Id (1)
23441
+ * @param event - the original Event object
23442
+ * @param pageX - the horizontal coordinate at which the event occurred, relative to the left edge of the entire document
23443
+ * @param pageY - the vertical coordinate at which the event occurred, relative to the left edge of the entire document
23444
+ * @param clientX - the horizontal coordinate within the application's client area at which the event occurred
23445
+ * @param clientY - the vertical coordinate within the application's client area at which the event occurred
23446
+ * @param pointerId - the Pointer, Touch or Mouse event Id (1)
22408
23447
  */
22409
23448
  setEvent(event, pageX = 0, pageY = 0, clientX = 0, clientY = 0, pointerId = 1) {
22410
23449
  this.event = event;
@@ -22439,7 +23478,7 @@ var Pointer = class extends Bounds {
22439
23478
  }
22440
23479
  this.gameWorldX = tmpVec.x;
22441
23480
  this.gameWorldY = tmpVec.y;
22442
- if (this.isNormalized === false) {
23481
+ if (!this.isNormalized) {
22443
23482
  this.width = event.width || 1;
22444
23483
  this.height = event.height || 1;
22445
23484
  } else if (typeof event.radiusX === "number") {
@@ -22452,7 +23491,7 @@ var Pointer = class extends Bounds {
22452
23491
  };
22453
23492
  var pointer_default = Pointer;
22454
23493
 
22455
- // src/input/pointerevent.js
23494
+ // src/input/pointerevent.ts
22456
23495
  var T_POINTERS = [];
22457
23496
  var eventHandlers = /* @__PURE__ */ new Map();
22458
23497
  var currentPointer;
@@ -22511,7 +23550,7 @@ function registerEventListener(eventList, callback) {
22511
23550
  for (let x = 0; x < eventList.length; x++) {
22512
23551
  if (POINTER_MOVE.indexOf(eventList[x]) === -1) {
22513
23552
  pointerEventTarget.addEventListener(eventList[x], callback, {
22514
- passive: preventDefault === false
23553
+ passive: !preventDefault
22515
23554
  });
22516
23555
  }
22517
23556
  }
@@ -22522,7 +23561,7 @@ function enablePointerEvent() {
22522
23561
  for (let v = 0; v < maxTouchPoints; v++) {
22523
23562
  T_POINTERS.push(new pointer_default());
22524
23563
  }
22525
- if (pointerEventTarget === null) {
23564
+ if (pointerEventTarget === null || pointerEventTarget === void 0) {
22526
23565
  pointerEventTarget = renderer.getCanvas();
22527
23566
  }
22528
23567
  if (pointerEvent) {
@@ -22537,7 +23576,7 @@ function enablePointerEvent() {
22537
23576
  if (typeof throttlingInterval === "undefined") {
22538
23577
  throttlingInterval = ~~(1e3 / timer_default.maxfps);
22539
23578
  }
22540
- if (autoFocus === true) {
23579
+ if (autoFocus) {
22541
23580
  focus();
22542
23581
  pointerEventTarget.addEventListener(
22543
23582
  activeEventList[2],
@@ -22545,7 +23584,7 @@ function enablePointerEvent() {
22545
23584
  () => {
22546
23585
  focus();
22547
23586
  },
22548
- { passive: preventDefault === false }
23587
+ { passive: !preventDefault }
22549
23588
  );
22550
23589
  }
22551
23590
  const events = findAllActiveEvents(activeEventList, POINTER_MOVE);
@@ -22565,7 +23604,10 @@ function enablePointerEvent() {
22565
23604
  if (activeEventList.indexOf(events[i]) !== -1) {
22566
23605
  pointerEventTarget.addEventListener(
22567
23606
  events[i],
22568
- throttle(onMoveEvent, throttlingInterval),
23607
+ throttle(
23608
+ onMoveEvent,
23609
+ throttlingInterval
23610
+ ),
22569
23611
  { passive: true }
22570
23612
  // do not preventDefault on Move events
22571
23613
  );
@@ -22606,7 +23648,7 @@ function findAllActiveEvents(activeEventList2, eventTypes) {
22606
23648
  }
22607
23649
  function triggerEvent(handlers, type, pointer2, pointerId) {
22608
23650
  let callback;
22609
- if (handlers.callbacks[type]) {
23651
+ if (type && handlers.callbacks[type]) {
22610
23652
  handlers.pointerId = pointerId;
22611
23653
  for (let i = handlers.callbacks[type].length - 1; i >= 0 && (callback = handlers.callbacks[type][i]); i--) {
22612
23654
  if (callback(pointer2) === false) {
@@ -22621,7 +23663,7 @@ function dispatchEvent(normalizedEvents2) {
22621
23663
  while (normalizedEvents2.length > 0) {
22622
23664
  const pointer2 = normalizedEvents2.pop();
22623
23665
  T_POINTERS.push(pointer2);
22624
- if (pointer2.isNormalized === true && typeof pointer2.event.timeStamp !== "undefined") {
23666
+ if (pointer2.isNormalized && typeof pointer2.event.timeStamp !== "undefined") {
22625
23667
  if (pointer2.event.timeStamp < lastTimeStamp) {
22626
23668
  continue;
22627
23669
  }
@@ -22636,7 +23678,8 @@ function dispatchEvent(normalizedEvents2) {
22636
23678
  }
22637
23679
  let candidates = game.world.broadphase.retrieve(
22638
23680
  currentPointer,
22639
- game.world._sortReverseZ
23681
+ (a, b) => game.world._sortReverseZ(a, b),
23682
+ void 0
22640
23683
  );
22641
23684
  candidates = candidates.concat([game.viewport]);
22642
23685
  for (let c = candidates.length, candidate; c--, candidate = candidates[c]; ) {
@@ -22714,7 +23757,12 @@ function dispatchEvent(normalizedEvents2) {
22714
23757
  break;
22715
23758
  default:
22716
23759
  if (eventInBounds) {
22717
- if (triggerEvent(handlers, pointer2.type, pointer2, pointer2.pointerId)) {
23760
+ if (triggerEvent(
23761
+ handlers,
23762
+ pointer2.type,
23763
+ pointer2,
23764
+ pointer2.pointerId
23765
+ )) {
22718
23766
  handled = true;
22719
23767
  break;
22720
23768
  }
@@ -22722,7 +23770,7 @@ function dispatchEvent(normalizedEvents2) {
22722
23770
  break;
22723
23771
  }
22724
23772
  }
22725
- if (handled === true) {
23773
+ if (handled) {
22726
23774
  break;
22727
23775
  }
22728
23776
  }
@@ -22771,7 +23819,7 @@ function onPointerEvent(e) {
22771
23819
  normalizeEvent(e);
22772
23820
  const button = normalizedEvents[0].button;
22773
23821
  if (dispatchEvent(normalizedEvents) || e.type === "wheel") {
22774
- if (preventDefault === true) {
23822
+ if (preventDefault) {
22775
23823
  e.preventDefault();
22776
23824
  }
22777
23825
  }
@@ -22806,12 +23854,12 @@ function globalToLocal(x, y, v) {
22806
23854
  function setTouchAction(element, value) {
22807
23855
  element.style["touch-action"] = value || "none";
22808
23856
  }
22809
- function bindPointer() {
22810
- const button = arguments.length < 2 ? pointer.LEFT : arguments[0];
22811
- const keyCode = arguments.length < 2 ? arguments[0] : arguments[1];
23857
+ function bindPointer(...args) {
23858
+ const button = args.length < 2 ? pointer.LEFT : args[0];
23859
+ const keyCode = args.length < 2 ? args[0] : args[1];
22812
23860
  enablePointerEvent();
22813
23861
  if (!getBindingKey(keyCode)) {
22814
- throw new Error("no action defined for keycode " + keyCode);
23862
+ throw new Error(`no action defined for keycode ${keyCode}`);
22815
23863
  }
22816
23864
  pointer.bind[button] = keyCode;
22817
23865
  }
@@ -22821,11 +23869,11 @@ function unbindPointer(button) {
22821
23869
  function registerPointerEvent(eventType, region, callback) {
22822
23870
  enablePointerEvent();
22823
23871
  if (pointerEventList.indexOf(eventType) === -1) {
22824
- throw new Error("invalid event type : " + eventType);
23872
+ throw new Error(`invalid event type : ${eventType}`);
22825
23873
  }
22826
23874
  if (typeof region === "undefined") {
22827
23875
  throw new Error(
22828
- "registerPointerEvent: region for " + toString(region) + " event is undefined "
23876
+ `registerPointerEvent: region for ${String(region)} event is undefined `
22829
23877
  );
22830
23878
  }
22831
23879
  const eventTypes = findAllActiveEvents(
@@ -22851,7 +23899,7 @@ function registerPointerEvent(eventType, region, callback) {
22851
23899
  }
22852
23900
  function releasePointerEvent(eventType, region, callback) {
22853
23901
  if (pointerEventList.indexOf(eventType) === -1) {
22854
- throw new Error("invalid event type : " + eventType);
23902
+ throw new Error(`invalid event type : ${eventType}`);
22855
23903
  }
22856
23904
  const eventTypes = findAllActiveEvents(
22857
23905
  activeEventList,
@@ -22889,7 +23937,7 @@ function releaseAllPointerEvents(region) {
22889
23937
  function requestPointerLock() {
22890
23938
  if (hasPointerLockSupport) {
22891
23939
  const element = game.getParentElement();
22892
- element.requestPointerLock();
23940
+ void element.requestPointerLock();
22893
23941
  return true;
22894
23942
  }
22895
23943
  return false;
@@ -23218,7 +24266,19 @@ var SAT_LOOKUP = {
23218
24266
  PolygonPolygon: testPolygonPolygon,
23219
24267
  PolygonEllipse: testPolygonEllipse,
23220
24268
  EllipsePolygon: testEllipsePolygon,
23221
- EllipseEllipse: testEllipseEllipse
24269
+ EllipseEllipse: testEllipseEllipse,
24270
+ RoundRectRoundRect: testPolygonPolygon,
24271
+ RoundRectPolygon: testPolygonPolygon,
24272
+ PolygonRoundRect: testPolygonPolygon,
24273
+ RoundRectEllipse: testPolygonEllipse,
24274
+ EllipseRoundRect: testEllipsePolygon,
24275
+ RectangleRectangle: testPolygonPolygon,
24276
+ RectanglePolygon: testPolygonPolygon,
24277
+ PolygonRectangle: testPolygonPolygon,
24278
+ RectangleEllipse: testPolygonEllipse,
24279
+ EllipseRectangle: testEllipsePolygon,
24280
+ RectangleRoundRect: testPolygonPolygon,
24281
+ RoundRectRectangle: testPolygonPolygon
23222
24282
  };
23223
24283
  var dummyObj = {
23224
24284
  pos: new Vector2d(0, 0),
@@ -23831,6 +24891,85 @@ var primitive_default = "varying vec4 vColor;\n\nvoid main(void) {\n gl_FragC
23831
24891
  // src/video/webgl/shaders/primitive.vert
23832
24892
  var primitive_default2 = "// Current vertex point\nattribute vec2 aVertex;\n// Per-vertex normal for line expansion\nattribute vec2 aNormal;\nattribute vec4 aColor;\n\n// Projection matrix\nuniform mat4 uProjectionMatrix;\n// Line width for stroke expansion\nuniform float uLineWidth;\n\nvarying vec4 vColor;\n\nvoid main(void) {\n // Expand vertex position along the normal by half the line width\n vec2 position = aVertex + aNormal * uLineWidth * 0.5;\n // Transform the vertex position by the projection matrix\n gl_Position = uProjectionMatrix * vec4(position, 0.0, 1.0);\n // Pass the remaining attributes to the fragment shader\n vColor = vec4(aColor.bgr * aColor.a, aColor.a);\n}\n";
23833
24893
 
24894
+ // src/video/webgl/buffer/index.js
24895
+ var IndexBuffer = class {
24896
+ /**
24897
+ * @param {WebGLRenderingContext|WebGL2RenderingContext} gl - the WebGL context
24898
+ * @param {number} maxIndices - maximum number of indices this buffer can hold
24899
+ * @param {boolean} [useUint32=false] - use Uint32 indices (WebGL2) instead of Uint16 (WebGL1)
24900
+ * @param {boolean} [dynamic=false] - if true, use STREAM_DRAW for frequent updates; if false, use STATIC_DRAW
24901
+ */
24902
+ constructor(gl, maxIndices, useUint32 = false, dynamic = false) {
24903
+ this.gl = gl;
24904
+ this.dynamic = dynamic;
24905
+ if (useUint32) {
24906
+ this.type = gl.UNSIGNED_INT;
24907
+ this.data = new Uint32Array(maxIndices);
24908
+ } else {
24909
+ this.type = gl.UNSIGNED_SHORT;
24910
+ this.data = new Uint16Array(maxIndices);
24911
+ }
24912
+ this.length = 0;
24913
+ this.buffer = gl.createBuffer();
24914
+ }
24915
+ /**
24916
+ * Fill the buffer with a repeating quad index pattern [0,1,2, 2,1,3, 4,5,6, ...]
24917
+ * and upload as a static buffer.
24918
+ * @param {number} maxQuads - number of quads to generate indices for
24919
+ */
24920
+ fillQuadPattern(maxQuads) {
24921
+ for (let i = 0, vertex = 0; i < maxQuads * 6; i += 6, vertex += 4) {
24922
+ this.data[i] = vertex;
24923
+ this.data[i + 1] = vertex + 1;
24924
+ this.data[i + 2] = vertex + 2;
24925
+ this.data[i + 3] = vertex + 2;
24926
+ this.data[i + 4] = vertex + 1;
24927
+ this.data[i + 5] = vertex + 3;
24928
+ }
24929
+ this.length = maxQuads * 6;
24930
+ this.bind();
24931
+ this.gl.bufferData(
24932
+ this.gl.ELEMENT_ARRAY_BUFFER,
24933
+ this.data,
24934
+ this.gl.STATIC_DRAW
24935
+ );
24936
+ }
24937
+ /**
24938
+ * Reset the index count (for dynamic buffers)
24939
+ */
24940
+ clear() {
24941
+ this.length = 0;
24942
+ }
24943
+ /**
24944
+ * Add indices to the buffer, rebased by the given vertex offset
24945
+ * @param {number[]} indices - source indices to add
24946
+ * @param {number} vertexOffset - value to add to each index (vertex count at time of insertion)
24947
+ */
24948
+ add(indices, vertexOffset) {
24949
+ for (let i = 0; i < indices.length; i++) {
24950
+ this.data[this.length + i] = indices[i] + vertexOffset;
24951
+ }
24952
+ this.length += indices.length;
24953
+ }
24954
+ /**
24955
+ * Upload the current index data to the GPU (for dynamic buffers)
24956
+ */
24957
+ upload() {
24958
+ this.bind();
24959
+ this.gl.bufferData(
24960
+ this.gl.ELEMENT_ARRAY_BUFFER,
24961
+ this.data.subarray(0, this.length),
24962
+ this.gl.STREAM_DRAW
24963
+ );
24964
+ }
24965
+ /**
24966
+ * bind this index buffer
24967
+ */
24968
+ bind() {
24969
+ this.gl.bindBuffer(this.gl.ELEMENT_ARRAY_BUFFER, this.buffer);
24970
+ }
24971
+ };
24972
+
23834
24973
  // src/video/webgl/buffer/vertex.js
23835
24974
  var VertexArrayBuffer = class {
23836
24975
  constructor(vertexSize, maxVertex) {
@@ -23858,7 +24997,7 @@ var VertexArrayBuffer = class {
23858
24997
  return this.vertexCount + vertex >= this.maxVertex;
23859
24998
  }
23860
24999
  /**
23861
- * push a new vertex to the buffer
25000
+ * push a new vertex to the buffer (quad format: x, y, u, v, tint)
23862
25001
  * @ignore
23863
25002
  */
23864
25003
  push(x, y, u, v, tint) {
@@ -23871,6 +25010,21 @@ var VertexArrayBuffer = class {
23871
25010
  this.vertexCount++;
23872
25011
  return this;
23873
25012
  }
25013
+ /**
25014
+ * push a new vertex with all-float data to the buffer
25015
+ * @param {ArrayLike<number>} data - float values for one vertex
25016
+ * @param {number} srcOffset - start index in the source data
25017
+ * @param {number} count - number of floats to copy (should equal vertexSize)
25018
+ * @ignore
25019
+ */
25020
+ pushFloats(data2, srcOffset, count) {
25021
+ const offset = this.vertexCount * this.vertexSize;
25022
+ for (let i = 0; i < count; i++) {
25023
+ this.bufferF32[offset + i] = data2[srcOffset + i];
25024
+ }
25025
+ this.vertexCount++;
25026
+ return this;
25027
+ }
23874
25028
  /**
23875
25029
  * return a reference to the data in Float32 format
23876
25030
  * @ignore
@@ -24152,20 +25306,23 @@ var GLShader = class {
24152
25306
  };
24153
25307
 
24154
25308
  // src/video/webgl/batchers/batcher.js
24155
- var MAX_VERTICES = 4096;
25309
+ var DEFAULT_MAX_VERTICES = 4096;
24156
25310
  var Batcher = class {
24157
25311
  /**
24158
25312
  * @param {WebGLRenderer} renderer - the current WebGL renderer session
24159
25313
  * @param {object} settings - additional settings to initialize this batcher
24160
- * @param {object[]} settings.attribute - an array of attributes definition
24161
- * @param {string} settings.attribute.name - name of the attribute in the vertex shader
24162
- * @param {number} settings.attribute.size - number of components per vertex attribute. Must be 1, 2, 3, or 4.
24163
- * @param {GLenum} settings.attribute.type - data type of each component in the array
24164
- * @param {boolean} settings.attribute.normalized - whether integer data values should be normalized into a certain range when being cast to a float
24165
- * @param {number} settings.attribute.offset - offset in bytes of the first component in the vertex attribute array
24166
- * @param {object} settings.shader - an array of attributes definition
25314
+ * @param {object[]} settings.attributes - an array of attributes definition
25315
+ * @param {string} settings.attributes.name - name of the attribute in the vertex shader
25316
+ * @param {number} settings.attributes.size - number of components per vertex attribute. Must be 1, 2, 3, or 4.
25317
+ * @param {GLenum} settings.attributes.type - data type of each component in the array
25318
+ * @param {boolean} settings.attributes.normalized - whether integer data values should be normalized into a certain range when being cast to a float
25319
+ * @param {number} settings.attributes.offset - offset in bytes of the first component in the vertex attribute array
25320
+ * @param {object} settings.shader - shader definition
24167
25321
  * @param {string} settings.shader.vertex - a string containing the GLSL source code to set
24168
25322
  * @param {string} settings.shader.fragment - a string containing the GLSL source code to set
25323
+ * @param {number} [settings.maxVertices=4096] - the maximum number of vertices this batcher can hold
25324
+ * @param {boolean} [settings.indexed=false] - whether this batcher uses an index buffer for indexed drawing (drawElements)
25325
+ * @param {string} [settings.projectionUniform="uProjectionMatrix"] - the name of the projection matrix uniform in the shader
24169
25326
  */
24170
25327
  constructor(renderer2, settings) {
24171
25328
  this.init(renderer2, settings);
@@ -24185,6 +25342,7 @@ var Batcher = class {
24185
25342
  this.stride = 0;
24186
25343
  this.vertexSize = 0;
24187
25344
  this.vertexData = null;
25345
+ const maxVertices = settings && settings.maxVertices || DEFAULT_MAX_VERTICES;
24188
25346
  if (typeof settings !== "undefined" && Array.isArray(settings.attributes)) {
24189
25347
  settings.attributes.forEach((attr) => {
24190
25348
  this.addAttribute(
@@ -24195,7 +25353,7 @@ var Batcher = class {
24195
25353
  attr.offset
24196
25354
  );
24197
25355
  });
24198
- this.vertexData = new VertexArrayBuffer(this.vertexSize, MAX_VERTICES);
25356
+ this.vertexData = new VertexArrayBuffer(this.vertexSize, maxVertices);
24199
25357
  } else {
24200
25358
  throw new Error("attributes definition missing");
24201
25359
  }
@@ -24208,6 +25366,15 @@ var Batcher = class {
24208
25366
  } else {
24209
25367
  throw new Error("shader definition missing");
24210
25368
  }
25369
+ this.projectionUniform = settings.projectionUniform || "uProjectionMatrix";
25370
+ this.useIndexBuffer = settings.indexed === true;
25371
+ this.glVertexBuffer = null;
25372
+ this.indexBuffer = null;
25373
+ if (this.useIndexBuffer) {
25374
+ const gl = this.gl;
25375
+ this.glVertexBuffer = gl.createBuffer();
25376
+ this.indexBuffer = new IndexBuffer(gl, maxVertices * 3, false, true);
25377
+ }
24211
25378
  }
24212
25379
  /**
24213
25380
  * Reset batcher internal state
@@ -24216,11 +25383,21 @@ var Batcher = class {
24216
25383
  reset() {
24217
25384
  this.gl = this.renderer.gl;
24218
25385
  this.vertexData.clear();
25386
+ if (this.useIndexBuffer) {
25387
+ this.indexBuffer.clear();
25388
+ }
24219
25389
  }
24220
25390
  /**
24221
25391
  * called by the WebGL renderer when a batcher becomes the current one
24222
25392
  */
24223
25393
  bind() {
25394
+ if (this.useIndexBuffer) {
25395
+ const gl = this.gl;
25396
+ gl.bindBuffer(gl.ARRAY_BUFFER, this.glVertexBuffer);
25397
+ if (this.indexBuffer) {
25398
+ this.indexBuffer.bind();
25399
+ }
25400
+ }
24224
25401
  if (this.renderer.currentProgram !== this.defaultShader.program) {
24225
25402
  this.useShader(this.defaultShader);
24226
25403
  }
@@ -24234,7 +25411,7 @@ var Batcher = class {
24234
25411
  if (this.currentShader !== shader || this.renderer.currentProgram !== shader.program) {
24235
25412
  this.flush();
24236
25413
  shader.bind();
24237
- shader.setUniform("uProjectionMatrix", this.renderer.projectionMatrix);
25414
+ shader.setUniform(this.projectionUniform, this.renderer.projectionMatrix);
24238
25415
  shader.setVertexAttributes(this.gl, this.attributes, this.stride);
24239
25416
  this.currentShader = shader;
24240
25417
  this.renderer.currentProgram = shader.program;
@@ -24285,7 +25462,18 @@ var Batcher = class {
24285
25462
  * @param {Matrix3d} matrix - the new projection matrix
24286
25463
  */
24287
25464
  setProjection(matrix) {
24288
- this.currentShader.setUniform("uProjectionMatrix", matrix);
25465
+ this.currentShader.setUniform(this.projectionUniform, matrix);
25466
+ }
25467
+ /**
25468
+ * Add index values to the index buffer (only for indexed batchers).
25469
+ * Indices are rebased relative to the current vertex count.
25470
+ * @param {number[]} indices - array of index values to add
25471
+ */
25472
+ addIndices(indices) {
25473
+ if (!this.useIndexBuffer) {
25474
+ return;
25475
+ }
25476
+ this.indexBuffer.add(indices, this.vertexData.vertexCount);
24289
25477
  }
24290
25478
  /**
24291
25479
  * Flush batched vertex data to the GPU
@@ -24297,22 +25485,54 @@ var Batcher = class {
24297
25485
  if (vertexCount > 0) {
24298
25486
  const gl = this.gl;
24299
25487
  const vertexSize = vertex.vertexSize;
24300
- if (this.renderer.WebGLVersion > 1) {
24301
- gl.bufferData(
24302
- gl.ARRAY_BUFFER,
24303
- vertex.toFloat32(),
24304
- gl.STREAM_DRAW,
24305
- 0,
24306
- vertexCount * vertexSize
25488
+ if (this.useIndexBuffer && this.indexBuffer.length > 0) {
25489
+ gl.bindBuffer(gl.ARRAY_BUFFER, this.glVertexBuffer);
25490
+ this.currentShader.setVertexAttributes(
25491
+ gl,
25492
+ this.attributes,
25493
+ this.stride
24307
25494
  );
24308
- } else {
24309
- gl.bufferData(
24310
- gl.ARRAY_BUFFER,
24311
- vertex.toFloat32(0, vertexCount * vertexSize),
24312
- gl.STREAM_DRAW
25495
+ if (this.renderer.WebGLVersion > 1) {
25496
+ gl.bufferData(
25497
+ gl.ARRAY_BUFFER,
25498
+ vertex.toFloat32(),
25499
+ gl.STREAM_DRAW,
25500
+ 0,
25501
+ vertexCount * vertexSize
25502
+ );
25503
+ } else {
25504
+ gl.bufferData(
25505
+ gl.ARRAY_BUFFER,
25506
+ vertex.toFloat32(0, vertexCount * vertexSize),
25507
+ gl.STREAM_DRAW
25508
+ );
25509
+ }
25510
+ this.indexBuffer.upload();
25511
+ gl.drawElements(
25512
+ mode,
25513
+ this.indexBuffer.length,
25514
+ this.indexBuffer.type,
25515
+ 0
24313
25516
  );
25517
+ this.indexBuffer.clear();
25518
+ } else {
25519
+ if (this.renderer.WebGLVersion > 1) {
25520
+ gl.bufferData(
25521
+ gl.ARRAY_BUFFER,
25522
+ vertex.toFloat32(),
25523
+ gl.STREAM_DRAW,
25524
+ 0,
25525
+ vertexCount * vertexSize
25526
+ );
25527
+ } else {
25528
+ gl.bufferData(
25529
+ gl.ARRAY_BUFFER,
25530
+ vertex.toFloat32(0, vertexCount * vertexSize),
25531
+ gl.STREAM_DRAW
25532
+ );
25533
+ }
25534
+ gl.drawArrays(mode, 0, vertexCount);
24314
25535
  }
24315
- gl.drawArrays(mode, 0, vertexCount);
24316
25536
  vertex.clear();
24317
25537
  }
24318
25538
  }
@@ -24483,43 +25703,6 @@ var PrimitiveBatcher = class extends Batcher {
24483
25703
  }
24484
25704
  };
24485
25705
 
24486
- // src/video/webgl/buffer/index.js
24487
- var IndexBuffer = class {
24488
- /**
24489
- * @param {WebGL2RenderingContext} gl - the WebGL context
24490
- * @param {number} maxQuads - maximum number of quads this buffer can index
24491
- * @param {boolean} [useUint32=false] - use Uint32 indices (WebGL2) instead of Uint16 (WebGL1)
24492
- */
24493
- constructor(gl, maxQuads, useUint32 = false) {
24494
- this.gl = gl;
24495
- if (useUint32) {
24496
- this.type = gl.UNSIGNED_INT;
24497
- this.data = new Uint32Array(maxQuads * 6);
24498
- } else {
24499
- this.type = gl.UNSIGNED_SHORT;
24500
- this.data = new Uint16Array(maxQuads * 6);
24501
- }
24502
- for (let i = 0, vertex = 0; i < this.data.length; i += 6, vertex += 4) {
24503
- this.data[i] = vertex;
24504
- this.data[i + 1] = vertex + 1;
24505
- this.data[i + 2] = vertex + 2;
24506
- this.data[i + 3] = vertex + 2;
24507
- this.data[i + 4] = vertex + 1;
24508
- this.data[i + 5] = vertex + 3;
24509
- }
24510
- this.buffer = gl.createBuffer();
24511
- this.bind();
24512
- gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, this.data, gl.STATIC_DRAW);
24513
- }
24514
- /**
24515
- * bind this index buffer
24516
- * @ignore
24517
- */
24518
- bind() {
24519
- this.gl.bindBuffer(this.gl.ELEMENT_ARRAY_BUFFER, this.buffer);
24520
- }
24521
- };
24522
-
24523
25706
  // src/video/webgl/shaders/quad.frag
24524
25707
  var quad_default = "uniform sampler2D uSampler;\nvarying vec4 vColor;\nvarying vec2 vRegion;\n\nvoid main(void) {\n gl_FragColor = texture2D(uSampler, vRegion) * vColor;\n}\n";
24525
25708
 
@@ -24571,11 +25754,13 @@ var QuadBatcher = class extends Batcher {
24571
25754
  this.currentTextureUnit = -1;
24572
25755
  this.boundTextures = [];
24573
25756
  this.currentSamplerUnit = -1;
25757
+ const maxQuads = this.vertexData.maxVertex / 4;
24574
25758
  this.indexBuffer = new IndexBuffer(
24575
25759
  this.gl,
24576
- this.vertexData.maxVertex / 4,
25760
+ maxQuads * 6,
24577
25761
  this.renderer.WebGLVersion > 1
24578
25762
  );
25763
+ this.indexBuffer.fillQuadPattern(maxQuads);
24579
25764
  }
24580
25765
  /**
24581
25766
  * Reset compositor internal state
@@ -24591,11 +25776,13 @@ var QuadBatcher = class extends Batcher {
24591
25776
  }
24592
25777
  this.currentTextureUnit = -1;
24593
25778
  this.currentSamplerUnit = -1;
25779
+ const maxQuads = this.vertexData.maxVertex / 4;
24594
25780
  this.indexBuffer = new IndexBuffer(
24595
25781
  this.gl,
24596
- this.vertexData.maxVertex / 4,
25782
+ maxQuads * 6,
24597
25783
  this.renderer.WebGLVersion > 1
24598
25784
  );
25785
+ this.indexBuffer.fillQuadPattern(maxQuads);
24599
25786
  }
24600
25787
  /**
24601
25788
  * Flush batched texture data to the GPU using indexed drawing.
@@ -25019,6 +26206,7 @@ var WebGLRenderer = class extends Renderer {
25019
26206
  if (this.currentBatcher !== void 0) {
25020
26207
  this.currentBatcher.flush();
25021
26208
  }
26209
+ this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.vertexBuffer);
25022
26210
  this.currentBatcher = batcher;
25023
26211
  this.currentBatcher.bind();
25024
26212
  }
@@ -25074,6 +26262,7 @@ var WebGLRenderer = class extends Renderer {
25074
26262
  * @param {Matrix3d} matrix - the new projection matrix
25075
26263
  */
25076
26264
  setProjection(matrix) {
26265
+ this.flush();
25077
26266
  super.setProjection(matrix);
25078
26267
  this.currentBatcher.setProjection(matrix);
25079
26268
  }
@@ -25326,42 +26515,79 @@ var WebGLRenderer = class extends Renderer {
25326
26515
  return this.gl;
25327
26516
  }
25328
26517
  /**
25329
- * set a blend mode for the given context. <br>
25330
- * Supported blend mode between Canvas and WebGL renderer : <br>
25331
- * - "normal" : this is the default mode and draws new content on top of the existing content <br>
25332
- * <img src="../images/normal-blendmode.png" width="510"/> <br>
25333
- * - "multiply" : the pixels of the top layer are multiplied with the corresponding pixel of the bottom layer. A darker picture is the result. <br>
25334
- * <img src="../images/multiply-blendmode.png" width="510"/> <br>
25335
- * - "additive or lighter" : where both content overlap the color is determined by adding color values. <br>
25336
- * <img src="../images/lighter-blendmode.png" width="510"/> <br>
25337
- * - "screen" : The pixels are inverted, multiplied, and inverted again. A lighter picture is the result (opposite of multiply) <br>
25338
- * <img src="../images/screen-blendmode.png" width="510"/> <br>
26518
+ * set the current blend mode for this renderer. <br>
26519
+ * All renderers support: <br>
26520
+ * - "normal" : draws new content on top of the existing content <br>
26521
+ * <img src="../images/normal-blendmode.png" width="180"/> <br>
26522
+ * - "add", "additive", or "lighter" : color values are added together <br>
26523
+ * <img src="../images/add-blendmode.png" width="180"/> <br>
26524
+ * - "multiply" : pixels are multiplied, resulting in a darker picture <br>
26525
+ * <img src="../images/multiply-blendmode.png" width="180"/> <br>
26526
+ * - "screen" : pixels are inverted, multiplied, and inverted again (opposite of multiply) <br>
26527
+ * <img src="../images/screen-blendmode.png" width="180"/> <br>
26528
+ * WebGL2 additionally supports: <br>
26529
+ * - "darken" : retains the darkest pixels of both layers <br>
26530
+ * <img src="../images/darken-blendmode.png" width="180"/> <br>
26531
+ * - "lighten" : retains the lightest pixels of both layers <br>
26532
+ * <img src="../images/lighten-blendmode.png" width="180"/> <br>
26533
+ * Other CSS blend modes ("overlay", "color-dodge", "color-burn", "hard-light", "soft-light",
26534
+ * "difference", "exclusion") may be supported by the Canvas renderer (browser-dependent)
26535
+ * and will always fall back to "normal" in WebGL. <br>
25339
26536
  * @see https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/globalCompositeOperation
25340
- * @param {string} [mode="normal"] - blend mode : "normal", "multiply", "lighter", "additive", "screen"
25341
- * @param {WebGLRenderingContext} [gl] - a WebGL context
26537
+ * @param {string} [mode="normal"] - blend mode
26538
+ * @param {boolean} [premultipliedAlpha=true] - whether textures use premultiplied alpha (affects source blend factor)
26539
+ * @returns {string} the blend mode actually applied (may differ if the requested mode is unsupported)
25342
26540
  */
25343
- setBlendMode(mode = "normal", gl = this.gl) {
26541
+ setBlendMode(mode = "normal", premultipliedAlpha = true) {
25344
26542
  if (this.currentBlendMode !== mode) {
26543
+ const gl = this.gl;
25345
26544
  this.flush();
25346
26545
  gl.enable(gl.BLEND);
25347
26546
  this.currentBlendMode = mode;
26547
+ const srcAlpha = premultipliedAlpha ? gl.ONE : gl.SRC_ALPHA;
25348
26548
  switch (mode) {
25349
26549
  case "screen":
26550
+ gl.blendEquation(gl.FUNC_ADD);
25350
26551
  gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_COLOR);
25351
26552
  break;
25352
26553
  case "lighter":
25353
26554
  case "additive":
25354
- gl.blendFunc(gl.ONE, gl.ONE);
26555
+ case "add":
26556
+ gl.blendEquation(gl.FUNC_ADD);
26557
+ gl.blendFunc(srcAlpha, gl.ONE);
25355
26558
  break;
25356
26559
  case "multiply":
26560
+ gl.blendEquation(gl.FUNC_ADD);
25357
26561
  gl.blendFunc(gl.DST_COLOR, gl.ONE_MINUS_SRC_ALPHA);
25358
26562
  break;
26563
+ case "darken":
26564
+ if (this.WebGLVersion > 1) {
26565
+ gl.blendEquation(gl.MIN);
26566
+ gl.blendFunc(gl.ONE, gl.ONE);
26567
+ } else {
26568
+ gl.blendEquation(gl.FUNC_ADD);
26569
+ gl.blendFunc(srcAlpha, gl.ONE_MINUS_SRC_ALPHA);
26570
+ this.currentBlendMode = "normal";
26571
+ }
26572
+ break;
26573
+ case "lighten":
26574
+ if (this.WebGLVersion > 1) {
26575
+ gl.blendEquation(gl.MAX);
26576
+ gl.blendFunc(gl.ONE, gl.ONE);
26577
+ } else {
26578
+ gl.blendEquation(gl.FUNC_ADD);
26579
+ gl.blendFunc(srcAlpha, gl.ONE_MINUS_SRC_ALPHA);
26580
+ this.currentBlendMode = "normal";
26581
+ }
26582
+ break;
25359
26583
  default:
25360
- gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
26584
+ gl.blendEquation(gl.FUNC_ADD);
26585
+ gl.blendFunc(srcAlpha, gl.ONE_MINUS_SRC_ALPHA);
25361
26586
  this.currentBlendMode = "normal";
25362
26587
  break;
25363
26588
  }
25364
26589
  }
26590
+ return this.currentBlendMode;
25365
26591
  }
25366
26592
  /**
25367
26593
  * restores the most recently saved renderer state by popping the top entry in the drawing state stack
@@ -26028,46 +27254,45 @@ function autoDetectRenderer(options) {
26028
27254
  return new CanvasRenderer(options);
26029
27255
  }
26030
27256
 
26031
- // src/application/header.js
27257
+ // src/application/header.ts
26032
27258
  function consoleHeader(app) {
26033
- let renderType = app.renderer.type;
26034
- let gpu_renderer = typeof app.renderer.GPURenderer === "string" ? " (" + app.renderer.GPURenderer + ")" : "";
26035
- let depthTesting = renderType.includes("WebGL") && app.renderer.depthTest === "z-buffer" ? "Depth Test | " : "";
26036
- let audioType = hasWebAudio ? "Web Audio" : "HTML5 Audio";
27259
+ const renderType = app.renderer.type;
27260
+ const gpu_renderer = typeof app.renderer.GPURenderer === "string" ? ` (${app.renderer.GPURenderer})` : "";
27261
+ const depthTesting = renderType.includes("WebGL") && app.renderer.depthTest === "z-buffer" ? "Depth Test | " : "";
27262
+ const audioType = hasWebAudio ? "Web Audio" : "HTML5 Audio";
26037
27263
  console.log(
26038
- renderType + " renderer" + gpu_renderer + " | " + depthTesting + audioType + " | pixel ratio " + devicePixelRatio + " | " + (platform.nodeJS ? "node.js" : platform.isMobile ? "mobile" : "desktop") + " | " + getScreenOrientation() + " | " + language
27264
+ `${renderType} renderer${gpu_renderer} | ${depthTesting}${audioType} | pixel ratio ${devicePixelRatio} | ${platform.nodeJS ? "node.js" : platform.isMobile ? "mobile" : "desktop"} | ${getScreenOrientation()} | ${language}`
26039
27265
  );
26040
27266
  console.log(
26041
- "resolution: requested " + app.settings.width + "x" + app.settings.height + ", got " + app.renderer.width + "x" + app.renderer.height
27267
+ `resolution: requested ${app.settings.width}x${app.settings.height}, got ${app.renderer.width}x${app.renderer.height}`
26042
27268
  );
26043
27269
  }
26044
27270
 
26045
- // src/application/resize.js
27271
+ // src/application/resize.ts
26046
27272
  function scale(game2, x, y) {
26047
- let renderer2 = game2.renderer;
26048
- let canvas = renderer2.getCanvas();
26049
- let context = renderer2.getContext();
26050
- let settings = renderer2.settings;
26051
- let pixelRatio = devicePixelRatio;
26052
- let w = settings.zoomX = canvas.width * x * pixelRatio;
26053
- let h = settings.zoomY = canvas.height * y * pixelRatio;
27273
+ const renderer2 = game2.renderer;
27274
+ const canvas = renderer2.getCanvas();
27275
+ const settings = renderer2.settings;
27276
+ const pixelRatio = devicePixelRatio;
27277
+ const w = settings.zoomX = canvas.width * x * pixelRatio;
27278
+ const h = settings.zoomY = canvas.height * y * pixelRatio;
26054
27279
  renderer2.scaleRatio.set(x * pixelRatio, y * pixelRatio);
26055
- canvas.style.width = w / pixelRatio + "px";
26056
- canvas.style.height = h / pixelRatio + "px";
27280
+ canvas.style.width = `${w / pixelRatio}px`;
27281
+ canvas.style.height = `${h / pixelRatio}px`;
26057
27282
  renderer2.setAntiAlias(settings.antiAlias);
26058
- renderer2.setBlendMode(settings.blendMode, context);
27283
+ renderer2.setBlendMode(settings.blendMode);
26059
27284
  game2.repaint();
26060
27285
  }
26061
27286
  function onresize(game2) {
26062
- let renderer2 = game2.renderer;
26063
- let settings = renderer2.settings;
27287
+ const renderer2 = game2.renderer;
27288
+ const settings = renderer2.settings;
26064
27289
  let scaleX = settings.scale, scaleY = settings.scale;
26065
27290
  let nodeBounds;
26066
27291
  if (settings.autoScale) {
26067
27292
  let canvasMaxWidth = Infinity;
26068
27293
  let canvasMaxHeight = Infinity;
26069
27294
  if (globalThis.getComputedStyle) {
26070
- let style = globalThis.getComputedStyle(renderer2.getCanvas(), null);
27295
+ const style = globalThis.getComputedStyle(renderer2.getCanvas(), null);
26071
27296
  canvasMaxWidth = parseInt(style.maxWidth, 10) || Infinity;
26072
27297
  canvasMaxHeight = parseInt(style.maxHeight, 10) || Infinity;
26073
27298
  }
@@ -26076,15 +27301,15 @@ function onresize(game2) {
26076
27301
  } else {
26077
27302
  nodeBounds = getParentBounds(game2.getParentElement());
26078
27303
  }
26079
- let _max_width = Math.min(canvasMaxWidth, nodeBounds.width);
26080
- let _max_height = Math.min(canvasMaxHeight, nodeBounds.height);
26081
- let screenRatio = _max_width / _max_height;
27304
+ const _max_width = Math.min(canvasMaxWidth, nodeBounds.width);
27305
+ const _max_height = Math.min(canvasMaxHeight, nodeBounds.height);
27306
+ const screenRatio = _max_width / _max_height;
26082
27307
  if (settings.scaleMethod === "fill-min" && screenRatio > renderer2.designRatio || settings.scaleMethod === "fill-max" && screenRatio < renderer2.designRatio || settings.scaleMethod === "flex-width") {
26083
- let sWidth = Math.min(canvasMaxWidth, settings.height * screenRatio);
27308
+ const sWidth = Math.min(canvasMaxWidth, settings.height * screenRatio);
26084
27309
  scaleX = scaleY = _max_width / sWidth;
26085
27310
  renderer2.resize(Math.floor(sWidth), settings.height);
26086
27311
  } else if (settings.scaleMethod === "fill-min" && screenRatio < renderer2.designRatio || settings.scaleMethod === "fill-max" && screenRatio > renderer2.designRatio || settings.scaleMethod === "flex-height") {
26087
- let sHeight = Math.min(
27312
+ const sHeight = Math.min(
26088
27313
  canvasMaxHeight,
26089
27314
  settings.width * (_max_height / _max_width)
26090
27315
  );
@@ -26106,23 +27331,91 @@ function onresize(game2) {
26106
27331
  scale(game2, scaleX, scaleY);
26107
27332
  }
26108
27333
 
26109
- // src/application/application.js
27334
+ // src/application/application.ts
26110
27335
  var Application = class {
26111
27336
  /**
26112
- * @param {number} width - The width of the canvas viewport
26113
- * @param {number} height - The height of the canvas viewport
26114
- * @param {ApplicationSettings} [options] - The optional parameters for the application and default renderer
26115
- * @throws Will throw an exception if it fails to instantiate a renderer
27337
+ * the parent HTML element holding the main canvas of this application
27338
+ */
27339
+ parentElement;
27340
+ /**
27341
+ * a reference to the active Canvas or WebGL renderer
27342
+ */
27343
+ renderer;
27344
+ /**
27345
+ * the active stage "default" camera
27346
+ */
27347
+ viewport;
27348
+ /**
27349
+ * a reference to the game world, <br>
27350
+ * a world is a virtual environment containing all the game objects
27351
+ */
27352
+ world;
27353
+ /**
27354
+ * when true, all objects will be added under the root world container.<br>
27355
+ * When false, a `me.Container` object will be created for each corresponding groups
27356
+ * @default true
27357
+ */
27358
+ mergeGroup;
27359
+ /**
27360
+ * Last time the game update loop was executed. <br>
27361
+ * Use this value to implement frame prediction in drawing events,
27362
+ * for creating smooth motion while running game update logic at
27363
+ * a lower fps.
27364
+ */
27365
+ lastUpdate;
27366
+ /**
27367
+ * true when this app instance has been initialized
27368
+ * @default false
27369
+ */
27370
+ isInitialized;
27371
+ /**
27372
+ * the given settings used when creating this application
27373
+ */
27374
+ settings;
27375
+ /**
27376
+ * Specify whether to pause this app when losing focus
27377
+ * @default true
27378
+ * @example
27379
+ * // keep the default game instance running even when losing focus
27380
+ * me.game.pauseOnBlur = false;
27381
+ */
27382
+ pauseOnBlur;
27383
+ /**
27384
+ * Specify whether to unpause this app when gaining back focus
27385
+ * @default true
27386
+ */
27387
+ resumeOnFocus;
27388
+ /**
27389
+ * Specify whether to stop this app when losing focus
27390
+ * @default false
26116
27391
  */
26117
- constructor(width, height, options) {
26118
- this.parentElement = void 0;
26119
- this.renderer = void 0;
26120
- this.viewport = void 0;
26121
- this.world = void 0;
27392
+ stopOnBlur;
27393
+ // to know when we have to refresh the display
27394
+ isDirty;
27395
+ // always refresh the display when updatesPerSecond are lower than fps
27396
+ isAlwaysDirty;
27397
+ // frame counter for frameSkipping
27398
+ frameCounter;
27399
+ frameRate;
27400
+ // time accumulation for multiple update calls
27401
+ accumulator;
27402
+ accumulatorMax;
27403
+ accumulatorUpdateDelta;
27404
+ // min update step size
27405
+ stepSize;
27406
+ updateDelta;
27407
+ lastUpdateStart;
27408
+ updateAverageDelta;
27409
+ /**
27410
+ * @param width - The width of the canvas viewport
27411
+ * @param height - The height of the canvas viewport
27412
+ * @param options - The optional parameters for the application and default renderer
27413
+ * @throws {Error} Will throw an exception if it fails to instantiate a renderer
27414
+ */
27415
+ constructor(width, height, options = {}) {
26122
27416
  this.mergeGroup = true;
26123
27417
  this.lastUpdate = 0;
26124
27418
  this.isInitialized = false;
26125
- this.settings = void 0;
26126
27419
  this.pauseOnBlur = true;
26127
27420
  this.resumeOnFocus = true;
26128
27421
  this.stopOnBlur = false;
@@ -26143,20 +27436,18 @@ var Application = class {
26143
27436
  }
26144
27437
  /**
26145
27438
  * init the game instance (create a physic world, update starting time, etc..)
26146
- * @param {number} width - The width of the canvas viewport
26147
- * @param {number} height - The height of the canvas viewport
26148
- * @param {ApplicationSettings} [options] - The optional parameters for the application and default renderer
27439
+ * @param width - The width of the canvas viewport
27440
+ * @param height - The height of the canvas viewport
27441
+ * @param options - The optional parameters for the application and default renderer
26149
27442
  */
26150
27443
  init(width, height, options) {
26151
- this.settings = Object.assign(defaultApplicationSettings, options || {});
27444
+ this.settings = Object.assign(
27445
+ defaultApplicationSettings,
27446
+ options || {}
27447
+ );
26152
27448
  this.settings.width = width;
26153
27449
  this.settings.height = height;
26154
- this.settings.transparent = !!this.settings.transparent;
26155
- this.settings.antiAlias = !!this.settings.antiAlias;
26156
- this.settings.failIfMajorPerformanceCaveat = !!this.settings.failIfMajorPerformanceCaveat;
26157
27450
  this.settings.depthTest = this.settings.depthTest === "z-buffer" ? "z-buffer" : "sorting";
26158
- this.settings.subPixel = !!this.settings.subPixel;
26159
- this.settings.verbose = !!this.settings.verbose;
26160
27451
  if (this.settings.scaleMethod.search(
26161
27452
  /^(fill-(min|max)|fit|flex(-(width|height))?|stretch)$/
26162
27453
  ) !== -1) {
@@ -26165,7 +27456,7 @@ var Application = class {
26165
27456
  this.settings.scaleMethod = ScaleMethods.Fit;
26166
27457
  this.settings.autoScale = this.settings.scale === "auto" || false;
26167
27458
  }
26168
- let uriFragment = getUriFragment();
27459
+ const uriFragment = getUriFragment();
26169
27460
  if (uriFragment.webgl === true || uriFragment.webgl1 === true || uriFragment.webgl2 === true) {
26170
27461
  this.settings.renderer = WEBGL;
26171
27462
  if (uriFragment.webgl1 === true) {
@@ -26192,14 +27483,14 @@ var Application = class {
26192
27483
  break;
26193
27484
  }
26194
27485
  } else {
26195
- let CustomRenderer = this.settings.renderer;
27486
+ const CustomRenderer = this.settings.renderer;
26196
27487
  this.renderer = new CustomRenderer(this.settings);
26197
27488
  }
26198
27489
  eventEmitter.addListener(WINDOW_ONRESIZE, () => {
26199
- return onresize(this);
27490
+ onresize(this);
26200
27491
  });
26201
27492
  eventEmitter.addListener(WINDOW_ONORIENTATION_CHANGE, () => {
26202
- return onresize(this);
27493
+ onresize(this);
26203
27494
  });
26204
27495
  this.parentElement.appendChild(this.renderer.getCanvas());
26205
27496
  if (platform.isMobile) {
@@ -26207,8 +27498,8 @@ var Application = class {
26207
27498
  }
26208
27499
  onresize(this);
26209
27500
  if ("MutationObserver" in globalThis) {
26210
- let observer = new MutationObserver(() => {
26211
- return onresize(this);
27501
+ const observer = new MutationObserver(() => {
27502
+ onresize(this);
26212
27503
  });
26213
27504
  observer.observe(this.parentElement, {
26214
27505
  attributes: false,
@@ -26216,7 +27507,7 @@ var Application = class {
26216
27507
  subtree: true
26217
27508
  });
26218
27509
  }
26219
- if (this.settings.consoleHeader !== false) {
27510
+ if (this.settings.consoleHeader) {
26220
27511
  consoleHeader(this);
26221
27512
  }
26222
27513
  this.world = new World(0, 0, this.settings.width, this.settings.height);
@@ -26235,18 +27526,18 @@ var Application = class {
26235
27526
  this.draw();
26236
27527
  });
26237
27528
  eventEmitter.addListener(BLUR, () => {
26238
- if (this.stopOnBlur === true) {
27529
+ if (this.stopOnBlur) {
26239
27530
  state_default.stop(true);
26240
27531
  }
26241
- if (this.pauseOnBlur === true) {
27532
+ if (this.pauseOnBlur) {
26242
27533
  state_default.pause(true);
26243
27534
  }
26244
27535
  });
26245
27536
  eventEmitter.addListener(FOCUS, () => {
26246
- if (this.stopOnBlur === true) {
27537
+ if (this.stopOnBlur) {
26247
27538
  state_default.restart(true);
26248
27539
  }
26249
- if (this.resumeOnFocus === true) {
27540
+ if (this.resumeOnFocus) {
26250
27541
  state_default.resume(true);
26251
27542
  }
26252
27543
  });
@@ -26256,9 +27547,11 @@ var Application = class {
26256
27547
  * destroy all current objects
26257
27548
  */
26258
27549
  reset() {
26259
- let current = state_default.get();
27550
+ const current = state_default.get();
26260
27551
  if (typeof current !== "undefined") {
26261
- this.viewport = current.cameras.get("default");
27552
+ this.viewport = current.cameras.get(
27553
+ "default"
27554
+ );
26262
27555
  }
26263
27556
  eventEmitter.emit(GAME_RESET);
26264
27557
  this.updateFrameRate();
@@ -26266,7 +27559,6 @@ var Application = class {
26266
27559
  /**
26267
27560
  * Specify the property to be used when sorting renderables for this application game world.
26268
27561
  * Accepted values : "x", "y", "z", "depth"
26269
- * @type {string}
26270
27562
  * @see {@link World.sortOn}
26271
27563
  */
26272
27564
  get sortOn() {
@@ -26299,7 +27591,7 @@ var Application = class {
26299
27591
  }
26300
27592
  /**
26301
27593
  * Returns the parent HTML Element holding the main canvas of this application
26302
- * @returns {HTMLElement} the parent HTML element
27594
+ * @returns the parent HTML element
26303
27595
  */
26304
27596
  getParentElement() {
26305
27597
  return this.parentElement;
@@ -26312,7 +27604,7 @@ var Application = class {
26312
27604
  }
26313
27605
  /**
26314
27606
  * update all objects related to this game active scene/stage
26315
- * @param {number} time - current timestamp as provided by the RAF callback
27607
+ * @param time - current timestamp as provided by the RAF callback
26316
27608
  */
26317
27609
  update(time) {
26318
27610
  if (++this.frameCounter % this.frameRate === 0) {
@@ -26324,7 +27616,7 @@ var Application = class {
26324
27616
  this.accumulatorUpdateDelta = timer_default.interpolation ? this.updateDelta : Math.max(this.updateDelta, this.updateAverageDelta);
26325
27617
  while (this.accumulator >= this.accumulatorUpdateDelta || timer_default.interpolation) {
26326
27618
  this.lastUpdateStart = globalThis.performance.now();
26327
- if (state_default.isPaused() !== true) {
27619
+ if (!state_default.isPaused()) {
26328
27620
  eventEmitter.emit(GAME_UPDATE, time);
26329
27621
  }
26330
27622
  this.isDirty = this.world.update(this.updateDelta);
@@ -26344,7 +27636,7 @@ var Application = class {
26344
27636
  * draw the active scene/stage associated to this game
26345
27637
  */
26346
27638
  draw() {
26347
- if (this.renderer.isContextValid === true && (this.isDirty || this.isAlwaysDirty)) {
27639
+ if (this.renderer.isContextValid && (this.isDirty || this.isAlwaysDirty)) {
26348
27640
  eventEmitter.emit(GAME_BEFORE_DRAW, globalThis.performance.now());
26349
27641
  this.renderer.clear();
26350
27642
  state_default.current().draw(this.renderer, this.world);
@@ -26681,7 +27973,7 @@ var ParticleEmitterSettings = {
26681
27973
  };
26682
27974
  var settings_default = ParticleEmitterSettings;
26683
27975
 
26684
- // src/particles/emitter.js
27976
+ // src/particles/emitter.ts
26685
27977
  function createDefaultParticleTexture(w = 8, h = 8) {
26686
27978
  const defaultParticleTexture = new canvasrendertarget_default(w, h, {
26687
27979
  offscreenCanvas: true
@@ -26692,9 +27984,27 @@ function createDefaultParticleTexture(w = 8, h = 8) {
26692
27984
  }
26693
27985
  var ParticleEmitter = class extends Container {
26694
27986
  /**
26695
- * @param {number} x - x position of the particle emitter
26696
- * @param {number} y - y position of the particle emitter
26697
- * @param {ParticleEmitterSettings} [settings=ParticleEmitterSettings] - the settings for the particle emitter.
27987
+ * the current (active) emitter settings
27988
+ */
27989
+ settings;
27990
+ /** @ignore */
27991
+ _stream;
27992
+ /** @ignore */
27993
+ _frequencyTimer;
27994
+ /** @ignore */
27995
+ _durationTimer;
27996
+ /** @ignore */
27997
+ _enabled;
27998
+ /** @ignore */
27999
+ _updateCount;
28000
+ /** @ignore */
28001
+ _dt;
28002
+ /** @ignore */
28003
+ _defaultParticle;
28004
+ /**
28005
+ * @param x - x position of the particle emitter
28006
+ * @param y - y position of the particle emitter
28007
+ * @param [settings=ParticleEmitterSettings] - the settings for the particle emitter.
26698
28008
  * @example
26699
28009
  * // Create a particle emitter at position 100, 100
26700
28010
  * let emitter = new ParticleEmitter(100, 100, {
@@ -26737,7 +28047,7 @@ var ParticleEmitter = class extends Container {
26737
28047
  }
26738
28048
  /**
26739
28049
  * Reset the emitter with particle emitter settings.
26740
- * @param {ParticleEmitterSettings} settings - [optional] object with emitter settings. See {@link ParticleEmitterSettings}
28050
+ * @param settings - [optional] object with emitter settings. See {@link ParticleEmitterSettings}
26741
28051
  */
26742
28052
  reset(settings = {}) {
26743
28053
  Object.assign(this.settings, settings_default, settings);
@@ -26753,14 +28063,14 @@ var ParticleEmitter = class extends Container {
26753
28063
  }
26754
28064
  /**
26755
28065
  * returns a random point on the x axis within the bounds of this emitter
26756
- * @returns {number}
28066
+ * @returns a random x position within the emitter bounds
26757
28067
  */
26758
28068
  getRandomPointX() {
26759
28069
  return randomFloat(0, this.getBounds().width);
26760
28070
  }
26761
28071
  /**
26762
28072
  * returns a random point on the y axis within the bounds this emitter
26763
- * @returns {number}
28073
+ * @returns a random y position within the emitter bounds
26764
28074
  */
26765
28075
  getRandomPointY() {
26766
28076
  return randomFloat(0, this.getBounds().height);
@@ -26775,14 +28085,14 @@ var ParticleEmitter = class extends Container {
26775
28085
  }
26776
28086
  /**
26777
28087
  * Emitter is of type stream and is launching particles
26778
- * @returns {boolean} Emitter is Stream and is launching particles
28088
+ * @returns Emitter is Stream and is launching particles
26779
28089
  */
26780
28090
  isRunning() {
26781
28091
  return this._enabled && this._stream;
26782
28092
  }
26783
28093
  /**
26784
28094
  * Launch particles from emitter constantly (e.g. for stream)
26785
- * @param {number} [duration] - time that the emitter releases particles in ms
28095
+ * @param [duration] - time that the emitter releases particles in ms
26786
28096
  */
26787
28097
  streamParticles(duration) {
26788
28098
  this._enabled = true;
@@ -26798,7 +28108,7 @@ var ParticleEmitter = class extends Container {
26798
28108
  }
26799
28109
  /**
26800
28110
  * Launch all particles from emitter and stop (e.g. for explosion)
26801
- * @param {number} [total] - number of particles to launch
28111
+ * @param [total] - number of particles to launch
26802
28112
  */
26803
28113
  burstParticles(total) {
26804
28114
  this._enabled = true;
@@ -26821,7 +28131,7 @@ var ParticleEmitter = class extends Container {
26821
28131
  }
26822
28132
  dt += this._dt;
26823
28133
  this._dt = 0;
26824
- this.isDirty |= super.update(dt);
28134
+ this.isDirty = this.isDirty || super.update(dt);
26825
28135
  if (this._enabled && this._stream) {
26826
28136
  if (this._durationTimer !== Infinity) {
26827
28137
  this._durationTimer -= dt;
@@ -26831,7 +28141,7 @@ var ParticleEmitter = class extends Container {
26831
28141
  }
26832
28142
  }
26833
28143
  this._frequencyTimer += dt;
26834
- const particlesCount = this.children.length;
28144
+ const particlesCount = this.children?.length ?? 0;
26835
28145
  if (particlesCount < this.settings.totalParticles && this._frequencyTimer >= this.settings.frequency) {
26836
28146
  if (particlesCount + this.settings.maxParticles <= this.settings.totalParticles) {
26837
28147
  this.addParticles(this.settings.maxParticles);
@@ -26849,7 +28159,7 @@ var ParticleEmitter = class extends Container {
26849
28159
  * @ignore
26850
28160
  */
26851
28161
  destroy() {
26852
- super.destroy(arguments);
28162
+ super.destroy();
26853
28163
  if (typeof this._defaultParticle !== "undefined") {
26854
28164
  this._defaultParticle.destroy();
26855
28165
  this._defaultParticle = void 0;
@@ -26859,7 +28169,7 @@ var ParticleEmitter = class extends Container {
26859
28169
  }
26860
28170
  };
26861
28171
 
26862
- // src/plugin/plugin.js
28172
+ // src/plugin/plugin.ts
26863
28173
  var plugin_exports = {};
26864
28174
  __export(plugin_exports, {
26865
28175
  BasePlugin: () => BasePlugin,
@@ -26870,14 +28180,20 @@ __export(plugin_exports, {
26870
28180
  });
26871
28181
 
26872
28182
  // src/version.ts
26873
- var version = "18.1.0";
28183
+ var version = "18.2.0";
26874
28184
 
26875
- // src/plugin/plugin.js
28185
+ // src/plugin/plugin.ts
26876
28186
  var cache = {};
26877
28187
  var BasePlugin = class {
26878
28188
  /**
26879
- * @param {Application} [app] - a reference to the app/game that registered this plugin
28189
+ * define the minimum required version of melonJS
28190
+ * this can be overridden by the plugin
26880
28191
  */
28192
+ version;
28193
+ /**
28194
+ * a reference to the app/game that registered this plugin
28195
+ */
28196
+ app;
26881
28197
  constructor(app = game) {
26882
28198
  this.version = version;
26883
28199
  this.app = app;
@@ -26891,42 +28207,42 @@ function patch(proto, name, fn) {
26891
28207
  const _parent = proto[name];
26892
28208
  Object.defineProperty(proto, name, {
26893
28209
  configurable: true,
26894
- value: /* @__PURE__ */ (function(name2, fn2) {
26895
- return function() {
28210
+ value: /* @__PURE__ */ (function(_name, fn2) {
28211
+ return function(...args) {
26896
28212
  this._patched = _parent;
26897
- const ret = fn2.apply(this, arguments);
28213
+ const ret = fn2.apply(this, args);
26898
28214
  this._patched = null;
26899
28215
  return ret;
26900
28216
  };
26901
28217
  })(name, fn)
26902
28218
  });
26903
28219
  } else {
26904
- throw new Error(name + " is not an existing function");
28220
+ throw new Error(`${name} is not an existing function`);
26905
28221
  }
26906
28222
  }
26907
- function register(plugin, name = plugin.toString().match(/ (\w+)/)[1]) {
26908
- if (cache[name]) {
26909
- throw new Error("plugin " + name + " already registered");
26910
- }
26911
- let _args = [];
26912
- if (arguments.length > 2) {
26913
- _args = Array.prototype.slice.call(arguments, 1);
28223
+ function register(pluginClass, name, ...args) {
28224
+ const pluginName = name || pluginClass.name || pluginClass.toString().match(/ (\w+)/)[1];
28225
+ if (cache[pluginName]) {
28226
+ throw new Error(`plugin ${pluginName} already registered`);
26914
28227
  }
26915
- _args[0] = plugin;
26916
- const instance = new (plugin.bind.apply(plugin, _args))();
28228
+ const instance = new pluginClass(...args);
26917
28229
  if (typeof instance === "undefined" || !(instance instanceof BasePlugin)) {
26918
28230
  throw new Error("Plugin should extend the BasePlugin Class !");
26919
28231
  }
26920
28232
  if (checkVersion(instance.version, version) > 0) {
26921
28233
  throw new Error(
26922
- "Plugin version mismatch, expected: " + instance.version + ", got: " + version
28234
+ `Plugin version mismatch, expected: ${instance.version}, got: ${version}`
26923
28235
  );
26924
28236
  }
26925
- cache[name] = instance;
28237
+ cache[pluginName] = instance;
26926
28238
  }
26927
28239
  function get(classType) {
26928
28240
  for (const name in cache) {
26929
- if (typeof classType === "string" && classType === name || cache[name] instanceof classType) {
28241
+ if (typeof classType === "string") {
28242
+ if (classType === name) {
28243
+ return cache[name];
28244
+ }
28245
+ } else if (cache[name] instanceof classType) {
26930
28246
  return cache[name];
26931
28247
  }
26932
28248
  }
@@ -27344,6 +28660,7 @@ var ImageLayer = class extends Sprite {
27344
28660
  constructor(x, y, settings) {
27345
28661
  super(x, y, settings);
27346
28662
  this.floating = true;
28663
+ this.visibleInAllCameras = true;
27347
28664
  this.offset.set(x, y);
27348
28665
  this.ratio = vector2dPool.get(1, 1);
27349
28666
  if (typeof settings.ratio !== "undefined") {
@@ -27489,13 +28806,25 @@ var ImageLayer = class extends Sprite {
27489
28806
  const bh = viewport.bounds.height;
27490
28807
  const ax = this.anchorPoint.x;
27491
28808
  const ay = this.anchorPoint.y;
27492
- let x = this.pos.x;
27493
- let y = this.pos.y;
27494
- if (this.ratio.x === 0 && this.ratio.y === 0) {
27495
- x = x + ax * (bw - width);
27496
- y = y + ay * (bh - height);
28809
+ const rx = this.ratio.x;
28810
+ const ry = this.ratio.y;
28811
+ const vZoom = viewport.zoom;
28812
+ let x, y;
28813
+ if (rx === 0 && ry === 0) {
28814
+ x = this.pos.x + ax * (bw - width);
28815
+ y = this.pos.y + ay * (bh - height);
28816
+ } else {
28817
+ x = ax * (rx - 1) * (bw - viewport.width) + this.offset.x - rx * viewport.pos.x;
28818
+ y = ay * (ry - 1) * (bh - viewport.height) + this.offset.y - ry * viewport.pos.y;
28819
+ if (this.repeatX) {
28820
+ x = x % width;
28821
+ }
28822
+ if (this.repeatY) {
28823
+ y = y % height;
28824
+ }
27497
28825
  }
27498
- renderer2.translate(x, y);
28826
+ renderer2.translate(x * vZoom, y * vZoom);
28827
+ renderer2.scale(vZoom, vZoom);
27499
28828
  renderer2.drawPattern(
27500
28829
  this._pattern,
27501
28830
  0,
@@ -28709,23 +30038,55 @@ var Trigger = class extends Renderable {
28709
30038
  }
28710
30039
  };
28711
30040
 
28712
- // src/renderable/ui/uibaseelement.js
30041
+ // src/renderable/ui/uibaseelement.ts
28713
30042
  var UIBaseElement = class extends Container {
28714
30043
  #boundPointerMoveHandler;
28715
30044
  /**
28716
30045
  * UI base elements use screen coordinates by default
28717
30046
  * (Note: any child elements added to a UIBaseElement should have their floating property to false)
28718
30047
  * @see Renderable.floating
28719
- * @type {boolean}
28720
30048
  * @default true
28721
30049
  */
28722
30050
  floating = true;
28723
30051
  /**
28724
- *
28725
- * @param {number} x - The x position of the container
28726
- * @param {number} y - The y position of the container
28727
- * @param {number} w - width of the container
28728
- * @param {number} h - height of the container
30052
+ * object can be clicked or not
30053
+ * @default true
30054
+ */
30055
+ isClickable;
30056
+ /**
30057
+ * object can be dragged or not
30058
+ * @default false
30059
+ */
30060
+ isDraggable;
30061
+ /**
30062
+ * Tap and hold threshold timeout in ms
30063
+ * @default 250
30064
+ */
30065
+ holdThreshold;
30066
+ /**
30067
+ * object can be tap and hold
30068
+ * @default false
30069
+ */
30070
+ isHoldable;
30071
+ /**
30072
+ * true if the pointer is over the object
30073
+ * @default false
30074
+ */
30075
+ hover;
30076
+ /**
30077
+ * false if the pointer is down, or true when the pointer status is up
30078
+ * @default true
30079
+ */
30080
+ released;
30081
+ // object has been updated (clicked,etc..)
30082
+ holdTimeout;
30083
+ // grab offset for dragging
30084
+ grabOffset;
30085
+ /**
30086
+ * @param x - The x position of the container
30087
+ * @param y - The y position of the container
30088
+ * @param w - width of the container
30089
+ * @param h - height of the container
28729
30090
  */
28730
30091
  constructor(x, y, w, h) {
28731
30092
  super(x, y, w, h);
@@ -28752,7 +30113,7 @@ var UIBaseElement = class extends Container {
28752
30113
  timer_default.clearTimer(this.holdTimeout);
28753
30114
  this.holdTimeout = timer_default.setTimeout(
28754
30115
  () => {
28755
- return this.hold();
30116
+ this.hold();
28756
30117
  },
28757
30118
  this.holdThreshold,
28758
30119
  false
@@ -28768,10 +30129,11 @@ var UIBaseElement = class extends Container {
28768
30129
  }
28769
30130
  /**
28770
30131
  * function called when the object is pressed (to be extended)
28771
- * @param {Pointer} event - the event object
28772
- * @returns {boolean} return false if we need to stop propagating the event
30132
+ * @param _event - the event object
30133
+ * @returns return false if we need to stop propagating the event
28773
30134
  */
28774
- onClick() {
30135
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
30136
+ onClick(_event) {
28775
30137
  return true;
28776
30138
  }
28777
30139
  /**
@@ -28781,35 +30143,38 @@ var UIBaseElement = class extends Container {
28781
30143
  enter(event) {
28782
30144
  this.hover = true;
28783
30145
  this.isDirty = true;
28784
- if (this.isDraggable === true) {
30146
+ if (this.isDraggable) {
28785
30147
  eventEmitter.addListener(POINTERMOVE, this.#boundPointerMoveHandler);
28786
30148
  this.grabOffset = vector2dPool.get(0, 0);
28787
30149
  }
28788
- return this.onOver(event);
30150
+ this.onOver(event);
28789
30151
  }
28790
30152
  /**
28791
30153
  * pointermove function
28792
30154
  * @ignore
28793
30155
  */
28794
30156
  pointerMove(event) {
28795
- if (this.hover === true && this.released === false) {
30157
+ if (this.hover && !this.released) {
28796
30158
  this.pos.set(event.gameX, event.gameY, this.pos.z);
28797
30159
  this.pos.sub(this.grabOffset);
28798
30160
  this.isDirty = true;
28799
- return this.onMove(event);
30161
+ this.onMove(event);
30162
+ return;
28800
30163
  }
28801
30164
  }
28802
30165
  /**
28803
30166
  * function called when the pointer is moved over the object
28804
- * @param {Pointer} event - the event object
30167
+ * @param _event - the event object
28805
30168
  */
28806
- onMove() {
30169
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
30170
+ onMove(_event) {
28807
30171
  }
28808
30172
  /**
28809
30173
  * function called when the pointer is over the object
28810
- * @param {Pointer} event - the event object
30174
+ * @param _event - the event object
28811
30175
  */
28812
- onOver() {
30176
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
30177
+ onOver(_event) {
28813
30178
  }
28814
30179
  /**
28815
30180
  * function callback for the pointerLeave event
@@ -28818,26 +30183,27 @@ var UIBaseElement = class extends Container {
28818
30183
  leave(event) {
28819
30184
  this.hover = false;
28820
30185
  this.isDirty = true;
28821
- if (this.isDraggable === true) {
30186
+ if (this.isDraggable) {
28822
30187
  eventEmitter.removeListener(POINTERMOVE, this.#boundPointerMoveHandler);
28823
30188
  vector2dPool.release(this.grabOffset);
28824
30189
  this.grabOffset = void 0;
28825
30190
  }
28826
30191
  this.release(event);
28827
- return this.onOut(event);
30192
+ this.onOut(event);
28828
30193
  }
28829
30194
  /**
28830
30195
  * function called when the pointer is leaving the object area
28831
- * @param {Pointer} event - the event object
30196
+ * @param _event - the event object
28832
30197
  */
28833
- onOut() {
30198
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
30199
+ onOut(_event) {
28834
30200
  }
28835
30201
  /**
28836
30202
  * function callback for the pointerup event
28837
30203
  * @ignore
28838
30204
  */
28839
30205
  release(event) {
28840
- if (this.released === false) {
30206
+ if (!this.released) {
28841
30207
  this.released = true;
28842
30208
  this.isDirty = true;
28843
30209
  timer_default.clearTimer(this.holdTimeout);
@@ -28847,9 +30213,11 @@ var UIBaseElement = class extends Container {
28847
30213
  }
28848
30214
  /**
28849
30215
  * function called when the object is pressed and released (to be extended)
28850
- * @returns {boolean} return false if we need to stop propagating the event
30216
+ * @param _event - the event object
30217
+ * @returns return false if we need to stop propagating the event
28851
30218
  */
28852
- onRelease() {
30219
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
30220
+ onRelease(_event) {
28853
30221
  return true;
28854
30222
  }
28855
30223
  /**
@@ -28885,10 +30253,10 @@ var UIBaseElement = class extends Container {
28885
30253
  return this.release(e);
28886
30254
  });
28887
30255
  registerPointerEvent("pointerenter", this, (e) => {
28888
- return this.enter(e);
30256
+ this.enter(e);
28889
30257
  });
28890
30258
  registerPointerEvent("pointerleave", this, (e) => {
28891
- return this.leave(e);
30259
+ this.leave(e);
28892
30260
  });
28893
30261
  super.onActivateEvent();
28894
30262
  }
@@ -28904,7 +30272,7 @@ var UIBaseElement = class extends Container {
28904
30272
  releasePointerEvent("pointerleave", this);
28905
30273
  timer_default.clearTimer(this.holdTimeout);
28906
30274
  this.holdTimeout = -1;
28907
- if (this.isDraggable === true) {
30275
+ if (this.isDraggable) {
28908
30276
  eventEmitter.removeListener(POINTERMOVE, this.#boundPointerMoveHandler);
28909
30277
  if (typeof this.grabOffset !== "undefined") {
28910
30278
  vector2dPool.release(this.grabOffset);
@@ -28915,20 +30283,43 @@ var UIBaseElement = class extends Container {
28915
30283
  }
28916
30284
  };
28917
30285
 
28918
- // src/renderable/ui/uispriteelement.js
30286
+ // src/renderable/ui/uispriteelement.ts
28919
30287
  var UISpriteElement = class extends Sprite {
28920
30288
  /**
28921
30289
  * if this UISpriteElement should use screen coordinates or local coordinates
28922
30290
  * (Note: any UISpriteElement elements added to a floating parent container should have their floating property to false)
28923
30291
  * @see Renderable.floating
28924
- * @type {boolean}
28925
30292
  * @default true
28926
30293
  */
28927
30294
  floating = true;
28928
30295
  /**
28929
- * @param {number} x - the x coordinate of the UISpriteElement Object
28930
- * @param {number} y - the y coordinate of the UISpriteElement Object
28931
- * @param {object} settings - See {@link Sprite}
30296
+ * object can be clicked or not
30297
+ * @default true
30298
+ */
30299
+ isClickable;
30300
+ /**
30301
+ * Tap and hold threshold timeout in ms
30302
+ * @default 250
30303
+ */
30304
+ holdThreshold;
30305
+ /**
30306
+ * object can be tap and hold
30307
+ * @default false
30308
+ */
30309
+ isHoldable;
30310
+ /**
30311
+ * true if the pointer is over the object
30312
+ * @default false
30313
+ */
30314
+ hover;
30315
+ // object has been updated (clicked,etc..)
30316
+ holdTimeout;
30317
+ released;
30318
+ /**
30319
+ * @param x - the x coordinate of the UISpriteElement Object
30320
+ * @param y - the y coordinate of the UISpriteElement Object
30321
+ * @param settings - See {@link Sprite}
30322
+ * @param settings.image - the image to use for the sprite
28932
30323
  * @example
28933
30324
  * // create a basic GUI Object
28934
30325
  * class myButton extends UISpriteElement {
@@ -28975,7 +30366,7 @@ var UISpriteElement = class extends Sprite {
28975
30366
  timer_default.clearTimer(this.holdTimeout);
28976
30367
  this.holdTimeout = timer_default.setTimeout(
28977
30368
  () => {
28978
- return this.hold();
30369
+ this.hold();
28979
30370
  },
28980
30371
  this.holdThreshold,
28981
30372
  false
@@ -28987,9 +30378,11 @@ var UISpriteElement = class extends Sprite {
28987
30378
  }
28988
30379
  /**
28989
30380
  * function called when the object is pressed (to be extended)
28990
- * @returns {boolean} return false if we need to stop propagating the event
30381
+ * @param _event - the event object
30382
+ * @returns return false if we need to stop propagating the event
28991
30383
  */
28992
- onClick() {
30384
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
30385
+ onClick(_event) {
28993
30386
  return false;
28994
30387
  }
28995
30388
  /**
@@ -28999,13 +30392,14 @@ var UISpriteElement = class extends Sprite {
28999
30392
  enter(event) {
29000
30393
  this.hover = true;
29001
30394
  this.isDirty = true;
29002
- return this.onOver(event);
30395
+ this.onOver(event);
29003
30396
  }
29004
30397
  /**
29005
30398
  * function called when the pointer is over the object
29006
- * @param {Pointer} event - the event object
30399
+ * @param _event - the event object
29007
30400
  */
29008
- onOver() {
30401
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
30402
+ onOver(_event) {
29009
30403
  }
29010
30404
  /**
29011
30405
  * function callback for the pointerLeave event
@@ -29015,20 +30409,21 @@ var UISpriteElement = class extends Sprite {
29015
30409
  this.hover = false;
29016
30410
  this.isDirty = true;
29017
30411
  this.release(event);
29018
- return this.onOut(event);
30412
+ this.onOut(event);
29019
30413
  }
29020
30414
  /**
29021
30415
  * function called when the pointer is leaving the object area
29022
- * @param {Pointer} event - the event object
30416
+ * @param _event - the event object
29023
30417
  */
29024
- onOut() {
30418
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
30419
+ onOut(_event) {
29025
30420
  }
29026
30421
  /**
29027
30422
  * function callback for the pointerup event
29028
30423
  * @ignore
29029
30424
  */
29030
30425
  release(event) {
29031
- if (this.released === false) {
30426
+ if (!this.released) {
29032
30427
  this.released = true;
29033
30428
  this.isDirty = true;
29034
30429
  timer_default.clearTimer(this.holdTimeout);
@@ -29038,9 +30433,11 @@ var UISpriteElement = class extends Sprite {
29038
30433
  }
29039
30434
  /**
29040
30435
  * function called when the object is pressed and released (to be extended)
29041
- * @returns {boolean} return false if we need to stop propagating the event
30436
+ * @param _event - the event object
30437
+ * @returns return false if we need to stop propagating the event
29042
30438
  */
29043
- onRelease() {
30439
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
30440
+ onRelease(_event) {
29044
30441
  return false;
29045
30442
  }
29046
30443
  /**
@@ -29076,10 +30473,10 @@ var UISpriteElement = class extends Sprite {
29076
30473
  return this.release(e);
29077
30474
  });
29078
30475
  registerPointerEvent("pointerenter", this, (e) => {
29079
- return this.enter(e);
30476
+ this.enter(e);
29080
30477
  });
29081
30478
  registerPointerEvent("pointerleave", this, (e) => {
29082
- return this.leave(e);
30479
+ this.leave(e);
29083
30480
  });
29084
30481
  }
29085
30482
  /**
@@ -29097,28 +30494,56 @@ var UISpriteElement = class extends Sprite {
29097
30494
  }
29098
30495
  };
29099
30496
 
29100
- // src/renderable/ui/uitextbutton.js
30497
+ // src/renderable/ui/uitextbutton.ts
29101
30498
  var UITextButton = class extends UIBaseElement {
30499
+ /**
30500
+ * The key to bind the action to
30501
+ */
30502
+ bindKey;
30503
+ /**
30504
+ * The css value of a color to be used if the pointer is not hovering over the button
30505
+ */
30506
+ hoverOffColor;
30507
+ /**
30508
+ * The css value of a color to be used if the pointer hovers over the button
30509
+ */
30510
+ hoverOnColor;
30511
+ /**
30512
+ * The css value of a color to be used to draw the border
30513
+ */
30514
+ borderStrokeColor;
30515
+ /**
30516
+ * Set the default text alignment (or justification),<br>
30517
+ * possible values are "left", "right", and "center".
30518
+ * @default "center"
30519
+ */
30520
+ textAlign;
30521
+ /**
30522
+ * Set the text baseline (e.g. the Y-coordinate for the draw operation), <br>
30523
+ * possible values are "top", "hanging", "middle", "alphabetic", "ideographic", "bottom"<br>
30524
+ * @default "middle"
30525
+ */
30526
+ textBaseline;
30527
+ /**
30528
+ * the bitmapText used by the UITextButton class
30529
+ */
30530
+ bitmapText;
30531
+ /**
30532
+ * the measured text dimensions
30533
+ */
30534
+ dimensions;
30535
+ /**
30536
+ * the round rect border
30537
+ */
30538
+ border;
29102
30539
  /**
29103
30540
  * A Bitmap Text Button with an outlined background border, filled with background color.
29104
30541
  * It uses a RoundRect as background and changes the background color on hovering over.
29105
30542
  * The background will be drawn with 0.5 opacity, so that the background of the button is
29106
30543
  * slightly shining through.
29107
- * @param {number} x - x pos of the button
29108
- * @param {number} y - y pos of the button
29109
- * @param {Object} settings - settings object
29110
- * @param {string} [settings.font] - The name of the BitmapText font to use
29111
- * @param {number} [settings.size=1] - The scale factor of the BitmapText
29112
- * @param {string} [settings.text] - The text to display
29113
- * @param {string} [settings.bindKey] - The key to bind the action to (default: none)
29114
- * @param {string} [settings.hoverOffColor="#00aa0080"] - The css value of a color to be used if the pointer is not hovering over the button
29115
- * @param {string} [settings.hoverOnColor="#00ff00ff"] - The css value of a color to be used if the pointer hovers over the button
29116
- * @param {string} [settings.borderStrokeColor="#000000"] - The css value of a color to be used to draw the border
29117
- * @param {string} [settings.fillStyle] - The css value of a tint color to be used to tint the BitmapText
29118
- * @param {string} [settings.textAlign="center"] - horizontal text alignment
29119
- * @param {string} [settings.textBaseline="middle"] - the text baseline
29120
- * @param {number} [settings.borderWidth] - Width of the button
29121
- * @param {number} [settings.borderHeight] - Height of the button
30544
+ * @param x - x pos of the button
30545
+ * @param y - y pos of the button
30546
+ * @param settings - settings object
29122
30547
  * @example
29123
30548
  * // Create a new Button
29124
30549
  * class PlayButton extends UITextButton {
@@ -29164,14 +30589,15 @@ var UITextButton = class extends UIBaseElement {
29164
30589
  this.addChild(this.bitmapText);
29165
30590
  }
29166
30591
  draw(renderer2) {
29167
- if (this.hover === true) {
29168
- renderer2.setColor(this.hoverOnColor);
30592
+ const r = renderer2;
30593
+ if (this.hover) {
30594
+ r.setColor(this.hoverOnColor);
29169
30595
  } else {
29170
- renderer2.setColor(this.hoverOffColor);
30596
+ r.setColor(this.hoverOffColor);
29171
30597
  }
29172
- renderer2.fill(this.border);
29173
- renderer2.setColor(this.borderStrokeColor);
29174
- renderer2.stroke(this.border);
30598
+ r.fill(this.border);
30599
+ r.setColor(this.borderStrokeColor);
30600
+ r.stroke(this.border);
29175
30601
  super.draw(renderer2);
29176
30602
  }
29177
30603
  };