littlejsengine 1.10.7 → 1.11.1

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/dist/littlejs.js CHANGED
@@ -1433,12 +1433,18 @@ let canvasMaxSize = vec2(1920, 1080);
1433
1433
  * @memberof Settings */
1434
1434
  let canvasFixedSize = vec2();
1435
1435
 
1436
- /** Disables filtering for crisper pixel art if true
1436
+ /** Use nearest neighbor scaling algorithm for canvas for more pixelated look
1437
1437
  * @type {Boolean}
1438
1438
  * @default
1439
1439
  * @memberof Settings */
1440
1440
  let canvasPixelated = true;
1441
1441
 
1442
+ /** Disables texture filtering for crisper pixel art
1443
+ * @type {Boolean}
1444
+ * @default
1445
+ * @memberof Settings */
1446
+ let tilesPixelated = true;
1447
+
1442
1448
  /** Default font used for text rendering
1443
1449
  * @type {String}
1444
1450
  * @default
@@ -1687,11 +1693,16 @@ function setCanvasMaxSize(size) { canvasMaxSize = size; }
1687
1693
  * @memberof Settings */
1688
1694
  function setCanvasFixedSize(size) { canvasFixedSize = size; }
1689
1695
 
1690
- /** Disables anti aliasing for pixel art if true
1696
+ /** Use nearest neighbor scaling algorithm for canvas for more pixelated look
1691
1697
  * @param {Boolean} pixelated
1692
1698
  * @memberof Settings */
1693
1699
  function setCanvasPixelated(pixelated) { canvasPixelated = pixelated; }
1694
1700
 
1701
+ /** Disables texture filtering for crisper pixel art
1702
+ * @param {Boolean} pixelated
1703
+ * @memberof Settings */
1704
+ function setTilesPixelated(pixelated) { tilesPixelated = pixelated; }
1705
+
1695
1706
  /** Set default font used for text rendering
1696
1707
  * @param {String} font
1697
1708
  * @memberof Settings */
@@ -2748,6 +2759,24 @@ function setBlendMode(additive, useWebGL=glEnable, context)
2748
2759
  }
2749
2760
  }
2750
2761
 
2762
+ /** Draw text on main canvas in world space
2763
+ * Automatically splits new lines into rows
2764
+ * @param {String} text
2765
+ * @param {Vector2} pos
2766
+ * @param {Number} [size]
2767
+ * @param {Color} [color=(1,1,1,1)]
2768
+ * @param {Number} [lineWidth]
2769
+ * @param {Color} [lineColor=(0,0,0,1)]
2770
+ * @param {CanvasTextAlign} [textAlign='center']
2771
+ * @param {String} [font=fontDefault]
2772
+ * @param {Number} [maxWidth]
2773
+ * @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} [context=mainContext]
2774
+ * @memberof Draw */
2775
+ function drawText(text, pos, size=1, color, lineWidth=0, lineColor, textAlign, font, maxWidth, context=mainContext)
2776
+ {
2777
+ drawTextScreen(text, worldToScreen(pos), size*cameraScale, color, lineWidth*cameraScale, lineColor, textAlign, font, maxWidth, context);
2778
+ }
2779
+
2751
2780
  /** Draw text on overlay canvas in world space
2752
2781
  * Automatically splits new lines into rows
2753
2782
  * @param {String} text
@@ -2758,12 +2787,11 @@ function setBlendMode(additive, useWebGL=glEnable, context)
2758
2787
  * @param {Color} [lineColor=(0,0,0,1)]
2759
2788
  * @param {CanvasTextAlign} [textAlign='center']
2760
2789
  * @param {String} [font=fontDefault]
2761
- * @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} [context=overlayContext]
2762
2790
  * @param {Number} [maxWidth]
2763
2791
  * @memberof Draw */
2764
- function drawText(text, pos, size=1, color, lineWidth=0, lineColor, textAlign, font, context, maxWidth)
2792
+ function drawTextOverlay(text, pos, size=1, color, lineWidth=0, lineColor, textAlign, font, maxWidth)
2765
2793
  {
2766
- drawTextScreen(text, worldToScreen(pos), size*cameraScale, color, lineWidth*cameraScale, lineColor, textAlign, font, context, maxWidth);
2794
+ drawText(text, pos, size, color, lineWidth, lineColor, textAlign, font, maxWidth, overlayContext);
2767
2795
  }
2768
2796
 
2769
2797
  /** Draw text on overlay canvas in screen space
@@ -2776,10 +2804,10 @@ function drawText(text, pos, size=1, color, lineWidth=0, lineColor, textAlign, f
2776
2804
  * @param {Color} [lineColor=(0,0,0,1)]
2777
2805
  * @param {CanvasTextAlign} [textAlign]
2778
2806
  * @param {String} [font=fontDefault]
2779
- * @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} [context=overlayContext]
2780
2807
  * @param {Number} [maxWidth]
2808
+ * @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} [context=overlayContext]
2781
2809
  * @memberof Draw */
2782
- function drawTextScreen(text, pos, size=1, color=new Color, lineWidth=0, lineColor=new Color(0,0,0), textAlign='center', font=fontDefault, context=overlayContext, maxWidth=undefined)
2810
+ function drawTextScreen(text, pos, size=1, color=new Color, lineWidth=0, lineColor=new Color(0,0,0), textAlign='center', font=fontDefault, maxWidth=undefined, context=overlayContext)
2783
2811
  {
2784
2812
  context.fillStyle = color.toString();
2785
2813
  context.lineWidth = lineWidth;
@@ -2855,7 +2883,6 @@ class FontImage
2855
2883
  {
2856
2884
  const context = this.context;
2857
2885
  context.save();
2858
- context.imageSmoothingEnabled = !canvasPixelated;
2859
2886
 
2860
2887
  const size = this.tileSize;
2861
2888
  const drawSize = size.add(this.paddingSize).scale(scale);
@@ -3484,9 +3511,6 @@ class Sound
3484
3511
 
3485
3512
  /** @property {Number} - How much to randomize frequency each time sound plays */
3486
3513
  this.randomness = 0;
3487
-
3488
- /** @property {GainNode} - Gain node for this sound */
3489
- this.gainNode = audioContext.createGain();
3490
3514
 
3491
3515
  if (zzfxSound)
3492
3516
  {
@@ -3533,13 +3557,19 @@ class Sound
3533
3557
 
3534
3558
  // play the sound
3535
3559
  const playbackRate = pitch + pitch * this.randomness*randomnessScale*rand(-1,1);
3536
- return this.source = playSamples(this.sampleChannels, volume, playbackRate, pan, loop, this.sampleRate, this.gainNode);
3560
+ this.gainNode = audioContext.createGain();
3561
+ this.source = playSamples(this.sampleChannels, volume, playbackRate, pan, loop, this.sampleRate, this.gainNode);
3562
+ return this.source;
3537
3563
  }
3538
3564
 
3539
- /** Set the sound volume
3565
+ /** Set the sound volume of the most recently played instance of this sound
3540
3566
  * @param {Number} [volume] - How much to scale volume by
3541
3567
  */
3542
- setVolume(volume=1) { this.gainNode.gain.value = volume; }
3568
+ setVolume(volume=1)
3569
+ {
3570
+ if (this.gainNode)
3571
+ this.gainNode.gain.value = volume;
3572
+ }
3543
3573
 
3544
3574
  /** Stop the last instance of this sound that was played */
3545
3575
  stop()
@@ -4293,7 +4323,7 @@ class TileLayer extends EngineObject
4293
4323
  }
4294
4324
 
4295
4325
  // disable smoothing for pixel art
4296
- this.context.imageSmoothingEnabled = !canvasPixelated;
4326
+ this.context.imageSmoothingEnabled = !tilesPixelated;
4297
4327
 
4298
4328
  // setup gl rendering if enabled
4299
4329
  glPreRender();
@@ -5114,7 +5144,7 @@ function glCreateTexture(image)
5114
5144
  }
5115
5145
 
5116
5146
  // use point filtering for pixelated rendering
5117
- const filter = canvasPixelated ? gl_NEAREST : gl_LINEAR;
5147
+ const filter = tilesPixelated ? gl_NEAREST : gl_LINEAR;
5118
5148
  glContext.texParameteri(gl_TEXTURE_2D, gl_TEXTURE_MIN_FILTER, filter);
5119
5149
  glContext.texParameteri(gl_TEXTURE_2D, gl_TEXTURE_MAG_FILTER, filter);
5120
5150
  return texture;
@@ -5263,7 +5293,7 @@ const engineName = 'LittleJS';
5263
5293
  * @type {String}
5264
5294
  * @default
5265
5295
  * @memberof Engine */
5266
- const engineVersion = '1.10.7';
5296
+ const engineVersion = '1.11.1';
5267
5297
 
