littlejsengine 1.9.8 → 1.9.9

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.
@@ -85,6 +85,11 @@ declare module "littlejsengine" {
85
85
  * @param {Array} [objects=engineObjects] - List of objects to check
86
86
  * @memberof Engine */
87
87
  export function engineObjectsCallback(pos?: Vector2, size?: number | Vector2, callbackFunction?: Function, objects?: any[]): void;
88
+ /** Add a new update function for a plugin
89
+ * @param {Function} [updateFunction]
90
+ * @param {Function} [renderFunction]
91
+ * @memberof Engine */
92
+ export function engineAddPlugin(updateFunction?: Function, renderFunction?: Function): void;
88
93
  /**
89
94
  * LittleJS Debug System
90
95
  * - Press Esc to show debug overlay with mouse pick
@@ -470,6 +475,10 @@ declare module "littlejsengine" {
470
475
  * @param {Number} scale
471
476
  * @memberof Settings */
472
477
  export function setParticleEmitRateScale(scale: number): void;
478
+ /** Set if touch input is allowed
479
+ * @param {Boolean} enable
480
+ * @memberof Settings */
481
+ export function setTouchInputEnable(enable: boolean): void;
473
482
  /** Set if gamepads are enabled
474
483
  * @param {Boolean} enable
475
484
  * @memberof Settings */
@@ -1306,6 +1315,7 @@ declare module "littlejsengine" {
1306
1315
  * - Tracks keyboard down, pressed, and released
1307
1316
  * - Tracks mouse buttons, position, and wheel
1308
1317
  * - Tracks multiple analog gamepads
1318
+ * - Touch input is handled as mouse input
1309
1319
  * - Virtual gamepad for touch devices
1310
1320
  * @namespace Input
1311
1321
  */
@@ -1335,6 +1345,7 @@ declare module "littlejsengine" {
1335
1345
  * - Tracks keyboard down, pressed, and released
1336
1346
  * - Tracks mouse buttons, position, and wheel
1337
1347
  * - Tracks multiple analog gamepads
1348
+ * - Touch input is handled as mouse input
1338
1349
  * - Virtual gamepad for touch devices
1339
1350
  * @namespace Input
1340
1351
  */
@@ -1500,6 +1500,13 @@ let gamepadDirectionEmulateStick = true;
1500
1500
  * @memberof Settings */
1501
1501
  let inputWASDEmulateDirection = true;
1502
1502
 
1503
+ /** True if touch input is enabled for mobile devices
1504
+ * - Touch events will be routed to mouse events
1505
+ * @type {Boolean}
1506
+ * @default
1507
+ * @memberof Settings */
1508
+ let touchInputEnable = true;
1509
+
1503
1510
  /** True if touch gamepad should appear on mobile devices
1504
1511
  * - Supports left analog stick, 4 face buttons and start button (button 9)
1505
1512
  * - Must be set by end of gameInit to be activated
@@ -1715,6 +1722,11 @@ function setGamepadDirectionEmulateStick(enable) { gamepadDirectionEmulateStick
1715
1722
  * @memberof Settings */
1716
1723
  function setInputWASDEmulateDirection(enable) { inputWASDEmulateDirection = enable; }
1717
1724
 
1725
+ /** Set if touch input is allowed
1726
+ * @param {Boolean} enable
1727
+ * @memberof Settings */
1728
+ function setTouchInputEnable(enable) { touchInputEnable = enable; }
1729
+
1718
1730
  /** Set if touch gamepad should appear on mobile devices
1719
1731
  * @param {Boolean} enable
1720
1732
  * @memberof Settings */
@@ -2235,13 +2247,16 @@ class EngineObject
2235
2247
  /** Render debug info for this object */
2236
2248
  renderDebugInfo()
2237
2249
  {
2238
- // show object info for debugging
2239
- const size = vec2(max(this.size.x, .2), max(this.size.y, .2));
2240
- const color1 = rgb(this.collideTiles?1:0, this.collideSolidObjects?1:0, this.isSolid?1:0, this.parent?.2:.5);
2241
- const color2 = this.parent ? rgb(1,1,1,.5) : rgb(0,0,0,.8);
2242
- drawRect(this.pos, size, color1, this.angle, false);
2243
- drawRect(this.pos, size.scale(.8), color2, this.angle, false);
2244
- this.parent && drawLine(this.pos, this.parent.pos, .1, rgb(0,0,1,.5), false);
2250
+ if (debug)
2251
+ {
2252
+ // show object info for debugging
2253
+ const size = vec2(max(this.size.x, .2), max(this.size.y, .2));
2254
+ const color1 = rgb(this.collideTiles?1:0, this.collideSolidObjects?1:0, this.isSolid?1:0, this.parent?.2:.5);
2255
+ const color2 = this.parent ? rgb(1,1,1,.5) : rgb(0,0,0,.8);
2256
+ drawRect(this.pos, size, color1, this.angle, false);
2257
+ drawRect(this.pos, size.scale(.8), color2, this.angle, false);
2258
+ this.parent && drawLine(this.pos, this.parent.pos, .1, rgb(0,0,1,.5), false);
2259
+ }
2245
2260
  }
2246
2261
  }
2247
2262
  /**
@@ -2748,6 +2763,7 @@ function toggleFullscreen()
2748
2763
  * - Tracks keyboard down, pressed, and released
2749
2764
  * - Tracks mouse buttons, position, and wheel
2750
2765
  * - Tracks multiple analog gamepads
2766
+ * - Touch input is handled as mouse input
2751
2767
  * - Virtual gamepad for touch devices
2752
2768
  * @namespace Input
2753
2769
  */
@@ -2881,7 +2897,8 @@ function inputUpdate()
2881
2897
  if (headlessMode) return;
2882
2898
 
2883
2899
  // clear input when lost focus (prevent stuck keys)
2884
- isTouchDevice || document.hasFocus() || clearInput();
2900
+ if(!(touchInputEnable && isTouchDevice) && !document.hasFocus())
2901
+ clearInput();
2885
2902
 
2886
2903
  // update mouse world space position
2887
2904
  mousePos = screenToWorld(mousePosScreen);
@@ -2953,7 +2970,7 @@ function inputInit()
2953
2970
  oncontextmenu = (e)=> false; // prevent right click menu
2954
2971
 
2955
2972
  // init touch input
2956
- if (isTouchDevice)
2973
+ if (isTouchDevice && touchInputEnable && !headlessMode)
2957
2974
  touchInputInit();
2958
2975
  }
2959
2976
 
@@ -3081,12 +3098,12 @@ function vibrateStop() { vibrate(0); }
3081
3098
 
3082
3099
  /** True if a touch device has been detected
3083
3100
  * @memberof Input */
3084
- const isTouchDevice = !headlessMode && window.ontouchstart !== undefined;
3101
+ const isTouchDevice = window.ontouchstart !== undefined;
3085
3102
 
3086
3103
  // touch gamepad internal variables
3087
3104
  let touchGamepadTimer = new Timer, touchGamepadButtons, touchGamepadStick;
3088
3105
 
3089
- // try to enable touch mouse
3106
+ // enable touch input mouse passthrough
3090
3107
  function touchInputInit()
3091
3108
  {
3092
3109
  // add non passive touch event listeners
@@ -3195,6 +3212,7 @@ function touchInputInit()
3195
3212
  // render the touch gamepad, called automatically by the engine
3196
3213
  function touchGamepadRender()
3197
3214
  {
3215
+ if (!touchInputEnable || !isTouchDevice || headlessMode) return;
3198
3216
  if (!touchGamepadEnable || !touchGamepadTimer.isSet())
3199
3217
  return;
3200
3218
 
@@ -4599,7 +4617,8 @@ function medalsInit(saveName)
4599
4617
  medalsForEach(medal=> medal.unlocked = (localStorage[medal.storageKey()] | 0));
4600
4618
 
4601
4619
  // engine automatically renders medals
4602
- addPluginRender(function()
4620
+ engineAddPlugin(undefined, medalsRender);
4621
+ function medalsRender()
4603
4622
  {
4604
4623
  if (!medalsDisplayQueue.length)
4605
4624
  return;
@@ -4623,7 +4642,7 @@ function medalsInit(saveName)
4623
4642
  time > slideOffTime ? (time - slideOffTime) / medalDisplaySlideTime : 0;
4624
4643
  medal.render(hidePercent);
4625
4644
  }
4626
- });
4645
+ }
4627
4646
  }
