littlejsengine 1.3.5 → 1.3.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/README.md +7 -4
- package/engine/engine.all.js +79 -67
- package/engine/engine.all.min.js +1 -1
- package/engine/engine.all.release.js +81 -69
- package/engine/engine.js +30 -27
- package/engine/engineDraw.js +10 -2
- package/engine/engineInput.js +1 -1
- package/engine/engineRelease.js +2 -2
- package/engine/engineSettings.js +2 -1
- package/engine/engineUtilities.js +36 -36
- package/engine/index.d.ts +11 -8
- package/examples/breakout/index.html +3 -3
- package/examples/particles/index.html +14 -14
- package/examples/platformer/index.html +5 -5
- package/examples/puzzle/index.html +2 -2
- package/examples/stress/index.html +1 -1
- package/index.html +13 -13
- package/package.json +9 -2
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
LittleJS is a super lightweight JavaScript game engine with a fast hybrid rendering system. It provides everything necessary to make amazing games, including rendering, physics, particles, sound effects, music, input handling, and debug tools. LittleJS is designed to be extremely small yet powerful enough for a variety of applications, from game jams to commercial releases. The code is very well documented with a variety of examples to get you started. Choo-Choo! 🚂
|
|
6
6
|
|
|
7
|
-
## [LittleJS Documentation](https://killedbyapixel.github.io/LittleJS/docs) - [LittleJS Trailer](https://youtu.be/chuBzGjv7Ms) - [LittleJS
|
|
7
|
+
## [LittleJS Documentation](https://killedbyapixel.github.io/LittleJS/docs) - [LittleJS Trailer](https://youtu.be/chuBzGjv7Ms) - [LittleJS Discord](https://discord.gg/zb7hcGkyZe)
|
|
8
8
|
|
|
9
9
|

