littlejsengine 1.18.28 → 1.19.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,16 @@
1
1
  // LittleJS Engine - MIT License - Copyright 2021 Frank Force
2
2
  // https://github.com/KilledByAPixel/LittleJS
3
3
 
4
- const engineName="LittleJS",engineVersion="1.18.28",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,engineUpdateInternal,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){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();primitiveCount=drawCount=0}}var p=m-frameTimeLastMS;frameTimeLastMS||(p=0);frameTimeLastMS=m;if(debug||debugWatermark)averageFPS=lerp(averageFPS,1e3/(p||1),.05);m=debug&&keyIsDown("Equal");const q=debug&&keyIsDown("Minus");m=m?10:q?.1:1;timeReal+=p*m/1e3;m*=timeScale;frameTimeBufferMS+=paused?0:p*m;1>=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();engineManualStep||requestAnimationFrame(h)}function k(){if(!headlessMode){if(canvasFixedSize.x){mainCanvasSize=canvasFixedSize.copy();var m=innerWidth/innerHeight,n=canvasFixedSize.x/canvasFixedSize.y,p=m<n?"100%":"";m=m<n?"":"100%";mainCanvas.style.width=p;mainCanvas.style.height=m;glCanvas&&(glCanvas.style.width=p,glCanvas.style.height=m)}else p=canvasPixelRatio??(devicePixelRatio||1),m=innerWidth*p|0,n=innerHeight*p|0,mainCanvasSize.x=min(m,canvasMaxSize.x),mainCanvasSize.y=min(n,canvasMaxSize.y),m/=n,ASSERT(canvasMinAspect<=canvasMaxAspect),canvasMaxAspect&&m>canvasMaxAspect?mainCanvasSize.x=min(mainCanvasSize.y*canvasMaxAspect|0,canvasMaxSize.x):m<canvasMinAspect&&(mainCanvasSize.y=min(mainCanvasSize.x/canvasMinAspect|0,canvasMaxSize.y)),m=(mainCanvasSize.x/p|0)+"px",p=(mainCanvasSize.y/p|0)+"px",mainCanvas.style.width=m,mainCanvas.style.height=p,glCanvas&&(glCanvas.style.width=m,glCanvas.style.height=p);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();engineManualStep||h()}showEngineVersion&&console.log(`${engineName} Engine v${engineVersion}`);ASSERT(!mainContext,"engine already initialized");if(!mainContext){ASSERT(isArray(f),"pass in images as array");document.body||document.documentElement.appendChild(document.createElement("body"));g||=document.body;a||=()=>{};b||=()=>{};c||=()=>{};d||=()=>{};e||=()=>{};engineUpdateInternal=h;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(imageFontInit());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()}}const engineStepMaxFrames=36e3;function engineStep(a=1){ASSERT(engineManualStep,"engineStep requires setEngineManualStep(true) before engineInit");ASSERT(engineUpdateInternal,"engineStep requires engineInit to complete");if(engineManualStep&&engineUpdateInternal)for(ASSERT(Number.isInteger(a)&&0<=a&&a<=engineStepMaxFrames,"engineStep requires a whole frame count from 0 to "+engineStepMaxFrames),a=min(a,engineStepMaxFrames);0<a;--a)engineUpdateInternal(frameTimeLastMS+1e3/frameRate)}function engineObjectsUpdate(){function a(b){if(!b.destroyed){b.update();for(const c of b.children)a(c)}}engineObjectsCollide=engineObjects.filter(b=>b.collideSolidObjects);for(const b of engineObjects)b.parent||b.destroyed||b.updatePhysics();for(const b of engineObjects)if(!b.parent&&!b.destroyed){b.update();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}let debugWatermark=0,debugKey="";const debug=0,debugOverlay=0,debugPhysics=0,debugParticles=0,debugRaycast=0,debugGamepads=0,debugSound=0,debugPointSize=.5;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=a=>Math.sign(a),hypot=(...a)=>Math.hypot(...a),log2=a=>Math.log2(a),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){ASSERT(0<c,"distanceWrap wrapSize must be > 0");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 0<a&&!(a&a-1)}function nearestPowerOfTwo(a){return 2**ceil(log2(a))}function isOverlapping(a,b,c,d=vec2()){const e=2*(a.y-c.y),f=b.x+d.x;b=b.y+d.y;return abs(2*(a.x-c.x))<f&&abs(e)<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=mod(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 isStringLike(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=(e*e+f*f)**.5;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){if(0>=a)return new Vector2;b=clamp(b/a);return randVec2(a*rand(b*b,1)**.5)}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){ASSERT(0!==a,"RandomGenerator seed must be non-zero (xorshift is fixed at 0)");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){const c=min(a,b);a=max(a,b)-c;b=this.float(2*a);return b<a?c+b:a-c-b}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)}setAlpha(a=1){this.a=a;ASSERT_COLOR_VALID(this);return this}copy(){return new Color(this.r,this.g,this.b,this.a)}withAlpha(a=1){return new Color(this.r,this.g,this.b,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(isStringLike(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()?this.setTime?1-percent(this.time-this.getGlobalTime(),0,this.setTime):1: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(isStringLike(a),"saveDataURL requires url string");ASSERT(isStringLike(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(isStringLike(a),"shareURL requires title string");ASSERT(isStringLike(b),"shareURL requires url string");navigator.share?.({title:a,url:b}).then(()=>c?.())}function readSaveData(a,b){ASSERT(isStringLike(a),"readSaveData requires saveName string");ASSERT(void 0===b||"object"===typeof b&&null!==b,"readSaveData: default must be an object - the result is {...default, ...loaded}, so a scalar default yields {}. Use readSaveData(key, {best:0}).best");let c={};try{const d=localStorage[a];if(d)try{c=JSON.parse(d)}catch{LOG("readSaveData: corrupt JSON for",a,"— using defaults")}}catch{LOG("readSaveData: localStorage unavailable — using defaults")}return{...b,...c}}function writeSaveData(a,b){ASSERT(isStringLike(a),"writeSaveData requires saveName string");try{localStorage[a]=JSON.stringify(b)}catch{LOG("writeSaveData: failed to write",a)}}function noiseHash(a){a=(a|0)^2654435769;a=Math.imul(a^a>>>16,2246822507);a=Math.imul(a^a>>>13,3266489909);return((a^a>>>16)>>>0)/2**32}function noise1D(a){const b=floor(a);return lerp(noiseHash(b),noiseHash(b+1),smoothStep(a-b))}function noise2D(a,b){const c=floor(a),d=floor(b);a=smoothStep(a-c);b=smoothStep(b-d);return lerp(lerp(noiseHash(c+374761393*d),noiseHash(c+1+374761393*d),a),lerp(noiseHash(c+374761393*(d+1)),noiseHash(c+1+374761393*(d+1)),a),b)}let cameraPos=vec2(),cameraAngle=0,cameraScale=32,timeScale=1,canvasColorTiles=!0,canvasClearColor=CLEAR_BLACK,canvasMaxSize=vec2(1920,1080),canvasMinAspect=0,canvasMaxAspect=0,canvasFixedSize=vec2(),canvasPixelated=!1,tilesPixelated=!0,canvasPixelRatio=1,fontDefault="arial",showSplashScreen=!1,headlessMode=!1,engineManualStep=!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,gamepadAxisFilterEnable=!0,inputWASDEmulateDirection=!0,touchInputEnable=!0,touchGamepadEnable=!1,touchGamepadPassthrough=!1,touchGamepadCenterButtonSize=0,touchGamepadButtonCount=4,touchGamepadLeftStick=!0,touchGamepadLeftButtonCount=0,touchGamepadRightStick=!1,touchGamepadAnalog=!0,touchGamepadFloating=!1,touchGamepadSize=100,touchGamepadAlpha=.3,touchGamepadDisplayTime=3,touchGamepadVibration=0,vibrateEnable=!0,soundEnable=!0,soundVolume=.3,soundDefaultRange=40,soundDefaultTaper=.7;function setCameraPos(a){cameraPos=a.copy()}function setCameraAngle(a){cameraAngle=a}function setCameraScale(a){cameraScale=a}function setTimeScale(a){timeScale=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 setCanvasPixelRatio(a){canvasPixelRatio=a}function setFontDefault(a){fontDefault=a}function setShowSplashScreen(a){showSplashScreen=a}function setHeadlessMode(a){headlessMode=a}function setEngineManualStep(a=!0){engineManualStep=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 setGamepadAxisFilterEnable(a){gamepadAxisFilterEnable=a}function setInputWASDEmulateDirection(a){inputWASDEmulateDirection=a}function setTouchInputEnable(a){touchInputEnable=a}function setTouchGamepadEnable(a){touchGamepadEnable=a}function setTouchGamepadPassthrough(a){touchGamepadPassthrough=a}function setTouchGamepadCenterButtonSize(a){touchGamepadCenterButtonSize=a}function setTouchGamepadButtonCount(a){touchGamepadButtonCount=a;0<a&&(touchGamepadRightStick=!1)}function setTouchGamepadLeftStick(a){(touchGamepadLeftStick=a)&&(touchGamepadLeftButtonCount=0)}function setTouchGamepadLeftButtonCount(a){touchGamepadLeftButtonCount=a;0<a&&(touchGamepadLeftStick=!1)}function setTouchGamepadRightStick(a){(touchGamepadRightStick=a)&&(touchGamepadButtonCount=0)}function setTouchGamepadAnalog(a){touchGamepadAnalog=a}function setTouchGamepadFloating(a){touchGamepadFloating=a}function setTouchGamepadSize(a){touchGamepadSize=a}function setTouchGamepadAlpha(a){touchGamepadAlpha=a}function setTouchGamepadDisplayTime(a){touchGamepadDisplayTime=a}function setTouchGamepadVibration(a){touchGamepadVibration=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 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(){var a=this.parent;if(a){const c=a.getMirrorSign();var b=this.localPos;const d=a.pos,e=b.x*c;b=b.y;if(a=a.angle){const f=cos(-a),g=sin(-a);this.pos.set(e*f-b*g+d.x,e*g+b*f+d.y)}else this.pos.set(e+d.x,b+d.y);this.angle=c*this.localAngle+a}for(const c of this.children)c.updateTransforms()}updatePhysics(){ASSERT(!this.parent);if(!this.destroyed){this.clampSpeed&&(this.velocity.x=clamp(this.velocity.x,-objectMaxSpeed,objectMaxSpeed),this.velocity.y=clamp(this.velocity.y,-objectMaxSpeed,objectMaxSpeed));var 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=0<(0<gravity.y?-1:1)?floor(a.y-this.size.y/2+1)+this.size.y/2+.001:ceil(a.y+this.size.y/2-1)-this.size.y/2-.001;if(.1>abs(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)}renderLight(){}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(!this.destroyed,"cannot add child to destroyed object");if(this.destroyed)return a;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;a.updateTransforms();return a}removeChild(a){ASSERT(a.parent===this&&this.children.includes(a));this.children.splice(this.children.indexOf(a),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(){let a="type = "+this.constructor.name;if(this.pos.x||this.pos.y)a+="\npos = "+this.pos;if(this.velocity.x||this.velocity.y)a+="\nvelocity = "+this.velocity;if(this.size.x||this.size.y)a+="\nsize = "+this.size;this.angle&&(a+="\nangle = "+this.angle.toFixed(3));this.color&&(a+="\ncolor = "+this.color);return a}renderDebugInfo(){if(debug){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,backgroundCanvas,mainCanvasSize=vec2(),textureInfos=[],drawCount,primitiveCount;function isWhite(a){return 1<=a.r&&1<=a.g&&1<=a.b}function isBlack(a){return 0>=a.r&&0>=a.g&&0>=a.b&&0>=a.a}function tile(a=0,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,f=0){this.pos=a.copy();this.size=b.copy();this.padding=d;this.textureInfo=c;this.bleed=e;this.columns=f}offset(a){return new TileInfo(this.pos.add(a),this.size,this.textureInfo,this.padding,this.bleed,this.columns)}frame(a){ASSERT("number"===typeof a);const b=(this.columns?a%this.columns:a)*(this.size.x+2*this.padding);a=(this.columns?a/this.columns|0:0)*(this.size.y+2*this.padding);ASSERT(this.pos.x+b+this.size.x<=this.textureInfo.size.x,"frame extends beyond texture width!");ASSERT(this.pos.y+a+this.size.y<=this.textureInfo.size.y,"frame extends beyond texture height!");return this.offset(new Vector2(b,a))}setColumns(a=0){ASSERT(isNumber(a)&&0<=a,"columns must be a number >= 0");this.columns=a;return this}index(a){return tile(a,this.size,this.textureInfo,this.padding,this.bleed).setColumns(this.columns)}setFullImage(a=this.textureInfo){this.textureInfo=a;this.pos=new Vector2;this.size=a.size.copy();this.bleed=this.padding=this.columns=0;return this}}class TextureInfo{constructor(a,b=!0,c=!1){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;this.wrap=c;b&&this.createWebGLTexture()}createWebGLTexture(){glRegisterTextureInfo(this)}destroyWebGLTexture(){glUnregisterTextureInfo(this)}hasWebGL(){return!!this.glTexture}setWrap(a=!0){this.wrap=a;glSetTextureWrap(this.glTexture,a)}}function drawTile(a,b=vec2(1),c,d=WHITE,e=0,f,g,h=glEnable,k=!1,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 f=g?d.add(g):d,glDrawUntextured(a.x,a.y,b.x,b.y,e,f.rgbaInt());else++drawCount,++primitiveCount,drawCanvas2D(a,b,e,f,q=>{if(m)q.scale(1,-1),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=CLEAR_WHITE,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,++primitiveCount,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 drawTextureWrapped(a,b,c,d=0,e=WHITE,f=0,g,h=glEnable,k=!1,l){ASSERT(isVector2(a),"pos must be a vec2");ASSERT(isVector2(b),"size must be a vec2");ASSERT(isVector2(c),"wrapCount must be a vec2");ASSERT(isColor(e),"color is invalid");ASSERT(isNumber(f),"angle must be a number");ASSERT(!g||isColor(g),"additiveColor must be a color");ASSERT(!l||!h,"context only supported in canvas 2D mode");ASSERT(!(d instanceof TileInfo),"pass a TextureInfo or texture index, not a TileInfo — use tileInfo.textureInfo");headlessMode||(d="number"===typeof d?textureInfos[d]:d,ASSERT(d instanceof TextureInfo,"texture not loaded"),ASSERT(0<d.size.x,"texture not loaded"),ASSERT(d.wrap,"drawTextureWrapped requires a wrap-enabled texture; call textureInfo.setWrap(true) first"),h&&glEnable?(ASSERT(!!glContext,"WebGL is not enabled!"),k&&([a,b,f]=screenToWorldTransform(a,b,f)),glSetTexture(d.glTexture),glDraw(a.x,a.y,b.x,b.y,f,0,0,c.x,c.y,e.rgbaInt(),g&&g.rgbaInt())):(++drawCount,++primitiveCount,k||(a=worldToScreen(a),b=b.scale(cameraScale),f-=cameraAngle),k=!canvasColorTiles||(g?isWhite(e.add(g))&&0>=g.a:isWhite(e)),h=!k&&g&&!isBlack(g),g=k?d.image:bakeTintedImage(d.image,e,g),l=l||drawContext,l.save(),l.translate(a.x+.5,a.y+.5),l.rotate(f),l.globalAlpha=h?1:e.a,a=l.createPattern(g,"repeat"),c=(new DOMMatrix).translate(-b.x/2,-b.y/2).scale(b.x/(c.x*g.width),b.y/(c.y*g.height)),a.setTransform(c),l.fillStyle=a,l.fillRect(-b.x/2,-b.y/2,b.x,b.y),l.globalAlpha=1,l.restore()))}function drawLineList(a,b=.1,c=WHITE,d=!1,e=vec2(),f=0,g=glEnable,h=!1,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,++primitiveCount,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=WHITE,e=vec2(),f=0,g=glEnable,h=!1,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,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)}let drawEllipseGradientOffset=0;function drawEllipseGradient(a,b=vec2(1),c=WHITE,d=CLEAR_WHITE,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(!headlessMode)if(f&&glEnable){ASSERT(!!glContext,"WebGL is not enabled!");g&&(a=screenToWorld(a),b=b.scale(1/cameraScale),e+=cameraAngle);f=glCircleSides;const k=b.x/2,l=b.y/2;b=c.rgbaInt();g=d.rgbaInt();h=drawEllipseGradientOffset++;const m=cos(-e),n=sin(-e);e=r=>{const t=sin(r)*k;r=cos(r)*l;return vec2(a.x+t*m-r*n,a.y+t*n+r*m)};const p=[e(h%f/f*PI*2)],q=[g];for(let r=f;r--;){const t=(r+h)%f/f*PI*2;p.push(a);q.push(b);p.push(e(t));q.push(g)}glDrawColoredPoints(p,q)}else++drawCount,++primitiveCount,drawCanvas2D(a,b,e,!1,k=>{const l=k.createRadialGradient(0,0,0,0,0,.5);l.addColorStop(0,c.toString());l.addColorStop(1,d.toString());k.fillStyle=l;k.beginPath();k.ellipse(0,0,.5,.5,0,0,9);k.fill()},g,h)}function drawCircleGradient(a,b=1,c=WHITE,d=CLEAR_WHITE,e=glEnable,f=!1,g){ASSERT(isNumber(b),"size must be a number");drawEllipseGradient(a,vec2(b),c,d,0,e,f,g)}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=WHITE,e=0,f=BLACK,g="center",h=fontDefault,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(isStringLike(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(isStringLike(h),"font must be a string");ASSERT(isStringLike(k),"fontStyle must be a string");ASSERT(isNumber(m),"angle must be a number");a=(a+"").split("\n");const p=b.y-(a.length-1)*c/2;n.save();n.fillStyle=d.toString();n.strokeStyle=f.toString();n.lineWidth=e;n.textAlign=g;n.font=k+" "+c+"px "+h;n.textBaseline="middle";n.translate(b.x,p);n.rotate(-m);let q=0;a.forEach(r=>{e&&n.strokeText(r,0,q,l);n.fillText(r,0,q,l);q+=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||isStringLike(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 cameraFit(a,b,c,d){function e(k){if(void 0===k||isNumber(k))k=vec2(k);return isVector2(k)?{top:k.y,right:k.x,bottom:k.y,left:k.x}:{top:k.top||0,right:k.right||0,bottom:k.bottom||0,left:k.left||0}}ASSERT(isVector2(a),"center must be a vec2");ASSERT(isVector2(b),"size must be a vec2");c=e(c);d=e(d);const f=b.x+c.left+c.right;b=b.y+c.top+c.bottom;const g=mainCanvasSize.x-d.left-d.right,h=mainCanvasSize.y-d.top-d.bottom;if(!(0<f&&0<b&&0<g&&0<h))return cameraScale;cameraScale=min(g/f,h/b);c=vec2(c.right-c.left,c.top-c.bottom).scale(.5);d=vec2(d.right-d.left,d.top-d.bottom).scale(.5/cameraScale);cameraPos=a.add(c).add(d);return 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");if(!cameraScale)return!1;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 setAdditiveBlendMode(a=!0){glAdditive=a;drawContext.globalCompositeOperation=a?"lighter":"source-over"}function setBackgroundCanvas(a){backgroundCanvas=a}function combineCanvases(){const a=mainCanvasSize.x,b=mainCanvasSize.y;workCanvas.width=a;workCanvas.height=b;workContext.fillStyle="#000";workContext.fillRect(0,0,a,b);backgroundCanvas&&workContext.drawImage(backgroundCanvas,0,0,a,b);glCopyToContext(workContext);workContext.drawImage(mainCanvas,0,0);mainContext.drawImage(workCanvas,0,0)}function bakeTintedImage(a,b,c){var d=a.width|0,e=a.height|0;workReadCanvas.width=d;workReadCanvas.height=e;workReadContext.drawImage(a,0,0);a=workReadContext.getImageData(0,0,d,e);d=a.data;if(c&&!isBlack(c))for(b=[b.r,b.g,b.b,b.a],c=[255*c.r,255*c.g,255*c.b,255*c.a],e=0;e<d.length;++e)d[e]=d[e]*b[e&3]+c[e&3]|0;else for(c=0;c<d.length;c+=4)d[c]*=b.r,d[c+1]*=b.g,d[c+2]*=b.b;workReadContext.putImageData(a,0,0);return workReadCanvas}function drawImageColor(a,b,c,d,e,f,g,h,k,l,m,n,p=0){e=max(1,e|0);f=max(1,f|0);const q=e-2*p,r=f-2*p;if(!canvasColorTiles||(n?isWhite(m.add(n))&&0>=n.a:isWhite(m)))a.globalAlpha=m.a,a.drawImage(b,c+p,d+p,q,r,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&&!isBlack(n)){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,q,r,g,h,k,l)}else{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,q,r,g,h,k,l);a.globalAlpha=1}}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 engineImageFont;class ImageFont{constructor(a){ASSERT(!!a,"tileInfo is required for ImageFont");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(isStringLike(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 v=q.length;v--;){var u=q.charCodeAt(v);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=ceil(b.x+v*c.x-t-c.x/2)+c.x/2-.5;h.y=ceil(b.y+r*c.y-c.y/2)+c.y/2-.5;drawTile(h,c,k,e,0,!1,void 0,f,!0,g)}})}}async function imageFontInit(){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);engineImageFont=new ImageFont(b)}let mousePos=vec2(),mousePosScreen=vec2(),mouseDelta=vec2(),mouseDeltaScreen=vec2(),mouseWheel=0,mouseInWindow=!0,isUsingGamepad=!1,lastInputDevice="mouse",inputMouseMoveThreshold=6,inputPreventDefault=!0,gamepadPrimary=0;const isTouchDevice=!headlessMode&&void 0!==window.ontouchstart;function setInputPreventDefault(a=!0){inputPreventDefault=a}function setInputMouseMoveThreshold(a){inputMouseMoveThreshold=a}function usingMouseInput(){return"mouse"===lastInputDevice}function usingKeyboardInput(){return"keyboard"===lastInputDevice}function usingGamepadInput(){return"gamepad"===lastInputDevice}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;touchGamepadStickPointerId.length=0;gamepadStickData.length=0;gamepadDpadData.length=0;gamepadAxisCentered.length=0}function keyIsDown(a,b=0){ASSERT(isStringLike(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(isStringLike(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(isStringLike(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(isStringLike(a),"up key must be a string");ASSERT(isStringLike(b),"down key must be a string");ASSERT(isStringLike(c),"left key must be a string");ASSERT(isStringLike(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 gamepadVibrate(a=gamepadPrimary,b=200,c=1,d=1,e=0){ASSERT(isNumber(a),"gamepad must be a number");vibrateEnable&&!headlessMode&&navigator?.getGamepads?.()[a]?.vibrationActuator?.playEffect?.("dual-rumble",{duration:b,strongMagnitude:c,weakMagnitude:d,startDelay:e})}function gamepadVibrateStop(a=gamepadPrimary){ASSERT(isNumber(a),"gamepad must be a number");vibrateEnable&&!headlessMode&&navigator?.getGamepads?.()[a]?.vibrationActuator?.reset?.()}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=[],gamepadAxisCentered=[],gamepadAxisCenteredFrames=15,touchGamepadTimer=new Timer,touchGamepadButtons=[],touchGamepadSticks=[],touchGamepadStickAnchors=[],touchGamepadStickPointerId=[],touchGamepadPointerRole=new Map;let touchGamepadOverlay,touchGamepadStage,touchGamepadSvg,touchGamepadSvgEls,touchGamepadSideZones=[],touchGamepadZoneC,touchGamepadNeedRelayout=!0,touchGamepadLastLayout;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(g){if(touchInputEnable){soundEnable&&!headlessMode&&audioContext&&!audioIsRunning()&&audioContext.resume();if(!touchGamepadEnable||touchGamepadPassthrough){const l=[];for(var h of g.touches){var k=h;touchGamepadSideZones.includes(k.target)||k.target===touchGamepadZoneC||l.push(h)}if(k=l.length){h=vec2(l[0].clientX,l[0].clientY);const m=mousePosScreen;mousePosScreen=c(h);e&&l[0].identifier===f?mouseDeltaScreen=mouseDeltaScreen.add(mousePosScreen.subtract(m)):e||(inputData[0][0]=3);f=l[0].identifier}else e&&(inputData[0][0]=inputData[0][0]&2|4);e=k}inputPreventDefault&&g.cancelable&&document.hasFocus()&&g.preventDefault();return!0}}document.addEventListener("touchstart",g=>d(g),{passive:!1});document.addEventListener("touchmove",g=>d(g),{passive:!1});document.addEventListener("touchend",g=>d(g),{passive:!1});let e,f}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||(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&&(d=a(d.code),inputData[0][d]=inputData[0][d]&2|4)}),document.addEventListener("mousedown",function(d){if(!isTouchDevice||!touchInputEnable){soundEnable&&!headlessMode&&audioContext&&!audioIsRunning()&&audioContext.resume();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){d.ctrlKey||(mouseWheel+=sign(d.deltaY));inputPreventDefault&&d.cancelable&&document.hasFocus()&&d.preventDefault()},{passive:!1}),document.addEventListener("contextmenu",function(d){d.preventDefault()}),document.addEventListener("blur",function(){inputClear();touchGamepadPointerRole.clear();touchGamepadButtons.length=0;touchGamepadSticks.length=0;touchGamepadStickPointerId.length=0}),isTouchDevice&&touchInputEnable&&b())}function inputUpdate(){headlessMode||(touchInputEnable&&isTouchDevice||document.hasFocus()||inputClear(),mousePos=screenToWorld(mousePosScreen),mouseDelta=screenToWorldDelta(mouseDeltaScreen),touchGamepadInit(),function(){var a=l=>{const m=n=>.3<n?percent(n,.3,.8):-.3>n?-percent(-n,.3,.8):0;return vec2(m(l.x),m(-l.y)).clampLength()};if(touchGamepadEnable&&isTouchDevice){if(ASSERT(!touchGamepadLeftStick||!touchGamepadLeftButtonCount,"set touchGamepadLeftStick or touchGamepadLeftButtonCount, not both"),ASSERT(!touchGamepadRightStick||!touchGamepadButtonCount,"set touchGamepadRightStick or touchGamepadButtonCount, not both"),touchGamepadTimer.isSet()){gamepadPrimary=0;var b=gamepadStickData[0]??(gamepadStickData[0]=[]),c=gamepadDpadData[0]??(gamepadDpadData[0]=vec2());b.length=0;c.set();for(var d=0;2>d;d++)if(touchGamepadSideStick(d)){var e=touchGamepadStickOut(d);b[e]=vec2();var f=touchGamepadSticks[d]??vec2();if(touchGamepadAnalog)b[e]=a(f);else if(.3<f.lengthSquared()){var g=clamp(round(f.x),-1,1);f=clamp(round(f.y),-1,1);b[e]=vec2(g,-f).clampLength();e||c.set(g,-f)}}a=inputData[1]??(inputData[1]=[]);for(b=12;b--;)c=gamepadIsDown(b,0),a[b]=touchGamepadButtons[b]?c?1:3:c?4:0,touchGamepadVibration&&3===a[b]&&(9===b||touchGamepadIsFaceButton(b))&&vibrate(touchGamepadVibration)}}else if(gamepadsEnable&&navigator?.getGamepads&&(debug||document.hasFocus())){b=navigator.getGamepads();c=min(8,b.length);for(d=0;d<c;++d){const l=b[d];if(!l){inputData[d+1]=void 0;gamepadStickData[d]=void 0;gamepadDpadData[d]=void 0;gamepadHadInput[d]=void 0;gamepadAxisCentered[d]=void 0;continue}e=inputData[d+1]??(inputData[d+1]=[]);g=gamepadStickData[d]??(gamepadStickData[d]=[]);f=gamepadDpadData[d]??(gamepadDpadData[d]=vec2());const m="standard"===l.mapping,n=gamepadAxisCentered[d]??(gamepadAxisCentered[d]=[]);var h=p=>{const q=l.axes[p];if(m&&4>p||!gamepadAxisFilterEnable)return q;const r=n[p]|0;if(r>gamepadAxisCenteredFrames)return q;n[p]=.3>abs(q)?r+1:0;return 0};for(var k=0;k<l.axes.length-1;k+=2)g[k>>1]=a(vec2(h(k),h(k+1)));h=!1;for(k=l.buttons.length;k--;){const p=l.buttons[k],q=gamepadIsDown(k,d);e[k]=p.pressed?q?1:3:q?4:0;p.pressed&&(!p.value||.9<p.value)&&(h=!0)}h&&(gamepadHadInput[d]=!0,gamepadHadInput[gamepadPrimary]||(gamepadPrimary=d));"standard"===l.mapping&&f.set((gamepadIsDown(15,d)&&1)-(gamepadIsDown(14,d)&&1),(gamepadIsDown(12,d)&&1)-(gamepadIsDown(13,d)&&1));gamepadDirectionEmulateStick&&(f.x||f.y)&&(g[0]=f.clampLength())}touchGamepadEnable&&isUsingGamepad&&touchGamepadTimer.unset()}}(),function(){const a=mouseIsDown(0)||mouseIsDown(1)||mouseIsDown(2)||mouseDeltaScreen.length()>inputMouseMoveThreshold;let b=!1;for(var c=gamepadStickCount();c--&&!b;)b=.2<gamepadStick(c).lengthSquared();for(c=17;c--&&!b;)b=gamepadIsDown(c);c=!1;for(const d in inputData[0])if(isNaN(+d)&&inputData[0][d]&1){c=!0;break}b?lastInputDevice="gamepad":a?lastInputDevice="mouse":c&&(lastInputDevice="keyboard");isUsingGamepad="gamepad"===lastInputDevice}())}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(){touchGamepadRender()}const touchGamepadSvgNS="http://www.w3.org/2000/svg";function touchGamepadInit(){if(!touchGamepadOverlay&&touchGamepadEnable&&isTouchDevice&&!headlessMode&&document.body){var a=touchGamepadOverlay=document.createElement("div");a.style.cssText="position:fixed;inset:0;z-index:50;pointer-events:none;opacity:0;touch-action:none;user-select:none;-webkit-user-select:none;-webkit-touch-callout:none;transition:opacity .2s;box-sizing:border-box;padding:env(safe-area-inset-top) env(safe-area-inset-right) env(safe-area-inset-bottom) env(safe-area-inset-left)";var b=touchGamepadStage=document.createElement("div");b.style.cssText="position:relative;width:100%;height:100%;pointer-events:none";a.appendChild(b);var c=touchGamepadSvg=document.createElementNS(touchGamepadSvgNS,"svg");c.style.cssText="position:absolute;inset:0;width:100%;height:100%;pointer-events:none;overflow:visible;fill:none;stroke:#fff;stroke-width:3";b.appendChild(c);c=()=>{const d=document.createElement("div");d.style.cssText="position:absolute;pointer-events:auto;touch-action:none";d.addEventListener("pointerdown",e=>touchGamepadPointerDown(e,d));d.addEventListener("pointermove",e=>touchGamepadPointerMove(e));d.addEventListener("pointerup",e=>touchGamepadPointerUp(e));d.addEventListener("pointercancel",e=>touchGamepadPointerUp(e));b.appendChild(d);return d};touchGamepadSideZones[0]=c();touchGamepadSideZones[1]=c();touchGamepadZoneC=c();addEventListener("resize",()=>touchGamepadNeedRelayout=!0);document.body.appendChild(a);touchGamepadNeedRelayout=!0}}function touchGamepadStageRect(){return touchGamepadStage.getBoundingClientRect()}function touchGamepadSideStick(a){return a?touchGamepadRightStick:touchGamepadLeftStick}function touchGamepadSideButtonCount(a){return a?touchGamepadButtonCount:touchGamepadLeftButtonCount}function touchGamepadSideButtonBase(a){return a?0:4}function touchGamepadStickOut(a){return a&&touchGamepadLeftStick?1:0}function touchGamepadSideHasControl(a){return touchGamepadSideStick(a)||0<touchGamepadSideButtonCount(a)}function touchGamepadIsFaceButton(a){for(let b=0;2>b;b++){const c=touchGamepadSideButtonBase(b);if(!touchGamepadSideStick(b)&&a>=c&&a<c+touchGamepadSideButtonCount(b))return!0}return!1}function touchGamepadSideCenter(a,b,c){if(touchGamepadFloating&&touchGamepadSideStick(a)&&touchGamepadStickAnchors[a])return touchGamepadStickAnchors[a];c-=touchGamepadSize;const d=touchGamepadSideButtonCount(a);touchGamepadSideStick(a)||2!==d&&3!==d||(c-=touchGamepadSize/4);return vec2(a?b-touchGamepadSize:touchGamepadSize,c)}function touchGamepadRelayout(){if(touchGamepadOverlay){var a=touchGamepadStageRect(),b=a.width;a=a.height;var c=touchGamepadSize,d=(f,g)=>f.style.cssText="position:absolute;pointer-events:auto;touch-action:none;"+g;if(paused){for(var e of touchGamepadSideZones)e.style.display="none";touchGamepadCenterButtonSize?(d(touchGamepadZoneC,"inset:0"),touchGamepadZoneC.style.display=""):touchGamepadZoneC.style.display="none"}else{for(e=0;2>e;e++){const f=touchGamepadSideZones[e],g=e?"right":"left";f.style.display=touchGamepadSideHasControl(e)?"":"none";if(touchGamepadFloating){const h=touchGamepadSideHasControl(e?0:1)?"50%":"100%";d(f,`${g}:0;bottom:0;width:${h};height:60%`)}else d(f,`${g}:0;bottom:0;width:${3*c}px;height:${3*c}px`)}touchGamepadZoneC.style.display=touchGamepadCenterButtonSize?"":"none";c=touchGamepadCenterButtonSize;d(touchGamepadZoneC,`left:50%;top:50%;width:${2*c}px;height:${2*c}px;transform:translate(-50%,-50%)`)}touchGamepadBuildSvg(b,a);touchGamepadNeedRelayout=!1}}function touchGamepadBuildSvg(a,b){const c=touchGamepadSvg;for(;c.firstChild;)c.removeChild(c.firstChild);const d=touchGamepadSvgEls={face:[],thumb:[]},e=touchGamepadSize,f=(k,l,m,n)=>{const p=document.createElementNS(touchGamepadSvgNS,"circle");p.setAttribute("cx",k);p.setAttribute("cy",l);p.setAttribute("r",m);n&&p.setAttribute("fill",n);c.appendChild(p);return p},g=k=>{const l=.18*e,m=.5*e,n=k.x;k=k.y;const p=document.createElementNS(touchGamepadSvgNS,"path");p.setAttribute("d",`M ${n-l} ${k-m} H ${n+l} V ${k-l} H ${n+m} V ${k+l} H ${n+l} `+`V ${k+m} H ${n-l} V ${k+l} H ${n-m} V ${k-l} H ${n-l} Z`);c.appendChild(p)};for(let k=0;2>k;k++){const l=touchGamepadSideButtonCount(k),m=touchGamepadSideButtonBase(k),n=touchGamepadSideCenter(k,a,b);if(touchGamepadSideStick(k))touchGamepadAnalog?f(n.x,n.y,e/2):g(n),d.thumb[k]=f(n.x,n.y,e/4,"#fff");else if(1===l)d.face[m]=f(n.x,n.y,e/2,"#000");else for(let p=0;p<l;p++){var h=mod(p-1,4);let q=2<l?h:min(h,l-1);q=3===q?2:2===q?3:q;h=vec2().setDirection(h,e/2);2===l&&(h.x*=-1);k||(h.x*=-1);h=n.add(h);d.face[m+q]=f(h.x,h.y,e/4,"#000")}}debug&&debugGamepads&&touchGamepadBuildDebug(a,b)}function touchGamepadBuildDebug(a,b){const c=touchGamepadSize,d=touchGamepadSvg,e=(h,k,l)=>{h=document.createElementNS(touchGamepadSvgNS,h);for(const m in k)h.setAttribute(m,k[m]);h.setAttribute("stroke",l);h.setAttribute("stroke-width",2);h.setAttribute("fill","none");d.appendChild(h)},f=(h,k,l)=>e("circle",{cx:h.x,cy:h.y,r:k},l);e("line",{x1:a/2,y1:0,x2:a/2,y2:b},"#0f0");for(var g=0;2>g;g++)if(touchGamepadSideStick(g))if(touchGamepadFloating){const h=.4*b,k=!touchGamepadSideHasControl(g?0:1);e("rect",{x:k?0:g?a/2:0,y:h,width:k?a:a/2,height:b-h},"#0ff")}else f(touchGamepadSideCenter(g,a,b),2*c,"#0ff");else 1<=touchGamepadSideButtonCount(g)&&f(touchGamepadSideCenter(g,a,b),c,"#0ff");if(touchGamepadCenterButtonSize)for(f(vec2(a/2,b/2),touchGamepadCenterButtonSize,"#ff0"),g=0;2>g;g++)touchGamepadSideHasControl(g)&&f(touchGamepadSideCenter(g,a,b),2*c,"#f0f")}function touchGamepadRender(){if(touchGamepadOverlay&&!headlessMode)if(touchGamepadEnable&&isTouchDevice){touchGamepadOverlay.style.display="";var a=debug&&debugGamepads,b=[touchGamepadButtonCount,touchGamepadLeftButtonCount,touchGamepadLeftStick,touchGamepadRightStick,touchGamepadAnalog,touchGamepadSize,touchGamepadFloating,touchGamepadCenterButtonSize,paused,a].join();b!==touchGamepadLastLayout&&(touchGamepadLastLayout=b,touchGamepadNeedRelayout=!0);touchGamepadNeedRelayout&&touchGamepadRelayout();b=touchGamepadDisplayTime?percent(touchGamepadTimer.get(),touchGamepadDisplayTime+1,touchGamepadDisplayTime):1;var c=a||touchGamepadTimer.isSet()&&0<b&&!paused;touchGamepadOverlay.style.opacity=c?a?1:b*touchGamepadAlpha:0;if(c){b=touchGamepadStageRect();a=b.width;c=b.height;var d=touchGamepadSize;if(b=touchGamepadSvgEls){for(let e=0;2>e;e++)if(touchGamepadSideStick(e)&&b.thumb[e]){const f=touchGamepadSideCenter(e,a,c).add((touchGamepadSticks[e]??vec2()).scale(d/2));b.thumb[e].setAttribute("cx",f.x);b.thumb[e].setAttribute("cy",f.y)}for(a=0;a<b.face.length;a++)b.face[a]&&b.face[a].setAttribute("fill",touchGamepadButtons[a]?"#fff":"#000")}}}else"none"!==touchGamepadOverlay.style.display&&(touchGamepadOverlay.style.display="none",touchGamepadPointerRole.clear(),touchGamepadButtons.length=0,touchGamepadSticks.length=0,touchGamepadStickPointerId.length=0)}function touchGamepadEventPos(a){const b=touchGamepadStageRect();return vec2(a.clientX-b.left,a.clientY-b.top)}function touchGamepadApplyStick(a,b){b=b.subtract(touchGamepadStickAnchors[a]);touchGamepadSticks[a]=b.scale(2/touchGamepadSize).clampLength();touchGamepadButtons[touchGamepadStickOut(a)?11:10]=1}function touchGamepadFaceButtonAt(a,b,c,d){const e=touchGamepadSideButtonCount(a),f=touchGamepadSideButtonBase(a);c=touchGamepadSideCenter(a,c,d);if(c.distance(b)>=touchGamepadSize)return-1;if(1===e)return f;b=c.subtract(b);a||(b.x*=-1);a=2===e?b.x<b.y?1:0:mod(b.direction()+2,4);a=3===a?2:2===a?3:a;return a<e?f+a:-1}function touchGamepadControlAt(a,b,c){const d=touchGamepadSize;var e=a.x<b/2;const f=.4*c;for(let h=0;2>h;h++){var g=h?!e:e;if(touchGamepadSideStick(h)){const k=touchGamepadSideHasControl(h?0:1);if(touchGamepadFloating?(!k||g)&&a.y>f:g&&touchGamepadSideCenter(h,b,c).distance(a)<2*d)return{role:"stick",side:h}}else if(1<=touchGamepadSideButtonCount(h)&&(g=touchGamepadFaceButtonAt(h,a,b,c),0<=g))return{role:"face",btn:g}}if(touchGamepadCenterButtonSize){for(e=0;2>e;e++)if(touchGamepadSideHasControl(e)&&touchGamepadSideCenter(e,b,c).distance(a)<2*d)return;if(vec2(b/2,c/2).distance(a)<touchGamepadCenterButtonSize)return{role:"start"}}}function touchGamepadPointerDown(a,b){if(touchGamepadEnable)if(a.preventDefault(),b.setPointerCapture(a.pointerId),touchGamepadTimer.set(),soundEnable&&!headlessMode&&audioContext&&!audioIsRunning()&&audioContext.resume(),paused)touchGamepadCenterButtonSize&&(touchGamepadButtons[9]=1,touchGamepadPointerRole.set(a.pointerId,"start"));else{var c=touchGamepadStageRect();b=c.width;var d=c.height;c=vec2(a.clientX-c.left,a.clientY-c.top);var e=touchGamepadControlAt(c,b,d);e&&("stick"===e.role?(e=e.side,touchGamepadStickAnchors[e]=touchGamepadFloating?c:touchGamepadSideCenter(e,b,d),touchGamepadStickPointerId[e]=a.pointerId,touchGamepadPointerRole.set(a.pointerId,"stick"+e),touchGamepadNeedRelayout=!0,touchGamepadApplyStick(e,c)):"face"===e.role?(touchGamepadButtons[e.btn]=1,touchGamepadPointerRole.set(a.pointerId,"face"+e.btn)):(touchGamepadButtons[9]=1,touchGamepadPointerRole.set(a.pointerId,"start")))}}function touchGamepadPointerMove(a){const b=touchGamepadPointerRole.get(a.pointerId);b&&(a.preventDefault(),a=touchGamepadEventPos(a),"stick0"!==b&&"stick1"!==b||touchGamepadApplyStick("stick1"===b?1:0,a))}function touchGamepadPointerUp(a){const b=touchGamepadPointerRole.get(a.pointerId);b&&(touchGamepadPointerRole.delete(a.pointerId),"stick0"===b||"stick1"===b?(a="stick1"===b?1:0,touchGamepadStickPointerId[a]=void 0,touchGamepadSticks[a]=vec2(),delete touchGamepadButtons[touchGamepadStickOut(a)?11:10]):"start"===b?delete touchGamepadButtons[9]:delete touchGamepadButtons[+b.slice(4)],touchGamepadTimer.set())}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)||isStringLike(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.sampleLength=0,this._sampleChannels=this.sampleBuffer=void 0,this.loadedPercent=0,this.onloadCallback=e,isArray(a)?(a=a.slice(),this.randomness=a[1]??b??.05,a[1]=0,this.sampleChannels=[zzfxG(...a)],this.buildSampleBuffer(),this.loadedPercent=1,e?.(this)):"string"===typeof a&&this.loadSound(a))}get sampleChannels(){const a=this.sampleBuffer;if(!this._sampleChannels&&a){const b=[];for(let c=0;c<a.numberOfChannels;c++)b.push(a.getChannelData(c).slice());this._sampleChannels=b}return this._sampleChannels}set sampleChannels(a){this._sampleChannels=a;this.sampleBuffer=void 0;this.sampleLength=a?.[0]?.length||0}buildSampleBuffer(){this.sampleBuffer||!this._sampleChannels||headlessMode||(this.sampleBuffer=createAudioBuffer(this._sampleChannels,this.sampleRate),this._sampleChannels=void 0)}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.sampleBuffer||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}c+=c*this.randomness*d*rand(-1,1);e=new SoundInstance(this,b,c,g,e,f);debug&&debugSound&&a&&(debugCircle(a,.5,"#0ff",.5,!0),this.range&&(debugCircle(a,2*this.range,"#0ff",.5),debugCircle(a,2*this.range*this.taper,"#0ff",.5)),debugText("vol "+b.toFixed(2)+" pitch "+c.toFixed(2),a,.5,"#0ff",.5));return e}}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.sampleLength/this.sampleRate||0}isLoaded(){return 1===this.loadedPercent}async loadSound(a){const 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);this.sampleRate=a.sampleRate;this.sampleLength=a.length;this.sampleBuffer=a;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.sound.buildSampleBuffer();(this.source=this.sound.sampleBuffer?playAudioBuffer(this.sound.sampleBuffer,this.volume,this.rate,this.pan,this.loop,this.gainNode,a,this.onendedCallback):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.cancelScheduledValues(b);this.gainNode.gain.setValueAtTime(this.volume,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(){if(!this.isPlaying())return this.pausedTime;const a=this.getDuration();return a?mod(audioContext.currentTime-this.startTime,a):0}getDuration(){return this.rate?this.sound.getDuration()/this.rate:0}getSource(){return this.source}}function speak(a,b=1,c=1,d=1,e=""){ASSERT("string"!==typeof b,"speak() signature changed: language is now the last parameter, after pitch");if(soundEnable&&!headlessMode&&"undefined"!==typeof speechSynthesis)return a=new SpeechSynthesisUtterance(a),a.lang=e,a.volume=2*b*soundVolume,a.rate=c,a.pitch=d,speechSynthesis.speak(a),a}function speakStop(){"undefined"!==typeof speechSynthesis&&speechSynthesis.cancel()}function getNoteFrequency(a,b=220){return b*2**(a/12)}function playSamples(a,b=1,c=1,d=0,e=!1,f=audioDefaultSampleRate,g,h=0,k){if(soundEnable&&!headlessMode){if(audioIsRunning())return a=createAudioBuffer(a,f),playAudioBuffer(a,b,c,d,e,g,h,k);audioContext.resume()}}function createAudioBuffer(a,b=audioDefaultSampleRate){const c=audioContext.createBuffer(a.length,a[0].length,b);a.forEach((d,e)=>c.getChannelData(e).set(d));return c}function playAudioBuffer(a,b=1,c=1,d=0,e=!1,f,g=0,h){if(soundEnable&&!headlessMode){if(audioIsRunning()){var k=audioContext.createBufferSource();k.buffer=a;k.playbackRate.value=c;k.loop=e;f=f||audioContext.createGain();f.gain.value=b;f.connect(audioMasterGain);var l=new StereoPannerNode(audioContext,{pan:clamp(d,-1,1)});k.connect(l).connect(f);k.addEventListener("ended",()=>{f.disconnect();l.disconnect();h&&h(k)});k.start(0,g*c);debug&&debugSound&&LOG("sound","vol",b.toFixed(2),"rate",c.toFixed(2),"pan",d.toFixed(2),e?"loop":"");return k}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,v=1,x=0,B=0,z=0){var w=audioDefaultSampleRate;let y=2*PI,D=k*=500*y/w/w;b=c*=(1+rand(b,-b))*y/w;let G=0,J=0,H=0,E=1,K=[],F=0,A=0,C=0,O;var M=y*abs(z)*2/w,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*w||9;x*=w;e*=w;f*=w;u*=w;l*=500*y/w**3;r*=y/w;m*=y/w;n*=w;p=p*w|0;for(w=d+x+e+f+u|0;A<w;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-v):A<d+x+e?v:A<w-u?(w-A-u)/f*v:0),C=u?C/2+(u>A?0:(A<w-u?1:(w-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 d of tileCollisionLayers)if(!b||d.isSolid){var c=a.subtract(d.pos);if(c.arrayCheck(d.size)&&(c=d.getCollisionData(c)))return c}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){let f,g,h;const k=d&&vec2();for(const l of tileCollisionLayers)if(!e||l.isSolid){const m=l.collisionRaycast(a,b,c,k);if(m){const n=a.distanceSquared(m);if(void 0===f||n<g)f=m,g=n,d&&(h=k.copy())}}f&&d&&d.setFrom(h);return f}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)}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.drawTile=()=>{},this.drawRect=()=>{},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)]:void 0}update(){!glEnable&&this.isUsingWebGL&&(this.isUsingWebGL=!1,this.redraw())}render(){ASSERT(drawContext!==this.context,"must call redrawEnd() after drawing tiles!");const a=this.drawSize||this.size,b=this.pos.add(a.scale(.5));this.draw(b,a,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.index(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)}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)}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;if(0>e+b.x/2||e-b.x/2>this.size.x||0>f+b.y/2||f-b.y/2>this.size.y)return!1;a=max(e-b.x/2|0,0);var g=max(f-b.y/2|0,0);e=min(max(e+b.x/2,a+1),this.size.x);b=min(max(f+b.y/2,g+1),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,v=1,x=0,B=PI,z=.1,w=.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=v;this.gravityScale=x;this.particleConeAngle=B;this.fadeRate=z;this.randomness=w;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);a=this.particles;let b=0;for(let c=0;c<a.length;++c){const d=a[c];d.update();d.destroyed||(a[b++]=d)}a.length=b;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))}}const particleDrawPos=new Vector2;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)))){c=f(vec2(this.pos.x,a.y));f=f(vec2(a.x,this.pos.y));d=b?max(d,b.restitution):d;e=b?max(e,b.friction):e;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);e=particleDrawPos.set(this.pos.x,this.pos.y);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}c&&setAdditiveBlendMode();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&&setAdditiveBlendMode(!1);debugParticles&&debugRect(e,h,"#f005",0,f)}}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;glBatchCount=0;glPolyMode=!1;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,c.wrap);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){glContext&&a!==glActiveTexture&&(glFlush(),glActiveTexture=a,glContext.bindTexture(glContext.TEXTURE_2D,glActiveTexture))}function glSetTextureWrap(a,b=!0){if(glContext&&a){var c=a===glActiveTexture;c?glFlush():glContext.bindTexture(glContext.TEXTURE_2D,a);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);!c&&glActiveTexture&&glContext.bindTexture(glContext.TEXTURE_2D,glActiveTexture)}}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,b=!1){if(glContext){var c=glContext.createTexture(),d=!1;a?.width?(glSetTextureData(c,a),glContext.bindTexture(glContext.TEXTURE_2D,c),d=!tilesPixelated&&isPowerOfTwo(a.width)&&isPowerOfTwo(a.height)):(a=new Uint8Array([255,255,255,255]),glContext.bindTexture(glContext.TEXTURE_2D,c),glContext.texImage2D(glContext.TEXTURE_2D,0,glContext.RGBA,1,1,0,glContext.RGBA,glContext.UNSIGNED_BYTE,a));a=tilesPixelated?glContext.NEAREST:glContext.LINEAR;var e=d?glContext.LINEAR_MIPMAP_LINEAR:a;glContext.texParameteri(glContext.TEXTURE_2D,glContext.TEXTURE_MAG_FILTER,a);glContext.texParameteri(glContext.TEXTURE_2D,glContext.TEXTURE_MIN_FILTER,e);b=b?glContext.REPEAT:glContext.CLAMP_TO_EDGE;glContext.texParameteri(glContext.TEXTURE_2D,glContext.TEXTURE_WRAP_S,b);glContext.texParameteri(glContext.TEXTURE_2D,glContext.TEXTURE_WRAP_T,b);d&&glContext.generateMipmap(glContext.TEXTURE_2D);glContext.bindTexture(glContext.TEXTURE_2D,glActiveTexture);return c}}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),!tilesPixelated&&isPowerOfTwo(b.width)&&isPowerOfTwo(b.height)&&glContext.generateMipmap(glContext.TEXTURE_2D),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,a.wrap)))}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;primitiveCount+=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 glDrawUntextured(a,b,c,d,e,f){glDraw(a,b,c,d,e,0,0,0,0,0,f)}function glDrawPointsTransform(a,b,c,d,e,f,g,h=!0){const k=[],l=sin(-g);g=cos(-g);for(const m of a){a=m.x*e;const n=m.y*f;k.push(vec2(c+g*a-l*n,d+l*a+g*n))}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();ASSERT(c<gl_MAX_POLY_VERTEXES,"poly exceeds max batch size");if(!(c>=gl_MAX_POLY_VERTEXES)){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();ASSERT(c<gl_MAX_POLY_VERTEXES,"poly exceeds max batch size");if(!(c>=gl_MAX_POLY_VERTEXES)){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),glContext.viewport(0,0,mainCanvasSize.x,mainCanvasSize.y))}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[];b/=2;const d=[],e=a.length;for(let n=0;n<e;n++){var f=a[c?(n-1+e)%e:max(n-1,0)],g=a[n],h=a[c?(n+1)%e:min(n+1,e-1)],k=g.x-f.x,l=g.y-f.y,m=(k*k+l*l)**.5;f=h.x-g.x;const p=h.y-g.y;h=(f*f+p*p)**.5;1e-6>m&&1e-6>h||(l=1e-6<m?-l/m:0,m=1e-6<m?k/m:0,k=l+(1e-6<h?-p/h:0),f=m+(1e-6<h?f/h:0),h=(k*k+f*f)**.5,1e-6>h?(k=l,f=m):(k/=h,f/=h,l=l*k+m*f,1e-6<l&&(l=min(1/l,10),k*=l,f*=l)),l=vec2(g.x-k*b,g.y-f*b),g=vec2(g.x+k*b,g.y+f*b),d.push(l),d.push(g))}1<d.length&&c&&(d.push(d[0]),d.push(d[1]));return d}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.slice().reverse());var c=(r,t,u,v)=>{const x=b(t,u,r);u=b(u,v,r);r=b(v,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],v=a[r];if(1e-9>b(t,u,v))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,v)))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}function drawEngineLogo(a){const b=mainContext;var c=canvasPixelRatio??(devicePixelRatio||1),d=mainCanvas.width=innerWidth*c,e=mainCanvas.height=innerHeight*c;c=percent(a,1,.8);var f=percent(a,0,.5),g=b.createRadialGradient(d/2,e/2,0,d/2,e/2,.6*hypot(d,e));g.addColorStop(0,hsl(0,0,lerp(0,c/2,f),c).toString());g.addColorStop(1,hsl(0,0,0,c).toString());b.save();b.fillStyle=g;b.fillRect(0,0,d,e);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)};c=(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(d,e)/99);b.translate(d/2,e/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();d=0;for(e=0;8>e;++e)d+=b.measureText("LittleJS"[e]).width;for(e=2;e--;)for(let l=0,m=40-d/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[e?"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);d=[vec2(44,8),vec2(64,8),vec2(59,8+6*k),vec2(49,8+6*k)];c(d,2,8,6*k);g(44,8,20,-7,0);for(d=5;d--;)f(59-6*d*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(d=2;d--;)f(17+15*g,47,d?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)],c(g,0,40,14);b.restore()}let debugMedals=!1,medalDisplayTime=5,medalDisplaySlideTime=.5,medalDisplaySize=vec2(640,80),medalsPreventUnlock=!1;const medals={};let medalsDisplayQueue=[],medalsSaveName,medalsDisplayTimeLast;function medalsInit(a){medalsSaveName=a;if(!debugMedals){let b={};try{b=JSON.parse(localStorage[a]||"{}")}catch(c){b={}}medalsForEach(c=>{c.unlocked=!(!b[c.id]||!b[c.id].unlocked)});medalsSave()}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))}function medalsReset(){medalsForEach(a=>a.unlocked=!1);medalsSave()}function medalsSave(){if(medalsSaveName){var a={};medalsForEach(b=>{const c={name:b.name,description:b.description,icon:b.icon,unlocked:b.unlocked};b.image&&(c.src=b.image.src);a[b.id]=c});localStorage[medalsSaveName]=JSON.stringify(a)}}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;this.image=void 0;e&&((this.image=new Image).src=e);medals[a]=this}unlock(){medalsPreventUnlock||this.unlocked||(ASSERT(medalsSaveName,"save name must be set"),this.unlocked=!0,medalsSave(),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)}}function setMedalDisplayTime(a){medalDisplayTime=a}function setMedalDisplaySlideTime(a){medalDisplaySlideTime=a}function setMedalDisplaySize(a){medalDisplaySize=a.copy()}function setMedalsPreventUnlock(a){medalsPreventUnlock=a}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"))if((a=this.call("Medal.getList"))&&a.result&&!a.result.error){this.medals=a.result.data?.medals||[];debugMedals&&LOG(this.medals);for(const 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=this.call("ScoreBoard.getBoards")?.result?.data?.scoreboards||[];debugMedals&&LOG(this.scoreboards);setInterval(()=>this.call("Gateway.ping",0,!0),6e4)}else debugMedals&&LOG("Newgrounds session unavailable; skipping plugin init"),this.medals=[],this.scoreboards=[]}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);try{return b.responseText&&JSON.parse(b.responseText)}catch(e){debugMedals&&LOG("newgrounds response is not valid JSON",e)}}}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.bindFramebuffer(glContext.FRAMEBUFFER,null),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),glContext.pixelStorei(glContext.UNPACK_FLIP_Y_WEBGL,!1),glSetInstancedMode(!0))},function(){postProcess.shader=void 0;postProcess.texture=void 0;LOG("PostProcessPlugin: WebGL context lost")},function(){d();LOG("PostProcessPlugin: WebGL context restored")})}}let lightSystem;class LightSystemPlugin{constructor(a,b){function c(){if(!headlessMode)if(glEnable){lightSystem.textureSize||(lightSystem.textureSize=mainCanvasSize.copy());lightSystem.texture=glContext.createTexture();glContext.bindTexture(glContext.TEXTURE_2D,lightSystem.texture);glContext.texImage2D(glContext.TEXTURE_2D,0,glContext.RGBA,lightSystem.textureSize.x,lightSystem.textureSize.y,0,glContext.RGBA,glContext.UNSIGNED_BYTE,null);glContext.texParameteri(glContext.TEXTURE_2D,glContext.TEXTURE_MAG_FILTER,glContext.LINEAR);glContext.texParameteri(glContext.TEXTURE_2D,glContext.TEXTURE_MIN_FILTER,glContext.LINEAR);glContext.texParameteri(glContext.TEXTURE_2D,glContext.TEXTURE_WRAP_S,glContext.CLAMP_TO_EDGE);glContext.texParameteri(glContext.TEXTURE_2D,glContext.TEXTURE_WRAP_T,glContext.CLAMP_TO_EDGE);lightSystem.lightShader=glCreateProgram("#version 300 es\nprecision highp float;uniform mat4 m;uniform vec2 lightPos;uniform float radius;in vec2 g;out vec2 vWorldPos;void main(){vec2 worldP=lightPos+(g-.5)*2.*radius;gl_Position=m*vec4(worldP,1,1);vWorldPos=worldP;}","#version 300 es\nprecision highp float;uniform vec2 lightPos;uniform float radius;uniform float fadeRange;uniform vec4 color;in vec2 vWorldPos;out vec4 c;void main(){float dist=distance(vWorldPos,lightPos);float t=clamp((radius-dist)/max(fadeRange,1e-6),0.,1.);c=vec4(color.rgb*t*color.a,1.);}");lightSystem.compositeShader=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 s;uniform vec3 iResolution;out vec4 c;void main(){vec2 uv=gl_FragCoord.xy/iResolution.xy;c=vec4(texture(s,uv).rgb,1.);}");lightSystem.lightVAO=glContext.createVertexArray();glContext.bindVertexArray(lightSystem.lightVAO);glContext.bindBuffer(glContext.ARRAY_BUFFER,glGeometryBuffer);var d=glContext.getAttribLocation(lightSystem.lightShader,"g");glContext.enableVertexAttribArray(d);glContext.vertexAttribPointer(d,2,glContext.FLOAT,!1,8,0);lightSystem.compositeVAO=glContext.createVertexArray();glContext.bindVertexArray(lightSystem.compositeVAO);glContext.bindBuffer(glContext.ARRAY_BUFFER,glGeometryBuffer);d=glContext.getAttribLocation(lightSystem.compositeShader,"p");glContext.enableVertexAttribArray(d);glContext.vertexAttribPointer(d,2,glContext.FLOAT,!1,8,0)}else console.warn("LightSystemPlugin: WebGL not enabled!")}ASSERT(!lightSystem,"LightSystemPlugin already initialized");ASSERT(!postProcess,"LightSystemPlugin must be created before PostProcessPlugin");lightSystem=this;this.enabled=!0;this.ambientColor=(b||BLACK).copy();this.textureSize=a?a.copy():void 0;this.compositeVAO=this.lightVAO=this.compositeShader=this.lightShader=this.texture=void 0;c();engineAddPlugin(void 0,function(){if(!headlessMode&&glEnable&&lightSystem.enabled&&lightSystem.texture){glFlush();var d=glAdditive,e=lightSystem.ambientColor;glContext.bindFramebuffer(glContext.FRAMEBUFFER,glFramebuffer);glContext.framebufferTexture2D(glContext.FRAMEBUFFER,glContext.COLOR_ATTACHMENT0,glContext.TEXTURE_2D,lightSystem.texture,0);glContext.viewport(0,0,lightSystem.textureSize.x,lightSystem.textureSize.y);glContext.clearColor(e.r,e.g,e.b,e.a);glContext.clear(glContext.COLOR_BUFFER_BIT);setAdditiveBlendMode();glContext.enable(glContext.BLEND);glContext.blendFunc(glContext.ONE,glContext.ONE);for(const f of engineObjects)f.destroyed||f.renderLight();glFlush();glContext.bindFramebuffer(glContext.FRAMEBUFFER,null);glContext.viewport(0,0,mainCanvasSize.x,mainCanvasSize.y);glContext.useProgram(lightSystem.compositeShader);glContext.bindVertexArray(lightSystem.compositeVAO);glContext.activeTexture(glContext.TEXTURE0);glContext.bindTexture(glContext.TEXTURE_2D,lightSystem.texture);e=lightSystem.compositeShader;glContext.uniform1i(glContext.getUniformLocation(e,"s"),0);glContext.uniform3f(glContext.getUniformLocation(e,"iResolution"),mainCanvas.width,mainCanvas.height,1);glContext.blendFunc(glContext.DST_COLOR,glContext.ZERO);glContext.drawArrays(glContext.TRIANGLE_STRIP,0,4);glActiveTexture&&glContext.bindTexture(glContext.TEXTURE_2D,glActiveTexture);setAdditiveBlendMode(d);glSetInstancedMode(!0)}},function(){lightSystem.texture=void 0;lightSystem.lightShader=void 0;lightSystem.compositeShader=void 0;lightSystem.lightVAO=void 0;lightSystem.compositeVAO=void 0;LOG("LightSystemPlugin: WebGL context lost")},function(){c();LOG("LightSystemPlugin: WebGL context restored")})}drawLight(a){if(!headlessMode&&glEnable&&this.lightShader){glFlush();glContext.useProgram(this.lightShader);glContext.bindVertexArray(this.lightVAO);var b=vec2(2*cameraScale).divide(mainCanvasSize),c=cameraPos.rotate(-cameraAngle);c=vec2(-1).subtract(c.multiply(b));var d=cos(cameraAngle),e=sin(cameraAngle);c=[b.x*d,b.y*e,0,0,-b.x*e,b.y*d,0,0,1,1,1,0,c.x,c.y,0,1];b=this.lightShader;glContext.uniformMatrix4fv(glContext.getUniformLocation(b,"m"),!1,c);glContext.uniform2f(glContext.getUniformLocation(b,"lightPos"),a.pos.x,a.pos.y);glContext.uniform1f(glContext.getUniformLocation(b,"radius"),a.radius);glContext.uniform1f(glContext.getUniformLocation(b,"fadeRange"),a.fadeRange);a=a.color;glContext.uniform4f(glContext.getUniformLocation(b,"color"),a.r,a.g,a.b,a.a);glContext.drawArrays(glContext.TRIANGLE_STRIP,0,4);glSetInstancedMode(!0)}}}class Light extends EngineObject{constructor(a,b,c,d){super(a,vec2(1),void 0,0,c);ASSERT(isNumber(b)&&0<=b,"Light radius must be a non-negative number");ASSERT(void 0===d||isNumber(d)&&0<=d,"Light fadeRange must be a non-negative number when provided");this.radius=b;this.fadeRange=void 0===d?b:d}render(){}renderLight(){lightSystem&&lightSystem.drawLight(this)}}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,v=0,x,B=[],z=[],w=[],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[v++]/2||0,z[g]=(z[g]||0)-l*D+l,w[g]=(w[g++]||0)+l*D+l;k&&(r=k%1,D=m[1]||0,k|=0)&&(B=J[[q=m[v=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,w]}let uiSystem,uiDebug=0;function uiSetDebug(a){uiDebug="boolean"===typeof a?a?1:0:a}class UISystemPlugin{constructor(a=mainContext){function b(c){let d,e;c.parent?(d=c.parent.nativePos,e=c.parent.size):(d=uiSystem.screenToNative(mainCanvasSize.scale(.5)),e=uiSystem.nativeHeight?vec2(mainCanvasSize.x*uiSystem.nativeHeight/mainCanvasSize.y,uiSystem.nativeHeight):mainCanvasSize);const f=c.anchor;c.nativePos=d.add(e.multiply(f).scale(.5)).subtract(c.size.multiply(f).scale(.5)).add(c.localPos)}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;this._onKeyDown=c=>this._keyInputObject?.onKeyDown(c);engineAddPlugin(function(){function c(g){if(!g.destroyed&&g.visible){b(g);for(let h=g.children.length;h--;){const k=g.children[h];k&&c(k)}g.destroyed||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()})}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){if(this._dragListeners)for(const[f,g]of this._dragListeners)document.removeEventListener(f,g);this._dragListeners=[];const e=(f,g)=>{const h=k=>{k.preventDefault();f&&f(k)};document.addEventListener(g,h);this._dragListeners.push([g,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}get keyInputObject(){return this._keyInputObject}set keyInputObject(a){const b=!!this._keyInputObject;this._keyInputObject=a;!b&&a?document.addEventListener("keydown",this._onKeyDown):b&&!a&&document.removeEventListener("keydown",this._onKeyDown)}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=()=>{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);return h}}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.nativePos=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;this.anchor=vec2();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){uiSystem.activeObject===this&&(uiSystem.activeObject=void 0);uiSystem.hoverObject===this&&(uiSystem.hoverObject=void 0);uiSystem.lastHoverObject===this&&(uiSystem.lastHoverObject=void 0);uiSystem.navigationObject===this&&(uiSystem.navigationObject=void 0);uiSystem.keyInputObject===this&&(uiSystem.keyInputObject=void 0);this.destroyed=1;this.parent?.removeChild(this);for(const a of this.children)a.parent=void 0,a.destroy();this.children.length=0}}isMouseOverlapping(){if(!mouseInWindow)return!1;const a=isTouchDevice?this.size.add(vec2(this.extraTouchSize||0)):this.size,b=uiSystem.screenToNative(mousePosScreen);return isOverlapping(this.nativePos,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.nativePos,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(){let a="type = "+this.constructor.name;this.text&&(a+="\ntext = "+this.text);if(this.nativePos.x||this.nativePos.y)a+="\nnativePos = "+this.nativePos;if(this.localPos.x||this.localPos.y)a+="\nlocalPos = "+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.nativePos,this.size,CLEAR_BLACK,4,a)}click(a=!0){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(isStringLike(c),"ui text must be a string");ASSERT(["left","center","right"].includes(d),"ui text align must be left, center, or right");ASSERT(isStringLike(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.nativePos,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(isStringLike(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.nativePos,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.nativePos,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(isStringLike(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.nativePos.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(isStringLike(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.nativePos.add(a.multiply(vec2(-1))),this.nativePos.add(a.multiply(vec2(1))),this.lineWidth,this.lineColor);uiSystem.drawLine(this.nativePos.add(a.multiply(vec2(-1,1))),this.nativePos.add(a.multiply(vec2(1,-1))),this.lineWidth,this.lineColor)}a=this.getTextSize();const b=this.nativePos.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(isStringLike(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.nativePos.x:this.nativePos.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.nativePos.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.nativePos.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.nativePos,a,this.textColor,this.textLineWidth,this.textLineColor,this.align,this.font,this.fontStyle,!0,this.textShadow)}navigatePressed(){this.value=this.value?0:1;this.onChange();this.onRelease();super.navigatePressed()}}class UIVideo extends UIObject{constructor(a,b,c,d=!1,e=!1,f=1){super(a,b||vec2());ASSERT(isStringLike(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.nativePos.x,this.nativePos.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())}}class UILayout extends UIObject{constructor(a,b=1,c=10,d=10,e=!1){super(a);ASSERT(isNumber(b)&&1<=b,"ui layout columns must be a number >= 1");ASSERT(isNumber(c),"ui layout gap must be a number");ASSERT(isNumber(d),"ui layout padding must be a number");this.columns=b;this.gap=c;this.padding=d;e&&(this.color=CLEAR_BLACK,this.gradientColor=void 0,this.lineWidth=0,this.shadowColor=CLEAR_BLACK);this.relayout()}addChild(a){super.addChild(a);this.relayout();return a}removeChild(a){super.removeChild(a);this.relayout()}relayout(){const a=this.children.length;if(a){var b=this.columns,c=ceil(a/b),d=Array(b).fill(0),e=Array(c).fill(0);for(var f=0;f<a;++f){var g=f%b,h=floor(f/b),k=this.children[f];d[g]=max(d[g],k.size.x);e[h]=max(e[h],k.size.y)}f=this.gap*(b-1);for(var l of d)f+=l;l=this.gap*(c-1);for(var m of e)l+=m;m=Array(b);g=0;for(h=0;h<b;++h)m[h]=g,g+=d[h];g=Array(c);h=0;for(k=0;k<c;++k)g[k]=h,h+=e[k];for(c=0;c<a;++c)h=c%b,k=floor(c/b),this.children[c].localPos=vec2(-f/2+m[h]+this.gap*h+d[h]/2,-l/2+g[k]+this.gap*k+e[k]/2);this.size=vec2(f+2*this.padding,l+2*this.padding)}else this.size=vec2(2*this.padding)}}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(){if(!this.destroyed){ASSERT(this.body,"Box2dObject has no body to destroy");box2d.world.DestroyBody(this.body);var a=box2d.objects.indexOf(this);0<=a&&box2d.objects.splice(a,1);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=box2d.instance._malloc(8*a.length);for(let k=0,l=0;k<a.length;++k)box2d.instance.HEAPF32[f+l>>2]=a[k].x,l+=4,box2d.instance.HEAPF32[f+l>>2]=a[k].y,l+=4;const g=box2d.instance.wrapPointer(f,box2d.instance.b2Vec2),h=new box2d.instance.b2PolygonShape;h.Set(g,a.length);box2d.instance._free(f);return this.addShape(h,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);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);void 0!==a&&d.set_center(box2d.vec2dTo(a));void 0!==b&&d.set_mass(b);void 0!==c&&d.set_I(c);this.body.SetMassData(d)}setFilterData(a=0,b=0,c=0){this.getFixtureList().forEach(d=>{const e=d.GetFilterData();e.set_categoryBits(a);e.set_maskBits(65535&~b);e.set_groupIndex(c);d.SetFilterData(e)})}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();a=a.scale(this.getMass());this.body.ApplyLinearImpulse(box2d.vec2dTo(a),box2d.vec2dTo(b))}applyImpulse(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*this.getInertia())}applyAngularImpulse(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(b.body.GetAngle()-a.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=a.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(b.body.GetAngle()-a.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=a.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(b.body.GetAngle()-a.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&&c&&(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&&c&&(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=box2d.vec2From(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,v=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(v.rotate(r)),u,x,d,h,!1,f,k,l,m)}for(e=4;e--;)t=1<e,v=e&&3>e,p=b.multiply(vec2(t?-1:1,v?-1:1)),t=n.offset(c.size.multiply(vec2(t?-1:1,v?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 v=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,v,!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)}function drawCrescent(a,b=1,c=0,d=WHITE,e=0,f=!1,g=0,h=BLACK,k=glEnable,l=!1,m){b=getCrescentPoints(vec2(),b,c,0,f);drawPoly(b,d,g,h,a,e,k,l,m)}function getCrescentPoints(a,b=1,c=0,d=0,e=!1,f=glCircleSides){ASSERT(isVector2(a),"pos must be a vec2");ASSERT(isNumber(b)&&isNumber(c),"size and percent must be numbers");c=mod(4*c,4);2<=c&&(d+=PI);c=2>=c?c-1:3-c;e&&(c=-c,d+=PI);e=[];f=max(3,f>>1);b/=2;for(var g=0;g<=f;g++){var h=g/f*PI;e.push(vec2(b*cos(h),b*sin(h)).rotate(d).add(a))}for(g=f;0<=g;g--)h=g/f*PI,e.push(vec2(b*cos(h),-b*c*sin(h)).rotate(d).add(a));return e}let textureSheetSize=2048,textureSheetPadding=1,textureSheets=[],textureSheetQueue=Promise.resolve(),textureSheetPendingCount=0;class TextureSheet{constructor(a=textureSheetSize){ASSERT(0<a,"texture sheet size must be positive");this.size=a;this.canvas=headlessMode?void 0:new OffscreenCanvas(a,a);this.context=this.canvas?.getContext("2d");this.textureInfo=new TextureInfo(this.canvas);this.cursor=vec2();this.rowHeight=0;this.glDirty=!1;headlessMode&&(this.textureInfo.size=vec2(a),this.textureInfo.sizeInverse=vec2(1/a))}tryAdd(a,b=a,c=textureSheetPadding,d=0){ASSERT(isVector2(a)&&isVector2(b),"sizes must be vec2");ASSERT(0<b.x&&0<b.y,"frame size must be positive");isNumber(d)&&(d=vec2(d));ASSERT(isVector2(d)&&0<=d.x&&0<=d.y,"sourcePadding must be a number or vec2 >= 0");var e=b.x+2*d.x,f=b.y+2*d.y;ASSERT(0===a.x%e&&0===a.y%f,"image size must be a multiple of the padded frame size");var g=b.x+2*c;d=b.y+2*c;var h=this.size/g|0;ASSERT(0<h,"frame is too wide to fit on a texture sheet");e=a.x/e;f=a.y/f*e;a=min(e,h);g*=a;d*=ceil(f/a);h=this.cursor.x;e=this.cursor.y;f=this.rowHeight;h+g>this.size&&(h=0,e+=f,f=0);if(!(e+d>this.size))return this.cursor.x=h+g,this.cursor.y=e,this.rowHeight=max(f,d),new TileInfo(vec2(h+c,e+c),b,this.textureInfo,c,0,a)}drawImage(a,b,c=!0,d=0){ASSERT(!!this.context,"texture sheet has no canvas");isNumber(d)&&(d=vec2(d));const e=b.size,f=e.x+2*d.x,g=e.y+2*d.y,h=a.width/f;var k=a.height/g*h;const l=b.columns||k,m=e.x+2*b.padding,n=e.y+2*b.padding;for(;k--;)this.context.drawImage(a,k%h*f+d.x,(k/h|0)*g+d.y,e.x,e.y,b.pos.x+k%l*m,b.pos.y+(k/l|0)*n,e.x,e.y);this.glDirty=!0;c&&this.updateTexture()}updateTexture(){this.glDirty&&(this.glDirty=!1,this.textureInfo.createWebGLTexture())}}function loadSprite(a,b,c=textureSheetPadding,d=0){ASSERT(isStringLike(a),"image src must be a string");ASSERT(!b||isVector2(b)||isNumber(b),"frameSize must be a vec2 or number");ASSERT(isNumber(c),"padding must be a number");ASSERT(isNumber(d)||isVector2(d),"sourcePadding must be a number or vec2");isNumber(b)&&(b=vec2(b));const e=new TileInfo(vec2(),vec2(),void 0,c,0);if(headlessMode)return e;e.textureInfo=(textureSheets[0]||textureSheetCreate()).textureInfo;const f=new Image,g=new Promise(h=>{f.onerror=f.onload=h;f.crossOrigin="anonymous";f.src=a});++textureSheetPendingCount;textureSheetQueue=textureSheetQueue.then(async()=>{await g;if(f.width){const h=vec2(f.width,f.height),{sheet:k,tile:l}=textureSheetAdd(h,b,c,d);Object.assign(e,l);k.drawImage(f,e,!1,d)}else LOG("loadSprite failed to load image:",a);--textureSheetPendingCount||textureSheets.forEach(h=>h.updateTexture())});return e}function loadAtlas(a,b,c=textureSheetPadding){ASSERT(isStringLike(a),"atlas image src must be a string");ASSERT(isStringLike(b)||"object"===typeof b,"atlas json must be a path or object");ASSERT(isNumber(c),"padding must be a number");const d={};if(headlessMode)return d;const e="object"===typeof b?Promise.resolve(b):fetch(b).then(h=>h.ok&&h.json()).catch(()=>{}),f=new Image,g=new Promise(h=>{f.onerror=f.onload=h;f.crossOrigin="anonymous";f.src=a});++textureSheetPendingCount;textureSheetQueue=textureSheetQueue.then(async()=>{var h=await e;await g;if(f.width&&h)for(const k of parseAtlas(h)){h=k.frames[0].sourceSize;const l=vec2(h.x*k.frames.length,h.y),{sheet:m,tile:n}=textureSheetAdd(l,h,c),p=m.context,q=h.x+2*c,r=h.y+2*c;k.frames.forEach((t,u)=>{const v=n.pos.x+u%n.columns*q+t.offset.x;u=n.pos.y+(u/n.columns|0)*r+t.offset.y;t.rotated?(p.save(),p.translate(v,u),p.rotate(-PI/2),p.drawImage(f,t.pos.x,t.pos.y,t.size.y,t.size.x,-t.size.y,0,t.size.y,t.size.x),p.restore()):p.drawImage(f,t.pos.x,t.pos.y,t.size.x,t.size.y,v,u,t.size.x,t.size.y)});m.glDirty=!0;d[k.name]=n}else LOG("loadAtlas failed to load:",a,b);--textureSheetPendingCount||textureSheets.forEach(k=>k.updateTexture())});return d}function parseAtlas(a){ASSERT(!!a?.frames,"unrecognized atlas format, expected TexturePacker or Aseprite json");var b=(isArray(a.frames)?a.frames.map(f=>[f.filename,f]):Object.entries(a.frames)).map(([f,g])=>({name:f.replace(/\.[^.\\/]+$/,""),pos:vec2(g.frame.x,g.frame.y),size:vec2(g.frame.w,g.frame.h),offset:vec2(g.spriteSourceSize?.x??0,g.spriteSourceSize?.y??0),sourceSize:vec2(g.sourceSize?.w??g.frame.w,g.sourceSize?.h??g.frame.h),rotated:!!g.rotated}));const c=[];a=a.meta?.frameTags;if(a?.length){const f=new Set;for(var d of a){c.push({name:d.name,frames:b.slice(d.from,d.to+1)});for(var e=d.from;e<=d.to;++e)f.add(e)}b.forEach((g,h)=>f.has(h)||c.push({name:g.name,frames:[g]}));return c}d=new Map;for(e of b)(b=e.name.match(/^(.+?)([-_ ])?(\d+)$/))&&!b[2]&&/\d$/.test(b[1])&&(b=void 0),a=b?b[1]:e.name,e.groupIndex=b?Number(b[3]):void 0,d.has(a)||d.set(a,[]),d.get(a).push(e);for(const[f,g]of d)g.sort((h,k)=>h.groupIndex-k.groupIndex),1<g.length&&g.every((h,k)=>h.groupIndex===g[0].groupIndex+k)&&g.every(h=>h.sourceSize.x===g[0].sourceSize.x&&h.sourceSize.y===g[0].sourceSize.y)?c.push({name:f,frames:g}):g.forEach(h=>c.push({name:h.name,frames:[h]}));return c}async function spritesReady(){for(;textureSheetPendingCount;)await textureSheetQueue}function textureSheetCreate(){const a=new TextureSheet;textureSheets.push(a);return a}function textureSheetAdd(a,b,c,d){let e,f;for(e of textureSheets)if(f=e.tryAdd(a,b,c,d))break;f||(e=textureSheetCreate(),f=e.tryAdd(a,b,c,d),ASSERT(!!f,"image is too large to fit on a texture sheet"));return{sheet:e,tile:f}}function setTextureSheetSize(a){textureSheetSize=a}function setTextureSheetPadding(a){textureSheetPadding=a}const tweenActive=[];let lastTime=0,lastTimeReal=0;function isLerpable(a){return a&&"function"===typeof a.lerp}class Tween{constructor(a,b=0,c=1,d=1,e={}){ASSERT("function"===typeof a,"Tween callback must be a function");isLerpable(b)?ASSERT(b.constructor===c.constructor,"Tween start and end must be the same type"):(ASSERT(isNumber(b),"Tween start must be a number or have a .lerp method"),ASSERT(isNumber(c),"Tween end must be a number when start is a number"));ASSERT(isNumber(d)&&0<d,"Tween duration must be > 0");this.callback=a;this.start=b;this.end=c;this.life=this.duration=d;this.ease=e.ease||Ease.LINEAR;this.useRealTime=!!e.useRealTime;this.paused=!!e.paused;this.thenCallback=void 0;this.loopRemaining=0;tweenActive.push(this);a(this.interp(d))}setEase(a){this.ease=a;return this}then(a){this.thenCallback=a;this.loopRemaining=0;return this}loop(a=Infinity){this.loopRemaining=a;this.thenCallback=()=>loopContinuation(this);return this}pingPong(a=Infinity){this.loopRemaining=a;this.thenCallback=()=>pingPongContinuation(this);return this}pause(){this.paused=!0}resume(){this.paused=!1}restart(){this.life=this.duration;this.paused=!1;0>tweenActive.indexOf(this)&&tweenActive.push(this);this.callback(this.interp(this.duration))}isActive(){return!this.paused&&0<=tweenActive.indexOf(this)}getPercent(){return percent(this.duration-this.life,0,this.duration)}getValue(){return this.interp(this.life)}interp(a){a=this.ease((this.duration-a)/this.duration);return isLerpable(this.start)?this.start.lerp(this.end,a):this.start+(this.end-this.start)*a}stop(){const a=tweenActive.indexOf(this);0<=a&&tweenActive.splice(a,1);this.thenCallback=void 0}}const Ease={LINEAR:a=>a,POWER:a=>b=>b**a,SINE:a=>1-cos(PI/2*a),CIRC:a=>1-(1-a*a)**.5,EXPO:a=>0===a?0:2**(10*a-10),BACK:a=>a*a*(2.70158*a-1.70158),ELASTIC:a=>0===a?0:1===a?1:-(2**(10*a-10))*sin((37-40*a)*PI/6),SPRING:a=>1-(sin(PI*(1-a)*(.2+2.5*(1-a)**3))*a**2.2+(1-a))*(1+1.2*a),BOUNCE:a=>{a=1-a;return 1-(a<4/11?7.5625*a*a:a<8/11?7.5625*(a-=6/11)*a+.75:a<10/11?7.5625*(a-=9/11)*a+.9375:7.5625*(a-=10.5/11)*a+.984375)},IN:a=>a,OUT:a=>b=>1-a(1-b),IN_OUT:a=>Ease.PIECEWISE(a,Ease.OUT(a)),PIECEWISE:(...a)=>{const b=a.length;return c=>{const d=c*b-1e-9>>0;return(a[d]((c-d/b)*b)+d)/b}},BEZIER:(a,b,c,d)=>{const e=f=>{var g=1-f;const h=3*g*g*f;g=3*g*f*f;f**=3;return[h*a+g*c+f,h*b+g*d+f]};return f=>{let g=0,h=1;for(let k=0;128>k;k++){const l=(g+h)/2,[m,n]=e(l);if(1e-5>abs(m-f))return n;m<f?g=l:h=l}return e((g+h)/2)[1]}}};function tweenProperty(a,b,c,d,e=1,f={}){ASSERT(null!=a&&"object"===typeof a,"tweenProperty target must be an object");ASSERT(isStringLike(b)&&0<b.length,"tweenProperty propertyPath must be a non-empty string");const g=b.split("."),h=g.pop();return new Tween(k=>{let l=a;for(const m of g)l=l[m],ASSERT(null!=l,"tweenProperty path does not resolve: "+b);l[h]=k},c,d,e,f)}function loopContinuation(a){Infinity!==a.loopRemaining&&1>=a.loopRemaining||(Infinity!==a.loopRemaining&&--a.loopRemaining,a.life=a.duration,a.thenCallback=()=>loopContinuation(a),tweenActive.push(a),a.callback(a.interp(a.duration)))}function pingPongContinuation(a){if(!(Infinity!==a.loopRemaining&&1>=a.loopRemaining)){Infinity!==a.loopRemaining&&--a.loopRemaining;var b=a.start;a.start=a.end;a.end=b;a.life=a.duration;a.thenCallback=()=>pingPongContinuation(a);tweenActive.push(a);a.callback(a.interp(a.duration))}}function tweenUpdate(a,b){void 0===a?(a=time-lastTime,b=timeReal-lastTimeReal,lastTime=time,lastTimeReal=timeReal):void 0===b&&(b=a);for(let d=tweenActive.length;d--;){const e=tweenActive[d];if(!e.paused){var c=e.useRealTime?b:a;0>=c||(e.life-=c,0<e.life?e.callback(e.interp(e.life)):(e.callback(e.interp(0)),tweenActive.splice(d,1),c=e.thenCallback,e.thenCallback=void 0,c&&c()))}}}function tweenStopAll(){for(const a of tweenActive)a.thenCallback=void 0;tweenActive.length=0}engineAddPlugin(tweenUpdate);const PATHFINDER_DIAGONAL_COST=Math.SQRT2,PATHFINDER_TILE_VEC=vec2(1);class PathFinderNode{constructor(a,b){this.pos=vec2(a,b);this.posWorld=vec2();this.walkable=!1;this.f=this.g=this.cost=0;this.parent=null;this.isClosed=this.isOpen=!1}reset(){this.walkable=!1;this.f=this.g=this.cost=0;this.parent=null;this.isClosed=this.isOpen=!1}isClear(){return this.walkable&&0===this.cost}}class PathFinder{constructor(a){isVector2(a)?(this.size=a.floor(),this.tileLayer=void 0):(ASSERT(a&&isVector2(a.size)&&"function"===typeof a.getCollisionData,"PathFinder requires a Vector2 size or a TileCollisionLayer"),this.size=a.size,this.tileLayer=a);this.heuristicWeight=1;this.maxLoop=1e3;this.smoothPath=!0;this.debug=!1;this.debugTime=1;this.nodes=Array(this.size.x*this.size.y);for(a=0;a<this.size.y;++a)for(let b=0;b<this.size.x;++b)this.nodes[b+a*this.size.x]=new PathFinderNode(b,a);this.collisionScratch=vec2()}isWalkable(a,b){return this.tileLayer?!this.tileLayer.getCollisionData(this.collisionScratch.set(a,b)):!0}getCost(a,b){return 0}getNode(a,b){return 0>a||0>b||a>=this.size.x||b>=this.size.y?null:this.nodes[a+b*this.size.x]}worldToTile(a){const b=this.tileLayer?this.tileLayer.pos.y:0;return vec2(floor(a.x-(this.tileLayer?this.tileLayer.pos.x:0)),floor(a.y-b))}tileToWorld(a,b){return vec2(a+.5+(this.tileLayer?this.tileLayer.pos.x:0),b+.5+(this.tileLayer?this.tileLayer.pos.y:0))}buildNodeData(){const a=this.size.x,b=this.size.y,c=this.tileLayer?this.tileLayer.pos.x:0,d=this.tileLayer?this.tileLayer.pos.y:0;for(let e=0;e<b;++e)for(let f=0;f<a;++f){const g=this.nodes[f+e*a];g.reset();const h=!!this.isWalkable(f,e),k=h?max(0,this.getCost(f,e)):0;g.walkable=h;g.cost=k;g.posWorld.set(f+.5+c,e+.5+d);this.debug&&0<this.debugTime&&(h?0<k&&debugRect(g.posWorld,PATHFINDER_TILE_VEC,rgb(1,0,0,min(.2,.05*k)),this.debugTime):debugRect(g.posWorld,PATHFINDER_TILE_VEC,rgb(1,0,0,.25),this.debugTime))}}aStarSearch(a,b){ASSERT(a&&b,"aStarSearch needs both endpoints");ASSERT(a!==b,"aStarSearch: start and end must differ — caller should handle trivial case");ASSERT(a.walkable&&b.walkable,"aStarSearch: endpoints must be walkable");const c=[a];a.isOpen=!0;for(a=0;0<c.length;){var d=0,e=c[0].f;for(var f=1;f<c.length;++f)c[f].f<e&&(e=c[f].f,d=f);e=c[d];if(e===b)break;if(++a>this.maxLoop)break;e.isOpen=!1;c.splice(d,1);e.isClosed=!0;this.debug&&0<this.debugTime&&debugRect(e.posWorld,PATHFINDER_TILE_VEC,rgb(1,1,1,.05),this.debugTime);for(d=-1;1>=d;++d)for(f=-1;1>=f;++f){if(0===f&&0===d)continue;const h=this.getNode(e.pos.x+f,e.pos.y+d);if(!h||!h.walkable||h.isClosed)continue;var g=1;if(0!==f&&0!==d){g=this.getNode(e.pos.x+f,e.pos.y);if(!g||!g.walkable)continue;g=this.getNode(e.pos.x,e.pos.y+d);if(!g||!g.walkable)continue;g=PATHFINDER_DIAGONAL_COST}g=e.g+g+h.cost;if(!h.isOpen)h.isOpen=!0,c.push(h);else if(g>=h.g)continue;h.parent=e;h.g=g;g=abs(b.pos.x-h.pos.x);const k=abs(b.pos.y-h.pos.y);g=max(g,k)+(Math.SQRT2-1)*min(g,k);h.f=h.g+g*this.heuristicWeight}}return null!==b.parent}getNearestClearNode(a,b=10,c=!0){ASSERT(isVector2(a),"worldPos must be a Vector2");c&&this.buildNodeData();var d=this.tileLayer?this.tileLayer.pos.y:0;c=floor(a.x-(this.tileLayer?this.tileLayer.pos.x:0));d=floor(a.y-d);for(let f=0;f<=b;++f){let g=null,h=0;for(let k=-f;k<=f;++k)for(let l=-f;l<=f;++l){if(0<f&&abs(l)!==f&&abs(k)!==f)continue;const m=this.getNode(c+l,d+k);if(!m||!m.isClear())continue;var e=m.posWorld.x-a.x;const n=m.posWorld.y-a.y;e=e*e+n*n;if(!g||e<h)g=m,h=e}if(g)return g}return null}smoothPathCorners(a){if(!(2>=a.length))for(var b=1;b<a.length-1;){var c=a[b-1],d=a[b],e=a[b+1],f=e.pos.x-c.pos.x,g=e.pos.y-c.pos.y,h=f*f+g*g;f=d.pos.x-c.pos.x;var k=d.pos.y-c.pos.y;g=e.pos.x-d.pos.x;var l=e.pos.y-d.pos.y;if(1===h)this.debug&&0<this.debugTime&&debugCircle(d.posWorld,.3,rgb(.5,0,.5,.5),this.debugTime),a.splice(b,1),b=max(1,b-1);else{if(2===h){if(this.debug&&0<this.debugTime&&debugCircle(d.posWorld,.3,rgb(1,0,0,.5),this.debugTime),c.pos.y===d.pos.y&&e.pos.x===d.pos.x?(d=c.pos.x,e=e.pos.y):(d=e.pos.x,e=c.pos.y),(d=this.getNode(d,e))&&d.isClear()){a.splice(b,1);b=max(1,b-1);continue}}else if(5===h){this.debug&&0<this.debugTime&&debugCircle(d.posWorld,.3,rgb(1,1,0,.5),this.debugTime);k=2<=b?a[b-2]:c;0===f||0===g?(f=e.pos.x,g=d.pos.y,h=c.pos.x,c=d.pos.y):(f=d.pos.x,g=e.pos.y,h=d.pos.x,c=c.pos.y);var m=f-k.pos.x;const n=g-k.pos.y;l=h-k.pos.x;k=c-k.pos.y;m=m*m+n*n;k=l*l+k*k;if((k=this.getNode(m<k?f:f===h&&g===c?f:h,m<k?g:f===h&&g===c?g:c))&&k!==d&&k.isClear()&&(d=this.getNode(e.pos.x+h-f,e.pos.y+c-g))&&d.isClear()){a[b]=k;b=max(1,b-1);continue}}else if(4===h||8===h)if(this.debug&&0<this.debugTime&&debugCircle(d.posWorld,.3,rgb(0,1,0,.5),this.debugTime),f===g&&k===l){++b;continue}else if(c.pos.y===e.pos.y?(e=d.pos.x,d=c.pos.y):(e=c.pos.x,d=d.pos.y),(d=this.getNode(e,d))&&d.isClear()){a[b]=d;b=max(1,b-1);continue}++b}}}smoothPathStringPull(a){if(!(2>=a.length)){for(var b of a)if(!b.isClear())return;b=a.slice();a.length=0;a.push(b[0]);var c=0;for(let f=1;f<b.length;++f){const g=b[f];var d=b[c],e=b[f-1];if((d===e||0!==(e.pos.x-d.pos.x)*(g.pos.y-d.pos.y)-(e.pos.y-d.pos.y)*(g.pos.x-d.pos.x))&&!this.isLineClear(g.pos,a[a.length-1].pos)){d=!1;for(e=f+1;e<b.length;++e)if(this.isLineClear(b[e].pos,a[a.length-1].pos)){d=!0;break}if(d)this.debug&&0<this.debugTime&&debugLine(g.posWorld,a[a.length-1].posWorld,rgb(0,0,1,.3),.02,this.debugTime);else{for(;c<b.length;++c)if(d=b[c],this.isLineClear(g.pos,d.pos)){a.push(d);f=c;break}ASSERT(c<b.length,"smoothPathStringPull: ran out of candidates")}}}a.push(b[b.length-1])}}dropCollinearNodes(a){for(let b=a.length-2;1<=b;--b){const c=a[b-1],d=a[b],e=a[b+1];(d.pos.x-c.pos.x)*(e.pos.y-c.pos.y)===(d.pos.y-c.pos.y)*(e.pos.x-c.pos.x)&&a.splice(b,1)}}isNodeClear(a,b){a=this.getNode(a,b);return null!==a&&a.isClear()}isLineClear(a,b){ASSERT(isVector2(a)&&isVector2(b),"isLineClear needs Vector2 endpoints");ASSERT(this.isNodeClear(a.x,a.y)&&this.isNodeClear(b.x,b.y),"isLineClear endpoints must be in-bounds and clear");const c=b.x-a.x,d=b.y-a.y;var e=abs(c);const f=abs(d),g=sign(c),h=sign(d);let k=a.x,l=a.y;if(f===e){for(;k!==b.x;){if(!(k===a.x||this.isNodeClear(k,l)&&this.isNodeClear(k,l-h))||!this.isNodeClear(k,l+h))return!1;k+=g;l+=h}if(!this.isNodeClear(b.x,b.y-h))return!1}else if(f<e)if(0===d)for(k+=g;k!==b.x;){if(!this.isNodeClear(k,l))return!1;k+=g}else{for(e=a.y;k!==b.x;){l=a.y+Math.trunc(d*(k-a.x)/c);if(!(e===l||this.isNodeClear(k-g,l+h)&&this.isNodeClear(k,l-h)))return!1;e=l;if(k!==a.x&&!this.isNodeClear(k,l))return!1;l+=h;if(!this.isNodeClear(k,l))return!1;k+=g}if(!this.isNodeClear(b.x,b.y-h))return!1}else if(0===c)for(l+=h;l!==b.y;){if(!this.isNodeClear(k,l))return!1;l+=h}else{for(e=a.x;l!==b.y;){k=a.x+Math.trunc(c*(l-a.y)/d);if(!(e===k||this.isNodeClear(k+g,l-h)&&this.isNodeClear(k-g,l)))return!1;e=k;if(l!==a.y&&!this.isNodeClear(k,l))return!1;k+=g;if(!this.isNodeClear(k,l))return!1;l+=h}if(!this.isNodeClear(b.x-g,b.y))return!1}return!0}findPath(a,b){ASSERT(isVector2(a)&&isVector2(b),"findPath needs Vector2 endpoints");this.buildNodeData();a=this.getNearestClearNode(a,10,!1);b=this.getNearestClearNode(b,10,!1);if(!a||!b)return[];if(a===b)return[a.posWorld.copy()];if(!this.aStarSearch(a,b))return[];for(a=[];b;b=b.parent)a.push(b);a.reverse();this.smoothPath&&(this.smoothPathCorners(a),this.smoothPathStringPull(a),this.dropCollinearNodes(a));b=a.map(c=>c.posWorld.copy());if(this.debug&&0<this.debugTime&&0<b.length){for(a=1;a<b.length;++a)debugLine(b[a-1],b[a],RED,.1,this.debugTime);for(const c of b)debugCircle(c,.5,rgb(1,0,0,.3),this.debugTime);debugCircle(b[0],.5,rgb(0,1,0,.5),this.debugTime);debugCircle(b[b.length-1],.5,rgb(0,1,0,.5),this.debugTime)}return b}}let threeJS;class ThreeJSPlugin{constructor(a,b=60){ASSERT(!threeJS,"ThreeJS plugin already initialized");threeJS=this;headlessMode||(ASSERT(mainCanvas,"ThreeJS plugin must be created after engineInit, call in gameInit"),ASSERT(a&&a.WebGLRenderer,"three.js module must be passed in"),this.THREE=a,this.renderer=new a.WebGLRenderer({antialias:!0}),this.scene=new a.Scene,this.camera=new a.PerspectiveCamera(b,1,.1,1e3),this.cameraAlign2D=!0,a=this.renderer.domElement,b=mainCanvas.parentElement,b.insertBefore(a,b.firstChild),a.style.cssText=mainCanvas.style.cssText,setBackgroundCanvas(a),engineAddPlugin(void 0,()=>this.render()))}alignCamera2D(){const a=mainCanvasSize.y/2/cameraScale/tan(this.camera.fov/2*PI/180);this.camera.position.set(cameraPos.x,cameraPos.y,a);this.camera.rotation.set(0,0,-cameraAngle)}render(){if(this.renderer){var a=this.renderer.domElement;if(a.width!=mainCanvasSize.x||a.height!=mainCanvasSize.y)this.renderer.setSize(mainCanvasSize.x,mainCanvasSize.y,!1),this.camera.aspect=mainCanvasSize.x/mainCanvasSize.y,this.camera.updateProjectionMatrix();a.style.cssText!=mainCanvas.style.cssText&&(a.style.cssText=mainCanvas.style.cssText);this.cameraAlign2D&&this.alignCamera2D();this.renderer.render(this.scene,this.camera)}}}class ThreeJSObject extends EngineObject{constructor(a,b,c,d=0){super(a,b);ASSERT(threeJS,"ThreeJS plugin must be initialized first");this.mesh=c;this.z=d;c&&(threeJS.scene.add(c),this.syncMesh())}update(){super.update();this.syncMesh()}syncMesh(){this.mesh&&(this.mesh.position.set(this.pos.x,this.pos.y,this.z),this.mesh.rotation.z=-this.angle)}render(){}destroy(a){this.destroyed||(this.mesh&&threeJS.scene.remove(this.mesh),super.destroy(a))}}
4
+ const engineName="LittleJS",engineVersion="1.19.3",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,windowWidthLast=0,windowHeightLast=0,windowPixelRatioLast=0,engineUpdateInternal,showEngineVersion=!0;const pluginList=[];class EnginePlugin{constructor(a,b,c,d,e){this.update=a;this.render=b;this.glContextLost=c;this.glContextRestored=d;this.preRender=e}}function engineAddPlugin(a,b,c,d,e){ASSERT(!pluginList.find(g=>g.update===a&&g.render===b&&g.glContextLost===c&&g.glContextRestored===d&&g.preRender===e));const f=new EnginePlugin(a,b,c,d,e);pluginList.push(f)}async function engineInit(a,b,c,d,e,f=[],g){function h(){mainContext.imageSmoothingEnabled=!tilesPixelated;glPreRender();pluginList.forEach(m=>m.preRender?.())}function k(m=0){function n(){if(!headlessMode){q||engineUpdateCanvas();h();d();engineObjects.sort((t,v)=>t.renderOrder-v.renderOrder);for(const t of engineObjects)t.destroyed||(setShader(t.shader),t.render());setShader();e();pluginList.forEach(t=>t.render?.());inputRender();debugRender();glFlush();debugRenderPost();primitiveCount=drawCount=0}}var p=m-frameTimeLastMS;frameTimeLastMS||(p=0);frameTimeLastMS=m;if(debug||debugWatermark)averageFPS=lerp(averageFPS,1e3/(p||1),.05);m=debug&&keyIsDown("Equal");const r=debug&&keyIsDown("Minus");m=m?10:r?.1:1;timeReal+=p*m/1e3;m*=timeScale;frameTimeBufferMS+=paused?p:p*m;if(paused||1>=m)frameTimeBufferMS=min(frameTimeBufferMS,50);let q=!1;p=0;0>frameTimeBufferMS&&-9<frameTimeBufferMS&&(p=frameTimeBufferMS,frameTimeBufferMS=0);for(;0<=frameTimeBufferMS;frameTimeBufferMS-=1e3/frameRate){paused||(time=frame++/frameRate);q=!0;engineUpdateCanvas();inputUpdate();paused||b();pluginList.forEach(t=>t.update?.());if(paused)for(var u of engineObjects)u.parent||u.updateTransforms();else engineObjectsUpdate();debugUpdate();c();inputUpdatePost();debugVideoCaptureIsActive()&&n()}frameTimeBufferMS+=p;u=!1;headlessMode||(p=devicePixelRatio,u=windowWidthLast!==innerWidth||windowHeightLast!==innerHeight||windowPixelRatioLast!==p,windowWidthLast=innerWidth,windowHeightLast=innerHeight,windowPixelRatioLast=p);debugVideoCaptureIsActive()||!q&&!u||n();engineManualStep||requestAnimationFrame(k)}async function l(){await a();engineManualStep||k()}showEngineVersion&&console.log(`${engineName} Engine v${engineVersion}`);ASSERT(!mainContext,"engine already initialized");if(!mainContext){ASSERT(isArray(f),"pass in images as array");document.body||document.documentElement.appendChild(document.createElement("body"));g||=document.body;a||=()=>{};b||=()=>{};c||=()=>{};d||=()=>{};e||=()=>{};engineUpdateInternal=k;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);engineUpdateCanvas();glPreRender();workContext=createCanvasContext(64);workCanvas=workContext.canvas;workReadContext=createCanvasContext(64,64,!0);workReadCanvas=workReadContext.canvas;g=f.map((m,n)=>loadTexture(n,m));f.length||g.push(loadTexture(0));g.push(imageFontInit());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 engineUpdateCanvas(){if(!headlessMode){var a=getCanvasPixelRatio();if(canvasFixedSize.x){mainCanvasSize=canvasFixedSize.copy();var b=innerWidth/innerHeight;const d=canvasFixedSize.x/canvasFixedSize.y;var c=b<d?"100%":"";b=b<d?"":"100%";mainCanvas.style.width=c;mainCanvas.style.height=b;glCanvas&&(glCanvas.style.width=c,glCanvas.style.height=b)}else mainCanvasSize.x=min(innerWidth,canvasMaxSize.x)|0,mainCanvasSize.y=min(innerHeight,canvasMaxSize.y)|0,c=innerWidth/innerHeight,ASSERT(canvasMinAspect<=canvasMaxAspect),canvasMaxAspect&&c>canvasMaxAspect?mainCanvasSize.x=min(mainCanvasSize.y*canvasMaxAspect|0,canvasMaxSize.x):c<canvasMinAspect&&(mainCanvasSize.y=min(mainCanvasSize.x/canvasMinAspect|0,canvasMaxSize.y)),mainCanvas.style.width=mainCanvasSize.x+"px",mainCanvas.style.height=mainCanvasSize.y+"px",glCanvas&&(glCanvas.style.width=mainCanvasSize.x+"px",glCanvas.style.height=mainCanvasSize.y+"px");c=mainCanvasSize.x*a|0;b=mainCanvasSize.y*a|0;mainCanvas.width!==c||mainCanvas.height!==b?(mainCanvas.width=c,mainCanvas.height=b):(mainContext.setTransform(1,0,0,1,0,0),mainContext.globalCompositeOperation="source-over",mainContext.clearRect(0,0,c,b));mainContext.setTransform(a,0,0,a,0,0);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"}}const engineStepMaxFrames=36e3;function engineStep(a=1){ASSERT(engineManualStep,"engineStep requires setEngineManualStep(true) before engineInit");ASSERT(engineUpdateInternal,"engineStep requires engineInit to complete");if(engineManualStep&&engineUpdateInternal)for(ASSERT(Number.isInteger(a)&&0<=a&&a<=engineStepMaxFrames,"engineStep requires a whole frame count from 0 to "+engineStepMaxFrames),a=min(a,engineStepMaxFrames);0<a;--a)engineUpdateInternal(frameTimeLastMS+1e3/frameRate)}function engineObjectsUpdate(){function a(b){if(!b.destroyed){b.update();for(const c of b.children)a(c)}}engineObjectsCollide=engineObjects.filter(b=>b.collideSolidObjects);for(const b of engineObjects)b.parent||b.destroyed||b.updatePhysics();for(const b of engineObjects)if(!b.parent&&!b.destroyed){b.update();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.persistent||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}let debugWatermark=0,debugKey="";const debug=0,debugOverlay=0,debugPhysics=0,debugParticles=0,debugRaycast=0,debugGamepads=0,debugSound=0,debugPointSize=.5;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=a=>Math.sign(a),hypot=(...a)=>Math.hypot(...a),log2=a=>Math.log2(a),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){ASSERT(0<c,"distanceWrap wrapSize must be > 0");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 0<a&&!(a&a-1)}function nearestPowerOfTwo(a){return 2**ceil(log2(a))}function isOverlapping(a,b,c,d=vec2()){const e=2*(a.y-c.y),f=b.x+d.x;b=b.y+d.y;return abs(2*(a.x-c.x))<f&&abs(e)<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 collideCircleCircle(a,b,c,d){a=a.subtract(c);b+=d;d=a.length();if(!(d>=b))return a.normalize(b-d)}function collideCircleBox(a,b,c,d){d=d.scale(.5);var e=vec2(clamp(a.x,c.x-d.x,c.x+d.x),clamp(a.y,c.y-d.y,c.y+d.y));e=a.subtract(e);const f=e.lengthSquared();if(f)return f>=b*b?void 0:e.normalize(b-f**.5);a=a.subtract(c);return pushOutAxis(a,d.x-abs(a.x),d.y-abs(a.y),b)}function collideBoxBox(a,b,c,d){a=a.subtract(c);c=(b.x+d.x)/2-abs(a.x);b=(b.y+d.y)/2-abs(a.y);if(!(0>=c||0>=b))return pushOutAxis(a,c,b)}function pushOutAxis(a,b,c,d=0){return b<=c?vec2((0<=a.x?1:-1)*(b+d),0):vec2(0,(0<=a.y?1:-1)*(c+d))}function oscillate(a=1,b=1,c=time,d=0,e=0){a=mod(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 isStringLike(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=(e*e+f*f)**.5;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,r=0;n=h?(n-a.x)/h:Infinity;var q=k?(p-a.y)/k:Infinity;for(p=l<m;r<g;){if(c(b))return a=vec2(a.x+h*r,a.y+k*r),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<q)?(b.x+=e,r=n,n+=l):(b.y+=f,r=q,q+=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){if(0>=a)return new Vector2;b=clamp(b/a);return randVec2(a*rand(b*b,1)**.5)}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){ASSERT(0!==a,"RandomGenerator seed must be non-zero (xorshift is fixed at 0)");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){const c=min(a,b);a=max(a,b)-c;b=this.float(2*a);return b<a?c+b:a-c-b}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)}setAlpha(a=1){this.a=a;ASSERT_COLOR_VALID(this);return this}copy(){return new Color(this.r,this.g,this.b,this.a)}withAlpha(a=1){return new Color(this.r,this.g,this.b,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(isStringLike(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()?this.setTime?1-percent(this.time-this.getGlobalTime(),0,this.setTime):1: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 createCanvasContext(a,b=a,c=!1){ASSERT(isNumber(a)&&isNumber(b),"canvas width and height must be numbers",a,b);return new OffscreenCanvas(a,b).getContext("2d",{willReadFrequently:c})}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(isStringLike(a),"saveDataURL requires url string");ASSERT(isStringLike(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(isStringLike(a),"shareURL requires title string");ASSERT(isStringLike(b),"shareURL requires url string");navigator.share?.({title:a,url:b}).then(()=>c?.())}function readSaveData(a,b){ASSERT(isStringLike(a),"readSaveData requires saveName string");ASSERT(void 0===b||"object"===typeof b&&null!==b,"readSaveData: default must be an object - the result is {...default, ...loaded}, so a scalar default yields {}. Use readSaveData(key, {best:0}).best");let c={};try{const d=localStorage[a];if(d)try{c=JSON.parse(d)}catch{LOG("readSaveData: corrupt JSON for",a,"— using defaults")}}catch{LOG("readSaveData: localStorage unavailable — using defaults")}return{...b,...c}}function writeSaveData(a,b){ASSERT(isStringLike(a),"writeSaveData requires saveName string");try{localStorage[a]=JSON.stringify(b)}catch{LOG("writeSaveData: failed to write",a)}}function noiseHash(a){a=(a|0)^2654435769;a=Math.imul(a^a>>>16,2246822507);a=Math.imul(a^a>>>13,3266489909);return((a^a>>>16)>>>0)/2**32}function noise1D(a){const b=floor(a);return lerp(noiseHash(b),noiseHash(b+1),smoothStep(a-b))}function noise2D(a,b){const c=floor(a),d=floor(b);a=smoothStep(a-c);b=smoothStep(b-d);return lerp(lerp(noiseHash(c+374761393*d),noiseHash(c+1+374761393*d),a),lerp(noiseHash(c+374761393*(d+1)),noiseHash(c+1+374761393*(d+1)),a),b)}let cameraPos=vec2(),cameraAngle=0,cameraScale=32,timeScale=1,canvasColorTiles=!0,canvasClearColor=CLEAR_BLACK,canvasMaxSize=vec2(3840,2160),canvasMinAspect=0,canvasMaxAspect=0,canvasFixedSize=vec2(),canvasPixelated=!1,tilesPixelated=!0,canvasPixelRatio=1,fontDefault="arial",showSplashScreen=!1,headlessMode=!1,engineManualStep=!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,gamepadAxisFilterEnable=!0,inputWASDEmulateDirection=!0,touchInputEnable=!0,touchGamepadEnable=!1,touchGamepadPassthrough=!1,touchGamepadCenterButtonSize=0,touchGamepadButtonCount=4,touchGamepadLeftStick=!0,touchGamepadLeftButtonCount=0,touchGamepadRightStick=!1,touchGamepadAnalog=!0,touchGamepadFloating=!1,touchGamepadSize=100,touchGamepadAlpha=.3,touchGamepadDisplayTime=3,touchGamepadVibration=0,vibrateEnable=!0,soundEnable=!0,soundVolume=.3,soundDefaultRange=40,soundDefaultTaper=.7,soundPauseWhenHidden=!0;function setCameraPos(a){cameraPos=a.copy()}function setCameraAngle(a){cameraAngle=a}function setCameraScale(a){cameraScale=a}function setTimeScale(a){timeScale=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 setCanvasPixelRatio(a){canvasPixelRatio=a}function getCanvasPixelRatio(){return canvasPixelRatio??(devicePixelRatio||1)}function setFontDefault(a){fontDefault=a}function setShowSplashScreen(a){showSplashScreen=a}function setHeadlessMode(a){headlessMode=a}function setEngineManualStep(a=!0){engineManualStep=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 setGamepadAxisFilterEnable(a){gamepadAxisFilterEnable=a}function setInputWASDEmulateDirection(a){inputWASDEmulateDirection=a}function setTouchInputEnable(a){touchInputEnable=a}function setTouchGamepadEnable(a){touchGamepadEnable=a}function setTouchGamepadPassthrough(a){touchGamepadPassthrough=a}function setTouchGamepadCenterButtonSize(a){touchGamepadCenterButtonSize=a}function setTouchGamepadButtonCount(a){touchGamepadButtonCount=a;0<a&&(touchGamepadRightStick=!1)}function setTouchGamepadLeftStick(a){(touchGamepadLeftStick=a)&&(touchGamepadLeftButtonCount=0)}function setTouchGamepadLeftButtonCount(a){touchGamepadLeftButtonCount=a;0<a&&(touchGamepadLeftStick=!1)}function setTouchGamepadRightStick(a){(touchGamepadRightStick=a)&&(touchGamepadButtonCount=0)}function setTouchGamepadAnalog(a){touchGamepadAnalog=a}function setTouchGamepadFloating(a){touchGamepadFloating=a}function setTouchGamepadSize(a){touchGamepadSize=a}function setTouchGamepadAlpha(a){touchGamepadAlpha=a}function setTouchGamepadDisplayTime(a){touchGamepadDisplayTime=a}function setTouchGamepadVibration(a){touchGamepadVibration=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 setSoundPauseWhenHidden(a){soundPauseWhenHidden=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.shader=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.persistent=this.collideRaycast=this.isSolid=this.collideSolidObjects=this.collideTiles=!1;engineObjects.push(this)}updateTransforms(){var a=this.parent;if(a){const c=a.getMirrorSign();var b=this.localPos;const d=a.pos,e=b.x*c;b=b.y;if(a=a.angle){const f=cos(-a),g=sin(-a);this.pos.set(e*f-b*g+d.x,e*g+b*f+d.y)}else this.pos.set(e+d.x,b+d.y);this.angle=c*this.localAngle+a}for(const c of this.children)c.updateTransforms()}updatePhysics(){ASSERT(!this.parent);if(!this.destroyed){this.clampSpeed&&(this.velocity.x=clamp(this.velocity.x,-objectMaxSpeed,objectMaxSpeed),this.velocity.y=clamp(this.velocity.y,-objectMaxSpeed,objectMaxSpeed));var 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&&this.size.x&&this.size.y)for(var e of engineObjectsCollide)if(!e.destroyed&&!e.parent&&e!==this&&e.size.x&&e.size.y&&(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=0<(0<gravity.y?-1:1)?floor(a.y-this.size.y/2+1)+this.size.y/2+.001:ceil(a.y+this.size.y/2-1)-this.size.y/2-.001;if(.1>abs(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)}renderLight(){}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,b){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(!this.destroyed,"cannot add child to destroyed object");if(this.destroyed)return a;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;a.updateTransforms();return a}removeChild(a){ASSERT(a.parent===this&&this.children.includes(a));this.children.splice(this.children.indexOf(a),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(){let a="type = "+this.constructor.name;if(this.pos.x||this.pos.y)a+="\npos = "+this.pos;if(this.velocity.x||this.velocity.y)a+="\nvelocity = "+this.velocity;if(this.size.x||this.size.y)a+="\nsize = "+this.size;this.angle&&(a+="\nangle = "+this.angle.toFixed(3));this.color&&(a+="\ncolor = "+this.color);return a}renderDebugInfo(){if(debug){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,backgroundCanvas,mainCanvasSize=vec2(),textureInfos=[],drawCount,primitiveCount;function isWhite(a){return 1<=a.r&&1<=a.g&&1<=a.b}function isBlack(a){return 0>=a.r&&0>=a.g&&0>=a.b&&0>=a.a}function tile(a=0,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,f=0){this.pos=a.copy();this.size=b.copy();this.padding=d;this.textureInfo=c;this.bleed=e;this.columns=f}offset(a){return new TileInfo(this.pos.add(a),this.size,this.textureInfo,this.padding,this.bleed,this.columns)}frame(a){ASSERT("number"===typeof a);const b=(this.columns?a%this.columns:a)*(this.size.x+2*this.padding);a=(this.columns?a/this.columns|0:0)*(this.size.y+2*this.padding);ASSERT(this.pos.x+b+this.size.x<=this.textureInfo.size.x,"frame extends beyond texture width!");ASSERT(this.pos.y+a+this.size.y<=this.textureInfo.size.y,"frame extends beyond texture height!");return this.offset(new Vector2(b,a))}setColumns(a=0){ASSERT(isNumber(a)&&0<=a,"columns must be a number >= 0");this.columns=a;return this}index(a){return tile(a,this.size,this.textureInfo,this.padding,this.bleed).setColumns(this.columns)}setFullImage(a=this.textureInfo){this.textureInfo=a;this.pos=new Vector2;this.size=a.size.copy();this.bleed=this.padding=this.columns=0;return this}}class TextureInfo{constructor(a,b=!0,c=!1){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;this.wrap=c;b&&this.createWebGLTexture()}createWebGLTexture(){glRegisterTextureInfo(this)}destroyWebGLTexture(){glUnregisterTextureInfo(this)}hasWebGL(){return!!this.glTexture}setWrap(a=!0){this.wrap=a;glSetTextureWrap(this.glTexture,a)}}class SpriteAnimation{constructor(a,b,c=.1){ASSERT(a instanceof TileInfo,"the first frame must be a TileInfo");ASSERT(1<=b&&0<c,"an animation needs at least one frame and a positive frame time");this.firstTile=a;this.frameCount=b;this.frameTime=c;this.speed=1;this.mode="loop";this.startTime=time;this.heldFrame=void 0}loop(){return this.restart("loop")}play(){return this.restart("once")}pingPong(){return this.restart("pingPong")}stop(){this.heldFrame=this.frame;return this}restart(a=this.mode){this.mode=a;this.startTime=time;this.heldFrame=void 0;return this}get elapsedFrames(){return(time-this.startTime)*this.speed/this.frameTime}get frame(){if(void 0!==this.heldFrame)return this.heldFrame;const a=this.frameCount;var b=floor(this.elapsedFrames);if("once"==this.mode)return min(b,a-1);if("loop"==this.mode)return b%a;const c=max(2*a-2,1);b%=c;return b<a?b:c-b}get tileInfo(){return this.firstTile.frame(this.frame)}get isDone(){return"once"==this.mode&&void 0===this.heldFrame&&this.elapsedFrames>=this.frameCount}}class Shader{constructor(a){ASSERT(isStringLike(a)&&String(a).includes("mainImage"),"a Shader needs fragment code that defines mainImage");this.fragmentCode=String(a);this.program3D=this.program=void 0;glShaderObjects.push(this)}}function drawTile(a,b=vec2(1),c,d=WHITE,e=0,f,g,h=glEnable,k=!1,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 r=c.size.y*p.y;glSetTexture(m.glTexture);if(n){const q=p.x*n;p=p.y*n;glDraw(a.x,a.y,f?-b.x:b.x,b.y,e,h+q,k+p,h-q+l,k-p+r,d.rgbaInt(),g&&g.rgbaInt())}else glDraw(a.x,a.y,f?-b.x:b.x,b.y,e,h,k,h+l,k+r,d.rgbaInt(),g&&g.rgbaInt())}else f=g?d.add(g):d,glDrawUntextured(a.x,a.y,b.x,b.y,e,f.rgbaInt());else++drawCount,++primitiveCount,drawCanvas2D(a,b,e,f,r=>{if(m)r.scale(1,-1),drawImageColor(r,m.image,c.pos.x,c.pos.y,c.size.x,c.size.y,-.5,-.5,1,1,d,g,n);else{const q=g?d.add(g):d;r.fillStyle=q.toString();r.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=CLEAR_WHITE,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,r=n&2?b:-b,q=n&2?k:l;f.push(vec2(a.x+(p*m-r*e),a.y+(p*e+r*m)));g.push(q)}glDrawColoredPoints(f,g)}else++drawCount,++primitiveCount,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 drawTextureWrapped(a,b,c,d=0,e=WHITE,f=0,g,h=glEnable,k=!1,l){ASSERT(isVector2(a),"pos must be a vec2");ASSERT(isVector2(b),"size must be a vec2");ASSERT(isVector2(c),"wrapCount must be a vec2");ASSERT(isColor(e),"color is invalid");ASSERT(isNumber(f),"angle must be a number");ASSERT(!g||isColor(g),"additiveColor must be a color");ASSERT(!l||!h,"context only supported in canvas 2D mode");ASSERT(!(d instanceof TileInfo),"pass a TextureInfo or texture index, not a TileInfo — use tileInfo.textureInfo");headlessMode||(d="number"===typeof d?textureInfos[d]:d,ASSERT(d instanceof TextureInfo,"texture not loaded"),ASSERT(0<d.size.x,"texture not loaded"),ASSERT(d.wrap,"drawTextureWrapped requires a wrap-enabled texture; call textureInfo.setWrap(true) first"),h&&glEnable?(ASSERT(!!glContext,"WebGL is not enabled!"),k&&([a,b,f]=screenToWorldTransform(a,b,f)),glSetTexture(d.glTexture),glDraw(a.x,a.y,b.x,b.y,f,0,0,c.x,c.y,e.rgbaInt(),g&&g.rgbaInt())):(++drawCount,++primitiveCount,k||(a=worldToScreen(a),b=b.scale(cameraScale),f-=cameraAngle),k=!canvasColorTiles||(g?isWhite(e.add(g))&&0>=g.a:isWhite(e)),h=!k&&g&&!isBlack(g),g=k?d.image:bakeTintedImage(d.image,e,g),l=l||drawContext,l.save(),l.translate(a.x+.5,a.y+.5),l.rotate(f),l.globalAlpha=h?1:e.a,a=l.createPattern(g,"repeat"),c=(new DOMMatrix).translate(-b.x/2,-b.y/2).scale(b.x/(c.x*g.width),b.y/(c.y*g.height)),a.setTransform(c),l.fillStyle=a,l.fillRect(-b.x/2,-b.y/2,b.x,b.y),l.globalAlpha=1,l.restore()))}function drawLineList(a,b=.1,c=WHITE,d=!1,e=vec2(),f=0,g=glEnable,h=!1,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,++primitiveCount,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=WHITE,e=vec2(),f=0,g=glEnable,h=!1,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 r=p/c*PI*2;m.push(vec2(sin(r)*n,cos(r)*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,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)}let drawEllipseGradientOffset=0;function drawEllipseGradient(a,b=vec2(1),c=WHITE,d=CLEAR_WHITE,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(!headlessMode)if(f&&glEnable){ASSERT(!!glContext,"WebGL is not enabled!");g&&(a=screenToWorld(a),b=b.scale(1/cameraScale),e+=cameraAngle);f=glCircleSides;const k=b.x/2,l=b.y/2;b=c.rgbaInt();g=d.rgbaInt();h=drawEllipseGradientOffset++;const m=cos(-e),n=sin(-e);e=q=>{const u=sin(q)*k;q=cos(q)*l;return vec2(a.x+u*m-q*n,a.y+u*n+q*m)};const p=[e(h%f/f*PI*2)],r=[g];for(let q=f;q--;){const u=(q+h)%f/f*PI*2;p.push(a);r.push(b);p.push(e(u));r.push(g)}glDrawColoredPoints(p,r)}else++drawCount,++primitiveCount,drawCanvas2D(a,b,e,!1,k=>{const l=k.createRadialGradient(0,0,0,0,0,.5);l.addColorStop(0,c.toString());l.addColorStop(1,d.toString());k.fillStyle=l;k.beginPath();k.ellipse(0,0,.5,.5,0,0,9);k.fill()},g,h)}function drawCircleGradient(a,b=1,c=WHITE,d=CLEAR_WHITE,e=glEnable,f=!1,g){ASSERT(isNumber(b),"size must be a number");drawEllipseGradient(a,vec2(b),c,d,0,e,f,g)}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=WHITE,e=0,f=BLACK,g="center",h=fontDefault,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(isStringLike(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(isStringLike(h),"font must be a string");ASSERT(isStringLike(k),"fontStyle must be a string");ASSERT(isNumber(m),"angle must be a number");a=(a+"").split("\n");const p=b.y-(a.length-1)*c/2;n.save();n.fillStyle=d.toString();n.strokeStyle=f.toString();n.lineWidth=e;n.textAlign=g;n.font=k+" "+c+"px "+h;n.textBaseline="middle";n.translate(b.x,p);n.rotate(-m);let r=0;a.forEach(q=>{e&&n.strokeText(q,0,r,l);n.fillText(q,0,r,l);r+=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||isStringLike(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 cameraFit(a,b,c,d){function e(k){if(void 0===k||isNumber(k))k=vec2(k);return isVector2(k)?{top:k.y,right:k.x,bottom:k.y,left:k.x}:{top:k.top||0,right:k.right||0,bottom:k.bottom||0,left:k.left||0}}ASSERT(isVector2(a),"center must be a vec2");ASSERT(isVector2(b),"size must be a vec2");c=e(c);d=e(d);const f=b.x+c.left+c.right;b=b.y+c.top+c.bottom;const g=mainCanvasSize.x-d.left-d.right,h=mainCanvasSize.y-d.top-d.bottom;if(!(0<f&&0<b&&0<g&&0<h))return cameraScale;cameraScale=min(g/f,h/b);c=vec2(c.right-c.left,c.top-c.bottom).scale(.5);d=vec2(d.right-d.left,d.top-d.bottom).scale(.5/cameraScale);cameraPos=a.add(c).add(d);return 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");if(!cameraScale)return!1;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 setAdditiveBlendMode(a=!0){glAdditive=a;drawContext.globalCompositeOperation=a?"lighter":"source-over"}function setShader(a){ASSERT(!a||a instanceof Shader,"shader must be a Shader");glCustomShader=a||void 0}function setBackgroundCanvas(a){backgroundCanvas=a}function combineCanvases(){const a=mainCanvas.width,b=mainCanvas.height;workCanvas.width=a;workCanvas.height=b;workContext.fillStyle="#000";workContext.fillRect(0,0,a,b);backgroundCanvas&&workContext.drawImage(backgroundCanvas,0,0,a,b);glCopyToContext(workContext);workContext.drawImage(mainCanvas,0,0);mainContext.save();mainContext.setTransform(1,0,0,1,0,0);mainContext.drawImage(workCanvas,0,0);mainContext.restore()}function bakeTintedImage(a,b,c){var d=a.width|0,e=a.height|0;workReadCanvas.width=d;workReadCanvas.height=e;workReadContext.drawImage(a,0,0);a=workReadContext.getImageData(0,0,d,e);d=a.data;if(c&&!isBlack(c))for(b=[b.r,b.g,b.b,b.a],c=[255*c.r,255*c.g,255*c.b,255*c.a],e=0;e<d.length;++e)d[e]=d[e]*b[e&3]+c[e&3]|0;else for(c=0;c<d.length;c+=4)d[c]*=b.r,d[c+1]*=b.g,d[c+2]*=b.b;workReadContext.putImageData(a,0,0);return workReadCanvas}function drawImageColor(a,b,c,d,e,f,g,h,k,l,m,n,p=0){e=max(1,e|0);f=max(1,f|0);const r=e-2*p,q=f-2*p;if(!canvasColorTiles||(n?isWhite(m.add(n))&&0>=n.a:isWhite(m)))a.globalAlpha=m.a,a.drawImage(b,c+p,d+p,r,q,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&&!isBlack(n)){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,q,g,h,k,l)}else{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,q,g,h,k,l);a.globalAlpha=1}}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 engineImageFont;class ImageFont{constructor(a){ASSERT(!!a,"tileInfo is required for ImageFont");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(isStringLike(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((r,q)=>{const u=d?(r.length-1)*c.x/2:0;for(let v=r.length;v--;){var t=r.charCodeAt(v);t=32>t||127<t?95:t-32;const x=t/p|0;k.pos.x=t%p*m+l;k.pos.y=x*n+l;h.x=ceil(b.x+v*c.x-u-c.x/2)+c.x/2-.5;h.y=ceil(b.y+q*c.y-c.y/2)+c.y/2-.5;drawTile(h,c,k,e,0,!1,void 0,f,!0,g)}})}}async function imageFontInit(){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);engineImageFont=new ImageFont(b)}let mousePos=vec2(),mousePosScreen=vec2(),mouseDelta=vec2(),mouseDeltaScreen=vec2(),mouseWheel=0,mouseInWindow=!0,isUsingGamepad=!1,lastInputDevice="mouse",inputMouseMoveThreshold=6,inputPreventDefault=!0,gamepadPrimary=0;const isTouchDevice=!headlessMode&&void 0!==window.ontouchstart;function setInputPreventDefault(a=!0){inputPreventDefault=a}function setInputMouseMoveThreshold(a){inputMouseMoveThreshold=a}function usingMouseInput(){return"mouse"===lastInputDevice}function usingKeyboardInput(){return"keyboard"===lastInputDevice}function usingGamepadInput(){return"gamepad"===lastInputDevice}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;touchGamepadStickPointerId.length=0;gamepadStickData.length=0;gamepadDpadData.length=0;gamepadAxisCentered.length=0}function keyIsDown(a,b=0){ASSERT(isStringLike(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(isStringLike(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(isStringLike(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(isStringLike(a),"up key must be a string");ASSERT(isStringLike(b),"down key must be a string");ASSERT(isStringLike(c),"left key must be a string");ASSERT(isStringLike(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 gamepadVibrate(a=gamepadPrimary,b=200,c=1,d=1,e=0){ASSERT(isNumber(a),"gamepad must be a number");vibrateEnable&&!headlessMode&&navigator?.getGamepads?.()[a]?.vibrationActuator?.playEffect?.("dual-rumble",{duration:b,strongMagnitude:c,weakMagnitude:d,startDelay:e})}function gamepadVibrateStop(a=gamepadPrimary){ASSERT(isNumber(a),"gamepad must be a number");vibrateEnable&&!headlessMode&&navigator?.getGamepads?.()[a]?.vibrationActuator?.reset?.()}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=[],gamepadAxisCentered=[],gamepadAxisCenteredFrames=15,touchGamepadTimer=new Timer,touchGamepadButtons=[],touchGamepadSticks=[],touchGamepadStickAnchors=[],touchGamepadStickPointerId=[],touchGamepadPointerRole=new Map;let touchGamepadOverlay,touchGamepadStage,touchGamepadSvg,touchGamepadSvgEls,touchGamepadSideZones=[],touchGamepadZoneC,touchGamepadNeedRelayout=!0,touchGamepadLastLayout;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(g){if(touchInputEnable){soundEnable&&!headlessMode&&audioContext&&!audioIsRunning()&&audioContext.resume();if(!touchGamepadEnable||touchGamepadPassthrough){const l=[];for(var h of g.touches){var k=h;touchGamepadSideZones.includes(k.target)||k.target===touchGamepadZoneC||l.push(h)}if(k=l.length){h=vec2(l[0].clientX,l[0].clientY);const m=mousePosScreen;mousePosScreen=c(h);e&&l[0].identifier===f?mouseDeltaScreen=mouseDeltaScreen.add(mousePosScreen.subtract(m)):e||(inputData[0][0]=3);f=l[0].identifier}else e&&(inputData[0][0]=inputData[0][0]&2|4);e=k}inputPreventDefault&&g.cancelable&&document.hasFocus()&&g.preventDefault();return!0}}document.addEventListener("touchstart",g=>d(g),{passive:!1});document.addEventListener("touchmove",g=>d(g),{passive:!1});document.addEventListener("touchend",g=>d(g),{passive:!1});let e,f}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*mainCanvasSize.x,d*mainCanvasSize.y)}headlessMode||(document.addEventListener("keydown",function(d){function e(g){return g?.isContentEditable||["INPUT","TEXTAREA","SELECT"].includes(g?.tagName)}d.repeat||(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&&(d=a(d.code),inputData[0][d]=inputData[0][d]&2|4)}),document.addEventListener("mousedown",function(d){if(!isTouchDevice||!touchInputEnable){soundEnable&&!headlessMode&&audioContext&&!audioIsRunning()&&audioContext.resume();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){d.ctrlKey||(mouseWheel+=sign(d.deltaY));inputPreventDefault&&d.cancelable&&document.hasFocus()&&d.preventDefault()},{passive:!1}),document.addEventListener("contextmenu",function(d){d.preventDefault()}),document.addEventListener("blur",function(){inputClear();touchGamepadPointerRole.clear();touchGamepadButtons.length=0;touchGamepadSticks.length=0;touchGamepadStickPointerId.length=0}),isTouchDevice&&touchInputEnable&&b())}function inputUpdate(){headlessMode||(touchInputEnable&&isTouchDevice||document.hasFocus()||inputClear(),mousePos=screenToWorld(mousePosScreen),mouseDelta=screenToWorldDelta(mouseDeltaScreen),touchGamepadInit(),function(){var a=l=>{const m=n=>.3<n?percent(n,.3,.8):-.3>n?-percent(-n,.3,.8):0;return vec2(m(l.x),m(-l.y)).clampLength()};if(touchGamepadEnable&&isTouchDevice){if(ASSERT(!touchGamepadLeftStick||!touchGamepadLeftButtonCount,"set touchGamepadLeftStick or touchGamepadLeftButtonCount, not both"),ASSERT(!touchGamepadRightStick||!touchGamepadButtonCount,"set touchGamepadRightStick or touchGamepadButtonCount, not both"),touchGamepadTimer.isSet()){gamepadPrimary=0;var b=gamepadStickData[0]??(gamepadStickData[0]=[]),c=gamepadDpadData[0]??(gamepadDpadData[0]=vec2());b.length=0;c.set();for(var d=0;2>d;d++)if(touchGamepadSideStick(d)){var e=touchGamepadStickOut(d);b[e]=vec2();var f=touchGamepadSticks[d]??vec2();if(touchGamepadAnalog)b[e]=a(f);else if(.3<f.lengthSquared()){var g=clamp(round(f.x),-1,1);f=clamp(round(f.y),-1,1);b[e]=vec2(g,-f).clampLength();e||c.set(g,-f)}}a=inputData[1]??(inputData[1]=[]);for(b=12;b--;)c=gamepadIsDown(b,0),a[b]=touchGamepadButtons[b]?c?1:3:c?4:0,touchGamepadVibration&&3===a[b]&&(9===b||touchGamepadIsFaceButton(b))&&vibrate(touchGamepadVibration)}}else if(gamepadsEnable&&navigator?.getGamepads&&(debug||document.hasFocus())){b=navigator.getGamepads();c=min(8,b.length);for(d=0;d<c;++d){const l=b[d];if(!l){inputData[d+1]=void 0;gamepadStickData[d]=void 0;gamepadDpadData[d]=void 0;gamepadHadInput[d]=void 0;gamepadAxisCentered[d]=void 0;continue}e=inputData[d+1]??(inputData[d+1]=[]);g=gamepadStickData[d]??(gamepadStickData[d]=[]);f=gamepadDpadData[d]??(gamepadDpadData[d]=vec2());const m="standard"===l.mapping,n=gamepadAxisCentered[d]??(gamepadAxisCentered[d]=[]);var h=p=>{const r=l.axes[p];if(m&&4>p||!gamepadAxisFilterEnable)return r;const q=n[p]|0;if(q>gamepadAxisCenteredFrames)return r;n[p]=.3>abs(r)?q+1:0;return 0};for(var k=0;k<l.axes.length-1;k+=2)g[k>>1]=a(vec2(h(k),h(k+1)));h=!1;for(k=l.buttons.length;k--;){const p=l.buttons[k],r=gamepadIsDown(k,d);e[k]=p.pressed?r?1:3:r?4:0;p.pressed&&(!p.value||.9<p.value)&&(h=!0)}h&&(gamepadHadInput[d]=!0,gamepadHadInput[gamepadPrimary]||(gamepadPrimary=d));"standard"===l.mapping&&f.set((gamepadIsDown(15,d)&&1)-(gamepadIsDown(14,d)&&1),(gamepadIsDown(12,d)&&1)-(gamepadIsDown(13,d)&&1));gamepadDirectionEmulateStick&&(f.x||f.y)&&(g[0]=f.clampLength())}touchGamepadEnable&&isUsingGamepad&&touchGamepadTimer.unset()}}(),function(){const a=mouseIsDown(0)||mouseIsDown(1)||mouseIsDown(2)||mouseDeltaScreen.length()>inputMouseMoveThreshold;let b=!1;for(var c=gamepadStickCount();c--&&!b;)b=.2<gamepadStick(c).lengthSquared();for(c=17;c--&&!b;)b=gamepadIsDown(c);c=!1;for(const d in inputData[0])if(isNaN(+d)&&inputData[0][d]&1){c=!0;break}b?lastInputDevice="gamepad":a?lastInputDevice="mouse":c&&(lastInputDevice="keyboard");isUsingGamepad="gamepad"===lastInputDevice}())}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(){touchGamepadRender()}const touchGamepadSvgNS="http://www.w3.org/2000/svg";function touchGamepadInit(){if(!touchGamepadOverlay&&touchGamepadEnable&&isTouchDevice&&!headlessMode&&document.body){var a=touchGamepadOverlay=document.createElement("div");a.style.cssText="position:fixed;inset:0;z-index:50;pointer-events:none;opacity:0;touch-action:none;user-select:none;-webkit-user-select:none;-webkit-touch-callout:none;transition:opacity .2s;box-sizing:border-box;padding:env(safe-area-inset-top) env(safe-area-inset-right) env(safe-area-inset-bottom) env(safe-area-inset-left)";var b=touchGamepadStage=document.createElement("div");b.style.cssText="position:relative;width:100%;height:100%;pointer-events:none";a.appendChild(b);var c=touchGamepadSvg=document.createElementNS(touchGamepadSvgNS,"svg");c.style.cssText="position:absolute;inset:0;width:100%;height:100%;pointer-events:none;overflow:visible;fill:none;stroke:#fff;stroke-width:3";b.appendChild(c);c=()=>{const d=document.createElement("div");d.style.cssText="position:absolute;pointer-events:auto;touch-action:none";d.addEventListener("pointerdown",e=>touchGamepadPointerDown(e,d));d.addEventListener("pointermove",e=>touchGamepadPointerMove(e));d.addEventListener("pointerup",e=>touchGamepadPointerUp(e));d.addEventListener("pointercancel",e=>touchGamepadPointerUp(e));b.appendChild(d);return d};touchGamepadSideZones[0]=c();touchGamepadSideZones[1]=c();touchGamepadZoneC=c();addEventListener("resize",()=>touchGamepadNeedRelayout=!0);document.body.appendChild(a);touchGamepadNeedRelayout=!0}}function touchGamepadStageRect(){return touchGamepadStage.getBoundingClientRect()}function touchGamepadSideStick(a){return a?touchGamepadRightStick:touchGamepadLeftStick}function touchGamepadSideButtonCount(a){return a?touchGamepadButtonCount:touchGamepadLeftButtonCount}function touchGamepadSideButtonBase(a){return a?0:4}function touchGamepadStickOut(a){return a&&touchGamepadLeftStick?1:0}function touchGamepadSideHasControl(a){return touchGamepadSideStick(a)||0<touchGamepadSideButtonCount(a)}function touchGamepadIsFaceButton(a){for(let b=0;2>b;b++){const c=touchGamepadSideButtonBase(b);if(!touchGamepadSideStick(b)&&a>=c&&a<c+touchGamepadSideButtonCount(b))return!0}return!1}function touchGamepadSideCenter(a,b,c){if(touchGamepadFloating&&touchGamepadSideStick(a)&&touchGamepadStickAnchors[a])return touchGamepadStickAnchors[a];c-=touchGamepadSize;const d=touchGamepadSideButtonCount(a);touchGamepadSideStick(a)||2!==d&&3!==d||(c-=touchGamepadSize/4);return vec2(a?b-touchGamepadSize:touchGamepadSize,c)}function touchGamepadRelayout(){if(touchGamepadOverlay){var a=touchGamepadStageRect(),b=a.width;a=a.height;var c=touchGamepadSize,d=(f,g)=>f.style.cssText="position:absolute;pointer-events:auto;touch-action:none;"+g;if(paused){for(var e of touchGamepadSideZones)e.style.display="none";touchGamepadCenterButtonSize?(d(touchGamepadZoneC,"inset:0"),touchGamepadZoneC.style.display=""):touchGamepadZoneC.style.display="none"}else{for(e=0;2>e;e++){const f=touchGamepadSideZones[e],g=e?"right":"left";f.style.display=touchGamepadSideHasControl(e)?"":"none";if(touchGamepadFloating){const h=touchGamepadSideHasControl(e?0:1)?"50%":"100%";d(f,`${g}:0;bottom:0;width:${h};height:60%`)}else d(f,`${g}:0;bottom:0;width:${3*c}px;height:${3*c}px`)}touchGamepadZoneC.style.display=touchGamepadCenterButtonSize?"":"none";c=touchGamepadCenterButtonSize;d(touchGamepadZoneC,`left:50%;top:50%;width:${2*c}px;height:${2*c}px;transform:translate(-50%,-50%)`)}touchGamepadBuildSvg(b,a);touchGamepadNeedRelayout=!1}}function touchGamepadBuildSvg(a,b){const c=touchGamepadSvg;for(;c.firstChild;)c.removeChild(c.firstChild);const d=touchGamepadSvgEls={face:[],thumb:[]},e=touchGamepadSize,f=(k,l,m,n)=>{const p=document.createElementNS(touchGamepadSvgNS,"circle");p.setAttribute("cx",k);p.setAttribute("cy",l);p.setAttribute("r",m);n&&p.setAttribute("fill",n);c.appendChild(p);return p},g=k=>{const l=.18*e,m=.5*e,n=k.x;k=k.y;const p=document.createElementNS(touchGamepadSvgNS,"path");p.setAttribute("d",`M ${n-l} ${k-m} H ${n+l} V ${k-l} H ${n+m} V ${k+l} H ${n+l} `+`V ${k+m} H ${n-l} V ${k+l} H ${n-m} V ${k-l} H ${n-l} Z`);c.appendChild(p)};for(let k=0;2>k;k++){const l=touchGamepadSideButtonCount(k),m=touchGamepadSideButtonBase(k),n=touchGamepadSideCenter(k,a,b);if(touchGamepadSideStick(k))touchGamepadAnalog?f(n.x,n.y,e/2):g(n),d.thumb[k]=f(n.x,n.y,e/4,"#fff");else if(1===l)d.face[m]=f(n.x,n.y,e/2,"#000");else for(let p=0;p<l;p++){var h=mod(p-1,4);let r=2<l?h:min(h,l-1);r=3===r?2:2===r?3:r;h=vec2().setDirection(h,e/2);2===l&&(h.x*=-1);k||(h.x*=-1);h=n.add(h);d.face[m+r]=f(h.x,h.y,e/4,"#000")}}debug&&debugGamepads&&touchGamepadBuildDebug(a,b)}function touchGamepadBuildDebug(a,b){const c=touchGamepadSize,d=touchGamepadSvg,e=(h,k,l)=>{h=document.createElementNS(touchGamepadSvgNS,h);for(const m in k)h.setAttribute(m,k[m]);h.setAttribute("stroke",l);h.setAttribute("stroke-width",2);h.setAttribute("fill","none");d.appendChild(h)},f=(h,k,l)=>e("circle",{cx:h.x,cy:h.y,r:k},l);e("line",{x1:a/2,y1:0,x2:a/2,y2:b},"#0f0");for(var g=0;2>g;g++)if(touchGamepadSideStick(g))if(touchGamepadFloating){const h=.4*b,k=!touchGamepadSideHasControl(g?0:1);e("rect",{x:k?0:g?a/2:0,y:h,width:k?a:a/2,height:b-h},"#0ff")}else f(touchGamepadSideCenter(g,a,b),2*c,"#0ff");else 1<=touchGamepadSideButtonCount(g)&&f(touchGamepadSideCenter(g,a,b),c,"#0ff");if(touchGamepadCenterButtonSize)for(f(vec2(a/2,b/2),touchGamepadCenterButtonSize,"#ff0"),g=0;2>g;g++)touchGamepadSideHasControl(g)&&f(touchGamepadSideCenter(g,a,b),2*c,"#f0f")}function touchGamepadRender(){if(touchGamepadOverlay&&!headlessMode)if(touchGamepadEnable&&isTouchDevice){touchGamepadOverlay.style.display="";var a=debug&&debugGamepads,b=[touchGamepadButtonCount,touchGamepadLeftButtonCount,touchGamepadLeftStick,touchGamepadRightStick,touchGamepadAnalog,touchGamepadSize,touchGamepadFloating,touchGamepadCenterButtonSize,paused,a].join();b!==touchGamepadLastLayout&&(touchGamepadLastLayout=b,touchGamepadNeedRelayout=!0);touchGamepadNeedRelayout&&touchGamepadRelayout();b=touchGamepadDisplayTime?percent(touchGamepadTimer.get(),touchGamepadDisplayTime+1,touchGamepadDisplayTime):1;var c=a||touchGamepadTimer.isSet()&&0<b&&!paused;touchGamepadOverlay.style.opacity=c?a?1:b*touchGamepadAlpha:0;if(c){b=touchGamepadStageRect();a=b.width;c=b.height;var d=touchGamepadSize;if(b=touchGamepadSvgEls){for(let e=0;2>e;e++)if(touchGamepadSideStick(e)&&b.thumb[e]){const f=touchGamepadSideCenter(e,a,c).add((touchGamepadSticks[e]??vec2()).scale(d/2));b.thumb[e].setAttribute("cx",f.x);b.thumb[e].setAttribute("cy",f.y)}for(a=0;a<b.face.length;a++)b.face[a]&&b.face[a].setAttribute("fill",touchGamepadButtons[a]?"#fff":"#000")}}}else"none"!==touchGamepadOverlay.style.display&&(touchGamepadOverlay.style.display="none",touchGamepadPointerRole.clear(),touchGamepadButtons.length=0,touchGamepadSticks.length=0,touchGamepadStickPointerId.length=0)}function touchGamepadEventPos(a){const b=touchGamepadStageRect();return vec2(a.clientX-b.left,a.clientY-b.top)}function touchGamepadApplyStick(a,b){b=b.subtract(touchGamepadStickAnchors[a]);touchGamepadSticks[a]=b.scale(2/touchGamepadSize).clampLength();touchGamepadButtons[touchGamepadStickOut(a)?11:10]=1}function touchGamepadFaceButtonAt(a,b,c,d){const e=touchGamepadSideButtonCount(a),f=touchGamepadSideButtonBase(a);c=touchGamepadSideCenter(a,c,d);if(c.distance(b)>=touchGamepadSize)return-1;if(1===e)return f;b=c.subtract(b);a||(b.x*=-1);a=2===e?b.x<b.y?1:0:mod(b.direction()+2,4);a=3===a?2:2===a?3:a;return a<e?f+a:-1}function touchGamepadControlAt(a,b,c){const d=touchGamepadSize;var e=a.x<b/2;const f=.4*c;for(let h=0;2>h;h++){var g=h?!e:e;if(touchGamepadSideStick(h)){const k=touchGamepadSideHasControl(h?0:1);if(touchGamepadFloating?(!k||g)&&a.y>f:g&&touchGamepadSideCenter(h,b,c).distance(a)<2*d)return{role:"stick",side:h}}else if(1<=touchGamepadSideButtonCount(h)&&(g=touchGamepadFaceButtonAt(h,a,b,c),0<=g))return{role:"face",btn:g}}if(touchGamepadCenterButtonSize){for(e=0;2>e;e++)if(touchGamepadSideHasControl(e)&&touchGamepadSideCenter(e,b,c).distance(a)<2*d)return;if(vec2(b/2,c/2).distance(a)<touchGamepadCenterButtonSize)return{role:"start"}}}function touchGamepadPointerDown(a,b){if(touchGamepadEnable)if(a.preventDefault(),b.setPointerCapture(a.pointerId),touchGamepadTimer.set(),soundEnable&&!headlessMode&&audioContext&&!audioIsRunning()&&audioContext.resume(),paused)touchGamepadCenterButtonSize&&(touchGamepadButtons[9]=1,touchGamepadPointerRole.set(a.pointerId,"start"));else{var c=touchGamepadStageRect();b=c.width;var d=c.height;c=vec2(a.clientX-c.left,a.clientY-c.top);var e=touchGamepadControlAt(c,b,d);e&&("stick"===e.role?(e=e.side,touchGamepadStickAnchors[e]=touchGamepadFloating?c:touchGamepadSideCenter(e,b,d),touchGamepadStickPointerId[e]=a.pointerId,touchGamepadPointerRole.set(a.pointerId,"stick"+e),touchGamepadNeedRelayout=!0,touchGamepadApplyStick(e,c)):"face"===e.role?(touchGamepadButtons[e.btn]=1,touchGamepadPointerRole.set(a.pointerId,"face"+e.btn)):(touchGamepadButtons[9]=1,touchGamepadPointerRole.set(a.pointerId,"start")))}}function touchGamepadPointerMove(a){const b=touchGamepadPointerRole.get(a.pointerId);b&&(a.preventDefault(),a=touchGamepadEventPos(a),"stick0"!==b&&"stick1"!==b||touchGamepadApplyStick("stick1"===b?1:0,a))}function touchGamepadPointerUp(a){const b=touchGamepadPointerRole.get(a.pointerId);b&&(touchGamepadPointerRole.delete(a.pointerId),"stick0"===b||"stick1"===b?(a="stick1"===b?1:0,touchGamepadStickPointerId[a]=void 0,touchGamepadSticks[a]=vec2(),delete touchGamepadButtons[touchGamepadStickOut(a)?11:10]):"start"===b?delete touchGamepadButtons[9]:delete touchGamepadButtons[+b.slice(4)],touchGamepadTimer.set())}let audioContext=new AudioContext,audioMasterGain=audioContext.createGain();audioMasterGain.connect(audioContext.destination);audioMasterGain.gain.value=soundVolume;let audioMasterEffectInput,audioMasterEffectOutput,audioMasterEffectOutputIsEffect;const audioDefaultSampleRate=44100;function audioIsRunning(){return"running"===audioContext.state}function audioInit(){soundEnable&&!headlessMode&&document.addEventListener("visibilitychange",audioVisibilityChange)}let audioSuspendedWhenHidden=!1;function audioVisibilityChange(){document.hidden?soundPauseWhenHidden&&"running"==audioContext.state&&(audioSuspendedWhenHidden=!0,audioContext.suspend()):audioSuspendedWhenHidden&&(audioSuspendedWhenHidden=!1,audioContext.resume())}function setAudioMasterEffect(a,b){var c=b||a;c=!!c&&"input"in c;b=audioEffectNode(b,"output")||audioEffectNode(a,"output");a=audioEffectNode(a,"input");ASSERT(!a||"function"===typeof a.connect,"input must be an AudioNode or an effect with input and output nodes");ASSERT(!b||"function"===typeof b.connect,"output must be an AudioNode or an effect with input and output nodes");audioMasterGain.disconnect(audioMasterEffectInput||audioContext.destination);audioMasterEffectOutput?.disconnect();audioMasterEffectOutputIsEffect&&audioMasterEffectOutput.connect(audioMasterGain);audioMasterEffectInput=a;audioMasterEffectOutput=b;audioMasterEffectOutputIsEffect=c;a?(audioMasterGain.connect(a),b.disconnect(),b.connect(audioContext.destination)):audioMasterGain.connect(audioContext.destination)}function audioEffectNode(a,b){return a&&"input"in a?a[b]:a}class Sound{constructor(a,b,c=soundDefaultRange,d=soundDefaultTaper,e){soundEnable&&!headlessMode&&(ASSERT(!a||isArray(a)||isStringLike(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.sampleLength=0,this._sampleChannels=this.sampleBuffer=void 0,this.loadedPercent=0,this.onloadCallback=e,this.output=void 0,isArray(a)?(c=a.slice(),this.randomness=c[1]??b??.05,c[1]=0,this.sampleChannels=[zzfxG(...c)],this.buildSampleBuffer(),this.loadedPercent=1,e?.(this)):"string"===typeof a&&this.loadSound(a).catch(f=>LOG("Sound load failed for",a,"-",f.message)))}get sampleChannels(){const a=this.sampleBuffer;if(!this._sampleChannels&&a){const b=[];for(let c=0;c<a.numberOfChannels;c++)b.push(a.getChannelData(c).slice());this._sampleChannels=b}return this._sampleChannels}set sampleChannels(a){this._sampleChannels=a;this.sampleBuffer=void 0;this.sampleLength=a?.[0]?.length||0}buildSampleBuffer(){this.sampleBuffer||!this._sampleChannels||headlessMode||(this.sampleBuffer=createAudioBuffer(this._sampleChannels,this.sampleRate),this._sampleChannels=void 0)}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.sampleBuffer||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/mainCanvasSize.x-1}c+=c*this.randomness*d*rand(-1,1);e=new SoundInstance(this,b,c,g,e,f);debug&&debugSound&&a&&(debugCircle(a,.5,"#0ff",.5,!0),this.range&&(debugCircle(a,2*this.range,"#0ff",.5),debugCircle(a,2*this.range*this.taper,"#0ff",.5)),debugText("vol "+b.toFixed(2)+" pitch "+c.toFixed(2),a,.5,"#0ff",.5));return e}}playLoop(a,b=1,c=1,d=1,e=!1){return this.play(a,b,c,d,!0,e)}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.sampleLength/this.sampleRate||0}isLoaded(){return 1===this.loadedPercent}async loadSound(a){const 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);this.sampleRate=a.sampleRate;this.sampleLength=a.length;this.sampleBuffer=a;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.output=a.output;this.onendedCallback=g=>{g===this.source&&(this.startTime=this.source=void 0,this.pausedTime=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.sound.buildSampleBuffer();(this.source=this.sound.sampleBuffer?playAudioBuffer(this.sound.sampleBuffer,this.volume,this.rate,this.pan,this.loop,this.gainNode,a,this.onendedCallback,this.output):playSamples(this.sound.sampleChannels,this.volume,this.rate,this.pan,this.loop,this.sound.sampleRate,this.gainNode,a,this.onendedCallback,this.output))?(this.startTime=audioContext.currentTime-a,this.pausedTime=void 0):(this.startTime=void 0,this.pausedTime=a)}setVolume(a,b=0){ASSERT(0<=a,"Sound volume must be positive or zero");ASSERT(0<=b,"Sound fade time must be positive or zero");this.volume=a;if(this.gainNode){var c=this.gainNode.gain,d=audioContext.currentTime;c.cancelScheduledValues(d);b?(c.setValueAtTime(c.value,d),c.linearRampToValueAtTime(a,d+b)):c.value=a}}setRate(a){ASSERT(0<=a,"Sound rate must be positive or zero");this.isPlaying()&&a&&(this.startTime=audioContext.currentTime-this.getCurrentTime()*this.rate/a);this.rate=a;this.source&&(this.source.playbackRate.value=a)}stop(a=0){ASSERT(0<=a,"Sound fade time must be positive or zero");if(this.isPlaying())if(a){const b=this.gainNode.gain,c=audioContext.currentTime;a=c+a;b.cancelScheduledValues(c);b.setValueAtTime(b.value,c);b.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(){if(!this.isPlaying())return this.pausedTime;const a=this.getDuration();return a?mod(audioContext.currentTime-this.startTime,a):0}getDuration(){return this.rate?this.sound.getDuration()/this.rate:0}getSource(){return this.source}}function speak(a,b=1,c=1,d=1,e=""){ASSERT("string"!==typeof b,"speak() signature changed: language is now the last parameter, after pitch");if(soundEnable&&!headlessMode&&"undefined"!==typeof speechSynthesis)return a=new SpeechSynthesisUtterance(a),a.lang=e,a.volume=clamp(b*soundVolume),a.rate=c,a.pitch=d,speechSynthesis.speak(a),a}function speakStop(){"undefined"!==typeof speechSynthesis&&speechSynthesis.cancel()}function getNoteFrequency(a,b=220){return b*2**(a/12)}function playSamples(a,b=1,c=1,d=0,e=!1,f=audioDefaultSampleRate,g,h=0,k,l){if(soundEnable&&!headlessMode){if(audioIsRunning())return a=createAudioBuffer(a,f),playAudioBuffer(a,b,c,d,e,g,h,k,l);audioContext.resume()}}function createAudioBuffer(a,b=audioDefaultSampleRate){const c=audioContext.createBuffer(a.length,a[0].length,b);a.forEach((d,e)=>c.getChannelData(e).set(d));return c}function playAudioBuffer(a,b=1,c=1,d=0,e=!1,f,g=0,h,k){if(soundEnable&&!headlessMode){if(audioIsRunning()){var l=audioContext.createBufferSource();l.buffer=a;l.playbackRate.value=c;l.loop=e;f=f||audioContext.createGain();f.gain.value=b;a=audioEffectNode(k,"input")||audioMasterGain;ASSERT("function"===typeof a.connect,"output must be an AudioNode or an effect with input and output nodes");f.connect(a);var m=new StereoPannerNode(audioContext,{pan:clamp(d,-1,1)});l.connect(m).connect(f);l.addEventListener("ended",()=>{f.disconnect();m.disconnect();h&&h(l)});l.start(0,g*c);debug&&debugSound&&LOG("sound","vol",b.toFixed(2),"rate",c.toFixed(2),"pan",d.toFixed(2),e?"loop":"");return l}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,r=0,q=0,u=0,t=0,v=1,x=0,w=0,y=0){var z=audioDefaultSampleRate;let A=2*PI,C=k*=500*A/z/z;b=c*=(1+rand(b,-b))*A/z;let F=0,H=0,G=0,E=1,I=[],B=0,D=0,J=0,O;var M=A*abs(y)*2/z,L=cos(M),N=sin(M)/2/2,K=1+N;M=-2*L/K;N=(1-N)/K;let P=(1+sign(y)*L)/2/K;L=-(sign(y)+L)/K;let Q=K=0,R=0,S=0;d=d*z||9;x*=z;e*=z;f*=z;t*=z;l*=500*A/z**3;q*=A/z;m*=A/z;n*=z;p=p*z|0;for(z=d+x+e+f+t|0;D<z;I[D++]=J*a)++G%(100*u|0)||(J=g?1<g?2<g?3<g?4<g?B/A%1<h/2?1:-1:sin(B**3):max(min(tan(B),1),-1):1-(2*B/A%2+2)%2:1-4*abs(round(B/A)-B/A):sin(B),J=(p?1-w+w*sin(A*D/p):1)*(4<g?J:sign(J)*abs(J)**h)*(D<d?D/d:D<d+x?1-(D-d)/x*(1-v):D<d+x+e?v:D<z-t?(z-D-t)/f*v:0),J=t?J/2+(t>D?0:(D<z-t?1:(z-D)/t)*I[D-t|0]/2/a):J,y&&(J=S=P*K+L*(K=Q)+P*(Q=J)-N*R-M*(R=S))),O=(c+=k+=l)*cos(q*F++),B+=O+O*r*sin(D**5),E&&++E>n&&(c+=m,b+=m,E=0),!p||++H%p||(c=b,k=C,E||=1);return I}const tileCollisionLayers=[];function tileCollisionGetData(a,b=!0){for(const d of tileCollisionLayers)if(!b||d.isSolid){var c=a.subtract(d.pos);if(c.arrayCheck(d.size)&&(c=d.getCollisionData(c)))return c}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){let f,g,h;const k=d&&vec2();for(const l of tileCollisionLayers)if(!e||l.isSolid){const m=l.collisionRaycast(a,b,c,k);if(m){const n=a.distanceSquared(m);if(void 0===f||n<g)f=m,g=n,d&&(h=k.copy())}}f&&d&&d.setFrom(h);return f}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 r=g.x;r--;)for(let q=g.y;q--;){const u=vec2(r,g.y-1-q);var l=n.data[r+q*g.x];l&&(l=new TileLayerData(l-1,0,!1,p),k.setData(u,l),m===d&&k.setCollisionData(u,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.context=headlessMode?void 0:createCanvasContext(e.x,e.y);this.canvas=this.context?.canvas;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)}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.drawTile=()=>{},this.drawRect=()=>{},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)]:void 0}update(){!glEnable&&this.isUsingWebGL&&(this.isUsingWebGL=!1,this.redraw())}render(){ASSERT(drawContext!==this.context,"must call redrawEnd() after drawing tiles!");const a=this.drawSize||this.size,b=this.pos.add(a.scale(.5));this.draw(b,a,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.index(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)}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)}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;if(0>e+b.x/2||e-b.x/2>this.size.x||0>f+b.y/2||f-b.y/2>this.size.y)return!1;a=max(e-b.x/2|0,0);var g=max(f-b.y/2|0,0);e=min(max(e+b.x/2,a+1),this.size.x);b=min(max(f+b.y/2,g+1),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,r=1,q=.1,u=.05,t=1,v=1,x=0,w=PI,y=.1,z=.2,A=!1,C=!1,F=!0,H=C?1e9:0,G=!1){super(a,vec2(),g,b,void 0,H);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=F;this.particleTime=n;this.sizeStart=p;this.sizeEnd=r;this.speed=q;this.angleSpeed=u;this.damping=t;this.angleDamping=v;this.gravityScale=x;this.particleConeAngle=w;this.fadeRate=y;this.randomness=z;this.collideTiles=A;this.additive=C;this.localSpace=G;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);a=this.particles;let b=0;for(let c=0;c<a.length;++c){const d=a[c];d.update();d.destroyed||(a[b++]=d)}a.length=b;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))}}const particleDrawPos=new Vector2;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)))){c=f(vec2(this.pos.x,a.y));f=f(vec2(a.x,this.pos.y));d=b?max(d,b.restitution):d;e=b?max(e,b.friction):e;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);e=particleDrawPos.set(this.pos.x,this.pos.y);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}c&&setAdditiveBlendMode();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&&setAdditiveBlendMode(!1);debugParticles&&debugRect(e,h,"#f005",0,f)}}let glCanvas,glContext,glAntialias=!0,glShader,glPolyShader,glPolyMode,glAdditive,glBatchAdditive,glActiveTexture,glArrayBuffer,glGeometryBuffer,glPositionData,glColorData,glBatchCount,glTextureInfos,glInstancedVAO,glPolyVAO,glFramebuffer,glRenderTarget,glShaderObjects=[],glCustomShader,glBatchShader,glProgramCustom,glTransform,glUniformLocations=new Map,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,gl_VERTEX_SOURCE="#version 300 es\nprecision highp float;uniform mat4 m;layout(location=0) in vec2 g;layout(location=1) in vec4 p;layout(location=2) in vec4 u;layout(location=3) in vec4 c;layout(location=4) in vec4 a;layout(location=5) in float r;out vec2 v,l;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);l=g;d=c;e=a;}";function glInit(a){function b(){glShader=glCreateProgram(gl_VERTEX_SOURCE,"#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;for(const d of glShaderObjects)d.program=void 0;glBatchShader=void 0;glProgramCustom=!0;glUniformLocations=new Map;glBatchCount=0;glPolyMode=!1;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,c.wrap);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.");var b=glRenderTarget?1:getCanvasPixelRatio(),c=mainCanvasSize.x*b|0;b=mainCanvasSize.y*b|0;glRenderTarget||glCanvas.width===c&&glCanvas.height===b||(glCanvas.width=c,glCanvas.height=b);glContext.viewport(0,0,c,b);a&&glClearCanvas();a=vec2(2*cameraScale).divide(mainCanvasSize);glRenderTarget&&(a.y=-a.y);c=cameraPos.rotate(-cameraAngle);c=vec2(-1).subtract(c.multiply(a));b=cos(cameraAngle);var d=sin(cameraAngle);glTransform=a=[a.x*b,a.y*d,0,0,-a.x*d,a.y*b,0,0,1,1,1,0,c.x,c.y,0,1];c=glPolyShader;glContext.useProgram(c);c=glContext.getUniformLocation(c,"m");glContext.uniformMatrix4fv(c,!1,a);c=glShader;glContext.useProgram(c);c=glContext.getUniformLocation(c,"m");glContext.uniformMatrix4fv(c,!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){glContext&&a!==glActiveTexture&&(glFlush(),glActiveTexture=a,glContext.bindTexture(glContext.TEXTURE_2D,glActiveTexture))}function glSetTextureWrap(a,b=!0){if(glContext&&a){var c=a===glActiveTexture;c?glFlush():glContext.bindTexture(glContext.TEXTURE_2D,a);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);!c&&glActiveTexture&&glContext.bindTexture(glContext.TEXTURE_2D,glActiveTexture)}}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 glUniformLocation(a,b){let c=glUniformLocations.get(a);c||glUniformLocations.set(a,c={});let d;return(d=c)[b]??(d[b]=glContext.getUniformLocation(a,b))}function glShaderProgram(a){return a.program||(a.program=glCreateProgram(gl_VERTEX_SOURCE,"#version 300 es\nprecision highp float;uniform sampler2D iChannel0;uniform vec3 iResolution;uniform float iTime;in vec2 v,l;in vec4 d,e;out vec4 c;\n#define localUV l\n"+a.fragmentCode+"\nvoid main(){vec4 t;mainImage(t,v);c=t*d+e;}"))}function glCreateTexture(a,b=!1){if(glContext){var c=glContext.createTexture(),d=!1;a?.width?(glSetTextureData(c,a),glContext.bindTexture(glContext.TEXTURE_2D,c),d=!tilesPixelated&&isPowerOfTwo(a.width)&&isPowerOfTwo(a.height)):(a=new Uint8Array([255,255,255,255]),glContext.bindTexture(glContext.TEXTURE_2D,c),glContext.texImage2D(glContext.TEXTURE_2D,0,glContext.RGBA,1,1,0,glContext.RGBA,glContext.UNSIGNED_BYTE,a));a=tilesPixelated?glContext.NEAREST:glContext.LINEAR;var e=d?glContext.LINEAR_MIPMAP_LINEAR:a;glContext.texParameteri(glContext.TEXTURE_2D,glContext.TEXTURE_MAG_FILTER,a);glContext.texParameteri(glContext.TEXTURE_2D,glContext.TEXTURE_MIN_FILTER,e);b=b?glContext.REPEAT:glContext.CLAMP_TO_EDGE;glContext.texParameteri(glContext.TEXTURE_2D,glContext.TEXTURE_WRAP_S,b);glContext.texParameteri(glContext.TEXTURE_2D,glContext.TEXTURE_WRAP_T,b);d&&glContext.generateMipmap(glContext.TEXTURE_2D);glContext.bindTexture(glContext.TEXTURE_2D,glActiveTexture);return c}}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),!tilesPixelated&&isPowerOfTwo(b.width)&&isPowerOfTwo(b.height)&&glContext.generateMipmap(glContext.TEXTURE_2D),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,a.wrap)))}function glUnregisterTextureInfo(a){if(!headlessMode){glTextureInfos.delete(a);var b=a.glTexture;a.glTexture=void 0;glDeleteTexture(b)}}function glFlush(){if(glEnable&&glContext&&glBatchCount){var a=glBatchAdditive?glContext.ONE:glContext.ONE_MINUS_SRC_ALPHA;glContext.blendFuncSeparate(glContext.SRC_ALPHA,a,glContext.ONE,a);glContext.enable(glContext.BLEND);glPolyMode||!glBatchShader&&!glProgramCustom||(a=glBatchShader?glShaderProgram(glBatchShader):glShader,glContext.useProgram(a),glProgramCustom=!!glBatchShader,glBatchShader&&(glContext.uniformMatrix4fv(glUniformLocation(a,"m"),!1,glTransform),glContext.uniform1f(glUniformLocation(a,"iTime"),time),glContext.uniform3f(glUniformLocation(a,"iResolution"),glCanvas.width,glCanvas.height,1)));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;primitiveCount+=glBatchCount;glBatchCount=0}glBatchAdditive=glAdditive;glBatchShader=glCustomShader}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||glBatchShader!==glCustomShader)&&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 glDrawUntextured(a,b,c,d,e,f){glDraw(a,b,c,d,e,0,0,0,0,0,f)}function glDrawPointsTransform(a,b,c,d,e,f,g,h=!0){const k=[],l=sin(-g);g=cos(-g);for(const m of a){a=m.x*e;const n=m.y*f;k.push(vec2(c+g*a-l*n,d+l*a+g*n))}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();ASSERT(c<gl_MAX_POLY_VERTEXES,"poly exceeds max batch size");if(!(c>=gl_MAX_POLY_VERTEXES)){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();ASSERT(c<gl_MAX_POLY_VERTEXES,"poly exceeds max batch size");if(!(c>=gl_MAX_POLY_VERTEXES)){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),glContext.viewport(0,0,glCanvas.width,glCanvas.height))}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[];b/=2;const d=[],e=a.length;for(let n=0;n<e;n++){var f=a[c?(n-1+e)%e:max(n-1,0)],g=a[n],h=a[c?(n+1)%e:min(n+1,e-1)],k=g.x-f.x,l=g.y-f.y,m=(k*k+l*l)**.5;f=h.x-g.x;const p=h.y-g.y;h=(f*f+p*p)**.5;1e-6>m&&1e-6>h||(l=1e-6<m?-l/m:0,m=1e-6<m?k/m:0,k=l+(1e-6<h?-p/h:0),f=m+(1e-6<h?f/h:0),h=(k*k+f*f)**.5,1e-6>h?(k=l,f=m):(k/=h,f/=h,l=l*k+m*f,1e-6<l&&(l=min(1/l,10),k*=l,f*=l)),l=vec2(g.x-k*b,g.y-f*b),g=vec2(g.x+k*b,g.y+f*b),d.push(l),d.push(g))}1<d.length&&c&&(d.push(d[0]),d.push(d[1]));return d}function glPolyStrip(a){if(3>a.length)return[];const b=(q,u,t)=>(u.x-q.x)*(t.y-q.y)-(u.y-q.y)*(t.x-q.x);0>(q=>{let u=0;for(let t=q.length;t--;)u+=q[t].cross(q[(t+1)%q.length]);return u})(a)&&(a=a.slice().reverse());var c=(q,u,t,v)=>{const x=b(u,t,q);t=b(t,v,q);q=b(v,u,q);return!((-1e-9>x?1:0)+(-1e-9>t?1:0)+(-1e-9>q?1:0)&&(1e-9<x?1:0)+(1e-9<t?1:0)+(1e-9<q?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 q=d[(k+1)%d.length],u=a[l],t=a[m],v=a[q];if(1e-9>b(u,t,v))continue;let x=!1;for(let w=0;w<d.length;w++){const y=d[w];if(y!==l&&y!==m&&y!==q&&(x=c(a[y],u,t,v)))break}if(!x){e.push([l,m,q]);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,r]=e[0];c.push(a[n],a[p],a[r]);for(d=1;d<e.length;d++){const[q,u,t]=e[d];c.push(a[r],a[q]);c.push(a[q],a[u],a[t]);r=t}return c}function drawEngineLogo(a){engineUpdateCanvas();const b=mainContext;var c=mainCanvasSize.x,d=mainCanvasSize.y,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,r,q=1)=>{0<=r?(l=b.fillStyle=b.createLinearGradient(l,m,n,p),l.addColorStop(0,`hsl(${360*[.95,.56,.13][r%3]} 99%${75}%)`),l.addColorStop(1,`hsl(${360*[.95,.56,.13][r%3]} 99%${50}%)`)):b.fillStyle="#000";-1<=r?(b.fill(),q&&b.stroke()):b.stroke()};f=(l,m,n,p=0,r=2*PI,q,u)=>{b.beginPath();b.arc(l,m,n,k*p,k*r);h(l,m-n,l,m+n,q,u)};g=(l,m,n,p,r)=>{b.beginPath();b.rect(l,m,n,p*k);h(l,m+p,l+n,m,r)};e=(l,m,n,p)=>{b.beginPath();for(const r of l)b.lineTo(r.x,r.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 debugMedals=!1,medalDisplayTime=5,medalDisplaySlideTime=.5,medalDisplaySize=vec2(640,80),medalsPreventUnlock=!1;const medals={};let medalsDisplayQueue=[],medalsSaveName,medalsDisplayTimeLast;function medalsInit(a){medalsSaveName=a;if(!debugMedals){let b={};try{b=JSON.parse(localStorage[a]||"{}")}catch(c){b={}}medalsForEach(c=>{c.unlocked=!(!b[c.id]||!b[c.id].unlocked)});medalsSave()}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))}function medalsReset(){medalsForEach(a=>a.unlocked=!1);medalsSave()}function medalsSave(){if(medalsSaveName){var a={};medalsForEach(b=>{const c={name:b.name,description:b.description,icon:b.icon,unlocked:b.unlocked};b.image&&(c.src=b.image.src);a[b.id]=c});localStorage[medalsSaveName]=JSON.stringify(a)}}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;this.image=void 0;e&&((this.image=new Image).src=e);medals[a]=this}unlock(){medalsPreventUnlock||this.unlocked||(ASSERT(medalsSaveName,"save name must be set"),this.unlocked=!0,medalsSave(),medalsDisplayQueue.push(this))}render(a=0){const b=mainContext;var c=min(medalDisplaySize.x,mainCanvasSize.x);const d=medalDisplaySize.y;var e=mainCanvasSize.x-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)}}function setMedalDisplayTime(a){medalDisplayTime=a}function setMedalDisplaySlideTime(a){medalDisplaySlideTime=a}function setMedalDisplaySize(a){medalDisplaySize=a.copy()}function setMedalsPreventUnlock(a){medalsPreventUnlock=a}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"))if((a=this.call("Medal.getList"))&&a.result&&!a.result.error){this.medals=a.result.data?.medals||[];debugMedals&&LOG(this.medals);for(const 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=this.call("ScoreBoard.getBoards")?.result?.data?.scoreboards||[];debugMedals&&LOG(this.scoreboards);setInterval(()=>this.call("Gateway.ping",0,!0),6e4)}else debugMedals&&LOG("Newgrounds session unavailable; skipping plugin init"),this.medals=[],this.scoreboards=[]}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);try{return b.responseText&&JSON.parse(b.responseText)}catch(e){debugMedals&&LOG("newgrounds response is not valid JSON",e)}}}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(){if(!headlessMode&&glEnable){glFlush();glContext.bindFramebuffer(glContext.FRAMEBUFFER,null);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);if(b){workCanvas.width=mainCanvas.width;workCanvas.height=mainCanvas.height;glCopyToContext(workContext);workContext.drawImage(mainCanvas,0,0);mainCanvas.width|=0;const e=getCanvasPixelRatio();mainContext.setTransform(e,0,0,e,0,0);glContext.texImage2D(glContext.TEXTURE_2D,0,glContext.RGBA,glContext.RGBA,glContext.UNSIGNED_BYTE,workCanvas)}else 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);glContext.pixelStorei(glContext.UNPACK_FLIP_Y_WEBGL,!1);glSetInstancedMode(!0)}},function(){postProcess.shader=void 0;postProcess.texture=void 0;LOG("PostProcessPlugin: WebGL context lost")},function(){d();LOG("PostProcessPlugin: WebGL context restored")})}}function postProcessBloomShader(a=.6,b=1,c=6){ASSERT(isNumber(a)&&isNumber(b)&&isNumber(c),"bloom settings must be numbers");ASSERT(0<c,"bloom size must be above zero");ASSERT(32>=c,"a bloom this wide takes a sample every few pixels of every ring, which is hundreds of samples a pixel",c);let d="",e=0;for(let f=0;3>f;++f){const g=((f+.5)/3)**.5*c,h=max(5+2*f,round(2*g))|1;e+=h;d+=`
5
+ for (int k = 0; k < ${h}; ++k)
6
+ {
7
+ float a = float(k) * ${(2*PI/h).toFixed(7)}${f?" + "+(2.3999632*f).toFixed(7):""};
8
+ glow += max(vec3(0), texture(iChannel0, uv + vec2(cos(a), sin(a)) * ${g.toFixed(4)} / iResolution.xy).rgb - ${a.toFixed(4)});
9
+ }`}return`
10
+ void mainImage(out vec4 color, vec2 pixel)
11
+ {
12
+ vec2 uv = pixel / iResolution.xy;
13
+ color = texture(iChannel0, uv);
14
+ vec3 glow = vec3(0);${d}
15
+ color.rgb += glow * ${(b/e).toFixed(6)};
16
+ }`}function postProcessBloom(a=.6,b=1,c=6,d=!1){return new PostProcessPlugin(postProcessBloomShader(a,b,c),d)}let lightSystem;class LightSystemPlugin{constructor(a,b){function c(){if(!headlessMode)if(glEnable){lightSystem.textureSize||(lightSystem.textureSize=mainCanvasSize.copy());lightSystem.texture=glContext.createTexture();glContext.bindTexture(glContext.TEXTURE_2D,lightSystem.texture);glContext.texImage2D(glContext.TEXTURE_2D,0,glContext.RGBA,lightSystem.textureSize.x,lightSystem.textureSize.y,0,glContext.RGBA,glContext.UNSIGNED_BYTE,null);glContext.texParameteri(glContext.TEXTURE_2D,glContext.TEXTURE_MAG_FILTER,glContext.LINEAR);glContext.texParameteri(glContext.TEXTURE_2D,glContext.TEXTURE_MIN_FILTER,glContext.LINEAR);glContext.texParameteri(glContext.TEXTURE_2D,glContext.TEXTURE_WRAP_S,glContext.CLAMP_TO_EDGE);glContext.texParameteri(glContext.TEXTURE_2D,glContext.TEXTURE_WRAP_T,glContext.CLAMP_TO_EDGE);lightSystem.lightShader=glCreateProgram("#version 300 es\nprecision highp float;uniform mat4 m;uniform vec2 lightPos;uniform float radius;in vec2 g;out vec2 vWorldPos;void main(){vec2 worldP=lightPos+(g-.5)*2.*radius;gl_Position=m*vec4(worldP,1,1);vWorldPos=worldP;}","#version 300 es\nprecision highp float;uniform vec2 lightPos;uniform float radius;uniform float fadeRange;uniform vec4 color;in vec2 vWorldPos;out vec4 c;void main(){float dist=distance(vWorldPos,lightPos);float t=clamp((radius-dist)/max(fadeRange,1e-6),0.,1.);c=vec4(color.rgb*t*color.a,1.);}");lightSystem.compositeShader=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 s;uniform vec3 iResolution;out vec4 c;void main(){vec2 uv=gl_FragCoord.xy/iResolution.xy;c=vec4(texture(s,uv).rgb,1.);}");lightSystem.lightVAO=glContext.createVertexArray();glContext.bindVertexArray(lightSystem.lightVAO);glContext.bindBuffer(glContext.ARRAY_BUFFER,glGeometryBuffer);var d=glContext.getAttribLocation(lightSystem.lightShader,"g");glContext.enableVertexAttribArray(d);glContext.vertexAttribPointer(d,2,glContext.FLOAT,!1,8,0);lightSystem.compositeVAO=glContext.createVertexArray();glContext.bindVertexArray(lightSystem.compositeVAO);glContext.bindBuffer(glContext.ARRAY_BUFFER,glGeometryBuffer);d=glContext.getAttribLocation(lightSystem.compositeShader,"p");glContext.enableVertexAttribArray(d);glContext.vertexAttribPointer(d,2,glContext.FLOAT,!1,8,0)}else console.warn("LightSystemPlugin: WebGL not enabled!")}ASSERT(!lightSystem,"LightSystemPlugin already initialized");ASSERT(!postProcess,"LightSystemPlugin must be created before PostProcessPlugin");lightSystem=this;this.enabled=!0;this.ambientColor=(b||BLACK).copy();this.textureSize=a?a.copy():void 0;this.compositeVAO=this.lightVAO=this.compositeShader=this.lightShader=this.texture=void 0;c();engineAddPlugin(void 0,function(){if(!headlessMode&&glEnable&&lightSystem.enabled&&lightSystem.texture){glFlush();var d=glAdditive,e=lightSystem.ambientColor;glContext.bindFramebuffer(glContext.FRAMEBUFFER,glFramebuffer);glContext.framebufferTexture2D(glContext.FRAMEBUFFER,glContext.COLOR_ATTACHMENT0,glContext.TEXTURE_2D,lightSystem.texture,0);glContext.viewport(0,0,lightSystem.textureSize.x,lightSystem.textureSize.y);glContext.clearColor(e.r,e.g,e.b,e.a);glContext.clear(glContext.COLOR_BUFFER_BIT);setAdditiveBlendMode();glContext.enable(glContext.BLEND);glContext.blendFunc(glContext.ONE,glContext.ONE);for(const f of engineObjects)f.destroyed||f.renderLight();glFlush();glContext.bindFramebuffer(glContext.FRAMEBUFFER,null);glContext.viewport(0,0,glCanvas.width,glCanvas.height);glContext.useProgram(lightSystem.compositeShader);glContext.bindVertexArray(lightSystem.compositeVAO);glContext.activeTexture(glContext.TEXTURE0);glContext.bindTexture(glContext.TEXTURE_2D,lightSystem.texture);e=lightSystem.compositeShader;glContext.uniform1i(glContext.getUniformLocation(e,"s"),0);glContext.uniform3f(glContext.getUniformLocation(e,"iResolution"),mainCanvas.width,mainCanvas.height,1);glContext.blendFunc(glContext.DST_COLOR,glContext.ZERO);glContext.drawArrays(glContext.TRIANGLE_STRIP,0,4);glActiveTexture&&glContext.bindTexture(glContext.TEXTURE_2D,glActiveTexture);setAdditiveBlendMode(d);glSetInstancedMode(!0)}},function(){lightSystem.texture=void 0;lightSystem.lightShader=void 0;lightSystem.compositeShader=void 0;lightSystem.lightVAO=void 0;lightSystem.compositeVAO=void 0;LOG("LightSystemPlugin: WebGL context lost")},function(){c();LOG("LightSystemPlugin: WebGL context restored")})}drawLight(a){if(!headlessMode&&glEnable&&this.lightShader){glFlush();glContext.useProgram(this.lightShader);glContext.bindVertexArray(this.lightVAO);var b=vec2(2*cameraScale).divide(mainCanvasSize),c=cameraPos.rotate(-cameraAngle);c=vec2(-1).subtract(c.multiply(b));var d=cos(cameraAngle),e=sin(cameraAngle);c=[b.x*d,b.y*e,0,0,-b.x*e,b.y*d,0,0,1,1,1,0,c.x,c.y,0,1];b=this.lightShader;glContext.uniformMatrix4fv(glContext.getUniformLocation(b,"m"),!1,c);glContext.uniform2f(glContext.getUniformLocation(b,"lightPos"),a.pos.x,a.pos.y);glContext.uniform1f(glContext.getUniformLocation(b,"radius"),a.radius);glContext.uniform1f(glContext.getUniformLocation(b,"fadeRange"),a.fadeRange);a=a.color;glContext.uniform4f(glContext.getUniformLocation(b,"color"),a.r,a.g,a.b,a.a);glContext.drawArrays(glContext.TRIANGLE_STRIP,0,4);glSetInstancedMode(!0)}}}class Light extends EngineObject{constructor(a,b,c,d){super(a,vec2(1),void 0,0,c);ASSERT(isNumber(b)&&0<=b,"Light radius must be a non-negative number");ASSERT(void 0===d||isNumber(d)&&0<=d,"Light fadeRange must be a non-negative number when provided");this.radius=b;this.fadeRange=void 0===d?b:d}render(){}renderLight(){lightSystem&&lightSystem.drawLight(this)}}class ZzFXMusic extends Sound{constructor(a){super(void 0);soundEnable&&!headlessMode&&(this.randomness=0,super.sampleChannels=zzfxM(...a),this.loadedPercent=1,this.onloadCallback?.(this))}}function zzfxM(a,b,c,d=125){let e,f,g,h,k,l,m,n,p,r,q,u,t,v=0,x,w=[],y=[],z=[],A=0,C=0,F=1,H={},G=audioDefaultSampleRate/d*60>>2;for(;F;A++)w=[F=n=u=0],c.forEach((E,I)=>{m=b[E][A]||[0,0,0];F|=b[E][A]&&1;x=u+(b[E][0].length-2-(n?0:1))*G;t=I===c.length-1;e=2;for(g=u;e<m.length+t;n=++e){k=m[e];p=e===m.length+t-1&&t||r!==(m[0]||0)||k|0;for(f=0;f<G&&n;f++>G-99&&p&&1>q?q+=1/99:0)l=(1-q)*w[v++]/2||0,y[g]=(y[g]||0)-l*C+l,z[g]=(z[g++]||0)+l*C+l;k&&(q=k%1,C=m[1]||0,k|=0)&&(w=H[[r=m[v=0]||0,k]]=H[[r,k]]||(h=[...a[r]],h[2]=(h[2]||220)*2**(k/12-1),0<k?zzfxG(...h):[]))}u=x});return[y,z]}function audioParamRamp(a,b,c=0){ASSERT(0<=c,"fadeTime must be positive or zero");const d=audioContext.currentTime;a.cancelScheduledValues(d);c?(a.setValueAtTime(a.value,d),a.linearRampToValueAtTime(b,d+c)):a.value=b}class AudioEffect{constructor(a=1){ASSERT(isNumber(a),"mix must be a number");this.input=audioContext.createGain();this.output=audioContext.createGain();this.dryGain=audioContext.createGain();this.wetGain=audioContext.createGain();this.mix=a;this.input.connect(this.dryGain).connect(this.output);this.wetGain.connect(this.output);this.setMix(a);this.output.connect(audioMasterGain)}setMix(a,b=0){ASSERT(isNumber(a),"mix must be a number");this.mix=a=clamp(a);this.rampParam(this.dryGain.gain,1-a,b);this.rampParam(this.wetGain.gain,a,b)}rampParam(a,b,c=0){audioParamRamp(a,b,c);if(c){var d=new ConstantSourceNode(audioContext,{offset:0});d.connect(this.input);d.onended=()=>d.disconnect();d.start();d.stop(audioContext.currentTime+c)}}connect(a){const b=a&&"input"in a?a.input:a;ASSERT(b&&"function"===typeof b.connect,"target must be an AudioEffect or AudioNode");this.output.disconnect();this.output.connect(b);return a}disconnect(){this.output.disconnect()}connectEffect(a,b=a){this.input.connect(a);b.connect(this.wetGain)}}class AudioFilter extends AudioEffect{constructor(a="lowpass",b=1e3,c=1,d=1){super(d);ASSERT(isNumber(b)&&0<=b,"frequency must be positive or zero");ASSERT(isNumber(c),"q must be a number");this.node=audioContext.createBiquadFilter();this.node.type=a;this.node.frequency.value=b;this.node.Q.value=c;this.connectEffect(this.node)}setFrequency(a,b=0){ASSERT(isNumber(a)&&0<=a,"frequency must be positive or zero");this.rampParam(this.node.frequency,a,b)}setQ(a,b=0){ASSERT(isNumber(a),"q must be a number");this.rampParam(this.node.Q,a,b)}}class AudioReverb extends AudioEffect{constructor(a=2,b=2,c=.5){super(c);this.node=audioContext.createConvolver();this.setRoom(a,b);this.connectEffect(this.node)}setRoom(a,b=2){ASSERT(isNumber(a)&&0<a,"duration must be positive");ASSERT(isNumber(b)&&0<b,"decay must be positive");this.node.buffer=this.createImpulse(a,b)}createImpulse(a,b){var c=audioContext.sampleRate;a=max(1,c*a|0);c=audioContext.createBuffer(2,a,c);for(let d=2;d--;){const e=c.getChannelData(d);for(let f=a;f--;)e[f]=rand(-1,1)*(1-f/a)**b}return c}}class AudioDelay extends AudioEffect{constructor(a=.3,b=.4,c=.5){super(c);this.node=audioContext.createDelay(5);this.feedbackGain=audioContext.createGain();this.node.connect(this.feedbackGain).connect(this.node);this.connectEffect(this.node);this.setTime(a);this.setFeedback(b)}setTime(a,b=0){ASSERT(isNumber(a)&&0<=a&&5>=a,"time must be between 0 and 5");this.rampParam(this.node.delayTime,a,b)}setFeedback(a,b=0){ASSERT(isNumber(a),"feedback must be a number");this.rampParam(this.feedbackGain.gain,clamp(a,0,.95),b)}}class AudioDistortion extends AudioEffect{constructor(a=.5,b=1){super(b);this.node=audioContext.createWaveShaper();this.node.oversample="2x";this.amount=a;this.setAmount(a);this.connectEffect(this.node)}setAmount(a){ASSERT(isNumber(a),"amount must be a number");this.amount=a=clamp(a);a*=100*a;const b=new Float32Array(1024);for(let c=1024;c--;){const d=2*c/1023-1;b[c]=(1+a)*d/(1+a*abs(d))}this.node.curve=b}}class AudioCompressor extends AudioEffect{constructor(a=-24,b=12,c=1){super(c);ASSERT(isNumber(a),"threshold must be a number");ASSERT(isNumber(b)&&1<=b,"ratio must be 1 or more");this.node=audioContext.createDynamicsCompressor();this.node.threshold.value=a;this.node.ratio.value=b;this.connectEffect(this.node)}setThreshold(a,b=0){ASSERT(isNumber(a),"threshold must be a number");this.rampParam(this.node.threshold,a,b)}setRatio(a,b=0){ASSERT(isNumber(a)&&1<=a,"ratio must be 1 or more");this.rampParam(this.node.ratio,a,b)}}let uiSystem,uiDebug=0;function uiSetDebug(a){uiDebug="boolean"===typeof a?a?1:0:a}class UISystemPlugin{constructor(a=mainContext){function b(c){let d,e;c.parent?(d=c.parent.nativePos,e=c.parent.size):(d=uiSystem.screenToNative(mainCanvasSize.scale(.5)),e=uiSystem.nativeHeight?vec2(mainCanvasSize.x*uiSystem.nativeHeight/mainCanvasSize.y,uiSystem.nativeHeight):mainCanvasSize);const f=c.anchor;c.nativePos=d.add(e.multiply(f).scale(.5)).subtract(c.size.multiply(f).scale(.5)).add(c.localPos)}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;this._onKeyDown=c=>this._keyInputObject?.onKeyDown(c);engineAddPlugin(function(){function c(g){if(!g.destroyed&&g.visible){b(g);for(let h=g.children.length;h--;){const k=g.children[h];k&&c(k)}g.destroyed||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()})}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,r=vec2()){const q=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,q),p||r.x||r.y)&&(q.shadowColor=n.toString(),q.shadowBlur=p,q.shadowOffsetX=r.x,q.shadowOffsetY=r.y);drawTextScreen(a,b,c.y,d,e,f,g,h,k,l?c.x:void 0,0,q);q.shadowColor="#0000"}setupDragAndDrop(a,b,c,d){if(this._dragListeners)for(const[f,g]of this._dragListeners)document.removeEventListener(f,g);this._dragListeners=[];const e=(f,g)=>{const h=k=>{k.preventDefault();f&&f(k)};document.addEventListener(g,h);this._dragListeners.push([g,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}get keyInputObject(){return this._keyInputObject}set keyInputObject(a){const b=!!this._keyInputObject;this._keyInputObject=a;!b&&a?document.addEventListener("keydown",this._onKeyDown):b&&!a&&document.removeEventListener("keydown",this._onKeyDown)}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=()=>{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);return h}}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.nativePos=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;this.anchor=vec2();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){uiSystem.activeObject===this&&(uiSystem.activeObject=void 0);uiSystem.hoverObject===this&&(uiSystem.hoverObject=void 0);uiSystem.lastHoverObject===this&&(uiSystem.lastHoverObject=void 0);uiSystem.navigationObject===this&&(uiSystem.navigationObject=void 0);uiSystem.keyInputObject===this&&(uiSystem.keyInputObject=void 0);this.destroyed=1;this.parent?.removeChild(this);for(const a of this.children)a.parent=void 0,a.destroy();this.children.length=0}}isMouseOverlapping(){if(!mouseInWindow)return!1;const a=isTouchDevice?this.size.add(vec2(this.extraTouchSize||0)):this.size,b=uiSystem.screenToNative(mousePosScreen);return isOverlapping(this.nativePos,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.nativePos,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(){let a="type = "+this.constructor.name;this.text&&(a+="\ntext = "+this.text);if(this.nativePos.x||this.nativePos.y)a+="\nnativePos = "+this.nativePos;if(this.localPos.x||this.localPos.y)a+="\nlocalPos = "+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.nativePos,this.size,CLEAR_BLACK,4,a)}click(a=!0){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(isStringLike(c),"ui text must be a string");ASSERT(["left","center","right"].includes(d),"ui text align must be left, center, or right");ASSERT(isStringLike(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.nativePos,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(isStringLike(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.nativePos,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.nativePos,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(isStringLike(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.nativePos.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(isStringLike(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.nativePos.add(a.multiply(vec2(-1))),this.nativePos.add(a.multiply(vec2(1))),this.lineWidth,this.lineColor);uiSystem.drawLine(this.nativePos.add(a.multiply(vec2(-1,1))),this.nativePos.add(a.multiply(vec2(1,-1))),this.lineWidth,this.lineColor)}a=this.getTextSize();const b=this.nativePos.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(isStringLike(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.nativePos.x:this.nativePos.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.nativePos.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.nativePos.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.nativePos,a,this.textColor,this.textLineWidth,this.textLineColor,this.align,this.font,this.fontStyle,!0,this.textShadow)}navigatePressed(){this.value=this.value?0:1;this.onChange();this.onRelease();super.navigatePressed()}}class UIVideo extends UIObject{constructor(a,b,c,d=!1,e=!1,f=1){super(a,b||vec2());ASSERT(isStringLike(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.nativePos.x,this.nativePos.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())}}class UILayout extends UIObject{constructor(a,b=1,c=10,d=10,e=!1){super(a);ASSERT(isNumber(b)&&1<=b,"ui layout columns must be a number >= 1");ASSERT(isNumber(c),"ui layout gap must be a number");ASSERT(isNumber(d),"ui layout padding must be a number");this.columns=b;this.gap=c;this.padding=d;e&&(this.color=CLEAR_BLACK,this.gradientColor=void 0,this.lineWidth=0,this.shadowColor=CLEAR_BLACK);this.relayout()}addChild(a){super.addChild(a);this.relayout();return a}removeChild(a){super.removeChild(a);this.relayout()}relayout(){const a=this.children.length;if(a){var b=this.columns,c=ceil(a/b),d=Array(b).fill(0),e=Array(c).fill(0);for(var f=0;f<a;++f){var g=f%b,h=floor(f/b),k=this.children[f];d[g]=max(d[g],k.size.x);e[h]=max(e[h],k.size.y)}f=this.gap*(b-1);for(var l of d)f+=l;l=this.gap*(c-1);for(var m of e)l+=m;m=Array(b);g=0;for(h=0;h<b;++h)m[h]=g,g+=d[h];g=Array(c);h=0;for(k=0;k<c;++k)g[k]=h,h+=e[k];for(c=0;c<a;++c)h=c%b,k=floor(c/b),this.children[c].localPos=vec2(-f/2+m[h]+this.gap*h+d[h]/2,-l/2+g[k]+this.gap*k+e[k]/2);this.size=vec2(f+2*this.padding,l+2*this.padding)}else this.size=vec2(2*this.padding)}}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(){if(!this.destroyed){ASSERT(this.body,"Box2dObject has no body to destroy");box2d.world.DestroyBody(this.body);var a=box2d.objects.indexOf(this);0<=a&&box2d.objects.splice(a,1);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=box2d.instance._malloc(8*a.length);for(let k=0,l=0;k<a.length;++k)box2d.instance.HEAPF32[f+l>>2]=a[k].x,l+=4,box2d.instance.HEAPF32[f+l>>2]=a[k].y,l+=4;const g=box2d.instance.wrapPointer(f,box2d.instance.b2Vec2),h=new box2d.instance.b2PolygonShape;h.Set(g,a.length);box2d.instance._free(f);return this.addShape(h,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);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);void 0!==a&&d.set_center(box2d.vec2dTo(a));void 0!==b&&d.set_mass(b);void 0!==c&&d.set_I(c);this.body.SetMassData(d)}setFilterData(a=0,b=0,c=0){this.getFixtureList().forEach(d=>{const e=d.GetFilterData();e.set_categoryBits(a);e.set_maskBits(65535&~b);e.set_groupIndex(c);d.SetFilterData(e)})}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();a=a.scale(this.getMass());this.body.ApplyLinearImpulse(box2d.vec2dTo(a),box2d.vec2dTo(b))}applyImpulse(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*this.getInertia())}applyAngularImpulse(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(b.body.GetAngle()-a.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=a.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(b.body.GetAngle()-a.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=a.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(b.body.GetAngle()-a.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&&c&&(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&&c&&(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=box2d.vec2From(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 r=l?-1:1,q=l?-h:h;drawTile(a,p,n,d,h,!1,f,k,l,m);for(var u=4;u--;){var t=u%2,v=b.multiply(vec2(t?1===u?1:-1:0,t?0:u?-1:1));t=vec2(t?e:p.x,t?p.y:e);const x=n.offset(c.size.multiply(vec2(1===u?1:3===u?-1:0,0===u?-r:2===u?r:0)));drawTile(a.add(v.rotate(q)),t,x,d,h,!1,f,k,l,m)}for(e=4;e--;)u=1<e,v=e&&3>e,p=b.multiply(vec2(u?-1:1,v?-1:1)),u=n.offset(c.size.multiply(vec2(u?-1:1,v?r:-r))),drawTile(a.add(p.rotate(q)),g,u,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),r=c.frame(2),q=b.add(vec2(g-2*e));g=vec2(e);b=b.scale(.5).subtract(g.scale(.5));c=l?-1:1;const u=l?-h:h;drawTile(a,q,r,d,h,!1,f,k,l,m);for(r=4;r--;){const v=h+r*PI/2;var t=r%2;const x=b.multiply(vec2(t?1===r?1:-1:0,t?0:r?-c:c));t=vec2(t?q.y:q.x,e);drawTile(a.add(x.rotate(u)),t,p,d,v,!1,f,k,l,m)}for(e=4;e--;)p=h+e*PI/2,q=b.multiply(vec2(!e||2<e?-1:1,1<e?-c:c)),drawTile(a.add(q.rotate(u)),g,n,d,p,!1,f,k,l,m)}function drawCrescent(a,b=1,c=0,d=WHITE,e=0,f=!1,g=0,h=BLACK,k=glEnable,l=!1,m){b=getCrescentPoints(vec2(),b,c,0,f);drawPoly(b,d,g,h,a,e,k,l,m)}function getCrescentPoints(a,b=1,c=0,d=0,e=!1,f=glCircleSides){ASSERT(isVector2(a),"pos must be a vec2");ASSERT(isNumber(b)&&isNumber(c),"size and percent must be numbers");c=mod(4*c,4);2<=c&&(d+=PI);c=2>=c?c-1:3-c;e&&(c=-c,d+=PI);e=[];f=max(3,f>>1);b/=2;for(var g=0;g<=f;g++){var h=g/f*PI;e.push(vec2(b*cos(h),b*sin(h)).rotate(d).add(a))}for(g=f;0<=g;g--)h=g/f*PI,e.push(vec2(b*cos(h),-b*c*sin(h)).rotate(d).add(a));return e}let textureSheetSize=2048,textureSheetPadding=1,textureSheets=[],textureSheetQueue=Promise.resolve(),textureSheetPendingCount=0;class TextureSheet{constructor(a=textureSheetSize){ASSERT(0<a,"texture sheet size must be positive");this.size=a;this.context=headlessMode?void 0:createCanvasContext(a);this.canvas=this.context?.canvas;this.textureInfo=new TextureInfo(this.canvas);this.cursor=vec2();this.rowHeight=0;this.glDirty=!1;headlessMode&&(this.textureInfo.size=vec2(a),this.textureInfo.sizeInverse=vec2(1/a))}tryAdd(a,b=a,c=textureSheetPadding,d=0){ASSERT(isVector2(a)&&isVector2(b),"sizes must be vec2");ASSERT(0<b.x&&0<b.y,"frame size must be positive");isNumber(d)&&(d=vec2(d));ASSERT(isVector2(d)&&0<=d.x&&0<=d.y,"sourcePadding must be a number or vec2 >= 0");var e=b.x+2*d.x,f=b.y+2*d.y;ASSERT(0===a.x%e&&0===a.y%f,"image size must be a multiple of the padded frame size");var g=b.x+2*c;d=b.y+2*c;var h=this.size/g|0;ASSERT(0<h,"frame is too wide to fit on a texture sheet");e=a.x/e;f=a.y/f*e;a=min(e,h);g*=a;d*=ceil(f/a);h=this.cursor.x;e=this.cursor.y;f=this.rowHeight;h+g>this.size&&(h=0,e+=f,f=0);if(!(e+d>this.size))return this.cursor.x=h+g,this.cursor.y=e,this.rowHeight=max(f,d),new TileInfo(vec2(h+c,e+c),b,this.textureInfo,c,0,a)}drawImage(a,b,c=!0,d=0){ASSERT(!!this.context,"texture sheet has no canvas");isNumber(d)&&(d=vec2(d));const e=b.size,f=e.x+2*d.x,g=e.y+2*d.y,h=a.width/f;var k=a.height/g*h;const l=b.columns||k,m=e.x+2*b.padding,n=e.y+2*b.padding;for(;k--;)this.context.drawImage(a,k%h*f+d.x,(k/h|0)*g+d.y,e.x,e.y,b.pos.x+k%l*m,b.pos.y+(k/l|0)*n,e.x,e.y);this.glDirty=!0;c&&this.updateTexture()}updateTexture(){this.glDirty&&(this.glDirty=!1,this.textureInfo.createWebGLTexture())}}function loadSprite(a,b,c=textureSheetPadding,d=0){ASSERT(isStringLike(a),"image src must be a string");ASSERT(!b||isVector2(b)||isNumber(b),"frameSize must be a vec2 or number");ASSERT(isNumber(c),"padding must be a number");ASSERT(isNumber(d)||isVector2(d),"sourcePadding must be a number or vec2");isNumber(b)&&(b=vec2(b));const e=new TileInfo(vec2(),vec2(),void 0,c,0);if(headlessMode)return e;e.textureInfo=(textureSheets[0]||textureSheetCreate()).textureInfo;const f=new Image,g=new Promise(h=>{f.onerror=f.onload=h;f.crossOrigin="anonymous";f.src=a});++textureSheetPendingCount;textureSheetQueue=textureSheetQueue.then(async()=>{await g;if(f.width){const h=vec2(f.width,f.height),{sheet:k,tile:l}=textureSheetAdd(h,b,c,d);Object.assign(e,l);k.drawImage(f,e,!1,d)}else LOG("loadSprite failed to load image:",a);--textureSheetPendingCount||textureSheets.forEach(h=>h.updateTexture())});return e}function loadAtlas(a,b,c=textureSheetPadding){ASSERT(isStringLike(a),"atlas image src must be a string");ASSERT(isStringLike(b)||"object"===typeof b,"atlas json must be a path or object");ASSERT(isNumber(c),"padding must be a number");const d={};if(headlessMode)return d;const e="object"===typeof b?Promise.resolve(b):fetch(b).then(h=>h.ok&&h.json()).catch(()=>{}),f=new Image,g=new Promise(h=>{f.onerror=f.onload=h;f.crossOrigin="anonymous";f.src=a});++textureSheetPendingCount;textureSheetQueue=textureSheetQueue.then(async()=>{var h=await e;await g;if(f.width&&h)for(const k of parseAtlas(h)){h=k.frames[0].sourceSize;const l=vec2(h.x*k.frames.length,h.y),{sheet:m,tile:n}=textureSheetAdd(l,h,c),p=m.context,r=h.x+2*c,q=h.y+2*c;k.frames.forEach((u,t)=>{const v=n.pos.x+t%n.columns*r+u.offset.x;t=n.pos.y+(t/n.columns|0)*q+u.offset.y;u.rotated?(p.save(),p.translate(v,t),p.rotate(-PI/2),p.drawImage(f,u.pos.x,u.pos.y,u.size.y,u.size.x,-u.size.y,0,u.size.y,u.size.x),p.restore()):p.drawImage(f,u.pos.x,u.pos.y,u.size.x,u.size.y,v,t,u.size.x,u.size.y)});m.glDirty=!0;d[k.name]=n}else LOG("loadAtlas failed to load:",a,b);--textureSheetPendingCount||textureSheets.forEach(k=>k.updateTexture())});return d}function parseAtlas(a){ASSERT(!!a?.frames,"unrecognized atlas format, expected TexturePacker or Aseprite json");var b=(isArray(a.frames)?a.frames.map(f=>[f.filename,f]):Object.entries(a.frames)).map(([f,g])=>({name:f.replace(/\.[^.\\/]+$/,""),pos:vec2(g.frame.x,g.frame.y),size:vec2(g.frame.w,g.frame.h),offset:vec2(g.spriteSourceSize?.x??0,g.spriteSourceSize?.y??0),sourceSize:vec2(g.sourceSize?.w??g.frame.w,g.sourceSize?.h??g.frame.h),rotated:!!g.rotated}));const c=[];a=a.meta?.frameTags;if(a?.length){const f=new Set;for(var d of a){c.push({name:d.name,frames:b.slice(d.from,d.to+1)});for(var e=d.from;e<=d.to;++e)f.add(e)}b.forEach((g,h)=>f.has(h)||c.push({name:g.name,frames:[g]}));return c}d=new Map;for(e of b)(b=e.name.match(/^(.+?)([-_ ])?(\d+)$/))&&!b[2]&&/\d$/.test(b[1])&&(b=void 0),a=b?b[1]:e.name,e.groupIndex=b?Number(b[3]):void 0,d.has(a)||d.set(a,[]),d.get(a).push(e);for(const[f,g]of d)g.sort((h,k)=>h.groupIndex-k.groupIndex),1<g.length&&g.every((h,k)=>h.groupIndex===g[0].groupIndex+k)&&g.every(h=>h.sourceSize.x===g[0].sourceSize.x&&h.sourceSize.y===g[0].sourceSize.y)?c.push({name:f,frames:g}):g.forEach(h=>c.push({name:h.name,frames:[h]}));return c}async function spritesReady(){for(;textureSheetPendingCount;)await textureSheetQueue}function textureSheetCreate(){const a=new TextureSheet;textureSheets.push(a);return a}function textureSheetAdd(a,b,c,d){let e,f;for(e of textureSheets)if(f=e.tryAdd(a,b,c,d))break;f||(e=textureSheetCreate(),f=e.tryAdd(a,b,c,d),ASSERT(!!f,"image is too large to fit on a texture sheet"));return{sheet:e,tile:f}}function setTextureSheetSize(a){textureSheetSize=a}function setTextureSheetPadding(a){textureSheetPadding=a}const tweenActive=[];let lastTime=0,lastTimeReal=0;function isLerpable(a){return a&&"function"===typeof a.lerp}class Tween{constructor(a,b=0,c=1,d=1,e={}){ASSERT("function"===typeof a,"Tween callback must be a function");isLerpable(b)?ASSERT(b.constructor===c.constructor,"Tween start and end must be the same type"):(ASSERT(isNumber(b),"Tween start must be a number or have a .lerp method"),ASSERT(isNumber(c),"Tween end must be a number when start is a number"));ASSERT(isNumber(d)&&0<d,"Tween duration must be > 0");this.callback=a;this.start=b;this.end=c;this.life=this.duration=d;this.ease=e.ease||Ease.LINEAR;this.useRealTime=!!e.useRealTime;this.paused=!!e.paused;this.thenCallback=void 0;this.loopRemaining=0;tweenActive.push(this);a(this.interp(d))}setEase(a){this.ease=a;return this}then(a){this.thenCallback=a;this.loopRemaining=0;return this}loop(a=Infinity){this.loopRemaining=a;this.thenCallback=()=>loopContinuation(this);return this}pingPong(a=Infinity){this.loopRemaining=a;this.thenCallback=()=>pingPongContinuation(this);return this}pause(){this.paused=!0}resume(){this.paused=!1}restart(){this.life=this.duration;this.paused=!1;0>tweenActive.indexOf(this)&&tweenActive.push(this);this.callback(this.interp(this.duration))}isActive(){return!this.paused&&0<=tweenActive.indexOf(this)}getPercent(){return percent(this.duration-this.life,0,this.duration)}getValue(){return this.interp(this.life)}interp(a){a=this.ease((this.duration-a)/this.duration);return isLerpable(this.start)?this.start.lerp(this.end,a):this.start+(this.end-this.start)*a}stop(){const a=tweenActive.indexOf(this);0<=a&&tweenActive.splice(a,1);this.thenCallback=void 0}}const Ease={LINEAR:a=>a,POWER:a=>b=>b**a,SINE:a=>1-cos(PI/2*a),CIRC:a=>1-(1-a*a)**.5,EXPO:a=>0===a?0:2**(10*a-10),BACK:a=>a*a*(2.70158*a-1.70158),ELASTIC:a=>0===a?0:1===a?1:-(2**(10*a-10))*sin((37-40*a)*PI/6),SPRING:a=>1-(sin(PI*(1-a)*(.2+2.5*(1-a)**3))*a**2.2+(1-a))*(1+1.2*a),BOUNCE:a=>{a=1-a;return 1-(a<4/11?7.5625*a*a:a<8/11?7.5625*(a-=6/11)*a+.75:a<10/11?7.5625*(a-=9/11)*a+.9375:7.5625*(a-=10.5/11)*a+.984375)},IN:a=>a,OUT:a=>b=>1-a(1-b),IN_OUT:a=>Ease.PIECEWISE(a,Ease.OUT(a)),PIECEWISE:(...a)=>{const b=a.length;return c=>{const d=c*b-1e-9>>0;return(a[d]((c-d/b)*b)+d)/b}},BEZIER:(a,b,c,d)=>{const e=f=>{var g=1-f;const h=3*g*g*f;g=3*g*f*f;f**=3;return[h*a+g*c+f,h*b+g*d+f]};return f=>{let g=0,h=1;for(let k=0;128>k;k++){const l=(g+h)/2,[m,n]=e(l);if(1e-5>abs(m-f))return n;m<f?g=l:h=l}return e((g+h)/2)[1]}}};function tweenProperty(a,b,c,d,e=1,f={}){ASSERT(null!=a&&"object"===typeof a,"tweenProperty target must be an object");ASSERT(isStringLike(b)&&0<b.length,"tweenProperty propertyPath must be a non-empty string");const g=b.split("."),h=g.pop();return new Tween(k=>{let l=a;for(const m of g)l=l[m],ASSERT(null!=l,"tweenProperty path does not resolve: "+b);l[h]=k},c,d,e,f)}function loopContinuation(a){Infinity!==a.loopRemaining&&1>=a.loopRemaining||(Infinity!==a.loopRemaining&&--a.loopRemaining,a.life=a.duration,a.thenCallback=()=>loopContinuation(a),tweenActive.push(a),a.callback(a.interp(a.duration)))}function pingPongContinuation(a){if(!(Infinity!==a.loopRemaining&&1>=a.loopRemaining)){Infinity!==a.loopRemaining&&--a.loopRemaining;var b=a.start;a.start=a.end;a.end=b;a.life=a.duration;a.thenCallback=()=>pingPongContinuation(a);tweenActive.push(a);a.callback(a.interp(a.duration))}}function tweenUpdate(a,b){void 0===a?(a=time-lastTime,b=timeReal-lastTimeReal,lastTime=time,lastTimeReal=timeReal):void 0===b&&(b=a);for(let d=tweenActive.length;d--;){const e=tweenActive[d];if(!e.paused){var c=e.useRealTime?b:a;0>=c||(e.life-=c,0<e.life?e.callback(e.interp(e.life)):(e.callback(e.interp(0)),tweenActive.splice(d,1),c=e.thenCallback,e.thenCallback=void 0,c&&c()))}}}function tweenStopAll(){for(const a of tweenActive)a.thenCallback=void 0;tweenActive.length=0}engineAddPlugin(tweenUpdate);const PATHFINDER_DIAGONAL_COST=Math.SQRT2,PATHFINDER_TILE_VEC=vec2(1);class PathFinderNode{constructor(a,b){this.pos=vec2(a,b);this.posWorld=vec2();this.walkable=!1;this.f=this.g=this.cost=0;this.parent=null;this.isClosed=this.isOpen=!1}reset(){this.walkable=!1;this.f=this.g=this.cost=0;this.parent=null;this.isClosed=this.isOpen=!1}isClear(){return this.walkable&&0===this.cost}}class PathFinder{constructor(a){isVector2(a)?(this.size=a.floor(),this.tileLayer=void 0):(ASSERT(a&&isVector2(a.size)&&"function"===typeof a.getCollisionData,"PathFinder requires a Vector2 size or a TileCollisionLayer"),this.size=a.size,this.tileLayer=a);this.heuristicWeight=1;this.maxLoop=1e3;this.smoothPath=!0;this.debug=!1;this.debugTime=1;this.nodes=Array(this.size.x*this.size.y);for(a=0;a<this.size.y;++a)for(let b=0;b<this.size.x;++b)this.nodes[b+a*this.size.x]=new PathFinderNode(b,a);this.collisionScratch=vec2()}isWalkable(a,b){return this.tileLayer?!this.tileLayer.getCollisionData(this.collisionScratch.set(a,b)):!0}getCost(a,b){return 0}getNode(a,b){return 0>a||0>b||a>=this.size.x||b>=this.size.y?null:this.nodes[a+b*this.size.x]}worldToTile(a){const b=this.tileLayer?this.tileLayer.pos.y:0;return vec2(floor(a.x-(this.tileLayer?this.tileLayer.pos.x:0)),floor(a.y-b))}tileToWorld(a,b){return vec2(a+.5+(this.tileLayer?this.tileLayer.pos.x:0),b+.5+(this.tileLayer?this.tileLayer.pos.y:0))}buildNodeData(){const a=this.size.x,b=this.size.y,c=this.tileLayer?this.tileLayer.pos.x:0,d=this.tileLayer?this.tileLayer.pos.y:0;for(let e=0;e<b;++e)for(let f=0;f<a;++f){const g=this.nodes[f+e*a];g.reset();const h=!!this.isWalkable(f,e),k=h?max(0,this.getCost(f,e)):0;g.walkable=h;g.cost=k;g.posWorld.set(f+.5+c,e+.5+d);this.debug&&0<this.debugTime&&(h?0<k&&debugRect(g.posWorld,PATHFINDER_TILE_VEC,rgb(1,0,0,min(.2,.05*k)),this.debugTime):debugRect(g.posWorld,PATHFINDER_TILE_VEC,rgb(1,0,0,.25),this.debugTime))}}aStarSearch(a,b){ASSERT(a&&b,"aStarSearch needs both endpoints");ASSERT(a!==b,"aStarSearch: start and end must differ — caller should handle trivial case");ASSERT(a.walkable&&b.walkable,"aStarSearch: endpoints must be walkable");const c=[a];a.isOpen=!0;for(a=0;0<c.length;){var d=0,e=c[0].f;for(var f=1;f<c.length;++f)c[f].f<e&&(e=c[f].f,d=f);e=c[d];if(e===b)break;if(++a>this.maxLoop)break;e.isOpen=!1;c.splice(d,1);e.isClosed=!0;this.debug&&0<this.debugTime&&debugRect(e.posWorld,PATHFINDER_TILE_VEC,rgb(1,1,1,.05),this.debugTime);for(d=-1;1>=d;++d)for(f=-1;1>=f;++f){if(0===f&&0===d)continue;const h=this.getNode(e.pos.x+f,e.pos.y+d);if(!h||!h.walkable||h.isClosed)continue;var g=1;if(0!==f&&0!==d){g=this.getNode(e.pos.x+f,e.pos.y);if(!g||!g.walkable)continue;g=this.getNode(e.pos.x,e.pos.y+d);if(!g||!g.walkable)continue;g=PATHFINDER_DIAGONAL_COST}g=e.g+g+h.cost;if(!h.isOpen)h.isOpen=!0,c.push(h);else if(g>=h.g)continue;h.parent=e;h.g=g;g=abs(b.pos.x-h.pos.x);const k=abs(b.pos.y-h.pos.y);g=max(g,k)+(Math.SQRT2-1)*min(g,k);h.f=h.g+g*this.heuristicWeight}}return null!==b.parent}getNearestClearNode(a,b=10,c=!0){ASSERT(isVector2(a),"worldPos must be a Vector2");c&&this.buildNodeData();var d=this.tileLayer?this.tileLayer.pos.y:0;c=floor(a.x-(this.tileLayer?this.tileLayer.pos.x:0));d=floor(a.y-d);for(let f=0;f<=b;++f){let g=null,h=0;for(let k=-f;k<=f;++k)for(let l=-f;l<=f;++l){if(0<f&&abs(l)!==f&&abs(k)!==f)continue;const m=this.getNode(c+l,d+k);if(!m||!m.isClear())continue;var e=m.posWorld.x-a.x;const n=m.posWorld.y-a.y;e=e*e+n*n;if(!g||e<h)g=m,h=e}if(g)return g}return null}smoothPathCorners(a){if(!(2>=a.length))for(var b=1;b<a.length-1;){var c=a[b-1],d=a[b],e=a[b+1],f=e.pos.x-c.pos.x,g=e.pos.y-c.pos.y,h=f*f+g*g;f=d.pos.x-c.pos.x;var k=d.pos.y-c.pos.y;g=e.pos.x-d.pos.x;var l=e.pos.y-d.pos.y;if(1===h)this.debug&&0<this.debugTime&&debugCircle(d.posWorld,.3,rgb(.5,0,.5,.5),this.debugTime),a.splice(b,1),b=max(1,b-1);else{if(2===h){if(this.debug&&0<this.debugTime&&debugCircle(d.posWorld,.3,rgb(1,0,0,.5),this.debugTime),c.pos.y===d.pos.y&&e.pos.x===d.pos.x?(d=c.pos.x,e=e.pos.y):(d=e.pos.x,e=c.pos.y),(d=this.getNode(d,e))&&d.isClear()){a.splice(b,1);b=max(1,b-1);continue}}else if(5===h){this.debug&&0<this.debugTime&&debugCircle(d.posWorld,.3,rgb(1,1,0,.5),this.debugTime);k=2<=b?a[b-2]:c;0===f||0===g?(f=e.pos.x,g=d.pos.y,h=c.pos.x,c=d.pos.y):(f=d.pos.x,g=e.pos.y,h=d.pos.x,c=c.pos.y);var m=f-k.pos.x;const n=g-k.pos.y;l=h-k.pos.x;k=c-k.pos.y;m=m*m+n*n;k=l*l+k*k;if((k=this.getNode(m<k?f:f===h&&g===c?f:h,m<k?g:f===h&&g===c?g:c))&&k!==d&&k.isClear()&&(d=this.getNode(e.pos.x+h-f,e.pos.y+c-g))&&d.isClear()){a[b]=k;b=max(1,b-1);continue}}else if(4===h||8===h)if(this.debug&&0<this.debugTime&&debugCircle(d.posWorld,.3,rgb(0,1,0,.5),this.debugTime),f===g&&k===l){++b;continue}else if(c.pos.y===e.pos.y?(e=d.pos.x,d=c.pos.y):(e=c.pos.x,d=d.pos.y),(d=this.getNode(e,d))&&d.isClear()){a[b]=d;b=max(1,b-1);continue}++b}}}smoothPathStringPull(a){if(!(2>=a.length)){for(var b of a)if(!b.isClear())return;b=a.slice();a.length=0;a.push(b[0]);var c=0;for(let f=1;f<b.length;++f){const g=b[f];var d=b[c],e=b[f-1];if((d===e||0!==(e.pos.x-d.pos.x)*(g.pos.y-d.pos.y)-(e.pos.y-d.pos.y)*(g.pos.x-d.pos.x))&&!this.isLineClear(g.pos,a[a.length-1].pos)){d=!1;for(e=f+1;e<b.length;++e)if(this.isLineClear(b[e].pos,a[a.length-1].pos)){d=!0;break}if(d)this.debug&&0<this.debugTime&&debugLine(g.posWorld,a[a.length-1].posWorld,rgb(0,0,1,.3),.02,this.debugTime);else{for(;c<b.length;++c)if(d=b[c],this.isLineClear(g.pos,d.pos)){a.push(d);f=c;break}ASSERT(c<b.length,"smoothPathStringPull: ran out of candidates")}}}a.push(b[b.length-1])}}dropCollinearNodes(a){for(let b=a.length-2;1<=b;--b){const c=a[b-1],d=a[b],e=a[b+1];(d.pos.x-c.pos.x)*(e.pos.y-c.pos.y)===(d.pos.y-c.pos.y)*(e.pos.x-c.pos.x)&&a.splice(b,1)}}isNodeClear(a,b){a=this.getNode(a,b);return null!==a&&a.isClear()}isLineClear(a,b){ASSERT(isVector2(a)&&isVector2(b),"isLineClear needs Vector2 endpoints");ASSERT(this.isNodeClear(a.x,a.y)&&this.isNodeClear(b.x,b.y),"isLineClear endpoints must be in-bounds and clear");const c=b.x-a.x,d=b.y-a.y;var e=abs(c);const f=abs(d),g=sign(c),h=sign(d);let k=a.x,l=a.y;if(f===e){for(;k!==b.x;){if(!(k===a.x||this.isNodeClear(k,l)&&this.isNodeClear(k,l-h))||!this.isNodeClear(k,l+h))return!1;k+=g;l+=h}if(!this.isNodeClear(b.x,b.y-h))return!1}else if(f<e)if(0===d)for(k+=g;k!==b.x;){if(!this.isNodeClear(k,l))return!1;k+=g}else{for(e=a.y;k!==b.x;){l=a.y+Math.trunc(d*(k-a.x)/c);if(!(e===l||this.isNodeClear(k-g,l+h)&&this.isNodeClear(k,l-h)))return!1;e=l;if(k!==a.x&&!this.isNodeClear(k,l))return!1;l+=h;if(!this.isNodeClear(k,l))return!1;k+=g}if(!this.isNodeClear(b.x,b.y-h))return!1}else if(0===c)for(l+=h;l!==b.y;){if(!this.isNodeClear(k,l))return!1;l+=h}else{for(e=a.x;l!==b.y;){k=a.x+Math.trunc(c*(l-a.y)/d);if(!(e===k||this.isNodeClear(k+g,l-h)&&this.isNodeClear(k-g,l)))return!1;e=k;if(l!==a.y&&!this.isNodeClear(k,l))return!1;k+=g;if(!this.isNodeClear(k,l))return!1;l+=h}if(!this.isNodeClear(b.x-g,b.y))return!1}return!0}findPath(a,b){ASSERT(isVector2(a)&&isVector2(b),"findPath needs Vector2 endpoints");this.buildNodeData();a=this.getNearestClearNode(a,10,!1);b=this.getNearestClearNode(b,10,!1);if(!a||!b)return[];if(a===b)return[a.posWorld.copy()];if(!this.aStarSearch(a,b))return[];for(a=[];b;b=b.parent)a.push(b);a.reverse();this.smoothPath&&(this.smoothPathCorners(a),this.smoothPathStringPull(a),this.dropCollinearNodes(a));b=a.map(c=>c.posWorld.copy());if(this.debug&&0<this.debugTime&&0<b.length){for(a=1;a<b.length;++a)debugLine(b[a-1],b[a],RED,.1,this.debugTime);for(const c of b)debugCircle(c,.5,rgb(1,0,0,.3),this.debugTime);debugCircle(b[0],.5,rgb(0,1,0,.5),this.debugTime);debugCircle(b[b.length-1],.5,rgb(0,1,0,.5),this.debugTime)}return b}}function vec3(a=0,b,c){return void 0===b?new Vector3(a,a,a):new Vector3(a,b,void 0===c?0:c)}function isVector3(a){return a instanceof Vector3&&a.isValid()}function ASSERT_VECTOR3_VALID(a){ASSERT(isVector3(a),"Vector3 is invalid.",a)}function randVector3(a=1,b=PI){b=rand(cos(b),1);const c=(1-b*b)**.5,d=rand(2*PI);return new Vector3(c*cos(d)*a,b*a,c*sin(d)*a)}function randInSphere(a=1,b=0){if(0>=a)return new Vector3;b=clamp(b/a);return randVector3(a*rand(b**3,1)**(1/3))}class Vector3{constructor(a=0,b=0,c=0){ASSERT(isNumber(a)&&isNumber(b)&&isNumber(c),"Vector3 components must be numbers");this.x=a;this.y=b;this.z=c}set(a=0,b=0,c=0){this.x=a;this.y=b;this.z=c;ASSERT_VECTOR3_VALID(this);return this}setFrom(a){return this.set(a.x,a.y,a.z)}copy(){return new Vector3(this.x,this.y,this.z)}add(a){return new Vector3(this.x+a.x,this.y+a.y,this.z+a.z)}subtract(a){return new Vector3(this.x-a.x,this.y-a.y,this.z-a.z)}multiply(a){return new Vector3(this.x*a.x,this.y*a.y,this.z*a.z)}divide(a){return new Vector3(this.x/a.x,this.y/a.y,this.z/a.z)}scale(a){return new Vector3(this.x*a,this.y*a,this.z*a)}length(){return this.lengthSquared()**.5}lengthSquared(){return this.x**2+this.y**2+this.z**2}reflect(a,b=1){return this.subtract(a.scale((1+b)*this.dot(a)))}distance(a){return this.distanceSquared(a)**.5}distanceSquared(a){return(this.x-a.x)**2+(this.y-a.y)**2+(this.z-a.z)**2}normalize(a=1){const b=this.length();return b?this.scale(a/b):new Vector3}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+this.z*a.z}cross(a){return new Vector3(this.y*a.z-this.z*a.y,this.z*a.x-this.x*a.z,this.x*a.y-this.y*a.x)}lerp(a,b){ASSERT_VECTOR3_VALID(a);return this.add(a.subtract(this).scale(clamp(b)))}rotate(a,b){ASSERT_VECTOR3_VALID(a);const c=cos(b);b=sin(b);const d=a.dot(this)*(1-c);return this.scale(c).add(a.cross(this).scale(b)).add(a.scale(d))}rotateX(a){const b=cos(a);a=sin(a);return new Vector3(this.x,this.y*b-this.z*a,this.y*a+this.z*b)}rotateY(a){const b=cos(a);a=sin(a);return new Vector3(this.x*b+this.z*a,this.y,this.z*b-this.x*a)}rotateZ(a){const b=cos(a);a=sin(a);return new Vector3(this.x*b-this.y*a,this.x*a+this.y*b,this.z)}abs(){return new Vector3(abs(this.x),abs(this.y),abs(this.z))}floor(){return new Vector3(floor(this.x),floor(this.y),floor(this.z))}round(){return new Vector3(round(this.x),round(this.y),round(this.z))}snap(a){ASSERT_NUMBER_VALID(a);return new Vector3(floor(this.x*a)/a,floor(this.y*a)/a,floor(this.z*a)/a)}transform(a){return a.transformPoint(this)}transformDirection(a){return a.transformDirection(this)}isValid(){return isNumber(this.x)&&isNumber(this.y)&&isNumber(this.z)}toString(a=3){if(!this.isValid())return`(${this.x},${this.y},${this.z})`;const b=c=>(0>c?"":" ")+c.toFixed(a);return`(${b(this.x)},${b(this.y)},${b(this.z)} )`}}const matrix4Scratch=new Float32Array(16);class Matrix4{constructor(a){this.m=new Float32Array(16);ASSERT(!a||16==a.length,"Matrix4 takes 16 values, use copy() to duplicate a matrix");a?this.m.set(a):this.m[0]=this.m[5]=this.m[10]=this.m[15]=1}static identity(){return new Matrix4}static translation(a){ASSERT_VECTOR3_VALID(a);const b=new Matrix4;b.m[12]=a.x;b.m[13]=a.y;b.m[14]=a.z;return b}static rotation(a){ASSERT_VECTOR3_VALID(a);const b=cos(a.x),c=sin(a.x),d=cos(a.y),e=sin(a.y),f=cos(a.z);a=sin(a.z);const g=new Matrix4,h=g.m;h[0]=d*f+e*c*a;h[1]=b*a;h[2]=-e*f+d*c*a;h[4]=-d*a+e*c*f;h[5]=b*f;h[6]=e*a+d*c*f;h[8]=e*b;h[9]=-c;h[10]=d*b;return g}static scaling(a){ASSERT_VECTOR3_VALID(a);const b=new Matrix4;b.m[0]=a.x;b.m[5]=a.y;b.m[10]=a.z;return b}static perspective(a,b,c,d){ASSERT(0<c&&d>c,"a perspective projection needs 0 < near < far, or nothing is visible",c,d);a=1/tan(a/2);const e=new Matrix4,f=e.m;f[0]=a/b;f[5]=a;f[10]=Infinity==d?-1:(d+c)/(c-d);f[11]=-1;f[14]=Infinity==d?-2*c:2*d*c/(c-d);f[15]=0;return e}static orthographic(a,b,c,d,e,f){ASSERT(f>e&&Infinity!=f,"an orthographic projection needs a real far plane past near, Infinity is perspective only",e,f);const g=new Matrix4,h=g.m;h[0]=2/(b-a);h[5]=2/(d-c);h[10]=-2/(f-e);h[12]=-(b+a)/(b-a);h[13]=-(d+c)/(d-c);h[14]=-(f+e)/(f-e);return g}static lookAt(a,b,c=vec3(0,1,0)){b=a.subtract(b).normalize();b.lengthSquared()||(b=vec3(0,0,1));c=c.cross(b).normalize();c.lengthSquared()||(c=(.99<abs(b.y)?vec3(0,0,1):vec3(0,1,0)).cross(b).normalize());const d=b.cross(c);return new Matrix4([c.x,c.y,c.z,0,d.x,d.y,d.z,0,b.x,b.y,b.z,0,a.x,a.y,a.z,1])}copy(){return new Matrix4(this.m)}multiply(a){const b=this.m;a=a.m;const c=matrix4Scratch;for(let d=0;4>d;++d)for(let e=0;4>e;++e)c[4*d+e]=b[e]*a[4*d]+b[4+e]*a[4*d+1]+b[8+e]*a[4*d+2]+b[12+e]*a[4*d+3];this.m.set(c);return this}translate(a){return this.multiply(Matrix4.translation(a))}rotate(a){return this.multiply(Matrix4.rotation(a))}scale(a){return this.multiply(Matrix4.scaling(a))}transpose(){const a=this.m;for(let b=0;4>b;++b)for(let c=b+1;4>c;++c){const d=a[4*b+c];a[4*b+c]=a[4*c+b];a[4*c+b]=d}return this}invert(){const a=this.m,[b,c,d,e,f,g,h,k,l,m,n,p,r,q,u,t]=a,v=b*g-c*f,x=b*h-d*f,w=b*k-e*f,y=c*h-d*g,z=c*k-e*g,A=d*k-e*h,C=l*q-m*r,F=l*u-n*r,H=l*t-p*r,G=m*u-n*q,E=m*t-p*q,I=n*t-p*u;let B=v*I-x*E+w*G+y*H-z*F+A*C;if(!B)return this;B=1/B;a[0]=(g*I-h*E+k*G)*B;a[1]=(d*E-c*I-e*G)*B;a[2]=(q*A-u*z+t*y)*B;a[3]=(n*z-m*A-p*y)*B;a[4]=(h*H-f*I-k*F)*B;a[5]=(b*I-d*H+e*F)*B;a[6]=(u*w-r*A-t*x)*B;a[7]=(l*A-n*w+p*x)*B;a[8]=(f*E-g*H+k*C)*B;a[9]=(c*H-b*E-e*C)*B;a[10]=(r*z-q*w+t*v)*B;a[11]=(m*w-l*z-p*v)*B;a[12]=(g*F-f*G-h*C)*B;a[13]=(b*G-c*F+d*C)*B;a[14]=(q*x-r*y-u*v)*B;a[15]=(l*y-m*x+n*v)*B;return this}transformPoint(a){const b=this.m;return new Vector3(b[0]*a.x+b[4]*a.y+b[8]*a.z+b[12],b[1]*a.x+b[5]*a.y+b[9]*a.z+b[13],b[2]*a.x+b[6]*a.y+b[10]*a.z+b[14])}transformDirection(a){const b=this.m;return new Vector3(b[0]*a.x+b[4]*a.y+b[8]*a.z,b[1]*a.x+b[5]*a.y+b[9]*a.z,b[2]*a.x+b[6]*a.y+b[10]*a.z)}getTranslation(){return new Vector3(this.m[12],this.m[13],this.m[14])}toString(){const a=this.m;let b="";for(let c=0;4>c;++c)b+=`[${a[c].toFixed(2).padStart(7)} ${a[4+c].toFixed(2).padStart(7)} ${a[8+c].toFixed(2).padStart(7)} ${a[12+c].toFixed(2).padStart(7)} ]\n`;return b}}function buildMatrix(a,b,c){ASSERT(!a||isVector3(a),"pos must be a Vector3",a);ASSERT(!c||isVector3(c),"scale must be a Vector3",c);b=b&&(b.x||b.y||b.z)?Matrix4.rotation(b):new Matrix4;const d=b.m;c&&(d[0]*=c.x,d[1]*=c.x,d[2]*=c.x,d[4]*=c.y,d[5]*=c.y,d[6]*=c.y,d[8]*=c.z,d[9]*=c.z,d[10]*=c.z);a&&(d[12]=a.x,d[13]=a.y,d[14]=a.z);return b}class Ray3D{constructor(a=vec3(),b=vec3(0,0,-1)){ASSERT_VECTOR3_VALID(a);ASSERT_VECTOR3_VALID(b);this.origin=a;this.direction=b}getPosition(a){return this.origin.add(this.direction.scale(a))}copy(){return new Ray3D(this.origin.copy(),this.direction.copy())}}function isPointInBox3D(a,b,c){return abs(a.x-b.x)<=c.x/2&&abs(a.y-b.y)<=c.y/2&&abs(a.z-b.z)<=c.z/2}function isOverlapping3D(a,b,c,d=vec3()){a=a.subtract(c);return abs(a.x)<(b.x+d.x)/2&&abs(a.y)<(b.y+d.y)/2&&abs(a.z)<(b.z+d.z)/2}function collideSphereSphere(a,b,c,d){a=a.subtract(c);b+=d;d=a.length();if(!(d>=b))return d?a.normalize(b-d):vec3(0,b,0)}function collideSphereBox(a,b,c,d){d=d.scale(.5);var e=vec3(clamp(a.x,c.x-d.x,c.x+d.x),clamp(a.y,c.y-d.y,c.y+d.y),clamp(a.z,c.z-d.z,c.z+d.z));e=a.subtract(e);const f=e.lengthSquared();if(f)return f>=b*b?void 0:e.normalize(b-f**.5);a=a.subtract(c);return pushOutAxis3D(a,d.x-abs(a.x),d.y-abs(a.y),d.z-abs(a.z),b)}function collideSphereInBox(a,b,c,d){const e=max(0,d.x/2-b),f=max(0,d.y/2-b);b=max(0,d.z/2-b);a=vec3(clamp(a.x,c.x-e,c.x+e)-a.x,clamp(a.y,c.y-f,c.y+f)-a.y,clamp(a.z,c.z-b,c.z+b)-a.z);return a.lengthSquared()?a:void 0}function pushOutAxis3D(a,b,c,d,e=0){return b<=c&&b<=d?vec3((0<=a.x?1:-1)*(b+e),0,0):c<=d?vec3(0,(0<=a.y?1:-1)*(c+e),0):vec3(0,0,(0<=a.z?1:-1)*(d+e))}function collideSphereCylinder(a,b,c,d,e){var f=e/2;e=a.x-c.x;const g=a.z-c.z,h=a.y-c.y,k=(e**2+g**2)**.5,l=k?min(k,d)/k:0;c=vec3(c.x+e*l,clamp(a.y,c.y-f,c.y+f),c.z+g*l);a=a.subtract(c);if(c=a.lengthSquared())return c>=b*b?void 0:a.normalize(b-c**.5);d-=k;f-=abs(h);return d<=f?(k?vec3(e/k,0,g/k):vec3(1,0,0)).scale(d+b):vec3(0,(0<=h?1:-1)*(f+b),0)}function collideBoxBox3D(a,b,c,d){a=a.subtract(c);c=(b.x+d.x)/2-abs(a.x);const e=(b.y+d.y)/2-abs(a.y);b=(b.z+d.z)/2-abs(a.z);if(!(0>=c||0>=e||0>=b))return pushOutAxis3D(a,c,e,b)}function raycastSphere(a,b,c){const{origin:d,direction:e}=a;b=d.subtract(b);if(a=e.dot(e)){c=b.dot(b)-c*c;if(0>c)return 0;b=2*b.dot(e);c=b*b-4*a*c;if(!(0>c))return a=(-b-c**.5)/(2*a),0<=a?a:void 0}}function raycastPlane(a,b,c){const{origin:d,direction:e}=a;a=e.dot(c);if(!(1e-9>abs(a)))return b=b.subtract(d).dot(c)/a,0>b?void 0:b}function raycastBox(a,b,c){const{origin:d,direction:e}=a;c=c.scale(.5);a=b.subtract(c);b=b.add(c);c=0;let f=Infinity;for(const k of"xyz"){var g=d[k];const l=e[k];var h=a[k];const m=b[k];if(l){if(h=(h-g)/l,g=(m-g)/l,h>g&&([h,g]=[g,h]),c=max(c,h),f=min(f,g),c>f)return}else if(g<h||g>m)return}return c}let render3D;const RENDER3D_VERTEX_FLOATS=9,RENDER3D_VERTEX_BYTES=4*RENDER3D_VERTEX_FLOATS,RENDER3D_INSTANCE_FLOATS=33,RENDER3D_INSTANCE_BYTES=4*RENDER3D_INSTANCE_FLOATS,RENDER3D_INSTANCE_ATTRIBS=[[4,4,0],[5,4,16],[6,4,32],[7,4,48],[8,3,64],[9,3,76],[10,3,88],[11,4,100],[12,4,116]],RENDER3D_VERTEX_INPUTS="layout(location=0) in vec3 p;layout(location=1) in vec3 n;layout(location=2) in vec2 t;layout(location=3) in vec4 c;layout(location=4) in vec4 m0;layout(location=5) in vec4 m1;layout(location=6) in vec4 m2;layout(location=7) in vec4 m3;layout(location=8) in vec3 n0;layout(location=9) in vec3 n1;layout(location=10) in vec3 n2;layout(location=11) in vec4 tint;layout(location=12) in vec4 uvRect;",RENDER3D_MAX_STREAM_VERTS=32768,RENDER3D_MAX_LIGHTS=8,RENDER3D_QUAD_UVS=Object.freeze([vec2(0,0),vec2(0,1),vec2(1,0),vec2(1,1)].map(a=>Object.freeze(a))),RENDER3D_FULL_UV_RECT=Object.freeze({x:0,y:0,w:1,h:1}),RENDER3D_DEFAULT_NORMAL=Object.freeze(vec3(0,1,0)),RENDER3D_DEFAULT_UV=Object.freeze(vec2()),RENDER3D_SHADOW_COLOR=Object.freeze(hsl(0,0,0,.5)),RENDER3D_IDENTITY=new Matrix4,RENDER3D_DEBUG_WIDTH=.05,RENDER3D_TEXT_LEADING=1.3;function render3DFaceNormal(a,b,c,d=a){a=c.subtract(a).cross(d.subtract(b));return a.lengthSquared()?a.normalize():RENDER3D_DEFAULT_NORMAL}function render3DQuadStrip(a,b,c,d){return[a,b,d,c]}function render3DQuadValues(a){return isArray(a)?render3DQuadStrip(...a):a}function render3DCanDraw(){if(!render3D.program)return!1;ASSERT(render3D.isRendering,"3D draws are only valid during the 3D pass, draw from an EngineObject3D or render3D.onRenderOpaque");return render3D.isRendering}const RENDER3D_STATE_FIELDS="blend additive depthTest depthWrite cullBackFaces mirrored lighting emissive receiveShadow specular pixelated shader".split(" ");function render3DCaptureBatchState(){const a={};for(const b of RENDER3D_STATE_FIELDS)a[b]=render3D[b];return a}function render3DStateChanged(a){for(const b of RENDER3D_STATE_FIELDS)if(render3D[b]!==a[b])return!0;return!1}function render3DWithState(a,b){const c=render3D,d={};for(const e in a)d[e]=c[e],c[e]=a[e];try{return b()}finally{Object.assign(c,d)}}function render3DIsAfter2D(a){return!!(a.renderAfter2D??render3D.renderAfter2D)}function render3DSize3(a){return isNumber(a)?vec3(a):a}function render3DMatrix(a){if(a instanceof Vector3)return buildMatrix(a);ASSERT(a instanceof Matrix4,"takes a Matrix4, or a Vector3 for a position");return a}function render3DNormalMatrix(a){return a.copy().invert().transpose()}function render3DAxis(a,b){return vec3(a[b],a[b+1],a[b+2])}function render3DSlopeNormal(a,b,c,d,e,f,g){const h=max(b-d,-f);f=min(b+d,f);d=max(c-e,-g);e=min(c+e,g);c=(a(f,c)-a(h,c))/(f-h||1);a=(a(b,e)-a(b,d))/(e-d||1);return vec3(-c,1,-a).normalize()}function render3DMaxScale(a){return max(a[0]*a[0]+a[1]*a[1]+a[2]*a[2],a[4]*a[4]+a[5]*a[5]+a[6]*a[6],a[8]*a[8]+a[9]*a[9]+a[10]*a[10])**.5}function render3DQuadAxes(a,b,c){return[a.subtract(b).add(c),a.subtract(b).subtract(c),a.add(b).add(c),a.add(b).subtract(c)]}function render3DSetObjectState(a){const b=render3D,c=a?.emissive||0;ASSERT(isNumber(c)&&0<=c,"emissive must be a number, 0 or more",c);b.lighting=!0;b.emissive=c;b.additive=!!a?.additive;b.specular=a?.specular||0;b.receiveShadow=!a||a.receiveShadow;b.cullBackFaces=b.mirrored=!1;b.pixelated=!!a?.pixelated;ASSERT(!a?.shader||a.shader instanceof Shader,"shader must be a Shader, not the snippet itself");b.shader=a?.shader||void 0;b.depthTest=!0}function render3DDrawObjects(a){for(const b of a)render3DSetObjectState(b),b.render3D();render3DSetObjectState()}function render3DDetach(a){a.parent?(a.pos3D=a.getWorldPos3D(),a.parent.removeChild(a)):a.worldPos3D&&(a.pos3D=a.worldPos3D)}function render3DInstance(a,b,c,d){var e=render3D,f=c instanceof TileInfo?c.textureInfo:c;a.instanceCount&&(a.instanceTextureInfo!==f||render3DStateChanged(a.instanceState))&&render3DFlushInstances(a);a.instanceCount||(a.instanceTextureInfo=f,a.instanceState=render3DCaptureBatchState(),e.instanceMeshes.push(a));e=a.instanceData;f=a.instanceCount++*RENDER3D_INSTANCE_FLOATS;if(!e||e.length<f+RENDER3D_INSTANCE_FLOATS){const g=new Float32Array(max(64*RENDER3D_INSTANCE_FLOATS,e?2*e.length:0));e&&g.set(e);a.instanceData=e=g}e.set(b.m,f);render3DNormalMatrix3(b.m,e,f+16);e[f+25]=d.r;e[f+26]=d.g;e[f+27]=d.b;e[f+28]=d.a;a=render3DGetTileUVs(c);e[f+29]=a.x;e[f+30]=a.y;e[f+31]=a.w;e[f+32]=a.h}function render3DFlushInstances(a){const b=render3D,c=glContext;for(const d of a?[a]:b.instanceMeshes){const e=d.instanceCount;d.instanceCount=0;if(!e||!d.buffer)continue;const f=b.instanceBuffers;c.bindBuffer(c.ARRAY_BUFFER,f[b.instanceBufferIndex=(b.instanceBufferIndex+1)%f.length]);c.bufferData(c.ARRAY_BUFFER,d.instanceData,c.DYNAMIC_DRAW,0,e*RENDER3D_INSTANCE_FLOATS);for(const[g,h,k]of RENDER3D_INSTANCE_ATTRIBS)c.vertexAttribPointer(g,h,c.FLOAT,!1,RENDER3D_INSTANCE_BYTES,k),c.enableVertexAttribArray(g);render3DSetDrawUniforms(RENDER3D_IDENTITY,d.instanceTextureInfo,WHITE,RENDER3D_FULL_UV_RECT,d.instanceState);render3DBindVertexBuffer(d.buffer);c.drawArraysInstanced(c.TRIANGLE_STRIP,0,d.bufferCount,e);for(const[g]of RENDER3D_INSTANCE_ATTRIBS)c.disableVertexAttribArray(g);++drawCount;primitiveCount+=d.bufferCount*e}a?(a=b.instanceMeshes.indexOf(a),0>a||b.instanceMeshes.splice(a,1)):b.instanceMeshes.length=0}function render3DClearInstances(){for(const a of render3D.instanceMeshes)a.instanceCount=0;render3D.instanceMeshes.length=0}function render3DLayerObjects(a){return engineObjects.filter(b=>!b.destroyed&&b instanceof EngineObject3D&&render3DIsAfter2D(b)===a)}function render3DCollectLights(){const a=engineObjects.filter(b=>!b.destroyed&&b instanceof Light3D&&0<b.color.a&&0<b.intensity&&(b.directional||0<b.radius));if(a.length>RENDER3D_MAX_LIGHTS){const b=render3D.camera.pos,c=new Map;for(const d of a)c.set(d,d.directional?-1:d.getWorldPos3D().distanceSquared(b));a.sort((d,e)=>c.get(d)-c.get(e));a.length=RENDER3D_MAX_LIGHTS}return a}const render3DCircleCache=new Map;function render3DCircle(a){a|=0;let b=render3DCircleCache.get(a);if(!b){b=new Float32Array(2*a+2);for(let c=0;c<=a;++c){const d=c/a*2*PI;b[2*c]=cos(d);b[2*c+1]=sin(d)}render3DCircleCache.set(a,b)}return b}let render3DSoftDotTexture;function render3DSoftDot(){if(render3DSoftDotTexture||!glContext||"undefined"==typeof OffscreenCanvas)return render3DSoftDotTexture;const a=createCanvasContext(32),b=a.createRadialGradient(16,16,0,16,16,16);for(const[c,d]of[[0,1],[.33,.9],[.67,.7],[1,0]])b.addColorStop(c,"rgba(255,255,255,"+d+")");a.fillStyle=b;a.fillRect(0,0,32,32);return render3DSoftDotTexture=new TextureInfo(a.canvas)}function render3DLookRotation(a,b){a=a.normalize();return a.lengthSquared()?1e-9>abs(a.x)+abs(a.z)?vec3(0<a.y?PI/2:-PI/2,b.y,0):vec3(Math.asin(clamp(a.y,-1,1)),atan2(-a.x,-a.z),0):b}class Render3DPlugin{constructor(){ASSERT(!render3D,"Render3D plugin already initialized");render3D=this;this.camera=new Camera3D;this.sunDirection=vec3(-.3,1,.5);this.sunColor=WHITE.copy();this.ambientColor=hsl(0,0,.3);this.fogColor=void 0;this.fogEnd=this.fogStart=0;this.gravity=vec3();this.softShadowHeight=0;this.shadows=this.smoothShading=!1;this.shadowMapSize=1024;this.shadowRange=40;this.shadowCenter=void 0;this.shadowBias=.003;this.shadowSoftness=1;this.lighting=!0;this.emissive=0;this.additive=!1;this.depthWrite=this.depthTest=!0;this.mirrored=this.cullBackFaces=!1;this.specular=0;this.shader=void 0;this.receiveShadow=!0;this.sky=this.onRenderTransparent=this.onRenderOpaque=void 0;this.renderAfter2D=!1;this.mipmaps=this.instancing=this.frustumCulling=this.sortTransparent=!0;this.pixelated=!1;this.anisotropy=4;this.boxMesh=buildBox();this.sphereMesh=buildSphere(1,16,8,!0);this.planeMesh=buildGrid();this.planeMesh.doubleSided=!1;this.planeMeshDoubleSided=buildGrid();this.shadowPass=this.isRendering=!1;this.viewMatrix=new Matrix4;this.projectionMatrix=new Matrix4;this.viewProjection=new Matrix4;this.shadowMatrix=new Matrix4;this.cameraRight=vec3(1,0,0);this.cameraUp=vec3(0,1,0);this.cameraForward=vec3(0,0,-1);this.cameraBack=vec3(0,0,1);this.blend=!1;this.frustumPlanes=[];this.shadowPlanes=[];this.currentProgram=this.program=void 0;this.lightCount=0;this.whiteTexture=this.vao=this.shadowShader=void 0;this.samplers=[];this.samplerKey=void 0;this.mipmapped=new WeakSet;this.shadowFramebuffer=this.shadowTexture=void 0;this.contextGeneration=this.shadowTextureSize=0;this.uniforms=new Map;this.uniformValues={};this.shadowMapDrawn=!1;this.passIsDefault=!0;this.lightPositions=new Float32Array(4*RENDER3D_MAX_LIGHTS);this.lightColors=new Float32Array(4*RENDER3D_MAX_LIGHTS);this.streamBuffer=void 0;this.instanceBuffers=[];this.instanceBufferIndex=0;this.instanceMeshes=[];this.attribValues=[];this.streamData=new ArrayBuffer(RENDER3D_MAX_STREAM_VERTS*RENDER3D_VERTEX_BYTES);this.streamFloats=new Float32Array(this.streamData);this.streamInts=new Uint32Array(this.streamData);this.streamCount=0;this.transparentQueue=this.capture=this.streamState=this.streamTileInfo=void 0;render3DInitGL();engineAddPlugin(void 0,render3DRender,render3DContextLost,render3DContextRestored,render3DPreRender)}updateMatrices(a=mainCanvasSize.y?mainCanvasSize.x/mainCanvasSize.y:1){const b=this.camera;b.align2D&&b.update2D();const c=b.getMatrix();this.viewMatrix=c.copy().invert();this.projectionMatrix=b.getProjectionMatrix(a);this.viewProjection=this.projectionMatrix.copy().multiply(this.viewMatrix);a=c.m;this.cameraRight=render3DAxis(a,0);this.cameraUp=render3DAxis(a,4);this.cameraBack=render3DAxis(a,8);this.cameraForward=this.cameraBack.scale(-1);this.frustumPlanes=render3DFrustumPlanes(this.viewProjection)}worldToClip(a){const b=this.viewProjection.m,c=b[3]*a.x+b[7]*a.y+b[11]*a.z+b[15],d=(b[2]*a.x+b[6]*a.y+b[10]*a.z+b[14])/c;if(!(0>=c||-1>d))return vec3((b[0]*a.x+b[4]*a.y+b[8]*a.z+b[12])/c,(b[1]*a.x+b[5]*a.y+b[9]*a.z+b[13])/c,d)}worldToScreen(a,b=mainCanvasSize){if(a=this.worldToClip(a))return vec2((a.x+1)/2*b.x,(1-a.y)/2*b.y)}screenToRay(a,b=mainCanvasSize){var c=b.x||1,d=b.y||1,e=c/d;b=this.camera;this.updateMatrices(e);c=a.x/c*2-1;a=1-a.y/d*2;d=b.orthographic?b.orthographic/2:tan(b.fov/2);e=this.cameraRight.scale(c*d*e).add(this.cameraUp.scale(a*d));return b.orthographic?new Ray3D(b.pos.add(e),this.cameraForward.copy()):new Ray3D(b.pos.copy(),this.cameraForward.add(e).normalize())}screenToGround(a,b=0,c=mainCanvasSize){a=this.screenToRay(a,c);b=raycastPlane(a,vec3(0,b,0),RENDER3D_DEFAULT_NORMAL);return void 0===b?void 0:a.getPosition(b)}pick(a,b=engineObjects){a=a instanceof Ray3D?a:this.screenToRay(a);let c;for(const d of b)b=render3DRaycastObject(a,d),void 0!==b&&(!c||b<c.distance)&&(c={object:d,distance:b});return c}playSound(a,b,c=1,d=1,e=1,f=!1){ASSERT(a instanceof Sound,"sound must be a Sound");ASSERT(isVector3(b),"pos3D must be a vec3");if(soundEnable&&!headlessMode&&(a.sampleBuffer||a._sampleChannels)){b=b.subtract(this.camera.pos);var g=a.range;if(g){const h=b.length();if(h>g)return;c*=percent(h,g,g*a.taper)}b=b.normalize().dot(this.cameraRight);d+=d*a.randomness*e*rand(-1,1);return new SoundInstance(a,c,d,b,f)}}playSoundLoop(a,b,c=1,d=1,e=1){return this.playSound(a,b,c,d,e,!0)}isSphereVisible(a,b){for(const c of this.shadowPass?this.shadowPlanes:this.frustumPlanes)if(c[0]*a.x+c[1]*a.y+c[2]*a.z+c[3]<-b)return!1;return!0}drawMesh(a,b=RENDER3D_IDENTITY,c,d=WHITE){b=render3DMatrix(b);ASSERT(!c||c instanceof TileInfo||c instanceof TextureInfo,"tileInfo must be a TileInfo or TextureInfo, it comes before color");ASSERT(isColor(d),"color must be a Color");if(this.capture)return void this.capture.combine(a,b,d);if(this.transparentQueue)return this.queueTransparent(b.getTranslation(),()=>this.drawMesh(a,b,c,d));if(render3DCanDraw()&&(!this.shadowPass||this.lighting)&&(a.buffer&&!a.dirty&&a.contextGeneration===this.contextGeneration||a.upload(),a.bufferCount&&(!this.frustumCulling||this.isSphereVisible(b.getTranslation(),a.radius*render3DMaxScale(b.m))))){var e=b.m,f=this.cullBackFaces,g=this.mirrored;this.cullBackFaces=!a.doubleSided;this.mirrored=0>e[0]*(e[5]*e[10]-e[6]*e[9])-e[4]*(e[1]*e[10]-e[2]*e[9])+e[8]*(e[1]*e[6]-e[2]*e[5]);!this.blend&&this.depthTest&&(a.instanced??this.instancing)?render3DInstance(a,b,c,d):(this.flush(),render3DSetDrawUniforms(b,c,d),render3DBindVertexBuffer(a.buffer),glContext.drawArrays(glContext.TRIANGLE_STRIP,0,a.bufferCount),++drawCount,primitiveCount+=a.bufferCount);this.cullBackFaces=f;this.mirrored=g}}drawStrip(a,b,c,d,e){if(this.capture)this.capture.addStrip(a,b,c,d);else{if(this.transparentQueue){var f=0,g=0,h=0;for(var k of a)f+=k.x,g+=k.y,h+=k.z;return this.queueTransparent(vec3(f,g,h).scale(1/a.length),()=>this.drawStrip(a,b,c,d,e))}ASSERT(isArray(a)&&2<a.length,"strip needs at least 3 points");f=a.length;g=render3DStripCount(f);if(h=render3DBeginStrip(g,e)){k=this.streamFloats;var l=this.streamInts,m=isArray(b),n=isArray(c),p=isArray(d),r=p?0:(d||WHITE).rgbaInt();for(let q=0;q<g;++q){const u=render3DStripIndex(q,f),t=n?c[u]:c||RENDER3D_DEFAULT_UV;render3DWriteVertex(k,l,this.streamCount++*RENDER3D_VERTEX_FLOATS,a[u],m?b[u]:b||RENDER3D_DEFAULT_NORMAL,h.x+t.x*h.w,h.y+t.y*h.h,p?d[u].rgbaInt():r)}}}}drawStripUnlit(a,b,c,d,e){render3DWithState({lighting:!1},()=>this.drawStrip(a,b,c,d,e))}flush(){if(this.streamCount&&render3DCanDraw()){var a=glContext;render3DSetDrawUniforms(RENDER3D_IDENTITY,this.streamTileInfo,WHITE,RENDER3D_FULL_UV_RECT,this.streamState);render3DBindVertexBuffer(this.streamBuffer);a.bufferSubData(a.ARRAY_BUFFER,0,this.streamFloats,0,this.streamCount*RENDER3D_VERTEX_FLOATS);a.drawArrays(a.TRIANGLE_STRIP,0,this.streamCount);++drawCount;primitiveCount+=this.streamCount;this.streamCount=0}}bake(a){this.flush();ASSERT(!this.capture,"bake cannot be nested");const b=this.capture=new Mesh;try{a()}finally{this.capture=void 0}return b}renderStages(a,b=!0){const c=[],d=[];for(var e of a)(e.transparent||e.additive?d:c).push(e);b&&this.sky&&this.drawSky();this.blend=!1;this.depthWrite=!0;e=(f,g)=>f.renderOrder-g.renderOrder;c.sort(e);d.sort(e);render3DDrawObjects(c);b&&this.onRenderOpaque?.();this.flush();render3DFlushInstances();this.blend=!0;this.depthWrite=!1;this.transparentQueue=this.sortTransparent?[]:void 0;try{render3DDrawObjects(d);for(const f of a)if(f.softShadow){const g=f.getMatrix();this.drawSoftShadow(g.getTranslation(),f.softShadow*render3DMaxScale(g.m),this.softShadowHeight)}b&&this.onRenderTransparent?.()}finally{this.flushTransparentQueue()}b&&render3DRenderDebug();this.flush();render3DSetObjectState();this.blend=!1;this.depthWrite=!0}queueTransparent(a,b){if(!this.transparentQueue)return b();this.transparentQueue.push({distance:a.distanceSquared(this.camera.pos),state:render3DCaptureBatchState(),draw:b})}flushTransparentQueue(){const a=this.transparentQueue;if(a){this.transparentQueue=void 0;a.sort((b,c)=>c.distance-b.distance);for(const b of a)render3DWithState(b.state,b.draw)}}drawSky(){this.flush();const{near:a,far:b}=this.camera,c=Infinity==b?1e4*a:(a+b)/2;render3DWithState({lighting:!1,blend:!1,depthTest:!1,depthWrite:!1,fogEnd:0,shader:void 0},()=>this.drawMesh(this.sky,buildMatrix(this.camera.pos,void 0,vec3(c))))}updateShadowMatrix(){ASSERT(0<this.shadowRange,"shadowRange must be positive");const a=0<this.shadowRange?this.shadowRange:1,b=a/2;var c=this.sunDirection.normalize(),d=this.shadowCenter||this.camera.pos.add(this.cameraForward.scale(.8*b));c=Matrix4.lookAt(d.add(c.scale(a)),d).invert();d=a/(this.shadowTextureSize||this.shadowMapSize);const e=c.m;e[12]=round(e[12]/d)*d;e[13]=round(e[13]/d)*d;this.shadowMatrix=Matrix4.orthographic(-b,b,-b,b,0,2*a).multiply(c);this.shadowPlanes=render3DFrustumPlanes(this.shadowMatrix)}setSky(a,b=hsl(.6,1,.9),c){this.sky?.dispose();this.sky=buildSky(a,b,c);this.fogColor=b.copy();return this.sky}setFog(a,b,c){this.fogStart=a;this.fogEnd=b;c&&(this.fogColor=c.copy())}drawBox(a,b=1,c=WHITE,d){this.drawMesh(this.boxMesh,buildMatrix(a,d,render3DSize3(b)),void 0,c)}drawSphere(a,b=1,c=WHITE){this.drawMesh(this.sphereMesh,buildMatrix(a,void 0,vec3(b)),void 0,c)}drawBillboard(a,b=vec2(1),c,d=WHITE,e=0,f=!1){if(this.capture)return this.drawStripUnlit(render3DBillboardCorners(a,b,e,f),this.cameraBack,RENDER3D_QUAD_UVS,d,c);if(this.transparentQueue)return this.queueTransparent(a,()=>this.drawBillboard(a,b,c,d,e,f));const g=render3DStripCount(4),h=render3DWithState({lighting:this.shadowPass&&this.lighting},()=>render3DBeginStrip(g,c));if(h){var k=render3DBillboardCorners(a,b,e,f),l=d.rgbaInt(),m=this.streamFloats,n=this.streamInts;for(let p=0;p<g;++p){const r=render3DStripIndex(p,4),q=RENDER3D_QUAD_UVS[r];render3DWriteVertex(m,n,this.streamCount++*RENDER3D_VERTEX_FLOATS,k[r],this.cameraBack,h.x+q.x*h.w,h.y+q.y*h.h,l)}}}drawQuad(a,b,c,d,e,f=WHITE){this.drawStrip(render3DQuadStrip(a,b,c,d),render3DFaceNormal(a,b,c,d),RENDER3D_QUAD_UVS,render3DQuadValues(f),e)}drawTriangle(a,b,c,d=WHITE){this.drawStrip([a,b,c],render3DFaceNormal(a,b,c),void 0,d)}drawLine(a,b,c=.1,d=WHITE){this.drawRibbon([a,b],c,void 0,d)}drawRibbon(a,b=.1,c,d=WHITE,e){const f=a.length;ASSERT(1<f,"a ribbon needs at least two points");ASSERT(!c||c instanceof TileInfo||c instanceof TextureInfo,"tileInfo must be a TileInfo or TextureInfo, it comes before color");const g=[],h=c?[]:void 0,k=[],l=this.cameraForward;let m=vec3(1,0,0);const n=2<f&&1e-12>a[0].distanceSquared(a[f-1]);for(let q=0;q<f;++q){const u=a[q];var p=isArray(b)?b[q]:b;const t=isArray(d)?d[q]:d;var r=e&&(isArray(e)?e[q]:e);const v=a[q<f-1?q+1:n?1:q],x=a[0<q?q-1:n?f-2:q];r=r||v.subtract(x).cross(l);1e-12<r.lengthSquared()&&(m=r.normalize());p=m.scale(p/2);g.push(u.add(p),u.subtract(p));h?.push(vec2(q/(f-1),0),vec2(q/(f-1),1));k.push(t,t)}render3DWithState({lighting:!1,cullBackFaces:!1},()=>this.drawStrip(g,l.scale(-1),h,k,c))}drawSoftDisc(a,b=1,c=WHITE,d=this.cameraBack,e=16){render3DAssertBlending();if(this.transparentQueue&&!this.capture)return this.queueTransparent(a,()=>this.drawSoftDisc(a,b,c,d,e));const f=d.normalize(),g=(.9>abs(f.y)?vec3(0,1,0):vec3(1,0,0)).cross(f).normalize(),h=g.cross(f);render3DDrawSoftDisc(b/2,c,e,f,(k,l,m)=>vec3(a.x+(g.x*k+h.x*l)*m,a.y+(g.y*k+h.y*l)*m,a.z+(g.z*k+h.z*l)*m))}drawSoftShadow(a,b=1,c=0,d=RENDER3D_SHADOW_COLOR,e=.02){render3DAssertBlending();const f=isNumber(c)?()=>c:c instanceof HeightMap?(g,h)=>c.getHeight(g,h):c;if(this.transparentQueue&&!this.capture)return this.queueTransparent(vec3(a.x,f(a.x,a.z)+e,a.z),()=>this.drawSoftShadow(a,b,c,d,e));render3DDrawSoftDisc(b/2,d,16,RENDER3D_DEFAULT_NORMAL,(g,h,k)=>{g=a.x+g*k;h=a.z+h*k;return vec3(g,f(g,h)+e,h)})}}function render3DAssertBlending(){const a=render3D;ASSERT(a.blend||a.capture||a.shadowPass||!a.isRendering,"soft discs and shadows need blending: set the object transparent or draw from onRenderTransparent")}function render3DDrawSoftDisc(a,b,c,d,e){const f=[1,.9,.7,0],g=render3DCircle(c);for(let h=0;3>h;++h){const k=[],l=[],m=b.withAlpha(b.a*f[h]),n=b.withAlpha(b.a*f[h+1]),p=a*h/3,r=a*(h+1)/3;for(let q=0;q<=c;++q){const u=g[2*q],t=g[2*q+1];k.push(e(u,t,r),e(u,t,p));l.push(n,m)}render3D.drawStripUnlit(k,d,void 0,l)}}let render3DDebugPrimitives=[];function render3DRenderDebug(){render3DDebugPrimitives.length&&(render3DWithState({lighting:!1,depthTest:!1,receiveShadow:!1,additive:!1,shader:void 0},()=>{for(const a of render3DDebugPrimitives)a.draw()}),render3DDebugPrimitives=render3DDebugPrimitives.filter(a=>0>a.timer))}function render3DDebugPush(a,b){ASSERT(isNumber(a),"duration must be a number");debug&&render3D?.program&&render3DDebugPrimitives.push({timer:new Timer(a),draw:b})}function debugBox3D(a,b=1,c=WHITE,d=0,e){const f=buildMatrix(a,e,render3DSize3(b)),g=h=>f.transformPoint(vec3(h&1?.5:-.5,h&2?.5:-.5,h&4?.5:-.5));render3DDebugPush(d,()=>{for(let h=0;8>h;++h)for(const k of[1,2,4])h&k||render3D.drawLine(g(h),g(h|k),RENDER3D_DEBUG_WIDTH,c)})}function debugSphere3D(a,b=1,c=WHITE,d=0){const e=render3DCircle(24),f=b/2;render3DDebugPush(d,()=>{for(const g of[(h,k)=>vec3(h,k,0),(h,k)=>vec3(h,0,k),(h,k)=>vec3(0,h,k)]){const h=[];for(let k=0;24>=k;++k)h.push(a.add(g(e[2*k],e[2*k+1]).scale(f)));render3D.drawRibbon(h,RENDER3D_DEBUG_WIDTH,void 0,c)}})}function debugLine3D(a,b,c=WHITE,d=RENDER3D_DEBUG_WIDTH,e=0){render3DDebugPush(e,()=>render3D.drawLine(a,b,d,c))}function debugPoint3D(a,b=WHITE,c=0,d=.2){render3DDebugPush(c,()=>{for(const e of[vec3(d/2,0,0),vec3(0,d/2,0),vec3(0,0,d/2)])render3D.drawLine(a.subtract(e),a.add(e),RENDER3D_DEBUG_WIDTH,b)})}class Camera3D{constructor(){this.pos=vec3(0,0,10);this.rotation=vec3();this.fov=PI/3;this.near=.1;this.far=1e3;this.orthographic=0;this.align2D=!1}getMatrix(){return buildMatrix(this.pos,this.rotation)}getViewMatrix(){return this.getMatrix().invert()}getProjectionMatrix(a){const b=this.orthographic/2,c=b*a;return b?Matrix4.orthographic(-c,c,-b,b,this.near,this.far):Matrix4.perspective(this.fov,a,this.near,this.far)}getForward(){return render3DAxis(this.getMatrix().m,8).scale(-1)}getRight(){return render3DAxis(this.getMatrix().m,0)}getUp(){return render3DAxis(this.getMatrix().m,4)}lookAt(a){this.rotation=render3DLookRotation(a.subtract(this.pos),this.rotation)}orbit(a,b,c,d=.5){const e=cos(d)*b;this.pos=a.add(vec3(sin(c)*e,sin(d)*b,cos(c)*e));this.lookAt(a)}follow(a,b,c=1){this.pos=this.pos.lerp(a.add(b),c);this.lookAt(a)}update2D(a=mainCanvasSize.y){const b=a/2/cameraScale,c=b/tan(this.fov/2);ASSERT(!a||c<this.far,"align2D needs this camera distance to match the 2D view, raise camera.far past it",c);this.orthographic&&(this.orthographic=2*b);this.pos=vec3(cameraPos.x,cameraPos.y,c);this.rotation=vec3(0,0,-cameraAngle)}}const RENDER3D_ATTRIBS=[[0,3,5126,!1,0],[1,3,5126,!1,12],[2,2,5126,!1,24],[3,4,5121,!0,32]],RENDER3D_VERTEX_SOURCE="#version 300 es\nprecision highp float;uniform mat4 viewProj,lightViewProj;"+RENDER3D_VERTEX_INPUTS+"out vec3 P,N;out vec2 T,L;out vec4 C,S;void main(){vec4 w=mat4(m0,m1,m2,m3)*vec4(p,1.);gl_Position=viewProj*w;P=w.xyz;N=mat3(n0,n1,n2)*n;T=uvRect.xy+t*uvRect.zw;L=t;C=c*tint;S=lightViewProj*w;}",RENDER3D_SNIPPET_NAMES="uniform float iTime;uniform vec3 iResolution;\n#define iChannel0 tex\n#define localUV L\n#define worldPos P\n#define worldNormal N\n#define sunDirection (-lightDir.xyz)\n#define sunColor lightColor.rgb\n#define ambientColor ambientFog.rgb\n#define lightCount extraLightCount\n#define lights extraLights\n#define lightColors extraLightColors\n";function render3DFragmentSource(a){return"#version 300 es\nprecision highp float;uniform vec4 lightDir,lightColor,ambientFog,fogColor,shadowParams;uniform vec4 extraLights["+RENDER3D_MAX_LIGHTS+"],extraLightColors["+RENDER3D_MAX_LIGHTS+"];uniform int extraLightCount;uniform vec3 cameraPos;uniform sampler2D tex;uniform highp sampler2DShadow shadowMap;in vec3 P,N;in vec2 T,L;in vec4 C,S;out vec4 o;float shadow(){if(shadowParams.x<=0.)return 1.;vec3 q=S.xyz/S.w*.5+.5;if(any(greaterThanEqual(abs(q-.5),vec3(.5))))return 1.;q.z-=shadowParams.y;float s=0.;for(int x=-1;x<=1;++x)for(int y=-1;y<=1;++y)s+=texture(shadowMap,vec3(q.xy+vec2(x,y)*shadowParams.z,q.z));return s/9.;}"+(a?RENDER3D_SNIPPET_NAMES+a+"\n":"")+"void main(){"+(a?"vec4 t;mainImage(t,T);":"vec4 t=texture(tex,T);")+"if(shadowParams.w>0.&&t.a<.5)discard;vec4 c=C*t;float e=lightDir.w;if(e<1.){vec3 n=dot(N,N)>0.?normalize(N):vec3(0,1,0);if(!gl_FrontFacing)n=-n;float nl=dot(n,-lightDir.xyz);float s=shadow();vec3 l=ambientFog.rgb+lightColor.rgb*max(nl,0.)*s;for(int i=0;i<"+RENDER3D_MAX_LIGHTS+";++i){if(i>=extraLightCount)break;vec4 L=extraLights[i];bool directional=L.w<0.;vec3 v=directional?L.xyz:L.xyz-P;float d=length(v);float a=directional?1.:max(0.,1.-d/L.w);l+=extraLightColors[i].rgb*extraLightColors[i].a*a*a*max(0.,dot(n,v/max(d,1e-6)));}c.rgb*=l*(1.-e)+e;if(lightColor.a>0.){vec3 v=normalize(cameraPos-P);vec3 r=reflect(lightDir.xyz,n);c.rgb+=lightColor.rgb*pow(max(dot(r,v),0.),16.)*lightColor.a*step(0.,nl)*s*(1.-e);}}else c.rgb*=e;if(ambientFog.a>0.){float z=distance(cameraPos,P);c.rgb=mix(c.rgb,shadowParams.w<0.?vec3(0):fogColor.rgb,smoothstep(fogColor.a,ambientFog.a,z));}o=vec4(c.rgb,shadowParams.w>0.?1.:c.a);}"}function render3DShaderProgram(a){ASSERT(a instanceof Shader,"render3D.shader must be a Shader, not the snippet itself");return a.program3D||(a.program3D=glCreateProgram(RENDER3D_VERTEX_SOURCE,render3DFragmentSource(a.fragmentCode)))}function render3DUseProgram(a){const b=glContext,c=render3D;b.useProgram(c.currentProgram=a);c.uniformValues={};b.uniformMatrix4fv(render3DUniform("viewProj"),!1,c.viewProjection.m);b.uniformMatrix4fv(render3DUniform("lightViewProj"),!1,c.shadowMatrix.m);b.uniform1i(render3DUniform("tex"),0);b.uniform1i(render3DUniform("shadowMap"),1);const d=c.camera.pos;b.uniform3f(render3DUniform("cameraPos"),d.x,d.y,d.z);b.uniform1i(render3DUniform("extraLightCount"),c.lightCount);c.lightCount&&(b.uniform4fv(render3DUniform("extraLights"),c.lightPositions,0,4*c.lightCount),b.uniform4fv(render3DUniform("extraLightColors"),c.lightColors,0,4*c.lightCount));a!==c.program&&(b.uniform1f(render3DUniform("iTime"),time),b.uniform3f(render3DUniform("iResolution"),glCanvas.width,glCanvas.height,1))}function render3DInitGL(){if(!headlessMode)if(glEnable&&glContext){var a=glContext,b=render3D;b.uniforms=new Map;b.uniformValues={};b.attribValues=[];b.program=glCreateProgram(RENDER3D_VERTEX_SOURCE,render3DFragmentSource());b.shadowShader=glCreateProgram("#version 300 es\nprecision highp float;uniform mat4 viewProj;"+RENDER3D_VERTEX_INPUTS+"out vec2 T;void main(){T=uvRect.xy+t*uvRect.zw;gl_Position=viewProj*mat4(m0,m1,m2,m3)*vec4(p,1.);}","#version 300 es\nprecision highp float;uniform sampler2D tex;in vec2 T;void main(){if(texture(tex,T).a<.5)discard;}");b.vao=a.createVertexArray();a.bindVertexArray(b.vao);for(const[c]of RENDER3D_ATTRIBS)a.enableVertexAttribArray(c);for(const[c]of RENDER3D_INSTANCE_ATTRIBS)a.vertexAttribDivisor(c,1);b.streamBuffer=a.createBuffer();a.bindBuffer(a.ARRAY_BUFFER,b.streamBuffer);a.bufferData(a.ARRAY_BUFFER,b.streamData.byteLength,a.DYNAMIC_DRAW);b.streamCount=0;b.instanceBuffers=[a.createBuffer(),a.createBuffer(),a.createBuffer()];b.whiteTexture=glCreateTexture();b.mipmapped=new WeakSet;b.samplers=[];b.samplerKey=void 0;render3DUpdateShadowMap(1);a.bindBuffer(a.ARRAY_BUFFER,glArrayBuffer);glSetInstancedMode(!0)}else console.warn("Render3DPlugin: WebGL not enabled, construct the plugin in gameInit with glEnable set")}function render3DContextLost(){const a=render3D;a.program=a.currentProgram=a.shadowShader=a.vao=a.streamBuffer=a.whiteTexture=void 0;for(const b of glShaderObjects)b.program3D=void 0;a.lightCount=0;a.instanceBuffers=a.samplers=[];a.samplerKey=void 0;render3DClearInstances();a.shadowFramebuffer=a.shadowTexture=void 0;a.shadowTextureSize=0;a.streamCount=0;++a.contextGeneration}function render3DContextRestored(){render3DInitGL()}function render3DUniform(a,b=render3D.currentProgram){const c=render3D.uniforms;let d=c.get(b);d||c.set(b,d={});let e;return(e=d)[a]??(e[a]=glContext.getUniformLocation(b,a))}const render3DNormalScratch=new Float32Array(9);function render3DDrawAttribs(a,b,c){const d=glContext;d.vertexAttrib4f(4,a[0],a[1],a[2],a[3]);d.vertexAttrib4f(5,a[4],a[5],a[6],a[7]);d.vertexAttrib4f(6,a[8],a[9],a[10],a[11]);d.vertexAttrib4f(7,a[12],a[13],a[14],a[15]);render3D.shadowPass||(a=render3DNormalMatrix3(a,render3DNormalScratch,0),d.vertexAttrib3f(8,a[0],a[1],a[2]),d.vertexAttrib3f(9,a[3],a[4],a[5]),d.vertexAttrib3f(10,a[6],a[7],a[8]));render3DAttrib4f(11,b.r,b.g,b.b,b.a);render3DAttrib4f(12,c.x,c.y,c.w,c.h)}function render3DAttrib4f(a,b,c,d,e){const f=render3D.attribValues,g=f[a];g&&g[0]===b&&g[1]===c&&g[2]===d&&g[3]===e||(f[a]=[b,c,d,e],glContext.vertexAttrib4f(a,b,c,d,e))}function render3DNormalMatrix3(a,b,c){const d=a[0],e=a[1],f=a[2],g=a[4],h=a[5],k=a[6],l=a[8],m=a[9];a=a[10];const n=h*a-m*k,p=m*f-e*a,r=e*k-h*f;var q=d*n+g*p+l*r;if(1e-12>abs(q))return b[c]=d,b[c+1]=e,b[c+2]=f,b[c+3]=g,b[c+4]=h,b[c+5]=k,b[c+6]=l,b[c+7]=m,b[c+8]=a,b;q=1/q;b[c]=n*q;b[c+1]=(l*k-g*a)*q;b[c+2]=(g*m-l*h)*q;b[c+3]=p*q;b[c+4]=(d*a-l*f)*q;b[c+5]=(l*e-d*m)*q;b[c+6]=r*q;b[c+7]=(g*f-d*k)*q;b[c+8]=(d*h-g*e)*q;return b}function render3DUpdateSamplers(){const a=glContext,b=render3D,c=tilesPixelated+" "+b.anisotropy;if(b.samplerKey!==c){b.samplerKey=c;for(const e of b.samplers)a.deleteSampler(e);var d=a.getExtension("EXT_texture_filter_anisotropic");b.samplers=[!1,!0].flatMap(e=>[a.CLAMP_TO_EDGE,a.REPEAT].map(f=>{const g=a.createSampler();a.samplerParameteri(g,a.TEXTURE_MAG_FILTER,e||tilesPixelated?a.NEAREST:a.LINEAR);a.samplerParameteri(g,a.TEXTURE_MIN_FILTER,e?a.NEAREST:tilesPixelated?a.NEAREST_MIPMAP_LINEAR:a.LINEAR_MIPMAP_LINEAR);a.samplerParameteri(g,a.TEXTURE_WRAP_S,f);a.samplerParameteri(g,a.TEXTURE_WRAP_T,f);d&&!e&&(f=a.getParameter(d.MAX_TEXTURE_MAX_ANISOTROPY_EXT),a.samplerParameterf(g,d.TEXTURE_MAX_ANISOTROPY_EXT,clamp(b.anisotropy,1,f)));return g}))}}function render3DBindTexture(a,b=render3D){const c=glContext,d=render3D;a=a instanceof TileInfo?a.textureInfo:a;const e=a?.glTexture||d.whiteTexture;c.bindTexture(c.TEXTURE_2D,e);if(!d.mipmaps&&!b.pixelated)return c.bindSampler(0,null);c.bindSampler(0,d.samplers[(a?.wrap?1:0)+(b.pixelated?2:0)]);b.pixelated||d.mipmapped.has(e)||(d.mipmapped.add(e),c.generateMipmap(c.TEXTURE_2D))}function render3DUniform4f(a,b,c,d,e){const f=render3D.uniformValues,g=f[a];g&&g[0]===b&&g[1]===c&&g[2]===d&&g[3]===e||(f[a]=[b,c,d,e],glContext.uniform4f(render3DUniform(a),b,c,d,e))}function render3DBindVertexBuffer(a){const b=glContext;b.bindBuffer(b.ARRAY_BUFFER,a);for(const c of RENDER3D_ATTRIBS)b.vertexAttribPointer(c[0],c[1],c[2],c[3],RENDER3D_VERTEX_BYTES,c[4])}const render3DTileUVRect={x:0,y:0,w:1,h:1};function render3DGetTileUVs(a){if(!(a instanceof TileInfo))return RENDER3D_FULL_UV_RECT;const b=a.textureInfo.sizeInverse,c=render3DTileUVRect,d=b.x*a.bleed,e=b.y*a.bleed;c.x=a.pos.x*b.x+d;c.y=a.pos.y*b.y+e;c.w=a.size.x*b.x-2*d;c.h=a.size.y*b.y-2*e;return c}function render3DSetDrawUniforms(a,b,c,d,e=render3D){var f=glContext;const g=render3D;d||=render3DGetTileUVs(b);render3DDrawAttribs(a.m,c,d);render3DBindTexture(b,e);g.shadowPass||(a=e.shader?render3DShaderProgram(e.shader):g.program,a===g.currentProgram||render3DUseProgram(a),e.blend?(f.enable(f.BLEND),a=e.additive?f.ONE:f.ONE_MINUS_SRC_ALPHA,f.blendFuncSeparate(f.SRC_ALPHA,a,f.ONE,a)):f.disable(f.BLEND),e.depthTest?f.enable(f.DEPTH_TEST):f.disable(f.DEPTH_TEST),f.depthMask(e.depthWrite),e.cullBackFaces?f.enable(f.CULL_FACE):f.disable(f.CULL_FACE),f.frontFace(e.mirrored?f.CCW:f.CW),f=g.sunDirection,a=-(f.length()||1),b=g.sunColor,c=g.ambientColor,d=g.fogColor||canvasClearColor,render3DUniform4f("lightDir",f.x/a,f.y/a,f.z/a,e.lighting?e.emissive:1),render3DUniform4f("lightColor",b.r,b.g,b.b,e.specular),render3DUniform4f("ambientFog",c.r,c.g,c.b,g.fogEnd),render3DUniform4f("fogColor",d.r,d.g,d.b,g.fogStart),render3DUniform4f("shadowParams",g.shadows&&g.passIsDefault&&e.receiveShadow?1:0,g.shadowBias,g.shadowSoftness/g.shadowTextureSize,e.blend?e.additive?-1:0:1))}function render3DFrustumPlanes(a){a=a.m;const b=[];for(let c=0;3>c;++c)for(const d of[1,-1]){const e=[a[3]+d*a[c],a[7]+d*a[4+c],a[11]+d*a[8+c],a[15]+d*a[12+c]],f=hypot(e[0],e[1],e[2])||1;b.push(e.map(g=>g/f))}return b}function render3DPreRender(){const a=render3D;a.updateMatrices();a.shadowMapDrawn=!1;render3DRenderPass(!1)}function render3DRender(){render3DRenderPass(!0)}function render3DRenderPass(a){const b=glContext,c=render3D;if(c.program){render3DUpdateSamplers();ASSERT(!c.fogEnd||c.fogStart<c.fogEnd,"fogStart must be less than fogEnd");ASSERT(!glRenderTarget,"the 3D pass needs the canvas depth buffer, it can not draw into a render target");var d=a===!!c.renderAfter2D,e=render3DLayerObjects(a);if(d||e.length){c.passIsDefault=d;a&&glFlush();c.streamCount=0;c.capture=c.transparentQueue=void 0;render3DClearInstances();b.bindVertexArray(c.vao);b.frontFace(b.CW);b.activeTexture(b.TEXTURE0);b.depthMask(!0);b.clear(b.DEPTH_BUFFER_BIT);a=render3DCollectLights();c.lightCount=a.length;var f=c.lightPositions,g=c.lightColors;a.forEach((h,k)=>{const l=h.directional?h.getWorldPos3D().normalize():h.getWorldPos3D();ASSERT(!h.directional||l.lengthSquared(),"a directional light shines from its position toward the origin, so it cannot sit on the origin");const m=h.color;k*=4;f[k]=l.x;f[k+1]=l.y;f[k+2]=l.z;f[k+3]=h.directional?-1:max(0,h.radius);g[k]=m.r;g[k+1]=m.g;g[k+2]=m.b;g[k+3]=m.a*h.intensity});c.isRendering=!0;try{c.shadows&&!c.shadowMapDrawn&&(render3DRenderShadowMap(),c.shadowMapDrawn=!0),render3DUseProgram(c.program),c.renderStages(e,d)}finally{c.isRendering=!1,c.currentProgram=void 0,c.streamCount=0,c.capture=c.transparentQueue=void 0,b.disable(b.DEPTH_TEST),b.disable(b.CULL_FACE),b.depthMask(!0),b.frontFace(b.CCW),b.bindSampler(0,null),glActiveTexture&&b.bindTexture(b.TEXTURE_2D,glActiveTexture),b.bindBuffer(b.ARRAY_BUFFER,glArrayBuffer),glSetInstancedMode(!0)}}}}function render3DUpdateShadowMap(a){const b=glContext,c=render3D;ASSERT(0<a,"shadowMapSize must be positive");if(!c.shadowTexture||c.shadowTextureSize!==a){c.shadowTexture&&b.deleteTexture(c.shadowTexture);c.shadowFramebuffer&&b.deleteFramebuffer(c.shadowFramebuffer);var d=c.shadowTexture=b.createTexture();b.activeTexture(b.TEXTURE1);b.bindTexture(b.TEXTURE_2D,d);b.texImage2D(b.TEXTURE_2D,0,b.DEPTH_COMPONENT24,a,a,0,b.DEPTH_COMPONENT,b.UNSIGNED_INT,null);b.texParameteri(b.TEXTURE_2D,b.TEXTURE_MIN_FILTER,b.LINEAR);b.texParameteri(b.TEXTURE_2D,b.TEXTURE_MAG_FILTER,b.LINEAR);b.texParameteri(b.TEXTURE_2D,b.TEXTURE_WRAP_S,b.CLAMP_TO_EDGE);b.texParameteri(b.TEXTURE_2D,b.TEXTURE_WRAP_T,b.CLAMP_TO_EDGE);b.texParameteri(b.TEXTURE_2D,b.TEXTURE_COMPARE_MODE,b.COMPARE_REF_TO_TEXTURE);b.texParameteri(b.TEXTURE_2D,b.TEXTURE_COMPARE_FUNC,b.LEQUAL);b.activeTexture(b.TEXTURE0);var e=c.shadowFramebuffer=b.createFramebuffer();b.bindFramebuffer(b.FRAMEBUFFER,e);b.framebufferTexture2D(b.FRAMEBUFFER,b.DEPTH_ATTACHMENT,b.TEXTURE_2D,d,0);b.drawBuffers([b.NONE]);b.readBuffer(b.NONE);ASSERT(b.checkFramebufferStatus(b.FRAMEBUFFER)==b.FRAMEBUFFER_COMPLETE,"shadow map framebuffer is incomplete, try a smaller shadowMapSize");b.bindFramebuffer(b.FRAMEBUFFER,null);c.shadowTextureSize=a}}function render3DRenderShadowMap(){const a=glContext,b=render3D;render3DUpdateShadowMap(b.shadowMapSize|0);b.updateShadowMatrix();a.activeTexture(a.TEXTURE1);a.bindTexture(a.TEXTURE_2D,null);a.activeTexture(a.TEXTURE0);a.bindFramebuffer(a.FRAMEBUFFER,b.shadowFramebuffer);a.viewport(0,0,b.shadowTextureSize,b.shadowTextureSize);a.clear(a.DEPTH_BUFFER_BIT);a.useProgram(b.shadowShader);a.uniformMatrix4fv(render3DUniform("viewProj",b.shadowShader),!1,b.shadowMatrix.m);a.enable(a.DEPTH_TEST);a.depthMask(!0);a.disable(a.BLEND);a.disable(a.CULL_FACE);b.shadowPass=!0;try{const c=render3DLayerObjects(!!b.renderAfter2D).filter(d=>d.castShadow&&!d.additive&&(!d.transparent||d.tileInfo));render3DDrawObjects(c);b.onRenderOpaque?.();b.flush();render3DFlushInstances()}finally{b.shadowPass=!1,a.bindFramebuffer(a.FRAMEBUFFER,null),a.viewport(0,0,glCanvas.width,glCanvas.height),a.activeTexture(a.TEXTURE1),a.bindTexture(a.TEXTURE_2D,b.shadowTexture),a.activeTexture(a.TEXTURE0)}}function render3DBeginStrip(a,b){const c=render3D;if(!(!render3DCanDraw()||c.shadowPass&&!c.lighting||(ASSERT(a<=RENDER3D_MAX_STREAM_VERTS,"strip is too large for the stream, bake it into a mesh"),a>RENDER3D_MAX_STREAM_VERTS))){var d=b instanceof TileInfo?b.textureInfo:b;c.streamCount&&(d!==c.streamTileInfo||render3DStateChanged(c.streamState)||c.streamCount+a>RENDER3D_MAX_STREAM_VERTS)&&c.flush();c.streamCount||(c.streamState=render3DCaptureBatchState());c.streamTileInfo=d;return render3DGetTileUVs(b)}}function render3DBillboardCorners(a,b,c,d){var e=render3D.cameraRight;let f=render3D.cameraUp;d&&(e=vec3(e.x,0,e.z),e=e.lengthSquared()?e.normalize():vec3(1,0,0),f=RENDER3D_DEFAULT_NORMAL);d=cos(c);c=sin(c);var g=b.x/2;const h=b.y/2;b=(e.x*d+f.x*c)*g;const k=(e.y*d+f.y*c)*g;g*=e.z*d+f.z*c;const l=(f.x*d-e.x*c)*h,m=(f.y*d-e.y*c)*h;e=(f.z*d-e.z*c)*h;return[vec3(a.x-b+l,a.y-k+m,a.z-g+e),vec3(a.x-b-l,a.y-k-m,a.z-g-e),vec3(a.x+b+l,a.y+k+m,a.z+g+e),vec3(a.x+b-l,a.y+k-m,a.z+g-e)]}function render3DStripCount(a){return a+2+(a&1)}function render3DStripIndex(a,b){return 1>a?0:a<=b?a-1:b-1}function render3DForEachStripVertex(a,b,c,d,e){ASSERT(isArray(a)&&2<a.length,"strip needs at least 3 points");const f=a.length,g=render3DStripCount(f),h=isArray(b),k=isArray(c),l=isArray(d);for(let m=0;m<g;++m){const n=render3DStripIndex(m,f);e(a[n],h?b[n]:b||RENDER3D_DEFAULT_NORMAL,k?c[n]:c||RENDER3D_DEFAULT_UV,l?d[n]:d||WHITE)}}function render3DWriteVertex(a,b,c,d,e,f,g,h){a[c]=d.x;a[c+1]=d.y;a[c+2]=d.z;a[c+3]=e.x;a[c+4]=e.y;a[c+5]=e.z;a[c+6]=f;a[c+7]=g;b[c+8]=h}function render3DPolygonStrip(a){const b=[a[0]];for(let c=1,d=a.length-1;c<=d;++c,--d)b.push(a[c]),c!==d&&b.push(a[d]);return b}const render3DMeshBuffers="undefined"==typeof FinalizationRegistry?void 0:new FinalizationRegistry(({buffer:a,generation:b})=>b===render3D?.contextGeneration&&glContext?.deleteBuffer(a));class Mesh{constructor(){this.points=[];this.normals=[];this.uvs=[];this.colors=[];this.buffer=void 0;this.bufferCount=0;this.dirty=!1;this.instanced=void 0;this.doubleSided=!1;this.instanceCount=0;this.instanceData=void 0;this.contextGeneration=this.radius=0}get vertexCount(){return this.points.length}addStrip(a,b,c,d){render3DForEachStripVertex(a,b,c,d,(e,f,g,h)=>{this.points.push(e);this.normals.push(f);this.uvs.push(g);this.colors.push(h)});this.dirty=!0;return this}addQuad(a,b,c,d,e,f){return this.addStrip(render3DQuadStrip(a,b,c,d),render3DFaceNormal(a,b,c,d),f?render3DQuadValues(f):RENDER3D_QUAD_UVS,render3DQuadValues(e))}combine(a,b=RENDER3D_IDENTITY,c=WHITE){b=render3DMatrix(b);const d=render3DNormalMatrix(b);for(let e=0;e<a.points.length;++e)this.points.push(b.transformPoint(a.points[e])),this.normals.push(d.transformDirection(a.normals[e]||RENDER3D_DEFAULT_NORMAL).normalize()),this.uvs.push((a.uvs[e]||RENDER3D_DEFAULT_UV).copy()),this.colors.push((a.colors[e]||WHITE).multiply(c));this.doubleSided||(this.doubleSided=a.doubleSided);this.dirty=!0;return this}scaleUVs(a){const b=isNumber(a)?vec2(a):a;this.uvs=this.uvs.map(c=>vec2(c.x*b.x,c.y*b.y));this.dirty=!0;return this}transform(a){a=render3DMatrix(a);const b=render3DNormalMatrix(a);for(let c=0;c<this.points.length;++c){this.points[c]=a.transformPoint(this.points[c]);let d,e;(d=this.normals)[e=c]&&(d[e]=b.transformDirection(this.normals[c]).normalize())}this.dirty=!0;return this}flipNormals(){for(const a of["points","normals","uvs","colors"]){const b=this[a];b.length&&(b.unshift(b[0]),b.push(b[b.length-1]))}this.normals=this.normals.map(a=>a.scale(-1));this.dirty=!0;return this}setColor(a){this.colors=this.points.map(()=>a);this.dirty=!0;return this}getBounds(){if(!this.points.length)return{min:vec3(),max:vec3()};const a=vec3(Infinity),b=vec3(-Infinity);for(const c of this.points)a.x=min(a.x,c.x),a.y=min(a.y,c.y),a.z=min(a.z,c.z),b.x=max(b.x,c.x),b.y=max(b.y,c.y),b.z=max(b.z,c.z);return{min:a,max:b}}center(){const a=this.getBounds();return this.transform(a.min.add(a.max).scale(-.5))}fit(a=1){var b=this.getBounds();b=b.max.subtract(b.min);a/=max(b.x,b.y,b.z)||1;return this.transform(Matrix4.scaling(vec3(a)))}computeRadius(){let a=0;for(const b of this.points)a=max(a,b.lengthSquared());return this.radius=a**.5}computeNormals(a=!1){const b=this.points,c=b.length,d=[];for(let g=0;g+2<c;++g){var e=b[g];const h=b[g+2];e=b[g+1].subtract(e).cross(h.subtract(e));d.push(e.lengthSquared()?e.normalize(g&1?1:-1):void 0)}const f=b.map(()=>RENDER3D_DEFAULT_NORMAL);if(a){const g=new Map,h=k=>`${round(1e5*k.x)},${round(1e5*k.y)},${round(1e5*k.z)}`;d.forEach((k,l)=>k&&[0,1,2].forEach(m=>{var n=b[l+m],p=b[l+(m+1)%3].subtract(n);m=b[l+(m+2)%3].subtract(n);p=Math.acos(clamp(p.dot(m)/(p.length()*m.length()||1),-1,1));n=h(n);g.set(n,(g.get(n)||vec3()).add(k.scale(p)))}));for(a=0;a<c;++a)f[a]=(g.get(h(b[a]))||RENDER3D_DEFAULT_NORMAL).normalize()}else d.forEach((g,h)=>g&&(f[h]=f[h+1]=f[h+2]=g));this.normals=f;this.dirty=!0;return this}upload(){this.computeRadius();if(!render3D?.program)return this;this.dispose();const a=this.points.length,b=new ArrayBuffer(a*RENDER3D_VERTEX_BYTES);var c=new Float32Array(b);const d=new Uint32Array(b);for(let e=0;e<a;++e){const f=this.uvs[e]||RENDER3D_DEFAULT_UV;render3DWriteVertex(c,d,e*RENDER3D_VERTEX_FLOATS,this.points[e],this.normals[e]||RENDER3D_DEFAULT_NORMAL,f.x,f.y,(this.colors[e]||WHITE).rgbaInt())}c=glContext;this.buffer=c.createBuffer();this.bufferCount=a;this.dirty=!1;c.bindBuffer(c.ARRAY_BUFFER,this.buffer);c.bufferData(c.ARRAY_BUFFER,b,c.STATIC_DRAW);this.contextGeneration=render3D.contextGeneration;render3DMeshBuffers?.register(this,{buffer:this.buffer,generation:this.contextGeneration},this);c.bindBuffer(c.ARRAY_BUFFER,glArrayBuffer);return this}render(a,b,c){render3D?.drawMesh(this,a,b,c)}dispose(){this.buffer&&(render3DMeshBuffers?.unregister(this),this.contextGeneration===render3D?.contextGeneration&&glContext?.deleteBuffer(this.buffer),this.buffer=void 0,this.bufferCount=0)}}function buildLathe(a,b=16,c=render3D?.smoothShading,d=!0){ASSERT(isArray(a)&&1<a.length,"lathe profile needs at least 2 points");b|=0;ASSERT(2<b,"lathe needs at least 3 sides");const e=new Mesh,f=a.length,g=(w,y)=>vec3(sin(y)*a[w][0],a[w][1],cos(y)*a[w][0]),h=w=>{const[y,z]=a[w],[A,C]=a[w+1];w=vec2(C-z,y-A);return w.length()?w.normalize():vec2(1,0)},k=2<f&&1e-9>abs(a[0][0]-a[f-1][0])&&1e-9>abs(a[0][1]-a[f-1][1]);var l=w=>{if(!(k||w&&w!=f-1)&&1e-9>abs(a[w][0]))return vec2(0,w?1:-1);let y=vec2();const z=A=>y=y.add(h(A).scale(hypot(a[A+1][0]-a[A][0],a[A+1][1]-a[A][1])));0<w?z(w-1):k&&z(f-2);w<f-1?z(w):k&&z(0);return y.length()?y.normalize():vec2(1,0)},m=(w,y)=>vec3(sin(y)*w.x,w.y,cos(y)*w.x),n=[0];for(var p=1;p<f;++p)n[p]=n[p-1]+hypot(a[p][0]-a[p-1][0],a[p][1]-a[p-1][1]);p=n[f-1]||1;for(let w=0;w+1<f;++w)if(c){var r=[],q=[],u=[],t=l(w),v=l(w+1);for(var x=0;x<=b;++x){const y=x/b*2*PI,z=x/b;r.push(g(w+1,y),g(w,y));q.push(m(v,y),m(t,y));u.push(vec2(z,1-n[w+1]/p),vec2(z,1-n[w]/p))}e.addStrip(r,q,u)}else for(r=h(w),q=0;q<b;++q)u=q/b*2*PI,t=(q+1)/b*2*PI,v=q/b,x=(q+1)/b,e.addStrip([g(w+1,u),g(w,u),g(w+1,t),g(w,t)],m(r,(u+t)/2),[vec2(v,1-n[w+1]/p),vec2(v,1-n[w]/p),vec2(x,1-n[w+1]/p),vec2(x,1-n[w]/p)]);if(d&&!k)for(const[w,y]of[[0,!1],[f-1,!0]])if(!(1e-9>abs(a[w][0]))){c=[];l=[];for(m=0;m<b;++m)n=(y?m:-m)/b*2*PI,c.push(g(w,n)),l.push(vec2(.5*sin(n)+.5,.5*cos(n)+.5));e.addStrip(render3DPolygonStrip(c),vec3(0,y?1:-1,0),render3DPolygonStrip(l))}e.doubleSided=!k&&!d&&(1e-9<abs(a[0][0])||1e-9<abs(a[f-1][0]));return e}function buildCylinder(a=1,b=1,c=16,d=render3D?.smoothShading,e=!0){return buildLathe([[a/2,-b/2],[a/2,b/2]],c,d,e)}function buildCone(a=1,b=1,c=16,d=render3D?.smoothShading,e=!0){return buildLathe([[a/2,-b/2],[0,b/2]],c,d,e)}function buildSphere(a=1,b=16,c=8,d=render3D?.smoothShading){ASSERT(1<c,"sphere needs at least 2 rings");const e=[];for(let f=0;f<=c;++f){const g=f/c*PI-PI/2;e.push([cos(g)*a/2,sin(g)*a/2])}return buildLathe(e,b,d)}function buildCapsule(a=1,b=1,c=16,d=4,e=render3D?.smoothShading){ASSERT(b>=a,"a capsule is at least as tall as it is wide, the ends take up the size",a,b);const f=[],g=a/2;a=max(0,b-a)/2;for(b=0;b<=d;++b){var h=b/d*PI/2;f.push([g*sin(h),-a-g*cos(h)])}for(b=0;b<=d;++b)h=b/d*PI/2,f.push([g*cos(h),a+g*sin(h)]);return buildLathe(f,c,e)}function buildTorus(a=1,b=.3,c=16,d=8,e=render3D?.smoothShading){ASSERT(b<=a,"the tube must fit inside the torus");const f=[];a=(a-b)/2;b/=2;for(let g=0;g<=d;++g){const h=g/d*2*PI;f.push([a+b*cos(h),b*sin(h)])}return buildLathe(f,c,e)}function buildBox(a=1){const b=new Mesh;a=render3DSize3(a).scale(.5);var c=[[vec3(0,0,1),vec3(1,0,0),vec3(0,1,0)],[vec3(0,0,-1),vec3(-1,0,0),vec3(0,1,0)],[vec3(1,0,0),vec3(0,0,-1),vec3(0,1,0)],[vec3(-1,0,0),vec3(0,0,1),vec3(0,1,0)],[vec3(0,1,0),vec3(1,0,0),vec3(0,0,-1)],[vec3(0,-1,0),vec3(1,0,0),vec3(0,0,1)]];for(const[d,e,f]of c){c=d.multiply(a);const g=e.multiply(a),h=f.multiply(a);b.addStrip(render3DQuadAxes(c,g,h),d,RENDER3D_QUAD_UVS)}return b}function buildRibbon(a,b=1,c=WHITE,d=!1,e=vec3(0,1,0)){ASSERT(isArray(a)&&1<a.length,"ribbon needs at least 2 points");const f=new Mesh,g=a.length,h=[];var k=(.9>abs(e.y)?vec3(0,1,0):vec3(1,0,0)).cross(e).normalize();for(let m=0;m<g;++m){var l=a[d?(m+1)%g:min(m+1,g-1)];const n=a[d?(m+g-1)%g:max(m-1,0)];l=l.subtract(n).cross(e);1e-12<l.lengthSquared()&&(k=l.normalize());l=k.scale((isArray(b)?b[m]:b)/2);h.push([a[m].subtract(l),a[m].add(l)])}for(a=0;a+1<g+(d?1:0);++a)k=(a+1)%g,b=h[a],e=h[k],k=isArray(c)?[c[a],c[a],c[k],c[k]]:c,f.addQuad(b[0],b[1],e[1],e[0],k);f.doubleSided=!0;return f}function buildGrid(a=vec2(1),b=1,c,d=()=>0,e=render3D?.smoothShading){isNumber(b)&&(b=vec2(b));ASSERT(0<b.x&&0<b.y&&0===b.x%1&&0===b.y%1,"grid segments must be whole numbers above zero");const f=new Mesh,g=b.x;b=b.y;const h=a.x/g,k=a.y/b,l=(r,q)=>{r=r*h-a.x/2;q=q*k-a.y/2;return vec3(r,d(r,q),q)},m=(r,q)=>render3DSlopeNormal(d,r*h-a.x/2,q*k-a.y/2,h/2,k/2,a.x/2,a.y/2),n=(r,q)=>c?isColor(c)?c:c(r*h-a.x/2,q*k-a.y/2):WHITE;for(let r=0;r<b;++r)if(e){var p=[];const q=[],u=[],t=[];for(let v=0;v<=g;++v)p.push(l(v,r),l(v,r+1)),q.push(m(v,r),m(v,r+1)),u.push(vec2(v/g,r/b),vec2(v/g,(r+1)/b)),t.push(n(v,r),n(v,r+1));f.addStrip(p,q,u,t)}else for(p=0;p<g;++p)f.addQuad(l(p,r),l(p,r+1),l(p+1,r+1),l(p+1,r),n(p+.5,r+.5),[vec2(p/g,r/b),vec2(p/g,(r+1)/b),vec2((p+1)/g,(r+1)/b),vec2((p+1)/g,r/b)]);f.doubleSided=!0;return f}function buildLoft(a){ASSERT(isArray(a)&&1<a.length,"loft needs at least 2 stations");ASSERT(a[0][0]>a[a.length-1][0],"loft stations go nose first, from the largest z to the smallest");const b=new Mesh,c=([e,f,g,h,k=.5])=>[vec3(-f/2,lerp(h,g,k),e),vec3(0,g,e),vec3(f/2,lerp(h,g,k),e),vec3(0,h,e)];for(var d=0;d+1<a.length;++d){const e=c(a[d]),f=c(a[d+1]);for(let g=0;4>g;++g)b.addQuad(e[g],e[(g+1)%4],f[(g+1)%4],f[g])}d=c(a[a.length-1]);a=c(a[0]);b.addQuad(d[0],d[1],d[2],d[3]);b.addQuad(a[3],a[2],a[1],a[0]);return b}function buildSky(a=hsl(.6,.8,.55),b=hsl(.6,1,.9),c=b,d=16,e=8){const f=new Mesh,g=(k,l)=>{k=k/e*PI-PI/2;return vec3(sin(l)*cos(k),sin(k),cos(l)*cos(k))},h=k=>{k=g(k,0).y;return 0>k?b.lerp(c,-k):b.lerp(a,k)};for(let k=0;k<e;++k){const l=[],m=[];for(let n=0;n<=d;++n){const p=n/d*2*PI;l.push(g(k,p),g(k+1,p));m.push(h(k),h(k+1))}f.addStrip(l,void 0,void 0,m)}return f}function buildExtrude(a,b=vec2(1),c=1){let d=a,e,f;if(a instanceof TileInfo){var g=render3DReadPixels(a.textureInfo);const t=g.data,v=a.pos.x|0,x=a.pos.y|0;e=a.size.x|0;f=a.size.y|0;d=[];for(a=0;a<f;++a){const w=d[a]=[];for(let y=0;y<e;++y){const z=4*((x+a)*g.width+v+y);w.push(127<t[z+3]?rgb(t[z]/255,t[z+1]/255,t[z+2]/255):void 0)}}}else ASSERT(isArray(a)&&a.length,"pixels must be a TileInfo or rows of pixels"),f=d.length,e=d[0].length;const h=(t,v)=>{if(!(0>t||0>v||t>=e||v>=f)&&(t=d[v]&&d[v][t]))return isColor(t)?.5<t.a?t:void 0:WHITE};g=(t,v,x)=>{let w=0,y;for(let A=0;A<=t;++A){const C=A<t?v(A):void 0;var z=y;C===z||C&&z&&C.rgbaInt()===z.rgbaInt()||(y&&x(w,A,y),w=A,y=C)}};const k=new Mesh,l=b.x/e,m=b.y/f,n=c/2,p=(t,v,x,w,y)=>k.addStrip(render3DQuadAxes(t.add(v.scale(.5)).add(x.scale(.5)),v.scale(.5),x.scale(.5)),w,RENDER3D_QUAD_UVS,y),r=vec3(1,0,0),q=vec3(0,1,0),u=vec3(0,0,1);for(let t=0;t<f;++t)g(e,v=>h(v,t),(v,x,w)=>{const y=r.scale((x-v)*l),z=q.scale(m);p(vec3(v*l-b.x/2,b.y/2-(t+1)*m,n),y,z,u,w);p(vec3(x*l-b.x/2,b.y/2-(t+1)*m,-n),y.scale(-1),z,u.scale(-1),w)}),g(e,v=>h(v,t-1)?void 0:h(v,t),(v,x,w)=>p(vec3(v*l-b.x/2,b.y/2-t*m,n),r.scale((x-v)*l),u.scale(-c),q,w)),g(e,v=>h(v,t+1)?void 0:h(v,t),(v,x,w)=>p(vec3(v*l-b.x/2,b.y/2-(t+1)*m,-n),r.scale((x-v)*l),u.scale(c),q.scale(-1),w));for(let t=0;t<e;++t)g(f,v=>h(t-1,v)?void 0:h(t,v),(v,x,w)=>p(vec3(t*l-b.x/2,b.y/2-x*m,-n),u.scale(c),q.scale((x-v)*m),r.scale(-1),w)),g(f,v=>h(t+1,v)?void 0:h(t,v),(v,x,w)=>p(vec3((t+1)*l-b.x/2,b.y/2-x*m,n),u.scale(-c),q.scale((x-v)*m),r,w));return k}function buildText3D(a,b=1,c=.2,d=engineImageFont){ASSERT(d instanceof ImageFont,"font must be an ImageFont, the engine font loads before gameInit");const e=d.tileInfo,f=e.padding,g=e.size.x+2*f,h=e.size.y+2*f,k=e.textureInfo.size.x/g|0;let l=render3DGlyphCache.get(d);l||render3DGlyphCache.set(d,l=new Map);const m=vec2(b*e.size.x/e.size.y,b),n=new Mesh,p=(a+"").split("\n");p.forEach((r,q)=>{q=((p.length-1)/2-q)*m.y*RENDER3D_TEXT_LEADING;for(let v=0;v<r.length;++v){var u=r.charCodeAt(v);if(u=32>u||127<u?95:u-32){var t=l.get(u);t||(t=vec2(u%k*g+f,(u/k|0)*h+f),l.set(u,t=buildExtrude(new TileInfo(t,e.size,e.textureInfo))));n.combine(t,buildMatrix(vec3((v-(r.length-1)/2)*m.x,q,0),void 0,vec3(m.x,m.y,c)))}}});return n}class HeightMap{constructor(a,b=vec2(1),c=1,d){isArray(a)||(a=render3DImageToArray(a,e=>e/255));d&&!isArray(d)&&(d=render3DImageToArray(d,(e,f,g,h)=>rgb(e/255,f/255,g/255,h/255)));ASSERT(isArray(a)&&1<a.length&&isArray(a[0])&&1<a[0].length,"height map needs at least 2 rows and 2 columns");ASSERT(0<b.x&&0<b.y,"height map size must be positive, a zero size has nowhere to look things up");this.heights=a;this.colors=d;this.size=b.copy();this.height=c}get rows(){return this.heights.length}get columns(){return this.heights[0].length}getHeight(a,b){a instanceof Vector3&&(b=a.z,a=a.x);var c=this.columns,d=this.rows,e=this.heights;a=clamp((a/this.size.x+.5)*(c-1),0,c-1);b=clamp((b/this.size.y+.5)*(d-1),0,d-1);c=min(floor(a),c-2);d=min(floor(b),d-2);a-=c;b-=d;const f=e[d][c],g=e[d+1][c],h=e[d+1][c+1];e=e[d][c+1];return(1>=a+b?f+a*(e-f)+b*(g-f):h+(1-a)*(g-h)+(1-b)*(e-h))*this.height}getNormal(a,b){a instanceof Vector3&&(b=a.z,a=a.x);return render3DSlopeNormal((c,d)=>this.getHeight(c,d),a,b,this.size.x/(this.columns-1)/2,this.size.y/(this.rows-1)/2,this.size.x/2,this.size.y/2)}getColor(a,b){a instanceof Vector3&&(b=a.z,a=a.x);const c=this.colors;if(!c)return WHITE;const d=c[0].length,e=c.length;a=clamp(round((a/this.size.x+.5)*(d-1)),0,d-1);b=clamp(round((b/this.size.y+.5)*(e-1)),0,e-1);return c[b][a]}raycast(a){const{origin:b,direction:c}=a,d=this.size;var e=this.height,f=c.length();if(f&&(a=raycastBox(a,vec3(0,e/2,0),vec3(d.x,abs(e)+.001,d.y)),void 0!==a)){var g=min(d.x/(this.columns-1),d.y/(this.rows-1))/2/f,h=a+hypot(d.x,d.y,e)/f;if(0<g&&(e=l=>{l=b.add(c.scale(l));if(!(abs(l.x)>d.x/2||abs(l.z)>d.y/2))return l.y<=this.getHeight(l.x,l.z)},f=e(a),void 0!==f))for(;a<h;a+=g){var k=e(a+g);if(void 0===k)break;if(k!==f){h=a;a+=g;for(g=0;16>g;++g)k=(h+a)/2,e(k)===f?h=k:a=k;return a}}}}buildMesh(a=render3D?.smoothShading){return buildGrid(this.size,vec2(this.columns-1,this.rows-1),this.colors&&((b,c)=>this.getColor(b,c)),(b,c)=>this.getHeight(b,c),a)}}function render3DImageData(a){a instanceof TextureInfo&&(a=a.image);ASSERT(a&&a.width&&a.height,"image is not loaded");ASSERT(workReadCanvas,"reading an image needs a canvas, pass arrays in headless mode");const b=a.width,c=a.height;workReadCanvas.width=b;workReadCanvas.height=c;workReadContext.drawImage(a,0,0);return workReadContext.getImageData(0,0,b,c)}function render3DImageToArray(a,b){const{data:c,width:d,height:e}=render3DImageData(a);a=[];for(let f=0;f<e;++f){const g=a[f]=[];for(let h=0;h<d;++h){const k=4*(f*d+h);g.push(b(c[k],c[k+1],c[k+2],c[k+3]))}}return a}const render3DGlyphCache=new WeakMap,render3DPixelCache=new WeakMap;function render3DReadPixels(a){a=a.image;let b=render3DPixelCache.get(a);b||render3DPixelCache.set(a,b=render3DImageData(a));return b}class EngineObject3D extends EngineObject{constructor(a=vec3(),b,c,d=WHITE){ASSERT(!c||c instanceof TileInfo||c instanceof TextureInfo,"tileInfo must be a TileInfo or TextureInfo, it comes before color");c instanceof TextureInfo&&(c=new TileInfo(vec2(),c.size,c,0,0));super(vec2(),vec2(),c,0,d);ASSERT(isVector3(a),"pos3D must be a vec3");ASSERT(!b||b instanceof Mesh,"mesh must be a Mesh or undefined");this.mass=0;this.pos3D=a.copy();this.rotation3D=vec3();this.scale3D=vec3(1);this.velocity3D=vec3();this.angleVelocity3D=vec3();this.mesh=b;this.size3D=vec3(1);this.softShadow=0;this.sync2D=this.pixelated=this.upright=!1;this.transparent=!b&&!!c;this.additive=!1;this.specular=this.emissive=0;this.castShadow=!0;this.collideAsSphere3D=!1;this.receiveShadow=!0;this.renderAfter2D=void 0}updatePhysics(){ASSERT(!this.sync2D||!this.collideSolidObjects||this.size.x&&this.size.y,"a sync2D object collides in 2D, so give it a 2D size as well as a size3D",this.size);this.sync2D&&super.updatePhysics();render3DMove(this);this.collideSolidObjects&&!this.sync2D&&render3DCollideSolid(this)}updateTransforms(){paused||(this.parent&&render3DMove(this),this.sync2D&&(this.pos3D.x=this.pos.x,this.pos3D.y=this.pos.y,this.rotation3D.z=-this.angle));super.updateTransforms()}setCollision(a=!0,b=!0,c=!1,d=!1){super.setCollision(a,b,c,d)}getWorldPos3D(){return this.getMatrix().getTranslation()}getForward3D(){return render3DAxis(this.getMatrix().m,8).normalize(-1)}getRight3D(){return render3DAxis(this.getMatrix().m,0).normalize()}getUp3D(){return render3DAxis(this.getMatrix().m,4).normalize()}getMatrix(){const a=buildMatrix(this.pos3D,this.rotation3D,this.scale3D);return this.parent instanceof EngineObject3D?this.parent.getMatrix().multiply(a):a}lookAt(a){const b=this.parent instanceof EngineObject3D?this.parent:void 0;a=b?b.getMatrix().invert().transformPoint(a):a;this.rotation3D=render3DLookRotation(a.subtract(this.pos3D),this.rotation3D)}setMesh(a){ASSERT(!a||a instanceof Mesh,"mesh must be a Mesh or undefined");const b=this.mesh;this.mesh=a;b&&b!==a&&b.buffer&&!engineObjects.some(c=>c.mesh===b)&&b.dispose();return a}render(){}render3D(){ASSERT(this.transparent||this.additive||1<=this.color.a,"an object that fades needs its transparent flag, an opaque draw ignores the color alpha",this.color);if(this.mesh)render3D.drawMesh(this.mesh,this.getMatrix(),this.tileInfo,this.color);else if(this.tileInfo){const a=this.getMatrix().m;render3D.drawBillboard(vec3(a[12],a[13],a[14]),vec2(this.size3D.x*hypot(a[0],a[1],a[2]),this.size3D.y*hypot(a[4],a[5],a[6])),this.tileInfo,this.color,this.rotation3D.z,this.upright)}}}function render3DMove(a){if(a.mass&&!a.sync2D){const b=a.velocity3D,c=render3D.gravity,d=a.gravityScale,e=a.damping;a.velocity3D=vec3(b.x*e+c.x*d,b.y*e+c.y*d,b.z*e+c.z*d)}a.pos3D=a.pos3D.add(a.velocity3D);a.rotation3D=a.rotation3D.add(a.angleVelocity3D)}function render3DSolidShape(a){ASSERT(!a.parent,"a child rides along with its parent, it has no world pos3D of its own to collide with");const b=a.size3D;var c=a.scale3D;const d=abs(c.x),e=abs(c.y);c=abs(c.z);return a.collideAsSphere3D?{pos:a.pos3D.copy(),radius:max(b.x,b.y,b.z)/2*max(d,e,c)}:{pos:a.pos3D.copy(),size:vec3(b.x*d,b.y*e,b.z*c)}}function render3DSolidReach(a){const b=a.size3D;var c=a.scale3D;const d=abs(c.x),e=abs(c.y);c=abs(c.z);return a.collideAsSphere3D?max(b.x,b.y,b.z)/2*max(d,e,c):hypot(b.x*d,b.y*e,b.z*c)/2}function render3DSolidPush(a,b){return a.size?b.size?collideBoxBox3D(a.pos,a.size,b.pos,b.size):(a=collideSphereBox(b.pos,b.radius,a.pos,a.size))&&a.scale(-1):b.size?collideSphereBox(a.pos,a.radius,b.pos,b.size):collideSphereSphere(a.pos,a.radius,b.pos,b.radius)}function render3DCollideSolid(a){let b=render3DSolidShape(a);const c=render3DSolidReach(a);for(const h of engineObjectsCollide){if(h===a)break;if(h.destroyed||h.parent||h.sync2D||!(h instanceof EngineObject3D))continue;if(!a.isSolid&&!h.isSolid)continue;var d=b.pos;const k=h.pos3D;var e=c+render3DSolidReach(h),f=d.x-k.x,g=d.y-k.y;d=d.z-k.z;f*f+g*g+d*d>e*e||!(e=render3DSolidPush(b,render3DSolidShape(h)))||(f=a.collideWithObject(h,e),g=h.collideWithObject(a,e.scale(-1)),f&&g&&(g=a.mass+h.mass,f=a.mass?h.mass?h.mass/g:1:0,g=h.mass?a.mass?a.mass/g:1:0,a.pos3D=a.pos3D.add(e.scale(f)),h.pos3D=h.pos3D.subtract(e.scale(g)),f&&(b=render3DSolidShape(a)),e=e.normalize(),0>a.velocity3D.dot(e)&&(a.velocity3D=a.velocity3D.reflect(e,a.restitution)),0<h.velocity3D.dot(e)&&(h.velocity3D=h.velocity3D.reflect(e,h.restitution))))}}function engineObjectsCollect3D(a,b,c=engineObjects){b=render3DSize3(b);const d=[];for(const f of c)if(f instanceof EngineObject3D&&!f.destroyed){c=f.getMatrix().m;var e=f.size3D;if(e.x||e.y||e.z)e=vec3(e.x*hypot(c[0],c[1],c[2]),e.y*hypot(c[4],c[5],c[6]),e.z*hypot(c[8],c[9],c[10])),isOverlapping3D(a,b,vec3(c[12],c[13],c[14]),e)&&d.push(f)}return d}function render3DRaycastObject(a,b){if(!b.destroyed&&b instanceof EngineObject3D&&(b.mesh||b.tileInfo)){var c=b.getMatrix(),d=b.mesh;b=(d?d.radius||d.computeRadius():hypot(b.size3D.x,b.size3D.y)/2)*render3DMaxScale(c.m);if(0<b)return raycastSphere(a,c.getTranslation(),b)}}function engineObjectsRaycast3D(a,b=engineObjects){const c=[];for(const d of b)b=render3DRaycastObject(a,d),void 0!==b&&c.push({o:d,distance:b});return c.sort((d,e)=>d.distance-e.distance).map(d=>d.o)}function engineObjectsCallback3D(a,b,c,d=engineObjects){engineObjectsCollect3D(a,b,d).forEach(c)}class Light3D extends EngineObject3D{constructor(a=vec3(),b=5,c=WHITE,d=1){super(a,void 0,void 0,c);ASSERT(0<=b,"light radius cannot be negative, 0 is an off switch like an alpha of 0");ASSERT(0<=d,"light intensity cannot be negative, 0 is an off switch");this.size3D=vec3();this.radius=b;this.intensity=d;this.directional=!1}render3D(){}}class DirectionalLight3D extends Light3D{constructor(a=vec3(0,1,0),b=WHITE,c=1){super(a,0,b,c);this.directional=!0}}class CameraControl3D extends EngineObject3D{constructor(a=vec3(),b=10,c=.4,d=0){super(a);this.size3D=vec3();this.distance=b;this.pitch=c;this.idleSpin=d;this.dragButton=this.yaw=0;this.dragSpeed=.01;this.zoomSpeed=.1;this.zoomRange=vec2(b/4,3*b);this.pitchRange=vec2(-.2,1.4)}update(){mouseIsDown(this.dragButton)?(this.yaw-=mouseDeltaScreen.x*this.dragSpeed,this.pitch+=mouseDeltaScreen.y*this.dragSpeed):this.yaw+=this.idleSpin;this.pitch=clamp(this.pitch,this.pitchRange.x,this.pitchRange.y);this.zoomSpeed&&mouseWheel&&(this.distance=clamp(this.distance*(1+sign(mouseWheel)*this.zoomSpeed),this.zoomRange.x,this.zoomRange.y));render3D.camera.orbit(this.getWorldPos3D(),this.distance,this.yaw,this.pitch)}render3D(){}}class FirstPersonCamera3D extends EngineObject3D{constructor(a=render3D.camera.pos,b=render3D.camera.rotation.y,c=render3D.camera.rotation.x){super(a);this.size3D=vec3();this.mass=1;this.yaw=b;this.pitch=c;this.moveSpeed=.1;this.lookSpeed=.003;this.pitchRange=vec2(-1.5,1.5);this.fly=!1;this.lockPointer=!0}update(){this.lockPointer&&mouseWasPressed(0)&&pointerLockRequest();if(pointerLockIsActive()||mouseIsDown(0))this.yaw-=mouseDeltaScreen.x*this.lookSpeed,this.pitch-=mouseDeltaScreen.y*this.lookSpeed;this.pitch=clamp(this.pitch,this.pitchRange.x,this.pitchRange.y);var a=keyDirection();a=vec3(a.x,0,-a.y).clampLength(1).scale(this.moveSpeed).rotateX(this.fly?this.pitch:0).rotateY(this.yaw);this.velocity3D=this.fly?a:vec3(a.x,this.velocity3D.y,a.z);render3D.camera.pos=this.getWorldPos3D();render3D.camera.rotation=vec3(this.pitch,this.yaw,0)}destroy(a){this.lockPointer&&pointerLockIsActive()&&pointerLockExit();super.destroy(a)}render3D(){}}class ParticleEmitter3D extends EngineObject3D{constructor(a=vec3(),b=0,c=0,d=100,e=PI,f,g=WHITE,h=WHITE,k=CLEAR_WHITE,l=CLEAR_WHITE,m=.5,n=.1,p=1,r=.1,q=1,u=0,t=.1,v=.2,x=!1){super(a,void 0,f);this.transparent=!0;this.castShadow=!1;this.size3D=vec3();this.emitSize=b;this.emitTime=c;this.emitRate=d;this.emitConeAngle=e;this.colorStartA=g.copy();this.colorStartB=h.copy();this.colorEndA=k.copy();this.colorEndB=l.copy();this.particleTime=m;this.sizeStart=n;this.sizeEnd=p;this.speed=r;this.damping=q;this.gravity=u;this.fadeRate=t;this.randomness=v;this.additive=x;this.angleSpeed=this.trailTime=0;this.angleDamping=1;this.particles=[];this.emitTimeBuffer=0}update(){var a=this.getMatrix();this.worldPos3D=a.getTranslation();a=render3DMaxScale(a.m);if(!this.emitTime||this.getAliveTime()<=this.emitTime){if(this.emitRate&&particleEmitRateScale)for(this.emitTimeBuffer+=this.emitRate*particleEmitRateScale*timeDelta;1<=this.emitTimeBuffer;--this.emitTimeBuffer)this.emitParticle()}else this.particles.length||this.destroy();const b=this.particles;for(let d=b.length;d--;){const e=b[d];var c=e.velocity;c.x*=this.damping;c.y*=this.damping;c.z*=this.damping;c.y+=this.gravity*a;e.pos=e.pos.add(c);e.angle+=e.angleVelocity*=this.angleDamping;if(this.trailTime){c=e.trail||(e.trail=[]);c.push(e.pos);const f=c.length-this.trailTime/timeDelta;0<f&&c.splice(0,f)}(e.age+=timeDelta)>=e.life&&(b[d]=b[b.length-1],b.pop())}}destroy(a){if(a||!this.particles.length||this.destroyed)return super.destroy(a);this.emitTime=-1;render3DDetach(this)}emitParticle(){const a=()=>rand(1-this.randomness,1+this.randomness),b=this.getMatrix(),c=render3DMaxScale(b.m);var d=this.emitSize;d=isVector3(d)?vec3(rand(-.5,.5)*d.x,rand(-.5,.5)*d.y,rand(-.5,.5)*d.z):randInSphere(d/2);const e=b.transformDirection(randVector3(1,this.emitConeAngle)).normalize();this.particles.push({pos:b.transformPoint(d),velocity:e.scale(this.speed*a()*c),colorStart:randColor(this.colorStartA,this.colorStartB,!0),colorEnd:randColor(this.colorEndA,this.colorEndB,!0),sizeStart:this.sizeStart*a()*c,sizeEnd:this.sizeEnd*a()*c,life:this.particleTime*a(),angle:this.angleSpeed?rand(2*PI):0,angleVelocity:this.angleSpeed?this.angleSpeed*a()*randSign():0,age:0})}render3D(){if(render3D.transparentQueue)return render3D.queueTransparent(this.getWorldPos3D(),()=>this.render3D());const a=this.fadeRate/2,b=this.tileInfo||render3DSoftDot();for(const e of this.particles){var c=e.age/e.life,d=c<a?c/a:c>1-a?(1-c)/a:1;const f=e.colorStart.lerp(e.colorEnd,c);c=lerp(e.sizeStart,e.sizeEnd,c);f.a*=d;if((d=e.trail)&&1<d.length){const g=[],h=[];for(let k=0;k<d.length;++k){const l=(k+1)/d.length;g.push(c*l);h.push(f.scale(1,l))}render3D.drawRibbon(d,g,this.tileInfo,h)}else b?render3D.drawBillboard(e.pos,vec2(c),b,f,e.angle):render3D.drawSoftDisc(e.pos,c,f,void 0,8)}}}class Trail3D extends EngineObject3D{constructor(a=vec3(),b=1,c=.2,d,e=WHITE,f=CLEAR_WHITE,g=!1){super(a,void 0,d,e);this.transparent=!0;this.additive=g;this.castShadow=!1;this.size3D=vec3();this.finishing=!1;this.lifeTime=b;this.width=c;this.colorEnd=f.copy();this.side=void 0;this.samples=[]}clear(){this.samples.length=0}destroy(a){if(a||!this.samples.length||this.destroyed||Infinity==this.lifeTime)return super.destroy(a);this.finishing=!0;render3DDetach(this)}update(){const a=this.samples;if(!this.finishing){const b=this.worldPos3D=this.getWorldPos3D(),c=a[a.length-1];(!c||1e-8<b.distanceSquared(c.pos))&&a.push({pos:b,side:this.side?.copy(),time:time})}for(;a.length&&time-a[0].time>this.lifeTime;)a.shift();this.finishing&&!a.length&&this.destroy()}render3D(){var a=this.samples;if(!(2>a.length)){var b=[],c=[],d=[],e=this.side?[]:void 0;for(const f of a)a=clamp((time-f.time)/this.lifeTime),b.push(f.pos),c.push(this.width*(1-a)),d.push(this.color.lerp(this.colorEnd,a)),e?.push(f.side);render3D.drawRibbon(b,c,this.tileInfo,d,e)}}}function parseOBJ(a,b=render3D?.smoothShading){const c=[],d=[],e=[],f=new Mesh;let g=!1;const h=(l,m)=>{l=parseInt(l);return m[0>l?m.length+l:l-1]};for(const l of a.split("\n"))switch(a=l.trim().split(/\s+/),a[0]){case"v":c.push(vec3(+a[1],+a[2],+a[3]));break;case"vn":d.push(vec3(+a[1],+a[2],+a[3]));break;case"vt":e.push(vec2(+a[1],1-+a[2]));break;case"f":var k=a.slice(1).map(p=>p.split("/"));if(3>k.length)break;a=k.map(p=>h(p[0],c));ASSERT(a.every(isVector3),"OBJ face uses a vertex index the file does not have",l);const m=k.map(p=>p[1]?h(p[1],e):RENDER3D_DEFAULT_UV),n=k.every(p=>p[2]);g||=n;k=n?render3DPolygonStrip(k.map(p=>h(p[2],d))):render3DFaceNormal(a[0],a[1],a[2],a[3]);f.addStrip(render3DPolygonStrip(a),k,render3DPolygonStrip(m))}!g&&b&&f.computeNormals(!0);return f}async function loadOBJ(a,b=render3D?.smoothShading){const c=await fetch(a);if(!c.ok)throw Error("loadOBJ failed: "+a);return parseOBJ(await c.text(),b)}let threeJS;class ThreeJSPlugin{constructor(a,b=60){ASSERT(!threeJS,"ThreeJS plugin already initialized");threeJS=this;headlessMode||(ASSERT(mainCanvas,"ThreeJS plugin must be created after engineInit, call in gameInit"),ASSERT(a&&a.WebGLRenderer,"three.js module must be passed in"),this.THREE=a,this.renderer=new a.WebGLRenderer({antialias:!0}),this.scene=new a.Scene,this.camera=new a.PerspectiveCamera(b,1,.1,1e3),this.cameraAlign2D=!0,a=this.renderer.domElement,b=mainCanvas.parentElement,b.insertBefore(a,b.firstChild),a.style.cssText=mainCanvas.style.cssText,setBackgroundCanvas(a),engineAddPlugin(void 0,()=>this.render()))}alignCamera2D(){const a=mainCanvasSize.y/2/cameraScale/tan(this.camera.fov/2*PI/180);this.camera.position.set(cameraPos.x,cameraPos.y,a);this.camera.rotation.set(0,0,-cameraAngle)}render(){if(this.renderer){var a=this.renderer.domElement,b=getCanvasPixelRatio(),c=mainCanvasSize.y*b|0;if(a.width!=(mainCanvasSize.x*b|0)||a.height!=c)this.renderer.setPixelRatio(b),this.renderer.setSize(mainCanvasSize.x,mainCanvasSize.y,!1),this.camera.aspect=mainCanvasSize.x/mainCanvasSize.y,this.camera.updateProjectionMatrix();a.style.cssText!=mainCanvas.style.cssText&&(a.style.cssText=mainCanvas.style.cssText);this.cameraAlign2D&&this.alignCamera2D();this.renderer.render(this.scene,this.camera)}}}class ThreeJSObject extends EngineObject{constructor(a,b,c,d=0){super(a,b);ASSERT(threeJS,"ThreeJS plugin must be initialized first");this.mesh=c;this.z=d;c&&(threeJS.scene.add(c),this.syncMesh())}update(){super.update();this.syncMesh()}syncMesh(){this.mesh&&(this.mesh.position.set(this.pos.x,this.pos.y,this.z),this.mesh.rotation.z=-this.angle)}render(){}destroy(a){this.destroyed||(this.mesh&&threeJS.scene.remove(this.mesh),super.destroy(a))}}