5268
5298
  /** Frames per second to update
5269
5299
  * @type {Number}
@@ -5355,7 +5385,8 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
5355
5385
  mainCanvasSize = vec2(mainCanvas.width, mainCanvas.height);
5356
5386
 
5357
5387
  // disable smoothing for pixel art
5358
- mainContext.imageSmoothingEnabled = !canvasPixelated;
5388
+ overlayContext.imageSmoothingEnabled =
5389
+ mainContext.imageSmoothingEnabled = !tilesPixelated;
5359
5390
 
5360
5391
  // setup gl rendering if enabled
5361
5392
  glPreRender();
@@ -5508,6 +5539,7 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
5508
5539
  'align-items:center;' + // horizontal center
5509
5540
  'justify-content:center;' + // vertical center
5510
5541
  'background:#000;' + // set background color
5542
+ (canvasPixelated ? 'image-rendering:pixelated;' : '') + // pixel art
5511
5543
  'user-select:none;' + // prevent hold to select
5512
5544
  '-webkit-user-select:none;' + // compatibility for ios
5513
5545
  (!touchInputEnable ? '' : // no touch css setttings
@@ -1 +1 @@
1
- let showWatermark=0,debugKey="";const debug=0,debugOverlay=0,debugPhysics=0,debugParticles=0,debugRaycast=0,debugGamepads=0,debugMedals=0;function ASSERT(){}function debugInit(){}function debugUpdate(){}function debugRender(){}function debugRect(){}function debugPoly(){}function debugCircle(){}function debugPoint(){}function debugLine(){}function debugOverlap(){}function debugText(){}function debugClear(){}function debugSaveCanvas(){}function debugSaveText(){}function debugSaveDataURL(){}const PI=Math.PI;function abs(a){return Math.abs(a)}function min(a,b){return Math.min(a,b)}function max(a,b){return Math.max(a,b)}function sign(a){return Math.sign(a)}function mod(a,b=1){return(a%b+b)%b}function clamp(a,b=0,c=1){return a<b?b:a>c?c:a}function percent(a,b,c){return(c-=b)?clamp((a-b)/c):0}function lerp(a,b,c){return b+clamp(a)*(c-b)}function distanceWrap(a,b,c=1){a=(a-b)%c;return 2*a%c-a}function lerpWrap(a,b,c,d=1){return c+clamp(a)*distanceWrap(b,c,d)}function distanceAngle(a,b){return distanceWrap(a,b,2*PI)}function lerpAngle(a,b,c){return lerpWrap(a,b,c,2*PI)}function smoothStep(a){return a*a*(3-2*a)}function nearestPowerOfTwo(a){return 2**Math.ceil(Math.log2(a))}function isOverlapping(a,b,c,d=vec2()){return 2*abs(a.x-c.x)<b.x+d.x&&2*abs(a.y-c.y)<b.y+d.y}function isIntersecting(a,b,c,d){c=c.subtract(d.scale(.5));d=c.add(d);b=b.subtract(a);c=a.subtract(c);d=a.subtract(d);a=[-b.x,b.x,-b.y,b.y];b=[c.x,-d.x,c.y,-d.y];c=0;d=1;for(let e=4;e--;)if(a[e]){const f=b[e]/a[e];if(0>a[e]){if(f>d)return!1;c=max(f,c)}else{if(f<c)return!1;d=min(f,d)}}else if(0>b[e])return!1;return!0}function wave(a=1,b=1,c=time){return b/2*(1-Math.cos(c*a*2*PI))}function formatTime(a){return(a/60|0)+":"+(10>a%60?"0":"")+(a%60|0)}function rand(a=1,b=0){return b+Math.random()*(a-b)}function randInt(a,b=0){return Math.floor(rand(a,b))}function randSign(){return 2*randInt(2)-1}function randVector(a=1){return(new Vector2).setAngle(rand(2*PI),a)}function randInCircle(a=1,b=0){return 0<a?randVector(a*rand(b/a,1)**.5):new Vector2}function randColor(a=new Color,b=new Color(0,0,0,1),c=!1){return 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))}class RandomGenerator{constructor(a){this.seed=a}float(a=1,b=0){this.seed^=this.seed<<13;this.seed^=this.seed>>>17;this.seed^=this.seed<<5;return b+(a-b)*abs(this.seed%1e8)/1e8}int(a,b=0){return Math.floor(this.float(a,b))}sign(){return.5<this.float()?1:-1}}function vec2(a=0,b){return"number"==typeof a?new Vector2(a,void 0==b?a:b):new Vector2(a.x,a.y)}function isVector2(a){return a instanceof Vector2}class Vector2{constructor(a=0,b=0){this.x=a;this.y=b;ASSERT(this.isValid())}set(a=0,b=0){this.x=a;this.y=b;ASSERT(this.isValid());return this}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){ASSERT(isVector2(a));return this.distanceSquared(a)**.5}distanceSquared(a){ASSERT(isVector2(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)}setDirection(a,b=1){a=mod(a,4);ASSERT(0==a||1==a||2==a||3==a);return vec2(a%2?a-1?-b:b:0,a%2?0:a?-b: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){ASSERT(isVector2(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)} )`}isValid(){return"number"==typeof this.x&&!isNaN(this.x)&&"number"==typeof this.y&&!isNaN(this.y)}}function rgb(a,b,c,d){return new Color(a,b,c,d)}function hsl(a,b,c,d){return(new Color).setHSLA(a,b,c,d)}function isColor(a){return a instanceof Color}class Color{constructor(a=1,b=1,c=1,d=1){this.r=a;this.g=b;this.b=c;this.a=d;ASSERT(this.isValid())}set(a=1,b=1,c=1,d=1){this.r=a;this.g=b;this.b=c;this.a=d;ASSERT(this.isValid());return this}copy(){return new Color(this.r,this.g,this.b,this.a)}add(a){ASSERT(isColor(a));return new Color(this.r+a.r,this.g+a.g,this.b+a.b,this.a+a.a)}subtract(a){ASSERT(isColor(a));return new Color(this.r-a.r,this.g-a.g,this.b-a.b,this.a-a.a)}multiply(a){ASSERT(isColor(a));return new Color(this.r*a.r,this.g*a.g,this.b*a.b,this.a*a.a)}divide(a){ASSERT(isColor(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){ASSERT(isColor(a));return this.add(a.subtract(this).scale(clamp(b)))}setHSLA(a=0,b=0,c=1,d=1){a=mod(a,1);b=clamp(b);c=clamp(c);b=.5>c?c*(1+b):c+b-c*b;c=2*c-b;const e=(f,g,k)=>1>6*(k=mod(k,1))?f+6*(g-f)*k:1>2*k?g:2>3*k?f+(g-f)*(4-6*k):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;ASSERT(this.isValid());return this}HSLA(){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 m=e-f;h=.5<g?m/(2-e-f):m/(e+f);a==e?k=(b-c)/m+(b<c?6:0):b==e?k=(c-a)/m+2:c==e&&(k=(a-b)/m+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=!0){const b=c=>(16>(c=255*clamp(c)|0)?"0":"")+c.toString(16);return"#"+b(this.r)+b(this.g)+b(this.b)+(a?b(this.a):"")}setHex(a){ASSERT("string"==typeof a&&"#"==a[0]);ASSERT([4,5,7,9].includes(a.length),"Invalid hex");6>a.length?(this.r=clamp(parseInt(a[1],16)/15),this.g=clamp(parseInt(a[2],16)/15),this.b=clamp(parseInt(a[3],16)/15),this.a=5==a.length?clamp(parseInt(a[4],16)/15):1):(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=9==a.length?clamp(parseInt(a.slice(7,9),16)/255):1);ASSERT(this.isValid());return this}rgbaInt(){const a=255*clamp(this.r)|0,b=255*clamp(this.g)<<8,c=255*clamp(this.b)<<16,d=255*clamp(this.a)<<24;return a+b+c+d}isValid(){return"number"==typeof this.r&&!isNaN(this.r)&&"number"==typeof this.g&&!isNaN(this.g)&&"number"==typeof this.b&&!isNaN(this.b)&&"number"==typeof this.a&&!isNaN(this.a)}}const WHITE=rgb(),BLACK=rgb(0,0,0),GRAY=rgb(.5,.5,.5),RED=rgb(1,0,0),ORANGE=rgb(1,.5,0),YELLOW=rgb(1,1,0),GREEN=rgb(0,1,0),CYAN=rgb(0,1,1),BLUE=rgb(0,0,1),PURPLE=rgb(.5,0,1),MAGENTA=rgb(1,0,1);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.isSet()?Math.abs(this.get())+" seconds "+(0>this.get()?"before":"after"):"unset"}valueOf(){return this.get()}}let cameraPos=vec2(),cameraScale=32,canvasMaxSize=vec2(1920,1080),canvasFixedSize=vec2(),canvasPixelated=!0,fontDefault="arial",showSplashScreen=!1,headlessMode=!1,glEnable=!0,glOverlay=!0,tileSizeDefault=vec2(16),tileFixBleedScale=0,enablePhysicsSolver=!0,objectDefaultMass=1,objectDefaultDamping=1,objectDefaultAngleDamping=1,objectDefaultElasticity=0,objectDefaultFriction=.8,objectMaxSpeed=1,gravity=0,particleEmitRateScale=1,gamepadsEnable=!0,gamepadDirectionEmulateStick=!0,inputWASDEmulateDirection=!0,touchInputEnable=!0,touchGamepadEnable=!1,touchGamepadAnalog=!0,touchGamepadSize=99,touchGamepadAlpha=.3,vibrateEnable=!0,soundEnable=!0,soundVolume=.3,soundDefaultRange=40,soundDefaultTaper=.7,medalDisplayTime=5,medalDisplaySlideTime=.5,medalDisplaySize=vec2(640,80),medalDisplayIconSize=50,medalsPreventUnlock=!1;function setCameraPos(a){cameraPos=a}function setCameraScale(a){cameraScale=a}function setCanvasMaxSize(a){canvasMaxSize=a}function setCanvasFixedSize(a){canvasFixedSize=a}function setCanvasPixelated(a){canvasPixelated=a}function setFontDefault(a){fontDefault=a}function setShowSplashScreen(a){showSplashScreen=a}function setHeadlessMode(a){headlessMode=a}function setGlEnable(a){glEnable=a}function setGlOverlay(a){glOverlay=a}function setTileSizeDefault(a){tileSizeDefault=a}function setTileFixBleedScale(a){tileFixBleedScale=a}function setEnablePhysicsSolver(a){enablePhysicsSolver=a}function setObjectDefaultMass(a){objectDefaultMass=a}function setObjectDefaultDamping(a){objectDefaultDamping=a}function setObjectDefaultAngleDamping(a){objectDefaultAngleDamping=a}function setObjectDefaultElasticity(a){objectDefaultElasticity=a}function setObjectDefaultFriction(a){objectDefaultFriction=a}function setObjectMaxSpeed(a){objectMaxSpeed=a}function setGravity(a){gravity=a}function setParticleEmitRateScale(a){particleEmitRateScale=a}function setGamepadsEnable(a){gamepadsEnable=a}function setGamepadDirectionEmulateStick(a){gamepadDirectionEmulateStick=a}function setInputWASDEmulateDirection(a){inputWASDEmulateDirection=a}function setTouchInputEnable(a){touchInputEnable=a}function setTouchGamepadEnable(a){touchGamepadEnable=a}function setTouchGamepadAnalog(a){touchGamepadAnalog=a}function setTouchGamepadSize(a){touchGamepadSize=a}function setTouchGamepadAlpha(a){touchGamepadAlpha=a}function setVibrateEnable(a){vibrateEnable=a}function setSoundEnable(a){soundEnable=a}function setSoundVolume(a){soundVolume=a;soundEnable&&!headlessMode&&audioGainNode&&(audioGainNode.gain.value=a)}function setSoundDefaultRange(a){soundDefaultRange=a}function setSoundDefaultTaper(a){soundDefaultTaper=a}function setMedalDisplayTime(a){medalDisplayTime=a}function setMedalDisplaySlideTime(a){medalDisplaySlideTime=a}function setMedalDisplaySize(a){medalDisplaySize=a}function setMedalDisplayIconSize(a){medalDisplayIconSize=a}function setMedalsPreventUnlock(a){medalsPreventUnlock=a}function setShowWatermark(a){showWatermark=a}function setDebugKey(a){debugKey=a}class EngineObject{constructor(a=vec2(),b=vec2(1),c,d=0,e=new Color,f=0){ASSERT(isVector2(a)&&isVector2(b),"ensure pos and size are vec2s");ASSERT("number"!==typeof c||!c,"old style tile setup");this.pos=a.copy();this.size=b;this.drawSize=void 0;this.tileInfo=c;this.angle=d;this.color=e;this.additiveColor=void 0;this.mirror=!1;this.mass=objectDefaultMass;this.damping=objectDefaultDamping;this.angleDamping=objectDefaultAngleDamping;this.elasticity=objectDefaultElasticity;this.friction=objectDefaultFriction;this.gravityScale=1;this.renderOrder=f;this.velocity=vec2();this.angleVelocity=0;this.spawnTime=time;this.children=[];this.clampSpeedLinear=!0;this.parent=void 0;this.localPos=vec2();this.localAngle=0;this.collideRaycast=this.isSolid=this.collideSolidObjects=this.collideTiles=!1;engineObjects.push(this)}updateTransforms(){const a=this.parent;if(a){const b=a.getMirrorSign();this.pos=this.localPos.multiply(vec2(b,1)).rotate(-a.angle).add(a.pos);this.angle=b*this.localAngle+a.angle}for(const b of this.children)b.updateTransforms()}update(){if(!this.parent){if(this.clampSpeedLinear)this.velocity.x=clamp(this.velocity.x,-objectMaxSpeed,objectMaxSpeed),this.velocity.y=clamp(this.velocity.y,-objectMaxSpeed,objectMaxSpeed);else{var a=this.velocity.lengthSquared();a>objectMaxSpeed*objectMaxSpeed&&(a=objectMaxSpeed/a**.5,this.velocity.x*=a,this.velocity.y*=a)}a=this.pos.copy();this.velocity.x*=this.damping;this.velocity.y*=this.damping;this.mass&&(this.velocity.y+=gravity*this.gravityScale);this.pos.x+=this.velocity.x;this.pos.y+=this.velocity.y;this.angle+=this.angleVelocity*=this.angleDamping;ASSERT(0<=this.angleDamping&&1>=this.angleDamping);ASSERT(0<=this.damping&&1>=this.damping);if(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)continue;if(!isOverlapping(this.pos,this.size,d.pos,d.size))continue;c=this.collideWithObject(d);var e=d.collideWithObject(this);if(!c||!e)continue;if(isOverlapping(a,this.size,d.pos,d.size)){c=a.subtract(d.pos);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&&debugOverlap(this.pos,this.size,d.pos,d.size,"#f00");continue}e=this.size.add(d.size);var f=2*(a.y-d.pos.y)>e.y+gravity;const k=2*abs(a.y-d.pos.y)<e.y;var g=2*abs(a.x-d.pos.x)<e.x;c=max(this.elasticity,d.elasticity);if(f||g||!k)if(this.pos.y=d.pos.y+(e.y/2+.001)*sign(a.y-d.pos.y),d.groundObject&&b||!d.mass)b&&(this.groundObject=d),this.velocity.y*=-c;else if(d.mass){g=(this.mass*this.velocity.y+d.mass*d.velocity.y)/(this.mass+d.mass);const h=d.velocity.y*(d.mass-this.mass)/(this.mass+d.mass)+2*this.velocity.y*this.mass/(this.mass+d.mass);this.velocity.y=lerp(c,g,this.velocity.y*(this.mass-d.mass)/(this.mass+d.mass)+2*d.velocity.y*d.mass/(this.mass+d.mass));d.velocity.y=lerp(c,g,h)}!f&&k&&(this.pos.x=d.pos.x+(e.x/2+.001)*sign(a.x-d.pos.x),d.mass?(e=(this.mass*this.velocity.x+d.mass*d.velocity.x)/(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),this.velocity.x=lerp(c,e,this.velocity.x*(this.mass-d.mass)/(this.mass+d.mass)+2*d.velocity.x*d.mass/(this.mass+d.mass)),d.velocity.x=lerp(c,e,f)):this.velocity.x*=-c);debugOverlay&&debugPhysics&&debugOverlap(this.pos,this.size,d.pos,d.size,"#f0f")}if(this.collideTiles&&tileCollisionTest(this.pos,this.size,this)&&!tileCollisionTest(a,this.size,this)){d=tileCollisionTest(vec2(a.x,this.pos.y),this.size,this);c=tileCollisionTest(vec2(this.pos.x,a.y),this.size,this);if(d||!c)this.velocity.y*=-this.elasticity,(this.groundObject=b)?this.pos.y=(a.y-this.size.y/2|0)+this.size.y/2+1e-4:this.pos.y=a.y;c&&(this.pos.x=a.x,this.velocity.x*=-this.elasticity);debugOverlay&&debugPhysics&&debugRect(this.pos,this.size,"#f00")}}}}render(){drawTile(this.pos,this.drawSize||this.size,this.tileInfo,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)}}localToWorld(a){return this.pos.add(a.rotate(-this.angle))}worldToLocal(a){return a.subtract(this.pos).rotate(this.angle)}localToWorldVector(a){return a.rotate(this.angle)}worldToLocalVector(a){return a.rotate(-this.angle)}collideWithTile(a,b){return 0<a}collideWithObject(a){return!0}getAliveTime(){return time-this.spawnTime}applyAcceleration(a){this.mass&&(this.velocity=this.velocity.add(a))}applyForce(a){this.applyAcceleration(a.scale(1/this.mass))}getMirrorSign(){return this.mirror?-1:1}addChild(a,b=vec2(),c=0){ASSERT(!a.parent&&!this.children.includes(a));this.children.push(a);a.parent=this;a.localPos=b.copy();a.localAngle=c}removeChild(a){ASSERT(a.parent==this&&this.children.includes(a));this.children.splice(this.children.indexOf(a),1);a.parent=0}setCollision(a=!0,b=!0,c=!0,d=!0){ASSERT(a||!b,"solid objects must be set to collide");this.collideSolidObjects=a;this.isSolid=b;this.collideTiles=c;this.collideRaycast=d}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}}renderDebugInfo(){if(debug){const a=vec2(max(this.size.x,.2),max(this.size.y,.2)),b=rgb(this.collideTiles?1:0,this.collideSolidObjects?1:0,this.isSolid?1:0,this.parent?.2:.5),c=this.parent?rgb(1,1,1,.5):rgb(0,0,0,.8);drawRect(this.pos,a,b,this.angle,!1);drawRect(this.pos,a.scale(.8),c,this.angle,!1);this.parent&&drawLine(this.pos,this.parent.pos,.1,rgb(0,0,1,.5),!1)}}}let mainCanvas,mainContext,overlayCanvas,overlayContext,mainCanvasSize=vec2(),textureInfos=[],drawCount;function tile(a=vec2(),b=tileSizeDefault,c=0,d=0){if(headlessMode)return new TileInfo;"number"===typeof b&&(ASSERT(0<b),b=vec2(b));var e=textureInfos[c];ASSERT(e,"Texture not loaded");const f=b.add(vec2(2*d));e=e.size.x/f.x|0;"number"===typeof a&&(a=vec2(a%e,a/e|0));a=vec2(a.x*f.x+d,a.y*f.y+d);return new TileInfo(a,b,c,d)}class TileInfo{constructor(a=vec2(),b=tileSizeDefault,c=0,d=0){this.pos=a.copy();this.size=b.copy();this.textureIndex=c;this.padding=d}offset(a){return new TileInfo(this.pos.add(a),this.size,this.textureIndex)}frame(a){ASSERT("number"==typeof a);return this.offset(vec2(a*(this.size.x+2*this.padding),0))}getTextureInfo(){return textureInfos[this.textureIndex]}}class TextureInfo{constructor(a){this.image=a;this.size=vec2(a.width,a.height);this.glTexture=glEnable&&glCreateTexture(a)}}function screenToWorld(a){return new Vector2((a.x-mainCanvasSize.x/2+.5)/cameraScale+cameraPos.x,(a.y-mainCanvasSize.y/2+.5)/-cameraScale+cameraPos.y)}function worldToScreen(a){return new Vector2((a.x-cameraPos.x)*cameraScale+mainCanvasSize.x/2-.5,(a.y-cameraPos.y)*-cameraScale+mainCanvasSize.y/2-.5)}function getCameraSize(){return mainCanvasSize.scale(1/cameraScale)}function drawTile(a,b=vec2(1),c,d=new Color,e=0,f,g=new Color(0,0,0,0),k=glEnable,h,m){ASSERT(!m||!k,"context only supported in canvas 2D mode");ASSERT("number"!==typeof c||!c,"this is an old style calls, to fix replace it with tile(tileIndex, tileSize)");const n=c&&c.getTextureInfo();if(k)if(h&&(a=screenToWorld(a),b=b.scale(1/cameraScale)),n){var l=vec2(1).divide(n.size);k=c.pos.x*l.x;h=c.pos.y*l.y;m=c.size.x*l.x;const p=c.size.y*l.y;l=l.scale(tileFixBleedScale);glSetTexture(n.glTexture);glDraw(a.x,a.y,f?-b.x:b.x,b.y,e,k+l.x,h+l.y,k-l.x+m,h-l.y+p,d.rgbaInt(),g.rgbaInt())}else glDraw(a.x,a.y,b.x,b.y,e,0,0,0,0,0,d.rgbaInt());else showWatermark&&++drawCount,b=vec2(b.x,-b.y),drawCanvas2D(a,b,e,f,p=>{if(n){const q=c.pos.x+tileFixBleedScale,r=c.pos.y+tileFixBleedScale,x=c.size.x-2*tileFixBleedScale,v=c.size.y-2*tileFixBleedScale;p.globalAlpha=d.a;p.drawImage(n.image,q,r,x,v,-.5,-.5,1,1);p.globalAlpha=1}else p.fillStyle=d,p.fillRect(-.5,-.5,1,1)},h,m)}function drawRect(a,b,c,d,e,f,g){drawTile(a,b,void 0,c,d,!1,void 0,e,f,g)}function drawLine(a,b,c=.1,d,e,f,g){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,f,g)}function drawPoly(a,b=new Color,c=0,d=new Color(0,0,0),e,f=mainContext){f.fillStyle=b.toString();f.beginPath();for(const g of e?a:a.map(worldToScreen))f.lineTo(g.x,g.y);f.closePath();f.fill();c&&(f.strokeStyle=d.toString(),f.lineWidth=e?c:c*cameraScale,f.stroke())}function drawEllipse(a,b=1,c=1,d=0,e=new Color,f=0,g=new Color(0,0,0),k,h=mainContext){k||(a=worldToScreen(a),b*=cameraScale,c*=cameraScale,f*=cameraScale);h.fillStyle=e.toString();h.beginPath();h.ellipse(a.x,a.y,b,c,d,0,9);h.fill();f&&(h.strokeStyle=g.toString(),h.lineWidth=f,h.stroke())}function drawCircle(a,b=1,c=new Color,d=0,e=new Color(0,0,0),f,g=mainContext){drawEllipse(a,b,b,0,c,d,e,f,g)}function drawCanvas2D(a,b,c,d,e,f,g=mainContext){f||(a=worldToScreen(a),b=b.scale(cameraScale));g.save();g.translate(a.x+.5,a.y+.5);g.rotate(c);g.scale(d?-b.x:b.x,-b.y);e(g);g.restore()}function setBlendMode(a,b=glEnable,c){ASSERT(!c||!b,"context only supported in canvas 2D mode");b?glAdditive=a:(c||=mainContext,c.globalCompositeOperation=a?"lighter":"source-over")}function drawText(a,b,c=1,d,e=0,f,g,k,h,m){drawTextScreen(a,worldToScreen(b),c*cameraScale,d,e*cameraScale,f,g,k,h,m)}function drawTextScreen(a,b,c=1,d=new Color,e=0,f=new Color(0,0,0),g="center",k=fontDefault,h=overlayContext,m){h.fillStyle=d.toString();h.lineWidth=e;h.strokeStyle=f.toString();h.textAlign=g;h.font=c+"px "+k;h.textBaseline="middle";h.lineJoin="round";b=b.copy();(a+"").split("\n").forEach(n=>{e&&h.strokeText(n,b.x,b.y,m);h.fillText(n,b.x,b.y,m);b.y+=c})}let engineFontImage;class FontImage{constructor(a,b=vec2(8),c=vec2(0,1),d=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.context=d}drawText(a,b,c=1,d){this.drawTextScreen(a,worldToScreen(b).floor(),c*cameraScale|0,d)}drawTextScreen(a,b,c=4,d){const e=this.context;e.save();e.imageSmoothingEnabled=!canvasPixelated;const f=this.tileSize,g=f.add(this.paddingSize).scale(c),k=this.image.width/this.tileSize.x|0;(a+"").split("\n").forEach((h,m)=>{const n=d?h.length*f.x*c/2|0:0;for(let q=h.length;q--;){var l=h[q].charCodeAt(0);if(32>l||127<l)l=127;var p=l-32;l=p%k;p=p/k|0;const r=b.add(vec2(q,m).multiply(g));e.drawImage(this.image,l*f.x,p*f.y,f.x,f.y,r.x-n,r.y,f.x*c,f.y*c)}});e.restore()}}function isFullscreen(){return!!document.fullscreenElement}function toggleFullscreen(){const a=mainCanvas.parentElement;isFullscreen()?document.exitFullscreen&&document.exitFullscreen():a.requestFullscreen&&a.requestFullscreen()}function keyIsDown(a,b=0){ASSERT(0<b||"number"!==typeof a||3>a,"use code string for keyboard");return inputData[b]&&!!(inputData[b][a]&1)}function keyWasPressed(a,b=0){ASSERT(0<b||"number"!==typeof a||3>a,"use code string for keyboard");return inputData[b]&&!!(inputData[b][a]&2)}function keyWasReleased(a,b=0){ASSERT(0<b||"number"!==typeof a||3>a,"use code string for keyboard");return inputData[b]&&!!(inputData[b][a]&4)}function clearInput(){inputData=[[]];touchGamepadButtons=[]}const mouseIsDown=keyIsDown,mouseWasPressed=keyWasPressed,mouseWasReleased=keyWasReleased;let mousePos=vec2(),mousePosScreen=vec2(),mouseWheel=0,isUsingGamepad=!1,preventDefaultInput=!1;function gamepadIsDown(a,b=0){return keyIsDown(a,b+1)}function gamepadWasPressed(a,b=0){return keyWasPressed(a,b+1)}function gamepadWasReleased(a,b=0){return keyWasReleased(a,b+1)}function gamepadStick(a,b=0){return gamepadStickData[b]?gamepadStickData[b][a]||vec2():vec2()}let inputData=[[]];function inputUpdate(){headlessMode||(touchInputEnable&&isTouchDevice||document.hasFocus()||clearInput(),mousePos=screenToWorld(mousePosScreen),gamepadsUpdate())}function inputUpdatePost(){if(!headlessMode){for(const a of inputData)for(const b in a)a[b]&=1;mouseWheel=0}}function inputInit(){function a(b){return inputWASDEmulateDirection?"KeyW"==b?"ArrowUp":"KeyS"==b?"ArrowDown":"KeyA"==b?"ArrowLeft":"KeyD"==b?"ArrowRight":b:b}headlessMode||(onkeydown=b=>{b.repeat||(isUsingGamepad=!1,inputData[0][b.code]=3,inputWASDEmulateDirection&&(inputData[0][a(b.code)]=3));preventDefaultInput&&b.preventDefault()},onkeyup=b=>{inputData[0][b.code]=4;inputWASDEmulateDirection&&(inputData[0][a(b.code)]=4)},onmousedown=b=>{soundEnable&&!headlessMode&&audioContext&&"running"!=audioContext.state&&audioContext.resume();isUsingGamepad=!1;inputData[0][b.button]=3;mousePosScreen=mouseToScreen(b);b.button&&b.preventDefault()},onmouseup=b=>inputData[0][b.button]=inputData[0][b.button]&2|4,onmousemove=b=>mousePosScreen=mouseToScreen(b),onwheel=b=>mouseWheel=b.ctrlKey?0:sign(b.deltaY),oncontextmenu=b=>!1,onblur=b=>clearInput(),isTouchDevice&&touchInputEnable&&touchInputInit())}function mouseToScreen(a){if(!mainCanvas||headlessMode)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)))}const gamepadStickData=[];function gamepadsUpdate(){const a=g=>{const k=h=>.3<h?percent(h,.3,.8):-.3>h?-percent(-h,.3,.8):0;return vec2(k(g.x),k(-g.y)).clampLength()};if(touchGamepadEnable&&isTouchDevice&&(ASSERT(touchGamepadButtons,"set touchGamepadEnable before calling init!"),touchGamepadTimer.isSet())){var b=gamepadStickData[0]||(gamepadStickData[0]=[]);b[0]=vec2();touchGamepadAnalog?b[0]=a(touchGamepadStick):.3<touchGamepadStick.lengthSquared()&&(b[0].x=Math.round(touchGamepadStick.x),b[0].y=-Math.round(touchGamepadStick.y),b[0]=b[0].clampLength());b=inputData[1]||(inputData[1]=[]);for(var c=10;c--;){var d=3==c?2:2==c?3:c,e=gamepadIsDown(d,0);b[d]=touchGamepadButtons[c]?e?1:3:e?4:0}}if(gamepadsEnable&&navigator&&navigator.getGamepads&&(debug||document.hasFocus()))for(b=navigator.getGamepads(),c=b.length;c--;){e=b[c];const g=inputData[c+1]||(inputData[c+1]=[]);d=gamepadStickData[c]||(gamepadStickData[c]=[]);if(e){for(var f=0;f<e.axes.length-1;f+=2)d[f>>1]=a(vec2(e.axes[f],e.axes[f+1]));for(f=e.buttons.length;f--;){const k=e.buttons[f],h=gamepadIsDown(f,c);g[f]=k.pressed?h?1:3:h?4:0;isUsingGamepad||=!c&&k.pressed}gamepadDirectionEmulateStick&&(e=vec2((gamepadIsDown(15,c)&&1)-(gamepadIsDown(14,c)&&1),(gamepadIsDown(12,c)&&1)-(gamepadIsDown(13,c)&&1)),e.lengthSquared()&&(d[0]=e.clampLength()));touchGamepadEnable&&isUsingGamepad&&touchGamepadTimer.unset()}}}function vibrate(a=100){vibrateEnable&&!headlessMode&&navigator&&navigator.vibrate&&navigator.vibrate(a)}function vibrateStop(){vibrate(0)}const isTouchDevice=void 0!==window.ontouchstart;let touchGamepadTimer=new Timer,touchGamepadButtons,touchGamepadStick;function touchInputInit(){function a(e){soundEnable&&!headlessMode&&audioContext&&"running"!=audioContext.state&&audioContext.resume();const f=e.touches.length;if(f){const g=vec2(e.touches[0].clientX,e.touches[0].clientY);mousePosScreen=mouseToScreen(g);d?isUsingGamepad=touchGamepadEnable:inputData[0][0]=3}else d&&(inputData[0][0]=inputData[0][0]&2|4);d=f;document.hasFocus()&&e.preventDefault();return!0}function b(e){touchGamepadStick=vec2();touchGamepadButtons=[];isUsingGamepad=!0;if(e.touches.length&&(touchGamepadTimer.set(),paused&&!d)){touchGamepadButtons[9]=1;a(e);return}const f=vec2(touchGamepadSize,mainCanvasSize.y-touchGamepadSize),g=mainCanvasSize.subtract(vec2(touchGamepadSize,touchGamepadSize)),k=mainCanvasSize.scale(.5);for(const m of e.touches){var h=mouseToScreen(vec2(m.clientX,m.clientY));h.distance(f)<touchGamepadSize?touchGamepadStick=h.subtract(f).scale(2/touchGamepadSize).clampLength():h.distance(g)<touchGamepadSize?(h=h.subtract(g).direction(),touchGamepadButtons[h]=1):h.distance(k)<touchGamepadSize&&!d&&(touchGamepadButtons[9]=1)}a(e);return!0}let c=a;touchGamepadEnable&&(c=b,touchGamepadButtons=[],touchGamepadStick=vec2());document.addEventListener("touchstart",e=>c(e),{passive:!1});document.addEventListener("touchmove",e=>c(e),{passive:!1});document.addEventListener("touchend",e=>c(e),{passive:!1});onmousedown=onmouseup=()=>0;let d}function touchGamepadRender(){if(touchInputEnable&&isTouchDevice&&!headlessMode&&touchGamepadEnable&&touchGamepadTimer.isSet()){var a=percent(touchGamepadTimer.get(),4,3);if(a&&!paused){var b=overlayContext;b.save();b.globalAlpha=a*touchGamepadAlpha;b.strokeStyle="#fff";b.lineWidth=3;b.fillStyle=0<touchGamepadStick.lengthSquared()?"#fff":"#000";b.beginPath();a=vec2(touchGamepadSize,mainCanvasSize.y-touchGamepadSize);if(touchGamepadAnalog)b.arc(a.x,a.y,touchGamepadSize/2,0,9),b.fill();else for(var c=10;c--;){var d=c*PI/4;b.arc(a.x,a.y,.6*touchGamepadSize,d+PI/8,d+PI/8);c%2&&b.arc(a.x,a.y,.33*touchGamepadSize,d,d);1==c&&b.fill()}b.stroke();a=vec2(mainCanvasSize.x-touchGamepadSize,mainCanvasSize.y-touchGamepadSize);for(c=4;c--;)d=a.add(vec2().setDirection(c,touchGamepadSize/2)),b.fillStyle=touchGamepadButtons[c]?"#fff":"#000",b.beginPath(),b.arc(d.x,d.y,touchGamepadSize/4,0,9),b.fill(),b.stroke();b.restore()}}}let audioContext=new AudioContext,audioGainNode;function audioInit(){soundEnable&&!headlessMode&&(audioGainNode=audioContext.createGain(),audioGainNode.connect(audioContext.destination),audioGainNode.gain.value=soundVolume)}class Sound{constructor(a,b=soundDefaultRange,c=soundDefaultTaper){soundEnable&&!headlessMode&&(this.range=b,this.taper=c,this.randomness=0,this.gainNode=audioContext.createGain(),a&&(this.randomness=void 0!=a[1]?a[1]:.05,a[1]=0,this.sampleChannels=[zzfxG(...a)],this.sampleRate=zzfxR))}play(a,b=1,c=1,d=1,e=!1){if(soundEnable&&!headlessMode&&this.sampleChannels){var f;if(a){if(f=this.range){const g=cameraPos.distanceSquared(a);if(g>f*f)return;b*=percent(g**.5,f,f*this.taper)}f=2*worldToScreen(a).x/mainCanvas.width-1}a=c+c*this.randomness*d*rand(-1,1);return this.source=playSamples(this.sampleChannels,b,a,f,e,this.sampleRate,this.gainNode)}}setVolume(a=1){this.gainNode.gain.value=a}stop(){this.source&&this.source.stop();this.source=void 0}getSource(){return this.source}playNote(a,b,c){return this.play(b,c,2**(a/12),0)}getDuration(){return this.sampleChannels&&this.sampleChannels[0].length/this.sampleRate}isLoading(){return!this.sampleChannels}}class SoundWave extends Sound{constructor(a,b=0,c,d,e){super(void 0,c,d);soundEnable&&!headlessMode&&(this.randomness=b,fetch(a).then(f=>f.arrayBuffer()).then(f=>audioContext.decodeAudioData(f)).then(f=>{this.sampleChannels=[];for(let g=f.numberOfChannels;g--;)this.sampleChannels[g]=Array.from(f.getChannelData(g));this.sampleRate=f.sampleRate}).then(()=>e&&e(this)))}}function playAudioFile(a,b=1,c=!1){if(soundEnable&&!headlessMode)return new SoundWave(a,0,0,0,d=>d.play(void 0,b,1,1,c))}class Music extends Sound{constructor(a){super(void 0);soundEnable&&!headlessMode&&(this.randomness=0,this.sampleChannels=zzfxM(...a),this.sampleRate=zzfxR)}playMusic(a,b=!1){return super.play(void 0,a,1,1,b)}}function speak(a,b="",c=1,d=1,e=1){if(soundEnable&&!headlessMode&&speechSynthesis)return a=new SpeechSynthesisUtterance(a),a.lang=b,a.volume=2*c*soundVolume,a.rate=d,a.pitch=e,speechSynthesis.speak(a),a}function speakStop(){speechSynthesis&&speechSynthesis.cancel()}function getNoteFrequency(a,b=220){return b*2**(a/12)}function playSamples(a,b=1,c=1,d=0,e=!1,f=zzfxR,g){if(soundEnable&&!headlessMode){var k=audioContext.createBuffer(a.length,a[0].length,f),h=audioContext.createBufferSource();a.forEach((m,n)=>k.getChannelData(n).set(m));h.buffer=k;h.playbackRate.value=c;h.loop=e;g=g||audioContext.createGain();g.gain.value=b;g.connect(audioGainNode);a=new StereoPannerNode(audioContext,{pan:clamp(d,-1,1)});h.connect(a).connect(g);"running"!=audioContext.state?audioContext.resume().then(()=>h.start()):h.start();return h}}function zzfx(...a){return playSamples([zzfxG(...a)])}const zzfxR=44100;function zzfxG(a=1,b=.05,c=220,d=0,e=0,f=.1,g=0,k=1,h=0,m=0,n=0,l=0,p=0,q=0,r=0,x=0,v=0,D=1,z=0,E=0,A=0){let w=2*PI;var t=zzfxR;let F=h*=500*w/t/t;b=c*=rand(1+b,1-b)*w/t;let C=[],y=0,G=0,u=0,H=1,R=0,S=0,B=0,J;var L=w*abs(A)*2/t,K=Math.cos(L),M=Math.sin(L)/2/2,I=1+M;L=-2*K/I;M=(1-M)/I;let N=(1+sign(A)*K)/2/I;K=-(sign(A)+K)/I;let O=I=0,P=0,Q=0;d=d*t+9;z*=t;e*=t;f*=t;v*=t;m*=500*w/t**3;r*=w/t;n*=w/t;l*=t;p=p*t|0;for(J=d+z+e+f+v|0;u<J;C[u++]=B*a)++S%(100*x|0)||(B=g?1<g?2<g?3<g?Math.sin(y**3):clamp(Math.tan(y),1,-1):1-(2*y/w%2+2)%2:1-4*abs(Math.round(y/w)-y/w):Math.sin(y),B=(p?1-E+E*Math.sin(w*u/p):1)*sign(B)*abs(B)**k*(u<d?u/d:u<d+z?1-(u-d)/z*(1-D):u<d+z+e?D:u<J-v?(J-u-v)/f*D:0),B=v?B/2+(v>u?0:(u<J-v?1:(J-u)/v)*C[u-v|0]/2/a):B,A&&(B=Q=N*I+K*(I=O)+N*(O=B)-M*P-L*(P=Q))),t=(c+=h+=m)*Math.cos(r*G++),y+=t+t*q*Math.sin(u**5),H&&++H>l&&(c+=n,b+=n,H=0),!p||++R%p||(c=b,h=F,H=H||1);return C}function zzfxM(a,b,c,d=125){let e,f,g,k,h,m,n,l,p,q,r,x,v,D=0,z,E=[],A=[],w=[],t=0,F=0,C=1,y={},G=zzfxR/d*60>>2;for(;C;t++)E=[C=l=x=0],c.forEach((u,H)=>{n=b[u][t]||[0,0,0];C|=b[u][t]&&1;z=x+(b[u][0].length-2-(l?0:1))*G;v=H==c.length-1;e=2;for(g=x;e<n.length+v;l=++e){h=n[e];p=e==n.length+v-1&&v||q!=(n[0]||0)||h|0;for(f=0;f<G&&l;f++>G-99&&p&&1>r?r+=1/99:0)m=(1-r)*E[D++]/2||0,A[g]=(A[g]||0)-m*F+m,w[g]=(w[g++]||0)+m*F+m;h&&(r=h%1,F=n[1]||0,h|=0)&&(E=y[[q=n[D=0]||0,h]]=y[[q,h]]||(k=[...a[q]],k[2]*=2**((h-12)/12),0<h?zzfxG(...k):[]))}x=z});return[A,w]}let tileCollision=[],tileCollisionSize=vec2();function initTileCollision(a){tileCollisionSize=a;tileCollision=[];for(a=tileCollision.length=tileCollisionSize.area();a--;)tileCollision[a]=0}function setTileCollisionData(a,b=0){a.arrayCheck(tileCollisionSize)&&(tileCollision[(a.y|0)*tileCollisionSize.x+a.x|0]=b)}function getTileCollisionData(a){return 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,vec2(b,e))))return!0}return!1}function tileCollisionRaycast(a,b,c){const d=b.subtract(a),e=d.length();var f=d.normalize();f=vec2(abs(1/f.x),abs(1/f.y));let g=a.floor(),k=f.x*(0>d.x?a.x-g.x:g.x-a.x+1),h=f.y*(0>d.y?a.y-g.y:g.y-a.y+1);for(;;){const m=getTileCollisionData(g);if(m&&(!c||c.collideWithTile(m,g)))return debugRaycast&&debugLine(a,b,"#f00",.02),debugRaycast&&debugPoint(g.add(vec2(.5)),"#ff0"),g.add(vec2(.5));if(k>e&&h>e)break;k>h?(g.y+=sign(d.y),h+=f.y):(g.x+=sign(d.x),k+=f.x)}debugRaycast&&debugLine(a,b,"#00f",.02)}class TileLayerData{constructor(a,b=0,c=!1,d=new Color){this.tile=a;this.direction=b;this.mirror=c;this.color=d}clear(){this.tile=this.direction=0;this.mirror=!1;this.color=new Color}}class TileLayer extends EngineObject{constructor(a,b=tileCollisionSize,c=tile(),d=vec2(1),e=0){super(a,b,c,0,void 0,e);this.canvas=document.createElement("canvas");this.context=this.canvas.getContext("2d");this.scale=d;this.isOverlay=!1;this.data=[];for(a=this.size.area();a--;)this.data.push(new TileLayerData);headlessMode&&(this.redraw=()=>{},this.render=()=>{},this.redrawStart=()=>{},this.redrawEnd=()=>{},this.drawTileData=()=>{},this.drawCanvas2D=()=>{})}setData(a,b,c=!1){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,"must call redrawEnd() after drawing tiles");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(!0);for(let a=this.size.x;a--;)for(let b=this.size.y;b--;)this.drawTileData(vec2(a,b),!1);this.redrawEnd()}redrawStart(a=!1){this.savedRenderSettings=[mainCanvas,mainContext,mainCanvasSize,cameraPos,cameraScale];mainCanvas=this.canvas;mainContext=this.context;mainCanvasSize=this.size.multiply(this.tileInfo.size);cameraPos=this.size.scale(.5);cameraScale=this.tileInfo.size.x;a&&(mainCanvas.width=mainCanvasSize.x,mainCanvas.height=mainCanvasSize.y);this.context.imageSmoothingEnabled=!canvasPixelated;glPreRender()}redrawEnd(){ASSERT(mainContext==this.context,"must call redrawStart() before drawing tiles");glCopyToContext(mainContext,!0);[mainCanvas,mainContext,mainCanvasSize,cameraPos,cameraScale]=this.savedRenderSettings}drawTileData(a,b=!0){var c=this.tileInfo.size;b&&(b=a.multiply(c),this.context.clearRect(b.x,this.canvas.height-b.y,c.x,-c.y));b=this.getData(a);void 0!=b.tile&&(ASSERT(mainContext==this.context,"must call redrawStart() before drawing tiles"),a=a.add(vec2(.5)),c=tile(b.tile,c,this.tileInfo.textureIndex),drawTile(a,vec2(1),c,b.color,b.direction*PI/2,b.mirror))}drawCanvas2D(a,b,c,d,e){const f=this.context;f.save();a=a.subtract(this.pos).multiply(this.tileInfo.size);b=b.multiply(this.tileInfo.size);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,d=new Color,e,f){this.drawCanvas2D(a,b,e,f,g=>{const k=c&&c.getTextureInfo();k?(g.globalAlpha=d.a,g.drawImage(k.image,c.pos.x,c.pos.y,c.size.x,c.size.y,-.5,-.5,1,1),g.globalAlpha=1):(g.fillStyle=d,g.fillRect(-.5,-.5,1,1))})}drawRect(a,b,c,d){this.drawTile(a,b,void 0,c,d)}}class ParticleEmitter extends EngineObject{constructor(a,b,c=0,d=0,e=100,f=PI,g,k=new Color,h=new Color,m=new Color(1,1,1,0),n=new Color(1,1,1,0),l=.5,p=.1,q=1,r=.1,x=.05,v=1,D=1,z=0,E=PI,A=.1,w=.2,t=!1,F=!1,C=!0,y=F?1e9:0,G=!1){super(a,vec2(),g,b,void 0,y);this.emitSize=c;this.emitTime=d;this.emitRate=e;this.emitConeAngle=f;this.colorStartA=k;this.colorStartB=h;this.colorEndA=m;this.colorEndB=n;this.randomColorLinear=C;this.particleTime=l;this.sizeStart=p;this.sizeEnd=q;this.speed=r;this.angleSpeed=x;this.damping=v;this.angleDamping=D;this.gravityScale=z;this.particleConeAngle=E;this.fadeRate=A;this.randomness=w;this.collideTiles=t;this.additive=F;this.localSpace=G;this.trailScale=0;this.particleCreateCallback=this.particleDestroyCallback=void 0;this.emitTimeBuffer=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="number"===typeof this.emitSize?randInCircle(this.emitSize/2):vec2(rand(-.5,.5),rand(-.5,.5)).multiply(this.emitSize).rotate(this.angle);let b=rand(this.particleConeAngle,-this.particleConeAngle);this.localSpace||(a=this.pos.add(a),b+=this.angle);const c=this.randomness;var d=l=>l+l*rand(c,-c);const e=d(this.particleTime),f=d(this.sizeStart),g=d(this.sizeEnd),k=d(this.speed);d=d(this.angleSpeed)*randSign();var h=rand(this.emitConeAngle,-this.emitConeAngle);const m=randColor(this.colorStartA,this.colorStartB,this.randomColorLinear),n=randColor(this.colorEndA,this.colorEndB,this.randomColorLinear);h=this.localSpace?h:this.angle+h;a=new Particle(a,this.tileInfo,b,m,n,e,f,g,this.fadeRate,this.additive,this.trailScale,this.localSpace&&this,this.particleDestroyCallback);a.velocity=vec2().setAngle(h,k);a.angleVelocity=d;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.renderOrder=this.renderOrder;a.mirror=!!randInt(2);this.particleCreateCallback&&this.particleCreateCallback(a);return a}render(){}}class Particle extends EngineObject{constructor(a,b,c,d,e,f,g,k,h,m,n,l,p){super(a,vec2(),b,c);this.colorStart=d;this.colorEndDelta=e.subtract(d);this.lifeTime=f;this.sizeStart=g;this.sizeEndDelta=k-g;this.fadeRate=h;this.additive=m;this.trailScale=n;this.localSpaceEmitter=l;this.destroyCallback=p;this.clampSpeedLinear=!1}render(){const a=min((time-this.spawnTime)/this.lifeTime,1),b=vec2(this.sizeStart+a*this.sizeEndDelta);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(!0);let d=this.pos,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));var g=f.length();g&&(f=f.scale(1/g),g*=this.trailScale,b.y=max(b.x,g),e=f.angle(),drawTile(d.add(f.multiply(vec2(0,-g/2))),b,this.tileInfo,c,e,this.mirror))}else drawTile(d,b,this.tileInfo,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)}}const medals={};let medalsDisplayQueue=[],medalsSaveName,medalsDisplayTimeLast;function medalsInit(a){medalsSaveName=a;debugMedals||medalsForEach(b=>b.unlocked=!!localStorage[b.storageKey()]);engineAddPlugin(void 0,function(){if(medalsDisplayQueue.length){var b=medalsDisplayQueue[0],c=timeReal-medalsDisplayTimeLast;if(medalsDisplayTimeLast)if(c>medalDisplayTime)medalsDisplayTimeLast=0,medalsDisplayQueue.shift();else{const d=medalDisplayTime-medalDisplaySlideTime;b.render(c<medalDisplaySlideTime?1-c/medalDisplaySlideTime:c>d?(c-d)/medalDisplaySlideTime:0)}else medalsDisplayTimeLast=timeReal}})}function medalsForEach(a){Object.values(medals).forEach(b=>a(b))}class Medal{constructor(a,b,c="",d="🏆",e){ASSERT(0<=a&&!medals[a]);this.id=a;this.name=b;this.description=c;this.icon=d;this.unlocked=!1;e&&((this.image=new Image).src=e);medals[a]=this}unlock(){medalsPreventUnlock||this.unlocked||(ASSERT(medalsSaveName,"save name must be set"),localStorage[this.storageKey()]=this.unlocked=!0,medalsDisplayQueue.push(this))}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).toString();b.strokeStyle=new Color(0,0,0).toString();b.lineWidth=3;b.rect(d,a,c,medalDisplaySize.y);b.fill();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,void 0,"left");c.y+=32;drawTextScreen(this.description,c,24,new Color(0,0,0),0,void 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}}let glCanvas,glContext,glAntialias=!0,glShader,glActiveTexture,glArrayBuffer,glGeometryBuffer,glPositionData,glColorData,glInstanceCount,glAdditive,glBatchAdditive;function glInit(){if(glEnable&&!headlessMode){glCanvas=document.createElement("canvas");glContext=glCanvas.getContext("webgl2",{antialias:glAntialias});var a=mainCanvas.parentElement;glOverlay&&a.appendChild(glCanvas);glShader=glCreateProgram("#version 300 es\nprecision highp float;uniform mat4 m;in vec2 g;in vec4 p,u,c,a;in float r;out vec2 v;out vec4 d,e;void main(){vec2 s=(g-.5)*p.zw;gl_Position=m*vec4(p.xy+s*cos(r)-vec2(-s.y,s)*sin(r),1,1);v=mix(u.xw,u.zy,g);d=c;e=a;}","#version 300 es\nprecision highp float;uniform sampler2D s;in vec2 v;in vec4 d,e;out vec4 c;void main(){c=texture(s,v)*d+e;}");a=new ArrayBuffer(gl_INSTANCE_BUFFER_SIZE);glPositionData=new Float32Array(a);glColorData=new Uint32Array(a);glArrayBuffer=glContext.createBuffer();glGeometryBuffer=glContext.createBuffer();a=new Float32Array([glInstanceCount=0,0,1,0,0,1,1,1]);glContext.bindBuffer(gl_ARRAY_BUFFER,glGeometryBuffer);glContext.bufferData(gl_ARRAY_BUFFER,a,gl_STATIC_DRAW)}}function glPreRender(){if(glEnable&&!headlessMode){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);textureInfos[0]&&glContext.bindTexture(gl_TEXTURE_2D,glActiveTexture=textureInfos[0].glTexture);var a=glAdditive=glBatchAdditive=0,b=(d,e,f,g)=>{d=glContext.getAttribLocation(glShader,d);const k=f&&gl_INSTANCE_BYTE_STRIDE,h=f&&1,m=1==f;glContext.enableVertexAttribArray(d);glContext.vertexAttribPointer(d,g,e,m,k,a);glContext.vertexAttribDivisor(d,h);a+=g*f};glContext.bindBuffer(gl_ARRAY_BUFFER,glGeometryBuffer);b("g",gl_FLOAT,0,2);glContext.bindBuffer(gl_ARRAY_BUFFER,glArrayBuffer);glContext.bufferData(gl_ARRAY_BUFFER,gl_INSTANCE_BUFFER_SIZE,gl_DYNAMIC_DRAW);b("p",gl_FLOAT,4,4);b("u",gl_FLOAT,4,4);b("c",gl_UNSIGNED_BYTE,1,4);b("a",gl_UNSIGNED_BYTE,1,4);b("r",gl_FLOAT,4,1);b=vec2(2*cameraScale).divide(mainCanvasSize);var c=vec2(-1).subtract(cameraPos.multiply(b));glContext.uniformMatrix4fv(glContext.getUniformLocation(glShader,"m"),!1,[b.x,0,0,0,0,b.y,0,0,1,1,1,1,c.x,c.y,0,0])}}function glSetTexture(a){headlessMode||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=new Uint8Array([255,255,255,255]),glContext.texImage2D(gl_TEXTURE_2D,0,gl_RGBA,1,1,0,gl_RGBA,gl_UNSIGNED_BYTE,a));a=canvasPixelated?gl_NEAREST:gl_LINEAR;glContext.texParameteri(gl_TEXTURE_2D,gl_TEXTURE_MIN_FILTER,a);glContext.texParameteri(gl_TEXTURE_2D,gl_TEXTURE_MAG_FILTER,a);return b}function glFlush(){if(glInstanceCount){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);glContext.drawArraysInstanced(gl_TRIANGLE_STRIP,0,4,glInstanceCount);showWatermark&&(drawCount+=glInstanceCount);glInstanceCount=0;glBatchAdditive=glAdditive}}function glCopyToContext(a,b=!1){glEnable&&(glInstanceCount||b)&&(glFlush(),glOverlay&&!b||a.drawImage(glCanvas,0,0))}function glSetAntialias(a=!0){ASSERT(!glCanvas,"must be called before engineInit");glAntialias=a}function glDraw(a,b,c,d,e,f,g,k,h,m,n=0){ASSERT("number"==typeof m&&"number"==typeof n,"invalid color");(glInstanceCount>=gl_MAX_INSTANCES||glBatchAdditive!=glAdditive)&&glFlush();let l=glInstanceCount*gl_INDICIES_PER_INSTANCE;glPositionData[l++]=a;glPositionData[l++]=b;glPositionData[l++]=c;glPositionData[l++]=d;glPositionData[l++]=f;glPositionData[l++]=g;glPositionData[l++]=k;glPositionData[l++]=h;glColorData[l++]=m;glColorData[l++]=n;glPositionData[l++]=e;glInstanceCount++}const gl_ONE=1,gl_TRIANGLE_STRIP=5,gl_SRC_ALPHA=770,gl_ONE_MINUS_SRC_ALPHA=771,gl_BLEND=3042,gl_TEXTURE_2D=3553,gl_UNSIGNED_BYTE=5121,gl_FLOAT=5126,gl_RGBA=6408,gl_NEAREST=9728,gl_LINEAR=9729,gl_TEXTURE_MAG_FILTER=10240,gl_TEXTURE_MIN_FILTER=10241,gl_COLOR_BUFFER_BIT=16384,gl_TEXTURE0=33984,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_INDICIES_PER_INSTANCE=11,gl_MAX_INSTANCES=1e4,gl_INSTANCE_BYTE_STRIDE=4*gl_INDICIES_PER_INSTANCE,gl_INSTANCE_BUFFER_SIZE=gl_MAX_INSTANCES*gl_INSTANCE_BYTE_STRIDE,engineName="LittleJS",engineVersion="1.10.7",frameRate=60,timeDelta=1/frameRate;let engineObjects=[],engineObjectsCollide=[],frame=0,time=0,timeReal=0,paused=!1;function setPaused(a){paused=a}let frameTimeLastMS=0,frameTimeBufferMS=0,averageFPS=0;const pluginUpdateList=[],pluginRenderList=[];function engineAddPlugin(a,b){ASSERT(!pluginUpdateList.includes(a));ASSERT(!pluginRenderList.includes(b));a&&pluginUpdateList.push(a);b&&pluginRenderList.push(b)}function engineInit(a,b,c,d,e,f=[],g=document.body){function k(n=0){var l=n-frameTimeLastMS;frameTimeLastMS=n;if(debug||showWatermark)averageFPS=lerp(.05,averageFPS,1e3/(l||1));n=debug&&keyIsDown("Equal");const p=debug&&keyIsDown("Minus");debug&&(l*=n?5:p?.2:1);timeReal+=l/1e3;frameTimeBufferMS+=paused?0:l;n||(frameTimeBufferMS=min(frameTimeBufferMS,50));h();if(paused){for(const r of engineObjects)r.parent||r.updateTransforms();inputUpdate();pluginUpdateList.forEach(r=>r());debugUpdate();c();inputUpdatePost()}else{l=0;0>frameTimeBufferMS&&-9<frameTimeBufferMS&&(l=frameTimeBufferMS,frameTimeBufferMS=0);for(;0<=frameTimeBufferMS;frameTimeBufferMS-=1e3/frameRate)time=frame++/frameRate,inputUpdate(),b(),pluginUpdateList.forEach(r=>r()),engineObjectsUpdate(),debugUpdate(),c(),inputUpdatePost();frameTimeBufferMS+=l}if(!headlessMode){mainCanvasSize=vec2(mainCanvas.width,mainCanvas.height);mainContext.imageSmoothingEnabled=!canvasPixelated;glPreRender();d();engineObjects.sort((r,x)=>r.renderOrder-x.renderOrder);for(var q of engineObjects)q.destroyed||q.render();e();pluginRenderList.forEach(r=>r());touchGamepadRender();debugRender();glCopyToContext(mainContext);showWatermark&&(overlayContext.textAlign="right",overlayContext.textBaseline="top",overlayContext.font="1em monospace",overlayContext.fillStyle="#000",q=engineName+" v"+engineVersion+" / "+drawCount+" / "+engineObjects.length+" / "+averageFPS.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(k)}function h(){if(!headlessMode){if(canvasFixedSize.x){mainCanvas.width=canvasFixedSize.x;mainCanvas.height=canvasFixedSize.y;const n=innerWidth/innerHeight,l=mainCanvas.width/mainCanvas.height;(glCanvas||mainCanvas).style.width=mainCanvas.style.width=overlayCanvas.style.width=n<l?"100%":"";(glCanvas||mainCanvas).style.height=mainCanvas.style.height=overlayCanvas.style.height=n<l?"":"100%"}else mainCanvas.width=min(innerWidth,canvasMaxSize.x),mainCanvas.height=min(innerHeight,canvasMaxSize.y);overlayCanvas.width=mainCanvas.width;overlayCanvas.height=mainCanvas.height;mainCanvasSize=vec2(mainCanvas.width,mainCanvas.height)}}function m(){new Promise(n=>n(a())).then(k)}ASSERT(Array.isArray(f),"pass in images as array");headlessMode?m():(g.style.cssText="margin:0;overflow:hidden;width:100vw;height:100vh;display:flex;align-items:center;justify-content:center;background:#000;user-select:none;-webkit-user-select:none;"+(touchInputEnable?"touch-action:none;-webkit-touch-callout:none":""),g.appendChild(mainCanvas=document.createElement("canvas")),mainContext=mainCanvas.getContext("2d"),inputInit(),audioInit(),debugInit(),glInit(),g.appendChild(overlayCanvas=document.createElement("canvas")),overlayContext=overlayCanvas.getContext("2d"),mainCanvas.style.cssText=overlayCanvas.style.cssText="position:absolute",glCanvas&&(glCanvas.style.cssText="position:absolute"),h(),g=f.map((n,l)=>new Promise(p=>{const q=new Image;q.onerror=q.onload=()=>{textureInfos[l]=new TextureInfo(q);p()};q.src=n})),f.length||g.push(new Promise(n=>{textureInfos[0]=new TextureInfo(new Image);n()})),showSplashScreen&&g.push(new Promise(n=>{function l(){clearInput();drawEngineSplashScreen(p+=.01);1<p?n():setTimeout(l,16)}let p=0;console.log(`${engineName} Engine v${engineVersion}`);l()})),Promise.all(g).then(m))}function engineObjectsUpdate(){function a(b){if(!b.destroyed){b.update();for(const c of b.children)a(c)}}engineObjectsCollide=engineObjects.filter(b=>b.collideSolidObjects);for(const b of engineObjects)b.parent||(a(b),b.updateTransforms());engineObjects=engineObjects.filter(b=>!b.destroyed)}function engineObjectsDestroy(){for(const a of engineObjects)a.parent||a.destroy();engineObjects=engineObjects.filter(a=>!a.destroyed)}function engineObjectsCollect(a,b,c=engineObjects){const d=[];if(a)if(b instanceof Vector2)for(const e of c)isOverlapping(a,b,e.pos,e.size)&&d.push(e);else{b*=b;for(const e of c)a.distanceSquared(e.pos)<b&&d.push(e)}else for(const e of c)d.push(e);return d}function engineObjectsCallback(a,b,c,d=engineObjects){engineObjectsCollect(a,b,d).forEach(e=>c(e))}function engineObjectsRaycast(a,b,c=engineObjects){const d=[];for(const e of c)e.collideRaycast&&isIntersecting(a,b,e.pos,e.size)&&(debugRaycast&&debugRect(e.pos,e.size,"#f00"),d.push(e));debugRaycast&&debugLine(a,b,d.length?"#f00":"#00f",.02);return d}function drawEngineSplashScreen(a){const b=overlayContext;var c=overlayCanvas.width=innerWidth,d=overlayCanvas.height=innerHeight,e=percent(a,1,.8),f=percent(a,0,.5),g=b.createRadialGradient(c/2,d/2,0,c/2,d/2,.7*Math.hypot(c,d));g.addColorStop(0,hsl(0,0,lerp(f,0,e/2),e).toString());g.addColorStop(1,hsl(0,0,0,e).toString());b.save();b.fillStyle=g;b.fillRect(0,0,c,d);g=(h,m,n,l,p)=>{b.beginPath();b.rect(h,m,n,p?l*k:l);(b.fillStyle=p)?b.fill():b.stroke()};f=(h,m,n,l=0,p=2*PI,q,r)=>{const x=(l+p)/2;l=k*(p-l)/2;b.beginPath();r&&b.lineTo(h,m);b.arc(h,m,n,x-l,x+l);(b.fillStyle=q)?b.fill():b.stroke()};e=(h=0,m=0)=>hsl([.98,.3,.57,.14][h%4]-10,.8,[0,.3,.5,.8,.9][m]).toString();a=wave(1,1,a);const k=percent(a,.1,.5);b.translate(c/2,d/2);c=min(6,min(c,d)/99);b.scale(c,c);b.translate(-40,-35);b.lineJoin=b.lineCap="round";b.lineWidth=.1+1.9*k;c=percent(a,.1,1);b.setLineDash([99*c,99]);g(7,16,18,-8,e(2,2));g(7,8,18,4,e(2,3));g(25,8,8,8,e(2,1));g(25,8,-18,8);g(25,8,8,8);g(25,16,7,23,e());g(11,39,14,-23,e(1,1));g(11,16,14,18,e(1,2));g(11,16,14,8,e(1,3));g(25,16,-14,24);g(15,29,6,-9,e(2,2));f(15,21,5,0,PI/2,e(2,4),1);g(21,21,-6,9);g(37,14,9,6,e(3,2));g(37,14,4.5,6,e(3,3));g(37,14,9,6);g(50,20,10,-8,e(0,1));g(50,20,6.5,-8,e(0,2));g(50,20,3.5,-8,e(0,3));g(50,20,10,-8);f(55,2,11.4,.5,PI-.5,e(3,3));f(55,2,11.4,.5,PI/2,e(3,2),1);f(55,2,11.4,.5,PI-.5);g(45,7,20,-7,e(0,2));g(45,-1,20,4,e(0,3));g(45,-1,20,8);for(c=5;c--;)f(60-6*c,30,9.9,0,2*PI,e(c+2,3)),f(60-6*c,30,10,-.5,PI+.5,e(c+2,2)),f(60-6*c,30,10.1,.5,PI-.5,e(c+2,1));f(36,30,10,PI/2,3*PI/2);f(48,30,10,PI/2,3*PI/2);f(60,30,10);b.beginPath();b.lineTo(36,20);b.lineTo(60,20);b.stroke();f(60,30,4,PI,3*PI,e(3,2));f(60,30,4,PI,2*PI,e(3,3));f(60,30,4,PI,3*PI);for(c=6;c--;)b.beginPath(),b.lineTo(53,54),b.lineTo(53,40),b.lineTo(53+(1+2.9*c)*k,40),b.lineTo(53+(4+3.5*c)*k,54),b.fillStyle=e(0,c%2+2),b.fill(),c%2&&b.stroke();g(6,40,5,5);g(6,40,5,5,e());g(15,54,38,-14,e());for(g=3;g--;)for(c=2;c--;)f(15*g+15,47,c?7:1,PI,3*PI,e(g,3)),b.stroke(),f(15*g+15,47,c?7:1,0,PI,e(g,2)),b.stroke();b.beginPath();b.lineTo(6,40);b.lineTo(68,40);b.stroke();b.beginPath();b.lineTo(77,54);b.lineTo(4,54);b.stroke();f=engineName;b.font="900 16px arial";b.textAlign="center";b.textBaseline="top";b.lineWidth=.1+3.9*k;g=0;for(c=0;c<f.length;++c)g+=b.measureText(f[c]).width;for(c=2;c--;)for(let h=0,m=41-g/2;h<f.length;++h)b.fillStyle=e(h,2),d=b.measureText(f[h]).width,b[c?"strokeText":"fillText"](f[h],m+d/2,55.5,17*k),m+=d;b.restore()}
1
+ let showWatermark=0,debugKey="";const debug=0,debugOverlay=0,debugPhysics=0,debugParticles=0,debugRaycast=0,debugGamepads=0,debugMedals=0;function ASSERT(){}function debugInit(){}function debugUpdate(){}function debugRender(){}function debugRect(){}function debugPoly(){}function debugCircle(){}function debugPoint(){}function debugLine(){}function debugOverlap(){}function debugText(){}function debugClear(){}function debugSaveCanvas(){}function debugSaveText(){}function debugSaveDataURL(){}const PI=Math.PI;function abs(a){return Math.abs(a)}function min(a,b){return Math.min(a,b)}function max(a,b){return Math.max(a,b)}function sign(a){return Math.sign(a)}function mod(a,b=1){return(a%b+b)%b}function clamp(a,b=0,c=1){return a<b?b:a>c?c:a}function percent(a,b,c){return(c-=b)?clamp((a-b)/c):0}function lerp(a,b,c){return b+clamp(a)*(c-b)}function distanceWrap(a,b,c=1){a=(a-b)%c;return 2*a%c-a}function lerpWrap(a,b,c,d=1){return c+clamp(a)*distanceWrap(b,c,d)}function distanceAngle(a,b){return distanceWrap(a,b,2*PI)}function lerpAngle(a,b,c){return lerpWrap(a,b,c,2*PI)}function smoothStep(a){return a*a*(3-2*a)}function nearestPowerOfTwo(a){return 2**Math.ceil(Math.log2(a))}function isOverlapping(a,b,c,d=vec2()){return 2*abs(a.x-c.x)<b.x+d.x&&2*abs(a.y-c.y)<b.y+d.y}function isIntersecting(a,b,c,d){c=c.subtract(d.scale(.5));d=c.add(d);b=b.subtract(a);c=a.subtract(c);d=a.subtract(d);a=[-b.x,b.x,-b.y,b.y];b=[c.x,-d.x,c.y,-d.y];c=0;d=1;for(let e=4;e--;)if(a[e]){const f=b[e]/a[e];if(0>a[e]){if(f>d)return!1;c=max(f,c)}else{if(f<c)return!1;d=min(f,d)}}else if(0>b[e])return!1;return!0}function wave(a=1,b=1,c=time){return b/2*(1-Math.cos(c*a*2*PI))}function formatTime(a){return(a/60|0)+":"+(10>a%60?"0":"")+(a%60|0)}function rand(a=1,b=0){return b+Math.random()*(a-b)}function randInt(a,b=0){return Math.floor(rand(a,b))}function randSign(){return 2*randInt(2)-1}function randVector(a=1){return(new Vector2).setAngle(rand(2*PI),a)}function randInCircle(a=1,b=0){return 0<a?randVector(a*rand(b/a,1)**.5):new Vector2}function randColor(a=new Color,b=new Color(0,0,0,1),c=!1){return 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))}class RandomGenerator{constructor(a){this.seed=a}float(a=1,b=0){this.seed^=this.seed<<13;this.seed^=this.seed>>>17;this.seed^=this.seed<<5;return b+(a-b)*abs(this.seed%1e8)/1e8}int(a,b=0){return Math.floor(this.float(a,b))}sign(){return.5<this.float()?1:-1}}function vec2(a=0,b){return"number"==typeof a?new Vector2(a,void 0==b?a:b):new Vector2(a.x,a.y)}function isVector2(a){return a instanceof Vector2}class Vector2{constructor(a=0,b=0){this.x=a;this.y=b;ASSERT(this.isValid())}set(a=0,b=0){this.x=a;this.y=b;ASSERT(this.isValid());return this}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){ASSERT(isVector2(a));return this.distanceSquared(a)**.5}distanceSquared(a){ASSERT(isVector2(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)}setDirection(a,b=1){a=mod(a,4);ASSERT(0==a||1==a||2==a||3==a);return vec2(a%2?a-1?-b:b:0,a%2?0:a?-b: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){ASSERT(isVector2(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)} )`}isValid(){return"number"==typeof this.x&&!isNaN(this.x)&&"number"==typeof this.y&&!isNaN(this.y)}}function rgb(a,b,c,d){return new Color(a,b,c,d)}function hsl(a,b,c,d){return(new Color).setHSLA(a,b,c,d)}function isColor(a){return a instanceof Color}class Color{constructor(a=1,b=1,c=1,d=1){this.r=a;this.g=b;this.b=c;this.a=d;ASSERT(this.isValid())}set(a=1,b=1,c=1,d=1){this.r=a;this.g=b;this.b=c;this.a=d;ASSERT(this.isValid());return this}copy(){return new Color(this.r,this.g,this.b,this.a)}add(a){ASSERT(isColor(a));return new Color(this.r+a.r,this.g+a.g,this.b+a.b,this.a+a.a)}subtract(a){ASSERT(isColor(a));return new Color(this.r-a.r,this.g-a.g,this.b-a.b,this.a-a.a)}multiply(a){ASSERT(isColor(a));return new Color(this.r*a.r,this.g*a.g,this.b*a.b,this.a*a.a)}divide(a){ASSERT(isColor(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){ASSERT(isColor(a));return this.add(a.subtract(this).scale(clamp(b)))}setHSLA(a=0,b=0,c=1,d=1){a=mod(a,1);b=clamp(b);c=clamp(c);b=.5>c?c*(1+b):c+b-c*b;c=2*c-b;const e=(f,g,k)=>1>6*(k=mod(k,1))?f+6*(g-f)*k:1>2*k?g:2>3*k?f+(g-f)*(4-6*k):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;ASSERT(this.isValid());return this}HSLA(){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 m=e-f;h=.5<g?m/(2-e-f):m/(e+f);a==e?k=(b-c)/m+(b<c?6:0):b==e?k=(c-a)/m+2:c==e&&(k=(a-b)/m+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=!0){const b=c=>(16>(c=255*clamp(c)|0)?"0":"")+c.toString(16);return"#"+b(this.r)+b(this.g)+b(this.b)+(a?b(this.a):"")}setHex(a){ASSERT("string"==typeof a&&"#"==a[0]);ASSERT([4,5,7,9].includes(a.length),"Invalid hex");6>a.length?(this.r=clamp(parseInt(a[1],16)/15),this.g=clamp(parseInt(a[2],16)/15),this.b=clamp(parseInt(a[3],16)/15),this.a=5==a.length?clamp(parseInt(a[4],16)/15):1):(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=9==a.length?clamp(parseInt(a.slice(7,9),16)/255):1);ASSERT(this.isValid());return this}rgbaInt(){const a=255*clamp(this.r)|0,b=255*clamp(this.g)<<8,c=255*clamp(this.b)<<16,d=255*clamp(this.a)<<24;return a+b+c+d}isValid(){return"number"==typeof this.r&&!isNaN(this.r)&&"number"==typeof this.g&&!isNaN(this.g)&&"number"==typeof this.b&&!isNaN(this.b)&&"number"==typeof this.a&&!isNaN(this.a)}}const WHITE=rgb(),BLACK=rgb(0,0,0),GRAY=rgb(.5,.5,.5),RED=rgb(1,0,0),ORANGE=rgb(1,.5,0),YELLOW=rgb(1,1,0),GREEN=rgb(0,1,0),CYAN=rgb(0,1,1),BLUE=rgb(0,0,1),PURPLE=rgb(.5,0,1),MAGENTA=rgb(1,0,1);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.isSet()?Math.abs(this.get())+" seconds "+(0>this.get()?"before":"after"):"unset"}valueOf(){return this.get()}}let cameraPos=vec2(),cameraScale=32,canvasMaxSize=vec2(1920,1080),canvasFixedSize=vec2(),canvasPixelated=!0,tilesPixelated=!0,fontDefault="arial",showSplashScreen=!1,headlessMode=!1,glEnable=!0,glOverlay=!0,tileSizeDefault=vec2(16),tileFixBleedScale=0,enablePhysicsSolver=!0,objectDefaultMass=1,objectDefaultDamping=1,objectDefaultAngleDamping=1,objectDefaultElasticity=0,objectDefaultFriction=.8,objectMaxSpeed=1,gravity=0,particleEmitRateScale=1,gamepadsEnable=!0,gamepadDirectionEmulateStick=!0,inputWASDEmulateDirection=!0,touchInputEnable=!0,touchGamepadEnable=!1,touchGamepadAnalog=!0,touchGamepadSize=99,touchGamepadAlpha=.3,vibrateEnable=!0,soundEnable=!0,soundVolume=.3,soundDefaultRange=40,soundDefaultTaper=.7,medalDisplayTime=5,medalDisplaySlideTime=.5,medalDisplaySize=vec2(640,80),medalDisplayIconSize=50,medalsPreventUnlock=!1;function setCameraPos(a){cameraPos=a}function setCameraScale(a){cameraScale=a}function setCanvasMaxSize(a){canvasMaxSize=a}function setCanvasFixedSize(a){canvasFixedSize=a}function setCanvasPixelated(a){canvasPixelated=a}function setTilesPixelated(a){tilesPixelated=a}function setFontDefault(a){fontDefault=a}function setShowSplashScreen(a){showSplashScreen=a}function setHeadlessMode(a){headlessMode=a}function setGlEnable(a){glEnable=a}function setGlOverlay(a){glOverlay=a}function setTileSizeDefault(a){tileSizeDefault=a}function setTileFixBleedScale(a){tileFixBleedScale=a}function setEnablePhysicsSolver(a){enablePhysicsSolver=a}function setObjectDefaultMass(a){objectDefaultMass=a}function setObjectDefaultDamping(a){objectDefaultDamping=a}function setObjectDefaultAngleDamping(a){objectDefaultAngleDamping=a}function setObjectDefaultElasticity(a){objectDefaultElasticity=a}function setObjectDefaultFriction(a){objectDefaultFriction=a}function setObjectMaxSpeed(a){objectMaxSpeed=a}function setGravity(a){gravity=a}function setParticleEmitRateScale(a){particleEmitRateScale=a}function setGamepadsEnable(a){gamepadsEnable=a}function setGamepadDirectionEmulateStick(a){gamepadDirectionEmulateStick=a}function setInputWASDEmulateDirection(a){inputWASDEmulateDirection=a}function setTouchInputEnable(a){touchInputEnable=a}function setTouchGamepadEnable(a){touchGamepadEnable=a}function setTouchGamepadAnalog(a){touchGamepadAnalog=a}function setTouchGamepadSize(a){touchGamepadSize=a}function setTouchGamepadAlpha(a){touchGamepadAlpha=a}function setVibrateEnable(a){vibrateEnable=a}function setSoundEnable(a){soundEnable=a}function setSoundVolume(a){soundVolume=a;soundEnable&&!headlessMode&&audioGainNode&&(audioGainNode.gain.value=a)}function setSoundDefaultRange(a){soundDefaultRange=a}function setSoundDefaultTaper(a){soundDefaultTaper=a}function setMedalDisplayTime(a){medalDisplayTime=a}function setMedalDisplaySlideTime(a){medalDisplaySlideTime=a}function setMedalDisplaySize(a){medalDisplaySize=a}function setMedalDisplayIconSize(a){medalDisplayIconSize=a}function setMedalsPreventUnlock(a){medalsPreventUnlock=a}function setShowWatermark(a){showWatermark=a}function setDebugKey(a){debugKey=a}class EngineObject{constructor(a=vec2(),b=vec2(1),c,d=0,e=new Color,f=0){ASSERT(isVector2(a)&&isVector2(b),"ensure pos and size are vec2s");ASSERT("number"!==typeof c||!c,"old style tile setup");this.pos=a.copy();this.size=b;this.drawSize=void 0;this.tileInfo=c;this.angle=d;this.color=e;this.additiveColor=void 0;this.mirror=!1;this.mass=objectDefaultMass;this.damping=objectDefaultDamping;this.angleDamping=objectDefaultAngleDamping;this.elasticity=objectDefaultElasticity;this.friction=objectDefaultFriction;this.gravityScale=1;this.renderOrder=f;this.velocity=vec2();this.angleVelocity=0;this.spawnTime=time;this.children=[];this.clampSpeedLinear=!0;this.parent=void 0;this.localPos=vec2();this.localAngle=0;this.collideRaycast=this.isSolid=this.collideSolidObjects=this.collideTiles=!1;engineObjects.push(this)}updateTransforms(){const a=this.parent;if(a){const b=a.getMirrorSign();this.pos=this.localPos.multiply(vec2(b,1)).rotate(-a.angle).add(a.pos);this.angle=b*this.localAngle+a.angle}for(const b of this.children)b.updateTransforms()}update(){if(!this.parent){if(this.clampSpeedLinear)this.velocity.x=clamp(this.velocity.x,-objectMaxSpeed,objectMaxSpeed),this.velocity.y=clamp(this.velocity.y,-objectMaxSpeed,objectMaxSpeed);else{var a=this.velocity.lengthSquared();a>objectMaxSpeed*objectMaxSpeed&&(a=objectMaxSpeed/a**.5,this.velocity.x*=a,this.velocity.y*=a)}a=this.pos.copy();this.velocity.x*=this.damping;this.velocity.y*=this.damping;this.mass&&(this.velocity.y+=gravity*this.gravityScale);this.pos.x+=this.velocity.x;this.pos.y+=this.velocity.y;this.angle+=this.angleVelocity*=this.angleDamping;ASSERT(0<=this.angleDamping&&1>=this.angleDamping);ASSERT(0<=this.damping&&1>=this.damping);if(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)continue;if(!isOverlapping(this.pos,this.size,d.pos,d.size))continue;c=this.collideWithObject(d);var e=d.collideWithObject(this);if(!c||!e)continue;if(isOverlapping(a,this.size,d.pos,d.size)){c=a.subtract(d.pos);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&&debugOverlap(this.pos,this.size,d.pos,d.size,"#f00");continue}e=this.size.add(d.size);var f=2*(a.y-d.pos.y)>e.y+gravity;const k=2*abs(a.y-d.pos.y)<e.y;var g=2*abs(a.x-d.pos.x)<e.x;c=max(this.elasticity,d.elasticity);if(f||g||!k)if(this.pos.y=d.pos.y+(e.y/2+.001)*sign(a.y-d.pos.y),d.groundObject&&b||!d.mass)b&&(this.groundObject=d),this.velocity.y*=-c;else if(d.mass){g=(this.mass*this.velocity.y+d.mass*d.velocity.y)/(this.mass+d.mass);const h=d.velocity.y*(d.mass-this.mass)/(this.mass+d.mass)+2*this.velocity.y*this.mass/(this.mass+d.mass);this.velocity.y=lerp(c,g,this.velocity.y*(this.mass-d.mass)/(this.mass+d.mass)+2*d.velocity.y*d.mass/(this.mass+d.mass));d.velocity.y=lerp(c,g,h)}!f&&k&&(this.pos.x=d.pos.x+(e.x/2+.001)*sign(a.x-d.pos.x),d.mass?(e=(this.mass*this.velocity.x+d.mass*d.velocity.x)/(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),this.velocity.x=lerp(c,e,this.velocity.x*(this.mass-d.mass)/(this.mass+d.mass)+2*d.velocity.x*d.mass/(this.mass+d.mass)),d.velocity.x=lerp(c,e,f)):this.velocity.x*=-c);debugOverlay&&debugPhysics&&debugOverlap(this.pos,this.size,d.pos,d.size,"#f0f")}if(this.collideTiles&&tileCollisionTest(this.pos,this.size,this)&&!tileCollisionTest(a,this.size,this)){d=tileCollisionTest(vec2(a.x,this.pos.y),this.size,this);c=tileCollisionTest(vec2(this.pos.x,a.y),this.size,this);if(d||!c)this.velocity.y*=-this.elasticity,(this.groundObject=b)?this.pos.y=(a.y-this.size.y/2|0)+this.size.y/2+1e-4:this.pos.y=a.y;c&&(this.pos.x=a.x,this.velocity.x*=-this.elasticity);debugOverlay&&debugPhysics&&debugRect(this.pos,this.size,"#f00")}}}}render(){drawTile(this.pos,this.drawSize||this.size,this.tileInfo,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)}}localToWorld(a){return this.pos.add(a.rotate(-this.angle))}worldToLocal(a){return a.subtract(this.pos).rotate(this.angle)}localToWorldVector(a){return a.rotate(this.angle)}worldToLocalVector(a){return a.rotate(-this.angle)}collideWithTile(a,b){return 0<a}collideWithObject(a){return!0}getAliveTime(){return time-this.spawnTime}applyAcceleration(a){this.mass&&(this.velocity=this.velocity.add(a))}applyForce(a){this.applyAcceleration(a.scale(1/this.mass))}getMirrorSign(){return this.mirror?-1:1}addChild(a,b=vec2(),c=0){ASSERT(!a.parent&&!this.children.includes(a));this.children.push(a);a.parent=this;a.localPos=b.copy();a.localAngle=c}removeChild(a){ASSERT(a.parent==this&&this.children.includes(a));this.children.splice(this.children.indexOf(a),1);a.parent=0}setCollision(a=!0,b=!0,c=!0,d=!0){ASSERT(a||!b,"solid objects must be set to collide");this.collideSolidObjects=a;this.isSolid=b;this.collideTiles=c;this.collideRaycast=d}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}}renderDebugInfo(){if(debug){const a=vec2(max(this.size.x,.2),max(this.size.y,.2)),b=rgb(this.collideTiles?1:0,this.collideSolidObjects?1:0,this.isSolid?1:0,this.parent?.2:.5),c=this.parent?rgb(1,1,1,.5):rgb(0,0,0,.8);drawRect(this.pos,a,b,this.angle,!1);drawRect(this.pos,a.scale(.8),c,this.angle,!1);this.parent&&drawLine(this.pos,this.parent.pos,.1,rgb(0,0,1,.5),!1)}}}let mainCanvas,mainContext,overlayCanvas,overlayContext,mainCanvasSize=vec2(),textureInfos=[],drawCount;function tile(a=vec2(),b=tileSizeDefault,c=0,d=0){if(headlessMode)return new TileInfo;"number"===typeof b&&(ASSERT(0<b),b=vec2(b));var e=textureInfos[c];ASSERT(e,"Texture not loaded");const f=b.add(vec2(2*d));e=e.size.x/f.x|0;"number"===typeof a&&(a=vec2(a%e,a/e|0));a=vec2(a.x*f.x+d,a.y*f.y+d);return new TileInfo(a,b,c,d)}class TileInfo{constructor(a=vec2(),b=tileSizeDefault,c=0,d=0){this.pos=a.copy();this.size=b.copy();this.textureIndex=c;this.padding=d}offset(a){return new TileInfo(this.pos.add(a),this.size,this.textureIndex)}frame(a){ASSERT("number"==typeof a);return this.offset(vec2(a*(this.size.x+2*this.padding),0))}getTextureInfo(){return textureInfos[this.textureIndex]}}class TextureInfo{constructor(a){this.image=a;this.size=vec2(a.width,a.height);this.glTexture=glEnable&&glCreateTexture(a)}}function screenToWorld(a){return new Vector2((a.x-mainCanvasSize.x/2+.5)/cameraScale+cameraPos.x,(a.y-mainCanvasSize.y/2+.5)/-cameraScale+cameraPos.y)}function worldToScreen(a){return new Vector2((a.x-cameraPos.x)*cameraScale+mainCanvasSize.x/2-.5,(a.y-cameraPos.y)*-cameraScale+mainCanvasSize.y/2-.5)}function getCameraSize(){return mainCanvasSize.scale(1/cameraScale)}function drawTile(a,b=vec2(1),c,d=new Color,e=0,f,g=new Color(0,0,0,0),k=glEnable,h,m){ASSERT(!m||!k,"context only supported in canvas 2D mode");ASSERT("number"!==typeof c||!c,"this is an old style calls, to fix replace it with tile(tileIndex, tileSize)");const n=c&&c.getTextureInfo();if(k)if(h&&(a=screenToWorld(a),b=b.scale(1/cameraScale)),n){var l=vec2(1).divide(n.size);k=c.pos.x*l.x;h=c.pos.y*l.y;m=c.size.x*l.x;const p=c.size.y*l.y;l=l.scale(tileFixBleedScale);glSetTexture(n.glTexture);glDraw(a.x,a.y,f?-b.x:b.x,b.y,e,k+l.x,h+l.y,k-l.x+m,h-l.y+p,d.rgbaInt(),g.rgbaInt())}else glDraw(a.x,a.y,b.x,b.y,e,0,0,0,0,0,d.rgbaInt());else showWatermark&&++drawCount,b=vec2(b.x,-b.y),drawCanvas2D(a,b,e,f,p=>{if(n){const q=c.pos.x+tileFixBleedScale,r=c.pos.y+tileFixBleedScale,x=c.size.x-2*tileFixBleedScale,v=c.size.y-2*tileFixBleedScale;p.globalAlpha=d.a;p.drawImage(n.image,q,r,x,v,-.5,-.5,1,1);p.globalAlpha=1}else p.fillStyle=d,p.fillRect(-.5,-.5,1,1)},h,m)}function drawRect(a,b,c,d,e,f,g){drawTile(a,b,void 0,c,d,!1,void 0,e,f,g)}function drawLine(a,b,c=.1,d,e,f,g){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,f,g)}function drawPoly(a,b=new Color,c=0,d=new Color(0,0,0),e,f=mainContext){f.fillStyle=b.toString();f.beginPath();for(const g of e?a:a.map(worldToScreen))f.lineTo(g.x,g.y);f.closePath();f.fill();c&&(f.strokeStyle=d.toString(),f.lineWidth=e?c:c*cameraScale,f.stroke())}function drawEllipse(a,b=1,c=1,d=0,e=new Color,f=0,g=new Color(0,0,0),k,h=mainContext){k||(a=worldToScreen(a),b*=cameraScale,c*=cameraScale,f*=cameraScale);h.fillStyle=e.toString();h.beginPath();h.ellipse(a.x,a.y,b,c,d,0,9);h.fill();f&&(h.strokeStyle=g.toString(),h.lineWidth=f,h.stroke())}function drawCircle(a,b=1,c=new Color,d=0,e=new Color(0,0,0),f,g=mainContext){drawEllipse(a,b,b,0,c,d,e,f,g)}function drawCanvas2D(a,b,c,d,e,f,g=mainContext){f||(a=worldToScreen(a),b=b.scale(cameraScale));g.save();g.translate(a.x+.5,a.y+.5);g.rotate(c);g.scale(d?-b.x:b.x,-b.y);e(g);g.restore()}function setBlendMode(a,b=glEnable,c){ASSERT(!c||!b,"context only supported in canvas 2D mode");b?glAdditive=a:(c||=mainContext,c.globalCompositeOperation=a?"lighter":"source-over")}function drawText(a,b,c=1,d,e=0,f,g,k,h,m=mainContext){drawTextScreen(a,worldToScreen(b),c*cameraScale,d,e*cameraScale,f,g,k,h,m)}function drawTextOverlay(a,b,c=1,d,e=0,f,g,k,h){drawText(a,b,c,d,e,f,g,k,h,overlayContext)}function drawTextScreen(a,b,c=1,d=new Color,e=0,f=new Color(0,0,0),g="center",k=fontDefault,h,m=overlayContext){m.fillStyle=d.toString();m.lineWidth=e;m.strokeStyle=f.toString();m.textAlign=g;m.font=c+"px "+k;m.textBaseline="middle";m.lineJoin="round";b=b.copy();(a+"").split("\n").forEach(n=>{e&&m.strokeText(n,b.x,b.y,h);m.fillText(n,b.x,b.y,h);b.y+=c})}let engineFontImage;class FontImage{constructor(a,b=vec2(8),c=vec2(0,1),d=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.context=d}drawText(a,b,c=1,d){this.drawTextScreen(a,worldToScreen(b).floor(),c*cameraScale|0,d)}drawTextScreen(a,b,c=4,d){const e=this.context;e.save();const f=this.tileSize,g=f.add(this.paddingSize).scale(c),k=this.image.width/this.tileSize.x|0;(a+"").split("\n").forEach((h,m)=>{const n=d?h.length*f.x*c/2|0:0;for(let q=h.length;q--;){var l=h[q].charCodeAt(0);if(32>l||127<l)l=127;var p=l-32;l=p%k;p=p/k|0;const r=b.add(vec2(q,m).multiply(g));e.drawImage(this.image,l*f.x,p*f.y,f.x,f.y,r.x-n,r.y,f.x*c,f.y*c)}});e.restore()}}function isFullscreen(){return!!document.fullscreenElement}function toggleFullscreen(){const a=mainCanvas.parentElement;isFullscreen()?document.exitFullscreen&&document.exitFullscreen():a.requestFullscreen&&a.requestFullscreen()}function keyIsDown(a,b=0){ASSERT(0<b||"number"!==typeof a||3>a,"use code string for keyboard");return inputData[b]&&!!(inputData[b][a]&1)}function keyWasPressed(a,b=0){ASSERT(0<b||"number"!==typeof a||3>a,"use code string for keyboard");return inputData[b]&&!!(inputData[b][a]&2)}function keyWasReleased(a,b=0){ASSERT(0<b||"number"!==typeof a||3>a,"use code string for keyboard");return inputData[b]&&!!(inputData[b][a]&4)}function clearInput(){inputData=[[]];touchGamepadButtons=[]}const mouseIsDown=keyIsDown,mouseWasPressed=keyWasPressed,mouseWasReleased=keyWasReleased;let mousePos=vec2(),mousePosScreen=vec2(),mouseWheel=0,isUsingGamepad=!1,preventDefaultInput=!1;function gamepadIsDown(a,b=0){return keyIsDown(a,b+1)}function gamepadWasPressed(a,b=0){return keyWasPressed(a,b+1)}function gamepadWasReleased(a,b=0){return keyWasReleased(a,b+1)}function gamepadStick(a,b=0){return gamepadStickData[b]?gamepadStickData[b][a]||vec2():vec2()}let inputData=[[]];function inputUpdate(){headlessMode||(touchInputEnable&&isTouchDevice||document.hasFocus()||clearInput(),mousePos=screenToWorld(mousePosScreen),gamepadsUpdate())}function inputUpdatePost(){if(!headlessMode){for(const a of inputData)for(const b in a)a[b]&=1;mouseWheel=0}}function inputInit(){function a(b){return inputWASDEmulateDirection?"KeyW"==b?"ArrowUp":"KeyS"==b?"ArrowDown":"KeyA"==b?"ArrowLeft":"KeyD"==b?"ArrowRight":b:b}headlessMode||(onkeydown=b=>{b.repeat||(isUsingGamepad=!1,inputData[0][b.code]=3,inputWASDEmulateDirection&&(inputData[0][a(b.code)]=3));preventDefaultInput&&b.preventDefault()},onkeyup=b=>{inputData[0][b.code]=4;inputWASDEmulateDirection&&(inputData[0][a(b.code)]=4)},onmousedown=b=>{soundEnable&&!headlessMode&&audioContext&&"running"!=audioContext.state&&audioContext.resume();isUsingGamepad=!1;inputData[0][b.button]=3;mousePosScreen=mouseToScreen(b);b.button&&b.preventDefault()},onmouseup=b=>inputData[0][b.button]=inputData[0][b.button]&2|4,onmousemove=b=>mousePosScreen=mouseToScreen(b),onwheel=b=>mouseWheel=b.ctrlKey?0:sign(b.deltaY),oncontextmenu=b=>!1,onblur=b=>clearInput(),isTouchDevice&&touchInputEnable&&touchInputInit())}function mouseToScreen(a){if(!mainCanvas||headlessMode)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)))}const gamepadStickData=[];function gamepadsUpdate(){const a=g=>{const k=h=>.3<h?percent(h,.3,.8):-.3>h?-percent(-h,.3,.8):0;return vec2(k(g.x),k(-g.y)).clampLength()};if(touchGamepadEnable&&isTouchDevice&&(ASSERT(touchGamepadButtons,"set touchGamepadEnable before calling init!"),touchGamepadTimer.isSet())){var b=gamepadStickData[0]||(gamepadStickData[0]=[]);b[0]=vec2();touchGamepadAnalog?b[0]=a(touchGamepadStick):.3<touchGamepadStick.lengthSquared()&&(b[0].x=Math.round(touchGamepadStick.x),b[0].y=-Math.round(touchGamepadStick.y),b[0]=b[0].clampLength());b=inputData[1]||(inputData[1]=[]);for(var c=10;c--;){var d=3==c?2:2==c?3:c,e=gamepadIsDown(d,0);b[d]=touchGamepadButtons[c]?e?1:3:e?4:0}}if(gamepadsEnable&&navigator&&navigator.getGamepads&&(debug||document.hasFocus()))for(b=navigator.getGamepads(),c=b.length;c--;){e=b[c];const g=inputData[c+1]||(inputData[c+1]=[]);d=gamepadStickData[c]||(gamepadStickData[c]=[]);if(e){for(var f=0;f<e.axes.length-1;f+=2)d[f>>1]=a(vec2(e.axes[f],e.axes[f+1]));for(f=e.buttons.length;f--;){const k=e.buttons[f],h=gamepadIsDown(f,c);g[f]=k.pressed?h?1:3:h?4:0;isUsingGamepad||=!c&&k.pressed}gamepadDirectionEmulateStick&&(e=vec2((gamepadIsDown(15,c)&&1)-(gamepadIsDown(14,c)&&1),(gamepadIsDown(12,c)&&1)-(gamepadIsDown(13,c)&&1)),e.lengthSquared()&&(d[0]=e.clampLength()));touchGamepadEnable&&isUsingGamepad&&touchGamepadTimer.unset()}}}function vibrate(a=100){vibrateEnable&&!headlessMode&&navigator&&navigator.vibrate&&navigator.vibrate(a)}function vibrateStop(){vibrate(0)}const isTouchDevice=void 0!==window.ontouchstart;let touchGamepadTimer=new Timer,touchGamepadButtons,touchGamepadStick;function touchInputInit(){function a(e){soundEnable&&!headlessMode&&audioContext&&"running"!=audioContext.state&&audioContext.resume();const f=e.touches.length;if(f){const g=vec2(e.touches[0].clientX,e.touches[0].clientY);mousePosScreen=mouseToScreen(g);d?isUsingGamepad=touchGamepadEnable:inputData[0][0]=3}else d&&(inputData[0][0]=inputData[0][0]&2|4);d=f;document.hasFocus()&&e.preventDefault();return!0}function b(e){touchGamepadStick=vec2();touchGamepadButtons=[];isUsingGamepad=!0;if(e.touches.length&&(touchGamepadTimer.set(),paused&&!d)){touchGamepadButtons[9]=1;a(e);return}const f=vec2(touchGamepadSize,mainCanvasSize.y-touchGamepadSize),g=mainCanvasSize.subtract(vec2(touchGamepadSize,touchGamepadSize)),k=mainCanvasSize.scale(.5);for(const m of e.touches){var h=mouseToScreen(vec2(m.clientX,m.clientY));h.distance(f)<touchGamepadSize?touchGamepadStick=h.subtract(f).scale(2/touchGamepadSize).clampLength():h.distance(g)<touchGamepadSize?(h=h.subtract(g).direction(),touchGamepadButtons[h]=1):h.distance(k)<touchGamepadSize&&!d&&(touchGamepadButtons[9]=1)}a(e);return!0}let c=a;touchGamepadEnable&&(c=b,touchGamepadButtons=[],touchGamepadStick=vec2());document.addEventListener("touchstart",e=>c(e),{passive:!1});document.addEventListener("touchmove",e=>c(e),{passive:!1});document.addEventListener("touchend",e=>c(e),{passive:!1});onmousedown=onmouseup=()=>0;let d}function touchGamepadRender(){if(touchInputEnable&&isTouchDevice&&!headlessMode&&touchGamepadEnable&&touchGamepadTimer.isSet()){var a=percent(touchGamepadTimer.get(),4,3);if(a&&!paused){var b=overlayContext;b.save();b.globalAlpha=a*touchGamepadAlpha;b.strokeStyle="#fff";b.lineWidth=3;b.fillStyle=0<touchGamepadStick.lengthSquared()?"#fff":"#000";b.beginPath();a=vec2(touchGamepadSize,mainCanvasSize.y-touchGamepadSize);if(touchGamepadAnalog)b.arc(a.x,a.y,touchGamepadSize/2,0,9),b.fill();else for(var c=10;c--;){var d=c*PI/4;b.arc(a.x,a.y,.6*touchGamepadSize,d+PI/8,d+PI/8);c%2&&b.arc(a.x,a.y,.33*touchGamepadSize,d,d);1==c&&b.fill()}b.stroke();a=vec2(mainCanvasSize.x-touchGamepadSize,mainCanvasSize.y-touchGamepadSize);for(c=4;c--;)d=a.add(vec2().setDirection(c,touchGamepadSize/2)),b.fillStyle=touchGamepadButtons[c]?"#fff":"#000",b.beginPath(),b.arc(d.x,d.y,touchGamepadSize/4,0,9),b.fill(),b.stroke();b.restore()}}}let audioContext=new AudioContext,audioGainNode;function audioInit(){soundEnable&&!headlessMode&&(audioGainNode=audioContext.createGain(),audioGainNode.connect(audioContext.destination),audioGainNode.gain.value=soundVolume)}class Sound{constructor(a,b=soundDefaultRange,c=soundDefaultTaper){soundEnable&&!headlessMode&&(this.range=b,this.taper=c,this.randomness=0,a&&(this.randomness=void 0!=a[1]?a[1]:.05,a[1]=0,this.sampleChannels=[zzfxG(...a)],this.sampleRate=zzfxR))}play(a,b=1,c=1,d=1,e=!1){if(soundEnable&&!headlessMode&&this.sampleChannels){var f;if(a){if(f=this.range){const g=cameraPos.distanceSquared(a);if(g>f*f)return;b*=percent(g**.5,f,f*this.taper)}f=2*worldToScreen(a).x/mainCanvas.width-1}a=c+c*this.randomness*d*rand(-1,1);this.gainNode=audioContext.createGain();return this.source=playSamples(this.sampleChannels,b,a,f,e,this.sampleRate,this.gainNode)}}setVolume(a=1){this.gainNode&&(this.gainNode.gain.value=a)}stop(){this.source&&this.source.stop();this.source=void 0}getSource(){return this.source}playNote(a,b,c){return this.play(b,c,2**(a/12),0)}getDuration(){return this.sampleChannels&&this.sampleChannels[0].length/this.sampleRate}isLoading(){return!this.sampleChannels}}class SoundWave extends Sound{constructor(a,b=0,c,d,e){super(void 0,c,d);soundEnable&&!headlessMode&&(this.randomness=b,fetch(a).then(f=>f.arrayBuffer()).then(f=>audioContext.decodeAudioData(f)).then(f=>{this.sampleChannels=[];for(let g=f.numberOfChannels;g--;)this.sampleChannels[g]=Array.from(f.getChannelData(g));this.sampleRate=f.sampleRate}).then(()=>e&&e(this)))}}function playAudioFile(a,b=1,c=!1){if(soundEnable&&!headlessMode)return new SoundWave(a,0,0,0,d=>d.play(void 0,b,1,1,c))}class Music extends Sound{constructor(a){super(void 0);soundEnable&&!headlessMode&&(this.randomness=0,this.sampleChannels=zzfxM(...a),this.sampleRate=zzfxR)}playMusic(a,b=!1){return super.play(void 0,a,1,1,b)}}function speak(a,b="",c=1,d=1,e=1){if(soundEnable&&!headlessMode&&speechSynthesis)return a=new SpeechSynthesisUtterance(a),a.lang=b,a.volume=2*c*soundVolume,a.rate=d,a.pitch=e,speechSynthesis.speak(a),a}function speakStop(){speechSynthesis&&speechSynthesis.cancel()}function getNoteFrequency(a,b=220){return b*2**(a/12)}function playSamples(a,b=1,c=1,d=0,e=!1,f=zzfxR,g){if(soundEnable&&!headlessMode){var k=audioContext.createBuffer(a.length,a[0].length,f),h=audioContext.createBufferSource();a.forEach((m,n)=>k.getChannelData(n).set(m));h.buffer=k;h.playbackRate.value=c;h.loop=e;g=g||audioContext.createGain();g.gain.value=b;g.connect(audioGainNode);a=new StereoPannerNode(audioContext,{pan:clamp(d,-1,1)});h.connect(a).connect(g);"running"!=audioContext.state?audioContext.resume().then(()=>h.start()):h.start();return h}}function zzfx(...a){return playSamples([zzfxG(...a)])}const zzfxR=44100;function zzfxG(a=1,b=.05,c=220,d=0,e=0,f=.1,g=0,k=1,h=0,m=0,n=0,l=0,p=0,q=0,r=0,x=0,v=0,D=1,z=0,E=0,A=0){let w=2*PI;var t=zzfxR;let F=h*=500*w/t/t;b=c*=rand(1+b,1-b)*w/t;let C=[],y=0,G=0,u=0,H=1,R=0,S=0,B=0,J;var L=w*abs(A)*2/t,K=Math.cos(L),M=Math.sin(L)/2/2,I=1+M;L=-2*K/I;M=(1-M)/I;let N=(1+sign(A)*K)/2/I;K=-(sign(A)+K)/I;let O=I=0,P=0,Q=0;d=d*t+9;z*=t;e*=t;f*=t;v*=t;m*=500*w/t**3;r*=w/t;n*=w/t;l*=t;p=p*t|0;for(J=d+z+e+f+v|0;u<J;C[u++]=B*a)++S%(100*x|0)||(B=g?1<g?2<g?3<g?Math.sin(y**3):clamp(Math.tan(y),1,-1):1-(2*y/w%2+2)%2:1-4*abs(Math.round(y/w)-y/w):Math.sin(y),B=(p?1-E+E*Math.sin(w*u/p):1)*sign(B)*abs(B)**k*(u<d?u/d:u<d+z?1-(u-d)/z*(1-D):u<d+z+e?D:u<J-v?(J-u-v)/f*D:0),B=v?B/2+(v>u?0:(u<J-v?1:(J-u)/v)*C[u-v|0]/2/a):B,A&&(B=Q=N*I+K*(I=O)+N*(O=B)-M*P-L*(P=Q))),t=(c+=h+=m)*Math.cos(r*G++),y+=t+t*q*Math.sin(u**5),H&&++H>l&&(c+=n,b+=n,H=0),!p||++R%p||(c=b,h=F,H=H||1);return C}function zzfxM(a,b,c,d=125){let e,f,g,k,h,m,n,l,p,q,r,x,v,D=0,z,E=[],A=[],w=[],t=0,F=0,C=1,y={},G=zzfxR/d*60>>2;for(;C;t++)E=[C=l=x=0],c.forEach((u,H)=>{n=b[u][t]||[0,0,0];C|=b[u][t]&&1;z=x+(b[u][0].length-2-(l?0:1))*G;v=H==c.length-1;e=2;for(g=x;e<n.length+v;l=++e){h=n[e];p=e==n.length+v-1&&v||q!=(n[0]||0)||h|0;for(f=0;f<G&&l;f++>G-99&&p&&1>r?r+=1/99:0)m=(1-r)*E[D++]/2||0,A[g]=(A[g]||0)-m*F+m,w[g]=(w[g++]||0)+m*F+m;h&&(r=h%1,F=n[1]||0,h|=0)&&(E=y[[q=n[D=0]||0,h]]=y[[q,h]]||(k=[...a[q]],k[2]*=2**((h-12)/12),0<h?zzfxG(...k):[]))}x=z});return[A,w]}let tileCollision=[],tileCollisionSize=vec2();function initTileCollision(a){tileCollisionSize=a;tileCollision=[];for(a=tileCollision.length=tileCollisionSize.area();a--;)tileCollision[a]=0}function setTileCollisionData(a,b=0){a.arrayCheck(tileCollisionSize)&&(tileCollision[(a.y|0)*tileCollisionSize.x+a.x|0]=b)}function getTileCollisionData(a){return 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,vec2(b,e))))return!0}return!1}function tileCollisionRaycast(a,b,c){const d=b.subtract(a),e=d.length();var f=d.normalize();f=vec2(abs(1/f.x),abs(1/f.y));let g=a.floor(),k=f.x*(0>d.x?a.x-g.x:g.x-a.x+1),h=f.y*(0>d.y?a.y-g.y:g.y-a.y+1);for(;;){const m=getTileCollisionData(g);if(m&&(!c||c.collideWithTile(m,g)))return debugRaycast&&debugLine(a,b,"#f00",.02),debugRaycast&&debugPoint(g.add(vec2(.5)),"#ff0"),g.add(vec2(.5));if(k>e&&h>e)break;k>h?(g.y+=sign(d.y),h+=f.y):(g.x+=sign(d.x),k+=f.x)}debugRaycast&&debugLine(a,b,"#00f",.02)}class TileLayerData{constructor(a,b=0,c=!1,d=new Color){this.tile=a;this.direction=b;this.mirror=c;this.color=d}clear(){this.tile=this.direction=0;this.mirror=!1;this.color=new Color}}class TileLayer extends EngineObject{constructor(a,b=tileCollisionSize,c=tile(),d=vec2(1),e=0){super(a,b,c,0,void 0,e);this.canvas=document.createElement("canvas");this.context=this.canvas.getContext("2d");this.scale=d;this.isOverlay=!1;this.data=[];for(a=this.size.area();a--;)this.data.push(new TileLayerData);headlessMode&&(this.redraw=()=>{},this.render=()=>{},this.redrawStart=()=>{},this.redrawEnd=()=>{},this.drawTileData=()=>{},this.drawCanvas2D=()=>{})}setData(a,b,c=!1){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,"must call redrawEnd() after drawing tiles");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(!0);for(let a=this.size.x;a--;)for(let b=this.size.y;b--;)this.drawTileData(vec2(a,b),!1);this.redrawEnd()}redrawStart(a=!1){this.savedRenderSettings=[mainCanvas,mainContext,mainCanvasSize,cameraPos,cameraScale];mainCanvas=this.canvas;mainContext=this.context;mainCanvasSize=this.size.multiply(this.tileInfo.size);cameraPos=this.size.scale(.5);cameraScale=this.tileInfo.size.x;a&&(mainCanvas.width=mainCanvasSize.x,mainCanvas.height=mainCanvasSize.y);this.context.imageSmoothingEnabled=!tilesPixelated;glPreRender()}redrawEnd(){ASSERT(mainContext==this.context,"must call redrawStart() before drawing tiles");glCopyToContext(mainContext,!0);[mainCanvas,mainContext,mainCanvasSize,cameraPos,cameraScale]=this.savedRenderSettings}drawTileData(a,b=!0){var c=this.tileInfo.size;b&&(b=a.multiply(c),this.context.clearRect(b.x,this.canvas.height-b.y,c.x,-c.y));b=this.getData(a);void 0!=b.tile&&(ASSERT(mainContext==this.context,"must call redrawStart() before drawing tiles"),a=a.add(vec2(.5)),c=tile(b.tile,c,this.tileInfo.textureIndex),drawTile(a,vec2(1),c,b.color,b.direction*PI/2,b.mirror))}drawCanvas2D(a,b,c,d,e){const f=this.context;f.save();a=a.subtract(this.pos).multiply(this.tileInfo.size);b=b.multiply(this.tileInfo.size);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,d=new Color,e,f){this.drawCanvas2D(a,b,e,f,g=>{const k=c&&c.getTextureInfo();k?(g.globalAlpha=d.a,g.drawImage(k.image,c.pos.x,c.pos.y,c.size.x,c.size.y,-.5,-.5,1,1),g.globalAlpha=1):(g.fillStyle=d,g.fillRect(-.5,-.5,1,1))})}drawRect(a,b,c,d){this.drawTile(a,b,void 0,c,d)}}class ParticleEmitter extends EngineObject{constructor(a,b,c=0,d=0,e=100,f=PI,g,k=new Color,h=new Color,m=new Color(1,1,1,0),n=new Color(1,1,1,0),l=.5,p=.1,q=1,r=.1,x=.05,v=1,D=1,z=0,E=PI,A=.1,w=.2,t=!1,F=!1,C=!0,y=F?1e9:0,G=!1){super(a,vec2(),g,b,void 0,y);this.emitSize=c;this.emitTime=d;this.emitRate=e;this.emitConeAngle=f;this.colorStartA=k;this.colorStartB=h;this.colorEndA=m;this.colorEndB=n;this.randomColorLinear=C;this.particleTime=l;this.sizeStart=p;this.sizeEnd=q;this.speed=r;this.angleSpeed=x;this.damping=v;this.angleDamping=D;this.gravityScale=z;this.particleConeAngle=E;this.fadeRate=A;this.randomness=w;this.collideTiles=t;this.additive=F;this.localSpace=G;this.trailScale=0;this.particleCreateCallback=this.particleDestroyCallback=void 0;this.emitTimeBuffer=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="number"===typeof this.emitSize?randInCircle(this.emitSize/2):vec2(rand(-.5,.5),rand(-.5,.5)).multiply(this.emitSize).rotate(this.angle);let b=rand(this.particleConeAngle,-this.particleConeAngle);this.localSpace||(a=this.pos.add(a),b+=this.angle);const c=this.randomness;var d=l=>l+l*rand(c,-c);const e=d(this.particleTime),f=d(this.sizeStart),g=d(this.sizeEnd),k=d(this.speed);d=d(this.angleSpeed)*randSign();var h=rand(this.emitConeAngle,-this.emitConeAngle);const m=randColor(this.colorStartA,this.colorStartB,this.randomColorLinear),n=randColor(this.colorEndA,this.colorEndB,this.randomColorLinear);h=this.localSpace?h:this.angle+h;a=new Particle(a,this.tileInfo,b,m,n,e,f,g,this.fadeRate,this.additive,this.trailScale,this.localSpace&&this,this.particleDestroyCallback);a.velocity=vec2().setAngle(h,k);a.angleVelocity=d;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.renderOrder=this.renderOrder;a.mirror=!!randInt(2);this.particleCreateCallback&&this.particleCreateCallback(a);return a}render(){}}class Particle extends EngineObject{constructor(a,b,c,d,e,f,g,k,h,m,n,l,p){super(a,vec2(),b,c);this.colorStart=d;this.colorEndDelta=e.subtract(d);this.lifeTime=f;this.sizeStart=g;this.sizeEndDelta=k-g;this.fadeRate=h;this.additive=m;this.trailScale=n;this.localSpaceEmitter=l;this.destroyCallback=p;this.clampSpeedLinear=!1}render(){const a=min((time-this.spawnTime)/this.lifeTime,1),b=vec2(this.sizeStart+a*this.sizeEndDelta);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(!0);let d=this.pos,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));var g=f.length();g&&(f=f.scale(1/g),g*=this.trailScale,b.y=max(b.x,g),e=f.angle(),drawTile(d.add(f.multiply(vec2(0,-g/2))),b,this.tileInfo,c,e,this.mirror))}else drawTile(d,b,this.tileInfo,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)}}const medals={};let medalsDisplayQueue=[],medalsSaveName,medalsDisplayTimeLast;function medalsInit(a){medalsSaveName=a;debugMedals||medalsForEach(b=>b.unlocked=!!localStorage[b.storageKey()]);engineAddPlugin(void 0,function(){if(medalsDisplayQueue.length){var b=medalsDisplayQueue[0],c=timeReal-medalsDisplayTimeLast;if(medalsDisplayTimeLast)if(c>medalDisplayTime)medalsDisplayTimeLast=0,medalsDisplayQueue.shift();else{const d=medalDisplayTime-medalDisplaySlideTime;b.render(c<medalDisplaySlideTime?1-c/medalDisplaySlideTime:c>d?(c-d)/medalDisplaySlideTime:0)}else medalsDisplayTimeLast=timeReal}})}function medalsForEach(a){Object.values(medals).forEach(b=>a(b))}class Medal{constructor(a,b,c="",d="🏆",e){ASSERT(0<=a&&!medals[a]);this.id=a;this.name=b;this.description=c;this.icon=d;this.unlocked=!1;e&&((this.image=new Image).src=e);medals[a]=this}unlock(){medalsPreventUnlock||this.unlocked||(ASSERT(medalsSaveName,"save name must be set"),localStorage[this.storageKey()]=this.unlocked=!0,medalsDisplayQueue.push(this))}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).toString();b.strokeStyle=new Color(0,0,0).toString();b.lineWidth=3;b.rect(d,a,c,medalDisplaySize.y);b.fill();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,void 0,"left");c.y+=32;drawTextScreen(this.description,c,24,new Color(0,0,0),0,void 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}}let glCanvas,glContext,glAntialias=!0,glShader,glActiveTexture,glArrayBuffer,glGeometryBuffer,glPositionData,glColorData,glInstanceCount,glAdditive,glBatchAdditive;function glInit(){if(glEnable&&!headlessMode){glCanvas=document.createElement("canvas");glContext=glCanvas.getContext("webgl2",{antialias:glAntialias});var a=mainCanvas.parentElement;glOverlay&&a.appendChild(glCanvas);glShader=glCreateProgram("#version 300 es\nprecision highp float;uniform mat4 m;in vec2 g;in vec4 p,u,c,a;in float r;out vec2 v;out vec4 d,e;void main(){vec2 s=(g-.5)*p.zw;gl_Position=m*vec4(p.xy+s*cos(r)-vec2(-s.y,s)*sin(r),1,1);v=mix(u.xw,u.zy,g);d=c;e=a;}","#version 300 es\nprecision highp float;uniform sampler2D s;in vec2 v;in vec4 d,e;out vec4 c;void main(){c=texture(s,v)*d+e;}");a=new ArrayBuffer(gl_INSTANCE_BUFFER_SIZE);glPositionData=new Float32Array(a);glColorData=new Uint32Array(a);glArrayBuffer=glContext.createBuffer();glGeometryBuffer=glContext.createBuffer();a=new Float32Array([glInstanceCount=0,0,1,0,0,1,1,1]);glContext.bindBuffer(gl_ARRAY_BUFFER,glGeometryBuffer);glContext.bufferData(gl_ARRAY_BUFFER,a,gl_STATIC_DRAW)}}function glPreRender(){if(glEnable&&!headlessMode){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);textureInfos[0]&&glContext.bindTexture(gl_TEXTURE_2D,glActiveTexture=textureInfos[0].glTexture);var a=glAdditive=glBatchAdditive=0,b=(d,e,f,g)=>{d=glContext.getAttribLocation(glShader,d);const k=f&&gl_INSTANCE_BYTE_STRIDE,h=f&&1,m=1==f;glContext.enableVertexAttribArray(d);glContext.vertexAttribPointer(d,g,e,m,k,a);glContext.vertexAttribDivisor(d,h);a+=g*f};glContext.bindBuffer(gl_ARRAY_BUFFER,glGeometryBuffer);b("g",gl_FLOAT,0,2);glContext.bindBuffer(gl_ARRAY_BUFFER,glArrayBuffer);glContext.bufferData(gl_ARRAY_BUFFER,gl_INSTANCE_BUFFER_SIZE,gl_DYNAMIC_DRAW);b("p",gl_FLOAT,4,4);b("u",gl_FLOAT,4,4);b("c",gl_UNSIGNED_BYTE,1,4);b("a",gl_UNSIGNED_BYTE,1,4);b("r",gl_FLOAT,4,1);b=vec2(2*cameraScale).divide(mainCanvasSize);var c=vec2(-1).subtract(cameraPos.multiply(b));glContext.uniformMatrix4fv(glContext.getUniformLocation(glShader,"m"),!1,[b.x,0,0,0,0,b.y,0,0,1,1,1,1,c.x,c.y,0,0])}}function glSetTexture(a){headlessMode||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=new Uint8Array([255,255,255,255]),glContext.texImage2D(gl_TEXTURE_2D,0,gl_RGBA,1,1,0,gl_RGBA,gl_UNSIGNED_BYTE,a));a=tilesPixelated?gl_NEAREST:gl_LINEAR;glContext.texParameteri(gl_TEXTURE_2D,gl_TEXTURE_MIN_FILTER,a);glContext.texParameteri(gl_TEXTURE_2D,gl_TEXTURE_MAG_FILTER,a);return b}function glFlush(){if(glInstanceCount){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);glContext.drawArraysInstanced(gl_TRIANGLE_STRIP,0,4,glInstanceCount);showWatermark&&(drawCount+=glInstanceCount);glInstanceCount=0;glBatchAdditive=glAdditive}}function glCopyToContext(a,b=!1){glEnable&&(glInstanceCount||b)&&(glFlush(),glOverlay&&!b||a.drawImage(glCanvas,0,0))}function glSetAntialias(a=!0){ASSERT(!glCanvas,"must be called before engineInit");glAntialias=a}function glDraw(a,b,c,d,e,f,g,k,h,m,n=0){ASSERT("number"==typeof m&&"number"==typeof n,"invalid color");(glInstanceCount>=gl_MAX_INSTANCES||glBatchAdditive!=glAdditive)&&glFlush();let l=glInstanceCount*gl_INDICIES_PER_INSTANCE;glPositionData[l++]=a;glPositionData[l++]=b;glPositionData[l++]=c;glPositionData[l++]=d;glPositionData[l++]=f;glPositionData[l++]=g;glPositionData[l++]=k;glPositionData[l++]=h;glColorData[l++]=m;glColorData[l++]=n;glPositionData[l++]=e;glInstanceCount++}const gl_ONE=1,gl_TRIANGLE_STRIP=5,gl_SRC_ALPHA=770,gl_ONE_MINUS_SRC_ALPHA=771,gl_BLEND=3042,gl_TEXTURE_2D=3553,gl_UNSIGNED_BYTE=5121,gl_FLOAT=5126,gl_RGBA=6408,gl_NEAREST=9728,gl_LINEAR=9729,gl_TEXTURE_MAG_FILTER=10240,gl_TEXTURE_MIN_FILTER=10241,gl_COLOR_BUFFER_BIT=16384,gl_TEXTURE0=33984,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_INDICIES_PER_INSTANCE=11,gl_MAX_INSTANCES=1e4,gl_INSTANCE_BYTE_STRIDE=4*gl_INDICIES_PER_INSTANCE,gl_INSTANCE_BUFFER_SIZE=gl_MAX_INSTANCES*gl_INSTANCE_BYTE_STRIDE,engineName="LittleJS",engineVersion="1.11.1",frameRate=60,timeDelta=1/frameRate;let engineObjects=[],engineObjectsCollide=[],frame=0,time=0,timeReal=0,paused=!1;function setPaused(a){paused=a}let frameTimeLastMS=0,frameTimeBufferMS=0,averageFPS=0;const pluginUpdateList=[],pluginRenderList=[];function engineAddPlugin(a,b){ASSERT(!pluginUpdateList.includes(a));ASSERT(!pluginRenderList.includes(b));a&&pluginUpdateList.push(a);b&&pluginRenderList.push(b)}function engineInit(a,b,c,d,e,f=[],g=document.body){function k(n=0){var l=n-frameTimeLastMS;frameTimeLastMS=n;if(debug||showWatermark)averageFPS=lerp(.05,averageFPS,1e3/(l||1));n=debug&&keyIsDown("Equal");const p=debug&&keyIsDown("Minus");debug&&(l*=n?5:p?.2:1);timeReal+=l/1e3;frameTimeBufferMS+=paused?0:l;n||(frameTimeBufferMS=min(frameTimeBufferMS,50));h();if(paused){for(const r of engineObjects)r.parent||r.updateTransforms();inputUpdate();pluginUpdateList.forEach(r=>r());debugUpdate();c();inputUpdatePost()}else{l=0;0>frameTimeBufferMS&&-9<frameTimeBufferMS&&(l=frameTimeBufferMS,frameTimeBufferMS=0);for(;0<=frameTimeBufferMS;frameTimeBufferMS-=1e3/frameRate)time=frame++/frameRate,inputUpdate(),b(),pluginUpdateList.forEach(r=>r()),engineObjectsUpdate(),debugUpdate(),c(),inputUpdatePost();frameTimeBufferMS+=l}if(!headlessMode){mainCanvasSize=vec2(mainCanvas.width,mainCanvas.height);overlayContext.imageSmoothingEnabled=mainContext.imageSmoothingEnabled=!tilesPixelated;glPreRender();d();engineObjects.sort((r,x)=>r.renderOrder-x.renderOrder);for(var q of engineObjects)q.destroyed||q.render();e();pluginRenderList.forEach(r=>r());touchGamepadRender();debugRender();glCopyToContext(mainContext);showWatermark&&(overlayContext.textAlign="right",overlayContext.textBaseline="top",overlayContext.font="1em monospace",overlayContext.fillStyle="#000",q=engineName+" v"+engineVersion+" / "+drawCount+" / "+engineObjects.length+" / "+averageFPS.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(k)}function h(){if(!headlessMode){if(canvasFixedSize.x){mainCanvas.width=canvasFixedSize.x;mainCanvas.height=canvasFixedSize.y;const n=innerWidth/innerHeight,l=mainCanvas.width/mainCanvas.height;(glCanvas||mainCanvas).style.width=mainCanvas.style.width=overlayCanvas.style.width=n<l?"100%":"";(glCanvas||mainCanvas).style.height=mainCanvas.style.height=overlayCanvas.style.height=n<l?"":"100%"}else mainCanvas.width=min(innerWidth,canvasMaxSize.x),mainCanvas.height=min(innerHeight,canvasMaxSize.y);overlayCanvas.width=mainCanvas.width;overlayCanvas.height=mainCanvas.height;mainCanvasSize=vec2(mainCanvas.width,mainCanvas.height)}}function m(){new Promise(n=>n(a())).then(k)}ASSERT(Array.isArray(f),"pass in images as array");headlessMode?m():(g.style.cssText="margin:0;overflow:hidden;width:100vw;height:100vh;display:flex;align-items:center;justify-content:center;background:#000;"+(canvasPixelated?"image-rendering:pixelated;":"")+"user-select:none;-webkit-user-select:none;"+(touchInputEnable?"touch-action:none;-webkit-touch-callout:none":""),g.appendChild(mainCanvas=document.createElement("canvas")),mainContext=mainCanvas.getContext("2d"),inputInit(),audioInit(),debugInit(),glInit(),g.appendChild(overlayCanvas=document.createElement("canvas")),overlayContext=overlayCanvas.getContext("2d"),mainCanvas.style.cssText=overlayCanvas.style.cssText="position:absolute",glCanvas&&(glCanvas.style.cssText="position:absolute"),h(),g=f.map((n,l)=>new Promise(p=>{const q=new Image;q.onerror=q.onload=()=>{textureInfos[l]=new TextureInfo(q);p()};q.src=n})),f.length||g.push(new Promise(n=>{textureInfos[0]=new TextureInfo(new Image);n()})),showSplashScreen&&g.push(new Promise(n=>{function l(){clearInput();drawEngineSplashScreen(p+=.01);1<p?n():setTimeout(l,16)}let p=0;console.log(`${engineName} Engine v${engineVersion}`);l()})),Promise.all(g).then(m))}function engineObjectsUpdate(){function a(b){if(!b.destroyed){b.update();for(const c of b.children)a(c)}}engineObjectsCollide=engineObjects.filter(b=>b.collideSolidObjects);for(const b of engineObjects)b.parent||(a(b),b.updateTransforms());engineObjects=engineObjects.filter(b=>!b.destroyed)}function engineObjectsDestroy(){for(const a of engineObjects)a.parent||a.destroy();engineObjects=engineObjects.filter(a=>!a.destroyed)}function engineObjectsCollect(a,b,c=engineObjects){const d=[];if(a)if(b instanceof Vector2)for(const e of c)isOverlapping(a,b,e.pos,e.size)&&d.push(e);else{b*=b;for(const e of c)a.distanceSquared(e.pos)<b&&d.push(e)}else for(const e of c)d.push(e);return d}function engineObjectsCallback(a,b,c,d=engineObjects){engineObjectsCollect(a,b,d).forEach(e=>c(e))}function engineObjectsRaycast(a,b,c=engineObjects){const d=[];for(const e of c)e.collideRaycast&&isIntersecting(a,b,e.pos,e.size)&&(debugRaycast&&debugRect(e.pos,e.size,"#f00"),d.push(e));debugRaycast&&debugLine(a,b,d.length?"#f00":"#00f",.02);return d}function drawEngineSplashScreen(a){const b=overlayContext;var c=overlayCanvas.width=innerWidth,d=overlayCanvas.height=innerHeight,e=percent(a,1,.8),f=percent(a,0,.5),g=b.createRadialGradient(c/2,d/2,0,c/2,d/2,.7*Math.hypot(c,d));g.addColorStop(0,hsl(0,0,lerp(f,0,e/2),e).toString());g.addColorStop(1,hsl(0,0,0,e).toString());b.save();b.fillStyle=g;b.fillRect(0,0,c,d);g=(h,m,n,l,p)=>{b.beginPath();b.rect(h,m,n,p?l*k:l);(b.fillStyle=p)?b.fill():b.stroke()};f=(h,m,n,l=0,p=2*PI,q,r)=>{const x=(l+p)/2;l=k*(p-l)/2;b.beginPath();r&&b.lineTo(h,m);b.arc(h,m,n,x-l,x+l);(b.fillStyle=q)?b.fill():b.stroke()};e=(h=0,m=0)=>hsl([.98,.3,.57,.14][h%4]-10,.8,[0,.3,.5,.8,.9][m]).toString();a=wave(1,1,a);const k=percent(a,.1,.5);b.translate(c/2,d/2);c=min(6,min(c,d)/99);b.scale(c,c);b.translate(-40,-35);b.lineJoin=b.lineCap="round";b.lineWidth=.1+1.9*k;c=percent(a,.1,1);b.setLineDash([99*c,99]);g(7,16,18,-8,e(2,2));g(7,8,18,4,e(2,3));g(25,8,8,8,e(2,1));g(25,8,-18,8);g(25,8,8,8);g(25,16,7,23,e());g(11,39,14,-23,e(1,1));g(11,16,14,18,e(1,2));g(11,16,14,8,e(1,3));g(25,16,-14,24);g(15,29,6,-9,e(2,2));f(15,21,5,0,PI/2,e(2,4),1);g(21,21,-6,9);g(37,14,9,6,e(3,2));g(37,14,4.5,6,e(3,3));g(37,14,9,6);g(50,20,10,-8,e(0,1));g(50,20,6.5,-8,e(0,2));g(50,20,3.5,-8,e(0,3));g(50,20,10,-8);f(55,2,11.4,.5,PI-.5,e(3,3));f(55,2,11.4,.5,PI/2,e(3,2),1);f(55,2,11.4,.5,PI-.5);g(45,7,20,-7,e(0,2));g(45,-1,20,4,e(0,3));g(45,-1,20,8);for(c=5;c--;)f(60-6*c,30,9.9,0,2*PI,e(c+2,3)),f(60-6*c,30,10,-.5,PI+.5,e(c+2,2)),f(60-6*c,30,10.1,.5,PI-.5,e(c+2,1));f(36,30,10,PI/2,3*PI/2);f(48,30,10,PI/2,3*PI/2);f(60,30,10);b.beginPath();b.lineTo(36,20);b.lineTo(60,20);b.stroke();f(60,30,4,PI,3*PI,e(3,2));f(60,30,4,PI,2*PI,e(3,3));f(60,30,4,PI,3*PI);for(c=6;c--;)b.beginPath(),b.lineTo(53,54),b.lineTo(53,40),b.lineTo(53+(1+2.9*c)*k,40),b.lineTo(53+(4+3.5*c)*k,54),b.fillStyle=e(0,c%2+2),b.fill(),c%2&&b.stroke();g(6,40,5,5);g(6,40,5,5,e());g(15,54,38,-14,e());for(g=3;g--;)for(c=2;c--;)f(15*g+15,47,c?7:1,PI,3*PI,e(g,3)),b.stroke(),f(15*g+15,47,c?7:1,0,PI,e(g,2)),b.stroke();b.beginPath();b.lineTo(6,40);b.lineTo(68,40);b.stroke();b.beginPath();b.lineTo(77,54);b.lineTo(4,54);b.stroke();f=engineName;b.font="900 16px arial";b.textAlign="center";b.textBaseline="top";b.lineWidth=.1+3.9*k;g=0;for(c=0;c<f.length;++c)g+=b.measureText(f[c]).width;for(c=2;c--;)for(let h=0,m=41-g/2;h<f.length;++h)b.fillStyle=e(h,2),d=b.measureText(f[h]).width,b[c?"strokeText":"fillText"](f[h],m+d/2,55.5,17*k),m+=d;b.restore()}
@@ -1001,12 +1001,18 @@ let canvasMaxSize = vec2(1920, 1080);
1001
1001
  * @memberof Settings */