|
|
10
10
|
|
|
@@ -29,7 +29,8 @@ LittleJS is a super lightweight JavaScript game engine with a fast hybrid render
|
|
|
29
29
|
- Debug tools and debug rendering system
|
|
30
30
|
- [Full documentation](https://killedbyapixel.github.io/LittleJS/docs) automatically generated from the source code block tags with [JSDoc](https://github.com/jsdoc/jsdoc)
|
|
31
31
|
- Build system automatically combines everything, minifies, and removes unused code
|
|
32
|
-
- For size coding competitions like [js13kGames](https://js13kgames.com/), starter project builds to a 7KB zip
|
|
32
|
+
- For size coding competitions like [js13kGames](https://js13kgames.com/), starter project builds to a 7KB zip file
|
|
33
|
+
- (https://github.com/lifthrasiir/roadroller)
|
|
33
34
|
- Easily build a Windows executable with [Electron](https://www.electronjs.org/) for distribution on platforms like Steam
|
|
34
35
|
- Open Source with the [MIT license](https://github.com/KilledByAPixel/LittleJS/blob/main/LICENSE) so it can be used for anything you want
|
|
35
36
|
|
|
@@ -41,11 +42,13 @@ To easily include LittleJS in your game, you can use one of the 3 pre-built js f
|
|
|
41
42
|
- [engine.all.release.js](https://github.com/KilledByAPixel/LittleJS/blob/main/engine/engine.all.release.js) - The engine optimized for release builds
|
|
42
43
|
- [engine.all.min.js](https://github.com/KilledByAPixel/LittleJS/blob/main/engine/engine.all.min.js) - The engine in release mode and minified
|
|
43
44
|
|
|
44
|
-
The hello world example includes a batch file [build.bat](https://github.com/KilledByAPixel/LittleJS/blob/main/build.bat) that compresses everything into a tiny zip file using Google Closure, UglifyJS, Roadroller, and ECT. You must run buildSetup.bat to install the necessary npm dependencies.
|
|
45
|
+
The hello world example includes a batch file [build.bat](https://github.com/KilledByAPixel/LittleJS/blob/main/build.bat) that compresses everything into a tiny zip file using Google Closure, UglifyJS, Roadroller, and ECT. You must run [buildSetup.bat](https://github.com/KilledByAPixel/LittleJS/blob/main/buildSetup.bat) to install the necessary npm dependencies.
|
|
45
46
|
|
|
46
47
|
## Games Made With LittleJS
|
|
47
48
|
|
|
48
|
-
- [Space Huggers](https://
|
|
49
|
+
- [Space Huggers](https://www.newgrounds.com/portal/view/819609) - Run and gun platformer with procedural levels
|
|
50
|
+
- [Space Huggers - JS13k Edition](https://github.com/KilledByAPixel/SpaceHuggers) - Original js13k game with source code
|
|
51
|
+
- [Isletopia](https://store.steampowered.com/app/1861260/Isletopia) - Relaxing strategy game of greenifying barren islands
|
|
49
52
|
- Send me your games!
|
|
50
53
|
|
|
51
54
|

|
package/engine/engine.all.js
CHANGED
|
@@ -804,6 +804,34 @@ class Color
|
|
|
804
804
|
return this;
|
|
805
805
|
}
|
|
806
806
|
|
|
807
|
+
/** Returns this color expressed in hsla format
|
|
808
|
+
* @return {Array} */
|
|
809
|
+
getHSLA()
|
|
810
|
+
{
|
|
811
|
+
const r = this.r;
|
|
812
|
+
const g = this.g;
|
|
813
|
+
const b = this.b;
|
|
814
|
+
const a = this.a;
|
|
815
|
+
const max = Math.max(r, g, b);
|
|
816
|
+
const min = Math.min(r, g, b);
|
|
817
|
+
const l = (max + min) / 2;
|
|
818
|
+
|
|
819
|
+
let h = 0, s = 0;
|
|
820
|
+
if (max != min)
|
|
821
|
+
{
|
|
822
|
+
let d = max - min;
|
|
823
|
+
s = l > .5 ? d / (2 - max - min) : d / (max + min);
|
|
824
|
+
if (r == max)
|
|
825
|
+
h = (g - b) / d + (g < b ? 6 : 0);
|
|
826
|
+
else if (g == max)
|
|
827
|
+
h = (b - r) / d + 2;
|
|
828
|
+
else if (b == max)
|
|
829
|
+
h = (r - g) / d + 4;
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
return [h / 6, s, l, a];
|
|
833
|
+
}
|
|
834
|
+
|
|
807
835
|
/** Returns a new color that has each component randomly adjusted
|
|
808
836
|
* @param {Number} [amount=.05]
|
|
809
837
|
* @param {Number} [alphaAmount=0]
|
|
@@ -835,42 +863,6 @@ class Color
|
|
|
835
863
|
return (this.r*255|0) + (this.g*255<<8) + (this.b*255<<16) + (this.a*255<<24);
|
|
836
864
|
}
|
|
837
865
|
|
|
838
|
-
/** Returns this color expressed as a hex code
|
|
839
|
-
* @return {String} */
|
|
840
|
-
hex()
|
|
841
|
-
{
|
|
842
|
-
const toHex = (c)=> ((c=c*255|0)<16 ? '0' : '') + c.toString(16);
|
|
843
|
-
return '#' + toHex(this.r) + toHex(this.g) + toHex(this.b);
|
|
844
|
-
}
|
|
845
|
-
|
|
846
|
-
/** Returns this color expressed in hsla format
|
|
847
|
-
* @return {Array} */
|
|
848
|
-
getHSLA()
|
|
849
|
-
{
|
|
850
|
-
const r = this.r;
|
|
851
|
-
const g = this.g;
|
|
852
|
-
const b = this.b;
|
|
853
|
-
const a = this.a;
|
|
854
|
-
const max = Math.max(r, g, b);
|
|
855
|
-
const min = Math.min(r, g, b);
|
|
856
|
-
const l = (max + min) / 2;
|
|
857
|
-
|
|
858
|
-
let h = 0, s = 0;
|
|
859
|
-
if (max != min)
|
|
860
|
-
{
|
|
861
|
-
let d = max - min;
|
|
862
|
-
s = l > .5 ? d / (2 - max - min) : d / (max + min);
|
|
863
|
-
if (r == max)
|
|
864
|
-
h = (g - b) / d + (g < b ? 6 : 0);
|
|
865
|
-
else if (g == max)
|
|
866
|
-
h = (b - r) / d + 2;
|
|
867
|
-
else if (b == max)
|
|
868
|
-
h = (r - g) / d + 4;
|
|
869
|
-
}
|
|
870
|
-
|
|
871
|
-
return [h / 6, s, l, a];
|
|
872
|
-
}
|
|
873
|
-
|
|
874
866
|
/** Set this color from a hex code
|
|
875
867
|
* @param {String} hex - html hex code
|
|
876
868
|
* @return {Color} */
|
|
@@ -884,6 +876,14 @@ class Color
|
|
|
884
876
|
ASSERT(this.r>=0 && this.r<=1 && this.g>=0 && this.g<=1 && this.b>=0 && this.b<=1);
|
|
885
877
|
return this;
|
|
886
878
|
}
|
|
879
|
+
|
|
880
|
+
/** Returns this color expressed as a hex code
|
|
881
|
+
* @return {String} */
|
|
882
|
+
getHex()
|
|
883
|
+
{
|
|
884
|
+
const toHex = (c)=> ((c=c*255|0)<16 ? '0' : '') + c.toString(16);
|
|
885
|
+
return '#' + toHex(this.r) + toHex(this.g) + toHex(this.b);
|
|
886
|
+
}
|
|
887
887
|
}
|
|
888
888
|
|
|
889
889
|
///////////////////////////////////////////////////////////////////////////////
|
|
@@ -950,7 +950,8 @@ class Timer
|
|
|
950
950
|
* @memberof Settings */
|
|
951
951
|
let canvasMaxSize = vec2(1920, 1200);
|
|
952
952
|
|
|
953
|
-
/** Fixed size of the canvas, if enabled
|
|
953
|
+
/** Fixed size of the canvas, if enabled canvas size never changes
|
|
954
|
+
* - you may also need to set mainCanvasSize if using screen space coords in startup
|
|
954
955
|
* @type {Vector2}
|
|
955
956
|
* @default
|
|
956
957
|
* @memberof Settings */
|
|
@@ -1585,18 +1586,26 @@ let overlayContext;
|
|
|
1585
1586
|
let mainCanvasSize = vec2();
|
|
1586
1587
|
|
|
1587
1588
|
/** Convert from screen to world space coordinates
|
|
1589
|
+
* - if calling outside of render, you may need to manually set mainCanvasSize
|
|
1588
1590
|
* @param {Vector2} screenPos
|
|
1589
1591
|
* @return {Vector2}
|
|
1590
1592
|
* @memberof Draw */
|
|
1591
1593
|
const screenToWorld = (screenPos)=>
|
|
1592
|
-
|
|
1594
|
+
{
|
|
1595
|
+
ASSERT(mainCanvasSize.x && mainCanvasSize.y, 'mainCanvasSize is invalid');
|
|
1596
|
+
return screenPos.add(vec2(.5)).subtract(mainCanvasSize.scale(.5)).multiply(vec2(1/cameraScale,-1/cameraScale)).add(cameraPos);
|
|
1597
|
+
}
|
|
1593
1598
|
|
|
1594
1599
|
/** Convert from world to screen space coordinates
|
|
1600
|
+
* - if calling outside of render, you may need to manually set mainCanvasSize
|
|
1595
1601
|
* @param {Vector2} worldPos
|
|
1596
1602
|
* @return {Vector2}
|
|
1597
1603
|
* @memberof Draw */
|
|
1598
1604
|
const worldToScreen = (worldPos)=>
|
|
1599
|
-
|
|
1605
|
+
{
|
|
1606
|
+
ASSERT(mainCanvasSize.x && mainCanvasSize.y, 'mainCanvasSize is invalid');
|
|
1607
|
+
return worldPos.subtract(cameraPos).multiply(vec2(cameraScale,-cameraScale)).add(mainCanvasSize.scale(.5)).subtract(vec2(.5));
|
|
1608
|
+
}
|
|
1600
1609
|
|
|
1601
1610
|
/** Draw textured tile centered in world space, with color applied if using WebGL
|
|
1602
1611
|
* @param {Vector2} pos - Center of the tile in world space
|
|
@@ -2074,7 +2083,7 @@ const mouseToScreen = (mousePos)=>
|
|
|
2074
2083
|
return vec2(); // fix bug that can occur if user clicks before page loads
|
|
2075
2084
|
|
|
2076
2085
|
const rect = mainCanvas.getBoundingClientRect();
|
|
2077
|
-
return
|
|
2086
|
+
return vec2(mainCanvas.width, mainCanvas.height).multiply(
|
|
2078
2087
|
vec2(percent(mousePos.x, rect.left, rect.right), percent(mousePos.y, rect.top, rect.bottom)));
|
|
2079
2088
|
}
|
|
2080
2089
|
|
|
@@ -4095,7 +4104,7 @@ gl_VERTEX_BYTE_STRIDE = (4 * 2) * 2 + (4) * 2; // vec2 * 2 + (char * 4) * 2
|
|
|
4095
4104
|
const engineName = 'LittleJS';
|
|
4096
4105
|
|
|
4097
4106
|
/** Version of engine */
|
|
4098
|
-
const engineVersion = '1.3.
|
|
4107
|
+
const engineVersion = '1.3.8';
|
|
4099
4108
|
|
|
4100
4109
|
/** Frames per second to update objects
|
|
4101
4110
|
* @default */
|
|
@@ -4186,6 +4195,33 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
4186
4195
|
timeReal += frameTimeDeltaMS / 1e3;
|
|
4187
4196
|
frameTimeBufferMS = min(frameTimeBufferMS + !paused * frameTimeDeltaMS, 50); // clamp incase of slow framerate
|
|
4188
4197
|
|
|
4198
|
+
if (canvasFixedSize.x)
|
|
4199
|
+
{
|
|
4200
|
+
// clear set fixed size
|
|
4201
|
+
overlayCanvas.width = mainCanvas.width = canvasFixedSize.x;
|
|
4202
|
+
overlayCanvas.height = mainCanvas.height = canvasFixedSize.y;
|
|
4203
|
+
|
|
4204
|
+
// fit to window by adding space on top or bottom if necessary
|
|
4205
|
+
const aspect = innerWidth / innerHeight;
|
|
4206
|
+
const fixedAspect = mainCanvas.width / mainCanvas.height;
|
|
4207
|
+
mainCanvas.style.width = overlayCanvas.style.width = aspect < fixedAspect ? '100%' : '';
|
|
4208
|
+
mainCanvas.style.height = overlayCanvas.style.height = aspect < fixedAspect ? '' : '100%';
|
|
4209
|
+
if (glCanvas)
|
|
4210
|
+
{
|
|
4211
|
+
glCanvas.style.width = mainCanvas.style.width;
|
|
4212
|
+
glCanvas.style.height = mainCanvas.style.height;
|
|
4213
|
+
}
|
|
4214
|
+
}
|
|
4215
|
+
else
|
|
4216
|
+
{
|
|
4217
|
+
// clear and set size to same as window
|
|
4218
|
+
overlayCanvas.width = mainCanvas.width = min(innerWidth, canvasMaxSize.x);
|
|
4219
|
+
overlayCanvas.height = mainCanvas.height = min(innerHeight, canvasMaxSize.y);
|
|
4220
|
+
}
|
|
4221
|
+
|
|
4222
|
+
// save canvas size
|
|
4223
|
+
mainCanvasSize = vec2(mainCanvas.width, mainCanvas.height);
|
|
4224
|
+
|
|
4189
4225
|
if (paused)
|
|
4190
4226
|
{
|
|
4191
4227
|
// do post update even when paused
|
|
@@ -4222,30 +4258,6 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
4222
4258
|
// add the time smoothing back in
|
|
4223
4259
|
frameTimeBufferMS += deltaSmooth;
|
|
4224
4260
|
}
|
|
4225
|
-
|
|
4226
|
-
if (canvasFixedSize.x)
|
|
4227
|
-
{
|
|
4228
|
-
// clear set fixed size
|
|
4229
|
-
mainCanvas.width = canvasFixedSize.x;
|
|
4230
|
-
mainCanvas.height = canvasFixedSize.y;
|
|
4231
|
-
|
|
4232
|
-
// fit to window by adding space on top or bottom if necessary
|
|
4233
|
-
const aspect = innerWidth / innerHeight;
|
|
4234
|
-
const fixedAspect = mainCanvas.width / mainCanvas.height;
|
|
4235
|
-
mainCanvas.style.width = overlayCanvas.style.width = aspect < fixedAspect ? '100%' : '';
|
|
4236
|
-
mainCanvas.style.height = overlayCanvas.style.height = aspect < fixedAspect ? '' : '100%';
|
|
4237
|
-
if (glCanvas)
|
|
4238
|
-
{
|
|
4239
|
-
glCanvas.style.width = mainCanvas.style.width;
|
|
4240
|
-
glCanvas.style.height = mainCanvas.style.height;
|
|
4241
|
-
}
|
|
4242
|
-
}
|
|
4243
|
-
else
|
|
4244
|
-
{
|
|
4245
|
-
// clear and set size to same as window
|
|
4246
|
-
mainCanvas.width = min(innerWidth, canvasMaxSize.x);
|
|
4247
|
-
mainCanvas.height = min(innerHeight, canvasMaxSize.y);
|
|
4248
|
-
}
|
|
4249
4261
|
|
|
4250
4262
|
// render sort then render while removing destroyed objects
|
|
4251
4263
|
enginePreRender();
|
|
@@ -4285,8 +4297,8 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
4285
4297
|
// called by engine to setup render system
|
|
4286
4298
|
function enginePreRender()
|
|
4287
4299
|
{
|
|
4288
|
-
// save canvas size
|
|
4289
|
-
mainCanvasSize = vec2(
|
|
4300
|
+
// save canvas size
|
|
4301
|
+
mainCanvasSize = vec2(mainCanvas.width, mainCanvas.height);
|
|
4290
4302
|
|
|
4291
4303
|
// disable smoothing for pixel art
|
|
4292
4304
|
mainContext.imageSmoothingEnabled = !cavasPixelated;
|
package/engine/engine.all.min.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
const debug=0,showWatermark=0,godMode=0,debugOverlay=0,debugPhysics=0,debugParticles=0,debugRaycast=0,debugGamepads=0,debugMedals=0,ASSERT=()=>{},debugInit=()=>{},debugUpdate=()=>{},debugRender=()=>{},debugRect=()=>{},debugCircle=()=>{},debugPoint=()=>{},debugLine=()=>{},debugAABB=()=>{},debugText=()=>{},debugClear=()=>{},debugSaveCanvas=()=>{};"use strict";const PI=Math.PI,abs=a=>0>a?-a:a,min=(a,b)=>a<b?a:b,max=(a,b)=>a>b?a:b,sign=a=>0>a?-1:1,mod=(a,b=1)=>(a%b+b)%b,clamp=(a,b=0,c=1)=>a<b?b:a>c?c:a,percent=(a,b=0,c=1)=>c-b?clamp((a-b)/(c-b)):0,lerp=(a,b=0,c=1)=>b+clamp(a)*(c-b),smoothStep=a=>a*a*(3-2*a),nearestPowerOfTwo=a=>2**Math.ceil(Math.log2(a)),isOverlapping=(a,b,c,d)=>2*abs(a.x-c.x)<b.x+d.x&2*abs(a.y-c.y)<b.y+d.y,wave=(a=1,b=1,c=time)=>b/2*(1-Math.cos(c*a*2*PI)),formatTime=a=>(a/60|0)+":"+(10>a%60?"0":"")+(a%60|0),rand=(a=1,b=0)=>b+(a-b)*Math.random(),randInt=(a=1,b=0)=>rand(a,b)|0,randSign=()=>2*(rand(2)|0)-1,randInCircle=(a=1,b=0)=>0<a?randVector(a*rand(b/a,1)**.5):new Vector2,randVector=(a=1)=>(new Vector2).setAngle(rand(2*PI),a),randColor=(a=new Color,b=new Color(0,0,0,1),c)=>c?a.lerp(b,rand()):new Color(rand(a.r,b.r),rand(a.g,b.g),rand(a.b,b.b),rand(a.a,b.a));let randSeed=1;const randSeeded=(a=1,b=0)=>{randSeed^=randSeed<<13;randSeed^=randSeed>>>17;randSeed^=randSeed<<5;return b+(a-b)*abs(randSeed%1e9)/1e9},vec2=(a=0,b)=>void 0==a.x?new Vector2(a,void 0==b?a:b):new Vector2(a.x,a.y);class Vector2{constructor(a=0,b=0){this.x=a;this.y=b}copy(){return new Vector2(this.x,this.y)}add(a){ASSERT(void 0!=a.x);return new Vector2(this.x+a.x,this.y+a.y)}subtract(a){ASSERT(void 0!=a.x);return new Vector2(this.x-a.x,this.y-a.y)}multiply(a){ASSERT(void 0!=a.x);return new Vector2(this.x*a.x,this.y*a.y)}divide(a){ASSERT(void 0!=a.x);return new Vector2(this.x/a.x,this.y/a.y)}scale(a){ASSERT(void 0==a.x);return new Vector2(this.x*a,this.y*a)}length(){return this.lengthSquared()**.5}lengthSquared(){return this.x**2+this.y**2}distance(a){return this.distanceSquared(a)**.5}distanceSquared(a){return(this.x-a.x)**2+(this.y-a.y)**2}normalize(a=1){const b=this.length();return b?this.scale(a/b):new Vector2(0,a)}clampLength(a=1){const b=this.length();return b>a?this.scale(a/b):this}dot(a){ASSERT(void 0!=a.x);return this.x*a.x+this.y*a.y}cross(a){ASSERT(void 0!=a.x);return this.x*a.y-this.y*a.x}angle(){return Math.atan2(this.x,this.y)}setAngle(a=0,b=1){this.x=b*Math.sin(a);this.y=b*Math.cos(a);return this}rotate(a){const b=Math.cos(a);a=Math.sin(a);return new Vector2(this.x*b-this.y*a,this.x*a+this.y*b)}direction(){return abs(this.x)>abs(this.y)?0>this.x?3:1:0>this.y?2:0}invert(){return new Vector2(this.y,-this.x)}floor(){return new Vector2(Math.floor(this.x),Math.floor(this.y))}area(){return abs(this.x*this.y)}lerp(a,b){ASSERT(void 0!=a.x);return this.add(a.subtract(this).scale(clamp(b)))}arrayCheck(a){return 0<=this.x&&0<=this.y&&this.x<a.x&&this.y<a.y}toString(a=3){return`(${(0>this.x?"":" ")+this.x.toFixed(a)},${(0>this.y?"":" ")+this.y.toFixed(a)} )`}}class Color{constructor(a=1,b=1,c=1,d=1){this.r=a;this.g=b;this.b=c;this.a=d}copy(){return new Color(this.r,this.g,this.b,this.a)}add(a){return new Color(this.r+a.r,this.g+a.g,this.b+a.b,this.a+a.a)}subtract(a){return new Color(this.r-a.r,this.g-a.g,this.b-a.b,this.a-a.a)}multiply(a){return new Color(this.r*a.r,this.g*a.g,this.b*a.b,this.a*a.a)}divide(a){return new Color(this.r/a.r,this.g/a.g,this.b/a.b,this.a/a.a)}scale(a,b=a){return new Color(this.r*a,this.g*a,this.b*a,this.a*b)}clamp(){return new Color(clamp(this.r),clamp(this.g),clamp(this.b),clamp(this.a))}lerp(a,b){return this.add(a.subtract(this).scale(clamp(b)))}setHSLA(a=0,b=0,c=1,d=1){b=.5>c?c*(1+b):c+b-c*b;c=2*c-b;const e=(f,h,g)=>(g=(g%1+1)%1)<1/6?f+6*(h-f)*g:.5>g?h:g<2/3?f+(h-f)*(2/3-g)*6:f;this.r=e(c,b,a+1/3);this.g=e(c,b,a);this.b=e(c,b,a-1/3);this.a=d;return this}mutate(a=.05,b=0){return new Color(this.r+rand(a,-a),this.g+rand(a,-a),this.b+rand(a,-a),this.a+rand(b,-b)).clamp()}toString(){ASSERT(0<=this.r&&1>=this.r&&0<=this.g&&1>=this.g&&0<=this.b&&1>=this.b&&0<=this.a&&1>=this.a);return`rgb(${255*this.r|0},${255*this.g|0},${255*this.b|0},${this.a})`}rgbaInt(){ASSERT(0<=this.r&&1>=this.r&&0<=this.g&&1>=this.g&&0<=this.b&&1>=this.b&&0<=this.a&&1>=this.a);return(255*this.r|0)+(255*this.g<<8)+(255*this.b<<16)+(255*this.a<<24)}hex(){const a=b=>(16>(b=255*b|0)?"0":"")+b.toString(16);return"#"+a(this.r)+a(this.g)+a(this.b)}getHSLA(){const a=this.r,b=this.g,c=this.b,d=this.a,e=Math.max(a,b,c),f=Math.min(a,b,c),h=(e+f)/2;let g=0,k=0;if(e!=f){let m=e-f;k=.5<h?m/(2-e-f):m/(e+f);a==e?g=(b-c)/m+(b<c?6:0):b==e?g=(c-a)/m+2:c==e&&(g=(a-b)/m+4)}return[g/6,k,h,d]}setHex(a){this.r=parseInt(a.slice(1,3),16)/255;this.g=parseInt(a.slice(3,5),16)/255;this.b=parseInt(a.slice(5,7),16)/255;this.a=1;ASSERT(0<=this.r&&1>=this.r&&0<=this.g&&1>=this.g&&0<=this.b&&1>=this.b);return this}}class Timer{constructor(a){this.time=void 0==a?void 0:time+a;this.setTime=a}set(a=0){this.time=time+a;this.setTime=a}unset(){this.time=void 0}isSet(){return void 0!=this.time}active(){return time<=this.time}elapsed(){return time>this.time}get(){return this.isSet()?time-this.time:0}getPercent(){return this.isSet()?percent(this.time-time,this.setTime,0):0}toString(){if(debug)return this.unset()?"unset":Math.abs(this.get())+" seconds "+(0>this.get()?"before":"after")}}"use strict";let canvasMaxSize=vec2(1920,1200),canvasFixedSize=vec2(),cavasPixelated=1,fontDefault="arial",tileSizeDefault=vec2(16),tileFixBleedScale=.3,objectDefaultSize=vec2(1),enablePhysicsSolver=1,objectDefaultMass=1,objectDefaultDamping=.99,objectDefaultAngleDamping=.99,objectDefaultElasticity=0,objectDefaultFriction=.8,objectMaxSpeed=1,gravity=0,particleEmitRateScale=1,cameraPos=vec2(),cameraScale=max(tileSizeDefault.x,tileSizeDefault.y),glEnable=1,glOverlay=1,gamepadsEnable=1,gamepadDirectionEmulateStick=1,inputWASDEmulateDirection=1,touchGamepadEnable=0,touchGamepadAnalog=1,touchGamepadSize=80,touchGamepadAlpha=.3,vibrateEnable=1,soundVolume=.5,soundEnable=1,soundDefaultRange=30,soundDefaultTaper=.7,medalDisplayTime=5,medalDisplaySlideTime=.5,medalDisplayWidth=640,medalDisplayHeight=80,medalDisplayIconSize=50;"use strict";const engineName="LittleJS",engineVersion="1.3.5",frameRate=60,timeDelta=1/frameRate;let engineObjects=[],engineObjectsCollide=[],frame=0,time=0,timeReal=0,paused=0,frameTimeLastMS=0,frameTimeBufferMS=0,tileImageSize,tileImageFixBleed,averageFPS,drawCount;const styleBody="margin:0;overflow:hidden;background:#000;touch-action:none;user-select:none;-webkit-user-select:none;-moz-user-select:none",styleCanvas="position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)";function engineInit(a,b,c,d,e,f){tileImage.onerror=tileImage.onload=()=>{tileImageFixBleed=vec2(tileFixBleedScale).divide(tileImageSize=vec2(tileImage.width,tileImage.height));debug&&(tileImage.onload=()=>ASSERT(1));document.body.style=styleBody;document.body.appendChild(mainCanvas=document.createElement("canvas"));mainContext=mainCanvas.getContext("2d");mainCanvas.style=styleCanvas;debugInit();glInit();document.body.appendChild(overlayCanvas=document.createElement("canvas"));overlayContext=overlayCanvas.getContext("2d");overlayCanvas.style=styleCanvas;a();touchGamepadCreate();h()};const h=(g=0)=>{var k=g-frameTimeLastMS;frameTimeLastMS=g;if(debug||showWatermark)averageFPS=lerp(.05,averageFPS||0,1e3/(k||1));debug&&(k*=keyIsDown(107)?5:keyIsDown(109)?.2:1);timeReal+=k/1e3;frameTimeBufferMS=min(frameTimeBufferMS+!paused*k,50);if(paused)inputUpdate(),debugUpdate(),c(),inputUpdatePost();else{g=0;0>frameTimeBufferMS&&-9<frameTimeBufferMS&&(g=frameTimeBufferMS,frameTimeBufferMS=0);for(;0<=frameTimeBufferMS;frameTimeBufferMS-=1e3/frameRate)inputUpdate(),b(),engineObjectsUpdate(),debugUpdate(),c(),inputUpdatePost();frameTimeBufferMS+=g}canvasFixedSize.x?(mainCanvas.width=canvasFixedSize.x,mainCanvas.height=canvasFixedSize.y,g=innerWidth/innerHeight,k=mainCanvas.width/mainCanvas.height,mainCanvas.style.width=overlayCanvas.style.width=g<k?"100%":"",mainCanvas.style.height=overlayCanvas.style.height=g<k?"":"100%",glCanvas&&(glCanvas.style.width=mainCanvas.style.width,glCanvas.style.height=mainCanvas.style.height)):(mainCanvas.width=min(innerWidth,canvasMaxSize.x),mainCanvas.height=min(innerHeight,canvasMaxSize.y));enginePreRender();d();engineObjects.sort((n,p)=>n.renderOrder-p.renderOrder);for(var m of engineObjects)m.destroyed||m.render();e();medalsRender();touchGamepadRender();debugRender();glCopyToContext(mainContext);showWatermark&&(overlayContext.textAlign="right",overlayContext.textBaseline="top",overlayContext.font="1em monospace",overlayContext.fillStyle="#000",m=engineName+" v"+engineVersion+" / "+drawCount+" / "+engineObjects.length+" / "+averageFPS.toFixed(1)+" "+(glEnable?"GL":"2D"),overlayContext.fillText(m,mainCanvas.width-3,3),overlayContext.fillStyle="#fff",overlayContext.fillText(m,mainCanvas.width-2,2),drawCount=0);requestAnimationFrame(h)};f?tileImage.src=f:tileImage.onload()}function enginePreRender(){mainCanvasSize=vec2(overlayCanvas.width=mainCanvas.width,overlayCanvas.height=mainCanvas.height);mainContext.imageSmoothingEnabled=!cavasPixelated;glPreRender(mainCanvas.width,mainCanvas.height,cameraPos.x,cameraPos.y,cameraScale)}function engineObjectsUpdate(){engineObjectsCollide=engineObjects.filter(b=>b.collideSolidObjects);const a=b=>{if(!b.destroyed){b.update();for(const c of b.children)a(c)}};for(const b of engineObjects)b.parent||a(b);engineObjects=engineObjects.filter(b=>!b.destroyed);time=++frame/frameRate}function engineObjectsDestroy(){for(const a of engineObjects)a.parent||a.destroy();engineObjects=engineObjects.filter(a=>!a.destroyed)}function engineObjectsCallback(a,b,c,d=engineObjects){if(a)if(void 0!=b.x)for(const e of d)isOverlapping(a,b,e.pos,e.size)&&c(e);else{b*=b;for(const e of d)a.distanceSquared(e.pos)<b&&c(e)}else for(const e of d)c(e)}"use strict";class EngineObject{constructor(a=vec2(),b=objectDefaultSize,c=-1,d=tileSizeDefault,e=0,f,h=0){ASSERT(a&&void 0!=a.x&&void 0!=b.x);this.pos=a.copy();this.size=b;this.drawSize;this.tileIndex=c;this.tileSize=d;this.angle=e;this.color=f;this.additiveColor;this.mass=objectDefaultMass;this.damping=objectDefaultDamping;this.angleDamping=objectDefaultAngleDamping;this.elasticity=objectDefaultElasticity;this.friction=objectDefaultFriction;this.gravityScale=1;this.renderOrder=h;this.velocity=new Vector2;this.angleVelocity=0;this.spawnTime=time;this.children=[];this.collideTiles=1;engineObjects.push(this)}update(){var a=this.parent;if(a)this.pos=this.localPos.multiply(vec2(a.getMirrorSign(),1)).rotate(-a.angle).add(a.pos),this.angle=a.getMirrorSign()*this.localAngle+a.angle;else if(this.velocity.x=clamp(this.velocity.x,-objectMaxSpeed,objectMaxSpeed),this.velocity.y=clamp(this.velocity.y,-objectMaxSpeed,objectMaxSpeed),a=this.pos.copy(),this.pos.x+=this.velocity.x*=this.damping,this.pos.y+=this.velocity.y=this.damping*this.velocity.y+gravity*this.gravityScale,this.angle+=this.angleVelocity*=this.angleDamping,ASSERT(0<=this.angleDamping&&1>=this.angleDamping),ASSERT(0<=this.damping&&1>=this.damping),enablePhysicsSolver&&this.mass){var b=0>this.velocity.y;if(this.groundObject){var c=this.groundObject.velocity?this.groundObject.velocity.x:0;this.velocity.x=c+(this.velocity.x-c)*this.friction;this.groundObject=0}if(this.collideSolidObjects)for(var d of engineObjectsCollide)if(!(!this.isSolid&!d.isSolid||d.destroyed||d.parent||d==this||!isOverlapping(this.pos,this.size,d.pos,d.size)||!this.collideWithObject(d)|!d.collideWithObject(this)))if(isOverlapping(a,this.size,d.pos,d.size)){c=a.subtract(d.pos);var e=c.length();c=.01>e?randVector(.001):c.scale(.001/e);this.velocity=this.velocity.add(c);d.mass&&(d.velocity=d.velocity.subtract(c));debugOverlay&&debugPhysics&&debugAABB(this.pos,this.size,d.pos,d.size,"#f00")}else{c=this.size.add(d.size);e=2*(a.y-d.pos.y)>c.y+gravity;var f=2*abs(a.y-d.pos.y)<c.y,h=2*abs(a.x-d.pos.x)<c.x;if(e||h||!f)if(this.pos.y=d.pos.y+(c.y/2+.001)*sign(a.y-d.pos.y),d.groundObject&&b||!d.mass)b&&(this.groundObject=d),this.velocity.y*=-this.elasticity;else if(d.mass){const g=(this.mass*this.velocity.y+d.mass*d.velocity.y)/(this.mass+d.mass),k=this.velocity.y*(this.mass-d.mass)/(this.mass+d.mass)+2*d.velocity.y*d.mass/(this.mass+d.mass),m=d.velocity.y*(d.mass-this.mass)/(this.mass+d.mass)+2*this.velocity.y*this.mass/(this.mass+d.mass),n=max(this.elasticity,d.elasticity);this.velocity.y=lerp(n,g,k);d.velocity.y=lerp(n,g,m)}e||!f&&h||(this.pos.x=d.pos.x+(c.x/2+.001)*sign(a.x-d.pos.x),d.mass?(c=(this.mass*this.velocity.x+d.mass*d.velocity.x)/(this.mass+d.mass),e=this.velocity.x*(this.mass-d.mass)/(this.mass+d.mass)+2*d.velocity.x*d.mass/(this.mass+d.mass),f=d.velocity.x*(d.mass-this.mass)/(this.mass+d.mass)+2*this.velocity.x*this.mass/(this.mass+d.mass),h=max(this.elasticity,d.elasticity),this.velocity.x=lerp(h,c,e),d.velocity.x=lerp(h,c,f)):this.velocity.x*=-this.elasticity);debugOverlay&&debugPhysics&&debugAABB(this.pos,this.size,d.pos,d.size,"#f0f")}if(this.collideTiles&&tileCollisionTest(this.pos,this.size,this)&&!tileCollisionTest(a,this.size,this)){c=tileCollisionTest(new Vector2(a.x,this.pos.y),this.size,this);d=tileCollisionTest(new Vector2(this.pos.x,a.y),this.size,this);if(c||!d)this.groundObject=b,this.velocity.y*=-this.elasticity,b=(a.y-this.size.y/2|0)-(a.y-this.size.y/2),0>b&&b>this.damping*this.velocity.y+gravity*this.gravityScale&&(this.velocity.y=this.damping?(b-gravity*this.gravityScale)/this.damping:0),this.pos.y=a.y;d&&(this.pos.x=a.x,this.velocity.x*=-this.elasticity)}}}render(){drawTile(this.pos,this.drawSize||this.size,this.tileIndex,this.tileSize,this.color,this.angle,this.mirror,this.additiveColor)}destroy(){if(!this.destroyed){this.destroyed=1;this.parent&&this.parent.removeChild(this);for(const a of this.children)a.destroy(a.parent=0)}}collideWithTile(a,b){return 0<a}collideWithTileRaycast(a,b){return 0<a}collideWithObject(a){return 1}getAliveTime(){return time-this.spawnTime}applyAcceleration(a){this.mass&&(this.velocity=this.velocity.add(a))}applyForce(a){this.applyAcceleration(a.scale(1/this.mass))}getMirrorSign(){return this.mirror?-1:1}addChild(a,b=vec2(),c=0){ASSERT(!a.parent&&!this.children.includes(a));this.children.push(a);a.parent=this;a.localPos=b.copy();a.localAngle=c}removeChild(a){ASSERT(a.parent==this&&this.children.includes(a));this.children.splice(this.children.indexOf(a),1);a.parent=0}setCollision(a=0,b=0,c=1){ASSERT(a||!b);this.collideSolidObjects=a;this.isSolid=b;this.collideTiles=c}toString(){if(debug){let a="type = "+this.constructor.name;if(this.pos.x||this.pos.y)a+="\npos = "+this.pos;if(this.velocity.x||this.velocity.y)a+="\nvelocity = "+this.velocity;if(this.size.x||this.size.y)a+="\nsize = "+this.size;this.angle&&(a+="\nangle = "+this.angle.toFixed(3));this.color&&(a+="\ncolor = "+this.color);return a}}}"use strict";const tileImage=new Image;let mainCanvas,mainContext,overlayCanvas,overlayContext,mainCanvasSize=vec2();const screenToWorld=a=>a.add(vec2(.5)).subtract(mainCanvasSize.scale(.5)).multiply(vec2(1/cameraScale,-1/cameraScale)).add(cameraPos),worldToScreen=a=>a.subtract(cameraPos).multiply(vec2(cameraScale,-cameraScale)).add(mainCanvasSize.scale(.5)).subtract(vec2(.5));function drawTile(a,b=vec2(1),c=-1,d=tileSizeDefault,e=new Color,f=0,h,g=new Color(0,0,0,0),k=glEnable){showWatermark&&++drawCount;if(glEnable&&k)if(0>c||!tileImage.width)glDraw(a.x,a.y,b.x,b.y,f,0,0,0,0,0,e.rgbaInt());else{var m=tileImageSize.x/d.x|0;k=d.x/tileImageSize.x;const n=d.y/tileImageSize.y,p=c%m*k;m=(c/m|0)*n;glDraw(a.x,a.y,h?-b.x:b.x,b.y,f,p+tileImageFixBleed.x,m+tileImageFixBleed.y,p-tileImageFixBleed.x+k,m-tileImageFixBleed.y+n,e.rgbaInt(),g.rgbaInt())}else drawCanvas2D(a,b,f,h,n=>{if(0>c)n.fillStyle=e,n.fillRect(-.5,-.5,1,1);else{var p=tileImageSize.x/d.x|0;const l=c%p*d.x+tileFixBleedScale;p=(c/p|0)*d.y+tileFixBleedScale;const u=d.x-2*tileFixBleedScale,v=d.y-2*tileFixBleedScale;n.globalAlpha=e.a;n.drawImage(tileImage,l,p,u,v,-.5,-.5,1,1)}})}function drawRect(a,b,c,d,e){drawTile(a,b,-1,tileSizeDefault,c,d,0,0,e)}function drawTileScreenSpace(a,b=vec2(1),c,d,e,f,h,g,k){drawTile(screenToWorld(a),b.scale(1/cameraScale),c,d,e,f,h,g,k)}function drawRectScreenSpace(a,b,c,d,e){drawTileScreenSpace(a,b,-1,tileSizeDefault,c,d,0,0,e)}function drawLine(a,b,c=.1,d,e){b=vec2((b.x-a.x)/2,(b.y-a.y)/2);c=vec2(c,2*b.length());drawRect(a.add(b),c,d,b.angle(),0,0,e)}function drawCanvas2D(a,b,c,d,e,f=mainContext){a=worldToScreen(a);b=b.scale(cameraScale);f.save();f.translate(a.x+.5|0,a.y-.5|0);f.rotate(c);f.scale(d?-b.x:b.x,b.y);e(f);f.restore()}function setBlendMode(a,b=glEnable){glEnable&&b?glSetBlendMode(a):mainContext.globalCompositeOperation=a?"lighter":"source-over"}function drawTextScreen(a,b,c=1,d=new Color,e=0,f=new Color(0,0,0),h="center",g=fontDefault){overlayContext.fillStyle=d;overlayContext.lineWidth=e;overlayContext.strokeStyle=f;overlayContext.textAlign=h;overlayContext.font=c+"px "+g;overlayContext.textBaseline="middle";overlayContext.lineJoin="round";b=b.copy();(a+"").split("\n").forEach(k=>{e&&overlayContext.strokeText(k,b.x,b.y);overlayContext.fillText(k,b.x,b.y);b.y+=c})}function drawText(a,b,c=1,d,e,f,h,g){drawTextScreen(a,worldToScreen(b),c*cameraScale,d,e*cameraScale,f,h,g)}let engineFontImage;class FontImage{constructor(a,b=vec2(8),c=vec2(0,1),d=0,e=overlayContext){a||engineFontImage||(engineFontImage=new Image,engineFontImage.src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQAAAAAYAQAAAAA9+x6JAAAAAnRSTlMAAHaTzTgAAAGiSURBVHjaZZABhxxBEIUf6ECLBdFY+Q0PMNgf0yCgsSAGZcT9sgIPtBWwIA5wgAPEoHUyJeeSlW+gjK+fegWwtROWpVQEyWh2npdpBmTUFVhb29RINgLIukoXr5LIAvYQ5ve+1FqWEMqNKTX3FAJHyQDRZvmKWubAACcv5z5Gtg2oyCWE+Yk/8JZQX1jTTCpKAFGIgza+dJCNBF2UskRlsgwitHbSV0QLgt9sTPtsRlvJjEr8C/FARWA2bJ/TtJ7lko34dNDn6usJUMzuErP89UUBJbWeozrwLLncXczd508deAjLWipLO4Q5XGPcJvPu92cNDaN0P5G1FL0nSOzddZOrJ6rNhbXGmeDvO3TF7DeJWl4bvaYQTNHCTeuqKZmbjHaSOFes+IX/+IhHrnAkXOAsfn24EM68XieIECoccD4KZLk/odiwzeo2rovYdhvb2HYFgyznJyDpYJdYOmfXgVdJTaUi4xA2uWYNYec9BLeqdl9EsoTw582mSFDX2DxVLbNt9U3YYoeatBad1c2Tj8t2akrjaIGJNywKB/7h75/gN3vCMSaadIUTAAAAAElFTkSuQmCC");this.image=a||engineFontImage;this.tileSize=b;this.paddingSize=c;this.startTileIndex=d;this.context=e}drawTextScreen(a,b,c=4,d){const e=this.context;e.save();e.imageSmoothingEnabled=!cavasPixelated;const f=this.tileSize,h=f.add(this.paddingSize).scale(c),g=this.image.width/this.tileSize.x|0;a.split("\n").forEach((k,m)=>{const n=d?k.length*f.x*c/2|0:0;for(let u=k.length;u--;){var p=k[u].charCodeAt();if(32>p||127<p)p=127;var l=this.startTileIndex+p-32;p=l%g;l=l/g|0;const v=b.add(vec2(u,m).multiply(h));e.drawImage(this.image,p*f.x,l*f.y,f.x,f.y,v.x-n,v.y,f.x*c,f.y*c)}});e.restore()}drawText(a,b,c=1,d){this.drawTextScreen(a,worldToScreen(b).floor(),c*cameraScale|0,d)}}const isFullscreen=()=>document.fullscreenElement;function toggleFullscreen(){isFullscreen()?document.exitFullscreen?document.exitFullscreen():document.mozCancelFullScreen&&document.mozCancelFullScreen():document.body.webkitRequestFullScreen?document.body.webkitRequestFullScreen():document.body.mozRequestFullScreen&&document.body.mozRequestFullScreen()}"use strict";const keyIsDown=(a,b=0)=>inputData[b]&&inputData[b][a]&1?1:0,keyWasPressed=(a,b=0)=>inputData[b]&&inputData[b][a]&2?1:0,keyWasReleased=(a,b=0)=>inputData[b]&&inputData[b][a]&4?1:0,clearInput=()=>inputData=[[]],mouseIsDown=keyIsDown,mouseWasPressed=keyWasPressed,mouseWasReleased=keyWasReleased;let mousePos=vec2(),mousePosScreen=vec2(),mouseWheel=0,isUsingGamepad=0,preventDefaultInput=0;const gamepadIsDown=(a,b=0)=>keyIsDown(a,b+1),gamepadWasPressed=(a,b=0)=>keyWasPressed(a,b+1),gamepadWasReleased=(a,b=0)=>keyWasReleased(a,b+1),gamepadStick=(a,b=0)=>stickData[b]?stickData[b][a]||vec2():vec2();let inputData=[[]];function inputUpdate(){document.hasFocus()||clearInput();mousePos=screenToWorld(mousePosScreen);gamepadsUpdate()}function inputUpdatePost(){for(const a of inputData)for(const b in a)a[b]&=1;mouseWheel=0}onkeydown=a=>{debug&&a.target!=document.body||(a.repeat||(inputData[isUsingGamepad=0][remapKeyCode(a.keyCode)]=3),preventDefaultInput&&a.preventDefault())};onkeyup=a=>{debug&&a.target!=document.body||(inputData[0][remapKeyCode(a.keyCode)]=4)};const remapKeyCode=a=>inputWASDEmulateDirection?87==a?38:83==a?40:65==a?37:68==a?39:a:a;onmousedown=a=>{inputData[isUsingGamepad=0][a.button]=3;onmousemove(a);a.button&&a.preventDefault()};onmouseup=a=>inputData[0][a.button]=inputData[0][a.button]&2|4;onmousemove=a=>mousePosScreen=mouseToScreen(a);onwheel=a=>a.ctrlKey||(mouseWheel=sign(a.deltaY));oncontextmenu=a=>!1;const mouseToScreen=a=>{if(!mainCanvas)return vec2();const b=mainCanvas.getBoundingClientRect();return mainCanvasSize.multiply(vec2(percent(a.x,b.left,b.right),percent(a.y,b.top,b.bottom)))},stickData=[];function gamepadsUpdate(){if(touchGamepadEnable&&touchGamepadTimer.isSet()){(stickData[0]||(stickData[0]=[]))[0]=vec2(touchGamepadStick.x,-touchGamepadStick.y);var a=inputData[1]||(inputData[1]=[]);for(var b=10;b--;){var c=3==b?2:2==b?3:b;a[c]=touchGamepadButtons[b]?1+2*!gamepadIsDown(c,0):4*gamepadIsDown(c,0)}}if(gamepadsEnable&&navigator.getGamepads&&(document.hasFocus()||debug))for(a=navigator.getGamepads(),b=a.length;b--;){var d=a[b];const h=inputData[b+1]||(inputData[b+1]=[]);c=stickData[b]||(stickData[b]=[]);if(d){var e=g=>.3<g?percent(g,.3,.8):-.3>g?-percent(-g,.3,.8):0;for(var f=0;f<d.axes.length-1;f+=2)c[f>>1]=vec2(e(d.axes[f]),e(-d.axes[f+1])).clampLength();for(e=d.buttons.length;e--;)f=d.buttons[e],h[e]=f.pressed?1+2*!gamepadIsDown(e,b):4*gamepadIsDown(e,b),isUsingGamepad|=!b&&f.pressed,touchGamepadEnable&&touchGamepadTimer.unset();gamepadDirectionEmulateStick&&(d=vec2(gamepadIsDown(15,b)-gamepadIsDown(14,b),gamepadIsDown(12,b)-gamepadIsDown(13,b)),d.lengthSquared()&&(c[0]=d.clampLength()))}}}const vibrate=a=>vibrateEnable&&Navigator.vibrate&&Navigator.vibrate(a),vibrateStop=()=>vibrate(0),isTouchDevice=void 0!==window.ontouchstart;if(isTouchDevice){let a,b;ontouchstart=ontouchmove=ontouchend=c=>{c.button=0;const d=c.touches.length;d?(b||zzfx(0,b=1),c.x=c.touches[0].clientX,c.y=c.touches[0].clientY,a?onmousemove(c):onmousedown(c)):a&&onmouseup(c);a=d;return!c.cancelable}}let touchGamepadTimer=new Timer,touchGamepadButtons=[],touchGamepadStick=vec2();function touchGamepadCreate(){touchGamepadEnable&&isTouchDevice&&(ontouchstart=ontouchmove=ontouchend=a=>{if(touchGamepadEnable){touchGamepadStick=vec2();touchGamepadButtons=[];if(a.touches.length&&(touchGamepadTimer.isSet()||zzfx(0),isUsingGamepad=1,touchGamepadTimer.set(),paused)){touchGamepadButtons[9]=1;return}var b=vec2(touchGamepadSize,mainCanvasSize.y-touchGamepadSize),c=mainCanvasSize.subtract(vec2(touchGamepadSize,touchGamepadSize)),d=mainCanvasSize.scale(.5);for(const e of a.touches)a=mouseToScreen(vec2(e.clientX,e.clientY)),a.distance(b)<touchGamepadSize?touchGamepadAnalog?touchGamepadStick=a.subtract(b).scale(2/touchGamepadSize).clampLength():(a=a.subtract(b).angle(),touchGamepadStick.setAngle((4*a/PI+8.5|0)*PI/4)):a.distance(c)<touchGamepadSize?(a=a.subtract(c).direction(),touchGamepadButtons[a]=1):a.distance(d)<touchGamepadSize&&(touchGamepadButtons[9]=1)}})}function touchGamepadRender(){if(touchGamepadEnable&&touchGamepadTimer.isSet()){var a=percent(touchGamepadTimer.get(),4,3);if(a&&!paused){overlayContext.save();overlayContext.globalAlpha=a*touchGamepadAlpha;overlayContext.strokeStyle="#fff";overlayContext.lineWidth=3;overlayContext.fillStyle=0<touchGamepadStick.lengthSquared()?"#fff":"#000";overlayContext.beginPath();a=vec2(touchGamepadSize,mainCanvasSize.y-touchGamepadSize);if(touchGamepadAnalog)overlayContext.arc(a.x,a.y,touchGamepadSize/2,0,9),overlayContext.fill();else for(var b=10;b--;){var c=b*PI/4;overlayContext.arc(a.x,a.y,.6*touchGamepadSize,c+PI/8,c+PI/8);b%2&&overlayContext.arc(a.x,a.y,.33*touchGamepadSize,c,c);1==b&&overlayContext.fill()}overlayContext.stroke();a=vec2(mainCanvasSize.x-touchGamepadSize,mainCanvasSize.y-touchGamepadSize);for(b=4;b--;)c=a.add((new Vector2).setAngle(b*PI/2,touchGamepadSize/2)),overlayContext.fillStyle=touchGamepadButtons[b]?"#fff":"#000",overlayContext.beginPath(),overlayContext.arc(c.x,c.y,touchGamepadSize/4,0,9),overlayContext.fill(),overlayContext.stroke();overlayContext.restore()}}}"use strict";class Sound{constructor(a,b=soundDefaultRange,c=soundDefaultTaper){soundEnable&&(this.range=b,this.taper=c,this.randomness=a[1]||0,a[1]=0,this.cachedSamples=zzfxG(...a))}play(a,b=1,c=1,d=1){if(soundEnable){var e=0;if(a){if(e=this.range){const f=cameraPos.distanceSquared(a);if(f>e*e)return;b*=percent(f**.5,e,e*this.taper)}e=2*worldToScreen(a).x/mainCanvas.width-1}a=c+c*this.randomness*d*rand(-1,1);return playSamples([this.cachedSamples],b,a,e)}}playNote(a,b,c=1){if(soundEnable)return this.play(b,c,2**(a/12),0)}}class Music{constructor(a){soundEnable&&(this.cachedSamples=zzfxM(...a))}play(a=1,b=1){if(soundEnable)return playSamples(this.cachedSamples,a,1,0,b)}}function playAudioFile(a,b=1,c=1){if(soundEnable)return a=new Audio(a),a.volume=soundVolume*b,a.loop=c,a.play(),a}function speak(a,b="",c=1,d=1,e=1){if(soundEnable&&speechSynthesis)return a=new SpeechSynthesisUtterance(a),a.lang=b,a.volume=2*c*soundVolume,a.rate=d,a.pitch=e,speechSynthesis.speak(a),a}const speakStop=()=>speechSynthesis&&speechSynthesis.cancel(),getNoteFrequency=(a,b=220)=>b*2**(a/12);let audioContext;function playSamples(a,b=1,c=1,d=0,e=0){if(soundEnable&&(audioContext||(audioContext=new(window.AudioContext||webkitAudioContext)),audioContext.resume(),"running"==audioContext.state)){var f=audioContext.createBuffer(a.length,a[0].length,zzfxR),h=audioContext.createBufferSource();a.forEach((g,k)=>f.getChannelData(k).set(g));h.buffer=f;h.playbackRate.value=c;h.loop=e;a=audioContext.createGain();a.gain.value=soundVolume*b;a.connect(audioContext.destination);(window.StereoPannerNode?h.connect(new StereoPannerNode(audioContext,{pan:clamp(d,-1,1)})):h).connect(a);h.start();return h}}const zzfx=(...a)=>playSamples([zzfxG(...a)]),zzfxR=44100;function zzfxG(a=1,b=.05,c=220,d=0,e=0,f=.1,h=0,g=1,k=0,m=0,n=0,p=0,l=0,u=0,v=0,A=0,r=0,B=1,y=0,C=0){let t=2*PI,F=k*=500*t/zzfxR/zzfxR,z=[];b=c*=(1+b*rand(-1,1))*t/zzfxR;let w=0,D=0,q=0,x=1,H=0,I=0,E=0,J,G;d=d*zzfxR+9;y*=zzfxR;e*=zzfxR;f*=zzfxR;r*=zzfxR;m*=500*t/zzfxR**3;v*=t/zzfxR;n*=t/zzfxR;p*=zzfxR;l=l*zzfxR|0;for(G=d+y+e+f+r|0;q<G;z[q++]=E)++I%(100*A|0)||(E=h?1<h?2<h?3<h?Math.sin((w%t)**3):max(min(Math.tan(w),1),-1):1-(2*w/t%2+2)%2:1-4*abs(Math.round(w/t)-w/t):Math.sin(w),E=(l?1-C+C*Math.sin(t*q/l):1)*sign(E)*abs(E)**g*a*soundVolume*(q<d?q/d:q<d+y?1-(q-d)/y*(1-B):q<d+y+e?B:q<G-r?(G-q-r)/f*B:0),E=r?E/2+(r>q?0:(q<G-r?1:(G-q)/r)*z[q-r|0]/2):E),J=(c+=k+=m)*Math.cos(v*D++),w+=J-J*u*(1-1e9*(Math.sin(q)+1)%2),x&&++x>p&&(c+=n,b+=n,x=0),!l||++H%l||(c=b,k=F,x=x||1);return z}function zzfxM(a,b,c,d=125){let e,f,h,g,k,m,n,p,l,u,v,A,r,B=0,y,C=[],t=[],F=[],z=0,w=0,D=1,q={},x=zzfxR/d*60>>2;for(;D;z++)C=[D=p=A=0],c.forEach((H,I)=>{n=b[H][z]||[0,0,0];D|=!!b[H][z];y=A+(b[H][0].length-2-!p)*x;r=I==c.length-1;f=2;for(g=A;f<n.length+r;p=++f){k=n[f];l=f==n.length+r-1&&r||u!=(n[0]||0)|k|0;for(h=0;h<x&&p;h++>x-99&&l?v+=(1>v)/99:0)m=(1-v)*C[B++]/2||0,t[g]=(t[g]||0)-m*w+m,F[g]=(F[g++]||0)+m*w+m;k&&(v=k%1,w=n[1]||0,k|=0)&&(C=q[[u=n[B=0]||0,k]]=q[[u,k]]||(e=[...a[u]],e[2]*=2**((k-12)/12),0<k?zzfxG(...e):[]))}A=y});return[t,F]}"use strict";let tileCollision=[],tileCollisionSize=vec2();function initTileCollision(a){tileCollisionSize=a;tileCollision=[];for(a=tileCollision.length=tileCollisionSize.area();a--;)tileCollision[a]=0}const setTileCollisionData=(a,b=0)=>a.arrayCheck(tileCollisionSize)&&(tileCollision[(a.y|0)*tileCollisionSize.x+a.x|0]=b),getTileCollisionData=a=>a.arrayCheck(tileCollisionSize)?tileCollision[(a.y|0)*tileCollisionSize.x+a.x|0]:0;function tileCollisionTest(a,b=vec2(),c){const d=max(a.x-b.x/2|0,0);var e=max(a.y-b.y/2|0,0);const f=min(a.x+b.x/2,tileCollisionSize.x);for(a=min(a.y+b.y/2,tileCollisionSize.y);e<a;++e)for(b=d;b<f;++b){const h=tileCollision[e*tileCollisionSize.x+b];if(h&&(!c||c.collideWithTile(h,new Vector2(b,e))))return 1}}function tileCollisionRaycast(a,b,c){a=a.floor();b=b.floor();var d=b.subtract(a);const e=abs(d.x),f=-abs(d.y),h=sign(d.x);d=sign(d.y);let g=e+f;for(let m=a.x,n=a.y;;){var k=getTileCollisionData(vec2(m,n));if(k&&(c?c.collideWithTileRaycast(k,new Vector2(m,n)):0<k))return debugRaycast&&debugLine(a,b,"#f00",.02,1),debugRaycast&&debugPoint(new Vector2(m+.5,n+.5),"#ff0",1),new Vector2(m+.5,n+.5);if(m==b.x&n==b.y)break;k=2*g;k>=f&&(g+=f,m+=h);k<=e&&(g+=e,n+=d)}debugRaycast&&debugLine(a,b,"#00f",.02,1)}class TileLayerData{constructor(a,b=0,c=0,d=new Color){this.tile=a;this.direction=b;this.mirror=c;this.color=d}clear(){this.tile=this.direction=this.mirror=0;color=new Color}}class TileLayer extends EngineObject{constructor(a,b=tileCollisionSize,c=tileSizeDefault,d=vec2(1),e=0){super(a,b,-1,c,0,void 0,e);this.canvas=document.createElement("canvas");this.context=this.canvas.getContext("2d");this.scale=d;this.isOverlay;this.data=[];for(a=this.size.area();a--;)this.data.push(new TileLayerData)}setData(a,b,c){a.arrayCheck(this.size)&&(this.data[(a.y|0)*this.size.x+a.x|0]=b,c&&this.drawTileData(a))}getData(a){return a.arrayCheck(this.size)&&this.data[(a.y|0)*this.size.x+a.x|0]}update(){}render(){ASSERT(mainContext!=this.context);glEnable&&!glOverlay&&!this.isOverlay&&glCopyToContext(mainContext);const a=worldToScreen(this.pos.add(vec2(0,this.size.y*this.scale.y)));(this.isOverlay?overlayContext:mainContext).drawImage(this.canvas,a.x,a.y,cameraScale*this.size.x*this.scale.x,cameraScale*this.size.y*this.scale.y)}redraw(){this.redrawStart(1);this.drawAllTileData();this.redrawEnd()}redrawStart(a=0){a&&(this.canvas.width=this.size.x*this.tileSize.x,this.canvas.height=this.size.y*this.tileSize.y);this.savedRenderSettings=[mainCanvas,mainContext,cameraPos,cameraScale];mainCanvas=this.canvas;mainContext=this.context;cameraPos=this.size.scale(.5);cameraScale=this.tileSize.x;enginePreRender()}redrawEnd(){ASSERT(mainContext==this.context);glCopyToContext(mainContext,1);[mainCanvas,mainContext,cameraPos,cameraScale]=this.savedRenderSettings}drawTileData(a){const b=a.floor().add(this.pos).add(vec2(.5));this.drawCanvas2D(b,vec2(1),0,0,c=>c.clearRect(-.5,-.5,1,1));a=this.getData(a);void 0!=a.tile&&(ASSERT(mainContext==this.context),drawTile(b,vec2(1),a.tile,this.tileSize,a.color,a.direction*PI/2,a.mirror))}drawAllTileData(){for(let a=this.size.x;a--;)for(let b=this.size.y;b--;)this.drawTileData(vec2(a,b))}drawCanvas2D(a,b,c=0,d,e){const f=this.context;f.save();a=a.subtract(this.pos).multiply(this.tileSize);b=b.multiply(this.tileSize);f.translate(a.x,this.canvas.height-a.y);f.rotate(c);f.scale(d?-b.x:b.x,b.y);e(f);f.restore()}drawTile(a,b=vec2(1),c=-1,d=tileSizeDefault,e=new Color,f,h){this.drawCanvas2D(a,b,f,h,g=>{if(0>c)g.fillStyle=e,g.fillRect(-.5,-.5,1,1);else{const k=tileImage.width/d.x;g.globalAlpha=e.a;g.drawImage(tileImage,c%k*d.x,(c/k|0)*d.x,d.x,d.y,-.5,-.5,1,1)}})}drawRect(a,b,c,d){this.drawTile(a,b,-1,0,c,d)}}"use strict";class ParticleEmitter extends EngineObject{constructor(a,b,c=0,d=0,e=100,f=PI,h=-1,g=tileSizeDefault,k=new Color,m=new Color,n=new Color(1,1,1,0),p=new Color(1,1,1,0),l=.5,u=.1,v=1,A=.1,r=.05,B=1,y=1,C=0,t=PI,F=.1,z=.2,w,D,q=1,x=D?1e9:0){super(a,new Vector2,h,g,b,void 0,x);this.emitSize=c;this.emitTime=d;this.emitRate=e;this.emitConeAngle=f;this.colorStartA=k;this.colorStartB=m;this.colorEndA=n;this.colorEndB=p;this.randomColorLinear=q;this.particleTime=l;this.sizeStart=u;this.sizeEnd=v;this.speed=A;this.angleSpeed=r;this.damping=B;this.angleDamping=y;this.gravityScale=C;this.particleConeAngle=t;this.fadeRate=F;this.randomness=z;this.collideTiles=w;this.additive=D;this.emitTimeBuffer=this.trailScale=0}update(){this.parent&&super.update();if(!this.emitTime||this.getAliveTime()<=this.emitTime){if(this.emitRate*particleEmitRateScale){const a=1/this.emitRate/particleEmitRateScale;for(this.emitTimeBuffer+=timeDelta;0<this.emitTimeBuffer;this.emitTimeBuffer-=a)this.emitParticle()}}else this.destroy();debugParticles&&debugRect(this.pos,vec2(this.emitSize),"#0f0",0,this.angle)}emitParticle(){var a=void 0!=this.emitSize.x?new Vector2(rand(-.5,.5),rand(-.5,.5)).multiply(this.emitSize).rotate(this.angle):randInCircle(.5*this.emitSize);a=new Particle(this.pos.add(a),this.tileIndex,this.tileSize,this.angle+rand(this.particleConeAngle,-this.particleConeAngle));const b=this.randomness;var c=n=>n+n*rand(b,-b);const d=c(this.particleTime),e=c(this.sizeStart),f=c(this.sizeEnd),h=c(this.speed);c=c(this.angleSpeed)*randSign();const g=rand(this.emitConeAngle,-this.emitConeAngle),k=randColor(this.colorStartA,this.colorStartB,this.randomColorLinear),m=randColor(this.colorEndA,this.colorEndB,this.randomColorLinear);a.colorStart=k;a.colorEndDelta=m.subtract(k);a.velocity=(new Vector2).setAngle(this.angle+g,h);a.angleVelocity=c;a.lifeTime=d;a.sizeStart=e;a.sizeEndDelta=f-e;a.fadeRate=this.fadeRate;a.damping=this.damping;a.angleDamping=this.angleDamping;a.elasticity=this.elasticity;a.friction=this.friction;a.gravityScale=this.gravityScale;a.collideTiles=this.collideTiles;a.additive=this.additive;a.renderOrder=this.renderOrder;a.trailScale=this.trailScale;a.mirror=.5>rand();a.destroyCallback=this.particleDestroyCallback;this.particleCreateCallback&&this.particleCreateCallback(a);return a}render(){}}class Particle extends EngineObject{constructor(a,b,c,d){super(a,new Vector2,b,c,d)}render(){const a=min((time-this.spawnTime)/this.lifeTime,1);var b=this.sizeStart+a*this.sizeEndDelta;b=new Vector2(b,b);var c=this.fadeRate/2;c=new Color(this.colorStart.r+a*this.colorEndDelta.r,this.colorStart.g+a*this.colorEndDelta.g,this.colorStart.b+a*this.colorEndDelta.b,(this.colorStart.a+a*this.colorEndDelta.a)*(a<c?a/c:a>1-c?(1-a)/c:1));this.additive&&setBlendMode(1);if(this.trailScale){var d=this.velocity.length();const e=this.velocity.scale(1/d);d*=this.trailScale;b.y=max(b.x,d);this.angle=e.angle();drawTile(this.pos.add(e.multiply(vec2(0,-d/2))),b,this.tileIndex,this.tileSize,c,this.angle,this.mirror)}else drawTile(this.pos,b,this.tileIndex,this.tileSize,c,this.angle,this.mirror);this.additive&&setBlendMode();debugParticles&&debugRect(this.pos,b,"#f005",0,this.angle);1==a&&(this.color=c,this.size=b,this.destroyCallback&&this.destroyCallback(this),this.destroyed=1)}}"use strict";const medals=[];let medalsPreventUnlock,newgrounds,medalsDisplayQueue=[],medalsSaveName,medalsDisplayTimeLast;function medalsInit(a){medalsSaveName=a;debugMedals||medals.forEach(b=>localStorage[b.storageKey()])}class Medal{constructor(a,b,c="",d="🏆",e){ASSERT(0<=a&&!medals[a]);medals[this.id=a]=this;this.name=b;this.description=c;this.icon=d;this.image=new Image;e&&(this.image.src=e)}unlock(){medalsPreventUnlock||this.unlocked||(ASSERT(medalsSaveName),localStorage[this.storageKey()]=this.unlocked=1,medalsDisplayQueue.push(this),newgrounds&&newgrounds.unlockMedal(this.id),localStorage["OS13kTrophy,"+this.icon+","+medalsSaveName+","+this.name]=this.description)}render(a=0){const b=overlayContext,c=min(medalDisplayWidth,mainCanvas.width),d=overlayCanvas.width-c;a*=-medalDisplayHeight;b.save();b.beginPath();b.fillStyle="#ddd";b.fill(b.rect(d,a,c,medalDisplayHeight));b.strokeStyle="#000";b.lineWidth=3;b.stroke();b.clip();this.renderIcon(d+15+medalDisplayIconSize/2,a+medalDisplayHeight/2);b.textAlign="left";b.font="38px "+fontDefault;b.fillText(this.name,d+medalDisplayIconSize+30,a+28);b.font="24px "+fontDefault;b.fillText(this.description,d+medalDisplayIconSize+30,a+60);b.restore()}renderIcon(a,b,c=medalDisplayIconSize){const d=overlayContext;d.fillStyle="#000";d.textAlign="center";d.textBaseline="middle";d.font=.7*c+"px "+fontDefault;this.image.src?d.drawImage(this.image,a-c/2,b-c/2,c,c):d.fillText(this.icon,a,b)}storageKey(){return medalsSaveName+"_"+this.id}}function medalsRender(){if(medalsDisplayQueue.length){var a=medalsDisplayQueue[0],b=timeReal-medalsDisplayTimeLast;if(medalsDisplayTimeLast)if(b>medalDisplayTime)medalsDisplayQueue.shift(medalsDisplayTimeLast=0);else{const c=medalDisplayTime-medalDisplaySlideTime;a.render(b<medalDisplaySlideTime?1-b/medalDisplaySlideTime:b>c?(b-c)/medalDisplaySlideTime:0)}else medalsDisplayTimeLast=timeReal}}class Newgrounds{constructor(a,b){ASSERT(!newgrounds&&a);this.app_id=a;this.cipher=b;this.host=location?location.hostname:"";b&&(this.cryptoJS=CryptoJS());this.session_id=new URL(location.href).searchParams.get("ngio_session_id")||0;if(0!=this.session_id){this.medals=(a=this.call("Medal.getList"))?a.result.data.medals:[];debugMedals&&console.log(this.medals);for(var c of this.medals)if(a=medals[c.id])a.image.src=c.icon,a.name=c.name,a.description=c.description,a.unlocked=c.unlocked,a.difficulty=c.difficulty,a.value=c.value,a.value&&(a.description=a.description+" ("+a.value+")");this.scoreboards=(c=this.call("ScoreBoard.getBoards"))?c.result.data.scoreboards:[];debugMedals&&console.log(this.scoreboards);setInterval(()=>this.call("Gateway.ping",0,1),3e5)}}unlockMedal(a){return this.call("Medal.unlock",{id:a},1)}postScore(a,b){return this.call("ScoreBoard.postScore",{id:a,value:b},1)}getScores(a,b=0,c=0,d=0,e=10){return this.call("ScoreBoard.getScores",{id:a,user:b,social:c,skip:d,limit:e})}logView(){return this.call("App.logView",{host:this.host},1)}call(a,b=0,c=0){a={component:a,parameters:b};if(this.cipher){b=this.cryptoJS;var d=b.enc.Base64.parse(this.cipher);const e=b.lib.WordArray.random(16);d=b.AES.encrypt(JSON.stringify(a),d,{iv:e});a.secure=b.enc.Base64.stringify(e.concat(d.ciphertext));a.parameters=0}b={app_id:this.app_id,session_id:this.session_id,call:a};a=new FormData;a.append("input",JSON.stringify(b));b=new XMLHttpRequest;b.open("POST","https://newgrounds.io/gateway_v3.php",!debugMedals&&c);b.send(a);debugMedals&&console.log(b.responseText);return b.responseText&&JSON.parse(b.responseText)}}const CryptoJS=()=>eval(Function("[M='GBMGXz^oVYPPKKbB`agTXU|LxPc_ZBcMrZvCr~wyGfWrwk@ATqlqeTp^N?p{we}jIpEnB_sEr`l?YDkDhWhprc|Er|XETG?pTl`e}dIc[_N~}fzRycIfpW{HTolvoPB_FMe_eH~BTMx]yyOhv?biWPCGc]kABencBhgERHGf{OL`Dj`c^sh@canhy[secghiyotcdOWgO{tJIE^JtdGQRNSCrwKYciZOa]Y@tcRATYKzv|sXpboHcbCBf`}SKeXPFM|RiJsSNaIb]QPc[D]Jy_O^XkOVTZep`ONmntLL`Qz~UupHBX_Ia~WX]yTRJIxG`ioZ{fefLJFhdyYoyLPvqgH?b`[TMnTwwfzDXhfM?rKs^aFr|nyBdPmVHTtAjXoYUloEziWDCw_suyYT~lSMksI~ZNCS[Bex~j]Vz?kx`gdYSEMCsHpjbyxQvw|XxX_^nQYue{sBzVWQKYndtYQMWRef{bOHSfQhiNdtR{o?cUAHQAABThwHPT}F{VvFmgN`E@FiFYS`UJmpQNM`X|tPKHlccT}z}k{sACHL?Rt@MkWplxO`ASgh?hBsuuP|xD~LSH~KBlRs]t|l|_tQAroDRqWS^SEr[sYdPB}TAROtW{mIkE|dWOuLgLmJrucGLpebrAFKWjikTUzS|j}M}szasKOmrjy[?hpwnEfX[jGpLt@^v_eNwSQHNwtOtDgWD{rk|UgASs@mziIXrsHN_|hZuxXlPJOsA^^?QY^yGoCBx{ekLuZzRqQZdsNSx@ezDAn{XNj@fRXIwrDX?{ZQHwTEfu@GhxDOykqts|n{jOeZ@c`dvTY?e^]ATvWpb?SVyg]GC?SlzteilZJAL]mlhLjYZazY__qcVFYvt@|bIQnSno@OXyt]OulzkWqH`rYFWrwGs`v|~XeTsIssLrbmHZCYHiJrX}eEzSssH}]l]IhPQhPoQ}rCXLyhFIT[clhzYOvyHqigxmjz`phKUU^TPf[GRAIhNqSOdayFP@FmKmuIzMOeoqdpxyCOwCthcLq?n`L`tLIBboNn~uXeFcPE{C~mC`h]jUUUQe^`UqvzCutYCgct|SBrAeiYQW?X~KzCz}guXbsUw?pLsg@hDArw?KeJD[BN?GD@wgFWCiHq@Ypp_QKFixEKWqRp]oJFuVIEvjDcTFu~Zz]a{IcXhWuIdMQjJ]lwmGQ|]g~c]Hl]pl`Pd^?loIcsoNir_kikBYyg?NarXZEGYspt_vLBIoj}LI[uBFvm}tbqvC|xyR~a{kob|HlctZslTGtPDhBKsNsoZPuH`U`Fqg{gKnGSHVLJ^O`zmNgMn~{rsQuoymw^JY?iUBvw_~mMr|GrPHTERS[MiNpY[Mm{ggHpzRaJaoFomtdaQ_?xuTRm}@KjU~RtPsAdxa|uHmy}n^i||FVL[eQAPrWfLm^ndczgF~Nk~aplQvTUpHvnTya]kOenZlLAQIm{lPl@CCTchvCF[fI{^zPkeYZTiamoEcKmBMfZhk_j_~Fjp|wPVZlkh_nHu]@tP|hS@^G^PdsQ~f[RqgTDqezxNFcaO}HZhb|MMiNSYSAnQWCDJukT~e|OTgc}sf[cnr?fyzTa|EwEtRG|I~|IO}O]S|rp]CQ}}DWhSjC_|z|oY|FYl@WkCOoPuWuqr{fJu?Brs^_EBI[@_OCKs}?]O`jnDiXBvaIWhhMAQDNb{U`bqVR}oqVAvR@AZHEBY@depD]OLh`kf^UsHhzKT}CS}HQKy}Q~AeMydXPQztWSSzDnghULQgMAmbWIZ|lWWeEXrE^EeNoZApooEmrXe{NAnoDf`m}UNlRdqQ@jOc~HLOMWs]IDqJHYoMziEedGBPOxOb?[X`KxkFRg@`mgFYnP{hSaxwZfBQqTm}_?RSEaQga]w[vxc]hMne}VfSlqUeMo_iqmd`ilnJXnhdj^EEFifvZyxYFRf^VaqBhLyrGlk~qowqzHOBlOwtx?i{m~`n^G?Yxzxux}b{LSlx]dS~thO^lYE}bzKmUEzwW^{rPGhbEov[Plv??xtyKJshbG`KuO?hjBdS@Ru}iGpvFXJRrvOlrKN?`I_n_tplk}kgwSXuKylXbRQ]]?a|{xiT[li?k]CJpwy^o@ebyGQrPfF`aszGKp]baIx~H?ElETtFh]dz[OjGl@C?]VDhr}OE@V]wLTc[WErXacM{We`F|utKKjgllAxvsVYBZ@HcuMgLboFHVZmi}eIXAIFhS@A@FGRbjeoJWZ_NKd^oEH`qgy`q[Tq{x?LRP|GfBFFJV|fgZs`MLbpPYUdIV^]mD@FG]pYAT^A^RNCcXVrPsgk{jTrAIQPs_`mD}rOqAZA[}RETFz]WkXFTz_m{N@{W@_fPKZLT`@aIqf|L^Mb|crNqZ{BVsijzpGPEKQQZGlApDn`ruH}cvF|iXcNqK}cxe_U~HRnKV}sCYb`D~oGvwG[Ca|UaybXea~DdD~LiIbGRxJ_VGheI{ika}KC[OZJLn^IBkPrQj_EuoFwZ}DpoBRcK]Q}?EmTv~i_Tul{bky?Iit~tgS|o}JL_VYcCQdjeJ_MfaA`FgCgc[Ii|CBHwq~nbJeYTK{e`CNstKfTKPzw{jdhp|qsZyP_FcugxCFNpKitlR~vUrx^NrSVsSTaEgnxZTmKc`R|lGJeX}ccKLsQZQhsFkeFd|ckHIVTlGMg`~uPwuHRJS_CPuN_ogXe{Ba}dO_UBhuNXby|h?JlgBIqMKx^_u{molgL[W_iavNQuOq?ap]PGB`clAicnl@k~pA?MWHEZ{HuTLsCpOxxrKlBh]FyMjLdFl|nMIvTHyGAlPogqfZ?PlvlFJvYnDQd}R@uAhtJmDfe|iJqdkYr}r@mEjjIetDl_I`TELfoR|qTBu@Tic[BaXjP?dCS~MUK[HPRI}OUOwAaf|_}HZzrwXvbnNgltjTwkBE~MztTQhtRSWoQHajMoVyBBA`kdgK~h`o[J`dm~pm]tk@i`[F~F]DBlJKklrkR]SNw@{aG~Vhl`KINsQkOy?WhcqUMTGDOM_]bUjVd|Yh_KUCCgIJ|LDIGZCPls{RzbVWVLEhHvWBzKq|^N?DyJB|__aCUjoEgsARki}j@DQXS`RNU|DJ^a~d{sh_Iu{ONcUtSrGWW@cvUjefHHi}eSSGrNtO?cTPBShLqzwMVjWQQCCFB^culBjZHEK_{dO~Q`YhJYFn]jq~XSnG@[lQr]eKrjXpG~L^h~tDgEma^AUFThlaR{xyuP@[^VFwXSeUbVetufa@dX]CLyAnDV@Bs[DnpeghJw^?UIana}r_CKGDySoRudklbgio}kIDpA@McDoPK?iYcG?_zOmnWfJp}a[JLR[stXMo?_^Ng[whQlrDbrawZeSZ~SJstIObdDSfAA{MV}?gNunLOnbMv_~KFQUAjIMj^GkoGxuYtYbGDImEYiwEMyTpMxN_LSnSMdl{bg@dtAnAMvhDTBR_FxoQgANniRqxd`pWv@rFJ|mWNWmh[GMJz_Nq`BIN@KsjMPASXORcdHjf~rJfgZYe_uulzqM_KdPlMsuvU^YJuLtofPhGonVOQxCMuXliNvJIaoC?hSxcxKVVxWlNs^ENDvCtSmO~WxI[itnjs^RDvI@KqG}YekaSbTaB]ki]XM@[ZnDAP~@|BzLRgOzmjmPkRE@_sobkT|SszXK[rZN?F]Z_u}Yue^[BZgLtR}FHzWyxWEX^wXC]MJmiVbQuBzkgRcKGUhOvUc_bga|Tx`KEM`JWEgTpFYVeXLCm|mctZR@uKTDeUONPozBeIkrY`cz]]~WPGMUf`MNUGHDbxZuO{gmsKYkAGRPqjc|_FtblEOwy}dnwCHo]PJhN~JoteaJ?dmYZeB^Xd?X^pOKDbOMF@Ugg^hETLdhwlA}PL@_ur|o{VZosP?ntJ_kG][g{Zq`Tu]dzQlSWiKfnxDnk}KOzp~tdFstMobmy[oPYjyOtUzMWdjcNSUAjRuqhLS@AwB^{BFnqjCmmlk?jpn}TksS{KcKkDboXiwK]qMVjm~V`LgWhjS^nLGwfhAYrjDSBL_{cRus~{?xar_xqPlArrYFd?pHKdMEZzzjJpfC?Hv}mAuIDkyBxFpxhstTx`IO{rp}XGuQ]VtbHerlRc_LFGWK[XluFcNGUtDYMZny[M^nVKVeMllQI[xtvwQnXFlWYqxZZFp_|]^oWX[{pOMpxXxvkbyJA[DrPzwD|LW|QcV{Nw~U^dgguSpG]ClmO@j_TENIGjPWwgdVbHganhM?ema|dBaqla|WBd`poj~klxaasKxGG^xbWquAl~_lKWxUkDFagMnE{zHug{b`A~IYcQYBF_E}wiA}K@yxWHrZ{[d~|ARsYsjeNWzkMs~IOqqp[yzDE|WFrivsidTcnbHFRoW@XpAV`lv_zj?B~tPCppRjgbbDTALeFaOf?VcjnKTQMLyp{NwdylHCqmo?oelhjWuXj~}{fpuX`fra?GNkDiChYgVSh{R[BgF~eQa^WVz}ATI_CpY?g_diae]|ijH`TyNIF}|D_xpmBq_JpKih{Ba|sWzhnAoyraiDvk`h{qbBfsylBGmRH}DRPdryEsSaKS~tIaeF[s]I~xxHVrcNe@Jjxa@jlhZueLQqHh_]twVMqG_EGuwyab{nxOF?`HCle}nBZzlTQjkLmoXbXhOtBglFoMz?eqre`HiE@vNwBulglmQjj]DB@pPkPUgA^sjOAUNdSu_`oAzar?n?eMnw{{hYmslYi[TnlJD'",..."]charCodeAtUinyxpf","for(;e<10359;c[e++]=p-=128,A=A?p-A&&A:p==34&&p)for(p=1;p<128;y=f.map((n,x)=>(U=r[n]*2+1,U=Math.log(U/(h-U)),t-=a[x]*U,U/500)),t=~-h/(1+Math.exp(t))|1,i=o%h<t,o=o%h+(i?t:h-t)*(o>>17)-!i*t,f.map((n,x)=>(U=r[n]+=(i*h/2-r[n]<<13)/((C[n]+=C[n]<5)+1/20)>>13,a[x]+=y[x]*(i-t/h))),p=p*2+i)for(f='010202103203210431053105410642065206541'.split(t=0).map((n,x)=>(U=0,[...n].map((n,x)=>(U=U*997+(c[e-n]|0)|0)),h*32-1&U*997+p+!!A*129)*12+x);o<h*32;o=o*64|M.charCodeAt(d++)&63);for(C=String.fromCharCode(...c);r=/[\0-#?@\\\\~]/.exec(C);)with(C.split(r))C=join(shift());return C")([],[],131072,[0,0,0,0,0,0,0,0,0,0,0,0],new Uint16Array(51e6).fill(32768),new Uint8Array(51e6),0,0,0,0));"use strict";let glCanvas,glContext,glTileTexture,glActiveTexture,glShader,glPositionData,glColorData,glBatchCount,glBatchAdditive,glAdditive;function glInit(){if(glEnable){glCanvas=document.createElement("canvas");glContext=glCanvas.getContext("webgl",{antialias:!1});glCanvas.style=styleCanvas;glTileTexture=glCreateTexture(tileImage);glOverlay&&document.body.appendChild(glCanvas);glShader=glCreateProgram("precision highp float;uniform mat4 m;attribute vec2 p,t;attribute vec4 c,a;varying vec2 v;varying vec4 d,e;void main(){gl_Position=m*vec4(p,1,1);v=t;d=c;e=a;}","precision highp float;varying vec2 v;varying vec4 d,e;uniform sampler2D s;void main(){gl_FragColor=texture2D(s,v)*d+e;}");var a=new ArrayBuffer(gl_MAX_BATCH*gl_VERTICES_PER_QUAD*gl_VERTEX_BYTE_STRIDE);glCreateBuffer(gl_ARRAY_BUFFER,a.byteLength,gl_DYNAMIC_DRAW);glPositionData=new Float32Array(a);glColorData=new Uint32Array(a);var b=glBatchCount=0;a=(c,d,e,f,h=0)=>{c=glContext.getAttribLocation(glShader,c);glContext.enableVertexAttribArray(c);glContext.vertexAttribPointer(c,f,d,h,gl_VERTEX_BYTE_STRIDE,b);b+=f*e};a("p",gl_FLOAT,4,2);a("t",gl_FLOAT,4,2);a("c",gl_UNSIGNED_BYTE,1,4,1);a("a",gl_UNSIGNED_BYTE,1,4,1)}}function glSetBlendMode(a){glEnable&&(glAdditive=a)}function glSetTexture(a=glTileTexture){glEnable&&(a!=glActiveTexture&&glFlush(),glContext.bindTexture(gl_TEXTURE_2D,glActiveTexture=a))}function glCompileShader(a,b){if(glEnable){b=glContext.createShader(b);glContext.shaderSource(b,a);glContext.compileShader(b);if(debug&&!glContext.getShaderParameter(b,gl_COMPILE_STATUS))throw glContext.getShaderInfoLog(b);return b}}function glCreateProgram(a,b){if(glEnable){var c=glContext.createProgram();glContext.attachShader(c,glCompileShader(a,gl_VERTEX_SHADER));glContext.attachShader(c,glCompileShader(b,gl_FRAGMENT_SHADER));glContext.linkProgram(c);if(debug&&!glContext.getProgramParameter(c,gl_LINK_STATUS))throw glContext.getProgramInfoLog(c);return c}}function glCreateBuffer(a,b,c){if(glEnable){var d=glContext.createBuffer();glContext.bindBuffer(a,d);glContext.bufferData(a,b,c);return d}}function glCreateTexture(a){if(glEnable&&a&&a.width){var b=glContext.createTexture();glContext.bindTexture(gl_TEXTURE_2D,b);glContext.texImage2D(gl_TEXTURE_2D,0,gl_RGBA,gl_RGBA,gl_UNSIGNED_BYTE,a);glContext.texParameteri(gl_TEXTURE_2D,gl_TEXTURE_MIN_FILTER,cavasPixelated?gl_NEAREST:gl_LINEAR);glContext.texParameteri(gl_TEXTURE_2D,gl_TEXTURE_MAG_FILTER,cavasPixelated?gl_NEAREST:gl_LINEAR);glContext.texParameteri(gl_TEXTURE_2D,gl_TEXTURE_WRAP_S,gl_CLAMP_TO_EDGE);glContext.texParameteri(gl_TEXTURE_2D,gl_TEXTURE_WRAP_T,gl_CLAMP_TO_EDGE);return b}}function glPreRender(a,b,c,d,e){glEnable&&(glContext.viewport(0,0,glCanvas.width=a,glCanvas.height=b),glContext.clear(gl_COLOR_BUFFER_BIT),glContext.bindTexture(gl_TEXTURE_2D,glActiveTexture=glTileTexture),glContext.useProgram(glShader),glSetBlendMode(),a=2*e/a,b=2*e/b,glContext.uniformMatrix4fv(glContext.getUniformLocation(glShader,"m"),0,new Float32Array([a,0,0,0,0,b,0,0,1,1,-1,1,-1-a*c,-1-b*d,0,0])))}function glFlush(){if(glEnable&&glBatchCount){var a=glBatchAdditive?gl_ONE:gl_ONE_MINUS_SRC_ALPHA;glContext.blendFuncSeparate(gl_SRC_ALPHA,a,gl_ONE,a);glContext.enable(gl_BLEND);glContext.bufferSubData(gl_ARRAY_BUFFER,0,glPositionData.subarray(0,glBatchCount*gl_VERTICES_PER_QUAD*gl_INDICIES_PER_VERT));glContext.drawArrays(gl_TRIANGLES,0,glBatchCount*gl_VERTICES_PER_QUAD);glBatchCount=0;glBatchAdditive=glAdditive}}function glCopyToContext(a,b){if(glEnable&&glBatchCount||b)glFlush(),glOverlay&&!b||a.drawImage(glCanvas,0,0)}function glDraw(a,b,c,d,e,f,h,g,k,m=4294967295,n=0){if(glEnable){glBatchCount!=gl_MAX_BATCH&&glBatchAdditive==glAdditive||glFlush();var p=Math.cos(e)/2,l=Math.sin(e)/2;e=p*c;p*=d;c*=l;d*=l;l=glBatchCount++*gl_VERTICES_PER_QUAD*gl_INDICIES_PER_VERT;glPositionData[l++]=a-e-d;glPositionData[l++]=b-p+c;glPositionData[l++]=f;glPositionData[l++]=k;glColorData[l++]=m;glColorData[l++]=n;glPositionData[l++]=a+e+d;glPositionData[l++]=b+p-c;glPositionData[l++]=g;glPositionData[l++]=h;glColorData[l++]=m;glColorData[l++]=n;glPositionData[l++]=a-e+d;glPositionData[l++]=b+p+c;glPositionData[l++]=f;glPositionData[l++]=h;glColorData[l++]=m;glColorData[l++]=n;glPositionData[l++]=a-e-d;glPositionData[l++]=b-p+c;glPositionData[l++]=f;glPositionData[l++]=k;glColorData[l++]=m;glColorData[l++]=n;glPositionData[l++]=a+e-d;glPositionData[l++]=b-p-c;glPositionData[l++]=g;glPositionData[l++]=k;glColorData[l++]=m;glColorData[l++]=n;glPositionData[l++]=a+e+d;glPositionData[l++]=b+p-c;glPositionData[l++]=g;glPositionData[l++]=h;glColorData[l++]=m;glColorData[l++]=n}}const gl_ONE=1,gl_TRIANGLES=4,gl_SRC_ALPHA=770,gl_ONE_MINUS_SRC_ALPHA=771,gl_BLEND=3042,gl_TEXTURE_2D=3553,gl_UNSIGNED_BYTE=5121,gl_FLOAT=5126,gl_RGBA=6408,gl_NEAREST=9728,gl_LINEAR=9729,gl_TEXTURE_MAG_FILTER=10240,gl_TEXTURE_MIN_FILTER=10241,gl_TEXTURE_WRAP_S=10242,gl_TEXTURE_WRAP_T=10243,gl_COLOR_BUFFER_BIT=16384,gl_CLAMP_TO_EDGE=33071,gl_ARRAY_BUFFER=34962,gl_DYNAMIC_DRAW=35048,gl_FRAGMENT_SHADER=35632,gl_VERTEX_SHADER=35633,gl_COMPILE_STATUS=35713,gl_LINK_STATUS=35714,gl_VERTICES_PER_QUAD=6,gl_INDICIES_PER_VERT=6,gl_MAX_BATCH=65536,gl_VERTEX_BYTE_STRIDE=24;
|
|
1
|
+
let showWatermark=0,godMode=0;const debug=0,debugOverlay=0,debugPhysics=0,debugParticles=0,debugRaycast=0,debugGamepads=0,debugMedals=0,ASSERT=()=>{},debugInit=()=>{},debugUpdate=()=>{},debugRender=()=>{},debugRect=()=>{},debugCircle=()=>{},debugPoint=()=>{},debugLine=()=>{},debugAABB=()=>{},debugText=()=>{},debugClear=()=>{},debugSaveCanvas=()=>{};"use strict";const PI=Math.PI,abs=a=>0>a?-a:a,min=(a,b)=>a<b?a:b,max=(a,b)=>a>b?a:b,sign=a=>0>a?-1:1,mod=(a,b=1)=>(a%b+b)%b,clamp=(a,b=0,c=1)=>a<b?b:a>c?c:a,percent=(a,b=0,c=1)=>c-b?clamp((a-b)/(c-b)):0,lerp=(a,b=0,c=1)=>b+clamp(a)*(c-b),smoothStep=a=>a*a*(3-2*a),nearestPowerOfTwo=a=>2**Math.ceil(Math.log2(a)),isOverlapping=(a,b,c,d)=>2*abs(a.x-c.x)<b.x+d.x&2*abs(a.y-c.y)<b.y+d.y,wave=(a=1,b=1,c=time)=>b/2*(1-Math.cos(c*a*2*PI)),formatTime=a=>(a/60|0)+":"+(10>a%60?"0":"")+(a%60|0),rand=(a=1,b=0)=>b+(a-b)*Math.random(),randInt=(a=1,b=0)=>rand(a,b)|0,randSign=()=>2*(rand(2)|0)-1,randInCircle=(a=1,b=0)=>0<a?randVector(a*rand(b/a,1)**.5):new Vector2,randVector=(a=1)=>(new Vector2).setAngle(rand(2*PI),a),randColor=(a=new Color,b=new Color(0,0,0,1),c)=>c?a.lerp(b,rand()):new Color(rand(a.r,b.r),rand(a.g,b.g),rand(a.b,b.b),rand(a.a,b.a));let randSeed=1;const randSeeded=(a=1,b=0)=>{randSeed^=randSeed<<13;randSeed^=randSeed>>>17;randSeed^=randSeed<<5;return b+(a-b)*abs(randSeed%1e9)/1e9},vec2=(a=0,b)=>void 0==a.x?new Vector2(a,void 0==b?a:b):new Vector2(a.x,a.y);class Vector2{constructor(a=0,b=0){this.x=a;this.y=b}copy(){return new Vector2(this.x,this.y)}add(a){ASSERT(void 0!=a.x);return new Vector2(this.x+a.x,this.y+a.y)}subtract(a){ASSERT(void 0!=a.x);return new Vector2(this.x-a.x,this.y-a.y)}multiply(a){ASSERT(void 0!=a.x);return new Vector2(this.x*a.x,this.y*a.y)}divide(a){ASSERT(void 0!=a.x);return new Vector2(this.x/a.x,this.y/a.y)}scale(a){ASSERT(void 0==a.x);return new Vector2(this.x*a,this.y*a)}length(){return this.lengthSquared()**.5}lengthSquared(){return this.x**2+this.y**2}distance(a){return this.distanceSquared(a)**.5}distanceSquared(a){return(this.x-a.x)**2+(this.y-a.y)**2}normalize(a=1){const b=this.length();return b?this.scale(a/b):new Vector2(0,a)}clampLength(a=1){const b=this.length();return b>a?this.scale(a/b):this}dot(a){ASSERT(void 0!=a.x);return this.x*a.x+this.y*a.y}cross(a){ASSERT(void 0!=a.x);return this.x*a.y-this.y*a.x}angle(){return Math.atan2(this.x,this.y)}setAngle(a=0,b=1){this.x=b*Math.sin(a);this.y=b*Math.cos(a);return this}rotate(a){const b=Math.cos(a);a=Math.sin(a);return new Vector2(this.x*b-this.y*a,this.x*a+this.y*b)}direction(){return abs(this.x)>abs(this.y)?0>this.x?3:1:0>this.y?2:0}invert(){return new Vector2(this.y,-this.x)}floor(){return new Vector2(Math.floor(this.x),Math.floor(this.y))}area(){return abs(this.x*this.y)}lerp(a,b){ASSERT(void 0!=a.x);return this.add(a.subtract(this).scale(clamp(b)))}arrayCheck(a){return 0<=this.x&&0<=this.y&&this.x<a.x&&this.y<a.y}toString(a=3){return`(${(0>this.x?"":" ")+this.x.toFixed(a)},${(0>this.y?"":" ")+this.y.toFixed(a)} )`}}class Color{constructor(a=1,b=1,c=1,d=1){this.r=a;this.g=b;this.b=c;this.a=d}copy(){return new Color(this.r,this.g,this.b,this.a)}add(a){return new Color(this.r+a.r,this.g+a.g,this.b+a.b,this.a+a.a)}subtract(a){return new Color(this.r-a.r,this.g-a.g,this.b-a.b,this.a-a.a)}multiply(a){return new Color(this.r*a.r,this.g*a.g,this.b*a.b,this.a*a.a)}divide(a){return new Color(this.r/a.r,this.g/a.g,this.b/a.b,this.a/a.a)}scale(a,b=a){return new Color(this.r*a,this.g*a,this.b*a,this.a*b)}clamp(){return new Color(clamp(this.r),clamp(this.g),clamp(this.b),clamp(this.a))}lerp(a,b){return this.add(a.subtract(this).scale(clamp(b)))}setHSLA(a=0,b=0,c=1,d=1){b=.5>c?c*(1+b):c+b-c*b;c=2*c-b;const e=(f,h,g)=>(g=(g%1+1)%1)<1/6?f+6*(h-f)*g:.5>g?h:g<2/3?f+(h-f)*(2/3-g)*6:f;this.r=e(c,b,a+1/3);this.g=e(c,b,a);this.b=e(c,b,a-1/3);this.a=d;return this}getHSLA(){const a=this.r,b=this.g,c=this.b,d=this.a,e=Math.max(a,b,c),f=Math.min(a,b,c),h=(e+f)/2;let g=0,k=0;if(e!=f){let m=e-f;k=.5<h?m/(2-e-f):m/(e+f);a==e?g=(b-c)/m+(b<c?6:0):b==e?g=(c-a)/m+2:c==e&&(g=(a-b)/m+4)}return[g/6,k,h,d]}mutate(a=.05,b=0){return new Color(this.r+rand(a,-a),this.g+rand(a,-a),this.b+rand(a,-a),this.a+rand(b,-b)).clamp()}toString(){ASSERT(0<=this.r&&1>=this.r&&0<=this.g&&1>=this.g&&0<=this.b&&1>=this.b&&0<=this.a&&1>=this.a);return`rgb(${255*this.r|0},${255*this.g|0},${255*this.b|0},${this.a})`}rgbaInt(){ASSERT(0<=this.r&&1>=this.r&&0<=this.g&&1>=this.g&&0<=this.b&&1>=this.b&&0<=this.a&&1>=this.a);return(255*this.r|0)+(255*this.g<<8)+(255*this.b<<16)+(255*this.a<<24)}setHex(a){this.r=parseInt(a.slice(1,3),16)/255;this.g=parseInt(a.slice(3,5),16)/255;this.b=parseInt(a.slice(5,7),16)/255;this.a=1;ASSERT(0<=this.r&&1>=this.r&&0<=this.g&&1>=this.g&&0<=this.b&&1>=this.b);return this}getHex(){const a=b=>(16>(b=255*b|0)?"0":"")+b.toString(16);return"#"+a(this.r)+a(this.g)+a(this.b)}}class Timer{constructor(a){this.time=void 0==a?void 0:time+a;this.setTime=a}set(a=0){this.time=time+a;this.setTime=a}unset(){this.time=void 0}isSet(){return void 0!=this.time}active(){return time<=this.time}elapsed(){return time>this.time}get(){return this.isSet()?time-this.time:0}getPercent(){return this.isSet()?percent(this.time-time,this.setTime,0):0}toString(){if(debug)return this.unset()?"unset":Math.abs(this.get())+" seconds "+(0>this.get()?"before":"after")}}"use strict";let canvasMaxSize=vec2(1920,1200),canvasFixedSize=vec2(),cavasPixelated=1,fontDefault="arial",tileSizeDefault=vec2(16),tileFixBleedScale=.3,objectDefaultSize=vec2(1),enablePhysicsSolver=1,objectDefaultMass=1,objectDefaultDamping=.99,objectDefaultAngleDamping=.99,objectDefaultElasticity=0,objectDefaultFriction=.8,objectMaxSpeed=1,gravity=0,particleEmitRateScale=1,cameraPos=vec2(),cameraScale=max(tileSizeDefault.x,tileSizeDefault.y),glEnable=1,glOverlay=1,gamepadsEnable=1,gamepadDirectionEmulateStick=1,inputWASDEmulateDirection=1,touchGamepadEnable=0,touchGamepadAnalog=1,touchGamepadSize=80,touchGamepadAlpha=.3,vibrateEnable=1,soundVolume=.5,soundEnable=1,soundDefaultRange=30,soundDefaultTaper=.7,medalDisplayTime=5,medalDisplaySlideTime=.5,medalDisplayWidth=640,medalDisplayHeight=80,medalDisplayIconSize=50;"use strict";const engineName="LittleJS",engineVersion="1.3.8",frameRate=60,timeDelta=1/frameRate;let engineObjects=[],engineObjectsCollide=[],frame=0,time=0,timeReal=0,paused=0,frameTimeLastMS=0,frameTimeBufferMS=0,tileImageSize,tileImageFixBleed,averageFPS,drawCount;const styleBody="margin:0;overflow:hidden;background:#000;touch-action:none;user-select:none;-webkit-user-select:none;-moz-user-select:none",styleCanvas="position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)";function engineInit(a,b,c,d,e,f){tileImage.onerror=tileImage.onload=()=>{tileImageFixBleed=vec2(tileFixBleedScale).divide(tileImageSize=vec2(tileImage.width,tileImage.height));debug&&(tileImage.onload=()=>ASSERT(1));document.body.style=styleBody;document.body.appendChild(mainCanvas=document.createElement("canvas"));mainContext=mainCanvas.getContext("2d");mainCanvas.style=styleCanvas;debugInit();glInit();document.body.appendChild(overlayCanvas=document.createElement("canvas"));overlayContext=overlayCanvas.getContext("2d");overlayCanvas.style=styleCanvas;a();touchGamepadCreate();h()};const h=(g=0)=>{var k=g-frameTimeLastMS;frameTimeLastMS=g;if(debug||showWatermark)averageFPS=lerp(.05,averageFPS||0,1e3/(k||1));debug&&(k*=keyIsDown(107)?5:keyIsDown(109)?.2:1);timeReal+=k/1e3;frameTimeBufferMS=min(frameTimeBufferMS+!paused*k,50);canvasFixedSize.x?(overlayCanvas.width=mainCanvas.width=canvasFixedSize.x,overlayCanvas.height=mainCanvas.height=canvasFixedSize.y,g=innerWidth/innerHeight,k=mainCanvas.width/mainCanvas.height,mainCanvas.style.width=overlayCanvas.style.width=g<k?"100%":"",mainCanvas.style.height=overlayCanvas.style.height=g<k?"":"100%",glCanvas&&(glCanvas.style.width=mainCanvas.style.width,glCanvas.style.height=mainCanvas.style.height)):(overlayCanvas.width=mainCanvas.width=min(innerWidth,canvasMaxSize.x),overlayCanvas.height=mainCanvas.height=min(innerHeight,canvasMaxSize.y));mainCanvasSize=vec2(mainCanvas.width,mainCanvas.height);if(paused)inputUpdate(),debugUpdate(),c(),inputUpdatePost();else{g=0;0>frameTimeBufferMS&&-9<frameTimeBufferMS&&(g=frameTimeBufferMS,frameTimeBufferMS=0);for(;0<=frameTimeBufferMS;frameTimeBufferMS-=1e3/frameRate)inputUpdate(),b(),engineObjectsUpdate(),debugUpdate(),c(),inputUpdatePost();frameTimeBufferMS+=g}enginePreRender();d();engineObjects.sort((n,p)=>n.renderOrder-p.renderOrder);for(var m of engineObjects)m.destroyed||m.render();e();medalsRender();touchGamepadRender();debugRender();glCopyToContext(mainContext);showWatermark&&(overlayContext.textAlign="right",overlayContext.textBaseline="top",overlayContext.font="1em monospace",overlayContext.fillStyle="#000",m=engineName+" v"+engineVersion+" / "+drawCount+" / "+engineObjects.length+" / "+averageFPS.toFixed(1)+" "+(glEnable?"GL":"2D"),overlayContext.fillText(m,mainCanvas.width-3,3),overlayContext.fillStyle="#fff",overlayContext.fillText(m,mainCanvas.width-2,2),drawCount=0);requestAnimationFrame(h)};f?tileImage.src=f:tileImage.onload()}function enginePreRender(){mainCanvasSize=vec2(mainCanvas.width,mainCanvas.height);mainContext.imageSmoothingEnabled=!cavasPixelated;glPreRender(mainCanvas.width,mainCanvas.height,cameraPos.x,cameraPos.y,cameraScale)}function engineObjectsUpdate(){engineObjectsCollide=engineObjects.filter(b=>b.collideSolidObjects);const a=b=>{if(!b.destroyed){b.update();for(const c of b.children)a(c)}};for(const b of engineObjects)b.parent||a(b);engineObjects=engineObjects.filter(b=>!b.destroyed);time=++frame/frameRate}function engineObjectsDestroy(){for(const a of engineObjects)a.parent||a.destroy();engineObjects=engineObjects.filter(a=>!a.destroyed)}function engineObjectsCallback(a,b,c,d=engineObjects){if(a)if(void 0!=b.x)for(const e of d)isOverlapping(a,b,e.pos,e.size)&&c(e);else{b*=b;for(const e of d)a.distanceSquared(e.pos)<b&&c(e)}else for(const e of d)c(e)}"use strict";class EngineObject{constructor(a=vec2(),b=objectDefaultSize,c=-1,d=tileSizeDefault,e=0,f,h=0){ASSERT(a&&void 0!=a.x&&void 0!=b.x);this.pos=a.copy();this.size=b;this.drawSize;this.tileIndex=c;this.tileSize=d;this.angle=e;this.color=f;this.additiveColor;this.mass=objectDefaultMass;this.damping=objectDefaultDamping;this.angleDamping=objectDefaultAngleDamping;this.elasticity=objectDefaultElasticity;this.friction=objectDefaultFriction;this.gravityScale=1;this.renderOrder=h;this.velocity=new Vector2;this.angleVelocity=0;this.spawnTime=time;this.children=[];this.collideTiles=1;engineObjects.push(this)}update(){var a=this.parent;if(a)this.pos=this.localPos.multiply(vec2(a.getMirrorSign(),1)).rotate(-a.angle).add(a.pos),this.angle=a.getMirrorSign()*this.localAngle+a.angle;else if(this.velocity.x=clamp(this.velocity.x,-objectMaxSpeed,objectMaxSpeed),this.velocity.y=clamp(this.velocity.y,-objectMaxSpeed,objectMaxSpeed),a=this.pos.copy(),this.pos.x+=this.velocity.x*=this.damping,this.pos.y+=this.velocity.y=this.damping*this.velocity.y+gravity*this.gravityScale,this.angle+=this.angleVelocity*=this.angleDamping,ASSERT(0<=this.angleDamping&&1>=this.angleDamping),ASSERT(0<=this.damping&&1>=this.damping),enablePhysicsSolver&&this.mass){var b=0>this.velocity.y;if(this.groundObject){var c=this.groundObject.velocity?this.groundObject.velocity.x:0;this.velocity.x=c+(this.velocity.x-c)*this.friction;this.groundObject=0}if(this.collideSolidObjects)for(var d of engineObjectsCollide)if(!(!this.isSolid&!d.isSolid||d.destroyed||d.parent||d==this||!isOverlapping(this.pos,this.size,d.pos,d.size)||!this.collideWithObject(d)|!d.collideWithObject(this)))if(isOverlapping(a,this.size,d.pos,d.size)){c=a.subtract(d.pos);var e=c.length();c=.01>e?randVector(.001):c.scale(.001/e);this.velocity=this.velocity.add(c);d.mass&&(d.velocity=d.velocity.subtract(c));debugOverlay&&debugPhysics&&debugAABB(this.pos,this.size,d.pos,d.size,"#f00")}else{c=this.size.add(d.size);e=2*(a.y-d.pos.y)>c.y+gravity;var f=2*abs(a.y-d.pos.y)<c.y,h=2*abs(a.x-d.pos.x)<c.x;if(e||h||!f)if(this.pos.y=d.pos.y+(c.y/2+.001)*sign(a.y-d.pos.y),d.groundObject&&b||!d.mass)b&&(this.groundObject=d),this.velocity.y*=-this.elasticity;else if(d.mass){const g=(this.mass*this.velocity.y+d.mass*d.velocity.y)/(this.mass+d.mass),k=this.velocity.y*(this.mass-d.mass)/(this.mass+d.mass)+2*d.velocity.y*d.mass/(this.mass+d.mass),m=d.velocity.y*(d.mass-this.mass)/(this.mass+d.mass)+2*this.velocity.y*this.mass/(this.mass+d.mass),n=max(this.elasticity,d.elasticity);this.velocity.y=lerp(n,g,k);d.velocity.y=lerp(n,g,m)}e||!f&&h||(this.pos.x=d.pos.x+(c.x/2+.001)*sign(a.x-d.pos.x),d.mass?(c=(this.mass*this.velocity.x+d.mass*d.velocity.x)/(this.mass+d.mass),e=this.velocity.x*(this.mass-d.mass)/(this.mass+d.mass)+2*d.velocity.x*d.mass/(this.mass+d.mass),f=d.velocity.x*(d.mass-this.mass)/(this.mass+d.mass)+2*this.velocity.x*this.mass/(this.mass+d.mass),h=max(this.elasticity,d.elasticity),this.velocity.x=lerp(h,c,e),d.velocity.x=lerp(h,c,f)):this.velocity.x*=-this.elasticity);debugOverlay&&debugPhysics&&debugAABB(this.pos,this.size,d.pos,d.size,"#f0f")}if(this.collideTiles&&tileCollisionTest(this.pos,this.size,this)&&!tileCollisionTest(a,this.size,this)){c=tileCollisionTest(new Vector2(a.x,this.pos.y),this.size,this);d=tileCollisionTest(new Vector2(this.pos.x,a.y),this.size,this);if(c||!d)this.groundObject=b,this.velocity.y*=-this.elasticity,b=(a.y-this.size.y/2|0)-(a.y-this.size.y/2),0>b&&b>this.damping*this.velocity.y+gravity*this.gravityScale&&(this.velocity.y=this.damping?(b-gravity*this.gravityScale)/this.damping:0),this.pos.y=a.y;d&&(this.pos.x=a.x,this.velocity.x*=-this.elasticity)}}}render(){drawTile(this.pos,this.drawSize||this.size,this.tileIndex,this.tileSize,this.color,this.angle,this.mirror,this.additiveColor)}destroy(){if(!this.destroyed){this.destroyed=1;this.parent&&this.parent.removeChild(this);for(const a of this.children)a.destroy(a.parent=0)}}collideWithTile(a,b){return 0<a}collideWithTileRaycast(a,b){return 0<a}collideWithObject(a){return 1}getAliveTime(){return time-this.spawnTime}applyAcceleration(a){this.mass&&(this.velocity=this.velocity.add(a))}applyForce(a){this.applyAcceleration(a.scale(1/this.mass))}getMirrorSign(){return this.mirror?-1:1}addChild(a,b=vec2(),c=0){ASSERT(!a.parent&&!this.children.includes(a));this.children.push(a);a.parent=this;a.localPos=b.copy();a.localAngle=c}removeChild(a){ASSERT(a.parent==this&&this.children.includes(a));this.children.splice(this.children.indexOf(a),1);a.parent=0}setCollision(a=0,b=0,c=1){ASSERT(a||!b);this.collideSolidObjects=a;this.isSolid=b;this.collideTiles=c}toString(){if(debug){let a="type = "+this.constructor.name;if(this.pos.x||this.pos.y)a+="\npos = "+this.pos;if(this.velocity.x||this.velocity.y)a+="\nvelocity = "+this.velocity;if(this.size.x||this.size.y)a+="\nsize = "+this.size;this.angle&&(a+="\nangle = "+this.angle.toFixed(3));this.color&&(a+="\ncolor = "+this.color);return a}}}"use strict";const tileImage=new Image;let mainCanvas,mainContext,overlayCanvas,overlayContext,mainCanvasSize=vec2();const screenToWorld=a=>{ASSERT(mainCanvasSize.x&&mainCanvasSize.y,"mainCanvasSize is invalid");return a.add(vec2(.5)).subtract(mainCanvasSize.scale(.5)).multiply(vec2(1/cameraScale,-1/cameraScale)).add(cameraPos)},worldToScreen=a=>{ASSERT(mainCanvasSize.x&&mainCanvasSize.y,"mainCanvasSize is invalid");return a.subtract(cameraPos).multiply(vec2(cameraScale,-cameraScale)).add(mainCanvasSize.scale(.5)).subtract(vec2(.5))};function drawTile(a,b=vec2(1),c=-1,d=tileSizeDefault,e=new Color,f=0,h,g=new Color(0,0,0,0),k=glEnable){showWatermark&&++drawCount;if(glEnable&&k)if(0>c||!tileImage.width)glDraw(a.x,a.y,b.x,b.y,f,0,0,0,0,0,e.rgbaInt());else{var m=tileImageSize.x/d.x|0;k=d.x/tileImageSize.x;const n=d.y/tileImageSize.y,p=c%m*k;m=(c/m|0)*n;glDraw(a.x,a.y,h?-b.x:b.x,b.y,f,p+tileImageFixBleed.x,m+tileImageFixBleed.y,p-tileImageFixBleed.x+k,m-tileImageFixBleed.y+n,e.rgbaInt(),g.rgbaInt())}else drawCanvas2D(a,b,f,h,n=>{if(0>c)n.fillStyle=e,n.fillRect(-.5,-.5,1,1);else{var p=tileImageSize.x/d.x|0;const l=c%p*d.x+tileFixBleedScale;p=(c/p|0)*d.y+tileFixBleedScale;const u=d.x-2*tileFixBleedScale,v=d.y-2*tileFixBleedScale;n.globalAlpha=e.a;n.drawImage(tileImage,l,p,u,v,-.5,-.5,1,1)}})}function drawRect(a,b,c,d,e){drawTile(a,b,-1,tileSizeDefault,c,d,0,0,e)}function drawTileScreenSpace(a,b=vec2(1),c,d,e,f,h,g,k){drawTile(screenToWorld(a),b.scale(1/cameraScale),c,d,e,f,h,g,k)}function drawRectScreenSpace(a,b,c,d,e){drawTileScreenSpace(a,b,-1,tileSizeDefault,c,d,0,0,e)}function drawLine(a,b,c=.1,d,e){b=vec2((b.x-a.x)/2,(b.y-a.y)/2);c=vec2(c,2*b.length());drawRect(a.add(b),c,d,b.angle(),0,0,e)}function drawCanvas2D(a,b,c,d,e,f=mainContext){a=worldToScreen(a);b=b.scale(cameraScale);f.save();f.translate(a.x+.5|0,a.y-.5|0);f.rotate(c);f.scale(d?-b.x:b.x,b.y);e(f);f.restore()}function setBlendMode(a,b=glEnable){glEnable&&b?glSetBlendMode(a):mainContext.globalCompositeOperation=a?"lighter":"source-over"}function drawTextScreen(a,b,c=1,d=new Color,e=0,f=new Color(0,0,0),h="center",g=fontDefault){overlayContext.fillStyle=d;overlayContext.lineWidth=e;overlayContext.strokeStyle=f;overlayContext.textAlign=h;overlayContext.font=c+"px "+g;overlayContext.textBaseline="middle";overlayContext.lineJoin="round";b=b.copy();(a+"").split("\n").forEach(k=>{e&&overlayContext.strokeText(k,b.x,b.y);overlayContext.fillText(k,b.x,b.y);b.y+=c})}function drawText(a,b,c=1,d,e,f,h,g){drawTextScreen(a,worldToScreen(b),c*cameraScale,d,e*cameraScale,f,h,g)}let engineFontImage;class FontImage{constructor(a,b=vec2(8),c=vec2(0,1),d=0,e=overlayContext){a||engineFontImage||(engineFontImage=new Image,engineFontImage.src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQAAAAAYAQAAAAA9+x6JAAAAAnRSTlMAAHaTzTgAAAGiSURBVHjaZZABhxxBEIUf6ECLBdFY+Q0PMNgf0yCgsSAGZcT9sgIPtBWwIA5wgAPEoHUyJeeSlW+gjK+fegWwtROWpVQEyWh2npdpBmTUFVhb29RINgLIukoXr5LIAvYQ5ve+1FqWEMqNKTX3FAJHyQDRZvmKWubAACcv5z5Gtg2oyCWE+Yk/8JZQX1jTTCpKAFGIgza+dJCNBF2UskRlsgwitHbSV0QLgt9sTPtsRlvJjEr8C/FARWA2bJ/TtJ7lko34dNDn6usJUMzuErP89UUBJbWeozrwLLncXczd508deAjLWipLO4Q5XGPcJvPu92cNDaN0P5G1FL0nSOzddZOrJ6rNhbXGmeDvO3TF7DeJWl4bvaYQTNHCTeuqKZmbjHaSOFes+IX/+IhHrnAkXOAsfn24EM68XieIECoccD4KZLk/odiwzeo2rovYdhvb2HYFgyznJyDpYJdYOmfXgVdJTaUi4xA2uWYNYec9BLeqdl9EsoTw582mSFDX2DxVLbNt9U3YYoeatBad1c2Tj8t2akrjaIGJNywKB/7h75/gN3vCMSaadIUTAAAAAElFTkSuQmCC");this.image=a||engineFontImage;this.tileSize=b;this.paddingSize=c;this.startTileIndex=d;this.context=e}drawTextScreen(a,b,c=4,d){const e=this.context;e.save();e.imageSmoothingEnabled=!cavasPixelated;const f=this.tileSize,h=f.add(this.paddingSize).scale(c),g=this.image.width/this.tileSize.x|0;a.split("\n").forEach((k,m)=>{const n=d?k.length*f.x*c/2|0:0;for(let u=k.length;u--;){var p=k[u].charCodeAt();if(32>p||127<p)p=127;var l=this.startTileIndex+p-32;p=l%g;l=l/g|0;const v=b.add(vec2(u,m).multiply(h));e.drawImage(this.image,p*f.x,l*f.y,f.x,f.y,v.x-n,v.y,f.x*c,f.y*c)}});e.restore()}drawText(a,b,c=1,d){this.drawTextScreen(a,worldToScreen(b).floor(),c*cameraScale|0,d)}}const isFullscreen=()=>document.fullscreenElement;function toggleFullscreen(){isFullscreen()?document.exitFullscreen?document.exitFullscreen():document.mozCancelFullScreen&&document.mozCancelFullScreen():document.body.webkitRequestFullScreen?document.body.webkitRequestFullScreen():document.body.mozRequestFullScreen&&document.body.mozRequestFullScreen()}"use strict";const keyIsDown=(a,b=0)=>inputData[b]&&inputData[b][a]&1?1:0,keyWasPressed=(a,b=0)=>inputData[b]&&inputData[b][a]&2?1:0,keyWasReleased=(a,b=0)=>inputData[b]&&inputData[b][a]&4?1:0,clearInput=()=>inputData=[[]],mouseIsDown=keyIsDown,mouseWasPressed=keyWasPressed,mouseWasReleased=keyWasReleased;let mousePos=vec2(),mousePosScreen=vec2(),mouseWheel=0,isUsingGamepad=0,preventDefaultInput=0;const gamepadIsDown=(a,b=0)=>keyIsDown(a,b+1),gamepadWasPressed=(a,b=0)=>keyWasPressed(a,b+1),gamepadWasReleased=(a,b=0)=>keyWasReleased(a,b+1),gamepadStick=(a,b=0)=>stickData[b]?stickData[b][a]||vec2():vec2();let inputData=[[]];function inputUpdate(){document.hasFocus()||clearInput();mousePos=screenToWorld(mousePosScreen);gamepadsUpdate()}function inputUpdatePost(){for(const a of inputData)for(const b in a)a[b]&=1;mouseWheel=0}onkeydown=a=>{debug&&a.target!=document.body||(a.repeat||(inputData[isUsingGamepad=0][remapKeyCode(a.keyCode)]=3),preventDefaultInput&&a.preventDefault())};onkeyup=a=>{debug&&a.target!=document.body||(inputData[0][remapKeyCode(a.keyCode)]=4)};const remapKeyCode=a=>inputWASDEmulateDirection?87==a?38:83==a?40:65==a?37:68==a?39:a:a;onmousedown=a=>{inputData[isUsingGamepad=0][a.button]=3;onmousemove(a);a.button&&a.preventDefault()};onmouseup=a=>inputData[0][a.button]=inputData[0][a.button]&2|4;onmousemove=a=>mousePosScreen=mouseToScreen(a);onwheel=a=>a.ctrlKey||(mouseWheel=sign(a.deltaY));oncontextmenu=a=>!1;const mouseToScreen=a=>{if(!mainCanvas)return vec2();const b=mainCanvas.getBoundingClientRect();return vec2(mainCanvas.width,mainCanvas.height).multiply(vec2(percent(a.x,b.left,b.right),percent(a.y,b.top,b.bottom)))},stickData=[];function gamepadsUpdate(){if(touchGamepadEnable&&touchGamepadTimer.isSet()){(stickData[0]||(stickData[0]=[]))[0]=vec2(touchGamepadStick.x,-touchGamepadStick.y);var a=inputData[1]||(inputData[1]=[]);for(var b=10;b--;){var c=3==b?2:2==b?3:b;a[c]=touchGamepadButtons[b]?1+2*!gamepadIsDown(c,0):4*gamepadIsDown(c,0)}}if(gamepadsEnable&&navigator.getGamepads&&(document.hasFocus()||debug))for(a=navigator.getGamepads(),b=a.length;b--;){var d=a[b];const h=inputData[b+1]||(inputData[b+1]=[]);c=stickData[b]||(stickData[b]=[]);if(d){var e=g=>.3<g?percent(g,.3,.8):-.3>g?-percent(-g,.3,.8):0;for(var f=0;f<d.axes.length-1;f+=2)c[f>>1]=vec2(e(d.axes[f]),e(-d.axes[f+1])).clampLength();for(e=d.buttons.length;e--;)f=d.buttons[e],h[e]=f.pressed?1+2*!gamepadIsDown(e,b):4*gamepadIsDown(e,b),isUsingGamepad|=!b&&f.pressed,touchGamepadEnable&&touchGamepadTimer.unset();gamepadDirectionEmulateStick&&(d=vec2(gamepadIsDown(15,b)-gamepadIsDown(14,b),gamepadIsDown(12,b)-gamepadIsDown(13,b)),d.lengthSquared()&&(c[0]=d.clampLength()))}}}const vibrate=a=>vibrateEnable&&Navigator.vibrate&&Navigator.vibrate(a),vibrateStop=()=>vibrate(0),isTouchDevice=void 0!==window.ontouchstart;if(isTouchDevice){let a,b;ontouchstart=ontouchmove=ontouchend=c=>{c.button=0;const d=c.touches.length;d?(b||zzfx(0,b=1),c.x=c.touches[0].clientX,c.y=c.touches[0].clientY,a?onmousemove(c):onmousedown(c)):a&&onmouseup(c);a=d;return!c.cancelable}}let touchGamepadTimer=new Timer,touchGamepadButtons=[],touchGamepadStick=vec2();function touchGamepadCreate(){touchGamepadEnable&&isTouchDevice&&(ontouchstart=ontouchmove=ontouchend=a=>{if(touchGamepadEnable){touchGamepadStick=vec2();touchGamepadButtons=[];if(a.touches.length&&(touchGamepadTimer.isSet()||zzfx(0),isUsingGamepad=1,touchGamepadTimer.set(),paused)){touchGamepadButtons[9]=1;return}var b=vec2(touchGamepadSize,mainCanvasSize.y-touchGamepadSize),c=mainCanvasSize.subtract(vec2(touchGamepadSize,touchGamepadSize)),d=mainCanvasSize.scale(.5);for(const e of a.touches)a=mouseToScreen(vec2(e.clientX,e.clientY)),a.distance(b)<touchGamepadSize?touchGamepadAnalog?touchGamepadStick=a.subtract(b).scale(2/touchGamepadSize).clampLength():(a=a.subtract(b).angle(),touchGamepadStick.setAngle((4*a/PI+8.5|0)*PI/4)):a.distance(c)<touchGamepadSize?(a=a.subtract(c).direction(),touchGamepadButtons[a]=1):a.distance(d)<touchGamepadSize&&(touchGamepadButtons[9]=1)}})}function touchGamepadRender(){if(touchGamepadEnable&&touchGamepadTimer.isSet()){var a=percent(touchGamepadTimer.get(),4,3);if(a&&!paused){overlayContext.save();overlayContext.globalAlpha=a*touchGamepadAlpha;overlayContext.strokeStyle="#fff";overlayContext.lineWidth=3;overlayContext.fillStyle=0<touchGamepadStick.lengthSquared()?"#fff":"#000";overlayContext.beginPath();a=vec2(touchGamepadSize,mainCanvasSize.y-touchGamepadSize);if(touchGamepadAnalog)overlayContext.arc(a.x,a.y,touchGamepadSize/2,0,9),overlayContext.fill();else for(var b=10;b--;){var c=b*PI/4;overlayContext.arc(a.x,a.y,.6*touchGamepadSize,c+PI/8,c+PI/8);b%2&&overlayContext.arc(a.x,a.y,.33*touchGamepadSize,c,c);1==b&&overlayContext.fill()}overlayContext.stroke();a=vec2(mainCanvasSize.x-touchGamepadSize,mainCanvasSize.y-touchGamepadSize);for(b=4;b--;)c=a.add((new Vector2).setAngle(b*PI/2,touchGamepadSize/2)),overlayContext.fillStyle=touchGamepadButtons[b]?"#fff":"#000",overlayContext.beginPath(),overlayContext.arc(c.x,c.y,touchGamepadSize/4,0,9),overlayContext.fill(),overlayContext.stroke();overlayContext.restore()}}}"use strict";class Sound{constructor(a,b=soundDefaultRange,c=soundDefaultTaper){soundEnable&&(this.range=b,this.taper=c,this.randomness=a[1]||0,a[1]=0,this.cachedSamples=zzfxG(...a))}play(a,b=1,c=1,d=1){if(soundEnable){var e=0;if(a){if(e=this.range){const f=cameraPos.distanceSquared(a);if(f>e*e)return;b*=percent(f**.5,e,e*this.taper)}e=2*worldToScreen(a).x/mainCanvas.width-1}a=c+c*this.randomness*d*rand(-1,1);return playSamples([this.cachedSamples],b,a,e)}}playNote(a,b,c=1){if(soundEnable)return this.play(b,c,2**(a/12),0)}}class Music{constructor(a){soundEnable&&(this.cachedSamples=zzfxM(...a))}play(a=1,b=1){if(soundEnable)return playSamples(this.cachedSamples,a,1,0,b)}}function playAudioFile(a,b=1,c=1){if(soundEnable)return a=new Audio(a),a.volume=soundVolume*b,a.loop=c,a.play(),a}function speak(a,b="",c=1,d=1,e=1){if(soundEnable&&speechSynthesis)return a=new SpeechSynthesisUtterance(a),a.lang=b,a.volume=2*c*soundVolume,a.rate=d,a.pitch=e,speechSynthesis.speak(a),a}const speakStop=()=>speechSynthesis&&speechSynthesis.cancel(),getNoteFrequency=(a,b=220)=>b*2**(a/12);let audioContext;function playSamples(a,b=1,c=1,d=0,e=0){if(soundEnable&&(audioContext||(audioContext=new(window.AudioContext||webkitAudioContext)),audioContext.resume(),"running"==audioContext.state)){var f=audioContext.createBuffer(a.length,a[0].length,zzfxR),h=audioContext.createBufferSource();a.forEach((g,k)=>f.getChannelData(k).set(g));h.buffer=f;h.playbackRate.value=c;h.loop=e;a=audioContext.createGain();a.gain.value=soundVolume*b;a.connect(audioContext.destination);(window.StereoPannerNode?h.connect(new StereoPannerNode(audioContext,{pan:clamp(d,-1,1)})):h).connect(a);h.start();return h}}const zzfx=(...a)=>playSamples([zzfxG(...a)]),zzfxR=44100;function zzfxG(a=1,b=.05,c=220,d=0,e=0,f=.1,h=0,g=1,k=0,m=0,n=0,p=0,l=0,u=0,v=0,A=0,r=0,B=1,y=0,C=0){let t=2*PI,F=k*=500*t/zzfxR/zzfxR,z=[];b=c*=(1+b*rand(-1,1))*t/zzfxR;let w=0,D=0,q=0,x=1,H=0,I=0,E=0,J,G;d=d*zzfxR+9;y*=zzfxR;e*=zzfxR;f*=zzfxR;r*=zzfxR;m*=500*t/zzfxR**3;v*=t/zzfxR;n*=t/zzfxR;p*=zzfxR;l=l*zzfxR|0;for(G=d+y+e+f+r|0;q<G;z[q++]=E)++I%(100*A|0)||(E=h?1<h?2<h?3<h?Math.sin((w%t)**3):max(min(Math.tan(w),1),-1):1-(2*w/t%2+2)%2:1-4*abs(Math.round(w/t)-w/t):Math.sin(w),E=(l?1-C+C*Math.sin(t*q/l):1)*sign(E)*abs(E)**g*a*soundVolume*(q<d?q/d:q<d+y?1-(q-d)/y*(1-B):q<d+y+e?B:q<G-r?(G-q-r)/f*B:0),E=r?E/2+(r>q?0:(q<G-r?1:(G-q)/r)*z[q-r|0]/2):E),J=(c+=k+=m)*Math.cos(v*D++),w+=J-J*u*(1-1e9*(Math.sin(q)+1)%2),x&&++x>p&&(c+=n,b+=n,x=0),!l||++H%l||(c=b,k=F,x=x||1);return z}function zzfxM(a,b,c,d=125){let e,f,h,g,k,m,n,p,l,u,v,A,r,B=0,y,C=[],t=[],F=[],z=0,w=0,D=1,q={},x=zzfxR/d*60>>2;for(;D;z++)C=[D=p=A=0],c.forEach((H,I)=>{n=b[H][z]||[0,0,0];D|=!!b[H][z];y=A+(b[H][0].length-2-!p)*x;r=I==c.length-1;f=2;for(g=A;f<n.length+r;p=++f){k=n[f];l=f==n.length+r-1&&r||u!=(n[0]||0)|k|0;for(h=0;h<x&&p;h++>x-99&&l?v+=(1>v)/99:0)m=(1-v)*C[B++]/2||0,t[g]=(t[g]||0)-m*w+m,F[g]=(F[g++]||0)+m*w+m;k&&(v=k%1,w=n[1]||0,k|=0)&&(C=q[[u=n[B=0]||0,k]]=q[[u,k]]||(e=[...a[u]],e[2]*=2**((k-12)/12),0<k?zzfxG(...e):[]))}A=y});return[t,F]}"use strict";let tileCollision=[],tileCollisionSize=vec2();function initTileCollision(a){tileCollisionSize=a;tileCollision=[];for(a=tileCollision.length=tileCollisionSize.area();a--;)tileCollision[a]=0}const setTileCollisionData=(a,b=0)=>a.arrayCheck(tileCollisionSize)&&(tileCollision[(a.y|0)*tileCollisionSize.x+a.x|0]=b),getTileCollisionData=a=>a.arrayCheck(tileCollisionSize)?tileCollision[(a.y|0)*tileCollisionSize.x+a.x|0]:0;function tileCollisionTest(a,b=vec2(),c){const d=max(a.x-b.x/2|0,0);var e=max(a.y-b.y/2|0,0);const f=min(a.x+b.x/2,tileCollisionSize.x);for(a=min(a.y+b.y/2,tileCollisionSize.y);e<a;++e)for(b=d;b<f;++b){const h=tileCollision[e*tileCollisionSize.x+b];if(h&&(!c||c.collideWithTile(h,new Vector2(b,e))))return 1}}function tileCollisionRaycast(a,b,c){a=a.floor();b=b.floor();var d=b.subtract(a);const e=abs(d.x),f=-abs(d.y),h=sign(d.x);d=sign(d.y);let g=e+f;for(let m=a.x,n=a.y;;){var k=getTileCollisionData(vec2(m,n));if(k&&(c?c.collideWithTileRaycast(k,new Vector2(m,n)):0<k))return debugRaycast&&debugLine(a,b,"#f00",.02,1),debugRaycast&&debugPoint(new Vector2(m+.5,n+.5),"#ff0",1),new Vector2(m+.5,n+.5);if(m==b.x&n==b.y)break;k=2*g;k>=f&&(g+=f,m+=h);k<=e&&(g+=e,n+=d)}debugRaycast&&debugLine(a,b,"#00f",.02,1)}class TileLayerData{constructor(a,b=0,c=0,d=new Color){this.tile=a;this.direction=b;this.mirror=c;this.color=d}clear(){this.tile=this.direction=this.mirror=0;color=new Color}}class TileLayer extends EngineObject{constructor(a,b=tileCollisionSize,c=tileSizeDefault,d=vec2(1),e=0){super(a,b,-1,c,0,void 0,e);this.canvas=document.createElement("canvas");this.context=this.canvas.getContext("2d");this.scale=d;this.isOverlay;this.data=[];for(a=this.size.area();a--;)this.data.push(new TileLayerData)}setData(a,b,c){a.arrayCheck(this.size)&&(this.data[(a.y|0)*this.size.x+a.x|0]=b,c&&this.drawTileData(a))}getData(a){return a.arrayCheck(this.size)&&this.data[(a.y|0)*this.size.x+a.x|0]}update(){}render(){ASSERT(mainContext!=this.context);glEnable&&!glOverlay&&!this.isOverlay&&glCopyToContext(mainContext);const a=worldToScreen(this.pos.add(vec2(0,this.size.y*this.scale.y)));(this.isOverlay?overlayContext:mainContext).drawImage(this.canvas,a.x,a.y,cameraScale*this.size.x*this.scale.x,cameraScale*this.size.y*this.scale.y)}redraw(){this.redrawStart(1);this.drawAllTileData();this.redrawEnd()}redrawStart(a=0){a&&(this.canvas.width=this.size.x*this.tileSize.x,this.canvas.height=this.size.y*this.tileSize.y);this.savedRenderSettings=[mainCanvas,mainContext,cameraPos,cameraScale];mainCanvas=this.canvas;mainContext=this.context;cameraPos=this.size.scale(.5);cameraScale=this.tileSize.x;enginePreRender()}redrawEnd(){ASSERT(mainContext==this.context);glCopyToContext(mainContext,1);[mainCanvas,mainContext,cameraPos,cameraScale]=this.savedRenderSettings}drawTileData(a){const b=a.floor().add(this.pos).add(vec2(.5));this.drawCanvas2D(b,vec2(1),0,0,c=>c.clearRect(-.5,-.5,1,1));a=this.getData(a);void 0!=a.tile&&(ASSERT(mainContext==this.context),drawTile(b,vec2(1),a.tile,this.tileSize,a.color,a.direction*PI/2,a.mirror))}drawAllTileData(){for(let a=this.size.x;a--;)for(let b=this.size.y;b--;)this.drawTileData(vec2(a,b))}drawCanvas2D(a,b,c=0,d,e){const f=this.context;f.save();a=a.subtract(this.pos).multiply(this.tileSize);b=b.multiply(this.tileSize);f.translate(a.x,this.canvas.height-a.y);f.rotate(c);f.scale(d?-b.x:b.x,b.y);e(f);f.restore()}drawTile(a,b=vec2(1),c=-1,d=tileSizeDefault,e=new Color,f,h){this.drawCanvas2D(a,b,f,h,g=>{if(0>c)g.fillStyle=e,g.fillRect(-.5,-.5,1,1);else{const k=tileImage.width/d.x;g.globalAlpha=e.a;g.drawImage(tileImage,c%k*d.x,(c/k|0)*d.x,d.x,d.y,-.5,-.5,1,1)}})}drawRect(a,b,c,d){this.drawTile(a,b,-1,0,c,d)}}"use strict";class ParticleEmitter extends EngineObject{constructor(a,b,c=0,d=0,e=100,f=PI,h=-1,g=tileSizeDefault,k=new Color,m=new Color,n=new Color(1,1,1,0),p=new Color(1,1,1,0),l=.5,u=.1,v=1,A=.1,r=.05,B=1,y=1,C=0,t=PI,F=.1,z=.2,w,D,q=1,x=D?1e9:0){super(a,new Vector2,h,g,b,void 0,x);this.emitSize=c;this.emitTime=d;this.emitRate=e;this.emitConeAngle=f;this.colorStartA=k;this.colorStartB=m;this.colorEndA=n;this.colorEndB=p;this.randomColorLinear=q;this.particleTime=l;this.sizeStart=u;this.sizeEnd=v;this.speed=A;this.angleSpeed=r;this.damping=B;this.angleDamping=y;this.gravityScale=C;this.particleConeAngle=t;this.fadeRate=F;this.randomness=z;this.collideTiles=w;this.additive=D;this.emitTimeBuffer=this.trailScale=0}update(){this.parent&&super.update();if(!this.emitTime||this.getAliveTime()<=this.emitTime){if(this.emitRate*particleEmitRateScale){const a=1/this.emitRate/particleEmitRateScale;for(this.emitTimeBuffer+=timeDelta;0<this.emitTimeBuffer;this.emitTimeBuffer-=a)this.emitParticle()}}else this.destroy();debugParticles&&debugRect(this.pos,vec2(this.emitSize),"#0f0",0,this.angle)}emitParticle(){var a=void 0!=this.emitSize.x?new Vector2(rand(-.5,.5),rand(-.5,.5)).multiply(this.emitSize).rotate(this.angle):randInCircle(.5*this.emitSize);a=new Particle(this.pos.add(a),this.tileIndex,this.tileSize,this.angle+rand(this.particleConeAngle,-this.particleConeAngle));const b=this.randomness;var c=n=>n+n*rand(b,-b);const d=c(this.particleTime),e=c(this.sizeStart),f=c(this.sizeEnd),h=c(this.speed);c=c(this.angleSpeed)*randSign();const g=rand(this.emitConeAngle,-this.emitConeAngle),k=randColor(this.colorStartA,this.colorStartB,this.randomColorLinear),m=randColor(this.colorEndA,this.colorEndB,this.randomColorLinear);a.colorStart=k;a.colorEndDelta=m.subtract(k);a.velocity=(new Vector2).setAngle(this.angle+g,h);a.angleVelocity=c;a.lifeTime=d;a.sizeStart=e;a.sizeEndDelta=f-e;a.fadeRate=this.fadeRate;a.damping=this.damping;a.angleDamping=this.angleDamping;a.elasticity=this.elasticity;a.friction=this.friction;a.gravityScale=this.gravityScale;a.collideTiles=this.collideTiles;a.additive=this.additive;a.renderOrder=this.renderOrder;a.trailScale=this.trailScale;a.mirror=.5>rand();a.destroyCallback=this.particleDestroyCallback;this.particleCreateCallback&&this.particleCreateCallback(a);return a}render(){}}class Particle extends EngineObject{constructor(a,b,c,d){super(a,new Vector2,b,c,d)}render(){const a=min((time-this.spawnTime)/this.lifeTime,1);var b=this.sizeStart+a*this.sizeEndDelta;b=new Vector2(b,b);var c=this.fadeRate/2;c=new Color(this.colorStart.r+a*this.colorEndDelta.r,this.colorStart.g+a*this.colorEndDelta.g,this.colorStart.b+a*this.colorEndDelta.b,(this.colorStart.a+a*this.colorEndDelta.a)*(a<c?a/c:a>1-c?(1-a)/c:1));this.additive&&setBlendMode(1);if(this.trailScale){var d=this.velocity.length();const e=this.velocity.scale(1/d);d*=this.trailScale;b.y=max(b.x,d);this.angle=e.angle();drawTile(this.pos.add(e.multiply(vec2(0,-d/2))),b,this.tileIndex,this.tileSize,c,this.angle,this.mirror)}else drawTile(this.pos,b,this.tileIndex,this.tileSize,c,this.angle,this.mirror);this.additive&&setBlendMode();debugParticles&&debugRect(this.pos,b,"#f005",0,this.angle);1==a&&(this.color=c,this.size=b,this.destroyCallback&&this.destroyCallback(this),this.destroyed=1)}}"use strict";const medals=[];let medalsPreventUnlock,newgrounds,medalsDisplayQueue=[],medalsSaveName,medalsDisplayTimeLast;function medalsInit(a){medalsSaveName=a;debugMedals||medals.forEach(b=>localStorage[b.storageKey()])}class Medal{constructor(a,b,c="",d="🏆",e){ASSERT(0<=a&&!medals[a]);medals[this.id=a]=this;this.name=b;this.description=c;this.icon=d;this.image=new Image;e&&(this.image.src=e)}unlock(){medalsPreventUnlock||this.unlocked||(ASSERT(medalsSaveName),localStorage[this.storageKey()]=this.unlocked=1,medalsDisplayQueue.push(this),newgrounds&&newgrounds.unlockMedal(this.id),localStorage["OS13kTrophy,"+this.icon+","+medalsSaveName+","+this.name]=this.description)}render(a=0){const b=overlayContext,c=min(medalDisplayWidth,mainCanvas.width),d=overlayCanvas.width-c;a*=-medalDisplayHeight;b.save();b.beginPath();b.fillStyle="#ddd";b.fill(b.rect(d,a,c,medalDisplayHeight));b.strokeStyle="#000";b.lineWidth=3;b.stroke();b.clip();this.renderIcon(d+15+medalDisplayIconSize/2,a+medalDisplayHeight/2);b.textAlign="left";b.font="38px "+fontDefault;b.fillText(this.name,d+medalDisplayIconSize+30,a+28);b.font="24px "+fontDefault;b.fillText(this.description,d+medalDisplayIconSize+30,a+60);b.restore()}renderIcon(a,b,c=medalDisplayIconSize){const d=overlayContext;d.fillStyle="#000";d.textAlign="center";d.textBaseline="middle";d.font=.7*c+"px "+fontDefault;this.image.src?d.drawImage(this.image,a-c/2,b-c/2,c,c):d.fillText(this.icon,a,b)}storageKey(){return medalsSaveName+"_"+this.id}}function medalsRender(){if(medalsDisplayQueue.length){var a=medalsDisplayQueue[0],b=timeReal-medalsDisplayTimeLast;if(medalsDisplayTimeLast)if(b>medalDisplayTime)medalsDisplayQueue.shift(medalsDisplayTimeLast=0);else{const c=medalDisplayTime-medalDisplaySlideTime;a.render(b<medalDisplaySlideTime?1-b/medalDisplaySlideTime:b>c?(b-c)/medalDisplaySlideTime:0)}else medalsDisplayTimeLast=timeReal}}class Newgrounds{constructor(a,b){ASSERT(!newgrounds&&a);this.app_id=a;this.cipher=b;this.host=location?location.hostname:"";b&&(this.cryptoJS=CryptoJS());this.session_id=new URL(location.href).searchParams.get("ngio_session_id")||0;if(0!=this.session_id){this.medals=(a=this.call("Medal.getList"))?a.result.data.medals:[];debugMedals&&console.log(this.medals);for(var c of this.medals)if(a=medals[c.id])a.image.src=c.icon,a.name=c.name,a.description=c.description,a.unlocked=c.unlocked,a.difficulty=c.difficulty,a.value=c.value,a.value&&(a.description=a.description+" ("+a.value+")");this.scoreboards=(c=this.call("ScoreBoard.getBoards"))?c.result.data.scoreboards:[];debugMedals&&console.log(this.scoreboards);setInterval(()=>this.call("Gateway.ping",0,1),3e5)}}unlockMedal(a){return this.call("Medal.unlock",{id:a},1)}postScore(a,b){return this.call("ScoreBoard.postScore",{id:a,value:b},1)}getScores(a,b=0,c=0,d=0,e=10){return this.call("ScoreBoard.getScores",{id:a,user:b,social:c,skip:d,limit:e})}logView(){return this.call("App.logView",{host:this.host},1)}call(a,b=0,c=0){a={component:a,parameters:b};if(this.cipher){b=this.cryptoJS;var d=b.enc.Base64.parse(this.cipher);const e=b.lib.WordArray.random(16);d=b.AES.encrypt(JSON.stringify(a),d,{iv:e});a.secure=b.enc.Base64.stringify(e.concat(d.ciphertext));a.parameters=0}b={app_id:this.app_id,session_id:this.session_id,call:a};a=new FormData;a.append("input",JSON.stringify(b));b=new XMLHttpRequest;b.open("POST","https://newgrounds.io/gateway_v3.php",!debugMedals&&c);b.send(a);debugMedals&&console.log(b.responseText);return b.responseText&&JSON.parse(b.responseText)}}const CryptoJS=()=>eval(Function("[M='GBMGXz^oVYPPKKbB`agTXU|LxPc_ZBcMrZvCr~wyGfWrwk@ATqlqeTp^N?p{we}jIpEnB_sEr`l?YDkDhWhprc|Er|XETG?pTl`e}dIc[_N~}fzRycIfpW{HTolvoPB_FMe_eH~BTMx]yyOhv?biWPCGc]kABencBhgERHGf{OL`Dj`c^sh@canhy[secghiyotcdOWgO{tJIE^JtdGQRNSCrwKYciZOa]Y@tcRATYKzv|sXpboHcbCBf`}SKeXPFM|RiJsSNaIb]QPc[D]Jy_O^XkOVTZep`ONmntLL`Qz~UupHBX_Ia~WX]yTRJIxG`ioZ{fefLJFhdyYoyLPvqgH?b`[TMnTwwfzDXhfM?rKs^aFr|nyBdPmVHTtAjXoYUloEziWDCw_suyYT~lSMksI~ZNCS[Bex~j]Vz?kx`gdYSEMCsHpjbyxQvw|XxX_^nQYue{sBzVWQKYndtYQMWRef{bOHSfQhiNdtR{o?cUAHQAABThwHPT}F{VvFmgN`E@FiFYS`UJmpQNM`X|tPKHlccT}z}k{sACHL?Rt@MkWplxO`ASgh?hBsuuP|xD~LSH~KBlRs]t|l|_tQAroDRqWS^SEr[sYdPB}TAROtW{mIkE|dWOuLgLmJrucGLpebrAFKWjikTUzS|j}M}szasKOmrjy[?hpwnEfX[jGpLt@^v_eNwSQHNwtOtDgWD{rk|UgASs@mziIXrsHN_|hZuxXlPJOsA^^?QY^yGoCBx{ekLuZzRqQZdsNSx@ezDAn{XNj@fRXIwrDX?{ZQHwTEfu@GhxDOykqts|n{jOeZ@c`dvTY?e^]ATvWpb?SVyg]GC?SlzteilZJAL]mlhLjYZazY__qcVFYvt@|bIQnSno@OXyt]OulzkWqH`rYFWrwGs`v|~XeTsIssLrbmHZCYHiJrX}eEzSssH}]l]IhPQhPoQ}rCXLyhFIT[clhzYOvyHqigxmjz`phKUU^TPf[GRAIhNqSOdayFP@FmKmuIzMOeoqdpxyCOwCthcLq?n`L`tLIBboNn~uXeFcPE{C~mC`h]jUUUQe^`UqvzCutYCgct|SBrAeiYQW?X~KzCz}guXbsUw?pLsg@hDArw?KeJD[BN?GD@wgFWCiHq@Ypp_QKFixEKWqRp]oJFuVIEvjDcTFu~Zz]a{IcXhWuIdMQjJ]lwmGQ|]g~c]Hl]pl`Pd^?loIcsoNir_kikBYyg?NarXZEGYspt_vLBIoj}LI[uBFvm}tbqvC|xyR~a{kob|HlctZslTGtPDhBKsNsoZPuH`U`Fqg{gKnGSHVLJ^O`zmNgMn~{rsQuoymw^JY?iUBvw_~mMr|GrPHTERS[MiNpY[Mm{ggHpzRaJaoFomtdaQ_?xuTRm}@KjU~RtPsAdxa|uHmy}n^i||FVL[eQAPrWfLm^ndczgF~Nk~aplQvTUpHvnTya]kOenZlLAQIm{lPl@CCTchvCF[fI{^zPkeYZTiamoEcKmBMfZhk_j_~Fjp|wPVZlkh_nHu]@tP|hS@^G^PdsQ~f[RqgTDqezxNFcaO}HZhb|MMiNSYSAnQWCDJukT~e|OTgc}sf[cnr?fyzTa|EwEtRG|I~|IO}O]S|rp]CQ}}DWhSjC_|z|oY|FYl@WkCOoPuWuqr{fJu?Brs^_EBI[@_OCKs}?]O`jnDiXBvaIWhhMAQDNb{U`bqVR}oqVAvR@AZHEBY@depD]OLh`kf^UsHhzKT}CS}HQKy}Q~AeMydXPQztWSSzDnghULQgMAmbWIZ|lWWeEXrE^EeNoZApooEmrXe{NAnoDf`m}UNlRdqQ@jOc~HLOMWs]IDqJHYoMziEedGBPOxOb?[X`KxkFRg@`mgFYnP{hSaxwZfBQqTm}_?RSEaQga]w[vxc]hMne}VfSlqUeMo_iqmd`ilnJXnhdj^EEFifvZyxYFRf^VaqBhLyrGlk~qowqzHOBlOwtx?i{m~`n^G?Yxzxux}b{LSlx]dS~thO^lYE}bzKmUEzwW^{rPGhbEov[Plv??xtyKJshbG`KuO?hjBdS@Ru}iGpvFXJRrvOlrKN?`I_n_tplk}kgwSXuKylXbRQ]]?a|{xiT[li?k]CJpwy^o@ebyGQrPfF`aszGKp]baIx~H?ElETtFh]dz[OjGl@C?]VDhr}OE@V]wLTc[WErXacM{We`F|utKKjgllAxvsVYBZ@HcuMgLboFHVZmi}eIXAIFhS@A@FGRbjeoJWZ_NKd^oEH`qgy`q[Tq{x?LRP|GfBFFJV|fgZs`MLbpPYUdIV^]mD@FG]pYAT^A^RNCcXVrPsgk{jTrAIQPs_`mD}rOqAZA[}RETFz]WkXFTz_m{N@{W@_fPKZLT`@aIqf|L^Mb|crNqZ{BVsijzpGPEKQQZGlApDn`ruH}cvF|iXcNqK}cxe_U~HRnKV}sCYb`D~oGvwG[Ca|UaybXea~DdD~LiIbGRxJ_VGheI{ika}KC[OZJLn^IBkPrQj_EuoFwZ}DpoBRcK]Q}?EmTv~i_Tul{bky?Iit~tgS|o}JL_VYcCQdjeJ_MfaA`FgCgc[Ii|CBHwq~nbJeYTK{e`CNstKfTKPzw{jdhp|qsZyP_FcugxCFNpKitlR~vUrx^NrSVsSTaEgnxZTmKc`R|lGJeX}ccKLsQZQhsFkeFd|ckHIVTlGMg`~uPwuHRJS_CPuN_ogXe{Ba}dO_UBhuNXby|h?JlgBIqMKx^_u{molgL[W_iavNQuOq?ap]PGB`clAicnl@k~pA?MWHEZ{HuTLsCpOxxrKlBh]FyMjLdFl|nMIvTHyGAlPogqfZ?PlvlFJvYnDQd}R@uAhtJmDfe|iJqdkYr}r@mEjjIetDl_I`TELfoR|qTBu@Tic[BaXjP?dCS~MUK[HPRI}OUOwAaf|_}HZzrwXvbnNgltjTwkBE~MztTQhtRSWoQHajMoVyBBA`kdgK~h`o[J`dm~pm]tk@i`[F~F]DBlJKklrkR]SNw@{aG~Vhl`KINsQkOy?WhcqUMTGDOM_]bUjVd|Yh_KUCCgIJ|LDIGZCPls{RzbVWVLEhHvWBzKq|^N?DyJB|__aCUjoEgsARki}j@DQXS`RNU|DJ^a~d{sh_Iu{ONcUtSrGWW@cvUjefHHi}eSSGrNtO?cTPBShLqzwMVjWQQCCFB^culBjZHEK_{dO~Q`YhJYFn]jq~XSnG@[lQr]eKrjXpG~L^h~tDgEma^AUFThlaR{xyuP@[^VFwXSeUbVetufa@dX]CLyAnDV@Bs[DnpeghJw^?UIana}r_CKGDySoRudklbgio}kIDpA@McDoPK?iYcG?_zOmnWfJp}a[JLR[stXMo?_^Ng[whQlrDbrawZeSZ~SJstIObdDSfAA{MV}?gNunLOnbMv_~KFQUAjIMj^GkoGxuYtYbGDImEYiwEMyTpMxN_LSnSMdl{bg@dtAnAMvhDTBR_FxoQgANniRqxd`pWv@rFJ|mWNWmh[GMJz_Nq`BIN@KsjMPASXORcdHjf~rJfgZYe_uulzqM_KdPlMsuvU^YJuLtofPhGonVOQxCMuXliNvJIaoC?hSxcxKVVxWlNs^ENDvCtSmO~WxI[itnjs^RDvI@KqG}YekaSbTaB]ki]XM@[ZnDAP~@|BzLRgOzmjmPkRE@_sobkT|SszXK[rZN?F]Z_u}Yue^[BZgLtR}FHzWyxWEX^wXC]MJmiVbQuBzkgRcKGUhOvUc_bga|Tx`KEM`JWEgTpFYVeXLCm|mctZR@uKTDeUONPozBeIkrY`cz]]~WPGMUf`MNUGHDbxZuO{gmsKYkAGRPqjc|_FtblEOwy}dnwCHo]PJhN~JoteaJ?dmYZeB^Xd?X^pOKDbOMF@Ugg^hETLdhwlA}PL@_ur|o{VZosP?ntJ_kG][g{Zq`Tu]dzQlSWiKfnxDnk}KOzp~tdFstMobmy[oPYjyOtUzMWdjcNSUAjRuqhLS@AwB^{BFnqjCmmlk?jpn}TksS{KcKkDboXiwK]qMVjm~V`LgWhjS^nLGwfhAYrjDSBL_{cRus~{?xar_xqPlArrYFd?pHKdMEZzzjJpfC?Hv}mAuIDkyBxFpxhstTx`IO{rp}XGuQ]VtbHerlRc_LFGWK[XluFcNGUtDYMZny[M^nVKVeMllQI[xtvwQnXFlWYqxZZFp_|]^oWX[{pOMpxXxvkbyJA[DrPzwD|LW|QcV{Nw~U^dgguSpG]ClmO@j_TENIGjPWwgdVbHganhM?ema|dBaqla|WBd`poj~klxaasKxGG^xbWquAl~_lKWxUkDFagMnE{zHug{b`A~IYcQYBF_E}wiA}K@yxWHrZ{[d~|ARsYsjeNWzkMs~IOqqp[yzDE|WFrivsidTcnbHFRoW@XpAV`lv_zj?B~tPCppRjgbbDTALeFaOf?VcjnKTQMLyp{NwdylHCqmo?oelhjWuXj~}{fpuX`fra?GNkDiChYgVSh{R[BgF~eQa^WVz}ATI_CpY?g_diae]|ijH`TyNIF}|D_xpmBq_JpKih{Ba|sWzhnAoyraiDvk`h{qbBfsylBGmRH}DRPdryEsSaKS~tIaeF[s]I~xxHVrcNe@Jjxa@jlhZueLQqHh_]twVMqG_EGuwyab{nxOF?`HCle}nBZzlTQjkLmoXbXhOtBglFoMz?eqre`HiE@vNwBulglmQjj]DB@pPkPUgA^sjOAUNdSu_`oAzar?n?eMnw{{hYmslYi[TnlJD'",..."]charCodeAtUinyxpf","for(;e<10359;c[e++]=p-=128,A=A?p-A&&A:p==34&&p)for(p=1;p<128;y=f.map((n,x)=>(U=r[n]*2+1,U=Math.log(U/(h-U)),t-=a[x]*U,U/500)),t=~-h/(1+Math.exp(t))|1,i=o%h<t,o=o%h+(i?t:h-t)*(o>>17)-!i*t,f.map((n,x)=>(U=r[n]+=(i*h/2-r[n]<<13)/((C[n]+=C[n]<5)+1/20)>>13,a[x]+=y[x]*(i-t/h))),p=p*2+i)for(f='010202103203210431053105410642065206541'.split(t=0).map((n,x)=>(U=0,[...n].map((n,x)=>(U=U*997+(c[e-n]|0)|0)),h*32-1&U*997+p+!!A*129)*12+x);o<h*32;o=o*64|M.charCodeAt(d++)&63);for(C=String.fromCharCode(...c);r=/[\0-#?@\\\\~]/.exec(C);)with(C.split(r))C=join(shift());return C")([],[],131072,[0,0,0,0,0,0,0,0,0,0,0,0],new Uint16Array(51e6).fill(32768),new Uint8Array(51e6),0,0,0,0));"use strict";let glCanvas,glContext,glTileTexture,glActiveTexture,glShader,glPositionData,glColorData,glBatchCount,glBatchAdditive,glAdditive;function glInit(){if(glEnable){glCanvas=document.createElement("canvas");glContext=glCanvas.getContext("webgl",{antialias:!1});glCanvas.style=styleCanvas;glTileTexture=glCreateTexture(tileImage);glOverlay&&document.body.appendChild(glCanvas);glShader=glCreateProgram("precision highp float;uniform mat4 m;attribute vec2 p,t;attribute vec4 c,a;varying vec2 v;varying vec4 d,e;void main(){gl_Position=m*vec4(p,1,1);v=t;d=c;e=a;}","precision highp float;varying vec2 v;varying vec4 d,e;uniform sampler2D s;void main(){gl_FragColor=texture2D(s,v)*d+e;}");var a=new ArrayBuffer(gl_MAX_BATCH*gl_VERTICES_PER_QUAD*gl_VERTEX_BYTE_STRIDE);glCreateBuffer(gl_ARRAY_BUFFER,a.byteLength,gl_DYNAMIC_DRAW);glPositionData=new Float32Array(a);glColorData=new Uint32Array(a);var b=glBatchCount=0;a=(c,d,e,f,h=0)=>{c=glContext.getAttribLocation(glShader,c);glContext.enableVertexAttribArray(c);glContext.vertexAttribPointer(c,f,d,h,gl_VERTEX_BYTE_STRIDE,b);b+=f*e};a("p",gl_FLOAT,4,2);a("t",gl_FLOAT,4,2);a("c",gl_UNSIGNED_BYTE,1,4,1);a("a",gl_UNSIGNED_BYTE,1,4,1)}}function glSetBlendMode(a){glEnable&&(glAdditive=a)}function glSetTexture(a=glTileTexture){glEnable&&(a!=glActiveTexture&&glFlush(),glContext.bindTexture(gl_TEXTURE_2D,glActiveTexture=a))}function glCompileShader(a,b){if(glEnable){b=glContext.createShader(b);glContext.shaderSource(b,a);glContext.compileShader(b);if(debug&&!glContext.getShaderParameter(b,gl_COMPILE_STATUS))throw glContext.getShaderInfoLog(b);return b}}function glCreateProgram(a,b){if(glEnable){var c=glContext.createProgram();glContext.attachShader(c,glCompileShader(a,gl_VERTEX_SHADER));glContext.attachShader(c,glCompileShader(b,gl_FRAGMENT_SHADER));glContext.linkProgram(c);if(debug&&!glContext.getProgramParameter(c,gl_LINK_STATUS))throw glContext.getProgramInfoLog(c);return c}}function glCreateBuffer(a,b,c){if(glEnable){var d=glContext.createBuffer();glContext.bindBuffer(a,d);glContext.bufferData(a,b,c);return d}}function glCreateTexture(a){if(glEnable&&a&&a.width){var b=glContext.createTexture();glContext.bindTexture(gl_TEXTURE_2D,b);glContext.texImage2D(gl_TEXTURE_2D,0,gl_RGBA,gl_RGBA,gl_UNSIGNED_BYTE,a);glContext.texParameteri(gl_TEXTURE_2D,gl_TEXTURE_MIN_FILTER,cavasPixelated?gl_NEAREST:gl_LINEAR);glContext.texParameteri(gl_TEXTURE_2D,gl_TEXTURE_MAG_FILTER,cavasPixelated?gl_NEAREST:gl_LINEAR);glContext.texParameteri(gl_TEXTURE_2D,gl_TEXTURE_WRAP_S,gl_CLAMP_TO_EDGE);glContext.texParameteri(gl_TEXTURE_2D,gl_TEXTURE_WRAP_T,gl_CLAMP_TO_EDGE);return b}}function glPreRender(a,b,c,d,e){glEnable&&(glContext.viewport(0,0,glCanvas.width=a,glCanvas.height=b),glContext.clear(gl_COLOR_BUFFER_BIT),glContext.bindTexture(gl_TEXTURE_2D,glActiveTexture=glTileTexture),glContext.useProgram(glShader),glSetBlendMode(),a=2*e/a,b=2*e/b,glContext.uniformMatrix4fv(glContext.getUniformLocation(glShader,"m"),0,new Float32Array([a,0,0,0,0,b,0,0,1,1,-1,1,-1-a*c,-1-b*d,0,0])))}function glFlush(){if(glEnable&&glBatchCount){var a=glBatchAdditive?gl_ONE:gl_ONE_MINUS_SRC_ALPHA;glContext.blendFuncSeparate(gl_SRC_ALPHA,a,gl_ONE,a);glContext.enable(gl_BLEND);glContext.bufferSubData(gl_ARRAY_BUFFER,0,glPositionData.subarray(0,glBatchCount*gl_VERTICES_PER_QUAD*gl_INDICIES_PER_VERT));glContext.drawArrays(gl_TRIANGLES,0,glBatchCount*gl_VERTICES_PER_QUAD);glBatchCount=0;glBatchAdditive=glAdditive}}function glCopyToContext(a,b){if(glEnable&&glBatchCount||b)glFlush(),glOverlay&&!b||a.drawImage(glCanvas,0,0)}function glDraw(a,b,c,d,e,f,h,g,k,m=4294967295,n=0){if(glEnable){glBatchCount!=gl_MAX_BATCH&&glBatchAdditive==glAdditive||glFlush();var p=Math.cos(e)/2,l=Math.sin(e)/2;e=p*c;p*=d;c*=l;d*=l;l=glBatchCount++*gl_VERTICES_PER_QUAD*gl_INDICIES_PER_VERT;glPositionData[l++]=a-e-d;glPositionData[l++]=b-p+c;glPositionData[l++]=f;glPositionData[l++]=k;glColorData[l++]=m;glColorData[l++]=n;glPositionData[l++]=a+e+d;glPositionData[l++]=b+p-c;glPositionData[l++]=g;glPositionData[l++]=h;glColorData[l++]=m;glColorData[l++]=n;glPositionData[l++]=a-e+d;glPositionData[l++]=b+p+c;glPositionData[l++]=f;glPositionData[l++]=h;glColorData[l++]=m;glColorData[l++]=n;glPositionData[l++]=a-e-d;glPositionData[l++]=b-p+c;glPositionData[l++]=f;glPositionData[l++]=k;glColorData[l++]=m;glColorData[l++]=n;glPositionData[l++]=a+e-d;glPositionData[l++]=b-p-c;glPositionData[l++]=g;glPositionData[l++]=k;glColorData[l++]=m;glColorData[l++]=n;glPositionData[l++]=a+e+d;glPositionData[l++]=b+p-c;glPositionData[l++]=g;glPositionData[l++]=h;glColorData[l++]=m;glColorData[l++]=n}}const gl_ONE=1,gl_TRIANGLES=4,gl_SRC_ALPHA=770,gl_ONE_MINUS_SRC_ALPHA=771,gl_BLEND=3042,gl_TEXTURE_2D=3553,gl_UNSIGNED_BYTE=5121,gl_FLOAT=5126,gl_RGBA=6408,gl_NEAREST=9728,gl_LINEAR=9729,gl_TEXTURE_MAG_FILTER=10240,gl_TEXTURE_MIN_FILTER=10241,gl_TEXTURE_WRAP_S=10242,gl_TEXTURE_WRAP_T=10243,gl_COLOR_BUFFER_BIT=16384,gl_CLAMP_TO_EDGE=33071,gl_ARRAY_BUFFER=34962,gl_DYNAMIC_DRAW=35048,gl_FRAGMENT_SHADER=35632,gl_VERTEX_SHADER=35633,gl_COMPILE_STATUS=35713,gl_LINK_STATUS=35714,gl_VERTICES_PER_QUAD=6,gl_INDICIES_PER_VERT=6,gl_MAX_BATCH=65536,gl_VERTEX_BYTE_STRIDE=24;
|
|
@@ -8,9 +8,9 @@
|
|
|
8
8
|
|
|
9
9
|
'use strict';
|
|
10
10
|
|
|
11
|
+
let showWatermark = 0;
|
|
12
|
+
let godMode = 0;
|
|
11
13
|
const debug = 0;
|
|
12
|
-
const showWatermark = 0;
|
|
13
|
-
const godMode = 0;
|
|
14
14
|
const debugOverlay = 0;
|
|
15
15
|
const debugPhysics = 0;
|
|
16
16
|
const debugParticles = 0;
|
|
@@ -442,6 +442,34 @@ class Color
|
|
|
442
442
|
return this;
|
|
443
443
|
}
|
|
444
444
|
|
|
445
|
+
/** Returns this color expressed in hsla format
|
|
446
|
+
* @return {Array} */
|
|
447
|
+
getHSLA()
|
|
448
|
+
{
|
|
449
|
+
const r = this.r;
|
|
450
|
+
const g = this.g;
|
|
451
|
+
const b = this.b;
|
|
452
|
+
const a = this.a;
|
|
453
|
+
const max = Math.max(r, g, b);
|
|
454
|
+
const min = Math.min(r, g, b);
|
|
455
|
+
const l = (max + min) / 2;
|
|
456
|
+
|
|
457
|
+
let h = 0, s = 0;
|
|
458
|
+
if (max != min)
|
|
459
|
+
{
|
|
460
|
+
let d = max - min;
|
|
461
|
+
s = l > .5 ? d / (2 - max - min) : d / (max + min);
|
|
462
|
+
if (r == max)
|
|
463
|
+
h = (g - b) / d + (g < b ? 6 : 0);
|
|
464
|
+
else if (g == max)
|
|
465
|
+
h = (b - r) / d + 2;
|
|
466
|
+
else if (b == max)
|
|
467
|
+
h = (r - g) / d + 4;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
return [h / 6, s, l, a];
|
|
471
|
+
}
|
|
472
|
+
|
|
445
473
|
/** Returns a new color that has each component randomly adjusted
|
|
446
474
|
* @param {Number} [amount=.05]
|
|
447
475
|
* @param {Number} [alphaAmount=0]
|
|
@@ -473,42 +501,6 @@ class Color
|
|
|
473
501
|
return (this.r*255|0) + (this.g*255<<8) + (this.b*255<<16) + (this.a*255<<24);
|
|
474
502
|
}
|
|
475
503
|
|
|
476
|
-
/** Returns this color expressed as a hex code
|
|
477
|
-
* @return {String} */
|
|
478
|
-
hex()
|
|
479
|
-
{
|
|
480
|
-
const toHex = (c)=> ((c=c*255|0)<16 ? '0' : '') + c.toString(16);
|
|
481
|
-
return '#' + toHex(this.r) + toHex(this.g) + toHex(this.b);
|
|
482
|
-
}
|
|
483
|
-
|
|
484
|
-
/** Returns this color expressed in hsla format
|
|
485
|
-
* @return {Array} */
|
|
486
|
-
getHSLA()
|
|
487
|
-
{
|
|
488
|
-
const r = this.r;
|
|
489
|
-
const g = this.g;
|
|
490
|
-
const b = this.b;
|
|
491
|
-
const a = this.a;
|
|
492
|
-
const max = Math.max(r, g, b);
|
|
493
|
-
const min = Math.min(r, g, b);
|
|
494
|
-
const l = (max + min) / 2;
|
|
495
|
-
|
|
496
|
-
let h = 0, s = 0;
|
|
497
|
-
if (max != min)
|
|
498
|
-
{
|
|
499
|
-
let d = max - min;
|
|
500
|
-
s = l > .5 ? d / (2 - max - min) : d / (max + min);
|
|
501
|
-
if (r == max)
|
|
502
|
-
h = (g - b) / d + (g < b ? 6 : 0);
|
|
503
|
-
else if (g == max)
|
|
504
|
-
h = (b - r) / d + 2;
|
|
505
|
-
else if (b == max)
|
|
506
|
-
h = (r - g) / d + 4;
|
|
507
|
-
}
|
|
508
|
-
|
|
509
|
-
return [h / 6, s, l, a];
|
|
510
|
-
}
|
|
511
|
-
|
|
512
504
|
/** Set this color from a hex code
|
|
513
505
|
* @param {String} hex - html hex code
|
|
514
506
|
* @return {Color} */
|
|
@@ -522,6 +514,14 @@ class Color
|
|
|
522
514
|
ASSERT(this.r>=0 && this.r<=1 && this.g>=0 && this.g<=1 && this.b>=0 && this.b<=1);
|
|
523
515
|
return this;
|
|
524
516
|
}
|
|
517
|
+
|
|
518
|
+
/** Returns this color expressed as a hex code
|
|
519
|
+
* @return {String} */
|
|
520
|
+
getHex()
|
|
521
|
+
{
|
|
522
|
+
const toHex = (c)=> ((c=c*255|0)<16 ? '0' : '') + c.toString(16);
|
|
523
|
+
return '#' + toHex(this.r) + toHex(this.g) + toHex(this.b);
|
|
524
|
+
}
|
|
525
525
|
}
|
|
526
526
|
|
|
527
527
|
///////////////////////////////////////////////////////////////////////////////
|
|
@@ -588,7 +588,8 @@ class Timer
|
|
|
588
588
|
* @memberof Settings */
|
|
589
589
|
let canvasMaxSize = vec2(1920, 1200);
|
|
590
590
|
|
|
591
|
-
/** Fixed size of the canvas, if enabled
|
|
591
|
+
/** Fixed size of the canvas, if enabled canvas size never changes
|
|
592
|
+
* - you may also need to set mainCanvasSize if using screen space coords in startup
|
|
592
593
|
* @type {Vector2}
|
|
593
594
|
* @default
|
|
594
595
|
* @memberof Settings */
|
|
@@ -818,7 +819,7 @@ let medalDisplayIconSize = 50;
|
|
|
818
819
|
const engineName = 'LittleJS';
|
|
819
820
|
|
|
820
821
|
/** Version of engine */
|
|
821
|
-
const engineVersion = '1.3.
|
|
822
|
+
const engineVersion = '1.3.8';
|
|
822
823
|
|
|
823
824
|
/** Frames per second to update objects
|
|
824
825
|
* @default */
|
|
@@ -909,6 +910,33 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
909
910
|
timeReal += frameTimeDeltaMS / 1e3;
|
|
910
911
|
frameTimeBufferMS = min(frameTimeBufferMS + !paused * frameTimeDeltaMS, 50); // clamp incase of slow framerate
|
|
911
912
|
|
|
913
|
+
if (canvasFixedSize.x)
|
|
914
|
+
{
|
|
915
|
+
// clear set fixed size
|
|
916
|
+
overlayCanvas.width = mainCanvas.width = canvasFixedSize.x;
|
|
917
|
+
overlayCanvas.height = mainCanvas.height = canvasFixedSize.y;
|
|
918
|
+
|
|
919
|
+
// fit to window by adding space on top or bottom if necessary
|
|
920
|
+
const aspect = innerWidth / innerHeight;
|
|
921
|
+
const fixedAspect = mainCanvas.width / mainCanvas.height;
|
|
922
|
+
mainCanvas.style.width = overlayCanvas.style.width = aspect < fixedAspect ? '100%' : '';
|
|
923
|
+
mainCanvas.style.height = overlayCanvas.style.height = aspect < fixedAspect ? '' : '100%';
|
|
924
|
+
if (glCanvas)
|
|
925
|
+
{
|
|
926
|
+
glCanvas.style.width = mainCanvas.style.width;
|
|
927
|
+
glCanvas.style.height = mainCanvas.style.height;
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
else
|
|
931
|
+
{
|
|
932
|
+
// clear and set size to same as window
|
|
933
|
+
overlayCanvas.width = mainCanvas.width = min(innerWidth, canvasMaxSize.x);
|
|
934
|
+
overlayCanvas.height = mainCanvas.height = min(innerHeight, canvasMaxSize.y);
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
// save canvas size
|
|
938
|
+
mainCanvasSize = vec2(mainCanvas.width, mainCanvas.height);
|
|
939
|
+
|
|
912
940
|
if (paused)
|
|
913
941
|
{
|
|
914
942
|
// do post update even when paused
|
|
@@ -945,30 +973,6 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
945
973
|
// add the time smoothing back in
|
|
946
974
|
frameTimeBufferMS += deltaSmooth;
|
|
947
975
|
}
|
|
948
|
-
|
|
949
|
-
if (canvasFixedSize.x)
|
|
950
|
-
{
|
|
951
|
-
// clear set fixed size
|
|
952
|
-
mainCanvas.width = canvasFixedSize.x;
|
|
953
|
-
mainCanvas.height = canvasFixedSize.y;
|
|
954
|
-
|
|
955
|
-
// fit to window by adding space on top or bottom if necessary
|
|
956
|
-
const aspect = innerWidth / innerHeight;
|
|
957
|
-
const fixedAspect = mainCanvas.width / mainCanvas.height;
|
|
958
|
-
mainCanvas.style.width = overlayCanvas.style.width = aspect < fixedAspect ? '100%' : '';
|
|
959
|
-
mainCanvas.style.height = overlayCanvas.style.height = aspect < fixedAspect ? '' : '100%';
|
|
960
|
-
if (glCanvas)
|
|
961
|
-
{
|
|
962
|
-
glCanvas.style.width = mainCanvas.style.width;
|
|
963
|
-
glCanvas.style.height = mainCanvas.style.height;
|
|
964
|
-
}
|
|
965
|
-
}
|
|
966
|
-
else
|
|
967
|
-
{
|
|
968
|
-
// clear and set size to same as window
|
|
969
|
-
mainCanvas.width = min(innerWidth, canvasMaxSize.x);
|
|
970
|
-
mainCanvas.height = min(innerHeight, canvasMaxSize.y);
|
|
971
|
-
}
|
|
972
976
|
|
|
973
977
|
// render sort then render while removing destroyed objects
|
|
974
978
|
enginePreRender();
|
|
@@ -1008,8 +1012,8 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
1008
1012
|
// called by engine to setup render system
|
|
1009
1013
|
function enginePreRender()
|
|
1010
1014
|
{
|
|
1011
|
-
// save canvas size
|
|
1012
|
-
mainCanvasSize = vec2(
|
|
1015
|
+
// save canvas size
|
|
1016
|
+
mainCanvasSize = vec2(mainCanvas.width, mainCanvas.height);
|
|
1013
1017
|
|
|
1014
1018
|
// disable smoothing for pixel art
|
|
1015
1019
|
mainContext.imageSmoothingEnabled = !cavasPixelated;
|
|
@@ -1507,18 +1511,26 @@ let overlayContext;
|
|
|
1507
1511
|
let mainCanvasSize = vec2();
|
|
1508
1512
|
|
|
1509
1513
|
/** Convert from screen to world space coordinates
|
|
1514
|
+
* - if calling outside of render, you may need to manually set mainCanvasSize
|
|
1510
1515
|
* @param {Vector2} screenPos
|
|
1511
1516
|
* @return {Vector2}
|
|
1512
1517
|
* @memberof Draw */
|
|
1513
1518
|
const screenToWorld = (screenPos)=>
|
|
1514
|
-
|
|
1519
|
+
{
|
|
1520
|
+
ASSERT(mainCanvasSize.x && mainCanvasSize.y, 'mainCanvasSize is invalid');
|
|
1521
|
+
return screenPos.add(vec2(.5)).subtract(mainCanvasSize.scale(.5)).multiply(vec2(1/cameraScale,-1/cameraScale)).add(cameraPos);
|
|
1522
|
+
}
|
|
1515
1523
|
|
|
1516
1524
|
/** Convert from world to screen space coordinates
|
|
1525
|
+
* - if calling outside of render, you may need to manually set mainCanvasSize
|
|
1517
1526
|
* @param {Vector2} worldPos
|
|
1518
1527
|
* @return {Vector2}
|
|
1519
1528
|
* @memberof Draw */
|
|
1520
1529
|
const worldToScreen = (worldPos)=>
|
|
1521
|
-
|
|
1530
|
+
{
|
|
1531
|
+
ASSERT(mainCanvasSize.x && mainCanvasSize.y, 'mainCanvasSize is invalid');
|
|
1532
|
+
return worldPos.subtract(cameraPos).multiply(vec2(cameraScale,-cameraScale)).add(mainCanvasSize.scale(.5)).subtract(vec2(.5));
|
|
1533
|
+
}
|
|
1522
1534
|
|
|
1523
1535
|
/** Draw textured tile centered in world space, with color applied if using WebGL
|
|
1524
1536
|
* @param {Vector2} pos - Center of the tile in world space
|
|
@@ -1996,7 +2008,7 @@ const mouseToScreen = (mousePos)=>
|
|
|
1996
2008
|
return vec2(); // fix bug that can occur if user clicks before page loads
|
|
1997
2009
|
|
|
1998
2010
|
const rect = mainCanvas.getBoundingClientRect();
|
|
1999
|
-
return
|
|
2011
|
+
return vec2(mainCanvas.width, mainCanvas.height).multiply(
|
|
2000
2012
|
vec2(percent(mousePos.x, rect.left, rect.right), percent(mousePos.y, rect.top, rect.bottom)));
|
|
2001
2013
|
}
|
|
2002
2014
|
|
package/engine/engine.js
CHANGED
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
const engineName = 'LittleJS';
|
|
23
23
|
|
|
24
24
|
/** Version of engine */
|
|
25
|
-
const engineVersion = '1.3.
|
|
25
|
+
const engineVersion = '1.3.8';
|
|
26
26
|
|
|
27
27
|
/** Frames per second to update objects
|
|
28
28
|
* @default */
|
|
@@ -113,6 +113,33 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
113
113
|
timeReal += frameTimeDeltaMS / 1e3;
|
|
114
114
|
frameTimeBufferMS = min(frameTimeBufferMS + !paused * frameTimeDeltaMS, 50); // clamp incase of slow framerate
|
|
115
115
|
|
|
116
|
+
if (canvasFixedSize.x)
|
|
117
|
+
{
|
|
118
|
+
// clear set fixed size
|
|
119
|
+
overlayCanvas.width = mainCanvas.width = canvasFixedSize.x;
|
|
120
|
+
overlayCanvas.height = mainCanvas.height = canvasFixedSize.y;
|
|
121
|
+
|
|
122
|
+
// fit to window by adding space on top or bottom if necessary
|
|
123
|
+
const aspect = innerWidth / innerHeight;
|
|
124
|
+
const fixedAspect = mainCanvas.width / mainCanvas.height;
|
|
125
|
+
mainCanvas.style.width = overlayCanvas.style.width = aspect < fixedAspect ? '100%' : '';
|
|
126
|
+
mainCanvas.style.height = overlayCanvas.style.height = aspect < fixedAspect ? '' : '100%';
|
|
127
|
+
if (glCanvas)
|
|
128
|
+
{
|
|
129
|
+
glCanvas.style.width = mainCanvas.style.width;
|
|
130
|
+
glCanvas.style.height = mainCanvas.style.height;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
else
|
|
134
|
+
{
|
|
135
|
+
// clear and set size to same as window
|
|
136
|
+
overlayCanvas.width = mainCanvas.width = min(innerWidth, canvasMaxSize.x);
|
|
137
|
+
overlayCanvas.height = mainCanvas.height = min(innerHeight, canvasMaxSize.y);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// save canvas size
|
|
141
|
+
mainCanvasSize = vec2(mainCanvas.width, mainCanvas.height);
|
|
142
|
+
|
|
116
143
|
if (paused)
|
|
117
144
|
{
|
|
118
145
|
// do post update even when paused
|
|
@@ -149,30 +176,6 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
149
176
|
// add the time smoothing back in
|
|
150
177
|
frameTimeBufferMS += deltaSmooth;
|
|
151
178
|
}
|
|
152
|
-
|
|
153
|
-
if (canvasFixedSize.x)
|
|
154
|
-
{
|
|
155
|
-
// clear set fixed size
|
|
156
|
-
mainCanvas.width = canvasFixedSize.x;
|
|
157
|
-
mainCanvas.height = canvasFixedSize.y;
|
|
158
|
-
|
|
159
|
-
// fit to window by adding space on top or bottom if necessary
|
|
160
|
-
const aspect = innerWidth / innerHeight;
|
|
161
|
-
const fixedAspect = mainCanvas.width / mainCanvas.height;
|
|
162
|
-
mainCanvas.style.width = overlayCanvas.style.width = aspect < fixedAspect ? '100%' : '';
|
|
163
|
-
mainCanvas.style.height = overlayCanvas.style.height = aspect < fixedAspect ? '' : '100%';
|
|
164
|
-
if (glCanvas)
|
|
165
|
-
{
|
|
166
|
-
glCanvas.style.width = mainCanvas.style.width;
|
|
167
|
-
glCanvas.style.height = mainCanvas.style.height;
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
else
|
|
171
|
-
{
|
|
172
|
-
// clear and set size to same as window
|
|
173
|
-
mainCanvas.width = min(innerWidth, canvasMaxSize.x);
|
|
174
|
-
mainCanvas.height = min(innerHeight, canvasMaxSize.y);
|
|
175
|
-
}
|
|
176
179
|
|
|
177
180
|
// render sort then render while removing destroyed objects
|
|
178
181
|
enginePreRender();
|
|
@@ -212,8 +215,8 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
212
215
|
// called by engine to setup render system
|
|
213
216
|
function enginePreRender()
|
|
214
217
|
{
|
|
215
|
-
// save canvas size
|
|
216
|
-
mainCanvasSize = vec2(
|
|
218
|
+
// save canvas size
|
|
219
|
+
mainCanvasSize = vec2(mainCanvas.width, mainCanvas.height);
|
|
217
220
|
|
|
218
221
|
// disable smoothing for pixel art
|
|
219
222
|
mainContext.imageSmoothingEnabled = !cavasPixelated;
|
package/engine/engineDraw.js
CHANGED
|
@@ -53,18 +53,26 @@ let overlayContext;
|
|
|
53
53
|
let mainCanvasSize = vec2();
|
|
54
54
|
|
|
55
55
|
/** Convert from screen to world space coordinates
|
|
56
|
+
* - if calling outside of render, you may need to manually set mainCanvasSize
|
|
56
57
|
* @param {Vector2} screenPos
|
|
57
58
|
* @return {Vector2}
|
|
58
59
|
* @memberof Draw */
|
|
59
60
|
const screenToWorld = (screenPos)=>
|
|
60
|
-
|
|
61
|
+
{
|
|
62
|
+
ASSERT(mainCanvasSize.x && mainCanvasSize.y, 'mainCanvasSize is invalid');
|
|
63
|
+
return screenPos.add(vec2(.5)).subtract(mainCanvasSize.scale(.5)).multiply(vec2(1/cameraScale,-1/cameraScale)).add(cameraPos);
|
|
64
|
+
}
|
|
61
65
|
|
|
62
66
|
/** Convert from world to screen space coordinates
|
|
67
|
+
* - if calling outside of render, you may need to manually set mainCanvasSize
|
|
63
68
|
* @param {Vector2} worldPos
|
|
64
69
|
* @return {Vector2}
|
|
65
70
|
* @memberof Draw */
|
|
66
71
|
const worldToScreen = (worldPos)=>
|
|
67
|
-
|
|
72
|
+
{
|
|
73
|
+
ASSERT(mainCanvasSize.x && mainCanvasSize.y, 'mainCanvasSize is invalid');
|
|
74
|
+
return worldPos.subtract(cameraPos).multiply(vec2(cameraScale,-cameraScale)).add(mainCanvasSize.scale(.5)).subtract(vec2(.5));
|
|
75
|
+
}
|
|
68
76
|
|
|
69
77
|
/** Draw textured tile centered in world space, with color applied if using WebGL
|
|
70
78
|
* @param {Vector2} pos - Center of the tile in world space
|
package/engine/engineInput.js
CHANGED
|
@@ -162,7 +162,7 @@ const mouseToScreen = (mousePos)=>
|
|
|
162
162
|
return vec2(); // fix bug that can occur if user clicks before page loads
|
|
163
163
|
|
|
164
164
|
const rect = mainCanvas.getBoundingClientRect();
|
|
165
|
-
return
|
|
165
|
+
return vec2(mainCanvas.width, mainCanvas.height).multiply(
|
|
166
166
|
vec2(percent(mousePos.x, rect.left, rect.right), percent(mousePos.y, rect.top, rect.bottom)));
|
|
167
167
|
}
|
|
168
168
|
|
package/engine/engineRelease.js
CHANGED
package/engine/engineSettings.js
CHANGED
|
@@ -14,7 +14,8 @@
|
|
|
14
14
|
* @memberof Settings */
|
|
15
15
|
let canvasMaxSize = vec2(1920, 1200);
|
|
16
16
|
|
|
17
|
-
/** Fixed size of the canvas, if enabled
|
|
17
|
+
/** Fixed size of the canvas, if enabled canvas size never changes
|
|
18
|
+
* - you may also need to set mainCanvasSize if using screen space coords in startup
|
|
18
19
|
* @type {Vector2}
|
|
19
20
|
* @default
|
|
20
21
|
* @memberof Settings */
|
|
@@ -409,6 +409,34 @@ class Color
|
|
|
409
409
|
return this;
|
|
410
410
|
}
|
|
411
411
|
|
|
412
|
+
/** Returns this color expressed in hsla format
|
|
413
|
+
* @return {Array} */
|
|
414
|
+
getHSLA()
|
|
415
|
+
{
|
|
416
|
+
const r = this.r;
|
|
417
|
+
const g = this.g;
|
|
418
|
+
const b = this.b;
|
|
419
|
+
const a = this.a;
|
|
420
|
+
const max = Math.max(r, g, b);
|
|
421
|
+
const min = Math.min(r, g, b);
|
|
422
|
+
const l = (max + min) / 2;
|
|
423
|
+
|
|
424
|
+
let h = 0, s = 0;
|
|
425
|
+
if (max != min)
|
|
426
|
+
{
|
|
427
|
+
let d = max - min;
|
|
428
|
+
s = l > .5 ? d / (2 - max - min) : d / (max + min);
|
|
429
|
+
if (r == max)
|
|
430
|
+
h = (g - b) / d + (g < b ? 6 : 0);
|
|
431
|
+
else if (g == max)
|
|
432
|
+
h = (b - r) / d + 2;
|
|
433
|
+
else if (b == max)
|
|
434
|
+
h = (r - g) / d + 4;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
return [h / 6, s, l, a];
|
|
438
|
+
}
|
|
439
|
+
|
|
412
440
|
/** Returns a new color that has each component randomly adjusted
|
|
413
441
|
* @param {Number} [amount=.05]
|
|
414
442
|
* @param {Number} [alphaAmount=0]
|
|
@@ -440,42 +468,6 @@ class Color
|
|
|
440
468
|
return (this.r*255|0) + (this.g*255<<8) + (this.b*255<<16) + (this.a*255<<24);
|
|
441
469
|
}
|
|
442
470
|
|
|
443
|
-
/** Returns this color expressed as a hex code
|
|
444
|
-
* @return {String} */
|
|
445
|
-
hex()
|
|
446
|
-
{
|
|
447
|
-
const toHex = (c)=> ((c=c*255|0)<16 ? '0' : '') + c.toString(16);
|
|
448
|
-
return '#' + toHex(this.r) + toHex(this.g) + toHex(this.b);
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
/** Returns this color expressed in hsla format
|
|
452
|
-
* @return {Array} */
|
|
453
|
-
getHSLA()
|
|
454
|
-
{
|
|
455
|
-
const r = this.r;
|
|
456
|
-
const g = this.g;
|
|
457
|
-
const b = this.b;
|
|
458
|
-
const a = this.a;
|
|
459
|
-
const max = Math.max(r, g, b);
|
|
460
|
-
const min = Math.min(r, g, b);
|
|
461
|
-
const l = (max + min) / 2;
|
|
462
|
-
|
|
463
|
-
let h = 0, s = 0;
|
|
464
|
-
if (max != min)
|
|
465
|
-
{
|
|
466
|
-
let d = max - min;
|
|
467
|
-
s = l > .5 ? d / (2 - max - min) : d / (max + min);
|
|
468
|
-
if (r == max)
|
|
469
|
-
h = (g - b) / d + (g < b ? 6 : 0);
|
|
470
|
-
else if (g == max)
|
|
471
|
-
h = (b - r) / d + 2;
|
|
472
|
-
else if (b == max)
|
|
473
|
-
h = (r - g) / d + 4;
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
return [h / 6, s, l, a];
|
|
477
|
-
}
|
|
478
|
-
|
|
479
471
|
/** Set this color from a hex code
|
|
480
472
|
* @param {String} hex - html hex code
|
|
481
473
|
* @return {Color} */
|
|
@@ -489,6 +481,14 @@ class Color
|
|
|
489
481
|
ASSERT(this.r>=0 && this.r<=1 && this.g>=0 && this.g<=1 && this.b>=0 && this.b<=1);
|
|
490
482
|
return this;
|
|
491
483
|
}
|
|
484
|
+
|
|
485
|
+
/** Returns this color expressed as a hex code
|
|
486
|
+
* @return {String} */
|
|
487
|
+
getHex()
|
|
488
|
+
{
|
|
489
|
+
const toHex = (c)=> ((c=c*255|0)<16 ? '0' : '') + c.toString(16);
|
|
490
|
+
return '#' + toHex(this.r) + toHex(this.g) + toHex(this.b);
|
|
491
|
+
}
|
|
492
492
|
}
|
|
493
493
|
|
|
494
494
|
///////////////////////////////////////////////////////////////////////////////
|
package/engine/index.d.ts
CHANGED
|
@@ -629,6 +629,9 @@ declare class Color {
|
|
|
629
629
|
* @param {Number} [alpha=1]
|
|
630
630
|
* @return {Color} */
|
|
631
631
|
setHSLA(h?: number, s?: number, l?: number, a?: number): Color;
|
|
632
|
+
/** Returns this color expressed in hsla format
|
|
633
|
+
* @return {Array} */
|
|
634
|
+
getHSLA(): any[];
|
|
632
635
|
/** Returns a new color that has each component randomly adjusted
|
|
633
636
|
* @param {Number} [amount=.05]
|
|
634
637
|
* @param {Number} [alphaAmount=0]
|
|
@@ -640,16 +643,13 @@ declare class Color {
|
|
|
640
643
|
/** Returns this color expressed as 32 bit integer RGBA value
|
|
641
644
|
* @return {Number} */
|
|
642
645
|
rgbaInt(): number;
|
|
643
|
-
/** Returns this color expressed as a hex code
|
|
644
|
-
* @return {String} */
|
|
645
|
-
hex(): string;
|
|
646
|
-
/** Returns this color expressed in hsla format
|
|
647
|
-
* @return {Array} */
|
|
648
|
-
getHSLA(): any[];
|
|
649
646
|
/** Set this color from a hex code
|
|
650
647
|
* @param {String} hex - html hex code
|
|
651
648
|
* @return {Color} */
|
|
652
649
|
setHex(hex: string): Color;
|
|
650
|
+
/** Returns this color expressed as a hex code
|
|
651
|
+
* @return {String} */
|
|
652
|
+
getHex(): string;
|
|
653
653
|
}
|
|
654
654
|
/**
|
|
655
655
|
* Timer object tracks how long has passed since it was set
|
|
@@ -695,7 +695,8 @@ declare class Timer {
|
|
|
695
695
|
* @default
|
|
696
696
|
* @memberof Settings */
|
|
697
697
|
declare let canvasMaxSize: Vector2;
|
|
698
|
-
/** Fixed size of the canvas, if enabled
|
|
698
|
+
/** Fixed size of the canvas, if enabled canvas size never changes
|
|
699
|
+
* - you may also need to set mainCanvasSize if using screen space coords in startup
|
|
699
700
|
* @type {Vector2}
|
|
700
701
|
* @default
|
|
701
702
|
* @memberof Settings */
|
|
@@ -991,11 +992,13 @@ declare let overlayContext: CanvasRenderingContext2D;
|
|
|
991
992
|
* @memberof Draw */
|
|
992
993
|
declare let mainCanvasSize: Vector2;
|
|
993
994
|
/** Convert from screen to world space coordinates
|
|
995
|
+
* - if calling outside of render, you may need to manually set mainCanvasSize
|
|
994
996
|
* @param {Vector2} screenPos
|
|
995
997
|
* @return {Vector2}
|
|
996
998
|
* @memberof Draw */
|
|
997
999
|
declare function screenToWorld(screenPos: Vector2): Vector2;
|
|
998
1000
|
/** Convert from world to screen space coordinates
|
|
1001
|
+
* - if calling outside of render, you may need to manually set mainCanvasSize
|
|
999
1002
|
* @param {Vector2} worldPos
|
|
1000
1003
|
* @return {Vector2}
|
|
1001
1004
|
* @memberof Draw */
|
|
@@ -1627,7 +1630,7 @@ declare const gl_VERTEX_BYTE_STRIDE: number;
|
|
|
1627
1630
|
/** Name of engine */
|
|
1628
1631
|
declare const engineName: "LittleJS";
|
|
1629
1632
|
/** Version of engine */
|
|
1630
|
-
declare const engineVersion: "1.3.
|
|
1633
|
+
declare const engineVersion: "1.3.8";
|
|
1631
1634
|
/** Frames per second to update objects
|
|
1632
1635
|
* @default */
|
|
1633
1636
|
declare const frameRate: 60;
|
|
@@ -5,6 +5,6 @@
|
|
|
5
5
|
<link rel=icon type=image/png href=../../favicon.png>
|
|
6
6
|
</head><body>
|
|
7
7
|
|
|
8
|
-
<script src=../../engine/engine.all.js?
|
|
9
|
-
<script src=game.js?
|
|
10
|
-
<script src=gameObjects.js?
|
|
8
|
+
<script src=../../engine/engine.all.js?111></script>
|
|
9
|
+
<script src=game.js?111></script>
|
|
10
|
+
<script src=gameObjects.js?111></script>
|
|
@@ -15,18 +15,18 @@ button
|
|
|
15
15
|
</style>
|
|
16
16
|
</head><body>
|
|
17
17
|
|
|
18
|
-
<script src=../../engine/engineDebug.js?
|
|
19
|
-
<script src=../../engine/engineUtilities.js?
|
|
20
|
-
<script src=../../engine/engineSettings.js?
|
|
21
|
-
<script src=../../engine/engineObject.js?
|
|
22
|
-
<script src=../../engine/engineDraw.js?
|
|
23
|
-
<script src=../../engine/engineInput.js?
|
|
24
|
-
<script src=../../engine/engineAudio.js?
|
|
25
|
-
<script src=../../engine/engineTileLayer.js?
|
|
26
|
-
<script src=../../engine/engineParticles.js?
|
|
27
|
-
<script src=../../engine/engineMedals.js?
|
|
28
|
-
<script src=../../engine/engineWebGL.js?
|
|
29
|
-
<script src=../../engine/engine.js?
|
|
18
|
+
<script src=../../engine/engineDebug.js?111></script>
|
|
19
|
+
<script src=../../engine/engineUtilities.js?111></script>
|
|
20
|
+
<script src=../../engine/engineSettings.js?111></script>
|
|
21
|
+
<script src=../../engine/engineObject.js?111></script>
|
|
22
|
+
<script src=../../engine/engineDraw.js?111></script>
|
|
23
|
+
<script src=../../engine/engineInput.js?111></script>
|
|
24
|
+
<script src=../../engine/engineAudio.js?111></script>
|
|
25
|
+
<script src=../../engine/engineTileLayer.js?111></script>
|
|
26
|
+
<script src=../../engine/engineParticles.js?111></script>
|
|
27
|
+
<script src=../../engine/engineMedals.js?111></script>
|
|
28
|
+
<script src=../../engine/engineWebGL.js?111></script>
|
|
29
|
+
<script src=../../engine/engine.js?111></script>
|
|
30
30
|
<script>
|
|
31
31
|
|
|
32
32
|
'use strict';
|
|
@@ -103,7 +103,7 @@ function gameInit()
|
|
|
103
103
|
if (type == 'color')
|
|
104
104
|
{
|
|
105
105
|
const color = particleSystem[name];
|
|
106
|
-
defaultValue = color.
|
|
106
|
+
defaultValue = color.getHex();
|
|
107
107
|
}
|
|
108
108
|
else if (type == 'alpha')
|
|
109
109
|
{
|
|
@@ -277,7 +277,7 @@ function updateSetting(setting, save=1)
|
|
|
277
277
|
if (type == 'color')
|
|
278
278
|
{
|
|
279
279
|
const color = particleSystem[name];
|
|
280
|
-
input.value = color.
|
|
280
|
+
input.value = color.getHex();
|
|
281
281
|
}
|
|
282
282
|
else if (type == 'alpha')
|
|
283
283
|
{
|
|
@@ -5,9 +5,9 @@
|
|
|
5
5
|
<link rel=icon type=image/png href=../../favicon.png>
|
|
6
6
|
</head><body>
|
|
7
7
|
|
|
8
|
-
<script src=../../engine/engine.all.js?
|
|
9
|
-
<script src=gameObjects.js?
|
|
10
|
-
<script src=gamePlayer.js?
|
|
11
|
-
<script src=gameEffects.js?
|
|
12
|
-
<script src=gameLevel.js?
|
|
8
|
+
<script src=../../engine/engine.all.js?111></script>
|
|
9
|
+
<script src=gameObjects.js?111></script>
|
|
10
|
+
<script src=gamePlayer.js?111></script>
|
|
11
|
+
<script src=gameEffects.js?111></script>
|
|
12
|
+
<script src=gameLevel.js?111></script>
|
|
13
13
|
<script src=game.js?103></script>
|
package/index.html
CHANGED
|
@@ -6,21 +6,21 @@
|
|
|
6
6
|
</head><body>
|
|
7
7
|
|
|
8
8
|
<!-- LittleJS engine scripts -->
|
|
9
|
-
<script src=engine/engineDebug.js?
|
|
10
|
-
<script src=engine/engineUtilities.js?
|
|
11
|
-
<script src=engine/engineSettings.js?
|
|
12
|
-
<script src=engine/engineObject.js?
|
|
13
|
-
<script src=engine/engineDraw.js?
|
|
14
|
-
<script src=engine/engineInput.js?
|
|
15
|
-
<script src=engine/engineAudio.js?
|
|
16
|
-
<script src=engine/engineTileLayer.js?
|
|
17
|
-
<script src=engine/engineParticles.js?
|
|
18
|
-
<script src=engine/engineMedals.js?
|
|
19
|
-
<script src=engine/engineWebGL.js?
|
|
20
|
-
<script src=engine/engine.js?
|
|
9
|
+
<script src=engine/engineDebug.js?111></script>
|
|
10
|
+
<script src=engine/engineUtilities.js?111></script>
|
|
11
|
+
<script src=engine/engineSettings.js?111></script>
|
|
12
|
+
<script src=engine/engineObject.js?111></script>
|
|
13
|
+
<script src=engine/engineDraw.js?111></script>
|
|
14
|
+
<script src=engine/engineInput.js?111></script>
|
|
15
|
+
<script src=engine/engineAudio.js?111></script>
|
|
16
|
+
<script src=engine/engineTileLayer.js?111></script>
|
|
17
|
+
<script src=engine/engineParticles.js?111></script>
|
|
18
|
+
<script src=engine/engineMedals.js?111></script>
|
|
19
|
+
<script src=engine/engineWebGL.js?111></script>
|
|
20
|
+
<script src=engine/engine.js?111></script>
|
|
21
21
|
|
|
22
22
|
<!-- You can also include all engine scripts like this... -->
|
|
23
23
|
<!-- <script src=engine/engine.all.js></script> -->
|
|
24
24
|
|
|
25
25
|
<!-- Add your game scripts here -->
|
|
26
|
-
<script src=game.js?
|
|
26
|
+
<script src=game.js?111></script>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "littlejsengine",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.8",
|
|
4
4
|
"description": "LittleJS - Tiny and Fast HTML5 Game Engine",
|
|
5
5
|
"main": "engine/engine.all.js",
|
|
6
6
|
"repository": {
|
|
@@ -21,5 +21,12 @@
|
|
|
21
21
|
"bugs": {
|
|
22
22
|
"url": "https://github.com/KilledByAPixel/LittleJS/issues"
|
|
23
23
|
},
|
|
24
|
-
"homepage": "https://github.com/KilledByAPixel/LittleJS"
|
|
24
|
+
"homepage": "https://github.com/KilledByAPixel/LittleJS",
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"ect-bin": "^1.4.1",
|
|
27
|
+
"google-closure-compiler": "^20220803.0.0",
|
|
28
|
+
"roadroller": "^2.1.0",
|
|
29
|
+
"typescript": "^4.7.4",
|
|
30
|
+
"uglify-js": "^3.16.3"
|
|
31
|
+
}
|
|
25
32
|
}
|