melonjs 19.4.0 → 19.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +18 -2
- package/build/application/application.d.ts.map +1 -1
- package/build/application/settings.d.ts +25 -2
- package/build/application/settings.d.ts.map +1 -1
- package/build/audio/audio.d.ts +77 -253
- package/build/audio/audio.d.ts.map +1 -1
- package/build/audio/backend.d.ts +121 -0
- package/build/audio/backend.d.ts.map +1 -0
- package/build/audio/playback.d.ts +157 -0
- package/build/audio/playback.d.ts.map +1 -0
- package/build/audio/procedural.d.ts +105 -0
- package/build/audio/procedural.d.ts.map +1 -0
- package/build/audio/types.d.ts +205 -0
- package/build/audio/types.d.ts.map +1 -0
- package/build/index.d.ts +6 -3
- package/build/index.d.ts.map +1 -1
- package/build/index.js +2273 -379
- package/build/index.js.map +4 -4
- package/build/level/tiled/TMXTileMap.d.ts.map +1 -1
- package/build/level/tiled/TMXTileset.d.ts +12 -0
- package/build/level/tiled/TMXTileset.d.ts.map +1 -1
- package/build/level/tiled/factories/shape.d.ts +1 -1
- package/build/level/tiled/factories/shape.d.ts.map +1 -1
- package/build/level/tiled/factories/tile.d.ts.map +1 -1
- package/build/loader/loader.d.ts +2 -2
- package/build/loader/loader.d.ts.map +1 -1
- package/build/loader/parsers/aseprite.d.ts +37 -0
- package/build/loader/parsers/aseprite.d.ts.map +1 -0
- package/build/physics/adapter.d.ts +560 -0
- package/build/physics/adapter.d.ts.map +1 -0
- package/build/physics/bounds.d.ts +18 -5
- package/build/physics/bounds.d.ts.map +1 -1
- package/build/physics/builtin/body.d.ts +605 -0
- package/build/physics/builtin/body.d.ts.map +1 -0
- package/build/physics/builtin/builtin-adapter.d.ts +91 -0
- package/build/physics/builtin/builtin-adapter.d.ts.map +1 -0
- package/build/physics/builtin/detector.d.ts +167 -0
- package/build/physics/builtin/detector.d.ts.map +1 -0
- package/build/physics/builtin/quadtree.d.ts +112 -0
- package/build/physics/builtin/quadtree.d.ts.map +1 -0
- package/build/physics/builtin/raycast.d.ts +4 -0
- package/build/physics/builtin/raycast.d.ts.map +1 -0
- package/build/physics/{sat.d.ts → builtin/sat.d.ts} +7 -7
- package/build/physics/builtin/sat.d.ts.map +1 -0
- package/build/physics/world.d.ts +61 -26
- package/build/physics/world.d.ts.map +1 -1
- package/build/renderable/collectable.d.ts +7 -1
- package/build/renderable/collectable.d.ts.map +1 -1
- package/build/renderable/container.d.ts +0 -13
- package/build/renderable/container.d.ts.map +1 -1
- package/build/renderable/renderable.d.ts +78 -17
- package/build/renderable/renderable.d.ts.map +1 -1
- package/build/renderable/trigger.d.ts +14 -1
- package/build/renderable/trigger.d.ts.map +1 -1
- package/build/renderable/ui/uispriteelement.d.ts.map +1 -1
- package/build/system/timer.d.ts +0 -5
- package/build/system/timer.d.ts.map +1 -1
- package/build/video/canvas/canvas_renderer.d.ts +0 -130
- package/build/video/canvas/canvas_renderer.d.ts.map +1 -1
- package/build/video/renderer.d.ts +111 -0
- package/build/video/renderer.d.ts.map +1 -1
- package/build/video/rendertarget/canvasrendertarget.d.ts.map +1 -1
- package/build/video/webgl/batchers/material_batcher.d.ts.map +1 -1
- package/build/video/webgl/effects/shine.d.ts +87 -0
- package/build/video/webgl/effects/shine.d.ts.map +1 -0
- package/build/video/webgl/webgl_renderer.d.ts +0 -106
- package/build/video/webgl/webgl_renderer.d.ts.map +1 -1
- package/package.json +1 -1
- package/build/physics/body.d.ts +0 -351
- package/build/physics/body.d.ts.map +0 -1
- package/build/physics/detector.d.ts +0 -72
- package/build/physics/detector.d.ts.map +0 -1
- package/build/physics/quadtree.d.ts +0 -69
- package/build/physics/quadtree.d.ts.map +0 -1
- package/build/physics/sat.d.ts.map +0 -1
package/build/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* melonJS Game Engine - 19.
|
|
2
|
+
* melonJS Game Engine - 19.5.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
|
|
@@ -799,11 +799,11 @@ var require_internal_state = __commonJS({
|
|
|
799
799
|
};
|
|
800
800
|
var getterFor = function(TYPE) {
|
|
801
801
|
return function(it) {
|
|
802
|
-
var
|
|
803
|
-
if (!isObject(it) || (
|
|
802
|
+
var state3;
|
|
803
|
+
if (!isObject(it) || (state3 = get2(it)).type !== TYPE) {
|
|
804
804
|
throw new TypeError2("Incompatible receiver, " + TYPE + " required");
|
|
805
805
|
}
|
|
806
|
-
return
|
|
806
|
+
return state3;
|
|
807
807
|
};
|
|
808
808
|
};
|
|
809
809
|
if (NATIVE_WEAK_MAP || shared.state) {
|
|
@@ -894,9 +894,9 @@ var require_make_built_in = __commonJS({
|
|
|
894
894
|
} else if (value.prototype) value.prototype = void 0;
|
|
895
895
|
} catch (error) {
|
|
896
896
|
}
|
|
897
|
-
var
|
|
898
|
-
if (!hasOwn(
|
|
899
|
-
|
|
897
|
+
var state3 = enforceInternalState(value);
|
|
898
|
+
if (!hasOwn(state3, "source")) {
|
|
899
|
+
state3.source = join(TEMPLATE, typeof name == "string" ? name : "");
|
|
900
900
|
}
|
|
901
901
|
return value;
|
|
902
902
|
};
|
|
@@ -1819,7 +1819,7 @@ var require_howler = __commonJS({
|
|
|
1819
1819
|
}
|
|
1820
1820
|
};
|
|
1821
1821
|
var Howler3 = new HowlerGlobal2();
|
|
1822
|
-
var
|
|
1822
|
+
var Howl4 = function(o) {
|
|
1823
1823
|
var self2 = this;
|
|
1824
1824
|
if (!o.src || o.src.length === 0) {
|
|
1825
1825
|
console.error("An array of source files must be passed with any new Howl.");
|
|
@@ -1827,7 +1827,7 @@ var require_howler = __commonJS({
|
|
|
1827
1827
|
}
|
|
1828
1828
|
self2.init(o);
|
|
1829
1829
|
};
|
|
1830
|
-
|
|
1830
|
+
Howl4.prototype = {
|
|
1831
1831
|
/**
|
|
1832
1832
|
* Initialize a new Howl group object.
|
|
1833
1833
|
* @param {Object} o Passed in properties for this group.
|
|
@@ -3245,23 +3245,23 @@ var require_howler = __commonJS({
|
|
|
3245
3245
|
define([], function() {
|
|
3246
3246
|
return {
|
|
3247
3247
|
Howler: Howler3,
|
|
3248
|
-
Howl:
|
|
3248
|
+
Howl: Howl4
|
|
3249
3249
|
};
|
|
3250
3250
|
});
|
|
3251
3251
|
}
|
|
3252
3252
|
if (typeof exports !== "undefined") {
|
|
3253
3253
|
exports.Howler = Howler3;
|
|
3254
|
-
exports.Howl =
|
|
3254
|
+
exports.Howl = Howl4;
|
|
3255
3255
|
}
|
|
3256
3256
|
if (typeof global !== "undefined") {
|
|
3257
3257
|
global.HowlerGlobal = HowlerGlobal2;
|
|
3258
3258
|
global.Howler = Howler3;
|
|
3259
|
-
global.Howl =
|
|
3259
|
+
global.Howl = Howl4;
|
|
3260
3260
|
global.Sound = Sound2;
|
|
3261
3261
|
} else if (typeof window !== "undefined") {
|
|
3262
3262
|
window.HowlerGlobal = HowlerGlobal2;
|
|
3263
3263
|
window.Howler = Howler3;
|
|
3264
|
-
window.Howl =
|
|
3264
|
+
window.Howl = Howl4;
|
|
3265
3265
|
window.Sound = Sound2;
|
|
3266
3266
|
}
|
|
3267
3267
|
})();
|
|
@@ -4853,6 +4853,30 @@ var Bounds = class _Bounds {
|
|
|
4853
4853
|
}
|
|
4854
4854
|
}
|
|
4855
4855
|
}
|
|
4856
|
+
/**
|
|
4857
|
+
* Expand this bounds to include every shape in `shapes`. Each shape
|
|
4858
|
+
* contributes its own `.getBounds()` (so `Rect`, `Polygon`, `Ellipse`,
|
|
4859
|
+
* `Bounds`, and anything else implementing the same getter all work);
|
|
4860
|
+
* shapes without `.getBounds()` are silently ignored. Use `clear=true`
|
|
4861
|
+
* to compute a fresh union (matches the `add` / `addBounds` shape).
|
|
4862
|
+
*
|
|
4863
|
+
* Useful for sizing a renderable from its `bodyDef.shapes` before
|
|
4864
|
+
* the underlying physics body has been constructed — used by the
|
|
4865
|
+
* TMX shape factory and {@link Trigger}.
|
|
4866
|
+
* @param shapes - one shape or an iterable of shapes
|
|
4867
|
+
* @param [clear] - reset the bounds before unioning
|
|
4868
|
+
*/
|
|
4869
|
+
addShapes(shapes, clear = false) {
|
|
4870
|
+
if (clear) {
|
|
4871
|
+
this.clear();
|
|
4872
|
+
}
|
|
4873
|
+
const iterable = typeof shapes[Symbol.iterator] === "function" ? shapes : [shapes];
|
|
4874
|
+
for (const s of iterable) {
|
|
4875
|
+
if (typeof s?.getBounds === "function") {
|
|
4876
|
+
this.addBounds(s.getBounds());
|
|
4877
|
+
}
|
|
4878
|
+
}
|
|
4879
|
+
}
|
|
4856
4880
|
/**
|
|
4857
4881
|
* Adds the given point to the bounds definition.
|
|
4858
4882
|
* @param point - The point to add to the bounds.
|
|
@@ -4977,18 +5001,6 @@ var Bounds = class _Bounds {
|
|
|
4977
5001
|
bounds.addBounds(this);
|
|
4978
5002
|
return bounds;
|
|
4979
5003
|
}
|
|
4980
|
-
/**
|
|
4981
|
-
* Returns a polygon whose edges are the same as this bounds.
|
|
4982
|
-
* @returns A new Polygon that represents this bounds.
|
|
4983
|
-
*/
|
|
4984
|
-
toPolygon() {
|
|
4985
|
-
return polygonPool.get(this.x, this.y, [
|
|
4986
|
-
new Vector2d(0, 0),
|
|
4987
|
-
new Vector2d(this.width, 0),
|
|
4988
|
-
new Vector2d(this.width, this.height),
|
|
4989
|
-
new Vector2d(0, this.height)
|
|
4990
|
-
]);
|
|
4991
|
-
}
|
|
4992
5004
|
};
|
|
4993
5005
|
var boundsPool = createPool((vertices) => {
|
|
4994
5006
|
const instance = new Bounds(vertices);
|
|
@@ -10032,7 +10044,9 @@ __export(audio_exports, {
|
|
|
10032
10044
|
disable: () => disable,
|
|
10033
10045
|
enable: () => enable,
|
|
10034
10046
|
fade: () => fade,
|
|
10047
|
+
getAudioContext: () => getAudioContext,
|
|
10035
10048
|
getCurrentTrack: () => getCurrentTrack,
|
|
10049
|
+
getMasterGain: () => getMasterGain,
|
|
10036
10050
|
getVolume: () => getVolume,
|
|
10037
10051
|
hasAudio: () => hasAudio,
|
|
10038
10052
|
hasFormat: () => hasFormat,
|
|
@@ -10041,6 +10055,7 @@ __export(audio_exports, {
|
|
|
10041
10055
|
mute: () => mute,
|
|
10042
10056
|
muteAll: () => muteAll,
|
|
10043
10057
|
muted: () => muted,
|
|
10058
|
+
noise: () => noise,
|
|
10044
10059
|
orientation: () => orientation,
|
|
10045
10060
|
panner: () => panner,
|
|
10046
10061
|
pause: () => pause,
|
|
@@ -10057,51 +10072,83 @@ __export(audio_exports, {
|
|
|
10057
10072
|
stop: () => stop,
|
|
10058
10073
|
stopOnAudioError: () => stopOnAudioError,
|
|
10059
10074
|
stopTrack: () => stopTrack,
|
|
10075
|
+
tone: () => tone,
|
|
10060
10076
|
unload: () => unload,
|
|
10061
10077
|
unloadAll: () => unloadAll,
|
|
10062
10078
|
unmute: () => unmute,
|
|
10063
10079
|
unmuteAll: () => unmuteAll
|
|
10064
10080
|
});
|
|
10081
|
+
|
|
10082
|
+
// src/audio/backend.ts
|
|
10065
10083
|
var import_howler = __toESM(require_howler(), 1);
|
|
10066
|
-
var
|
|
10067
|
-
var
|
|
10068
|
-
|
|
10069
|
-
|
|
10070
|
-
|
|
10071
|
-
|
|
10084
|
+
var stopOnAudioError = true;
|
|
10085
|
+
var state = {
|
|
10086
|
+
tracks: {},
|
|
10087
|
+
currentTrackId: null,
|
|
10088
|
+
retryCounter: 0,
|
|
10089
|
+
audioExts: []
|
|
10090
|
+
};
|
|
10091
|
+
function getSoundOrThrow(sound_name) {
|
|
10092
|
+
const sound2 = state.tracks[sound_name];
|
|
10093
|
+
if (!sound2) {
|
|
10094
|
+
throw new Error(`audio clip ${sound_name} does not exist`);
|
|
10095
|
+
}
|
|
10096
|
+
return sound2;
|
|
10097
|
+
}
|
|
10098
|
+
var soundLoadError = function(sound_name, onerror_cb, stopOnError = true) {
|
|
10099
|
+
if (state.retryCounter++ >= 3) {
|
|
10072
10100
|
const errmsg = `melonJS: failed loading ${sound_name}`;
|
|
10073
|
-
if (!
|
|
10074
|
-
|
|
10101
|
+
if (!stopOnError) {
|
|
10102
|
+
import_howler.Howler.mute(true);
|
|
10075
10103
|
onerror_cb?.();
|
|
10076
|
-
console.
|
|
10104
|
+
console.warn(`${errmsg}, disabling audio`);
|
|
10077
10105
|
} else {
|
|
10078
10106
|
onerror_cb?.();
|
|
10079
10107
|
throw new Error(errmsg);
|
|
10080
10108
|
}
|
|
10081
10109
|
} else {
|
|
10082
|
-
|
|
10110
|
+
state.tracks[sound_name]?.load();
|
|
10083
10111
|
}
|
|
10084
10112
|
};
|
|
10085
|
-
|
|
10086
|
-
|
|
10087
|
-
|
|
10088
|
-
|
|
10113
|
+
function getAudioContext() {
|
|
10114
|
+
if (import_howler.Howler.noAudio) return null;
|
|
10115
|
+
if (import_howler.Howler.usingWebAudio && !import_howler.Howler.ctx) {
|
|
10116
|
+
import_howler.Howler.volume(import_howler.Howler.volume());
|
|
10117
|
+
}
|
|
10118
|
+
return import_howler.Howler.ctx ?? null;
|
|
10089
10119
|
}
|
|
10090
|
-
function
|
|
10091
|
-
|
|
10120
|
+
function getMasterGain() {
|
|
10121
|
+
if (!getAudioContext()) return null;
|
|
10122
|
+
return import_howler.Howler.masterGain ?? null;
|
|
10092
10123
|
}
|
|
10093
|
-
function
|
|
10094
|
-
return
|
|
10124
|
+
function getGlobalVolume() {
|
|
10125
|
+
return import_howler.Howler.volume();
|
|
10095
10126
|
}
|
|
10096
|
-
function
|
|
10097
|
-
|
|
10127
|
+
function setGlobalVolume(v) {
|
|
10128
|
+
import_howler.Howler.volume(v);
|
|
10098
10129
|
}
|
|
10099
|
-
function
|
|
10100
|
-
|
|
10130
|
+
function setGlobalMuted(muted2) {
|
|
10131
|
+
import_howler.Howler.mute(muted2);
|
|
10132
|
+
}
|
|
10133
|
+
function isGlobalMuted() {
|
|
10134
|
+
return import_howler.Howler._muted;
|
|
10135
|
+
}
|
|
10136
|
+
function stopAllPlayback() {
|
|
10137
|
+
import_howler.Howler.stop();
|
|
10138
|
+
}
|
|
10139
|
+
function hasCodec(codec) {
|
|
10140
|
+
if (!isAudioAvailable()) return false;
|
|
10141
|
+
return import_howler.Howler.codecs(codec) === true;
|
|
10101
10142
|
}
|
|
10143
|
+
function isAudioAvailable() {
|
|
10144
|
+
return !import_howler.Howler.noAudio;
|
|
10145
|
+
}
|
|
10146
|
+
|
|
10147
|
+
// src/audio/playback.ts
|
|
10148
|
+
var import_howler2 = __toESM(require_howler(), 1);
|
|
10102
10149
|
function load(sound2, onloadcb, onerrorcb, settings = {}) {
|
|
10103
10150
|
const urls = [];
|
|
10104
|
-
if (audioExts.length === 0) {
|
|
10151
|
+
if (state.audioExts.length === 0) {
|
|
10105
10152
|
throw new Error(
|
|
10106
10153
|
"target audio extension(s) should be set through me.audio.init() before calling the preloader."
|
|
10107
10154
|
);
|
|
@@ -10109,25 +10156,25 @@ function load(sound2, onloadcb, onerrorcb, settings = {}) {
|
|
|
10109
10156
|
if (isDataUrl(sound2.src)) {
|
|
10110
10157
|
urls.push(sound2.src);
|
|
10111
10158
|
} else {
|
|
10112
|
-
for (let i = 0; i < audioExts.length; i++) {
|
|
10159
|
+
for (let i = 0; i < state.audioExts.length; i++) {
|
|
10113
10160
|
urls.push(
|
|
10114
|
-
`${sound2.src + sound2.name}.${audioExts[i]}${settings.nocache ?? ""}`
|
|
10161
|
+
`${sound2.src + sound2.name}.${state.audioExts[i]}${settings.nocache ?? ""}`
|
|
10115
10162
|
);
|
|
10116
10163
|
}
|
|
10117
10164
|
}
|
|
10118
|
-
|
|
10165
|
+
state.tracks[sound2.name] = new import_howler2.Howl({
|
|
10119
10166
|
src: urls,
|
|
10120
|
-
volume:
|
|
10167
|
+
volume: getGlobalVolume(),
|
|
10121
10168
|
autoplay: sound2.autoplay === true,
|
|
10122
|
-
loop: sound2.loop
|
|
10169
|
+
loop: sound2.loop === true,
|
|
10123
10170
|
html5: sound2.stream === true || sound2.html5 === true,
|
|
10124
10171
|
// @ts-expect-error xhrWithCredentials is a valid Howl option but not in the type definitions
|
|
10125
10172
|
xhrWithCredentials: settings.withCredentials,
|
|
10126
10173
|
onloaderror() {
|
|
10127
|
-
soundLoadError.call(this, sound2.name, onerrorcb);
|
|
10174
|
+
soundLoadError.call(this, sound2.name, onerrorcb, stopOnAudioError);
|
|
10128
10175
|
},
|
|
10129
10176
|
onload() {
|
|
10130
|
-
|
|
10177
|
+
state.retryCounter = 0;
|
|
10131
10178
|
if (typeof onloadcb === "function") {
|
|
10132
10179
|
onloadcb();
|
|
10133
10180
|
}
|
|
@@ -10136,176 +10183,310 @@ function load(sound2, onloadcb, onerrorcb, settings = {}) {
|
|
|
10136
10183
|
return 1;
|
|
10137
10184
|
}
|
|
10138
10185
|
function play(sound_name, loop = false, onend, volume) {
|
|
10139
|
-
const sound2 =
|
|
10140
|
-
|
|
10141
|
-
|
|
10142
|
-
|
|
10143
|
-
|
|
10144
|
-
|
|
10145
|
-
|
|
10146
|
-
|
|
10147
|
-
|
|
10148
|
-
|
|
10149
|
-
|
|
10150
|
-
|
|
10151
|
-
sound2.on("end", onend, id);
|
|
10152
|
-
} else {
|
|
10153
|
-
sound2.once("end", onend, id);
|
|
10154
|
-
}
|
|
10186
|
+
const sound2 = getSoundOrThrow(sound_name);
|
|
10187
|
+
const id = sound2.play();
|
|
10188
|
+
sound2.loop(loop, id);
|
|
10189
|
+
sound2.volume(
|
|
10190
|
+
typeof volume === "number" ? clamp(volume, 0, 1) : getGlobalVolume(),
|
|
10191
|
+
id
|
|
10192
|
+
);
|
|
10193
|
+
if (typeof onend === "function") {
|
|
10194
|
+
if (loop) {
|
|
10195
|
+
sound2.on("end", onend, id);
|
|
10196
|
+
} else {
|
|
10197
|
+
sound2.once("end", onend, id);
|
|
10155
10198
|
}
|
|
10156
|
-
return id;
|
|
10157
|
-
} else {
|
|
10158
|
-
throw new Error(`audio clip ${sound_name} does not exist`);
|
|
10159
10199
|
}
|
|
10200
|
+
return id;
|
|
10160
10201
|
}
|
|
10161
10202
|
function fade(sound_name, from, to, duration, id) {
|
|
10162
|
-
|
|
10163
|
-
if (sound2 && typeof sound2 !== "undefined") {
|
|
10164
|
-
sound2.fade(from, to, duration, id);
|
|
10165
|
-
} else {
|
|
10166
|
-
throw new Error(`audio clip ${sound_name} does not exist`);
|
|
10167
|
-
}
|
|
10203
|
+
getSoundOrThrow(sound_name).fade(from, to, duration, id);
|
|
10168
10204
|
}
|
|
10169
10205
|
function seek(sound_name, ...args) {
|
|
10170
|
-
|
|
10171
|
-
if (sound2 && typeof sound2 !== "undefined") {
|
|
10172
|
-
return sound2.seek(...args);
|
|
10173
|
-
} else {
|
|
10174
|
-
throw new Error(`audio clip ${sound_name} does not exist`);
|
|
10175
|
-
}
|
|
10206
|
+
return getSoundOrThrow(sound_name).seek(...args);
|
|
10176
10207
|
}
|
|
10177
10208
|
function rate(sound_name, ...args) {
|
|
10178
|
-
|
|
10179
|
-
if (sound2 && typeof sound2 !== "undefined") {
|
|
10180
|
-
return sound2.rate(...args);
|
|
10181
|
-
} else {
|
|
10182
|
-
throw new Error(`audio clip ${sound_name} does not exist`);
|
|
10183
|
-
}
|
|
10209
|
+
return getSoundOrThrow(sound_name).rate(...args);
|
|
10184
10210
|
}
|
|
10185
10211
|
function stereo(sound_name, pan, id) {
|
|
10186
|
-
const sound2 =
|
|
10187
|
-
if (
|
|
10188
|
-
return
|
|
10189
|
-
} else {
|
|
10190
|
-
throw new Error(`audio clip ${sound_name} does not exist`);
|
|
10212
|
+
const sound2 = getSoundOrThrow(sound_name);
|
|
10213
|
+
if (pan === void 0) {
|
|
10214
|
+
return sound2.stereo();
|
|
10191
10215
|
}
|
|
10216
|
+
sound2.stereo(pan, id);
|
|
10192
10217
|
}
|
|
10193
10218
|
function position(sound_name, x, y, z, id) {
|
|
10194
|
-
const sound2 =
|
|
10195
|
-
if (
|
|
10196
|
-
return sound2.pos(
|
|
10197
|
-
} else {
|
|
10198
|
-
throw new Error(`audio clip ${sound_name} does not exist`);
|
|
10219
|
+
const sound2 = getSoundOrThrow(sound_name);
|
|
10220
|
+
if (x === void 0) {
|
|
10221
|
+
return sound2.pos();
|
|
10199
10222
|
}
|
|
10223
|
+
sound2.pos(x, y, z, id);
|
|
10200
10224
|
}
|
|
10201
10225
|
function orientation(sound_name, x, y, z, id) {
|
|
10202
|
-
const sound2 =
|
|
10203
|
-
if (
|
|
10204
|
-
return sound2.orientation(
|
|
10205
|
-
} else {
|
|
10206
|
-
throw new Error(`audio clip ${sound_name} does not exist`);
|
|
10226
|
+
const sound2 = getSoundOrThrow(sound_name);
|
|
10227
|
+
if (x === void 0) {
|
|
10228
|
+
return sound2.orientation();
|
|
10207
10229
|
}
|
|
10230
|
+
sound2.orientation(x, y, z, id);
|
|
10208
10231
|
}
|
|
10209
10232
|
function panner(sound_name, attributes, id) {
|
|
10210
|
-
const sound2 =
|
|
10211
|
-
if (
|
|
10212
|
-
|
|
10213
|
-
|
|
10214
|
-
|
|
10215
|
-
);
|
|
10216
|
-
} else {
|
|
10217
|
-
throw new Error(`audio clip ${sound_name} does not exist`);
|
|
10233
|
+
const sound2 = getSoundOrThrow(sound_name);
|
|
10234
|
+
if (attributes !== void 0) {
|
|
10235
|
+
const attrs = attributes;
|
|
10236
|
+
if (id !== void 0) sound2.pannerAttr(attrs, id);
|
|
10237
|
+
else sound2.pannerAttr(attrs);
|
|
10218
10238
|
}
|
|
10239
|
+
return id !== void 0 ? sound2.pannerAttr(id) : sound2.pannerAttr();
|
|
10219
10240
|
}
|
|
10220
10241
|
function stop(sound_name, id) {
|
|
10221
|
-
if (
|
|
10222
|
-
|
|
10223
|
-
|
|
10224
|
-
sound2.stop(id);
|
|
10225
|
-
sound2.off("end", void 0, id);
|
|
10226
|
-
} else {
|
|
10227
|
-
throw new Error(`audio clip ${sound_name} does not exist`);
|
|
10228
|
-
}
|
|
10229
|
-
} else {
|
|
10230
|
-
import_howler.Howler.stop();
|
|
10242
|
+
if (sound_name === void 0) {
|
|
10243
|
+
stopAllPlayback();
|
|
10244
|
+
return;
|
|
10231
10245
|
}
|
|
10246
|
+
const sound2 = getSoundOrThrow(sound_name);
|
|
10247
|
+
sound2.stop(id);
|
|
10248
|
+
sound2.off("end", void 0, id);
|
|
10232
10249
|
}
|
|
10233
10250
|
function pause(sound_name, id) {
|
|
10234
|
-
|
|
10235
|
-
if (sound2 && typeof sound2 !== "undefined") {
|
|
10236
|
-
sound2.pause(id);
|
|
10237
|
-
} else {
|
|
10238
|
-
throw new Error(`audio clip ${sound_name} does not exist`);
|
|
10239
|
-
}
|
|
10251
|
+
getSoundOrThrow(sound_name).pause(id);
|
|
10240
10252
|
}
|
|
10241
10253
|
function resume(sound_name, id) {
|
|
10242
|
-
|
|
10243
|
-
|
|
10244
|
-
|
|
10254
|
+
getSoundOrThrow(sound_name).play(id);
|
|
10255
|
+
}
|
|
10256
|
+
|
|
10257
|
+
// src/audio/procedural.ts
|
|
10258
|
+
function _resumeIfSuspended(ctx) {
|
|
10259
|
+
if (ctx.state === "suspended") {
|
|
10260
|
+
ctx.resume().catch(() => {
|
|
10261
|
+
});
|
|
10262
|
+
}
|
|
10263
|
+
}
|
|
10264
|
+
function _buildGainEnvelope(ctx, t0, t1, attack, duration, gain) {
|
|
10265
|
+
const atk = Math.min(duration / 2, Math.max(1e-3, attack));
|
|
10266
|
+
const env = ctx.createGain();
|
|
10267
|
+
env.gain.setValueAtTime(0, t0);
|
|
10268
|
+
env.gain.linearRampToValueAtTime(gain, t0 + atk);
|
|
10269
|
+
if (gain > 1e-4) {
|
|
10270
|
+
env.gain.exponentialRampToValueAtTime(1e-4, t1);
|
|
10245
10271
|
} else {
|
|
10246
|
-
|
|
10272
|
+
env.gain.linearRampToValueAtTime(0, t1);
|
|
10247
10273
|
}
|
|
10274
|
+
return env;
|
|
10275
|
+
}
|
|
10276
|
+
function _connectToOutput(ctx, source, pan, t0) {
|
|
10277
|
+
const out = getMasterGain() ?? ctx.destination;
|
|
10278
|
+
if (pan === 0) {
|
|
10279
|
+
source.connect(out);
|
|
10280
|
+
return null;
|
|
10281
|
+
}
|
|
10282
|
+
const panner2 = ctx.createStereoPanner();
|
|
10283
|
+
panner2.pan.setValueAtTime(Math.max(-1, Math.min(1, pan)), t0);
|
|
10284
|
+
source.connect(panner2).connect(out);
|
|
10285
|
+
return panner2;
|
|
10286
|
+
}
|
|
10287
|
+
function tone(opts) {
|
|
10288
|
+
const ctx = getAudioContext();
|
|
10289
|
+
if (!ctx) return;
|
|
10290
|
+
const {
|
|
10291
|
+
freq,
|
|
10292
|
+
duration,
|
|
10293
|
+
wave = "sine",
|
|
10294
|
+
gain = 0.1,
|
|
10295
|
+
attack = 5e-3,
|
|
10296
|
+
pan = 0,
|
|
10297
|
+
pitchSlide = 1
|
|
10298
|
+
} = opts;
|
|
10299
|
+
const freqs = Array.isArray(freq) ? freq : [freq];
|
|
10300
|
+
if (freqs.length === 0) return;
|
|
10301
|
+
_resumeIfSuspended(ctx);
|
|
10302
|
+
const dur = Math.max(1e-3, duration);
|
|
10303
|
+
const t0 = ctx.currentTime;
|
|
10304
|
+
const t1 = t0 + dur;
|
|
10305
|
+
const env = _buildGainEnvelope(ctx, t0, t1, attack, dur, gain);
|
|
10306
|
+
const panner2 = _connectToOutput(ctx, env, pan, t0);
|
|
10307
|
+
let remaining = freqs.length;
|
|
10308
|
+
for (const f of freqs) {
|
|
10309
|
+
const osc = ctx.createOscillator();
|
|
10310
|
+
osc.type = wave;
|
|
10311
|
+
osc.frequency.setValueAtTime(f, t0);
|
|
10312
|
+
if (pitchSlide !== 1 && f > 0) {
|
|
10313
|
+
osc.frequency.exponentialRampToValueAtTime(
|
|
10314
|
+
Math.max(0.01, f * pitchSlide),
|
|
10315
|
+
t1
|
|
10316
|
+
);
|
|
10317
|
+
}
|
|
10318
|
+
osc.connect(env);
|
|
10319
|
+
osc.start(t0);
|
|
10320
|
+
osc.stop(t1 + 0.02);
|
|
10321
|
+
osc.onended = () => {
|
|
10322
|
+
osc.disconnect();
|
|
10323
|
+
if (--remaining === 0) {
|
|
10324
|
+
env.disconnect();
|
|
10325
|
+
panner2?.disconnect();
|
|
10326
|
+
}
|
|
10327
|
+
};
|
|
10328
|
+
}
|
|
10329
|
+
}
|
|
10330
|
+
function fillNoiseBuffer(data2, type) {
|
|
10331
|
+
const n = data2.length;
|
|
10332
|
+
if (type === "white") {
|
|
10333
|
+
for (let i = 0; i < n; i++) {
|
|
10334
|
+
data2[i] = Math.random() * 2 - 1;
|
|
10335
|
+
}
|
|
10336
|
+
return;
|
|
10337
|
+
}
|
|
10338
|
+
if (type === "pink") {
|
|
10339
|
+
let b0 = 0;
|
|
10340
|
+
let b1 = 0;
|
|
10341
|
+
let b2 = 0;
|
|
10342
|
+
let b3 = 0;
|
|
10343
|
+
let b4 = 0;
|
|
10344
|
+
let b5 = 0;
|
|
10345
|
+
let b6 = 0;
|
|
10346
|
+
for (let i = 0; i < n; i++) {
|
|
10347
|
+
const w = Math.random() * 2 - 1;
|
|
10348
|
+
b0 = 0.99886 * b0 + w * 0.0555179;
|
|
10349
|
+
b1 = 0.99332 * b1 + w * 0.0750759;
|
|
10350
|
+
b2 = 0.969 * b2 + w * 0.153852;
|
|
10351
|
+
b3 = 0.8665 * b3 + w * 0.3104856;
|
|
10352
|
+
b4 = 0.55 * b4 + w * 0.5329522;
|
|
10353
|
+
b5 = -0.7616 * b5 - w * 0.016898;
|
|
10354
|
+
data2[i] = (b0 + b1 + b2 + b3 + b4 + b5 + b6 + w * 0.5362) * 0.11;
|
|
10355
|
+
b6 = w * 0.115926;
|
|
10356
|
+
}
|
|
10357
|
+
return;
|
|
10358
|
+
}
|
|
10359
|
+
let last = 0;
|
|
10360
|
+
for (let i = 0; i < n; i++) {
|
|
10361
|
+
const w = Math.random() * 2 - 1;
|
|
10362
|
+
last = (last + 0.02 * w) / 1.02;
|
|
10363
|
+
data2[i] = last * 3.5;
|
|
10364
|
+
}
|
|
10365
|
+
}
|
|
10366
|
+
function noise(opts) {
|
|
10367
|
+
const ctx = getAudioContext();
|
|
10368
|
+
if (!ctx) return;
|
|
10369
|
+
const {
|
|
10370
|
+
duration,
|
|
10371
|
+
type = "white",
|
|
10372
|
+
gain = 0.1,
|
|
10373
|
+
attack = 5e-3,
|
|
10374
|
+
pan = 0,
|
|
10375
|
+
filter,
|
|
10376
|
+
filterSweep = 1
|
|
10377
|
+
} = opts;
|
|
10378
|
+
_resumeIfSuspended(ctx);
|
|
10379
|
+
const dur = Math.max(1e-3, duration);
|
|
10380
|
+
const t0 = ctx.currentTime;
|
|
10381
|
+
const t1 = t0 + dur;
|
|
10382
|
+
const env = _buildGainEnvelope(ctx, t0, t1, attack, dur, gain);
|
|
10383
|
+
const sampleCount = Math.max(1, Math.ceil(dur * ctx.sampleRate));
|
|
10384
|
+
const buffer = ctx.createBuffer(1, sampleCount, ctx.sampleRate);
|
|
10385
|
+
fillNoiseBuffer(buffer.getChannelData(0), type);
|
|
10386
|
+
const src = ctx.createBufferSource();
|
|
10387
|
+
src.buffer = buffer;
|
|
10388
|
+
src.connect(env);
|
|
10389
|
+
let tail = env;
|
|
10390
|
+
if (filter !== void 0) {
|
|
10391
|
+
const biquad = ctx.createBiquadFilter();
|
|
10392
|
+
biquad.type = filter.type;
|
|
10393
|
+
biquad.frequency.setValueAtTime(filter.frequency, t0);
|
|
10394
|
+
if (filter.Q !== void 0) {
|
|
10395
|
+
biquad.Q.setValueAtTime(filter.Q, t0);
|
|
10396
|
+
}
|
|
10397
|
+
if (filterSweep !== 1 && filter.frequency > 0) {
|
|
10398
|
+
biquad.frequency.exponentialRampToValueAtTime(
|
|
10399
|
+
Math.max(0.01, filter.frequency * filterSweep),
|
|
10400
|
+
t1
|
|
10401
|
+
);
|
|
10402
|
+
}
|
|
10403
|
+
tail.connect(biquad);
|
|
10404
|
+
tail = biquad;
|
|
10405
|
+
}
|
|
10406
|
+
const panner2 = _connectToOutput(ctx, tail, pan, t0);
|
|
10407
|
+
src.start(t0);
|
|
10408
|
+
src.stop(t1 + 0.02);
|
|
10409
|
+
src.onended = () => {
|
|
10410
|
+
src.disconnect();
|
|
10411
|
+
env.disconnect();
|
|
10412
|
+
if (tail !== env) tail.disconnect();
|
|
10413
|
+
panner2?.disconnect();
|
|
10414
|
+
};
|
|
10415
|
+
}
|
|
10416
|
+
|
|
10417
|
+
// src/audio/audio.ts
|
|
10418
|
+
function init(format = "mp3") {
|
|
10419
|
+
state.audioExts = format.split(",");
|
|
10420
|
+
return isAudioAvailable();
|
|
10421
|
+
}
|
|
10422
|
+
function hasFormat(codec) {
|
|
10423
|
+
return hasCodec(codec);
|
|
10424
|
+
}
|
|
10425
|
+
function hasAudio() {
|
|
10426
|
+
return isAudioAvailable();
|
|
10427
|
+
}
|
|
10428
|
+
function enable() {
|
|
10429
|
+
unmuteAll();
|
|
10430
|
+
}
|
|
10431
|
+
function disable() {
|
|
10432
|
+
muteAll();
|
|
10248
10433
|
}
|
|
10249
10434
|
function playTrack(sound_name, volume) {
|
|
10250
|
-
|
|
10251
|
-
return play(
|
|
10435
|
+
state.currentTrackId = sound_name;
|
|
10436
|
+
return play(state.currentTrackId, true, null, volume);
|
|
10252
10437
|
}
|
|
10253
10438
|
function stopTrack() {
|
|
10254
|
-
if (
|
|
10255
|
-
|
|
10256
|
-
|
|
10439
|
+
if (state.currentTrackId !== null) {
|
|
10440
|
+
state.tracks[state.currentTrackId]?.stop();
|
|
10441
|
+
state.currentTrackId = null;
|
|
10257
10442
|
}
|
|
10258
10443
|
}
|
|
10259
10444
|
function pauseTrack() {
|
|
10260
|
-
if (
|
|
10261
|
-
|
|
10445
|
+
if (state.currentTrackId !== null) {
|
|
10446
|
+
state.tracks[state.currentTrackId]?.pause();
|
|
10262
10447
|
}
|
|
10263
10448
|
}
|
|
10264
10449
|
function resumeTrack() {
|
|
10265
|
-
if (
|
|
10266
|
-
|
|
10450
|
+
if (state.currentTrackId !== null) {
|
|
10451
|
+
state.tracks[state.currentTrackId]?.play();
|
|
10267
10452
|
}
|
|
10268
10453
|
}
|
|
10269
10454
|
function getCurrentTrack() {
|
|
10270
|
-
return
|
|
10455
|
+
return state.currentTrackId;
|
|
10271
10456
|
}
|
|
10272
10457
|
function setVolume(volume) {
|
|
10273
|
-
|
|
10458
|
+
setGlobalVolume(volume);
|
|
10274
10459
|
}
|
|
10275
10460
|
function getVolume() {
|
|
10276
|
-
return
|
|
10461
|
+
return getGlobalVolume();
|
|
10277
10462
|
}
|
|
10278
10463
|
function mute(sound_name, id, shouldMute = true) {
|
|
10279
|
-
|
|
10280
|
-
if (sound2 && typeof sound2 !== "undefined") {
|
|
10281
|
-
sound2.mute(shouldMute, id);
|
|
10282
|
-
} else {
|
|
10283
|
-
throw new Error(`audio clip ${sound_name} does not exist`);
|
|
10284
|
-
}
|
|
10464
|
+
getSoundOrThrow(sound_name).mute(shouldMute, id);
|
|
10285
10465
|
}
|
|
10286
10466
|
function unmute(sound_name, id) {
|
|
10287
10467
|
mute(sound_name, id, false);
|
|
10288
10468
|
}
|
|
10289
10469
|
function muteAll() {
|
|
10290
|
-
|
|
10470
|
+
setGlobalMuted(true);
|
|
10291
10471
|
}
|
|
10292
10472
|
function unmuteAll() {
|
|
10293
|
-
|
|
10473
|
+
setGlobalMuted(false);
|
|
10294
10474
|
}
|
|
10295
10475
|
function muted() {
|
|
10296
|
-
return
|
|
10476
|
+
return isGlobalMuted();
|
|
10297
10477
|
}
|
|
10298
10478
|
function unload(sound_name) {
|
|
10299
|
-
|
|
10479
|
+
const sound2 = state.tracks[sound_name];
|
|
10480
|
+
if (!sound2) {
|
|
10300
10481
|
return false;
|
|
10301
10482
|
}
|
|
10302
|
-
|
|
10303
|
-
delete
|
|
10483
|
+
sound2.unload();
|
|
10484
|
+
delete state.tracks[sound_name];
|
|
10304
10485
|
return true;
|
|
10305
10486
|
}
|
|
10306
10487
|
function unloadAll() {
|
|
10307
|
-
for (const sound_name in
|
|
10308
|
-
if (Object.prototype.hasOwnProperty.call(
|
|
10488
|
+
for (const sound_name in state.tracks) {
|
|
10489
|
+
if (Object.prototype.hasOwnProperty.call(state.tracks, sound_name)) {
|
|
10309
10490
|
unload(sound_name);
|
|
10310
10491
|
}
|
|
10311
10492
|
}
|
|
@@ -10341,7 +10522,6 @@ var CameraEffect = class {
|
|
|
10341
10522
|
*/
|
|
10342
10523
|
update(_dt) {
|
|
10343
10524
|
}
|
|
10344
|
-
// eslint-disable-line @typescript-eslint/no-unused-vars
|
|
10345
10525
|
/**
|
|
10346
10526
|
* Called after the scene renders to draw visual overlays (e.g. color fills for fading).
|
|
10347
10527
|
* @param _renderer - the renderer to draw with
|
|
@@ -10350,7 +10530,6 @@ var CameraEffect = class {
|
|
|
10350
10530
|
*/
|
|
10351
10531
|
draw(_renderer2, _width, _height) {
|
|
10352
10532
|
}
|
|
10353
|
-
// eslint-disable-line @typescript-eslint/no-unused-vars
|
|
10354
10533
|
/**
|
|
10355
10534
|
* Called when the effect is removed from the camera. Override to clean up resources.
|
|
10356
10535
|
*/
|
|
@@ -11277,7 +11456,7 @@ var collision = {
|
|
|
11277
11456
|
}
|
|
11278
11457
|
};
|
|
11279
11458
|
|
|
11280
|
-
// src/physics/body.js
|
|
11459
|
+
// src/physics/builtin/body.js
|
|
11281
11460
|
var Body = class {
|
|
11282
11461
|
/**
|
|
11283
11462
|
* @param {Renderable|Container|Entity|Sprite|NineSliceSprite} ancestor - the parent object this body is attached to
|
|
@@ -11308,11 +11487,16 @@ var Body = class {
|
|
|
11308
11487
|
this.friction.set(0, 0);
|
|
11309
11488
|
this.bounce = 0;
|
|
11310
11489
|
this.mass = 1;
|
|
11490
|
+
this.angle = 0;
|
|
11491
|
+
this.angularVelocity = 0;
|
|
11492
|
+
this.angularDrag = 0;
|
|
11493
|
+
this.pseudoInertia = 1;
|
|
11311
11494
|
if (typeof this.maxVel === "undefined") {
|
|
11312
11495
|
this.maxVel = vector2dPool.get();
|
|
11313
11496
|
}
|
|
11314
11497
|
this.maxVel.set(490, 490);
|
|
11315
11498
|
this.isStatic = false;
|
|
11499
|
+
this.isSensor = false;
|
|
11316
11500
|
this.gravityScale = 1;
|
|
11317
11501
|
this.ignoreGravity = false;
|
|
11318
11502
|
this.falling = false;
|
|
@@ -11340,6 +11524,229 @@ var Body = class {
|
|
|
11340
11524
|
setStatic(isStatic = true) {
|
|
11341
11525
|
this.isStatic = isStatic === true;
|
|
11342
11526
|
}
|
|
11527
|
+
/**
|
|
11528
|
+
* set this body's linear velocity. Portable across physics adapters —
|
|
11529
|
+
* under the builtin adapter this mutates `body.vel`; under Matter it
|
|
11530
|
+
* delegates to `Matter.Body.setVelocity`.
|
|
11531
|
+
* @param {number} x - velocity along the X axis
|
|
11532
|
+
* @param {number} y - velocity along the Y axis
|
|
11533
|
+
*/
|
|
11534
|
+
setVelocity(x, y) {
|
|
11535
|
+
this.vel.set(x, y);
|
|
11536
|
+
}
|
|
11537
|
+
/**
|
|
11538
|
+
* read this body's linear velocity into an optional output vector.
|
|
11539
|
+
* @param {Vector2d} [out] - vector to write into; a new Vector2d is
|
|
11540
|
+
* allocated when omitted
|
|
11541
|
+
* @returns {Vector2d}
|
|
11542
|
+
*/
|
|
11543
|
+
getVelocity(out) {
|
|
11544
|
+
return (out ?? vector2dPool.get()).copy(this.vel);
|
|
11545
|
+
}
|
|
11546
|
+
/**
|
|
11547
|
+
* accumulate a force on this body for the current step. Repeated calls
|
|
11548
|
+
* within a single update add together; the engine clears the
|
|
11549
|
+
* accumulator at the end of each integration step. Force magnitude
|
|
11550
|
+
* conventions differ between adapters — consult the active adapter's
|
|
11551
|
+
* docs for tuning ranges (builtin: px/frame²; Matter: Newtonian
|
|
11552
|
+
* `force/mass·dt²`, typically ~100× smaller than builtin).
|
|
11553
|
+
* @param {number} x - force along the X axis
|
|
11554
|
+
* @param {number} y - force along the Y axis
|
|
11555
|
+
* @param {number} [pointX] - world X of the application point; when
|
|
11556
|
+
* present (along with `pointY`) and different from the body centroid,
|
|
11557
|
+
* the resulting lever arm generates a torque
|
|
11558
|
+
* `τ = (r.x · F.y) − (r.y · F.x)` that bumps {@link Body#angularVelocity}
|
|
11559
|
+
* by `τ / pseudoInertia`. Omit both `pointX` and `pointY` for the
|
|
11560
|
+
* linear-only behaviour that's compatible with code written before
|
|
11561
|
+
* the angular API was added.
|
|
11562
|
+
* @param {number} [pointY] - world Y of the application point
|
|
11563
|
+
* @example
|
|
11564
|
+
* // pure linear thrust (2-arg form, unchanged behaviour):
|
|
11565
|
+
* ship.body.applyForce(0, -0.05);
|
|
11566
|
+
*
|
|
11567
|
+
* // off-centre push on a crate: the contact point at the top of the
|
|
11568
|
+
* // crate is above its centroid, so the same horizontal force now
|
|
11569
|
+
* // both translates AND tips the crate forward.
|
|
11570
|
+
* const topX = crate.pos.x + crate.width / 2;
|
|
11571
|
+
* const topY = crate.pos.y;
|
|
11572
|
+
* crate.body.applyForce(1.5, 0, topX, topY);
|
|
11573
|
+
*
|
|
11574
|
+
* // wind pushing on the top of a flag-pole: pole tilts, base stays put
|
|
11575
|
+
* // (only meaningful here if the base is anchored / static).
|
|
11576
|
+
* pole.body.applyForce(0.3, 0, pole.pos.x + pole.width / 2, pole.pos.y);
|
|
11577
|
+
*/
|
|
11578
|
+
applyForce(x, y, pointX, pointY) {
|
|
11579
|
+
this.force.x += x;
|
|
11580
|
+
this.force.y += y;
|
|
11581
|
+
if (typeof pointX === "number" && typeof pointY === "number") {
|
|
11582
|
+
const bounds = this.bounds;
|
|
11583
|
+
const rx = pointX - bounds.centerX;
|
|
11584
|
+
const ry = pointY - bounds.centerY;
|
|
11585
|
+
const torque = rx * y - ry * x;
|
|
11586
|
+
if (this.pseudoInertia > 0) {
|
|
11587
|
+
this.angularVelocity += torque / this.pseudoInertia;
|
|
11588
|
+
}
|
|
11589
|
+
}
|
|
11590
|
+
}
|
|
11591
|
+
/**
|
|
11592
|
+
* Apply an instantaneous angular impulse: `Δω = τ / pseudoInertia`.
|
|
11593
|
+
* The angular analog of {@link Body#applyImpulse} — bypasses the
|
|
11594
|
+
* lever-arm computation in {@link Body#applyForce} for the
|
|
11595
|
+
* "just spin this up directly" case (a power-up's intrinsic spin,
|
|
11596
|
+
* an explicit thruster, a knockback spin effect on hit).
|
|
11597
|
+
* @param {number} torque - angular impulse magnitude. Positive values
|
|
11598
|
+
* produce clockwise rotation on screen (matching the Y-down canvas
|
|
11599
|
+
* convention); negative values rotate counter-clockwise.
|
|
11600
|
+
* @example
|
|
11601
|
+
* // give a pickup a one-shot spin-up when collected:
|
|
11602
|
+
* pickup.body.applyTorque(80);
|
|
11603
|
+
*
|
|
11604
|
+
* // explosion knockback that both pushes and spins:
|
|
11605
|
+
* crate.body.applyImpulse(impulseX, impulseY);
|
|
11606
|
+
* crate.body.applyTorque((Math.random() - 0.5) * 100);
|
|
11607
|
+
*/
|
|
11608
|
+
applyTorque(torque) {
|
|
11609
|
+
if (this.pseudoInertia > 0) {
|
|
11610
|
+
this.angularVelocity += torque / this.pseudoInertia;
|
|
11611
|
+
}
|
|
11612
|
+
}
|
|
11613
|
+
/**
|
|
11614
|
+
* Set angular velocity directly. Bypasses inertia — the value is the
|
|
11615
|
+
* actual rad/frame that integration will apply next step. Use this
|
|
11616
|
+
* for "set and hold" rotation (a coin that always spins at the same
|
|
11617
|
+
* rate); use {@link Body#applyTorque} for impulse-style spin-up.
|
|
11618
|
+
* @param {number} omega - target angular velocity (rad / frame)
|
|
11619
|
+
* @example
|
|
11620
|
+
* // make a fan blade spin at a fixed rate:
|
|
11621
|
+
* fan.body.setAngularVelocity(0.1); // ~6°/frame
|
|
11622
|
+
*/
|
|
11623
|
+
setAngularVelocity(omega) {
|
|
11624
|
+
this.angularVelocity = omega;
|
|
11625
|
+
}
|
|
11626
|
+
/**
|
|
11627
|
+
* Read current angular velocity (rad / frame).
|
|
11628
|
+
* @returns {number}
|
|
11629
|
+
* @example
|
|
11630
|
+
* // freeze rotation if the body is spinning too fast:
|
|
11631
|
+
* if (Math.abs(body.getAngularVelocity()) > 2) {
|
|
11632
|
+
* body.setAngularVelocity(0);
|
|
11633
|
+
* }
|
|
11634
|
+
*/
|
|
11635
|
+
getAngularVelocity() {
|
|
11636
|
+
return this.angularVelocity;
|
|
11637
|
+
}
|
|
11638
|
+
/**
|
|
11639
|
+
* Set absolute rotation angle (radians). Updates the body's `angle`
|
|
11640
|
+
* field and re-syncs the renderable's `currentTransform` immediately
|
|
11641
|
+
* so the visual rotation reflects the new value without waiting for
|
|
11642
|
+
* the next integration step.
|
|
11643
|
+
* @param {number} rad - target angle in radians
|
|
11644
|
+
* @example
|
|
11645
|
+
* // turret aims at the player every frame:
|
|
11646
|
+
* const dx = player.centerX - turret.centerX;
|
|
11647
|
+
* const dy = player.centerY - turret.centerY;
|
|
11648
|
+
* turret.body.setAngle(Math.atan2(dy, dx));
|
|
11649
|
+
*/
|
|
11650
|
+
setAngle(rad) {
|
|
11651
|
+
this.angle = rad;
|
|
11652
|
+
this._syncAngleTransform();
|
|
11653
|
+
}
|
|
11654
|
+
/**
|
|
11655
|
+
* Read absolute rotation angle (radians).
|
|
11656
|
+
* @returns {number}
|
|
11657
|
+
*/
|
|
11658
|
+
getAngle() {
|
|
11659
|
+
return this.angle;
|
|
11660
|
+
}
|
|
11661
|
+
/**
|
|
11662
|
+
* Sync `this.angle` to the renderable's `currentTransform`. Pivot is
|
|
11663
|
+
* the body's bounds center (matches the matter adapter's rotation
|
|
11664
|
+
* pivot — see `MatterAdapter.syncFromPhysics`). Internal helper used
|
|
11665
|
+
* by both the per-step integrator and {@link Body#setAngle}.
|
|
11666
|
+
* @ignore
|
|
11667
|
+
*/
|
|
11668
|
+
_syncAngleTransform() {
|
|
11669
|
+
const t = this.ancestor?.currentTransform;
|
|
11670
|
+
if (!t) {
|
|
11671
|
+
return;
|
|
11672
|
+
}
|
|
11673
|
+
const bounds = this.bounds;
|
|
11674
|
+
const cx = bounds.centerX - this.ancestor.pos.x;
|
|
11675
|
+
const cy = bounds.centerY - this.ancestor.pos.y;
|
|
11676
|
+
t.identity();
|
|
11677
|
+
if (this.angle !== 0) {
|
|
11678
|
+
if (cx !== 0 || cy !== 0) {
|
|
11679
|
+
t.translate(cx, cy);
|
|
11680
|
+
t.rotate(this.angle);
|
|
11681
|
+
t.translate(-cx, -cy);
|
|
11682
|
+
} else {
|
|
11683
|
+
t.rotate(this.angle);
|
|
11684
|
+
}
|
|
11685
|
+
}
|
|
11686
|
+
}
|
|
11687
|
+
/**
|
|
11688
|
+
* apply an instantaneous impulse to this body — a single-step velocity
|
|
11689
|
+
* change scaled by inverse mass (`dv = J / m`). Useful for one-shot
|
|
11690
|
+
* events like a cue strike, projectile launch, or knockback, where
|
|
11691
|
+
* mass should influence the resulting velocity change. Repeated calls
|
|
11692
|
+
* within a single update accumulate. Static bodies (mass 0) ignore
|
|
11693
|
+
* the call. Identical signature on the Matter adapter, where the
|
|
11694
|
+
* adapter integrates the impulse the same way (matter has no native
|
|
11695
|
+
* `applyImpulse`).
|
|
11696
|
+
* @param {number} x - impulse along the X axis
|
|
11697
|
+
* @param {number} y - impulse along the Y axis
|
|
11698
|
+
*/
|
|
11699
|
+
applyImpulse(x, y) {
|
|
11700
|
+
const invMass = this.mass > 0 ? 1 / this.mass : 0;
|
|
11701
|
+
this.vel.x += x * invMass;
|
|
11702
|
+
this.vel.y += y * invMass;
|
|
11703
|
+
}
|
|
11704
|
+
/**
|
|
11705
|
+
* set this body's mass. Useful for variable-mass entities (projectiles
|
|
11706
|
+
* loaded with ammo, weight pickups, characters carrying objects).
|
|
11707
|
+
* Mass affects `applyImpulse` (via `dv = J / m`) and the proportional
|
|
11708
|
+
* push-out response in dynamic-dynamic collisions. A mass of 0 makes
|
|
11709
|
+
* the body inert to forces and impulses (without going static).
|
|
11710
|
+
* @param {number} m - new mass, non-negative
|
|
11711
|
+
*/
|
|
11712
|
+
setMass(m) {
|
|
11713
|
+
this.mass = m;
|
|
11714
|
+
}
|
|
11715
|
+
/**
|
|
11716
|
+
* set this body's restitution / bounce factor. `0` = no bounce (energy
|
|
11717
|
+
* absorbed on contact); `1` = perfect elastic rebound; values in
|
|
11718
|
+
* between dampen the rebound. Applied by `Body.respondToCollision` —
|
|
11719
|
+
* see `BuiltinAdapter` docs for the cancellation math.
|
|
11720
|
+
*
|
|
11721
|
+
* Matches the `bodyDef.restitution` field name used at registration
|
|
11722
|
+
* time; the body-side property has historically been called `bounce`,
|
|
11723
|
+
* which is the canonical legacy name and is preserved.
|
|
11724
|
+
* @param {number} r - restitution factor, typically in [0, 1]
|
|
11725
|
+
*/
|
|
11726
|
+
setBounce(r) {
|
|
11727
|
+
this.bounce = r;
|
|
11728
|
+
}
|
|
11729
|
+
/**
|
|
11730
|
+
* set this body's per-body gravity multiplier. `1` = world gravity
|
|
11731
|
+
* (default), `0` = ignore world gravity (e.g. flying enemy, underwater
|
|
11732
|
+
* float), `2` = 2× gravity (heavy-feel objects). Multiplied with the
|
|
11733
|
+
* world's `gravity.y` each frame inside `applyGravity`.
|
|
11734
|
+
* @param {number} scale - gravity scale factor
|
|
11735
|
+
*/
|
|
11736
|
+
setGravityScale(scale2) {
|
|
11737
|
+
this.gravityScale = scale2;
|
|
11738
|
+
}
|
|
11739
|
+
/**
|
|
11740
|
+
* toggle this body between solid and sensor. Sensor bodies still emit
|
|
11741
|
+
* collision events (`onCollisionStart`, `onCollisionActive`,
|
|
11742
|
+
* `onCollisionEnd`) but the solver does not physically resolve the
|
|
11743
|
+
* contact — same semantics as Matter's `isSensor`. Useful for one-way
|
|
11744
|
+
* platforms, trigger zones, and ground-snap assists.
|
|
11745
|
+
* @param {boolean} [isSensor=true]
|
|
11746
|
+
*/
|
|
11747
|
+
setSensor(isSensor = true) {
|
|
11748
|
+
this.isSensor = isSensor === true;
|
|
11749
|
+
}
|
|
11343
11750
|
/**
|
|
11344
11751
|
* add a collision shape to this body <br>
|
|
11345
11752
|
* (note: me.Rect objects will be converted to me.Polygon before being added)
|
|
@@ -11352,11 +11759,21 @@ var Body = class {
|
|
|
11352
11759
|
* this.body.addShape(me.loader.getJSON("shapesdef").banana);
|
|
11353
11760
|
*/
|
|
11354
11761
|
addShape(shape) {
|
|
11355
|
-
if (shape instanceof Rect
|
|
11762
|
+
if (shape instanceof Rect) {
|
|
11356
11763
|
const poly = shape.toPolygon();
|
|
11357
11764
|
this.shapes.push(poly);
|
|
11358
11765
|
this.bounds.add(poly.points);
|
|
11359
11766
|
this.bounds.translate(poly.pos);
|
|
11767
|
+
} else if (shape instanceof Bounds) {
|
|
11768
|
+
const poly = polygonPool.get(shape.x, shape.y, [
|
|
11769
|
+
new Vector2d(0, 0),
|
|
11770
|
+
new Vector2d(shape.width, 0),
|
|
11771
|
+
new Vector2d(shape.width, shape.height),
|
|
11772
|
+
new Vector2d(0, shape.height)
|
|
11773
|
+
]);
|
|
11774
|
+
this.shapes.push(poly);
|
|
11775
|
+
this.bounds.add(poly.points);
|
|
11776
|
+
this.bounds.translate(poly.pos);
|
|
11360
11777
|
} else if (shape instanceof Ellipse) {
|
|
11361
11778
|
if (!this.shapes.includes(shape)) {
|
|
11362
11779
|
this.shapes.push(shape);
|
|
@@ -11377,11 +11794,26 @@ var Body = class {
|
|
|
11377
11794
|
} else {
|
|
11378
11795
|
this.fromJSON(shape);
|
|
11379
11796
|
}
|
|
11797
|
+
this._recomputePseudoInertia();
|
|
11380
11798
|
if (typeof this.onBodyUpdate === "function") {
|
|
11381
11799
|
this.onBodyUpdate(this);
|
|
11382
11800
|
}
|
|
11383
11801
|
return this.shapes.length;
|
|
11384
11802
|
}
|
|
11803
|
+
/**
|
|
11804
|
+
* Recompute the default `pseudoInertia` from the current bounds.
|
|
11805
|
+
* Uses the moment-of-inertia formula for a unit-mass rectangle —
|
|
11806
|
+
* `(width² + height²) / 12` — which gives a value that scales
|
|
11807
|
+
* sensibly with body size: a small body resists rotation less, a
|
|
11808
|
+
* large body more. Clamped to a minimum of 1 to keep divisions
|
|
11809
|
+
* well-defined even on degenerate 0-size bodies.
|
|
11810
|
+
* @ignore
|
|
11811
|
+
*/
|
|
11812
|
+
_recomputePseudoInertia() {
|
|
11813
|
+
const w = this.bounds.width;
|
|
11814
|
+
const h = this.bounds.height;
|
|
11815
|
+
this.pseudoInertia = Math.max(1, (w * w + h * h) / 12);
|
|
11816
|
+
}
|
|
11385
11817
|
/**
|
|
11386
11818
|
* set the body vertices to the given one
|
|
11387
11819
|
* @param {Vector2d[]} vertices - an array of me.Vector2d points defining a convex hull
|
|
@@ -11545,7 +11977,7 @@ var Body = class {
|
|
|
11545
11977
|
this.vel.y -= projVel * ratio * overlapN.y;
|
|
11546
11978
|
}
|
|
11547
11979
|
}
|
|
11548
|
-
if (overlap.y !== 0 && !this.ignoreGravity) {
|
|
11980
|
+
if (overlap.y !== 0 && !this.ignoreGravity && this.gravityScale !== 0) {
|
|
11549
11981
|
const dir = this.falling === true ? 1 : this.jumping === true ? -1 : 0;
|
|
11550
11982
|
this.falling = overlap.y >= dir;
|
|
11551
11983
|
this.jumping = overlap.y <= -dir;
|
|
@@ -11691,6 +12123,13 @@ var Body = class {
|
|
|
11691
12123
|
this.falling = this.vel.y * Math.sign(this.force.y) > 0;
|
|
11692
12124
|
this.jumping = this.falling ? false : this.jumping;
|
|
11693
12125
|
this.ancestor.pos.add(this.vel);
|
|
12126
|
+
if (this.angularVelocity !== 0 || this.angle !== 0) {
|
|
12127
|
+
if (this.angularDrag > 0 && this.angularVelocity !== 0) {
|
|
12128
|
+
this.angularVelocity *= 1 - this.angularDrag;
|
|
12129
|
+
}
|
|
12130
|
+
this.angle += this.angularVelocity * deltaTime;
|
|
12131
|
+
this._syncAngleTransform();
|
|
12132
|
+
}
|
|
11694
12133
|
return this.vel.x !== 0 || this.vel.y !== 0;
|
|
11695
12134
|
}
|
|
11696
12135
|
/**
|
|
@@ -11749,6 +12188,7 @@ var Renderable = class _Renderable extends Rect {
|
|
|
11749
12188
|
}
|
|
11750
12189
|
this.currentTransform.identity();
|
|
11751
12190
|
this.body = void 0;
|
|
12191
|
+
this.bodyDef = void 0;
|
|
11752
12192
|
this.GUID = void 0;
|
|
11753
12193
|
this.onVisibilityChange = void 0;
|
|
11754
12194
|
this.alwaysUpdate = false;
|
|
@@ -12244,28 +12684,74 @@ var Renderable = class _Renderable extends Rect {
|
|
|
12244
12684
|
this.isDirty = false;
|
|
12245
12685
|
}
|
|
12246
12686
|
/**
|
|
12247
|
-
*
|
|
12248
|
-
*
|
|
12249
|
-
*
|
|
12687
|
+
* Legacy collision callback — fires every frame this renderable body is
|
|
12688
|
+
* overlapping another body. Kept for backward compatibility with code
|
|
12689
|
+
* written against pre-19.5 melonJS; semantics are unchanged from the
|
|
12690
|
+
* 19.4 contract.
|
|
12691
|
+
*
|
|
12692
|
+
* **NOTE — `onCollision` is NOT equivalent to {@link Renderable.onCollisionActive}.**
|
|
12693
|
+
* The two handlers exist side by side and have intentionally different
|
|
12694
|
+
* contracts:
|
|
12695
|
+
*
|
|
12696
|
+
* | | `onCollision` (legacy) | `onCollisionActive` (modern) |
|
|
12697
|
+
* |---|---|---|
|
|
12698
|
+
* | Cadence for dynamic-dynamic pairs | 2× per frame per side | 1× per frame per side |
|
|
12699
|
+
* | `response.a` semantics | Fixed per pair (first body in detector call) | Always the receiver (`response.a === this`) |
|
|
12700
|
+
* | `response.b` semantics | Fixed per pair | Always the partner (`response.b === other`) |
|
|
12701
|
+
* | `response.normal` / `response.depth` | ✗ | ✓ — `normal.y < -0.7` = "push me up" |
|
|
12702
|
+
* | `return false` to skip push-out | ✓ (honored by SAT) | ✗ — use `bodyDef.isSensor` or `setSensor` instead |
|
|
12703
|
+
*
|
|
12704
|
+
* If you're writing new code, prefer `onCollisionActive`. Keep
|
|
12705
|
+
* `onCollision` only when its every-frame, return-false, fixed-`a`/`b`
|
|
12706
|
+
* semantics are what you want.
|
|
12707
|
+
*
|
|
12708
|
+
* @param {import("../physics/adapter.ts").CollisionResponse} response - the collision response object
|
|
12250
12709
|
* @param {Renderable} other - the other renderable touching this one (a reference to response.a or response.b)
|
|
12251
|
-
* @returns {boolean} true if the object should respond to the collision (its position and velocity will be corrected)
|
|
12710
|
+
* @returns {boolean} true if the object should respond to the collision (its position and velocity will be corrected); the return value is only honored by the builtin SAT adapter.
|
|
12252
12711
|
* @example
|
|
12253
|
-
* // collision handler
|
|
12712
|
+
* // legacy collision handler — note the receiver-side check on response.a
|
|
12254
12713
|
* onCollision(response) {
|
|
12255
12714
|
* if (response.b.body.collisionType === me.collision.types.ENEMY_OBJECT) {
|
|
12256
|
-
* // makes the other object solid, by substracting the overlap vector to the current position
|
|
12257
12715
|
* this.pos.sub(response.overlapV);
|
|
12258
12716
|
* this.hurt();
|
|
12259
|
-
* //
|
|
12260
|
-
* return false;
|
|
12717
|
+
* return false; // skip the SAT push-out
|
|
12261
12718
|
* }
|
|
12262
|
-
* // Make the object solid
|
|
12263
12719
|
* return true;
|
|
12264
|
-
* }
|
|
12720
|
+
* }
|
|
12265
12721
|
*/
|
|
12266
|
-
onCollision() {
|
|
12267
|
-
return false;
|
|
12722
|
+
onCollision(_response, _other) {
|
|
12268
12723
|
}
|
|
12724
|
+
/*
|
|
12725
|
+
* Modern collision lifecycle hooks (`onCollisionStart`,
|
|
12726
|
+
* `onCollisionActive`, `onCollisionEnd`) are intentionally NOT
|
|
12727
|
+
* defined on the base class. Subclasses override the ones they
|
|
12728
|
+
* care about; the adapter's dispatch logic checks
|
|
12729
|
+
* `typeof renderable.onCollisionX === "function"` to know whether
|
|
12730
|
+
* to fire them. Defining base stubs would make every renderable
|
|
12731
|
+
* "look like" it implements every hook, which would in turn make
|
|
12732
|
+
* the `onCollisionActive supersedes onCollision` rule fire even
|
|
12733
|
+
* for legacy-only code.
|
|
12734
|
+
*
|
|
12735
|
+
* Documented surface (subclasses implement these as needed):
|
|
12736
|
+
*
|
|
12737
|
+
* onCollisionStart(response, other)
|
|
12738
|
+
* One-shot on first contact. Use for stomp bounces, pickups,
|
|
12739
|
+
* trigger entry. Receiver-symmetric (`response.a === this`,
|
|
12740
|
+
* `response.b === other`). Same contract on every adapter.
|
|
12741
|
+
*
|
|
12742
|
+
* onCollisionActive(response, other)
|
|
12743
|
+
* Every step while two bodies remain in contact. The modern
|
|
12744
|
+
* equivalent of `onCollision`, with different semantics:
|
|
12745
|
+
* - 1× per frame per side (vs `onCollision`'s 2× for dyn-dyn)
|
|
12746
|
+
* - `response.a === this`, `response.b === other`
|
|
12747
|
+
* - `response.normal` is the MTV of the receiver
|
|
12748
|
+
* - `return false` is NOT honored (use `bodyDef.isSensor`)
|
|
12749
|
+
* When defined, `onCollision` is suppressed on the same
|
|
12750
|
+
* renderable. Stomp idiom: `response.normal.y < -0.7`.
|
|
12751
|
+
*
|
|
12752
|
+
* onCollisionEnd(response, other)
|
|
12753
|
+
* One-shot when two bodies separate.
|
|
12754
|
+
*/
|
|
12269
12755
|
/**
|
|
12270
12756
|
* Destroy function<br>
|
|
12271
12757
|
* @ignore
|
|
@@ -12309,9 +12795,31 @@ var Renderable = class _Renderable extends Rect {
|
|
|
12309
12795
|
}
|
|
12310
12796
|
/**
|
|
12311
12797
|
* OnDestroy Notification function<br>
|
|
12312
|
-
* Called by engine before deleting the object
|
|
12798
|
+
* Called by engine before deleting the object. `Stage.destroy(app)`
|
|
12799
|
+
* forwards the active Application, so subclasses that wire teardown
|
|
12800
|
+
* against the app object can override as `onDestroyEvent(app)`.
|
|
12801
|
+
* @param {...*} _args - forwarded by `destroy(...args)`; Stage passes the Application instance
|
|
12313
12802
|
*/
|
|
12314
|
-
onDestroyEvent() {
|
|
12803
|
+
onDestroyEvent(..._args) {
|
|
12804
|
+
}
|
|
12805
|
+
/**
|
|
12806
|
+
* Lifecycle hook fired by {@link Container} when this renderable is
|
|
12807
|
+
* added to a container that is part of the active scene graph.
|
|
12808
|
+
* Override to wire up input handlers, register external listeners,
|
|
12809
|
+
* or grab adapter references — `this.parentApp` is guaranteed to be
|
|
12810
|
+
* available here. Pair with {@link Renderable#onDeactivateEvent}.
|
|
12811
|
+
* @param {...*} _args - forwarded by `Container.addChild`
|
|
12812
|
+
*/
|
|
12813
|
+
onActivateEvent(..._args) {
|
|
12814
|
+
}
|
|
12815
|
+
/**
|
|
12816
|
+
* Lifecycle hook fired by {@link Container} when this renderable is
|
|
12817
|
+
* removed from its container or its container is itself removed.
|
|
12818
|
+
* Override to release input handlers, unsubscribe from events, or
|
|
12819
|
+
* drop adapter references. Pair with {@link Renderable#onActivateEvent}.
|
|
12820
|
+
* @param {...*} _args - forwarded by `Container.removeChildNow`
|
|
12821
|
+
*/
|
|
12822
|
+
onDeactivateEvent(..._args) {
|
|
12315
12823
|
}
|
|
12316
12824
|
};
|
|
12317
12825
|
|
|
@@ -12420,6 +12928,421 @@ function fetchData(url, responseType, settings = {}) {
|
|
|
12420
12928
|
});
|
|
12421
12929
|
}
|
|
12422
12930
|
|
|
12931
|
+
// src/loader/parsers/aseprite.js
|
|
12932
|
+
var ASE_MAGIC_FILE = 42464;
|
|
12933
|
+
var ASE_MAGIC_FRAME = 61946;
|
|
12934
|
+
var CHUNK_OLD_PALETTE_04 = 4;
|
|
12935
|
+
var CHUNK_OLD_PALETTE_11 = 17;
|
|
12936
|
+
var CHUNK_LAYER = 8196;
|
|
12937
|
+
var CHUNK_CEL = 8197;
|
|
12938
|
+
var CHUNK_PALETTE = 8217;
|
|
12939
|
+
var CHUNK_TAGS = 8216;
|
|
12940
|
+
var CHUNK_TILESET = 8227;
|
|
12941
|
+
var CEL_RAW_IMAGE = 0;
|
|
12942
|
+
var CEL_LINKED = 1;
|
|
12943
|
+
var CEL_COMPRESSED_IMAGE = 2;
|
|
12944
|
+
var CEL_COMPRESSED_TILEMAP = 3;
|
|
12945
|
+
var ERR_TILEMAP_MODE = "aseprite: this file uses Aseprite's tilemap mode (tileset chunk or tilemap-cel), which is not supported. Export the sprite to a regular PNG (File \u2192 Export Sprite Sheet) or flatten the tilemap layer to pixel layers.";
|
|
12946
|
+
var DEPTH_RGBA = 32;
|
|
12947
|
+
var DEPTH_GRAYSCALE = 16;
|
|
12948
|
+
var DEPTH_INDEXED = 8;
|
|
12949
|
+
var LAYER_FLAG_VISIBLE = 1;
|
|
12950
|
+
var TAG_DIRECTIONS = ["forward", "reverse", "pingpong", "pingpong_reverse"];
|
|
12951
|
+
var Reader = class {
|
|
12952
|
+
constructor(buffer, offset = 0) {
|
|
12953
|
+
this.view = new DataView(buffer);
|
|
12954
|
+
this.bytes = new Uint8Array(buffer);
|
|
12955
|
+
this.offset = offset;
|
|
12956
|
+
}
|
|
12957
|
+
u8() {
|
|
12958
|
+
return this.view.getUint8(this.offset++);
|
|
12959
|
+
}
|
|
12960
|
+
u16() {
|
|
12961
|
+
const v = this.view.getUint16(this.offset, true);
|
|
12962
|
+
this.offset += 2;
|
|
12963
|
+
return v;
|
|
12964
|
+
}
|
|
12965
|
+
i16() {
|
|
12966
|
+
const v = this.view.getInt16(this.offset, true);
|
|
12967
|
+
this.offset += 2;
|
|
12968
|
+
return v;
|
|
12969
|
+
}
|
|
12970
|
+
u32() {
|
|
12971
|
+
const v = this.view.getUint32(this.offset, true);
|
|
12972
|
+
this.offset += 4;
|
|
12973
|
+
return v;
|
|
12974
|
+
}
|
|
12975
|
+
i32() {
|
|
12976
|
+
const v = this.view.getInt32(this.offset, true);
|
|
12977
|
+
this.offset += 4;
|
|
12978
|
+
return v;
|
|
12979
|
+
}
|
|
12980
|
+
str() {
|
|
12981
|
+
const len = this.u16();
|
|
12982
|
+
const slice = this.bytes.subarray(this.offset, this.offset + len);
|
|
12983
|
+
this.offset += len;
|
|
12984
|
+
return new TextDecoder("utf-8").decode(slice);
|
|
12985
|
+
}
|
|
12986
|
+
skip(n) {
|
|
12987
|
+
this.offset += n;
|
|
12988
|
+
}
|
|
12989
|
+
slice(n) {
|
|
12990
|
+
const out = this.bytes.subarray(this.offset, this.offset + n);
|
|
12991
|
+
this.offset += n;
|
|
12992
|
+
return out;
|
|
12993
|
+
}
|
|
12994
|
+
};
|
|
12995
|
+
async function inflate(bytes) {
|
|
12996
|
+
const stream = new Response(bytes).body.pipeThrough(
|
|
12997
|
+
new DecompressionStream("deflate")
|
|
12998
|
+
);
|
|
12999
|
+
return new Uint8Array(await new Response(stream).arrayBuffer());
|
|
13000
|
+
}
|
|
13001
|
+
function decodeCelPixels(raw, w, h, depth, palette, transparentIndex) {
|
|
13002
|
+
const out = new Uint8ClampedArray(w * h * 4);
|
|
13003
|
+
switch (depth) {
|
|
13004
|
+
case DEPTH_RGBA:
|
|
13005
|
+
out.set(raw);
|
|
13006
|
+
break;
|
|
13007
|
+
case DEPTH_GRAYSCALE:
|
|
13008
|
+
for (let i = 0, j = 0; i < w * h; i++, j += 4) {
|
|
13009
|
+
const v = raw[i * 2];
|
|
13010
|
+
const a = raw[i * 2 + 1];
|
|
13011
|
+
out[j] = v;
|
|
13012
|
+
out[j + 1] = v;
|
|
13013
|
+
out[j + 2] = v;
|
|
13014
|
+
out[j + 3] = a;
|
|
13015
|
+
}
|
|
13016
|
+
break;
|
|
13017
|
+
case DEPTH_INDEXED:
|
|
13018
|
+
for (let i = 0, j = 0; i < w * h; i++, j += 4) {
|
|
13019
|
+
const idx = raw[i];
|
|
13020
|
+
if (idx === transparentIndex) {
|
|
13021
|
+
out[j] = 0;
|
|
13022
|
+
out[j + 1] = 0;
|
|
13023
|
+
out[j + 2] = 0;
|
|
13024
|
+
out[j + 3] = 0;
|
|
13025
|
+
} else {
|
|
13026
|
+
const p = palette[idx];
|
|
13027
|
+
if (p) {
|
|
13028
|
+
out[j] = p[0];
|
|
13029
|
+
out[j + 1] = p[1];
|
|
13030
|
+
out[j + 2] = p[2];
|
|
13031
|
+
out[j + 3] = p[3];
|
|
13032
|
+
}
|
|
13033
|
+
}
|
|
13034
|
+
}
|
|
13035
|
+
break;
|
|
13036
|
+
default:
|
|
13037
|
+
throw new Error("aseprite: unsupported color depth " + depth);
|
|
13038
|
+
}
|
|
13039
|
+
return out;
|
|
13040
|
+
}
|
|
13041
|
+
async function parseAsepriteFile(buffer) {
|
|
13042
|
+
const r = new Reader(buffer);
|
|
13043
|
+
r.u32();
|
|
13044
|
+
const magic = r.u16();
|
|
13045
|
+
if (magic !== ASE_MAGIC_FILE) {
|
|
13046
|
+
throw new Error(
|
|
13047
|
+
"aseprite: invalid file magic 0x" + magic.toString(16) + " (expected 0xA5E0)"
|
|
13048
|
+
);
|
|
13049
|
+
}
|
|
13050
|
+
const frameCount = r.u16();
|
|
13051
|
+
const width = r.u16();
|
|
13052
|
+
const height = r.u16();
|
|
13053
|
+
const depth = r.u16();
|
|
13054
|
+
r.u32();
|
|
13055
|
+
r.u16();
|
|
13056
|
+
r.u32();
|
|
13057
|
+
r.u32();
|
|
13058
|
+
const transparentIndex = r.u8();
|
|
13059
|
+
r.skip(3);
|
|
13060
|
+
r.u16();
|
|
13061
|
+
r.offset = 128;
|
|
13062
|
+
const palette = [];
|
|
13063
|
+
const layers = [];
|
|
13064
|
+
const tags = [];
|
|
13065
|
+
const frames = [];
|
|
13066
|
+
const ancestorVisible = [];
|
|
13067
|
+
for (let f = 0; f < frameCount; f++) {
|
|
13068
|
+
const frameStart = r.offset;
|
|
13069
|
+
const frameSize = r.u32();
|
|
13070
|
+
const fmagic = r.u16();
|
|
13071
|
+
if (fmagic !== ASE_MAGIC_FRAME) {
|
|
13072
|
+
throw new Error(
|
|
13073
|
+
"aseprite: invalid frame magic at frame " + f + " (got 0x" + fmagic.toString(16) + ")"
|
|
13074
|
+
);
|
|
13075
|
+
}
|
|
13076
|
+
let chunkCount = r.u16();
|
|
13077
|
+
const duration = r.u16();
|
|
13078
|
+
r.skip(2);
|
|
13079
|
+
const newChunks = r.u32();
|
|
13080
|
+
if (chunkCount === 65535) {
|
|
13081
|
+
chunkCount = newChunks;
|
|
13082
|
+
}
|
|
13083
|
+
const cels = [];
|
|
13084
|
+
for (let c = 0; c < chunkCount; c++) {
|
|
13085
|
+
const chunkStart = r.offset;
|
|
13086
|
+
const chunkSize = r.u32();
|
|
13087
|
+
const chunkType = r.u16();
|
|
13088
|
+
const chunkEnd = chunkStart + chunkSize;
|
|
13089
|
+
switch (chunkType) {
|
|
13090
|
+
case CHUNK_LAYER: {
|
|
13091
|
+
const flags = r.u16();
|
|
13092
|
+
r.u16();
|
|
13093
|
+
const childLevel = r.u16();
|
|
13094
|
+
r.u16();
|
|
13095
|
+
r.u16();
|
|
13096
|
+
r.u16();
|
|
13097
|
+
r.u8();
|
|
13098
|
+
r.skip(3);
|
|
13099
|
+
const name = r.str();
|
|
13100
|
+
const selfVisible = (flags & LAYER_FLAG_VISIBLE) !== 0;
|
|
13101
|
+
const parentVisible = childLevel === 0 ? true : ancestorVisible[childLevel - 1] !== false;
|
|
13102
|
+
const visible = selfVisible && parentVisible;
|
|
13103
|
+
ancestorVisible[childLevel] = visible;
|
|
13104
|
+
ancestorVisible.length = childLevel + 1;
|
|
13105
|
+
layers.push({ flags, name, visible });
|
|
13106
|
+
break;
|
|
13107
|
+
}
|
|
13108
|
+
case CHUNK_CEL: {
|
|
13109
|
+
const layer = r.u16();
|
|
13110
|
+
const x = r.i16();
|
|
13111
|
+
const y = r.i16();
|
|
13112
|
+
const opacity = r.u8();
|
|
13113
|
+
const celType = r.u16();
|
|
13114
|
+
r.skip(7);
|
|
13115
|
+
if (celType === CEL_RAW_IMAGE) {
|
|
13116
|
+
const cw = r.u16();
|
|
13117
|
+
const ch = r.u16();
|
|
13118
|
+
const pixelBytes = cw * ch * (depth / 8);
|
|
13119
|
+
const raw = r.slice(pixelBytes);
|
|
13120
|
+
cels.push({ layer, x, y, opacity, w: cw, h: ch, raw });
|
|
13121
|
+
} else if (celType === CEL_LINKED) {
|
|
13122
|
+
const linkFrame = r.u16();
|
|
13123
|
+
cels.push({ layer, x, y, opacity, linkFrame });
|
|
13124
|
+
} else if (celType === CEL_COMPRESSED_IMAGE) {
|
|
13125
|
+
const cw = r.u16();
|
|
13126
|
+
const ch = r.u16();
|
|
13127
|
+
const compressed = r.slice(chunkEnd - r.offset);
|
|
13128
|
+
const inflated = await inflate(compressed);
|
|
13129
|
+
cels.push({
|
|
13130
|
+
layer,
|
|
13131
|
+
x,
|
|
13132
|
+
y,
|
|
13133
|
+
opacity,
|
|
13134
|
+
w: cw,
|
|
13135
|
+
h: ch,
|
|
13136
|
+
raw: inflated
|
|
13137
|
+
});
|
|
13138
|
+
} else if (celType === CEL_COMPRESSED_TILEMAP) {
|
|
13139
|
+
throw new Error(ERR_TILEMAP_MODE);
|
|
13140
|
+
}
|
|
13141
|
+
break;
|
|
13142
|
+
}
|
|
13143
|
+
case CHUNK_PALETTE: {
|
|
13144
|
+
const size = r.u32();
|
|
13145
|
+
const first = r.u32();
|
|
13146
|
+
const last = r.u32();
|
|
13147
|
+
r.skip(8);
|
|
13148
|
+
for (let i = first; i <= last; i++) {
|
|
13149
|
+
const flags = r.u16();
|
|
13150
|
+
const cr = r.u8();
|
|
13151
|
+
const cg = r.u8();
|
|
13152
|
+
const cb = r.u8();
|
|
13153
|
+
const ca = r.u8();
|
|
13154
|
+
if (flags & 1) {
|
|
13155
|
+
r.str();
|
|
13156
|
+
}
|
|
13157
|
+
palette[i] = [cr, cg, cb, ca];
|
|
13158
|
+
}
|
|
13159
|
+
void size;
|
|
13160
|
+
break;
|
|
13161
|
+
}
|
|
13162
|
+
case CHUNK_OLD_PALETTE_04:
|
|
13163
|
+
case CHUNK_OLD_PALETTE_11: {
|
|
13164
|
+
const isLegacy6Bit = chunkType === CHUNK_OLD_PALETTE_11;
|
|
13165
|
+
if (palette.length === 0) {
|
|
13166
|
+
const packets = r.u16();
|
|
13167
|
+
let idx = 0;
|
|
13168
|
+
for (let p = 0; p < packets; p++) {
|
|
13169
|
+
idx += r.u8();
|
|
13170
|
+
let entries = r.u8();
|
|
13171
|
+
if (entries === 0) {
|
|
13172
|
+
entries = 256;
|
|
13173
|
+
}
|
|
13174
|
+
for (let e = 0; e < entries; e++) {
|
|
13175
|
+
let cr = r.u8();
|
|
13176
|
+
let cg = r.u8();
|
|
13177
|
+
let cb = r.u8();
|
|
13178
|
+
if (isLegacy6Bit) {
|
|
13179
|
+
cr = cr << 2 | cr >> 4;
|
|
13180
|
+
cg = cg << 2 | cg >> 4;
|
|
13181
|
+
cb = cb << 2 | cb >> 4;
|
|
13182
|
+
}
|
|
13183
|
+
palette[idx++] = [cr, cg, cb, 255];
|
|
13184
|
+
}
|
|
13185
|
+
}
|
|
13186
|
+
}
|
|
13187
|
+
break;
|
|
13188
|
+
}
|
|
13189
|
+
case CHUNK_TAGS: {
|
|
13190
|
+
const count = r.u16();
|
|
13191
|
+
r.skip(8);
|
|
13192
|
+
for (let t = 0; t < count; t++) {
|
|
13193
|
+
const from = r.u16();
|
|
13194
|
+
const to = r.u16();
|
|
13195
|
+
const dir = r.u8();
|
|
13196
|
+
const repeat = r.u16();
|
|
13197
|
+
r.skip(6);
|
|
13198
|
+
r.skip(3);
|
|
13199
|
+
r.skip(1);
|
|
13200
|
+
const name = r.str();
|
|
13201
|
+
tags.push({
|
|
13202
|
+
name,
|
|
13203
|
+
from,
|
|
13204
|
+
to,
|
|
13205
|
+
direction: TAG_DIRECTIONS[dir] || "forward",
|
|
13206
|
+
repeat
|
|
13207
|
+
});
|
|
13208
|
+
}
|
|
13209
|
+
break;
|
|
13210
|
+
}
|
|
13211
|
+
case CHUNK_TILESET:
|
|
13212
|
+
throw new Error(ERR_TILEMAP_MODE);
|
|
13213
|
+
default:
|
|
13214
|
+
break;
|
|
13215
|
+
}
|
|
13216
|
+
r.offset = chunkEnd;
|
|
13217
|
+
}
|
|
13218
|
+
frames.push({ duration, cels });
|
|
13219
|
+
r.offset = frameStart + frameSize;
|
|
13220
|
+
}
|
|
13221
|
+
return {
|
|
13222
|
+
width,
|
|
13223
|
+
height,
|
|
13224
|
+
depth,
|
|
13225
|
+
transparentIndex,
|
|
13226
|
+
palette,
|
|
13227
|
+
layers,
|
|
13228
|
+
tags,
|
|
13229
|
+
frames
|
|
13230
|
+
};
|
|
13231
|
+
}
|
|
13232
|
+
function composite(parsed) {
|
|
13233
|
+
const { width, height, depth, frames, layers, palette, transparentIndex } = parsed;
|
|
13234
|
+
const stripWidth = width * frames.length;
|
|
13235
|
+
const stripHeight = height;
|
|
13236
|
+
const canvas = typeof OffscreenCanvas !== "undefined" ? new OffscreenCanvas(stripWidth, stripHeight) : Object.assign(document.createElement("canvas"), {
|
|
13237
|
+
width: stripWidth,
|
|
13238
|
+
height: stripHeight
|
|
13239
|
+
});
|
|
13240
|
+
const ctx = canvas.getContext("2d");
|
|
13241
|
+
ctx.imageSmoothingEnabled = false;
|
|
13242
|
+
for (let f = 0; f < frames.length; f++) {
|
|
13243
|
+
const frame = frames[f];
|
|
13244
|
+
const fx = f * width;
|
|
13245
|
+
const ordered = frame.cels.slice().sort((a, b) => {
|
|
13246
|
+
return a.layer - b.layer;
|
|
13247
|
+
});
|
|
13248
|
+
for (const cel of ordered) {
|
|
13249
|
+
const layer = layers[cel.layer];
|
|
13250
|
+
if (layer && layer.visible === false) {
|
|
13251
|
+
continue;
|
|
13252
|
+
}
|
|
13253
|
+
let source = cel;
|
|
13254
|
+
if (cel.linkFrame !== void 0) {
|
|
13255
|
+
source = frames[cel.linkFrame]?.cels.find((c) => {
|
|
13256
|
+
return c.layer === cel.layer;
|
|
13257
|
+
});
|
|
13258
|
+
if (!source || !source.raw) {
|
|
13259
|
+
continue;
|
|
13260
|
+
}
|
|
13261
|
+
}
|
|
13262
|
+
const rgba = decodeCelPixels(
|
|
13263
|
+
source.raw,
|
|
13264
|
+
source.w,
|
|
13265
|
+
source.h,
|
|
13266
|
+
depth,
|
|
13267
|
+
palette,
|
|
13268
|
+
transparentIndex
|
|
13269
|
+
);
|
|
13270
|
+
const img = new ImageData(rgba, source.w, source.h);
|
|
13271
|
+
const tmp = typeof OffscreenCanvas !== "undefined" ? new OffscreenCanvas(source.w, source.h) : Object.assign(document.createElement("canvas"), {
|
|
13272
|
+
width: source.w,
|
|
13273
|
+
height: source.h
|
|
13274
|
+
});
|
|
13275
|
+
tmp.getContext("2d").putImageData(img, 0, 0);
|
|
13276
|
+
if (cel.opacity !== 255) {
|
|
13277
|
+
ctx.globalAlpha = cel.opacity / 255;
|
|
13278
|
+
}
|
|
13279
|
+
ctx.drawImage(tmp, fx + cel.x, cel.y);
|
|
13280
|
+
if (cel.opacity !== 255) {
|
|
13281
|
+
ctx.globalAlpha = 1;
|
|
13282
|
+
}
|
|
13283
|
+
}
|
|
13284
|
+
}
|
|
13285
|
+
return canvas;
|
|
13286
|
+
}
|
|
13287
|
+
function buildAtlasJSON(parsed, imageName) {
|
|
13288
|
+
const { width, height, frames, tags } = parsed;
|
|
13289
|
+
const out = {};
|
|
13290
|
+
for (let i = 0; i < frames.length; i++) {
|
|
13291
|
+
out[String(i)] = {
|
|
13292
|
+
frame: { x: i * width, y: 0, w: width, h: height },
|
|
13293
|
+
rotated: false,
|
|
13294
|
+
trimmed: false,
|
|
13295
|
+
spriteSourceSize: { x: 0, y: 0, w: width, h: height },
|
|
13296
|
+
sourceSize: { w: width, h: height },
|
|
13297
|
+
duration: frames[i].duration
|
|
13298
|
+
};
|
|
13299
|
+
}
|
|
13300
|
+
return {
|
|
13301
|
+
frames: out,
|
|
13302
|
+
meta: {
|
|
13303
|
+
app: "http://www.aseprite.org/",
|
|
13304
|
+
version: "binary",
|
|
13305
|
+
image: imageName,
|
|
13306
|
+
format: "RGBA8888",
|
|
13307
|
+
size: { w: width * frames.length, h: height },
|
|
13308
|
+
scale: "1",
|
|
13309
|
+
frameTags: tags
|
|
13310
|
+
}
|
|
13311
|
+
};
|
|
13312
|
+
}
|
|
13313
|
+
async function canvasToBitmap(canvas) {
|
|
13314
|
+
if (typeof canvas.transferToImageBitmap === "function" && typeof createImageBitmap === "function") {
|
|
13315
|
+
return canvas.transferToImageBitmap();
|
|
13316
|
+
}
|
|
13317
|
+
if (typeof createImageBitmap === "function") {
|
|
13318
|
+
return await createImageBitmap(canvas);
|
|
13319
|
+
}
|
|
13320
|
+
return canvas;
|
|
13321
|
+
}
|
|
13322
|
+
async function parseAseprite(buffer, imageName = "default") {
|
|
13323
|
+
const parsed = await parseAsepriteFile(buffer);
|
|
13324
|
+
const canvas = composite(parsed);
|
|
13325
|
+
const bitmap = await canvasToBitmap(canvas);
|
|
13326
|
+
const json = buildAtlasJSON(parsed, imageName);
|
|
13327
|
+
return { bitmap, json };
|
|
13328
|
+
}
|
|
13329
|
+
function preloadAseprite(data2, onload2, onerror, settings) {
|
|
13330
|
+
fetchData(data2.src, "arrayBuffer", settings).then(async (buffer) => {
|
|
13331
|
+
const { bitmap, json } = await parseAseprite(buffer, data2.name);
|
|
13332
|
+
imgList[data2.name] = bitmap;
|
|
13333
|
+
jsonList[data2.name] = json;
|
|
13334
|
+
if (typeof onload2 === "function") {
|
|
13335
|
+
onload2();
|
|
13336
|
+
}
|
|
13337
|
+
}).catch((error) => {
|
|
13338
|
+
console.error(`Failed to parse aseprite asset "${data2.name}":`, error);
|
|
13339
|
+
if (typeof onerror === "function") {
|
|
13340
|
+
onerror(error);
|
|
13341
|
+
}
|
|
13342
|
+
});
|
|
13343
|
+
return 1;
|
|
13344
|
+
}
|
|
13345
|
+
|
|
12423
13346
|
// src/loader/parsers/binary.js
|
|
12424
13347
|
function preloadBinary(data2, onload2, onerror, settings) {
|
|
12425
13348
|
fetchData(data2.src, "arrayBuffer", settings).then((response) => {
|
|
@@ -13512,6 +14435,13 @@ var isStringArray = (value) => {
|
|
|
13512
14435
|
function deferredRemove(child, keepalive) {
|
|
13513
14436
|
this.removeChildNow(child, keepalive);
|
|
13514
14437
|
}
|
|
14438
|
+
function registerChildBody(child, worldContainer) {
|
|
14439
|
+
if (child.bodyDef) {
|
|
14440
|
+
worldContainer.adapter.addBody(child, child.bodyDef);
|
|
14441
|
+
} else if (child.body instanceof Body) {
|
|
14442
|
+
worldContainer.addBody(child.body);
|
|
14443
|
+
}
|
|
14444
|
+
}
|
|
13515
14445
|
var globalFloatingCounter = 0;
|
|
13516
14446
|
var Container = class _Container extends Renderable {
|
|
13517
14447
|
/**
|
|
@@ -13536,7 +14466,6 @@ var Container = class _Container extends Renderable {
|
|
|
13536
14466
|
};
|
|
13537
14467
|
this.enableChildBoundsUpdate = false;
|
|
13538
14468
|
this.backgroundColor = colorPool.get(0, 0, 0, 0);
|
|
13539
|
-
this.drawCount = 0;
|
|
13540
14469
|
this.autoTransform = true;
|
|
13541
14470
|
this.isKinematic = false;
|
|
13542
14471
|
this.anchorPoint.set(0, 0);
|
|
@@ -13601,7 +14530,7 @@ var Container = class _Container extends Renderable {
|
|
|
13601
14530
|
throw new Error(`${String(child)} is not an instance of Renderable`);
|
|
13602
14531
|
}
|
|
13603
14532
|
if (child.ancestor instanceof _Container) {
|
|
13604
|
-
child.ancestor.removeChildNow(child);
|
|
14533
|
+
child.ancestor.removeChildNow(child, true);
|
|
13605
14534
|
} else {
|
|
13606
14535
|
child.GUID = createGUID(child.id);
|
|
13607
14536
|
}
|
|
@@ -13619,25 +14548,21 @@ var Container = class _Container extends Renderable {
|
|
|
13619
14548
|
if (this.autoSort === true) {
|
|
13620
14549
|
this.sort();
|
|
13621
14550
|
}
|
|
13622
|
-
if (typeof child.onActivateEvent === "function" && this.isAttachedToRoot()) {
|
|
13623
|
-
child.onActivateEvent();
|
|
13624
|
-
}
|
|
13625
|
-
if (this.enableChildBoundsUpdate === true) {
|
|
13626
|
-
this.updateBounds();
|
|
13627
|
-
}
|
|
13628
14551
|
if (this.isAttachedToRoot()) {
|
|
13629
14552
|
const worldContainer = this.getRootAncestor();
|
|
13630
|
-
|
|
13631
|
-
worldContainer.addBody(child.body);
|
|
13632
|
-
}
|
|
14553
|
+
registerChildBody(child, worldContainer);
|
|
13633
14554
|
if (child instanceof _Container) {
|
|
13634
14555
|
child.forEach((cchild) => {
|
|
13635
|
-
|
|
13636
|
-
worldContainer.addBody(cchild.body);
|
|
13637
|
-
}
|
|
14556
|
+
registerChildBody(cchild, worldContainer);
|
|
13638
14557
|
});
|
|
13639
14558
|
}
|
|
13640
14559
|
}
|
|
14560
|
+
if (typeof child.onActivateEvent === "function" && this.isAttachedToRoot()) {
|
|
14561
|
+
child.onActivateEvent();
|
|
14562
|
+
}
|
|
14563
|
+
if (this.enableChildBoundsUpdate === true) {
|
|
14564
|
+
this.updateBounds();
|
|
14565
|
+
}
|
|
13641
14566
|
this.isDirty = true;
|
|
13642
14567
|
this.onChildChange.call(this, this.getChildren().length - 1);
|
|
13643
14568
|
return child;
|
|
@@ -13655,7 +14580,7 @@ var Container = class _Container extends Renderable {
|
|
|
13655
14580
|
}
|
|
13656
14581
|
if (index >= 0 && index <= this.getChildren().length) {
|
|
13657
14582
|
if (child.ancestor instanceof _Container) {
|
|
13658
|
-
child.ancestor.removeChildNow(child);
|
|
14583
|
+
child.ancestor.removeChildNow(child, true);
|
|
13659
14584
|
} else {
|
|
13660
14585
|
child.GUID = createGUID();
|
|
13661
14586
|
}
|
|
@@ -13665,25 +14590,21 @@ var Container = class _Container extends Renderable {
|
|
|
13665
14590
|
child.floating = false;
|
|
13666
14591
|
}
|
|
13667
14592
|
child.updateBounds();
|
|
13668
|
-
if (typeof child.onActivateEvent === "function" && this.isAttachedToRoot()) {
|
|
13669
|
-
child.onActivateEvent();
|
|
13670
|
-
}
|
|
13671
|
-
if (this.enableChildBoundsUpdate === true) {
|
|
13672
|
-
this.updateBounds();
|
|
13673
|
-
}
|
|
13674
14593
|
if (this.isAttachedToRoot()) {
|
|
13675
14594
|
const worldContainer = this.getRootAncestor();
|
|
13676
|
-
|
|
13677
|
-
worldContainer.addBody(child.body);
|
|
13678
|
-
}
|
|
14595
|
+
registerChildBody(child, worldContainer);
|
|
13679
14596
|
if (child instanceof _Container) {
|
|
13680
14597
|
child.forEach((cchild) => {
|
|
13681
|
-
|
|
13682
|
-
worldContainer.addBody(cchild.body);
|
|
13683
|
-
}
|
|
14598
|
+
registerChildBody(cchild, worldContainer);
|
|
13684
14599
|
});
|
|
13685
14600
|
}
|
|
13686
14601
|
}
|
|
14602
|
+
if (typeof child.onActivateEvent === "function" && this.isAttachedToRoot()) {
|
|
14603
|
+
child.onActivateEvent();
|
|
14604
|
+
}
|
|
14605
|
+
if (this.enableChildBoundsUpdate === true) {
|
|
14606
|
+
this.updateBounds();
|
|
14607
|
+
}
|
|
13687
14608
|
this.isDirty = true;
|
|
13688
14609
|
this.onChildChange.call(this, index);
|
|
13689
14610
|
return child;
|
|
@@ -13928,8 +14849,11 @@ var Container = class _Container extends Renderable {
|
|
|
13928
14849
|
}
|
|
13929
14850
|
/**
|
|
13930
14851
|
* @ignore
|
|
14852
|
+
* @param {...*} _args - reserved; widens the signature so subclass
|
|
14853
|
+
* overrides like `onActivateEvent(app)` remain structurally
|
|
14854
|
+
* assignable to the base Container/Renderable type.
|
|
13931
14855
|
*/
|
|
13932
|
-
onActivateEvent() {
|
|
14856
|
+
onActivateEvent(..._args) {
|
|
13933
14857
|
this.forEach((child) => {
|
|
13934
14858
|
if (typeof child.onActivateEvent === "function") {
|
|
13935
14859
|
child.onActivateEvent();
|
|
@@ -13961,10 +14885,23 @@ var Container = class _Container extends Renderable {
|
|
|
13961
14885
|
if (typeof child.onDeactivateEvent === "function") {
|
|
13962
14886
|
child.onDeactivateEvent();
|
|
13963
14887
|
}
|
|
13964
|
-
|
|
13965
|
-
|
|
13966
|
-
if (
|
|
13967
|
-
root.
|
|
14888
|
+
const root = this.getRootAncestor();
|
|
14889
|
+
if (root?.broadphase) {
|
|
14890
|
+
if (typeof child.addChild === "function") {
|
|
14891
|
+
root.broadphase.removeContainer(child);
|
|
14892
|
+
}
|
|
14893
|
+
root.broadphase.remove(child);
|
|
14894
|
+
}
|
|
14895
|
+
if (child.body) {
|
|
14896
|
+
const world = root;
|
|
14897
|
+
if (world?.adapter?.removeBody) {
|
|
14898
|
+
world.adapter.removeBody(child);
|
|
14899
|
+
} else if (child.body instanceof Body) {
|
|
14900
|
+
root?.removeBody?.(child.body);
|
|
14901
|
+
} else {
|
|
14902
|
+
console.warn(
|
|
14903
|
+
"melonJS: removeChildNow could not clean up an adapter-specific body \u2014 the container is not attached to a world with an adapter. The body remains in the physics engine."
|
|
14904
|
+
);
|
|
13968
14905
|
}
|
|
13969
14906
|
}
|
|
13970
14907
|
if (!keepalive) {
|
|
@@ -14074,8 +15011,11 @@ var Container = class _Container extends Renderable {
|
|
|
14074
15011
|
}
|
|
14075
15012
|
/**
|
|
14076
15013
|
* @ignore
|
|
15014
|
+
* @param {...*} _args - reserved; widens the signature so subclass
|
|
15015
|
+
* overrides like `onDeactivateEvent(app)` remain structurally
|
|
15016
|
+
* assignable to the base Container/Renderable type.
|
|
14077
15017
|
*/
|
|
14078
|
-
onDeactivateEvent() {
|
|
15018
|
+
onDeactivateEvent(..._args) {
|
|
14079
15019
|
this.forEach((child) => {
|
|
14080
15020
|
if (typeof child.onDeactivateEvent === "function") {
|
|
14081
15021
|
child.onDeactivateEvent();
|
|
@@ -14160,7 +15100,6 @@ var Container = class _Container extends Renderable {
|
|
|
14160
15100
|
*/
|
|
14161
15101
|
draw(renderer2, viewport) {
|
|
14162
15102
|
const bounds = this.getBounds();
|
|
14163
|
-
this.drawCount = 0;
|
|
14164
15103
|
renderer2.translate(this.pos.x, this.pos.y);
|
|
14165
15104
|
if (this.root === false && this.clipping === true && bounds.isFinite() === true && Number.isFinite(this.width) === true && Number.isFinite(this.height) === true) {
|
|
14166
15105
|
renderer2.clipRect(0, 0, this.width, this.height);
|
|
@@ -14192,7 +15131,6 @@ var Container = class _Container extends Renderable {
|
|
|
14192
15131
|
}
|
|
14193
15132
|
renderer2.restore();
|
|
14194
15133
|
}
|
|
14195
|
-
this.drawCount++;
|
|
14196
15134
|
}
|
|
14197
15135
|
}
|
|
14198
15136
|
}
|
|
@@ -14504,16 +15442,14 @@ var CanvasRenderTarget = class extends RenderTarget {
|
|
|
14504
15442
|
* @ignore
|
|
14505
15443
|
*/
|
|
14506
15444
|
destroy(renderer2) {
|
|
14507
|
-
if (renderer2 && typeof renderer2.gl !== "undefined" &&
|
|
15445
|
+
if (renderer2 && typeof renderer2.gl !== "undefined" && this.canvas && renderer2.cache.has(this.canvas)) {
|
|
14508
15446
|
renderer2.setBatcher("quad");
|
|
14509
|
-
renderer2.currentBatcher.deleteTexture2D(
|
|
14510
|
-
renderer2.currentBatcher.getTexture2D(this.glTextureUnit)
|
|
14511
|
-
);
|
|
14512
|
-
this.glTextureUnit = void 0;
|
|
15447
|
+
renderer2.currentBatcher.deleteTexture2D(renderer2.cache.get(this.canvas));
|
|
14513
15448
|
}
|
|
14514
15449
|
if (renderer2) {
|
|
14515
15450
|
renderer2.cache.delete(this.canvas);
|
|
14516
15451
|
}
|
|
15452
|
+
this.glTextureUnit = void 0;
|
|
14517
15453
|
this.context = void 0;
|
|
14518
15454
|
this.canvas = void 0;
|
|
14519
15455
|
}
|
|
@@ -15512,6 +16448,7 @@ var Renderer = class {
|
|
|
15512
16448
|
this.currentColor = this.renderState.currentColor;
|
|
15513
16449
|
this.currentTint = this.renderState.currentTint;
|
|
15514
16450
|
this.currentScissor = this.renderState.currentScissor;
|
|
16451
|
+
this.lineWidth = 1;
|
|
15515
16452
|
this.maskLevel = 0;
|
|
15516
16453
|
this.projectionMatrix = new Matrix3d();
|
|
15517
16454
|
this.uvOffset = 0;
|
|
@@ -16059,6 +16996,128 @@ var Renderer = class {
|
|
|
16059
16996
|
context.drawImage(src, 0, 0);
|
|
16060
16997
|
return canvasTexture.canvas;
|
|
16061
16998
|
}
|
|
16999
|
+
/**
|
|
17000
|
+
* Push the current transform / alpha / clip state onto an internal
|
|
17001
|
+
* stack. Pair with {@link Renderer#restore}. Implemented by subclasses.
|
|
17002
|
+
*/
|
|
17003
|
+
save() {
|
|
17004
|
+
}
|
|
17005
|
+
/**
|
|
17006
|
+
* Pop the topmost saved state from the stack, restoring transform,
|
|
17007
|
+
* alpha and clip. Pair with {@link Renderer#save}. Implemented by
|
|
17008
|
+
* subclasses.
|
|
17009
|
+
*/
|
|
17010
|
+
restore() {
|
|
17011
|
+
}
|
|
17012
|
+
/**
|
|
17013
|
+
* Translate the current transform by `(x, y)`. Implemented by subclasses.
|
|
17014
|
+
* @param {number} x - horizontal translation in pixels
|
|
17015
|
+
* @param {number} y - vertical translation in pixels
|
|
17016
|
+
*/
|
|
17017
|
+
// eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars
|
|
17018
|
+
translate(x, y) {
|
|
17019
|
+
}
|
|
17020
|
+
/**
|
|
17021
|
+
* Rotate the current transform by `angle` (radians). Implemented by subclasses.
|
|
17022
|
+
* @param {number} angle - rotation angle in radians
|
|
17023
|
+
*/
|
|
17024
|
+
// eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars
|
|
17025
|
+
rotate(angle) {
|
|
17026
|
+
}
|
|
17027
|
+
/**
|
|
17028
|
+
* Scale the current transform by `(x, y)`. Implemented by subclasses.
|
|
17029
|
+
* @param {number} x - horizontal scale factor
|
|
17030
|
+
* @param {number} y - vertical scale factor
|
|
17031
|
+
*/
|
|
17032
|
+
// eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars
|
|
17033
|
+
scale(x, y) {
|
|
17034
|
+
}
|
|
17035
|
+
/**
|
|
17036
|
+
* Set the renderer global alpha (0..1) for subsequent draw calls.
|
|
17037
|
+
* Implemented by subclasses.
|
|
17038
|
+
* @param {number} alpha - opacity value in the [0..1] range
|
|
17039
|
+
*/
|
|
17040
|
+
// eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars
|
|
17041
|
+
setGlobalAlpha(alpha2) {
|
|
17042
|
+
}
|
|
17043
|
+
/**
|
|
17044
|
+
* Fill an axis-aligned rectangle with the current color.
|
|
17045
|
+
* Implemented by subclasses.
|
|
17046
|
+
* @param {number} x - left edge in viewport pixels
|
|
17047
|
+
* @param {number} y - top edge in viewport pixels
|
|
17048
|
+
* @param {number} width - rectangle width
|
|
17049
|
+
* @param {number} height - rectangle height
|
|
17050
|
+
*/
|
|
17051
|
+
// eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars
|
|
17052
|
+
fillRect(x, y, width, height) {
|
|
17053
|
+
}
|
|
17054
|
+
/**
|
|
17055
|
+
* Stroke (outline) an axis-aligned rectangle with the current color
|
|
17056
|
+
* at the current {@link Renderer#lineWidth}. Implemented by subclasses.
|
|
17057
|
+
* @param {number} x - left edge in viewport pixels
|
|
17058
|
+
* @param {number} y - top edge in viewport pixels
|
|
17059
|
+
* @param {number} width - rectangle width
|
|
17060
|
+
* @param {number} height - rectangle height
|
|
17061
|
+
* @param {boolean} [fill=false] - also fill the rectangle
|
|
17062
|
+
*/
|
|
17063
|
+
// eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars
|
|
17064
|
+
strokeRect(x, y, width, height, fill) {
|
|
17065
|
+
}
|
|
17066
|
+
/**
|
|
17067
|
+
* Fill an axis-aligned ellipse with the current color.
|
|
17068
|
+
* Implemented by subclasses.
|
|
17069
|
+
* @param {number} x - ellipse center X in viewport pixels
|
|
17070
|
+
* @param {number} y - ellipse center Y in viewport pixels
|
|
17071
|
+
* @param {number} w - horizontal radius
|
|
17072
|
+
* @param {number} h - vertical radius
|
|
17073
|
+
*/
|
|
17074
|
+
// eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars
|
|
17075
|
+
fillEllipse(x, y, w, h) {
|
|
17076
|
+
}
|
|
17077
|
+
/**
|
|
17078
|
+
* Stroke (outline) an axis-aligned ellipse with the current color
|
|
17079
|
+
* at the current {@link Renderer#lineWidth}. Implemented by subclasses.
|
|
17080
|
+
* @param {number} x - ellipse center X in viewport pixels
|
|
17081
|
+
* @param {number} y - ellipse center Y in viewport pixels
|
|
17082
|
+
* @param {number} w - horizontal radius
|
|
17083
|
+
* @param {number} h - vertical radius
|
|
17084
|
+
* @param {boolean} [fill=false] - also fill the ellipse
|
|
17085
|
+
*/
|
|
17086
|
+
// eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars
|
|
17087
|
+
strokeEllipse(x, y, w, h, fill) {
|
|
17088
|
+
}
|
|
17089
|
+
/**
|
|
17090
|
+
* Draw a straight line between two points with the current color
|
|
17091
|
+
* at the current {@link Renderer#lineWidth}. Implemented by subclasses.
|
|
17092
|
+
* @param {number} startX - start X in viewport pixels
|
|
17093
|
+
* @param {number} startY - start Y in viewport pixels
|
|
17094
|
+
* @param {number} endX - end X in viewport pixels
|
|
17095
|
+
* @param {number} endY - end Y in viewport pixels
|
|
17096
|
+
*/
|
|
17097
|
+
// eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars
|
|
17098
|
+
strokeLine(startX, startY, endX, endY) {
|
|
17099
|
+
}
|
|
17100
|
+
/**
|
|
17101
|
+
* Draw an image or sub-image into the framebuffer.
|
|
17102
|
+
* Implemented by subclasses (`CanvasRenderer` / `WebGLRenderer`).
|
|
17103
|
+
*
|
|
17104
|
+
* The 9-argument signature shown here is the canonical form used
|
|
17105
|
+
* throughout the engine; the concrete renderers also accept the
|
|
17106
|
+
* shorter `drawImage(image, dx, dy)` and `drawImage(image, dx, dy,
|
|
17107
|
+
* dWidth, dHeight)` variants from the HTML Canvas2D spec.
|
|
17108
|
+
* @param {HTMLImageElement|SVGImageElement|HTMLVideoElement|HTMLCanvasElement|ImageBitmap|OffscreenCanvas|VideoFrame} image - the source image
|
|
17109
|
+
* @param {number} sx - source rectangle left
|
|
17110
|
+
* @param {number} sy - source rectangle top
|
|
17111
|
+
* @param {number} sw - source rectangle width
|
|
17112
|
+
* @param {number} sh - source rectangle height
|
|
17113
|
+
* @param {number} dx - destination left
|
|
17114
|
+
* @param {number} dy - destination top
|
|
17115
|
+
* @param {number} dw - destination width
|
|
17116
|
+
* @param {number} dh - destination height
|
|
17117
|
+
*/
|
|
17118
|
+
// eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars
|
|
17119
|
+
drawImage(image, sx, sy, sw, sh, dx, dy, dw, dh) {
|
|
17120
|
+
}
|
|
16062
17121
|
/**
|
|
16063
17122
|
* A mask limits rendering elements to the shape and position of the given mask object.
|
|
16064
17123
|
* So, if the renderable is larger than the mask, only the intersecting part of the renderable will be visible.
|
|
@@ -16161,7 +17220,7 @@ var ArrayMultimap = class {
|
|
|
16161
17220
|
};
|
|
16162
17221
|
|
|
16163
17222
|
// src/video/texture/parser/aseprite.js
|
|
16164
|
-
function
|
|
17223
|
+
function parseAseprite2(data2, textureAtlas) {
|
|
16165
17224
|
const atlas = {};
|
|
16166
17225
|
const frames = data2.frames;
|
|
16167
17226
|
for (const name in frames) {
|
|
@@ -16419,7 +17478,7 @@ var TextureAtlas = class {
|
|
|
16419
17478
|
} else {
|
|
16420
17479
|
this.atlases.set(
|
|
16421
17480
|
atlas.meta.image || "default",
|
|
16422
|
-
|
|
17481
|
+
parseAseprite2(atlas, this)
|
|
16423
17482
|
);
|
|
16424
17483
|
}
|
|
16425
17484
|
break;
|
|
@@ -20553,6 +21612,65 @@ var TMXTileset = class {
|
|
|
20553
21612
|
}
|
|
20554
21613
|
if (!this.isCollection) {
|
|
20555
21614
|
this._initAtlas(tileset);
|
|
21615
|
+
this._applyAsepriteFrameTags(tileset);
|
|
21616
|
+
}
|
|
21617
|
+
}
|
|
21618
|
+
/**
|
|
21619
|
+
* If `tileset.image` was loaded via the .aseprite binary parser, walk
|
|
21620
|
+
* its `meta.frameTags` and synthesize per-tile animations. Each tag
|
|
21621
|
+
* `{ from, to }` becomes an animation on tile `from` that cycles through
|
|
21622
|
+
* tiles `[from..to]` using each frame's `duration`.
|
|
21623
|
+
*
|
|
21624
|
+
* Throws on tag configurations that don't map to Tiled's animation model:
|
|
21625
|
+
* non-forward direction (Tiled animations are forward-only) and non-zero
|
|
21626
|
+
* repeat counts (Tiled animations loop forever).
|
|
21627
|
+
* @ignore
|
|
21628
|
+
*/
|
|
21629
|
+
_applyAsepriteFrameTags(tileset) {
|
|
21630
|
+
const atlas = getJSON(getBasename("" + tileset.image));
|
|
21631
|
+
if (!atlas || !atlas.meta || typeof atlas.meta.app !== "string") {
|
|
21632
|
+
return;
|
|
21633
|
+
}
|
|
21634
|
+
if (!atlas.meta.app.includes("aseprite")) {
|
|
21635
|
+
return;
|
|
21636
|
+
}
|
|
21637
|
+
const tags = atlas.meta.frameTags;
|
|
21638
|
+
if (!Array.isArray(tags) || tags.length === 0) {
|
|
21639
|
+
return;
|
|
21640
|
+
}
|
|
21641
|
+
const frames = atlas.meta.frames || atlas.frames || {};
|
|
21642
|
+
for (const tag of tags) {
|
|
21643
|
+
if (tag.direction !== "forward") {
|
|
21644
|
+
throw new Error(
|
|
21645
|
+
`melonJS: aseprite tag "${tag.name}" on tileset '${this.name}' uses direction "${tag.direction}" \u2014 Tiled tile animations only support forward playback. Re-export the tag as "forward" in Aseprite.`
|
|
21646
|
+
);
|
|
21647
|
+
}
|
|
21648
|
+
if (typeof tag.repeat === "number" && tag.repeat !== 0) {
|
|
21649
|
+
throw new Error(
|
|
21650
|
+
`melonJS: aseprite tag "${tag.name}" on tileset '${this.name}' has a finite repeat count (${tag.repeat}) \u2014 Tiled tile animations only support infinite looping. Set the tag's repeat to "\u221E" in Aseprite.`
|
|
21651
|
+
);
|
|
21652
|
+
}
|
|
21653
|
+
const anim = [];
|
|
21654
|
+
for (let f = tag.from; f <= tag.to; f++) {
|
|
21655
|
+
const frame = frames[String(f)] || frames[f];
|
|
21656
|
+
anim.push({
|
|
21657
|
+
tileid: f,
|
|
21658
|
+
duration: frame && typeof frame.duration === "number" ? frame.duration : 100
|
|
21659
|
+
});
|
|
21660
|
+
}
|
|
21661
|
+
if (anim.length === 0) {
|
|
21662
|
+
continue;
|
|
21663
|
+
}
|
|
21664
|
+
if (this.animations.has(tag.from)) {
|
|
21665
|
+
continue;
|
|
21666
|
+
}
|
|
21667
|
+
this.isAnimated = true;
|
|
21668
|
+
this.animations.set(tag.from, {
|
|
21669
|
+
dt: 0,
|
|
21670
|
+
idx: 0,
|
|
21671
|
+
frames: anim,
|
|
21672
|
+
cur: anim[0]
|
|
21673
|
+
});
|
|
20556
21674
|
}
|
|
20557
21675
|
}
|
|
20558
21676
|
/**
|
|
@@ -21236,9 +22354,14 @@ var TMXTileMap = class {
|
|
|
21236
22354
|
settings.tint = parseTintColor(settings.tintcolor);
|
|
21237
22355
|
}
|
|
21238
22356
|
const obj = createTMXObject(settings, this);
|
|
21239
|
-
if (isCollisionGroup && !settings.name
|
|
21240
|
-
obj.
|
|
21241
|
-
|
|
22357
|
+
if (isCollisionGroup && !settings.name) {
|
|
22358
|
+
if (obj.bodyDef) {
|
|
22359
|
+
obj.bodyDef.collisionType = collision.types.WORLD_SHAPE;
|
|
22360
|
+
obj.bodyDef.type = "static";
|
|
22361
|
+
} else if (obj.body) {
|
|
22362
|
+
obj.body.collisionType = collision.types.WORLD_SHAPE;
|
|
22363
|
+
obj.body.isStatic = true;
|
|
22364
|
+
}
|
|
21242
22365
|
}
|
|
21243
22366
|
if (obj.isRenderable === true && !(settings instanceof TMXLayer)) {
|
|
21244
22367
|
if (!settings.visible) {
|
|
@@ -21657,6 +22780,7 @@ function setBaseURL(type, url = "./") {
|
|
|
21657
22780
|
baseURL["tmx"] = url;
|
|
21658
22781
|
baseURL["tsx"] = url;
|
|
21659
22782
|
baseURL["fontface"] = url;
|
|
22783
|
+
baseURL["aseprite"] = url;
|
|
21660
22784
|
}
|
|
21661
22785
|
}
|
|
21662
22786
|
var onload;
|
|
@@ -21679,6 +22803,7 @@ function initParsers() {
|
|
|
21679
22803
|
setParser("video", preloadVideo);
|
|
21680
22804
|
setParser("obj", preloadOBJ);
|
|
21681
22805
|
setParser("mtl", preloadMTL);
|
|
22806
|
+
setParser("aseprite", preloadAseprite);
|
|
21682
22807
|
parserInitialized = true;
|
|
21683
22808
|
}
|
|
21684
22809
|
function completeLoading(onloadcb) {
|
|
@@ -21852,6 +22977,16 @@ function unload2(asset) {
|
|
|
21852
22977
|
}
|
|
21853
22978
|
delete mtlList[asset.name];
|
|
21854
22979
|
return true;
|
|
22980
|
+
case "aseprite": {
|
|
22981
|
+
const hadImage = asset.name in imgList;
|
|
22982
|
+
const hadJson = asset.name in jsonList;
|
|
22983
|
+
if (!hadImage && !hadJson) {
|
|
22984
|
+
return false;
|
|
22985
|
+
}
|
|
22986
|
+
delete imgList[asset.name];
|
|
22987
|
+
delete jsonList[asset.name];
|
|
22988
|
+
return true;
|
|
22989
|
+
}
|
|
21855
22990
|
default:
|
|
21856
22991
|
throw new Error(
|
|
21857
22992
|
"unload : unknown or invalid resource type : " + asset.type
|
|
@@ -23799,7 +24934,6 @@ var Camera2d = class extends Renderable {
|
|
|
23799
24934
|
}
|
|
23800
24935
|
}
|
|
23801
24936
|
/** @ignore */
|
|
23802
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
23803
24937
|
updateTarget(_dt) {
|
|
23804
24938
|
if (this.target) {
|
|
23805
24939
|
targetV.setV(this.pos);
|
|
@@ -24528,7 +25662,7 @@ function _endFreeze() {
|
|
|
24528
25662
|
const wasOwnedPause = _freezeStartedPause;
|
|
24529
25663
|
_freezeStartedPause = false;
|
|
24530
25664
|
if (wasOwnedPause) {
|
|
24531
|
-
|
|
25665
|
+
state2.resume(_freezeMusic);
|
|
24532
25666
|
}
|
|
24533
25667
|
_resolveFreezeWaiters();
|
|
24534
25668
|
}
|
|
@@ -24573,19 +25707,19 @@ function _switchState(stateId) {
|
|
|
24573
25707
|
}
|
|
24574
25708
|
var _app;
|
|
24575
25709
|
once(BOOT, () => {
|
|
24576
|
-
|
|
24577
|
-
|
|
25710
|
+
state2.set(state2.LOADING, new loadingscreen_default());
|
|
25711
|
+
state2.set(state2.DEFAULT, new Stage());
|
|
24578
25712
|
on(GAME_INIT, (app) => {
|
|
24579
25713
|
_app = app;
|
|
24580
25714
|
});
|
|
24581
25715
|
once(VIDEO_INIT, () => {
|
|
24582
|
-
|
|
25716
|
+
state2.change(state2.DEFAULT, true);
|
|
24583
25717
|
});
|
|
24584
25718
|
on(BLUR, () => {
|
|
24585
25719
|
_cancelFreeze();
|
|
24586
25720
|
});
|
|
24587
25721
|
});
|
|
24588
|
-
var
|
|
25722
|
+
var state2 = {
|
|
24589
25723
|
/**
|
|
24590
25724
|
* default state ID for Loading Stage
|
|
24591
25725
|
*/
|
|
@@ -24875,7 +26009,7 @@ var state = {
|
|
|
24875
26009
|
const onComplete = () => {
|
|
24876
26010
|
defer(
|
|
24877
26011
|
_switchState,
|
|
24878
|
-
|
|
26012
|
+
state2,
|
|
24879
26013
|
stateId
|
|
24880
26014
|
);
|
|
24881
26015
|
};
|
|
@@ -24961,7 +26095,7 @@ var state = {
|
|
|
24961
26095
|
return _state === stateId;
|
|
24962
26096
|
}
|
|
24963
26097
|
};
|
|
24964
|
-
var state_default =
|
|
26098
|
+
var state_default = state2;
|
|
24965
26099
|
|
|
24966
26100
|
// src/system/timer.ts
|
|
24967
26101
|
var Timer = class {
|
|
@@ -25109,10 +26243,17 @@ var Timer = class {
|
|
|
25109
26243
|
return this.delta;
|
|
25110
26244
|
}
|
|
25111
26245
|
/**
|
|
25112
|
-
*
|
|
26246
|
+
* update
|
|
25113
26247
|
* @ignore
|
|
25114
26248
|
*/
|
|
25115
|
-
|
|
26249
|
+
update(time) {
|
|
26250
|
+
this.last = this.now;
|
|
26251
|
+
this.now = time;
|
|
26252
|
+
this.delta = this.now - this.last;
|
|
26253
|
+
if (this.delta < 0) {
|
|
26254
|
+
this.delta = 0;
|
|
26255
|
+
}
|
|
26256
|
+
this.tick = this.delta > this.minstep && this.interpolation ? this.delta / this.step : 1;
|
|
25116
26257
|
this.framecount++;
|
|
25117
26258
|
this.framedelta += this.delta;
|
|
25118
26259
|
if (this.framecount % 10 === 0) {
|
|
@@ -25124,19 +26265,6 @@ var Timer = class {
|
|
|
25124
26265
|
this.framedelta = 0;
|
|
25125
26266
|
this.framecount = 0;
|
|
25126
26267
|
}
|
|
25127
|
-
}
|
|
25128
|
-
/**
|
|
25129
|
-
* update
|
|
25130
|
-
* @ignore
|
|
25131
|
-
*/
|
|
25132
|
-
update(time) {
|
|
25133
|
-
this.last = this.now;
|
|
25134
|
-
this.now = time;
|
|
25135
|
-
this.delta = this.now - this.last;
|
|
25136
|
-
if (this.delta < 0) {
|
|
25137
|
-
this.delta = 0;
|
|
25138
|
-
}
|
|
25139
|
-
this.tick = this.delta > this.minstep && this.interpolation ? this.delta / this.step : 1;
|
|
25140
26268
|
this.updateTimers();
|
|
25141
26269
|
}
|
|
25142
26270
|
/**
|
|
@@ -26075,14 +27203,24 @@ function createShapeObject(settings) {
|
|
|
26075
27203
|
settings.height
|
|
26076
27204
|
);
|
|
26077
27205
|
const shape = getDefaultShape(settings);
|
|
27206
|
+
if (!shape || Array.isArray(shape) && shape.length === 0) {
|
|
27207
|
+
throw new Error(
|
|
27208
|
+
`melonJS: TMX object (id=${settings.id}, name=${settings.name ?? ""}, type=${settings.type ?? ""}) has no usable collision shape. Check the object's geometry in Tiled.`
|
|
27209
|
+
);
|
|
27210
|
+
}
|
|
26078
27211
|
obj.anchorPoint.set(0, 0);
|
|
26079
27212
|
obj.name = settings.name;
|
|
26080
27213
|
obj.type = settings.type;
|
|
26081
27214
|
obj.class = settings.class || settings.type;
|
|
26082
27215
|
obj.id = settings.id;
|
|
26083
|
-
obj.
|
|
26084
|
-
|
|
26085
|
-
|
|
27216
|
+
obj.bodyDef = {
|
|
27217
|
+
type: "static",
|
|
27218
|
+
shapes: Array.isArray(shape) ? shape : [shape]
|
|
27219
|
+
};
|
|
27220
|
+
const bounds = boundsPool.get();
|
|
27221
|
+
bounds.addShapes(obj.bodyDef.shapes, true);
|
|
27222
|
+
obj.resize(bounds.width, bounds.height);
|
|
27223
|
+
boundsPool.release(bounds);
|
|
26086
27224
|
obj.pos.z = settings.z;
|
|
26087
27225
|
return obj;
|
|
26088
27226
|
}
|
|
@@ -26909,8 +28047,10 @@ function createTextObject(settings) {
|
|
|
26909
28047
|
function createTileObject(settings) {
|
|
26910
28048
|
const shape = getDefaultShape(settings);
|
|
26911
28049
|
const obj = settings.tile.getRenderable(settings);
|
|
26912
|
-
obj.
|
|
26913
|
-
|
|
28050
|
+
obj.bodyDef = {
|
|
28051
|
+
type: "static",
|
|
28052
|
+
shapes: Array.isArray(shape) ? shape : [shape]
|
|
28053
|
+
};
|
|
26914
28054
|
obj.pos.setMuted(settings.x, settings.y, settings.z);
|
|
26915
28055
|
return obj;
|
|
26916
28056
|
}
|
|
@@ -27217,10 +28357,14 @@ var Collectable = class extends Sprite {
|
|
|
27217
28357
|
vector2dPool.get(this.width, this.height)
|
|
27218
28358
|
]);
|
|
27219
28359
|
}
|
|
27220
|
-
this.
|
|
27221
|
-
|
|
27222
|
-
|
|
27223
|
-
|
|
28360
|
+
this.bodyDef = {
|
|
28361
|
+
type: "static",
|
|
28362
|
+
shapes: Array.isArray(shape) ? shape : [shape],
|
|
28363
|
+
collisionType: collision.types.COLLECTABLE_OBJECT,
|
|
28364
|
+
// by default only collides with PLAYER_OBJECT
|
|
28365
|
+
collisionMask: collision.types.PLAYER_OBJECT,
|
|
28366
|
+
isSensor: true
|
|
28367
|
+
};
|
|
27224
28368
|
if (settings.anchorPoint) {
|
|
27225
28369
|
this.anchorPoint.set(settings.anchorPoint.x, settings.anchorPoint.y);
|
|
27226
28370
|
} else {
|
|
@@ -28182,11 +29326,17 @@ var Trigger = class extends Renderable {
|
|
|
28182
29326
|
vector2dPool.get(this.width, this.height)
|
|
28183
29327
|
]);
|
|
28184
29328
|
}
|
|
28185
|
-
this.
|
|
28186
|
-
|
|
28187
|
-
|
|
28188
|
-
|
|
28189
|
-
|
|
29329
|
+
this.bodyDef = {
|
|
29330
|
+
type: "static",
|
|
29331
|
+
shapes: Array.isArray(shape) ? shape : [shape],
|
|
29332
|
+
collisionType: collision.types.ACTION_OBJECT,
|
|
29333
|
+
collisionMask: collision.types.PLAYER_OBJECT,
|
|
29334
|
+
isSensor: true
|
|
29335
|
+
};
|
|
29336
|
+
const bounds = boundsPool.get();
|
|
29337
|
+
bounds.addShapes(this.bodyDef.shapes, true);
|
|
29338
|
+
this.resize(bounds.width, bounds.height);
|
|
29339
|
+
boundsPool.release(bounds);
|
|
28190
29340
|
}
|
|
28191
29341
|
/**
|
|
28192
29342
|
* @ignore
|
|
@@ -28277,21 +29427,20 @@ var Trigger = class extends Renderable {
|
|
|
28277
29427
|
}
|
|
28278
29428
|
}
|
|
28279
29429
|
/**
|
|
28280
|
-
*
|
|
28281
|
-
*
|
|
28282
|
-
*
|
|
28283
|
-
*
|
|
29430
|
+
* Fire the trigger when an entity first enters the trigger zone.
|
|
29431
|
+
* Using `onCollisionStart` rather than `onCollision` so the event
|
|
29432
|
+
* runs exactly once per entry — under the legacy alias this would
|
|
29433
|
+
* fire every frame the entity was inside the trigger.
|
|
28284
29434
|
*/
|
|
28285
|
-
|
|
29435
|
+
onCollisionStart() {
|
|
28286
29436
|
if (this.name === "Trigger") {
|
|
28287
29437
|
this.triggerEvent();
|
|
28288
29438
|
}
|
|
28289
|
-
return false;
|
|
28290
29439
|
}
|
|
28291
29440
|
};
|
|
28292
29441
|
|
|
28293
29442
|
// src/version.ts
|
|
28294
|
-
var version = "19.
|
|
29443
|
+
var version = "19.5.0";
|
|
28295
29444
|
|
|
28296
29445
|
// src/system/bootstrap.ts
|
|
28297
29446
|
var initialized = false;
|
|
@@ -29527,7 +30676,167 @@ var ResponseObject = class {
|
|
|
29527
30676
|
};
|
|
29528
30677
|
var response_default = ResponseObject;
|
|
29529
30678
|
|
|
29530
|
-
// src/physics/
|
|
30679
|
+
// src/physics/builtin/raycast.ts
|
|
30680
|
+
function _cross(ax, ay, bx, by) {
|
|
30681
|
+
return ax * by - ay * bx;
|
|
30682
|
+
}
|
|
30683
|
+
function _computeShapeAbsPos(renderable, shape, outPos) {
|
|
30684
|
+
const ancestor = renderable.ancestor;
|
|
30685
|
+
const ancestorAbs = ancestor && typeof ancestor.getAbsolutePosition === "function" ? ancestor.getAbsolutePosition() : null;
|
|
30686
|
+
const ax = ancestorAbs ? ancestorAbs.x : 0;
|
|
30687
|
+
const ay = ancestorAbs ? ancestorAbs.y : 0;
|
|
30688
|
+
outPos.x = renderable.pos.x + ax + shape.pos.x;
|
|
30689
|
+
outPos.y = renderable.pos.y + ay + shape.pos.y;
|
|
30690
|
+
}
|
|
30691
|
+
var _shapeAbs = new Vector2d(0, 0);
|
|
30692
|
+
function _raycastPolygon(fromX, fromY, dx, dy, shape, shapeAbsX, shapeAbsY) {
|
|
30693
|
+
const points = shape.points;
|
|
30694
|
+
const normals = shape.normals;
|
|
30695
|
+
const len = points.length;
|
|
30696
|
+
let bestT = Infinity;
|
|
30697
|
+
let bestNormalIdx = -1;
|
|
30698
|
+
for (let i = 0; i < len; i++) {
|
|
30699
|
+
const p0 = points[i];
|
|
30700
|
+
const p1 = points[(i + 1) % len];
|
|
30701
|
+
const ax = shapeAbsX + p0.x;
|
|
30702
|
+
const ay = shapeAbsY + p0.y;
|
|
30703
|
+
const ex = p1.x - p0.x;
|
|
30704
|
+
const ey = p1.y - p0.y;
|
|
30705
|
+
const denom = _cross(dx, dy, ex, ey);
|
|
30706
|
+
if (Math.abs(denom) < 1e-9) {
|
|
30707
|
+
continue;
|
|
30708
|
+
}
|
|
30709
|
+
const fx = ax - fromX;
|
|
30710
|
+
const fy = ay - fromY;
|
|
30711
|
+
const t = _cross(fx, fy, ex, ey) / denom;
|
|
30712
|
+
const s = _cross(fx, fy, dx, dy) / denom;
|
|
30713
|
+
if (t >= 0 && t <= 1 && s >= 0 && s <= 1 && t < bestT) {
|
|
30714
|
+
bestT = t;
|
|
30715
|
+
bestNormalIdx = i;
|
|
30716
|
+
}
|
|
30717
|
+
}
|
|
30718
|
+
if (bestNormalIdx === -1) {
|
|
30719
|
+
return null;
|
|
30720
|
+
}
|
|
30721
|
+
const normal = normals[bestNormalIdx];
|
|
30722
|
+
return { t: bestT, normalX: normal.x, normalY: normal.y };
|
|
30723
|
+
}
|
|
30724
|
+
function _raycastEllipse(fromX, fromY, dx, dy, shape, shapeAbsX, shapeAbsY) {
|
|
30725
|
+
const cx = shapeAbsX;
|
|
30726
|
+
const cy = shapeAbsY;
|
|
30727
|
+
const rx = shape.radiusV.x;
|
|
30728
|
+
const ry = shape.radiusV.y;
|
|
30729
|
+
if (rx <= 0 || ry <= 0) {
|
|
30730
|
+
return null;
|
|
30731
|
+
}
|
|
30732
|
+
const ox = (fromX - cx) / rx;
|
|
30733
|
+
const oy = (fromY - cy) / ry;
|
|
30734
|
+
const ddx = dx / rx;
|
|
30735
|
+
const ddy = dy / ry;
|
|
30736
|
+
const A = ddx * ddx + ddy * ddy;
|
|
30737
|
+
const B = 2 * (ddx * ox + ddy * oy);
|
|
30738
|
+
const C = ox * ox + oy * oy - 1;
|
|
30739
|
+
const disc = B * B - 4 * A * C;
|
|
30740
|
+
if (disc < 0 || A < 1e-12) {
|
|
30741
|
+
return null;
|
|
30742
|
+
}
|
|
30743
|
+
const sqrtDisc = Math.sqrt(disc);
|
|
30744
|
+
const t0 = (-B - sqrtDisc) / (2 * A);
|
|
30745
|
+
const t1 = (-B + sqrtDisc) / (2 * A);
|
|
30746
|
+
let t;
|
|
30747
|
+
if (t0 >= 0 && t0 <= 1) {
|
|
30748
|
+
t = t0;
|
|
30749
|
+
} else if (t1 >= 0 && t1 <= 1) {
|
|
30750
|
+
t = t1;
|
|
30751
|
+
} else {
|
|
30752
|
+
return null;
|
|
30753
|
+
}
|
|
30754
|
+
const hitX = fromX + dx * t;
|
|
30755
|
+
const hitY = fromY + dy * t;
|
|
30756
|
+
let nx = (hitX - cx) / (rx * rx);
|
|
30757
|
+
let ny = (hitY - cy) / (ry * ry);
|
|
30758
|
+
const nLen = Math.hypot(nx, ny);
|
|
30759
|
+
if (nLen > 0) {
|
|
30760
|
+
nx /= nLen;
|
|
30761
|
+
ny /= nLen;
|
|
30762
|
+
}
|
|
30763
|
+
return { t, normalX: nx, normalY: ny };
|
|
30764
|
+
}
|
|
30765
|
+
function raycastQuery(world, fromX, fromY, toX, toY) {
|
|
30766
|
+
const dx = toX - fromX;
|
|
30767
|
+
const dy = toY - fromY;
|
|
30768
|
+
const segLen = Math.hypot(dx, dy);
|
|
30769
|
+
if (segLen < 1e-9) {
|
|
30770
|
+
return [];
|
|
30771
|
+
}
|
|
30772
|
+
const line = linePool.get(fromX, fromY, [
|
|
30773
|
+
new Vector2d(0, 0),
|
|
30774
|
+
new Vector2d(dx, dy)
|
|
30775
|
+
]);
|
|
30776
|
+
const candidates = [];
|
|
30777
|
+
world.broadphase.retrieve(line, void 0, candidates);
|
|
30778
|
+
const hits = [];
|
|
30779
|
+
for (let i = candidates.length - 1; i >= 0; i--) {
|
|
30780
|
+
const objB = candidates[i];
|
|
30781
|
+
if (!objB.body) {
|
|
30782
|
+
continue;
|
|
30783
|
+
}
|
|
30784
|
+
if (!line.getBounds().overlaps(objB.getBounds())) {
|
|
30785
|
+
continue;
|
|
30786
|
+
}
|
|
30787
|
+
const bodyB = objB.body;
|
|
30788
|
+
const shapeCount = bodyB.shapes.length;
|
|
30789
|
+
if (shapeCount === 0) {
|
|
30790
|
+
continue;
|
|
30791
|
+
}
|
|
30792
|
+
let bestT = Infinity;
|
|
30793
|
+
let bestNormalX = 0;
|
|
30794
|
+
let bestNormalY = 0;
|
|
30795
|
+
for (let j = 0; j < shapeCount; j++) {
|
|
30796
|
+
const shapeB = bodyB.getShape(j);
|
|
30797
|
+
_computeShapeAbsPos(objB, shapeB, _shapeAbs);
|
|
30798
|
+
const hit = shapeB.type === "Ellipse" ? _raycastEllipse(
|
|
30799
|
+
fromX,
|
|
30800
|
+
fromY,
|
|
30801
|
+
dx,
|
|
30802
|
+
dy,
|
|
30803
|
+
shapeB,
|
|
30804
|
+
_shapeAbs.x,
|
|
30805
|
+
_shapeAbs.y
|
|
30806
|
+
) : _raycastPolygon(
|
|
30807
|
+
fromX,
|
|
30808
|
+
fromY,
|
|
30809
|
+
dx,
|
|
30810
|
+
dy,
|
|
30811
|
+
shapeB,
|
|
30812
|
+
_shapeAbs.x,
|
|
30813
|
+
_shapeAbs.y
|
|
30814
|
+
);
|
|
30815
|
+
if (hit !== null && hit.t < bestT) {
|
|
30816
|
+
bestT = hit.t;
|
|
30817
|
+
bestNormalX = hit.normalX;
|
|
30818
|
+
bestNormalY = hit.normalY;
|
|
30819
|
+
}
|
|
30820
|
+
}
|
|
30821
|
+
if (bestT === Infinity) {
|
|
30822
|
+
continue;
|
|
30823
|
+
}
|
|
30824
|
+
const flipSign = bestNormalX * dx + bestNormalY * dy > 0 ? -1 : 1;
|
|
30825
|
+
hits.push({
|
|
30826
|
+
renderable: objB,
|
|
30827
|
+
point: new Vector2d(fromX + dx * bestT, fromY + dy * bestT),
|
|
30828
|
+
normal: new Vector2d(bestNormalX * flipSign, bestNormalY * flipSign),
|
|
30829
|
+
fraction: bestT
|
|
30830
|
+
});
|
|
30831
|
+
}
|
|
30832
|
+
linePool.release(line);
|
|
30833
|
+
hits.sort((a, b) => {
|
|
30834
|
+
return a.fraction - b.fraction;
|
|
30835
|
+
});
|
|
30836
|
+
return hits;
|
|
30837
|
+
}
|
|
30838
|
+
|
|
30839
|
+
// src/physics/builtin/sat.js
|
|
29531
30840
|
var LEFT_VORNOI_REGION = -1;
|
|
29532
30841
|
var MIDDLE_VORNOI_REGION = 0;
|
|
29533
30842
|
var RIGHT_VORNOI_REGION = 1;
|
|
@@ -29805,7 +31114,7 @@ function testEllipsePolygon(a, ellipseA, b, polyB, response) {
|
|
|
29805
31114
|
return result;
|
|
29806
31115
|
}
|
|
29807
31116
|
|
|
29808
|
-
// src/physics/detector.js
|
|
31117
|
+
// src/physics/builtin/detector.js
|
|
29809
31118
|
var SAT_LOOKUP = {
|
|
29810
31119
|
PolygonPolygon: testPolygonPolygon,
|
|
29811
31120
|
PolygonEllipse: testPolygonEllipse,
|
|
@@ -29824,15 +31133,6 @@ var SAT_LOOKUP = {
|
|
|
29824
31133
|
RectangleRoundRect: testPolygonPolygon,
|
|
29825
31134
|
RoundRectRectangle: testPolygonPolygon
|
|
29826
31135
|
};
|
|
29827
|
-
var dummyObj = {
|
|
29828
|
-
pos: new Vector2d(0, 0),
|
|
29829
|
-
ancestor: {
|
|
29830
|
-
_absPos: new Vector2d(0, 0),
|
|
29831
|
-
getAbsolutePosition: function() {
|
|
29832
|
-
return this._absPos;
|
|
29833
|
-
}
|
|
29834
|
-
}
|
|
29835
|
-
};
|
|
29836
31136
|
var boundsA = new Bounds();
|
|
29837
31137
|
var boundsB = new Bounds();
|
|
29838
31138
|
var Detector = class {
|
|
@@ -29842,6 +31142,116 @@ var Detector = class {
|
|
|
29842
31142
|
constructor(world) {
|
|
29843
31143
|
this.world = world;
|
|
29844
31144
|
this.response = new response_default();
|
|
31145
|
+
this._activePairs = /* @__PURE__ */ new Map();
|
|
31146
|
+
this._frameSeen = /* @__PURE__ */ new Map();
|
|
31147
|
+
this._symViews = [
|
|
31148
|
+
{
|
|
31149
|
+
a: null,
|
|
31150
|
+
b: null,
|
|
31151
|
+
overlap: 0,
|
|
31152
|
+
overlapN: { x: 0, y: 0 },
|
|
31153
|
+
overlapV: { x: 0, y: 0 },
|
|
31154
|
+
normal: { x: 0, y: 0 },
|
|
31155
|
+
depth: 0
|
|
31156
|
+
},
|
|
31157
|
+
{
|
|
31158
|
+
a: null,
|
|
31159
|
+
b: null,
|
|
31160
|
+
overlap: 0,
|
|
31161
|
+
overlapN: { x: 0, y: 0 },
|
|
31162
|
+
overlapV: { x: 0, y: 0 },
|
|
31163
|
+
normal: { x: 0, y: 0 },
|
|
31164
|
+
depth: 0
|
|
31165
|
+
}
|
|
31166
|
+
];
|
|
31167
|
+
}
|
|
31168
|
+
/**
|
|
31169
|
+
* Populate one of the pooled symmetric views from a SAT response.
|
|
31170
|
+
* `flip=false` builds the view for `response.a`'s side (legacy
|
|
31171
|
+
* fields kept as-is, `normal = -overlapN` = MTV of original a).
|
|
31172
|
+
* `flip=true` builds it for `response.b`'s side: `a` / `b` swap,
|
|
31173
|
+
* `overlapN` / `overlapV` negated, `normal = +overlapN` (the MTV
|
|
31174
|
+
* of original b).
|
|
31175
|
+
* @ignore
|
|
31176
|
+
*/
|
|
31177
|
+
_fillSymView(slot, satResponse, flip) {
|
|
31178
|
+
const view = this._symViews[slot];
|
|
31179
|
+
const oN = satResponse.overlapN;
|
|
31180
|
+
const oV = satResponse.overlapV;
|
|
31181
|
+
if (flip) {
|
|
31182
|
+
view.a = satResponse.b;
|
|
31183
|
+
view.b = satResponse.a;
|
|
31184
|
+
view.overlapN.x = -oN.x;
|
|
31185
|
+
view.overlapN.y = -oN.y;
|
|
31186
|
+
view.overlapV.x = -oV.x;
|
|
31187
|
+
view.overlapV.y = -oV.y;
|
|
31188
|
+
view.normal.x = oN.x;
|
|
31189
|
+
view.normal.y = oN.y;
|
|
31190
|
+
} else {
|
|
31191
|
+
view.a = satResponse.a;
|
|
31192
|
+
view.b = satResponse.b;
|
|
31193
|
+
view.overlapN.x = oN.x;
|
|
31194
|
+
view.overlapN.y = oN.y;
|
|
31195
|
+
view.overlapV.x = oV.x;
|
|
31196
|
+
view.overlapV.y = oV.y;
|
|
31197
|
+
view.normal.x = -oN.x;
|
|
31198
|
+
view.normal.y = -oN.y;
|
|
31199
|
+
}
|
|
31200
|
+
view.overlap = satResponse.overlap;
|
|
31201
|
+
view.depth = satResponse.overlap;
|
|
31202
|
+
return view;
|
|
31203
|
+
}
|
|
31204
|
+
/**
|
|
31205
|
+
* Called by the adapter at the start of a physics step. Resets the
|
|
31206
|
+
* "seen this frame" set so the end-of-step diff can fire
|
|
31207
|
+
* `onCollisionEnd` for pairs that no longer overlap.
|
|
31208
|
+
* @ignore
|
|
31209
|
+
*/
|
|
31210
|
+
beginFrame() {
|
|
31211
|
+
this._frameSeen.clear();
|
|
31212
|
+
}
|
|
31213
|
+
/**
|
|
31214
|
+
* Called by the adapter at the end of a physics step. Diffs the
|
|
31215
|
+
* "seen this frame" set against the previous-frame active pairs:
|
|
31216
|
+
* - pairs in active but not seen → fire onCollisionEnd
|
|
31217
|
+
* - swap active ← seen for the next step's diff
|
|
31218
|
+
* @ignore
|
|
31219
|
+
*/
|
|
31220
|
+
endFrame() {
|
|
31221
|
+
for (const [key, pair] of this._activePairs) {
|
|
31222
|
+
if (this._frameSeen.has(key)) {
|
|
31223
|
+
continue;
|
|
31224
|
+
}
|
|
31225
|
+
const [a, b] = pair;
|
|
31226
|
+
const aAttached = a.ancestor != null;
|
|
31227
|
+
const bAttached = b.ancestor != null;
|
|
31228
|
+
if (!aAttached && !bAttached) {
|
|
31229
|
+
continue;
|
|
31230
|
+
}
|
|
31231
|
+
if (aAttached && typeof a.onCollisionEnd === "function") {
|
|
31232
|
+
a.onCollisionEnd(void 0, b);
|
|
31233
|
+
}
|
|
31234
|
+
if (bAttached && typeof b.onCollisionEnd === "function") {
|
|
31235
|
+
b.onCollisionEnd(void 0, a);
|
|
31236
|
+
}
|
|
31237
|
+
}
|
|
31238
|
+
const prev = this._activePairs;
|
|
31239
|
+
this._activePairs = this._frameSeen;
|
|
31240
|
+
this._frameSeen = prev;
|
|
31241
|
+
}
|
|
31242
|
+
/**
|
|
31243
|
+
* Build a stable order-independent key for a pair of renderables,
|
|
31244
|
+
* using their GUID. Returns undefined if either lacks a GUID (defensive
|
|
31245
|
+
* — detached or pool-recycled objects mid-step).
|
|
31246
|
+
* @ignore
|
|
31247
|
+
*/
|
|
31248
|
+
_pairKey(a, b) {
|
|
31249
|
+
const ga = a.GUID;
|
|
31250
|
+
const gb = b.GUID;
|
|
31251
|
+
if (ga === void 0 || gb === void 0) {
|
|
31252
|
+
return void 0;
|
|
31253
|
+
}
|
|
31254
|
+
return ga < gb ? `${ga}|${gb}` : `${gb}|${ga}`;
|
|
29845
31255
|
}
|
|
29846
31256
|
/**
|
|
29847
31257
|
* determine if two objects should collide (based on both respective objects body collision mask and type).<br>
|
|
@@ -29902,10 +31312,37 @@ var Detector = class {
|
|
|
29902
31312
|
if (boundsA.overlaps(boundsB)) {
|
|
29903
31313
|
if (this.collides(objA.body, objB.body)) {
|
|
29904
31314
|
collisionCounter++;
|
|
29905
|
-
|
|
31315
|
+
const pairKey = this._pairKey(objA, objB);
|
|
31316
|
+
const firstVisitThisFrame = pairKey !== void 0 && !this._frameSeen.has(pairKey);
|
|
31317
|
+
if (firstVisitThisFrame) {
|
|
31318
|
+
this._frameSeen.set(pairKey, [objA, objB]);
|
|
31319
|
+
const isEntry = !this._activePairs.has(pairKey);
|
|
31320
|
+
const viewA = this._fillSymView(0, this.response, false);
|
|
31321
|
+
const viewB = this._fillSymView(1, this.response, true);
|
|
31322
|
+
if (isEntry) {
|
|
31323
|
+
if (typeof objA.onCollisionStart === "function") {
|
|
31324
|
+
objA.onCollisionStart(viewA, objB);
|
|
31325
|
+
}
|
|
31326
|
+
if (typeof objB.onCollisionStart === "function") {
|
|
31327
|
+
objB.onCollisionStart(viewB, objA);
|
|
31328
|
+
}
|
|
31329
|
+
}
|
|
31330
|
+
if (typeof objA.onCollisionActive === "function") {
|
|
31331
|
+
objA.onCollisionActive(viewA, objB);
|
|
31332
|
+
}
|
|
31333
|
+
if (typeof objB.onCollisionActive === "function") {
|
|
31334
|
+
objB.onCollisionActive(viewB, objA);
|
|
31335
|
+
}
|
|
31336
|
+
}
|
|
31337
|
+
const eitherSensor = objA.body.isSensor === true || objB.body.isSensor === true;
|
|
31338
|
+
const aHasModern = typeof objA.onCollisionActive === "function";
|
|
31339
|
+
const bHasModern = typeof objB.onCollisionActive === "function";
|
|
31340
|
+
const aOptsOut = !aHasModern && typeof objA.onCollision === "function" && objA.onCollision(this.response, objB) === false;
|
|
31341
|
+
if (!aOptsOut && objA.body.isStatic === false && !eitherSensor) {
|
|
29906
31342
|
objA.body.respondToCollision.call(objA.body, this.response);
|
|
29907
31343
|
}
|
|
29908
|
-
|
|
31344
|
+
const bOptsOut = !bHasModern && typeof objB.onCollision === "function" && objB.onCollision(this.response, objA) === false;
|
|
31345
|
+
if (!bOptsOut && objB.body.isStatic === false && !eitherSensor) {
|
|
29909
31346
|
objB.body.respondToCollision.call(objB.body, this.response);
|
|
29910
31347
|
}
|
|
29911
31348
|
if (objA.body.shapes.length > 1 || objB.body.shapes.length > 1) {
|
|
@@ -29976,41 +31413,275 @@ var Detector = class {
|
|
|
29976
31413
|
* }
|
|
29977
31414
|
*/
|
|
29978
31415
|
rayCast(line, result = []) {
|
|
29979
|
-
|
|
29980
|
-
const
|
|
29981
|
-
|
|
29982
|
-
|
|
29983
|
-
|
|
29984
|
-
|
|
29985
|
-
|
|
29986
|
-
|
|
29987
|
-
|
|
29988
|
-
|
|
29989
|
-
|
|
29990
|
-
|
|
29991
|
-
|
|
29992
|
-
|
|
29993
|
-
|
|
29994
|
-
|
|
29995
|
-
|
|
29996
|
-
|
|
29997
|
-
|
|
29998
|
-
|
|
29999
|
-
|
|
30000
|
-
|
|
30001
|
-
|
|
31416
|
+
const fromX = line.pos.x + line.points[0].x;
|
|
31417
|
+
const fromY = line.pos.y + line.points[0].y;
|
|
31418
|
+
const toX = line.pos.x + line.points[1].x;
|
|
31419
|
+
const toY = line.pos.y + line.points[1].y;
|
|
31420
|
+
const hits = raycastQuery(this.world, fromX, fromY, toX, toY);
|
|
31421
|
+
for (let i = 0; i < hits.length; i++) {
|
|
31422
|
+
result[i] = hits[i].renderable;
|
|
31423
|
+
}
|
|
31424
|
+
result.length = hits.length;
|
|
31425
|
+
return result;
|
|
31426
|
+
}
|
|
31427
|
+
};
|
|
31428
|
+
var detector_default = Detector;
|
|
31429
|
+
|
|
31430
|
+
// src/physics/builtin/builtin-adapter.ts
|
|
31431
|
+
var BuiltinAdapter = class {
|
|
31432
|
+
/**
|
|
31433
|
+
* Short adapter identifier exposed as `world.physic`. Lets user code
|
|
31434
|
+
* branch on the active physics implementation without importing the
|
|
31435
|
+
* concrete adapter class.
|
|
31436
|
+
*/
|
|
31437
|
+
physicLabel = "builtin";
|
|
31438
|
+
/**
|
|
31439
|
+
* Advertised capabilities; user code may branch on these.
|
|
31440
|
+
*/
|
|
31441
|
+
capabilities = {
|
|
31442
|
+
constraints: false,
|
|
31443
|
+
continuousCollisionDetection: false,
|
|
31444
|
+
sleepingBodies: false,
|
|
31445
|
+
raycasts: true,
|
|
31446
|
+
velocityLimit: true,
|
|
31447
|
+
isGrounded: true
|
|
31448
|
+
};
|
|
31449
|
+
/**
|
|
31450
|
+
* World gravity. Mutate to change at runtime.
|
|
31451
|
+
* @default <0, 0.98>
|
|
31452
|
+
*/
|
|
31453
|
+
gravity;
|
|
31454
|
+
/**
|
|
31455
|
+
* Active physics bodies in this simulation.
|
|
31456
|
+
*/
|
|
31457
|
+
bodies = /* @__PURE__ */ new Set();
|
|
31458
|
+
/**
|
|
31459
|
+
* Collision detector instance, created in {@link init}.
|
|
31460
|
+
*/
|
|
31461
|
+
detector;
|
|
31462
|
+
/**
|
|
31463
|
+
* Back-reference to the owning world, set in {@link init}.
|
|
31464
|
+
*/
|
|
31465
|
+
world;
|
|
31466
|
+
constructor(options = {}) {
|
|
31467
|
+
this.gravity = options.gravity ?? new Vector2d(0, 0.98);
|
|
31468
|
+
}
|
|
31469
|
+
init(world) {
|
|
31470
|
+
this.world = world;
|
|
31471
|
+
this.detector = new detector_default(world);
|
|
31472
|
+
}
|
|
31473
|
+
destroy() {
|
|
31474
|
+
this.bodies.clear();
|
|
31475
|
+
}
|
|
31476
|
+
step(dt) {
|
|
31477
|
+
const isPaused = state_default.isPaused();
|
|
31478
|
+
this.detector.beginFrame();
|
|
31479
|
+
for (const body of this.bodies) {
|
|
31480
|
+
const ancestor = body.ancestor;
|
|
31481
|
+
if (!body.isStatic && ancestor) {
|
|
31482
|
+
if (!(isPaused && !ancestor.updateWhenPaused) && (ancestor.inViewport || ancestor.alwaysUpdate)) {
|
|
31483
|
+
this.applyGravity(body);
|
|
31484
|
+
if (body.update(dt)) {
|
|
31485
|
+
ancestor.isDirty = true;
|
|
30002
31486
|
}
|
|
30003
|
-
|
|
30004
|
-
}
|
|
31487
|
+
this.detector.collisions(ancestor);
|
|
31488
|
+
}
|
|
31489
|
+
}
|
|
31490
|
+
body.force.set(0, 0);
|
|
31491
|
+
}
|
|
31492
|
+
this.detector.endFrame();
|
|
31493
|
+
}
|
|
31494
|
+
syncFromPhysics() {
|
|
31495
|
+
}
|
|
31496
|
+
addBody(renderable, def) {
|
|
31497
|
+
let body = renderable.body;
|
|
31498
|
+
const isAlreadyAdapterManaged = body instanceof Body && this.bodies.has(body);
|
|
31499
|
+
if (isAlreadyAdapterManaged) {
|
|
31500
|
+
throw new Error(
|
|
31501
|
+
"BuiltinAdapter.addBody: renderable is already adapter-managed. Use adapter.updateShape() / property mutation / adapter.removeBody() first if you need to change the body."
|
|
31502
|
+
);
|
|
31503
|
+
}
|
|
31504
|
+
if (!(body instanceof Body)) {
|
|
31505
|
+
body = new Body(renderable, def.shapes);
|
|
31506
|
+
renderable.body = body;
|
|
31507
|
+
} else if (def.shapes.length > 0) {
|
|
31508
|
+
body.shapes.length = 0;
|
|
31509
|
+
body.getBounds().clear();
|
|
31510
|
+
for (const s of def.shapes) {
|
|
31511
|
+
body.addShape(s);
|
|
30005
31512
|
}
|
|
30006
31513
|
}
|
|
30007
|
-
|
|
31514
|
+
body.setStatic(def.type === "static");
|
|
31515
|
+
if (typeof def.collisionType === "number") {
|
|
31516
|
+
body.collisionType = def.collisionType;
|
|
31517
|
+
}
|
|
31518
|
+
if (typeof def.collisionMask === "number") {
|
|
31519
|
+
body.collisionMask = def.collisionMask;
|
|
31520
|
+
}
|
|
31521
|
+
if (def.frictionAir !== void 0) {
|
|
31522
|
+
if (typeof def.frictionAir === "number") {
|
|
31523
|
+
body.setFriction(def.frictionAir, def.frictionAir);
|
|
31524
|
+
} else {
|
|
31525
|
+
body.setFriction(def.frictionAir.x, def.frictionAir.y);
|
|
31526
|
+
}
|
|
31527
|
+
}
|
|
31528
|
+
if (typeof def.restitution === "number") {
|
|
31529
|
+
body.bounce = def.restitution;
|
|
31530
|
+
}
|
|
31531
|
+
if (typeof def.density === "number") {
|
|
31532
|
+
body.mass = def.density;
|
|
31533
|
+
}
|
|
31534
|
+
if (typeof def.gravityScale === "number") {
|
|
31535
|
+
body.gravityScale = def.gravityScale;
|
|
31536
|
+
}
|
|
31537
|
+
if (def.maxVelocity !== void 0) {
|
|
31538
|
+
body.setMaxVelocity(def.maxVelocity.x, def.maxVelocity.y);
|
|
31539
|
+
}
|
|
31540
|
+
if (def.isSensor === true) {
|
|
31541
|
+
body.isSensor = true;
|
|
31542
|
+
}
|
|
31543
|
+
this.bodies.add(body);
|
|
31544
|
+
return body;
|
|
31545
|
+
}
|
|
31546
|
+
removeBody(renderable) {
|
|
31547
|
+
const body = renderable.body;
|
|
31548
|
+
if (body instanceof Body) {
|
|
31549
|
+
this.bodies.delete(body);
|
|
31550
|
+
}
|
|
31551
|
+
}
|
|
31552
|
+
updateShape(renderable, shapes) {
|
|
31553
|
+
const body = renderable.body;
|
|
31554
|
+
if (!(body instanceof Body)) {
|
|
31555
|
+
return;
|
|
31556
|
+
}
|
|
31557
|
+
body.shapes.length = 0;
|
|
31558
|
+
body.getBounds().clear();
|
|
31559
|
+
for (const s of shapes) {
|
|
31560
|
+
body.addShape(s);
|
|
31561
|
+
}
|
|
31562
|
+
}
|
|
31563
|
+
getVelocity(renderable, out) {
|
|
31564
|
+
const body = renderable.body;
|
|
31565
|
+
const target = out ?? new Vector2d();
|
|
31566
|
+
if (!body || !this.bodies.has(body)) {
|
|
31567
|
+
return target.set(0, 0);
|
|
31568
|
+
}
|
|
31569
|
+
return target.set(body.vel.x, body.vel.y);
|
|
31570
|
+
}
|
|
31571
|
+
setVelocity(renderable, v) {
|
|
31572
|
+
renderable.body.vel.setV(v);
|
|
31573
|
+
}
|
|
31574
|
+
applyForce(renderable, force, point) {
|
|
31575
|
+
const body = renderable.body;
|
|
31576
|
+
if (point) {
|
|
31577
|
+
body.applyForce(force.x, force.y, point.x, point.y);
|
|
31578
|
+
} else {
|
|
31579
|
+
body.applyForce(force.x, force.y);
|
|
31580
|
+
}
|
|
31581
|
+
}
|
|
31582
|
+
applyImpulse(renderable, impulse) {
|
|
31583
|
+
const body = renderable.body;
|
|
31584
|
+
const invMass = body.mass > 0 ? 1 / body.mass : 0;
|
|
31585
|
+
body.vel.x += impulse.x * invMass;
|
|
31586
|
+
body.vel.y += impulse.y * invMass;
|
|
31587
|
+
}
|
|
31588
|
+
setPosition(renderable, p) {
|
|
31589
|
+
renderable.pos.x = p.x;
|
|
31590
|
+
renderable.pos.y = p.y;
|
|
31591
|
+
}
|
|
31592
|
+
setAngle(renderable, angle) {
|
|
31593
|
+
renderable.body.setAngle(angle);
|
|
31594
|
+
}
|
|
31595
|
+
getAngle(renderable) {
|
|
31596
|
+
return renderable.body.angle;
|
|
31597
|
+
}
|
|
31598
|
+
setAngularVelocity(renderable, omega) {
|
|
31599
|
+
renderable.body.setAngularVelocity(omega);
|
|
31600
|
+
}
|
|
31601
|
+
getAngularVelocity(renderable) {
|
|
31602
|
+
return renderable.body.angularVelocity;
|
|
31603
|
+
}
|
|
31604
|
+
applyTorque(renderable, torque) {
|
|
31605
|
+
renderable.body.applyTorque(torque);
|
|
31606
|
+
}
|
|
31607
|
+
setStatic(renderable, isStatic) {
|
|
31608
|
+
renderable.body.setStatic(isStatic);
|
|
31609
|
+
}
|
|
31610
|
+
setGravityScale(renderable, scale2) {
|
|
31611
|
+
renderable.body.gravityScale = scale2;
|
|
31612
|
+
}
|
|
31613
|
+
setFrictionAir(renderable, friction) {
|
|
31614
|
+
const body = renderable.body;
|
|
31615
|
+
if (typeof friction === "number") {
|
|
31616
|
+
body.setFriction(friction, friction);
|
|
31617
|
+
} else {
|
|
31618
|
+
body.setFriction(friction.x, friction.y);
|
|
31619
|
+
}
|
|
31620
|
+
}
|
|
31621
|
+
setMaxVelocity(renderable, limit) {
|
|
31622
|
+
renderable.body.setMaxVelocity(limit.x, limit.y);
|
|
31623
|
+
}
|
|
31624
|
+
getMaxVelocity(renderable) {
|
|
31625
|
+
const v = renderable.body.maxVel;
|
|
31626
|
+
return { x: v.x, y: v.y };
|
|
31627
|
+
}
|
|
31628
|
+
setCollisionType(renderable, type) {
|
|
31629
|
+
renderable.body.collisionType = type;
|
|
31630
|
+
}
|
|
31631
|
+
setCollisionMask(renderable, mask) {
|
|
31632
|
+
renderable.body.setCollisionMask(mask);
|
|
31633
|
+
}
|
|
31634
|
+
setSensor(renderable, isSensor) {
|
|
31635
|
+
renderable.body.isSensor = isSensor;
|
|
31636
|
+
}
|
|
31637
|
+
getBodyAABB(renderable, out) {
|
|
31638
|
+
const body = renderable.body;
|
|
31639
|
+
if (!body || !this.bodies.has(body)) {
|
|
31640
|
+
return void 0;
|
|
31641
|
+
}
|
|
31642
|
+
const b = body.bounds;
|
|
31643
|
+
out.setMinMax(b.min.x, b.min.y, b.max.x, b.max.y);
|
|
31644
|
+
return out;
|
|
31645
|
+
}
|
|
31646
|
+
getBodyShapes(renderable) {
|
|
31647
|
+
const body = renderable.body;
|
|
31648
|
+
if (!body || !this.bodies.has(body)) {
|
|
31649
|
+
return [];
|
|
31650
|
+
}
|
|
31651
|
+
return body.shapes;
|
|
31652
|
+
}
|
|
31653
|
+
isGrounded(renderable) {
|
|
31654
|
+
const body = renderable.body;
|
|
31655
|
+
return !body.falling && !body.jumping;
|
|
31656
|
+
}
|
|
31657
|
+
raycast(from, to) {
|
|
31658
|
+
const hits = raycastQuery(this.world, from.x, from.y, to.x, to.y);
|
|
31659
|
+
return hits[0] ?? null;
|
|
31660
|
+
}
|
|
31661
|
+
queryAABB(rect) {
|
|
31662
|
+
const queryBounds = rect.getBounds();
|
|
31663
|
+
const result = [];
|
|
31664
|
+
this.world.broadphase.retrieve(rect, void 0, result);
|
|
31665
|
+
let writeIdx = 0;
|
|
31666
|
+
for (let i = 0, len = result.length; i < len; i++) {
|
|
31667
|
+
const r = result[i];
|
|
31668
|
+
const b = r.getBounds();
|
|
31669
|
+
if (b.overlaps(queryBounds)) {
|
|
31670
|
+
result[writeIdx++] = r;
|
|
31671
|
+
}
|
|
31672
|
+
}
|
|
31673
|
+
result.length = writeIdx;
|
|
30008
31674
|
return result;
|
|
30009
31675
|
}
|
|
31676
|
+
applyGravity(body) {
|
|
31677
|
+
if (body.gravityScale !== 0 && !body.ignoreGravity) {
|
|
31678
|
+
body.force.x += body.mass * this.gravity.x * body.gravityScale;
|
|
31679
|
+
body.force.y += body.mass * this.gravity.y * body.gravityScale;
|
|
31680
|
+
}
|
|
31681
|
+
}
|
|
30010
31682
|
};
|
|
30011
|
-
var detector_default = Detector;
|
|
30012
31683
|
|
|
30013
|
-
// src/physics/quadtree.js
|
|
31684
|
+
// src/physics/builtin/quadtree.js
|
|
30014
31685
|
var QT_ARRAY = [];
|
|
30015
31686
|
function QT_ARRAY_POP(world, bounds, max_objects = 4, max_levels = 4, level2 = 0) {
|
|
30016
31687
|
if (QT_ARRAY.length > 0) {
|
|
@@ -30045,6 +31716,8 @@ var QuadTree = class {
|
|
|
30045
31716
|
this.level = level2;
|
|
30046
31717
|
this.objects = [];
|
|
30047
31718
|
this.nodes = [];
|
|
31719
|
+
this._subtreeCount = 0;
|
|
31720
|
+
this._retrieveScratch = level2 === 0 ? [] : null;
|
|
30048
31721
|
}
|
|
30049
31722
|
/*
|
|
30050
31723
|
* Split the node into 4 subnodes
|
|
@@ -30176,6 +31849,7 @@ var QuadTree = class {
|
|
|
30176
31849
|
* @param {object} item - object to be added
|
|
30177
31850
|
*/
|
|
30178
31851
|
insert(item) {
|
|
31852
|
+
this._subtreeCount++;
|
|
30179
31853
|
if (this.nodes.length > 0) {
|
|
30180
31854
|
const index = this.getIndex(item);
|
|
30181
31855
|
if (index !== -1) {
|
|
@@ -30201,15 +31875,61 @@ var QuadTree = class {
|
|
|
30201
31875
|
}
|
|
30202
31876
|
}
|
|
30203
31877
|
/**
|
|
30204
|
-
*
|
|
31878
|
+
* Recursively remove the given container and its descendants from
|
|
31879
|
+
* the quadtree. Mirrors `insertContainer` so the broadphase can be
|
|
31880
|
+
* kept in sync when a subtree is detached via
|
|
31881
|
+
* `Container.removeChildNow` between two `world.update()` rebuilds
|
|
31882
|
+
* (pointer events fire async in that window and would otherwise
|
|
31883
|
+
* hit destroyed renderables).
|
|
31884
|
+
* @param {Container} container - group of objects to be removed
|
|
31885
|
+
*/
|
|
31886
|
+
removeContainer(container) {
|
|
31887
|
+
const children = container.getChildren?.();
|
|
31888
|
+
if (!children) {
|
|
31889
|
+
return;
|
|
31890
|
+
}
|
|
31891
|
+
const childrenLength = children.length;
|
|
31892
|
+
for (let i = childrenLength, child; i--, child = children[i]; ) {
|
|
31893
|
+
if (child.isKinematic !== true) {
|
|
31894
|
+
if (typeof child.addChild === "function") {
|
|
31895
|
+
if (child.name !== "rootContainer") {
|
|
31896
|
+
this.remove(child);
|
|
31897
|
+
}
|
|
31898
|
+
this.removeContainer(child);
|
|
31899
|
+
} else if (typeof child.getBounds === "function") {
|
|
31900
|
+
this.remove(child);
|
|
31901
|
+
}
|
|
31902
|
+
}
|
|
31903
|
+
}
|
|
31904
|
+
}
|
|
31905
|
+
/**
|
|
31906
|
+
* Return all objects that could collide with the given object.
|
|
31907
|
+
*
|
|
31908
|
+
* **Re-entrancy contract:** when called with no explicit `result`
|
|
31909
|
+
* argument, this method reuses a single root-level scratch array to
|
|
31910
|
+
* avoid per-frame allocations. The returned reference is therefore
|
|
31911
|
+
* **not safe to retain** past the next `retrieve()` call, AND it is
|
|
31912
|
+
* **not safe to issue another scratch-mode `retrieve()` while iterating
|
|
31913
|
+
* the previous result** — the second call clears the scratch and
|
|
31914
|
+
* refills it, corrupting the outer iteration. In-engine callers
|
|
31915
|
+
* (`pointerevent.ts`, `detector.js`) iterate synchronously and never
|
|
31916
|
+
* recurse into `retrieve()`, so they're fine. User-facing portable
|
|
31917
|
+
* APIs (`adapter.queryAABB`, `adapter.raycast`) pass their own array
|
|
31918
|
+
* via the `result` parameter, which bypasses the scratch entirely
|
|
31919
|
+
* and is safe to call from inside collision handlers.
|
|
31920
|
+
*
|
|
30205
31921
|
* @param {object} item - object to be checked against
|
|
30206
31922
|
* @param {object} [fn] - a sorting function for the returned array
|
|
31923
|
+
* @param {object[]} [result] - optional caller-supplied result array.
|
|
31924
|
+
* Pass an explicit (typically empty) array to sidestep the shared
|
|
31925
|
+
* scratch — required for re-entrancy safety.
|
|
30207
31926
|
* @returns {object[]} array with all detected objects
|
|
30208
31927
|
*/
|
|
30209
31928
|
retrieve(item, fn, result) {
|
|
30210
31929
|
const isRoot = typeof result === "undefined";
|
|
30211
31930
|
if (isRoot) {
|
|
30212
|
-
result =
|
|
31931
|
+
result = this._retrieveScratch;
|
|
31932
|
+
result.length = 0;
|
|
30213
31933
|
}
|
|
30214
31934
|
const objects = this.objects;
|
|
30215
31935
|
for (let i = 0, len = objects.length; i < len; i++) {
|
|
@@ -30245,9 +31965,6 @@ var QuadTree = class {
|
|
|
30245
31965
|
const index = this.getIndex(item);
|
|
30246
31966
|
if (index !== -1) {
|
|
30247
31967
|
found = this.nodes[index].remove(item);
|
|
30248
|
-
if (found && this.nodes[index].isPrunable()) {
|
|
30249
|
-
this.nodes[index].clear();
|
|
30250
|
-
}
|
|
30251
31968
|
}
|
|
30252
31969
|
}
|
|
30253
31970
|
if (found === false) {
|
|
@@ -30257,6 +31974,24 @@ var QuadTree = class {
|
|
|
30257
31974
|
found = true;
|
|
30258
31975
|
}
|
|
30259
31976
|
}
|
|
31977
|
+
if (found === false && this.nodes.length > 0) {
|
|
31978
|
+
for (let i = 0; i < this.nodes.length; i++) {
|
|
31979
|
+
if (this.nodes[i].remove(item)) {
|
|
31980
|
+
found = true;
|
|
31981
|
+
break;
|
|
31982
|
+
}
|
|
31983
|
+
}
|
|
31984
|
+
}
|
|
31985
|
+
if (found) {
|
|
31986
|
+
this._subtreeCount--;
|
|
31987
|
+
if (this.nodes.length > 0 && this.objects.length === 0 && this._subtreeCount === 0) {
|
|
31988
|
+
for (let i = 0; i < this.nodes.length; i++) {
|
|
31989
|
+
this.nodes[i].clear();
|
|
31990
|
+
QT_ARRAY_PUSH(this.nodes[i]);
|
|
31991
|
+
}
|
|
31992
|
+
this.nodes.length = 0;
|
|
31993
|
+
}
|
|
31994
|
+
}
|
|
30260
31995
|
return found;
|
|
30261
31996
|
}
|
|
30262
31997
|
/**
|
|
@@ -30264,20 +31999,14 @@ var QuadTree = class {
|
|
|
30264
31999
|
* @returns {boolean} true if the node is prunable
|
|
30265
32000
|
*/
|
|
30266
32001
|
isPrunable() {
|
|
30267
|
-
return
|
|
32002
|
+
return this._subtreeCount === 0;
|
|
30268
32003
|
}
|
|
30269
32004
|
/**
|
|
30270
32005
|
* return true if the node has any children
|
|
30271
32006
|
* @returns {boolean} true if the node has any children
|
|
30272
32007
|
*/
|
|
30273
32008
|
hasChildren() {
|
|
30274
|
-
|
|
30275
|
-
const subnode = this.nodes[i];
|
|
30276
|
-
if (subnode.nodes.length > 0 || subnode.objects.length > 0) {
|
|
30277
|
-
return true;
|
|
30278
|
-
}
|
|
30279
|
-
}
|
|
30280
|
-
return false;
|
|
32009
|
+
return this._subtreeCount > this.objects.length;
|
|
30281
32010
|
}
|
|
30282
32011
|
/**
|
|
30283
32012
|
* clear the quadtree
|
|
@@ -30290,6 +32019,7 @@ var QuadTree = class {
|
|
|
30290
32019
|
QT_ARRAY_PUSH(this.nodes[i]);
|
|
30291
32020
|
}
|
|
30292
32021
|
this.nodes.length = 0;
|
|
32022
|
+
this._subtreeCount = 0;
|
|
30293
32023
|
if (typeof bounds !== "undefined") {
|
|
30294
32024
|
this.bounds.setMinMax(
|
|
30295
32025
|
bounds.min.x,
|
|
@@ -30302,36 +32032,76 @@ var QuadTree = class {
|
|
|
30302
32032
|
};
|
|
30303
32033
|
|
|
30304
32034
|
// src/physics/world.js
|
|
32035
|
+
var EMPTY_BODIES = Object.freeze(/* @__PURE__ */ new Set());
|
|
30305
32036
|
var World = class extends Container {
|
|
30306
32037
|
/**
|
|
30307
32038
|
* @param {number} [x=0] - position of the container (accessible via the inherited pos.x property)
|
|
30308
32039
|
* @param {number} [y=0] - position of the container (accessible via the inherited pos.y property)
|
|
30309
32040
|
* @param {number} [width=Infinity] - width of the world container
|
|
30310
32041
|
* @param {number} [height=Infinity] - height of the world container
|
|
32042
|
+
* @param {PhysicsAdapter} [adapter] - physics adapter to use; defaults to a new {@link BuiltinAdapter} instance
|
|
30311
32043
|
*/
|
|
30312
|
-
constructor(x = 0, y = 0, width = Infinity, height = Infinity) {
|
|
32044
|
+
constructor(x = 0, y = 0, width = Infinity, height = Infinity, adapter = void 0) {
|
|
30313
32045
|
super(x, y, width, height, true);
|
|
30314
32046
|
this.name = "rootContainer";
|
|
30315
32047
|
this.anchorPoint.set(0, 0);
|
|
30316
32048
|
this.app = void 0;
|
|
30317
32049
|
this.physic = "builtin";
|
|
30318
32050
|
this.fps = 60;
|
|
30319
|
-
this.gravity = new Vector2d(0, 0.98);
|
|
30320
32051
|
this.preRender = false;
|
|
30321
32052
|
this.gpuTilemap = true;
|
|
30322
|
-
this.
|
|
32053
|
+
this.adapter = adapter ?? new BuiltinAdapter();
|
|
32054
|
+
this.adapter.init?.(this);
|
|
30323
32055
|
this.broadphase = new QuadTree(
|
|
30324
32056
|
this,
|
|
30325
32057
|
this.getBounds().clone(),
|
|
30326
32058
|
collision.maxChildren,
|
|
30327
32059
|
collision.maxDepth
|
|
30328
32060
|
);
|
|
30329
|
-
this.detector = new detector_default(this);
|
|
30330
32061
|
on(GAME_RESET, this.reset, this);
|
|
30331
32062
|
on(LEVEL_LOADED, () => {
|
|
30332
32063
|
this.broadphase.clear(this.getBounds());
|
|
30333
32064
|
});
|
|
30334
32065
|
}
|
|
32066
|
+
/**
|
|
32067
|
+
* Active physics bodies in this simulation. Backed by the active
|
|
32068
|
+
* adapter; mutating this set directly is no longer the recommended
|
|
32069
|
+
* pattern — use `world.adapter.addBody(...)` / `removeBody(...)`.
|
|
32070
|
+
*
|
|
32071
|
+
* Adapters that don't expose a native `bodies` Set (e.g. third-party
|
|
32072
|
+
* integrations that own their own body storage) cause this getter
|
|
32073
|
+
* to return a frozen empty Set, so any `world.bodies.add(...)`
|
|
32074
|
+
* attempt throws `TypeError` instead of silently mutating a
|
|
32075
|
+
* throwaway.
|
|
32076
|
+
* @returns {Set<Body>}
|
|
32077
|
+
*/
|
|
32078
|
+
get bodies() {
|
|
32079
|
+
return (
|
|
32080
|
+
/** @type {{ bodies?: Set<Body> }} */
|
|
32081
|
+
this.adapter.bodies ?? EMPTY_BODIES
|
|
32082
|
+
);
|
|
32083
|
+
}
|
|
32084
|
+
/**
|
|
32085
|
+
* world gravity. Mutate to change at runtime.
|
|
32086
|
+
* @returns {Vector2d}
|
|
32087
|
+
*/
|
|
32088
|
+
get gravity() {
|
|
32089
|
+
return this.adapter.gravity;
|
|
32090
|
+
}
|
|
32091
|
+
set gravity(v) {
|
|
32092
|
+
this.adapter.gravity = v;
|
|
32093
|
+
}
|
|
32094
|
+
/**
|
|
32095
|
+
* the collision detector instance used by this world instance.
|
|
32096
|
+
* Available only when the active adapter is {@link BuiltinAdapter}.
|
|
32097
|
+
* @returns {Detector | undefined}
|
|
32098
|
+
*/
|
|
32099
|
+
get detector() {
|
|
32100
|
+
return (
|
|
32101
|
+
/** @type {{ detector?: Detector }} */
|
|
32102
|
+
this.adapter.detector
|
|
32103
|
+
);
|
|
32104
|
+
}
|
|
30335
32105
|
/**
|
|
30336
32106
|
* reset the game world
|
|
30337
32107
|
*/
|
|
@@ -30339,28 +32109,40 @@ var World = class extends Container {
|
|
|
30339
32109
|
this.broadphase.clear();
|
|
30340
32110
|
this.anchorPoint.set(0, 0);
|
|
30341
32111
|
super.reset();
|
|
30342
|
-
const
|
|
30343
|
-
|
|
30344
|
-
|
|
30345
|
-
|
|
30346
|
-
|
|
30347
|
-
|
|
30348
|
-
|
|
30349
|
-
|
|
30350
|
-
|
|
30351
|
-
|
|
32112
|
+
const bodies = (
|
|
32113
|
+
/** @type {{ bodies?: Set<Body> }} */
|
|
32114
|
+
this.adapter.bodies
|
|
32115
|
+
);
|
|
32116
|
+
if (bodies !== void 0) {
|
|
32117
|
+
const persistentBodies = [];
|
|
32118
|
+
bodies.forEach((value) => {
|
|
32119
|
+
if (value.ancestor && value.ancestor.isPersistent) {
|
|
32120
|
+
persistentBodies.push(value);
|
|
32121
|
+
}
|
|
30352
32122
|
});
|
|
32123
|
+
bodies.clear();
|
|
32124
|
+
if (persistentBodies.length > 0) {
|
|
32125
|
+
persistentBodies.forEach((body) => {
|
|
32126
|
+
this.addBody(body);
|
|
32127
|
+
});
|
|
32128
|
+
}
|
|
30353
32129
|
}
|
|
30354
32130
|
}
|
|
30355
32131
|
/**
|
|
30356
|
-
* Add a physic body to the game world
|
|
32132
|
+
* Add a physic body to the game world. Legacy API for code that
|
|
32133
|
+
* constructed `new Body(...)` directly and now wants to register it
|
|
32134
|
+
* with the active physics adapter.
|
|
30357
32135
|
* @see Container.addChild
|
|
30358
32136
|
* @param {Body} body
|
|
30359
32137
|
* @returns {World} this game world
|
|
30360
32138
|
*/
|
|
30361
32139
|
addBody(body) {
|
|
30362
32140
|
if (this.physic === "builtin") {
|
|
30363
|
-
|
|
32141
|
+
const bodies = (
|
|
32142
|
+
/** @type {{ bodies?: Set<Body> }} */
|
|
32143
|
+
this.adapter.bodies
|
|
32144
|
+
);
|
|
32145
|
+
bodies?.add(body);
|
|
30364
32146
|
}
|
|
30365
32147
|
return this;
|
|
30366
32148
|
}
|
|
@@ -30372,21 +32154,22 @@ var World = class extends Container {
|
|
|
30372
32154
|
*/
|
|
30373
32155
|
removeBody(body) {
|
|
30374
32156
|
if (this.physic === "builtin") {
|
|
30375
|
-
|
|
32157
|
+
const bodies = (
|
|
32158
|
+
/** @type {{ bodies?: Set<Body> }} */
|
|
32159
|
+
this.adapter.bodies
|
|
32160
|
+
);
|
|
32161
|
+
bodies?.delete(body);
|
|
30376
32162
|
}
|
|
30377
32163
|
return this;
|
|
30378
32164
|
}
|
|
30379
32165
|
/**
|
|
30380
|
-
* Apply gravity to the given body
|
|
32166
|
+
* Apply gravity to the given body. Backward-compat shim; the actual
|
|
32167
|
+
* simulation runs through the active adapter.
|
|
30381
32168
|
* @private
|
|
30382
32169
|
* @param {Body} body
|
|
30383
32170
|
*/
|
|
30384
32171
|
bodyApplyGravity(body) {
|
|
30385
|
-
|
|
30386
|
-
const gravity = this.gravity;
|
|
30387
|
-
body.force.x += body.mass * gravity.x * body.gravityScale;
|
|
30388
|
-
body.force.y += body.mass * gravity.y * body.gravityScale;
|
|
30389
|
-
}
|
|
32172
|
+
this.adapter.applyGravity?.(body);
|
|
30390
32173
|
}
|
|
30391
32174
|
/**
|
|
30392
32175
|
* update the game world
|
|
@@ -30402,28 +32185,13 @@ var World = class extends Container {
|
|
|
30402
32185
|
return super.update(dt);
|
|
30403
32186
|
}
|
|
30404
32187
|
/**
|
|
30405
|
-
* update the
|
|
32188
|
+
* update the physics simulation by one step (called by the game world update method)
|
|
30406
32189
|
* @param {number} dt - the time passed since the last frame update
|
|
30407
32190
|
*/
|
|
30408
32191
|
step(dt) {
|
|
30409
|
-
if (this.physic
|
|
30410
|
-
|
|
30411
|
-
|
|
30412
|
-
if (!body.isStatic) {
|
|
30413
|
-
const ancestor = body.ancestor;
|
|
30414
|
-
if (!ancestor) {
|
|
30415
|
-
continue;
|
|
30416
|
-
}
|
|
30417
|
-
if (!(isPaused && !ancestor.updateWhenPaused) && (ancestor.inViewport || ancestor.alwaysUpdate)) {
|
|
30418
|
-
this.bodyApplyGravity(body);
|
|
30419
|
-
if (body.update(dt) === true) {
|
|
30420
|
-
ancestor.isDirty = true;
|
|
30421
|
-
}
|
|
30422
|
-
this.detector.collisions(ancestor);
|
|
30423
|
-
body.force.set(0, 0);
|
|
30424
|
-
}
|
|
30425
|
-
}
|
|
30426
|
-
}
|
|
32192
|
+
if (this.physic !== "none") {
|
|
32193
|
+
this.adapter.step(dt);
|
|
32194
|
+
this.adapter.syncFromPhysics();
|
|
30427
32195
|
}
|
|
30428
32196
|
emit(WORLD_STEP, dt);
|
|
30429
32197
|
}
|
|
@@ -31576,16 +33344,14 @@ var MaterialBatcher = class extends Batcher {
|
|
|
31576
33344
|
);
|
|
31577
33345
|
}
|
|
31578
33346
|
} else if (typeof globalThis.OffscreenCanvas !== "undefined" && pixels instanceof globalThis.OffscreenCanvas) {
|
|
31579
|
-
const imageBitmap = pixels.transferToImageBitmap();
|
|
31580
33347
|
gl.texImage2D(
|
|
31581
33348
|
gl.TEXTURE_2D,
|
|
31582
33349
|
0,
|
|
31583
33350
|
gl.RGBA,
|
|
31584
33351
|
gl.RGBA,
|
|
31585
33352
|
gl.UNSIGNED_BYTE,
|
|
31586
|
-
|
|
33353
|
+
pixels
|
|
31587
33354
|
);
|
|
31588
|
-
imageBitmap.close();
|
|
31589
33355
|
} else {
|
|
31590
33356
|
gl.texImage2D(
|
|
31591
33357
|
gl.TEXTURE_2D,
|
|
@@ -34913,6 +36679,16 @@ function onresize(game2) {
|
|
|
34913
36679
|
}
|
|
34914
36680
|
|
|
34915
36681
|
// src/application/application.ts
|
|
36682
|
+
function resolvePhysicSetting(physic) {
|
|
36683
|
+
if (physic === "none") {
|
|
36684
|
+
return { adapter: void 0, physicLabel: "none" };
|
|
36685
|
+
}
|
|
36686
|
+
if (physic === void 0 || physic === "builtin") {
|
|
36687
|
+
return { adapter: void 0, physicLabel: "builtin" };
|
|
36688
|
+
}
|
|
36689
|
+
const adapter = typeof physic === "object" && "adapter" in physic ? physic.adapter : physic;
|
|
36690
|
+
return { adapter, physicLabel: adapter?.physicLabel ?? "builtin" };
|
|
36691
|
+
}
|
|
34916
36692
|
var Application = class {
|
|
34917
36693
|
/**
|
|
34918
36694
|
* the parent HTML element holding the main canvas of this application
|
|
@@ -35135,10 +36911,30 @@ var Application = class {
|
|
|
35135
36911
|
if (this.settings.consoleHeader) {
|
|
35136
36912
|
consoleHeader(this);
|
|
35137
36913
|
}
|
|
35138
|
-
|
|
36914
|
+
const { adapter, physicLabel } = resolvePhysicSetting(this.settings.physic);
|
|
36915
|
+
this.world = new World(
|
|
36916
|
+
0,
|
|
36917
|
+
0,
|
|
36918
|
+
this.settings.width,
|
|
36919
|
+
this.settings.height,
|
|
36920
|
+
adapter
|
|
36921
|
+
);
|
|
35139
36922
|
this.world.app = this;
|
|
35140
|
-
this.world.physic =
|
|
36923
|
+
this.world.physic = physicLabel;
|
|
35141
36924
|
this.world.gpuTilemap = this.settings.gpuTilemap;
|
|
36925
|
+
if (this.settings.consoleHeader) {
|
|
36926
|
+
if (this.world.physic === "none") {
|
|
36927
|
+
console.log("physics: disabled");
|
|
36928
|
+
} else if (this.world.adapter) {
|
|
36929
|
+
const a = this.world.adapter;
|
|
36930
|
+
const label = a.name ?? a.constructor.name;
|
|
36931
|
+
const version2 = a.version ? ` ${a.version}` : "";
|
|
36932
|
+
const url = a.url ? ` | ${a.url}` : "";
|
|
36933
|
+
console.log(`physics: ${label}${version2}${url}`);
|
|
36934
|
+
} else {
|
|
36935
|
+
console.log("physics: enabled (no adapter)");
|
|
36936
|
+
}
|
|
36937
|
+
}
|
|
35142
36938
|
if (this.settings.gpuTilemap && // duck-type rather than `instanceof WebGLRenderer` to avoid a
|
|
35143
36939
|
// runtime import; only the WebGL renderer carries `WebGLVersion`
|
|
35144
36940
|
this.renderer.WebGLVersion !== 2) {
|
|
@@ -36586,7 +38382,6 @@ var UIBaseElement = class extends Container {
|
|
|
36586
38382
|
* @param _event - the event object
|
|
36587
38383
|
* @returns return false if we need to stop propagating the event
|
|
36588
38384
|
*/
|
|
36589
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
36590
38385
|
onClick(_event) {
|
|
36591
38386
|
return true;
|
|
36592
38387
|
}
|
|
@@ -36620,14 +38415,12 @@ var UIBaseElement = class extends Container {
|
|
|
36620
38415
|
* function called when the pointer is moved over the object
|
|
36621
38416
|
* @param _event - the event object
|
|
36622
38417
|
*/
|
|
36623
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
36624
38418
|
onMove(_event) {
|
|
36625
38419
|
}
|
|
36626
38420
|
/**
|
|
36627
38421
|
* function called when the pointer is over the object
|
|
36628
38422
|
* @param _event - the event object
|
|
36629
38423
|
*/
|
|
36630
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
36631
38424
|
onOver(_event) {
|
|
36632
38425
|
}
|
|
36633
38426
|
/**
|
|
@@ -36649,7 +38442,6 @@ var UIBaseElement = class extends Container {
|
|
|
36649
38442
|
* function called when the pointer is leaving the object area
|
|
36650
38443
|
* @param _event - the event object
|
|
36651
38444
|
*/
|
|
36652
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
36653
38445
|
onOut(_event) {
|
|
36654
38446
|
}
|
|
36655
38447
|
/**
|
|
@@ -36670,7 +38462,6 @@ var UIBaseElement = class extends Container {
|
|
|
36670
38462
|
* @param _event - the event object
|
|
36671
38463
|
* @returns return false if we need to stop propagating the event
|
|
36672
38464
|
*/
|
|
36673
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
36674
38465
|
onRelease(_event) {
|
|
36675
38466
|
return true;
|
|
36676
38467
|
}
|
|
@@ -36835,7 +38626,6 @@ var UISpriteElement = class extends Sprite {
|
|
|
36835
38626
|
* @param _event - the event object
|
|
36836
38627
|
* @returns return false if we need to stop propagating the event
|
|
36837
38628
|
*/
|
|
36838
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
36839
38629
|
onClick(_event) {
|
|
36840
38630
|
return false;
|
|
36841
38631
|
}
|
|
@@ -36852,7 +38642,6 @@ var UISpriteElement = class extends Sprite {
|
|
|
36852
38642
|
* function called when the pointer is over the object
|
|
36853
38643
|
* @param _event - the event object
|
|
36854
38644
|
*/
|
|
36855
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
36856
38645
|
onOver(_event) {
|
|
36857
38646
|
}
|
|
36858
38647
|
/**
|
|
@@ -36869,7 +38658,6 @@ var UISpriteElement = class extends Sprite {
|
|
|
36869
38658
|
* function called when the pointer is leaving the object area
|
|
36870
38659
|
* @param _event - the event object
|
|
36871
38660
|
*/
|
|
36872
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
36873
38661
|
onOut(_event) {
|
|
36874
38662
|
}
|
|
36875
38663
|
/**
|
|
@@ -36890,7 +38678,6 @@ var UISpriteElement = class extends Sprite {
|
|
|
36890
38678
|
* @param _event - the event object
|
|
36891
38679
|
* @returns return false if we need to stop propagating the event
|
|
36892
38680
|
*/
|
|
36893
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
36894
38681
|
onRelease(_event) {
|
|
36895
38682
|
return false;
|
|
36896
38683
|
}
|
|
@@ -37714,6 +39501,111 @@ var SepiaEffect = class extends ColorMatrixEffect {
|
|
|
37714
39501
|
}
|
|
37715
39502
|
};
|
|
37716
39503
|
|
|
39504
|
+
// src/video/webgl/effects/shine.js
|
|
39505
|
+
var ShineEffect = class extends ShaderEffect {
|
|
39506
|
+
/**
|
|
39507
|
+
* @param {import("../webgl_renderer.js").default} renderer - the current renderer instance
|
|
39508
|
+
* @param {object} [options] - effect options
|
|
39509
|
+
* @param {number[]} [options.color=[1.0, 1.0, 1.0]] - shine color as [r, g, b] (0.0–1.0)
|
|
39510
|
+
* @param {number} [options.speed=0.5] - sweeps per second
|
|
39511
|
+
* @param {number} [options.width=0.15] - glint half-width as a fraction of one tile (0.0–1.0)
|
|
39512
|
+
* @param {number} [options.intensity=0.5] - maximum highlight strength
|
|
39513
|
+
* @param {number} [options.angle=0.5] - sweep direction in radians (0 = horizontal L→R, π/2 = vertical T→B)
|
|
39514
|
+
* @param {number} [options.bands=1.0] - number of parallel glints (1 = single shine; ~14.5 mimics a coin's etched-rim look)
|
|
39515
|
+
* @param {number} [options.pulseDepth=0.0] - subtle base-brightness pulse amplitude (0 disables the pulse)
|
|
39516
|
+
* @param {number} [options.pulseSpeed=3.0] - pulse oscillation rate (radians/second)
|
|
39517
|
+
*/
|
|
39518
|
+
constructor(renderer2, options = {}) {
|
|
39519
|
+
super(
|
|
39520
|
+
renderer2,
|
|
39521
|
+
`
|
|
39522
|
+
uniform vec3 uShineColor;
|
|
39523
|
+
uniform float uShineWidth;
|
|
39524
|
+
uniform float uShineSpeed;
|
|
39525
|
+
uniform float uShineIntensity;
|
|
39526
|
+
uniform float uShineAngle;
|
|
39527
|
+
uniform float uShineBands;
|
|
39528
|
+
uniform float uPulseDepth;
|
|
39529
|
+
uniform float uPulseSpeed;
|
|
39530
|
+
uniform float uTime;
|
|
39531
|
+
vec4 apply(vec4 color, vec2 uv) {
|
|
39532
|
+
if (color.a == 0.0) return color;
|
|
39533
|
+
// Optional brightness pulse on the base color.
|
|
39534
|
+
float pulse = (1.0 - uPulseDepth) + uPulseDepth * sin(uTime * uPulseSpeed);
|
|
39535
|
+
// Project uv along the sweep axis (uShineAngle).
|
|
39536
|
+
float pos = uv.x * cos(uShineAngle) + uv.y * sin(uShineAngle);
|
|
39537
|
+
// Tile by uShineBands so we get N parallel glints in unison.
|
|
39538
|
+
// localX and sweep both live in [0,1] tile-space; the
|
|
39539
|
+
// wrap-around distance (min(d, 1-d)) lets the glint exit
|
|
39540
|
+
// the right edge of a tile and immediately re-enter from
|
|
39541
|
+
// the left edge of the next, so there is NO perceived
|
|
39542
|
+
// pause between sweeps. Previous implementation padded
|
|
39543
|
+
// the sweep range to [-W, 1+W] which created a visible
|
|
39544
|
+
// off-screen gap of duration ~2W/(1+2W) per cycle.
|
|
39545
|
+
float localX = fract(pos * uShineBands);
|
|
39546
|
+
float sweep = fract(uTime * uShineSpeed);
|
|
39547
|
+
float d = abs(localX - sweep);
|
|
39548
|
+
float dist = min(d, 1.0 - d);
|
|
39549
|
+
float glint = smoothstep(uShineWidth, 0.0, dist) * uShineIntensity;
|
|
39550
|
+
// color.rgb is already premultiplied by alpha \u2014 multiply the
|
|
39551
|
+
// added glint by color.a so it stays premultiplied too, but
|
|
39552
|
+
// do NOT scale the whole result by color.a (that would dim
|
|
39553
|
+
// antialiased edges twice).
|
|
39554
|
+
vec3 result = color.rgb * pulse + uShineColor * glint * color.a;
|
|
39555
|
+
return vec4(result, color.a);
|
|
39556
|
+
}
|
|
39557
|
+
`
|
|
39558
|
+
);
|
|
39559
|
+
this.setUniform(
|
|
39560
|
+
"uShineColor",
|
|
39561
|
+
new Float32Array(options.color ?? [1, 1, 1])
|
|
39562
|
+
);
|
|
39563
|
+
this.setUniform("uShineWidth", options.width ?? 0.15);
|
|
39564
|
+
this.setUniform("uShineSpeed", options.speed ?? 0.5);
|
|
39565
|
+
this.setUniform("uShineIntensity", options.intensity ?? 0.5);
|
|
39566
|
+
this.setUniform("uShineAngle", options.angle ?? 0.5);
|
|
39567
|
+
this.setUniform("uShineBands", options.bands ?? 1);
|
|
39568
|
+
this.setUniform("uPulseDepth", options.pulseDepth ?? 0);
|
|
39569
|
+
this.setUniform("uPulseSpeed", options.pulseSpeed ?? 3);
|
|
39570
|
+
this.setUniform("uTime", 0);
|
|
39571
|
+
}
|
|
39572
|
+
/**
|
|
39573
|
+
* set the current time (call each frame for animation)
|
|
39574
|
+
* @param {number} time - time in seconds
|
|
39575
|
+
*/
|
|
39576
|
+
setTime(time) {
|
|
39577
|
+
this.setUniform("uTime", time);
|
|
39578
|
+
}
|
|
39579
|
+
/**
|
|
39580
|
+
* set the shine color
|
|
39581
|
+
* @param {number[]} color - shine color as [r, g, b] (0.0–1.0)
|
|
39582
|
+
*/
|
|
39583
|
+
setColor(color) {
|
|
39584
|
+
this.setUniform("uShineColor", new Float32Array(color));
|
|
39585
|
+
}
|
|
39586
|
+
/**
|
|
39587
|
+
* set the sweep speed
|
|
39588
|
+
* @param {number} value - sweeps per second
|
|
39589
|
+
*/
|
|
39590
|
+
setSpeed(value) {
|
|
39591
|
+
this.setUniform("uShineSpeed", value);
|
|
39592
|
+
}
|
|
39593
|
+
/**
|
|
39594
|
+
* set the highlight intensity
|
|
39595
|
+
* @param {number} value - maximum highlight strength
|
|
39596
|
+
*/
|
|
39597
|
+
setIntensity(value) {
|
|
39598
|
+
this.setUniform("uShineIntensity", value);
|
|
39599
|
+
}
|
|
39600
|
+
/**
|
|
39601
|
+
* set the number of parallel glints
|
|
39602
|
+
* @param {number} value - 1 for a single shine, >1 for tiled stripes
|
|
39603
|
+
*/
|
|
39604
|
+
setBands(value) {
|
|
39605
|
+
this.setUniform("uShineBands", value);
|
|
39606
|
+
}
|
|
39607
|
+
};
|
|
39608
|
+
|
|
37717
39609
|
// src/video/webgl/effects/tintPulse.js
|
|
37718
39610
|
var TintPulseEffect = class extends ShaderEffect {
|
|
37719
39611
|
/**
|
|
@@ -38385,6 +40277,7 @@ export {
|
|
|
38385
40277
|
BlurEffect,
|
|
38386
40278
|
Body,
|
|
38387
40279
|
Bounds,
|
|
40280
|
+
BuiltinAdapter,
|
|
38388
40281
|
CANVAS,
|
|
38389
40282
|
Camera2d,
|
|
38390
40283
|
CameraEffect,
|
|
@@ -38449,6 +40342,7 @@ export {
|
|
|
38449
40342
|
SepiaEffect,
|
|
38450
40343
|
ShaderEffect,
|
|
38451
40344
|
ShakeEffect,
|
|
40345
|
+
ShineEffect,
|
|
38452
40346
|
Sprite,
|
|
38453
40347
|
Stage,
|
|
38454
40348
|
TMXHexagonalRenderer,
|