1002
1002
  let canvasFixedSize = vec2();
1003
1003
 
1004
- /** Disables filtering for crisper pixel art if true
1004
+ /** Use nearest neighbor scaling algorithm for canvas for more pixelated look
1005
1005
  * @type {Boolean}
1006
1006
  * @default
1007
1007
  * @memberof Settings */
1008
1008
  let canvasPixelated = true;
1009
1009
 
1010
+ /** Disables texture filtering for crisper pixel art
1011
+ * @type {Boolean}
1012
+ * @default
1013
+ * @memberof Settings */
1014
+ let tilesPixelated = true;
1015
+
1010
1016
  /** Default font used for text rendering
1011
1017
  * @type {String}
1012
1018
  * @default
@@ -1255,11 +1261,16 @@ function setCanvasMaxSize(size) { canvasMaxSize = size; }
1255
1261
  * @memberof Settings */
1256
1262
  function setCanvasFixedSize(size) { canvasFixedSize = size; }
1257
1263
 
1258
- /** Disables anti aliasing for pixel art if true
1264
+ /** Use nearest neighbor scaling algorithm for canvas for more pixelated look
1259
1265
  * @param {Boolean} pixelated
1260
1266
  * @memberof Settings */
1261
1267
  function setCanvasPixelated(pixelated) { canvasPixelated = pixelated; }
