littlejsengine 1.4.91 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/build.bat +4 -6
- package/engine/engine.all.js +27 -12
- package/engine/engine.all.min.js +1 -1
- package/engine/engine.all.module.js +27 -12
- package/engine/engine.all.module.min.js +1 -1
- package/engine/engine.all.release.js +23 -8
- package/engine/engine.js +1 -1
- package/engine/engineAudio.js +14 -1
- package/engine/engineBuild.bat +8 -8
- package/engine/engineDebug.js +4 -4
- package/engine/engineDraw.js +1 -1
- package/engine/engineInput.js +1 -1
- package/engine/engineObject.js +5 -3
- package/engine/engineParticles.js +1 -1
- package/engine/index.d.ts +1588 -1673
- package/examples/breakout/index.html +1 -1
- package/examples/empty/index.html +1 -4
- package/examples/module/game.js +1 -4
- package/examples/module/index.html +1 -1
- package/examples/particles/index.html +1 -1
- package/examples/platformer/index.html +1 -1
- package/examples/puzzle/index.html +1 -1
- package/examples/stress/index.html +1 -1
- package/examples/typescript/build.bat +14 -0
- package/examples/typescript/game.js +89 -0
- package/examples/typescript/game.ts +117 -0
- package/examples/typescript/index.html +9 -0
- package/examples/typescript/tiles.png +0 -0
- package/examples/typescript/tsconfig.json +8 -0
- package/index.html +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -37,6 +37,7 @@ LittleJS is a super lightweight JavaScript game engine with a fast hybrid render
|
|
|
37
37
|
- Easily build a Windows executable with [Electron](https://www.electronjs.org/) for distribution on platforms like Steam
|
|
38
38
|
- Open Source with the [MIT license](https://github.com/KilledByAPixel/LittleJS/blob/main/LICENSE) so it can be used for anything you want
|
|
39
39
|
- Can be imported as a JavaScript module and includes [an example module project](https://killedbyapixel.github.io/LittleJS/examples/module)
|
|
40
|
+
- Compatible with TypeScript and includes [an example TS project](https://killedbyapixel.github.io/LittleJS/examples/typescript)
|
|
40
41
|
|
|
41
42
|
## Builds
|
|
42
43
|
|
package/build.bat
CHANGED
|
@@ -39,6 +39,7 @@ if %ERRORLEVEL% NEQ 0 (
|
|
|
39
39
|
pause
|
|
40
40
|
exit /b %ERRORLEVEL%
|
|
41
41
|
)
|
|
42
|
+
del temp_%BUILD_FILENAME%
|
|
42
43
|
|
|
43
44
|
rem more minification with uglify or terser (they both do about the same)
|
|
44
45
|
call npx uglifyjs -o %BUILD_FILENAME% --compress --mangle -- %BUILD_FILENAME%
|
|
@@ -49,8 +50,7 @@ if %ERRORLEVEL% NEQ 0 (
|
|
|
49
50
|
)
|
|
50
51
|
|
|
51
52
|
rem roadroller compresses the code better then zip
|
|
52
|
-
|
|
53
|
-
call npx roadroller roadroller_%BUILD_FILENAME% -o roadroller_%BUILD_FILENAME%
|
|
53
|
+
call npx roadroller %BUILD_FILENAME% -o %BUILD_FILENAME%
|
|
54
54
|
if %ERRORLEVEL% NEQ 0 (
|
|
55
55
|
pause
|
|
56
56
|
exit /b %ERRORLEVEL%
|
|
@@ -58,14 +58,12 @@ if %ERRORLEVEL% NEQ 0 (
|
|
|
58
58
|
|
|
59
59
|
rem build the html, you can add html header and footers here
|
|
60
60
|
rem type ..\header.html >> index.html
|
|
61
|
-
echo ^<body^>^<
|
|
62
|
-
type
|
|
61
|
+
echo ^<body^>^<script^> >> index.html
|
|
62
|
+
type %BUILD_FILENAME% >> index.html
|
|
63
63
|
echo ^</script^> >> index.html
|
|
64
64
|
|
|
65
65
|
rem delete intermediate files
|
|
66
66
|
del %BUILD_FILENAME%
|
|
67
|
-
del temp_%BUILD_FILENAME%
|
|
68
|
-
del roadroller_%BUILD_FILENAME%
|
|
69
67
|
|
|
70
68
|
rem zip the result, ect is recommended
|
|
71
69
|
call ..\node_modules\ect-bin\vendor\win32\ect.exe -9 -strip -zip ..\%NAME%.zip index.html tiles.png
|
package/engine/engine.all.js
CHANGED
|
@@ -59,9 +59,9 @@ const ASSERT = enableAsserts ? (...assert)=> console.assert(...assert) : ()=>{};
|
|
|
59
59
|
* @param {String} [color='#fff']
|
|
60
60
|
* @param {Number} [time=0]
|
|
61
61
|
* @param {Number} [angle=0]
|
|
62
|
-
* @param {Boolean} [fill=
|
|
62
|
+
* @param {Boolean} [fill=false]
|
|
63
63
|
* @memberof Debug */
|
|
64
|
-
const debugRect = (pos, size=vec2(), color='#fff', time=0, angle=0, fill=
|
|
64
|
+
const debugRect = (pos, size=vec2(), color='#fff', time=0, angle=0, fill=false)=>
|
|
65
65
|
{
|
|
66
66
|
ASSERT(typeof color == 'string'); // pass in regular html strings as colors
|
|
67
67
|
debugPrimitives.push({pos, size:vec2(size), color, time:new Timer(time), angle, fill});
|
|
@@ -72,9 +72,9 @@ const debugRect = (pos, size=vec2(), color='#fff', time=0, angle=0, fill=0)=>
|
|
|
72
72
|
* @param {Number} [radius=0]
|
|
73
73
|
* @param {String} [color='#fff']
|
|
74
74
|
* @param {Number} [time=0]
|
|
75
|
-
* @param {Boolean} [fill=
|
|
75
|
+
* @param {Boolean} [fill=false]
|
|
76
76
|
* @memberof Debug */
|
|
77
|
-
const debugCircle = (pos, radius=0, color='#fff', time=0, fill=
|
|
77
|
+
const debugCircle = (pos, radius=0, color='#fff', time=0, fill=false)=>
|
|
78
78
|
{
|
|
79
79
|
ASSERT(typeof color == 'string'); // pass in regular html strings as colors
|
|
80
80
|
debugPrimitives.push({pos, size:radius, color, time:new Timer(time), angle:0, fill});
|
|
@@ -1528,9 +1528,9 @@ class EngineObject
|
|
|
1528
1528
|
}
|
|
1529
1529
|
|
|
1530
1530
|
/** Set how this object collides
|
|
1531
|
-
* @param {
|
|
1532
|
-
* @param {
|
|
1533
|
-
* @param {
|
|
1531
|
+
* @param {Boolean} [collideSolidObjects=1] - Does it collide with solid objects
|
|
1532
|
+
* @param {Boolean} [isSolid=1] - Does it collide with and block other objects (expensive in large numbers)
|
|
1533
|
+
* @param {Boolean} [collideTiles=1] - Does it collide with the tile collision */
|
|
1534
1534
|
setCollision(collideSolidObjects=1, isSolid=1, collideTiles=1)
|
|
1535
1535
|
{
|
|
1536
1536
|
ASSERT(collideSolidObjects || !isSolid); // solid objects must be set to collide
|
|
@@ -1540,6 +1540,8 @@ class EngineObject
|
|
|
1540
1540
|
this.collideTiles = collideTiles;
|
|
1541
1541
|
}
|
|
1542
1542
|
|
|
1543
|
+
/** Returns string containg info about this object for debugging
|
|
1544
|
+
* @return {String} */
|
|
1543
1545
|
toString()
|
|
1544
1546
|
{
|
|
1545
1547
|
if (debug)
|
|
@@ -1609,7 +1611,7 @@ let overlayContext;
|
|
|
1609
1611
|
let mainCanvasSize = vec2();
|
|
1610
1612
|
|
|
1611
1613
|
/** Tile sheet for batch rendering system
|
|
1612
|
-
* @type {
|
|
1614
|
+
* @type {CanvasImageSource}
|
|
1613
1615
|
* @memberof Draw */
|
|
1614
1616
|
const tileImage = new Image;
|
|
1615
1617
|
|
|
@@ -2190,7 +2192,7 @@ const vibrateStop = ()=> vibrate(0);
|
|
|
2190
2192
|
// Touch input
|
|
2191
2193
|
|
|
2192
2194
|
/** True if a touch device has been detected
|
|
2193
|
-
* @const {
|
|
2195
|
+
* @const {Boolean}
|
|
2194
2196
|
* @memberof Input */
|
|
2195
2197
|
const isTouchDevice = window.ontouchstart !== undefined;
|
|
2196
2198
|
|
|
@@ -2520,8 +2522,21 @@ class Music
|
|
|
2520
2522
|
{
|
|
2521
2523
|
if (!soundEnable) return;
|
|
2522
2524
|
|
|
2523
|
-
|
|
2525
|
+
this.source = playSamples(this.cachedSamples, volume, 1, 0, loop);
|
|
2524
2526
|
}
|
|
2527
|
+
|
|
2528
|
+
/** Stop the music */
|
|
2529
|
+
stop()
|
|
2530
|
+
{
|
|
2531
|
+
if (this.source)
|
|
2532
|
+
this.source.stop();
|
|
2533
|
+
this.source = 0;
|
|
2534
|
+
}
|
|
2535
|
+
|
|
2536
|
+
/** Check if music is playing
|
|
2537
|
+
* @return {Boolean}
|
|
2538
|
+
*/
|
|
2539
|
+
isPlaying() { return this.source; }
|
|
2525
2540
|
}
|
|
2526
2541
|
|
|
2527
2542
|
/** Play an mp3 or wav audio from a local file or url
|
|
@@ -3212,7 +3227,7 @@ class ParticleEmitter extends EngineObject
|
|
|
3212
3227
|
* @param {Number} [emitRate=100] - How many particles per second to spawn, does not emit if 0
|
|
3213
3228
|
* @param {Number} [emitConeAngle=PI] - Local angle to apply velocity to particles from emitter
|
|
3214
3229
|
* @param {Number} [tileIndex=-1] - Index into tile sheet, if <0 no texture is applied
|
|
3215
|
-
* @param {
|
|
3230
|
+
* @param {Vector2} [tileSize=tileSizeDefault] - Tile size for particles
|
|
3216
3231
|
* @param {Color} [colorStartA=new Color(1,1,1)] - Color at start of life 1, randomized between start colors
|
|
3217
3232
|
* @param {Color} [colorStartB=new Color(1,1,1)] - Color at start of life 2, randomized between start colors
|
|
3218
3233
|
* @param {Color} [colorEndA=new Color(1,1,1,0)] - Color at end of life 1, randomized between end colors
|
|
@@ -4190,7 +4205,7 @@ gl_VERTEX_BUFFER_SIZE = gl_MAX_BATCH * gl_VERTICES_PER_QUAD * gl_VERTEX_BYTE_STR
|
|
|
4190
4205
|
const engineName = 'LittleJS';
|
|
4191
4206
|
|
|
4192
4207
|
/** Version of engine */
|
|
4193
|
-
const engineVersion = '1.
|
|
4208
|
+
const engineVersion = '1.5.0';
|
|
4194
4209
|
|
|
4195
4210
|
/** Frames per second to update objects
|
|
4196
4211
|
* @default */
|
package/engine/engine.all.min.js
CHANGED
|
@@ -1 +1 @@
|
|
|
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*randInt(2)-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 setRandSeed=a=>randSeed=a,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),isVector2=a=>!isNaN(a.x)&&!isNaN(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(isVector2(a));return new Vector2(this.x+a.x,this.y+a.y)}subtract(a){ASSERT(isVector2(a));return new Vector2(this.x-a.x,this.y-a.y)}multiply(a){ASSERT(isVector2(a));return new Vector2(this.x*a.x,this.y*a.y)}divide(a){ASSERT(isVector2(a));return new Vector2(this.x/a.x,this.y/a.y)}scale(a){ASSERT(!isVector2(a));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(isVector2(a));return this.x*a.x+this.y*a.y}cross(a){ASSERT(isVector2(a));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(isVector2(a));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){if(debug)return`(${(0>this.x?"":" ")+this.x.toFixed(a)},${(0>this.y?"":" ")+this.y.toFixed(a)} )`}}const colorRGBA=(a,b,c,d)=>new Color(a,b,c,d),colorHSLA=(a,b,c,d)=>(new Color).setHSLA(a,b,c,d);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,g,k)=>(k=(k%1+1)%1)<1/6?f+6*(g-f)*k:.5>k?g:k<2/3?f+(g-f)*(2/3-k)*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=clamp(this.r),b=clamp(this.g),c=clamp(this.b),d=clamp(this.a),e=Math.max(a,b,c),f=Math.min(a,b,c),g=(e+f)/2;let k=0,h=0;if(e!=f){let l=e-f;h=.5<g?l/(2-e-f):l/(e+f);a==e?k=(b-c)/l+(b<c?6:0):b==e?k=(c-a)/l+2:c==e&&(k=(a-b)/l+4)}return[k/6,h,g,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(a=1){const b=c=>(16>(c=255*c|0)?"0":"")+c.toString(16);return"#"+b(this.r)+b(this.g)+b(this.b)+(a?b(this.a):"")}setHex(a){this.r=clamp(parseInt(a.slice(1,3),16)/255);this.g=clamp(parseInt(a.slice(3,5),16)/255);this.b=clamp(parseInt(a.slice(5,7),16)/255);this.a=7<a.length?clamp(parseInt(a.slice(7,9),16)/255):1;return this}rgbaInt(){const a=255*clamp(this.r)|0,b=(255*clamp(this.g)|0)<<8,c=(255*clamp(this.b)|0)<<16,d=(255*clamp(this.a)|0)<<24;return a+b+c+d}}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")}valueOf(){return this.get()}}"use strict";let cameraPos=vec2(),cameraScale=32,canvasMaxSize=vec2(1920,1200),canvasFixedSize=vec2(),cavasPixelated=1,fontDefault="arial",glEnable=1,glOverlay=1,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,gamepadsEnable=1,gamepadDirectionEmulateStick=1,inputWASDEmulateDirection=1,touchGamepadEnable=0,touchGamepadAnalog=1,touchGamepadSize=99,touchGamepadAlpha=.3,vibrateEnable=1,soundEnable=1,soundVolume=.5,soundDefaultRange=40,soundDefaultTaper=.7,medalDisplayTime=5,medalDisplaySlideTime=.5,medalDisplaySize=vec2(640,80),medalDisplayIconSize=50,medalsPreventUnlock;"use strict";const engineName="LittleJS",engineVersion="1.4.9",frameRate=60,timeDelta=1/frameRate;let engineObjects=[],engineObjectsCollide=[],frame=0,time=0,timeReal=0,paused=0;function setPaused(a){paused=a}function engineInit(a,b,c,d,e,f){function g(m=0){var n=m-k;k=m;if(debug||showWatermark)l=lerp(.05,l,1e3/(n||1));m=debug&&keyIsDown(107);const p=debug&&keyIsDown(109);debug&&(n*=m?5:p?.2:1);timeReal+=n/1e3;h+=!paused*n;m||(h=min(h,50));canvasFixedSize.x?(overlayCanvas.width=mainCanvas.width=canvasFixedSize.x,overlayCanvas.height=mainCanvas.height=canvasFixedSize.y,n=innerWidth/innerHeight,m=mainCanvas.width/mainCanvas.height,(glCanvas||mainCanvas).style.width=mainCanvas.style.width=overlayCanvas.style.width=n<m?"100%":"",(glCanvas||mainCanvas).style.height=mainCanvas.style.height=overlayCanvas.style.height=n<m?"":"100%"):(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{n=0;0>h&&-9<h&&(n=h,h=0);for(;0<=h;h-=1e3/frameRate)inputUpdate(),b(),engineObjectsUpdate(),debugUpdate(),c(),inputUpdatePost();h+=n}enginePreRender();d();engineObjects.sort((r,w)=>r.renderOrder-w.renderOrder);for(var q of engineObjects)q.destroyed||q.render();e();glRenderPostProcess();medalsRender();touchGamepadRender();debugRender();glEnable&&glCopyToContext(mainContext);showWatermark&&(overlayContext.textAlign="right",overlayContext.textBaseline="top",overlayContext.font="1em monospace",overlayContext.fillStyle="#000",q=engineName+" v"+engineVersion+" / "+drawCount+" / "+engineObjects.length+" / "+l.toFixed(1)+(glEnable?" GL":" 2D"),overlayContext.fillText(q,mainCanvas.width-3,3),overlayContext.fillStyle="#fff",overlayContext.fillText(q,mainCanvas.width-2,2),drawCount=0);requestAnimationFrame(g)}tileImage.onerror=tileImage.onload=()=>{tileImageFixBleed=vec2(tileFixBleedScale).divide(tileImageSize=vec2(tileImage.width,tileImage.height));debug&&(tileImage.onload=()=>ASSERT(1));document.body.style="margin:0;overflow:hidden;background:#000;touch-action:none;user-select:none;-webkit-user-select:none";document.body.appendChild(mainCanvas=document.createElement("canvas"));mainContext=mainCanvas.getContext("2d");debugInit();glEnable&&glInit();document.body.appendChild(overlayCanvas=document.createElement("canvas"));overlayContext=overlayCanvas.getContext("2d");(glCanvas||mainCanvas).style=mainCanvas.style=overlayCanvas.style="position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)";a();touchGamepadCreate();g()};let k=0,h,l;f?tileImage.src=f:tileImage.onload()}function enginePreRender(){mainCanvasSize=vec2(mainCanvas.width,mainCanvas.height);mainContext.imageSmoothingEnabled=!cavasPixelated;glEnable&&glPreRender()}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,g=0){ASSERT(isVector2(a)&&isVector2(b));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=g;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.velocity.y+=gravity*this.gravityScale,this.pos.x+=this.velocity.x*=this.damping,this.pos.y+=this.velocity.y*=this.damping,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,g=2*abs(a.x-d.pos.x)<c.x;if(e|g|!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){g=(this.mass*this.velocity.y+d.mass*d.velocity.y)/(this.mass+d.mass);const k=this.velocity.y*(this.mass-d.mass)/(this.mass+d.mass)+2*d.velocity.y*d.mass/(this.mass+d.mass),h=d.velocity.y*(d.mass-this.mass)/(this.mass+d.mass)+2*this.velocity.y*this.mass/(this.mass+d.mass),l=max(this.elasticity,d.elasticity);this.velocity.y=lerp(l,g,k);d.velocity.y=lerp(l,g,h)}!e&f&&(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),g=max(this.elasticity,d.elasticity),this.velocity.x=lerp(g,c,e),d.velocity.x=lerp(g,c,f)):this.velocity.x*=-this.elasticity);debugOverlay&&debugPhysics&&debugAABB(this.pos,this.size,d.pos,d.size,"#f0f")}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),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=1,b=1,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";let mainCanvas,mainContext,overlayCanvas,overlayContext,mainCanvasSize=vec2();const tileImage=new Image;let tileImageSize,tileImageFixBleed,drawCount;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,g,k=new Color(0,0,0,0),h=glEnable){showWatermark&&++drawCount;if(glEnable&&h)if(0>c||!tileImage.width)glDraw(a.x,a.y,b.x,b.y,f,0,0,0,0,0,e.rgbaInt());else{var l=tileImageSize.x/d.x|0;h=d.x/tileImageSize.x;const m=d.y/tileImageSize.y,n=c%l*h;l=(c/l|0)*m;glDraw(a.x,a.y,g?-b.x:b.x,b.y,f,n+tileImageFixBleed.x,l+tileImageFixBleed.y,n-tileImageFixBleed.x+h,l-tileImageFixBleed.y+m,e.rgbaInt(),k.rgbaInt())}else drawCanvas2D(a,b,f,g,m=>{if(0>c)m.fillStyle=e,m.fillRect(-.5,-.5,1,1);else{var n=tileImageSize.x/d.x|0;const p=c%n*d.x+tileFixBleedScale;n=(c/n|0)*d.y+tileFixBleedScale;const q=d.x-2*tileFixBleedScale,r=d.y-2*tileFixBleedScale;m.globalAlpha=e.a;m.drawImage(tileImage,p,n,q,r,-.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,g,k,h){drawTile(screenToWorld(a),b.scale(1/cameraScale),c,d,e,f,g,k,h)}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(),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),g="center",k=fontDefault,h=overlayContext){h.fillStyle=d;h.lineWidth=e;h.strokeStyle=f;h.textAlign=g;h.font=c+"px "+k;h.textBaseline="middle";h.lineJoin="round";b=b.copy();(a+"").split("\n").forEach(l=>{e&&h.strokeText(l,b.x,b.y);h.fillText(l,b.x,b.y);b.y+=c})}function drawText(a,b,c=1,d,e,f,g,k){drawTextScreen(a,worldToScreen(b),c*cameraScale,d,e*cameraScale,f,g,k,mainContext)}let engineFontImage;class FontImage{constructor(a,b=vec2(8),c=vec2(0,1),d=0,e=overlayContext){engineFontImage||((engineFontImage=new Image).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,g=f.add(this.paddingSize).scale(c),k=this.image.width/this.tileSize.x|0;(a+"").split("\n").forEach((h,l)=>{const m=d?h.length*f.x*c/2|0:0;for(let q=h.length;q--;){var n=h[q].charCodeAt();if(32>n||127<n)n=127;var p=this.startTileIndex+n-32;n=p%k;p=p/k|0;const r=b.add(vec2(q,l).multiply(g));e.drawImage(this.image,n*f.x,p*f.y,f.x,f.y,r.x-m,r.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.body.requestFullscreen&&document.body.requestFullscreen()}"use strict";const keyIsDown=(a,b=0)=>inputData[b]&&inputData[b][a]&1,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(){isTouchDevice||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&&navigator.getGamepads&&(document.hasFocus()||debug))for(a=navigator.getGamepads(),b=a.length;b--;){var d=a[b];const g=inputData[b+1]||(inputData[b+1]=[]);c=stickData[b]||(stickData[b]=[]);if(d){var e=k=>.3<k?percent(k,.3,.8):-.3>k?-percent(-k,.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],g[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&&navigator.vibrate&&navigator.vibrate(a),vibrateStop=()=>vibrate(0),isTouchDevice=void 0!==window.ontouchstart;if(isTouchDevice){let a,b=onmousedown,c=onmouseup;onmousedown=onmouseup=()=>0;ontouchstart=d=>{zzfx(0);ontouchstart=ontouchmove=ontouchend=e=>{e.button=0;const f=e.touches.length;f?(e.x=e.touches[0].clientX,e.y=e.touches[0].clientY,a?onmousemove(e):b(e)):a&&c(e);a=f;return!0};return ontouchstart(d)}}let touchGamepadTimer=new Timer,touchGamepadButtons,touchGamepadStick;function touchGamepadCreate(){touchGamepadEnable&&isTouchDevice&&(touchGamepadButtons=[],touchGamepadStick=vec2(),ontouchstart=a=>{zzfx(0);ontouchstart=ontouchmove=ontouchend=b=>{touchGamepadStick=vec2();touchGamepadButtons=[];if(b.touches.length&&(isUsingGamepad=1,touchGamepadTimer.set(),paused)){touchGamepadButtons[9]=1;return}const c=vec2(touchGamepadSize,mainCanvasSize.y-touchGamepadSize),d=mainCanvasSize.subtract(vec2(touchGamepadSize,touchGamepadSize)),e=mainCanvasSize.scale(.5);for(const f of b.touches)b=mouseToScreen(vec2(f.clientX,f.clientY)),b.distance(c)<touchGamepadSize?touchGamepadAnalog?touchGamepadStick=b.subtract(c).scale(2/touchGamepadSize).clampLength():(b=b.subtract(c).angle(),touchGamepadStick.setAngle((4*b/PI+8.5|0)*PI/4)):b.distance(d)<touchGamepadSize?(b=b.subtract(d).direction(),touchGamepadButtons[b]=1):b.distance(e)<touchGamepadSize&&(touchGamepadButtons[9]=1)};return ontouchstart(a)})}function touchGamepadRender(){if(touchGamepadEnable&&touchGamepadTimer.isSet()){var a=percent(touchGamepadTimer,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;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){if(soundEnable)return this.play(b,c,2**(a/12),0)}}class Music{constructor(a){soundEnable&&(this.cachedSamples=zzfxM(...a))}play(a,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||=new AudioContext,audioContext.resume(),"running"==audioContext.state)){var f=audioContext.createBuffer(a.length,a[0].length,zzfxR),g=audioContext.createBufferSource();a.forEach((k,h)=>f.getChannelData(h).set(k));g.buffer=f;g.playbackRate.value=c;g.loop=e;a=audioContext.createGain();a.gain.value=soundVolume*b;a.connect(audioContext.destination);g.connect(new StereoPannerNode(audioContext,{pan:clamp(d,-1,1)})).connect(a);g.start();return g}}const zzfx=(...a)=>playSamples([zzfxG(...a)]),zzfxR=44100;function zzfxG(a=1,b=.05,c=220,d=0,e=0,f=.1,g=0,k=1,h=0,l=0,m=0,n=0,p=0,q=0,r=0,w=0,u=0,B=1,z=0,C=0){let v=2*PI,F=h*=500*v/zzfxR/zzfxR,A=[];b=c*=(1+b*rand(-1,1))*v/zzfxR;let x=0,D=0,t=0,y=1,G=0,I=0,E=0,J,H;d=d*zzfxR+9;z*=zzfxR;e*=zzfxR;f*=zzfxR;u*=zzfxR;l*=500*v/zzfxR**3;r*=v/zzfxR;m*=v/zzfxR;n*=zzfxR;p=p*zzfxR|0;for(H=d+z+e+f+u|0;t<H;A[t++]=E)++I%(100*w|0)||(E=g?1<g?2<g?3<g?Math.sin((x%v)**3):max(min(Math.tan(x),1),-1):1-(2*x/v%2+2)%2:1-4*abs(Math.round(x/v)-x/v):Math.sin(x),E=(p?1-C+C*Math.sin(v*t/p):1)*sign(E)*abs(E)**k*a*soundVolume*(t<d?t/d:t<d+z?1-(t-d)/z*(1-B):t<d+z+e?B:t<H-u?(H-t-u)/f*B:0),E=u?E/2+(u>t?0:(t<H-u?1:(H-t)/u)*A[t-u|0]/2):E),J=(c+=h+=l)*Math.cos(r*D++),x+=J-J*q*(1-1e9*(Math.sin(t)+1)%2),y&&++y>n&&(c+=m,b+=m,y=0),!p||++G%p||(c=b,h=F,y=y||1);return A}function zzfxM(a,b,c,d=125){let e,f,g,k,h,l,m,n,p,q,r,w,u,B=0,z,C=[],v=[],F=[],A=0,x=0,D=1,t={},y=zzfxR/d*60>>2;for(;D;A++)C=[D=n=w=0],c.forEach((G,I)=>{m=b[G][A]||[0,0,0];D|=!!b[G][A];z=w+(b[G][0].length-2-!n)*y;u=I==c.length-1;e=2;for(g=w;e<m.length+u;n=++e){h=m[e];p=e==m.length+u-1&&u||q!=(m[0]||0)|h|0;for(f=0;f<y&&n;f++>y-99&&p?r+=(1>r)/99:0)l=(1-r)*C[B++]/2||0,v[g]=(v[g]||0)-l*x+l,F[g]=(F[g++]||0)+l*x+l;h&&(r=h%1,x=m[1]||0,h|=0)&&(C=t[[q=m[B=0]||0,h]]=t[[q,h]]||(k=[...a[q]],k[2]*=2**((h-12)/12),0<h?zzfxG(...k):[]))}w=z});return[v,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 g=tileCollision[e*tileCollisionSize.x+b];if(g&&(!c||c.collideWithTile(g,new Vector2(b,e))))return 1}}function tileCollisionRaycast(a,b,c){var d=(b=b.floor()).subtract(a=a.floor());const e=abs(d.x),f=-abs(d.y),g=sign(d.x);d=sign(d.y);for(let h=a.x,l=a.y,m=e+f;;){var k=getTileCollisionData(vec2(h,l));if(k&&(c?c.collideWithTileRaycast(k,new Vector2(h,l)):0<k))return debugRaycast&&debugLine(a,b,"#f00",.02,1),debugRaycast&&debugPoint(new Vector2(h+.5,l+.5),"#ff0",1),new Vector2(h+.5,l+.5);if(h==b.x&l==b.y)break;k=2*m;k>=f&&(m+=f,h+=g);k<=e&&(m+=e,l+=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){this.savedRenderSettings=[mainCanvas,mainContext,mainCanvasSize,cameraPos,cameraScale];mainCanvas=this.canvas;mainContext=this.context;cameraPos=this.size.scale(.5);cameraScale=this.tileSize.x;a&&(mainCanvas.width=this.size.x*this.tileSize.x,mainCanvas.height=this.size.y*this.tileSize.y);enginePreRender()}redrawEnd(){ASSERT(mainContext==this.context);glEnable&&glCopyToContext(mainContext,1);[mainCanvas,mainContext,mainCanvasSize,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,g){this.drawCanvas2D(a,b,f,g,k=>{if(0>c)k.fillStyle=e,k.fillRect(-.5,-.5,1,1);else{const h=tileImage.width/d.x;k.globalAlpha=e.a;k.drawImage(tileImage,c%h*d.x,(c/h|0)*d.y,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,g=-1,k=tileSizeDefault,h=new Color,l=new Color,m=new Color(1,1,1,0),n=new Color(1,1,1,0),p=.5,q=.1,r=1,w=.1,u=.05,B=1,z=1,C=0,v=PI,F=.1,A=.2,x,D,t=1,y=D?1e9:0,G){super(a,new Vector2,g,k,b,void 0,y);this.emitSize=c;this.emitTime=d;this.emitRate=e;this.emitConeAngle=f;this.colorStartA=h;this.colorStartB=l;this.colorEndA=m;this.colorEndB=n;this.randomColorLinear=t;this.particleTime=p;this.sizeStart=q;this.sizeEnd=r;this.speed=w;this.angleSpeed=u;this.damping=B;this.angleDamping=z;this.gravityScale=C;this.particleConeAngle=v;this.fadeRate=F;this.randomness=A;this.collideTiles=x;this.additive=D;this.localSpace=G;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(this.emitSize/2),b=rand(this.particleConeAngle,-this.particleConeAngle);this.localSpace||(a=this.pos.add(a),b+=this.angle);a=new Particle(a,this.tileIndex,this.tileSize,b);const c=this.randomness;var d=m=>m+m*rand(c,-c);b=d(this.particleTime);const e=d(this.sizeStart),f=d(this.sizeEnd),g=d(this.speed);d=d(this.angleSpeed)*randSign();var k=rand(this.emitConeAngle,-this.emitConeAngle);const h=randColor(this.colorStartA,this.colorStartB,this.randomColorLinear),l=randColor(this.colorEndA,this.colorEndB,this.randomColorLinear);k=this.localSpace?k:this.angle+k;a.colorStart=h;a.colorEndDelta=l.subtract(h);a.velocity=(new Vector2).setAngle(k,g);a.angleVelocity=d;a.lifeTime=b;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=randInt(2);a.localSpaceEmitter=this.localSpace&&this;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);let d=this.pos;var e=this.angle;this.localSpaceEmitter&&(d=this.localSpaceEmitter.pos.add(d.rotate(-this.localSpaceEmitter.angle)),e+=this.localSpaceEmitter.angle);if(this.trailScale){var f=this.velocity;this.localSpaceEmitter&&(f=f.rotate(-this.localSpaceEmitter.angle));e=f.length();f=f.scale(1/e);const g=e*this.trailScale;b.y=max(b.x,g);e=f.angle();drawTile(d.add(f.multiply(vec2(0,-g/2))),b,this.tileIndex,this.tileSize,c,e,this.mirror)}else drawTile(d,b,this.tileIndex,this.tileSize,c,e,this.mirror);this.additive&&setBlendMode();debugParticles&&debugRect(d,b,"#f005",0,e);1==a&&(this.color=c,this.size=b,this.destroyCallback&&this.destroyCallback(this),this.destroyed=1)}}"use strict";const medals=[];let medalsDisplayQueue=[],medalsSaveName,medalsDisplayTimeLast;function medalsInit(a){medalsSaveName=a;debugMedals||medals.forEach(b=>b.unlocked=localStorage[b.storageKey()]|0)}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;e&&((this.image=new Image).src=e)}unlock(){medalsPreventUnlock||this.unlocked||(ASSERT(medalsSaveName),localStorage[this.storageKey()]=this.unlocked=1,medalsDisplayQueue.push(this),newgrounds&&newgrounds.unlockMedal(this.id))}render(a=0){const b=overlayContext;var c=min(medalDisplaySize.x,mainCanvas.width);const d=overlayCanvas.width-c;a*=-medalDisplaySize.y;b.save();b.beginPath();b.fillStyle=new Color(.9,.9,.9);b.strokeStyle=new Color(0,0,0);b.lineWidth=3;b.fill(b.rect(d,a,c,medalDisplaySize.y));b.stroke();b.clip();this.renderIcon(vec2(d+15+medalDisplayIconSize/2,a+medalDisplaySize.y/2));c=vec2(d+medalDisplayIconSize+30,a+28);drawTextScreen(this.name,c,38,new Color(0,0,0),0,0,"left");c.y+=32;drawTextScreen(this.description,c,24,new Color(0,0,0),0,0,"left");b.restore()}renderIcon(a,b=medalDisplayIconSize){this.image?overlayContext.drawImage(this.image,a.x-b/2,a.y-b/2,b,b):drawTextScreen(this.icon,a,.7*b,new Color(0,0,0))}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}}let newgrounds;function newgroundsInit(a,b){newgrounds=new Newgrounds(a,b)}class Newgrounds{constructor(a,b){ASSERT(!newgrounds&&a);this.app_id=a;this.cipher=b;this.host=location?location.hostname:"";b&&(this.cryptoJS=this.CryptoJS());if(this.session_id=new URL(location.href).searchParams.get("ngio_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=new Image,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)}CryptoJS(){return 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,glArrayBuffer,glPositionData,glColorData,glBatchCount,glBatchAdditive,glAdditive;function glInit(){glCanvas=document.createElement("canvas");glContext=glCanvas.getContext("webgl",{antialias:!1});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 vec4 v,d,e;void main(){gl_Position=m*vec4(p,1,1);v=vec4(t,p);d=c;e=a;}","precision highp float;varying vec4 v,d,e;uniform sampler2D s;void main(){gl_FragColor=texture2D(s,v.xy)*d+e;}");const a=new ArrayBuffer(gl_VERTEX_BUFFER_SIZE);glArrayBuffer=glContext.createBuffer();glPositionData=new Float32Array(a);glColorData=new Uint32Array(a);glBatchCount=0}function glSetBlendMode(a){glAdditive=a}function glSetTexture(a=glTileTexture){a!=glActiveTexture&&glFlush();glContext.bindTexture(gl_TEXTURE_2D,glActiveTexture=a)}function glCompileShader(a,b){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){const 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 glCreateTexture(a){const b=glContext.createTexture();glContext.bindTexture(gl_TEXTURE_2D,b);a&&a.width&&glContext.texImage2D(gl_TEXTURE_2D,0,gl_RGBA,gl_RGBA,gl_UNSIGNED_BYTE,a);a=cavasPixelated?gl_NEAREST:gl_LINEAR;glContext.texParameteri(gl_TEXTURE_2D,gl_TEXTURE_MIN_FILTER,a);glContext.texParameteri(gl_TEXTURE_2D,gl_TEXTURE_MAG_FILTER,a);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(){glContext.viewport(0,0,glCanvas.width=mainCanvas.width,glCanvas.height=mainCanvas.height);glContext.clear(gl_COLOR_BUFFER_BIT);glContext.useProgram(glShader);glContext.activeTexture(gl_TEXTURE0);glContext.bindTexture(gl_TEXTURE_2D,glActiveTexture=glTileTexture);glContext.bindBuffer(gl_ARRAY_BUFFER,glArrayBuffer);glContext.bufferData(gl_ARRAY_BUFFER,gl_VERTEX_BUFFER_SIZE,gl_DYNAMIC_DRAW);glSetBlendMode();let a=0;var b=(d,e,f,g,k=0)=>{d=glContext.getAttribLocation(glShader,d);glContext.enableVertexAttribArray(d);glContext.vertexAttribPointer(d,g,e,k,gl_VERTEX_BYTE_STRIDE,a);a+=g*f};b("p",gl_FLOAT,4,2);b("t",gl_FLOAT,4,2);b("c",gl_UNSIGNED_BYTE,1,4,1);b("a",gl_UNSIGNED_BYTE,1,4,1);b=2*cameraScale/mainCanvas.width;const c=2*cameraScale/mainCanvas.height;glContext.uniformMatrix4fv(glContext.getUniformLocation(glShader,"m"),0,new Float32Array([b,0,0,0,0,c,0,0,1,1,-1,1,-1-b*cameraPos.x,-1-c*cameraPos.y,0,0]))}function glFlush(){if(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(glBatchCount||b)glFlush(),glOverlay&&!b||a.drawImage(glCanvas,0,0)}function glDraw(a,b,c,d,e,f,g,k,h,l,m=0){glBatchCount!=gl_MAX_BATCH&&glBatchAdditive==glAdditive||glFlush();var n=Math.cos(e)/2,p=Math.sin(e)/2;e=n*c;n*=d;c*=p;d*=p;for(let q=6,r=glBatchCount++*gl_VERTICES_PER_QUAD*gl_INDICIES_PER_VERT;q--;){p=q-4&&1<q;const w=q-5&&q-2&&q-1;glPositionData[r++]=a+(p?-e:e)+(w?d:-d);glPositionData[r++]=b+(w?n:-n)+(p?c:-c);glPositionData[r++]=p?f:k;glPositionData[r++]=w?g:h;glColorData[r++]=l;glColorData[r++]=m}}let glPostShader,glPostArrayBuffer,glPostTexture;function glInitPostProcess(a){ASSERT(!glPostShader);a||="void mainImage(out vec4 c,vec2 p){c=texture2D(iChannel0,p/iResolution.xy);}";glPostShader=glCreateProgram("precision highp float;attribute vec2 p;void main(){gl_Position=vec4(p,1,1);}","precision highp float;uniform sampler2D iChannel0;uniform vec3 iResolution;uniform float iTime;\n"+a+"\nvoid main(){mainImage(gl_FragColor,gl_FragCoord.xy);gl_FragColor.a=1.;}");glPostArrayBuffer=glContext.createBuffer();glPostTexture=glCreateTexture();mainCanvas.style.visibility="hidden"}function glRenderPostProcess(){if(glPostShader){glEnable?(glFlush(),mainContext.drawImage(glCanvas,0,0)):glContext.viewport(0,0,glCanvas.width=mainCanvas.width,glCanvas.height=mainCanvas.height);glContext.useProgram(glPostShader);glContext.disable(gl_BLEND);glContext.bindBuffer(gl_ARRAY_BUFFER,glPostArrayBuffer);glContext.bufferData(gl_ARRAY_BUFFER,new Float32Array([-3,1,1,-3,1,1]),gl_STATIC_DRAW);glContext.pixelStorei(gl_UNPACK_FLIP_Y_WEBGL,!0);glContext.activeTexture(gl_TEXTURE0);glContext.bindTexture(gl_TEXTURE_2D,glPostTexture);glContext.texImage2D(gl_TEXTURE_2D,0,gl_RGBA,gl_RGBA,gl_UNSIGNED_BYTE,mainCanvas);var a=glContext.getAttribLocation(glPostShader,"p");glContext.enableVertexAttribArray(a);glContext.vertexAttribPointer(a,2,gl_FLOAT,0,8,0);glContext.uniform1i(glContext.getUniformLocation(glPostShader,"iChannel0"),0);glContext.uniform1f(glContext.getUniformLocation(glPostShader,"iTime"),time);glContext.uniform3f(glContext.getUniformLocation(glPostShader,"iResolution"),mainCanvas.width,mainCanvas.height,1);glContext.drawArrays(gl_TRIANGLES,0,3)}}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_BYTE=5120,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_TEXTURE0=33984,gl_TEXTURE1=33985,gl_ARRAY_BUFFER=34962,gl_STATIC_DRAW=35044,gl_DYNAMIC_DRAW=35048,gl_FRAGMENT_SHADER=35632,gl_VERTEX_SHADER=35633,gl_COMPILE_STATUS=35713,gl_LINK_STATUS=35714,gl_UNPACK_FLIP_Y_WEBGL=37440,gl_VERTICES_PER_QUAD=6,gl_INDICIES_PER_VERT=6,gl_MAX_BATCH=65536,gl_VERTEX_BYTE_STRIDE=24,gl_VERTEX_BUFFER_SIZE=gl_MAX_BATCH*gl_VERTICES_PER_QUAD*gl_VERTEX_BYTE_STRIDE;
|
|
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*randInt(2)-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 setRandSeed=a=>randSeed=a,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),isVector2=a=>!isNaN(a.x)&&!isNaN(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(isVector2(a));return new Vector2(this.x+a.x,this.y+a.y)}subtract(a){ASSERT(isVector2(a));return new Vector2(this.x-a.x,this.y-a.y)}multiply(a){ASSERT(isVector2(a));return new Vector2(this.x*a.x,this.y*a.y)}divide(a){ASSERT(isVector2(a));return new Vector2(this.x/a.x,this.y/a.y)}scale(a){ASSERT(!isVector2(a));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(isVector2(a));return this.x*a.x+this.y*a.y}cross(a){ASSERT(isVector2(a));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(isVector2(a));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){if(debug)return`(${(0>this.x?"":" ")+this.x.toFixed(a)},${(0>this.y?"":" ")+this.y.toFixed(a)} )`}}const colorRGBA=(a,b,c,d)=>new Color(a,b,c,d),colorHSLA=(a,b,c,d)=>(new Color).setHSLA(a,b,c,d);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,g,k)=>(k=(k%1+1)%1)<1/6?f+6*(g-f)*k:.5>k?g:k<2/3?f+(g-f)*(2/3-k)*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=clamp(this.r),b=clamp(this.g),c=clamp(this.b),d=clamp(this.a),e=Math.max(a,b,c),f=Math.min(a,b,c),g=(e+f)/2;let k=0,h=0;if(e!=f){let l=e-f;h=.5<g?l/(2-e-f):l/(e+f);a==e?k=(b-c)/l+(b<c?6:0):b==e?k=(c-a)/l+2:c==e&&(k=(a-b)/l+4)}return[k/6,h,g,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(a=1){const b=c=>(16>(c=255*c|0)?"0":"")+c.toString(16);return"#"+b(this.r)+b(this.g)+b(this.b)+(a?b(this.a):"")}setHex(a){this.r=clamp(parseInt(a.slice(1,3),16)/255);this.g=clamp(parseInt(a.slice(3,5),16)/255);this.b=clamp(parseInt(a.slice(5,7),16)/255);this.a=7<a.length?clamp(parseInt(a.slice(7,9),16)/255):1;return this}rgbaInt(){const a=255*clamp(this.r)|0,b=(255*clamp(this.g)|0)<<8,c=(255*clamp(this.b)|0)<<16,d=(255*clamp(this.a)|0)<<24;return a+b+c+d}}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")}valueOf(){return this.get()}}"use strict";let cameraPos=vec2(),cameraScale=32,canvasMaxSize=vec2(1920,1200),canvasFixedSize=vec2(),cavasPixelated=1,fontDefault="arial",glEnable=1,glOverlay=1,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,gamepadsEnable=1,gamepadDirectionEmulateStick=1,inputWASDEmulateDirection=1,touchGamepadEnable=0,touchGamepadAnalog=1,touchGamepadSize=99,touchGamepadAlpha=.3,vibrateEnable=1,soundEnable=1,soundVolume=.5,soundDefaultRange=40,soundDefaultTaper=.7,medalDisplayTime=5,medalDisplaySlideTime=.5,medalDisplaySize=vec2(640,80),medalDisplayIconSize=50,medalsPreventUnlock;"use strict";const engineName="LittleJS",engineVersion="1.5.0",frameRate=60,timeDelta=1/frameRate;let engineObjects=[],engineObjectsCollide=[],frame=0,time=0,timeReal=0,paused=0;function setPaused(a){paused=a}function engineInit(a,b,c,d,e,f){function g(m=0){var n=m-k;k=m;if(debug||showWatermark)l=lerp(.05,l,1e3/(n||1));m=debug&&keyIsDown(107);const p=debug&&keyIsDown(109);debug&&(n*=m?5:p?.2:1);timeReal+=n/1e3;h+=!paused*n;m||(h=min(h,50));canvasFixedSize.x?(overlayCanvas.width=mainCanvas.width=canvasFixedSize.x,overlayCanvas.height=mainCanvas.height=canvasFixedSize.y,n=innerWidth/innerHeight,m=mainCanvas.width/mainCanvas.height,(glCanvas||mainCanvas).style.width=mainCanvas.style.width=overlayCanvas.style.width=n<m?"100%":"",(glCanvas||mainCanvas).style.height=mainCanvas.style.height=overlayCanvas.style.height=n<m?"":"100%"):(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{n=0;0>h&&-9<h&&(n=h,h=0);for(;0<=h;h-=1e3/frameRate)inputUpdate(),b(),engineObjectsUpdate(),debugUpdate(),c(),inputUpdatePost();h+=n}enginePreRender();d();engineObjects.sort((r,w)=>r.renderOrder-w.renderOrder);for(var q of engineObjects)q.destroyed||q.render();e();glRenderPostProcess();medalsRender();touchGamepadRender();debugRender();glEnable&&glCopyToContext(mainContext);showWatermark&&(overlayContext.textAlign="right",overlayContext.textBaseline="top",overlayContext.font="1em monospace",overlayContext.fillStyle="#000",q=engineName+" v"+engineVersion+" / "+drawCount+" / "+engineObjects.length+" / "+l.toFixed(1)+(glEnable?" GL":" 2D"),overlayContext.fillText(q,mainCanvas.width-3,3),overlayContext.fillStyle="#fff",overlayContext.fillText(q,mainCanvas.width-2,2),drawCount=0);requestAnimationFrame(g)}tileImage.onerror=tileImage.onload=()=>{tileImageFixBleed=vec2(tileFixBleedScale).divide(tileImageSize=vec2(tileImage.width,tileImage.height));debug&&(tileImage.onload=()=>ASSERT(1));document.body.style="margin:0;overflow:hidden;background:#000;touch-action:none;user-select:none;-webkit-user-select:none";document.body.appendChild(mainCanvas=document.createElement("canvas"));mainContext=mainCanvas.getContext("2d");debugInit();glEnable&&glInit();document.body.appendChild(overlayCanvas=document.createElement("canvas"));overlayContext=overlayCanvas.getContext("2d");(glCanvas||mainCanvas).style=mainCanvas.style=overlayCanvas.style="position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)";a();touchGamepadCreate();g()};let k=0,h,l;f?tileImage.src=f:tileImage.onload()}function enginePreRender(){mainCanvasSize=vec2(mainCanvas.width,mainCanvas.height);mainContext.imageSmoothingEnabled=!cavasPixelated;glEnable&&glPreRender()}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,g=0){ASSERT(isVector2(a)&&isVector2(b));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=g;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.velocity.y+=gravity*this.gravityScale,this.pos.x+=this.velocity.x*=this.damping,this.pos.y+=this.velocity.y*=this.damping,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,g=2*abs(a.x-d.pos.x)<c.x;if(e|g|!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){g=(this.mass*this.velocity.y+d.mass*d.velocity.y)/(this.mass+d.mass);const k=this.velocity.y*(this.mass-d.mass)/(this.mass+d.mass)+2*d.velocity.y*d.mass/(this.mass+d.mass),h=d.velocity.y*(d.mass-this.mass)/(this.mass+d.mass)+2*this.velocity.y*this.mass/(this.mass+d.mass),l=max(this.elasticity,d.elasticity);this.velocity.y=lerp(l,g,k);d.velocity.y=lerp(l,g,h)}!e&f&&(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),g=max(this.elasticity,d.elasticity),this.velocity.x=lerp(g,c,e),d.velocity.x=lerp(g,c,f)):this.velocity.x*=-this.elasticity);debugOverlay&&debugPhysics&&debugAABB(this.pos,this.size,d.pos,d.size,"#f0f")}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),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=1,b=1,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";let mainCanvas,mainContext,overlayCanvas,overlayContext,mainCanvasSize=vec2();const tileImage=new Image;let tileImageSize,tileImageFixBleed,drawCount;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,g,k=new Color(0,0,0,0),h=glEnable){showWatermark&&++drawCount;if(glEnable&&h)if(0>c||!tileImage.width)glDraw(a.x,a.y,b.x,b.y,f,0,0,0,0,0,e.rgbaInt());else{var l=tileImageSize.x/d.x|0;h=d.x/tileImageSize.x;const m=d.y/tileImageSize.y,n=c%l*h;l=(c/l|0)*m;glDraw(a.x,a.y,g?-b.x:b.x,b.y,f,n+tileImageFixBleed.x,l+tileImageFixBleed.y,n-tileImageFixBleed.x+h,l-tileImageFixBleed.y+m,e.rgbaInt(),k.rgbaInt())}else drawCanvas2D(a,b,f,g,m=>{if(0>c)m.fillStyle=e,m.fillRect(-.5,-.5,1,1);else{var n=tileImageSize.x/d.x|0;const p=c%n*d.x+tileFixBleedScale;n=(c/n|0)*d.y+tileFixBleedScale;const q=d.x-2*tileFixBleedScale,r=d.y-2*tileFixBleedScale;m.globalAlpha=e.a;m.drawImage(tileImage,p,n,q,r,-.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,g,k,h){drawTile(screenToWorld(a),b.scale(1/cameraScale),c,d,e,f,g,k,h)}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(),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),g="center",k=fontDefault,h=overlayContext){h.fillStyle=d;h.lineWidth=e;h.strokeStyle=f;h.textAlign=g;h.font=c+"px "+k;h.textBaseline="middle";h.lineJoin="round";b=b.copy();(a+"").split("\n").forEach(l=>{e&&h.strokeText(l,b.x,b.y);h.fillText(l,b.x,b.y);b.y+=c})}function drawText(a,b,c=1,d,e,f,g,k){drawTextScreen(a,worldToScreen(b),c*cameraScale,d,e*cameraScale,f,g,k,mainContext)}let engineFontImage;class FontImage{constructor(a,b=vec2(8),c=vec2(0,1),d=0,e=overlayContext){engineFontImage||((engineFontImage=new Image).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,g=f.add(this.paddingSize).scale(c),k=this.image.width/this.tileSize.x|0;(a+"").split("\n").forEach((h,l)=>{const m=d?h.length*f.x*c/2|0:0;for(let q=h.length;q--;){var n=h[q].charCodeAt();if(32>n||127<n)n=127;var p=this.startTileIndex+n-32;n=p%k;p=p/k|0;const r=b.add(vec2(q,l).multiply(g));e.drawImage(this.image,n*f.x,p*f.y,f.x,f.y,r.x-m,r.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.body.requestFullscreen&&document.body.requestFullscreen()}"use strict";const keyIsDown=(a,b=0)=>inputData[b]&&inputData[b][a]&1,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(){isTouchDevice||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&&navigator.getGamepads&&(document.hasFocus()||debug))for(a=navigator.getGamepads(),b=a.length;b--;){var d=a[b];const g=inputData[b+1]||(inputData[b+1]=[]);c=stickData[b]||(stickData[b]=[]);if(d){var e=k=>.3<k?percent(k,.3,.8):-.3>k?-percent(-k,.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],g[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&&navigator.vibrate&&navigator.vibrate(a),vibrateStop=()=>vibrate(0),isTouchDevice=void 0!==window.ontouchstart;if(isTouchDevice){let a,b=onmousedown,c=onmouseup;onmousedown=onmouseup=()=>0;ontouchstart=d=>{zzfx(0);ontouchstart=ontouchmove=ontouchend=e=>{e.button=0;const f=e.touches.length;f?(e.x=e.touches[0].clientX,e.y=e.touches[0].clientY,a?onmousemove(e):b(e)):a&&c(e);a=f;return!0};return ontouchstart(d)}}let touchGamepadTimer=new Timer,touchGamepadButtons,touchGamepadStick;function touchGamepadCreate(){touchGamepadEnable&&isTouchDevice&&(touchGamepadButtons=[],touchGamepadStick=vec2(),ontouchstart=a=>{zzfx(0);ontouchstart=ontouchmove=ontouchend=b=>{touchGamepadStick=vec2();touchGamepadButtons=[];if(b.touches.length&&(isUsingGamepad=1,touchGamepadTimer.set(),paused)){touchGamepadButtons[9]=1;return}const c=vec2(touchGamepadSize,mainCanvasSize.y-touchGamepadSize),d=mainCanvasSize.subtract(vec2(touchGamepadSize,touchGamepadSize)),e=mainCanvasSize.scale(.5);for(const f of b.touches)b=mouseToScreen(vec2(f.clientX,f.clientY)),b.distance(c)<touchGamepadSize?touchGamepadAnalog?touchGamepadStick=b.subtract(c).scale(2/touchGamepadSize).clampLength():(b=b.subtract(c).angle(),touchGamepadStick.setAngle((4*b/PI+8.5|0)*PI/4)):b.distance(d)<touchGamepadSize?(b=b.subtract(d).direction(),touchGamepadButtons[b]=1):b.distance(e)<touchGamepadSize&&(touchGamepadButtons[9]=1)};return ontouchstart(a)})}function touchGamepadRender(){if(touchGamepadEnable&&touchGamepadTimer.isSet()){var a=percent(touchGamepadTimer,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;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){if(soundEnable)return this.play(b,c,2**(a/12),0)}}class Music{constructor(a){soundEnable&&(this.cachedSamples=zzfxM(...a))}play(a,b=1){soundEnable&&(this.source=playSamples(this.cachedSamples,a,1,0,b))}stop(){this.source&&this.source.stop();this.source=0}isPlaying(){return this.source}}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||=new AudioContext,audioContext.resume(),"running"==audioContext.state)){var f=audioContext.createBuffer(a.length,a[0].length,zzfxR),g=audioContext.createBufferSource();a.forEach((k,h)=>f.getChannelData(h).set(k));g.buffer=f;g.playbackRate.value=c;g.loop=e;a=audioContext.createGain();a.gain.value=soundVolume*b;a.connect(audioContext.destination);g.connect(new StereoPannerNode(audioContext,{pan:clamp(d,-1,1)})).connect(a);g.start();return g}}const zzfx=(...a)=>playSamples([zzfxG(...a)]),zzfxR=44100;function zzfxG(a=1,b=.05,c=220,d=0,e=0,f=.1,g=0,k=1,h=0,l=0,m=0,n=0,p=0,q=0,r=0,w=0,u=0,B=1,z=0,C=0){let v=2*PI,F=h*=500*v/zzfxR/zzfxR,A=[];b=c*=(1+b*rand(-1,1))*v/zzfxR;let x=0,D=0,t=0,y=1,G=0,I=0,E=0,J,H;d=d*zzfxR+9;z*=zzfxR;e*=zzfxR;f*=zzfxR;u*=zzfxR;l*=500*v/zzfxR**3;r*=v/zzfxR;m*=v/zzfxR;n*=zzfxR;p=p*zzfxR|0;for(H=d+z+e+f+u|0;t<H;A[t++]=E)++I%(100*w|0)||(E=g?1<g?2<g?3<g?Math.sin((x%v)**3):max(min(Math.tan(x),1),-1):1-(2*x/v%2+2)%2:1-4*abs(Math.round(x/v)-x/v):Math.sin(x),E=(p?1-C+C*Math.sin(v*t/p):1)*sign(E)*abs(E)**k*a*soundVolume*(t<d?t/d:t<d+z?1-(t-d)/z*(1-B):t<d+z+e?B:t<H-u?(H-t-u)/f*B:0),E=u?E/2+(u>t?0:(t<H-u?1:(H-t)/u)*A[t-u|0]/2):E),J=(c+=h+=l)*Math.cos(r*D++),x+=J-J*q*(1-1e9*(Math.sin(t)+1)%2),y&&++y>n&&(c+=m,b+=m,y=0),!p||++G%p||(c=b,h=F,y=y||1);return A}function zzfxM(a,b,c,d=125){let e,f,g,k,h,l,m,n,p,q,r,w,u,B=0,z,C=[],v=[],F=[],A=0,x=0,D=1,t={},y=zzfxR/d*60>>2;for(;D;A++)C=[D=n=w=0],c.forEach((G,I)=>{m=b[G][A]||[0,0,0];D|=!!b[G][A];z=w+(b[G][0].length-2-!n)*y;u=I==c.length-1;e=2;for(g=w;e<m.length+u;n=++e){h=m[e];p=e==m.length+u-1&&u||q!=(m[0]||0)|h|0;for(f=0;f<y&&n;f++>y-99&&p?r+=(1>r)/99:0)l=(1-r)*C[B++]/2||0,v[g]=(v[g]||0)-l*x+l,F[g]=(F[g++]||0)+l*x+l;h&&(r=h%1,x=m[1]||0,h|=0)&&(C=t[[q=m[B=0]||0,h]]=t[[q,h]]||(k=[...a[q]],k[2]*=2**((h-12)/12),0<h?zzfxG(...k):[]))}w=z});return[v,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 g=tileCollision[e*tileCollisionSize.x+b];if(g&&(!c||c.collideWithTile(g,new Vector2(b,e))))return 1}}function tileCollisionRaycast(a,b,c){var d=(b=b.floor()).subtract(a=a.floor());const e=abs(d.x),f=-abs(d.y),g=sign(d.x);d=sign(d.y);for(let h=a.x,l=a.y,m=e+f;;){var k=getTileCollisionData(vec2(h,l));if(k&&(c?c.collideWithTileRaycast(k,new Vector2(h,l)):0<k))return debugRaycast&&debugLine(a,b,"#f00",.02,1),debugRaycast&&debugPoint(new Vector2(h+.5,l+.5),"#ff0",1),new Vector2(h+.5,l+.5);if(h==b.x&l==b.y)break;k=2*m;k>=f&&(m+=f,h+=g);k<=e&&(m+=e,l+=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){this.savedRenderSettings=[mainCanvas,mainContext,mainCanvasSize,cameraPos,cameraScale];mainCanvas=this.canvas;mainContext=this.context;cameraPos=this.size.scale(.5);cameraScale=this.tileSize.x;a&&(mainCanvas.width=this.size.x*this.tileSize.x,mainCanvas.height=this.size.y*this.tileSize.y);enginePreRender()}redrawEnd(){ASSERT(mainContext==this.context);glEnable&&glCopyToContext(mainContext,1);[mainCanvas,mainContext,mainCanvasSize,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,g){this.drawCanvas2D(a,b,f,g,k=>{if(0>c)k.fillStyle=e,k.fillRect(-.5,-.5,1,1);else{const h=tileImage.width/d.x;k.globalAlpha=e.a;k.drawImage(tileImage,c%h*d.x,(c/h|0)*d.y,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,g=-1,k=tileSizeDefault,h=new Color,l=new Color,m=new Color(1,1,1,0),n=new Color(1,1,1,0),p=.5,q=.1,r=1,w=.1,u=.05,B=1,z=1,C=0,v=PI,F=.1,A=.2,x,D,t=1,y=D?1e9:0,G){super(a,new Vector2,g,k,b,void 0,y);this.emitSize=c;this.emitTime=d;this.emitRate=e;this.emitConeAngle=f;this.colorStartA=h;this.colorStartB=l;this.colorEndA=m;this.colorEndB=n;this.randomColorLinear=t;this.particleTime=p;this.sizeStart=q;this.sizeEnd=r;this.speed=w;this.angleSpeed=u;this.damping=B;this.angleDamping=z;this.gravityScale=C;this.particleConeAngle=v;this.fadeRate=F;this.randomness=A;this.collideTiles=x;this.additive=D;this.localSpace=G;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(this.emitSize/2),b=rand(this.particleConeAngle,-this.particleConeAngle);this.localSpace||(a=this.pos.add(a),b+=this.angle);a=new Particle(a,this.tileIndex,this.tileSize,b);const c=this.randomness;var d=m=>m+m*rand(c,-c);b=d(this.particleTime);const e=d(this.sizeStart),f=d(this.sizeEnd),g=d(this.speed);d=d(this.angleSpeed)*randSign();var k=rand(this.emitConeAngle,-this.emitConeAngle);const h=randColor(this.colorStartA,this.colorStartB,this.randomColorLinear),l=randColor(this.colorEndA,this.colorEndB,this.randomColorLinear);k=this.localSpace?k:this.angle+k;a.colorStart=h;a.colorEndDelta=l.subtract(h);a.velocity=(new Vector2).setAngle(k,g);a.angleVelocity=d;a.lifeTime=b;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=randInt(2);a.localSpaceEmitter=this.localSpace&&this;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);let d=this.pos;var e=this.angle;this.localSpaceEmitter&&(d=this.localSpaceEmitter.pos.add(d.rotate(-this.localSpaceEmitter.angle)),e+=this.localSpaceEmitter.angle);if(this.trailScale){var f=this.velocity;this.localSpaceEmitter&&(f=f.rotate(-this.localSpaceEmitter.angle));e=f.length();f=f.scale(1/e);const g=e*this.trailScale;b.y=max(b.x,g);e=f.angle();drawTile(d.add(f.multiply(vec2(0,-g/2))),b,this.tileIndex,this.tileSize,c,e,this.mirror)}else drawTile(d,b,this.tileIndex,this.tileSize,c,e,this.mirror);this.additive&&setBlendMode();debugParticles&&debugRect(d,b,"#f005",0,e);1==a&&(this.color=c,this.size=b,this.destroyCallback&&this.destroyCallback(this),this.destroyed=1)}}"use strict";const medals=[];let medalsDisplayQueue=[],medalsSaveName,medalsDisplayTimeLast;function medalsInit(a){medalsSaveName=a;debugMedals||medals.forEach(b=>b.unlocked=localStorage[b.storageKey()]|0)}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;e&&((this.image=new Image).src=e)}unlock(){medalsPreventUnlock||this.unlocked||(ASSERT(medalsSaveName),localStorage[this.storageKey()]=this.unlocked=1,medalsDisplayQueue.push(this),newgrounds&&newgrounds.unlockMedal(this.id))}render(a=0){const b=overlayContext;var c=min(medalDisplaySize.x,mainCanvas.width);const d=overlayCanvas.width-c;a*=-medalDisplaySize.y;b.save();b.beginPath();b.fillStyle=new Color(.9,.9,.9);b.strokeStyle=new Color(0,0,0);b.lineWidth=3;b.fill(b.rect(d,a,c,medalDisplaySize.y));b.stroke();b.clip();this.renderIcon(vec2(d+15+medalDisplayIconSize/2,a+medalDisplaySize.y/2));c=vec2(d+medalDisplayIconSize+30,a+28);drawTextScreen(this.name,c,38,new Color(0,0,0),0,0,"left");c.y+=32;drawTextScreen(this.description,c,24,new Color(0,0,0),0,0,"left");b.restore()}renderIcon(a,b=medalDisplayIconSize){this.image?overlayContext.drawImage(this.image,a.x-b/2,a.y-b/2,b,b):drawTextScreen(this.icon,a,.7*b,new Color(0,0,0))}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}}let newgrounds;function newgroundsInit(a,b){newgrounds=new Newgrounds(a,b)}class Newgrounds{constructor(a,b){ASSERT(!newgrounds&&a);this.app_id=a;this.cipher=b;this.host=location?location.hostname:"";b&&(this.cryptoJS=this.CryptoJS());if(this.session_id=new URL(location.href).searchParams.get("ngio_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=new Image,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)}CryptoJS(){return 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,glArrayBuffer,glPositionData,glColorData,glBatchCount,glBatchAdditive,glAdditive;function glInit(){glCanvas=document.createElement("canvas");glContext=glCanvas.getContext("webgl",{antialias:!1});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 vec4 v,d,e;void main(){gl_Position=m*vec4(p,1,1);v=vec4(t,p);d=c;e=a;}","precision highp float;varying vec4 v,d,e;uniform sampler2D s;void main(){gl_FragColor=texture2D(s,v.xy)*d+e;}");const a=new ArrayBuffer(gl_VERTEX_BUFFER_SIZE);glArrayBuffer=glContext.createBuffer();glPositionData=new Float32Array(a);glColorData=new Uint32Array(a);glBatchCount=0}function glSetBlendMode(a){glAdditive=a}function glSetTexture(a=glTileTexture){a!=glActiveTexture&&glFlush();glContext.bindTexture(gl_TEXTURE_2D,glActiveTexture=a)}function glCompileShader(a,b){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){const 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 glCreateTexture(a){const b=glContext.createTexture();glContext.bindTexture(gl_TEXTURE_2D,b);a&&a.width&&glContext.texImage2D(gl_TEXTURE_2D,0,gl_RGBA,gl_RGBA,gl_UNSIGNED_BYTE,a);a=cavasPixelated?gl_NEAREST:gl_LINEAR;glContext.texParameteri(gl_TEXTURE_2D,gl_TEXTURE_MIN_FILTER,a);glContext.texParameteri(gl_TEXTURE_2D,gl_TEXTURE_MAG_FILTER,a);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(){glContext.viewport(0,0,glCanvas.width=mainCanvas.width,glCanvas.height=mainCanvas.height);glContext.clear(gl_COLOR_BUFFER_BIT);glContext.useProgram(glShader);glContext.activeTexture(gl_TEXTURE0);glContext.bindTexture(gl_TEXTURE_2D,glActiveTexture=glTileTexture);glContext.bindBuffer(gl_ARRAY_BUFFER,glArrayBuffer);glContext.bufferData(gl_ARRAY_BUFFER,gl_VERTEX_BUFFER_SIZE,gl_DYNAMIC_DRAW);glSetBlendMode();let a=0;var b=(d,e,f,g,k=0)=>{d=glContext.getAttribLocation(glShader,d);glContext.enableVertexAttribArray(d);glContext.vertexAttribPointer(d,g,e,k,gl_VERTEX_BYTE_STRIDE,a);a+=g*f};b("p",gl_FLOAT,4,2);b("t",gl_FLOAT,4,2);b("c",gl_UNSIGNED_BYTE,1,4,1);b("a",gl_UNSIGNED_BYTE,1,4,1);b=2*cameraScale/mainCanvas.width;const c=2*cameraScale/mainCanvas.height;glContext.uniformMatrix4fv(glContext.getUniformLocation(glShader,"m"),0,new Float32Array([b,0,0,0,0,c,0,0,1,1,-1,1,-1-b*cameraPos.x,-1-c*cameraPos.y,0,0]))}function glFlush(){if(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(glBatchCount||b)glFlush(),glOverlay&&!b||a.drawImage(glCanvas,0,0)}function glDraw(a,b,c,d,e,f,g,k,h,l,m=0){glBatchCount!=gl_MAX_BATCH&&glBatchAdditive==glAdditive||glFlush();var n=Math.cos(e)/2,p=Math.sin(e)/2;e=n*c;n*=d;c*=p;d*=p;for(let q=6,r=glBatchCount++*gl_VERTICES_PER_QUAD*gl_INDICIES_PER_VERT;q--;){p=q-4&&1<q;const w=q-5&&q-2&&q-1;glPositionData[r++]=a+(p?-e:e)+(w?d:-d);glPositionData[r++]=b+(w?n:-n)+(p?c:-c);glPositionData[r++]=p?f:k;glPositionData[r++]=w?g:h;glColorData[r++]=l;glColorData[r++]=m}}let glPostShader,glPostArrayBuffer,glPostTexture;function glInitPostProcess(a){ASSERT(!glPostShader);a||="void mainImage(out vec4 c,vec2 p){c=texture2D(iChannel0,p/iResolution.xy);}";glPostShader=glCreateProgram("precision highp float;attribute vec2 p;void main(){gl_Position=vec4(p,1,1);}","precision highp float;uniform sampler2D iChannel0;uniform vec3 iResolution;uniform float iTime;\n"+a+"\nvoid main(){mainImage(gl_FragColor,gl_FragCoord.xy);gl_FragColor.a=1.;}");glPostArrayBuffer=glContext.createBuffer();glPostTexture=glCreateTexture();mainCanvas.style.visibility="hidden"}function glRenderPostProcess(){if(glPostShader){glEnable?(glFlush(),mainContext.drawImage(glCanvas,0,0)):glContext.viewport(0,0,glCanvas.width=mainCanvas.width,glCanvas.height=mainCanvas.height);glContext.useProgram(glPostShader);glContext.disable(gl_BLEND);glContext.bindBuffer(gl_ARRAY_BUFFER,glPostArrayBuffer);glContext.bufferData(gl_ARRAY_BUFFER,new Float32Array([-3,1,1,-3,1,1]),gl_STATIC_DRAW);glContext.pixelStorei(gl_UNPACK_FLIP_Y_WEBGL,!0);glContext.activeTexture(gl_TEXTURE0);glContext.bindTexture(gl_TEXTURE_2D,glPostTexture);glContext.texImage2D(gl_TEXTURE_2D,0,gl_RGBA,gl_RGBA,gl_UNSIGNED_BYTE,mainCanvas);var a=glContext.getAttribLocation(glPostShader,"p");glContext.enableVertexAttribArray(a);glContext.vertexAttribPointer(a,2,gl_FLOAT,0,8,0);glContext.uniform1i(glContext.getUniformLocation(glPostShader,"iChannel0"),0);glContext.uniform1f(glContext.getUniformLocation(glPostShader,"iTime"),time);glContext.uniform3f(glContext.getUniformLocation(glPostShader,"iResolution"),mainCanvas.width,mainCanvas.height,1);glContext.drawArrays(gl_TRIANGLES,0,3)}}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_BYTE=5120,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_TEXTURE0=33984,gl_TEXTURE1=33985,gl_ARRAY_BUFFER=34962,gl_STATIC_DRAW=35044,gl_DYNAMIC_DRAW=35048,gl_FRAGMENT_SHADER=35632,gl_VERTEX_SHADER=35633,gl_COMPILE_STATUS=35713,gl_LINK_STATUS=35714,gl_UNPACK_FLIP_Y_WEBGL=37440,gl_VERTICES_PER_QUAD=6,gl_INDICIES_PER_VERT=6,gl_MAX_BATCH=65536,gl_VERTEX_BYTE_STRIDE=24,gl_VERTEX_BUFFER_SIZE=gl_MAX_BATCH*gl_VERTICES_PER_QUAD*gl_VERTEX_BYTE_STRIDE;
|
|
@@ -59,9 +59,9 @@ const ASSERT = enableAsserts ? (...assert)=> console.assert(...assert) : ()=>{};
|
|
|
59
59
|
* @param {String} [color='#fff']
|
|
60
60
|
* @param {Number} [time=0]
|
|
61
61
|
* @param {Number} [angle=0]
|
|
62
|
-
* @param {Boolean} [fill=
|
|
62
|
+
* @param {Boolean} [fill=false]
|
|
63
63
|
* @memberof Debug */
|
|
64
|
-
const debugRect = (pos, size=vec2(), color='#fff', time=0, angle=0, fill=
|
|
64
|
+
const debugRect = (pos, size=vec2(), color='#fff', time=0, angle=0, fill=false)=>
|
|
65
65
|
{
|
|
66
66
|
ASSERT(typeof color == 'string'); // pass in regular html strings as colors
|
|
67
67
|
debugPrimitives.push({pos, size:vec2(size), color, time:new Timer(time), angle, fill});
|
|
@@ -72,9 +72,9 @@ const debugRect = (pos, size=vec2(), color='#fff', time=0, angle=0, fill=0)=>
|
|
|
72
72
|
* @param {Number} [radius=0]
|
|
73
73
|
* @param {String} [color='#fff']
|
|
74
74
|
* @param {Number} [time=0]
|
|
75
|
-
* @param {Boolean} [fill=
|
|
75
|
+
* @param {Boolean} [fill=false]
|
|
76
76
|
* @memberof Debug */
|
|
77
|
-
const debugCircle = (pos, radius=0, color='#fff', time=0, fill=
|
|
77
|
+
const debugCircle = (pos, radius=0, color='#fff', time=0, fill=false)=>
|
|
78
78
|
{
|
|
79
79
|
ASSERT(typeof color == 'string'); // pass in regular html strings as colors
|
|
80
80
|
debugPrimitives.push({pos, size:radius, color, time:new Timer(time), angle:0, fill});
|
|
@@ -1528,9 +1528,9 @@ class EngineObject
|
|
|
1528
1528
|
}
|
|
1529
1529
|
|
|
1530
1530
|
/** Set how this object collides
|
|
1531
|
-
* @param {
|
|
1532
|
-
* @param {
|
|
1533
|
-
* @param {
|
|
1531
|
+
* @param {Boolean} [collideSolidObjects=1] - Does it collide with solid objects
|
|
1532
|
+
* @param {Boolean} [isSolid=1] - Does it collide with and block other objects (expensive in large numbers)
|
|
1533
|
+
* @param {Boolean} [collideTiles=1] - Does it collide with the tile collision */
|
|
1534
1534
|
setCollision(collideSolidObjects=1, isSolid=1, collideTiles=1)
|
|
1535
1535
|
{
|
|
1536
1536
|
ASSERT(collideSolidObjects || !isSolid); // solid objects must be set to collide
|
|
@@ -1540,6 +1540,8 @@ class EngineObject
|
|
|
1540
1540
|
this.collideTiles = collideTiles;
|
|
1541
1541
|
}
|
|
1542
1542
|
|
|
1543
|
+
/** Returns string containg info about this object for debugging
|
|
1544
|
+
* @return {String} */
|
|
1543
1545
|
toString()
|
|
1544
1546
|
{
|
|
1545
1547
|
if (debug)
|
|
@@ -1609,7 +1611,7 @@ let overlayContext;
|
|
|
1609
1611
|
let mainCanvasSize = vec2();
|
|
1610
1612
|
|
|
1611
1613
|
/** Tile sheet for batch rendering system
|
|
1612
|
-
* @type {
|
|
1614
|
+
* @type {CanvasImageSource}
|
|
1613
1615
|
* @memberof Draw */
|
|
1614
1616
|
const tileImage = new Image;
|
|
1615
1617
|
|
|
@@ -2190,7 +2192,7 @@ const vibrateStop = ()=> vibrate(0);
|
|
|
2190
2192
|
// Touch input
|
|
2191
2193
|
|
|
2192
2194
|
/** True if a touch device has been detected
|
|
2193
|
-
* @const {
|
|
2195
|
+
* @const {Boolean}
|
|
2194
2196
|
* @memberof Input */
|
|
2195
2197
|
const isTouchDevice = window.ontouchstart !== undefined;
|
|
2196
2198
|
|
|
@@ -2520,8 +2522,21 @@ class Music
|
|
|
2520
2522
|
{
|
|
2521
2523
|
if (!soundEnable) return;
|
|
2522
2524
|
|
|
2523
|
-
|
|
2525
|
+
this.source = playSamples(this.cachedSamples, volume, 1, 0, loop);
|
|
2524
2526
|
}
|
|
2527
|
+
|
|
2528
|
+
/** Stop the music */
|
|
2529
|
+
stop()
|
|
2530
|
+
{
|
|
2531
|
+
if (this.source)
|
|
2532
|
+
this.source.stop();
|
|
2533
|
+
this.source = 0;
|
|
2534
|
+
}
|
|
2535
|
+
|
|
2536
|
+
/** Check if music is playing
|
|
2537
|
+
* @return {Boolean}
|
|
2538
|
+
*/
|
|
2539
|
+
isPlaying() { return this.source; }
|
|
2525
2540
|
}
|
|
2526
2541
|
|
|
2527
2542
|
/** Play an mp3 or wav audio from a local file or url
|
|
@@ -3212,7 +3227,7 @@ class ParticleEmitter extends EngineObject
|
|
|
3212
3227
|
* @param {Number} [emitRate=100] - How many particles per second to spawn, does not emit if 0
|
|
3213
3228
|
* @param {Number} [emitConeAngle=PI] - Local angle to apply velocity to particles from emitter
|
|
3214
3229
|
* @param {Number} [tileIndex=-1] - Index into tile sheet, if <0 no texture is applied
|
|
3215
|
-
* @param {
|
|
3230
|
+
* @param {Vector2} [tileSize=tileSizeDefault] - Tile size for particles
|
|
3216
3231
|
* @param {Color} [colorStartA=new Color(1,1,1)] - Color at start of life 1, randomized between start colors
|
|
3217
3232
|
* @param {Color} [colorStartB=new Color(1,1,1)] - Color at start of life 2, randomized between start colors
|
|
3218
3233
|
* @param {Color} [colorEndA=new Color(1,1,1,0)] - Color at end of life 1, randomized between end colors
|
|
@@ -4190,7 +4205,7 @@ gl_VERTEX_BUFFER_SIZE = gl_MAX_BATCH * gl_VERTICES_PER_QUAD * gl_VERTEX_BYTE_STR
|
|
|
4190
4205
|
const engineName = 'LittleJS';
|
|
4191
4206
|
|
|
4192
4207
|
/** Version of engine */
|
|
4193
|
-
const engineVersion = '1.
|
|
4208
|
+
const engineVersion = '1.5.0';
|
|
4194
4209
|
|
|
4195
4210
|
/** Frames per second to update objects
|
|
4196
4211
|
* @default */
|