-
-
Notifications
You must be signed in to change notification settings - Fork 215
Expand file tree
/
Copy pathlittlejs.min.js
More file actions
4 lines (3 loc) · 138 KB
/
littlejs.min.js
File metadata and controls
4 lines (3 loc) · 138 KB
1
2
3
4
// LittleJS Engine - MIT License - Copyright 2021 Frank Force
// https://github.com/KilledByAPixel/LittleJS
const engineName="LittleJS",engineVersion="1.18.0",frameRate=60,timeDelta=1/frameRate;let engineObjects=[],engineObjectsCollide=[],frame=0,time=0,timeReal=0,paused=!1;function getPaused(){return paused}function setPaused(a=!0){paused=a}let frameTimeLastMS=0,frameTimeBufferMS=0,averageFPS=0,showEngineVersion=!0;const pluginList=[];class EnginePlugin{constructor(a,b,c,d){this.update=a;this.render=b;this.glContextLost=c;this.glContextRestored=d}}function engineAddPlugin(a,b,c,d){ASSERT(!pluginList.find(f=>f.update===a&&f.render===b&&f.glContextLost===c&&f.glContextRestored===d));const e=new EnginePlugin(a,b,c,d);pluginList.push(e)}async function engineInit(a,b,c,d,e,f=[],g=document.body){function h(m=0){function n(){if(!headlessMode){r||k();mainCanvasSize=vec2(mainCanvas.width,mainCanvas.height);mainContext.imageSmoothingEnabled=!tilesPixelated;glPreRender();d();engineObjects.sort((t,u)=>t.renderOrder-u.renderOrder);for(const t of engineObjects)t.destroyed||t.render();e();pluginList.forEach(t=>t.render?.());inputRender();debugRender();glFlush();debugRenderPost();drawCount=0}}var p=m-frameTimeLastMS;frameTimeLastMS=m;if(debug||debugWatermark)averageFPS=lerp(averageFPS,1e3/(p||1),.05);m=debug&&keyIsDown("Equal");const q=debug&&keyIsDown("Minus");debug&&(p*=m?10:q?.1:1);timeReal+=p/1e3;frameTimeBufferMS+=paused?0:p;m||(frameTimeBufferMS=min(frameTimeBufferMS,50));let r=!1;if(paused){r=!0;k();inputUpdate();pluginList.forEach(t=>t.update?.());for(const t of engineObjects)t.parent||t.updateTransforms();debugUpdate();c();inputUpdatePost();debugVideoCaptureIsActive()&&n()}else{p=0;0>frameTimeBufferMS&&-9<frameTimeBufferMS&&(p=frameTimeBufferMS,frameTimeBufferMS=0);for(;0<=frameTimeBufferMS;frameTimeBufferMS-=1e3/frameRate)time=frame++/frameRate,r=!0,k(),inputUpdate(),b(),pluginList.forEach(t=>t.update?.()),engineObjectsUpdate(),debugUpdate(),c(),inputUpdatePost(),debugVideoCaptureIsActive()&&n();frameTimeBufferMS+=p}debugVideoCaptureIsActive()||n();requestAnimationFrame(h)}function k(){if(!headlessMode){if(canvasFixedSize.x){mainCanvasSize=canvasFixedSize.copy();var m=innerWidth/innerHeight;const p=canvasFixedSize.x/canvasFixedSize.y;var n=m<p?"100%":"";m=m<p?"":"100%";mainCanvas.style.width=n;mainCanvas.style.height=m;glCanvas&&(glCanvas.style.width=n,glCanvas.style.height=m)}else mainCanvasSize.x=min(innerWidth,canvasMaxSize.x),mainCanvasSize.y=min(innerHeight,canvasMaxSize.y),n=innerWidth/innerHeight,ASSERT(canvasMinAspect<=canvasMaxAspect),canvasMaxAspect&&n>canvasMaxAspect?mainCanvasSize.x=min(mainCanvasSize.y*canvasMaxAspect|0,canvasMaxSize.x):n<canvasMinAspect&&(mainCanvasSize.y=min(mainCanvasSize.x/canvasMinAspect|0,canvasMaxSize.y));mainCanvas.width=mainCanvasSize.x;mainCanvas.height=mainCanvasSize.y;0<canvasClearColor.a&&!glEnable&&(mainContext.fillStyle=canvasClearColor.toString(),mainContext.fillRect(0,0,mainCanvasSize.x,mainCanvasSize.y),mainContext.fillStyle=BLACK.toString());mainContext.lineJoin="round";mainContext.lineCap="round"}}async function l(){await a();h()}showEngineVersion&&console.log(`${engineName} Engine v${engineVersion}`);ASSERT(!mainContext,"engine already initialized");ASSERT(isArray(f),"pass in images as array");a||=()=>{};b||=()=>{};c||=()=>{};d||=()=>{};e||=()=>{};if(headlessMode)return l();glInit(g);g.style.cssText="margin:0;overflow:hidden;background:#000;user-select:none;-webkit-user-select:none;touch-action:none;-webkit-touch-callout:none";mainCanvas=g.appendChild(document.createElement("canvas"));drawContext=mainContext=mainCanvas.getContext("2d");inputInit();audioInit();debugInit();mainCanvas.style.cssText="position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)";glCanvas&&(glCanvas.style.cssText="position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)");setCanvasPixelated(canvasPixelated);k();glPreRender();workCanvas=new OffscreenCanvas(64,64);workContext=workCanvas.getContext("2d");workReadCanvas=new OffscreenCanvas(64,64);workReadContext=workReadCanvas.getContext("2d",{willReadFrequently:!0});g=f.map((m,n)=>loadTexture(n,m));f.length||g.push(loadTexture(0));g.push(fontImageInit());showSplashScreen&&g.push(new Promise(m=>{function n(){inputClear();drawEngineLogo(p+=.01);1<p?m():setTimeout(n,16)}let p=0;n()}));await Promise.all(g);return l()}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)if(!b.parent&&!b.destroyed){b.update();b.updatePhysics();for(const c of b.children)a(c);b.updateTransforms()}engineObjects=engineObjects.filter(b=>!b.destroyed)}function engineObjectsDestroy(a=!0){for(const b of engineObjects)b.parent||b.destroy(a);engineObjects=engineObjects.filter(b=>!b.destroyed)}function engineObjectsCollect(a,b,c=engineObjects){const d=[];if(a)if(b instanceof Vector2)for(const e of c)e.isOverlapping(a,b)&&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 drawEngineLogo(a){const b=mainContext;var c=mainCanvas.width=innerWidth,d=mainCanvas.height=innerHeight,e=percent(a,1,.8),f=percent(a,0,.5),g=b.createRadialGradient(c/2,d/2,0,c/2,d/2,.6*hypot(c,d));g.addColorStop(0,hsl(0,0,lerp(0,e/2,f),e).toString());g.addColorStop(1,hsl(0,0,0,e).toString());b.save();b.fillStyle=g;b.fillRect(0,0,c,d);const h=(l,m,n,p,q,r=1)=>{0<=q?(l=b.fillStyle=b.createLinearGradient(l,m,n,p),l.addColorStop(0,`hsl(${360*[.95,.56,.13][q%3]} 99%${75}%`),l.addColorStop(1,`hsl(${360*[.95,.56,.13][q%3]} 99%${50}%`)):b.fillStyle="#000";-1<=q?(b.fill(),r&&b.stroke()):b.stroke()};f=(l,m,n,p=0,q=2*PI,r,t)=>{b.beginPath();b.arc(l,m,n,k*p,k*q);h(l,m-n,l,m+n,r,t)};g=(l,m,n,p,q)=>{b.beginPath();b.rect(l,m,n,p*k);h(l,m+p,l+n,m,q)};e=(l,m,n,p)=>{b.beginPath();for(const q of l)b.lineTo(q.x,q.y);b.closePath();h(0,n,0,n+p,m)};a=oscillate(1,1,a);const k=percent(a,.1,.5);a=min(6,min(c,d)/99);b.translate(c/2,d/2);b.scale(a,a);b.translate(-40,-35);1>k&&b.setLineDash([99*k,99]);b.lineJoin=b.lineCap="round";b.lineWidth=.1+1.9*k;b.font="900 15.5px arial";b.lineWidth=.1+3.9*k;b.textAlign="center";b.textBaseline="top";g(11,55,59,8*k,-1);b.beginPath();c=0;for(d=0;8>d;++d)c+=b.measureText("LittleJS"[d]).width;for(d=2;d--;)for(let l=0,m=40-c/2;8>l;++l){a=b.measureText("LittleJS"[l]).width;const n=m+a/2;h(n,54,n+2,67,5<l?1:0);b[d?"strokeText":"fillText"]("LittleJS"[l],n,54.5,17*k);m+=a}b.lineWidth=.1+1.9*k;g(3,54,73,0);g(7,15,26,-7,0);g(25,15,8,25,-1);g(10,40,15,-25,1);g(14,21,7,9,2);g(38,20,6,-6,2);g(49,20,10,-6,0);c=[vec2(44,8),vec2(64,8),vec2(59,8+6*k),vec2(49,8+6*k)];e(c,2,8,6*k);g(44,8,20,-7,0);for(c=5;c--;)f(59-6*c*k,30,10,0,2*PI,1,0);f(59,30,4,0,7,2);g(35,20,24,0);f(59,30,10);f(47,30,10,PI/2,3*PI/2);f(35,30,10,PI/2,3*PI/2);g(7,40,13,7,-1);g(17,40,43,14,-1);for(g=3;g--;)for(c=2;c--;)f(17+15*g,47,c?7:1,0,2*PI,2);for(f=2;f--;)g=53+6*k*f,g=[vec2(g+7,54),vec2(g,40),vec2(g+6*k,40),vec2(g+7+6*k,54)],e(g,0,40,14);b.restore()}let debugWatermark=0,debugKey="";const debug=0,debugOverlay=0,debugPhysics=0,debugParticles=0,debugRaycast=0,debugGamepads=0,debugMedals=0;function ASSERT(){}function LOG(){}function debugInit(){}function debugUpdate(){}function debugRender(){}function debugRenderPost(){}function debugRect(){}function debugPoly(){}function debugCircle(){}function debugPoint(){}function debugLine(){}function debugOverlap(){}function debugText(){}function debugClear(){}function debugScreenshot(){}function debugShowErrors(){}function debugVideoCaptureIsActive(){return!1}function debugVideoCaptureStart(){}function debugVideoCaptureStop(){}function debugVideoCaptureUpdate(){}function debugProtectConstant(a){return a}const PI=Math.PI,abs=Math.abs,floor=Math.floor,ceil=Math.ceil,round=Math.round,min=Math.min,max=Math.max,sign=Math.sign,hypot=Math.hypot,log2=Math.log2,sin=Math.sin,cos=Math.cos,tan=Math.tan,atan2=Math.atan2;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 a+clamp(c)*(b-a)}function percentLerp(a,b,c,d,e){return lerp(d,e,percent(a,b,c))}function distanceWrap(a,b,c=1){a=(a-b)%c;return 2*a%c-a}function lerpWrap(a,b,c,d=1){return a+clamp(c)*distanceWrap(b,a,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 isPowerOfTwo(a){return!(a&a-1)}function nearestPowerOfTwo(a){return 2**ceil(log2(a))}function isOverlapping(a,b,c,d=vec2()){const e=2*(a.x-c.x);a=2*(a.y-c.y);c=b.x+d.x;b=b.y+d.y;return e>=-c&&e<c&&a>=-b&&a<b}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 oscillate(a=1,b=1,c=time,d=0,e=0){a=(d+c*a)%1;e=1===e?2*abs(2*a-1)-1:2===e?.5>a?-1:1:3===e?2*a-1:-cos(2*a*PI);return b/2*(e+1)}function isNumber(a){return"number"===typeof a&&!isNaN(a)}function isString(a){return null!=a&&"string"===typeof a?.toString()}function isArray(a){return Array.isArray(a)}function lineTest(a,b,c,d){ASSERT(isVector2(a),"posStart must be a vec2");ASSERT(isVector2(b),"posEnd must be a vec2");ASSERT("function"===typeof c,"testFunction must be a function");ASSERT(!d||isVector2(d),"normal must be a vec2");var e=b.x-a.x,f=b.y-a.y;const g=hypot(e,f);if(g){b=a.floor();var h=e/g,k=f/g;e=sign(h);f=sign(k);var l=h?abs(1/h):Infinity,m=k?abs(1/k):Infinity,n=0<e?b.x+1:b.x,p=0<f?b.y+1:b.y,q=0;n=h?(n-a.x)/h:Infinity;var r=k?(p-a.y)/k:Infinity;for(p=l<m;q<g;){if(c(b))return a=vec2(a.x+h*q,a.y+k*q),c=a.floor(),c.x<b.x?a.x=b.x:c.x>b.x&&(a.x=b.x+1-1e-9),c.y<b.y?a.y=b.y:c.y>b.y&&(a.y=b.y+1-1e-9),d&&(p?d.set(-e,0):d.set(0,-f)),a;(p=n<r)?(b.x+=e,q=n,n+=l):(b.y+=f,q=r,r+=m)}}}function rand(a=1,b=0){return b+Math.random()*(a-b)}function randInt(a,b=0){return floor(rand(a,b))}function randBool(a=.5){return rand()<a}function randSign(){return 2*randInt(2)-1}function randVec2(a=1){return(new Vector2).setAngle(rand(2*PI),a)}function randInCircle(a=1,b=0){return 0<a?randVec2(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=123456789){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 floor(this.float(a,b))}bool(a=.5){return this.float()<a}sign(){return.5<this.float()?1:-1}floatSign(a=1,b=0){return this.float(a,b)*this.sign()}angle(){return this.float(-PI,PI)}vec2(a=1,b=0){return vec2(this.float(a,b),this.float(a,b))}randColor(a=new Color,b=new Color(0,0,0,1),c=!1){return c?a.lerp(b,this.float()):new Color(this.float(a.r,b.r),this.float(a.g,b.g),this.float(a.b,b.b),this.float(a.a,b.a))}mutateColor(a,b=.05,c=0){ASSERT_NUMBER_VALID(b);ASSERT_NUMBER_VALID(c);return new Color(a.r+this.float(b,-b),a.g+this.float(b,-b),a.b+this.float(b,-b),a.a+this.float(c,-c)).clamp()}}function vec2(a=0,b){return new Vector2(a,b??a)}function isVector2(a){return a instanceof Vector2&&a.isValid()}function ASSERT_VECTOR2_VALID(a){ASSERT(isVector2(a),"Vector2 is invalid.",a)}function ASSERT_NUMBER_VALID(a){ASSERT(isNumber(a),"Number is invalid.",a)}function ASSERT_VECTOR2_NORMAL(a){ASSERT_VECTOR2_VALID(a);ASSERT(.01>abs(a.lengthSquared()-1),"Vector2 is not normal.",a)}class Vector2{constructor(a=0,b=0){this.x=a;this.y=b;ASSERT(this.isValid(),"Constructed Vector2 is invalid.",this)}set(a=0,b=0){this.x=a;this.y=b;ASSERT_VECTOR2_VALID(this);return this}setFrom(a){return this.set(a.x,a.y)}copy(){return new Vector2(this.x,this.y)}add(a){return new Vector2(this.x+a.x,this.y+a.y)}subtract(a){return new Vector2(this.x-a.x,this.y-a.y)}multiply(a){return new Vector2(this.x*a.x,this.y*a.y)}divide(a){return new Vector2(this.x/a.x,this.y/a.y)}scale(a){return new Vector2(this.x*a,this.y*a)}length(){return this.lengthSquared()**.5}lengthSquared(){return this.x**2+this.y**2}distance(a){return this.distanceSquared(a)**.5}distanceSquared(a){return(this.x-a.x)**2+(this.y-a.y)**2}normalize(a=1){const b=this.length();return b?this.scale(a/b):new Vector2(0,a)}clampLength(a=1){const b=this.length();return b>a?this.scale(a/b):this.copy()}dot(a){return this.x*a.x+this.y*a.y}cross(a){return this.x*a.y-this.y*a.x}reflect(a,b=1){return this.subtract(a.scale((1+b)*this.dot(a)))}angle(){return atan2(this.x,this.y)}setAngle(a=0,b=1){ASSERT_NUMBER_VALID(a);ASSERT_NUMBER_VALID(b);this.x=b*sin(a);this.y=b*cos(a);return this}rotate(a){ASSERT_NUMBER_VALID(a);const b=cos(-a);a=sin(-a);return new Vector2(this.x*b-this.y*a,this.x*a+this.y*b)}setDirection(a,b=1){ASSERT_NUMBER_VALID(a);ASSERT_NUMBER_VALID(b);a=mod(a,4);ASSERT(0===a||1===a||2===a||3===a,"Vector2.setDirection() direction must be an integer between 0 and 3.");this.x=a%2?a-1?-b:b:0;this.y=a%2?0:a?-b:b;return this}direction(){return abs(this.x)>abs(this.y)?0>this.x?3:1:0>this.y?2:0}abs(){return new Vector2(abs(this.x),abs(this.y))}floor(){return new Vector2(floor(this.x),floor(this.y))}snap(a){ASSERT_NUMBER_VALID(a);return new Vector2(floor(this.x*a)/a,floor(this.y*a)/a)}mod(a=1){return new Vector2(mod(this.x,a),mod(this.y,a))}area(){return abs(this.x*this.y)}lerp(a,b){ASSERT_VECTOR2_VALID(a);ASSERT_NUMBER_VALID(b);b=clamp(b);return new Vector2(a.x*b+this.x*(1-b),a.y*b+this.y*(1-b))}arrayCheck(a){return 0<=this.x&&0<=this.y&&this.x<a.x&&this.y<a.y}toString(a=3){ASSERT_NUMBER_VALID(a);return this.isValid()?`(${(0>this.x?"":" ")+this.x.toFixed(a)},${(0>this.y?"":" ")+this.y.toFixed(a)} )`:`(${this.x}, ${this.y})`}isValid(){return isNumber(this.x)&&isNumber(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&&a.isValid()}function ASSERT_COLOR_VALID(a){ASSERT(isColor(a),"Color is invalid.",a)}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(),"Constructed Color is invalid.",this)}set(a=1,b=1,c=1,d=1){this.r=a;this.g=b;this.b=c;this.a=d;ASSERT_COLOR_VALID(this);return this}setFrom(a){return this.set(a.r,a.g,a.b,a.a)}copy(){return new Color(this.r,this.g,this.b,this.a)}add(a){return new Color(this.r+a.r,this.g+a.g,this.b+a.b,this.a+a.a)}subtract(a){return new Color(this.r-a.r,this.g-a.g,this.b-a.b,this.a-a.a)}multiply(a){return new Color(this.r*a.r,this.g*a.g,this.b*a.b,this.a*a.a)}divide(a){return new Color(this.r/a.r,this.g/a.g,this.b/a.b,this.a/a.a)}scale(a,b=a){return new Color(this.r*a,this.g*a,this.b*a,this.a*b)}clamp(){return new Color(clamp(this.r),clamp(this.g),clamp(this.b),clamp(this.a))}lerp(a,b){ASSERT_COLOR_VALID(a);ASSERT_NUMBER_VALID(b);b=clamp(b);return new Color(a.r*b+this.r*(1-b),a.g*b+this.g*(1-b),a.b*b+this.b*(1-b),a.a*b+this.a*(1-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,h)=>1>6*(h=mod(h,1))?f+6*(g-f)*h:1>2*h?g:2>3*h?f+(g-f)*(4-6*h):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_COLOR_VALID(this);return this}HSLA(){const a=clamp(this.r),b=clamp(this.g),c=clamp(this.b),d=clamp(this.a),e=max(a,b,c),f=min(a,b,c),g=(e+f)/2;let h=0,k=0;if(e!==f){let l=e-f;k=.5<g?l/(2-e-f):l/(e+f);a===e?h=(b-c)/l+(b<c?6:0):b===e?h=(c-a)/l+2:c===e&&(h=(a-b)/l+4)}return[h/6,k,g,d]}mutate(a=.05,b=0){ASSERT_NUMBER_VALID(a);ASSERT_NUMBER_VALID(b);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){if(debug&&!this.isValid())return"#000";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(isString(a),"Color hex code must be a string");ASSERT("#"===a[0],"Color hex code must start with #");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_COLOR_VALID(this);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 isNumber(this.r)&&isNumber(this.g)&&isNumber(this.b)&&isNumber(this.a)}}const WHITE=debugProtectConstant(rgb()),CLEAR_WHITE=debugProtectConstant(rgb(1,1,1,0)),BLACK=debugProtectConstant(rgb(0,0,0)),CLEAR_BLACK=debugProtectConstant(rgb(0,0,0,0)),GRAY=debugProtectConstant(rgb(.5,.5,.5)),RED=debugProtectConstant(rgb(1,0,0)),ORANGE=debugProtectConstant(rgb(1,.5,0)),YELLOW=debugProtectConstant(rgb(1,1,0)),GREEN=debugProtectConstant(rgb(0,1,0)),CYAN=debugProtectConstant(rgb(0,1,1)),BLUE=debugProtectConstant(rgb(0,0,1)),PURPLE=debugProtectConstant(rgb(.5,0,1)),MAGENTA=debugProtectConstant(rgb(1,0,1));class Timer{constructor(a,b=!1){ASSERT(void 0===a||isNumber(a),"Constructed Timer is invalid.",a);this.useRealTime=b;b=this.getGlobalTime();this.time=void 0===a?void 0:b+a;this.setTime=a}set(a=0){ASSERT(isNumber(a),"Timer is invalid.",a);this.time=this.getGlobalTime()+a;this.setTime=a}setUseRealTime(a=!0){ASSERT(!this.isSet(),"Cannot change global time setting while timer is set.");this.useRealTime=a}unset(){this.time=void 0}isSet(){return void 0!==this.time}active(){return this.getGlobalTime()<this.time}elapsed(){return this.getGlobalTime()>=this.time}get(){return this.isSet()?this.getGlobalTime()-this.time:0}getPercent(){return this.isSet()?1-percent(this.time-this.getGlobalTime(),0,this.setTime):0}getSetTime(){return this.isSet()?this.setTime:0}getGlobalTime(){return this.useRealTime?timeReal:time}toString(){return this.isSet()?abs(this.get())+" seconds "+(0>this.get()?"before":"after"):"unset"}valueOf(){return this.get()}}function formatTime(a){const b=0>a?"-":"";a=abs(a)|0;return b+(a/60|0)+":"+(10>a%60?"0":"")+a%60}async function fetchJSON(a){const b=await fetch(a);if(!b.ok)throw Error(`Failed to fetch JSON from ${a}: ${b.status} ${b.statusText}`);return b.json()}function saveText(a,b="text",c="text/plain"){saveDataURL(URL.createObjectURL(new Blob([a],{type:c})),b)}function saveCanvas(a,b="screenshot",c="image/png"){if(a instanceof OffscreenCanvas){const d=document.createElement("canvas");d.width=a.width;d.height=a.height;d.getContext("2d").drawImage(a,0,0);saveDataURL(d.toDataURL(c),b)}else saveDataURL(a.toDataURL(c),b)}function saveDataURL(a,b="download",c){ASSERT(isString(a),"saveDataURL requires url string");ASSERT(isString(b),"saveDataURL requires filename string");const d=document.createElement("a");d.download=b;d.href=a;d.click();void 0!==c&&setTimeout(()=>URL.revokeObjectURL(a),c)}function shareURL(a,b,c){ASSERT(isString(a),"shareURL requires title string");ASSERT(isString(b),"shareURL requires url string");navigator.share?.({title:a,url:b}).then(()=>c?.())}function readSaveData(a,b){ASSERT(isString(a),"loadData requires saveName string");a=(a=localStorage[a])?JSON.parse(a):{};return{...b,...a}}function writeSaveData(a,b){ASSERT(isString(a),"saveData requires saveName string");localStorage[a]=JSON.stringify(b)}let cameraPos=vec2(),cameraAngle=0,cameraScale=32,canvasColorTiles=!0,canvasClearColor=CLEAR_BLACK,canvasMaxSize=vec2(1920,1080),canvasMinAspect=0,canvasMaxAspect=0,canvasFixedSize=vec2(),canvasPixelated=!1,tilesPixelated=!0,fontDefault="arial",showSplashScreen=!1,headlessMode=!1,glEnable=!0,glCircleSides=32,tileDefaultSize=vec2(16),tileDefaultPadding=0,tileDefaultBleed=0,enablePhysicsSolver=!0,objectDefaultMass=1,objectDefaultDamping=1,objectDefaultAngleDamping=1,objectDefaultRestitution=0,objectDefaultFriction=.8,objectMaxSpeed=1,gravity=vec2(),particleEmitRateScale=1,gamepadsEnable=!0,gamepadDirectionEmulateStick=!0,inputWASDEmulateDirection=!0,touchInputEnable=!0,touchGamepadEnable=!1,touchGamepadCenterButton=!0,touchGamepadButtonCount=4,touchGamepadAnalog=!0,touchGamepadSize=99,touchGamepadAlpha=.3,touchGamepadDisplayTime=3,vibrateEnable=!0,soundEnable=!0,soundVolume=.3,soundDefaultRange=40,soundDefaultTaper=.7,medalDisplayTime=5,medalDisplaySlideTime=.5,medalDisplaySize=vec2(640,80),medalsPreventUnlock=!1;function setCameraPos(a){cameraPos=a.copy()}function setCameraAngle(a){cameraAngle=a}function setCameraScale(a){cameraScale=a}function setCanvasColorTiles(a){canvasColorTiles=a}function setCanvasClearColor(a){canvasClearColor=a.copy()}function setCanvasMaxSize(a){canvasMaxSize=a.copy()}function setCanvasMinAspect(a){canvasMinAspect=a}function setCanvasMaxAspect(a){canvasMaxAspect=a}function setCanvasFixedSize(a){canvasFixedSize=a.copy()}function setCanvasPixelated(a){canvasPixelated=a;mainCanvas&&(mainCanvas.style.imageRendering=a?"pixelated":"");glCanvas&&(glCanvas.style.imageRendering=a?"pixelated":"")}function setTilesPixelated(a){tilesPixelated=a}function setFontDefault(a){fontDefault=a}function setShowSplashScreen(a){showSplashScreen=a}function setHeadlessMode(a){headlessMode=a}function setGLEnable(a){a&&!glCanBeEnabled?console.warn("Can not enable WebGL if it was disabled on start."):(glEnable=a,glCanvas&&(glCanvas.style.display=a?"":"none"))}function setGLCircleSides(a){glCircleSides=a}function setTileDefaultSize(a){tileDefaultSize=a.copy()}function setTileDefaultPadding(a){tileDefaultPadding=a}function setTileDefaultBleed(a){tileDefaultBleed=a}function setEnablePhysicsSolver(a){enablePhysicsSolver=a}function setObjectDefaultMass(a){objectDefaultMass=a}function setObjectDefaultDamping(a){objectDefaultDamping=a}function setObjectDefaultAngleDamping(a){objectDefaultAngleDamping=a}function setObjectDefaultRestitution(a){objectDefaultRestitution=a}function setObjectDefaultFriction(a){objectDefaultFriction=a}function setObjectMaxSpeed(a){objectMaxSpeed=a}function setGravity(a){gravity=a.copy()}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 setTouchGamepadCenterButton(a){touchGamepadCenterButton=a}function setTouchGamepadButtonCount(a){touchGamepadButtonCount=a}function setTouchGamepadAnalog(a){touchGamepadAnalog=a}function setTouchGamepadSize(a){touchGamepadSize=a}function setTouchGamepadAlpha(a){touchGamepadAlpha=a}function setTouchGamepadDisplayTime(a){touchGamepadDisplayTime=a}function setVibrateEnable(a){vibrateEnable=a}function setSoundEnable(a){soundEnable=a}function setSoundVolume(a){soundVolume=a;soundEnable&&!headlessMode&&audioMasterGain&&(audioMasterGain.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.copy()}function setMedalsPreventUnlock(a){medalsPreventUnlock=a}function setDebugWatermark(a){debugWatermark=a}function setDebugKey(a){debugKey=a}class EngineObject{constructor(a=vec2(),b=vec2(1),c,d=0,e=WHITE,f=0){ASSERT(isVector2(a),"object pos must be a vec2");ASSERT(isVector2(b),"object size must be a vec2");ASSERT(!c||c instanceof TileInfo,"object tileInfo should be a TileInfo or undefined");ASSERT("number"===typeof d&&isFinite(d),"object angle should be a number");ASSERT(isColor(e),"object color should be a valid rgba color");ASSERT("number"===typeof f,"object renderOrder should be a number");this.pos=a.copy();this.size=b.copy();this.drawSize=void 0;this.tileInfo=c;this.angle=d;this.color=e.copy();this.additiveColor=void 0;this.destroyed=this.mirror=!1;this.mass=objectDefaultMass;this.damping=objectDefaultDamping;this.angleDamping=objectDefaultAngleDamping;this.restitution=objectDefaultRestitution;this.friction=objectDefaultFriction;this.gravityScale=1;this.renderOrder=f;this.velocity=vec2();this.angleVelocity=0;this.spawnTime=time;this.children=[];this.clampSpeed=!0;this.parent=this.groundObject=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()}updatePhysics(){ASSERT(!this.parent);this.clampSpeed&&(this.velocity.x=clamp(this.velocity.x,-objectMaxSpeed,objectMaxSpeed),this.velocity.y=clamp(this.velocity.y,-objectMaxSpeed,objectMaxSpeed));const a=this.pos.copy();this.velocity.x*=this.damping;this.velocity.y*=this.damping;this.mass&&(this.velocity.x+=gravity.x*this.gravityScale,this.velocity.y+=gravity.y*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&&0>gravity.y||0<this.velocity.y&&0<gravity.y;if(this.groundObject){var c=max(this.friction,this.groundObject.friction),d=this.groundObject.velocity.x;this.velocity.x=d+(this.velocity.x-d)*c;this.groundObject=void 0}if(this.collideSolidObjects)for(var e of engineObjectsCollide)if(!e.destroyed&&!e.parent&&e!==this&&(this.isSolid||e.isSolid)&&this.isOverlappingObject(e)&&(c=this.collideWithObject(e),d=e.collideWithObject(this),c&&d))if(isOverlapping(a,this.size,e.pos,e.size))c=a.subtract(e.pos),d=c.length(),c=.001>d?vec2(0,1):c.scale(.001/d),this.velocity=this.velocity.add(c),e.mass&&(e.velocity=e.velocity.subtract(c)),debugPhysics&&debugOverlap(this.pos,this.size,e.pos,e.size,"#f00");else{d=this.size.add(e.size);var f=2*(a.y-e.pos.y)>d.y+gravity.y,g=2*abs(a.y-e.pos.y)<d.y,h=2*abs(a.x-e.pos.x)<d.x;c=max(this.restitution,e.restitution);if(f||h||!g)if(this.pos.y=e.pos.y+(d.y/2+.001)*sign(a.y-e.pos.y),e.groundObject&&b||!e.mass)b&&(this.groundObject=e),this.velocity.y*=-c;else if(e.mass){h=(this.mass*this.velocity.y+e.mass*e.velocity.y)/(this.mass+e.mass);const k=e.velocity.y*(e.mass-this.mass)/(this.mass+e.mass)+2*this.velocity.y*this.mass/(this.mass+e.mass);this.velocity.y=lerp(h,this.velocity.y*(this.mass-e.mass)/(this.mass+e.mass)+2*e.velocity.y*e.mass/(this.mass+e.mass),c);e.velocity.y=lerp(h,k,c)}!f&&g&&(this.pos.x=e.pos.x+(d.x/2+.001)*sign(a.x-e.pos.x),e.mass?(d=(this.mass*this.velocity.x+e.mass*e.velocity.x)/(this.mass+e.mass),f=e.velocity.x*(e.mass-this.mass)/(this.mass+e.mass)+2*this.velocity.x*this.mass/(this.mass+e.mass),this.velocity.x=lerp(d,this.velocity.x*(this.mass-e.mass)/(this.mass+e.mass)+2*e.velocity.x*e.mass/(this.mass+e.mass),c),e.velocity.x=lerp(d,f,c)):this.velocity.x*=-c);debugPhysics&&debugOverlap(this.pos,this.size,e.pos,e.size,"#f0f")}if(this.collideTiles&&(e=tileCollisionTest(this.pos,this.size,this))&&!tileCollisionTest(a,this.size,this)){d=tileCollisionTest(vec2(this.pos.x,a.y),this.size,this);f=tileCollisionTest(vec2(a.x,this.pos.y),this.size,this);c=max(this.restitution,e.restitution);if(d){g=floor(a.y-this.size.y/2+1)+this.size.y/2+.001;if(.1>g-this.pos.y&&!tileCollisionTest(vec2(this.pos.x,g),this.size,this)){this.pos.y=g;debugPhysics&&debugRect(this.pos,this.size,"#ff0");return}this.pos.x=a.x;this.velocity.x*=-c}if(f||!d)b?(b=this.size.y/2+1e-4,this.pos.y=0>gravity.y?floor(a.y-this.size.y/2)+b:ceil(a.y+this.size.y/2)-b,this.groundObject=e):(this.pos.y=a.y,this.groundObject=void 0),this.velocity.y*=-c;debugPhysics&&debugRect(this.pos,this.size,"#f00")}}}update(){}render(){drawTile(this.pos,this.drawSize||this.size,this.tileInfo,this.color,this.angle,this.mirror,this.additiveColor)}destroy(a=!1){if(!this.destroyed){this.destroyed=!0;this.parent?.removeChild(this);for(const b of this.children)b.parent=void 0,b.destroy(a)}}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}getUp(a=1){return vec2().setAngle(this.angle,a)}getRight(a=1){return vec2().setAngle(this.angle+PI/2,a)}getAliveTime(){return time-this.spawnTime}getSpeed(){return this.velocity.length()}applyAcceleration(a){this.mass&&(this.velocity=this.velocity.add(a))}applyAngularAcceleration(a){this.mass&&(this.angleVelocity+=a)}applyForce(a){this.mass&&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));ASSERT(a instanceof EngineObject,"child must be an EngineObject");ASSERT(a!==this,"cannot add self as child");this.children.push(a);a.parent=this;a.localPos=b.copy();a.localAngle=c;return a}removeChild(a){ASSERT(a.parent===this&&this.children.includes(a));ASSERT(a instanceof EngineObject,"child must be an EngineObject");const b=this.children.indexOf(a);ASSERT(0<=b,"child not found in children array");0<=b&&this.children.splice(b,1);a.parent=void 0}isOverlappingObject(a){return this.isOverlapping(a.pos,a.size)}isOverlapping(a,b=vec2()){return isOverlapping(this.pos,this.size,a,b)}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){var 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){var a=this.collideTiles||this.collideSolidObjects||this.isSolid;if(a||this.parent){var b=vec2(max(this.size.x,.2),max(this.size.y,.2)),c=rgb(this.collideTiles?1:0,this.collideSolidObjects?1:0,this.isSolid?1:0,.5);debugRect(this.pos,b,c,0,this.angle,a);this.parent&&debugRect(this.pos,b.scale(.8),rgb(1,1,1,.5),0,this.angle);this.parent&&debugLine(this.pos,this.parent.pos,rgb(1,1,1,.5),.5)}}}}let mainCanvas,mainContext,drawContext,workCanvas,workContext,workReadCanvas,workReadContext,mainCanvasSize=vec2(),textureInfos=[],drawCount;function tile(a=new Vector2,b=tileDefaultSize,c=0,d=tileDefaultPadding,e=tileDefaultBleed){ASSERT(isVector2(a)||"number"===typeof a,"index must be a vec2 or number");ASSERT(isVector2(b)||"number"===typeof b,"size must be a vec2 or number");ASSERT(isNumber(c)||c instanceof TextureInfo,"texture must be a number or TextureInfo");ASSERT(isNumber(d),"padding must be a number");if(headlessMode)return new TileInfo;"number"===typeof b&&(ASSERT(0<b),b=new Vector2(b,b));c="number"===typeof c?textureInfos[c]:c;ASSERT(c instanceof TextureInfo,"tile texture is not loaded");ASSERT(0<c.size.x,"tile texture is not loaded");const f=b.x+2*d,g=b.y+2*d;let h;if("number"===typeof a){const k=c.size.x/f|0;h=a%k;a=a/k|0}else h=a.x,a=a.y;a=new Vector2(h*f+d,a*g+d);return new TileInfo(a,b,c,d,e)}class TileInfo{constructor(a=vec2(),b=tileDefaultSize,c=textureInfos[0],d=tileDefaultPadding,e=tileDefaultBleed){this.pos=a.copy();this.size=b.copy();this.padding=d;this.textureInfo=c;this.bleed=e}offset(a){return new TileInfo(this.pos.add(a),this.size,this.textureInfo,this.padding,this.bleed)}frame(a){ASSERT("number"===typeof a);a*=this.size.x+2*this.padding;ASSERT(a<this.textureInfo.size.x,"frame extends beyond texture width!");return this.offset(new Vector2(a))}setFullImage(a=this.textureInfo){this.textureInfo=a;this.pos=new Vector2;this.size=a.size.copy();this.bleed=this.padding=0;return this}tile(a){return tile(a,this.size,this.textureInfo,this.padding,this.bleed)}}class TextureInfo{constructor(a,b=!0){this.size=(this.image=a)?vec2(a.width,a.height):vec2();this.sizeInverse=a?vec2(1/a.width,1/a.height):vec2();this.glTexture=void 0;b&&this.createWebGLTexture()}createWebGLTexture(){glRegisterTextureInfo(this)}destroyWebGLTexture(){glUnregisterTextureInfo(this)}hasWebGL(){return!!this.glTexture}}function drawTile(a,b=vec2(1),c,d=WHITE,e=0,f,g,h=glEnable,k,l){ASSERT(isVector2(a),"pos must be a vec2");ASSERT(isVector2(b),"size must be a vec2");ASSERT(isColor(d),"color is invalid");ASSERT(isNumber(e),"angle must be a number");ASSERT(!g||isColor(g),"additiveColor must be a color");ASSERT(!l||!h,"context only supported in canvas 2D mode");const m=c?.textureInfo,n=c?.bleed??0;if(h&&glEnable)if(ASSERT(!!glContext,"WebGL is not enabled!"),k&&([a,b,e]=screenToWorldTransform(a,b,e)),m){var p=m.sizeInverse;h=c.pos.x*p.x;k=c.pos.y*p.y;l=c.size.x*p.x;const q=c.size.y*p.y;glSetTexture(m.glTexture);if(n){const r=p.x*n;p=p.y*n;glDraw(a.x,a.y,f?-b.x:b.x,b.y,e,h+r,k+p,h-r+l,k-p+q,d.rgbaInt(),g&&g.rgbaInt())}else glDraw(a.x,a.y,f?-b.x:b.x,b.y,e,h,k,h+l,k+q,d.rgbaInt(),g&&g.rgbaInt())}else glDraw(a.x,a.y,b.x,b.y,e,0,0,0,0,0,d.rgbaInt());else++drawCount,b=new Vector2(b.x,-b.y),drawCanvas2D(a,b,e,f,q=>{if(m)drawImageColor(q,m.image,c.pos.x,c.pos.y,c.size.x,c.size.y,-.5,-.5,1,1,d,g,n);else{const r=g?d.add(g):d;q.fillStyle=r.toString();q.fillRect(-.5,-.5,1,1)}},k,l)}function drawRect(a,b,c,d,e,f,g){drawTile(a,b,void 0,c,d,!1,void 0,e,f,g)}function drawRectGradient(a,b,c=WHITE,d=BLACK,e=0,f=glEnable,g=!1,h){ASSERT(isVector2(a),"pos must be a vec2");ASSERT(isVector2(b),"size must be a vec2");ASSERT(isColor(c)&&isColor(d),"color is invalid");ASSERT(isNumber(e),"angle must be a number");ASSERT(!h||!f,"context only supported in canvas 2D mode");if(f&&glEnable){ASSERT(!!glContext,"WebGL is not enabled!");g&&(a=screenToWorld(a),b=b.scale(1/cameraScale),e+=cameraAngle);f=[];g=[];h=b.x/2;b=b.y/2;const k=c.rgbaInt(),l=d.rgbaInt(),m=cos(-e);e=sin(-e);for(let n=4;n--;){const p=n&1?h:-h,q=n&2?b:-b,r=n&2?k:l;f.push(vec2(a.x+(p*m-q*e),a.y+(p*e+q*m)));g.push(r)}glDrawColoredPoints(f,g)}else++drawCount,b=new Vector2(b.x,-b.y),drawCanvas2D(a,b,e,!1,k=>{const l=k.createLinearGradient(0,-.5,0,.5);l.addColorStop(0,c.toString());l.addColorStop(1,d.toString());k.fillStyle=l;k.fillRect(-.5,-.5,1,1)},g,h)}function drawLineList(a,b=.1,c,d=!1,e=vec2(),f=0,g=glEnable,h,k){ASSERT(isArray(a),"points must be an array");ASSERT(isNumber(b),"width must be a number");ASSERT(isColor(c),"color is invalid");ASSERT(isVector2(e),"pos must be a vec2");ASSERT(isNumber(f),"angle must be a number");ASSERT(!k||!g,"context only supported in canvas 2D mode");g&&glEnable?(ASSERT(!!glContext,"WebGL is not enabled!"),g=vec2(1),h&&([e,g,f]=screenToWorldTransform(e,g,f)),glDrawOutlineTransform(a,c.rgbaInt(),b,e.x,e.y,g.x,g.y,f,d)):(++drawCount,drawCanvas2D(e,vec2(1),f,!1,l=>{l.strokeStyle=c.toString();l.lineWidth=b;l.beginPath();for(let m=0;m<a.length;++m){const n=a[m];l.lineTo(n.x,n.y)}d&&l.closePath();l.stroke()},h,k))}function drawLine(a,b,c=.1,d,e=vec2(),f=0,g,h,k){b=vec2((b.x-a.x)/2,(b.y-a.y)/2);c=vec2(c,2*b.length());e=e.add(a.add(b));h&&(b.y*=-1);f+=b.angle();drawRect(e,c,d,f,g,h,k)}function drawRegularPoly(a,b=vec2(1),c=3,d=WHITE,e=0,f=BLACK,g=0,h=glEnable,k=!1,l){ASSERT(isVector2(b),"size must be a vec2");ASSERT(isNumber(c),"sides must be a number");const m=[],n=b.x/2;b=b.y/2;for(let p=c;p--;){const q=p/c*PI*2;m.push(vec2(sin(q)*n,cos(q)*b))}drawPoly(m,d,e,f,a,g,h,k,l)}function drawPoly(a,b=WHITE,c=0,d=BLACK,e=vec2(),f=0,g=glEnable,h=!1,k){ASSERT(isVector2(e),"pos must be a vec2");ASSERT(isArray(a),"points must be an array");ASSERT(isColor(b)&&isColor(d),"color is invalid");ASSERT(isNumber(c),"lineWidth must be a number");ASSERT(isNumber(f),"angle must be a number");ASSERT(!k||!g,"context only supported in canvas 2D mode");g&&glEnable?(ASSERT(!!glContext,"WebGL is not enabled!"),g=vec2(1),h&&([e,g,f]=screenToWorldTransform(e,g,f)),glDrawPointsTransform(a,b.rgbaInt(),e.x,e.y,g.x,g.y,f),0<c&&glDrawOutlineTransform(a,d.rgbaInt(),c,e.x,e.y,g.x,g.y,f)):drawCanvas2D(e,vec2(1),f,!1,l=>{l.fillStyle=b.toString();l.beginPath();for(const m of a)l.lineTo(m.x,m.y);l.closePath();l.fill();c&&(l.strokeStyle=d.toString(),l.lineWidth=c,l.stroke())},h,k)}function drawEllipse(a,b=vec2(1),c=WHITE,d=0,e=0,f=BLACK,g=glEnable,h=!1,k){ASSERT(isVector2(a),"pos must be a vec2");ASSERT(isVector2(b),"size must be a vec2");ASSERT(isColor(c)&&isColor(f),"color is invalid");ASSERT(isNumber(d),"angle must be a number");ASSERT(isNumber(e),"lineWidth must be a number");ASSERT(0<=e,"lineWidth must be a positive value or 0");ASSERT(!k||!g,"context only supported in canvas 2D mode");e=clamp(e,0,Math.min(b.x,b.y));g&&glEnable?drawRegularPoly(a,b,glCircleSides,c,e,f,d,g,h,k):drawCanvas2D(a,vec2(1),d,!1,l=>{l.fillStyle=c.toString();l.beginPath();l.ellipse(0,0,b.x/2,b.y/2,0,0,9);l.fill();e&&(l.strokeStyle=f.toString(),l.lineWidth=e,l.stroke())},h,k)}function drawCircle(a,b=1,c=WHITE,d=0,e=BLACK,f=glEnable,g=!1,h){ASSERT(isNumber(b),"size must be a number");drawEllipse(a,vec2(b),c,0,d,e,f,g,h)}function drawCanvas2D(a,b,c=0,d=!1,e,f=!1,g=drawContext){ASSERT(isVector2(a),"pos must be a vec2");ASSERT(isVector2(b),"size must be a vec2");ASSERT(isNumber(c),"angle must be a number");ASSERT("function"===typeof e,"drawFunction must be a function");f||(a=worldToScreen(a),b=b.scale(cameraScale),c-=cameraAngle);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,h,k,l,m=0,n=drawContext){b=worldToScreen(b);c*=cameraScale;e*=cameraScale;m-=cameraAngle;drawTextScreen(a,b,c,d,e,f,g,h,k,l,-1*m,n)}function drawTextScreen(a,b,c,d=WHITE,e=0,f=BLACK,g="center",h=fontDefault,k="",l,m=0,n=drawContext){ASSERT(isString(a),"text must be a string");ASSERT(isVector2(b),"pos must be a vec2");ASSERT(isNumber(c),"size must be a number");ASSERT(isColor(d),"color must be a color");ASSERT(isNumber(e),"lineWidth must be a number");ASSERT(isColor(f),"lineColor must be a color");ASSERT(["left","center","right"].includes(g),"align must be left, center, or right");ASSERT(isString(h),"font must be a string");ASSERT(isString(k),"fontStyle must be a string");ASSERT(isNumber(m),"angle must be a number");n.fillStyle=d.toString();n.strokeStyle=f.toString();n.lineWidth=e;n.textAlign=g;n.font=k+" "+c+"px "+h;n.textBaseline="middle";a=(a+"").split("\n");d=b.y-(a.length-1)*c/2;n.save();n.translate(b.x,d);n.rotate(-m);let p=0;a.forEach(q=>{e&&n.strokeText(q,0,p,l);n.fillText(q,0,p,l);p+=c});n.restore()}async function loadTexture(a,b){ASSERT(isNumber(a),"textureIndex must be a number");ASSERT(!textureInfos[a],"textureIndex is already loaded!");ASSERT(!b||isString(b),"image src must be a string");const c=new Image;b&&await new Promise(d=>{c.onerror=c.onload=d;c.crossOrigin="anonymous";c.src=b});textureInfos[a]=new TextureInfo(c)}function screenToWorld(a){ASSERT(isVector2(a),"screenPos must be a vec2");let b=(a.x-mainCanvasSize.x/2+.5)/cameraScale;a=(a.y-mainCanvasSize.y/2+.5)/-cameraScale;if(cameraAngle){const c=cos(-cameraAngle),d=sin(-cameraAngle),e=b*d+a*c;b=b*c-a*d;a=e}return new Vector2(b+cameraPos.x,a+cameraPos.y)}function worldToScreen(a){ASSERT(isVector2(a),"worldPos must be a vec2");let b=a.x-cameraPos.x;a=a.y-cameraPos.y;if(cameraAngle){const c=cos(cameraAngle),d=sin(cameraAngle),e=b*d+a*c;b=b*c-a*d;a=e}return new Vector2(b*cameraScale+mainCanvasSize.x/2-.5,a*-cameraScale+mainCanvasSize.y/2-.5)}function screenToWorldDelta(a){ASSERT(isVector2(a),"screenDelta must be a vec2");let b=a.x/cameraScale;a=a.y/-cameraScale;if(cameraAngle){const c=cos(-cameraAngle),d=sin(-cameraAngle),e=b*d+a*c;b=b*c-a*d;a=e}return new Vector2(b,a)}function worldToScreenDelta(a){ASSERT(isVector2(a),"worldDelta must be a vec2");let b=a.x;a=a.y;if(cameraAngle){const c=cos(cameraAngle),d=sin(cameraAngle),e=b*d+a*c;b=b*c-a*d;a=e}return new Vector2(b*cameraScale,a*-cameraScale)}function screenToWorldTransform(a,b,c=0){ASSERT(isVector2(a),"screenPos must be a vec2");ASSERT(isVector2(b),"screenSize must be a vec2");ASSERT(isNumber(c),"screenAngle must be a number");return[screenToWorld(a),b.scale(1/cameraScale),c+cameraAngle]}function getCameraSize(){return mainCanvasSize.scale(1/cameraScale)}function isOnScreen(a,b=0){ASSERT(isVector2(a),"pos must be a vec2");ASSERT(isVector2(b)||isNumber(b),"size must be a vec2 or number");let c=a.x-cameraPos.x;a=a.y-cameraPos.y;if(cameraAngle){var d=cos(cameraAngle),e=sin(cameraAngle);const f=c*e+a*d;c=c*d-a*e;a=f}c*=2*cameraScale;a*=2*-cameraScale;b instanceof Vector2&&(b=b.length());b*=cameraScale;d=mainCanvasSize.x;e=mainCanvasSize.y;return c+b>-d&&c-b<d&&a+b>-e&&a-b<e}function setBlendMode(a=!1,b=drawContext){glAdditive=a;b.globalCompositeOperation=a?"lighter":"source-over"}function combineCanvases(){const a=mainCanvasSize.x,b=mainCanvasSize.y;workCanvas.width=a;workCanvas.height=b;workContext.fillRect(0,0,a,b);glCopyToContext(workContext);workContext.drawImage(mainCanvas,0,0);mainContext.drawImage(workCanvas,0,0)}function drawImageColor(a,b,c,d,e,f,g,h,k,l,m,n,p=0){function q(u){return 1<=u.r&&1<=u.g&&1<=u.b}e=max(1,e|0);f=max(1,f|0);const r=e-2*p,t=f-2*p;if(!canvasColorTiles||(n?q(m.add(n))&&0>=n.a:q(m)))a.globalAlpha=m.a,a.drawImage(b,c+p,d+p,r,t,g,h,k,l),a.globalAlpha=1;else if(workReadCanvas.width=e,workReadCanvas.height=f,workReadContext.drawImage(b,c|0,d|0,e,f,0,0,e,f),b=workReadContext.getImageData(0,0,e,f),c=b.data,!n||0>=n.r&&0>=n.g&&0>=n.b&&0>=n.a){for(n=0;n<c.length;n+=4)c[n]*=m.r,c[n+1]*=m.g,c[n+2]*=m.b;workReadContext.putImageData(b,0,0);a.globalAlpha=m.a;a.drawImage(workReadCanvas,p,p,r,t,g,h,k,l);a.globalAlpha=1}else{m=[m.r,m.g,m.b,m.a];n=[255*n.r,255*n.g,255*n.b,255*n.a];for(d=0;d<c.length;++d)c[d]=c[d]*m[d&3]+n[d&3]|0;workReadContext.putImageData(b,0,0);a.drawImage(workReadCanvas,p,p,r,t,g,h,k,l)}}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}let engineFontImage;class FontImage{constructor(a){ASSERT(!!a,"tileInfo is required for FontImage");this.tileInfo=a.frame(0)}drawText(a,b,c=1,d,e,f,g){ASSERT(isVector2(c)||"number"===typeof c,"size must be a vec2 or number");"number"===typeof c?(ASSERT(0<c),c*=cameraScale,c=new Vector2(c,c)):c=c.scale(cameraScale);this.drawTextScreen(a,worldToScreen(b),c,d,e,f,g)}drawTextScreen(a,b,c,d=!0,e=WHITE,f=glEnable,g){ASSERT(isString(a),"text must be a string");ASSERT(isVector2(b),"pos must be a vec2");ASSERT(isVector2(c)||"number"===typeof c,"size must be a vec2 or number");ASSERT(isColor(e),"color must be a color");c="number"===typeof c?new Vector2(c,c):c;const h=new Vector2,k=this.tileInfo,l=k.padding,m=k.size.x+2*l,n=k.size.y+2*l,p=k.textureInfo.size.x/m|0;(a+"").split("\n").forEach((q,r)=>{const t=d?(q.length-1)*c.x/2:0;for(let w=q.length;w--;){var u=q.charCodeAt(w);u=32>u||127<u?95:u-32;const x=u/p|0;k.pos.x=u%p*m+l;k.pos.y=x*n+l;h.x=b.x+w*c.x-t|0;h.y=b.y+r*c.y|0;drawTile(h,c,k,e,0,!1,void 0,f,!0,g)}})}}async function fontImageInit(){const a=new Image;await new Promise(e=>{a.onerror=a.onload=e;a.crossOrigin="anonymous";a.src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAUAAAAAeAQMAAABnrVXaAAAABlBMVEUAAAD///+l2Z/dAAAAAXRSTlMAQObYZgAAAjpJREFUOMu9kzFu2zAUhn+CAROgqrk+B2l0BWYxMjlXeYaAtFtbdA1sGgHqRQfI0CNkSG5AwYB0BQ8d5Bsomwah6CPVeGg6tEPzAxLwyI+P78cP4u9lNO9OoMKnLMOobG5020/yaj/MrRcCGh1gBbyiLTPJEYaIiom5KM9Jq7KgynMGtb6L4GL4MF2H4LQKCXTvDVw2I4MsgZT7QLExdiutH+D08VOP3INXRrWX1/mmpbkNgAPYRVANb4xpcegYvhiNbIXauQICEjBuYLfMakaakWQeXxiZ0VDtuJCKs3ztMV59QtsHJNcRxDzfdL21ty3PrfIcXTN+E+GFAv6T5nbT9jd50/WFxb5ksdAv49qS6ouymG66ji08UMT6moykYLAo+V0j23GN4m829ZySAD5K7QsBfQTvOG8eE+gTeGYRAmnNAubN3hf5Zv9tJWDHp/VTuaSm7SN4fyINQqaNO3RMVxvpSPXnOChnRNvFcGY0gnwiPswYwTKVPE0zVtX3mTEIOoFzaqLrGuJaV+Uqumb71fVk/VoOH3cdLNQP/FHi8hV0CQNoqBZsUPlLPMsdCJro9QAaQQ0woDy9BJm0eTxCFnO9srcYlhNVlfR2EyTrph1uUtbUtAJifwRgrKuYdXVHeb0YI3QpawohQHkloI3J5FuVwI5ORxC9k2Tuz9Ir1IjgeIPGMHYkAZe2RuYkmWFmt3gGbTPOmBUWVTmRmHtGrfpzG/yuQNOKa6gBB/WA9khitPgl6/GP+gl2Af6tCbvaygAAAABJRU5ErkJggg=="});var b=vec2();const c=vec2(8),d=new TextureInfo(a);b=new TileInfo(b,c,d,1,0);engineFontImage=new FontImage(b)}let mousePos=vec2(),mousePosScreen=vec2(),mouseDelta=vec2(),mouseDeltaScreen=vec2(),mouseWheel=0,mouseInWindow=!0,isUsingGamepad=!1,inputPreventDefault=!0,gamepadPrimary=0;const isTouchDevice=!headlessMode&&void 0!==window.ontouchstart;function setInputPreventDefault(a){inputPreventDefault=a}function inputClearKey(a,b=0,c=!0,d=!0,e=!0){inputData[b]&&(inputData[b][a]&=~((c?1:0)|(d?2:0)|(e?4:0)))}function inputClear(){inputData.length=0;inputData[0]=[];touchGamepadButtons.length=0;touchGamepadSticks.length=0;gamepadStickData.length=0;gamepadDpadData.length=0}function keyIsDown(a,b=0){ASSERT(isString(a),"key must be a number or string");ASSERT(0<b||"number"!==typeof a||3>a,"use code string for keyboard");return!!(inputData[b]?.[a]&1)}function keyWasPressed(a,b=0){ASSERT(isString(a),"key must be a number or string");ASSERT(0<b||"number"!==typeof a||3>a,"use code string for keyboard");return!!(inputData[b]?.[a]&2)}function keyWasReleased(a,b=0){ASSERT(isString(a),"key must be a number or string");ASSERT(0<b||"number"!==typeof a||3>a,"use code string for keyboard");return!!(inputData[b]?.[a]&4)}function keyDirection(a="ArrowUp",b="ArrowDown",c="ArrowLeft",d="ArrowRight"){ASSERT(isString(a),"up key must be a string");ASSERT(isString(b),"down key must be a string");ASSERT(isString(c),"left key must be a string");ASSERT(isString(d),"right key must be a string");return vec2((keyIsDown(d)?1:0)-(keyIsDown(c)?1:0),(keyIsDown(a)?1:0)-(keyIsDown(b)?1:0))}function mouseIsDown(a){ASSERT(isNumber(a),"mouse button must be a number");return keyIsDown(a)}function mouseWasPressed(a){ASSERT(isNumber(a),"mouse button must be a number");return keyWasPressed(a)}function mouseWasReleased(a){ASSERT(isNumber(a),"mouse button must be a number");return keyWasReleased(a)}function gamepadIsDown(a,b=gamepadPrimary){ASSERT(isNumber(a),"button must be a number");ASSERT(isNumber(b),"gamepad must be a number");return keyIsDown(a,b+1)}function gamepadWasPressed(a,b=gamepadPrimary){ASSERT(isNumber(a),"button must be a number");ASSERT(isNumber(b),"gamepad must be a number");return keyWasPressed(a,b+1)}function gamepadWasReleased(a,b=gamepadPrimary){ASSERT(isNumber(a),"button must be a number");ASSERT(isNumber(b),"gamepad must be a number");return keyWasReleased(a,b+1)}function gamepadStick(a,b=gamepadPrimary){ASSERT(isNumber(a),"stick must be a number");ASSERT(isNumber(b),"gamepad must be a number");return gamepadStickData[b]?.[a]??vec2()}function gamepadDpad(a=gamepadPrimary){ASSERT(isNumber(a),"gamepad must be a number");return gamepadDpadData[a]??vec2()}function gamepadConnected(a=gamepadPrimary){ASSERT(isNumber(a),"gamepad must be a number");return!!inputData[a+1]}function gamepadStickCount(a=gamepadPrimary){ASSERT(isNumber(a),"gamepad must be a number");return gamepadStickData[a]?.length??0}function vibrate(a=100){ASSERT(isNumber(a)||isArray(a),"pattern must be a number or array");vibrateEnable&&!headlessMode&&navigator?.vibrate?.(a)}function vibrateStop(){vibrate(0)}function pointerLockRequest(){!isTouchDevice&&mainCanvas.requestPointerLock?.()}function pointerLockExit(){document.exitPointerLock?.()}function pointerLockIsActive(){return document.pointerLockElement===mainCanvas}const inputData=[[]],gamepadStickData=[],gamepadDpadData=[],gamepadHadInput=[],touchGamepadTimer=new Timer,touchGamepadButtons=[],touchGamepadSticks=[];function inputInit(){function a(d){return inputWASDEmulateDirection?"KeyW"===d?"ArrowUp":"KeyS"===d?"ArrowDown":"KeyA"===d?"ArrowLeft":"KeyD"===d?"ArrowRight":d:d}function b(){function d(f){if(touchInputEnable){if(touchGamepadEnable)a:{touchGamepadSticks.length=0;touchGamepadSticks[0]=vec2();touchGamepadSticks[1]=vec2();touchGamepadButtons.length=0;isUsingGamepad=!0;if(f.touches.length&&(touchGamepadTimer.set(),touchGamepadCenterButton&&!e&&paused)){touchGamepadButtons[9]=1;break a}if(!paused){var g=vec2(touchGamepadSize,mainCanvasSize.y-touchGamepadSize),h=touchGamepadButtonCenter(),k=mainCanvasSize.scale(.5);for(const n of f.touches){var l=c(vec2(n.clientX,n.clientY));if(g.distance(l)<touchGamepadSize)l=l.subtract(g),touchGamepadSticks[0]=l.scale(2/touchGamepadSize).clampLength();else if(h.distance(l)<touchGamepadSize){if(1===touchGamepadButtonCount){var m=l.subtract(h);touchGamepadSticks[1]=m.scale(2/touchGamepadSize).clampLength()}m=h.subtract(l).direction();m=mod(m+2,4);1===touchGamepadButtonCount?m=0:2===touchGamepadButtonCount&&(l=h.subtract(l),m=-l.x<l.y?1:0);m=3===m?2:2===m?3:m;m<touchGamepadButtonCount&&(touchGamepadButtons[m]=1)}else touchGamepadCenterButton&&k.distance(l)<touchGamepadSize&&(touchGamepadButtons[9]=1)}}}soundEnable&&!headlessMode&&audioContext&&!audioIsRunning()&&audioContext.resume();(g=f.touches.length)?(h=vec2(f.touches[0].clientX,f.touches[0].clientY),k=mousePosScreen,mousePosScreen=c(h),e?(mouseDeltaScreen=mouseDeltaScreen.add(mousePosScreen.subtract(k)),isUsingGamepad=touchGamepadEnable):inputData[0][0]=3):e&&(inputData[0][0]=inputData[0][0]&2|4);e=g;inputPreventDefault&&f.cancelable&&document.hasFocus()&&f.preventDefault();return!0}}document.addEventListener("touchstart",f=>d(f),{passive:!1});document.addEventListener("touchmove",f=>d(f),{passive:!1});document.addEventListener("touchend",f=>d(f),{passive:!1});let e}function c(d){const e=mainCanvas.getBoundingClientRect(),f=percent(d.x,e.left,e.right);d=percent(d.y,e.top,e.bottom);return vec2(f*mainCanvas.width,d*mainCanvas.height)}headlessMode||(document.addEventListener("keydown",function(d){function e(g){return g?.isContentEditable||["INPUT","TEXTAREA","SELECT"].includes(g?.tagName)}d.repeat||(isUsingGamepad=!1,inputData[0][d.code]=3,inputWASDEmulateDirection&&(inputData[0][a(d.code)]=3));if(inputPreventDefault&&d.cancelable&&document.hasFocus()&&!(d.ctrlKey||d.metaKey||d.altKey||e(d.target)||e(document.activeElement))){var f="string"===typeof d.key&&1===d.key.length;("ArrowUp ArrowDown ArrowLeft ArrowRight Space Tab Backspace".split(" ").includes(d.code)||f)&&d.preventDefault()}}),document.addEventListener("keyup",function(d){inputData[0][d.code]=inputData[0][d.code]&2|4;inputWASDEmulateDirection&&(inputData[0][a(d.code)]=4)}),document.addEventListener("mousedown",function(d){if(!isTouchDevice||!touchInputEnable){soundEnable&&!headlessMode&&audioContext&&!audioIsRunning()&&audioContext.resume();isUsingGamepad=!1;inputData[0][d.button]=3;var e=mousePosScreen;mousePosScreen=c(vec2(d.x,d.y));mouseDeltaScreen=mouseDeltaScreen.add(mousePosScreen.subtract(e));inputPreventDefault&&d.cancelable&&document.hasFocus()&&d.preventDefault()}}),document.addEventListener("mouseup",function(d){isTouchDevice&&touchInputEnable||(inputData[0][d.button]=inputData[0][d.button]&2|4)}),document.addEventListener("mousemove",function(d){mouseInWindow=!0;const e=mousePosScreen;mousePosScreen=c(vec2(d.x,d.y));d=pointerLockIsActive()?vec2(d.movementX,d.movementY):mousePosScreen.subtract(e);mouseDeltaScreen=mouseDeltaScreen.add(d)}),document.addEventListener("mouseleave",function(){mouseInWindow=!1}),document.addEventListener("wheel",function(d){mouseWheel=d.ctrlKey?0:sign(d.deltaY);inputPreventDefault&&d.cancelable&&document.hasFocus()&&d.preventDefault()},{passive:!1}),document.addEventListener("contextmenu",function(d){d.preventDefault()}),document.addEventListener("blur",function(){inputClear()}),isTouchDevice&&touchInputEnable&&b())}function inputUpdate(){headlessMode||(touchInputEnable&&isTouchDevice||document.hasFocus()||inputClear(),mousePos=screenToWorld(mousePosScreen),mouseDelta=screenToWorldDelta(mouseDeltaScreen),function(){var a=k=>{const l=m=>.3<m?percent(m,.3,.8):-.3>m?-percent(-m,.3,.8):0;return vec2(l(k.x),l(-k.y)).clampLength()};if(touchGamepadEnable&&isTouchDevice){if(touchGamepadTimer.isSet()){gamepadPrimary=0;var b=gamepadStickData[0]??(gamepadStickData[0]=[]),c=gamepadDpadData[0]??(gamepadDpadData[0]=vec2());b[0]=vec2();c.set();var d=touchGamepadSticks[0]??vec2();if(touchGamepadAnalog)b[0]=a(d);else if(.3<d.lengthSquared()){var e=clamp(round(d.x),-1,1);d=clamp(round(d.y),-1,1);c.set(e,-d);b[0]=c.clampLength()}1===touchGamepadButtonCount&&(c=touchGamepadSticks[1]??vec2(),b[1]=a(c));a=inputData[1]??(inputData[1]=[]);for(b=10;b--;)c=gamepadIsDown(b,0),a[b]=touchGamepadButtons[b]?c?1:3:c?4:0}}else if(gamepadsEnable&&navigator&&navigator.getGamepads&&(debug||document.hasFocus())){b=navigator.getGamepads();c=min(8,b.length);for(e=0;e<c;++e){var f=b[e];if(!f){inputData[e+1]=void 0;gamepadStickData[e]=void 0;gamepadDpadData[e]=void 0;gamepadHadInput[e]=void 0;continue}var g=inputData[e+1]??(inputData[e+1]=[]);d=gamepadStickData[e]??(gamepadStickData[e]=[]);const k=gamepadDpadData[e]??(gamepadDpadData[e]=vec2());for(var h=0;h<f.axes.length-1;h+=2)d[h>>1]=a(vec2(f.axes[h],f.axes[h+1]));h=!1;for(let l=f.buttons.length;l--;){const m=f.buttons[l],n=gamepadIsDown(l,e);g[l]=m.pressed?n?1:3:n?4:0;m.pressed&&(!m.value||.9<m.value)&&(h=!0)}h&&(gamepadHadInput[e]=!0,gamepadHadInput[gamepadPrimary]||(gamepadPrimary=e),isUsingGamepad||=gamepadPrimary===e);"standard"===f.mapping?k.set((gamepadIsDown(15,e)&&1)-(gamepadIsDown(14,e)&&1),(gamepadIsDown(12,e)&&1)-(gamepadIsDown(13,e)&&1)):f.axes&&2<=f.axes.length&&(g=clamp(round(f.axes[0]),-1,1),f=clamp(round(f.axes[1]),-1,1),k.set(g,-f));gamepadDirectionEmulateStick&&(k.x||k.y)&&(d[0]=k.clampLength())}touchGamepadEnable&&isUsingGamepad&&touchGamepadTimer.unset()}}())}function inputUpdatePost(){if(!headlessMode){for(const a of inputData)for(const b in a)a[b]&=1;mouseWheel=0;mouseDelta=vec2();mouseDeltaScreen=vec2()}}function inputRender(){if(touchInputEnable&&isTouchDevice&&!headlessMode&&touchGamepadEnable&&(touchGamepadTimer.isSet()||!touchGamepadDisplayTime)){var a=touchGamepadDisplayTime?percent(touchGamepadTimer.get(),touchGamepadDisplayTime+1,touchGamepadDisplayTime):1;if(a&&!paused){var b=mainContext;b.save();b.globalAlpha=a*touchGamepadAlpha;b.strokeStyle="#fff";b.lineWidth=3;a=touchGamepadSticks[0]??vec2();b.fillStyle=0<a.lengthSquared()?"#fff":"#000";b.beginPath();a=vec2(touchGamepadSize,mainCanvasSize.y-touchGamepadSize);if(touchGamepadAnalog)b.arc(a.x,a.y,touchGamepadSize/2,0,9);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)}b.fill();b.stroke();a=touchGamepadButtonCenter();c=1<touchGamepadButtonCount?touchGamepadSize/4:touchGamepadSize/2;for(d=0;d<touchGamepadButtonCount;d++){var e=mod(d-1,4);let f=2<touchGamepadButtonCount?e:min(e,touchGamepadButtonCount-1);f=3===f?2:2===f?3:f;e=2>touchGamepadButtonCount?a:a.add(vec2().setDirection(e,touchGamepadSize/2));b.fillStyle=touchGamepadButtons[f]?"#fff":"#000";b.beginPath();b.arc(e.x,e.y,c,0,9);b.fill();b.stroke()}b.restore()}}}function touchGamepadButtonCenter(){const a=mainCanvasSize.subtract(vec2(touchGamepadSize));2===touchGamepadButtonCount&&(a.x+=touchGamepadSize/2);return a}let audioContext=new AudioContext,audioMasterGain;const audioDefaultSampleRate=44100;function audioIsRunning(){return"running"===audioContext.state}function audioInit(){soundEnable&&!headlessMode&&(audioMasterGain=audioContext.createGain(),audioMasterGain.connect(audioContext.destination),audioMasterGain.gain.value=soundVolume)}class Sound{constructor(a,b,c=soundDefaultRange,d=soundDefaultTaper,e){soundEnable&&!headlessMode&&(ASSERT(!a||isArray(a)||isString(a),"asset must be a file name or zzfx array"),ASSERT(void 0===b||isNumber(b),"randomness must be a number"),ASSERT(void 0===b||0<=b&&1>=b,"randomness must be between 0 and 1"),ASSERT(isNumber(c),"range must be a number"),ASSERT(isNumber(d),"taper must be a number"),this.range=c,this.taper=d,this.randomness=b??0,this.sampleRate=audioDefaultSampleRate,this.loadedPercent=0,this.onloadCallback=e,Array.isArray(a)?(this.randomness=a[1]??b??.05,a[1]=0,this.sampleChannels=[zzfxG(...a)],this.loadedPercent=1,e?.(this)):"string"===typeof a&&this.loadSound(a))}play(a,b=1,c=1,d=1,e=!1,f=!1){ASSERT(!a||isVector2(a),"pos must be a vec2");ASSERT(isNumber(b),"volume must be a number");ASSERT(isNumber(c),"pitch must be a number");ASSERT(isNumber(d),"randomnessScale must be a number");if(soundEnable&&!headlessMode&&this.sampleChannels){var g;if(a){if(g=this.range){const h=cameraPos.distanceSquared(a);if(h>g*g)return;b*=percent(h**.5,g,g*this.taper)}g=2*worldToScreen(a).x/mainCanvas.width-1}a=c+c*this.randomness*d*rand(-1,1);return new SoundInstance(this,b,a,g,e,f)}}playMusic(a=1,b=!0,c=!1){return this.play(void 0,a,1,0,b,c)}playNote(a=0,b,c){ASSERT(isNumber(a),"semitoneOffset must be a number");a=getNoteFrequency(a,1);return this.play(b,c,a,0)}getDuration(){return this.sampleChannels?.[0]?.length/this.sampleRate||0}isLoaded(){return 1===this.loadedPercent}async loadSound(a){var b=await fetch(a);if(!b.ok)throw Error(`Failed to load sound from ${a}: ${b.status} ${b.statusText}`);a=await b.arrayBuffer();a=await audioContext.decodeAudioData(a);b=a.numberOfChannels;const c=[];for(let d=0;d<b;d++){const e=a.getChannelData(d),f=e.length;c[d]=Array(f);let g=0;for(;g<f;){await new Promise(k=>setTimeout(k,0));const h=min(g+1e5,f);for(;g<h;g++)c[d][g]=e[g];this.loadedPercent=(d*f+g)/(b*f)}}this.sampleRate=a.sampleRate;this.sampleChannels=c;this.loadedPercent=1;this.onloadCallback?.(this)}}class SoundInstance{constructor(a,b=1,c=1,d=0,e=!1,f=!1){ASSERT(a instanceof Sound,"SoundInstance requires a valid Sound object");ASSERT(0<=b,"Sound volume must be positive or zero");ASSERT(0<=c,"Sound rate must be positive or zero");ASSERT(isNumber(d),"Sound pan must be a number");this.sound=a;this.volume=b;this.rate=c;this.pan=d;this.loop=e;this.pausedTime=0;this.source=this.gainNode=this.startTime=void 0;this.onendedCallback=g=>{g===this.source&&(this.source=void 0)};f||this.start()}start(a=0){ASSERT(0<=a,"Sound start offset must be positive or zero");this.isPlaying()&&this.stop();this.gainNode=audioContext.createGain();(this.source=playSamples(this.sound.sampleChannels,this.volume,this.rate,this.pan,this.loop,this.sound.sampleRate,this.gainNode,a,this.onendedCallback))?(this.startTime=audioContext.currentTime-a,this.pausedTime=void 0):(this.startTime=void 0,this.pausedTime=0)}setVolume(a){ASSERT(0<=a,"Sound volume must be positive or zero");this.volume=a;this.gainNode&&(this.gainNode.gain.value=a)}stop(a=0){ASSERT(0<=a,"Sound fade time must be positive or zero");if(this.isPlaying())if(a){const b=audioContext.currentTime;a=b+a;this.gainNode.gain.linearRampToValueAtTime(1,b);this.gainNode.gain.linearRampToValueAtTime(0,a);this.source.stop(a)}else this.source.stop();this.pausedTime=0;this.startTime=this.source=void 0}pause(){this.isPaused()||(this.pausedTime=this.getCurrentTime(),this.source.stop(),this.startTime=this.source=void 0)}resume(){this.isPaused()&&this.start(this.pausedTime)}isPlaying(){return!!this.source}isPaused(){return!this.isPlaying()}getCurrentTime(){const a=mod(audioContext.currentTime-this.startTime,this.getDuration());return this.isPlaying()?a:this.pausedTime}getDuration(){return this.rate?this.sound.getDuration()/this.rate:0}getSource(){return this.source}}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?.cancel()}function getNoteFrequency(a,b=220){return b*2**(a/12)}function playSamples(a,b=1,c=1,d=0,e=!1,f=audioDefaultSampleRate,g,h=0,k){if(soundEnable&&!headlessMode){if(audioIsRunning()){var l=audioContext.createBuffer(a.length,a[0].length,f),m=audioContext.createBufferSource();a.forEach((n,p)=>l.getChannelData(p).set(n));m.buffer=l;m.playbackRate.value=c;m.loop=e;g=g||audioContext.createGain();g.gain.value=b;g.connect(audioMasterGain);a=new StereoPannerNode(audioContext,{pan:clamp(d,-1,1)});m.connect(a).connect(g);k&&m.addEventListener("ended",()=>k(m));m.start(0,h*c);return m}audioContext.resume()}}function zzfx(...a){return playSamples([zzfxG(...a)])}function zzfxG(a=1,b=.05,c=220,d=0,e=0,f=.1,g=0,h=1,k=0,l=0,m=0,n=0,p=0,q=0,r=0,t=0,u=0,w=1,x=0,B=0,z=0){var v=audioDefaultSampleRate;let y=2*PI,D=k*=500*y/v/v;b=c*=(1+rand(b,-b))*y/v;let G=0,J=0,H=0,E=1,K=[],F=0,A=0,C=0,O;var M=y*abs(z)*2/v,L=cos(M),N=sin(M)/2/2,I=1+N;M=-2*L/I;N=(1-N)/I;let P=(1+sign(z)*L)/2/I;L=-(sign(z)+L)/I;let Q=I=0,R=0,S=0;d=d*v||9;x*=v;e*=v;f*=v;u*=v;l*=500*y/v**3;r*=y/v;m*=y/v;n*=v;p=p*v|0;for(v=d+x+e+f+u|0;A<v;K[A++]=C*a)++H%(100*t|0)||(C=g?1<g?2<g?3<g?4<g?F/y%1<h/2?1:-1:sin(F**3):max(min(tan(F),1),-1):1-(2*F/y%2+2)%2:1-4*abs(round(F/y)-F/y):sin(F),C=(p?1-B+B*sin(y*A/p):1)*(4<g?C:sign(C)*abs(C)**h)*(A<d?A/d:A<d+x?1-(A-d)/x*(1-w):A<d+x+e?w:A<v-u?(v-A-u)/f*w:0),C=u?C/2+(u>A?0:(A<v-u?1:(v-A)/u)*K[A-u|0]/2/a):C,z&&(C=S=P*I+L*(I=Q)+P*(Q=C)-N*R-M*(R=S))),O=(c+=k+=l)*cos(r*G++),F+=O+O*q*sin(A**5),E&&++E>n&&(c+=m,b+=m,E=0),!p||++J%p||(c=b,k=D,E||=1);return K}const tileCollisionLayers=[];function tileCollisionGetData(a,b=!0){for(const c of tileCollisionLayers)if((!b||c.isSolid)&&a.arrayCheck(c.size)){const d=c.getCollisionData(a);if(d)return d}return 0}function tileCollisionTest(a,b=vec2(),c,d=!0){for(const e of tileCollisionLayers)if((!d||e.isSolid)&&e.collisionTest(a,b,c))return e}function tileCollisionRaycast(a,b,c,d,e=!0){for(const f of tileCollisionLayers)if(!e||f.isSolid){const g=f.collisionRaycast(a,b,c,d);if(g)return g}}function tileLayersLoad(a,b=tile(),c=0,d,e=!0){a||(a={},a.height=a.width=50,a.layers=[{}],a.layers[0].data=Array(2500).fill(0));ASSERT(a.width&&a.height);ASSERT(a.layers&&a.layers.length);const f=[],g=vec2(a.width,a.height),h=a.layers.length;for(let m=h;m--;){const n=a.layers[m];ASSERT(n.data&&n.data.length);ASSERT(g.area()===n.data.length);var k=c-(h-1-m);k=new TileCollisionLayer(vec2(),g,b,k);f[m]=k;const p=n.tintcolor?(new Color).setHex(n.tintcolor):n.color||WHITE;ASSERT(isColor(p),"layer color is not a color");for(let q=g.x;q--;)for(let r=g.y;r--;){const t=vec2(q,g.y-1-r);var l=n.data[q+r*g.x];l&&(l=new TileLayerData(l-1,0,!1,p),k.setData(t,l),m===d&&k.setCollisionData(t,1))}e&&k.redraw()}return f}class TileLayerData{constructor(a,b=0,c=!1,d=new Color){this.tile=a;this.direction=b;this.mirror=c;this.color=d.copy()}clear(){this.tile=this.direction=0;this.mirror=!1;this.color=new Color}}class CanvasLayer extends EngineObject{constructor(a,b,c=0,d=0,e=vec2(512),f=!0){ASSERT(isVector2(e),"canvasSize must be a Vector2");super(a,b,void 0,c,WHITE,d);this.canvas=headlessMode?void 0:new OffscreenCanvas(e.x,e.y);this.context=this.canvas?.getContext("2d");this.textureInfo=new TextureInfo(this.canvas,f);this.mass=0}destroy(){this.destroyed||(this.textureInfo.destroyWebGLTexture(),super.destroy())}render(){this.draw(this.pos,this.size,this.color,this.angle,this.mirror,this.additiveColor)}draw(a,b,c=WHITE,d=0,e=!1,f,g=!1,h){const k=(new TileInfo).setFullImage(this.textureInfo),l=this.hasWebGL();drawTile(a,b,k,c,d,e,f,l,g,h)}drawTile(a,b=vec2(1),c,d=new Color,e=0,f=!1){a=a.subtract(this.pos).multiply(this.tileInfo.size);b=b.multiply(this.tileInfo.size);a.y=this.canvas.height-a.y;const g=mainCanvasSize;mainCanvasSize=vec2(this.canvas.width,this.canvas.height);const h=this.hasWebGL();h&&glSetRenderTarget(this.textureInfo.glTexture);drawTile(a,b,c,d,e,f,void 0,h,!0,h?void 0:this.context);h&&glSetRenderTarget();mainCanvasSize=g}drawRect(a,b,c,d){this.drawTile(a,b,void 0,c,d)}updateWebGL(){this.textureInfo.createWebGLTexture()}hasWebGL(){return glEnable&&this.textureInfo.hasWebGL()}}class TileLayer extends CanvasLayer{constructor(a,b,c=tile(),d=0,e=!0){const f=c?b.multiply(c.size):b;super(a,b,0,d,f,e);this.tileInfo=void 0;this.data=[];this.isUsingWebGL=!1;if(headlessMode)this.render=()=>{},this.redraw=()=>{},this.redrawStart=()=>{},this.redrawEnd=()=>{},this.drawTileData=()=>{},this.redrawTileData=()=>{},this.drawLayerTile=()=>{},this.drawLayerRect=()=>{},this.clearLayerRect=()=>{};else for(c&&(this.tileInfo=c.frame(0),this.tileInfo.bleed=0),a=this.size.area();a--;)this.data.push(new TileLayerData)}setData(a,b,c=!1){a=a.floor();ASSERT(isVector2(a),"layerPos must be a Vector2");ASSERT(b instanceof TileLayerData,"data must be a TileLayerData");a.arrayCheck(this.size)&&(this.data[(a.y|0)*this.size.x+a.x|0]=b,c&&(drawContext===this.context?this.drawTileData(a):this.redrawTileData(a)))}clearData(a,b=!1){this.setData(a,new TileLayerData,b)}getData(a){ASSERT(isVector2(a),"layerPos must be a Vector2");return a.arrayCheck(this.size)&&this.data[(a.y|0)*this.size.x+a.x|0]}update(){!glEnable&&this.isUsingWebGL&&(this.isUsingWebGL=!1,this.redraw())}render(){ASSERT(drawContext!==this.context,"must call redrawEnd() after drawing tiles!");const a=this.pos.add((this.drawSize||this.size).scale(.5));this.draw(a,this.size,this.color,this.angle,this.mirror,this.additiveColor)}onRedraw(){}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.isUsingWebGL&&glFlush();this.onRedraw();this.redrawEnd()}redrawStart(a=!1){if(this.context){ASSERT(drawContext!==this.context);this.savedRenderSettings=[drawContext,mainCanvasSize,cameraPos,cameraScale,canvasClearColor];drawContext=this.context;var b=this.tileInfo?.size??vec2(1);mainCanvasSize=this.size.multiply(b);canvasClearColor=CLEAR_BLACK;cameraPos=this.size.multiply(b).scale(.5);cameraScale=1;(this.isUsingWebGL=this.hasWebGL())?glSetRenderTarget(this.textureInfo.glTexture,a):(this.context.imageSmoothingEnabled=!tilesPixelated,a&&(this.canvas.width=mainCanvasSize.x,this.canvas.height=mainCanvasSize.y))}}redrawEnd(){this.context&&(ASSERT(drawContext===this.context),this.isUsingWebGL&&glSetRenderTarget(),[drawContext,mainCanvasSize,cameraPos,cameraScale,canvasClearColor]=this.savedRenderSettings)}drawTileData(a,b=!0){if(this.context){ASSERT(drawContext===this.context,"must call redrawStart() before drawing tiles");var c=this.tileInfo?.size??vec2(1),d=a.multiply(c);b&&this.clearLayerRect(d,c);a=this.getData(a);a.tile&&(b=this.tileInfo&&this.tileInfo.tile(a.tile),this.drawLayerTile(d,c,b,a.color,a.direction*PI/2,a.mirror))}}redrawTileData(a,b=!0){this.context&&(ASSERT(drawContext!==this.context,"redrawStart() should not be active when calling redrawTileData(), instead use drawTileData()"),this.redrawStart(),this.drawTileData(a,b),this.redrawEnd())}drawLayerTile(a,b=vec2(1),c,d=WHITE,e=0,f,g){a=a.add(b.scale(.5));drawTile(a,b,c,d,e,f,g,this.isUsingWebGL)}drawLayerRect(a,b,c,d=0){this.drawLayerTile(a,b,void 0,c,d)}clearLayerRect(a,b){ASSERT(drawContext===this.context,"must call redrawStart() before clearing tiles");const c=a.x;a=this.canvas.height-a.y-b.y;this.hasWebGL()?glClearRect(c,a,b.x,b.y):this.context.clearRect(c,a,b.x,b.y)}}class TileCollisionLayer extends TileLayer{constructor(a,b,c=tile(),d=0,e=!0){super(a,b.floor(),c,d,e);this.collisionData=[];this.initCollision(this.size);tileCollisionLayers.push(this);this.isSolid=!0}destroy(){if(!this.destroyed){var a=tileCollisionLayers.indexOf(this);ASSERT(0<=a,"tile collision layer not found in array");0<=a&&tileCollisionLayers.splice(a,1);super.destroy()}}initCollision(a){ASSERT(isVector2(a),"size must be a Vector2");this.size=a.floor();this.collisionData=[];this.collisionData.length=a.area();this.collisionData.fill(0)}setCollisionData(a,b=1){ASSERT(isVector2(a),"layerPos must be a Vector2");const c=(a.y|0)*this.size.x+a.x|0;a.arrayCheck(this.size)&&(this.collisionData[c]=b)}clearCollisionData(a){this.setCollisionData(a,0)}getCollisionData(a){ASSERT(isVector2(a),"layerPos must be a Vector2");const b=(a.y|0)*this.size.x+a.x|0;return a.arrayCheck(this.size)?this.collisionData[b]:0}collisionTest(a,b=new Vector2,c){ASSERT(isVector2(a)&&isVector2(b),"pos and size must be Vector2s");ASSERT(!c||"function"===typeof c||c instanceof EngineObject,"callbackObject must be a function or EngineObject");const d=c?"function"===typeof c?(h,k)=>c(h,k):(h,k)=>c.collideWithTile(h,k):()=>!0;var e=a.x-this.pos.x,f=a.y-this.pos.y;a=max(e-b.x/2|0,0);var g=max(f-b.y/2|0,0);e=min(e+b.x/2,this.size.x);b=min(f+b.y/2,this.size.y);for(f=new Vector2;g<b;++g)for(let h=a;h<e;++h){const k=this.collisionData[g*this.size.x+h];if(k&&d(k,f.set(h+this.pos.x,g+this.pos.y)))return!0}return!1}collisionRaycast(a,b,c,d){ASSERT(isVector2(a)&&isVector2(b),"positions must be Vector2s");ASSERT(!c||"function"===typeof c||c instanceof EngineObject,"callbackObject must be a function or EngineObject");const e=c?"function"===typeof c?(h,k)=>c(h,k):(h,k)=>c.collideWithTile(h,k):h=>0<h,f=new Vector2,g=lineTest(a,b,h=>{const k=this.getCollisionData(f.set(h.x-this.pos.x,h.y-this.pos.y));return k&&e(k,h)},d);if(debugRaycast&&g){const h=g.floor().add(vec2(.5));debugRect(h,vec2(1),"#f008");debugLine(a,b,"#00f",.02);debugLine(a,g,"#f00",.02);debugPoint(g,"#0f0");d&&debugLine(g,g.add(d),"#ff0",.02)}return g}}class ParticleEmitter extends EngineObject{constructor(a,b,c=0,d=0,e=100,f=PI,g,h=WHITE,k=WHITE,l=CLEAR_WHITE,m=CLEAR_WHITE,n=.5,p=.1,q=1,r=.1,t=.05,u=1,w=1,x=0,B=PI,z=.1,v=.2,y=!1,D=!1,G=!0,J=D?1e9:0,H=!1){super(a,vec2(),g,b,void 0,J);this.emitCircle="number"===typeof c;this.emitSize="number"===typeof c?vec2(c):c.copy();this.emitTime=d;this.emitRate=e;this.emitConeAngle=f;this.colorStartA=h.copy();this.colorStartB=k.copy();this.colorEndA=l.copy();this.colorEndB=m.copy();this.randomColorLinear=G;this.particleTime=n;this.sizeStart=p;this.sizeEnd=q;this.speed=r;this.angleSpeed=t;this.damping=u;this.angleDamping=w;this.gravityScale=x;this.particleConeAngle=B;this.fadeRate=z;this.randomness=v;this.collideTiles=y;this.additive=D;this.localSpace=H;this.trailScale=0;this.particleCollideCallback=this.particleDestroyCallback=this.particleCreateCallback=void 0;this.emitTimeBuffer=this.velocityInheritance=0;this.particles=[];this.previousAngle=this.angle;this.previousPos=this.pos.copy()}update(){ASSERT(0<=this.angleDamping&&1>=this.angleDamping);ASSERT(0<=this.damping&&1>=this.damping);if(this.velocityInheritance){var a=this.velocityInheritance;this.velocity.x=a*(this.pos.x-this.previousPos.x);this.velocity.y=a*(this.pos.y-this.previousPos.y);this.angleVelocity=a*(this.angle-this.previousAngle);this.previousAngle=this.angle;this.previousPos.x=this.pos.x;this.previousPos.y=this.pos.y}if(this.isActive()){if(this.emitRate&&particleEmitRateScale)for(a=1/this.emitRate/particleEmitRateScale,this.emitTimeBuffer+=timeDelta;0<this.emitTimeBuffer;this.emitTimeBuffer-=a)this.emitParticle()}else 0===this.particles.length&&this.destroy(!0);this.particles=this.particles.filter(b=>{b.update();return!b.destroyed});debugParticles&&(this.emitCircle?debugCircle(this.pos,this.emitSize.x/2,"#0f0"):debugRect(this.pos,this.emitSize,"#0f0",0,this.angle))}emitParticle(){var a=this.emitCircle?randInCircle(this.emitSize.x/2):vec2(rand(-.5,.5),rand(-.5,.5)).multiply(this.emitSize).rotate(this.angle);let b=rand(this.particleConeAngle,-this.particleConeAngle);this.localSpace||(a.x+=this.pos.x,a.y+=this.pos.y,b+=this.angle);const c=this.randomness;var d=n=>n+n*rand(c,-c);const e=d(this.particleTime),f=d(this.sizeStart),g=d(this.sizeEnd);var h=d(this.speed),k=d(this.angleSpeed)*randSign(),l=rand(this.emitConeAngle,-this.emitConeAngle);d=randColor(this.colorStartA,this.colorStartB,this.randomColorLinear);const m=randColor(this.colorEndA,this.colorEndB,this.randomColorLinear);l=this.localSpace?l:this.angle+l;h=vec2(h*sin(l),h*cos(l));!this.localSpace&&0<this.velocityInheritance&&(h.x+=this.velocity.x,h.y+=this.velocity.y,k+=this.angleVelocity);a=new Particle(this,a,b,d,m,e,f,g,h,k);this.particles.push(a);this.particleCreateCallback?.(a);return a}updatePhysics(){}render(){for(const a of this.particles)a.render()}isActive(){return!this.emitTime||this.getAliveTime()<this.emitTime}destroy(a=!1){this.destroyed||(super.destroy(a),!a&&0<this.particles.length&&(this.destroyed=!1,this.emitTime=-1))}}class Particle{constructor(a,b,c,d,e,f,g,h,k=vec2(),l=0){this.emitter=a;this.pos=b;this.angle=c;this.size=vec2(g);this.color=d.copy();this.colorStart=d;this.colorEnd=e;this.lifeTime=f;this.sizeStart=g;this.sizeEnd=h;this.velocity=k;this.angleVelocity=l;this.spawnTime=time;this.mirror=randBool();this.groundObject=void 0;this.destroyed=!1;this.tileInfo=a.tileInfo}update(){var a=this.emitter,b=a.damping,c=a.angleDamping,d=a.restitution,e=a.friction,f=a.gravityScale;const g=a.collideTiles,h=a.particleCollideCallback;if(0<this.lifeTime&&time-this.spawnTime>this.lifeTime)this.destroy();else if(a=this.pos.copy(),this.velocity.x*=b,this.velocity.y*=b,this.pos.x+=this.velocity.x+=gravity.x*f,this.pos.y+=this.velocity.y+=gravity.y*f,this.angle+=this.angleVelocity*=c,enablePhysicsSolver&&g&&(b=this.velocity.lengthSquared(),b>objectMaxSpeed*objectMaxSpeed&&(b=objectMaxSpeed/b**.5,this.velocity.x*=b,this.velocity.y*=b),this.groundObject=void 0,f=h?k=>{const l=tileCollisionGetData(k);return l&&h(this,l,k)}:k=>0<tileCollisionGetData(k),f(this.pos)&&(b=tileCollisionTest(this.pos),!f(a)&&(!h||h?.(this,b))))){c=f(vec2(this.pos.x,a.y));f=f(vec2(a.x,this.pos.y));d=max(d,b.restitution);e=max(e,b.friction);c&&(this.pos.x=a.x,this.velocity.x*=-d,this.velocity.y*=e);if(f||!c){if(0>this.velocity.y&&0>gravity.y||0<this.velocity.y&&0<gravity.y)this.groundObject=b;this.pos.y=a.y;this.velocity.y*=-d;this.velocity.x*=e}debugPhysics&&debugRect(this.pos,this.size,"#f00")}}destroy(){const a=this.emitter.particleDestroyCallback,b=this.colorEnd;this.color.set(b.r,b.g,b.b,b.a);this.size.set(this.sizeEnd,this.sizeEnd);this.destroyed=!0;a?.(this)}render(){var a=this.emitter,b=a.localSpace;const c=a.additive,d=a.trailScale;var e=a.fadeRate/2,f=0<this.lifeTime?min((time-this.spawnTime)/this.lifeTime,1):1,g=1-f;const h=vec2(g*this.sizeStart+f*this.sizeEnd);this.color.r=g*this.colorStart.r+f*this.colorEnd.r;this.color.g=g*this.colorStart.g+f*this.colorEnd.g;this.color.b=g*this.colorStart.b+f*this.colorEnd.b;this.color.a=(g*this.colorStart.a+f*this.colorEnd.a)*(f<e?f/e:f>1-e?(1-f)/e:1);c&&setBlendMode(!0);e=this.pos.copy();f=this.angle;if(b){g=a.angle;const k=cos(g),l=sin(g);e.set(a.pos.x+e.x*k-e.y*l,a.pos.y+e.x*l+e.y*k);f+=g}if(d){if(a=b?this.velocity.rotate(-a.angle):this.velocity,b=a.length())h.y=max(h.x,b*d),f=atan2(a.x,a.y),drawTile(e,h,this.tileInfo,this.color,f,this.mirror)}else drawTile(e,h,this.tileInfo,this.color,f,this.mirror);c&&setBlendMode();debugParticles&&debugRect(e,h,"#f005",0,f)}}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=mainContext;var c=min(medalDisplaySize.x,mainCanvas.width);const d=medalDisplaySize.y;var e=mainCanvas.width-c;a*=-d;var f=hsl(0,0,.9);b.save();b.beginPath();b.fillStyle=f.toString();b.strokeStyle=BLACK.toString();b.lineWidth=3;b.rect(e,a,c,d);b.fill();b.stroke();b.clip();f=vec2(.1,.05).scale(d);const g=d-2*f.x;this.renderIcon(vec2(e+f.x+g/2,a+d/2),g);const h=.5*d,k=.3*d;e=vec2(e+g+2*f.x,a+2*f.y+h/2);c=c-g-3*f.x;drawTextScreen(this.name,e,h,BLACK,0,void 0,"left",void 0,void 0,c);e.y=a+d-2*f.y-k/2;drawTextScreen(this.description,e,k,BLACK,0,void 0,"left",void 0,void 0,c);b.restore()}renderIcon(a,b){this.image?mainContext.drawImage(this.image,a.x-b/2,a.y-b/2,b,b):drawTextScreen(this.icon,a,.7*b,BLACK)}storageKey(){return medalsSaveName+"_"+this.id}}let glCanvas,glContext,glAntialias=!0,glShader,glPolyShader,glPolyMode,glAdditive,glBatchAdditive,glActiveTexture,glArrayBuffer,glGeometryBuffer,glPositionData,glColorData,glBatchCount,glTextureInfos,glInstancedVAO,glPolyVAO,glFramebuffer,glRenderTarget,glCanBeEnabled=!0;const gl_ARRAY_BUFFER_SIZE=5e5,gl_INDICES_PER_INSTANCE=11,gl_INSTANCE_BYTE_STRIDE=4*gl_INDICES_PER_INSTANCE,gl_MAX_INSTANCES=gl_ARRAY_BUFFER_SIZE/gl_INSTANCE_BYTE_STRIDE|0,gl_INDICES_PER_POLY_VERTEX=3,gl_POLY_VERTEX_BYTE_STRIDE=4*gl_INDICES_PER_POLY_VERTEX,gl_MAX_POLY_VERTEXES=gl_ARRAY_BUFFER_SIZE/gl_POLY_VERTEX_BYTE_STRIDE|0;function glInit(a){function b(){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;}");glPolyShader=glCreateProgram("#version 300 es\nprecision highp float;uniform mat4 m;in vec2 p;in vec4 c;out vec4 d;void main(){gl_Position=m*vec4(p,1,1);d=c;}","#version 300 es\nprecision highp float;in vec4 d;out vec4 c;void main(){c=d;}");var c=new ArrayBuffer(gl_ARRAY_BUFFER_SIZE);glPositionData=new Float32Array(c);glColorData=new Uint32Array(c);glArrayBuffer=glContext.createBuffer();glGeometryBuffer=glContext.createBuffer();glFramebuffer=glContext.createFramebuffer();glBatchCount=0;c=new Float32Array([0,0,1,0,0,1,1,1]);glContext.bindBuffer(glContext.ARRAY_BUFFER,glGeometryBuffer);glContext.bufferData(glContext.ARRAY_BUFFER,c,glContext.STATIC_DRAW);let d,e,f;c=(g,h,k,l,m=0)=>{g=glContext.getAttribLocation(e,g);const n=1===k,p=k&&f;glContext.enableVertexAttribArray(g);glContext.vertexAttribPointer(g,l,h,n,p,d);glContext.vertexAttribDivisor(g,m);d+=l*k};glInstancedVAO=glContext.createVertexArray();glContext.bindVertexArray(glInstancedVAO);d=0;e=glShader;f=gl_INSTANCE_BYTE_STRIDE;glContext.bindBuffer(glContext.ARRAY_BUFFER,glGeometryBuffer);c("g",glContext.FLOAT,0,2);glContext.bindBuffer(glContext.ARRAY_BUFFER,glArrayBuffer);glContext.bufferData(glContext.ARRAY_BUFFER,gl_ARRAY_BUFFER_SIZE,glContext.DYNAMIC_DRAW);c("p",glContext.FLOAT,4,4,1);c("u",glContext.FLOAT,4,4,1);c("c",glContext.UNSIGNED_BYTE,1,4,1);c("a",glContext.UNSIGNED_BYTE,1,4,1);c("r",glContext.FLOAT,4,1,1);glPolyVAO=glContext.createVertexArray();glContext.bindVertexArray(glPolyVAO);d=0;e=glPolyShader;f=gl_POLY_VERTEX_BYTE_STRIDE;c("p",glContext.FLOAT,4,2);c("c",glContext.UNSIGNED_BYTE,1,4)}glTextureInfos=new Set;!glEnable||headlessMode?glCanBeEnabled=!1:(glCanvas=document.createElement("canvas"),(glContext=glCanvas.getContext("webgl2",{antialias:glAntialias}))?(a.appendChild(glCanvas),b(),glCanvas.addEventListener("webglcontextlost",c=>{glEnable=!1;glCanvas.style.display="none";c.preventDefault();LOG("WebGL context lost! Switching to Canvas2d rendering.");for(const d of glTextureInfos)d.glTexture=void 0;glActiveTexture=void 0;pluginList.forEach(d=>d.glContextLost?.())}),glCanvas.addEventListener("webglcontextrestored",()=>{glEnable=!0;glCanvas.style.display="";LOG("WebGL context restored, reinitializing...");b();for(const c of glTextureInfos)c.glTexture=glCreateTexture(c.image);pluginList.forEach(c=>c.glContextRestored?.())})):(console.warn("WebGL2 not supported, falling back to 2D canvas rendering!"),glCanvas=glContext=void 0,glCanBeEnabled=glEnable=!1))}function glSetInstancedMode(a=!1){if(a||glPolyMode)glFlush(),glPolyMode=!1,glContext.useProgram(glShader),glContext.bindVertexArray(glInstancedVAO)}function glSetPolyMode(){glPolyMode||(glFlush(),glPolyMode=!0,glContext.useProgram(glPolyShader),glContext.bindVertexArray(glPolyVAO))}function glPreRender(a=!0){if(glEnable&&glContext){ASSERT(!glBatchCount,"glPreRender called with unflushed batch.");glRenderTarget||(glCanvas.width=mainCanvasSize.x,glCanvas.height=mainCanvasSize.y);glContext.viewport(0,0,mainCanvasSize.x,mainCanvasSize.y);a&&glClearCanvas();a=vec2(2*cameraScale).divide(mainCanvasSize);glRenderTarget&&(a.y=-a.y);var b=cameraPos.rotate(-cameraAngle);b=vec2(-1).subtract(b.multiply(a));var c=cos(cameraAngle),d=sin(cameraAngle);a=[a.x*c,a.y*d,0,0,-a.x*d,a.y*c,0,0,1,1,1,0,b.x,b.y,0,1];b=glPolyShader;glContext.useProgram(b);b=glContext.getUniformLocation(b,"m");glContext.uniformMatrix4fv(b,!1,a);b=glShader;glContext.useProgram(b);b=glContext.getUniformLocation(b,"m");glContext.uniformMatrix4fv(b,!1,a);glContext.activeTexture(glContext.TEXTURE0);textureInfos[0]&&(glActiveTexture=textureInfos[0].glTexture,glContext.bindTexture(glContext.TEXTURE_2D,glActiveTexture));glContext.bindBuffer(glContext.ARRAY_BUFFER,glArrayBuffer);glAdditive=glBatchAdditive=!1;glSetInstancedMode(!0)}}function glClearCanvas(){if(glContext){var a=canvasClearColor;glContext.clearColor(a.r,a.g,a.b,a.a);glContext.clear(glContext.COLOR_BUFFER_BIT)}}function glSetTexture(a,b=!1){glContext&&a!==glActiveTexture&&(glFlush(),glActiveTexture=a,glContext.bindTexture(glContext.TEXTURE_2D,glActiveTexture),a=b?glContext.REPEAT:glContext.CLAMP_TO_EDGE,glContext.texParameteri(glContext.TEXTURE_2D,glContext.TEXTURE_WRAP_S,a),glContext.texParameteri(glContext.TEXTURE_2D,glContext.TEXTURE_WRAP_T,a))}function glCompileShader(a,b){if(glContext){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){if(glContext){var 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){if(glContext){var b=glContext.createTexture(),c=!1;a?.width?(glSetTextureData(b,a),glContext.bindTexture(glContext.TEXTURE_2D,b),c=!tilesPixelated&&isPowerOfTwo(a.width)&&isPowerOfTwo(a.height)):(a=new Uint8Array([255,255,255,255]),glContext.bindTexture(glContext.TEXTURE_2D,b),glContext.texImage2D(glContext.TEXTURE_2D,0,glContext.RGBA,1,1,0,glContext.RGBA,glContext.UNSIGNED_BYTE,a));a=tilesPixelated?glContext.NEAREST:glContext.LINEAR;var d=c?glContext.LINEAR_MIPMAP_LINEAR:a;glContext.texParameteri(glContext.TEXTURE_2D,glContext.TEXTURE_MAG_FILTER,a);glContext.texParameteri(glContext.TEXTURE_2D,glContext.TEXTURE_MIN_FILTER,d);c&&glContext.generateMipmap(glContext.TEXTURE_2D);glContext.bindTexture(glContext.TEXTURE_2D,glActiveTexture);return b}}function glDeleteTexture(a){glContext&&glContext.deleteTexture(a)}function glSetTextureData(a,b){glContext&&(ASSERT(0<b?.width,"Invalid image data."),glContext.bindTexture(glContext.TEXTURE_2D,a),glContext.texImage2D(glContext.TEXTURE_2D,0,glContext.RGBA,glContext.RGBA,glContext.UNSIGNED_BYTE,b),glContext.bindTexture(glContext.TEXTURE_2D,glActiveTexture))}function glRegisterTextureInfo(a){headlessMode||(glTextureInfos.add(a),glContext&&(a.glTexture?glSetTextureData(a.glTexture,a.image):a.glTexture=glCreateTexture(a.image)))}function glUnregisterTextureInfo(a){if(!headlessMode){glTextureInfos.delete(a);var b=a.glTexture;a.glTexture=void 0;glDeleteTexture(b)}}function glFlush(){if(glEnable&&glContext&&glBatchCount){const 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,0,glBatchCount*(glPolyMode?gl_INDICES_PER_POLY_VERTEX:gl_INDICES_PER_INSTANCE));glPolyMode?glContext.drawArrays(glContext.TRIANGLE_STRIP,0,glBatchCount):glContext.drawArraysInstanced(glContext.TRIANGLE_STRIP,0,4,glBatchCount);drawCount+=glBatchCount;glBatchCount=0}glBatchAdditive=glAdditive}function glCopyToContext(a){glEnable&&glContext&&(glFlush(),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=0,f=0,g=0,h=1,k=1,l=-1,m=0){(glBatchCount>=gl_MAX_INSTANCES||glBatchAdditive!==glAdditive)&&glFlush();glSetInstancedMode();let n=glBatchCount++*gl_INDICES_PER_INSTANCE;glPositionData[n++]=a;glPositionData[n++]=b;glPositionData[n++]=c;glPositionData[n++]=d;glPositionData[n++]=f;glPositionData[n++]=g;glPositionData[n++]=h;glPositionData[n++]=k;glColorData[n++]=l;glColorData[n++]=m;glPositionData[n++]=e}function glDrawPointsTransform(a,b,c,d,e,f,g,h=!0){const k=[];for(const l of a){a=l.x*e;const m=l.y*f,n=sin(-g),p=cos(-g);k.push(vec2(c+p*a-n*m,d+n*a+p*m))}c=h?glPolyStrip(k):k;glDrawPoints(c,b)}function glDrawOutlineTransform(a,b,c,d,e,f,g,h,k=!0){a=glMakeOutline(a,c,k);glDrawPointsTransform(a,b,d,e,f,g,h,!1)}function glDrawPoints(a,b){if(glEnable&&!(3>a.length)){var c=a.length+2;(glBatchCount+c>=gl_MAX_POLY_VERTEXES||glBatchAdditive!==glAdditive)&&glFlush();glSetPolyMode();var d=glBatchCount*gl_INDICES_PER_POLY_VERTEX;for(let f=c;f--;){var e=clamp(f-1,0,c-3);e=a[e];glPositionData[d++]=e.x;glPositionData[d++]=e.y;glColorData[d++]=b}glBatchCount+=c}}function glDrawColoredPoints(a,b){if(glEnable&&!(3>a.length)){var c=a.length+2;(glBatchCount+c>=gl_MAX_POLY_VERTEXES||glBatchAdditive!==glAdditive)&&glFlush();glSetPolyMode();var d=glBatchCount*gl_INDICES_PER_POLY_VERTEX;for(let f=c;f--;){var e=clamp(f-1,0,c-3);const g=a[e];e=b[e];glPositionData[d++]=g.x;glPositionData[d++]=g.y;glColorData[d++]=e}glBatchCount+=c}}function glSetRenderTarget(a,b=!1){a?(glRenderTarget=a,glContext.bindFramebuffer(glContext.FRAMEBUFFER,glFramebuffer),glContext.framebufferTexture2D(glContext.FRAMEBUFFER,glContext.COLOR_ATTACHMENT0,glContext.TEXTURE_2D,a,0),glPreRender(b)):(glFlush(),glRenderTarget=void 0,glContext.bindFramebuffer(glContext.FRAMEBUFFER,null))}function glClearRect(a,b,c,d){glEnable&&(glContext.enable(glContext.SCISSOR_TEST),glContext.scissor(a,b,c,d),glContext.clearColor(0,0,0,0),glContext.clear(glContext.COLOR_BUFFER_BIT),glContext.disable(glContext.SCISSOR_TEST))}function glMakeOutline(a,b,c=!0){if(2>a.length)return[];const d=b/2,e=[],f=a.length;b*=100;for(let p=0;p<f;p++){var g=a[c?(p-1+f)%f:max(p-1,0)],h=a[p],k=a[c?(p+1)%f:min(p+1,f-1)],l=h.x-g.x,m=h.y-g.y,n=(l*l+m*m)**.5;g=k.x-h.x;const q=k.y-h.y;k=(g*g+q*q)**.5;1e-6>n&&1e-6>k||(m=1e-6<n?-m/n:0,n=1e-6<n?l/n:0,l=m+(1e-6<k?-q/k:0),g=n+(1e-6<k?g/k:0),k=(l*l+g*g)**.5,1e-6>k?(l=m,g=n):(l/=k,g/=k,m=m*l+n*g,1e-6<m&&(m=min(1/m,b),l*=m,g*=m)),m=vec2(h.x-l*d,h.y-g*d),h=vec2(h.x+l*d,h.y+g*d),e.push(m),e.push(h))}1<e.length&&c&&(e.push(e[0]),e.push(e[1]));return e}function glPolyStrip(a){if(3>a.length)return[];const b=(r,t,u)=>(t.x-r.x)*(u.y-r.y)-(t.y-r.y)*(u.x-r.x);0>(r=>{let t=0;for(let u=r.length;u--;)t+=r[u].cross(r[(u+1)%r.length]);return t})(a)&&(a=a.reverse());var c=(r,t,u,w)=>{const x=b(t,u,r);u=b(u,w,r);r=b(w,t,r);return!((-1e-9>x?1:0)+(-1e-9>u?1:0)+(-1e-9>r?1:0)&&(1e-9<x?1:0)+(1e-9<u?1:0)+(1e-9<r?1:0))},d=[];for(var e=0;e<a.length;++e)d[e]=e;e=[];let f=0;const g=a.length**2+100;for(;3<d.length&&f++<g;){var h=!1;for(var k=0;k<d.length;k++){var l=d[(k+d.length-1)%d.length],m=d[k];const r=d[(k+1)%d.length],t=a[l],u=a[m],w=a[r];if(1e-9>b(t,u,w))continue;let x=!1;for(let B=0;B<d.length;B++){const z=d[B];if(z!==l&&z!==m&&z!==r&&(x=c(a[z],t,u,w)))break}if(!x){e.push([l,m,r]);d.splice(k,1);h=!0;break}}if(!h){h=-1;k=Infinity;for(l=0;l<d.length;l++)m=abs(b(a[d[(l+d.length-1)%d.length]],a[d[l]],a[d[(l+1)%d.length]])),m<k&&(k=m,h=l);if(0>h)break;e.push([d[(h+d.length-1)%d.length],d[h],d[(h+1)%d.length]]);d.splice(h,1)}}3===d.length&&e.push([d[0],d[1],d[2]]);if(!e.length)return[];c=[];let[n,p,q]=e[0];c.push(a[n],a[p],a[q]);for(d=1;d<e.length;d++){const[r,t,u]=e[d];c.push(a[q],a[r]);c.push(a[r],a[t],a[u]);q=u}return c}let newgrounds;class NewgroundsMedal extends Medal{constructor(a,b,c,d,e){super(a,b,c,d,e)}unlock(){super.unlock();newgrounds&&newgrounds.unlockMedal(this.id)}}class NewgroundsPlugin{constructor(a,b,c){ASSERT(!newgrounds,"there can only be one newgrounds object");ASSERT(!b||c,"must provide cryptojs if there is a cipher");newgrounds=this;this.app_id=a;this.cipher=b;this.cryptoJS=c;this.host=location?location.hostname:"";if(this.session_id=new URL(location.href).searchParams.get("ngio_session_id")){this.medals=(a=this.call("Medal.getList"))?a.result.data.medals:[];debugMedals&&LOG(this.medals);for(var d of this.medals)if(a=medals[d.id])a.image=new Image,a.image.src=d.icon,a.name=d.name,a.description=d.description,a.unlocked=d.unlocked,a.difficulty=d.difficulty,a.value=d.value,a.value&&(a.description+=` (${a.value})`);this.scoreboards=(d=this.call("ScoreBoard.getBoards"))?d.result.data.scoreboards:[];debugMedals&&LOG(this.scoreboards);setInterval(()=>this.call("Gateway.ping",0,!0),6e4)}}unlockMedal(a){return this.call("Medal.unlock",{id:a},!0)}postScore(a,b){return this.call("ScoreBoard.postScore",{id:a,value:b},!0)}getScores(a,b,c=0,d=0,e=10){return this.call("ScoreBoard.getScores",{id:a,user:b,social:c,skip:d,limit:e})}logView(){return this.call("App.logView",{host:this.host},!0)}call(a,b,c=!1){a={component:a,parameters:b};if(this.cipher){b=this.cryptoJS;var d=b.enc.Base64.parse(this.cipher);const e=b.lib.WordArray.random(16);d=b.AES.encrypt(JSON.stringify(a),d,{iv:e});a.secure=b.enc.Base64.stringify(e.concat(d.ciphertext));a.parameters=0}b={app_id:this.app_id,session_id:this.session_id,call:a};a=new FormData;a.append("input",JSON.stringify(b));b=new XMLHttpRequest;b.open("POST","https://newgrounds.io/gateway_v3.php",!debugMedals&&c);try{b.send(a)}catch(e){debugMedals&&LOG("newgrounds call failed",e);return}debugMedals&&LOG(b.responseText);return b.responseText&&JSON.parse(b.responseText)}}let postProcess;class PostProcessPlugin{constructor(a,b=!1,c=!1){function d(){if(!headlessMode)if(glEnable){postProcess.texture=glCreateTexture();postProcess.shader=glCreateProgram("#version 300 es\nprecision highp float;in vec2 p;void main(){gl_Position=vec4(p+p-1.,1,1);}","#version 300 es\nprecision highp float;uniform sampler2D iChannel0;uniform vec3 iResolution;uniform float iTime;out vec4 c;\n"+a+"\nvoid main(){mainImage(c,gl_FragCoord.xy);c.a=1.;}");postProcess.vao=glContext.createVertexArray();glContext.bindVertexArray(postProcess.vao);glContext.bindBuffer(glContext.ARRAY_BUFFER,glGeometryBuffer);var e=glContext.getAttribLocation(postProcess.shader,"p");glContext.enableVertexAttribArray(e);glContext.vertexAttribPointer(e,2,glContext.FLOAT,!1,8,0)}else console.warn("PostProcessPlugin: WebGL not enabled!")}ASSERT(!postProcess,"Post process already initialized");ASSERT(!(b&&c),"Post process cannot both include main canvas and use feedback texture");postProcess=this;a||="void mainImage(out vec4 c,vec2 p){c=texture(iChannel0,p/iResolution.xy);}";this.vao=this.texture=this.shader=void 0;d();engineAddPlugin(void 0,function(){!headlessMode&&glEnable&&(glFlush(),glContext.useProgram(postProcess.shader),glContext.bindVertexArray(postProcess.vao),glContext.pixelStorei(glContext.UNPACK_FLIP_Y_WEBGL,!0),glContext.disable(glContext.BLEND),glContext.activeTexture(glContext.TEXTURE0),glContext.bindTexture(glContext.TEXTURE_2D,postProcess.texture),b?(workCanvas.width=mainCanvasSize.x,workCanvas.height=mainCanvasSize.y,glCopyToContext(workContext),workContext.drawImage(mainCanvas,0,0),mainCanvas.width|=0,glContext.texImage2D(glContext.TEXTURE_2D,0,glContext.RGBA,glContext.RGBA,glContext.UNSIGNED_BYTE,workCanvas)):c||glContext.texImage2D(glContext.TEXTURE_2D,0,glContext.RGBA,glContext.RGBA,glContext.UNSIGNED_BYTE,glCanvas),glContext.uniform1i(glContext.getUniformLocation(postProcess.shader,"iChannel0"),0),glContext.uniform1f(glContext.getUniformLocation(postProcess.shader,"iTime"),time),glContext.uniform3f(glContext.getUniformLocation(postProcess.shader,"iResolution"),mainCanvas.width,mainCanvas.height,1),glContext.drawArrays(glContext.TRIANGLE_STRIP,0,4),c&&glContext.texImage2D(glContext.TEXTURE_2D,0,glContext.RGBA,glContext.RGBA,glContext.UNSIGNED_BYTE,glCanvas),glSetInstancedMode(!0))},function(){postProcess.shader=void 0;postProcess.texture=void 0;LOG("PostProcessPlugin: WebGL context lost")},function(){d();LOG("PostProcessPlugin: WebGL context restored")})}}class ZzFXMusic extends Sound{constructor(a){super(void 0);soundEnable&&!headlessMode&&(this.randomness=0,this.sampleChannels=zzfxM(...a),this.sampleRate=audioDefaultSampleRate)}playMusic(a=1,b=!0){return super.play(void 0,a,1,0,b)}}function zzfxM(a,b,c,d=125){let e,f,g,h,k,l,m,n,p,q,r,t,u,w=0,x,B=[],z=[],v=[],y=0,D=0,G=1,J={},H=audioDefaultSampleRate/d*60>>2;for(;G;y++)B=[G=n=t=0],c.forEach((E,K)=>{m=b[E][y]||[0,0,0];G|=b[E][y]&&1;x=t+(b[E][0].length-2-(n?0:1))*H;u=K===c.length-1;e=2;for(g=t;e<m.length+u;n=++e){k=m[e];p=e===m.length+u-1&&u||q!==(m[0]||0)||k|0;for(f=0;f<H&&n;f++>H-99&&p&&1>r?r+=1/99:0)l=(1-r)*B[w++]/2||0,z[g]=(z[g]||0)-l*D+l,v[g]=(v[g++]||0)+l*D+l;k&&(r=k%1,D=m[1]||0,k|=0)&&(B=J[[q=m[w=0]||0,k]]=J[[q,k]]||(h=[...a[q]],h[2]=(h[2]||220)*2**(k/12-1),0<k?zzfxG(...h):[]))}t=x});return[z,v]}let uiSystem,uiDebug=0;function uiSetDebug(a){uiDebug="boolean"===typeof a?a?1:0:a}class UISystemPlugin{constructor(a=mainContext){function b(c){c.parent&&(c.pos.x=c.localPos.x+c.parent.pos.x,c.pos.y=c.localPos.y+c.parent.pos.y)}ASSERT(!uiSystem,"UI system already initialized");uiSystem=this;this.activateOnPress=!1;this.defaultColor=WHITE;this.defaultTextColor=this.defaultLineColor=BLACK;this.defaultButtonColor=hsl(0,0,.7);this.defaultHoverColor=hsl(0,0,.9);this.defaultDisabledColor=hsl(0,0,.3);this.defaultGradientColor=void 0;this.defaultLineWidth=4;this.defaultCornerRadius=0;this.defaultTextFitScale=.8;this.defaultFont=fontDefault;this.defaultSoundClick=this.defaultSoundRelease=this.defaultSoundPress=void 0;this.defaultShadowColor=CLEAR_BLACK;this.defaultShadowBlur=5;this.defaultShadowOffset=vec2(5);this.nativeHeight=0;this.navigationObject=void 0;this.navigationTimer=new Timer(void 0,!0);this.navigationDelay=.2;this.navigationDirection=1;this.navigationMode=!1;this.uiObjects=[];this.uiContext=a;this.keyInputObject=this.confirmDialog=this.lastHoverObject=this.hoverObject=this.activeObject=void 0;engineAddPlugin(function(){function c(g){if(g.visible){b(g);for(let h=g.children.length;h--;)c(g.children[h]);g.update()}}uiSystem.activeObject&&!uiSystem.activeObject.visible&&(uiSystem.activeObject=void 0);uiSystem.lastHoverObject=uiSystem.hoverObject;uiSystem.hoverObject=void 0;mouseWasPressed(0)&&(uiSystem.navigationMode=!1,uiSystem.navigationObject=void 0);uiSystem.keyInputObject&&(uiSystem.activeObject=uiSystem.keyInputObject,uiSystem.hoverObject=uiSystem.keyInputObject,uiSystem.navigationMode=!1,uiSystem.navigationObject=void 0);var d=uiSystem.getNavigableObjects();if(!d.length)uiSystem.navigationObject=void 0;else if(!uiSystem.keyInputObject){d.includes(uiSystem.navigationObject)||(uiSystem.navigationObject=void 0);isTouchDevice||!uiSystem.navigationMode||uiSystem.navigationObject||(uiSystem.navigationObject=d.find(g=>g.navigationAutoSelect));if(!uiSystem.navigationTimer.active()){var e=sign(uiSystem.getNavigationDirection());if(e){if(uiSystem.navigationObject){var f=d.indexOf(uiSystem.navigationObject);e=mod(f+e,d.length);f=d[e]}else(f=d.find(g=>g.navigationAutoSelect))||(f=d[0<e?0:d.length-1]);uiSystem.navigationObject!==f&&(uiSystem.navigationMode=!0,uiSystem.hoverObject=void 0,uiSystem.navigationObject=f,uiSystem.navigationTimer.set(uiSystem.navigationDelay),f.soundPress&&f.soundPress.play())}}uiSystem.navigationObject&&uiSystem.getNavigationWasPressed()&&uiSystem.navigationObject.navigatePressed()}for(d=uiSystem.uiObjects.length;d--;)e=uiSystem.uiObjects[d],e.parent||c(e);uiSystem.uiObjects=uiSystem.uiObjects.filter(g=>!g.destroyed)},function(){function c(e){if(e.visible){b(e);e.render();for(const f of e.children)c(f)}}const d=uiSystem.uiContext;d.save();if(uiSystem.nativeHeight){const e=mainCanvasSize.y/uiSystem.nativeHeight;d.translate(-e*mainCanvasSize.x/2,0);d.scale(e,e);d.translate(mainCanvasSize.x/2/e,0)}uiSystem.uiObjects.forEach(e=>e.parent||c(e));if(0<uiDebug){function e(f,g=!0){g&&=!!f.visible;b(f);f.renderDebug(g);for(const h of f.children)e(h,g)}uiSystem.uiObjects.forEach(f=>f.parent||e(f))}d.restore()});document.addEventListener("keydown",function(c){uiSystem.keyInputObject?.onKeyDown(c)})}drawRect(a,b,c=WHITE,d=0,e=BLACK,f=0,g,h=BLACK,k=0,l=vec2()){ASSERT(isVector2(a),"pos must be a vec2");ASSERT(isVector2(b),"size must be a vec2");ASSERT(isColor(c),"color must be a color");ASSERT(isNumber(d),"lineWidth must be a number");ASSERT(isColor(e),"lineColor must be a color");ASSERT(isNumber(f),"cornerRadius must be a number");const m=uiSystem.uiContext;if(g){const n=m.createLinearGradient(a.x,a.y-b.y/2,a.x,a.y+b.y/2);c=c.toString();n.addColorStop(0,c);n.addColorStop(.5,g.toString());n.addColorStop(1,c);m.fillStyle=n}else m.fillStyle=c.toString();(k||l.x||l.y)&&0<h.a&&(m.shadowColor=h.toString(),m.shadowBlur=k,m.shadowOffsetX=l.x,m.shadowOffsetY=l.y);m.beginPath();f&&m.roundRect?m.roundRect(a.x-b.x/2,a.y-b.y/2,b.x,b.y,f):m.rect(a.x-b.x/2,a.y-b.y/2,b.x,b.y);m.fill();m.shadowColor="#0000";d&&0<e.a&&(m.strokeStyle=e.toString(),m.lineWidth=d,m.stroke())}drawLine(a,b,c=uiSystem.defaultLineWidth,d=uiSystem.defaultLineColor){ASSERT(isVector2(a),"posA must be a vec2");ASSERT(isVector2(b),"posB must be a vec2");ASSERT(isNumber(c),"lineWidth must be a number");ASSERT(isColor(d),"lineColor must be a color");const e=uiSystem.uiContext;e.strokeStyle=d.toString();e.lineWidth=c;e.beginPath();e.lineTo(a.x,a.y);e.lineTo(b.x,b.y);e.stroke()}drawTile(a,b,c,d=uiSystem.defaultColor,e=0,f=!1,g=BLACK,h=0,k=vec2()){const l=uiSystem.uiContext;(h||k.x||k.y)&&0<g.a&&(l.shadowColor=g.toString(),l.shadowBlur=h,l.shadowOffsetX=k.x,l.shadowOffsetY=k.y);drawTile(a,b,c,d,e,f,CLEAR_BLACK,!1,!0,l);l.shadowColor="#0000"}drawText(a,b,c,d=uiSystem.defaultColor,e=uiSystem.defaultLineWidth,f=uiSystem.defaultLineColor,g="center",h=uiSystem.defaultFont,k="",l=!0,m,n=BLACK,p=0,q=vec2()){const r=uiSystem.uiContext;0<n.a&&(m&&drawTextScreen(a,b.add(m),c.y,n,e,f,g,h,k,l?c.x:void 0,0,r),p||q.x||q.y)&&(r.shadowColor=n.toString(),r.shadowBlur=p,r.shadowOffsetX=q.x,r.shadowOffsetY=q.y);drawTextScreen(a,b,c.y,d,e,f,g,h,k,l?c.x:void 0,0,r);r.shadowColor="#0000"}setupDragAndDrop(a,b,c,d){function e(f,g){document.addEventListener(g,function(h){h.preventDefault();f&&f(h)})}e(a,"drop");e(b,"dragenter");e(c,"dragleave");e(d,"dragover")}screenToNative(a){if(!uiSystem.nativeHeight)return a;const b=mainCanvasSize.y/uiSystem.nativeHeight,c=1/b;a=a.copy();a.x+=b*mainCanvasSize.x/2;a.x*=c;a.y*=c;a.x-=c*mainCanvasSize.x/2;return a}destroyObjects(){for(const a of this.uiObjects)a.parent||a.destroy();this.uiObjects=this.uiObjects.filter(a=>!a.destroyed);this.keyInputObject=this.lastHoverObject=this.hoverObject=this.activeObject=void 0}getNavigableObjects(){function a(c){if(c.visible&&!c.disabled){c.isInteractive()&&void 0!==c.navigationIndex&&b.push(c);for(let d=c.children.length;d--;)a(c.children[d])}}let b=[];for(let c=uiSystem.uiObjects.length;c--;){const d=uiSystem.uiObjects[c];uiSystem.confirmDialog&&d!==uiSystem.confirmDialog||d.parent||a(d)}b.sort((c,d)=>c.navigationIndex-d.navigationIndex);return b}getNavigationDirection(){const a=1===uiSystem.navigationDirection;var b=2===uiSystem.navigationDirection;if(isUsingGamepad){const c=gamepadStick(0,gamepadPrimary),d=gamepadDpad(gamepadPrimary);return b?-(c.y||d.y)||c.x||d.x:a?-(c.y||d.y):c.x||d.x}if(b)return keyIsDown("ArrowUp")||keyIsDown("ArrowLeft")?-1:keyIsDown("ArrowDown")||keyIsDown("ArrowRight")?1:0;b=a?"ArrowDown":"ArrowRight";return keyIsDown(a?"ArrowUp":"ArrowLeft")?-1:keyIsDown(b)?1:0}getNavigationOtherDirection(){if(2===uiSystem.navigationDirection)return 0;const a=1===uiSystem.navigationDirection;if(isUsingGamepad){var b=gamepadStick(0,gamepadPrimary);const c=gamepadDpad(gamepadPrimary);return a?b.x||c.x:b.y||c.y}b=a?"ArrowRight":"ArrowDown";return keyIsDown(a?"ArrowLeft":"ArrowUp")?-1:keyIsDown(b)?1:0}getNavigationWasPressed(){return isUsingGamepad?gamepadWasPressed(0,gamepadPrimary):keyWasPressed("Space")||keyWasPressed("Enter")}showConfirmDialog(a="Are you sure?",b,c,d=vec2(500,250),e="Escape"){function f(){ASSERT(uiSystem.confirmDialog===h);h.destroy();uiSystem.confirmDialog=void 0;uiSystem.navigationDirection=g;inputClear()}ASSERT(!uiSystem.confirmDialog);const g=uiSystem.navigationDirection;uiSystem.navigationDirection=2;const h=new UIObject(vec2(),d);uiSystem.confirmDialog=h;h.onRender=()=>{h.pos=uiSystem.screenToNative(mainCanvasSize.scale(.5));const k=hsl(0,0,0,.7);uiSystem.drawRect(vec2(),vec2(1e9),k)};h.onUpdate=()=>{keyWasPressed(e)&&f()};h.isMouseOverlapping=()=>!0;a=new UIText(vec2(0,-50),vec2(d.x-50,70),a);h.addChild(a);a=new UIButton(vec2(-80,50),vec2(120,70),"Yes");a.textHeight=40;a.navigationIndex=1;a.hoverColor=hsl(0,1,.5);a.onClick=()=>{f();b&&b()};h.addChild(a);a=new UIButton(vec2(80,50),vec2(120,70),"No");a.textHeight=40;a.navigationIndex=2;a.navigationAutoSelect=!0;a.onClick=()=>{f();c&&c()};h.addChild(a)}}class UIObject{constructor(a=vec2(),b=vec2()){ASSERT(isVector2(a),"ui object pos must be a vec2");ASSERT(isVector2(b),"ui object size must be a vec2");this.localPos=a.copy();this.pos=a.copy();this.size=b.copy();this.color=uiSystem.defaultColor.copy();this.text=this.activeColor=void 0;this.disabledColor=uiSystem.defaultDisabledColor.copy();this.disabled=!1;this.textColor=uiSystem.defaultTextColor.copy();this.hoverColor=uiSystem.defaultHoverColor.copy();this.lineColor=uiSystem.defaultLineColor.copy();this.gradientColor=uiSystem.defaultGradientColor?uiSystem.defaultGradientColor.copy():void 0;this.lineWidth=uiSystem.defaultLineWidth;this.cornerRadius=uiSystem.defaultCornerRadius;this.font=uiSystem.defaultFont;this.textHeight=this.textWidth=this.fontStyle=void 0;this.textFitScale=uiSystem.defaultTextFitScale;this.textShadow=void 0;this.textLineColor=uiSystem.defaultLineColor.copy();this.textLineWidth=0;this.visible=!0;this.children=[];this.parent=void 0;this.extraTouchSize=0;this.soundPress=uiSystem.defaultSoundPress;this.soundRelease=uiSystem.defaultSoundRelease;this.soundClick=uiSystem.defaultSoundClick;this.dragActivate=this.interactive=!1;this.canBeHover=!0;this.shadowColor=uiSystem.defaultShadowColor?.copy();this.shadowBlur=uiSystem.defaultShadowBlur;this.shadowOffset=uiSystem.defaultShadowOffset?.copy();this.navigationIndex=void 0;this.navigationAutoSelect=!1;uiSystem.uiObjects.push(this)}addChild(a){ASSERT(!a.parent&&!this.children.includes(a));this.children.push(a);a.parent=this;return a}removeChild(a){ASSERT(a.parent===this&&this.children.includes(a));this.children.splice(this.children.indexOf(a),1);a.parent=void 0}destroy(){if(!this.destroyed){this.destroyed=1;this.parent?.removeChild(this);for(const a of this.children)a.parent=void 0,a.destroy()}}isMouseOverlapping(){if(!mouseInWindow)return!1;const a=isTouchDevice?this.size.add(vec2(this.extraTouchSize||0)):this.size,b=uiSystem.screenToNative(mousePosScreen);return isOverlapping(this.pos,a,b)}update(){this.onUpdate();this.disabled&&(this===uiSystem.activeObject&&(uiSystem.activeObject=void 0),this===uiSystem.keyInputObject&&(uiSystem.keyInputObject=void 0));if(!uiSystem.keyInputObject){var a=uiSystem.lastHoverObject===this,b=this.isActiveObject(),c=mouseIsDown(0),d=this.dragActivate?c:mouseWasPressed(0);this.canBeHover&&!uiSystem.navigationMode&&(d||b||!c&&!isTouchDevice)&&!uiSystem.hoverObject&&this.isMouseOverlapping()&&(uiSystem.hoverObject=this);if(this.isHoverObject()){if(!this.disabled){if(d&&this.interactive){if(!this.dragActivate||!a||mouseWasPressed(0))this.onPress();this.soundPress&&this.soundPress.play();if(uiSystem.activeObject&&!b)uiSystem.activeObject.onRelease();uiSystem.activeObject=this;uiSystem.activateOnPress&&this.click(!this.soundPress)}uiSystem.activateOnPress||!c&&this.isActiveObject()&&this.interactive&&this.click()}d&&inputClearKey(0,0,0,1,0)}b&&(!c||this.dragActivate&&!this.isHoverObject())&&(this.onRelease(),this.soundRelease&&this.soundRelease.play(),uiSystem.activeObject=void 0);this.isHoverObject()!==a&&(this.isHoverObject()?this.onEnter():this.onLeave())}}render(){this.onRender();if(this.size.x&&this.size.y){var a=this.isNavigationObject(),b=a?this.color:this.interactive&&this.isActiveObject()&&!this.disabled?this.color:this.lineColor,c=a?this.hoverColor:this.disabled?this.disabledColor:this.interactive?this.isActiveObject()?this.activeColor||this.hoverColor:this.isHoverObject()?this.hoverColor:this.color:this.color;uiSystem.drawRect(this.pos,this.size,c,this.lineWidth*(a?1.5:1),b,this.cornerRadius,this.gradientColor,this.shadowColor,this.shadowBlur,this.shadowOffset)}}getTextSize(){return vec2(this.textWidth||this.textFitScale*this.size.x,this.textHeight||this.textFitScale*this.size.y)}navigatePressed(){this.click()}isHoverObject(){return uiSystem.hoverObject===this}isActiveObject(){return uiSystem.activeObject===this}isNavigationObject(){return uiSystem.navigationObject===this}isKeyInputObject(){return uiSystem.keyInputObject===this}isInteractive(){return this.interactive&&this.visible&&!this.disabled}toString(){if(debug){var a="type = "+this.constructor.name;this.text&&(a+="\ntext = "+this.text);if(this.pos.x||this.pos.y)a+="\npos = "+this.pos;if(this.localPos.x||this.localPos.y)a+="localPos = "+this.localPos;if(this.size.x||this.size.y)a+="\nsize = "+this.size;this.color&&(a+="\ncolor = "+this.color);return a}}renderDebug(a=!0){a=a?this.isHoverObject()?YELLOW:this.disabled?PURPLE:this.interactive?RED:BLUE:GREEN;uiSystem.drawRect(this.pos,this.size,CLEAR_BLACK,4,a)}click(a){this.onClick();a&&this.soundClick&&this.soundClick.play()}onUpdate(){}onRender(){}onEnter(){}onLeave(){}onPress(){}onRelease(){}onClick(){}onChange(){}}class UIText extends UIObject{constructor(a,b,c="",d="center",e=uiSystem.defaultFont){super(a,b);ASSERT(isString(c),"ui text must be a string");ASSERT(["left","center","right"].includes(d),"ui text align must be left, center, or right");ASSERT(isString(e),"ui text font must be a string");this.text=c;this.align=d;this.font=e;this.canBeHover=!1;this.shadowColor=this.color=CLEAR_BLACK;this.gradientColor=void 0;this.lineWidth=0;this.textFitScale=1}render(){super.render();const a=this.getTextSize();uiSystem.drawText(this.text,this.pos,a,this.textColor,this.textLineWidth,this.textLineColor,this.align,this.font,this.fontStyle,!0,this.textShadow,this.shadowColor,this.shadowBlur,this.shadowOffset)}}class UITextInput extends UIObject{constructor(a,b,c=""){super(a,b);ASSERT(isString(c),"ui text must be a string");this.maxLength=0;this.text=c;this.canBeHover=this.interactive=!0}click(){uiSystem.keyInputObject=this;this.onClick()}stopEditing(){this.isKeyInputObject()&&(this.soundRelease&&this.soundRelease.play(),uiSystem.activeObject=void 0,uiSystem.keyInputObject=void 0,this.onChange())}onKeyDown(a){const b=a.code;a=a.key;"Backspace"===b?this.text=this.text.slice(0,-1):"Enter"===b||"Escape"===b?this.stopEditing():1===a.length&&(!this.maxLength||this.text.length<this.maxLength)&&(this.text+=a)}update(){super.update();this.isKeyInputObject()&&(mouseWasPressed(0)&&!this.isMouseOverlapping()||gamepadWasPressed(0,gamepadPrimary))&&(this.stopEditing(),inputClearKey(0,0))}render(){super.render();const a=this.getTextSize();let b=this.text;this.isKeyInputObject()&&(b+=.5>timeReal%1?"█":"░");uiSystem.drawText(b,this.pos,a,this.textColor,this.textLineWidth,this.textLineColor,this.align,this.font,this.fontStyle,!0,this.textShadow)}}class UITile extends UIObject{constructor(a,b,c,d=WHITE,e=0,f=!1){super(a,b);ASSERT(c instanceof TileInfo,"ui tile tileInfo must be a TileInfo");ASSERT(isColor(d),"ui tile color must be a color");ASSERT(isNumber(e),"ui tile angle must be a number");this.tileInfo=c;this.angle=e;this.mirror=f;this.color=d.copy();this.shadowColor=CLEAR_BLACK}render(){uiSystem.drawTile(this.pos,this.size,this.tileInfo,this.color,this.angle,this.mirror,this.shadowColor,this.shadowBlur,this.shadowOffset)}}class UIButton extends UIObject{constructor(a,b,c="",d=uiSystem.defaultButtonColor){super(a,b);ASSERT(isString(c),"ui button must be a string");ASSERT(isColor(d),"ui button color must be a color");this.textOffset=vec2();this.text=c;this.color=d.copy();this.interactive=!0}render(){super.render();const a=this.getTextSize();uiSystem.drawText(this.text,this.pos.add(this.textOffset),a,this.textColor,this.textLineWidth,this.textLineColor,this.align,this.font,this.fontStyle,!0,this.textShadow)}}class UICheckbox extends UIObject{constructor(a,b,c=!1,d="",e=uiSystem.defaultButtonColor){super(a,b);ASSERT(isString(d),"ui checkbox must be a string");ASSERT(isColor(e),"ui checkbox color must be a color");this.checked=c;this.text=d;this.color=e.copy();this.interactive=!0}click(){this.checked=!this.checked;this.onClick();this.onChange()}render(){super.render();if(this.checked){var a=this.cornerRadius/min(this.size.x,this.size.y)*2;a=lerp(1,2**.5/2,a)/2;a=this.size.scale(a);uiSystem.drawLine(this.pos.add(a.multiply(vec2(-1))),this.pos.add(a.multiply(vec2(1))),this.lineWidth,this.lineColor);uiSystem.drawLine(this.pos.add(a.multiply(vec2(-1,1))),this.pos.add(a.multiply(vec2(1,-1))),this.lineWidth,this.lineColor)}a=this.getTextSize();const b=this.pos.add(vec2(this.size.x,0));uiSystem.drawText(this.text,b,a,this.textColor,this.textLineWidth,this.textLineColor,"left",this.font,this.fontStyle,!1,this.textShadow)}}class UISlider extends UIObject{constructor(a,b,c=.5,d="",e=uiSystem.defaultButtonColor,f=WHITE){super(a,b);ASSERT(isNumber(c),"ui slider value must be a number");ASSERT(isString(d),"ui slider must be a string");ASSERT(isColor(e),"ui slider color must be a color");ASSERT(isColor(f),"ui slider handleColor must be a color");this.value=c;this.handleColor=f.copy();this.fillMode=!1;this.text=d;this.color=e.copy();this.interactive=!0}update(){super.update();if(this.interactive){var a=this.value;if(this.isActiveObject()){var b=this.size.x>this.size.y,c=b?this.pos.x:this.pos.y,d=(b?this.size.x:this.size.y)-(b?this.size.y:this.size.x);const e=c-d/2;c+=d/2;d=uiSystem.screenToNative(mousePosScreen);this.value=b?percent(d.x,e,c):percent(d.y,c,e)}else this.isNavigationObject()&&(b=uiSystem.getNavigationOtherDirection(),uiSystem.navigationTimer.active()||(this.value=clamp(this.value+.01*b)));this.value===a||this.onChange()}}render(){super.render();var a=this.size.x>this.size.y,b=a?this.size.x:this.size.y,c=a?this.size.y:this.size.x;if(this.fillMode){c=min(c,2*this.cornerRadius);c=lerp(c,b,this.value);b=(c-b)*(a?.5:-.5);b=this.pos.add(a?vec2(b,0):vec2(0,b));var d=this.disabled?this.disabledColor:this.handleColor;a=a?vec2(c,this.size.y):vec2(this.size.x,c);uiSystem.drawRect(b,a,d,this.lineWidth,this.lineColor,this.cornerRadius,this.gradientColor)}else d=clamp(a?this.value:1-this.value),b=(b-c)*(d-.5),a=this.pos.add(a?vec2(b,0):vec2(0,b)),b=this.disabled?this.disabledColor:this.handleColor,c=vec2(c),uiSystem.drawRect(a,c,b,this.lineWidth,this.lineColor,this.cornerRadius,this.gradientColor);a=this.getTextSize();uiSystem.drawText(this.text,this.pos,a,this.textColor,this.textLineWidth,this.textLineColor,this.align,this.font,this.fontStyle,!0,this.textShadow)}navigatePressed(){this.value=this.value?0:1;this.onRelease();super.navigatePressed()}}class UIVideo extends UIObject{constructor(a,b,c,d=!1,e=!1,f=1){super(a,b||vec2());ASSERT(isString(c),"video src must be a string");ASSERT(isNumber(f),"video volume must be a number");this.color=BLACK;this.cornerRadius=0;this.volume=f;this.video=document.createElement("video");this.video.loop=e;this.video.volume=clamp(f*soundVolume);this.video.muted=!soundEnable;this.video.style.display="none";this.video.src=c;document.body.appendChild(this.video);d&&this.play()}async play(){try{await this.video.play()}catch(a){}}pause(){this.video.pause()}stop(){this.video.pause();this.video.currentTime=0}isLoading(){return this.video.readyState<this.video.HAVE_CURRENT_DATA}isPaused(){return this.video.paused}isPlaying(){return!this.isPaused()&&!this.hasEnded()&&!this.isLoading()}hasEnded(){return this.video.ended}setVolume(a){this.volume=a;this.video.volume=clamp(a*soundVolume)}setPlaybackRate(a){this.video.playbackRate=a}getCurrentTime(){return this.video.currentTime||0}getDuration(){return this.video.duration||0}getVideoSize(){return vec2(this.video.videoWidth,this.video.videoHeight)}setTime(a){this.video.currentTime=clamp(a,0,this.getDuration())}update(){super.update();this.video.volume=clamp(this.volume*soundVolume)}render(){super.render();if(!this.isLoading()){var a=uiSystem.uiContext,b=this.size;a.save();a.translate(this.pos.x,this.pos.y);a.drawImage(this.video,-b.x/2,-b.y/2,b.x,b.y);a.restore()}}destroy(){this.destroyed||(this.video.pause(),this.video.remove(),super.destroy())}}let box2d,box2dDebug=!1;function box2dSetDebug(a){box2dDebug=a}class Box2dObject extends EngineObject{constructor(a=vec2(),b=vec2(),c,d=0,e,f=box2d.bodyTypeDynamic,g=0){super(a,b,c,d,e,g);b=new box2d.instance.b2BodyDef;b.set_type(f);b.set_position(box2d.vec2dTo(a));b.set_angle(-d);this.body=box2d.world.CreateBody(b);this.lineColor=BLACK;this.edgeLists=[];this.edgeLoops=[];this.body.object=this;box2d.objects.push(this)}destroy(){this.destroyed||(ASSERT(this.body,"Box2dObject has no body to destroy"),box2d.world.DestroyBody(this.body),super.destroy())}updatePhysics(){}render(){this.tileInfo?super.render():this.drawFixtures(this.color,this.lineColor,this.lineWidth)}renderDebugInfo(){var a=!this.getIsAwake();const b=this.getBodyType()===box2d.bodyTypeStatic;a=rgb(a?1:0,a?1:0,b?1:0,.5);this.drawFixtures(a)}drawFixtures(a=WHITE,b=BLACK,c=.1,d,e){this.getFixtureList().forEach(f=>{box2d.castShapeObject(f.GetShape()).GetType()!==box2d.instance.b2Shape.e_edge&&box2d.drawFixture(f,this.pos,this.angle,a,b,c,d,e)});this.edgeLists.forEach(f=>drawLineList(f,c,b,!1,this.pos,this.angle));this.edgeLoops.forEach(f=>drawLineList(f,c,b,!0,this.pos,this.angle))}beginContact(a){}endContact(a){}addShape(a,b=1,c=.2,d=0,e=!1){ASSERT(isNumber(b),"density must be a number");ASSERT(isNumber(c),"friction must be a number");ASSERT(isNumber(d),"restitution must be a number");const f=new box2d.instance.b2FixtureDef;f.set_shape(a);f.set_density(b);f.set_friction(c);f.set_restitution(d);f.set_isSensor(e);return this.body.CreateFixture(f)}addBox(a=vec2(1),b=vec2(),c=0,d,e,f,g){ASSERT(isVector2(a),"size must be a Vector2");ASSERT(0<a.x&&0<a.y,"size must be positive");ASSERT(isVector2(b),"offset must be a Vector2");ASSERT(isNumber(c),"angle must be a number");const h=new box2d.instance.b2PolygonShape;h.SetAsBox(a.x/2,a.y/2,box2d.vec2dTo(b),c);return this.addShape(h,d,e,f,g)}addPoly(a,b,c,d,e){ASSERT(isArray(a),"points must be an array");ASSERT(3<=a.length&&8>=a.length);const f=new box2d.instance.b2PolygonShape;var g=box2d.instance._malloc(8*a.length);for(let h=0,k=0;h<a.length;++h)box2d.instance.HEAPF32[g+k>>2]=a[h].x,k+=4,box2d.instance.HEAPF32[g+k>>2]=a[h].y,k+=4;g=box2d.instance.wrapPointer(g,box2d.instance.b2Vec2);f.Set(g,a.length);return this.addShape(f,b,c,d,e)}addRegularPoly(a=1,b=8,c,d,e,f){ASSERT(isNumber(a)&&0<a,"diameter must be a positive number");ASSERT(isNumber(b)&&2<b,"sides must be a positive number greater than 2");const g=[];a/=2;for(let h=b;h--;)g.push(vec2(a,0).rotate((h+.5)/b*PI*2));return this.addPoly(g,c,d,e,f)}addRandomPoly(a=1,b,c,d,e){ASSERT(isNumber(a)&&0<a,"diameter must be a positive number");const f=randInt(3,9),g=[];a/=2;for(let h=f;h--;)g.push(vec2(rand(a/2,1.5*a),0).rotate(h/f*PI*2));return this.addPoly(g,b,c,d,e)}addCircle(a=1,b=vec2(),c,d,e,f){ASSERT(isNumber(a)&&0<a,"diameter must be a positive number");ASSERT(isVector2(b),"offset must be a Vector2");const g=new box2d.instance.b2CircleShape;g.set_m_p(box2d.vec2dTo(b));g.set_m_radius(a/2);return this.addShape(g,c,d,e,f)}addEdge(a,b,c,d,e,f){ASSERT(isVector2(a),"point1 must be a Vector2");ASSERT(isVector2(b),"point2 must be a Vector2");const g=new box2d.instance.b2EdgeShape;g.Set(box2d.vec2dTo(a),box2d.vec2dTo(b));return this.addShape(g,c,d,e,f)}addEdgeList(a,b,c,d,e){ASSERT(isArray(a),"points must be an array");const f=[],g=[];for(let k=0;k<a.length-1;++k){var h=new box2d.instance.b2EdgeShape;a[k-1]&&h.set_m_vertex0(box2d.vec2dTo(a[k-1]));a[k+0]&&h.set_m_vertex1(box2d.vec2dTo(a[k+0]));a[k+1]&&h.set_m_vertex2(box2d.vec2dTo(a[k+1]));a[k+2]&&h.set_m_vertex3(box2d.vec2dTo(a[k+2]));h=this.addShape(h,b,c,d,e);f.push(h);g.push(a[k].copy())}g.push(a[a.length-1].copy());this.edgeLists.push(g);return f}addEdgeLoop(a,b,c,d,e){ASSERT(isArray(a),"points must be an array");const f=[],g=[];for(let k=0;k<a.length;++k){var h=new box2d.instance.b2EdgeShape;h.set_m_vertex0(box2d.vec2dTo(a[mod(k-1,a.length)]));h.set_m_vertex1(box2d.vec2dTo(a[mod(k+0,a.length)]));h.set_m_vertex2(box2d.vec2dTo(a[mod(k+1,a.length)]));h.set_m_vertex3(box2d.vec2dTo(a[mod(k+2,a.length)]));h=this.addShape(h,b,c,d,e);f.push(h);k<a.length&&g.push(a[k].copy())}this.edgeLoops.push(g);return f}destroyFixture(a){this.body.DestroyFixture(a)}destroyAllFixtures(){this.getFixtureList().forEach(a=>this.destroyFixture(a))}getCenterOfMass(){return box2d.vec2From(this.body.GetWorldCenter())}getLinearVelocity(){return box2d.vec2From(this.body.GetLinearVelocity())}getAngularVelocity(){return this.body.GetAngularVelocity()}getMass(){return this.body.GetMass()}getInertia(){return this.body.GetInertia()}getIsAwake(){return this.body.IsAwake()}getBodyType(){return this.body.GetType()}getSpeed(){return this.getLinearVelocity().length()}setTransform(a,b){this.pos=a;this.angle=b;this.body.SetTransform(box2d.vec2dTo(a),b)}setPosition(a){this.setTransform(a,this.body.GetAngle())}setAngle(a){this.setTransform(box2d.vec2From(this.body.GetPosition()),-a)}setLinearVelocity(a){this.body.SetLinearVelocity(box2d.vec2dTo(a))}setAngularVelocity(a){this.body.SetAngularVelocity(a)}setLinearDamping(a){this.body.SetLinearDamping(a)}setAngularDamping(a){this.body.SetAngularDamping(a)}setGravityScale(a=1){this.body.SetGravityScale(this.gravityScale=a)}setBullet(a=!0){this.body.SetBullet(a)}setAwake(a=!0){this.body.SetAwake(a)}setBodyType(a){this.body.SetType(a)}setSleepingAllowed(a=!0){this.body.SetSleepingAllowed(a)}setFixedRotation(a=!0){this.body.SetFixedRotation(a)}setCenterOfMass(a){this.setMassData(a)}setMass(a){this.setMassData(void 0,a)}setMomentOfInertia(a){this.setMassData(void 0,void 0,a)}resetMassData(){this.body.ResetMassData()}setMassData(a,b,c){const d=new box2d.instance.b2MassData;this.body.GetMassData(d);a&&d.set_center(box2d.vec2dTo(a));b&&d.set_mass(b);c&&d.set_I(c);this.body.SetMassData(d)}setFilterData(a=0,b=0,c=0){this.getFixtureList().forEach(d=>{d=d.GetFilterData();d.set_categoryBits(a);d.set_maskBits(65535&~b);d.set_groupIndex(c)})}setSensor(a=!0){this.getFixtureList().forEach(b=>b.SetSensor(a))}applyForce(a,b){b||=this.getCenterOfMass();this.setAwake();this.body.ApplyForce(box2d.vec2dTo(a),box2d.vec2dTo(b))}applyAcceleration(a,b){b||=this.getCenterOfMass();this.setAwake();this.body.ApplyLinearImpulse(box2d.vec2dTo(a),box2d.vec2dTo(b))}applyTorque(a){this.setAwake();this.body.ApplyTorque(a)}applyAngularAcceleration(a){this.setAwake();this.body.ApplyAngularImpulse(a)}hasFixtures(){return!box2d.isNull(this.body.GetFixtureList())}getFixtureList(){const a=[];for(let b=this.body.GetFixtureList();!box2d.isNull(b);)a.push(b),b=b.GetNext();return a}hasJoints(){return!box2d.isNull(this.body.GetJointList())}getJointList(){const a=[];for(let b=this.body.GetJointList();!box2d.isNull(b);)a.push(b),b=b.get_next();return a}}class Box2dStaticObject extends Box2dObject{constructor(a,b,c,d=0,e,f=0){super(a,b,c,d,e,box2d.bodyTypeStatic,f)}}class Box2dKinematicObject extends Box2dObject{constructor(a,b,c,d=0,e,f=0){super(a,b,c,d,e,box2d.bodyTypeKinematic,f)}}class Box2dTileLayer extends Box2dStaticObject{constructor(a){ASSERT(a instanceof TileCollisionLayer,"tileLayer must be a TileCollisionLayer");super(a.pos,a.size);this.tileLayer=a;this.addChild(a)}render(){}buildCollision(a=.2,b=0){this.destroyAllFixtures();this.pos=this.tileLayer.pos.copy();this.size=this.tileLayer.size.copy();const c=[],d=(k,l)=>k+l*this.size.x,e=(k,l)=>!c[d(k,l)]&&0<this.tileLayer.getCollisionData(vec2(k,l));for(let k=0;k<this.size.x;++k)for(let l=0;l<this.size.y;++l){if(!e(k,l))continue;var f=1;let m=1;for(var g=!0;e(k+f,l);)++f;for(;g;){for(var h=0;h<f;++h)if(!e(k+h,l+m)){g=!1;break}g&&++m}for(g=f;g--;)for(h=m;h--;)c[d(k+g,l+h)]=!0;g=vec2(f,m);f=vec2(k+f/2,l+m/2);this.addBox(g,f,0,0,a,b)}}}class Box2dRaycastResult{constructor(a,b,c,d){this.object=a.GetBody().object;this.fixture=a;this.point=b;this.normal=c;this.fraction=d}}class Box2dJoint{constructor(a){this.box2dJoint=box2d.castJointObject(box2d.world.CreateJoint(a))}destroy(){box2d.world.DestroyJoint(this.box2dJoint);this.box2dJoint=0}getObjectA(){return this.box2dJoint.GetBodyA().object}getObjectB(){return this.box2dJoint.GetBodyB().object}getAnchorA(){return box2d.vec2From(this.box2dJoint.GetAnchorA())}getAnchorB(){return box2d.vec2From(this.box2dJoint.GetAnchorB())}getReactionForce(a){return box2d.vec2From(this.box2dJoint.GetReactionForce(1/a))}getReactionTorque(a){return this.box2dJoint.GetReactionTorque(1/a)}getCollideConnected(){return this.box2dJoint.getCollideConnected()}isActive(){return this.box2dJoint.IsActive()}}class Box2dTargetJoint extends Box2dJoint{constructor(a,b,c){a.setAwake();const d=new box2d.instance.b2MouseJointDef;d.set_bodyA(b.body);d.set_bodyB(a.body);d.set_target(box2d.vec2dTo(c));d.set_maxForce(2e3*a.getMass());super(d)}setTarget(a){this.box2dJoint.SetTarget(box2d.vec2dTo(a))}getTarget(){return box2d.vec2From(this.box2dJoint.GetTarget())}setMaxForce(a){this.box2dJoint.SetMaxForce(a)}getMaxForce(){return this.box2dJoint.GetMaxForce()}setFrequency(a){this.box2dJoint.SetFrequency(a)}getFrequency(){return this.box2dJoint.GetFrequency()}}class Box2dDistanceJoint extends Box2dJoint{constructor(a,b,c,d,e=!1){c||=box2d.vec2From(a.body.GetPosition());d||=box2d.vec2From(b.body.GetPosition());const f=a.worldToLocal(c),g=b.worldToLocal(d),h=new box2d.instance.b2DistanceJointDef;h.set_bodyA(a.body);h.set_bodyB(b.body);h.set_localAnchorA(box2d.vec2dTo(f));h.set_localAnchorB(box2d.vec2dTo(g));h.set_length(c.distance(d));h.set_collideConnected(e);super(h)}getLocalAnchorA(){return box2d.vec2From(this.box2dJoint.GetLocalAnchorA())}getLocalAnchorB(){return box2d.vec2From(this.box2dJoint.GetLocalAnchorB())}setLength(a){this.box2dJoint.SetLength(a)}getLength(){return this.box2dJoint.GetLength()}setFrequency(a){this.box2dJoint.SetFrequency(a)}getFrequency(){return this.box2dJoint.GetFrequency()}setDampingRatio(a){this.box2dJoint.SetDampingRatio(a)}getDampingRatio(){return this.box2dJoint.GetDampingRatio()}}class Box2dPinJoint extends Box2dDistanceJoint{constructor(a,b,c=a.pos,d=!1){super(a,b,void 0,c,d)}}class Box2dRopeJoint extends Box2dJoint{constructor(a,b,c,d,e=0,f=!1){c||=box2d.vec2From(a.body.GetPosition());d||=box2d.vec2From(b.body.GetPosition());const g=a.worldToLocal(c),h=b.worldToLocal(d),k=new box2d.instance.b2RopeJointDef;k.set_bodyA(a.body);k.set_bodyB(b.body);k.set_localAnchorA(box2d.vec2dTo(g));k.set_localAnchorB(box2d.vec2dTo(h));k.set_maxLength(c.distance(d)+e);k.set_collideConnected(f);super(k)}getLocalAnchorA(){return box2d.vec2From(this.box2dJoint.GetLocalAnchorA())}getLocalAnchorB(){return box2d.vec2From(this.box2dJoint.GetLocalAnchorB())}setMaxLength(a){this.box2dJoint.SetMaxLength(a)}getMaxLength(){return this.box2dJoint.GetMaxLength()}}class Box2dRevoluteJoint extends Box2dJoint{constructor(a,b,c,d=!1){c||=box2d.vec2From(b.body.GetPosition());const e=a.worldToLocal(c);c=b.worldToLocal(c);const f=new box2d.instance.b2RevoluteJointDef;f.set_bodyA(a.body);f.set_bodyB(b.body);f.set_localAnchorA(box2d.vec2dTo(e));f.set_localAnchorB(box2d.vec2dTo(c));f.set_referenceAngle(a.body.GetAngle()-b.body.GetAngle());f.set_collideConnected(d);super(f)}getLocalAnchorA(){return box2d.vec2From(this.box2dJoint.GetLocalAnchorA())}getLocalAnchorB(){return box2d.vec2From(this.box2dJoint.GetLocalAnchorB())}getReferenceAngle(){return this.box2dJoint.GetReferenceAngle()}getJointAngle(){return this.box2dJoint.GetJointAngle()}getJointSpeed(){return this.box2dJoint.GetJointSpeed()}isLimitEnabled(){return this.box2dJoint.IsLimitEnabled()}enableLimit(a=!0){return this.box2dJoint.EnableLimit(a)}getLowerLimit(){return this.box2dJoint.GetLowerLimit()}getUpperLimit(){return this.box2dJoint.GetUpperLimit()}setLimits(a,b){return this.box2dJoint.SetLimits(a,b)}isMotorEnabled(){return this.box2dJoint.IsMotorEnabled()}enableMotor(a=!0){return this.box2dJoint.EnableMotor(a)}setMotorSpeed(a){return this.box2dJoint.SetMotorSpeed(a)}getMotorSpeed(){return this.box2dJoint.GetMotorSpeed()}setMaxMotorTorque(a){return this.box2dJoint.SetMaxMotorTorque(a)}getMaxMotorTorque(){return this.box2dJoint.GetMaxMotorTorque()}getMotorTorque(a){return this.box2dJoint.GetMotorTorque(1/a)}}class Box2dGearJoint extends Box2dJoint{constructor(a,b,c,d,e=1){const f=new box2d.instance.b2GearJointDef;f.set_bodyA(a.body);f.set_bodyB(b.body);f.set_joint1(c.box2dJoint);f.set_joint2(d.box2dJoint);f.set_ratio(e);super(f);this.joint1=c;this.joint2=d}getJoint1(){return this.joint1}getJoint2(){return this.joint2}setRatio(a){return this.box2dJoint.SetRatio(a)}getRatio(){return this.box2dJoint.GetRatio()}}class Box2dPrismaticJoint extends Box2dJoint{constructor(a,b,c,d=vec2(0,1),e=!1){c||=box2d.vec2From(b.body.GetPosition());const f=a.worldToLocal(c);c=b.worldToLocal(c);d=b.worldToLocalVector(d);const g=new box2d.instance.b2PrismaticJointDef;g.set_bodyA(a.body);g.set_bodyB(b.body);g.set_localAnchorA(box2d.vec2dTo(f));g.set_localAnchorB(box2d.vec2dTo(c));g.set_localAxisA(box2d.vec2dTo(d));g.set_referenceAngle(a.body.GetAngle()-b.body.GetAngle());g.set_collideConnected(e);super(g)}getLocalAnchorA(){return box2d.vec2From(this.box2dJoint.GetLocalAnchorA())}getLocalAnchorB(){return box2d.vec2From(this.box2dJoint.GetLocalAnchorB())}getLocalAxisA(){return box2d.vec2From(this.box2dJoint.GetLocalAxisA())}getReferenceAngle(){return this.box2dJoint.GetReferenceAngle()}getJointTranslation(){return this.box2dJoint.GetJointTranslation()}getJointSpeed(){return this.box2dJoint.GetJointSpeed()}isLimitEnabled(){return this.box2dJoint.IsLimitEnabled()}enableLimit(a=!0){return this.box2dJoint.EnableLimit(a)}getLowerLimit(){return this.box2dJoint.GetLowerLimit()}getUpperLimit(){return this.box2dJoint.GetUpperLimit()}setLimits(a,b){return this.box2dJoint.SetLimits(a,b)}isMotorEnabled(){return this.box2dJoint.IsMotorEnabled()}enableMotor(a=!0){return this.box2dJoint.EnableMotor(a)}setMotorSpeed(a){return this.box2dJoint.SetMotorSpeed(a)}getMotorSpeed(){return this.box2dJoint.GetMotorSpeed()}setMaxMotorForce(a){return this.box2dJoint.SetMaxMotorForce(a)}getMaxMotorForce(){return this.box2dJoint.GetMaxMotorForce()}getMotorForce(a){return this.box2dJoint.GetMotorForce(1/a)}}class Box2dWheelJoint extends Box2dJoint{constructor(a,b,c,d=vec2(0,1),e=!1){c||=box2d.vec2From(b.body.GetPosition());const f=a.worldToLocal(c);c=b.worldToLocal(c);d=b.worldToLocalVector(d);const g=new box2d.instance.b2WheelJointDef;g.set_bodyA(a.body);g.set_bodyB(b.body);g.set_localAnchorA(box2d.vec2dTo(f));g.set_localAnchorB(box2d.vec2dTo(c));g.set_localAxisA(box2d.vec2dTo(d));g.set_collideConnected(e);super(g)}getLocalAnchorA(){return box2d.vec2From(this.box2dJoint.GetLocalAnchorA())}getLocalAnchorB(){return box2d.vec2From(this.box2dJoint.GetLocalAnchorB())}getLocalAxisA(){return box2d.vec2From(this.box2dJoint.GetLocalAxisA())}getJointTranslation(){return this.box2dJoint.GetJointTranslation()}getJointSpeed(){return this.box2dJoint.GetJointSpeed()}isMotorEnabled(){return this.box2dJoint.IsMotorEnabled()}enableMotor(a=!0){return this.box2dJoint.EnableMotor(a)}setMotorSpeed(a){return this.box2dJoint.SetMotorSpeed(a)}getMotorSpeed(){return this.box2dJoint.GetMotorSpeed()}setMaxMotorTorque(a){return this.box2dJoint.SetMaxMotorTorque(a)}getMaxMotorTorque(){return this.box2dJoint.GetMaxMotorTorque()}getMotorTorque(a){return this.box2dJoint.GetMotorTorque(1/a)}setSpringFrequencyHz(a){return this.box2dJoint.SetSpringFrequencyHz(a)}getSpringFrequencyHz(){return this.box2dJoint.GetSpringFrequencyHz()}setSpringDampingRatio(a){return this.box2dJoint.SetSpringDampingRatio(a)}getSpringDampingRatio(){return this.box2dJoint.GetSpringDampingRatio()}}class Box2dWeldJoint extends Box2dJoint{constructor(a,b,c,d=!1){c||=box2d.vec2From(b.body.GetPosition());const e=a.worldToLocal(c);c=b.worldToLocal(c);const f=new box2d.instance.b2WeldJointDef;f.set_bodyA(a.body);f.set_bodyB(b.body);f.set_localAnchorA(box2d.vec2dTo(e));f.set_localAnchorB(box2d.vec2dTo(c));f.set_referenceAngle(a.body.GetAngle()-b.body.GetAngle());f.set_collideConnected(d);super(f)}getLocalAnchorA(){return box2d.vec2From(this.box2dJoint.GetLocalAnchorA())}getLocalAnchorB(){return box2d.vec2From(this.box2dJoint.GetLocalAnchorB())}getReferenceAngle(){return this.box2dJoint.GetReferenceAngle()}setFrequency(a){return this.box2dJoint.SetFrequency(a)}getFrequency(){return this.box2dJoint.GetFrequency()}setSpringDampingRatio(a){return this.box2dJoint.SetSpringDampingRatio(a)}getSpringDampingRatio(){return this.box2dJoint.GetSpringDampingRatio()}}class Box2dFrictionJoint extends Box2dJoint{constructor(a,b,c,d=!1){c||=box2d.vec2From(b.body.GetPosition());const e=a.worldToLocal(c);c=b.worldToLocal(c);const f=new box2d.instance.b2FrictionJointDef;f.set_bodyA(a.body);f.set_bodyB(b.body);f.set_localAnchorA(box2d.vec2dTo(e));f.set_localAnchorB(box2d.vec2dTo(c));f.set_collideConnected(d);super(f)}getLocalAnchorA(){return box2d.vec2From(this.box2dJoint.GetLocalAnchorA())}getLocalAnchorB(){return box2d.vec2From(this.box2dJoint.GetLocalAnchorB())}setMaxForce(a){this.box2dJoint.SetMaxForce(a)}getMaxForce(){return this.box2dJoint.GetMaxForce()}setMaxTorque(a){this.box2dJoint.SetMaxTorque(a)}getMaxTorque(){return this.box2dJoint.GetMaxTorque()}}class Box2dPulleyJoint extends Box2dJoint{constructor(a,b,c,d,e,f,g=1,h=!1){e||=box2d.vec2From(a.body.GetPosition());f||=box2d.vec2From(b.body.GetPosition());const k=a.worldToLocal(e),l=b.worldToLocal(f),m=new box2d.instance.b2PulleyJointDef;m.set_bodyA(a.body);m.set_bodyB(b.body);m.set_groundAnchorA(box2d.vec2dTo(c));m.set_groundAnchorB(box2d.vec2dTo(d));m.set_localAnchorA(box2d.vec2dTo(k));m.set_localAnchorB(box2d.vec2dTo(l));m.set_ratio(g);m.set_lengthA(c.distance(e));m.set_lengthB(d.distance(f));m.set_collideConnected(h);super(m)}getGroundAnchorA(){return box2d.vec2From(this.box2dJoint.GetGroundAnchorA())}getGroundAnchorB(){return box2d.vec2From(this.box2dJoint.GetGroundAnchorB())}getLengthA(){return this.box2dJoint.GetLengthA()}getLengthB(){return this.box2dJoint.GetLengthB()}getRatio(){return this.box2dJoint.GetRatio()}getCurrentLengthA(){return this.box2dJoint.GetCurrentLengthA()}getCurrentLengthB(){return this.box2dJoint.GetCurrentLengthB()}}class Box2dMotorJoint extends Box2dJoint{constructor(a,b){const c=a.worldToLocal(box2d.vec2From(b.body.GetPosition())),d=b.body.GetAngle()-a.body.GetAngle(),e=new box2d.instance.b2MotorJointDef;e.set_bodyA(a.body);e.set_bodyB(b.body);e.set_linearOffset(box2d.vec2dTo(c));e.set_angularOffset(d);super(e)}setLinearOffset(a){this.box2dJoint.SetLinearOffset(box2d.vec2dTo(a))}getLinearOffset(){return box2d.vec2From(this.box2dJoint.GetLinearOffset())}setAngularOffset(a){this.box2dJoint.SetAngularOffset(a)}getAngularOffset(){return this.box2dJoint.GetAngularOffset()}setMaxForce(a){this.box2dJoint.SetMaxForce(a)}getMaxForce(){return this.box2dJoint.GetMaxForce()}setMaxTorque(a){this.box2dJoint.SetMaxTorque(a)}getMaxTorque(){return this.box2dJoint.GetMaxTorque()}setCorrectionFactor(a){this.box2dJoint.SetCorrectionFactor(a)}getCorrectionFactor(){return this.box2dJoint.GetCorrectionFactor()}}class Box2dPlugin{constructor(a){ASSERT(!box2d,"Box2D already initialized");box2d=this;this.instance=a;this.world=new box2d.instance.b2World;this.objects=[];this.velocityIterations=8;this.positionIterations=3;this.bodyTypeStatic=a.b2_staticBody;this.bodyTypeKinematic=a.b2_kinematicBody;this.bodyTypeDynamic=a.b2_dynamicBody;a=new box2d.instance.JSContactListener;a.BeginContact=function(b){var c=box2d.instance.wrapPointer(b,box2d.instance.b2Contact);b=c.GetFixtureA();c=c.GetFixtureB();b=b.GetBody().object;c=c.GetBody().object;b.beginContact(c);c.beginContact(b)};a.EndContact=function(b){var c=box2d.instance.wrapPointer(b,box2d.instance.b2Contact);b=c.GetFixtureA();c=c.GetFixtureB();b=b.GetBody().object;c=c.GetBody().object;b.endContact(c);c.endContact(b)};a.PreSolve=function(){};a.PostSolve=function(){};box2d.world.SetContactListener(a)}step(a=1){for(box2d.world.SetGravity(box2d.vec2dTo(gravity));a--;)box2d.world.Step(timeDelta,this.velocityIterations,this.positionIterations)}raycastAll(a,b){const c=new box2d.instance.JSRayCastCallback;c.ReportFixture=function(e,f,g,h){e=box2d.instance.wrapPointer(e,box2d.instance.b2Fixture);f=box2d.vec2FromPointer(f);g=box2d.vec2FromPointer(g);d.push(new Box2dRaycastResult(e,f,g,h));return 1};const d=[];box2d.world.RayCast(c,box2d.vec2dTo(a),box2d.vec2dTo(b));debugRaycast&&debugLine(a,b,d.length?"#f00":"#00f",.02);return d}raycast(a,b){a=box2d.raycastAll(a,b);if(a.length)return a.reduce((c,d)=>c.fraction<d.fraction?c:d)}boxCastAll(a,b){const c=new box2d.instance.JSQueryCallback;c.ReportFixture=function(f){f=box2d.instance.wrapPointer(f,box2d.instance.b2Fixture).GetBody().object;e.includes(f)||e.push(f);return!0};const d=new box2d.instance.b2AABB;d.set_lowerBound(box2d.vec2dTo(a.subtract(b.scale(.5))));d.set_upperBound(box2d.vec2dTo(a.add(b.scale(.5))));let e=[];box2d.world.QueryAABB(c,d);debugRaycast&&debugRect(a,b,e.length?"#f00":"#00f",.02);return e}boxCast(a,b){const c=new box2d.instance.JSQueryCallback;c.ReportFixture=function(f){e=box2d.instance.wrapPointer(f,box2d.instance.b2Fixture).GetBody().object;return!1};const d=new box2d.instance.b2AABB;d.set_lowerBound(box2d.vec2dTo(a.subtract(b.scale(.5))));d.set_upperBound(box2d.vec2dTo(a.add(b.scale(.5))));let e;box2d.world.QueryAABB(c,d);debugRaycast&&debugRect(a,b,e?"#f00":"#00f",.02);return e}circleCastAll(a,b){const c=(b/2)**2;return box2d.boxCastAll(a,vec2(b)).filter(d=>d.pos.distanceSquared(a)<c)}circleCast(a,b){const c=(b/2)**2;b=box2d.boxCastAll(a,vec2(b));let d,e;for(const f of b)b=f.pos.distanceSquared(a),b<c&&(!d||b<e)&&(d=f,e=b);return d}pointCast(a,b=!0){const c=new box2d.instance.JSQueryCallback;c.ReportFixture=function(f){f=box2d.instance.wrapPointer(f,box2d.instance.b2Fixture);if(b&&f.GetBody().GetType()!==box2d.instance.b2_dynamicBody||!f.TestPoint(box2d.vec2dTo(a)))return!0;e=f.GetBody().object;return!1};const d=new box2d.instance.b2AABB;d.set_lowerBound(box2d.vec2dTo(a));d.set_upperBound(box2d.vec2dTo(a));let e;box2d.world.QueryAABB(c,d);debugRaycast&&debugRect(a,vec2(),e?"#f00":"#00f",.02);return e}drawFixture(a,b,c,d=WHITE,e=BLACK,f=.1,g,h){a=box2d.castShapeObject(a.GetShape());switch(a.GetType()){case box2d.instance.b2Shape.e_polygon:let k=[];for(let l=a.GetVertexCount();l--;)k.push(box2d.vec2From(a.GetVertex(l)));drawPoly(k,d,f,e,b,c,g,!1,h);break;case box2d.instance.b2Shape.e_circle:c=a.get_m_radius();drawCircle(b,2*c,d,f,e,g,!1,h);break;case box2d.instance.b2Shape.e_edge:d=box2d.vec2From(a.get_m_vertex1()),a=box2d.vec2From(a.get_m_vertex2()),drawLine(d,a,f,e,b,c,g,!1,h)}}vec2From(a){ASSERT(a instanceof box2d.instance.b2Vec2);return new Vector2(a.get_x(),a.get_y())}vec2FromPointer(a){a=box2d.instance.wrapPointer(a,box2d.instance.b2Vec2);return box2d.vec2From(a)}vec2dTo(a){ASSERT(isVector2(a));return new box2d.instance.b2Vec2(a.x,a.y)}isNull(a){return!box2d.instance.getPointer(a)}castShapeObject(a){switch(a.GetType()){case box2d.instance.b2Shape.e_circle:return box2d.instance.castObject(a,box2d.instance.b2CircleShape);case box2d.instance.b2Shape.e_edge:return box2d.instance.castObject(a,box2d.instance.b2EdgeShape);case box2d.instance.b2Shape.e_polygon:return box2d.instance.castObject(a,box2d.instance.b2PolygonShape);case box2d.instance.b2Shape.e_chain:return box2d.instance.castObject(a,box2d.instance.b2ChainShape)}ASSERT(!1,"Unknown box2d object type")}castJointObject(a){switch(a.GetType()){case box2d.instance.e_revoluteJoint:return box2d.instance.castObject(a,box2d.instance.b2RevoluteJoint);case box2d.instance.e_prismaticJoint:return box2d.instance.castObject(a,box2d.instance.b2PrismaticJoint);case box2d.instance.e_distanceJoint:return box2d.instance.castObject(a,box2d.instance.b2DistanceJoint);case box2d.instance.e_pulleyJoint:return box2d.instance.castObject(a,box2d.instance.b2PulleyJoint);case box2d.instance.e_mouseJoint:return box2d.instance.castObject(a,box2d.instance.b2MouseJoint);case box2d.instance.e_gearJoint:return box2d.instance.castObject(a,box2d.instance.b2GearJoint);case box2d.instance.e_wheelJoint:return box2d.instance.castObject(a,box2d.instance.b2WheelJoint);case box2d.instance.e_weldJoint:return box2d.instance.castObject(a,box2d.instance.b2WeldJoint);case box2d.instance.e_frictionJoint:return box2d.instance.castObject(a,box2d.instance.b2FrictionJoint);case box2d.instance.e_ropeJoint:return box2d.instance.castObject(a,box2d.instance.b2RopeJoint);case box2d.instance.e_motorJoint:return box2d.instance.castObject(a,box2d.instance.b2MotorJoint)}ASSERT(!1,"Unknown box2d object type")}}async function box2dInit(){new Box2dPlugin(await Box2D());(function(){const a=new box2d.instance.JSDraw,b=d=>{d=box2d.instance.wrapPointer(d,box2d.instance.b2Color);return new Color(d.get_r(),d.get_g(),d.get_b())},c=(d,e)=>{const f=[];for(;e--;)f.push(box2d.vec2FromPointer(d+8*e));return f};a.DrawSegment=function(d,e,f){f=b(f).scale(1,.8);d=box2d.vec2FromPointer(d);e=box2d.vec2FromPointer(e);drawLine(d,e,.1,f,vec2(),0,!1)};a.DrawPolygon=function(d,e,f){f=b(f).scale(1,.8);d=c(d,e);drawPoly(d,CLEAR_WHITE,.1,f,vec2(),0,!1)};a.DrawSolidPolygon=function(d,e,f){f=b(f).scale(1,.8);d=c(d,e);drawPoly(d,f,0,f,vec2(),0,!1)};a.DrawCircle=function(d,e,f){f=b(f).scale(1,.8);d=box2d.vec2FromPointer(d);drawCircle(d,2*e,CLEAR_WHITE,.1,f,!1)};a.DrawSolidCircle=function(d,e,f,g){g=b(g).scale(1,.8);d=box2d.vec2FromPointer(d);f=box2d.vec2FromPointer(f).scale(e);drawCircle(d,2*e,g,.1,g,!1);drawLine(vec2(),f,.1,g,d,0,!1)};a.DrawTransform=function(d){d=box2d.instance.wrapPointer(d,box2d.instance.b2Transform);const e=vec2(d.get_p());d=-d.get_q().GetAngle();const f=vec2(1,0),g=rgb(.75,0,0,.8),h=vec2(0,1),k=rgb(0,.75,0,.8);drawLine(vec2(),f,.1,g,e,d,!1);drawLine(vec2(),h,.1,k,e,d,!1)};a.AppendFlags(box2d.instance.b2Draw.e_shapeBit);a.AppendFlags(box2d.instance.b2Draw.e_jointBit);box2d.world.SetDebugDraw(a)})();engineAddPlugin(function(){if(!paused){box2d.step();box2d.objects=box2d.objects.filter(a=>!a.destroyed);for(const a of box2d.objects)a.body&&(a.pos=box2d.vec2From(a.body.GetPosition()),a.angle=-a.body.GetAngle())}},function(){(box2dDebug||debugPhysics)&&box2d.world.DrawDebugData()});return box2d}function drawNineSliceScreen(a,b,c,d=32,e=2,f=0){drawNineSlice(a,b,c,WHITE,d,BLACK,e,f,!1,!0)}function drawNineSlice(a,b,c,d,e=1,f,g=.05,h=0,k=glEnable,l,m){const n=c.offset(c.size);var p=b.add(vec2(g-2*e));g=vec2(e);b=b.scale(.5).subtract(g.scale(.5));const q=l?-1:1,r=l?-h:h;drawTile(a,p,n,d,h,!1,f,k,l,m);for(var t=4;t--;){var u=t%2,w=b.multiply(vec2(u?1===t?1:-1:0,u?0:t?-1:1));u=vec2(u?e:p.x,u?p.y:e);const x=n.offset(c.size.multiply(vec2(1===t?1:3===t?-1:0,0===t?-q:2===t?q:0)));drawTile(a.add(w.rotate(r)),u,x,d,h,!1,f,k,l,m)}for(e=4;e--;)t=1<e,w=e&&3>e,p=b.multiply(vec2(t?-1:1,w?-1:1)),t=n.offset(c.size.multiply(vec2(t?-1:1,w?q:-q))),drawTile(a.add(p.rotate(r)),g,t,d,h,!1,f,k,l,m)}function drawThreeSliceScreen(a,b,c,d=32,e=2,f=0){drawThreeSlice(a,b,c,WHITE,d,BLACK,e,f,!1,!0)}function drawThreeSlice(a,b,c,d,e=1,f,g=.05,h=0,k=glEnable,l,m){const n=c.frame(0);var p=c.frame(1),q=c.frame(2),r=b.add(vec2(g-2*e));g=vec2(e);b=b.scale(.5).subtract(g.scale(.5));c=l?-1:1;const t=l?-h:h;drawTile(a,r,q,d,h,!1,f,k,l,m);for(q=4;q--;){const w=h+q*PI/2;var u=q%2;const x=b.multiply(vec2(u?1===q?1:-1:0,u?0:q?-c:c));u=vec2(u?r.y:r.x,e);drawTile(a.add(x.rotate(t)),u,p,d,w,!1,f,k,l,m)}for(e=4;e--;)p=h+e*PI/2,r=b.multiply(vec2(!e||2<e?-1:1,1<e?-c:c)),drawTile(a.add(r.rotate(t)),g,n,d,p,!1,f,k,l,m)}