1262
1268
 
1269
+ /** Disables texture filtering for crisper pixel art
1270
+ * @param {Boolean} pixelated
1271
+ * @memberof Settings */
1272
+ function setTilesPixelated(pixelated) { tilesPixelated = pixelated; }
1273
+
1263
1274
  /** Set default font used for text rendering
1264
1275
  * @param {String} font
1265
1276
  * @memberof Settings */
@@ -2316,6 +2327,24 @@ function setBlendMode(additive, useWebGL=glEnable, context)
2316
2327
  }
2317
2328
  }
2318
2329
 
2330
+ /** Draw text on main canvas in world space
2331
+ * Automatically splits new lines into rows
2332
+ * @param {String} text
2333
+ * @param {Vector2} pos
2334
+ * @param {Number} [size]
2335
+ * @param {Color} [color=(1,1,1,1)]
2336
+ * @param {Number} [lineWidth]
2337
+ * @param {Color} [lineColor=(0,0,0,1)]
2338
+ * @param {CanvasTextAlign} [textAlign='center']
2339
+ * @param {String} [font=fontDefault]
2340
+ * @param {Number} [maxWidth]
2341
+ * @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} [context=mainContext]
2342
+ * @memberof Draw */
2343
+ function drawText(text, pos, size=1, color, lineWidth=0, lineColor, textAlign, font, maxWidth, context=mainContext)
2344
+ {
2345
+ drawTextScreen(text, worldToScreen(pos), size*cameraScale, color, lineWidth*cameraScale, lineColor, textAlign, font, maxWidth, context);
2346
+ }
2347
+
2319
2348
  /** Draw text on overlay canvas in world space
2320
2349
  * Automatically splits new lines into rows
2321
2350
  * @param {String} text
@@ -2326,12 +2355,11 @@ function setBlendMode(additive, useWebGL=glEnable, context)
2326
2355
  * @param {Color} [lineColor=(0,0,0,1)]
2327
2356
  * @param {CanvasTextAlign} [textAlign='center']
2328
2357
  * @param {String} [font=fontDefault]
2329
- * @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} [context=overlayContext]
2330
2358
  * @param {Number} [maxWidth]
2331
2359
  * @memberof Draw */
