littlejsengine 1.26.0 → 1.26.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,7 @@
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.26.0",frameRate=60;let timeDelta=1/frameRate,engineObjects=[],engineObjectsCollide=[],engineObjectsCollideStaticLast=[],frame=0,time=0,timeReal=0,paused=!1;function getPaused(){return paused}function setPaused(isPaused=!0){paused=isPaused}let frameTimeLastMS=0,frameTimeBufferMS=0,averageFPS=0;const frameDeltaHistory=[];let frameIntervalMS=1e3/60,frameDeltaCarryMS=0,frameDeltaCarryAverageMS=0,frameDeltaSmoothing=!0;function engineFrameFit(unitMS){let timeMS=0,frames=0,offMS=0,sumF=0,sumT=0,sumFF=0,sumFT=0;var count=frameDeltaHistory.length;for(const d of frameDeltaHistory){var held=max(1,round(d/unitMS));timeMS+=d,frames+=held,offMS+=abs(d-held*unitMS),sumF+=frames,sumT+=timeMS,sumFF+=frames*frames,sumFT+=frames*timeMS}var spread=count*sumFF-sumF*sumF;return[spread?(count*sumFT-sumF*sumT)/spread:unitMS,offMS/count/unitMS]}let timeFixedStart=0,frameFixedStart=0,windowWidthLast=0,windowHeightLast=0,windowPixelRatioLast=0,engineUpdateInternal,engineFrameScheduled=!1,engineFrameErrorLast;const engineObjectsCollidePairs=new Map;function engineObjectsCollidePairAnswer(asker,other){return engineObjectsCollidePairs.get(asker)?.get(other)}function engineObjectsCollidePairAdd(asker,other,resolve=!1){let others=engineObjectsCollidePairs.get(asker);others||engineObjectsCollidePairs.set(asker,others=new Map),others.set(other,resolve)}let engineCollideGridMin=64,engineCollideGrid;const engineCollideGridBox=[0,0,0,0];function engineCollideGridCells(grid,o){var grid=grid.size,w=abs(o.size.x)/2,h=abs(o.size.y)/2,box=engineCollideGridBox;return box[0]=floor((o.pos.x-w)/grid),box[1]=floor((o.pos.y-h)/grid),box[2]=floor((o.pos.x+w)/grid),box[3]=floor((o.pos.y+h)/grid),box}const engineCollideGridFits=(x0,y0,x1,y1)=>(x1-x0+1)*(y1-y0+1)<=1024&&abs(x0)<2**31&&abs(y0)<2**31&&abs(x1)<2**31&&abs(y1)<2**31;function engineCollideGridPlace(grid,o){if(grid.index.has(o)){var[x0,y0,x1,y1]=engineCollideGridCells(grid,o),big=!engineCollideGridFits(x0,y0,x1,y1),was=grid.at.get(o);if(!was||was[0]!==x0||was[1]!==y0||was[2]!==x1||was[3]!==y1||was[4]!==big){if(was&&!was[4])for(let x=was[0];x<=was[2];++x)for(let y=was[1];y<=was[3];++y){var cell=grid.cells.get(1048576*x+y);cell.splice(cell.indexOf(o),1)}if(grid.big.delete(o),big)grid.big.add(o);else for(let x=x0;x<=x1;++x)for(let y=y0;y<=y1;++y){var key=1048576*x+y;const cell=grid.cells.get(key);cell?cell.push(o):grid.cells.set(key,[o])}grid.at.set(o,[x0,y0,x1,y1,big])}}}function engineCollideGridAdd(grid,other,after,stamp,near){grid.seen.get(other)!==stamp&&(grid.seen.set(other,stamp),grid.index.get(other)>after)&&near.push(other)}function*engineCollideGridWalk(o){var grid=engineCollideGrid.built||=function(list){let extent=0;for(const o of list)extent+=min(max(abs(o.size.x),abs(o.size.y)),16)||0;const index=new Map,grid={size:max(2*extent/list.length,.5),cells:new Map,at:new Map,index:index,big:new Set,seen:new Map,stamp:0,byIndex:(a,b)=>index.get(a)-index.get(b)};return list.forEach((o,i)=>{index.set(o,i),engineCollideGridPlace(grid,o)}),grid}(engineCollideGrid.list);let after=-1,x,y,w,h,k=0;for(var near=[];;){if(o.pos.x===x&&o.pos.y===y&&o.size.x===w&&o.size.y===h||(x=o.pos.x,y=o.pos.y,w=o.size.x,h=o.size.y,function(grid,o,after,near){var[o,y0,x1,y1]=engineCollideGridCells(grid,o),stamp=++grid.stamp;near.length=0;for(const other of grid.big)engineCollideGridAdd(grid,other,after,stamp,near);if(engineCollideGridFits(o,y0,x1,y1))for(let x=o;x<=x1;++x)for(let y=y0;y<=y1;++y){var cell=grid.cells.get(1048576*x+y);if(cell)for(const other of cell)engineCollideGridAdd(grid,other,after,stamp,near)}else for(const other of grid.index.keys())engineCollideGridAdd(grid,other,after,stamp,near);near.sort(grid.byIndex)}(grid,o,after,near),k=0),k>=near.length)return;var other=near[k++];after=grid.index.get(other),yield other}}let engineInitialized=!1,engineInitDone,engineLoads,engineLoadsDone=0,engineObjectsUpdateCount=0;const engineChildStack=[];const pluginList=[];class EnginePlugin{constructor(update,render,glContextLost,glContextRestored,preRender){this.update=update,this.render=render,this.glContextLost=glContextLost,this.glContextRestored=glContextRestored,this.preRender=preRender}}function engineAddPlugin(update,render,glContextLost,glContextRestored,preRender){update=new EnginePlugin(update,render,glContextLost,glContextRestored,preRender);pluginList.push(update)}function engineAddLoad(promise){var done;return engineLoads&&(engineLoads.push(promise),promise.then(done=()=>{++engineLoadsDone},done)),promise}async function engineWaitForLoads(){let waiting=!0;const start=performance.now(),drawFrame=()=>{var elapsed;waiting&&(elapsed=(performance.now()-start)/1e3,headlessMode||!loadingScreen||elapsed<.5||(inputClear(),engineUpdateCanvas(),loadingScreen(engineLoadsDone/engineLoads.length)),setTimeout(drawFrame,16))};headlessMode||drawFrame();try{for(let count;count!==engineLoads.length;)count=engineLoads.length,await Promise.allSettled(engineLoads)}finally{waiting=!1}}async function engineInit(gameInit,gameUpdate,gameUpdatePost,gameRender,gameRenderPost,imageSources=[],rootElement){if(engineInitialized)return engineInitDone;function engineUpdate(frameTimeMS=0){var manualStepAtStart=engineManualStep;let frameTimeDeltaMS=frameTimeMS-frameTimeLastMS;frameTimeLastMS||(frameTimeDeltaMS=0),frameTimeLastMS=frameTimeMS,(debug||debugWatermark)&&(averageFPS=lerp(averageFPS,1e3/(frameTimeDeltaMS||1),.05)),manualStepAtStart||(frameTimeDeltaMS=0<(frameTimeMS=frameTimeDeltaMS)?(frameTimeMS<250&&(frameDeltaHistory.push(frameTimeMS),64<frameDeltaHistory.length&&frameDeltaHistory.shift(),recent=frameDeltaHistory.slice(-16).sort((a,b)=>a-b),[lastMS,lastOff]=engineFrameFit(frameIntervalMS),[recent,quartileOff]=engineFrameFit(recent[recent.length>>2]),frameIntervalMS=lastOff<=quartileOff?lastMS:recent,frameDeltaSmoothing=min(lastOff,quartileOff)<(frameDeltaSmoothing?.2:.12)),frameDeltaSmoothing?(lastMS=frameDeltaCarryAverageMS/32,frameDeltaCarryMS+=frameTimeMS,(recent=round((frameDeltaCarryMS-lastMS)/frameIntervalMS))<1?0:(lastOff=recent*frameIntervalMS+lastMS,frameDeltaCarryMS-=lastOff,frameDeltaCarryAverageMS+=(frameDeltaCarryMS-frameDeltaCarryAverageMS)/32,lastOff)):(quartileOff=frameTimeMS+frameDeltaCarryMS,frameDeltaCarryMS=min(quartileOff,0),frameDeltaCarryAverageMS=0,max(quartileOff,0))):0),audioUpdateVolume();var recent=debug&&debugKeysAlways,lastMS=recent&&keyIsDown("Equal"),lastOff=recent&&keyIsDown("Minus"),debugScale=lastMS?10:lastOff?.1:1,frameTimeMS=frameTimeDeltaMS,quartileOff=(timeReal+=frameTimeDeltaMS*debugScale/1e3,timeScale*debugScale);frameTimeDeltaMS*=quartileOff;let wasUpdated=!1;if(engineVariableStep)(0<frameTimeMS||manualStepAtStart)&&((frozenTick=paused||!quartileOff)||(timeDelta=manualStepAtStart?quartileOff/frameRate:min(frameTimeMS,50)*quartileOff/1e3,time+=timeDelta,++frame),engineTick(frozenTick));else{var frozenTick=paused||!quartileOff;frameTimeBufferMS+=frozenTick?frameTimeMS:frameTimeDeltaMS;let deltaSmooth=0;(frameTimeBufferMS=min(frameTimeBufferMS,50*(frozenTick?1:max(1,quartileOff))))<0&&-9<frameTimeBufferMS&&(deltaSmooth=frameTimeBufferMS,frameTimeBufferMS=0);try{for(;0<=frameTimeBufferMS;)try{const frozenTick=paused||!(timeScale*debugScale);frozenTick||(time=timeFixedStart+(frame++-frameFixedStart)/frameRate),engineTick(frozenTick)}finally{frameTimeBufferMS-=1e3/frameRate}}finally{frameTimeBufferMS+=deltaSmooth}}function engineTick(frozenTick){if(wasUpdated=!0,engineUpdateCanvas(),!headlessMode){if(touchInputEnable&&isTouchDevice||document.hasFocus()||inputClear(),mousePos=screenToWorld(mousePosScreen),mouseDelta=screenToWorldDelta(mouseDeltaScreen),!touchGamepadOverlay&&touchGamepadEnable&&isTouchDevice&&!headlessMode&&document.body){var overlay=touchGamepadOverlay=document.createElement("div");overlay.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)";const stage=touchGamepadStage=document.createElement("div");stage.style.cssText="position:relative;width:100%;height:100%;pointer-events:none",overlay.appendChild(stage);var svg=touchGamepadSvg=document.createElementNS(touchGamepadSvgNS,"svg"),svg=(svg.style.cssText="position:absolute;inset:0;width:100%;height:100%;pointer-events:none;overflow:visible;fill:none;stroke:#fff;stroke-width:3",stage.appendChild(svg),()=>{const z=document.createElement("div");return z.style.cssText="position:absolute;pointer-events:auto;touch-action:none",z.addEventListener("pointerdown",e=>function(e,zone){var W,H,hit,side;touchGamepadEnable&&"mouse"!==e.pointerType&&(e.preventDefault(),zone.setPointerCapture(e.pointerId),touchGamepadTimer.set(),soundEnable&&!headlessMode&&audioContext&&!audioIsRunning()&&audioResume(),paused?touchGamepadCenterButtonSize&&!touchGamepadButtons[9]&&(touchGamepadButtons[9]=touchGamepadButtonsPressed[9]=1,touchGamepadPointerRole.set(e.pointerId,"start")):(zone=touchGamepadStageRect(),W=zone.width,H=zone.height,zone=vec2(e.clientX-zone.left,e.clientY-zone.top),(hit=function(p,W,H){var S=touchGamepadSize,leftHalf=p.x<W/2,floatTop=.4*H;if(touchGamepadCenterButtonSize&&vec2(W/2,H/2).distance(p)<touchGamepadCenterButtonSize&&!function(p,W,H){for(let side=0;side<2;side++)if(touchGamepadStartBlockSide(side)&&touchGamepadSideCenter(side,W,H).distance(p)<2*touchGamepadSize)return 1;return}(p,W,H))return{role:"start"};for(let side=0;side<2;side++){var onHalf=side?!leftHalf:leftHalf;if(touchGamepadSideStick(side)){var otherControl=touchGamepadSideHasControl(side?0:1);if(touchGamepadFloating?(!otherControl||onHalf)&&p.y>floatTop:onHalf&&touchGamepadSideCenter(side,W,H).distance(p)<2*S)return{role:"stick",side:side}}else if(1<=touchGamepadSideButtonCount(side)){otherControl=function(side,p,W,H){var count=touchGamepadSideButtonCount(side),base=side?0:4,W=touchGamepadSideCenter(side,W,H);if(W.distance(p)>=touchGamepadSize)return-1;if(1===count)return base;H=W.subtract(p);side||(H.x*=-1);W=2===count?H.x<H.y?1:0:mod(H.direction()+2,4);return(W=touchGamepadFaceIndex(W))<count?base+W:-1}(side,p,W,H);if(0<=otherControl)return{role:"face",btn:otherControl}}}}(zone,W,H))&&("stick"===hit.role?(side=hit.side,void 0===touchGamepadStickPointerId[side]&&(touchGamepadStickAnchors[side]=touchGamepadFloating?zone:touchGamepadSideCenter(side,W,H),touchGamepadStickPointerId[side]=e.pointerId,touchGamepadPointerRole.set(e.pointerId,"stick"+side),touchGamepadNeedRelayout=!0,touchGamepadApplyStick(side,zone),touchGamepadButtonsPressed[touchGamepadStickButton(side)]=1)):"face"===hit.role?touchGamepadButtons[hit.btn]||(touchGamepadButtons[hit.btn]=touchGamepadButtonsPressed[hit.btn]=1,touchGamepadPointerRole.set(e.pointerId,"face"+hit.btn)):touchGamepadButtons[9]||(touchGamepadButtons[9]=touchGamepadButtonsPressed[9]=1,touchGamepadPointerRole.set(e.pointerId,"start")))))}(e,z)),z.addEventListener("pointermove",e=>function(e){var role=touchGamepadPointerRole.get(e.pointerId);role&&(e.preventDefault(),e=function(e){var r=touchGamepadStageRect();return vec2(e.clientX-r.left,e.clientY-r.top)}(e),"stick0"!==role&&"stick1"!==role||touchGamepadApplyStick("stick1"===role?1:0,e))}(e)),z.addEventListener("pointerup",e=>touchGamepadPointerUp(e)),z.addEventListener("pointercancel",e=>touchGamepadPointerUp(e)),stage.appendChild(z),z});touchGamepadSideZones[0]=svg(),touchGamepadSideZones[1]=svg(),touchGamepadZoneC=svg(),addEventListener("resize",()=>touchGamepadNeedRelayout=!0),document.body.appendChild(overlay),touchGamepadNeedRelayout=!0}{const applyDeadZones=v=>{var deadZone=v=>.3<v?percent(v,.3,.8):v<-.3?-percent(-v,.3,.8):0;return vec2(deadZone(v.x),deadZone(-v.y)).clampLength()};if(touchGamepadEnable&&isTouchDevice&&touchGamepadTimer.isSet()&&gamepadsEnable&&function(){for(const gamepad of inputGetGamepads())if(gamepad){for(const button of gamepad.buttons)if(button.pressed)return 1;if("standard"===gamepad.mapping)for(let j=0;j<4&&j<gamepad.axes.length;++j)if(.5<abs(gamepad.axes[j]))return 1}return}()&&touchGamepadTimer.unset(),touchGamepadEnable&&isTouchDevice&&touchGamepadTimer.isSet()){gamepadPrimary=0;var out,x,touchStick,sticks=gamepadStickData[0]??(gamepadStickData[0]=[]),dpad=gamepadDpadData[0]??(gamepadDpadData[0]=vec2());sticks.length=0,dpad.set();for(let side=0;side<2;side++)touchGamepadSideStick(side)&&(out=touchGamepadStickOut(side),sticks[out]=vec2(),touchStick=touchGamepadSticks[side]??vec2(),touchGamepadAnalog?sticks[out]=applyDeadZones(touchStick):.3<touchStick.lengthSquared()&&(x=clamp(round(touchStick.x),-1,1),touchStick=clamp(round(touchStick.y),-1,1),sticks[out]=vec2(x,-touchStick).clampLength(),out||dpad.set(x,-touchStick)));var data=inputData[1]??(inputData[1]=[]);for(let i=12;i--;){var wasDown=gamepadIsDown(i,0),isDown=touchGamepadButtons[i],pressed=touchGamepadButtonsPressed[i]||isDown&&!wasDown;data[i]=isDown?pressed?3:1:pressed?6:wasDown?4:0,touchGamepadVibration&&2&data[i]&&(9===i||function(i){for(let side=0;side<2;side++){var base=touchGamepadSideButtonBase(side);if(!touchGamepadSideStick(side)&&base<=i&&i<base+touchGamepadSideButtonCount(side))return 1}return}(i))&&vibrate(touchGamepadVibration)}touchGamepadButtonsPressed.length=0,gamepadButtonsLast[0]=void 0}else if(!gamepadsEnable||debug||document.hasFocus()){var gamepads=gamepadsEnable?inputGetGamepads():[];for(let i=0;i<8;++i){const gamepad=gamepads[i];if(gamepad){var data=inputData[i+1]??(inputData[i+1]=[]),buttonsLast=gamepadButtonsLast[i]??(gamepadButtonsLast[i]=[]),sticks=gamepadStickData[i]??(gamepadStickData[i]=[]),dpad=gamepadDpadData[i]??(gamepadDpadData[i]=vec2());const isStandard="standard"===gamepad.mapping,centered=gamepadAxisCentered[i]??(gamepadAxisCentered[i]=[]);var readAxis=j=>{var frames,v=isFinite(gamepad.axes[j])?gamepad.axes[j]:0;return isStandard&&j<4||!gamepadAxisFilterEnable||(frames=0|centered[j])>gamepadAxisCenteredFrames?v:(centered[j]=abs(v)<.3?1+frames:0,0)};for(let j=0;j<gamepad.axes.length-1;j+=2)sticks[j>>1]=applyDeadZones(vec2(readAxis(j),readAxis(j+1)));let hadInput=sticks.some(stick=>.25<stick.lengthSquared());for(let j=gamepad.buttons.length;j--;){var button=gamepad.buttons[j],wasDown=buttonsLast[j];buttonsLast[j]=button.pressed,data[j]=button.pressed?wasDown?1:3:wasDown?4:0,button.pressed&&(!button.value||.9<button.value)&&(hadInput=!0)}data.length=buttonsLast.length=gamepad.buttons.length,sticks.length=gamepad.axes.length>>1,hadInput&&(gamepadHadInput[i]=!0,gamepadHadInput[gamepadPrimary]||(gamepadPrimary=i)),"standard"===gamepad.mapping&&dpad.set((gamepadIsDown(15,i)&&1)-(gamepadIsDown(14,i)&&1),(gamepadIsDown(12,i)&&1)-(gamepadIsDown(13,i)&&1)),gamepadDirectionEmulateStick&&(dpad.x||dpad.y)?sticks[0]=dpad.clampLength():gamepadDirectionEmulateStick&&!sticks.length&&(sticks[0]=vec2())}else inputData[i+1]=void 0,gamepadStickData[i]=void 0,gamepadDpadData[i]=void 0,gamepadHadInput[i]=void 0,gamepadAxisCentered[i]=void 0,gamepadButtonsLast[i]=void 0}touchGamepadEnable&&isUsingGamepad&&touchGamepadTimer.unset()}else gamepadButtonsLast.length=0}{svg=mouseIsDown(0)||mouseIsDown(1)||mouseIsDown(2)||mouseDeltaScreen.length()>inputMouseMoveThreshold;let gamepadActive=!1;for(let s=gamepadStickCount();s--&&!gamepadActive;)gamepadActive=.2<gamepadStick(s).lengthSquared();for(let b=17;b--&&!gamepadActive;)gamepadActive=gamepadIsDown(b);let keyboardActive=!1;for(const k in inputData[0])if(isNaN(+k)&&1&inputData[0][k]){keyboardActive=!0;break}gamepadActive?lastInputDevice="gamepad":svg?lastInputDevice="mouse":keyboardActive&&(lastInputDevice="keyboard"),isUsingGamepad="gamepad"===lastInputDevice}}if(frozenTick||gameUpdate(),pluginList.forEach(plugin=>plugin.update?.()),frozenTick){for(const o of engineObjects)o.parent||o.updateTransforms();engineObjects.some(o=>o.destroyed)&&(engineObjects=engineObjects.filter(o=>!o.destroyed))}else engineObjectsUpdate();gameUpdatePost(),inputUpdatePost()}engineManualStep&&!manualStepAtStart&&(frameTimeBufferMS=-500/frameRate);let windowChanged=!1;if(headlessMode||(frameTimeMS=devicePixelRatio,windowChanged=windowWidthLast!==innerWidth||windowHeightLast!==innerHeight||windowPixelRatioLast!==frameTimeMS,windowWidthLast=innerWidth,windowHeightLast=innerHeight,windowPixelRatioLast=frameTimeMS),wasUpdated||windowChanged)if(!headlessMode){wasUpdated||engineUpdateCanvas(),mainContext.imageSmoothingEnabled=!tilesPixelated,glPreRender(),setShader(),pluginList.forEach(plugin=>plugin.preRender?.()),gameRender(),engineObjectsSort();for(const o of engineObjects)o.destroyed||(setShader(o.shader),o.render());setShader(),gameRenderPost(),setShader(),setAdditiveBlendMode(!1),pluginList.forEach(plugin=>plugin.render?.()),!function(){if(touchGamepadOverlay&&!headlessMode)if(touchGamepadEnable&&isTouchDevice){touchGamepadOverlay.style.display="";var dbg=debug&&debugGamepads,layout=[touchGamepadButtonCount,touchGamepadLeftButtonCount,touchGamepadLeftStick,touchGamepadRightStick,touchGamepadAnalog,touchGamepadSize,touchGamepadFloating,touchGamepadCenterButtonSize,paused,dbg].join(),layout=(layout!==touchGamepadLastLayout&&(touchGamepadLastLayout=layout,touchGamepadNeedRelayout=!0),touchGamepadNeedRelayout&&!function(){if(touchGamepadOverlay){var r=touchGamepadStageRect(),W=r.width,r=r.height,S=touchGamepadSize,setZone=(z,css)=>z.style.cssText="position:absolute;pointer-events:auto;touch-action:none;"+css;if(paused){for(const zone of touchGamepadSideZones)zone.style.display="none";touchGamepadCenterButtonSize?(setZone(touchGamepadZoneC,"inset:0"),touchGamepadZoneC.style.display=""):touchGamepadZoneC.style.display="none"}else{for(let side=0;side<2;side++){var width,zone=touchGamepadSideZones[side],edge=side?"right":"left";zone.style.display=touchGamepadSideHasControl(side)?"":"none",touchGamepadFloating&&touchGamepadSideStick(side)?(width=touchGamepadSideHasControl(side?0:1)?"50%":"100%",setZone(zone,edge+`:0;bottom:0;width:${width};height:60%`)):setZone(zone,edge+`:0;bottom:0;width:${3*S}px;height:${3*S}px`)}touchGamepadZoneC.style.display=touchGamepadCenterButtonSize?"":"none";var c=touchGamepadCenterButtonSize;setZone(touchGamepadZoneC,`left:50%;top:50%;width:${2*c}px;height:${2*c}px;transform:translate(-50%,-50%)`)}!function(W,H){const svg=touchGamepadSvg;for(;svg.firstChild;)svg.removeChild(svg.firstChild);const els=touchGamepadSvgEls={face:[],thumb:[]},S=touchGamepadSize,circle=(cx,cy,rr,fill)=>{var c=document.createElementNS(touchGamepadSvgNS,"circle");return c.setAttribute("cx",cx),c.setAttribute("cy",cy),c.setAttribute("r",rr),fill&&c.setAttribute("fill",fill),svg.appendChild(c),c};for(let side=0;side<2;side++){var count=touchGamepadSideButtonCount(side),base=touchGamepadSideButtonBase(side),ctr=touchGamepadSideCenter(side,W,H);if(touchGamepadSideStick(side))touchGamepadAnalog?circle(ctr.x,ctr.y,S/2):(ctr=>{var a=.18*S,b=.5*S,x=ctr.x,ctr=ctr.y,p=document.createElementNS(touchGamepadSvgNS,"path");p.setAttribute("d",`M ${x-a} ${ctr-b} H ${x+a} V ${ctr-a} H ${x+b} V ${ctr+a} H ${x+a} `+`V ${ctr+b} H ${x-a} V ${ctr+a} H ${x-b} V ${ctr-a} H ${x-a} Z`),svg.appendChild(p)})(ctr),els.thumb[side]=circle(ctr.x,ctr.y,S/4,"#fff");else if(1===count)els.face[base]=circle(ctr.x,ctr.y,S/2,"#000");else for(let i=0;i<count;i++){var button,j=mod(i-1,4),j=(button=touchGamepadFaceIndex(2<count?j:min(j,count-1)),vec2().setDirection(j,S/2)),j=(2===count&&(j.x*=-1),side||(j.x*=-1),ctr.add(j));els.face[base+button]=circle(j.x,j.y,S/4,"#000")}}debug&&debugGamepads&&!function(W,H){const S=touchGamepadSize,svg=touchGamepadSvg,shape=(tag,attrs,stroke)=>{var el=document.createElementNS(touchGamepadSvgNS,tag);for(const k in attrs)el.setAttribute(k,attrs[k]);el.setAttribute("stroke",stroke),el.setAttribute("stroke-width",2),el.setAttribute("fill","none"),svg.appendChild(el)},ring=(c,rr,stroke)=>shape("circle",{cx:c.x,cy:c.y,r:rr},stroke);shape("line",{x1:W/2,y1:0,x2:W/2,y2:H},"#0f0");for(let side=0;side<2;side++){var top,full,x;touchGamepadSideStick(side)?touchGamepadFloating?(top=.4*H,full=!touchGamepadSideHasControl(side?0:1),x=!full&&side?W/2:0,shape("rect",{x:x,y:top,width:full?W:W/2,height:H-top},"#0ff")):ring(touchGamepadSideCenter(side,W,H),2*S,"#0ff"):1<=touchGamepadSideButtonCount(side)&&ring(touchGamepadSideCenter(side,W,H),S,"#0ff")}if(touchGamepadCenterButtonSize){ring(vec2(W/2,H/2),touchGamepadCenterButtonSize,"#ff0");for(let side=0;side<2;side++)touchGamepadStartBlockSide(side)&&ring(touchGamepadSideCenter(side,W,H),2*S,"#f0f")}}(W,H)}(W,r),touchGamepadNeedRelayout=!1}}(),touchGamepadDisplayTime&&!touchGamepadPointerRole.size?percent(touchGamepadTimer.get(),touchGamepadDisplayTime+1,touchGamepadDisplayTime):1),visible=dbg||touchGamepadTimer.isSet()&&0<layout&&!paused;if(touchGamepadOverlay.style.opacity=visible?dbg?1:layout*touchGamepadAlpha:0,visible){var t,dbg=touchGamepadStageRect(),W=dbg.width,H=dbg.height,S=touchGamepadSize,els=touchGamepadSvgEls;if(els){for(let side=0;side<2;side++)touchGamepadSideStick(side)&&els.thumb[side]&&(t=touchGamepadSideCenter(side,W,H).add((touchGamepadSticks[side]??vec2()).scale(S/2)),els.thumb[side].setAttribute("cx",t.x),els.thumb[side].setAttribute("cy",t.y));for(let i=0;i<els.face.length;i++)els.face[i]&&els.face[i].setAttribute("fill",touchGamepadButtons[i]?"#fff":"#000")}}}else"none"!==touchGamepadOverlay.style.display&&(touchGamepadOverlay.style.display="none",touchGamepadPointerRole.clear(),touchGamepadButtons.length=0,touchGamepadButtonsPressed.length=0,touchGamepadSticks.length=0,touchGamepadStickPointerId.length=0,inputData[1]=void 0,gamepadStickData[0]=gamepadDpadData[0]=void 0)}(),glFlush(),drawCount=0,primitiveCount=0}engineManualStep||engineScheduleFrame()}if(engineInitialized=!0,console.log("LittleJS Engine v1.26.0"),engineLoads=[],engineLoadsDone=0,"string"==typeof imageSources&&(imageSources=[imageSources]),gameInit||=()=>{},gameUpdate||=()=>{},gameUpdatePost||=()=>{},gameRender||=()=>{},gameRenderPost||=()=>{},headlessMode)return engineInitDone=startEngine([]);document.body||document.documentElement.appendChild(document.createElement("body")),function(rootElement){if(!glEnable||headlessMode)glCanBeEnabled=!1;else if(glCanvas=document.createElement("canvas"),glContext=glCanvas.getContext("webgl2",{antialias:glAntialias})){rootElement.appendChild(glCanvas),initWebGL();for(const info of glTextureInfos)info.glTexture||=glCreateTexture(info.image,info.wrap,info.pixelated);glCanvas.addEventListener("webglcontextlost",e=>{glEnableBeforeLoss=glEnable,glEnable=!1,glCanvas.style.display="none",e.preventDefault();for(const info of glTextureInfos)info.glTexture=void 0;glActiveTexture=void 0;for(const shader of glShaderObjects)shader.program=void 0;glBatchShader=void 0,glProgramCustom=!0,glUniformLocations=new WeakMap,glBatchCount=0,glPolyMode=!1,pluginList.forEach(plugin=>plugin.glContextLost?.())}),glCanvas.addEventListener("webglcontextrestored",()=>{glEnable=glEnableBeforeLoss,glCanvas.style.display="",glMipmappedTextures=new WeakSet,glMipmapsUntilTarget=new WeakSet,glMipmapsStale.clear(),glPremultipliedTextures=new WeakSet,initWebGL();for(const info of glTextureInfos)info.glTexture=glCreateTexture(info.image,info.wrap,info.pixelated);pluginList.forEach(plugin=>plugin.glContextRestored?.());for(const o of engineObjects)o instanceof TileLayer&&o.isUsingWebGL&&!o.destroyed&&o.redraw()})}else console.warn("WebGL2 not supported, falling back to 2D canvas rendering!"),glCanvas=glContext=void 0,glEnable=!1,glCanBeEnabled=!1,glDeviceFailed=!0;function initWebGL(){glMaxTextureSize=0|glContext.getParameter(glContext.MAX_TEXTURE_SIZE),glShader=glCreateProgram(gl_VERTEX_SOURCE,"#version 300 es\nprecision highp float;uniform sampler2D s;uniform bool premultipliedTexture;in vec2 v;in vec4 d,e;out vec4 c;void main(){vec4 t=texture(s,v);"+gl_FRAGMENT_TINT_SOURCE+"}"),glShaderPremultiplied=!1,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;}"),(glFailedPrograms.has(glShader)||glFailedPrograms.has(glPolyShader))&&(console.error("LittleJS: WebGL can not draw on this device, using Canvas2D"),glEnable=glCanBeEnabled=!1,glDeviceFailed=!0,glCanvas.style.display="none");var glInstanceData=new ArrayBuffer(gl_ARRAY_BUFFER_SIZE),glInstanceData=(glPositionData=new Float32Array(glInstanceData),glColorData=new Uint32Array(glInstanceData),glArrayBuffer=glContext.createBuffer(),glGeometryBuffer=glContext.createBuffer(),glFramebuffer=glContext.createFramebuffer(),glBatchCount=0,new Float32Array([0,0,1,0,0,1,1,1]));glContext.bindBuffer(glContext.ARRAY_BUFFER,glGeometryBuffer),glContext.bufferData(glContext.ARRAY_BUFFER,glInstanceData,glContext.STATIC_DRAW);let offset,shader,stride;glInstanceData=(name,type,typeSize,size,divisor=0)=>{var name=glContext.getAttribLocation(shader,name),normalize=1===typeSize,fixedStride=typeSize&&stride;glContext.enableVertexAttribArray(name),glContext.vertexAttribPointer(name,size,type,normalize,fixedStride,offset),glContext.vertexAttribDivisor(name,divisor),offset+=size*typeSize};glInstancedVAO=glContext.createVertexArray(),glContext.bindVertexArray(glInstancedVAO),offset=0,shader=glShader,stride=gl_INSTANCE_BYTE_STRIDE,glContext.bindBuffer(glContext.ARRAY_BUFFER,glGeometryBuffer),glInstanceData("g",glContext.FLOAT,0,2),glContext.bindBuffer(glContext.ARRAY_BUFFER,glArrayBuffer),glContext.bufferData(glContext.ARRAY_BUFFER,gl_ARRAY_BUFFER_SIZE,glContext.DYNAMIC_DRAW),glInstanceData("p",glContext.FLOAT,4,4,1),glInstanceData("u",glContext.FLOAT,4,4,1),glInstanceData("c",glContext.UNSIGNED_BYTE,1,4,1),glInstanceData("a",glContext.UNSIGNED_BYTE,1,4,1),glInstanceData("r",glContext.FLOAT,4,1,1),glPolyVAO=glContext.createVertexArray(),glContext.bindVertexArray(glPolyVAO),offset=0,shader=glPolyShader,stride=gl_POLY_VERTEX_BYTE_STRIDE,glInstanceData("p",glContext.FLOAT,4,2),glInstanceData("c",glContext.UNSIGNED_BYTE,1,4)}}(rootElement||=document.body);let styleRoot="margin:0;overflow:hidden;background:#000;user-select:none;-webkit-user-select:none;touch-action:none;-webkit-touch-callout:none;";function handleTouch(e){if(touchInputEnable){if(inputLastTouchTime=performance.now(),soundEnable&&!headlessMode&&audioContext&&!audioIsRunning()&&audioResume(),!touchGamepadEnable||touchGamepadPassthrough){const isGamepadTouch=t=>touchGamepadSideZones.includes(t.target)||t.target===touchGamepadZoneC;var gameTouches=[];for(const t of e.touches)isGamepadTouch(t)||gameTouches.push(t);var touching=gameTouches.length;if(touching){var pressTouch=!inputWasTouching&&"touchstart"==e.type&&[...e.changedTouches].find(t=>!isGamepadTouch(t)),touch=pressTouch||gameTouches[0];let pos=vec2(touch.clientX,touch.clientY);var a,b,distance,pinching=1<touching&&touchPinchWheel,delta=(1<touching?([a,b]=gameTouches,key=a.identifier+" "+b.identifier,distance=hypot(a.clientX-b.clientX,a.clientY-b.clientY),inputPinch&&inputPinch.key===key&&(delta=(inputPinch.distance-distance)/50,touchPinch+=delta,touchPinchWheel)&&(mouseWheel+=delta),inputPinch={key:key,distance:distance},pinching&&(pos=vec2((a.clientX+b.clientX)/2,(a.clientY+b.clientY)/2))):inputPinch=void 0,mousePosScreen);mousePosScreen=mouseEventToScreen(pos),pinching?1&inputData[0][0]&&(inputData[0][0]=2&inputData[0][0]|4):inputWasTouching&&touch.identifier===inputTouchIdentifier?mouseDeltaScreen=mouseDeltaScreen.add(mousePosScreen.subtract(delta)):pressTouch&&inCanvas(pressTouch.clientX,pressTouch.clientY)&&(inputData[0][0]=3|4&inputData[0][0]),inputTouchIdentifier=pinching?void 0:touch.identifier}else inputPinch=void 0,inputWasTouching&&1&inputData[0][0]&&(inputData[0][0]=2&inputData[0][0]|4);inputWasTouching=touching}var key;return inputPreventDefault&&e.cancelable&&document.hasFocus()&&!isOnControl(e)&&(key=document.activeElement,"touchstart"==e.type&&isTextInput(key)&&!key.contains(e.target)&&key.blur(),e.preventDefault()),!0}inputWasTouching&&1&inputData[0][0]&&(inputData[0][0]=2&inputData[0][0]|4),inputWasTouching=0}function isOnControl(e){return e.target?.closest?.("input,textarea,select,button,a,label,[contenteditable]")}function isTextInput(element){var tag=element?.tagName;return"INPUT"===tag?!["button","checkbox","color","file","image","radio","range","reset","submit"].includes(element.type):!!element?.isContentEditable||"TEXTAREA"===tag||"SELECT"===tag}function inCanvas(x,y){var rect=mainCanvas.getBoundingClientRect();return x>=rect.left&&x<=rect.right&&y>=rect.top&&y<=rect.bottom}function mouseEventToScreen(mousePos){var rect=mainCanvas.getBoundingClientRect(),px=percent(mousePos.x,rect.left,rect.right),mousePos=percent(mousePos.y,rect.top,rect.bottom);return vec2(px*mainCanvasSize.x,mousePos*mainCanvasSize.y)}rootElement!==document.body&&rootElement.clientHeight&&"static"===getComputedStyle(rootElement).position&&(styleRoot+="position:relative;"),rootElement.style.cssText=styleRoot+rootElement.style.cssText,mainCanvas=rootElement.appendChild(document.createElement("canvas")),drawContext=mainContext=mainCanvas.getContext("2d"),headlessMode||(document.addEventListener("keydown",function(e){soundEnable&&!headlessMode&&audioContext&&!audioIsRunning()&&audioResume();var typing=isTextInput(e.target)||isTextInput(document.activeElement);e.repeat||typing||inputKeysHeld.has(e.code)&&!e.metaKey||(inputKeysHeld.add(e.code),inputWASDEmulateDirection&&inputKeysHeld.has(inputArrowToWASD[e.code])&&1&inputData[0][e.code]||(inputData[0][e.code]=3|4&inputData[0][e.code]),(remap=function(k){return inputWASDEmulateDirection&&inputWASDToArrow[k]||k}(e.code))===e.code)||1&inputData[0][remap]||(inputData[0][remap]=3|4&inputData[0][remap]);var remap;inputPreventDefault&&e.cancelable&&document.hasFocus()&&(e.ctrlKey||e.metaKey||e.altKey||!typing&&(remap="string"==typeof e.key&&1===e.key.length,["ArrowUp","ArrowDown","ArrowLeft","ArrowRight","Space","Tab","Backspace"].includes(e.code)||remap)&&e.preventDefault())}),document.addEventListener("keyup",function onKeyUp(e){inputKeysHeld.delete(e.code);const remap=inputWASDToArrow[e.code]||e.code;for(const key of remap===e.code?[e.code]:[e.code,remap])inputKeysHeld.has(key)||inputWASDEmulateDirection&&inputKeysHeld.has(inputArrowToWASD[key])||1&inputData[0][key]&&(inputData[0][key]=2&inputData[0][key]|4);if("Meta"===e.key)for(const code of[...inputKeysHeld])onKeyUp({code:code})}),document.addEventListener("mousedown",function(e){var mousePosScreenLast;touchInputEnable&&inputIsTouchMouseEvent(e)||(soundEnable&&!headlessMode&&audioContext&&!audioIsRunning()&&audioResume(),(pointerLockIsActive()||inCanvas(e.x,e.y))&&(inputData[0][e.button]=3|4&inputData[0][e.button],mousePosScreenLast=mousePosScreen,mousePosScreen=mouseEventToScreen(vec2(e.x,e.y)),mouseDeltaScreen=mouseDeltaScreen.add(mousePosScreen.subtract(mousePosScreenLast)),inputPreventDefault)&&e.cancelable&&document.hasFocus()&&!isOnControl(e)&&(isTextInput(mousePosScreenLast=document.activeElement)&&!mousePosScreenLast.contains(e.target)&&mousePosScreenLast.blur(),e.preventDefault()))}),document.addEventListener("mouseup",function(e){touchInputEnable&&inputIsTouchMouseEvent(e)||(1&inputData[0][e.button]&&(inputData[0][e.button]=2&inputData[0][e.button]|4),inputPreventDefault&&e.cancelable&&(3===e.button||4===e.button)&&e.preventDefault())}),document.addEventListener("mousemove",function(e){mouseInWindow=!0;var mousePosScreenLast=mousePosScreen;mousePosScreen=mouseEventToScreen(vec2(e.x,e.y));let movement;{var rect;movement=pointerLockIsActive()?(rect=mainCanvas.getBoundingClientRect(),vec2(e.movementX*mainCanvasSize.x/rect.width,e.movementY*mainCanvasSize.y/rect.height)):mousePosScreen.subtract(mousePosScreenLast)}mouseDeltaScreen=mouseDeltaScreen.add(movement)}),document.addEventListener("mouseleave",function(){mouseInWindow=!1}),document.addEventListener("wheel",function(e){var target=e.target;target?.closest?.("input,textarea,select,[contenteditable]")||(e.ctrlKey||(mouseWheel+=sign(e.deltaY)),inputPreventDefault&&e.cancelable&&document.hasFocus()&&e.preventDefault())},{passive:!1}),document.addEventListener("contextmenu",function(e){inputPreventDefault&&!isTextInput(e.target)&&e.preventDefault()}),addEventListener("blur",function(){inputClear()}),isTouchDevice&&(document.addEventListener("touchstart",e=>handleTouch(e),{passive:!1}),document.addEventListener("touchmove",e=>handleTouch(e),{passive:!1}),document.addEventListener("touchend",e=>handleTouch(e),{passive:!1}),document.addEventListener("touchcancel",e=>handleTouch(e),{passive:!1}))),soundEnable&&!headlessMode&&(audioSetSession(),document.addEventListener("visibilitychange",audioVisibilityChange));rootElement="position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)",mainCanvas.style.cssText=rootElement,glCanvas&&(glCanvas.style.cssText=rootElement),setCanvasPixelated(canvasPixelated),engineUpdateCanvas(),glPreRender(),workContext=createCanvasContext(64),workCanvas=workContext.canvas,workReadContext=createCanvasContext(64,64,!0),workReadCanvas=workReadContext.canvas,rootElement=imageSources.map((src,i)=>loadTexture(i,src));return imageSources.length||rootElement.push(loadTexture(0)),rootElement.push(async function(){const image=new Image,tilePos=(await new Promise(resolve=>{image.onerror=image.onload=resolve,image.crossOrigin="anonymous",image.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=="}),vec2()),tileSize=vec2(8),textureInfo=new TextureInfo(image),tileInfo=new TileInfo(tilePos,tileSize,textureInfo,1,0);engineImageFont=new ImageFont(tileInfo)}()),showSplashScreen&&(imageSources=new Promise(resolve=>{let t=0;!function updateSplash(){inputClear();drawEngineLogo(t+=.01);1<t?resolve():setTimeout(updateSplash,16)}()}),rootElement.push(imageSources)),engineInitDone=startEngine(rootElement);async function startEngine(images){showSplashScreen&&!headlessMode&&await images[images.length-1];var init=(async()=>{await Promise.all(images),await gameInit()})();if(engineAddLoad(init),await engineWaitForLoads(),engineLoads=void 0,await 0,engineUpdateInternal=engineUpdate,!engineManualStep){if(debug)return engineUpdate();try{engineUpdate()}catch(error){engineFrameFailed(error)}}}}function engineUpdateCanvas(){if(!headlessMode){var dpr=getCanvasPixelRatio();if(canvasFixedSize.x){mainCanvasSize.set(canvasFixedSize.x,canvasFixedSize.y);var innerAspect=innerWidth/innerHeight,fixedAspect=canvasFixedSize.x/canvasFixedSize.y,w=innerAspect<fixedAspect?"100%":"",innerAspect=innerAspect<fixedAspect?"":"100%";mainCanvas.style.width=w,mainCanvas.style.height=innerAspect,glCanvas&&(glCanvas.style.width=w,glCanvas.style.height=innerAspect)}else{mainCanvasSize.x=0|min(innerWidth,canvasMaxSize.x),mainCanvasSize.y=0|min(innerHeight,canvasMaxSize.y);const innerAspect=mainCanvasSize.x/mainCanvasSize.y;if(canvasMaxAspect&&innerAspect>canvasMaxAspect){const w=mainCanvasSize.y*canvasMaxAspect|0;mainCanvasSize.x=min(w,canvasMaxSize.x)}else if(innerAspect<canvasMinAspect){const h=mainCanvasSize.x/canvasMinAspect|0;mainCanvasSize.y=min(h,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")}fixedAspect=mainCanvasSize.x*dpr|0,w=mainCanvasSize.y*dpr|0;mainCanvas.width!==fixedAspect||mainCanvas.height!==w?(mainCanvas.width=fixedAspect,mainCanvas.height=w):(mainContext.setTransform(1,0,0,1,0,0),mainContext.globalCompositeOperation="source-over",mainContext.globalAlpha=1,mainContext.filter="none",mainContext.shadowColor="rgba(0,0,0,0)",mainContext.clearRect(0,0,fixedAspect,w)),mainContext.setTransform(dpr,0,0,dpr,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"}}function engineFrameFailed(error){var text=String(error);text!==engineFrameErrorLast&&console.error(error),engineFrameErrorLast=text,inputUpdatePost(),engineScheduleFrame()}function engineScheduleFrame(){if(!engineFrameScheduled){engineFrameScheduled=!0;const next=frameTimeMS=>{if(engineFrameScheduled=!1,!engineManualStep){if(debug)return engineUpdateInternal(frameTimeMS);try{engineUpdateInternal(frameTimeMS)}catch(error){engineFrameFailed(error)}}};"function"==typeof requestAnimationFrame?requestAnimationFrame(next):setTimeout(()=>next(performance.now()),1e3/frameRate)}}function engineStep(frames=1){if(engineManualStep&&engineUpdateInternal)for(let i=frames=min(frames,36e3);0<i&&engineManualStep;--i)engineUpdateInternal(frameTimeLastMS+1e3/frameRate)}function engineObjectsUpdate(){++engineObjectsUpdateCount,engineObjectsCollidePairs.clear(),engineObjectsSort();var fixed=[];engineObjectsCollide=[],engineObjectsCollideStaticLast=[];for(const o of engineObjects)o.collideSolidObjects&&(engineObjectsCollide.push(o),(o.mass?engineObjectsCollideStaticLast:fixed).push(o));for(const o of fixed)engineObjectsCollideStaticLast.push(o);engineObjectsCollideStaticLast.length>=engineCollideGridMin&&(engineCollideGrid={list:engineObjectsCollideStaticLast,built:void 0});try{for(const o of engineObjects)o.parent||o.destroyed||(o.updatePhysics(),engineCollideGrid?.built&&engineCollideGridPlace(engineCollideGrid.built,o))}finally{engineCollideGrid=engineCollideGrid&&void 0}function updateChildObjects(children){if(children.length){var o,start=engineChildStack.length;for(const child of children)engineChildStack.push(child);try{for(let i=start;i<engineChildStack.length;++i)(o=engineChildStack[i]).destroyed||o.updatePass===pass||(o.updatePass=pass,o.updateTransforms(!1),o.update(),o.children.length&&o.updateTransforms(!1),updateChildObjects(o.children))}finally{engineChildStack.length=start}}}const pass=engineObjectsUpdateCount;function updateTopObject(o){o.parent||o.destroyed||o.updatePass===pass||(o.updatePass=pass,o.update(),updateChildObjects(o.children),o.updateTransforms())}for(const o of engineObjects)updateTopObject(o);for(const o of engineObjects)updateTopObject(o);engineObjects.some(o=>o.destroyed)&&(engineObjects=engineObjects.filter(o=>!o.destroyed))}function engineObjectsSort(){for(let i=engineObjects.length;0<--i;)if(engineObjects[i].renderOrder<engineObjects[i-1].renderOrder)return engineObjects.sort((a,b)=>a.renderOrder-b.renderOrder)}function engineObjectsDestroy(immediate=!0){for(const o of engineObjects)o.parent||o.persistent||o.destroy(immediate);engineObjects=engineObjects.filter(o=>!o.destroyed)}function engineObjectsCollect(pos,size,objects=engineObjects,testCenters=!1){var collectedObjects=[];if(pos)if(!size||size instanceof Vector2){var boxSize=size instanceof Vector2?size:vec2();for(const o of objects)o.destroyed||(testCenters?isOverlapping(pos,boxSize,o.pos):o.isOverlapping(pos,boxSize))&&collectedObjects.push(o)}else{var dx,radiusSquared=(size/2)**2;for(const o of objects)o.destroyed||(dx=testCenters?pos.x-o.pos.x:max(abs(pos.x-o.pos.x)-o.size.x/2,0))*dx+(dx=testCenters?pos.y-o.pos.y:max(abs(pos.y-o.pos.y)-o.size.y/2,0))*dx<radiusSquared&&collectedObjects.push(o)}else for(const o of objects)o.destroyed||collectedObjects.push(o);return collectedObjects}function engineObjectsCallback(pos,size,callbackFunction,objects=engineObjects,testCenters=!1){for(const o of engineObjectsCollect(pos,size,objects,testCenters))o.destroyed||callbackFunction(o)}function engineObjectsRaycast(start,end,objects=engineObjects){var hitObjects=[];for(const o of objects)o.collideRaycast&&!o.destroyed&&isIntersecting(start,end,o.pos,o.size)&&hitObjects.push(o);return hitObjects}let debugWatermark=!1,debugKey="",debugKeysAlways=!1,debugTweakables=!1;const debug=!1,debugOverlay=!1,debugGamepads=!1;var debugPointSize=.5;function ASSERT(){}function LOG(){}function debugRect(){}function debugPoly(){}function debugCircle(){}function debugPoint(){}function debugLine(){}function debugOverlap(){}function debugText(){}function debugClear(){}function debugScreenshot(){}function debugShowErrors(){}function setDebugOverlay(){}function debugProtectConstant(o){return Object.freeze(o)}function tweak(){}function tweakButton(){}function tweakDivider(){}function tweakEngineDefaults(){}const editorEdit2DRelease={map:void 0,layer:void 0,hover:void 0,get objects(){return[]},get selection(){return new Set},paint(){return!1},changeObjects(){return!1},strokeEnd(){},strokeCancel(){},bulk(){},undo(){return!1},toJSON(){return""}};class LevelEditor{constructor(){this.paletteTiles=this.use3D=this.tool=void 0,this.keys={},this.buttons=[],this.tools={}}get isOpen(){return!1}get is3D(){return!1}get edit2D(){return editorEdit2DRelease}get edit3D(){}open(){}close(){}addKey(){}addButton(){}addTool(){}onTile(){}onRestart(){}onPlayFrom(){}onOpen(){}onClose(){}onUpdate(){}onDraw(){}onPanel(){}onSave(){return!1}}let levelEditor=new LevelEditor;function setLevelEditor(editor){levelEditor=editor}function debugBox3D(){}function debugSphere3D(){}function debugLine3D(){}function debugPoint3D(){}const PI=Math.PI,abs=Math.abs,floor=Math.floor,ceil=Math.ceil,round=Math.round,min=Math.min,max=Math.max,sign=x=>Math.sign(x),hypot=Math.hypot,log2=x=>Math.log2(x),sin=Math.sin,cos=Math.cos,tan=Math.tan,atan2=Math.atan2;function mod(dividend,divisor=1){return(dividend%divisor+divisor)%divisor}function clamp(value,min=0,max=1){return value<min?min:max<value?max:value}function percent(value,valueA,valueB){return(valueB-=valueA)?clamp((value-valueA)/valueB):0}function lerp(valueA,valueB,percent){return valueA+clamp(percent)*(valueB-valueA)}function percentLerp(value,percentA,percentB,lerpA,lerpB){return lerp(lerpA,lerpB,percent(value,percentA,percentB))}function distanceWrap(valueA,valueB,wrapSize=1){valueA=(valueA-valueB)%wrapSize;return 2*valueA%wrapSize-valueA}function lerpWrap(valueA,valueB,percent,wrapSize=1){return valueA+clamp(percent)*distanceWrap(valueB,valueA,wrapSize)}function distanceAngle(angleA,angleB){return distanceWrap(angleA,angleB,2*PI)}function lerpAngle(angleA,angleB,percent){return lerpWrap(angleA,angleB,percent,2*PI)}function smoothStep(percent){return(percent=clamp(percent))*percent*(3-2*percent)}function isPowerOfTwo(value){return 0<value&&value%1==0&&2**round(log2(value))===value}function nearestPowerOfTwo(value){var power=2**ceil(log2(max(value,1)));return power<value?2*power:power}function isOverlapping(posA,sizeA,posB,sizeB=vec2()){var dx=2*(posA.x-posB.x),posA=2*(posA.y-posB.y),posB=sizeA.x+sizeB.x,sizeA=sizeA.y+sizeB.y;return abs(dx)<posB&&abs(posA)<sizeA}function isIntersecting(start,end,pos,size){var pos=pos.subtract(size.scale(.5)),size=pos.add(size),end=end.subtract(start),pos=start.subtract(pos),start=start.subtract(size),p=[-end.x,end.x,-end.y,end.y],q=[pos.x,-start.x,pos.y,-start.y];let tMin=0,tMax=1;for(let i=4;i--;)if(p[i]){var t=q[i]/p[i];if(p[i]<0){if(t>tMax)return!1;tMin=max(t,tMin)}else{if(t<tMin)return!1;tMax=min(t,tMax)}}else if(q[i]<0)return!1;return!0}function collideCircleCircle(posA,radiusA,posB,radiusB){posA=posA.subtract(posB),posB=radiusA+radiusB,radiusA=posA.length();if(!(posB<=radiusA))return posA.normalize(posB-radiusA)}function collideCircleBox(pos,radius,boxPos,boxSize){var boxSize=boxSize.scale(.5),closest=vec2(clamp(pos.x,boxPos.x-boxSize.x,boxPos.x+boxSize.x),clamp(pos.y,boxPos.y-boxSize.y,boxPos.y+boxSize.y)),closest=pos.subtract(closest),distSq=closest.lengthSquared();return distSq?radius*radius<=distSq?void 0:closest.normalize(radius-distSq**.5):pushOutAxis(closest=pos.subtract(boxPos),boxSize.x-abs(closest.x),boxSize.y-abs(closest.y),radius)}function collideBoxBox(posA,sizeA,posB,sizeB){posA=posA.subtract(posB),posB=(sizeA.x+sizeB.x)/2-abs(posA.x),sizeA=(sizeA.y+sizeB.y)/2-abs(posA.y);if(!(posB<=0||sizeA<=0))return pushOutAxis(posA,posB,sizeA)}function pushOutAxis(d,penX,penY,extra=0){var s=v=>0<=v?1:-1;return penX<=penY?vec2(s(d.x)*(penX+extra),0):vec2(0,s(d.y)*(penY+extra))}function oscillate(frequency=1,amplitude=1,t=time,offset=0,type=0){offset=mod(offset+t*frequency,1);let value;return amplitude/2*((value=1===type?1-2*abs(2*offset-1):2===type?offset<.5?-1:1:3===type?2*offset-1:-cos(2*offset*PI))+1)}function isNumber(n){return"number"==typeof n&&n==n}function isStringLike(s){return null!=s&&"function"==typeof s.toString&&"string"==typeof s.toString()}function isArray(a){return Array.isArray(a)}function lineTest(posStart,posEnd,testFunction,normal){var dx=posEnd.x-posStart.x,posEnd=posEnd.y-posStart.y,totalLength=(dx*dx+posEnd*posEnd)**.5;if(totalLength){var hitPos,hitPosFloor,pos=posStart.floor(),dirX=(dx<0&&pos.x===posStart.x&&--pos.x,posEnd<0&&pos.y===posStart.y&&--pos.y,dx/totalLength),dirY=posEnd/totalLength,stepX=sign(dirX),stepY=sign(dirY),tDeltaX=dirX?abs(1/dirX):1/0,tDeltaY=dirY?abs(1/dirY):1/0,dx=0<stepX?pos.x+1:pos.x,posEnd=0<stepY?pos.y+1:pos.y;let t=0,tX=dirX?(dx-posStart.x)/dirX:1/0,tY=dirY?(posEnd-posStart.y)/dirY:1/0,wasX=tDeltaX<tDeltaY;for(;t<totalLength;){if(testFunction(pos))return(hitPosFloor=(hitPos=vec2(posStart.x+dirX*t,posStart.y+dirY*t)).floor()).x<pos.x?hitPos.x=pos.x:hitPosFloor.x>pos.x&&(hitPos.x=pos.x+1-1e-9),hitPosFloor.y<pos.y?hitPos.y=pos.y:hitPosFloor.y>pos.y&&(hitPos.y=pos.y+1-1e-9),normal&&(wasX?normal.set(-stepX,0):normal.set(0,-stepY)),hitPos;(wasX=tX<tY)?(pos.x+=stepX,t=tX,tX+=tDeltaX):(pos.y+=stepY,t=tY,tY+=tDeltaY)}}}function rand(valueA=1,valueB=0){return valueB+Math.random()*(valueA-valueB)}function randInt(valueA,valueB=0){return floor(rand(valueA,valueB))}function randBool(chance=.5){return rand()<chance}function randSign(){return 2*randInt(2)-1}function randVec2(length=1){return(new Vector2).setAngle(rand(2*PI),length)}function randInCircle(radius=1,minRadius=0){return radius<=0?new Vector2:randVec2(radius*rand((minRadius=clamp(minRadius/radius))*minRadius,1)**.5)}function randColor(colorA=new Color,colorB=new Color(0,0,0,1),linear=!1){return linear?colorA.lerp(colorB,rand()):new Color(rand(colorA.r,colorB.r),rand(colorA.g,colorB.g),rand(colorA.b,colorB.b),rand(colorA.a,colorB.a))}class RandomGenerator{constructor(seed=123456789){this.seed=seed}float(valueA=1,valueB=0){return this.seed=0|this.seed||123456789,this.seed^=this.seed<<13,this.seed^=this.seed>>>17,this.seed^=this.seed<<5,valueB+(valueA-valueB)*noiseHash(this.seed)}int(valueA,valueB=0){return floor(this.float(valueA,valueB))}bool(chance=.5){return this.float()<chance}sign(){return.5<this.float()?1:-1}floatSign(valueA=1,valueB=0){var lo=min(valueA,valueB),valueA=max(valueA,valueB)-lo,valueB=this.float(2*valueA);return valueB<valueA?lo+valueB:valueA-lo-valueB}angle(){return this.float(-PI,PI)}vec2(valueA=1,valueB=0){return vec2(this.float(valueA,valueB),this.float(valueA,valueB))}direction(length=1){return(new Vector2).setAngle(this.float(2*PI),length)}randColor(colorA=new Color,colorB=new Color(0,0,0,1),linear=!1){return linear?colorA.lerp(colorB,this.float()):new Color(this.float(colorA.r,colorB.r),this.float(colorA.g,colorB.g),this.float(colorA.b,colorB.b),this.float(colorA.a,colorB.a))}mutateColor(color,amount=.05,alphaAmount=0){return new Color(color.r+this.float(amount,-amount),color.g+this.float(amount,-amount),color.b+this.float(amount,-amount),color.a+this.float(alphaAmount,-alphaAmount)).clamp()}}function vec2(x=0,y){return new Vector2(x,y??x)}function isVector2(v){return v instanceof Vector2&&v.isValid()}class Vector2{constructor(x=0,y=0){this.x=x,this.y=y}set(x=0,y=0){return this.x=x,this.y=y,this}setFrom(v){return this.set(v.x,v.y)}copy(){return new Vector2(this.x,this.y)}add(v){return new Vector2(this.x+v.x,this.y+v.y)}subtract(v){return new Vector2(this.x-v.x,this.y-v.y)}multiply(v){return new Vector2(this.x*v.x,this.y*v.y)}divide(v){return new Vector2(this.x/v.x,this.y/v.y)}scale(s){return new Vector2(this.x*s,this.y*s)}length(){return this.lengthSquared()**.5}lengthSquared(){return this.x**2+this.y**2}distance(v){return this.distanceSquared(v)**.5}distanceSquared(v){return(this.x-v.x)**2+(this.y-v.y)**2}normalize(length=1){var l=this.length();return l?this.scale(length/l):new Vector2(0,length)}clampLength(length=1){var l=this.length();return length<l?this.scale(length/l):this.copy()}dot(v){return this.x*v.x+this.y*v.y}cross(v){return this.x*v.y-this.y*v.x}reflect(normal,restitution=1){return this.subtract(normal.scale((1+restitution)*this.dot(normal)))}angle(){return atan2(this.x,this.y)}setAngle(angle=0,length=1){return this.x=length*sin(angle),this.y=length*cos(angle),this}rotate(angle){var c=cos(-angle),angle=sin(-angle);return new Vector2(this.x*c-this.y*angle,this.x*angle+this.y*c)}setDirection(direction,length=1){return direction=mod(direction,4),this.x=direction%2?direction-1?-length:length:0,this.y=direction%2?0:direction?-length:length,this}direction(){return abs(this.x)>abs(this.y)?this.x<0?3:1:this.y<0?2:0}abs(){return new Vector2(abs(this.x),abs(this.y))}floor(){return new Vector2(floor(this.x),floor(this.y))}round(){return new Vector2(round(this.x),round(this.y))}snap(grid){return new Vector2(floor(this.x*grid)/grid,floor(this.y*grid)/grid)}mod(divisor=1){return new Vector2(mod(this.x,divisor),mod(this.y,divisor))}area(){return abs(this.x*this.y)}lerp(v,percent){percent=clamp(percent);return new Vector2(v.x*percent+this.x*(1-percent),v.y*percent+this.y*(1-percent))}arrayCheck(arraySize){return 0<=this.x&&0<=this.y&&this.x<arraySize.x&&this.y<arraySize.y}toString(digits=3){return this.isValid()?`(${(this.x<0?"":" ")+this.x.toFixed(digits)},${(this.y<0?"":" ")+this.y.toFixed(digits)} )`:`(${this.x}, ${this.y})`}isValid(){return isNumber(this.x)&&isNumber(this.y)}}function rgb(r,g,b,a){return debug&&(2<r||2<g||2<b)&&colorRangeWarn("rgb(255, 0, 0) is rgb(1, 0, 0)"),new Color(r,g,b,a)}let colorRangeWarned=!1;function colorRangeWarn(example){colorRangeWarned||(colorRangeWarned=!0,console.warn("color values are 0 to 1 here, not 0 to 255 or percents: "+example))}function hsl(h,s,l,a){return debug&&(2<s||2<l)&&colorRangeWarn("hsl(.5, 100, 50) is hsl(.5, 1, .5)"),(new Color).setHSLA(h,s,l,a)}function isColor(c){return c instanceof Color&&c.isValid()}class Color{constructor(r=1,g=1,b=1,a=1){this.r=r,this.g=g,this.b=b,this.a=a}set(r=1,g=1,b=1,a=1){return this.r=r,this.g=g,this.b=b,this.a=a,this}setFrom(c){return this.set(c.r,c.g,c.b,c.a)}setAlpha(a=1){return this.a=a,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(c){return new Color(this.r+c.r,this.g+c.g,this.b+c.b,this.a+c.a)}subtract(c){return new Color(this.r-c.r,this.g-c.g,this.b-c.b,this.a-c.a)}multiply(c){return new Color(this.r*c.r,this.g*c.g,this.b*c.b,this.a*c.a)}divide(c){return new Color(this.r/c.r,this.g/c.g,this.b/c.b,this.a/c.a)}scale(scale,alphaScale=scale){return new Color(this.r*scale,this.g*scale,this.b*scale,this.a*alphaScale)}clamp(){return new Color(clamp(this.r),clamp(this.g),clamp(this.b),clamp(this.a))}lerp(c,percent){percent=clamp(percent);return new Color(c.r*percent+this.r*(1-percent),c.g*percent+this.g*(1-percent),c.b*percent+this.b*(1-percent),c.a*percent+this.a*(1-percent))}setHSLA(h=0,s=0,l=1,a=1){h=mod(h,1),s=clamp(s);var s=(l=clamp(l))<.5?l*(1+s):l+s-l*s,l=2*l-s,f=(p,q,t)=>6*(t=mod(t,1))<1?p+6*(q-p)*t:2*t<1?q:3*t<2?p+(q-p)*(4-6*t):p;return this.r=f(l,s,h+1/3),this.g=f(l,s,h),this.b=f(l,s,h-1/3),this.a=a,this}HSLA(){var d,r=clamp(this.r),g=clamp(this.g),b=clamp(this.b),a=clamp(this.a),maxC=max(r,g,b),minC=min(r,g,b),l=(maxC+minC)/2;let h=0,s=0;return maxC!==minC&&(d=maxC-minC,s=.5<l?d/(2-maxC-minC):d/(maxC+minC),r===maxC?h=(g-b)/d+(g<b?6:0):g===maxC?h=(b-r)/d+2:b===maxC&&(h=(r-g)/d+4)),[h/6,s,l,a]}mutate(amount=.05,alphaAmount=0){return new Color(this.r+rand(amount,-amount),this.g+rand(amount,-amount),this.b+rand(amount,-amount),this.a+rand(alphaAmount,-alphaAmount)).clamp()}toString(useAlpha=!0){var colorHex;return this.isValid()?"#"+(colorHex=c=>((c=round(255*clamp(c)))<16?"0":"")+c.toString(16))(this.r)+colorHex(this.g)+colorHex(this.b)+(useAlpha?colorHex(this.a):""):"#000"}setHex(hex){if(hex.length<6){var fromHex=c=>clamp(parseInt(hex[c],16)/15);this.r=fromHex(1),this.g=fromHex(2),this.b=fromHex(3),this.a=5===hex.length?fromHex(4):1}else{const fromHex=c=>clamp(parseInt(hex.slice(c,c+2),16)/255);this.r=fromHex(1),this.g=fromHex(3),this.b=fromHex(5),this.a=9===hex.length?fromHex(7):1}return this}rgbaInt(){return(255*clamp(this.r)+.5|0)+(255*clamp(this.g)+.5<<8)+(255*clamp(this.b)+.5<<16)+(255*clamp(this.a)+.5<<24)}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));var GRAY=debugProtectConstant(rgb(.5,.5,.5));const RED=debugProtectConstant(rgb(1,0,0));var ORANGE=debugProtectConstant(rgb(1,.5,0));const YELLOW=debugProtectConstant(rgb(1,1,0)),GREEN=debugProtectConstant(rgb(0,1,0));var CYAN=debugProtectConstant(rgb(0,1,1));const BLUE=debugProtectConstant(rgb(0,0,1)),PURPLE=debugProtectConstant(rgb(.5,0,1));var MAGENTA=debugProtectConstant(rgb(1,0,1));class Timer{constructor(timeLeft,useRealTime=!1){this.useRealTime=useRealTime;useRealTime=this.getGlobalTime();this.time=void 0===timeLeft?void 0:useRealTime+timeLeft,this.setTime=timeLeft}set(timeLeft=0){var globalTime=this.getGlobalTime();this.time=globalTime+timeLeft,this.setTime=timeLeft}setUseRealTime(useRealTime=!0){this.useRealTime=useRealTime}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()?0<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 "+(this.get()<0?"before":"after"):"unset"}valueOf(){return this.get()}}function formatTime(t){return(t<=-1?"-":"")+((t=0|abs(t))/60|0)+":"+(t%60<10?"0":"")+t%60}async function fetchJSON(url){var response=await loadFetch(url,"fetchJSON");if(!response.ok)throw new Error(`fetchJSON: could not load ${url}, HTTP ${response.status} `+response.statusText);response=await response.text();let json;try{json=JSON.parse(response)}catch(e){throw new Error(`fetchJSON: ${url} is not JSON`+(/^\s*</.test(response)?", it is a web page, so the path may be wrong":": "+e.message))}return json,json}async function loadFetch(url,who){try{return await fetch(url)}catch(error){var fromDisk="file:"===globalThis.location?.protocol,who=new Error(who+`: could not load ${url}, `+error.message+(fromDisk?", the page was opened from a file, serve it from a local web server":""));throw who.cause=error,who}}function saveText(text,filename="text",type="text/plain"){saveDataURL(URL.createObjectURL(new Blob([text],{type:type})),filename,1e3)}function createCanvasContext(width,height=width,willReadFrequently=!1){return("undefined"!=typeof OffscreenCanvas?new OffscreenCanvas(width,height):Object.assign(document.createElement("canvas"),{width:width,height:height})).getContext("2d",{willReadFrequently:willReadFrequently})}function canvasAvailable(){return"undefined"!=typeof OffscreenCanvas||"undefined"!=typeof document&&document.createElement}function saveCanvas(canvas,filename="screenshot",type="image/png"){var saveCanvas;"undefined"!=typeof OffscreenCanvas&&canvas instanceof OffscreenCanvas?((saveCanvas=document.createElement("canvas")).width=canvas.width,saveCanvas.height=canvas.height,saveCanvas.getContext("2d").drawImage(canvas,0,0),saveDataURL(saveCanvas.toDataURL(type),filename)):saveDataURL(canvas.toDataURL(type),filename)}function saveDataURL(url,filename="download",revokeTime){var link=document.createElement("a");link.download=filename,link.href=url,link.click(),void 0!==revokeTime&&setTimeout(()=>URL.revokeObjectURL(url),revokeTime)}function shareURL(title,url,callback){navigator.share?.({title:title,url:url}).then(()=>callback?.(),()=>{})}function readSaveData(saveName,defaultSaveData){let loadedData={};try{var data=localStorage.getItem(saveName);if(data){try{loadedData=JSON.parse(data)}catch{console.warn("readSaveData: the save "+saveName+" can not be read, using the defaults")}loadedData&&"object"==typeof loadedData&&!isArray(loadedData)||(loadedData={})}}catch{}return{...defaultSaveData,...loadedData}}function writeSaveData(saveName,saveData){let text;try{text=JSON.stringify(saveData)}catch(error){return console.warn("writeSaveData: the save "+saveName+" can not be written as JSON, "+error.message),!1}if(void 0===text)return console.warn("writeSaveData: the save "+saveName+" is nothing as JSON, it is not written"),!1;try{return localStorage.setItem(saveName,text),!0}catch{return console.warn("writeSaveData: the save "+saveName+" could not be written, storage is full or off"),!1}}function noiseHash(i){i=2654435769^(0|i),i=Math.imul(i^i>>>16,2246822507);return i=Math.imul(i^i>>>13,3266489909),((i^=i>>>16)>>>0)/2**32}function noise1D(x){var i=floor(x);return lerp(noiseHash(i),noiseHash(i+1),smoothStep(x-i))}function noise2D(x,y){var ix=floor(x),iy=floor(y),x=smoothStep(x-ix),y=smoothStep(y-iy),h=(a,b)=>noiseHash(a+Math.imul(b,374761393));return lerp(lerp(h(ix,iy),h(ix+1,iy),x),lerp(h(ix,iy+1),h(ix+1,iy+1),x),y)}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,loadingScreen=function(progress){var x=mainContext,w=mainCanvasSize.x,h=mainCanvasSize.y,barWidth=min(.6*w,400),left=(w-barWidth)/2,top=h/2;x.save(),x.fillStyle="#000",x.fillRect(0,0,w,h),x.fillStyle=x.strokeStyle="#fff",x.font="28px "+fontDefault,x.textAlign="center",x.textBaseline="bottom",x.fillText("Loading",w/2,top-16),x.lineWidth=2,x.strokeRect(left,top,barWidth,12),x.fillRect(left,top,barWidth*clamp(progress),12),x.restore()},headlessMode=!1,engineManualStep=!1,engineVariableStep=!1,glEnable=!0,drawScreenSpace=!1,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,touchPinchWheel=!0,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,soundIgnoreSilentSwitch=!1,soundVolume=.3,soundDefaultRange=40,soundDefaultTaper=.7,soundPauseWhenHidden=!0;function setCameraPos(pos){cameraPos=pos.copy()}function setCameraAngle(angle){cameraAngle=angle}function setCameraScale(scale){cameraScale=scale}function setTimeScale(scale){timeScale=max(0,scale)}function setCanvasColorTiles(colorTiles){canvasColorTiles=colorTiles}function setCanvasClearColor(color){canvasClearColor=color.copy()}function setCanvasMaxSize(size){canvasMaxSize=size.copy()}function setCanvasMinAspect(aspect){canvasMinAspect=aspect}function setCanvasMaxAspect(aspect){canvasMaxAspect=aspect}function setCanvasFixedSize(size){canvasFixedSize=size.copy()}function setCanvasPixelated(pixelated){canvasPixelated=pixelated,mainCanvas&&(mainCanvas.style.imageRendering=pixelated?"pixelated":""),glCanvas&&(glCanvas.style.imageRendering=pixelated?"pixelated":"")}function setTilesPixelated(pixelated){tilesPixelated=pixelated}function setCanvasPixelRatio(pixelRatio){canvasPixelRatio=pixelRatio}function getCanvasPixelRatio(){return canvasPixelRatio??(devicePixelRatio||1)}function setFontDefault(font){fontDefault=font}function setShowSplashScreen(show){showSplashScreen=show}function setLoadingScreen(callback){loadingScreen=callback}function setHeadlessMode(headless){headlessMode=headless}function setEngineManualStep(enable=!0){var resume=engineManualStep&&!enable;enable&&!engineManualStep&&(frameTimeBufferMS=-500/frameRate),engineManualStep=enable,resume&&engineUpdateInternal&&(frameTimeLastMS=performance.now(),engineScheduleFrame())}function setEngineVariableStep(enable=!0){engineVariableStep&&!enable&&(timeFixedStart=frame?time+1/frameRate:time,frameFixedStart=frame,timeDelta=1/frameRate,frameTimeBufferMS=engineManualStep?-500/frameRate:0),engineVariableStep=enable}function setDrawScreenSpace(screenSpace){drawScreenSpace=screenSpace}function setGLEnable(enable){enable&&!glCanBeEnabled?console.warn("Can not enable WebGL if it was disabled on start."):(glEnable=enable,glCanvas&&(glCanvas.style.display=enable?"":"none"))}function setGLCircleSides(sides){glCircleSides=sides}function setTileDefaultSize(size){tileDefaultSize=size.copy()}function setTileDefaultPadding(padding){tileDefaultPadding=padding}function setTileDefaultBleed(bleed){tileDefaultBleed=bleed}function setEnablePhysicsSolver(enable){enablePhysicsSolver=enable}function setObjectDefaultMass(mass){objectDefaultMass=mass}function setObjectDefaultDamping(damp){objectDefaultDamping=damp}function setObjectDefaultAngleDamping(damp){objectDefaultAngleDamping=damp}function setObjectDefaultRestitution(restitution){objectDefaultRestitution=restitution}function setObjectDefaultFriction(friction){objectDefaultFriction=friction}function setObjectMaxSpeed(speed){objectMaxSpeed=speed}function setGravity(newGravity){gravity=newGravity.copy()}function setParticleEmitRateScale(scale){particleEmitRateScale=scale}function setGamepadsEnable(enable){gamepadsEnable=enable}function setGamepadDirectionEmulateStick(enable){gamepadDirectionEmulateStick=enable}function setGamepadAxisFilterEnable(enable){gamepadAxisFilterEnable=enable}function setInputWASDEmulateDirection(enable){inputWASDEmulateDirection=enable}function setTouchInputEnable(enable){touchInputEnable=enable}function setTouchGamepadEnable(enable){touchGamepadEnable=enable}function setTouchPinchWheel(enable){touchPinchWheel=enable}function setTouchGamepadPassthrough(passthrough){touchGamepadPassthrough=passthrough}function setTouchGamepadCenterButtonSize(size){touchGamepadCenterButtonSize=size}function setTouchGamepadButtonCount(count){0<(touchGamepadButtonCount=count)&&(touchGamepadRightStick=!1)}function setTouchGamepadLeftStick(enable){(touchGamepadLeftStick=enable)&&(touchGamepadLeftButtonCount=0)}function setTouchGamepadLeftButtonCount(count){0<(touchGamepadLeftButtonCount=count)&&(touchGamepadLeftStick=!1)}function setTouchGamepadRightStick(rightStick){(touchGamepadRightStick=rightStick)&&(touchGamepadButtonCount=0)}function setTouchGamepadAnalog(analog){touchGamepadAnalog=analog}function setTouchGamepadFloating(floating){touchGamepadFloating=floating}function setTouchGamepadSize(size){touchGamepadSize=size}function setTouchGamepadAlpha(alpha){touchGamepadAlpha=alpha}function setTouchGamepadDisplayTime(time){touchGamepadDisplayTime=time}function setTouchGamepadVibration(ms){touchGamepadVibration=ms}function setVibrateEnable(enable){vibrateEnable=enable}function setSoundEnable(enable){soundEnable=enable}function setSoundIgnoreSilentSwitch(ignore){soundIgnoreSilentSwitch=ignore,audioSetSession()}function setSoundVolume(volume){soundVolume=volume,audioUpdateVolume()}function setSoundDefaultRange(range){soundDefaultRange=range}function setSoundDefaultTaper(taper){soundDefaultTaper=taper}function setSoundPauseWhenHidden(pause){soundPauseWhenHidden=pause}function setDebugWatermark(show){debugWatermark=show}function setDebugKey(key){debugKey=key}function setDebugKeysAlways(enable=!0){debugKeysAlways=enable}function setDebugTweakables(show=!0){debugTweakables=show}class EngineObject{constructor(pos=vec2(),size=vec2(1),tileInfo,angle=0,color=WHITE,renderOrder=0){this.pos=pos.copy(),this.size=size.copy(),this.drawSize=void 0,this.tileInfo=tileInfo,this.angle=angle,this.color=color.copy(),this.additiveColor=void 0,this.shader=void 0,this.castShadow=!0,this.castBackgroundShadow=!1,this.emissive=0,this.mirror=!1,this.destroyed=!1,this.updatePass=0,this.mass=objectDefaultMass,this.damping=objectDefaultDamping,this.angleDamping=objectDefaultAngleDamping,this.restitution=objectDefaultRestitution,this.friction=objectDefaultFriction,this.gravityScale=1,this.renderOrder=renderOrder,this.velocity=vec2(),this.angleVelocity=0,this.spawnTime=time,this.children=[],this.clampSpeed=!0,this.groundObject=void 0,this.parent=void 0,this.localPos=void 0,this.localAngle=0,this.collideLevel=!1,this.collideSolidObjects=!1,this.isSolid=!1,this.collideRaycast=!1,this.persistent=!1,engineObjects.push(this)}updateTransforms(updateChildren=!0){var mirror,pp,lx,lp,c,s,parent=this.parent;if(parent&&(mirror=parent.getMirrorSign(),lp=this.localPos,pp=parent.pos,lx=lp.x*mirror,lp=lp.y,(parent=parent.angle)?(c=cos(-parent),s=sin(-parent),this.pos.set(lx*c-lp*s+pp.x,lx*s+lp*c+pp.y)):this.pos.set(lx+pp.x,lp+pp.y),this.angle=mirror*this.localAngle+parent),updateChildren)for(const child of this.children)child.updateTransforms()}updatePhysics(){if(!this.destroyed){var solve=enablePhysicsSolver&&this.mass,oldPos=solve&&(this.collideSolidObjects||this.collideLevel)?this.pos.copy():void 0;if(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.clampSpeed&&enablePhysicsSolver&&(this.collideSolidObjects||this.collideLevel&&this.mass)&&(this.velocity.x=clamp(this.velocity.x,-objectMaxSpeed,objectMaxSpeed),this.velocity.y=clamp(this.velocity.y,-objectMaxSpeed,objectMaxSpeed)),this.pos.x+=this.velocity.x,this.pos.y+=this.velocity.y,this.angle+=this.angleVelocity*=this.angleDamping,solve){var gravityY=this.gravityScale<0?-gravity.y:gravity.y,wasFalling=this.velocity.y<0&&gravityY<0||0<this.velocity.y&&0<gravityY;if(this.groundObject&&(solve=max(this.friction,this.groundObject.friction),groundSpeed=this.groundObject.velocity.x,this.velocity.x=groundSpeed+(this.velocity.x-groundSpeed)*solve,this.groundObject=void 0),this.collideSolidObjects&&this.size.x&&this.size.y)for(const o of engineCollideGrid?engineCollideGridWalk(this):engineObjectsCollideStaticLast)if(!o.destroyed&&!o.parent&&o!==this&&o.size.x&&o.size.y&&(this.isSolid||o.isSolid)&&this.isOverlappingObject(o)){var answer=engineObjectsCollidePairAnswer(o,this);if(!1!==answer){if(!answer){var answer=this.collideWithObject(o),collide2=o.collideWithObject(this);if(engineCollideGrid?.built&&engineCollideGridPlace(engineCollideGrid.built,o),!answer||!collide2){engineObjectsCollidePairAdd(this,o);continue}}answer=isOverlapping(oldPos,this.size,o.pos,o.size);if(!answer||o.mass&&!o.groundObject)if(answer){collide2=oldPos.subtract(o.pos),answer=collide2.length(),answer=answer<.001?vec2(0,.001):collide2.scale(.001/answer);this.velocity=this.velocity.add(answer),o.mass&&(o.velocity=o.velocity.subtract(answer)),engineObjectsCollidePairAdd(this,o)}else{var elastic1,answer=this.size.add(o.size),smallStepUp=2*(0<gravityY?o.pos.y-oldPos.y:oldPos.y-o.pos.y)>answer.y-abs(gravityY),isBlockedX=2*abs(oldPos.y-o.pos.y)<answer.y,isBlockedY=2*abs(oldPos.x-o.pos.x)<answer.x,restitution=max(this.restitution,o.restitution);if(!smallStepUp&&!isBlockedY&&isBlockedX||(this.pos.y=o.pos.y+(answer.y/2+.001)*sign(oldPos.y-o.pos.y),o.groundObject&&wasFalling||!o.mass?(wasFalling&&(this.groundObject=o),this.velocity.y=o.velocity.y-(this.velocity.y-o.velocity.y)*restitution):(isBlockedY=(this.mass*this.velocity.y+o.mass*o.velocity.y)/(this.mass+o.mass),elastic0=this.velocity.y*(this.mass-o.mass)/(this.mass+o.mass)+2*o.velocity.y*o.mass/(this.mass+o.mass),elastic1=o.velocity.y*(o.mass-this.mass)/(this.mass+o.mass)+2*this.velocity.y*this.mass/(this.mass+o.mass),this.velocity.y=lerp(isBlockedY,elastic0,restitution),o.velocity.y=lerp(isBlockedY,elastic1,restitution))),!smallStepUp&&isBlockedX)if(this.pos.x=o.pos.x+(answer.x/2+.001)*sign(oldPos.x-o.pos.x),o.mass){const inelastic=(this.mass*this.velocity.x+o.mass*o.velocity.x)/(this.mass+o.mass),elastic0=this.velocity.x*(this.mass-o.mass)/(this.mass+o.mass)+2*o.velocity.x*o.mass/(this.mass+o.mass),elastic1=o.velocity.x*(o.mass-this.mass)/(this.mass+o.mass)+2*this.velocity.x*this.mass/(this.mass+o.mass);this.velocity.x=lerp(inelastic,elastic0,restitution),o.velocity.x=lerp(inelastic,elastic1,restitution)}else this.velocity.x=o.velocity.x-(this.velocity.x-o.velocity.x)*restitution;engineObjectsCollidePairAdd(this,o,!0)}else{var elastic0=collideBoxBox(this.pos,this.size,o.pos,o.size);if(elastic0){this.pos.x+=elastic0.x+.001*sign(elastic0.x),this.pos.y+=elastic0.y+.001*sign(elastic0.y);const restitution=max(this.restitution,o.restitution);if(elastic0.x){isBlockedY=this.velocity.x-o.velocity.x;isBlockedY*elastic0.x<0&&(this.velocity.x=o.velocity.x-isBlockedY*restitution)}else{const v=this.velocity.y-o.velocity.y;v*elastic0.y<0&&(this.velocity.y=o.velocity.y-v*restitution),elastic0.y*gravityY<0&&(this.groundObject=o)}}engineObjectsCollidePairAdd(this,o)}}}if(this.collideLevel){var groundSpeed=tileCollisionTest(this.pos,this.size,this);if(groundSpeed&&!tileCollisionTest(oldPos,this.size,this)){const isBlockedX=tileCollisionTest(vec2(this.pos.x,oldPos.y),this.size,this),isBlockedY=tileCollisionTest(vec2(oldPos.x,this.pos.y),this.size,this),restitution=max(this.restitution,groundSpeed.restitution);if(isBlockedX){const epsilon=.001;solve=0<gravityY?ceil(oldPos.y+this.size.y/2-1)-this.size.y/2-.001:floor(oldPos.y-this.size.y/2+1)+this.size.y/2+.001;if(abs(solve-this.pos.y)<.1&&!tileCollisionTest(vec2(this.pos.x,solve),this.size,this))return void(this.pos.y=solve);var solve=groundSpeed.pos.x,offsetX=this.size.x/2+1e-4,movingLeft=this.pos.x<oldPos.x,solve=solve+(movingLeft?floor(oldPos.x-solve-this.size.x/2)+offsetX:ceil(oldPos.x-solve+this.size.x/2)-offsetX),offsetX=(movingLeft?min:max)(solve,oldPos.x);this.pos.x=tileCollisionTest(vec2(offsetX,oldPos.y),this.size,this)?oldPos.x:offsetX,this.velocity.x*=-restitution}if(isBlockedY||!isBlockedX){const epsilon=1e-4;movingLeft=this.size.y/2+1e-4,solve=groundSpeed.pos.y;const y=solve+(this.pos.y<oldPos.y?floor(oldPos.y-solve-this.size.y/2)+movingLeft:ceil(oldPos.y-solve+this.size.y/2)-movingLeft);offsetX=this.pos.y!==oldPos.y&&!tileCollisionTest(vec2(this.pos.x,y),this.size,this);this.pos.y=offsetX?y:oldPos.y,this.groundObject=wasFalling?groundSpeed:void 0,this.velocity.y*=-restitution}}}}}}update(){}render(){drawTile(this.pos,this.drawSize||this.size,this.tileInfo,this.color,this.angle,this.mirror,this.additiveColor,glEnable,!1)}renderLight(){}renderShadow(){this.render()}renderEmissive(){this.render()}destroy(immediate=!1){if(!this.destroyed){this.destroyed=!0,this.parent?.removeChild(this);for(const child of this.children)child.parent=void 0,child.destroy(immediate)}}localToWorld(pos){return this.pos.add(pos.rotate(this.angle))}worldToLocal(pos){return pos.subtract(this.pos).rotate(-this.angle)}localToWorldVector(vec){return vec.rotate(this.angle)}worldToLocalVector(vec){return vec.rotate(-this.angle)}collideWithTile(tileData,pos){return 0<tileData}collideWithObject(object,push){return!0}getUp(scale=1){return vec2().setAngle(this.angle,scale)}getRight(scale=1){return vec2().setAngle(this.angle+PI/2,scale)}getAliveTime(){return time-this.spawnTime}getSpeed(){return this.velocity.length()}applyAcceleration(acceleration){this.mass&&(this.velocity=this.velocity.add(acceleration))}applyAngularAcceleration(acceleration){this.mass&&(this.angleVelocity+=acceleration)}applyForce(force){this.mass&&this.applyAcceleration(force.scale(1/this.mass))}getMirrorSign(){return this.mirror?-1:1}addChild(child,localPos=vec2(),localAngle=0){if(!this.destroyed){for(let p=this;p;p=p.parent);this.children.push(child),child.parent=this,child.localPos=localPos.copy(),child.localAngle=localAngle,child.updateTransforms()}return child}attach(child){child.parent?.removeChild(child);var mirror=this.getMirrorSign(),local=this.worldToLocal(child.pos);return local.x*=mirror,this.addChild(child,local,mirror*(child.angle-this.angle))}removeChild(child){var i=this.children.indexOf(child);i<0||(this.children.splice(i,1),child.parent=child.localPos=void 0)}isOverlappingObject(object){return this.isOverlapping(object.pos,object.size)}isOverlapping(pos,size=vec2()){return isOverlapping(this.pos,this.size,pos,size)}setCollision(collideSolidObjects=!0,isSolid=!0,collideLevel=!0,collideRaycast=!0){this.collideSolidObjects=collideSolidObjects,this.isSolid=isSolid,this.collideLevel=collideLevel,this.collideRaycast=collideRaycast}toString(){let text="type = "+this.constructor.name;return(this.pos.x||this.pos.y)&&(text+="\npos = "+this.pos),(this.velocity.x||this.velocity.y)&&(text+="\nvelocity = "+this.velocity),(this.size.x||this.size.y)&&(text+="\nsize = "+this.size),this.angle&&(text+="\nangle = "+this.angle.toFixed(3)),text+="\ncolor = "+this.color}renderDebugInfo(){var hasPhysics,size;debug&&((hasPhysics=this.collideLevel||this.collideSolidObjects||this.isSolid)||this.parent)&&(size=vec2(max(this.size.x,.2),max(this.size.y,.2)),rgb(this.collideLevel?1:0,this.collideSolidObjects?1:0,this.isSolid?1:0,.5),this.pos,hasPhysics||this.angle,this.parent&&(this.pos,size.scale(.8),rgb(1,1,1,.5),this.angle),this.parent)&&(this.pos,this.parent.pos,rgb(1,1,1,.5))}}let mainCanvas,mainContext,drawContext,workCanvas,workContext,workReadCanvas,workReadContext,backgroundCanvas,mainCanvasSize=vec2(),textureInfos=[],drawCount=0,primitiveCount=0;function isWhite(c){return 1<=c.r&&1<=c.g&&1<=c.b}function isBlack(c){return c.r<=0&&c.g<=0&&c.b<=0&&c.a<=0}function tile(index=0,size=tileDefaultSize,texture=0,padding=tileDefaultPadding,bleed=tileDefaultBleed){"number"==typeof size&&(size=new Vector2(size,size));texture="number"==typeof texture?textureInfos[texture]:texture;if(!texture?.size.x)return new TileInfo(new Vector2,headlessMode?size.copy():new Vector2,texture,padding,bleed);var sizePaddedX=size.x+2*padding,sizePaddedY=size.y+2*padding;let x,y;y="number"==typeof index?(cols=texture.size.x/sizePaddedX|0,x=index%cols,index/cols|0):(x=index.x,index.y);var cols=new Vector2(x*sizePaddedX+padding,y*sizePaddedY+padding);return new TileInfo(cols,size,texture,padding,bleed)}class TileInfo{constructor(pos=vec2(),size=tileDefaultSize,textureInfo=textureInfos[0],padding=tileDefaultPadding,bleed=tileDefaultBleed,columns=0){this.pos=pos.copy(),this.size=size.copy(),this.padding=padding,this.textureInfo=textureInfo,this.tiles=void 0,this.bleed=bleed,this.columns=columns}offset(offset){return new TileInfo(this.pos.add(offset),this.size,this.textureInfo,this.padding,this.bleed,this.columns)}frame(frame){var w=this.size.x+2*this.padding,h=this.size.y+2*this.padding,w=(this.columns?frame%this.columns:frame)*w,frame=(this.columns?frame/this.columns|0:0)*h;return this.offset(new Vector2(w,frame))}setColumns(columns=0){return this.columns=columns,this}index(index){return tile(index,this.size,this.textureInfo,this.padding,this.bleed).setColumns(this.columns)}setFullImage(textureInfo=this.textureInfo){return this.textureInfo=textureInfo,this.pos=new Vector2,this.size=textureInfo.size.copy(),this.bleed=this.padding=this.columns=0,this}}class TextureInfo{constructor(image,useWebGL=!0,wrap=!1,pixelated){this.image=image,this.size=image?vec2(image.width,image.height):vec2(),this.sizeInverse=image?vec2(1/image.width,1/image.height):vec2(),this.glTexture=void 0,this.wrap=wrap,this.pixelated=pixelated,useWebGL&&this.createWebGLTexture()}createWebGLTexture(){var image=this.image;image?.width&&(this.size=vec2(image.width,image.height),this.sizeInverse=vec2(1/image.width,1/image.height)),image=this,headlessMode||(glTextureInfos.add(image),glContext&&(image.glTexture?glSetTextureData(image.glTexture,image.image,image.pixelated):image.glTexture=glCreateTexture(image.image,image.wrap,image.pixelated)))}destroyWebGLTexture(){var textureInfo,glTexture;textureInfo=this,headlessMode||(glTextureInfos.delete(textureInfo),glTexture=textureInfo.glTexture,textureInfo.glTexture=void 0,glDeleteTexture(glTexture))}hasWebGL(){return!!this.glTexture}setWrap(wrap=!0){this.wrap=wrap,glSetTextureWrap(this.glTexture,wrap)}setPixelated(pixelated){this.pixelated=pixelated,this.glTexture&&(this.destroyWebGLTexture(),this.createWebGLTexture())}}class SpriteAnimation{constructor(tileInfo,frameCount,frameTime=.1){this.firstTile=tileInfo,this.frameCount=frameCount,this.frameTime=frameTime,this.speed=1,this.mode="loop",this.startTime=time,this.heldFrame=void 0,this.onEnd=void 0}loop(){return this.restart("loop")}play(onEnd){return this.restart("once"),this.onEnd=onEnd,this}pingPong(){return this.restart("pingPong")}stop(){return this.heldFrame=this.frame,this}restart(mode=this.mode){return this.mode=mode,this.startTime=time,this.heldFrame=void 0,this.onEnd=void 0,this}get elapsedFrames(){return(time-this.startTime)*this.speed/this.frameTime}get frame(){var n,f,k;return this.isDone,void 0!==this.heldFrame?this.heldFrame:(n=this.frameCount,f=floor(this.elapsedFrames),"once"==this.mode?clamp(this.speed<0?n-1+ceil(this.elapsedFrames):f,0,n-1):"loop"==this.mode?mod(f,n):(k=mod(f,f=max(2*n-2,1)))<n?k:f-k)}get tileInfo(){return this.firstTile.frame(this.frame)}get isDone(){var onEnd,done="once"==this.mode&&void 0===this.heldFrame&&abs(this.elapsedFrames)>=this.frameCount;return done&&this.onEnd&&(onEnd=this.onEnd,this.onEnd=void 0,onEnd()),done}}class SpriteAnimator{constructor(clips){var names=Object.keys(clips);this.clips=clips,this.name=names[0]}set(name,onEnd){return name==this.name&&!this.clip.isDone||(this.name=name,this.clip.restart(),this.clip.onEnd=onEnd),this}get clip(){return this.clips[this.name]}get settledClip(){for(let i=4,clip;i--&&clip!==this.clip;)(clip=this.clip).isDone;return this.clip}get tileInfo(){return this.settledClip.tileInfo}get frame(){return this.settledClip.frame}get isDone(){return this.settledClip.isDone}}class Shader{constructor(fragmentCode){this.fragmentCode=String(fragmentCode),this.program=void 0,this.program3D=void 0,glShaderObjects.push(this)}dispose(){var shader=this,i=glShaderObjects.indexOf(shader);if(i<0||glShaderObjects.splice(i,1),glContext&&!headlessMode){glBatchShader===shader&&glFlush();for(const program of[shader.program,shader.program3D])program&&glContext.deleteProgram(program)}shader.program=shader.program3D=void 0}}function drawTile(pos,size=vec2(1),tileInfo,color=WHITE,angle=0,mirror,additiveColor,useWebGL=glEnable,screenSpace=drawScreenSpace,context){if(!headlessMode||context){const textureInfo=tileInfo?.textureInfo;if(!textureInfo||tileInfo.size.x&&tileInfo.size.y&&textureInfo.size.x){const bleed=tileInfo?.bleed??0;if(useWebGL&&glEnable){if(screenSpace){if(glSkipScreenSpace)return;[pos,size,angle]=screenToWorldTransform(pos,size,angle)}var x,y,w,h,bleedX;textureInfo?(useWebGL=textureInfo.sizeInverse,x=tileInfo.pos.x*useWebGL.x,y=tileInfo.pos.y*useWebGL.y,w=tileInfo.size.x*useWebGL.x,h=tileInfo.size.y*useWebGL.y,glSetTexture(textureInfo.glTexture),bleed?(bleedX=useWebGL.x*bleed,useWebGL=useWebGL.y*bleed,glDraw(pos.x,pos.y,mirror?-size.x:size.x,size.y,angle,x+bleedX,y+useWebGL,x-bleedX+w,y-useWebGL+h,color.rgbaInt(),additiveColor&&additiveColor.rgbaInt())):glDraw(pos.x,pos.y,mirror?-size.x:size.x,size.y,angle,x,y,x+w,y+h,color.rgbaInt(),additiveColor&&additiveColor.rgbaInt())):(bleedX=additiveColor?color.add(additiveColor):color,glDrawUntextured(pos.x,pos.y,size.x,size.y,angle,bleedX.rgbaInt()))}else++drawCount,++primitiveCount,drawCanvas2D(pos,size,angle,mirror,context=>{var y,w,h,x;textureInfo?(context.scale(1,-1),void 0!==textureInfo.pixelated&&(context.imageSmoothingEnabled=!textureInfo.pixelated),x=tileInfo.pos.x,y=tileInfo.pos.y,w=tileInfo.size.x,h=tileInfo.size.y,function(context,image,sx,sy,sWidth,sHeight,dx,dy,dWidth,dHeight,color,additiveColor,bleed){var sx2=bleed,sy2=bleed,sWidth2=(sWidth=max(1,0|sWidth),sHeight=max(1,0|sHeight),sWidth-2*bleed),bleed=sHeight-2*bleed;!canvasColorTiles||isWhite(color)&&(!additiveColor||isBlack(additiveColor))?(context.globalAlpha=color.a,context.drawImage(image,sx+sx2,sy+sy2,sWidth2,bleed,dx,dy,dWidth,dHeight),context.globalAlpha=1):(workReadCanvas.width!==sWidth||workReadCanvas.height!==sHeight?(workReadCanvas.width=sWidth,workReadCanvas.height=sHeight):workReadContext.clearRect(0,0,sWidth,sHeight),workReadContext.drawImage(image,0|sx,0|sy,sWidth,sHeight,0,0,sWidth,sHeight),image=workReadContext.getImageData(0,0,sWidth,sHeight),sx=tintImageData(image.data,color,additiveColor),workReadContext.putImageData(image,0,0),sx||(context.globalAlpha=color.a),context.drawImage(workReadCanvas,sx2,sy2,sWidth2,bleed,dx,dy,dWidth,dHeight),sx||(context.globalAlpha=1))}(context,textureInfo.image,x,y,w,h,-.5,-.5,1,1,color,additiveColor,bleed)):(x=additiveColor?color.add(additiveColor):color,context.fillStyle=x.toString(),context.fillRect(-.5,-.5,1,1))},screenSpace,context)}}}function drawRect(pos,size,color,angle,useWebGL,screenSpace,context){drawTile(pos,size,void 0,color,angle,!1,void 0,useWebGL,screenSpace,context)}function drawRectGradient(pos,size=vec2(1),colorTop=WHITE,colorBottom=CLEAR_WHITE,angle=0,useWebGL=glEnable,screenSpace=drawScreenSpace,context){if(!headlessMode||context)if(useWebGL&&glEnable){if(screenSpace){if(glSkipScreenSpace)return;[pos,size,angle]=screenToWorldTransform(pos,size,angle)}var points=[],colors=[],halfSizeX=size.x/2,halfSizeY=size.y/2,colorTopInt=colorTop.rgbaInt(),colorBottomInt=colorBottom.rgbaInt(),c=cos(-angle),s=sin(-angle);for(let i=4;i--;){var x=1&i?halfSizeX:-halfSizeX,y=2&i?halfSizeY:-halfSizeY,color=2&i?colorTopInt:colorBottomInt;points.push(vec2(pos.x+(x*c-y*s),pos.y+(x*s+y*c))),colors.push(color)}glDrawColoredPoints(points,colors)}else++drawCount,++primitiveCount,drawCanvas2D(pos,size,angle,!1,context=>{var gradient=context.createLinearGradient(0,.5,0,-.5);gradient.addColorStop(0,colorTop.toString()),gradient.addColorStop(1,colorBottom.toString()),context.fillStyle=gradient,context.fillRect(-.5,-.5,1,1)},screenSpace,context)}function drawTextureWrapped(pos,size,wrapCount,texture=0,color=WHITE,angle=0,additiveColor,useWebGL=glEnable,screenSpace=drawScreenSpace,context){if(!headlessMode||context){texture="number"==typeof texture?textureInfos[texture]:texture;if(useWebGL&&glEnable){if(screenSpace){if(glSkipScreenSpace)return;[pos,size,angle]=screenToWorldTransform(pos,size,angle)}glSetTexture(texture.glTexture),glDraw(pos.x,pos.y,size.x,size.y,angle,0,0,wrapCount.x,wrapCount.y,color.rgbaInt(),additiveColor&&additiveColor.rgbaInt())}else{++drawCount,++primitiveCount,screenSpace||(pos=worldToScreen(pos),size=size.scale(cameraScale),angle-=cameraAngle);useWebGL=!canvasColorTiles||isWhite(color)&&(!additiveColor||isBlack(additiveColor)),screenSpace=!useWebGL&&additiveColor&&!isBlack(additiveColor);let source=texture.image;if(!useWebGL){var kept,useWebGL=color.r+","+color.g+","+color.b+","+color.a+(additiveColor?","+additiveColor.r+","+additiveColor.g+","+additiveColor.b+","+additiveColor.a:"");let baked=drawTextureWrappedBakes.get(source);(baked?.key!==useWebGL||"getContext"in source)&&(kept=(kept=baked?.context)?.canvas.width===(0|source.width)&&kept?.canvas.height===(0|source.height)?kept:createCanvasContext(0|source.width,0|source.height,!0),function(image,color,additiveColor,context=workReadContext){var w=0|image.width,h=0|image.height,image=(context.canvas.width=w,context.canvas.height=h,context.drawImage(image,0,0),context.getImageData(0,0,w,h));tintImageData(image.data,color,additiveColor),context.putImageData(image,0,0),context.canvas}(source,color,additiveColor,kept),drawTextureWrappedBakes.set(source,baked={key:useWebGL,context:kept})),source=baked.context.canvas}(context=context||drawContext).save(),void 0!==texture.pixelated&&(context.imageSmoothingEnabled=!texture.pixelated),context.translate(pos.x+.5,pos.y+.5),context.rotate(angle),context.globalAlpha=screenSpace?1:color.a;additiveColor=context.createPattern(source,"repeat"),useWebGL=(new DOMMatrix).translate(-size.x/2,-size.y/2).scale(size.x/(wrapCount.x*source.width),size.y/(wrapCount.y*source.height));additiveColor.setTransform(useWebGL),context.fillStyle=additiveColor,context.fillRect(-size.x/2,-size.y/2,size.x,size.y),context.globalAlpha=1,context.restore()}}}function drawLineList(points,width=.1,color=WHITE,wrap=!1,pos=vec2(),angle=0,useWebGL=glEnable,screenSpace=drawScreenSpace,context){if(!headlessMode||context)if(useWebGL&&glEnable){let sx=1,sy=1;if(screenSpace){if(glSkipScreenSpace)return;[pos,useWebGL,angle]=screenToWorldTransform(pos,vec2(1),angle),sx=useWebGL.x,sy=useWebGL.y}glDrawOutlineTransform(points,color.rgbaInt(),width,pos.x,pos.y,sx,sy,angle,wrap)}else++drawCount,++primitiveCount,drawCanvas2D(pos,vec2(1),angle,!1,context=>{if(width){context.strokeStyle=color.toString(),context.lineWidth=abs(width),context.beginPath();for(let i=0;i<points.length;++i){var point=points[i];context.lineTo(point.x,point.y)}wrap&&context.closePath(),context.stroke()}},screenSpace,context)}function drawLine(posA,posB,width=.1,color=WHITE,pos=vec2(),angle=0,useWebGL=glEnable,screenSpace=drawScreenSpace,context){posB=vec2((posB.x-posA.x)/2,(posB.y-posA.y)/2),width=vec2(width,2*posB.length()),posA=posA.add(posB);pos=pos.add(angle?posA.rotate(screenSpace?-angle:angle):posA),screenSpace&&(posB.y*=-1),drawRect(pos,width,color,angle+=posB.angle(),useWebGL,screenSpace,context)}function drawRegularPoly(pos,size=vec2(1),sides=3,color=WHITE,lineWidth=0,lineColor=BLACK,angle=0,useWebGL=glEnable,screenSpace=drawScreenSpace,context){var points=drawRegularPolyPoints,pool=drawRegularPolyPool,sizeX=(points.length=0,size.x/2),sizeY=size.y/2;for(let i=sides;0<i--;){var a=i/sides*PI*2,point=pool[points.length]||=vec2();point.x=sin(a)*sizeX,point.y=cos(a)*sizeY,points.push(point)}drawPoly(points,color,lineWidth,lineColor,pos,angle,useWebGL,screenSpace,context)}const drawRegularPolyPoints=[],drawRegularPolyPool=[];function drawPoly(points,color=WHITE,lineWidth=0,lineColor=BLACK,pos=vec2(),angle=0,useWebGL=glEnable,screenSpace=drawScreenSpace,context=void 0){if(!headlessMode||context)if(useWebGL&&glEnable){let sx=1,sy=1;if(screenSpace){if(glSkipScreenSpace)return;[pos,useWebGL,angle]=screenToWorldTransform(pos,vec2(1),angle),sx=useWebGL.x,sy=useWebGL.y}glDrawPointsTransform(points,color.rgbaInt(),pos.x,pos.y,sx,sy,angle),0<lineWidth&&glDrawOutlineTransform(points,lineColor.rgbaInt(),lineWidth,pos.x,pos.y,sx,sy,angle)}else++drawCount,++primitiveCount,drawCanvas2D(pos,vec2(1),angle,!1,context=>{context.fillStyle=color.toString(),context.beginPath();for(const point of points)context.lineTo(point.x,point.y);context.closePath(),context.fill(),0<lineWidth&&(context.strokeStyle=lineColor.toString(),context.lineWidth=lineWidth,context.stroke())},screenSpace,context)}const drawEllipseRings=new Map;function drawEllipse(pos,size=vec2(1),color=WHITE,angle=0,lineWidth=0,lineColor=BLACK,useWebGL=glEnable,screenSpace=drawScreenSpace,context){if(!headlessMode||context)if(lineWidth=clamp(lineWidth,0,min(abs(size.x),abs(size.y))),useWebGL&&glEnable){var sides=glCircleSides;if(0<lineWidth)drawRegularPoly(pos,size,sides,color,lineWidth,lineColor,angle,useWebGL,screenSpace,context);else{let ring=drawEllipseRings.get(sides);if(!ring){var points=[];for(let i=sides;0<i--;){var a=i/sides*PI*2;points.push(vec2(sin(a),cos(a)))}drawEllipseRings.set(sides,ring=glPolyStrip(points))}if(screenSpace){if(glSkipScreenSpace)return;[pos,size,angle]=screenToWorldTransform(pos,size,angle)}glDrawPointsTransform(ring,color.rgbaInt(),pos.x,pos.y,size.x/2,size.y/2,angle,!1)}}else++drawCount,++primitiveCount,drawCanvas2D(pos,vec2(1),angle,!1,context=>{context.fillStyle=color.toString(),context.beginPath(),context.ellipse(0,0,abs(size.x)/2,abs(size.y)/2,0,0,9),context.fill(),0<lineWidth&&(context.strokeStyle=lineColor.toString(),context.lineWidth=lineWidth,context.stroke())},screenSpace,context)}function drawCircle(pos,size=1,color=WHITE,lineWidth=0,lineColor=BLACK,useWebGL=glEnable,screenSpace=drawScreenSpace,context){drawEllipse(pos,vec2(size),color,0,lineWidth,lineColor,useWebGL,screenSpace,context)}let drawEllipseGradientOffset=0;function drawEllipseGradient(pos,size=vec2(1),colorInner=WHITE,colorOuter=CLEAR_WHITE,angle=0,useWebGL=glEnable,screenSpace=drawScreenSpace,context){if(!headlessMode||context)if(useWebGL&&glEnable){if(screenSpace){if(glSkipScreenSpace)return;[pos,size,angle]=screenToWorldTransform(pos,size,angle)}var sides=glCircleSides;const radiusX=size.x/2,radiusY=size.y/2;var innerInt=colorInner.rgbaInt(),outerInt=colorOuter.rgbaInt(),offset=drawEllipseGradientOffset++;const c=cos(-angle),s=sin(-angle);var rim=a=>{var lx=sin(a)*radiusX,a=cos(a)*radiusY;return vec2(pos.x+lx*c-a*s,pos.y+lx*s+a*c)},points=[rim(offset%sides/sides*PI*2)],colors=[outerInt];for(let i=sides;0<i--;){var a=(i+offset)%sides/sides*PI*2;points.push(pos),colors.push(innerInt),points.push(rim(a)),colors.push(outerInt)}glDrawColoredPoints(points,colors)}else++drawCount,++primitiveCount,drawCanvas2D(pos,size,angle,!1,context=>{var gradient=context.createRadialGradient(0,0,0,0,0,.5);gradient.addColorStop(0,colorInner.toString()),gradient.addColorStop(1,colorOuter.toString()),context.fillStyle=gradient,context.beginPath(),context.ellipse(0,0,.5,.5,0,0,9),context.fill()},screenSpace,context)}function drawCircleGradient(pos,size=1,colorInner=WHITE,colorOuter=CLEAR_WHITE,useWebGL=glEnable,screenSpace=drawScreenSpace,context){drawEllipseGradient(pos,vec2(size),colorInner,colorOuter,0,useWebGL,screenSpace,context)}function drawCanvas2D(pos,size,angle=0,mirror=!1,drawFunction,screenSpace=drawScreenSpace,context=drawContext){headlessMode&&!context||(screenSpace||(pos=worldToScreen(pos),size=size.scale(cameraScale),angle-=cameraAngle),context.save(),context.translate(pos.x+.5,pos.y+.5),context.rotate(angle),context.scale(mirror?-size.x:size.x,-size.y),drawFunction(context),context.restore())}function drawText(text,pos,size=1,color=WHITE,lineWidth=0,lineColor=BLACK,textAlign="center",font=fontDefault,fontStyle="",maxWidth,angle=0,context=drawContext){pos=worldToScreen(pos),size*=cameraScale,lineWidth*=cameraScale,void 0!==maxWidth&&(maxWidth*=cameraScale),drawTextScreen(text,pos,size,color,lineWidth,lineColor,textAlign,font,fontStyle,maxWidth,angle-=cameraAngle,context)}const drawFontsChecked=new Set;const textLines=text=>(text+="").includes("\r")?text.split(/\r?\n/):text.split("\n");var textIntl=globalThis.Intl;const textSegmenter=textIntl?.Segmenter&&new textIntl.Segmenter,textJoins=/[\p{M}\u200c\u200d\u0600-\u0605\u06dd\u070f\u0890\u0891\u08e2\u0d4e\u0e33\u0eb3\u1100-\u11ff\ua960-\ua97c\ud7b0-\ud7fb\uff9e\uff9f\u{10000}-\u{10ffff}]/u;function textCharacters(line){return textJoins.test(line)?textSegmenter?Array.from(textSegmenter.segment(line),s=>s.segment.codePointAt(0)):Array.from(line,c=>c.codePointAt(0)).filter(c=>8205!==c&&65039!==c&&!(768<=c&&c<880)):line}const textGraphemes=text=>textJoins.test(text)?textSegmenter?Array.from(textSegmenter.segment(text),s=>s.segment):[...text]:text.split(""),textCharacterCode=(characters,i)=>{{if((characters="string"==typeof characters?characters.charCodeAt(i):characters[i])<192||592<=characters)return characters;let base=textBaseLetters.get(characters);return void 0===base&&textBaseLetters.set(characters,base=String.fromCharCode(characters).normalize("NFD").charCodeAt(0)),base}},textBaseLetters=new Map;function drawTextScreen(text,pos,size,color=WHITE,lineWidth=0,lineColor=BLACK,textAlign="center",font=fontDefault,fontStyle="",maxWidth,angle=0,context=drawContext){if(!headlessMode||context){text=textLines(text),color=(context.save(),context.fillStyle=color.toString(),context.strokeStyle=lineColor.toString(),context.lineWidth=lineWidth,context.textAlign=textAlign,fontStyle+" "+size+"px "+font);debug&&function(context,fontText,font,fontStyle){fontStyle=fontStyle+"|"+font,drawFontsChecked.has(fontStyle)||(drawFontsChecked.add(fontStyle),context.font="1px littlejs-font-check",context.font=fontText,"1px littlejs-font-check"===context.font&&console.warn(`the canvas does not take the font ${font}, so text draws as 10px sans-serif; `+`a family name with spaces or digits goes in quotes, as "'Press Start 2P'"`))}(context,color,font,fontStyle),context.font=color,context.textBaseline="middle",context.translate(pos.x+.5,pos.y+.5),context.rotate(angle);let yOffset=-(text.length-1)*size/2;text.forEach(line=>{lineWidth&&context.strokeText(line,0,yOffset,maxWidth),context.fillText(line,0,yOffset,maxWidth),yOffset+=size}),context.restore()}}async function loadTexture(textureIndex,src){var old=textureInfos[textureIndex];old?.destroyWebGLTexture();const image=new Image;return src&&await engineAddLoad(new Promise(resolve=>{image.onload=resolve,image.onerror=()=>{console.warn("failed to load image: "+src),resolve()},image.crossOrigin="anonymous",image.src=src})),textureInfos[textureIndex]=new TextureInfo(image)}function screenToWorld(screenPos){let x=(screenPos.x-mainCanvasSize.x/2+.5)/cameraScale,y=(screenPos.y-mainCanvasSize.y/2+.5)/-cameraScale;var xr,s;return cameraAngle&&(screenPos=cos(-cameraAngle),s=sin(-cameraAngle),xr=x*screenPos-y*s,s=x*s+y*screenPos,x=xr,y=s),new Vector2(x+cameraPos.x,y+cameraPos.y)}function worldToScreen(worldPos){let x=worldPos.x-cameraPos.x,y=worldPos.y-cameraPos.y;var xr,s;return cameraAngle&&(worldPos=cos(cameraAngle),s=sin(cameraAngle),xr=x*worldPos-y*s,s=x*s+y*worldPos,x=xr,y=s),new Vector2(x*cameraScale+mainCanvasSize.x/2-.5,y*-cameraScale+mainCanvasSize.y/2-.5)}function screenToWorldDelta(screenDelta){let x=screenDelta.x/cameraScale,y=screenDelta.y/-cameraScale;var xr,s;return cameraAngle&&(screenDelta=cos(-cameraAngle),s=sin(-cameraAngle),xr=x*screenDelta-y*s,s=x*s+y*screenDelta,x=xr,y=s),new Vector2(x,y)}function worldToScreenDelta(worldDelta){let x=worldDelta.x,y=worldDelta.y;var xr,s;return cameraAngle&&(worldDelta=cos(cameraAngle),s=sin(cameraAngle),xr=x*worldDelta-y*s,s=x*s+y*worldDelta,x=xr,y=s),new Vector2(x*cameraScale,y*-cameraScale)}function screenToWorldTransform(screenPos,screenSize,screenAngle=0){return[screenToWorld(screenPos),screenSize.scale(1/cameraScale),screenAngle+cameraAngle]}function getCameraSize(){return mainCanvasSize.scale(1/cameraScale)}function cameraFit(center,size,worldMargin,screenInset){var c,s,fitW,worldMargin=padSides(worldMargin),screenInset=padSides(screenInset),worldW=size.x+worldMargin.left+worldMargin.right,size=size.y+worldMargin.top+worldMargin.bottom,viewW=mainCanvasSize.x-screenInset.left-screenInset.right,viewH=mainCanvasSize.y-screenInset.top-screenInset.bottom;return 0<worldW&&0<size&&0<viewW&&0<viewH&&(c=cos(cameraAngle),s=sin(cameraAngle),fitW=abs(c)*worldW+abs(s)*size,worldW=abs(s)*worldW+abs(c)*size,cameraScale=min(viewW/fitW,viewH/worldW),size=vec2(worldMargin.right-worldMargin.left,worldMargin.top-worldMargin.bottom).scale(.5),viewH=vec2((viewW=.5*(screenInset.right-screenInset.left)/cameraScale)*c+(fitW=.5*(screenInset.top-screenInset.bottom)/cameraScale)*s,fitW*c-viewW*s),cameraPos=center.add(size).add(viewH)),cameraScale;function padSides(p){return isVector2(p=void 0!==p&&!isNumber(p)?p:vec2(p))?{top:p.y,right:p.x,bottom:p.y,left:p.x}:{top:p.top||0,right:p.right||0,bottom:p.bottom||0,left:p.left||0}}}function isOnScreen(pos,size=0){if(!cameraScale)return!1;let x=pos.x-cameraPos.x,y=pos.y-cameraPos.y;cameraAngle&&(pos=cos(cameraAngle),s=sin(cameraAngle),xr=x*pos-y*s,s=x*s+y*pos,x=xr,y=s),x*=2*cameraScale,y*=2*-cameraScale,size instanceof Vector2&&(size=size.length()),size*=cameraScale;var s,pos=mainCanvasSize.x,xr=mainCanvasSize.y;return x+size>-pos&&x-size<pos&&y+size>-xr&&y-size<xr}function setAdditiveBlendMode(additive=!0){glAdditive=additive,drawContext&&(drawContext.globalCompositeOperation=additive?"lighter":"source-over")}function setShader(shader){glCustomShader=shader||void 0}function setBackgroundCanvas(canvas){backgroundCanvas=canvas}function combineCanvases(){var w=mainCanvas.width,h=mainCanvas.height;workCanvas.width=w,workCanvas.height=h,workContext.fillStyle="#000",workContext.fillRect(0,0,w,h),backgroundCanvas&&workContext.drawImage(backgroundCanvas,0,0,w,h),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 tintImageData(data,color,additiveColor){if(additiveColor&&!isBlack(additiveColor)){var colorMultiply=[clamp(color.r),clamp(color.g),clamp(color.b),clamp(color.a)],colorAdd=[255*additiveColor.r,255*additiveColor.g,255*additiveColor.b,255*additiveColor.a];for(let i=0;i<data.length;++i)data[i]=data[i]*colorMultiply[3&i]+colorAdd[3&i]|0;return!0}for(let i=0;i<data.length;i+=4)data[i]*=clamp(color.r),data[i+1]*=clamp(color.g),data[i+2]*=clamp(color.b);return!1}const drawTextureWrappedBakes=new WeakMap;function isFullscreen(){return!!document.fullscreenElement}function toggleFullscreen(){var rootElement=mainCanvas.parentElement;isFullscreen()?document.exitFullscreen&&document.exitFullscreen()?.catch?.(()=>{}):rootElement.requestFullscreen&&rootElement.requestFullscreen()?.catch?.(()=>{})}function setCursor(cursorStyle="auto"){mainCanvas.parentElement.style.cursor=cursorStyle}let engineImageFont;class ImageFont{constructor(tileInfo){this.tileInfo=tileInfo}drawText(text,pos,size=1,center,color,useWebGL,context){size="number"==typeof size?(size*=cameraScale,new Vector2(size,size)):size.scale(cameraScale);var skip=glSkipScreenSpace;glSkipScreenSpace=!1,this.drawTextScreen(text,worldToScreen(pos),size,center,color,useWebGL,context),glSkipScreenSpace=skip}drawTextScreen(text,pos,size,center=!0,color=WHITE,useWebGL=glEnable,context){const glyphSize="number"==typeof size?new Vector2(size,size):size,drawPos=new Vector2,tileInfo=this.tileInfo.frame(0);size=textLines(text);const centerOffsetY=center?(size.length-1)*glyphSize.y/2:0;size.forEach((line,j)=>{var characters=textCharacters(line),centerOffset=center?(characters.length-1)*glyphSize.x/2:0;for(let i=characters.length;i--;){var charCode=textCharacterCode(characters,i);this.getGlyphPos(charCode<32||127<charCode?95:charCode-32,tileInfo.pos),drawPos.x=ceil(pos.x+i*glyphSize.x-centerOffset-glyphSize.x/2)+glyphSize.x/2-.5,drawPos.y=ceil(pos.y+j*glyphSize.y-centerOffsetY-glyphSize.y/2)+glyphSize.y/2-.5,drawTile(drawPos,glyphSize,tileInfo,color,0,!1,void 0,useWebGL,!0,context)}})}getGlyphPos(index,pos=new Vector2){var columns,t=this.tileInfo,padding=t.padding,w=t.size.x+2*padding,h=t.size.y+2*padding;return t.columns?pos.set(t.pos.x+index%t.columns*w,t.pos.y+(index/t.columns|0)*h):(columns=t.textureInfo.size.x/w|0,t=((t.pos.y-padding)/h|0)*columns+((t.pos.x-padding)/w|0)+index,pos.set(t%columns*w+padding,(t/columns|0)*h+padding))}}function engineGlowAlpha(r,falloff){var falloff=3.5*falloff,edge=Math.exp(-falloff);return(Math.exp(-falloff*r*r)-edge)/(1-edge)}const engineGlowTextures=new Map;function engineGlowTexture(falloff=1){var key=max(round(10*falloff),1)/10,falloff=engineGlowTextures.get(key);if(!falloff&&glContext&&canvasAvailable()){var context=createCanvasContext(64),gradient=context.createRadialGradient(32,32,0,32,32,32);for(let i=0;i<=16;++i)gradient.addColorStop(i/16,"rgba(255,255,255,"+engineGlowAlpha(i/16,key).toFixed(4)+")");context.fillStyle=gradient,context.fillRect(0,0,64,64),engineGlowTextures.set(key,falloff=new TextureInfo(context.canvas,!0,!1,!1))}return falloff}let mousePos=vec2(),mousePosScreen=vec2(),mouseDelta=vec2(),mouseDeltaScreen=vec2(),mouseWheel=0,touchPinch=0,mouseInWindow=!0,isUsingGamepad=!1,lastInputDevice="mouse",inputMouseMoveThreshold=6,inputPreventDefault=!0,gamepadPrimary=0;const isTouchDevice=!headlessMode&&"undefined"!=typeof window&&void 0!==window.ontouchstart;function setInputPreventDefault(preventDefault=!0){inputPreventDefault=preventDefault}function setInputMouseMoveThreshold(threshold){inputMouseMoveThreshold=threshold}function usingMouseInput(){return"mouse"===lastInputDevice}function usingKeyboardInput(){return"keyboard"===lastInputDevice}function usingGamepadInput(){return"gamepad"===lastInputDevice}function inputClearKey(key,device=0,clearDown=!0,clearPressed=!0,clearReleased=!0){inputData[device]&&(inputData[device][key]&=~((clearDown?1:0)|(clearPressed?2:0)|(clearReleased?4:0)))}const inputWASDToArrow={KeyW:"ArrowUp",KeyS:"ArrowDown",KeyA:"ArrowLeft",KeyD:"ArrowRight"},inputArrowToWASD={ArrowUp:"KeyW",ArrowDown:"KeyS",ArrowLeft:"KeyA",ArrowRight:"KeyD"},inputKeysHeld=new Set;let inputPinch,inputWasTouching=0,inputTouchIdentifier,inputLastTouchTime=-1e9;function inputIsTouchMouseEvent(e){return e.sourceCapabilities?.firesTouchEvents||performance.now()-inputLastTouchTime<500}function inputClear(){for(let i=inputData.length;i--;)i&&!inputData[i]||(inputData[i]=[]);inputKeysHeld.clear(),inputWasTouching=0,inputPinch=void 0,touchGamepadPointerRole.clear(),touchGamepadButtons.length=0,touchGamepadButtonsPressed.length=0,touchGamepadSticks.length=0,touchGamepadStickPointerId.length=0,gamepadStickData.length=0,gamepadDpadData.length=0}function keyIsDown(key,device=0){return!!(1&inputData[device]?.[key])&&!0}function keyWasPressed(key,device=0){return!!(2&inputData[device]?.[key])&&!0}function keyWasReleased(key,device=0){return!!(4&inputData[device]?.[key])&&!0}function keyDirection(up="ArrowUp",down="ArrowDown",left="ArrowLeft",right="ArrowRight"){var k=key=>keyIsDown(key)?1:0;return vec2(k(right)-k(left),k(up)-k(down))}function mouseIsDown(button){return keyIsDown(button)}function mouseWasPressed(button){return keyWasPressed(button)}function mouseWasReleased(button){return keyWasReleased(button)}function gamepadIsDown(button,gamepad=gamepadPrimary){return keyIsDown(button,gamepad+1)}function gamepadWasPressed(button,gamepad=gamepadPrimary){return keyWasPressed(button,gamepad+1)}function gamepadWasReleased(button,gamepad=gamepadPrimary){return keyWasReleased(button,gamepad+1)}function gamepadStick(stick,gamepad=gamepadPrimary){return gamepadStickData[gamepad]?.[stick]??vec2()}function gamepadDpad(gamepad=gamepadPrimary){return gamepadDpadData[gamepad]?.copy()??vec2()}function gamepadConnected(gamepad=gamepadPrimary){return!!inputData[gamepad+1]}function gamepadStickCount(gamepad=gamepadPrimary){return gamepadStickData[gamepad]?.length??0}function gamepadVibrate(gamepad=gamepadPrimary,duration=200,strongMagnitude=1,weakMagnitude=1,startDelay=0){vibrateEnable&&!headlessMode&&inputGetGamepads()[gamepad]?.vibrationActuator?.playEffect?.("dual-rumble",{duration:duration,strongMagnitude:strongMagnitude,weakMagnitude:weakMagnitude,startDelay:startDelay})?.catch?.(()=>{})}function gamepadVibrateStop(gamepad=gamepadPrimary){vibrateEnable&&!headlessMode&&inputGetGamepads()[gamepad]?.vibrationActuator?.reset?.()}function inputGetGamepads(){try{return navigator?.getGamepads?.()||[]}catch(e){return[]}}function vibrate(pattern=100){vibrateEnable&&!headlessMode&&navigator?.vibrate?.(pattern)}function vibrateStop(){vibrate(0)}function pointerLockRequest(){try{(mainCanvas.requestPointerLock?.())?.catch?.(()=>{})}catch{}}function pointerLockExit(){document.exitPointerLock?.()}function pointerLockIsActive(){return document.pointerLockElement===mainCanvas}const inputData=[[]],gamepadStickData=[],gamepadDpadData=[],gamepadHadInput=[],gamepadButtonsLast=[],gamepadAxisCentered=[],gamepadAxisCenteredFrames=15,touchGamepadTimer=new Timer(void 0,!0),touchGamepadButtons=[],touchGamepadSticks=[],touchGamepadButtonsPressed=[],touchGamepadStickAnchors=[],touchGamepadStickPointerId=[],touchGamepadPointerRole=new Map;let touchGamepadOverlay,touchGamepadStage,touchGamepadSvg,touchGamepadSvgEls,touchGamepadSideZones=[],touchGamepadZoneC,touchGamepadNeedRelayout=!0,touchGamepadLastLayout;function inputUpdatePost(){if(!headlessMode){for(const deviceInputData of inputData)for(const i in deviceInputData)deviceInputData[i]&=1;mouseWheel=touchPinch=0,mouseDelta=vec2(),mouseDeltaScreen=vec2()}}const touchGamepadSvgNS="http://www.w3.org/2000/svg";function touchGamepadStageRect(){return touchGamepadStage.getBoundingClientRect()}function touchGamepadSideStick(side){return side?touchGamepadRightStick:touchGamepadLeftStick}function touchGamepadSideButtonCount(side){return side?touchGamepadButtonCount:touchGamepadLeftButtonCount}function touchGamepadSideButtonBase(side){return side?0:4}function touchGamepadStickOut(side){return side&&touchGamepadLeftStick?1:0}function touchGamepadStickButton(side){return touchGamepadStickOut(side)?11:10}function touchGamepadFaceIndex(b){return 3===b?2:2===b?3:b}function touchGamepadSideHasControl(side){return touchGamepadSideStick(side)||0<touchGamepadSideButtonCount(side)}function touchGamepadSideCenter(side,W,H){if(touchGamepadFloating&&touchGamepadSideStick(side)&&touchGamepadStickAnchors[side])return touchGamepadStickAnchors[side];let y=H-touchGamepadSize;H=touchGamepadSideButtonCount(side);return touchGamepadSideStick(side)||2!==H&&3!==H||(y-=touchGamepadSize/4),vec2(side?W-touchGamepadSize:touchGamepadSize,y)}function touchGamepadApplyStick(side,p){p=p.subtract(touchGamepadStickAnchors[side]);touchGamepadSticks[side]=p.scale(2/touchGamepadSize).clampLength(),touchGamepadButtons[touchGamepadStickButton(side)]=1}function touchGamepadStartBlockSide(side){return touchGamepadSideHasControl(side)&&(!touchGamepadFloating||!touchGamepadSideStick(side))}function touchGamepadPointerUp(e){var role=touchGamepadPointerRole.get(e.pointerId);role&&(touchGamepadPointerRole.delete(e.pointerId),"stick0"===role||"stick1"===role?(e="stick1"===role?1:0,touchGamepadStickPointerId[e]=void 0,touchGamepadSticks[e]=vec2(),delete touchGamepadButtons[touchGamepadStickButton(e)]):"start"===role?delete touchGamepadButtons[9]:delete touchGamepadButtons[+role.slice(4)],touchGamepadTimer.set())}let audioContext="undefined"==typeof AudioContext?void 0:new AudioContext;function audioResume(){audioContext?.resume()?.catch?.(()=>{})}let audioMasterGain=audioContext?.createGain(),audioMasterVolume;function audioUpdateVolume(){audioMasterGain&&soundVolume!==audioMasterVolume&&isFinite(soundVolume)&&(audioMasterGain.gain.value=audioMasterVolume=soundVolume)}audioMasterGain&&(audioMasterGain.connect(audioContext.destination),audioMasterGain.gain.value=audioMasterVolume=soundVolume,audioContext.addEventListener?.("statechange",function(){if(audioIsRunning()){audioWaitingPrune();var instances=[...audioWaitingInstances.keys()];audioWaitingInstances.clear();for(const instance of instances)instance.resume()}}));let audioMasterEffectInput,audioMasterEffectOutput,audioMasterEffectOutputIsEffect;const audioDefaultSampleRate=44100;function audioIsRunning(){return"running"===audioContext?.state}function audioSetSession(){var session="undefined"!=typeof navigator&&navigator.audioSession;session&&(session.type=soundIgnoreSilentSwitch?"playback":"auto")}let audioSuspendedWhenHidden=!1;function audioVisibilityChange(){audioContext&&(document.hidden?soundPauseWhenHidden&&"running"==audioContext.state&&(audioSuspendedWhenHidden=!0,audioContext.suspend()):audioSuspendedWhenHidden&&(audioSuspendedWhenHidden=!1,audioResume()))}const audioWaitingInstances=new Map;function audioWaitingPrune(now=performance.now()){for(var[instance,startTime]of audioWaitingInstances){var remaining=(instance.getDuration()-instance.pausedTime)/instance.rate;!instance.loop&&now-startTime>1e3*remaining&&audioWaitingInstances.delete(instance)}}function setAudioMasterEffect(input,output){var outputArg=output||input,outputArg=!!outputArg&&"input"in outputArg;if(output=audioEffectNode(output,"output")||audioEffectNode(input,"output"),input=audioEffectNode(input,"input"),audioMasterGain){if(audioMasterGain.disconnect(audioMasterEffectInput||audioContext.destination),audioMasterEffectOutput){let fedSpeakers=!0;try{audioMasterEffectOutput.disconnect(audioContext.destination)}catch{fedSpeakers=!1}audioMasterEffectOutputIsEffect&&fedSpeakers&&audioMasterEffectOutput.connect(audioMasterGain)}audioMasterEffectInput=input,audioMasterEffectOutput=output,audioMasterEffectOutputIsEffect=outputArg,(input?(audioMasterGain.connect(input),output.disconnect(),output):audioMasterGain).connect(audioContext.destination)}}function audioEffectNode(effectOrNode,key){return effectOrNode&&"input"in effectOrNode?effectOrNode[key]:effectOrNode}const audioFinite=(value,otherwise)=>isFinite(value)?value:otherwise;function audioParamRamp(param,value,fadeTime=0){value=audioFinite(value,0),fadeTime=audioFinite(fadeTime,0);var startTime=audioContext.currentTime;return param.cancelScheduledValues(startTime),fadeTime?(param.setValueAtTime(param.value,startTime),param.linearRampToValueAtTime(value,startTime+fadeTime)):param.value=value,startTime+fadeTime}class Sound{constructor(asset,randomness,range,taper=soundDefaultTaper,onloadCallback){if(soundEnable&&!headlessMode&&audioContext){var rangeIsDefault=void 0===range;if(rangeIsDefault&&(range=soundDefaultRange),this.range=range,this.rangeIsDefault=rangeIsDefault,this.taper=taper,this.randomness=randomness??0,this.sampleRate=44100,this.sampleLength=0,this.sampleBuffer=void 0,this._sampleChannels=void 0,this.loadedPercent=0,this.onloadCallback=onloadCallback,this.output=void 0,isArray(asset)){range=asset.slice();this.randomness=randomness??clamp(range[1]??.05),range[1]=0,this.sampleChannels=[zzfxG(...range)],this.buildSampleBuffer(),this.loadedPercent=1,onloadCallback?.(this)}else if(asset){const filename=asset+"";engineAddLoad(this.loadSound(filename).catch(e=>{console.warn("Sound load failed for",filename,"-",e?.message??e);try{1!==this.loadedPercent&&this.onloadCallback?.(this)}catch(callbackError){console.warn("Sound onloadCallback failed for",filename,"-",callbackError)}}))}}else this.loadedPercent=1,onloadCallback?.(this)}get range(){return this.rangeValue}set range(range){this.rangeValue=range,this.rangeIsDefault=!1}get sampleChannels(){var buffer=this.sampleBuffer;if(!this._sampleChannels&&buffer){var channels=[];for(let i=0;i<buffer.numberOfChannels;i++)channels.push(buffer.getChannelData(i).slice());this._sampleChannels=channels}return this._sampleChannels}set sampleChannels(sampleChannels){this._sampleChannels=sampleChannels,this.sampleBuffer=void 0,this.sampleLength=sampleChannels?.[0]?.length||0}buildSampleBuffer(){this.sampleBuffer||!this._sampleChannels||headlessMode||(this.sampleBuffer=createAudioBuffer(this._sampleChannels,this.sampleRate),this._sampleChannels=void 0)}play(pos,volume=1,pitch=1,randomnessScale=1,loop=!1,paused=!1){if(soundEnable&&!headlessMode&&(this.sampleBuffer||this._sampleChannels)){let pan;if(pos){var range=this.range;if(range){var lengthSquared=cameraPos.distanceSquared(pos);if(range*range<=lengthSquared)return;var lengthSquared=lengthSquared**.5,taperRange=range*this.taper;taperRange<lengthSquared&&(volume*=percent(lengthSquared,range,taperRange))}pan=2*worldToScreen(pos).x/mainCanvasSize.x-1}lengthSquared=pitch+pitch*this.randomness*randomnessScale*rand(-1,1),range=new SoundInstance(this,volume,lengthSquared,pan,loop,paused);return debug,range}}playLoop(pos,volume=1,pitch=1,randomnessScale=1,paused=!1){return this.play(pos,volume,pitch,randomnessScale,!0,paused)}playMusic(volume=1,loop=!0,paused=!1){return this.play(void 0,volume,1,0,loop,paused)}playNote(semitoneOffset=0,pos,volume){semitoneOffset=getNoteFrequency(semitoneOffset,1);return this.play(pos,volume,semitoneOffset,0)}getDuration(){return this.sampleLength/this.sampleRate||0}isLoaded(){return 1===this.loadedPercent}async loadSound(filename){var response=await loadFetch(filename,"loadSound");if(!response.ok)throw new Error(`loadSound: could not load ${filename}, HTTP ${response.status} `+response.statusText);filename=await response.arrayBuffer(),response=await audioContext.decodeAudioData(filename);this.sampleRate=response.sampleRate,this.sampleLength=response.length,this.sampleBuffer=response,this._sampleChannels=void 0,this.loadedPercent=1,this.onloadCallback?.(this)}}class SoundInstance{constructor(sound,volume=1,rate=1,pan=0,loop=!1,paused=!1){this.sound=sound,this.volume=audioFinite(volume,0),this.rate=audioFinite(rate,1),this.pan=audioFinite(pan,0),this.loop=loop,this.pausedTime=0,this.startTime=void 0,this.startOffset=0,this.gainNode=void 0,this.pannerNode=void 0,this.source=void 0,this.output=sound.output,this.onendedCallback=void 0,this.sourceEnded=source=>{source===this.source&&(this.source=this.gainNode=this.pannerNode=void 0,this.startTime=void 0,this.pausedTime=0,this.onendedCallback?.(source))},paused||this.start()}start(offset=0){if(this.isPlaying()&&this.stop(),this.gainNode=audioContext.createGain(),this.pannerNode=new StereoPannerNode(audioContext,{pan:clamp(this.pan,-1,1)}),this.sound.buildSampleBuffer(),this.source=this.sound.sampleBuffer?playAudioBuffer(this.sound.sampleBuffer,this.volume,this.rate,this.pan,this.loop,this.gainNode,offset,this.sourceEnded,this.output,this.pannerNode):playSamples(this.sound.sampleChannels,this.volume,this.rate,this.pan,this.loop,this.sound.sampleRate,this.gainNode,offset,this.sourceEnded,this.output,this.pannerNode),audioWaitingInstances.delete(this),this.source)this.startTime=audioContext.currentTime,this.startOffset=offset,this.pausedTime=void 0;else if(this.startTime=this.gainNode=this.pannerNode=void 0,this.pausedTime=offset,!audioIsRunning()){audioWaitingPrune();for(const other of audioWaitingInstances.keys())other.sound===this.sound&&audioWaitingInstances.delete(other);audioWaitingInstances.set(this,performance.now())}}setVolume(volume,fadeTime=0){this.volume=volume=audioFinite(volume,0),this.gainNode&&audioParamRamp(this.gainNode.gain,volume,fadeTime)}setPan(pan){this.pan=pan=audioFinite(pan,0),this.pannerNode&&(this.pannerNode.pan.value=clamp(pan,-1,1))}setRate(rate){this.isPlaying()&&(this.startOffset=this.getCurrentTime(),this.startTime=audioContext.currentTime),this.rate=rate=audioFinite(rate,1),this.source&&(this.source.playbackRate.value=rate)}stop(fadeTime=0){audioWaitingInstances.delete(this),this.isPlaying()&&(fadeTime?this.source.stop(audioParamRamp(this.gainNode.gain,0,fadeTime)):this.source.stop()),this.pausedTime=0,this.source=void 0,this.startTime=void 0,this.gainNode=void 0,this.pannerNode=void 0}pause(){audioWaitingInstances.delete(this),this.isPaused()||(this.pausedTime=this.getCurrentTime(),this.source.stop(),this.source=void 0,this.startTime=void 0,this.gainNode=void 0,this.pannerNode=void 0)}resume(){this.isPaused()&&this.start(this.pausedTime)}isPlaying(){return!!this.source}isPaused(){return!this.isPlaying()}getCurrentTime(){var duration,place;return this.isPlaying()?(duration=this.getDuration(),place=this.startOffset+(audioContext.currentTime-this.startTime)*this.rate,duration?(this.loop?mod:min)(place,duration):0):this.pausedTime}getDuration(){return this.sound.getDuration()}getSource(){return this.source}}function speak(text,volume=1,rate=1,pitch=1,language=""){if(soundEnable&&!headlessMode&&"undefined"!=typeof speechSynthesis)return(text=new SpeechSynthesisUtterance(text)).lang=language,text.volume=clamp(volume*soundVolume),text.rate=rate,text.pitch=pitch,speechSynthesis.speak(text),text}function speakStop(){"undefined"!=typeof speechSynthesis&&speechSynthesis.cancel()}function getNoteFrequency(semitoneOffset,rootFrequency=220){return rootFrequency*2**(semitoneOffset/12)}function playSamples(sampleChannels,volume=1,rate=1,pan=0,loop=!1,sampleRate=44100,gainNode,offset=0,onended,output,pannerNode){if(soundEnable&&!headlessMode){if(audioIsRunning())return playAudioBuffer(createAudioBuffer(sampleChannels,sampleRate),volume,rate,pan,loop,gainNode,offset,onended,output,pannerNode);audioSuspendedWhenHidden||audioResume()}}function createAudioBuffer(sampleChannels,sampleRate=44100){var channelCount=sampleChannels.length,sampleLength=sampleChannels[0].length;const buffer=audioContext.createBuffer(channelCount,sampleLength,sampleRate);return sampleChannels.forEach((c,i)=>buffer.getChannelData(i).set(c)),buffer}function playAudioBuffer(buffer,volume=1,rate=1,pan=0,loop=!1,gainNode,offset=0,onended,output,pannerNode){if(soundEnable&&!headlessMode){if(audioIsRunning()){volume=audioFinite(volume,0),rate=audioFinite(rate,1),pan=audioFinite(pan,0);const source=audioContext.createBufferSource();source.buffer=buffer,source.playbackRate.value=rate,source.loop=loop,(gainNode=gainNode||audioContext.createGain()).gain.value=volume;buffer=audioEffectNode(output,"input")||audioMasterGain;gainNode.connect(buffer);const panner=pannerNode||new StereoPannerNode(audioContext,{pan:clamp(pan,-1,1)});return source.connect(panner).connect(gainNode),source.addEventListener("ended",()=>{gainNode.disconnect(),panner.disconnect(),onended&&onended(source)}),source.start(0,offset),debug,source}audioSuspendedWhenHidden||audioResume()}}function zzfx(...zzfxSound){return playSamples([zzfxG(...zzfxSound)])}function zzfxG(volume=1,randomness=.05,frequency=220,attack=0,sustain=0,release=.1,shape=0,shapeCurve=1,slide=0,deltaSlide=0,pitchJump=0,pitchJumpTime=0,repeatTime=0,noise=0,modulation=0,bitCrush=0,delay=0,sustainVolume=1,decay=0,tremolo=0,filter=0){let PI2=2*PI,startSlide=slide*=500*PI2/44100/44100,startFrequency=frequency*=(1+rand(randomness,-randomness))*PI2/44100,modOffset=0,repeat=0,crush=0,jump=1,length,b,t=0,i=0,s=0,f,w=PI2*min(abs(filter),11024)*2/44100,cosw=cos(w),alpha=sin(w)/2/2,a0=1+alpha,a1=-2*cosw/a0,a2=(1-alpha)/a0,b0=(1+sign(filter)*cosw)/2/a0,b1=-(sign(filter)+cosw)/a0,b2=b0,x2=0,x1=0,y2=0,y1=0;for(deltaSlide*=500*PI2/44100**3,modulation*=PI2/44100,pitchJump*=PI2/44100,pitchJumpTime*=44100,repeatTime=44100*repeatTime|0,length=(attack=44100*attack||9)+(decay*=44100)+(sustain*=44100)+(release*=44100)+(delay*=44100)|0,b=new Float32Array(0<length?length:0);i<length;b[i++]=s*volume)++crush%(100*bitCrush|0)||(s=shape?1<shape?2<shape?3<shape?4<shape?t/PI2%1<shapeCurve/2?1:-1:sin(t**3):max(min(tan(t),1),-1):1-(2*t/PI2%2+2)%2:1-4*abs(round(t/PI2)-t/PI2):sin(t),s=(repeatTime?1-tremolo+tremolo*sin(PI2*i/repeatTime):1)*(4<shape?s:sign(s)*abs(s)**shapeCurve)*(i<attack?i/attack:i<attack+decay?1-(i-attack)/decay*(1-sustainVolume):i<attack+decay+sustain?sustainVolume:i<length-delay?(length-i-delay)/release*sustainVolume:0),s=delay?s/2+(delay>i?0:(i<length-delay?1:(length-i)/delay)*b[i-delay|0]/2/(volume||1)):s,filter&&(s=y1=b2*x2+b1*(x2=x1)+b0*(x1=s)-a2*y2-a1*(y2=y1))),f=(frequency+=slide+=deltaSlide)*cos(modulation*modOffset++),t+=f+f*noise*(i*i*PI2%2-1),jump&&++jump>pitchJumpTime&&(frequency+=pitchJump,startFrequency+=pitchJump,jump=0),!repeatTime||++repeat%repeatTime||(frequency=startFrequency,slide=startSlide,jump||=1);return b}const tileCollisionLayers=[];function tileCollisionTester(callbackObject){return callbackObject?"function"==typeof callbackObject?(tileData,pos)=>callbackObject(tileData,pos):(tileData,pos)=>callbackObject.collideWithTile(tileData,pos):tileData=>0<tileData}function tileCollisionGetData(pos,solidOnly=!0){let found=0;for(const layer of tileCollisionLayers)if(!solidOnly||layer.isSolid){var x=pos.x-layer.pos.x,y=pos.y-layer.pos.y,size=layer.size;if(0<=x&&0<=y&&x<size.x&&y<size.y){y=layer.collisionData[(0|y)*size.x+(0|x)];if(0<y)return y;y&&!found&&(found=y)}}return found}function tileCollisionTest(pos,size=vec2(),callbackObject,solidOnly=!0){for(const layer of tileCollisionLayers)if((!solidOnly||layer.isSolid)&&layer.collisionTest(pos,size,callbackObject))return layer}function tileCollisionRaycast(posStart,posEnd,callbackObject,normal,solidOnly=!0){let closestHit,closestDistSq,closestNormal;var hitPos,d,scratchNormal=normal&&vec2();for(const layer of tileCollisionLayers)solidOnly&&!layer.isSolid||(hitPos=layer.collisionRaycast(posStart,posEnd,callbackObject,scratchNormal))&&(d=posStart.distanceSquared(hitPos),void 0===closestHit||d<closestDistSq)&&(closestHit=hitPos,closestDistSq=d,normal)&&(closestNormal=scratchNormal.copy());return closestHit&&normal&&normal.setFrom(closestNormal),closestHit}const tileLayersTiledFlips=[[0,0],[3,1],[2,1],[3,0],[0,1],[1,0],[2,0],[1,1]];function tileLayersLoad(tileMapData,tileInfo=tileLayerDefaultTile(),renderOrder=0,collisionLayer,draw=!0){var{width:mapWidth,height:mapHeight}=tileMapData=tileMapData?tileMapData:{width:50,height:50,layers:[{data:new Array(2500).fill(0)}]};if(!(Number.isInteger(mapWidth)&&Number.isInteger(mapHeight)&&0<mapWidth&&0<mapHeight))throw new Error(`tileLayersLoad: a map is a whole number of cells across and down, not ${mapWidth} by `+mapHeight);if(!isArray(tileMapData.layers)||!tileMapData.layers.length)throw new Error("tileLayersLoad: a map has a list of layers");var spacing,size,width,mapWidth=tileMapData.tilesets?.[0];tileInfo&&!tileInfo.tiles&&mapWidth&&(0<mapWidth.margin||0<mapWidth.spacing)&&(mapHeight=mapWidth.margin||0,spacing=mapWidth.spacing||0,size=tileInfo.size,width=tileInfo.textureInfo?.size.x||0,mapWidth=mapWidth.columns||max(1,floor((width-2*mapHeight+spacing)/(size.x+spacing))),width=tileInfo.pos.subtract(vec2(tileInfo.padding)),tileInfo=new TileInfo(width.add(vec2(mapHeight)),size,tileInfo.textureInfo,spacing/2,tileInfo.bleed,mapWidth));const layers=[],addLayers=(dataLayers,groupColor,groupVisible)=>{for(const dataLayer of dataLayers){var tint,visible,type=dataLayer.type;type&&"tilelayer"!==type&&"group"!==type?layers.push({dataLayer:dataLayer}):(visible=((tint=(tint=dataLayer.tintcolor)?tileLayersColor(tint):(dataLayer.color||WHITE).copy()).a*=dataLayer.opacity??1,groupVisible&&!1!==dataLayer.visible),"group"===type?addLayers(dataLayer.layers||[],groupColor.multiply(tint),visible):layers.push({dataLayer:dataLayer,color:groupColor.multiply(tint),visible:visible}))}};if(addLayers(tileMapData.layers,WHITE,!0),"string"==typeof collisionLayer){const name=collisionLayer;(collisionLayer=layers.findIndex(l=>l.color&&l.dataLayer.name===name))<0&&console.error("tileLayersLoad: no tile layer is named "+name+", none is solid")}var layerIndex,dataLayer,color,tileLayers=[],levelSize=vec2(tileMapData.width,tileMapData.height),layerCount=layers.length;for([layerIndex,{dataLayer,color}]of layers.entries()){var tiles=dataLayer.data;if(color&&(!isArray(tiles)&&!ArrayBuffer.isView(tiles)||dataLayer.data.length!==levelSize.area()))throw new Error(`tileLayersLoad: layer ${dataLayer.name??layerIndex} has ${dataLayer.data?.length} tiles for a map of `+levelSize.area()+"; infinite maps and compressed layers are not read")}for(let layerIndex=layerCount;layerIndex--;){const{dataLayer,color:layerColor,visible}=layers[layerIndex];if(layerColor){var layerRenderOrder=renderOrder-(layerCount-1-layerIndex),tileLayer=new TileCollisionLayer(vec2(),levelSize,tileInfo,layerRenderOrder);tileLayer.isSolid=layerIndex===collisionLayer,tileLayers[layerIndex]=tileLayer,visible||(tileLayer.render=()=>{});for(let x=0;x<levelSize.x;++x)for(let y=0;y<levelSize.y;++y){var direction,mirror,pos=vec2(x,levelSize.y-1-y),data=dataLayer.data[x+y*levelSize.x];data&&([direction,mirror]=tileLayersTiledFlips[data>>>29],data=(268435455&data)-1,data=new TileLayerData(data,direction,!!mirror,layerColor),tileLayer.setData(pos,data),layerIndex===collisionLayer)&&tileLayer.setCollisionData(pos,1)}draw&&visible&&tileLayer.redraw()}}return layers,tileLayers}function tileLayersColor(hex){return(new Color).setHex(9===hex.length?"#"+hex.slice(3)+hex.slice(1,3):hex)}const tileLayersLDtkTypes={Int:"int",Float:"float",Bool:"bool",String:"string",Multilines:"string",Color:"color",FilePath:"file"};function tileLayersFromLDtk(ldtk,level=0){var levels=ldtk?.levels?.length?ldtk.levels:(ldtk?.worlds||[]).flatMap(w=>w.levels||[]);const data="string"==typeof level?levels.find(l=>l.identifier===level):levels[level];if(!data)throw new Error("tileLayersFromLDtk: no level "+level);if(!isArray(data.layerInstances))throw new Error("tileLayersFromLDtk: level "+data.identifier+" has no layers, levels saved as separate files are not read");var instances=data.layerInstances;const tiled=l=>"Entities"!==l.__type,first=instances.find(l=>tiled(l)&&null!=l.__tilesetDefUid)||instances.find(tiled)||instances[0];var grid=first?.__gridSize||16;const width=first?.__cWid||ceil(data.pxWid/grid),height=first?.__cHei||ceil(data.pxHei/grid);if(!(0<width&&0<height&&width*height<=1<<22))throw new Error("tileLayersFromLDtk: level "+data.identifier+" is "+width+" by "+height+" cells");const map={width:width,height:height,tilewidth:grid,tileheight:grid,orientation:"orthogonal",renderorder:"right-down",infinite:!1,layers:[],nextlayerid:1,nextobjectid:1};var tileset=(ldtk?.defs?.tilesets||[]).find(t=>t.uid===first?.__tilesetDefUid);tileset&&(map.tilesets=[{firstgid:1,name:String(tileset.relPath||"").replace(/^.*[\\/]/,"").replace(/\.\w+$/,""),image:tileset.relPath,imagewidth:tileset.pxWid,imageheight:tileset.pxHei,tilewidth:tileset.tileGridSize,tileheight:tileset.tileGridSize,margin:tileset.padding||0,spacing:tileset.spacing||0,columns:tileset.__cWid,tilecount:tileset.__cWid*tileset.__cHei}]);for(let i=instances.length;i--;){var instance=instances[i],name=instance.__identifier,id=map.nextlayerid++;if(tiled(instance))if(instance.__gridSize!==grid||instance.__cWid!==width||instance.__cHei!==height)console.warn(`tileLayersFromLDtk: layer ${name} has another grid, left out`);else{const empty=()=>new Array(width*height).fill(0),opacity=instance.__opacity??1;var values="IntGrid"===instance.__type;if(values){const data=empty();(instance.intGridCsv||[]).forEach((value,k)=>k<data.length&&(data[k]=value)),map.layers.push({id:id,name:name,type:"tilelayer",width:width,height:height,data:data,opacity:opacity,visible:!1,x:0,y:0})}var tiles=[...instance.autoLayerTiles||[],...instance.gridTiles||[]].filter(t=>isArray(t.px));if(tiles.length)if(!tileset||instance.__tilesetDefUid===tileset.uid&&tileset.tileGridSize===grid){var tilesName=values?name+" tiles":name;const stacks=[{depth:0,alpha:1,data:empty()}];var depth=new Uint16Array(width*height);for(const t of tiles){var x=floor(t.px[0]/grid),y=floor(t.px[1]/grid),cell=x+y*width;0<=x&&x<width&&0<=y&&y<height&&(((depth,alpha)=>{let s=stacks.find(s=>s.depth===depth&&s.alpha===alpha);return s||stacks.push(s={depth:depth,alpha:alpha,data:empty()}),s.data})(depth[cell]++,round(1e3*clamp(t.a??1))/1e3)[cell]=(t.t+1|(1&t.f?2147483648:0)|(2&t.f?1073741824:0))>>>0)}var visible=!1!==instance.visible;stacks.sort((a,b)=>a.depth-b.depth||b.alpha-a.alpha);for(const{depth,alpha,data}of stacks){var layerName=tilesName+(depth?` (${depth+1})`:"")+(alpha<1?" "+String(alpha).replace(/^0/,""):"");map.layers.push({id:values||depth||alpha<1?map.nextlayerid++:id,name:layerName,type:"tilelayer",width:width,height:height,data:data,opacity:opacity*alpha,visible:visible,x:0,y:0})}}else console.warn(`tileLayersFromLDtk: the tiles of layer ${name} are on another tileset, left out`);else values||map.layers.push({id:id,name:name,type:"tilelayer",width:width,height:height,data:empty(),opacity:opacity,visible:!1!==instance.visible,x:0,y:0})}else{tiles=(instance.entityInstances||[]).filter(entity=>isArray(entity.px)).map(entity=>{var[pivotX=0,pivotY=0]=entity.__pivot||[],w=entity.width||0,h=entity.height||0,properties=[];for(const field of entity.fieldInstances||[]){var type=tileLayersLDtkTypes[field.__type]||/^(Local|Extern)Enum\./.test(field.__type)&&"string";type&&null!=field.__value&&properties.push({name:field.__identifier,type:type,value:field.__value})}return{id:map.nextobjectid++,name:"",type:entity.__identifier,x:entity.px[0]+(.5-pivotX)*w,y:entity.px[1]+(.5-pivotY)*h,width:w,height:h,rotation:0,visible:!0,properties:properties}});map.layers.push({id:id,name:name,type:"objectgroup",objects:tiles,opacity:1,visible:!0,x:0,y:0})}}return map}const objectLayersTypes=new Map;function objectLayersAddType(name,make,defaults={},tileInfo){objectLayersTypes.set(String(name),{make:make,defaults:defaults,tileInfo:tileInfo})}function objectLayersLoad(tileMapData){const made=[],addObjects=dataLayers=>{for(const dataLayer of dataLayers||[])if("group"===dataLayer.type&&addObjects(dataLayer.layers),"objectgroup"===dataLayer.type)for(const object of dataLayer.objects||[]){var result=function(tileMapData,object){var name=object.type||object.class,type=objectLayersTypes.get(name);if(!type)return void(debug&&name&&console.warn(`objectLayersLoad: no type added for ${name}, skipped`));var{height:tileMapData=0,tilewidth=1,tileheight=1}=tileMapData,tilewidth=vec2(object.x/tilewidth,tileMapData-object.y/tileheight),tileMapData=type["make"],result=tileMapData.prototype?new tileMapData(tilewidth):tileMapData(tilewidth);if(result&&"object"==typeof result){for(var[key,value]of Object.entries(function(type,object){var properties={};for(var[key,value]of Object.entries(type.defaults))properties[key]=value?.copy?value.copy():value;for(const property of object.properties||[]){const{name,value}=property;var parts,x,y;"color"===property.type?value&&(properties[name]=tileLayersColor(value)):isVector2(type.defaults[name])?(parts=String(value).split(","),[x,y]=parts.map(Number),2===parts.length&&parts.every(part=>part.trim())&&isNumber(x)&&isNumber(y)&&(properties[name]=vec2(x,y))):properties[name]=value}return properties}(type,object)))"function"==typeof result[key]&&"function"!=typeof value?debug&&console.warn(`objectLayersLoad: ${name} property ${key} has a method's name, left out`):result[key]=value;return result}}(tileMapData,object);dataLayer,object,result&&made.push(result)}};return addObjects(tileMapData?.layers),made}class TileLayerData{constructor(tile,direction=0,mirror=!1,color=new Color){this.tile=tile,this.direction=direction,this.mirror=mirror,this.color=color.copy()}clear(){this.tile=void 0,this.direction=0,this.mirror=!1,this.color=new Color}}class CanvasLayer extends EngineObject{constructor(pos,size,angle=0,renderOrder=0,canvasSize=vec2(512),useWebGL=!0){super(pos,size,void 0,angle,WHITE,renderOrder),this.context=headlessMode?void 0:createCanvasContext(canvasSize.x,canvasSize.y),this.canvas=this.context?.canvas,this.textureInfo=new TextureInfo(this.canvas,useWebGL);pos=useWebGL&&glContext?glContext.getParameter(glContext.MAX_TEXTURE_SIZE):0;pos&&max(canvasSize.x,canvasSize.y)>pos&&console.warn(`LittleJS: a ${canvasSize.x}x${canvasSize.y} layer is over this device's ${pos} pixel texture limit and draws black, split it into smaller layers`),this.mass=0}destroy(immediate=!1){this.destroyed||(this.textureInfo.destroyWebGLTexture(),super.destroy(immediate))}render(){this.draw(this.pos,this.size,this.color,this.angle,this.mirror,this.additiveColor,!1)}draw(pos,size,color=WHITE,angle=0,mirror=!1,additiveColor,screenSpace=drawScreenSpace,context){var t=this.textureInfo,c=this.canvas,c=(!c||this.hasWebGL()||c.width===t.size.x&&c.height===t.size.y||(t.size=vec2(c.width,c.height),t.sizeInverse=vec2(1/c.width,1/c.height)),(new TileInfo).setFullImage(t));drawTile(pos,size,c,color,angle,mirror,additiveColor,!context&&this.hasWebGL(),screenSpace,context)}updateWebGL(){this.textureInfo.createWebGLTexture()}hasWebGL(){return glEnable&&this.textureInfo.hasWebGL()}}function tileLayerDefaultTile(){return textureInfos[0]?.size.x?tile():void 0}class TileLayer extends CanvasLayer{constructor(pos,size,tileInfo=tileLayerDefaultTile(),renderOrder=0,useWebGL=!0){size=size.floor();var canvasSize=tileInfo?size.multiply(tileInfo.size):size;super(pos,size,0,renderOrder,canvasSize,useWebGL),this.tileInfo=void 0,this.data=[],this.isUsingWebGL=!1,this.tilesInWebGL=void 0,this.debugShow=!0,tileInfo&&(this.tileInfo=tileInfo.frame(0),this.tileInfo.bleed=0,this.tileInfo.tiles=tileInfo.tiles);for(let j=this.size.area();j--;)this.data.push(new TileLayerData);headlessMode&&(this.render=()=>{},this.redraw=()=>{},this.redrawStart=()=>{},this.redrawEnd=()=>{},this.drawTileData=()=>{},this.redrawTileData=()=>{},this.drawLayerTile=()=>{},this.drawLayerRect=()=>{},this.drawTile=()=>{},this.drawRect=()=>{},this.clearLayerRect=()=>{})}setData(layerPos,data,redraw=!1){(layerPos=layerPos.floor()).arrayCheck(this.size)&&(this.data[(0|layerPos.y)*this.size.x+(0|layerPos.x)]=data,redraw)&&(drawContext===this.context?this.drawTileData(layerPos):this.redrawTileData(layerPos))}clearData(layerPos,redraw=!1){this.setData(layerPos,new TileLayerData,redraw)}getData(layerPos){return layerPos.arrayCheck(this.size)?this.data[(0|layerPos.y)*this.size.x+(0|layerPos.x)]:void 0}update(){}render(){this.redrawIfSwitched();var size=this.drawSize||this.size,pos=this.pos.add(size.scale(.5));this.draw(pos,size,this.color,this.angle,this.mirror,this.additiveColor,!1)}onRedraw(){}redraw(){this.redrawStart(!0);for(let x=this.size.x;x--;)for(let y=this.size.y;y--;)this.drawTileData(vec2(x,y),!1);this.isUsingWebGL&&glFlush(),this.onRedraw(),this.redrawEnd(),this.tilesInWebGL=this.isUsingWebGL}redrawIfSwitched(){void 0!==this.tilesInWebGL&&this.hasWebGL()!==this.tilesInWebGL&&this.redraw()}redrawStart(clear=!1){var tileSize;this.context&&(clear||this.redrawIfSwitched(),this.savedRenderSettings=[drawContext,mainCanvasSize,cameraPos,cameraScale,cameraAngle,canvasClearColor,glCustomShader],setShader(),this.savedRenderTarget=[glRenderTarget,glColorMask,glColorAdditive,glSkipScreenSpace],glColorMask=-1,glColorAdditive=0,glSkipScreenSpace=!1,drawContext=this.context,tileSize=this.tileInfo?.size??vec2(1),mainCanvasSize=this.size.multiply(tileSize),canvasClearColor=CLEAR_BLACK,cameraPos=this.size.multiply(tileSize).scale(.5),cameraScale=1,cameraAngle=0,this.isUsingWebGL=this.hasWebGL(),this.isUsingWebGL?glSetRenderTarget(this.textureInfo.glTexture,clear):(clear&&(this.canvas.width=mainCanvasSize.x,this.canvas.height=mainCanvasSize.y),this.context.imageSmoothingEnabled=!tilesPixelated))}redrawEnd(){var target,colorMask,colorAdditive,skipScreenSpace;this.context&&([drawContext,mainCanvasSize,cameraPos,cameraScale,cameraAngle,canvasClearColor,glCustomShader]=this.savedRenderSettings,[target,colorMask,colorAdditive,skipScreenSpace]=this.savedRenderTarget,this.isUsingWebGL&&glSetRenderTarget(target),glColorMask=colorMask,glColorAdditive=colorAdditive,glSkipScreenSpace=skipScreenSpace)}drawTileData(layerPos,clear=!0){var cellPixels,drawPos,tileInfo;this.context&&(cellPixels=this.tileInfo?.size??vec2(1),drawPos=layerPos.multiply(cellPixels),clear&&this.clearLayerRect(drawPos,cellPixels),clear=this.getData(layerPos))&&void 0!==clear.tile&&(tileInfo=function(t,tile){if(t)return t.tiles?t.tiles[tile]:t.columns?t.textureInfo&&t.pos.y+(tile/t.columns|0)*(t.size.y+2*t.padding)+t.size.y>t.textureInfo.size.y?void 0:t.frame(tile):t.index(tile)}(layerPos=this.tileInfo,clear.tile),layerPos&&!tileInfo||this.drawLayerTile(drawPos,cellPixels,tileInfo,clear.color,clear.direction*PI/2,clear.mirror))}redrawTileData(layerPos,clear=!0){this.context&&(this.redrawStart(),this.drawTileData(layerPos,clear),this.redrawEnd())}drawLayerTile(pos,size=vec2(1),tileInfo,color=WHITE,angle=0,mirror,additiveColor){drawTile(pos.add(size.scale(.5)),size,tileInfo,color,angle,mirror,additiveColor,this.isUsingWebGL,!1)}drawLayerRect(pos,size,color,angle=0){this.drawLayerTile(pos,size,void 0,color,angle)}drawTile(pos,size=vec2(1),tileInfo,color=new Color,angle=0,mirror=!1){var tileSize=this.tileInfo?.size??vec2(1),tileSize=(pos=pos.subtract(this.pos).multiply(tileSize),size=size.multiply(tileSize),pos.x-=.5,pos.y=this.canvas.height-pos.y-.5,mainCanvasSize),oldTarget=glRenderTarget,oldColorMask=glColorMask,oldSkip=glSkipScreenSpace,oldColorAdditive=glColorAdditive,oldShader=glCustomShader,useWebGL=(mainCanvasSize=vec2(this.canvas.width,this.canvas.height),glColorMask=-1,glColorAdditive=0,setShader(),glSkipScreenSpace=!1,this.hasWebGL()),drawContext=(useWebGL&&glSetRenderTarget(this.textureInfo.glTexture),useWebGL?void 0:this.context);drawTile(pos,size,tileInfo,color,angle,mirror,void 0,useWebGL,!0,drawContext),mainCanvasSize=tileSize,useWebGL&&glSetRenderTarget(oldTarget),glColorMask=oldColorMask,glColorAdditive=oldColorAdditive,glSkipScreenSpace=oldSkip,setShader(oldShader)}drawRect(pos,size,color,angle){this.drawTile(pos,size,void 0,color,angle)}clearLayerRect(pos,size){var x=pos.x,pos=this.canvas.height-pos.y-size.y;this.hasWebGL()?glClearRect(x,pos,size.x,size.y):this.context.clearRect(x,pos,size.x,size.y)}}class TileCollisionLayer extends TileLayer{constructor(pos,size,tileInfo=tileLayerDefaultTile(),renderOrder=0,useWebGL=!0){super(pos,size.floor(),tileInfo,renderOrder,useWebGL),this.collisionData=[],this.initCollision(this.size),tileCollisionLayers.push(this),this.isSolid=!0,this.shadowSolidOnly=!0}renderShadow(){if(!this.shadowSolidOnly||this.angle||this.mirror)return super.renderShadow();var size=this.size,cellWorld=(this.drawSize||size).divide(size),cellPixels=this.tileInfo?this.tileInfo.size:vec2(1),view=getCameraSize().scale(.5),low=cameraPos.subtract(view),view=cameraPos.add(view),x0=max(0,floor((low.x-this.pos.x)/cellWorld.x)),x1=min(size.x,ceil((view.x-this.pos.x)/cellWorld.x)),low=max(0,floor((low.y-this.pos.y)/cellWorld.y)),y1=min(size.y,ceil((view.y-this.pos.y)/cellWorld.y)),textureHeight=size.y*cellPixels.y,useWebGL=this.hasWebGL();for(let y=low;y<y1;++y)for(let x=x0;x<x1;++x)if(0<this.collisionData[y*size.x+x]){let end=x+1;for(;end<x1&&0<this.collisionData[y*size.x+end];)++end;var count=end-x,tileInfo=new TileInfo(vec2(x*cellPixels.x,textureHeight-(y+1)*cellPixels.y),vec2(count*cellPixels.x,cellPixels.y),this.textureInfo,0,0);drawTile(vec2(this.pos.x+(x+count/2)*cellWorld.x,this.pos.y+(y+.5)*cellWorld.y),vec2(count*cellWorld.x,cellWorld.y),tileInfo,this.color,0,!1,void 0,useWebGL,!1),x=end}}destroy(immediate=!1){var index;this.destroyed||(0<=(index=tileCollisionLayers.indexOf(this))&&tileCollisionLayers.splice(index,1),super.destroy(immediate))}initCollision(size){this.size=size.floor(),this.collisionData=new Array(this.size.area()).fill(0)}setCollisionData(layerPos,data=1){var i=(0|layerPos.y)*this.size.x+(0|layerPos.x);layerPos.arrayCheck(this.size)&&(this.collisionData[i]=data)}clearCollisionData(layerPos){this.setCollisionData(layerPos,0)}getCollisionData(layerPos){var i=(0|layerPos.y)*this.size.x+(0|layerPos.x);return layerPos.arrayCheck(this.size)?this.collisionData[i]:0}collisionTest(pos,size=new Vector2,callbackObject){var collisionTest=tileCollisionTester(callbackObject),callbackObject=pos.x-this.pos.x,pos=pos.y-this.pos.y;if(!(callbackObject+size.x/2<0||callbackObject-size.x/2>this.size.x||pos+size.y/2<0||pos-size.y/2>this.size.y)){var minX=max(callbackObject-size.x/2|0,0),minY=max(pos-size.y/2|0,0),maxX=min(size.x?callbackObject+size.x/2:minX+1,this.size.x),maxY=min(size.y?pos+size.y/2:minY+1,this.size.y);for(let y=minY;y<maxY;++y)for(let x=minX;x<maxX;++x){var tileData=this.collisionData[y*this.size.x+x];if(tileData&&collisionTest(tileData,vec2(x+this.pos.x,y+this.pos.y)))return!0}}return!1}collisionRaycast(posStart,posEnd,callbackObject,normal){const collisionTest=tileCollisionTester(callbackObject),offset=this.pos;var p,q,high,callbackObject=posStart.subtract(offset),posStart=posEnd.subtract(posStart);let t0=0,t1=1;for([p,q,high]of[[callbackObject.x,posStart.x,this.size.x+1],[callbackObject.y,posStart.y,this.size.y+1]])if(q){var u=(-1-p)/q,v=(high-p)/q;t0=max(t0,min(u,v)),t1=min(t1,max(u,v))}else if(p<-1||p>high)return;if(!(t0>t1))return(callbackObject=lineTest(t0?callbackObject.add(posStart.scale(t0)):callbackObject,t1<1?callbackObject.add(posStart.scale(t1)):posEnd.subtract(offset),pos=>{var tileData=this.getCollisionData(pos);return tileData&&collisionTest(tileData,vec2(pos.x+offset.x,pos.y+offset.y))},normal))&&(posStart=callbackObject.floor(),callbackObject.x+=offset.x,callbackObject.y+=offset.y,callbackObject.x=(posEnd=(v,low)=>v<low?low:v<low+1?v:low+1-max(1e-9,1e-15*abs(low)))(callbackObject.x,posStart.x+offset.x),callbackObject.y=posEnd(callbackObject.y,posStart.y+offset.y)),callbackObject}}class ParticleEmitter extends EngineObject{constructor(pos,angle,emitSize=0,emitTime=0,emitRate=100,emitConeAngle=PI,tileInfo,colorStartA=WHITE,colorStartB=WHITE,colorEndA=CLEAR_WHITE,colorEndB=CLEAR_WHITE,particleTime=.5,sizeStart=.1,sizeEnd=1,speed=.1,angleSpeed=.05,damping=1,angleDamping=1,gravityScale=0,particleConeAngle=PI,fadeRate=.1,randomness=.2,collideLevel=!1,additive=!1,randomColorLinear=!0,renderOrder=additive?1e9:0,localSpace=!1){if(super(pos,vec2(),tileInfo,angle,void 0,renderOrder),debug){var numbers={emitTime:emitTime,emitRate:emitRate,emitConeAngle:emitConeAngle,particleTime:particleTime,sizeStart:sizeStart,sizeEnd:sizeEnd,speed:speed,angleSpeed:angleSpeed,damping:damping,angleDamping:angleDamping,gravityScale:gravityScale,particleConeAngle:particleConeAngle,fadeRate:fadeRate,randomness:randomness};var colors={colorStartA:colorStartA,colorStartB:colorStartB,colorEndA:colorEndA,colorEndB:colorEndB}}this.emitCircle="number"==typeof emitSize,this.emitSize="number"==typeof emitSize?vec2(emitSize):emitSize.copy(),this.emitTime=emitTime,this.emitRate=emitRate,this.emitConeAngle=emitConeAngle,this.colorStartA=colorStartA.copy(),this.colorStartB=colorStartB.copy(),this.colorEndA=colorEndA.copy(),this.colorEndB=colorEndB.copy(),this.randomColorLinear=randomColorLinear,this.particleTime=particleTime,this.sizeStart=sizeStart,this.sizeEnd=sizeEnd,this.speed=speed,this.angleSpeed=angleSpeed,this.damping=damping,this.angleDamping=angleDamping,this.gravityScale=gravityScale,this.particleConeAngle=particleConeAngle,this.fadeRate=fadeRate,this.randomness=randomness,this.collideLevel=collideLevel,this.additive=additive,this.localSpace=localSpace,this.trailScale=0,this.scale=1,this.gravity=0,this.particleCreateCallback=void 0,this.particleDestroyCallback=void 0,this.particleCollideCallback=void 0,this.particleUpdateCallback=void 0,this.velocityInheritance=0,this.emitTimeBuffer=1,this.emitElapsed=0,this.particles=[],this.previousPos=void 0,this.previousAngle=this.angle,this.emitFrom=vec2(),this.emitBehind=0}update(){if(this.previousPos||(this.previousPos=this.pos.copy(),this.previousAngle=this.angle),this.velocityInheritance&&(p=this.velocityInheritance,this.velocity.x=p*(this.pos.x-this.previousPos.x),this.velocity.y=p*(this.pos.y-this.previousPos.y),this.angleVelocity=p*(this.angle-this.previousAngle)),this.previousAngle=this.angle,this.emitFrom.x=this.previousPos.x,this.emitFrom.y=this.previousPos.y,this.previousPos.x=this.pos.x,this.previousPos.y=this.pos.y,this.isActive()){var p=this.emitRate*particleEmitRateScale,step=this.emitTime?min(timeDelta,this.emitTime-this.emitElapsed):timeDelta;if(this.emitElapsed+=timeDelta,0<p&&p<1/0){this.emitTimeBuffer+=p*step+(this.emitTime&&this.emitElapsed>=this.emitTime?1e-6:0);var travelled=this.emitFrom&&this.pos.distance(this.emitFrom)<=2*objectMaxSpeed,count=floor(this.emitTimeBuffer);try{for(let i=count;i--;--this.emitTimeBuffer)this.emitBehind=travelled?i/count:0,this.emitParticle()}finally{this.emitBehind=0}}}else 0===this.particles.length&&this.destroy(!0);var particles=this.particles;let alive=0;for(let i=0;i<particles.length;++i){const p=particles[i];p.update(),p.destroyed||(particles[alive++]=p)}particles.length=alive}emitParticle(){var scale=this.scale;let pos=this.emitCircle?randInCircle(this.emitSize.x/2*scale):vec2(rand(-.5,.5)*this.emitSize.x*scale,rand(-.5,.5)*this.emitSize.y*scale),angle=rand(this.particleConeAngle,-this.particleConeAngle);this.localSpace||(this.emitCircle||(pos=pos.rotate(this.angle)),behind=this.emitBehind,pos.x+=this.pos.x+(this.emitFrom.x-this.pos.x)*behind,pos.y+=this.pos.y+(this.emitFrom.y-this.pos.y)*behind,angle+=this.angle);const randomness=this.randomness;var behind=v=>v+v*rand(randomness,-randomness),particleTime=behind(this.particleTime),sizeStart=behind(this.sizeStart)*scale,sizeEnd=behind(this.sizeEnd)*scale,scale=behind(this.speed)*scale,behind=behind(this.angleSpeed)*randSign(),coneAngle=rand(this.emitConeAngle,-this.emitConeAngle),colorStart=randColor(this.colorStartA,this.colorStartB,this.randomColorLinear),colorEnd=randColor(this.colorEndA,this.colorEndB,this.randomColorLinear),coneAngle=this.localSpace?coneAngle:this.angle+coneAngle,scale=vec2(scale*sin(coneAngle),scale*cos(coneAngle));let angleVelocity=behind;!this.localSpace&&0<this.velocityInheritance&&(scale.x+=this.velocity.x,scale.y+=this.velocity.y,angleVelocity+=this.angleVelocity);coneAngle=new Particle(this,pos,angle,colorStart,colorEnd,particleTime,sizeStart,sizeEnd,scale,angleVelocity);return this.particles.push(coneAngle),this.particleCreateCallback?.(coneAngle),coneAngle}updatePhysics(){}render(){this.additive&&setAdditiveBlendMode(),particleTile=null,particleTileBatch=!0;for(const particle of this.particles)particle.render();particleTileBatch=!1,this.additive&&setAdditiveBlendMode(!1)}isActive(){return!this.emitTime||this.emitElapsed<this.emitTime}destroy(immediate=!1){this.destroyed||(super.destroy(immediate),!immediate&&0<this.particles.length&&(this.destroyed=!1,this.emitTime=-1))}}const particleDrawPos=new Vector2,particleDrawSize=new Vector2,particleCollidePos=new Vector2;let particleTile,particleTileBatch=!1,particleTileTextured=!1,particleTileEmpty=!1,particleTileU0=0,particleTileV0=0,particleTileU1=0,particleTileV1=0;function particleCollideTest(particle,collideCallback,x,y){var data=tileCollisionGetData(particleCollidePos.set(x,y));return collideCallback?!!data&&!!collideCallback(particle,data,vec2(x,y)):0<data}class Particle{constructor(emitter,pos,angle,colorStart,colorEnd,lifeTime,sizeStart,sizeEnd,velocity=vec2(),angleVelocity=0){this.emitter=emitter,this.scale=emitter.scale??1,this.pos=pos,this.angle=angle,this.size=vec2(sizeStart),this.color=colorStart.copy(),this.colorStart=colorStart,this.colorEnd=colorEnd,this.lifeTime=lifeTime,this.sizeStart=sizeStart,this.sizeEnd=sizeEnd,this.velocity=velocity,this.angleVelocity=angleVelocity,this.spawnTime=time,this.mirror=randBool(),this.groundObject=void 0,this.destroyed=!1,this.tileInfo=emitter.tileInfo}update(){if(!this.destroyed){var emitter=this.emitter,damping=emitter.damping,angleDamping=emitter.angleDamping,restitution=emitter.restitution,friction=emitter.friction,gravityScale=emitter.gravityScale,collideLevel=emitter.collideLevel,collideCallback=emitter.particleCollideCallback,updateCallback=emitter.particleUpdateCallback;if(this.lifeTime<=0||time-this.spawnTime>this.lifeTime)this.destroy();else{var collideLevel=enablePhysicsSolver&&collideLevel,oldX=this.pos.x,oldY=this.pos.y,fall=this.scale;let gravityX=gravity.x*gravityScale*fall,gravityY=(gravity.y*gravityScale+(emitter.gravity||0))*fall;if(emitter.localSpace&&emitter.angle&&(gravityScale=cos(emitter.angle),fall=sin(emitter.angle),[gravityX,gravityY]=[gravityX*gravityScale-gravityY*fall,gravityX*fall+gravityY*gravityScale]),this.velocity.x=this.velocity.x*damping+gravityX,this.velocity.y=this.velocity.y*damping+gravityY,collideLevel){var emitter=this.velocity.lengthSquared();if(emitter>objectMaxSpeed*objectMaxSpeed){const s=objectMaxSpeed/emitter**.5;this.velocity.x*=s,this.velocity.y*=s}}this.pos.x+=this.velocity.x,this.pos.y+=this.velocity.y,this.angle+=this.angleVelocity*=angleDamping,collideLevel&&(this.groundObject=void 0,particleCollideTest(this,collideCallback,this.pos.x,this.pos.y)&&(fall=tileCollisionTest(this.pos),particleCollideTest(this,collideCallback,oldX,oldY)||(gravityScale=particleCollideTest(this,collideCallback,this.pos.x,oldY),damping=particleCollideTest(this,collideCallback,oldX,this.pos.y),emitter=fall?max(restitution,fall.restitution):restitution,angleDamping=fall?max(friction,fall.friction):friction,gravityScale&&(this.pos.x=oldX,this.velocity.x*=-emitter,this.velocity.y*=angleDamping),!damping&&gravityScale)||(collideLevel=gravityY,(this.velocity.y<0&&collideLevel<0||0<this.velocity.y&&0<collideLevel)&&(this.groundObject=fall),this.pos.y=oldY,this.velocity.y*=-emitter,this.velocity.x*=angleDamping)),this.destroyed)||updateCallback?.(this)}}}destroy(){var destroyCallback,c;this.destroyed||(destroyCallback=this.emitter.particleDestroyCallback,c=this.colorEnd,this.color.set(c.r,c.g,c.b,c.a),this.size.set(this.sizeEnd,this.sizeEnd),this.destroyed=!0,destroyCallback?.(this))}render(){var s,emitter=this.emitter,localSpace=emitter.localSpace,additive=emitter.additive&&!glAdditive,trailScale=emitter.trailScale,fadeRate=emitter.fadeRate/2,p1=0<this.lifeTime?min((time-this.spawnTime)/this.lifeTime,1):1,p2=1-p1,sizeNow=p2*this.sizeStart+p1*this.sizeEnd,sizeNow=(this.size.set(sizeNow,sizeNow),particleDrawSize.set(sizeNow,sizeNow)),fadeRate=p1<fadeRate?p1/fadeRate:1-fadeRate<p1?(1-p1)/fadeRate:1,p2=(this.color.r=p2*this.colorStart.r+p1*this.colorEnd.r,this.color.g=p2*this.colorStart.g+p1*this.colorEnd.g,this.color.b=p2*this.colorStart.b+p1*this.colorEnd.b,this.color.a=(p2*this.colorStart.a+p1*this.colorEnd.a)*fadeRate,particleDrawPos.set(this.pos.x,this.pos.y));let angle=this.angle;localSpace&&(p1=emitter.angle,fadeRate=cos(-p1),s=sin(-p1),p2.set(emitter.pos.x+p2.x*fadeRate-p2.y*s,emitter.pos.y+p2.x*s+p2.y*fadeRate),angle+=p1),additive&&setAdditiveBlendMode(),trailScale&&(fadeRate=(s=localSpace?this.velocity.rotate(emitter.angle):this.velocity).length())&&(sizeNow.y=max(sizeNow.x,fadeRate*trailScale),angle=atan2(s.x,s.y)),glEnable&&!headlessMode?(p1=this.tileInfo,particleTileBatch&&p1===particleTile||function(tileInfo){var bleed,x,y,w,bleedX,textureInfo=(particleTile=tileInfo)?.textureInfo;particleTileTextured=!!textureInfo,particleTileEmpty=!(!textureInfo||tileInfo.size.x&&tileInfo.size.y),textureInfo&&!particleTileEmpty&&(glSetTexture(textureInfo.glTexture),textureInfo=textureInfo.sizeInverse,bleed=tileInfo.bleed,x=tileInfo.pos.x*textureInfo.x,y=tileInfo.pos.y*textureInfo.y,w=tileInfo.size.x*textureInfo.x,tileInfo=tileInfo.size.y*textureInfo.y,particleTileV1=bleed?(bleedX=textureInfo.x*bleed,textureInfo=textureInfo.y*bleed,particleTileU0=x+bleedX,particleTileV0=y+textureInfo,particleTileU1=x-bleedX+w,y-textureInfo+tileInfo):(particleTileU0=x,particleTileV0=y,particleTileU1=x+w,y+tileInfo))}(p1),particleTileTextured?particleTileEmpty||(localSpace=p1.textureInfo.glTexture,glActiveTexture!==localSpace&&glSetTexture(localSpace),glDraw(p2.x,p2.y,this.mirror?-sizeNow.x:sizeNow.x,sizeNow.y,angle,particleTileU0,particleTileV0,particleTileU1,particleTileV1,this.color.rgbaInt())):glDrawUntextured(p2.x,p2.y,sizeNow.x,sizeNow.y,angle,this.color.rgbaInt())):drawTile(p2,sizeNow,this.tileInfo,this.color,angle,this.mirror,void 0,!1,!1),additive&&setAdditiveBlendMode(!1)}}let glCanvas,glContext,glMaxTextureSize=0;function glClampTextureSize(size){return glMaxTextureSize?min(size,glMaxTextureSize):size}let glAntialias=!0,glMipmappedTextures=new WeakSet,glMipmapsUntilTarget=new WeakSet,glMipmapsStale=new Set,glPremultipliedTextures=new WeakSet,glShaderPremultiplied,glEnableBeforeLoss=!0,glShader,glPolyShader,glPolyMode,glAdditive,glBatchAdditive,glActiveTexture,glArrayBuffer,glGeometryBuffer,glPositionData,glColorData,glBatchCount,glTextureInfos=new Set,glInstancedVAO,glPolyVAO,glFramebuffer,glRenderTarget,glShaderObjects=[],glCustomShader,glBatchShader,glProgramCustom,glTransform,glRenderTargetSaved,glUniformLocations=new WeakMap,glCanBeEnabled=!0,glDeviceFailed=!1,glFailedPrograms=new WeakSet,glColorMask=-1,glColorAdditive=0,glRenderTargetBase,glSkipScreenSpace=!1;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;}",gl_FRAGMENT_TINT_SOURCE="c=t*d+e;if(premultipliedTexture){c.a=min(c.a,1.);c.rgb=min(t.rgb*d.rgb*min(d.a+e.a,1.)+e.rgb*c.a,c.a);}";function glSetInstancedMode(force=!1){(force||glPolyMode)&&(glFlush(),glPolyMode=!1,glContext.useProgram(glShader),glContext.bindVertexArray(glInstancedVAO),glContext.bindBuffer(glContext.ARRAY_BUFFER,glArrayBuffer))}function glPreRender(clear=!0){var dpr,sa,bufferSizeX;glEnable&&glContext&&(dpr=glRenderTarget?1:getCanvasPixelRatio(),bufferSizeX=mainCanvasSize.x*dpr|0,dpr=mainCanvasSize.y*dpr|0,glRenderTarget||glCanvas.width===bufferSizeX&&glCanvas.height===dpr||(glCanvas.width=bufferSizeX,glCanvas.height=dpr),glContext.viewport(0,0,bufferSizeX,dpr),clear&&glClearCanvas(),bufferSizeX=vec2(2*cameraScale).divide(mainCanvasSize),glRenderTarget&&(bufferSizeX.y=-bufferSizeX.y),dpr=cameraPos.rotate(-cameraAngle),clear=vec2(-1).subtract(dpr.multiply(bufferSizeX)),dpr=cos(cameraAngle),sa=sin(cameraAngle),sa=[bufferSizeX.x*dpr,bufferSizeX.y*sa,0,0,-bufferSizeX.x*sa,bufferSizeX.y*dpr,0,0,1,1,1,0,clear.x,clear.y,0,1],glTransform=sa,(bufferSizeX=(program,uniform,value)=>{glContext.useProgram(program),glContext.uniformMatrix4fv(glUniformLocation(program,uniform),!1,value)})(glPolyShader,"m",sa),bufferSizeX(glShader,"m",sa),glContext.activeTexture(glContext.TEXTURE0),textureInfos[0]&&(glActiveTexture=textureInfos[0].glTexture,glContext.bindTexture(glContext.TEXTURE_2D,glActiveTexture)),glContext.bindBuffer(glContext.ARRAY_BUFFER,glArrayBuffer),glSetInstancedMode(!(glAdditive=glBatchAdditive=!1)))}function glClearCanvas(){var color;glContext&&(color=canvasClearColor,glContext.clearColor(color.r*color.a,color.g*color.a,color.b*color.a,color.a),glContext.clear(glContext.COLOR_BUFFER_BIT))}function glSetTexture(texture){glContext&&(texture!==glActiveTexture&&(glFlush(),glActiveTexture=texture,glContext.bindTexture(glContext.TEXTURE_2D,glActiveTexture)),glMipmapsStale.size)&&glUpdateMipmaps(texture)}function glUpdateMipmaps(texture){glMipmapsStale.has(texture)&&(glMipmapsStale.delete(texture),glContext.generateMipmap(glContext.TEXTURE_2D))}const glWrapRepeat=Object.freeze([10497,10497]),glWrapClamp=Object.freeze([33071,33071]);function glWrapModes(wrap){return isArray(wrap)?wrap:wrap?glWrapRepeat:glWrapClamp}function glSetTextureWrap(texture,wrap=!0){var isCurrent;glContext&&texture&&([texture,wrap]=((isCurrent=texture===glActiveTexture)?glFlush():glContext.bindTexture(glContext.TEXTURE_2D,texture),glWrapModes(wrap)),glContext.texParameteri(glContext.TEXTURE_2D,glContext.TEXTURE_WRAP_S,texture),glContext.texParameteri(glContext.TEXTURE_2D,glContext.TEXTURE_WRAP_T,wrap),!isCurrent)&&glActiveTexture&&glContext.bindTexture(glContext.TEXTURE_2D,glActiveTexture)}function glCompileShader(source,type){if(glContext){type=glContext.createShader(type);if(glContext.shaderSource(type,source),glContext.compileShader(type),debug&&!glContext.getShaderParameter(type,glContext.COMPILE_STATUS))throw glContext.getShaderInfoLog(type);return type}}function glCreateProgram(vsSource,fsSource){if(glContext){var program=glContext.createProgram(),vsSource=glCompileShader(vsSource,glContext.VERTEX_SHADER),fsSource=glCompileShader(fsSource,glContext.FRAGMENT_SHADER);if(glContext.attachShader(program,vsSource),glContext.attachShader(program,fsSource),glContext.linkProgram(program),!glContext.getProgramParameter(program,glContext.LINK_STATUS)){vsSource=glContext.getProgramInfoLog(program)+"\n"+glContext.getShaderInfoLog(vsSource)+glContext.getShaderInfoLog(fsSource);if(debug)throw vsSource;console.error("LittleJS: a shader failed to build\n"+vsSource),glFailedPrograms.add(program)}return program}}function glUniformLocation(program,name){let cache=glUniformLocations.get(program);return cache||glUniformLocations.set(program,cache={}),cache[name]??=glContext.getUniformLocation(program,name)}function glShaderTrack(shader){glShaderObjects.includes(shader)||glShaderObjects.push(shader)}function glCreateTexture(image,wrap=!1,pixelated=tilesPixelated){if(glContext){var texture=glContext.createTexture();let mipMap=!1;image?.width?(glSetTextureData(texture,image,pixelated),glContext.bindTexture(glContext.TEXTURE_2D,texture),!(mipMap=!pixelated)||isPowerOfTwo(image.width)&&isPowerOfTwo(image.height)||glMipmapsUntilTarget.add(texture)):(image=new Uint8Array([255,255,255,255]),glContext.bindTexture(glContext.TEXTURE_2D,texture),glContext.texImage2D(glContext.TEXTURE_2D,0,glContext.RGBA,1,1,0,glContext.RGBA,glContext.UNSIGNED_BYTE,image));var image=pixelated?glContext.NEAREST:glContext.LINEAR,pixelated=mipMap?glContext.LINEAR_MIPMAP_LINEAR:image,[image,pixelated]=(glContext.texParameteri(glContext.TEXTURE_2D,glContext.TEXTURE_MAG_FILTER,image),glContext.texParameteri(glContext.TEXTURE_2D,glContext.TEXTURE_MIN_FILTER,pixelated),glWrapModes(wrap));return glContext.texParameteri(glContext.TEXTURE_2D,glContext.TEXTURE_WRAP_S,image),glContext.texParameteri(glContext.TEXTURE_2D,glContext.TEXTURE_WRAP_T,pixelated),mipMap&&(glContext.generateMipmap(glContext.TEXTURE_2D),glMipmappedTextures.add(texture)),glContext.bindTexture(glContext.TEXTURE_2D,glActiveTexture),texture}}function glDeleteTexture(texture){glContext&&texture&&(texture===glActiveTexture&&(glFlush(),glActiveTexture=void 0),glMipmapsStale.delete(texture),glPremultipliedTextures.delete(texture),glContext.deleteTexture(texture))}function glSetTextureData(texture,image,pixelated=tilesPixelated){var context;glContext&&(texture===glActiveTexture&&glFlush(),glContext.bindTexture(glContext.TEXTURE_2D,texture),"undefined"!=typeof ImageBitmap&&image instanceof ImageBitmap&&((context=createCanvasContext(image.width,image.height)).drawImage(image,0,0),image=context.canvas),context=!pixelated,glContext.pixelStorei(glContext.UNPACK_PREMULTIPLY_ALPHA_WEBGL,context),glContext.texImage2D(glContext.TEXTURE_2D,0,glContext.RGBA,glContext.RGBA,glContext.UNSIGNED_BYTE,image),glContext.pixelStorei(glContext.UNPACK_PREMULTIPLY_ALPHA_WEBGL,!1),context?glPremultipliedTextures.add(texture):glPremultipliedTextures.delete(texture),glMipmappedTextures.has(texture)&&glContext.generateMipmap(glContext.TEXTURE_2D),glContext.bindTexture(glContext.TEXTURE_2D,glActiveTexture))}function glFlush(){if(glEnable&&glContext&&glBatchCount){var height,premultiplied=!glPolyMode&&glPremultipliedTextures.has(glActiveTexture),sourceBlend=premultiplied?glContext.ONE:glContext.SRC_ALPHA,destBlend=glBatchAdditive?glContext.ONE:glContext.ONE_MINUS_SRC_ALPHA;if(glContext.blendFuncSeparate(sourceBlend,destBlend,glContext.ONE,destBlend),glContext.enable(glContext.BLEND),!glPolyMode&&(glBatchShader||glProgramCustom)){const program=!glBatchShader||((sourceBlend=glBatchShader).program||glShaderTrack(sourceBlend),sourceBlend=sourceBlend.program||=glCreateProgram(gl_VERTEX_SOURCE,"#version 300 es\nprecision highp float;uniform sampler2D iChannel0;uniform vec3 iResolution;uniform float iTime;uniform bool premultipliedTexture;in vec2 v,l;in vec4 d,e;out vec4 c;void mainImage(out vec4,vec2);void main(){vec4 t;mainImage(t,v);"+gl_FRAGMENT_TINT_SOURCE+"}\n#define localUV l\n"+sourceBlend.fragmentCode+"\n"),glFailedPrograms.has(sourceBlend))?glShader:sourceBlend;glContext.useProgram(program),glProgramCustom=!!glBatchShader,glBatchShader&&(destBlend=name=>glUniformLocation(program,name),glContext.uniformMatrix4fv(destBlend("m"),!1,glTransform),glContext.uniform1f(destBlend("iTime"),time),sourceBlend=glRenderTarget?mainCanvasSize.x:glCanvas.width,height=glRenderTarget?mainCanvasSize.y:glCanvas.height,glContext.uniform3f(destBlend("iResolution"),sourceBlend,height,1),glContext.uniform1i(destBlend("premultipliedTexture"),+premultiplied),program===glShader)&&(glShaderPremultiplied=premultiplied)}glPolyMode||glBatchShader||glShaderPremultiplied===premultiplied||(glContext.uniform1i(glUniformLocation(glShader,"premultipliedTexture"),+premultiplied),glShaderPremultiplied=premultiplied);sourceBlend=glBatchCount*(glPolyMode?gl_INDICES_PER_POLY_VERTEX:gl_INDICES_PER_INSTANCE);glContext.bufferSubData(glContext.ARRAY_BUFFER,0,glPositionData,0,sourceBlend),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(context){glEnable&&glContext&&(glFlush(),context.drawImage(glCanvas,0,0))}function glSetAntialias(antialias=!0){glAntialias=antialias}function glDraw(x,y,sizeX,sizeY,angle=0,uv0X=0,uv0Y=0,uv1X=1,uv1Y=1,rgba=-1,rgbaAdditive=0){(glBatchCount>=gl_MAX_INSTANCES||glBatchAdditive!==glAdditive||glBatchShader!==glCustomShader)&&glFlush(),glSetInstancedMode();var offset=glBatchCount++*gl_INDICES_PER_INSTANCE;glPositionData[offset++]=x,glPositionData[offset++]=y,glPositionData[offset++]=sizeX,glPositionData[offset++]=sizeY,glPositionData[offset++]=uv0X,glPositionData[offset++]=uv0Y,glPositionData[offset++]=uv1X,glPositionData[offset++]=uv1Y,glColorData[offset++]=rgba&glColorMask,glColorData[offset++]=rgbaAdditive&glColorMask|glColorAdditive,glPositionData[+offset]=angle}function glDrawUntextured(x,y,sizeX,sizeY,angle,rgba){glDraw(x,y,sizeX,sizeY,angle,0,0,0,0,0,rgba)}const glPointsOut=[],glPointsPool=[];function glDrawPointsTransform(points,rgba,x,y,sx,sy,angle,tristrip=!0){var pointsOut=glPointsOut,pool=glPointsPool,count=points.length,sa=(pointsOut.length=count,sin(-angle)),ca=cos(-angle);for(let i=0;i<count;++i){var p=points[i],out=pool[i]||=vec2(),px=p.x*sx,p=p.y*sy;out.x=x+ca*px-sa*p,out.y=y+sa*px+ca*p,pointsOut[i]=out}glDrawPoints(tristrip?glPolyStrip(pointsOut):pointsOut,rgba)}function glDrawOutlineTransform(points,rgba,lineWidth,x,y,sx,sy,angle,wrap=!0){glDrawPointsTransform(function(points,width,wrap=!0){if(points.length<2)return[];var halfWidth=width/2,strip=glOutlineStrip,pool=glOutlinePool,n=(strip.length=0,points.length),e=1e-6;for(let i=0;i<n;i++){var prev=points[wrap?(i-1+n)%n:max(i-1,0)],curr=points[i],next=points[wrap?(i+1)%n:min(i+1,n-1)],dx1=curr.x-prev.x,prev=curr.y-prev.y,len1=(dx1*dx1+prev*prev)**.5,dx2=next.x-curr.x,next=next.y-curr.y,len2=(dx2*dx2+next*next)**.5;if(!(len1<e&&len2<e)){prev=e<len1?-prev/len1:0,dx1=e<len1?dx1/len1:0;let nx=prev+(e<len2?-next/len2:0),ny=dx1+(e<len2?dx2/len2:0);len1=(nx*nx+ny*ny)**.5,len2=(len1<e?(nx=prev,ny=dx1):(nx/=len1,ny/=len1,next=prev*nx+dx1*ny,e<next&&(dx2=min(1/next,10),nx*=dx2,ny*=dx2)),pool[strip.length]||=vec2()),len1=pool[strip.length+1]||=vec2();len2.x=curr.x-nx*halfWidth,len2.y=curr.y-ny*halfWidth,len1.x=curr.x+nx*halfWidth,len1.y=curr.y+ny*halfWidth,strip.push(len2,len1)}}1<strip.length&&wrap&&(strip.push(strip[0]),strip.push(strip[1]));return strip}(points,lineWidth,wrap),rgba,x,y,sx,sy,angle,!1)}function glDrawPoints(points,rgba){glDrawPolyStrip(points,rgba)}function glDrawColoredPoints(points,pointColors){glDrawPolyStrip(points,0,pointColors)}function glDrawPolyStrip(points,rgba,pointColors){if(glEnable&&!(points.length<3)){var vertCount=points.length+2;if((glBatchCount+vertCount>=gl_MAX_POLY_VERTEXES||glBatchAdditive!==glAdditive)&&glFlush(),!(vertCount>=gl_MAX_POLY_VERTEXES)){glPolyMode||(glFlush(),glPolyMode=!0,glContext.useProgram(glPolyShader),glContext.bindVertexArray(glPolyVAO));let offset=glBatchCount*gl_INDICES_PER_POLY_VERTEX;for(let i=vertCount;i--;){var j=clamp(i-1,0,vertCount-3),point=points[j],j=pointColors?pointColors[j]:rgba;glPositionData[offset++]=point.x,glPositionData[offset++]=point.y,glColorData[offset++]=j&glColorMask|glColorAdditive}glBatchCount+=vertCount}}}function glSetRenderTarget(texture,clear=!1){glFlush();var baseSize,previousTarget=glRenderTarget;if(texture)glMipmapsUntilTarget.has(texture)&&(glMipmapsUntilTarget.delete(texture),glMipmappedTextures.delete(texture),glContext.bindTexture(glContext.TEXTURE_2D,texture),glContext.texParameteri(glContext.TEXTURE_2D,glContext.TEXTURE_MIN_FILTER,glContext.LINEAR),glContext.bindTexture(glContext.TEXTURE_2D,glActiveTexture)),glPremultipliedTextures.add(texture),glRenderTarget||(glRenderTargetSaved=[glTransform,glAdditive]),glRenderTarget=texture,glContext.bindFramebuffer(glContext.FRAMEBUFFER,glFramebuffer),glContext.framebufferTexture2D(glContext.FRAMEBUFFER,glContext.COLOR_ATTACHMENT0,glContext.TEXTURE_2D,texture,0),glPreRender(clear);else if(glRenderTarget=void 0,glRenderTargetBase?([clear,baseSize]=glRenderTargetBase,glContext.bindFramebuffer(glContext.FRAMEBUFFER,glFramebuffer),glContext.framebufferTexture2D(glContext.FRAMEBUFFER,glContext.COLOR_ATTACHMENT0,glContext.TEXTURE_2D,clear,0),glContext.viewport(0,0,baseSize.x,baseSize.y)):(glContext.bindFramebuffer(glContext.FRAMEBUFFER,null),glContext.viewport(0,0,glCanvas.width,glCanvas.height)),glRenderTargetSaved){[glTransform,glAdditive]=glRenderTargetSaved,glRenderTargetSaved=void 0;for(const program of[glPolyShader,glShader])glContext.useProgram(program),glContext.uniformMatrix4fv(glUniformLocation(program,"m"),!1,glTransform);glBatchAdditive=glAdditive,glSetInstancedMode(!0)}previousTarget&&previousTarget!==texture&&glMipmappedTextures.has(previousTarget)&&glMipmapsStale.add(previousTarget)}function glClearRect(x,y,width,height){glEnable&&(glFlush(),glContext.enable(glContext.SCISSOR_TEST),glContext.scissor(x,y,width,height),glContext.clearColor(0,0,0,0),glContext.clear(glContext.COLOR_BUFFER_BIT),glContext.disable(glContext.SCISSOR_TEST))}const glOutlineStrip=[],glOutlinePool=[];function glPolyStrip(points){if(points.length<3)return[];const cross=(a,b,c)=>(b.x-a.x)*(c.y-a.y)-(b.y-a.y)*(c.x-a.x);(poly=>{let area=0;for(let i=poly.length;i--;){var j=(i+1)%poly.length;area+=poly[i].cross(poly[j])}return area})(points)<0&&(points=points.slice().reverse());const e=1e-9,n=points.length;let convex=!0,flips=0,lastSide=0;for(let i=0;convex&&i<n;++i){var a=points[(i+n-1)%n],b=points[i],dx=points[(i+1)%n].x-b.x,a=(convex=cross(a,b,points[(i+1)%n])>-e,dx>e?1:dx<-e?-1:0);a&&a!==lastSide&&(lastSide&&++flips,lastSide=a)}if(convex&&flips<=2){const strip=[points[0]];for(let i=1,j=n-1;i<=j;++i,--j)strip.push(points[i]),i!==j&&strip.push(points[j]);return strip}var indices=[];for(let i=0;i<points.length;++i)indices[i]=i;var triangles=[];let attempts=0;for(var maxAttempts=points.length**2+100;3<indices.length&&attempts++<maxAttempts;){let foundEar=!1;for(let i=0;i<indices.length;i++){var i0=indices[(i+indices.length-1)%indices.length],i1=indices[i],i2=indices[(i+1)%indices.length];const a=points[i0],b=points[i1],c=points[i2];if(!(cross(a,b,c)<e)){let hasInside=!1;for(let j=0;j<indices.length;j++){var k=indices[j];if(k!==i0&&k!==i1&&k!==i2){k=points[k];if(hasInside=((p,a,b,c)=>{var c1=cross(a,b,p),b=cross(b,c,p),c=cross(c,a,p),a=(c1<-e?1:0)+(b<-e?1:0)+(c<-e?1:0),p=(c1>e?1:0)+(b>e?1:0)+(c>e?1:0);return!(a&&p)})(k,a,b,c))break}}if(!hasInside){triangles.push([i0,i1,i2]),indices.splice(i,1),foundEar=!0;break}}}if(!foundEar){let worstIndex=-1,worstValue=1/0;for(let i=0;i<indices.length;i++){const i0=indices[(i+indices.length-1)%indices.length],i1=indices[i],i2=indices[(i+1)%indices.length];var value=abs(cross(points[i0],points[i1],points[i2]));value<worstValue&&(worstValue=value,worstIndex=i)}if(worstIndex<0)break;const i0=indices[(worstIndex+indices.length-1)%indices.length],i1=indices[worstIndex],i2=indices[(worstIndex+1)%indices.length];triangles.push([i0,i1,i2]),indices.splice(worstIndex,1)}}if(3===indices.length&&triangles.push([indices[0],indices[1],indices[2]]),!triangles.length)return[];const strip=[];let[a0,b0,c0]=triangles[0];strip.push(points[a0],points[b0],points[c0]);for(let i=1;i<triangles.length;i++){const[a,b,c]=triangles[i];strip.push(points[c0],points[a]),strip.push(points[a],points[b],points[c]),c0=c}return strip}function drawEngineLogo(t){const blackAndWhite=0;engineUpdateCanvas();const x=mainContext,w=mainCanvasSize.x;var h=mainCanvasSize.y,p3=percent(t,1,.8),p4=percent(t,0,.5),g=x.createRadialGradient(w/2,h/2,0,w/2,h/2,.6*hypot(w,h));g.addColorStop(0,hsl(0,0,lerp(0,p3/2,p4),p3).toString()),g.addColorStop(1,hsl(0,0,0,p3).toString()),x.save(),x.fillStyle=g,x.fillRect(0,0,w,h);const gradient=(X1,Y1,X2,Y2,C,S=1)=>{0<=C?blackAndWhite?x.fillStyle="#fff":((X1=x.fillStyle=x.createLinearGradient(X1,Y1,X2,Y2)).addColorStop(0,color(C,2)),X1.addColorStop(1,color(C,1))):x.fillStyle="#000",-1<=C&&(x.fill(),!S)||x.stroke()};var circle=(X,Y,R,A=0,B=2*PI,C,S)=>{x.beginPath(),x.arc(X,Y,R,p*A,p*B),gradient(X,Y-R,X,Y+R,C,S)},p4=(X,Y,W,H,C)=>{x.beginPath(),x.rect(X,Y,W,H*p),gradient(X,Y+H,X+W,Y,C)},poly=(points,C,Y,H)=>{x.beginPath();for(const p of points)x.lineTo(p.x,p.y);x.closePath(),gradient(0,Y,0,Y+H,C)};const color=(c,l)=>l?`hsl(${360*[.95,.56,.13][c%3]} 99%${[0,50,75][l]}%)`:"#000",p=percent(oscillate(1,1,t),.1,.5);p3=min(6,min(w,h)/99);x.translate(w/2,h/2),x.scale(p3,p3),x.translate(-40,-35),p<1&&x.setLineDash([99*p,99]),x.lineJoin=x.lineCap="round",x.lineWidth=.1+1.9*p;{var s="LittleJS";x.font="900 15.5px arial",x.lineWidth=.1+3.9*p,x.textAlign="center",x.textBaseline="top",p4(11,55,59,8*p,-1),x.beginPath();let w2=0;for(let i=0;i<s.length;++i)w2+=x.measureText(s[i]).width;for(let j=2;j--;)for(let i=0,X=40-w2/2;i<s.length;++i){const w=x.measureText(s[i]).width,X2=X+w/2;gradient(X2,54,X2+2,67,5<i?1:0),x[j?"strokeText":"fillText"](s[i],X2,54.5,17*p),X+=w}x.lineWidth=.1+1.9*p,p4(3,54,73,0)}p4(7,15,26,-7,0),p4(25,15,8,25,-1),p4(10,40,15,-25,1),p4(14,21,7,9,2),p4(38,20,6,-6,2),p4(49,20,10,-6,0),poly([vec2(44,8),vec2(64,8),vec2(59,8+6*p),vec2(49,8+6*p)],2,8,6*p),p4(44,8,20,-7,0);for(let i=5;i--;)circle(59-6*i*p,30,10,0,2*PI,1,0);circle(59,30,4,0,7,2),p4(35,20,24,0),circle(59,30,10),circle(47,30,10,PI/2,3*PI/2),circle(35,30,10,PI/2,3*PI/2),p4(7,40,13,7,-1),p4(17,40,43,14,-1);for(let i=3;i--;)for(let j=2;j--;)circle(17+15*i,47,j?7:1,0,2*PI,2);for(let i=2;i--;){let o=53+6*p*i;poly([vec2(7+o,54),vec2(o,40),vec2(o+6*p,40),vec2(7+o+6*p,54)],0,40,14)}x.restore()}let medalDisplayTime=5,medalDisplaySlideTime=.5,medalDisplaySize=vec2(640,80),medalsPreventUnlock=!1;const medals={};let medalsDisplayQueue=[],medalsSaveName,medalsDisplayTimeLast,medalsRenderAdded;function medalsInit(saveName){const early=[];medalsSaveName||medalsForEach(medal=>medal.unlocked&&medal.isLocal()&&early.push(medal)),medalsSaveName=saveName,medalsLoad(),early.length&&(early.forEach(medal=>medal.unlocked=!0),medalsSave()),void 0!==wavedash&&wavedash?.sendAchievements(),medalsRenderAdded||engineAddPlugin(void 0,medalsRender),medalsRenderAdded=!0}function medalsLoad(){if(medalsSaveName){const saved=readSaveData(medalsSaveName);medalsForEach(medal=>{medal.isLocal()&&(medal.unlocked=!!saved[medal.id]?.unlocked)}),medalsSave()}}function medalsRender(){var medal,elapsed,slideTime,slideOffTime;medalsDisplayQueue.length&&(medal=medalsDisplayQueue[0],elapsed=timeReal-medalsDisplayTimeLast,medalsDisplayTimeLast?elapsed>medalDisplayTime?(medalsDisplayTimeLast=0,medalsDisplayQueue.shift()):(slideTime=min(medalDisplaySlideTime,medalDisplayTime/2),slideOffTime=medalDisplayTime-slideTime,medal.render(elapsed<slideTime?1-elapsed/slideTime:slideOffTime<elapsed?(elapsed-slideOffTime)/slideTime:0)):medalsDisplayTimeLast=timeReal)}function medalsForEach(callback){Object.values(medals).forEach(medal=>callback(medal))}function medalsReset(){if(medalsForEach(medal=>medal.isLocal()&&(medal.unlocked=!1)),medalsSaveName){var saved=readSaveData(medalsSaveName);for(const id in saved)medals[id]&&!medals[id].isLocal()||!saved[id]||"object"!=typeof saved[id]||(saved[id].unlocked=!1);writeSaveData(medalsSaveName,saved)}medalsSave()}function medalsSave(){if(medalsSaveName){const saved=readSaveData(medalsSaveName),data={...saved};medalsForEach(medal=>{var entry;medal.isLocal()?(entry={name:medal.name,description:medal.description,icon:medal.icon,unlocked:medal.unlocked||!!saved[medal.id]?.unlocked},medal.image&&(entry.src=medal.image.src),data[medal.id]=entry):saved[medal.id]&&(data[medal.id]=saved[medal.id])}),writeSaveData(medalsSaveName,data)}}class Medal{constructor(id,name,description="",icon="🏆",src){this.id=id,this.name=name,this.description=description,this.icon=icon,this.unlocked=!1,this.image=void 0,src&&((this.image=new Image).src=src),medals[id]=this,medalsSaveName&&this.isLocal()&&(this.unlocked=!!readSaveData(medalsSaveName)[id]?.unlocked)}unlock(){return medalsPreventUnlock||this.unlocked||(this.unlocked=!0,medalsSave(),medalsDisplayQueue.push(this)),Promise.resolve(this.unlocked)}isLocal(){return!0}render(hidePercent=0){var context=mainContext,width=min(medalDisplaySize.x,mainCanvasSize.x),height=medalDisplaySize.y,x=mainCanvasSize.x-width,hidePercent=-height*hidePercent,backgroundColor=hsl(0,0,.9),backgroundColor=(context.save(),context.beginPath(),context.fillStyle=backgroundColor.toString(),context.strokeStyle=BLACK.toString(),context.lineWidth=3,context.rect(1.5+x,1.5+hidePercent,width-3,height-3),context.fill(),context.stroke(),context.clip(),vec2(.1,.05).scale(height)),iconSize=height-2*backgroundColor.x,nameSize=(this.renderIcon(vec2(x+backgroundColor.x+iconSize/2,hidePercent+height/2),iconSize),.5*height),descriptionSize=.3*height,x=vec2(x+iconSize+2*backgroundColor.x,hidePercent+2*backgroundColor.y+nameSize/2),width=width-iconSize-3*backgroundColor.x;drawTextScreen(this.name,x,nameSize,BLACK,0,void 0,"left",void 0,void 0,width),x.y=hidePercent+height-2*backgroundColor.y-descriptionSize/2,drawTextScreen(this.description,x,descriptionSize,BLACK,0,void 0,"left",void 0,void 0,width),context.restore()}renderIcon(pos,size){var image=this.image;image&&image.complete&&image.naturalWidth?mainContext.drawImage(image,pos.x-size/2,pos.y-size/2,size,size):drawTextScreen(this.icon,pos,.7*size,BLACK)}}function setMedalDisplayTime(time){medalDisplayTime=time}function setMedalDisplaySlideTime(time){medalDisplaySlideTime=time}function setMedalDisplaySize(size){medalDisplaySize=size.copy()}function setMedalsPreventUnlock(preventUnlock){medalsPreventUnlock=preventUnlock}let newgrounds;const newgroundsUnlocksToResend=new Set,newgroundsUnlocksRefused=new Set,newgroundsSecureComponents=["Medal.unlock","ScoreBoard.postScore"],newgroundsSessionErrors=[104,110,111];function newgroundsSessionLost(response){return newgroundsSessionErrors.includes(response?.result?.data?.error?.code??response?.error?.code)}function newgroundsNotifyPage(component,id){globalThis.top?.postMessage(JSON.stringify({ngioComponent:component,id:id}),"*")}class NewgroundsMedal extends Medal{constructor(id,name,description,icon,src){super(id,name,description,icon,src),this.difficulty=void 0,this.value=void 0}isLocal(){return!newgrounds?.session_id}unlock(){if(medalsPreventUnlock||this.unlocked||this.isLocal())return super.unlock();if(newgroundsUnlocksRefused.has(this))return Promise.resolve(!1);const pending=newgrounds.pendingUnlocks;var request;return pending.has(this)?pending.get(this):(request=newgrounds.unlockMedal(this.id).then(response=>{newgroundsSessionLost(response)&&newgrounds.dropSession();var serverMedal=response?.result?.data?.medal;return!serverMedal?.unlocked||medalsPreventUnlock?this.isLocal()?this.unlocked:((!response||serverMedal?.unlocked?newgroundsUnlocksToResend:(pending.delete(this),newgroundsUnlocksRefused)).add(this),!1):((response=newgrounds.medals.find(m=>m.id==this.id))&&Object.assign(response,serverMedal),serverMedal.icon&&serverMedal.icon!=this.image?.src&&((this.image=new Image).src=serverMedal.icon),pending.delete(this),newgroundsNotifyPage("Medal.unlock",this.id),super.unlock())}),pending.set(this,request),request)}}class NewgroundsPlugin{constructor(app_id,cipher){(newgrounds=this).app_id=app_id,this.cipher=cipher,this.cryptoKey=void 0;app_id="undefined"!=typeof location;this.host=app_id?location.hostname:"",this.medals=[],this.scoreboards=[],this.user=null,this.pendingUnlocks=new Map,this.session_id=app_id?new URL(location.href).searchParams.get("ngio_session_id"):null,this.session_id&&medalsForEach(medal=>medal instanceof NewgroundsMedal&&(medal.unlocked=!1)),this.ready=this.init()}async init(){this.call("App.logView",{host:this.host});let medalList;this.session_id&&(session=(session=(await this.call("App.checkSession"))?.result?.data?.session)&&!session.expired&&session.user,(medalList=session&&(await this.call("Medal.getList"))?.result?.data?.medals)&&this.session_id?this.user=session:(this.dropSession(),medalList=void 0)),medalList=medalList||(await this.call("Medal.getList"))?.result?.data?.medals,this.medals=medalList||[];for(const newgroundsMedal of this.medals){var medal=medals[newgroundsMedal.id];medal instanceof NewgroundsMedal&&(medal.image=new Image,medal.image.src=newgroundsMedal.icon,medal.name=newgroundsMedal.name,medal.description=newgroundsMedal.description,medal.unlocked=medal.unlocked||!!newgroundsMedal.unlocked,medal.difficulty=newgroundsMedal.difficulty,medal.value=newgroundsMedal.value,medal.value&&(medal.description+=` (${medal.value})`),this.session_id)&&medal.unlocked&&(newgroundsMedal.unlocked=!0,this.pendingUnlocks.delete(medal),newgroundsUnlocksToResend.delete(medal),newgroundsUnlocksRefused.delete(medal))}var session=await this.call("ScoreBoard.getBoards");if(this.scoreboards=session?.result?.data?.scoreboards||[],this.session_id){const keepAlive=setInterval(async()=>{var response,session;return this.session_id?(session=(response=await this.call("App.checkSession"))?.result?.data?.session,newgroundsSessionLost(response)||session&&(session.expired||!session.user)?this.dropSession():void this.resendUnlocks()):clearInterval(keepAlive)},6e4)}return this}dropSession(){if(this.session_id){var confirmed=Object.values(medals).filter(medal=>medal.unlocked),confirmed=(this.session_id=null,this.user=null,medalsLoad(),confirmed.forEach(medal=>medal.unlocked=!0),medalsSave(),[...this.pendingUnlocks.keys()]);this.pendingUnlocks.clear(),newgroundsUnlocksToResend.clear(),newgroundsUnlocksRefused.clear();for(const medal of confirmed)medal.unlock()}}resendUnlocks(){if(!medalsPreventUnlock){for(const medal of newgroundsUnlocksToResend)this.pendingUnlocks.delete(medal),medal.unlock();newgroundsUnlocksToResend.clear()}}unlockMedal(id){return this.call("Medal.unlock",{id:id})}postScore(id,value){return this.call("ScoreBoard.postScore",{id:id,value:value}).then(response=>(response?.result?.data?.success&&newgroundsNotifyPage("ScoreBoard.postScore",id),newgroundsSessionLost(response)&&this.dropSession(),response))}getScores(id,user,social=!1,skip=0,limit=10,period){var session_id=user||social?this.session_id:null;return this.call("ScoreBoard.getScores",{id:id,user:user,social:social,skip:skip,limit:limit,period:period},session_id)}async encrypt(text){this.cryptoKey||(keyBytes=Uint8Array.from(atob(this.cipher),c=>c.charCodeAt(0)),this.cryptoKey=await crypto.subtle.importKey("raw",keyBytes,"AES-CBC",!1,["encrypt"]));var keyBytes=crypto.getRandomValues(new Uint8Array(16)),text=new Uint8Array(await crypto.subtle.encrypt({name:"AES-CBC",iv:keyBytes},this.cryptoKey,(new TextEncoder).encode(text))),bytes=new Uint8Array(keyBytes.length+text.length);bytes.set(keyBytes),bytes.set(text,keyBytes.length);let binary="";for(const b of bytes)binary+=String.fromCharCode(b);return btoa(binary)}async call(component,parameters,session_id=this.session_id){try{let execute={component:component,parameters:parameters};if(this.cipher&&newgroundsSecureComponents.includes(component)){var secure=await this.encrypt(JSON.stringify(execute)).catch(e=>!1);if(!secure)return{success:!1,error:{code:201,message:"Invalid Encryption: the cipher is not a Base64 AES-128 key"}};execute={secure:secure}}var request={app_id:this.app_id,session_id:session_id,execute:execute},formData=new FormData,signal=(formData.append("request",JSON.stringify(request)),globalThis.AbortSignal?.timeout?.(15e3)),text=await(await fetch("https://www.newgrounds.io/gateway_v3.php",{method:"POST",body:formData,signal:signal})).text();return text?JSON.parse(text):void 0}catch(e){}}}let wavedash;const wavedashDisplayTypes={number:0,seconds:1,milliseconds:2,ticks:3},wavedashSDK=()=>globalThis.Wavedash;function wavedashWait(name,answer){let timer;var limit=new Promise(resolve=>timer=setTimeout(resolve,15e3));return Promise.race([Promise.resolve(answer),limit]).catch(error=>{console.warn("Wavedash "+name+" failed: "+error)}).finally(()=>clearTimeout(timer))}class WavedashMedal extends Medal{constructor(id,achievement,name,description,icon,src){super(id,name,description,icon,src),this.achievement=achievement}unlock(){return medalsPreventUnlock||this.unlocked||!wavedashSDK()?super.unlock():(this.unlocked=!0,medalsSave(),wavedash?.sendAchievements(),Promise.resolve(!0))}}class WavedashPlugin{constructor(leaderboards={}){(wavedash=this).leaderboards=Object.create(null),this.leaderboardSettings=leaderboards,this.achievementsSent=new Set,this.achievementTries=Object.create(null),this.achievementRetry=void 0,wavedashWait("init",this.call("init"));for(const name in leaderboards)this.leaderboard(name);this.sendAchievements()}isActive(){return!!wavedashSDK()}call(name,...args){var sdk=wavedashSDK();if(sdk)try{return sdk[name](...args)}catch(error){console.warn("Wavedash "+name+" failed: "+error)}}leaderboard(name){if(this.leaderboards[name])return this.leaderboards[name];var{lowerWins=!1,display="number"}=(Object.prototype.hasOwnProperty.call(this.leaderboardSettings,name)?this.leaderboardSettings[name]:void 0)||{},known=wavedashDisplayTypes.hasOwnProperty(display),lowerWins=this.call("getOrCreateLeaderboard",name,lowerWins?0:1,known?wavedashDisplayTypes[display]:0);const id=wavedashWait("leaderboard "+name,lowerWins).then(result=>result?.success?result.data?.id:void 0);return id.then(value=>void 0===value&&this.leaderboards[name]===id&&delete this.leaderboards[name]),this.leaderboards[name]=id}async postScore(name,score){return!!this.isActive()&&void 0!==(name=await this.leaderboard(name))&&!!(await wavedashWait("postScore",this.call("uploadLeaderboardScore",name,round(score),!0)))?.success}async getScores(name,offset=0,limit=10,friendsOnly=!1){return this.isActive()&&void 0!==(name=await this.leaderboard(name))&&(name=await wavedashWait("getScores",this.call("listLeaderboardEntries",name,0|offset,0|limit,!!friendsOnly)))?.success?name.data:void 0}sendAchievements(){if(this.isActive()){let refused=!1;medalsForEach(medal=>{var result,medal=medal instanceof WavedashMedal&&medal.unlocked&&medal.achievement;medal&&!this.achievementsSent.has(medal)&&(!0===(result=this.call("setAchievement",medal,!0))||!0===result?.success?this.achievementsSent.add(medal):(this.achievementTries[medal]=(this.achievementTries[medal]||0)+1)<30?refused=!0:(console.warn("Wavedash refused achievement "+medal+" 30 times; is it made, with that identifier?"),this.achievementsSent.add(medal)))}),clearTimeout(this.achievementRetry),this.achievementRetry=refused?setTimeout(()=>this.sendAchievements(),2e3):void 0}}}let postProcess;class PostProcessPlugin{constructor(shaderCode,includeMainCanvas=!1,feedbackTexture=!1){function initPostProcess(){var pLocation;!headlessMode&&glEnable&&(feedbackTexture&&(postProcess.feedbackTexture=glCreateTexture(),glContext.bindTexture(glContext.TEXTURE_2D,postProcess.feedbackTexture),glContext.texImage2D(glContext.TEXTURE_2D,0,glContext.RGBA,1,1,0,glContext.RGBA,glContext.UNSIGNED_BYTE,new Uint8Array([0,0,0,255])),glContext.bindTexture(glContext.TEXTURE_2D,glActiveTexture)),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);}",function(shaderCode,values={}){let declared="";for(const name in values){var value=values[name],value=isArray(value)?value.length:1;declared+=`uniform ${1<value?"vec"+value:"float"} ${name};`}return"#version 300 es\nprecision highp float;uniform sampler2D iChannel0;uniform sampler2D iChannel1;uniform highp sampler2D iChannel2;uniform vec3 iResolution;uniform float iTime;uniform vec3 iDepthRange;"+declared+"out vec4 c;float sceneDepth(vec2 uv){float d=texture(iChannel2,uv).r*2.-1.,n=iDepthRange.x,f=iDepthRange.y;return iDepthRange.z>0.?(d*(f-n)+f+n)/2.:f>0.?2.*n*f/(f+n-d*(f-n)):2.*n/max(1.-d,1e-7);}\n#define LJS_HAS_DEPTH (iDepthRange != vec3(0))\nvoid mainImage(out vec4,vec2);void main(){mainImage(c,gl_FragCoord.xy);c.a=1.;}\n"+shaderCode+"\n"}(postProcess.shaderCode,postProcess.values)),postProcess.valueNames=postProcessValueKey(postProcess.values),postProcess.vao=glContext.createVertexArray(),glContext.bindVertexArray(postProcess.vao),glContext.bindBuffer(glContext.ARRAY_BUFFER,glGeometryBuffer),pLocation=glContext.getAttribLocation(postProcess.shader,"p"),glContext.enableVertexAttribArray(pLocation),glContext.vertexAttribPointer(pLocation,2,glContext.FLOAT,!1,8,0),glContext.bindVertexArray(glPolyMode?glPolyVAO:glInstancedVAO),glContext.bindBuffer(glContext.ARRAY_BUFFER,glArrayBuffer))}(postProcess=this).shaderCode=shaderCode||postProcessEffects(),this.values={},this.valueNames="",this.shader=void 0,this.texture=void 0,this.feedbackTexture=void 0,this.vao=void 0,headlessMode||glEnable||console.warn("PostProcessPlugin: WebGL not enabled!"),engineAddPlugin(void 0,function(){if(!headlessMode&&glEnable){if(glFlush(),postProcess.shader&&postProcessValueKey(postProcess.values)!==postProcess.valueNames&&postProcess.setShaderCode(postProcess.shaderCode),!postProcess.shader){if(glContext.isContextLost())return;initPostProcess()}if(!glFailedPrograms.has(postProcess.shader)){glContext.bindFramebuffer(glContext.FRAMEBUFFER,null),glContext.useProgram(postProcess.shader),glContext.bindVertexArray(postProcess.vao),glContext.pixelStorei(glContext.UNPACK_FLIP_Y_WEBGL,!0),glContext.pixelStorei(glContext.UNPACK_PREMULTIPLY_ALPHA_WEBGL,!0),glContext.disable(glContext.BLEND),glContext.activeTexture(glContext.TEXTURE0),glContext.bindTexture(glContext.TEXTURE_2D,postProcess.texture),includeMainCanvas?(workCanvas.width!==mainCanvas.width||workCanvas.height!==mainCanvas.height?(workCanvas.width=mainCanvas.width,workCanvas.height=mainCanvas.height):workContext.clearRect(0,0,workCanvas.width,workCanvas.height),glCopyToContext(workContext),workContext.drawImage(mainCanvas,0,0),mainContext.save(),mainContext.setTransform(1,0,0,1,0,0),mainContext.clearRect(0,0,mainCanvas.width,mainCanvas.height),mainContext.restore(),glContext.texImage2D(glContext.TEXTURE_2D,0,glContext.RGBA,glContext.RGBA,glContext.UNSIGNED_BYTE,workCanvas)):glContext.texImage2D(glContext.TEXTURE_2D,0,glContext.RGBA,glContext.RGBA,glContext.UNSIGNED_BYTE,glCanvas),feedbackTexture&&(glContext.activeTexture(glContext.TEXTURE1),glContext.bindTexture(glContext.TEXTURE_2D,postProcess.feedbackTexture));var depth=void 0!==render3D&&render3D?.depthTexture?render3D:void 0,depth=(glContext.activeTexture(glContext.TEXTURE2),glContext.bindTexture(glContext.TEXTURE_2D,depth?.cameraDepthTexture||null),glContext.activeTexture(glContext.TEXTURE0),depth?.camera),uniformLocation=(glContext.uniform3f(glUniformLocation(postProcess.shader,"iDepthRange"),depth?.near||0,depth&&depth.far!=1/0?depth.far:0,depth?.orthographic?1:0),name=>glUniformLocation(postProcess.shader,name));glContext.uniform1i(uniformLocation("iChannel0"),0),glContext.uniform1i(uniformLocation("iChannel1"),1),glContext.uniform1i(uniformLocation("iChannel2"),2),glContext.uniform1f(uniformLocation("iTime"),time),glContext.uniform3f(uniformLocation("iResolution"),mainCanvas.width,mainCanvas.height,1);for(const name in postProcess.values){var value=postProcess.values[name],location=uniformLocation(name);isArray(value)?[,,glContext.uniform2fv,glContext.uniform3fv,glContext.uniform4fv][value.length]?.call(glContext,location,value):glContext.uniform1f(location,value)}glContext.drawArrays(glContext.TRIANGLE_STRIP,0,4),feedbackTexture&&(glContext.activeTexture(glContext.TEXTURE1),glContext.texImage2D(glContext.TEXTURE_2D,0,glContext.RGBA,glContext.RGBA,glContext.UNSIGNED_BYTE,glCanvas),glContext.activeTexture(glContext.TEXTURE0)),glContext.activeTexture(glContext.TEXTURE2),glContext.bindTexture(glContext.TEXTURE_2D,null),glContext.activeTexture(glContext.TEXTURE0),glContext.pixelStorei(glContext.UNPACK_FLIP_Y_WEBGL,!1),glContext.pixelStorei(glContext.UNPACK_PREMULTIPLY_ALPHA_WEBGL,!1),glActiveTexture&&glContext.bindTexture(glContext.TEXTURE_2D,glActiveTexture),glSetInstancedMode(!0)}}},function(){postProcess.shader=void 0,postProcess.texture=void 0,postProcess.feedbackTexture=void 0},function(){initPostProcess()})}setShaderCode(shaderCode){this.shaderCode=shaderCode||postProcessEffects(),this.shader&&!headlessMode&&glContext&&(glContext.deleteProgram(this.shader),glContext.deleteTexture(this.texture),this.feedbackTexture&&glContext.deleteTexture(this.feedbackTexture),glContext.deleteVertexArray(this.vao),this.shader=this.texture=this.feedbackTexture=this.vao=void 0)}}const postProcessValueKey=values=>Object.entries(values).map(([name,v])=>name+":"+(isArray(v)?v.length:1)).join();function postProcessBloomShader(threshold=.6,strength=1,size=6){return postProcessEffects(postProcessGlow(threshold,strength,size))}function postProcessBloom(threshold=.6,strength=1,size=6,includeMainCanvas=!1){return new PostProcessPlugin(postProcessBloomShader(threshold,strength,size),includeMainCanvas)}const postProcessNumber=n=>"string"==typeof n?n:n&&abs(n)<.01?String(n):n.toFixed(4);new Set("c uv p sceneDepth mainImage main attribute const uniform varying layout centroid flat smooth noperspective patch sample break continue do for while switch case default if else subroutine in out inout float double int void bool true false invariant precise discard return lowp mediump highp precision struct common partition active asm class union enum typedef template this resource goto inline noinline public static extern external interface long short half fixed unsigned superp input output filter sizeof cast namespace using coherent volatile restrict readonly writeonly atomic_uint uint mat2 mat3 mat4 vec2 vec3 vec4 ivec2 ivec3 ivec4 bvec2 bvec3 bvec4 uvec2 uvec3 uvec4 radians degrees sin cos tan asin acos atan sinh cosh tanh asinh acosh atanh pow exp log exp2 log2 sqrt inversesqrt abs sign floor trunc round roundEven ceil fract mod modf min max clamp mix step smoothstep isnan isinf length distance dot cross normalize faceforward reflect refract matrixCompMult outerProduct transpose determinant inverse lessThan lessThanEqual greaterThan greaterThanEqual equal notEqual any all not textureSize texture textureProj textureLod textureOffset texelFetch texelFetchOffset textureProjOffset textureLodOffset textureProjLod textureProjLodOffset textureGrad textureGradOffset textureProjGrad textureProjGradOffset dFdx dFdy fwidth".split(" "));function postProcessEffects(...effects){return"void mainImage(out vec4 c, vec2 p)\n{\n vec2 uv = p / iResolution.xy;\n c = texture(iChannel0, uv);\n"+effects.map(e=>" {\n"+e+"\n }\n").join("")+"}\n"}function postProcessGlow(threshold=.6,strength=1,size=6){let code="",taps=0;for(let j=0;j<3;++j){var radius=((j+.5)/3)**.5*size,count=1|max(5+2*j,round(2*radius));taps+=count,code+=`
4
+ const engineName="LittleJS",engineVersion="1.26.1",frameRate=60;let timeDelta=1/frameRate,engineObjects=[],engineObjectsCollide=[],engineObjectsCollideStaticLast=[],frame=0,time=0,timeReal=0,paused=!1;function getPaused(){return paused}function setPaused(isPaused=!0){paused=isPaused}let averageFPS=0,frameTimeLastMS=0,frameTimeBufferMS=0;const frameDeltaHistory=[];let frameIntervalMS=1e3/60,frameDeltaCarryMS=0,frameDeltaCarryAverageMS=0,frameDeltaSmoothing=!0;function engineFrameFit(unitMS){let timeMS=0,frames=0,offMS=0,sumF=0,sumT=0,sumFF=0,sumFT=0;var count=frameDeltaHistory.length;for(const d of frameDeltaHistory){var held=max(1,round(d/unitMS));timeMS+=d,frames+=held,offMS+=abs(d-held*unitMS),sumF+=frames,sumT+=timeMS,sumFF+=frames*frames,sumFT+=frames*timeMS}var spread=count*sumFF-sumF*sumF;return[spread?(count*sumFT-sumF*sumT)/spread:unitMS,offMS/count/unitMS]}let timeFixedStart=0,frameFixedStart=0,windowWidthLast=0,windowHeightLast=0,windowPixelRatioLast=0,engineUpdateInternal,engineFrameScheduled=!1,engineFrameErrorLast;const engineObjectsCollidePairs=new Map;function engineObjectsCollidePairAnswer(asker,other){return engineObjectsCollidePairs.get(asker)?.get(other)}function engineObjectsCollidePairAdd(asker,other,resolve=!1){let others=engineObjectsCollidePairs.get(asker);others||engineObjectsCollidePairs.set(asker,others=new Map),others.set(other,resolve)}let engineCollideGridMin=64,engineCollideGrid;const engineCollideGridBox=[0,0,0,0];function engineCollideGridCells(grid,o){var grid=grid.size,w=abs(o.size.x)/2,h=abs(o.size.y)/2,box=engineCollideGridBox;return box[0]=floor((o.pos.x-w)/grid),box[1]=floor((o.pos.y-h)/grid),box[2]=floor((o.pos.x+w)/grid),box[3]=floor((o.pos.y+h)/grid),box}const engineCollideGridFits=(x0,y0,x1,y1)=>(x1-x0+1)*(y1-y0+1)<=1024&&abs(x0)<2**31&&abs(y0)<2**31&&abs(x1)<2**31&&abs(y1)<2**31;function engineCollideGridPlace(grid,o){if(grid.index.has(o)){var[x0,y0,x1,y1]=engineCollideGridCells(grid,o),big=!engineCollideGridFits(x0,y0,x1,y1),was=grid.at.get(o);if(!was||was[0]!==x0||was[1]!==y0||was[2]!==x1||was[3]!==y1||was[4]!==big){if(was&&!was[4])for(let x=was[0];x<=was[2];++x)for(let y=was[1];y<=was[3];++y){var cell=grid.cells.get(1048576*x+y);cell.splice(cell.indexOf(o),1)}if(grid.big.delete(o),big)grid.big.add(o);else for(let x=x0;x<=x1;++x)for(let y=y0;y<=y1;++y){var key=1048576*x+y;const cell=grid.cells.get(key);cell?cell.push(o):grid.cells.set(key,[o])}grid.at.set(o,[x0,y0,x1,y1,big])}}}function engineCollideGridAdd(grid,other,after,stamp,near){grid.seen.get(other)!==stamp&&(grid.seen.set(other,stamp),grid.index.get(other)>after)&&near.push(other)}function*engineCollideGridWalk(o){var grid=engineCollideGrid.built||=function(list){let extent=0;for(const o of list)extent+=min(max(abs(o.size.x),abs(o.size.y)),16)||0;const index=new Map,grid={size:max(2*extent/list.length,.5),cells:new Map,at:new Map,index:index,big:new Set,seen:new Map,stamp:0,byIndex:(a,b)=>index.get(a)-index.get(b)};return list.forEach((o,i)=>{index.set(o,i),engineCollideGridPlace(grid,o)}),grid}(engineCollideGrid.list);let after=-1,x,y,w,h,k=0;for(var near=[];;){if(o.pos.x===x&&o.pos.y===y&&o.size.x===w&&o.size.y===h||(x=o.pos.x,y=o.pos.y,w=o.size.x,h=o.size.y,function(grid,o,after,near){var[o,y0,x1,y1]=engineCollideGridCells(grid,o),stamp=++grid.stamp;near.length=0;for(const other of grid.big)engineCollideGridAdd(grid,other,after,stamp,near);if(engineCollideGridFits(o,y0,x1,y1))for(let x=o;x<=x1;++x)for(let y=y0;y<=y1;++y){var cell=grid.cells.get(1048576*x+y);if(cell)for(const other of cell)engineCollideGridAdd(grid,other,after,stamp,near)}else for(const other of grid.index.keys())engineCollideGridAdd(grid,other,after,stamp,near);near.sort(grid.byIndex)}(grid,o,after,near),k=0),k>=near.length)return;var other=near[k++];after=grid.index.get(other),yield other}}let engineInitialized=!1,engineInitDone,engineLoads,engineLoadsDone=0,engineObjectsUpdateCount=0;const engineChildStack=[];const pluginList=[];class EnginePlugin{constructor(update,render,glContextLost,glContextRestored,preRender){this.update=update,this.render=render,this.glContextLost=glContextLost,this.glContextRestored=glContextRestored,this.preRender=preRender}}function engineAddPlugin(update,render,glContextLost,glContextRestored,preRender){update=new EnginePlugin(update,render,glContextLost,glContextRestored,preRender);pluginList.push(update)}function engineAddLoad(promise){var done;return engineLoads&&(engineLoads.push(promise),promise.then(done=()=>{++engineLoadsDone},done)),promise}async function engineWaitForLoads(){let waiting=!0;const start=performance.now(),drawFrame=()=>{var elapsed;waiting&&(elapsed=(performance.now()-start)/1e3,headlessMode||!loadingScreen||elapsed<.5||(inputClear(),engineUpdateCanvas(),loadingScreen(engineLoadsDone/engineLoads.length)),setTimeout(drawFrame,16))};headlessMode||drawFrame();try{for(let count;count!==engineLoads.length;)count=engineLoads.length,await Promise.allSettled(engineLoads)}finally{waiting=!1}}async function engineInit(gameInit,gameUpdate,gameUpdatePost,gameRender,gameRenderPost,imageSources=[],rootElement){if(engineInitialized)return engineInitDone;function engineUpdate(frameTimeMS=0){var manualStepAtStart=engineManualStep;let frameTimeDeltaMS=frameTimeMS-frameTimeLastMS;frameTimeLastMS||(frameTimeDeltaMS=0),frameTimeLastMS=frameTimeMS,frameTimeDeltaMS&&frameTimeDeltaMS<1e3&&(averageFPS=averageFPS?lerp(averageFPS,1e3/frameTimeDeltaMS,.05):1e3/frameTimeDeltaMS),manualStepAtStart||(frameTimeDeltaMS=0<(frameTimeMS=frameTimeDeltaMS)?(frameTimeMS<250&&(frameDeltaHistory.push(frameTimeMS),64<frameDeltaHistory.length&&frameDeltaHistory.shift(),recent=frameDeltaHistory.slice(-16).sort((a,b)=>a-b),[lastMS,lastOff]=engineFrameFit(frameIntervalMS),[recent,quartileOff]=engineFrameFit(recent[recent.length>>2]),frameIntervalMS=lastOff<=quartileOff?lastMS:recent,frameDeltaSmoothing=min(lastOff,quartileOff)<(frameDeltaSmoothing?.2:.12)),frameDeltaSmoothing?(lastMS=frameDeltaCarryAverageMS/32,frameDeltaCarryMS+=frameTimeMS,(recent=round((frameDeltaCarryMS-lastMS)/frameIntervalMS))<1?0:(lastOff=recent*frameIntervalMS+lastMS,frameDeltaCarryMS-=lastOff,frameDeltaCarryAverageMS+=(frameDeltaCarryMS-frameDeltaCarryAverageMS)/32,lastOff)):(quartileOff=frameTimeMS+frameDeltaCarryMS,frameDeltaCarryMS=min(quartileOff,0),frameDeltaCarryAverageMS=0,max(quartileOff,0))):0),audioUpdateVolume();var recent=debug&&debugKeysAlways,lastMS=recent&&keyIsDown("Equal"),lastOff=recent&&keyIsDown("Minus"),debugScale=lastMS?10:lastOff?.1:1,frameTimeMS=frameTimeDeltaMS,quartileOff=(timeReal+=frameTimeDeltaMS*debugScale/1e3,timeScale*debugScale);frameTimeDeltaMS*=quartileOff;let wasUpdated=!1;if(engineVariableStep)(0<frameTimeMS||manualStepAtStart)&&((frozenTick=paused||!quartileOff)||(timeDelta=manualStepAtStart?quartileOff/frameRate:min(frameTimeMS,50)*quartileOff/1e3,time+=timeDelta,++frame),engineTick(frozenTick));else{var frozenTick=paused||!quartileOff;frameTimeBufferMS+=frozenTick?frameTimeMS:frameTimeDeltaMS;let deltaSmooth=0;(frameTimeBufferMS=min(frameTimeBufferMS,50*(frozenTick?1:max(1,quartileOff))))<0&&-9<frameTimeBufferMS&&(deltaSmooth=frameTimeBufferMS,frameTimeBufferMS=0);try{for(;0<=frameTimeBufferMS;)try{const frozenTick=paused||!(timeScale*debugScale);frozenTick||(time=timeFixedStart+(frame++-frameFixedStart)/frameRate),engineTick(frozenTick)}finally{frameTimeBufferMS-=1e3/frameRate}}finally{frameTimeBufferMS+=deltaSmooth}}function engineTick(frozenTick){if(wasUpdated=!0,engineUpdateCanvas(),!headlessMode){if(touchInputEnable&&isTouchDevice||document.hasFocus()||inputClear(),mousePos=screenToWorld(mousePosScreen),mouseDelta=screenToWorldDelta(mouseDeltaScreen),!touchGamepadOverlay&&touchGamepadEnable&&isTouchDevice&&!headlessMode&&document.body){var overlay=touchGamepadOverlay=document.createElement("div");overlay.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)";const stage=touchGamepadStage=document.createElement("div");stage.style.cssText="position:relative;width:100%;height:100%;pointer-events:none",overlay.appendChild(stage);var svg=touchGamepadSvg=document.createElementNS(touchGamepadSvgNS,"svg"),svg=(svg.style.cssText="position:absolute;inset:0;width:100%;height:100%;pointer-events:none;overflow:visible;fill:none;stroke:#fff;stroke-width:3",stage.appendChild(svg),()=>{const z=document.createElement("div");return z.style.cssText="position:absolute;pointer-events:auto;touch-action:none",z.addEventListener("pointerdown",e=>function(e,zone){var W,H,hit,side;touchGamepadEnable&&"mouse"!==e.pointerType&&(e.preventDefault(),zone.setPointerCapture(e.pointerId),touchGamepadTimer.set(),soundEnable&&!headlessMode&&audioContext&&!audioIsRunning()&&audioResume(),paused?touchGamepadCenterButtonSize&&!touchGamepadButtons[9]&&(touchGamepadButtons[9]=touchGamepadButtonsPressed[9]=1,touchGamepadPointerRole.set(e.pointerId,"start")):(zone=touchGamepadStageRect(),W=zone.width,H=zone.height,zone=vec2(e.clientX-zone.left,e.clientY-zone.top),(hit=function(p,W,H){var S=touchGamepadSize,leftHalf=p.x<W/2,floatTop=.4*H;if(touchGamepadCenterButtonSize&&vec2(W/2,H/2).distance(p)<touchGamepadCenterButtonSize&&!function(p,W,H){for(let side=0;side<2;side++)if(touchGamepadStartBlockSide(side)&&touchGamepadSideCenter(side,W,H).distance(p)<2*touchGamepadSize)return 1;return}(p,W,H))return{role:"start"};for(let side=0;side<2;side++){var onHalf=side?!leftHalf:leftHalf;if(touchGamepadSideStick(side)){var otherControl=touchGamepadSideHasControl(side?0:1);if(touchGamepadFloating?(!otherControl||onHalf)&&p.y>floatTop:onHalf&&touchGamepadSideCenter(side,W,H).distance(p)<2*S)return{role:"stick",side:side}}else if(1<=touchGamepadSideButtonCount(side)){otherControl=function(side,p,W,H){var count=touchGamepadSideButtonCount(side),base=side?0:4,W=touchGamepadSideCenter(side,W,H);if(W.distance(p)>=touchGamepadSize)return-1;if(1===count)return base;H=W.subtract(p);side||(H.x*=-1);W=2===count?H.x<H.y?1:0:mod(H.direction()+2,4);return(W=touchGamepadFaceIndex(W))<count?base+W:-1}(side,p,W,H);if(0<=otherControl)return{role:"face",btn:otherControl}}}}(zone,W,H))&&("stick"===hit.role?(side=hit.side,void 0===touchGamepadStickPointerId[side]&&(touchGamepadStickAnchors[side]=touchGamepadFloating?zone:touchGamepadSideCenter(side,W,H),touchGamepadStickPointerId[side]=e.pointerId,touchGamepadPointerRole.set(e.pointerId,"stick"+side),touchGamepadNeedRelayout=!0,touchGamepadApplyStick(side,zone),touchGamepadButtonsPressed[touchGamepadStickButton(side)]=1)):"face"===hit.role?touchGamepadButtons[hit.btn]||(touchGamepadButtons[hit.btn]=touchGamepadButtonsPressed[hit.btn]=1,touchGamepadPointerRole.set(e.pointerId,"face"+hit.btn)):touchGamepadButtons[9]||(touchGamepadButtons[9]=touchGamepadButtonsPressed[9]=1,touchGamepadPointerRole.set(e.pointerId,"start")))))}(e,z)),z.addEventListener("pointermove",e=>function(e){var role=touchGamepadPointerRole.get(e.pointerId);role&&(e.preventDefault(),e=function(e){var r=touchGamepadStageRect();return vec2(e.clientX-r.left,e.clientY-r.top)}(e),"stick0"!==role&&"stick1"!==role||touchGamepadApplyStick("stick1"===role?1:0,e))}(e)),z.addEventListener("pointerup",e=>touchGamepadPointerUp(e)),z.addEventListener("pointercancel",e=>touchGamepadPointerUp(e)),stage.appendChild(z),z});touchGamepadSideZones[0]=svg(),touchGamepadSideZones[1]=svg(),touchGamepadZoneC=svg(),addEventListener("resize",()=>touchGamepadNeedRelayout=!0),document.body.appendChild(overlay),touchGamepadNeedRelayout=!0}{const applyDeadZones=v=>{var deadZone=v=>.3<v?percent(v,.3,.8):v<-.3?-percent(-v,.3,.8):0;return vec2(deadZone(v.x),deadZone(-v.y)).clampLength()};if(touchGamepadEnable&&isTouchDevice&&touchGamepadTimer.isSet()&&gamepadsEnable&&function(){for(const gamepad of inputGetGamepads())if(gamepad){for(const button of gamepad.buttons)if(button.pressed)return 1;if("standard"===gamepad.mapping)for(let j=0;j<4&&j<gamepad.axes.length;++j)if(.5<abs(gamepad.axes[j]))return 1}return}()&&touchGamepadTimer.unset(),touchGamepadEnable&&isTouchDevice&&touchGamepadTimer.isSet()){gamepadPrimary=0;var out,x,touchStick,sticks=gamepadStickData[0]??(gamepadStickData[0]=[]),dpad=gamepadDpadData[0]??(gamepadDpadData[0]=vec2());sticks.length=0,dpad.set();for(let side=0;side<2;side++)touchGamepadSideStick(side)&&(out=touchGamepadStickOut(side),sticks[out]=vec2(),touchStick=touchGamepadSticks[side]??vec2(),touchGamepadAnalog?sticks[out]=applyDeadZones(touchStick):.3<touchStick.lengthSquared()&&(x=clamp(round(touchStick.x),-1,1),touchStick=clamp(round(touchStick.y),-1,1),sticks[out]=vec2(x,-touchStick).clampLength(),out||dpad.set(x,-touchStick)));var data=inputData[1]??(inputData[1]=[]);for(let i=12;i--;){var wasDown=gamepadIsDown(i,0),isDown=touchGamepadButtons[i],pressed=touchGamepadButtonsPressed[i]||isDown&&!wasDown;data[i]=isDown?pressed?3:1:pressed?6:wasDown?4:0,touchGamepadVibration&&2&data[i]&&(9===i||function(i){for(let side=0;side<2;side++){var base=touchGamepadSideButtonBase(side);if(!touchGamepadSideStick(side)&&base<=i&&i<base+touchGamepadSideButtonCount(side))return 1}return}(i))&&vibrate(touchGamepadVibration)}touchGamepadButtonsPressed.length=0,gamepadButtonsLast[0]=void 0}else if(!gamepadsEnable||debug||document.hasFocus()){var gamepads=gamepadsEnable?inputGetGamepads():[];for(let i=0;i<8;++i){const gamepad=gamepads[i];if(gamepad){var data=inputData[i+1]??(inputData[i+1]=[]),buttonsLast=gamepadButtonsLast[i]??(gamepadButtonsLast[i]=[]),sticks=gamepadStickData[i]??(gamepadStickData[i]=[]),dpad=gamepadDpadData[i]??(gamepadDpadData[i]=vec2());const isStandard="standard"===gamepad.mapping,centered=gamepadAxisCentered[i]??(gamepadAxisCentered[i]=[]);var readAxis=j=>{var frames,v=isFinite(gamepad.axes[j])?gamepad.axes[j]:0;return isStandard&&j<4||!gamepadAxisFilterEnable||(frames=0|centered[j])>gamepadAxisCenteredFrames?v:(centered[j]=abs(v)<.3?1+frames:0,0)};for(let j=0;j<gamepad.axes.length-1;j+=2)sticks[j>>1]=applyDeadZones(vec2(readAxis(j),readAxis(j+1)));let hadInput=sticks.some(stick=>.25<stick.lengthSquared());for(let j=gamepad.buttons.length;j--;){var button=gamepad.buttons[j],wasDown=buttonsLast[j];buttonsLast[j]=button.pressed,data[j]=button.pressed?wasDown?1:3:wasDown?4:0,button.pressed&&(!button.value||.9<button.value)&&(hadInput=!0)}data.length=buttonsLast.length=gamepad.buttons.length,sticks.length=gamepad.axes.length>>1,hadInput&&(gamepadHadInput[i]=!0,gamepadHadInput[gamepadPrimary]||(gamepadPrimary=i)),"standard"===gamepad.mapping&&dpad.set((gamepadIsDown(15,i)&&1)-(gamepadIsDown(14,i)&&1),(gamepadIsDown(12,i)&&1)-(gamepadIsDown(13,i)&&1)),gamepadDirectionEmulateStick&&(dpad.x||dpad.y)?sticks[0]=dpad.clampLength():gamepadDirectionEmulateStick&&!sticks.length&&(sticks[0]=vec2())}else inputData[i+1]=void 0,gamepadStickData[i]=void 0,gamepadDpadData[i]=void 0,gamepadHadInput[i]=void 0,gamepadAxisCentered[i]=void 0,gamepadButtonsLast[i]=void 0}touchGamepadEnable&&isUsingGamepad&&touchGamepadTimer.unset()}else gamepadButtonsLast.length=0}{svg=mouseIsDown(0)||mouseIsDown(1)||mouseIsDown(2)||mouseDeltaScreen.length()>inputMouseMoveThreshold;let gamepadActive=!1;for(let s=gamepadStickCount();s--&&!gamepadActive;)gamepadActive=.2<gamepadStick(s).lengthSquared();for(let b=17;b--&&!gamepadActive;)gamepadActive=gamepadIsDown(b);let keyboardActive=!1;for(const k in inputData[0])if(isNaN(+k)&&1&inputData[0][k]){keyboardActive=!0;break}gamepadActive?lastInputDevice="gamepad":svg?lastInputDevice="mouse":keyboardActive&&(lastInputDevice="keyboard"),isUsingGamepad="gamepad"===lastInputDevice}}if(frozenTick||gameUpdate(),pluginList.forEach(plugin=>plugin.update?.()),frozenTick){for(const o of engineObjects)o.parent||o.updateTransforms();engineObjects.some(o=>o.destroyed)&&(engineObjects=engineObjects.filter(o=>!o.destroyed))}else engineObjectsUpdate();gameUpdatePost(),inputUpdatePost()}engineManualStep&&!manualStepAtStart&&(frameTimeBufferMS=-500/frameRate);let windowChanged=!1;if(headlessMode||(frameTimeMS=devicePixelRatio,windowChanged=windowWidthLast!==innerWidth||windowHeightLast!==innerHeight||windowPixelRatioLast!==frameTimeMS,windowWidthLast=innerWidth,windowHeightLast=innerHeight,windowPixelRatioLast=frameTimeMS),wasUpdated||windowChanged)if(!headlessMode){wasUpdated||engineUpdateCanvas(),mainContext.imageSmoothingEnabled=!tilesPixelated,glPreRender(),setShader(),pluginList.forEach(plugin=>plugin.preRender?.()),gameRender(),engineObjectsSort();for(const o of engineObjects)o.destroyed||(setShader(o.shader),o.render());setShader(),gameRenderPost(),setShader(),setAdditiveBlendMode(!1),pluginList.forEach(plugin=>plugin.render?.()),!function(){if(touchGamepadOverlay&&!headlessMode)if(touchGamepadEnable&&isTouchDevice){touchGamepadOverlay.style.display="";var dbg=debug&&debugGamepads,layout=[touchGamepadButtonCount,touchGamepadLeftButtonCount,touchGamepadLeftStick,touchGamepadRightStick,touchGamepadAnalog,touchGamepadSize,touchGamepadFloating,touchGamepadCenterButtonSize,paused,dbg].join(),layout=(layout!==touchGamepadLastLayout&&(touchGamepadLastLayout=layout,touchGamepadNeedRelayout=!0),touchGamepadNeedRelayout&&!function(){if(touchGamepadOverlay){var r=touchGamepadStageRect(),W=r.width,r=r.height,S=touchGamepadSize,setZone=(z,css)=>z.style.cssText="position:absolute;pointer-events:auto;touch-action:none;"+css;if(paused){for(const zone of touchGamepadSideZones)zone.style.display="none";touchGamepadCenterButtonSize?(setZone(touchGamepadZoneC,"inset:0"),touchGamepadZoneC.style.display=""):touchGamepadZoneC.style.display="none"}else{for(let side=0;side<2;side++){var width,zone=touchGamepadSideZones[side],edge=side?"right":"left";zone.style.display=touchGamepadSideHasControl(side)?"":"none",touchGamepadFloating&&touchGamepadSideStick(side)?(width=touchGamepadSideHasControl(side?0:1)?"50%":"100%",setZone(zone,edge+`:0;bottom:0;width:${width};height:60%`)):setZone(zone,edge+`:0;bottom:0;width:${3*S}px;height:${3*S}px`)}touchGamepadZoneC.style.display=touchGamepadCenterButtonSize?"":"none";var c=touchGamepadCenterButtonSize;setZone(touchGamepadZoneC,`left:50%;top:50%;width:${2*c}px;height:${2*c}px;transform:translate(-50%,-50%)`)}!function(W,H){const svg=touchGamepadSvg;for(;svg.firstChild;)svg.removeChild(svg.firstChild);const els=touchGamepadSvgEls={face:[],thumb:[]},S=touchGamepadSize,circle=(cx,cy,rr,fill)=>{var c=document.createElementNS(touchGamepadSvgNS,"circle");return c.setAttribute("cx",cx),c.setAttribute("cy",cy),c.setAttribute("r",rr),fill&&c.setAttribute("fill",fill),svg.appendChild(c),c};for(let side=0;side<2;side++){var count=touchGamepadSideButtonCount(side),base=touchGamepadSideButtonBase(side),ctr=touchGamepadSideCenter(side,W,H);if(touchGamepadSideStick(side))touchGamepadAnalog?circle(ctr.x,ctr.y,S/2):(ctr=>{var a=.18*S,b=.5*S,x=ctr.x,ctr=ctr.y,p=document.createElementNS(touchGamepadSvgNS,"path");p.setAttribute("d",`M ${x-a} ${ctr-b} H ${x+a} V ${ctr-a} H ${x+b} V ${ctr+a} H ${x+a} `+`V ${ctr+b} H ${x-a} V ${ctr+a} H ${x-b} V ${ctr-a} H ${x-a} Z`),svg.appendChild(p)})(ctr),els.thumb[side]=circle(ctr.x,ctr.y,S/4,"#fff");else if(1===count)els.face[base]=circle(ctr.x,ctr.y,S/2,"#000");else for(let i=0;i<count;i++){var button,j=mod(i-1,4),j=(button=touchGamepadFaceIndex(2<count?j:min(j,count-1)),vec2().setDirection(j,S/2)),j=(2===count&&(j.x*=-1),side||(j.x*=-1),ctr.add(j));els.face[base+button]=circle(j.x,j.y,S/4,"#000")}}debug&&debugGamepads&&!function(W,H){const S=touchGamepadSize,svg=touchGamepadSvg,shape=(tag,attrs,stroke)=>{var el=document.createElementNS(touchGamepadSvgNS,tag);for(const k in attrs)el.setAttribute(k,attrs[k]);el.setAttribute("stroke",stroke),el.setAttribute("stroke-width",2),el.setAttribute("fill","none"),svg.appendChild(el)},ring=(c,rr,stroke)=>shape("circle",{cx:c.x,cy:c.y,r:rr},stroke);shape("line",{x1:W/2,y1:0,x2:W/2,y2:H},"#0f0");for(let side=0;side<2;side++){var top,full,x;touchGamepadSideStick(side)?touchGamepadFloating?(top=.4*H,full=!touchGamepadSideHasControl(side?0:1),x=!full&&side?W/2:0,shape("rect",{x:x,y:top,width:full?W:W/2,height:H-top},"#0ff")):ring(touchGamepadSideCenter(side,W,H),2*S,"#0ff"):1<=touchGamepadSideButtonCount(side)&&ring(touchGamepadSideCenter(side,W,H),S,"#0ff")}if(touchGamepadCenterButtonSize){ring(vec2(W/2,H/2),touchGamepadCenterButtonSize,"#ff0");for(let side=0;side<2;side++)touchGamepadStartBlockSide(side)&&ring(touchGamepadSideCenter(side,W,H),2*S,"#f0f")}}(W,H)}(W,r),touchGamepadNeedRelayout=!1}}(),touchGamepadDisplayTime&&!touchGamepadPointerRole.size?percent(touchGamepadTimer.get(),touchGamepadDisplayTime+1,touchGamepadDisplayTime):1),visible=dbg||touchGamepadTimer.isSet()&&0<layout&&!paused;if(touchGamepadOverlay.style.opacity=visible?dbg?1:layout*touchGamepadAlpha:0,visible){var t,dbg=touchGamepadStageRect(),W=dbg.width,H=dbg.height,S=touchGamepadSize,els=touchGamepadSvgEls;if(els){for(let side=0;side<2;side++)touchGamepadSideStick(side)&&els.thumb[side]&&(t=touchGamepadSideCenter(side,W,H).add((touchGamepadSticks[side]??vec2()).scale(S/2)),els.thumb[side].setAttribute("cx",t.x),els.thumb[side].setAttribute("cy",t.y));for(let i=0;i<els.face.length;i++)els.face[i]&&els.face[i].setAttribute("fill",touchGamepadButtons[i]?"#fff":"#000")}}}else"none"!==touchGamepadOverlay.style.display&&(touchGamepadOverlay.style.display="none",touchGamepadPointerRole.clear(),touchGamepadButtons.length=0,touchGamepadButtonsPressed.length=0,touchGamepadSticks.length=0,touchGamepadStickPointerId.length=0,inputData[1]=void 0,gamepadStickData[0]=gamepadDpadData[0]=void 0)}(),glFlush(),drawCount=0,primitiveCount=0}engineManualStep||engineScheduleFrame()}if(engineInitialized=!0,console.log("LittleJS Engine v1.26.1"),engineLoads=[],engineLoadsDone=0,"string"==typeof imageSources&&(imageSources=[imageSources]),gameInit||=()=>{},gameUpdate||=()=>{},gameUpdatePost||=()=>{},gameRender||=()=>{},gameRenderPost||=()=>{},headlessMode)return engineInitDone=startEngine([]);document.body||document.documentElement.appendChild(document.createElement("body")),function(rootElement){if(!glEnable||headlessMode)glCanBeEnabled=!1;else if(glCanvas=document.createElement("canvas"),glContext=glCanvas.getContext("webgl2",{antialias:glAntialias})){rootElement.appendChild(glCanvas),initWebGL();for(const info of glTextureInfos)info.glTexture||=glCreateTexture(info.image,info.wrap,info.pixelated);glCanvas.addEventListener("webglcontextlost",e=>{glEnableBeforeLoss=glEnable,glEnable=!1,glCanvas.style.display="none",e.preventDefault();for(const info of glTextureInfos)info.glTexture=void 0;glActiveTexture=void 0;for(const shader of glShaderObjects)shader.program=void 0;glBatchShader=void 0,glProgramCustom=!0,glUniformLocations=new WeakMap,glBatchCount=0,glPolyMode=!1,pluginList.forEach(plugin=>plugin.glContextLost?.())}),glCanvas.addEventListener("webglcontextrestored",()=>{glEnable=glEnableBeforeLoss,glCanvas.style.display="",glMipmappedTextures=new WeakSet,glMipmapsUntilTarget=new WeakSet,glMipmapsStale.clear(),glPremultipliedTextures=new WeakSet,initWebGL();for(const info of glTextureInfos)info.glTexture=glCreateTexture(info.image,info.wrap,info.pixelated);pluginList.forEach(plugin=>plugin.glContextRestored?.());for(const o of engineObjects)o instanceof TileLayer&&o.isUsingWebGL&&!o.destroyed&&o.redraw()})}else console.warn("WebGL2 not supported, falling back to 2D canvas rendering!"),glCanvas=glContext=void 0,glEnable=!1,glCanBeEnabled=!1,glDeviceFailed=!0;function initWebGL(){glMaxTextureSize=0|glContext.getParameter(glContext.MAX_TEXTURE_SIZE),glShader=glCreateProgram(gl_VERTEX_SOURCE,"#version 300 es\nprecision highp float;uniform sampler2D s;uniform bool premultipliedTexture;in vec2 v;in vec4 d,e;out vec4 c;void main(){vec4 t=texture(s,v);"+gl_FRAGMENT_TINT_SOURCE+"}"),glShaderPremultiplied=!1,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;}"),(glFailedPrograms.has(glShader)||glFailedPrograms.has(glPolyShader))&&(console.error("LittleJS: WebGL can not draw on this device, using Canvas2D"),glEnable=glCanBeEnabled=!1,glDeviceFailed=!0,glCanvas.style.display="none");var glInstanceData=new ArrayBuffer(gl_ARRAY_BUFFER_SIZE),glInstanceData=(glPositionData=new Float32Array(glInstanceData),glColorData=new Uint32Array(glInstanceData),glArrayBuffer=glContext.createBuffer(),glGeometryBuffer=glContext.createBuffer(),glFramebuffer=glContext.createFramebuffer(),glBatchCount=0,new Float32Array([0,0,1,0,0,1,1,1]));glContext.bindBuffer(glContext.ARRAY_BUFFER,glGeometryBuffer),glContext.bufferData(glContext.ARRAY_BUFFER,glInstanceData,glContext.STATIC_DRAW);let offset,shader,stride;glInstanceData=(name,type,typeSize,size,divisor=0)=>{var name=glContext.getAttribLocation(shader,name),normalize=1===typeSize,fixedStride=typeSize&&stride;glContext.enableVertexAttribArray(name),glContext.vertexAttribPointer(name,size,type,normalize,fixedStride,offset),glContext.vertexAttribDivisor(name,divisor),offset+=size*typeSize};glInstancedVAO=glContext.createVertexArray(),glContext.bindVertexArray(glInstancedVAO),offset=0,shader=glShader,stride=gl_INSTANCE_BYTE_STRIDE,glContext.bindBuffer(glContext.ARRAY_BUFFER,glGeometryBuffer),glInstanceData("g",glContext.FLOAT,0,2),glContext.bindBuffer(glContext.ARRAY_BUFFER,glArrayBuffer),glContext.bufferData(glContext.ARRAY_BUFFER,gl_ARRAY_BUFFER_SIZE,glContext.DYNAMIC_DRAW),glInstanceData("p",glContext.FLOAT,4,4,1),glInstanceData("u",glContext.FLOAT,4,4,1),glInstanceData("c",glContext.UNSIGNED_BYTE,1,4,1),glInstanceData("a",glContext.UNSIGNED_BYTE,1,4,1),glInstanceData("r",glContext.FLOAT,4,1,1),glPolyVAO=glContext.createVertexArray(),glContext.bindVertexArray(glPolyVAO),offset=0,shader=glPolyShader,stride=gl_POLY_VERTEX_BYTE_STRIDE,glInstanceData("p",glContext.FLOAT,4,2),glInstanceData("c",glContext.UNSIGNED_BYTE,1,4)}}(rootElement||=document.body);let styleRoot="margin:0;overflow:hidden;background:#000;user-select:none;-webkit-user-select:none;touch-action:none;-webkit-touch-callout:none;";function handleTouch(e){if(soundEnable&&!headlessMode&&audioContext&&!audioIsRunning()&&audioResume(),touchInputEnable){if(inputLastTouchTime=performance.now(),!touchGamepadEnable||touchGamepadPassthrough){const isGamepadTouch=t=>touchGamepadSideZones.includes(t.target)||t.target===touchGamepadZoneC;var gameTouches=[];for(const t of e.touches)isGamepadTouch(t)||gameTouches.push(t);var touching=gameTouches.length;if(touching){var pressTouch=!inputWasTouching&&"touchstart"==e.type&&[...e.changedTouches].find(t=>!isGamepadTouch(t)),touch=pressTouch||gameTouches[0];let pos=vec2(touch.clientX,touch.clientY);var a,b,distance,pinching=1<touching&&touchPinchWheel,delta=(1<touching?([a,b]=gameTouches,key=a.identifier+" "+b.identifier,distance=hypot(a.clientX-b.clientX,a.clientY-b.clientY),inputPinch&&inputPinch.key===key&&(delta=(inputPinch.distance-distance)/50,touchPinch+=delta,touchPinchWheel)&&(mouseWheel+=delta),inputPinch={key:key,distance:distance},pinching&&(pos=vec2((a.clientX+b.clientX)/2,(a.clientY+b.clientY)/2))):inputPinch=void 0,mousePosScreen);mousePosScreen=mouseEventToScreen(pos),pinching?1&inputData[0][0]&&(inputData[0][0]=2&inputData[0][0]|4):inputWasTouching&&touch.identifier===inputTouchIdentifier?mouseDeltaScreen=mouseDeltaScreen.add(mousePosScreen.subtract(delta)):pressTouch&&inCanvas(pressTouch.clientX,pressTouch.clientY)&&(inputData[0][0]=3|4&inputData[0][0]),inputTouchIdentifier=pinching?void 0:touch.identifier}else inputPinch=void 0,inputWasTouching&&1&inputData[0][0]&&(inputData[0][0]=2&inputData[0][0]|4);inputWasTouching=touching}var key;return inputPreventDefault&&e.cancelable&&document.hasFocus()&&!isOnControl(e)&&(key=document.activeElement,"touchstart"==e.type&&isTextInput(key)&&!key.contains(e.target)&&key.blur(),e.preventDefault()),!0}inputWasTouching&&1&inputData[0][0]&&(inputData[0][0]=2&inputData[0][0]|4),inputWasTouching=0}function isOnControl(e){return e.target?.closest?.("input,textarea,select,button,a,label,[contenteditable]")}function isTextInput(element){var tag=element?.tagName;return"INPUT"===tag?!["button","checkbox","color","file","image","radio","range","reset","submit"].includes(element.type):!!element?.isContentEditable||"TEXTAREA"===tag||"SELECT"===tag}function inCanvas(x,y){var rect=mainCanvas.getBoundingClientRect();return x>=rect.left&&x<=rect.right&&y>=rect.top&&y<=rect.bottom}function mouseEventToScreen(mousePos){var rect=mainCanvas.getBoundingClientRect(),px=percent(mousePos.x,rect.left,rect.right),mousePos=percent(mousePos.y,rect.top,rect.bottom);return vec2(px*mainCanvasSize.x,mousePos*mainCanvasSize.y)}rootElement!==document.body&&rootElement.clientHeight&&"static"===getComputedStyle(rootElement).position&&(styleRoot+="position:relative;"),rootElement.style.cssText=styleRoot+rootElement.style.cssText,mainCanvas=rootElement.appendChild(document.createElement("canvas")),drawContext=mainContext=mainCanvas.getContext("2d"),headlessMode||(document.addEventListener("keydown",function(e){soundEnable&&!headlessMode&&audioContext&&!audioIsRunning()&&audioResume();var typing=isTextInput(e.target)||isTextInput(document.activeElement);e.repeat||typing||inputKeysHeld.has(e.code)&&!e.metaKey||(inputKeysHeld.add(e.code),inputWASDEmulateDirection&&inputKeysHeld.has(inputArrowToWASD[e.code])&&1&inputData[0][e.code]||(inputData[0][e.code]=3|4&inputData[0][e.code]),(remap=function(k){return inputWASDEmulateDirection&&inputWASDToArrow[k]||k}(e.code))===e.code)||1&inputData[0][remap]||(inputData[0][remap]=3|4&inputData[0][remap]);var remap;inputPreventDefault&&e.cancelable&&document.hasFocus()&&(e.ctrlKey||e.metaKey||e.altKey||!typing&&(remap="string"==typeof e.key&&1===e.key.length,["ArrowUp","ArrowDown","ArrowLeft","ArrowRight","Space","Tab","Backspace"].includes(e.code)||remap)&&e.preventDefault())}),document.addEventListener("keyup",function onKeyUp(e){inputKeysHeld.delete(e.code);const remap=inputWASDToArrow[e.code]||e.code;for(const key of remap===e.code?[e.code]:[e.code,remap])inputKeysHeld.has(key)||inputWASDEmulateDirection&&inputKeysHeld.has(inputArrowToWASD[key])||1&inputData[0][key]&&(inputData[0][key]=2&inputData[0][key]|4);if("Meta"===e.key)for(const code of[...inputKeysHeld])onKeyUp({code:code})}),document.addEventListener("mousedown",function(e){var mousePosScreenLast;touchInputEnable&&inputIsTouchMouseEvent(e)||(soundEnable&&!headlessMode&&audioContext&&!audioIsRunning()&&audioResume(),(pointerLockIsActive()||inCanvas(e.x,e.y))&&(inputData[0][e.button]=3|4&inputData[0][e.button],mousePosScreenLast=mousePosScreen,mousePosScreen=mouseEventToScreen(vec2(e.x,e.y)),mouseDeltaScreen=mouseDeltaScreen.add(mousePosScreen.subtract(mousePosScreenLast)),inputPreventDefault)&&e.cancelable&&document.hasFocus()&&!isOnControl(e)&&(isTextInput(mousePosScreenLast=document.activeElement)&&!mousePosScreenLast.contains(e.target)&&mousePosScreenLast.blur(),e.preventDefault()))}),document.addEventListener("mouseup",function(e){touchInputEnable&&inputIsTouchMouseEvent(e)||(1&inputData[0][e.button]&&(inputData[0][e.button]=2&inputData[0][e.button]|4),inputPreventDefault&&e.cancelable&&(3===e.button||4===e.button)&&e.preventDefault())}),document.addEventListener("mousemove",function(e){mouseInWindow=!0;var mousePosScreenLast=mousePosScreen;mousePosScreen=mouseEventToScreen(vec2(e.x,e.y));let movement;{var rect;movement=pointerLockIsActive()?(rect=mainCanvas.getBoundingClientRect(),vec2(e.movementX*mainCanvasSize.x/rect.width,e.movementY*mainCanvasSize.y/rect.height)):mousePosScreen.subtract(mousePosScreenLast)}mouseDeltaScreen=mouseDeltaScreen.add(movement)}),document.addEventListener("mouseleave",function(){mouseInWindow=!1}),document.addEventListener("wheel",function(e){var target=e.target;target?.closest?.("input,textarea,select,[contenteditable]")||(e.ctrlKey||(mouseWheel+=sign(e.deltaY)),inputPreventDefault&&e.cancelable&&document.hasFocus()&&e.preventDefault())},{passive:!1}),document.addEventListener("contextmenu",function(e){var target=e.target;!inputPreventDefault||isTextInput(target)||target?.closest?.("a[href]")||e.preventDefault()}),addEventListener("blur",function(){inputClear()}),isTouchDevice&&(document.addEventListener("touchstart",e=>handleTouch(e),{passive:!1}),document.addEventListener("touchmove",e=>handleTouch(e),{passive:!1}),document.addEventListener("touchend",e=>handleTouch(e),{passive:!1}),document.addEventListener("touchcancel",e=>handleTouch(e),{passive:!1}))),soundEnable&&!headlessMode&&(audioSetSession(),document.addEventListener("visibilitychange",audioVisibilityChange));rootElement="position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)",mainCanvas.style.cssText=rootElement,glCanvas&&(glCanvas.style.cssText=rootElement),setCanvasPixelated(canvasPixelated),engineUpdateCanvas(),glPreRender(),workContext=createCanvasContext(64),workCanvas=workContext.canvas,workReadContext=createCanvasContext(64,64,!0),workReadCanvas=workReadContext.canvas,rootElement=imageSources.map((src,i)=>loadTexture(i,src));return imageSources.length||rootElement.push(loadTexture(0)),rootElement.push(async function(){const image=new Image,tilePos=(await new Promise(resolve=>{image.onerror=image.onload=resolve,image.crossOrigin="anonymous",image.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=="}),vec2()),tileSize=vec2(8),textureInfo=new TextureInfo(image),tileInfo=new TileInfo(tilePos,tileSize,textureInfo,1,0);engineImageFont=new ImageFont(tileInfo)}()),showSplashScreen&&(imageSources=new Promise(resolve=>{let t=0;!function updateSplash(){inputClear();drawEngineLogo(t+=.01);1<t?resolve():setTimeout(updateSplash,16)}()}),rootElement.push(imageSources)),engineInitDone=startEngine(rootElement);async function startEngine(images){showSplashScreen&&!headlessMode&&await images[images.length-1];var init=(async()=>{await Promise.all(images),await gameInit()})();if(engineAddLoad(init),await engineWaitForLoads(),engineLoads=void 0,await 0,engineUpdateInternal=engineUpdate,!engineManualStep){if(debug)return engineUpdate();try{engineUpdate()}catch(error){engineFrameFailed(error)}}}}function engineUpdateCanvas(){if(!headlessMode){var dpr=getCanvasPixelRatio();if(canvasFixedSize.x){mainCanvasSize.set(canvasFixedSize.x,canvasFixedSize.y);var innerAspect=innerWidth/innerHeight,fixedAspect=canvasFixedSize.x/canvasFixedSize.y,w=innerAspect<fixedAspect?"100%":"",innerAspect=innerAspect<fixedAspect?"":"100%";mainCanvas.style.width=w,mainCanvas.style.height=innerAspect,glCanvas&&(glCanvas.style.width=w,glCanvas.style.height=innerAspect)}else{mainCanvasSize.x=0|min(innerWidth,canvasMaxSize.x),mainCanvasSize.y=0|min(innerHeight,canvasMaxSize.y);const innerAspect=mainCanvasSize.x/mainCanvasSize.y;if(canvasMaxAspect&&innerAspect>canvasMaxAspect){const w=mainCanvasSize.y*canvasMaxAspect|0;mainCanvasSize.x=min(w,canvasMaxSize.x)}else if(innerAspect<canvasMinAspect){const h=mainCanvasSize.x/canvasMinAspect|0;mainCanvasSize.y=min(h,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")}fixedAspect=mainCanvasSize.x*dpr|0,w=mainCanvasSize.y*dpr|0;mainCanvas.width!==fixedAspect||mainCanvas.height!==w?(mainCanvas.width=fixedAspect,mainCanvas.height=w):(mainContext.setTransform(1,0,0,1,0,0),mainContext.globalCompositeOperation="source-over",mainContext.globalAlpha=1,mainContext.filter="none",mainContext.shadowColor="rgba(0,0,0,0)",mainContext.clearRect(0,0,fixedAspect,w)),mainContext.setTransform(dpr,0,0,dpr,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"}}function engineFrameFailed(error){var text=String(error);text!==engineFrameErrorLast&&console.error(error),engineFrameErrorLast=text,inputUpdatePost(),engineScheduleFrame()}function engineScheduleFrame(){if(!engineFrameScheduled){engineFrameScheduled=!0;const next=frameTimeMS=>{if(engineFrameScheduled=!1,!engineManualStep){if(debug)return engineUpdateInternal(frameTimeMS);try{engineUpdateInternal(frameTimeMS)}catch(error){engineFrameFailed(error)}}};"function"==typeof requestAnimationFrame?requestAnimationFrame(next):setTimeout(()=>next(performance.now()),1e3/frameRate)}}function engineStep(frames=1){if(engineManualStep&&engineUpdateInternal)for(let i=frames=min(frames,36e3);0<i&&engineManualStep;--i)engineUpdateInternal(frameTimeLastMS+1e3/frameRate)}function engineObjectsUpdate(){++engineObjectsUpdateCount,engineObjectsCollidePairs.clear(),engineObjectsSort();var fixed=[];engineObjectsCollide=[],engineObjectsCollideStaticLast=[];for(const o of engineObjects)o.collideSolidObjects&&(engineObjectsCollide.push(o),(o.mass?engineObjectsCollideStaticLast:fixed).push(o));for(const o of fixed)engineObjectsCollideStaticLast.push(o);engineObjectsCollideStaticLast.length>=engineCollideGridMin&&(engineCollideGrid={list:engineObjectsCollideStaticLast,built:void 0});try{for(const o of engineObjects)o.parent||o.destroyed||(o.updatePhysics(),engineCollideGrid?.built&&engineCollideGridPlace(engineCollideGrid.built,o))}finally{engineCollideGrid=engineCollideGrid&&void 0}function updateChildObjects(children){if(children.length){var o,start=engineChildStack.length;for(const child of children)engineChildStack.push(child);try{for(let i=start;i<engineChildStack.length;++i)(o=engineChildStack[i]).destroyed||o.updatePass===pass||(o.updatePass=pass,o.updateTransforms(!1),o.update(),o.children.length&&o.updateTransforms(!1),updateChildObjects(o.children))}finally{engineChildStack.length=start}}}const pass=engineObjectsUpdateCount;function updateTopObject(o){o.parent||o.destroyed||o.updatePass===pass||(o.updatePass=pass,o.update(),updateChildObjects(o.children),o.updateTransforms())}for(const o of engineObjects)updateTopObject(o);for(const o of engineObjects)updateTopObject(o);engineObjects.some(o=>o.destroyed)&&(engineObjects=engineObjects.filter(o=>!o.destroyed))}function engineObjectsSort(){for(let i=engineObjects.length;0<--i;)if(engineObjects[i].renderOrder<engineObjects[i-1].renderOrder)return engineObjects.sort((a,b)=>a.renderOrder-b.renderOrder)}function engineObjectsDestroy(immediate=!0){for(const o of engineObjects)o.parent||o.persistent||o.destroy(immediate);engineObjects=engineObjects.filter(o=>!o.destroyed)}function engineObjectsCollect(pos,size,objects=engineObjects,testCenters=!1){var collectedObjects=[];if(pos)if(!size||size instanceof Vector2){var boxSize=size instanceof Vector2?size:vec2();for(const o of objects)o.destroyed||(testCenters?isOverlapping(pos,boxSize,o.pos):o.isOverlapping(pos,boxSize))&&collectedObjects.push(o)}else{var dx,radiusSquared=(size/2)**2;for(const o of objects)o.destroyed||(dx=testCenters?pos.x-o.pos.x:max(abs(pos.x-o.pos.x)-o.size.x/2,0))*dx+(dx=testCenters?pos.y-o.pos.y:max(abs(pos.y-o.pos.y)-o.size.y/2,0))*dx<radiusSquared&&collectedObjects.push(o)}else for(const o of objects)o.destroyed||collectedObjects.push(o);return collectedObjects}function engineObjectsCallback(pos,size,callbackFunction,objects=engineObjects,testCenters=!1){for(const o of engineObjectsCollect(pos,size,objects,testCenters))o.destroyed||callbackFunction(o)}function engineObjectsRaycast(start,end,objects=engineObjects){var hitObjects=[];for(const o of objects)o.collideRaycast&&!o.destroyed&&isIntersecting(start,end,o.pos,o.size)&&hitObjects.push(o);return hitObjects}let debugWatermark=!1,debugKey="",debugKeysAlways=!1,debugTweakables=!1;const debug=!1,debugOverlay=!1,debugGamepads=!1;var debugPointSize=.5;function ASSERT(){}function LOG(){}function debugRect(){}function debugPoly(){}function debugCircle(){}function debugPoint(){}function debugLine(){}function debugOverlap(){}function debugText(){}function debugClear(){}function debugScreenshot(){}function debugShowErrors(){}function setDebugOverlay(){}function debugProtectConstant(o){return Object.freeze(o)}function tweak(){}function tweakButton(){}function tweakDivider(){}function tweakEngineDefaults(){}const editorEdit2DRelease={map:void 0,layer:void 0,hover:void 0,get objects(){return[]},get selection(){return new Set},paint(){return!1},changeObjects(){return!1},strokeEnd(){},strokeCancel(){},bulk(){},undo(){return!1},toJSON(){return""}};class LevelEditor{constructor(){this.paletteTiles=this.use3D=this.tool=void 0,this.keys={},this.buttons=[],this.tools={}}get isOpen(){return!1}get is3D(){return!1}get edit2D(){return editorEdit2DRelease}get edit3D(){}open(){}close(){}addKey(){}addButton(){}addTool(){}onTile(){}onRestart(){}onPlayFrom(){}onOpen(){}onClose(){}onUpdate(){}onDraw(){}onPanel(){}onSave(){return!1}}let levelEditor=new LevelEditor;function setLevelEditor(editor){levelEditor=editor}function debugBox3D(){}function debugSphere3D(){}function debugLine3D(){}function debugPoint3D(){}const PI=Math.PI,abs=Math.abs,floor=Math.floor,ceil=Math.ceil,round=Math.round,min=Math.min,max=Math.max,sign=x=>Math.sign(x),hypot=Math.hypot,log2=x=>Math.log2(x),sin=Math.sin,cos=Math.cos,tan=Math.tan,atan2=Math.atan2;function mod(dividend,divisor=1){return(dividend%divisor+divisor)%divisor}function clamp(value,min=0,max=1){return value<min?min:max<value?max:value}function percent(value,valueA,valueB){return(valueB-=valueA)?clamp((value-valueA)/valueB):0}function lerp(valueA,valueB,percent){return valueA+clamp(percent)*(valueB-valueA)}function percentLerp(value,percentA,percentB,lerpA,lerpB){return lerp(lerpA,lerpB,percent(value,percentA,percentB))}function distanceWrap(valueA,valueB,wrapSize=1){valueA=(valueA-valueB)%wrapSize;return 2*valueA%wrapSize-valueA}function lerpWrap(valueA,valueB,percent,wrapSize=1){return valueA+clamp(percent)*distanceWrap(valueB,valueA,wrapSize)}function distanceAngle(angleA,angleB){return distanceWrap(angleA,angleB,2*PI)}function lerpAngle(angleA,angleB,percent){return lerpWrap(angleA,angleB,percent,2*PI)}function smoothStep(percent){return(percent=clamp(percent))*percent*(3-2*percent)}function isPowerOfTwo(value){return 0<value&&value%1==0&&2**round(log2(value))===value}function nearestPowerOfTwo(value){var power=2**ceil(log2(max(value,1)));return power<value?2*power:power}function isOverlapping(posA,sizeA,posB,sizeB=vec2()){var dx=2*(posA.x-posB.x),posA=2*(posA.y-posB.y),posB=sizeA.x+sizeB.x,sizeA=sizeA.y+sizeB.y;return abs(dx)<posB&&abs(posA)<sizeA}function isIntersecting(start,end,pos,size){var pos=pos.subtract(size.scale(.5)),size=pos.add(size),end=end.subtract(start),pos=start.subtract(pos),start=start.subtract(size),p=[-end.x,end.x,-end.y,end.y],q=[pos.x,-start.x,pos.y,-start.y];let tMin=0,tMax=1;for(let i=4;i--;)if(p[i]){var t=q[i]/p[i];if(p[i]<0){if(t>tMax)return!1;tMin=max(t,tMin)}else{if(t<tMin)return!1;tMax=min(t,tMax)}}else if(q[i]<0)return!1;return!0}function collideCircleCircle(posA,radiusA,posB,radiusB){posA=posA.subtract(posB),posB=radiusA+radiusB,radiusA=posA.length();if(!(posB<=radiusA))return posA.normalize(posB-radiusA)}function collideCircleBox(pos,radius,boxPos,boxSize){var boxSize=boxSize.scale(.5),closest=vec2(clamp(pos.x,boxPos.x-boxSize.x,boxPos.x+boxSize.x),clamp(pos.y,boxPos.y-boxSize.y,boxPos.y+boxSize.y)),closest=pos.subtract(closest),distSq=closest.lengthSquared();return distSq?radius*radius<=distSq?void 0:closest.normalize(radius-distSq**.5):pushOutAxis(closest=pos.subtract(boxPos),boxSize.x-abs(closest.x),boxSize.y-abs(closest.y),radius)}function collideBoxBox(posA,sizeA,posB,sizeB){posA=posA.subtract(posB),posB=(sizeA.x+sizeB.x)/2-abs(posA.x),sizeA=(sizeA.y+sizeB.y)/2-abs(posA.y);if(!(posB<=0||sizeA<=0))return pushOutAxis(posA,posB,sizeA)}function pushOutAxis(d,penX,penY,extra=0){var s=v=>0<=v?1:-1;return penX<=penY?vec2(s(d.x)*(penX+extra),0):vec2(0,s(d.y)*(penY+extra))}function oscillate(frequency=1,amplitude=1,t=time,offset=0,type=0){offset=mod(offset+t*frequency,1);let value;return amplitude/2*((value=1===type?1-2*abs(2*offset-1):2===type?offset<.5?-1:1:3===type?2*offset-1:-cos(2*offset*PI))+1)}function isNumber(n){return"number"==typeof n&&n==n}function isStringLike(s){return null!=s&&"function"==typeof s.toString&&"string"==typeof s.toString()}function isArray(a){return Array.isArray(a)}function lineTest(posStart,posEnd,testFunction,normal){var dx=posEnd.x-posStart.x,posEnd=posEnd.y-posStart.y,totalLength=(dx*dx+posEnd*posEnd)**.5;if(totalLength){var hitPos,hitPosFloor,pos=posStart.floor(),dirX=(dx<0&&pos.x===posStart.x&&--pos.x,posEnd<0&&pos.y===posStart.y&&--pos.y,dx/totalLength),dirY=posEnd/totalLength,stepX=sign(dirX),stepY=sign(dirY),tDeltaX=dirX?abs(1/dirX):1/0,tDeltaY=dirY?abs(1/dirY):1/0,dx=0<stepX?pos.x+1:pos.x,posEnd=0<stepY?pos.y+1:pos.y;let t=0,tX=dirX?(dx-posStart.x)/dirX:1/0,tY=dirY?(posEnd-posStart.y)/dirY:1/0,wasX=tDeltaX<tDeltaY;for(;t<totalLength;){if(testFunction(pos))return(hitPosFloor=(hitPos=vec2(posStart.x+dirX*t,posStart.y+dirY*t)).floor()).x<pos.x?hitPos.x=pos.x:hitPosFloor.x>pos.x&&(hitPos.x=pos.x+1-1e-9),hitPosFloor.y<pos.y?hitPos.y=pos.y:hitPosFloor.y>pos.y&&(hitPos.y=pos.y+1-1e-9),normal&&(wasX?normal.set(-stepX,0):normal.set(0,-stepY)),hitPos;(wasX=tX<tY)?(pos.x+=stepX,t=tX,tX+=tDeltaX):(pos.y+=stepY,t=tY,tY+=tDeltaY)}}}function rand(valueA=1,valueB=0){return valueB+Math.random()*(valueA-valueB)}function randInt(valueA,valueB=0){return floor(rand(valueA,valueB))}function randBool(chance=.5){return rand()<chance}function randSign(){return 2*randInt(2)-1}function randVec2(length=1){return(new Vector2).setAngle(rand(2*PI),length)}function randInCircle(radius=1,minRadius=0){return radius<=0?new Vector2:randVec2(radius*rand((minRadius=clamp(minRadius/radius))*minRadius,1)**.5)}function randColor(colorA=new Color,colorB=new Color(0,0,0,1),linear=!1){return linear?colorA.lerp(colorB,rand()):new Color(rand(colorA.r,colorB.r),rand(colorA.g,colorB.g),rand(colorA.b,colorB.b),rand(colorA.a,colorB.a))}class RandomGenerator{constructor(seed=123456789){this.seed=seed}float(valueA=1,valueB=0){return this.seed=0|this.seed||123456789,this.seed^=this.seed<<13,this.seed^=this.seed>>>17,this.seed^=this.seed<<5,valueB+(valueA-valueB)*noiseHash(this.seed)}int(valueA,valueB=0){return floor(this.float(valueA,valueB))}bool(chance=.5){return this.float()<chance}sign(){return.5<this.float()?1:-1}floatSign(valueA=1,valueB=0){var lo=min(valueA,valueB),valueA=max(valueA,valueB)-lo,valueB=this.float(2*valueA);return valueB<valueA?lo+valueB:valueA-lo-valueB}angle(){return this.float(-PI,PI)}vec2(valueA=1,valueB=0){return vec2(this.float(valueA,valueB),this.float(valueA,valueB))}direction(length=1){return(new Vector2).setAngle(this.float(2*PI),length)}randColor(colorA=new Color,colorB=new Color(0,0,0,1),linear=!1){return linear?colorA.lerp(colorB,this.float()):new Color(this.float(colorA.r,colorB.r),this.float(colorA.g,colorB.g),this.float(colorA.b,colorB.b),this.float(colorA.a,colorB.a))}mutateColor(color,amount=.05,alphaAmount=0){return new Color(color.r+this.float(amount,-amount),color.g+this.float(amount,-amount),color.b+this.float(amount,-amount),color.a+this.float(alphaAmount,-alphaAmount)).clamp()}}function vec2(x=0,y){return new Vector2(x,y??x)}function isVector2(v){return v instanceof Vector2&&v.isValid()}class Vector2{constructor(x=0,y=0){this.x=x,this.y=y}set(x=0,y=0){return this.x=x,this.y=y,this}setFrom(v){return this.set(v.x,v.y)}copy(){return new Vector2(this.x,this.y)}add(v){return new Vector2(this.x+v.x,this.y+v.y)}subtract(v){return new Vector2(this.x-v.x,this.y-v.y)}multiply(v){return new Vector2(this.x*v.x,this.y*v.y)}divide(v){return new Vector2(this.x/v.x,this.y/v.y)}scale(s){return new Vector2(this.x*s,this.y*s)}length(){return this.lengthSquared()**.5}lengthSquared(){return this.x**2+this.y**2}distance(v){return this.distanceSquared(v)**.5}distanceSquared(v){return(this.x-v.x)**2+(this.y-v.y)**2}normalize(length=1){var l=this.length();return l?this.scale(length/l):new Vector2(0,length)}clampLength(length=1){var l=this.length();return length<l?this.scale(length/l):this.copy()}dot(v){return this.x*v.x+this.y*v.y}cross(v){return this.x*v.y-this.y*v.x}reflect(normal,restitution=1){return this.subtract(normal.scale((1+restitution)*this.dot(normal)))}angle(){return atan2(this.x,this.y)}setAngle(angle=0,length=1){return this.x=length*sin(angle),this.y=length*cos(angle),this}rotate(angle){var c=cos(-angle),angle=sin(-angle);return new Vector2(this.x*c-this.y*angle,this.x*angle+this.y*c)}setDirection(direction,length=1){return direction=mod(direction,4),this.x=direction%2?direction-1?-length:length:0,this.y=direction%2?0:direction?-length:length,this}direction(){return abs(this.x)>abs(this.y)?this.x<0?3:1:this.y<0?2:0}abs(){return new Vector2(abs(this.x),abs(this.y))}floor(){return new Vector2(floor(this.x),floor(this.y))}round(){return new Vector2(round(this.x),round(this.y))}snap(grid){return new Vector2(floor(this.x*grid)/grid,floor(this.y*grid)/grid)}mod(divisor=1){return new Vector2(mod(this.x,divisor),mod(this.y,divisor))}area(){return abs(this.x*this.y)}lerp(v,percent){percent=clamp(percent);return new Vector2(v.x*percent+this.x*(1-percent),v.y*percent+this.y*(1-percent))}arrayCheck(arraySize){return 0<=this.x&&0<=this.y&&this.x<arraySize.x&&this.y<arraySize.y}toString(digits=3){return this.isValid()?`(${(this.x<0?"":" ")+this.x.toFixed(digits)},${(this.y<0?"":" ")+this.y.toFixed(digits)} )`:`(${this.x}, ${this.y})`}isValid(){return isNumber(this.x)&&isNumber(this.y)}}function rgb(r,g,b,a){return debug&&(2<r||2<g||2<b)&&colorRangeWarn("rgb(255, 0, 0) is rgb(1, 0, 0)"),new Color(r,g,b,a)}let colorRangeWarned=!1;function colorRangeWarn(example){colorRangeWarned||(colorRangeWarned=!0,console.warn("color values are 0 to 1 here, not 0 to 255 or percents: "+example))}function hsl(h,s,l,a){return debug&&(2<s||2<l)&&colorRangeWarn("hsl(.5, 100, 50) is hsl(.5, 1, .5)"),(new Color).setHSLA(h,s,l,a)}function isColor(c){return c instanceof Color&&c.isValid()}class Color{constructor(r=1,g=1,b=1,a=1){this.r=r,this.g=g,this.b=b,this.a=a}set(r=1,g=1,b=1,a=1){return this.r=r,this.g=g,this.b=b,this.a=a,this}setFrom(c){return this.set(c.r,c.g,c.b,c.a)}setAlpha(a=1){return this.a=a,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(c){return new Color(this.r+c.r,this.g+c.g,this.b+c.b,this.a+c.a)}subtract(c){return new Color(this.r-c.r,this.g-c.g,this.b-c.b,this.a-c.a)}multiply(c){return new Color(this.r*c.r,this.g*c.g,this.b*c.b,this.a*c.a)}divide(c){return new Color(this.r/c.r,this.g/c.g,this.b/c.b,this.a/c.a)}scale(scale,alphaScale=scale){return new Color(this.r*scale,this.g*scale,this.b*scale,this.a*alphaScale)}clamp(){return new Color(clamp(this.r),clamp(this.g),clamp(this.b),clamp(this.a))}lerp(c,percent){percent=clamp(percent);return new Color(c.r*percent+this.r*(1-percent),c.g*percent+this.g*(1-percent),c.b*percent+this.b*(1-percent),c.a*percent+this.a*(1-percent))}setHSLA(h=0,s=0,l=1,a=1){h=mod(h,1),s=clamp(s);var s=(l=clamp(l))<.5?l*(1+s):l+s-l*s,l=2*l-s,f=(p,q,t)=>6*(t=mod(t,1))<1?p+6*(q-p)*t:2*t<1?q:3*t<2?p+(q-p)*(4-6*t):p;return this.r=f(l,s,h+1/3),this.g=f(l,s,h),this.b=f(l,s,h-1/3),this.a=a,this}HSLA(){var d,r=clamp(this.r),g=clamp(this.g),b=clamp(this.b),a=clamp(this.a),maxC=max(r,g,b),minC=min(r,g,b),l=(maxC+minC)/2;let h=0,s=0;return maxC!==minC&&(d=maxC-minC,s=.5<l?d/(2-maxC-minC):d/(maxC+minC),r===maxC?h=(g-b)/d+(g<b?6:0):g===maxC?h=(b-r)/d+2:b===maxC&&(h=(r-g)/d+4)),[h/6,s,l,a]}mutate(amount=.05,alphaAmount=0){return new Color(this.r+rand(amount,-amount),this.g+rand(amount,-amount),this.b+rand(amount,-amount),this.a+rand(alphaAmount,-alphaAmount)).clamp()}toString(useAlpha=!0){var colorHex;return this.isValid()?"#"+(colorHex=c=>((c=round(255*clamp(c)))<16?"0":"")+c.toString(16))(this.r)+colorHex(this.g)+colorHex(this.b)+(useAlpha?colorHex(this.a):""):"#000"}setHex(hex){if(hex.length<6){var fromHex=c=>clamp(parseInt(hex[c],16)/15);this.r=fromHex(1),this.g=fromHex(2),this.b=fromHex(3),this.a=5===hex.length?fromHex(4):1}else{const fromHex=c=>clamp(parseInt(hex.slice(c,c+2),16)/255);this.r=fromHex(1),this.g=fromHex(3),this.b=fromHex(5),this.a=9===hex.length?fromHex(7):1}return this}rgbaInt(){return(255*clamp(this.r)+.5|0)+(255*clamp(this.g)+.5<<8)+(255*clamp(this.b)+.5<<16)+(255*clamp(this.a)+.5<<24)}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));var GRAY=debugProtectConstant(rgb(.5,.5,.5));const RED=debugProtectConstant(rgb(1,0,0));var ORANGE=debugProtectConstant(rgb(1,.5,0));const YELLOW=debugProtectConstant(rgb(1,1,0)),GREEN=debugProtectConstant(rgb(0,1,0));var CYAN=debugProtectConstant(rgb(0,1,1));const BLUE=debugProtectConstant(rgb(0,0,1)),PURPLE=debugProtectConstant(rgb(.5,0,1));var MAGENTA=debugProtectConstant(rgb(1,0,1));class Timer{constructor(timeLeft,useRealTime=!1){this.useRealTime=useRealTime;useRealTime=this.getGlobalTime();this.time=void 0===timeLeft?void 0:useRealTime+timeLeft,this.setTime=timeLeft}set(timeLeft=0){var globalTime=this.getGlobalTime();this.time=globalTime+timeLeft,this.setTime=timeLeft}setUseRealTime(useRealTime=!0){this.useRealTime=useRealTime}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()?0<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 "+(this.get()<0?"before":"after"):"unset"}valueOf(){return this.get()}}function formatTime(t){return(t<=-1?"-":"")+((t=0|abs(t))/60|0)+":"+(t%60<10?"0":"")+t%60}async function fetchJSON(url){var response=await loadFetch(url,"fetchJSON");if(!response.ok)throw new Error(`fetchJSON: could not load ${url}, HTTP ${response.status} `+response.statusText);response=await response.text();let json;try{json=JSON.parse(response)}catch(e){throw new Error(`fetchJSON: ${url} is not JSON`+(/^\s*</.test(response)?", it is a web page, so the path may be wrong":": "+e.message))}return json,json}async function loadFetch(url,who){try{return await fetch(url)}catch(error){var fromDisk="file:"===globalThis.location?.protocol,who=new Error(who+`: could not load ${url}, `+error.message+(fromDisk?", the page was opened from a file, serve it from a local web server":""));throw who.cause=error,who}}function saveText(text,filename="text",type="text/plain"){saveDataURL(URL.createObjectURL(new Blob([text],{type:type})),filename,1e3)}function createCanvasContext(width,height=width,willReadFrequently=!1){return("undefined"!=typeof OffscreenCanvas?new OffscreenCanvas(width,height):Object.assign(document.createElement("canvas"),{width:width,height:height})).getContext("2d",{willReadFrequently:willReadFrequently})}function canvasAvailable(){return"undefined"!=typeof OffscreenCanvas||"undefined"!=typeof document&&document.createElement}function saveCanvas(canvas,filename="screenshot",type="image/png"){var saveCanvas;"undefined"!=typeof OffscreenCanvas&&canvas instanceof OffscreenCanvas?((saveCanvas=document.createElement("canvas")).width=canvas.width,saveCanvas.height=canvas.height,saveCanvas.getContext("2d").drawImage(canvas,0,0),saveDataURL(saveCanvas.toDataURL(type),filename)):saveDataURL(canvas.toDataURL(type),filename)}function saveDataURL(url,filename="download",revokeTime){var link=document.createElement("a");link.download=filename,link.href=url,link.click(),void 0!==revokeTime&&setTimeout(()=>URL.revokeObjectURL(url),revokeTime)}function shareURL(title,url,callback){navigator.share?.({title:title,url:url}).then(()=>callback?.(),()=>{})}function readSaveData(saveName,defaultSaveData){let loadedData={};try{var data=localStorage.getItem(saveName);if(data){try{loadedData=JSON.parse(data)}catch{console.warn("readSaveData: the save "+saveName+" can not be read, using the defaults")}loadedData&&"object"==typeof loadedData&&!isArray(loadedData)||(loadedData={})}}catch{}return{...defaultSaveData,...loadedData}}function writeSaveData(saveName,saveData){let text;try{text=JSON.stringify(saveData)}catch(error){return console.warn("writeSaveData: the save "+saveName+" can not be written as JSON, "+error.message),!1}if(void 0===text)return console.warn("writeSaveData: the save "+saveName+" is nothing as JSON, it is not written"),!1;try{return localStorage.setItem(saveName,text),!0}catch{return console.warn("writeSaveData: the save "+saveName+" could not be written, storage is full or off"),!1}}function noiseHash(i){i=2654435769^(0|i),i=Math.imul(i^i>>>16,2246822507);return i=Math.imul(i^i>>>13,3266489909),((i^=i>>>16)>>>0)/2**32}function noise1D(x){var i=floor(x);return lerp(noiseHash(i),noiseHash(i+1),smoothStep(x-i))}function noise2D(x,y){var ix=floor(x),iy=floor(y),x=smoothStep(x-ix),y=smoothStep(y-iy),h=(a,b)=>noiseHash(a+Math.imul(b,374761393));return lerp(lerp(h(ix,iy),h(ix+1,iy),x),lerp(h(ix,iy+1),h(ix+1,iy+1),x),y)}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,loadingScreen=function(progress){var x=mainContext,w=mainCanvasSize.x,h=mainCanvasSize.y,barWidth=min(.6*w,400),left=(w-barWidth)/2,top=h/2;x.save(),x.fillStyle="#000",x.fillRect(0,0,w,h),x.fillStyle=x.strokeStyle="#fff",x.font="28px "+fontDefault,x.textAlign="center",x.textBaseline="bottom",x.fillText("Loading",w/2,top-16),x.lineWidth=2,x.strokeRect(left,top,barWidth,12),x.fillRect(left,top,barWidth*clamp(progress),12),x.restore()},headlessMode=!1,engineManualStep=!1,engineVariableStep=!1,glEnable=!0,drawScreenSpace=!1,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,touchPinchWheel=!0,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,soundIgnoreSilentSwitch=!1,soundVolume=.3,soundDefaultRange=40,soundDefaultTaper=.7,soundPauseWhenHidden=!0;function setCameraPos(pos){cameraPos=pos.copy()}function setCameraAngle(angle){cameraAngle=angle}function setCameraScale(scale){cameraScale=scale}function setTimeScale(scale){timeScale=max(0,scale)}function setCanvasColorTiles(colorTiles){canvasColorTiles=colorTiles}function setCanvasClearColor(color){canvasClearColor=color.copy()}function setCanvasMaxSize(size){canvasMaxSize=size.copy()}function setCanvasMinAspect(aspect){canvasMinAspect=aspect}function setCanvasMaxAspect(aspect){canvasMaxAspect=aspect}function setCanvasFixedSize(size){canvasFixedSize=size.copy()}function setCanvasPixelated(pixelated){canvasPixelated=pixelated,mainCanvas&&(mainCanvas.style.imageRendering=pixelated?"pixelated":""),glCanvas&&(glCanvas.style.imageRendering=pixelated?"pixelated":"")}function setTilesPixelated(pixelated){tilesPixelated=pixelated}function setCanvasPixelRatio(pixelRatio){canvasPixelRatio=pixelRatio}function getCanvasPixelRatio(){return canvasPixelRatio??(devicePixelRatio||1)}function setFontDefault(font){fontDefault=font}function setShowSplashScreen(show){showSplashScreen=show}function setLoadingScreen(callback){loadingScreen=callback}function setHeadlessMode(headless){headlessMode=headless}function setEngineManualStep(enable=!0){var resume=engineManualStep&&!enable;enable&&!engineManualStep&&(frameTimeBufferMS=-500/frameRate),engineManualStep=enable,resume&&engineUpdateInternal&&(frameTimeLastMS=performance.now(),engineScheduleFrame())}function setEngineVariableStep(enable=!0){engineVariableStep&&!enable&&(timeFixedStart=frame?time+1/frameRate:time,frameFixedStart=frame,timeDelta=1/frameRate,frameTimeBufferMS=engineManualStep?-500/frameRate:0),engineVariableStep=enable}function setDrawScreenSpace(screenSpace){drawScreenSpace=screenSpace}function setGLEnable(enable){enable&&!glCanBeEnabled?console.warn("Can not enable WebGL if it was disabled on start."):(glEnable=enable,glCanvas&&(glCanvas.style.display=enable?"":"none"))}function setGLCircleSides(sides){glCircleSides=sides}function setTileDefaultSize(size){tileDefaultSize=size.copy()}function setTileDefaultPadding(padding){tileDefaultPadding=padding}function setTileDefaultBleed(bleed){tileDefaultBleed=bleed}function setEnablePhysicsSolver(enable){enablePhysicsSolver=enable}function setObjectDefaultMass(mass){objectDefaultMass=mass}function setObjectDefaultDamping(damp){objectDefaultDamping=damp}function setObjectDefaultAngleDamping(damp){objectDefaultAngleDamping=damp}function setObjectDefaultRestitution(restitution){objectDefaultRestitution=restitution}function setObjectDefaultFriction(friction){objectDefaultFriction=friction}function setObjectMaxSpeed(speed){objectMaxSpeed=speed}function setGravity(newGravity){gravity=newGravity.copy()}function setParticleEmitRateScale(scale){particleEmitRateScale=scale}function setGamepadsEnable(enable){gamepadsEnable=enable}function setGamepadDirectionEmulateStick(enable){gamepadDirectionEmulateStick=enable}function setGamepadAxisFilterEnable(enable){gamepadAxisFilterEnable=enable}function setInputWASDEmulateDirection(enable){inputWASDEmulateDirection=enable}function setTouchInputEnable(enable){touchInputEnable=enable}function setTouchGamepadEnable(enable){touchGamepadEnable=enable}function setTouchPinchWheel(enable){touchPinchWheel=enable}function setTouchGamepadPassthrough(passthrough){touchGamepadPassthrough=passthrough}function setTouchGamepadCenterButtonSize(size){touchGamepadCenterButtonSize=size}function setTouchGamepadButtonCount(count){0<(touchGamepadButtonCount=count)&&(touchGamepadRightStick=!1)}function setTouchGamepadLeftStick(enable){(touchGamepadLeftStick=enable)&&(touchGamepadLeftButtonCount=0)}function setTouchGamepadLeftButtonCount(count){0<(touchGamepadLeftButtonCount=count)&&(touchGamepadLeftStick=!1)}function setTouchGamepadRightStick(rightStick){(touchGamepadRightStick=rightStick)&&(touchGamepadButtonCount=0)}function setTouchGamepadAnalog(analog){touchGamepadAnalog=analog}function setTouchGamepadFloating(floating){touchGamepadFloating=floating}function setTouchGamepadSize(size){touchGamepadSize=size}function setTouchGamepadAlpha(alpha){touchGamepadAlpha=alpha}function setTouchGamepadDisplayTime(time){touchGamepadDisplayTime=time}function setTouchGamepadVibration(ms){touchGamepadVibration=ms}function setVibrateEnable(enable){vibrateEnable=enable}function setSoundEnable(enable){soundEnable=enable}function setSoundIgnoreSilentSwitch(ignore){soundIgnoreSilentSwitch=ignore,audioSetSession()}function setSoundVolume(volume){soundVolume=volume,audioUpdateVolume()}function setSoundDefaultRange(range){soundDefaultRange=range}function setSoundDefaultTaper(taper){soundDefaultTaper=taper}function setSoundPauseWhenHidden(pause){soundPauseWhenHidden=pause}function setDebugWatermark(show){debugWatermark=show}function setDebugKey(key){debugKey=key}function setDebugKeysAlways(enable=!0){debugKeysAlways=enable}function setDebugTweakables(show=!0){debugTweakables=show}class EngineObject{constructor(pos=vec2(),size=vec2(1),tileInfo,angle=0,color=WHITE,renderOrder=0){this.pos=pos.copy(),this.size=size.copy(),this.drawSize=void 0,this.tileInfo=tileInfo,this.angle=angle,this.color=color.copy(),this.additiveColor=void 0,this.shader=void 0,this.castShadow=!0,this.castBackgroundShadow=!1,this.emissive=0,this.mirror=!1,this.destroyed=!1,this.updatePass=0,this.mass=objectDefaultMass,this.damping=objectDefaultDamping,this.angleDamping=objectDefaultAngleDamping,this.restitution=objectDefaultRestitution,this.friction=objectDefaultFriction,this.gravityScale=1,this.renderOrder=renderOrder,this.velocity=vec2(),this.angleVelocity=0,this.spawnTime=time,this.children=[],this.clampSpeed=!0,this.groundObject=void 0,this.parent=void 0,this.localPos=void 0,this.localAngle=0,this.collideLevel=!1,this.collideSolidObjects=!1,this.isSolid=!1,this.collideRaycast=!1,this.persistent=!1,engineObjects.push(this)}updateTransforms(updateChildren=!0){var mirror,pp,lx,lp,c,s,parent=this.parent;if(parent&&(mirror=parent.getMirrorSign(),lp=this.localPos,pp=parent.pos,lx=lp.x*mirror,lp=lp.y,(parent=parent.angle)?(c=cos(-parent),s=sin(-parent),this.pos.set(lx*c-lp*s+pp.x,lx*s+lp*c+pp.y)):this.pos.set(lx+pp.x,lp+pp.y),this.angle=mirror*this.localAngle+parent),updateChildren)for(const child of this.children)child.updateTransforms()}updatePhysics(){if(!this.destroyed){var solve=enablePhysicsSolver&&this.mass,oldPos=solve&&(this.collideSolidObjects||this.collideLevel)?this.pos.copy():void 0;if(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.clampSpeed&&enablePhysicsSolver&&(this.collideSolidObjects||this.collideLevel&&this.mass)&&(this.velocity.x=clamp(this.velocity.x,-objectMaxSpeed,objectMaxSpeed),this.velocity.y=clamp(this.velocity.y,-objectMaxSpeed,objectMaxSpeed)),this.pos.x+=this.velocity.x,this.pos.y+=this.velocity.y,this.angle+=this.angleVelocity*=this.angleDamping,solve){var gravityY=this.gravityScale<0?-gravity.y:gravity.y,wasFalling=this.velocity.y<0&&gravityY<0||0<this.velocity.y&&0<gravityY;if(this.groundObject&&(solve=max(this.friction,this.groundObject.friction),groundSpeed=this.groundObject.velocity.x,this.velocity.x=groundSpeed+(this.velocity.x-groundSpeed)*solve,this.groundObject=void 0),this.collideSolidObjects&&this.size.x&&this.size.y)for(const o of engineCollideGrid?engineCollideGridWalk(this):engineObjectsCollideStaticLast)if(!o.destroyed&&!o.parent&&o!==this&&o.size.x&&o.size.y&&(this.isSolid||o.isSolid)&&this.isOverlappingObject(o)){var answer=engineObjectsCollidePairAnswer(o,this);if(!1!==answer){if(!answer){var answer=this.collideWithObject(o),collide2=o.collideWithObject(this);if(engineCollideGrid?.built&&engineCollideGridPlace(engineCollideGrid.built,o),!answer||!collide2){engineObjectsCollidePairAdd(this,o);continue}}answer=isOverlapping(oldPos,this.size,o.pos,o.size);if(!answer||o.mass&&!o.groundObject)if(answer){collide2=oldPos.subtract(o.pos),answer=collide2.length(),answer=answer<.001?vec2(0,.001):collide2.scale(.001/answer);this.velocity=this.velocity.add(answer),o.mass&&(o.velocity=o.velocity.subtract(answer)),engineObjectsCollidePairAdd(this,o)}else{var elastic1,answer=this.size.add(o.size),smallStepUp=2*(0<gravityY?o.pos.y-oldPos.y:oldPos.y-o.pos.y)>answer.y-abs(gravityY),isBlockedX=2*abs(oldPos.y-o.pos.y)<answer.y,isBlockedY=2*abs(oldPos.x-o.pos.x)<answer.x,restitution=max(this.restitution,o.restitution);if(!smallStepUp&&!isBlockedY&&isBlockedX||(this.pos.y=o.pos.y+(answer.y/2+.001)*sign(oldPos.y-o.pos.y),o.groundObject&&wasFalling||!o.mass?(wasFalling&&(this.groundObject=o),this.velocity.y=o.velocity.y-(this.velocity.y-o.velocity.y)*restitution):(isBlockedY=(this.mass*this.velocity.y+o.mass*o.velocity.y)/(this.mass+o.mass),elastic0=this.velocity.y*(this.mass-o.mass)/(this.mass+o.mass)+2*o.velocity.y*o.mass/(this.mass+o.mass),elastic1=o.velocity.y*(o.mass-this.mass)/(this.mass+o.mass)+2*this.velocity.y*this.mass/(this.mass+o.mass),this.velocity.y=lerp(isBlockedY,elastic0,restitution),o.velocity.y=lerp(isBlockedY,elastic1,restitution))),!smallStepUp&&isBlockedX)if(this.pos.x=o.pos.x+(answer.x/2+.001)*sign(oldPos.x-o.pos.x),o.mass){const inelastic=(this.mass*this.velocity.x+o.mass*o.velocity.x)/(this.mass+o.mass),elastic0=this.velocity.x*(this.mass-o.mass)/(this.mass+o.mass)+2*o.velocity.x*o.mass/(this.mass+o.mass),elastic1=o.velocity.x*(o.mass-this.mass)/(this.mass+o.mass)+2*this.velocity.x*this.mass/(this.mass+o.mass);this.velocity.x=lerp(inelastic,elastic0,restitution),o.velocity.x=lerp(inelastic,elastic1,restitution)}else this.velocity.x=o.velocity.x-(this.velocity.x-o.velocity.x)*restitution;engineObjectsCollidePairAdd(this,o,!0)}else{var elastic0=collideBoxBox(this.pos,this.size,o.pos,o.size);if(elastic0){this.pos.x+=elastic0.x+.001*sign(elastic0.x),this.pos.y+=elastic0.y+.001*sign(elastic0.y);const restitution=max(this.restitution,o.restitution);if(elastic0.x){isBlockedY=this.velocity.x-o.velocity.x;isBlockedY*elastic0.x<0&&(this.velocity.x=o.velocity.x-isBlockedY*restitution)}else{const v=this.velocity.y-o.velocity.y;v*elastic0.y<0&&(this.velocity.y=o.velocity.y-v*restitution),elastic0.y*gravityY<0&&(this.groundObject=o)}}engineObjectsCollidePairAdd(this,o)}}}if(this.collideLevel){var groundSpeed=tileCollisionTest(this.pos,this.size,this);if(groundSpeed&&!tileCollisionTest(oldPos,this.size,this)){const isBlockedX=tileCollisionTest(vec2(this.pos.x,oldPos.y),this.size,this),isBlockedY=tileCollisionTest(vec2(oldPos.x,this.pos.y),this.size,this),restitution=max(this.restitution,groundSpeed.restitution);if(isBlockedX){const epsilon=.001;solve=0<gravityY?ceil(oldPos.y+this.size.y/2-1)-this.size.y/2-.001:floor(oldPos.y-this.size.y/2+1)+this.size.y/2+.001;if(abs(solve-this.pos.y)<.1&&!tileCollisionTest(vec2(this.pos.x,solve),this.size,this))return void(this.pos.y=solve);var solve=groundSpeed.pos.x,offsetX=this.size.x/2+1e-4,movingLeft=this.pos.x<oldPos.x,solve=solve+(movingLeft?floor(oldPos.x-solve-this.size.x/2)+offsetX:ceil(oldPos.x-solve+this.size.x/2)-offsetX),offsetX=(movingLeft?min:max)(solve,oldPos.x);this.pos.x=tileCollisionTest(vec2(offsetX,oldPos.y),this.size,this)?oldPos.x:offsetX,this.velocity.x*=-restitution}if(isBlockedY||!isBlockedX){const epsilon=1e-4;movingLeft=this.size.y/2+1e-4,solve=groundSpeed.pos.y;const y=solve+(this.pos.y<oldPos.y?floor(oldPos.y-solve-this.size.y/2)+movingLeft:ceil(oldPos.y-solve+this.size.y/2)-movingLeft);offsetX=this.pos.y!==oldPos.y&&!tileCollisionTest(vec2(this.pos.x,y),this.size,this);this.pos.y=offsetX?y:oldPos.y,this.groundObject=wasFalling?groundSpeed:void 0,this.velocity.y*=-restitution}}}}}}update(){}render(){drawTile(this.pos,this.drawSize||this.size,this.tileInfo,this.color,this.angle,this.mirror,this.additiveColor,glEnable,!1)}renderLight(){}renderShadow(){this.render()}renderEmissive(){this.render()}destroy(immediate=!1){if(!this.destroyed){this.destroyed=!0,this.parent?.removeChild(this);for(const child of this.children)child.parent=void 0,child.destroy(immediate)}}localToWorld(pos){return this.pos.add(pos.rotate(this.angle))}worldToLocal(pos){return pos.subtract(this.pos).rotate(-this.angle)}localToWorldVector(vec){return vec.rotate(this.angle)}worldToLocalVector(vec){return vec.rotate(-this.angle)}collideWithTile(tileData,pos){return 0<tileData}collideWithObject(object,push){return!0}getUp(scale=1){return vec2().setAngle(this.angle,scale)}getRight(scale=1){return vec2().setAngle(this.angle+PI/2,scale)}getAliveTime(){return time-this.spawnTime}getSpeed(){return this.velocity.length()}applyAcceleration(acceleration){this.mass&&(this.velocity=this.velocity.add(acceleration))}applyAngularAcceleration(acceleration){this.mass&&(this.angleVelocity+=acceleration)}applyForce(force){this.mass&&this.applyAcceleration(force.scale(1/this.mass))}getMirrorSign(){return this.mirror?-1:1}addChild(child,localPos=vec2(),localAngle=0){if(!this.destroyed){for(let p=this;p;p=p.parent);this.children.push(child),child.parent=this,child.localPos=localPos.copy(),child.localAngle=localAngle,child.updateTransforms()}return child}attach(child){child.parent?.removeChild(child);var mirror=this.getMirrorSign(),local=this.worldToLocal(child.pos);return local.x*=mirror,this.addChild(child,local,mirror*(child.angle-this.angle))}removeChild(child){var i=this.children.indexOf(child);i<0||(this.children.splice(i,1),child.parent=child.localPos=void 0)}isOverlappingObject(object){return this.isOverlapping(object.pos,object.size)}isOverlapping(pos,size=vec2()){return isOverlapping(this.pos,this.size,pos,size)}setCollision(collideSolidObjects=!0,isSolid=!0,collideLevel=!0,collideRaycast=!0){this.collideSolidObjects=collideSolidObjects,this.isSolid=isSolid,this.collideLevel=collideLevel,this.collideRaycast=collideRaycast}toString(){let text="type = "+this.constructor.name;return(this.pos.x||this.pos.y)&&(text+="\npos = "+this.pos),(this.velocity.x||this.velocity.y)&&(text+="\nvelocity = "+this.velocity),(this.size.x||this.size.y)&&(text+="\nsize = "+this.size),this.angle&&(text+="\nangle = "+this.angle.toFixed(3)),text+="\ncolor = "+this.color}renderDebugInfo(){var hasPhysics,size;debug&&((hasPhysics=this.collideLevel||this.collideSolidObjects||this.isSolid)||this.parent)&&(size=vec2(max(this.size.x,.2),max(this.size.y,.2)),rgb(this.collideLevel?1:0,this.collideSolidObjects?1:0,this.isSolid?1:0,.5),this.pos,hasPhysics||this.angle,this.parent&&(this.pos,size.scale(.8),rgb(1,1,1,.5),this.angle),this.parent)&&(this.pos,this.parent.pos,rgb(1,1,1,.5))}}let mainCanvas,mainContext,drawContext,workCanvas,workContext,workReadCanvas,workReadContext,backgroundCanvas,mainCanvasSize=vec2(),textureInfos=[],drawCount=0,primitiveCount=0;function isWhite(c){return 1<=c.r&&1<=c.g&&1<=c.b}function isBlack(c){return c.r<=0&&c.g<=0&&c.b<=0&&c.a<=0}function tile(index=0,size=tileDefaultSize,texture=0,padding=tileDefaultPadding,bleed=tileDefaultBleed){"number"==typeof size&&(size=new Vector2(size,size));texture="number"==typeof texture?textureInfos[texture]:texture;if(!texture?.size.x)return new TileInfo(new Vector2,headlessMode?size.copy():new Vector2,texture,padding,bleed);var sizePaddedX=size.x+2*padding,sizePaddedY=size.y+2*padding;let x,y;y="number"==typeof index?(cols=texture.size.x/sizePaddedX|0,x=index%cols,index/cols|0):(x=index.x,index.y);var cols=new Vector2(x*sizePaddedX+padding,y*sizePaddedY+padding);return new TileInfo(cols,size,texture,padding,bleed)}class TileInfo{constructor(pos=vec2(),size=tileDefaultSize,textureInfo=textureInfos[0],padding=tileDefaultPadding,bleed=tileDefaultBleed,columns=0){this.pos=pos.copy(),this.size=size.copy(),this.padding=padding,this.textureInfo=textureInfo,this.tiles=void 0,this.bleed=bleed,this.columns=columns}offset(offset){return new TileInfo(this.pos.add(offset),this.size,this.textureInfo,this.padding,this.bleed,this.columns)}frame(frame){var w=this.size.x+2*this.padding,h=this.size.y+2*this.padding,w=(this.columns?frame%this.columns:frame)*w,frame=(this.columns?frame/this.columns|0:0)*h;return this.offset(new Vector2(w,frame))}setColumns(columns=0){return this.columns=columns,this}index(index){return tile(index,this.size,this.textureInfo,this.padding,this.bleed).setColumns(this.columns)}setFullImage(textureInfo=this.textureInfo){return this.textureInfo=textureInfo,this.pos=new Vector2,this.size=textureInfo.size.copy(),this.bleed=this.padding=this.columns=0,this}}class TextureInfo{constructor(image,useWebGL=!0,wrap=!1,pixelated){this.image=image,this.size=image?vec2(image.width,image.height):vec2(),this.sizeInverse=image?vec2(1/image.width,1/image.height):vec2(),this.glTexture=void 0,this.wrap=wrap,this.pixelated=pixelated,useWebGL&&this.createWebGLTexture()}createWebGLTexture(){var image=this.image;image?.width&&(this.size=vec2(image.width,image.height),this.sizeInverse=vec2(1/image.width,1/image.height)),image=this,headlessMode||(glTextureInfos.add(image),glContext&&(image.glTexture?glSetTextureData(image.glTexture,image.image,image.pixelated):image.glTexture=glCreateTexture(image.image,image.wrap,image.pixelated)))}destroyWebGLTexture(){var textureInfo,glTexture;textureInfo=this,headlessMode||(glTextureInfos.delete(textureInfo),glTexture=textureInfo.glTexture,textureInfo.glTexture=void 0,glDeleteTexture(glTexture))}hasWebGL(){return!!this.glTexture}setWrap(wrap=!0){this.wrap=wrap,glSetTextureWrap(this.glTexture,wrap)}setPixelated(pixelated){this.pixelated=pixelated,this.glTexture&&(this.destroyWebGLTexture(),this.createWebGLTexture())}}class SpriteAnimation{constructor(tileInfo,frameCount,frameTime=.1){this.firstTile=tileInfo,this.frameCount=frameCount,this.frameTime=frameTime,this.speed=1,this.mode="loop",this.startTime=time,this.heldFrame=void 0,this.onEnd=void 0}loop(){return this.restart("loop")}play(onEnd){return this.restart("once"),this.onEnd=onEnd,this}pingPong(){return this.restart("pingPong")}stop(){return this.heldFrame=this.frame,this}restart(mode=this.mode){return this.mode=mode,this.startTime=time,this.heldFrame=void 0,this.onEnd=void 0,this}get elapsedFrames(){return(time-this.startTime)*this.speed/this.frameTime}get frame(){var n,f,k;return this.isDone,void 0!==this.heldFrame?this.heldFrame:(n=this.frameCount,f=floor(this.elapsedFrames),"once"==this.mode?clamp(this.speed<0?n-1+ceil(this.elapsedFrames):f,0,n-1):"loop"==this.mode?mod(f,n):(k=mod(f,f=max(2*n-2,1)))<n?k:f-k)}get tileInfo(){return this.firstTile.frame(this.frame)}get isDone(){var onEnd,done="once"==this.mode&&void 0===this.heldFrame&&abs(this.elapsedFrames)>=this.frameCount;return done&&this.onEnd&&(onEnd=this.onEnd,this.onEnd=void 0,onEnd()),done}}class SpriteAnimator{constructor(clips){var names=Object.keys(clips);this.clips=clips,this.name=names[0]}set(name,onEnd){return name==this.name&&!this.clip.isDone||(this.name=name,this.clip.restart(),this.clip.onEnd=onEnd),this}get clip(){return this.clips[this.name]}get settledClip(){for(let i=4,clip;i--&&clip!==this.clip;)(clip=this.clip).isDone;return this.clip}get tileInfo(){return this.settledClip.tileInfo}get frame(){return this.settledClip.frame}get isDone(){return this.settledClip.isDone}}class Shader{constructor(fragmentCode){this.fragmentCode=String(fragmentCode),this.program=void 0,this.program3D=void 0,glShaderObjects.push(this)}dispose(){var shader=this,i=glShaderObjects.indexOf(shader);if(i<0||glShaderObjects.splice(i,1),glContext&&!headlessMode){glBatchShader===shader&&glFlush();for(const program of[shader.program,shader.program3D])program&&glContext.deleteProgram(program)}shader.program=shader.program3D=void 0}}function drawTile(pos,size=vec2(1),tileInfo,color=WHITE,angle=0,mirror,additiveColor,useWebGL=glEnable,screenSpace=drawScreenSpace,context){if(!headlessMode||context){const textureInfo=tileInfo?.textureInfo;if(!textureInfo||tileInfo.size.x&&tileInfo.size.y&&textureInfo.size.x){const bleed=tileInfo?.bleed??0;if(useWebGL&&glEnable){if(screenSpace){if(glSkipScreenSpace)return;[pos,size,angle]=screenToWorldTransform(pos,size,angle)}var x,y,w,h,bleedX;textureInfo?(useWebGL=textureInfo.sizeInverse,x=tileInfo.pos.x*useWebGL.x,y=tileInfo.pos.y*useWebGL.y,w=tileInfo.size.x*useWebGL.x,h=tileInfo.size.y*useWebGL.y,glSetTexture(textureInfo.glTexture),bleed?(bleedX=useWebGL.x*bleed,useWebGL=useWebGL.y*bleed,glDraw(pos.x,pos.y,mirror?-size.x:size.x,size.y,angle,x+bleedX,y+useWebGL,x-bleedX+w,y-useWebGL+h,color.rgbaInt(),additiveColor&&additiveColor.rgbaInt())):glDraw(pos.x,pos.y,mirror?-size.x:size.x,size.y,angle,x,y,x+w,y+h,color.rgbaInt(),additiveColor&&additiveColor.rgbaInt())):(bleedX=additiveColor?color.add(additiveColor):color,glDrawUntextured(pos.x,pos.y,size.x,size.y,angle,bleedX.rgbaInt()))}else++drawCount,++primitiveCount,drawCanvas2D(pos,size,angle,mirror,context=>{var y,w,h,x;textureInfo?(context.scale(1,-1),void 0!==textureInfo.pixelated&&(context.imageSmoothingEnabled=!textureInfo.pixelated),x=tileInfo.pos.x,y=tileInfo.pos.y,w=tileInfo.size.x,h=tileInfo.size.y,function(context,image,sx,sy,sWidth,sHeight,dx,dy,dWidth,dHeight,color,additiveColor,bleed){var sx2=bleed,sy2=bleed,sWidth2=(sWidth=max(1,0|sWidth),sHeight=max(1,0|sHeight),sWidth-2*bleed),bleed=sHeight-2*bleed;!canvasColorTiles||isWhite(color)&&(!additiveColor||isBlack(additiveColor))?(context.globalAlpha=color.a,context.drawImage(image,sx+sx2,sy+sy2,sWidth2,bleed,dx,dy,dWidth,dHeight),context.globalAlpha=1):(workReadCanvas.width!==sWidth||workReadCanvas.height!==sHeight?(workReadCanvas.width=sWidth,workReadCanvas.height=sHeight):workReadContext.clearRect(0,0,sWidth,sHeight),workReadContext.drawImage(image,0|sx,0|sy,sWidth,sHeight,0,0,sWidth,sHeight),image=workReadContext.getImageData(0,0,sWidth,sHeight),sx=tintImageData(image.data,color,additiveColor),workReadContext.putImageData(image,0,0),sx||(context.globalAlpha=color.a),context.drawImage(workReadCanvas,sx2,sy2,sWidth2,bleed,dx,dy,dWidth,dHeight),sx||(context.globalAlpha=1))}(context,textureInfo.image,x,y,w,h,-.5,-.5,1,1,color,additiveColor,bleed)):(x=additiveColor?color.add(additiveColor):color,context.fillStyle=x.toString(),context.fillRect(-.5,-.5,1,1))},screenSpace,context)}}}function drawRect(pos,size,color,angle,useWebGL,screenSpace,context){drawTile(pos,size,void 0,color,angle,!1,void 0,useWebGL,screenSpace,context)}function drawRectGradient(pos,size=vec2(1),colorTop=WHITE,colorBottom=CLEAR_WHITE,angle=0,useWebGL=glEnable,screenSpace=drawScreenSpace,context){if(!headlessMode||context)if(useWebGL&&glEnable){if(screenSpace){if(glSkipScreenSpace)return;[pos,size,angle]=screenToWorldTransform(pos,size,angle)}var points=[],colors=[],halfSizeX=size.x/2,halfSizeY=size.y/2,colorTopInt=colorTop.rgbaInt(),colorBottomInt=colorBottom.rgbaInt(),c=cos(-angle),s=sin(-angle);for(let i=4;i--;){var x=1&i?halfSizeX:-halfSizeX,y=2&i?halfSizeY:-halfSizeY,color=2&i?colorTopInt:colorBottomInt;points.push(vec2(pos.x+(x*c-y*s),pos.y+(x*s+y*c))),colors.push(color)}glDrawColoredPoints(points,colors)}else++drawCount,++primitiveCount,drawCanvas2D(pos,size,angle,!1,context=>{var gradient=context.createLinearGradient(0,.5,0,-.5);gradient.addColorStop(0,colorTop.toString()),gradient.addColorStop(1,colorBottom.toString()),context.fillStyle=gradient,context.fillRect(-.5,-.5,1,1)},screenSpace,context)}function drawTextureWrapped(pos,size,wrapCount,texture=0,color=WHITE,angle=0,additiveColor,useWebGL=glEnable,screenSpace=drawScreenSpace,context){if(!headlessMode||context){texture="number"==typeof texture?textureInfos[texture]:texture;if(useWebGL&&glEnable){if(screenSpace){if(glSkipScreenSpace)return;[pos,size,angle]=screenToWorldTransform(pos,size,angle)}glSetTexture(texture.glTexture),glDraw(pos.x,pos.y,size.x,size.y,angle,0,0,wrapCount.x,wrapCount.y,color.rgbaInt(),additiveColor&&additiveColor.rgbaInt())}else{++drawCount,++primitiveCount,screenSpace||(pos=worldToScreen(pos),size=size.scale(cameraScale),angle-=cameraAngle);useWebGL=!canvasColorTiles||isWhite(color)&&(!additiveColor||isBlack(additiveColor)),screenSpace=!useWebGL&&additiveColor&&!isBlack(additiveColor);let source=texture.image;if(!useWebGL){var kept,useWebGL=color.r+","+color.g+","+color.b+","+color.a+(additiveColor?","+additiveColor.r+","+additiveColor.g+","+additiveColor.b+","+additiveColor.a:"");let baked=drawTextureWrappedBakes.get(source);(baked?.key!==useWebGL||"getContext"in source)&&(kept=(kept=baked?.context)?.canvas.width===(0|source.width)&&kept?.canvas.height===(0|source.height)?kept:createCanvasContext(0|source.width,0|source.height,!0),function(image,color,additiveColor,context=workReadContext){var w=0|image.width,h=0|image.height,image=(context.canvas.width=w,context.canvas.height=h,context.drawImage(image,0,0),context.getImageData(0,0,w,h));tintImageData(image.data,color,additiveColor),context.putImageData(image,0,0),context.canvas}(source,color,additiveColor,kept),drawTextureWrappedBakes.set(source,baked={key:useWebGL,context:kept})),source=baked.context.canvas}(context=context||drawContext).save(),void 0!==texture.pixelated&&(context.imageSmoothingEnabled=!texture.pixelated),context.translate(pos.x+.5,pos.y+.5),context.rotate(angle),context.globalAlpha=screenSpace?1:color.a;additiveColor=context.createPattern(source,"repeat"),useWebGL=(new DOMMatrix).translate(-size.x/2,-size.y/2).scale(size.x/(wrapCount.x*source.width),size.y/(wrapCount.y*source.height));additiveColor.setTransform(useWebGL),context.fillStyle=additiveColor,context.fillRect(-size.x/2,-size.y/2,size.x,size.y),context.globalAlpha=1,context.restore()}}}function drawLineList(points,width=.1,color=WHITE,wrap=!1,pos=vec2(),angle=0,useWebGL=glEnable,screenSpace=drawScreenSpace,context){if(!headlessMode||context)if(useWebGL&&glEnable){let sx=1,sy=1;if(screenSpace){if(glSkipScreenSpace)return;[pos,useWebGL,angle]=screenToWorldTransform(pos,vec2(1),angle),sx=useWebGL.x,sy=useWebGL.y}glDrawOutlineTransform(points,color.rgbaInt(),width,pos.x,pos.y,sx,sy,angle,wrap)}else++drawCount,++primitiveCount,drawCanvas2D(pos,vec2(1),angle,!1,context=>{if(width){context.strokeStyle=color.toString(),context.lineWidth=abs(width),context.beginPath();for(let i=0;i<points.length;++i){var point=points[i];context.lineTo(point.x,point.y)}wrap&&context.closePath(),context.stroke()}},screenSpace,context)}function drawLine(posA,posB,width=.1,color=WHITE,pos=vec2(),angle=0,useWebGL=glEnable,screenSpace=drawScreenSpace,context){posB=vec2((posB.x-posA.x)/2,(posB.y-posA.y)/2),width=vec2(width,2*posB.length()),posA=posA.add(posB);pos=pos.add(angle?posA.rotate(screenSpace?-angle:angle):posA),screenSpace&&(posB.y*=-1),drawRect(pos,width,color,angle+=posB.angle(),useWebGL,screenSpace,context)}function drawRegularPoly(pos,size=vec2(1),sides=3,color=WHITE,lineWidth=0,lineColor=BLACK,angle=0,useWebGL=glEnable,screenSpace=drawScreenSpace,context){var points=drawRegularPolyPoints,pool=drawRegularPolyPool,sizeX=(points.length=0,size.x/2),sizeY=size.y/2;for(let i=sides;0<i--;){var a=i/sides*PI*2,point=pool[points.length]||=vec2();point.x=sin(a)*sizeX,point.y=cos(a)*sizeY,points.push(point)}drawPoly(points,color,lineWidth,lineColor,pos,angle,useWebGL,screenSpace,context)}const drawRegularPolyPoints=[],drawRegularPolyPool=[];function drawPoly(points,color=WHITE,lineWidth=0,lineColor=BLACK,pos=vec2(),angle=0,useWebGL=glEnable,screenSpace=drawScreenSpace,context=void 0){if(!headlessMode||context)if(useWebGL&&glEnable){let sx=1,sy=1;if(screenSpace){if(glSkipScreenSpace)return;[pos,useWebGL,angle]=screenToWorldTransform(pos,vec2(1),angle),sx=useWebGL.x,sy=useWebGL.y}glDrawPointsTransform(points,color.rgbaInt(),pos.x,pos.y,sx,sy,angle),0<lineWidth&&glDrawOutlineTransform(points,lineColor.rgbaInt(),lineWidth,pos.x,pos.y,sx,sy,angle)}else++drawCount,++primitiveCount,drawCanvas2D(pos,vec2(1),angle,!1,context=>{context.fillStyle=color.toString(),context.beginPath();for(const point of points)context.lineTo(point.x,point.y);context.closePath(),context.fill(),0<lineWidth&&(context.strokeStyle=lineColor.toString(),context.lineWidth=lineWidth,context.stroke())},screenSpace,context)}const drawEllipseRings=new Map;function drawEllipse(pos,size=vec2(1),color=WHITE,angle=0,lineWidth=0,lineColor=BLACK,useWebGL=glEnable,screenSpace=drawScreenSpace,context){if(!headlessMode||context)if(lineWidth=clamp(lineWidth,0,min(abs(size.x),abs(size.y))),useWebGL&&glEnable){var sides=glCircleSides;if(0<lineWidth)drawRegularPoly(pos,size,sides,color,lineWidth,lineColor,angle,useWebGL,screenSpace,context);else{let ring=drawEllipseRings.get(sides);if(!ring){var points=[];for(let i=sides;0<i--;){var a=i/sides*PI*2;points.push(vec2(sin(a),cos(a)))}drawEllipseRings.set(sides,ring=glPolyStrip(points))}if(screenSpace){if(glSkipScreenSpace)return;[pos,size,angle]=screenToWorldTransform(pos,size,angle)}glDrawPointsTransform(ring,color.rgbaInt(),pos.x,pos.y,size.x/2,size.y/2,angle,!1)}}else++drawCount,++primitiveCount,drawCanvas2D(pos,vec2(1),angle,!1,context=>{context.fillStyle=color.toString(),context.beginPath(),context.ellipse(0,0,abs(size.x)/2,abs(size.y)/2,0,0,9),context.fill(),0<lineWidth&&(context.strokeStyle=lineColor.toString(),context.lineWidth=lineWidth,context.stroke())},screenSpace,context)}function drawCircle(pos,size=1,color=WHITE,lineWidth=0,lineColor=BLACK,useWebGL=glEnable,screenSpace=drawScreenSpace,context){drawEllipse(pos,vec2(size),color,0,lineWidth,lineColor,useWebGL,screenSpace,context)}let drawEllipseGradientOffset=0;function drawEllipseGradient(pos,size=vec2(1),colorInner=WHITE,colorOuter=CLEAR_WHITE,angle=0,useWebGL=glEnable,screenSpace=drawScreenSpace,context){if(!headlessMode||context)if(useWebGL&&glEnable){if(screenSpace){if(glSkipScreenSpace)return;[pos,size,angle]=screenToWorldTransform(pos,size,angle)}var sides=glCircleSides;const radiusX=size.x/2,radiusY=size.y/2;var innerInt=colorInner.rgbaInt(),outerInt=colorOuter.rgbaInt(),offset=drawEllipseGradientOffset++;const c=cos(-angle),s=sin(-angle);var rim=a=>{var lx=sin(a)*radiusX,a=cos(a)*radiusY;return vec2(pos.x+lx*c-a*s,pos.y+lx*s+a*c)},points=[rim(offset%sides/sides*PI*2)],colors=[outerInt];for(let i=sides;0<i--;){var a=(i+offset)%sides/sides*PI*2;points.push(pos),colors.push(innerInt),points.push(rim(a)),colors.push(outerInt)}glDrawColoredPoints(points,colors)}else++drawCount,++primitiveCount,drawCanvas2D(pos,size,angle,!1,context=>{var gradient=context.createRadialGradient(0,0,0,0,0,.5);gradient.addColorStop(0,colorInner.toString()),gradient.addColorStop(1,colorOuter.toString()),context.fillStyle=gradient,context.beginPath(),context.ellipse(0,0,.5,.5,0,0,9),context.fill()},screenSpace,context)}function drawCircleGradient(pos,size=1,colorInner=WHITE,colorOuter=CLEAR_WHITE,useWebGL=glEnable,screenSpace=drawScreenSpace,context){drawEllipseGradient(pos,vec2(size),colorInner,colorOuter,0,useWebGL,screenSpace,context)}function drawCanvas2D(pos,size,angle=0,mirror=!1,drawFunction,screenSpace=drawScreenSpace,context=drawContext){headlessMode&&!context||(screenSpace||(pos=worldToScreen(pos),size=size.scale(cameraScale),angle-=cameraAngle),context.save(),context.translate(pos.x+.5,pos.y+.5),context.rotate(angle),context.scale(mirror?-size.x:size.x,-size.y),drawFunction(context),context.restore())}function drawText(text,pos,size=1,color=WHITE,lineWidth=0,lineColor=BLACK,textAlign="center",font=fontDefault,fontStyle="",maxWidth,angle=0,context=drawContext){pos=worldToScreen(pos),size*=cameraScale,lineWidth*=cameraScale,void 0!==maxWidth&&(maxWidth*=cameraScale),drawTextScreen(text,pos,size,color,lineWidth,lineColor,textAlign,font,fontStyle,maxWidth,angle-=cameraAngle,context)}const drawFontsChecked=new Set;const textLines=text=>(text+="").includes("\r")?text.split(/\r?\n/):text.split("\n");var textIntl=globalThis.Intl;const textSegmenter=textIntl?.Segmenter&&new textIntl.Segmenter,textJoins=/[\p{M}\u200c\u200d\u0600-\u0605\u06dd\u070f\u0890\u0891\u08e2\u0d4e\u0e33\u0eb3\u1100-\u11ff\ua960-\ua97c\ud7b0-\ud7fb\uff9e\uff9f\u{10000}-\u{10ffff}]/u;function textCharacters(line){return textJoins.test(line)?textSegmenter?Array.from(textSegmenter.segment(line),s=>s.segment.codePointAt(0)):Array.from(line,c=>c.codePointAt(0)).filter(c=>8205!==c&&65039!==c&&!(768<=c&&c<880)):line}const textGraphemes=text=>textJoins.test(text)?textSegmenter?Array.from(textSegmenter.segment(text),s=>s.segment):[...text]:text.split(""),textCharacterCode=(characters,i)=>{{if((characters="string"==typeof characters?characters.charCodeAt(i):characters[i])<192||592<=characters)return characters;let base=textBaseLetters.get(characters);return void 0===base&&textBaseLetters.set(characters,base=String.fromCharCode(characters).normalize("NFD").charCodeAt(0)),base}},textBaseLetters=new Map;function drawTextScreen(text,pos,size,color=WHITE,lineWidth=0,lineColor=BLACK,textAlign="center",font=fontDefault,fontStyle="",maxWidth,angle=0,context=drawContext){if(!headlessMode||context){text=textLines(text),color=(context.save(),context.fillStyle=color.toString(),context.strokeStyle=lineColor.toString(),context.lineWidth=lineWidth,context.textAlign=textAlign,fontStyle+" "+size+"px "+font);debug&&function(context,fontText,font,fontStyle){fontStyle=fontStyle+"|"+font,drawFontsChecked.has(fontStyle)||(drawFontsChecked.add(fontStyle),context.font="1px littlejs-font-check",context.font=fontText,"1px littlejs-font-check"===context.font&&console.warn(`the canvas does not take the font ${font}, so text draws as 10px sans-serif; `+`a family name with spaces or digits goes in quotes, as "'Press Start 2P'"`))}(context,color,font,fontStyle),context.font=color,context.textBaseline="middle",context.translate(pos.x+.5,pos.y+.5),context.rotate(angle);let yOffset=-(text.length-1)*size/2;text.forEach(line=>{lineWidth&&context.strokeText(line,0,yOffset,maxWidth),context.fillText(line,0,yOffset,maxWidth),yOffset+=size}),context.restore()}}async function loadTexture(textureIndex,src){var old=textureInfos[textureIndex];old?.destroyWebGLTexture();const image=new Image;return src&&await engineAddLoad(new Promise(resolve=>{image.onload=resolve,image.onerror=()=>{console.warn("failed to load image: "+src),resolve()},image.crossOrigin="anonymous",image.src=src})),textureInfos[textureIndex]=new TextureInfo(image)}function screenToWorld(screenPos){let x=(screenPos.x-mainCanvasSize.x/2+.5)/cameraScale,y=(screenPos.y-mainCanvasSize.y/2+.5)/-cameraScale;var xr,s;return cameraAngle&&(screenPos=cos(-cameraAngle),s=sin(-cameraAngle),xr=x*screenPos-y*s,s=x*s+y*screenPos,x=xr,y=s),new Vector2(x+cameraPos.x,y+cameraPos.y)}function worldToScreen(worldPos){let x=worldPos.x-cameraPos.x,y=worldPos.y-cameraPos.y;var xr,s;return cameraAngle&&(worldPos=cos(cameraAngle),s=sin(cameraAngle),xr=x*worldPos-y*s,s=x*s+y*worldPos,x=xr,y=s),new Vector2(x*cameraScale+mainCanvasSize.x/2-.5,y*-cameraScale+mainCanvasSize.y/2-.5)}function screenToWorldDelta(screenDelta){let x=screenDelta.x/cameraScale,y=screenDelta.y/-cameraScale;var xr,s;return cameraAngle&&(screenDelta=cos(-cameraAngle),s=sin(-cameraAngle),xr=x*screenDelta-y*s,s=x*s+y*screenDelta,x=xr,y=s),new Vector2(x,y)}function worldToScreenDelta(worldDelta){let x=worldDelta.x,y=worldDelta.y;var xr,s;return cameraAngle&&(worldDelta=cos(cameraAngle),s=sin(cameraAngle),xr=x*worldDelta-y*s,s=x*s+y*worldDelta,x=xr,y=s),new Vector2(x*cameraScale,y*-cameraScale)}function screenToWorldTransform(screenPos,screenSize,screenAngle=0){return[screenToWorld(screenPos),screenSize.scale(1/cameraScale),screenAngle+cameraAngle]}function getCameraSize(){return mainCanvasSize.scale(1/cameraScale)}function cameraFit(center,size,worldMargin,screenInset){var c,s,fitW,worldMargin=padSides(worldMargin),screenInset=padSides(screenInset),worldW=size.x+worldMargin.left+worldMargin.right,size=size.y+worldMargin.top+worldMargin.bottom,viewW=mainCanvasSize.x-screenInset.left-screenInset.right,viewH=mainCanvasSize.y-screenInset.top-screenInset.bottom;return 0<worldW&&0<size&&0<viewW&&0<viewH&&(c=cos(cameraAngle),s=sin(cameraAngle),fitW=abs(c)*worldW+abs(s)*size,worldW=abs(s)*worldW+abs(c)*size,cameraScale=min(viewW/fitW,viewH/worldW),size=vec2(worldMargin.right-worldMargin.left,worldMargin.top-worldMargin.bottom).scale(.5),viewH=vec2((viewW=.5*(screenInset.right-screenInset.left)/cameraScale)*c+(fitW=.5*(screenInset.top-screenInset.bottom)/cameraScale)*s,fitW*c-viewW*s),cameraPos=center.add(size).add(viewH)),cameraScale;function padSides(p){return isVector2(p=void 0!==p&&!isNumber(p)?p:vec2(p))?{top:p.y,right:p.x,bottom:p.y,left:p.x}:{top:p.top||0,right:p.right||0,bottom:p.bottom||0,left:p.left||0}}}function isOnScreen(pos,size=0){if(!cameraScale)return!1;let x=pos.x-cameraPos.x,y=pos.y-cameraPos.y;cameraAngle&&(pos=cos(cameraAngle),s=sin(cameraAngle),xr=x*pos-y*s,s=x*s+y*pos,x=xr,y=s),x*=2*cameraScale,y*=2*-cameraScale,size instanceof Vector2&&(size=size.length()),size*=cameraScale;var s,pos=mainCanvasSize.x,xr=mainCanvasSize.y;return x+size>-pos&&x-size<pos&&y+size>-xr&&y-size<xr}function setAdditiveBlendMode(additive=!0){glAdditive=additive,drawContext&&(drawContext.globalCompositeOperation=additive?"lighter":"source-over")}function setShader(shader){glCustomShader=shader||void 0}function setBackgroundCanvas(canvas){backgroundCanvas=canvas}function combineCanvases(){var w=mainCanvas.width,h=mainCanvas.height;workCanvas.width=w,workCanvas.height=h,workContext.fillStyle="#000",workContext.fillRect(0,0,w,h),backgroundCanvas&&workContext.drawImage(backgroundCanvas,0,0,w,h),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 tintImageData(data,color,additiveColor){if(additiveColor&&!isBlack(additiveColor)){var colorMultiply=[clamp(color.r),clamp(color.g),clamp(color.b),clamp(color.a)],colorAdd=[255*additiveColor.r,255*additiveColor.g,255*additiveColor.b,255*additiveColor.a];for(let i=0;i<data.length;++i)data[i]=data[i]*colorMultiply[3&i]+colorAdd[3&i]|0;return!0}for(let i=0;i<data.length;i+=4)data[i]*=clamp(color.r),data[i+1]*=clamp(color.g),data[i+2]*=clamp(color.b);return!1}const drawTextureWrappedBakes=new WeakMap;function isFullscreen(){return!!document.fullscreenElement}function toggleFullscreen(){var rootElement=mainCanvas.parentElement;isFullscreen()?document.exitFullscreen&&document.exitFullscreen()?.catch?.(()=>{}):rootElement.requestFullscreen&&rootElement.requestFullscreen()?.catch?.(()=>{})}function setCursor(cursorStyle="auto"){mainCanvas.parentElement.style.cursor=cursorStyle}let engineImageFont;class ImageFont{constructor(tileInfo){this.tileInfo=tileInfo}drawText(text,pos,size=1,center,color,useWebGL,context){size="number"==typeof size?(size*=cameraScale,new Vector2(size,size)):size.scale(cameraScale);var skip=glSkipScreenSpace;glSkipScreenSpace=!1,this.drawTextScreen(text,worldToScreen(pos),size,center,color,useWebGL,context),glSkipScreenSpace=skip}drawTextScreen(text,pos,size,center=!0,color=WHITE,useWebGL=glEnable,context){const glyphSize="number"==typeof size?new Vector2(size,size):size,drawPos=new Vector2,tileInfo=this.tileInfo.frame(0);size=textLines(text);const centerOffsetY=center?(size.length-1)*glyphSize.y/2:0;size.forEach((line,j)=>{var characters=textCharacters(line),centerOffset=center?(characters.length-1)*glyphSize.x/2:0;for(let i=characters.length;i--;){var charCode=textCharacterCode(characters,i);this.getGlyphPos(charCode<32||127<charCode?95:charCode-32,tileInfo.pos),drawPos.x=ceil(pos.x+i*glyphSize.x-centerOffset-glyphSize.x/2)+glyphSize.x/2-.5,drawPos.y=ceil(pos.y+j*glyphSize.y-centerOffsetY-glyphSize.y/2)+glyphSize.y/2-.5,drawTile(drawPos,glyphSize,tileInfo,color,0,!1,void 0,useWebGL,!0,context)}})}getGlyphPos(index,pos=new Vector2){var columns,t=this.tileInfo,padding=t.padding,w=t.size.x+2*padding,h=t.size.y+2*padding;return t.columns?pos.set(t.pos.x+index%t.columns*w,t.pos.y+(index/t.columns|0)*h):(columns=t.textureInfo.size.x/w|0,t=((t.pos.y-padding)/h|0)*columns+((t.pos.x-padding)/w|0)+index,pos.set(t%columns*w+padding,(t/columns|0)*h+padding))}}function engineGlowAlpha(r,falloff){var falloff=3.5*falloff,edge=Math.exp(-falloff);return(Math.exp(-falloff*r*r)-edge)/(1-edge)}const engineGlowTextures=new Map;function engineGlowTexture(falloff=1){var key=max(round(10*falloff),1)/10,falloff=engineGlowTextures.get(key);if(!falloff&&glContext&&canvasAvailable()){var context=createCanvasContext(64),gradient=context.createRadialGradient(32,32,0,32,32,32);for(let i=0;i<=16;++i)gradient.addColorStop(i/16,"rgba(255,255,255,"+engineGlowAlpha(i/16,key).toFixed(4)+")");context.fillStyle=gradient,context.fillRect(0,0,64,64),engineGlowTextures.set(key,falloff=new TextureInfo(context.canvas,!0,!1,!1))}return falloff}let mousePos=vec2(),mousePosScreen=vec2(),mouseDelta=vec2(),mouseDeltaScreen=vec2(),mouseWheel=0,touchPinch=0,mouseInWindow=!0,isUsingGamepad=!1,lastInputDevice="mouse",inputMouseMoveThreshold=6,inputPreventDefault=!0,gamepadPrimary=0;const isTouchDevice=!headlessMode&&"undefined"!=typeof window&&void 0!==window.ontouchstart;function setInputPreventDefault(preventDefault=!0){inputPreventDefault=preventDefault}function setInputMouseMoveThreshold(threshold){inputMouseMoveThreshold=threshold}function usingMouseInput(){return"mouse"===lastInputDevice}function usingKeyboardInput(){return"keyboard"===lastInputDevice}function usingGamepadInput(){return"gamepad"===lastInputDevice}function inputClearKey(key,device=0,clearDown=!0,clearPressed=!0,clearReleased=!0){inputData[device]&&(inputData[device][key]&=~((clearDown?1:0)|(clearPressed?2:0)|(clearReleased?4:0)))}const inputWASDToArrow={KeyW:"ArrowUp",KeyS:"ArrowDown",KeyA:"ArrowLeft",KeyD:"ArrowRight"},inputArrowToWASD={ArrowUp:"KeyW",ArrowDown:"KeyS",ArrowLeft:"KeyA",ArrowRight:"KeyD"},inputKeysHeld=new Set;let inputPinch,inputWasTouching=0,inputTouchIdentifier,inputLastTouchTime=-1e9;function inputIsTouchMouseEvent(e){return e.sourceCapabilities?.firesTouchEvents||performance.now()-inputLastTouchTime<500}function inputClear(){for(let i=inputData.length;i--;)i&&!inputData[i]||(inputData[i]=[]);inputKeysHeld.clear(),inputWasTouching=0,inputPinch=void 0,touchGamepadPointerRole.clear(),touchGamepadButtons.length=0,touchGamepadButtonsPressed.length=0,touchGamepadSticks.length=0,touchGamepadStickPointerId.length=0,gamepadStickData.length=0,gamepadDpadData.length=0}function keyIsDown(key,device=0){return!!(1&inputData[device]?.[key])&&!0}function keyWasPressed(key,device=0){return!!(2&inputData[device]?.[key])&&!0}function keyWasReleased(key,device=0){return!!(4&inputData[device]?.[key])&&!0}function keyDirection(up="ArrowUp",down="ArrowDown",left="ArrowLeft",right="ArrowRight"){var k=key=>keyIsDown(key)?1:0;return vec2(k(right)-k(left),k(up)-k(down))}function mouseIsDown(button){return keyIsDown(button)}function mouseWasPressed(button){return keyWasPressed(button)}function mouseWasReleased(button){return keyWasReleased(button)}function gamepadIsDown(button,gamepad=gamepadPrimary){return keyIsDown(button,gamepad+1)}function gamepadWasPressed(button,gamepad=gamepadPrimary){return keyWasPressed(button,gamepad+1)}function gamepadWasReleased(button,gamepad=gamepadPrimary){return keyWasReleased(button,gamepad+1)}function gamepadStick(stick,gamepad=gamepadPrimary){return gamepadStickData[gamepad]?.[stick]??vec2()}function gamepadDpad(gamepad=gamepadPrimary){return gamepadDpadData[gamepad]?.copy()??vec2()}function gamepadConnected(gamepad=gamepadPrimary){return!!inputData[gamepad+1]}function gamepadStickCount(gamepad=gamepadPrimary){return gamepadStickData[gamepad]?.length??0}function gamepadVibrate(gamepad=gamepadPrimary,duration=200,strongMagnitude=1,weakMagnitude=1,startDelay=0){vibrateEnable&&!headlessMode&&inputGetGamepads()[gamepad]?.vibrationActuator?.playEffect?.("dual-rumble",{duration:duration,strongMagnitude:strongMagnitude,weakMagnitude:weakMagnitude,startDelay:startDelay})?.catch?.(()=>{})}function gamepadVibrateStop(gamepad=gamepadPrimary){vibrateEnable&&!headlessMode&&inputGetGamepads()[gamepad]?.vibrationActuator?.reset?.()}function inputGetGamepads(){try{return navigator?.getGamepads?.()||[]}catch(e){return[]}}function vibrate(pattern=100){vibrateEnable&&!headlessMode&&navigator?.vibrate?.(pattern)}function vibrateStop(){vibrate(0)}function pointerLockRequest(){try{(mainCanvas.requestPointerLock?.())?.catch?.(()=>{})}catch{}}function pointerLockExit(){document.exitPointerLock?.()}function pointerLockIsActive(){return document.pointerLockElement===mainCanvas}const inputData=[[]],gamepadStickData=[],gamepadDpadData=[],gamepadHadInput=[],gamepadButtonsLast=[],gamepadAxisCentered=[],gamepadAxisCenteredFrames=15,touchGamepadTimer=new Timer(void 0,!0),touchGamepadButtons=[],touchGamepadSticks=[],touchGamepadButtonsPressed=[],touchGamepadStickAnchors=[],touchGamepadStickPointerId=[],touchGamepadPointerRole=new Map;let touchGamepadOverlay,touchGamepadStage,touchGamepadSvg,touchGamepadSvgEls,touchGamepadSideZones=[],touchGamepadZoneC,touchGamepadNeedRelayout=!0,touchGamepadLastLayout;function inputUpdatePost(){if(!headlessMode){for(const deviceInputData of inputData)for(const i in deviceInputData)deviceInputData[i]&=1;mouseWheel=touchPinch=0,mouseDelta=vec2(),mouseDeltaScreen=vec2()}}const touchGamepadSvgNS="http://www.w3.org/2000/svg";function touchGamepadStageRect(){return touchGamepadStage.getBoundingClientRect()}function touchGamepadSideStick(side){return side?touchGamepadRightStick:touchGamepadLeftStick}function touchGamepadSideButtonCount(side){return side?touchGamepadButtonCount:touchGamepadLeftButtonCount}function touchGamepadSideButtonBase(side){return side?0:4}function touchGamepadStickOut(side){return side&&touchGamepadLeftStick?1:0}function touchGamepadStickButton(side){return touchGamepadStickOut(side)?11:10}function touchGamepadFaceIndex(b){return 3===b?2:2===b?3:b}function touchGamepadSideHasControl(side){return touchGamepadSideStick(side)||0<touchGamepadSideButtonCount(side)}function touchGamepadSideCenter(side,W,H){if(touchGamepadFloating&&touchGamepadSideStick(side)&&touchGamepadStickAnchors[side])return touchGamepadStickAnchors[side];let y=H-touchGamepadSize;H=touchGamepadSideButtonCount(side);return touchGamepadSideStick(side)||2!==H&&3!==H||(y-=touchGamepadSize/4),vec2(side?W-touchGamepadSize:touchGamepadSize,y)}function touchGamepadApplyStick(side,p){p=p.subtract(touchGamepadStickAnchors[side]);touchGamepadSticks[side]=p.scale(2/touchGamepadSize).clampLength(),touchGamepadButtons[touchGamepadStickButton(side)]=1}function touchGamepadStartBlockSide(side){return touchGamepadSideHasControl(side)&&(!touchGamepadFloating||!touchGamepadSideStick(side))}function touchGamepadPointerUp(e){var role=touchGamepadPointerRole.get(e.pointerId);role&&(touchGamepadPointerRole.delete(e.pointerId),"stick0"===role||"stick1"===role?(e="stick1"===role?1:0,touchGamepadStickPointerId[e]=void 0,touchGamepadSticks[e]=vec2(),delete touchGamepadButtons[touchGamepadStickButton(e)]):"start"===role?delete touchGamepadButtons[9]:delete touchGamepadButtons[+role.slice(4)],touchGamepadTimer.set())}let audioContext="undefined"==typeof AudioContext?void 0:new AudioContext;function audioResume(){audioContext?.resume()?.catch?.(()=>{})}let audioMasterGain=audioContext?.createGain(),audioMasterVolume;function audioUpdateVolume(){audioMasterGain&&soundVolume!==audioMasterVolume&&isFinite(soundVolume)&&(audioMasterGain.gain.value=audioMasterVolume=soundVolume)}audioMasterGain&&(audioMasterGain.connect(audioContext.destination),audioMasterGain.gain.value=audioMasterVolume=soundVolume,audioContext.addEventListener?.("statechange",function(){if(audioIsRunning()){audioWaitingPrune();var instances=[...audioWaitingInstances.keys()];audioWaitingInstances.clear();for(const instance of instances)instance.resume()}}));let audioMasterEffectInput,audioMasterEffectOutput,audioMasterEffectOutputIsEffect;const audioDefaultSampleRate=44100;function audioIsRunning(){return"running"===audioContext?.state}function audioSetSession(){var session="undefined"!=typeof navigator&&navigator.audioSession;session&&(session.type=soundIgnoreSilentSwitch?"playback":"auto")}let audioSuspendedWhenHidden=!1;function audioVisibilityChange(){audioContext&&(document.hidden?soundPauseWhenHidden&&"running"==audioContext.state&&(audioSuspendedWhenHidden=!0,audioContext.suspend()):audioSuspendedWhenHidden&&(audioSuspendedWhenHidden=!1,audioResume()))}const audioWaitingInstances=new Map;function audioWaitingPrune(now=performance.now()){for(var[instance,startTime]of audioWaitingInstances){var remaining=(instance.getDuration()-instance.pausedTime)/instance.rate;!instance.loop&&now-startTime>1e3*remaining&&audioWaitingInstances.delete(instance)}}function setAudioMasterEffect(input,output){var outputArg=output||input,outputArg=!!outputArg&&"input"in outputArg;if(output=audioEffectNode(output,"output")||audioEffectNode(input,"output"),input=audioEffectNode(input,"input"),audioMasterGain){if(audioMasterGain.disconnect(audioMasterEffectInput||audioContext.destination),audioMasterEffectOutput){let fedSpeakers=!0;try{audioMasterEffectOutput.disconnect(audioContext.destination)}catch{fedSpeakers=!1}audioMasterEffectOutputIsEffect&&fedSpeakers&&audioMasterEffectOutput.connect(audioMasterGain)}audioMasterEffectInput=input,audioMasterEffectOutput=output,audioMasterEffectOutputIsEffect=outputArg,(input?(audioMasterGain.connect(input),output.disconnect(),output):audioMasterGain).connect(audioContext.destination)}}function audioEffectNode(effectOrNode,key){return effectOrNode&&"input"in effectOrNode?effectOrNode[key]:effectOrNode}const audioFinite=(value,otherwise)=>isFinite(value)?value:otherwise;function audioParamRamp(param,value,fadeTime=0){value=audioFinite(value,0),fadeTime=audioFinite(fadeTime,0);var startTime=audioContext.currentTime;return param.cancelScheduledValues(startTime),fadeTime?(param.setValueAtTime(param.value,startTime),param.linearRampToValueAtTime(value,startTime+fadeTime)):param.value=value,startTime+fadeTime}class Sound{constructor(asset,randomness,range,taper=soundDefaultTaper,onloadCallback){if(soundEnable&&!headlessMode&&audioContext){var rangeIsDefault=void 0===range;if(rangeIsDefault&&(range=soundDefaultRange),this.range=range,this.rangeIsDefault=rangeIsDefault,this.taper=taper,this.randomness=randomness??0,this.sampleRate=44100,this.sampleLength=0,this.sampleBuffer=void 0,this._sampleChannels=void 0,this.loadedPercent=0,this.onloadCallback=onloadCallback,this.output=void 0,isArray(asset)){range=asset.slice();this.randomness=randomness??clamp(range[1]??.05),range[1]=0,this.sampleChannels=[zzfxG(...range)],this.buildSampleBuffer(),this.loadedPercent=1,onloadCallback?.(this)}else if(asset){const filename=asset+"";engineAddLoad(this.loadSound(filename).catch(e=>{console.warn("Sound load failed for",filename,"-",e?.message??e);try{1!==this.loadedPercent&&this.onloadCallback?.(this)}catch(callbackError){console.warn("Sound onloadCallback failed for",filename,"-",callbackError)}}))}}else this.loadedPercent=1,onloadCallback?.(this)}get range(){return this.rangeValue}set range(range){this.rangeValue=range,this.rangeIsDefault=!1}get sampleChannels(){var buffer=this.sampleBuffer;if(!this._sampleChannels&&buffer){var channels=[];for(let i=0;i<buffer.numberOfChannels;i++)channels.push(buffer.getChannelData(i).slice());this._sampleChannels=channels}return this._sampleChannels}set sampleChannels(sampleChannels){this._sampleChannels=sampleChannels,this.sampleBuffer=void 0,this.sampleLength=sampleChannels?.[0]?.length||0}buildSampleBuffer(){this.sampleBuffer||!this._sampleChannels||headlessMode||(this.sampleBuffer=createAudioBuffer(this._sampleChannels,this.sampleRate),this._sampleChannels=void 0)}play(pos,volume=1,pitch=1,randomnessScale=1,loop=!1,paused=!1){if(soundEnable&&!headlessMode&&(this.sampleBuffer||this._sampleChannels)){let pan;if(pos){var range=this.range;if(range){var lengthSquared=cameraPos.distanceSquared(pos);if(range*range<=lengthSquared)return;var lengthSquared=lengthSquared**.5,taperRange=range*this.taper;taperRange<lengthSquared&&(volume*=percent(lengthSquared,range,taperRange))}pan=2*worldToScreen(pos).x/mainCanvasSize.x-1}lengthSquared=pitch+pitch*this.randomness*randomnessScale*rand(-1,1),range=new SoundInstance(this,volume,lengthSquared,pan,loop,paused);return debug,range}}playLoop(pos,volume=1,pitch=1,randomnessScale=1,paused=!1){return this.play(pos,volume,pitch,randomnessScale,!0,paused)}playMusic(volume=1,loop=!0,paused=!1){return this.play(void 0,volume,1,0,loop,paused)}playNote(semitoneOffset=0,pos,volume){semitoneOffset=getNoteFrequency(semitoneOffset,1);return this.play(pos,volume,semitoneOffset,0)}getDuration(){return this.sampleLength/this.sampleRate||0}isLoaded(){return 1===this.loadedPercent}async loadSound(filename){var response=await loadFetch(filename,"loadSound");if(!response.ok)throw new Error(`loadSound: could not load ${filename}, HTTP ${response.status} `+response.statusText);filename=await response.arrayBuffer(),response=await audioContext.decodeAudioData(filename);this.sampleRate=response.sampleRate,this.sampleLength=response.length,this.sampleBuffer=response,this._sampleChannels=void 0,this.loadedPercent=1,this.onloadCallback?.(this)}}class SoundInstance{constructor(sound,volume=1,rate=1,pan=0,loop=!1,paused=!1){this.sound=sound,this.volume=audioFinite(volume,0),this.rate=audioFinite(rate,1),this.pan=audioFinite(pan,0),this.loop=loop,this.pausedTime=0,this.startTime=void 0,this.startOffset=0,this.gainNode=void 0,this.pannerNode=void 0,this.source=void 0,this.output=sound.output,this.onendedCallback=void 0,this.sourceEnded=source=>{source===this.source&&(this.source=this.gainNode=this.pannerNode=void 0,this.startTime=void 0,this.pausedTime=0,this.onendedCallback?.(source))},paused||this.start()}start(offset=0){if(this.isPlaying()&&this.stop(),this.gainNode=audioContext.createGain(),this.pannerNode=new StereoPannerNode(audioContext,{pan:clamp(this.pan,-1,1)}),this.sound.buildSampleBuffer(),this.source=this.sound.sampleBuffer?playAudioBuffer(this.sound.sampleBuffer,this.volume,this.rate,this.pan,this.loop,this.gainNode,offset,this.sourceEnded,this.output,this.pannerNode):playSamples(this.sound.sampleChannels,this.volume,this.rate,this.pan,this.loop,this.sound.sampleRate,this.gainNode,offset,this.sourceEnded,this.output,this.pannerNode),audioWaitingInstances.delete(this),this.source)this.startTime=audioContext.currentTime,this.startOffset=offset,this.pausedTime=void 0;else if(this.startTime=this.gainNode=this.pannerNode=void 0,this.pausedTime=offset,!audioIsRunning()){audioWaitingPrune();for(const other of audioWaitingInstances.keys())other.sound===this.sound&&audioWaitingInstances.delete(other);audioWaitingInstances.set(this,performance.now())}}setVolume(volume,fadeTime=0){this.volume=volume=audioFinite(volume,0),this.gainNode&&audioParamRamp(this.gainNode.gain,volume,fadeTime)}setPan(pan){this.pan=pan=audioFinite(pan,0),this.pannerNode&&(this.pannerNode.pan.value=clamp(pan,-1,1))}setRate(rate){this.isPlaying()&&(this.startOffset=this.getCurrentTime(),this.startTime=audioContext.currentTime),this.rate=rate=audioFinite(rate,1),this.source&&(this.source.playbackRate.value=rate)}stop(fadeTime=0){audioWaitingInstances.delete(this),this.isPlaying()&&(fadeTime?this.source.stop(audioParamRamp(this.gainNode.gain,0,fadeTime)):this.source.stop()),this.pausedTime=0,this.source=void 0,this.startTime=void 0,this.gainNode=void 0,this.pannerNode=void 0}pause(){audioWaitingInstances.delete(this),this.isPaused()||(this.pausedTime=this.getCurrentTime(),this.source.stop(),this.source=void 0,this.startTime=void 0,this.gainNode=void 0,this.pannerNode=void 0)}resume(){this.isPaused()&&this.start(this.pausedTime)}isPlaying(){return!!this.source}isPaused(){return!this.isPlaying()}getCurrentTime(){var duration,place;return this.isPlaying()?(duration=this.getDuration(),place=this.startOffset+(audioContext.currentTime-this.startTime)*this.rate,duration?(this.loop?mod:min)(place,duration):0):this.pausedTime}getDuration(){return this.sound.getDuration()}getSource(){return this.source}}function speak(text,volume=1,rate=1,pitch=1,language=""){if(soundEnable&&!headlessMode&&"undefined"!=typeof speechSynthesis)return(text=new SpeechSynthesisUtterance(text)).lang=language,text.volume=clamp(volume*soundVolume),text.rate=rate,text.pitch=pitch,speechSynthesis.speak(text),text}function speakStop(){"undefined"!=typeof speechSynthesis&&speechSynthesis.cancel()}function getNoteFrequency(semitoneOffset,rootFrequency=220){return rootFrequency*2**(semitoneOffset/12)}function playSamples(sampleChannels,volume=1,rate=1,pan=0,loop=!1,sampleRate=44100,gainNode,offset=0,onended,output,pannerNode){if(soundEnable&&!headlessMode){if(audioIsRunning())return playAudioBuffer(createAudioBuffer(sampleChannels,sampleRate),volume,rate,pan,loop,gainNode,offset,onended,output,pannerNode);audioSuspendedWhenHidden||audioResume()}}function createAudioBuffer(sampleChannels,sampleRate=44100){var channelCount=sampleChannels.length,sampleLength=sampleChannels[0].length;const buffer=audioContext.createBuffer(channelCount,sampleLength,sampleRate);return sampleChannels.forEach((c,i)=>buffer.getChannelData(i).set(c)),buffer}function playAudioBuffer(buffer,volume=1,rate=1,pan=0,loop=!1,gainNode,offset=0,onended,output,pannerNode){if(soundEnable&&!headlessMode){if(audioIsRunning()){volume=audioFinite(volume,0),rate=audioFinite(rate,1),pan=audioFinite(pan,0);const source=audioContext.createBufferSource();source.buffer=buffer,source.playbackRate.value=rate,source.loop=loop,(gainNode=gainNode||audioContext.createGain()).gain.value=volume;buffer=audioEffectNode(output,"input")||audioMasterGain;gainNode.connect(buffer);const panner=pannerNode||new StereoPannerNode(audioContext,{pan:clamp(pan,-1,1)});return source.connect(panner).connect(gainNode),source.addEventListener("ended",()=>{gainNode.disconnect(),panner.disconnect(),onended&&onended(source)}),source.start(0,offset),debug,source}audioSuspendedWhenHidden||audioResume()}}function zzfx(...zzfxSound){return playSamples([zzfxG(...zzfxSound)])}function zzfxG(volume=1,randomness=.05,frequency=220,attack=0,sustain=0,release=.1,shape=0,shapeCurve=1,slide=0,deltaSlide=0,pitchJump=0,pitchJumpTime=0,repeatTime=0,noise=0,modulation=0,bitCrush=0,delay=0,sustainVolume=1,decay=0,tremolo=0,filter=0){let PI2=2*PI,startSlide=slide*=500*PI2/44100/44100,startFrequency=frequency*=(1+rand(randomness,-randomness))*PI2/44100,modOffset=0,repeat=0,crush=0,jump=1,length,b,t=0,i=0,s=0,f,w=PI2*min(abs(filter),11024)*2/44100,cosw=cos(w),alpha=sin(w)/2/2,a0=1+alpha,a1=-2*cosw/a0,a2=(1-alpha)/a0,b0=(1+sign(filter)*cosw)/2/a0,b1=-(sign(filter)+cosw)/a0,b2=b0,x2=0,x1=0,y2=0,y1=0;for(deltaSlide*=500*PI2/44100**3,modulation*=PI2/44100,pitchJump*=PI2/44100,pitchJumpTime*=44100,repeatTime=44100*repeatTime|0,length=(attack=44100*attack||9)+(decay*=44100)+(sustain*=44100)+(release*=44100)+(delay*=44100)|0,b=new Float32Array(0<length?length:0);i<length;b[i++]=s*volume)++crush%(100*bitCrush|0)||(s=shape?1<shape?2<shape?3<shape?4<shape?t/PI2%1<shapeCurve/2?1:-1:sin(t**3):max(min(tan(t),1),-1):1-(2*t/PI2%2+2)%2:1-4*abs(round(t/PI2)-t/PI2):sin(t),s=(repeatTime?1-tremolo+tremolo*sin(PI2*i/repeatTime):1)*(4<shape?s:sign(s)*abs(s)**shapeCurve)*(i<attack?i/attack:i<attack+decay?1-(i-attack)/decay*(1-sustainVolume):i<attack+decay+sustain?sustainVolume:i<length-delay?(length-i-delay)/release*sustainVolume:0),s=delay?s/2+(delay>i?0:(i<length-delay?1:(length-i)/delay)*b[i-delay|0]/2/(volume||1)):s,filter&&(s=y1=b2*x2+b1*(x2=x1)+b0*(x1=s)-a2*y2-a1*(y2=y1))),f=(frequency+=slide+=deltaSlide)*cos(modulation*modOffset++),t+=f+f*noise*(i*i*PI2%2-1),jump&&++jump>pitchJumpTime&&(frequency+=pitchJump,startFrequency+=pitchJump,jump=0),!repeatTime||++repeat%repeatTime||(frequency=startFrequency,slide=startSlide,jump||=1);return b}const tileCollisionLayers=[];function tileCollisionTester(callbackObject){return callbackObject?"function"==typeof callbackObject?(tileData,pos)=>callbackObject(tileData,pos):(tileData,pos)=>callbackObject.collideWithTile(tileData,pos):tileData=>0<tileData}function tileCollisionGetData(pos,solidOnly=!0){let found=0;for(const layer of tileCollisionLayers)if(!solidOnly||layer.isSolid){var x=pos.x-layer.pos.x,y=pos.y-layer.pos.y,size=layer.size;if(0<=x&&0<=y&&x<size.x&&y<size.y){y=layer.collisionData[(0|y)*size.x+(0|x)];if(0<y)return y;y&&!found&&(found=y)}}return found}function tileCollisionTest(pos,size=vec2(),callbackObject,solidOnly=!0){for(const layer of tileCollisionLayers)if((!solidOnly||layer.isSolid)&&layer.collisionTest(pos,size,callbackObject))return layer}function tileCollisionRaycast(posStart,posEnd,callbackObject,normal,solidOnly=!0){let closestHit,closestDistSq,closestNormal;var hitPos,d,scratchNormal=normal&&vec2();for(const layer of tileCollisionLayers)solidOnly&&!layer.isSolid||(hitPos=layer.collisionRaycast(posStart,posEnd,callbackObject,scratchNormal))&&(d=posStart.distanceSquared(hitPos),void 0===closestHit||d<closestDistSq)&&(closestHit=hitPos,closestDistSq=d,normal)&&(closestNormal=scratchNormal.copy());return closestHit&&normal&&normal.setFrom(closestNormal),closestHit}const tileLayersTiledFlips=[[0,0],[3,1],[2,1],[3,0],[0,1],[1,0],[2,0],[1,1]];function tileLayersLoad(tileMapData,tileInfo=tileLayerDefaultTile(),renderOrder=0,collisionLayer,draw=!0){var{width:mapWidth,height:mapHeight}=tileMapData=tileMapData?tileMapData:{width:50,height:50,layers:[{data:new Array(2500).fill(0)}]};if(!(Number.isInteger(mapWidth)&&Number.isInteger(mapHeight)&&0<mapWidth&&0<mapHeight))throw new Error(`tileLayersLoad: a map is a whole number of cells across and down, not ${mapWidth} by `+mapHeight);if(!isArray(tileMapData.layers)||!tileMapData.layers.length)throw new Error("tileLayersLoad: a map has a list of layers");var spacing,size,width,mapWidth=tileMapData.tilesets?.[0];tileInfo&&!tileInfo.tiles&&mapWidth&&(0<mapWidth.margin||0<mapWidth.spacing)&&(mapHeight=mapWidth.margin||0,spacing=mapWidth.spacing||0,size=tileInfo.size,width=tileInfo.textureInfo?.size.x||0,mapWidth=mapWidth.columns||max(1,floor((width-2*mapHeight+spacing)/(size.x+spacing))),width=tileInfo.pos.subtract(vec2(tileInfo.padding)),tileInfo=new TileInfo(width.add(vec2(mapHeight)),size,tileInfo.textureInfo,spacing/2,tileInfo.bleed,mapWidth));const layers=[],addLayers=(dataLayers,groupColor,groupVisible)=>{for(const dataLayer of dataLayers){var tint,visible,type=dataLayer.type;type&&"tilelayer"!==type&&"group"!==type?layers.push({dataLayer:dataLayer}):(visible=((tint=(tint=dataLayer.tintcolor)?tileLayersColor(tint):(dataLayer.color||WHITE).copy()).a*=dataLayer.opacity??1,groupVisible&&!1!==dataLayer.visible),"group"===type?addLayers(dataLayer.layers||[],groupColor.multiply(tint),visible):layers.push({dataLayer:dataLayer,color:groupColor.multiply(tint),visible:visible}))}};if(addLayers(tileMapData.layers,WHITE,!0),"string"==typeof collisionLayer){const name=collisionLayer;(collisionLayer=layers.findIndex(l=>l.color&&l.dataLayer.name===name))<0&&console.error("tileLayersLoad: no tile layer is named "+name+", none is solid")}var layerIndex,dataLayer,color,tileLayers=[],levelSize=vec2(tileMapData.width,tileMapData.height),layerCount=layers.length;for([layerIndex,{dataLayer,color}]of layers.entries()){var tiles=dataLayer.data;if(color&&(!isArray(tiles)&&!ArrayBuffer.isView(tiles)||dataLayer.data.length!==levelSize.area()))throw new Error(`tileLayersLoad: layer ${dataLayer.name??layerIndex} has ${dataLayer.data?.length} tiles for a map of `+levelSize.area()+"; infinite maps and compressed layers are not read")}for(let layerIndex=layerCount;layerIndex--;){const{dataLayer,color:layerColor,visible}=layers[layerIndex];if(layerColor){var layerRenderOrder=renderOrder-(layerCount-1-layerIndex),tileLayer=new TileCollisionLayer(vec2(),levelSize,tileInfo,layerRenderOrder);tileLayer.isSolid=layerIndex===collisionLayer,tileLayers[layerIndex]=tileLayer,visible||(tileLayer.render=()=>{});for(let x=0;x<levelSize.x;++x)for(let y=0;y<levelSize.y;++y){var direction,mirror,pos=vec2(x,levelSize.y-1-y),data=dataLayer.data[x+y*levelSize.x];data&&([direction,mirror]=tileLayersTiledFlips[data>>>29],data=(268435455&data)-1,data=new TileLayerData(data,direction,!!mirror,layerColor),tileLayer.setData(pos,data),layerIndex===collisionLayer)&&tileLayer.setCollisionData(pos,1)}draw&&visible&&tileLayer.redraw()}}return layers,tileLayers}function tileLayersColor(hex){return(new Color).setHex(9===hex.length?"#"+hex.slice(3)+hex.slice(1,3):hex)}const tileLayersLDtkTypes={Int:"int",Float:"float",Bool:"bool",String:"string",Multilines:"string",Color:"color",FilePath:"file"};function tileLayersFromLDtk(ldtk,level=0){var levels=ldtk?.levels?.length?ldtk.levels:(ldtk?.worlds||[]).flatMap(w=>w.levels||[]);const data="string"==typeof level?levels.find(l=>l.identifier===level):levels[level];if(!data)throw new Error("tileLayersFromLDtk: no level "+level);if(!isArray(data.layerInstances))throw new Error("tileLayersFromLDtk: level "+data.identifier+" has no layers, levels saved as separate files are not read");var instances=data.layerInstances;const tiled=l=>"Entities"!==l.__type,first=instances.find(l=>tiled(l)&&null!=l.__tilesetDefUid)||instances.find(tiled)||instances[0];var grid=first?.__gridSize||16;const width=first?.__cWid||ceil(data.pxWid/grid),height=first?.__cHei||ceil(data.pxHei/grid);if(!(0<width&&0<height&&width*height<=1<<22))throw new Error("tileLayersFromLDtk: level "+data.identifier+" is "+width+" by "+height+" cells");const map={width:width,height:height,tilewidth:grid,tileheight:grid,orientation:"orthogonal",renderorder:"right-down",infinite:!1,layers:[],nextlayerid:1,nextobjectid:1};var tileset=(ldtk?.defs?.tilesets||[]).find(t=>t.uid===first?.__tilesetDefUid);tileset&&(map.tilesets=[{firstgid:1,name:String(tileset.relPath||"").replace(/^.*[\\/]/,"").replace(/\.\w+$/,""),image:tileset.relPath,imagewidth:tileset.pxWid,imageheight:tileset.pxHei,tilewidth:tileset.tileGridSize,tileheight:tileset.tileGridSize,margin:tileset.padding||0,spacing:tileset.spacing||0,columns:tileset.__cWid,tilecount:tileset.__cWid*tileset.__cHei}]);for(let i=instances.length;i--;){var instance=instances[i],name=instance.__identifier,id=map.nextlayerid++;if(tiled(instance))if(instance.__gridSize!==grid||instance.__cWid!==width||instance.__cHei!==height)console.warn(`tileLayersFromLDtk: layer ${name} has another grid, left out`);else{const empty=()=>new Array(width*height).fill(0),opacity=instance.__opacity??1;var values="IntGrid"===instance.__type;if(values){const data=empty();(instance.intGridCsv||[]).forEach((value,k)=>k<data.length&&(data[k]=value)),map.layers.push({id:id,name:name,type:"tilelayer",width:width,height:height,data:data,opacity:opacity,visible:!1,x:0,y:0})}var tiles=[...instance.autoLayerTiles||[],...instance.gridTiles||[]].filter(t=>isArray(t.px));if(tiles.length)if(!tileset||instance.__tilesetDefUid===tileset.uid&&tileset.tileGridSize===grid){var tilesName=values?name+" tiles":name;const stacks=[{depth:0,alpha:1,data:empty()}];var depth=new Uint16Array(width*height);for(const t of tiles){var x=floor(t.px[0]/grid),y=floor(t.px[1]/grid),cell=x+y*width;0<=x&&x<width&&0<=y&&y<height&&(((depth,alpha)=>{let s=stacks.find(s=>s.depth===depth&&s.alpha===alpha);return s||stacks.push(s={depth:depth,alpha:alpha,data:empty()}),s.data})(depth[cell]++,round(1e3*clamp(t.a??1))/1e3)[cell]=(t.t+1|(1&t.f?2147483648:0)|(2&t.f?1073741824:0))>>>0)}var visible=!1!==instance.visible;stacks.sort((a,b)=>a.depth-b.depth||b.alpha-a.alpha);for(const{depth,alpha,data}of stacks){var layerName=tilesName+(depth?` (${depth+1})`:"")+(alpha<1?" "+String(alpha).replace(/^0/,""):"");map.layers.push({id:values||depth||alpha<1?map.nextlayerid++:id,name:layerName,type:"tilelayer",width:width,height:height,data:data,opacity:opacity*alpha,visible:visible,x:0,y:0})}}else console.warn(`tileLayersFromLDtk: the tiles of layer ${name} are on another tileset, left out`);else values||map.layers.push({id:id,name:name,type:"tilelayer",width:width,height:height,data:empty(),opacity:opacity,visible:!1!==instance.visible,x:0,y:0})}else{tiles=(instance.entityInstances||[]).filter(entity=>isArray(entity.px)).map(entity=>{var[pivotX=0,pivotY=0]=entity.__pivot||[],w=entity.width||0,h=entity.height||0,properties=[];for(const field of entity.fieldInstances||[]){var type=tileLayersLDtkTypes[field.__type]||/^(Local|Extern)Enum\./.test(field.__type)&&"string";type&&null!=field.__value&&properties.push({name:field.__identifier,type:type,value:field.__value})}return{id:map.nextobjectid++,name:"",type:entity.__identifier,x:entity.px[0]+(.5-pivotX)*w,y:entity.px[1]+(.5-pivotY)*h,width:w,height:h,rotation:0,visible:!0,properties:properties}});map.layers.push({id:id,name:name,type:"objectgroup",objects:tiles,opacity:1,visible:!0,x:0,y:0})}}return map}const objectLayersTypes=new Map;function objectLayersAddType(name,make,defaults={},tileInfo){objectLayersTypes.set(String(name),{make:make,defaults:defaults,tileInfo:tileInfo})}function objectLayersLoad(tileMapData){const made=[],addObjects=dataLayers=>{for(const dataLayer of dataLayers||[])if("group"===dataLayer.type&&addObjects(dataLayer.layers),"objectgroup"===dataLayer.type)for(const object of dataLayer.objects||[]){var result=function(tileMapData,object){var name=object.type||object.class,type=objectLayersTypes.get(name);if(!type)return void(debug&&name&&console.warn(`objectLayersLoad: no type added for ${name}, skipped`));var{height:tileMapData=0,tilewidth=1,tileheight=1}=tileMapData,tilewidth=vec2(object.x/tilewidth,tileMapData-object.y/tileheight),tileMapData=type["make"],result=tileMapData.prototype?new tileMapData(tilewidth):tileMapData(tilewidth);if(result&&"object"==typeof result){for(var[key,value]of Object.entries(function(type,object){var properties={};for(var[key,value]of Object.entries(type.defaults))properties[key]=value?.copy?value.copy():value;for(const property of object.properties||[]){const{name,value}=property;var parts,x,y;"color"===property.type?value&&(properties[name]=tileLayersColor(value)):isVector2(type.defaults[name])?(parts=String(value).split(","),[x,y]=parts.map(Number),2===parts.length&&parts.every(part=>part.trim())&&isNumber(x)&&isNumber(y)&&(properties[name]=vec2(x,y))):properties[name]=value}return properties}(type,object)))"function"==typeof result[key]&&"function"!=typeof value?debug&&console.warn(`objectLayersLoad: ${name} property ${key} has a method's name, left out`):result[key]=value;return result}}(tileMapData,object);dataLayer,object,result&&made.push(result)}};return addObjects(tileMapData?.layers),made}class TileLayerData{constructor(tile,direction=0,mirror=!1,color=new Color){this.tile=tile,this.direction=direction,this.mirror=mirror,this.color=color.copy()}clear(){this.tile=void 0,this.direction=0,this.mirror=!1,this.color=new Color}}class CanvasLayer extends EngineObject{constructor(pos,size,angle=0,renderOrder=0,canvasSize=vec2(512),useWebGL=!0){super(pos,size,void 0,angle,WHITE,renderOrder),this.context=headlessMode?void 0:createCanvasContext(canvasSize.x,canvasSize.y),this.canvas=this.context?.canvas,this.textureInfo=new TextureInfo(this.canvas,useWebGL);pos=useWebGL&&glContext?glContext.getParameter(glContext.MAX_TEXTURE_SIZE):0;pos&&max(canvasSize.x,canvasSize.y)>pos&&console.warn(`LittleJS: a ${canvasSize.x}x${canvasSize.y} layer is over this device's ${pos} pixel texture limit and draws black, split it into smaller layers`),this.mass=0}destroy(immediate=!1){this.destroyed||(this.textureInfo.destroyWebGLTexture(),super.destroy(immediate))}render(){this.draw(this.pos,this.size,this.color,this.angle,this.mirror,this.additiveColor,!1)}draw(pos,size,color=WHITE,angle=0,mirror=!1,additiveColor,screenSpace=drawScreenSpace,context){var t=this.textureInfo,c=this.canvas,c=(!c||this.hasWebGL()||c.width===t.size.x&&c.height===t.size.y||(t.size=vec2(c.width,c.height),t.sizeInverse=vec2(1/c.width,1/c.height)),(new TileInfo).setFullImage(t));drawTile(pos,size,c,color,angle,mirror,additiveColor,!context&&this.hasWebGL(),screenSpace,context)}updateWebGL(){this.textureInfo.createWebGLTexture()}hasWebGL(){return glEnable&&this.textureInfo.hasWebGL()}}function tileLayerDefaultTile(){return textureInfos[0]?.size.x?tile():void 0}class TileLayer extends CanvasLayer{constructor(pos,size,tileInfo=tileLayerDefaultTile(),renderOrder=0,useWebGL=!0){size=size.floor();var canvasSize=tileInfo?size.multiply(tileInfo.size):size;super(pos,size,0,renderOrder,canvasSize,useWebGL),this.tileInfo=void 0,this.data=[],this.isUsingWebGL=!1,this.tilesInWebGL=void 0,this.debugShow=!0,tileInfo&&(this.tileInfo=tileInfo.frame(0),this.tileInfo.bleed=0,this.tileInfo.tiles=tileInfo.tiles);for(let j=this.size.area();j--;)this.data.push(new TileLayerData);headlessMode&&(this.render=()=>{},this.redraw=()=>{},this.redrawStart=()=>{},this.redrawEnd=()=>{},this.drawTileData=()=>{},this.redrawTileData=()=>{},this.drawLayerTile=()=>{},this.drawLayerRect=()=>{},this.drawTile=()=>{},this.drawRect=()=>{},this.clearLayerRect=()=>{})}setData(layerPos,data,redraw=!1){(layerPos=layerPos.floor()).arrayCheck(this.size)&&(this.data[(0|layerPos.y)*this.size.x+(0|layerPos.x)]=data,redraw)&&(drawContext===this.context?this.drawTileData(layerPos):this.redrawTileData(layerPos))}clearData(layerPos,redraw=!1){this.setData(layerPos,new TileLayerData,redraw)}getData(layerPos){return layerPos.arrayCheck(this.size)?this.data[(0|layerPos.y)*this.size.x+(0|layerPos.x)]:void 0}update(){}render(){this.redrawIfSwitched();var size=this.drawSize||this.size,pos=this.pos.add(size.scale(.5));this.draw(pos,size,this.color,this.angle,this.mirror,this.additiveColor,!1)}onRedraw(){}redraw(){this.redrawStart(!0);for(let x=this.size.x;x--;)for(let y=this.size.y;y--;)this.drawTileData(vec2(x,y),!1);this.isUsingWebGL&&glFlush(),this.onRedraw(),this.redrawEnd(),this.tilesInWebGL=this.isUsingWebGL}redrawIfSwitched(){void 0!==this.tilesInWebGL&&this.hasWebGL()!==this.tilesInWebGL&&this.redraw()}redrawStart(clear=!1){var tileSize;this.context&&(clear||this.redrawIfSwitched(),this.savedRenderSettings=[drawContext,mainCanvasSize,cameraPos,cameraScale,cameraAngle,canvasClearColor,glCustomShader],setShader(),this.savedRenderTarget=[glRenderTarget,glColorMask,glColorAdditive,glSkipScreenSpace],glColorMask=-1,glColorAdditive=0,glSkipScreenSpace=!1,drawContext=this.context,tileSize=this.tileInfo?.size??vec2(1),mainCanvasSize=this.size.multiply(tileSize),canvasClearColor=CLEAR_BLACK,cameraPos=this.size.multiply(tileSize).scale(.5),cameraScale=1,cameraAngle=0,this.isUsingWebGL=this.hasWebGL(),this.isUsingWebGL?glSetRenderTarget(this.textureInfo.glTexture,clear):(clear&&(this.canvas.width=mainCanvasSize.x,this.canvas.height=mainCanvasSize.y),this.context.imageSmoothingEnabled=!tilesPixelated))}redrawEnd(){var target,colorMask,colorAdditive,skipScreenSpace;this.context&&([drawContext,mainCanvasSize,cameraPos,cameraScale,cameraAngle,canvasClearColor,glCustomShader]=this.savedRenderSettings,[target,colorMask,colorAdditive,skipScreenSpace]=this.savedRenderTarget,this.isUsingWebGL&&glSetRenderTarget(target),glColorMask=colorMask,glColorAdditive=colorAdditive,glSkipScreenSpace=skipScreenSpace)}drawTileData(layerPos,clear=!0){var cellPixels,drawPos,tileInfo;this.context&&(cellPixels=this.tileInfo?.size??vec2(1),drawPos=layerPos.multiply(cellPixels),clear&&this.clearLayerRect(drawPos,cellPixels),clear=this.getData(layerPos))&&void 0!==clear.tile&&(tileInfo=function(t,tile){if(t)return t.tiles?t.tiles[tile]:t.columns?t.textureInfo&&t.pos.y+(tile/t.columns|0)*(t.size.y+2*t.padding)+t.size.y>t.textureInfo.size.y?void 0:t.frame(tile):t.index(tile)}(layerPos=this.tileInfo,clear.tile),layerPos&&!tileInfo||this.drawLayerTile(drawPos,cellPixels,tileInfo,clear.color,clear.direction*PI/2,clear.mirror))}redrawTileData(layerPos,clear=!0){this.context&&(this.redrawStart(),this.drawTileData(layerPos,clear),this.redrawEnd())}drawLayerTile(pos,size=vec2(1),tileInfo,color=WHITE,angle=0,mirror,additiveColor){drawTile(pos.add(size.scale(.5)),size,tileInfo,color,angle,mirror,additiveColor,this.isUsingWebGL,!1)}drawLayerRect(pos,size,color,angle=0){this.drawLayerTile(pos,size,void 0,color,angle)}drawTile(pos,size=vec2(1),tileInfo,color=new Color,angle=0,mirror=!1){var tileSize=this.tileInfo?.size??vec2(1),tileSize=(pos=pos.subtract(this.pos).multiply(tileSize),size=size.multiply(tileSize),pos.x-=.5,pos.y=this.canvas.height-pos.y-.5,mainCanvasSize),oldTarget=glRenderTarget,oldColorMask=glColorMask,oldSkip=glSkipScreenSpace,oldColorAdditive=glColorAdditive,oldShader=glCustomShader,useWebGL=(mainCanvasSize=vec2(this.canvas.width,this.canvas.height),glColorMask=-1,glColorAdditive=0,setShader(),glSkipScreenSpace=!1,this.hasWebGL()),drawContext=(useWebGL&&glSetRenderTarget(this.textureInfo.glTexture),useWebGL?void 0:this.context);drawTile(pos,size,tileInfo,color,angle,mirror,void 0,useWebGL,!0,drawContext),mainCanvasSize=tileSize,useWebGL&&glSetRenderTarget(oldTarget),glColorMask=oldColorMask,glColorAdditive=oldColorAdditive,glSkipScreenSpace=oldSkip,setShader(oldShader)}drawRect(pos,size,color,angle){this.drawTile(pos,size,void 0,color,angle)}clearLayerRect(pos,size){var x=pos.x,pos=this.canvas.height-pos.y-size.y;this.hasWebGL()?glClearRect(x,pos,size.x,size.y):this.context.clearRect(x,pos,size.x,size.y)}}class TileCollisionLayer extends TileLayer{constructor(pos,size,tileInfo=tileLayerDefaultTile(),renderOrder=0,useWebGL=!0){super(pos,size.floor(),tileInfo,renderOrder,useWebGL),this.collisionData=[],this.initCollision(this.size),tileCollisionLayers.push(this),this.isSolid=!0,this.shadowSolidOnly=!0}renderShadow(){if(!this.shadowSolidOnly||this.angle||this.mirror)return super.renderShadow();var size=this.size,cellWorld=(this.drawSize||size).divide(size),cellPixels=this.tileInfo?this.tileInfo.size:vec2(1),view=getCameraSize().scale(.5),low=cameraPos.subtract(view),view=cameraPos.add(view),x0=max(0,floor((low.x-this.pos.x)/cellWorld.x)),x1=min(size.x,ceil((view.x-this.pos.x)/cellWorld.x)),low=max(0,floor((low.y-this.pos.y)/cellWorld.y)),y1=min(size.y,ceil((view.y-this.pos.y)/cellWorld.y)),textureHeight=size.y*cellPixels.y,useWebGL=this.hasWebGL();for(let y=low;y<y1;++y)for(let x=x0;x<x1;++x)if(0<this.collisionData[y*size.x+x]){let end=x+1;for(;end<x1&&0<this.collisionData[y*size.x+end];)++end;var count=end-x,tileInfo=new TileInfo(vec2(x*cellPixels.x,textureHeight-(y+1)*cellPixels.y),vec2(count*cellPixels.x,cellPixels.y),this.textureInfo,0,0);drawTile(vec2(this.pos.x+(x+count/2)*cellWorld.x,this.pos.y+(y+.5)*cellWorld.y),vec2(count*cellWorld.x,cellWorld.y),tileInfo,this.color,0,!1,void 0,useWebGL,!1),x=end}}destroy(immediate=!1){var index;this.destroyed||(0<=(index=tileCollisionLayers.indexOf(this))&&tileCollisionLayers.splice(index,1),super.destroy(immediate))}initCollision(size){this.size=size.floor(),this.collisionData=new Array(this.size.area()).fill(0)}setCollisionData(layerPos,data=1){var i=(0|layerPos.y)*this.size.x+(0|layerPos.x);layerPos.arrayCheck(this.size)&&(this.collisionData[i]=data)}clearCollisionData(layerPos){this.setCollisionData(layerPos,0)}getCollisionData(layerPos){var i=(0|layerPos.y)*this.size.x+(0|layerPos.x);return layerPos.arrayCheck(this.size)?this.collisionData[i]:0}collisionTest(pos,size=new Vector2,callbackObject){var collisionTest=tileCollisionTester(callbackObject),callbackObject=pos.x-this.pos.x,pos=pos.y-this.pos.y;if(!(callbackObject+size.x/2<0||callbackObject-size.x/2>this.size.x||pos+size.y/2<0||pos-size.y/2>this.size.y)){var minX=max(callbackObject-size.x/2|0,0),minY=max(pos-size.y/2|0,0),maxX=min(size.x?callbackObject+size.x/2:minX+1,this.size.x),maxY=min(size.y?pos+size.y/2:minY+1,this.size.y);for(let y=minY;y<maxY;++y)for(let x=minX;x<maxX;++x){var tileData=this.collisionData[y*this.size.x+x];if(tileData&&collisionTest(tileData,vec2(x+this.pos.x,y+this.pos.y)))return!0}}return!1}collisionRaycast(posStart,posEnd,callbackObject,normal){const collisionTest=tileCollisionTester(callbackObject),offset=this.pos;var p,q,high,callbackObject=posStart.subtract(offset),posStart=posEnd.subtract(posStart);let t0=0,t1=1;for([p,q,high]of[[callbackObject.x,posStart.x,this.size.x+1],[callbackObject.y,posStart.y,this.size.y+1]])if(q){var u=(-1-p)/q,v=(high-p)/q;t0=max(t0,min(u,v)),t1=min(t1,max(u,v))}else if(p<-1||p>high)return;if(!(t0>t1))return(callbackObject=lineTest(t0?callbackObject.add(posStart.scale(t0)):callbackObject,t1<1?callbackObject.add(posStart.scale(t1)):posEnd.subtract(offset),pos=>{var tileData=this.getCollisionData(pos);return tileData&&collisionTest(tileData,vec2(pos.x+offset.x,pos.y+offset.y))},normal))&&(posStart=callbackObject.floor(),callbackObject.x+=offset.x,callbackObject.y+=offset.y,callbackObject.x=(posEnd=(v,low)=>v<low?low:v<low+1?v:low+1-max(1e-9,1e-15*abs(low)))(callbackObject.x,posStart.x+offset.x),callbackObject.y=posEnd(callbackObject.y,posStart.y+offset.y)),callbackObject}}class ParticleEmitter extends EngineObject{constructor(pos,angle,emitSize=0,emitTime=0,emitRate=100,emitConeAngle=PI,tileInfo,colorStartA=WHITE,colorStartB=WHITE,colorEndA=CLEAR_WHITE,colorEndB=CLEAR_WHITE,particleTime=.5,sizeStart=.1,sizeEnd=1,speed=.1,angleSpeed=.05,damping=1,angleDamping=1,gravityScale=0,particleConeAngle=PI,fadeRate=.1,randomness=.2,collideLevel=!1,additive=!1,randomColorLinear=!0,renderOrder=additive?1e9:0,localSpace=!1){if(super(pos,vec2(),tileInfo,angle,void 0,renderOrder),debug){var numbers={emitTime:emitTime,emitRate:emitRate,emitConeAngle:emitConeAngle,particleTime:particleTime,sizeStart:sizeStart,sizeEnd:sizeEnd,speed:speed,angleSpeed:angleSpeed,damping:damping,angleDamping:angleDamping,gravityScale:gravityScale,particleConeAngle:particleConeAngle,fadeRate:fadeRate,randomness:randomness};var colors={colorStartA:colorStartA,colorStartB:colorStartB,colorEndA:colorEndA,colorEndB:colorEndB}}this.emitCircle="number"==typeof emitSize,this.emitSize="number"==typeof emitSize?vec2(emitSize):emitSize.copy(),this.emitTime=emitTime,this.emitRate=emitRate,this.emitConeAngle=emitConeAngle,this.colorStartA=colorStartA.copy(),this.colorStartB=colorStartB.copy(),this.colorEndA=colorEndA.copy(),this.colorEndB=colorEndB.copy(),this.randomColorLinear=randomColorLinear,this.particleTime=particleTime,this.sizeStart=sizeStart,this.sizeEnd=sizeEnd,this.speed=speed,this.angleSpeed=angleSpeed,this.damping=damping,this.angleDamping=angleDamping,this.gravityScale=gravityScale,this.particleConeAngle=particleConeAngle,this.fadeRate=fadeRate,this.randomness=randomness,this.collideLevel=collideLevel,this.additive=additive,this.localSpace=localSpace,this.trailScale=0,this.scale=1,this.gravity=0,this.particleCreateCallback=void 0,this.particleDestroyCallback=void 0,this.particleCollideCallback=void 0,this.particleUpdateCallback=void 0,this.velocityInheritance=0,this.emitTimeBuffer=1,this.emitElapsed=0,this.particles=[],this.previousPos=void 0,this.previousAngle=this.angle,this.emitFrom=vec2(),this.emitBehind=0}update(){if(this.previousPos||(this.previousPos=this.pos.copy(),this.previousAngle=this.angle),this.velocityInheritance&&(p=this.velocityInheritance,this.velocity.x=p*(this.pos.x-this.previousPos.x),this.velocity.y=p*(this.pos.y-this.previousPos.y),this.angleVelocity=p*(this.angle-this.previousAngle)),this.previousAngle=this.angle,this.emitFrom.x=this.previousPos.x,this.emitFrom.y=this.previousPos.y,this.previousPos.x=this.pos.x,this.previousPos.y=this.pos.y,this.isActive()){var p=this.emitRate*particleEmitRateScale,step=this.emitTime?min(timeDelta,this.emitTime-this.emitElapsed):timeDelta;if(this.emitElapsed+=timeDelta,0<p&&p<1/0){this.emitTimeBuffer+=p*step+(this.emitTime&&this.emitElapsed>=this.emitTime?1e-6:0);var travelled=this.emitFrom&&this.pos.distance(this.emitFrom)<=2*objectMaxSpeed,count=floor(this.emitTimeBuffer);try{for(let i=count;i--;--this.emitTimeBuffer)this.emitBehind=travelled?i/count:0,this.emitParticle()}finally{this.emitBehind=0}}}else 0===this.particles.length&&this.destroy(!0);var particles=this.particles;let alive=0;for(let i=0;i<particles.length;++i){const p=particles[i];p.update(),p.destroyed||(particles[alive++]=p)}particles.length=alive}emitParticle(){var scale=this.scale;let pos=this.emitCircle?randInCircle(this.emitSize.x/2*scale):vec2(rand(-.5,.5)*this.emitSize.x*scale,rand(-.5,.5)*this.emitSize.y*scale),angle=rand(this.particleConeAngle,-this.particleConeAngle);this.localSpace||(this.emitCircle||(pos=pos.rotate(this.angle)),behind=this.emitBehind,pos.x+=this.pos.x+(this.emitFrom.x-this.pos.x)*behind,pos.y+=this.pos.y+(this.emitFrom.y-this.pos.y)*behind,angle+=this.angle);const randomness=this.randomness;var behind=v=>v+v*rand(randomness,-randomness),particleTime=behind(this.particleTime),sizeStart=behind(this.sizeStart)*scale,sizeEnd=behind(this.sizeEnd)*scale,scale=behind(this.speed)*scale,behind=behind(this.angleSpeed)*randSign(),coneAngle=rand(this.emitConeAngle,-this.emitConeAngle),colorStart=randColor(this.colorStartA,this.colorStartB,this.randomColorLinear),colorEnd=randColor(this.colorEndA,this.colorEndB,this.randomColorLinear),coneAngle=this.localSpace?coneAngle:this.angle+coneAngle,scale=vec2(scale*sin(coneAngle),scale*cos(coneAngle));let angleVelocity=behind;!this.localSpace&&0<this.velocityInheritance&&(scale.x+=this.velocity.x,scale.y+=this.velocity.y,angleVelocity+=this.angleVelocity);coneAngle=new Particle(this,pos,angle,colorStart,colorEnd,particleTime,sizeStart,sizeEnd,scale,angleVelocity);return this.particles.push(coneAngle),this.particleCreateCallback?.(coneAngle),coneAngle}updatePhysics(){}render(){this.additive&&setAdditiveBlendMode(),particleTile=null,particleTileBatch=!0;for(const particle of this.particles)particle.render();particleTileBatch=!1,this.additive&&setAdditiveBlendMode(!1)}isActive(){return!this.emitTime||this.emitElapsed<this.emitTime}destroy(immediate=!1){this.destroyed||(super.destroy(immediate),!immediate&&0<this.particles.length&&(this.destroyed=!1,this.emitTime=-1))}}const particleDrawPos=new Vector2,particleDrawSize=new Vector2,particleCollidePos=new Vector2;let particleTile,particleTileBatch=!1,particleTileTextured=!1,particleTileEmpty=!1,particleTileU0=0,particleTileV0=0,particleTileU1=0,particleTileV1=0;function particleCollideTest(particle,collideCallback,x,y){var data=tileCollisionGetData(particleCollidePos.set(x,y));return collideCallback?!!data&&!!collideCallback(particle,data,vec2(x,y)):0<data}class Particle{constructor(emitter,pos,angle,colorStart,colorEnd,lifeTime,sizeStart,sizeEnd,velocity=vec2(),angleVelocity=0){this.emitter=emitter,this.scale=emitter.scale??1,this.pos=pos,this.angle=angle,this.size=vec2(sizeStart),this.color=colorStart.copy(),this.colorStart=colorStart,this.colorEnd=colorEnd,this.lifeTime=lifeTime,this.sizeStart=sizeStart,this.sizeEnd=sizeEnd,this.velocity=velocity,this.angleVelocity=angleVelocity,this.spawnTime=time,this.mirror=randBool(),this.groundObject=void 0,this.destroyed=!1,this.tileInfo=emitter.tileInfo}update(){if(!this.destroyed){var emitter=this.emitter,damping=emitter.damping,angleDamping=emitter.angleDamping,restitution=emitter.restitution,friction=emitter.friction,gravityScale=emitter.gravityScale,collideLevel=emitter.collideLevel,collideCallback=emitter.particleCollideCallback,updateCallback=emitter.particleUpdateCallback;if(this.lifeTime<=0||time-this.spawnTime>this.lifeTime)this.destroy();else{var collideLevel=enablePhysicsSolver&&collideLevel,oldX=this.pos.x,oldY=this.pos.y,fall=this.scale;let gravityX=gravity.x*gravityScale*fall,gravityY=(gravity.y*gravityScale+(emitter.gravity||0))*fall;if(emitter.localSpace&&emitter.angle&&(gravityScale=cos(emitter.angle),fall=sin(emitter.angle),[gravityX,gravityY]=[gravityX*gravityScale-gravityY*fall,gravityX*fall+gravityY*gravityScale]),this.velocity.x=this.velocity.x*damping+gravityX,this.velocity.y=this.velocity.y*damping+gravityY,collideLevel){var emitter=this.velocity.lengthSquared();if(emitter>objectMaxSpeed*objectMaxSpeed){const s=objectMaxSpeed/emitter**.5;this.velocity.x*=s,this.velocity.y*=s}}this.pos.x+=this.velocity.x,this.pos.y+=this.velocity.y,this.angle+=this.angleVelocity*=angleDamping,collideLevel&&(this.groundObject=void 0,particleCollideTest(this,collideCallback,this.pos.x,this.pos.y)&&(fall=tileCollisionTest(this.pos),particleCollideTest(this,collideCallback,oldX,oldY)||(gravityScale=particleCollideTest(this,collideCallback,this.pos.x,oldY),damping=particleCollideTest(this,collideCallback,oldX,this.pos.y),emitter=fall?max(restitution,fall.restitution):restitution,angleDamping=fall?max(friction,fall.friction):friction,gravityScale&&(this.pos.x=oldX,this.velocity.x*=-emitter,this.velocity.y*=angleDamping),!damping&&gravityScale)||(collideLevel=gravityY,(this.velocity.y<0&&collideLevel<0||0<this.velocity.y&&0<collideLevel)&&(this.groundObject=fall),this.pos.y=oldY,this.velocity.y*=-emitter,this.velocity.x*=angleDamping)),this.destroyed)||updateCallback?.(this)}}}destroy(){var destroyCallback,c;this.destroyed||(destroyCallback=this.emitter.particleDestroyCallback,c=this.colorEnd,this.color.set(c.r,c.g,c.b,c.a),this.size.set(this.sizeEnd,this.sizeEnd),this.destroyed=!0,destroyCallback?.(this))}render(){var s,emitter=this.emitter,localSpace=emitter.localSpace,additive=emitter.additive&&!glAdditive,trailScale=emitter.trailScale,fadeRate=emitter.fadeRate/2,p1=0<this.lifeTime?min((time-this.spawnTime)/this.lifeTime,1):1,p2=1-p1,sizeNow=p2*this.sizeStart+p1*this.sizeEnd,sizeNow=(this.size.set(sizeNow,sizeNow),particleDrawSize.set(sizeNow,sizeNow)),fadeRate=p1<fadeRate?p1/fadeRate:1-fadeRate<p1?(1-p1)/fadeRate:1,p2=(this.color.r=p2*this.colorStart.r+p1*this.colorEnd.r,this.color.g=p2*this.colorStart.g+p1*this.colorEnd.g,this.color.b=p2*this.colorStart.b+p1*this.colorEnd.b,this.color.a=(p2*this.colorStart.a+p1*this.colorEnd.a)*fadeRate,particleDrawPos.set(this.pos.x,this.pos.y));let angle=this.angle;localSpace&&(p1=emitter.angle,fadeRate=cos(-p1),s=sin(-p1),p2.set(emitter.pos.x+p2.x*fadeRate-p2.y*s,emitter.pos.y+p2.x*s+p2.y*fadeRate),angle+=p1),additive&&setAdditiveBlendMode(),trailScale&&(fadeRate=(s=localSpace?this.velocity.rotate(emitter.angle):this.velocity).length())&&(sizeNow.y=max(sizeNow.x,fadeRate*trailScale),angle=atan2(s.x,s.y)),glEnable&&!headlessMode?(p1=this.tileInfo,particleTileBatch&&p1===particleTile||function(tileInfo){var bleed,x,y,w,bleedX,textureInfo=(particleTile=tileInfo)?.textureInfo;particleTileTextured=!!textureInfo,particleTileEmpty=!(!textureInfo||tileInfo.size.x&&tileInfo.size.y),textureInfo&&!particleTileEmpty&&(glSetTexture(textureInfo.glTexture),textureInfo=textureInfo.sizeInverse,bleed=tileInfo.bleed,x=tileInfo.pos.x*textureInfo.x,y=tileInfo.pos.y*textureInfo.y,w=tileInfo.size.x*textureInfo.x,tileInfo=tileInfo.size.y*textureInfo.y,particleTileV1=bleed?(bleedX=textureInfo.x*bleed,textureInfo=textureInfo.y*bleed,particleTileU0=x+bleedX,particleTileV0=y+textureInfo,particleTileU1=x-bleedX+w,y-textureInfo+tileInfo):(particleTileU0=x,particleTileV0=y,particleTileU1=x+w,y+tileInfo))}(p1),particleTileTextured?particleTileEmpty||(localSpace=p1.textureInfo.glTexture,glActiveTexture!==localSpace&&glSetTexture(localSpace),glDraw(p2.x,p2.y,this.mirror?-sizeNow.x:sizeNow.x,sizeNow.y,angle,particleTileU0,particleTileV0,particleTileU1,particleTileV1,this.color.rgbaInt())):glDrawUntextured(p2.x,p2.y,sizeNow.x,sizeNow.y,angle,this.color.rgbaInt())):drawTile(p2,sizeNow,this.tileInfo,this.color,angle,this.mirror,void 0,!1,!1),additive&&setAdditiveBlendMode(!1)}}let glCanvas,glContext,glMaxTextureSize=0;function glClampTextureSize(size){return glMaxTextureSize?min(size,glMaxTextureSize):size}let glAntialias=!0,glMipmappedTextures=new WeakSet,glMipmapsUntilTarget=new WeakSet,glMipmapsStale=new Set,glPremultipliedTextures=new WeakSet,glShaderPremultiplied,glEnableBeforeLoss=!0,glShader,glPolyShader,glPolyMode,glAdditive,glBatchAdditive,glActiveTexture,glArrayBuffer,glGeometryBuffer,glPositionData,glColorData,glBatchCount,glTextureInfos=new Set,glInstancedVAO,glPolyVAO,glFramebuffer,glRenderTarget,glShaderObjects=[],glCustomShader,glBatchShader,glProgramCustom,glTransform,glRenderTargetSaved,glUniformLocations=new WeakMap,glCanBeEnabled=!0,glDeviceFailed=!1,glFailedPrograms=new WeakSet,glColorMask=-1,glColorAdditive=0,glRenderTargetBase,glSkipScreenSpace=!1;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;}",gl_FRAGMENT_TINT_SOURCE="c=t*d+e;if(premultipliedTexture){c.a=min(c.a,1.);c.rgb=min(t.rgb*d.rgb*min(d.a+e.a,1.)+e.rgb*c.a,c.a);}";function glSetInstancedMode(force=!1){(force||glPolyMode)&&(glFlush(),glPolyMode=!1,glContext.useProgram(glShader),glContext.bindVertexArray(glInstancedVAO),glContext.bindBuffer(glContext.ARRAY_BUFFER,glArrayBuffer))}function glPreRender(clear=!0){var dpr,sa,bufferSizeX;glEnable&&glContext&&(dpr=glRenderTarget?1:getCanvasPixelRatio(),bufferSizeX=mainCanvasSize.x*dpr|0,dpr=mainCanvasSize.y*dpr|0,glRenderTarget||glCanvas.width===bufferSizeX&&glCanvas.height===dpr||(glCanvas.width=bufferSizeX,glCanvas.height=dpr),glContext.viewport(0,0,bufferSizeX,dpr),clear&&glClearCanvas(),bufferSizeX=vec2(2*cameraScale).divide(mainCanvasSize),glRenderTarget&&(bufferSizeX.y=-bufferSizeX.y),dpr=cameraPos.rotate(-cameraAngle),clear=vec2(-1).subtract(dpr.multiply(bufferSizeX)),dpr=cos(cameraAngle),sa=sin(cameraAngle),sa=[bufferSizeX.x*dpr,bufferSizeX.y*sa,0,0,-bufferSizeX.x*sa,bufferSizeX.y*dpr,0,0,1,1,1,0,clear.x,clear.y,0,1],glTransform=sa,(bufferSizeX=(program,uniform,value)=>{glContext.useProgram(program),glContext.uniformMatrix4fv(glUniformLocation(program,uniform),!1,value)})(glPolyShader,"m",sa),bufferSizeX(glShader,"m",sa),glContext.activeTexture(glContext.TEXTURE0),textureInfos[0]&&(glActiveTexture=textureInfos[0].glTexture,glContext.bindTexture(glContext.TEXTURE_2D,glActiveTexture)),glContext.bindBuffer(glContext.ARRAY_BUFFER,glArrayBuffer),glSetInstancedMode(!(glAdditive=glBatchAdditive=!1)))}function glClearCanvas(){var color;glContext&&(color=canvasClearColor,glContext.clearColor(color.r*color.a,color.g*color.a,color.b*color.a,color.a),glContext.clear(glContext.COLOR_BUFFER_BIT))}function glSetTexture(texture){glContext&&(texture!==glActiveTexture&&(glFlush(),glActiveTexture=texture,glContext.bindTexture(glContext.TEXTURE_2D,glActiveTexture)),glMipmapsStale.size)&&glUpdateMipmaps(texture)}function glUpdateMipmaps(texture){glMipmapsStale.has(texture)&&(glMipmapsStale.delete(texture),glContext.generateMipmap(glContext.TEXTURE_2D))}const glWrapRepeat=Object.freeze([10497,10497]),glWrapClamp=Object.freeze([33071,33071]);function glWrapModes(wrap){return isArray(wrap)?wrap:wrap?glWrapRepeat:glWrapClamp}function glSetTextureWrap(texture,wrap=!0){var isCurrent;glContext&&texture&&([texture,wrap]=((isCurrent=texture===glActiveTexture)?glFlush():glContext.bindTexture(glContext.TEXTURE_2D,texture),glWrapModes(wrap)),glContext.texParameteri(glContext.TEXTURE_2D,glContext.TEXTURE_WRAP_S,texture),glContext.texParameteri(glContext.TEXTURE_2D,glContext.TEXTURE_WRAP_T,wrap),!isCurrent)&&glActiveTexture&&glContext.bindTexture(glContext.TEXTURE_2D,glActiveTexture)}function glCompileShader(source,type){if(glContext){type=glContext.createShader(type);if(glContext.shaderSource(type,source),glContext.compileShader(type),debug&&!glContext.getShaderParameter(type,glContext.COMPILE_STATUS))throw glContext.getShaderInfoLog(type);return type}}function glCreateProgram(vsSource,fsSource){if(glContext){var program=glContext.createProgram(),vsSource=glCompileShader(vsSource,glContext.VERTEX_SHADER),fsSource=glCompileShader(fsSource,glContext.FRAGMENT_SHADER);if(glContext.attachShader(program,vsSource),glContext.attachShader(program,fsSource),glContext.linkProgram(program),!glContext.getProgramParameter(program,glContext.LINK_STATUS)){vsSource=glContext.getProgramInfoLog(program)+"\n"+glContext.getShaderInfoLog(vsSource)+glContext.getShaderInfoLog(fsSource);if(debug)throw vsSource;console.error("LittleJS: a shader failed to build\n"+vsSource),glFailedPrograms.add(program)}return program}}function glUniformLocation(program,name){let cache=glUniformLocations.get(program);return cache||glUniformLocations.set(program,cache={}),cache[name]??=glContext.getUniformLocation(program,name)}function glShaderTrack(shader){glShaderObjects.includes(shader)||glShaderObjects.push(shader)}function glCreateTexture(image,wrap=!1,pixelated=tilesPixelated){if(glContext){var texture=glContext.createTexture();let mipMap=!1;image?.width?(glSetTextureData(texture,image,pixelated),glContext.bindTexture(glContext.TEXTURE_2D,texture),!(mipMap=!pixelated)||isPowerOfTwo(image.width)&&isPowerOfTwo(image.height)||glMipmapsUntilTarget.add(texture)):(image=new Uint8Array([255,255,255,255]),glContext.bindTexture(glContext.TEXTURE_2D,texture),glContext.texImage2D(glContext.TEXTURE_2D,0,glContext.RGBA,1,1,0,glContext.RGBA,glContext.UNSIGNED_BYTE,image));var image=pixelated?glContext.NEAREST:glContext.LINEAR,pixelated=mipMap?glContext.LINEAR_MIPMAP_LINEAR:image,[image,pixelated]=(glContext.texParameteri(glContext.TEXTURE_2D,glContext.TEXTURE_MAG_FILTER,image),glContext.texParameteri(glContext.TEXTURE_2D,glContext.TEXTURE_MIN_FILTER,pixelated),glWrapModes(wrap));return glContext.texParameteri(glContext.TEXTURE_2D,glContext.TEXTURE_WRAP_S,image),glContext.texParameteri(glContext.TEXTURE_2D,glContext.TEXTURE_WRAP_T,pixelated),mipMap&&(glContext.generateMipmap(glContext.TEXTURE_2D),glMipmappedTextures.add(texture)),glContext.bindTexture(glContext.TEXTURE_2D,glActiveTexture),texture}}function glDeleteTexture(texture){glContext&&texture&&(texture===glActiveTexture&&(glFlush(),glActiveTexture=void 0),glMipmapsStale.delete(texture),glPremultipliedTextures.delete(texture),glContext.deleteTexture(texture))}function glSetTextureData(texture,image,pixelated=tilesPixelated){var context;glContext&&(texture===glActiveTexture&&glFlush(),glContext.bindTexture(glContext.TEXTURE_2D,texture),"undefined"!=typeof ImageBitmap&&image instanceof ImageBitmap&&((context=createCanvasContext(image.width,image.height)).drawImage(image,0,0),image=context.canvas),context=!pixelated,glContext.pixelStorei(glContext.UNPACK_PREMULTIPLY_ALPHA_WEBGL,context),glContext.texImage2D(glContext.TEXTURE_2D,0,glContext.RGBA,glContext.RGBA,glContext.UNSIGNED_BYTE,image),glContext.pixelStorei(glContext.UNPACK_PREMULTIPLY_ALPHA_WEBGL,!1),context?glPremultipliedTextures.add(texture):glPremultipliedTextures.delete(texture),glMipmappedTextures.has(texture)&&glContext.generateMipmap(glContext.TEXTURE_2D),glContext.bindTexture(glContext.TEXTURE_2D,glActiveTexture))}function glFlush(){if(glEnable&&glContext&&glBatchCount){var height,premultiplied=!glPolyMode&&glPremultipliedTextures.has(glActiveTexture),sourceBlend=premultiplied?glContext.ONE:glContext.SRC_ALPHA,destBlend=glBatchAdditive?glContext.ONE:glContext.ONE_MINUS_SRC_ALPHA;if(glContext.blendFuncSeparate(sourceBlend,destBlend,glContext.ONE,destBlend),glContext.enable(glContext.BLEND),!glPolyMode&&(glBatchShader||glProgramCustom)){const program=!glBatchShader||((sourceBlend=glBatchShader).program||glShaderTrack(sourceBlend),sourceBlend=sourceBlend.program||=glCreateProgram(gl_VERTEX_SOURCE,"#version 300 es\nprecision highp float;uniform sampler2D iChannel0;uniform vec3 iResolution;uniform float iTime;uniform bool premultipliedTexture;in vec2 v,l;in vec4 d,e;out vec4 c;void mainImage(out vec4,vec2);void main(){vec4 t;mainImage(t,v);"+gl_FRAGMENT_TINT_SOURCE+"}\n#define localUV l\n"+sourceBlend.fragmentCode+"\n"),glFailedPrograms.has(sourceBlend))?glShader:sourceBlend;glContext.useProgram(program),glProgramCustom=!!glBatchShader,glBatchShader&&(destBlend=name=>glUniformLocation(program,name),glContext.uniformMatrix4fv(destBlend("m"),!1,glTransform),glContext.uniform1f(destBlend("iTime"),time),sourceBlend=glRenderTarget?mainCanvasSize.x:glCanvas.width,height=glRenderTarget?mainCanvasSize.y:glCanvas.height,glContext.uniform3f(destBlend("iResolution"),sourceBlend,height,1),glContext.uniform1i(destBlend("premultipliedTexture"),+premultiplied),program===glShader)&&(glShaderPremultiplied=premultiplied)}glPolyMode||glBatchShader||glShaderPremultiplied===premultiplied||(glContext.uniform1i(glUniformLocation(glShader,"premultipliedTexture"),+premultiplied),glShaderPremultiplied=premultiplied);sourceBlend=glBatchCount*(glPolyMode?gl_INDICES_PER_POLY_VERTEX:gl_INDICES_PER_INSTANCE);glContext.bufferSubData(glContext.ARRAY_BUFFER,0,glPositionData,0,sourceBlend),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(context){glEnable&&glContext&&(glFlush(),context.drawImage(glCanvas,0,0))}function glSetAntialias(antialias=!0){glAntialias=antialias}function glDraw(x,y,sizeX,sizeY,angle=0,uv0X=0,uv0Y=0,uv1X=1,uv1Y=1,rgba=-1,rgbaAdditive=0){(glBatchCount>=gl_MAX_INSTANCES||glBatchAdditive!==glAdditive||glBatchShader!==glCustomShader)&&glFlush(),glSetInstancedMode();var offset=glBatchCount++*gl_INDICES_PER_INSTANCE;glPositionData[offset++]=x,glPositionData[offset++]=y,glPositionData[offset++]=sizeX,glPositionData[offset++]=sizeY,glPositionData[offset++]=uv0X,glPositionData[offset++]=uv0Y,glPositionData[offset++]=uv1X,glPositionData[offset++]=uv1Y,glColorData[offset++]=rgba&glColorMask,glColorData[offset++]=rgbaAdditive&glColorMask|glColorAdditive,glPositionData[+offset]=angle}function glDrawUntextured(x,y,sizeX,sizeY,angle,rgba){glDraw(x,y,sizeX,sizeY,angle,0,0,0,0,0,rgba)}const glPointsOut=[],glPointsPool=[];function glDrawPointsTransform(points,rgba,x,y,sx,sy,angle,tristrip=!0){var pointsOut=glPointsOut,pool=glPointsPool,count=points.length,sa=(pointsOut.length=count,sin(-angle)),ca=cos(-angle);for(let i=0;i<count;++i){var p=points[i],out=pool[i]||=vec2(),px=p.x*sx,p=p.y*sy;out.x=x+ca*px-sa*p,out.y=y+sa*px+ca*p,pointsOut[i]=out}glDrawPoints(tristrip?glPolyStrip(pointsOut):pointsOut,rgba)}function glDrawOutlineTransform(points,rgba,lineWidth,x,y,sx,sy,angle,wrap=!0){glDrawPointsTransform(function(points,width,wrap=!0){if(points.length<2)return[];var halfWidth=width/2,strip=glOutlineStrip,pool=glOutlinePool,n=(strip.length=0,points.length),e=1e-6;for(let i=0;i<n;i++){var prev=points[wrap?(i-1+n)%n:max(i-1,0)],curr=points[i],next=points[wrap?(i+1)%n:min(i+1,n-1)],dx1=curr.x-prev.x,prev=curr.y-prev.y,len1=(dx1*dx1+prev*prev)**.5,dx2=next.x-curr.x,next=next.y-curr.y,len2=(dx2*dx2+next*next)**.5;if(!(len1<e&&len2<e)){prev=e<len1?-prev/len1:0,dx1=e<len1?dx1/len1:0;let nx=prev+(e<len2?-next/len2:0),ny=dx1+(e<len2?dx2/len2:0);len1=(nx*nx+ny*ny)**.5,len2=(len1<e?(nx=prev,ny=dx1):(nx/=len1,ny/=len1,next=prev*nx+dx1*ny,e<next&&(dx2=min(1/next,10),nx*=dx2,ny*=dx2)),pool[strip.length]||=vec2()),len1=pool[strip.length+1]||=vec2();len2.x=curr.x-nx*halfWidth,len2.y=curr.y-ny*halfWidth,len1.x=curr.x+nx*halfWidth,len1.y=curr.y+ny*halfWidth,strip.push(len2,len1)}}1<strip.length&&wrap&&(strip.push(strip[0]),strip.push(strip[1]));return strip}(points,lineWidth,wrap),rgba,x,y,sx,sy,angle,!1)}function glDrawPoints(points,rgba){glDrawPolyStrip(points,rgba)}function glDrawColoredPoints(points,pointColors){glDrawPolyStrip(points,0,pointColors)}function glDrawPolyStrip(points,rgba,pointColors){if(glEnable&&!(points.length<3)){var vertCount=points.length+2;if((glBatchCount+vertCount>=gl_MAX_POLY_VERTEXES||glBatchAdditive!==glAdditive)&&glFlush(),!(vertCount>=gl_MAX_POLY_VERTEXES)){glPolyMode||(glFlush(),glPolyMode=!0,glContext.useProgram(glPolyShader),glContext.bindVertexArray(glPolyVAO));let offset=glBatchCount*gl_INDICES_PER_POLY_VERTEX;for(let i=vertCount;i--;){var j=clamp(i-1,0,vertCount-3),point=points[j],j=pointColors?pointColors[j]:rgba;glPositionData[offset++]=point.x,glPositionData[offset++]=point.y,glColorData[offset++]=j&glColorMask|glColorAdditive}glBatchCount+=vertCount}}}function glSetRenderTarget(texture,clear=!1){glFlush();var baseSize,previousTarget=glRenderTarget;if(texture)glMipmapsUntilTarget.has(texture)&&(glMipmapsUntilTarget.delete(texture),glMipmappedTextures.delete(texture),glContext.bindTexture(glContext.TEXTURE_2D,texture),glContext.texParameteri(glContext.TEXTURE_2D,glContext.TEXTURE_MIN_FILTER,glContext.LINEAR),glContext.bindTexture(glContext.TEXTURE_2D,glActiveTexture)),glPremultipliedTextures.add(texture),glRenderTarget||(glRenderTargetSaved=[glTransform,glAdditive]),glRenderTarget=texture,glContext.bindFramebuffer(glContext.FRAMEBUFFER,glFramebuffer),glContext.framebufferTexture2D(glContext.FRAMEBUFFER,glContext.COLOR_ATTACHMENT0,glContext.TEXTURE_2D,texture,0),glPreRender(clear);else if(glRenderTarget=void 0,glRenderTargetBase?([clear,baseSize]=glRenderTargetBase,glContext.bindFramebuffer(glContext.FRAMEBUFFER,glFramebuffer),glContext.framebufferTexture2D(glContext.FRAMEBUFFER,glContext.COLOR_ATTACHMENT0,glContext.TEXTURE_2D,clear,0),glContext.viewport(0,0,baseSize.x,baseSize.y)):(glContext.bindFramebuffer(glContext.FRAMEBUFFER,null),glContext.viewport(0,0,glCanvas.width,glCanvas.height)),glRenderTargetSaved){[glTransform,glAdditive]=glRenderTargetSaved,glRenderTargetSaved=void 0;for(const program of[glPolyShader,glShader])glContext.useProgram(program),glContext.uniformMatrix4fv(glUniformLocation(program,"m"),!1,glTransform);glBatchAdditive=glAdditive,glSetInstancedMode(!0)}previousTarget&&previousTarget!==texture&&glMipmappedTextures.has(previousTarget)&&glMipmapsStale.add(previousTarget)}function glClearRect(x,y,width,height){glEnable&&(glFlush(),glContext.enable(glContext.SCISSOR_TEST),glContext.scissor(x,y,width,height),glContext.clearColor(0,0,0,0),glContext.clear(glContext.COLOR_BUFFER_BIT),glContext.disable(glContext.SCISSOR_TEST))}const glOutlineStrip=[],glOutlinePool=[];function glPolyStrip(points){if(points.length<3)return[];const cross=(a,b,c)=>(b.x-a.x)*(c.y-a.y)-(b.y-a.y)*(c.x-a.x);(poly=>{let area=0;for(let i=poly.length;i--;){var j=(i+1)%poly.length;area+=poly[i].cross(poly[j])}return area})(points)<0&&(points=points.slice().reverse());const e=1e-9,n=points.length;let convex=!0,flips=0,lastSide=0;for(let i=0;convex&&i<n;++i){var a=points[(i+n-1)%n],b=points[i],dx=points[(i+1)%n].x-b.x,a=(convex=cross(a,b,points[(i+1)%n])>-e,dx>e?1:dx<-e?-1:0);a&&a!==lastSide&&(lastSide&&++flips,lastSide=a)}if(convex&&flips<=2){const strip=[points[0]];for(let i=1,j=n-1;i<=j;++i,--j)strip.push(points[i]),i!==j&&strip.push(points[j]);return strip}var indices=[];for(let i=0;i<points.length;++i)indices[i]=i;var triangles=[];let attempts=0;for(var maxAttempts=points.length**2+100;3<indices.length&&attempts++<maxAttempts;){let foundEar=!1;for(let i=0;i<indices.length;i++){var i0=indices[(i+indices.length-1)%indices.length],i1=indices[i],i2=indices[(i+1)%indices.length];const a=points[i0],b=points[i1],c=points[i2];if(!(cross(a,b,c)<e)){let hasInside=!1;for(let j=0;j<indices.length;j++){var k=indices[j];if(k!==i0&&k!==i1&&k!==i2){k=points[k];if(hasInside=((p,a,b,c)=>{var c1=cross(a,b,p),b=cross(b,c,p),c=cross(c,a,p),a=(c1<-e?1:0)+(b<-e?1:0)+(c<-e?1:0),p=(c1>e?1:0)+(b>e?1:0)+(c>e?1:0);return!(a&&p)})(k,a,b,c))break}}if(!hasInside){triangles.push([i0,i1,i2]),indices.splice(i,1),foundEar=!0;break}}}if(!foundEar){let worstIndex=-1,worstValue=1/0;for(let i=0;i<indices.length;i++){const i0=indices[(i+indices.length-1)%indices.length],i1=indices[i],i2=indices[(i+1)%indices.length];var value=abs(cross(points[i0],points[i1],points[i2]));value<worstValue&&(worstValue=value,worstIndex=i)}if(worstIndex<0)break;const i0=indices[(worstIndex+indices.length-1)%indices.length],i1=indices[worstIndex],i2=indices[(worstIndex+1)%indices.length];triangles.push([i0,i1,i2]),indices.splice(worstIndex,1)}}if(3===indices.length&&triangles.push([indices[0],indices[1],indices[2]]),!triangles.length)return[];const strip=[];let[a0,b0,c0]=triangles[0];strip.push(points[a0],points[b0],points[c0]);for(let i=1;i<triangles.length;i++){const[a,b,c]=triangles[i];strip.push(points[c0],points[a]),strip.push(points[a],points[b],points[c]),c0=c}return strip}function drawEngineLogo(t){const blackAndWhite=0;engineUpdateCanvas();const x=mainContext,w=mainCanvasSize.x;var h=mainCanvasSize.y,p3=percent(t,1,.8),p4=percent(t,0,.5),g=x.createRadialGradient(w/2,h/2,0,w/2,h/2,.6*hypot(w,h));g.addColorStop(0,hsl(0,0,lerp(0,p3/2,p4),p3).toString()),g.addColorStop(1,hsl(0,0,0,p3).toString()),x.save(),x.fillStyle=g,x.fillRect(0,0,w,h);const gradient=(X1,Y1,X2,Y2,C,S=1)=>{0<=C?blackAndWhite?x.fillStyle="#fff":((X1=x.fillStyle=x.createLinearGradient(X1,Y1,X2,Y2)).addColorStop(0,color(C,2)),X1.addColorStop(1,color(C,1))):x.fillStyle="#000",-1<=C&&(x.fill(),!S)||x.stroke()};var circle=(X,Y,R,A=0,B=2*PI,C,S)=>{x.beginPath(),x.arc(X,Y,R,p*A,p*B),gradient(X,Y-R,X,Y+R,C,S)},p4=(X,Y,W,H,C)=>{x.beginPath(),x.rect(X,Y,W,H*p),gradient(X,Y+H,X+W,Y,C)},poly=(points,C,Y,H)=>{x.beginPath();for(const p of points)x.lineTo(p.x,p.y);x.closePath(),gradient(0,Y,0,Y+H,C)};const color=(c,l)=>l?`hsl(${360*[.95,.56,.13][c%3]} 99%${[0,50,75][l]}%)`:"#000",p=percent(oscillate(1,1,t),.1,.5);p3=min(6,min(w,h)/99);x.translate(w/2,h/2),x.scale(p3,p3),x.translate(-40,-35),p<1&&x.setLineDash([99*p,99]),x.lineJoin=x.lineCap="round",x.lineWidth=.1+1.9*p;{var s="LittleJS";x.font="900 15.5px arial",x.lineWidth=.1+3.9*p,x.textAlign="center",x.textBaseline="top",p4(11,55,59,8*p,-1),x.beginPath();let w2=0;for(let i=0;i<s.length;++i)w2+=x.measureText(s[i]).width;for(let j=2;j--;)for(let i=0,X=40-w2/2;i<s.length;++i){const w=x.measureText(s[i]).width,X2=X+w/2;gradient(X2,54,X2+2,67,5<i?1:0),x[j?"strokeText":"fillText"](s[i],X2,54.5,17*p),X+=w}x.lineWidth=.1+1.9*p,p4(3,54,73,0)}p4(7,15,26,-7,0),p4(25,15,8,25,-1),p4(10,40,15,-25,1),p4(14,21,7,9,2),p4(38,20,6,-6,2),p4(49,20,10,-6,0),poly([vec2(44,8),vec2(64,8),vec2(59,8+6*p),vec2(49,8+6*p)],2,8,6*p),p4(44,8,20,-7,0);for(let i=5;i--;)circle(59-6*i*p,30,10,0,2*PI,1,0);circle(59,30,4,0,7,2),p4(35,20,24,0),circle(59,30,10),circle(47,30,10,PI/2,3*PI/2),circle(35,30,10,PI/2,3*PI/2),p4(7,40,13,7,-1),p4(17,40,43,14,-1);for(let i=3;i--;)for(let j=2;j--;)circle(17+15*i,47,j?7:1,0,2*PI,2);for(let i=2;i--;){let o=53+6*p*i;poly([vec2(7+o,54),vec2(o,40),vec2(o+6*p,40),vec2(7+o+6*p,54)],0,40,14)}x.restore()}let medalDisplayTime=5,medalDisplaySlideTime=.5,medalDisplaySize=vec2(640,80),medalsPreventUnlock=!1;const medals={};let medalsDisplayQueue=[],medalsSaveName,medalsDisplayTimeLast,medalsRenderAdded;function medalsInit(saveName){const early=[];medalsSaveName||medalsForEach(medal=>medal.unlocked&&medal.isLocal()&&early.push(medal)),medalsSaveName=saveName,medalsLoad(),early.length&&(early.forEach(medal=>medal.unlocked=!0),medalsSave()),void 0!==wavedash&&wavedash?.sendAchievements(),medalsRenderAdded||engineAddPlugin(void 0,medalsRender),medalsRenderAdded=!0}function medalsLoad(){if(medalsSaveName){const saved=readSaveData(medalsSaveName);medalsForEach(medal=>{medal.isLocal()&&(medal.unlocked=!!saved[medal.id]?.unlocked)}),medalsSave()}}function medalsRender(){var medal,elapsed,slideTime,slideOffTime;medalsDisplayQueue.length&&(medal=medalsDisplayQueue[0],elapsed=timeReal-medalsDisplayTimeLast,medalsDisplayTimeLast?elapsed>medalDisplayTime?(medalsDisplayTimeLast=0,medalsDisplayQueue.shift()):(slideTime=min(medalDisplaySlideTime,medalDisplayTime/2),slideOffTime=medalDisplayTime-slideTime,medal.render(elapsed<slideTime?1-elapsed/slideTime:slideOffTime<elapsed?(elapsed-slideOffTime)/slideTime:0)):medalsDisplayTimeLast=timeReal)}function medalsForEach(callback){Object.values(medals).forEach(medal=>callback(medal))}function medalsReset(){if(medalsForEach(medal=>medal.isLocal()&&(medal.unlocked=!1)),medalsSaveName){var saved=readSaveData(medalsSaveName);for(const id in saved)medals[id]&&!medals[id].isLocal()||!saved[id]||"object"!=typeof saved[id]||(saved[id].unlocked=!1);writeSaveData(medalsSaveName,saved)}medalsSave()}function medalsSave(){if(medalsSaveName){const saved=readSaveData(medalsSaveName),data={...saved};medalsForEach(medal=>{var entry;medal.isLocal()?(entry={name:medal.name,description:medal.description,icon:medal.icon,unlocked:medal.unlocked||!!saved[medal.id]?.unlocked},medal.image&&(entry.src=medal.image.src),data[medal.id]=entry):saved[medal.id]&&(data[medal.id]=saved[medal.id])}),writeSaveData(medalsSaveName,data)}}class Medal{constructor(id,name,description="",icon="🏆",src){this.id=id,this.name=name,this.description=description,this.icon=icon,this.unlocked=!1,this.image=void 0,src&&((this.image=new Image).src=src),medals[id]=this,medalsSaveName&&this.isLocal()&&(this.unlocked=!!readSaveData(medalsSaveName)[id]?.unlocked)}unlock(){return medalsPreventUnlock||this.unlocked||(this.unlocked=!0,medalsSave(),medalsDisplayQueue.push(this)),Promise.resolve(this.unlocked)}isLocal(){return!0}render(hidePercent=0){var context=mainContext,width=min(medalDisplaySize.x,mainCanvasSize.x),height=medalDisplaySize.y,x=mainCanvasSize.x-width,hidePercent=-height*hidePercent,backgroundColor=hsl(0,0,.9),backgroundColor=(context.save(),context.beginPath(),context.fillStyle=backgroundColor.toString(),context.strokeStyle=BLACK.toString(),context.lineWidth=3,context.rect(1.5+x,1.5+hidePercent,width-3,height-3),context.fill(),context.stroke(),context.clip(),vec2(.1,.05).scale(height)),iconSize=height-2*backgroundColor.x,nameSize=(this.renderIcon(vec2(x+backgroundColor.x+iconSize/2,hidePercent+height/2),iconSize),.5*height),descriptionSize=.3*height,x=vec2(x+iconSize+2*backgroundColor.x,hidePercent+2*backgroundColor.y+nameSize/2),width=width-iconSize-3*backgroundColor.x;drawTextScreen(this.name,x,nameSize,BLACK,0,void 0,"left",void 0,void 0,width),x.y=hidePercent+height-2*backgroundColor.y-descriptionSize/2,drawTextScreen(this.description,x,descriptionSize,BLACK,0,void 0,"left",void 0,void 0,width),context.restore()}renderIcon(pos,size){var image=this.image;image&&image.complete&&image.naturalWidth?mainContext.drawImage(image,pos.x-size/2,pos.y-size/2,size,size):drawTextScreen(this.icon,pos,.7*size,BLACK)}}function setMedalDisplayTime(time){medalDisplayTime=time}function setMedalDisplaySlideTime(time){medalDisplaySlideTime=time}function setMedalDisplaySize(size){medalDisplaySize=size.copy()}function setMedalsPreventUnlock(preventUnlock){medalsPreventUnlock=preventUnlock}let newgrounds;const newgroundsUnlocksToResend=new Set,newgroundsUnlocksRefused=new Set,newgroundsSecureComponents=["Medal.unlock","ScoreBoard.postScore"],newgroundsSessionErrors=[104,110,111];function newgroundsSessionLost(response){return newgroundsSessionErrors.includes(response?.result?.data?.error?.code??response?.error?.code)}function newgroundsNotifyPage(component,id){globalThis.top?.postMessage(JSON.stringify({ngioComponent:component,id:id}),"*")}class NewgroundsMedal extends Medal{constructor(id,name,description,icon,src){super(id,name,description,icon,src),this.difficulty=void 0,this.value=void 0}isLocal(){return!newgrounds?.session_id}unlock(){if(medalsPreventUnlock||this.unlocked||this.isLocal())return super.unlock();if(newgroundsUnlocksRefused.has(this))return Promise.resolve(!1);const pending=newgrounds.pendingUnlocks;var request;return pending.has(this)?pending.get(this):(request=newgrounds.unlockMedal(this.id).then(response=>{newgroundsSessionLost(response)&&newgrounds.dropSession();var serverMedal=response?.result?.data?.medal;return!serverMedal?.unlocked||medalsPreventUnlock?this.isLocal()?this.unlocked:((!response||serverMedal?.unlocked?newgroundsUnlocksToResend:(pending.delete(this),newgroundsUnlocksRefused)).add(this),!1):((response=newgrounds.medals.find(m=>m.id==this.id))&&Object.assign(response,serverMedal),serverMedal.icon&&serverMedal.icon!=this.image?.src&&((this.image=new Image).src=serverMedal.icon),pending.delete(this),newgroundsNotifyPage("Medal.unlock",this.id),super.unlock())}),pending.set(this,request),request)}}class NewgroundsPlugin{constructor(app_id,cipher){(newgrounds=this).app_id=app_id,this.cipher=cipher,this.cryptoKey=void 0;app_id="undefined"!=typeof location;this.host=app_id?location.hostname:"",this.medals=[],this.scoreboards=[],this.user=null,this.pendingUnlocks=new Map,this.session_id=app_id?new URL(location.href).searchParams.get("ngio_session_id"):null,this.session_id&&medalsForEach(medal=>medal instanceof NewgroundsMedal&&(medal.unlocked=!1)),this.ready=this.init()}async init(){this.call("App.logView",{host:this.host});let medalList;this.session_id&&(session=(session=(await this.call("App.checkSession"))?.result?.data?.session)&&!session.expired&&session.user,(medalList=session&&(await this.call("Medal.getList"))?.result?.data?.medals)&&this.session_id?this.user=session:(this.dropSession(),medalList=void 0)),medalList=medalList||(await this.call("Medal.getList"))?.result?.data?.medals,this.medals=medalList||[];for(const newgroundsMedal of this.medals){var medal=medals[newgroundsMedal.id];medal instanceof NewgroundsMedal&&(medal.image=new Image,medal.image.src=newgroundsMedal.icon,medal.name=newgroundsMedal.name,medal.description=newgroundsMedal.description,medal.unlocked=medal.unlocked||!!newgroundsMedal.unlocked,medal.difficulty=newgroundsMedal.difficulty,medal.value=newgroundsMedal.value,medal.value&&(medal.description+=` (${medal.value})`),this.session_id)&&medal.unlocked&&(newgroundsMedal.unlocked=!0,this.pendingUnlocks.delete(medal),newgroundsUnlocksToResend.delete(medal),newgroundsUnlocksRefused.delete(medal))}var session=await this.call("ScoreBoard.getBoards");if(this.scoreboards=session?.result?.data?.scoreboards||[],this.session_id){const keepAlive=setInterval(async()=>{var response,session;return this.session_id?(session=(response=await this.call("App.checkSession"))?.result?.data?.session,newgroundsSessionLost(response)||session&&(session.expired||!session.user)?this.dropSession():void this.resendUnlocks()):clearInterval(keepAlive)},6e4)}return this}dropSession(){if(this.session_id){var confirmed=Object.values(medals).filter(medal=>medal.unlocked),confirmed=(this.session_id=null,this.user=null,medalsLoad(),confirmed.forEach(medal=>medal.unlocked=!0),medalsSave(),[...this.pendingUnlocks.keys()]);this.pendingUnlocks.clear(),newgroundsUnlocksToResend.clear(),newgroundsUnlocksRefused.clear();for(const medal of confirmed)medal.unlock()}}resendUnlocks(){if(!medalsPreventUnlock){for(const medal of newgroundsUnlocksToResend)this.pendingUnlocks.delete(medal),medal.unlock();newgroundsUnlocksToResend.clear()}}unlockMedal(id){return this.call("Medal.unlock",{id:id})}postScore(id,value){return this.call("ScoreBoard.postScore",{id:id,value:value}).then(response=>(response?.result?.data?.success&&newgroundsNotifyPage("ScoreBoard.postScore",id),newgroundsSessionLost(response)&&this.dropSession(),response))}getScores(id,user,social=!1,skip=0,limit=10,period){var session_id=user||social?this.session_id:null;return this.call("ScoreBoard.getScores",{id:id,user:user,social:social,skip:skip,limit:limit,period:period},session_id)}async encrypt(text){this.cryptoKey||(keyBytes=Uint8Array.from(atob(this.cipher),c=>c.charCodeAt(0)),this.cryptoKey=await crypto.subtle.importKey("raw",keyBytes,"AES-CBC",!1,["encrypt"]));var keyBytes=crypto.getRandomValues(new Uint8Array(16)),text=new Uint8Array(await crypto.subtle.encrypt({name:"AES-CBC",iv:keyBytes},this.cryptoKey,(new TextEncoder).encode(text))),bytes=new Uint8Array(keyBytes.length+text.length);bytes.set(keyBytes),bytes.set(text,keyBytes.length);let binary="";for(const b of bytes)binary+=String.fromCharCode(b);return btoa(binary)}async call(component,parameters,session_id=this.session_id){try{let execute={component:component,parameters:parameters};if(this.cipher&&newgroundsSecureComponents.includes(component)){var secure=await this.encrypt(JSON.stringify(execute)).catch(e=>!1);if(!secure)return{success:!1,error:{code:201,message:"Invalid Encryption: the cipher is not a Base64 AES-128 key"}};execute={secure:secure}}var request={app_id:this.app_id,session_id:session_id,execute:execute},formData=new FormData,signal=(formData.append("request",JSON.stringify(request)),globalThis.AbortSignal?.timeout?.(15e3)),text=await(await fetch("https://www.newgrounds.io/gateway_v3.php",{method:"POST",body:formData,signal:signal})).text();return text?JSON.parse(text):void 0}catch(e){}}}let wavedash;const wavedashDisplayTypes={number:0,seconds:1,milliseconds:2,ticks:3},wavedashSDK=()=>globalThis.Wavedash;function wavedashWait(name,answer){let timer;var limit=new Promise(resolve=>timer=setTimeout(resolve,15e3));return Promise.race([Promise.resolve(answer),limit]).catch(error=>{console.warn("Wavedash "+name+" failed: "+error)}).finally(()=>clearTimeout(timer))}class WavedashMedal extends Medal{constructor(id,achievement,name,description,icon,src){super(id,name,description,icon,src),this.achievement=achievement}unlock(){return medalsPreventUnlock||this.unlocked||!wavedashSDK()?super.unlock():(this.unlocked=!0,medalsSave(),wavedash?.sendAchievements(),Promise.resolve(!0))}}class WavedashPlugin{constructor(leaderboards={}){(wavedash=this).leaderboards=Object.create(null),this.leaderboardSettings=leaderboards,this.achievementsSent=new Set,this.achievementTries=Object.create(null),this.achievementRetry=void 0,wavedashWait("init",this.call("init"));for(const name in leaderboards)this.leaderboard(name);this.sendAchievements()}isActive(){return!!wavedashSDK()}call(name,...args){var sdk=wavedashSDK();if(sdk)try{return sdk[name](...args)}catch(error){console.warn("Wavedash "+name+" failed: "+error)}}leaderboard(name){if(this.leaderboards[name])return this.leaderboards[name];var{lowerWins=!1,display="number"}=(Object.prototype.hasOwnProperty.call(this.leaderboardSettings,name)?this.leaderboardSettings[name]:void 0)||{},known=wavedashDisplayTypes.hasOwnProperty(display),lowerWins=this.call("getOrCreateLeaderboard",name,lowerWins?0:1,known?wavedashDisplayTypes[display]:0);const id=wavedashWait("leaderboard "+name,lowerWins).then(result=>result?.success?result.data?.id:void 0);return id.then(value=>void 0===value&&this.leaderboards[name]===id&&delete this.leaderboards[name]),this.leaderboards[name]=id}async postScore(name,score){return!!this.isActive()&&void 0!==(name=await this.leaderboard(name))&&!!(await wavedashWait("postScore",this.call("uploadLeaderboardScore",name,round(score),!0)))?.success}async getScores(name,offset=0,limit=10,friendsOnly=!1){return this.isActive()&&void 0!==(name=await this.leaderboard(name))&&(name=await wavedashWait("getScores",this.call("listLeaderboardEntries",name,0|offset,0|limit,!!friendsOnly)))?.success?name.data:void 0}sendAchievements(){if(this.isActive()){let refused=!1;medalsForEach(medal=>{var result,medal=medal instanceof WavedashMedal&&medal.unlocked&&medal.achievement;medal&&!this.achievementsSent.has(medal)&&(!0===(result=this.call("setAchievement",medal,!0))||!0===result?.success?this.achievementsSent.add(medal):(this.achievementTries[medal]=(this.achievementTries[medal]||0)+1)<30?refused=!0:(console.warn("Wavedash refused achievement "+medal+" 30 times; is it made, with that identifier?"),this.achievementsSent.add(medal)))}),clearTimeout(this.achievementRetry),this.achievementRetry=refused?setTimeout(()=>this.sendAchievements(),2e3):void 0}}}let postProcess;class PostProcessPlugin{constructor(shaderCode,includeMainCanvas=!1,feedbackTexture=!1){function initPostProcess(){var pLocation;!headlessMode&&glEnable&&(feedbackTexture&&(postProcess.feedbackTexture=glCreateTexture(),glContext.bindTexture(glContext.TEXTURE_2D,postProcess.feedbackTexture),glContext.texImage2D(glContext.TEXTURE_2D,0,glContext.RGBA,1,1,0,glContext.RGBA,glContext.UNSIGNED_BYTE,new Uint8Array([0,0,0,255])),glContext.bindTexture(glContext.TEXTURE_2D,glActiveTexture)),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);}",function(shaderCode,values={}){let declared="";for(const name in values){var value=values[name],value=isArray(value)?value.length:1;declared+=`uniform ${1<value?"vec"+value:"float"} ${name};`}return"#version 300 es\nprecision highp float;uniform sampler2D iChannel0;uniform sampler2D iChannel1;uniform highp sampler2D iChannel2;uniform vec3 iResolution;uniform float iTime;uniform vec3 iDepthRange;"+declared+"out vec4 c;float sceneDepth(vec2 uv){float d=texture(iChannel2,uv).r*2.-1.,n=iDepthRange.x,f=iDepthRange.y;return iDepthRange.z>0.?(d*(f-n)+f+n)/2.:f>0.?2.*n*f/(f+n-d*(f-n)):2.*n/max(1.-d,1e-7);}\n#define LJS_HAS_DEPTH (iDepthRange != vec3(0))\nvoid mainImage(out vec4,vec2);void main(){mainImage(c,gl_FragCoord.xy);c.a=1.;}\n"+shaderCode+"\n"}(postProcess.shaderCode,postProcess.values)),postProcess.valueNames=postProcessValueKey(postProcess.values),postProcess.vao=glContext.createVertexArray(),glContext.bindVertexArray(postProcess.vao),glContext.bindBuffer(glContext.ARRAY_BUFFER,glGeometryBuffer),pLocation=glContext.getAttribLocation(postProcess.shader,"p"),glContext.enableVertexAttribArray(pLocation),glContext.vertexAttribPointer(pLocation,2,glContext.FLOAT,!1,8,0),glContext.bindVertexArray(glPolyMode?glPolyVAO:glInstancedVAO),glContext.bindBuffer(glContext.ARRAY_BUFFER,glArrayBuffer))}(postProcess=this).shaderCode=shaderCode||postProcessEffects(),this.enabled=!0,this.values={},this.valueNames="",this.shader=void 0,this.texture=void 0,this.feedbackTexture=void 0,this.vao=void 0,headlessMode||glEnable||console.warn("PostProcessPlugin: WebGL not enabled!"),engineAddPlugin(void 0,function(){if(!headlessMode&&glEnable&&postProcess.enabled){if(glFlush(),postProcess.shader&&postProcessValueKey(postProcess.values)!==postProcess.valueNames&&postProcess.setShaderCode(postProcess.shaderCode),!postProcess.shader){if(glContext.isContextLost())return;initPostProcess()}if(!glFailedPrograms.has(postProcess.shader)){glContext.bindFramebuffer(glContext.FRAMEBUFFER,null),glContext.useProgram(postProcess.shader),glContext.bindVertexArray(postProcess.vao),glContext.pixelStorei(glContext.UNPACK_FLIP_Y_WEBGL,!0),glContext.pixelStorei(glContext.UNPACK_PREMULTIPLY_ALPHA_WEBGL,!0),glContext.disable(glContext.BLEND),glContext.activeTexture(glContext.TEXTURE0),glContext.bindTexture(glContext.TEXTURE_2D,postProcess.texture),includeMainCanvas?(workCanvas.width!==mainCanvas.width||workCanvas.height!==mainCanvas.height?(workCanvas.width=mainCanvas.width,workCanvas.height=mainCanvas.height):workContext.clearRect(0,0,workCanvas.width,workCanvas.height),glCopyToContext(workContext),workContext.drawImage(mainCanvas,0,0),mainContext.save(),mainContext.setTransform(1,0,0,1,0,0),mainContext.clearRect(0,0,mainCanvas.width,mainCanvas.height),mainContext.restore(),glContext.texImage2D(glContext.TEXTURE_2D,0,glContext.RGBA,glContext.RGBA,glContext.UNSIGNED_BYTE,workCanvas)):glContext.texImage2D(glContext.TEXTURE_2D,0,glContext.RGBA,glContext.RGBA,glContext.UNSIGNED_BYTE,glCanvas),feedbackTexture&&(glContext.activeTexture(glContext.TEXTURE1),glContext.bindTexture(glContext.TEXTURE_2D,postProcess.feedbackTexture));var depth=void 0!==render3D&&render3D?.depthTexture?render3D:void 0,depth=(glContext.activeTexture(glContext.TEXTURE2),glContext.bindTexture(glContext.TEXTURE_2D,depth?.cameraDepthTexture||null),glContext.activeTexture(glContext.TEXTURE0),depth?.camera),uniformLocation=(glContext.uniform3f(glUniformLocation(postProcess.shader,"iDepthRange"),depth?.near||0,depth&&depth.far!=1/0?depth.far:0,depth?.orthographic?1:0),name=>glUniformLocation(postProcess.shader,name));glContext.uniform1i(uniformLocation("iChannel0"),0),glContext.uniform1i(uniformLocation("iChannel1"),1),glContext.uniform1i(uniformLocation("iChannel2"),2),glContext.uniform1f(uniformLocation("iTime"),time),glContext.uniform3f(uniformLocation("iResolution"),mainCanvas.width,mainCanvas.height,1);for(const name in postProcess.values){var value=postProcess.values[name],location=uniformLocation(name);isArray(value)?[,,glContext.uniform2fv,glContext.uniform3fv,glContext.uniform4fv][value.length]?.call(glContext,location,value):glContext.uniform1f(location,value)}glContext.drawArrays(glContext.TRIANGLE_STRIP,0,4),feedbackTexture&&(glContext.activeTexture(glContext.TEXTURE1),glContext.texImage2D(glContext.TEXTURE_2D,0,glContext.RGBA,glContext.RGBA,glContext.UNSIGNED_BYTE,glCanvas),glContext.activeTexture(glContext.TEXTURE0)),glContext.activeTexture(glContext.TEXTURE2),glContext.bindTexture(glContext.TEXTURE_2D,null),glContext.activeTexture(glContext.TEXTURE0),glContext.pixelStorei(glContext.UNPACK_FLIP_Y_WEBGL,!1),glContext.pixelStorei(glContext.UNPACK_PREMULTIPLY_ALPHA_WEBGL,!1),glActiveTexture&&glContext.bindTexture(glContext.TEXTURE_2D,glActiveTexture),glSetInstancedMode(!0)}}},function(){postProcess.shader=void 0,postProcess.texture=void 0,postProcess.feedbackTexture=void 0},function(){initPostProcess()})}setShaderCode(shaderCode){this.shaderCode=shaderCode||postProcessEffects(),this.shader&&!headlessMode&&glContext&&(glContext.deleteProgram(this.shader),glContext.deleteTexture(this.texture),this.feedbackTexture&&glContext.deleteTexture(this.feedbackTexture),glContext.deleteVertexArray(this.vao),this.shader=this.texture=this.feedbackTexture=this.vao=void 0)}}const postProcessValueKey=values=>Object.entries(values).map(([name,v])=>name+":"+(isArray(v)?v.length:1)).join();function postProcessBloomShader(threshold=.6,strength=1,size=6){return postProcessEffects(postProcessGlow(threshold,strength,size))}function postProcessBloom(threshold=.6,strength=1,size=6,includeMainCanvas=!1){return new PostProcessPlugin(postProcessBloomShader(threshold,strength,size),includeMainCanvas)}const postProcessNumber=n=>"string"==typeof n?n:n&&abs(n)<.01?String(n):n.toFixed(4);new Set("c uv p sceneDepth mainImage main attribute const uniform varying layout centroid flat smooth noperspective patch sample break continue do for while switch case default if else subroutine in out inout float double int void bool true false invariant precise discard return lowp mediump highp precision struct common partition active asm class union enum typedef template this resource goto inline noinline public static extern external interface long short half fixed unsigned superp input output filter sizeof cast namespace using coherent volatile restrict readonly writeonly atomic_uint uint mat2 mat3 mat4 vec2 vec3 vec4 ivec2 ivec3 ivec4 bvec2 bvec3 bvec4 uvec2 uvec3 uvec4 radians degrees sin cos tan asin acos atan sinh cosh tanh asinh acosh atanh pow exp log exp2 log2 sqrt inversesqrt abs sign floor trunc round roundEven ceil fract mod modf min max clamp mix step smoothstep isnan isinf length distance dot cross normalize faceforward reflect refract matrixCompMult outerProduct transpose determinant inverse lessThan lessThanEqual greaterThan greaterThanEqual equal notEqual any all not textureSize texture textureProj textureLod textureOffset texelFetch texelFetchOffset textureProjOffset textureLodOffset textureProjLod textureProjLodOffset textureGrad textureGradOffset textureProjGrad textureProjGradOffset dFdx dFdy fwidth".split(" "));function postProcessEffects(...effects){return"void mainImage(out vec4 c, vec2 p)\n{\n vec2 uv = p / iResolution.xy;\n c = texture(iChannel0, uv);\n"+effects.map(e=>" {\n"+e+"\n }\n").join("")+"}\n"}function postProcessGlow(threshold=.6,strength=1,size=6){let code="",taps=0;for(let j=0;j<3;++j){var radius=((j+.5)/3)**.5*size,count=1|max(5+2*j,round(2*radius));taps+=count,code+=`
5
5
  for (int _k = 0; _k < ${count}; ++_k)
6
6
  {
7
7
  float _a = float(_k) * ${(2*PI/count).toFixed(7)}${j?" + "+(2.3999632*j).toFixed(7):""};
@@ -41,4 +41,4 @@ const engineName="LittleJS",engineVersion="1.26.0",frameRate=60;let timeDelta=1/
41
41
  vec4 _line = vec4(${n(color.r)}, ${n(color.g)}, ${n(color.b)}, ${n(color.a)});
42
42
  if (LJS_HAS_DEPTH)
43
43
  c.rgb = mix(c.rgb, _line.rgb, _line.a * step(${n(threshold)}, max(_dx, _dy) / max(abs(_d), 1e-6)));`}function postProcessTV({noise=.1,scanlines=.5,scanlineSpacing=6,glow=.4,vignette=1,curve=0}={}){var parts=[];return curve&&parts.push(postProcessCurve(curve)),glow&&parts.push(postProcessGlow(0,glow,2)),scanlines&&parts.push(postProcessScanlines(scanlines,scanlineSpacing)),noise&&parts.push(postProcessNoise(noise,1)),vignette&&parts.push(postProcessVignette(vignette,6)),parts.map(part=>" {\n"+part+"\n }").join("\n")}let lightSystem;class LightSystemPlugin{constructor(textureSize,ambientColor){(lightSystem=this).enabled=!0,this.shadersFailed=!1,this.ambientColor=(ambientColor||BLACK).copy(),this.textureSize=textureSize?textureSize.copy():void 0,this.textureSizeAuto=!textureSize,this.texture=void 0,this.lightShader=void 0,this.compositeShader=void 0,this.lightVAO=void 0,this.compositeVAO=void 0,this.shadows=!1,this.shadowMapSize=1024,this.shadowMapScale=2,this.shadowTextureSize=256,this.shadowPassCount=16,this.shadowSoftness=.5,this.shadowPass=!1,this.emissivePass=!1,this.shadowMap=void 0,this.backgroundMap=void 0,this.shadowTextureA=void 0,this.shadowTextureB=void 0,this.shadowCopyShader=void 0,this.shadowStretchShader=void 0,this.shadowCopyVAO=void 0,this.shadowStretchVAO=void 0,this.shadowContext=void 0,this.shadowMapOrigin=vec2(),this.shadowMapWorldSize=0,this.directionalLight=void 0,this.directionalTextureSize=512,this.directionalTexture=void 0,this.directionalTextureA=void 0,this.directionalTextureB=void 0,this.directionalTextureSizeAllocated=0,this.backgroundCasters=!1,this.directionalPrograms={},this.shadowMapSizeAllocated=0,this.shadowTextureSizeAllocated=0;const quadVertex="#version 300 es\nprecision highp float;in vec2 p;out vec2 uv;void main(){gl_Position=vec4(p+p-1.,1,1);uv=p;}";function initLightSystem(){headlessMode||(glEnable?(lightSystem.shadowContext||=createCanvasContext(1),lightSystem.textureSizeAuto&&(lightSystem.textureSize=mainCanvasSize.copy()),lightSystem.clampTextureSizes(),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;out vec2 vUV;void main(){vec2 worldP=lightPos+(g-.5)*2.*radius;gl_Position=m*vec4(worldP,1,1);vWorldPos=worldP;vUV=g;}","#version 300 es\nprecision highp float;uniform vec2 lightPos;uniform float radius;uniform float fadeRange;uniform vec4 color;uniform sampler2D shadowTexture;uniform bool useShadow;in vec2 vWorldPos;in vec2 vUV;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.);if(useShadow)c.rgb*=texture(shadowTexture,vUV).rgb;}"),lightSystemShadersFailed(lightSystem.lightShader)||(glContext.useProgram(lightSystem.lightShader),glContext.uniform1i(glUniformLocation(lightSystem.lightShader,"shadowTexture"),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.);}"),lightSystemShadersFailed(lightSystem.compositeShader))||(lightSystem.lightVAO=createQuadVAO(lightSystem.lightShader,"g"),lightSystem.compositeVAO=createQuadVAO(lightSystem.compositeShader,"p"))):console.warn("LightSystemPlugin: WebGL not enabled!"))}function lightSystemShadersFailed(...programs){return!lightSystem.shadersFailed&&programs.some(program=>glFailedPrograms.has(program))&&(console.error("LightSystemPlugin: its shaders did not build on this device, the scene draws without it"),lightSystem.shadersFailed=!0),lightSystem.shadersFailed}function createQuadVAO(program,attribute){var gl=glContext,vao=gl.createVertexArray(),program=(gl.bindVertexArray(vao),gl.bindBuffer(gl.ARRAY_BUFFER,glGeometryBuffer),gl.getAttribLocation(program,attribute));return gl.enableVertexAttribArray(program),gl.vertexAttribPointer(program,2,gl.FLOAT,!1,8,0),vao}function createTexture(size){var gl=glContext,texture=gl.createTexture();return gl.bindTexture(gl.TEXTURE_2D,texture),gl.texImage2D(gl.TEXTURE_2D,0,gl.RGBA,size,size,0,gl.RGBA,gl.UNSIGNED_BYTE,null),gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MAG_FILTER,gl.LINEAR),gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MIN_FILTER,gl.LINEAR),gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_WRAP_S,gl.CLAMP_TO_EDGE),gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_WRAP_T,gl.CLAMP_TO_EDGE),texture}function freeShadowTextures(){var gl=glContext,ls=lightSystem;gl.deleteTexture(ls.shadowMap),gl.deleteTexture(ls.shadowTextureA),gl.deleteTexture(ls.shadowTextureB),gl.deleteTexture(ls.backgroundMap),ls.shadowMap=ls.shadowTextureA=ls.shadowTextureB=ls.backgroundMap=void 0}function freeDirectionalTextures(){var gl=glContext,ls=lightSystem;for(const texture of[ls.directionalTexture,ls.directionalTextureA,ls.directionalTextureB])gl.deleteTexture(texture);ls.directionalTexture=ls.directionalTextureA=ls.directionalTextureB=void 0,ls.directionalTextureSizeAllocated=0}initLightSystem(),engineAddPlugin(void 0,function(){if(!headlessMode&&glEnable&&!lightSystem.shadersFailed&&lightSystem.enabled){if(!lightSystem.texture){if(glContext.isContextLost())return;if(initLightSystem(),!lightSystem.texture)return}glFlush();var prevAdditive=glAdditive;if((lightSystem.shadows||lightSystem.directionalLight)&&!function(){var ls=lightSystem;if(ls.clampTextureSizes(),(!ls.shadowMap||ls.shadowMapSize!==ls.shadowMapSizeAllocated||ls.shadowTextureSize!==ls.shadowTextureSizeAllocated)&&(ls.shadowMap&&freeShadowTextures(),function(){var gl=glContext,ls=lightSystem;ls.shadowMap=createTexture(ls.shadowMapSize),ls.shadowTextureA=createTexture(ls.shadowTextureSize),ls.shadowTextureB=createTexture(ls.shadowTextureSize),ls.shadowMapSizeAllocated=ls.shadowMapSize,ls.shadowTextureSizeAllocated=ls.shadowTextureSize,glActiveTexture&&gl.bindTexture(gl.TEXTURE_2D,glActiveTexture);ls.shadowCopyShader||(ls.shadowCopyShader=glCreateProgram(quadVertex,"#version 300 es\nprecision highp float;uniform sampler2D s;uniform vec2 lightPos;uniform float radius;uniform vec2 mapOrigin;uniform float mapInvSize;uniform float tap;uniform float core;in vec2 uv;out vec4 c;void main(){vec2 w=lightPos+(uv-.5)*2.*radius;vec3 o=vec3(1);for(int i=0;i<4;i++){vec2 m=(w+tap*(vec2(i%2,i/2)*2.-1.)-mapOrigin)*mapInvSize;o=min(o,m==clamp(m,0.,1.)?texture(s,vec2(m.x,1.-m.y)).rgb:vec3(1));}c=vec4(length(w-lightPos)<core?vec3(1):o,1);}"),ls.shadowStretchShader=glCreateProgram(quadVertex,"#version 300 es\nprecision highp float;uniform sampler2D s;uniform float scale;uniform float brightness;in vec2 uv;out vec4 c;void main(){float mask=1.-smoothstep(.35,.525,length(uv-.5));vec3 b=texture(s,(uv-.5)/scale+.5).rgb;c=vec4(min(texture(s,uv).rgb+brightness*mask*b,b),1);}"),lightSystemShadersFailed(ls.shadowCopyShader,ls.shadowStretchShader))||(ls.shadowCopyVAO=createQuadVAO(ls.shadowCopyShader,"p"),ls.shadowStretchVAO=createQuadVAO(ls.shadowStretchShader,"p"),gl.useProgram(ls.shadowCopyShader),gl.uniform1i(glUniformLocation(ls.shadowCopyShader,"s"),1),gl.useProgram(ls.shadowStretchShader),gl.uniform1i(glUniformLocation(ls.shadowStretchShader,"s"),1))}(),ls.shadersFailed))return;var size=ls.shadowMapSize,view=mainCanvasSize.scale(1/cameraScale),view=ls.shadowMapScale*max(view.x,view.y),grid=ls.directionalLight?min(size,ls.directionalTextureSize):size,grid=view/grid,grid=vec2(floor(cameraPos.x/grid)*grid,floor(cameraPos.y/grid)*grid),saved=(ls.shadowMapOrigin=grid.subtract(vec2(view/2)),ls.shadowMapWorldSize=view,[drawContext,mainCanvasSize,cameraPos,cameraScale,cameraAngle,canvasClearColor,glCustomShader]);drawContext=ls.shadowContext,mainCanvasSize=vec2(size),cameraPos=grid,cameraScale=size/view,cameraAngle=0,canvasClearColor=WHITE,glSkipScreenSpace=!0,ls.shadowPass=!0;try{var drawMap=(target,casts)=>{glSetRenderTarget(target,!0);for(const o of engineObjects)!o.destroyed&&casts(o)&&(glColorMask=4278190080,setShader(o.shader),o.renderShadow());glFlush()};(ls.shadows||ls.directionalLight?.castShadow)&&drawMap(ls.shadowMap,o=>o.castShadow),ls.backgroundCasters=!!ls.directionalLight&&engineObjects.some(o=>!o.destroyed&&o.castBackgroundShadow),ls.backgroundCasters&&(ls.backgroundMap||(ls.backgroundMap=createTexture(size),glActiveTexture&&glContext.bindTexture(glContext.TEXTURE_2D,glActiveTexture)),drawMap(ls.backgroundMap,o=>o.castBackgroundShadow))}finally{ls.shadowPass=!1,glColorMask=-1,glSkipScreenSpace=!1,glSetRenderTarget(),[drawContext,mainCanvasSize,cameraPos,cameraScale,cameraAngle,canvasClearColor,glCustomShader]=saved}}(),!lightSystem.shadersFailed){var sun=lightSystem.directionalLight;if(sun?function(){const gl=glContext,ls=lightSystem,light=ls.directionalLight;if(!ls.directionalPrograms.seed&&(function(){const gl=glContext,ls=lightSystem,p=ls.directionalPrograms,header="#version 300 es\nprecision highp float;in vec2 uv;out vec4 c;";var units;p.seed=glCreateProgram(quadVertex,header+"uniform sampler2D f,b;uniform float useF,useB,tap;void main(){vec3 o=vec3(1);for(int i=0;i<4;i++){vec2 m=uv+tap*(vec2(i%2,i/2)*2.-1.);m.y=1.-m.y;vec3 v=mix(vec3(1),texture(f,m).rgb,useF)*mix(vec3(1),texture(b,m).rgb,useB);o=min(o,v);}c=vec4(o,1);}"),p.shadow=glCreateProgram(quadVertex,header+"uniform sampler2D s;uniform vec2 shift;uniform float fade;void main(){vec2 u=uv-shift;vec3 b=u==clamp(u,0.,1.)?texture(s,u).rgb+fade:vec3(1);c=vec4(min(texture(s,uv).rgb,b),1);}"),p.leak=glCreateProgram(quadVertex,header+"uniform sampler2D s;uniform vec2 shift;uniform float fade;void main(){vec2 u=uv-shift;vec3 b=(u==clamp(u,0.,1.)?texture(s,u).rgb:vec3(1))-fade;c=vec4(max(texture(s,uv).rgb,b),1);}"),p.combine=glCreateProgram(quadVertex,header+"uniform sampler2D s,t,f;uniform vec2 rim;uniform float useF,useT;void main(){vec3 l=texture(s,uv).rgb*mix(vec3(1),texture(t,uv).rgb,useT);vec2 u=uv-rim;vec3 up=u==clamp(u,0.,1.)?texture(s,u).rgb*mix(vec3(1),texture(t,u).rgb,useT):vec3(1);vec3 dark=1.-mix(vec3(1),texture(f,vec2(uv.x,1.-uv.y)).rgb,useF);c=vec4(max(l,.8*up*dark),1);}"),p.add=glCreateProgram("#version 300 es\nprecision highp float;uniform mat4 m;uniform vec2 origin;uniform float worldSize;in vec2 g;out vec2 uv;void main(){uv=g*3.-1.;gl_Position=m*vec4(origin+uv*worldSize,1,1);}",header+"uniform sampler2D s;uniform vec4 color;void main(){c=vec4(texture(s,uv).rgb*color.rgb*color.a,1);}"),lightSystemShadersFailed(p.seed,p.shadow,p.leak,p.combine,p.add)||(p.seedVAO=createQuadVAO(p.seed,"p"),p.shadowVAO=createQuadVAO(p.shadow,"p"),p.leakVAO=createQuadVAO(p.leak,"p"),p.combineVAO=createQuadVAO(p.combine,"p"),p.addVAO=createQuadVAO(p.add,"g"),(units=(program,names)=>{gl.useProgram(program),names.forEach((name,i)=>gl.uniform1i(glUniformLocation(program,name),i+1))})(p.seed,["f","b"]),units(p.shadow,["s"]),units(p.leak,["s"]),units(p.combine,["s","t","f"]),units(p.add,["s"]))}(),ls.shadersFailed))return;ls.directionalTexture&&ls.directionalTextureSize===ls.directionalTextureSizeAllocated||(ls.directionalTexture&&freeDirectionalTextures(),function(){var gl=glContext,ls=lightSystem,size=ls.directionalTextureSize;ls.directionalTexture=createTexture(size),ls.directionalTextureA=createTexture(size),ls.directionalTextureB=createTexture(size),ls.directionalTextureSizeAllocated=size,glActiveTexture&&gl.bindTexture(gl.TEXTURE_2D,glActiveTexture)}());const p=ls.directionalPrograms,N=ls.directionalTextureSize,W=ls.shadowMapWorldSize,d=light.sunDirection.normalize(-1),toUV=texels=>vec2(d.x*texels/N,d.y*texels/N),casts=light.castShadow?1:0,cap=ceil(log2(N))+1,target=(gl.bindFramebuffer(gl.FRAMEBUFFER,glFramebuffer),gl.viewport(0,0,N,N),gl.disable(gl.BLEND),texture=>gl.framebufferTexture2D(gl.FRAMEBUFFER,gl.COLOR_ATTACHMENT0,gl.TEXTURE_2D,texture,0)),bind=(unit,texture)=>{gl.activeTexture(gl.TEXTURE0+unit),gl.bindTexture(gl.TEXTURE_2D,texture)},use=name=>(gl.useProgram(p[name]),gl.bindVertexArray(p[name+"VAO"]),p[name]),draw=()=>gl.drawArrays(gl.TRIANGLE_STRIP,0,4),u=glUniformLocation;let program=use("seed"),src=(bind(1,ls.shadowMap),bind(2,ls.backgroundMap||ls.shadowMap),gl.uniform1f(u(program,"useF"),casts),gl.uniform1f(u(program,"useB"),0),gl.uniform1f(u(program,"tap"),.25/N),target(ls.directionalTextureA),draw(),ls.directionalTextureA),dst=ls.directionalTextureB;var L=light.shadowLength*N/W;if(casts&&0<L){program=use("shadow");var passes=clamp(ceil(log2(max(L,1))),1,cap);for(let k=0;k<passes;++k){var shift=toUV(2**k);gl.uniform2f(u(program,"shift"),shift.x,shift.y),gl.uniform1f(u(program,"fade"),2**k/L),bind(1,src),target(dst),draw(),[src,dst]=[dst,src]}}const shadows=src,free=dst,leaks=ls.backgroundCasters;leaks&&(program=use("seed"),bind(1,ls.shadowMap),bind(2,ls.backgroundMap),gl.uniform1f(u(program,"useF"),0),gl.uniform1f(u(program,"useB"),1),target(free),draw());let leakSrc=free,leakDst=ls.directionalTexture;var D=light.backgroundDepth*N/W;if(leaks&&0<D){program=use("leak");var passes=clamp(ceil(log2(max(D,1))),1,cap);for(let k=0;k<passes;++k){var shift=toUV(2**k);gl.uniform2f(u(program,"shift"),shift.x,shift.y),gl.uniform1f(u(program,"fade"),2**k/D),bind(1,leakSrc),target(leakDst),draw(),[leakSrc,leakDst]=[leakDst,leakSrc]}}const textures=[ls.directionalTexture,ls.directionalTextureA,ls.directionalTextureB],built=textures.find(texture=>texture!==shadows&&texture!==leakSrc),rim=(program=use("combine"),bind(1,shadows),bind(2,leakSrc),bind(3,ls.shadowMap),toUV(3));gl.uniform2f(u(program,"rim"),rim.x,rim.y),gl.uniform1f(u(program,"useF"),casts),gl.uniform1f(u(program,"useT"),leaks?1:0),target(built),draw(),[ls.directionalTextureA,ls.directionalTextureB]=textures.filter(texture=>texture!==built),ls.directionalTexture=built,bind(1,null),bind(2,null),bind(3,null),gl.activeTexture(gl.TEXTURE0),glActiveTexture&&gl.bindTexture(gl.TEXTURE_2D,glActiveTexture),gl.bindFramebuffer(gl.FRAMEBUFFER,null),gl.enable(gl.BLEND)}():((lightSystem.directionalTexture||lightSystem.backgroundMap)&&(freeDirectionalTextures(),glContext.deleteTexture(lightSystem.backgroundMap),lightSystem.backgroundMap=void 0,lightSystem.backgroundCasters=!1),!lightSystem.shadows&&lightSystem.shadowMap&&freeShadowTextures()),!lightSystem.shadersFailed){var size=lightSystem.textureSize,wantX=glClampTextureSize(mainCanvasSize.x),wantY=glClampTextureSize(mainCanvasSize.y),size=(!lightSystem.textureSizeAuto||size.x===wantX&&size.y===wantY||(lightSystem.textureSize=vec2(wantX,wantY),glContext.bindTexture(glContext.TEXTURE_2D,lightSystem.texture),glContext.texImage2D(glContext.TEXTURE_2D,0,glContext.RGBA,wantX,wantY,0,glContext.RGBA,glContext.UNSIGNED_BYTE,null),glActiveTexture&&glContext.bindTexture(glContext.TEXTURE_2D,glActiveTexture)),lightSystem.ambientColor),wantX=(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(size.r,size.g,size.b,size.a),glContext.clear(glContext.COLOR_BUFFER_BIT),setAdditiveBlendMode(),glContext.enable(glContext.BLEND),glContext.blendFunc(glContext.ONE,glContext.ONE),lightSystem.lightShader);glContext.useProgram(wantX),glContext.uniformMatrix4fv(glUniformLocation(wantX,"m"),!1,glTransform),glSetInstancedMode(!0),glRenderTargetBase=[lightSystem.texture,lightSystem.textureSize];try{for(const o of engineObjects)o.destroyed||(glAdditive||setAdditiveBlendMode(),o.renderLight());sun&&lightSystem.directionalTexture&&(glFlush(),gl=glContext,p=lightSystem.directionalPrograms,as=p.add,c=sun.color,gl.useProgram(as),gl.bindVertexArray(p.addVAO),gl.uniformMatrix4fv(glUniformLocation(as,"m"),!1,glTransform),gl.uniform2f(glUniformLocation(as,"origin"),lightSystem.shadowMapOrigin.x,lightSystem.shadowMapOrigin.y),gl.uniform1f(glUniformLocation(as,"worldSize"),lightSystem.shadowMapWorldSize),gl.uniform4f(glUniformLocation(as,"color"),c.r,c.g,c.b,c.a),gl.activeTexture(gl.TEXTURE1),gl.bindTexture(gl.TEXTURE_2D,lightSystem.directionalTexture),gl.activeTexture(gl.TEXTURE0),gl.enable(gl.BLEND),gl.blendFunc(gl.ONE,gl.ONE),gl.drawArrays(gl.TRIANGLE_STRIP,0,4),glSetInstancedMode(!0));var gl,p,as,c,level,saved=[drawContext,glCustomShader];drawContext=lightSystem.shadowContext,lightSystem.emissivePass=!0;try{for(const o of engineObjects)!o.destroyed&&0<o.emissive&&(level=255*clamp(o.emissive)+.5|0,glColorMask=4278190080,glColorAdditive=level|level<<8|level<<16,glAdditive||setAdditiveBlendMode(),setShader(o.shader),o.renderEmissive());glFlush()}finally{lightSystem.emissivePass=!1,glColorMask=-1,glColorAdditive=0,[drawContext,glCustomShader]=saved}}finally{glRenderTargetBase=void 0}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);wantY=lightSystem.compositeShader;glContext.uniform1i(glUniformLocation(wantY,"s"),0),glContext.uniform3f(glUniformLocation(wantY,"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),glSetInstancedMode(!0),setAdditiveBlendMode();for(const o of engineObjects)o instanceof Light&&!o.destroyed&&o.renderGlow();glFlush(),setAdditiveBlendMode(prevAdditive)}}}},function(){lightSystem.texture=void 0,lightSystem.lightShader=void 0,lightSystem.compositeShader=void 0,lightSystem.lightVAO=void 0,lightSystem.compositeVAO=void 0,function(){var ls=lightSystem;ls.shadowMap=ls.shadowTextureA=ls.shadowTextureB=ls.backgroundMap=void 0,ls.shadowCopyShader=ls.shadowStretchShader=void 0,ls.shadowCopyVAO=ls.shadowStretchVAO=void 0}(),function(){var ls=lightSystem;ls.directionalTexture=ls.directionalTextureA=ls.directionalTextureB=void 0,ls.directionalPrograms={},ls.directionalTextureSizeAllocated=0}()},function(){lightSystem.shadersFailed=!1,initLightSystem()})}clampTextureSizes(){this.shadowMapSize=glClampTextureSize(this.shadowMapSize),this.shadowTextureSize=glClampTextureSize(this.shadowTextureSize),this.directionalTextureSize=glClampTextureSize(max(1,floor(this.directionalTextureSize)||1));var x,y,size=this.textureSize;size&&(x=glClampTextureSize(size.x),y=glClampTextureSize(size.y),x===size.x&&y===size.y||(this.textureSize=vec2(x,y)))}drawLight(light){var ls,useShadow;!headlessMode&&glEnable&&this.lightShader&&isOnScreen(light.pos,2*light.radius)&&(glFlush(),(useShadow=this.shadows&&light.castShadow&&!!this.shadowMap)&&this.renderLightShadow(light),glContext.useProgram(this.lightShader),glContext.bindVertexArray(this.lightVAO),glContext.enable(glContext.BLEND),glContext.blendFunc(glContext.ONE,glContext.ONE),ls=this.lightShader,glContext.uniform2f(glUniformLocation(ls,"lightPos"),light.pos.x,light.pos.y),glContext.uniform1f(glUniformLocation(ls,"radius"),light.radius),glContext.uniform1f(glUniformLocation(ls,"fadeRange"),light.fadeRange),glContext.uniform1i(glUniformLocation(ls,"useShadow"),useShadow?1:0),useShadow=light.color,glContext.uniform4f(glUniformLocation(ls,"color"),useShadow.r,useShadow.g,useShadow.b,useShadow.a),glContext.drawArrays(glContext.TRIANGLE_STRIP,0,4),glSetInstancedMode(!0))}renderLightShadow(light){var gl=glContext,size=this.shadowTextureSize,cs=(gl.disable(gl.BLEND),gl.viewport(0,0,size,size),gl.activeTexture(gl.TEXTURE1),gl.framebufferTexture2D(gl.FRAMEBUFFER,gl.COLOR_ATTACHMENT0,gl.TEXTURE_2D,this.shadowTextureA,0),this.shadowCopyShader),ss=(gl.useProgram(cs),gl.bindVertexArray(this.shadowCopyVAO),gl.bindTexture(gl.TEXTURE_2D,this.shadowMap),gl.uniform2f(glUniformLocation(cs,"lightPos"),light.pos.x,light.pos.y),gl.uniform1f(glUniformLocation(cs,"radius"),light.radius),gl.uniform2f(glUniformLocation(cs,"mapOrigin"),this.shadowMapOrigin.x,this.shadowMapOrigin.y),gl.uniform1f(glUniformLocation(cs,"mapInvSize"),1/this.shadowMapWorldSize),gl.uniform1f(glUniformLocation(cs,"tap"),light.radius/size),gl.uniform1f(glUniformLocation(cs,"core"),light.shadowCore),gl.drawArrays(gl.TRIANGLE_STRIP,0,4),this.shadowStretchShader);gl.useProgram(ss),gl.bindVertexArray(this.shadowStretchVAO);let src=this.shadowTextureA,dst=this.shadowTextureB;for(let k=0;k<this.shadowPassCount;++k)gl.framebufferTexture2D(gl.FRAMEBUFFER,gl.COLOR_ATTACHMENT0,gl.TEXTURE_2D,dst,0),gl.bindTexture(gl.TEXTURE_2D,src),gl.uniform1f(glUniformLocation(ss,"scale"),1+2*1.5**k/256),gl.uniform1f(glUniformLocation(ss,"brightness"),clamp((10.1-k)/7.2)**2*this.shadowSoftness),gl.drawArrays(gl.TRIANGLE_STRIP,0,4),[src,dst]=[dst,src];gl.bindTexture(gl.TEXTURE_2D,src),gl.activeTexture(gl.TEXTURE0),gl.framebufferTexture2D(gl.FRAMEBUFFER,gl.COLOR_ATTACHMENT0,gl.TEXTURE_2D,this.texture,0),gl.viewport(0,0,this.textureSize.x,this.textureSize.y),gl.enable(gl.BLEND),gl.blendFunc(gl.ONE,gl.ONE)}setShadowTransparent(transparent=!0){this.shadowPass&&(glColorMask=transparent?-1:4278190080)}}class Light extends EngineObject{constructor(pos,radius,color,fadeRange){super(pos,vec2(1),void 0,0,color),this.mass=0,this.radius=radius,this.fadeRange=void 0===fadeRange?radius:fadeRange,this.shadowCore=0,this.glow=0,this.glowFalloff=1,this.glowTileInfo=void 0}render(){}renderLight(){lightSystem&&lightSystem.drawLight(this)}renderGlow(){var size,texture;0<this.glow&&(size=vec2(this.glow),isOnScreen(this.pos,size))&&(texture=engineGlowTexture(this.glowFalloff))&&(this.glowTileInfo?.textureInfo!==texture&&(this.glowTileInfo=new TileInfo(vec2(),texture.size,texture)),drawTile(this.pos,size,this.glowTileInfo,this.color,0,!1,void 0,!0,!1))}}class DirectionalLight extends EngineObject{constructor(sunDirection=vec2(-1,1),color=WHITE){super(vec2(),vec2(),void 0,0,color),this.mass=0,this.sunDirection=sunDirection.copy(),this.shadowLength=20,this.backgroundDepth=3,lightSystem?.directionalLight?.destroy(),lightSystem&&(lightSystem.directionalLight=this)}update(){}render(){}destroy(immediate){lightSystem?.directionalLight===this&&(lightSystem.directionalLight=void 0),super.destroy(immediate)}}class AudioEffect{constructor(mix=1){this.mix=clamp(mix),audioContext&&(this.input=audioContext.createGain(),this.output=audioContext.createGain(),this.dryGain=audioContext.createGain(),this.wetGain=audioContext.createGain(),this.input.connect(this.dryGain).connect(this.output),this.wetGain.connect(this.output),this.setMix(mix),this.output.connect(audioMasterGain))}setMix(mix,fadeTime=0){this.mix=mix=clamp(mix),audioContext&&(this.rampParam(this.dryGain.gain,1-mix,fadeTime),this.rampParam(this.wetGain.gain,mix,fadeTime))}rampParam(param,value,fadeTime=0){if(audioParamRamp(param,value,fadeTime),fadeTime){const keepAlive=new ConstantSourceNode(audioContext,{offset:0});keepAlive.connect(this.input),keepAlive.onended=()=>keepAlive.disconnect(),keepAlive.start(),keepAlive.stop(audioContext.currentTime+fadeTime)}}connect(target){var node;return audioContext&&(node=target&&"input"in target?target.input:target,this.output.disconnect(),this.output.connect(node)),target}disconnect(){this.output?.disconnect()}connectEffect(first,last=first){this.input.connect(first),last.connect(this.wetGain)}}class AudioFilter extends AudioEffect{constructor(type="lowpass",frequency=1e3,q=1,mix=1){super(mix),audioContext&&(this.node=audioContext.createBiquadFilter(),this.node.type=type,this.node.frequency.value=frequency,this.node.Q.value=q,this.connectEffect(this.node))}setFrequency(frequency,fadeTime=0){audioContext&&this.rampParam(this.node.frequency,frequency,fadeTime)}setQ(q,fadeTime=0){audioContext&&this.rampParam(this.node.Q,q,fadeTime)}}class AudioReverb extends AudioEffect{constructor(duration=2,decay=2,mix=.5){super(mix),audioContext&&(this.node=audioContext.createConvolver(),this.setRoom(duration,decay),this.connectEffect(this.node))}setRoom(duration,decay=2){audioContext&&(this.node.buffer=this.createImpulse(duration,decay))}createImpulse(duration,decay){var sampleRate=audioContext.sampleRate,length=max(1,sampleRate*duration|0),buffer=audioContext.createBuffer(2,length,sampleRate);for(let channel=2;channel--;){var samples=buffer.getChannelData(channel);for(let i=length;i--;)samples[i]=rand(-1,1)*(1-i/length)**decay}return buffer}}class AudioDelay extends AudioEffect{constructor(time=.3,feedback=.4,mix=.5){super(mix),audioContext&&(this.node=audioContext.createDelay(5),this.feedbackGain=audioContext.createGain(),this.node.connect(this.feedbackGain).connect(this.node),this.connectEffect(this.node),this.setTime(time),this.setFeedback(feedback))}setTime(time,fadeTime=0){audioContext&&this.rampParam(this.node.delayTime,time,fadeTime)}setFeedback(feedback,fadeTime=0){audioContext&&this.rampParam(this.feedbackGain.gain,clamp(feedback,0,.95),fadeTime)}}class AudioDistortion extends AudioEffect{constructor(amount=.5,mix=1){super(mix),this.amount=clamp(amount),audioContext&&(this.node=audioContext.createWaveShaper(),this.node.oversample="2x",this.setAmount(amount),this.connectEffect(this.node))}setAmount(amount){if(this.amount=amount=clamp(amount),audioContext){var drive=100*amount*amount,curve=new Float32Array(1024);for(let i=1024;i--;){var x=2*i/1023-1;curve[i]=(1+drive)*x/(1+drive*abs(x))}this.node.curve=curve}}}class AudioCompressor extends AudioEffect{constructor(threshold=-24,ratio=12,mix=1){super(mix),audioContext&&(this.node=audioContext.createDynamicsCompressor(),this.node.threshold.value=threshold,this.node.ratio.value=ratio,this.connectEffect(this.node))}setThreshold(threshold,fadeTime=0){audioContext&&this.rampParam(this.node.threshold,threshold,fadeTime)}setRatio(ratio,fadeTime=0){audioContext&&this.rampParam(this.node.ratio,ratio,fadeTime)}}let uiSystem,uiDebug=0,uiActiveReleased=!1;function uiSetDebug(debugMode){uiDebug="boolean"==typeof debugMode?debugMode?1:0:debugMode}class UISystemPlugin{constructor(context=mainContext){function updateTransforms(o){let targetPos,targetSize;targetSize=o.parent?(targetPos=o.parent.nativePos,o.parent.size):(targetPos=uiSystem.screenToNative(mainCanvasSize.scale(.5)),uiSystem.nativeHeight?vec2(mainCanvasSize.x*uiSystem.nativeHeight/mainCanvasSize.y,uiSystem.nativeHeight):mainCanvasSize);var a=o.anchor;o.nativePos=targetPos.add(targetSize.multiply(a).scale(.5)).subtract(o.size.multiply(a).scale(.5)).add(o.localPos)}(uiSystem=this).activateOnPress=!1,this.defaultColor=WHITE,this.defaultLineColor=BLACK,this.defaultTextColor=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.defaultSoundPress=void 0,this.defaultSoundRelease=void 0,this.defaultSoundClick=void 0,this.defaultShadowColor=CLEAR_BLACK,this.defaultShadowBlur=5,this.defaultShadowOffset=vec2(5),this.defaultSlice=void 0,this.defaultHandleSlice=void 0,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=context,this.activeObject=void 0,this.hoverObject=void 0,this.lastHoverObject=void 0,this.confirmDialog=void 0,this._keyInputObject=void 0,this._dragListeners=void 0,this._onKeyDown=e=>{var key,o=this._keyInputObject;o&&uiObjectIsUsable(o)&&!uiObjectIsBehindDialog(o)&&(e.stopPropagation(),key=e.key||"",e.ctrlKey||e.metaKey||e.altKey||1!==key.length&&!/^(Backspace|Delete|Tab|Arrow(Up|Down|Left|Right)|Home|End|Enter|Escape)$/.test(key)||e.preventDefault(),"keydown"===e.type)&&o.onKeyDown(e)},engineAddPlugin(function(){++updatePass;var activeObject=uiSystem.activeObject;activeObject&&!uiObjectIsUsable(activeObject)&&(uiSystem.activeObject=void 0,activeObject.destroyed||activeObject===uiSystem.keyInputObject||activeObject.onRelease());activeObject=uiSystem.keyInputObject;!activeObject||uiObjectIsUsable(activeObject)&&!uiObjectIsBehindDialog(activeObject)||(activeObject instanceof UITextInput&&!activeObject.destroyed?activeObject.stopEditing():uiSystem.keyInputObject=void 0);var activeObject=uiSystem.keyInputObject,clickedOff=activeObject instanceof UITextInput&&mouseWasPressed(0)&&!activeObject.isMouseOverlapping(),activeObject=(clickedOff&&activeObject.stopEditing(),uiSystem.lastHoverObject=uiSystem.hoverObject,uiSystem.hoverObject=void 0,uiSystem.lastHoverObject);activeObject&&!activeObject.destroyed&&function(o){for(;o;o=o.parent)if(!o.visible)return 1;return}(activeObject)&&(uiSystem.lastHoverObject=void 0,activeObject.onLeave());mouseWasPressed(0)&&(uiSystem.navigationMode=!1,uiSystem.navigationObject=void 0);uiSystem.keyInputObject&&(uiSystem.activeObject=uiSystem.keyInputObject,uiSystem.hoverObject=uiSystem.lastHoverObject);activeObject=uiSystem.getNavigableObjects();if(activeObject.length){if(!uiSystem.keyInputObject){if(activeObject.includes(uiSystem.navigationObject)||(uiSystem.navigationObject=void 0),isTouchDevice||uiSystem.navigationMode&&!uiSystem.navigationObject&&(uiSystem.navigationObject=activeObject.find(o=>o.navigationAutoSelect)),!uiSystem.navigationTimer.active()){var newIndex,newIndex,direction=sign(uiSystem.getNavigationDirection());if(direction){let newNavigationObject;uiSystem.navigationObject?(newIndex=mod(activeObject.indexOf(uiSystem.navigationObject)+direction,activeObject.length),newNavigationObject=activeObject[newIndex]):(newNavigationObject=activeObject.find(o=>o.navigationAutoSelect))||(newIndex=0<direction?0:activeObject.length-1,newNavigationObject=activeObject[newIndex]),uiSystem.navigationObject!==newNavigationObject&&(uiSystem.navigationMode=!0,uiSystem.hoverObject=void 0,uiSystem.navigationObject=newNavigationObject,uiSystem.navigationTimer.set(uiSystem.navigationDelay),newNavigationObject.soundPress)&&newNavigationObject.soundPress.play()}}uiSystem.navigationObject&&uiSystem.getNavigationWasPressed()&&(uiSystem.navigationObject.navigatePressed(),isUsingGamepad?inputClearKey(0,gamepadPrimary+1,!1,!0,!1):(inputClearKey("Space",0,!1,!0,!1),inputClearKey("Enter",0,!1,!0,!1)))}}else uiSystem.navigationObject=void 0;var uiObjects=uiSystem.uiObjects,dialog=uiSystem.confirmDialog;dialog&&updateObject(dialog);for(let i=uiObjects.length;i--;){var o=uiObjects[i];o.parent||o===dialog||updateObject(o)}function updateObject(o){if(!o.destroyed&&o.visible&&o.uiUpdatePass!==updatePass){o.uiUpdatePass=updatePass,updateTransforms(o);var children=o.children.slice();for(let i=children.length;i--;)updateObject(children[i]);o.destroyed||o.update()}}clickedOff&&mouseWasPressed(0)&&inputClearKey(0,0),uiSystem.uiObjects=uiSystem.uiObjects.filter(o=>!o.destroyed)},function(){var context=uiSystem.uiContext;{var s;context.save(),uiSystem.nativeHeight&&(s=mainCanvasSize.y/uiSystem.nativeHeight,context.translate(-s*mainCanvasSize.x/2,0),context.scale(s,s),context.translate(mainCanvasSize.x/2/s,0))}function renderObject(o){if(!o.destroyed&&o.visible){updateTransforms(o),o.render();for(const c of o.children)renderObject(c)}}const dialog=uiSystem.confirmDialog;{function renderDebug(o,visible=!0){if(visible&&=!!o.visible,!(o.destroyed||!visible&&uiDebug<2)){updateTransforms(o),o.renderDebug(visible);for(const c of o.children)renderDebug(c,visible)}}uiSystem.uiObjects.forEach(o=>o.parent||o===dialog||renderObject(o)),dialog&&renderObject(dialog),0<uiDebug&&(uiSystem.uiObjects.forEach(o=>o.parent||o===dialog||renderDebug(o)),dialog)&&renderDebug(dialog)}context.restore()});let updatePass=0}drawRect(pos,size,color=WHITE,lineWidth=0,lineColor=BLACK,cornerRadius=0,gradientColor,shadowColor=BLACK,shadowBlur=0,shadowOffset=vec2()){var g,c,context=uiSystem.uiContext;gradientColor?(g=context.createLinearGradient(pos.x,pos.y-size.y/2,pos.x,pos.y+size.y/2),c=color.toString(),g.addColorStop(0,c),g.addColorStop(.5,gradientColor.toString()),g.addColorStop(1,c),context.fillStyle=g):context.fillStyle=color.toString(),uiSetShadow(context,shadowColor,shadowBlur,shadowOffset),context.beginPath(),cornerRadius&&context.roundRect?context.roundRect(pos.x-size.x/2,pos.y-size.y/2,size.x,size.y,cornerRadius):context.rect(pos.x-size.x/2,pos.y-size.y/2,size.x,size.y),context.fill(),context.shadowColor="#0000",lineWidth&&0<lineColor.a&&(context.strokeStyle=lineColor.toString(),context.lineWidth=lineWidth,context.stroke())}drawSlice(slice,pos,size,color=WHITE){0<color.a&&slice.drawScreen(pos,size,color,void 0,0,!1,uiSystem.uiContext)}drawLine(posA,posB,lineWidth=uiSystem.defaultLineWidth,lineColor=uiSystem.defaultLineColor){var context=uiSystem.uiContext;context.strokeStyle=lineColor.toString(),context.lineWidth=lineWidth,context.beginPath(),context.lineTo(posA.x,posA.y),context.lineTo(posB.x,posB.y),context.stroke()}drawTile(pos,size,tileInfo,color=uiSystem.defaultColor,angle=0,mirror=!1,shadowColor=BLACK,shadowBlur=0,shadowOffset=vec2()){var context=uiSystem.uiContext;uiSetShadow(context,shadowColor,shadowBlur,shadowOffset),drawTile(pos,size,tileInfo,color,angle,mirror,CLEAR_BLACK,!1,!0,context),context.shadowColor="#0000"}drawText(text,pos,size,color=uiSystem.defaultColor,lineWidth=uiSystem.defaultLineWidth,lineColor=uiSystem.defaultLineColor,align="center",font=uiSystem.defaultFont,fontStyle="",applyMaxWidth=!0,textShadow=void 0,shadowColor=BLACK,shadowBlur=0,shadowOffset=vec2()){var context=uiSystem.uiContext;textShadow&&0<shadowColor.a&&drawTextScreen(text,pos.add(textShadow),size.y,shadowColor,lineWidth,lineColor,align,font,fontStyle,applyMaxWidth?size.x:void 0,0,context),uiSetShadow(context,shadowColor,shadowBlur,shadowOffset),drawTextScreen(text,pos,size.y,color,lineWidth,lineColor,align,font,fontStyle,applyMaxWidth?size.x:void 0,0,context),context.shadowColor="#0000"}setupDragAndDrop(onDrop,onDragEnter,onDragLeave,onDragOver){if(this._dragListeners)for(var[type,listener]of this._dragListeners)document.removeEventListener(type,listener);this._dragListeners=[];var setCallback=(callback,listenerType,when=()=>!0)=>{var listener=e=>{e.preventDefault(),when()&&callback&&callback(e)};document.addEventListener(listenerType,listener),this._dragListeners.push([listenerType,listener])};let depth=0;setCallback(onDrop,"drop",()=>!(depth=0)),setCallback(onDragEnter,"dragenter",()=>!depth++),setCallback(onDragLeave,"dragleave",()=>!!depth&&!--depth),setCallback(onDragOver,"dragover")}screenToNative(pos){var s,sInv,p;return uiSystem.nativeHeight?(sInv=1/(s=mainCanvasSize.y/uiSystem.nativeHeight),(p=pos.copy()).x+=s*mainCanvasSize.x/2,p.x*=sInv,p.y*=sInv,p.x-=sInv*mainCanvasSize.x/2,p):pos}get keyInputObject(){return this._keyInputObject}set keyInputObject(obj){var had=!!this._keyInputObject;if(this._keyInputObject=obj,!had&&obj){addEventListener("keydown",this._onKeyDown,!0),addEventListener("keyup",this._onKeyDown,!0);var keys=inputData[0];for(const key in keys)isNaN(+key)&&(keys[key]=0);inputKeysHeld.clear();var held=this.activeObject;held&&mouseIsDown(0)&&(this.activeObject=void 0,held.onRelease(),held.soundRelease)&&held.soundRelease.play()}else had&&!obj&&(removeEventListener("keydown",this._onKeyDown,!0),removeEventListener("keyup",this._onKeyDown,!0))}destroyObjects(){for(const o of this.uiObjects)o.parent||o.destroy();this.uiObjects=this.uiObjects.filter(o=>!o.destroyed),this.activeObject=void 0,this.hoverObject=void 0,this.lastHoverObject=void 0,this.keyInputObject=void 0,this.confirmDialog=void 0}getNavigableObjects(){function getNavigableRecursive(o){if(!o.destroyed&&o.visible&&!o.disabled){o.isInteractive()&&void 0!==o.navigationIndex&&objects.push(o);for(const child of o.children)getNavigableRecursive(child)}}const objects=[];if(uiSystem.confirmDialog)getNavigableRecursive(uiSystem.confirmDialog);else for(const o of uiSystem.uiObjects)o.parent||getNavigableRecursive(o);return objects.sort((a,b)=>a.navigationIndex-b.navigationIndex),objects}isMouseOverUI(){function isOverRecursive(o){return!(o.destroyed||!o.visible)&&(!(!o.canBeHover||!o.isMouseOverlapping())||o.children.some(isOverRecursive))}return!!uiSystem.keyInputObject||(uiSystem.confirmDialog?isOverRecursive(uiSystem.confirmDialog):uiSystem.uiObjects.some(o=>!o.parent&&isOverRecursive(o)))}getNavigationDirection(){var stick,dpad,vertical=1===uiSystem.navigationDirection,both=2===uiSystem.navigationDirection;return isUsingGamepad?(stick=gamepadStick(0,gamepadPrimary),dpad=gamepadDpad(gamepadPrimary),both?-(stick.y||dpad.y)||stick.x||dpad.x:vertical?-(stick.y||dpad.y):stick.x||dpad.x):(stick="ArrowUp",both?keyIsDown(stick)||keyIsDown("ArrowLeft")?-1:keyIsDown("ArrowDown")||keyIsDown("ArrowRight")?1:0:(dpad=vertical?"ArrowDown":"ArrowRight",keyIsDown(vertical?stick:"ArrowLeft")?-1:keyIsDown(dpad)?1:0))}getNavigationOtherDirection(){var vertical,stick,dpad;return 2===uiSystem.navigationDirection?0:(vertical=1===uiSystem.navigationDirection,isUsingGamepad?(stick=gamepadStick(0,gamepadPrimary),dpad=gamepadDpad(gamepadPrimary),vertical?stick.x||dpad.x:stick.y||dpad.y):vertical?keyIsDown("ArrowLeft")?-1:keyIsDown("ArrowRight")?1:0:keyIsDown("ArrowUp")?1:keyIsDown("ArrowDown")?-1:0)}getNavigationWasPressed(){return isUsingGamepad?gamepadWasPressed(0,gamepadPrimary):keyWasPressed("Space")||keyWasPressed("Enter")}showConfirmDialog(text="Are you sure?",yesCallback,noCallback,size=vec2(500,250),exitKey="Escape"){if(uiSystem.confirmDialog)return uiSystem.confirmDialog;const savedNavigationDirection=uiSystem.navigationDirection,savedNavigationObject=uiSystem.navigationObject,confirmMenu=(uiSystem.navigationDirection=2,new UIObject(vec2(),size));let opened=!((uiSystem.confirmDialog=confirmMenu).onRender=()=>{var backgroundColor=hsl(0,0,0,.7);uiSystem.drawRect(vec2(),vec2(1e9),backgroundColor)});confirmMenu.onUpdate=()=>{opened&&(keyWasPressed(exitKey)||gamepadWasPressed(1))&&(closeMenu(),noCallback)&&noCallback(),opened=!0},confirmMenu.isMouseOverlapping=()=>!0;var scaleX=size.x/500,scaleY=size.y/250,y=size.y/5,size=new UIText(vec2(0,-y),vec2(size.x-50,70*scaleY),text),text=(confirmMenu.addChild(size),vec2(120*scaleX,70*scaleY)),size=new UIButton(vec2(-80*scaleX,y),text,"Yes"),size=(size.textHeight=40*scaleY,size.navigationIndex=1,size.hoverColor=hsl(0,1,.5),size.onClick=()=>{closeMenu(),yesCallback&&yesCallback()},confirmMenu.addChild(size),new UIButton(vec2(80*scaleX,y),text,"No"));size.textHeight=40*scaleY,size.navigationIndex=2,size.navigationAutoSelect=!0,size.onClick=()=>{closeMenu(),noCallback&&noCallback()},confirmMenu.addChild(size);const destroy=confirmMenu.destroy.bind(confirmMenu);function closeMenu(){confirmMenu.destroy(),inputClear()}return confirmMenu.destroy=()=>{var closing=uiSystem.confirmDialog===confirmMenu;closing&&(uiSystem.confirmDialog=void 0,uiSystem.navigationDirection=savedNavigationDirection),destroy(),closing&&savedNavigationObject&&!savedNavigationObject.destroyed&&(uiSystem.navigationObject=savedNavigationObject)},confirmMenu}}function uiObjectIsUsable(o){for(;o;o=o.parent)if(o.destroyed||!o.visible||o.disabled)return!1;return!0}function uiSetShadow(context,shadowColor,shadowBlur,shadowOffset){0<shadowColor.a&&(shadowBlur||shadowOffset.x||shadowOffset.y)&&(context.shadowColor=shadowColor.toString(),context.shadowBlur=shadowBlur,context.shadowOffsetX=shadowOffset.x,context.shadowOffsetY=shadowOffset.y)}function uiObjectIsBehindDialog(o){var dialog=uiSystem.confirmDialog;if(dialog){for(;o;o=o.parent)if(o===dialog)return;return 1}}function uiObjectIsDisabled(o){for(;o;o=o.parent)if(o.disabled)return!0;return!1}class UIObject{constructor(pos=vec2(),size=vec2()){this.localPos=pos.copy(),this.nativePos=pos.copy(),this.size=size.copy(),this.color=uiSystem.defaultColor.copy(),this.activeColor=void 0,this.text=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.slice=uiSystem.defaultSlice,this.font=uiSystem.defaultFont,this.fontStyle=void 0,this.textWidth=void 0,this.textHeight=void 0,this.textFitScale=uiSystem.defaultTextFitScale,this.textShadow=void 0,this.textLineColor=uiSystem.defaultLineColor.copy(),this.textLineWidth=0,this.visible=!0,this.uiUpdatePass=0,this.children=[],this.parent=void 0,this.extraTouchSize=0,this.soundPress=uiSystem.defaultSoundPress,this.soundRelease=uiSystem.defaultSoundRelease,this.soundClick=uiSystem.defaultSoundClick,this.interactive=!1,this.dragActivate=!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(),this.align="center",this.destroyed=!1,uiSystem.uiObjects.push(this)}addChild(child){return this.children.push(child),child.parent=this,child}removeChild(child){this.children.splice(this.children.indexOf(child),1),child.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=!0,this.parent?.removeChild(this);for(const child of this.children)child.parent=void 0,child.destroy();this.children.length=0}}isMouseOverlapping(){var size,pos;return!!mouseInWindow&&(size=isTouchDevice?this.size.add(vec2(this.extraTouchSize||0)):this.size,pos=uiSystem.screenToNative(mousePosScreen),isOverlapping(this.nativePos,size,pos))}update(){if(this.onUpdate(),!this.destroyed){var disabled=uiObjectIsDisabled(this);if(!(disabled&&this===uiSystem.activeObject&&(uiSystem.activeObject=void 0,this!==uiSystem.keyInputObject)&&(this.onRelease(),this.destroyed)||uiSystem.keyInputObject)){var wasHover=uiSystem.lastHoverObject===this;let isActive=this.isActiveObject();var mouseDown=mouseIsDown(0),mousePress=mouseWasPressed(0)||this.dragActivate&&mouseDown;if(isActive&&mouseWasPressed(0)){if(uiSystem.activateOnPress||!this.interactive||disabled||this.click(),this.destroyed)return;if(uiSystem.activeObject=void 0,isActive=!1,this.onRelease(),this.soundRelease&&this.soundRelease.play(),this.destroyed)return}if(!this.canBeHover||uiSystem.navigationMode||(mousePress||isActive||!mouseDown&&!isTouchDevice)&&!uiSystem.hoverObject&&this.isMouseOverlapping()&&(uiSystem.hoverObject=this),this.isHoverObject()){if(!disabled){if(mousePress&&this.interactive){disabled=!this.dragActivate||!wasHover||mouseWasPressed(0);if(disabled){if(this.onPress(),this.destroyed)return void inputClearKey(0,0,!1,!0,!1);this.soundPress&&this.soundPress.play(),uiSystem.activeObject&&!isActive&&uiSystem.activeObject.onRelease()}if(uiSystem.keyInputObject&&uiSystem.keyInputObject!==this?(this.onRelease(),this.soundRelease&&this.soundRelease.play()):(uiSystem.activeObject=this,uiActiveReleased=!mouseDown),disabled&&uiSystem.activateOnPress&&this.click(!this.soundPress),this.destroyed)return void inputClearKey(0,0,!1,!0,!1)}if(uiSystem.activateOnPress||!mouseDown&&isActive&&this.isActiveObject()&&this.interactive&&(mouseWasReleased(0)||uiActiveReleased)&&this.click(),this.destroyed)return}mousePress&&inputClearKey(0,0,!1,!0,!1)}!isActive||mouseDown&&(!this.dragActivate||this.isHoverObject())||(this.onRelease(),this.soundRelease&&this.soundRelease.play(),uiSystem.activeObject=void 0),this.isHoverObject()!==wasHover&&(this.isHoverObject()?this.onEnter():this.onLeave())}}}render(){var lineColor,disabled,isNavigationObject;this.onRender(),this.size.x&&this.size.y&&(isNavigationObject=this.isNavigationObject(),disabled=this.interactive?uiObjectIsDisabled(this):this.disabled,lineColor=isNavigationObject||this.interactive&&this.isActiveObject()&&!disabled?this.color:this.lineColor,disabled=isNavigationObject?this.hoverColor:disabled?this.disabledColor:this.interactive?this.isActiveObject()?this.activeColor||this.hoverColor:this.isHoverObject()?this.hoverColor:this.color:this.color,isNavigationObject=this.lineWidth*(isNavigationObject?1.5:1),this.slice?uiSystem.drawSlice(this.slice,this.nativePos,this.size,disabled):uiSystem.drawRect(this.nativePos,this.size,disabled,isNavigationObject,lineColor,this.cornerRadius,this.gradientColor,this.shadowColor||CLEAR_BLACK,this.shadowBlur,this.shadowOffset))}getTextSize(){var lines=this.textHeight?1:function(text){let count=1;for(let i=-1;0<=(i=text.indexOf("\n",i+1));)++count;return count}(this.text+"");return vec2(this.textWidth||this.textFitScale*this.size.x,this.textHeight||this.textFitScale*this.size.y/lines)}getTextPos(textSize){var side="left"===this.align?-1:"right"===this.align?1:0;return this.nativePos.add(vec2(side*textSize.x/2,0))}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&&uiObjectIsUsable(this)}toString(){let text="type = "+this.constructor.name;return this.text&&(text+="\ntext = "+this.text),(this.nativePos.x||this.nativePos.y)&&(text+="\nnativePos = "+this.nativePos),(this.localPos.x||this.localPos.y)&&(text+="\nlocalPos = "+this.localPos),(this.size.x||this.size.y)&&(text+="\nsize = "+this.size),this.color&&(text+="\ncolor = "+this.color),text}renderDebug(visible=!0){visible=visible?this.isHoverObject()?YELLOW:uiObjectIsDisabled(this)?PURPLE:this.interactive?RED:BLUE:GREEN;uiSystem.drawRect(this.nativePos,this.size,CLEAR_BLACK,4,visible)}click(playSound=!0){this.onClick(),playSound&&this.soundClick&&this.soundClick.play()}onUpdate(){}onRender(){}onEnter(){}onLeave(){}onPress(){}onRelease(){}onClick(){}onChange(){}onKeyDown(e){}}class UIText extends UIObject{constructor(pos,size,text="",align="center",font=uiSystem.defaultFont){super(pos,size),this.text=text,this.align=align,this.font=font,this.canBeHover=!1,this.color=CLEAR_BLACK.copy(),this.shadowColor=CLEAR_BLACK.copy(),this.gradientColor=void 0,this.lineWidth=0,this.textFitScale=1}render(){super.render();var textSize=this.getTextSize();uiSystem.drawText(this.text,this.getTextPos(textSize),textSize,this.textColor,this.textLineWidth,this.textLineColor,this.align,this.font,this.fontStyle,!0,this.textShadow,this.shadowColor||CLEAR_BLACK,this.shadowBlur,this.shadowOffset)}}class UITextInput extends UIObject{constructor(pos,size,text=""){super(pos,size),this.maxLength=0,this.text=text+"",this.interactive=!0,this.canBeHover=!0}click(playSound=!0){var editing=uiSystem.keyInputObject;editing!==this&&editing instanceof UITextInput&&editing.stopEditing(),uiSystem.keyInputObject=this,inputClearKey(0,gamepadPrimary+1,!1,!0,!1),this.onClick(),playSound&&this.soundClick&&this.soundClick.play()}stopEditing(){this.isKeyInputObject()&&(this.soundRelease&&this.soundRelease.play(),uiSystem.activeObject=void 0,uiSystem.keyInputObject=void 0,this.onChange())}onKeyDown(e){var code=e.code,key=e.key||"";e.repeat&&("Enter"===key||"Space"===code)||(this.text+="","Backspace"===key?this.text=textGraphemes(this.text).slice(0,-1).join(""):"Enter"===key||"Escape"===key?this.stopEditing():1!==key.length||(e.ctrlKey||e.metaKey)&&!e.getModifierState?.("AltGraph")||this.maxLength&&!(textGraphemes(this.text).length<this.maxLength)||(this.text+=key))}update(){super.update(),this.isKeyInputObject()&&(gamepadWasPressed(0,gamepadPrimary)?(this.stopEditing(),inputClearKey(0,gamepadPrimary+1,!1,!0,!1)):mouseWasPressed(0)&&inputClearKey(0,0,!1,!0,!1))}render(){super.render();var textSize=this.getTextSize();let text=this.text;this.isKeyInputObject()&&(text+=timeReal%1<.5?"█":"░"),uiSystem.drawText(text,this.getTextPos(textSize),textSize,this.textColor,this.textLineWidth,this.textLineColor,this.align,this.font,this.fontStyle,!0,this.textShadow)}}class UITile extends UIObject{constructor(pos,size,tileInfo,color=WHITE,angle=0,mirror=!1){super(pos,size),this.tileInfo=tileInfo,this.angle=angle,this.mirror=mirror,this.color=color.copy(),this.shadowColor=CLEAR_BLACK.copy()}render(){this.onRender(),uiSystem.drawTile(this.nativePos,this.size,this.tileInfo,this.color,this.angle,this.mirror,this.shadowColor||CLEAR_BLACK,this.shadowBlur,this.shadowOffset)}}class UIButton extends UIObject{constructor(pos,size,text="",color=uiSystem.defaultButtonColor){super(pos,size),this.textOffset=vec2(),this.text=text,this.color=color.copy(),this.interactive=!0}render(){super.render();var textSize=this.getTextSize();uiSystem.drawText(this.text,this.getTextPos(textSize).add(this.textOffset),textSize,this.textColor,this.textLineWidth,this.textLineColor,this.align,this.font,this.fontStyle,!0,this.textShadow)}}class UICheckbox extends UIObject{constructor(pos,size,checked=!1,text="",color=uiSystem.defaultButtonColor){super(pos,size),this.checked=checked,this.text=text,this.color=color.copy(),this.interactive=!0}click(playSound=!0){this.checked=!this.checked,super.click(playSound),this.onChange()}render(){super.render(),this.checked&&(length=lerp(1,2**.5/2,this.cornerRadius/min(this.size.x,this.size.y)*2)/2,length=this.size.scale(length),uiSystem.drawLine(this.nativePos.add(length.multiply(vec2(-1))),this.nativePos.add(length.multiply(vec2(1))),this.lineWidth,this.lineColor),uiSystem.drawLine(this.nativePos.add(length.multiply(vec2(-1,1))),this.nativePos.add(length.multiply(vec2(1,-1))),this.lineWidth,this.lineColor));var length=this.getTextSize(),pos=this.nativePos.add(vec2(this.size.x,0));uiSystem.drawText(this.text,pos,length,this.textColor,this.textLineWidth,this.textLineColor,"left",this.font,this.fontStyle,!1,this.textShadow)}}class UISlider extends UIObject{constructor(pos,size,value=.5,text="",color=uiSystem.defaultButtonColor,handleColor=WHITE){super(pos,size),this.value=value,this.handleColor=handleColor.copy(),this.handleSlice=uiSystem.defaultHandleSlice,this.fillMode=!1,this.text=text,this.color=color.copy(),this.interactive=!0}update(){var oldValue,handleSize,centerPos,barSize,wasActive=this.isActiveObject();super.update(),this.interactive&&(oldValue=this.value,wasActive||this.isActiveObject()?(handleSize=(wasActive=this.size.x>this.size.y)?this.size.y:this.size.x,barSize=wasActive?this.size.x:this.size.y,handleSize=(centerPos=wasActive?this.nativePos.x:this.nativePos.y)-(barSize=barSize-handleSize)/2,centerPos=centerPos+barSize/2,barSize=uiSystem.screenToNative(mousePosScreen),this.value=wasActive?percent(barSize.x,handleSize,centerPos):percent(barSize.y,centerPos,handleSize)):this.isNavigationObject()&&(wasActive=uiSystem.getNavigationOtherDirection(),uiSystem.navigationTimer.active()||(this.value=clamp(this.value+.01*wasActive))),this.value!==oldValue)&&this.onChange()}drawHandle(pos,size,color){var slice=this.handleSlice||this.slice;slice?uiSystem.drawSlice(slice,pos,size,color):uiSystem.drawRect(pos,size,color,this.lineWidth,this.lineColor,this.cornerRadius,this.gradientColor)}render(){super.render();var isHorizontal=this.size.x>this.size.y,barWidth=isHorizontal?this.size.x:this.size.y,handleWidth=isHorizontal?this.size.y:this.size.x,color=uiObjectIsDisabled(this)?this.disabledColor:this.handleColor;if(this.fillMode){var progressWidth=lerp(min(handleWidth,2*this.cornerRadius),barWidth,this.value),p=(progressWidth-barWidth)*(isHorizontal?.5:-.5),p=this.nativePos.add(isHorizontal?vec2(p,0):vec2(0,p)),progressWidth=isHorizontal?vec2(progressWidth,this.size.y):vec2(this.size.x,progressWidth);this.drawHandle(p,progressWidth,color)}else{const p=(barWidth-handleWidth)*(clamp(isHorizontal?this.value:1-this.value)-.5),pos=this.nativePos.add(isHorizontal?vec2(p,0):vec2(0,p)),drawSize=vec2(handleWidth);this.drawHandle(pos,drawSize,color)}p=this.getTextSize();uiSystem.drawText(this.text,this.getTextPos(p),p,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(pos,size,src,autoplay=!1,loop=!1,volume=1){super(pos,size||vec2()),this.color=BLACK.copy(),this.cornerRadius=0,this.volume=volume,this.video=document.createElement("video"),this.video.loop=loop,this.video.volume=clamp(volume*soundVolume),this.video.muted=!soundEnable,this._soundEnabled=soundEnable,this.video.style.display="none",this.video.playsInline=!0,this.video.src=src,document.body.appendChild(this.video),autoplay&&this.play()}async play(){try{return await this.video.play(),!0}catch(e){return!1}}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(volume){this.volume=volume,this.video.volume=clamp(volume*soundVolume)}setPlaybackRate(rate){this.video.playbackRate=rate}getCurrentTime(){return this.video.currentTime||0}getDuration(){return this.video.duration||0}getVideoSize(){return vec2(this.video.videoWidth,this.video.videoHeight)}setTime(time){var duration=this.video.duration;this.video.currentTime=duration?clamp(time,0,duration):max(time,0)}update(){super.update(),this.video.volume=clamp(this.volume*soundVolume),this._soundEnabled!==soundEnable&&(this.video.muted=!(this._soundEnabled=soundEnable))}render(){var context,s;super.render(),this.isLoading()||(context=uiSystem.uiContext,s=this.size,context.save(),context.translate(this.nativePos.x,this.nativePos.y),context.drawImage(this.video,-s.x/2,-s.y/2,s.x,s.y),context.restore())}destroy(){this.destroyed||(this.video.pause(),this.video.removeAttribute?.("src"),this.video.load?.(),this.video.remove(),super.destroy())}}class UILayout extends UIObject{constructor(pos,columns=1,gap=10,padding=10,transparent=!1){super(pos),this.columns=columns,this.gap=gap,this.padding=padding,transparent&&(this.color=CLEAR_BLACK.copy(),this.gradientColor=void 0,this.lineWidth=0,this.shadowColor=CLEAR_BLACK.copy(),this.canBeHover=!1),this.relayout()}addChild(child){return super.addChild(child),this.relayout(),child}removeChild(child){super.removeChild(child),this.relayout()}relayout(){var children=this.children.filter(child=>child.visible),n=children.length;if(n){var cols=clamp(floor(this.columns),1,n),rows=ceil(n/cols),colWidths=new Array(cols).fill(0),rowHeights=new Array(rows).fill(0);for(let i=0;i<n;++i){var col=i%cols,row=floor(i/cols),child=children[i];colWidths[col]=max(colWidths[col],child.size.x),rowHeights[row]=max(rowHeights[row],child.size.y)}let contentWidth=this.gap*(cols-1);for(const w of colWidths)contentWidth+=w;let contentHeight=this.gap*(rows-1);for(const h of rowHeights)contentHeight+=h;var colOffsets=new Array(cols);let xAcc=0;for(let c=0;c<cols;++c)colOffsets[c]=xAcc,xAcc+=colWidths[c];var rowOffsets=new Array(rows);let yAcc=0;for(let r=0;r<rows;++r)rowOffsets[r]=yAcc,yAcc+=rowHeights[r];for(let i=0;i<n;++i){const col=i%cols,row=floor(i/cols);var x=-contentWidth/2+colOffsets[col]+this.gap*col+colWidths[col]/2,y=-contentHeight/2+rowOffsets[row]+this.gap*row+rowHeights[row]/2;children[i].localPos=vec2(x,y)}this.size=vec2(contentWidth+2*this.padding,contentHeight+2*this.padding)}else this.size=vec2(2*this.padding);this.parent instanceof UILayout&&this.parent.relayout()}}let box2d,box2dDebug=!1,box2dTempVectors;function box2dTemp(v,slot=0){slot=(box2dTempVectors||=[new box2d.instance.b2Vec2,new box2d.instance.b2Vec2])[slot];return slot.Set(v.x,v.y),slot}const box2dQueryObjects={},box2dGravity={x:NaN,y:NaN};function box2dQueryObject(key,type){return box2dQueryObjects[key]||=new box2d.instance[type]}function box2dFixtureOverlaps(fixture,aabb){var shape=fixture.GetShape(),transform=fixture.GetBody().GetTransform(),shapeBox=box2dQueryObject("shapeAABB","b2AABB"),lower=aabb.get_lowerBound(),upper=aabb.get_upperBound();for(let i=shape.GetChildCount();i--;){shape.ComputeAABB(shapeBox,transform,i);var a=shapeBox.get_lowerBound(),b=shapeBox.get_upperBound();if(a.get_x()<=upper.get_x()&&b.get_x()>=lower.get_x()&&a.get_y()<=upper.get_y()&&b.get_y()>=lower.get_y())return 1}}function box2dEdgePoints(points,loop){var kept=[];for(const p of points)(!kept.length||25e-6<p.distanceSquared(kept[kept.length-1]))&&kept.push(p);for(;loop&&1<kept.length&&kept[kept.length-1].distanceSquared(kept[0])<=25e-6;)kept.pop();return kept}function box2dWakeWithContacts(body){body.SetAwake(!0);for(let edge=body.GetContactList();!box2d.isNull(edge);edge=edge.get_next())edge.get_other().SetAwake(!0)}const box2dFinite=(value,otherwise)=>isFinite(value)?value:otherwise;function box2dWakeJoint(joint){joint.GetBodyA().SetAwake(!0),joint.GetBodyB().SetAwake(!0)}function box2dCenterInertia(mass,x,y){var f=Math.fround;return f(f(mass)*f(f(f(x)*f(x))+f(f(y)*f(y))))}const box2dPending=[];let box2dPendingBusy=0;function box2dWhenUnlocked(f){box2dPending.push(f),box2d.world.IsLocked()||box2dPendingBusy||box2dRunPending()}function box2dRunPending(){++box2dPendingBusy;try{for(;box2dPending.length;)box2dPending.shift()()}finally{--box2dPendingBusy}}const box2dJoints=new Map,box2dGearJoints=new Set;function box2dDestroyGears(joint){for(const gear of box2dGearJoints)gear.box2dJoint?gear.joint1!==joint&&gear.joint2!==joint||(box2dGearJoints.delete(gear),gear.destroy()):box2dGearJoints.delete(gear)}function box2dSetDebug(enable){box2dDebug=enable}const box2dUnusedFields={velocity:"setLinearVelocity",angleVelocity:"setAngularVelocity",damping:"setLinearDamping",angleDamping:"setAngularDamping",mass:"setMass or the density of its shapes",friction:"the friction of its shapes, given to addBox and the rest",restitution:"the restitution of its shapes, given to addBox and the rest"},box2dUnusedWarned=new Set,box2dUnusedStarts=new WeakMap;function box2dUnusedWarn(name,use){box2dUnusedWarned.has(name)||(box2dUnusedWarned.add(name),console.warn("Box2dObject."+name+" does nothing on a Box2D body, use "+use))}class Box2dObject extends EngineObject{constructor(pos=vec2(),size=vec2(),tileInfo,angle=0,color,bodyType=box2d.bodyTypeDynamic,renderOrder=0){super(pos,size,tileInfo,angle,color,renderOrder);size=new box2d.instance.b2BodyDef;size.set_type(bodyType),size.set_position(box2dTemp(pos)),size.set_angle(-angle),this.body=box2d.world.CreateBody(size),box2d.instance.destroy(size),this.lineColor=BLACK.copy(),this.lineWidth=.1,this.edgeLists=[],this.edgeLoops=[],this.edgeListFixtures=new Map,this.body.object=this,box2d.objects.push(this),debug&&box2dUnusedStarts.set(this,function(o){var values=[];for(const name in box2dUnusedFields){var value=o[name];value instanceof Vector2?values.push(value.x,value.y):values.push(value)}return values.push(o.gravityScale),values}(this))}destroy(immediate=!1){if(!this.destroyed){const body=this.body;box2dWhenUnlocked(()=>{this.body===body&&(box2d.world.DestroyBody(body),body.object=void 0,this.body=void 0)}),super.destroy(immediate)}}updatePhysics(){}updateTransforms(updateChildren=!0){super.updateTransforms(updateChildren)}render(){this.tileInfo?super.render():this.drawFixtures(this.color,this.lineColor,this.lineWidth)}renderDebugInfo(){var isAsleep=!this.getIsAwake(),isAsleep=rgb(isAsleep?1:0,isAsleep?1:0,this.getBodyType()===box2d.bodyTypeStatic?1:0,.5);this.drawFixtures(isAsleep)}drawFixtures(color=WHITE,lineColor=BLACK,lineWidth=.1,useWebGL,context){const edgeFixtures=this.edgeListFixtures;this.getFixtureList().forEach(fixture=>{edgeFixtures.has(box2d.instance.getPointer(fixture))||box2d.drawFixture(fixture,this.pos,this.angle,color,lineColor,lineWidth,useWebGL,context)}),this.edgeLists.forEach(points=>drawLineList(points,lineWidth,lineColor,!1,this.pos,this.angle,useWebGL,!1,context)),this.edgeLoops.forEach(points=>drawLineList(points,lineWidth,lineColor,!0,this.pos,this.angle,useWebGL,!1,context))}beginContact(otherObject,fixture,otherFixture){}endContact(otherObject,fixture,otherFixture){}addShape(shape,density=1,friction=.2,restitution=0,isSensor=!1){var fd=new box2d.instance.b2FixtureDef,shape=(fd.set_shape(shape),fd.set_density(density),fd.set_friction(friction),fd.set_restitution(restitution),fd.set_isSensor(isSensor),this.body.CreateFixture(fd));return box2d.instance.destroy(fd),shape}addBox(size=vec2(1),offset=vec2(),angle=0,density,friction,restitution,isSensor){var shape;if(1e-6<size.x*size.y&&isFinite(size.x*size.y))return(shape=new box2d.instance.b2PolygonShape).SetAsBox(size.x/2,size.y/2,box2dTemp(offset),-angle),size=this.addShape(shape,density,friction,restitution,isSensor),box2d.instance.destroy(shape),size}addPoly(points,density,friction,restitution,isSensor){points=function(points){let area=0;if(3<=(points=points.filter((p,i)=>points.slice(0,i).every(q=>1e-6<p.distanceSquared(q)))).length&&points.length<=8)for(const a of points)for(const b of points)for(const c of points)area=max(area,abs(b.subtract(a).cross(c.subtract(a))));if(1e-6<=area){var buffer=box2d.instance._malloc(8*points.length);for(let i=0,offset=0;i<points.length;++i)box2d.instance.HEAPF32[buffer+offset>>2]=points[i].x,offset+=4,box2d.instance.HEAPF32[buffer+offset>>2]=points[i].y,offset+=4;var box2dPoints=box2d.instance.wrapPointer(buffer,box2d.instance.b2Vec2),shape=new box2d.instance.b2PolygonShape;return shape.Set(box2dPoints,points.length),box2d.instance._free(buffer),shape}}(points);if(points)return density=this.addShape(points,density,friction,restitution,isSensor),box2d.instance.destroy(points),density}addRegularPoly(diameter=1,sides=8,density,friction,restitution,isSensor){var points=[],radius=diameter/2;for(let i=sides=min(sides,8);0<i--;)points.push(vec2(radius,0).rotate((i+.5)/sides*PI*2));return this.addPoly(points,density,friction,restitution,isSensor)}addRandomPoly(diameter=1,density,friction,restitution,isSensor){var sides=randInt(3,9),points=[],radius=diameter/2;for(let i=sides;i--;)points.push(vec2(rand(radius/2,1.5*radius),0).rotate(i/sides*PI*2));return this.addPoly(points,density,friction,restitution,isSensor)}addCircle(diameter=1,offset=vec2(),density,friction,restitution,isSensor){var shape;if(isFinite(diameter))return(shape=new box2d.instance.b2CircleShape).set_m_p(box2dTemp(offset)),shape.set_m_radius(diameter/2),offset=this.addShape(shape,density,friction,restitution,isSensor),box2d.instance.destroy(shape),offset}addEdge(point1,point2,density,friction,restitution,isSensor){var shape=new box2d.instance.b2EdgeShape,point1=(shape.Set(box2dTemp(point1),box2dTemp(point2,1)),this.addShape(shape,density,friction,restitution,isSensor));return box2d.instance.destroy(shape),point1}addEdgeList(points,density,friction,restitution,isSensor){points=box2dEdgePoints(points);const fixtures=[],edgePoints=[];if(!(points.length<2)){for(let i=0;i<points.length-1;++i){var shape=new box2d.instance.b2EdgeShape,f=(points[i-1]&&shape.set_m_vertex0(box2dTemp(points[i-1])),points[i+0]&&shape.set_m_vertex1(box2dTemp(points[i+0])),points[i+1]&&shape.set_m_vertex2(box2dTemp(points[i+1])),points[i+2]&&shape.set_m_vertex3(box2dTemp(points[i+2])),shape.set_m_hasVertex0(!!points[i-1]),shape.set_m_hasVertex3(!!points[i+2]),this.addShape(shape,density,friction,restitution,isSensor));box2d.instance.destroy(shape),fixtures.push(f),edgePoints.push(points[i].copy())}edgePoints.push(points[points.length-1].copy()),this.edgeLists.push(edgePoints),fixtures.forEach(f=>this.edgeListFixtures.set(box2d.instance.getPointer(f),edgePoints))}return fixtures}addEdgeLoop(points,density,friction,restitution,isSensor){points=box2dEdgePoints(points,!0);const fixtures=[],edgePoints=[];if(!(points.length<2)){var getPoint=i=>points[mod(i,points.length)];for(let i=0;i<points.length;++i){var shape=new box2d.instance.b2EdgeShape,f=(shape.set_m_vertex0(box2dTemp(getPoint(i-1))),shape.set_m_vertex1(box2dTemp(getPoint(i+0))),shape.set_m_vertex2(box2dTemp(getPoint(i+1))),shape.set_m_vertex3(box2dTemp(getPoint(i+2))),shape.set_m_hasVertex0(!0),shape.set_m_hasVertex3(!0),this.addShape(shape,density,friction,restitution,isSensor));box2d.instance.destroy(shape),fixtures.push(f),edgePoints.push(points[i].copy())}this.edgeLoops.push(edgePoints),fixtures.forEach(f=>this.edgeListFixtures.set(box2d.instance.getPointer(f),edgePoints))}return fixtures}destroyFixture(fixture){const pointer=box2d.instance.getPointer(fixture),edgeFixtures=this.edgeListFixtures,points=edgeFixtures.get(pointer);points&&(this.edgeLists=this.edgeLists.filter(p=>p!==points),this.edgeLoops=this.edgeLoops.filter(p=>p!==points),edgeFixtures.forEach((p,pointer)=>p===points&&edgeFixtures.delete(pointer))),box2dWhenUnlocked(()=>this.body&&this.getFixtureList().some(f=>box2d.instance.getPointer(f)===pointer)&&this.body.DestroyFixture(fixture))}destroyAllFixtures(){this.edgeLists=[],this.edgeLoops=[],this.edgeListFixtures.clear();const fixtures=this.getFixtureList(),getPointer=box2d.instance.getPointer;box2dWhenUnlocked(()=>{if(this.body){var alive=new Set(this.getFixtureList().map(getPointer));for(const fixture of fixtures)alive.has(getPointer(fixture))&&this.body.DestroyFixture(fixture)}})}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(){var center=this.body.GetLocalCenter();return max(0,Math.fround(this.body.GetInertia()-box2dCenterInertia(this.getMass(),center.get_x(),center.get_y())))}getIsAwake(){return this.body.IsAwake()}getBodyType(){return this.body.GetType()}getSpeed(){return this.getLinearVelocity().length()}setTransform(pos,angle){this.pos.set(pos.x,pos.y),this.angle=angle;const x=pos.x,y=pos.y;box2dWhenUnlocked(()=>{this.body&&(this.body.SetTransform(box2dTemp(vec2(x,y)),-angle),box2dWakeWithContacts(this.body))})}setPosition(pos){this.pos.set(pos.x,pos.y);const x=pos.x,y=pos.y;box2dWhenUnlocked(()=>{this.body&&(this.body.SetTransform(box2dTemp(vec2(x,y)),this.body.GetAngle()),box2dWakeWithContacts(this.body))})}setAngle(angle){this.angle=angle,box2dWhenUnlocked(()=>{this.body&&(this.body.SetTransform(this.body.GetPosition(),-angle),box2dWakeWithContacts(this.body))})}setLinearVelocity(velocity){this.body.SetLinearVelocity(box2dTemp(velocity))}setAngularVelocity(angularVelocity){this.body.SetAngularVelocity(-angularVelocity)}setLinearDamping(damping){this.body.SetLinearDamping(damping)}setAngularDamping(damping){this.body.SetAngularDamping(damping)}setGravityScale(scale=1){this.body.SetGravityScale(this.gravityScale=scale),this.body.SetAwake(!0)}setBullet(isBullet=!0){this.body.SetBullet(isBullet)}setAwake(isAwake=!0){this.body.SetAwake(isAwake)}setBodyType(type){box2dWhenUnlocked(()=>this.body&&this.body.SetType(type))}setSleepingAllowed(isAllowed=!0){this.body.SetSleepingAllowed(isAllowed)}setFixedRotation(isFixed=!0){this.body.SetFixedRotation(isFixed),this.body.SetAwake(!0)}setCenterOfMass(center){this.setMassData(center)}setMass(mass){this.setMassData(void 0,mass)}setMomentOfInertia(momentOfInertia){this.setMassData(void 0,void 0,momentOfInertia)}resetMassData(){box2dWhenUnlocked(()=>this.body&&this.body.ResetMassData())}setMassData(localCenter,mass,momentOfInertia){localCenter=localCenter&&localCenter.copy(),box2dWhenUnlocked(()=>{var data,cx,cy,f,center,oldMass,I;this.body&&(data=box2dQueryObject("massData","b2MassData"),this.body.GetMassData(data),center=data.get_center(),oldMass=data.get_mass(),cx=localCenter?localCenter.x:center.get_x(),cy=localCenter?localCenter.y:center.get_y(),center=(f=Math.fround)(data.get_I()-box2dCenterInertia(oldMass,center.get_x(),center.get_y())),I=f((center=momentOfInertia??center)+(oldMass=box2dCenterInertia(0<(mass=box2dFinite(mass??oldMass,oldMass))?mass:1,cx,cy))),data.set_mass(mass),data.set_center(box2dTemp(vec2(cx,cy))),data.set_I(0<center&&0<f(I-oldMass)?I:0),this.body.SetMassData(data),this.body.SetAwake(!0))})}setFilterData(categoryBits=1,ignoreCategoryBits=0,groupIndex=0){this.getFixtureList().forEach(fixture=>{var filter=fixture.GetFilterData();filter.set_categoryBits(categoryBits),filter.set_maskBits(65535&~ignoreCategoryBits),filter.set_groupIndex(groupIndex),fixture.SetFilterData(filter)})}setSensor(isSensor=!0){this.getFixtureList().forEach(f=>f.SetSensor(isSensor)),box2dWakeWithContacts(this.body)}applyForce(force,pos){pos||=this.getCenterOfMass(),this.setAwake(),this.body.ApplyForce(box2dTemp(force),box2dTemp(pos,1))}applyAcceleration(acceleration,pos){pos||=this.getCenterOfMass(),this.setAwake();acceleration=acceleration.scale(this.getMass());this.body.ApplyLinearImpulse(box2dTemp(acceleration),box2dTemp(pos,1))}applyImpulse(impulse,pos){pos||=this.getCenterOfMass(),this.setAwake(),this.body.ApplyLinearImpulse(box2dTemp(impulse),box2dTemp(pos,1))}applyTorque(torque){this.setAwake(),this.body.ApplyTorque(-torque)}applyAngularAcceleration(acceleration){this.getBodyType()!==box2d.bodyTypeDynamic||this.body.IsFixedRotation()||(this.setAwake(),this.setAngularVelocity(this.getAngularVelocity()+acceleration))}applyAngularImpulse(impulse){this.setAwake(),this.body.ApplyAngularImpulse(-impulse)}hasFixtures(){return!box2d.isNull(this.body.GetFixtureList())}getFixtureList(){var fixtures=[];for(let fixture=this.body.GetFixtureList();!box2d.isNull(fixture);)fixtures.push(fixture),fixture=fixture.GetNext();return fixtures}hasJoints(){return 0<this.getJointList().length}getJointList(){var joints=[];for(let edge=this.body.GetJointList();!box2d.isNull(edge);edge=edge.get_next()){var joint=edge.get_joint(),wrapper=box2dJoints.get(box2d.instance.getPointer(joint));wrapper&&!wrapper.box2dJoint||joints.push(wrapper||box2d.castJointObject(joint))}return joints}}class Box2dStaticObject extends Box2dObject{constructor(pos,size,tileInfo,angle=0,color,renderOrder=0){super(pos,size,tileInfo,angle,color,box2d.bodyTypeStatic,renderOrder)}}class Box2dKinematicObject extends Box2dObject{constructor(pos,size,tileInfo,angle=0,color,renderOrder=0){super(pos,size,tileInfo,angle,color,box2d.bodyTypeKinematic,renderOrder)}}class Box2dTileLayer extends Box2dStaticObject{constructor(tileLayer){super(tileLayer.pos,tileLayer.size),this.tileLayer=tileLayer,this.addChild(tileLayer),this.buildCollision()}render(){}buildCollision(friction=.2,restitution=0){this.destroyAllFixtures();const processed=[],getIndex=(x,y)=>x+y*this.size.x;var isSolidUnprocessed=(x,y)=>!processed[getIndex(x,y)]&&0<this.tileLayer.getCollisionData(vec2(x,y));for(let x=0;x<this.size.x;++x)for(let y=0;y<this.size.y;++y)if(isSolidUnprocessed(x,y)){let width=1,height=1,canExpand=!0;for(;isSolidUnprocessed(x+width,y);)++width;for(;canExpand;){for(let checkX=0;checkX<width;++checkX)if(!isSolidUnprocessed(x+checkX,y+height)){canExpand=!1;break}canExpand&&++height}for(let rectX=width;rectX--;)for(let rectY=height;rectY--;)processed[getIndex(x+rectX,y+rectY)]=!0;var shapeSize=vec2(width,height),offset=vec2(x+width/2,y+height/2);this.addBox(shapeSize,offset,0,0,friction,restitution)}}}class Box2dRaycastResult{constructor(fixture,point,normal,fraction){this.object=fixture.GetBody().object,this.fixture=fixture,this.point=point,this.normal=normal,this.fraction=fraction}}class Box2dJoint{constructor(jointDef){var bodyA=jointDef.get_bodyA(),bodyB=jointDef.get_bodyB(),bothLive=!box2d.isNull(bodyA)&&!box2d.isNull(bodyB),bodyA=!bothLive||box2d.instance.getPointer(bodyA)!==box2d.instance.getPointer(bodyB);this.box2dJoint=0,bothLive&&bodyA?(this.box2dJoint=box2d.castJointObject(box2d.world.CreateJoint(jointDef)),box2d.instance.destroy(jointDef),box2dJoints.set(box2d.instance.getPointer(this.box2dJoint),this)):box2d.instance.destroy(jointDef)}destroy(){const joint=this.box2dJoint;if(joint){this.box2dJoint=0,box2dDestroyGears(this);const pointer=box2d.instance.getPointer(joint);box2dWhenUnlocked(()=>{box2dJoints.get(pointer)===this&&(box2dJoints.delete(pointer),box2d.world.DestroyJoint(joint))})}}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(time=timeDelta){return box2d.vec2From(this.box2dJoint.GetReactionForce(1/time))}getReactionTorque(time=timeDelta){return-this.box2dJoint.GetReactionTorque(1/time)}getCollideConnected(){return this.box2dJoint.GetCollideConnected()}isActive(){return!!this.box2dJoint&&this.box2dJoint.IsActive()}isDestroyed(){return!this.box2dJoint}}class Box2dTargetJoint extends Box2dJoint{constructor(object,fixedObject,worldPos){object.setAwake();var jointDef=new box2d.instance.b2MouseJointDef;jointDef.set_bodyA(fixedObject.body),jointDef.set_bodyB(object.body),jointDef.set_target(box2dTemp(worldPos)),jointDef.set_maxForce(2e3*object.getMass()),super(jointDef)}setTarget(pos){this.box2dJoint.SetTarget(box2dTemp(pos))}getTarget(){return box2d.vec2From(this.box2dJoint.GetTarget())}setMaxForce(force){this.box2dJoint.SetMaxForce(force),box2dWakeJoint(this.box2dJoint)}getMaxForce(){return this.box2dJoint.GetMaxForce()}setFrequency(hz){this.box2dJoint.SetFrequency(max(box2dFinite(hz,0),.001)),box2dWakeJoint(this.box2dJoint)}getFrequency(){return this.box2dJoint.GetFrequency()}}class Box2dDistanceJoint extends Box2dJoint{constructor(objectA,objectB,anchorA,anchorB,collide=!1){anchorA||=box2d.vec2From(objectA.body.GetPosition()),anchorB||=box2d.vec2From(objectB.body.GetPosition());var localAnchorA=objectA.worldToLocal(anchorA),localAnchorB=objectB.worldToLocal(anchorB),jointDef=new box2d.instance.b2DistanceJointDef;jointDef.set_bodyA(objectA.body),jointDef.set_bodyB(objectB.body),jointDef.set_localAnchorA(box2dTemp(localAnchorA)),jointDef.set_localAnchorB(box2dTemp(localAnchorB)),jointDef.set_length(anchorA.distance(anchorB)),jointDef.set_collideConnected(collide),super(jointDef)}getLocalAnchorA(){return box2d.vec2From(this.box2dJoint.GetLocalAnchorA())}getLocalAnchorB(){return box2d.vec2From(this.box2dJoint.GetLocalAnchorB())}setLength(length){this.box2dJoint.SetLength(length),box2dWakeJoint(this.box2dJoint)}getLength(){return this.box2dJoint.GetLength()}setFrequency(hz){this.box2dJoint.SetFrequency(hz),box2dWakeJoint(this.box2dJoint)}getFrequency(){return this.box2dJoint.GetFrequency()}setDampingRatio(ratio){this.box2dJoint.SetDampingRatio(ratio),box2dWakeJoint(this.box2dJoint)}getDampingRatio(){return this.box2dJoint.GetDampingRatio()}}class Box2dRopeJoint extends Box2dJoint{constructor(objectA,objectB,anchorA,anchorB,extraLength=0,collide=!1){anchorA||=box2d.vec2From(objectA.body.GetPosition()),anchorB||=box2d.vec2From(objectB.body.GetPosition());var localAnchorA=objectA.worldToLocal(anchorA),localAnchorB=objectB.worldToLocal(anchorB),jointDef=new box2d.instance.b2RopeJointDef;jointDef.set_bodyA(objectA.body),jointDef.set_bodyB(objectB.body),jointDef.set_localAnchorA(box2dTemp(localAnchorA)),jointDef.set_localAnchorB(box2dTemp(localAnchorB)),jointDef.set_maxLength(anchorA.distance(anchorB)+extraLength),jointDef.set_collideConnected(collide),super(jointDef)}getLocalAnchorA(){return box2d.vec2From(this.box2dJoint.GetLocalAnchorA())}getLocalAnchorB(){return box2d.vec2From(this.box2dJoint.GetLocalAnchorB())}setMaxLength(length){this.box2dJoint.SetMaxLength(length),box2dWakeJoint(this.box2dJoint)}getMaxLength(){return this.box2dJoint.GetMaxLength()}}class Box2dRevoluteJoint extends Box2dJoint{constructor(objectA,objectB,anchor,collide=!1){anchor||=box2d.vec2From(objectB.body.GetPosition());var localAnchorA=objectA.worldToLocal(anchor),anchor=objectB.worldToLocal(anchor),jointDef=new box2d.instance.b2RevoluteJointDef;jointDef.set_bodyA(objectA.body),jointDef.set_bodyB(objectB.body),jointDef.set_localAnchorA(box2dTemp(localAnchorA)),jointDef.set_localAnchorB(box2dTemp(anchor)),jointDef.set_referenceAngle(objectB.body.GetAngle()-objectA.body.GetAngle()),jointDef.set_collideConnected(collide),super(jointDef)}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(enable=!0){this.box2dJoint.EnableLimit(enable)}getLowerLimit(){return-this.box2dJoint.GetUpperLimit()}getUpperLimit(){return-this.box2dJoint.GetLowerLimit()}setLimits(min,max){min=box2dFinite(min,0),(max=box2dFinite(max,0))<min&&([min,max]=[max,min]),this.box2dJoint.SetLimits(-max,-min)}isMotorEnabled(){return this.box2dJoint.IsMotorEnabled()}enableMotor(enable=!0){this.box2dJoint.EnableMotor(enable)}setMotorSpeed(speed){this.box2dJoint.SetMotorSpeed(-speed)}getMotorSpeed(){return-this.box2dJoint.GetMotorSpeed()}setMaxMotorTorque(torque){this.box2dJoint.SetMaxMotorTorque(torque)}getMaxMotorTorque(){return this.box2dJoint.GetMaxMotorTorque()}getMotorTorque(time=timeDelta){return-this.box2dJoint.GetMotorTorque(1/time)}}class Box2dPinJoint extends Box2dRevoluteJoint{constructor(objectA,objectB,pos=objectA.pos,collide=!1){super(objectA,objectB,pos,collide)}}class Box2dGearJoint extends Box2dJoint{constructor(objectA,objectB,joint1,joint2,ratio=1){var ratioSign=joint1 instanceof Box2dRevoluteJoint==joint2 instanceof Box2dRevoluteJoint?1:-1,jointDef=new box2d.instance.b2GearJointDef;jointDef.set_bodyA(objectA.body),jointDef.set_bodyB(objectB.body),jointDef.set_joint1(joint1.box2dJoint),jointDef.set_joint2(joint2.box2dJoint),jointDef.set_ratio(ratio*ratioSign),super(jointDef),this.joint1=joint1,this.joint2=joint2,this.ratioSign=ratioSign,box2dGearJoints.add(this)}getJoint1(){return this.joint1}getJoint2(){return this.joint2}setRatio(ratio){this.box2dJoint.SetRatio(ratio*this.ratioSign),box2dWakeJoint(this.box2dJoint)}getRatio(){return this.box2dJoint.GetRatio()*this.ratioSign}}class Box2dPrismaticJoint extends Box2dJoint{constructor(objectA,objectB,anchor,worldAxis=vec2(0,1),collide=!1){anchor||=box2d.vec2From(objectB.body.GetPosition());var localAnchorA=objectA.worldToLocal(anchor),anchor=objectB.worldToLocal(anchor),worldAxis=objectA.worldToLocalVector(worldAxis),jointDef=new box2d.instance.b2PrismaticJointDef;jointDef.set_bodyA(objectA.body),jointDef.set_bodyB(objectB.body),jointDef.set_localAnchorA(box2dTemp(localAnchorA)),jointDef.set_localAnchorB(box2dTemp(anchor)),jointDef.set_localAxisA(box2dTemp(worldAxis)),jointDef.set_referenceAngle(objectB.body.GetAngle()-objectA.body.GetAngle()),jointDef.set_collideConnected(collide),super(jointDef)}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(enable=!0){this.box2dJoint.EnableLimit(enable)}getLowerLimit(){return this.box2dJoint.GetLowerLimit()}getUpperLimit(){return this.box2dJoint.GetUpperLimit()}setLimits(min,max){min=box2dFinite(min,0),(max=box2dFinite(max,0))<min&&([min,max]=[max,min]),this.box2dJoint.SetLimits(min,max)}isMotorEnabled(){return this.box2dJoint.IsMotorEnabled()}enableMotor(enable=!0){this.box2dJoint.EnableMotor(enable)}setMotorSpeed(speed){this.box2dJoint.SetMotorSpeed(speed)}getMotorSpeed(){return this.box2dJoint.GetMotorSpeed()}setMaxMotorForce(force){this.box2dJoint.SetMaxMotorForce(force)}getMaxMotorForce(){return this.box2dJoint.GetMaxMotorForce()}getMotorForce(time=timeDelta){return this.box2dJoint.GetMotorForce(1/time)}}class Box2dWheelJoint extends Box2dJoint{constructor(objectA,objectB,anchor,worldAxis=vec2(0,1),collide=!1){anchor||=box2d.vec2From(objectB.body.GetPosition());var localAnchorA=objectA.worldToLocal(anchor),anchor=objectB.worldToLocal(anchor),worldAxis=objectA.worldToLocalVector(worldAxis).normalize(),jointDef=new box2d.instance.b2WheelJointDef;jointDef.set_bodyA(objectA.body),jointDef.set_bodyB(objectB.body),jointDef.set_localAnchorA(box2dTemp(localAnchorA)),jointDef.set_localAnchorB(box2dTemp(anchor)),jointDef.set_localAxisA(box2dTemp(worldAxis)),jointDef.set_collideConnected(collide),super(jointDef)}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(enable=!0){this.box2dJoint.EnableMotor(enable)}setMotorSpeed(speed){this.box2dJoint.SetMotorSpeed(-speed)}getMotorSpeed(){return-this.box2dJoint.GetMotorSpeed()}setMaxMotorTorque(torque){this.box2dJoint.SetMaxMotorTorque(torque)}getMaxMotorTorque(){return this.box2dJoint.GetMaxMotorTorque()}getMotorTorque(time=timeDelta){return-this.box2dJoint.GetMotorTorque(1/time)}setSpringFrequencyHz(hz){this.box2dJoint.SetSpringFrequencyHz(hz),box2dWakeJoint(this.box2dJoint)}getSpringFrequencyHz(){return this.box2dJoint.GetSpringFrequencyHz()}setSpringDampingRatio(ratio){this.box2dJoint.SetSpringDampingRatio(ratio),box2dWakeJoint(this.box2dJoint)}getSpringDampingRatio(){return this.box2dJoint.GetSpringDampingRatio()}}class Box2dWeldJoint extends Box2dJoint{constructor(objectA,objectB,anchor,collide=!1){anchor||=box2d.vec2From(objectB.body.GetPosition());var localAnchorA=objectA.worldToLocal(anchor),anchor=objectB.worldToLocal(anchor),referenceAngle=objectB.body.GetAngle()-objectA.body.GetAngle(),jointDef=new box2d.instance.b2WeldJointDef;jointDef.set_bodyA(objectA.body),jointDef.set_bodyB(objectB.body),jointDef.set_localAnchorA(box2dTemp(localAnchorA)),jointDef.set_localAnchorB(box2dTemp(anchor)),jointDef.set_referenceAngle(referenceAngle),jointDef.set_collideConnected(collide),super(jointDef),this.referenceAngle=-referenceAngle}getLocalAnchorA(){return box2d.vec2From(this.box2dJoint.GetLocalAnchorA())}getLocalAnchorB(){return box2d.vec2From(this.box2dJoint.GetLocalAnchorB())}getReferenceAngle(){return this.referenceAngle}setFrequency(hz){this.box2dJoint.SetFrequency(hz),box2dWakeJoint(this.box2dJoint)}getFrequency(){return this.box2dJoint.GetFrequency()}setDampingRatio(ratio){this.box2dJoint.SetDampingRatio(ratio),box2dWakeJoint(this.box2dJoint)}getDampingRatio(){return this.box2dJoint.GetDampingRatio()}}class Box2dFrictionJoint extends Box2dJoint{constructor(objectA,objectB,anchor,collide=!1){anchor||=box2d.vec2From(objectB.body.GetPosition());var localAnchorA=objectA.worldToLocal(anchor),anchor=objectB.worldToLocal(anchor),jointDef=new box2d.instance.b2FrictionJointDef;jointDef.set_bodyA(objectA.body),jointDef.set_bodyB(objectB.body),jointDef.set_localAnchorA(box2dTemp(localAnchorA)),jointDef.set_localAnchorB(box2dTemp(anchor)),jointDef.set_collideConnected(collide),super(jointDef)}getLocalAnchorA(){return box2d.vec2From(this.box2dJoint.GetLocalAnchorA())}getLocalAnchorB(){return box2d.vec2From(this.box2dJoint.GetLocalAnchorB())}setMaxForce(force){this.box2dJoint.SetMaxForce(max(box2dFinite(force,0),0))}getMaxForce(){return this.box2dJoint.GetMaxForce()}setMaxTorque(torque){this.box2dJoint.SetMaxTorque(max(box2dFinite(torque,0),0))}getMaxTorque(){return this.box2dJoint.GetMaxTorque()}}class Box2dPulleyJoint extends Box2dJoint{constructor(objectA,objectB,groundAnchorA,groundAnchorB,anchorA,anchorB,ratio=1,collide=!1){anchorA||=box2d.vec2From(objectA.body.GetPosition()),anchorB||=box2d.vec2From(objectB.body.GetPosition());var localAnchorA=objectA.worldToLocal(anchorA),localAnchorB=objectB.worldToLocal(anchorB),jointDef=new box2d.instance.b2PulleyJointDef;jointDef.set_bodyA(objectA.body),jointDef.set_bodyB(objectB.body),jointDef.set_groundAnchorA(box2dTemp(groundAnchorA)),jointDef.set_groundAnchorB(box2dTemp(groundAnchorB)),jointDef.set_localAnchorA(box2dTemp(localAnchorA)),jointDef.set_localAnchorB(box2dTemp(localAnchorB)),jointDef.set_ratio(box2dFinite(ratio,1)||1),jointDef.set_lengthA(groundAnchorA.distance(anchorA)),jointDef.set_lengthB(groundAnchorB.distance(anchorB)),jointDef.set_collideConnected(collide),super(jointDef)}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(objectA,objectB){var linearOffset=objectA.worldToLocal(box2d.vec2From(objectB.body.GetPosition())),angularOffset=objectB.body.GetAngle()-objectA.body.GetAngle(),jointDef=new box2d.instance.b2MotorJointDef;jointDef.set_bodyA(objectA.body),jointDef.set_bodyB(objectB.body),jointDef.set_linearOffset(box2dTemp(linearOffset)),jointDef.set_angularOffset(angularOffset),super(jointDef)}setLinearOffset(offset){this.box2dJoint.SetLinearOffset(box2dTemp(offset))}getLinearOffset(){return box2d.vec2From(this.box2dJoint.GetLinearOffset())}setAngularOffset(offset){this.box2dJoint.SetAngularOffset(-offset)}getAngularOffset(){return-this.box2dJoint.GetAngularOffset()}setMaxForce(force){this.box2dJoint.SetMaxForce(max(box2dFinite(force,0),0)),box2dWakeJoint(this.box2dJoint)}getMaxForce(){return this.box2dJoint.GetMaxForce()}setMaxTorque(torque){this.box2dJoint.SetMaxTorque(max(box2dFinite(torque,0),0)),box2dWakeJoint(this.box2dJoint)}getMaxTorque(){return this.box2dJoint.GetMaxTorque()}setCorrectionFactor(factor){this.box2dJoint.SetCorrectionFactor(clamp(factor)),box2dWakeJoint(this.box2dJoint)}getCorrectionFactor(){return this.box2dJoint.GetCorrectionFactor()}}class Box2dPlugin{constructor(instance){(box2d=this).instance=instance,this.world=new box2d.instance.b2World,this.objects=[],this.velocityIterations=8,this.positionIterations=3,this.bodyTypeStatic=instance.b2_staticBody,this.bodyTypeKinematic=instance.b2_kinematicBody,this.bodyTypeDynamic=instance.b2_dynamicBody;instance=new box2d.instance.JSDestructionListener,instance.SayGoodbyeJoint=function(jointPointer){var joint=box2dJoints.get(jointPointer);joint&&(box2dDestroyGears(joint),joint.box2dJoint=0),box2dJoints.delete(jointPointer)},instance.SayGoodbyeFixture=function(){},box2d.world.SetDestructionListener(instance),instance=new box2d.instance.JSContactListener;instance.BeginContact=function(contactPtr){var contactPtr=box2d.instance.wrapPointer(contactPtr,box2d.instance.b2Contact),fixtureA=contactPtr.GetFixtureA(),contactPtr=contactPtr.GetFixtureB(),objectA=fixtureA.GetBody().object,objectB=contactPtr.GetBody().object;objectA&&objectB&&(objectA.beginContact(objectB,fixtureA,contactPtr),objectB.beginContact(objectA,contactPtr,fixtureA))},instance.EndContact=function(contactPtr){var contactPtr=box2d.instance.wrapPointer(contactPtr,box2d.instance.b2Contact),fixtureA=contactPtr.GetFixtureA(),contactPtr=contactPtr.GetFixtureB(),objectA=fixtureA.GetBody().object,objectB=contactPtr.GetBody().object;objectA&&objectB&&(objectA.endContact(objectB,fixtureA,contactPtr),objectB.endContact(objectA,contactPtr,fixtureA))},instance.PreSolve=function(){},instance.PostSolve=function(){},box2d.world.SetContactListener(instance)}step(frames=1){if(gravity.x!==box2dGravity.x||gravity.y!==box2dGravity.y){box2dGravity.x=gravity.x,box2dGravity.y=gravity.y,box2d.world.SetGravity(box2dTemp(gravity));for(let b=box2d.world.GetBodyList();!box2d.isNull(b);b=b.GetNext())b.SetAwake(!0)}for(let i=frames;i--;)box2d.world.Step(timeDelta,this.velocityIterations,this.positionIterations),box2dRunPending();this.objects=this.objects.filter(o=>!o.destroyed)}raycastAll(start,end,includeSensors=!1){var f=Math.fround,dx=f(f(end.x)-f(start.x)),dy=f(f(end.y)-f(start.y)),dx=f(f(dx*dx)+f(dy*dy));if(!(0<dx&&dx<1/0))return[];f=box2dQueryObject("rayCast","JSRayCastCallback");f.ReportFixture=function(fixturePointer,point,normal,fraction){var fixturePointer=box2d.instance.wrapPointer(fixturePointer,box2d.instance.b2Fixture),o=fixturePointer.GetBody().object;if(o&&!o.destroyed){if(!includeSensors&&fixturePointer.IsSensor())return-1;point=box2d.vec2FromPointer(point),normal=box2d.vec2FromPointer(normal),raycastResults.push(new Box2dRaycastResult(fixturePointer,point,normal,fraction))}return 1};const raycastResults=[];return box2d.world.RayCast(f,box2dTemp(start),box2dTemp(end,1)),raycastResults.sort((a,b)=>a.fraction-b.fraction),raycastResults}raycast(start,end,includeSensors=!1){return box2d.raycastAll(start,end,includeSensors)[0]}boxCastAll(pos,size,includeSensors=!1){var queryCallback=box2dQueryObject("query","JSQueryCallback");queryCallback.ReportFixture=function(fixturePointer){var o,fixturePointer=box2d.instance.wrapPointer(fixturePointer,box2d.instance.b2Fixture);return!includeSensors&&fixturePointer.IsSensor()||(o=fixturePointer.GetBody().object)&&!o.destroyed&&!queryObjects.includes(o)&&box2dFixtureOverlaps(fixturePointer,aabb)&&queryObjects.push(o),!0};const aabb=box2dQueryObject("aabb","b2AABB");aabb.set_lowerBound(box2dTemp(pos.subtract(size.scale(.5)))),aabb.set_upperBound(box2dTemp(pos.add(size.scale(.5))));let queryObjects=[];return box2d.world.QueryAABB(queryCallback,aabb),queryObjects}boxCast(pos,size,includeSensors=!1){var queryCallback=box2dQueryObject("query","JSQueryCallback");queryCallback.ReportFixture=function(fixturePointer){var o,fixturePointer=box2d.instance.wrapPointer(fixturePointer,box2d.instance.b2Fixture);return!(includeSensors||!fixturePointer.IsSensor())||!((o=fixturePointer.GetBody().object)&&!o.destroyed&&box2dFixtureOverlaps(fixturePointer,aabb)&&(queryObject=o,1))};const aabb=box2dQueryObject("aabb","b2AABB");aabb.set_lowerBound(box2dTemp(pos.subtract(size.scale(.5)))),aabb.set_upperBound(box2dTemp(pos.add(size.scale(.5))));let queryObject;return box2d.world.QueryAABB(queryCallback,aabb),queryObject}circleCastAll(pos,diameter,includeSensors=!1){const radius2=(diameter/2)**2;diameter=box2d.objects.filter(o=>!o.destroyed&&o.body&&o.pos.distanceSquared(pos)<radius2&&(includeSensors||o.getFixtureList().some(fixture=>!fixture.IsSensor())));return diameter}circleCast(pos,diameter,includeSensors=!1){let bestResult,bestDistance2;for(const result of box2d.circleCastAll(pos,diameter,includeSensors)){var distance2=result.pos.distanceSquared(pos);(!bestResult||distance2<bestDistance2)&&(bestResult=result,bestDistance2=distance2)}return bestResult}pointCast(pos,dynamicOnly=!0,includeSensors=!1){var queryCallback=box2dQueryObject("query","JSQueryCallback"),aabb=(queryCallback.ReportFixture=function(fixturePointer){var fixturePointer=box2d.instance.wrapPointer(fixturePointer,box2d.instance.b2Fixture);return!((includeSensors||!fixturePointer.IsSensor())&&(!dynamicOnly||fixturePointer.GetBody().GetType()===box2d.bodyTypeDynamic)&&fixturePointer.TestPoint(box2dTemp(pos))&&(fixturePointer=fixturePointer.GetBody().object)&&!fixturePointer.destroyed&&(queryObject=fixturePointer,1))},box2dQueryObject("aabb","b2AABB"));aabb.set_lowerBound(box2dTemp(pos)),aabb.set_upperBound(box2dTemp(pos));let queryObject;return box2d.world.QueryAABB(queryCallback,aabb),queryObject}drawFixture(fixture,pos,angle,color=WHITE,lineColor=BLACK,lineWidth=.1,useWebGL,context){var shape=box2d.castShapeObject(fixture.GetShape());switch(shape.GetType()){case box2d.instance.b2Shape.e_polygon:var points=[];for(let i=shape.GetVertexCount();i--;)points.push(box2d.vec2From(shape.GetVertex(i)));drawPoly(points,color,lineWidth,lineColor,pos,angle,useWebGL,!1,context);break;case box2d.instance.b2Shape.e_circle:var radius=shape.get_m_radius(),offset=box2d.vec2From(shape.get_m_p());drawCircle(pos.add(offset.rotate(angle)),2*radius,color,lineWidth,lineColor,useWebGL,!1,context);break;case box2d.instance.b2Shape.e_edge:drawLine(box2d.vec2From(shape.get_m_vertex1()),box2d.vec2From(shape.get_m_vertex2()),lineWidth,lineColor,pos,angle,useWebGL,!1,context)}}vec2From(v){return new Vector2(v.get_x(),v.get_y())}vec2FromPointer(vp){vp=box2d.instance.wrapPointer(vp,box2d.instance.b2Vec2);return box2d.vec2From(vp)}vec2dTo(v){return new box2d.instance.b2Vec2(v.x,v.y)}isNull(o){return!box2d.instance.getPointer(o)}castShapeObject(o){switch(o.GetType()){case box2d.instance.b2Shape.e_circle:return box2d.instance.castObject(o,box2d.instance.b2CircleShape);case box2d.instance.b2Shape.e_edge:return box2d.instance.castObject(o,box2d.instance.b2EdgeShape);case box2d.instance.b2Shape.e_polygon:return box2d.instance.castObject(o,box2d.instance.b2PolygonShape);case box2d.instance.b2Shape.e_chain:return box2d.instance.castObject(o,box2d.instance.b2ChainShape)}}castJointObject(o){switch(o.GetType()){case box2d.instance.e_revoluteJoint:return box2d.instance.castObject(o,box2d.instance.b2RevoluteJoint);case box2d.instance.e_prismaticJoint:return box2d.instance.castObject(o,box2d.instance.b2PrismaticJoint);case box2d.instance.e_distanceJoint:return box2d.instance.castObject(o,box2d.instance.b2DistanceJoint);case box2d.instance.e_pulleyJoint:return box2d.instance.castObject(o,box2d.instance.b2PulleyJoint);case box2d.instance.e_mouseJoint:return box2d.instance.castObject(o,box2d.instance.b2MouseJoint);case box2d.instance.e_gearJoint:return box2d.instance.castObject(o,box2d.instance.b2GearJoint);case box2d.instance.e_wheelJoint:return box2d.instance.castObject(o,box2d.instance.b2WheelJoint);case box2d.instance.e_weldJoint:return box2d.instance.castObject(o,box2d.instance.b2WeldJoint);case box2d.instance.e_frictionJoint:return box2d.instance.castObject(o,box2d.instance.b2FrictionJoint);case box2d.instance.e_ropeJoint:return box2d.instance.castObject(o,box2d.instance.b2RopeJoint);case box2d.instance.e_motorJoint:return box2d.instance.castObject(o,box2d.instance.b2MotorJoint)}}}async function box2dInit(){new Box2dPlugin(await Box2D());{const debugDraw=new box2d.instance.JSDraw,box2dColor=c=>new Color(c.get_r(),c.get_g(),c.get_b()),getDebugColor=color=>{return color=color,box2dColor(box2d.instance.wrapPointer(color,box2d.instance.b2Color)).scale(1,.8)},getPointsList=(vertices,vertexCount)=>{var points=[];for(let i=vertexCount;i--;)points.push(box2d.vec2FromPointer(vertices+8*i));return points};debugDraw.DrawSegment=function(point1,point2,color){color=getDebugColor(color),drawLine(point1=box2d.vec2FromPointer(point1),point2=box2d.vec2FromPointer(point2),.1,color,vec2(),0,!1,!1)},debugDraw.DrawPolygon=function(vertices,vertexCount,color){color=getDebugColor(color),drawPoly(getPointsList(vertices,vertexCount),CLEAR_WHITE,.1,color,vec2(),0,!1,!1)},debugDraw.DrawSolidPolygon=function(vertices,vertexCount,color){color=getDebugColor(color),drawPoly(getPointsList(vertices,vertexCount),color,0,color,vec2(),0,!1,!1)},debugDraw.DrawCircle=function(center,radius,color){color=getDebugColor(color),drawCircle(center=box2d.vec2FromPointer(center),2*radius,CLEAR_WHITE,.1,color,!1,!1)},debugDraw.DrawSolidCircle=function(center,radius,axis,color){color=getDebugColor(color),center=box2d.vec2FromPointer(center),axis=box2d.vec2FromPointer(axis).scale(radius),drawCircle(center,2*radius,color,.1,color,!1,!1),drawLine(vec2(),axis,.1,color,center,0,!1,!1)},debugDraw.DrawTransform=function(transform){transform=box2d.instance.wrapPointer(transform,box2d.instance.b2Transform);var pos=box2d.vec2From(transform.get_p()),transform=-transform.get_q().GetAngle(),p1=vec2(1,0),c1=rgb(.75,0,0,.8),p2=vec2(0,1),c2=rgb(0,.75,0,.8);drawLine(vec2(),p1,.1,c1,pos,transform,!1,!1),drawLine(vec2(),p2,.1,c2,pos,transform,!1,!1)},debugDraw.AppendFlags(box2d.instance.b2Draw.e_shapeBit),debugDraw.AppendFlags(box2d.instance.b2Draw.e_jointBit),box2d.world.SetDebugDraw(debugDraw)}return engineAddPlugin(function(){if(paused||!timeScale)box2d.objects=box2d.objects.filter(o=>!o.destroyed);else{box2d.step();for(const o of box2d.objects){var p;o.body&&(debug&&function(o){var start=box2dUnusedStarts.get(o);if(start){let i=0;for(const name in box2dUnusedFields){var value=o[name];let changed;value instanceof Vector2?(changed=value.x!==start[i]||value.y!==start[i+1],i+=2):changed=value!==start[i++],changed&&box2dUnusedWarn(name,box2dUnusedFields[name])}o.gravityScale!==start[i]&&(start[i]=o.gravityScale,Math.fround(o.gravityScale)!==o.body.GetGravityScale())&&box2dUnusedWarn("gravityScale","setGravityScale")}}(o),p=o.body.GetPosition(),o.pos.set(p.get_x(),p.get_y()),o.angle=-o.body.GetAngle())}}},function(){box2dDebug&&box2d.world.DrawDebugData()}),box2d}function drawNineSliceScreen(pos,size,startTile,color=WHITE,borderSize=32,additiveColor,extraSpace=2,angle=0,useWebGL=!1,context){drawNineSlice(pos,size,startTile,color,borderSize,additiveColor,extraSpace,angle,useWebGL,!0,context)}function drawNineSlice(pos,size,startTile,color,borderSize,additiveColor,extraSpace,angle=0,useWebGL=glEnable,screenSpace=drawScreenSpace,context){if(borderSize??=screenSpace?32:1,extraSpace??=screenSpace?2:.05,size.x&&size.y){color&&color.a<1&&(extraSpace=0);const step=startTile.size.add(vec2(2*startTile.padding));if(screenSpace&&!angle)drawSliceSnapped(pos,size,borderSize,(col,row)=>[startTile.offset(step.multiply(vec2(col,row))),0],color,additiveColor,useWebGL,context);else{borderSize=min(borderSize,abs(size.x)/2,abs(size.y)/2);var centerTile=startTile.offset(step),centerSize=size.add(vec2(extraSpace-2*borderSize)),cornerSize=vec2(borderSize),cornerOffset=size.scale(.5).subtract(cornerSize.scale(.5)),flip=screenSpace?-1:1,rotateAngle=screenSpace?-angle:angle;drawTile(pos,centerSize,centerTile,color,angle,!1,additiveColor,useWebGL,screenSpace,context);for(let i=4;i--;){var horizontal=i%2,sidePos=cornerOffset.multiply(vec2(horizontal?1===i?1:-1:0,horizontal?0:i?-1:1)),horizontal=vec2(horizontal?borderSize:centerSize.x,horizontal?centerSize.y:borderSize),sideTile=centerTile.offset(step.multiply(vec2(1===i?1:3===i?-1:0,0===i?-flip:2===i?flip:0)));drawTile(pos.add(sidePos.rotate(rotateAngle)),horizontal,sideTile,color,angle,!1,additiveColor,useWebGL,screenSpace,context)}for(let i=4;i--;){var flipX=1<i,flipY=i&&i<3,cornerPos=cornerOffset.multiply(vec2(flipX?-1:1,flipY?-1:1)),flipX=centerTile.offset(step.multiply(vec2(flipX?-1:1,flipY?flip:-flip)));drawTile(pos.add(cornerPos.rotate(rotateAngle)),cornerSize,flipX,color,angle,!1,additiveColor,useWebGL,screenSpace,context)}}}}function drawThreeSliceScreen(pos,size,startTile,color=WHITE,borderSize=32,additiveColor,extraSpace=2,angle=0,useWebGL=!1,context){drawThreeSlice(pos,size,startTile,color,borderSize,additiveColor,extraSpace,angle,useWebGL,!0,context)}function drawThreeSlice(pos,size,startTile,color,borderSize,additiveColor,extraSpace,angle=0,useWebGL=glEnable,screenSpace=drawScreenSpace,context){if(borderSize??=screenSpace?32:1,extraSpace??=screenSpace?2:.05,size.x&&size.y){color&&color.a<1&&(extraSpace=0);const cornerTile=startTile.frame(0),sideTile=startTile.frame(1),centerTile=startTile.frame(2);if(screenSpace&&!angle){const turns=[[0,0,1],[3,-1,1],[3,2,2]];void drawSliceSnapped(pos,size,borderSize,(col,row)=>{var turn=turns[row][col],col=1!==col&&1!==row;return turn<0?[centerTile,0]:[col?cornerTile:sideTile,turn*PI/2]},color,additiveColor,useWebGL,context)}else{borderSize=min(borderSize,abs(size.x)/2,abs(size.y)/2);var centerSize=size.add(vec2(extraSpace-2*borderSize)),cornerSize=vec2(borderSize),cornerOffset=size.scale(.5).subtract(cornerSize.scale(.5)),flip=screenSpace?-1:1,rotateAngle=screenSpace?-angle:angle;drawTile(pos,centerSize,centerTile,color,angle,!1,additiveColor,useWebGL,screenSpace,context);for(let i=4;i--;){var a=angle+i*PI/2,horizontal=i%2,sidePos=cornerOffset.multiply(vec2(horizontal?1===i?1:-1:0,horizontal?0:i?-flip:flip)),horizontal=vec2(horizontal?centerSize.y:centerSize.x,borderSize);drawTile(pos.add(sidePos.rotate(rotateAngle)),horizontal,sideTile,color,a,!1,additiveColor,useWebGL,screenSpace,context)}for(let i=4;i--;){const a=angle+i*PI/2;var flipX=!i||2<i,flipY=1<i,flipX=cornerOffset.multiply(vec2(flipX?-1:1,flipY?-flip:flip));drawTile(pos.add(flipX.rotate(rotateAngle)),cornerSize,cornerTile,color,a,!1,additiveColor,useWebGL,screenSpace,context)}}}}class TileSlice{constructor(tileInfo,slices=9,borderSize,extraSpace){this.tileInfo=tileInfo,this.slices=slices,this.borderSize=borderSize,this.extraSpace=extraSpace}draw(pos,size,color=WHITE,additiveColor,angle=0,useWebGL=glEnable,screenSpace=drawScreenSpace,context){if(screenSpace)return this.drawScreen(pos,size,color,additiveColor,angle,useWebGL,context);9===this.slices?drawNineSlice(pos,size,this.tileInfo,color,this.borderSize,additiveColor,this.extraSpace,angle,useWebGL,screenSpace,context):3===this.slices?drawThreeSlice(pos,size,this.tileInfo,color,this.borderSize,additiveColor,this.extraSpace,angle,useWebGL,screenSpace,context):drawTile(pos,size,this.tileInfo,color,angle,!1,additiveColor,useWebGL,screenSpace,context)}drawScreen(pos,size,color=WHITE,additiveColor,angle=0,useWebGL=!1,context){9===this.slices?drawNineSliceScreen(pos,size,this.tileInfo,color,this.borderSize,additiveColor,this.extraSpace,angle,useWebGL,context):3===this.slices?drawThreeSliceScreen(pos,size,this.tileInfo,color,this.borderSize,additiveColor,this.extraSpace,angle,useWebGL,context):drawTile(pos,size,this.tileInfo,color,angle,!1,additiveColor,useWebGL,!0,context)}}function drawSliceSnapped(pos,size,borderSize,pieceTile,color,additiveColor,useWebGL,context){var transform=(!useWebGL||!glEnable||!!context)&&(context||drawContext)?.getTransform?.(),ratio=getCanvasPixelRatio(),[transform,ratio,offsetX,offsetY]=transform?[transform.a||1,transform.d||1,transform.e||0,transform.f||0]:[ratio,ratio,0,0],edges=(center,length,scale,offset)=>{var border=max(1,round(borderSize*abs(scale))),center=round((center-length/2)*scale+offset),length=center+round(length*scale),start=min(center,length),center=max(center,length),length=min(start+border,floor((start+center)/2)),start=[start,length,max(center-border,length),center];return(scale<0?start.reverse():start).map(edge=>(edge-offset)/scale)},xs=edges(pos.x,size.x,transform,offsetX),ys=edges(pos.y,size.y,ratio,offsetY);for(let row=3;row--;)for(let col=3;col--;){var tileInfo,angle,turned,w=xs[col+1]-xs[col],h=ys[row+1]-ys[row];w<=0||h<=0||([tileInfo,angle]=pieceTile(col,row),turned=round(angle/(PI/2))%2,drawTile(vec2(xs[col]+w/2-.5,ys[row]+h/2-.5),turned?vec2(h,w):vec2(w,h),tileInfo,color,angle,!1,additiveColor,useWebGL,!0,context))}}function drawCrescent(pos,size=1,percent=0,color=WHITE,angle=0,invert=!1,lineWidth=0,lineColor=BLACK,useWebGL=glEnable,screenSpace=drawScreenSpace,context){drawPoly(getCrescentPoints(vec2(),size,percent,0,invert),color,lineWidth,lineColor,pos,angle,useWebGL,screenSpace,context)}function getCrescentPoints(pos,size=1,percent=0,angle=0,invert=!1,sides=glCircleSides){let p=mod(4*percent,4);2<=p&&(angle+=PI),p=p<=2?p-1:3-p,invert&&(p=-p,angle+=PI);var points=[],segs=max(3,sides>>1),radius=size/2;for(let i=0;i<=segs;i++){var t=i/segs*PI;points.push(vec2(radius*cos(t),radius*sin(t)).rotate(angle).add(pos))}for(let i=segs;0<=i;i--){const t=i/segs*PI;points.push(vec2(radius*cos(t),-radius*p*sin(t)).rotate(angle).add(pos))}return points}let textureSheetSize=2048,textureSheetPadding=1,textureSheets=[],textureSheetQueue=Promise.resolve(),textureSheetPendingCount=0;const textureSheetLoaded=new Map,textureSheetLoadKey=(...parts)=>parts.map(p=>isVector2(p)?p.x+","+p.y:String(p)).join("|"),textureSheetLoadFailed=(key,loaded)=>textureSheetLoaded.get(key)===loaded&&textureSheetLoaded.delete(key);class TextureSheet{constructor(size=textureSheetSize){this.size=size,this.context=headlessMode?void 0:createCanvasContext(size),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(size),this.textureInfo.sizeInverse=vec2(1/size))}tryAdd(imageSize,frameSize,padding=textureSheetPadding,sourcePadding=0){var sourcePadding=isNumber(sourcePadding)?vec2(sourcePadding):sourcePadding,sourceCellWidth=(frameSize||=imageSize.subtract(sourcePadding.scale(2))).x+2*sourcePadding.x,sourcePadding=frameSize.y+2*sourcePadding.y,cellWidth=frameSize.x+2*padding,cellHeight=frameSize.y+2*padding,maxColumns=this.size/cellWidth|0,sourceCellWidth=imageSize.x/sourceCellWidth|0,imageSize=sourceCellWidth*(imageSize.y/sourcePadding|0);if(imageSize){sourcePadding=min(sourceCellWidth,maxColumns),sourceCellWidth=sourcePadding*cellWidth,maxColumns=ceil(imageSize/sourcePadding)*cellHeight;let x=this.cursor.x,y=this.cursor.y,rowHeight=this.rowHeight;if(x+sourceCellWidth>this.size&&(x=0,y+=rowHeight,rowHeight=0),!(y+maxColumns>this.size))return this.cursor.x=x+sourceCellWidth,this.cursor.y=y,this.rowHeight=max(rowHeight,maxColumns),new TileInfo(vec2(x+padding,y+padding),frameSize,this.textureInfo,padding,0,sourcePadding)}}drawImage(image,tileInfo,update=!0,sourcePadding=0){var sourcePad=isNumber(sourcePadding)?vec2(sourcePadding):sourcePadding,frameSize=tileInfo.size,sourceCellWidth=frameSize.x+2*sourcePad.x,sourceCellHeight=frameSize.y+2*sourcePad.y,sourceColumns=image.width/sourceCellWidth|0,sourcePadding=sourceColumns*(image.height/sourceCellHeight|0),columns=tileInfo.columns||sourcePadding,cellWidth=frameSize.x+2*tileInfo.padding,cellHeight=frameSize.y+2*tileInfo.padding;for(let i=sourcePadding;i--;){var sourceX=i%sourceColumns*sourceCellWidth+sourcePad.x,sourceY=(i/sourceColumns|0)*sourceCellHeight+sourcePad.y;this.context.drawImage(image,sourceX,sourceY,frameSize.x,frameSize.y,tileInfo.pos.x+i%columns*cellWidth,tileInfo.pos.y+(i/columns|0)*cellHeight,frameSize.x,frameSize.y)}this.glDirty=!0,update&&this.updateTexture()}updateTexture(){this.glDirty&&(this.glDirty=!1,this.textureInfo.createWebGLTexture())}}function loadSprite(src,frameSize,padding=textureSheetPadding,sourcePadding=0){isNumber(frameSize)&&(frameSize=vec2(frameSize));const key=textureSheetLoadKey("sprite",src,frameSize,padding,sourcePadding);if(!headlessMode&&textureSheetLoaded.has(key))return textureSheetLoaded.get(key);const tileInfo=new TileInfo(vec2(),vec2(),void 0,padding,0);if(!headlessMode){textureSheetLoaded.set(key,tileInfo),tileInfo.textureInfo=(textureSheets[0]||textureSheetCreate()).textureInfo;const image=new Image,imagePromise=new Promise(resolve=>{image.onerror=image.onload=resolve,image.crossOrigin="anonymous",image.src=src});textureSheetQueueJob("loadSprite "+src,async()=>{var added;await imagePromise,image.width?(added=textureSheetAdd(vec2(image.width,image.height),frameSize,padding,sourcePadding))?(Object.assign(tileInfo,added.tile),added.sheet.drawImage(image,tileInfo,!1,sourcePadding)):console.warn("loadSprite image is too large to fit on a texture sheet:",src):(console.warn("loadSprite failed to load image:",src),textureSheetLoadFailed(key,tileInfo))},()=>textureSheetLoadFailed(key,tileInfo))}return tileInfo}function loadTiles(sources,tileSize=tileDefaultSize,padding=textureSheetPadding){const size=isNumber(tileSize)?vec2(tileSize):tileSize.copy(),key=textureSheetLoadKey("tiles",sources.join("\n"),size,padding);if(!headlessMode&&textureSheetLoaded.has(key))return textureSheetLoaded.get(key);const set=new TileInfo(vec2(),size,void 0,0,0);if(set.tiles=[],!headlessMode){textureSheetLoaded.set(key,set),set.textureInfo=(textureSheets[0]||textureSheetCreate()).textureInfo;for(const src of sources){const image=new Image,imagePromise=new Promise(resolve=>{image.onerror=image.onload=resolve,image.crossOrigin="anonymous",image.src=src});textureSheetQueueJob("loadTiles "+src,async()=>{await imagePromise;var columns=image.width/size.x|0,rows=image.height/size.y|0,count=columns*rows,added=count&&textureSheetAdd(vec2(columns*size.x,rows*size.y),size,padding,0);if(image.width||textureSheetLoadFailed(key,set),!added)return console.warn("loadTiles: "+src+(count?" does not fit on a texture sheet":" failed to load, or is smaller than a tile")+", its tiles are left out");added.sheet.drawImage(image,added.tile,!1);for(let k=0;k<count;++k)set.tiles.push(added.tile.frame(k))},()=>textureSheetLoadFailed(key,set))}}return set}function loadAtlas(imageSrc,jsonSrc,padding=textureSheetPadding){const key="object"==typeof jsonSrc?void 0:textureSheetLoadKey("atlas",imageSrc,jsonSrc,padding);if(!headlessMode&&textureSheetLoaded.has(key))return textureSheetLoaded.get(key);const atlas={};if(!headlessMode){key&&textureSheetLoaded.set(key,atlas);const jsonPromise="object"==typeof jsonSrc?Promise.resolve(jsonSrc):fetch(jsonSrc).then(r=>r.ok&&r.json()).catch(()=>{}),image=new Image,imagePromise=new Promise(resolve=>{image.onerror=image.onload=resolve,image.crossOrigin="anonymous",image.src=imageSrc});textureSheetQueueJob("loadAtlas "+imageSrc,async()=>{var data=await jsonPromise;if(await imagePromise,image.width&&data){for(const group of parseAtlas(data))if(group.frames.length){var sourceSize=group.frames[0].sourceSize,added=textureSheetAdd(vec2(sourceSize.x*group.frames.length,sourceSize.y),sourceSize,padding);if(added){const{sheet,tile}=added,context=sheet.context,cellWidth=sourceSize.x+2*padding,cellHeight=sourceSize.y+2*padding;group.frames.forEach((f,i)=>{var x=tile.pos.x+i%tile.columns*cellWidth+f.offset.x,i=tile.pos.y+(i/tile.columns|0)*cellHeight+f.offset.y;f.rotated?(context.save(),context.translate(x,i),context.rotate(-PI/2),context.drawImage(image,f.pos.x,f.pos.y,f.size.y,f.size.x,-f.size.y,0,f.size.y,f.size.x),context.restore()):context.drawImage(image,f.pos.x,f.pos.y,f.size.x,f.size.y,x,i,f.size.x,f.size.y)}),sheet.glDirty=!0,atlas[group.name]=tile}else console.warn("loadAtlas frames are too large to fit on a texture sheet:",group.name)}}else console.warn("loadAtlas failed to load:",imageSrc,jsonSrc),textureSheetLoadFailed(key,atlas)},()=>textureSheetLoadFailed(key,atlas))}return atlas}function textureSheetQueueJob(name,job,failed){++textureSheetPendingCount,textureSheetQueue=textureSheetQueue.then(async()=>{try{await job()}catch(e){console.error(name+" failed:",e),failed?.()}finally{if(!--textureSheetPendingCount)for(const sheet of textureSheets)try{sheet.updateTexture()}catch(e){console.error("texture sheet upload failed:",e)}}})}function parseAtlas(data){var frames=(isArray(data.frames)?data.frames.map(f=>[f.filename,f]):Object.entries(data.frames)).map(([name,f])=>({name:name.replace(/\.[^.\\/]+$/,""),pos:vec2(f.frame.x,f.frame.y),size:vec2(f.frame.w,f.frame.h),offset:vec2(f.spriteSourceSize?.x??0,f.spriteSourceSize?.y??0),sourceSize:vec2(f.sourceSize?.w??f.frame.w,f.sourceSize?.h??f.frame.h),rotated:!!f.rotated}));const groups=[];data=data.meta?.frameTags;if(data?.length){const tagged=new Set;for(const tag of data){var from=max(0,floor(tag.from)||0),to=min(frames.length-1,floor(tag.to)||0);groups.push({name:tag.name,frames:frames.slice(from,to+1)});for(let i=from;i<=to;++i)tagged.add(i)}frames.forEach((f,i)=>tagged.has(i)||groups.push({name:f.name,frames:[f]}))}else{var stems=new Map;for(const f of frames){let match=f.name.match(/^(.+?)([-_ /])?(\d+)$/);var stem=(match=match&&!match[2]&&/\d$/.test(match[1])?void 0:match)?match[1]:f.name;f.groupIndex=match?Number(match[3]):void 0,stems.has(stem)||stems.set(stem,[]),stems.get(stem).push(f)}for(const[stem,list]of stems)list.sort((a,b)=>a.groupIndex-b.groupIndex),1<list.length&&list.every((f,i)=>f.groupIndex===list[0].groupIndex+i)&&list.every(f=>f.sourceSize.x===list[0].sourceSize.x&&f.sourceSize.y===list[0].sourceSize.y)?groups.push({name:stem,frames:list}):list.forEach(f=>groups.push({name:f.name,frames:[f]}))}return parseAtlasNamesCheck(groups)}function parseAtlasNamesCheck(groups){var name,names=new Set;for({name}of groups)names.has(name)?console.warn(`parseAtlas: two sprites are named ${name}, the second takes the first's place`):names.add(name);return groups}async function spritesReady(){for(;textureSheetPendingCount;)await textureSheetQueue}function textureSheetCreate(){var sheet=new TextureSheet;return textureSheets.push(sheet),sheet}function textureSheetAdd(imageSize,frameSize,padding,sourcePadding){let sheet,tile;for(sheet of textureSheets)if(tile=sheet.tryAdd(imageSize,frameSize,padding,sourcePadding))break;if(!tile){var emptySheet={size:textureSheetSize,cursor:vec2(),rowHeight:0};if(!TextureSheet.prototype.tryAdd.call(emptySheet,imageSize,frameSize,padding,sourcePadding))return;sheet=textureSheetCreate(),tile=sheet.tryAdd(imageSize,frameSize,padding,sourcePadding)}return{sheet:sheet,tile:tile}}function setTextureSheetSize(size){textureSheetSize=size}function setTextureSheetPadding(padding){textureSheetPadding=padding}const tweenActive=[],tweenUpdateList=[];let tweenUpdatePass=0;function tweenActivate(tween){tween.activePass=tweenUpdatePass,tween.active||(tween.active=!0,tween.listed||tweenActive.push(tween),tween.listed=!0)}function tweenCall(tween,life,pass){try{tween.callback(tween.interp(life))}catch(error){throw tween.active&&(void 0===pass||tween.activePass<pass)&&(tween.active=!1),error}}function tweenIsLerpable(v){return v&&"function"==typeof v.lerp}class Tween{constructor(callback,start=0,end=1,duration=1,options={}){tweenIsLerpable(start),this.callback=callback,this.start=start,this.end=end,this.duration=duration,this.life=duration,this.ease=options.ease||Ease.LINEAR,this.useRealTime=!!options.useRealTime,this.paused=!!options.paused,this.onComplete=void 0,this.thenCallback=void 0,this.loopRemaining=0,this.active=!1,this.listed=!1,this.activePass=0,this.lastTime=time,this.lastTimeReal=timeReal,this.target=void 0,tweenActivate(this),tweenCall(this,duration)}setEase(easeFn){return this.ease=easeFn,this}then(callback){return this.onComplete=callback,this}loop(count=1/0){return this.loopRemaining=count,this.thenCallback=()=>function tweenLoopContinuation(tween){const passed=tweenPassed(tween);if(tween.loopRemaining<=passed)return!1;return tweenNextIteration(tween,passed,()=>tweenLoopContinuation(tween))}(this),this}pingPong(count=1/0){return this.loopRemaining=count,this.thenCallback=()=>function tweenPingPongContinuation(tween){const passed=tweenPassed(tween);const done=tween.loopRemaining<=passed;const swaps=done?max(tween.loopRemaining-1,0):passed;if(1&swaps){const tmp=tween.start;tween.start=tween.end,tween.end=tmp}if(done)return 1&swaps&&tween.callback(tween.interp(0)),!1;return tweenNextIteration(tween,passed,()=>tweenPingPongContinuation(tween))}(this),this}pause(){this.paused=!0}resume(){this.paused=!1}restart(){this.life=this.duration,this.paused=!1,this.lastTime=time,this.lastTimeReal=timeReal,tweenActivate(this),tweenCall(this,this.duration)}isActive(){return!this.paused&&this.active}getPercent(){return percent(this.duration-this.life,0,this.duration)}getValue(){return this.interp(this.life)}interp(life){var y,s=this.start,e=this.end;return life<=0?"function"==typeof e.copy?e.copy():e:(y=1-(life=this.ease((this.duration-life)/this.duration)),s instanceof Vector2?vec2(e.x*life+s.x*y,e.y*life+s.y*y):void 0!==Vector3&&s instanceof Vector3?vec3(e.x*life+s.x*y,e.y*life+s.y*y,e.z*life+s.z*y):tweenIsLerpable(s)?s.lerp(e,life):s+(e-s)*life)}stop(){this.active=!1,this.thenCallback=void 0}}const Ease={LINEAR:x=>x,POWER:n=>x=>x**n,SINE:x=>1-cos(x*(PI/2)),CIRC:x=>1-(1-x*x)**.5,EXPO:x=>0===x?0:2**(10*x-10),BACK:x=>x*x*(2.70158*x-1.70158),ELASTIC:x=>0===x?0:1===x?1:-(2**(10*x-10))*sin((37-40*x)*PI/6),SPRING:x=>1-(sin(PI*(1-x)*(.2+2.5*(1-x)**3))*x**2.2+(1-x))*(1+1.2*x),BOUNCE:x=>{let t=1-x,f;return 1-(f=t<4/11?7.5625*t*t:t<8/11?7.5625*(t-=6/11)*t+.75:t<10/11?7.5625*(t-=9/11)*t+.9375:7.5625*(t-=10.5/11)*t+.984375)},IN:f=>f,OUT:f=>x=>1-f(1-x),IN_OUT:f=>Ease.PIECEWISE(f,Ease.OUT(f)),PIECEWISE:(...fns)=>{const n=fns.length;return x=>{var i=x*n-1e-9>>0;return(fns[i]((x-i/n)*n)+i)/n}},BEZIER:(x1,y1,x2,y2)=>{const curve=t=>{var u=1-t,c1=3*u*u*t,u=3*u*t*t,t=t**3;return[c1*x1+u*x2+t,c1*y1+u*y2+t]};return x=>{if(x<=0)return 0;if(1<=x)return 1;let t0=0,t1=1;for(let i=32;i--;){var tMid=(t0+t1)/2;curve(tMid)[0]<x?t0=tMid:t1=tMid}return curve((t0+t1)/2)[1]}}};function tweenProperty(target,propertyPath,start,end,duration=1,options={}){const parts=propertyPath.split("."),lastKey=parts.pop();let tween;return(tween=new Tween(value=>{if(target.destroyed)tween?.stop();else{let obj=target;for(const k of parts)obj=obj[k];obj[lastKey]=value}},start,end,duration,options)).target=target,tween}function tweenPassed(tween){var duration=tween.duration;return 0<duration?1+floor(-min(tween.life,0)/duration):1}function tweenNextIteration(tween,passed,continuation){return tween.loopRemaining-=passed,function(tween){var duration=tween.duration;tween.life=0<duration?min(tween.life,0)%duration+duration:1e-9}(tween),tween.thenCallback=continuation,tweenActivate(tween),tweenCall(tween,tween.life),!0}function tweenUpdate(gameDelta,realDelta){var enginePath=void 0===gameDelta,list=(enginePath||void 0!==realDelta||(realDelta=gameDelta),tweenUpdateList.length?[]:tweenUpdateList),pass=++tweenUpdatePass;let kept=0;for(const t of tweenActive)t.active?list.push(tweenActive[kept++]=t):t.listed=!1;tweenActive.length=kept;let failed;try{for(let i=list.length;i--;){var t=list[i];try{!function(t,pass,enginePath,gameDelta,realDelta){if(t.active&&!(t.activePass>=pass)){let dt;enginePath?(dt=t.useRealTime?timeReal-t.lastTimeReal:time-t.lastTime,t.lastTime=time,t.lastTimeReal=timeReal):dt=t.useRealTime?realDelta:gameDelta,t.target?.destroyed?t.stop():t.paused||dt<=0||(t.life-=dt,1e-9<t.life?tweenCall(t,t.life,pass):(tweenCall(t,0,pass),!t.active||t.activePass>=pass||(t.active=!1,enginePath=t.thenCallback,t.thenCallback=void 0,enginePath&&enginePath())||!t.onComplete||t.onComplete()))}}(t,pass,enginePath,gameDelta,realDelta)}catch(error){failed??={error:error}}}}finally{list.length=0}if(failed)throw failed.error}function tweenStopAll(){for(const t of tweenActive)t.thenCallback=void 0,t.active=t.listed=!1;tweenActive.length=0}engineAddPlugin(tweenUpdate);let sceneCurrent,sceneLeaving=!1,scenePluginAdded=!1;function setScene(scene){if(scenePluginAdded||(scenePluginAdded=!0,engineAddPlugin(sceneUpdate,sceneRenderPost,void 0,void 0,sceneRender)),sceneCurrent){sceneLeaving=!0;try{sceneCurrent.leave?.()}finally{sceneLeaving=!1}}engineObjectsDestroy(),(sceneCurrent=scene)?.enter?.()}function getScene(){return sceneCurrent}function sceneUpdate(){!paused&&timeScale&&sceneCurrent?.update?.()}function sceneRender(){sceneCurrent?.render?.()}function sceneRenderPost(){sceneCurrent?.renderPost?.()}class ParallaxLayer extends CanvasLayer{constructor(pos=vec2(),size=vec2(32,16),parallax=.5,renderOrder=-1e3,drawFunction=parallaxMountains(),canvasSize=vec2(512,256)){super(pos,size,0,renderOrder,canvasSize),this.castShadow=!1,this.parallax=isVector2(parallax)?parallax.copy():vec2(parallax),this.wrapX=!0,this.wrapY=!1,this.zoomFollow=1,this.zoomScale=cameraScale,this.drawFunction=drawFunction,this.redraw()}redraw(){var size,context=this.context;context&&(size=vec2(this.canvas.width,this.canvas.height),context.clearRect(0,0,size.x,size.y),this.drawFunction(context,size,this),this.updateWebGL())}zoomFactor(){return this.zoomFollow<1&&0<cameraScale?(this.zoomScale/cameraScale)**(1-this.zoomFollow):1}getDrawSize(){return this.size.scale(this.zoomFactor())}getDrawPositions(){const zoom=this.zoomFactor(),size=this.size.scale(zoom),p=this.parallax;var offset=this.pos.subtract(cameraPos);const center=cameraPos.add(vec2(offset.x*(1-p.x)*zoom,offset.y*(1-p.y)*zoom));offset=mainCanvasSize.scale(.5/(cameraScale||1));const reach=cameraAngle?vec2(offset.length()):offset;var offset=(wrap,axis)=>{var to;return wrap&&0<size[axis]?(wrap=cameraPos[axis]-reach[axis]-size[axis]/2-center[axis],to=cameraPos[axis]+reach[axis]+size[axis]/2-center[axis],[wrap=ceil(wrap/size[axis]),min(floor(to/size[axis]),wrap+99)]):[0,0]},[x0,x1]=offset(this.wrapX,"x"),[offset,y1]=offset(this.wrapY,"y"),positions=[];for(let y=offset;y<=y1;++y)for(let x=x0;x<=x1;++x)positions.push(vec2(center.x+x*size.x,center.y+y*size.y));return positions}render(){var size=this.getDrawSize();for(const pos of this.getDrawPositions())this.draw(pos,size,this.color,0,this.mirror,this.additiveColor,!1)}}function parallaxMountains(topColor=hsl(.6,.2,.6),bottomColor=hsl(.6,.3,.25),seed){return(context,size)=>{var w=size.x,h=size.y,ridge=function(width,height,random){var slopeRange=height/256,ridge=new Float32Array(width+1),middle=height/2;let y=middle,slope=random.float(-slopeRange,slopeRange);for(let x=0;x<=width;++x)ridge[x]=y,y+=slope-=.005*(y-middle),random.float()<.2&&(slope=random.float(-slopeRange,slopeRange));var drift=ridge[width]-ridge[0];for(let x=0;x<=width;++x)ridge[x]=clamp(ridge[x]-drift*x/width,0,height);return ridge}(w,h,new RandomGenerator(void 0===seed?randInt(1e9)+1:7919*seed+1)),strip=createCanvasContext(1,h);for(let i=h;i--;)strip.fillStyle=topColor.lerp(bottomColor,i/h).toString(),strip.fillRect(0,i,1,1);for(let x=w;x--;)ridge[x]<h&&context.drawImage(strip.canvas,0,0,1,h,x,ridge[x],1,h-ridge[x])}}const PATHFINDER_DIAGONAL_COST=Math.SQRT2,PATHFINDER_TILE_VEC=vec2(1);class PathFinderNode{constructor(x,y){this.pos=vec2(x,y),this.posWorld=vec2(),this.walkable=!1,this.cost=0,this.g=0,this.f=0,this.h=0,this.parent=null,this.isOpen=!1,this.isClosed=!1,this.heapIndex=0}resetSearch(){this.g=this.f=this.h=0,this.parent=null,this.isOpen=this.isClosed=!1}reset(){this.walkable=!1,this.cost=0,this.resetSearch()}isClear(){return this.walkable&&0===this.cost}}class PathFinder{constructor(source){isVector2(source)?(this.size=source.floor(),this.tileLayer=void 0):(this.size=(source=source).size,this.tileLayer=source),this.heuristicWeight=1,this.maxLoop=void 0,this.searchGaveUp=!1,this.smoothPath=!0,this.debug=!1,this.debugTime=1,this.nodes=new Array(this.size.x*this.size.y);for(let y=0;y<this.size.y;++y)for(let x=0;x<this.size.x;++x)this.nodes[x+y*this.size.x]=new PathFinderNode(x,y);this.nodeDataBuilt=!1,this.collisionScratch=vec2(),this.searchNodes=[]}isWalkable(x,y){return!(this.tileLayer&&0<this.tileLayer.getCollisionData(this.collisionScratch.set(x,y)))}getCost(x,y){return 0}getNode(x,y){return x<0||y<0||x>=this.size.x||y>=this.size.y?null:this.nodes[x+y*this.size.x]}worldToTile(worldPos){var ox=this.tileLayer?this.tileLayer.pos.x:0,oy=this.tileLayer?this.tileLayer.pos.y:0;return vec2(floor(worldPos.x-ox),floor(worldPos.y-oy))}tileToWorld(x,y){return vec2(x+.5+(this.tileLayer?this.tileLayer.pos.x:0),y+.5+(this.tileLayer?this.tileLayer.pos.y:0))}buildNodeData(){this.nodeDataBuilt=!0;var w=this.size.x,h=this.size.y,ox=this.tileLayer?this.tileLayer.pos.x:0,oy=this.tileLayer?this.tileLayer.pos.y:0;for(let y=0;y<h;++y)for(let x=0;x<w;++x){var node=this.nodes[x+y*w],walkable=(node.reset(),!!this.isWalkable(x,y)),cost=walkable?max(0,this.getCost(x,y)):0;node.walkable=walkable,node.cost=cost,node.posWorld.set(x+.5+ox,y+.5+oy),this.debug&&0<this.debugTime&&(walkable?0<cost&&(node.posWorld,PATHFINDER_TILE_VEC,rgb(1,0,0,min(.2,.05*cost)),this.debugTime):(node.posWorld,PATHFINDER_TILE_VEC,rgb(1,0,0,.25),this.debugTime))}}aStarSearch(startNode,endNode){var searchNodes=this.searchNodes;for(const n of searchNodes)n.resetSearch();searchNodes.length=0,searchNodes.push(startNode);const openList=[],isBetter=(a,b)=>a.f<b.f-1e-9||a.f<b.f+1e-9&&a.h<b.h;var siftUp=node=>{let i=node.heapIndex;for(;i;){var parent=i-1>>1;if(!isBetter(node,openList[parent]))break;(openList[i]=openList[parent]).heapIndex=i,i=parent}(openList[i]=node).heapIndex=i},maxLoop=(startNode.isOpen=!0,startNode.heapIndex=openList.length,siftUp(startNode),this.maxLoop??this.size.x*this.size.y);let loopCount=0;for(this.searchGaveUp=!1;0<openList.length;){var current=openList[0];if(current===endNode)break;if(++loopCount>maxLoop){this.searchGaveUp=!endNode.parent;break}right=left=last=best=void 0;var best=openList[0],last=openList.pop();if(last!==best){let i=0;for(;;){var left=2*i+1,right=1+left;if(left>=openList.length)break;right=right<openList.length&&isBetter(openList[right],openList[left])?right:left;if(!isBetter(openList[right],last))break;(openList[i]=openList[right]).heapIndex=i,i=right}(openList[i]=last).heapIndex=i}current.isOpen=!1,current.isClosed=!0,this.debug&&0<this.debugTime&&(current.posWorld,PATHFINDER_TILE_VEC,rgb(1,1,1,.05),this.debugTime);for(let dy=-1;dy<=1;++dy)for(let dx=-1;dx<=1;++dx)if(0!==dx||0!==dy){var neighbor=this.getNode(current.pos.x+dx,current.pos.y+dy);if(neighbor&&neighbor.walkable&&!neighbor.isClosed){let stepCost=1;if(0!==dx&&0!==dy){var card1=this.getNode(current.pos.x+dx,current.pos.y);if(!card1||!card1.walkable)continue;card1=this.getNode(current.pos.x,current.pos.y+dy);if(!card1||!card1.walkable)continue;stepCost=PATHFINDER_DIAGONAL_COST}card1=current.g+stepCost+neighbor.cost;if(neighbor.isOpen){if(card1>=neighbor.g)continue}else neighbor.isOpen=!0,neighbor.heapIndex=openList.length,openList.push(neighbor),searchNodes.push(neighbor);neighbor.parent=current,neighbor.g=card1;var adx=abs(endNode.pos.x-neighbor.pos.x),ady=abs(endNode.pos.y-neighbor.pos.y),adx=max(adx,ady)+(Math.SQRT2-1)*min(adx,ady);neighbor.h=adx,neighbor.f=neighbor.g+adx*this.heuristicWeight,siftUp(neighbor)}}}return null!==endNode.parent}getNearestClearNode(worldPos,searchRange=10,rebuild=!0){return!rebuild&&this.nodeDataBuilt||this.buildNodeData(),pathFinderNearestNode(this,worldPos,searchRange,node=>node.isClear())}smoothPathCorners(path){if(!(path.length<=2)){let i=1;for(;i<path.length-1;){var prev=path[i-1],node=path[i],next=path[i+1],dx=next.pos.x-prev.pos.x,dy=next.pos.y-prev.pos.y,dx=dx*dx+dy*dy,dy=node.pos.x-prev.pos.x,stepDy=node.pos.y-prev.pos.y,stepDxNext=next.pos.x-node.pos.x,stepDyNext=next.pos.y-node.pos.y;if(1==dx)this.debug&&0<this.debugTime&&(node.posWorld,rgb(.5,0,.5,.5),this.debugTime),path.splice(i,1),i=max(1,i-1);else{if(2==dx){this.debug&&0<this.debugTime&&(node.posWorld,rgb(1,0,0,.5),this.debugTime);let sx,sy;sy=(prev.pos.y===node.pos.y&&next.pos.x===node.pos.x?(sx=prev.pos.x,next):(sx=next.pos.x,prev)).pos.y;var shortcut=this.getNode(sx,sy);if(shortcut&&shortcut.isClear()){path.splice(i,1),i=max(1,i-1);continue}}else if(5==dx){this.debug&&0<this.debugTime&&(node.posWorld,rgb(1,1,0,.5),this.debugTime);var prevPrev=2<=i?path[i-2]:prev;let s1x,s1y,s2x,s2y;s2y=(0==dy||0==stepDxNext?(s1x=next.pos.x,s1y=node.pos.y,s2x=prev.pos.x,node):(s1x=node.pos.x,s1y=next.pos.y,s2x=node.pos.x,prev)).pos.y;var dd1x=s1x-prevPrev.pos.x,dd1y=s1y-prevPrev.pos.y,dd2x=s2x-prevPrev.pos.x,prevPrev=s2y-prevPrev.pos.y,dd1x=dd1x*dd1x+dd1y*dd1y<dd2x*dd2x+prevPrev*prevPrev,dd1y=dd1x?s1x:s2x,dd2x=dd1x?s1y:s2y;const shortcut=this.getNode(dd1y,dd2x);if(shortcut&&shortcut!==node&&shortcut.isClear()){prevPrev=next.pos.x+s2x-s1x,dd1x=next.pos.y+s2y-s1y,dd1y=this.getNode(prevPrev,dd1x);if(dd1y&&dd1y.isClear()){path[i]=shortcut,i=max(1,i-1);continue}}}else if(4==dx||8==dx){if(this.debug&&0<this.debugTime&&(node.posWorld,rgb(0,1,0,.5),this.debugTime),dy==stepDxNext&&stepDy==stepDyNext){++i;continue}{let sx,sy;sy=(prev.pos.y===next.pos.y?(sx=node.pos.x,prev):(sx=prev.pos.x,node)).pos.y;const shortcut=this.getNode(sx,sy);if(shortcut&&shortcut.isClear()){path[i]=shortcut,i=max(1,i-1);continue}}}++i}}}}smoothPathStringPull(path){if(!(path.length<=2)){const original=path.slice(),last=original.length-1;var corners=[];for(let i=1;i<last;++i){var a=original[i-1].pos,b=original[i].pos,c=original[i+1].pos;(b.x-a.x)*(c.y-a.y)!=(b.y-a.y)*(c.x-a.x)&&corners.push(i)}corners.push(last),path.length=0,path.push(original[0]);for(let k=0;k<last;){let j=k+1;if(original[k].isClear()){var clear=i=>original[i].isClear()&&this.isLineClear(original[k].pos,original[i].pos);let c=corners.length-1,next=1+last;for(;0<=c&&corners[c]>k+1&&!clear(corners[c]);)next=corners[c--];j=0<=c&&corners[c]>k+1?corners[c]:k+1;for(let i=next-1;i>j;--i)if(clear(i)){j=i;break}}path.push(original[j]),k=j}}}dropCollinearNodes(path){if(!(path.length<3)){let kept=1;for(let i=1;i<path.length-1;++i){var a=path[kept-1],b=path[i],c=path[i+1];(b.pos.x-a.pos.x)*(c.pos.y-a.pos.y)!=(b.pos.y-a.pos.y)*(c.pos.x-a.pos.x)&&(path[kept++]=b)}path[kept++]=path[path.length-1],path.length=kept}}isNodeClear(x,y){x=this.getNode(x,y);return null!==x&&x.isClear()}isLineClear(startPos,endPos){var dx=endPos.x-startPos.x,dy=endPos.y-startPos.y,adx=abs(dx),ady=abs(dy),sx=sign(dx),sy=sign(dy);let x=startPos.x,y=startPos.y;if(ady===adx){for(;x!==endPos.x;){if(x!==startPos.x){if(!this.isNodeClear(x,y))return!1;if(!this.isNodeClear(x,y-sy))return!1}if(!this.isNodeClear(x,y+sy))return!1;x+=sx,y+=sy}if(!this.isNodeClear(endPos.x,endPos.y-sy))return!1}else if(ady<adx)if(0==dy)for(x+=sx;x!==endPos.x;){if(!this.isNodeClear(x,y))return!1;x+=sx}else{let lastY=startPos.y;for(;x!==endPos.x;){if(y=startPos.y+Math.trunc(dy*(x-startPos.x)/dx),lastY!==y){if(!this.isNodeClear(x-sx,y+sy))return!1;if(!this.isNodeClear(x,y-sy))return!1}if(lastY=y,x!==startPos.x&&!this.isNodeClear(x,y))return!1;if(y+=sy,!this.isNodeClear(x,y))return!1;x+=sx}ady=endPos.y-sy;if(!this.isNodeClear(endPos.x,ady))return!1}else if(0==dx)for(y+=sy;y!==endPos.y;){if(!this.isNodeClear(x,y))return!1;y+=sy}else{let lastX=startPos.x;for(;y!==endPos.y;){if(x=startPos.x+Math.trunc(dx*(y-startPos.y)/dy),lastX!==x){if(!this.isNodeClear(x+sx,y-sy))return!1;if(!this.isNodeClear(x-sx,y))return!1}if(lastX=x,y!==startPos.y&&!this.isNodeClear(x,y))return!1;if(x+=sx,!this.isNodeClear(x,y))return!1;y+=sy}adx=endPos.x-sx;if(!this.isNodeClear(adx,endPos.y))return!1}return!0}findPath(startPos,endPos,rebuild=!0){this.searchGaveUp=!1,!rebuild&&this.nodeDataBuilt||this.buildNodeData();rebuild=node=>node.walkable,startPos=pathFinderNearestNode(this,startPos,10,rebuild),endPos=pathFinderNearestNode(this,endPos,10,rebuild);if(!startPos||!endPos)return[];if(startPos===endPos)return[startPos.posWorld.copy()];if(!this.aStarSearch(startPos,endPos))return[];var nodePath=[];for(let n=endPos;n;n=n.parent)nodePath.push(n);nodePath.reverse(),this.smoothPath&&(this.smoothPathCorners(nodePath),this.smoothPathStringPull(nodePath),this.dropCollinearNodes(nodePath));var result=nodePath.map(n=>n.posWorld.copy());if(this.debug&&0<this.debugTime&&0<result.length){for(let i=1;i<result.length;++i)result[i-1],result[i],RED,this.debugTime;for(const p of result)p,rgb(1,0,0,.3),this.debugTime;result[0],rgb(0,1,0,.5),this.debugTime,result[result.length-1],rgb(0,1,0,.5),this.debugTime}return result}}function pathFinderNearestNode(finder,worldPos,searchRange,test){var center=finder.worldToTile(worldPos),centerX=center.x,centerY=center.y;let nearest=null,nearestDistSq=0;for(let offset=0;offset<=searchRange;++offset){var bound=max(0,offset-.5);if(nearest&&bound*bound>=nearestDistSq)break;for(let dy=-offset;dy<=offset;++dy)for(let dx=-offset,step=abs(dy)===offset?1:2*offset;dx<=offset;dx+=step){var ddx,node=finder.getNode(centerX+dx,centerY+dy);node&&test(node)&&(ddx=(ddx=node.posWorld.x-worldPos.x)*ddx+(ddx=node.posWorld.y-worldPos.y)*ddx,!nearest||ddx<nearestDistSq)&&(nearest=node,nearestDistSq=ddx)}}return nearest}function vec3(x=0,y,z){return void 0===y?new Vector3(x,x,x):new Vector3(x,y,void 0===z?0:z)}function isVector3(v){return v instanceof Vector3&&v.isValid()}function randVector3(length=1,coneAngle=PI){var coneAngle=rand(cos(coneAngle),1),s=(1-coneAngle*coneAngle)**.5,a=rand(2*PI);return new Vector3(s*cos(a)*length,coneAngle*length,s*sin(a)*length)}function randInSphere(radius=1,minRadius=0){return radius<=0?new Vector3:randVector3(radius*rand(clamp(minRadius/radius)**3,1)**(1/3))}class Vector3{constructor(x=0,y=0,z=0){this.x=x,this.y=y,this.z=z}set(x=0,y=0,z=0){return this.x=x,this.y=y,this.z=z,this}setFrom(v){return this.set(v.x,v.y,v.z)}copy(){return new Vector3(this.x,this.y,this.z)}add(v){return new Vector3(this.x+v.x,this.y+v.y,this.z+v.z)}subtract(v){return new Vector3(this.x-v.x,this.y-v.y,this.z-v.z)}multiply(v){return new Vector3(this.x*v.x,this.y*v.y,this.z*v.z)}divide(v){return new Vector3(this.x/v.x,this.y/v.y,this.z/v.z)}scale(s){return new Vector3(this.x*s,this.y*s,this.z*s)}length(){return this.lengthSquared()**.5}lengthSquared(){return this.x**2+this.y**2+this.z**2}reflect(normal,restitution=1){return this.subtract(normal.scale((1+restitution)*this.dot(normal)))}distance(v){return this.distanceSquared(v)**.5}distanceSquared(v){return(this.x-v.x)**2+(this.y-v.y)**2+(this.z-v.z)**2}normalize(length=1){var l=this.length();return l?this.scale(length/l):new Vector3}clampLength(length=1){var l=this.length();return length<l?this.scale(length/l):this.copy()}dot(v){return this.x*v.x+this.y*v.y+this.z*v.z}cross(v){return new Vector3(this.y*v.z-this.z*v.y,this.z*v.x-this.x*v.z,this.x*v.y-this.y*v.x)}lerp(v,percent){return this.add(v.subtract(this).scale(clamp(percent)))}rotate(axis,angle){var c=cos(angle),angle=sin(angle),d=axis.dot(this)*(1-c);return this.scale(c).add(axis.cross(this).scale(angle)).add(axis.scale(d))}rotateX(angle){var c=cos(angle),angle=sin(angle);return new Vector3(this.x,this.y*c-this.z*angle,this.y*angle+this.z*c)}rotateY(angle){var c=cos(angle),angle=sin(angle);return new Vector3(this.x*c+this.z*angle,this.y,this.z*c-this.x*angle)}rotateZ(angle){var c=cos(angle),angle=sin(angle);return new Vector3(this.x*c-this.y*angle,this.x*angle+this.y*c,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(grid){return new Vector3(floor(this.x*grid)/grid,floor(this.y*grid)/grid,floor(this.z*grid)/grid)}transform(matrix){return matrix.transformPoint(this)}transformDirection(matrix){return matrix.transformDirection(this)}isValid(){return isNumber(this.x)&&isNumber(this.y)&&isNumber(this.z)}toString(digits=3){var f;return this.isValid()?`(${(f=v=>(v<0?"":" ")+v.toFixed(digits))(this.x)},${f(this.y)},${f(this.z)} )`:`(${this.x},${this.y},${this.z})`}}const matrix4Scratch=new Float32Array(16);class Matrix4{constructor(m){this.m=new Float32Array(16),m?this.m.set(m):this.m[0]=this.m[5]=this.m[10]=this.m[15]=1}static identity(){return new Matrix4}static translation(v){var r=new Matrix4;return r.m[12]=v.x,r.m[13]=v.y,r.m[14]=v.z,r}static rotation(euler,matrix=new Matrix4){return function(m,k,euler){var cx=cos(euler.x),sx=sin(euler.x),cy=cos(euler.y),sy=sin(euler.y),cz=cos(euler.z),euler=sin(euler.z);m[k]=cy*cz+sy*sx*euler,m[k+1]=cx*euler,m[k+2]=-sy*cz+cy*sx*euler,m[k+3]=0,m[k+4]=-cy*euler+sy*sx*cz,m[k+5]=cx*cz,m[k+6]=sy*euler+cy*sx*cz,m[k+7]=0,m[k+8]=sy*cx,m[k+9]=-sx,m[k+10]=cy*cx,m[k+11]=0,m[k+12]=m[k+13]=m[k+14]=0,m[k+15]=1}(matrix.m,0,euler),matrix}static scaling(v){var r=new Matrix4;return r.m[0]=v.x,r.m[5]=v.y,r.m[10]=v.z,r}static perspective(fov,aspect,near,far){var fov=1/tan(fov/2),r=new Matrix4,m=r.m;return m[0]=fov/aspect,m[5]=fov,m[10]=far==1/0?-1:(far+near)/(near-far),m[11]=-1,m[14]=far==1/0?-2*near:2*far*near/(near-far),m[15]=0,r}static orthographic(left,right,bottom,top,near,far){var r=new Matrix4,m=r.m;return m[0]=2/(right-left),m[5]=2/(top-bottom),m[10]=-2/(far-near),m[12]=-(right+left)/(right-left),m[13]=-(top+bottom)/(top-bottom),m[14]=-(far+near)/(far-near),r}static lookAt(eye,target,up=vec3(0,1,0)){let z=eye.subtract(target).normalize(),x=(z.lengthSquared()||(z=vec3(0,0,1)),up.cross(z).normalize());x.lengthSquared()||(x=(.99<abs(z.y)?vec3(0,0,0<z.y?-1:1):vec3(0,1,0)).cross(z).normalize());target=z.cross(x);return new Matrix4([x.x,x.y,x.z,0,target.x,target.y,target.z,0,z.x,z.y,z.z,0,eye.x,eye.y,eye.z,1])}copy(){return new Matrix4(this.m)}multiply(matrix){var a=this.m,b=matrix.m,r=matrix4Scratch;for(let j=0;j<4;++j)for(let i=0;i<4;++i)r[4*j+i]=a[i]*b[4*j]+a[4+i]*b[4*j+1]+a[8+i]*b[4*j+2]+a[12+i]*b[4*j+3];return this.m.set(r),this}translate(v){return this.multiply(Matrix4.translation(v))}rotate(euler){return this.multiply(Matrix4.rotation(euler))}scale(v){return this.multiply(Matrix4.scaling(v))}transpose(){var m=this.m;for(let i=0;i<4;++i)for(let j=i+1;j<4;++j){var t=m[4*i+j];m[4*i+j]=m[4*j+i],m[4*j+i]=t}return this}invert(){var m=this.m,[a00,a01,a02,a03,a10,a11,a12,a13,a20,a21,a22,a23,a30,a31,a32,a33]=m,b00=a00*a11-a01*a10,b01=a00*a12-a02*a10,b02=a00*a13-a03*a10,b03=a01*a12-a02*a11,b04=a01*a13-a03*a11,b05=a02*a13-a03*a12,b06=a20*a31-a21*a30,b07=a20*a32-a22*a30,b08=a20*a33-a23*a30,b09=a21*a32-a22*a31,b10=a21*a33-a23*a31,b11=a22*a33-a23*a32,det=b00*b11-b01*b10+b02*b09+b03*b08-b04*b07+b05*b06;return det&&(m[0]=(a11*b11-a12*b10+a13*b09)*(det=1/det),m[1]=(a02*b10-a01*b11-a03*b09)*det,m[2]=(a31*b05-a32*b04+a33*b03)*det,m[3]=(a22*b04-a21*b05-a23*b03)*det,m[4]=(a12*b08-a10*b11-a13*b07)*det,m[5]=(a00*b11-a02*b08+a03*b07)*det,m[6]=(a32*b02-a30*b05-a33*b01)*det,m[7]=(a20*b05-a22*b02+a23*b01)*det,m[8]=(a10*b10-a11*b08+a13*b06)*det,m[9]=(a01*b08-a00*b10-a03*b06)*det,m[10]=(a30*b04-a31*b02+a33*b00)*det,m[11]=(a21*b02-a20*b04-a23*b00)*det,m[12]=(a11*b07-a10*b09-a12*b06)*det,m[13]=(a00*b09-a01*b07+a02*b06)*det,m[14]=(a31*b01-a30*b03-a32*b00)*det,m[15]=(a20*b03-a21*b01+a22*b00)*det),this}transformPoint(v){var m=this.m;return new Vector3(m[0]*v.x+m[4]*v.y+m[8]*v.z+m[12],m[1]*v.x+m[5]*v.y+m[9]*v.z+m[13],m[2]*v.x+m[6]*v.y+m[10]*v.z+m[14])}transformDirection(v){var m=this.m;return new Vector3(m[0]*v.x+m[4]*v.y+m[8]*v.z,m[1]*v.x+m[5]*v.y+m[9]*v.z,m[2]*v.x+m[6]*v.y+m[10]*v.z)}getTranslation(){return new Vector3(this.m[12],this.m[13],this.m[14])}determinant(){var m=this.m;return m[0]*(m[5]*m[10]-m[6]*m[9])-m[4]*(m[1]*m[10]-m[2]*m[9])+m[8]*(m[1]*m[6]-m[2]*m[5])}getScale(){var m=this.m,x=hypot(m[0],m[1],m[2]),y=hypot(m[4],m[5],m[6]),m=hypot(m[8],m[9],m[10]);return new Vector3(this.determinant()<0?-x:x,y,m)}getRotation(){var m=this.m,s=this.getScale(),sx=s.x||1,sy=s.y||1,s=s.z||1,m1=m[1]/sx,m5=m[5]/sy,m8=m[8]/s,m9=m[9]/s,s=m[10]/s,c=hypot(m8,s),pitch=atan2(-m9,c);return 1e-9<c?new Vector3(pitch,atan2(m8,s),atan2(m1,m5)):new Vector3(pitch,atan2(m[4]/sy*-m9,m[0]/sx),0)}toString(){const m=this.m,f=i=>m[i].toFixed(2).padStart(7);let s="";for(let row=0;row<4;++row)s+=`[${f(row)} ${f(4+row)} ${f(8+row)} ${f(12+row)} ]
44
- `;return s}}function buildMatrix(pos,rotation,scale,matrix=new Matrix4){return matrix4Compose(matrix.m,0,pos,rotation,scale),matrix}function matrix4Compose(m,k,pos,rotation,scale){var cx,sx,cy,sy,cz,scaleX=scale?scale.x:1,scaleY=scale?scale.y:1,scale=scale?scale.z:1;rotation&&(rotation.x||rotation.y||rotation.z)?(cx=cos(rotation.x),sx=sin(rotation.x),cy=cos(rotation.y),sy=sin(rotation.y),cz=cos(rotation.z),rotation=sin(rotation.z),m[k]=(cy*cz+sy*sx*rotation)*scaleX,m[k+1]=cx*rotation*scaleX,m[k+2]=(-sy*cz+cy*sx*rotation)*scaleX,m[k+4]=(-cy*rotation+sy*sx*cz)*scaleY,m[k+5]=cx*cz*scaleY,m[k+6]=(sy*rotation+cy*sx*cz)*scaleY,m[k+8]=sy*cx*scale,m[k+9]=-sx*scale,m[k+10]=cy*cx*scale):(m[k]=scaleX,m[k+1]=0,m[k+2]=0,m[k+4]=0,m[k+5]=scaleY,m[k+6]=0,m[k+8]=0,m[k+9]=0,m[k+10]=scale),m[k+3]=m[k+7]=m[k+11]=0,m[k+15]=1,m[k+12]=pos?pos.x:0,m[k+13]=pos?pos.y:0,m[k+14]=pos?pos.z:0}class Ray3D{constructor(origin=vec3(),direction=vec3(0,0,-1)){this.origin=origin,this.direction=direction}getPosition(distance){return this.origin.add(this.direction.scale(distance))}copy(){return new Ray3D(this.origin.copy(),this.direction.copy())}}const isTurned3D=rotation=>!!rotation&&!!(rotation.x||rotation.y||rotation.z);function boxAxes3D(rotation){rotation=buildMatrix(void 0,rotation).m;return[vec3(rotation[0],rotation[1],rotation[2]),vec3(rotation[4],rotation[5],rotation[6]),vec3(rotation[8],rotation[9],rotation[10])]}function boxLocal3D(point,pos,axes){point=point.subtract(pos);return vec3(point.dot(axes[0]),point.dot(axes[1]),point.dot(axes[2]))}const boxWorld3D=(v,axes)=>axes[0].scale(v.x).add(axes[1].scale(v.y)).add(axes[2].scale(v.z));function isPointInBox3D(point,pos,size,rotation){return isTurned3D(rotation)?isPointInBox3D(boxLocal3D(point,pos,boxAxes3D(rotation)),vec3(),size):abs(point.x-pos.x)<=size.x/2&&abs(point.y-pos.y)<=size.y/2&&abs(point.z-pos.z)<=size.z/2}function isOverlapping3D(posA,sizeA,posB,sizeB=vec3(),rotationA,rotationB){return isTurned3D(rotationA)||isTurned3D(rotationB)?!!collideBoxBox3D(posA,sizeA,posB,sizeB,rotationA,rotationB):(rotationA=posA.subtract(posB),abs(rotationA.x)<(sizeA.x+sizeB.x)/2&&abs(rotationA.y)<(sizeA.y+sizeB.y)/2&&abs(rotationA.z)<(sizeA.z+sizeB.z)/2)}function collideSphereSphere(posA,radiusA,posB,radiusB){posA=posA.subtract(posB),posB=radiusA+radiusB,radiusA=posA.length();if(!(posB<=radiusA))return radiusA?posA.normalize(posB-radiusA):vec3(0,posB,0)}function collideSphereBox(pos,radius,boxPos,boxSize,boxRotation){var distSq;return isTurned3D(boxRotation)?collideSphereOrientedBox3D(pos,radius,boxPos,boxSize,boxAxes3D(boxRotation)):(boxRotation=boxSize.scale(.5),boxSize=vec3(clamp(pos.x,boxPos.x-boxRotation.x,boxPos.x+boxRotation.x),clamp(pos.y,boxPos.y-boxRotation.y,boxPos.y+boxRotation.y),clamp(pos.z,boxPos.z-boxRotation.z,boxPos.z+boxRotation.z)),(distSq=(boxSize=pos.subtract(boxSize)).lengthSquared())?radius*radius<=distSq?void 0:boxSize.normalize(radius-distSq**.5):pushOutAxis3D(boxSize=pos.subtract(boxPos),boxRotation.x-abs(boxSize.x),boxRotation.y-abs(boxSize.y),boxRotation.z-abs(boxSize.z),radius))}function collideSphereInBox(pos,radius,boxPos,boxSize){var x=max(0,boxSize.x/2-radius),y=max(0,boxSize.y/2-radius),boxSize=max(0,boxSize.z/2-radius),radius=vec3(clamp(pos.x,boxPos.x-x,boxPos.x+x)-pos.x,clamp(pos.y,boxPos.y-y,boxPos.y+y)-pos.y,clamp(pos.z,boxPos.z-boxSize,boxPos.z+boxSize)-pos.z);return radius.lengthSquared()?radius:void 0}function pushOutAxis3D(d,penX,penY,penZ,extra=0){var s=v=>0<=v?1:-1;return penX<=penY&&penX<=penZ?vec3(s(d.x)*(penX+extra),0,0):penY<=penZ?vec3(0,s(d.y)*(penY+extra),0):vec3(0,0,s(d.z)*(penZ+extra))}function collideSphereCylinder(pos,radius,cylinderPos,cylinderRadius,cylinderHeight){var cylinderHeight=cylinderHeight/2,offsetX=pos.x-cylinderPos.x,offsetZ=pos.z-cylinderPos.z,offsetY=pos.y-cylinderPos.y,radialDist=(offsetX**2+offsetZ**2)**.5,radialScale=radialDist?min(radialDist,cylinderRadius)/radialDist:0,cylinderPos=vec3(cylinderPos.x+offsetX*radialScale,clamp(pos.y,cylinderPos.y-cylinderHeight,cylinderPos.y+cylinderHeight),cylinderPos.z+offsetZ*radialScale),radialScale=pos.subtract(cylinderPos),pos=radialScale.lengthSquared();return pos?radius*radius<=pos?void 0:radialScale.normalize(radius-pos**.5):(cylinderPos=cylinderRadius-radialDist)<=(radialScale=cylinderHeight-abs(offsetY))?(radialDist?vec3(offsetX/radialDist,0,offsetZ/radialDist):vec3(1,0,0)).scale(cylinderPos+radius):vec3(0,(0<=offsetY?1:-1)*(radialScale+radius),0)}function collideBoxBox3D(posA,sizeA,posB,sizeB,rotationA,rotationB){return isTurned3D(rotationA)||isTurned3D(rotationB)?collideOrientedBoxes3D(posA,sizeA,boxAxes3D(rotationA),posB,sizeB,boxAxes3D(rotationB)):(rotationA=posA.subtract(posB),rotationB=(sizeA.x+sizeB.x)/2-abs(rotationA.x),posA=(sizeA.y+sizeB.y)/2-abs(rotationA.y),posB=(sizeA.z+sizeB.z)/2-abs(rotationA.z),rotationB<=0||posA<=0||posB<=0?void 0:pushOutAxis3D(rotationA,rotationB,posA,posB))}function collideSphereOrientedBox3D(pos,radius,boxPos,boxSize,axes){pos=collideSphereBox(boxLocal3D(pos,boxPos,axes),radius,vec3(),boxSize);return pos&&boxWorld3D(pos,axes)}const BOX_WORLD_AXES=Object.freeze([vec3(1,0,0),vec3(0,1,0),vec3(0,0,1)]);function collideOrientedBoxes3D(posA,sizeA,axesA,posB,sizeB,axesB){const d=posA.subtract(posB),ha=sizeA.scale(.5),hb=sizeB.scale(.5),reach=(h,axes,n)=>h.x*abs(axes[0].dot(n))+h.y*abs(axes[1].dot(n))+h.z*abs(axes[2].dot(n));let push,least=1/0;var separated=(axis,weight)=>{var lengthSquared=axis.lengthSquared();if(!(lengthSquared<1e-12)){var axis=axis.scale(lengthSquared**-.5),lengthSquared=d.dot(axis),overlap=reach(ha,axesA,axis)+reach(hb,axesB,axis)-abs(lengthSquared);if(overlap<=0)return!0;overlap*weight<least&&(least=overlap*weight,push=axis.scale(lengthSquared<0?-overlap:overlap))}return!1};for(const axis of[...axesA,...axesB])if(separated(axis,1))return;for(const a of axesA)for(const b of axesB)if(separated(a.cross(b),1.05))return;return push}function raycastSphere(ray,pos,radius){var{origin:ray,direction}=ray,x=ray.x-pos.x,y=ray.y-pos.y,ray=ray.z-pos.z,pos=x*x+y*y+ray*ray-radius*radius;return pos<0?0:(radius=direction.dot(direction))&&!((y=(x=2*(x*direction.x+y*direction.y+ray*direction.z))*x-4*radius*pos)<0)&&0<=(ray=(-x-y**.5)/(2*radius))?ray:void 0}function raycastPlane(ray,planePos,planeNormal){var{origin:ray,direction}=ray,direction=direction.dot(planeNormal);return abs(direction)<1e-9||(planePos=planePos.subtract(ray).dot(planeNormal)/direction)<0?void 0:planePos}function raycastBox(ray,pos,size,rotation){if(isTurned3D(rotation)){var rotation=boxAxes3D(rotation),d=ray.direction;const direction=vec3(d.dot(rotation[0]),d.dot(rotation[1]),d.dot(rotation[2]));return raycastBox(new Ray3D(boxLocal3D(ray.origin,pos,rotation),direction),vec3(),size)}const{origin,direction}=ray;var d=size.scale(.5),boxMin=pos.subtract(d),boxMax=pos.add(d);let tMin=0,tMax=1/0;for(const axis of"xyz"){const o=origin[axis],d=direction[axis];var mn=boxMin[axis],mx=boxMax[axis];if(d){let t0=(mn-o)/d,t1=(mx-o)/d;if(t0>t1&&([t0,t1]=[t1,t0]),tMin=max(tMin,t0),tMax=min(tMax,t1),tMin>tMax)return}else if(o<mn||mx<o)return}return tMin}let render3D;const RENDER3D_VERTEX_BYTES=36,RENDER3D_INSTANCE_FLOATS=24,RENDER3D_INSTANCE_BYTES=4*RENDER3D_INSTANCE_FLOATS,RENDER3D_MATRIX_FLOATS=16,RENDER3D_COLOR_FLOATS=8,RENDER3D_INSTANCE_ATTRIBS=[[4,4,0],[5,4,16],[6,4,32],[7,4,48],[11,4,64],[12,4,80]],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=11) in vec4 tint;layout(location=12) in vec4 uvRect;",RENDER3D_MAX_LIGHTS=8,RENDER3D_QUAD_UVS=Object.freeze([vec2(0,0),vec2(0,1),vec2(1,0),vec2(1,1)].map(uv=>Object.freeze(uv))),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;let render3DShadowCut;function render3DFaceNormal(a,b,c,d=a){c=c.subtract(a).cross(d.subtract(b));return c.lengthSquared()?c.normalize():RENDER3D_DEFAULT_NORMAL}function render3DQuadStrip(a,b,c,d){return[a,b,d,c]}function render3DQuadValues(v){return isArray(v)?render3DQuadStrip(...v):v}function render3DCanDraw(){return!!render3D.program&&render3D.isRendering}function render3DCaptureBatchState(){var r=render3D,d=r.drawState;return{blend:d.blend,additive:d.additive,depthTest:d.depthTest,depthWrite:d.depthWrite,cullBackFaces:d.cullBackFaces,mirrored:d.mirrored,lighting:d.lighting,emissive:d.emissive,receiveShadow:d.receiveShadow,specular:d.specular,pixelated:d.pixelated,shader:d.shader,normalMap:d.normalMap,normalScale:d.normalScale,shininess:d.shininess,reflectivity:d.reflectivity,emissiveMap:d.emissiveMap,emissiveMapColor:(d.emissiveMapColor||WHITE).copy(),environmentMap:d.environmentMap,version:r.stateVersion}}function render3DApplyBatchState(s){var r=render3D;r.blend=s.blend,r.additive=s.additive,r.depthTest=s.depthTest,r.depthWrite=s.depthWrite,r.cullBackFaces=s.cullBackFaces,r.mirrored=s.mirrored,r.lighting=s.lighting,r.emissive=s.emissive,r.receiveShadow=s.receiveShadow,r.specular=s.specular,r.pixelated=s.pixelated,r.shader=s.shader,r.normalMap=s.normalMap,r.normalScale=s.normalScale,r.shininess=s.shininess,r.reflectivity=s.reflectivity,r.emissiveMap=s.emissiveMap,r.emissiveMapColor=s.emissiveMapColor,r.environmentMap=s.environmentMap}function render3DStateChanged(s){var d,c,r=render3D;if(s.version!==r.stateVersion)return d=r.drawState,c=s.emissiveMapColor,d.blend!==s.blend||d.additive!==s.additive||d.depthTest!==s.depthTest||d.depthWrite!==s.depthWrite||d.cullBackFaces!==s.cullBackFaces||d.mirrored!==s.mirrored||d.lighting!==s.lighting||d.emissive!==s.emissive||d.receiveShadow!==s.receiveShadow||d.specular!==s.specular||d.pixelated!==s.pixelated||d.shader!==s.shader||d.normalMap!==s.normalMap||d.normalScale!==s.normalScale||d.shininess!==s.shininess||d.reflectivity!==s.reflectivity||d.emissiveMap!==s.emissiveMap||d.environmentMap!==s.environmentMap||d.emissiveR!==c.r||d.emissiveG!==c.g||d.emissiveB!==c.b||void(s.version=r.stateVersion)}function render3DSphereVisible(x,y,z,radius){var planes=render3D.shadowPass&&!render3D.depthPass?render3D.shadowPlanes:render3D.frustumPlanes;for(let i=0;i<planes.length;++i){var p=planes[i];if(p[0]*x+p[1]*y+p[2]*z+p[3]<-radius)return!1}return!0}function render3DWithState(fields,fn){var r=render3D,saved={};for(const key in fields)saved[key]=r[key],r[key]=fields[key];try{fn()}finally{Object.assign(r,saved)}}function render3DSize3(size){return isNumber(size)?vec3(size):size}function render3DSize2(size){return isNumber(size)?vec2(size):size}function render3DMatrix(matrix){return matrix instanceof Vector3?buildMatrix(matrix):matrix}function render3DNormalMatrix(matrix){return matrix.copy().invert().transpose()}function render3DAxis(m,i){return vec3(m[i],m[i+1],m[i+2])}function render3DMaxScale(m){return max(m[0]*m[0]+m[1]*m[1]+m[2]*m[2],m[4]*m[4]+m[5]*m[5]+m[6]*m[6],m[8]*m[8]+m[9]*m[9]+m[10]*m[10])**.5}function render3DMaxStretch(m,k=0){return render3DMaxStretchSquared(m,k)**.5}function render3DMaxStretchSquared(m,k=0){var xx=m[k]*m[k]+m[k+1]*m[k+1]+m[k+2]*m[k+2],yy=m[k+4]*m[k+4]+m[k+5]*m[k+5]+m[k+6]*m[k+6],zz=m[k+8]*m[k+8]+m[k+9]*m[k+9]+m[k+10]*m[k+10],xy=abs(m[k]*m[k+4]+m[k+1]*m[k+5]+m[k+2]*m[k+6]),xz=abs(m[k]*m[k+8]+m[k+1]*m[k+9]+m[k+2]*m[k+10]),m=abs(m[k+4]*m[k+8]+m[k+5]*m[k+9]+m[k+6]*m[k+10]);return max(xx+xy+xz,yy+xy+m,zz+xz+m)}function render3DQuadAxes(center,right,up){return[center.subtract(right).add(up),center.subtract(right).subtract(up),center.add(right).add(up),center.add(right).subtract(up)]}function render3DSetObjectState(o){var r=render3D,transparent=o?.transparent,o=o?.drawOwner?function(o){for(const name in RENDER3D_PART_SETTINGS)render3DPartScratch[name]=render3DSetting(o,name);return render3DPartScratch}(o):o,emissive=o?.emissive||0,emissive=(r.lighting=!0,r.emissive=emissive,r.additive=!!o?.additive,r.specular=o?.specular||0,o?.shininess??16),reflectivity=o?.reflectivity||0;r.shininess=emissive,r.reflectivity=reflectivity,r.normalMap=o?.normalMap||void 0,r.normalScale=o?.normalScale??1,r.emissiveMap=o?.emissiveMap||void 0,r.emissiveMapColor=o?.emissiveMapColor||WHITE,r.environmentMap=o?.environment||void 0,r.receiveShadow=!o||o.receiveShadow,r.cullBackFaces=r.mirrored=!1,r.pixelated=!!o?.pixelated,r.shader=o?.shader||void 0,r.depthTest=!0,r.shadowPass&&(r.blend=!!transparent)}function render3DDrawObjects(objects){for(const o of objects)render3DSetObjectState(o),o.render3D();render3DSetObjectState()}function render3DInstanceSlot(mesh,textureInfo,mirrored,unlit=!1){var r=render3D,textureInfo=(mesh.instanceCount&&(mesh.instanceTextureInfo!==textureInfo||mesh.instanceMirrored!==mirrored||mesh.instanceUnlit!==unlit||render3DStateChanged(mesh.instanceState))&&render3DFlushInstances(mesh),mesh.instanceCount||(mesh.instanceTextureInfo=textureInfo,mesh.instanceMirrored=mirrored,mesh.instanceUnlit=unlit,mesh.instanceState=render3DCaptureBatchState(),r.instanceMeshes.push(mesh)),mesh.instanceData),mirrored=mesh.instanceCount++*RENDER3D_INSTANCE_FLOATS;return(!textureInfo||textureInfo.length<mirrored+RENDER3D_INSTANCE_FLOATS)&&(unlit=new Float32Array(max(64*RENDER3D_INSTANCE_FLOATS,textureInfo?2*textureInfo.length:0)),textureInfo&&unlit.set(textureInfo),mesh.instanceData=unlit),mirrored}function render3DFlushInstances(only){var r=render3D,gl=glContext;for(const mesh of only?[only]:r.instanceMeshes){var buffers,state,count=mesh.instanceCount;mesh.instanceCount=0,count&&mesh.buffer&&(buffers=(buffers=r.instanceBuffers)[r.instanceBufferIndex=(r.instanceBufferIndex+1)%buffers.length],gl.bindBuffer(gl.ARRAY_BUFFER,buffers),gl.bufferData(gl.ARRAY_BUFFER,mesh.instanceData,gl.DYNAMIC_DRAW,0,count*RENDER3D_INSTANCE_FLOATS),state=mesh.instanceState,void 0!==mesh.instanceMirrored&&(state.cullBackFaces=!mesh.doubleSided,state.mirrored=mesh.instanceMirrored),mesh.instanceUnlit&&(state.lighting=!1),render3DDrawInstanced(mesh,buffers,count,mesh.instanceTextureInfo,state))}only?(only=r.instanceMeshes.indexOf(only))<0||r.instanceMeshes.splice(only,1):r.instanceMeshes.length=0}function render3DDrawInstanced(mesh,buffer,count,textureInfo,state,colorBuffer){var location,size,offset,gl=glContext,matrixBytes=4*RENDER3D_MATRIX_FLOATS;for([location,size,offset]of RENDER3D_INSTANCE_ATTRIBS){var inColors=colorBuffer&&offset>=matrixBytes,stride=colorBuffer?inColors?4*RENDER3D_COLOR_FLOATS:matrixBytes:RENDER3D_INSTANCE_BYTES;gl.bindBuffer(gl.ARRAY_BUFFER,inColors?colorBuffer:buffer),gl.vertexAttribPointer(location,size,gl.FLOAT,!1,stride,inColors?offset-matrixBytes:offset),gl.enableVertexAttribArray(location),gl.vertexAttribDivisor(location,1)}render3DSetDrawUniforms(RENDER3D_IDENTITY,textureInfo,WHITE,RENDER3D_FULL_UV_RECT,state),render3DBindMesh(mesh),gl.drawElementsInstanced(gl.TRIANGLES,mesh.bufferCount,mesh.indexType,0,count);for(const[location]of RENDER3D_INSTANCE_ATTRIBS)gl.disableVertexAttribArray(location),gl.vertexAttribDivisor(location,0);++drawCount,primitiveCount+=mesh.bufferCount/3*count}function render3DClearInstances(){for(const mesh of render3D.instanceMeshes)mesh.instanceCount=0;render3D.instanceMeshes.length=0}function render3DLayerObjects(after2D){return engineObjects.filter(o=>!o.destroyed&&o instanceof EngineObject3D&&function(o){return!!((o.drawOwner?render3DSetting(o,"renderAfter2D"):o.renderAfter2D)??render3D.renderAfter2D)}(o)===after2D)}function render3DShadowCaster(){var light=render3D.shadowLight;return light&&!light.destroyed&&!light.directional&&0<light.coneAngle&&0<light.radius&&0<light.intensity&&0<light.color.a?light:void 0}const RENDER3D_SHADOW_CONE_MAX=1.35;function render3DLightCone(light){var outer,widest=light===render3DShadowCaster()?RENDER3D_SHADOW_CONE_MAX:PI,widest=light.directional?0:min(light.coneAngle,widest);return 0<widest?(outer=cos(widest),widest=cos(widest*(1-clamp(light.coneSoftness))),widest=1/max(widest-outer,1e-4),[(light=light.getForward3D()).x*widest,light.y*widest,light.z*widest,outer*widest]):[0,0,0,-1]}const render3DCircleCache=new Map;function render3DLookRotation(direction,current){direction=direction.normalize();return direction.lengthSquared()?abs(direction.x)+abs(direction.z)<1e-9?vec3(0<direction.y?PI/2:-PI/2,current.y,0):vec3(Math.asin(clamp(direction.y,-1,1)),atan2(-direction.x,-direction.z),0):current}class Render3DPlugin{constructor(){(render3D=this).drawState={blend:!1,additive:!1,depthTest:!0,depthWrite:!0,cullBackFaces:!1,mirrored:!1,lighting:!0,emissive:0,receiveShadow:!0,specular:0,pixelated:!1,shader:void 0,normalMap:void 0,normalScale:1,shininess:16,reflectivity:0,emissiveMap:void 0,emissiveMapColor:WHITE,environmentMap:void 0,emissiveR:1,emissiveG:1,emissiveB:1},this.stateVersion=0,this.camera=new Camera3D,this.sunDirection=vec3(-.3,1,.5),this.sunColor=WHITE.copy(),this.ambientColor=hsl(0,0,.3),this.ambientGroundColor=void 0,this.fogColor=void 0,this.fogStart=0,this.fogEnd=0,this.gravity=vec3(),this.softShadowHeight=0,this.smoothShading=!1,this.depthTexture=!1,this.shadows=!1,this.soundDefaultRange=100,this.shadowMapSize=1024,this.shadowLight=void 0,this.shadowRange=40,this.shadowCenter=void 0,this.shadowBias=.003,this.shadowSoftness=1,this.onRenderOpaque=void 0,this.onRenderTransparent=void 0,this.sky=void 0,this.skyBox=void 0,this.environment=void 0,this.renderAfter2D=!1,this.sortTransparent=!0,this.frustumCulling=!0,this.instancing=!0,this.mipmaps=!0,this.anisotropy=4,this.boxMesh=buildBox(),this.sphereMesh=buildSphere(1,16,8,!0),this.planeMesh=buildGrid(),this.planeMesh.doubleSided=!1,this.planeMeshDoubleSided=buildGrid(),this.billboardMesh=(new Mesh).addStrip([vec3(-.5,.5,0),vec3(-.5,-.5,0),vec3(.5,.5,0),vec3(.5,-.5,0)],vec3(0,0,1),RENDER3D_QUAD_UVS),this.billboardMesh.doubleSided=!0,this.isRendering=!1,this.shadowPass=!1,this.viewMatrix=new Matrix4,this.projectionMatrix=new Matrix4,this.viewProjection=new Matrix4,this.shadowMatrix=new Matrix4,this.gelAxes=[1,0,0,1],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.frustumPlanes=[],this.shadowPlanes=[],this.program=void 0,this.currentProgram=void 0,this.lightCount=0,this.shadowShader=void 0,this.vao=void 0,this.cubeCaptures=new Set,this.capturingCube=void 0,this.captureFramebuffer=void 0,this.captureDepth=void 0,this.captureDepthSize=0,this.skyBoxProgram=void 0,this.skyBoxVao=void 0,this.whiteTexture=void 0,this.samplers=new Map,this.samplerKey=void 0,this.shadowTexture=void 0,this.shadowFramebuffer=void 0,this.shadowTextureSize=0,this.cameraDepthTexture=void 0,this.cameraDepthFramebuffer=void 0,this.cameraDepthWidth=0,this.cameraDepthHeight=0,this.depthPass=!1,this.contextGeneration=0,this.uniforms=new WeakMap,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.lightCones=new Float32Array(4*RENDER3D_MAX_LIGHTS),this.shadowLightIndex=-1,this.shadowDepthBias=0,this.streamBuffer=void 0,this.instanceBuffers=[],this.instanceBufferIndex=0,this.instanceMeshes=[],this.attribValues=[],this.streamData=new ArrayBuffer(32768*RENDER3D_VERTEX_BYTES),this.streamFloats=new Float32Array(this.streamData),this.streamInts=new Uint32Array(this.streamData),this.streamCount=0,this.streamTileInfo=void 0,this.streamState=void 0,this.streamUnlit=!1,this.capture=void 0,this.transparentQueue=void 0,render3DInitGL(),engineAddPlugin(void 0,render3DRender,render3DContextLost,render3DContextRestored,render3DPreRender)}get lighting(){return this.drawState.lighting}set lighting(v){var d=this.drawState;d.lighting!==v&&(d.lighting=v,++this.stateVersion)}get emissive(){return this.drawState.emissive}set emissive(v){var d=this.drawState;d.emissive!==v&&(d.emissive=v,++this.stateVersion)}get additive(){return this.drawState.additive}set additive(v){var d=this.drawState;d.additive!==v&&(d.additive=v,++this.stateVersion)}get depthTest(){return this.drawState.depthTest}set depthTest(v){var d=this.drawState;d.depthTest!==v&&(d.depthTest=v,++this.stateVersion)}get depthWrite(){return this.drawState.depthWrite}set depthWrite(v){var d=this.drawState;d.depthWrite!==v&&(d.depthWrite=v,++this.stateVersion)}get cullBackFaces(){return this.drawState.cullBackFaces}set cullBackFaces(v){var d=this.drawState;d.cullBackFaces!==v&&(d.cullBackFaces=v,++this.stateVersion)}get mirrored(){return this.drawState.mirrored}set mirrored(v){var d=this.drawState;d.mirrored!==v&&(d.mirrored=v,++this.stateVersion)}get specular(){return this.drawState.specular}set specular(v){var d=this.drawState;d.specular!==v&&(d.specular=v,++this.stateVersion)}get shininess(){return this.drawState.shininess}set shininess(v){var d=this.drawState;d.shininess!==v&&(d.shininess=v,++this.stateVersion)}get normalMap(){return this.drawState.normalMap}set normalMap(v){var d=this.drawState;d.normalMap!==v&&(d.normalMap=v,++this.stateVersion)}get normalScale(){return this.drawState.normalScale}set normalScale(v){var d=this.drawState;d.normalScale!==v&&(d.normalScale=v,++this.stateVersion)}get reflectivity(){return this.drawState.reflectivity}set reflectivity(v){var d=this.drawState;d.reflectivity!==v&&(d.reflectivity=v,++this.stateVersion)}get emissiveMap(){return this.drawState.emissiveMap}set emissiveMap(v){var d=this.drawState;d.emissiveMap!==v&&(d.emissiveMap=v,++this.stateVersion)}get environmentMap(){return this.drawState.environmentMap}set environmentMap(v){var d=this.drawState;d.environmentMap!==v&&(d.environmentMap=v,++this.stateVersion)}get emissiveMapColor(){return this.drawState.emissiveMapColor}set emissiveMapColor(v){var d=this.drawState,c=v||WHITE;d.emissiveMapColor=v,c.r===d.emissiveR&&c.g===d.emissiveG&&c.b===d.emissiveB||(d.emissiveR=c.r,d.emissiveG=c.g,d.emissiveB=c.b,++this.stateVersion)}get shader(){return this.drawState.shader}set shader(v){var d=this.drawState;d.shader!==v&&(d.shader=v,++this.stateVersion)}get receiveShadow(){return this.drawState.receiveShadow}set receiveShadow(v){var d=this.drawState;d.receiveShadow!==v&&(d.receiveShadow=v,++this.stateVersion)}get pixelated(){return this.drawState.pixelated}set pixelated(v){var d=this.drawState;d.pixelated!==v&&(d.pixelated=v,++this.stateVersion)}get blend(){return this.drawState.blend}set blend(v){var d=this.drawState;d.blend!==v&&(d.blend=v,++this.stateVersion)}updateMatrices(aspect=mainCanvasSize.y?mainCanvasSize.x/mainCanvasSize.y:1){var camera=this.camera,cameraMatrix=(camera.align2D&&camera.update2D(),camera.getMatrix()),camera=(this.viewMatrix=cameraMatrix.copy().invert(),this.projectionMatrix=camera.getProjectionMatrix(aspect),this.viewProjection=this.projectionMatrix.copy().multiply(this.viewMatrix),cameraMatrix.m);this.cameraRight=render3DAxis(camera,0),this.cameraUp=render3DAxis(camera,4),this.cameraBack=render3DAxis(camera,8),this.cameraForward=this.cameraBack.scale(-1),this.frustumPlanes=render3DFrustumPlanes(this.viewProjection)}worldToClip(pos){var m=this.viewProjection.m,w=m[3]*pos.x+m[7]*pos.y+m[11]*pos.z+m[15],z=(m[2]*pos.x+m[6]*pos.y+m[10]*pos.z+m[14])/w;if(!(w<=0||z<-1))return vec3((m[0]*pos.x+m[4]*pos.y+m[8]*pos.z+m[12])/w,(m[1]*pos.x+m[5]*pos.y+m[9]*pos.z+m[13])/w,z)}worldToScreen(pos,canvasSize=mainCanvasSize){pos=this.worldToClip(pos);if(pos)return vec2((pos.x+1)/2*canvasSize.x,(1-pos.y)/2*canvasSize.y)}screenToRay(screenPos,canvasSize=mainCanvasSize){var width=canvasSize.x||1,canvasSize=canvasSize.y||1,aspect=width/canvasSize,camera=this.camera,width=(this.updateMatrices(aspect),screenPos.x/width*2-1),screenPos=1-screenPos.y/canvasSize*2,canvasSize=camera.orthographic?camera.orthographic/2:tan(camera.fov/2),width=this.cameraRight.scale(width*canvasSize*aspect).add(this.cameraUp.scale(screenPos*canvasSize));return camera.orthographic?new Ray3D(camera.pos.add(width).add(this.cameraForward.scale(camera.near)),this.cameraForward.copy()):new Ray3D(camera.pos.copy(),this.cameraForward.add(width).normalize())}screenToGround(screenPos,groundHeight=0,canvasSize=mainCanvasSize){screenPos=this.screenToRay(screenPos,canvasSize),canvasSize=raycastPlane(screenPos,vec3(0,groundHeight,0),RENDER3D_DEFAULT_NORMAL);return void 0===canvasSize?void 0:screenPos.getPosition(canvasSize)}pick(from,objects=engineObjects){var ray=from instanceof Ray3D?from:this.screenToRay(from);let nearest;for(const o of objects){var distance=render3DRaycastObject(ray,o);void 0!==distance&&(!nearest||distance<nearest.distance)&&(nearest={object:o,distance:distance})}return nearest}playSound(sound,pos3D,volume=1,pitch=1,randomnessScale=1,loop=!1,paused=!1){var pos3D=pos3D.subtract(this.camera.pos),range=sound.rangeIsDefault?this.soundDefaultRange:sound.range;if(range){var distance=pos3D.length();if(range<=distance)return;var taperRange=range*sound.taper;taperRange<distance&&(volume*=percent(distance,range,taperRange))}distance=sound.play(void 0,volume,pitch,randomnessScale,loop,paused);return distance?.setPan(pos3D.normalize().dot(this.cameraRight)),distance}playSoundLoop(sound,pos3D,volume=1,pitch=1,randomnessScale=1){return this.playSound(sound,pos3D,volume,pitch,randomnessScale,!0)}isSphereVisible(center,radius){return render3DSphereVisible(center.x,center.y,center.z,radius)}drawMesh(mesh,matrix=RENDER3D_IDENTITY,tileInfo,color=WHITE){if(matrix=render3DMatrix(matrix),this.capture)this.capture.combine(mesh,matrix,color);else{if(this.transparentQueue){const m=matrix.copy(),c=color.copy();return this.queueTransparent(m.getTranslation(),()=>this.drawMesh(mesh,m,tileInfo,c))}if(render3DCanDraw()&&(!this.shadowPass||this.lighting)&&(render3DMeshUpload(mesh),mesh.bufferCount)){const m=matrix.m;var cullBackFaces,mirrored;this.frustumCulling&&!render3DSphereVisible(m[12],m[13],m[14],mesh.radius*render3DMaxStretch(m))||(!this.blend&&this.depthTest&&(mesh.instanced??this.instancing)?function(mesh,matrix,tileInfo,color){var k=render3DInstanceSlot(mesh,render3DTextureOf(tileInfo),matrix.determinant()<0),matrix=((mesh=mesh.instanceData).set(matrix.m,k),mesh[k+16]=color.r,mesh[k+17]=color.g,mesh[k+18]=color.b,mesh[k+19]=color.a,render3DGetTileUVs(tileInfo));mesh[k+20]=matrix.x,mesh[k+21]=matrix.y,mesh[k+22]=matrix.w,mesh[k+23]=matrix.h}(mesh,matrix,tileInfo,color):(cullBackFaces=this.cullBackFaces,mirrored=this.mirrored,this.cullBackFaces=!mesh.doubleSided,this.mirrored=matrix.determinant()<0,this.flush(),render3DFlushBeforeOverlay(),render3DSetDrawUniforms(matrix,tileInfo,color),render3DBindMesh(mesh),glContext.drawElements(glContext.TRIANGLES,mesh.bufferCount,mesh.indexType,0),++drawCount,primitiveCount+=mesh.bufferCount/3,this.cullBackFaces=cullBackFaces,this.mirrored=mirrored))}}}drawStrip(points,normals,uvs,colors,tileInfo){if(this.capture)this.capture.addStrip(points,normals,uvs,colors);else{if(this.transparentQueue){let x=0,y=0,z=0;for(const p of points)x+=p.x,y+=p.y,z+=p.z;return this.queueTransparent(vec3(x,y,z).scale(1/points.length),()=>this.drawStrip(points,normals,uvs,colors,tileInfo))}var n=points.length,count=render3DStripCount(n),uvRect=render3DBeginStrip(count,tileInfo);if(uvRect){var floats=this.streamFloats,ints=this.streamInts,normalArray=isArray(normals),uvArray=isArray(uvs),colorArray=isArray(colors),rgba=colorArray?0:(colors||WHITE).rgbaInt();for(let k=0;k<count;++k){var i=render3DStripIndex(k,n),p=points[i],uv=uvArray?uvs[i]:uvs||RENDER3D_DEFAULT_UV;render3DWriteVertex(floats,ints,9*this.streamCount++,p.x,p.y,p.z,normalArray?normals[i]:normals||RENDER3D_DEFAULT_NORMAL,uvRect.x+uv.x*uvRect.w,uvRect.y+uv.y*uvRect.h,colorArray?colors[i].rgbaInt():rgba)}}}}drawStripUnlit(points,normals,uvs,colors,tileInfo){render3DWithState({lighting:!1},()=>this.drawStrip(points,normals,uvs,colors,tileInfo))}flush(){var gl;this.streamCount&&render3DCanDraw()&&(gl=glContext,this.streamUnlit&&(this.streamState.lighting=!1),render3DSetDrawUniforms(RENDER3D_IDENTITY,this.streamTileInfo,WHITE,RENDER3D_FULL_UV_RECT,this.streamState),render3DBindVertexBuffer(this.streamBuffer),gl.bufferSubData(gl.ARRAY_BUFFER,0,this.streamFloats,0,9*this.streamCount),gl.drawArrays(gl.TRIANGLE_STRIP,0,this.streamCount),++drawCount,primitiveCount+=this.streamCount,this.streamCount=0)}bake(drawFunction){this.flush();var mesh=this.capture=new Mesh;try{drawFunction()}finally{this.capture=void 0}return mesh}renderStages(objects,isDefault=!0){var opaque=[],transparent=[];for(const o of objects)(o.transparent||render3DSetting(o,"additive")?transparent:opaque).push(o);isDefault&&(this.skyBox&&this.skyBox!==this.capturingCube||this.sky)&&this.drawSky(),this.blend=!1,this.depthWrite=!0;var m,byOrder=(a,b)=>(a.drawOwner?render3DSetting(a,"renderOrder"):a.renderOrder)-(b.drawOwner?render3DSetting(b,"renderOrder"):b.renderOrder);opaque.sort(byOrder),transparent.sort(byOrder),render3DDrawObjects(opaque),isDefault&&this.onRenderOpaque?.(),this.flush(),render3DFlushInstances(),this.blend=!0,this.depthWrite=!1,this.transparentQueue=this.sortTransparent?[]:void 0;try{render3DDrawObjects(transparent);for(const o of objects)o.softShadow&&(m=render3DObjectMatrix(o),this.drawSoftShadow(m.getTranslation(),o.softShadow*render3DMaxScale(m.m),this.softShadowHeight));isDefault&&this.onRenderTransparent?.()}finally{this.flushTransparentQueue()}isDefault&&this.capturingCube,this.flush(),render3DSetObjectState(),this.blend=!1,this.depthWrite=!0}queueTransparent(pos,draw){var f,c;this.transparentQueue?(f=this.cameraForward,c=this.camera.pos,pos=(pos.x-c.x)*f.x+(pos.y-c.y)*f.y+(pos.z-c.z)*f.z,this.transparentQueue.push({distance:pos,state:render3DCaptureBatchState(),draw:draw})):draw()}flushTransparentQueue(){var queue=this.transparentQueue;if(queue){this.transparentQueue=void 0,queue.sort((a,b)=>b.distance-a.distance);var state=render3DCaptureBatchState();try{for(const item of queue)render3DApplyBatchState(item.state),item.draw()}finally{render3DApplyBatchState(state)}}}drawSky(){if(this.flush(),(!this.skyBox||this.skyBox===this.capturingCube||!function(cube){var gl=glContext,r=render3D;if(r.skyBoxProgram||=glCreateProgram("#version 300 es\nprecision highp float;out vec2 v;void main(){v=vec2(gl_VertexID&1,gl_VertexID>>1)*4.-1.;gl_Position=vec4(v,0,1);}","#version 300 es\nprecision highp float;uniform mat4 inverseViewProj;uniform samplerCube sky;in vec2 v;out vec4 o;void main(){vec4 a=inverseViewProj*vec4(v,-1,1),b=inverseViewProj*vec4(v,0,1);o=vec4(texture(sky,b.xyz/b.w-a.xyz/a.w).rgb,1);}"),glFailedPrograms.has(r.skyBoxProgram))return;r.skyBoxVao||=gl.createVertexArray();var depthTest=gl.isEnabled(gl.DEPTH_TEST),blend=gl.isEnabled(gl.BLEND),cull=gl.isEnabled(gl.CULL_FACE),depthWrite=gl.getParameter(gl.DEPTH_WRITEMASK),cube=(gl.useProgram(r.skyBoxProgram),r.currentProgram=void 0,gl.activeTexture(gl.TEXTURE6),gl.bindTexture(gl.TEXTURE_CUBE_MAP,render3DCubeTexture(cube)),gl.bindSampler(6,null),gl.uniform1i(glUniformLocation(r.skyBoxProgram,"sky"),6),r.viewMatrix.copy());return cube.m[12]=cube.m[13]=cube.m[14]=0,gl.uniformMatrix4fv(glUniformLocation(r.skyBoxProgram,"inverseViewProj"),!1,r.projectionMatrix.copy().multiply(cube).invert().m),gl.disable(gl.DEPTH_TEST),gl.disable(gl.BLEND),gl.disable(gl.CULL_FACE),gl.depthMask(!1),gl.bindVertexArray(r.skyBoxVao),gl.drawArrays(gl.TRIANGLES,0,3),gl.bindVertexArray(r.vao),gl.bindTexture(gl.TEXTURE_CUBE_MAP,null),gl.activeTexture(gl.TEXTURE0),depthTest&&gl.enable(gl.DEPTH_TEST),blend&&gl.enable(gl.BLEND),cull&&gl.enable(gl.CULL_FACE),gl.depthMask(depthWrite),1}(this.skyBox))&&this.sky){var{near,far}=this.camera;const radius=far==1/0?1e4*near:(near+far)/2;render3DWithState({lighting:!1,blend:!1,depthTest:!1,depthWrite:!1,fogEnd:0,shader:void 0,emissiveMap:void 0},()=>this.drawMesh(this.sky,buildMatrix(this.camera.pos,void 0,vec3(radius))))}}updateShadowMatrix(){var caster=render3DShadowCaster();if(caster){var pos=caster.getWorldPos3D(),forward=caster.getForward3D(),up=.99<abs(forward.y)?vec3(0,0,1):vec3(0,1,0),far=caster.radius,near=min(max(far/500,.02),far/2),fov=2*min(caster.coneAngle,RENDER3D_SHADOW_CONE_MAX)+.1;const view=Matrix4.lookAt(pos,pos.add(forward),up);this.shadowMatrix=Matrix4.perspective(fov,1,near,far).multiply(view.copy().invert());pos=view.m,forward=render3DObjectMatrix(caster).m,up=vec3(forward[0],forward[1],forward[2]).normalize(),fov=vec3(forward[4],forward[5],forward[6]).normalize(),caster=vec3(pos[0],pos[1],pos[2]),forward=vec3(pos[4],pos[5],pos[6]);this.gelAxes=[up.dot(caster),up.dot(forward),fov.dot(caster),fov.dot(forward)],this.shadowPlanes=render3DFrustumPlanes(this.shadowMatrix),void(this.shadowDepthBias=this.shadowBias*far*far*near/(far-near))}else{this.shadowDepthBias=this.shadowBias;pos=0<this.shadowRange?this.shadowRange:1,up=pos/2,caster=this.sunDirection.normalize(),fov=this.shadowCenter||this.camera.pos.add(this.cameraForward.scale(.8*up));const view=Matrix4.lookAt(fov.add(caster.scale(pos)),fov).invert();forward=pos/(this.shadowTextureSize||this.shadowMapSize),far=view.m;far[12]=round(far[12]/forward)*forward,far[13]=round(far[13]/forward)*forward,this.shadowMatrix=Matrix4.orthographic(-up,up,-up,up,0,2*pos).multiply(view),this.shadowPlanes=render3DFrustumPlanes(this.shadowMatrix)}}setSky(topColor=hsl(.6,.8,.55),horizonColor=hsl(.6,1,.9),bottomColor=horizonColor,ambient=.5){return this.sky?.dispose(),this.sky=buildSky(topColor,horizonColor,bottomColor),this.fogColor=horizonColor.copy(),this.ambientColor=topColor.scale(ambient,1),this.ambientGroundColor=bottomColor.scale(ambient,1),this.sky}setFog(fogStart,fogEnd,fogColor){this.fogStart=fogStart,this.fogEnd=fogEnd,fogColor&&(this.fogColor=fogColor.copy())}drawBox(pos,size=1,color=WHITE,rotation){this.drawMesh(this.boxMesh,buildMatrix(pos,rotation,render3DSize3(size)),void 0,color)}drawSphere(pos,size=1,color=WHITE){this.drawMesh(this.sphereMesh,buildMatrix(pos,void 0,vec3(size)),void 0,color)}drawBillboard(pos,size=vec2(1),tileInfo,color=WHITE,angle=0,upright=!1){if(this.capture)return this.drawStripUnlit(function(pos,size,angle,upright){var size=render3DBillboardAxes(size,angle,upright),angle=size[0],upright=size[1],rz=size[2],ux=size[3],uy=size[4],size=size[5];return[vec3(pos.x-angle+ux,pos.y-upright+uy,pos.z-rz+size),vec3(pos.x-angle-ux,pos.y-upright-uy,pos.z-rz-size),vec3(pos.x+angle+ux,pos.y+upright+uy,pos.z+rz+size),vec3(pos.x+angle-ux,pos.y+upright-uy,pos.z+rz-size)]}(pos,size,angle,upright),this.cameraBack,RENDER3D_QUAD_UVS,color.copy(),tileInfo);if(this.transparentQueue){const p=pos.copy(),s=size.copy(),c=color.copy();return this.queueTransparent(p,()=>this.drawBillboard(p,s,tileInfo,c,angle,upright))}var unlit=!this.shadowPass;if(!this.blend&&this.depthTest&&this.instancing){if(!render3DCanDraw()||this.shadowPass&&!this.lighting)return;if(this.frustumCulling&&!render3DSphereVisible(pos.x,pos.y,pos.z,hypot(size.x,size.y)/2))return;var quad=this.billboardMesh,cb=this.cameraBack,uv=render3DGetTileUVs(tileInfo);render3DMeshUpload(quad);const a=render3DBillboardAxes(size,angle,upright);var k=render3DInstanceSlot(quad,render3DTextureOf(tileInfo),!1,unlit),quad=quad.instanceData;quad[k]=2*a[0],quad[k+1]=2*a[1],quad[k+2]=2*a[2],quad[k+3]=0,quad[k+4]=2*a[3],quad[k+5]=2*a[4],quad[k+6]=2*a[5],quad[k+7]=0,quad[k+8]=cb.x,quad[k+9]=cb.y,quad[k+10]=cb.z,quad[k+11]=0,quad[k+12]=pos.x,quad[k+13]=pos.y,quad[k+14]=pos.z,quad[k+15]=1,quad[k+16]=color.r,quad[k+17]=color.g,quad[k+18]=color.b,quad[k+19]=color.a,quad[k+20]=uv.x,quad[k+21]=uv.y,quad[k+22]=uv.w,void(quad[k+23]=uv.h)}else{cb=render3DBeginStrip(6,tileInfo,unlit);if(cb){const a=render3DBillboardAxes(size,angle,upright);var quad=a[0],k=a[1],uv=a[2],unlit=a[3],size=a[4],uz=a[5],x=pos.x,y=pos.y,pos=pos.z,n=this.cameraBack,color=color.rgbaInt(),u0=cb.x,v0=cb.y,u1=u0+cb.w,cb=v0+cb.h,floats=this.streamFloats,ints=this.streamInts,j=9*this.streamCount;this.streamCount+=6,render3DWriteVertex(floats,ints,j,x-quad+unlit,y-k+size,pos-uv+uz,n,u0,v0,color),render3DWriteVertex(floats,ints,j+=9,x-quad+unlit,y-k+size,pos-uv+uz,n,u0,v0,color),render3DWriteVertex(floats,ints,j+=9,x-quad-unlit,y-k-size,pos-uv-uz,n,u0,cb,color),render3DWriteVertex(floats,ints,j+=9,x+quad+unlit,y+k+size,pos+uv+uz,n,u1,v0,color),render3DWriteVertex(floats,ints,j+=9,x+quad-unlit,y+k-size,pos+uv-uz,n,u1,cb,color),render3DWriteVertex(floats,ints,j+=9,x+quad-unlit,y+k-size,pos+uv-uz,n,u1,cb,color)}}}drawQuad(a,b,c,d,tileInfo,color=WHITE){this.transparentQueue&&(a=a.copy(),b=b.copy(),c=c.copy(),d=d.copy(),color=isArray(color)?color.map(k=>k.copy()):color.copy()),this.drawStrip([a,b,d,c],render3DFaceNormal(a,b,c,d),RENDER3D_QUAD_UVS,render3DQuadValues(color),tileInfo)}drawTriangle(a,b,c,color=WHITE){this.transparentQueue&&(a=a.copy(),b=b.copy(),c=c.copy(),color=color.copy()),this.drawStrip([a,b,c],render3DFaceNormal(a,b,c),void 0,color)}drawLine(posA,posB,width=.1,color=WHITE){this.drawRibbon([posA,posB],width,void 0,color)}drawRibbon(points,width=.1,tileInfo,color=WHITE,side){var count=points.length;const strip=[],uvs=tileInfo?[]:void 0,colors=[],forward=this.cameraForward;let across=vec3(1,0,0);var loop=2<count&&points[0].distanceSquared(points[count-1])<1e-12;for(let i=0;i<count;++i){var p=points[i],w=isArray(width)?width[i]:width;let c=isArray(color)?color[i]:color;this.transparentQueue&&(c=c.copy());var s=side&&(isArray(side)?side[i]:side),next=points[i<count-1?i+1:loop?1:i],last=points[0<i?i-1:loop?count-2:i],s=s||next.subtract(last).cross(forward),next=(across=1e-12<s.lengthSquared()?s.normalize():across).scale(w/2);strip.push(p.add(next),p.subtract(next)),uvs?.push(vec2(i/(count-1),0),vec2(i/(count-1),1)),colors.push(c,c)}render3DWithState({lighting:!1,cullBackFaces:!1},()=>this.drawStrip(strip,forward.scale(-1),uvs,colors,tileInfo))}drawSoftDisc(pos,size=1,color=WHITE,normal=this.cameraBack,sides=16){if(render3DAssertBlending(),this.transparentQueue&&!this.capture){const p=pos.copy(),c=color.copy(),n=normal.copy();return this.queueTransparent(p,()=>this.drawSoftDisc(p,size,c,n,sides))}const n=normal.normalize();const u=(abs(n.y)<.9?vec3(0,1,0):vec3(1,0,0)).cross(n).normalize(),w=u.cross(n);render3DDrawSoftDisc(size/2,color,sides,n,(c,s,r)=>vec3(pos.x+(u.x*c+w.x*s)*r,pos.y+(u.y*c+w.y*s)*r,pos.z+(u.z*c+w.z*s)*r))}drawSoftShadow(pos,size=1,floorHeight=0,color=RENDER3D_SHADOW_COLOR,lift=.02){render3DAssertBlending();const heightMap=floorHeight,height=isNumber(floorHeight)?()=>floorHeight:heightMap.getHeight?(x,z)=>heightMap.getHeight(x,z):floorHeight;if(this.transparentQueue&&!this.capture){const p=pos.copy(),c=color.copy();return this.queueTransparent(vec3(p.x,height(p.x,p.z)+lift,p.z),()=>this.drawSoftShadow(p,size,floorHeight,c,lift))}render3DDrawSoftDisc(size/2,color,16,RENDER3D_DEFAULT_NORMAL,(c,s,r)=>{c=pos.x+c*r,s=pos.z+s*r;return vec3(c,height(c,s)+lift,s)})}}function render3DAssertBlending(){render3D}function render3DDrawSoftDisc(radius,color,sides,normal,pointAt){var alpha=[1,.9,.7,0],circle=function(sides){sides|=0;let circle=render3DCircleCache.get(sides);if(!circle){circle=new Float32Array(2*sides+2);for(let i=0;i<=sides;++i){var a=i/sides*2*PI;circle[2*i]=cos(a),circle[2*i+1]=sin(a)}render3DCircleCache.set(sides,circle)}return circle}(sides);for(let k=0;k<3;++k){var points=[],colors=[],c0=color.withAlpha(color.a*alpha[k]),c1=color.withAlpha(color.a*alpha[k+1]),r0=radius*k/3,r1=radius*(k+1)/3;for(let i=0;i<=sides;++i){var c=circle[2*i],s=circle[2*i+1];points.push(pointAt(c,s,r1),pointAt(c,s,r0)),colors.push(c1,c0)}render3D.drawStripUnlit(points,normal,void 0,colors)}}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(aspect){var h=this.orthographic/2,w=h*aspect;return h?Matrix4.orthographic(-w,w,-h,h,this.near,this.far):Matrix4.perspective(this.fov,aspect,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(target){this.rotation=render3DLookRotation(target.subtract(this.pos),this.rotation)}orbit(target,distance,yaw,pitch=.5){var r=cos(pitch)*distance;this.pos=target.add(vec3(sin(yaw)*r,sin(pitch)*distance,cos(yaw)*r)),this.rotation=render3DLookRotation(target.subtract(this.pos),vec3(0,yaw,0))}follow(target,offset,percent=1){this.pos=this.pos.lerp(target.add(offset),percent),this.lookAt(target)}update2D(canvasHeight=mainCanvasSize.y){var canvasHeight=canvasHeight/2/cameraScale,distance=canvasHeight/tan(this.fov/2);this.orthographic&&=2*canvasHeight,this.pos=vec3(cameraPos.x,cameraPos.y,distance),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;vec3 c0=m0.xyz,c1=m1.xyz,c2=m2.xyz;N=mat3(c0/max(dot(c0,c0),1e-20),c1/max(dot(c1,c1),1e-20),c2/max(dot(c2,c2),1e-20))*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 ambientGroundColor ambientGround.rgb\n#define lightCount extraLightCount\n#define lights extraLights\n#define lightColors extraLightColors\n";function render3DFragmentSource(fragmentCode){return"#version 300 es\nprecision highp float;uniform vec4 lightDir,lightColor,ambientFog,ambientGround,fogColor,shadowParams;uniform vec4 extraLights["+RENDER3D_MAX_LIGHTS+"],extraLightColors["+RENDER3D_MAX_LIGHTS+"];uniform vec4 extraLightCones["+RENDER3D_MAX_LIGHTS+"];uniform int extraLightCount;uniform vec3 cameraPos;uniform vec4 materialParams,emissiveTint,skyTop,skyHorizon,skyBottom,gelAxes;uniform sampler2D tex,normalTex,emissiveTex,gelTex;uniform samplerCube envMap;uniform vec4 envParams;uniform bool premultipliedTexture;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.||S.w<=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/(S.w*S.w);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.;}vec3 gel(){if(S.w<=0.)return vec3(1);vec2 g=S.xy/S.w,u=vec2(dot(gelAxes.xy,g),dot(gelAxes.zw,g))*.5+.5;return texture(gelTex,vec2(u.x,1.-u.y)).rgb;}vec3 normalMapNormal(vec3 n){vec3 p1=dFdx(P),p2=dFdy(P);vec2 t1=dFdx(T),t2=dFdy(T);vec3 a=cross(p2,n),b=cross(n,p1),u=a*t1.x+b*t2.x,v=a*t1.y+b*t2.y;float k=max(dot(u,u),dot(v,v));if(k<=0.)return n;vec3 m=texture(normalTex,T).xyz*2.-1.;m.xy*=materialParams.x;return normalize((u*m.x-v*m.y)*inversesqrt(k)+n*m.z);}"+(fragmentCode?"void mainImage(out vec4,vec2);":"")+"void main(){"+(fragmentCode?"vec4 t;mainImage(t,T);":"vec4 t=texture(tex,T);")+"if(premultipliedTexture&&t.a>0.)t.rgb/=t.a;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;if(materialParams.x!=0.)n=normalMapNormal(n);float nl=dot(n,-lightDir.xyz);float sh=shadow(),s=shadowParams.x<1.5?sh:1.;int si=int(shadowParams.x+.5)-2;vec3 l=(ambientGround.a>0.?mix(ambientGround.rgb,ambientFog.rgb,n.y*.5+.5):ambientFog.rgb)+lightColor.rgb*max(nl,0.)*s;vec3 eye=lightColor.a>0.?normalize(cameraPos-P):vec3(0),sp=vec3(0);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);v/=max(d,1e-6);float ln=dot(n,v);vec3 lc=extraLightColors[i].rgb*extraLightColors[i].a*a*a;vec4 K=extraLightCones[i];float k=clamp(dot(K.xyz,-v)-K.w,0.,1.);lc*=k*k*(3.-2.*k);if(i==si)lc*=sh*gel();l+=lc*max(0.,ln);if(lightColor.a>0.)sp+=lc*pow(max(dot(reflect(-v,n),eye),0.),materialParams.y)*step(0.,ln);}c.rgb*=l*(1.-e)+e;if(lightColor.a>0.){vec3 r=reflect(lightDir.xyz,n);c.rgb+=lightColor.rgb*pow(max(dot(r,eye),0.),materialParams.y)*lightColor.a*step(0.,nl)*s*(1.-e)+sp*lightColor.a*(1.-e);}if(materialParams.z>0.){vec3 w=normalize(P-cameraPos),q=reflect(w,n);float f=materialParams.z+(1.-materialParams.z)*pow(clamp(1.-dot(n,-w),0.,1.),5.);c.rgb=mix(c.rgb,envParams.x>0.?textureLod(envMap,q,clamp(max(envParams.z-.5*log2(materialParams.y+2.),envParams.w+log2(max(max(length(dFdx(q)),length(dFdy(q))),1e-9))),0.,envParams.y)).rgb:q.y>0.?mix(skyHorizon.rgb,skyTop.rgb,q.y):mix(skyHorizon.rgb,skyBottom.rgb,-q.y),f);}}else c.rgb*=e;if(emissiveTint.a>0.)c.rgb+=texture(emissiveTex,T).rgb*emissiveTint.rgb;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));}if(shadowParams.w>0.&&t.a<.5)discard;o=vec4(c.rgb,shadowParams.w>0.?1.:c.a);}"+(fragmentCode?"\n"+RENDER3D_SNIPPET_NAMES+fragmentCode+"\n":"")}function render3DUseProgram(program){var gl=glContext,r=render3D,c=(gl.useProgram(r.currentProgram=program),r.uniformValues={},gl.uniformMatrix4fv(render3DUniform("viewProj"),!1,r.viewProjection.m),gl.uniformMatrix4fv(render3DUniform("lightViewProj"),!1,r.shadowMatrix.m),gl.uniform1i(render3DUniform("tex"),0),gl.uniform1i(render3DUniform("shadowMap"),1),gl.uniform1i(render3DUniform("normalTex"),2),gl.uniform1i(render3DUniform("emissiveTex"),3),gl.uniform1i(render3DUniform("gelTex"),4),gl.uniform1i(render3DUniform("envMap"),5),gl.uniform4fv(render3DUniform("gelAxes"),r.gelAxes),r.camera.pos);gl.uniform3f(render3DUniform("cameraPos"),c.x,c.y,c.z),gl.uniform1i(render3DUniform("extraLightCount"),r.lightCount),r.lightCount&&(gl.uniform4fv(render3DUniform("extraLights"),r.lightPositions,0,4*r.lightCount),gl.uniform4fv(render3DUniform("extraLightColors"),r.lightColors,0,4*r.lightCount),gl.uniform4fv(render3DUniform("extraLightCones"),r.lightCones,0,4*r.lightCount)),program!==r.program&&(gl.uniform1f(render3DUniform("iTime"),time),c=r.capturingCube?.size,gl.uniform3f(render3DUniform("iResolution"),c||glCanvas.width,c||glCanvas.height,1))}function render3DInitGL(){if(!headlessMode)if(glEnable&&glContext){var gl=glContext,r=render3D;if(glFlush(),r.uniforms=new WeakMap,r.uniformValues={},render3DShadowCut=void 0,r.attribValues=[],r.program=glCreateProgram(RENDER3D_VERTEX_SOURCE,render3DFragmentSource()),r.shadowShader=glCreateProgram("#version 300 es\nprecision highp float;uniform mat4 viewProj;"+RENDER3D_VERTEX_INPUTS+"out vec2 T;out float A;void main(){T=uvRect.xy+t*uvRect.zw;A=c.a*tint.a;gl_Position=viewProj*mat4(m0,m1,m2,m3)*vec4(p,1.);}","#version 300 es\nprecision highp float;uniform sampler2D tex;uniform float cut;in vec2 T;in float A;void main(){if(texture(tex,T).a*mix(1.,A,cut)<.5)discard;}"),glFailedPrograms.has(r.program)||glFailedPrograms.has(r.shadowShader))console.error("Render3DPlugin: its shaders did not build on this device, so nothing 3D is drawn"),r.program=r.shadowShader=void 0;else{r.vao=gl.createVertexArray(),gl.bindVertexArray(r.vao);for(var[location]of RENDER3D_ATTRIBS)gl.enableVertexAttribArray(location);r.streamBuffer=gl.createBuffer(),gl.bindBuffer(gl.ARRAY_BUFFER,r.streamBuffer),gl.bufferData(gl.ARRAY_BUFFER,r.streamData.byteLength,gl.DYNAMIC_DRAW),r.streamCount=0,r.instanceBuffers=[gl.createBuffer(),gl.createBuffer(),gl.createBuffer()],r.whiteTexture=glCreateTexture(),r.samplers=new Map,r.samplerKey=void 0,render3DUpdateShadowMap(1),gl.bindBuffer(gl.ARRAY_BUFFER,glArrayBuffer),glSetInstancedMode(!0)}}else console.warn(glDeviceFailed?"Render3DPlugin: this device can not draw WebGL, so nothing 3D is drawn":"Render3DPlugin: WebGL not enabled, construct the plugin in gameInit with glEnable set")}class CubeMap{constructor(size,faces){this.size=size,this.faces=faces,this.capturePos=void 0,this.glTexture=void 0,this.contextGeneration=-1}capture(pos3D){this.capturePos=pos3D.copy(),render3D.cubeCaptures.add(this)}dispose(){this.glTexture&&glContext?.deleteTexture(this.glTexture),this.glTexture=void 0,render3D?.cubeCaptures.delete(this)}}function makeCubeMap(size,colorOf){var faces=[];for(let face=0;face<6;++face){var data=new Uint8Array(size*size*4);for(let y=0;y<size;++y)for(let x=0;x<size;++x){var color=colorOf(function(face,s,t){face=0===face?vec3(1,-t,-s):1===face?vec3(-1,-t,s):2===face?vec3(s,1,t):3===face?vec3(s,-1,-t):4===face?vec3(s,-t,1):vec3(-s,-t,-1);return face.normalize()}(face,(x+.5)/size*2-1,(y+.5)/size*2-1)),i=4*(x+y*size);data[i]=255*clamp(color.r)+.5|0,data[1+i]=255*clamp(color.g)+.5|0,data[2+i]=255*clamp(color.b)+.5|0,data[3+i]=255*clamp(color.a)+.5|0}faces.push(data)}return new CubeMap(size,faces)}async function loadCubeMap(sources){const images=await Promise.all(sources.map(src=>new Promise((resolve,reject)=>{const image=new Image;image.crossOrigin="anonymous",image.onload=()=>resolve(image),image.onerror=()=>reject(new Error("loadCubeMap could not load "+src)),image.src=src}))),size=images[0].width;if(images.every(image=>image.width===size&&image.height===size))return sources=[1,0,2,3,4,5].map(i=>{var context=createCanvasContext(size);return context.scale(-1,1),context.drawImage(images[i],-size,0),context.canvas}),new CubeMap(size,sources);throw new Error("loadCubeMap needs six square images of one size")}function render3DCubeTexture(cube){const gl=glContext;if(cube&&gl){if(cube.glTexture&&cube.contextGeneration===render3D.contextGeneration)return cube.glTexture;var texture=cube.glTexture=gl.createTexture();if(cube.contextGeneration=render3D.contextGeneration,gl.bindTexture(gl.TEXTURE_CUBE_MAP,texture),gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL,!1),cube.faces)cube.faces.forEach((face,i)=>face instanceof Uint8Array?gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X+i,0,gl.RGBA,cube.size,cube.size,0,gl.RGBA,gl.UNSIGNED_BYTE,face):gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X+i,0,gl.RGBA,gl.RGBA,gl.UNSIGNED_BYTE,face));else{cube.size=min(cube.size,gl.getParameter(gl.MAX_CUBE_MAP_TEXTURE_SIZE),gl.getParameter(gl.MAX_RENDERBUFFER_SIZE));for(let i=0;i<6;++i)gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X+i,0,gl.RGBA,cube.size,cube.size,0,gl.RGBA,gl.UNSIGNED_BYTE,null);cube.capturePos&&render3D.cubeCaptures.add(cube)}return gl.texParameteri(gl.TEXTURE_CUBE_MAP,gl.TEXTURE_MIN_FILTER,gl.LINEAR_MIPMAP_LINEAR),gl.texParameteri(gl.TEXTURE_CUBE_MAP,gl.TEXTURE_MAG_FILTER,gl.LINEAR),gl.texParameteri(gl.TEXTURE_CUBE_MAP,gl.TEXTURE_WRAP_S,gl.CLAMP_TO_EDGE),gl.texParameteri(gl.TEXTURE_CUBE_MAP,gl.TEXTURE_WRAP_T,gl.CLAMP_TO_EDGE),gl.generateMipmap(gl.TEXTURE_CUBE_MAP),texture}}function render3DCaptureCubes(objects){var gl=glContext,r=render3D,camera=r.camera,captures=[...r.cubeCaptures],view=(r.cubeCaptures.clear(),r.captureFramebuffer||(r.captureFramebuffer=gl.createFramebuffer(),r.captureDepth=gl.createRenderbuffer(),r.captureDepthSize=0),new Camera3D);view.fov=PI/2,view.near=!camera.orthographic&&0<camera.near?camera.near:.1,view.far=camera.far;let faceMatrix=new Matrix4;view.getMatrix=()=>faceMatrix.copy(),gl.bindFramebuffer(gl.FRAMEBUFFER,r.captureFramebuffer);try{for(const cube of captures){var texture=render3DCubeTexture(cube),size=cube.size;r.cubeCaptures.delete(cube),r.captureDepthSize!==size&&(gl.bindRenderbuffer(gl.RENDERBUFFER,r.captureDepth),gl.renderbufferStorage(gl.RENDERBUFFER,gl.DEPTH_COMPONENT24,size,size),gl.framebufferRenderbuffer(gl.FRAMEBUFFER,gl.DEPTH_ATTACHMENT,gl.RENDERBUFFER,r.captureDepth),r.captureDepthSize=size),r.capturingCube=cube,gl.activeTexture(gl.TEXTURE0+5),gl.bindTexture(gl.TEXTURE_CUBE_MAP,null),gl.activeTexture(gl.TEXTURE0),render3DBoundEnvironment=null,gl.viewport(0,0,size,size),view.pos=cube.capturePos,r.camera=view;for(let face=0;face<6;++face){gl.framebufferTexture2D(gl.FRAMEBUFFER,gl.COLOR_ATTACHMENT0,gl.TEXTURE_CUBE_MAP_POSITIVE_X+face,texture,0),faceMatrix=function(face,pos){var[face,up,forward]=[[vec3(0,0,-1),vec3(0,-1,0),vec3(1,0,0)],[vec3(0,0,1),vec3(0,-1,0),vec3(-1,0,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(1,0,0),vec3(0,-1,0),vec3(0,0,1)],[vec3(-1,0,0),vec3(0,-1,0),vec3(0,0,-1)]][face];return new Matrix4([face.x,face.y,face.z,0,up.x,up.y,up.z,0,-forward.x,-forward.y,-forward.z,0,pos.x,pos.y,pos.z,1])}(face,cube.capturePos),r.updateMatrices(1),(face<2||3<face)&&(r.cameraRight=r.cameraRight.scale(-1),r.cameraUp=r.cameraUp.scale(-1));var c=canvasClearColor;gl.clearColor(c.r,c.g,c.b,1),gl.depthMask(!0),gl.clear(gl.COLOR_BUFFER_BIT|gl.DEPTH_BUFFER_BIT),render3DUseProgram(r.program),r.renderStages(objects,!0)}gl.framebufferTexture2D(gl.FRAMEBUFFER,gl.COLOR_ATTACHMENT0,gl.TEXTURE_2D,null,0),gl.bindTexture(gl.TEXTURE_CUBE_MAP,texture),gl.generateMipmap(gl.TEXTURE_CUBE_MAP),gl.bindTexture(gl.TEXTURE_CUBE_MAP,null)}}finally{r.capturingCube=void 0,r.camera=camera,r.updateMatrices(),gl.bindFramebuffer(gl.FRAMEBUFFER,null),gl.viewport(0,0,glCanvas.width,glCanvas.height),gl.activeTexture(gl.TEXTURE0+5),gl.bindTexture(gl.TEXTURE_CUBE_MAP,render3DCubeTexture(r.environment)||null),gl.activeTexture(gl.TEXTURE0),render3DBoundEnvironment=r.environment||null}}function render3DContextLost(){var r=render3D;r.program=r.currentProgram=r.shadowShader=r.vao=r.streamBuffer=r.whiteTexture=void 0,r.skyBoxProgram=r.skyBoxVao=r.captureFramebuffer=r.captureDepth=void 0,r.captureDepthSize=0;for(const shader of glShaderObjects)shader.program3D=void 0;r.lightCount=0,r.instanceBuffers=[],r.samplers=new Map,r.samplerKey=void 0,render3DClearInstances(),r.shadowFramebuffer=r.shadowTexture=void 0,r.shadowTextureSize=0,r.cameraDepthFramebuffer=r.cameraDepthTexture=void 0,r.cameraDepthWidth=r.cameraDepthHeight=0,r.streamCount=0,++r.contextGeneration}function render3DContextRestored(){render3DInitGL()}function render3DUniform(name,program=render3D.currentProgram){var u=render3D.uniforms;let cache=u.get(program);return cache||u.set(program,cache={}),cache[name]??=glContext.getUniformLocation(program,name)}function render3DAttrib4f(location,x,y,z,w){var last=render3D.attribValues[location]||=[NaN,NaN,NaN,NaN];last[0]===x&&last[1]===y&&last[2]===z&&last[3]===w||(last[0]=x,last[1]=y,last[2]=z,last[3]=w,glContext.vertexAttrib4f(location,x,y,z,w))}function render3DBindTexture(tileInfo,state=render3D,unit=0){var gl=glContext,r=render3D,tileInfo=render3DTextureOf(tileInfo),texture=tileInfo?.glTexture||r.whiteTexture;unit&&gl.activeTexture(gl.TEXTURE0+unit),gl.bindTexture(gl.TEXTURE_2D,texture),texture===r.whiteTexture||!r.mipmaps&&!state.pixelated?gl.bindSampler(unit,null):(gl.bindSampler(unit,function(wrap,pixelated,texturePixelated=tilesPixelated){var gl=glContext,r=render3D,[wrap,wrapT]=glWrapModes(wrap),key=1e5*wrap+4*wrapT+(pixelated?1:0)+(texturePixelated?2:0),sampler=r.samplers.get(key);return sampler||(sampler=gl.createSampler(),gl.samplerParameteri(sampler,gl.TEXTURE_MAG_FILTER,pixelated||texturePixelated?gl.NEAREST:gl.LINEAR),gl.samplerParameteri(sampler,gl.TEXTURE_MIN_FILTER,pixelated?gl.NEAREST:texturePixelated?gl.NEAREST_MIPMAP_LINEAR:gl.LINEAR_MIPMAP_LINEAR),gl.samplerParameteri(sampler,gl.TEXTURE_WRAP_S,wrap),gl.samplerParameteri(sampler,gl.TEXTURE_WRAP_T,wrapT),(texturePixelated=gl.getExtension("EXT_texture_filter_anisotropic"))&&!pixelated&&(wrap=gl.getParameter(texturePixelated.MAX_TEXTURE_MAX_ANISOTROPY_EXT),gl.samplerParameterf(sampler,texturePixelated.TEXTURE_MAX_ANISOTROPY_EXT,clamp(r.anisotropy,1,wrap))),r.samplers.set(key,sampler)),sampler}(tileInfo?.wrap,state.pixelated,tileInfo?.pixelated??tilesPixelated)),glUpdateMipmaps(texture),state.pixelated||glMipmappedTextures.has(texture)||(glMipmappedTextures.add(texture),gl.generateMipmap(gl.TEXTURE_2D))),unit&&gl.activeTexture(gl.TEXTURE0)}function render3DSetMaterialUniforms(state){var r=render3D,loaded=map=>render3DTextureOf(map)?.glTexture?map:void 0,normalMap=state.normalScale?loaded(state.normalMap):void 0,loaded=loaded(state.emissiveMap),ec=(render3DUniform4f("materialParams",normalMap?state.normalScale:0,max(state.shininess,.001),state.reflectivity,0),state.emissiveMapColor||WHITE);if(loaded?render3DUniform4f("emissiveTint",ec.r,ec.g,ec.b,1):render3DUniform4f("emissiveTint",0,0,0,0),render3DBindMap(2,normalMap,state),render3DBindMap(3,loaded,state),0<state.reflectivity){let environment=state.environmentMap||r.environment;environment===r.capturingCube&&(environment=void 0),ec=environment,normalMap=glContext,ec&&ec!==render3DBoundEnvironment&&normalMap&&(normalMap.activeTexture(normalMap.TEXTURE0+5),normalMap.bindTexture(normalMap.TEXTURE_CUBE_MAP,render3DCubeTexture(ec)||null),normalMap.activeTexture(normalMap.TEXTURE0),render3DBoundEnvironment=ec);loaded=environment?environment.size:1,state=(render3DUniform4f("envParams",environment?1:0,max(0,log2(loaded)-2),log2(.45*loaded),log2(2*loaded/PI)),r.sky&&render3DSkyColors.get(r.sky)),normalMap=r.ambientColor,ec=r.ambientGroundColor||normalMap,loaded=state?state[0]:normalMap,r=state?state[2]:ec;render3DUniform4f("skyTop",loaded.r,loaded.g,loaded.b,1),render3DUniform4f("skyBottom",r.r,r.g,r.b,1),state?render3DUniform4f("skyHorizon",state[1].r,state[1].g,state[1].b,1):render3DUniform4f("skyHorizon",(normalMap.r+ec.r)/2,(normalMap.g+ec.g)/2,(normalMap.b+ec.b)/2,1)}}let render3DBoundEnvironment;let render3DBoundMaps=[];function render3DBindMap(unit,map,state){var bound=render3DBoundMaps,pixelated=!!map&&state.pixelated;bound[unit]===map&&bound[unit+2]===pixelated||(bound[unit]=map,bound[unit+2]=pixelated,render3DBindTexture(map,state,unit))}function render3DUniform4f(name,x,y,z,w){var last=render3D.uniformValues[name]||=[NaN,NaN,NaN,NaN];last[0]===x&&last[1]===y&&last[2]===z&&last[3]===w||(last[0]=x,last[1]=y,last[2]=z,last[3]=w,glContext.uniform4f(render3DUniform(name),x,y,z,w))}function render3DBindVertexBuffer(buffer){var gl=glContext;gl.bindBuffer(gl.ARRAY_BUFFER,buffer);for(const a of RENDER3D_ATTRIBS)gl.vertexAttribPointer(a[0],a[1],a[2],a[3],RENDER3D_VERTEX_BYTES,a[4])}function render3DBindMesh(mesh){render3DBindVertexBuffer(mesh.buffer),glContext.bindBuffer(glContext.ELEMENT_ARRAY_BUFFER,mesh.indexBuffer)}function render3DTextureOf(tileInfo){return tileInfo instanceof TileInfo?tileInfo.textureInfo:tileInfo}const render3DLevel=[];function render3DLevelLeave(o){o=render3DLevel.indexOf(o);0<=o&&render3DLevel.splice(o,1)}const render3DTileUVRect={x:0,y:0,w:1,h:1},render3DWholeTiles=new WeakSet;function render3DGetTileUVs(tileInfo){var inv,rect,bleedX,bleedY;return tileInfo instanceof TileInfo&&tileInfo.textureInfo&&!render3DWholeTiles.has(tileInfo)?(inv=tileInfo.textureInfo.sizeInverse,rect=render3DTileUVRect,bleedX=inv.x*tileInfo.bleed,bleedY=inv.y*tileInfo.bleed,rect.x=tileInfo.pos.x*inv.x+bleedX,rect.y=tileInfo.pos.y*inv.y+bleedY,rect.w=tileInfo.size.x*inv.x-2*bleedX,rect.h=tileInfo.size.y*inv.y-2*bleedY,rect):RENDER3D_FULL_UV_RECT}function render3DSetDrawUniforms(matrix,tileInfo,tint,uvRect,state=render3D){var fc,gl=glContext,r=render3D;uvRect||=render3DGetTileUVs(tileInfo),function(m,tint,uvRect){var gl=glContext;gl.vertexAttrib4f(4,m[0],m[1],m[2],m[3]),gl.vertexAttrib4f(5,m[4],m[5],m[6],m[7]),gl.vertexAttrib4f(6,m[8],m[9],m[10],m[11]),gl.vertexAttrib4f(7,m[12],m[13],m[14],m[15]),render3DAttrib4f(11,tint.r,tint.g,tint.b,tint.a),render3DAttrib4f(12,uvRect.x,uvRect.y,uvRect.w,uvRect.h)}(matrix.m,tint,uvRect),render3DBindTexture(tileInfo,state),r.shadowPass?(matrix=state.blend?1:0,render3DShadowCut!==matrix&&glContext.uniform1f(render3DUniform("cut",r.shadowShader),render3DShadowCut=matrix)):((uvRect=state.shader?((tint=state.shader).program3D||glShaderTrack(tint),tint=tint.program3D||=glCreateProgram(RENDER3D_VERTEX_SOURCE,render3DFragmentSource(tint.fragmentCode)),glFailedPrograms.has(tint)?render3D.program:tint):r.program)!==r.currentProgram&&render3DUseProgram(uvRect),state.blend?(gl.enable(gl.BLEND),matrix=state.additive?gl.ONE:gl.ONE_MINUS_SRC_ALPHA,gl.blendFuncSeparate(gl.SRC_ALPHA,matrix,gl.ONE,matrix)):gl.disable(gl.BLEND),state.depthTest?gl.enable(gl.DEPTH_TEST):gl.disable(gl.DEPTH_TEST),gl.depthMask(state.depthWrite),state.cullBackFaces?gl.enable(gl.CULL_FACE):gl.disable(gl.CULL_FACE),gl.frontFace(state.mirrored?gl.CCW:gl.CW),uvRect=-((tint=r.sunDirection).length()||1),matrix=r.sunColor,gl=r.ambientColor,fc=r.fogColor||canvasClearColor,render3DUniform4f("lightDir",tint.x/uvRect,tint.y/uvRect,tint.z/uvRect,state.lighting?state.emissive:1),render3DUniform4f("lightColor",matrix.r,matrix.g,matrix.b,state.specular),render3DUniform4f("ambientFog",gl.r,gl.g,gl.b,r.fogEnd),(tint=r.ambientGroundColor)?render3DUniform4f("ambientGround",tint.r,tint.g,tint.b,1):render3DUniform4f("ambientGround",0,0,0,0),render3DUniform4f("fogColor",fc.r,fc.g,fc.b,r.fogEnd?min(r.fogStart,r.fogEnd-.001):r.fogStart),uvRect=state.blend?state.additive?-1:0:1,render3DUniform4f("shadowParams",(matrix=r.shadows&&r.passIsDefault&&state.receiveShadow?1:0)&&0<=r.shadowLightIndex?2+r.shadowLightIndex:matrix,r.shadowDepthBias,r.shadowSoftness/r.shadowTextureSize,uvRect),gl=render3DTextureOf(tileInfo),tint="premultipliedTexture",fc=+!(!gl?.glTexture||!glPremultipliedTextures.has(gl.glTexture)),(matrix=render3D.uniformValues)[tint]!==fc&&(matrix[tint]=fc,glContext.uniform1i(render3DUniform(tint),fc)),render3DSetMaterialUniforms(state))}function render3DFrustumPlanes(matrix){var m=matrix.m,planes=[];for(let i=0;i<3;++i)for(const sign of[1,-1]){var p=[m[3]+sign*m[i],m[7]+sign*m[4+i],m[11]+sign*m[8+i],m[15]+sign*m[12+i]];const l=hypot(p[0],p[1],p[2])||1;planes.push(p.map(v=>v/l))}return planes}function render3DPreRender(){var r=render3D;r.updateMatrices(),render3DRenderPass(r.shadowMapDrawn=!1)}function render3DRender(){render3DRenderPass(!0)}function render3DRenderPass(after2D){const gl=glContext,r=render3D;if(r.program&&glEnable){!function(){var r=render3D,key=tilesPixelated+" "+r.anisotropy;if(r.samplerKey!==key){r.samplerKey=key;for(const sampler of r.samplers.values())glContext.deleteSampler(sampler);r.samplers.clear()}}();var isDefault=after2D===!!r.renderAfter2D,after2D=render3DLayerObjects(after2D);if(isDefault||after2D.length){r.passIsDefault=isDefault,glFlush(),r.streamCount=0,r.capture=r.transparentQueue=void 0,render3DClearInstances();var lights=function(){var lights=engineObjects.filter(o=>!o.destroyed&&o instanceof Light3D&&0<o.color.a&&0<o.intensity&&(o.directional||0<o.radius));if(lights.length>RENDER3D_MAX_LIGHTS){const cameraPos=render3D.camera.pos,distances=new Map;for(const light of lights)distances.set(light,light.directional?-1:light.getWorldPos3D().distanceSquared(cameraPos));lights.sort((a,b)=>distances.get(a)-distances.get(b));var caster=render3DShadowCaster();caster&&lights.indexOf(caster)>=RENDER3D_MAX_LIGHTS&&(lights[RENDER3D_MAX_LIGHTS-1]=caster),lights.length=RENDER3D_MAX_LIGHTS}return lights}(),caster=(r.lightCount=lights.length,render3DShadowCaster());r.shadowLightIndex=caster?lights.indexOf(caster):-1;const positions=r.lightPositions,colors=r.lightColors;lights.forEach((light,i)=>{var p=light.directional?light.getWorldPos3D().normalize():light.getWorldPos3D(),c=light.color,i=4*i;positions[i]=p.x,positions[1+i]=p.y,positions[2+i]=p.z,positions[3+i]=light.directional?-1:max(0,light.radius),colors[i]=c.r,colors[1+i]=c.g,colors[2+i]=c.b,colors[3+i]=c.a*light.intensity,r.lightCones.set(render3DLightCone(light),i)}),gl.bindVertexArray(r.vao),gl.frontFace(gl.CW),gl.activeTexture(gl.TEXTURE1),gl.bindTexture(gl.TEXTURE_2D,r.shadowTexture||null),render3DSetMapUnits(r.whiteTexture);caster=render3DTextureOf(render3DShadowCaster()?.gel)?.glTexture;caster&&(gl.activeTexture(gl.TEXTURE4),gl.bindTexture(gl.TEXTURE_2D,caster),gl.activeTexture(gl.TEXTURE0)),gl.depthMask(!0),gl.clear(gl.DEPTH_BUFFER_BIT),r.isRendering=!0;try{r.shadows&&!r.shadowMapDrawn&&(!function(){var gl=glContext,r=render3D;render3DUpdateShadowMap(0|r.shadowMapSize),r.updateShadowMatrix(),gl.activeTexture(gl.TEXTURE1),gl.bindTexture(gl.TEXTURE_2D,null),gl.activeTexture(gl.TEXTURE0),gl.bindFramebuffer(gl.FRAMEBUFFER,r.shadowFramebuffer),gl.viewport(0,0,r.shadowTextureSize,r.shadowTextureSize),gl.clear(gl.DEPTH_BUFFER_BIT),gl.useProgram(r.shadowShader),gl.uniformMatrix4fv(render3DUniform("viewProj",r.shadowShader),!1,r.shadowMatrix.m),gl.enable(gl.DEPTH_TEST),gl.depthMask(!0),gl.disable(gl.BLEND),gl.disable(gl.CULL_FACE),r.shadowPass=!0;try{render3DDrawObjects(render3DLayerObjects(!!r.renderAfter2D).filter(o=>render3DSetting(o,"castShadow")&&!render3DSetting(o,"additive")&&(!o.transparent||o.tileInfo))),r.onRenderOpaque?.(),r.flush(),render3DFlushInstances()}finally{r.shadowPass=!1,gl.bindFramebuffer(gl.FRAMEBUFFER,null),gl.viewport(0,0,glCanvas.width,glCanvas.height),gl.activeTexture(gl.TEXTURE1),gl.bindTexture(gl.TEXTURE_2D,r.shadowTexture),gl.activeTexture(gl.TEXTURE0)}}(),r.shadowMapDrawn=!0),r.depthTexture&&isDefault&&function(){var gl=glContext,r=render3D,width=glCanvas.width,height=glCanvas.height;(function(width,height){var gl=glContext,r=render3D;var texture,framebuffer;r.cameraDepthTexture&&r.cameraDepthWidth===width&&r.cameraDepthHeight===height||(r.cameraDepthTexture&&gl.deleteTexture(r.cameraDepthTexture),r.cameraDepthFramebuffer&&gl.deleteFramebuffer(r.cameraDepthFramebuffer),texture=r.cameraDepthTexture=gl.createTexture(),gl.bindTexture(gl.TEXTURE_2D,texture),gl.texImage2D(gl.TEXTURE_2D,0,gl.DEPTH_COMPONENT24,width,height,0,gl.DEPTH_COMPONENT,gl.UNSIGNED_INT,null),gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MIN_FILTER,gl.NEAREST),gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MAG_FILTER,gl.NEAREST),gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_WRAP_S,gl.CLAMP_TO_EDGE),gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_WRAP_T,gl.CLAMP_TO_EDGE),gl.bindTexture(gl.TEXTURE_2D,null),framebuffer=r.cameraDepthFramebuffer=gl.createFramebuffer(),gl.bindFramebuffer(gl.FRAMEBUFFER,framebuffer),gl.framebufferTexture2D(gl.FRAMEBUFFER,gl.DEPTH_ATTACHMENT,gl.TEXTURE_2D,texture,0),gl.drawBuffers([gl.NONE]),gl.readBuffer(gl.NONE),gl.bindFramebuffer(gl.FRAMEBUFFER,null),r.cameraDepthWidth=width,r.cameraDepthHeight=height)})(width,height),gl.bindFramebuffer(gl.FRAMEBUFFER,r.cameraDepthFramebuffer),gl.viewport(0,0,width,height),gl.clear(gl.DEPTH_BUFFER_BIT),gl.useProgram(r.shadowShader),gl.uniformMatrix4fv(render3DUniform("viewProj",r.shadowShader),!1,r.viewProjection.m),gl.enable(gl.DEPTH_TEST),gl.depthMask(!0),gl.disable(gl.BLEND),gl.disable(gl.CULL_FACE),r.shadowPass=r.depthPass=!0;try{render3DDrawObjects(render3DLayerObjects(!!r.renderAfter2D).filter(o=>!render3DSetting(o,"additive")&&(!o.transparent||o.tileInfo))),r.onRenderOpaque?.(),r.flush(),render3DFlushInstances()}finally{r.shadowPass=r.depthPass=!1,gl.bindFramebuffer(gl.FRAMEBUFFER,null),gl.viewport(0,0,width,height)}}(),isDefault&&r.cubeCaptures.size&&render3DCaptureCubes(after2D),render3DUseProgram(r.program),r.renderStages(after2D,isDefault)}finally{r.isRendering=!1,r.currentProgram=void 0,r.streamCount=0,r.capture=r.transparentQueue=void 0,gl.disable(gl.DEPTH_TEST),gl.disable(gl.CULL_FACE),gl.depthMask(!0),gl.frontFace(gl.CCW),gl.bindSampler(0,null),render3DSetMapUnits(null),glActiveTexture&&gl.bindTexture(gl.TEXTURE_2D,glActiveTexture),gl.bindBuffer(gl.ARRAY_BUFFER,glArrayBuffer),glSetInstancedMode(!0)}}}}function render3DSetMapUnits(texture){var gl=glContext;for(const unit of[2,3,4])gl.activeTexture(gl.TEXTURE0+unit),gl.bindTexture(gl.TEXTURE_2D,texture),gl.bindSampler(unit,null);gl.activeTexture(gl.TEXTURE0+5),gl.bindTexture(gl.TEXTURE_CUBE_MAP,texture&&render3DCubeTexture(render3D.environment)||null),gl.bindSampler(5,null),render3DBoundEnvironment=texture&&render3D.environment||null,gl.activeTexture(gl.TEXTURE0),render3DBoundMaps=[]}function render3DUpdateShadowMap(size){var texture,framebuffer,gl=glContext,r=render3D;size=glClampTextureSize(size),r.shadowTexture&&r.shadowTextureSize===size||(r.shadowTexture&&gl.deleteTexture(r.shadowTexture),r.shadowFramebuffer&&gl.deleteFramebuffer(r.shadowFramebuffer),texture=r.shadowTexture=gl.createTexture(),gl.activeTexture(gl.TEXTURE1),gl.bindTexture(gl.TEXTURE_2D,texture),gl.texImage2D(gl.TEXTURE_2D,0,gl.DEPTH_COMPONENT24,size,size,0,gl.DEPTH_COMPONENT,gl.UNSIGNED_INT,null),gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MIN_FILTER,gl.LINEAR),gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MAG_FILTER,gl.LINEAR),gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_WRAP_S,gl.CLAMP_TO_EDGE),gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_WRAP_T,gl.CLAMP_TO_EDGE),gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_COMPARE_MODE,gl.COMPARE_REF_TO_TEXTURE),gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_COMPARE_FUNC,gl.LEQUAL),gl.activeTexture(gl.TEXTURE0),framebuffer=r.shadowFramebuffer=gl.createFramebuffer(),gl.bindFramebuffer(gl.FRAMEBUFFER,framebuffer),gl.framebufferTexture2D(gl.FRAMEBUFFER,gl.DEPTH_ATTACHMENT,gl.TEXTURE_2D,texture,0),gl.drawBuffers([gl.NONE]),gl.readBuffer(gl.NONE),gl.bindFramebuffer(gl.FRAMEBUFFER,null),r.shadowTextureSize=size)}function render3DBeginStrip(count,tileInfo,unlit=!1){var textureInfo,r=render3D;if(render3DCanDraw()&&((!r.shadowPass||r.lighting)&&!(32768<count)))return textureInfo=render3DTextureOf(tileInfo),r.streamCount&&(textureInfo!==r.streamTileInfo||r.streamUnlit!==unlit||render3DStateChanged(r.streamState)||32768<r.streamCount+count)&&r.flush(),render3DFlushBeforeOverlay(),r.streamCount||(r.streamState=render3DCaptureBatchState()),r.streamUnlit=unlit,r.streamTileInfo=textureInfo,render3DGetTileUVs(tileInfo)}function render3DFlushBeforeOverlay(){var r=render3D;r.depthTest||r.shadowPass||!r.instanceMeshes.length||(r.flush(),render3DFlushInstances())}function render3DMeshUpload(mesh){mesh.buffer&&!mesh.dirty&&mesh.contextGeneration===render3D.contextGeneration||mesh.upload()}function render3DWorldSize(o,m){o=o.size3D;return vec3(o.x*hypot(m[0],m[1],m[2]),o.y*hypot(m[4],m[5],m[6]),o.z*hypot(m[8],m[9],m[10]))}function render3DSlopeNormal(heightFunction,x,z,ex,ez,halfX,halfZ){var x0=max(x-ex,-halfX),ex=min(x+ex,halfX),halfX=max(z-ez,-halfZ),ez=min(z+ez,halfZ),halfZ=(heightFunction(ex,z)-heightFunction(x0,z))/(ex-x0||1),z=(heightFunction(x,ez)-heightFunction(x,halfX))/(ez-halfX||1),ex=1/hypot(halfZ,1,z);return vec3(-halfZ*ex,ex,-z*ex)}const render3DBillboardAxesScratch=new Float64Array(6);function render3DBillboardAxes(size,angle,upright){var r=render3D.cameraRight,u=render3D.cameraUp;let rx=r.x,ry=r.y,rz=r.z,ux=u.x,uy=u.y,uz=u.z;upright&&(r=hypot(rx,rz),rx=r?rx/r:1,ry=0,rz=r?rz/r:0,ux=0,uy=1,uz=0);u=cos(angle),upright=sin(angle),r=size.x/2,angle=size.y/2,size=render3DBillboardAxesScratch;return size[0]=(rx*u+ux*upright)*r,size[1]=(ry*u+uy*upright)*r,size[2]=(rz*u+uz*upright)*r,size[3]=(ux*u-rx*upright)*angle,size[4]=(uy*u-ry*upright)*angle,size[5]=(uz*u-rz*upright)*angle,size}function render3DStripCount(n){return n+2+(1&n)}function render3DStripIndex(k,n){return k<1?0:k<=n?k-1:n-1}function render3DWriteVertex(floats,ints,j,x,y,z,n,u,v,rgba){floats[j]=x,floats[j+1]=y,floats[j+2]=z,floats[j+3]=n.x,floats[j+4]=n.y,floats[j+5]=n.z,floats[j+6]=u,floats[j+7]=v,ints[j+8]=rgba}function render3DFlipTriangles(indices){for(let t=0;t<indices.length;t+=3){var b=indices[t+1];indices[t+1]=indices[t+2],indices[t+2]=b}return indices}function render3DStripTriangles(count,remap,place){var indices=[];for(let i=2;i<count;++i){var a=1&i?i-1:i-2,b=1&i?i-2:i-1;place[a]!=place[b]&&place[b]!=place[i]&&place[a]!=place[i]&&indices.push(remap[a],remap[b],remap[i])}return indices}function render3DMeshVertexData(mesh,vertices,data=new ArrayBuffer(vertices.length*RENDER3D_VERTEX_BYTES)){var count=vertices.length,floats=new Float32Array(data),ints=new Uint32Array(data);let r=0,x0=1/0,y0=1/0,z0=1/0,x1=-1/0,y1=-1/0,z1=-1/0;for(let j=0;j<count;++j){var i=vertices[j],p=mesh.points[i],uv=mesh.uvs[i]||RENDER3D_DEFAULT_UV,x=p.x,y=p.y,p=p.z;render3DWriteVertex(floats,ints,9*j,x,y,p,mesh.normals[i]||RENDER3D_DEFAULT_NORMAL,uv.x,uv.y,(mesh.colors[i]||WHITE).rgbaInt()),r=max(r,x*x+y*y+p*p),x0=min(x0,x),y0=min(y0,y),z0=min(z0,p),x1=max(x1,x),y1=max(y1,y),z1=max(z1,p)}return mesh.radius=r**.5,mesh.bounds=count?{min:vec3(x0,y0,z0),max:vec3(x1,y1,z1)}:{min:vec3(),max:vec3()},data}function render3DPolygonStrip(points){var strip=[points[0]];for(let i=1,j=points.length-1;i<=j;++i,--j)strip.push(points[i]),i!==j&&strip.push(points[j]);return strip}function render3DFlipWinding(mesh){if(mesh.indices)return render3DFlipTriangles(mesh.indices);for(const key of["points","normals","uvs","colors"]){var a=mesh[key];a.length&&(a.unshift(a[0]),a.push(a[a.length-1]))}mesh.vertexKeys=void 0}const render3DMeshBuffers="undefined"==typeof FinalizationRegistry?void 0:new FinalizationRegistry(({buffer,indexBuffer,generation})=>{generation===render3D?.contextGeneration&&glContext&&(glContext.deleteBuffer(buffer),glContext.deleteBuffer(indexBuffer))});function render3DSmoothNormalSums(points,indices){var strip=!indices,triangles=strip?max(points.length-2,0):indices.length/3|0,{groups,count}=function(points){var groups=new Int32Array(points.length),first=new Map,next=[],places=[];for(let i=0;i<points.length;++i){var p=points[i],x=round(1e5*p.x),y=round(1e5*p.y),z=round(1e5*p.z),p=Math.imul(x,73856093)^Math.imul(y,19349663)^Math.imul(z,83492791);let group=first.get(p);for(;void 0!==group&&(places[3*group]!==x||places[3*group+1]!==y||places[3*group+2]!==z);)group=next[group];void 0===group&&(group=next.length,next.push(first.get(p)),first.set(p,group),places.push(x,y,z)),groups[i]=group}return{groups:groups,count:next.length}}(points);const sums=new Float64Array(3*count),touched=new Uint8Array(count);let nx,ny,nz;var addCorner=(a,b,c,group)=>{var ux=b.x-a.x,uy=b.y-a.y,b=b.z-a.z,vx=c.x-a.x,vy=c.y-a.y,c=c.z-a.z,a=Math.acos(clamp((ux*vx+uy*vy+b*c)/((ux**2+uy**2+b**2)**.5*(vx**2+vy**2+c**2)**.5||1),-1,1)),ux=3*group;sums[ux]+=nx*a,sums[1+ux]+=ny*a,sums[2+ux]+=nz*a,touched[group]=1};for(let t=0;t<triangles;++t){var i0=strip?t:indices[3*t],i1=strip?t+1:indices[3*t+1],i2=strip?t+2:indices[3*t+2],a=points[i0],b=points[i1],c=points[i2],ux=b.x-a.x,uy=b.y-a.y,uz=b.z-a.z,vx=c.x-a.x,vy=c.y-a.y,vz=c.z-a.z,cx=uy*vz-uz*vy,uz=uz*vx-ux*vz,vz=ux*vy-uy*vx,ux=cx**2+uz**2+vz**2;ux&&(vy=(!strip||1&t?1:-1)/ux**.5,nx=cx*vy,ny=uz*vy,nz=vz*vy,addCorner(a,b,c,groups[i0]),addCorner(b,c,a,groups[i1]),addCorner(c,a,b,groups[i2]))}return{groups:groups,sums:sums,touched:touched}}class Mesh{constructor(){this.points=[],this.normals=[],this.uvs=[],this.colors=[],this.buffer=void 0,this.indexBuffer=void 0,this.bufferCount=0,this.indexType=0,this.dirty=!1,this.instanced=void 0,this.doubleSided=!1,this.dynamicDraw=!1,this.indices=void 0,this.vertexKeys=void 0,this.vertexLayout=void 0,this.vertexDataPacked=!1,this.instanceCount=0,this.instanceData=void 0,this.radius=0,this.bounds=void 0,this.contextGeneration=0}get vertexCount(){return this.points.length}addStrip(points,normals,uvs,colors){var part;return this.indices?(part=(new Mesh).addStrip(points,normals,uvs,colors).toIndexed(),this.addTriangles(part.points,part.indices,part.normals,part.uvs,part.colors)):(function(points,normals,uvs,colors,callback){var n=points.length,count=render3DStripCount(n),normalArray=isArray(normals),uvArray=isArray(uvs),colorArray=isArray(colors);for(let k=0;k<count;++k){var i=render3DStripIndex(k,n);callback(points[i],normalArray?normals[i]:normals||RENDER3D_DEFAULT_NORMAL,uvArray?uvs[i]:uvs||RENDER3D_DEFAULT_UV,colorArray?colors[i]:colors||WHITE)}}(points,normals,uvs,colors,(p,n,uv,c)=>{this.points.push(p),this.normals.push(n),this.uvs.push(uv),this.colors.push(c)}),this.vertexKeys=void 0,this.dirty=!0,this)}addTriangles(points,indices,normals,uvs,colors){this.toIndexed();var offset=this.points.length,normalArray=isArray(normals),uvArray=isArray(uvs),colorArray=isArray(colors);for(let i=0;i<points.length;++i)this.points.push(points[i]),this.normals.push(normalArray?normals[i]:normals||RENDER3D_DEFAULT_NORMAL),this.uvs.push(uvArray?uvs[i]:uvs||RENDER3D_DEFAULT_UV),this.colors.push(colorArray?colors[i]:colors||WHITE);for(const i of indices)this.indices.push(i+offset);return this.dirty=!0,this}toIndexed(){var vertices,indices;return this.indices||({vertices,indices}=this.getTriangles(),this.points=vertices.map(i=>this.points[i]),this.normals=vertices.map(i=>this.normals[i]||RENDER3D_DEFAULT_NORMAL),this.uvs=vertices.map(i=>this.uvs[i]||RENDER3D_DEFAULT_UV),this.colors=vertices.map(i=>this.colors[i]||WHITE),this.indices=render3DFlipTriangles(indices),this.vertexKeys=void 0,this.dirty=!0),this}addQuad(a,b,c,d,color,uvs){return this.addStrip([a,b,d,c],render3DFaceNormal(a,b,c,d),uvs?render3DQuadValues(uvs):RENDER3D_QUAD_UVS,render3DQuadValues(color))}combine(mesh,matrix=RENDER3D_IDENTITY,color=WHITE){var last,normalMatrix=render3DNormalMatrix(matrix=render3DMatrix(matrix)),mirrors=matrix.determinant()<0;let part=mesh,order;if(this.indices||mesh.indices){this.toIndexed(),part=mesh.indices?mesh:(new Mesh).combine(mesh).toIndexed();var offset=this.points.length,indices=part.indices;for(let t=0,n=indices.length;t<n;t+=3)this.indices.push(indices[t]+offset,indices[t+(mirrors?2:1)]+offset,indices[t+(mirrors?1:2)]+offset)}else mirrors&&part.points.length&&(last=part.points.length-1,order=[0,...part.points.keys(),last]);for(let j=0,count=(order||part.points).length;j<count;++j){var i=order?order[j]:j;this.points.push(matrix.transformPoint(part.points[i])),this.normals.push(normalMatrix.transformDirection(part.normals[i]||RENDER3D_DEFAULT_NORMAL).normalize()),this.uvs.push((part.uvs[i]||RENDER3D_DEFAULT_UV).copy()),this.colors.push((part.colors[i]||WHITE).multiply(color))}return this.doubleSided||=mesh.doubleSided,this.vertexKeys=void 0,this.dirty=!0,this}mirror(axis=vec3(1,0,0)){var axis=axis.normalize(),a=-2*axis.x,b=-2*axis.y,c=-2*axis.z,a=new Matrix4([1+a*axis.x,b*axis.x,c*axis.x,0,a*axis.y,1+b*axis.y,c*axis.y,0,a*axis.z,b*axis.z,1+c*axis.z,0,0,0,0,1]);return(new Mesh).combine(this).combine(this,a)}spin(count,axis=vec3(0,1,0)){var mesh=new Mesh,unit=axis.normalize();for(let i=0;i<count;++i)mesh.combine(this,function(axis,angle){var x=vec3(1,0,0).rotate(axis,angle),y=vec3(0,1,0).rotate(axis,angle),axis=vec3(0,0,1).rotate(axis,angle);return new Matrix4([x.x,x.y,x.z,0,y.x,y.y,y.z,0,axis.x,axis.y,axis.z,0,0,0,0,1])}(unit,i/count*2*PI));return mesh}union(mesh,matrix){return render3DMeshCSG(this,mesh,matrix,0)}subtract(mesh,matrix){return render3DMeshCSG(this,mesh,matrix,1)}intersect(mesh,matrix){return render3DMeshCSG(this,mesh,matrix,2)}scaleUVs(scale){const s=render3DSize2(scale);return this.uvs=this.uvs.map(uv=>vec2(uv.x*s.x,uv.y*s.y)),this.dirty=!0,this}transform(matrix){var normalMatrix=render3DNormalMatrix(matrix=render3DMatrix(matrix));for(let i=0;i<this.points.length;++i)this.points[i]=matrix.transformPoint(this.points[i]),this.normals[i]&&=normalMatrix.transformDirection(this.normals[i]).normalize();return matrix.determinant()<0&&render3DFlipWinding(this),this.dirty=!0,this}flipNormals(){return render3DFlipWinding(this),this.normals=this.normals.map(n=>n.scale(-1)),this.dirty=!0,this}setColor(color){return this.colors=this.points.map(()=>color),this.dirty=!0,this}getBounds(){if(!this.points.length)return{min:vec3(),max:vec3()};var lo=vec3(1/0),hi=vec3(-1/0);for(const p of this.points)lo.x=min(lo.x,p.x),lo.y=min(lo.y,p.y),lo.z=min(lo.z,p.z),hi.x=max(hi.x,p.x),hi.y=max(hi.y,p.y),hi.z=max(hi.z,p.z);return{min:lo,max:hi}}center(){var bounds=this.getBounds();return this.transform(bounds.min.add(bounds.max).scale(-.5))}fit(size=1){var bounds=this.getBounds(),bounds=bounds.max.subtract(bounds.min),size=size/(max(bounds.x,bounds.y,bounds.z)||1);return this.transform(Matrix4.scaling(vec3(size)))}computeRadius(){let r=0;for(const p of this.points)r=max(r,p.lengthSquared());return this.bounds=this.getBounds(),this.radius=r**.5}computeNormals(smooth=!1){if(this.indices)if(smooth){const{groups,sums,touched}=render3DSmoothNormalSums(this.points,this.indices);this.normals=this.points.map((p,i)=>{var s=3*groups[i],x=sums[s],y=sums[1+s],s=sums[2+s],lengthSquared=x**2+y**2+s**2,k=1/lengthSquared**.5;return touched[groups[i]]&&lengthSquared?vec3(x*k,y*k,s*k):RENDER3D_DEFAULT_NORMAL})}else{var split=a=>this.indices.map(i=>a[i]);this.points=split(this.points),this.normals=split(this.normals),this.uvs=split(this.uvs),this.colors=split(this.colors),this.indices=this.indices.map((_,i)=>i);const points=this.points,indices=this.indices,normals=points.map(()=>RENDER3D_DEFAULT_NORMAL);for(let t=0;t<indices.length;t+=3){var a=points[indices[t]],b=points[indices[t+1]],c=points[indices[t+2]],b=b.subtract(a).cross(c.subtract(a));b.lengthSquared()&&(normals[indices[t]]=normals[indices[t+1]]=normals[indices[t+2]]=b.normalize())}this.normals=normals}else{const points=this.points,n=points.length;if(smooth){const{groups,sums,touched}=render3DSmoothNormalSums(points);this.normals=points.map((p,i)=>{var s=3*groups[i],x=sums[s],y=sums[1+s],s=sums[2+s],l=(x**2+y**2+s**2)**.5;return touched[groups[i]]?l?vec3(x*(1/l),y*(1/l),s*(1/l)):vec3():vec3(0,1,0)})}else{var faceNormals=[];for(let i=0;i+2<n;++i){const a=points[i],b=points[i+1],c=points[i+2];var normal=b.subtract(a).cross(c.subtract(a));faceNormals.push(normal.lengthSquared()?normal.normalize(1&i?1:-1):void 0)}const normals=points.map(()=>RENDER3D_DEFAULT_NORMAL);faceNormals.forEach((f,i)=>f&&(normals[i]=normals[i+1]=normals[i+2]=f)),this.normals=normals}this.vertexKeys=void 0}return this.dirty=!0,this}upload(){var gl,indices,wide,data,layout;return render3D?.program&&glContext?(gl=glContext,layout=this.vertexLayout,this.dynamicDraw&&layout&&this.buffer&&this.contextGeneration===render3D.contextGeneration&&layout.pointCount===this.points.length?(gl.bindBuffer(gl.ARRAY_BUFFER,this.buffer),gl.bufferSubData(gl.ARRAY_BUFFER,0,this.vertexDataPacked?layout.data:render3DMeshVertexData(this,layout.vertices,layout.data)),gl.bindBuffer(gl.ARRAY_BUFFER,glArrayBuffer),this.dirty=this.vertexDataPacked=!1):(this.dispose(),{vertices:layout,indices}=this.getTriangles(),wide=65535<layout.length,data=render3DMeshVertexData(this,layout),this.vertexLayout=this.dynamicDraw?{vertices:layout,pointCount:this.points.length,data:data}:void 0,this.vertexKeys=void 0,this.buffer=gl.createBuffer(),this.indexBuffer=gl.createBuffer(),this.bufferCount=indices.length,this.indexType=wide?gl.UNSIGNED_INT:gl.UNSIGNED_SHORT,this.dirty=this.vertexDataPacked=!1,gl.bindBuffer(gl.ARRAY_BUFFER,this.buffer),gl.bufferData(gl.ARRAY_BUFFER,data,this.dynamicDraw?gl.DYNAMIC_DRAW:gl.STATIC_DRAW),gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER,this.indexBuffer),gl.bufferData(gl.ELEMENT_ARRAY_BUFFER,new(wide?Uint32Array:Uint16Array)(indices),gl.STATIC_DRAW),this.contextGeneration=render3D.contextGeneration,layout={buffer:this.buffer,indexBuffer:this.indexBuffer,generation:this.contextGeneration},render3DMeshBuffers?.register(this,layout,this),gl.bindBuffer(gl.ARRAY_BUFFER,glArrayBuffer))):this.computeRadius(),this}getTriangles(){var count=this.points.length,vertices=[];if(this.indices){for(let i=0;i<count;++i)vertices.push(i);return{vertices:vertices,indices:render3DFlipTriangles(this.indices.slice())}}var remap=new Int32Array(count),place=new Int32Array(count),keys=this.vertexKeys;if(keys&&keys.length===count){var first=new Int32Array(count).fill(-1);for(let i=0;i<count;++i){var key=keys[i],j=first[key];j<0?(remap[first[key]=i]=vertices.length,vertices.push(i)):remap[i]=remap[j],place[i]=first[key]}}else{var values=new Float64Array(9*count);let size=1;for(;size<2*count;)size*=2;var mask=size-1,seen=new Int32Array(size),places=new Int32Array(size),mix=(h,v)=>Math.imul(h^v,2654435761)>>>0;for(let i=0;i<count;++i){var p=this.points[i],n=this.normals[i]||RENDER3D_DEFAULT_NORMAL,uv=this.uvs[i]||RENDER3D_DEFAULT_UV,k=9*i,x=values[k]=round(1e6*p.x),y=values[1+k]=round(1e6*p.y),z=values[2+k]=round(1e6*p.z);values[3+k]=round(1e6*n.x),values[4+k]=round(1e6*n.y),values[5+k]=round(1e6*n.z),values[6+k]=round(1e6*uv.x),values[7+k]=round(1e6*uv.y),values[8+k]=(this.colors[i]||WHITE).rgbaInt();let h=mix(mix(mix(2166136261,x),y),z);for(let slot=h&mask;;slot=slot+1&mask){var o=places[slot]-1;if(o<0){places[slot]=i+1,place[i]=i;break}if(values[9*o]===x&&values[9*o+1]===y&&values[9*o+2]===z){place[i]=o;break}}for(let m=3;m<9;++m)h=mix(h,values[k+m]);for(let slot=h&mask;;slot=slot+1&mask){const o=seen[slot]-1;if(o<0){seen[slot]=i+1,remap[i]=vertices.length,vertices.push(i);break}let same=!0;for(let m=0;same&&m<9;++m)same=values[9*o+m]===values[k+m];if(same){remap[i]=remap[o];break}}}}return{vertices:vertices,indices:render3DStripTriangles(count,remap,place)}}render(matrix,tileInfo,color){render3D?.drawMesh(this,matrix,tileInfo,color)}dispose(){this.buffer&&(render3DMeshBuffers?.unregister(this),this.contextGeneration===render3D?.contextGeneration&&(glContext?.deleteBuffer(this.buffer),glContext?.deleteBuffer(this.indexBuffer)),this.buffer=this.indexBuffer=void 0,this.bufferCount=0)}}function buildLathe(profile,sides=16,smooth=render3D?.smoothShading,capped=!0){sides|=0;var mesh=new Mesh;const rings=profile.length;var point=(i,a)=>vec3(sin(a)*profile[i][0],profile[i][1],cos(a)*profile[i][0]);const segmentNormal=i=>{var[r0,y0]=profile[i],[i,y1]=profile[i+1],y1=vec2(y1-y0,r0-i);return y1.length()?y1.normalize():vec2(1,0)},closed=2<rings&&abs(profile[0][0]-profile[rings-1][0])<1e-9&&abs(profile[0][1]-profile[rings-1][1])<1e-9;var vertexNormal=i=>{if(!closed&&(!i||i==rings-1)&&abs(profile[i][0])<1e-9){var up=i?1:-1;if(segmentNormal(i?i-1:0).y*up>Math.SQRT1_2-1e-9)return vec2(0,up)}let n=vec2();up=s=>{return n=n.add(segmentNormal(s).scale((s=s,hypot(profile[s+1][0]-profile[s][0],profile[s+1][1]-profile[s][1]))))};return 0<i?up(i-1):closed&&up(rings-2),i<rings-1?up(i):closed&&up(0),n.length()?n.normalize():vec2(1,0)},normal3D=(n,a)=>vec3(sin(a)*n.x,n.y,cos(a)*n.x);const lengths=[0];for(let i=1;i<rings;++i)lengths[i]=lengths[i-1]+hypot(profile[i][0]-profile[i-1][0],profile[i][1]-profile[i-1][1]);const total=lengths[rings-1]||1;var v=i=>1-lengths[i]/total;for(let i=0;i+1<rings;++i)if(smooth){var points=[],normals=[],uvs=[],n0=vertexNormal(i),n1=vertexNormal(i+1),half=PI/sides,turn1=abs(profile[i+1][0])<1e-9?-half:0,turn0=abs(profile[i][0])<1e-9?half:0;for(let j=0;j<=sides;++j){var a=j/sides*2*PI,u=j/sides;points.push(point(i+1,a),point(i,a)),normals.push(normal3D(n1,a+turn1),normal3D(n0,a+turn0)),uvs.push(vec2(u,v(i+1)),vec2(u,v(i)))}mesh.addStrip(points,normals,uvs)}else{var n=segmentNormal(i);for(let j=0;j<sides;++j){var a0=j/sides*2*PI,a1=(j+1)/sides*2*PI,u0=j/sides,u1=(j+1)/sides;mesh.addStrip([point(i+1,a0),point(i,a0),point(i+1,a1),point(i,a1)],normal3D(n,(a0+a1)/2),[vec2(u0,v(i+1)),vec2(u0,v(i)),vec2(u1,v(i+1)),vec2(u1,v(i))])}}if(capped&&!closed)for(var[i,up]of[[0,!1],[rings-1,!0]])if(!(abs(profile[i][0])<1e-9)){const points=[],uvs=[];for(let j=0;j<sides;++j){const a=(up?j:-j)/sides*2*PI;points.push(point(i,a)),uvs.push(vec2(.5*sin(a)+.5,.5*cos(a)+.5))}mesh.addStrip(render3DPolygonStrip(points),vec3(0,up?1:-1,0),render3DPolygonStrip(uvs))}return mesh.doubleSided=!closed&&!capped&&(1e-9<abs(profile[0][0])||1e-9<abs(profile[rings-1][0])),mesh}function buildSphere(size=1,sides=16,rings=8,smooth=render3D?.smoothShading){var profile=[];for(let i=0;i<=rings;++i){var a=i/rings*PI-PI/2;profile.push([cos(a)*size/2,sin(a)*size/2])}return buildLathe(profile,sides,smooth)}function buildBox(size=1,bevel=0,bevelSegments=1){var half=render3DSize3(size).scale(.5),size=[[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)]],bevel=min(bevel,half.x,half.y,half.z);if(0<bevel)return function(half,t,segments,faces){const mesh=new Mesh,inner=half.subtract(vec3(t)),n=max(1,0|segments);var normal,r,u,triangle=(a,b,c,na,nb,nc)=>{let normal=b.subtract(a).cross(c.subtract(a));if(!(normal.lengthSquared()<1e-20)){normal.dot(a.add(b).add(c))<0&&([b,c,nb,nc,normal]=[c,b,nc,nb,normal.scale(-1)]),normal=normal.normalize();let face=faces[0];for(const f of faces)f[0].dot(normal)>face[0].dot(normal)&&(face=f);const[,r,u]=face,hr=abs(half.dot(r)),hu=abs(half.dot(u));var uv=p=>vec2(.5+p.dot(r)/(2*hr),.5-p.dot(u)/(2*hu));mesh.addStrip([a,b,c],1<n&&na?[na,nb,nc]:normal,[uv(a),uv(b),uv(c)])}};for([normal,r,u]of faces){var[a,b,c,d]=render3DQuadAxes(normal.multiply(half),r.multiply(inner),u.multiply(inner));triangle(a,b,c),triangle(c,b,d)}var axes=[vec3(1,0,0),vec3(0,1,0),vec3(0,0,1)];for(let k=0;k<3;++k)for(const si of[-1,1])for(const sj of[-1,1]){const n1=axes[(k+1)%3].scale(si),n2=axes[(k+2)%3].scale(sj);var middle=n1.multiply(inner).add(n2.multiply(inner)),along=axes[k].multiply(inner),start=middle.subtract(along),end=middle.add(along),dir=m=>n1.scale(cos(m/n*PI/2)).add(n2.scale(sin(m/n*PI/2)));for(let m=0;m<n;++m){var d0=dir(m),d1=dir(m+1);const a=start.add(d0.scale(t)),b=end.add(d0.scale(t)),c=start.add(d1.scale(t)),d=end.add(d1.scale(t));triangle(a,b,c,d0,d0,d1),triangle(c,b,d,d1,d0,d1)}}for(const sx of[-1,1])for(const sy of[-1,1])for(const sz of[-1,1]){const nx=vec3(sx,0,0),ny=vec3(0,sy,0),nz=vec3(0,0,sz),corner=inner.multiply(vec3(sx,sy,sz)),dir=(i,j)=>{var polar=i/n*PI/2,j=i?j/i*PI/2:0;return nz.scale(cos(polar)).add(nx.scale(cos(j)*sin(polar))).add(ny.scale(sin(j)*sin(polar)))};var point=(i,j)=>corner.add(dir(i,j).scale(t));for(let i=0;i<n;++i){for(let j=0;j<=i;++j)triangle(point(i,j),point(i+1,j),point(i+1,j+1),dir(i,j),dir(i+1,j),dir(i+1,j+1));for(let j=0;j<i;++j)triangle(point(i,j),point(i+1,j+1),point(i,j+1),dir(i,j),dir(i+1,j+1),dir(i,j+1))}}return mesh}(half,bevel,bevelSegments,size);var n,r,u,mesh=new Mesh;for([n,r,u]of size){var center=n.multiply(half),right=r.multiply(half),up=u.multiply(half);mesh.addStrip(render3DQuadAxes(center,right,up),n,RENDER3D_QUAD_UVS)}return mesh}function buildGrid(size=vec2(1),segments=1,color,heightFunction,smooth=heightFunction&&render3D?.smoothShading){heightFunction||=()=>0,size=render3DSize2(size),segments=render3DSize2(segments);var mesh=new Mesh;const segmentsX=segments.x,segmentsZ=segments.y,cellX=size.x/segmentsX,cellZ=size.y/segmentsZ,halfX=size.x/2,halfZ=size.y/2,ex=cellX/2,ez=cellZ/2,px=i=>i*cellX-halfX,pz=j=>j*cellZ-halfZ,colorAt=color,cellColor=(i,j)=>color?isColor(color)?color:colorAt(px(i),pz(j)):WHITE;var row=j=>{var points=[],normals=[],uvs=[],colors=[],z=pz(j);for(let i=0;i<=segmentsX;++i){var x=px(i);points.push(vec3(x,heightFunction(x,z),z)),uvs.push(vec2(i/segmentsX,j/segmentsZ)),smooth&&(normals.push(render3DSlopeNormal(heightFunction,x,z,ex,ez,halfX,halfZ)),colors.push(cellColor(i,j)))}return{points:points,normals:normals,uvs:uvs,colors:colors}};let above=row(0);for(let j=0;j<segmentsZ;++j){var below=row(j+1);if(smooth){var points=[],normals=[],uvs=[],colors=[];for(let i=0;i<=segmentsX;++i)points.push(above.points[i],below.points[i]),normals.push(above.normals[i],below.normals[i]),uvs.push(above.uvs[i],below.uvs[i]),colors.push(above.colors[i],below.colors[i]);mesh.addStrip(points,normals,uvs,colors)}else for(let i=0;i<segmentsX;++i)mesh.addQuad(above.points[i],below.points[i],below.points[i+1],above.points[i+1],cellColor(i+.5,j+.5),[above.uvs[i],below.uvs[i],below.uvs[i+1],above.uvs[i+1]]);above=below}if(smooth){var n=2*(segmentsX+1)+2,keys=mesh.vertexKeys=new Int32Array(mesh.points.length),id=(i,j)=>j*(segmentsX+1)+i;for(let j=0;j<segmentsZ;++j){var start=j*n;keys[start]=id(0,j);for(let i=0;i<=segmentsX;++i)keys[1+start+2*i]=id(i,j),keys[2+start+2*i]=id(i,j+1);keys[start+n-1]=id(segmentsX,j+1)}}return mesh.doubleSided=!0,mesh}function buildSky(topColor=hsl(.6,.8,.55),horizonColor=hsl(.6,1,.9),bottomColor=horizonColor,sides=16,rings=8){sides=buildSphere(2,sides,rings,!0).flipNormals();return sides.colors=sides.points.map(p=>p.y<0?horizonColor.lerp(bottomColor,-p.y):horizonColor.lerp(topColor,p.y)),render3DSkyColors.set(sides,[topColor.copy(),horizonColor.copy(),bottomColor.copy()]),sides}const render3DSkyColors=new WeakMap;class EngineObject3D extends EngineObject{constructor(pos3D=vec3(),mesh,tileInfo,color=WHITE){tileInfo instanceof TextureInfo&&render3DWholeTiles.add(tileInfo=new TileInfo(vec2(),tileInfo.size,tileInfo,0,0)),super(vec2(pos3D.x,pos3D.y),vec2(),tileInfo,0,color),this.mass=0,this.pos3D=pos3D.copy(),this.rotation3D=vec3(),this.scale3D=vec3(1),this.velocity3D=vec3(),this.angleVelocity3D=vec3();color=(this.mesh=mesh)&&mesh.points.length?!mesh.dirty&&mesh.bounds||mesh.getBounds():void 0;this.size3D=color?color.max.subtract(color.min):vec3(1),this.softShadow=0,this.upright=!1,this.pixelated=!1,this.sync2D=!1,this.transparent=!mesh&&!!tileInfo,this.additive=!1,this.emissive=0,this.specular=0,this.shininess=16,this.normalMap=void 0,this.normalScale=1,this.reflectivity=0,this.emissiveMap=void 0,this.emissiveMapColor=WHITE,this.environment=void 0,this.castShadow=!0,this.collideAsSphere3D=!1,this.groundAngle=PI/4,this.receiveShadow=!0,this.renderAfter2D=void 0,this.localMatrix=void 0,this.worldMatrix=new Matrix4,this.matrixBuilt=new Float64Array(9).fill(NaN),this.matrixVersion=0,this.matrixParent=void 0,this.matrixParentVersion=0,this.movePass=engineObjectsUpdateCount,this.collideFrom=vec3(),this.collideFromPass=-1,this.drawOwner=void 0}updatePhysics(){this.sync2D&&super.updatePhysics();var v,gx,moving,ground=this.groundObject,solve=(this.sync2D||(this.groundObject=void 0),enablePhysicsSolver&&!this.sync2D),cap=this.clampSpeed&&solve&&(this.collideSolidObjects||this.collideLevel&&this.mass)?objectMaxSpeed:void 0,oldPos=(this.collideLevel||this.collideSolidObjects)&&this.mass&&!this.sync2D?this.pos3D.copy():void 0;if(this.collideSolidObjects&&!this.sync2D&&render3DCollideFrom(this),render3DMove(this,cap),ground&&this.mass&&!this.sync2D&&(cap=max(this.friction,ground.friction),v=this.velocity3D,gx=(moving=ground instanceof EngineObject3D?ground.velocity3D:void 0)?.x??0,moving=moving?.z??0,v.x=gx+(v.x-gx)*cap,v.z=moving+(v.z-moving)*cap),oldPos&&this.collideLevel&&solve&&render3DCollideLevel(this,oldPos,ground),this.collideSolidObjects&&solve){var a=this;let shapeA=render3DSolidShape(a);var reachA=render3DSolidReach(a);for(const b of engineObjectsCollide){if(b===a)break;if(!b.destroyed&&b instanceof EngineObject3D&&!b.parent&&!b.sync2D&&(a.isSolid||b.isSolid)){var p=shapeA.pos,q=b.pos3D,reach=reachA+render3DSolidReach(b),dx=p.x-q.x,dy=p.y-q.y,p=p.z-q.z;if(!(reach*reach<dx*dx+dy*dy+p*p)){var pushed,q=render3DSolidShape(b);let push=render3DSolidPush(shapeA,q);push&&(push=render3DPushFrom(shapeA,q,a,b,push),render3DCollideAsk(a,b,push,!1))&&(reach=push.lengthSquared(),dx=push.y/reach**.5,dy=0<dx&&a.mass&&dx>=cos(a.groundAngle)&&render3DOnFace(push,q),p=dx<0&&b.mass&&-dx>=cos(b.groundAngle)&&render3DOnFace(push,shapeA),(dy||p)&&(dy?a.groundObject=b:b.groundObject=a,push.x||push.z)&&(push=vec3(0,reach/push.y,0)),q=a.mass+b.mass,dx=a.mass?b.mass?b.mass/q:1:0,p=b.mass?a.mass?a.mass/q:1:0,dy=a.pos3D.copy(),reach=b.pos3D.copy(),a.pos3D=a.pos3D.add(push.scale(dx)),b.pos3D=b.pos3D.subtract(push.scale(p)),q=push.normalize(),dx&&a.velocity3D.dot(q)<0&&(a.velocity3D=a.velocity3D.reflect(q,a.restitution)),p&&0<b.velocity3D.dot(q)&&(b.velocity3D=b.velocity3D.reflect(q,b.restitution)),dx&&p&&(pushed=a.pos3D.copy(),render3DSettleFixed(a,b,dy),b.pos3D=b.pos3D.add(a.pos3D.subtract(pushed))),p&&(q=b.pos3D.copy(),render3DSettleFixed(b,a,reach),p=b.pos3D.x-q.x,reach=b.pos3D.y-q.y,q=b.pos3D.z-q.z,dx)&&(p||reach||q)&&(a.pos3D=a.pos3D.add(vec3(p,reach,q)),render3DSettleFixed(a,b,dy)),dx)&&(shapeA=render3DSolidShape(a))}}}}}updateTransforms(updateChildren=!0){if(paused||this.parent&&this.movePass!==engineObjectsUpdateCount&&(this.movePass=engineObjectsUpdateCount,render3DMove(this)),super.updateTransforms(!1),!paused&&this.sync2D&&(this.pos3D.x=this.pos.x,this.pos3D.y=this.pos.y,this.rotation3D.z=-this.angle),updateChildren)for(const child of this.children)child.updateTransforms()}setCollision(collideSolidObjects=!0,isSolid=!0,collideLevel=!0,collideRaycast=!1){super.setCollision(collideSolidObjects,isSolid,collideLevel,collideRaycast)}collideWithVoxel(type,cell){return!0}getWorldPos3D(){return render3DObjectMatrix(this).getTranslation()}getForward3D(){return render3DAxis(render3DObjectMatrix(this).m,8).normalize(-1)}getRight3D(){return render3DAxis(render3DObjectMatrix(this).m,0).normalize()}getUp3D(){return render3DAxis(render3DObjectMatrix(this).m,4).normalize()}getMatrix(){return render3DObjectMatrix(this).copy()}get roughness(){return(2/(this.shininess+2))**.25}set roughness(roughness){this.shininess=max(1,2/max(roughness,.001)**4-2)}lookAt(target){var parent=this.parent instanceof EngineObject3D?this.parent:void 0,parent=parent?parent.getMatrix().invert().transformPoint(target):target;this.rotation3D=render3DLookRotation(parent.subtract(this.pos3D),this.rotation3D)}addChild(child,localPos,localAngle){return child instanceof EngineObject3D&&(child.movePass=engineObjectsUpdateCount),super.addChild(child,localPos,localAngle)}attach(child){if(!(child instanceof EngineObject3D))return super.attach(child);child.parent?.removeChild(child);var local=render3DObjectMatrix(this).copy().invert().multiply(render3DObjectMatrix(child));return super.attach(child),child.pos3D=local.getTranslation(),child.rotation3D=local.getRotation(),child.scale3D=local.getScale(),child.localMatrix&&(child.localMatrix=local),child}removeChild(child){child instanceof EngineObject3D&&!child.destroyed&&render3DTakeWorld(child,render3DObjectMatrix(child)),child instanceof EngineObject3D&&child.drawOwner===this&&(child.drawOwner=void 0),super.removeChild(child)}setMesh(mesh){const old=this.mesh;return this.mesh=mesh,old&&old!==mesh&&old.buffer&&!engineObjects.some(o=>o.mesh===old)&&old.dispose(),mesh}render(){}render3D(){var size,matrix=render3DObjectMatrix(this);this.mesh?render3D.drawMesh(this.mesh,matrix,this.tileInfo,this.color):this.tileInfo&&(size=render3DWorldSize(this,matrix=matrix.m),render3D.drawBillboard(vec3(matrix[12],matrix[13],matrix[14]),vec2(size.x,size.y),this.tileInfo,this.color,this.rotation3D.z,this.upright))}}const RENDER3D_PART_SETTINGS={emissive:0,additive:!1,specular:0,shininess:16,reflectivity:0,normalMap:void 0,normalScale:1,emissiveMap:void 0,emissiveMapColor:WHITE,receiveShadow:!0,castShadow:!0,pixelated:!1,shader:void 0,environment:void 0,renderAfter2D:void 0,renderOrder:0};function render3DSetting(o,name){var owner=o.drawOwner;if(owner){owner=render3DSetting(owner,name);if(owner!==RENDER3D_PART_SETTINGS[name])return owner}return o[name]}const render3DPartScratch={...RENDER3D_PART_SETTINGS};function render3DTakeWorld(o,world){o.pos3D=world.getTranslation(),o.rotation3D=world.getRotation(),o.scale3D=world.getScale(),o.localMatrix&&(o.localMatrix=world.copy())}const render3DLocalMatrix=new Matrix4;function render3DObjectMatrix(o){var parent=o.parent instanceof EngineObject3D?o.parent:void 0,parentMatrix=parent&&render3DObjectMatrix(parent),p=o.pos3D,r=o.rotation3D,s=o.scale3D,k=o.matrixBuilt,local=o.localMatrix;return local?(parent?(o.worldMatrix.m.set(parentMatrix.m),o.worldMatrix.multiply(local),o.matrixParentVersion=parent.matrixVersion):o.worldMatrix.m.set(local.m),k[0]=NaN,o.matrixParent=parent,++o.matrixVersion):(k[0]!==p.x||k[1]!==p.y||k[2]!==p.z||k[3]!==r.x||k[4]!==r.y||k[5]!==r.z||k[6]!==s.x||k[7]!==s.y||k[8]!==s.z||o.matrixParent!==parent||parent&&o.matrixParentVersion!==parent.matrixVersion)&&(k[0]=p.x,k[1]=p.y,k[2]=p.z,k[3]=r.x,k[4]=r.y,k[5]=r.z,k[6]=s.x,k[7]=s.y,k[8]=s.z,parent?(buildMatrix(p,r,s,render3DLocalMatrix),o.worldMatrix.m.set(parentMatrix.m),o.worldMatrix.multiply(render3DLocalMatrix),o.matrixParentVersion=parent.matrixVersion):buildMatrix(p,r,s,o.worldMatrix),o.matrixParent=parent,++o.matrixVersion),o.worldMatrix}function render3DMove(o,cap){var p=o.pos3D,v=o.velocity3D,r=o.rotation3D,a=o.angleVelocity3D,d=o.sync2D?1:o.damping,e=o.sync2D?1:o.angleDamping;v.x*=d,v.y*=d,v.z*=d,o.mass&&!o.sync2D&&(d=render3D.gravity,o=o.gravityScale,v.x+=d.x*o,v.y+=d.y*o,v.z+=d.z*o),void 0!==cap&&(v.x=clamp(v.x,-cap,cap),v.y=clamp(v.y,-cap,cap),v.z=clamp(v.z,-cap,cap)),p.x+=v.x,p.y+=v.y,p.z+=v.z,r.x+=a.x*=e,r.y+=a.y*=e,r.z+=a.z*=e}function render3DCollideLevel(o,oldPos,ground){for(const level of render3DLevel)level.destroyed||level.levelCollide3D(o,oldPos,level===ground)}function render3DSolidShape(o){var axes,s=o.size3D,k=o.scale3D,kx=abs(k.x),ky=abs(k.y),k=abs(k.z);return o.collideAsSphere3D?{pos:o.pos3D.copy(),radius:max(s.x,s.y,s.z)/2*max(kx,ky,k)}:(axes=!render3DIsSprite(o)&&isTurned3D(o.rotation3D)?function(o){var r=o.rotation3D,kept=render3DSolidAxesCache.get(o);if(kept&&kept.x===r.x&&kept.y===r.y&&kept.z===r.z)return kept.axes;kept=boxAxes3D(r);return render3DSolidAxesCache.set(o,{x:r.x,y:r.y,z:r.z,axes:kept}),kept}(o):void 0,{pos:o.pos3D.copy(),size:vec3(s.x*kx,s.y*ky,s.z*k),axes:axes})}const render3DSolidAxesCache=new WeakMap;function render3DSolidReach(o){var s=o.size3D,k=o.scale3D,kx=abs(k.x),ky=abs(k.y),k=abs(k.z);return o.collideAsSphere3D?max(s.x,s.y,s.z)/2*max(kx,ky,k):((o=s.x*kx)*o+(kx=s.y*ky)*kx+(o=s.z*k)*o)**.5/2}function render3DSolidPush(a,b){var push,sphereBox=(sphere,box)=>box.axes?collideSphereOrientedBox3D(sphere.pos,sphere.radius,box.pos,box.size,box.axes):collideSphereBox(sphere.pos,sphere.radius,box.pos,box.size);return a.size?b.size?a.axes||b.axes?collideOrientedBoxes3D(a.pos,a.size,a.axes??BOX_WORLD_AXES,b.pos,b.size,b.axes??BOX_WORLD_AXES):collideBoxBox3D(a.pos,a.size,b.pos,b.size):(push=sphereBox(b,a))&&push.scale(-1):b.size?sphereBox(a,b):collideSphereSphere(a.pos,a.radius,b.pos,b.radius)}function render3DCollideAsk(a,b,push,useKept=!0){var kept=useKept||render3DSettleAsked(a,b)?engineObjectsCollidePairAnswer(a,b):void 0;return void 0!==kept||(kept=a.collideWithObject(b,push),push=b.collideWithObject(a,push.scale(-1)),engineObjectsCollidePairAdd(a,b,kept=!(!kept||!push)),engineObjectsCollidePairAdd(b,a,kept),useKept&&render3DSettleAsked(a,b,!0)),kept}const render3DSettleAskedPairs={pass:-1,pairs:new Map};function render3DSettleAsked(a,b,mark=!1){var kept=render3DSettleAskedPairs;if(kept.pass!==engineObjectsUpdateCount){if(!mark)return;kept.pass=engineObjectsUpdateCount,kept.pairs.clear()}if(mark){for(var[one,other]of[[a,b],[b,a]]){var set=kept.pairs.get(one);set?set.add(other):kept.pairs.set(one,new Set([other]))}return 1}return kept.pairs.get(a)?.has(b)}function render3DCollideFrom(o){return o.collideFromPass!==engineObjectsUpdateCount&&(o.collideFromPass=engineObjectsUpdateCount,o.collideFrom.set(o.pos3D.x,o.pos3D.y,o.pos3D.z)),o.collideFrom}function render3DPushFrom(shapeA,shapeB,a,b,push){var aFrom=render3DCollideFrom(a),bFrom=render3DCollideFrom(b),a=a.pos3D,b=b.pos3D,mx=a.x-aFrom.x-b.x+bFrom.x,my=a.y-aFrom.y-b.y+bFrom.y,a=a.z-aFrom.z-b.z+bFrom.z;return push.x*mx+push.y*my+push.z*a<=0?push:function(a,b,aFrom,bFrom){if(!a.axes&&!b.axes){var half=shape=>shape.size?shape.size.scale(.5):vec3(shape.radius),halfA=half(a),halfB=half(b);let axis,amount=1/0,side=0;for(const k of["x","y","z"]){var reach=halfA[k]+halfB[k],was=aFrom[k]-bFrom[k];abs(was)<reach-1e-6||(was=sign(was),0<(reach=reach-was*(a.pos[k]-b.pos[k]))&&reach<amount&&(axis=k,amount=reach,side=was))}if(axis)return(half=vec3())[axis]=side*amount,half}}(shapeA,shapeB,aFrom,bFrom)??push}const render3DFixed={pass:-1,list:[]};function render3DSettleFixed(o,pusher,from){if(!o.destroyed&&o.collideSolidObjects){let shape=render3DSolidShape(o);var reachO=render3DSolidReach(o);for(const b of(render3DFixed.pass!==engineObjectsUpdateCount&&(render3DFixed.pass=engineObjectsUpdateCount,render3DFixed.list=engineObjectsCollide.filter(b=>!b.mass&&b instanceof EngineObject3D&&!b.parent&&!b.sync2D)),render3DFixed.list))if(b!==o&&b!==pusher&&!b.mass&&!b.destroyed&&(o.isSolid||b.isSolid)){var p=shape.pos,q=b.pos3D,reach=reachO+render3DSolidReach(b),dx=p.x-q.x,dy=p.y-q.y,p=p.z-q.z;if(!(reach*reach<dx*dx+dy*dy+p*p)){q=render3DSolidShape(b);let push=render3DSolidPush(shape,q);!push||push.lengthSquared()<1e-12||(push=render3DPushFrom(shape,q,o,b,push),render3DCollideAsk(o,b,push)&&(reach=push.lengthSquared(),0<(dx=push.y/reach**.5)&&o.mass&&dx>=cos(o.groundAngle)&&render3DOnFace(push,q)&&(o.groundObject=b,push.x||push.z)&&(push=vec3(0,reach/push.y,0)),o.pos3D=o.pos3D.add(push),shape=render3DSolidShape(o),dy=push.normalize(),o.velocity3D.dot(dy)<0)&&(o.velocity3D=o.velocity3D.reflect(dy,o.restitution)))}}o.collideLevel&&o.mass&&render3DCollideLevel(o,from,void 0)}}const render3DIsSprite=o=>!o.mesh&&!!o.tileInfo;function render3DOnFace(push,shape){return!!shape.size&&(push=push.normalize(),shape=shape.axes??BOX_WORLD_AXES,max(abs(push.dot(shape[0])),abs(push.dot(shape[1])),abs(push.dot(shape[2])))>1-1e-6)}function engineObjectsCollect3D(pos,size,objects=engineObjects,testCenters=!1){var radiusSquared="number"==typeof size&&0<size?(size/2)**2:void 0,box=radiusSquared?void 0:"number"==typeof size?vec3():render3DSize3(size),collected=[];for(const o of objects)if(o instanceof EngineObject3D&&!o.destroyed){const m=render3DObjectMatrix(o).m,s=o.size3D;if(s.x||s.y||s.z){var center=vec3(m[12],m[13],m[14]);const worldSize=testCenters?vec3():render3DWorldSize(o,m);var axis=i=>vec3(m[i],m[i+1],m[i+2]).normalize();let axes=!testCenters&&!render3DIsSprite(o)&&(m[1]||m[2]||m[4]||m[6]||m[8]||m[9])?[axis(0),axis(4),axis(8)]:void 0;if(axes&&(1e-6<abs(axes[0].dot(axes[1]))||1e-6<abs(axes[0].dot(axes[2]))||1e-6<abs(axes[1].dot(axes[2])))){const e=axes.map((a,i)=>a.scale(i?1<i?worldSize.z/2:worldSize.y/2:worldSize.x/2));var axis=c=>2*(abs(e[0][c])+abs(e[1][c])+abs(e[2][c]));worldSize.set(axis("x"),axis("y"),axis("z")),axes=void 0}let hit;(hit=box&&axes?!!collideOrientedBoxes3D(pos,box,BOX_WORLD_AXES,center,worldSize,axes):box?isOverlapping3D(pos,box,center,worldSize):axes?!!collideSphereOrientedBox3D(pos,radiusSquared**.5,center,worldSize,axes):(axis=max(abs(pos.x-center.x)-worldSize.x/2,0))*axis+(axis=max(abs(pos.y-center.y)-worldSize.y/2,0))*axis+(axis=max(abs(pos.z-center.z)-worldSize.z/2,0))*axis<radiusSquared)&&collected.push(o)}}return collected}const render3DRaycastCenter=vec3();function render3DRaycastObject(ray,o){if(!o.destroyed&&o instanceof EngineObject3D){if(o instanceof HeightMap||o instanceof VoxelMap)return o.levelRaycast3D(ray);if((o.mesh||o.tileInfo)&&!(o instanceof InstancedMesh3D)){var matrix=render3DObjectMatrix(o),mesh=o.mesh;if(!mesh)return function(ray,o,matrix){var center=matrix.getTranslation(),matrix=render3DWorldSize(o,matrix.m),matrix=vec2(matrix.x,matrix.y);var right,t,p;if(0<matrix.x&&0<matrix.y)return ray.direction.lengthSquared()?(o=render3DBillboardAxes(matrix,o.rotation3D.z,o.upright),right=vec3(o[0],o[1],o[2]),o=vec3(o[3],o[4],o[5]),void 0!==(t=raycastPlane(ray,center,right.cross(o)))&&(p=ray.getPosition(t).subtract(center),abs(p.dot(right))<=right.lengthSquared())&&abs(p.dot(o))<=o.lengthSquared()?t:void 0):render3DRaycastDisc(ray,center,hypot(matrix.x,matrix.y)/2)}(ray,o,matrix);o=(mesh.dirty||!mesh.radius?mesh.computeRadius():mesh.radius)*render3DMaxStretch(matrix.m);if(0<o){var m=matrix.m,m=render3DRaycastCenter.set(m[12],m[13],m[14]),distance=raycastSphere(ray,m,o);if(void 0!==distance)return matrix.determinant()?(distance=matrix.copy().invert(),matrix=mesh.bounds||mesh.getBounds(),void 0!==(matrix=raycastBox(distance=new Ray3D(distance.transformPoint(ray.origin),distance.transformDirection(ray.direction)),matrix.min.add(matrix.max).scale(.5),matrix.max.subtract(matrix.min)))&&ray.direction.lengthSquared()?function(ray,mesh){var points=mesh.points,indices=mesh.indices,both=mesh.doubleSided,ox=ray.origin.x,oy=ray.origin.y,oz=ray.origin.z,dx=ray.direction.x,dy=ray.direction.y,dz=ray.direction.z,count=indices?indices.length/3|0:points.length-2;let nearest;for(let i=0;i<count;++i){let a,b,c;c=indices?(a=points[indices[3*i]],b=points[indices[3*i+2]],points[indices[3*i+1]]):(a=points[1&i?i+1:i],b=points[1&i?i:i+1],points[i+2]);var sy,sz,sx,e1x=b.x-a.x,e1y=b.y-a.y,e1z=b.z-a.z,e2x=c.x-a.x,e2y=c.y-a.y,e2z=c.z-a.z,hx=dy*e2z-dz*e2y,hy=dz*e2x-dx*e2z,hz=dx*e2y-dy*e2x,det=e1x*hx+e1y*hy+e1z*hz;(both?det:det<0)&&(sx=ox-a.x,sy=oy-a.y,sz=oz-a.z,0<=(hx=(sx*hx+sy*hy+sz*hz)/det))&&hx<=1&&0<=(e1z=(dx*(hy=sy*e1z-sz*e1y)+dy*(hz=sz*e1x-sx*e1z)+dz*(sz=sx*e1y-sy*e1x))/det)&&hx+e1z<=1&&0<=(sx=(e2x*hy+e2y*hz+e2z*sz)/det)&&!(sx>=nearest)&&(nearest=sx)}return nearest}(distance,mesh):matrix):render3DRaycastDisc(ray,m,o)}}}}function render3DRaycastDisc(ray,center,radius){var d=ray.direction,center=center.subtract(ray.origin),ray=d.dot(d),ray=ray?center.dot(d)/ray:0;return 0<=ray&&center.subtract(d.scale(ray)).lengthSquared()<=radius*radius?ray:void 0}function engineObjectsRaycast3D(ray,objects=engineObjects){var hits=[];for(const o of objects){var distance=render3DRaycastObject(ray,o);void 0!==distance&&hits.push({o:o,distance:distance})}return hits.sort((a,b)=>a.distance-b.distance).map(hit=>hit.o)}function engineObjectsCallback3D(pos,size,callback,objects=engineObjects,testCenters=!1){for(const o of engineObjectsCollect3D(pos,size,objects,testCenters))o.destroyed||callback(o)}class InstancedMesh3D extends EngineObject3D{constructor(mesh,count,tileInfo,color=WHITE){super(vec3(),mesh,tileInfo,color),this.size3D=vec3(),this.count=count,this.maxCount=count,this.matrixData=new Float32Array(count*RENDER3D_MATRIX_FLOATS),this.colorData=new Float32Array(count*RENDER3D_COLOR_FLOATS),this.dirtyStart=0,this.dirtyEnd=count,this.colorDirtyStart=0,this.colorDirtyEnd=count,this.reachSquared=0,this.scaleSquared=0,this.boundsStart=0,this.boundsEnd=count,this.fixedRadius=void 0,this.buffer=void 0,this.colorBuffer=void 0,this.bufferGeneration=-1;var uv=render3DGetTileUVs(this.uvTileInfo=tileInfo),matrices=this.matrixData,colors=this.colorData;for(let i=0;i<count;++i){matrices.set(RENDER3D_IDENTITY.m,i*RENDER3D_MATRIX_FLOATS);var k=i*RENDER3D_COLOR_FLOATS;colors[k]=color.r,colors[1+k]=color.g,colors[2+k]=color.b,colors[3+k]=color.a,colors[4+k]=uv.x,colors[5+k]=uv.y,colors[6+k]=uv.w,colors[7+k]=uv.h}}setTransformAt(i,pos,rotation,scale){matrix4Compose(this.matrixData,i*RENDER3D_MATRIX_FLOATS,pos,rotation,scale),this.markDirty(i)}setTransforms(start,count,x,y,z,rx,ry,rz,sx,sy,sz){var m=this.matrixData,end=start+count;for(let i=start;i<end;++i){var cx,sinX,cy,sinY,cz,sinZ,k=i*RENDER3D_MATRIX_FLOATS,scaleX=sx?sx[i]:1,scaleY=sy?sy[i]:1,scaleZ=sz?sz[i]:1;rx?(cx=cos(rx[i]),sinX=sin(rx[i]),cy=cos(ry[i]),sinY=sin(ry[i]),cz=cos(rz[i]),sinZ=sin(rz[i]),m[k]=(cy*cz+sinY*sinX*sinZ)*scaleX,m[1+k]=cx*sinZ*scaleX,m[2+k]=(-sinY*cz+cy*sinX*sinZ)*scaleX,m[4+k]=(-cy*sinZ+sinY*sinX*cz)*scaleY,m[5+k]=cx*cz*scaleY,m[6+k]=(sinY*sinZ+cy*sinX*cz)*scaleY,m[8+k]=sinY*cx*scaleZ,m[9+k]=-sinX*scaleZ,m[10+k]=cy*cx*scaleZ):(m[k]=scaleX,m[1+k]=0,m[2+k]=0,m[4+k]=0,m[5+k]=scaleY,m[6+k]=0,m[8+k]=0,m[9+k]=0,m[10+k]=scaleZ),m[3+k]=m[7+k]=m[11+k]=0,m[15+k]=1,m[12+k]=x[i],m[13+k]=y[i],m[14+k]=z[i]}count&&this.markDirtyRange(start,end)}setMatrixAt(i,matrix){var data=this.matrixData,k=i*RENDER3D_MATRIX_FLOATS,m=matrix.m;for(let j=0;j<16;++j)data[k+j]=m[j];this.markDirty(i)}getMatrixAt(i){var i=i*RENDER3D_MATRIX_FLOATS,matrix=new Matrix4;return matrix.m.set(this.matrixData.subarray(i,16+i)),matrix}setColorAt(i,color){var data=this.colorData,k=i*RENDER3D_COLOR_FLOATS;data[k]=color.r,data[1+k]=color.g,data[2+k]=color.b,data[3+k]=color.a,this.markColorDirty(i)}markDirty(i){i<this.dirtyStart&&(this.dirtyStart=i),i>=this.dirtyEnd&&(this.dirtyEnd=i+1),i<this.boundsStart&&(this.boundsStart=i),i>=this.boundsEnd&&(this.boundsEnd=i+1)}markDirtyRange(start,end){start<this.dirtyStart&&(this.dirtyStart=start),end>this.dirtyEnd&&(this.dirtyEnd=end),start<this.boundsStart&&(this.boundsStart=start),end>this.boundsEnd&&(this.boundsEnd=end)}markColorDirty(i){i<this.colorDirtyStart&&(this.colorDirtyStart=i),i>=this.colorDirtyEnd&&(this.colorDirtyEnd=i+1)}get radius(){if(void 0!==this.fixedRadius)return this.fixedRadius;if(this.boundsEnd>this.boundsStart){var d=this.matrixData;let reachSquared=this.reachSquared,scaleSquared=this.scaleSquared;for(let i=this.boundsStart;i<this.boundsEnd;++i){var k=i*RENDER3D_MATRIX_FLOATS,x=d[12+k],y=d[13+k],z=d[14+k],x=x*x+y*y+z*z,y=render3DMaxStretchSquared(d,k);x>reachSquared&&(reachSquared=x),y>scaleSquared&&(scaleSquared=y)}this.reachSquared=reachSquared,this.scaleSquared=scaleSquared,this.boundsStart=1/0,this.boundsEnd=0}var mesh=this.mesh,mesh=mesh.radius||mesh.computeRadius();return this.reachSquared**.5+mesh*this.scaleSquared**.5}set radius(radius){this.fixedRadius=radius}render3D(){const r=render3D,gl=glContext,mesh=this.mesh;if(r.transparentQueue)return r.queueTransparent(this.getWorldPos3D(),()=>this.render3D());if(mesh&&this.count&&render3DCanDraw()&&(!r.shadowPass||r.lighting)&&(render3DMeshUpload(mesh),mesh.bufferCount)&&(!r.frustumCulling||render3DSphereVisible(0,0,0,this.radius))){if(this.uvTileInfo!==this.tileInfo){var uv=render3DGetTileUVs(this.tileInfo),data=this.colorData;for(let k=4;k<data.length;k+=RENDER3D_COLOR_FLOATS)data[k]=uv.x,data[k+1]=uv.y,data[k+2]=uv.w,data[k+3]=uv.h;this.uvTileInfo=this.tileInfo,this.colorDirtyStart=0,this.colorDirtyEnd=this.maxCount}this.buffer&&this.bufferGeneration===r.contextGeneration?((upload=(buffer,data,floats,start,end)=>{end<=start||(gl.bindBuffer(gl.ARRAY_BUFFER,buffer),gl.bufferSubData(gl.ARRAY_BUFFER,start*floats*4,data,start*floats,(end-start)*floats))})(this.buffer,this.matrixData,RENDER3D_MATRIX_FLOATS,this.dirtyStart,this.dirtyEnd),upload(this.colorBuffer,this.colorData,RENDER3D_COLOR_FLOATS,this.colorDirtyStart,this.colorDirtyEnd)):(this.bufferGeneration=r.contextGeneration,gl.bindBuffer(gl.ARRAY_BUFFER,this.buffer=gl.createBuffer()),gl.bufferData(gl.ARRAY_BUFFER,this.matrixData,gl.DYNAMIC_DRAW),gl.bindBuffer(gl.ARRAY_BUFFER,this.colorBuffer=gl.createBuffer()),gl.bufferData(gl.ARRAY_BUFFER,this.colorData,gl.DYNAMIC_DRAW)),this.dirtyStart=this.colorDirtyStart=1/0,this.dirtyEnd=this.colorDirtyEnd=0,r.flush(),render3DFlushBeforeOverlay();var upload=r.cullBackFaces,tileInfo=this.tileInfo;r.cullBackFaces=!mesh.doubleSided,render3DDrawInstanced(mesh,this.buffer,this.count,render3DTextureOf(tileInfo),r,this.colorBuffer),r.cullBackFaces=upload}}destroy(immediate){this.buffer&&this.bufferGeneration===render3D?.contextGeneration&&(glContext?.deleteBuffer(this.buffer),glContext?.deleteBuffer(this.colorBuffer)),this.buffer=this.colorBuffer=void 0,super.destroy(immediate)}}class Light3D extends EngineObject3D{constructor(pos3D=vec3(),radius=5,color=WHITE,intensity=1){super(pos3D,void 0,void 0,color),this.size3D=vec3(),this.radius=radius,this.intensity=intensity,this.directional=!1,this.coneAngle=0,this.coneSoftness=.2,this.glow=0,this.glowFalloff=1,this.gel=void 0,this.additive=!0,this.flareObject=void 0}addFlare(size,count,intensity,saturation,color){size=new LensFlare3D(size,count,intensity,saturation,color);return this.flare=size}get flare(){return this.flareObject&&!this.flareObject.destroyed?this.flareObject:void 0}set flare(flare){var old=this.flare;flare!==old&&(old&&old.destroy(),flare&&(flare.light=this,flare.parent||this.addChild(flare)),this.flareObject=flare||void 0)}destroy(immediate){this.flare=void 0,super.destroy(immediate)}render3D(){var r,c,toCamera,distance,pos;0<this.glow&&!this.directional&&(r=render3D,c=this.color,pos=render3DObjectMatrix(this).getTranslation(),toCamera=(distance=(toCamera=r.camera.pos.subtract(pos)).length())?pos.add(toCamera.scale(min(this.glow,distance)/2/distance)):pos,distance=rgb(c.r,c.g,c.b,c.a*min(this.intensity,1)),!(pos=engineGlowTexture(this.glowFalloff))&&glContext||r.drawBillboard(toCamera,vec2(this.glow),pos,distance))}}class DirectionalLight3D extends Light3D{constructor(pos3D=vec3(0,1,0),color=WHITE,intensity=1){super(pos3D,0,color,intensity),this.directional=!0}}function render3DDetach(o){o.parent?o.parent.removeChild(o):o.matrixParent&&render3DTakeWorld(o,o.worldMatrix)}let render3DSoftDotTexture;function normalMapFromHeight(size,heightFunction,strength=1){var context,width=0|size.x,size=0|size.y;return canvasAvailable()?(context=createCanvasContext(width,size),heightFunction=function(width,height,heightFunction,strength){const heights=new Float32Array(width*height),pixels=new Uint8ClampedArray(width*height*4);for(let y=0;y<height;++y)for(let x=0;x<width;++x)heights[y*width+x]=heightFunction(x,y);var h=(x,y)=>heights[mod(y,height)*width+mod(x,width)];for(let y=0;y<height;++y)for(let x=0;x<width;++x){var dx=(h(x+1,y)-h(x-1,y))/2*strength,dy=(h(x,y+1)-h(x,y-1))/2*strength,k=1/hypot(dx,dy,1),i=4*(y*width+x);pixels[i]=round(255*(-dx*k*.5+.5)),pixels[1+i]=round(255*(dy*k*.5+.5)),pixels[2+i]=round(255*(.5*k+.5)),pixels[3+i]=255}return pixels}(width,size,heightFunction,strength),context.putImageData(new ImageData(heightFunction,width,size),0,0),new TextureInfo(context.canvas,!0,!0)):((strength=new TextureInfo(void 0,!1,!0)).size=vec2(width,size),strength.sizeInverse=vec2(1/width,1/size),strength)}function render3DBevelProfile(corner,d1,d2,t,segments){var p1=[corner[0]+d1[0]*t,corner[1]+d1[1]*t],p2=[corner[0]+d2[0]*t,corner[1]+d2[1]*t];if(1===(segments=max(1,0|segments)))return[p1,p1,p2,p2];var half=Math.acos(clamp(d1[0]*d2[0]+d1[1]*d2[1],-1,1))/2,bx=d1[0]+d2[0],d1=d1[1]+d2[1],d2=t/cos(half)/hypot(bx,d1),cx=corner[0]+bx*d2,cy=corner[1]+d1*d2,radius=t*tan(half),start=atan2(p1[1]-cy,p1[0]-cx),turn=mod(atan2(p2[1]-cy,p2[0]-cx)-start+PI,2*PI)-PI,points=[p1];for(let i=1;i<segments;++i){var a=start+turn*i/segments;points.push([cx+cos(a)*radius,cy+sin(a)*radius])}return points.push(p2),points}function buildCylinder(size=1,height=1,sides=16,smooth=render3D?.smoothShading,capped=!0,bevel=0,bevelSegments=1){size/=2,height/=2,bevel=min(bevel,size,height);return buildLathe(0<bevel?[...render3DBevelProfile([size,-height],[-1,0],[0,1],bevel,bevelSegments),...render3DBevelProfile([size,height],[0,-1],[-1,0],bevel,bevelSegments)]:[[size,-height],[size,height]],sides,smooth,capped)}function buildCone(size=1,height=1,sides=16,smooth=render3D?.smoothShading,capped=!0,bevel=0,bevelSegments=1){var size=size/2,h=height/2,slant=hypot(size,height),bevel=min(bevel,size,slant/2);return buildLathe(0<bevel?[...render3DBevelProfile([size,-h],[-1,0],[-size/slant,height/slant],bevel,bevelSegments),[0,h]]:[[size,-h],[0,h]],sides,smooth,capped)}function buildCapsule(size=1,height=1,sides=16,rings=4,smooth=render3D?.smoothShading){var profile=[],r=size/2,straight=max(0,height-size)/2;for(let i=0;i<=rings;++i){var a=i/rings*PI/2;profile.push([r*sin(a),-straight-r*cos(a)])}for(let i=0;i<=rings;++i){const a=i/rings*PI/2;profile.push([r*cos(a),straight+r*sin(a)])}return buildLathe(profile,sides,smooth)}function buildTorus(size=1,tubeSize=.3,sides=16,tubeSides=8,smooth=render3D?.smoothShading){var profile=[],radius=(size-tubeSize)/2,tubeRadius=tubeSize/2;for(let i=0;i<=tubeSides;++i){var a=i/tubeSides*2*PI;profile.push([radius+tubeRadius*cos(a),tubeRadius*sin(a)])}return buildLathe(profile,sides,smooth)}function buildRibbon(points,width=1,color=WHITE,closed=!1,up=vec3(0,1,0)){var mesh=new Mesh,count=points.length,edges=[];let across=(abs(up.y)<.9?vec3(0,1,0):vec3(1,0,0)).cross(up).normalize();for(let i=0;i<count;++i){var next=points[closed?(i+1)%count:min(i+1,count-1)],last=points[closed?(i+count-1)%count:max(i-1,0)],next=next.subtract(last).cross(up),last=(across=1e-12<next.lengthSquared()?next.normalize():across).scale((isArray(width)?width[i]:width)/2);edges.push([points[i].subtract(last),points[i].add(last)])}for(let i=0;i+1<count+(closed?1:0);++i){var j=(i+1)%count,a=edges[i],b=edges[j],j=isArray(color)?[color[i],color[i],color[j],color[j]]:color;mesh.addQuad(a[0],a[1],b[1],b[0],j)}return mesh.doubleSided=!0,mesh}function buildLoft(stations){var mesh=new Mesh,section=([z,w,t,b,m=.5])=>[vec3(-w/2,lerp(b,t,m),z),vec3(0,t,z),vec3(w/2,lerp(b,t,m),z),vec3(0,b,z)];for(let i=0;i+1<stations.length;++i){var s1=section(stations[i]),s2=section(stations[i+1]);for(let k=0;k<4;++k)mesh.addQuad(s1[k],s1[(k+1)%4],s2[(k+1)%4],s2[k])}var tail=section(stations[stations.length-1]),nose=section(stations[0]);return mesh.addQuad(tail[0],tail[1],tail[2],tail[3]),mesh.addQuad(nose[3],nose[2],nose[1],nose[0]),mesh}function buildExtrude(pixels,size=vec2(1),depth=1){let rows=pixels,width,height;if(pixels instanceof TileInfo){var image=function(textureInfo){textureInfo=textureInfo.image;let pixels=render3DPixelCache.get(textureInfo);pixels||render3DPixelCache.set(textureInfo,pixels=render3DImageData(textureInfo));return pixels}(pixels.textureInfo),data=image.data,x0=0|pixels.pos.x,y0=0|pixels.pos.y;width=0|pixels.size.x,height=0|pixels.size.y,rows=[];for(let y=0;y<height;++y){var row=rows[y]=[];for(let x=0;x<width;++x){var k=4*((y0+y)*image.width+x0+x);row.push(127<data[3+k]?rgb(data[k]/255,data[1+k]/255,data[2+k]/255):void 0)}}}else height=rows.length,width=rows[0].length;const solid=(x,y)=>{if(!(x<0||y<0||x>=width||y>=height)){y=rows[y]&&rows[y][x];if(y)return isColor(y)?.5<y.a?y:void 0:WHITE}};var runs=(count,colorAt,emit)=>{let start=0,color;for(let i=0;i<=count;++i){var c=i<count?colorAt(i):void 0;a=c,b=color,a!==b&&(!a||!b||a.rgbaInt()!==b.rgbaInt())&&(color&&emit(start,i,color),start=i,color=c)}var a,b};const mesh=new Mesh,sx=size.x/width,sy=size.y/height,hz=depth/2,px=x=>x*sx-size.x/2,py=y=>size.y/2-y*sy,quad=(origin,right,up,normal,color)=>mesh.addStrip(render3DQuadAxes(origin.add(right.scale(.5)).add(up.scale(.5)),right.scale(.5),up.scale(.5)),normal,RENDER3D_QUAD_UVS,color),X=vec3(1,0,0),Y=vec3(0,1,0),Z=vec3(0,0,1);for(let y=0;y<height;++y)runs(width,x=>solid(x,y),(a,b,c)=>{var w=X.scale((b-a)*sx),h=Y.scale(sy);quad(vec3(px(a),py(y+1),hz),w,h,Z,c),quad(vec3(px(b),py(y+1),-hz),w.scale(-1),h,Z.scale(-1),c)}),runs(width,x=>solid(x,y-1)?void 0:solid(x,y),(a,b,c)=>quad(vec3(px(a),py(y),hz),X.scale((b-a)*sx),Z.scale(-depth),Y,c)),runs(width,x=>solid(x,y+1)?void 0:solid(x,y),(a,b,c)=>quad(vec3(px(a),py(y+1),-hz),X.scale((b-a)*sx),Z.scale(depth),Y.scale(-1),c));for(let x=0;x<width;++x)runs(height,y=>solid(x-1,y)?void 0:solid(x,y),(a,b,c)=>quad(vec3(px(x),py(b),-hz),Z.scale(depth),Y.scale((b-a)*sy),X.scale(-1),c)),runs(height,y=>solid(x+1,y)?void 0:solid(x,y),(a,b,c)=>quad(vec3(px(x+1),py(b),hz),Z.scale(-depth),Y.scale((b-a)*sy),X,c));return mesh}function buildText3D(text,size=1,depth=.2,font=engineImageFont){const tileInfo=font.tileInfo;let glyphs=render3DGlyphCache.get(font);glyphs||render3DGlyphCache.set(font,glyphs=new Map);const charSize=vec2(size*tileInfo.size.x/tileInfo.size.y,size),mesh=new Mesh,lines=textLines(text);return lines.forEach((line,j)=>{var y=((lines.length-1)/2-j)*charSize.y*1.3,characters=textCharacters(line);for(let i=0;i<characters.length;++i){var charCode=textCharacterCode(characters,i),charCode=charCode<32||127<charCode?95:charCode-32;if(charCode){let glyph=glyphs.get(charCode);glyph||(pos=font.getGlyphPos(charCode),glyphs.set(charCode,glyph=buildExtrude(new TileInfo(pos,tileInfo.size,tileInfo.textureInfo))));var pos,charCode=(i-(characters.length-1)/2)*charSize.x;mesh.combine(glyph,buildMatrix(vec3(charCode,y,0),void 0,vec3(charSize.x,charSize.y,depth)))}}}),mesh}class HeightMap extends EngineObject3D{constructor(heights,mapSize=vec2(1),height=1,colors,pos3D=vec3(),smooth=render3D?.smoothShading){super(pos3D),isArray(heights)||(heights=render3DImageToArray(heights,r=>r/255)),colors&&!isArray(colors)&&(colors=render3DImageToArray(colors,(r,g,b,a)=>rgb(r/255,g/255,b/255,a/255))),this.heights=heights,this.colors=colors,this.mapSize=mapSize.copy(),this.height=height,this.smooth=!!smooth,this.size3D=vec3(mapSize.x,height,mapSize.y),this.rebuild(),render3DLevel.push(this)}get rows(){return this.heights.length}get columns(){return this.heights[0].length}getHeight(x,z){x instanceof Vector3&&(z=x.z,x=x.x);var p=this.pos3D,columns=(x-=p.x,z-=p.z,this.columns),rows=this.rows,h=this.heights,x=clamp((x/this.mapSize.x+.5)*(columns-1),0,columns-1),z=clamp((z/this.mapSize.y+.5)*(rows-1),0,rows-1),columns=min(floor(x),columns-2),rows=min(floor(z),rows-2),x=x-columns,z=z-rows,a=h[rows][columns],b=h[rows+1][columns],c=h[rows+1][columns+1],h=h[rows][columns+1];return(x+z<=1?a+x*(h-a)+z*(b-a):c+(1-x)*(b-c)+(1-z)*(h-c))*this.height+p.y}getNormal(x,z){x instanceof Vector3&&(z=x.z,x=x.x);const p=this.pos3D,size=this.mapSize;var ex=size.x/(this.columns-1)/2,ez=size.y/(this.rows-1)/2;return render3DSlopeNormal((lx,lz)=>this.getHeight(lx+p.x,lz+p.z),x-p.x,z-p.z,ex,ez,size.x/2,size.y/2)}getColor(x,z){x instanceof Vector3&&(z=x.z,x=x.x);var c=this.colors;if(!c)return WHITE;x-=this.pos3D.x,z-=this.pos3D.z;var columns=c[0].length,rows=c.length,x=clamp(round((x/this.mapSize.x+.5)*(columns-1)),0,columns-1);return c[clamp(round((z/this.mapSize.y+.5)*(rows-1)),0,rows-1)][x]}raycast(ray){const p=this.pos3D,origin=ray.origin.subtract(p),direction=ray.direction;var ray=this.mapSize,height=this.height,length=direction.length();if(length){var start=raycastBox(new Ray3D(origin,direction),vec3(0,height/2,0),vec3(ray.x,abs(height)+.001,ray.y));if(void 0!==start&&(0<ray.x&&0<ray.y)){let end=start+hypot(ray.x,ray.y,height)/length;direction.x&&(end=min(end,(sign(direction.x)*ray.x/2-origin.x)/direction.x)),direction.z&&(end=min(end,(sign(direction.z)*ray.y/2-origin.z)/direction.z));var f0,f1,height=(this.columns-1)/ray.x,length=(this.rows-1)/ray.y,u0=(origin.x/ray.x+.5)*(this.columns-1),height=direction.x*height,ray=(origin.z/ray.y+.5)*(this.rows-1),length=direction.z*length,breaks=[start,end];for([f0,f1]of[[u0,height],[ray,length],[u0+ray,height+length]])if(f1){const a=f0+f1*start,b=f0+f1*end;for(let k=ceil(min(a,b));k<=max(a,b);++k)breaks.push((k-f0)/f1)}breaks.sort((a,b)=>a-b);var above=at=>{at=origin.add(direction.scale(at));return at.y+p.y-this.getHeight(at.x+p.x,at.z+p.z)},touching=d=>abs(d)<=1e-9;let a=start,da=above(a);if(touching(da))return start;var startUnder=da<=0;for(const b of breaks)if(!(b<=a||b>end)){var db=above(b);if(touching(db))return b;if(db<=0!=startUnder)return da===db?b:a+(b-a)*da/(da-db);a=b,da=db}}}}buildMesh(smooth=render3D?.smoothShading){const p=this.pos3D,size=this.mapSize,nudgeX=smooth?0:size.x/(this.columns-1)/1e3,nudgeZ=smooth?0:size.y/(this.rows-1)/1e3;return buildGrid(size,vec2(this.columns-1,this.rows-1),this.colors&&((x,z)=>this.getColor(x-nudgeX+p.x,z-nudgeZ+p.z)),(x,z)=>this.getHeight(x+p.x,z+p.z)-p.y,smooth)}rebuild(){this.setMesh(this.buildMesh(this.smooth))}levelRaycast3D(ray){return this.raycast(ray)}levelSegment3D(from,to){var top=this.pos3D.y+max(this.height,0);if(!(from.y>top&&to.y>top||from.y<this.getHeight(from.x,from.z))){top=this.raycast(new Ray3D(from,to.subtract(from)));if(!(void 0===top||1<top||!top&&to.y>=this.getHeight(to.x,to.z)))return{distance:top,normal:this.getNormal(from.x+(to.x-from.x)*top,from.z+(to.z-from.z)*top)}}}levelCollide3D(o,oldPos,wasOn){var p=o.pos3D,m=this.pos3D,size=this.mapSize;if(!(abs(p.x-m.x)>size.x/2||abs(p.z-m.z)>size.y/2)){m=o.size3D.y*abs(o.scale3D.y)/2;let ground=this.getHeight(p.x,p.z);var size=m/2,endFoot=max(p.y-m,ground)+size,size=oldPos&&this.levelSegment3D(vec3(oldPos.x,oldPos.y-m+size,oldPos.z),vec3(p.x,endFoot,p.z));if(size&&size.distance)p.x=oldPos.x+(p.x-oldPos.x)*size.distance,p.z=oldPos.z+(p.z-oldPos.z)*size.distance,ground=this.getHeight(p.x,p.z);else{endFoot=wasOn&&o.velocity3D.y<=0?hypot(p.x-oldPos.x,p.z-oldPos.z):0;if(p.y-m>ground+endFoot)return}p.y=ground+m;size=o.velocity3D;size.y<0&&(size.y*=-max(o.restitution,this.restitution)),o.groundObject=this}}update(){super.update()}destroy(immediate){render3DLevelLeave(this),super.destroy(immediate)}}function render3DImageData(image){var width=(image=image instanceof TextureInfo?image.image:image).width,height=image.height;return workReadCanvas.width=width,workReadCanvas.height=height,workReadContext.drawImage(image,0,0),workReadContext.getImageData(0,0,width,height)}function render3DImageToArray(image,sample){var{data,width,height}=render3DImageData(image),rows=[];for(let y=0;y<height;++y){var row=rows[y]=[];for(let x=0;x<width;++x){var k=4*(y*width+x);row.push(sample(data[k],data[1+k],data[2+k],data[3+k]))}}return rows}const render3DGlyphCache=new WeakMap,render3DPixelCache=new WeakMap;class CameraControl3D extends EngineObject3D{constructor(target=vec3(),distance=10,pitch=.4,idleSpin=0){super(target),this.size3D=vec3(),this.distance=distance,this.pitch=pitch,this.idleSpin=idleSpin,this.yaw=0,this.dragButton=0,this.dragSpeed=.01,this.zoomSpeed=.1,this.zoomRange=vec2(distance/4,3*distance),this.pitchRange=vec2(min(-.2,pitch),max(1.4,pitch))}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(pos3D=render3D.camera.pos,yaw=render3D.camera.rotation.y,pitch=render3D.camera.rotation.x){super(pos3D),this.size3D=vec3(),this.mass=1,this.yaw=yaw,this.pitch=pitch,this.moveSpeed=.1,this.lookSpeed=.003,this.pitchRange=vec2(-1.5,1.5),this.fly=!1,this.lockPointer=!0,this.jumpSpeed=0,this.eyeHeight=0}updatePhysics(){var gravityScale=this.gravityScale;this.fly&&(this.gravityScale=0),super.updatePhysics(),this.gravityScale=gravityScale}update(){this.lockPointer&&mouseWasPressed(0)&&pointerLockRequest(),(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 input=keyDirection(),input=vec3(input.x,0,-input.y).clampLength(1).scale(this.moveSpeed).rotateX(this.fly?this.pitch:0).rotateY(this.yaw),input=(this.velocity3D=this.fly?input:vec3(input.x,this.velocity3D.y,input.z),this.jumpSpeed&&!this.fly&&this.groundObject&&(keyWasPressed("Space")||gamepadWasPressed(0))&&(this.velocity3D.y=this.jumpSpeed),vec3(this.pitch,this.yaw,0));render3D.camera.pos=render3DObjectMatrix(this).transformPoint(vec3(0,this.eyeHeight,0)),render3D.camera.rotation=this.parent instanceof EngineObject3D?this.parent.getMatrix().multiply(Matrix4.rotation(input)).getRotation():input}destroy(immediate){this.lockPointer&&pointerLockIsActive()&&pointerLockExit(),super.destroy(immediate)}render3D(){}}const render3DParticlePos=vec3(),render3DParticleColor=new Color,render3DParticleSize=vec2(),render3DParticleFrom=vec3(),render3DParticleTo=vec3(),render3DParticlesCalling=new Set;function render3DParticleView(emitter){return{emitter:emitter,pos:vec3(),velocity:vec3(),age:0,lifeTime:0,scale:1,destroyed:!1,destroy(){this.destroyed=!0}}}class ParticleEmitter3D extends EngineObject3D{constructor(pos3D=vec3(),emitSize=0,emitTime=0,emitRate=100,emitConeAngle=PI,tileInfo,colorStartA=WHITE,colorStartB=WHITE,colorEndA=CLEAR_WHITE,colorEndB=CLEAR_WHITE,particleTime=.5,sizeStart=.1,sizeEnd=1,speed=.1,damping=1,gravity=0,fadeRate=.1,randomness=.2,additive=!1){super(pos3D,void 0,tileInfo),this.transparent=!0,this.castShadow=!1,this.size3D=vec3(),this.emitSize=emitSize,this.emitFlat=!1,this.emitTime=emitTime,this.emitRate=emitRate,this.emitConeAngle=emitConeAngle,this.colorStartA=colorStartA.copy(),this.colorStartB=colorStartB.copy(),this.colorEndA=colorEndA.copy(),this.colorEndB=colorEndB.copy(),this.particleTime=particleTime,this.sizeStart=sizeStart,this.sizeEnd=sizeEnd,this.speed=speed,this.damping=damping,this.gravity=gravity,this.gravityScale=0,this.fadeRate=fadeRate,this.randomness=randomness,this.additive=additive,this.trailTime=0,this.angleSpeed=0,this.angleDamping=1,this.particleData=new Float32Array(1344),this.particleCount=0,this.trailData=void 0,this.trailMax=0,this.collideLevel=!1,this.stick=0,this.particleCreateCallback=void 0,this.particleUpdateCallback=void 0,this.particleCollideCallback=void 0,this.particleDestroyCallback=void 0,this.particleView=render3DParticleView(this),this.worldPos3D=void 0,this.emitTimeBuffer=1,this.emitElapsed=0,this.emitFrom=void 0,this.emitBehind=0}update(){var matrix=render3DObjectMatrix(this),matrix=(this.emitFrom=this.worldPos3D,this.worldPos3D=matrix.getTranslation(),render3DMaxScale(matrix.m));if(this.particleView.scale=matrix,!this.emitTime||this.emitElapsed<this.emitTime){var rate=this.emitRate*particleEmitRateScale,step=this.emitTime?min(timeDelta,this.emitTime-this.emitElapsed):timeDelta;if(this.emitElapsed+=timeDelta,0<rate&&rate<1/0){this.emitTimeBuffer+=rate*step+(this.emitTime&&this.emitElapsed>=this.emitTime?1e-6:0);var travelled=this.emitFrom&&this.worldPos3D.distance(this.emitFrom)<=2*objectMaxSpeed,count=floor(this.emitTimeBuffer);try{for(let i=count;i--;--this.emitTimeBuffer)this.emitBehind=travelled?i/count:0,this.emitParticle()}finally{this.emitBehind=0}}}else this.particleCount||this.destroy();var trailMax=this.trailTime?max(1,round(this.trailTime*frameRate)):0;if(trailMax!==this.trailMax){this.trailMax=trailMax,this.trailData=trailMax?new Float32Array(this.particleData.length/21*trailMax*3):void 0;for(let k=20;k<this.particleData.length;k+=21)this.particleData[k]=0}let data=this.particleData,trail=this.trailData;var reread=()=>{data=this.particleData,trail=this.trailData},damping=this.damping,angleDamping=this.angleDamping,rate=render3D.gravity,step=this.gravityScale,gravityX=rate.x*step*matrix,gravityY=(this.gravity+rate.y*step)*matrix,gravityZ=rate.z*step*matrix,collideLevel=this.collideLevel&&enablePhysicsSolver&&render3DLevel.length,updateCallback=this.particleUpdateCallback,destroyCallback=this.particleDestroyCallback;for(let i=this.particleCount;i--;){var k=21*i,vx=data[3+k]=data[3+k]*damping+gravityX,vy=data[4+k]=data[4+k]*damping+gravityY,vz=data[5+k]=data[5+k]*damping+gravityZ,vx=(data[k]+=vx,data[1+k]+=vy,data[2+k]+=vz,data[18+k]+=data[19+k]*=angleDamping,collideLevel&&this.particleCollide(k,data[k]-vx,data[1+k]-vy,data[2+k]-vz)),vy=(collideLevel&&reread(),i*trailMax*3);if(trailMax){let n=data[20+k];n===trailMax&&(trail.copyWithin(vy,3+vy,vy+3*n),--n);var vz=vy+3*n;trail[vz]=data[k],trail[1+vz]=data[1+k],trail[2+vz]=data[2+k],data[20+k]=n+1}updateCallback&&!vx&&(this.particleCall(updateCallback,k),reread()),(data[17+k]+=timeDelta)>=data[16+k]&&(destroyCallback&&(this.particleCall(destroyCallback,k),reread()),vx=21*(vz=--this.particleCount),i!==vz)&&(data.copyWithin(k,vx,21+vx),trailMax)&&trail.copyWithin(vy,vz*trailMax*3,(1+vz)*trailMax*3)}}destroy(immediate){if(immediate||!this.particleCount||this.destroyed)return super.destroy(immediate);this.emitTime=-1,render3DDetach(this)}emitParticle(){var random=()=>rand(1-this.randomness,1+this.randomness),matrix=render3DObjectMatrix(this),scale=render3DMaxScale(matrix.m),size=this.emitSize,box=size,flat=this.emitFlat;let offset;offset=isVector3(size)?vec3(rand(-.5,.5)*box.x,flat?0:rand(-.5,.5)*box.y,rand(-.5,.5)*box.z):flat?vec3((box=randInCircle(size/2)).x,0,box.y):randInSphere(size/2);var flat=matrix.transformDirection(randVector3(1,this.emitConeAngle)).normalize(),box=matrix.transformPoint(offset),size=this.speed*random()*scale,matrix=this.emitFrom,behind=this.emitBehind,matrix=(matrix&&behind&&(here=this.worldPos3D,box.x+=(matrix.x-here.x)*behind,box.y+=(matrix.y-here.y)*behind,box.z+=(matrix.z-here.z)*behind),randColor(this.colorStartA,this.colorStartB,!0)),here=randColor(this.colorEndA,this.colorEndB,!0),behind=21*this.particleCount++,grown=(21+behind>this.particleData.length&&((grown=new Float32Array(2*this.particleData.length)).set(this.particleData),this.particleData=grown,this.trailData)&&((grown=new Float32Array(2*this.trailData.length)).set(this.trailData),this.trailData=grown),this.particleData);grown[behind]=box.x,grown[1+behind]=box.y,grown[2+behind]=box.z,grown[3+behind]=flat.x*size,grown[4+behind]=flat.y*size,grown[5+behind]=flat.z*size,grown[6+behind]=matrix.r,grown[7+behind]=matrix.g,grown[8+behind]=matrix.b,grown[9+behind]=matrix.a,grown[10+behind]=here.r,grown[11+behind]=here.g,grown[12+behind]=here.b,grown[13+behind]=here.a,grown[14+behind]=this.sizeStart*random()*scale,grown[15+behind]=this.sizeEnd*random()*scale,grown[16+behind]=this.particleTime*random(),grown[17+behind]=0,grown[18+behind]=this.angleSpeed?rand(2*PI):0,grown[19+behind]=this.angleSpeed?this.angleSpeed*random()*randSign():0,grown[20+behind]=0,this.particleView.scale=scale,this.particleCreateCallback&&this.particleCall(this.particleCreateCallback,behind)}particleCall(callback,k,level,pos){var nested=render3DParticlesCalling.has(this),view=nested?render3DParticleView(this):this.particleView,data=this.particleData;view.scale=this.particleView.scale,view.pos.set(data[k],data[k+1],data[k+2]),view.velocity.set(data[k+3],data[k+4],data[k+5]),view.age=data[k+17],view.lifeTime=data[k+16],view.destroyed=!1,render3DParticlesCalling.add(this);let result;try{result=callback(view,level,pos)}finally{nested||render3DParticlesCalling.delete(this)}data=this.particleData;return data[k]=view.pos.x,data[k+1]=view.pos.y,data[k+2]=view.pos.z,data[k+3]=view.velocity.x,data[k+4]=view.velocity.y,data[k+5]=view.velocity.z,view.destroyed&&(data[k+17]=max(data[k+17],data[k+16])),result}particleCollide(k,x,y,z){var data=this.particleData,from=render3DParticleFrom.set(x,y,z),to=render3DParticleTo.set(data[k],data[k+1],data[k+2]);let hit,level;for(const l of render3DLevel){var h=l.destroyed?void 0:l.levelSegment3D(from,to);h&&(!hit||h.distance<hit.distance)&&(hit=h,level=l)}if(hit){x=from.lerp(to,hit.distance),y=this.particleCollideCallback;if(y&&(!this.particleCall(y,k,level,x.copy())||this.particleView.destroyed))return this.particleView.destroyed;var data=this.particleData,z=hit.normal,y=vec3(data[k+3],data[k+4],data[k+5]),into=z.scale(y.dot(z)),restitution=max(this.restitution,level.restitution),friction=max(this.friction,level.friction)*(1-clamp(this.stick)),y=y.subtract(into).scale(friction).subtract(into.scale(restitution)),friction=x.add(z.scale(.001));data[k]=friction.x,data[k+1]=friction.y,data[k+2]=friction.z,data[k+3]=y.x,data[k+4]=y.y,data[k+5]=y.z}return!1}render3D(){var r=render3D;if(r.transparentQueue)return r.queueTransparent(this.getWorldPos3D(),()=>this.render3D());var fade=this.fadeRate/2,texture=this.tileInfo||function(){if(render3DSoftDotTexture||!glContext||!canvasAvailable())return render3DSoftDotTexture;var stop,alpha,context=createCanvasContext(32),gradient=context.createRadialGradient(16,16,0,16,16,16);for([stop,alpha]of[[0,1],[.33,.9],[.67,.7],[1,0]])gradient.addColorStop(stop,"rgba(255,255,255,"+alpha+")");return context.fillStyle=gradient,context.fillRect(0,0,32,32),render3DSoftDotTexture=new TextureInfo(context.canvas)}(),color=render3DParticleColor,size=render3DParticleSize,quad=r.billboardMesh,instanced=texture&&r.instancing&&!r.capture&&render3DCanDraw();let data,textureInfo,uv,lit;instanced&&(render3DMeshUpload(quad),textureInfo=render3DTextureOf(texture),uv=render3DGetTileUVs(texture),lit=r.lighting,r.lighting=r.shadowPass&&lit,r.cullBackFaces=r.mirrored=!1);var cb=r.cameraBack,particles=this.particleData,trailMax=this.trailMax,pos=render3DParticlePos;try{for(let i=0,count=this.particleCount;i<count;++i){var a,k,p=21*i,t=particles[17+p]/particles[16+p],alpha=t<fade?t/fade:1-fade<t?(1-t)/fade:1,s=(color.r=particles[6+p]+(particles[10+p]-particles[6+p])*t,color.g=particles[7+p]+(particles[11+p]-particles[7+p])*t,color.b=particles[8+p]+(particles[12+p]-particles[8+p])*t,color.a=(particles[9+p]+(particles[13+p]-particles[9+p])*t)*alpha,size.x=size.y=lerp(particles[14+p],particles[15+p],t)),angle=particles[18+p],trailCount=particles[20+p];if(1<trailCount){var trail=this.trailData,points=[],widths=[],colors=[];for(let j=0;j<trailCount;++j){var f=(j+1)/trailCount,q=3*(i*trailMax+j);points.push(vec3(trail[q],trail[1+q],trail[2+q])),widths.push(s*f),colors.push(color.scale(1,f))}r.drawRibbon(points,widths,this.tileInfo,colors)}else instanced?(a=render3DBillboardAxes(size,angle,!1),k=render3DInstanceSlot(quad,textureInfo),(data=quad.instanceData)[k]=2*a[0],data[k+1]=2*a[1],data[k+2]=2*a[2],data[k+3]=0,data[k+4]=2*a[3],data[k+5]=2*a[4],data[k+6]=2*a[5],data[k+7]=0,data[k+8]=cb.x,data[k+9]=cb.y,data[k+10]=cb.z,data[k+11]=0,data[k+12]=particles[p],data[k+13]=particles[1+p],data[k+14]=particles[2+p],data[k+15]=1,data[k+16]=color.r,data[k+17]=color.g,data[k+18]=color.b,data[k+19]=color.a,data[k+20]=uv.x,data[k+21]=uv.y,data[k+22]=uv.w,data[k+23]=uv.h):(pos.x=particles[p],pos.y=particles[1+p],pos.z=particles[2+p],texture?r.drawBillboard(pos,size,texture,color,angle):r.drawSoftDisc(pos,s,color,void 0,8))}}finally{instanced&&(r.lighting=lit,r.flush(),render3DFlushInstances(quad))}}}class Trail3D extends EngineObject3D{constructor(pos3D=vec3(),lifeTime=1,width=.2,tileInfo,color=WHITE,colorEnd=CLEAR_WHITE,additive=!1){super(pos3D,void 0,tileInfo,color),this.transparent=!0,this.additive=additive,this.castShadow=!1,this.size3D=vec3(),this.finishing=!1,this.lifeTime=lifeTime,this.width=width,this.colorEnd=colorEnd.copy(),this.side=void 0,this.samples=[]}clear(){this.samples.length=0}destroy(immediate){if(immediate||!this.samples.length||this.destroyed||this.lifeTime==1/0)return super.destroy(immediate);this.finishing=!0,render3DDetach(this)}update(){var pos,last,samples=this.samples;for(!this.finishing&&(pos=this.worldPos3D=this.getWorldPos3D(),!(last=samples[samples.length-1])||1e-8<pos.distanceSquared(last.pos))&&samples.push({pos:pos,side:this.side?.copy(),time:time});samples.length&&time-samples[0].time>this.lifeTime;)samples.shift();this.finishing&&!samples.length&&this.destroy()}render3D(){var samples=this.samples;if(!(samples.length<2)){var points=[],widths=[],colors=[],sides=this.side?[]:void 0;for(const s of samples){var age=clamp((time-s.time)/this.lifeTime);points.push(s.pos),widths.push(this.width*(1-age)),colors.push(this.color.lerp(this.colorEnd,age)),sides?.push(s.side)}render3D.drawRibbon(points,widths,this.tileInfo,colors,sides)}}}const render3DFlareShapes=["glow","disc","ring","hex","streak","star"];let render3DFlareTiles;class LensFlare3D extends EngineObject3D{constructor(size=1,count=7,intensity=1,saturation=1,color=WHITE){super(vec3(),void 0,void 0,color),this.size3D=vec3(),this.flareSize=size,this.count=count,this.intensity=intensity,this.saturation=saturation,this.seed=1,this.shapes=void 0,this.glowSize=1,this.ghostSize=1,this.elements=void 0,this.light=void 0,this.occlusion=!0,this.fadeTime=.15,this.visible=0,this.visibleFound=!1,this.renderOrder=1e9,this.madeKey="",this.made=[]}getElements(){if(this.elements)return this.elements;var shapes=this.shapes?.length?this.shapes:void 0,glow=this.glowSize,ghost=this.ghostSize,key=[this.count,this.seed,this.saturation,glow,ghost,shapes?.length,shapes].join();if(key!==this.madeKey){var random=new RandomGenerator(7919*this.seed+1),s=clamp(this.saturation),made=this.made=0<glow?[{at:0,size:.7*glow,color:hsl(0,0,1,.5),shape:"glow"},{at:0,size:.25*glow,color:hsl(0,0,1,.9),shape:"glow"}]:[];for(let i=0;i<this.count;++i){var at=(i+random.float(.2,.8))/max(this.count,1)*1.9+.2,pick=random.float(),pick=shapes?shapes[min(pick*shapes.length|0,shapes.length-1)]:pick<.5?"disc":pick<.8?"ring":"glow";made.push({at:at,size:random.float(.04,.1)*(1+at)*ghost,shape:pick,color:hsl(random.float(),.9*s,.6,random.float(.15,.4))})}this.madeKey=key}return this.made}flareSource(){var from,camera=render3D.camera.pos,light=this.light;if(!light?.destroyed)return!light||light.directional?(from=light?light.getWorldPos3D():render3D.sunDirection).lengthSquared()?{direction:from=from.normalize(),distance:1/0,pos:camera.add(from.scale(100))}:void 0:(camera=(light=(from=light.getWorldPos3D()).subtract(camera)).length())?{direction:light.scale(1/camera),distance:camera,pos:from}:void 0}flareLook(){var source=this.flareSource(),center=mainCanvasSize.scale(.5),sun=source&&render3D.worldToScreen(source.pos);if(sun&&center.x&&center.y){var strength=clamp((1.3-max(abs(sun.x-center.x)/center.x,abs(sun.y-center.y)/center.y))/.5)*this.intensity;if(0<strength){let tint=this.color.multiply(render3D.sunColor),height=mainCanvasSize.y*this.flareSize;var light=this.light;if(light){var cone=render3DLightCone(light),d=source.direction,d=clamp(-(cone[0]*d.x+cone[1]*d.y+cone[2]*d.z)-cone[3]),cone=light.color,d=cone.a*clamp(light.intensity)*d;if(!(0<d))return;tint=this.color.multiply(rgb(cone.r,cone.g,cone.b,d)),light.directional||(height*=min(1,light.radius/source.distance))}return 0<height?{sun:sun,center:center,strength:strength,tint:tint,height:height}:void 0}}}getScreenPos(){var source=this.flareSource();return source&&render3D.worldToScreen(source.pos)}getScreenElements(){this.visibleFound&&!paused||(this.visibleFound=!1,this.updateVisible(0));var look=0<this.visible&&this.flareLook();if(!look)return[];const{sun,center,tint,height}=look,strength=look.strength*this.visible;return this.getElements().map(e=>{var c=e.color.multiply(tint),size="number"==typeof e.size?e.size*height:e.size.scale(height);return{pos:sun.add(center.subtract(sun).scale(e.at)),size:size,shape:e.shape,tileInfo:e.tileInfo,angle:e.angle||0,color:rgb(c.r,c.g,c.b,c.a*strength)}})}isHidden(){const source=this.flareSource();if(!source)return!0;var ray=new Ray3D(render3D.camera.pos,source.direction),reach=source.distance,blockers=engineObjects.filter(o=>o!==this&&o instanceof EngineObject3D&&!o.destroyed&&!o.transparent&&!render3DSetting(o,"additive"));const maps=void 0===VoxelMap?[]:blockers.filter(o=>o instanceof VoxelMap);for(const map of maps)if(map.raycast(ray,reach,type=>!map.blockType(type).seeThrough))return!0;const near=this.light&&reach<1/0?min(2,.4*this.light.radius):0;let candidates=blockers.filter(o=>!maps.some(map=>map===o));for(let tries=5;tries--;){const hit=render3D.pick(ray,candidates);if(!hit||hit.distance>=reach)return!1;if(!(reach-hit.distance<=near&&(o=>{if(!o.mesh||o instanceof HeightMap)return!1;var matrix=render3DObjectMatrix(o),o=o.mesh.bounds||o.mesh.getBounds();const m=matrix.m,scale=k=>hypot(m[k],m[k+1],m[k+2]);var thinnest=min((o.max.x-o.min.x)*scale(0),(o.max.y-o.min.y)*scale(4),(o.max.z-o.min.z)*scale(8));return!(!matrix.determinant()||thinnest>2*near)&&(thinnest=matrix.copy().invert().transformPoint(source.pos)).x>=o.min.x&&thinnest.x<=o.max.x&&thinnest.y>=o.min.y&&thinnest.y<=o.max.y&&thinnest.z>=o.min.z&&thinnest.z<=o.max.z})(hit.object)))return!0;candidates=candidates.filter(o=>o!==hit.object)}return!0}update(){if(super.update(),this.light?.destroyed)return this.destroy();this.updateVisible(timeDelta)}updateVisible(seconds){this.visibleFound||render3D.updateMatrices();var shows=!(!this.flareLook()||this.occlusion&&this.isHidden()),seconds=0<this.fadeTime&&this.visibleFound?seconds/this.fadeTime:1;this.visible=clamp(this.visible+(shows?seconds:-seconds)),this.visibleFound=!0}render(){if(!headlessMode&&glEnable){var elements=this.getScreenElements();if(elements.length){setAdditiveBlendMode(!0);for(const e of elements){var tileInfo=e.tileInfo||function(shape){if(!render3DFlareTiles){if(headlessMode||!glContext||!canvasAvailable())return;const shapes=render3DFlareShapes,context=createCanvasContext(128*shapes.length,128),alpha={glow:t=>engineGlowAlpha(t,1),disc:t=>t<.8?.4+.1*t:t<.92?.48+3*(t-.8):(1-t)/.08*.84,ring:t=>max(0,1-abs(t-.86)/.12),hex:t=>t<.8?.4+.1*t:t<.92?.48+3*(t-.8):(1-t)/.08*.84,streak:t=>(1-t)**1.5},fill=(shape,r,path)=>{var gradient=context.createRadialGradient(0,0,0,0,0,r);for(let k=0;k<=32;++k)gradient.addColorStop(k/32,"rgba(255,255,255,"+clamp(alpha[shape](k/32)).toFixed(4)+")");context.fillStyle=gradient,context.beginPath(),path?path():context.arc(0,0,r,0,2*PI),context.fill()},ray=(r,angle)=>{context.save(),context.rotate(angle),context.scale(1,.07),fill("streak",r),context.restore()},texture=(shapes.forEach((shape,i)=>{if(context.save(),context.translate(128*i+64,64),"hex"==shape){var image=context.createImageData(128,128),data=image.data,apothem=62*cos(PI/6);for(let k=0;k<16384;++k){var x=abs(k%128+.5-64),y=abs(.5+(k/128|0)-64),x=max(y,x*cos(PI/6)+y/2)/apothem;data[4*k]=data[4*k+1]=data[4*k+2]=255,data[4*k+3]=x<1?255*clamp(alpha.hex(x)):0}context.putImageData(image,128*i,0)}else if("streak"==shape)ray(62,0);else if("star"==shape){for(let k=3;k--;)ray(62,k*PI/3+PI/2);context.scale(.3,.3),fill("glow",62)}else fill(shape,62);context.restore()}),new TextureInfo(context.canvas,!0,!1,!1));render3DFlareTiles=new Map(shapes.map((shape,i)=>[shape,new TileInfo(vec2(128*i,0),vec2(128),texture)]))}return render3DFlareTiles.get(shape)||render3DFlareTiles.get("glow")}(e.shape),size="number"==typeof e.size?vec2(e.size):e.size;tileInfo&&drawTile(e.pos,size,tileInfo,e.color,e.angle,!1,void 0,!0,!0)}setAdditiveBlendMode(!1)}}}render3D(){}}function parseOBJ(text,smooth=render3D?.smoothShading){const positions=[],normals=[],uvs=[],points=[],vertexNormals=[],vertexUVs=[],indices=[],seen=new Map,fromFile=[];let missingNormals=!1,face=0;const index=(s,list)=>{s=parseInt(s);return s<0?list.length+s:s-1},lookup=(s,list)=>list[index(s,list)];for(const line of text.replace(/\\\r?\n/g," ").split(/\r\n?|\n/)){var parts=line.replace(/#.*/,"").trim().split(/\s+/);switch(parts[0]){case"v":positions.push(vec3(+parts[1],+parts[2],+parts[3]));break;case"vn":normals.push(vec3(+parts[1],+parts[2],+parts[3]));break;case"vt":uvs.push(vec2(+parts[1],1-(+parts[2]||0)));break;case"f":var corners=parts.slice(1).map(c=>c.split("/"));if(!(corners.length<3)){const facePoints=corners.map(c=>lookup(c[0],positions));if(facePoints.every(isVector3)){const hasNormals=corners.every(c=>c[2]),faceNormal=(missingNormals||=!hasNormals,hasNormals?void 0:render3DFaceNormal(facePoints[0],facePoints[1],facePoints[2],facePoints[3]));var ids=corners.map((c,i)=>{var key=index(c[0],positions)+"/"+(c[1]?index(c[1],uvs):"")+"/"+(hasNormals?index(c[2],normals):smooth?"":"f"+face);let id=seen.get(key);return void 0===id&&(seen.set(key,id=points.length),points.push(facePoints[i]),vertexUVs.push(c[1]?lookup(c[1],uvs):RENDER3D_DEFAULT_UV),vertexNormals.push(hasNormals?lookup(c[2],normals):faceNormal),fromFile.push(hasNormals)),id});for(let k=2;k<ids.length;++k)indices.push(ids[1],ids[k],ids[(k+1)%ids.length]);++face}}}}const mesh=(new Mesh).addTriangles(points,indices,vertexNormals,vertexUVs);if(missingNormals&&smooth){const given=mesh.normals;mesh.computeNormals(!0),fromFile.forEach((f,i)=>f&&(mesh.normals[i]=given[i]))}return mesh}async function loadOBJ(url,smooth=render3D?.smoothShading){var text,response=await loadFetch(url,"loadOBJ");if(response.ok)return(smooth=parseOBJ(text=await response.text(),smooth)).points.length||console.warn("loadOBJ: "+url+" has no faces"+(/^\s*</.test(text)?", it is a web page, so the path may be wrong":"")),smooth;throw new Error(`loadOBJ: could not load ${url}, ${response.status} `+response.statusText)}const RENDER3D_CSG_EPSILON=1e-5;class Render3DCSGVertex{constructor(pos,normal,uv,color){this.pos=pos,this.normal=normal,this.uv=uv,this.color=color}lerp(v,t){return new Render3DCSGVertex(this.pos.lerp(v.pos,t),this.normal.lerp(v.normal,t),this.uv.lerp(v.uv,t),this.color.lerp(v.color,t))}flipped(){return new Render3DCSGVertex(this.pos,this.normal.scale(-1),this.uv,this.color)}}class Render3DCSGPolygon{constructor(vertices,plane){this.vertices=vertices,this.plane=plane}flipped(){var plane={normal:this.plane.normal.scale(-1),w:-this.plane.w};return new Render3DCSGPolygon(this.vertices.map(v=>v.flipped()).reverse(),plane)}}class Render3DCSGNode{constructor(){this.plane=void 0,this.front=void 0,this.back=void 0,this.polygons=[]}}function render3DCSGSplit(plane,polygon,coplanarFront,coplanarBack,front,back){var{normal,w}=plane,vertices=polygon.vertices,types=[];let polygonType=0;for(const v of vertices){var t=normal.dot(v.pos)-w,t=t<-RENDER3D_CSG_EPSILON?2:t>RENDER3D_CSG_EPSILON?1:0;polygonType|=t,types.push(t)}if(0===polygonType)(0<normal.dot(polygon.plane.normal)?coplanarFront:coplanarBack).push(polygon);else if(1===polygonType)front.push(polygon);else if(2===polygonType)back.push(polygon);else{var f=[],b=[];for(let i=0;i<vertices.length;++i){var j=(i+1)%vertices.length,ti=types[i],tj=types[j],vi=vertices[i],j=vertices[j];2!==ti&&f.push(vi),1!==ti&&b.push(vi),3==(ti|tj)&&(ti=vi.lerp(j,(w-normal.dot(vi.pos))/normal.dot(j.pos.subtract(vi.pos))),f.push(ti),b.push(ti))}3<=f.length&&front.push(new Render3DCSGPolygon(f,polygon.plane)),3<=b.length&&back.push(new Render3DCSGPolygon(b,polygon.plane))}}function render3DCSGBuild(root,polygons){for(var stack=[[root,polygons]];stack.length;){var[node,list]=stack.pop();if(list.length){node.plane||=list[0].plane;var front=[],back=[];for(const polygon of list)render3DCSGSplit(node.plane,polygon,node.polygons,node.polygons,front,back);front.length&&stack.push([node.front||=new Render3DCSGNode,front]),back.length&&stack.push([node.back||=new Render3DCSGNode,back])}}}function render3DCSGInvert(root){for(const node of function(root){for(var nodes=[],stack=[root];stack.length;){var node=stack.pop();nodes.push(node),node.front&&stack.push(node.front),node.back&&stack.push(node.back)}return nodes}(root))node.plane&&={normal:node.plane.normal.scale(-1),w:-node.plane.w},[node.front,node.back]=[node.back,node.front]}function render3DCSGClipPolygons(root,polygons){for(var result=[],stack=[[root,polygons]];stack.length;){var[node,list]=stack.pop();if(node.plane){var front=[],back=[];for(const polygon of list)render3DCSGSplit(node.plane,polygon,front,back,front,back);if(node.front)stack.push([node.front,front]);else for(const polygon of front)result.push(polygon);node.back&&stack.push([node.back,back])}else for(const polygon of list)result.push(polygon)}return result}function render3DCSGNear(polygons,polygonsOther){var lo=vec3(1/0),hi=vec3(-1/0);for(const polygon of polygonsOther)for(var{pos}of polygon.vertices)lo.x=min(lo.x,pos.x),lo.y=min(lo.y,pos.y),lo.z=min(lo.z,pos.z),hi.x=max(hi.x,pos.x),hi.y=max(hi.y,pos.y),hi.z=max(hi.z,pos.z);var near=[],far=[],faces=[[vec3(1,0,0),hi.x],[vec3(-1,0,0),-lo.x],[vec3(0,1,0),hi.y],[vec3(0,-1,0),-lo.y],[vec3(0,0,1),hi.z],[vec3(0,0,-1),-lo.z]],e=RENDER3D_CSG_EPSILON;for(const polygon of polygons){let x0=1/0,y0=1/0,z0=1/0,x1=-1/0,y1=-1/0,z1=-1/0;for(const{pos}of polygon.vertices)x0=min(x0,pos.x),y0=min(y0,pos.y),z0=min(z0,pos.z),x1=max(x1,pos.x),y1=max(y1,pos.y),z1=max(z1,pos.z);if(x0>hi.x+e||x1<lo.x-e||y0>hi.y+e||y1<lo.y-e||z0>hi.z+e||z1<lo.z-e)far.push(polygon);else{let inside=[polygon];for(var[normal,w]of faces){var next=[];for(const part of inside)render3DCSGSplit({normal:normal,w:w},part,next,next,far,next);inside=next}for(const part of inside)near.push(part)}}return[near,far]}function render3DCSGPolygons(mesh,matrix){const list=(new Mesh).combine(mesh,matrix).toIndexed();var vertex=i=>new Render3DCSGVertex(list.points[i],list.normals[i],list.uvs[i],list.colors[i]),polygons=[];for(let t=0;t<list.indices.length;t+=3){var i=list.indices[t],j=list.indices[t+1],k=list.indices[t+2],plane=function(a,b,c){if(!((b=b.subtract(a).cross(c.subtract(a))).lengthSquared()<1e-18))return{normal:c=b.normalize(),w:c.dot(a)}}(list.points[i],list.points[j],list.points[k]);plane&&polygons.push(new Render3DCSGPolygon([vertex(i),vertex(j),vertex(k)],plane))}return polygons}function render3DCSGMesh(polygons){const reach=4*RENDER3D_CSG_EPSILON,placer=function(distance){const cells=new Map;return p=>{var cx=floor(p.x/distance),cy=floor(p.y/distance),cz=floor(p.z/distance);for(let x=cx-1;x<=cx+1;++x)for(let y=cy-1;y<=cy+1;++y)for(let z=cz-1;z<=cz+1;++z)for(const q of cells.get(x+","+y+","+z)||[])if(q.distanceSquared(p)<distance**2)return q;var k=cx+","+cy+","+cz;return cells.has(k)||cells.set(k,[]),cells.get(k).push(p),p}}(RENDER3D_CSG_EPSILON),placed=new Set;var polygons=polygons.map(polygon=>{var loop=[];for(const v of polygon.vertices){var pos=(p=>{p=placer(p);return placed.add(p),p})(v.pos);loop.length&&loop[loop.length-1].pos===pos||loop.push(new Render3DCSGVertex(pos,v.normal,v.uv,v.color))}return 1<loop.length&&loop[0].pos===loop[loop.length-1].pos&&loop.pop(),loop}),places=[...placed];let lo=vec3(1/0),hi=vec3(-1/0);for(const p of places)lo=vec3(min(lo.x,p.x),min(lo.y,p.y),min(lo.z,p.z)),hi=vec3(max(hi.x,p.x),max(hi.y,p.y),max(hi.z,p.z));var span=places.length?max(hi.x-lo.x,hi.y-lo.y,hi.z-lo.z):0;const size=max(span/Math.cbrt(places.length||1),reach),grid=new Map;for(const p of places){var k=(p=>floor((p.x-lo.x)/size)+","+floor((p.y-lo.y)/size)+","+floor((p.z-lo.z)/size))(p);grid.has(k)||grid.set(k,[]),grid.get(k).push(p)}const mesh=new Mesh,shared=new Map;mesh.indices=[];for(const loop of polygons)if(!(loop.length<3)){const full=[];for(let i=0;i<loop.length;++i){var t,p,a=loop[i],b=loop[(i+1)%loop.length];full.push(a);for([t,p]of((a,b)=>{var t,found=[],ab=b.subtract(a),length2=ab.lengthSquared(),x0=floor((min(a.x,b.x)-lo.x-reach)/size),x1=floor((max(a.x,b.x)-lo.x+reach)/size),y0=floor((min(a.y,b.y)-lo.y-reach)/size),y1=floor((max(a.y,b.y)-lo.y+reach)/size),z0=floor((min(a.z,b.z)-lo.z-reach)/size),z1=floor((max(a.z,b.z)-lo.z+reach)/size);for(let x=x0;x<=x1;++x)for(let y=y0;y<=y1;++y)for(let z=z0;z<=z1;++z)for(const p of grid.get(x+","+y+","+z)||[])p===a||p===b||(t=p.subtract(a).dot(ab)/length2)<=0||1<=t||a.add(ab.scale(t)).distanceSquared(p)<reach**2&&found.push([t,p]);return found.sort((f,g)=>f[0]-g[0])})(a.pos,b.pos)){var v=a.lerp(b,t);full.push(new Render3DCSGVertex(p,v.normal,v.uv,v.color))}}const n=full.length,pos=i=>full[i%n].pos;let area=vec3();for(let j=1;j<n-1;++j)area=area.add(pos(j).subtract(pos(0)).cross(pos(j+1).subtract(pos(0))));if(!(area.lengthSquared()<1e-18)){const ring=full.map(v=>({pos:v.pos,id:(v=>{var n=v.normal.lengthSquared()?v.normal.normalize():RENDER3D_DEFAULT_NORMAL,c=v.color,key=[n.x,n.y,n.z,v.uv.x,v.uv.y,c.r,c.g,c.b,c.a].map(x=>round(1e6*x)).join();let atPlace=shared.get(v.pos),i;return atPlace||shared.set(v.pos,atPlace=new Map),void 0===(i=atPlace.get(key))&&(atPlace.set(key,i=mesh.points.length),mesh.points.push(v.pos.copy()),mesh.normals.push(n),mesh.uvs.push(v.uv.copy()),mesh.colors.push(c.copy())),i})(v)})),earArea=i=>{var a=ring[(i+ring.length-1)%ring.length].pos,b=ring[i].pos,i=ring[(i+1)%ring.length].pos;return b.subtract(a).cross(i.subtract(a))};for(;3<ring.length;){let i=ring.findIndex((_,i)=>{i=earArea(i);return 1e-18<=i.lengthSquared()&&1e-18<=area.subtract(i).lengthSquared()});i<0&&(i=0),area=area.subtract(earArea(i)),mesh.indices.push(ring[(i+ring.length-1)%ring.length].id,ring[i].id,ring[(i+1)%ring.length].id),ring.splice(i,1)}mesh.indices.push(ring[0].id,ring[1].id,ring[2].id)}}return mesh.dirty=!0,mesh}function render3DMeshCSG(a,b,matrix,operation){var a=render3DCSGPolygons(a),b=render3DCSGPolygons(b,matrix),[matrix,farA]=render3DCSGNear(a,b),[nearB,farB]=render3DCSGNear(b,a),treeA=new Render3DCSGNode,treeB=new Render3DCSGNode,a=(render3DCSGBuild(treeA,a),render3DCSGBuild(treeB,b),render3DCSGClipPolygons),b=list=>list.map(polygon=>polygon.flipped());let polygons;return render3DCSGMesh(polygons=0===operation?[...farA,...a(treeB,matrix),...farB,...b(a(treeA,b(a(treeA,nearB))))]:1===operation?(render3DCSGInvert(treeA),[...farA,...b(a(treeB,b(matrix))),...a(treeA,b(a(treeA,nearB)))]):(render3DCSGInvert(treeA),render3DCSGInvert(treeB),[...b(a(treeB,b(matrix))),...b(a(treeA,b(a(treeA,nearB))))]))}const RENDER3D_VOXEL_FACES=[[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)],[vec3(0,0,1),vec3(1,0,0),vec3(0,1,0)],[vec3(0,0,-1),vec3(-1,0,0),vec3(0,1,0)]],RENDER3D_VOXEL_SHADES=[.55,.7,.85,1].map(v=>Object.freeze(rgb(v,v,v))),RENDER3D_VOXEL_CORNERS=RENDER3D_VOXEL_FACES.map(([normal,right,up])=>render3DQuadAxes(normal.scale(.5),right.scale(.5),up.scale(.5))),RENDER3D_VOXEL_CORNER_SIDES=[[-1,1],[-1,-1],[1,1],[1,-1]],RENDER3D_VOXEL_ORDER=[0,1,2,3],RENDER3D_VOXEL_FLIPPED=[1,3,0,2];class VoxelMap extends EngineObject3D{constructor(pos3D=vec3(),mapSize=vec3(16),tileInfo=tile()){super(pos3D),this.mapSize=mapSize.floor(),this.tileInfo=tileInfo,this.data=new Uint8Array(this.mapSize.x*this.mapSize.y*this.mapSize.z),this.ambientOcclusion=!0,this.size3D=vec3();pos3D=this.mapSize,this.chunkCount=vec3(ceil(pos3D.x/16),ceil(pos3D.y/16),ceil(pos3D.z/16)),this.chunkMeshes=[],this.chunkTransparentMeshes=[],this.chunkCenters=[],this.chunksChanged=new Set,this.blockTypes=[],this.tiles=new Map,mapSize=new EngineObject3D;mapSize.size3D=vec3(),mapSize.transparent=!0,mapSize.render3D=()=>this.renderChunks(!0),(mapSize.drawOwner=this).addChild(mapSize),render3DLevel.push(this)}getVoxel(cell){return this.voxelAt(floor(cell.x),floor(cell.y),floor(cell.z))}voxelAt(x,y,z){var s=this.mapSize;return x<0||y<0||z<0||x>=s.x||y>=s.y||z>=s.z?0:this.data[x+s.x*(y+s.y*z)]}setVoxel(cell,type){var x=floor(cell.x),y=floor(cell.y),z=floor(cell.z),cell=this.mapSize;if(!(x<0||y<0||z<0||x>=cell.x||y>=cell.y||z>=cell.z)){cell=x+cell.x*(y+cell.y*z);if(this.data[cell]!==type){this.data[cell]=type,this.markChunk(x,y,z);for(let dz=-1;dz<=1;++dz)for(let dy=-1;dy<=1;++dy)for(let dx=-1;dx<=1;++dx)(this.ambientOcclusion||abs(dx)+abs(dy)+abs(dz)===1)&&this.voxelAt(x+dx,y+dy,z+dz)&&this.markChunk(x+dx,y+dy,z+dz)}}}markChunk(x,y,z){var s=this.mapSize,c=this.chunkCount;x<0||y<0||z<0||x>=s.x||y>=s.y||z>=s.z||this.chunksChanged.add(floor(x/16)+c.x*(floor(y/16)+c.y*floor(z/16)))}setBlockType(type,faces,{seeThrough=!1,transparent=!1,doubleSided=!1}={}){faces=isNumber(faces)?[faces,faces,faces,faces,faces,faces]:isArray(faces)?faces:[faces.side,faces.side,faces.top??faces.side,faces.bottom??faces.side,faces.side,faces.side];this.blockTypes[type]={faces:faces,seeThrough:seeThrough||transparent,transparent:transparent,doubleSided:doubleSided},this.rebuild()}blockType(type){return this.blockTypes[type]||={faces:[type,type,type,type,type,type],seeThrough:!1,transparent:!1,doubleSided:!1}}rebuild(){this.tiles.clear();var c=this.chunkCount;for(let i=c.x*c.y*c.z;i--;)this.chunksChanged.add(i)}buildChunks(){for(const index of this.chunksChanged)this.buildChunk(index);this.chunksChanged.clear()}buildChunk(index){var c=this.chunkCount,n=16,s=this.mapSize,data=this.data,x0=index%c.x*n,y0=floor(index/c.x)%c.y*n,c=floor(index/(c.x*c.y))*n,x1=min(x0+n,s.x),y1=min(y0+n,s.y),z1=min(c+n,s.z),center=vec3((x0+x1)/2,(y0+y1)/2,(c+z1)/2),opaque=new Mesh,transparent=new Mesh,ao=this.ambientOcclusion,solid=(x,y,z)=>{x=this.voxelAt(x,y,z);return x&&!this.blockType(x).seeThrough?1:0},points=[],uvs=[],colors=[],shade=[];for(let z=c;z<z1;++z)for(let y=y0;y<y1;++y)for(let x=x0;x<x1;++x){var type=data[x+s.x*(y+s.y*z)];if(type){var block=this.blockType(type),mesh=block.transparent?transparent:opaque,cx=x+.5-center.x,cy=y+.5-center.y,cz=z+.5-center.z;for(let f=0;f<6;++f){var[normal,right,up]=RENDER3D_VOXEL_FACES[f],bx=x+normal.x,by=y+normal.y,bz=z+normal.z,next=this.voxelAt(bx,by,bz);if(!next||next!==type&&this.blockType(next).seeThrough){for(let k=0;k<4;++k){var[r,u]=RENDER3D_VOXEL_CORNER_SIDES[k],side1=ao&&solid(bx+right.x*r,by+right.y*r,bz+right.z*r),side2=ao&&solid(bx+up.x*u,by+up.y*u,bz+up.z*u),r=ao&&solid(bx+right.x*r+up.x*u,by+right.y*r+up.y*u,bz+right.z*r+up.z*u);shade[k]=side1&&side2?0:3-+side1-+side2-+r}var corners=RENDER3D_VOXEL_CORNERS[f],quad=this.tileUVs(block.faces[f]),order=shade[1]+shade[2]<shade[0]+shade[3]?RENDER3D_VOXEL_FLIPPED:RENDER3D_VOXEL_ORDER;for(let k=0;k<4;++k){var i=order[k],p=corners[i];points[k]=vec3(cx+p.x,cy+p.y,cz+p.z),uvs[k]=quad[i],colors[k]=ao?RENDER3D_VOXEL_SHADES[shade[i]]:WHITE}mesh.addStrip(points,normal,uvs,colors),block.doubleSided&&mesh.addStrip([points[0],points[2],points[1],points[3]],normal.scale(-1),[uvs[0],uvs[2],uvs[1],uvs[3]],[colors[0],colors[2],colors[1],colors[3]])}}}}this.chunkMeshes[index]?.dispose(),this.chunkTransparentMeshes[index]?.dispose(),this.chunkMeshes[index]=opaque.points.length?opaque:void 0,this.chunkTransparentMeshes[index]=transparent.points.length?transparent:void 0,this.chunkCenters[index]=center}tileUVs(index){let uvs=this.tiles.get(index);if(!uvs){var first=this.tileInfo;const rect=render3DGetTileUVs(first.columns?first.frame(index):first.index(index));uvs=RENDER3D_QUAD_UVS.map(q=>Object.freeze(vec2(rect.x+q.x*rect.w,rect.y+q.y*rect.h))),this.tiles.set(index,uvs)}return uvs}boxBlocked(pos,size,o){const m=this.pos3D,s=this.mapSize;var cells=(center,half,corner,count)=>{var first=floor(center-half-corner+1e-9),center=max(ceil(center+half-corner-1e-9)-1,first);return[max(first,0),min(center,count-1)]},[x0,x1]=cells(pos.x,size.x/2,m.x,s.x),[y0,y1]=cells(pos.y,size.y/2,m.y,s.y),[cells,z1]=cells(pos.z,size.z/2,m.z,s.z);for(let z=cells;z<=z1;++z)for(let y=y0;y<=y1;++y)for(let x=x0;x<=x1;++x){var type=this.data[x+s.x*(y+s.y*z)];if(type&&o.collideWithVoxel(type,vec3(x,y,z)))return!0}return!1}levelCollide3D(o,oldPos){var k=o.scale3D,size=vec3(o.size3D.x*abs(k.x),o.size3D.y*abs(k.y),o.size3D.z*abs(k.z)),p=o.pos3D,v=o.velocity3D,m=this.pos3D;if(this.boxBlocked(p,size,o))if(this.boxBlocked(oldPos,size,o)){var k=size.y/2,flat=vec3(size.x,0,size.z);let top;for(let y=floor(oldPos.y-k-m.y);y<=floor(oldPos.y-m.y);++y)this.boxBlocked(vec3(oldPos.x,m.y+y+.5,oldPos.z),flat,o)&&(top=y+1);var from,lead,first,last,slab,slabSize,k=void 0===top?void 0:vec3(oldPos.x,m.y+top+k+1e-4,oldPos.z);if(k&&!this.boxBlocked(k,size,o))return p.set(k.x,k.y,k.z),v.y=max(v.y,0),void(o.groundObject=this);const tiny=1e-9,place=oldPos.copy();for(const axis of["y","x","z"]){const move=p[axis]-place[axis],halfSize=size[axis]/2,corner=m[axis];move&&(from=(lead=at=>0<move?ceil(at+halfSize-corner-tiny)-1:floor(at-halfSize-corner+tiny))(place[axis]),lead=lead(p[axis]),first=min(from,lead)+(0<move?1:0),last=max(from,lead)-(0<move?0:1),slab=place.copy(),slabSize=size.copy(),slab[axis]=corner+(first+last+1)/2,slabSize[axis]=last-first+1,from!==lead&&this.boxBlocked(slab,slabSize,o)?("y"===axis&&move<0&&(o.groundObject=this),v[axis]=0):place[axis]=p[axis])}void p.set(place.x,place.y,place.z)}else{var restitution=max(o.restitution,this.restitution);const place=oldPos.copy();for(const axis of["y","x","z"]){var moved=place.copy();if(moved[axis]=p[axis],this.boxBlocked(moved,size,o)){const half=size[axis]/2,move=p[axis]-oldPos[axis];move&&(moved[axis]=move<0?m[axis]+floor(p[axis]-half-m[axis])+1+half+1e-4:m[axis]+ceil(p[axis]+half-m[axis])-1-half-1e-4,this.boxBlocked(moved,size,o)||(place[axis]=moved[axis])),"y"===axis&&move<0&&(o.groundObject=this),v[axis]*=-restitution}else place[axis]=p[axis]}p.set(place.x,place.y,place.z)}}raycast(ray,maxDistance=1/0,test=()=>!0){var s=this.mapSize,o=ray.origin.subtract(this.pos3D),d=ray.direction,axes=["x","y","z"];if(d.lengthSquared()){let enter=0,exit=maxDistance,entryAxis=-1;for(let k=0;k<3;++k){var a=axes[k],dk=d[a],ok=o[a];if(dk){let t0=-ok/dk,t1=(s[a]-ok)/dk;t0>t1&&([t0,t1]=[t1,t0]),t0>enter&&(enter=t0,entryAxis=k),exit=min(exit,t1)}else if(ok<0||ok>s[a])return}if(!(enter>exit)){const start=o.add(d.scale(enter)),cell=[],step=[],next=[],delta=[];for(let k=0;k<3;++k){const a=axes[k],dk=d[a];cell[k]=clamp(floor(start[a]),0,s[a]-1),step[k]=sign(dk),next[k]=dk?enter+(cell[k]+(0<dk?1:0)-start[a])/dk:1/0,delta[k]=dk?abs(1/dk):1/0}var normalOn=k=>vec3(0===k?-step[0]:0,1===k?-step[1]:0,2===k?-step[2]:0);let normal,t=enter;for(normal=0<=entryAxis?normalOn(entryAxis):normalOn(abs(d.x)>=abs(d.y)&&abs(d.x)>=abs(d.z)?0:abs(d.y)>=abs(d.z)?1:2);t<=maxDistance;){var type=this.data[cell[0]+s.x*(cell[1]+s.y*cell[2])];if(type){var at=vec3(cell[0],cell[1],cell[2]);if(test(type,at))return{distance:t,cell:at,normal:normal,type:type}}const k=next[0]<next[1]?next[0]<next[2]?0:2:next[1]<next[2]?1:2;if(t=next[k],cell[k]+=step[k],cell[k]<0||cell[k]>=s[axes[k]])return;next[k]+=delta[k],normal=normalOn(k)}}}}levelRaycast3D(ray){return this.raycast(ray)?.distance}levelSegment3D(from,to){var to=to.subtract(from),hit=this.raycast(new Ray3D(from,to),1);if(hit){if(!hit.distance){from=from.subtract(to.normalize(1e-6)).subtract(this.pos3D);if(this.voxelAt(floor(from.x),floor(from.y),floor(from.z)))return}return{distance:hit.distance,normal:hit.normal}}}update(){super.update()}render3D(){this.renderChunks(!1)}renderChunks(transparent){this.buildChunks();var meshes=transparent?this.chunkTransparentMeshes:this.chunkMeshes,p=this.pos3D,texture=this.tileInfo.textureInfo;for(let i=0;i<meshes.length;++i){var mesh=meshes[i],c=this.chunkCenters[i];mesh&&render3D.drawMesh(mesh,vec3(p.x+c.x,p.y+c.y,p.z+c.z),texture,this.color)}}destroy(immediate){render3DLevelLeave(this);for(const mesh of[...this.chunkMeshes,...this.chunkTransparentMeshes])mesh?.dispose();super.destroy(immediate)}}const level3DTypes=new Map,level3DBaseScale=new WeakMap;function level3DAddType(name,make,defaults={},tileInfo){level3DTypes.set(String(name),{make:make,defaults:defaults,tileInfo:tileInfo})}function level3DAddMesh(name,mesh,tileInfo,color=WHITE){level3DAddType(name,function(pos,properties){return level3DMakeShape(pos,properties,mesh,tileInfo)},{color:color,solid:!1},tileInfo)}function level3DLoad(level){var prefabs=level.prefabs;if(prefabs&&"object"==typeof prefabs&&!isArray(prefabs))for(const name in prefabs){var known=level3DPrefabs.get(name);(known?known.fromLevel:!level3DTypes.has(name))&&prefabs[name]&&"object"==typeof prefabs[name]&&level3DPrefabSet(name,prefabs[name],!0)}scene=level.scene,++level3DSceneCubeLoads.skyBox,++level3DSceneCubeLoads.environment,(r=render3D)&&scene&&"object"==typeof scene&&(color=level3DHexColor,(sky=isArray(scene.sky)&&3===scene.sky.length?scene.sky.map(color):[]).length&&sky.every(c=>c)&&([sky,horizon,bottom]=sky,ambient=level3DFinite(scene.ambient)?scene.ambient:.5,level3DSkies.has(r.sky)&&r.sky.dispose(),level3DSkies.add(r.sky=buildSky(sky,horizon,bottom)),r.fogColor=horizon.copy(),r.ambientColor=sky.scale(ambient,1),r.ambientGroundColor=bottom.scale(ambient,1)),(horizon=level3DVector(scene.sunDirection))&&horizon.lengthSquared()&&(r.sunDirection=horizon),r.sunColor=color(scene.sunColor)||r.sunColor,isArray(sky=scene.fog)&&2===sky.length&&sky.every(level3DFinite)&&(r.fogStart=sky[0],r.fogEnd=sky[1]),r.fogColor=color(scene.fogColor)||r.fogColor,"boolean"==typeof scene.shadows&&(r.shadows=scene.shadows),"boolean"==typeof scene.lensFlare&&(scene.lensFlare?level3DSunHasFlare()||(level3DSunFlare=new LensFlare3D):(level3DSunFlare&&!level3DSunFlare.destroyed&&level3DSunFlare.destroy(),level3DSunFlare=void 0)),level3DSceneCubeMap("skyBox",scene.skyBox),level3DSceneCubeMap("environment",scene.environment));var scene,r,color,horizon,sky,result,made=[],bottom=level3DVoxelMap=function(voxels){var shape=function(voxels){if(voxels&&"object"==typeof voxels){var size=level3DVector(voxels.size);if(!(!size||[size.x,size.y,size.z].some(n=>n<1||n%1)||size.x*size.y*size.z>LEVEL3D_VOXEL_CELLS))return{pos:level3DVector(voxels.pos,vec3()),size:size}}}(voxels);if(shape&&void 0!==VoxelMap)return shape=new VoxelMap(shape.pos,shape.size,level3DVoxelTiles||function(){if(textureInfos[0]?.size.x||headlessMode||!canvasAvailable())return tile();if(!level3DVoxelPlain){var context=createCanvasContext(256);for(let i=0;i<256;++i){var x=i%16*16,y=16*(i/16|0),hue=.618*i%1;context.fillStyle=hsl(hue,.45,.4).toString(),context.fillRect(x,y,16,16),context.fillStyle=hsl(hue,.5,.55).toString(),context.fillRect(1+x,1+y,14,14)}level3DVoxelPlain=new TextureInfo(context.canvas)}return tile(0,16,level3DVoxelPlain,0)}()),function(blocks,data){if(data.fill(0),isArray(blocks))for(let i=0,cell=0;i+1<blocks.length&&cell<data.length;i+=2){var count=blocks[i],type=blocks[i+1];if(!isNumber(count)||!isNumber(type)||count<0)return;count=min(cell+floor(count),data.length);data.fill(0<=type&&type<=255&&type%1==0?type:0,cell,count),cell=count}}(voxels.blocks,shape.data),level3DVoxelSetupMap?.(shape),shape.rebuild(),shape}(level.voxels),ambient=(bottom&&made.push(bottom),level3DTerrainMap=function(terrain){var shape=function(terrain){if(terrain&&"object"==typeof terrain){var size=terrain.size,rows=terrain.heights,n=LEVEL3D_TERRAIN_SAMPLES;if(isArray(size)&&2===size.length&&size.every(v=>level3DFinite(v)&&0<v)&&!(!isArray(rows)||rows.length<2||rows.length>n)&&isArray(rows[0])){const columns=rows[0].length;if(!(columns<2||columns>n)&&rows.every(row=>isArray(row)&&row.length===columns))return n=rows.map(row=>row.map(v=>level3DFinite(v)?clamp(v):0)),{pos:level3DVector(terrain.pos,vec3()),size:vec2(size[0],size[1]),height:level3DFinite(terrain.height)&&0<terrain.height?terrain.height:1,heights:n}}}}(terrain);if(shape&&void 0!==HeightMap)return level3DTerrainSetColors(shape=new HeightMap(shape.heights,shape.size,shape.height,void 0,shape.pos),terrain),shape.colors&&shape.rebuild(),shape}(level.terrain));ambient&&made.push(ambient);for(const object of isArray(level.objects)?level.objects:[])object&&"object"==typeof object&&(result=function(object){return level3DMakeAt(object,level3DVector(object.pos,vec3()),level3DVector(object.rotation,vec3()).scale(PI/180),level3DVector(object.scale,vec3(1)))}(object),object,result)&&made.push(result);return made}let level3DVoxelTiles,level3DVoxelSetupMap,level3DVoxelMap;function level3DVoxelSetup(tileInfo,setup){level3DVoxelTiles=tileInfo,level3DVoxelSetupMap=setup}const LEVEL3D_VOXEL_CELLS=2**24;let level3DTerrainMap;const LEVEL3D_TERRAIN_SAMPLES=129;function level3DHexColor(hex){return"string"==typeof hex&&/^#([0-9a-f]{6}|[0-9a-f]{8})$/i.test(hex)?(new Color).setHex(hex):void 0}const level3DFinite=v=>"number"==typeof v&&isFinite(v);function level3DTerrainSetColors(map,terrain,paint=function(terrain,samples){var colors=(terrain=terrain?.paint)?.colors,runs=terrain?.cells;if(isArray(colors)&&isArray(runs)&&!(255<colors.length)&&colors.every(level3DHexColor)){var cells=new Uint8Array(samples);let at=0;for(let i=0;i<runs.length;i+=2){var count=runs[i],color=runs[i+1];if(!(0<count&&count%1==0&&0<=color&&color%1==0)||color>colors.length||at+count>samples)return;cells.fill(color,at,at+=count)}return at===samples?{colors:colors.slice(),cells:cells}:void 0}}(terrain,map.rows*map.columns)){const base=level3DHexColor(terrain.color)||WHITE;if(map.color=base,map.colors=void 0,paint&&paint.cells.some(v=>v)){const palette=[base,...paint.colors.map(level3DHexColor)];map.color=WHITE,map.colors=map.heights.map((row,r)=>row.map((v,c)=>palette[paint.cells[r*map.columns+c]]||base))}}let level3DVoxelPlain;const level3DSkies=new WeakSet;const level3DSceneCubeMaps=new Map,level3DSceneCubeLoaded=new WeakMap,level3DSceneCubeLoads={skyBox:0,environment:0};function level3DSceneCubeMap(field,urls){if(isArray(urls)&&6===urls.length&&urls.every(url=>"string"==typeof url)){const key=urls.join("\n"),load=++level3DSceneCubeLoads[field],before=render3D[field];let cube=level3DSceneCubeMaps.get(key);level3DSceneCubeMaps.delete(key),level3DSceneCubeMaps.set(key,cube||=loadCubeMap(urls)),cube.then(map=>level3DSceneCubeLoaded.set(cube,map),()=>{});for(var[oldKey,old]of level3DSceneCubeMaps){if(level3DSceneCubeMaps.size<=4)break;var map=level3DSceneCubeLoaded.get(old);map&&(map===render3D.skyBox||map===render3D.environment)||(level3DSceneCubeMaps.delete(oldKey),old.then(map=>map!==render3D.skyBox&&map!==render3D.environment&&map.dispose(),()=>{}))}cube.then(map=>load===level3DSceneCubeLoads[field]&&render3D[field]===before&&(render3D[field]=map),error=>{level3DSceneCubeMaps.get(key)===cube&&level3DSceneCubeMaps.delete(key),console.warn(error.message)})}}let level3DSunFlare;const level3DSunHasFlare=()=>engineObjects.some(o=>o instanceof LensFlare3D&&!o.destroyed&&!o.light);function level3DVector(value,fallback){return isArray(value)&&3===value.length&&value.every(level3DFinite)?vec3(value[0],value[1],value[2]):fallback}function level3DMakeAt(object,pos,rotation,scale){var type=level3DTypes.get(object.type);if(type){var properties=function(type,object){var key,value,properties={},object=object.properties;for([key,value]of Object.entries(type.defaults))properties[key]=value?.copy?value.copy():value;for(const[key,value]of Object.entries(object&&"object"==typeof object?object:{})){var d=type.defaults[key];isColor(d)?level3DHexColor(value)&&(properties[key]=level3DHexColor(value)):isVector3(d)?properties[key]=level3DVector(value,properties[key]):isVector2(d)?isArray(value)&&2===value.length&&value.every(level3DFinite)&&(properties[key]=vec2(value[0],value[1])):"number"==typeof d?level3DFinite(value)&&(properties[key]=value):void 0!==d&&typeof value!=typeof d||(properties[key]=value)}return properties}(type,object),make=type["make"],count=engineObjects.length;let result;try{result=make.prototype?new make(pos,properties):make(pos,properties)}catch(error){if(!debug)throw error;console.error(`level3DLoad: ${object.type} ${object.id} could not be made, skipped:`,error);for(const o of engineObjects.slice(count))o.destroy();return}if(result&&"object"==typeof result){result instanceof EngineObject3D&&(level3DBaseScale.set(result,result.scale3D.copy()),result.rotation3D=rotation.copy(),result.scale3D=result.scale3D.multiply(scale));for(const key in type.defaults)result[key]=properties[key];return result instanceof Prefab3D&&result.placeAt(pos),result}}else debug&&console.warn(`level3DLoad: no type added for ${object.type}, skipped`)}function level3DSpawn(type,pos3D=vec3(),rotation3D=vec3(),scale3D=vec3(1),properties){return level3DMakeAt(properties?{type:type,properties:properties}:{type:type},pos3D.copy(),rotation3D,scale3D)}const level3DPrefabs=new Map;let level3DPrefabDepth=0,level3DPrefabAttached=0;const level3DPrefabMaking=[];function level3DAddPrefab(name,prefab){level3DPrefabSet(name,prefab,!1)}function level3DPrefabSet(name,prefab,fromLevel){name=String(name);var objects=isArray(prefab?.objects)?prefab.objects.filter(o=>o&&"object"==typeof o):[];level3DPrefabs.set(name,{attached:!!prefab?.attached,objects:JSON.parse(JSON.stringify(objects)),fromLevel:fromLevel,source:prefab}),level3DTypes.set(name,{make:class extends Prefab3D{constructor(pos){super(pos,name)}},defaults:{},tileInfo:void 0})}async function level3DLoadPrefab(name,url){level3DAddPrefab(name,await fetchJSON(url))}function level3DPrefabPartTransform(instance,object){var pos=level3DVector(object.pos,vec3()),scale=level3DVector(object.scale,vec3(1)),object=level3DVector(object.rotation,vec3()).scale(PI/180),object=buildMatrix(vec3(),instance.rotation3D).multiply(buildMatrix(vec3(),object));return{pos:buildMatrix(instance.pos3D,instance.rotation3D,instance.scale3D).transformPoint(pos),rotation:object.getRotation(),scale:scale.multiply(instance.scale3D)}}class Prefab3D extends EngineObject3D{constructor(pos3D,prefabName=""){super(pos3D),this.prefabName=prefabName,this.attached=!!level3DPrefabs.get(prefabName)?.attached||0<level3DPrefabAttached,this.parts=[],this.originOffset=vec3(),this.partObjects=[],this.partsPlaced="",this.partsMade=!1}placeAt(pos3D){this.pos3D=pos3D.copy(),this.placeParts();var o=this.originOffset;(o.x||o.y||o.z)&&(this.pos3D=buildMatrix(pos3D,this.rotation3D,this.scale3D).transformPoint(o))}placeParts(){this.destroyed||(this.partsMade||=(this.makeParts(),!0),this.partsPlaced=this.placeKey(),this.attached)||this.parts.forEach((part,i)=>{part instanceof EngineObject3D&&(i=level3DPrefabPartTransform(this,this.partObjects[i]),part.rotation3D=i.rotation,part.scale3D=(level3DBaseScale.get(part)??vec3(1)).multiply(i.scale),part instanceof Prefab3D?part.placeAt(i.pos):part.pos3D=i.pos)})}placeKey(){var p=this.pos3D,r=this.rotation3D,s=this.scale3D;return[p.x,p.y,p.z,r.x,r.y,r.z,s.x,s.y,s.z].join()}makeParts(){var prefab=level3DPrefabs.get(this.prefabName);if(prefab)if(level3DPrefabMaking.includes(this.prefabName)||8<=level3DPrefabDepth)debug&&console.error(`level3DLoad: the prefab ${this.prefabName} holds itself, left out there`);else{level3DPrefabMaking.push(this.prefabName),++level3DPrefabDepth,this.attached&&++level3DPrefabAttached;try{var low=vec3(1/0),high=vec3(-1/0),children=[];for(const object of prefab.objects){var local={pos:level3DVector(object.pos,vec3()),scale:level3DVector(object.scale,vec3(1)),rotation:level3DVector(object.rotation,vec3()).scale(PI/180)},at=this.attached?local:level3DPrefabPartTransform(this,object),part=level3DMakeAt(object,at.pos,at.rotation,at.scale);if(part&&(this.parts.push(part),this.partObjects.push(object),this.attached&&part instanceof EngineObject3D)){part.setCollision(!1,!1,!1),children.push(part);var box=buildMatrix(part.pos3D,part.rotation3D,part.size3D.multiply(part.scale3D));for(let i=8;i--;){var p=box.transformPoint(vec3(1&i?.5:-.5,2&i?.5:-.5,4&i?.5:-.5));for(const k of["x","y","z"])low[k]=min(low[k],p[k]),high[k]=max(high[k],p[k])}}}children.length&&(this.size3D=high.subtract(low),this.originOffset=low.add(high).scale(.5));for(const part of children)part.pos3D=part.pos3D.subtract(this.originOffset),(part.drawOwner=this).addChild(part)}finally{--level3DPrefabDepth,level3DPrefabMaking.pop(),this.attached&&--level3DPrefabAttached}}}update(){super.update(),this.attached||this.partsPlaced===this.placeKey()||this.placeParts()}destroy(immediate){if(!this.destroyed){for(const part of this.parts)part.parent!==this&&part.destroy?.(immediate);super.destroy(immediate)}}render3D(){}}function level3DMakeShape(pos,properties,mesh,tileInfo,asSphere=!1){pos=new EngineObject3D(pos,mesh,0<=properties.tile?tile(properties.tile):tileInfo,properties.color);return pos.collideAsSphere3D=asSphere,properties.solid&&pos.setCollision(),pos}let level3DCylinderMesh;level3DAddType("Box",function(pos,properties){return level3DMakeShape(pos,properties,render3D.boxMesh)},{color:WHITE,tile:-1,solid:!0}),level3DAddType("Sphere",function(pos,properties){return level3DMakeShape(pos,properties,render3D.sphereMesh,void 0,!0)},{color:WHITE,tile:-1,solid:!0}),level3DAddType("Cylinder",function(pos,properties){return level3DMakeShape(pos,properties,level3DCylinderMesh||=buildCylinder())},{color:WHITE,tile:-1,solid:!0}),level3DAddType("Light",function(pos,properties){pos=new Light3D(pos,properties.radius,properties.color,properties.intensity);return pos.coneAngle=max(properties.cone,0)*PI/180,pos.coneSoftness=properties.softness,properties.shadows&&render3D&&(render3D.shadowLight=pos),properties.lensFlare&&pos.addFlare(),pos},{color:WHITE,radius:5,intensity:1,cone:0,softness:.2,shadows:!1,lensFlare:!1});textIntl=["Emitter","Particles","Color","Motion","Collision","Texture","Behaviors"];const particleEffectSettings=[];let particleEffectSettingGroup;function particleEffectAddSetting(name,kind,value,min,max,step,description,hardMin=min,hardMax=max){var extra=["trailScale","velocityInheritance","localSpace","restitution","friction","gravity"].includes(name);particleEffectSettings.push({group:particleEffectSettingGroup,name:name,kind:kind,value:value,min:min,max:max,step:step,description:description,hardMin:hardMin,hardMax:hardMax,extra:extra})}particleEffectSettingGroup="Emitter",particleEffectAddSetting("emitRate","number",100,0,500,.1,"Particles per second, 0 emits none",0,1e4),particleEffectAddSetting("emitTime","number",0,0,5,.05,"Seconds to emit for, 0 is forever",0,1e9),particleEffectAddSetting("emitSize","number",0,0,10,.05,"Diameter, or width when rectangular",0,1e9),particleEffectAddSetting("emitRect","checkbox",!1,0,0,0,"Rectangle emitter, off is a circle"),particleEffectAddSetting("emitHeight","number",0,0,10,.05,"Rectangle height",0,1e9),particleEffectAddSetting("emitConeAngle","number",PI,0,PI,.01,"Half angle particles move in, 3.14 is all ways"),particleEffectAddSetting("angle","number",0,-3.14,3.14,.01,"Emitter angle, 0 points up",-PI,PI),particleEffectSettingGroup="Particles",particleEffectAddSetting("particleTime","number",.5,0,5,.05,"Seconds each particle lives",0,1e9),particleEffectAddSetting("sizeStart","number",.1,0,5,.01,"Size at the start of life",0,1e9),particleEffectAddSetting("sizeEnd","number",1,0,5,.01,"Size at the end of life",0,1e9),particleEffectAddSetting("fadeRate","number",.1,0,1,.01,"Share of life spent fading in and out"),particleEffectAddSetting("randomness","number",.2,0,1,.01,"How much each particle varies"),particleEffectAddSetting("particleConeAngle","number",PI,0,PI,.01,"Half angle of start rotation, 3.14 is any"),particleEffectAddSetting("trailScale","number",0,0,20,.1,"Stretch along the motion, 0 is off",0,1e9),particleEffectAddSetting("additive","checkbox",!1,0,0,0,"Glow with additive blending"),particleEffectSettingGroup="Color",particleEffectAddSetting("colorStartA","color",[1,1,1,1],0,1,.01,"Start color, each particle picks between A and B"),particleEffectAddSetting("colorStartB","color",[1,1,1,1],0,1,.01,"Second start color"),particleEffectAddSetting("colorEndA","color",[1,1,1,0],0,1,.01,"End color, each particle picks between A and B"),particleEffectAddSetting("colorEndB","color",[1,1,1,0],0,1,.01,"Second end color"),particleEffectAddSetting("randomColorLinear","checkbox",!0,0,0,0,"Pick along A to B, off picks each channel"),particleEffectSettingGroup="Motion",particleEffectAddSetting("speed","number",.1,0,.5,.005,"Start speed, world units per frame",-1e9,1e9),particleEffectAddSetting("angleSpeed","number",.05,0,.5,.005,"Spin speed, radians per frame",-1e9,1e9),particleEffectAddSetting("damping","number",1,.8,1,.001,"Speed kept each frame, 1 keeps it all",0,1),particleEffectAddSetting("angleDamping","number",1,.8,1,.001,"Spin kept each frame",0,1),particleEffectAddSetting("gravityScale","number",0,-2,2,.05,"How much gravity pulls",-1e9,1e9),particleEffectAddSetting("gravity","number",0,-.02,.02,5e-4,"Its own fall each frame, the same in any game",-1,1),particleEffectAddSetting("velocityInheritance","number",0,0,1,.01,"Share of emitter motion passed on"),particleEffectAddSetting("localSpace","checkbox",!1,0,0,0,"Particles move with the emitter"),particleEffectAddSetting("collideLevel","checkbox",!(particleEffectSettingGroup="Collision"),0,0,0,"Hit tiles, the preview adds a floor"),particleEffectAddSetting("restitution","number",0,0,1,.01,"Bounce when hitting tiles"),particleEffectAddSetting("friction","number",.8,0,1,.01,"Speed kept sliding along tiles, the larger of this and the friction of the layer, as for an object"),particleEffectSettingGroup="Texture",particleEffectAddSetting("shape","shape","soft",0,0,0,"Built-in shape, none uses the tile"),particleEffectAddSetting("tileIndex","number",-1,-1,63,1,"Tile in texture 0 when no shape, -1 is untextured",-1,1e4),particleEffectAddSetting("tileSize","number",16,1,128,1,"Tile size in texture pixels",1,4096),particleEffectAddSetting("tilePadding","number",1,0,8,1,"Pixels of padding around each tile",0,64);const particleEffectBehaviors=[{name:"wobble",min:0,max:5,value:1,description:"Sway from side to side"},{name:"swirl",min:-2,max:2,value:1,description:"Turn the direction of travel"},{name:"turbulence",min:0,max:5,value:1,description:"Random pushes every frame"},{name:"attract",min:-2,max:2,value:1,description:"Pull toward the emitter, negative pushes away"},{name:"orbit",min:-2,max:2,value:1,description:"Push around the emitter, add attract to circle it"},{name:"wind",min:-2,max:2,value:1,description:"A sideways push that grows with age"},{name:"stick",min:0,max:1,value:1,description:"Grip on landing, pair with collideLevel"}].map(b=>({...b,update:void 0,update3D:void 0})),particleEffectBehavior=name=>particleEffectBehaviors.find(b=>b.name===name),particleEffectShapes=["dot","soft","glow","smoke","spark","square","triangle","ring","star","plus"];function particleEffectColor(value){if(isArray(value=value instanceof Color?[value.r,value.g,value.b,value.a]:value)&&4===value.length&&value.every(isNumber))return value.map(c=>clamp(c))}function particleEffectSanitize(raw){var text="string"==typeof raw?.name?raw.name.replace(/[\x00-\x1f\x7f]+/g," ").trim():"",text=text?textGraphemes(text).slice(0,60).join("").trim():"Effect",input=raw?.settings&&"object"==typeof raw.settings?{...raw.settings}:{},settings=(void 0===input.shape&&isNumber(input.tileIndex)&&(input.shape=""),{});for(const setting of particleEffectSettings){var value=input[setting.name],fallback=setting.value;"checkbox"===setting.kind?settings[setting.name]="boolean"==typeof value?value:0===value||1===value?!!value:fallback:"color"===setting.kind?settings[setting.name]=particleEffectColor(value)||fallback.slice():"shape"===setting.kind?settings[setting.name]=""===value||particleEffectShapes.includes(value)?value:fallback:isNumber(value)?(value=clamp("angle"===setting.name&&isFinite(value)&&abs(value)>PI?mod(value+PI,2*PI)-PI:value,setting.hardMin,setting.hardMax),settings[setting.name]=1<=setting.step?round(value):value):settings[setting.name]=fallback}settings.localSpace&&(settings.collideLevel=!1);var behaviors=[],list=isArray(raw?.behaviors)?raw.behaviors:[];for(const b of particleEffectBehaviors){var found=list.find(x=>x?.name===b.name);found&&(found=isNumber(found.strength)?clamp(found.strength,b.min,b.max):b.value,behaviors.push({name:b.name,strength:found}))}for(const x of list)"string"!=typeof x?.name||!x.name||particleEffectBehavior(x.name)||behaviors.some(b=>b.name===x.name)||behaviors.push({name:x.name,strength:isNumber(x.strength)?x.strength:1});return{name:text,settings:settings,behaviors:behaviors}}function particleEffectRecolor(effect,hue=0,saturation=1){var settings={...(effect=particleEffectSanitize(effect)).settings};for(const name of["colorStartA","colorStartB","colorEndA","colorEndB"]){var[h,s,l,a]=new Color(...settings[name]).HSLA(),h=(new Color).setHSLA(mod(h+hue),clamp(s*saturation),l,a);settings[name]=[h.r,h.g,h.b,h.a]}return{...effect,settings:settings,behaviors:effect.behaviors.map(b=>({...b}))}}const particleEffectsByName=new Map;function particleEffectsAdd(effects){for(const effect of isArray(effects)?effects:[effects]){var clean=particleEffectSanitize(effect);particleEffectsByName.set(clean.name.toLowerCase(),clean)}}function particleEffectsGet(name){return particleEffectsByName.get(String(name).toLowerCase())}function particleEffectsParse(text){return particleEffectsFromData(JSON.parse(text))}function particleEffectsFromData(data){data=isArray(data?.effects)?data.effects:data?.settings?[data]:void 0;if(data&&data.length)return data.map(particleEffectSanitize);throw new Error("no effects in this file")}function particleEffectsText(effects){return JSON.stringify({version:1,effects:effects},void 0,1)}async function particleEffectsLoad(url){url=particleEffectsFromData(await fetchJSON(url));return particleEffectsAdd(url),url}let particleEffectShapeTiles;function particleEffectShapeTile(name){if(!particleEffectShapeTiles){if(headlessMode||!glContext||!canvasAvailable())return;const r=15,count=particleEffectShapes.length,context=createCanvasContext(32*count*4,128),soft=(context.scale(4,4),context.fillStyle=context.strokeStyle="#fff",(x,y,radius,alpha=t=>1-t)=>{var g=context.createRadialGradient(x,y,0,x,y,radius);for(let i=0;i<=8;++i)g.addColorStop(i/8,"rgba(255,255,255,"+alpha(i/8).toFixed(3)+")");context.fillStyle=g,context.beginPath(),context.arc(x,y,radius,0,2*PI),context.fill(),context.fillStyle="#fff"}),draw={dot:(x,y)=>{context.beginPath(),context.arc(x,y,r,0,2*PI),context.fill()},soft:(x,y)=>soft(x,y,r),glow:(x,y)=>soft(x,y,r,t=>engineGlowAlpha(t,1)),smoke:(x,y)=>{for(var[dx,dy,dr]of[[-4,-3,9],[4,-2,8],[0,4,9],[-5,5,6],[6,5,6]])soft(x+dx,y+dy,dr,t=>.7*(1-t))},spark:(x,y)=>{context.save(),context.translate(x,y),context.scale(.3,1),soft(0,0,r,t=>engineGlowAlpha(t,.6)),context.restore()},square:(x,y)=>context.fillRect(x-r,y-r,30,30),triangle:(x,y)=>{context.beginPath(),context.moveTo(x,y-r),context.lineTo(x+r,y+r),context.lineTo(x-r,y+r),context.fill()},ring:(x,y)=>{context.lineWidth=3,context.beginPath(),context.arc(x,y,13,0,2*PI),context.stroke()},star:(x,y)=>{context.beginPath();for(let i=0;i<10;++i){var a=i*PI/5-PI/2,d=i%2?6.75:r;context.lineTo(x+cos(a)*d,y+sin(a)*d)}context.fill()},plus:(x,y)=>{context.fillRect(x-r,y-4,30,8),context.fillRect(x-4,y-r,8,30)}},texture=(particleEffectShapes.forEach((name,i)=>draw[name](32*i+16,16)),new TextureInfo(context.canvas,!0,!1,!1));particleEffectShapeTiles=new Map(particleEffectShapes.map((name,i)=>[name,new TileInfo(vec2(4*(32*i+1),4),vec2(120),texture)]))}return particleEffectShapeTiles.get(name)}var b=particleEffectBehavior;function particleEffectsAddBehavior(name,update,update3D,min=-2,max=2,value=1,description=""){var old=particleEffectBehavior(name);old&&particleEffectBehaviors.splice(particleEffectBehaviors.indexOf(old),1),particleEffectBehaviors.push({name:name,update:update,update3D:update3D,min:min,max:max,value:value,description:description})}function particleEffectUpdateCallback(behaviors,is3D){const calls=behaviors.map(b=>[particleEffectBehavior(b.name)?.[is3D?"update3D":"update"],b.strength]).filter(c=>c[0]);if(calls.length)return p=>{for(var[update,strength]of calls)update(p,strength)}}b("wobble").update=(p,s)=>{p.velocity.x+=.003*s*p.scale*sin(6*time+97*p.lifeTime%9)},b("swirl").update=(p,s)=>{var s=.05*s,c=cos(s),s=sin(s),p=p.velocity;p.set(p.x*c-p.y*s,p.x*s+p.y*c)},b("turbulence").update=(p,s)=>{p.velocity.x+=rand(-1,1)*s*.005*p.scale,p.velocity.y+=rand(-1,1)*s*.005*p.scale},b("attract").update=(p,s)=>{var e=p.emitter,x=e.localSpace?0:e.pos.x,e=e.localSpace?0:e.pos.y;p.velocity.x+=(x-p.pos.x)*s*.002,p.velocity.y+=(e-p.pos.y)*s*.002},b("orbit").update=(p,s)=>{var e=p.emitter,x=e.localSpace?0:e.pos.x,e=e.localSpace?0:e.pos.y;p.velocity.x-=(p.pos.y-e)*s*.002,p.velocity.y+=(p.pos.x-x)*s*.002},b("wind").update=(p,s)=>p.velocity.x+=.004*s*p.scale*min((time-p.spawnTime)/p.lifeTime,1),b("stick").update=(p,s)=>{p.groundObject&&(p.velocity.x*=1-s,p.angleVelocity*=1-s)};const particleEffectIndirect=["emitSize","emitRect","emitHeight","tileIndex","tileSize","tilePadding","shape"];function particleEffectTileInfo(s){var texture,cell;return s.shape?particleEffectShapeTile(s.shape):!(s.tileIndex<0)&&(texture=textureInfos[0],cell=s.tileSize+2*s.tilePadding,texture)&&texture.size.x&&s.tileIndex<floor(texture.size.x/cell)*floor(texture.size.y/cell)?tile(s.tileIndex,s.tileSize,0,s.tilePadding):void 0}function particleEffectApply(emitter,effect){particleEffectSet(emitter,particleEffectSanitize(effect))}function particleEffectSet(emitter,effect){var s=effect.settings,ended=emitter.emitTime<0;for(const setting of particleEffectSettings)particleEffectIndirect.includes(setting.name)||(emitter[setting.name]="color"===setting.kind?new Color(...s[setting.name]):s[setting.name]);ended&&(emitter.emitTime=-1),emitter.emitCircle=!s.emitRect,emitter.emitSize=vec2(s.emitSize,s.emitRect?s.emitHeight:s.emitSize),emitter.tileInfo=particleEffectTileInfo(s),emitter.renderOrder=s.additive?1e9:0,emitter.particleUpdateCallback=particleEffectUpdateCallback(effect.behaviors)}function particleEffectResolve(nameOrEffect,options){nameOrEffect="string"==typeof nameOrEffect?particleEffectsGet(nameOrEffect):nameOrEffect;if(nameOrEffect){if(isVector2(options.emitSize)&&(options={...options,emitSize:options.emitSize.x,emitHeight:options.emitSize.y,emitRect:options.emitRect??!0}),debug);var settings={...nameOrEffect.settings};for(const setting of particleEffectSettings){var value=options[setting.name];void 0!==value&&function(setting,value){return"checkbox"===setting.kind?"boolean"==typeof value||0===value||1===value:"color"===setting.kind?!!particleEffectColor(value):"shape"===setting.kind?""===value||particleEffectShapes.includes(value):isNumber(value)}(setting,value)&&(settings[setting.name]=value)}var nameOrEffect=particleEffectSanitize({...nameOrEffect,settings:settings}),{hue=0,saturation=1}=options;return hue||1!=saturation?particleEffectRecolor(nameOrEffect,hue,saturation):nameOrEffect}}function particleEffect(nameOrEffect,pos=vec2(),options={}){var nameOrEffect=particleEffectResolve(nameOrEffect,options||={});if(nameOrEffect)return particleEffectSet(pos=new ParticleEmitter(pos.copy()),nameOrEffect),pos.scale=options.scale??1,void 0!==options.angle&&(pos.angle=options.angle),(nameOrEffect=options.tileInfo)&&(pos.tileInfo=nameOrEffect instanceof TextureInfo?new TileInfo(vec2(),nameOrEffect.size,nameOrEffect):nameOrEffect),pos}function particleEffectFromEmitter(emitter,name="Effect"){var settings={};for(const setting of particleEffectSettings)particleEffectIndirect.includes(setting.name)||void 0===emitter[setting.name]||(settings[setting.name]=emitter[setting.name]);return emitter.emitTime<0&&(settings.emitTime=emitter.emitElapsed),settings.emitRect=!emitter.emitCircle,settings.emitSize=emitter.emitSize.x,settings.emitHeight=emitter.emitSize.y,settings.shape="",settings.tileIndex=-1,particleEffectSanitize({name:name,settings:settings})}{const b=particleEffectBehavior,center=p=>p.emitter.worldPos3D||p.emitter.pos3D;b("wobble").update3D=(p,s)=>p.velocity.x+=.003*s*p.scale*sin(6*time+97*p.lifeTime%9),b("swirl").update3D=(p,s)=>{var s=.05*s,c=cos(s),s=sin(s),p=p.velocity;p.set(p.x*c-p.y*s,p.x*s+p.y*c,p.z)},b("turbulence").update3D=(p,s)=>{s=.005*s*p.scale;p.velocity.x+=rand(-1,1)*s,p.velocity.y+=rand(-1,1)*s,p.velocity.z+=rand(-1,1)*s},b("attract").update3D=(p,s)=>{var c=center(p);p.velocity.x+=(c.x-p.pos.x)*s*.002,p.velocity.y+=(c.y-p.pos.y)*s*.002,p.velocity.z+=(c.z-p.pos.z)*s*.002},b("orbit").update3D=(p,s)=>{var c=center(p);p.velocity.x-=(p.pos.y-c.y)*s*.002,p.velocity.y+=(p.pos.x-c.x)*s*.002},b("wind").update3D=(p,s)=>p.velocity.x+=.004*s*p.scale*min(p.age/p.lifeTime,1)}function particleEffectApply3D(emitter,effect){particleEffectSet3D(emitter,particleEffectSanitize(effect))}function particleEffectSet3D(emitter,effect){var s=effect.settings,e=emitter;e.emitSize=s.emitRect?vec3(s.emitSize,s.emitHeight,s.emitSize):s.emitSize,e.emitTime=e.emitTime<0?-1:s.emitTime,e.emitRate=s.emitRate,e.emitConeAngle=s.emitConeAngle,e.tileInfo=particleEffectTileInfo(s);for(const name of["colorStartA","colorStartB","colorEndA","colorEndB"])e[name]=new Color(...s[name]);for(const name of["particleTime","sizeStart","sizeEnd","speed","damping","gravity","fadeRate","randomness","additive","gravityScale","angleSpeed","angleDamping","collideLevel","restitution"])e[name]=s[name];e.trailTime=s.trailScale/60,e.friction=s.friction,e.stick=effect.behaviors.find(b=>"stick"==b.name)?.strength??0,e.rotation3D=vec3(0,0,-s.angle),e.particleUpdateCallback=particleEffectUpdateCallback(effect.behaviors,!0)}function particleEffect3D(nameOrEffect,pos3D=vec3(),options={}){nameOrEffect=particleEffectResolve(nameOrEffect,options||={});if(nameOrEffect)return particleEffectSet3D(pos3D=new ParticleEmitter3D(pos3D.copy()),nameOrEffect),void 0!==options.angle&&(pos3D.rotation3D=vec3(0,0,-options.angle)),pos3D.scale3D=vec3(options.scale??1),pos3D.emitFlat=!!options.flatten,options.tileInfo&&(pos3D.tileInfo=options.tileInfo),pos3D}const particleEffectsBuiltIn=[];b=(name,settings,behaviors=[])=>{particleEffectsBuiltIn.push(name),particleEffectsAdd({name:name,settings:settings,behaviors:behaviors})};b("fire",{emitRate:120,emitSize:.6,emitConeAngle:.3,shape:"glow",particleTime:.8,sizeStart:.7,sizeEnd:.15,speed:.02,angleSpeed:.02,damping:.95,gravity:.003,fadeRate:.3,randomness:.3,additive:!0,colorStartA:hsl(.08,1,.6),colorStartB:hsl(.14,1,.65),colorEndA:hsl(.02,1,.5,0),colorEndB:hsl(0,1,.3,0)}),b("torch",{emitRate:60,emitSize:.25,emitConeAngle:.2,shape:"glow",particleTime:.6,sizeStart:.45,sizeEnd:.1,speed:.015,angleSpeed:.02,damping:.95,gravity:.003,fadeRate:.3,randomness:.3,additive:!0,colorStartA:hsl(.09,1,.6),colorStartB:hsl(.13,1,.7),colorEndA:hsl(.03,1,.5,0),colorEndB:hsl(0,1,.35,0)}),b("smoke",{emitRate:25,emitSize:.5,emitConeAngle:.3,shape:"smoke",particleTime:3,sizeStart:.5,sizeEnd:2,speed:.01,angleSpeed:.01,damping:.99,gravity:4e-4,fadeRate:.5,randomness:.3,colorStartA:hsl(0,0,.5,.5),colorStartB:hsl(0,0,.3,.5),colorEndA:hsl(0,0,.2,0),colorEndB:hsl(0,0,.4,0)},[{name:"wobble",strength:.5}]),b("steam",{emitRate:30,emitSize:.4,emitConeAngle:.2,shape:"smoke",particleTime:1.5,sizeStart:.3,sizeEnd:1.5,speed:.02,damping:.98,gravity:8e-4,fadeRate:.6,randomness:.3,colorStartA:hsl(0,0,1,.4),colorStartB:hsl(.6,.2,.9,.3),colorEndA:hsl(0,0,1,0),colorEndB:hsl(.6,.2,.9,0)},[{name:"wobble",strength:.3}]),b("explosion",{emitRate:600,emitTime:.1,emitSize:.5,shape:"glow",particleTime:.6,sizeStart:1,sizeEnd:.1,speed:.1,damping:.9,fadeRate:.2,randomness:.5,additive:!0,colorStartA:hsl(.12,1,.65),colorStartB:hsl(.06,1,.55),colorEndA:hsl(0,1,.5,0),colorEndB:hsl(0,0,.3,0)}),b("sparks",{emitRate:100,emitSize:.2,shape:"soft",particleTime:.6,sizeStart:.08,sizeEnd:.04,speed:.07,damping:.96,gravity:-.006,fadeRate:.1,randomness:.4,additive:!0,trailScale:3,colorStartA:hsl(.17,1,.9),colorStartB:hsl(.12,1,.65),colorEndA:hsl(.07,1,.5,0),colorEndB:hsl(.03,1,.5,0)}),b("hit",{emitRate:400,emitTime:.05,emitSize:.2,shape:"spark",particleTime:.35,sizeStart:.5,sizeEnd:.1,speed:.08,damping:.85,fadeRate:.1,randomness:.4,additive:!0,trailScale:2,colorStartA:hsl(0,0,1),colorStartB:hsl(0,1,.7),colorEndA:hsl(0,1,.5,0),colorEndB:hsl(0,1,.4,0)}),b("dust",{emitRate:300,emitTime:.05,emitSize:1,emitRect:!0,emitHeight:.1,emitConeAngle:1.2,shape:"smoke",particleTime:.8,sizeStart:.3,sizeEnd:.8,speed:.04,damping:.9,gravity:5e-4,fadeRate:.5,randomness:.4,colorStartA:hsl(.1,.3,.6,.5),colorStartB:hsl(.08,.2,.5,.5),colorEndA:hsl(.1,.3,.6,0),colorEndB:hsl(.08,.2,.5,0)}),b("debris",{emitRate:200,emitTime:.05,emitSize:.5,emitConeAngle:1,shape:"square",particleTime:1.5,sizeStart:.15,sizeEnd:.1,speed:.07,angleSpeed:.2,damping:.99,gravity:-.008,fadeRate:.1,randomness:.5,colorStartA:hsl(.08,.4,.35),colorStartB:hsl(0,0,.4),colorEndA:hsl(.08,.4,.3),colorEndB:hsl(0,0,.35)}),b("sparkle",{emitRate:30,emitSize:1,shape:"star",particleTime:.8,sizeStart:.3,sizeEnd:0,speed:.005,angleSpeed:.05,fadeRate:.5,randomness:.3,additive:!0,colorStartA:hsl(.14,1,.7),colorStartB:hsl(0,0,1),colorEndA:hsl(.14,1,.7,0),colorEndB:hsl(0,0,1,0)}),b("magic",{emitRate:60,emitSize:1,shape:"soft",particleTime:1.5,sizeStart:.25,sizeEnd:0,speed:.015,fadeRate:.3,randomness:.3,additive:!0,colorStartA:hsl(.55,1,.65),colorStartB:hsl(.79,1,.65),colorEndA:hsl(0,0,1,0),colorEndB:hsl(0,0,1,0)},[{name:"swirl",strength:.5}]),b("heal",{emitRate:30,emitSize:.8,emitConeAngle:.2,shape:"plus",particleTime:1.2,sizeStart:.25,sizeEnd:.1,speed:.015,angleSpeed:0,particleConeAngle:0,gravity:5e-4,fadeRate:.4,randomness:.3,additive:!0,colorStartA:hsl(.33,1,.6),colorStartB:hsl(.3,1,.75),colorEndA:hsl(.33,1,.6,0),colorEndB:hsl(.3,1,.75,0)}),b("poison",{emitRate:20,emitSize:.8,shape:"smoke",particleTime:2,sizeStart:.3,sizeEnd:1,speed:.006,gravity:2e-4,fadeRate:.6,randomness:.4,colorStartA:hsl(.3,.8,.45,.6),colorStartB:hsl(.8,.6,.45,.6),colorEndA:hsl(.3,.8,.4,0),colorEndB:hsl(.8,.6,.4,0)},[{name:"wobble",strength:.5}]),b("portal",{emitRate:80,emitSize:1,shape:"soft",particleTime:1,sizeStart:.15,sizeEnd:.05,speed:.02,fadeRate:.3,randomness:.3,additive:!0,colorStartA:hsl(.75,1,.6),colorStartB:hsl(.6,1,.6),colorEndA:hsl(.75,1,.6,0),colorEndB:hsl(.6,1,.6,0)},[{name:"orbit",strength:1},{name:"attract",strength:.5}]),b("rain",{emitRate:30,emitSize:1,emitRect:!0,emitHeight:.6,angle:PI,emitConeAngle:0,shape:"soft",particleTime:.8,sizeStart:.02,sizeEnd:.02,speed:.03,gravity:-.001,fadeRate:.1,randomness:.2,trailScale:5,colorStartA:hsl(.63,1,.8,.6),colorStartB:hsl(.63,1,.8,.4),colorEndA:hsl(.63,1,.8,.6),colorEndB:hsl(.63,1,.8,.4)}),b("snow",{emitRate:6,emitSize:1,emitRect:!0,emitHeight:.6,angle:PI,emitConeAngle:.3,shape:"soft",particleTime:4,sizeStart:.05,sizeEnd:.05,speed:.004,damping:.98,gravity:-2e-4,fadeRate:.2,randomness:.3,colorStartA:hsl(0,0,1),colorStartB:hsl(.58,1,.9),colorEndA:hsl(0,0,1),colorEndB:hsl(.58,1,.9)},[{name:"wobble",strength:.15}]),b("leaves",{emitRate:3,emitSize:1,emitRect:!0,emitHeight:.2,angle:PI,emitConeAngle:.5,shape:"triangle",particleTime:4,sizeStart:.12,sizeEnd:.12,speed:.003,angleSpeed:.05,damping:.98,gravity:-1e-4,fadeRate:.2,randomness:.4,randomColorLinear:!1,colorStartA:hsl(.25,.7,.4),colorStartB:hsl(.07,.8,.5),colorEndA:hsl(.08,.6,.35),colorEndB:hsl(.05,.7,.4)},[{name:"wobble",strength:.15},{name:"wind",strength:.05}]),b("bubbles",{emitRate:8,emitSize:1,emitRect:!0,emitHeight:.1,emitConeAngle:.2,shape:"ring",particleTime:2,sizeStart:.1,sizeEnd:.2,speed:.01,gravity:3e-4,fadeRate:.2,randomness:.4,colorStartA:hsl(.55,.8,.8,.8),colorStartB:hsl(.5,.6,.9,.6),colorEndA:hsl(.55,.8,.8,.3),colorEndB:hsl(.5,.6,.9,.2)},[{name:"wobble",strength:.3}]),b("fireflies",{emitRate:3,emitSize:1,shape:"glow",particleTime:3,sizeStart:.15,sizeEnd:.15,speed:.003,damping:.99,fadeRate:.8,randomness:.3,additive:!0,colorStartA:hsl(.2,1,.6),colorStartB:hsl(.15,1,.7),colorEndA:hsl(.2,1,.6),colorEndB:hsl(.15,1,.7)},[{name:"turbulence",strength:.3}]),b("trail",{emitRate:60,shape:"soft",particleTime:.5,sizeStart:.4,sizeEnd:0,speed:0,angleSpeed:0,fadeRate:.1,randomness:.2,additive:!0,colorStartA:hsl(.08,1,.6),colorStartB:hsl(.12,1,.65),colorEndA:hsl(.03,1,.5,0),colorEndB:hsl(0,1,.4,0)}),b("muzzle",{emitRate:300,emitTime:.05,emitConeAngle:.3,shape:"glow",particleTime:.2,sizeStart:.5,sizeEnd:.1,speed:.1,damping:.8,fadeRate:.1,randomness:.3,additive:!0,colorStartA:hsl(.14,1,.8),colorStartB:hsl(.1,1,.6),colorEndA:hsl(.08,1,.5,0),colorEndB:hsl(.05,1,.4,0)}),b("blood",{emitRate:300,emitTime:.1,emitConeAngle:.8,shape:"dot",particleTime:3,sizeStart:.15,sizeEnd:.1,speed:.09,gravity:-.01,fadeRate:.1,randomness:.4,collideLevel:!0,restitution:.1,colorStartA:hsl(0,1,.35),colorStartB:hsl(0,1,.2),colorEndA:hsl(0,1,.25),colorEndB:hsl(0,1,.15)},[{name:"stick",strength:1}]),b("confetti",{emitRate:200,emitTime:.1,emitSize:.2,emitConeAngle:.6,shape:"square",particleTime:3,sizeStart:.1,sizeEnd:.1,speed:.09,angleSpeed:.15,damping:.96,gravity:-.004,fadeRate:.1,randomness:.4,randomColorLinear:!1,colorStartA:hsl(0,1,.5),colorStartB:hsl(.6,1,.6),colorEndA:hsl(0,1,.5),colorEndB:hsl(.6,1,.6)},[{name:"wobble",strength:.3}]),b("splash",{emitRate:300,emitTime:.05,emitSize:.5,emitConeAngle:.6,shape:"soft",particleTime:.7,sizeStart:.12,sizeEnd:.06,speed:.08,gravity:-.008,fadeRate:.2,randomness:.4,colorStartA:hsl(.58,.8,.7,.8),colorStartB:hsl(.55,.6,.85,.8),colorEndA:hsl(.58,.8,.7,.2),colorEndB:hsl(.55,.6,.85,.2)});class GLTFPart{constructor(name,mesh,color,textureInfo,transparent){this.name=name,this.mesh=mesh,this.color=color,this.textureInfo=textureInfo,this.transparent=transparent,this.pixelated=!1,this.node=0,this.unlit=!1,this.normalMap=void 0,this.normalScale=1,this.emissiveMap=void 0,this.emissiveMapColor=WHITE,this.roughness=1,this.skin=void 0,this.vertexSource=void 0}}class GLTFAnimation{constructor(name,channels){this.name=name,this.channels=channels,this.duration=channels.reduce((d,c)=>max(d,c.times[c.times.length-1]||0),0)}}class GLTFModel{constructor(parts,animations=[],nodeTree){this.parts=parts,this.animations=animations,this.nodeTree=nodeTree,this.modelMatrix=new Matrix4,this.mesh=new Mesh;for(const part of parts)this.mesh.combine(part.mesh,!part.skin&&nodeTree?.restPose?.[part.node]||RENDER3D_IDENTITY,part.color);animations=new Set(parts.map(p=>p.textureInfo));this.textureInfo=1===animations.size?animations.values().next().value:void 0,this.bounds=void 0}getBounds(){var bounds=this.bounds||=this.mesh.getBounds();return{min:bounds.min.copy(),max:bounds.max.copy()}}center(){var bounds=this.getBounds();return this.transform(bounds.min.add(bounds.max).scale(-.5))}fit(size=1){var bounds=this.getBounds(),bounds=bounds.max.subtract(bounds.min);return this.transform(Matrix4.scaling(vec3(size/(max(bounds.x,bounds.y,bounds.z)||1))))}transform(matrix){matrix=render3DMatrix(matrix);for(const part of this.parts)part.mesh.transform(matrix);return this.mesh.transform(matrix),this.bounds=void 0,this.modelMatrix=matrix.copy().multiply(this.modelMatrix),this}getAnimation(animation){return animation instanceof GLTFAnimation?animation:isNumber(animation)?this.animations[animation]:this.animations.find(a=>a.name===animation)}getPose(animation,time){return this.nodeTree?this.partPoses(gltfNodeWorlds(this.nodeTree,gltfPoseNodes(this.nodeTree,animation,time))):this.parts.map(()=>new Matrix4)}partPoses(worldOf){const tree=this.nodeTree,model=this.modelMatrix,modelInverse=model.copy().invert();return this.parts.map(part=>part.skin?new Matrix4:model.copy().multiply(worldOf(part.node)).multiply(tree.restInverse[part.node]).multiply(modelInverse))}dispose(){for(const part of this.parts)part.mesh.dispose();this.mesh.dispose();for(const textureInfo of new Set(this.parts.flatMap(p=>[p.textureInfo,p.normalMap,p.emissiveMap])))textureInfo!==gltfWhiteTextureInfo&&textureInfo?.destroyWebGLTexture()}createObject(pos3D=vec3()){return new GLTFObject(this,pos3D)}}class GLTFObject extends EngineObject3D{constructor(model,pos3D=vec3()){super(pos3D);pos3D=model.mesh.points.length?model.getBounds():void 0;pos3D&&(this.size3D=pos3D.max.subtract(pos3D.min)),this.model=model,this.animation=void 0,this.animationTime=0,this.animationSpeed=1,this.animationLoop=!0,this.animationPlaying=!1,this.blendFrom=void 0,this.blendTime=0,this.blendElapsed=0,this.poseNodes=void 0,this.poseMixed=!1,this.poseWorldOf=void 0,this.parts=[];for(const part of model.parts){var modelMatrix,mesh=new EngineObject3D(vec3(),part.skin?(mesh=part.mesh,copy=void 0,(copy=new Mesh).points=mesh.points.map(p=>p.copy()),copy.normals=mesh.normals.map(n=>n.copy()),copy.uvs=mesh.uvs.slice(),copy.colors=mesh.colors.slice(),copy.indices=mesh.indices&&mesh.indices.slice(),copy.doubleSided=mesh.doubleSided,copy.dynamicDraw=!0,copy):part.mesh,part.textureInfo,part.color),copy=(mesh.transparent=part.transparent,mesh.pixelated=part.pixelated,mesh.emissive=part.unlit?1:0,mesh.normalMap=part.normalMap,mesh.normalScale=part.normalScale,mesh.emissiveMap=part.emissiveMap,mesh.emissiveMapColor=part.emissiveMapColor.copy(),mesh.roughness=part.roughness,!part.skin&&model.nodeTree?.restPose?.[part.node]);copy&&(modelMatrix=(modelMatrix=model.modelMatrix).copy().multiply(copy).multiply(modelMatrix.copy().invert()),mesh.localMatrix=modelMatrix,mesh.pos3D=modelMatrix.getTranslation(),mesh.rotation3D=modelMatrix.getRotation(),mesh.scale3D=modelMatrix.getScale()),(mesh.drawOwner=this).addChild(mesh),this.parts.push(mesh)}}destroy(immediate){this.destroyed||(this.parts.forEach((o,i)=>this.model.parts[i]?.skin&&o.mesh?.dispose()),super.destroy(immediate))}play(animation=0,loop=!0,speed=1,blend=0){var fading,animation=this.model.getAnimation(animation);animation&&(0<blend&&animation===this.animation&&this.animationPlaying?(this.animationLoop=loop,this.animationSpeed=speed):(fading=this.blendFrom,this.blendFrom=void 0,0<blend&&(fading=!this.animation||fading||this.poseMixed?void 0:{animation:this.animation,time:this.animationTime,speed:this.animationPlaying?this.animationSpeed:0,loop:this.animationLoop},this.blendFrom=fading||{nodes:this.poseNodes||new Map},this.blendTime=blend,this.blendElapsed=0),this.animation=animation,this.animationLoop=loop,this.animationSpeed=speed,this.animationPlaying=!0,this.setAnimationTime(speed<0?animation.duration:0)))}stop(){this.animationPlaying=!1,this.blendFrom=void 0}setAnimationTime(time){this.animationTime=time;var model=this.model,tree=model.nodeTree;if(this.animation&&tree){let nodes=gltfPoseNodes(tree,this.animation,time);var fromNodes,time=this.blendFrom,worldOf=(time&&(fromNodes=time.nodes||gltfPoseNodes(tree,time.animation,time.time),nodes=function(tree,a,b,weight){var mixed=new Map,lerp3=(p,q)=>p.map((v,i)=>v+(q[i]-v)*weight);for(const node of new Set([...a.keys(),...b.keys()])){var p=a.get(node)||gltfNodeTRS(tree.nodes[node]),q=b.get(node)||gltfNodeTRS(tree.nodes[node]);mixed.set(node,{translation:lerp3(p.translation,q.translation),scale:lerp3(p.scale,q.scale),rotation:function(a,b,t){let dot=a[0]*b[0]+a[1]*b[1]+a[2]*b[2]+a[3]*b[3],sign=1;dot<0&&(dot=-dot,sign=-1);let wa=1-t,wb=t*sign;{var angle,s;dot<.9995&&(angle=Math.acos(dot),s=sin(angle),wa=sin(wa*angle)/s,wb=sin(t*angle)/s*sign)}const out=a.map((v,i)=>v*wa+b[i]*wb),l=hypot(...out)||1;return out.map(v=>v/l)}(p.rotation,q.rotation,weight)})}return mixed}(tree,fromNodes,nodes,smoothStep(clamp(this.blendElapsed/this.blendTime)))),gltfNodeWorlds(tree,nodes)),pose=(this.poseNodes=nodes,this.poseWorldOf=worldOf,this.poseMixed=!!time,model.partPoses(worldOf)),parts=this.parts;for(let i=0;i<pose.length&&i<parts.length;++i){var o=parts[i],m=pose[i],skin=model.parts[i].skin;o.destroyed||o.parent!==this||(skin?gltfSkinApply(skin,worldOf,model.modelMatrix,o.mesh):(o.localMatrix=m,o.pos3D=m.getTranslation(),o.rotation3D=m.getRotation(),o.scale3D=m.getScale()))}}}update(){super.update();var animation=this.animation,from=this.blendFrom;if(animation&&(this.animationPlaying||from)){let t=this.animationTime;this.animationPlaying&&(t=gltfAnimationStep(animation,t,this.animationSpeed,this.animationLoop),!this.animationLoop)&&(t>=animation.duration||t<=0)&&(this.animationPlaying=!1),from&&(this.blendElapsed+=timeDelta,from.animation)&&(from.time=gltfAnimationStep(from.animation,from.time,from.speed,from.loop)),this.setAnimationTime(t),from&&this.blendElapsed>=this.blendTime&&(this.blendFrom=void 0)}}getJointMatrix(name){var tree=this.model.nodeTree,index=tree?.nodes?.findIndex(node=>node.name===name)??-1;if(!(index<0))return tree=this.poseWorldOf||gltfNodeWorlds(tree,new Map),this.getMatrix().multiply(this.model.modelMatrix).multiply(tree(index))}}async function loadGLTF(url){var response=await loadFetch(url,"loadGLTF");if(!response.ok)throw new Error(`loadGLTF: could not load ${url}, ${response.status} `+response.statusText);var from=response.url||url;let base="";/^(blob|data):/i.test(from)||(base=response.url?new URL(".",from).href:from.slice(0,from.lastIndexOf("/")+1));from=await response.arrayBuffer();try{return await parseGLTF(from,base)}catch(e){response=/^\s*</.test(String.fromCharCode(...new Uint8Array(from,0,min(from.byteLength,64))));throw new Error("loadGLTF "+url+(response?" is a web page, so the path may be wrong":": "+e.message))}}async function parseGLTF(data,baseUrl="",files){let json=data=ArrayBuffer.isView(data)?data.buffer.slice(data.byteOffset,data.byteOffset+data.byteLength):data,glbBuffer;if(data instanceof ArrayBuffer){var view=new DataView(data);if(12<=data.byteLength&&1179937895===view.getUint32(0,!0)){if(2!==view.getUint32(4,!0))throw new Error("only GLB version 2 is read");for(let offset=12;offset+8<=data.byteLength;){var length=view.getUint32(offset,!0),type=view.getUint32(offset+4,!0),chunk=data.slice(offset+8,offset+8+length);1313821514===type?json=(new TextDecoder).decode(chunk):5130562===type&&(glbBuffer=chunk),offset+=8+length}}else json=(new TextDecoder).decode(data)}if(!(json="string"==typeof json?JSON.parse(json):json).asset||!String(json.asset.version).startsWith("2"))throw new Error("only glTF 2.0 is read");for(const name of json.extensionsRequired||[]){if("KHR_draco_mesh_compression"===name||"EXT_meshopt_compression"===name)throw new Error(`glTF with ${name} is not read, export it uncompressed`);if("KHR_texture_basisu"===name)throw new Error("glTF with KHR_texture_basisu textures is not read, export them as png or jpeg")}json.extensionsUsed?.includes("KHR_texture_basisu")&&!json.extensionsRequired?.includes("KHR_texture_basisu")&&console.warn("glTF: KHR_texture_basisu textures are not read and are left out, export them as png or jpeg");const buffers=await Promise.all((json.buffers||[]).map((buffer,i)=>buffer.uri?gltfFetch(buffer.uri,baseUrl,files).then(r=>r.arrayBuffer()):glbBuffer)),usedTextures=new Set((json.materials||[]).flatMap(m=>[m.pbrMetallicRoughness?.baseColorTexture?.index,m.normalTexture?.index,m.emissiveTexture?.index])),normalTextures=new Set((json.materials||[]).map(m=>m.normalTexture?.index)),opaqueTextures=function(json){var opaque=new Set,cut=new Set;for(const material of json.materials||[]){var index=material.pbrMetallicRoughness?.baseColorTexture?.index;void 0!==index&&(material.alphaMode&&"OPAQUE"!==material.alphaMode?cut:opaque).add(index)}for(const index of cut)opaque.delete(index);return opaque}(json),textures=await Promise.all((json.textures||[]).map(async(texture,index)=>{if(glContext&&"undefined"!=typeof createImageBitmap&&usedTextures.has(index))try{var view,source=texture.source??texture.extensions?.EXT_texture_webp?.source??texture.extensions?.EXT_texture_avif?.source,image=json.images[source],sampler=json.samplers?.[texture.sampler]||{};let blob;blob=image.uri?await gltfFetch(image.uri,baseUrl,files).then(r=>r.blob()):(view=json.bufferViews[image.bufferView],new Blob([new Uint8Array(buffers[view.buffer],view.byteOffset||0,view.byteLength)],{type:image.mimeType}));var opaque=opaqueTextures.has(index)&&"image/jpeg"!==blob.type,bitmap=normalTextures.has(index)?await createImageBitmap(blob,{colorSpaceConversion:"none",premultiplyAlpha:"none"}):opaque?await createImageBitmap(blob,{premultiplyAlpha:"none"}).then(gltfOpaqueImage):await createImageBitmap(blob);return new TextureInfo(bitmap,!0,[sampler.wrapS??10497,sampler.wrapT??10497],9728===sampler.magFilter)}catch(e){console.warn("glTF image not loaded, left out: "+(e?.message||e))}})),parts=[],parents=[],restInverse=[],restPose=[],visited=new Set,visit=((json.nodes||[]).forEach((node,i)=>(node.children||[]).forEach(child=>parents[child]??=i)),(index,parentMatrix,parentIndex,parentRest)=>{if(visited.has(index))throw new Error("glTF node "+index+" is reached twice, nodes must form trees");visited.add(index);var node=json.nodes[index],local=gltfNodeMatrix(node);let matrix=parentMatrix?parentMatrix.copy().multiply(local):local,rest=parentRest&&parentRest.copy().multiply(local);if(!node.matrix&&node.scale?.includes(0)&&(rest||=matrix,parentRest=gltfNodeMatrix({...node,scale:node.scale.map(s=>s||1)}),matrix=parentMatrix?parentMatrix.copy().multiply(parentRest):parentRest),parents[index]=parentIndex,restInverse[index]=matrix.copy().invert(),rest&&(restPose[index]=rest.copy().multiply(restInverse[index])),void 0!==node.mesh){var mesh=json.meshes[node.mesh],skin=void 0!==node.skin?json.skins?.[node.skin]:void 0;for(const primitive of mesh.primitives){var skinned=skin?.joints?.length&&void 0!==primitive.attributes.JOINTS_0&&void 0!==primitive.attributes.WEIGHTS_0,part=function(json,buffers,textures,primitive,matrix,name){var mode=primitive.mode??4;if(!(mode<4)){var attributes=primitive.attributes,read=(index,make)=>{var{data,components,count}=gltfAccessor(json,buffers,index),list=[];for(let i=0;i<count;++i)list.push(make(data,i*components,components));return list},points=read(attributes.POSITION,(d,k)=>vec3(d[k],d[k+1],d[k+2])),normals=void 0!==attributes.NORMAL?read(attributes.NORMAL,(d,k)=>vec3(d[k],d[k+1],d[k+2])):void 0,material=json.materials?.[primitive.material]||{},pbr=material.pbrMetallicRoughness||{},textureRef=pbr.baseColorTexture,uvTransform=textureRef?.extensions?.KHR_texture_transform,uvAccessor=attributes["TEXCOORD_"+(uvTransform?.texCoord??textureRef?.texCoord??0)];const[ox,oy]=uvTransform?.offset||[0,0],[sx,sy]=uvTransform?.scale||[1,1],r=uvTransform?.rotation||0,c=cos(r),s=sin(r);uvTransform=void 0!==uvAccessor?read(uvAccessor,(d,k)=>vec2(c*sx*d[k]+s*sy*d[k+1]+ox,c*sy*d[k+1]-s*sx*d[k]+oy)):void 0,uvAccessor=void 0!==attributes.COLOR_0?read(attributes.COLOR_0,(d,k,n)=>rgb(gltfSRGB(d[k]),gltfSRGB(d[k+1]),gltfSRGB(d[k+2]),3<n?d[k+3]:1)):void 0;let indices=void 0!==primitive.indices?Array.from(gltfAccessor(json,buffers,primitive.indices).data):points.map((_,i)=>i);5===mode?indices=indices.flatMap((_,i,s)=>i<2?[]:1&i?[s[i-1],s[i-2],s[i]]:[s[i-2],s[i-1],s[i]]):6===mode&&(indices=indices.flatMap((_,i,s)=>i<2?[]:[s[0],s[i-1],s[i]]));var read=(new Mesh).addTriangles(points,indices,normals,uvTransform,uvAccessor),attributes=normals?void 0:read.indices.slice(),primitive=(normals||read.computeNormals(!1),read.transform(matrix),pbr.baseColorFactor||[1,1,1,1]),mode=(read.doubleSided=!!material.doubleSided,material.extensions?.KHR_materials_transmission?.transmissionFactor||0),points="BLEND"===material.alphaMode,uvTransform=(points?primitive[3]:1)*(1-.8*mode),uvAccessor=textureRef&&json.textures?.[textureRef.index],normals=new GLTFPart(name,read,rgb(gltfSRGB(primitive[0]),gltfSRGB(primitive[1]),gltfSRGB(primitive[2]),uvTransform),textureRef?textures[textureRef.index]:void 0,points||0<mode),matrix=(normals.pixelated=9728===json.samplers?.[uvAccessor?.sampler]?.magFilter,normals.vertexSource=attributes,normals.unlit=!!material.extensions?.KHR_materials_unlit,material.normalTexture),name=material.emissiveTexture,[read,primitive,uvTransform]=(normals.normalMap=matrix&&textures[matrix.index],normals.normalScale=matrix?.scale??1,normals.roughness=pbr.roughnessFactor??1,material.emissiveFactor||[0,0,0]);return(read||primitive||uvTransform)&&(textureRef=material.extensions?.KHR_materials_emissive_strength?.emissiveStrength??1,normals.emissiveMap=name?textures[name.index]:function(){if(gltfWhiteTextureInfo||!glContext||!canvasAvailable())return gltfWhiteTextureInfo;var context=createCanvasContext(1);return context.fillStyle="#fff",context.fillRect(0,0,1,1),gltfWhiteTextureInfo=new TextureInfo(context.canvas)}(),normals.emissiveMapColor=rgb(gltfSRGB(read)*textureRef,gltfSRGB(primitive)*textureRef,gltfSRGB(uvTransform)*textureRef)),normals}}(json,buffers,textures,primitive,skinned?new Matrix4:matrix,node.name||mesh.name||"part "+parts.length);part&&(part.node=index,skinned&&(part.skin=function(json,buffers,skin,primitive,part){const joints=skin.joints,mesh=part.mesh,count=mesh.points.length,bind=void 0!==skin.inverseBindMatrices?gltfAccessor(json,buffers,skin.inverseBindMatrices).data:void 0,inverseBind=joints.map((_,j)=>bind?new Matrix4(bind.subarray(16*j,16*j+16)):new Matrix4),attributes=primitive.attributes,read=name=>gltfAccessor(json,buffers,attributes[name]).data,sets=[[read("JOINTS_0"),read("WEIGHTS_0")]],vertexJoints=(void 0!==attributes.JOINTS_1&&void 0!==attributes.WEIGHTS_1&&sets.push([read("JOINTS_1"),read("WEIGHTS_1")]),new Uint16Array(4*count)),vertexWeights=new Float32Array(4*count),bindPoints=new Float32Array(3*count),bindNormals=new Float32Array(3*count);for(let v=0;v<count;++v){var fileJoints,fileWeights,from=part.vertexSource?part.vertexSource[v]:v,influences=[];for([fileJoints,fileWeights]of sets)for(let k=0;k<4;++k)influences.push([fileJoints[4*from+k],fileWeights[4*from+k]]);1<sets.length&&influences.sort((a,b)=>b[1]-a[1]);let total=0;for(let k=0;k<4;++k)total+=influences[k][1];for(let k=0;k<4;++k){var[joint,weight]=influences[k];vertexJoints[4*v+k]=joint<joints.length?joint:0,vertexWeights[4*v+k]=total?weight/total:k?0:1}var p=mesh.points[v],n=mesh.normals[v];bindPoints.set([p.x,p.y,p.z],3*v),bindNormals.set([n.x,n.y,n.z],3*v),mesh.points[v]=p.copy(),mesh.normals[v]=n.copy()}return{joints:joints,inverseBind:inverseBind,vertexJoints:vertexJoints,vertexWeights:vertexWeights,bindPoints:bindPoints,bindNormals:bindNormals}}(json,buffers,skin,primitive,part)),parts.push(part))}}for(const child of node.children||[])visit(child,matrix,index,rest)});let animations;try{var scene=json.scenes?.[json.scene??0];if(scene)(scene.nodes||[]).forEach(i=>visit(i));else if(json.nodes){const children=new Set(json.nodes.flatMap(n=>n.children||[]));json.nodes.forEach((n,i)=>children.has(i)||visit(i))}animations=(json.animations||[]).map((animation,i)=>new GLTFAnimation(animation.name||"animation "+i,animation.channels.filter(c=>void 0!==c.target.node&&["translation","rotation","scale"].includes(c.target.path)&&!json.nodes[c.target.node].matrix).map(c=>{var sampler=animation.samplers[c.sampler];return{node:c.target.node,path:c.target.path,interpolation:sampler.interpolation||"LINEAR",times:gltfAccessor(json,buffers,sampler.input).data,values:gltfAccessor(json,buffers,sampler.output).data,components:"rotation"===c.target.path?4:3}})))}catch(error){for(const texture of textures)texture?.destroyWebGLTexture();throw error}var used=new Set(parts.flatMap(p=>[p.textureInfo,p.normalMap,p.emissiveMap]));for(const texture of textures)texture&&!used.has(texture)&&texture.destroyWebGLTexture();var scene={nodes:json.nodes,parents:parents,restInverse:restInverse,restPose:restPose},restWorlds=gltfNodeWorlds(scene,new Map);for(const part of parts)part.skin&&gltfSkinApply(part.skin,restWorlds,RENDER3D_IDENTITY,part.mesh);return new GLTFModel(parts,animations,scene)}function gltfPoseNodes(tree,animation,time){var moved=new Map;for(const channel of animation.channels){let node=moved.get(channel.node);node||moved.set(channel.node,node=gltfNodeTRS(tree.nodes[channel.node])),!function(channel,time,out){var{times,values,components:n,interpolation:channel}=channel,last=times.length-1,cubic="CUBICSPLINE"===channel,stride=cubic?3*n:n,at=cubic?n:0;let k=0,high=last;for(;k<high;){var mid=k+high+1>>1;times[mid]<=time?k=mid:high=mid-1}if(k>=last||time<=times[0]||"STEP"===channel)for(let j=0;j<n;++j)out[j]=values[k*stride+at+j];else{var dt=times[k+1]-times[k],t=(time-times[k])/dt,a=k*stride,b=a+stride;if(cubic){var last=t*t,channel=last*t,h00=2*channel-3*last+1,h10=channel-2*last+t,h01=-2*channel+3*last,h11=channel-last;for(let j=0;j<n;++j)out[j]=h00*values[a+n+j]+h10*dt*values[a+2*n+j]+h01*values[b+n+j]+h11*dt*values[b+j]}else if(4===n){let dot=0;for(let j=0;j<4;++j)dot+=values[a+j]*values[b+j];cubic=dot<0?-1:1;dot*=cubic;let wa=1-t,wb=t*cubic;dot<.9995&&(channel=Math.acos(dot),last=sin(channel),wa=sin((1-t)*channel)/last,wb=sin(t*channel)/last*cubic);for(let j=0;j<4;++j)out[j]=wa*values[a+j]+wb*values[b+j]}else for(let j=0;j<n;++j)out[j]=values[a+j]+(values[b+j]-values[a+j])*t;if(4===n){var l=hypot(out[0],out[1],out[2],out[3])||1;for(let j=0;j<4;++j)out[j]/=l}}}(channel,time,node[channel.path])}return moved}function gltfNodeWorlds(tree,moved){const world=[],worldOf=i=>{var local,parent;return world[i]||(local=gltfNodeMatrix(moved.get(i)||tree.nodes[i]),parent=tree.parents[i],world[i]=void 0===parent?local:worldOf(parent).copy().multiply(local))};return worldOf}function gltfAnimationStep(animation,time,speed,loop){animation=animation.duration,time+=timeDelta*speed;return loop?animation?mod(time,animation):0:clamp(time,0,animation)}function gltfSkinApply(skin,worldOf,modelMatrix,mesh){var jointCount=skin.joints.length,M=skin.jointMatrices||=new Float32Array(21*jointCount),m=gltfSkinScratch[0],n=gltfSkinScratch[1];for(let j=0;j<jointCount;++j){m.m.set(modelMatrix.m),m.multiply(worldOf(skin.joints[j])).multiply(skin.inverseBind[j]),n.m.set(m.m),n.invert().transpose();var a=m.m,b=n.m,o=21*j;M[o]=a[0],M[1+o]=a[1],M[2+o]=a[2],M[3+o]=a[4],M[4+o]=a[5],M[5+o]=a[6],M[6+o]=a[8],M[7+o]=a[9],M[8+o]=a[10],M[9+o]=a[12],M[10+o]=a[13],M[11+o]=a[14],M[12+o]=b[0],M[13+o]=b[1],M[14+o]=b[2],M[15+o]=b[4],M[16+o]=b[5],M[17+o]=b[6],M[18+o]=b[8],M[19+o]=b[9],M[20+o]=b[10]}var{vertexJoints,vertexWeights,bindPoints,bindNormals}=skin,points=mesh.points,normals=mesh.normals,layout=mesh.vertexLayout,count=points.length,floats=layout&&mesh.indices&&layout.pointCount===count?new Float32Array(layout.data):void 0;let r=0,x0=1/0,y0=1/0,z0=1/0,x1=-1/0,y1=-1/0,z1=-1/0;for(let v=0;v<count;++v){var v3=3*v,v4=4*v,px=bindPoints[v3],py=bindPoints[1+v3],pz=bindPoints[2+v3],qx=bindNormals[v3],qy=bindNormals[1+v3],qz=bindNormals[2+v3];let x=0,y=0,z=0,nx=0,ny=0,nz=0;for(let k=0;k<4;++k){var w=vertexWeights[v4+k];if(w){const o=21*vertexJoints[v4+k];x+=w*(M[o]*px+M[3+o]*py+M[6+o]*pz+M[9+o]),y+=w*(M[1+o]*px+M[4+o]*py+M[7+o]*pz+M[10+o]),z+=w*(M[2+o]*px+M[5+o]*py+M[8+o]*pz+M[11+o]),nx+=w*(M[12+o]*qx+M[15+o]*qy+M[18+o]*qz),ny+=w*(M[13+o]*qx+M[16+o]*qy+M[19+o]*qz),nz+=w*(M[14+o]*qx+M[17+o]*qy+M[20+o]*qz)}}var v3=points[v],normal=normals[v],l=(nx*nx+ny*ny+nz*nz)**.5||1;v3.x=x,v3.y=y,v3.z=z,normal.x=nx/l,normal.y=ny/l,normal.z=nz/l,floats&&(floats[v3=9*v]=x,floats[1+v3]=y,floats[2+v3]=z,floats[3+v3]=normal.x,floats[4+v3]=normal.y,floats[5+v3]=normal.z,r=max(r,x*x+y*y+z*z),x0=min(x0,x),y0=min(y0,y),z0=min(z0,z),x1=max(x1,x),y1=max(y1,y),z1=max(z1,z))}floats&&(mesh.radius=r**.5,mesh.bounds=count?{min:vec3(x0,y0,z0),max:vec3(x1,y1,z1)}:{min:vec3(),max:vec3()},mesh.vertexDataPacked=!0),mesh.dirty=!0}const gltfSkinScratch=[new Matrix4,new Matrix4];function gltfNodeTRS(node){return{translation:[...node.translation||[0,0,0]],rotation:[...node.rotation||[0,0,0,1]],scale:[...node.scale||[1,1,1]]}}function gltfOpaqueImage(image){var gl=glContext,{width,height}=image;if(gl.isContextLost())return image;var data=new Uint8ClampedArray(width*height*4),texture=gl.createTexture(),framebuffer=gl.createFramebuffer(),bound=gl.getParameter(gl.FRAMEBUFFER_BINDING);try{gl.bindTexture(gl.TEXTURE_2D,texture),gl.texImage2D(gl.TEXTURE_2D,0,gl.RGBA,gl.RGBA,gl.UNSIGNED_BYTE,image),gl.bindFramebuffer(gl.FRAMEBUFFER,framebuffer),gl.framebufferTexture2D(gl.FRAMEBUFFER,gl.COLOR_ATTACHMENT0,gl.TEXTURE_2D,texture,0),gl.readPixels(0,0,width,height,gl.RGBA,gl.UNSIGNED_BYTE,data)}finally{gl.bindFramebuffer(gl.FRAMEBUFFER,bound),gl.bindTexture(gl.TEXTURE_2D,glActiveTexture),gl.deleteFramebuffer(framebuffer),gl.deleteTexture(texture)}for(let i=3;i<data.length;i+=4)data[i]=255;return image.close(),createImageBitmap(new ImageData(data,width,height))}function gltfFetch(uri,baseUrl,files){if(files&&!/^(data|https?|blob):/i.test(uri)){let name=uri.replace(/[?#].*$/,"");try{name=decodeURIComponent(name)}catch{}const clean=text=>{var parts=[];for(const part of text.replace(/\\/g,"/").split("/"))if(".."===part?!parts.pop():part&&"."!==part&&!parts.push(part))return;return parts.join("/")};var path=clean(baseUrl+name);if(void 0===path)return Promise.reject(new Error("glTF needs "+name+", which is above the files given"));const lower=path.toLowerCase(),file=lower.slice(lower.lastIndexOf("/")+1),byPath=new Map([...files].map(([key,value])=>[clean(key)?.toLowerCase(),value]));var files=files.get(path)??byPath.get(lower),keys=[...byPath.keys()];return files?Promise.resolve(new Response(files)):(files=keys.filter(k=>k?.includes("/")&&lower.endsWith("/"+k)).sort((a,b)=>b.length-a.length)[0])?Promise.resolve(new Response(byPath.get(files))):1===(files=new Set(keys.filter(k=>k?.slice(k.lastIndexOf("/")+1)===file).map(k=>byPath.get(k)))).size?Promise.resolve(new Response([...files][0])):Promise.reject(new Error("glTF needs "+path+(files.size?", and more than one file has its name":", which is not among the files given")))}if(uri.startsWith("data:")){var keys=uri.indexOf(","),bytes=atob(uri.slice(keys+1)),data=new Uint8Array(bytes.length);for(let i=0;i<bytes.length;++i)data[i]=bytes.charCodeAt(i);return Promise.resolve(new Response(data.buffer,{headers:{"Content-Type":uri.slice(5,uri.indexOf(";"))}}))}path="undefined"!=typeof location&&location.href,files=path?new URL(baseUrl,path):/^[a-z][a-z0-9+.-]*:/i.test(baseUrl)?baseUrl:void 0;const url=files?new URL(uri,files).href:baseUrl+uri;return loadFetch(url,"glTF").then(r=>{if(r.ok)return r;throw new Error("glTF needs "+uri+", not found at "+url)})}function gltfNodeMatrix(node){var x,y,z,w,sx,sy,sz;return node.matrix?new Matrix4(node.matrix):([x,y,z,w]=node.rotation||[0,0,0,1],[sx,sy,sz]=node.scale||[1,1,1],node=node.translation||[0,0,0],new Matrix4([(1-2*(y*y+z*z))*sx,2*(x*y+z*w)*sx,2*(x*z-y*w)*sx,0,2*(x*y-z*w)*sy,(1-2*(x*x+z*z))*sy,2*(y*z+x*w)*sy,0,2*(x*z+y*w)*sz,2*(y*z-x*w)*sz,(1-2*(x*x+y*y))*sz,0,node[0],node[1],node[2],1]))}function gltfAccessor(json,buffers,index){var a=json.accessors[index],view=json.bufferViews?.[a.bufferView],components={SCALAR:1,VEC2:2,VEC3:3,VEC4:4,MAT4:16}[a.type],types={5120:Int8Array,5121:Uint8Array,5122:Int16Array,5123:Uint16Array,5125:Uint32Array,5126:Float32Array},Type=types[a.componentType];if(!components||!Type)throw new Error(`glTF accessor of ${a.type} ${a.componentType} is not read`);var size=Type.BYTES_PER_ELEMENT,scales=[[Int8Array,127],[Uint8Array,255],[Int16Array,32767],[Uint16Array,65535]],scale=a.normalized&&new Map(scales).get(Type)||1;if(!(view||a.count<=1<<20))throw new Error("glTF accessor "+index+" has no buffer and a count of "+a.count);var scales=view&&(buffers[view.buffer]?.byteLength??0),stride=view?.byteStride||components*size;if(view&&!((view.byteOffset||0)+(a.byteOffset||0)+(a.count-1)*stride+components*size<=scales))throw new Error("glTF accessor "+index+" reaches past its buffer");var out=new Float32Array(a.count*components);if(view){var buffer=buffers[view.buffer],offset=(view.byteOffset||0)+(a.byteOffset||0);if(offset%size||stride%size)throw new Error("glTF accessor "+index+" is not aligned to its "+size+" byte values");if(stride===components*size)out.set(new Type(buffer,offset,a.count*components));else for(let i=0;i<a.count;++i)out.set(new Type(buffer,offset+i*stride,components),i*components)}var sparse=a.sparse;if(sparse){var{indices:scales,values:view}=sparse,types=types[scales.componentType],indexView=json.bufferViews?.[scales.bufferView],json=json.bufferViews?.[view.bufferView];if(!types||!indexView||!json)throw new Error("glTF sparse accessor is missing its indices or values");var indexAt=(indexView.byteOffset||0)+(scales.byteOffset||0),valueAt=(json.byteOffset||0)+(view.byteOffset||0);if(indexAt%types.BYTES_PER_ELEMENT||valueAt%size||!(indexAt+sparse.count*types.BYTES_PER_ELEMENT<=(buffers[indexView.buffer]?.byteLength??0))||!(valueAt+sparse.count*components*size<=(buffers[json.buffer]?.byteLength??0)))throw new Error("glTF sparse accessor "+index+" is not aligned or reaches past its buffer");var at=new types(buffers[indexView.buffer],(indexView.byteOffset||0)+(scales.byteOffset||0),sparse.count),data=new Type(buffers[json.buffer],(json.byteOffset||0)+(view.byteOffset||0),sparse.count*components);for(let i=0;i<sparse.count;++i)out.set(data.subarray(i*components,(i+1)*components),at[i]*components)}if(1!==scale)for(let i=0;i<out.length;++i)out[i]=max(out[i]/scale,-1);return{data:out,components:components,count:a.count}}let gltfWhiteTextureInfo;function gltfSRGB(c){return c<=.0031308?12.92*c:1<=c?c:1.055*c**(1/2.4)-.055}let threeJS;class ThreeJSPlugin{constructor(THREE,cameraFOV=60){threeJS=this,headlessMode||(this.THREE=THREE,this.renderer=new THREE.WebGLRenderer({antialias:!0}),this.scene=new THREE.Scene,this.camera=new THREE.PerspectiveCamera(cameraFOV,1,.1,1e3),this.cameraAlign2D=!0,this.cameraNear=void 0,this.cameraFar=void 0,THREE=this.renderer.domElement,(cameraFOV=mainCanvas.parentElement).insertBefore(THREE,cameraFOV.firstChild),THREE.style.cssText=mainCanvas.style.cssText,setBackgroundCanvas(THREE),engineAddPlugin(void 0,()=>this.render()))}alignCamera2D(){var distance=mainCanvasSize.y/2/cameraScale/tan(this.camera.fov/2*PI/180),baseNear=this.cameraNear??=this.camera.near,baseFar=this.cameraFar??=this.camera.far,scale=max(1,2*distance/baseFar),baseNear=baseNear*scale,baseFar=baseFar*scale;baseNear==this.camera.near&&baseFar==this.camera.far||(this.camera.near=baseNear,this.camera.far=baseFar,this.camera.updateProjectionMatrix()),this.camera.position.set(cameraPos.x,cameraPos.y,distance),this.camera.rotation.set(0,0,-cameraAngle)}render(){var threeCanvas,dpr,bufferSizeX,bufferSizeY;this.renderer&&(threeCanvas=this.renderer.domElement,dpr=getCanvasPixelRatio(),bufferSizeX=mainCanvasSize.x*dpr|0,bufferSizeY=mainCanvasSize.y*dpr|0,threeCanvas.width==bufferSizeX&&threeCanvas.height==bufferSizeY||(this.renderer.setPixelRatio(dpr),this.renderer.setSize(mainCanvasSize.x,mainCanvasSize.y,!1),this.camera.aspect=mainCanvasSize.x/mainCanvasSize.y,this.camera.updateProjectionMatrix()),threeCanvas.style.cssText!=mainCanvas.style.cssText&&(threeCanvas.style.cssText=mainCanvas.style.cssText),this.cameraAlign2D&&this.alignCamera2D(),this.renderer.render(this.scene,this.camera))}}class ThreeJSObject extends EngineObject{constructor(pos,size,mesh,z=0){super(pos,size),this.mesh=mesh,this.z=z,mesh&&(threeJS.scene?.add(mesh),this.syncMesh())}updateTransforms(updateChildren=!0){super.updateTransforms(updateChildren),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(immediate){this.destroyed||(this.mesh&&threeJS.scene?.remove(this.mesh),super.destroy(immediate))}}export{engineName,engineVersion,frameRate,timeDelta,engineObjects,engineObjectsCollide,frame,time,timeReal,paused,getPaused,setPaused,engineInit,engineStep,engineObjectsUpdate,engineObjectsDestroy,engineObjectsCollect,engineObjectsCallback,engineObjectsRaycast,engineAddPlugin,engineAddLoad,debug,debugOverlay,debugWatermark,debugKey,debugKeysAlways,debugTweakables,levelEditor,LevelEditor,setLevelEditor,ASSERT,LOG,debugPointSize,debugRect,debugPoly,debugCircle,debugPoint,debugLine,debugOverlap,debugText,debugClear,debugScreenshot,debugShowErrors,cameraPos,cameraAngle,cameraScale,timeScale,canvasColorTiles,canvasClearColor,canvasMaxSize,canvasMinAspect,canvasMaxAspect,canvasFixedSize,canvasPixelated,tilesPixelated,canvasPixelRatio,fontDefault,showSplashScreen,loadingScreen,headlessMode,engineManualStep,engineVariableStep,tileDefaultSize,tileDefaultPadding,tileDefaultBleed,enablePhysicsSolver,objectDefaultMass,objectDefaultDamping,objectDefaultAngleDamping,objectDefaultRestitution,objectDefaultFriction,objectMaxSpeed,gravity,particleEmitRateScale,glEnable,drawScreenSpace,glCircleSides,gamepadsEnable,gamepadDirectionEmulateStick,gamepadAxisFilterEnable,inputWASDEmulateDirection,touchInputEnable,touchGamepadEnable,touchGamepadPassthrough,touchPinchWheel,touchGamepadCenterButtonSize,touchGamepadButtonCount,touchGamepadLeftStick,touchGamepadLeftButtonCount,touchGamepadRightStick,touchGamepadAnalog,touchGamepadFloating,touchGamepadSize,touchGamepadAlpha,touchGamepadDisplayTime,touchGamepadVibration,vibrateEnable,soundEnable,soundIgnoreSilentSwitch,soundVolume,soundDefaultRange,soundDefaultTaper,soundPauseWhenHidden,setCameraPos,setCameraAngle,setCameraScale,setTimeScale,setCanvasColorTiles,setCanvasClearColor,setCanvasMaxSize,setCanvasMinAspect,setCanvasMaxAspect,setCanvasFixedSize,setCanvasPixelated,setTilesPixelated,setCanvasPixelRatio,getCanvasPixelRatio,setFontDefault,setShowSplashScreen,setLoadingScreen,setHeadlessMode,setEngineManualStep,setEngineVariableStep,setGLEnable,setDrawScreenSpace,setTileDefaultSize,setTileDefaultPadding,setTileDefaultBleed,setEnablePhysicsSolver,setObjectDefaultMass,setObjectDefaultDamping,setObjectDefaultAngleDamping,setObjectDefaultRestitution,setObjectDefaultFriction,setObjectMaxSpeed,setGravity,setParticleEmitRateScale,setGLCircleSides,setTouchInputEnable,setGamepadsEnable,setGamepadDirectionEmulateStick,setGamepadAxisFilterEnable,setInputWASDEmulateDirection,setTouchGamepadEnable,setTouchGamepadPassthrough,setTouchPinchWheel,setTouchGamepadCenterButtonSize,setTouchGamepadButtonCount,setTouchGamepadLeftStick,setTouchGamepadLeftButtonCount,setTouchGamepadRightStick,setTouchGamepadAnalog,setTouchGamepadFloating,setTouchGamepadSize,setTouchGamepadAlpha,setTouchGamepadDisplayTime,setTouchGamepadVibration,setVibrateEnable,setSoundEnable,setSoundIgnoreSilentSwitch,setSoundVolume,setSoundDefaultRange,setSoundDefaultTaper,setSoundPauseWhenHidden,setDebugWatermark,setDebugKey,setDebugKeysAlways,setDebugTweakables,setDebugOverlay,PI,abs,floor,ceil,round,min,max,sign,hypot,log2,sin,cos,tan,atan2,mod,clamp,percent,distanceWrap,lerpWrap,distanceAngle,lerpAngle,lerp,percentLerp,smoothStep,nearestPowerOfTwo,isPowerOfTwo,isOverlapping,isIntersecting,collideCircleCircle,collideCircleBox,collideBoxBox,lineTest,oscillate,formatTime,fetchJSON,saveText,createCanvasContext,saveCanvas,saveDataURL,shareURL,readSaveData,writeSaveData,noise1D,noise2D,rand,randInt,randBool,randSign,randInCircle,randVec2,randColor,RandomGenerator,Vector2,Color,Timer,vec2,rgb,hsl,isColor,isVector2,isNumber,isStringLike,isArray,WHITE,CLEAR_WHITE,BLACK,CLEAR_BLACK,GRAY,RED,ORANGE,YELLOW,GREEN,CYAN,BLUE,PURPLE,MAGENTA,tile,TileInfo,TextureInfo,loadTexture,SpriteAnimation,SpriteAnimator,Shader,mainCanvas,mainContext,drawContext,workCanvas,workContext,workReadCanvas,workReadContext,backgroundCanvas,mainCanvasSize,textureInfos,drawCount,primitiveCount,screenToWorld,worldToScreen,screenToWorldDelta,worldToScreenDelta,screenToWorldTransform,drawTile,drawRect,drawRectGradient,drawTextureWrapped,drawLineList,drawLine,drawPoly,drawRegularPoly,drawEllipse,drawCircle,drawEllipseGradient,drawCircleGradient,drawCanvas2D,drawText,drawTextScreen,setAdditiveBlendMode,setShader,setBackgroundCanvas,combineCanvases,engineImageFont,ImageFont,isFullscreen,toggleFullscreen,setCursor,getCameraSize,cameraFit,isOnScreen,glCanvas,glContext,glAntialias,glClearCanvas,glSetTexture,glSetTextureWrap,glCompileShader,glCreateProgram,glCreateTexture,glDeleteTexture,glSetTextureData,glFlush,glCopyToContext,glSetAntialias,glMaxTextureSize,glClampTextureSize,glDraw,glDrawUntextured,glDrawPointsTransform,glDrawOutlineTransform,glDrawPoints,glDrawColoredPoints,glSetRenderTarget,glClearRect,keyIsDown,keyWasPressed,keyWasReleased,keyDirection,inputClear,inputClearKey,mouseIsDown,mouseWasPressed,mouseWasReleased,mousePos,mousePosScreen,mouseDelta,mouseDeltaScreen,mouseWheel,touchPinch,mouseInWindow,isUsingGamepad,lastInputDevice,inputMouseMoveThreshold,inputPreventDefault,gamepadPrimary,isTouchDevice,setInputPreventDefault,setInputMouseMoveThreshold,usingMouseInput,usingKeyboardInput,usingGamepadInput,gamepadIsDown,gamepadWasPressed,gamepadWasReleased,gamepadStick,gamepadStickCount,gamepadDpad,gamepadConnected,gamepadVibrate,gamepadVibrateStop,vibrate,vibrateStop,pointerLockRequest,pointerLockExit,pointerLockIsActive,audioContext,audioMasterGain,setAudioMasterEffect,audioDefaultSampleRate,audioIsRunning,Sound,SoundInstance,speak,speakStop,getNoteFrequency,playSamples,playAudioBuffer,createAudioBuffer,zzfx,zzfxG,EngineObject,tileCollisionLayers,tileCollisionGetData,tileCollisionTest,tileCollisionRaycast,tileLayersLoad,tileLayersFromLDtk,objectLayersAddType,objectLayersLoad,TileLayerData,CanvasLayer,TileLayer,TileCollisionLayer,ParticleEmitter,Particle,medals,medalsPreventUnlock,medalDisplayTime,medalDisplaySlideTime,medalDisplaySize,medalsInit,medalsForEach,medalsReset,setMedalDisplayTime,setMedalDisplaySlideTime,setMedalDisplaySize,setMedalsPreventUnlock,Medal,newgrounds,NewgroundsPlugin,NewgroundsMedal,wavedash,WavedashPlugin,WavedashMedal,postProcess,PostProcessPlugin,postProcessBloom,postProcessBloomShader,postProcessEffects,postProcessGlow,postProcessScanlines,postProcessNoise,postProcessVignette,postProcessCurve,postProcessChromatic,postProcessOutline,postProcessTiltShift,postProcessDepthOfField,postProcessTV,lightSystem,LightSystemPlugin,Light,DirectionalLight,AudioEffect,AudioFilter,AudioReverb,AudioDelay,AudioDistortion,AudioCompressor,uiSystem,uiDebug,uiSetDebug,UISystemPlugin,UIObject,UIText,UITextInput,UITile,UIButton,UICheckbox,UISlider,UIVideo,UILayout,box2d,box2dDebug,box2dSetDebug,box2dInit,Box2dPlugin,Box2dObject,Box2dStaticObject,Box2dKinematicObject,Box2dTileLayer,Box2dRaycastResult,Box2dJoint,Box2dTargetJoint,Box2dDistanceJoint,Box2dPinJoint,Box2dRopeJoint,Box2dRevoluteJoint,Box2dGearJoint,Box2dPrismaticJoint,Box2dWheelJoint,Box2dWeldJoint,Box2dFrictionJoint,Box2dPulleyJoint,Box2dMotorJoint,drawNineSlice,drawNineSliceScreen,drawThreeSlice,drawThreeSliceScreen,TileSlice,drawCrescent,getCrescentPoints,Tween,tweenProperty,tweenStopAll,tweenUpdate,Ease,setScene,getScene,ParallaxLayer,parallaxMountains,PathFinder,PathFinderNode,tweak,tweakDivider,tweakButton,tweakEngineDefaults,vec3,isVector3,randVector3,randInSphere,Vector3,Matrix4,Ray3D,buildMatrix,isPointInBox3D,isOverlapping3D,collideSphereSphere,collideSphereBox,collideSphereInBox,collideSphereCylinder,collideBoxBox3D,raycastSphere,raycastPlane,raycastBox,render3D,Render3DPlugin,CubeMap,makeCubeMap,loadCubeMap,Camera3D,EngineObject3D,Mesh,InstancedMesh3D,buildLathe,buildCylinder,buildSphere,buildCone,buildCapsule,buildTorus,buildBox,buildGrid,buildRibbon,buildLoft,buildSky,normalMapFromHeight,buildExtrude,buildText3D,HeightMap,VoxelMap,Light3D,DirectionalLight3D,CameraControl3D,FirstPersonCamera3D,ParticleEmitter3D,Trail3D,LensFlare3D,engineObjectsCollect3D,engineObjectsCallback3D,engineObjectsRaycast3D,parseOBJ,loadOBJ,GLTFModel,GLTFPart,GLTFObject,GLTFAnimation,parseGLTF,loadGLTF,debugBox3D,debugSphere3D,debugLine3D,debugPoint3D,level3DAddType,level3DAddMesh,level3DLoad,level3DAddPrefab,level3DLoadPrefab,level3DSpawn,Prefab3D,level3DVoxelSetup,threeJS,ThreeJSPlugin,ThreeJSObject,textureSheetSize,textureSheetPadding,setTextureSheetSize,setTextureSheetPadding,textureSheets,TextureSheet,loadSprite,loadTiles,loadAtlas,parseAtlas,spritesReady,textIntl as particleEffectGroups,particleEffectSettings,particleEffectBehaviors,particleEffectShapes,particleEffectSanitize,particleEffectRecolor,particleEffectsAdd,particleEffectsGet,particleEffectsParse,particleEffectsText,particleEffectsLoad,particleEffectShapeTile,particleEffect,particleEffect3D,particleEffectsBuiltIn,particleEffectApply,particleEffectApply3D,particleEffectFromEmitter,particleEffectsAddBehavior};
44
+ `;return s}}function buildMatrix(pos,rotation,scale,matrix=new Matrix4){return matrix4Compose(matrix.m,0,pos,rotation,scale),matrix}function matrix4Compose(m,k,pos,rotation,scale){var cx,sx,cy,sy,cz,scaleX=scale?scale.x:1,scaleY=scale?scale.y:1,scale=scale?scale.z:1;rotation&&(rotation.x||rotation.y||rotation.z)?(cx=cos(rotation.x),sx=sin(rotation.x),cy=cos(rotation.y),sy=sin(rotation.y),cz=cos(rotation.z),rotation=sin(rotation.z),m[k]=(cy*cz+sy*sx*rotation)*scaleX,m[k+1]=cx*rotation*scaleX,m[k+2]=(-sy*cz+cy*sx*rotation)*scaleX,m[k+4]=(-cy*rotation+sy*sx*cz)*scaleY,m[k+5]=cx*cz*scaleY,m[k+6]=(sy*rotation+cy*sx*cz)*scaleY,m[k+8]=sy*cx*scale,m[k+9]=-sx*scale,m[k+10]=cy*cx*scale):(m[k]=scaleX,m[k+1]=0,m[k+2]=0,m[k+4]=0,m[k+5]=scaleY,m[k+6]=0,m[k+8]=0,m[k+9]=0,m[k+10]=scale),m[k+3]=m[k+7]=m[k+11]=0,m[k+15]=1,m[k+12]=pos?pos.x:0,m[k+13]=pos?pos.y:0,m[k+14]=pos?pos.z:0}class Ray3D{constructor(origin=vec3(),direction=vec3(0,0,-1)){this.origin=origin,this.direction=direction}getPosition(distance){return this.origin.add(this.direction.scale(distance))}copy(){return new Ray3D(this.origin.copy(),this.direction.copy())}}const isTurned3D=rotation=>!!rotation&&!!(rotation.x||rotation.y||rotation.z);function boxAxes3D(rotation){rotation=buildMatrix(void 0,rotation).m;return[vec3(rotation[0],rotation[1],rotation[2]),vec3(rotation[4],rotation[5],rotation[6]),vec3(rotation[8],rotation[9],rotation[10])]}function boxLocal3D(point,pos,axes){point=point.subtract(pos);return vec3(point.dot(axes[0]),point.dot(axes[1]),point.dot(axes[2]))}const boxWorld3D=(v,axes)=>axes[0].scale(v.x).add(axes[1].scale(v.y)).add(axes[2].scale(v.z));function isPointInBox3D(point,pos,size,rotation){return isTurned3D(rotation)?isPointInBox3D(boxLocal3D(point,pos,boxAxes3D(rotation)),vec3(),size):abs(point.x-pos.x)<=size.x/2&&abs(point.y-pos.y)<=size.y/2&&abs(point.z-pos.z)<=size.z/2}function isOverlapping3D(posA,sizeA,posB,sizeB=vec3(),rotationA,rotationB){return isTurned3D(rotationA)||isTurned3D(rotationB)?!!collideBoxBox3D(posA,sizeA,posB,sizeB,rotationA,rotationB):(rotationA=posA.subtract(posB),abs(rotationA.x)<(sizeA.x+sizeB.x)/2&&abs(rotationA.y)<(sizeA.y+sizeB.y)/2&&abs(rotationA.z)<(sizeA.z+sizeB.z)/2)}function collideSphereSphere(posA,radiusA,posB,radiusB){posA=posA.subtract(posB),posB=radiusA+radiusB,radiusA=posA.length();if(!(posB<=radiusA))return radiusA?posA.normalize(posB-radiusA):vec3(0,posB,0)}function collideSphereBox(pos,radius,boxPos,boxSize,boxRotation){var distSq;return isTurned3D(boxRotation)?collideSphereOrientedBox3D(pos,radius,boxPos,boxSize,boxAxes3D(boxRotation)):(boxRotation=boxSize.scale(.5),boxSize=vec3(clamp(pos.x,boxPos.x-boxRotation.x,boxPos.x+boxRotation.x),clamp(pos.y,boxPos.y-boxRotation.y,boxPos.y+boxRotation.y),clamp(pos.z,boxPos.z-boxRotation.z,boxPos.z+boxRotation.z)),(distSq=(boxSize=pos.subtract(boxSize)).lengthSquared())?radius*radius<=distSq?void 0:boxSize.normalize(radius-distSq**.5):pushOutAxis3D(boxSize=pos.subtract(boxPos),boxRotation.x-abs(boxSize.x),boxRotation.y-abs(boxSize.y),boxRotation.z-abs(boxSize.z),radius))}function collideSphereInBox(pos,radius,boxPos,boxSize){var x=max(0,boxSize.x/2-radius),y=max(0,boxSize.y/2-radius),boxSize=max(0,boxSize.z/2-radius),radius=vec3(clamp(pos.x,boxPos.x-x,boxPos.x+x)-pos.x,clamp(pos.y,boxPos.y-y,boxPos.y+y)-pos.y,clamp(pos.z,boxPos.z-boxSize,boxPos.z+boxSize)-pos.z);return radius.lengthSquared()?radius:void 0}function pushOutAxis3D(d,penX,penY,penZ,extra=0){var s=v=>0<=v?1:-1;return penX<=penY&&penX<=penZ?vec3(s(d.x)*(penX+extra),0,0):penY<=penZ?vec3(0,s(d.y)*(penY+extra),0):vec3(0,0,s(d.z)*(penZ+extra))}function collideSphereCylinder(pos,radius,cylinderPos,cylinderRadius,cylinderHeight){var cylinderHeight=cylinderHeight/2,offsetX=pos.x-cylinderPos.x,offsetZ=pos.z-cylinderPos.z,offsetY=pos.y-cylinderPos.y,radialDist=(offsetX**2+offsetZ**2)**.5,radialScale=radialDist?min(radialDist,cylinderRadius)/radialDist:0,cylinderPos=vec3(cylinderPos.x+offsetX*radialScale,clamp(pos.y,cylinderPos.y-cylinderHeight,cylinderPos.y+cylinderHeight),cylinderPos.z+offsetZ*radialScale),radialScale=pos.subtract(cylinderPos),pos=radialScale.lengthSquared();return pos?radius*radius<=pos?void 0:radialScale.normalize(radius-pos**.5):(cylinderPos=cylinderRadius-radialDist)<=(radialScale=cylinderHeight-abs(offsetY))?(radialDist?vec3(offsetX/radialDist,0,offsetZ/radialDist):vec3(1,0,0)).scale(cylinderPos+radius):vec3(0,(0<=offsetY?1:-1)*(radialScale+radius),0)}function collideBoxBox3D(posA,sizeA,posB,sizeB,rotationA,rotationB){return isTurned3D(rotationA)||isTurned3D(rotationB)?collideOrientedBoxes3D(posA,sizeA,boxAxes3D(rotationA),posB,sizeB,boxAxes3D(rotationB)):(rotationA=posA.subtract(posB),rotationB=(sizeA.x+sizeB.x)/2-abs(rotationA.x),posA=(sizeA.y+sizeB.y)/2-abs(rotationA.y),posB=(sizeA.z+sizeB.z)/2-abs(rotationA.z),rotationB<=0||posA<=0||posB<=0?void 0:pushOutAxis3D(rotationA,rotationB,posA,posB))}function collideSphereOrientedBox3D(pos,radius,boxPos,boxSize,axes){pos=collideSphereBox(boxLocal3D(pos,boxPos,axes),radius,vec3(),boxSize);return pos&&boxWorld3D(pos,axes)}const BOX_WORLD_AXES=Object.freeze([vec3(1,0,0),vec3(0,1,0),vec3(0,0,1)]);function collideOrientedBoxes3D(posA,sizeA,axesA,posB,sizeB,axesB){const d=posA.subtract(posB),ha=sizeA.scale(.5),hb=sizeB.scale(.5),reach=(h,axes,n)=>h.x*abs(axes[0].dot(n))+h.y*abs(axes[1].dot(n))+h.z*abs(axes[2].dot(n));let push,least=1/0;var separated=(axis,weight)=>{var lengthSquared=axis.lengthSquared();if(!(lengthSquared<1e-12)){var axis=axis.scale(lengthSquared**-.5),lengthSquared=d.dot(axis),overlap=reach(ha,axesA,axis)+reach(hb,axesB,axis)-abs(lengthSquared);if(overlap<=0)return!0;overlap*weight<least&&(least=overlap*weight,push=axis.scale(lengthSquared<0?-overlap:overlap))}return!1};for(const axis of[...axesA,...axesB])if(separated(axis,1))return;for(const a of axesA)for(const b of axesB)if(separated(a.cross(b),1.05))return;return push}function raycastSphere(ray,pos,radius){var{origin:ray,direction}=ray,x=ray.x-pos.x,y=ray.y-pos.y,ray=ray.z-pos.z,pos=x*x+y*y+ray*ray-radius*radius;return pos<0?0:(radius=direction.dot(direction))&&!((y=(x=2*(x*direction.x+y*direction.y+ray*direction.z))*x-4*radius*pos)<0)&&0<=(ray=(-x-y**.5)/(2*radius))?ray:void 0}function raycastPlane(ray,planePos,planeNormal){var{origin:ray,direction}=ray,direction=direction.dot(planeNormal);return abs(direction)<1e-9||(planePos=planePos.subtract(ray).dot(planeNormal)/direction)<0?void 0:planePos}function raycastBox(ray,pos,size,rotation){if(isTurned3D(rotation)){var rotation=boxAxes3D(rotation),d=ray.direction;const direction=vec3(d.dot(rotation[0]),d.dot(rotation[1]),d.dot(rotation[2]));return raycastBox(new Ray3D(boxLocal3D(ray.origin,pos,rotation),direction),vec3(),size)}const{origin,direction}=ray;var d=size.scale(.5),boxMin=pos.subtract(d),boxMax=pos.add(d);let tMin=0,tMax=1/0;for(const axis of"xyz"){const o=origin[axis],d=direction[axis];var mn=boxMin[axis],mx=boxMax[axis];if(d){let t0=(mn-o)/d,t1=(mx-o)/d;if(t0>t1&&([t0,t1]=[t1,t0]),tMin=max(tMin,t0),tMax=min(tMax,t1),tMin>tMax)return}else if(o<mn||mx<o)return}return tMin}let render3D;const RENDER3D_VERTEX_BYTES=36,RENDER3D_INSTANCE_FLOATS=24,RENDER3D_INSTANCE_BYTES=4*RENDER3D_INSTANCE_FLOATS,RENDER3D_MATRIX_FLOATS=16,RENDER3D_COLOR_FLOATS=8,RENDER3D_INSTANCE_ATTRIBS=[[4,4,0],[5,4,16],[6,4,32],[7,4,48],[11,4,64],[12,4,80]],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=11) in vec4 tint;layout(location=12) in vec4 uvRect;",RENDER3D_MAX_LIGHTS=8,RENDER3D_QUAD_UVS=Object.freeze([vec2(0,0),vec2(0,1),vec2(1,0),vec2(1,1)].map(uv=>Object.freeze(uv))),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;let render3DShadowCut;function render3DFaceNormal(a,b,c,d=a){c=c.subtract(a).cross(d.subtract(b));return c.lengthSquared()?c.normalize():RENDER3D_DEFAULT_NORMAL}function render3DQuadStrip(a,b,c,d){return[a,b,d,c]}function render3DQuadValues(v){return isArray(v)?render3DQuadStrip(...v):v}function render3DCanDraw(){return!!render3D.program&&render3D.isRendering}function render3DCaptureBatchState(){var r=render3D,d=r.drawState;return{blend:d.blend,additive:d.additive,depthTest:d.depthTest,depthWrite:d.depthWrite,cullBackFaces:d.cullBackFaces,mirrored:d.mirrored,lighting:d.lighting,emissive:d.emissive,receiveShadow:d.receiveShadow,specular:d.specular,pixelated:d.pixelated,shader:d.shader,normalMap:d.normalMap,normalScale:d.normalScale,shininess:d.shininess,reflectivity:d.reflectivity,emissiveMap:d.emissiveMap,emissiveMapColor:(d.emissiveMapColor||WHITE).copy(),environmentMap:d.environmentMap,version:r.stateVersion}}function render3DApplyBatchState(s){var r=render3D;r.blend=s.blend,r.additive=s.additive,r.depthTest=s.depthTest,r.depthWrite=s.depthWrite,r.cullBackFaces=s.cullBackFaces,r.mirrored=s.mirrored,r.lighting=s.lighting,r.emissive=s.emissive,r.receiveShadow=s.receiveShadow,r.specular=s.specular,r.pixelated=s.pixelated,r.shader=s.shader,r.normalMap=s.normalMap,r.normalScale=s.normalScale,r.shininess=s.shininess,r.reflectivity=s.reflectivity,r.emissiveMap=s.emissiveMap,r.emissiveMapColor=s.emissiveMapColor,r.environmentMap=s.environmentMap}function render3DStateChanged(s){var d,c,r=render3D;if(s.version!==r.stateVersion)return d=r.drawState,c=s.emissiveMapColor,d.blend!==s.blend||d.additive!==s.additive||d.depthTest!==s.depthTest||d.depthWrite!==s.depthWrite||d.cullBackFaces!==s.cullBackFaces||d.mirrored!==s.mirrored||d.lighting!==s.lighting||d.emissive!==s.emissive||d.receiveShadow!==s.receiveShadow||d.specular!==s.specular||d.pixelated!==s.pixelated||d.shader!==s.shader||d.normalMap!==s.normalMap||d.normalScale!==s.normalScale||d.shininess!==s.shininess||d.reflectivity!==s.reflectivity||d.emissiveMap!==s.emissiveMap||d.environmentMap!==s.environmentMap||d.emissiveR!==c.r||d.emissiveG!==c.g||d.emissiveB!==c.b||void(s.version=r.stateVersion)}function render3DSphereVisible(x,y,z,radius){var planes=render3D.shadowPass&&!render3D.depthPass?render3D.shadowPlanes:render3D.frustumPlanes;for(let i=0;i<planes.length;++i){var p=planes[i];if(p[0]*x+p[1]*y+p[2]*z+p[3]<-radius)return!1}return!0}function render3DWithState(fields,fn){var r=render3D,saved={};for(const key in fields)saved[key]=r[key],r[key]=fields[key];try{fn()}finally{Object.assign(r,saved)}}function render3DSize3(size){return isNumber(size)?vec3(size):size}function render3DSize2(size){return isNumber(size)?vec2(size):size}function render3DMatrix(matrix){return matrix instanceof Vector3?buildMatrix(matrix):matrix}function render3DNormalMatrix(matrix){return matrix.copy().invert().transpose()}function render3DAxis(m,i){return vec3(m[i],m[i+1],m[i+2])}function render3DMaxScale(m){return max(m[0]*m[0]+m[1]*m[1]+m[2]*m[2],m[4]*m[4]+m[5]*m[5]+m[6]*m[6],m[8]*m[8]+m[9]*m[9]+m[10]*m[10])**.5}function render3DMaxStretch(m,k=0){return render3DMaxStretchSquared(m,k)**.5}function render3DMaxStretchSquared(m,k=0){var xx=m[k]*m[k]+m[k+1]*m[k+1]+m[k+2]*m[k+2],yy=m[k+4]*m[k+4]+m[k+5]*m[k+5]+m[k+6]*m[k+6],zz=m[k+8]*m[k+8]+m[k+9]*m[k+9]+m[k+10]*m[k+10],xy=abs(m[k]*m[k+4]+m[k+1]*m[k+5]+m[k+2]*m[k+6]),xz=abs(m[k]*m[k+8]+m[k+1]*m[k+9]+m[k+2]*m[k+10]),m=abs(m[k+4]*m[k+8]+m[k+5]*m[k+9]+m[k+6]*m[k+10]);return max(xx+xy+xz,yy+xy+m,zz+xz+m)}function render3DQuadAxes(center,right,up){return[center.subtract(right).add(up),center.subtract(right).subtract(up),center.add(right).add(up),center.add(right).subtract(up)]}function render3DSetObjectState(o){var r=render3D,transparent=o?.transparent,o=o?.drawOwner?function(o){for(const name in RENDER3D_PART_SETTINGS)render3DPartScratch[name]=render3DSetting(o,name);return render3DPartScratch}(o):o,emissive=o?.emissive||0,emissive=(r.lighting=!0,r.emissive=emissive,r.additive=!!o?.additive,r.specular=o?.specular||0,o?.shininess??16),reflectivity=o?.reflectivity||0;r.shininess=emissive,r.reflectivity=reflectivity,r.normalMap=o?.normalMap||void 0,r.normalScale=o?.normalScale??1,r.emissiveMap=o?.emissiveMap||void 0,r.emissiveMapColor=o?.emissiveMapColor||WHITE,r.environmentMap=o?.environment||void 0,r.receiveShadow=!o||o.receiveShadow,r.cullBackFaces=r.mirrored=!1,r.pixelated=!!o?.pixelated,r.shader=o?.shader||void 0,r.depthTest=!0,r.shadowPass&&(r.blend=!!transparent)}function render3DDrawObjects(objects){for(const o of objects)render3DSetObjectState(o),o.render3D();render3DSetObjectState()}function render3DInstanceSlot(mesh,textureInfo,mirrored,unlit=!1){var r=render3D,textureInfo=(mesh.instanceCount&&(mesh.instanceTextureInfo!==textureInfo||mesh.instanceMirrored!==mirrored||mesh.instanceUnlit!==unlit||render3DStateChanged(mesh.instanceState))&&render3DFlushInstances(mesh),mesh.instanceCount||(mesh.instanceTextureInfo=textureInfo,mesh.instanceMirrored=mirrored,mesh.instanceUnlit=unlit,mesh.instanceState=render3DCaptureBatchState(),r.instanceMeshes.push(mesh)),mesh.instanceData),mirrored=mesh.instanceCount++*RENDER3D_INSTANCE_FLOATS;return(!textureInfo||textureInfo.length<mirrored+RENDER3D_INSTANCE_FLOATS)&&(unlit=new Float32Array(max(64*RENDER3D_INSTANCE_FLOATS,textureInfo?2*textureInfo.length:0)),textureInfo&&unlit.set(textureInfo),mesh.instanceData=unlit),mirrored}function render3DFlushInstances(only){var r=render3D,gl=glContext;for(const mesh of only?[only]:r.instanceMeshes){var buffers,state,count=mesh.instanceCount;mesh.instanceCount=0,count&&mesh.buffer&&(buffers=(buffers=r.instanceBuffers)[r.instanceBufferIndex=(r.instanceBufferIndex+1)%buffers.length],gl.bindBuffer(gl.ARRAY_BUFFER,buffers),gl.bufferData(gl.ARRAY_BUFFER,mesh.instanceData,gl.DYNAMIC_DRAW,0,count*RENDER3D_INSTANCE_FLOATS),state=mesh.instanceState,void 0!==mesh.instanceMirrored&&(state.cullBackFaces=!mesh.doubleSided,state.mirrored=mesh.instanceMirrored),mesh.instanceUnlit&&(state.lighting=!1),render3DDrawInstanced(mesh,buffers,count,mesh.instanceTextureInfo,state))}only?(only=r.instanceMeshes.indexOf(only))<0||r.instanceMeshes.splice(only,1):r.instanceMeshes.length=0}function render3DDrawInstanced(mesh,buffer,count,textureInfo,state,colorBuffer){var location,size,offset,gl=glContext,matrixBytes=4*RENDER3D_MATRIX_FLOATS;for([location,size,offset]of RENDER3D_INSTANCE_ATTRIBS){var inColors=colorBuffer&&offset>=matrixBytes,stride=colorBuffer?inColors?4*RENDER3D_COLOR_FLOATS:matrixBytes:RENDER3D_INSTANCE_BYTES;gl.bindBuffer(gl.ARRAY_BUFFER,inColors?colorBuffer:buffer),gl.vertexAttribPointer(location,size,gl.FLOAT,!1,stride,inColors?offset-matrixBytes:offset),gl.enableVertexAttribArray(location),gl.vertexAttribDivisor(location,1)}render3DSetDrawUniforms(RENDER3D_IDENTITY,textureInfo,WHITE,RENDER3D_FULL_UV_RECT,state),render3DBindMesh(mesh),gl.drawElementsInstanced(gl.TRIANGLES,mesh.bufferCount,mesh.indexType,0,count);for(const[location]of RENDER3D_INSTANCE_ATTRIBS)gl.disableVertexAttribArray(location),gl.vertexAttribDivisor(location,0);++drawCount,primitiveCount+=mesh.bufferCount/3*count}function render3DClearInstances(){for(const mesh of render3D.instanceMeshes)mesh.instanceCount=0;render3D.instanceMeshes.length=0}function render3DLayerObjects(after2D){return engineObjects.filter(o=>!o.destroyed&&o instanceof EngineObject3D&&function(o){return!!((o.drawOwner?render3DSetting(o,"renderAfter2D"):o.renderAfter2D)??render3D.renderAfter2D)}(o)===after2D)}function render3DShadowCaster(){var light=render3D.shadowLight;return light&&!light.destroyed&&!light.directional&&0<light.coneAngle&&0<light.radius&&0<light.intensity&&0<light.color.a?light:void 0}const RENDER3D_SHADOW_CONE_MAX=1.35;function render3DLightCone(light){var outer,widest=light===render3DShadowCaster()?RENDER3D_SHADOW_CONE_MAX:PI,widest=light.directional?0:min(light.coneAngle,widest);return 0<widest?(outer=cos(widest),widest=cos(widest*(1-clamp(light.coneSoftness))),widest=1/max(widest-outer,1e-4),[(light=light.getForward3D()).x*widest,light.y*widest,light.z*widest,outer*widest]):[0,0,0,-1]}const render3DCircleCache=new Map;function render3DLookRotation(direction,current){direction=direction.normalize();return direction.lengthSquared()?abs(direction.x)+abs(direction.z)<1e-9?vec3(0<direction.y?PI/2:-PI/2,current.y,0):vec3(Math.asin(clamp(direction.y,-1,1)),atan2(-direction.x,-direction.z),0):current}class Render3DPlugin{constructor(){(render3D=this).drawState={blend:!1,additive:!1,depthTest:!0,depthWrite:!0,cullBackFaces:!1,mirrored:!1,lighting:!0,emissive:0,receiveShadow:!0,specular:0,pixelated:!1,shader:void 0,normalMap:void 0,normalScale:1,shininess:16,reflectivity:0,emissiveMap:void 0,emissiveMapColor:WHITE,environmentMap:void 0,emissiveR:1,emissiveG:1,emissiveB:1},this.stateVersion=0,this.camera=new Camera3D,this.sunDirection=vec3(-.3,1,.5),this.sunColor=WHITE.copy(),this.ambientColor=hsl(0,0,.3),this.ambientGroundColor=void 0,this.fogColor=void 0,this.fogStart=0,this.fogEnd=0,this.gravity=vec3(),this.softShadowHeight=0,this.smoothShading=!1,this.depthTexture=!1,this.shadows=!1,this.soundDefaultRange=100,this.shadowMapSize=1024,this.shadowLight=void 0,this.shadowRange=40,this.shadowCenter=void 0,this.shadowBias=.003,this.shadowSoftness=1,this.onRenderOpaque=void 0,this.onRenderTransparent=void 0,this.sky=void 0,this.skyBox=void 0,this.environment=void 0,this.renderAfter2D=!1,this.sortTransparent=!0,this.frustumCulling=!0,this.instancing=!0,this.mipmaps=!0,this.anisotropy=4,this.boxMesh=buildBox(),this.sphereMesh=buildSphere(1,16,8,!0),this.planeMesh=buildGrid(),this.planeMesh.doubleSided=!1,this.planeMeshDoubleSided=buildGrid(),this.billboardMesh=(new Mesh).addStrip([vec3(-.5,.5,0),vec3(-.5,-.5,0),vec3(.5,.5,0),vec3(.5,-.5,0)],vec3(0,0,1),RENDER3D_QUAD_UVS),this.billboardMesh.doubleSided=!0,this.isRendering=!1,this.shadowPass=!1,this.viewMatrix=new Matrix4,this.projectionMatrix=new Matrix4,this.viewProjection=new Matrix4,this.shadowMatrix=new Matrix4,this.gelAxes=[1,0,0,1],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.frustumPlanes=[],this.shadowPlanes=[],this.program=void 0,this.currentProgram=void 0,this.lightCount=0,this.shadowShader=void 0,this.vao=void 0,this.cubeCaptures=new Set,this.capturingCube=void 0,this.captureFramebuffer=void 0,this.captureDepth=void 0,this.captureDepthSize=0,this.skyBoxProgram=void 0,this.skyBoxVao=void 0,this.whiteTexture=void 0,this.samplers=new Map,this.samplerKey=void 0,this.shadowTexture=void 0,this.shadowFramebuffer=void 0,this.shadowTextureSize=0,this.cameraDepthTexture=void 0,this.cameraDepthFramebuffer=void 0,this.cameraDepthWidth=0,this.cameraDepthHeight=0,this.depthPass=!1,this.contextGeneration=0,this.uniforms=new WeakMap,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.lightCones=new Float32Array(4*RENDER3D_MAX_LIGHTS),this.shadowLightIndex=-1,this.shadowDepthBias=0,this.streamBuffer=void 0,this.instanceBuffers=[],this.instanceBufferIndex=0,this.instanceMeshes=[],this.attribValues=[],this.streamData=new ArrayBuffer(32768*RENDER3D_VERTEX_BYTES),this.streamFloats=new Float32Array(this.streamData),this.streamInts=new Uint32Array(this.streamData),this.streamCount=0,this.streamTileInfo=void 0,this.streamState=void 0,this.streamUnlit=!1,this.capture=void 0,this.transparentQueue=void 0,render3DInitGL(),engineAddPlugin(void 0,render3DRender,render3DContextLost,render3DContextRestored,render3DPreRender)}get lighting(){return this.drawState.lighting}set lighting(v){var d=this.drawState;d.lighting!==v&&(d.lighting=v,++this.stateVersion)}get emissive(){return this.drawState.emissive}set emissive(v){var d=this.drawState;d.emissive!==v&&(d.emissive=v,++this.stateVersion)}get additive(){return this.drawState.additive}set additive(v){var d=this.drawState;d.additive!==v&&(d.additive=v,++this.stateVersion)}get depthTest(){return this.drawState.depthTest}set depthTest(v){var d=this.drawState;d.depthTest!==v&&(d.depthTest=v,++this.stateVersion)}get depthWrite(){return this.drawState.depthWrite}set depthWrite(v){var d=this.drawState;d.depthWrite!==v&&(d.depthWrite=v,++this.stateVersion)}get cullBackFaces(){return this.drawState.cullBackFaces}set cullBackFaces(v){var d=this.drawState;d.cullBackFaces!==v&&(d.cullBackFaces=v,++this.stateVersion)}get mirrored(){return this.drawState.mirrored}set mirrored(v){var d=this.drawState;d.mirrored!==v&&(d.mirrored=v,++this.stateVersion)}get specular(){return this.drawState.specular}set specular(v){var d=this.drawState;d.specular!==v&&(d.specular=v,++this.stateVersion)}get shininess(){return this.drawState.shininess}set shininess(v){var d=this.drawState;d.shininess!==v&&(d.shininess=v,++this.stateVersion)}get normalMap(){return this.drawState.normalMap}set normalMap(v){var d=this.drawState;d.normalMap!==v&&(d.normalMap=v,++this.stateVersion)}get normalScale(){return this.drawState.normalScale}set normalScale(v){var d=this.drawState;d.normalScale!==v&&(d.normalScale=v,++this.stateVersion)}get reflectivity(){return this.drawState.reflectivity}set reflectivity(v){var d=this.drawState;d.reflectivity!==v&&(d.reflectivity=v,++this.stateVersion)}get emissiveMap(){return this.drawState.emissiveMap}set emissiveMap(v){var d=this.drawState;d.emissiveMap!==v&&(d.emissiveMap=v,++this.stateVersion)}get environmentMap(){return this.drawState.environmentMap}set environmentMap(v){var d=this.drawState;d.environmentMap!==v&&(d.environmentMap=v,++this.stateVersion)}get emissiveMapColor(){return this.drawState.emissiveMapColor}set emissiveMapColor(v){var d=this.drawState,c=v||WHITE;d.emissiveMapColor=v,c.r===d.emissiveR&&c.g===d.emissiveG&&c.b===d.emissiveB||(d.emissiveR=c.r,d.emissiveG=c.g,d.emissiveB=c.b,++this.stateVersion)}get shader(){return this.drawState.shader}set shader(v){var d=this.drawState;d.shader!==v&&(d.shader=v,++this.stateVersion)}get receiveShadow(){return this.drawState.receiveShadow}set receiveShadow(v){var d=this.drawState;d.receiveShadow!==v&&(d.receiveShadow=v,++this.stateVersion)}get pixelated(){return this.drawState.pixelated}set pixelated(v){var d=this.drawState;d.pixelated!==v&&(d.pixelated=v,++this.stateVersion)}get blend(){return this.drawState.blend}set blend(v){var d=this.drawState;d.blend!==v&&(d.blend=v,++this.stateVersion)}updateMatrices(aspect=mainCanvasSize.y?mainCanvasSize.x/mainCanvasSize.y:1){var camera=this.camera,cameraMatrix=(camera.align2D&&camera.update2D(),camera.getMatrix()),camera=(this.viewMatrix=cameraMatrix.copy().invert(),this.projectionMatrix=camera.getProjectionMatrix(aspect),this.viewProjection=this.projectionMatrix.copy().multiply(this.viewMatrix),cameraMatrix.m);this.cameraRight=render3DAxis(camera,0),this.cameraUp=render3DAxis(camera,4),this.cameraBack=render3DAxis(camera,8),this.cameraForward=this.cameraBack.scale(-1),this.frustumPlanes=render3DFrustumPlanes(this.viewProjection)}worldToClip(pos){var m=this.viewProjection.m,w=m[3]*pos.x+m[7]*pos.y+m[11]*pos.z+m[15],z=(m[2]*pos.x+m[6]*pos.y+m[10]*pos.z+m[14])/w;if(!(w<=0||z<-1))return vec3((m[0]*pos.x+m[4]*pos.y+m[8]*pos.z+m[12])/w,(m[1]*pos.x+m[5]*pos.y+m[9]*pos.z+m[13])/w,z)}worldToScreen(pos,canvasSize=mainCanvasSize){pos=this.worldToClip(pos);if(pos)return vec2((pos.x+1)/2*canvasSize.x,(1-pos.y)/2*canvasSize.y)}screenToRay(screenPos,canvasSize=mainCanvasSize){var width=canvasSize.x||1,canvasSize=canvasSize.y||1,aspect=width/canvasSize,camera=this.camera,width=(this.updateMatrices(aspect),screenPos.x/width*2-1),screenPos=1-screenPos.y/canvasSize*2,canvasSize=camera.orthographic?camera.orthographic/2:tan(camera.fov/2),width=this.cameraRight.scale(width*canvasSize*aspect).add(this.cameraUp.scale(screenPos*canvasSize));return camera.orthographic?new Ray3D(camera.pos.add(width).add(this.cameraForward.scale(camera.near)),this.cameraForward.copy()):new Ray3D(camera.pos.copy(),this.cameraForward.add(width).normalize())}screenToGround(screenPos,groundHeight=0,canvasSize=mainCanvasSize){screenPos=this.screenToRay(screenPos,canvasSize),canvasSize=raycastPlane(screenPos,vec3(0,groundHeight,0),RENDER3D_DEFAULT_NORMAL);return void 0===canvasSize?void 0:screenPos.getPosition(canvasSize)}pick(from,objects=engineObjects){var ray=from instanceof Ray3D?from:this.screenToRay(from);let nearest;for(const o of objects){var distance=render3DRaycastObject(ray,o);void 0!==distance&&(!nearest||distance<nearest.distance)&&(nearest={object:o,distance:distance})}return nearest}playSound(sound,pos3D,volume=1,pitch=1,randomnessScale=1,loop=!1,paused=!1){var pos3D=pos3D.subtract(this.camera.pos),range=sound.rangeIsDefault?this.soundDefaultRange:sound.range;if(range){var distance=pos3D.length();if(range<=distance)return;var taperRange=range*sound.taper;taperRange<distance&&(volume*=percent(distance,range,taperRange))}distance=sound.play(void 0,volume,pitch,randomnessScale,loop,paused);return distance?.setPan(pos3D.normalize().dot(this.cameraRight)),distance}playSoundLoop(sound,pos3D,volume=1,pitch=1,randomnessScale=1){return this.playSound(sound,pos3D,volume,pitch,randomnessScale,!0)}isSphereVisible(center,radius){return render3DSphereVisible(center.x,center.y,center.z,radius)}drawMesh(mesh,matrix=RENDER3D_IDENTITY,tileInfo,color=WHITE){if(matrix=render3DMatrix(matrix),this.capture)this.capture.combine(mesh,matrix,color);else{if(this.transparentQueue){const m=matrix.copy(),c=color.copy();return this.queueTransparent(m.getTranslation(),()=>this.drawMesh(mesh,m,tileInfo,c))}if(render3DCanDraw()&&(!this.shadowPass||this.lighting)&&(render3DMeshUpload(mesh),mesh.bufferCount)){const m=matrix.m;var cullBackFaces,mirrored;this.frustumCulling&&!render3DSphereVisible(m[12],m[13],m[14],mesh.radius*render3DMaxStretch(m))||(!this.blend&&this.depthTest&&(mesh.instanced??this.instancing)?function(mesh,matrix,tileInfo,color){var k=render3DInstanceSlot(mesh,render3DTextureOf(tileInfo),matrix.determinant()<0),matrix=((mesh=mesh.instanceData).set(matrix.m,k),mesh[k+16]=color.r,mesh[k+17]=color.g,mesh[k+18]=color.b,mesh[k+19]=color.a,render3DGetTileUVs(tileInfo));mesh[k+20]=matrix.x,mesh[k+21]=matrix.y,mesh[k+22]=matrix.w,mesh[k+23]=matrix.h}(mesh,matrix,tileInfo,color):(cullBackFaces=this.cullBackFaces,mirrored=this.mirrored,this.cullBackFaces=!mesh.doubleSided,this.mirrored=matrix.determinant()<0,this.flush(),render3DFlushBeforeOverlay(),render3DSetDrawUniforms(matrix,tileInfo,color),render3DBindMesh(mesh),glContext.drawElements(glContext.TRIANGLES,mesh.bufferCount,mesh.indexType,0),++drawCount,primitiveCount+=mesh.bufferCount/3,this.cullBackFaces=cullBackFaces,this.mirrored=mirrored))}}}drawStrip(points,normals,uvs,colors,tileInfo){if(this.capture)this.capture.addStrip(points,normals,uvs,colors);else{if(this.transparentQueue){let x=0,y=0,z=0;for(const p of points)x+=p.x,y+=p.y,z+=p.z;return this.queueTransparent(vec3(x,y,z).scale(1/points.length),()=>this.drawStrip(points,normals,uvs,colors,tileInfo))}var n=points.length,count=render3DStripCount(n),uvRect=render3DBeginStrip(count,tileInfo);if(uvRect){var floats=this.streamFloats,ints=this.streamInts,normalArray=isArray(normals),uvArray=isArray(uvs),colorArray=isArray(colors),rgba=colorArray?0:(colors||WHITE).rgbaInt();for(let k=0;k<count;++k){var i=render3DStripIndex(k,n),p=points[i],uv=uvArray?uvs[i]:uvs||RENDER3D_DEFAULT_UV;render3DWriteVertex(floats,ints,9*this.streamCount++,p.x,p.y,p.z,normalArray?normals[i]:normals||RENDER3D_DEFAULT_NORMAL,uvRect.x+uv.x*uvRect.w,uvRect.y+uv.y*uvRect.h,colorArray?colors[i].rgbaInt():rgba)}}}}drawStripUnlit(points,normals,uvs,colors,tileInfo){render3DWithState({lighting:!1},()=>this.drawStrip(points,normals,uvs,colors,tileInfo))}flush(){var gl;this.streamCount&&render3DCanDraw()&&(gl=glContext,this.streamUnlit&&(this.streamState.lighting=!1),render3DSetDrawUniforms(RENDER3D_IDENTITY,this.streamTileInfo,WHITE,RENDER3D_FULL_UV_RECT,this.streamState),render3DBindVertexBuffer(this.streamBuffer),gl.bufferSubData(gl.ARRAY_BUFFER,0,this.streamFloats,0,9*this.streamCount),gl.drawArrays(gl.TRIANGLE_STRIP,0,this.streamCount),++drawCount,primitiveCount+=this.streamCount,this.streamCount=0)}bake(drawFunction){this.flush();var mesh=this.capture=new Mesh;try{drawFunction()}finally{this.capture=void 0}return mesh}renderStages(objects,isDefault=!0){var opaque=[],transparent=[];for(const o of objects)(o.transparent||render3DSetting(o,"additive")?transparent:opaque).push(o);isDefault&&(this.skyBox&&this.skyBox!==this.capturingCube||this.sky)&&this.drawSky(),this.blend=!1,this.depthWrite=!0;var m,byOrder=(a,b)=>(a.drawOwner?render3DSetting(a,"renderOrder"):a.renderOrder)-(b.drawOwner?render3DSetting(b,"renderOrder"):b.renderOrder);opaque.sort(byOrder),transparent.sort(byOrder),render3DDrawObjects(opaque),isDefault&&this.onRenderOpaque?.(),this.flush(),render3DFlushInstances(),this.blend=!0,this.depthWrite=!1,this.transparentQueue=this.sortTransparent?[]:void 0;try{render3DDrawObjects(transparent);for(const o of objects)o.softShadow&&(m=render3DObjectMatrix(o),this.drawSoftShadow(m.getTranslation(),o.softShadow*render3DMaxScale(m.m),this.softShadowHeight));isDefault&&this.onRenderTransparent?.()}finally{this.flushTransparentQueue()}isDefault&&this.capturingCube,this.flush(),render3DSetObjectState(),this.blend=!1,this.depthWrite=!0}queueTransparent(pos,draw){var f,c;this.transparentQueue?(f=this.cameraForward,c=this.camera.pos,pos=(pos.x-c.x)*f.x+(pos.y-c.y)*f.y+(pos.z-c.z)*f.z,this.transparentQueue.push({distance:pos,state:render3DCaptureBatchState(),draw:draw})):draw()}flushTransparentQueue(){var queue=this.transparentQueue;if(queue){this.transparentQueue=void 0,queue.sort((a,b)=>b.distance-a.distance);var state=render3DCaptureBatchState();try{for(const item of queue)render3DApplyBatchState(item.state),item.draw()}finally{render3DApplyBatchState(state)}}}drawSky(){if(this.flush(),(!this.skyBox||this.skyBox===this.capturingCube||!function(cube){var gl=glContext,r=render3D;if(r.skyBoxProgram||=glCreateProgram("#version 300 es\nprecision highp float;out vec2 v;void main(){v=vec2(gl_VertexID&1,gl_VertexID>>1)*4.-1.;gl_Position=vec4(v,0,1);}","#version 300 es\nprecision highp float;uniform mat4 inverseViewProj;uniform samplerCube sky;in vec2 v;out vec4 o;void main(){vec4 a=inverseViewProj*vec4(v,-1,1),b=inverseViewProj*vec4(v,0,1);o=vec4(texture(sky,b.xyz/b.w-a.xyz/a.w).rgb,1);}"),glFailedPrograms.has(r.skyBoxProgram))return;r.skyBoxVao||=gl.createVertexArray();var depthTest=gl.isEnabled(gl.DEPTH_TEST),blend=gl.isEnabled(gl.BLEND),cull=gl.isEnabled(gl.CULL_FACE),depthWrite=gl.getParameter(gl.DEPTH_WRITEMASK),cube=(gl.useProgram(r.skyBoxProgram),r.currentProgram=void 0,gl.activeTexture(gl.TEXTURE6),gl.bindTexture(gl.TEXTURE_CUBE_MAP,render3DCubeTexture(cube)),gl.bindSampler(6,null),gl.uniform1i(glUniformLocation(r.skyBoxProgram,"sky"),6),r.viewMatrix.copy());return cube.m[12]=cube.m[13]=cube.m[14]=0,gl.uniformMatrix4fv(glUniformLocation(r.skyBoxProgram,"inverseViewProj"),!1,r.projectionMatrix.copy().multiply(cube).invert().m),gl.disable(gl.DEPTH_TEST),gl.disable(gl.BLEND),gl.disable(gl.CULL_FACE),gl.depthMask(!1),gl.bindVertexArray(r.skyBoxVao),gl.drawArrays(gl.TRIANGLES,0,3),gl.bindVertexArray(r.vao),gl.bindTexture(gl.TEXTURE_CUBE_MAP,null),gl.activeTexture(gl.TEXTURE0),depthTest&&gl.enable(gl.DEPTH_TEST),blend&&gl.enable(gl.BLEND),cull&&gl.enable(gl.CULL_FACE),gl.depthMask(depthWrite),1}(this.skyBox))&&this.sky){var{near,far}=this.camera;const radius=far==1/0?1e4*near:(near+far)/2;render3DWithState({lighting:!1,blend:!1,depthTest:!1,depthWrite:!1,fogEnd:0,shader:void 0,emissiveMap:void 0},()=>this.drawMesh(this.sky,buildMatrix(this.camera.pos,void 0,vec3(radius))))}}updateShadowMatrix(){var caster=render3DShadowCaster();if(caster){var pos=caster.getWorldPos3D(),forward=caster.getForward3D(),up=.99<abs(forward.y)?vec3(0,0,1):vec3(0,1,0),far=caster.radius,near=min(max(far/500,.02),far/2),fov=2*min(caster.coneAngle,RENDER3D_SHADOW_CONE_MAX)+.1;const view=Matrix4.lookAt(pos,pos.add(forward),up);this.shadowMatrix=Matrix4.perspective(fov,1,near,far).multiply(view.copy().invert());pos=view.m,forward=render3DObjectMatrix(caster).m,up=vec3(forward[0],forward[1],forward[2]).normalize(),fov=vec3(forward[4],forward[5],forward[6]).normalize(),caster=vec3(pos[0],pos[1],pos[2]),forward=vec3(pos[4],pos[5],pos[6]);this.gelAxes=[up.dot(caster),up.dot(forward),fov.dot(caster),fov.dot(forward)],this.shadowPlanes=render3DFrustumPlanes(this.shadowMatrix),void(this.shadowDepthBias=this.shadowBias*far*far*near/(far-near))}else{this.shadowDepthBias=this.shadowBias;pos=0<this.shadowRange?this.shadowRange:1,up=pos/2,caster=this.sunDirection.normalize(),fov=this.shadowCenter||this.camera.pos.add(this.cameraForward.scale(.8*up));const view=Matrix4.lookAt(fov.add(caster.scale(pos)),fov).invert();forward=pos/(this.shadowTextureSize||this.shadowMapSize),far=view.m;far[12]=round(far[12]/forward)*forward,far[13]=round(far[13]/forward)*forward,this.shadowMatrix=Matrix4.orthographic(-up,up,-up,up,0,2*pos).multiply(view),this.shadowPlanes=render3DFrustumPlanes(this.shadowMatrix)}}setSky(topColor=hsl(.6,.8,.55),horizonColor=hsl(.6,1,.9),bottomColor=horizonColor,ambient=.5){return this.sky?.dispose(),this.sky=buildSky(topColor,horizonColor,bottomColor),this.fogColor=horizonColor.copy(),this.ambientColor=topColor.scale(ambient,1),this.ambientGroundColor=bottomColor.scale(ambient,1),this.sky}setFog(fogStart,fogEnd,fogColor){this.fogStart=fogStart,this.fogEnd=fogEnd,fogColor&&(this.fogColor=fogColor.copy())}drawBox(pos,size=1,color=WHITE,rotation){this.drawMesh(this.boxMesh,buildMatrix(pos,rotation,render3DSize3(size)),void 0,color)}drawSphere(pos,size=1,color=WHITE){this.drawMesh(this.sphereMesh,buildMatrix(pos,void 0,vec3(size)),void 0,color)}drawBillboard(pos,size=vec2(1),tileInfo,color=WHITE,angle=0,upright=!1){if(this.capture)return this.drawStripUnlit(function(pos,size,angle,upright){var size=render3DBillboardAxes(size,angle,upright),angle=size[0],upright=size[1],rz=size[2],ux=size[3],uy=size[4],size=size[5];return[vec3(pos.x-angle+ux,pos.y-upright+uy,pos.z-rz+size),vec3(pos.x-angle-ux,pos.y-upright-uy,pos.z-rz-size),vec3(pos.x+angle+ux,pos.y+upright+uy,pos.z+rz+size),vec3(pos.x+angle-ux,pos.y+upright-uy,pos.z+rz-size)]}(pos,size,angle,upright),this.cameraBack,RENDER3D_QUAD_UVS,color.copy(),tileInfo);if(this.transparentQueue){const p=pos.copy(),s=size.copy(),c=color.copy();return this.queueTransparent(p,()=>this.drawBillboard(p,s,tileInfo,c,angle,upright))}var unlit=!this.shadowPass;if(!this.blend&&this.depthTest&&this.instancing){if(!render3DCanDraw()||this.shadowPass&&!this.lighting)return;if(this.frustumCulling&&!render3DSphereVisible(pos.x,pos.y,pos.z,hypot(size.x,size.y)/2))return;var quad=this.billboardMesh,cb=this.cameraBack,uv=render3DGetTileUVs(tileInfo);render3DMeshUpload(quad);const a=render3DBillboardAxes(size,angle,upright);var k=render3DInstanceSlot(quad,render3DTextureOf(tileInfo),!1,unlit),quad=quad.instanceData;quad[k]=2*a[0],quad[k+1]=2*a[1],quad[k+2]=2*a[2],quad[k+3]=0,quad[k+4]=2*a[3],quad[k+5]=2*a[4],quad[k+6]=2*a[5],quad[k+7]=0,quad[k+8]=cb.x,quad[k+9]=cb.y,quad[k+10]=cb.z,quad[k+11]=0,quad[k+12]=pos.x,quad[k+13]=pos.y,quad[k+14]=pos.z,quad[k+15]=1,quad[k+16]=color.r,quad[k+17]=color.g,quad[k+18]=color.b,quad[k+19]=color.a,quad[k+20]=uv.x,quad[k+21]=uv.y,quad[k+22]=uv.w,void(quad[k+23]=uv.h)}else{cb=render3DBeginStrip(6,tileInfo,unlit);if(cb){const a=render3DBillboardAxes(size,angle,upright);var quad=a[0],k=a[1],uv=a[2],unlit=a[3],size=a[4],uz=a[5],x=pos.x,y=pos.y,pos=pos.z,n=this.cameraBack,color=color.rgbaInt(),u0=cb.x,v0=cb.y,u1=u0+cb.w,cb=v0+cb.h,floats=this.streamFloats,ints=this.streamInts,j=9*this.streamCount;this.streamCount+=6,render3DWriteVertex(floats,ints,j,x-quad+unlit,y-k+size,pos-uv+uz,n,u0,v0,color),render3DWriteVertex(floats,ints,j+=9,x-quad+unlit,y-k+size,pos-uv+uz,n,u0,v0,color),render3DWriteVertex(floats,ints,j+=9,x-quad-unlit,y-k-size,pos-uv-uz,n,u0,cb,color),render3DWriteVertex(floats,ints,j+=9,x+quad+unlit,y+k+size,pos+uv+uz,n,u1,v0,color),render3DWriteVertex(floats,ints,j+=9,x+quad-unlit,y+k-size,pos+uv-uz,n,u1,cb,color),render3DWriteVertex(floats,ints,j+=9,x+quad-unlit,y+k-size,pos+uv-uz,n,u1,cb,color)}}}drawQuad(a,b,c,d,tileInfo,color=WHITE){this.transparentQueue&&(a=a.copy(),b=b.copy(),c=c.copy(),d=d.copy(),color=isArray(color)?color.map(k=>k.copy()):color.copy()),this.drawStrip([a,b,d,c],render3DFaceNormal(a,b,c,d),RENDER3D_QUAD_UVS,render3DQuadValues(color),tileInfo)}drawTriangle(a,b,c,color=WHITE){this.transparentQueue&&(a=a.copy(),b=b.copy(),c=c.copy(),color=color.copy()),this.drawStrip([a,b,c],render3DFaceNormal(a,b,c),void 0,color)}drawLine(posA,posB,width=.1,color=WHITE){this.drawRibbon([posA,posB],width,void 0,color)}drawRibbon(points,width=.1,tileInfo,color=WHITE,side){var count=points.length;const strip=[],uvs=tileInfo?[]:void 0,colors=[],forward=this.cameraForward;let across=vec3(1,0,0);var loop=2<count&&points[0].distanceSquared(points[count-1])<1e-12;for(let i=0;i<count;++i){var p=points[i],w=isArray(width)?width[i]:width;let c=isArray(color)?color[i]:color;this.transparentQueue&&(c=c.copy());var s=side&&(isArray(side)?side[i]:side),next=points[i<count-1?i+1:loop?1:i],last=points[0<i?i-1:loop?count-2:i],s=s||next.subtract(last).cross(forward),next=(across=1e-12<s.lengthSquared()?s.normalize():across).scale(w/2);strip.push(p.add(next),p.subtract(next)),uvs?.push(vec2(i/(count-1),0),vec2(i/(count-1),1)),colors.push(c,c)}render3DWithState({lighting:!1,cullBackFaces:!1},()=>this.drawStrip(strip,forward.scale(-1),uvs,colors,tileInfo))}drawSoftDisc(pos,size=1,color=WHITE,normal=this.cameraBack,sides=16){if(render3DAssertBlending(),this.transparentQueue&&!this.capture){const p=pos.copy(),c=color.copy(),n=normal.copy();return this.queueTransparent(p,()=>this.drawSoftDisc(p,size,c,n,sides))}const n=normal.normalize();const u=(abs(n.y)<.9?vec3(0,1,0):vec3(1,0,0)).cross(n).normalize(),w=u.cross(n);render3DDrawSoftDisc(size/2,color,sides,n,(c,s,r)=>vec3(pos.x+(u.x*c+w.x*s)*r,pos.y+(u.y*c+w.y*s)*r,pos.z+(u.z*c+w.z*s)*r))}drawSoftShadow(pos,size=1,floorHeight=0,color=RENDER3D_SHADOW_COLOR,lift=.02){render3DAssertBlending();const heightMap=floorHeight,height=isNumber(floorHeight)?()=>floorHeight:heightMap.getHeight?(x,z)=>heightMap.getHeight(x,z):floorHeight;if(this.transparentQueue&&!this.capture){const p=pos.copy(),c=color.copy();return this.queueTransparent(vec3(p.x,height(p.x,p.z)+lift,p.z),()=>this.drawSoftShadow(p,size,floorHeight,c,lift))}render3DDrawSoftDisc(size/2,color,16,RENDER3D_DEFAULT_NORMAL,(c,s,r)=>{c=pos.x+c*r,s=pos.z+s*r;return vec3(c,height(c,s)+lift,s)})}}function render3DAssertBlending(){render3D}function render3DDrawSoftDisc(radius,color,sides,normal,pointAt){var alpha=[1,.9,.7,0],circle=function(sides){sides|=0;let circle=render3DCircleCache.get(sides);if(!circle){circle=new Float32Array(2*sides+2);for(let i=0;i<=sides;++i){var a=i/sides*2*PI;circle[2*i]=cos(a),circle[2*i+1]=sin(a)}render3DCircleCache.set(sides,circle)}return circle}(sides);for(let k=0;k<3;++k){var points=[],colors=[],c0=color.withAlpha(color.a*alpha[k]),c1=color.withAlpha(color.a*alpha[k+1]),r0=radius*k/3,r1=radius*(k+1)/3;for(let i=0;i<=sides;++i){var c=circle[2*i],s=circle[2*i+1];points.push(pointAt(c,s,r1),pointAt(c,s,r0)),colors.push(c1,c0)}render3D.drawStripUnlit(points,normal,void 0,colors)}}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,this.align2DZ=0}getMatrix(){return buildMatrix(this.pos,this.rotation)}getViewMatrix(){return this.getMatrix().invert()}getProjectionMatrix(aspect){var h=this.orthographic/2,w=h*aspect;return h?Matrix4.orthographic(-w,w,-h,h,this.near,this.far):Matrix4.perspective(this.fov,aspect,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(target){this.rotation=render3DLookRotation(target.subtract(this.pos),this.rotation)}orbit(target,distance,yaw,pitch=.5){var r=cos(pitch)*distance;this.pos=target.add(vec3(sin(yaw)*r,sin(pitch)*distance,cos(yaw)*r)),this.rotation=render3DLookRotation(target.subtract(this.pos),vec3(0,yaw,0))}follow(target,offset,percent=1){this.pos=this.pos.lerp(target.add(offset),percent),this.lookAt(target)}update2D(canvasHeight=mainCanvasSize.y){var canvasHeight=canvasHeight/2/cameraScale,distance=canvasHeight/tan(this.fov/2);this.orthographic&&=2*canvasHeight,this.pos=vec3(cameraPos.x,cameraPos.y,this.align2DZ+distance),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;vec3 c0=m0.xyz,c1=m1.xyz,c2=m2.xyz;N=mat3(c0/max(dot(c0,c0),1e-20),c1/max(dot(c1,c1),1e-20),c2/max(dot(c2,c2),1e-20))*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 ambientGroundColor ambientGround.rgb\n#define lightCount extraLightCount\n#define lights extraLights\n#define lightColors extraLightColors\n";function render3DFragmentSource(fragmentCode){var hasNormal=/\bmainNormal\s*\(/.test(fragmentCode||"");return"#version 300 es\nprecision highp float;uniform vec4 lightDir,lightColor,ambientFog,ambientGround,fogColor,shadowParams;uniform vec4 extraLights["+RENDER3D_MAX_LIGHTS+"],extraLightColors["+RENDER3D_MAX_LIGHTS+"];uniform vec4 extraLightCones["+RENDER3D_MAX_LIGHTS+"];uniform int extraLightCount;uniform vec3 cameraPos;uniform vec4 materialParams,emissiveTint,skyTop,skyHorizon,skyBottom,gelAxes;uniform sampler2D tex,normalTex,emissiveTex,gelTex;uniform samplerCube envMap;uniform vec4 envParams;uniform bool premultipliedTexture;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.||S.w<=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/(S.w*S.w);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.;}vec3 gel(){if(S.w<=0.)return vec3(1);vec2 g=S.xy/S.w,u=vec2(dot(gelAxes.xy,g),dot(gelAxes.zw,g))*.5+.5;return texture(gelTex,vec2(u.x,1.-u.y)).rgb;}vec3 normalMapNormal(vec3 n){vec3 p1=dFdx(P),p2=dFdy(P);vec2 t1=dFdx(T),t2=dFdy(T);vec3 a=cross(p2,n),b=cross(n,p1),u=a*t1.x+b*t2.x,v=a*t1.y+b*t2.y;float k=max(dot(u,u),dot(v,v));if(k<=0.)return n;vec3 m=texture(normalTex,T).xyz*2.-1.;m.xy*=materialParams.x;return normalize((u*m.x-v*m.y)*inversesqrt(k)+n*m.z);}"+(fragmentCode?"void mainImage(out vec4,vec2);":"")+(hasNormal?"void mainNormal(inout vec3);":"")+"void main(){"+(fragmentCode?"vec4 t;mainImage(t,T);":"vec4 t=texture(tex,T);")+"if(premultipliedTexture&&t.a>0.)t.rgb/=t.a;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;if(materialParams.x!=0.)n=normalMapNormal(n);"+(hasNormal?"mainNormal(n);n=normalize(n);":"")+"float nl=dot(n,-lightDir.xyz);float sh=shadow(),s=shadowParams.x<1.5?sh:1.;int si=int(shadowParams.x+.5)-2;vec3 l=(ambientGround.a>0.?mix(ambientGround.rgb,ambientFog.rgb,n.y*.5+.5):ambientFog.rgb)+lightColor.rgb*max(nl,0.)*s;vec3 eye=lightColor.a>0.?normalize(cameraPos-P):vec3(0),sp=vec3(0);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);v/=max(d,1e-6);float ln=dot(n,v);vec3 lc=extraLightColors[i].rgb*extraLightColors[i].a*a*a;vec4 K=extraLightCones[i];float k=clamp(dot(K.xyz,-v)-K.w,0.,1.);lc*=k*k*(3.-2.*k);if(i==si)lc*=sh*gel();l+=lc*max(0.,ln);if(lightColor.a>0.)sp+=lc*pow(max(dot(reflect(-v,n),eye),0.),materialParams.y)*step(0.,ln);}c.rgb*=l*(1.-e)+e;if(lightColor.a>0.){vec3 r=reflect(lightDir.xyz,n);c.rgb+=lightColor.rgb*pow(max(dot(r,eye),0.),materialParams.y)*lightColor.a*step(0.,nl)*s*(1.-e)+sp*lightColor.a*(1.-e);}if(materialParams.z>0.){vec3 w=normalize(P-cameraPos),q=reflect(w,n);float f=materialParams.z+(1.-materialParams.z)*pow(clamp(1.-dot(n,-w),0.,1.),5.);c.rgb=mix(c.rgb,envParams.x>0.?textureLod(envMap,q,clamp(max(envParams.z-.5*log2(materialParams.y+2.),envParams.w+log2(max(max(length(dFdx(q)),length(dFdy(q))),1e-9))),0.,envParams.y)).rgb:q.y>0.?mix(skyHorizon.rgb,skyTop.rgb,q.y):mix(skyHorizon.rgb,skyBottom.rgb,-q.y),f);}}else c.rgb*=e;if(emissiveTint.a>0.)c.rgb+=texture(emissiveTex,T).rgb*emissiveTint.rgb;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));}if(shadowParams.w>0.&&t.a<.5)discard;o=vec4(c.rgb,shadowParams.w>0.?1.:c.a);}"+(fragmentCode?"\n"+RENDER3D_SNIPPET_NAMES+fragmentCode+"\n":"")}function render3DUseProgram(program){var gl=glContext,r=render3D,c=(gl.useProgram(r.currentProgram=program),r.uniformValues={},gl.uniformMatrix4fv(render3DUniform("viewProj"),!1,r.viewProjection.m),gl.uniformMatrix4fv(render3DUniform("lightViewProj"),!1,r.shadowMatrix.m),gl.uniform1i(render3DUniform("tex"),0),gl.uniform1i(render3DUniform("shadowMap"),1),gl.uniform1i(render3DUniform("normalTex"),2),gl.uniform1i(render3DUniform("emissiveTex"),3),gl.uniform1i(render3DUniform("gelTex"),4),gl.uniform1i(render3DUniform("envMap"),5),gl.uniform4fv(render3DUniform("gelAxes"),r.gelAxes),r.camera.pos);gl.uniform3f(render3DUniform("cameraPos"),c.x,c.y,c.z),gl.uniform1i(render3DUniform("extraLightCount"),r.lightCount),r.lightCount&&(gl.uniform4fv(render3DUniform("extraLights"),r.lightPositions,0,4*r.lightCount),gl.uniform4fv(render3DUniform("extraLightColors"),r.lightColors,0,4*r.lightCount),gl.uniform4fv(render3DUniform("extraLightCones"),r.lightCones,0,4*r.lightCount)),program!==r.program&&(gl.uniform1f(render3DUniform("iTime"),time),c=r.capturingCube?.size,gl.uniform3f(render3DUniform("iResolution"),c||glCanvas.width,c||glCanvas.height,1))}function render3DInitGL(){if(!headlessMode)if(glEnable&&glContext){var gl=glContext,r=render3D;if(glFlush(),r.uniforms=new WeakMap,r.uniformValues={},render3DShadowCut=void 0,r.attribValues=[],r.program=glCreateProgram(RENDER3D_VERTEX_SOURCE,render3DFragmentSource()),r.shadowShader=glCreateProgram("#version 300 es\nprecision highp float;uniform mat4 viewProj;"+RENDER3D_VERTEX_INPUTS+"out vec2 T;out float A;void main(){T=uvRect.xy+t*uvRect.zw;A=c.a*tint.a;gl_Position=viewProj*mat4(m0,m1,m2,m3)*vec4(p,1.);}","#version 300 es\nprecision highp float;uniform sampler2D tex;uniform float cut;in vec2 T;in float A;void main(){if(texture(tex,T).a*mix(1.,A,cut)<.5)discard;}"),glFailedPrograms.has(r.program)||glFailedPrograms.has(r.shadowShader))console.error("Render3DPlugin: its shaders did not build on this device, so nothing 3D is drawn"),r.program=r.shadowShader=void 0;else{r.vao=gl.createVertexArray(),gl.bindVertexArray(r.vao);for(var[location]of RENDER3D_ATTRIBS)gl.enableVertexAttribArray(location);r.streamBuffer=gl.createBuffer(),gl.bindBuffer(gl.ARRAY_BUFFER,r.streamBuffer),gl.bufferData(gl.ARRAY_BUFFER,r.streamData.byteLength,gl.DYNAMIC_DRAW),r.streamCount=0,r.instanceBuffers=[gl.createBuffer(),gl.createBuffer(),gl.createBuffer()],r.whiteTexture=glCreateTexture(),r.samplers=new Map,r.samplerKey=void 0,render3DUpdateShadowMap(1),gl.bindBuffer(gl.ARRAY_BUFFER,glArrayBuffer),glSetInstancedMode(!0)}}else console.warn(glDeviceFailed?"Render3DPlugin: this device can not draw WebGL, so nothing 3D is drawn":"Render3DPlugin: WebGL not enabled, construct the plugin in gameInit with glEnable set")}class CubeMap{constructor(size,faces){this.size=size,this.faces=faces,this.capturePos=void 0,this.glTexture=void 0,this.contextGeneration=-1}capture(pos3D){this.capturePos=pos3D.copy(),render3D.cubeCaptures.add(this)}dispose(){this.glTexture&&glContext?.deleteTexture(this.glTexture),this.glTexture=void 0,render3D?.cubeCaptures.delete(this)}}function makeCubeMap(size,colorOf){var faces=[];for(let face=0;face<6;++face){var data=new Uint8Array(size*size*4);for(let y=0;y<size;++y)for(let x=0;x<size;++x){var color=colorOf(function(face,s,t){face=0===face?vec3(1,-t,-s):1===face?vec3(-1,-t,s):2===face?vec3(s,1,t):3===face?vec3(s,-1,-t):4===face?vec3(s,-t,1):vec3(-s,-t,-1);return face.normalize()}(face,(x+.5)/size*2-1,(y+.5)/size*2-1)),i=4*(x+y*size);data[i]=255*clamp(color.r)+.5|0,data[1+i]=255*clamp(color.g)+.5|0,data[2+i]=255*clamp(color.b)+.5|0,data[3+i]=255*clamp(color.a)+.5|0}faces.push(data)}return new CubeMap(size,faces)}async function loadCubeMap(sources){const images=await Promise.all(sources.map(src=>new Promise((resolve,reject)=>{const image=new Image;image.crossOrigin="anonymous",image.onload=()=>resolve(image),image.onerror=()=>reject(new Error("loadCubeMap could not load "+src)),image.src=src}))),size=images[0].width;if(images.every(image=>image.width===size&&image.height===size))return sources=[1,0,2,3,4,5].map(i=>{var context=createCanvasContext(size);return context.scale(-1,1),context.drawImage(images[i],-size,0),context.canvas}),new CubeMap(size,sources);throw new Error("loadCubeMap needs six square images of one size")}function render3DCubeTexture(cube){const gl=glContext;if(cube&&gl){if(cube.glTexture&&cube.contextGeneration===render3D.contextGeneration)return cube.glTexture;var texture=cube.glTexture=gl.createTexture();if(cube.contextGeneration=render3D.contextGeneration,gl.bindTexture(gl.TEXTURE_CUBE_MAP,texture),gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL,!1),cube.faces)cube.faces.forEach((face,i)=>face instanceof Uint8Array?gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X+i,0,gl.RGBA,cube.size,cube.size,0,gl.RGBA,gl.UNSIGNED_BYTE,face):gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X+i,0,gl.RGBA,gl.RGBA,gl.UNSIGNED_BYTE,face));else{cube.size=min(cube.size,gl.getParameter(gl.MAX_CUBE_MAP_TEXTURE_SIZE),gl.getParameter(gl.MAX_RENDERBUFFER_SIZE));for(let i=0;i<6;++i)gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X+i,0,gl.RGBA,cube.size,cube.size,0,gl.RGBA,gl.UNSIGNED_BYTE,null);cube.capturePos&&render3D.cubeCaptures.add(cube)}return gl.texParameteri(gl.TEXTURE_CUBE_MAP,gl.TEXTURE_MIN_FILTER,gl.LINEAR_MIPMAP_LINEAR),gl.texParameteri(gl.TEXTURE_CUBE_MAP,gl.TEXTURE_MAG_FILTER,gl.LINEAR),gl.texParameteri(gl.TEXTURE_CUBE_MAP,gl.TEXTURE_WRAP_S,gl.CLAMP_TO_EDGE),gl.texParameteri(gl.TEXTURE_CUBE_MAP,gl.TEXTURE_WRAP_T,gl.CLAMP_TO_EDGE),gl.generateMipmap(gl.TEXTURE_CUBE_MAP),texture}}function render3DCaptureCubes(objects){var gl=glContext,r=render3D,camera=r.camera,captures=[...r.cubeCaptures],view=(r.cubeCaptures.clear(),r.captureFramebuffer||(r.captureFramebuffer=gl.createFramebuffer(),r.captureDepth=gl.createRenderbuffer(),r.captureDepthSize=0),new Camera3D);view.fov=PI/2,view.near=!camera.orthographic&&0<camera.near?camera.near:.1,view.far=camera.far;let faceMatrix=new Matrix4;view.getMatrix=()=>faceMatrix.copy(),gl.bindFramebuffer(gl.FRAMEBUFFER,r.captureFramebuffer);try{for(const cube of captures){var texture=render3DCubeTexture(cube),size=cube.size;r.cubeCaptures.delete(cube),r.captureDepthSize!==size&&(gl.bindRenderbuffer(gl.RENDERBUFFER,r.captureDepth),gl.renderbufferStorage(gl.RENDERBUFFER,gl.DEPTH_COMPONENT24,size,size),gl.framebufferRenderbuffer(gl.FRAMEBUFFER,gl.DEPTH_ATTACHMENT,gl.RENDERBUFFER,r.captureDepth),r.captureDepthSize=size),r.capturingCube=cube,gl.activeTexture(gl.TEXTURE0+5),gl.bindTexture(gl.TEXTURE_CUBE_MAP,null),gl.activeTexture(gl.TEXTURE0),render3DBoundEnvironment=null,gl.viewport(0,0,size,size),view.pos=cube.capturePos,r.camera=view;for(let face=0;face<6;++face){gl.framebufferTexture2D(gl.FRAMEBUFFER,gl.COLOR_ATTACHMENT0,gl.TEXTURE_CUBE_MAP_POSITIVE_X+face,texture,0),faceMatrix=function(face,pos){var[face,up,forward]=[[vec3(0,0,-1),vec3(0,-1,0),vec3(1,0,0)],[vec3(0,0,1),vec3(0,-1,0),vec3(-1,0,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(1,0,0),vec3(0,-1,0),vec3(0,0,1)],[vec3(-1,0,0),vec3(0,-1,0),vec3(0,0,-1)]][face];return new Matrix4([face.x,face.y,face.z,0,up.x,up.y,up.z,0,-forward.x,-forward.y,-forward.z,0,pos.x,pos.y,pos.z,1])}(face,cube.capturePos),r.updateMatrices(1),(face<2||3<face)&&(r.cameraRight=r.cameraRight.scale(-1),r.cameraUp=r.cameraUp.scale(-1));var c=canvasClearColor;gl.clearColor(c.r,c.g,c.b,1),gl.depthMask(!0),gl.clear(gl.COLOR_BUFFER_BIT|gl.DEPTH_BUFFER_BIT),render3DUseProgram(r.program),r.renderStages(objects,!0)}gl.framebufferTexture2D(gl.FRAMEBUFFER,gl.COLOR_ATTACHMENT0,gl.TEXTURE_2D,null,0),gl.bindTexture(gl.TEXTURE_CUBE_MAP,texture),gl.generateMipmap(gl.TEXTURE_CUBE_MAP),gl.bindTexture(gl.TEXTURE_CUBE_MAP,null)}}finally{r.capturingCube=void 0,r.camera=camera,r.updateMatrices(),gl.bindFramebuffer(gl.FRAMEBUFFER,null),gl.viewport(0,0,glCanvas.width,glCanvas.height),gl.activeTexture(gl.TEXTURE0+5),gl.bindTexture(gl.TEXTURE_CUBE_MAP,render3DCubeTexture(r.environment)||null),gl.activeTexture(gl.TEXTURE0),render3DBoundEnvironment=r.environment||null}}function render3DContextLost(){var r=render3D;r.program=r.currentProgram=r.shadowShader=r.vao=r.streamBuffer=r.whiteTexture=void 0,r.skyBoxProgram=r.skyBoxVao=r.captureFramebuffer=r.captureDepth=void 0,r.captureDepthSize=0;for(const shader of glShaderObjects)shader.program3D=void 0;r.lightCount=0,r.instanceBuffers=[],r.samplers=new Map,r.samplerKey=void 0,render3DClearInstances(),r.shadowFramebuffer=r.shadowTexture=void 0,r.shadowTextureSize=0,r.cameraDepthFramebuffer=r.cameraDepthTexture=void 0,r.cameraDepthWidth=r.cameraDepthHeight=0,r.streamCount=0,++r.contextGeneration}function render3DContextRestored(){render3DInitGL()}function render3DUniform(name,program=render3D.currentProgram){var u=render3D.uniforms;let cache=u.get(program);return cache||u.set(program,cache={}),cache[name]??=glContext.getUniformLocation(program,name)}function render3DAttrib4f(location,x,y,z,w){var last=render3D.attribValues[location]||=[NaN,NaN,NaN,NaN];last[0]===x&&last[1]===y&&last[2]===z&&last[3]===w||(last[0]=x,last[1]=y,last[2]=z,last[3]=w,glContext.vertexAttrib4f(location,x,y,z,w))}function render3DBindTexture(tileInfo,state=render3D,unit=0){var gl=glContext,r=render3D,tileInfo=render3DTextureOf(tileInfo),texture=tileInfo?.glTexture||r.whiteTexture;unit&&gl.activeTexture(gl.TEXTURE0+unit),gl.bindTexture(gl.TEXTURE_2D,texture),texture===r.whiteTexture||!r.mipmaps&&!state.pixelated?gl.bindSampler(unit,null):(gl.bindSampler(unit,function(wrap,pixelated,texturePixelated=tilesPixelated){var gl=glContext,r=render3D,[wrap,wrapT]=glWrapModes(wrap),key=1e5*wrap+4*wrapT+(pixelated?1:0)+(texturePixelated?2:0),sampler=r.samplers.get(key);return sampler||(sampler=gl.createSampler(),gl.samplerParameteri(sampler,gl.TEXTURE_MAG_FILTER,pixelated||texturePixelated?gl.NEAREST:gl.LINEAR),gl.samplerParameteri(sampler,gl.TEXTURE_MIN_FILTER,pixelated?gl.NEAREST:texturePixelated?gl.NEAREST_MIPMAP_LINEAR:gl.LINEAR_MIPMAP_LINEAR),gl.samplerParameteri(sampler,gl.TEXTURE_WRAP_S,wrap),gl.samplerParameteri(sampler,gl.TEXTURE_WRAP_T,wrapT),(texturePixelated=gl.getExtension("EXT_texture_filter_anisotropic"))&&!pixelated&&(wrap=gl.getParameter(texturePixelated.MAX_TEXTURE_MAX_ANISOTROPY_EXT),gl.samplerParameterf(sampler,texturePixelated.TEXTURE_MAX_ANISOTROPY_EXT,clamp(r.anisotropy,1,wrap))),r.samplers.set(key,sampler)),sampler}(tileInfo?.wrap,state.pixelated,tileInfo?.pixelated??tilesPixelated)),glUpdateMipmaps(texture),state.pixelated||glMipmappedTextures.has(texture)||(glMipmappedTextures.add(texture),gl.generateMipmap(gl.TEXTURE_2D))),unit&&gl.activeTexture(gl.TEXTURE0)}function render3DSetMaterialUniforms(state){var r=render3D,loaded=map=>render3DTextureOf(map)?.glTexture?map:void 0,normalMap=state.normalScale?loaded(state.normalMap):void 0,loaded=loaded(state.emissiveMap),ec=(render3DUniform4f("materialParams",normalMap?state.normalScale:0,max(state.shininess,.001),state.reflectivity,0),state.emissiveMapColor||WHITE);if(loaded?render3DUniform4f("emissiveTint",ec.r,ec.g,ec.b,1):render3DUniform4f("emissiveTint",0,0,0,0),render3DBindMap(2,normalMap,state),render3DBindMap(3,loaded,state),0<state.reflectivity){let environment=state.environmentMap||r.environment;environment===r.capturingCube&&(environment=void 0),ec=environment,normalMap=glContext,ec&&ec!==render3DBoundEnvironment&&normalMap&&(normalMap.activeTexture(normalMap.TEXTURE0+5),normalMap.bindTexture(normalMap.TEXTURE_CUBE_MAP,render3DCubeTexture(ec)||null),normalMap.activeTexture(normalMap.TEXTURE0),render3DBoundEnvironment=ec);loaded=environment?environment.size:1,state=(render3DUniform4f("envParams",environment?1:0,max(0,log2(loaded)-2),log2(.45*loaded),log2(2*loaded/PI)),r.sky&&render3DSkyColors.get(r.sky)),normalMap=r.ambientColor,ec=r.ambientGroundColor||normalMap,loaded=state?state[0]:normalMap,r=state?state[2]:ec;render3DUniform4f("skyTop",loaded.r,loaded.g,loaded.b,1),render3DUniform4f("skyBottom",r.r,r.g,r.b,1),state?render3DUniform4f("skyHorizon",state[1].r,state[1].g,state[1].b,1):render3DUniform4f("skyHorizon",(normalMap.r+ec.r)/2,(normalMap.g+ec.g)/2,(normalMap.b+ec.b)/2,1)}}let render3DBoundEnvironment;let render3DBoundMaps=[];function render3DBindMap(unit,map,state){var bound=render3DBoundMaps,pixelated=!!map&&state.pixelated;bound[unit]===map&&bound[unit+2]===pixelated||(bound[unit]=map,bound[unit+2]=pixelated,render3DBindTexture(map,state,unit))}function render3DUniform4f(name,x,y,z,w){var last=render3D.uniformValues[name]||=[NaN,NaN,NaN,NaN];last[0]===x&&last[1]===y&&last[2]===z&&last[3]===w||(last[0]=x,last[1]=y,last[2]=z,last[3]=w,glContext.uniform4f(render3DUniform(name),x,y,z,w))}function render3DBindVertexBuffer(buffer){var gl=glContext;gl.bindBuffer(gl.ARRAY_BUFFER,buffer);for(const a of RENDER3D_ATTRIBS)gl.vertexAttribPointer(a[0],a[1],a[2],a[3],RENDER3D_VERTEX_BYTES,a[4])}function render3DBindMesh(mesh){render3DBindVertexBuffer(mesh.buffer),glContext.bindBuffer(glContext.ELEMENT_ARRAY_BUFFER,mesh.indexBuffer)}function render3DTextureOf(tileInfo){return tileInfo instanceof TileInfo?tileInfo.textureInfo:tileInfo}const render3DLevel=[];function render3DLevelLeave(o){o=render3DLevel.indexOf(o);0<=o&&render3DLevel.splice(o,1)}const render3DTileUVRect={x:0,y:0,w:1,h:1},render3DWholeTiles=new WeakSet;function render3DGetTileUVs(tileInfo){var inv,rect,bleedX,bleedY;return tileInfo instanceof TileInfo&&tileInfo.textureInfo&&!render3DWholeTiles.has(tileInfo)?(inv=tileInfo.textureInfo.sizeInverse,rect=render3DTileUVRect,bleedX=inv.x*tileInfo.bleed,bleedY=inv.y*tileInfo.bleed,rect.x=tileInfo.pos.x*inv.x+bleedX,rect.y=tileInfo.pos.y*inv.y+bleedY,rect.w=tileInfo.size.x*inv.x-2*bleedX,rect.h=tileInfo.size.y*inv.y-2*bleedY,rect):RENDER3D_FULL_UV_RECT}function render3DSetDrawUniforms(matrix,tileInfo,tint,uvRect,state=render3D){var fc,gl=glContext,r=render3D;uvRect||=render3DGetTileUVs(tileInfo),function(m,tint,uvRect){var gl=glContext;gl.vertexAttrib4f(4,m[0],m[1],m[2],m[3]),gl.vertexAttrib4f(5,m[4],m[5],m[6],m[7]),gl.vertexAttrib4f(6,m[8],m[9],m[10],m[11]),gl.vertexAttrib4f(7,m[12],m[13],m[14],m[15]),render3DAttrib4f(11,tint.r,tint.g,tint.b,tint.a),render3DAttrib4f(12,uvRect.x,uvRect.y,uvRect.w,uvRect.h)}(matrix.m,tint,uvRect),render3DBindTexture(tileInfo,state),r.shadowPass?(matrix=state.blend?1:0,render3DShadowCut!==matrix&&glContext.uniform1f(render3DUniform("cut",r.shadowShader),render3DShadowCut=matrix)):((uvRect=state.shader?((tint=state.shader).program3D||glShaderTrack(tint),tint=tint.program3D||=glCreateProgram(RENDER3D_VERTEX_SOURCE,render3DFragmentSource(tint.fragmentCode)),glFailedPrograms.has(tint)?render3D.program:tint):r.program)!==r.currentProgram&&render3DUseProgram(uvRect),state.blend?(gl.enable(gl.BLEND),matrix=state.additive?gl.ONE:gl.ONE_MINUS_SRC_ALPHA,gl.blendFuncSeparate(gl.SRC_ALPHA,matrix,gl.ONE,matrix)):gl.disable(gl.BLEND),state.depthTest?gl.enable(gl.DEPTH_TEST):gl.disable(gl.DEPTH_TEST),gl.depthMask(state.depthWrite),state.cullBackFaces?gl.enable(gl.CULL_FACE):gl.disable(gl.CULL_FACE),gl.frontFace(state.mirrored?gl.CCW:gl.CW),uvRect=-((tint=r.sunDirection).length()||1),matrix=r.sunColor,gl=r.ambientColor,fc=r.fogColor||canvasClearColor,render3DUniform4f("lightDir",tint.x/uvRect,tint.y/uvRect,tint.z/uvRect,state.lighting?state.emissive:1),render3DUniform4f("lightColor",matrix.r,matrix.g,matrix.b,state.specular),render3DUniform4f("ambientFog",gl.r,gl.g,gl.b,r.fogEnd),(tint=r.ambientGroundColor)?render3DUniform4f("ambientGround",tint.r,tint.g,tint.b,1):render3DUniform4f("ambientGround",0,0,0,0),render3DUniform4f("fogColor",fc.r,fc.g,fc.b,r.fogEnd?min(r.fogStart,r.fogEnd-.001):r.fogStart),uvRect=state.blend?state.additive?-1:0:1,render3DUniform4f("shadowParams",(matrix=r.shadows&&r.passIsDefault&&state.receiveShadow?1:0)&&0<=r.shadowLightIndex?2+r.shadowLightIndex:matrix,r.shadowDepthBias,r.shadowSoftness/r.shadowTextureSize,uvRect),gl=render3DTextureOf(tileInfo),tint="premultipliedTexture",fc=+!(!gl?.glTexture||!glPremultipliedTextures.has(gl.glTexture)),(matrix=render3D.uniformValues)[tint]!==fc&&(matrix[tint]=fc,glContext.uniform1i(render3DUniform(tint),fc)),render3DSetMaterialUniforms(state))}function render3DFrustumPlanes(matrix){var m=matrix.m,planes=[];for(let i=0;i<3;++i)for(const sign of[1,-1]){var p=[m[3]+sign*m[i],m[7]+sign*m[4+i],m[11]+sign*m[8+i],m[15]+sign*m[12+i]];const l=hypot(p[0],p[1],p[2])||1;planes.push(p.map(v=>v/l))}return planes}function render3DPreRender(){var r=render3D;r.updateMatrices(),render3DRenderPass(r.shadowMapDrawn=!1)}function render3DRender(){render3DRenderPass(!0)}function render3DRenderPass(after2D){const gl=glContext,r=render3D;if(r.program&&glEnable){!function(){var r=render3D,key=tilesPixelated+" "+r.anisotropy;if(r.samplerKey!==key){r.samplerKey=key;for(const sampler of r.samplers.values())glContext.deleteSampler(sampler);r.samplers.clear()}}();var isDefault=after2D===!!r.renderAfter2D,after2D=render3DLayerObjects(after2D);if(isDefault||after2D.length){r.passIsDefault=isDefault,glFlush(),r.streamCount=0,r.capture=r.transparentQueue=void 0,render3DClearInstances();var lights=function(){var lights=engineObjects.filter(o=>!o.destroyed&&o instanceof Light3D&&0<o.color.a&&0<o.intensity&&(o.directional||0<o.radius));if(lights.length>RENDER3D_MAX_LIGHTS){const cameraPos=render3D.camera.pos,distances=new Map;for(const light of lights)distances.set(light,light.directional?-1:light.getWorldPos3D().distanceSquared(cameraPos));lights.sort((a,b)=>distances.get(a)-distances.get(b));var caster=render3DShadowCaster();caster&&lights.indexOf(caster)>=RENDER3D_MAX_LIGHTS&&(lights[RENDER3D_MAX_LIGHTS-1]=caster),lights.length=RENDER3D_MAX_LIGHTS}return lights}(),caster=(r.lightCount=lights.length,render3DShadowCaster());r.shadowLightIndex=caster?lights.indexOf(caster):-1;const positions=r.lightPositions,colors=r.lightColors;lights.forEach((light,i)=>{var p=light.directional?light.getWorldPos3D().normalize():light.getWorldPos3D(),c=light.color,i=4*i;positions[i]=p.x,positions[1+i]=p.y,positions[2+i]=p.z,positions[3+i]=light.directional?-1:max(0,light.radius),colors[i]=c.r,colors[1+i]=c.g,colors[2+i]=c.b,colors[3+i]=c.a*light.intensity,r.lightCones.set(render3DLightCone(light),i)}),gl.bindVertexArray(r.vao),gl.frontFace(gl.CW),gl.activeTexture(gl.TEXTURE1),gl.bindTexture(gl.TEXTURE_2D,r.shadowTexture||null),render3DSetMapUnits(r.whiteTexture);caster=render3DTextureOf(render3DShadowCaster()?.gel)?.glTexture;caster&&(gl.activeTexture(gl.TEXTURE4),gl.bindTexture(gl.TEXTURE_2D,caster),gl.activeTexture(gl.TEXTURE0)),gl.depthMask(!0),gl.clear(gl.DEPTH_BUFFER_BIT),r.isRendering=!0;try{r.shadows&&!r.shadowMapDrawn&&(!function(){var gl=glContext,r=render3D;render3DUpdateShadowMap(0|r.shadowMapSize),r.updateShadowMatrix(),gl.activeTexture(gl.TEXTURE1),gl.bindTexture(gl.TEXTURE_2D,null),gl.activeTexture(gl.TEXTURE0),gl.bindFramebuffer(gl.FRAMEBUFFER,r.shadowFramebuffer),gl.viewport(0,0,r.shadowTextureSize,r.shadowTextureSize),gl.clear(gl.DEPTH_BUFFER_BIT),gl.useProgram(r.shadowShader),gl.uniformMatrix4fv(render3DUniform("viewProj",r.shadowShader),!1,r.shadowMatrix.m),gl.enable(gl.DEPTH_TEST),gl.depthMask(!0),gl.disable(gl.BLEND),gl.disable(gl.CULL_FACE),r.shadowPass=!0;try{render3DDrawObjects(render3DLayerObjects(!!r.renderAfter2D).filter(o=>render3DSetting(o,"castShadow")&&!render3DSetting(o,"additive")&&(!o.transparent||o.tileInfo))),r.onRenderOpaque?.(),r.flush(),render3DFlushInstances()}finally{r.shadowPass=!1,gl.bindFramebuffer(gl.FRAMEBUFFER,null),gl.viewport(0,0,glCanvas.width,glCanvas.height),gl.activeTexture(gl.TEXTURE1),gl.bindTexture(gl.TEXTURE_2D,r.shadowTexture),gl.activeTexture(gl.TEXTURE0)}}(),r.shadowMapDrawn=!0),r.depthTexture&&isDefault&&function(){var gl=glContext,r=render3D,width=glCanvas.width,height=glCanvas.height;(function(width,height){var gl=glContext,r=render3D;var texture,framebuffer;r.cameraDepthTexture&&r.cameraDepthWidth===width&&r.cameraDepthHeight===height||(r.cameraDepthTexture&&gl.deleteTexture(r.cameraDepthTexture),r.cameraDepthFramebuffer&&gl.deleteFramebuffer(r.cameraDepthFramebuffer),texture=r.cameraDepthTexture=gl.createTexture(),gl.bindTexture(gl.TEXTURE_2D,texture),gl.texImage2D(gl.TEXTURE_2D,0,gl.DEPTH_COMPONENT24,width,height,0,gl.DEPTH_COMPONENT,gl.UNSIGNED_INT,null),gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MIN_FILTER,gl.NEAREST),gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MAG_FILTER,gl.NEAREST),gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_WRAP_S,gl.CLAMP_TO_EDGE),gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_WRAP_T,gl.CLAMP_TO_EDGE),gl.bindTexture(gl.TEXTURE_2D,null),framebuffer=r.cameraDepthFramebuffer=gl.createFramebuffer(),gl.bindFramebuffer(gl.FRAMEBUFFER,framebuffer),gl.framebufferTexture2D(gl.FRAMEBUFFER,gl.DEPTH_ATTACHMENT,gl.TEXTURE_2D,texture,0),gl.drawBuffers([gl.NONE]),gl.readBuffer(gl.NONE),gl.bindFramebuffer(gl.FRAMEBUFFER,null),r.cameraDepthWidth=width,r.cameraDepthHeight=height)})(width,height),gl.bindFramebuffer(gl.FRAMEBUFFER,r.cameraDepthFramebuffer),gl.viewport(0,0,width,height),gl.clear(gl.DEPTH_BUFFER_BIT),gl.useProgram(r.shadowShader),gl.uniformMatrix4fv(render3DUniform("viewProj",r.shadowShader),!1,r.viewProjection.m),gl.enable(gl.DEPTH_TEST),gl.depthMask(!0),gl.disable(gl.BLEND),gl.disable(gl.CULL_FACE),r.shadowPass=r.depthPass=!0;try{render3DDrawObjects(render3DLayerObjects(!!r.renderAfter2D).filter(o=>!render3DSetting(o,"additive")&&(!o.transparent||o.tileInfo))),r.onRenderOpaque?.(),r.flush(),render3DFlushInstances()}finally{r.shadowPass=r.depthPass=!1,gl.bindFramebuffer(gl.FRAMEBUFFER,null),gl.viewport(0,0,width,height)}}(),isDefault&&r.cubeCaptures.size&&render3DCaptureCubes(after2D),render3DUseProgram(r.program),r.renderStages(after2D,isDefault)}finally{r.isRendering=!1,r.currentProgram=void 0,r.streamCount=0,r.capture=r.transparentQueue=void 0,gl.disable(gl.DEPTH_TEST),gl.disable(gl.CULL_FACE),gl.depthMask(!0),gl.frontFace(gl.CCW),gl.bindSampler(0,null),render3DSetMapUnits(null),glActiveTexture&&gl.bindTexture(gl.TEXTURE_2D,glActiveTexture),gl.bindBuffer(gl.ARRAY_BUFFER,glArrayBuffer),glSetInstancedMode(!0)}}}}function render3DSetMapUnits(texture){var gl=glContext;for(const unit of[2,3,4])gl.activeTexture(gl.TEXTURE0+unit),gl.bindTexture(gl.TEXTURE_2D,texture),gl.bindSampler(unit,null);gl.activeTexture(gl.TEXTURE0+5),gl.bindTexture(gl.TEXTURE_CUBE_MAP,texture&&render3DCubeTexture(render3D.environment)||null),gl.bindSampler(5,null),render3DBoundEnvironment=texture&&render3D.environment||null,gl.activeTexture(gl.TEXTURE0),render3DBoundMaps=[]}function render3DUpdateShadowMap(size){var texture,framebuffer,gl=glContext,r=render3D;size=glClampTextureSize(size),r.shadowTexture&&r.shadowTextureSize===size||(r.shadowTexture&&gl.deleteTexture(r.shadowTexture),r.shadowFramebuffer&&gl.deleteFramebuffer(r.shadowFramebuffer),texture=r.shadowTexture=gl.createTexture(),gl.activeTexture(gl.TEXTURE1),gl.bindTexture(gl.TEXTURE_2D,texture),gl.texImage2D(gl.TEXTURE_2D,0,gl.DEPTH_COMPONENT24,size,size,0,gl.DEPTH_COMPONENT,gl.UNSIGNED_INT,null),gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MIN_FILTER,gl.LINEAR),gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MAG_FILTER,gl.LINEAR),gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_WRAP_S,gl.CLAMP_TO_EDGE),gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_WRAP_T,gl.CLAMP_TO_EDGE),gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_COMPARE_MODE,gl.COMPARE_REF_TO_TEXTURE),gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_COMPARE_FUNC,gl.LEQUAL),gl.activeTexture(gl.TEXTURE0),framebuffer=r.shadowFramebuffer=gl.createFramebuffer(),gl.bindFramebuffer(gl.FRAMEBUFFER,framebuffer),gl.framebufferTexture2D(gl.FRAMEBUFFER,gl.DEPTH_ATTACHMENT,gl.TEXTURE_2D,texture,0),gl.drawBuffers([gl.NONE]),gl.readBuffer(gl.NONE),gl.bindFramebuffer(gl.FRAMEBUFFER,null),r.shadowTextureSize=size)}function render3DBeginStrip(count,tileInfo,unlit=!1){var textureInfo,r=render3D;if(render3DCanDraw()&&((!r.shadowPass||r.lighting)&&!(32768<count)))return textureInfo=render3DTextureOf(tileInfo),r.streamCount&&(textureInfo!==r.streamTileInfo||r.streamUnlit!==unlit||render3DStateChanged(r.streamState)||32768<r.streamCount+count)&&r.flush(),render3DFlushBeforeOverlay(),r.streamCount||(r.streamState=render3DCaptureBatchState()),r.streamUnlit=unlit,r.streamTileInfo=textureInfo,render3DGetTileUVs(tileInfo)}function render3DFlushBeforeOverlay(){var r=render3D;r.depthTest||r.shadowPass||!r.instanceMeshes.length||(r.flush(),render3DFlushInstances())}function render3DMeshUpload(mesh){mesh.buffer&&!mesh.dirty&&mesh.contextGeneration===render3D.contextGeneration||mesh.upload()}function render3DWorldSize(o,m){o=o.size3D;return vec3(o.x*hypot(m[0],m[1],m[2]),o.y*hypot(m[4],m[5],m[6]),o.z*hypot(m[8],m[9],m[10]))}function render3DSlopeNormal(heightFunction,x,z,ex,ez,halfX,halfZ){var x0=max(x-ex,-halfX),ex=min(x+ex,halfX),halfX=max(z-ez,-halfZ),ez=min(z+ez,halfZ),halfZ=(heightFunction(ex,z)-heightFunction(x0,z))/(ex-x0||1),z=(heightFunction(x,ez)-heightFunction(x,halfX))/(ez-halfX||1),ex=1/hypot(halfZ,1,z);return vec3(-halfZ*ex,ex,-z*ex)}const render3DBillboardAxesScratch=new Float64Array(6);function render3DBillboardAxes(size,angle,upright){var r=render3D.cameraRight,u=render3D.cameraUp;let rx=r.x,ry=r.y,rz=r.z,ux=u.x,uy=u.y,uz=u.z;upright&&(r=hypot(rx,rz),rx=r?rx/r:1,ry=0,rz=r?rz/r:0,ux=0,uy=1,uz=0);u=cos(angle),upright=sin(angle),r=size.x/2,angle=size.y/2,size=render3DBillboardAxesScratch;return size[0]=(rx*u+ux*upright)*r,size[1]=(ry*u+uy*upright)*r,size[2]=(rz*u+uz*upright)*r,size[3]=(ux*u-rx*upright)*angle,size[4]=(uy*u-ry*upright)*angle,size[5]=(uz*u-rz*upright)*angle,size}function render3DStripCount(n){return n+2+(1&n)}function render3DStripIndex(k,n){return k<1?0:k<=n?k-1:n-1}function render3DWriteVertex(floats,ints,j,x,y,z,n,u,v,rgba){floats[j]=x,floats[j+1]=y,floats[j+2]=z,floats[j+3]=n.x,floats[j+4]=n.y,floats[j+5]=n.z,floats[j+6]=u,floats[j+7]=v,ints[j+8]=rgba}function render3DFlipTriangles(indices){for(let t=0;t<indices.length;t+=3){var b=indices[t+1];indices[t+1]=indices[t+2],indices[t+2]=b}return indices}function render3DStripTriangles(count,remap,place){var indices=[];for(let i=2;i<count;++i){var a=1&i?i-1:i-2,b=1&i?i-2:i-1;place[a]!=place[b]&&place[b]!=place[i]&&place[a]!=place[i]&&indices.push(remap[a],remap[b],remap[i])}return indices}function render3DMeshVertexData(mesh,vertices,data=new ArrayBuffer(vertices.length*RENDER3D_VERTEX_BYTES)){var count=vertices.length,floats=new Float32Array(data),ints=new Uint32Array(data);let r=0,x0=1/0,y0=1/0,z0=1/0,x1=-1/0,y1=-1/0,z1=-1/0;for(let j=0;j<count;++j){var i=vertices[j],p=mesh.points[i],uv=mesh.uvs[i]||RENDER3D_DEFAULT_UV,x=p.x,y=p.y,p=p.z;render3DWriteVertex(floats,ints,9*j,x,y,p,mesh.normals[i]||RENDER3D_DEFAULT_NORMAL,uv.x,uv.y,(mesh.colors[i]||WHITE).rgbaInt()),r=max(r,x*x+y*y+p*p),x0=min(x0,x),y0=min(y0,y),z0=min(z0,p),x1=max(x1,x),y1=max(y1,y),z1=max(z1,p)}return mesh.radius=r**.5,mesh.bounds=count?{min:vec3(x0,y0,z0),max:vec3(x1,y1,z1)}:{min:vec3(),max:vec3()},data}function render3DPolygonStrip(points){var strip=[points[0]];for(let i=1,j=points.length-1;i<=j;++i,--j)strip.push(points[i]),i!==j&&strip.push(points[j]);return strip}function render3DFlipWinding(mesh){if(mesh.indices)return render3DFlipTriangles(mesh.indices);for(const key of["points","normals","uvs","colors"]){var a=mesh[key];a.length&&(a.unshift(a[0]),a.push(a[a.length-1]))}mesh.vertexKeys=void 0}const render3DMeshBuffers="undefined"==typeof FinalizationRegistry?void 0:new FinalizationRegistry(({buffer,indexBuffer,generation})=>{generation===render3D?.contextGeneration&&glContext&&(glContext.deleteBuffer(buffer),glContext.deleteBuffer(indexBuffer))});function render3DSmoothNormalSums(points,indices){var strip=!indices,triangles=strip?max(points.length-2,0):indices.length/3|0,{groups,count}=function(points){var groups=new Int32Array(points.length),first=new Map,next=[],places=[];for(let i=0;i<points.length;++i){var p=points[i],x=round(1e5*p.x),y=round(1e5*p.y),z=round(1e5*p.z),p=Math.imul(x,73856093)^Math.imul(y,19349663)^Math.imul(z,83492791);let group=first.get(p);for(;void 0!==group&&(places[3*group]!==x||places[3*group+1]!==y||places[3*group+2]!==z);)group=next[group];void 0===group&&(group=next.length,next.push(first.get(p)),first.set(p,group),places.push(x,y,z)),groups[i]=group}return{groups:groups,count:next.length}}(points);const sums=new Float64Array(3*count),touched=new Uint8Array(count);let nx,ny,nz;var addCorner=(a,b,c,group)=>{var ux=b.x-a.x,uy=b.y-a.y,b=b.z-a.z,vx=c.x-a.x,vy=c.y-a.y,c=c.z-a.z,a=Math.acos(clamp((ux*vx+uy*vy+b*c)/((ux**2+uy**2+b**2)**.5*(vx**2+vy**2+c**2)**.5||1),-1,1)),ux=3*group;sums[ux]+=nx*a,sums[1+ux]+=ny*a,sums[2+ux]+=nz*a,touched[group]=1};for(let t=0;t<triangles;++t){var i0=strip?t:indices[3*t],i1=strip?t+1:indices[3*t+1],i2=strip?t+2:indices[3*t+2],a=points[i0],b=points[i1],c=points[i2],ux=b.x-a.x,uy=b.y-a.y,uz=b.z-a.z,vx=c.x-a.x,vy=c.y-a.y,vz=c.z-a.z,cx=uy*vz-uz*vy,uz=uz*vx-ux*vz,vz=ux*vy-uy*vx,ux=cx**2+uz**2+vz**2;ux&&(vy=(!strip||1&t?1:-1)/ux**.5,nx=cx*vy,ny=uz*vy,nz=vz*vy,addCorner(a,b,c,groups[i0]),addCorner(b,c,a,groups[i1]),addCorner(c,a,b,groups[i2]))}return{groups:groups,sums:sums,touched:touched}}class Mesh{constructor(){this.points=[],this.normals=[],this.uvs=[],this.colors=[],this.buffer=void 0,this.indexBuffer=void 0,this.bufferCount=0,this.indexType=0,this.dirty=!1,this.instanced=void 0,this.doubleSided=!1,this.dynamicDraw=!1,this.indices=void 0,this.vertexKeys=void 0,this.vertexLayout=void 0,this.vertexDataPacked=!1,this.instanceCount=0,this.instanceData=void 0,this.radius=0,this.bounds=void 0,this.contextGeneration=0}get vertexCount(){return this.points.length}addStrip(points,normals,uvs,colors){var part;return this.indices?(part=(new Mesh).addStrip(points,normals,uvs,colors).toIndexed(),this.addTriangles(part.points,part.indices,part.normals,part.uvs,part.colors)):(function(points,normals,uvs,colors,callback){var n=points.length,count=render3DStripCount(n),normalArray=isArray(normals),uvArray=isArray(uvs),colorArray=isArray(colors);for(let k=0;k<count;++k){var i=render3DStripIndex(k,n);callback(points[i],normalArray?normals[i]:normals||RENDER3D_DEFAULT_NORMAL,uvArray?uvs[i]:uvs||RENDER3D_DEFAULT_UV,colorArray?colors[i]:colors||WHITE)}}(points,normals,uvs,colors,(p,n,uv,c)=>{this.points.push(p),this.normals.push(n),this.uvs.push(uv),this.colors.push(c)}),this.vertexKeys=void 0,this.dirty=!0,this)}addTriangles(points,indices,normals,uvs,colors){this.toIndexed();var offset=this.points.length,normalArray=isArray(normals),uvArray=isArray(uvs),colorArray=isArray(colors);for(let i=0;i<points.length;++i)this.points.push(points[i]),this.normals.push(normalArray?normals[i]:normals||RENDER3D_DEFAULT_NORMAL),this.uvs.push(uvArray?uvs[i]:uvs||RENDER3D_DEFAULT_UV),this.colors.push(colorArray?colors[i]:colors||WHITE);for(const i of indices)this.indices.push(i+offset);return this.dirty=!0,this}toIndexed(){var vertices,indices;return this.indices||({vertices,indices}=this.getTriangles(),this.points=vertices.map(i=>this.points[i]),this.normals=vertices.map(i=>this.normals[i]||RENDER3D_DEFAULT_NORMAL),this.uvs=vertices.map(i=>this.uvs[i]||RENDER3D_DEFAULT_UV),this.colors=vertices.map(i=>this.colors[i]||WHITE),this.indices=render3DFlipTriangles(indices),this.vertexKeys=void 0,this.dirty=!0),this}addQuad(a,b,c,d,color,uvs){return this.addStrip([a,b,d,c],render3DFaceNormal(a,b,c,d),uvs?render3DQuadValues(uvs):RENDER3D_QUAD_UVS,render3DQuadValues(color))}combine(mesh,matrix=RENDER3D_IDENTITY,color=WHITE){var last,normalMatrix=render3DNormalMatrix(matrix=render3DMatrix(matrix)),mirrors=matrix.determinant()<0;let part=mesh,order;if(this.indices||mesh.indices){this.toIndexed(),part=mesh.indices?mesh:(new Mesh).combine(mesh).toIndexed();var offset=this.points.length,indices=part.indices;for(let t=0,n=indices.length;t<n;t+=3)this.indices.push(indices[t]+offset,indices[t+(mirrors?2:1)]+offset,indices[t+(mirrors?1:2)]+offset)}else mirrors&&part.points.length&&(last=part.points.length-1,order=[0,...part.points.keys(),last]);for(let j=0,count=(order||part.points).length;j<count;++j){var i=order?order[j]:j;this.points.push(matrix.transformPoint(part.points[i])),this.normals.push(normalMatrix.transformDirection(part.normals[i]||RENDER3D_DEFAULT_NORMAL).normalize()),this.uvs.push((part.uvs[i]||RENDER3D_DEFAULT_UV).copy()),this.colors.push((part.colors[i]||WHITE).multiply(color))}return this.doubleSided||=mesh.doubleSided,this.vertexKeys=void 0,this.dirty=!0,this}mirror(axis=vec3(1,0,0)){var axis=axis.normalize(),a=-2*axis.x,b=-2*axis.y,c=-2*axis.z,a=new Matrix4([1+a*axis.x,b*axis.x,c*axis.x,0,a*axis.y,1+b*axis.y,c*axis.y,0,a*axis.z,b*axis.z,1+c*axis.z,0,0,0,0,1]);return(new Mesh).combine(this).combine(this,a)}spin(count,axis=vec3(0,1,0)){var mesh=new Mesh,unit=axis.normalize();for(let i=0;i<count;++i)mesh.combine(this,function(axis,angle){var x=vec3(1,0,0).rotate(axis,angle),y=vec3(0,1,0).rotate(axis,angle),axis=vec3(0,0,1).rotate(axis,angle);return new Matrix4([x.x,x.y,x.z,0,y.x,y.y,y.z,0,axis.x,axis.y,axis.z,0,0,0,0,1])}(unit,i/count*2*PI));return mesh}union(mesh,matrix){return render3DMeshCSG(this,mesh,matrix,0)}subtract(mesh,matrix){return render3DMeshCSG(this,mesh,matrix,1)}intersect(mesh,matrix){return render3DMeshCSG(this,mesh,matrix,2)}scaleUVs(scale){const s=render3DSize2(scale);return this.uvs=this.uvs.map(uv=>vec2(uv.x*s.x,uv.y*s.y)),this.dirty=!0,this}transform(matrix){var normalMatrix=render3DNormalMatrix(matrix=render3DMatrix(matrix));for(let i=0;i<this.points.length;++i)this.points[i]=matrix.transformPoint(this.points[i]),this.normals[i]&&=normalMatrix.transformDirection(this.normals[i]).normalize();return matrix.determinant()<0&&render3DFlipWinding(this),this.dirty=!0,this}flipNormals(){return render3DFlipWinding(this),this.normals=this.normals.map(n=>n.scale(-1)),this.dirty=!0,this}setColor(color){return this.colors=this.points.map(()=>color),this.dirty=!0,this}getBounds(){if(!this.points.length)return{min:vec3(),max:vec3()};var lo=vec3(1/0),hi=vec3(-1/0);for(const p of this.points)lo.x=min(lo.x,p.x),lo.y=min(lo.y,p.y),lo.z=min(lo.z,p.z),hi.x=max(hi.x,p.x),hi.y=max(hi.y,p.y),hi.z=max(hi.z,p.z);return{min:lo,max:hi}}center(){var bounds=this.getBounds();return this.transform(bounds.min.add(bounds.max).scale(-.5))}fit(size=1){var bounds=this.getBounds(),bounds=bounds.max.subtract(bounds.min),size=size/(max(bounds.x,bounds.y,bounds.z)||1);return this.transform(Matrix4.scaling(vec3(size)))}computeRadius(){let r=0;for(const p of this.points)r=max(r,p.lengthSquared());return this.bounds=this.getBounds(),this.radius=r**.5}computeNormals(smooth=!1){if(this.indices)if(smooth){const{groups,sums,touched}=render3DSmoothNormalSums(this.points,this.indices);this.normals=this.points.map((p,i)=>{var s=3*groups[i],x=sums[s],y=sums[1+s],s=sums[2+s],lengthSquared=x**2+y**2+s**2,k=1/lengthSquared**.5;return touched[groups[i]]&&lengthSquared?vec3(x*k,y*k,s*k):RENDER3D_DEFAULT_NORMAL})}else{var split=a=>this.indices.map(i=>a[i]);this.points=split(this.points),this.normals=split(this.normals),this.uvs=split(this.uvs),this.colors=split(this.colors),this.indices=this.indices.map((_,i)=>i);const points=this.points,indices=this.indices,normals=points.map(()=>RENDER3D_DEFAULT_NORMAL);for(let t=0;t<indices.length;t+=3){var a=points[indices[t]],b=points[indices[t+1]],c=points[indices[t+2]],b=b.subtract(a).cross(c.subtract(a));b.lengthSquared()&&(normals[indices[t]]=normals[indices[t+1]]=normals[indices[t+2]]=b.normalize())}this.normals=normals}else{const points=this.points,n=points.length;if(smooth){const{groups,sums,touched}=render3DSmoothNormalSums(points);this.normals=points.map((p,i)=>{var s=3*groups[i],x=sums[s],y=sums[1+s],s=sums[2+s],l=(x**2+y**2+s**2)**.5;return touched[groups[i]]?l?vec3(x*(1/l),y*(1/l),s*(1/l)):vec3():vec3(0,1,0)})}else{var faceNormals=[];for(let i=0;i+2<n;++i){const a=points[i],b=points[i+1],c=points[i+2];var normal=b.subtract(a).cross(c.subtract(a));faceNormals.push(normal.lengthSquared()?normal.normalize(1&i?1:-1):void 0)}const normals=points.map(()=>RENDER3D_DEFAULT_NORMAL);faceNormals.forEach((f,i)=>f&&(normals[i]=normals[i+1]=normals[i+2]=f)),this.normals=normals}this.vertexKeys=void 0}return this.dirty=!0,this}upload(){var gl,indices,wide,data,layout;return render3D?.program&&glContext?(gl=glContext,layout=this.vertexLayout,this.dynamicDraw&&layout&&this.buffer&&this.contextGeneration===render3D.contextGeneration&&layout.pointCount===this.points.length?(gl.bindBuffer(gl.ARRAY_BUFFER,this.buffer),gl.bufferSubData(gl.ARRAY_BUFFER,0,this.vertexDataPacked?layout.data:render3DMeshVertexData(this,layout.vertices,layout.data)),gl.bindBuffer(gl.ARRAY_BUFFER,glArrayBuffer),this.dirty=this.vertexDataPacked=!1):(this.dispose(),{vertices:layout,indices}=this.getTriangles(),wide=65535<layout.length,data=render3DMeshVertexData(this,layout),this.vertexLayout=this.dynamicDraw?{vertices:layout,pointCount:this.points.length,data:data}:void 0,this.vertexKeys=void 0,this.buffer=gl.createBuffer(),this.indexBuffer=gl.createBuffer(),this.bufferCount=indices.length,this.indexType=wide?gl.UNSIGNED_INT:gl.UNSIGNED_SHORT,this.dirty=this.vertexDataPacked=!1,gl.bindBuffer(gl.ARRAY_BUFFER,this.buffer),gl.bufferData(gl.ARRAY_BUFFER,data,this.dynamicDraw?gl.DYNAMIC_DRAW:gl.STATIC_DRAW),gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER,this.indexBuffer),gl.bufferData(gl.ELEMENT_ARRAY_BUFFER,new(wide?Uint32Array:Uint16Array)(indices),gl.STATIC_DRAW),this.contextGeneration=render3D.contextGeneration,layout={buffer:this.buffer,indexBuffer:this.indexBuffer,generation:this.contextGeneration},render3DMeshBuffers?.register(this,layout,this),gl.bindBuffer(gl.ARRAY_BUFFER,glArrayBuffer))):this.computeRadius(),this}getTriangles(){var count=this.points.length,vertices=[];if(this.indices){for(let i=0;i<count;++i)vertices.push(i);return{vertices:vertices,indices:render3DFlipTriangles(this.indices.slice())}}var remap=new Int32Array(count),place=new Int32Array(count),keys=this.vertexKeys;if(keys&&keys.length===count){var first=new Int32Array(count).fill(-1);for(let i=0;i<count;++i){var key=keys[i],j=first[key];j<0?(remap[first[key]=i]=vertices.length,vertices.push(i)):remap[i]=remap[j],place[i]=first[key]}}else{var values=new Float64Array(9*count);let size=1;for(;size<2*count;)size*=2;var mask=size-1,seen=new Int32Array(size),places=new Int32Array(size),mix=(h,v)=>Math.imul(h^v,2654435761)>>>0;for(let i=0;i<count;++i){var p=this.points[i],n=this.normals[i]||RENDER3D_DEFAULT_NORMAL,uv=this.uvs[i]||RENDER3D_DEFAULT_UV,k=9*i,x=values[k]=round(1e6*p.x),y=values[1+k]=round(1e6*p.y),z=values[2+k]=round(1e6*p.z);values[3+k]=round(1e6*n.x),values[4+k]=round(1e6*n.y),values[5+k]=round(1e6*n.z),values[6+k]=round(1e6*uv.x),values[7+k]=round(1e6*uv.y),values[8+k]=(this.colors[i]||WHITE).rgbaInt();let h=mix(mix(mix(2166136261,x),y),z);for(let slot=h&mask;;slot=slot+1&mask){var o=places[slot]-1;if(o<0){places[slot]=i+1,place[i]=i;break}if(values[9*o]===x&&values[9*o+1]===y&&values[9*o+2]===z){place[i]=o;break}}for(let m=3;m<9;++m)h=mix(h,values[k+m]);for(let slot=h&mask;;slot=slot+1&mask){const o=seen[slot]-1;if(o<0){seen[slot]=i+1,remap[i]=vertices.length,vertices.push(i);break}let same=!0;for(let m=0;same&&m<9;++m)same=values[9*o+m]===values[k+m];if(same){remap[i]=remap[o];break}}}}return{vertices:vertices,indices:render3DStripTriangles(count,remap,place)}}render(matrix,tileInfo,color){render3D?.drawMesh(this,matrix,tileInfo,color)}dispose(){this.buffer&&(render3DMeshBuffers?.unregister(this),this.contextGeneration===render3D?.contextGeneration&&(glContext?.deleteBuffer(this.buffer),glContext?.deleteBuffer(this.indexBuffer)),this.buffer=this.indexBuffer=void 0,this.bufferCount=0)}}function buildLathe(profile,sides=16,smooth=render3D?.smoothShading,capped=!0){sides|=0;var mesh=new Mesh;const rings=profile.length;var point=(i,a)=>vec3(sin(a)*profile[i][0],profile[i][1],cos(a)*profile[i][0]);const segmentNormal=i=>{var[r0,y0]=profile[i],[i,y1]=profile[i+1],y1=vec2(y1-y0,r0-i);return y1.length()?y1.normalize():vec2(1,0)},closed=2<rings&&abs(profile[0][0]-profile[rings-1][0])<1e-9&&abs(profile[0][1]-profile[rings-1][1])<1e-9;var vertexNormal=i=>{if(!closed&&(!i||i==rings-1)&&abs(profile[i][0])<1e-9){var up=i?1:-1;if(segmentNormal(i?i-1:0).y*up>Math.SQRT1_2-1e-9)return vec2(0,up)}let n=vec2();up=s=>{return n=n.add(segmentNormal(s).scale((s=s,hypot(profile[s+1][0]-profile[s][0],profile[s+1][1]-profile[s][1]))))};return 0<i?up(i-1):closed&&up(rings-2),i<rings-1?up(i):closed&&up(0),n.length()?n.normalize():vec2(1,0)},normal3D=(n,a)=>vec3(sin(a)*n.x,n.y,cos(a)*n.x);const lengths=[0];for(let i=1;i<rings;++i)lengths[i]=lengths[i-1]+hypot(profile[i][0]-profile[i-1][0],profile[i][1]-profile[i-1][1]);const total=lengths[rings-1]||1;var v=i=>1-lengths[i]/total;for(let i=0;i+1<rings;++i)if(smooth){var points=[],normals=[],uvs=[],n0=vertexNormal(i),n1=vertexNormal(i+1),half=PI/sides,turn1=abs(profile[i+1][0])<1e-9?-half:0,turn0=abs(profile[i][0])<1e-9?half:0;for(let j=0;j<=sides;++j){var a=j/sides*2*PI,u=j/sides;points.push(point(i+1,a),point(i,a)),normals.push(normal3D(n1,a+turn1),normal3D(n0,a+turn0)),uvs.push(vec2(u,v(i+1)),vec2(u,v(i)))}mesh.addStrip(points,normals,uvs)}else{var n=segmentNormal(i);for(let j=0;j<sides;++j){var a0=j/sides*2*PI,a1=(j+1)/sides*2*PI,u0=j/sides,u1=(j+1)/sides;mesh.addStrip([point(i+1,a0),point(i,a0),point(i+1,a1),point(i,a1)],normal3D(n,(a0+a1)/2),[vec2(u0,v(i+1)),vec2(u0,v(i)),vec2(u1,v(i+1)),vec2(u1,v(i))])}}if(capped&&!closed)for(var[i,up]of[[0,!1],[rings-1,!0]])if(!(abs(profile[i][0])<1e-9)){const points=[],uvs=[];for(let j=0;j<sides;++j){const a=(up?j:-j)/sides*2*PI;points.push(point(i,a)),uvs.push(vec2(.5*sin(a)+.5,.5*cos(a)+.5))}mesh.addStrip(render3DPolygonStrip(points),vec3(0,up?1:-1,0),render3DPolygonStrip(uvs))}return mesh.doubleSided=!closed&&!capped&&(1e-9<abs(profile[0][0])||1e-9<abs(profile[rings-1][0])),mesh}function buildSphere(size=1,sides=16,rings=8,smooth=render3D?.smoothShading){var profile=[];for(let i=0;i<=rings;++i){var a=i/rings*PI-PI/2;profile.push([cos(a)*size/2,sin(a)*size/2])}return buildLathe(profile,sides,smooth)}function buildBox(size=1,bevel=0,bevelSegments=1){var half=render3DSize3(size).scale(.5),size=[[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)]],bevel=min(bevel,half.x,half.y,half.z);if(0<bevel)return function(half,t,segments,faces){const mesh=new Mesh,inner=half.subtract(vec3(t)),n=max(1,0|segments);var normal,r,u,triangle=(a,b,c,na,nb,nc)=>{let normal=b.subtract(a).cross(c.subtract(a));if(!(normal.lengthSquared()<1e-20)){normal.dot(a.add(b).add(c))<0&&([b,c,nb,nc,normal]=[c,b,nc,nb,normal.scale(-1)]),normal=normal.normalize();let face=faces[0];for(const f of faces)f[0].dot(normal)>face[0].dot(normal)&&(face=f);const[,r,u]=face,hr=abs(half.dot(r)),hu=abs(half.dot(u));var uv=p=>vec2(.5+p.dot(r)/(2*hr),.5-p.dot(u)/(2*hu));mesh.addStrip([a,b,c],1<n&&na?[na,nb,nc]:normal,[uv(a),uv(b),uv(c)])}};for([normal,r,u]of faces){var[a,b,c,d]=render3DQuadAxes(normal.multiply(half),r.multiply(inner),u.multiply(inner));triangle(a,b,c),triangle(c,b,d)}var axes=[vec3(1,0,0),vec3(0,1,0),vec3(0,0,1)];for(let k=0;k<3;++k)for(const si of[-1,1])for(const sj of[-1,1]){const n1=axes[(k+1)%3].scale(si),n2=axes[(k+2)%3].scale(sj);var middle=n1.multiply(inner).add(n2.multiply(inner)),along=axes[k].multiply(inner),start=middle.subtract(along),end=middle.add(along),dir=m=>n1.scale(cos(m/n*PI/2)).add(n2.scale(sin(m/n*PI/2)));for(let m=0;m<n;++m){var d0=dir(m),d1=dir(m+1);const a=start.add(d0.scale(t)),b=end.add(d0.scale(t)),c=start.add(d1.scale(t)),d=end.add(d1.scale(t));triangle(a,b,c,d0,d0,d1),triangle(c,b,d,d1,d0,d1)}}for(const sx of[-1,1])for(const sy of[-1,1])for(const sz of[-1,1]){const nx=vec3(sx,0,0),ny=vec3(0,sy,0),nz=vec3(0,0,sz),corner=inner.multiply(vec3(sx,sy,sz)),dir=(i,j)=>{var polar=i/n*PI/2,j=i?j/i*PI/2:0;return nz.scale(cos(polar)).add(nx.scale(cos(j)*sin(polar))).add(ny.scale(sin(j)*sin(polar)))};var point=(i,j)=>corner.add(dir(i,j).scale(t));for(let i=0;i<n;++i){for(let j=0;j<=i;++j)triangle(point(i,j),point(i+1,j),point(i+1,j+1),dir(i,j),dir(i+1,j),dir(i+1,j+1));for(let j=0;j<i;++j)triangle(point(i,j),point(i+1,j+1),point(i,j+1),dir(i,j),dir(i+1,j+1),dir(i,j+1))}}return mesh}(half,bevel,bevelSegments,size);var n,r,u,mesh=new Mesh;for([n,r,u]of size){var center=n.multiply(half),right=r.multiply(half),up=u.multiply(half);mesh.addStrip(render3DQuadAxes(center,right,up),n,RENDER3D_QUAD_UVS)}return mesh}function buildGrid(size=vec2(1),segments=1,color,heightFunction,smooth=heightFunction&&render3D?.smoothShading){heightFunction||=()=>0,size=render3DSize2(size),segments=render3DSize2(segments);var mesh=new Mesh;const segmentsX=segments.x,segmentsZ=segments.y,cellX=size.x/segmentsX,cellZ=size.y/segmentsZ,halfX=size.x/2,halfZ=size.y/2,ex=cellX/2,ez=cellZ/2,px=i=>i*cellX-halfX,pz=j=>j*cellZ-halfZ,colorAt=color,cellColor=(i,j)=>color?isColor(color)?color:colorAt(px(i),pz(j)):WHITE;var row=j=>{var points=[],normals=[],uvs=[],colors=[],z=pz(j);for(let i=0;i<=segmentsX;++i){var x=px(i);points.push(vec3(x,heightFunction(x,z),z)),uvs.push(vec2(i/segmentsX,j/segmentsZ)),smooth&&(normals.push(render3DSlopeNormal(heightFunction,x,z,ex,ez,halfX,halfZ)),colors.push(cellColor(i,j)))}return{points:points,normals:normals,uvs:uvs,colors:colors}};let above=row(0);for(let j=0;j<segmentsZ;++j){var below=row(j+1);if(smooth){var points=[],normals=[],uvs=[],colors=[];for(let i=0;i<=segmentsX;++i)points.push(above.points[i],below.points[i]),normals.push(above.normals[i],below.normals[i]),uvs.push(above.uvs[i],below.uvs[i]),colors.push(above.colors[i],below.colors[i]);mesh.addStrip(points,normals,uvs,colors)}else for(let i=0;i<segmentsX;++i)mesh.addQuad(above.points[i],below.points[i],below.points[i+1],above.points[i+1],cellColor(i+.5,j+.5),[above.uvs[i],below.uvs[i],below.uvs[i+1],above.uvs[i+1]]);above=below}if(smooth){var n=2*(segmentsX+1)+2,keys=mesh.vertexKeys=new Int32Array(mesh.points.length),id=(i,j)=>j*(segmentsX+1)+i;for(let j=0;j<segmentsZ;++j){var start=j*n;keys[start]=id(0,j);for(let i=0;i<=segmentsX;++i)keys[1+start+2*i]=id(i,j),keys[2+start+2*i]=id(i,j+1);keys[start+n-1]=id(segmentsX,j+1)}}return mesh.doubleSided=!0,mesh}function buildSky(topColor=hsl(.6,.8,.55),horizonColor=hsl(.6,1,.9),bottomColor=horizonColor,sides=16,rings=8){sides=buildSphere(2,sides,rings,!0).flipNormals();return sides.colors=sides.points.map(p=>p.y<0?horizonColor.lerp(bottomColor,-p.y):horizonColor.lerp(topColor,p.y)),render3DSkyColors.set(sides,[topColor.copy(),horizonColor.copy(),bottomColor.copy()]),sides}const render3DSkyColors=new WeakMap;class EngineObject3D extends EngineObject{constructor(pos3D=vec3(),mesh,tileInfo,color=WHITE){tileInfo instanceof TextureInfo&&render3DWholeTiles.add(tileInfo=new TileInfo(vec2(),tileInfo.size,tileInfo,0,0)),super(vec2(pos3D.x,pos3D.y),vec2(),tileInfo,0,color),this.mass=0,this.pos3D=pos3D.copy(),this.rotation3D=vec3(),this.scale3D=vec3(1),this.velocity3D=vec3(),this.angleVelocity3D=vec3();color=(this.mesh=mesh)&&mesh.points.length?!mesh.dirty&&mesh.bounds||mesh.getBounds():void 0;this.size3D=color?color.max.subtract(color.min):vec3(1),this.softShadow=0,this.upright=!1,this.pixelated=!1,this.sync2D=!1,this.transparent=!mesh&&!!tileInfo,this.additive=!1,this.emissive=0,this.specular=0,this.shininess=16,this.normalMap=void 0,this.normalScale=1,this.reflectivity=0,this.emissiveMap=void 0,this.emissiveMapColor=WHITE,this.environment=void 0,this.castShadow=!0,this.collideAsSphere3D=!1,this.groundAngle=PI/4,this.receiveShadow=!0,this.renderAfter2D=void 0,this.localMatrix=void 0,this.worldMatrix=new Matrix4,this.matrixBuilt=new Float64Array(9).fill(NaN),this.matrixVersion=0,this.matrixParent=void 0,this.matrixParentVersion=0,this.movePass=engineObjectsUpdateCount,this.collideFrom=vec3(),this.collideFromPass=-1,this.drawOwner=void 0}updatePhysics(){this.sync2D&&super.updatePhysics();var v,gx,moving,ground=this.groundObject,solve=(this.sync2D||(this.groundObject=void 0),enablePhysicsSolver&&!this.sync2D),cap=this.clampSpeed&&solve&&(this.collideSolidObjects||this.collideLevel&&this.mass)?objectMaxSpeed:void 0,oldPos=(this.collideLevel||this.collideSolidObjects)&&this.mass&&!this.sync2D?this.pos3D.copy():void 0;if(this.collideSolidObjects&&!this.sync2D&&render3DCollideFrom(this),render3DMove(this,cap),ground&&this.mass&&!this.sync2D&&(cap=max(this.friction,ground.friction),v=this.velocity3D,gx=(moving=ground instanceof EngineObject3D?ground.velocity3D:void 0)?.x??0,moving=moving?.z??0,v.x=gx+(v.x-gx)*cap,v.z=moving+(v.z-moving)*cap),oldPos&&this.collideLevel&&solve&&render3DCollideLevel(this,oldPos,ground),this.collideSolidObjects&&solve){var a=this;let shapeA=render3DSolidShape(a);var reachA=render3DSolidReach(a);for(const b of engineObjectsCollide){if(b===a)break;if(!b.destroyed&&b instanceof EngineObject3D&&!b.parent&&!b.sync2D&&(a.isSolid||b.isSolid)){var p=shapeA.pos,q=b.pos3D,reach=reachA+render3DSolidReach(b),dx=p.x-q.x,dy=p.y-q.y,p=p.z-q.z;if(!(reach*reach<dx*dx+dy*dy+p*p)){var pushed,q=render3DSolidShape(b);let push=render3DSolidPush(shapeA,q);push&&(push=render3DPushFrom(shapeA,q,a,b,push),render3DCollideAsk(a,b,push,!1))&&(reach=push.lengthSquared(),dx=push.y/reach**.5,dy=0<dx&&a.mass&&dx>=cos(a.groundAngle)&&render3DOnFace(push,q),p=dx<0&&b.mass&&-dx>=cos(b.groundAngle)&&render3DOnFace(push,shapeA),(dy||p)&&(dy?a.groundObject=b:b.groundObject=a,push.x||push.z)&&(push=vec3(0,reach/push.y,0)),q=a.mass+b.mass,dx=a.mass?b.mass?b.mass/q:1:0,p=b.mass?a.mass?a.mass/q:1:0,dy=a.pos3D.copy(),reach=b.pos3D.copy(),a.pos3D=a.pos3D.add(push.scale(dx)),b.pos3D=b.pos3D.subtract(push.scale(p)),q=push.normalize(),dx&&a.velocity3D.dot(q)<0&&(a.velocity3D=a.velocity3D.reflect(q,a.restitution)),p&&0<b.velocity3D.dot(q)&&(b.velocity3D=b.velocity3D.reflect(q,b.restitution)),dx&&p&&(pushed=a.pos3D.copy(),render3DSettleFixed(a,b,dy),b.pos3D=b.pos3D.add(a.pos3D.subtract(pushed))),p&&(q=b.pos3D.copy(),render3DSettleFixed(b,a,reach),p=b.pos3D.x-q.x,reach=b.pos3D.y-q.y,q=b.pos3D.z-q.z,dx)&&(p||reach||q)&&(a.pos3D=a.pos3D.add(vec3(p,reach,q)),render3DSettleFixed(a,b,dy)),dx)&&(shapeA=render3DSolidShape(a))}}}}}updateTransforms(updateChildren=!0){if(paused||this.parent&&this.movePass!==engineObjectsUpdateCount&&(this.movePass=engineObjectsUpdateCount,render3DMove(this)),super.updateTransforms(!1),!paused&&this.sync2D&&(this.pos3D.x=this.pos.x,this.pos3D.y=this.pos.y,this.rotation3D.z=-this.angle),updateChildren)for(const child of this.children)child.updateTransforms()}setCollision(collideSolidObjects=!0,isSolid=!0,collideLevel=!0,collideRaycast=!1){super.setCollision(collideSolidObjects,isSolid,collideLevel,collideRaycast)}collideWithVoxel(type,cell){return!0}getWorldPos3D(){return render3DObjectMatrix(this).getTranslation()}getForward3D(){return render3DAxis(render3DObjectMatrix(this).m,8).normalize(-1)}getRight3D(){return render3DAxis(render3DObjectMatrix(this).m,0).normalize()}getUp3D(){return render3DAxis(render3DObjectMatrix(this).m,4).normalize()}getMatrix(){return render3DObjectMatrix(this).copy()}get roughness(){return(2/(this.shininess+2))**.25}set roughness(roughness){this.shininess=max(1,2/max(roughness,.001)**4-2)}lookAt(target){var parent=this.parent instanceof EngineObject3D?this.parent:void 0,parent=parent?parent.getMatrix().invert().transformPoint(target):target;this.rotation3D=render3DLookRotation(parent.subtract(this.pos3D),this.rotation3D)}addChild(child,localPos,localAngle){return child instanceof EngineObject3D&&(child.movePass=engineObjectsUpdateCount),super.addChild(child,localPos,localAngle)}attach(child){if(!(child instanceof EngineObject3D))return super.attach(child);child.parent?.removeChild(child);var local=render3DObjectMatrix(this).copy().invert().multiply(render3DObjectMatrix(child));return super.attach(child),child.pos3D=local.getTranslation(),child.rotation3D=local.getRotation(),child.scale3D=local.getScale(),child.localMatrix&&(child.localMatrix=local),child}removeChild(child){child instanceof EngineObject3D&&!child.destroyed&&render3DTakeWorld(child,render3DObjectMatrix(child)),child instanceof EngineObject3D&&child.drawOwner===this&&(child.drawOwner=void 0),super.removeChild(child)}setMesh(mesh){const old=this.mesh;return this.mesh=mesh,old&&old!==mesh&&old.buffer&&!engineObjects.some(o=>o.mesh===old)&&old.dispose(),mesh}render(){}render3D(){var size,matrix=render3DObjectMatrix(this);this.mesh?render3D.drawMesh(this.mesh,matrix,this.tileInfo,this.color):this.tileInfo&&(size=render3DWorldSize(this,matrix=matrix.m),render3D.drawBillboard(vec3(matrix[12],matrix[13],matrix[14]),vec2(size.x,size.y),this.tileInfo,this.color,this.rotation3D.z,this.upright))}}const RENDER3D_PART_SETTINGS={emissive:0,additive:!1,specular:0,shininess:16,reflectivity:0,normalMap:void 0,normalScale:1,emissiveMap:void 0,emissiveMapColor:WHITE,receiveShadow:!0,castShadow:!0,pixelated:!1,shader:void 0,environment:void 0,renderAfter2D:void 0,renderOrder:0};function render3DSetting(o,name){var owner=o.drawOwner;if(owner){owner=render3DSetting(owner,name);if(owner!==RENDER3D_PART_SETTINGS[name])return owner}return o[name]}const render3DPartScratch={...RENDER3D_PART_SETTINGS};function render3DTakeWorld(o,world){o.pos3D=world.getTranslation(),o.rotation3D=world.getRotation(),o.scale3D=world.getScale(),o.localMatrix&&(o.localMatrix=world.copy())}const render3DLocalMatrix=new Matrix4;function render3DObjectMatrix(o){var parent=o.parent instanceof EngineObject3D?o.parent:void 0,parentMatrix=parent&&render3DObjectMatrix(parent),p=o.pos3D,r=o.rotation3D,s=o.scale3D,k=o.matrixBuilt,local=o.localMatrix;return local?(parent?(o.worldMatrix.m.set(parentMatrix.m),o.worldMatrix.multiply(local),o.matrixParentVersion=parent.matrixVersion):o.worldMatrix.m.set(local.m),k[0]=NaN,o.matrixParent=parent,++o.matrixVersion):(k[0]!==p.x||k[1]!==p.y||k[2]!==p.z||k[3]!==r.x||k[4]!==r.y||k[5]!==r.z||k[6]!==s.x||k[7]!==s.y||k[8]!==s.z||o.matrixParent!==parent||parent&&o.matrixParentVersion!==parent.matrixVersion)&&(k[0]=p.x,k[1]=p.y,k[2]=p.z,k[3]=r.x,k[4]=r.y,k[5]=r.z,k[6]=s.x,k[7]=s.y,k[8]=s.z,parent?(buildMatrix(p,r,s,render3DLocalMatrix),o.worldMatrix.m.set(parentMatrix.m),o.worldMatrix.multiply(render3DLocalMatrix),o.matrixParentVersion=parent.matrixVersion):buildMatrix(p,r,s,o.worldMatrix),o.matrixParent=parent,++o.matrixVersion),o.worldMatrix}function render3DMove(o,cap){var p=o.pos3D,v=o.velocity3D,r=o.rotation3D,a=o.angleVelocity3D,d=o.sync2D?1:o.damping,e=o.sync2D?1:o.angleDamping;v.x*=d,v.y*=d,v.z*=d,o.mass&&!o.sync2D&&(d=render3D.gravity,o=o.gravityScale,v.x+=d.x*o,v.y+=d.y*o,v.z+=d.z*o),void 0!==cap&&(v.x=clamp(v.x,-cap,cap),v.y=clamp(v.y,-cap,cap),v.z=clamp(v.z,-cap,cap)),p.x+=v.x,p.y+=v.y,p.z+=v.z,r.x+=a.x*=e,r.y+=a.y*=e,r.z+=a.z*=e}function render3DCollideLevel(o,oldPos,ground){for(const level of render3DLevel)level.destroyed||level.levelCollide3D(o,oldPos,level===ground)}function render3DSolidShape(o){var axes,s=o.size3D,k=o.scale3D,kx=abs(k.x),ky=abs(k.y),k=abs(k.z);return o.collideAsSphere3D?{pos:o.pos3D.copy(),radius:max(s.x,s.y,s.z)/2*max(kx,ky,k)}:(axes=!render3DIsSprite(o)&&isTurned3D(o.rotation3D)?function(o){var r=o.rotation3D,kept=render3DSolidAxesCache.get(o);if(kept&&kept.x===r.x&&kept.y===r.y&&kept.z===r.z)return kept.axes;kept=boxAxes3D(r);return render3DSolidAxesCache.set(o,{x:r.x,y:r.y,z:r.z,axes:kept}),kept}(o):void 0,{pos:o.pos3D.copy(),size:vec3(s.x*kx,s.y*ky,s.z*k),axes:axes})}const render3DSolidAxesCache=new WeakMap;function render3DSolidReach(o){var s=o.size3D,k=o.scale3D,kx=abs(k.x),ky=abs(k.y),k=abs(k.z);return o.collideAsSphere3D?max(s.x,s.y,s.z)/2*max(kx,ky,k):((o=s.x*kx)*o+(kx=s.y*ky)*kx+(o=s.z*k)*o)**.5/2}function render3DSolidPush(a,b){var push,sphereBox=(sphere,box)=>box.axes?collideSphereOrientedBox3D(sphere.pos,sphere.radius,box.pos,box.size,box.axes):collideSphereBox(sphere.pos,sphere.radius,box.pos,box.size);return a.size?b.size?a.axes||b.axes?collideOrientedBoxes3D(a.pos,a.size,a.axes??BOX_WORLD_AXES,b.pos,b.size,b.axes??BOX_WORLD_AXES):collideBoxBox3D(a.pos,a.size,b.pos,b.size):(push=sphereBox(b,a))&&push.scale(-1):b.size?sphereBox(a,b):collideSphereSphere(a.pos,a.radius,b.pos,b.radius)}function render3DCollideAsk(a,b,push,useKept=!0){var kept=useKept||render3DSettleAsked(a,b)?engineObjectsCollidePairAnswer(a,b):void 0;return void 0!==kept||(kept=a.collideWithObject(b,push),push=b.collideWithObject(a,push.scale(-1)),engineObjectsCollidePairAdd(a,b,kept=!(!kept||!push)),engineObjectsCollidePairAdd(b,a,kept),useKept&&render3DSettleAsked(a,b,!0)),kept}const render3DSettleAskedPairs={pass:-1,pairs:new Map};function render3DSettleAsked(a,b,mark=!1){var kept=render3DSettleAskedPairs;if(kept.pass!==engineObjectsUpdateCount){if(!mark)return;kept.pass=engineObjectsUpdateCount,kept.pairs.clear()}if(mark){for(var[one,other]of[[a,b],[b,a]]){var set=kept.pairs.get(one);set?set.add(other):kept.pairs.set(one,new Set([other]))}return 1}return kept.pairs.get(a)?.has(b)}function render3DCollideFrom(o){return o.collideFromPass!==engineObjectsUpdateCount&&(o.collideFromPass=engineObjectsUpdateCount,o.collideFrom.set(o.pos3D.x,o.pos3D.y,o.pos3D.z)),o.collideFrom}function render3DPushFrom(shapeA,shapeB,a,b,push){var aFrom=render3DCollideFrom(a),bFrom=render3DCollideFrom(b),a=a.pos3D,b=b.pos3D,mx=a.x-aFrom.x-b.x+bFrom.x,my=a.y-aFrom.y-b.y+bFrom.y,a=a.z-aFrom.z-b.z+bFrom.z;return push.x*mx+push.y*my+push.z*a<=0?push:function(a,b,aFrom,bFrom){if(!a.axes&&!b.axes){var half=shape=>shape.size?shape.size.scale(.5):vec3(shape.radius),halfA=half(a),halfB=half(b);let axis,amount=1/0,side=0;for(const k of["x","y","z"]){var reach=halfA[k]+halfB[k],was=aFrom[k]-bFrom[k];abs(was)<reach-1e-6||(was=sign(was),0<(reach=reach-was*(a.pos[k]-b.pos[k]))&&reach<amount&&(axis=k,amount=reach,side=was))}if(axis)return(half=vec3())[axis]=side*amount,half}}(shapeA,shapeB,aFrom,bFrom)??push}const render3DFixed={pass:-1,list:[]};function render3DSettleFixed(o,pusher,from){if(!o.destroyed&&o.collideSolidObjects){let shape=render3DSolidShape(o);var reachO=render3DSolidReach(o);for(const b of(render3DFixed.pass!==engineObjectsUpdateCount&&(render3DFixed.pass=engineObjectsUpdateCount,render3DFixed.list=engineObjectsCollide.filter(b=>!b.mass&&b instanceof EngineObject3D&&!b.parent&&!b.sync2D)),render3DFixed.list))if(b!==o&&b!==pusher&&!b.mass&&!b.destroyed&&(o.isSolid||b.isSolid)){var p=shape.pos,q=b.pos3D,reach=reachO+render3DSolidReach(b),dx=p.x-q.x,dy=p.y-q.y,p=p.z-q.z;if(!(reach*reach<dx*dx+dy*dy+p*p)){q=render3DSolidShape(b);let push=render3DSolidPush(shape,q);!push||push.lengthSquared()<1e-12||(push=render3DPushFrom(shape,q,o,b,push),render3DCollideAsk(o,b,push)&&(reach=push.lengthSquared(),0<(dx=push.y/reach**.5)&&o.mass&&dx>=cos(o.groundAngle)&&render3DOnFace(push,q)&&(o.groundObject=b,push.x||push.z)&&(push=vec3(0,reach/push.y,0)),o.pos3D=o.pos3D.add(push),shape=render3DSolidShape(o),dy=push.normalize(),o.velocity3D.dot(dy)<0)&&(o.velocity3D=o.velocity3D.reflect(dy,o.restitution)))}}o.collideLevel&&o.mass&&render3DCollideLevel(o,from,void 0)}}const render3DIsSprite=o=>!o.mesh&&!!o.tileInfo;function render3DOnFace(push,shape){return!!shape.size&&(push=push.normalize(),shape=shape.axes??BOX_WORLD_AXES,max(abs(push.dot(shape[0])),abs(push.dot(shape[1])),abs(push.dot(shape[2])))>1-1e-6)}function engineObjectsCollect3D(pos,size,objects=engineObjects,testCenters=!1){var radiusSquared="number"==typeof size&&0<size?(size/2)**2:void 0,box=radiusSquared?void 0:"number"==typeof size?vec3():render3DSize3(size),collected=[];for(const o of objects)if(o instanceof EngineObject3D&&!o.destroyed){const m=render3DObjectMatrix(o).m,s=o.size3D;if(s.x||s.y||s.z){var center=vec3(m[12],m[13],m[14]);const worldSize=testCenters?vec3():render3DWorldSize(o,m);var axis=i=>vec3(m[i],m[i+1],m[i+2]).normalize();let axes=!testCenters&&!render3DIsSprite(o)&&(m[1]||m[2]||m[4]||m[6]||m[8]||m[9])?[axis(0),axis(4),axis(8)]:void 0;if(axes&&(1e-6<abs(axes[0].dot(axes[1]))||1e-6<abs(axes[0].dot(axes[2]))||1e-6<abs(axes[1].dot(axes[2])))){const e=axes.map((a,i)=>a.scale(i?1<i?worldSize.z/2:worldSize.y/2:worldSize.x/2));var axis=c=>2*(abs(e[0][c])+abs(e[1][c])+abs(e[2][c]));worldSize.set(axis("x"),axis("y"),axis("z")),axes=void 0}let hit;(hit=box&&axes?!!collideOrientedBoxes3D(pos,box,BOX_WORLD_AXES,center,worldSize,axes):box?isOverlapping3D(pos,box,center,worldSize):axes?!!collideSphereOrientedBox3D(pos,radiusSquared**.5,center,worldSize,axes):(axis=max(abs(pos.x-center.x)-worldSize.x/2,0))*axis+(axis=max(abs(pos.y-center.y)-worldSize.y/2,0))*axis+(axis=max(abs(pos.z-center.z)-worldSize.z/2,0))*axis<radiusSquared)&&collected.push(o)}}return collected}const render3DRaycastCenter=vec3();function render3DRaycastObject(ray,o){if(!o.destroyed&&o instanceof EngineObject3D){if(o instanceof HeightMap||o instanceof VoxelMap)return o.levelRaycast3D(ray);if((o.mesh||o.tileInfo)&&!(o instanceof InstancedMesh3D)){var matrix=render3DObjectMatrix(o),mesh=o.mesh;if(!mesh)return function(ray,o,matrix){var center=matrix.getTranslation(),matrix=render3DWorldSize(o,matrix.m),matrix=vec2(matrix.x,matrix.y);var right,t,p;if(0<matrix.x&&0<matrix.y)return ray.direction.lengthSquared()?(o=render3DBillboardAxes(matrix,o.rotation3D.z,o.upright),right=vec3(o[0],o[1],o[2]),o=vec3(o[3],o[4],o[5]),void 0!==(t=raycastPlane(ray,center,right.cross(o)))&&(p=ray.getPosition(t).subtract(center),abs(p.dot(right))<=right.lengthSquared())&&abs(p.dot(o))<=o.lengthSquared()?t:void 0):render3DRaycastDisc(ray,center,hypot(matrix.x,matrix.y)/2)}(ray,o,matrix);o=(mesh.dirty||!mesh.radius?mesh.computeRadius():mesh.radius)*render3DMaxStretch(matrix.m);if(0<o){var m=matrix.m,m=render3DRaycastCenter.set(m[12],m[13],m[14]),distance=raycastSphere(ray,m,o);if(void 0!==distance)return matrix.determinant()?(distance=matrix.copy().invert(),matrix=mesh.bounds||mesh.getBounds(),void 0!==(matrix=raycastBox(distance=new Ray3D(distance.transformPoint(ray.origin),distance.transformDirection(ray.direction)),matrix.min.add(matrix.max).scale(.5),matrix.max.subtract(matrix.min)))&&ray.direction.lengthSquared()?function(ray,mesh){var points=mesh.points,indices=mesh.indices,both=mesh.doubleSided,ox=ray.origin.x,oy=ray.origin.y,oz=ray.origin.z,dx=ray.direction.x,dy=ray.direction.y,dz=ray.direction.z,count=indices?indices.length/3|0:points.length-2;let nearest;for(let i=0;i<count;++i){let a,b,c;c=indices?(a=points[indices[3*i]],b=points[indices[3*i+2]],points[indices[3*i+1]]):(a=points[1&i?i+1:i],b=points[1&i?i:i+1],points[i+2]);var sy,sz,sx,e1x=b.x-a.x,e1y=b.y-a.y,e1z=b.z-a.z,e2x=c.x-a.x,e2y=c.y-a.y,e2z=c.z-a.z,hx=dy*e2z-dz*e2y,hy=dz*e2x-dx*e2z,hz=dx*e2y-dy*e2x,det=e1x*hx+e1y*hy+e1z*hz;(both?det:det<0)&&(sx=ox-a.x,sy=oy-a.y,sz=oz-a.z,0<=(hx=(sx*hx+sy*hy+sz*hz)/det))&&hx<=1&&0<=(e1z=(dx*(hy=sy*e1z-sz*e1y)+dy*(hz=sz*e1x-sx*e1z)+dz*(sz=sx*e1y-sy*e1x))/det)&&hx+e1z<=1&&0<=(sx=(e2x*hy+e2y*hz+e2z*sz)/det)&&!(sx>=nearest)&&(nearest=sx)}return nearest}(distance,mesh):matrix):render3DRaycastDisc(ray,m,o)}}}}function render3DRaycastDisc(ray,center,radius){var d=ray.direction,center=center.subtract(ray.origin),ray=d.dot(d),ray=ray?center.dot(d)/ray:0;return 0<=ray&&center.subtract(d.scale(ray)).lengthSquared()<=radius*radius?ray:void 0}function engineObjectsRaycast3D(ray,objects=engineObjects){var hits=[];for(const o of objects){var distance=render3DRaycastObject(ray,o);void 0!==distance&&hits.push({o:o,distance:distance})}return hits.sort((a,b)=>a.distance-b.distance).map(hit=>hit.o)}function engineObjectsCallback3D(pos,size,callback,objects=engineObjects,testCenters=!1){for(const o of engineObjectsCollect3D(pos,size,objects,testCenters))o.destroyed||callback(o)}class InstancedMesh3D extends EngineObject3D{constructor(mesh,count,tileInfo,color=WHITE){super(vec3(),mesh,tileInfo,color),this.size3D=vec3(),this.count=count,this.maxCount=count,this.matrixData=new Float32Array(count*RENDER3D_MATRIX_FLOATS),this.colorData=new Float32Array(count*RENDER3D_COLOR_FLOATS),this.dirtyStart=0,this.dirtyEnd=count,this.colorDirtyStart=0,this.colorDirtyEnd=count,this.reachSquared=0,this.scaleSquared=0,this.boundsStart=0,this.boundsEnd=count,this.fixedRadius=void 0,this.buffer=void 0,this.colorBuffer=void 0,this.bufferGeneration=-1;var uv=render3DGetTileUVs(this.uvTileInfo=tileInfo),matrices=this.matrixData,colors=this.colorData;for(let i=0;i<count;++i){matrices.set(RENDER3D_IDENTITY.m,i*RENDER3D_MATRIX_FLOATS);var k=i*RENDER3D_COLOR_FLOATS;colors[k]=color.r,colors[1+k]=color.g,colors[2+k]=color.b,colors[3+k]=color.a,colors[4+k]=uv.x,colors[5+k]=uv.y,colors[6+k]=uv.w,colors[7+k]=uv.h}}setTransformAt(i,pos,rotation,scale){matrix4Compose(this.matrixData,i*RENDER3D_MATRIX_FLOATS,pos,rotation,scale),this.markDirty(i)}setTransforms(start,count,x,y,z,rx,ry,rz,sx,sy,sz){var m=this.matrixData,end=start+count;for(let i=start;i<end;++i){var cx,sinX,cy,sinY,cz,sinZ,k=i*RENDER3D_MATRIX_FLOATS,scaleX=sx?sx[i]:1,scaleY=sy?sy[i]:1,scaleZ=sz?sz[i]:1;rx?(cx=cos(rx[i]),sinX=sin(rx[i]),cy=cos(ry[i]),sinY=sin(ry[i]),cz=cos(rz[i]),sinZ=sin(rz[i]),m[k]=(cy*cz+sinY*sinX*sinZ)*scaleX,m[1+k]=cx*sinZ*scaleX,m[2+k]=(-sinY*cz+cy*sinX*sinZ)*scaleX,m[4+k]=(-cy*sinZ+sinY*sinX*cz)*scaleY,m[5+k]=cx*cz*scaleY,m[6+k]=(sinY*sinZ+cy*sinX*cz)*scaleY,m[8+k]=sinY*cx*scaleZ,m[9+k]=-sinX*scaleZ,m[10+k]=cy*cx*scaleZ):(m[k]=scaleX,m[1+k]=0,m[2+k]=0,m[4+k]=0,m[5+k]=scaleY,m[6+k]=0,m[8+k]=0,m[9+k]=0,m[10+k]=scaleZ),m[3+k]=m[7+k]=m[11+k]=0,m[15+k]=1,m[12+k]=x[i],m[13+k]=y[i],m[14+k]=z[i]}count&&this.markDirtyRange(start,end)}setMatrixAt(i,matrix){var data=this.matrixData,k=i*RENDER3D_MATRIX_FLOATS,m=matrix.m;for(let j=0;j<16;++j)data[k+j]=m[j];this.markDirty(i)}getMatrixAt(i){var i=i*RENDER3D_MATRIX_FLOATS,matrix=new Matrix4;return matrix.m.set(this.matrixData.subarray(i,16+i)),matrix}setColorAt(i,color){var data=this.colorData,k=i*RENDER3D_COLOR_FLOATS;data[k]=color.r,data[1+k]=color.g,data[2+k]=color.b,data[3+k]=color.a,this.markColorDirty(i)}markDirty(i){i<this.dirtyStart&&(this.dirtyStart=i),i>=this.dirtyEnd&&(this.dirtyEnd=i+1),i<this.boundsStart&&(this.boundsStart=i),i>=this.boundsEnd&&(this.boundsEnd=i+1)}markDirtyRange(start,end){start<this.dirtyStart&&(this.dirtyStart=start),end>this.dirtyEnd&&(this.dirtyEnd=end),start<this.boundsStart&&(this.boundsStart=start),end>this.boundsEnd&&(this.boundsEnd=end)}markColorDirty(i){i<this.colorDirtyStart&&(this.colorDirtyStart=i),i>=this.colorDirtyEnd&&(this.colorDirtyEnd=i+1)}get radius(){if(void 0!==this.fixedRadius)return this.fixedRadius;if(this.boundsEnd>this.boundsStart){var d=this.matrixData;let reachSquared=this.reachSquared,scaleSquared=this.scaleSquared;for(let i=this.boundsStart;i<this.boundsEnd;++i){var k=i*RENDER3D_MATRIX_FLOATS,x=d[12+k],y=d[13+k],z=d[14+k],x=x*x+y*y+z*z,y=render3DMaxStretchSquared(d,k);x>reachSquared&&(reachSquared=x),y>scaleSquared&&(scaleSquared=y)}this.reachSquared=reachSquared,this.scaleSquared=scaleSquared,this.boundsStart=1/0,this.boundsEnd=0}var mesh=this.mesh,mesh=mesh.radius||mesh.computeRadius();return this.reachSquared**.5+mesh*this.scaleSquared**.5}set radius(radius){this.fixedRadius=radius}render3D(){const r=render3D,gl=glContext,mesh=this.mesh;if(r.transparentQueue)return r.queueTransparent(this.getWorldPos3D(),()=>this.render3D());if(mesh&&this.count&&render3DCanDraw()&&(!r.shadowPass||r.lighting)&&(render3DMeshUpload(mesh),mesh.bufferCount)&&(!r.frustumCulling||render3DSphereVisible(0,0,0,this.radius))){if(this.uvTileInfo!==this.tileInfo){var uv=render3DGetTileUVs(this.tileInfo),data=this.colorData;for(let k=4;k<data.length;k+=RENDER3D_COLOR_FLOATS)data[k]=uv.x,data[k+1]=uv.y,data[k+2]=uv.w,data[k+3]=uv.h;this.uvTileInfo=this.tileInfo,this.colorDirtyStart=0,this.colorDirtyEnd=this.maxCount}this.buffer&&this.bufferGeneration===r.contextGeneration?((upload=(buffer,data,floats,start,end)=>{end<=start||(gl.bindBuffer(gl.ARRAY_BUFFER,buffer),gl.bufferSubData(gl.ARRAY_BUFFER,start*floats*4,data,start*floats,(end-start)*floats))})(this.buffer,this.matrixData,RENDER3D_MATRIX_FLOATS,this.dirtyStart,this.dirtyEnd),upload(this.colorBuffer,this.colorData,RENDER3D_COLOR_FLOATS,this.colorDirtyStart,this.colorDirtyEnd)):(this.bufferGeneration=r.contextGeneration,gl.bindBuffer(gl.ARRAY_BUFFER,this.buffer=gl.createBuffer()),gl.bufferData(gl.ARRAY_BUFFER,this.matrixData,gl.DYNAMIC_DRAW),gl.bindBuffer(gl.ARRAY_BUFFER,this.colorBuffer=gl.createBuffer()),gl.bufferData(gl.ARRAY_BUFFER,this.colorData,gl.DYNAMIC_DRAW)),this.dirtyStart=this.colorDirtyStart=1/0,this.dirtyEnd=this.colorDirtyEnd=0,r.flush(),render3DFlushBeforeOverlay();var upload=r.cullBackFaces,tileInfo=this.tileInfo;r.cullBackFaces=!mesh.doubleSided,render3DDrawInstanced(mesh,this.buffer,this.count,render3DTextureOf(tileInfo),r,this.colorBuffer),r.cullBackFaces=upload}}destroy(immediate){this.buffer&&this.bufferGeneration===render3D?.contextGeneration&&(glContext?.deleteBuffer(this.buffer),glContext?.deleteBuffer(this.colorBuffer)),this.buffer=this.colorBuffer=void 0,super.destroy(immediate)}}class Light3D extends EngineObject3D{constructor(pos3D=vec3(),radius=5,color=WHITE,intensity=1){super(pos3D,void 0,void 0,color),this.size3D=vec3(),this.radius=radius,this.intensity=intensity,this.directional=!1,this.coneAngle=0,this.coneSoftness=.2,this.glow=0,this.glowFalloff=1,this.gel=void 0,this.additive=!0,this.flareObject=void 0}addFlare(size,count,intensity,saturation,color){size=new LensFlare3D(size,count,intensity,saturation,color);return this.flare=size}get flare(){return this.flareObject&&!this.flareObject.destroyed?this.flareObject:void 0}set flare(flare){var old=this.flare;flare!==old&&(old&&old.destroy(),flare&&(flare.light=this,flare.parent||this.addChild(flare)),this.flareObject=flare||void 0)}destroy(immediate){this.flare=void 0,super.destroy(immediate)}render3D(){var r,c,toCamera,distance,pos;0<this.glow&&!this.directional&&(r=render3D,c=this.color,pos=render3DObjectMatrix(this).getTranslation(),toCamera=(distance=(toCamera=r.camera.pos.subtract(pos)).length())?pos.add(toCamera.scale(min(this.glow,distance)/2/distance)):pos,distance=rgb(c.r,c.g,c.b,c.a*min(this.intensity,1)),!(pos=engineGlowTexture(this.glowFalloff))&&glContext||r.drawBillboard(toCamera,vec2(this.glow),pos,distance))}}class DirectionalLight3D extends Light3D{constructor(pos3D=vec3(0,1,0),color=WHITE,intensity=1){super(pos3D,0,color,intensity),this.directional=!0}}function render3DDetach(o){o.parent?o.parent.removeChild(o):o.matrixParent&&render3DTakeWorld(o,o.worldMatrix)}let render3DSoftDotTexture;function normalMapFromHeight(size,heightFunction,strength=1){var context,width=0|size.x,size=0|size.y;return canvasAvailable()?(context=createCanvasContext(width,size),heightFunction=function(width,height,heightFunction,strength){const heights=new Float32Array(width*height),pixels=new Uint8ClampedArray(width*height*4);for(let y=0;y<height;++y)for(let x=0;x<width;++x)heights[y*width+x]=heightFunction(x,y);var h=(x,y)=>heights[mod(y,height)*width+mod(x,width)];for(let y=0;y<height;++y)for(let x=0;x<width;++x){var dx=(h(x+1,y)-h(x-1,y))/2*strength,dy=(h(x,y+1)-h(x,y-1))/2*strength,k=1/hypot(dx,dy,1),i=4*(y*width+x);pixels[i]=round(255*(-dx*k*.5+.5)),pixels[1+i]=round(255*(dy*k*.5+.5)),pixels[2+i]=round(255*(.5*k+.5)),pixels[3+i]=255}return pixels}(width,size,heightFunction,strength),context.putImageData(new ImageData(heightFunction,width,size),0,0),new TextureInfo(context.canvas,!0,!0)):((strength=new TextureInfo(void 0,!1,!0)).size=vec2(width,size),strength.sizeInverse=vec2(1/width,1/size),strength)}function render3DBevelProfile(corner,d1,d2,t,segments){var p1=[corner[0]+d1[0]*t,corner[1]+d1[1]*t],p2=[corner[0]+d2[0]*t,corner[1]+d2[1]*t];if(1===(segments=max(1,0|segments)))return[p1,p1,p2,p2];var half=Math.acos(clamp(d1[0]*d2[0]+d1[1]*d2[1],-1,1))/2,bx=d1[0]+d2[0],d1=d1[1]+d2[1],d2=t/cos(half)/hypot(bx,d1),cx=corner[0]+bx*d2,cy=corner[1]+d1*d2,radius=t*tan(half),start=atan2(p1[1]-cy,p1[0]-cx),turn=mod(atan2(p2[1]-cy,p2[0]-cx)-start+PI,2*PI)-PI,points=[p1];for(let i=1;i<segments;++i){var a=start+turn*i/segments;points.push([cx+cos(a)*radius,cy+sin(a)*radius])}return points.push(p2),points}function buildCylinder(size=1,height=1,sides=16,smooth=render3D?.smoothShading,capped=!0,bevel=0,bevelSegments=1){size/=2,height/=2,bevel=min(bevel,size,height);return buildLathe(0<bevel?[...render3DBevelProfile([size,-height],[-1,0],[0,1],bevel,bevelSegments),...render3DBevelProfile([size,height],[0,-1],[-1,0],bevel,bevelSegments)]:[[size,-height],[size,height]],sides,smooth,capped)}function buildCone(size=1,height=1,sides=16,smooth=render3D?.smoothShading,capped=!0,bevel=0,bevelSegments=1){var size=size/2,h=height/2,slant=hypot(size,height),bevel=min(bevel,size,slant/2);return buildLathe(0<bevel?[...render3DBevelProfile([size,-h],[-1,0],[-size/slant,height/slant],bevel,bevelSegments),[0,h]]:[[size,-h],[0,h]],sides,smooth,capped)}function buildCapsule(size=1,height=1,sides=16,rings=4,smooth=render3D?.smoothShading){var profile=[],r=size/2,straight=max(0,height-size)/2;for(let i=0;i<=rings;++i){var a=i/rings*PI/2;profile.push([r*sin(a),-straight-r*cos(a)])}for(let i=0;i<=rings;++i){const a=i/rings*PI/2;profile.push([r*cos(a),straight+r*sin(a)])}return buildLathe(profile,sides,smooth)}function buildTorus(size=1,tubeSize=.3,sides=16,tubeSides=8,smooth=render3D?.smoothShading){var profile=[],radius=(size-tubeSize)/2,tubeRadius=tubeSize/2;for(let i=0;i<=tubeSides;++i){var a=i/tubeSides*2*PI;profile.push([radius+tubeRadius*cos(a),tubeRadius*sin(a)])}return buildLathe(profile,sides,smooth)}function buildRibbon(points,width=1,color=WHITE,closed=!1,up=vec3(0,1,0)){var mesh=new Mesh,count=points.length,edges=[];let across=(abs(up.y)<.9?vec3(0,1,0):vec3(1,0,0)).cross(up).normalize();for(let i=0;i<count;++i){var next=points[closed?(i+1)%count:min(i+1,count-1)],last=points[closed?(i+count-1)%count:max(i-1,0)],next=next.subtract(last).cross(up),last=(across=1e-12<next.lengthSquared()?next.normalize():across).scale((isArray(width)?width[i]:width)/2);edges.push([points[i].subtract(last),points[i].add(last)])}for(let i=0;i+1<count+(closed?1:0);++i){var j=(i+1)%count,a=edges[i],b=edges[j],j=isArray(color)?[color[i],color[i],color[j],color[j]]:color;mesh.addQuad(a[0],a[1],b[1],b[0],j)}return mesh.doubleSided=!0,mesh}function buildLoft(stations){var mesh=new Mesh,section=([z,w,t,b,m=.5])=>[vec3(-w/2,lerp(b,t,m),z),vec3(0,t,z),vec3(w/2,lerp(b,t,m),z),vec3(0,b,z)];for(let i=0;i+1<stations.length;++i){var s1=section(stations[i]),s2=section(stations[i+1]);for(let k=0;k<4;++k)mesh.addQuad(s1[k],s1[(k+1)%4],s2[(k+1)%4],s2[k])}var tail=section(stations[stations.length-1]),nose=section(stations[0]);return mesh.addQuad(tail[0],tail[1],tail[2],tail[3]),mesh.addQuad(nose[3],nose[2],nose[1],nose[0]),mesh}function buildExtrude(pixels,size=vec2(1),depth=1){let rows=pixels,width,height;if(pixels instanceof TileInfo){var image=function(textureInfo){textureInfo=textureInfo.image;let pixels=render3DPixelCache.get(textureInfo);pixels||render3DPixelCache.set(textureInfo,pixels=render3DImageData(textureInfo));return pixels}(pixels.textureInfo),data=image.data,x0=0|pixels.pos.x,y0=0|pixels.pos.y;width=0|pixels.size.x,height=0|pixels.size.y,rows=[];for(let y=0;y<height;++y){var row=rows[y]=[];for(let x=0;x<width;++x){var k=4*((y0+y)*image.width+x0+x);row.push(127<data[3+k]?rgb(data[k]/255,data[1+k]/255,data[2+k]/255):void 0)}}}else height=rows.length,width=rows[0].length;const solid=(x,y)=>{if(!(x<0||y<0||x>=width||y>=height)){y=rows[y]&&rows[y][x];if(y)return isColor(y)?.5<y.a?y:void 0:WHITE}};var runs=(count,colorAt,emit)=>{let start=0,color;for(let i=0;i<=count;++i){var c=i<count?colorAt(i):void 0;a=c,b=color,a!==b&&(!a||!b||a.rgbaInt()!==b.rgbaInt())&&(color&&emit(start,i,color),start=i,color=c)}var a,b};const mesh=new Mesh,sx=size.x/width,sy=size.y/height,hz=depth/2,px=x=>x*sx-size.x/2,py=y=>size.y/2-y*sy,quad=(origin,right,up,normal,color)=>mesh.addStrip(render3DQuadAxes(origin.add(right.scale(.5)).add(up.scale(.5)),right.scale(.5),up.scale(.5)),normal,RENDER3D_QUAD_UVS,color),X=vec3(1,0,0),Y=vec3(0,1,0),Z=vec3(0,0,1);for(let y=0;y<height;++y)runs(width,x=>solid(x,y),(a,b,c)=>{var w=X.scale((b-a)*sx),h=Y.scale(sy);quad(vec3(px(a),py(y+1),hz),w,h,Z,c),quad(vec3(px(b),py(y+1),-hz),w.scale(-1),h,Z.scale(-1),c)}),runs(width,x=>solid(x,y-1)?void 0:solid(x,y),(a,b,c)=>quad(vec3(px(a),py(y),hz),X.scale((b-a)*sx),Z.scale(-depth),Y,c)),runs(width,x=>solid(x,y+1)?void 0:solid(x,y),(a,b,c)=>quad(vec3(px(a),py(y+1),-hz),X.scale((b-a)*sx),Z.scale(depth),Y.scale(-1),c));for(let x=0;x<width;++x)runs(height,y=>solid(x-1,y)?void 0:solid(x,y),(a,b,c)=>quad(vec3(px(x),py(b),-hz),Z.scale(depth),Y.scale((b-a)*sy),X.scale(-1),c)),runs(height,y=>solid(x+1,y)?void 0:solid(x,y),(a,b,c)=>quad(vec3(px(x+1),py(b),hz),Z.scale(-depth),Y.scale((b-a)*sy),X,c));return mesh}function buildText3D(text,size=1,depth=.2,font=engineImageFont){const tileInfo=font.tileInfo;let glyphs=render3DGlyphCache.get(font);glyphs||render3DGlyphCache.set(font,glyphs=new Map);const charSize=vec2(size*tileInfo.size.x/tileInfo.size.y,size),mesh=new Mesh,lines=textLines(text);return lines.forEach((line,j)=>{var y=((lines.length-1)/2-j)*charSize.y*1.3,characters=textCharacters(line);for(let i=0;i<characters.length;++i){var charCode=textCharacterCode(characters,i),charCode=charCode<32||127<charCode?95:charCode-32;if(charCode){let glyph=glyphs.get(charCode);glyph||(pos=font.getGlyphPos(charCode),glyphs.set(charCode,glyph=buildExtrude(new TileInfo(pos,tileInfo.size,tileInfo.textureInfo))));var pos,charCode=(i-(characters.length-1)/2)*charSize.x;mesh.combine(glyph,buildMatrix(vec3(charCode,y,0),void 0,vec3(charSize.x,charSize.y,depth)))}}}),mesh}class HeightMap extends EngineObject3D{constructor(heights,mapSize=vec2(1),height=1,colors,pos3D=vec3(),smooth=render3D?.smoothShading){super(pos3D),isArray(heights)||(heights=render3DImageToArray(heights,r=>r/255)),colors&&!isArray(colors)&&(colors=render3DImageToArray(colors,(r,g,b,a)=>rgb(r/255,g/255,b/255,a/255))),this.heights=heights,this.colors=colors,this.mapSize=mapSize.copy(),this.height=height,this.smooth=!!smooth,this.size3D=vec3(mapSize.x,height,mapSize.y),this.rebuild(),render3DLevel.push(this)}get rows(){return this.heights.length}get columns(){return this.heights[0].length}getHeight(x,z){x instanceof Vector3&&(z=x.z,x=x.x);var p=this.pos3D,columns=(x-=p.x,z-=p.z,this.columns),rows=this.rows,h=this.heights,x=clamp((x/this.mapSize.x+.5)*(columns-1),0,columns-1),z=clamp((z/this.mapSize.y+.5)*(rows-1),0,rows-1),columns=min(floor(x),columns-2),rows=min(floor(z),rows-2),x=x-columns,z=z-rows,a=h[rows][columns],b=h[rows+1][columns],c=h[rows+1][columns+1],h=h[rows][columns+1];return(x+z<=1?a+x*(h-a)+z*(b-a):c+(1-x)*(b-c)+(1-z)*(h-c))*this.height+p.y}getNormal(x,z){x instanceof Vector3&&(z=x.z,x=x.x);const p=this.pos3D,size=this.mapSize;var ex=size.x/(this.columns-1)/2,ez=size.y/(this.rows-1)/2;return render3DSlopeNormal((lx,lz)=>this.getHeight(lx+p.x,lz+p.z),x-p.x,z-p.z,ex,ez,size.x/2,size.y/2)}getColor(x,z){x instanceof Vector3&&(z=x.z,x=x.x);var c=this.colors;if(!c)return WHITE;x-=this.pos3D.x,z-=this.pos3D.z;var columns=c[0].length,rows=c.length,x=clamp(round((x/this.mapSize.x+.5)*(columns-1)),0,columns-1);return c[clamp(round((z/this.mapSize.y+.5)*(rows-1)),0,rows-1)][x]}raycast(ray){const p=this.pos3D,origin=ray.origin.subtract(p),direction=ray.direction;var ray=this.mapSize,height=this.height,length=direction.length();if(length){var start=raycastBox(new Ray3D(origin,direction),vec3(0,height/2,0),vec3(ray.x,abs(height)+.001,ray.y));if(void 0!==start&&(0<ray.x&&0<ray.y)){let end=start+hypot(ray.x,ray.y,height)/length;direction.x&&(end=min(end,(sign(direction.x)*ray.x/2-origin.x)/direction.x)),direction.z&&(end=min(end,(sign(direction.z)*ray.y/2-origin.z)/direction.z));var f0,f1,height=(this.columns-1)/ray.x,length=(this.rows-1)/ray.y,u0=(origin.x/ray.x+.5)*(this.columns-1),height=direction.x*height,ray=(origin.z/ray.y+.5)*(this.rows-1),length=direction.z*length,breaks=[start,end];for([f0,f1]of[[u0,height],[ray,length],[u0+ray,height+length]])if(f1){const a=f0+f1*start,b=f0+f1*end;for(let k=ceil(min(a,b));k<=max(a,b);++k)breaks.push((k-f0)/f1)}breaks.sort((a,b)=>a-b);var above=at=>{at=origin.add(direction.scale(at));return at.y+p.y-this.getHeight(at.x+p.x,at.z+p.z)},touching=d=>abs(d)<=1e-9;let a=start,da=above(a);if(touching(da))return start;var startUnder=da<=0;for(const b of breaks)if(!(b<=a||b>end)){var db=above(b);if(touching(db))return b;if(db<=0!=startUnder)return da===db?b:a+(b-a)*da/(da-db);a=b,da=db}}}}buildMesh(smooth=render3D?.smoothShading){const p=this.pos3D,size=this.mapSize,nudgeX=smooth?0:size.x/(this.columns-1)/1e3,nudgeZ=smooth?0:size.y/(this.rows-1)/1e3;return buildGrid(size,vec2(this.columns-1,this.rows-1),this.colors&&((x,z)=>this.getColor(x-nudgeX+p.x,z-nudgeZ+p.z)),(x,z)=>this.getHeight(x+p.x,z+p.z)-p.y,smooth)}rebuild(){this.setMesh(this.buildMesh(this.smooth))}levelRaycast3D(ray){return this.raycast(ray)}levelSegment3D(from,to){var top=this.pos3D.y+max(this.height,0);if(!(from.y>top&&to.y>top||from.y<this.getHeight(from.x,from.z))){top=this.raycast(new Ray3D(from,to.subtract(from)));if(!(void 0===top||1<top||!top&&to.y>=this.getHeight(to.x,to.z)))return{distance:top,normal:this.getNormal(from.x+(to.x-from.x)*top,from.z+(to.z-from.z)*top)}}}levelCollide3D(o,oldPos,wasOn){var p=o.pos3D,m=this.pos3D,size=this.mapSize;if(!(abs(p.x-m.x)>size.x/2||abs(p.z-m.z)>size.y/2)){m=o.size3D.y*abs(o.scale3D.y)/2;let ground=this.getHeight(p.x,p.z);var size=m/2,endFoot=max(p.y-m,ground)+size,size=oldPos&&this.levelSegment3D(vec3(oldPos.x,oldPos.y-m+size,oldPos.z),vec3(p.x,endFoot,p.z));if(size&&size.distance)p.x=oldPos.x+(p.x-oldPos.x)*size.distance,p.z=oldPos.z+(p.z-oldPos.z)*size.distance,ground=this.getHeight(p.x,p.z);else{endFoot=wasOn&&o.velocity3D.y<=0?hypot(p.x-oldPos.x,p.z-oldPos.z):0;if(p.y-m>ground+endFoot)return}p.y=ground+m;size=o.velocity3D;size.y<0&&(size.y*=-max(o.restitution,this.restitution)),o.groundObject=this}}update(){super.update()}destroy(immediate){render3DLevelLeave(this),super.destroy(immediate)}}function render3DImageData(image){var width=(image=image instanceof TextureInfo?image.image:image).width,height=image.height;return workReadCanvas.width=width,workReadCanvas.height=height,workReadContext.drawImage(image,0,0),workReadContext.getImageData(0,0,width,height)}function render3DImageToArray(image,sample){var{data,width,height}=render3DImageData(image),rows=[];for(let y=0;y<height;++y){var row=rows[y]=[];for(let x=0;x<width;++x){var k=4*(y*width+x);row.push(sample(data[k],data[1+k],data[2+k],data[3+k]))}}return rows}const render3DGlyphCache=new WeakMap,render3DPixelCache=new WeakMap;class CameraControl3D extends EngineObject3D{constructor(target=vec3(),distance=10,pitch=.4,idleSpin=0){super(target),this.size3D=vec3(),this.distance=distance,this.pitch=pitch,this.idleSpin=idleSpin,this.yaw=0,this.dragButton=0,this.dragSpeed=.01,this.zoomSpeed=.1,this.zoomRange=vec2(distance/4,3*distance),this.pitchRange=vec2(min(-.2,pitch),max(1.4,pitch))}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(pos3D=render3D.camera.pos,yaw=render3D.camera.rotation.y,pitch=render3D.camera.rotation.x){super(pos3D),this.size3D=vec3(),this.mass=1,this.yaw=yaw,this.pitch=pitch,this.moveSpeed=.1,this.lookSpeed=.003,this.pitchRange=vec2(-1.5,1.5),this.fly=!1,this.lockPointer=!0,this.jumpSpeed=0,this.eyeHeight=0}updatePhysics(){var gravityScale=this.gravityScale;this.fly&&(this.gravityScale=0),super.updatePhysics(),this.gravityScale=gravityScale}update(){this.lockPointer&&mouseWasPressed(0)&&pointerLockRequest(),(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 input=keyDirection(),input=vec3(input.x,0,-input.y).clampLength(1).scale(this.moveSpeed).rotateX(this.fly?this.pitch:0).rotateY(this.yaw),input=(this.velocity3D=this.fly?input:vec3(input.x,this.velocity3D.y,input.z),this.jumpSpeed&&!this.fly&&this.groundObject&&(keyWasPressed("Space")||gamepadWasPressed(0))&&(this.velocity3D.y=this.jumpSpeed),vec3(this.pitch,this.yaw,0));render3D.camera.pos=render3DObjectMatrix(this).transformPoint(vec3(0,this.eyeHeight,0)),render3D.camera.rotation=this.parent instanceof EngineObject3D?this.parent.getMatrix().multiply(Matrix4.rotation(input)).getRotation():input}destroy(immediate){this.lockPointer&&pointerLockIsActive()&&pointerLockExit(),super.destroy(immediate)}render3D(){}}const render3DParticlePos=vec3(),render3DParticleColor=new Color,render3DParticleSize=vec2(),render3DParticleFrom=vec3(),render3DParticleTo=vec3(),render3DParticlesCalling=new Set;function render3DParticleView(emitter){return{emitter:emitter,pos:vec3(),velocity:vec3(),age:0,lifeTime:0,scale:1,destroyed:!1,destroy(){this.destroyed=!0}}}class ParticleEmitter3D extends EngineObject3D{constructor(pos3D=vec3(),emitSize=0,emitTime=0,emitRate=100,emitConeAngle=PI,tileInfo,colorStartA=WHITE,colorStartB=WHITE,colorEndA=CLEAR_WHITE,colorEndB=CLEAR_WHITE,particleTime=.5,sizeStart=.1,sizeEnd=1,speed=.1,damping=1,gravity=0,fadeRate=.1,randomness=.2,additive=!1){super(pos3D,void 0,tileInfo),this.transparent=!0,this.castShadow=!1,this.size3D=vec3(),this.emitSize=emitSize,this.emitFlat=!1,this.emitTime=emitTime,this.emitRate=emitRate,this.emitConeAngle=emitConeAngle,this.colorStartA=colorStartA.copy(),this.colorStartB=colorStartB.copy(),this.colorEndA=colorEndA.copy(),this.colorEndB=colorEndB.copy(),this.particleTime=particleTime,this.sizeStart=sizeStart,this.sizeEnd=sizeEnd,this.speed=speed,this.damping=damping,this.gravity=gravity,this.gravityScale=0,this.fadeRate=fadeRate,this.randomness=randomness,this.additive=additive,this.trailTime=0,this.angleSpeed=0,this.angleDamping=1,this.particleData=new Float32Array(1344),this.particleCount=0,this.trailData=void 0,this.trailMax=0,this.collideLevel=!1,this.stick=0,this.particleCreateCallback=void 0,this.particleUpdateCallback=void 0,this.particleCollideCallback=void 0,this.particleDestroyCallback=void 0,this.particleView=render3DParticleView(this),this.worldPos3D=void 0,this.emitTimeBuffer=1,this.emitElapsed=0,this.emitFrom=void 0,this.emitBehind=0}update(){var matrix=render3DObjectMatrix(this),matrix=(this.emitFrom=this.worldPos3D,this.worldPos3D=matrix.getTranslation(),render3DMaxScale(matrix.m));if(this.particleView.scale=matrix,!this.emitTime||this.emitElapsed<this.emitTime){var rate=this.emitRate*particleEmitRateScale,step=this.emitTime?min(timeDelta,this.emitTime-this.emitElapsed):timeDelta;if(this.emitElapsed+=timeDelta,0<rate&&rate<1/0){this.emitTimeBuffer+=rate*step+(this.emitTime&&this.emitElapsed>=this.emitTime?1e-6:0);var travelled=this.emitFrom&&this.worldPos3D.distance(this.emitFrom)<=2*objectMaxSpeed,count=floor(this.emitTimeBuffer);try{for(let i=count;i--;--this.emitTimeBuffer)this.emitBehind=travelled?i/count:0,this.emitParticle()}finally{this.emitBehind=0}}}else this.particleCount||this.destroy();var trailMax=this.trailTime?max(1,round(this.trailTime*frameRate)):0;if(trailMax!==this.trailMax){this.trailMax=trailMax,this.trailData=trailMax?new Float32Array(this.particleData.length/21*trailMax*3):void 0;for(let k=20;k<this.particleData.length;k+=21)this.particleData[k]=0}let data=this.particleData,trail=this.trailData;var reread=()=>{data=this.particleData,trail=this.trailData},damping=this.damping,angleDamping=this.angleDamping,rate=render3D.gravity,step=this.gravityScale,gravityX=rate.x*step*matrix,gravityY=(this.gravity+rate.y*step)*matrix,gravityZ=rate.z*step*matrix,collideLevel=this.collideLevel&&enablePhysicsSolver&&render3DLevel.length,updateCallback=this.particleUpdateCallback,destroyCallback=this.particleDestroyCallback;for(let i=this.particleCount;i--;){var k=21*i,vx=data[3+k]=data[3+k]*damping+gravityX,vy=data[4+k]=data[4+k]*damping+gravityY,vz=data[5+k]=data[5+k]*damping+gravityZ,vx=(data[k]+=vx,data[1+k]+=vy,data[2+k]+=vz,data[18+k]+=data[19+k]*=angleDamping,collideLevel&&this.particleCollide(k,data[k]-vx,data[1+k]-vy,data[2+k]-vz)),vy=(collideLevel&&reread(),i*trailMax*3);if(trailMax){let n=data[20+k];n===trailMax&&(trail.copyWithin(vy,3+vy,vy+3*n),--n);var vz=vy+3*n;trail[vz]=data[k],trail[1+vz]=data[1+k],trail[2+vz]=data[2+k],data[20+k]=n+1}updateCallback&&!vx&&(this.particleCall(updateCallback,k),reread()),(data[17+k]+=timeDelta)>=data[16+k]&&(destroyCallback&&(this.particleCall(destroyCallback,k),reread()),vx=21*(vz=--this.particleCount),i!==vz)&&(data.copyWithin(k,vx,21+vx),trailMax)&&trail.copyWithin(vy,vz*trailMax*3,(1+vz)*trailMax*3)}}destroy(immediate){if(immediate||!this.particleCount||this.destroyed)return super.destroy(immediate);this.emitTime=-1,render3DDetach(this)}emitParticle(){var random=()=>rand(1-this.randomness,1+this.randomness),matrix=render3DObjectMatrix(this),scale=render3DMaxScale(matrix.m),size=this.emitSize,box=size,flat=this.emitFlat;let offset;offset=isVector3(size)?vec3(rand(-.5,.5)*box.x,flat?0:rand(-.5,.5)*box.y,rand(-.5,.5)*box.z):flat?vec3((box=randInCircle(size/2)).x,0,box.y):randInSphere(size/2);var flat=matrix.transformDirection(randVector3(1,this.emitConeAngle)).normalize(),box=matrix.transformPoint(offset),size=this.speed*random()*scale,matrix=this.emitFrom,behind=this.emitBehind,matrix=(matrix&&behind&&(here=this.worldPos3D,box.x+=(matrix.x-here.x)*behind,box.y+=(matrix.y-here.y)*behind,box.z+=(matrix.z-here.z)*behind),randColor(this.colorStartA,this.colorStartB,!0)),here=randColor(this.colorEndA,this.colorEndB,!0),behind=21*this.particleCount++,grown=(21+behind>this.particleData.length&&((grown=new Float32Array(2*this.particleData.length)).set(this.particleData),this.particleData=grown,this.trailData)&&((grown=new Float32Array(2*this.trailData.length)).set(this.trailData),this.trailData=grown),this.particleData);grown[behind]=box.x,grown[1+behind]=box.y,grown[2+behind]=box.z,grown[3+behind]=flat.x*size,grown[4+behind]=flat.y*size,grown[5+behind]=flat.z*size,grown[6+behind]=matrix.r,grown[7+behind]=matrix.g,grown[8+behind]=matrix.b,grown[9+behind]=matrix.a,grown[10+behind]=here.r,grown[11+behind]=here.g,grown[12+behind]=here.b,grown[13+behind]=here.a,grown[14+behind]=this.sizeStart*random()*scale,grown[15+behind]=this.sizeEnd*random()*scale,grown[16+behind]=this.particleTime*random(),grown[17+behind]=0,grown[18+behind]=this.angleSpeed?rand(2*PI):0,grown[19+behind]=this.angleSpeed?this.angleSpeed*random()*randSign():0,grown[20+behind]=0,this.particleView.scale=scale,this.particleCreateCallback&&this.particleCall(this.particleCreateCallback,behind)}particleCall(callback,k,level,pos){var nested=render3DParticlesCalling.has(this),view=nested?render3DParticleView(this):this.particleView,data=this.particleData;view.scale=this.particleView.scale,view.pos.set(data[k],data[k+1],data[k+2]),view.velocity.set(data[k+3],data[k+4],data[k+5]),view.age=data[k+17],view.lifeTime=data[k+16],view.destroyed=!1,render3DParticlesCalling.add(this);let result;try{result=callback(view,level,pos)}finally{nested||render3DParticlesCalling.delete(this)}data=this.particleData;return data[k]=view.pos.x,data[k+1]=view.pos.y,data[k+2]=view.pos.z,data[k+3]=view.velocity.x,data[k+4]=view.velocity.y,data[k+5]=view.velocity.z,view.destroyed&&(data[k+17]=max(data[k+17],data[k+16])),result}particleCollide(k,x,y,z){var data=this.particleData,from=render3DParticleFrom.set(x,y,z),to=render3DParticleTo.set(data[k],data[k+1],data[k+2]);let hit,level;for(const l of render3DLevel){var h=l.destroyed?void 0:l.levelSegment3D(from,to);h&&(!hit||h.distance<hit.distance)&&(hit=h,level=l)}if(hit){x=from.lerp(to,hit.distance),y=this.particleCollideCallback;if(y&&(!this.particleCall(y,k,level,x.copy())||this.particleView.destroyed))return this.particleView.destroyed;var data=this.particleData,z=hit.normal,y=vec3(data[k+3],data[k+4],data[k+5]),into=z.scale(y.dot(z)),restitution=max(this.restitution,level.restitution),friction=max(this.friction,level.friction)*(1-clamp(this.stick)),y=y.subtract(into).scale(friction).subtract(into.scale(restitution)),friction=x.add(z.scale(.001));data[k]=friction.x,data[k+1]=friction.y,data[k+2]=friction.z,data[k+3]=y.x,data[k+4]=y.y,data[k+5]=y.z}return!1}render3D(){var r=render3D;if(r.transparentQueue)return r.queueTransparent(this.getWorldPos3D(),()=>this.render3D());var fade=this.fadeRate/2,texture=this.tileInfo||function(){if(render3DSoftDotTexture||!glContext||!canvasAvailable())return render3DSoftDotTexture;var stop,alpha,context=createCanvasContext(32),gradient=context.createRadialGradient(16,16,0,16,16,16);for([stop,alpha]of[[0,1],[.33,.9],[.67,.7],[1,0]])gradient.addColorStop(stop,"rgba(255,255,255,"+alpha+")");return context.fillStyle=gradient,context.fillRect(0,0,32,32),render3DSoftDotTexture=new TextureInfo(context.canvas)}(),color=render3DParticleColor,size=render3DParticleSize,quad=r.billboardMesh,instanced=texture&&r.instancing&&!r.capture&&render3DCanDraw();let data,textureInfo,uv,lit;instanced&&(render3DMeshUpload(quad),textureInfo=render3DTextureOf(texture),uv=render3DGetTileUVs(texture),lit=r.lighting,r.lighting=r.shadowPass&&lit,r.cullBackFaces=r.mirrored=!1);var cb=r.cameraBack,particles=this.particleData,trailMax=this.trailMax,pos=render3DParticlePos;try{for(let i=0,count=this.particleCount;i<count;++i){var a,k,p=21*i,t=particles[17+p]/particles[16+p],alpha=t<fade?t/fade:1-fade<t?(1-t)/fade:1,s=(color.r=particles[6+p]+(particles[10+p]-particles[6+p])*t,color.g=particles[7+p]+(particles[11+p]-particles[7+p])*t,color.b=particles[8+p]+(particles[12+p]-particles[8+p])*t,color.a=(particles[9+p]+(particles[13+p]-particles[9+p])*t)*alpha,size.x=size.y=lerp(particles[14+p],particles[15+p],t)),angle=particles[18+p],trailCount=particles[20+p];if(1<trailCount){var trail=this.trailData,points=[],widths=[],colors=[];for(let j=0;j<trailCount;++j){var f=(j+1)/trailCount,q=3*(i*trailMax+j);points.push(vec3(trail[q],trail[1+q],trail[2+q])),widths.push(s*f),colors.push(color.scale(1,f))}r.drawRibbon(points,widths,this.tileInfo,colors)}else instanced?(a=render3DBillboardAxes(size,angle,!1),k=render3DInstanceSlot(quad,textureInfo),(data=quad.instanceData)[k]=2*a[0],data[k+1]=2*a[1],data[k+2]=2*a[2],data[k+3]=0,data[k+4]=2*a[3],data[k+5]=2*a[4],data[k+6]=2*a[5],data[k+7]=0,data[k+8]=cb.x,data[k+9]=cb.y,data[k+10]=cb.z,data[k+11]=0,data[k+12]=particles[p],data[k+13]=particles[1+p],data[k+14]=particles[2+p],data[k+15]=1,data[k+16]=color.r,data[k+17]=color.g,data[k+18]=color.b,data[k+19]=color.a,data[k+20]=uv.x,data[k+21]=uv.y,data[k+22]=uv.w,data[k+23]=uv.h):(pos.x=particles[p],pos.y=particles[1+p],pos.z=particles[2+p],texture?r.drawBillboard(pos,size,texture,color,angle):r.drawSoftDisc(pos,s,color,void 0,8))}}finally{instanced&&(r.lighting=lit,r.flush(),render3DFlushInstances(quad))}}}class Trail3D extends EngineObject3D{constructor(pos3D=vec3(),lifeTime=1,width=.2,tileInfo,color=WHITE,colorEnd=CLEAR_WHITE,additive=!1){super(pos3D,void 0,tileInfo,color),this.transparent=!0,this.additive=additive,this.castShadow=!1,this.size3D=vec3(),this.finishing=!1,this.lifeTime=lifeTime,this.width=width,this.colorEnd=colorEnd.copy(),this.side=void 0,this.samples=[]}clear(){this.samples.length=0}destroy(immediate){if(immediate||!this.samples.length||this.destroyed||this.lifeTime==1/0)return super.destroy(immediate);this.finishing=!0,render3DDetach(this)}update(){var pos,last,samples=this.samples;for(!this.finishing&&(pos=this.worldPos3D=this.getWorldPos3D(),!(last=samples[samples.length-1])||1e-8<pos.distanceSquared(last.pos))&&samples.push({pos:pos,side:this.side?.copy(),time:time});samples.length&&time-samples[0].time>this.lifeTime;)samples.shift();this.finishing&&!samples.length&&this.destroy()}render3D(){var samples=this.samples;if(!(samples.length<2)){var points=[],widths=[],colors=[],sides=this.side?[]:void 0;for(const s of samples){var age=clamp((time-s.time)/this.lifeTime);points.push(s.pos),widths.push(this.width*(1-age)),colors.push(this.color.lerp(this.colorEnd,age)),sides?.push(s.side)}render3D.drawRibbon(points,widths,this.tileInfo,colors,sides)}}}const render3DFlareShapes=["glow","disc","ring","hex","streak","star"];let render3DFlareTiles;class LensFlare3D extends EngineObject3D{constructor(size=1,count=7,intensity=1,saturation=1,color=WHITE){super(vec3(),void 0,void 0,color),this.size3D=vec3(),this.flareSize=size,this.count=count,this.intensity=intensity,this.saturation=saturation,this.seed=1,this.shapes=void 0,this.glowSize=1,this.ghostSize=1,this.elements=void 0,this.light=void 0,this.occlusion=!0,this.fadeTime=.15,this.visible=0,this.visibleFound=!1,this.renderOrder=1e9,this.madeKey="",this.made=[]}getElements(){if(this.elements)return this.elements;var shapes=this.shapes?.length?this.shapes:void 0,glow=this.glowSize,ghost=this.ghostSize,key=[this.count,this.seed,this.saturation,glow,ghost,shapes?.length,shapes].join();if(key!==this.madeKey){var random=new RandomGenerator(7919*this.seed+1),s=clamp(this.saturation),made=this.made=0<glow?[{at:0,size:.7*glow,color:hsl(0,0,1,.5),shape:"glow"},{at:0,size:.25*glow,color:hsl(0,0,1,.9),shape:"glow"}]:[];for(let i=0;i<this.count;++i){var at=(i+random.float(.2,.8))/max(this.count,1)*1.9+.2,pick=random.float(),pick=shapes?shapes[min(pick*shapes.length|0,shapes.length-1)]:pick<.5?"disc":pick<.8?"ring":"glow";made.push({at:at,size:random.float(.04,.1)*(1+at)*ghost,shape:pick,color:hsl(random.float(),.9*s,.6,random.float(.15,.4))})}this.madeKey=key}return this.made}flareSource(){var from,camera=render3D.camera.pos,light=this.light;if(!light?.destroyed)return!light||light.directional?(from=light?light.getWorldPos3D():render3D.sunDirection).lengthSquared()?{direction:from=from.normalize(),distance:1/0,pos:camera.add(from.scale(100))}:void 0:(camera=(light=(from=light.getWorldPos3D()).subtract(camera)).length())?{direction:light.scale(1/camera),distance:camera,pos:from}:void 0}flareLook(){var source=this.flareSource(),center=mainCanvasSize.scale(.5),sun=source&&render3D.worldToScreen(source.pos);if(sun&&center.x&&center.y){var strength=clamp((1.3-max(abs(sun.x-center.x)/center.x,abs(sun.y-center.y)/center.y))/.5)*this.intensity;if(0<strength){let tint=this.color.multiply(render3D.sunColor),height=mainCanvasSize.y*this.flareSize;var light=this.light;if(light){var cone=render3DLightCone(light),d=source.direction,d=clamp(-(cone[0]*d.x+cone[1]*d.y+cone[2]*d.z)-cone[3]),cone=light.color,d=cone.a*clamp(light.intensity)*d;if(!(0<d))return;tint=this.color.multiply(rgb(cone.r,cone.g,cone.b,d)),light.directional||(height*=min(1,light.radius/source.distance))}return 0<height?{sun:sun,center:center,strength:strength,tint:tint,height:height}:void 0}}}getScreenPos(){var source=this.flareSource();return source&&render3D.worldToScreen(source.pos)}getScreenElements(){this.visibleFound&&!paused||(this.visibleFound=!1,this.updateVisible(0));var look=0<this.visible&&this.flareLook();if(!look)return[];const{sun,center,tint,height}=look,strength=look.strength*this.visible;return this.getElements().map(e=>{var c=e.color.multiply(tint),size="number"==typeof e.size?e.size*height:e.size.scale(height);return{pos:sun.add(center.subtract(sun).scale(e.at)),size:size,shape:e.shape,tileInfo:e.tileInfo,angle:e.angle||0,color:rgb(c.r,c.g,c.b,c.a*strength)}})}isHidden(){const source=this.flareSource();if(!source)return!0;var ray=new Ray3D(render3D.camera.pos,source.direction),reach=source.distance,blockers=engineObjects.filter(o=>o!==this&&o instanceof EngineObject3D&&!o.destroyed&&!o.transparent&&!render3DSetting(o,"additive"));const maps=void 0===VoxelMap?[]:blockers.filter(o=>o instanceof VoxelMap);for(const map of maps)if(map.raycast(ray,reach,type=>!map.blockType(type).seeThrough))return!0;const near=this.light&&reach<1/0?min(2,.4*this.light.radius):0;let candidates=blockers.filter(o=>!maps.some(map=>map===o));for(let tries=5;tries--;){const hit=render3D.pick(ray,candidates);if(!hit||hit.distance>=reach)return!1;if(!(reach-hit.distance<=near&&(o=>{if(!o.mesh||o instanceof HeightMap)return!1;var matrix=render3DObjectMatrix(o),o=o.mesh.bounds||o.mesh.getBounds();const m=matrix.m,scale=k=>hypot(m[k],m[k+1],m[k+2]);var thinnest=min((o.max.x-o.min.x)*scale(0),(o.max.y-o.min.y)*scale(4),(o.max.z-o.min.z)*scale(8));return!(!matrix.determinant()||thinnest>2*near)&&(thinnest=matrix.copy().invert().transformPoint(source.pos)).x>=o.min.x&&thinnest.x<=o.max.x&&thinnest.y>=o.min.y&&thinnest.y<=o.max.y&&thinnest.z>=o.min.z&&thinnest.z<=o.max.z})(hit.object)))return!0;candidates=candidates.filter(o=>o!==hit.object)}return!0}update(){if(super.update(),this.light?.destroyed)return this.destroy();this.updateVisible(timeDelta)}updateVisible(seconds){this.visibleFound||render3D.updateMatrices();var shows=!(!this.flareLook()||this.occlusion&&this.isHidden()),seconds=0<this.fadeTime&&this.visibleFound?seconds/this.fadeTime:1;this.visible=clamp(this.visible+(shows?seconds:-seconds)),this.visibleFound=!0}render(){if(!headlessMode&&glEnable){var elements=this.getScreenElements();if(elements.length){setAdditiveBlendMode(!0);for(const e of elements){var tileInfo=e.tileInfo||function(shape){if(!render3DFlareTiles){if(headlessMode||!glContext||!canvasAvailable())return;const shapes=render3DFlareShapes,context=createCanvasContext(128*shapes.length,128),alpha={glow:t=>engineGlowAlpha(t,1),disc:t=>t<.8?.4+.1*t:t<.92?.48+3*(t-.8):(1-t)/.08*.84,ring:t=>max(0,1-abs(t-.86)/.12),hex:t=>t<.8?.4+.1*t:t<.92?.48+3*(t-.8):(1-t)/.08*.84,streak:t=>(1-t)**1.5},fill=(shape,r,path)=>{var gradient=context.createRadialGradient(0,0,0,0,0,r);for(let k=0;k<=32;++k)gradient.addColorStop(k/32,"rgba(255,255,255,"+clamp(alpha[shape](k/32)).toFixed(4)+")");context.fillStyle=gradient,context.beginPath(),path?path():context.arc(0,0,r,0,2*PI),context.fill()},ray=(r,angle)=>{context.save(),context.rotate(angle),context.scale(1,.07),fill("streak",r),context.restore()},texture=(shapes.forEach((shape,i)=>{if(context.save(),context.translate(128*i+64,64),"hex"==shape){var image=context.createImageData(128,128),data=image.data,apothem=62*cos(PI/6);for(let k=0;k<16384;++k){var x=abs(k%128+.5-64),y=abs(.5+(k/128|0)-64),x=max(y,x*cos(PI/6)+y/2)/apothem;data[4*k]=data[4*k+1]=data[4*k+2]=255,data[4*k+3]=x<1?255*clamp(alpha.hex(x)):0}context.putImageData(image,128*i,0)}else if("streak"==shape)ray(62,0);else if("star"==shape){for(let k=3;k--;)ray(62,k*PI/3+PI/2);context.scale(.3,.3),fill("glow",62)}else fill(shape,62);context.restore()}),new TextureInfo(context.canvas,!0,!1,!1));render3DFlareTiles=new Map(shapes.map((shape,i)=>[shape,new TileInfo(vec2(128*i,0),vec2(128),texture)]))}return render3DFlareTiles.get(shape)||render3DFlareTiles.get("glow")}(e.shape),size="number"==typeof e.size?vec2(e.size):e.size;tileInfo&&drawTile(e.pos,size,tileInfo,e.color,e.angle,!1,void 0,!0,!0)}setAdditiveBlendMode(!1)}}}render3D(){}}function parseOBJ(text,smooth=render3D?.smoothShading){const positions=[],normals=[],uvs=[],points=[],vertexNormals=[],vertexUVs=[],indices=[],seen=new Map,fromFile=[];let missingNormals=!1,face=0;const index=(s,list)=>{s=parseInt(s);return s<0?list.length+s:s-1},lookup=(s,list)=>list[index(s,list)];for(const line of text.replace(/\\\r?\n/g," ").split(/\r\n?|\n/)){var parts=line.replace(/#.*/,"").trim().split(/\s+/);switch(parts[0]){case"v":positions.push(vec3(+parts[1],+parts[2],+parts[3]));break;case"vn":normals.push(vec3(+parts[1],+parts[2],+parts[3]));break;case"vt":uvs.push(vec2(+parts[1],1-(+parts[2]||0)));break;case"f":var corners=parts.slice(1).map(c=>c.split("/"));if(!(corners.length<3)){const facePoints=corners.map(c=>lookup(c[0],positions));if(facePoints.every(isVector3)){const hasNormals=corners.every(c=>c[2]),faceNormal=(missingNormals||=!hasNormals,hasNormals?void 0:render3DFaceNormal(facePoints[0],facePoints[1],facePoints[2],facePoints[3]));var ids=corners.map((c,i)=>{var key=index(c[0],positions)+"/"+(c[1]?index(c[1],uvs):"")+"/"+(hasNormals?index(c[2],normals):smooth?"":"f"+face);let id=seen.get(key);return void 0===id&&(seen.set(key,id=points.length),points.push(facePoints[i]),vertexUVs.push(c[1]?lookup(c[1],uvs):RENDER3D_DEFAULT_UV),vertexNormals.push(hasNormals?lookup(c[2],normals):faceNormal),fromFile.push(hasNormals)),id});for(let k=2;k<ids.length;++k)indices.push(ids[1],ids[k],ids[(k+1)%ids.length]);++face}}}}const mesh=(new Mesh).addTriangles(points,indices,vertexNormals,vertexUVs);if(missingNormals&&smooth){const given=mesh.normals;mesh.computeNormals(!0),fromFile.forEach((f,i)=>f&&(mesh.normals[i]=given[i]))}return mesh}async function loadOBJ(url,smooth=render3D?.smoothShading){var text,response=await loadFetch(url,"loadOBJ");if(response.ok)return(smooth=parseOBJ(text=await response.text(),smooth)).points.length||console.warn("loadOBJ: "+url+" has no faces"+(/^\s*</.test(text)?", it is a web page, so the path may be wrong":"")),smooth;throw new Error(`loadOBJ: could not load ${url}, ${response.status} `+response.statusText)}const RENDER3D_CSG_EPSILON=1e-5;class Render3DCSGVertex{constructor(pos,normal,uv,color){this.pos=pos,this.normal=normal,this.uv=uv,this.color=color}lerp(v,t){return new Render3DCSGVertex(this.pos.lerp(v.pos,t),this.normal.lerp(v.normal,t),this.uv.lerp(v.uv,t),this.color.lerp(v.color,t))}flipped(){return new Render3DCSGVertex(this.pos,this.normal.scale(-1),this.uv,this.color)}}class Render3DCSGPolygon{constructor(vertices,plane){this.vertices=vertices,this.plane=plane}flipped(){var plane={normal:this.plane.normal.scale(-1),w:-this.plane.w};return new Render3DCSGPolygon(this.vertices.map(v=>v.flipped()).reverse(),plane)}}class Render3DCSGNode{constructor(){this.plane=void 0,this.front=void 0,this.back=void 0,this.polygons=[]}}function render3DCSGSplit(plane,polygon,coplanarFront,coplanarBack,front,back){var{normal,w}=plane,vertices=polygon.vertices,types=[];let polygonType=0;for(const v of vertices){var t=normal.dot(v.pos)-w,t=t<-RENDER3D_CSG_EPSILON?2:t>RENDER3D_CSG_EPSILON?1:0;polygonType|=t,types.push(t)}if(0===polygonType)(0<normal.dot(polygon.plane.normal)?coplanarFront:coplanarBack).push(polygon);else if(1===polygonType)front.push(polygon);else if(2===polygonType)back.push(polygon);else{var f=[],b=[];for(let i=0;i<vertices.length;++i){var j=(i+1)%vertices.length,ti=types[i],tj=types[j],vi=vertices[i],j=vertices[j];2!==ti&&f.push(vi),1!==ti&&b.push(vi),3==(ti|tj)&&(ti=vi.lerp(j,(w-normal.dot(vi.pos))/normal.dot(j.pos.subtract(vi.pos))),f.push(ti),b.push(ti))}3<=f.length&&front.push(new Render3DCSGPolygon(f,polygon.plane)),3<=b.length&&back.push(new Render3DCSGPolygon(b,polygon.plane))}}function render3DCSGBuild(root,polygons){for(var stack=[[root,polygons]];stack.length;){var[node,list]=stack.pop();if(list.length){node.plane||=list[0].plane;var front=[],back=[];for(const polygon of list)render3DCSGSplit(node.plane,polygon,node.polygons,node.polygons,front,back);front.length&&stack.push([node.front||=new Render3DCSGNode,front]),back.length&&stack.push([node.back||=new Render3DCSGNode,back])}}}function render3DCSGInvert(root){for(const node of function(root){for(var nodes=[],stack=[root];stack.length;){var node=stack.pop();nodes.push(node),node.front&&stack.push(node.front),node.back&&stack.push(node.back)}return nodes}(root))node.plane&&={normal:node.plane.normal.scale(-1),w:-node.plane.w},[node.front,node.back]=[node.back,node.front]}function render3DCSGClipPolygons(root,polygons){for(var result=[],stack=[[root,polygons]];stack.length;){var[node,list]=stack.pop();if(node.plane){var front=[],back=[];for(const polygon of list)render3DCSGSplit(node.plane,polygon,front,back,front,back);if(node.front)stack.push([node.front,front]);else for(const polygon of front)result.push(polygon);node.back&&stack.push([node.back,back])}else for(const polygon of list)result.push(polygon)}return result}function render3DCSGNear(polygons,polygonsOther){var lo=vec3(1/0),hi=vec3(-1/0);for(const polygon of polygonsOther)for(var{pos}of polygon.vertices)lo.x=min(lo.x,pos.x),lo.y=min(lo.y,pos.y),lo.z=min(lo.z,pos.z),hi.x=max(hi.x,pos.x),hi.y=max(hi.y,pos.y),hi.z=max(hi.z,pos.z);var near=[],far=[],faces=[[vec3(1,0,0),hi.x],[vec3(-1,0,0),-lo.x],[vec3(0,1,0),hi.y],[vec3(0,-1,0),-lo.y],[vec3(0,0,1),hi.z],[vec3(0,0,-1),-lo.z]],e=RENDER3D_CSG_EPSILON;for(const polygon of polygons){let x0=1/0,y0=1/0,z0=1/0,x1=-1/0,y1=-1/0,z1=-1/0;for(const{pos}of polygon.vertices)x0=min(x0,pos.x),y0=min(y0,pos.y),z0=min(z0,pos.z),x1=max(x1,pos.x),y1=max(y1,pos.y),z1=max(z1,pos.z);if(x0>hi.x+e||x1<lo.x-e||y0>hi.y+e||y1<lo.y-e||z0>hi.z+e||z1<lo.z-e)far.push(polygon);else{let inside=[polygon];for(var[normal,w]of faces){var next=[];for(const part of inside)render3DCSGSplit({normal:normal,w:w},part,next,next,far,next);inside=next}for(const part of inside)near.push(part)}}return[near,far]}function render3DCSGPolygons(mesh,matrix){const list=(new Mesh).combine(mesh,matrix).toIndexed();var vertex=i=>new Render3DCSGVertex(list.points[i],list.normals[i],list.uvs[i],list.colors[i]),polygons=[];for(let t=0;t<list.indices.length;t+=3){var i=list.indices[t],j=list.indices[t+1],k=list.indices[t+2],plane=function(a,b,c){if(!((b=b.subtract(a).cross(c.subtract(a))).lengthSquared()<1e-18))return{normal:c=b.normalize(),w:c.dot(a)}}(list.points[i],list.points[j],list.points[k]);plane&&polygons.push(new Render3DCSGPolygon([vertex(i),vertex(j),vertex(k)],plane))}return polygons}function render3DCSGMesh(polygons){const reach=4*RENDER3D_CSG_EPSILON,placer=function(distance){const cells=new Map;return p=>{var cx=floor(p.x/distance),cy=floor(p.y/distance),cz=floor(p.z/distance);for(let x=cx-1;x<=cx+1;++x)for(let y=cy-1;y<=cy+1;++y)for(let z=cz-1;z<=cz+1;++z)for(const q of cells.get(x+","+y+","+z)||[])if(q.distanceSquared(p)<distance**2)return q;var k=cx+","+cy+","+cz;return cells.has(k)||cells.set(k,[]),cells.get(k).push(p),p}}(RENDER3D_CSG_EPSILON),placed=new Set;var polygons=polygons.map(polygon=>{var loop=[];for(const v of polygon.vertices){var pos=(p=>{p=placer(p);return placed.add(p),p})(v.pos);loop.length&&loop[loop.length-1].pos===pos||loop.push(new Render3DCSGVertex(pos,v.normal,v.uv,v.color))}return 1<loop.length&&loop[0].pos===loop[loop.length-1].pos&&loop.pop(),loop}),places=[...placed];let lo=vec3(1/0),hi=vec3(-1/0);for(const p of places)lo=vec3(min(lo.x,p.x),min(lo.y,p.y),min(lo.z,p.z)),hi=vec3(max(hi.x,p.x),max(hi.y,p.y),max(hi.z,p.z));var span=places.length?max(hi.x-lo.x,hi.y-lo.y,hi.z-lo.z):0;const size=max(span/Math.cbrt(places.length||1),reach),grid=new Map;for(const p of places){var k=(p=>floor((p.x-lo.x)/size)+","+floor((p.y-lo.y)/size)+","+floor((p.z-lo.z)/size))(p);grid.has(k)||grid.set(k,[]),grid.get(k).push(p)}const mesh=new Mesh,shared=new Map;mesh.indices=[];for(const loop of polygons)if(!(loop.length<3)){const full=[];for(let i=0;i<loop.length;++i){var t,p,a=loop[i],b=loop[(i+1)%loop.length];full.push(a);for([t,p]of((a,b)=>{var t,found=[],ab=b.subtract(a),length2=ab.lengthSquared(),x0=floor((min(a.x,b.x)-lo.x-reach)/size),x1=floor((max(a.x,b.x)-lo.x+reach)/size),y0=floor((min(a.y,b.y)-lo.y-reach)/size),y1=floor((max(a.y,b.y)-lo.y+reach)/size),z0=floor((min(a.z,b.z)-lo.z-reach)/size),z1=floor((max(a.z,b.z)-lo.z+reach)/size);for(let x=x0;x<=x1;++x)for(let y=y0;y<=y1;++y)for(let z=z0;z<=z1;++z)for(const p of grid.get(x+","+y+","+z)||[])p===a||p===b||(t=p.subtract(a).dot(ab)/length2)<=0||1<=t||a.add(ab.scale(t)).distanceSquared(p)<reach**2&&found.push([t,p]);return found.sort((f,g)=>f[0]-g[0])})(a.pos,b.pos)){var v=a.lerp(b,t);full.push(new Render3DCSGVertex(p,v.normal,v.uv,v.color))}}const n=full.length,pos=i=>full[i%n].pos;let area=vec3();for(let j=1;j<n-1;++j)area=area.add(pos(j).subtract(pos(0)).cross(pos(j+1).subtract(pos(0))));if(!(area.lengthSquared()<1e-18)){const ring=full.map(v=>({pos:v.pos,id:(v=>{var n=v.normal.lengthSquared()?v.normal.normalize():RENDER3D_DEFAULT_NORMAL,c=v.color,key=[n.x,n.y,n.z,v.uv.x,v.uv.y,c.r,c.g,c.b,c.a].map(x=>round(1e6*x)).join();let atPlace=shared.get(v.pos),i;return atPlace||shared.set(v.pos,atPlace=new Map),void 0===(i=atPlace.get(key))&&(atPlace.set(key,i=mesh.points.length),mesh.points.push(v.pos.copy()),mesh.normals.push(n),mesh.uvs.push(v.uv.copy()),mesh.colors.push(c.copy())),i})(v)})),earArea=i=>{var a=ring[(i+ring.length-1)%ring.length].pos,b=ring[i].pos,i=ring[(i+1)%ring.length].pos;return b.subtract(a).cross(i.subtract(a))};for(;3<ring.length;){let i=ring.findIndex((_,i)=>{i=earArea(i);return 1e-18<=i.lengthSquared()&&1e-18<=area.subtract(i).lengthSquared()});i<0&&(i=0),area=area.subtract(earArea(i)),mesh.indices.push(ring[(i+ring.length-1)%ring.length].id,ring[i].id,ring[(i+1)%ring.length].id),ring.splice(i,1)}mesh.indices.push(ring[0].id,ring[1].id,ring[2].id)}}return mesh.dirty=!0,mesh}function render3DMeshCSG(a,b,matrix,operation){var a=render3DCSGPolygons(a),b=render3DCSGPolygons(b,matrix),[matrix,farA]=render3DCSGNear(a,b),[nearB,farB]=render3DCSGNear(b,a),treeA=new Render3DCSGNode,treeB=new Render3DCSGNode,a=(render3DCSGBuild(treeA,a),render3DCSGBuild(treeB,b),render3DCSGClipPolygons),b=list=>list.map(polygon=>polygon.flipped());let polygons;return render3DCSGMesh(polygons=0===operation?[...farA,...a(treeB,matrix),...farB,...b(a(treeA,b(a(treeA,nearB))))]:1===operation?(render3DCSGInvert(treeA),[...farA,...b(a(treeB,b(matrix))),...a(treeA,b(a(treeA,nearB)))]):(render3DCSGInvert(treeA),render3DCSGInvert(treeB),[...b(a(treeB,b(matrix))),...b(a(treeA,b(a(treeA,nearB))))]))}const RENDER3D_VOXEL_FACES=[[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)],[vec3(0,0,1),vec3(1,0,0),vec3(0,1,0)],[vec3(0,0,-1),vec3(-1,0,0),vec3(0,1,0)]],RENDER3D_VOXEL_SHADES=[.55,.7,.85,1].map(v=>Object.freeze(rgb(v,v,v))),RENDER3D_VOXEL_CORNERS=RENDER3D_VOXEL_FACES.map(([normal,right,up])=>render3DQuadAxes(normal.scale(.5),right.scale(.5),up.scale(.5))),RENDER3D_VOXEL_CORNER_SIDES=[[-1,1],[-1,-1],[1,1],[1,-1]],RENDER3D_VOXEL_ORDER=[0,1,2,3],RENDER3D_VOXEL_FLIPPED=[1,3,0,2];class VoxelMap extends EngineObject3D{constructor(pos3D=vec3(),mapSize=vec3(16),tileInfo=tile()){super(pos3D),this.mapSize=mapSize.floor(),this.tileInfo=tileInfo,this.data=new Uint8Array(this.mapSize.x*this.mapSize.y*this.mapSize.z),this.ambientOcclusion=!0,this.size3D=vec3();pos3D=this.mapSize,this.chunkCount=vec3(ceil(pos3D.x/16),ceil(pos3D.y/16),ceil(pos3D.z/16)),this.chunkMeshes=[],this.chunkTransparentMeshes=[],this.chunkCenters=[],this.chunksChanged=new Set,this.blockTypes=[],this.tiles=new Map,mapSize=new EngineObject3D;mapSize.size3D=vec3(),mapSize.transparent=!0,mapSize.render3D=()=>this.renderChunks(!0),(mapSize.drawOwner=this).addChild(mapSize),render3DLevel.push(this)}getVoxel(cell){return this.voxelAt(floor(cell.x),floor(cell.y),floor(cell.z))}voxelAt(x,y,z){var s=this.mapSize;return x<0||y<0||z<0||x>=s.x||y>=s.y||z>=s.z?0:this.data[x+s.x*(y+s.y*z)]}setVoxel(cell,type){var x=floor(cell.x),y=floor(cell.y),z=floor(cell.z),cell=this.mapSize;if(!(x<0||y<0||z<0||x>=cell.x||y>=cell.y||z>=cell.z)){cell=x+cell.x*(y+cell.y*z);if(this.data[cell]!==type){this.data[cell]=type,this.markChunk(x,y,z);for(let dz=-1;dz<=1;++dz)for(let dy=-1;dy<=1;++dy)for(let dx=-1;dx<=1;++dx)(this.ambientOcclusion||abs(dx)+abs(dy)+abs(dz)===1)&&this.voxelAt(x+dx,y+dy,z+dz)&&this.markChunk(x+dx,y+dy,z+dz)}}}markChunk(x,y,z){var s=this.mapSize,c=this.chunkCount;x<0||y<0||z<0||x>=s.x||y>=s.y||z>=s.z||this.chunksChanged.add(floor(x/16)+c.x*(floor(y/16)+c.y*floor(z/16)))}setBlockType(type,faces,{seeThrough=!1,transparent=!1,doubleSided=!1}={}){faces=isNumber(faces)?[faces,faces,faces,faces,faces,faces]:isArray(faces)?faces:[faces.side,faces.side,faces.top??faces.side,faces.bottom??faces.side,faces.side,faces.side];this.blockTypes[type]={faces:faces,seeThrough:seeThrough||transparent,transparent:transparent,doubleSided:doubleSided},this.rebuild()}blockType(type){return this.blockTypes[type]||={faces:[type,type,type,type,type,type],seeThrough:!1,transparent:!1,doubleSided:!1}}rebuild(){this.tiles.clear();var c=this.chunkCount;for(let i=c.x*c.y*c.z;i--;)this.chunksChanged.add(i)}buildChunks(){for(const index of this.chunksChanged)this.buildChunk(index);this.chunksChanged.clear()}buildChunk(index){var c=this.chunkCount,n=16,s=this.mapSize,data=this.data,x0=index%c.x*n,y0=floor(index/c.x)%c.y*n,c=floor(index/(c.x*c.y))*n,x1=min(x0+n,s.x),y1=min(y0+n,s.y),z1=min(c+n,s.z),center=vec3((x0+x1)/2,(y0+y1)/2,(c+z1)/2),opaque=new Mesh,transparent=new Mesh,ao=this.ambientOcclusion,solid=(x,y,z)=>{x=this.voxelAt(x,y,z);return x&&!this.blockType(x).seeThrough?1:0},points=[],uvs=[],colors=[],shade=[];for(let z=c;z<z1;++z)for(let y=y0;y<y1;++y)for(let x=x0;x<x1;++x){var type=data[x+s.x*(y+s.y*z)];if(type){var block=this.blockType(type),mesh=block.transparent?transparent:opaque,cx=x+.5-center.x,cy=y+.5-center.y,cz=z+.5-center.z;for(let f=0;f<6;++f){var[normal,right,up]=RENDER3D_VOXEL_FACES[f],bx=x+normal.x,by=y+normal.y,bz=z+normal.z,next=this.voxelAt(bx,by,bz);if(!next||next!==type&&this.blockType(next).seeThrough){for(let k=0;k<4;++k){var[r,u]=RENDER3D_VOXEL_CORNER_SIDES[k],side1=ao&&solid(bx+right.x*r,by+right.y*r,bz+right.z*r),side2=ao&&solid(bx+up.x*u,by+up.y*u,bz+up.z*u),r=ao&&solid(bx+right.x*r+up.x*u,by+right.y*r+up.y*u,bz+right.z*r+up.z*u);shade[k]=side1&&side2?0:3-+side1-+side2-+r}var corners=RENDER3D_VOXEL_CORNERS[f],quad=this.tileUVs(block.faces[f]),order=shade[1]+shade[2]<shade[0]+shade[3]?RENDER3D_VOXEL_FLIPPED:RENDER3D_VOXEL_ORDER;for(let k=0;k<4;++k){var i=order[k],p=corners[i];points[k]=vec3(cx+p.x,cy+p.y,cz+p.z),uvs[k]=quad[i],colors[k]=ao?RENDER3D_VOXEL_SHADES[shade[i]]:WHITE}mesh.addStrip(points,normal,uvs,colors),block.doubleSided&&mesh.addStrip([points[0],points[2],points[1],points[3]],normal.scale(-1),[uvs[0],uvs[2],uvs[1],uvs[3]],[colors[0],colors[2],colors[1],colors[3]])}}}}this.chunkMeshes[index]?.dispose(),this.chunkTransparentMeshes[index]?.dispose(),this.chunkMeshes[index]=opaque.points.length?opaque:void 0,this.chunkTransparentMeshes[index]=transparent.points.length?transparent:void 0,this.chunkCenters[index]=center}tileUVs(index){let uvs=this.tiles.get(index);if(!uvs){var first=this.tileInfo;const rect=render3DGetTileUVs(first.columns?first.frame(index):first.index(index));uvs=RENDER3D_QUAD_UVS.map(q=>Object.freeze(vec2(rect.x+q.x*rect.w,rect.y+q.y*rect.h))),this.tiles.set(index,uvs)}return uvs}boxBlocked(pos,size,o){const m=this.pos3D,s=this.mapSize;var cells=(center,half,corner,count)=>{var first=floor(center-half-corner+1e-9),center=max(ceil(center+half-corner-1e-9)-1,first);return[max(first,0),min(center,count-1)]},[x0,x1]=cells(pos.x,size.x/2,m.x,s.x),[y0,y1]=cells(pos.y,size.y/2,m.y,s.y),[cells,z1]=cells(pos.z,size.z/2,m.z,s.z);for(let z=cells;z<=z1;++z)for(let y=y0;y<=y1;++y)for(let x=x0;x<=x1;++x){var type=this.data[x+s.x*(y+s.y*z)];if(type&&o.collideWithVoxel(type,vec3(x,y,z)))return!0}return!1}levelCollide3D(o,oldPos){var k=o.scale3D,size=vec3(o.size3D.x*abs(k.x),o.size3D.y*abs(k.y),o.size3D.z*abs(k.z)),p=o.pos3D,v=o.velocity3D,m=this.pos3D;if(this.boxBlocked(p,size,o))if(this.boxBlocked(oldPos,size,o)){var k=size.y/2,flat=vec3(size.x,0,size.z);let top;for(let y=floor(oldPos.y-k-m.y);y<=floor(oldPos.y-m.y);++y)this.boxBlocked(vec3(oldPos.x,m.y+y+.5,oldPos.z),flat,o)&&(top=y+1);var from,lead,first,last,slab,slabSize,k=void 0===top?void 0:vec3(oldPos.x,m.y+top+k+1e-4,oldPos.z);if(k&&!this.boxBlocked(k,size,o))return p.set(k.x,k.y,k.z),v.y=max(v.y,0),void(o.groundObject=this);const tiny=1e-9,place=oldPos.copy();for(const axis of["y","x","z"]){const move=p[axis]-place[axis],halfSize=size[axis]/2,corner=m[axis];move&&(from=(lead=at=>0<move?ceil(at+halfSize-corner-tiny)-1:floor(at-halfSize-corner+tiny))(place[axis]),lead=lead(p[axis]),first=min(from,lead)+(0<move?1:0),last=max(from,lead)-(0<move?0:1),slab=place.copy(),slabSize=size.copy(),slab[axis]=corner+(first+last+1)/2,slabSize[axis]=last-first+1,from!==lead&&this.boxBlocked(slab,slabSize,o)?("y"===axis&&move<0&&(o.groundObject=this),v[axis]=0):place[axis]=p[axis])}void p.set(place.x,place.y,place.z)}else{var restitution=max(o.restitution,this.restitution);const place=oldPos.copy();for(const axis of["y","x","z"]){var moved=place.copy();if(moved[axis]=p[axis],this.boxBlocked(moved,size,o)){const half=size[axis]/2,move=p[axis]-oldPos[axis];move&&(moved[axis]=move<0?m[axis]+floor(p[axis]-half-m[axis])+1+half+1e-4:m[axis]+ceil(p[axis]+half-m[axis])-1-half-1e-4,this.boxBlocked(moved,size,o)||(place[axis]=moved[axis])),"y"===axis&&move<0&&(o.groundObject=this),v[axis]*=-restitution}else place[axis]=p[axis]}p.set(place.x,place.y,place.z)}}raycast(ray,maxDistance=1/0,test=()=>!0){var s=this.mapSize,o=ray.origin.subtract(this.pos3D),d=ray.direction,axes=["x","y","z"];if(d.lengthSquared()){let enter=0,exit=maxDistance,entryAxis=-1;for(let k=0;k<3;++k){var a=axes[k],dk=d[a],ok=o[a];if(dk){let t0=-ok/dk,t1=(s[a]-ok)/dk;t0>t1&&([t0,t1]=[t1,t0]),t0>enter&&(enter=t0,entryAxis=k),exit=min(exit,t1)}else if(ok<0||ok>s[a])return}if(!(enter>exit)){const start=o.add(d.scale(enter)),cell=[],step=[],next=[],delta=[];for(let k=0;k<3;++k){const a=axes[k],dk=d[a];cell[k]=clamp(floor(start[a]),0,s[a]-1),step[k]=sign(dk),next[k]=dk?enter+(cell[k]+(0<dk?1:0)-start[a])/dk:1/0,delta[k]=dk?abs(1/dk):1/0}var normalOn=k=>vec3(0===k?-step[0]:0,1===k?-step[1]:0,2===k?-step[2]:0);let normal,t=enter;for(normal=0<=entryAxis?normalOn(entryAxis):normalOn(abs(d.x)>=abs(d.y)&&abs(d.x)>=abs(d.z)?0:abs(d.y)>=abs(d.z)?1:2);t<=maxDistance;){var type=this.data[cell[0]+s.x*(cell[1]+s.y*cell[2])];if(type){var at=vec3(cell[0],cell[1],cell[2]);if(test(type,at))return{distance:t,cell:at,normal:normal,type:type}}const k=next[0]<next[1]?next[0]<next[2]?0:2:next[1]<next[2]?1:2;if(t=next[k],cell[k]+=step[k],cell[k]<0||cell[k]>=s[axes[k]])return;next[k]+=delta[k],normal=normalOn(k)}}}}levelRaycast3D(ray){return this.raycast(ray)?.distance}levelSegment3D(from,to){var to=to.subtract(from),hit=this.raycast(new Ray3D(from,to),1);if(hit){if(!hit.distance){from=from.subtract(to.normalize(1e-6)).subtract(this.pos3D);if(this.voxelAt(floor(from.x),floor(from.y),floor(from.z)))return}return{distance:hit.distance,normal:hit.normal}}}update(){super.update()}render3D(){this.renderChunks(!1)}renderChunks(transparent){this.buildChunks();var meshes=transparent?this.chunkTransparentMeshes:this.chunkMeshes,p=this.pos3D,texture=this.tileInfo.textureInfo;for(let i=0;i<meshes.length;++i){var mesh=meshes[i],c=this.chunkCenters[i];mesh&&render3D.drawMesh(mesh,vec3(p.x+c.x,p.y+c.y,p.z+c.z),texture,this.color)}}destroy(immediate){render3DLevelLeave(this);for(const mesh of[...this.chunkMeshes,...this.chunkTransparentMeshes])mesh?.dispose();super.destroy(immediate)}}const level3DTypes=new Map,level3DBaseScale=new WeakMap;function level3DAddType(name,make,defaults={},tileInfo){level3DTypes.set(String(name),{make:make,defaults:defaults,tileInfo:tileInfo})}function level3DAddMesh(name,mesh,tileInfo,color=WHITE){level3DAddType(name,function(pos,properties){return level3DMakeShape(pos,properties,mesh,tileInfo)},{color:color,solid:!1},tileInfo)}function level3DLoad(level){var prefabs=level.prefabs;if(prefabs&&"object"==typeof prefabs&&!isArray(prefabs))for(const name in prefabs){var known=level3DPrefabs.get(name);(known?known.fromLevel:!level3DTypes.has(name))&&prefabs[name]&&"object"==typeof prefabs[name]&&level3DPrefabSet(name,prefabs[name],!0)}scene=level.scene,++level3DSceneCubeLoads.skyBox,++level3DSceneCubeLoads.environment,(r=render3D)&&scene&&"object"==typeof scene&&(color=level3DHexColor,(sky=isArray(scene.sky)&&3===scene.sky.length?scene.sky.map(color):[]).length&&sky.every(c=>c)&&([sky,horizon,bottom]=sky,ambient=level3DFinite(scene.ambient)?scene.ambient:.5,level3DSkies.has(r.sky)&&r.sky.dispose(),level3DSkies.add(r.sky=buildSky(sky,horizon,bottom)),r.fogColor=horizon.copy(),r.ambientColor=sky.scale(ambient,1),r.ambientGroundColor=bottom.scale(ambient,1)),(horizon=level3DVector(scene.sunDirection))&&horizon.lengthSquared()&&(r.sunDirection=horizon),r.sunColor=color(scene.sunColor)||r.sunColor,isArray(sky=scene.fog)&&2===sky.length&&sky.every(level3DFinite)&&(r.fogStart=sky[0],r.fogEnd=sky[1]),r.fogColor=color(scene.fogColor)||r.fogColor,"boolean"==typeof scene.shadows&&(r.shadows=scene.shadows),"boolean"==typeof scene.lensFlare&&(scene.lensFlare?level3DSunHasFlare()||(level3DSunFlare=new LensFlare3D):(level3DSunFlare&&!level3DSunFlare.destroyed&&level3DSunFlare.destroy(),level3DSunFlare=void 0)),level3DSceneCubeMap("skyBox",scene.skyBox),level3DSceneCubeMap("environment",scene.environment));var scene,r,color,horizon,sky,result,made=[],bottom=level3DVoxelMap=function(voxels){var shape=function(voxels){if(voxels&&"object"==typeof voxels){var size=level3DVector(voxels.size);if(!(!size||[size.x,size.y,size.z].some(n=>n<1||n%1)||size.x*size.y*size.z>LEVEL3D_VOXEL_CELLS))return{pos:level3DVector(voxels.pos,vec3()),size:size}}}(voxels);if(shape&&void 0!==VoxelMap)return shape=new VoxelMap(shape.pos,shape.size,level3DVoxelTiles||function(){if(textureInfos[0]?.size.x||headlessMode||!canvasAvailable())return tile();if(!level3DVoxelPlain){var context=createCanvasContext(256);for(let i=0;i<256;++i){var x=i%16*16,y=16*(i/16|0),hue=.618*i%1;context.fillStyle=hsl(hue,.45,.4).toString(),context.fillRect(x,y,16,16),context.fillStyle=hsl(hue,.5,.55).toString(),context.fillRect(1+x,1+y,14,14)}level3DVoxelPlain=new TextureInfo(context.canvas)}return tile(0,16,level3DVoxelPlain,0)}()),function(blocks,data){if(data.fill(0),isArray(blocks))for(let i=0,cell=0;i+1<blocks.length&&cell<data.length;i+=2){var count=blocks[i],type=blocks[i+1];if(!isNumber(count)||!isNumber(type)||count<0)return;count=min(cell+floor(count),data.length);data.fill(0<=type&&type<=255&&type%1==0?type:0,cell,count),cell=count}}(voxels.blocks,shape.data),level3DVoxelSetupMap?.(shape),shape.rebuild(),shape}(level.voxels),ambient=(bottom&&made.push(bottom),level3DTerrainMap=function(terrain){var shape=function(terrain){if(terrain&&"object"==typeof terrain){var size=terrain.size,rows=terrain.heights,n=LEVEL3D_TERRAIN_SAMPLES;if(isArray(size)&&2===size.length&&size.every(v=>level3DFinite(v)&&0<v)&&!(!isArray(rows)||rows.length<2||rows.length>n)&&isArray(rows[0])){const columns=rows[0].length;if(!(columns<2||columns>n)&&rows.every(row=>isArray(row)&&row.length===columns))return n=rows.map(row=>row.map(v=>level3DFinite(v)?clamp(v):0)),{pos:level3DVector(terrain.pos,vec3()),size:vec2(size[0],size[1]),height:level3DFinite(terrain.height)&&0<terrain.height?terrain.height:1,heights:n}}}}(terrain);if(shape&&void 0!==HeightMap)return level3DTerrainSetColors(shape=new HeightMap(shape.heights,shape.size,shape.height,void 0,shape.pos),terrain),shape.colors&&shape.rebuild(),shape}(level.terrain));ambient&&made.push(ambient);for(const object of isArray(level.objects)?level.objects:[])object&&"object"==typeof object&&(result=function(object){return level3DMakeAt(object,level3DVector(object.pos,vec3()),level3DVector(object.rotation,vec3()).scale(PI/180),level3DVector(object.scale,vec3(1)))}(object),object,result)&&made.push(result);return made}let level3DVoxelTiles,level3DVoxelSetupMap,level3DVoxelMap;function level3DVoxelSetup(tileInfo,setup){level3DVoxelTiles=tileInfo,level3DVoxelSetupMap=setup}const LEVEL3D_VOXEL_CELLS=2**24;let level3DTerrainMap;const LEVEL3D_TERRAIN_SAMPLES=129;function level3DHexColor(hex){return"string"==typeof hex&&/^#([0-9a-f]{6}|[0-9a-f]{8})$/i.test(hex)?(new Color).setHex(hex):void 0}const level3DFinite=v=>"number"==typeof v&&isFinite(v);function level3DTerrainSetColors(map,terrain,paint=function(terrain,samples){var colors=(terrain=terrain?.paint)?.colors,runs=terrain?.cells;if(isArray(colors)&&isArray(runs)&&!(255<colors.length)&&colors.every(level3DHexColor)){var cells=new Uint8Array(samples);let at=0;for(let i=0;i<runs.length;i+=2){var count=runs[i],color=runs[i+1];if(!(0<count&&count%1==0&&0<=color&&color%1==0)||color>colors.length||at+count>samples)return;cells.fill(color,at,at+=count)}return at===samples?{colors:colors.slice(),cells:cells}:void 0}}(terrain,map.rows*map.columns)){const base=level3DHexColor(terrain.color)||WHITE;if(map.color=base,map.colors=void 0,paint&&paint.cells.some(v=>v)){const palette=[base,...paint.colors.map(level3DHexColor)];map.color=WHITE,map.colors=map.heights.map((row,r)=>row.map((v,c)=>palette[paint.cells[r*map.columns+c]]||base))}}let level3DVoxelPlain;const level3DSkies=new WeakSet;const level3DSceneCubeMaps=new Map,level3DSceneCubeLoaded=new WeakMap,level3DSceneCubeLoads={skyBox:0,environment:0};function level3DSceneCubeMap(field,urls){if(isArray(urls)&&6===urls.length&&urls.every(url=>"string"==typeof url)){const key=urls.join("\n"),load=++level3DSceneCubeLoads[field],before=render3D[field];let cube=level3DSceneCubeMaps.get(key);level3DSceneCubeMaps.delete(key),level3DSceneCubeMaps.set(key,cube||=loadCubeMap(urls)),cube.then(map=>level3DSceneCubeLoaded.set(cube,map),()=>{});for(var[oldKey,old]of level3DSceneCubeMaps){if(level3DSceneCubeMaps.size<=4)break;var map=level3DSceneCubeLoaded.get(old);map&&(map===render3D.skyBox||map===render3D.environment)||(level3DSceneCubeMaps.delete(oldKey),old.then(map=>map!==render3D.skyBox&&map!==render3D.environment&&map.dispose(),()=>{}))}cube.then(map=>load===level3DSceneCubeLoads[field]&&render3D[field]===before&&(render3D[field]=map),error=>{level3DSceneCubeMaps.get(key)===cube&&level3DSceneCubeMaps.delete(key),console.warn(error.message)})}}let level3DSunFlare;const level3DSunHasFlare=()=>engineObjects.some(o=>o instanceof LensFlare3D&&!o.destroyed&&!o.light);function level3DVector(value,fallback){return isArray(value)&&3===value.length&&value.every(level3DFinite)?vec3(value[0],value[1],value[2]):fallback}function level3DMakeAt(object,pos,rotation,scale){var type=level3DTypes.get(object.type);if(type){var properties=function(type,object){var key,value,properties={},object=object.properties;for([key,value]of Object.entries(type.defaults))properties[key]=value?.copy?value.copy():value;for(const[key,value]of Object.entries(object&&"object"==typeof object?object:{})){var d=type.defaults[key];isColor(d)?level3DHexColor(value)&&(properties[key]=level3DHexColor(value)):isVector3(d)?properties[key]=level3DVector(value,properties[key]):isVector2(d)?isArray(value)&&2===value.length&&value.every(level3DFinite)&&(properties[key]=vec2(value[0],value[1])):"number"==typeof d?level3DFinite(value)&&(properties[key]=value):void 0!==d&&typeof value!=typeof d||(properties[key]=value)}return properties}(type,object),make=type["make"],count=engineObjects.length;let result;try{result=make.prototype?new make(pos,properties):make(pos,properties)}catch(error){if(!debug)throw error;console.error(`level3DLoad: ${object.type} ${object.id} could not be made, skipped:`,error);for(const o of engineObjects.slice(count))o.destroy();return}if(result&&"object"==typeof result){result instanceof EngineObject3D&&(level3DBaseScale.set(result,result.scale3D.copy()),result.rotation3D=rotation.copy(),result.scale3D=result.scale3D.multiply(scale));for(const key in type.defaults)result[key]=properties[key];return result instanceof Prefab3D&&result.placeAt(pos),result}}else debug&&console.warn(`level3DLoad: no type added for ${object.type}, skipped`)}function level3DSpawn(type,pos3D=vec3(),rotation3D=vec3(),scale3D=vec3(1),properties){return level3DMakeAt(properties?{type:type,properties:properties}:{type:type},pos3D.copy(),rotation3D,scale3D)}const level3DPrefabs=new Map;let level3DPrefabDepth=0,level3DPrefabAttached=0;const level3DPrefabMaking=[];function level3DAddPrefab(name,prefab){level3DPrefabSet(name,prefab,!1)}function level3DPrefabSet(name,prefab,fromLevel){name=String(name);var objects=isArray(prefab?.objects)?prefab.objects.filter(o=>o&&"object"==typeof o):[];level3DPrefabs.set(name,{attached:!!prefab?.attached,objects:JSON.parse(JSON.stringify(objects)),fromLevel:fromLevel,source:prefab}),level3DTypes.set(name,{make:class extends Prefab3D{constructor(pos){super(pos,name)}},defaults:{},tileInfo:void 0})}async function level3DLoadPrefab(name,url){level3DAddPrefab(name,await fetchJSON(url))}function level3DPrefabPartTransform(instance,object){var pos=level3DVector(object.pos,vec3()),scale=level3DVector(object.scale,vec3(1)),object=level3DVector(object.rotation,vec3()).scale(PI/180),object=buildMatrix(vec3(),instance.rotation3D).multiply(buildMatrix(vec3(),object));return{pos:buildMatrix(instance.pos3D,instance.rotation3D,instance.scale3D).transformPoint(pos),rotation:object.getRotation(),scale:scale.multiply(instance.scale3D)}}class Prefab3D extends EngineObject3D{constructor(pos3D,prefabName=""){super(pos3D),this.prefabName=prefabName,this.attached=!!level3DPrefabs.get(prefabName)?.attached||0<level3DPrefabAttached,this.parts=[],this.originOffset=vec3(),this.partObjects=[],this.partsPlaced="",this.partsMade=!1}placeAt(pos3D){this.pos3D=pos3D.copy(),this.placeParts();var o=this.originOffset;(o.x||o.y||o.z)&&(this.pos3D=buildMatrix(pos3D,this.rotation3D,this.scale3D).transformPoint(o))}placeParts(){this.destroyed||(this.partsMade||=(this.makeParts(),!0),this.partsPlaced=this.placeKey(),this.attached)||this.parts.forEach((part,i)=>{part instanceof EngineObject3D&&(i=level3DPrefabPartTransform(this,this.partObjects[i]),part.rotation3D=i.rotation,part.scale3D=(level3DBaseScale.get(part)??vec3(1)).multiply(i.scale),part instanceof Prefab3D?part.placeAt(i.pos):part.pos3D=i.pos)})}placeKey(){var p=this.pos3D,r=this.rotation3D,s=this.scale3D;return[p.x,p.y,p.z,r.x,r.y,r.z,s.x,s.y,s.z].join()}makeParts(){var prefab=level3DPrefabs.get(this.prefabName);if(prefab)if(level3DPrefabMaking.includes(this.prefabName)||8<=level3DPrefabDepth)debug&&console.error(`level3DLoad: the prefab ${this.prefabName} holds itself, left out there`);else{level3DPrefabMaking.push(this.prefabName),++level3DPrefabDepth,this.attached&&++level3DPrefabAttached;try{var low=vec3(1/0),high=vec3(-1/0),children=[];for(const object of prefab.objects){var local={pos:level3DVector(object.pos,vec3()),scale:level3DVector(object.scale,vec3(1)),rotation:level3DVector(object.rotation,vec3()).scale(PI/180)},at=this.attached?local:level3DPrefabPartTransform(this,object),part=level3DMakeAt(object,at.pos,at.rotation,at.scale);if(part&&(this.parts.push(part),this.partObjects.push(object),this.attached&&part instanceof EngineObject3D)){part.setCollision(!1,!1,!1),children.push(part);var box=buildMatrix(part.pos3D,part.rotation3D,part.size3D.multiply(part.scale3D));for(let i=8;i--;){var p=box.transformPoint(vec3(1&i?.5:-.5,2&i?.5:-.5,4&i?.5:-.5));for(const k of["x","y","z"])low[k]=min(low[k],p[k]),high[k]=max(high[k],p[k])}}}children.length&&(this.size3D=high.subtract(low),this.originOffset=low.add(high).scale(.5));for(const part of children)part.pos3D=part.pos3D.subtract(this.originOffset),(part.drawOwner=this).addChild(part)}finally{--level3DPrefabDepth,level3DPrefabMaking.pop(),this.attached&&--level3DPrefabAttached}}}update(){super.update(),this.attached||this.partsPlaced===this.placeKey()||this.placeParts()}destroy(immediate){if(!this.destroyed){for(const part of this.parts)part.parent!==this&&part.destroy?.(immediate);super.destroy(immediate)}}render3D(){}}function level3DMakeShape(pos,properties,mesh,tileInfo,asSphere=!1){pos=new EngineObject3D(pos,mesh,0<=properties.tile?tile(properties.tile):tileInfo,properties.color);return pos.collideAsSphere3D=asSphere,properties.solid&&pos.setCollision(),pos}let level3DCylinderMesh;level3DAddType("Box",function(pos,properties){return level3DMakeShape(pos,properties,render3D.boxMesh)},{color:WHITE,tile:-1,solid:!0}),level3DAddType("Sphere",function(pos,properties){return level3DMakeShape(pos,properties,render3D.sphereMesh,void 0,!0)},{color:WHITE,tile:-1,solid:!0}),level3DAddType("Cylinder",function(pos,properties){return level3DMakeShape(pos,properties,level3DCylinderMesh||=buildCylinder())},{color:WHITE,tile:-1,solid:!0}),level3DAddType("Light",function(pos,properties){pos=new Light3D(pos,properties.radius,properties.color,properties.intensity);return pos.coneAngle=max(properties.cone,0)*PI/180,pos.coneSoftness=properties.softness,properties.shadows&&render3D&&(render3D.shadowLight=pos),properties.lensFlare&&pos.addFlare(),pos},{color:WHITE,radius:5,intensity:1,cone:0,softness:.2,shadows:!1,lensFlare:!1});textIntl=["Emitter","Particles","Color","Motion","Collision","Texture","Behaviors"];const particleEffectSettings=[];let particleEffectSettingGroup;function particleEffectAddSetting(name,kind,value,min,max,step,description,hardMin=min,hardMax=max){var extra=["trailScale","velocityInheritance","localSpace","restitution","friction","gravity"].includes(name);particleEffectSettings.push({group:particleEffectSettingGroup,name:name,kind:kind,value:value,min:min,max:max,step:step,description:description,hardMin:hardMin,hardMax:hardMax,extra:extra})}particleEffectSettingGroup="Emitter",particleEffectAddSetting("emitRate","number",100,0,500,.1,"Particles per second, 0 emits none",0,1e4),particleEffectAddSetting("emitTime","number",0,0,5,.05,"Seconds to emit for, 0 is forever",0,1e9),particleEffectAddSetting("emitSize","number",0,0,10,.05,"Diameter, or width when rectangular",0,1e9),particleEffectAddSetting("emitRect","checkbox",!1,0,0,0,"Rectangle emitter, off is a circle"),particleEffectAddSetting("emitHeight","number",0,0,10,.05,"Rectangle height",0,1e9),particleEffectAddSetting("emitConeAngle","number",PI,0,PI,.01,"Half angle particles move in, 3.14 is all ways"),particleEffectAddSetting("angle","number",0,-3.14,3.14,.01,"Emitter angle, 0 points up",-PI,PI),particleEffectSettingGroup="Particles",particleEffectAddSetting("particleTime","number",.5,0,5,.05,"Seconds each particle lives",0,1e9),particleEffectAddSetting("sizeStart","number",.1,0,5,.01,"Size at the start of life",0,1e9),particleEffectAddSetting("sizeEnd","number",1,0,5,.01,"Size at the end of life",0,1e9),particleEffectAddSetting("fadeRate","number",.1,0,1,.01,"Share of life spent fading in and out"),particleEffectAddSetting("randomness","number",.2,0,1,.01,"How much each particle varies"),particleEffectAddSetting("particleConeAngle","number",PI,0,PI,.01,"Half angle of start rotation, 3.14 is any"),particleEffectAddSetting("trailScale","number",0,0,20,.1,"Stretch along the motion, 0 is off",0,1e9),particleEffectAddSetting("additive","checkbox",!1,0,0,0,"Glow with additive blending"),particleEffectSettingGroup="Color",particleEffectAddSetting("colorStartA","color",[1,1,1,1],0,1,.01,"Start color, each particle picks between A and B"),particleEffectAddSetting("colorStartB","color",[1,1,1,1],0,1,.01,"Second start color"),particleEffectAddSetting("colorEndA","color",[1,1,1,0],0,1,.01,"End color, each particle picks between A and B"),particleEffectAddSetting("colorEndB","color",[1,1,1,0],0,1,.01,"Second end color"),particleEffectAddSetting("randomColorLinear","checkbox",!0,0,0,0,"Pick along A to B, off picks each channel"),particleEffectSettingGroup="Motion",particleEffectAddSetting("speed","number",.1,0,.5,.005,"Start speed, world units per frame",-1e9,1e9),particleEffectAddSetting("angleSpeed","number",.05,0,.5,.005,"Spin speed, radians per frame",-1e9,1e9),particleEffectAddSetting("damping","number",1,.8,1,.001,"Speed kept each frame, 1 keeps it all",0,1),particleEffectAddSetting("angleDamping","number",1,.8,1,.001,"Spin kept each frame",0,1),particleEffectAddSetting("gravityScale","number",0,-2,2,.05,"How much gravity pulls",-1e9,1e9),particleEffectAddSetting("gravity","number",0,-.02,.02,5e-4,"Its own fall each frame, the same in any game",-1,1),particleEffectAddSetting("velocityInheritance","number",0,0,1,.01,"Share of emitter motion passed on"),particleEffectAddSetting("localSpace","checkbox",!1,0,0,0,"Particles move with the emitter"),particleEffectAddSetting("collideLevel","checkbox",!(particleEffectSettingGroup="Collision"),0,0,0,"Hit tiles, the preview adds a floor"),particleEffectAddSetting("restitution","number",0,0,1,.01,"Bounce when hitting tiles"),particleEffectAddSetting("friction","number",.8,0,1,.01,"Speed kept sliding along tiles, the larger of this and the friction of the layer, as for an object"),particleEffectSettingGroup="Texture",particleEffectAddSetting("shape","shape","soft",0,0,0,"Built-in shape, none uses the tile"),particleEffectAddSetting("tileIndex","number",-1,-1,63,1,"Tile in texture 0 when no shape, -1 is untextured",-1,1e4),particleEffectAddSetting("tileSize","number",16,1,128,1,"Tile size in texture pixels",1,4096),particleEffectAddSetting("tilePadding","number",1,0,8,1,"Pixels of padding around each tile",0,64);const particleEffectBehaviors=[{name:"wobble",min:0,max:5,value:1,description:"Sway from side to side"},{name:"swirl",min:-2,max:2,value:1,description:"Turn the direction of travel"},{name:"turbulence",min:0,max:5,value:1,description:"Random pushes every frame"},{name:"attract",min:-2,max:2,value:1,description:"Pull toward the emitter, negative pushes away"},{name:"orbit",min:-2,max:2,value:1,description:"Push around the emitter, add attract to circle it"},{name:"wind",min:-2,max:2,value:1,description:"A sideways push that grows with age"},{name:"stick",min:0,max:1,value:1,description:"Grip on landing, pair with collideLevel"}].map(b=>({...b,update:void 0,update3D:void 0})),particleEffectBehavior=name=>particleEffectBehaviors.find(b=>b.name===name),particleEffectShapes=["dot","soft","glow","smoke","spark","square","triangle","ring","star","plus"];function particleEffectColor(value){if(isArray(value=value instanceof Color?[value.r,value.g,value.b,value.a]:value)&&4===value.length&&value.every(isNumber))return value.map(c=>clamp(c))}function particleEffectSanitize(raw){var text="string"==typeof raw?.name?raw.name.replace(/[\x00-\x1f\x7f]+/g," ").trim():"",text=text?textGraphemes(text).slice(0,60).join("").trim():"Effect",input=raw?.settings&&"object"==typeof raw.settings?{...raw.settings}:{},settings=(void 0===input.shape&&isNumber(input.tileIndex)&&(input.shape=""),{});for(const setting of particleEffectSettings){var value=input[setting.name],fallback=setting.value;"checkbox"===setting.kind?settings[setting.name]="boolean"==typeof value?value:0===value||1===value?!!value:fallback:"color"===setting.kind?settings[setting.name]=particleEffectColor(value)||fallback.slice():"shape"===setting.kind?settings[setting.name]=""===value||particleEffectShapes.includes(value)?value:fallback:isNumber(value)?(value=clamp("angle"===setting.name&&isFinite(value)&&abs(value)>PI?mod(value+PI,2*PI)-PI:value,setting.hardMin,setting.hardMax),settings[setting.name]=1<=setting.step?round(value):value):settings[setting.name]=fallback}settings.localSpace&&(settings.collideLevel=!1);var behaviors=[],list=isArray(raw?.behaviors)?raw.behaviors:[];for(const b of particleEffectBehaviors){var found=list.find(x=>x?.name===b.name);found&&(found=isNumber(found.strength)?clamp(found.strength,b.min,b.max):b.value,behaviors.push({name:b.name,strength:found}))}for(const x of list)"string"!=typeof x?.name||!x.name||particleEffectBehavior(x.name)||behaviors.some(b=>b.name===x.name)||behaviors.push({name:x.name,strength:isNumber(x.strength)?x.strength:1});return{name:text,settings:settings,behaviors:behaviors}}function particleEffectRecolor(effect,hue=0,saturation=1){var settings={...(effect=particleEffectSanitize(effect)).settings};for(const name of["colorStartA","colorStartB","colorEndA","colorEndB"]){var[h,s,l,a]=new Color(...settings[name]).HSLA(),h=(new Color).setHSLA(mod(h+hue),clamp(s*saturation),l,a);settings[name]=[h.r,h.g,h.b,h.a]}return{...effect,settings:settings,behaviors:effect.behaviors.map(b=>({...b}))}}const particleEffectsByName=new Map;function particleEffectsAdd(effects){for(const effect of isArray(effects)?effects:[effects]){var clean=particleEffectSanitize(effect);particleEffectsByName.set(clean.name.toLowerCase(),clean)}}function particleEffectsGet(name){return particleEffectsByName.get(String(name).toLowerCase())}function particleEffectsParse(text){return particleEffectsFromData(JSON.parse(text))}function particleEffectsFromData(data){data=isArray(data?.effects)?data.effects:data?.settings?[data]:void 0;if(data&&data.length)return data.map(particleEffectSanitize);throw new Error("no effects in this file")}function particleEffectsText(effects){return JSON.stringify({version:1,effects:effects},void 0,1)}async function particleEffectsLoad(url){url=particleEffectsFromData(await fetchJSON(url));return particleEffectsAdd(url),url}let particleEffectShapeTiles;function particleEffectShapeTile(name){if(!particleEffectShapeTiles){if(headlessMode||!glContext||!canvasAvailable())return;const r=15,count=particleEffectShapes.length,context=createCanvasContext(32*count*4,128),soft=(context.scale(4,4),context.fillStyle=context.strokeStyle="#fff",(x,y,radius,alpha=t=>1-t)=>{var g=context.createRadialGradient(x,y,0,x,y,radius);for(let i=0;i<=8;++i)g.addColorStop(i/8,"rgba(255,255,255,"+alpha(i/8).toFixed(3)+")");context.fillStyle=g,context.beginPath(),context.arc(x,y,radius,0,2*PI),context.fill(),context.fillStyle="#fff"}),draw={dot:(x,y)=>{context.beginPath(),context.arc(x,y,r,0,2*PI),context.fill()},soft:(x,y)=>soft(x,y,r),glow:(x,y)=>soft(x,y,r,t=>engineGlowAlpha(t,1)),smoke:(x,y)=>{for(var[dx,dy,dr]of[[-4,-3,9],[4,-2,8],[0,4,9],[-5,5,6],[6,5,6]])soft(x+dx,y+dy,dr,t=>.7*(1-t))},spark:(x,y)=>{context.save(),context.translate(x,y),context.scale(.3,1),soft(0,0,r,t=>engineGlowAlpha(t,.6)),context.restore()},square:(x,y)=>context.fillRect(x-r,y-r,30,30),triangle:(x,y)=>{context.beginPath(),context.moveTo(x,y-r),context.lineTo(x+r,y+r),context.lineTo(x-r,y+r),context.fill()},ring:(x,y)=>{context.lineWidth=3,context.beginPath(),context.arc(x,y,13,0,2*PI),context.stroke()},star:(x,y)=>{context.beginPath();for(let i=0;i<10;++i){var a=i*PI/5-PI/2,d=i%2?6.75:r;context.lineTo(x+cos(a)*d,y+sin(a)*d)}context.fill()},plus:(x,y)=>{context.fillRect(x-r,y-4,30,8),context.fillRect(x-4,y-r,8,30)}},texture=(particleEffectShapes.forEach((name,i)=>draw[name](32*i+16,16)),new TextureInfo(context.canvas,!0,!1,!1));particleEffectShapeTiles=new Map(particleEffectShapes.map((name,i)=>[name,new TileInfo(vec2(4*(32*i+1),4),vec2(120),texture)]))}return particleEffectShapeTiles.get(name)}var b=particleEffectBehavior;function particleEffectsAddBehavior(name,update,update3D,min=-2,max=2,value=1,description=""){var old=particleEffectBehavior(name);old&&particleEffectBehaviors.splice(particleEffectBehaviors.indexOf(old),1),particleEffectBehaviors.push({name:name,update:update,update3D:update3D,min:min,max:max,value:value,description:description})}function particleEffectUpdateCallback(behaviors,is3D){const calls=behaviors.map(b=>[particleEffectBehavior(b.name)?.[is3D?"update3D":"update"],b.strength]).filter(c=>c[0]);if(calls.length)return p=>{for(var[update,strength]of calls)update(p,strength)}}b("wobble").update=(p,s)=>{p.velocity.x+=.003*s*p.scale*sin(6*time+97*p.lifeTime%9)},b("swirl").update=(p,s)=>{var s=.05*s,c=cos(s),s=sin(s),p=p.velocity;p.set(p.x*c-p.y*s,p.x*s+p.y*c)},b("turbulence").update=(p,s)=>{p.velocity.x+=rand(-1,1)*s*.005*p.scale,p.velocity.y+=rand(-1,1)*s*.005*p.scale},b("attract").update=(p,s)=>{var e=p.emitter,x=e.localSpace?0:e.pos.x,e=e.localSpace?0:e.pos.y;p.velocity.x+=(x-p.pos.x)*s*.002,p.velocity.y+=(e-p.pos.y)*s*.002},b("orbit").update=(p,s)=>{var e=p.emitter,x=e.localSpace?0:e.pos.x,e=e.localSpace?0:e.pos.y;p.velocity.x-=(p.pos.y-e)*s*.002,p.velocity.y+=(p.pos.x-x)*s*.002},b("wind").update=(p,s)=>p.velocity.x+=.004*s*p.scale*min((time-p.spawnTime)/p.lifeTime,1),b("stick").update=(p,s)=>{p.groundObject&&(p.velocity.x*=1-s,p.angleVelocity*=1-s)};const particleEffectIndirect=["emitSize","emitRect","emitHeight","tileIndex","tileSize","tilePadding","shape"];function particleEffectTileInfo(s){var texture,cell;return s.shape?particleEffectShapeTile(s.shape):!(s.tileIndex<0)&&(texture=textureInfos[0],cell=s.tileSize+2*s.tilePadding,texture)&&texture.size.x&&s.tileIndex<floor(texture.size.x/cell)*floor(texture.size.y/cell)?tile(s.tileIndex,s.tileSize,0,s.tilePadding):void 0}function particleEffectApply(emitter,effect){particleEffectSet(emitter,particleEffectSanitize(effect))}function particleEffectSet(emitter,effect){var s=effect.settings,ended=emitter.emitTime<0;for(const setting of particleEffectSettings)particleEffectIndirect.includes(setting.name)||(emitter[setting.name]="color"===setting.kind?new Color(...s[setting.name]):s[setting.name]);ended&&(emitter.emitTime=-1),emitter.emitCircle=!s.emitRect,emitter.emitSize=vec2(s.emitSize,s.emitRect?s.emitHeight:s.emitSize),emitter.tileInfo=particleEffectTileInfo(s),emitter.renderOrder=s.additive?1e9:0,emitter.particleUpdateCallback=particleEffectUpdateCallback(effect.behaviors)}function particleEffectResolve(nameOrEffect,options){nameOrEffect="string"==typeof nameOrEffect?particleEffectsGet(nameOrEffect):nameOrEffect;if(nameOrEffect){if(isVector2(options.emitSize)&&(options={...options,emitSize:options.emitSize.x,emitHeight:options.emitSize.y,emitRect:options.emitRect??!0}),debug);var settings={...nameOrEffect.settings};for(const setting of particleEffectSettings){var value=options[setting.name];void 0!==value&&function(setting,value){return"checkbox"===setting.kind?"boolean"==typeof value||0===value||1===value:"color"===setting.kind?!!particleEffectColor(value):"shape"===setting.kind?""===value||particleEffectShapes.includes(value):isNumber(value)}(setting,value)&&(settings[setting.name]=value)}var nameOrEffect=particleEffectSanitize({...nameOrEffect,settings:settings}),{hue=0,saturation=1}=options;return hue||1!=saturation?particleEffectRecolor(nameOrEffect,hue,saturation):nameOrEffect}}function particleEffect(nameOrEffect,pos=vec2(),options={}){var nameOrEffect=particleEffectResolve(nameOrEffect,options||={});if(nameOrEffect)return particleEffectSet(pos=new ParticleEmitter(pos.copy()),nameOrEffect),pos.scale=options.scale??1,void 0!==options.angle&&(pos.angle=options.angle),(nameOrEffect=options.tileInfo)&&(pos.tileInfo=nameOrEffect instanceof TextureInfo?new TileInfo(vec2(),nameOrEffect.size,nameOrEffect):nameOrEffect),pos}function particleEffectFromEmitter(emitter,name="Effect"){var settings={};for(const setting of particleEffectSettings)particleEffectIndirect.includes(setting.name)||void 0===emitter[setting.name]||(settings[setting.name]=emitter[setting.name]);return emitter.emitTime<0&&(settings.emitTime=emitter.emitElapsed),settings.emitRect=!emitter.emitCircle,settings.emitSize=emitter.emitSize.x,settings.emitHeight=emitter.emitSize.y,settings.shape="",settings.tileIndex=-1,particleEffectSanitize({name:name,settings:settings})}{const b=particleEffectBehavior,center=p=>p.emitter.worldPos3D||p.emitter.pos3D;b("wobble").update3D=(p,s)=>p.velocity.x+=.003*s*p.scale*sin(6*time+97*p.lifeTime%9),b("swirl").update3D=(p,s)=>{var s=.05*s,c=cos(s),s=sin(s),p=p.velocity;p.set(p.x*c-p.y*s,p.x*s+p.y*c,p.z)},b("turbulence").update3D=(p,s)=>{s=.005*s*p.scale;p.velocity.x+=rand(-1,1)*s,p.velocity.y+=rand(-1,1)*s,p.velocity.z+=rand(-1,1)*s},b("attract").update3D=(p,s)=>{var c=center(p);p.velocity.x+=(c.x-p.pos.x)*s*.002,p.velocity.y+=(c.y-p.pos.y)*s*.002,p.velocity.z+=(c.z-p.pos.z)*s*.002},b("orbit").update3D=(p,s)=>{var c=center(p);p.velocity.x-=(p.pos.y-c.y)*s*.002,p.velocity.y+=(p.pos.x-c.x)*s*.002},b("wind").update3D=(p,s)=>p.velocity.x+=.004*s*p.scale*min(p.age/p.lifeTime,1)}function particleEffectApply3D(emitter,effect){particleEffectSet3D(emitter,particleEffectSanitize(effect))}function particleEffectSet3D(emitter,effect){var s=effect.settings,e=emitter;e.emitSize=s.emitRect?vec3(s.emitSize,s.emitHeight,s.emitSize):s.emitSize,e.emitTime=e.emitTime<0?-1:s.emitTime,e.emitRate=s.emitRate,e.emitConeAngle=s.emitConeAngle,e.tileInfo=particleEffectTileInfo(s);for(const name of["colorStartA","colorStartB","colorEndA","colorEndB"])e[name]=new Color(...s[name]);for(const name of["particleTime","sizeStart","sizeEnd","speed","damping","gravity","fadeRate","randomness","additive","gravityScale","angleSpeed","angleDamping","collideLevel","restitution"])e[name]=s[name];e.trailTime=s.trailScale/60,e.friction=s.friction,e.stick=effect.behaviors.find(b=>"stick"==b.name)?.strength??0,e.rotation3D=vec3(0,0,-s.angle),e.particleUpdateCallback=particleEffectUpdateCallback(effect.behaviors,!0)}function particleEffect3D(nameOrEffect,pos3D=vec3(),options={}){nameOrEffect=particleEffectResolve(nameOrEffect,options||={});if(nameOrEffect)return particleEffectSet3D(pos3D=new ParticleEmitter3D(pos3D.copy()),nameOrEffect),void 0!==options.angle&&(pos3D.rotation3D=vec3(0,0,-options.angle)),pos3D.scale3D=vec3(options.scale??1),pos3D.emitFlat=!!options.flatten,options.tileInfo&&(pos3D.tileInfo=options.tileInfo),pos3D}const particleEffectsBuiltIn=[];b=(name,settings,behaviors=[])=>{particleEffectsBuiltIn.push(name),particleEffectsAdd({name:name,settings:settings,behaviors:behaviors})};b("fire",{emitRate:120,emitSize:.6,emitConeAngle:.3,shape:"glow",particleTime:.8,sizeStart:.7,sizeEnd:.15,speed:.02,angleSpeed:.02,damping:.95,gravity:.003,fadeRate:.3,randomness:.3,additive:!0,colorStartA:hsl(.08,1,.6),colorStartB:hsl(.14,1,.65),colorEndA:hsl(.02,1,.5,0),colorEndB:hsl(0,1,.3,0)}),b("torch",{emitRate:60,emitSize:.25,emitConeAngle:.2,shape:"glow",particleTime:.6,sizeStart:.45,sizeEnd:.1,speed:.015,angleSpeed:.02,damping:.95,gravity:.003,fadeRate:.3,randomness:.3,additive:!0,colorStartA:hsl(.09,1,.6),colorStartB:hsl(.13,1,.7),colorEndA:hsl(.03,1,.5,0),colorEndB:hsl(0,1,.35,0)}),b("smoke",{emitRate:25,emitSize:.5,emitConeAngle:.3,shape:"smoke",particleTime:3,sizeStart:.5,sizeEnd:2,speed:.01,angleSpeed:.01,damping:.99,gravity:4e-4,fadeRate:.5,randomness:.3,colorStartA:hsl(0,0,.5,.5),colorStartB:hsl(0,0,.3,.5),colorEndA:hsl(0,0,.2,0),colorEndB:hsl(0,0,.4,0)},[{name:"wobble",strength:.5}]),b("steam",{emitRate:30,emitSize:.4,emitConeAngle:.2,shape:"smoke",particleTime:1.5,sizeStart:.3,sizeEnd:1.5,speed:.02,damping:.98,gravity:8e-4,fadeRate:.6,randomness:.3,colorStartA:hsl(0,0,1,.4),colorStartB:hsl(.6,.2,.9,.3),colorEndA:hsl(0,0,1,0),colorEndB:hsl(.6,.2,.9,0)},[{name:"wobble",strength:.3}]),b("explosion",{emitRate:600,emitTime:.1,emitSize:.5,shape:"glow",particleTime:.6,sizeStart:1,sizeEnd:.1,speed:.1,damping:.9,fadeRate:.2,randomness:.5,additive:!0,colorStartA:hsl(.12,1,.65),colorStartB:hsl(.06,1,.55),colorEndA:hsl(0,1,.5,0),colorEndB:hsl(0,0,.3,0)}),b("sparks",{emitRate:100,emitSize:.2,shape:"soft",particleTime:.6,sizeStart:.08,sizeEnd:.04,speed:.07,damping:.96,gravity:-.006,fadeRate:.1,randomness:.4,additive:!0,trailScale:3,colorStartA:hsl(.17,1,.9),colorStartB:hsl(.12,1,.65),colorEndA:hsl(.07,1,.5,0),colorEndB:hsl(.03,1,.5,0)}),b("hit",{emitRate:400,emitTime:.05,emitSize:.2,shape:"spark",particleTime:.35,sizeStart:.5,sizeEnd:.1,speed:.08,damping:.85,fadeRate:.1,randomness:.4,additive:!0,trailScale:2,colorStartA:hsl(0,0,1),colorStartB:hsl(0,1,.7),colorEndA:hsl(0,1,.5,0),colorEndB:hsl(0,1,.4,0)}),b("dust",{emitRate:300,emitTime:.05,emitSize:1,emitRect:!0,emitHeight:.1,emitConeAngle:1.2,shape:"smoke",particleTime:.8,sizeStart:.3,sizeEnd:.8,speed:.04,damping:.9,gravity:5e-4,fadeRate:.5,randomness:.4,colorStartA:hsl(.1,.3,.6,.5),colorStartB:hsl(.08,.2,.5,.5),colorEndA:hsl(.1,.3,.6,0),colorEndB:hsl(.08,.2,.5,0)}),b("debris",{emitRate:200,emitTime:.05,emitSize:.5,emitConeAngle:1,shape:"square",particleTime:1.5,sizeStart:.15,sizeEnd:.1,speed:.07,angleSpeed:.2,damping:.99,gravity:-.008,fadeRate:.1,randomness:.5,colorStartA:hsl(.08,.4,.35),colorStartB:hsl(0,0,.4),colorEndA:hsl(.08,.4,.3),colorEndB:hsl(0,0,.35)}),b("sparkle",{emitRate:30,emitSize:1,shape:"star",particleTime:.8,sizeStart:.3,sizeEnd:0,speed:.005,angleSpeed:.05,fadeRate:.5,randomness:.3,additive:!0,colorStartA:hsl(.14,1,.7),colorStartB:hsl(0,0,1),colorEndA:hsl(.14,1,.7,0),colorEndB:hsl(0,0,1,0)}),b("magic",{emitRate:60,emitSize:1,shape:"soft",particleTime:1.5,sizeStart:.25,sizeEnd:0,speed:.015,fadeRate:.3,randomness:.3,additive:!0,colorStartA:hsl(.55,1,.65),colorStartB:hsl(.79,1,.65),colorEndA:hsl(0,0,1,0),colorEndB:hsl(0,0,1,0)},[{name:"swirl",strength:.5}]),b("heal",{emitRate:30,emitSize:.8,emitConeAngle:.2,shape:"plus",particleTime:1.2,sizeStart:.25,sizeEnd:.1,speed:.015,angleSpeed:0,particleConeAngle:0,gravity:5e-4,fadeRate:.4,randomness:.3,additive:!0,colorStartA:hsl(.33,1,.6),colorStartB:hsl(.3,1,.75),colorEndA:hsl(.33,1,.6,0),colorEndB:hsl(.3,1,.75,0)}),b("poison",{emitRate:20,emitSize:.8,shape:"smoke",particleTime:2,sizeStart:.3,sizeEnd:1,speed:.006,gravity:2e-4,fadeRate:.6,randomness:.4,colorStartA:hsl(.3,.8,.45,.6),colorStartB:hsl(.8,.6,.45,.6),colorEndA:hsl(.3,.8,.4,0),colorEndB:hsl(.8,.6,.4,0)},[{name:"wobble",strength:.5}]),b("portal",{emitRate:80,emitSize:1,shape:"soft",particleTime:1,sizeStart:.15,sizeEnd:.05,speed:.02,fadeRate:.3,randomness:.3,additive:!0,colorStartA:hsl(.75,1,.6),colorStartB:hsl(.6,1,.6),colorEndA:hsl(.75,1,.6,0),colorEndB:hsl(.6,1,.6,0)},[{name:"orbit",strength:1},{name:"attract",strength:.5}]),b("rain",{emitRate:30,emitSize:1,emitRect:!0,emitHeight:.6,angle:PI,emitConeAngle:0,shape:"soft",particleTime:.8,sizeStart:.02,sizeEnd:.02,speed:.03,gravity:-.001,fadeRate:.1,randomness:.2,trailScale:5,colorStartA:hsl(.63,1,.8,.6),colorStartB:hsl(.63,1,.8,.4),colorEndA:hsl(.63,1,.8,.6),colorEndB:hsl(.63,1,.8,.4)}),b("snow",{emitRate:6,emitSize:1,emitRect:!0,emitHeight:.6,angle:PI,emitConeAngle:.3,shape:"soft",particleTime:4,sizeStart:.05,sizeEnd:.05,speed:.004,damping:.98,gravity:-2e-4,fadeRate:.2,randomness:.3,colorStartA:hsl(0,0,1),colorStartB:hsl(.58,1,.9),colorEndA:hsl(0,0,1),colorEndB:hsl(.58,1,.9)},[{name:"wobble",strength:.15}]),b("leaves",{emitRate:3,emitSize:1,emitRect:!0,emitHeight:.2,angle:PI,emitConeAngle:.5,shape:"triangle",particleTime:4,sizeStart:.12,sizeEnd:.12,speed:.003,angleSpeed:.05,damping:.98,gravity:-1e-4,fadeRate:.2,randomness:.4,randomColorLinear:!1,colorStartA:hsl(.25,.7,.4),colorStartB:hsl(.07,.8,.5),colorEndA:hsl(.08,.6,.35),colorEndB:hsl(.05,.7,.4)},[{name:"wobble",strength:.15},{name:"wind",strength:.05}]),b("bubbles",{emitRate:8,emitSize:1,emitRect:!0,emitHeight:.1,emitConeAngle:.2,shape:"ring",particleTime:2,sizeStart:.1,sizeEnd:.2,speed:.01,gravity:3e-4,fadeRate:.2,randomness:.4,colorStartA:hsl(.55,.8,.8,.8),colorStartB:hsl(.5,.6,.9,.6),colorEndA:hsl(.55,.8,.8,.3),colorEndB:hsl(.5,.6,.9,.2)},[{name:"wobble",strength:.3}]),b("fireflies",{emitRate:3,emitSize:1,shape:"glow",particleTime:3,sizeStart:.15,sizeEnd:.15,speed:.003,damping:.99,fadeRate:.8,randomness:.3,additive:!0,colorStartA:hsl(.2,1,.6),colorStartB:hsl(.15,1,.7),colorEndA:hsl(.2,1,.6),colorEndB:hsl(.15,1,.7)},[{name:"turbulence",strength:.3}]),b("trail",{emitRate:60,shape:"soft",particleTime:.5,sizeStart:.4,sizeEnd:0,speed:0,angleSpeed:0,fadeRate:.1,randomness:.2,additive:!0,colorStartA:hsl(.08,1,.6),colorStartB:hsl(.12,1,.65),colorEndA:hsl(.03,1,.5,0),colorEndB:hsl(0,1,.4,0)}),b("muzzle",{emitRate:300,emitTime:.05,emitConeAngle:.3,shape:"glow",particleTime:.2,sizeStart:.5,sizeEnd:.1,speed:.1,damping:.8,fadeRate:.1,randomness:.3,additive:!0,colorStartA:hsl(.14,1,.8),colorStartB:hsl(.1,1,.6),colorEndA:hsl(.08,1,.5,0),colorEndB:hsl(.05,1,.4,0)}),b("blood",{emitRate:300,emitTime:.1,emitConeAngle:.8,shape:"dot",particleTime:3,sizeStart:.15,sizeEnd:.1,speed:.09,gravity:-.01,fadeRate:.1,randomness:.4,collideLevel:!0,restitution:.1,colorStartA:hsl(0,1,.35),colorStartB:hsl(0,1,.2),colorEndA:hsl(0,1,.25),colorEndB:hsl(0,1,.15)},[{name:"stick",strength:1}]),b("confetti",{emitRate:200,emitTime:.1,emitSize:.2,emitConeAngle:.6,shape:"square",particleTime:3,sizeStart:.1,sizeEnd:.1,speed:.09,angleSpeed:.15,damping:.96,gravity:-.004,fadeRate:.1,randomness:.4,randomColorLinear:!1,colorStartA:hsl(0,1,.5),colorStartB:hsl(.6,1,.6),colorEndA:hsl(0,1,.5),colorEndB:hsl(.6,1,.6)},[{name:"wobble",strength:.3}]),b("splash",{emitRate:300,emitTime:.05,emitSize:.5,emitConeAngle:.6,shape:"soft",particleTime:.7,sizeStart:.12,sizeEnd:.06,speed:.08,gravity:-.008,fadeRate:.2,randomness:.4,colorStartA:hsl(.58,.8,.7,.8),colorStartB:hsl(.55,.6,.85,.8),colorEndA:hsl(.58,.8,.7,.2),colorEndB:hsl(.55,.6,.85,.2)});class GLTFPart{constructor(name,mesh,color,textureInfo,transparent){this.name=name,this.mesh=mesh,this.color=color,this.textureInfo=textureInfo,this.transparent=transparent,this.pixelated=!1,this.node=0,this.unlit=!1,this.normalMap=void 0,this.normalScale=1,this.emissiveMap=void 0,this.emissiveMapColor=WHITE,this.roughness=1,this.skin=void 0,this.vertexSource=void 0}}class GLTFAnimation{constructor(name,channels){this.name=name,this.channels=channels,this.duration=channels.reduce((d,c)=>max(d,c.times[c.times.length-1]||0),0)}}class GLTFModel{constructor(parts,animations=[],nodeTree){this.parts=parts,this.animations=animations,this.nodeTree=nodeTree,this.modelMatrix=new Matrix4,this.mesh=new Mesh;for(const part of parts)this.mesh.combine(part.mesh,!part.skin&&nodeTree?.restPose?.[part.node]||RENDER3D_IDENTITY,part.color);animations=new Set(parts.map(p=>p.textureInfo));this.textureInfo=1===animations.size?animations.values().next().value:void 0,this.bounds=void 0}getBounds(){var bounds=this.bounds||=this.mesh.getBounds();return{min:bounds.min.copy(),max:bounds.max.copy()}}center(){var bounds=this.getBounds();return this.transform(bounds.min.add(bounds.max).scale(-.5))}fit(size=1){var bounds=this.getBounds(),bounds=bounds.max.subtract(bounds.min);return this.transform(Matrix4.scaling(vec3(size/(max(bounds.x,bounds.y,bounds.z)||1))))}transform(matrix){matrix=render3DMatrix(matrix);for(const part of this.parts)part.mesh.transform(matrix);return this.mesh.transform(matrix),this.bounds=void 0,this.modelMatrix=matrix.copy().multiply(this.modelMatrix),this}getAnimation(animation){return animation instanceof GLTFAnimation?animation:isNumber(animation)?this.animations[animation]:this.animations.find(a=>a.name===animation)}getPose(animation,time){return this.nodeTree?this.partPoses(gltfNodeWorlds(this.nodeTree,gltfPoseNodes(this.nodeTree,animation,time))):this.parts.map(()=>new Matrix4)}partPoses(worldOf){const tree=this.nodeTree,model=this.modelMatrix,modelInverse=model.copy().invert();return this.parts.map(part=>part.skin?new Matrix4:model.copy().multiply(worldOf(part.node)).multiply(tree.restInverse[part.node]).multiply(modelInverse))}dispose(){for(const part of this.parts)part.mesh.dispose();this.mesh.dispose();for(const textureInfo of new Set(this.parts.flatMap(p=>[p.textureInfo,p.normalMap,p.emissiveMap])))textureInfo!==gltfWhiteTextureInfo&&textureInfo?.destroyWebGLTexture()}createObject(pos3D=vec3()){return new GLTFObject(this,pos3D)}}class GLTFObject extends EngineObject3D{constructor(model,pos3D=vec3()){super(pos3D);pos3D=model.mesh.points.length?model.getBounds():void 0;pos3D&&(this.size3D=pos3D.max.subtract(pos3D.min)),this.model=model,this.animation=void 0,this.animationTime=0,this.animationSpeed=1,this.animationLoop=!0,this.animationPlaying=!1,this.blendFrom=void 0,this.blendTime=0,this.blendElapsed=0,this.poseNodes=void 0,this.poseMixed=!1,this.poseWorldOf=void 0,this.parts=[];for(const part of model.parts){var modelMatrix,mesh=new EngineObject3D(vec3(),part.skin?(mesh=part.mesh,copy=void 0,(copy=new Mesh).points=mesh.points.map(p=>p.copy()),copy.normals=mesh.normals.map(n=>n.copy()),copy.uvs=mesh.uvs.slice(),copy.colors=mesh.colors.slice(),copy.indices=mesh.indices&&mesh.indices.slice(),copy.doubleSided=mesh.doubleSided,copy.dynamicDraw=!0,copy):part.mesh,part.textureInfo,part.color),copy=(mesh.transparent=part.transparent,mesh.pixelated=part.pixelated,mesh.emissive=part.unlit?1:0,mesh.normalMap=part.normalMap,mesh.normalScale=part.normalScale,mesh.emissiveMap=part.emissiveMap,mesh.emissiveMapColor=part.emissiveMapColor.copy(),mesh.roughness=part.roughness,!part.skin&&model.nodeTree?.restPose?.[part.node]);copy&&(modelMatrix=(modelMatrix=model.modelMatrix).copy().multiply(copy).multiply(modelMatrix.copy().invert()),mesh.localMatrix=modelMatrix,mesh.pos3D=modelMatrix.getTranslation(),mesh.rotation3D=modelMatrix.getRotation(),mesh.scale3D=modelMatrix.getScale()),(mesh.drawOwner=this).addChild(mesh),this.parts.push(mesh)}}destroy(immediate){this.destroyed||(this.parts.forEach((o,i)=>this.model.parts[i]?.skin&&o.mesh?.dispose()),super.destroy(immediate))}play(animation=0,loop=!0,speed=1,blend=0){var fading,animation=this.model.getAnimation(animation);animation&&(0<blend&&animation===this.animation&&this.animationPlaying?(this.animationLoop=loop,this.animationSpeed=speed):(fading=this.blendFrom,this.blendFrom=void 0,0<blend&&(fading=!this.animation||fading||this.poseMixed?void 0:{animation:this.animation,time:this.animationTime,speed:this.animationPlaying?this.animationSpeed:0,loop:this.animationLoop},this.blendFrom=fading||{nodes:this.poseNodes||new Map},this.blendTime=blend,this.blendElapsed=0),this.animation=animation,this.animationLoop=loop,this.animationSpeed=speed,this.animationPlaying=!0,this.setAnimationTime(speed<0?animation.duration:0)))}stop(){this.animationPlaying=!1,this.blendFrom=void 0}setAnimationTime(time){this.animationTime=time;var model=this.model,tree=model.nodeTree;if(this.animation&&tree){let nodes=gltfPoseNodes(tree,this.animation,time);var fromNodes,time=this.blendFrom,worldOf=(time&&(fromNodes=time.nodes||gltfPoseNodes(tree,time.animation,time.time),nodes=function(tree,a,b,weight){var mixed=new Map,lerp3=(p,q)=>p.map((v,i)=>v+(q[i]-v)*weight);for(const node of new Set([...a.keys(),...b.keys()])){var p=a.get(node)||gltfNodeTRS(tree.nodes[node]),q=b.get(node)||gltfNodeTRS(tree.nodes[node]);mixed.set(node,{translation:lerp3(p.translation,q.translation),scale:lerp3(p.scale,q.scale),rotation:function(a,b,t){let dot=a[0]*b[0]+a[1]*b[1]+a[2]*b[2]+a[3]*b[3],sign=1;dot<0&&(dot=-dot,sign=-1);let wa=1-t,wb=t*sign;{var angle,s;dot<.9995&&(angle=Math.acos(dot),s=sin(angle),wa=sin(wa*angle)/s,wb=sin(t*angle)/s*sign)}const out=a.map((v,i)=>v*wa+b[i]*wb),l=hypot(...out)||1;return out.map(v=>v/l)}(p.rotation,q.rotation,weight)})}return mixed}(tree,fromNodes,nodes,smoothStep(clamp(this.blendElapsed/this.blendTime)))),gltfNodeWorlds(tree,nodes)),pose=(this.poseNodes=nodes,this.poseWorldOf=worldOf,this.poseMixed=!!time,model.partPoses(worldOf)),parts=this.parts;for(let i=0;i<pose.length&&i<parts.length;++i){var o=parts[i],m=pose[i],skin=model.parts[i].skin;o.destroyed||o.parent!==this||(skin?gltfSkinApply(skin,worldOf,model.modelMatrix,o.mesh):(o.localMatrix=m,o.pos3D=m.getTranslation(),o.rotation3D=m.getRotation(),o.scale3D=m.getScale()))}}}update(){super.update();var animation=this.animation,from=this.blendFrom;if(animation&&(this.animationPlaying||from)){let t=this.animationTime;this.animationPlaying&&(t=gltfAnimationStep(animation,t,this.animationSpeed,this.animationLoop),!this.animationLoop)&&(t>=animation.duration||t<=0)&&(this.animationPlaying=!1),from&&(this.blendElapsed+=timeDelta,from.animation)&&(from.time=gltfAnimationStep(from.animation,from.time,from.speed,from.loop)),this.setAnimationTime(t),from&&this.blendElapsed>=this.blendTime&&(this.blendFrom=void 0)}}getJointMatrix(name){var tree=this.model.nodeTree,index=tree?.nodes?.findIndex(node=>node.name===name)??-1;if(!(index<0))return tree=this.poseWorldOf||gltfNodeWorlds(tree,new Map),this.getMatrix().multiply(this.model.modelMatrix).multiply(tree(index))}}async function loadGLTF(url){var response=await loadFetch(url,"loadGLTF");if(!response.ok)throw new Error(`loadGLTF: could not load ${url}, ${response.status} `+response.statusText);var from=response.url||url;let base="";/^(blob|data):/i.test(from)||(base=response.url?new URL(".",from).href:from.slice(0,from.lastIndexOf("/")+1));from=await response.arrayBuffer();try{return await parseGLTF(from,base)}catch(e){response=/^\s*</.test(String.fromCharCode(...new Uint8Array(from,0,min(from.byteLength,64))));throw new Error("loadGLTF "+url+(response?" is a web page, so the path may be wrong":": "+e.message))}}async function parseGLTF(data,baseUrl="",files){let json=data=ArrayBuffer.isView(data)?data.buffer.slice(data.byteOffset,data.byteOffset+data.byteLength):data,glbBuffer;if(data instanceof ArrayBuffer){var view=new DataView(data);if(12<=data.byteLength&&1179937895===view.getUint32(0,!0)){if(2!==view.getUint32(4,!0))throw new Error("only GLB version 2 is read");for(let offset=12;offset+8<=data.byteLength;){var length=view.getUint32(offset,!0),type=view.getUint32(offset+4,!0),chunk=data.slice(offset+8,offset+8+length);1313821514===type?json=(new TextDecoder).decode(chunk):5130562===type&&(glbBuffer=chunk),offset+=8+length}}else json=(new TextDecoder).decode(data)}if(!(json="string"==typeof json?JSON.parse(json):json).asset||!String(json.asset.version).startsWith("2"))throw new Error("only glTF 2.0 is read");for(const name of json.extensionsRequired||[]){if("KHR_draco_mesh_compression"===name||"EXT_meshopt_compression"===name)throw new Error(`glTF with ${name} is not read, export it uncompressed`);if("KHR_texture_basisu"===name)throw new Error("glTF with KHR_texture_basisu textures is not read, export them as png or jpeg")}json.extensionsUsed?.includes("KHR_texture_basisu")&&!json.extensionsRequired?.includes("KHR_texture_basisu")&&console.warn("glTF: KHR_texture_basisu textures are not read and are left out, export them as png or jpeg");const buffers=await Promise.all((json.buffers||[]).map((buffer,i)=>buffer.uri?gltfFetch(buffer.uri,baseUrl,files).then(r=>r.arrayBuffer()):glbBuffer)),usedTextures=new Set((json.materials||[]).flatMap(m=>[m.pbrMetallicRoughness?.baseColorTexture?.index,m.normalTexture?.index,m.emissiveTexture?.index])),normalTextures=new Set((json.materials||[]).map(m=>m.normalTexture?.index)),opaqueTextures=function(json){var opaque=new Set,cut=new Set;for(const material of json.materials||[]){var index=material.pbrMetallicRoughness?.baseColorTexture?.index;void 0!==index&&(material.alphaMode&&"OPAQUE"!==material.alphaMode?cut:opaque).add(index)}for(const index of cut)opaque.delete(index);return opaque}(json),textures=await Promise.all((json.textures||[]).map(async(texture,index)=>{if(glContext&&"undefined"!=typeof createImageBitmap&&usedTextures.has(index))try{var view,source=texture.source??texture.extensions?.EXT_texture_webp?.source??texture.extensions?.EXT_texture_avif?.source,image=json.images[source],sampler=json.samplers?.[texture.sampler]||{};let blob;blob=image.uri?await gltfFetch(image.uri,baseUrl,files).then(r=>r.blob()):(view=json.bufferViews[image.bufferView],new Blob([new Uint8Array(buffers[view.buffer],view.byteOffset||0,view.byteLength)],{type:image.mimeType}));var opaque=opaqueTextures.has(index)&&"image/jpeg"!==blob.type,bitmap=normalTextures.has(index)?await createImageBitmap(blob,{colorSpaceConversion:"none",premultiplyAlpha:"none"}):opaque?await createImageBitmap(blob,{premultiplyAlpha:"none"}).then(gltfOpaqueImage):await createImageBitmap(blob);return new TextureInfo(bitmap,!0,[sampler.wrapS??10497,sampler.wrapT??10497],9728===sampler.magFilter)}catch(e){console.warn("glTF image not loaded, left out: "+(e?.message||e))}})),parts=[],parents=[],restInverse=[],restPose=[],visited=new Set,visit=((json.nodes||[]).forEach((node,i)=>(node.children||[]).forEach(child=>parents[child]??=i)),(index,parentMatrix,parentIndex,parentRest)=>{if(visited.has(index))throw new Error("glTF node "+index+" is reached twice, nodes must form trees");visited.add(index);var node=json.nodes[index],local=gltfNodeMatrix(node);let matrix=parentMatrix?parentMatrix.copy().multiply(local):local,rest=parentRest&&parentRest.copy().multiply(local);if(!node.matrix&&node.scale?.includes(0)&&(rest||=matrix,parentRest=gltfNodeMatrix({...node,scale:node.scale.map(s=>s||1)}),matrix=parentMatrix?parentMatrix.copy().multiply(parentRest):parentRest),parents[index]=parentIndex,restInverse[index]=matrix.copy().invert(),rest&&(restPose[index]=rest.copy().multiply(restInverse[index])),void 0!==node.mesh){var mesh=json.meshes[node.mesh],skin=void 0!==node.skin?json.skins?.[node.skin]:void 0;for(const primitive of mesh.primitives){var skinned=skin?.joints?.length&&void 0!==primitive.attributes.JOINTS_0&&void 0!==primitive.attributes.WEIGHTS_0,part=function(json,buffers,textures,primitive,matrix,name){var mode=primitive.mode??4;if(!(mode<4)){var attributes=primitive.attributes,read=(index,make)=>{var{data,components,count}=gltfAccessor(json,buffers,index),list=[];for(let i=0;i<count;++i)list.push(make(data,i*components,components));return list},points=read(attributes.POSITION,(d,k)=>vec3(d[k],d[k+1],d[k+2])),normals=void 0!==attributes.NORMAL?read(attributes.NORMAL,(d,k)=>vec3(d[k],d[k+1],d[k+2])):void 0,material=json.materials?.[primitive.material]||{},pbr=material.pbrMetallicRoughness||{},textureRef=pbr.baseColorTexture,uvTransform=textureRef?.extensions?.KHR_texture_transform,uvAccessor=attributes["TEXCOORD_"+(uvTransform?.texCoord??textureRef?.texCoord??0)];const[ox,oy]=uvTransform?.offset||[0,0],[sx,sy]=uvTransform?.scale||[1,1],r=uvTransform?.rotation||0,c=cos(r),s=sin(r);uvTransform=void 0!==uvAccessor?read(uvAccessor,(d,k)=>vec2(c*sx*d[k]+s*sy*d[k+1]+ox,c*sy*d[k+1]-s*sx*d[k]+oy)):void 0,uvAccessor=void 0!==attributes.COLOR_0?read(attributes.COLOR_0,(d,k,n)=>rgb(gltfSRGB(d[k]),gltfSRGB(d[k+1]),gltfSRGB(d[k+2]),3<n?d[k+3]:1)):void 0;let indices=void 0!==primitive.indices?Array.from(gltfAccessor(json,buffers,primitive.indices).data):points.map((_,i)=>i);5===mode?indices=indices.flatMap((_,i,s)=>i<2?[]:1&i?[s[i-1],s[i-2],s[i]]:[s[i-2],s[i-1],s[i]]):6===mode&&(indices=indices.flatMap((_,i,s)=>i<2?[]:[s[0],s[i-1],s[i]]));var read=(new Mesh).addTriangles(points,indices,normals,uvTransform,uvAccessor),attributes=normals?void 0:read.indices.slice(),primitive=(normals||read.computeNormals(!1),read.transform(matrix),pbr.baseColorFactor||[1,1,1,1]),mode=(read.doubleSided=!!material.doubleSided,material.extensions?.KHR_materials_transmission?.transmissionFactor||0),points="BLEND"===material.alphaMode,uvTransform=(points?primitive[3]:1)*(1-.8*mode),uvAccessor=textureRef&&json.textures?.[textureRef.index],normals=new GLTFPart(name,read,rgb(gltfSRGB(primitive[0]),gltfSRGB(primitive[1]),gltfSRGB(primitive[2]),uvTransform),textureRef?textures[textureRef.index]:void 0,points||0<mode),matrix=(normals.pixelated=9728===json.samplers?.[uvAccessor?.sampler]?.magFilter,normals.vertexSource=attributes,normals.unlit=!!material.extensions?.KHR_materials_unlit,material.normalTexture),name=material.emissiveTexture,[read,primitive,uvTransform]=(normals.normalMap=matrix&&textures[matrix.index],normals.normalScale=matrix?.scale??1,normals.roughness=pbr.roughnessFactor??1,material.emissiveFactor||[0,0,0]);return(read||primitive||uvTransform)&&(textureRef=material.extensions?.KHR_materials_emissive_strength?.emissiveStrength??1,normals.emissiveMap=name?textures[name.index]:function(){if(gltfWhiteTextureInfo||!glContext||!canvasAvailable())return gltfWhiteTextureInfo;var context=createCanvasContext(1);return context.fillStyle="#fff",context.fillRect(0,0,1,1),gltfWhiteTextureInfo=new TextureInfo(context.canvas)}(),normals.emissiveMapColor=rgb(gltfSRGB(read)*textureRef,gltfSRGB(primitive)*textureRef,gltfSRGB(uvTransform)*textureRef)),normals}}(json,buffers,textures,primitive,skinned?new Matrix4:matrix,node.name||mesh.name||"part "+parts.length);part&&(part.node=index,skinned&&(part.skin=function(json,buffers,skin,primitive,part){const joints=skin.joints,mesh=part.mesh,count=mesh.points.length,bind=void 0!==skin.inverseBindMatrices?gltfAccessor(json,buffers,skin.inverseBindMatrices).data:void 0,inverseBind=joints.map((_,j)=>bind?new Matrix4(bind.subarray(16*j,16*j+16)):new Matrix4),attributes=primitive.attributes,read=name=>gltfAccessor(json,buffers,attributes[name]).data,sets=[[read("JOINTS_0"),read("WEIGHTS_0")]],vertexJoints=(void 0!==attributes.JOINTS_1&&void 0!==attributes.WEIGHTS_1&&sets.push([read("JOINTS_1"),read("WEIGHTS_1")]),new Uint16Array(4*count)),vertexWeights=new Float32Array(4*count),bindPoints=new Float32Array(3*count),bindNormals=new Float32Array(3*count);for(let v=0;v<count;++v){var fileJoints,fileWeights,from=part.vertexSource?part.vertexSource[v]:v,influences=[];for([fileJoints,fileWeights]of sets)for(let k=0;k<4;++k)influences.push([fileJoints[4*from+k],fileWeights[4*from+k]]);1<sets.length&&influences.sort((a,b)=>b[1]-a[1]);let total=0;for(let k=0;k<4;++k)total+=influences[k][1];for(let k=0;k<4;++k){var[joint,weight]=influences[k];vertexJoints[4*v+k]=joint<joints.length?joint:0,vertexWeights[4*v+k]=total?weight/total:k?0:1}var p=mesh.points[v],n=mesh.normals[v];bindPoints.set([p.x,p.y,p.z],3*v),bindNormals.set([n.x,n.y,n.z],3*v),mesh.points[v]=p.copy(),mesh.normals[v]=n.copy()}return{joints:joints,inverseBind:inverseBind,vertexJoints:vertexJoints,vertexWeights:vertexWeights,bindPoints:bindPoints,bindNormals:bindNormals}}(json,buffers,skin,primitive,part)),parts.push(part))}}for(const child of node.children||[])visit(child,matrix,index,rest)});let animations;try{var scene=json.scenes?.[json.scene??0];if(scene)(scene.nodes||[]).forEach(i=>visit(i));else if(json.nodes){const children=new Set(json.nodes.flatMap(n=>n.children||[]));json.nodes.forEach((n,i)=>children.has(i)||visit(i))}animations=(json.animations||[]).map((animation,i)=>new GLTFAnimation(animation.name||"animation "+i,animation.channels.filter(c=>void 0!==c.target.node&&["translation","rotation","scale"].includes(c.target.path)&&!json.nodes[c.target.node].matrix).map(c=>{var sampler=animation.samplers[c.sampler];return{node:c.target.node,path:c.target.path,interpolation:sampler.interpolation||"LINEAR",times:gltfAccessor(json,buffers,sampler.input).data,values:gltfAccessor(json,buffers,sampler.output).data,components:"rotation"===c.target.path?4:3}})))}catch(error){for(const texture of textures)texture?.destroyWebGLTexture();throw error}var used=new Set(parts.flatMap(p=>[p.textureInfo,p.normalMap,p.emissiveMap]));for(const texture of textures)texture&&!used.has(texture)&&texture.destroyWebGLTexture();var scene={nodes:json.nodes,parents:parents,restInverse:restInverse,restPose:restPose},restWorlds=gltfNodeWorlds(scene,new Map);for(const part of parts)part.skin&&gltfSkinApply(part.skin,restWorlds,RENDER3D_IDENTITY,part.mesh);return new GLTFModel(parts,animations,scene)}function gltfPoseNodes(tree,animation,time){var moved=new Map;for(const channel of animation.channels){let node=moved.get(channel.node);node||moved.set(channel.node,node=gltfNodeTRS(tree.nodes[channel.node])),!function(channel,time,out){var{times,values,components:n,interpolation:channel}=channel,last=times.length-1,cubic="CUBICSPLINE"===channel,stride=cubic?3*n:n,at=cubic?n:0;let k=0,high=last;for(;k<high;){var mid=k+high+1>>1;times[mid]<=time?k=mid:high=mid-1}if(k>=last||time<=times[0]||"STEP"===channel)for(let j=0;j<n;++j)out[j]=values[k*stride+at+j];else{var dt=times[k+1]-times[k],t=(time-times[k])/dt,a=k*stride,b=a+stride;if(cubic){var last=t*t,channel=last*t,h00=2*channel-3*last+1,h10=channel-2*last+t,h01=-2*channel+3*last,h11=channel-last;for(let j=0;j<n;++j)out[j]=h00*values[a+n+j]+h10*dt*values[a+2*n+j]+h01*values[b+n+j]+h11*dt*values[b+j]}else if(4===n){let dot=0;for(let j=0;j<4;++j)dot+=values[a+j]*values[b+j];cubic=dot<0?-1:1;dot*=cubic;let wa=1-t,wb=t*cubic;dot<.9995&&(channel=Math.acos(dot),last=sin(channel),wa=sin((1-t)*channel)/last,wb=sin(t*channel)/last*cubic);for(let j=0;j<4;++j)out[j]=wa*values[a+j]+wb*values[b+j]}else for(let j=0;j<n;++j)out[j]=values[a+j]+(values[b+j]-values[a+j])*t;if(4===n){var l=hypot(out[0],out[1],out[2],out[3])||1;for(let j=0;j<4;++j)out[j]/=l}}}(channel,time,node[channel.path])}return moved}function gltfNodeWorlds(tree,moved){const world=[],worldOf=i=>{var local,parent;return world[i]||(local=gltfNodeMatrix(moved.get(i)||tree.nodes[i]),parent=tree.parents[i],world[i]=void 0===parent?local:worldOf(parent).copy().multiply(local))};return worldOf}function gltfAnimationStep(animation,time,speed,loop){animation=animation.duration,time+=timeDelta*speed;return loop?animation?mod(time,animation):0:clamp(time,0,animation)}function gltfSkinApply(skin,worldOf,modelMatrix,mesh){var jointCount=skin.joints.length,M=skin.jointMatrices||=new Float32Array(21*jointCount),m=gltfSkinScratch[0],n=gltfSkinScratch[1];for(let j=0;j<jointCount;++j){m.m.set(modelMatrix.m),m.multiply(worldOf(skin.joints[j])).multiply(skin.inverseBind[j]),n.m.set(m.m),n.invert().transpose();var a=m.m,b=n.m,o=21*j;M[o]=a[0],M[1+o]=a[1],M[2+o]=a[2],M[3+o]=a[4],M[4+o]=a[5],M[5+o]=a[6],M[6+o]=a[8],M[7+o]=a[9],M[8+o]=a[10],M[9+o]=a[12],M[10+o]=a[13],M[11+o]=a[14],M[12+o]=b[0],M[13+o]=b[1],M[14+o]=b[2],M[15+o]=b[4],M[16+o]=b[5],M[17+o]=b[6],M[18+o]=b[8],M[19+o]=b[9],M[20+o]=b[10]}var{vertexJoints,vertexWeights,bindPoints,bindNormals}=skin,points=mesh.points,normals=mesh.normals,layout=mesh.vertexLayout,count=points.length,floats=layout&&mesh.indices&&layout.pointCount===count?new Float32Array(layout.data):void 0;let r=0,x0=1/0,y0=1/0,z0=1/0,x1=-1/0,y1=-1/0,z1=-1/0;for(let v=0;v<count;++v){var v3=3*v,v4=4*v,px=bindPoints[v3],py=bindPoints[1+v3],pz=bindPoints[2+v3],qx=bindNormals[v3],qy=bindNormals[1+v3],qz=bindNormals[2+v3];let x=0,y=0,z=0,nx=0,ny=0,nz=0;for(let k=0;k<4;++k){var w=vertexWeights[v4+k];if(w){const o=21*vertexJoints[v4+k];x+=w*(M[o]*px+M[3+o]*py+M[6+o]*pz+M[9+o]),y+=w*(M[1+o]*px+M[4+o]*py+M[7+o]*pz+M[10+o]),z+=w*(M[2+o]*px+M[5+o]*py+M[8+o]*pz+M[11+o]),nx+=w*(M[12+o]*qx+M[15+o]*qy+M[18+o]*qz),ny+=w*(M[13+o]*qx+M[16+o]*qy+M[19+o]*qz),nz+=w*(M[14+o]*qx+M[17+o]*qy+M[20+o]*qz)}}var v3=points[v],normal=normals[v],l=(nx*nx+ny*ny+nz*nz)**.5||1;v3.x=x,v3.y=y,v3.z=z,normal.x=nx/l,normal.y=ny/l,normal.z=nz/l,floats&&(floats[v3=9*v]=x,floats[1+v3]=y,floats[2+v3]=z,floats[3+v3]=normal.x,floats[4+v3]=normal.y,floats[5+v3]=normal.z,r=max(r,x*x+y*y+z*z),x0=min(x0,x),y0=min(y0,y),z0=min(z0,z),x1=max(x1,x),y1=max(y1,y),z1=max(z1,z))}floats&&(mesh.radius=r**.5,mesh.bounds=count?{min:vec3(x0,y0,z0),max:vec3(x1,y1,z1)}:{min:vec3(),max:vec3()},mesh.vertexDataPacked=!0),mesh.dirty=!0}const gltfSkinScratch=[new Matrix4,new Matrix4];function gltfNodeTRS(node){return{translation:[...node.translation||[0,0,0]],rotation:[...node.rotation||[0,0,0,1]],scale:[...node.scale||[1,1,1]]}}function gltfOpaqueImage(image){var gl=glContext,{width,height}=image;if(gl.isContextLost())return image;var data=new Uint8ClampedArray(width*height*4),texture=gl.createTexture(),framebuffer=gl.createFramebuffer(),bound=gl.getParameter(gl.FRAMEBUFFER_BINDING);try{gl.bindTexture(gl.TEXTURE_2D,texture),gl.texImage2D(gl.TEXTURE_2D,0,gl.RGBA,gl.RGBA,gl.UNSIGNED_BYTE,image),gl.bindFramebuffer(gl.FRAMEBUFFER,framebuffer),gl.framebufferTexture2D(gl.FRAMEBUFFER,gl.COLOR_ATTACHMENT0,gl.TEXTURE_2D,texture,0),gl.readPixels(0,0,width,height,gl.RGBA,gl.UNSIGNED_BYTE,data)}finally{gl.bindFramebuffer(gl.FRAMEBUFFER,bound),gl.bindTexture(gl.TEXTURE_2D,glActiveTexture),gl.deleteFramebuffer(framebuffer),gl.deleteTexture(texture)}for(let i=3;i<data.length;i+=4)data[i]=255;return image.close(),createImageBitmap(new ImageData(data,width,height))}function gltfFetch(uri,baseUrl,files){if(files&&!/^(data|https?|blob):/i.test(uri)){let name=uri.replace(/[?#].*$/,"");try{name=decodeURIComponent(name)}catch{}const clean=text=>{var parts=[];for(const part of text.replace(/\\/g,"/").split("/"))if(".."===part?!parts.pop():part&&"."!==part&&!parts.push(part))return;return parts.join("/")};var path=clean(baseUrl+name);if(void 0===path)return Promise.reject(new Error("glTF needs "+name+", which is above the files given"));const lower=path.toLowerCase(),file=lower.slice(lower.lastIndexOf("/")+1),byPath=new Map([...files].map(([key,value])=>[clean(key)?.toLowerCase(),value]));var files=files.get(path)??byPath.get(lower),keys=[...byPath.keys()];return files?Promise.resolve(new Response(files)):(files=keys.filter(k=>k?.includes("/")&&lower.endsWith("/"+k)).sort((a,b)=>b.length-a.length)[0])?Promise.resolve(new Response(byPath.get(files))):1===(files=new Set(keys.filter(k=>k?.slice(k.lastIndexOf("/")+1)===file).map(k=>byPath.get(k)))).size?Promise.resolve(new Response([...files][0])):Promise.reject(new Error("glTF needs "+path+(files.size?", and more than one file has its name":", which is not among the files given")))}if(uri.startsWith("data:")){var keys=uri.indexOf(","),bytes=atob(uri.slice(keys+1)),data=new Uint8Array(bytes.length);for(let i=0;i<bytes.length;++i)data[i]=bytes.charCodeAt(i);return Promise.resolve(new Response(data.buffer,{headers:{"Content-Type":uri.slice(5,uri.indexOf(";"))}}))}path="undefined"!=typeof location&&location.href,files=path?new URL(baseUrl,path):/^[a-z][a-z0-9+.-]*:/i.test(baseUrl)?baseUrl:void 0;const url=files?new URL(uri,files).href:baseUrl+uri;return loadFetch(url,"glTF").then(r=>{if(r.ok)return r;throw new Error("glTF needs "+uri+", not found at "+url)})}function gltfNodeMatrix(node){var x,y,z,w,sx,sy,sz;return node.matrix?new Matrix4(node.matrix):([x,y,z,w]=node.rotation||[0,0,0,1],[sx,sy,sz]=node.scale||[1,1,1],node=node.translation||[0,0,0],new Matrix4([(1-2*(y*y+z*z))*sx,2*(x*y+z*w)*sx,2*(x*z-y*w)*sx,0,2*(x*y-z*w)*sy,(1-2*(x*x+z*z))*sy,2*(y*z+x*w)*sy,0,2*(x*z+y*w)*sz,2*(y*z-x*w)*sz,(1-2*(x*x+y*y))*sz,0,node[0],node[1],node[2],1]))}function gltfAccessor(json,buffers,index){var a=json.accessors[index],view=json.bufferViews?.[a.bufferView],components={SCALAR:1,VEC2:2,VEC3:3,VEC4:4,MAT4:16}[a.type],types={5120:Int8Array,5121:Uint8Array,5122:Int16Array,5123:Uint16Array,5125:Uint32Array,5126:Float32Array},Type=types[a.componentType];if(!components||!Type)throw new Error(`glTF accessor of ${a.type} ${a.componentType} is not read`);var size=Type.BYTES_PER_ELEMENT,scales=[[Int8Array,127],[Uint8Array,255],[Int16Array,32767],[Uint16Array,65535]],scale=a.normalized&&new Map(scales).get(Type)||1;if(!(view||a.count<=1<<20))throw new Error("glTF accessor "+index+" has no buffer and a count of "+a.count);var scales=view&&(buffers[view.buffer]?.byteLength??0),stride=view?.byteStride||components*size;if(view&&!((view.byteOffset||0)+(a.byteOffset||0)+(a.count-1)*stride+components*size<=scales))throw new Error("glTF accessor "+index+" reaches past its buffer");var out=new Float32Array(a.count*components);if(view){var buffer=buffers[view.buffer],offset=(view.byteOffset||0)+(a.byteOffset||0);if(offset%size||stride%size)throw new Error("glTF accessor "+index+" is not aligned to its "+size+" byte values");if(stride===components*size)out.set(new Type(buffer,offset,a.count*components));else for(let i=0;i<a.count;++i)out.set(new Type(buffer,offset+i*stride,components),i*components)}var sparse=a.sparse;if(sparse){var{indices:scales,values:view}=sparse,types=types[scales.componentType],indexView=json.bufferViews?.[scales.bufferView],json=json.bufferViews?.[view.bufferView];if(!types||!indexView||!json)throw new Error("glTF sparse accessor is missing its indices or values");var indexAt=(indexView.byteOffset||0)+(scales.byteOffset||0),valueAt=(json.byteOffset||0)+(view.byteOffset||0);if(indexAt%types.BYTES_PER_ELEMENT||valueAt%size||!(indexAt+sparse.count*types.BYTES_PER_ELEMENT<=(buffers[indexView.buffer]?.byteLength??0))||!(valueAt+sparse.count*components*size<=(buffers[json.buffer]?.byteLength??0)))throw new Error("glTF sparse accessor "+index+" is not aligned or reaches past its buffer");var at=new types(buffers[indexView.buffer],(indexView.byteOffset||0)+(scales.byteOffset||0),sparse.count),data=new Type(buffers[json.buffer],(json.byteOffset||0)+(view.byteOffset||0),sparse.count*components);for(let i=0;i<sparse.count;++i)out.set(data.subarray(i*components,(i+1)*components),at[i]*components)}if(1!==scale)for(let i=0;i<out.length;++i)out[i]=max(out[i]/scale,-1);return{data:out,components:components,count:a.count}}let gltfWhiteTextureInfo;function gltfSRGB(c){return c<=.0031308?12.92*c:1<=c?c:1.055*c**(1/2.4)-.055}let threeJS;class ThreeJSPlugin{constructor(THREE,cameraFOV=60){threeJS=this,headlessMode||(this.THREE=THREE,this.renderer=new THREE.WebGLRenderer({antialias:!0}),this.scene=new THREE.Scene,this.camera=new THREE.PerspectiveCamera(cameraFOV,1,.1,1e3),this.cameraAlign2D=!0,this.cameraNear=void 0,this.cameraFar=void 0,THREE=this.renderer.domElement,(cameraFOV=mainCanvas.parentElement).insertBefore(THREE,cameraFOV.firstChild),THREE.style.cssText=mainCanvas.style.cssText,setBackgroundCanvas(THREE),engineAddPlugin(void 0,()=>this.render()))}alignCamera2D(){var distance=mainCanvasSize.y/2/cameraScale/tan(this.camera.fov/2*PI/180),baseNear=this.cameraNear??=this.camera.near,baseFar=this.cameraFar??=this.camera.far,scale=max(1,2*distance/baseFar),baseNear=baseNear*scale,baseFar=baseFar*scale;baseNear==this.camera.near&&baseFar==this.camera.far||(this.camera.near=baseNear,this.camera.far=baseFar,this.camera.updateProjectionMatrix()),this.camera.position.set(cameraPos.x,cameraPos.y,distance),this.camera.rotation.set(0,0,-cameraAngle)}render(){var threeCanvas,dpr,bufferSizeX,bufferSizeY;this.renderer&&(threeCanvas=this.renderer.domElement,dpr=getCanvasPixelRatio(),bufferSizeX=mainCanvasSize.x*dpr|0,bufferSizeY=mainCanvasSize.y*dpr|0,threeCanvas.width==bufferSizeX&&threeCanvas.height==bufferSizeY||(this.renderer.setPixelRatio(dpr),this.renderer.setSize(mainCanvasSize.x,mainCanvasSize.y,!1),this.camera.aspect=mainCanvasSize.x/mainCanvasSize.y,this.camera.updateProjectionMatrix()),threeCanvas.style.cssText!=mainCanvas.style.cssText&&(threeCanvas.style.cssText=mainCanvas.style.cssText),this.cameraAlign2D&&this.alignCamera2D(),this.renderer.render(this.scene,this.camera))}}class ThreeJSObject extends EngineObject{constructor(pos,size,mesh,z=0){super(pos,size),this.mesh=mesh,this.z=z,mesh&&(threeJS.scene?.add(mesh),this.syncMesh())}updateTransforms(updateChildren=!0){super.updateTransforms(updateChildren),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(immediate){this.destroyed||(this.mesh&&threeJS.scene?.remove(this.mesh),super.destroy(immediate))}}export{engineName,engineVersion,frameRate,timeDelta,engineObjects,engineObjectsCollide,frame,time,timeReal,averageFPS,paused,getPaused,setPaused,engineInit,engineStep,engineObjectsUpdate,engineObjectsDestroy,engineObjectsCollect,engineObjectsCallback,engineObjectsRaycast,engineAddPlugin,engineAddLoad,debug,debugOverlay,debugWatermark,debugKey,debugKeysAlways,debugTweakables,levelEditor,LevelEditor,setLevelEditor,ASSERT,LOG,debugPointSize,debugRect,debugPoly,debugCircle,debugPoint,debugLine,debugOverlap,debugText,debugClear,debugScreenshot,debugShowErrors,cameraPos,cameraAngle,cameraScale,timeScale,canvasColorTiles,canvasClearColor,canvasMaxSize,canvasMinAspect,canvasMaxAspect,canvasFixedSize,canvasPixelated,tilesPixelated,canvasPixelRatio,fontDefault,showSplashScreen,loadingScreen,headlessMode,engineManualStep,engineVariableStep,tileDefaultSize,tileDefaultPadding,tileDefaultBleed,enablePhysicsSolver,objectDefaultMass,objectDefaultDamping,objectDefaultAngleDamping,objectDefaultRestitution,objectDefaultFriction,objectMaxSpeed,gravity,particleEmitRateScale,glEnable,drawScreenSpace,glCircleSides,gamepadsEnable,gamepadDirectionEmulateStick,gamepadAxisFilterEnable,inputWASDEmulateDirection,touchInputEnable,touchGamepadEnable,touchGamepadPassthrough,touchPinchWheel,touchGamepadCenterButtonSize,touchGamepadButtonCount,touchGamepadLeftStick,touchGamepadLeftButtonCount,touchGamepadRightStick,touchGamepadAnalog,touchGamepadFloating,touchGamepadSize,touchGamepadAlpha,touchGamepadDisplayTime,touchGamepadVibration,vibrateEnable,soundEnable,soundIgnoreSilentSwitch,soundVolume,soundDefaultRange,soundDefaultTaper,soundPauseWhenHidden,setCameraPos,setCameraAngle,setCameraScale,setTimeScale,setCanvasColorTiles,setCanvasClearColor,setCanvasMaxSize,setCanvasMinAspect,setCanvasMaxAspect,setCanvasFixedSize,setCanvasPixelated,setTilesPixelated,setCanvasPixelRatio,getCanvasPixelRatio,setFontDefault,setShowSplashScreen,setLoadingScreen,setHeadlessMode,setEngineManualStep,setEngineVariableStep,setGLEnable,setDrawScreenSpace,setTileDefaultSize,setTileDefaultPadding,setTileDefaultBleed,setEnablePhysicsSolver,setObjectDefaultMass,setObjectDefaultDamping,setObjectDefaultAngleDamping,setObjectDefaultRestitution,setObjectDefaultFriction,setObjectMaxSpeed,setGravity,setParticleEmitRateScale,setGLCircleSides,setTouchInputEnable,setGamepadsEnable,setGamepadDirectionEmulateStick,setGamepadAxisFilterEnable,setInputWASDEmulateDirection,setTouchGamepadEnable,setTouchGamepadPassthrough,setTouchPinchWheel,setTouchGamepadCenterButtonSize,setTouchGamepadButtonCount,setTouchGamepadLeftStick,setTouchGamepadLeftButtonCount,setTouchGamepadRightStick,setTouchGamepadAnalog,setTouchGamepadFloating,setTouchGamepadSize,setTouchGamepadAlpha,setTouchGamepadDisplayTime,setTouchGamepadVibration,setVibrateEnable,setSoundEnable,setSoundIgnoreSilentSwitch,setSoundVolume,setSoundDefaultRange,setSoundDefaultTaper,setSoundPauseWhenHidden,setDebugWatermark,setDebugKey,setDebugKeysAlways,setDebugTweakables,setDebugOverlay,PI,abs,floor,ceil,round,min,max,sign,hypot,log2,sin,cos,tan,atan2,mod,clamp,percent,distanceWrap,lerpWrap,distanceAngle,lerpAngle,lerp,percentLerp,smoothStep,nearestPowerOfTwo,isPowerOfTwo,isOverlapping,isIntersecting,collideCircleCircle,collideCircleBox,collideBoxBox,lineTest,oscillate,formatTime,fetchJSON,saveText,createCanvasContext,saveCanvas,saveDataURL,shareURL,readSaveData,writeSaveData,noise1D,noise2D,rand,randInt,randBool,randSign,randInCircle,randVec2,randColor,RandomGenerator,Vector2,Color,Timer,vec2,rgb,hsl,isColor,isVector2,isNumber,isStringLike,isArray,WHITE,CLEAR_WHITE,BLACK,CLEAR_BLACK,GRAY,RED,ORANGE,YELLOW,GREEN,CYAN,BLUE,PURPLE,MAGENTA,tile,TileInfo,TextureInfo,loadTexture,SpriteAnimation,SpriteAnimator,Shader,mainCanvas,mainContext,drawContext,workCanvas,workContext,workReadCanvas,workReadContext,backgroundCanvas,mainCanvasSize,textureInfos,drawCount,primitiveCount,screenToWorld,worldToScreen,screenToWorldDelta,worldToScreenDelta,screenToWorldTransform,drawTile,drawRect,drawRectGradient,drawTextureWrapped,drawLineList,drawLine,drawPoly,drawRegularPoly,drawEllipse,drawCircle,drawEllipseGradient,drawCircleGradient,drawCanvas2D,drawText,drawTextScreen,setAdditiveBlendMode,setShader,setBackgroundCanvas,combineCanvases,engineImageFont,ImageFont,isFullscreen,toggleFullscreen,setCursor,getCameraSize,cameraFit,isOnScreen,glCanvas,glContext,glAntialias,glClearCanvas,glSetTexture,glSetTextureWrap,glCompileShader,glCreateProgram,glCreateTexture,glDeleteTexture,glSetTextureData,glFlush,glCopyToContext,glSetAntialias,glMaxTextureSize,glClampTextureSize,glDraw,glDrawUntextured,glDrawPointsTransform,glDrawOutlineTransform,glDrawPoints,glDrawColoredPoints,glSetRenderTarget,glClearRect,keyIsDown,keyWasPressed,keyWasReleased,keyDirection,inputClear,inputClearKey,mouseIsDown,mouseWasPressed,mouseWasReleased,mousePos,mousePosScreen,mouseDelta,mouseDeltaScreen,mouseWheel,touchPinch,mouseInWindow,isUsingGamepad,lastInputDevice,inputMouseMoveThreshold,inputPreventDefault,gamepadPrimary,isTouchDevice,setInputPreventDefault,setInputMouseMoveThreshold,usingMouseInput,usingKeyboardInput,usingGamepadInput,gamepadIsDown,gamepadWasPressed,gamepadWasReleased,gamepadStick,gamepadStickCount,gamepadDpad,gamepadConnected,gamepadVibrate,gamepadVibrateStop,vibrate,vibrateStop,pointerLockRequest,pointerLockExit,pointerLockIsActive,audioContext,audioMasterGain,setAudioMasterEffect,audioDefaultSampleRate,audioIsRunning,Sound,SoundInstance,speak,speakStop,getNoteFrequency,playSamples,playAudioBuffer,createAudioBuffer,zzfx,zzfxG,EngineObject,tileCollisionLayers,tileCollisionGetData,tileCollisionTest,tileCollisionRaycast,tileLayersLoad,tileLayersFromLDtk,objectLayersAddType,objectLayersLoad,TileLayerData,CanvasLayer,TileLayer,TileCollisionLayer,ParticleEmitter,Particle,medals,medalsPreventUnlock,medalDisplayTime,medalDisplaySlideTime,medalDisplaySize,medalsInit,medalsForEach,medalsReset,setMedalDisplayTime,setMedalDisplaySlideTime,setMedalDisplaySize,setMedalsPreventUnlock,Medal,newgrounds,NewgroundsPlugin,NewgroundsMedal,wavedash,WavedashPlugin,WavedashMedal,postProcess,PostProcessPlugin,postProcessBloom,postProcessBloomShader,postProcessEffects,postProcessGlow,postProcessScanlines,postProcessNoise,postProcessVignette,postProcessCurve,postProcessChromatic,postProcessOutline,postProcessTiltShift,postProcessDepthOfField,postProcessTV,lightSystem,LightSystemPlugin,Light,DirectionalLight,AudioEffect,AudioFilter,AudioReverb,AudioDelay,AudioDistortion,AudioCompressor,uiSystem,uiDebug,uiSetDebug,UISystemPlugin,UIObject,UIText,UITextInput,UITile,UIButton,UICheckbox,UISlider,UIVideo,UILayout,box2d,box2dDebug,box2dSetDebug,box2dInit,Box2dPlugin,Box2dObject,Box2dStaticObject,Box2dKinematicObject,Box2dTileLayer,Box2dRaycastResult,Box2dJoint,Box2dTargetJoint,Box2dDistanceJoint,Box2dPinJoint,Box2dRopeJoint,Box2dRevoluteJoint,Box2dGearJoint,Box2dPrismaticJoint,Box2dWheelJoint,Box2dWeldJoint,Box2dFrictionJoint,Box2dPulleyJoint,Box2dMotorJoint,drawNineSlice,drawNineSliceScreen,drawThreeSlice,drawThreeSliceScreen,TileSlice,drawCrescent,getCrescentPoints,Tween,tweenProperty,tweenStopAll,tweenUpdate,Ease,setScene,getScene,ParallaxLayer,parallaxMountains,PathFinder,PathFinderNode,tweak,tweakDivider,tweakButton,tweakEngineDefaults,vec3,isVector3,randVector3,randInSphere,Vector3,Matrix4,Ray3D,buildMatrix,isPointInBox3D,isOverlapping3D,collideSphereSphere,collideSphereBox,collideSphereInBox,collideSphereCylinder,collideBoxBox3D,raycastSphere,raycastPlane,raycastBox,render3D,Render3DPlugin,CubeMap,makeCubeMap,loadCubeMap,Camera3D,EngineObject3D,Mesh,InstancedMesh3D,buildLathe,buildCylinder,buildSphere,buildCone,buildCapsule,buildTorus,buildBox,buildGrid,buildRibbon,buildLoft,buildSky,normalMapFromHeight,buildExtrude,buildText3D,HeightMap,VoxelMap,Light3D,DirectionalLight3D,CameraControl3D,FirstPersonCamera3D,ParticleEmitter3D,Trail3D,LensFlare3D,engineObjectsCollect3D,engineObjectsCallback3D,engineObjectsRaycast3D,parseOBJ,loadOBJ,GLTFModel,GLTFPart,GLTFObject,GLTFAnimation,parseGLTF,loadGLTF,debugBox3D,debugSphere3D,debugLine3D,debugPoint3D,level3DAddType,level3DAddMesh,level3DLoad,level3DAddPrefab,level3DLoadPrefab,level3DSpawn,Prefab3D,level3DVoxelSetup,threeJS,ThreeJSPlugin,ThreeJSObject,textureSheetSize,textureSheetPadding,setTextureSheetSize,setTextureSheetPadding,textureSheets,TextureSheet,loadSprite,loadTiles,loadAtlas,parseAtlas,spritesReady,textIntl as particleEffectGroups,particleEffectSettings,particleEffectBehaviors,particleEffectShapes,particleEffectSanitize,particleEffectRecolor,particleEffectsAdd,particleEffectsGet,particleEffectsParse,particleEffectsText,particleEffectsLoad,particleEffectShapeTile,particleEffect,particleEffect3D,particleEffectsBuiltIn,particleEffectApply,particleEffectApply3D,particleEffectFromEmitter,particleEffectsAddBehavior};