littlejsengine 1.18.8 → 1.18.12
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 +2 -4
- package/dist/littlejs.d.ts +67 -3
- package/dist/littlejs.esm.js +262 -23
- package/dist/littlejs.esm.min.js +1 -1
- package/dist/littlejs.js +257 -23
- package/dist/littlejs.min.js +1 -1
- package/dist/littlejs.release.js +253 -21
- package/package.json +1 -1
- package/plugins/medalSystem.js +2 -1
- package/plugins/newgrounds.js +7 -0
- package/plugins/pathFinder.js +35 -6
- package/plugins/tweenSystem.js +8 -0
- package/src/engine.js +3 -2
- package/src/engineAudio.js +3 -2
- package/src/engineDebug.js +4 -2
- package/src/engineDraw.js +89 -9
- package/src/engineExport.js +5 -0
- package/src/engineUtilities.js +43 -0
- package/src/engineWebGL.js +63 -1
package/dist/littlejs.esm.js
CHANGED
|
@@ -35,7 +35,7 @@ const engineName = 'LittleJS';
|
|
|
35
35
|
* @type {string}
|
|
36
36
|
* @default
|
|
37
37
|
* @memberof Engine */
|
|
38
|
-
const engineVersion = '1.18.
|
|
38
|
+
const engineVersion = '1.18.12';
|
|
39
39
|
|
|
40
40
|
/** Frames per second to update
|
|
41
41
|
* @type {number}
|
|
@@ -207,7 +207,7 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
|
|
|
207
207
|
const combinedScale = timeScale * debugScale;
|
|
208
208
|
frameTimeDeltaMS *= combinedScale;
|
|
209
209
|
frameTimeBufferMS += paused ? 0 : frameTimeDeltaMS;
|
|
210
|
-
if (
|
|
210
|
+
if (combinedScale <= 1)
|
|
211
211
|
frameTimeBufferMS = min(frameTimeBufferMS, 50); // clamp min framerate
|
|
212
212
|
|
|
213
213
|
let wasUpdated = false;
|
|
@@ -294,6 +294,7 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
|
|
|
294
294
|
glFlush();
|
|
295
295
|
debugRenderPost();
|
|
296
296
|
drawCount = 0;
|
|
297
|
+
primitiveCount = 0;
|
|
297
298
|
}
|
|
298
299
|
}
|
|
299
300
|
|
|
@@ -1068,7 +1069,8 @@ function debugRender()
|
|
|
1068
1069
|
debugContext.fillText('FPS: ' + averageFPS.toFixed(1) + (glEnable?' WebGL':' Canvas2D'),
|
|
1069
1070
|
x, y += h);
|
|
1070
1071
|
debugContext.fillText('Objects: ' + engineObjects.length, x, y += h);
|
|
1071
|
-
debugContext.fillText('Draw
|
|
1072
|
+
debugContext.fillText('Draw Calls: ' + drawCount, x, y += h);
|
|
1073
|
+
debugContext.fillText('Primitives: ' + primitiveCount, x, y += h);
|
|
1072
1074
|
debugContext.fillText('---------', x, y += h);
|
|
1073
1075
|
debugContext.fillStyle = '#f00';
|
|
1074
1076
|
debugContext.fillText('ESC: Debug Overlay', x, y += h);
|
|
@@ -1139,7 +1141,8 @@ function debugRenderPost()
|
|
|
1139
1141
|
mainContext.font = '1em monospace';
|
|
1140
1142
|
mainContext.fillStyle = '#000';
|
|
1141
1143
|
const text = engineName + ' v' + engineVersion + ' / '
|
|
1142
|
-
+ drawCount + ' / ' +
|
|
1144
|
+
+ drawCount + ' / ' + primitiveCount + ' / '
|
|
1145
|
+
+ engineObjects.length + ' / ' + averageFPS.toFixed(1)
|
|
1143
1146
|
+ (glEnable ? ' GL' : ' 2D') ;
|
|
1144
1147
|
mainContext.fillText(text, mainCanvas.width-3, 3);
|
|
1145
1148
|
mainContext.fillStyle = '#fff';
|
|
@@ -2449,6 +2452,7 @@ const MAGENTA = debugProtectConstant(rgb(1,0,1));
|
|
|
2449
2452
|
* - File saving (text, canvas, data URLs)
|
|
2450
2453
|
* - Native share dialog support
|
|
2451
2454
|
* - Local storage save data management
|
|
2455
|
+
* - Gradient noise (1D and 2D)
|
|
2452
2456
|
* @namespace Utilities
|
|
2453
2457
|
*/
|
|
2454
2458
|
|
|
@@ -2650,6 +2654,48 @@ function writeSaveData(saveName, saveData)
|
|
|
2650
2654
|
{
|
|
2651
2655
|
ASSERT(isStringLike(saveName), 'saveData requires saveName string');
|
|
2652
2656
|
localStorage[saveName] = JSON.stringify(saveData);
|
|
2657
|
+
}
|
|
2658
|
+
|
|
2659
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
2660
|
+
|
|
2661
|
+
// Deterministic well-distributed hash of an integer lattice index to [0, 1).
|
|
2662
|
+
// Murmur3 finalizer — adjacent integers produce uncorrelated outputs.
|
|
2663
|
+
function noiseHash(i)
|
|
2664
|
+
{
|
|
2665
|
+
let h = (i | 0) ^ 0x9e3779b9;
|
|
2666
|
+
h = Math.imul(h ^ (h >>> 16), 0x85ebca6b);
|
|
2667
|
+
h = Math.imul(h ^ (h >>> 13), 0xc2b2ae35);
|
|
2668
|
+
h ^= h >>> 16;
|
|
2669
|
+
return (h >>> 0) / 2**32;
|
|
2670
|
+
}
|
|
2671
|
+
|
|
2672
|
+
/** 1D gradient noise — returns a smooth value in [0, 1] for any real x.
|
|
2673
|
+
* Integer inputs land on deterministic lattice values; non-integer inputs
|
|
2674
|
+
* are interpolated with smoothStep for C1 continuity.
|
|
2675
|
+
* @param {number} x
|
|
2676
|
+
* @return {number}
|
|
2677
|
+
* @memberof Utilities */
|
|
2678
|
+
function noise1D(x)
|
|
2679
|
+
{
|
|
2680
|
+
const i = floor(x);
|
|
2681
|
+
return lerp(noiseHash(i), noiseHash(i + 1), smoothStep(x - i));
|
|
2682
|
+
}
|
|
2683
|
+
|
|
2684
|
+
/** 2D gradient noise — returns a smooth value in [0, 1] for any real (x, y).
|
|
2685
|
+
* @param {number} x
|
|
2686
|
+
* @param {number} y
|
|
2687
|
+
* @return {number}
|
|
2688
|
+
* @memberof Utilities */
|
|
2689
|
+
function noise2D(x, y)
|
|
2690
|
+
{
|
|
2691
|
+
const ix = floor(x), iy = floor(y);
|
|
2692
|
+
const fx = smoothStep(x - ix), fy = smoothStep(y - iy);
|
|
2693
|
+
// large prime decorrelates neighboring rows
|
|
2694
|
+
const h = (a, b) => noiseHash(a + b * 374761393);
|
|
2695
|
+
return lerp(
|
|
2696
|
+
lerp(h(ix, iy ), h(ix + 1, iy ), fx),
|
|
2697
|
+
lerp(h(ix, iy + 1), h(ix + 1, iy + 1), fx),
|
|
2698
|
+
fy);
|
|
2653
2699
|
}
|
|
2654
2700
|
/**
|
|
2655
2701
|
* LittleJS Engine Settings
|
|
@@ -3856,6 +3902,12 @@ let textureInfos = [];
|
|
|
3856
3902
|
* @memberof Draw */
|
|
3857
3903
|
let drawCount;
|
|
3858
3904
|
|
|
3905
|
+
/** Keeps track of how many primitives were drawn each frame for debugging
|
|
3906
|
+
* A single draw call can render many primitives (e.g. a WebGL sprite batch).
|
|
3907
|
+
* @type {number}
|
|
3908
|
+
* @memberof Draw */
|
|
3909
|
+
let primitiveCount;
|
|
3910
|
+
|
|
3859
3911
|
// internal predicates for tint short-circuiting in canvas2D draw paths
|
|
3860
3912
|
// isWhite ignores alpha because alpha is applied via globalAlpha, not multiply
|
|
3861
3913
|
// isBlack includes alpha so additive colors that only contribute alpha are not skipped
|
|
@@ -4096,19 +4148,19 @@ function drawTile(pos, size=vec2(1), tileInfo, color=WHITE,
|
|
|
4096
4148
|
}
|
|
4097
4149
|
else
|
|
4098
4150
|
{
|
|
4099
|
-
//
|
|
4100
|
-
//
|
|
4101
|
-
//
|
|
4102
|
-
// color.add(additiveColor) on line ~337.
|
|
4151
|
+
// untextured: glDrawUntextured picks the optimal path (poly
|
|
4152
|
+
// tristrip if already in poly mode, otherwise instanced with
|
|
4153
|
+
// uvs/rgba zeroed). Color+additive are folded together to match
|
|
4154
|
+
// the Canvas2D path's color.add(additiveColor) on line ~337.
|
|
4103
4155
|
const combined = additiveColor ? color.add(additiveColor) : color;
|
|
4104
|
-
|
|
4105
|
-
0, combined.rgbaInt());
|
|
4156
|
+
glDrawUntextured(pos.x, pos.y, size.x, size.y, angle, combined.rgbaInt());
|
|
4106
4157
|
}
|
|
4107
4158
|
}
|
|
4108
4159
|
else
|
|
4109
4160
|
{
|
|
4110
4161
|
// normal canvas 2D rendering method (slower)
|
|
4111
4162
|
++drawCount;
|
|
4163
|
+
++primitiveCount;
|
|
4112
4164
|
size = new Vector2(size.x, -size.y); // flip upside down sprites
|
|
4113
4165
|
drawCanvas2D(pos, size, angle, mirror, (context)=>
|
|
4114
4166
|
{
|
|
@@ -4148,13 +4200,13 @@ function drawRect(pos, size, color, angle, useWebGL, screenSpace, context)
|
|
|
4148
4200
|
* @param {Vector2} pos
|
|
4149
4201
|
* @param {Vector2} [size=vec2(1)]
|
|
4150
4202
|
* @param {Color} [colorTop=WHITE]
|
|
4151
|
-
* @param {Color} [colorBottom=
|
|
4203
|
+
* @param {Color} [colorBottom=CLEAR_WHITE]
|
|
4152
4204
|
* @param {number} [angle]
|
|
4153
4205
|
* @param {boolean} [useWebGL=glEnable]
|
|
4154
4206
|
* @param {boolean} [screenSpace]
|
|
4155
4207
|
* @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} [context]
|
|
4156
4208
|
* @memberof Draw */
|
|
4157
|
-
function drawRectGradient(pos, size, colorTop=WHITE, colorBottom=
|
|
4209
|
+
function drawRectGradient(pos, size, colorTop=WHITE, colorBottom=CLEAR_WHITE, angle=0, useWebGL=glEnable, screenSpace=false, context)
|
|
4158
4210
|
{
|
|
4159
4211
|
ASSERT(isVector2(pos), 'pos must be a vec2');
|
|
4160
4212
|
ASSERT(isVector2(size), 'size must be a vec2');
|
|
@@ -4194,6 +4246,7 @@ function drawRectGradient(pos, size, colorTop=WHITE, colorBottom=BLACK, angle=0,
|
|
|
4194
4246
|
{
|
|
4195
4247
|
// normal canvas 2D rendering method (slower)
|
|
4196
4248
|
++drawCount;
|
|
4249
|
+
++primitiveCount;
|
|
4197
4250
|
size = new Vector2(size.x, -size.y); // fix upside down sprites
|
|
4198
4251
|
drawCanvas2D(pos, size, angle, false, (context)=>
|
|
4199
4252
|
{
|
|
@@ -4256,8 +4309,9 @@ function drawTextureWrapped(pos, size, wrapCount, texture=0, color=WHITE,
|
|
|
4256
4309
|
return;
|
|
4257
4310
|
}
|
|
4258
4311
|
|
|
4259
|
-
// Canvas2D path — increment
|
|
4312
|
+
// Canvas2D path — increment counts here (WebGL counts via glFlush)
|
|
4260
4313
|
++drawCount;
|
|
4314
|
+
++primitiveCount;
|
|
4261
4315
|
|
|
4262
4316
|
if (!screenSpace)
|
|
4263
4317
|
{
|
|
@@ -4331,6 +4385,7 @@ function drawLineList(points, width=.1, color, wrap=false, pos=vec2(), angle=0,
|
|
|
4331
4385
|
{
|
|
4332
4386
|
// normal canvas 2D rendering method (slower)
|
|
4333
4387
|
++drawCount;
|
|
4388
|
+
++primitiveCount;
|
|
4334
4389
|
drawCanvas2D(pos, vec2(1), angle, false, (context)=>
|
|
4335
4390
|
{
|
|
4336
4391
|
context.strokeStyle = color.toString();
|
|
@@ -4511,6 +4566,77 @@ function drawCircle(pos, size=1, color=WHITE, lineWidth=0, lineColor=BLACK, useW
|
|
|
4511
4566
|
drawEllipse(pos, vec2(size), color, 0, lineWidth, lineColor, useWebGL, screenSpace, context);
|
|
4512
4567
|
}
|
|
4513
4568
|
|
|
4569
|
+
/** Draw a circle filled with a radial gradient from the center to the rim
|
|
4570
|
+
* - Best when batched with other untextured polys
|
|
4571
|
+
* - If drawing mostly textured sprites, bake the gradient into a texture and use drawTile instead
|
|
4572
|
+
* - Stacking gradients at the exact same position may show a faint vertical artifact
|
|
4573
|
+
* @param {Vector2} pos
|
|
4574
|
+
* @param {number} [size=1] - Diameter
|
|
4575
|
+
* @param {Color} [colorInner=WHITE]
|
|
4576
|
+
* @param {Color} [colorOuter=CLEAR_WHITE]
|
|
4577
|
+
* @param {boolean} [useWebGL=glEnable]
|
|
4578
|
+
* @param {boolean} [screenSpace]
|
|
4579
|
+
* @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} [context]
|
|
4580
|
+
* @memberof Draw */
|
|
4581
|
+
let drawCircleGradientOffset = 0;
|
|
4582
|
+
function drawCircleGradient(pos, size=1, colorInner=WHITE, colorOuter=CLEAR_WHITE, useWebGL=glEnable, screenSpace=false, context)
|
|
4583
|
+
{
|
|
4584
|
+
ASSERT(isVector2(pos), 'pos must be a vec2');
|
|
4585
|
+
ASSERT(isNumber(size), 'size must be a number');
|
|
4586
|
+
ASSERT(isColor(colorInner) && isColor(colorOuter), 'color is invalid');
|
|
4587
|
+
ASSERT(!context || !useWebGL, 'context only supported in canvas 2D mode');
|
|
4588
|
+
|
|
4589
|
+
if (headlessMode) return;
|
|
4590
|
+
|
|
4591
|
+
if (useWebGL && glEnable)
|
|
4592
|
+
{
|
|
4593
|
+
ASSERT(!!glContext, 'WebGL is not enabled!');
|
|
4594
|
+
if (screenSpace)
|
|
4595
|
+
{
|
|
4596
|
+
// convert to world space
|
|
4597
|
+
pos = screenToWorld(pos);
|
|
4598
|
+
size /= cameraScale;
|
|
4599
|
+
}
|
|
4600
|
+
// fan as tristrip; rotate the boundary vertex by one slice per call
|
|
4601
|
+
// so back-to-back gradients at the same position have their hole
|
|
4602
|
+
// (from gpu edge-rule on the boundary line-degen) at different rim
|
|
4603
|
+
// verts and don't visibly stack
|
|
4604
|
+
const sides = glCircleSides;
|
|
4605
|
+
const radius = size/2;
|
|
4606
|
+
const innerInt = colorInner.rgbaInt();
|
|
4607
|
+
const outerInt = colorOuter.rgbaInt();
|
|
4608
|
+
const offset = drawCircleGradientOffset++;
|
|
4609
|
+
const startA = (offset%sides)/sides*PI*2;
|
|
4610
|
+
const points = [vec2(pos.x + sin(startA)*radius, pos.y + cos(startA)*radius)];
|
|
4611
|
+
const colors = [outerInt];
|
|
4612
|
+
for (let i=sides; i--;)
|
|
4613
|
+
{
|
|
4614
|
+
const a = ((i+offset)%sides)/sides*PI*2;
|
|
4615
|
+
points.push(pos);
|
|
4616
|
+
colors.push(innerInt);
|
|
4617
|
+
points.push(vec2(pos.x + sin(a)*radius, pos.y + cos(a)*radius));
|
|
4618
|
+
colors.push(outerInt);
|
|
4619
|
+
}
|
|
4620
|
+
glDrawColoredPoints(points, colors);
|
|
4621
|
+
}
|
|
4622
|
+
else
|
|
4623
|
+
{
|
|
4624
|
+
// normal canvas 2D rendering method (slower)
|
|
4625
|
+
++drawCount;
|
|
4626
|
+
++primitiveCount;
|
|
4627
|
+
drawCanvas2D(pos, vec2(size), 0, false, (context)=>
|
|
4628
|
+
{
|
|
4629
|
+
const gradient = context.createRadialGradient(0, 0, 0, 0, 0, .5);
|
|
4630
|
+
gradient.addColorStop(0, colorInner.toString());
|
|
4631
|
+
gradient.addColorStop(1, colorOuter.toString());
|
|
4632
|
+
context.fillStyle = gradient;
|
|
4633
|
+
context.beginPath();
|
|
4634
|
+
context.ellipse(0, 0, .5, .5, 0, 0, 9);
|
|
4635
|
+
context.fill();
|
|
4636
|
+
}, screenSpace, context);
|
|
4637
|
+
}
|
|
4638
|
+
}
|
|
4639
|
+
|
|
4514
4640
|
/**
|
|
4515
4641
|
* @callback Canvas2DDrawFunction - A function that draws to a 2D canvas context
|
|
4516
4642
|
* @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} context
|
|
@@ -6356,14 +6482,15 @@ class SoundInstance
|
|
|
6356
6482
|
|
|
6357
6483
|
/** Speak text with passed in settings
|
|
6358
6484
|
* @param {string} text - The text to speak
|
|
6359
|
-
* @param {string} [language] - The language/accent to use (examples: en, it, ru, ja, zh)
|
|
6360
6485
|
* @param {number} [volume] - How much to scale volume by
|
|
6361
6486
|
* @param {number} [rate] - How quickly to speak
|
|
6362
6487
|
* @param {number} [pitch] - How much to change the pitch by
|
|
6488
|
+
* @param {string} [language] - The language/accent to use (examples: en, it, ru, ja, zh)
|
|
6363
6489
|
* @return {SpeechSynthesisUtterance} - The utterance that was spoken
|
|
6364
6490
|
* @memberof Audio */
|
|
6365
|
-
function speak(text,
|
|
6491
|
+
function speak(text, volume=1, rate=1, pitch=1, language='')
|
|
6366
6492
|
{
|
|
6493
|
+
ASSERT(typeof volume !== 'string', 'speak() signature changed: language is now the last parameter, after pitch');
|
|
6367
6494
|
if (!soundEnable || headlessMode) return;
|
|
6368
6495
|
if (!speechSynthesis) return;
|
|
6369
6496
|
|
|
@@ -8356,7 +8483,8 @@ function glFlush()
|
|
|
8356
8483
|
glContext.drawArrays(glContext.TRIANGLE_STRIP, 0, glBatchCount);
|
|
8357
8484
|
else
|
|
8358
8485
|
glContext.drawArraysInstanced(glContext.TRIANGLE_STRIP, 0, 4, glBatchCount);
|
|
8359
|
-
drawCount
|
|
8486
|
+
++drawCount;
|
|
8487
|
+
primitiveCount += glBatchCount;
|
|
8360
8488
|
glBatchCount = 0;
|
|
8361
8489
|
}
|
|
8362
8490
|
glBatchAdditive = glAdditive;
|
|
@@ -8417,6 +8545,67 @@ function glDraw(x, y, sizeX, sizeY, angle=0, uv0X=0, uv0Y=0, uv1X=1, uv1Y=1, rgb
|
|
|
8417
8545
|
glPositionData[offset++] = angle;
|
|
8418
8546
|
}
|
|
8419
8547
|
|
|
8548
|
+
/** Add an untextured rect to the gl draw list
|
|
8549
|
+
* Picks the optimal path: if already in poly mode, emits a tristrip rect
|
|
8550
|
+
* so it batches with surrounding polys; otherwise uses the instanced path
|
|
8551
|
+
* with uvs and rgba zeroed so the color falls through the additive slot.
|
|
8552
|
+
* @param {number} x
|
|
8553
|
+
* @param {number} y
|
|
8554
|
+
* @param {number} sizeX
|
|
8555
|
+
* @param {number} sizeY
|
|
8556
|
+
* @param {number} angle
|
|
8557
|
+
* @param {number} rgba - color as 32-bit integer
|
|
8558
|
+
* @memberof WebGL */
|
|
8559
|
+
function glDrawUntextured(x, y, sizeX, sizeY, angle, rgba)
|
|
8560
|
+
{
|
|
8561
|
+
if (glPolyMode)
|
|
8562
|
+
{
|
|
8563
|
+
// batch with surrounding polys as a 4-vertex tristrip rect
|
|
8564
|
+
const vertCount = 6; // 4 corners + 2 degenerate verts
|
|
8565
|
+
if (glBatchCount+vertCount >= gl_MAX_POLY_VERTEXES || glBatchAdditive !== glAdditive)
|
|
8566
|
+
glFlush();
|
|
8567
|
+
|
|
8568
|
+
// compute rotated corners in world space (matches glDrawPointsTransform rotation)
|
|
8569
|
+
const hx = sizeX*.5, hy = sizeY*.5;
|
|
8570
|
+
const c = cos(angle), s = sin(angle);
|
|
8571
|
+
const chx = c*hx, shx = s*hx, chy = c*hy, shy = s*hy;
|
|
8572
|
+
const x0 = x - chx - shy, y0 = y + shx - chy; // (-hx,-hy)
|
|
8573
|
+
const x1 = x + chx - shy, y1 = y - shx - chy; // ( hx,-hy)
|
|
8574
|
+
const x2 = x - chx + shy, y2 = y + shx + chy; // (-hx, hy)
|
|
8575
|
+
const x3 = x + chx + shy, y3 = y - shx + chy; // ( hx, hy)
|
|
8576
|
+
|
|
8577
|
+
// write tristrip with leading/trailing degenerate verts
|
|
8578
|
+
let offset = glBatchCount * gl_INDICES_PER_POLY_VERTEX;
|
|
8579
|
+
glPositionData[offset++] = x0; glPositionData[offset++] = y0; glColorData[offset++] = rgba;
|
|
8580
|
+
glPositionData[offset++] = x0; glPositionData[offset++] = y0; glColorData[offset++] = rgba;
|
|
8581
|
+
glPositionData[offset++] = x1; glPositionData[offset++] = y1; glColorData[offset++] = rgba;
|
|
8582
|
+
glPositionData[offset++] = x2; glPositionData[offset++] = y2; glColorData[offset++] = rgba;
|
|
8583
|
+
glPositionData[offset++] = x3; glPositionData[offset++] = y3; glColorData[offset++] = rgba;
|
|
8584
|
+
glPositionData[offset++] = x3; glPositionData[offset++] = y3; glColorData[offset++] = rgba;
|
|
8585
|
+
glBatchCount += vertCount;
|
|
8586
|
+
return;
|
|
8587
|
+
}
|
|
8588
|
+
|
|
8589
|
+
// instanced path: zero uvs and rgba so the texture contribution is killed,
|
|
8590
|
+
// then carry the real color in the additive slot
|
|
8591
|
+
if (glBatchCount >= gl_MAX_INSTANCES || glBatchAdditive !== glAdditive)
|
|
8592
|
+
glFlush();
|
|
8593
|
+
glSetInstancedMode();
|
|
8594
|
+
|
|
8595
|
+
let offset = glBatchCount++ * gl_INDICES_PER_INSTANCE;
|
|
8596
|
+
glPositionData[offset++] = x;
|
|
8597
|
+
glPositionData[offset++] = y;
|
|
8598
|
+
glPositionData[offset++] = sizeX;
|
|
8599
|
+
glPositionData[offset++] = sizeY;
|
|
8600
|
+
glPositionData[offset++] = 0;
|
|
8601
|
+
glPositionData[offset++] = 0;
|
|
8602
|
+
glPositionData[offset++] = 0;
|
|
8603
|
+
glPositionData[offset++] = 0;
|
|
8604
|
+
glColorData[offset++] = 0;
|
|
8605
|
+
glColorData[offset++] = rgba;
|
|
8606
|
+
glPositionData[offset++] = angle;
|
|
8607
|
+
}
|
|
8608
|
+
|
|
8420
8609
|
/** Transform and add a polygon to the gl draw list
|
|
8421
8610
|
* @param {Array<Vector2>} points - Array of Vector2 points
|
|
8422
8611
|
* @param {number} rgba - Color of the polygon as a 32-bit integer
|
|
@@ -9053,7 +9242,8 @@ class Medal
|
|
|
9053
9242
|
/** @property {boolean} - Is the medal unlocked? */
|
|
9054
9243
|
this.unlocked = false;
|
|
9055
9244
|
|
|
9056
|
-
|
|
9245
|
+
/** @property {HTMLImageElement|undefined} - Source image for the medal icon, if any */
|
|
9246
|
+
this.image = undefined;
|
|
9057
9247
|
if (src)
|
|
9058
9248
|
(this.image = new Image).src = src;
|
|
9059
9249
|
|
|
@@ -9216,13 +9406,18 @@ class NewgroundsPlugin
|
|
|
9216
9406
|
ASSERT(!cipher || cryptoJS, 'must provide cryptojs if there is a cipher');
|
|
9217
9407
|
|
|
9218
9408
|
newgrounds = this; // set global newgrounds object
|
|
9409
|
+
/** @property {string} - The newgrounds App ID */
|
|
9219
9410
|
this.app_id = app_id;
|
|
9411
|
+
/** @property {string|undefined} - AES-128/Base64 encryption key, if any */
|
|
9220
9412
|
this.cipher = cipher;
|
|
9413
|
+
/** @property {Object|undefined} - CryptoJS instance used when cipher is set */
|
|
9221
9414
|
this.cryptoJS = cryptoJS;
|
|
9415
|
+
/** @property {string} - Hostname used when logging views */
|
|
9222
9416
|
this.host = location ? location.hostname : '';
|
|
9223
9417
|
|
|
9224
9418
|
// get session id from url search params
|
|
9225
9419
|
const url = new URL(location.href);
|
|
9420
|
+
/** @property {string|null} - Newgrounds session id from the URL (null when not logged in) */
|
|
9226
9421
|
this.session_id = url.searchParams.get('ngio_session_id');
|
|
9227
9422
|
|
|
9228
9423
|
if (!this.session_id)
|
|
@@ -9230,6 +9425,7 @@ class NewgroundsPlugin
|
|
|
9230
9425
|
|
|
9231
9426
|
// get medals
|
|
9232
9427
|
const medalsResult = this.call('Medal.getList');
|
|
9428
|
+
/** @property {Array} - Medals fetched from Newgrounds (empty until session is active) */
|
|
9233
9429
|
this.medals = medalsResult ? medalsResult.result.data['medals'] : [];
|
|
9234
9430
|
debugMedals && LOG(this.medals);
|
|
9235
9431
|
for (const newgroundsMedal of this.medals)
|
|
@@ -9253,6 +9449,7 @@ class NewgroundsPlugin
|
|
|
9253
9449
|
|
|
9254
9450
|
// get scoreboards
|
|
9255
9451
|
const scoreboardResult = this.call('ScoreBoard.getBoards');
|
|
9452
|
+
/** @property {Array} - Scoreboards fetched from Newgrounds */
|
|
9256
9453
|
this.scoreboards = scoreboardResult ? scoreboardResult.result.data.scoreboards : [];
|
|
9257
9454
|
debugMedals && LOG(this.scoreboards);
|
|
9258
9455
|
|
|
@@ -13565,13 +13762,21 @@ class Tween
|
|
|
13565
13762
|
}
|
|
13566
13763
|
ASSERT(isNumber(duration) && duration > 0, 'Tween duration must be > 0');
|
|
13567
13764
|
|
|
13765
|
+
/** @property {function(number|Vector2|Color):void} - Called with the interpolated value each frame */
|
|
13568
13766
|
this.callback = callback;
|
|
13767
|
+
/** @property {number|Vector2|Color} - Starting value */
|
|
13569
13768
|
this.start = start;
|
|
13769
|
+
/** @property {number|Vector2|Color} - Ending value */
|
|
13570
13770
|
this.end = end;
|
|
13771
|
+
/** @property {number} - Total duration in seconds */
|
|
13571
13772
|
this.duration = duration;
|
|
13773
|
+
/** @property {number} - Remaining time in seconds (counts down from duration to 0) */
|
|
13572
13774
|
this.life = duration;
|
|
13775
|
+
/** @property {function(number):number} - Easing curve mapping [0,1] -> [0,1] */
|
|
13573
13776
|
this.ease = options.ease || Ease.LINEAR;
|
|
13777
|
+
/** @property {boolean} - If true, advance even when the game is paused */
|
|
13574
13778
|
this.useRealTime = !!options.useRealTime;
|
|
13779
|
+
/** @property {boolean} - If true, stop advancing until cleared */
|
|
13575
13780
|
this.paused = !!options.paused;
|
|
13576
13781
|
|
|
13577
13782
|
/** @private completion callback set by then(), loop(), pingPong(). */
|
|
@@ -14104,7 +14309,9 @@ class PathFinder
|
|
|
14104
14309
|
// .size + .getCollisionData.
|
|
14105
14310
|
if (isVector2(source))
|
|
14106
14311
|
{
|
|
14312
|
+
/** @property {Vector2} - Grid dimensions in tiles */
|
|
14107
14313
|
this.size = source.floor();
|
|
14314
|
+
/** @property {TileCollisionLayer|undefined} - Tile layer driving walkability, if any */
|
|
14108
14315
|
this.tileLayer = undefined;
|
|
14109
14316
|
}
|
|
14110
14317
|
else
|
|
@@ -14116,13 +14323,18 @@ class PathFinder
|
|
|
14116
14323
|
}
|
|
14117
14324
|
|
|
14118
14325
|
// Tunables (public, freely re-assignable).
|
|
14326
|
+
/** @property {number} - A* heuristic multiplier (1 = admissible, higher = greedier) */
|
|
14119
14327
|
this.heuristicWeight = 1;
|
|
14120
|
-
|
|
14328
|
+
/** @property {number} - Maximum A* expansions before giving up */
|
|
14329
|
+
this.maxLoop = 1e3;
|
|
14330
|
+
/** @property {boolean} - If true, post-process paths with two-pass smoothing */
|
|
14121
14331
|
this.smoothPath = true;
|
|
14332
|
+
/** @property {boolean} - If true, draw debug visualization during findPath */
|
|
14122
14333
|
this.debug = false;
|
|
14123
|
-
|
|
14334
|
+
/** @property {number} - Debug primitive lifetime in seconds (0 disables drawing) */
|
|
14335
|
+
this.debugTime = 1;
|
|
14124
14336
|
|
|
14125
|
-
|
|
14337
|
+
/** @property {Array<PathFinderNode>} - Flat row-major array of size.x*size.y nodes */
|
|
14126
14338
|
this.nodes = new Array(this.size.x * this.size.y);
|
|
14127
14339
|
for (let y = 0; y < this.size.y; ++y)
|
|
14128
14340
|
for (let x = 0; x < this.size.x; ++x)
|
|
@@ -14296,9 +14508,12 @@ class PathFinder
|
|
|
14296
14508
|
// Best path so far through neighbor — record it.
|
|
14297
14509
|
neighbor.parent = current;
|
|
14298
14510
|
neighbor.g = tentativeG;
|
|
14299
|
-
|
|
14300
|
-
|
|
14301
|
-
|
|
14511
|
+
// Octile heuristic — tightest admissible distance for an
|
|
14512
|
+
// 8-connected grid with cardinal cost 1 and diagonal cost √2.
|
|
14513
|
+
const adx = abs(endNode.pos.x - neighbor.pos.x);
|
|
14514
|
+
const ady = abs(endNode.pos.y - neighbor.pos.y);
|
|
14515
|
+
const h = max(adx, ady) + (Math.SQRT2 - 1) * min(adx, ady);
|
|
14516
|
+
neighbor.f = neighbor.g + h * this.heuristicWeight;
|
|
14302
14517
|
}
|
|
14303
14518
|
}
|
|
14304
14519
|
|
|
@@ -14580,6 +14795,24 @@ class PathFinder
|
|
|
14580
14795
|
path.push(original[original.length - 1]);
|
|
14581
14796
|
}
|
|
14582
14797
|
|
|
14798
|
+
/** Drop any middle node that lies exactly on the line through its two
|
|
14799
|
+
* neighbors. Backstop for the smoothing passes — the corners pass
|
|
14800
|
+
* intentionally keeps truly-straight runs, and the string-pulling pass
|
|
14801
|
+
* checks collinearity against the original path, not the in-progress
|
|
14802
|
+
* result, so it can leave 3+ collinear nodes in some edge cases.
|
|
14803
|
+
* @param {PathFinderNode[]} path
|
|
14804
|
+
* @private */
|
|
14805
|
+
dropCollinearNodes(path)
|
|
14806
|
+
{
|
|
14807
|
+
for (let i = path.length - 2; i >= 1; --i)
|
|
14808
|
+
{
|
|
14809
|
+
const a = path[i - 1], b = path[i], c = path[i + 1];
|
|
14810
|
+
if ((b.pos.x - a.pos.x) * (c.pos.y - a.pos.y) ===
|
|
14811
|
+
(b.pos.y - a.pos.y) * (c.pos.x - a.pos.x))
|
|
14812
|
+
path.splice(i, 1);
|
|
14813
|
+
}
|
|
14814
|
+
}
|
|
14815
|
+
|
|
14583
14816
|
/** Lookup helper: true when the node at tile coords (x, y) is in-bounds
|
|
14584
14817
|
* and clear (walkable, zero-cost). Used by isLineClear's hot path.
|
|
14585
14818
|
* @param {number} x
|
|
@@ -14747,6 +14980,7 @@ class PathFinder
|
|
|
14747
14980
|
{
|
|
14748
14981
|
this.smoothPathCorners(nodePath);
|
|
14749
14982
|
this.smoothPathStringPull(nodePath);
|
|
14983
|
+
this.dropCollinearNodes(nodePath);
|
|
14750
14984
|
}
|
|
14751
14985
|
|
|
14752
14986
|
// Convert to world-space Vector2 path. Return copies, not live node
|
|
@@ -14957,6 +15191,8 @@ export
|
|
|
14957
15191
|
shareURL,
|
|
14958
15192
|
readSaveData,
|
|
14959
15193
|
writeSaveData,
|
|
15194
|
+
noise1D,
|
|
15195
|
+
noise2D,
|
|
14960
15196
|
|
|
14961
15197
|
// Random
|
|
14962
15198
|
rand,
|
|
@@ -15010,6 +15246,7 @@ export
|
|
|
15010
15246
|
mainCanvasSize,
|
|
15011
15247
|
textureInfos,
|
|
15012
15248
|
drawCount,
|
|
15249
|
+
primitiveCount,
|
|
15013
15250
|
screenToWorld,
|
|
15014
15251
|
worldToScreen,
|
|
15015
15252
|
screenToWorldDelta,
|
|
@@ -15025,6 +15262,7 @@ export
|
|
|
15025
15262
|
drawRegularPoly,
|
|
15026
15263
|
drawEllipse,
|
|
15027
15264
|
drawCircle,
|
|
15265
|
+
drawCircleGradient,
|
|
15028
15266
|
drawCanvas2D,
|
|
15029
15267
|
drawText,
|
|
15030
15268
|
drawTextScreen,
|
|
@@ -15054,6 +15292,7 @@ export
|
|
|
15054
15292
|
glCopyToContext,
|
|
15055
15293
|
glSetAntialias,
|
|
15056
15294
|
glDraw,
|
|
15295
|
+
glDrawUntextured,
|
|
15057
15296
|
glDrawPointsTransform,
|
|
15058
15297
|
glDrawOutlineTransform,
|
|
15059
15298
|
glDrawPoints,
|