2332
- function drawText(text, pos, size=1, color, lineWidth=0, lineColor, textAlign, font, context, maxWidth)
2360
+ function drawTextOverlay(text, pos, size=1, color, lineWidth=0, lineColor, textAlign, font, maxWidth)
2333
2361
  {
2334
- drawTextScreen(text, worldToScreen(pos), size*cameraScale, color, lineWidth*cameraScale, lineColor, textAlign, font, context, maxWidth);
2362
+ drawText(text, pos, size, color, lineWidth, lineColor, textAlign, font, maxWidth, overlayContext);
2335
2363
  }
2336
2364
 
2337
2365
  /** Draw text on overlay canvas in screen space
@@ -2344,10 +2372,10 @@ function drawText(text, pos, size=1, color, lineWidth=0, lineColor, textAlign, f
2344
2372
  * @param {Color} [lineColor=(0,0,0,1)]
2345
2373
  * @param {CanvasTextAlign} [textAlign]
2346
2374
  * @param {String} [font=fontDefault]
2347
- * @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} [context=overlayContext]
2348
2375
  * @param {Number} [maxWidth]
2376
+ * @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} [context=overlayContext]
2349
2377
  * @memberof Draw */
2350
- function drawTextScreen(text, pos, size=1, color=new Color, lineWidth=0, lineColor=new Color(0,0,0), textAlign='center', font=fontDefault, context=overlayContext, maxWidth=undefined)
2378
+ function drawTextScreen(text, pos, size=1, color=new Color, lineWidth=0, lineColor=new Color(0,0,0), textAlign='center', font=fontDefault, maxWidth=undefined, context=overlayContext)
2351
2379
  {
2352
2380
  context.fillStyle = color.toString();
2353
2381
  context.lineWidth = lineWidth;
@@ -2423,7 +2451,6 @@ class FontImage
2423
2451
  {
2424
2452
  const context = this.context;
2425
2453
  context.save();
2426
- context.imageSmoothingEnabled = !canvasPixelated;
2427
2454
 
2428
2455
  const size = this.tileSize;
2429
2456
  const drawSize = size.add(this.paddingSize).scale(scale);
@@ -3052,9 +3079,6 @@ class Sound
3052
3079
 
3053
3080
  /** @property {Number} - How much to randomize frequency each time sound plays */
3054
3081
  this.randomness = 0;
3055
-
3056
- /** @property {GainNode} - Gain node for this sound */
3057
- this.gainNode = audioContext.createGain();
3058
3082
 
3059
3083
  if (zzfxSound)
3060
3084
  {
@@ -3101,13 +3125,19 @@ class Sound
3101
3125
 
3102
3126
  // play the sound
3103
3127
  const playbackRate = pitch + pitch * this.randomness*randomnessScale*rand(-1,1);
3104
- return this.source = playSamples(this.sampleChannels, volume, playbackRate, pan, loop, this.sampleRate, this.gainNode);
3128
+ this.gainNode = audioContext.createGain();
3129
+ this.source = playSamples(this.sampleChannels, volume, playbackRate, pan, loop, this.sampleRate, this.gainNode);
3130
+ return this.source;
3105
3131
  }
3106
3132
 
3107
- /** Set the sound volume
3133
+ /** Set the sound volume of the most recently played instance of this sound
3108
3134
  * @param {Number} [volume] - How much to scale volume by
3109
3135
  */
3110
- setVolume(volume=1) { this.gainNode.gain.value = volume; }
3136
+ setVolume(volume=1)
3137
+ {
3138
+ if (this.gainNode)
3139
+ this.gainNode.gain.value = volume;
3140
+ }
3111
3141
 
3112
3142
  /** Stop the last instance of this sound that was played */
3113
3143
  stop()
@@ -3861,7 +3891,7 @@ class TileLayer extends EngineObject
3861
3891
  }
3862
3892
 
3863
3893
  // disable smoothing for pixel art
3864
- this.context.imageSmoothingEnabled = !canvasPixelated;
3894
+ this.context.imageSmoothingEnabled = !tilesPixelated;
3865
3895
 
3866
3896
  // setup gl rendering if enabled
3867
3897
  glPreRender();
@@ -4682,7 +4712,7 @@ function glCreateTexture(image)
4682
4712
  }
