littlejsengine 1.11.7 → 1.11.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.
package/dist/littlejs.js CHANGED
@@ -744,7 +744,7 @@ class RandomGenerator
744
744
  this.seed ^= this.seed << 13;
745
745
  this.seed ^= this.seed >>> 17;
746
746
  this.seed ^= this.seed << 5;
747
- return valueB + (valueA - valueB) * abs(this.seed % 1e8) / 1e8;
747
+ return valueB + (valueA - valueB) * ((this.seed >>> 0) / 2**32);
748
748
  }
749
749
 
750
750
  /** Returns a floored seeded random value the two values passed in
@@ -933,11 +933,11 @@ class Vector2
933
933
  return this.x*v.y - this.y*v.x;
934
934
  }
935
935
 
936
- /** Returns the angle of this vector, up is angle 0
936
+ /** Returns the clockwise angle of this vector, up is angle 0
937
937
  * @return {Number} */
938
938
  angle() { return Math.atan2(this.x, this.y); }
939
939
 
940
- /** Sets this vector with angle and length passed in
940
+ /** Sets this vector with clockwise angle and length passed in
941
941
  * @param {Number} [angle]
942
942
  * @param {Number} [length]
943
943
  * @return {Vector2} */
@@ -948,13 +948,13 @@ class Vector2
948
948
  return this;
949
949
  }
950
950
 
951
- /** Returns copy of this vector rotated by the angle passed in
951
+ /** Returns copy of this vector rotated by the clockwise angle passed in
952
952
  * @param {Number} angle
953
953
  * @return {Vector2} */
954
954
  rotate(angle)
955
955
  {
956
- const c = Math.cos(angle), s = Math.sin(angle);
957
- return new Vector2(this.x*c + this.y*s, this.x*s + this.y*c);
956
+ const c = Math.cos(-angle), s = Math.sin(-angle);
957
+ return new Vector2(this.x*c - this.y*s, this.x*s + this.y*c);
958
958
  }
959
959
 
960
960
  /** Set the integer direction of this vector, corresponding to multiples of 90 degree rotation (0-3)
@@ -1289,57 +1289,57 @@ class Color
1289
1289
  ///////////////////////////////////////////////////////////////////////////////
1290
1290
  // default colors
1291
1291
 
1292
- /** Color - White
1292
+ /** Color - White #ffffff
1293
1293
  * @type {Color}
1294
1294
  * @memberof Utilities */
1295
- const WHITE = rgb();
1295
+ const WHITE = rgb();
1296
1296
 
1297
- /** Color - Black
1297
+ /** Color - Black #000000
1298
1298
  * @type {Color}
1299
1299
  * @memberof Utilities */
1300
1300
  const BLACK = rgb(0,0,0);
1301
1301
 
1302
- /** Color - Gray
1302
+ /** Color - Gray #808080
1303
1303
  * @type {Color}
1304
1304
  * @memberof Utilities */
1305
1305
  const GRAY = rgb(.5,.5,.5);
1306
1306
 
1307
- /** Color - Red
1307
+ /** Color - Red #ff0000
1308
1308
  * @type {Color}
1309
1309
  * @memberof Utilities */
1310
1310
  const RED = rgb(1,0,0);
1311
1311
 
1312
- /** Color - Orange
1312
+ /** Color - Orange #ff8000
1313
1313
  * @type {Color}
1314
1314
  * @memberof Utilities */
1315
1315
  const ORANGE = rgb(1,.5,0);
1316
1316
 
1317
- /** Color - Yellow
1317
+ /** Color - Yellow #ffff00
1318
1318
  * @type {Color}
1319
1319
  * @memberof Utilities */
1320
1320
  const YELLOW = rgb(1,1,0);
1321
1321
 
1322
- /** Color - Green
1322
+ /** Color - Green #00ff00
1323
1323
  * @type {Color}
1324
1324
  * @memberof Utilities */
1325
1325
  const GREEN = rgb(0,1,0);
1326
1326
 
1327
- /** Color - Cyan
1327
+ /** Color - Cyan #00ffff
1328
1328
  * @type {Color}
1329
1329
  * @memberof Utilities */
1330
1330
  const CYAN = rgb(0,1,1);
1331
1331
 
1332
- /** Color - Blue
1332
+ /** Color - Blue #0000ff
1333
1333
  * @type {Color}
1334
1334
  * @memberof Utilities */
1335
1335
  const BLUE = rgb(0,0,1);
1336
1336
 
1337
- /** Color - Purple
1337
+ /** Color - Purple #8000ff
1338
1338
  * @type {Color}
1339
1339
  * @memberof Utilities */
1340
1340
  const PURPLE = rgb(.5,0,1);
1341
1341
 
1342
- /** Color - Magenta
1342
+ /** Color - Magenta #ff00ff
1343
1343
  * @type {Color}
1344
1344
  * @memberof Utilities */
1345
1345
  const MAGENTA = rgb(1,0,1);
@@ -2009,7 +2009,7 @@ class EngineObject
2009
2009
  {
2010
2010
  // copy parent pos/angle
2011
2011
  const mirror = parent.getMirrorSign();
2012
- this.pos = this.localPos.multiply(vec2(mirror,1)).rotate(-parent.angle).add(parent.pos);
2012
+ this.pos = this.localPos.multiply(vec2(mirror,1)).rotate(parent.angle).add(parent.pos);
2013
2013
  this.angle = mirror*this.localAngle + parent.angle;
2014
2014
  }
2015
2015
 
@@ -2237,11 +2237,11 @@ class EngineObject
2237
2237
 
2238
2238
  /** Convert from local space to world space for a vector (rotation only)
2239
2239
  * @param {Vector2} vec - local space vector */
2240
- localToWorldVector(vec) { return vec.rotate(-this.angle); }
2240
+ localToWorldVector(vec) { return vec.rotate(this.angle); }
2241
2241
 
2242
2242
  /** Convert from world space to local space for a vector (rotation only)
2243
2243
  * @param {Vector2} vec - world space vector */
2244
- worldToLocalVector(vec) { return vec.rotate(this.angle); }
2244
+ worldToLocalVector(vec) { return vec.rotate(-this.angle); }
2245
2245
 
2246
2246
  /** Called to check if a tile collision should be resolved
2247
2247
  * @param {Number} tileData - the value of the tile at the position
@@ -2562,6 +2562,8 @@ function drawTile(pos, size=vec2(1), tileInfo, color=new Color,
2562
2562
  ASSERT(!context || !useWebGL, 'context only supported in canvas 2D mode');
2563
2563
  ASSERT(typeof tileInfo !== 'number' || !tileInfo,
2564
2564
  'this is an old style calls, to fix replace it with tile(tileIndex, tileSize)');
2565
+ ASSERT(isVector2(pos) && isVector2(size));
2566
+ ASSERT(isColor(color) && isColor(additiveColor));
2565
2567
 
2566
2568
  const textureInfo = tileInfo && tileInfo.getTextureInfo();
2567
2569
  if (useWebGL)
@@ -2615,7 +2617,7 @@ function drawTile(pos, size=vec2(1), tileInfo, color=new Color,
2615
2617
  else
2616
2618
  {
2617
2619
  // if no tile info, force untextured
2618
- context.fillStyle = color;
2620
+ context.fillStyle = color.toString();
2619
2621
  context.fillRect(-.5, -.5, 1, 1);
2620
2622
  }
2621
2623
  }, screenSpace, context);
@@ -2662,6 +2664,7 @@ function drawLine(posA, posB, thickness=.1, color, useWebGL, screenSpace, contex
2662
2664
  * @memberof Draw */
2663
2665
  function drawPoly(points, color=new Color, lineWidth=0, lineColor=new Color(0,0,0), screenSpace, context=mainContext)
