littlejsengine 1.9.6 → 1.9.8
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/LICENSE +22 -0
- package/README.md +13 -8
- package/dist/littlejs.d.ts +85 -84
- package/dist/littlejs.esm.js +370 -390
- package/dist/littlejs.esm.min.js +1 -1
- package/dist/littlejs.js +365 -386
- package/dist/littlejs.min.js +1 -1
- package/dist/littlejs.release.js +302 -355
- package/examples/box2d/game.js +138 -0
- package/examples/box2d/index.html +25 -0
- package/examples/box2d/scenes.js +412 -0
- package/examples/box2d/tiles.png +0 -0
- package/examples/breakout/game.js +3 -3
- package/examples/breakout/index.html +1 -0
- package/examples/platformer/gameEffects.js +2 -2
- package/examples/platformer/gameLevel.js +0 -1
- package/package.json +1 -1
- package/plugins/Box2D_v2.3.1_min.wasm.js +630 -0
- package/plugins/Box2D_v2.3.1_min.wasm.wasm +0 -0
- package/plugins/box2d.js +873 -0
- package/plugins/newgrounds.js +169 -0
- package/plugins/postProcess.js +101 -0
- package/src/engine.js +45 -26
- package/src/engineAudio.js +72 -47
- package/src/engineDebug.js +68 -33
- package/src/engineDraw.js +1 -1
- package/src/engineExport.js +5 -4
- package/src/engineMedals.js +39 -171
- package/src/engineObject.js +47 -4
- package/src/engineParticles.js +3 -0
- package/src/engineRelease.js +5 -2
- package/src/engineSettings.js +8 -3
- package/src/engineUtilities.js +81 -7
- package/src/engineWebGL.js +1 -94
package/dist/littlejs.release.js
CHANGED
|
@@ -26,13 +26,16 @@ function debugInit (){}
|
|
|
26
26
|
function debugUpdate (){}
|
|
27
27
|
function debugRender (){}
|
|
28
28
|
function debugRect (){}
|
|
29
|
+
function debugPoly (){}
|
|
29
30
|
function debugCircle (){}
|
|
30
31
|
function debugPoint (){}
|
|
31
32
|
function debugLine (){}
|
|
32
|
-
function
|
|
33
|
+
function debugOverlap (){}
|
|
33
34
|
function debugText (){}
|
|
34
35
|
function debugClear (){}
|
|
35
|
-
function debugSaveCanvas (){}
|
|
36
|
+
function debugSaveCanvas (){}
|
|
37
|
+
function debugSaveText (){}
|
|
38
|
+
function debugSaveDataURL(){}
|
|
36
39
|
/**
|
|
37
40
|
* LittleJS Utility Classes and Functions
|
|
38
41
|
* - General purpose math library
|
|
@@ -336,7 +339,7 @@ class RandomGenerator
|
|
|
336
339
|
*/
|
|
337
340
|
function vec2(x=0, y)
|
|
338
341
|
{
|
|
339
|
-
return typeof x
|
|
342
|
+
return typeof x == 'number' ?
|
|
340
343
|
new Vector2(x, y == undefined? x : y) :
|
|
341
344
|
new Vector2(x.x, x.y);
|
|
342
345
|
}
|
|
@@ -365,13 +368,19 @@ class Vector2
|
|
|
365
368
|
* @param {Number} [y] - Y axis location */
|
|
366
369
|
constructor(x=0, y=0)
|
|
367
370
|
{
|
|
368
|
-
ASSERT(typeof x
|
|
371
|
+
ASSERT(typeof x == 'number' && typeof y == 'number');
|
|
369
372
|
/** @property {Number} - X axis location */
|
|
370
373
|
this.x = x;
|
|
371
374
|
/** @property {Number} - Y axis location */
|
|
372
375
|
this.y = y;
|
|
373
376
|
}
|
|
374
377
|
|
|
378
|
+
/** Sets values of this vector and returns self
|
|
379
|
+
* @param {Number} [x] - X axis location
|
|
380
|
+
* @param {Number} [y] - Y axis location
|
|
381
|
+
* @return {Vector2} */
|
|
382
|
+
set(x=0, y=0) { this.x=x; this.y=y; return this; }
|
|
383
|
+
|
|
375
384
|
/** Returns a new vector that is a copy of this
|
|
376
385
|
* @return {Vector2} */
|
|
377
386
|
copy() { return new Vector2(this.x, this.y); }
|
|
@@ -623,6 +632,15 @@ class Color
|
|
|
623
632
|
this.a = a;
|
|
624
633
|
}
|
|
625
634
|
|
|
635
|
+
/** Sets values of this color and returns self
|
|
636
|
+
* @param {Number} [r] - red
|
|
637
|
+
* @param {Number} [g] - green
|
|
638
|
+
* @param {Number} [b] - blue
|
|
639
|
+
* @param {Number} [a] - alpha
|
|
640
|
+
* @return {Color} */
|
|
641
|
+
set(r=1, g=1, b=1, a=1)
|
|
642
|
+
{ this.r=r; this.g=g; this.b=b; this.a=a; return this; }
|
|
643
|
+
|
|
626
644
|
/** Returns a new color that is a copy of this
|
|
627
645
|
* @return {Color} */
|
|
628
646
|
copy() { return new Color(this.r, this.g, this.b, this.a); }
|
|
@@ -750,14 +768,15 @@ class Color
|
|
|
750
768
|
).clamp();
|
|
751
769
|
}
|
|
752
770
|
|
|
753
|
-
/** Returns this color expressed as a
|
|
771
|
+
/** Returns this color expressed as a hex color code
|
|
754
772
|
* @param {Boolean} [useAlpha] - if alpha should be included in result
|
|
755
773
|
* @return {String} */
|
|
756
|
-
toString(useAlpha = true)
|
|
757
|
-
{
|
|
758
|
-
|
|
774
|
+
toString(useAlpha = true)
|
|
775
|
+
{
|
|
776
|
+
const toHex = (c)=> ((c=c*255|0)<16 ? '0' : '') + c.toString(16);
|
|
777
|
+
return '#' + toHex(this.r) + toHex(this.g) + toHex(this.b) + (useAlpha ? toHex(this.a) : '');
|
|
759
778
|
}
|
|
760
|
-
|
|
779
|
+
|
|
761
780
|
/** Set this color from a hex code
|
|
762
781
|
* @param {String} hex - html hex code
|
|
763
782
|
* @return {Color} */
|
|
@@ -783,6 +802,64 @@ class Color
|
|
|
783
802
|
}
|
|
784
803
|
}
|
|
785
804
|
|
|
805
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
806
|
+
// default colors
|
|
807
|
+
|
|
808
|
+
/** Color - White
|
|
809
|
+
* @type {Color}
|
|
810
|
+
* @memberof Utilities */
|
|
811
|
+
const WHITE = rgb();
|
|
812
|
+
|
|
813
|
+
/** Color - Black
|
|
814
|
+
* @type {Color}
|
|
815
|
+
* @memberof Utilities */
|
|
816
|
+
const BLACK = rgb(0,0,0);
|
|
817
|
+
|
|
818
|
+
/** Color - Gray
|
|
819
|
+
* @type {Color}
|
|
820
|
+
* @memberof Utilities */
|
|
821
|
+
const GRAY = rgb(.5,.5,.5);
|
|
822
|
+
|
|
823
|
+
/** Color - Red
|
|
824
|
+
* @type {Color}
|
|
825
|
+
* @memberof Utilities */
|
|
826
|
+
const RED = rgb(1,0,0);
|
|
827
|
+
|
|
828
|
+
/** Color - Orange
|
|
829
|
+
* @type {Color}
|
|
830
|
+
* @memberof Utilities */
|
|
831
|
+
const ORANGE = rgb(1,.5,0);
|
|
832
|
+
|
|
833
|
+
/** Color - Yellow
|
|
834
|
+
* @type {Color}
|
|
835
|
+
* @memberof Utilities */
|
|
836
|
+
const YELLOW = rgb(1,1,0);
|
|
837
|
+
|
|
838
|
+
/** Color - Green
|
|
839
|
+
* @type {Color}
|
|
840
|
+
* @memberof Utilities */
|
|
841
|
+
const GREEN = rgb(0,1,0);
|
|
842
|
+
|
|
843
|
+
/** Color - Cyan
|
|
844
|
+
* @type {Color}
|
|
845
|
+
* @memberof Utilities */
|
|
846
|
+
const CYAN = rgb(0,1,1);
|
|
847
|
+
|
|
848
|
+
/** Color - Blue
|
|
849
|
+
* @type {Color}
|
|
850
|
+
* @memberof Utilities */
|
|
851
|
+
const BLUE = rgb(0,0,1);
|
|
852
|
+
|
|
853
|
+
/** Color - Purple
|
|
854
|
+
* @type {Color}
|
|
855
|
+
* @memberof Utilities */
|
|
856
|
+
const PURPLE = rgb(.5,0,1);
|
|
857
|
+
|
|
858
|
+
/** Color - Magenta
|
|
859
|
+
* @type {Color}
|
|
860
|
+
* @memberof Utilities */
|
|
861
|
+
const MAGENTA = rgb(1,0,1);
|
|
862
|
+
|
|
786
863
|
///////////////////////////////////////////////////////////////////////////////
|
|
787
864
|
|
|
788
865
|
/**
|
|
@@ -863,9 +940,9 @@ let cameraScale = 32;
|
|
|
863
940
|
|
|
864
941
|
/** The max size of the canvas, centered if window is larger
|
|
865
942
|
* @type {Vector2}
|
|
866
|
-
* @default Vector2(1920,
|
|
943
|
+
* @default Vector2(1920,1080)
|
|
867
944
|
* @memberof Settings */
|
|
868
|
-
let canvasMaxSize = vec2(1920,
|
|
945
|
+
let canvasMaxSize = vec2(1920, 1080);
|
|
869
946
|
|
|
870
947
|
/** Fixed size of the canvas, if enabled canvas size never changes
|
|
871
948
|
* - you may also need to set mainCanvasSize if using screen space coords in startup
|
|
@@ -1254,7 +1331,12 @@ function setSoundEnable(enable) { soundEnable = enable; }
|
|
|
1254
1331
|
/** Set volume scale to apply to all sound, music and speech
|
|
1255
1332
|
* @param {Number} volume
|
|
1256
1333
|
* @memberof Settings */
|
|
1257
|
-
function setSoundVolume(volume)
|
|
1334
|
+
function setSoundVolume(volume)
|
|
1335
|
+
{
|
|
1336
|
+
soundVolume = volume;
|
|
1337
|
+
if (soundEnable && !headlessMode && audioGainNode)
|
|
1338
|
+
audioGainNode.gain.value = volume; // update gain immediatly
|
|
1339
|
+
}
|
|
1258
1340
|
|
|
1259
1341
|
/** Set default range where sound no longer plays
|
|
1260
1342
|
* @param {Number} range
|
|
@@ -1387,6 +1469,8 @@ class EngineObject
|
|
|
1387
1469
|
this.spawnTime = time;
|
|
1388
1470
|
/** @property {Array} - List of children of this object */
|
|
1389
1471
|
this.children = [];
|
|
1472
|
+
/** @property {Boolean} - Limit object speed using linear or circular math */
|
|
1473
|
+
this.clampSpeedLinear = true;
|
|
1390
1474
|
|
|
1391
1475
|
// parent child system
|
|
1392
1476
|
/** @property {EngineObject} - Parent of object if in local space */
|
|
@@ -1435,8 +1519,21 @@ class EngineObject
|
|
|
1435
1519
|
return;
|
|
1436
1520
|
|
|
1437
1521
|
// limit max speed to prevent missing collisions
|
|
1438
|
-
|
|
1439
|
-
|
|
1522
|
+
if (this.clampSpeedLinear)
|
|
1523
|
+
{
|
|
1524
|
+
this.velocity.x = clamp(this.velocity.x, -objectMaxSpeed, objectMaxSpeed);
|
|
1525
|
+
this.velocity.y = clamp(this.velocity.y, -objectMaxSpeed, objectMaxSpeed);
|
|
1526
|
+
}
|
|
1527
|
+
else
|
|
1528
|
+
{
|
|
1529
|
+
const length2 = this.velocity.lengthSquared();
|
|
1530
|
+
if (length2 > objectMaxSpeed*objectMaxSpeed)
|
|
1531
|
+
{
|
|
1532
|
+
const s = objectMaxSpeed / length2**.5;
|
|
1533
|
+
this.velocity.x *= s;
|
|
1534
|
+
this.velocity.y *= s;
|
|
1535
|
+
}
|
|
1536
|
+
}
|
|
1440
1537
|
|
|
1441
1538
|
// apply physics
|
|
1442
1539
|
const oldPos = this.pos.copy();
|
|
@@ -1492,7 +1589,7 @@ class EngineObject
|
|
|
1492
1589
|
if (o.mass) // push away if not fixed
|
|
1493
1590
|
o.velocity = o.velocity.subtract(velocity);
|
|
1494
1591
|
|
|
1495
|
-
debugOverlay && debugPhysics &&
|
|
1592
|
+
debugOverlay && debugPhysics && debugOverlap(this.pos, this.size, o.pos, o.size, '#f00');
|
|
1496
1593
|
continue;
|
|
1497
1594
|
}
|
|
1498
1595
|
|
|
@@ -1554,7 +1651,7 @@ class EngineObject
|
|
|
1554
1651
|
else // bounce if other object is fixed
|
|
1555
1652
|
this.velocity.x *= -elasticity;
|
|
1556
1653
|
}
|
|
1557
|
-
debugOverlay && debugPhysics &&
|
|
1654
|
+
debugOverlay && debugPhysics && debugOverlap(this.pos, this.size, o.pos, o.size, '#f0f');
|
|
1558
1655
|
}
|
|
1559
1656
|
}
|
|
1560
1657
|
if (this.collideTiles)
|
|
@@ -1615,6 +1712,22 @@ class EngineObject
|
|
|
1615
1712
|
for (const child of this.children)
|
|
1616
1713
|
child.destroy(child.parent = 0);
|
|
1617
1714
|
}
|
|
1715
|
+
|
|
1716
|
+
/** Convert from local space to world space
|
|
1717
|
+
* @param {Vector2} pos - local space point */
|
|
1718
|
+
localToWorld(pos) { return this.pos.add(pos.rotate(-this.angle)); }
|
|
1719
|
+
|
|
1720
|
+
/** Convert from world space to local space
|
|
1721
|
+
* @param {Vector2} pos - world space point */
|
|
1722
|
+
worldToLocal(pos) { return pos.subtract(this.pos).rotate(this.angle); }
|
|
1723
|
+
|
|
1724
|
+
/** Convert from local space to world space for a vector (rotation only)
|
|
1725
|
+
* @param {Vector2} vec - local space vector */
|
|
1726
|
+
localToWorldVector(vec) { return vec.rotate(this.angle); }
|
|
1727
|
+
|
|
1728
|
+
/** Convert from world space to local space for a vector (rotation only)
|
|
1729
|
+
* @param {Vector2} vec - world space vector */
|
|
1730
|
+
worldToLocalVector(vec) { return vec.rotate(-this.angle); }
|
|
1618
1731
|
|
|
1619
1732
|
/** Called to check if a tile collision should be resolved
|
|
1620
1733
|
* @param {Number} tileData - the value of the tile at the position
|
|
@@ -1701,6 +1814,18 @@ class EngineObject
|
|
|
1701
1814
|
return text;
|
|
1702
1815
|
}
|
|
1703
1816
|
}
|
|
1817
|
+
|
|
1818
|
+
/** Render debug info for this object */
|
|
1819
|
+
renderDebugInfo()
|
|
1820
|
+
{
|
|
1821
|
+
// show object info for debugging
|
|
1822
|
+
const size = vec2(max(this.size.x, .2), max(this.size.y, .2));
|
|
1823
|
+
const color1 = rgb(this.collideTiles?1:0, this.collideSolidObjects?1:0, this.isSolid?1:0, this.parent?.2:.5);
|
|
1824
|
+
const color2 = this.parent ? rgb(1,1,1,.5) : rgb(0,0,0,.8);
|
|
1825
|
+
drawRect(this.pos, size, color1, this.angle, false);
|
|
1826
|
+
drawRect(this.pos, size.scale(.8), color2, this.angle, false);
|
|
1827
|
+
this.parent && drawLine(this.pos, this.parent.pos, .1, rgb(0,0,1,.5), false);
|
|
1828
|
+
}
|
|
1704
1829
|
}
|
|
1705
1830
|
/**
|
|
1706
1831
|
* LittleJS Drawing System
|
|
@@ -2024,7 +2149,7 @@ function drawCanvas2D(pos, size, angle, mirror, drawFunction, screenSpace, conte
|
|
|
2024
2149
|
context.save();
|
|
2025
2150
|
context.translate(pos.x+.5, pos.y+.5);
|
|
2026
2151
|
context.rotate(angle);
|
|
2027
|
-
context.scale(mirror ? -size.x : size.x, size.y);
|
|
2152
|
+
context.scale(mirror ? -size.x : size.x, -size.y);
|
|
2028
2153
|
drawFunction(context);
|
|
2029
2154
|
context.restore();
|
|
2030
2155
|
}
|
|
@@ -2719,6 +2844,32 @@ function touchGamepadRender()
|
|
|
2719
2844
|
|
|
2720
2845
|
|
|
2721
2846
|
|
|
2847
|
+
/** Audio context used by the engine
|
|
2848
|
+
* @type {AudioContext}
|
|
2849
|
+
* @memberof Audio */
|
|
2850
|
+
let audioContext;
|
|
2851
|
+
|
|
2852
|
+
/** Master gain node for all audio to pass through
|
|
2853
|
+
* @type {GainNode}
|
|
2854
|
+
* @memberof Audio */
|
|
2855
|
+
let audioGainNode;
|
|
2856
|
+
|
|
2857
|
+
function audioInit()
|
|
2858
|
+
{
|
|
2859
|
+
if (!soundEnable || headlessMode) return;
|
|
2860
|
+
|
|
2861
|
+
// create audio context
|
|
2862
|
+
audioContext = new AudioContext;
|
|
2863
|
+
|
|
2864
|
+
// create and connect gain node
|
|
2865
|
+
// (createGain is more widely spported then GainNode construtor)
|
|
2866
|
+
audioGainNode = audioContext.createGain();
|
|
2867
|
+
audioGainNode.connect(audioContext.destination);
|
|
2868
|
+
setSoundVolume(soundVolume); // update gain volume
|
|
2869
|
+
}
|
|
2870
|
+
|
|
2871
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
2872
|
+
|
|
2722
2873
|
/**
|
|
2723
2874
|
* Sound Object - Stores a sound for later use and can be played positionally
|
|
2724
2875
|
*
|
|
@@ -2770,7 +2921,8 @@ class Sound
|
|
|
2770
2921
|
*/
|
|
2771
2922
|
play(pos, volume=1, pitch=1, randomnessScale=1, loop=false)
|
|
2772
2923
|
{
|
|
2773
|
-
if (!soundEnable ||
|
|
2924
|
+
if (!soundEnable || headlessMode) return;
|
|
2925
|
+
if (!this.sampleChannels) return;
|
|
2774
2926
|
|
|
2775
2927
|
let pan;
|
|
2776
2928
|
if (pos)
|
|
@@ -2793,7 +2945,17 @@ class Sound
|
|
|
2793
2945
|
|
|
2794
2946
|
// play the sound
|
|
2795
2947
|
const playbackRate = pitch + pitch * this.randomness*randomnessScale*rand(-1,1);
|
|
2796
|
-
|
|
2948
|
+
this.gainNode = audioContext.createGain();
|
|
2949
|
+
return this.source = playSamples(this.sampleChannels, volume, playbackRate, pan, loop, this.sampleRate, this.gainNode);
|
|
2950
|
+
}
|
|
2951
|
+
|
|
2952
|
+
/** Set the sound volume
|
|
2953
|
+
* @param {Number} [volume] - How much to scale volume by
|
|
2954
|
+
*/
|
|
2955
|
+
setVolume(volume=1)
|
|
2956
|
+
{
|
|
2957
|
+
if (this.gainNode)
|
|
2958
|
+
this.gainNode.gain.value = volume;
|
|
2797
2959
|
}
|
|
2798
2960
|
|
|
2799
2961
|
/** Stop the last instance of this sound that was played */
|
|
@@ -2847,16 +3009,14 @@ class SoundWave extends Sound
|
|
|
2847
3009
|
* @param {Number} [randomness] - How much to randomize frequency each time sound plays
|
|
2848
3010
|
* @param {Number} [range=soundDefaultRange] - World space max range of sound, will not play if camera is farther away
|
|
2849
3011
|
* @param {Number} [taper=soundDefaultTaper] - At what percentage of range should it start tapering off
|
|
3012
|
+
* @param {Function} [onloadCallback] - callback function to call when sound is loaded
|
|
2850
3013
|
*/
|
|
2851
|
-
constructor(filename, randomness=0, range, taper)
|
|
3014
|
+
constructor(filename, randomness=0, range, taper, onloadCallback)
|
|
2852
3015
|
{
|
|
2853
3016
|
super(undefined, range, taper);
|
|
2854
|
-
this.randomness = randomness;
|
|
2855
|
-
|
|
2856
3017
|
if (!soundEnable || headlessMode) return;
|
|
2857
|
-
if (!audioContext)
|
|
2858
|
-
audioContext = new AudioContext; // create audio context
|
|
2859
3018
|
|
|
3019
|
+
this.randomness = randomness;
|
|
2860
3020
|
fetch(filename)
|
|
2861
3021
|
.then(response => response.arrayBuffer())
|
|
2862
3022
|
.then(arrayBuffer => audioContext.decodeAudioData(arrayBuffer))
|
|
@@ -2866,10 +3026,23 @@ class SoundWave extends Sound
|
|
|
2866
3026
|
for (let i = audioBuffer.numberOfChannels; i--;)
|
|
2867
3027
|
this.sampleChannels[i] = Array.from(audioBuffer.getChannelData(i));
|
|
2868
3028
|
this.sampleRate = audioBuffer.sampleRate;
|
|
2869
|
-
});
|
|
3029
|
+
}).then(() => onloadCallback && onloadCallback(this));
|
|
2870
3030
|
}
|
|
2871
3031
|
}
|
|
2872
3032
|
|
|
3033
|
+
/** Play an mp3, ogg, or wav audio from a local file or url
|
|
3034
|
+
* @param {String} filename - Location of sound file to play
|
|
3035
|
+
* @param {Number} [volume] - How much to scale volume by
|
|
3036
|
+
* @param {Boolean} [loop] - True if the music should loop
|
|
3037
|
+
* @return {SoundWave} - The sound object for this file
|
|
3038
|
+
* @memberof Audio */
|
|
3039
|
+
function playAudioFile(filename, volume=1, loop=false)
|
|
3040
|
+
{
|
|
3041
|
+
if (!soundEnable || headlessMode) return;
|
|
3042
|
+
|
|
3043
|
+
return new SoundWave(filename,0,0,0, s=>s.play(undefined, volume, 1, 1, loop));
|
|
3044
|
+
}
|
|
3045
|
+
|
|
2873
3046
|
/**
|
|
2874
3047
|
* Music Object - Stores a zzfx music track for later use
|
|
2875
3048
|
*
|
|
@@ -2924,23 +3097,6 @@ class Music extends Sound
|
|
|
2924
3097
|
{ return super.play(undefined, volume, 1, 1, loop); }
|
|
2925
3098
|
}
|
|
2926
3099
|
|
|
2927
|
-
/** Play an mp3, ogg, or wav audio from a local file or url
|
|
2928
|
-
* @param {String} filename - Location of sound file to play
|
|
2929
|
-
* @param {Number} [volume] - How much to scale volume by
|
|
2930
|
-
* @param {Boolean} [loop] - True if the music should loop
|
|
2931
|
-
* @return {HTMLAudioElement} - The audio element for this sound
|
|
2932
|
-
* @memberof Audio */
|
|
2933
|
-
function playAudioFile(filename, volume=1, loop=false)
|
|
2934
|
-
{
|
|
2935
|
-
if (!soundEnable || headlessMode) return;
|
|
2936
|
-
|
|
2937
|
-
const audio = new Audio(filename);
|
|
2938
|
-
audio.volume = soundVolume * volume;
|
|
2939
|
-
audio.loop = loop;
|
|
2940
|
-
audio.play();
|
|
2941
|
-
return audio;
|
|
2942
|
-
}
|
|
2943
|
-
|
|
2944
3100
|
/** Speak text with passed in settings
|
|
2945
3101
|
* @param {String} text - The text to speak
|
|
2946
3102
|
* @param {String} [language] - The language/accent to use (examples: en, it, ru, ja, zh)
|
|
@@ -2951,7 +3107,8 @@ function playAudioFile(filename, volume=1, loop=false)
|
|
|
2951
3107
|
* @memberof Audio */
|
|
2952
3108
|
function speak(text, language='', volume=1, rate=1, pitch=1)
|
|
2953
3109
|
{
|
|
2954
|
-
if (!soundEnable ||
|
|
3110
|
+
if (!soundEnable || headlessMode) return;
|
|
3111
|
+
if (!speechSynthesis) return;
|
|
2955
3112
|
|
|
2956
3113
|
// common languages (not supported by all browsers)
|
|
2957
3114
|
// en - english, it - italian, fr - french, de - german, es - spanish
|
|
@@ -2981,30 +3138,23 @@ function getNoteFrequency(semitoneOffset, rootFrequency=220)
|
|
|
2981
3138
|
|
|
2982
3139
|
///////////////////////////////////////////////////////////////////////////////
|
|
2983
3140
|
|
|
2984
|
-
|
|
2985
|
-
|
|
2986
|
-
* @memberof Audio */
|
|
2987
|
-
let audioContext;
|
|
2988
|
-
|
|
2989
|
-
/** Keep track if audio was suspended when last sound was played
|
|
2990
|
-
* @type {Boolean}
|
|
2991
|
-
* @memberof Audio */
|
|
3141
|
+
// internal tracking if audio was suspended when last sound was played
|
|
3142
|
+
// allows first suspended sound to play when audio is resumed
|
|
2992
3143
|
let audioSuspended = false;
|
|
2993
3144
|
|
|
2994
3145
|
/** Play cached audio samples with given settings
|
|
2995
|
-
* @param {Array}
|
|
2996
|
-
* @param {Number}
|
|
2997
|
-
* @param {Number}
|
|
2998
|
-
* @param {Number}
|
|
2999
|
-
* @param {Boolean}
|
|
3000
|
-
* @param {Number}
|
|
3146
|
+
* @param {Array} sampleChannels - Array of arrays of samples to play (for stereo playback)
|
|
3147
|
+
* @param {Number} [volume] - How much to scale volume by
|
|
3148
|
+
* @param {Number} [rate] - The playback rate to use
|
|
3149
|
+
* @param {Number} [pan] - How much to apply stereo panning
|
|
3150
|
+
* @param {Boolean} [loop] - True if the sound should loop when it reaches the end
|
|
3151
|
+
* @param {Number} [sampleRate=44100] - Sample rate for the sound
|
|
3152
|
+
* @param {GainNode} [gainNode] - Optional gain node for volume control while playing
|
|
3001
3153
|
* @return {AudioBufferSourceNode} - The audio node of the sound played
|
|
3002
3154
|
* @memberof Audio */
|
|
3003
|
-
function playSamples(sampleChannels, volume=1, rate=1, pan=0, loop=false, sampleRate=zzfxR)
|
|
3155
|
+
function playSamples(sampleChannels, volume=1, rate=1, pan=0, loop=false, sampleRate=zzfxR, gainNode)
|
|
3004
3156
|
{
|
|
3005
3157
|
if (!soundEnable || headlessMode) return;
|
|
3006
|
-
if (!audioContext)
|
|
3007
|
-
audioContext = new AudioContext; // create audio context
|
|
3008
3158
|
|
|
3009
3159
|
// prevent sounds from building up if they can't be played
|
|
3010
3160
|
const audioWasSuspended = audioSuspended;
|
|
@@ -3028,10 +3178,10 @@ function playSamples(sampleChannels, volume=1, rate=1, pan=0, loop=false, sample
|
|
|
3028
3178
|
source.playbackRate.value = rate;
|
|
3029
3179
|
source.loop = loop;
|
|
3030
3180
|
|
|
3031
|
-
// create and connect gain node
|
|
3032
|
-
|
|
3033
|
-
gainNode.gain.value =
|
|
3034
|
-
gainNode.connect(
|
|
3181
|
+
// create and connect gain node
|
|
3182
|
+
gainNode = gainNode || audioContext.createGain();
|
|
3183
|
+
gainNode.gain.value = volume;
|
|
3184
|
+
gainNode.connect(audioGainNode);
|
|
3035
3185
|
|
|
3036
3186
|
// connect source to stereo panner and gain
|
|
3037
3187
|
source.connect(new StereoPannerNode(audioContext, {'pan':clamp(pan, -1, 1)})).connect(gainNode);
|
|
@@ -3938,6 +4088,9 @@ class Particle extends EngineObject
|
|
|
3938
4088
|
this.localSpaceEmitter = localSpaceEmitter;
|
|
3939
4089
|
/** @property {Function} - Called when particle dies */
|
|
3940
4090
|
this.destroyCallback = destroyCallback;
|
|
4091
|
+
|
|
4092
|
+
// particles use circular clamped speed
|
|
4093
|
+
this.clampSpeedLinear = false;
|
|
3941
4094
|
}
|
|
3942
4095
|
|
|
3943
4096
|
/** Render the particle, automatically called each frame, sorted by renderOrder */
|
|
@@ -4007,9 +4160,9 @@ class Particle extends EngineObject
|
|
|
4007
4160
|
|
|
4008
4161
|
|
|
4009
4162
|
/** List of all medals
|
|
4010
|
-
* @type {
|
|
4163
|
+
* @type {Object}
|
|
4011
4164
|
* @memberof Medals */
|
|
4012
|
-
const medals =
|
|
4165
|
+
const medals = {};
|
|
4013
4166
|
|
|
4014
4167
|
// Engine internal variables not exposed to documentation
|
|
4015
4168
|
let medalsDisplayQueue = [], medalsSaveName, medalsDisplayTimeLast;
|
|
@@ -4025,9 +4178,45 @@ function medalsInit(saveName)
|
|
|
4025
4178
|
{
|
|
4026
4179
|
// check if medals are unlocked
|
|
4027
4180
|
medalsSaveName = saveName;
|
|
4028
|
-
|
|
4181
|
+
if (!debugMedals)
|
|
4182
|
+
medalsForEach(medal=> medal.unlocked = (localStorage[medal.storageKey()] | 0));
|
|
4183
|
+
|
|
4184
|
+
// engine automatically renders medals
|
|
4185
|
+
addPluginRender(function()
|
|
4186
|
+
{
|
|
4187
|
+
if (!medalsDisplayQueue.length)
|
|
4188
|
+
return;
|
|
4189
|
+
|
|
4190
|
+
// update first medal in queue
|
|
4191
|
+
const medal = medalsDisplayQueue[0];
|
|
4192
|
+
const time = timeReal - medalsDisplayTimeLast;
|
|
4193
|
+
if (!medalsDisplayTimeLast)
|
|
4194
|
+
medalsDisplayTimeLast = timeReal;
|
|
4195
|
+
else if (time > medalDisplayTime)
|
|
4196
|
+
{
|
|
4197
|
+
medalsDisplayTimeLast = 0;
|
|
4198
|
+
medalsDisplayQueue.shift();
|
|
4199
|
+
}
|
|
4200
|
+
else
|
|
4201
|
+
{
|
|
4202
|
+
// slide on/off medals
|
|
4203
|
+
const slideOffTime = medalDisplayTime - medalDisplaySlideTime;
|
|
4204
|
+
const hidePercent =
|
|
4205
|
+
time < medalDisplaySlideTime ? 1 - time / medalDisplaySlideTime :
|
|
4206
|
+
time > slideOffTime ? (time - slideOffTime) / medalDisplaySlideTime : 0;
|
|
4207
|
+
medal.render(hidePercent);
|
|
4208
|
+
}
|
|
4209
|
+
});
|
|
4029
4210
|
}
|
|
4030
4211
|
|
|
4212
|
+
/** Calls a function for each medal
|
|
4213
|
+
* @param {Function} callback
|
|
4214
|
+
* @memberof Medals */
|
|
4215
|
+
function medalsForEach(callback)
|
|
4216
|
+
{ Object.values(medals).forEach(medal=>callback(medal)); }
|
|
4217
|
+
|
|
4218
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
4219
|
+
|
|
4031
4220
|
/**
|
|
4032
4221
|
* Medal - Tracks an unlockable medal
|
|
4033
4222
|
* @example
|
|
@@ -4072,7 +4261,6 @@ class Medal
|
|
|
4072
4261
|
ASSERT(medalsSaveName, 'save name must be set');
|
|
4073
4262
|
localStorage[this.storageKey()] = this.unlocked = 1;
|
|
4074
4263
|
medalsDisplayQueue.push(this);
|
|
4075
|
-
newgrounds && newgrounds.unlockMedal(this.id);
|
|
4076
4264
|
}
|
|
4077
4265
|
|
|
4078
4266
|
/** Render a medal
|
|
@@ -4120,173 +4308,6 @@ class Medal
|
|
|
4120
4308
|
|
|
4121
4309
|
// Get local storage key used by the medal
|
|
4122
4310
|
storageKey() { return medalsSaveName + '_' + this.id; }
|
|
4123
|
-
}
|
|
4124
|
-
|
|
4125
|
-
// engine automatically renders medals
|
|
4126
|
-
function medalsRender()
|
|
4127
|
-
{
|
|
4128
|
-
if (!medalsDisplayQueue.length)
|
|
4129
|
-
return;
|
|
4130
|
-
|
|
4131
|
-
// update first medal in queue
|
|
4132
|
-
const medal = medalsDisplayQueue[0];
|
|
4133
|
-
const time = timeReal - medalsDisplayTimeLast;
|
|
4134
|
-
if (!medalsDisplayTimeLast)
|
|
4135
|
-
medalsDisplayTimeLast = timeReal;
|
|
4136
|
-
else if (time > medalDisplayTime)
|
|
4137
|
-
{
|
|
4138
|
-
medalsDisplayTimeLast = 0;
|
|
4139
|
-
medalsDisplayQueue.shift();
|
|
4140
|
-
}
|
|
4141
|
-
else
|
|
4142
|
-
{
|
|
4143
|
-
// slide on/off medals
|
|
4144
|
-
const slideOffTime = medalDisplayTime - medalDisplaySlideTime;
|
|
4145
|
-
const hidePercent =
|
|
4146
|
-
time < medalDisplaySlideTime ? 1 - time / medalDisplaySlideTime :
|
|
4147
|
-
time > slideOffTime ? (time - slideOffTime) / medalDisplaySlideTime : 0;
|
|
4148
|
-
medal.render(hidePercent);
|
|
4149
|
-
}
|
|
4150
|
-
}
|
|
4151
|
-
|
|
4152
|
-
///////////////////////////////////////////////////////////////////////////////
|
|
4153
|
-
|
|
4154
|
-
// global Newgrounds object
|
|
4155
|
-
let newgrounds;
|
|
4156
|
-
|
|
4157
|
-
/** This can used to enable Newgrounds functionality
|
|
4158
|
-
* @param {Number} app_id - The newgrounds App ID
|
|
4159
|
-
* @param {String} [cipher] - The encryption Key (AES-128/Base64)
|
|
4160
|
-
* @param {Object} [cryptoJS] - An instance of CryptoJS, if there is a cipher
|
|
4161
|
-
* @memberof Medals */
|
|
4162
|
-
function newgroundsInit(app_id, cipher, cryptoJS)
|
|
4163
|
-
{ newgrounds = new Newgrounds(app_id, cipher, cryptoJS); }
|
|
4164
|
-
|
|
4165
|
-
/**
|
|
4166
|
-
* Newgrounds API wrapper object
|
|
4167
|
-
* @example
|
|
4168
|
-
* // create a newgrounds object, replace the app id with your own
|
|
4169
|
-
* const app_id = '53123:1ZuSTQ9l';
|
|
4170
|
-
* newgrounds = new Newgrounds(app_id);
|
|
4171
|
-
*/
|
|
4172
|
-
class Newgrounds
|
|
4173
|
-
{
|
|
4174
|
-
/** Create a newgrounds object
|
|
4175
|
-
* @param {Number} app_id - The newgrounds App ID
|
|
4176
|
-
* @param {String} [cipher] - The encryption Key (AES-128/Base64)
|
|
4177
|
-
* @param {Object} [cryptoJS] - An instance of CryptoJS, if there is a cipher */
|
|
4178
|
-
constructor(app_id, cipher, cryptoJS)
|
|
4179
|
-
{
|
|
4180
|
-
ASSERT(!newgrounds && app_id>0, 'there can only be one newgrounds object');
|
|
4181
|
-
ASSERT(!cipher || cryptoJS, 'must provide cryptojs if there is a cipher');
|
|
4182
|
-
|
|
4183
|
-
this.app_id = app_id;
|
|
4184
|
-
this.cipher = cipher;
|
|
4185
|
-
this.cryptoJS = cryptoJS;
|
|
4186
|
-
this.host = location ? location.hostname : '';
|
|
4187
|
-
|
|
4188
|
-
// get session id from url search params
|
|
4189
|
-
const url = new URL(location.href);
|
|
4190
|
-
this.session_id = url.searchParams.get('ngio_session_id');
|
|
4191
|
-
|
|
4192
|
-
if (!this.session_id)
|
|
4193
|
-
return; // only use newgrounds when logged in
|
|
4194
|
-
|
|
4195
|
-
// get medals
|
|
4196
|
-
const medalsResult = this.call('Medal.getList');
|
|
4197
|
-
this.medals = medalsResult ? medalsResult.result.data['medals'] : [];
|
|
4198
|
-
debugMedals && console.log(this.medals);
|
|
4199
|
-
for (const newgroundsMedal of this.medals)
|
|
4200
|
-
{
|
|
4201
|
-
const medal = medals[newgroundsMedal['id']];
|
|
4202
|
-
if (medal)
|
|
4203
|
-
{
|
|
4204
|
-
// copy newgrounds medal data
|
|
4205
|
-
medal.image = new Image;
|
|
4206
|
-
medal.image.src = newgroundsMedal['icon'];
|
|
4207
|
-
medal.name = newgroundsMedal['name'];
|
|
4208
|
-
medal.description = newgroundsMedal['description'];
|
|
4209
|
-
medal.unlocked = newgroundsMedal['unlocked'];
|
|
4210
|
-
medal.difficulty = newgroundsMedal['difficulty'];
|
|
4211
|
-
medal.value = newgroundsMedal['value'];
|
|
4212
|
-
|
|
4213
|
-
if (medal.value)
|
|
4214
|
-
medal.description = medal.description + ' (' + medal.value + ')';
|
|
4215
|
-
}
|
|
4216
|
-
}
|
|
4217
|
-
|
|
4218
|
-
// get scoreboards
|
|
4219
|
-
const scoreboardResult = this.call('ScoreBoard.getBoards');
|
|
4220
|
-
this.scoreboards = scoreboardResult ? scoreboardResult.result.data.scoreboards : [];
|
|
4221
|
-
debugMedals && console.log(this.scoreboards);
|
|
4222
|
-
|
|
4223
|
-
const keepAliveMS = 5 * 60 * 1e3;
|
|
4224
|
-
setInterval(()=>this.call('Gateway.ping', 0, true), keepAliveMS);
|
|
4225
|
-
}
|
|
4226
|
-
|
|
4227
|
-
/** Send message to unlock a medal by id
|
|
4228
|
-
* @param {Number} id - The medal id */
|
|
4229
|
-
unlockMedal(id) { return this.call('Medal.unlock', {'id':id}, true); }
|
|
4230
|
-
|
|
4231
|
-
/** Send message to post score
|
|
4232
|
-
* @param {Number} id - The scoreboard id
|
|
4233
|
-
* @param {Number} value - The score value */
|
|
4234
|
-
postScore(id, value) { return this.call('ScoreBoard.postScore', {'id':id, 'value':value}, true); }
|
|
4235
|
-
|
|
4236
|
-
/** Get scores from a scoreboard
|
|
4237
|
-
* @param {Number} id - The scoreboard id
|
|
4238
|
-
* @param {String} [user] - A user's id or name
|
|
4239
|
-
* @param {Number} [social] - If true, only social scores will be loaded
|
|
4240
|
-
* @param {Number} [skip] - Number of scores to skip before start
|
|
4241
|
-
* @param {Number} [limit] - Number of scores to include in the list
|
|
4242
|
-
* @return {Object} - The response JSON object
|
|
4243
|
-
*/
|
|
4244
|
-
getScores(id, user, social=0, skip=0, limit=10)
|
|
4245
|
-
{ return this.call('ScoreBoard.getScores', {'id':id, 'user':user, 'social':social, 'skip':skip, 'limit':limit}); }
|
|
4246
|
-
|
|
4247
|
-
/** Send message to log a view */
|
|
4248
|
-
logView() { return this.call('App.logView', {'host':this.host}, true); }
|
|
4249
|
-
|
|
4250
|
-
/** Send a message to call a component of the Newgrounds API
|
|
4251
|
-
* @param {String} component - Name of the component
|
|
4252
|
-
* @param {Object} [parameters] - Parameters to use for call
|
|
4253
|
-
* @param {Boolean} [async] - If true, don't wait for response before continuing
|
|
4254
|
-
* @return {Object} - The response JSON object
|
|
4255
|
-
*/
|
|
4256
|
-
call(component, parameters, async=false)
|
|
4257
|
-
{
|
|
4258
|
-
const call = {'component':component, 'parameters':parameters};
|
|
4259
|
-
if (this.cipher)
|
|
4260
|
-
{
|
|
4261
|
-
// encrypt using AES-128 Base64 with cryptoJS
|
|
4262
|
-
const cryptoJS = this.cryptoJS;
|
|
4263
|
-
const aesKey = cryptoJS['enc']['Base64']['parse'](this.cipher);
|
|
4264
|
-
const iv = cryptoJS['lib']['WordArray']['random'](16);
|
|
4265
|
-
const encrypted = cryptoJS['AES']['encrypt'](JSON.stringify(call), aesKey, {'iv':iv});
|
|
4266
|
-
call['secure'] = cryptoJS['enc']['Base64']['stringify'](iv.concat(encrypted['ciphertext']));
|
|
4267
|
-
call['parameters'] = 0;
|
|
4268
|
-
}
|
|
4269
|
-
|
|
4270
|
-
// build the input object
|
|
4271
|
-
const input =
|
|
4272
|
-
{
|
|
4273
|
-
'app_id': this.app_id,
|
|
4274
|
-
'session_id': this.session_id,
|
|
4275
|
-
'call': call
|
|
4276
|
-
};
|
|
4277
|
-
|
|
4278
|
-
// build post data
|
|
4279
|
-
const formData = new FormData();
|
|
4280
|
-
formData.append('input', JSON.stringify(input));
|
|
4281
|
-
|
|
4282
|
-
// send post data
|
|
4283
|
-
const xmlHttp = new XMLHttpRequest();
|
|
4284
|
-
const url = 'https://newgrounds.io/gateway_v3.php';
|
|
4285
|
-
xmlHttp.open('POST', url, !debugMedals && async);
|
|
4286
|
-
xmlHttp.send(formData);
|
|
4287
|
-
debugMedals && console.log(xmlHttp.responseText);
|
|
4288
|
-
return xmlHttp.responseText && JSON.parse(xmlHttp.responseText);
|
|
4289
|
-
}
|
|
4290
4311
|
}
|
|
4291
4312
|
/**
|
|
4292
4313
|
* LittleJS WebGL Interface
|
|
@@ -4377,7 +4398,7 @@ function glPreRender()
|
|
|
4377
4398
|
|
|
4378
4399
|
// clear and set to same size as main canvas
|
|
4379
4400
|
glContext.viewport(0, 0, glCanvas.width=mainCanvas.width, glCanvas.height=mainCanvas.height);
|
|
4380
|
-
|
|
4401
|
+
glContext.clear(gl_COLOR_BUFFER_BIT);
|
|
4381
4402
|
|
|
4382
4403
|
// set up the shader
|
|
4383
4404
|
glContext.useProgram(glShader);
|
|
@@ -4560,99 +4581,6 @@ function glDraw(x, y, sizeX, sizeY, angle, uv0X, uv0Y, uv1X, uv1Y, rgba, rgbaAdd
|
|
|
4560
4581
|
glInstanceCount++;
|
|
4561
4582
|
}
|
|
4562
4583
|
|
|
4563
|
-
///////////////////////////////////////////////////////////////////////////////
|
|
4564
|
-
// post processing - can be enabled to pass other canvases through a final shader
|
|
4565
|
-
|
|
4566
|
-
let glPostShader, glPostTexture, glPostIncludeOverlay;
|
|
4567
|
-
|
|
4568
|
-
/** Set up a post processing shader
|
|
4569
|
-
* @param {String} shaderCode
|
|
4570
|
-
* @param {Boolean} includeOverlay
|
|
4571
|
-
* @memberof WebGL */
|
|
4572
|
-
function glInitPostProcess(shaderCode, includeOverlay=false)
|
|
4573
|
-
{
|
|
4574
|
-
ASSERT(!glPostShader, 'can only have 1 post effects shader');
|
|
4575
|
-
if (headlessMode) return;
|
|
4576
|
-
if (!shaderCode) // default shader pass through
|
|
4577
|
-
shaderCode = 'void mainImage(out vec4 c,vec2 p){c=texture(iChannel0,p/iResolution.xy);}';
|
|
4578
|
-
|
|
4579
|
-
// create the shader
|
|
4580
|
-
glPostShader = glCreateProgram(
|
|
4581
|
-
'#version 300 es\n' + // specify GLSL ES version
|
|
4582
|
-
'precision highp float;'+ // use highp for better accuracy
|
|
4583
|
-
'in vec2 p;'+ // position
|
|
4584
|
-
'void main(){'+ // shader entry point
|
|
4585
|
-
'gl_Position=vec4(p+p-1.,1,1);'+ // set position
|
|
4586
|
-
'}' // end of shader
|
|
4587
|
-
,
|
|
4588
|
-
'#version 300 es\n' + // specify GLSL ES version
|
|
4589
|
-
'precision highp float;'+ // use highp for better accuracy
|
|
4590
|
-
'uniform sampler2D iChannel0;'+ // input texture
|
|
4591
|
-
'uniform vec3 iResolution;'+ // size of output texture
|
|
4592
|
-
'uniform float iTime;'+ // time
|
|
4593
|
-
'out vec4 c;'+ // out color
|
|
4594
|
-
'\n' + shaderCode + '\n'+ // insert custom shader code
|
|
4595
|
-
'void main(){'+ // shader entry point
|
|
4596
|
-
'mainImage(c,gl_FragCoord.xy);'+ // call post process function
|
|
4597
|
-
'c.a=1.;'+ // always use full alpha
|
|
4598
|
-
'}' // end of shader
|
|
4599
|
-
);
|
|
4600
|
-
|
|
4601
|
-
// create buffer and texture
|
|
4602
|
-
glPostTexture = glCreateTexture(undefined);
|
|
4603
|
-
glPostIncludeOverlay = includeOverlay;
|
|
4604
|
-
|
|
4605
|
-
// hide the original 2d canvas
|
|
4606
|
-
mainCanvas.style.visibility = 'hidden';
|
|
4607
|
-
if (glPostIncludeOverlay)
|
|
4608
|
-
overlayCanvas.style.visibility = 'hidden';
|
|
4609
|
-
}
|
|
4610
|
-
|
|
4611
|
-
// Render the post processing shader, called automatically by the engine
|
|
4612
|
-
function glRenderPostProcess()
|
|
4613
|
-
{
|
|
4614
|
-
if (!glPostShader || headlessMode) return;
|
|
4615
|
-
|
|
4616
|
-
// prepare to render post process shader
|
|
4617
|
-
if (glEnable)
|
|
4618
|
-
{
|
|
4619
|
-
glFlush(); // clear out the buffer
|
|
4620
|
-
mainContext.drawImage(glCanvas, 0, 0); // copy to the main canvas
|
|
4621
|
-
}
|
|
4622
|
-
else
|
|
4623
|
-
{
|
|
4624
|
-
// set the viewport
|
|
4625
|
-
glContext.viewport(0, 0, glCanvas.width = mainCanvas.width, glCanvas.height = mainCanvas.height);
|
|
4626
|
-
}
|
|
4627
|
-
|
|
4628
|
-
// copy overlay canvas so it will be included in post processing
|
|
4629
|
-
glPostIncludeOverlay && mainContext.drawImage(overlayCanvas, 0, 0);
|
|
4630
|
-
|
|
4631
|
-
// setup shader program to draw one triangle
|
|
4632
|
-
glContext.useProgram(glPostShader);
|
|
4633
|
-
glContext.bindBuffer(gl_ARRAY_BUFFER, glGeometryBuffer);
|
|
4634
|
-
glContext.pixelStorei(gl_UNPACK_FLIP_Y_WEBGL, 1);
|
|
4635
|
-
glContext.disable(gl_BLEND);
|
|
4636
|
-
|
|
4637
|
-
// set textures, pass in the 2d canvas and gl canvas in separate texture channels
|
|
4638
|
-
glContext.activeTexture(gl_TEXTURE0);
|
|
4639
|
-
glContext.bindTexture(gl_TEXTURE_2D, glPostTexture);
|
|
4640
|
-
glContext.texImage2D(gl_TEXTURE_2D, 0, gl_RGBA, gl_RGBA, gl_UNSIGNED_BYTE, mainCanvas);
|
|
4641
|
-
|
|
4642
|
-
// set vertex position attribute
|
|
4643
|
-
const vertexByteStride = 8;
|
|
4644
|
-
const pLocation = glContext.getAttribLocation(glPostShader, 'p');
|
|
4645
|
-
glContext.enableVertexAttribArray(pLocation);
|
|
4646
|
-
glContext.vertexAttribPointer(pLocation, 2, gl_FLOAT, false, vertexByteStride, 0);
|
|
4647
|
-
|
|
4648
|
-
// set uniforms and draw
|
|
4649
|
-
const uniformLocation = (name)=>glContext.getUniformLocation(glPostShader, name);
|
|
4650
|
-
glContext.uniform1i(uniformLocation('iChannel0'), 0);
|
|
4651
|
-
glContext.uniform1f(uniformLocation('iTime'), time);
|
|
4652
|
-
glContext.uniform3f(uniformLocation('iResolution'), mainCanvas.width, mainCanvas.height, 1);
|
|
4653
|
-
glContext.drawArrays(gl_TRIANGLE_STRIP, 0, 4);
|
|
4654
|
-
}
|
|
4655
|
-
|
|
4656
4584
|
///////////////////////////////////////////////////////////////////////////////
|
|
4657
4585
|
// store gl constants as integers so their name doesn't use space in minifed
|
|
4658
4586
|
const
|
|
@@ -4717,7 +4645,7 @@ const engineName = 'LittleJS';
|
|
|
4717
4645
|
* @type {String}
|
|
4718
4646
|
* @default
|
|
4719
4647
|
* @memberof Engine */
|
|
4720
|
-
const engineVersion = '1.9.
|
|
4648
|
+
const engineVersion = '1.9.8';
|
|
4721
4649
|
|
|
4722
4650
|
/** Frames per second to update
|
|
4723
4651
|
* @type {Number}
|
|
@@ -4771,6 +4699,22 @@ function setPaused(isPaused) { paused = isPaused; }
|
|
|
4771
4699
|
let frameTimeLastMS = 0, frameTimeBufferMS = 0, averageFPS = 0;
|
|
4772
4700
|
|
|
4773
4701
|
///////////////////////////////////////////////////////////////////////////////
|
|
4702
|
+
// plugin hooks
|
|
4703
|
+
|
|
4704
|
+
const pluginUpdateList = [], pluginRenderList = [];
|
|
4705
|
+
|
|
4706
|
+
/** Add a new update function for a plugin
|
|
4707
|
+
* @param {Function} updateFunction
|
|
4708
|
+
* @memberof Engine */
|
|
4709
|
+
function addPluginUpdate(updateFunction) { pluginUpdateList.push(updateFunction); }
|
|
4710
|
+
|
|
4711
|
+
/** Add a new render function for a plugin
|
|
4712
|
+
* @param {Function} renderFunction
|
|
4713
|
+
* @memberof Engine */
|
|
4714
|
+
function addPluginRender(renderFunction) { pluginRenderList.push(renderFunction); }
|
|
4715
|
+
|
|
4716
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
4717
|
+
// Main engine functions
|
|
4774
4718
|
|
|
4775
4719
|
/** Startup LittleJS engine with your callback functions
|
|
4776
4720
|
* @param {Function} gameInit - Called once after the engine starts up, setup the game
|
|
@@ -4846,6 +4790,7 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
4846
4790
|
// update game and objects
|
|
4847
4791
|
inputUpdate();
|
|
4848
4792
|
gameUpdate();
|
|
4793
|
+
pluginUpdateList.forEach(f=>f());
|
|
4849
4794
|
engineObjectsUpdate();
|
|
4850
4795
|
|
|
4851
4796
|
// do post update
|
|
@@ -4867,8 +4812,7 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
4867
4812
|
for (const o of engineObjects)
|
|
4868
4813
|
o.destroyed || o.render();
|
|
4869
4814
|
gameRenderPost();
|
|
4870
|
-
|
|
4871
|
-
medalsRender();
|
|
4815
|
+
pluginRenderList.forEach(f=>f());
|
|
4872
4816
|
touchGamepadRender();
|
|
4873
4817
|
debugRender();
|
|
4874
4818
|
glCopyToContext(mainContext);
|
|
@@ -4950,6 +4894,7 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
4950
4894
|
|
|
4951
4895
|
// init stuff and start engine
|
|
4952
4896
|
inputInit();
|
|
4897
|
+
audioInit();
|
|
4953
4898
|
debugInit();
|
|
4954
4899
|
glInit();
|
|
4955
4900
|
|
|
@@ -5157,21 +5102,23 @@ function drawEngineSplashScreen(t)
|
|
|
5157
5102
|
x.setLineDash([99*p2,99]);
|
|
5158
5103
|
|
|
5159
5104
|
// cab top
|
|
5160
|
-
rect(7,
|
|
5161
|
-
rect(7,
|
|
5162
|
-
rect(25,
|
|
5163
|
-
rect(25,
|
|
5164
|
-
rect(25,
|
|
5105
|
+
rect(7,16,18,-8,color(2,2));
|
|
5106
|
+
rect(7,8,18,4,color(2,3));
|
|
5107
|
+
rect(25,8,8,8,color(2,1));
|
|
5108
|
+
rect(25,8,-18,8);
|
|
5109
|
+
rect(25,8,8,8);
|
|
5165
5110
|
|
|
5166
5111
|
// cab
|
|
5167
|
-
rect(25,
|
|
5168
|
-
rect(11,
|
|
5169
|
-
rect(11,
|
|
5170
|
-
rect(11,
|
|
5171
|
-
rect(
|
|
5172
|
-
|
|
5173
|
-
|
|
5174
|
-
rect(
|
|
5112
|
+
rect(25,16,7,23,color());
|
|
5113
|
+
rect(11,39,14,-23,color(1,1));
|
|
5114
|
+
rect(11,16,14,18,color(1,2));
|
|
5115
|
+
rect(11,16,14,8,color(1,3));
|
|
5116
|
+
rect(25,16,-14,24);
|
|
5117
|
+
|
|
5118
|
+
// cab window
|
|
5119
|
+
rect(15,29,6,-9,color(2,2));
|
|
5120
|
+
circle(15,21,5,0,PI/2,color(2,4),1);
|
|
5121
|
+
rect(21,21,-6,9);
|
|
5175
5122
|
|
|
5176
5123
|
// little stack
|
|
5177
5124
|
rect(37,14,9,6,color(3,2));
|
|
@@ -5179,16 +5126,16 @@ function drawEngineSplashScreen(t)
|
|
|
5179
5126
|
rect(37,14,9,6);
|
|
5180
5127
|
|
|
5181
5128
|
// big stack
|
|
5182
|
-
rect(50,20,10,-8,color(0,1))
|
|
5183
|
-
rect(50,20,6.5,-8,color(0,2))
|
|
5184
|
-
rect(50,20,3.5,-8,color(0,3))
|
|
5185
|
-
rect(50,20,10,-8)
|
|
5186
|
-
circle(55,2,11.4,.5,PI-.5,color(3,3))
|
|
5187
|
-
circle(55,2,11.4,.5,PI/2,color(3,2),1)
|
|
5188
|
-
circle(55,2,11.4,.5,PI-.5)
|
|
5189
|
-
rect(45,7,20,-7,color(0,2))
|
|
5190
|
-
rect(45,-1,20,4,color(0,3))
|
|
5191
|
-
rect(45,-1,20,8)
|
|
5129
|
+
rect(50,20,10,-8,color(0,1));
|
|
5130
|
+
rect(50,20,6.5,-8,color(0,2));
|
|
5131
|
+
rect(50,20,3.5,-8,color(0,3));
|
|
5132
|
+
rect(50,20,10,-8);
|
|
5133
|
+
circle(55,2,11.4,.5,PI-.5,color(3,3));
|
|
5134
|
+
circle(55,2,11.4,.5,PI/2,color(3,2),1);
|
|
5135
|
+
circle(55,2,11.4,.5,PI-.5);
|
|
5136
|
+
rect(45,7,20,-7,color(0,2));
|
|
5137
|
+
rect(45,-1,20,4,color(0,3));
|
|
5138
|
+
rect(45,-1,20,8);
|
|
5192
5139
|
|
|
5193
5140
|
// engine
|
|
5194
5141
|
for (let i=5; i--;)
|