4683
4713
 
4684
4714
  // use point filtering for pixelated rendering
4685
- const filter = canvasPixelated ? gl_NEAREST : gl_LINEAR;
4715
+ const filter = tilesPixelated ? gl_NEAREST : gl_LINEAR;
4686
4716
  glContext.texParameteri(gl_TEXTURE_2D, gl_TEXTURE_MIN_FILTER, filter);
4687
4717
  glContext.texParameteri(gl_TEXTURE_2D, gl_TEXTURE_MAG_FILTER, filter);
4688
4718
  return texture;
@@ -4831,7 +4861,7 @@ const engineName = 'LittleJS';
4831
4861
  * @type {String}
4832
4862
  * @default
4833
4863
  * @memberof Engine */
4834
- const engineVersion = '1.10.7';
4864
+ const engineVersion = '1.11.1';
4835
4865
 
4836
4866
  /** Frames per second to update
4837
4867
  * @type {Number}
@@ -4923,7 +4953,8 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
4923
4953
  mainCanvasSize = vec2(mainCanvas.width, mainCanvas.height);
4924
4954
 
4925
4955
  // disable smoothing for pixel art
4926
- mainContext.imageSmoothingEnabled = !canvasPixelated;
4956
+ overlayContext.imageSmoothingEnabled =
4957
+ mainContext.imageSmoothingEnabled = !tilesPixelated;
4927
4958
 
4928
4959
  // setup gl rendering if enabled
4929
4960
  glPreRender();
@@ -5076,6 +5107,7 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
5076
5107
  'align-items:center;' + // horizontal center
5077
5108
  'justify-content:center;' + // vertical center
5078
5109
  'background:#000;' + // set background color
5110
+ (canvasPixelated ? 'image-rendering:pixelated;' : '') + // pixel art
5079
5111
  'user-select:none;' + // prevent hold to select
5080
5112
  '-webkit-user-select:none;' + // compatibility for ios
5081
5113
  (!touchInputEnable ? '' : // no touch css setttings