2664
2666
  {
2667
+ ASSERT(isColor(color) && isColor(lineColor));
2665
2668
  context.fillStyle = color.toString();
2666
2669
  context.beginPath();
2667
2670
  for (const point of screenSpace ? points : points.map(worldToScreen))
@@ -2689,6 +2692,7 @@ function drawPoly(points, color=new Color, lineWidth=0, lineColor=new Color(0,0,
2689
2692
  * @memberof Draw */
2690
2693
  function drawEllipse(pos, width=1, height=1, angle=0, color=new Color, lineWidth=0, lineColor=new Color(0,0,0), screenSpace, context=mainContext)
2691
2694
  {
2695
+ ASSERT(isColor(color) && isColor(lineColor));
2692
2696
  if (!screenSpace)
2693
2697
  {
2694
2698
  pos = worldToScreen(pos);
@@ -5309,7 +5313,7 @@ const engineName = 'LittleJS';
5309
5313
  * @type {String}
5310
5314
  * @default
5311
5315
  * @memberof Engine */
5312
- const engineVersion = '1.11.7';
5316
+ const engineVersion = '1.11.9';
5313
5317
 
5314
5318
  /** Frames per second to update
5315
5319
  * @type {Number}
@@ -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 debugScreenshot(){}function debugSaveCanvas(){}function debugSaveText(){}function debugSaveDataURL(){}const PI=Math.PI;function abs(a){return Math.abs(a)}function min(a,b){return Math.min(a,b)}function max(a,b){return Math.max(a,b)}function sign(a){return Math.sign(a)}function mod(a,b=1){return(a%b+b)%b}function clamp(a,b=0,c=1){return a<b?b:a>c?c:a}function percent(a,b,c){return(c-=b)?clamp((a-b)/c):0}function lerp(a,b,c){return b+clamp(a)*(c-b)}function distanceWrap(a,b,c=1){a=(a-b)%c;return 2*a%c-a}function lerpWrap(a,b,c,d=1){return c+clamp(a)*distanceWrap(b,c,d)}function distanceAngle(a,b){return distanceWrap(a,b,2*PI)}function lerpAngle(a,b,c){return lerpWrap(a,b,c,2*PI)}function smoothStep(a){return a*a*(3-2*a)}function nearestPowerOfTwo(a){return 2**Math.ceil(Math.log2(a))}function isOverlapping(a,b,c,d=vec2()){return 2*abs(a.x-c.x)<b.x+d.x&&2*abs(a.y-c.y)<b.y+d.y}function isIntersecting(a,b,c,d){c=c.subtract(d.scale(.5));d=c.add(d);b=b.subtract(a);c=a.subtract(c);d=a.subtract(d);a=[-b.x,b.x,-b.y,b.y];b=[c.x,-d.x,c.y,-d.y];c=0;d=1;for(let e=4;e--;)if(a[e]){const f=b[e]/a[e];if(0>a[e]){if(f>d)return!1;c=max(f,c)}else{if(f<c)return!1;d=min(f,d)}}else if(0>b[e])return!1;return!0}function wave(a=1,b=1,c=time){return b/2*(1-Math.cos(c*a*2*PI))}function formatTime(a){return(a/60|0)+":"+(10>a%60?"0":"")+(a%60|0)}function rand(a=1,b=0){return b+Math.random()*(a-b)}function randInt(a,b=0){return Math.floor(rand(a,b))}function randSign(){return 2*randInt(2)-1}function randVector(a=1){return(new Vector2).setAngle(rand(2*PI),a)}function randInCircle(a=1,b=0){return 0<a?randVector(a*rand(b/a,1)**.5):new Vector2}function randColor(a=new Color,b=new Color(0,0,0,1),c=!1){return c?a.lerp(b,rand()):new Color(rand(a.r,b.r),rand(a.g,b.g),rand(a.b,b.b),rand(a.a,b.a))}class RandomGenerator{constructor(a){this.seed=a}float(a=1,b=0){this.seed^=this.seed<<13;this.seed^=this.seed>>>17;this.seed^=this.seed<<5;return b+(a-b)*abs(this.seed%1e8)/1e8}int(a,b=0){return Math.floor(this.float(a,b))}sign(){return.5<this.float()?1:-1}}function vec2(a=0,b){return"number"==typeof a?new Vector2(a,void 0==b?a:b):new Vector2(a.x,a.y)}function isVector2(a){return a instanceof Vector2}class Vector2{constructor(a=0,b=0){this.x=a;this.y=b;ASSERT(this.isValid())}set(a=0,b=0){this.x=a;this.y=b;ASSERT(this.isValid());return this}copy(){return new Vector2(this.x,this.y)}add(a){ASSERT(isVector2(a));return new Vector2(this.x+a.x,this.y+a.y)}subtract(a){ASSERT(isVector2(a));return new Vector2(this.x-a.x,this.y-a.y)}multiply(a){ASSERT(isVector2(a));return new Vector2(this.x*a.x,this.y*a.y)}divide(a){ASSERT(isVector2(a));return new Vector2(this.x/a.x,this.y/a.y)}scale(a){ASSERT(!isVector2(a));return new Vector2(this.x*a,this.y*a)}length(){return this.lengthSquared()**.5}lengthSquared(){return this.x**2+this.y**2}distance(a){ASSERT(isVector2(a));return this.distanceSquared(a)**.5}distanceSquared(a){ASSERT(isVector2(a));return(this.x-a.x)**2+(this.y-a.y)**2}normalize(a=1){const b=this.length();return b?this.scale(a/b):new Vector2(0,a)}clampLength(a=1){const b=this.length();return b>a?this.scale(a/b):this}dot(a){ASSERT(isVector2(a));return this.x*a.x+this.y*a.y}cross(a){ASSERT(isVector2(a));return this.x*a.y-this.y*a.x}angle(){return Math.atan2(this.x,this.y)}setAngle(a=0,b=1){this.x=b*Math.sin(a);this.y=b*Math.cos(a);return this}rotate(a){const b=Math.cos(a);a=Math.sin(a);return new Vector2(this.x*b+this.y*a,this.x*a+this.y*b)}setDirection(a,b=1){a=mod(a,4);ASSERT(0==a||1==a||2==a||3==a);return vec2(a%2?a-1?-b:b:0,a%2?0:a?-b:b)}direction(){return abs(this.x)>abs(this.y)?0>this.x?3:1:0>this.y?2:0}invert(){return new Vector2(this.y,-this.x)}floor(){return new Vector2(Math.floor(this.x),Math.floor(this.y))}area(){return abs(this.x*this.y)}lerp(a,b){ASSERT(isVector2(a));return this.add(a.subtract(this).scale(clamp(b)))}arrayCheck(a){ASSERT(isVector2(a));return 0<=this.x&&0<=this.y&&this.x<a.x&&this.y<a.y}toString(a=3){if(debug)return`(${(0>this.x?"":" ")+this.x.toFixed(a)},${(0>this.y?"":" ")+this.y.toFixed(a)} )`}isValid(){return"number"==typeof this.x&&!isNaN(this.x)&&"number"==typeof this.y&&!isNaN(this.y)}}function rgb(a,b,c,d){return new Color(a,b,c,d)}function hsl(a,b,c,d){return(new Color).setHSLA(a,b,c,d)}function isColor(a){return a instanceof Color}class Color{constructor(a=1,b=1,c=1,d=1){this.r=a;this.g=b;this.b=c;this.a=d;ASSERT(this.isValid())}set(a=1,b=1,c=1,d=1){this.r=a;this.g=b;this.b=c;this.a=d;ASSERT(this.isValid());return this}copy(){return new Color(this.r,this.g,this.b,this.a)}add(a){ASSERT(isColor(a));return new Color(this.r+a.r,this.g+a.g,this.b+a.b,this.a+a.a)}subtract(a){ASSERT(isColor(a));return new Color(this.r-a.r,this.g-a.g,this.b-a.b,this.a-a.a)}multiply(a){ASSERT(isColor(a));return new Color(this.r*a.r,this.g*a.g,this.b*a.b,this.a*a.a)}divide(a){ASSERT(isColor(a));return new Color(this.r/a.r,this.g/a.g,this.b/a.b,this.a/a.a)}scale(a,b=a){return new Color(this.r*a,this.g*a,this.b*a,this.a*b)}clamp(){return new Color(clamp(this.r),clamp(this.g),clamp(this.b),clamp(this.a))}lerp(a,b){ASSERT(isColor(a));return this.add(a.subtract(this).scale(clamp(b)))}setHSLA(a=0,b=0,c=1,d=1){a=mod(a,1);b=clamp(b);c=clamp(c);b=.5>c?c*(1+b):c+b-c*b;c=2*c-b;const e=(f,g,k)=>1>6*(k=mod(k,1))?f+6*(g-f)*k:1>2*k?g:2>3*k?f+(g-f)*(4-6*k):f;this.r=e(c,b,a+1/3);this.g=e(c,b,a);this.b=e(c,b,a-1/3);this.a=d;ASSERT(this.isValid());return this}HSLA(){const a=clamp(this.r),b=clamp(this.g),c=clamp(this.b),d=clamp(this.a),e=Math.max(a,b,c),f=Math.min(a,b,c),g=(e+f)/2;let k=0,h=0;if(e!=f){let m=e-f;h=.5<g?m/(2-e-f):m/(e+f);a==e?k=(b-c)/m+(b<c?6:0):b==e?k=(c-a)/m+2:c==e&&(k=(a-b)/m+4)}return[k/6,h,g,d]}mutate(a=.05,b=0){return new Color(this.r+rand(a,-a),this.g+rand(a,-a),this.b+rand(a,-a),this.a+rand(b,-b)).clamp()}toString(a=!0){const b=c=>(16>(c=255*clamp(c)|0)?"0":"")+c.toString(16);return"#"+b(this.r)+b(this.g)+b(this.b)+(a?b(this.a):"")}setHex(a){ASSERT("string"==typeof a&&"#"==a[0]);ASSERT([4,5,7,9].includes(a.length),"Invalid hex");6>a.length?(this.r=clamp(parseInt(a[1],16)/15),this.g=clamp(parseInt(a[2],16)/15),this.b=clamp(parseInt(a[3],16)/15),this.a=5==a.length?clamp(parseInt(a[4],16)/15):1):(this.r=clamp(parseInt(a.slice(1,3),16)/255),this.g=clamp(parseInt(a.slice(3,5),16)/255),this.b=clamp(parseInt(a.slice(5,7),16)/255),this.a=9==a.length?clamp(parseInt(a.slice(7,9),16)/255):1);ASSERT(this.isValid());return this}rgbaInt(){const a=255*clamp(this.r)|0,b=255*clamp(this.g)<<8,c=255*clamp(this.b)<<16,d=255*clamp(this.a)<<24;return a+b+c+d}isValid(){return"number"==typeof this.r&&!isNaN(this.r)&&"number"==typeof this.g&&!isNaN(this.g)&&"number"==typeof this.b&&!isNaN(this.b)&&"number"==typeof this.a&&!isNaN(this.a)}}const WHITE=rgb(),BLACK=rgb(0,0,0),GRAY=rgb(.5,.5,.5),RED=rgb(1,0,0),ORANGE=rgb(1,.5,0),YELLOW=rgb(1,1,0),GREEN=rgb(0,1,0),CYAN=rgb(0,1,1),BLUE=rgb(0,0,1),PURPLE=rgb(.5,0,1),MAGENTA=rgb(1,0,1);class Timer{constructor(a){this.time=void 0==a?void 0:time+a;this.setTime=a}set(a=0){this.time=time+a;this.setTime=a}unset(){this.time=void 0}isSet(){return void 0!=this.time}active(){return time<this.time}elapsed(){return time>=this.time}get(){return this.isSet()?time-this.time:0}getPercent(){return this.isSet()?1-percent(this.time-time,0,this.setTime):0}toString(){if(debug)return this.isSet()?Math.abs(this.get())+" seconds "+(0>this.get()?"before":"after"):"unset"}valueOf(){return this.get()}}let cameraPos=vec2(),cameraScale=32,canvasMaxSize=vec2(1920,1080),canvasFixedSize=vec2(),canvasPixelated=!0,tilesPixelated=!0,fontDefault="arial",showSplashScreen=!1,headlessMode=!1,glEnable=!0,glOverlay=!0,tileSizeDefault=vec2(16),tileFixBleedScale=0,enablePhysicsSolver=!0,objectDefaultMass=1,objectDefaultDamping=1,objectDefaultAngleDamping=1,objectDefaultElasticity=0,objectDefaultFriction=.8,objectMaxSpeed=1,gravity=0,particleEmitRateScale=1,gamepadsEnable=!0,gamepadDirectionEmulateStick=!0,inputWASDEmulateDirection=!0,touchInputEnable=!0,touchGamepadEnable=!1,touchGamepadAnalog=!0,touchGamepadSize=99,touchGamepadAlpha=.3,vibrateEnable=!0,soundEnable=!0,soundVolume=.3,soundDefaultRange=40,soundDefaultTaper=.7,medalDisplayTime=5,medalDisplaySlideTime=.5,medalDisplaySize=vec2(640,80),medalDisplayIconSize=50,medalsPreventUnlock=!1;function setCameraPos(a){cameraPos=a}function setCameraScale(a){cameraScale=a}function setCanvasMaxSize(a){canvasMaxSize=a}function setCanvasFixedSize(a){canvasFixedSize=a}function setCanvasPixelated(a){canvasPixelated=a}function setTilesPixelated(a){tilesPixelated=a}function setFontDefault(a){fontDefault=a}function setShowSplashScreen(a){showSplashScreen=a}function setHeadlessMode(a){headlessMode=a}function setGlEnable(a){glEnable=a}function setGlOverlay(a){glOverlay=a}function setTileSizeDefault(a){tileSizeDefault=a}function setTileFixBleedScale(a){tileFixBleedScale=a}function setEnablePhysicsSolver(a){enablePhysicsSolver=a}function setObjectDefaultMass(a){objectDefaultMass=a}function setObjectDefaultDamping(a){objectDefaultDamping=a}function setObjectDefaultAngleDamping(a){objectDefaultAngleDamping=a}function setObjectDefaultElasticity(a){objectDefaultElasticity=a}function setObjectDefaultFriction(a){objectDefaultFriction=a}function setObjectMaxSpeed(a){objectMaxSpeed=a}function setGravity(a){gravity=a}function setParticleEmitRateScale(a){particleEmitRateScale=a}function setGamepadsEnable(a){gamepadsEnable=a}function setGamepadDirectionEmulateStick(a){gamepadDirectionEmulateStick=a}function setInputWASDEmulateDirection(a){inputWASDEmulateDirection=a}function setTouchInputEnable(a){touchInputEnable=a}function setTouchGamepadEnable(a){touchGamepadEnable=a}function setTouchGamepadAnalog(a){touchGamepadAnalog=a}function setTouchGamepadSize(a){touchGamepadSize=a}function setTouchGamepadAlpha(a){touchGamepadAlpha=a}function setVibrateEnable(a){vibrateEnable=a}function setSoundEnable(a){soundEnable=a}function setSoundVolume(a){soundVolume=a;soundEnable&&!headlessMode&&audioGainNode&&(audioGainNode.gain.value=a)}function setSoundDefaultRange(a){soundDefaultRange=a}function setSoundDefaultTaper(a){soundDefaultTaper=a}function setMedalDisplayTime(a){medalDisplayTime=a}function setMedalDisplaySlideTime(a){medalDisplaySlideTime=a}function setMedalDisplaySize(a){medalDisplaySize=a}function setMedalDisplayIconSize(a){medalDisplayIconSize=a}function setMedalsPreventUnlock(a){medalsPreventUnlock=a}function setShowWatermark(a){showWatermark=a}function setDebugKey(a){debugKey=a}class EngineObject{constructor(a=vec2(),b=vec2(1),c,d=0,e=new Color,f=0){ASSERT(isVector2(a)&&isVector2(b),"ensure pos and size are vec2s");ASSERT("number"!==typeof c||!c,"old style tile setup");this.pos=a.copy();this.size=b;this.drawSize=void 0;this.tileInfo=c;this.angle=d;this.color=e;this.additiveColor=void 0;this.mirror=!1;this.mass=objectDefaultMass;this.damping=objectDefaultDamping;this.angleDamping=objectDefaultAngleDamping;this.elasticity=objectDefaultElasticity;this.friction=objectDefaultFriction;this.gravityScale=1;this.renderOrder=f;this.velocity=vec2();this.angleVelocity=0;this.spawnTime=time;this.children=[];this.clampSpeedLinear=!0;this.parent=void 0;this.localPos=vec2();this.localAngle=0;this.collideRaycast=this.isSolid=this.collideSolidObjects=this.collideTiles=!1;engineObjects.push(this)}updateTransforms(){const a=this.parent;if(a){const b=a.getMirrorSign();this.pos=this.localPos.multiply(vec2(b,1)).rotate(-a.angle).add(a.pos);this.angle=b*this.localAngle+a.angle}for(const b of this.children)b.updateTransforms()}update(){if(!this.parent){if(this.clampSpeedLinear)this.velocity.x=clamp(this.velocity.x,-objectMaxSpeed,objectMaxSpeed),this.velocity.y=clamp(this.velocity.y,-objectMaxSpeed,objectMaxSpeed);else{var a=this.velocity.lengthSquared();a>objectMaxSpeed*objectMaxSpeed&&(a=objectMaxSpeed/a**.5,this.velocity.x*=a,this.velocity.y*=a)}a=this.pos.copy();this.velocity.x*=this.damping;this.velocity.y*=this.damping;this.mass&&(this.velocity.y+=gravity*this.gravityScale);this.pos.x+=this.velocity.x;this.pos.y+=this.velocity.y;this.angle+=this.angleVelocity*=this.angleDamping;ASSERT(0<=this.angleDamping&&1>=this.angleDamping);ASSERT(0<=this.damping&&1>=this.damping);if(enablePhysicsSolver&&this.mass){var b=0>this.velocity.y;if(this.groundObject){var c=this.groundObject.velocity?this.groundObject.velocity.x:0;this.velocity.x=c+(this.velocity.x-c)*this.friction;this.groundObject=0}if(this.collideSolidObjects)for(var d of engineObjectsCollide){if(!this.isSolid&&!d.isSolid||d.destroyed||d.parent||d==this)continue;if(!isOverlapping(this.pos,this.size,d.pos,d.size))continue;c=this.collideWithObject(d);var e=d.collideWithObject(this);if(!c||!e)continue;if(isOverlapping(a,this.size,d.pos,d.size)){c=a.subtract(d.pos);e=c.length();c=.01>e?randVector(.001):c.scale(.001/e);this.velocity=this.velocity.add(c);d.mass&&(d.velocity=d.velocity.subtract(c));debugOverlay&&debugPhysics&&debugOverlap(this.pos,this.size,d.pos,d.size,"#f00");continue}e=this.size.add(d.size);var f=2*(a.y-d.pos.y)>e.y+gravity;const k=2*abs(a.y-d.pos.y)<e.y;var g=2*abs(a.x-d.pos.x)<e.x;c=max(this.elasticity,d.elasticity);if(f||g||!k)if(this.pos.y=d.pos.y+(e.y/2+.001)*sign(a.y-d.pos.y),d.groundObject&&b||!d.mass)b&&(this.groundObject=d),this.velocity.y*=-c;else if(d.mass){g=(this.mass*this.velocity.y+d.mass*d.velocity.y)/(this.mass+d.mass);const h=d.velocity.y*(d.mass-this.mass)/(this.mass+d.mass)+2*this.velocity.y*this.mass/(this.mass+d.mass);this.velocity.y=lerp(c,g,this.velocity.y*(this.mass-d.mass)/(this.mass+d.mass)+2*d.velocity.y*d.mass/(this.mass+d.mass));d.velocity.y=lerp(c,g,h)}!f&&k&&(this.pos.x=d.pos.x+(e.x/2+.001)*sign(a.x-d.pos.x),d.mass?(e=(this.mass*this.velocity.x+d.mass*d.velocity.x)/(this.mass+d.mass),f=d.velocity.x*(d.mass-this.mass)/(this.mass+d.mass)+2*this.velocity.x*this.mass/(this.mass+d.mass),this.velocity.x=lerp(c,e,this.velocity.x*(this.mass-d.mass)/(this.mass+d.mass)+2*d.velocity.x*d.mass/(this.mass+d.mass)),d.velocity.x=lerp(c,e,f)):this.velocity.x*=-c);debugOverlay&&debugPhysics&&debugOverlap(this.pos,this.size,d.pos,d.size,"#f0f")}if(this.collideTiles&&tileCollisionTest(this.pos,this.size,this)&&!tileCollisionTest(a,this.size,this)){d=tileCollisionTest(vec2(a.x,this.pos.y),this.size,this);c=tileCollisionTest(vec2(this.pos.x,a.y),this.size,this);if(d||!c)this.velocity.y*=-this.elasticity,(this.groundObject=b)?this.pos.y=(a.y-this.size.y/2|0)+this.size.y/2+1e-4:this.pos.y=a.y;c&&(this.pos.x=a.x,this.velocity.x*=-this.elasticity);debugOverlay&&debugPhysics&&debugRect(this.pos,this.size,"#f00")}}}}render(){drawTile(this.pos,this.drawSize||this.size,this.tileInfo,this.color,this.angle,this.mirror,this.additiveColor)}destroy(){if(!this.destroyed){this.destroyed=1;this.parent&&this.parent.removeChild(this);for(const a of this.children)a.destroy(a.parent=0)}}localToWorld(a){return this.pos.add(a.rotate(this.angle))}worldToLocal(a){return a.subtract(this.pos).rotate(-this.angle)}localToWorldVector(a){return a.rotate(-this.angle)}worldToLocalVector(a){return a.rotate(this.angle)}collideWithTile(a,b){return 0<a}collideWithObject(a){return!0}getAliveTime(){return time-this.spawnTime}applyAcceleration(a){this.mass&&(this.velocity=this.velocity.add(a))}applyForce(a){this.applyAcceleration(a.scale(1/this.mass))}getMirrorSign(){return this.mirror?-1:1}addChild(a,b=vec2(),c=0){ASSERT(!a.parent&&!this.children.includes(a));this.children.push(a);a.parent=this;a.localPos=b.copy();a.localAngle=c}removeChild(a){ASSERT(a.parent==this&&this.children.includes(a));this.children.splice(this.children.indexOf(a),1);a.parent=0}setCollision(a=!0,b=!0,c=!0,d=!0){ASSERT(a||!b,"solid objects must be set to collide");this.collideSolidObjects=a;this.isSolid=b;this.collideTiles=c;this.collideRaycast=d}toString(){if(debug){let a="type = "+this.constructor.name;if(this.pos.x||this.pos.y)a+="\npos = "+this.pos;if(this.velocity.x||this.velocity.y)a+="\nvelocity = "+this.velocity;if(this.size.x||this.size.y)a+="\nsize = "+this.size;this.angle&&(a+="\nangle = "+this.angle.toFixed(3));this.color&&(a+="\ncolor = "+this.color);return a}}renderDebugInfo(){if(debug){const a=vec2(max(this.size.x,.2),max(this.size.y,.2)),b=rgb(this.collideTiles?1:0,this.collideSolidObjects?1:0,this.isSolid?1:0,this.parent?.2:.5),c=this.parent?rgb(1,1,1,.5):rgb(0,0,0,.8);drawRect(this.pos,a,b,this.angle,!1);drawRect(this.pos,a.scale(.8),c,this.angle,!1);this.parent&&drawLine(this.pos,this.parent.pos,.1,rgb(0,0,1,.5),!1)}}}let mainCanvas,mainContext,overlayCanvas,overlayContext,mainCanvasSize=vec2(),textureInfos=[],drawCount;function tile(a=vec2(),b=tileSizeDefault,c=0,d=0){if(headlessMode)return new TileInfo;"number"===typeof b&&(ASSERT(0<b),b=vec2(b));var e=textureInfos[c];ASSERT(!!e,"Texture not loaded");const f=b.add(vec2(2*d));"number"===typeof a&&(e=e.size.x/f.x|0,a=0<e?vec2(a%e,a/e|0):vec2());a=vec2(a.x*f.x+d,a.y*f.y+d);return new TileInfo(a,b,c,d)}class TileInfo{constructor(a=vec2(),b=tileSizeDefault,c=0,d=0){this.pos=a.copy();this.size=b.copy();this.textureIndex=c;this.padding=d}offset(a){return new TileInfo(this.pos.add(a),this.size,this.textureIndex)}frame(a){ASSERT("number"==typeof a);return this.offset(vec2(a*(this.size.x+2*this.padding),0))}getTextureInfo(){return textureInfos[this.textureIndex]}}class TextureInfo{constructor(a){this.image=a;this.size=vec2(a.width,a.height);this.glTexture=glEnable&&glCreateTexture(a)}}function screenToWorld(a){return new Vector2((a.x-mainCanvasSize.x/2+.5)/cameraScale+cameraPos.x,(a.y-mainCanvasSize.y/2+.5)/-cameraScale+cameraPos.y)}function worldToScreen(a){return new Vector2((a.x-cameraPos.x)*cameraScale+mainCanvasSize.x/2-.5,(a.y-cameraPos.y)*-cameraScale+mainCanvasSize.y/2-.5)}function getCameraSize(){return mainCanvasSize.scale(1/cameraScale)}function drawTile(a,b=vec2(1),c,d=new Color,e=0,f,g=new Color(0,0,0,0),k=glEnable,h,m){ASSERT(!m||!k,"context only supported in canvas 2D mode");ASSERT("number"!==typeof c||!c,"this is an old style calls, to fix replace it with tile(tileIndex, tileSize)");const n=c&&c.getTextureInfo();if(k)if(h&&(a=screenToWorld(a),b=b.scale(1/cameraScale)),n){var l=vec2(1).divide(n.size);k=c.pos.x*l.x;h=c.pos.y*l.y;m=c.size.x*l.x;const p=c.size.y*l.y;l=l.scale(tileFixBleedScale);glSetTexture(n.glTexture);glDraw(a.x,a.y,f?-b.x:b.x,b.y,e,k+l.x,h+l.y,k-l.x+m,h-l.y+p,d.rgbaInt(),g.rgbaInt())}else glDraw(a.x,a.y,b.x,b.y,e,0,0,0,0,0,d.rgbaInt());else showWatermark&&++drawCount,b=vec2(b.x,-b.y),drawCanvas2D(a,b,e,f,p=>{if(n){const q=c.pos.x+tileFixBleedScale,r=c.pos.y+tileFixBleedScale,x=c.size.x-2*tileFixBleedScale,v=c.size.y-2*tileFixBleedScale;p.globalAlpha=d.a;p.drawImage(n.image,q,r,x,v,-.5,-.5,1,1);p.globalAlpha=1}else p.fillStyle=d,p.fillRect(-.5,-.5,1,1)},h,m)}function drawRect(a,b,c,d,e,f,g){drawTile(a,b,void 0,c,d,!1,void 0,e,f,g)}function drawLine(a,b,c=.1,d,e,f,g){b=vec2((b.x-a.x)/2,(b.y-a.y)/2);c=vec2(c,2*b.length());drawRect(a.add(b),c,d,b.angle(),e,f,g)}function drawPoly(a,b=new Color,c=0,d=new Color(0,0,0),e,f=mainContext){f.fillStyle=b.toString();f.beginPath();for(const g of e?a:a.map(worldToScreen))f.lineTo(g.x,g.y);f.closePath();f.fill();c&&(f.strokeStyle=d.toString(),f.lineWidth=e?c:c*cameraScale,f.stroke())}function drawEllipse(a,b=1,c=1,d=0,e=new Color,f=0,g=new Color(0,0,0),k,h=mainContext){k||(a=worldToScreen(a),b*=cameraScale,c*=cameraScale,f*=cameraScale);h.fillStyle=e.toString();h.beginPath();h.ellipse(a.x,a.y,b,c,d,0,9);h.fill();f&&(h.strokeStyle=g.toString(),h.lineWidth=f,h.stroke())}function drawCircle(a,b=1,c=new Color,d=0,e=new Color(0,0,0),f,g=mainContext){drawEllipse(a,b,b,0,c,d,e,f,g)}function drawCanvas2D(a,b,c,d,e,f,g=mainContext){f||(a=worldToScreen(a),b=b.scale(cameraScale));g.save();g.translate(a.x+.5,a.y+.5);g.rotate(c);g.scale(d?-b.x:b.x,-b.y);e(g);g.restore()}function drawText(a,b,c=1,d,e=0,f,g,k,h,m=mainContext){drawTextScreen(a,worldToScreen(b),c*cameraScale,d,e*cameraScale,f,g,k,h,m)}function drawTextOverlay(a,b,c=1,d,e=0,f,g,k,h){drawText(a,b,c,d,e,f,g,k,h,overlayContext)}function drawTextScreen(a,b,c=1,d=new Color,e=0,f=new Color(0,0,0),g="center",k=fontDefault,h,m=overlayContext){m.fillStyle=d.toString();m.lineWidth=e;m.strokeStyle=f.toString();m.textAlign=g;m.font=c+"px "+k;m.textBaseline="middle";m.lineJoin="round";b=b.copy();a=(a+"").split("\n");b.y-=(a.length-1)*c/2;a.forEach(n=>{e&&m.strokeText(n,b.x,b.y,h);m.fillText(n,b.x,b.y,h);b.y+=c})}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 combineCanvases(){glCopyToContext(mainContext,!0);mainContext.drawImage(overlayCanvas,0,0);glClearCanvas();overlayCanvas.width|=0}let engineFontImage;class FontImage{constructor(a,b=vec2(8),c=vec2(0,1),d=overlayContext){engineFontImage||((engineFontImage=new Image).src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQAAAAAYAQAAAAA9+x6JAAAAAnRSTlMAAHaTzTgAAAGiSURBVHjaZZABhxxBEIUf6ECLBdFY+Q0PMNgf0yCgsSAGZcT9sgIPtBWwIA5wgAPEoHUyJeeSlW+gjK+fegWwtROWpVQEyWh2npdpBmTUFVhb29RINgLIukoXr5LIAvYQ5ve+1FqWEMqNKTX3FAJHyQDRZvmKWubAACcv5z5Gtg2oyCWE+Yk/8JZQX1jTTCpKAFGIgza+dJCNBF2UskRlsgwitHbSV0QLgt9sTPtsRlvJjEr8C/FARWA2bJ/TtJ7lko34dNDn6usJUMzuErP89UUBJbWeozrwLLncXczd508deAjLWipLO4Q5XGPcJvPu92cNDaN0P5G1FL0nSOzddZOrJ6rNhbXGmeDvO3TF7DeJWl4bvaYQTNHCTeuqKZmbjHaSOFes+IX/+IhHrnAkXOAsfn24EM68XieIECoccD4KZLk/odiwzeo2rovYdhvb2HYFgyznJyDpYJdYOmfXgVdJTaUi4xA2uWYNYec9BLeqdl9EsoTw582mSFDX2DxVLbNt9U3YYoeatBad1c2Tj8t2akrjaIGJNywKB/7h75/gN3vCMSaadIUTAAAAAElFTkSuQmCC");this.image=a||engineFontImage;this.tileSize=b;this.paddingSize=c;this.context=d}drawText(a,b,c=1,d){this.drawTextScreen(a,worldToScreen(b).floor(),c*cameraScale|0,d)}drawTextScreen(a,b,c=4,d){const e=this.context;e.save();const f=this.tileSize,g=f.add(this.paddingSize).scale(c),k=this.image.width/this.tileSize.x|0;(a+"").split("\n").forEach((h,m)=>{const n=d?h.length*f.x*c/2|0:0;for(let q=h.length;q--;){var l=h[q].charCodeAt(0);if(32>l||127<l)l=127;var p=l-32;l=p%k;p=p/k|0;const r=b.add(vec2(q,m).multiply(g));e.drawImage(this.image,l*f.x,p*f.y,f.x,f.y,r.x-n,r.y,f.x*c,f.y*c)}});e.restore()}}function isFullscreen(){return!!document.fullscreenElement}function toggleFullscreen(){const a=mainCanvas.parentElement;isFullscreen()?document.exitFullscreen&&document.exitFullscreen():a.requestFullscreen&&a.requestFullscreen()}function setCursor(a="auto"){mainCanvas.parentElement.style.cursor=a}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 keyDirection(a="ArrowUp",b="ArrowDown",c="ArrowLeft",d="ArrowRight"){return vec2((keyIsDown(d)?1:0)-(keyIsDown(c)?1:0),(keyIsDown(a)?1:0)-(keyIsDown(b)?1:0))}function clearInput(){inputData=[[]];touchGamepadButtons=[]}const mouseIsDown=keyIsDown,mouseWasPressed=keyWasPressed,mouseWasReleased=keyWasReleased;let mousePos=vec2(),mousePosScreen=vec2(),mouseWheel=0,isUsingGamepad=!1,preventDefaultInput=!1;function gamepadIsDown(a,b=0){return keyIsDown(a,b+1)}function gamepadWasPressed(a,b=0){return keyWasPressed(a,b+1)}function gamepadWasReleased(a,b=0){return keyWasReleased(a,b+1)}function gamepadStick(a,b=0){return gamepadStickData[b]?gamepadStickData[b][a]||vec2():vec2()}let inputData=[[]];function inputUpdate(){headlessMode||(touchInputEnable&&isTouchDevice||document.hasFocus()||clearInput(),mousePos=screenToWorld(mousePosScreen),gamepadsUpdate())}function inputUpdatePost(){if(!headlessMode){for(const a of inputData)for(const b in a)a[b]&=1;mouseWheel=0}}function inputInit(){function a(b){return inputWASDEmulateDirection?"KeyW"==b?"ArrowUp":"KeyS"==b?"ArrowDown":"KeyA"==b?"ArrowLeft":"KeyD"==b?"ArrowRight":b:b}headlessMode||(onkeydown=b=>{b.repeat||(isUsingGamepad=!1,inputData[0][b.code]=3,inputWASDEmulateDirection&&(inputData[0][a(b.code)]=3));preventDefaultInput&&b.preventDefault()},onkeyup=b=>{inputData[0][b.code]=4;inputWASDEmulateDirection&&(inputData[0][a(b.code)]=4)},onmousedown=b=>{soundEnable&&!headlessMode&&audioContext&&"running"!=audioContext.state&&audioContext.resume();isUsingGamepad=!1;inputData[0][b.button]=3;mousePosScreen=mouseEventToScreen(b);b.button&&b.preventDefault()},onmouseup=b=>inputData[0][b.button]=inputData[0][b.button]&2|4,onmousemove=b=>mousePosScreen=mouseEventToScreen(b),onwheel=b=>mouseWheel=b.ctrlKey?0:sign(b.deltaY),oncontextmenu=b=>!1,onblur=b=>clearInput(),isTouchDevice&&touchInputEnable&&touchInputInit())}function mouseEventToScreen(a){const b=mainCanvas.getBoundingClientRect(),c=percent(a.x,b.left,b.right);a=percent(a.y,b.top,b.bottom);return vec2(c*mainCanvas.width,a*mainCanvas.height)}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&&!headlessMode&&audioContext&&"running"!=audioContext.state&&audioContext.resume();const f=e.touches.length;if(f){const g=vec2(e.touches[0].clientX,e.touches[0].clientY);mousePosScreen=mouseEventToScreen(g);d?isUsingGamepad=touchGamepadEnable:inputData[0][0]=3}else d&&(inputData[0][0]=inputData[0][0]&2|4);d=f;document.hasFocus()&&e.preventDefault();return!0}function b(e){touchGamepadStick=vec2();touchGamepadButtons=[];isUsingGamepad=!0;if(e.touches.length&&(touchGamepadTimer.set(),paused&&!d)){touchGamepadButtons[9]=1;a(e);return}const f=vec2(touchGamepadSize,mainCanvasSize.y-touchGamepadSize),g=mainCanvasSize.subtract(vec2(touchGamepadSize,touchGamepadSize)),k=mainCanvasSize.scale(.5);for(const m of e.touches){var h=mouseEventToScreen(vec2(m.clientX,m.clientY));h.distance(f)<touchGamepadSize?touchGamepadStick=h.subtract(f).scale(2/touchGamepadSize).clampLength():h.distance(g)<touchGamepadSize?(h=h.subtract(g).direction(),touchGamepadButtons[h]=1):h.distance(k)<touchGamepadSize&&!d&&(touchGamepadButtons[9]=1)}a(e);return!0}let c=a;touchGamepadEnable&&(c=b,touchGamepadButtons=[],touchGamepadStick=vec2());document.addEventListener("touchstart",e=>c(e),{passive:!1});document.addEventListener("touchmove",e=>c(e),{passive:!1});document.addEventListener("touchend",e=>c(e),{passive:!1});onmousedown=onmouseup=()=>0;let d}function touchGamepadRender(){if(touchInputEnable&&isTouchDevice&&!headlessMode&&touchGamepadEnable&&touchGamepadTimer.isSet()){var a=percent(touchGamepadTimer.get(),4,3);if(a&&!paused){var b=overlayContext;b.save();b.globalAlpha=a*touchGamepadAlpha;b.strokeStyle="#fff";b.lineWidth=3;b.fillStyle=0<touchGamepadStick.lengthSquared()?"#fff":"#000";b.beginPath();a=vec2(touchGamepadSize,mainCanvasSize.y-touchGamepadSize);if(touchGamepadAnalog)b.arc(a.x,a.y,touchGamepadSize/2,0,9),b.fill();else for(var c=10;c--;){var d=c*PI/4;b.arc(a.x,a.y,.6*touchGamepadSize,d+PI/8,d+PI/8);c%2&&b.arc(a.x,a.y,.33*touchGamepadSize,d,d);1==c&&b.fill()}b.stroke();a=vec2(mainCanvasSize.x-touchGamepadSize,mainCanvasSize.y-touchGamepadSize);for(c=4;c--;)d=a.add(vec2().setDirection(c,touchGamepadSize/2)),b.fillStyle=touchGamepadButtons[c]?"#fff":"#000",b.beginPath(),b.arc(d.x,d.y,touchGamepadSize/4,0,9),b.fill(),b.stroke();b.restore()}}}let audioContext=new AudioContext,audioGainNode;function audioInit(){soundEnable&&!headlessMode&&(audioGainNode=audioContext.createGain(),audioGainNode.connect(audioContext.destination),audioGainNode.gain.value=soundVolume)}class Sound{constructor(a,b=soundDefaultRange,c=soundDefaultTaper){soundEnable&&!headlessMode&&(this.range=b,this.taper=c,this.randomness=0,a&&(this.randomness=void 0!=a[1]?a[1]:.05,a[1]=0,this.sampleChannels=[zzfxG(...a)],this.sampleRate=zzfxR))}play(a,b=1,c=1,d=1,e=!1){if(soundEnable&&!headlessMode&&this.sampleChannels){var f;if(a){if(f=this.range){const g=cameraPos.distanceSquared(a);if(g>f*f)return;b*=percent(g**.5,f,f*this.taper)}f=2*worldToScreen(a).x/mainCanvas.width-1}a=c+c*this.randomness*d*rand(-1,1);this.gainNode=audioContext.createGain();return this.source=playSamples(this.sampleChannels,b,a,f,e,this.sampleRate,this.gainNode)}}setVolume(a=1){this.gainNode&&(this.gainNode.gain.value=a)}stop(){this.source&&this.source.stop();this.source=void 0}getSource(){return this.source}playNote(a,b,c){return this.play(b,c,2**(a/12),0)}getDuration(){return this.sampleChannels&&this.sampleChannels[0].length/this.sampleRate}isLoading(){return!this.sampleChannels}}class SoundWave extends Sound{constructor(a,b=0,c,d,e){super(void 0,c,d);soundEnable&&!headlessMode&&(this.randomness=b,fetch(a).then(f=>f.arrayBuffer()).then(f=>audioContext.decodeAudioData(f)).then(f=>{this.sampleChannels=[];for(let g=f.numberOfChannels;g--;)this.sampleChannels[g]=Array.from(f.getChannelData(g));this.sampleRate=f.sampleRate}).then(()=>e&&e(this)))}}function playAudioFile(a,b=1,c=!1){if(soundEnable&&!headlessMode)return new SoundWave(a,0,0,0,d=>d.play(void 0,b,1,1,c))}class Music extends Sound{constructor(a){super(void 0);soundEnable&&!headlessMode&&(this.randomness=0,this.sampleChannels=zzfxM(...a),this.sampleRate=zzfxR)}playMusic(a,b=!1){return super.play(void 0,a,1,1,b)}}function speak(a,b="",c=1,d=1,e=1){if(soundEnable&&!headlessMode&&speechSynthesis)return a=new SpeechSynthesisUtterance(a),a.lang=b,a.volume=2*c*soundVolume,a.rate=d,a.pitch=e,speechSynthesis.speak(a),a}function speakStop(){speechSynthesis&&speechSynthesis.cancel()}function getNoteFrequency(a,b=220){return b*2**(a/12)}function playSamples(a,b=1,c=1,d=0,e=!1,f=zzfxR,g){if(soundEnable&&!headlessMode){var k=audioContext.createBuffer(a.length,a[0].length,f),h=audioContext.createBufferSource();a.forEach((m,n)=>k.getChannelData(n).set(m));h.buffer=k;h.playbackRate.value=c;h.loop=e;g=g||audioContext.createGain();g.gain.value=b;g.connect(audioGainNode);a=new StereoPannerNode(audioContext,{pan:clamp(d,-1,1)});h.connect(a).connect(g);"running"!=audioContext.state?audioContext.resume().then(()=>h.start()):h.start();return h}}function zzfx(...a){return playSamples([zzfxG(...a)])}const zzfxR=44100;function zzfxG(a=1,b=.05,c=220,d=0,e=0,f=.1,g=0,k=1,h=0,m=0,n=0,l=0,p=0,q=0,r=0,x=0,v=0,D=1,z=0,E=0,A=0){let w=2*PI;var t=zzfxR;let F=h*=500*w/t/t;b=c*=rand(1+b,1-b)*w/t;let C=[],y=0,G=0,u=0,H=1,R=0,S=0,B=0,J;var L=w*abs(A)*2/t,K=Math.cos(L),M=Math.sin(L)/2/2,I=1+M;L=-2*K/I;M=(1-M)/I;let N=(1+sign(A)*K)/2/I;K=-(sign(A)+K)/I;let O=I=0,P=0,Q=0;d=d*t+9;z*=t;e*=t;f*=t;v*=t;m*=500*w/t**3;r*=w/t;n*=w/t;l*=t;p=p*t|0;for(J=d+z+e+f+v|0;u<J;C[u++]=B*a)++S%(100*x|0)||(B=g?1<g?2<g?3<g?Math.sin(y**3):clamp(Math.tan(y),1,-1):1-(2*y/w%2+2)%2:1-4*abs(Math.round(y/w)-y/w):Math.sin(y),B=(p?1-E+E*Math.sin(w*u/p):1)*sign(B)*abs(B)**k*(u<d?u/d:u<d+z?1-(u-d)/z*(1-D):u<d+z+e?D:u<J-v?(J-u-v)/f*D:0),B=v?B/2+(v>u?0:(u<J-v?1:(J-u)/v)*C[u-v|0]/2/a):B,A&&(B=Q=N*I+K*(I=O)+N*(O=B)-M*P-L*(P=Q))),t=(c+=h+=m)*Math.cos(r*G++),y+=t+t*q*Math.sin(u**5),H&&++H>l&&(c+=n,b+=n,H=0),!p||++R%p||(c=b,h=F,H=H||1);return C}function zzfxM(a,b,c,d=125){let e,f,g,k,h,m,n,l,p,q,r,x,v,D=0,z,E=[],A=[],w=[],t=0,F=0,C=1,y={},G=zzfxR/d*60>>2;for(;C;t++)E=[C=l=x=0],c.forEach((u,H)=>{n=b[u][t]||[0,0,0];C|=b[u][t]&&1;z=x+(b[u][0].length-2-(l?0:1))*G;v=H==c.length-1;e=2;for(g=x;e<n.length+v;l=++e){h=n[e];p=e==n.length+v-1&&v||q!=(n[0]||0)||h|0;for(f=0;f<G&&l;f++>G-99&&p&&1>r?r+=1/99:0)m=(1-r)*E[D++]/2||0,A[g]=(A[g]||0)-m*F+m,w[g]=(w[g++]||0)+m*F+m;h&&(r=h%1,F=n[1]||0,h|=0)&&(E=y[[q=n[D=0]||0,h]]=y[[q,h]]||(k=[...a[q]],k[2]*=2**((h-12)/12),0<h?zzfxG(...k):[]))}x=z});return[A,w]}let tileCollision=[],tileCollisionSize=vec2();function initTileCollision(a){tileCollisionSize=a;tileCollision=[];for(a=tileCollision.length=tileCollisionSize.area();a--;)tileCollision[a]=0}function setTileCollisionData(a,b=0){a.arrayCheck(tileCollisionSize)&&(tileCollision[(a.y|0)*tileCollisionSize.x+a.x|0]=b)}function getTileCollisionData(a){return a.arrayCheck(tileCollisionSize)?tileCollision[(a.y|0)*tileCollisionSize.x+a.x|0]:0}function tileCollisionTest(a,b=vec2(),c){const d=max(a.x-b.x/2|0,0);var e=max(a.y-b.y/2|0,0);const f=min(a.x+b.x/2,tileCollisionSize.x);for(a=min(a.y+b.y/2,tileCollisionSize.y);e<a;++e)for(b=d;b<f;++b){const g=tileCollision[e*tileCollisionSize.x+b];if(g&&(!c||c.collideWithTile(g,vec2(b,e))))return!0}return!1}function tileCollisionRaycast(a,b,c){const d=b.subtract(a),e=d.length();var f=d.normalize();f=vec2(abs(1/f.x),abs(1/f.y));let g=a.floor(),k=f.x*(0>d.x?a.x-g.x:g.x-a.x+1),h=f.y*(0>d.y?a.y-g.y:g.y-a.y+1);for(;;){const m=getTileCollisionData(g);if(m&&(!c||c.collideWithTile(m,g)))return debugRaycast&&debugLine(a,b,"#f00",.02),debugRaycast&&debugPoint(g.add(vec2(.5)),"#ff0"),g.add(vec2(.5));if(k>e&&h>e)break;k>h?(g.y+=sign(d.y),h+=f.y):(g.x+=sign(d.x),k+=f.x)}debugRaycast&&debugLine(a,b,"#00f",.02)}class TileLayerData{constructor(a,b=0,c=!1,d=new Color){this.tile=a;this.direction=b;this.mirror=c;this.color=d}clear(){this.tile=this.direction=0;this.mirror=!1;this.color=new Color}}class TileLayer extends EngineObject{constructor(a,b=tileCollisionSize,c=tile(),d=vec2(1),e=0){super(a,b,c,0,void 0,e);this.canvas=document.createElement("canvas");this.context=this.canvas.getContext("2d");this.scale=d;this.isOverlay=!1;this.data=[];for(a=this.size.area();a--;)this.data.push(new TileLayerData);headlessMode&&(this.redraw=()=>{},this.render=()=>{},this.redrawStart=()=>{},this.redrawEnd=()=>{},this.drawTileData=()=>{},this.drawCanvas2D=()=>{})}setData(a,b,c=!1){a.arrayCheck(this.size)&&(this.data[(a.y|0)*this.size.x+a.x|0]=b,c&&this.drawTileData(a))}getData(a){return a.arrayCheck(this.size)&&this.data[(a.y|0)*this.size.x+a.x|0]}update(){}render(){ASSERT(mainContext!=this.context,"must call redrawEnd() after drawing tiles");glOverlay||this.isOverlay||glCopyToContext(mainContext);let a=worldToScreen(this.pos.add(vec2(0,this.size.y*this.scale.y)));a=a.floor();(this.isOverlay?overlayContext:mainContext).drawImage(this.canvas,a.x,a.y,cameraScale*this.size.x*this.scale.x,cameraScale*this.size.y*this.scale.y)}redraw(){this.redrawStart(!0);for(let a=this.size.x;a--;)for(let b=this.size.y;b--;)this.drawTileData(vec2(a,b),!1);this.redrawEnd()}redrawStart(a=!1){this.savedRenderSettings=[mainCanvas,mainContext,mainCanvasSize,cameraPos,cameraScale];mainCanvas=this.canvas;mainContext=this.context;mainCanvasSize=this.size.multiply(this.tileInfo.size);cameraPos=this.size.scale(.5);cameraScale=this.tileInfo.size.x;a&&(mainCanvas.width=mainCanvasSize.x,mainCanvas.height=mainCanvasSize.y);this.context.imageSmoothingEnabled=!tilesPixelated;glPreRender()}redrawEnd(){ASSERT(mainContext==this.context,"must call redrawStart() before drawing tiles");glCopyToContext(mainContext,!0);[mainCanvas,mainContext,mainCanvasSize,cameraPos,cameraScale]=this.savedRenderSettings}drawTileData(a,b=!0){var c=this.tileInfo.size;b&&(b=a.multiply(c),this.context.clearRect(b.x,this.canvas.height-b.y,c.x,-c.y));b=this.getData(a);void 0!=b.tile&&(ASSERT(mainContext==this.context,"must call redrawStart() before drawing tiles"),a=a.add(vec2(.5)),c=tile(b.tile,c,this.tileInfo.textureIndex),drawTile(a,vec2(1),c,b.color,b.direction*PI/2,b.mirror))}drawCanvas2D(a,b,c,d,e){const f=this.context;f.save();a=a.subtract(this.pos).multiply(this.tileInfo.size);b=b.multiply(this.tileInfo.size);f.translate(a.x,this.canvas.height-a.y);f.rotate(c);f.scale(d?-b.x:b.x,b.y);e(f);f.restore()}drawTile(a,b=vec2(1),c,d=new Color,e,f){this.drawCanvas2D(a,b,e,f,g=>{const k=c&&c.getTextureInfo();k?(g.globalAlpha=d.a,g.drawImage(k.image,c.pos.x,c.pos.y,c.size.x,c.size.y,-.5,-.5,1,1),g.globalAlpha=1):(g.fillStyle=d,g.fillRect(-.5,-.5,1,1))})}drawRect(a,b,c,d){this.drawTile(a,b,void 0,c,d)}}class ParticleEmitter extends EngineObject{constructor(a,b,c=0,d=0,e=100,f=PI,g,k=new Color,h=new Color,m=new Color(1,1,1,0),n=new Color(1,1,1,0),l=.5,p=.1,q=1,r=.1,x=.05,v=1,D=1,z=0,E=PI,A=.1,w=.2,t=!1,F=!1,C=!0,y=F?1e9:0,G=!1){super(a,vec2(),g,b,void 0,y);this.emitSize=c;this.emitTime=d;this.emitRate=e;this.emitConeAngle=f;this.colorStartA=k;this.colorStartB=h;this.colorEndA=m;this.colorEndB=n;this.randomColorLinear=C;this.particleTime=l;this.sizeStart=p;this.sizeEnd=q;this.speed=r;this.angleSpeed=x;this.damping=v;this.angleDamping=D;this.gravityScale=z;this.particleConeAngle=E;this.fadeRate=A;this.randomness=w;this.collideTiles=t;this.additive=F;this.localSpace=G;this.trailScale=0;this.particleCreateCallback=this.particleDestroyCallback=void 0;this.emitTimeBuffer=0}update(){this.parent&&super.update();if(!this.emitTime||this.getAliveTime()<=this.emitTime){if(this.emitRate*particleEmitRateScale){const a=1/this.emitRate/particleEmitRateScale;for(this.emitTimeBuffer+=timeDelta;0<this.emitTimeBuffer;this.emitTimeBuffer-=a)this.emitParticle()}}else this.destroy();debugParticles&&debugRect(this.pos,vec2(this.emitSize),"#0f0",0,this.angle)}emitParticle(){var a="number"===typeof this.emitSize?randInCircle(this.emitSize/2):vec2(rand(-.5,.5),rand(-.5,.5)).multiply(this.emitSize).rotate(this.angle);let b=rand(this.particleConeAngle,-this.particleConeAngle);this.localSpace||(a=this.pos.add(a),b+=this.angle);const c=this.randomness;var d=l=>l+l*rand(c,-c);const e=d(this.particleTime),f=d(this.sizeStart),g=d(this.sizeEnd),k=d(this.speed);d=d(this.angleSpeed)*randSign();var h=rand(this.emitConeAngle,-this.emitConeAngle);const m=randColor(this.colorStartA,this.colorStartB,this.randomColorLinear),n=randColor(this.colorEndA,this.colorEndB,this.randomColorLinear);h=this.localSpace?h:this.angle+h;a=new Particle(a,this.tileInfo,b,m,n,e,f,g,this.fadeRate,this.additive,this.trailScale,this.localSpace&&this,this.particleDestroyCallback);a.velocity=vec2().setAngle(h,k);a.angleVelocity=d;a.fadeRate=this.fadeRate;a.damping=this.damping;a.angleDamping=this.angleDamping;a.elasticity=this.elasticity;a.friction=this.friction;a.gravityScale=this.gravityScale;a.collideTiles=this.collideTiles;a.renderOrder=this.renderOrder;a.mirror=!!randInt(2);this.particleCreateCallback&&this.particleCreateCallback(a);return a}render(){}}class Particle extends EngineObject{constructor(a,b,c,d,e,f,g,k,h,m,n,l,p){super(a,vec2(),b,c);this.colorStart=d;this.colorEndDelta=e.subtract(d);this.lifeTime=f;this.sizeStart=g;this.sizeEndDelta=k-g;this.fadeRate=h;this.additive=m;this.trailScale=n;this.localSpaceEmitter=l;this.destroyCallback=p;this.clampSpeedLinear=!1}render(){const a=0<this.lifeTime?min((time-this.spawnTime)/this.lifeTime,1):1,b=vec2(this.sizeStart+a*this.sizeEndDelta);var c=this.fadeRate/2;c=new Color(this.colorStart.r+a*this.colorEndDelta.r,this.colorStart.g+a*this.colorEndDelta.g,this.colorStart.b+a*this.colorEndDelta.b,(this.colorStart.a+a*this.colorEndDelta.a)*(a<c?a/c:a>1-c?(1-a)/c:1));this.additive&&setBlendMode(!0);let d=this.pos,e=this.angle;this.localSpaceEmitter&&(d=this.localSpaceEmitter.pos.add(d.rotate(-this.localSpaceEmitter.angle)),e+=this.localSpaceEmitter.angle);if(this.trailScale){var f=this.velocity;this.localSpaceEmitter&&(f=f.rotate(-this.localSpaceEmitter.angle));var g=f.length();g&&(f=f.scale(1/g),g*=this.trailScale,b.y=max(b.x,g),e=f.angle(),drawTile(d.add(f.multiply(vec2(0,-g/2))),b,this.tileInfo,c,e,this.mirror))}else drawTile(d,b,this.tileInfo,c,e,this.mirror);this.additive&&setBlendMode();debugParticles&&debugRect(d,b,"#f005",0,e);1==a&&(this.color=c,this.size=b,this.destroyCallback&&this.destroyCallback(this),this.destroyed=1)}}const medals={};let medalsDisplayQueue=[],medalsSaveName,medalsDisplayTimeLast;function medalsInit(a){medalsSaveName=a;debugMedals||medalsForEach(b=>b.unlocked=!!localStorage[b.storageKey()]);engineAddPlugin(void 0,function(){if(medalsDisplayQueue.length){var b=medalsDisplayQueue[0],c=timeReal-medalsDisplayTimeLast;if(medalsDisplayTimeLast)if(c>medalDisplayTime)medalsDisplayTimeLast=0,medalsDisplayQueue.shift();else{const d=medalDisplayTime-medalDisplaySlideTime;b.render(c<medalDisplaySlideTime?1-c/medalDisplaySlideTime:c>d?(c-d)/medalDisplaySlideTime:0)}else medalsDisplayTimeLast=timeReal}})}function medalsForEach(a){Object.values(medals).forEach(b=>a(b))}class Medal{constructor(a,b,c="",d="🏆",e){ASSERT(0<=a&&!medals[a]);this.id=a;this.name=b;this.description=c;this.icon=d;this.unlocked=!1;e&&((this.image=new Image).src=e);medals[a]=this}unlock(){medalsPreventUnlock||this.unlocked||(ASSERT(medalsSaveName,"save name must be set"),localStorage[this.storageKey()]=this.unlocked=!0,medalsDisplayQueue.push(this))}render(a=0){const b=overlayContext;var c=min(medalDisplaySize.x,mainCanvas.width);const d=overlayCanvas.width-c;a*=-medalDisplaySize.y;b.save();b.beginPath();b.fillStyle=new Color(.9,.9,.9).toString();b.strokeStyle=new Color(0,0,0).toString();b.lineWidth=3;b.rect(d,a,c,medalDisplaySize.y);b.fill();b.stroke();b.clip();this.renderIcon(vec2(d+15+medalDisplayIconSize/2,a+medalDisplaySize.y/2));c=vec2(d+medalDisplayIconSize+30,a+28);drawTextScreen(this.name,c,38,new Color(0,0,0),0,void 0,"left");c.y+=32;drawTextScreen(this.description,c,24,new Color(0,0,0),0,void 0,"left");b.restore()}renderIcon(a,b=medalDisplayIconSize){this.image?overlayContext.drawImage(this.image,a.x-b/2,a.y-b/2,b,b):drawTextScreen(this.icon,a,.7*b,new Color(0,0,0))}storageKey(){return medalsSaveName+"_"+this.id}}let glCanvas,glContext,glAntialias=!0,glShader,glActiveTexture,glArrayBuffer,glGeometryBuffer,glPositionData,glColorData,glInstanceCount,glAdditive,glBatchAdditive;const gl_MAX_INSTANCES=1e4,gl_INDICES_PER_INSTANCE=11,gl_INSTANCE_BYTE_STRIDE=4*gl_INDICES_PER_INSTANCE,gl_INSTANCE_BUFFER_SIZE=gl_MAX_INSTANCES*gl_INSTANCE_BYTE_STRIDE;function glInit(){if(glEnable&&!headlessMode){glCanvas=document.createElement("canvas");glContext=glCanvas.getContext("webgl2",{antialias:glAntialias});var a=mainCanvas.parentElement;glOverlay&&a.appendChild(glCanvas);glShader=glCreateProgram("#version 300 es\nprecision highp float;uniform mat4 m;in vec2 g;in vec4 p,u,c,a;in float r;out vec2 v;out vec4 d,e;void main(){vec2 s=(g-.5)*p.zw;gl_Position=m*vec4(p.xy+s*cos(r)-vec2(-s.y,s)*sin(r),1,1);v=mix(u.xw,u.zy,g);d=c;e=a;}","#version 300 es\nprecision highp float;uniform sampler2D s;in vec2 v;in vec4 d,e;out vec4 c;void main(){c=texture(s,v)*d+e;}");a=new ArrayBuffer(gl_INSTANCE_BUFFER_SIZE);glPositionData=new Float32Array(a);glColorData=new Uint32Array(a);glArrayBuffer=glContext.createBuffer();glGeometryBuffer=glContext.createBuffer();a=new Float32Array([glInstanceCount=0,0,1,0,0,1,1,1]);glContext.bindBuffer(glContext.ARRAY_BUFFER,glGeometryBuffer);glContext.bufferData(glContext.ARRAY_BUFFER,a,glContext.STATIC_DRAW)}}function glPreRender(){if(glEnable&&!headlessMode){glClearCanvas();glContext.useProgram(glShader);glContext.activeTexture(glContext.TEXTURE0);textureInfos[0]&&glContext.bindTexture(glContext.TEXTURE_2D,glActiveTexture=textureInfos[0].glTexture);var a=glAdditive=glBatchAdditive=0,b=(d,e,f,g)=>{d=glContext.getAttribLocation(glShader,d);const k=f&&gl_INSTANCE_BYTE_STRIDE,h=f&&1,m=1==f;glContext.enableVertexAttribArray(d);glContext.vertexAttribPointer(d,g,e,m,k,a);glContext.vertexAttribDivisor(d,h);a+=g*f};glContext.bindBuffer(glContext.ARRAY_BUFFER,glGeometryBuffer);b("g",glContext.FLOAT,0,2);glContext.bindBuffer(glContext.ARRAY_BUFFER,glArrayBuffer);glContext.bufferData(glContext.ARRAY_BUFFER,gl_INSTANCE_BUFFER_SIZE,glContext.DYNAMIC_DRAW);b("p",glContext.FLOAT,4,4);b("u",glContext.FLOAT,4,4);b("c",glContext.UNSIGNED_BYTE,1,4);b("a",glContext.UNSIGNED_BYTE,1,4);b("r",glContext.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 glClearCanvas(){glContext.viewport(0,0,glCanvas.width=mainCanvas.width,glCanvas.height=mainCanvas.height);glContext.clear(glContext.COLOR_BUFFER_BIT)}function glSetTexture(a){headlessMode||a==glActiveTexture||(glFlush(),glContext.bindTexture(glContext.TEXTURE_2D,glActiveTexture=a))}function glCompileShader(a,b){b=glContext.createShader(b);glContext.shaderSource(b,a);glContext.compileShader(b);if(debug&&!glContext.getShaderParameter(b,glContext.COMPILE_STATUS))throw glContext.getShaderInfoLog(b);return b}function glCreateProgram(a,b){const c=glContext.createProgram();glContext.attachShader(c,glCompileShader(a,glContext.VERTEX_SHADER));glContext.attachShader(c,glCompileShader(b,glContext.FRAGMENT_SHADER));glContext.linkProgram(c);if(debug&&!glContext.getProgramParameter(c,glContext.LINK_STATUS))throw glContext.getProgramInfoLog(c);return c}function glCreateTexture(a){const b=glContext.createTexture();glContext.bindTexture(glContext.TEXTURE_2D,b);a&&a.width?glContext.texImage2D(glContext.TEXTURE_2D,0,glContext.RGBA,glContext.RGBA,glContext.UNSIGNED_BYTE,a):(a=new Uint8Array([255,255,255,255]),glContext.texImage2D(glContext.TEXTURE_2D,0,glContext.RGBA,1,1,0,glContext.RGBA,glContext.UNSIGNED_BYTE,a));a=tilesPixelated?glContext.NEAREST:glContext.LINEAR;glContext.texParameteri(glContext.TEXTURE_2D,glContext.TEXTURE_MIN_FILTER,a);glContext.texParameteri(glContext.TEXTURE_2D,glContext.TEXTURE_MAG_FILTER,a);return b}function glFlush(){if(glInstanceCount){var a=glBatchAdditive?glContext.ONE:glContext.ONE_MINUS_SRC_ALPHA;glContext.blendFuncSeparate(glContext.SRC_ALPHA,a,glContext.ONE,a);glContext.enable(glContext.BLEND);glContext.bufferSubData(glContext.ARRAY_BUFFER,0,glPositionData);glContext.drawArraysInstanced(glContext.TRIANGLE_STRIP,0,4,glInstanceCount);showWatermark&&(drawCount+=glInstanceCount);glInstanceCount=0;glBatchAdditive=glAdditive}}function glCopyToContext(a,b=!1){glEnable&&(glInstanceCount||b)&&(glFlush(),glOverlay&&!b||a.drawImage(glCanvas,0,0))}function glSetAntialias(a=!0){ASSERT(!glCanvas,"must be called before engineInit");glAntialias=a}function glDraw(a,b,c,d,e,f,g,k,h,m,n=0){ASSERT("number"==typeof m&&"number"==typeof n,"invalid color");(glInstanceCount>=gl_MAX_INSTANCES||glBatchAdditive!=glAdditive)&&glFlush();let l=glInstanceCount++*gl_INDICES_PER_INSTANCE;glPositionData[l++]=a;glPositionData[l++]=b;glPositionData[l++]=c;glPositionData[l++]=d;glPositionData[l++]=f;glPositionData[l++]=g;glPositionData[l++]=k;glPositionData[l++]=h;glColorData[l++]=m;glColorData[l++]=n;glPositionData[l++]=e}const engineName="LittleJS",engineVersion="1.11.7",frameRate=60,timeDelta=1/frameRate;let engineObjects=[],engineObjectsCollide=[],frame=0,time=0,timeReal=0,paused=!1;function setPaused(a){paused=a}let frameTimeLastMS=0,frameTimeBufferMS=0,averageFPS=0;const pluginUpdateList=[],pluginRenderList=[];function engineAddPlugin(a,b){ASSERT(!pluginUpdateList.includes(a));ASSERT(!pluginRenderList.includes(b));a&&pluginUpdateList.push(a);b&&pluginRenderList.push(b)}function engineInit(a,b,c,d,e,f=[],g=document.body){function k(n=0){var l=n-frameTimeLastMS;frameTimeLastMS=n;if(debug||showWatermark)averageFPS=lerp(.05,averageFPS,1e3/(l||1));n=debug&&keyIsDown("Equal");const p=debug&&keyIsDown("Minus");debug&&(l*=n?5:p?.2:1);timeReal+=l/1e3;frameTimeBufferMS+=paused?0:l;n||(frameTimeBufferMS=min(frameTimeBufferMS,50));h();if(paused){for(const r of engineObjects)r.parent||r.updateTransforms();inputUpdate();pluginUpdateList.forEach(r=>r());debugUpdate();c();inputUpdatePost()}else{l=0;0>frameTimeBufferMS&&-9<frameTimeBufferMS&&(l=frameTimeBufferMS,frameTimeBufferMS=0);for(;0<=frameTimeBufferMS;frameTimeBufferMS-=1e3/frameRate)time=frame++/frameRate,inputUpdate(),b(),pluginUpdateList.forEach(r=>r()),engineObjectsUpdate(),debugUpdate(),c(),inputUpdatePost();frameTimeBufferMS+=l}if(!headlessMode){mainCanvasSize=vec2(mainCanvas.width,mainCanvas.height);overlayContext.imageSmoothingEnabled=mainContext.imageSmoothingEnabled=!tilesPixelated;glPreRender();d();engineObjects.sort((r,x)=>r.renderOrder-x.renderOrder);for(var q of engineObjects)q.destroyed||q.render();e();pluginRenderList.forEach(r=>r());touchGamepadRender();debugRender();glCopyToContext(mainContext);showWatermark&&(overlayContext.textAlign="right",overlayContext.textBaseline="top",overlayContext.font="1em monospace",overlayContext.fillStyle="#000",q=engineName+" v"+engineVersion+" / "+drawCount+" / "+engineObjects.length+" / "+averageFPS.toFixed(1)+(glEnable?" GL":" 2D"),overlayContext.fillText(q,mainCanvas.width-3,3),overlayContext.fillStyle="#fff",overlayContext.fillText(q,mainCanvas.width-2,2),drawCount=0)}requestAnimationFrame(k)}function h(){if(!headlessMode){if(canvasFixedSize.x){mainCanvas.width=canvasFixedSize.x;mainCanvas.height=canvasFixedSize.y;const n=innerWidth/innerHeight,l=mainCanvas.width/mainCanvas.height;(glCanvas||mainCanvas).style.width=mainCanvas.style.width=overlayCanvas.style.width=n<l?"100%":"";(glCanvas||mainCanvas).style.height=mainCanvas.style.height=overlayCanvas.style.height=n<l?"":"100%"}else mainCanvas.width=min(innerWidth,canvasMaxSize.x),mainCanvas.height=min(innerHeight,canvasMaxSize.y);overlayCanvas.width=mainCanvas.width;overlayCanvas.height=mainCanvas.height;mainCanvasSize=vec2(mainCanvas.width,mainCanvas.height)}}function m(){new Promise(n=>n(a())).then(k)}ASSERT(!mainContext,"engine already initialized");ASSERT(Array.isArray(f),"pass in images as array");a||=()=>{};b||=()=>{};c||=()=>{};d||=()=>{};e||=()=>{};headlessMode?m():(g.style.cssText="margin:0;overflow:hidden;width:100vw;height:100vh;display:flex;align-items:center;justify-content:center;background:#000;"+(canvasPixelated?"image-rendering:pixelated;":"")+"user-select:none;-webkit-user-select:none;"+(touchInputEnable?"touch-action:none;-webkit-touch-callout:none":""),g.appendChild(mainCanvas=document.createElement("canvas")),mainContext=mainCanvas.getContext("2d"),inputInit(),audioInit(),debugInit(),glInit(),g.appendChild(overlayCanvas=document.createElement("canvas")),overlayContext=overlayCanvas.getContext("2d"),mainCanvas.style.cssText=overlayCanvas.style.cssText="position:absolute",glCanvas&&(glCanvas.style.cssText="position:absolute"),h(),g=f.map((n,l)=>new Promise(p=>{const q=new Image;q.crossOrigin="anonymous";q.onerror=q.onload=()=>{textureInfos[l]=new TextureInfo(q);p()};q.src=n})),f.length||g.push(new Promise(n=>{textureInfos[0]=new TextureInfo(new Image);n()})),showSplashScreen&&g.push(new Promise(n=>{function l(){clearInput();drawEngineSplashScreen(p+=.01);1<p?n():setTimeout(l,16)}let p=0;console.log(`${engineName} Engine v${engineVersion}`);l()})),Promise.all(g).then(m))}function engineObjectsUpdate(){function a(b){if(!b.destroyed){b.update();for(const c of b.children)a(c)}}engineObjectsCollide=engineObjects.filter(b=>b.collideSolidObjects);for(const b of engineObjects)b.parent||(a(b),b.updateTransforms());engineObjects=engineObjects.filter(b=>!b.destroyed)}function engineObjectsDestroy(){for(const a of engineObjects)a.parent||a.destroy();engineObjects=engineObjects.filter(a=>!a.destroyed)}function engineObjectsCollect(a,b,c=engineObjects){const d=[];if(a)if(b instanceof Vector2)for(const e of c)isOverlapping(a,b,e.pos,e.size)&&d.push(e);else{b*=b;for(const e of c)a.distanceSquared(e.pos)<b&&d.push(e)}else for(const e of c)d.push(e);return d}function engineObjectsCallback(a,b,c,d=engineObjects){engineObjectsCollect(a,b,d).forEach(e=>c(e))}function engineObjectsRaycast(a,b,c=engineObjects){const d=[];for(const e of c)e.collideRaycast&&isIntersecting(a,b,e.pos,e.size)&&(debugRaycast&&debugRect(e.pos,e.size,"#f00"),d.push(e));debugRaycast&&debugLine(a,b,d.length?"#f00":"#00f",.02);return d}function drawEngineSplashScreen(a){const b=overlayContext;var c=overlayCanvas.width=innerWidth,d=overlayCanvas.height=innerHeight,e=percent(a,1,.8),f=percent(a,0,.5),g=b.createRadialGradient(c/2,d/2,0,c/2,d/2,.7*Math.hypot(c,d));g.addColorStop(0,hsl(0,0,lerp(f,0,e/2),e).toString());g.addColorStop(1,hsl(0,0,0,e).toString());b.save();b.fillStyle=g;b.fillRect(0,0,c,d);g=(h,m,n,l,p)=>{b.beginPath();b.rect(h,m,n,p?l*k:l);(b.fillStyle=p)?b.fill():b.stroke()};f=(h,m,n,l=0,p=2*PI,q,r)=>{const x=(l+p)/2;l=k*(p-l)/2;b.beginPath();r&&b.lineTo(h,m);b.arc(h,m,n,x-l,x+l);(b.fillStyle=q)?b.fill():b.stroke()};e=(h=0,m=0)=>hsl([.98,.3,.57,.14][h%4]-10,.8,[0,.3,.5,.8,.9][m]).toString();a=wave(1,1,a);const k=percent(a,.1,.5);b.translate(c/2,d/2);c=min(6,min(c,d)/99);b.scale(c,c);b.translate(-40,-35);b.lineJoin=b.lineCap="round";b.lineWidth=.1+1.9*k;c=percent(a,.1,1);b.setLineDash([99*c,99]);g(7,16,18,-8,e(2,2));g(7,8,18,4,e(2,3));g(25,8,8,8,e(2,1));g(25,8,-18,8);g(25,8,8,8);g(25,16,7,23,e());g(11,39,14,-23,e(1,1));g(11,16,14,18,e(1,2));g(11,16,14,8,e(1,3));g(25,16,-14,24);g(15,29,6,-9,e(2,2));f(15,21,5,0,PI/2,e(2,4),1);g(21,21,-6,9);g(37,14,9,6,e(3,2));g(37,14,4.5,6,e(3,3));g(37,14,9,6);g(50,20,10,-8,e(0,1));g(50,20,6.5,-8,e(0,2));g(50,20,3.5,-8,e(0,3));g(50,20,10,-8);f(55,2,11.4,.5,PI-.5,e(3,3));f(55,2,11.4,.5,PI/2,e(3,2),1);f(55,2,11.4,.5,PI-.5);g(45,7,20,-7,e(0,2));g(45,-1,20,4,e(0,3));g(45,-1,20,8);for(c=5;c--;)f(60-6*c,30,9.9,0,2*PI,e(c+2,3)),f(60-6*c,30,10,-.5,PI+.5,e(c+2,2)),f(60-6*c,30,10.1,.5,PI-.5,e(c+2,1));f(36,30,10,PI/2,3*PI/2);f(48,30,10,PI/2,3*PI/2);f(60,30,10);b.beginPath();b.lineTo(36,20);b.lineTo(60,20);b.stroke();f(60,30,4,PI,3*PI,e(3,2));f(60,30,4,PI,2*PI,e(3,3));f(60,30,4,PI,3*PI);for(c=6;c--;)b.beginPath(),b.lineTo(53,54),b.lineTo(53,40),b.lineTo(53+(1+2.9*c)*k,40),b.lineTo(53+(4+3.5*c)*k,54),b.fillStyle=e(0,c%2+2),b.fill(),c%2&&b.stroke();g(6,40,5,5);g(6,40,5,5,e());g(15,54,38,-14,e());for(g=3;g--;)for(c=2;c--;)f(15*g+15,47,c?7:1,PI,3*PI,e(g,3)),b.stroke(),f(15*g+15,47,c?7:1,0,PI,e(g,2)),b.stroke();b.beginPath();b.lineTo(6,40);b.lineTo(68,40);b.stroke();b.beginPath();b.lineTo(77,54);b.lineTo(4,54);b.stroke();f=engineName;b.font="900 16px arial";b.textAlign="center";b.textBaseline="top";b.lineWidth=.1+3.9*k;g=0;for(c=0;c<f.length;++c)g+=b.measureText(f[c]).width;for(c=2;c--;)for(let h=0,m=41-g/2;h<f.length;++h)b.fillStyle=e(h,2),d=b.measureText(f[h]).width,b[c?"strokeText":"fillText"](f[h],m+d/2,55.5,17*k),m+=d;b.restore()}
1
+ let showWatermark=0,debugKey="";const debug=0,debugOverlay=0,debugPhysics=0,debugParticles=0,debugRaycast=0,debugGamepads=0,debugMedals=0;function ASSERT(){}function debugInit(){}function debugUpdate(){}function debugRender(){}function debugRect(){}function debugPoly(){}function debugCircle(){}function debugPoint(){}function debugLine(){}function debugOverlap(){}function debugText(){}function debugClear(){}function debugScreenshot(){}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+(this.seed>>>0)/2**32*(a-b)}int(a,b=0){return Math.floor(this.float(a,b))}sign(){return.5<this.float()?1:-1}}function vec2(a=0,b){return"number"==typeof a?new Vector2(a,void 0==b?a:b):new Vector2(a.x,a.y)}function isVector2(a){return a instanceof Vector2}class Vector2{constructor(a=0,b=0){this.x=a;this.y=b;ASSERT(this.isValid())}set(a=0,b=0){this.x=a;this.y=b;ASSERT(this.isValid());return this}copy(){return new Vector2(this.x,this.y)}add(a){ASSERT(isVector2(a));return new Vector2(this.x+a.x,this.y+a.y)}subtract(a){ASSERT(isVector2(a));return new Vector2(this.x-a.x,this.y-a.y)}multiply(a){ASSERT(isVector2(a));return new Vector2(this.x*a.x,this.y*a.y)}divide(a){ASSERT(isVector2(a));return new Vector2(this.x/a.x,this.y/a.y)}scale(a){ASSERT(!isVector2(a));return new Vector2(this.x*a,this.y*a)}length(){return this.lengthSquared()**.5}lengthSquared(){return this.x**2+this.y**2}distance(a){ASSERT(isVector2(a));return this.distanceSquared(a)**.5}distanceSquared(a){ASSERT(isVector2(a));return(this.x-a.x)**2+(this.y-a.y)**2}normalize(a=1){const b=this.length();return b?this.scale(a/b):new Vector2(0,a)}clampLength(a=1){const b=this.length();return b>a?this.scale(a/b):this}dot(a){ASSERT(isVector2(a));return this.x*a.x+this.y*a.y}cross(a){ASSERT(isVector2(a));return this.x*a.y-this.y*a.x}angle(){return Math.atan2(this.x,this.y)}setAngle(a=0,b=1){this.x=b*Math.sin(a);this.y=b*Math.cos(a);return this}rotate(a){const b=Math.cos(-a);a=Math.sin(-a);return new Vector2(this.x*b-this.y*a,this.x*a+this.y*b)}setDirection(a,b=1){a=mod(a,4);ASSERT(0==a||1==a||2==a||3==a);return vec2(a%2?a-1?-b:b:0,a%2?0:a?-b:b)}direction(){return abs(this.x)>abs(this.y)?0>this.x?3:1:0>this.y?2:0}invert(){return new Vector2(this.y,-this.x)}floor(){return new Vector2(Math.floor(this.x),Math.floor(this.y))}area(){return abs(this.x*this.y)}lerp(a,b){ASSERT(isVector2(a));return this.add(a.subtract(this).scale(clamp(b)))}arrayCheck(a){ASSERT(isVector2(a));return 0<=this.x&&0<=this.y&&this.x<a.x&&this.y<a.y}toString(a=3){if(debug)return`(${(0>this.x?"":" ")+this.x.toFixed(a)},${(0>this.y?"":" ")+this.y.toFixed(a)} )`}isValid(){return"number"==typeof this.x&&!isNaN(this.x)&&"number"==typeof this.y&&!isNaN(this.y)}}function rgb(a,b,c,d){return new Color(a,b,c,d)}function hsl(a,b,c,d){return(new Color).setHSLA(a,b,c,d)}function isColor(a){return a instanceof Color}class Color{constructor(a=1,b=1,c=1,d=1){this.r=a;this.g=b;this.b=c;this.a=d;ASSERT(this.isValid())}set(a=1,b=1,c=1,d=1){this.r=a;this.g=b;this.b=c;this.a=d;ASSERT(this.isValid());return this}copy(){return new Color(this.r,this.g,this.b,this.a)}add(a){ASSERT(isColor(a));return new Color(this.r+a.r,this.g+a.g,this.b+a.b,this.a+a.a)}subtract(a){ASSERT(isColor(a));return new Color(this.r-a.r,this.g-a.g,this.b-a.b,this.a-a.a)}multiply(a){ASSERT(isColor(a));return new Color(this.r*a.r,this.g*a.g,this.b*a.b,this.a*a.a)}divide(a){ASSERT(isColor(a));return new Color(this.r/a.r,this.g/a.g,this.b/a.b,this.a/a.a)}scale(a,b=a){return new Color(this.r*a,this.g*a,this.b*a,this.a*b)}clamp(){return new Color(clamp(this.r),clamp(this.g),clamp(this.b),clamp(this.a))}lerp(a,b){ASSERT(isColor(a));return this.add(a.subtract(this).scale(clamp(b)))}setHSLA(a=0,b=0,c=1,d=1){a=mod(a,1);b=clamp(b);c=clamp(c);b=.5>c?c*(1+b):c+b-c*b;c=2*c-b;const e=(f,g,k)=>1>6*(k=mod(k,1))?f+6*(g-f)*k:1>2*k?g:2>3*k?f+(g-f)*(4-6*k):f;this.r=e(c,b,a+1/3);this.g=e(c,b,a);this.b=e(c,b,a-1/3);this.a=d;ASSERT(this.isValid());return this}HSLA(){const a=clamp(this.r),b=clamp(this.g),c=clamp(this.b),d=clamp(this.a),e=Math.max(a,b,c),f=Math.min(a,b,c),g=(e+f)/2;let k=0,h=0;if(e!=f){let m=e-f;h=.5<g?m/(2-e-f):m/(e+f);a==e?k=(b-c)/m+(b<c?6:0):b==e?k=(c-a)/m+2:c==e&&(k=(a-b)/m+4)}return[k/6,h,g,d]}mutate(a=.05,b=0){return new Color(this.r+rand(a,-a),this.g+rand(a,-a),this.b+rand(a,-a),this.a+rand(b,-b)).clamp()}toString(a=!0){const b=c=>(16>(c=255*clamp(c)|0)?"0":"")+c.toString(16);return"#"+b(this.r)+b(this.g)+b(this.b)+(a?b(this.a):"")}setHex(a){ASSERT("string"==typeof a&&"#"==a[0]);ASSERT([4,5,7,9].includes(a.length),"Invalid hex");6>a.length?(this.r=clamp(parseInt(a[1],16)/15),this.g=clamp(parseInt(a[2],16)/15),this.b=clamp(parseInt(a[3],16)/15),this.a=5==a.length?clamp(parseInt(a[4],16)/15):1):(this.r=clamp(parseInt(a.slice(1,3),16)/255),this.g=clamp(parseInt(a.slice(3,5),16)/255),this.b=clamp(parseInt(a.slice(5,7),16)/255),this.a=9==a.length?clamp(parseInt(a.slice(7,9),16)/255):1);ASSERT(this.isValid());return this}rgbaInt(){const a=255*clamp(this.r)|0,b=255*clamp(this.g)<<8,c=255*clamp(this.b)<<16,d=255*clamp(this.a)<<24;return a+b+c+d}isValid(){return"number"==typeof this.r&&!isNaN(this.r)&&"number"==typeof this.g&&!isNaN(this.g)&&"number"==typeof this.b&&!isNaN(this.b)&&"number"==typeof this.a&&!isNaN(this.a)}}const WHITE=rgb(),BLACK=rgb(0,0,0),GRAY=rgb(.5,.5,.5),RED=rgb(1,0,0),ORANGE=rgb(1,.5,0),YELLOW=rgb(1,1,0),GREEN=rgb(0,1,0),CYAN=rgb(0,1,1),BLUE=rgb(0,0,1),PURPLE=rgb(.5,0,1),MAGENTA=rgb(1,0,1);class Timer{constructor(a){this.time=void 0==a?void 0:time+a;this.setTime=a}set(a=0){this.time=time+a;this.setTime=a}unset(){this.time=void 0}isSet(){return void 0!=this.time}active(){return time<this.time}elapsed(){return time>=this.time}get(){return this.isSet()?time-this.time:0}getPercent(){return this.isSet()?1-percent(this.time-time,0,this.setTime):0}toString(){if(debug)return this.isSet()?Math.abs(this.get())+" seconds "+(0>this.get()?"before":"after"):"unset"}valueOf(){return this.get()}}let cameraPos=vec2(),cameraScale=32,canvasMaxSize=vec2(1920,1080),canvasFixedSize=vec2(),canvasPixelated=!0,tilesPixelated=!0,fontDefault="arial",showSplashScreen=!1,headlessMode=!1,glEnable=!0,glOverlay=!0,tileSizeDefault=vec2(16),tileFixBleedScale=0,enablePhysicsSolver=!0,objectDefaultMass=1,objectDefaultDamping=1,objectDefaultAngleDamping=1,objectDefaultElasticity=0,objectDefaultFriction=.8,objectMaxSpeed=1,gravity=0,particleEmitRateScale=1,gamepadsEnable=!0,gamepadDirectionEmulateStick=!0,inputWASDEmulateDirection=!0,touchInputEnable=!0,touchGamepadEnable=!1,touchGamepadAnalog=!0,touchGamepadSize=99,touchGamepadAlpha=.3,vibrateEnable=!0,soundEnable=!0,soundVolume=.3,soundDefaultRange=40,soundDefaultTaper=.7,medalDisplayTime=5,medalDisplaySlideTime=.5,medalDisplaySize=vec2(640,80),medalDisplayIconSize=50,medalsPreventUnlock=!1;function setCameraPos(a){cameraPos=a}function setCameraScale(a){cameraScale=a}function setCanvasMaxSize(a){canvasMaxSize=a}function setCanvasFixedSize(a){canvasFixedSize=a}function setCanvasPixelated(a){canvasPixelated=a}function setTilesPixelated(a){tilesPixelated=a}function setFontDefault(a){fontDefault=a}function setShowSplashScreen(a){showSplashScreen=a}function setHeadlessMode(a){headlessMode=a}function setGlEnable(a){glEnable=a}function setGlOverlay(a){glOverlay=a}function setTileSizeDefault(a){tileSizeDefault=a}function setTileFixBleedScale(a){tileFixBleedScale=a}function setEnablePhysicsSolver(a){enablePhysicsSolver=a}function setObjectDefaultMass(a){objectDefaultMass=a}function setObjectDefaultDamping(a){objectDefaultDamping=a}function setObjectDefaultAngleDamping(a){objectDefaultAngleDamping=a}function setObjectDefaultElasticity(a){objectDefaultElasticity=a}function setObjectDefaultFriction(a){objectDefaultFriction=a}function setObjectMaxSpeed(a){objectMaxSpeed=a}function setGravity(a){gravity=a}function setParticleEmitRateScale(a){particleEmitRateScale=a}function setGamepadsEnable(a){gamepadsEnable=a}function setGamepadDirectionEmulateStick(a){gamepadDirectionEmulateStick=a}function setInputWASDEmulateDirection(a){inputWASDEmulateDirection=a}function setTouchInputEnable(a){touchInputEnable=a}function setTouchGamepadEnable(a){touchGamepadEnable=a}function setTouchGamepadAnalog(a){touchGamepadAnalog=a}function setTouchGamepadSize(a){touchGamepadSize=a}function setTouchGamepadAlpha(a){touchGamepadAlpha=a}function setVibrateEnable(a){vibrateEnable=a}function setSoundEnable(a){soundEnable=a}function setSoundVolume(a){soundVolume=a;soundEnable&&!headlessMode&&audioGainNode&&(audioGainNode.gain.value=a)}function setSoundDefaultRange(a){soundDefaultRange=a}function setSoundDefaultTaper(a){soundDefaultTaper=a}function setMedalDisplayTime(a){medalDisplayTime=a}function setMedalDisplaySlideTime(a){medalDisplaySlideTime=a}function setMedalDisplaySize(a){medalDisplaySize=a}function setMedalDisplayIconSize(a){medalDisplayIconSize=a}function setMedalsPreventUnlock(a){medalsPreventUnlock=a}function setShowWatermark(a){showWatermark=a}function setDebugKey(a){debugKey=a}class EngineObject{constructor(a=vec2(),b=vec2(1),c,d=0,e=new Color,f=0){ASSERT(isVector2(a)&&isVector2(b),"ensure pos and size are vec2s");ASSERT("number"!==typeof c||!c,"old style tile setup");this.pos=a.copy();this.size=b;this.drawSize=void 0;this.tileInfo=c;this.angle=d;this.color=e;this.additiveColor=void 0;this.mirror=!1;this.mass=objectDefaultMass;this.damping=objectDefaultDamping;this.angleDamping=objectDefaultAngleDamping;this.elasticity=objectDefaultElasticity;this.friction=objectDefaultFriction;this.gravityScale=1;this.renderOrder=f;this.velocity=vec2();this.angleVelocity=0;this.spawnTime=time;this.children=[];this.clampSpeedLinear=!0;this.parent=void 0;this.localPos=vec2();this.localAngle=0;this.collideRaycast=this.isSolid=this.collideSolidObjects=this.collideTiles=!1;engineObjects.push(this)}updateTransforms(){const a=this.parent;if(a){const b=a.getMirrorSign();this.pos=this.localPos.multiply(vec2(b,1)).rotate(a.angle).add(a.pos);this.angle=b*this.localAngle+a.angle}for(const b of this.children)b.updateTransforms()}update(){if(!this.parent){if(this.clampSpeedLinear)this.velocity.x=clamp(this.velocity.x,-objectMaxSpeed,objectMaxSpeed),this.velocity.y=clamp(this.velocity.y,-objectMaxSpeed,objectMaxSpeed);else{var a=this.velocity.lengthSquared();a>objectMaxSpeed*objectMaxSpeed&&(a=objectMaxSpeed/a**.5,this.velocity.x*=a,this.velocity.y*=a)}a=this.pos.copy();this.velocity.x*=this.damping;this.velocity.y*=this.damping;this.mass&&(this.velocity.y+=gravity*this.gravityScale);this.pos.x+=this.velocity.x;this.pos.y+=this.velocity.y;this.angle+=this.angleVelocity*=this.angleDamping;ASSERT(0<=this.angleDamping&&1>=this.angleDamping);ASSERT(0<=this.damping&&1>=this.damping);if(enablePhysicsSolver&&this.mass){var b=0>this.velocity.y;if(this.groundObject){var c=this.groundObject.velocity?this.groundObject.velocity.x:0;this.velocity.x=c+(this.velocity.x-c)*this.friction;this.groundObject=0}if(this.collideSolidObjects)for(var d of engineObjectsCollide){if(!this.isSolid&&!d.isSolid||d.destroyed||d.parent||d==this)continue;if(!isOverlapping(this.pos,this.size,d.pos,d.size))continue;c=this.collideWithObject(d);var e=d.collideWithObject(this);if(!c||!e)continue;if(isOverlapping(a,this.size,d.pos,d.size)){c=a.subtract(d.pos);e=c.length();c=.01>e?randVector(.001):c.scale(.001/e);this.velocity=this.velocity.add(c);d.mass&&(d.velocity=d.velocity.subtract(c));debugOverlay&&debugPhysics&&debugOverlap(this.pos,this.size,d.pos,d.size,"#f00");continue}e=this.size.add(d.size);var f=2*(a.y-d.pos.y)>e.y+gravity;const k=2*abs(a.y-d.pos.y)<e.y;var g=2*abs(a.x-d.pos.x)<e.x;c=max(this.elasticity,d.elasticity);if(f||g||!k)if(this.pos.y=d.pos.y+(e.y/2+.001)*sign(a.y-d.pos.y),d.groundObject&&b||!d.mass)b&&(this.groundObject=d),this.velocity.y*=-c;else if(d.mass){g=(this.mass*this.velocity.y+d.mass*d.velocity.y)/(this.mass+d.mass);const h=d.velocity.y*(d.mass-this.mass)/(this.mass+d.mass)+2*this.velocity.y*this.mass/(this.mass+d.mass);this.velocity.y=lerp(c,g,this.velocity.y*(this.mass-d.mass)/(this.mass+d.mass)+2*d.velocity.y*d.mass/(this.mass+d.mass));d.velocity.y=lerp(c,g,h)}!f&&k&&(this.pos.x=d.pos.x+(e.x/2+.001)*sign(a.x-d.pos.x),d.mass?(e=(this.mass*this.velocity.x+d.mass*d.velocity.x)/(this.mass+d.mass),f=d.velocity.x*(d.mass-this.mass)/(this.mass+d.mass)+2*this.velocity.x*this.mass/(this.mass+d.mass),this.velocity.x=lerp(c,e,this.velocity.x*(this.mass-d.mass)/(this.mass+d.mass)+2*d.velocity.x*d.mass/(this.mass+d.mass)),d.velocity.x=lerp(c,e,f)):this.velocity.x*=-c);debugOverlay&&debugPhysics&&debugOverlap(this.pos,this.size,d.pos,d.size,"#f0f")}if(this.collideTiles&&tileCollisionTest(this.pos,this.size,this)&&!tileCollisionTest(a,this.size,this)){d=tileCollisionTest(vec2(a.x,this.pos.y),this.size,this);c=tileCollisionTest(vec2(this.pos.x,a.y),this.size,this);if(d||!c)this.velocity.y*=-this.elasticity,(this.groundObject=b)?this.pos.y=(a.y-this.size.y/2|0)+this.size.y/2+1e-4:this.pos.y=a.y;c&&(this.pos.x=a.x,this.velocity.x*=-this.elasticity);debugOverlay&&debugPhysics&&debugRect(this.pos,this.size,"#f00")}}}}render(){drawTile(this.pos,this.drawSize||this.size,this.tileInfo,this.color,this.angle,this.mirror,this.additiveColor)}destroy(){if(!this.destroyed){this.destroyed=1;this.parent&&this.parent.removeChild(this);for(const a of this.children)a.destroy(a.parent=0)}}localToWorld(a){return this.pos.add(a.rotate(this.angle))}worldToLocal(a){return a.subtract(this.pos).rotate(-this.angle)}localToWorldVector(a){return a.rotate(this.angle)}worldToLocalVector(a){return a.rotate(-this.angle)}collideWithTile(a,b){return 0<a}collideWithObject(a){return!0}getAliveTime(){return time-this.spawnTime}applyAcceleration(a){this.mass&&(this.velocity=this.velocity.add(a))}applyForce(a){this.applyAcceleration(a.scale(1/this.mass))}getMirrorSign(){return this.mirror?-1:1}addChild(a,b=vec2(),c=0){ASSERT(!a.parent&&!this.children.includes(a));this.children.push(a);a.parent=this;a.localPos=b.copy();a.localAngle=c}removeChild(a){ASSERT(a.parent==this&&this.children.includes(a));this.children.splice(this.children.indexOf(a),1);a.parent=0}setCollision(a=!0,b=!0,c=!0,d=!0){ASSERT(a||!b,"solid objects must be set to collide");this.collideSolidObjects=a;this.isSolid=b;this.collideTiles=c;this.collideRaycast=d}toString(){if(debug){let a="type = "+this.constructor.name;if(this.pos.x||this.pos.y)a+="\npos = "+this.pos;if(this.velocity.x||this.velocity.y)a+="\nvelocity = "+this.velocity;if(this.size.x||this.size.y)a+="\nsize = "+this.size;this.angle&&(a+="\nangle = "+this.angle.toFixed(3));this.color&&(a+="\ncolor = "+this.color);return a}}renderDebugInfo(){if(debug){const a=vec2(max(this.size.x,.2),max(this.size.y,.2)),b=rgb(this.collideTiles?1:0,this.collideSolidObjects?1:0,this.isSolid?1:0,this.parent?.2:.5),c=this.parent?rgb(1,1,1,.5):rgb(0,0,0,.8);drawRect(this.pos,a,b,this.angle,!1);drawRect(this.pos,a.scale(.8),c,this.angle,!1);this.parent&&drawLine(this.pos,this.parent.pos,.1,rgb(0,0,1,.5),!1)}}}let mainCanvas,mainContext,overlayCanvas,overlayContext,mainCanvasSize=vec2(),textureInfos=[],drawCount;function tile(a=vec2(),b=tileSizeDefault,c=0,d=0){if(headlessMode)return new TileInfo;"number"===typeof b&&(ASSERT(0<b),b=vec2(b));var e=textureInfos[c];ASSERT(!!e,"Texture not loaded");const f=b.add(vec2(2*d));"number"===typeof a&&(e=e.size.x/f.x|0,a=0<e?vec2(a%e,a/e|0):vec2());a=vec2(a.x*f.x+d,a.y*f.y+d);return new TileInfo(a,b,c,d)}class TileInfo{constructor(a=vec2(),b=tileSizeDefault,c=0,d=0){this.pos=a.copy();this.size=b.copy();this.textureIndex=c;this.padding=d}offset(a){return new TileInfo(this.pos.add(a),this.size,this.textureIndex)}frame(a){ASSERT("number"==typeof a);return this.offset(vec2(a*(this.size.x+2*this.padding),0))}getTextureInfo(){return textureInfos[this.textureIndex]}}class TextureInfo{constructor(a){this.image=a;this.size=vec2(a.width,a.height);this.glTexture=glEnable&&glCreateTexture(a)}}function screenToWorld(a){return new Vector2((a.x-mainCanvasSize.x/2+.5)/cameraScale+cameraPos.x,(a.y-mainCanvasSize.y/2+.5)/-cameraScale+cameraPos.y)}function worldToScreen(a){return new Vector2((a.x-cameraPos.x)*cameraScale+mainCanvasSize.x/2-.5,(a.y-cameraPos.y)*-cameraScale+mainCanvasSize.y/2-.5)}function getCameraSize(){return mainCanvasSize.scale(1/cameraScale)}function drawTile(a,b=vec2(1),c,d=new Color,e=0,f,g=new Color(0,0,0,0),k=glEnable,h,m){ASSERT(!m||!k,"context only supported in canvas 2D mode");ASSERT("number"!==typeof c||!c,"this is an old style calls, to fix replace it with tile(tileIndex, tileSize)");ASSERT(isVector2(a)&&isVector2(b));ASSERT(isColor(d)&&isColor(g));const n=c&&c.getTextureInfo();if(k)if(h&&(a=screenToWorld(a),b=b.scale(1/cameraScale)),n){var l=vec2(1).divide(n.size);k=c.pos.x*l.x;h=c.pos.y*l.y;m=c.size.x*l.x;const p=c.size.y*l.y;l=l.scale(tileFixBleedScale);glSetTexture(n.glTexture);glDraw(a.x,a.y,f?-b.x:b.x,b.y,e,k+l.x,h+l.y,k-l.x+m,h-l.y+p,d.rgbaInt(),g.rgbaInt())}else glDraw(a.x,a.y,b.x,b.y,e,0,0,0,0,0,d.rgbaInt());else showWatermark&&++drawCount,b=vec2(b.x,-b.y),drawCanvas2D(a,b,e,f,p=>{if(n){const q=c.pos.x+tileFixBleedScale,r=c.pos.y+tileFixBleedScale,x=c.size.x-2*tileFixBleedScale,v=c.size.y-2*tileFixBleedScale;p.globalAlpha=d.a;p.drawImage(n.image,q,r,x,v,-.5,-.5,1,1);p.globalAlpha=1}else p.fillStyle=d.toString(),p.fillRect(-.5,-.5,1,1)},h,m)}function drawRect(a,b,c,d,e,f,g){drawTile(a,b,void 0,c,d,!1,void 0,e,f,g)}function drawLine(a,b,c=.1,d,e,f,g){b=vec2((b.x-a.x)/2,(b.y-a.y)/2);c=vec2(c,2*b.length());drawRect(a.add(b),c,d,b.angle(),e,f,g)}function drawPoly(a,b=new Color,c=0,d=new Color(0,0,0),e,f=mainContext){ASSERT(isColor(b)&&isColor(d));f.fillStyle=b.toString();f.beginPath();for(const g of e?a:a.map(worldToScreen))f.lineTo(g.x,g.y);f.closePath();f.fill();c&&(f.strokeStyle=d.toString(),f.lineWidth=e?c:c*cameraScale,f.stroke())}function drawEllipse(a,b=1,c=1,d=0,e=new Color,f=0,g=new Color(0,0,0),k,h=mainContext){ASSERT(isColor(e)&&isColor(g));k||(a=worldToScreen(a),b*=cameraScale,c*=cameraScale,f*=cameraScale);h.fillStyle=e.toString();h.beginPath();h.ellipse(a.x,a.y,b,c,d,0,9);h.fill();f&&(h.strokeStyle=g.toString(),h.lineWidth=f,h.stroke())}function drawCircle(a,b=1,c=new Color,d=0,e=new Color(0,0,0),f,g=mainContext){drawEllipse(a,b,b,0,c,d,e,f,g)}function drawCanvas2D(a,b,c,d,e,f,g=mainContext){f||(a=worldToScreen(a),b=b.scale(cameraScale));g.save();g.translate(a.x+.5,a.y+.5);g.rotate(c);g.scale(d?-b.x:b.x,-b.y);e(g);g.restore()}function drawText(a,b,c=1,d,e=0,f,g,k,h,m=mainContext){drawTextScreen(a,worldToScreen(b),c*cameraScale,d,e*cameraScale,f,g,k,h,m)}function drawTextOverlay(a,b,c=1,d,e=0,f,g,k,h){drawText(a,b,c,d,e,f,g,k,h,overlayContext)}function drawTextScreen(a,b,c=1,d=new Color,e=0,f=new Color(0,0,0),g="center",k=fontDefault,h,m=overlayContext){m.fillStyle=d.toString();m.lineWidth=e;m.strokeStyle=f.toString();m.textAlign=g;m.font=c+"px "+k;m.textBaseline="middle";m.lineJoin="round";b=b.copy();a=(a+"").split("\n");b.y-=(a.length-1)*c/2;a.forEach(n=>{e&&m.strokeText(n,b.x,b.y,h);m.fillText(n,b.x,b.y,h);b.y+=c})}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 combineCanvases(){glCopyToContext(mainContext,!0);mainContext.drawImage(overlayCanvas,0,0);glClearCanvas();overlayCanvas.width|=0}let engineFontImage;class FontImage{constructor(a,b=vec2(8),c=vec2(0,1),d=overlayContext){engineFontImage||((engineFontImage=new Image).src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQAAAAAYAQAAAAA9+x6JAAAAAnRSTlMAAHaTzTgAAAGiSURBVHjaZZABhxxBEIUf6ECLBdFY+Q0PMNgf0yCgsSAGZcT9sgIPtBWwIA5wgAPEoHUyJeeSlW+gjK+fegWwtROWpVQEyWh2npdpBmTUFVhb29RINgLIukoXr5LIAvYQ5ve+1FqWEMqNKTX3FAJHyQDRZvmKWubAACcv5z5Gtg2oyCWE+Yk/8JZQX1jTTCpKAFGIgza+dJCNBF2UskRlsgwitHbSV0QLgt9sTPtsRlvJjEr8C/FARWA2bJ/TtJ7lko34dNDn6usJUMzuErP89UUBJbWeozrwLLncXczd508deAjLWipLO4Q5XGPcJvPu92cNDaN0P5G1FL0nSOzddZOrJ6rNhbXGmeDvO3TF7DeJWl4bvaYQTNHCTeuqKZmbjHaSOFes+IX/+IhHrnAkXOAsfn24EM68XieIECoccD4KZLk/odiwzeo2rovYdhvb2HYFgyznJyDpYJdYOmfXgVdJTaUi4xA2uWYNYec9BLeqdl9EsoTw582mSFDX2DxVLbNt9U3YYoeatBad1c2Tj8t2akrjaIGJNywKB/7h75/gN3vCMSaadIUTAAAAAElFTkSuQmCC");this.image=a||engineFontImage;this.tileSize=b;this.paddingSize=c;this.context=d}drawText(a,b,c=1,d){this.drawTextScreen(a,worldToScreen(b).floor(),c*cameraScale|0,d)}drawTextScreen(a,b,c=4,d){const e=this.context;e.save();const f=this.tileSize,g=f.add(this.paddingSize).scale(c),k=this.image.width/this.tileSize.x|0;(a+"").split("\n").forEach((h,m)=>{const n=d?h.length*f.x*c/2|0:0;for(let q=h.length;q--;){var l=h[q].charCodeAt(0);if(32>l||127<l)l=127;var p=l-32;l=p%k;p=p/k|0;const r=b.add(vec2(q,m).multiply(g));e.drawImage(this.image,l*f.x,p*f.y,f.x,f.y,r.x-n,r.y,f.x*c,f.y*c)}});e.restore()}}function isFullscreen(){return!!document.fullscreenElement}function toggleFullscreen(){const a=mainCanvas.parentElement;isFullscreen()?document.exitFullscreen&&document.exitFullscreen():a.requestFullscreen&&a.requestFullscreen()}function setCursor(a="auto"){mainCanvas.parentElement.style.cursor=a}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 keyDirection(a="ArrowUp",b="ArrowDown",c="ArrowLeft",d="ArrowRight"){return vec2((keyIsDown(d)?1:0)-(keyIsDown(c)?1:0),(keyIsDown(a)?1:0)-(keyIsDown(b)?1:0))}function clearInput(){inputData=[[]];touchGamepadButtons=[]}const mouseIsDown=keyIsDown,mouseWasPressed=keyWasPressed,mouseWasReleased=keyWasReleased;let mousePos=vec2(),mousePosScreen=vec2(),mouseWheel=0,isUsingGamepad=!1,preventDefaultInput=!1;function gamepadIsDown(a,b=0){return keyIsDown(a,b+1)}function gamepadWasPressed(a,b=0){return keyWasPressed(a,b+1)}function gamepadWasReleased(a,b=0){return keyWasReleased(a,b+1)}function gamepadStick(a,b=0){return gamepadStickData[b]?gamepadStickData[b][a]||vec2():vec2()}let inputData=[[]];function inputUpdate(){headlessMode||(touchInputEnable&&isTouchDevice||document.hasFocus()||clearInput(),mousePos=screenToWorld(mousePosScreen),gamepadsUpdate())}function inputUpdatePost(){if(!headlessMode){for(const a of inputData)for(const b in a)a[b]&=1;mouseWheel=0}}function inputInit(){function a(b){return inputWASDEmulateDirection?"KeyW"==b?"ArrowUp":"KeyS"==b?"ArrowDown":"KeyA"==b?"ArrowLeft":"KeyD"==b?"ArrowRight":b:b}headlessMode||(onkeydown=b=>{b.repeat||(isUsingGamepad=!1,inputData[0][b.code]=3,inputWASDEmulateDirection&&(inputData[0][a(b.code)]=3));preventDefaultInput&&b.preventDefault()},onkeyup=b=>{inputData[0][b.code]=4;inputWASDEmulateDirection&&(inputData[0][a(b.code)]=4)},onmousedown=b=>{soundEnable&&!headlessMode&&audioContext&&"running"!=audioContext.state&&audioContext.resume();isUsingGamepad=!1;inputData[0][b.button]=3;mousePosScreen=mouseEventToScreen(b);b.button&&b.preventDefault()},onmouseup=b=>inputData[0][b.button]=inputData[0][b.button]&2|4,onmousemove=b=>mousePosScreen=mouseEventToScreen(b),onwheel=b=>mouseWheel=b.ctrlKey?0:sign(b.deltaY),oncontextmenu=b=>!1,onblur=b=>clearInput(),isTouchDevice&&touchInputEnable&&touchInputInit())}function mouseEventToScreen(a){const b=mainCanvas.getBoundingClientRect(),c=percent(a.x,b.left,b.right);a=percent(a.y,b.top,b.bottom);return vec2(c*mainCanvas.width,a*mainCanvas.height)}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&&!headlessMode&&audioContext&&"running"!=audioContext.state&&audioContext.resume();const f=e.touches.length;if(f){const g=vec2(e.touches[0].clientX,e.touches[0].clientY);mousePosScreen=mouseEventToScreen(g);d?isUsingGamepad=touchGamepadEnable:inputData[0][0]=3}else d&&(inputData[0][0]=inputData[0][0]&2|4);d=f;document.hasFocus()&&e.preventDefault();return!0}function b(e){touchGamepadStick=vec2();touchGamepadButtons=[];isUsingGamepad=!0;if(e.touches.length&&(touchGamepadTimer.set(),paused&&!d)){touchGamepadButtons[9]=1;a(e);return}const f=vec2(touchGamepadSize,mainCanvasSize.y-touchGamepadSize),g=mainCanvasSize.subtract(vec2(touchGamepadSize,touchGamepadSize)),k=mainCanvasSize.scale(.5);for(const m of e.touches){var h=mouseEventToScreen(vec2(m.clientX,m.clientY));h.distance(f)<touchGamepadSize?touchGamepadStick=h.subtract(f).scale(2/touchGamepadSize).clampLength():h.distance(g)<touchGamepadSize?(h=h.subtract(g).direction(),touchGamepadButtons[h]=1):h.distance(k)<touchGamepadSize&&!d&&(touchGamepadButtons[9]=1)}a(e);return!0}let c=a;touchGamepadEnable&&(c=b,touchGamepadButtons=[],touchGamepadStick=vec2());document.addEventListener("touchstart",e=>c(e),{passive:!1});document.addEventListener("touchmove",e=>c(e),{passive:!1});document.addEventListener("touchend",e=>c(e),{passive:!1});onmousedown=onmouseup=()=>0;let d}function touchGamepadRender(){if(touchInputEnable&&isTouchDevice&&!headlessMode&&touchGamepadEnable&&touchGamepadTimer.isSet()){var a=percent(touchGamepadTimer.get(),4,3);if(a&&!paused){var b=overlayContext;b.save();b.globalAlpha=a*touchGamepadAlpha;b.strokeStyle="#fff";b.lineWidth=3;b.fillStyle=0<touchGamepadStick.lengthSquared()?"#fff":"#000";b.beginPath();a=vec2(touchGamepadSize,mainCanvasSize.y-touchGamepadSize);if(touchGamepadAnalog)b.arc(a.x,a.y,touchGamepadSize/2,0,9),b.fill();else for(var c=10;c--;){var d=c*PI/4;b.arc(a.x,a.y,.6*touchGamepadSize,d+PI/8,d+PI/8);c%2&&b.arc(a.x,a.y,.33*touchGamepadSize,d,d);1==c&&b.fill()}b.stroke();a=vec2(mainCanvasSize.x-touchGamepadSize,mainCanvasSize.y-touchGamepadSize);for(c=4;c--;)d=a.add(vec2().setDirection(c,touchGamepadSize/2)),b.fillStyle=touchGamepadButtons[c]?"#fff":"#000",b.beginPath(),b.arc(d.x,d.y,touchGamepadSize/4,0,9),b.fill(),b.stroke();b.restore()}}}let audioContext=new AudioContext,audioGainNode;function audioInit(){soundEnable&&!headlessMode&&(audioGainNode=audioContext.createGain(),audioGainNode.connect(audioContext.destination),audioGainNode.gain.value=soundVolume)}class Sound{constructor(a,b=soundDefaultRange,c=soundDefaultTaper){soundEnable&&!headlessMode&&(this.range=b,this.taper=c,this.randomness=0,a&&(this.randomness=void 0!=a[1]?a[1]:.05,a[1]=0,this.sampleChannels=[zzfxG(...a)],this.sampleRate=zzfxR))}play(a,b=1,c=1,d=1,e=!1){if(soundEnable&&!headlessMode&&this.sampleChannels){var f;if(a){if(f=this.range){const g=cameraPos.distanceSquared(a);if(g>f*f)return;b*=percent(g**.5,f,f*this.taper)}f=2*worldToScreen(a).x/mainCanvas.width-1}a=c+c*this.randomness*d*rand(-1,1);this.gainNode=audioContext.createGain();return this.source=playSamples(this.sampleChannels,b,a,f,e,this.sampleRate,this.gainNode)}}setVolume(a=1){this.gainNode&&(this.gainNode.gain.value=a)}stop(){this.source&&this.source.stop();this.source=void 0}getSource(){return this.source}playNote(a,b,c){return this.play(b,c,2**(a/12),0)}getDuration(){return this.sampleChannels&&this.sampleChannels[0].length/this.sampleRate}isLoading(){return!this.sampleChannels}}class SoundWave extends Sound{constructor(a,b=0,c,d,e){super(void 0,c,d);soundEnable&&!headlessMode&&(this.randomness=b,fetch(a).then(f=>f.arrayBuffer()).then(f=>audioContext.decodeAudioData(f)).then(f=>{this.sampleChannels=[];for(let g=f.numberOfChannels;g--;)this.sampleChannels[g]=Array.from(f.getChannelData(g));this.sampleRate=f.sampleRate}).then(()=>e&&e(this)))}}function playAudioFile(a,b=1,c=!1){if(soundEnable&&!headlessMode)return new SoundWave(a,0,0,0,d=>d.play(void 0,b,1,1,c))}class Music extends Sound{constructor(a){super(void 0);soundEnable&&!headlessMode&&(this.randomness=0,this.sampleChannels=zzfxM(...a),this.sampleRate=zzfxR)}playMusic(a,b=!1){return super.play(void 0,a,1,1,b)}}function speak(a,b="",c=1,d=1,e=1){if(soundEnable&&!headlessMode&&speechSynthesis)return a=new SpeechSynthesisUtterance(a),a.lang=b,a.volume=2*c*soundVolume,a.rate=d,a.pitch=e,speechSynthesis.speak(a),a}function speakStop(){speechSynthesis&&speechSynthesis.cancel()}function getNoteFrequency(a,b=220){return b*2**(a/12)}function playSamples(a,b=1,c=1,d=0,e=!1,f=zzfxR,g){if(soundEnable&&!headlessMode){var k=audioContext.createBuffer(a.length,a[0].length,f),h=audioContext.createBufferSource();a.forEach((m,n)=>k.getChannelData(n).set(m));h.buffer=k;h.playbackRate.value=c;h.loop=e;g=g||audioContext.createGain();g.gain.value=b;g.connect(audioGainNode);a=new StereoPannerNode(audioContext,{pan:clamp(d,-1,1)});h.connect(a).connect(g);"running"!=audioContext.state?audioContext.resume().then(()=>h.start()):h.start();return h}}function zzfx(...a){return playSamples([zzfxG(...a)])}const zzfxR=44100;function zzfxG(a=1,b=.05,c=220,d=0,e=0,f=.1,g=0,k=1,h=0,m=0,n=0,l=0,p=0,q=0,r=0,x=0,v=0,D=1,z=0,E=0,A=0){let w=2*PI;var t=zzfxR;let F=h*=500*w/t/t;b=c*=rand(1+b,1-b)*w/t;let C=[],y=0,G=0,u=0,H=1,R=0,S=0,B=0,J;var L=w*abs(A)*2/t,K=Math.cos(L),M=Math.sin(L)/2/2,I=1+M;L=-2*K/I;M=(1-M)/I;let N=(1+sign(A)*K)/2/I;K=-(sign(A)+K)/I;let O=I=0,P=0,Q=0;d=d*t+9;z*=t;e*=t;f*=t;v*=t;m*=500*w/t**3;r*=w/t;n*=w/t;l*=t;p=p*t|0;for(J=d+z+e+f+v|0;u<J;C[u++]=B*a)++S%(100*x|0)||(B=g?1<g?2<g?3<g?Math.sin(y**3):clamp(Math.tan(y),1,-1):1-(2*y/w%2+2)%2:1-4*abs(Math.round(y/w)-y/w):Math.sin(y),B=(p?1-E+E*Math.sin(w*u/p):1)*sign(B)*abs(B)**k*(u<d?u/d:u<d+z?1-(u-d)/z*(1-D):u<d+z+e?D:u<J-v?(J-u-v)/f*D:0),B=v?B/2+(v>u?0:(u<J-v?1:(J-u)/v)*C[u-v|0]/2/a):B,A&&(B=Q=N*I+K*(I=O)+N*(O=B)-M*P-L*(P=Q))),t=(c+=h+=m)*Math.cos(r*G++),y+=t+t*q*Math.sin(u**5),H&&++H>l&&(c+=n,b+=n,H=0),!p||++R%p||(c=b,h=F,H=H||1);return C}function zzfxM(a,b,c,d=125){let e,f,g,k,h,m,n,l,p,q,r,x,v,D=0,z,E=[],A=[],w=[],t=0,F=0,C=1,y={},G=zzfxR/d*60>>2;for(;C;t++)E=[C=l=x=0],c.forEach((u,H)=>{n=b[u][t]||[0,0,0];C|=b[u][t]&&1;z=x+(b[u][0].length-2-(l?0:1))*G;v=H==c.length-1;e=2;for(g=x;e<n.length+v;l=++e){h=n[e];p=e==n.length+v-1&&v||q!=(n[0]||0)||h|0;for(f=0;f<G&&l;f++>G-99&&p&&1>r?r+=1/99:0)m=(1-r)*E[D++]/2||0,A[g]=(A[g]||0)-m*F+m,w[g]=(w[g++]||0)+m*F+m;h&&(r=h%1,F=n[1]||0,h|=0)&&(E=y[[q=n[D=0]||0,h]]=y[[q,h]]||(k=[...a[q]],k[2]*=2**((h-12)/12),0<h?zzfxG(...k):[]))}x=z});return[A,w]}let tileCollision=[],tileCollisionSize=vec2();function initTileCollision(a){tileCollisionSize=a;tileCollision=[];for(a=tileCollision.length=tileCollisionSize.area();a--;)tileCollision[a]=0}function setTileCollisionData(a,b=0){a.arrayCheck(tileCollisionSize)&&(tileCollision[(a.y|0)*tileCollisionSize.x+a.x|0]=b)}function getTileCollisionData(a){return a.arrayCheck(tileCollisionSize)?tileCollision[(a.y|0)*tileCollisionSize.x+a.x|0]:0}function tileCollisionTest(a,b=vec2(),c){const d=max(a.x-b.x/2|0,0);var e=max(a.y-b.y/2|0,0);const f=min(a.x+b.x/2,tileCollisionSize.x);for(a=min(a.y+b.y/2,tileCollisionSize.y);e<a;++e)for(b=d;b<f;++b){const g=tileCollision[e*tileCollisionSize.x+b];if(g&&(!c||c.collideWithTile(g,vec2(b,e))))return!0}return!1}function tileCollisionRaycast(a,b,c){const d=b.subtract(a),e=d.length();var f=d.normalize();f=vec2(abs(1/f.x),abs(1/f.y));let g=a.floor(),k=f.x*(0>d.x?a.x-g.x:g.x-a.x+1),h=f.y*(0>d.y?a.y-g.y:g.y-a.y+1);for(;;){const m=getTileCollisionData(g);if(m&&(!c||c.collideWithTile(m,g)))return debugRaycast&&debugLine(a,b,"#f00",.02),debugRaycast&&debugPoint(g.add(vec2(.5)),"#ff0"),g.add(vec2(.5));if(k>e&&h>e)break;k>h?(g.y+=sign(d.y),h+=f.y):(g.x+=sign(d.x),k+=f.x)}debugRaycast&&debugLine(a,b,"#00f",.02)}class TileLayerData{constructor(a,b=0,c=!1,d=new Color){this.tile=a;this.direction=b;this.mirror=c;this.color=d}clear(){this.tile=this.direction=0;this.mirror=!1;this.color=new Color}}class TileLayer extends EngineObject{constructor(a,b=tileCollisionSize,c=tile(),d=vec2(1),e=0){super(a,b,c,0,void 0,e);this.canvas=document.createElement("canvas");this.context=this.canvas.getContext("2d");this.scale=d;this.isOverlay=!1;this.data=[];for(a=this.size.area();a--;)this.data.push(new TileLayerData);headlessMode&&(this.redraw=()=>{},this.render=()=>{},this.redrawStart=()=>{},this.redrawEnd=()=>{},this.drawTileData=()=>{},this.drawCanvas2D=()=>{})}setData(a,b,c=!1){a.arrayCheck(this.size)&&(this.data[(a.y|0)*this.size.x+a.x|0]=b,c&&this.drawTileData(a))}getData(a){return a.arrayCheck(this.size)&&this.data[(a.y|0)*this.size.x+a.x|0]}update(){}render(){ASSERT(mainContext!=this.context,"must call redrawEnd() after drawing tiles");glOverlay||this.isOverlay||glCopyToContext(mainContext);let a=worldToScreen(this.pos.add(vec2(0,this.size.y*this.scale.y)));a=a.floor();(this.isOverlay?overlayContext:mainContext).drawImage(this.canvas,a.x,a.y,cameraScale*this.size.x*this.scale.x,cameraScale*this.size.y*this.scale.y)}redraw(){this.redrawStart(!0);for(let a=this.size.x;a--;)for(let b=this.size.y;b--;)this.drawTileData(vec2(a,b),!1);this.redrawEnd()}redrawStart(a=!1){this.savedRenderSettings=[mainCanvas,mainContext,mainCanvasSize,cameraPos,cameraScale];mainCanvas=this.canvas;mainContext=this.context;mainCanvasSize=this.size.multiply(this.tileInfo.size);cameraPos=this.size.scale(.5);cameraScale=this.tileInfo.size.x;a&&(mainCanvas.width=mainCanvasSize.x,mainCanvas.height=mainCanvasSize.y);this.context.imageSmoothingEnabled=!tilesPixelated;glPreRender()}redrawEnd(){ASSERT(mainContext==this.context,"must call redrawStart() before drawing tiles");glCopyToContext(mainContext,!0);[mainCanvas,mainContext,mainCanvasSize,cameraPos,cameraScale]=this.savedRenderSettings}drawTileData(a,b=!0){var c=this.tileInfo.size;b&&(b=a.multiply(c),this.context.clearRect(b.x,this.canvas.height-b.y,c.x,-c.y));b=this.getData(a);void 0!=b.tile&&(ASSERT(mainContext==this.context,"must call redrawStart() before drawing tiles"),a=a.add(vec2(.5)),c=tile(b.tile,c,this.tileInfo.textureIndex),drawTile(a,vec2(1),c,b.color,b.direction*PI/2,b.mirror))}drawCanvas2D(a,b,c,d,e){const f=this.context;f.save();a=a.subtract(this.pos).multiply(this.tileInfo.size);b=b.multiply(this.tileInfo.size);f.translate(a.x,this.canvas.height-a.y);f.rotate(c);f.scale(d?-b.x:b.x,b.y);e(f);f.restore()}drawTile(a,b=vec2(1),c,d=new Color,e,f){this.drawCanvas2D(a,b,e,f,g=>{const k=c&&c.getTextureInfo();k?(g.globalAlpha=d.a,g.drawImage(k.image,c.pos.x,c.pos.y,c.size.x,c.size.y,-.5,-.5,1,1),g.globalAlpha=1):(g.fillStyle=d,g.fillRect(-.5,-.5,1,1))})}drawRect(a,b,c,d){this.drawTile(a,b,void 0,c,d)}}class ParticleEmitter extends EngineObject{constructor(a,b,c=0,d=0,e=100,f=PI,g,k=new Color,h=new Color,m=new Color(1,1,1,0),n=new Color(1,1,1,0),l=.5,p=.1,q=1,r=.1,x=.05,v=1,D=1,z=0,E=PI,A=.1,w=.2,t=!1,F=!1,C=!0,y=F?1e9:0,G=!1){super(a,vec2(),g,b,void 0,y);this.emitSize=c;this.emitTime=d;this.emitRate=e;this.emitConeAngle=f;this.colorStartA=k;this.colorStartB=h;this.colorEndA=m;this.colorEndB=n;this.randomColorLinear=C;this.particleTime=l;this.sizeStart=p;this.sizeEnd=q;this.speed=r;this.angleSpeed=x;this.damping=v;this.angleDamping=D;this.gravityScale=z;this.particleConeAngle=E;this.fadeRate=A;this.randomness=w;this.collideTiles=t;this.additive=F;this.localSpace=G;this.trailScale=0;this.particleCreateCallback=this.particleDestroyCallback=void 0;this.emitTimeBuffer=0}update(){this.parent&&super.update();if(!this.emitTime||this.getAliveTime()<=this.emitTime){if(this.emitRate*particleEmitRateScale){const a=1/this.emitRate/particleEmitRateScale;for(this.emitTimeBuffer+=timeDelta;0<this.emitTimeBuffer;this.emitTimeBuffer-=a)this.emitParticle()}}else this.destroy();debugParticles&&debugRect(this.pos,vec2(this.emitSize),"#0f0",0,this.angle)}emitParticle(){var a="number"===typeof this.emitSize?randInCircle(this.emitSize/2):vec2(rand(-.5,.5),rand(-.5,.5)).multiply(this.emitSize).rotate(this.angle);let b=rand(this.particleConeAngle,-this.particleConeAngle);this.localSpace||(a=this.pos.add(a),b+=this.angle);const c=this.randomness;var d=l=>l+l*rand(c,-c);const e=d(this.particleTime),f=d(this.sizeStart),g=d(this.sizeEnd),k=d(this.speed);d=d(this.angleSpeed)*randSign();var h=rand(this.emitConeAngle,-this.emitConeAngle);const m=randColor(this.colorStartA,this.colorStartB,this.randomColorLinear),n=randColor(this.colorEndA,this.colorEndB,this.randomColorLinear);h=this.localSpace?h:this.angle+h;a=new Particle(a,this.tileInfo,b,m,n,e,f,g,this.fadeRate,this.additive,this.trailScale,this.localSpace&&this,this.particleDestroyCallback);a.velocity=vec2().setAngle(h,k);a.angleVelocity=d;a.fadeRate=this.fadeRate;a.damping=this.damping;a.angleDamping=this.angleDamping;a.elasticity=this.elasticity;a.friction=this.friction;a.gravityScale=this.gravityScale;a.collideTiles=this.collideTiles;a.renderOrder=this.renderOrder;a.mirror=!!randInt(2);this.particleCreateCallback&&this.particleCreateCallback(a);return a}render(){}}class Particle extends EngineObject{constructor(a,b,c,d,e,f,g,k,h,m,n,l,p){super(a,vec2(),b,c);this.colorStart=d;this.colorEndDelta=e.subtract(d);this.lifeTime=f;this.sizeStart=g;this.sizeEndDelta=k-g;this.fadeRate=h;this.additive=m;this.trailScale=n;this.localSpaceEmitter=l;this.destroyCallback=p;this.clampSpeedLinear=!1}render(){const a=0<this.lifeTime?min((time-this.spawnTime)/this.lifeTime,1):1,b=vec2(this.sizeStart+a*this.sizeEndDelta);var c=this.fadeRate/2;c=new Color(this.colorStart.r+a*this.colorEndDelta.r,this.colorStart.g+a*this.colorEndDelta.g,this.colorStart.b+a*this.colorEndDelta.b,(this.colorStart.a+a*this.colorEndDelta.a)*(a<c?a/c:a>1-c?(1-a)/c:1));this.additive&&setBlendMode(!0);let d=this.pos,e=this.angle;this.localSpaceEmitter&&(d=this.localSpaceEmitter.pos.add(d.rotate(-this.localSpaceEmitter.angle)),e+=this.localSpaceEmitter.angle);if(this.trailScale){var f=this.velocity;this.localSpaceEmitter&&(f=f.rotate(-this.localSpaceEmitter.angle));var g=f.length();g&&(f=f.scale(1/g),g*=this.trailScale,b.y=max(b.x,g),e=f.angle(),drawTile(d.add(f.multiply(vec2(0,-g/2))),b,this.tileInfo,c,e,this.mirror))}else drawTile(d,b,this.tileInfo,c,e,this.mirror);this.additive&&setBlendMode();debugParticles&&debugRect(d,b,"#f005",0,e);1==a&&(this.color=c,this.size=b,this.destroyCallback&&this.destroyCallback(this),this.destroyed=1)}}const medals={};let medalsDisplayQueue=[],medalsSaveName,medalsDisplayTimeLast;function medalsInit(a){medalsSaveName=a;debugMedals||medalsForEach(b=>b.unlocked=!!localStorage[b.storageKey()]);engineAddPlugin(void 0,function(){if(medalsDisplayQueue.length){var b=medalsDisplayQueue[0],c=timeReal-medalsDisplayTimeLast;if(medalsDisplayTimeLast)if(c>medalDisplayTime)medalsDisplayTimeLast=0,medalsDisplayQueue.shift();else{const d=medalDisplayTime-medalDisplaySlideTime;b.render(c<medalDisplaySlideTime?1-c/medalDisplaySlideTime:c>d?(c-d)/medalDisplaySlideTime:0)}else medalsDisplayTimeLast=timeReal}})}function medalsForEach(a){Object.values(medals).forEach(b=>a(b))}class Medal{constructor(a,b,c="",d="🏆",e){ASSERT(0<=a&&!medals[a]);this.id=a;this.name=b;this.description=c;this.icon=d;this.unlocked=!1;e&&((this.image=new Image).src=e);medals[a]=this}unlock(){medalsPreventUnlock||this.unlocked||(ASSERT(medalsSaveName,"save name must be set"),localStorage[this.storageKey()]=this.unlocked=!0,medalsDisplayQueue.push(this))}render(a=0){const b=overlayContext;var c=min(medalDisplaySize.x,mainCanvas.width);const d=overlayCanvas.width-c;a*=-medalDisplaySize.y;b.save();b.beginPath();b.fillStyle=new Color(.9,.9,.9).toString();b.strokeStyle=new Color(0,0,0).toString();b.lineWidth=3;b.rect(d,a,c,medalDisplaySize.y);b.fill();b.stroke();b.clip();this.renderIcon(vec2(d+15+medalDisplayIconSize/2,a+medalDisplaySize.y/2));c=vec2(d+medalDisplayIconSize+30,a+28);drawTextScreen(this.name,c,38,new Color(0,0,0),0,void 0,"left");c.y+=32;drawTextScreen(this.description,c,24,new Color(0,0,0),0,void 0,"left");b.restore()}renderIcon(a,b=medalDisplayIconSize){this.image?overlayContext.drawImage(this.image,a.x-b/2,a.y-b/2,b,b):drawTextScreen(this.icon,a,.7*b,new Color(0,0,0))}storageKey(){return medalsSaveName+"_"+this.id}}let glCanvas,glContext,glAntialias=!0,glShader,glActiveTexture,glArrayBuffer,glGeometryBuffer,glPositionData,glColorData,glInstanceCount,glAdditive,glBatchAdditive;const gl_MAX_INSTANCES=1e4,gl_INDICES_PER_INSTANCE=11,gl_INSTANCE_BYTE_STRIDE=4*gl_INDICES_PER_INSTANCE,gl_INSTANCE_BUFFER_SIZE=gl_MAX_INSTANCES*gl_INSTANCE_BYTE_STRIDE;function glInit(){if(glEnable&&!headlessMode){glCanvas=document.createElement("canvas");glContext=glCanvas.getContext("webgl2",{antialias:glAntialias});var a=mainCanvas.parentElement;glOverlay&&a.appendChild(glCanvas);glShader=glCreateProgram("#version 300 es\nprecision highp float;uniform mat4 m;in vec2 g;in vec4 p,u,c,a;in float r;out vec2 v;out vec4 d,e;void main(){vec2 s=(g-.5)*p.zw;gl_Position=m*vec4(p.xy+s*cos(r)-vec2(-s.y,s)*sin(r),1,1);v=mix(u.xw,u.zy,g);d=c;e=a;}","#version 300 es\nprecision highp float;uniform sampler2D s;in vec2 v;in vec4 d,e;out vec4 c;void main(){c=texture(s,v)*d+e;}");a=new ArrayBuffer(gl_INSTANCE_BUFFER_SIZE);glPositionData=new Float32Array(a);glColorData=new Uint32Array(a);glArrayBuffer=glContext.createBuffer();glGeometryBuffer=glContext.createBuffer();a=new Float32Array([glInstanceCount=0,0,1,0,0,1,1,1]);glContext.bindBuffer(glContext.ARRAY_BUFFER,glGeometryBuffer);glContext.bufferData(glContext.ARRAY_BUFFER,a,glContext.STATIC_DRAW)}}function glPreRender(){if(glEnable&&!headlessMode){glClearCanvas();glContext.useProgram(glShader);glContext.activeTexture(glContext.TEXTURE0);textureInfos[0]&&glContext.bindTexture(glContext.TEXTURE_2D,glActiveTexture=textureInfos[0].glTexture);var a=glAdditive=glBatchAdditive=0,b=(d,e,f,g)=>{d=glContext.getAttribLocation(glShader,d);const k=f&&gl_INSTANCE_BYTE_STRIDE,h=f&&1,m=1==f;glContext.enableVertexAttribArray(d);glContext.vertexAttribPointer(d,g,e,m,k,a);glContext.vertexAttribDivisor(d,h);a+=g*f};glContext.bindBuffer(glContext.ARRAY_BUFFER,glGeometryBuffer);b("g",glContext.FLOAT,0,2);glContext.bindBuffer(glContext.ARRAY_BUFFER,glArrayBuffer);glContext.bufferData(glContext.ARRAY_BUFFER,gl_INSTANCE_BUFFER_SIZE,glContext.DYNAMIC_DRAW);b("p",glContext.FLOAT,4,4);b("u",glContext.FLOAT,4,4);b("c",glContext.UNSIGNED_BYTE,1,4);b("a",glContext.UNSIGNED_BYTE,1,4);b("r",glContext.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 glClearCanvas(){glContext.viewport(0,0,glCanvas.width=mainCanvas.width,glCanvas.height=mainCanvas.height);glContext.clear(glContext.COLOR_BUFFER_BIT)}function glSetTexture(a){headlessMode||a==glActiveTexture||(glFlush(),glContext.bindTexture(glContext.TEXTURE_2D,glActiveTexture=a))}function glCompileShader(a,b){b=glContext.createShader(b);glContext.shaderSource(b,a);glContext.compileShader(b);if(debug&&!glContext.getShaderParameter(b,glContext.COMPILE_STATUS))throw glContext.getShaderInfoLog(b);return b}function glCreateProgram(a,b){const c=glContext.createProgram();glContext.attachShader(c,glCompileShader(a,glContext.VERTEX_SHADER));glContext.attachShader(c,glCompileShader(b,glContext.FRAGMENT_SHADER));glContext.linkProgram(c);if(debug&&!glContext.getProgramParameter(c,glContext.LINK_STATUS))throw glContext.getProgramInfoLog(c);return c}function glCreateTexture(a){const b=glContext.createTexture();glContext.bindTexture(glContext.TEXTURE_2D,b);a&&a.width?glContext.texImage2D(glContext.TEXTURE_2D,0,glContext.RGBA,glContext.RGBA,glContext.UNSIGNED_BYTE,a):(a=new Uint8Array([255,255,255,255]),glContext.texImage2D(glContext.TEXTURE_2D,0,glContext.RGBA,1,1,0,glContext.RGBA,glContext.UNSIGNED_BYTE,a));a=tilesPixelated?glContext.NEAREST:glContext.LINEAR;glContext.texParameteri(glContext.TEXTURE_2D,glContext.TEXTURE_MIN_FILTER,a);glContext.texParameteri(glContext.TEXTURE_2D,glContext.TEXTURE_MAG_FILTER,a);return b}function glFlush(){if(glInstanceCount){var a=glBatchAdditive?glContext.ONE:glContext.ONE_MINUS_SRC_ALPHA;glContext.blendFuncSeparate(glContext.SRC_ALPHA,a,glContext.ONE,a);glContext.enable(glContext.BLEND);glContext.bufferSubData(glContext.ARRAY_BUFFER,0,glPositionData);glContext.drawArraysInstanced(glContext.TRIANGLE_STRIP,0,4,glInstanceCount);showWatermark&&(drawCount+=glInstanceCount);glInstanceCount=0;glBatchAdditive=glAdditive}}function glCopyToContext(a,b=!1){glEnable&&(glInstanceCount||b)&&(glFlush(),glOverlay&&!b||a.drawImage(glCanvas,0,0))}function glSetAntialias(a=!0){ASSERT(!glCanvas,"must be called before engineInit");glAntialias=a}function glDraw(a,b,c,d,e,f,g,k,h,m,n=0){ASSERT("number"==typeof m&&"number"==typeof n,"invalid color");(glInstanceCount>=gl_MAX_INSTANCES||glBatchAdditive!=glAdditive)&&glFlush();let l=glInstanceCount++*gl_INDICES_PER_INSTANCE;glPositionData[l++]=a;glPositionData[l++]=b;glPositionData[l++]=c;glPositionData[l++]=d;glPositionData[l++]=f;glPositionData[l++]=g;glPositionData[l++]=k;glPositionData[l++]=h;glColorData[l++]=m;glColorData[l++]=n;glPositionData[l++]=e}const engineName="LittleJS",engineVersion="1.11.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){ASSERT(!pluginUpdateList.includes(a));ASSERT(!pluginRenderList.includes(b));a&&pluginUpdateList.push(a);b&&pluginRenderList.push(b)}function engineInit(a,b,c,d,e,f=[],g=document.body){function k(n=0){var l=n-frameTimeLastMS;frameTimeLastMS=n;if(debug||showWatermark)averageFPS=lerp(.05,averageFPS,1e3/(l||1));n=debug&&keyIsDown("Equal");const p=debug&&keyIsDown("Minus");debug&&(l*=n?5:p?.2:1);timeReal+=l/1e3;frameTimeBufferMS+=paused?0:l;n||(frameTimeBufferMS=min(frameTimeBufferMS,50));h();if(paused){for(const r of engineObjects)r.parent||r.updateTransforms();inputUpdate();pluginUpdateList.forEach(r=>r());debugUpdate();c();inputUpdatePost()}else{l=0;0>frameTimeBufferMS&&-9<frameTimeBufferMS&&(l=frameTimeBufferMS,frameTimeBufferMS=0);for(;0<=frameTimeBufferMS;frameTimeBufferMS-=1e3/frameRate)time=frame++/frameRate,inputUpdate(),b(),pluginUpdateList.forEach(r=>r()),engineObjectsUpdate(),debugUpdate(),c(),inputUpdatePost();frameTimeBufferMS+=l}if(!headlessMode){mainCanvasSize=vec2(mainCanvas.width,mainCanvas.height);overlayContext.imageSmoothingEnabled=mainContext.imageSmoothingEnabled=!tilesPixelated;glPreRender();d();engineObjects.sort((r,x)=>r.renderOrder-x.renderOrder);for(var q of engineObjects)q.destroyed||q.render();e();pluginRenderList.forEach(r=>r());touchGamepadRender();debugRender();glCopyToContext(mainContext);showWatermark&&(overlayContext.textAlign="right",overlayContext.textBaseline="top",overlayContext.font="1em monospace",overlayContext.fillStyle="#000",q=engineName+" v"+engineVersion+" / "+drawCount+" / "+engineObjects.length+" / "+averageFPS.toFixed(1)+(glEnable?" GL":" 2D"),overlayContext.fillText(q,mainCanvas.width-3,3),overlayContext.fillStyle="#fff",overlayContext.fillText(q,mainCanvas.width-2,2),drawCount=0)}requestAnimationFrame(k)}function h(){if(!headlessMode){if(canvasFixedSize.x){mainCanvas.width=canvasFixedSize.x;mainCanvas.height=canvasFixedSize.y;const n=innerWidth/innerHeight,l=mainCanvas.width/mainCanvas.height;(glCanvas||mainCanvas).style.width=mainCanvas.style.width=overlayCanvas.style.width=n<l?"100%":"";(glCanvas||mainCanvas).style.height=mainCanvas.style.height=overlayCanvas.style.height=n<l?"":"100%"}else mainCanvas.width=min(innerWidth,canvasMaxSize.x),mainCanvas.height=min(innerHeight,canvasMaxSize.y);overlayCanvas.width=mainCanvas.width;overlayCanvas.height=mainCanvas.height;mainCanvasSize=vec2(mainCanvas.width,mainCanvas.height)}}function m(){new Promise(n=>n(a())).then(k)}ASSERT(!mainContext,"engine already initialized");ASSERT(Array.isArray(f),"pass in images as array");a||=()=>{};b||=()=>{};c||=()=>{};d||=()=>{};e||=()=>{};headlessMode?m():(g.style.cssText="margin:0;overflow:hidden;width:100vw;height:100vh;display:flex;align-items:center;justify-content:center;background:#000;"+(canvasPixelated?"image-rendering:pixelated;":"")+"user-select:none;-webkit-user-select:none;"+(touchInputEnable?"touch-action:none;-webkit-touch-callout:none":""),g.appendChild(mainCanvas=document.createElement("canvas")),mainContext=mainCanvas.getContext("2d"),inputInit(),audioInit(),debugInit(),glInit(),g.appendChild(overlayCanvas=document.createElement("canvas")),overlayContext=overlayCanvas.getContext("2d"),mainCanvas.style.cssText=overlayCanvas.style.cssText="position:absolute",glCanvas&&(glCanvas.style.cssText="position:absolute"),h(),g=f.map((n,l)=>new Promise(p=>{const q=new Image;q.crossOrigin="anonymous";q.onerror=q.onload=()=>{textureInfos[l]=new TextureInfo(q);p()};q.src=n})),f.length||g.push(new Promise(n=>{textureInfos[0]=new TextureInfo(new Image);n()})),showSplashScreen&&g.push(new Promise(n=>{function l(){clearInput();drawEngineSplashScreen(p+=.01);1<p?n():setTimeout(l,16)}let p=0;console.log(`${engineName} Engine v${engineVersion}`);l()})),Promise.all(g).then(m))}function engineObjectsUpdate(){function a(b){if(!b.destroyed){b.update();for(const c of b.children)a(c)}}engineObjectsCollide=engineObjects.filter(b=>b.collideSolidObjects);for(const b of engineObjects)b.parent||(a(b),b.updateTransforms());engineObjects=engineObjects.filter(b=>!b.destroyed)}function engineObjectsDestroy(){for(const a of engineObjects)a.parent||a.destroy();engineObjects=engineObjects.filter(a=>!a.destroyed)}function engineObjectsCollect(a,b,c=engineObjects){const d=[];if(a)if(b instanceof Vector2)for(const e of c)isOverlapping(a,b,e.pos,e.size)&&d.push(e);else{b*=b;for(const e of c)a.distanceSquared(e.pos)<b&&d.push(e)}else for(const e of c)d.push(e);return d}function engineObjectsCallback(a,b,c,d=engineObjects){engineObjectsCollect(a,b,d).forEach(e=>c(e))}function engineObjectsRaycast(a,b,c=engineObjects){const d=[];for(const e of c)e.collideRaycast&&isIntersecting(a,b,e.pos,e.size)&&(debugRaycast&&debugRect(e.pos,e.size,"#f00"),d.push(e));debugRaycast&&debugLine(a,b,d.length?"#f00":"#00f",.02);return d}function drawEngineSplashScreen(a){const b=overlayContext;var c=overlayCanvas.width=innerWidth,d=overlayCanvas.height=innerHeight,e=percent(a,1,.8),f=percent(a,0,.5),g=b.createRadialGradient(c/2,d/2,0,c/2,d/2,.7*Math.hypot(c,d));g.addColorStop(0,hsl(0,0,lerp(f,0,e/2),e).toString());g.addColorStop(1,hsl(0,0,0,e).toString());b.save();b.fillStyle=g;b.fillRect(0,0,c,d);g=(h,m,n,l,p)=>{b.beginPath();b.rect(h,m,n,p?l*k:l);(b.fillStyle=p)?b.fill():b.stroke()};f=(h,m,n,l=0,p=2*PI,q,r)=>{const x=(l+p)/2;l=k*(p-l)/2;b.beginPath();r&&b.lineTo(h,m);b.arc(h,m,n,x-l,x+l);(b.fillStyle=q)?b.fill():b.stroke()};e=(h=0,m=0)=>hsl([.98,.3,.57,.14][h%4]-10,.8,[0,.3,.5,.8,.9][m]).toString();a=wave(1,1,a);const k=percent(a,.1,.5);b.translate(c/2,d/2);c=min(6,min(c,d)/99);b.scale(c,c);b.translate(-40,-35);b.lineJoin=b.lineCap="round";b.lineWidth=.1+1.9*k;c=percent(a,.1,1);b.setLineDash([99*c,99]);g(7,16,18,-8,e(2,2));g(7,8,18,4,e(2,3));g(25,8,8,8,e(2,1));g(25,8,-18,8);g(25,8,8,8);g(25,16,7,23,e());g(11,39,14,-23,e(1,1));g(11,16,14,18,e(1,2));g(11,16,14,8,e(1,3));g(25,16,-14,24);g(15,29,6,-9,e(2,2));f(15,21,5,0,PI/2,e(2,4),1);g(21,21,-6,9);g(37,14,9,6,e(3,2));g(37,14,4.5,6,e(3,3));g(37,14,9,6);g(50,20,10,-8,e(0,1));g(50,20,6.5,-8,e(0,2));g(50,20,3.5,-8,e(0,3));g(50,20,10,-8);f(55,2,11.4,.5,PI-.5,e(3,3));f(55,2,11.4,.5,PI/2,e(3,2),1);f(55,2,11.4,.5,PI-.5);g(45,7,20,-7,e(0,2));g(45,-1,20,4,e(0,3));g(45,-1,20,8);for(c=5;c--;)f(60-6*c,30,9.9,0,2*PI,e(c+2,3)),f(60-6*c,30,10,-.5,PI+.5,e(c+2,2)),f(60-6*c,30,10.1,.5,PI-.5,e(c+2,1));f(36,30,10,PI/2,3*PI/2);f(48,30,10,PI/2,3*PI/2);f(60,30,10);b.beginPath();b.lineTo(36,20);b.lineTo(60,20);b.stroke();f(60,30,4,PI,3*PI,e(3,2));f(60,30,4,PI,2*PI,e(3,3));f(60,30,4,PI,3*PI);for(c=6;c--;)b.beginPath(),b.lineTo(53,54),b.lineTo(53,40),b.lineTo(53+(1+2.9*c)*k,40),b.lineTo(53+(4+3.5*c)*k,54),b.fillStyle=e(0,c%2+2),b.fill(),c%2&&b.stroke();g(6,40,5,5);g(6,40,5,5,e());g(15,54,38,-14,e());for(g=3;g--;)for(c=2;c--;)f(15*g+15,47,c?7:1,PI,3*PI,e(g,3)),b.stroke(),f(15*g+15,47,c?7:1,0,PI,e(g,2)),b.stroke();b.beginPath();b.lineTo(6,40);b.lineTo(68,40);b.stroke();b.beginPath();b.lineTo(77,54);b.lineTo(4,54);b.stroke();f=engineName;b.font="900 16px arial";b.textAlign="center";b.textBaseline="top";b.lineWidth=.1+3.9*k;g=0;for(c=0;c<f.length;++c)g+=b.measureText(f[c]).width;for(c=2;c--;)for(let h=0,m=41-g/2;h<f.length;++h)b.fillStyle=e(h,2),d=b.measureText(f[h]).width,b[c?"strokeText":"fillText"](f[h],m+d/2,55.5,17*k),m+=d;b.restore()}
@@ -312,7 +312,7 @@ class RandomGenerator
312
312
  this.seed ^= this.seed << 13;
313
313
  this.seed ^= this.seed >>> 17;
314
314
  this.seed ^= this.seed << 5;
315
- return valueB + (valueA - valueB) * abs(this.seed % 1e8) / 1e8;
315
+ return valueB + (valueA - valueB) * ((this.seed >>> 0) / 2**32);
316
316
  }
317
317
 
318
318
  /** Returns a floored seeded random value the two values passed in
@@ -501,11 +501,11 @@ class Vector2
501
501
  return this.x*v.y - this.y*v.x;
502
502
  }
503
503
 
504
- /** Returns the angle of this vector, up is angle 0
504
+ /** Returns the clockwise angle of this vector, up is angle 0
505
505
  * @return {Number} */
506
506
  angle() { return Math.atan2(this.x, this.y); }
507
507
 
508
- /** Sets this vector with angle and length passed in
508
+ /** Sets this vector with clockwise angle and length passed in
509
509
  * @param {Number} [angle]
510
510
  * @param {Number} [length]
511
511
  * @return {Vector2} */
@@ -516,13 +516,13 @@ class Vector2
516
516
  return this;
517
517
  }
518
518
 
519
- /** Returns copy of this vector rotated by the angle passed in
519
+ /** Returns copy of this vector rotated by the clockwise angle passed in
520
520
  * @param {Number} angle
521
521
  * @return {Vector2} */
522
522
  rotate(angle)
523
523
  {
524
- const c = Math.cos(angle), s = Math.sin(angle);
525
- return new Vector2(this.x*c + this.y*s, this.x*s + this.y*c);
524
+ const c = Math.cos(-angle), s = Math.sin(-angle);
525
+ return new Vector2(this.x*c - this.y*s, this.x*s + this.y*c);
526
526
  }
527
527
 
528
528
  /** Set the integer direction of this vector, corresponding to multiples of 90 degree rotation (0-3)
@@ -857,57 +857,57 @@ class Color
857
857
  ///////////////////////////////////////////////////////////////////////////////
858
858
  // default colors
859
859
 
860
- /** Color - White
860
+ /** Color - White #ffffff
861
861
  * @type {Color}
862
862
  * @memberof Utilities */
863
- const WHITE = rgb();
863
+ const WHITE = rgb();
864
864
 
865
- /** Color - Black
865
+ /** Color - Black #000000
866
866
  * @type {Color}
867
867
  * @memberof Utilities */
868
868
  const BLACK = rgb(0,0,0);
869
869
 
870
- /** Color - Gray
870
+ /** Color - Gray #808080
871
871
  * @type {Color}
872
872
  * @memberof Utilities */
873
873
  const GRAY = rgb(.5,.5,.5);
874
874
 
875
- /** Color - Red
875
+ /** Color - Red #ff0000
876
876
  * @type {Color}
877
877
  * @memberof Utilities */
878
878
  const RED = rgb(1,0,0);
879
879
 
880
- /** Color - Orange
880
+ /** Color - Orange #ff8000
881
881
  * @type {Color}
882
882
  * @memberof Utilities */
883
883
  const ORANGE = rgb(1,.5,0);
884
884
 
885
- /** Color - Yellow
885
+ /** Color - Yellow #ffff00
886
886
  * @type {Color}
887
887
  * @memberof Utilities */
888
888
  const YELLOW = rgb(1,1,0);
889
889
 
890
- /** Color - Green
890
+ /** Color - Green #00ff00
891
891
  * @type {Color}
892
892
  * @memberof Utilities */
893
893
  const GREEN = rgb(0,1,0);
894
894
 
895
- /** Color - Cyan
895
+ /** Color - Cyan #00ffff
896
896
  * @type {Color}
897
897
  * @memberof Utilities */
898
898
  const CYAN = rgb(0,1,1);
899
899
 
900
- /** Color - Blue
900
+ /** Color - Blue #0000ff
901
901
  * @type {Color}
902
902
  * @memberof Utilities */
903
903
  const BLUE = rgb(0,0,1);
904
904
 
905
- /** Color - Purple
905
+ /** Color - Purple #8000ff
906
906
  * @type {Color}
907
907
  * @memberof Utilities */
908
908
  const PURPLE = rgb(.5,0,1);
909
909
 
910
- /** Color - Magenta
910
+ /** Color - Magenta #ff00ff
911
911
  * @type {Color}
912
912
  * @memberof Utilities */
913
913
  const MAGENTA = rgb(1,0,1);
@@ -1577,7 +1577,7 @@ class EngineObject
1577
1577
  {
1578
1578
  // copy parent pos/angle
1579
1579
  const mirror = parent.getMirrorSign();
1580
- this.pos = this.localPos.multiply(vec2(mirror,1)).rotate(-parent.angle).add(parent.pos);
1580
+ this.pos = this.localPos.multiply(vec2(mirror,1)).rotate(parent.angle).add(parent.pos);
1581
1581
  this.angle = mirror*this.localAngle + parent.angle;
1582
1582
  }
1583
1583
 
@@ -1805,11 +1805,11 @@ class EngineObject
1805
1805
 
1806
1806
  /** Convert from local space to world space for a vector (rotation only)
1807
1807
  * @param {Vector2} vec - local space vector */
1808
- localToWorldVector(vec) { return vec.rotate(-this.angle); }
1808
+ localToWorldVector(vec) { return vec.rotate(this.angle); }
1809
1809
 
1810
1810
  /** Convert from world space to local space for a vector (rotation only)
1811
1811
  * @param {Vector2} vec - world space vector */
1812
- worldToLocalVector(vec) { return vec.rotate(this.angle); }
1812
+ worldToLocalVector(vec) { return vec.rotate(-this.angle); }
1813
1813
 
1814
1814
  /** Called to check if a tile collision should be resolved
1815
1815
  * @param {Number} tileData - the value of the tile at the position
@@ -2130,6 +2130,8 @@ function drawTile(pos, size=vec2(1), tileInfo, color=new Color,
2130
2130
  ASSERT(!context || !useWebGL, 'context only supported in canvas 2D mode');
2131
2131
  ASSERT(typeof tileInfo !== 'number' || !tileInfo,
2132
2132
  'this is an old style calls, to fix replace it with tile(tileIndex, tileSize)');
2133
+ ASSERT(isVector2(pos) && isVector2(size));
2134
+ ASSERT(isColor(color) && isColor(additiveColor));
2133
2135
 
2134
2136
  const textureInfo = tileInfo && tileInfo.getTextureInfo();
2135
2137
  if (useWebGL)
@@ -2183,7 +2185,7 @@ function drawTile(pos, size=vec2(1), tileInfo, color=new Color,
2183
2185
  else
2184
2186
  {
2185
2187
  // if no tile info, force untextured
2186
- context.fillStyle = color;
2188
+ context.fillStyle = color.toString();
2187
2189
  context.fillRect(-.5, -.5, 1, 1);
2188
2190
  }
2189
2191
  }, screenSpace, context);
@@ -2230,6 +2232,7 @@ function drawLine(posA, posB, thickness=.1, color, useWebGL, screenSpace, contex
2230
2232
  * @memberof Draw */
2231
2233
  function drawPoly(points, color=new Color, lineWidth=0, lineColor=new Color(0,0,0), screenSpace, context=mainContext)
2232
2234
  {
2235
+ ASSERT(isColor(color) && isColor(lineColor));
2233
2236
  context.fillStyle = color.toString();
2234
2237
  context.beginPath();
2235
2238
  for (const point of screenSpace ? points : points.map(worldToScreen))
@@ -2257,6 +2260,7 @@ function drawPoly(points, color=new Color, lineWidth=0, lineColor=new Color(0,0,
2257
2260
  * @memberof Draw */
2258
2261
  function drawEllipse(pos, width=1, height=1, angle=0, color=new Color, lineWidth=0, lineColor=new Color(0,0,0), screenSpace, context=mainContext)
2259
2262
  {
2263
+ ASSERT(isColor(color) && isColor(lineColor));
2260
2264
  if (!screenSpace)
2261
2265
  {
2262
2266
  pos = worldToScreen(pos);
@@ -4877,7 +4881,7 @@ const engineName = 'LittleJS';
4877
4881
  * @type {String}
4878
4882
  * @default
4879
4883
  * @memberof Engine */
4880
- const engineVersion = '1.11.7';
4884
+ const engineVersion = '1.11.9';
4881
4885
 
4882
4886
  /** Frames per second to update
4883
4887
  * @type {Number}