4628
4647
 
4629
4648
  /** Calls a function for each medal
@@ -5062,7 +5081,7 @@ const engineName = 'LittleJS';
5062
5081
  * @type {String}
5063
5082
  * @default
5064
5083
  * @memberof Engine */
5065
- const engineVersion = '1.9.8';
5084
+ const engineVersion = '1.9.9';
5066
5085
 
5067
5086
  /** Frames per second to update
5068
5087
  * @type {Number}
@@ -5121,14 +5140,14 @@ let frameTimeLastMS = 0, frameTimeBufferMS = 0, averageFPS = 0;
5121
5140
  const pluginUpdateList = [], pluginRenderList = [];
5122
5141
 
5123
5142
  /** Add a new update function for a plugin
5124
- * @param {Function} updateFunction
5125
- * @memberof Engine */
5126
- function addPluginUpdate(updateFunction) { pluginUpdateList.push(updateFunction); }
5127
-
5128
- /** Add a new render function for a plugin
5129
- * @param {Function} renderFunction
5143
+ * @param {Function} [updateFunction]
5144
+ * @param {Function} [renderFunction]
5130
5145
  * @memberof Engine */
5131
- function addPluginRender(renderFunction) { pluginRenderList.push(renderFunction); }
5146
+ function engineAddPlugin(updateFunction, renderFunction)
5147
+ {
5148
+ updateFunction && pluginUpdateList.push(updateFunction);
5149
+ renderFunction && pluginRenderList.push(renderFunction);
5150
+ }
5132
5151
 
5133
5152
  ///////////////////////////////////////////////////////////////////////////////
5134
5153
  // Main engine functions
@@ -5301,10 +5320,11 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
5301
5320
  const styleBody =
5302
5321
  'margin:0;overflow:hidden;' + // fill the window
5303
5322
  'background:#000;' + // set background color
5304
- 'touch-action:none;' + // prevent mobile pinch to resize
5305
- 'user-select:none;' + // prevent mobile hold to select
5323
+ 'user-select:none;' + // prevent hold to select
5306
5324
  '-webkit-user-select:none;' + // compatibility for ios
5307
- '-webkit-touch-callout:none'; // compatibility for ios
5325
+ (!touchInputEnable ? '' : // no touch css setttings
5326
+ 'touch-action:none;' + // prevent mobile pinch to resize
5327
+ '-webkit-touch-callout:none');// compatibility for ios
5308
5328
  document.body.style.cssText = styleBody;
5309
5329
  document.body.appendChild(mainCanvas = document.createElement('canvas'));
5310
5330
  mainContext = mainCanvas.getContext('2d');
