littlejsengine 1.24.2 → 1.25.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/CHANGELOG.md +55 -0
- package/EDITOR.md +2 -2
- package/FAQ.md +6 -0
- package/REFERENCE.md +86 -41
- package/dist/littlejs.d.ts +600 -323
- package/dist/littlejs.esm.js +2412 -1797
- package/dist/littlejs.esm.min.js +38 -24
- package/dist/littlejs.js +2410 -1797
- package/dist/littlejs.min.js +37 -23
- package/dist/littlejs.release.js +2393 -1826
- package/package.json +1 -1
- package/plugins/box2d.js +41 -28
- package/plugins/drawUtilities.js +374 -380
- package/plugins/gltf.js +331 -55
- package/plugins/lightSystem.js +2 -2
- package/plugins/math3d.js +1013 -1014
- package/plugins/newgrounds.js +1 -4
- package/plugins/particleEffects.js +3 -4
- package/plugins/pathFinder.js +52 -28
- package/plugins/pluginExport.js +2 -0
- package/plugins/postProcess.js +196 -65
- package/plugins/render3d.js +8 -6
- package/plugins/render3dEditor.js +3 -3
- package/plugins/render3dExtras.js +31 -10
- package/plugins/render3dLevel.js +15 -4
- package/plugins/textureSheet.js +15 -7
- package/plugins/tweenSystem.js +43 -38
- package/plugins/uiSystem.js +25 -20
- package/src/engine.js +39 -21
- package/src/engineAudio.js +13 -9
- package/src/engineBuild.mjs +49 -2
- package/src/engineDebug.js +3 -2
- package/src/engineDraw.js +20 -18
- package/src/engineEditor.js +75 -23
- package/src/engineInput.js +10 -10
- package/src/engineMath.js +3 -6
- package/src/engineObject.js +8 -9
- package/src/engineParticles.js +8 -7
- package/src/engineRelease.js +17 -10
- package/src/engineTileLayer.js +33 -14
- package/src/engineUtilities.js +15 -2
- package/src/engineWebGL.js +34 -12
package/plugins/newgrounds.js
CHANGED
|
@@ -25,7 +25,7 @@ let newgrounds;
|
|
|
25
25
|
const newgroundsUnlocksToResend = new Set; // pending medals whose request did not reach the server
|
|
26
26
|
const newgroundsUnlocksRefused = new Set; // medals the server refused this visit, asked again they answer no unsent
|
|
27
27
|
const newgroundsSecureComponents = ['Medal.unlock', 'ScoreBoard.postScore']; // the calls encrypted with a cipher
|
|
28
|
-
const newgroundsSessionErrors = [104, 110, 111]; // expired session, login required, session
|
|
28
|
+
const newgroundsSessionErrors = [104, 110, 111]; // expired session, login required, session canceled
|
|
29
29
|
const newgroundsTimeoutMS = 15e3; // how long a request may take before it fails
|
|
30
30
|
|
|
31
31
|
// whether the server answered that the session is gone, as opposed to a request that failed on the way
|
|
@@ -181,9 +181,6 @@ class NewgroundsPlugin
|
|
|
181
181
|
this.ready = this.init();
|
|
182
182
|
}
|
|
183
183
|
|
|
184
|
-
/** @deprecated since 1.20, the view is logged when the plugin starts, so this does nothing */
|
|
185
|
-
logView() {}
|
|
186
|
-
|
|
187
184
|
/** Log the view, check the session, fetch the medals and scoreboards, then keep the session alive; the constructor runs it once
|
|
188
185
|
* @private */
|
|
189
186
|
async init()
|
|
@@ -162,7 +162,6 @@ function particleEffectSanitize(raw)
|
|
|
162
162
|
const text = typeof raw?.name === 'string' ? raw.name.replace(/[\x00-\x1f\x7f]+/g, ' ').trim() : '';
|
|
163
163
|
const name = text ? text.slice(0, 60).trim() : 'Effect'; // no space left where it was cut
|
|
164
164
|
const input = raw?.settings && typeof raw.settings === 'object' ? {...raw.settings} : {};
|
|
165
|
-
input.collideLevel ??= input.collideTiles; // its name before 1.20
|
|
166
165
|
// a library saved before shapes names its tile and no shape, it keeps its tile
|
|
167
166
|
if (input.shape === undefined && isNumber(input.tileIndex))
|
|
168
167
|
input.shape = '';
|
|
@@ -212,10 +211,10 @@ function particleEffectSanitize(raw)
|
|
|
212
211
|
}
|
|
213
212
|
|
|
214
213
|
/** A copy of an effect with its four colors turned around the color wheel and their saturation scaled; lightness and
|
|
215
|
-
* alpha stay, and
|
|
214
|
+
* alpha stay, and gray and white have no hue to turn
|
|
216
215
|
* @param {Object} effect
|
|
217
216
|
* @param {number} [hue] - How far around the wheel, 1 is all the way
|
|
218
|
-
* @param {number} [saturation] - Multiplies the saturation, 0 is
|
|
217
|
+
* @param {number} [saturation] - Multiplies the saturation, 0 is gray, clamped to 1
|
|
219
218
|
* @return {Object}
|
|
220
219
|
* @memberof ParticleEffects */
|
|
221
220
|
function particleEffectRecolor(effect, hue=0, saturation=1)
|
|
@@ -505,7 +504,7 @@ function particleEffectResolve(nameOrEffect, options)
|
|
|
505
504
|
* @param {Object} [options] - What to change for this play, each left out when not wanted:
|
|
506
505
|
* scale grows the whole effect, the built-ins fit a one unit object at 1;
|
|
507
506
|
* hue turns its colors around the color wheel, 1 is all the way;
|
|
508
|
-
* saturation multiplies its saturation, 0 is
|
|
507
|
+
* saturation multiplies its saturation, 0 is gray;
|
|
509
508
|
* angle is its direction, 0 is up, the effect's own angle when not given;
|
|
510
509
|
* tileInfo, a TileInfo or a TextureInfo, is the game's own art to draw with in place of the effect's shape,
|
|
511
510
|
* tinted by its colors, a whole texture drawn as one tile;
|
package/plugins/pathFinder.js
CHANGED
|
@@ -26,7 +26,8 @@ const PATHFINDER_TILE_VEC = vec2(1);
|
|
|
26
26
|
* @memberof PathFinding */
|
|
27
27
|
class PathFinderNode
|
|
28
28
|
{
|
|
29
|
-
/**
|
|
29
|
+
/** Make the node of a grid cell, as PathFinder does for each
|
|
30
|
+
* @param {number} x - Tile x
|
|
30
31
|
* @param {number} y - Tile y */
|
|
31
32
|
constructor(x, y)
|
|
32
33
|
{
|
|
@@ -66,7 +67,8 @@ class PathFinderNode
|
|
|
66
67
|
/** Reset per-search state and walkability (called by buildNodeData). */
|
|
67
68
|
reset() { this.walkable = false; this.cost = 0; this.resetSearch(); }
|
|
68
69
|
|
|
69
|
-
/** True if walkable and not blocked by cost.
|
|
70
|
+
/** True if walkable and not blocked by cost.
|
|
71
|
+
* @return {boolean} */
|
|
70
72
|
isClear()
|
|
71
73
|
{
|
|
72
74
|
return this.walkable && this.cost === 0;
|
|
@@ -88,7 +90,8 @@ class PathFinderNode
|
|
|
88
90
|
*/
|
|
89
91
|
class PathFinder
|
|
90
92
|
{
|
|
91
|
-
/**
|
|
93
|
+
/** Make a path finder over a tile layer or a grid
|
|
94
|
+
* @param {TileCollisionLayer|Vector2} source - Either a TileCollisionLayer
|
|
92
95
|
* (size and walkability auto-derived) or a Vector2 grid size (user
|
|
93
96
|
* overrides isWalkable). */
|
|
94
97
|
constructor(source)
|
|
@@ -157,7 +160,7 @@ class PathFinder
|
|
|
157
160
|
* the instance or via a subclass.
|
|
158
161
|
* @param {number} x - Tile x
|
|
159
162
|
* @param {number} y - Tile y
|
|
160
|
-
* @
|
|
163
|
+
* @return {boolean} */
|
|
161
164
|
isWalkable(x, y)
|
|
162
165
|
{
|
|
163
166
|
if (!this.tileLayer) return true;
|
|
@@ -168,7 +171,7 @@ class PathFinder
|
|
|
168
171
|
* Override to add cost-weighted terrain (mud, swamp, etc).
|
|
169
172
|
* @param {number} x - Tile x
|
|
170
173
|
* @param {number} y - Tile y
|
|
171
|
-
* @
|
|
174
|
+
* @return {number} */
|
|
172
175
|
getCost(x, y)
|
|
173
176
|
{
|
|
174
177
|
return 0;
|
|
@@ -177,7 +180,7 @@ class PathFinder
|
|
|
177
180
|
/** Get the node at tile coords, or null if out of bounds.
|
|
178
181
|
* @param {number} x
|
|
179
182
|
* @param {number} y
|
|
180
|
-
* @
|
|
183
|
+
* @return {PathFinderNode|null} */
|
|
181
184
|
getNode(x, y)
|
|
182
185
|
{
|
|
183
186
|
if (x < 0 || y < 0 || x >= this.size.x || y >= this.size.y) return null;
|
|
@@ -186,7 +189,7 @@ class PathFinder
|
|
|
186
189
|
|
|
187
190
|
/** Convert a world-space position to integer tile coords (no clamping).
|
|
188
191
|
* @param {Vector2} worldPos
|
|
189
|
-
* @
|
|
192
|
+
* @return {Vector2} */
|
|
190
193
|
worldToTile(worldPos)
|
|
191
194
|
{
|
|
192
195
|
const ox = this.tileLayer ? this.tileLayer.pos.x : 0;
|
|
@@ -197,7 +200,7 @@ class PathFinder
|
|
|
197
200
|
/** Convert integer tile coords to the world-space center of that tile.
|
|
198
201
|
* @param {number} x
|
|
199
202
|
* @param {number} y
|
|
200
|
-
* @
|
|
203
|
+
* @return {Vector2} */
|
|
201
204
|
tileToWorld(x, y)
|
|
202
205
|
{
|
|
203
206
|
const ox = this.tileLayer ? this.tileLayer.pos.x : 0;
|
|
@@ -241,7 +244,7 @@ class PathFinder
|
|
|
241
244
|
* reached; false on disconnected goal or maxLoop exhaustion, which sets searchGaveUp.
|
|
242
245
|
* @param {PathFinderNode} startNode
|
|
243
246
|
* @param {PathFinderNode} endNode
|
|
244
|
-
* @
|
|
247
|
+
* @return {boolean}
|
|
245
248
|
* @private */
|
|
246
249
|
aStarSearch(startNode, endNode)
|
|
247
250
|
{
|
|
@@ -385,9 +388,9 @@ class PathFinder
|
|
|
385
388
|
* unchanged walkability, pass `rebuild=false` and call `buildNodeData()`
|
|
386
389
|
* once externally to avoid redundant work.
|
|
387
390
|
* @param {Vector2} worldPos
|
|
388
|
-
* @param {number} [searchRange
|
|
389
|
-
* @param {boolean} [rebuild
|
|
390
|
-
* @
|
|
391
|
+
* @param {number} [searchRange] - Max box-radius in tiles
|
|
392
|
+
* @param {boolean} [rebuild] - Whether to call buildNodeData first
|
|
393
|
+
* @return {PathFinderNode|null} */
|
|
391
394
|
getNearestClearNode(worldPos, searchRange = 10, rebuild = true)
|
|
392
395
|
{
|
|
393
396
|
ASSERT(isVector2(worldPos), 'worldPos must be a Vector2');
|
|
@@ -550,20 +553,36 @@ class PathFinder
|
|
|
550
553
|
{
|
|
551
554
|
if (path.length <= 2) return;
|
|
552
555
|
|
|
553
|
-
// Greedy: from each kept node, jump to the furthest
|
|
554
|
-
// line to it,
|
|
556
|
+
// Greedy: from each kept node, jump to the furthest corner with a clear
|
|
557
|
+
// line to it, then on along the straight run past it as far as one is
|
|
558
|
+
// clear, or else the next node. Every segment is one isLineClear
|
|
555
559
|
// accepted or one the path already had, so none can cross a wall.
|
|
556
|
-
|
|
560
|
+
// Corners first, so a winding path costs a check per corner and not
|
|
561
|
+
// per cell of it.
|
|
562
|
+
const original = path.slice(), last = original.length - 1;
|
|
563
|
+
const corners = [];
|
|
564
|
+
for (let i = 1; i < last; ++i)
|
|
565
|
+
{
|
|
566
|
+
const a = original[i - 1].pos, b = original[i].pos, c = original[i + 1].pos;
|
|
567
|
+
if ((b.x - a.x) * (c.y - a.y) !== (b.y - a.y) * (c.x - a.x))
|
|
568
|
+
corners.push(i);
|
|
569
|
+
}
|
|
570
|
+
corners.push(last);
|
|
557
571
|
path.length = 0;
|
|
558
572
|
path.push(original[0]);
|
|
559
|
-
for (let k = 0; k <
|
|
573
|
+
for (let k = 0; k < last;)
|
|
560
574
|
{
|
|
561
|
-
let j =
|
|
575
|
+
let j = k + 1;
|
|
562
576
|
if (original[k].isClear()) // isLineClear needs both ends clear
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
577
|
+
{
|
|
578
|
+
const clear = (i)=> original[i].isClear() && this.isLineClear(original[k].pos, original[i].pos);
|
|
579
|
+
let c = corners.length - 1, next = last + 1; // next is the corner past the one found
|
|
580
|
+
while (c >= 0 && corners[c] > k + 1 && !clear(corners[c]))
|
|
581
|
+
next = corners[c--];
|
|
582
|
+
j = c >= 0 && corners[c] > k + 1 ? corners[c] : k + 1;
|
|
583
|
+
for (let i = next - 1; i > j; --i)
|
|
584
|
+
if (clear(i)) { j = i; break; }
|
|
585
|
+
}
|
|
567
586
|
path.push(original[j]);
|
|
568
587
|
k = j;
|
|
569
588
|
}
|
|
@@ -578,20 +597,25 @@ class PathFinder
|
|
|
578
597
|
* @private */
|
|
579
598
|
dropCollinearNodes(path)
|
|
580
599
|
{
|
|
581
|
-
|
|
600
|
+
// one pass, a node kept only where the way turns from the last one kept
|
|
601
|
+
if (path.length < 3) return;
|
|
602
|
+
let kept = 1;
|
|
603
|
+
for (let i = 1; i < path.length - 1; ++i)
|
|
582
604
|
{
|
|
583
|
-
const a = path[
|
|
584
|
-
if ((b.pos.x - a.pos.x) * (c.pos.y - a.pos.y)
|
|
605
|
+
const a = path[kept - 1], b = path[i], c = path[i + 1];
|
|
606
|
+
if ((b.pos.x - a.pos.x) * (c.pos.y - a.pos.y) !==
|
|
585
607
|
(b.pos.y - a.pos.y) * (c.pos.x - a.pos.x))
|
|
586
|
-
path
|
|
608
|
+
path[kept++] = b;
|
|
587
609
|
}
|
|
610
|
+
path[kept++] = path[path.length - 1];
|
|
611
|
+
path.length = kept;
|
|
588
612
|
}
|
|
589
613
|
|
|
590
614
|
/** Lookup helper: true when the node at tile coords (x, y) is in-bounds
|
|
591
615
|
* and clear (walkable, zero-cost). Used by isLineClear's hot path.
|
|
592
616
|
* @param {number} x
|
|
593
617
|
* @param {number} y
|
|
594
|
-
* @
|
|
618
|
+
* @return {boolean}
|
|
595
619
|
* @private */
|
|
596
620
|
isNodeClear(x, y)
|
|
597
621
|
{
|
|
@@ -608,7 +632,7 @@ class PathFinder
|
|
|
608
632
|
* CheckLine() in pathFinding.cpp.
|
|
609
633
|
* @param {Vector2} startPos - Tile coords
|
|
610
634
|
* @param {Vector2} endPos - Tile coords
|
|
611
|
-
* @
|
|
635
|
+
* @return {boolean}
|
|
612
636
|
* @private */
|
|
613
637
|
isLineClear(startPos, endPos)
|
|
614
638
|
{
|
|
@@ -731,7 +755,7 @@ class PathFinder
|
|
|
731
755
|
* @param {Vector2} startPos - World-space start
|
|
732
756
|
* @param {Vector2} endPos - World-space end
|
|
733
757
|
* @param {boolean} [rebuild] - Whether to call buildNodeData first
|
|
734
|
-
* @
|
|
758
|
+
* @return {Vector2[]} */
|
|
735
759
|
findPath(startPos, endPos, rebuild = true)
|
|
736
760
|
{
|
|
737
761
|
ASSERT(isVector2(startPos) && isVector2(endPos), 'findPath needs Vector2 endpoints');
|