@@ -5645,6 +5665,7 @@ export {
5645
5665
  engineObjectsUpdate,
5646
5666
  engineObjectsDestroy,
5647
5667
  engineObjectsCallback,
5668
+ engineAddPlugin,
5648
5669
 
5649
5670
  // Globals
5650
5671
  debug,
@@ -5726,6 +5747,7 @@ export {
5726
5747
  setObjectMaxSpeed,
5727
5748
  setGravity,
5728
5749
  setParticleEmitRateScale,
5750
+ setTouchInputEnable,
5729
5751
  setGamepadsEnable,
5730
5752
  setGamepadDirectionEmulateStick,
5731
5753
  setInputWASDEmulateDirection,
@@ -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){ASSERT("number"==typeof a&&"number"==typeof b);this.x=a;this.y=b}set(a=0,b=0){this.x=a;this.y=b;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){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)} )`}}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}set(a=1,b=1,c=1,d=1){this.r=a;this.g=b;this.b=c;this.a=d;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;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 l=e-f;h=.5<g?l/(2-e-f):l/(e+f);a==e?k=(b-c)/l+(b<c?6:0):b==e?k=(c-a)/l+2:c==e&&(k=(a-b)/l+4)}return[k/6,h,g,d]}mutate(a=.05,b=0){return new Color(this.r+rand(a,-a),this.g+rand(a,-a),this.b+rand(a,-a),this.a+rand(b,-b)).clamp()}toString(a=!0){const b=c=>(16>(c=255*c|0)?"0":"")+c.toString(16);return"#"+b(this.r)+b(this.g)+b(this.b)+(a?b(this.a):"")}setHex(a){this.r=clamp(parseInt(a.slice(1,3),16)/255);this.g=clamp(parseInt(a.slice(3,5),16)/255);this.b=clamp(parseInt(a.slice(5,7),16)/255);this.a=7<a.length?clamp(parseInt(a.slice(7,9),16)/255):1;return this}rgbaInt(){const a=255*clamp(this.r)|0,b=255*clamp(this.g)<<8,c=255*clamp(this.b)<<16,d=255*clamp(this.a)<<24;return a+b+c+d}}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=.5,enablePhysicsSolver=!0,objectDefaultMass=1,objectDefaultDamping=1,objectDefaultAngleDamping=1,objectDefaultElasticity=0,objectDefaultFriction=.8,objectMaxSpeed=1,gravity=0,particleEmitRateScale=1,gamepadsEnable=!0,gamepadDirectionEmulateStick=!0,inputWASDEmulateDirection=!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 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,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.y+=gravity*this.gravityScale;this.pos.x+=this.velocity.x*=this.damping;this.pos.y+=this.velocity.y*=this.damping;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)){c=tileCollisionTest(vec2(a.x,this.pos.y),this.size,this);d=tileCollisionTest(vec2(this.pos.x,a.y),this.size,this);if(c||!d)this.groundObject=b,this.velocity.y*=-this.elasticity,b=(a.y-this.size.y/2|0)-(a.y-this.size.y/2),0>b&&b>this.damping*this.velocity.y+gravity*this.gravityScale&&(this.velocity.y=this.damping?(b-gravity*this.gravityScale)/this.damping:0),this.pos.y=a.y;d&&(this.pos.x=a.x,this.velocity.x*=-this.elasticity)}}}}render(){drawTile(this.pos,this.drawSize||this.size,this.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(){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){if(headlessMode)return new TileInfo;"number"===typeof b&&(ASSERT(0<b),b=vec2(b));if("number"===typeof a){var d=textureInfos[c];ASSERT(d,"Texture not loaded");d=d.size.x/b.x|0;a=vec2(a%d*b.x,(a/d|0)*b.y)}return new TileInfo(a,b,c)}class TileInfo{constructor(a=vec2(),b=tileSizeDefault,c=0){this.pos=a.copy();this.size=b.copy();this.textureIndex=c}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,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);this.fixBleedSize=vec2(tileFixBleedScale).divide(this.size)}}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,l){ASSERT(!l||!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){k=c.pos.x/n.size.x;h=c.pos.y/n.size.y;l=c.size.x/n.size.x;const m=c.size.y/n.size.y,p=n.fixBleedSize;glSetTexture(n.glTexture);glDraw(a.x,a.y,f?-b.x:b.x,b.y,e,k+p.x,h+p.y,k-p.x+l,h-p.y+m,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,drawCanvas2D(a,b,e,f,m=>{if(n){const p=c.pos.x+tileFixBleedScale,q=c.pos.y+tileFixBleedScale,u=c.size.x-2*tileFixBleedScale,w=c.size.y-2*tileFixBleedScale;m.globalAlpha=d.a;m.drawImage(n.image,p,q,u,w,-.5,-.5,1,1);m.globalAlpha=1}else m.fillStyle=d,m.fillRect(-.5,-.5,1,1)},h,l)}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 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){drawTextScreen(a,worldToScreen(b),c*cameraScale,d,e*cameraScale,f,g,k,h)}function drawTextScreen(a,b,c=1,d=new Color,e=0,f=new Color(0,0,0),g="center",k=fontDefault,h=overlayContext){h.fillStyle=d.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(l=>{e&&h.strokeText(l,b.x,b.y);h.fillText(l,b.x,b.y);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,l)=>{const n=d?h.length*f.x*c/2|0:0;for(let q=h.length;q--;){var m=h[q].charCodeAt(0);if(32>m||127<m)m=127;var p=m-32;m=p%k;p=p/k|0;const u=b.add(vec2(q,l).multiply(g));e.drawImage(this.image,m*f.x,p*f.y,f.x,f.y,u.x-n,u.y,f.x*c,f.y*c)}});e.restore()}}function isFullscreen(){return!!document.fullscreenElement}function toggleFullscreen(){isFullscreen()?document.exitFullscreen&&document.exitFullscreen():document.body.requestFullscreen&&document.body.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=[[]]}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||(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=>{debug&&b.target!=document.body||(b.repeat||(isUsingGamepad=!1,inputData[0][b.code]=3,inputWASDEmulateDirection&&(inputData[0][a(b.code)]=3)),preventDefaultInput&&b.preventDefault())},onkeyup=b=>{debug&&b.target!=document.body||(inputData[0][b.code]=4,inputWASDEmulateDirection&&(inputData[0][a(b.code)]=4))},onmousedown=b=>{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,isTouchDevice&&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=!headlessMode&&void 0!==window.ontouchstart;let touchGamepadTimer=new Timer,touchGamepadButtons,touchGamepadStick;function touchInputInit(){function a(e){soundEnable&&audioContext&&"running"!=audioContext.state&&zzfx(0);const f=e.touches.length;if(f){const g=vec2(e.touches[0].clientX,e.touches[0].clientY);mousePosScreen=mouseToScreen(g);d?isUsingGamepad=!1: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)){touchGamepadButtons[9]=1;return}const f=vec2(touchGamepadSize,mainCanvasSize.y-touchGamepadSize),g=mainCanvasSize.subtract(vec2(touchGamepadSize,touchGamepadSize)),k=mainCanvasSize.scale(.5);for(const l of e.touches){var h=mouseToScreen(vec2(l.clientX,l.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&&(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(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,audioGainNode;function audioInit(){soundEnable&&!headlessMode&&(audioContext=new AudioContext,audioGainNode=audioContext.createGain(),audioGainNode.connect(audioContext.destination),setSoundVolume(soundVolume))}class Sound{constructor(a,b=soundDefaultRange,c=soundDefaultTaper){soundEnable&&!headlessMode&&(this.range=b,this.taper=c,this.randomness=0,a&&(this.randomness=a[1]||.05,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)}let audioSuspended=!1;function playSamples(a,b=1,c=1,d=0,e=!1,f=zzfxR,g){if(soundEnable&&!headlessMode){var k=audioSuspended;if(audioSuspended="running"!=audioContext.state)if(audioContext.resume(),k)return;var h=audioContext.createBuffer(a.length,a[0].length,f);f=audioContext.createBufferSource();a.forEach((l,n)=>h.getChannelData(n).set(l));f.buffer=h;f.playbackRate.value=c;f.loop=e;g=g||audioContext.createGain();g.gain.value=b;g.connect(audioGainNode);f.connect(new StereoPannerNode(audioContext,{pan:clamp(d,-1,1)})).connect(g);f.start();return f}}function zzfx(...a){return new Sound(a).play()}const zzfxR=44100;function zzfxG(a=1,b,c=220,d=0,e=0,f=.1,g=0,k=1,h=0,l=0,n=0,m=0,p=0,q=0,u=0,w=0,v=0,D=1,y=0,E=0,z=0){b=2*PI;var r=zzfxR;let F=h*=500*b/r/r,B=c*=b/r,C=[],x=0,G=0,t=0,H=1,R=0,S=0,A=0,J;var L=b*abs(z)*2/r,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(z)*K)/2/I;K=-(sign(z)+K)/I;let O=I=0,P=0,Q=0;d=d*r+9;y*=r;e*=r;f*=r;v*=r;l*=500*b/r**3;u*=b/r;n*=b/r;m*=r;p=p*r|0;for(J=d+y+e+f+v|0;t<J;C[t++]=A*a)++S%(100*w|0)||(A=g?1<g?2<g?3<g?Math.sin(x**3):clamp(Math.tan(x),1,-1):1-(2*x/b%2+2)%2:1-4*abs(Math.round(x/b)-x/b):Math.sin(x),A=(p?1-E+E*Math.sin(b*t/p):1)*sign(A)*abs(A)**k*(t<d?t/d:t<d+y?1-(t-d)/y*(1-D):t<d+y+e?D:t<J-v?(J-t-v)/f*D:0),A=v?A/2+(v>t?0:(t<J-v?1:(J-t)/v)*C[t-v|0]/2/a):A,z&&(A=Q=N*I+K*(I=O)+N*(O=A)-M*P-L*(P=Q))),r=(c+=h+=l)*Math.cos(u*G++),x+=r+r*q*Math.sin(t**5),H&&++H>m&&(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,l,n,m,p,q,u,w,v,D=0,y,E=[],z=[],r=[],F=0,B=0,C=1,x={},G=zzfxR/d*60>>2;for(;C;F++)E=[C=m=w=0],c.forEach((t,H)=>{n=b[t][F]||[0,0,0];C|=b[t][F]&&1;y=w+(b[t][0].length-2-(m?0:1))*G;v=H==c.length-1;e=2;for(g=w;e<n.length+v;m=++e){h=n[e];p=e==n.length+v-1&&v||q!=(n[0]||0)||h|0;for(f=0;f<G&&m;f++>G-99&&p&&1>u?u+=1/99:0)l=(1-u)*E[D++]/2||0,z[g]=(z[g]||0)-l*B+l,r[g]=(r[g++]||0)+l*B+l;h&&(u=h%1,B=n[1]||0,h|=0)&&(E=x[[q=n[D=0]||0,h]]=x[[q,h]]||(k=[...a[q]],k[2]*=2**((h-12)/12),0<h?zzfxG(...k):[]))}w=y});return[z,r]}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}}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 l=getTileCollisionData(g);if(l&&(!c||c.collideWithTile(l,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&&(a=this.pos.add(a).add(vec2(.5)),ASSERT(mainContext==this.context,"must call redrawStart() before drawing tiles"),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,l=new Color(1,1,1,0),n=new Color(1,1,1,0),m=.5,p=.1,q=1,u=.1,w=.05,v=1,D=1,y=0,E=PI,z=.1,r=.2,F=!1,B=!1,C=!0,x=B?1e9:0,G=!1){super(a,vec2(),g,b,void 0,x);this.emitSize=c;this.emitTime=d;this.emitRate=e;this.emitConeAngle=f;this.colorStartA=k;this.colorStartB=h;this.colorEndA=l;this.colorEndB=n;this.randomColorLinear=C;this.particleTime=m;this.sizeStart=p;this.sizeEnd=q;this.speed=u;this.angleSpeed=w;this.damping=v;this.angleDamping=D;this.gravityScale=y;this.particleConeAngle=E;this.fadeRate=z;this.randomness=r;this.collideTiles=F;this.additive=B;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=m=>m+m*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 l=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,l,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,l,n,m,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=l;this.trailScale=n;this.localSpaceEmitter=m;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()]|0);addPluginRender(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]);medals[this.id=a]=this;this.name=b;this.description=c;this.icon=d;e&&((this.image=new Image).src=e)}unlock(){medalsPreventUnlock||this.unlocked||(ASSERT(medalsSaveName,"save name must be set"),localStorage[this.storageKey()]=this.unlocked=1,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,glShader,glActiveTexture,glArrayBuffer,glGeometryBuffer,glPositionData,glColorData,glInstanceCount,glAdditive,glBatchAdditive;function glInit(){if(glEnable&&!headlessMode){glCanvas=document.createElement("canvas");glContext=glCanvas.getContext("webgl2");glOverlay&&document.body.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;}");var 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);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,l=1==f;glContext.enableVertexAttribArray(d);glContext.vertexAttribPointer(d,g,e,l,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&&glContext.texImage2D(gl_TEXTURE_2D,0,gl_RGBA,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 glDraw(a,b,c,d,e,f,g,k,h,l,n=0){ASSERT("number"==typeof l&&"number"==typeof n,"invalid color");(glInstanceCount>=gl_MAX_INSTANCES||glBatchAdditive!=glAdditive)&&glFlush();let m=glInstanceCount*gl_INDICIES_PER_INSTANCE;glPositionData[m++]=a;glPositionData[m++]=b;glPositionData[m++]=c;glPositionData[m++]=d;glPositionData[m++]=f;glPositionData[m++]=g;glPositionData[m++]=k;glPositionData[m++]=h;glColorData[m++]=l;glColorData[m++]=n;glPositionData[m++]=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.9.8",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 addPluginUpdate(a){pluginUpdateList.push(a)}function addPluginRender(a){pluginRenderList.push(a)}function engineInit(a,b,c,d,e,f=["tiles.png"]){function g(l=0){var n=l-frameTimeLastMS;frameTimeLastMS=l;if(debug||showWatermark)averageFPS=lerp(.05,averageFPS,1e3/(n||1));l=debug&&keyIsDown("Equal");const m=debug&&keyIsDown("Minus");debug&&(n*=l?5:m?.2:1);timeReal+=n/1e3;frameTimeBufferMS+=paused?0:n;l||(frameTimeBufferMS=min(frameTimeBufferMS,50));k();if(paused){for(const q of engineObjects)q.parent||q.updateTransforms();inputUpdate();debugUpdate();c();inputUpdatePost()}else{n=0;0>frameTimeBufferMS&&-9<frameTimeBufferMS&&(n=frameTimeBufferMS,frameTimeBufferMS=0);for(;0<=frameTimeBufferMS;frameTimeBufferMS-=1e3/frameRate)time=frame++/frameRate,inputUpdate(),b(),pluginUpdateList.forEach(q=>q()),engineObjectsUpdate(),debugUpdate(),c(),inputUpdatePost();frameTimeBufferMS+=n}if(!headlessMode){mainCanvasSize=vec2(mainCanvas.width,mainCanvas.height);mainContext.imageSmoothingEnabled=!canvasPixelated;glPreRender();d();engineObjects.sort((q,u)=>q.renderOrder-u.renderOrder);for(var p of engineObjects)p.destroyed||p.render();e();pluginRenderList.forEach(q=>q());touchGamepadRender();debugRender();glCopyToContext(mainContext);showWatermark&&(overlayContext.textAlign="right",overlayContext.textBaseline="top",overlayContext.font="1em monospace",overlayContext.fillStyle="#000",p=engineName+" v"+engineVersion+" / "+drawCount+" / "+engineObjects.length+" / "+averageFPS.toFixed(1)+(glEnable?" GL":" 2D"),overlayContext.fillText(p,mainCanvas.width-3,3),overlayContext.fillStyle="#fff",overlayContext.fillText(p,mainCanvas.width-2,2),drawCount=0)}requestAnimationFrame(g)}function k(){if(!headlessMode){if(canvasFixedSize.x){mainCanvas.width=canvasFixedSize.x;mainCanvas.height=canvasFixedSize.y;const l=innerWidth/innerHeight,n=mainCanvas.width/mainCanvas.height;(glCanvas||mainCanvas).style.width=mainCanvas.style.width=overlayCanvas.style.width=l<n?"100%":"";(glCanvas||mainCanvas).style.height=mainCanvas.style.height=overlayCanvas.style.height=l<n?"":"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 h(){a();g()}ASSERT(Array.isArray(f),"pass in images as array");headlessMode?h():(document.body.style.cssText="margin:0;overflow:hidden;background:#000;touch-action:none;user-select:none;-webkit-user-select:none;-webkit-touch-callout:none",document.body.appendChild(mainCanvas=document.createElement("canvas")),mainContext=mainCanvas.getContext("2d"),inputInit(),audioInit(),debugInit(),glInit(),document.body.appendChild(overlayCanvas=document.createElement("canvas")),overlayContext=overlayCanvas.getContext("2d"),(glCanvas||mainCanvas).style.cssText=mainCanvas.style.cssText=overlayCanvas.style.cssText="position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)",k(),f=f.map((l,n)=>new Promise(m=>{const p=new Image;p.onerror=p.onload=()=>{textureInfos[n]=new TextureInfo(p);m()};p.src=l})),showSplashScreen&&f.push(new Promise(l=>{function n(){clearInput();drawEngineSplashScreen(m+=.01);1<m?l():setTimeout(n,16)}let m=0;console.log(`${engineName} Engine v${engineVersion}`);n()})),Promise.all(f).then(h))}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,l,n,m,p)=>{b.beginPath();b.rect(h,l,n,p?m*k:m);(b.fillStyle=p)?b.fill():b.stroke()};f=(h,l,n,m=0,p=2*PI,q,u)=>{const w=(m+p)/2;m=k*(p-m)/2;b.beginPath();u&&b.lineTo(h,l);b.arc(h,l,n,w-m,w+m);(b.fillStyle=q)?b.fill():b.stroke()};e=(h=0,l=0)=>hsl([.98,.3,.57,.14][h%4]-10,.8,[0,.3,.5,.8,.9][l]).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,l=41-g/2;h<f.length;++h)b.fillStyle=e(h,2),d=b.measureText(f[h]).width,b[c?"strokeText":"fillText"](f[h],l+d/2,55.5,17*k),l+=d;b.restore()}export{engineName,engineVersion,frameRate,timeDelta,engineObjects,frame,time,timeReal,paused,setPaused,engineInit,engineObjectsUpdate,engineObjectsDestroy,engineObjectsCallback,debug,debugOverlay,showWatermark,ASSERT,debugRect,debugPoly,debugCircle,debugPoint,debugLine,debugOverlap,debugText,debugClear,debugSaveCanvas,debugSaveText,debugSaveDataURL,cameraPos,cameraScale,canvasMaxSize,canvasFixedSize,canvasPixelated,fontDefault,showSplashScreen,headlessMode,tileSizeDefault,tileFixBleedScale,enablePhysicsSolver,objectDefaultMass,objectDefaultDamping,objectDefaultAngleDamping,objectDefaultElasticity,objectDefaultFriction,objectMaxSpeed,gravity,particleEmitRateScale,glEnable,glOverlay,gamepadsEnable,gamepadDirectionEmulateStick,inputWASDEmulateDirection,touchGamepadEnable,touchGamepadAnalog,touchGamepadSize,touchGamepadAlpha,vibrateEnable,soundEnable,soundVolume,soundDefaultRange,soundDefaultTaper,medalDisplayTime,medalDisplaySlideTime,medalDisplaySize,medalDisplayIconSize,setCameraPos,setCameraScale,setCanvasMaxSize,setCanvasFixedSize,setCanvasPixelated,setFontDefault,setShowSplashScreen,setHeadlessMode,setGlEnable,setGlOverlay,setTileSizeDefault,setTileFixBleedScale,setEnablePhysicsSolver,setObjectDefaultMass,setObjectDefaultDamping,setObjectDefaultAngleDamping,setObjectDefaultElasticity,setObjectDefaultFriction,setObjectMaxSpeed,setGravity,setParticleEmitRateScale,setGamepadsEnable,setGamepadDirectionEmulateStick,setInputWASDEmulateDirection,setTouchGamepadEnable,setTouchGamepadAnalog,setTouchGamepadSize,setTouchGamepadAlpha,setVibrateEnable,setSoundEnable,setSoundVolume,setSoundDefaultRange,setSoundDefaultTaper,setMedalDisplayTime,setMedalDisplaySlideTime,setMedalDisplaySize,setMedalDisplayIconSize,setMedalsPreventUnlock,setShowWatermark,setDebugKey,PI,abs,min,max,sign,mod,clamp,percent,distanceWrap,lerpWrap,distanceAngle,lerpAngle,lerp,smoothStep,nearestPowerOfTwo,isOverlapping,wave,formatTime,rand,randInt,randSign,randInCircle,randVector,randColor,RandomGenerator,Vector2,Color,Timer,vec2,rgb,hsl,textureInfos,tile,TileInfo,TextureInfo,mainCanvas,mainContext,overlayCanvas,overlayContext,mainCanvasSize,screenToWorld,worldToScreen,drawTile,drawRect,drawLine,drawCanvas2D,setBlendMode,drawTextScreen,drawText,engineFontImage,FontImage,isFullscreen,toggleFullscreen,getCameraSize,glCanvas,glContext,glSetTexture,glCompileShader,glCreateProgram,glCreateTexture,keyIsDown,keyWasPressed,keyWasReleased,clearInput,mouseIsDown,mouseWasPressed,mouseWasReleased,mousePos,mousePosScreen,mouseWheel,isUsingGamepad,preventDefaultInput,gamepadIsDown,gamepadWasPressed,gamepadWasReleased,gamepadStick,mouseToScreen,gamepadsUpdate,vibrate,vibrateStop,isTouchDevice,Sound,SoundWave,Music,playAudioFile,speak,speakStop,getNoteFrequency,audioContext,playSamples,zzfx,EngineObject,tileCollision,tileCollisionSize,initTileCollision,setTileCollisionData,getTileCollisionData,tileCollisionTest,tileCollisionRaycast,TileLayerData,TileLayer,ParticleEmitter,Particle,medals,medalsPreventUnlock,medalsInit,Medal};
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){ASSERT("number"==typeof a&&"number"==typeof b);this.x=a;this.y=b}set(a=0,b=0){this.x=a;this.y=b;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){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)} )`}}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}set(a=1,b=1,c=1,d=1){this.r=a;this.g=b;this.b=c;this.a=d;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;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 l=e-f;h=.5<g?l/(2-e-f):l/(e+f);a==e?k=(b-c)/l+(b<c?6:0):b==e?k=(c-a)/l+2:c==e&&(k=(a-b)/l+4)}return[k/6,h,g,d]}mutate(a=.05,b=0){return new Color(this.r+rand(a,-a),this.g+rand(a,-a),this.b+rand(a,-a),this.a+rand(b,-b)).clamp()}toString(a=!0){const b=c=>(16>(c=255*c|0)?"0":"")+c.toString(16);return"#"+b(this.r)+b(this.g)+b(this.b)+(a?b(this.a):"")}setHex(a){this.r=clamp(parseInt(a.slice(1,3),16)/255);this.g=clamp(parseInt(a.slice(3,5),16)/255);this.b=clamp(parseInt(a.slice(5,7),16)/255);this.a=7<a.length?clamp(parseInt(a.slice(7,9),16)/255):1;return this}rgbaInt(){const a=255*clamp(this.r)|0,b=255*clamp(this.g)<<8,c=255*clamp(this.b)<<16,d=255*clamp(this.a)<<24;return a+b+c+d}}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=.5,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,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.y+=gravity*this.gravityScale;this.pos.x+=this.velocity.x*=this.damping;this.pos.y+=this.velocity.y*=this.damping;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)){c=tileCollisionTest(vec2(a.x,this.pos.y),this.size,this);d=tileCollisionTest(vec2(this.pos.x,a.y),this.size,this);if(c||!d)this.groundObject=b,this.velocity.y*=-this.elasticity,b=(a.y-this.size.y/2|0)-(a.y-this.size.y/2),0>b&&b>this.damping*this.velocity.y+gravity*this.gravityScale&&(this.velocity.y=this.damping?(b-gravity*this.gravityScale)/this.damping:0),this.pos.y=a.y;d&&(this.pos.x=a.x,this.velocity.x*=-this.elasticity)}}}}render(){drawTile(this.pos,this.drawSize||this.size,this.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){if(headlessMode)return new TileInfo;"number"===typeof b&&(ASSERT(0<b),b=vec2(b));if("number"===typeof a){var d=textureInfos[c];ASSERT(d,"Texture not loaded");d=d.size.x/b.x|0;a=vec2(a%d*b.x,(a/d|0)*b.y)}return new TileInfo(a,b,c)}class TileInfo{constructor(a=vec2(),b=tileSizeDefault,c=0){this.pos=a.copy();this.size=b.copy();this.textureIndex=c}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,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);this.fixBleedSize=vec2(tileFixBleedScale).divide(this.size)}}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,l){ASSERT(!l||!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){k=c.pos.x/n.size.x;h=c.pos.y/n.size.y;l=c.size.x/n.size.x;const m=c.size.y/n.size.y,p=n.fixBleedSize;glSetTexture(n.glTexture);glDraw(a.x,a.y,f?-b.x:b.x,b.y,e,k+p.x,h+p.y,k-p.x+l,h-p.y+m,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,drawCanvas2D(a,b,e,f,m=>{if(n){const p=c.pos.x+tileFixBleedScale,q=c.pos.y+tileFixBleedScale,u=c.size.x-2*tileFixBleedScale,w=c.size.y-2*tileFixBleedScale;m.globalAlpha=d.a;m.drawImage(n.image,p,q,u,w,-.5,-.5,1,1);m.globalAlpha=1}else m.fillStyle=d,m.fillRect(-.5,-.5,1,1)},h,l)}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 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){drawTextScreen(a,worldToScreen(b),c*cameraScale,d,e*cameraScale,f,g,k,h)}function drawTextScreen(a,b,c=1,d=new Color,e=0,f=new Color(0,0,0),g="center",k=fontDefault,h=overlayContext){h.fillStyle=d.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(l=>{e&&h.strokeText(l,b.x,b.y);h.fillText(l,b.x,b.y);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,l)=>{const n=d?h.length*f.x*c/2|0:0;for(let q=h.length;q--;){var m=h[q].charCodeAt(0);if(32>m||127<m)m=127;var p=m-32;m=p%k;p=p/k|0;const u=b.add(vec2(q,l).multiply(g));e.drawImage(this.image,m*f.x,p*f.y,f.x,f.y,u.x-n,u.y,f.x*c,f.y*c)}});e.restore()}}function isFullscreen(){return!!document.fullscreenElement}function toggleFullscreen(){isFullscreen()?document.exitFullscreen&&document.exitFullscreen():document.body.requestFullscreen&&document.body.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=[[]]}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=>{debug&&b.target!=document.body||(b.repeat||(isUsingGamepad=!1,inputData[0][b.code]=3,inputWASDEmulateDirection&&(inputData[0][a(b.code)]=3)),preventDefaultInput&&b.preventDefault())},onkeyup=b=>{debug&&b.target!=document.body||(inputData[0][b.code]=4,inputWASDEmulateDirection&&(inputData[0][a(b.code)]=4))},onmousedown=b=>{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,isTouchDevice&&touchInputEnable&&!headlessMode&&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&&audioContext&&"running"!=audioContext.state&&zzfx(0);const f=e.touches.length;if(f){const g=vec2(e.touches[0].clientX,e.touches[0].clientY);mousePosScreen=mouseToScreen(g);d?isUsingGamepad=!1: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)){touchGamepadButtons[9]=1;return}const f=vec2(touchGamepadSize,mainCanvasSize.y-touchGamepadSize),g=mainCanvasSize.subtract(vec2(touchGamepadSize,touchGamepadSize)),k=mainCanvasSize.scale(.5);for(const l of e.touches){var h=mouseToScreen(vec2(l.clientX,l.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&&(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,audioGainNode;function audioInit(){soundEnable&&!headlessMode&&(audioContext=new AudioContext,audioGainNode=audioContext.createGain(),audioGainNode.connect(audioContext.destination),setSoundVolume(soundVolume))}class Sound{constructor(a,b=soundDefaultRange,c=soundDefaultTaper){soundEnable&&!headlessMode&&(this.range=b,this.taper=c,this.randomness=0,a&&(this.randomness=a[1]||.05,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)}let audioSuspended=!1;function playSamples(a,b=1,c=1,d=0,e=!1,f=zzfxR,g){if(soundEnable&&!headlessMode){var k=audioSuspended;if(audioSuspended="running"!=audioContext.state)if(audioContext.resume(),k)return;var h=audioContext.createBuffer(a.length,a[0].length,f);f=audioContext.createBufferSource();a.forEach((l,n)=>h.getChannelData(n).set(l));f.buffer=h;f.playbackRate.value=c;f.loop=e;g=g||audioContext.createGain();g.gain.value=b;g.connect(audioGainNode);f.connect(new StereoPannerNode(audioContext,{pan:clamp(d,-1,1)})).connect(g);f.start();return f}}function zzfx(...a){return new Sound(a).play()}const zzfxR=44100;function zzfxG(a=1,b,c=220,d=0,e=0,f=.1,g=0,k=1,h=0,l=0,n=0,m=0,p=0,q=0,u=0,w=0,v=0,D=1,y=0,E=0,z=0){b=2*PI;var r=zzfxR;let F=h*=500*b/r/r,B=c*=b/r,C=[],x=0,G=0,t=0,H=1,R=0,S=0,A=0,J;var L=b*abs(z)*2/r,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(z)*K)/2/I;K=-(sign(z)+K)/I;let O=I=0,P=0,Q=0;d=d*r+9;y*=r;e*=r;f*=r;v*=r;l*=500*b/r**3;u*=b/r;n*=b/r;m*=r;p=p*r|0;for(J=d+y+e+f+v|0;t<J;C[t++]=A*a)++S%(100*w|0)||(A=g?1<g?2<g?3<g?Math.sin(x**3):clamp(Math.tan(x),1,-1):1-(2*x/b%2+2)%2:1-4*abs(Math.round(x/b)-x/b):Math.sin(x),A=(p?1-E+E*Math.sin(b*t/p):1)*sign(A)*abs(A)**k*(t<d?t/d:t<d+y?1-(t-d)/y*(1-D):t<d+y+e?D:t<J-v?(J-t-v)/f*D:0),A=v?A/2+(v>t?0:(t<J-v?1:(J-t)/v)*C[t-v|0]/2/a):A,z&&(A=Q=N*I+K*(I=O)+N*(O=A)-M*P-L*(P=Q))),r=(c+=h+=l)*Math.cos(u*G++),x+=r+r*q*Math.sin(t**5),H&&++H>m&&(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,l,n,m,p,q,u,w,v,D=0,y,E=[],z=[],r=[],F=0,B=0,C=1,x={},G=zzfxR/d*60>>2;for(;C;F++)E=[C=m=w=0],c.forEach((t,H)=>{n=b[t][F]||[0,0,0];C|=b[t][F]&&1;y=w+(b[t][0].length-2-(m?0:1))*G;v=H==c.length-1;e=2;for(g=w;e<n.length+v;m=++e){h=n[e];p=e==n.length+v-1&&v||q!=(n[0]||0)||h|0;for(f=0;f<G&&m;f++>G-99&&p&&1>u?u+=1/99:0)l=(1-u)*E[D++]/2||0,z[g]=(z[g]||0)-l*B+l,r[g]=(r[g++]||0)+l*B+l;h&&(u=h%1,B=n[1]||0,h|=0)&&(E=x[[q=n[D=0]||0,h]]=x[[q,h]]||(k=[...a[q]],k[2]*=2**((h-12)/12),0<h?zzfxG(...k):[]))}w=y});return[z,r]}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}}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 l=getTileCollisionData(g);if(l&&(!c||c.collideWithTile(l,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&&(a=this.pos.add(a).add(vec2(.5)),ASSERT(mainContext==this.context,"must call redrawStart() before drawing tiles"),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,l=new Color(1,1,1,0),n=new Color(1,1,1,0),m=.5,p=.1,q=1,u=.1,w=.05,v=1,D=1,y=0,E=PI,z=.1,r=.2,F=!1,B=!1,C=!0,x=B?1e9:0,G=!1){super(a,vec2(),g,b,void 0,x);this.emitSize=c;this.emitTime=d;this.emitRate=e;this.emitConeAngle=f;this.colorStartA=k;this.colorStartB=h;this.colorEndA=l;this.colorEndB=n;this.randomColorLinear=C;this.particleTime=m;this.sizeStart=p;this.sizeEnd=q;this.speed=u;this.angleSpeed=w;this.damping=v;this.angleDamping=D;this.gravityScale=y;this.particleConeAngle=E;this.fadeRate=z;this.randomness=r;this.collideTiles=F;this.additive=B;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=m=>m+m*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 l=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,l,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,l,n,m,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=l;this.trailScale=n;this.localSpaceEmitter=m;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()]|0);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]);medals[this.id=a]=this;this.name=b;this.description=c;this.icon=d;e&&((this.image=new Image).src=e)}unlock(){medalsPreventUnlock||this.unlocked||(ASSERT(medalsSaveName,"save name must be set"),localStorage[this.storageKey()]=this.unlocked=1,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,glShader,glActiveTexture,glArrayBuffer,glGeometryBuffer,glPositionData,glColorData,glInstanceCount,glAdditive,glBatchAdditive;function glInit(){if(glEnable&&!headlessMode){glCanvas=document.createElement("canvas");glContext=glCanvas.getContext("webgl2");glOverlay&&document.body.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;}");var 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);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,l=1==f;glContext.enableVertexAttribArray(d);glContext.vertexAttribPointer(d,g,e,l,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&&glContext.texImage2D(gl_TEXTURE_2D,0,gl_RGBA,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 glDraw(a,b,c,d,e,f,g,k,h,l,n=0){ASSERT("number"==typeof l&&"number"==typeof n,"invalid color");(glInstanceCount>=gl_MAX_INSTANCES||glBatchAdditive!=glAdditive)&&glFlush();let m=glInstanceCount*gl_INDICIES_PER_INSTANCE;glPositionData[m++]=a;glPositionData[m++]=b;glPositionData[m++]=c;glPositionData[m++]=d;glPositionData[m++]=f;glPositionData[m++]=g;glPositionData[m++]=k;glPositionData[m++]=h;glColorData[m++]=l;glColorData[m++]=n;glPositionData[m++]=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.9.9",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){a&&pluginUpdateList.push(a);b&&pluginRenderList.push(b)}function engineInit(a,b,c,d,e,f=["tiles.png"]){function g(l=0){var n=l-frameTimeLastMS;frameTimeLastMS=l;if(debug||showWatermark)averageFPS=lerp(.05,averageFPS,1e3/(n||1));l=debug&&keyIsDown("Equal");const m=debug&&keyIsDown("Minus");debug&&(n*=l?5:m?.2:1);timeReal+=n/1e3;frameTimeBufferMS+=paused?0:n;l||(frameTimeBufferMS=min(frameTimeBufferMS,50));k();if(paused){for(const q of engineObjects)q.parent||q.updateTransforms();inputUpdate();debugUpdate();c();inputUpdatePost()}else{n=0;0>frameTimeBufferMS&&-9<frameTimeBufferMS&&(n=frameTimeBufferMS,frameTimeBufferMS=0);for(;0<=frameTimeBufferMS;frameTimeBufferMS-=1e3/frameRate)time=frame++/frameRate,inputUpdate(),b(),pluginUpdateList.forEach(q=>q()),engineObjectsUpdate(),debugUpdate(),c(),inputUpdatePost();frameTimeBufferMS+=n}if(!headlessMode){mainCanvasSize=vec2(mainCanvas.width,mainCanvas.height);mainContext.imageSmoothingEnabled=!canvasPixelated;glPreRender();d();engineObjects.sort((q,u)=>q.renderOrder-u.renderOrder);for(var p of engineObjects)p.destroyed||p.render();e();pluginRenderList.forEach(q=>q());touchGamepadRender();debugRender();glCopyToContext(mainContext);showWatermark&&(overlayContext.textAlign="right",overlayContext.textBaseline="top",overlayContext.font="1em monospace",overlayContext.fillStyle="#000",p=engineName+" v"+engineVersion+" / "+drawCount+" / "+engineObjects.length+" / "+averageFPS.toFixed(1)+(glEnable?" GL":" 2D"),overlayContext.fillText(p,mainCanvas.width-3,3),overlayContext.fillStyle="#fff",overlayContext.fillText(p,mainCanvas.width-2,2),drawCount=0)}requestAnimationFrame(g)}function k(){if(!headlessMode){if(canvasFixedSize.x){mainCanvas.width=canvasFixedSize.x;mainCanvas.height=canvasFixedSize.y;const l=innerWidth/innerHeight,n=mainCanvas.width/mainCanvas.height;(glCanvas||mainCanvas).style.width=mainCanvas.style.width=overlayCanvas.style.width=l<n?"100%":"";(glCanvas||mainCanvas).style.height=mainCanvas.style.height=overlayCanvas.style.height=l<n?"":"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 h(){a();g()}ASSERT(Array.isArray(f),"pass in images as array");headlessMode?h():(document.body.style.cssText="margin:0;overflow:hidden;background:#000;user-select:none;-webkit-user-select:none;"+(touchInputEnable?"touch-action:none;-webkit-touch-callout:none":""),document.body.appendChild(mainCanvas=document.createElement("canvas")),mainContext=mainCanvas.getContext("2d"),inputInit(),audioInit(),debugInit(),glInit(),document.body.appendChild(overlayCanvas=document.createElement("canvas")),overlayContext=overlayCanvas.getContext("2d"),(glCanvas||mainCanvas).style.cssText=mainCanvas.style.cssText=overlayCanvas.style.cssText="position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)",k(),f=f.map((l,n)=>new Promise(m=>{const p=new Image;p.onerror=p.onload=()=>{textureInfos[n]=new TextureInfo(p);m()};p.src=l})),showSplashScreen&&f.push(new Promise(l=>{function n(){clearInput();drawEngineSplashScreen(m+=.01);1<m?l():setTimeout(n,16)}let m=0;console.log(`${engineName} Engine v${engineVersion}`);n()})),Promise.all(f).then(h))}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,l,n,m,p)=>{b.beginPath();b.rect(h,l,n,p?m*k:m);(b.fillStyle=p)?b.fill():b.stroke()};f=(h,l,n,m=0,p=2*PI,q,u)=>{const w=(m+p)/2;m=k*(p-m)/2;b.beginPath();u&&b.lineTo(h,l);b.arc(h,l,n,w-m,w+m);(b.fillStyle=q)?b.fill():b.stroke()};e=(h=0,l=0)=>hsl([.98,.3,.57,.14][h%4]-10,.8,[0,.3,.5,.8,.9][l]).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,l=41-g/2;h<f.length;++h)b.fillStyle=e(h,2),d=b.measureText(f[h]).width,b[c?"strokeText":"fillText"](f[h],l+d/2,55.5,17*k),l+=d;b.restore()}export{engineName,engineVersion,frameRate,timeDelta,engineObjects,frame,time,timeReal,paused,setPaused,engineInit,engineObjectsUpdate,engineObjectsDestroy,engineObjectsCallback,engineAddPlugin,debug,debugOverlay,showWatermark,ASSERT,debugRect,debugPoly,debugCircle,debugPoint,debugLine,debugOverlap,debugText,debugClear,debugSaveCanvas,debugSaveText,debugSaveDataURL,cameraPos,cameraScale,canvasMaxSize,canvasFixedSize,canvasPixelated,fontDefault,showSplashScreen,headlessMode,tileSizeDefault,tileFixBleedScale,enablePhysicsSolver,objectDefaultMass,objectDefaultDamping,objectDefaultAngleDamping,objectDefaultElasticity,objectDefaultFriction,objectMaxSpeed,gravity,particleEmitRateScale,glEnable,glOverlay,gamepadsEnable,gamepadDirectionEmulateStick,inputWASDEmulateDirection,touchGamepadEnable,touchGamepadAnalog,touchGamepadSize,touchGamepadAlpha,vibrateEnable,soundEnable,soundVolume,soundDefaultRange,soundDefaultTaper,medalDisplayTime,medalDisplaySlideTime,medalDisplaySize,medalDisplayIconSize,setCameraPos,setCameraScale,setCanvasMaxSize,setCanvasFixedSize,setCanvasPixelated,setFontDefault,setShowSplashScreen,setHeadlessMode,setGlEnable,setGlOverlay,setTileSizeDefault,setTileFixBleedScale,setEnablePhysicsSolver,setObjectDefaultMass,setObjectDefaultDamping,setObjectDefaultAngleDamping,setObjectDefaultElasticity,setObjectDefaultFriction,setObjectMaxSpeed,setGravity,setParticleEmitRateScale,setTouchInputEnable,setGamepadsEnable,setGamepadDirectionEmulateStick,setInputWASDEmulateDirection,setTouchGamepadEnable,setTouchGamepadAnalog,setTouchGamepadSize,setTouchGamepadAlpha,setVibrateEnable,setSoundEnable,setSoundVolume,setSoundDefaultRange,setSoundDefaultTaper,setMedalDisplayTime,setMedalDisplaySlideTime,setMedalDisplaySize,setMedalDisplayIconSize,setMedalsPreventUnlock,setShowWatermark,setDebugKey,PI,abs,min,max,sign,mod,clamp,percent,distanceWrap,lerpWrap,distanceAngle,lerpAngle,lerp,smoothStep,nearestPowerOfTwo,isOverlapping,wave,formatTime,rand,randInt,randSign,randInCircle,randVector,randColor,RandomGenerator,Vector2,Color,Timer,vec2,rgb,hsl,textureInfos,tile,TileInfo,TextureInfo,mainCanvas,mainContext,overlayCanvas,overlayContext,mainCanvasSize,screenToWorld,worldToScreen,drawTile,drawRect,drawLine,drawCanvas2D,setBlendMode,drawTextScreen,drawText,engineFontImage,FontImage,isFullscreen,toggleFullscreen,getCameraSize,glCanvas,glContext,glSetTexture,glCompileShader,glCreateProgram,glCreateTexture,keyIsDown,keyWasPressed,keyWasReleased,clearInput,mouseIsDown,mouseWasPressed,mouseWasReleased,mousePos,mousePosScreen,mouseWheel,isUsingGamepad,preventDefaultInput,gamepadIsDown,gamepadWasPressed,gamepadWasReleased,gamepadStick,mouseToScreen,gamepadsUpdate,vibrate,vibrateStop,isTouchDevice,Sound,SoundWave,Music,playAudioFile,speak,speakStop,getNoteFrequency,audioContext,playSamples,zzfx,EngineObject,tileCollision,tileCollisionSize,initTileCollision,setTileCollisionData,getTileCollisionData,tileCollisionTest,tileCollisionRaycast,TileLayerData,TileLayer,ParticleEmitter,Particle,medals,medalsPreventUnlock,medalsInit,Medal};