littlejsengine 1.11.4 → 1.11.5

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 +1 @@
1
- let showWatermark=0,debugKey="";const debug=0,debugOverlay=0,debugPhysics=0,debugParticles=0,debugRaycast=0,debugGamepads=0,debugMedals=0;function ASSERT(){}function debugInit(){}function debugUpdate(){}function debugRender(){}function debugRect(){}function debugPoly(){}function debugCircle(){}function debugPoint(){}function debugLine(){}function debugOverlap(){}function debugText(){}function debugClear(){}function debugSaveCanvas(){}function debugSaveText(){}function debugSaveDataURL(){}const PI=Math.PI;function abs(a){return Math.abs(a)}function min(a,b){return Math.min(a,b)}function max(a,b){return Math.max(a,b)}function sign(a){return Math.sign(a)}function mod(a,b=1){return(a%b+b)%b}function clamp(a,b=0,c=1){return a<b?b:a>c?c:a}function percent(a,b,c){return(c-=b)?clamp((a-b)/c):0}function lerp(a,b,c){return b+clamp(a)*(c-b)}function distanceWrap(a,b,c=1){a=(a-b)%c;return 2*a%c-a}function lerpWrap(a,b,c,d=1){return c+clamp(a)*distanceWrap(b,c,d)}function distanceAngle(a,b){return distanceWrap(a,b,2*PI)}function lerpAngle(a,b,c){return lerpWrap(a,b,c,2*PI)}function smoothStep(a){return a*a*(3-2*a)}function nearestPowerOfTwo(a){return 2**Math.ceil(Math.log2(a))}function isOverlapping(a,b,c,d=vec2()){return 2*abs(a.x-c.x)<b.x+d.x&&2*abs(a.y-c.y)<b.y+d.y}function isIntersecting(a,b,c,d){c=c.subtract(d.scale(.5));d=c.add(d);b=b.subtract(a);c=a.subtract(c);d=a.subtract(d);a=[-b.x,b.x,-b.y,b.y];b=[c.x,-d.x,c.y,-d.y];c=0;d=1;for(let e=4;e--;)if(a[e]){const f=b[e]/a[e];if(0>a[e]){if(f>d)return!1;c=max(f,c)}else{if(f<c)return!1;d=min(f,d)}}else if(0>b[e])return!1;return!0}function wave(a=1,b=1,c=time){return b/2*(1-Math.cos(c*a*2*PI))}function formatTime(a){return(a/60|0)+":"+(10>a%60?"0":"")+(a%60|0)}function rand(a=1,b=0){return b+Math.random()*(a-b)}function randInt(a,b=0){return Math.floor(rand(a,b))}function randSign(){return 2*randInt(2)-1}function randVector(a=1){return(new Vector2).setAngle(rand(2*PI),a)}function randInCircle(a=1,b=0){return 0<a?randVector(a*rand(b/a,1)**.5):new Vector2}function randColor(a=new Color,b=new Color(0,0,0,1),c=!1){return c?a.lerp(b,rand()):new Color(rand(a.r,b.r),rand(a.g,b.g),rand(a.b,b.b),rand(a.a,b.a))}class RandomGenerator{constructor(a){this.seed=a}float(a=1,b=0){this.seed^=this.seed<<13;this.seed^=this.seed>>>17;this.seed^=this.seed<<5;return b+(a-b)*abs(this.seed%1e8)/1e8}int(a,b=0){return Math.floor(this.float(a,b))}sign(){return.5<this.float()?1:-1}}function vec2(a=0,b){return"number"==typeof a?new Vector2(a,void 0==b?a:b):new Vector2(a.x,a.y)}function isVector2(a){return a instanceof Vector2}class Vector2{constructor(a=0,b=0){this.x=a;this.y=b;ASSERT(this.isValid())}set(a=0,b=0){this.x=a;this.y=b;ASSERT(this.isValid());return this}copy(){return new Vector2(this.x,this.y)}add(a){ASSERT(isVector2(a));return new Vector2(this.x+a.x,this.y+a.y)}subtract(a){ASSERT(isVector2(a));return new Vector2(this.x-a.x,this.y-a.y)}multiply(a){ASSERT(isVector2(a));return new Vector2(this.x*a.x,this.y*a.y)}divide(a){ASSERT(isVector2(a));return new Vector2(this.x/a.x,this.y/a.y)}scale(a){ASSERT(!isVector2(a));return new Vector2(this.x*a,this.y*a)}length(){return this.lengthSquared()**.5}lengthSquared(){return this.x**2+this.y**2}distance(a){ASSERT(isVector2(a));return this.distanceSquared(a)**.5}distanceSquared(a){ASSERT(isVector2(a));return(this.x-a.x)**2+(this.y-a.y)**2}normalize(a=1){const b=this.length();return b?this.scale(a/b):new Vector2(0,a)}clampLength(a=1){const b=this.length();return b>a?this.scale(a/b):this}dot(a){ASSERT(isVector2(a));return this.x*a.x+this.y*a.y}cross(a){ASSERT(isVector2(a));return this.x*a.y-this.y*a.x}angle(){return Math.atan2(this.x,this.y)}setAngle(a=0,b=1){this.x=b*Math.sin(a);this.y=b*Math.cos(a);return this}rotate(a){const b=Math.cos(a);a=Math.sin(a);return new Vector2(this.x*b-this.y*a,this.x*a+this.y*b)}setDirection(a,b=1){a=mod(a,4);ASSERT(0==a||1==a||2==a||3==a);return vec2(a%2?a-1?-b:b:0,a%2?0:a?-b:b)}direction(){return abs(this.x)>abs(this.y)?0>this.x?3:1:0>this.y?2:0}invert(){return new Vector2(this.y,-this.x)}floor(){return new Vector2(Math.floor(this.x),Math.floor(this.y))}area(){return abs(this.x*this.y)}lerp(a,b){ASSERT(isVector2(a));return this.add(a.subtract(this).scale(clamp(b)))}arrayCheck(a){ASSERT(isVector2(a));return 0<=this.x&&0<=this.y&&this.x<a.x&&this.y<a.y}toString(a=3){if(debug)return`(${(0>this.x?"":" ")+this.x.toFixed(a)},${(0>this.y?"":" ")+this.y.toFixed(a)} )`}isValid(){return"number"==typeof this.x&&!isNaN(this.x)&&"number"==typeof this.y&&!isNaN(this.y)}}function rgb(a,b,c,d){return new Color(a,b,c,d)}function hsl(a,b,c,d){return(new Color).setHSLA(a,b,c,d)}function isColor(a){return a instanceof Color}class Color{constructor(a=1,b=1,c=1,d=1){this.r=a;this.g=b;this.b=c;this.a=d;ASSERT(this.isValid())}set(a=1,b=1,c=1,d=1){this.r=a;this.g=b;this.b=c;this.a=d;ASSERT(this.isValid());return this}copy(){return new Color(this.r,this.g,this.b,this.a)}add(a){ASSERT(isColor(a));return new Color(this.r+a.r,this.g+a.g,this.b+a.b,this.a+a.a)}subtract(a){ASSERT(isColor(a));return new Color(this.r-a.r,this.g-a.g,this.b-a.b,this.a-a.a)}multiply(a){ASSERT(isColor(a));return new Color(this.r*a.r,this.g*a.g,this.b*a.b,this.a*a.a)}divide(a){ASSERT(isColor(a));return new Color(this.r/a.r,this.g/a.g,this.b/a.b,this.a/a.a)}scale(a,b=a){return new Color(this.r*a,this.g*a,this.b*a,this.a*b)}clamp(){return new Color(clamp(this.r),clamp(this.g),clamp(this.b),clamp(this.a))}lerp(a,b){ASSERT(isColor(a));return this.add(a.subtract(this).scale(clamp(b)))}setHSLA(a=0,b=0,c=1,d=1){a=mod(a,1);b=clamp(b);c=clamp(c);b=.5>c?c*(1+b):c+b-c*b;c=2*c-b;const e=(f,g,k)=>1>6*(k=mod(k,1))?f+6*(g-f)*k:1>2*k?g:2>3*k?f+(g-f)*(4-6*k):f;this.r=e(c,b,a+1/3);this.g=e(c,b,a);this.b=e(c,b,a-1/3);this.a=d;ASSERT(this.isValid());return this}HSLA(){const a=clamp(this.r),b=clamp(this.g),c=clamp(this.b),d=clamp(this.a),e=Math.max(a,b,c),f=Math.min(a,b,c),g=(e+f)/2;let k=0,h=0;if(e!=f){let m=e-f;h=.5<g?m/(2-e-f):m/(e+f);a==e?k=(b-c)/m+(b<c?6:0):b==e?k=(c-a)/m+2:c==e&&(k=(a-b)/m+4)}return[k/6,h,g,d]}mutate(a=.05,b=0){return new Color(this.r+rand(a,-a),this.g+rand(a,-a),this.b+rand(a,-a),this.a+rand(b,-b)).clamp()}toString(a=!0){const b=c=>(16>(c=255*clamp(c)|0)?"0":"")+c.toString(16);return"#"+b(this.r)+b(this.g)+b(this.b)+(a?b(this.a):"")}setHex(a){ASSERT("string"==typeof a&&"#"==a[0]);ASSERT([4,5,7,9].includes(a.length),"Invalid hex");6>a.length?(this.r=clamp(parseInt(a[1],16)/15),this.g=clamp(parseInt(a[2],16)/15),this.b=clamp(parseInt(a[3],16)/15),this.a=5==a.length?clamp(parseInt(a[4],16)/15):1):(this.r=clamp(parseInt(a.slice(1,3),16)/255),this.g=clamp(parseInt(a.slice(3,5),16)/255),this.b=clamp(parseInt(a.slice(5,7),16)/255),this.a=9==a.length?clamp(parseInt(a.slice(7,9),16)/255):1);ASSERT(this.isValid());return this}rgbaInt(){const a=255*clamp(this.r)|0,b=255*clamp(this.g)<<8,c=255*clamp(this.b)<<16,d=255*clamp(this.a)<<24;return a+b+c+d}isValid(){return"number"==typeof this.r&&!isNaN(this.r)&&"number"==typeof this.g&&!isNaN(this.g)&&"number"==typeof this.b&&!isNaN(this.b)&&"number"==typeof this.a&&!isNaN(this.a)}}const WHITE=rgb(),BLACK=rgb(0,0,0),GRAY=rgb(.5,.5,.5),RED=rgb(1,0,0),ORANGE=rgb(1,.5,0),YELLOW=rgb(1,1,0),GREEN=rgb(0,1,0),CYAN=rgb(0,1,1),BLUE=rgb(0,0,1),PURPLE=rgb(.5,0,1),MAGENTA=rgb(1,0,1);class Timer{constructor(a){this.time=void 0==a?void 0:time+a;this.setTime=a}set(a=0){this.time=time+a;this.setTime=a}unset(){this.time=void 0}isSet(){return void 0!=this.time}active(){return time<this.time}elapsed(){return time>=this.time}get(){return this.isSet()?time-this.time:0}getPercent(){return this.isSet()?percent(this.time-time,this.setTime,0):0}toString(){if(debug)return this.isSet()?Math.abs(this.get())+" seconds "+(0>this.get()?"before":"after"):"unset"}valueOf(){return this.get()}}let cameraPos=vec2(),cameraScale=32,canvasMaxSize=vec2(1920,1080),canvasFixedSize=vec2(),canvasPixelated=!0,tilesPixelated=!0,fontDefault="arial",showSplashScreen=!1,headlessMode=!1,glEnable=!0,glOverlay=!0,tileSizeDefault=vec2(16),tileFixBleedScale=0,enablePhysicsSolver=!0,objectDefaultMass=1,objectDefaultDamping=1,objectDefaultAngleDamping=1,objectDefaultElasticity=0,objectDefaultFriction=.8,objectMaxSpeed=1,gravity=0,particleEmitRateScale=1,gamepadsEnable=!0,gamepadDirectionEmulateStick=!0,inputWASDEmulateDirection=!0,touchInputEnable=!0,touchGamepadEnable=!1,touchGamepadAnalog=!0,touchGamepadSize=99,touchGamepadAlpha=.3,vibrateEnable=!0,soundEnable=!0,soundVolume=.3,soundDefaultRange=40,soundDefaultTaper=.7,medalDisplayTime=5,medalDisplaySlideTime=.5,medalDisplaySize=vec2(640,80),medalDisplayIconSize=50,medalsPreventUnlock=!1;function setCameraPos(a){cameraPos=a}function setCameraScale(a){cameraScale=a}function setCanvasMaxSize(a){canvasMaxSize=a}function setCanvasFixedSize(a){canvasFixedSize=a}function setCanvasPixelated(a){canvasPixelated=a}function setTilesPixelated(a){tilesPixelated=a}function setFontDefault(a){fontDefault=a}function setShowSplashScreen(a){showSplashScreen=a}function setHeadlessMode(a){headlessMode=a}function setGlEnable(a){glEnable=a}function setGlOverlay(a){glOverlay=a}function setTileSizeDefault(a){tileSizeDefault=a}function setTileFixBleedScale(a){tileFixBleedScale=a}function setEnablePhysicsSolver(a){enablePhysicsSolver=a}function setObjectDefaultMass(a){objectDefaultMass=a}function setObjectDefaultDamping(a){objectDefaultDamping=a}function setObjectDefaultAngleDamping(a){objectDefaultAngleDamping=a}function setObjectDefaultElasticity(a){objectDefaultElasticity=a}function setObjectDefaultFriction(a){objectDefaultFriction=a}function setObjectMaxSpeed(a){objectMaxSpeed=a}function setGravity(a){gravity=a}function setParticleEmitRateScale(a){particleEmitRateScale=a}function setGamepadsEnable(a){gamepadsEnable=a}function setGamepadDirectionEmulateStick(a){gamepadDirectionEmulateStick=a}function setInputWASDEmulateDirection(a){inputWASDEmulateDirection=a}function setTouchInputEnable(a){touchInputEnable=a}function setTouchGamepadEnable(a){touchGamepadEnable=a}function setTouchGamepadAnalog(a){touchGamepadAnalog=a}function setTouchGamepadSize(a){touchGamepadSize=a}function setTouchGamepadAlpha(a){touchGamepadAlpha=a}function setVibrateEnable(a){vibrateEnable=a}function setSoundEnable(a){soundEnable=a}function setSoundVolume(a){soundVolume=a;soundEnable&&!headlessMode&&audioGainNode&&(audioGainNode.gain.value=a)}function setSoundDefaultRange(a){soundDefaultRange=a}function setSoundDefaultTaper(a){soundDefaultTaper=a}function setMedalDisplayTime(a){medalDisplayTime=a}function setMedalDisplaySlideTime(a){medalDisplaySlideTime=a}function setMedalDisplaySize(a){medalDisplaySize=a}function setMedalDisplayIconSize(a){medalDisplayIconSize=a}function setMedalsPreventUnlock(a){medalsPreventUnlock=a}function setShowWatermark(a){showWatermark=a}function setDebugKey(a){debugKey=a}class EngineObject{constructor(a=vec2(),b=vec2(1),c,d=0,e=new Color,f=0){ASSERT(isVector2(a)&&isVector2(b),"ensure pos and size are vec2s");ASSERT("number"!==typeof c||!c,"old style tile setup");this.pos=a.copy();this.size=b;this.drawSize=void 0;this.tileInfo=c;this.angle=d;this.color=e;this.additiveColor=void 0;this.mirror=!1;this.mass=objectDefaultMass;this.damping=objectDefaultDamping;this.angleDamping=objectDefaultAngleDamping;this.elasticity=objectDefaultElasticity;this.friction=objectDefaultFriction;this.gravityScale=1;this.renderOrder=f;this.velocity=vec2();this.angleVelocity=0;this.spawnTime=time;this.children=[];this.clampSpeedLinear=!0;this.parent=void 0;this.localPos=vec2();this.localAngle=0;this.collideRaycast=this.isSolid=this.collideSolidObjects=this.collideTiles=!1;engineObjects.push(this)}updateTransforms(){const a=this.parent;if(a){const b=a.getMirrorSign();this.pos=this.localPos.multiply(vec2(b,1)).rotate(-a.angle).add(a.pos);this.angle=b*this.localAngle+a.angle}for(const b of this.children)b.updateTransforms()}update(){if(!this.parent){if(this.clampSpeedLinear)this.velocity.x=clamp(this.velocity.x,-objectMaxSpeed,objectMaxSpeed),this.velocity.y=clamp(this.velocity.y,-objectMaxSpeed,objectMaxSpeed);else{var a=this.velocity.lengthSquared();a>objectMaxSpeed*objectMaxSpeed&&(a=objectMaxSpeed/a**.5,this.velocity.x*=a,this.velocity.y*=a)}a=this.pos.copy();this.velocity.x*=this.damping;this.velocity.y*=this.damping;this.mass&&(this.velocity.y+=gravity*this.gravityScale);this.pos.x+=this.velocity.x;this.pos.y+=this.velocity.y;this.angle+=this.angleVelocity*=this.angleDamping;ASSERT(0<=this.angleDamping&&1>=this.angleDamping);ASSERT(0<=this.damping&&1>=this.damping);if(enablePhysicsSolver&&this.mass){var b=0>this.velocity.y;if(this.groundObject){var c=this.groundObject.velocity?this.groundObject.velocity.x:0;this.velocity.x=c+(this.velocity.x-c)*this.friction;this.groundObject=0}if(this.collideSolidObjects)for(var d of engineObjectsCollide){if(!this.isSolid&&!d.isSolid||d.destroyed||d.parent||d==this)continue;if(!isOverlapping(this.pos,this.size,d.pos,d.size))continue;c=this.collideWithObject(d);var e=d.collideWithObject(this);if(!c||!e)continue;if(isOverlapping(a,this.size,d.pos,d.size)){c=a.subtract(d.pos);e=c.length();c=.01>e?randVector(.001):c.scale(.001/e);this.velocity=this.velocity.add(c);d.mass&&(d.velocity=d.velocity.subtract(c));debugOverlay&&debugPhysics&&debugOverlap(this.pos,this.size,d.pos,d.size,"#f00");continue}e=this.size.add(d.size);var f=2*(a.y-d.pos.y)>e.y+gravity;const k=2*abs(a.y-d.pos.y)<e.y;var g=2*abs(a.x-d.pos.x)<e.x;c=max(this.elasticity,d.elasticity);if(f||g||!k)if(this.pos.y=d.pos.y+(e.y/2+.001)*sign(a.y-d.pos.y),d.groundObject&&b||!d.mass)b&&(this.groundObject=d),this.velocity.y*=-c;else if(d.mass){g=(this.mass*this.velocity.y+d.mass*d.velocity.y)/(this.mass+d.mass);const h=d.velocity.y*(d.mass-this.mass)/(this.mass+d.mass)+2*this.velocity.y*this.mass/(this.mass+d.mass);this.velocity.y=lerp(c,g,this.velocity.y*(this.mass-d.mass)/(this.mass+d.mass)+2*d.velocity.y*d.mass/(this.mass+d.mass));d.velocity.y=lerp(c,g,h)}!f&&k&&(this.pos.x=d.pos.x+(e.x/2+.001)*sign(a.x-d.pos.x),d.mass?(e=(this.mass*this.velocity.x+d.mass*d.velocity.x)/(this.mass+d.mass),f=d.velocity.x*(d.mass-this.mass)/(this.mass+d.mass)+2*this.velocity.x*this.mass/(this.mass+d.mass),this.velocity.x=lerp(c,e,this.velocity.x*(this.mass-d.mass)/(this.mass+d.mass)+2*d.velocity.x*d.mass/(this.mass+d.mass)),d.velocity.x=lerp(c,e,f)):this.velocity.x*=-c);debugOverlay&&debugPhysics&&debugOverlap(this.pos,this.size,d.pos,d.size,"#f0f")}if(this.collideTiles&&tileCollisionTest(this.pos,this.size,this)&&!tileCollisionTest(a,this.size,this)){d=tileCollisionTest(vec2(a.x,this.pos.y),this.size,this);c=tileCollisionTest(vec2(this.pos.x,a.y),this.size,this);if(d||!c)this.velocity.y*=-this.elasticity,(this.groundObject=b)?this.pos.y=(a.y-this.size.y/2|0)+this.size.y/2+1e-4:this.pos.y=a.y;c&&(this.pos.x=a.x,this.velocity.x*=-this.elasticity);debugOverlay&&debugPhysics&&debugRect(this.pos,this.size,"#f00")}}}}render(){drawTile(this.pos,this.drawSize||this.size,this.tileInfo,this.color,this.angle,this.mirror,this.additiveColor)}destroy(){if(!this.destroyed){this.destroyed=1;this.parent&&this.parent.removeChild(this);for(const a of this.children)a.destroy(a.parent=0)}}localToWorld(a){return this.pos.add(a.rotate(-this.angle))}worldToLocal(a){return a.subtract(this.pos).rotate(this.angle)}localToWorldVector(a){return a.rotate(this.angle)}worldToLocalVector(a){return a.rotate(-this.angle)}collideWithTile(a,b){return 0<a}collideWithObject(a){return!0}getAliveTime(){return time-this.spawnTime}applyAcceleration(a){this.mass&&(this.velocity=this.velocity.add(a))}applyForce(a){this.applyAcceleration(a.scale(1/this.mass))}getMirrorSign(){return this.mirror?-1:1}addChild(a,b=vec2(),c=0){ASSERT(!a.parent&&!this.children.includes(a));this.children.push(a);a.parent=this;a.localPos=b.copy();a.localAngle=c}removeChild(a){ASSERT(a.parent==this&&this.children.includes(a));this.children.splice(this.children.indexOf(a),1);a.parent=0}setCollision(a=!0,b=!0,c=!0,d=!0){ASSERT(a||!b,"solid objects must be set to collide");this.collideSolidObjects=a;this.isSolid=b;this.collideTiles=c;this.collideRaycast=d}toString(){if(debug){let a="type = "+this.constructor.name;if(this.pos.x||this.pos.y)a+="\npos = "+this.pos;if(this.velocity.x||this.velocity.y)a+="\nvelocity = "+this.velocity;if(this.size.x||this.size.y)a+="\nsize = "+this.size;this.angle&&(a+="\nangle = "+this.angle.toFixed(3));this.color&&(a+="\ncolor = "+this.color);return a}}renderDebugInfo(){if(debug){const a=vec2(max(this.size.x,.2),max(this.size.y,.2)),b=rgb(this.collideTiles?1:0,this.collideSolidObjects?1:0,this.isSolid?1:0,this.parent?.2:.5),c=this.parent?rgb(1,1,1,.5):rgb(0,0,0,.8);drawRect(this.pos,a,b,this.angle,!1);drawRect(this.pos,a.scale(.8),c,this.angle,!1);this.parent&&drawLine(this.pos,this.parent.pos,.1,rgb(0,0,1,.5),!1)}}}let mainCanvas,mainContext,overlayCanvas,overlayContext,mainCanvasSize=vec2(),textureInfos=[],drawCount;function tile(a=vec2(),b=tileSizeDefault,c=0,d=0){if(headlessMode)return new TileInfo;"number"===typeof b&&(ASSERT(0<b),b=vec2(b));var e=textureInfos[c];ASSERT(!!e,"Texture not loaded");const f=b.add(vec2(2*d));"number"===typeof a&&(e=e.size.x/f.x|0,a=0<e?vec2(a%e,a/e|0):vec2());a=vec2(a.x*f.x+d,a.y*f.y+d);return new TileInfo(a,b,c,d)}class TileInfo{constructor(a=vec2(),b=tileSizeDefault,c=0,d=0){this.pos=a.copy();this.size=b.copy();this.textureIndex=c;this.padding=d}offset(a){return new TileInfo(this.pos.add(a),this.size,this.textureIndex)}frame(a){ASSERT("number"==typeof a);return this.offset(vec2(a*(this.size.x+2*this.padding),0))}getTextureInfo(){return textureInfos[this.textureIndex]}}class TextureInfo{constructor(a){this.image=a;this.size=vec2(a.width,a.height);this.glTexture=glEnable&&glCreateTexture(a)}}function screenToWorld(a){return new Vector2((a.x-mainCanvasSize.x/2+.5)/cameraScale+cameraPos.x,(a.y-mainCanvasSize.y/2+.5)/-cameraScale+cameraPos.y)}function worldToScreen(a){return new Vector2((a.x-cameraPos.x)*cameraScale+mainCanvasSize.x/2-.5,(a.y-cameraPos.y)*-cameraScale+mainCanvasSize.y/2-.5)}function getCameraSize(){return mainCanvasSize.scale(1/cameraScale)}function drawTile(a,b=vec2(1),c,d=new Color,e=0,f,g=new Color(0,0,0,0),k=glEnable,h,m){ASSERT(!m||!k,"context only supported in canvas 2D mode");ASSERT("number"!==typeof c||!c,"this is an old style calls, to fix replace it with tile(tileIndex, tileSize)");const n=c&&c.getTextureInfo();if(k)if(h&&(a=screenToWorld(a),b=b.scale(1/cameraScale)),n){var l=vec2(1).divide(n.size);k=c.pos.x*l.x;h=c.pos.y*l.y;m=c.size.x*l.x;const p=c.size.y*l.y;l=l.scale(tileFixBleedScale);glSetTexture(n.glTexture);glDraw(a.x,a.y,f?-b.x:b.x,b.y,e,k+l.x,h+l.y,k-l.x+m,h-l.y+p,d.rgbaInt(),g.rgbaInt())}else glDraw(a.x,a.y,b.x,b.y,e,0,0,0,0,0,d.rgbaInt());else showWatermark&&++drawCount,b=vec2(b.x,-b.y),drawCanvas2D(a,b,e,f,p=>{if(n){const q=c.pos.x+tileFixBleedScale,r=c.pos.y+tileFixBleedScale,x=c.size.x-2*tileFixBleedScale,v=c.size.y-2*tileFixBleedScale;p.globalAlpha=d.a;p.drawImage(n.image,q,r,x,v,-.5,-.5,1,1);p.globalAlpha=1}else p.fillStyle=d,p.fillRect(-.5,-.5,1,1)},h,m)}function drawRect(a,b,c,d,e,f,g){drawTile(a,b,void 0,c,d,!1,void 0,e,f,g)}function drawLine(a,b,c=.1,d,e,f,g){b=vec2((b.x-a.x)/2,(b.y-a.y)/2);c=vec2(c,2*b.length());drawRect(a.add(b),c,d,b.angle(),e,f,g)}function drawPoly(a,b=new Color,c=0,d=new Color(0,0,0),e,f=mainContext){f.fillStyle=b.toString();f.beginPath();for(const g of e?a:a.map(worldToScreen))f.lineTo(g.x,g.y);f.closePath();f.fill();c&&(f.strokeStyle=d.toString(),f.lineWidth=e?c:c*cameraScale,f.stroke())}function drawEllipse(a,b=1,c=1,d=0,e=new Color,f=0,g=new Color(0,0,0),k,h=mainContext){k||(a=worldToScreen(a),b*=cameraScale,c*=cameraScale,f*=cameraScale);h.fillStyle=e.toString();h.beginPath();h.ellipse(a.x,a.y,b,c,d,0,9);h.fill();f&&(h.strokeStyle=g.toString(),h.lineWidth=f,h.stroke())}function drawCircle(a,b=1,c=new Color,d=0,e=new Color(0,0,0),f,g=mainContext){drawEllipse(a,b,b,0,c,d,e,f,g)}function drawCanvas2D(a,b,c,d,e,f,g=mainContext){f||(a=worldToScreen(a),b=b.scale(cameraScale));g.save();g.translate(a.x+.5,a.y+.5);g.rotate(c);g.scale(d?-b.x:b.x,-b.y);e(g);g.restore()}function drawText(a,b,c=1,d,e=0,f,g,k,h,m=mainContext){drawTextScreen(a,worldToScreen(b),c*cameraScale,d,e*cameraScale,f,g,k,h,m)}function drawTextOverlay(a,b,c=1,d,e=0,f,g,k,h){drawText(a,b,c,d,e,f,g,k,h,overlayContext)}function drawTextScreen(a,b,c=1,d=new Color,e=0,f=new Color(0,0,0),g="center",k=fontDefault,h,m=overlayContext){m.fillStyle=d.toString();m.lineWidth=e;m.strokeStyle=f.toString();m.textAlign=g;m.font=c+"px "+k;m.textBaseline="middle";m.lineJoin="round";b=b.copy();(a+"").split("\n").forEach(n=>{e&&m.strokeText(n,b.x,b.y,h);m.fillText(n,b.x,b.y,h);b.y+=c})}function setBlendMode(a,b=glEnable,c){ASSERT(!c||!b,"context only supported in canvas 2D mode");b?glAdditive=a:(c||=mainContext,c.globalCompositeOperation=a?"lighter":"source-over")}function combineCanvases(){glCopyToContext(mainContext,!0);mainContext.drawImage(overlayCanvas,0,0);glClearCanvas();overlayCanvas.width|=0}let engineFontImage;class FontImage{constructor(a,b=vec2(8),c=vec2(0,1),d=overlayContext){engineFontImage||((engineFontImage=new Image).src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQAAAAAYAQAAAAA9+x6JAAAAAnRSTlMAAHaTzTgAAAGiSURBVHjaZZABhxxBEIUf6ECLBdFY+Q0PMNgf0yCgsSAGZcT9sgIPtBWwIA5wgAPEoHUyJeeSlW+gjK+fegWwtROWpVQEyWh2npdpBmTUFVhb29RINgLIukoXr5LIAvYQ5ve+1FqWEMqNKTX3FAJHyQDRZvmKWubAACcv5z5Gtg2oyCWE+Yk/8JZQX1jTTCpKAFGIgza+dJCNBF2UskRlsgwitHbSV0QLgt9sTPtsRlvJjEr8C/FARWA2bJ/TtJ7lko34dNDn6usJUMzuErP89UUBJbWeozrwLLncXczd508deAjLWipLO4Q5XGPcJvPu92cNDaN0P5G1FL0nSOzddZOrJ6rNhbXGmeDvO3TF7DeJWl4bvaYQTNHCTeuqKZmbjHaSOFes+IX/+IhHrnAkXOAsfn24EM68XieIECoccD4KZLk/odiwzeo2rovYdhvb2HYFgyznJyDpYJdYOmfXgVdJTaUi4xA2uWYNYec9BLeqdl9EsoTw582mSFDX2DxVLbNt9U3YYoeatBad1c2Tj8t2akrjaIGJNywKB/7h75/gN3vCMSaadIUTAAAAAElFTkSuQmCC");this.image=a||engineFontImage;this.tileSize=b;this.paddingSize=c;this.context=d}drawText(a,b,c=1,d){this.drawTextScreen(a,worldToScreen(b).floor(),c*cameraScale|0,d)}drawTextScreen(a,b,c=4,d){const e=this.context;e.save();const f=this.tileSize,g=f.add(this.paddingSize).scale(c),k=this.image.width/this.tileSize.x|0;(a+"").split("\n").forEach((h,m)=>{const n=d?h.length*f.x*c/2|0:0;for(let q=h.length;q--;){var l=h[q].charCodeAt(0);if(32>l||127<l)l=127;var p=l-32;l=p%k;p=p/k|0;const r=b.add(vec2(q,m).multiply(g));e.drawImage(this.image,l*f.x,p*f.y,f.x,f.y,r.x-n,r.y,f.x*c,f.y*c)}});e.restore()}}function isFullscreen(){return!!document.fullscreenElement}function toggleFullscreen(){const a=mainCanvas.parentElement;isFullscreen()?document.exitFullscreen&&document.exitFullscreen():a.requestFullscreen&&a.requestFullscreen()}function setCursor(a="auto"){mainCanvas.parentElement.style.cursor=a}function keyIsDown(a,b=0){ASSERT(0<b||"number"!==typeof a||3>a,"use code string for keyboard");return inputData[b]&&!!(inputData[b][a]&1)}function keyWasPressed(a,b=0){ASSERT(0<b||"number"!==typeof a||3>a,"use code string for keyboard");return inputData[b]&&!!(inputData[b][a]&2)}function keyWasReleased(a,b=0){ASSERT(0<b||"number"!==typeof a||3>a,"use code string for keyboard");return inputData[b]&&!!(inputData[b][a]&4)}function clearInput(){inputData=[[]];touchGamepadButtons=[]}const mouseIsDown=keyIsDown,mouseWasPressed=keyWasPressed,mouseWasReleased=keyWasReleased;let mousePos=vec2(),mousePosScreen=vec2(),mouseWheel=0,isUsingGamepad=!1,preventDefaultInput=!1;function gamepadIsDown(a,b=0){return keyIsDown(a,b+1)}function gamepadWasPressed(a,b=0){return keyWasPressed(a,b+1)}function gamepadWasReleased(a,b=0){return keyWasReleased(a,b+1)}function gamepadStick(a,b=0){return gamepadStickData[b]?gamepadStickData[b][a]||vec2():vec2()}let inputData=[[]];function inputUpdate(){headlessMode||(touchInputEnable&&isTouchDevice||document.hasFocus()||clearInput(),mousePos=screenToWorld(mousePosScreen),gamepadsUpdate())}function inputUpdatePost(){if(!headlessMode){for(const a of inputData)for(const b in a)a[b]&=1;mouseWheel=0}}function inputInit(){function a(b){return inputWASDEmulateDirection?"KeyW"==b?"ArrowUp":"KeyS"==b?"ArrowDown":"KeyA"==b?"ArrowLeft":"KeyD"==b?"ArrowRight":b:b}headlessMode||(onkeydown=b=>{b.repeat||(isUsingGamepad=!1,inputData[0][b.code]=3,inputWASDEmulateDirection&&(inputData[0][a(b.code)]=3));preventDefaultInput&&b.preventDefault()},onkeyup=b=>{inputData[0][b.code]=4;inputWASDEmulateDirection&&(inputData[0][a(b.code)]=4)},onmousedown=b=>{soundEnable&&!headlessMode&&audioContext&&"running"!=audioContext.state&&audioContext.resume();isUsingGamepad=!1;inputData[0][b.button]=3;mousePosScreen=mouseToScreen(b);b.button&&b.preventDefault()},onmouseup=b=>inputData[0][b.button]=inputData[0][b.button]&2|4,onmousemove=b=>mousePosScreen=mouseToScreen(b),onwheel=b=>mouseWheel=b.ctrlKey?0:sign(b.deltaY),oncontextmenu=b=>!1,onblur=b=>clearInput(),isTouchDevice&&touchInputEnable&&touchInputInit())}function mouseToScreen(a){if(!mainCanvas||headlessMode)return vec2();const b=mainCanvas.getBoundingClientRect();return vec2(mainCanvas.width,mainCanvas.height).multiply(vec2(percent(a.x,b.left,b.right),percent(a.y,b.top,b.bottom)))}const gamepadStickData=[];function gamepadsUpdate(){const a=g=>{const k=h=>.3<h?percent(h,.3,.8):-.3>h?-percent(-h,.3,.8):0;return vec2(k(g.x),k(-g.y)).clampLength()};if(touchGamepadEnable&&isTouchDevice&&(ASSERT(touchGamepadButtons,"set touchGamepadEnable before calling init!"),touchGamepadTimer.isSet())){var b=gamepadStickData[0]||(gamepadStickData[0]=[]);b[0]=vec2();touchGamepadAnalog?b[0]=a(touchGamepadStick):.3<touchGamepadStick.lengthSquared()&&(b[0].x=Math.round(touchGamepadStick.x),b[0].y=-Math.round(touchGamepadStick.y),b[0]=b[0].clampLength());b=inputData[1]||(inputData[1]=[]);for(var c=10;c--;){var d=3==c?2:2==c?3:c,e=gamepadIsDown(d,0);b[d]=touchGamepadButtons[c]?e?1:3:e?4:0}}if(gamepadsEnable&&navigator&&navigator.getGamepads&&(debug||document.hasFocus()))for(b=navigator.getGamepads(),c=b.length;c--;){e=b[c];const g=inputData[c+1]||(inputData[c+1]=[]);d=gamepadStickData[c]||(gamepadStickData[c]=[]);if(e){for(var f=0;f<e.axes.length-1;f+=2)d[f>>1]=a(vec2(e.axes[f],e.axes[f+1]));for(f=e.buttons.length;f--;){const k=e.buttons[f],h=gamepadIsDown(f,c);g[f]=k.pressed?h?1:3:h?4:0;isUsingGamepad||=!c&&k.pressed}gamepadDirectionEmulateStick&&(e=vec2((gamepadIsDown(15,c)&&1)-(gamepadIsDown(14,c)&&1),(gamepadIsDown(12,c)&&1)-(gamepadIsDown(13,c)&&1)),e.lengthSquared()&&(d[0]=e.clampLength()));touchGamepadEnable&&isUsingGamepad&&touchGamepadTimer.unset()}}}function vibrate(a=100){vibrateEnable&&!headlessMode&&navigator&&navigator.vibrate&&navigator.vibrate(a)}function vibrateStop(){vibrate(0)}const isTouchDevice=!headlessMode&&void 0!==window.ontouchstart;let touchGamepadTimer=new Timer,touchGamepadButtons,touchGamepadStick;function touchInputInit(){function a(e){soundEnable&&!headlessMode&&audioContext&&"running"!=audioContext.state&&audioContext.resume();const f=e.touches.length;if(f){const g=vec2(e.touches[0].clientX,e.touches[0].clientY);mousePosScreen=mouseToScreen(g);d?isUsingGamepad=touchGamepadEnable:inputData[0][0]=3}else d&&(inputData[0][0]=inputData[0][0]&2|4);d=f;document.hasFocus()&&e.preventDefault();return!0}function b(e){touchGamepadStick=vec2();touchGamepadButtons=[];isUsingGamepad=!0;if(e.touches.length&&(touchGamepadTimer.set(),paused&&!d)){touchGamepadButtons[9]=1;a(e);return}const f=vec2(touchGamepadSize,mainCanvasSize.y-touchGamepadSize),g=mainCanvasSize.subtract(vec2(touchGamepadSize,touchGamepadSize)),k=mainCanvasSize.scale(.5);for(const m of e.touches){var h=mouseToScreen(vec2(m.clientX,m.clientY));h.distance(f)<touchGamepadSize?touchGamepadStick=h.subtract(f).scale(2/touchGamepadSize).clampLength():h.distance(g)<touchGamepadSize?(h=h.subtract(g).direction(),touchGamepadButtons[h]=1):h.distance(k)<touchGamepadSize&&!d&&(touchGamepadButtons[9]=1)}a(e);return!0}let c=a;touchGamepadEnable&&(c=b,touchGamepadButtons=[],touchGamepadStick=vec2());document.addEventListener("touchstart",e=>c(e),{passive:!1});document.addEventListener("touchmove",e=>c(e),{passive:!1});document.addEventListener("touchend",e=>c(e),{passive:!1});onmousedown=onmouseup=()=>0;let d}function touchGamepadRender(){if(touchInputEnable&&isTouchDevice&&!headlessMode&&touchGamepadEnable&&touchGamepadTimer.isSet()){var a=percent(touchGamepadTimer.get(),4,3);if(a&&!paused){var b=overlayContext;b.save();b.globalAlpha=a*touchGamepadAlpha;b.strokeStyle="#fff";b.lineWidth=3;b.fillStyle=0<touchGamepadStick.lengthSquared()?"#fff":"#000";b.beginPath();a=vec2(touchGamepadSize,mainCanvasSize.y-touchGamepadSize);if(touchGamepadAnalog)b.arc(a.x,a.y,touchGamepadSize/2,0,9),b.fill();else for(var c=10;c--;){var d=c*PI/4;b.arc(a.x,a.y,.6*touchGamepadSize,d+PI/8,d+PI/8);c%2&&b.arc(a.x,a.y,.33*touchGamepadSize,d,d);1==c&&b.fill()}b.stroke();a=vec2(mainCanvasSize.x-touchGamepadSize,mainCanvasSize.y-touchGamepadSize);for(c=4;c--;)d=a.add(vec2().setDirection(c,touchGamepadSize/2)),b.fillStyle=touchGamepadButtons[c]?"#fff":"#000",b.beginPath(),b.arc(d.x,d.y,touchGamepadSize/4,0,9),b.fill(),b.stroke();b.restore()}}}let audioContext=new AudioContext,audioGainNode;function audioInit(){soundEnable&&!headlessMode&&(audioGainNode=audioContext.createGain(),audioGainNode.connect(audioContext.destination),audioGainNode.gain.value=soundVolume)}class Sound{constructor(a,b=soundDefaultRange,c=soundDefaultTaper){soundEnable&&!headlessMode&&(this.range=b,this.taper=c,this.randomness=0,a&&(this.randomness=void 0!=a[1]?a[1]:.05,a[1]=0,this.sampleChannels=[zzfxG(...a)],this.sampleRate=zzfxR))}play(a,b=1,c=1,d=1,e=!1){if(soundEnable&&!headlessMode&&this.sampleChannels){var f;if(a){if(f=this.range){const g=cameraPos.distanceSquared(a);if(g>f*f)return;b*=percent(g**.5,f,f*this.taper)}f=2*worldToScreen(a).x/mainCanvas.width-1}a=c+c*this.randomness*d*rand(-1,1);this.gainNode=audioContext.createGain();return this.source=playSamples(this.sampleChannels,b,a,f,e,this.sampleRate,this.gainNode)}}setVolume(a=1){this.gainNode&&(this.gainNode.gain.value=a)}stop(){this.source&&this.source.stop();this.source=void 0}getSource(){return this.source}playNote(a,b,c){return this.play(b,c,2**(a/12),0)}getDuration(){return this.sampleChannels&&this.sampleChannels[0].length/this.sampleRate}isLoading(){return!this.sampleChannels}}class SoundWave extends Sound{constructor(a,b=0,c,d,e){super(void 0,c,d);soundEnable&&!headlessMode&&(this.randomness=b,fetch(a).then(f=>f.arrayBuffer()).then(f=>audioContext.decodeAudioData(f)).then(f=>{this.sampleChannels=[];for(let g=f.numberOfChannels;g--;)this.sampleChannels[g]=Array.from(f.getChannelData(g));this.sampleRate=f.sampleRate}).then(()=>e&&e(this)))}}function playAudioFile(a,b=1,c=!1){if(soundEnable&&!headlessMode)return new SoundWave(a,0,0,0,d=>d.play(void 0,b,1,1,c))}class Music extends Sound{constructor(a){super(void 0);soundEnable&&!headlessMode&&(this.randomness=0,this.sampleChannels=zzfxM(...a),this.sampleRate=zzfxR)}playMusic(a,b=!1){return super.play(void 0,a,1,1,b)}}function speak(a,b="",c=1,d=1,e=1){if(soundEnable&&!headlessMode&&speechSynthesis)return a=new SpeechSynthesisUtterance(a),a.lang=b,a.volume=2*c*soundVolume,a.rate=d,a.pitch=e,speechSynthesis.speak(a),a}function speakStop(){speechSynthesis&&speechSynthesis.cancel()}function getNoteFrequency(a,b=220){return b*2**(a/12)}function playSamples(a,b=1,c=1,d=0,e=!1,f=zzfxR,g){if(soundEnable&&!headlessMode){var k=audioContext.createBuffer(a.length,a[0].length,f),h=audioContext.createBufferSource();a.forEach((m,n)=>k.getChannelData(n).set(m));h.buffer=k;h.playbackRate.value=c;h.loop=e;g=g||audioContext.createGain();g.gain.value=b;g.connect(audioGainNode);a=new StereoPannerNode(audioContext,{pan:clamp(d,-1,1)});h.connect(a).connect(g);"running"!=audioContext.state?audioContext.resume().then(()=>h.start()):h.start();return h}}function zzfx(...a){return playSamples([zzfxG(...a)])}const zzfxR=44100;function zzfxG(a=1,b=.05,c=220,d=0,e=0,f=.1,g=0,k=1,h=0,m=0,n=0,l=0,p=0,q=0,r=0,x=0,v=0,D=1,z=0,E=0,A=0){let w=2*PI;var t=zzfxR;let F=h*=500*w/t/t;b=c*=rand(1+b,1-b)*w/t;let C=[],y=0,G=0,u=0,H=1,R=0,S=0,B=0,J;var L=w*abs(A)*2/t,K=Math.cos(L),M=Math.sin(L)/2/2,I=1+M;L=-2*K/I;M=(1-M)/I;let N=(1+sign(A)*K)/2/I;K=-(sign(A)+K)/I;let O=I=0,P=0,Q=0;d=d*t+9;z*=t;e*=t;f*=t;v*=t;m*=500*w/t**3;r*=w/t;n*=w/t;l*=t;p=p*t|0;for(J=d+z+e+f+v|0;u<J;C[u++]=B*a)++S%(100*x|0)||(B=g?1<g?2<g?3<g?Math.sin(y**3):clamp(Math.tan(y),1,-1):1-(2*y/w%2+2)%2:1-4*abs(Math.round(y/w)-y/w):Math.sin(y),B=(p?1-E+E*Math.sin(w*u/p):1)*sign(B)*abs(B)**k*(u<d?u/d:u<d+z?1-(u-d)/z*(1-D):u<d+z+e?D:u<J-v?(J-u-v)/f*D:0),B=v?B/2+(v>u?0:(u<J-v?1:(J-u)/v)*C[u-v|0]/2/a):B,A&&(B=Q=N*I+K*(I=O)+N*(O=B)-M*P-L*(P=Q))),t=(c+=h+=m)*Math.cos(r*G++),y+=t+t*q*Math.sin(u**5),H&&++H>l&&(c+=n,b+=n,H=0),!p||++R%p||(c=b,h=F,H=H||1);return C}function zzfxM(a,b,c,d=125){let e,f,g,k,h,m,n,l,p,q,r,x,v,D=0,z,E=[],A=[],w=[],t=0,F=0,C=1,y={},G=zzfxR/d*60>>2;for(;C;t++)E=[C=l=x=0],c.forEach((u,H)=>{n=b[u][t]||[0,0,0];C|=b[u][t]&&1;z=x+(b[u][0].length-2-(l?0:1))*G;v=H==c.length-1;e=2;for(g=x;e<n.length+v;l=++e){h=n[e];p=e==n.length+v-1&&v||q!=(n[0]||0)||h|0;for(f=0;f<G&&l;f++>G-99&&p&&1>r?r+=1/99:0)m=(1-r)*E[D++]/2||0,A[g]=(A[g]||0)-m*F+m,w[g]=(w[g++]||0)+m*F+m;h&&(r=h%1,F=n[1]||0,h|=0)&&(E=y[[q=n[D=0]||0,h]]=y[[q,h]]||(k=[...a[q]],k[2]*=2**((h-12)/12),0<h?zzfxG(...k):[]))}x=z});return[A,w]}let tileCollision=[],tileCollisionSize=vec2();function initTileCollision(a){tileCollisionSize=a;tileCollision=[];for(a=tileCollision.length=tileCollisionSize.area();a--;)tileCollision[a]=0}function setTileCollisionData(a,b=0){a.arrayCheck(tileCollisionSize)&&(tileCollision[(a.y|0)*tileCollisionSize.x+a.x|0]=b)}function getTileCollisionData(a){return a.arrayCheck(tileCollisionSize)?tileCollision[(a.y|0)*tileCollisionSize.x+a.x|0]:0}function tileCollisionTest(a,b=vec2(),c){const d=max(a.x-b.x/2|0,0);var e=max(a.y-b.y/2|0,0);const f=min(a.x+b.x/2,tileCollisionSize.x);for(a=min(a.y+b.y/2,tileCollisionSize.y);e<a;++e)for(b=d;b<f;++b){const g=tileCollision[e*tileCollisionSize.x+b];if(g&&(!c||c.collideWithTile(g,vec2(b,e))))return!0}return!1}function tileCollisionRaycast(a,b,c){const d=b.subtract(a),e=d.length();var f=d.normalize();f=vec2(abs(1/f.x),abs(1/f.y));let g=a.floor(),k=f.x*(0>d.x?a.x-g.x:g.x-a.x+1),h=f.y*(0>d.y?a.y-g.y:g.y-a.y+1);for(;;){const m=getTileCollisionData(g);if(m&&(!c||c.collideWithTile(m,g)))return debugRaycast&&debugLine(a,b,"#f00",.02),debugRaycast&&debugPoint(g.add(vec2(.5)),"#ff0"),g.add(vec2(.5));if(k>e&&h>e)break;k>h?(g.y+=sign(d.y),h+=f.y):(g.x+=sign(d.x),k+=f.x)}debugRaycast&&debugLine(a,b,"#00f",.02)}class TileLayerData{constructor(a,b=0,c=!1,d=new Color){this.tile=a;this.direction=b;this.mirror=c;this.color=d}clear(){this.tile=this.direction=0;this.mirror=!1;this.color=new Color}}class TileLayer extends EngineObject{constructor(a,b=tileCollisionSize,c=tile(),d=vec2(1),e=0){super(a,b,c,0,void 0,e);this.canvas=document.createElement("canvas");this.context=this.canvas.getContext("2d");this.scale=d;this.isOverlay=!1;this.data=[];for(a=this.size.area();a--;)this.data.push(new TileLayerData);headlessMode&&(this.redraw=()=>{},this.render=()=>{},this.redrawStart=()=>{},this.redrawEnd=()=>{},this.drawTileData=()=>{},this.drawCanvas2D=()=>{})}setData(a,b,c=!1){a.arrayCheck(this.size)&&(this.data[(a.y|0)*this.size.x+a.x|0]=b,c&&this.drawTileData(a))}getData(a){return a.arrayCheck(this.size)&&this.data[(a.y|0)*this.size.x+a.x|0]}update(){}render(){ASSERT(mainContext!=this.context,"must call redrawEnd() after drawing tiles");glOverlay||this.isOverlay||glCopyToContext(mainContext);const a=worldToScreen(this.pos.add(vec2(0,this.size.y*this.scale.y)));(this.isOverlay?overlayContext:mainContext).drawImage(this.canvas,a.x,a.y,cameraScale*this.size.x*this.scale.x,cameraScale*this.size.y*this.scale.y)}redraw(){this.redrawStart(!0);for(let a=this.size.x;a--;)for(let b=this.size.y;b--;)this.drawTileData(vec2(a,b),!1);this.redrawEnd()}redrawStart(a=!1){this.savedRenderSettings=[mainCanvas,mainContext,mainCanvasSize,cameraPos,cameraScale];mainCanvas=this.canvas;mainContext=this.context;mainCanvasSize=this.size.multiply(this.tileInfo.size);cameraPos=this.size.scale(.5);cameraScale=this.tileInfo.size.x;a&&(mainCanvas.width=mainCanvasSize.x,mainCanvas.height=mainCanvasSize.y);this.context.imageSmoothingEnabled=!tilesPixelated;glPreRender()}redrawEnd(){ASSERT(mainContext==this.context,"must call redrawStart() before drawing tiles");glCopyToContext(mainContext,!0);[mainCanvas,mainContext,mainCanvasSize,cameraPos,cameraScale]=this.savedRenderSettings}drawTileData(a,b=!0){var c=this.tileInfo.size;b&&(b=a.multiply(c),this.context.clearRect(b.x,this.canvas.height-b.y,c.x,-c.y));b=this.getData(a);void 0!=b.tile&&(ASSERT(mainContext==this.context,"must call redrawStart() before drawing tiles"),a=a.add(vec2(.5)),c=tile(b.tile,c,this.tileInfo.textureIndex),drawTile(a,vec2(1),c,b.color,b.direction*PI/2,b.mirror))}drawCanvas2D(a,b,c,d,e){const f=this.context;f.save();a=a.subtract(this.pos).multiply(this.tileInfo.size);b=b.multiply(this.tileInfo.size);f.translate(a.x,this.canvas.height-a.y);f.rotate(c);f.scale(d?-b.x:b.x,b.y);e(f);f.restore()}drawTile(a,b=vec2(1),c,d=new Color,e,f){this.drawCanvas2D(a,b,e,f,g=>{const k=c&&c.getTextureInfo();k?(g.globalAlpha=d.a,g.drawImage(k.image,c.pos.x,c.pos.y,c.size.x,c.size.y,-.5,-.5,1,1),g.globalAlpha=1):(g.fillStyle=d,g.fillRect(-.5,-.5,1,1))})}drawRect(a,b,c,d){this.drawTile(a,b,void 0,c,d)}}class ParticleEmitter extends EngineObject{constructor(a,b,c=0,d=0,e=100,f=PI,g,k=new Color,h=new Color,m=new Color(1,1,1,0),n=new Color(1,1,1,0),l=.5,p=.1,q=1,r=.1,x=.05,v=1,D=1,z=0,E=PI,A=.1,w=.2,t=!1,F=!1,C=!0,y=F?1e9:0,G=!1){super(a,vec2(),g,b,void 0,y);this.emitSize=c;this.emitTime=d;this.emitRate=e;this.emitConeAngle=f;this.colorStartA=k;this.colorStartB=h;this.colorEndA=m;this.colorEndB=n;this.randomColorLinear=C;this.particleTime=l;this.sizeStart=p;this.sizeEnd=q;this.speed=r;this.angleSpeed=x;this.damping=v;this.angleDamping=D;this.gravityScale=z;this.particleConeAngle=E;this.fadeRate=A;this.randomness=w;this.collideTiles=t;this.additive=F;this.localSpace=G;this.trailScale=0;this.particleCreateCallback=this.particleDestroyCallback=void 0;this.emitTimeBuffer=0}update(){this.parent&&super.update();if(!this.emitTime||this.getAliveTime()<=this.emitTime){if(this.emitRate*particleEmitRateScale){const a=1/this.emitRate/particleEmitRateScale;for(this.emitTimeBuffer+=timeDelta;0<this.emitTimeBuffer;this.emitTimeBuffer-=a)this.emitParticle()}}else this.destroy();debugParticles&&debugRect(this.pos,vec2(this.emitSize),"#0f0",0,this.angle)}emitParticle(){var a="number"===typeof this.emitSize?randInCircle(this.emitSize/2):vec2(rand(-.5,.5),rand(-.5,.5)).multiply(this.emitSize).rotate(this.angle);let b=rand(this.particleConeAngle,-this.particleConeAngle);this.localSpace||(a=this.pos.add(a),b+=this.angle);const c=this.randomness;var d=l=>l+l*rand(c,-c);const e=d(this.particleTime),f=d(this.sizeStart),g=d(this.sizeEnd),k=d(this.speed);d=d(this.angleSpeed)*randSign();var h=rand(this.emitConeAngle,-this.emitConeAngle);const m=randColor(this.colorStartA,this.colorStartB,this.randomColorLinear),n=randColor(this.colorEndA,this.colorEndB,this.randomColorLinear);h=this.localSpace?h:this.angle+h;a=new Particle(a,this.tileInfo,b,m,n,e,f,g,this.fadeRate,this.additive,this.trailScale,this.localSpace&&this,this.particleDestroyCallback);a.velocity=vec2().setAngle(h,k);a.angleVelocity=d;a.fadeRate=this.fadeRate;a.damping=this.damping;a.angleDamping=this.angleDamping;a.elasticity=this.elasticity;a.friction=this.friction;a.gravityScale=this.gravityScale;a.collideTiles=this.collideTiles;a.renderOrder=this.renderOrder;a.mirror=!!randInt(2);this.particleCreateCallback&&this.particleCreateCallback(a);return a}render(){}}class Particle extends EngineObject{constructor(a,b,c,d,e,f,g,k,h,m,n,l,p){super(a,vec2(),b,c);this.colorStart=d;this.colorEndDelta=e.subtract(d);this.lifeTime=f;this.sizeStart=g;this.sizeEndDelta=k-g;this.fadeRate=h;this.additive=m;this.trailScale=n;this.localSpaceEmitter=l;this.destroyCallback=p;this.clampSpeedLinear=!1}render(){const a=min((time-this.spawnTime)/this.lifeTime,1),b=vec2(this.sizeStart+a*this.sizeEndDelta);var c=this.fadeRate/2;c=new Color(this.colorStart.r+a*this.colorEndDelta.r,this.colorStart.g+a*this.colorEndDelta.g,this.colorStart.b+a*this.colorEndDelta.b,(this.colorStart.a+a*this.colorEndDelta.a)*(a<c?a/c:a>1-c?(1-a)/c:1));this.additive&&setBlendMode(!0);let d=this.pos,e=this.angle;this.localSpaceEmitter&&(d=this.localSpaceEmitter.pos.add(d.rotate(-this.localSpaceEmitter.angle)),e+=this.localSpaceEmitter.angle);if(this.trailScale){var f=this.velocity;this.localSpaceEmitter&&(f=f.rotate(-this.localSpaceEmitter.angle));var g=f.length();g&&(f=f.scale(1/g),g*=this.trailScale,b.y=max(b.x,g),e=f.angle(),drawTile(d.add(f.multiply(vec2(0,-g/2))),b,this.tileInfo,c,e,this.mirror))}else drawTile(d,b,this.tileInfo,c,e,this.mirror);this.additive&&setBlendMode();debugParticles&&debugRect(d,b,"#f005",0,e);1==a&&(this.color=c,this.size=b,this.destroyCallback&&this.destroyCallback(this),this.destroyed=1)}}const medals={};let medalsDisplayQueue=[],medalsSaveName,medalsDisplayTimeLast;function medalsInit(a){medalsSaveName=a;debugMedals||medalsForEach(b=>b.unlocked=!!localStorage[b.storageKey()]);engineAddPlugin(void 0,function(){if(medalsDisplayQueue.length){var b=medalsDisplayQueue[0],c=timeReal-medalsDisplayTimeLast;if(medalsDisplayTimeLast)if(c>medalDisplayTime)medalsDisplayTimeLast=0,medalsDisplayQueue.shift();else{const d=medalDisplayTime-medalDisplaySlideTime;b.render(c<medalDisplaySlideTime?1-c/medalDisplaySlideTime:c>d?(c-d)/medalDisplaySlideTime:0)}else medalsDisplayTimeLast=timeReal}})}function medalsForEach(a){Object.values(medals).forEach(b=>a(b))}class Medal{constructor(a,b,c="",d="🏆",e){ASSERT(0<=a&&!medals[a]);this.id=a;this.name=b;this.description=c;this.icon=d;this.unlocked=!1;e&&((this.image=new Image).src=e);medals[a]=this}unlock(){medalsPreventUnlock||this.unlocked||(ASSERT(medalsSaveName,"save name must be set"),localStorage[this.storageKey()]=this.unlocked=!0,medalsDisplayQueue.push(this))}render(a=0){const b=overlayContext;var c=min(medalDisplaySize.x,mainCanvas.width);const d=overlayCanvas.width-c;a*=-medalDisplaySize.y;b.save();b.beginPath();b.fillStyle=new Color(.9,.9,.9).toString();b.strokeStyle=new Color(0,0,0).toString();b.lineWidth=3;b.rect(d,a,c,medalDisplaySize.y);b.fill();b.stroke();b.clip();this.renderIcon(vec2(d+15+medalDisplayIconSize/2,a+medalDisplaySize.y/2));c=vec2(d+medalDisplayIconSize+30,a+28);drawTextScreen(this.name,c,38,new Color(0,0,0),0,void 0,"left");c.y+=32;drawTextScreen(this.description,c,24,new Color(0,0,0),0,void 0,"left");b.restore()}renderIcon(a,b=medalDisplayIconSize){this.image?overlayContext.drawImage(this.image,a.x-b/2,a.y-b/2,b,b):drawTextScreen(this.icon,a,.7*b,new Color(0,0,0))}storageKey(){return medalsSaveName+"_"+this.id}}let glCanvas,glContext,glAntialias=!0,glShader,glActiveTexture,glArrayBuffer,glGeometryBuffer,glPositionData,glColorData,glInstanceCount,glAdditive,glBatchAdditive;const gl_MAX_INSTANCES=1e4,gl_INDICES_PER_INSTANCE=11,gl_INSTANCE_BYTE_STRIDE=4*gl_INDICES_PER_INSTANCE,gl_INSTANCE_BUFFER_SIZE=gl_MAX_INSTANCES*gl_INSTANCE_BYTE_STRIDE;function glInit(){if(glEnable&&!headlessMode){glCanvas=document.createElement("canvas");glContext=glCanvas.getContext("webgl2",{antialias:glAntialias});var a=mainCanvas.parentElement;glOverlay&&a.appendChild(glCanvas);glShader=glCreateProgram("#version 300 es\nprecision highp float;uniform mat4 m;in vec2 g;in vec4 p,u,c,a;in float r;out vec2 v;out vec4 d,e;void main(){vec2 s=(g-.5)*p.zw;gl_Position=m*vec4(p.xy+s*cos(r)-vec2(-s.y,s)*sin(r),1,1);v=mix(u.xw,u.zy,g);d=c;e=a;}","#version 300 es\nprecision highp float;uniform sampler2D s;in vec2 v;in vec4 d,e;out vec4 c;void main(){c=texture(s,v)*d+e;}");a=new ArrayBuffer(gl_INSTANCE_BUFFER_SIZE);glPositionData=new Float32Array(a);glColorData=new Uint32Array(a);glArrayBuffer=glContext.createBuffer();glGeometryBuffer=glContext.createBuffer();a=new Float32Array([glInstanceCount=0,0,1,0,0,1,1,1]);glContext.bindBuffer(glContext.ARRAY_BUFFER,glGeometryBuffer);glContext.bufferData(glContext.ARRAY_BUFFER,a,glContext.STATIC_DRAW)}}function glPreRender(){if(glEnable&&!headlessMode){glClearCanvas();glContext.useProgram(glShader);glContext.activeTexture(glContext.TEXTURE0);textureInfos[0]&&glContext.bindTexture(glContext.TEXTURE_2D,glActiveTexture=textureInfos[0].glTexture);var a=glAdditive=glBatchAdditive=0,b=(d,e,f,g)=>{d=glContext.getAttribLocation(glShader,d);const k=f&&gl_INSTANCE_BYTE_STRIDE,h=f&&1,m=1==f;glContext.enableVertexAttribArray(d);glContext.vertexAttribPointer(d,g,e,m,k,a);glContext.vertexAttribDivisor(d,h);a+=g*f};glContext.bindBuffer(glContext.ARRAY_BUFFER,glGeometryBuffer);b("g",glContext.FLOAT,0,2);glContext.bindBuffer(glContext.ARRAY_BUFFER,glArrayBuffer);glContext.bufferData(glContext.ARRAY_BUFFER,gl_INSTANCE_BUFFER_SIZE,glContext.DYNAMIC_DRAW);b("p",glContext.FLOAT,4,4);b("u",glContext.FLOAT,4,4);b("c",glContext.UNSIGNED_BYTE,1,4);b("a",glContext.UNSIGNED_BYTE,1,4);b("r",glContext.FLOAT,4,1);b=vec2(2*cameraScale).divide(mainCanvasSize);var c=vec2(-1).subtract(cameraPos.multiply(b));glContext.uniformMatrix4fv(glContext.getUniformLocation(glShader,"m"),!1,[b.x,0,0,0,0,b.y,0,0,1,1,1,1,c.x,c.y,0,0])}}function glClearCanvas(){glContext.viewport(0,0,glCanvas.width=mainCanvas.width,glCanvas.height=mainCanvas.height);glContext.clear(glContext.COLOR_BUFFER_BIT)}function glSetTexture(a){headlessMode||a==glActiveTexture||(glFlush(),glContext.bindTexture(glContext.TEXTURE_2D,glActiveTexture=a))}function glCompileShader(a,b){b=glContext.createShader(b);glContext.shaderSource(b,a);glContext.compileShader(b);if(debug&&!glContext.getShaderParameter(b,glContext.COMPILE_STATUS))throw glContext.getShaderInfoLog(b);return b}function glCreateProgram(a,b){const c=glContext.createProgram();glContext.attachShader(c,glCompileShader(a,glContext.VERTEX_SHADER));glContext.attachShader(c,glCompileShader(b,glContext.FRAGMENT_SHADER));glContext.linkProgram(c);if(debug&&!glContext.getProgramParameter(c,glContext.LINK_STATUS))throw glContext.getProgramInfoLog(c);return c}function glCreateTexture(a){const b=glContext.createTexture();glContext.bindTexture(glContext.TEXTURE_2D,b);a&&a.width?glContext.texImage2D(glContext.TEXTURE_2D,0,glContext.RGBA,glContext.RGBA,glContext.UNSIGNED_BYTE,a):(a=new Uint8Array([255,255,255,255]),glContext.texImage2D(glContext.TEXTURE_2D,0,glContext.RGBA,1,1,0,glContext.RGBA,glContext.UNSIGNED_BYTE,a));a=tilesPixelated?glContext.NEAREST:glContext.LINEAR;glContext.texParameteri(glContext.TEXTURE_2D,glContext.TEXTURE_MIN_FILTER,a);glContext.texParameteri(glContext.TEXTURE_2D,glContext.TEXTURE_MAG_FILTER,a);return b}function glFlush(){if(glInstanceCount){var a=glBatchAdditive?glContext.ONE:glContext.ONE_MINUS_SRC_ALPHA;glContext.blendFuncSeparate(glContext.SRC_ALPHA,a,glContext.ONE,a);glContext.enable(glContext.BLEND);glContext.bufferSubData(glContext.ARRAY_BUFFER,0,glPositionData);glContext.drawArraysInstanced(glContext.TRIANGLE_STRIP,0,4,glInstanceCount);showWatermark&&(drawCount+=glInstanceCount);glInstanceCount=0;glBatchAdditive=glAdditive}}function glCopyToContext(a,b=!1){glEnable&&(glInstanceCount||b)&&(glFlush(),glOverlay&&!b||a.drawImage(glCanvas,0,0))}function glSetAntialias(a=!0){ASSERT(!glCanvas,"must be called before engineInit");glAntialias=a}function glDraw(a,b,c,d,e,f,g,k,h,m,n=0){ASSERT("number"==typeof m&&"number"==typeof n,"invalid color");(glInstanceCount>=gl_MAX_INSTANCES||glBatchAdditive!=glAdditive)&&glFlush();let l=glInstanceCount++*gl_INDICES_PER_INSTANCE;glPositionData[l++]=a;glPositionData[l++]=b;glPositionData[l++]=c;glPositionData[l++]=d;glPositionData[l++]=f;glPositionData[l++]=g;glPositionData[l++]=k;glPositionData[l++]=h;glColorData[l++]=m;glColorData[l++]=n;glPositionData[l++]=e}const engineName="LittleJS",engineVersion="1.11.4",frameRate=60,timeDelta=1/frameRate;let engineObjects=[],engineObjectsCollide=[],frame=0,time=0,timeReal=0,paused=!1;function setPaused(a){paused=a}let frameTimeLastMS=0,frameTimeBufferMS=0,averageFPS=0;const pluginUpdateList=[],pluginRenderList=[];function engineAddPlugin(a,b){ASSERT(!pluginUpdateList.includes(a));ASSERT(!pluginRenderList.includes(b));a&&pluginUpdateList.push(a);b&&pluginRenderList.push(b)}function engineInit(a,b,c,d,e,f=[],g=document.body){function k(n=0){var l=n-frameTimeLastMS;frameTimeLastMS=n;if(debug||showWatermark)averageFPS=lerp(.05,averageFPS,1e3/(l||1));n=debug&&keyIsDown("Equal");const p=debug&&keyIsDown("Minus");debug&&(l*=n?5:p?.2:1);timeReal+=l/1e3;frameTimeBufferMS+=paused?0:l;n||(frameTimeBufferMS=min(frameTimeBufferMS,50));h();if(paused){for(const r of engineObjects)r.parent||r.updateTransforms();inputUpdate();pluginUpdateList.forEach(r=>r());debugUpdate();c();inputUpdatePost()}else{l=0;0>frameTimeBufferMS&&-9<frameTimeBufferMS&&(l=frameTimeBufferMS,frameTimeBufferMS=0);for(;0<=frameTimeBufferMS;frameTimeBufferMS-=1e3/frameRate)time=frame++/frameRate,inputUpdate(),b(),pluginUpdateList.forEach(r=>r()),engineObjectsUpdate(),debugUpdate(),c(),inputUpdatePost();frameTimeBufferMS+=l}if(!headlessMode){mainCanvasSize=vec2(mainCanvas.width,mainCanvas.height);overlayContext.imageSmoothingEnabled=mainContext.imageSmoothingEnabled=!tilesPixelated;glPreRender();d();engineObjects.sort((r,x)=>r.renderOrder-x.renderOrder);for(var q of engineObjects)q.destroyed||q.render();e();pluginRenderList.forEach(r=>r());touchGamepadRender();debugRender();glCopyToContext(mainContext);showWatermark&&(overlayContext.textAlign="right",overlayContext.textBaseline="top",overlayContext.font="1em monospace",overlayContext.fillStyle="#000",q=engineName+" v"+engineVersion+" / "+drawCount+" / "+engineObjects.length+" / "+averageFPS.toFixed(1)+(glEnable?" GL":" 2D"),overlayContext.fillText(q,mainCanvas.width-3,3),overlayContext.fillStyle="#fff",overlayContext.fillText(q,mainCanvas.width-2,2),drawCount=0)}requestAnimationFrame(k)}function h(){if(!headlessMode){if(canvasFixedSize.x){mainCanvas.width=canvasFixedSize.x;mainCanvas.height=canvasFixedSize.y;const n=innerWidth/innerHeight,l=mainCanvas.width/mainCanvas.height;(glCanvas||mainCanvas).style.width=mainCanvas.style.width=overlayCanvas.style.width=n<l?"100%":"";(glCanvas||mainCanvas).style.height=mainCanvas.style.height=overlayCanvas.style.height=n<l?"":"100%"}else mainCanvas.width=min(innerWidth,canvasMaxSize.x),mainCanvas.height=min(innerHeight,canvasMaxSize.y);overlayCanvas.width=mainCanvas.width;overlayCanvas.height=mainCanvas.height;mainCanvasSize=vec2(mainCanvas.width,mainCanvas.height)}}function m(){new Promise(n=>n(a())).then(k)}ASSERT(!mainContext,"engine already initialized");ASSERT(Array.isArray(f),"pass in images as array");headlessMode?m():(g.style.cssText="margin:0;overflow:hidden;width:100vw;height:100vh;display:flex;align-items:center;justify-content:center;background:#000;"+(canvasPixelated?"image-rendering:pixelated;":"")+"user-select:none;-webkit-user-select:none;"+(touchInputEnable?"touch-action:none;-webkit-touch-callout:none":""),g.appendChild(mainCanvas=document.createElement("canvas")),mainContext=mainCanvas.getContext("2d"),inputInit(),audioInit(),debugInit(),glInit(),g.appendChild(overlayCanvas=document.createElement("canvas")),overlayContext=overlayCanvas.getContext("2d"),mainCanvas.style.cssText=overlayCanvas.style.cssText="position:absolute",glCanvas&&(glCanvas.style.cssText="position:absolute"),h(),g=f.map((n,l)=>new Promise(p=>{const q=new Image;q.crossOrigin="anonymous";q.onerror=q.onload=()=>{textureInfos[l]=new TextureInfo(q);p()};q.src=n})),f.length||g.push(new Promise(n=>{textureInfos[0]=new TextureInfo(new Image);n()})),showSplashScreen&&g.push(new Promise(n=>{function l(){clearInput();drawEngineSplashScreen(p+=.01);1<p?n():setTimeout(l,16)}let p=0;console.log(`${engineName} Engine v${engineVersion}`);l()})),Promise.all(g).then(m))}function engineObjectsUpdate(){function a(b){if(!b.destroyed){b.update();for(const c of b.children)a(c)}}engineObjectsCollide=engineObjects.filter(b=>b.collideSolidObjects);for(const b of engineObjects)b.parent||(a(b),b.updateTransforms());engineObjects=engineObjects.filter(b=>!b.destroyed)}function engineObjectsDestroy(){for(const a of engineObjects)a.parent||a.destroy();engineObjects=engineObjects.filter(a=>!a.destroyed)}function engineObjectsCollect(a,b,c=engineObjects){const d=[];if(a)if(b instanceof Vector2)for(const e of c)isOverlapping(a,b,e.pos,e.size)&&d.push(e);else{b*=b;for(const e of c)a.distanceSquared(e.pos)<b&&d.push(e)}else for(const e of c)d.push(e);return d}function engineObjectsCallback(a,b,c,d=engineObjects){engineObjectsCollect(a,b,d).forEach(e=>c(e))}function engineObjectsRaycast(a,b,c=engineObjects){const d=[];for(const e of c)e.collideRaycast&&isIntersecting(a,b,e.pos,e.size)&&(debugRaycast&&debugRect(e.pos,e.size,"#f00"),d.push(e));debugRaycast&&debugLine(a,b,d.length?"#f00":"#00f",.02);return d}function drawEngineSplashScreen(a){const b=overlayContext;var c=overlayCanvas.width=innerWidth,d=overlayCanvas.height=innerHeight,e=percent(a,1,.8),f=percent(a,0,.5),g=b.createRadialGradient(c/2,d/2,0,c/2,d/2,.7*Math.hypot(c,d));g.addColorStop(0,hsl(0,0,lerp(f,0,e/2),e).toString());g.addColorStop(1,hsl(0,0,0,e).toString());b.save();b.fillStyle=g;b.fillRect(0,0,c,d);g=(h,m,n,l,p)=>{b.beginPath();b.rect(h,m,n,p?l*k:l);(b.fillStyle=p)?b.fill():b.stroke()};f=(h,m,n,l=0,p=2*PI,q,r)=>{const x=(l+p)/2;l=k*(p-l)/2;b.beginPath();r&&b.lineTo(h,m);b.arc(h,m,n,x-l,x+l);(b.fillStyle=q)?b.fill():b.stroke()};e=(h=0,m=0)=>hsl([.98,.3,.57,.14][h%4]-10,.8,[0,.3,.5,.8,.9][m]).toString();a=wave(1,1,a);const k=percent(a,.1,.5);b.translate(c/2,d/2);c=min(6,min(c,d)/99);b.scale(c,c);b.translate(-40,-35);b.lineJoin=b.lineCap="round";b.lineWidth=.1+1.9*k;c=percent(a,.1,1);b.setLineDash([99*c,99]);g(7,16,18,-8,e(2,2));g(7,8,18,4,e(2,3));g(25,8,8,8,e(2,1));g(25,8,-18,8);g(25,8,8,8);g(25,16,7,23,e());g(11,39,14,-23,e(1,1));g(11,16,14,18,e(1,2));g(11,16,14,8,e(1,3));g(25,16,-14,24);g(15,29,6,-9,e(2,2));f(15,21,5,0,PI/2,e(2,4),1);g(21,21,-6,9);g(37,14,9,6,e(3,2));g(37,14,4.5,6,e(3,3));g(37,14,9,6);g(50,20,10,-8,e(0,1));g(50,20,6.5,-8,e(0,2));g(50,20,3.5,-8,e(0,3));g(50,20,10,-8);f(55,2,11.4,.5,PI-.5,e(3,3));f(55,2,11.4,.5,PI/2,e(3,2),1);f(55,2,11.4,.5,PI-.5);g(45,7,20,-7,e(0,2));g(45,-1,20,4,e(0,3));g(45,-1,20,8);for(c=5;c--;)f(60-6*c,30,9.9,0,2*PI,e(c+2,3)),f(60-6*c,30,10,-.5,PI+.5,e(c+2,2)),f(60-6*c,30,10.1,.5,PI-.5,e(c+2,1));f(36,30,10,PI/2,3*PI/2);f(48,30,10,PI/2,3*PI/2);f(60,30,10);b.beginPath();b.lineTo(36,20);b.lineTo(60,20);b.stroke();f(60,30,4,PI,3*PI,e(3,2));f(60,30,4,PI,2*PI,e(3,3));f(60,30,4,PI,3*PI);for(c=6;c--;)b.beginPath(),b.lineTo(53,54),b.lineTo(53,40),b.lineTo(53+(1+2.9*c)*k,40),b.lineTo(53+(4+3.5*c)*k,54),b.fillStyle=e(0,c%2+2),b.fill(),c%2&&b.stroke();g(6,40,5,5);g(6,40,5,5,e());g(15,54,38,-14,e());for(g=3;g--;)for(c=2;c--;)f(15*g+15,47,c?7:1,PI,3*PI,e(g,3)),b.stroke(),f(15*g+15,47,c?7:1,0,PI,e(g,2)),b.stroke();b.beginPath();b.lineTo(6,40);b.lineTo(68,40);b.stroke();b.beginPath();b.lineTo(77,54);b.lineTo(4,54);b.stroke();f=engineName;b.font="900 16px arial";b.textAlign="center";b.textBaseline="top";b.lineWidth=.1+3.9*k;g=0;for(c=0;c<f.length;++c)g+=b.measureText(f[c]).width;for(c=2;c--;)for(let h=0,m=41-g/2;h<f.length;++h)b.fillStyle=e(h,2),d=b.measureText(f[h]).width,b[c?"strokeText":"fillText"](f[h],m+d/2,55.5,17*k),m+=d;b.restore()}
1
+ let showWatermark=0,debugKey="";const debug=0,debugOverlay=0,debugPhysics=0,debugParticles=0,debugRaycast=0,debugGamepads=0,debugMedals=0;function ASSERT(){}function debugInit(){}function debugUpdate(){}function debugRender(){}function debugRect(){}function debugPoly(){}function debugCircle(){}function debugPoint(){}function debugLine(){}function debugOverlap(){}function debugText(){}function debugClear(){}function debugSaveCanvas(){}function debugSaveText(){}function debugSaveDataURL(){}const PI=Math.PI;function abs(a){return Math.abs(a)}function min(a,b){return Math.min(a,b)}function max(a,b){return Math.max(a,b)}function sign(a){return Math.sign(a)}function mod(a,b=1){return(a%b+b)%b}function clamp(a,b=0,c=1){return a<b?b:a>c?c:a}function percent(a,b,c){return(c-=b)?clamp((a-b)/c):0}function lerp(a,b,c){return b+clamp(a)*(c-b)}function distanceWrap(a,b,c=1){a=(a-b)%c;return 2*a%c-a}function lerpWrap(a,b,c,d=1){return c+clamp(a)*distanceWrap(b,c,d)}function distanceAngle(a,b){return distanceWrap(a,b,2*PI)}function lerpAngle(a,b,c){return lerpWrap(a,b,c,2*PI)}function smoothStep(a){return a*a*(3-2*a)}function nearestPowerOfTwo(a){return 2**Math.ceil(Math.log2(a))}function isOverlapping(a,b,c,d=vec2()){return 2*abs(a.x-c.x)<b.x+d.x&&2*abs(a.y-c.y)<b.y+d.y}function isIntersecting(a,b,c,d){c=c.subtract(d.scale(.5));d=c.add(d);b=b.subtract(a);c=a.subtract(c);d=a.subtract(d);a=[-b.x,b.x,-b.y,b.y];b=[c.x,-d.x,c.y,-d.y];c=0;d=1;for(let e=4;e--;)if(a[e]){const f=b[e]/a[e];if(0>a[e]){if(f>d)return!1;c=max(f,c)}else{if(f<c)return!1;d=min(f,d)}}else if(0>b[e])return!1;return!0}function wave(a=1,b=1,c=time){return b/2*(1-Math.cos(c*a*2*PI))}function formatTime(a){return(a/60|0)+":"+(10>a%60?"0":"")+(a%60|0)}function rand(a=1,b=0){return b+Math.random()*(a-b)}function randInt(a,b=0){return Math.floor(rand(a,b))}function randSign(){return 2*randInt(2)-1}function randVector(a=1){return(new Vector2).setAngle(rand(2*PI),a)}function randInCircle(a=1,b=0){return 0<a?randVector(a*rand(b/a,1)**.5):new Vector2}function randColor(a=new Color,b=new Color(0,0,0,1),c=!1){return c?a.lerp(b,rand()):new Color(rand(a.r,b.r),rand(a.g,b.g),rand(a.b,b.b),rand(a.a,b.a))}class RandomGenerator{constructor(a){this.seed=a}float(a=1,b=0){this.seed^=this.seed<<13;this.seed^=this.seed>>>17;this.seed^=this.seed<<5;return b+(a-b)*abs(this.seed%1e8)/1e8}int(a,b=0){return Math.floor(this.float(a,b))}sign(){return.5<this.float()?1:-1}}function vec2(a=0,b){return"number"==typeof a?new Vector2(a,void 0==b?a:b):new Vector2(a.x,a.y)}function isVector2(a){return a instanceof Vector2}class Vector2{constructor(a=0,b=0){this.x=a;this.y=b;ASSERT(this.isValid())}set(a=0,b=0){this.x=a;this.y=b;ASSERT(this.isValid());return this}copy(){return new Vector2(this.x,this.y)}add(a){ASSERT(isVector2(a));return new Vector2(this.x+a.x,this.y+a.y)}subtract(a){ASSERT(isVector2(a));return new Vector2(this.x-a.x,this.y-a.y)}multiply(a){ASSERT(isVector2(a));return new Vector2(this.x*a.x,this.y*a.y)}divide(a){ASSERT(isVector2(a));return new Vector2(this.x/a.x,this.y/a.y)}scale(a){ASSERT(!isVector2(a));return new Vector2(this.x*a,this.y*a)}length(){return this.lengthSquared()**.5}lengthSquared(){return this.x**2+this.y**2}distance(a){ASSERT(isVector2(a));return this.distanceSquared(a)**.5}distanceSquared(a){ASSERT(isVector2(a));return(this.x-a.x)**2+(this.y-a.y)**2}normalize(a=1){const b=this.length();return b?this.scale(a/b):new Vector2(0,a)}clampLength(a=1){const b=this.length();return b>a?this.scale(a/b):this}dot(a){ASSERT(isVector2(a));return this.x*a.x+this.y*a.y}cross(a){ASSERT(isVector2(a));return this.x*a.y-this.y*a.x}angle(){return Math.atan2(this.x,this.y)}setAngle(a=0,b=1){this.x=b*Math.sin(a);this.y=b*Math.cos(a);return this}rotate(a){const b=Math.cos(a);a=Math.sin(a);return new Vector2(this.x*b-this.y*a,this.x*a+this.y*b)}setDirection(a,b=1){a=mod(a,4);ASSERT(0==a||1==a||2==a||3==a);return vec2(a%2?a-1?-b:b:0,a%2?0:a?-b:b)}direction(){return abs(this.x)>abs(this.y)?0>this.x?3:1:0>this.y?2:0}invert(){return new Vector2(this.y,-this.x)}floor(){return new Vector2(Math.floor(this.x),Math.floor(this.y))}area(){return abs(this.x*this.y)}lerp(a,b){ASSERT(isVector2(a));return this.add(a.subtract(this).scale(clamp(b)))}arrayCheck(a){ASSERT(isVector2(a));return 0<=this.x&&0<=this.y&&this.x<a.x&&this.y<a.y}toString(a=3){if(debug)return`(${(0>this.x?"":" ")+this.x.toFixed(a)},${(0>this.y?"":" ")+this.y.toFixed(a)} )`}isValid(){return"number"==typeof this.x&&!isNaN(this.x)&&"number"==typeof this.y&&!isNaN(this.y)}}function rgb(a,b,c,d){return new Color(a,b,c,d)}function hsl(a,b,c,d){return(new Color).setHSLA(a,b,c,d)}function isColor(a){return a instanceof Color}class Color{constructor(a=1,b=1,c=1,d=1){this.r=a;this.g=b;this.b=c;this.a=d;ASSERT(this.isValid())}set(a=1,b=1,c=1,d=1){this.r=a;this.g=b;this.b=c;this.a=d;ASSERT(this.isValid());return this}copy(){return new Color(this.r,this.g,this.b,this.a)}add(a){ASSERT(isColor(a));return new Color(this.r+a.r,this.g+a.g,this.b+a.b,this.a+a.a)}subtract(a){ASSERT(isColor(a));return new Color(this.r-a.r,this.g-a.g,this.b-a.b,this.a-a.a)}multiply(a){ASSERT(isColor(a));return new Color(this.r*a.r,this.g*a.g,this.b*a.b,this.a*a.a)}divide(a){ASSERT(isColor(a));return new Color(this.r/a.r,this.g/a.g,this.b/a.b,this.a/a.a)}scale(a,b=a){return new Color(this.r*a,this.g*a,this.b*a,this.a*b)}clamp(){return new Color(clamp(this.r),clamp(this.g),clamp(this.b),clamp(this.a))}lerp(a,b){ASSERT(isColor(a));return this.add(a.subtract(this).scale(clamp(b)))}setHSLA(a=0,b=0,c=1,d=1){a=mod(a,1);b=clamp(b);c=clamp(c);b=.5>c?c*(1+b):c+b-c*b;c=2*c-b;const e=(f,g,k)=>1>6*(k=mod(k,1))?f+6*(g-f)*k:1>2*k?g:2>3*k?f+(g-f)*(4-6*k):f;this.r=e(c,b,a+1/3);this.g=e(c,b,a);this.b=e(c,b,a-1/3);this.a=d;ASSERT(this.isValid());return this}HSLA(){const a=clamp(this.r),b=clamp(this.g),c=clamp(this.b),d=clamp(this.a),e=Math.max(a,b,c),f=Math.min(a,b,c),g=(e+f)/2;let k=0,h=0;if(e!=f){let m=e-f;h=.5<g?m/(2-e-f):m/(e+f);a==e?k=(b-c)/m+(b<c?6:0):b==e?k=(c-a)/m+2:c==e&&(k=(a-b)/m+4)}return[k/6,h,g,d]}mutate(a=.05,b=0){return new Color(this.r+rand(a,-a),this.g+rand(a,-a),this.b+rand(a,-a),this.a+rand(b,-b)).clamp()}toString(a=!0){const b=c=>(16>(c=255*clamp(c)|0)?"0":"")+c.toString(16);return"#"+b(this.r)+b(this.g)+b(this.b)+(a?b(this.a):"")}setHex(a){ASSERT("string"==typeof a&&"#"==a[0]);ASSERT([4,5,7,9].includes(a.length),"Invalid hex");6>a.length?(this.r=clamp(parseInt(a[1],16)/15),this.g=clamp(parseInt(a[2],16)/15),this.b=clamp(parseInt(a[3],16)/15),this.a=5==a.length?clamp(parseInt(a[4],16)/15):1):(this.r=clamp(parseInt(a.slice(1,3),16)/255),this.g=clamp(parseInt(a.slice(3,5),16)/255),this.b=clamp(parseInt(a.slice(5,7),16)/255),this.a=9==a.length?clamp(parseInt(a.slice(7,9),16)/255):1);ASSERT(this.isValid());return this}rgbaInt(){const a=255*clamp(this.r)|0,b=255*clamp(this.g)<<8,c=255*clamp(this.b)<<16,d=255*clamp(this.a)<<24;return a+b+c+d}isValid(){return"number"==typeof this.r&&!isNaN(this.r)&&"number"==typeof this.g&&!isNaN(this.g)&&"number"==typeof this.b&&!isNaN(this.b)&&"number"==typeof this.a&&!isNaN(this.a)}}const WHITE=rgb(),BLACK=rgb(0,0,0),GRAY=rgb(.5,.5,.5),RED=rgb(1,0,0),ORANGE=rgb(1,.5,0),YELLOW=rgb(1,1,0),GREEN=rgb(0,1,0),CYAN=rgb(0,1,1),BLUE=rgb(0,0,1),PURPLE=rgb(.5,0,1),MAGENTA=rgb(1,0,1);class Timer{constructor(a){this.time=void 0==a?void 0:time+a;this.setTime=a}set(a=0){this.time=time+a;this.setTime=a}unset(){this.time=void 0}isSet(){return void 0!=this.time}active(){return time<this.time}elapsed(){return time>=this.time}get(){return this.isSet()?time-this.time:0}getPercent(){return this.isSet()?percent(this.time-time,this.setTime,0):0}toString(){if(debug)return this.isSet()?Math.abs(this.get())+" seconds "+(0>this.get()?"before":"after"):"unset"}valueOf(){return this.get()}}let cameraPos=vec2(),cameraScale=32,canvasMaxSize=vec2(1920,1080),canvasFixedSize=vec2(),canvasPixelated=!0,tilesPixelated=!0,fontDefault="arial",showSplashScreen=!1,headlessMode=!1,glEnable=!0,glOverlay=!0,tileSizeDefault=vec2(16),tileFixBleedScale=0,enablePhysicsSolver=!0,objectDefaultMass=1,objectDefaultDamping=1,objectDefaultAngleDamping=1,objectDefaultElasticity=0,objectDefaultFriction=.8,objectMaxSpeed=1,gravity=0,particleEmitRateScale=1,gamepadsEnable=!0,gamepadDirectionEmulateStick=!0,inputWASDEmulateDirection=!0,touchInputEnable=!0,touchGamepadEnable=!1,touchGamepadAnalog=!0,touchGamepadSize=99,touchGamepadAlpha=.3,vibrateEnable=!0,soundEnable=!0,soundVolume=.3,soundDefaultRange=40,soundDefaultTaper=.7,medalDisplayTime=5,medalDisplaySlideTime=.5,medalDisplaySize=vec2(640,80),medalDisplayIconSize=50,medalsPreventUnlock=!1;function setCameraPos(a){cameraPos=a}function setCameraScale(a){cameraScale=a}function setCanvasMaxSize(a){canvasMaxSize=a}function setCanvasFixedSize(a){canvasFixedSize=a}function setCanvasPixelated(a){canvasPixelated=a}function setTilesPixelated(a){tilesPixelated=a}function setFontDefault(a){fontDefault=a}function setShowSplashScreen(a){showSplashScreen=a}function setHeadlessMode(a){headlessMode=a}function setGlEnable(a){glEnable=a}function setGlOverlay(a){glOverlay=a}function setTileSizeDefault(a){tileSizeDefault=a}function setTileFixBleedScale(a){tileFixBleedScale=a}function setEnablePhysicsSolver(a){enablePhysicsSolver=a}function setObjectDefaultMass(a){objectDefaultMass=a}function setObjectDefaultDamping(a){objectDefaultDamping=a}function setObjectDefaultAngleDamping(a){objectDefaultAngleDamping=a}function setObjectDefaultElasticity(a){objectDefaultElasticity=a}function setObjectDefaultFriction(a){objectDefaultFriction=a}function setObjectMaxSpeed(a){objectMaxSpeed=a}function setGravity(a){gravity=a}function setParticleEmitRateScale(a){particleEmitRateScale=a}function setGamepadsEnable(a){gamepadsEnable=a}function setGamepadDirectionEmulateStick(a){gamepadDirectionEmulateStick=a}function setInputWASDEmulateDirection(a){inputWASDEmulateDirection=a}function setTouchInputEnable(a){touchInputEnable=a}function setTouchGamepadEnable(a){touchGamepadEnable=a}function setTouchGamepadAnalog(a){touchGamepadAnalog=a}function setTouchGamepadSize(a){touchGamepadSize=a}function setTouchGamepadAlpha(a){touchGamepadAlpha=a}function setVibrateEnable(a){vibrateEnable=a}function setSoundEnable(a){soundEnable=a}function setSoundVolume(a){soundVolume=a;soundEnable&&!headlessMode&&audioGainNode&&(audioGainNode.gain.value=a)}function setSoundDefaultRange(a){soundDefaultRange=a}function setSoundDefaultTaper(a){soundDefaultTaper=a}function setMedalDisplayTime(a){medalDisplayTime=a}function setMedalDisplaySlideTime(a){medalDisplaySlideTime=a}function setMedalDisplaySize(a){medalDisplaySize=a}function setMedalDisplayIconSize(a){medalDisplayIconSize=a}function setMedalsPreventUnlock(a){medalsPreventUnlock=a}function setShowWatermark(a){showWatermark=a}function setDebugKey(a){debugKey=a}class EngineObject{constructor(a=vec2(),b=vec2(1),c,d=0,e=new Color,f=0){ASSERT(isVector2(a)&&isVector2(b),"ensure pos and size are vec2s");ASSERT("number"!==typeof c||!c,"old style tile setup");this.pos=a.copy();this.size=b;this.drawSize=void 0;this.tileInfo=c;this.angle=d;this.color=e;this.additiveColor=void 0;this.mirror=!1;this.mass=objectDefaultMass;this.damping=objectDefaultDamping;this.angleDamping=objectDefaultAngleDamping;this.elasticity=objectDefaultElasticity;this.friction=objectDefaultFriction;this.gravityScale=1;this.renderOrder=f;this.velocity=vec2();this.angleVelocity=0;this.spawnTime=time;this.children=[];this.clampSpeedLinear=!0;this.parent=void 0;this.localPos=vec2();this.localAngle=0;this.collideRaycast=this.isSolid=this.collideSolidObjects=this.collideTiles=!1;engineObjects.push(this)}updateTransforms(){const a=this.parent;if(a){const b=a.getMirrorSign();this.pos=this.localPos.multiply(vec2(b,1)).rotate(-a.angle).add(a.pos);this.angle=b*this.localAngle+a.angle}for(const b of this.children)b.updateTransforms()}update(){if(!this.parent){if(this.clampSpeedLinear)this.velocity.x=clamp(this.velocity.x,-objectMaxSpeed,objectMaxSpeed),this.velocity.y=clamp(this.velocity.y,-objectMaxSpeed,objectMaxSpeed);else{var a=this.velocity.lengthSquared();a>objectMaxSpeed*objectMaxSpeed&&(a=objectMaxSpeed/a**.5,this.velocity.x*=a,this.velocity.y*=a)}a=this.pos.copy();this.velocity.x*=this.damping;this.velocity.y*=this.damping;this.mass&&(this.velocity.y+=gravity*this.gravityScale);this.pos.x+=this.velocity.x;this.pos.y+=this.velocity.y;this.angle+=this.angleVelocity*=this.angleDamping;ASSERT(0<=this.angleDamping&&1>=this.angleDamping);ASSERT(0<=this.damping&&1>=this.damping);if(enablePhysicsSolver&&this.mass){var b=0>this.velocity.y;if(this.groundObject){var c=this.groundObject.velocity?this.groundObject.velocity.x:0;this.velocity.x=c+(this.velocity.x-c)*this.friction;this.groundObject=0}if(this.collideSolidObjects)for(var d of engineObjectsCollide){if(!this.isSolid&&!d.isSolid||d.destroyed||d.parent||d==this)continue;if(!isOverlapping(this.pos,this.size,d.pos,d.size))continue;c=this.collideWithObject(d);var e=d.collideWithObject(this);if(!c||!e)continue;if(isOverlapping(a,this.size,d.pos,d.size)){c=a.subtract(d.pos);e=c.length();c=.01>e?randVector(.001):c.scale(.001/e);this.velocity=this.velocity.add(c);d.mass&&(d.velocity=d.velocity.subtract(c));debugOverlay&&debugPhysics&&debugOverlap(this.pos,this.size,d.pos,d.size,"#f00");continue}e=this.size.add(d.size);var f=2*(a.y-d.pos.y)>e.y+gravity;const k=2*abs(a.y-d.pos.y)<e.y;var g=2*abs(a.x-d.pos.x)<e.x;c=max(this.elasticity,d.elasticity);if(f||g||!k)if(this.pos.y=d.pos.y+(e.y/2+.001)*sign(a.y-d.pos.y),d.groundObject&&b||!d.mass)b&&(this.groundObject=d),this.velocity.y*=-c;else if(d.mass){g=(this.mass*this.velocity.y+d.mass*d.velocity.y)/(this.mass+d.mass);const h=d.velocity.y*(d.mass-this.mass)/(this.mass+d.mass)+2*this.velocity.y*this.mass/(this.mass+d.mass);this.velocity.y=lerp(c,g,this.velocity.y*(this.mass-d.mass)/(this.mass+d.mass)+2*d.velocity.y*d.mass/(this.mass+d.mass));d.velocity.y=lerp(c,g,h)}!f&&k&&(this.pos.x=d.pos.x+(e.x/2+.001)*sign(a.x-d.pos.x),d.mass?(e=(this.mass*this.velocity.x+d.mass*d.velocity.x)/(this.mass+d.mass),f=d.velocity.x*(d.mass-this.mass)/(this.mass+d.mass)+2*this.velocity.x*this.mass/(this.mass+d.mass),this.velocity.x=lerp(c,e,this.velocity.x*(this.mass-d.mass)/(this.mass+d.mass)+2*d.velocity.x*d.mass/(this.mass+d.mass)),d.velocity.x=lerp(c,e,f)):this.velocity.x*=-c);debugOverlay&&debugPhysics&&debugOverlap(this.pos,this.size,d.pos,d.size,"#f0f")}if(this.collideTiles&&tileCollisionTest(this.pos,this.size,this)&&!tileCollisionTest(a,this.size,this)){d=tileCollisionTest(vec2(a.x,this.pos.y),this.size,this);c=tileCollisionTest(vec2(this.pos.x,a.y),this.size,this);if(d||!c)this.velocity.y*=-this.elasticity,(this.groundObject=b)?this.pos.y=(a.y-this.size.y/2|0)+this.size.y/2+1e-4:this.pos.y=a.y;c&&(this.pos.x=a.x,this.velocity.x*=-this.elasticity);debugOverlay&&debugPhysics&&debugRect(this.pos,this.size,"#f00")}}}}render(){drawTile(this.pos,this.drawSize||this.size,this.tileInfo,this.color,this.angle,this.mirror,this.additiveColor)}destroy(){if(!this.destroyed){this.destroyed=1;this.parent&&this.parent.removeChild(this);for(const a of this.children)a.destroy(a.parent=0)}}localToWorld(a){return this.pos.add(a.rotate(-this.angle))}worldToLocal(a){return a.subtract(this.pos).rotate(this.angle)}localToWorldVector(a){return a.rotate(this.angle)}worldToLocalVector(a){return a.rotate(-this.angle)}collideWithTile(a,b){return 0<a}collideWithObject(a){return!0}getAliveTime(){return time-this.spawnTime}applyAcceleration(a){this.mass&&(this.velocity=this.velocity.add(a))}applyForce(a){this.applyAcceleration(a.scale(1/this.mass))}getMirrorSign(){return this.mirror?-1:1}addChild(a,b=vec2(),c=0){ASSERT(!a.parent&&!this.children.includes(a));this.children.push(a);a.parent=this;a.localPos=b.copy();a.localAngle=c}removeChild(a){ASSERT(a.parent==this&&this.children.includes(a));this.children.splice(this.children.indexOf(a),1);a.parent=0}setCollision(a=!0,b=!0,c=!0,d=!0){ASSERT(a||!b,"solid objects must be set to collide");this.collideSolidObjects=a;this.isSolid=b;this.collideTiles=c;this.collideRaycast=d}toString(){if(debug){let a="type = "+this.constructor.name;if(this.pos.x||this.pos.y)a+="\npos = "+this.pos;if(this.velocity.x||this.velocity.y)a+="\nvelocity = "+this.velocity;if(this.size.x||this.size.y)a+="\nsize = "+this.size;this.angle&&(a+="\nangle = "+this.angle.toFixed(3));this.color&&(a+="\ncolor = "+this.color);return a}}renderDebugInfo(){if(debug){const a=vec2(max(this.size.x,.2),max(this.size.y,.2)),b=rgb(this.collideTiles?1:0,this.collideSolidObjects?1:0,this.isSolid?1:0,this.parent?.2:.5),c=this.parent?rgb(1,1,1,.5):rgb(0,0,0,.8);drawRect(this.pos,a,b,this.angle,!1);drawRect(this.pos,a.scale(.8),c,this.angle,!1);this.parent&&drawLine(this.pos,this.parent.pos,.1,rgb(0,0,1,.5),!1)}}}let mainCanvas,mainContext,overlayCanvas,overlayContext,mainCanvasSize=vec2(),textureInfos=[],drawCount;function tile(a=vec2(),b=tileSizeDefault,c=0,d=0){if(headlessMode)return new TileInfo;"number"===typeof b&&(ASSERT(0<b),b=vec2(b));var e=textureInfos[c];ASSERT(!!e,"Texture not loaded");const f=b.add(vec2(2*d));"number"===typeof a&&(e=e.size.x/f.x|0,a=0<e?vec2(a%e,a/e|0):vec2());a=vec2(a.x*f.x+d,a.y*f.y+d);return new TileInfo(a,b,c,d)}class TileInfo{constructor(a=vec2(),b=tileSizeDefault,c=0,d=0){this.pos=a.copy();this.size=b.copy();this.textureIndex=c;this.padding=d}offset(a){return new TileInfo(this.pos.add(a),this.size,this.textureIndex)}frame(a){ASSERT("number"==typeof a);return this.offset(vec2(a*(this.size.x+2*this.padding),0))}getTextureInfo(){return textureInfos[this.textureIndex]}}class TextureInfo{constructor(a){this.image=a;this.size=vec2(a.width,a.height);this.glTexture=glEnable&&glCreateTexture(a)}}function screenToWorld(a){return new Vector2((a.x-mainCanvasSize.x/2+.5)/cameraScale+cameraPos.x,(a.y-mainCanvasSize.y/2+.5)/-cameraScale+cameraPos.y)}function worldToScreen(a){return new Vector2((a.x-cameraPos.x)*cameraScale+mainCanvasSize.x/2-.5,(a.y-cameraPos.y)*-cameraScale+mainCanvasSize.y/2-.5)}function getCameraSize(){return mainCanvasSize.scale(1/cameraScale)}function drawTile(a,b=vec2(1),c,d=new Color,e=0,f,g=new Color(0,0,0,0),k=glEnable,h,m){ASSERT(!m||!k,"context only supported in canvas 2D mode");ASSERT("number"!==typeof c||!c,"this is an old style calls, to fix replace it with tile(tileIndex, tileSize)");const n=c&&c.getTextureInfo();if(k)if(h&&(a=screenToWorld(a),b=b.scale(1/cameraScale)),n){var l=vec2(1).divide(n.size);k=c.pos.x*l.x;h=c.pos.y*l.y;m=c.size.x*l.x;const p=c.size.y*l.y;l=l.scale(tileFixBleedScale);glSetTexture(n.glTexture);glDraw(a.x,a.y,f?-b.x:b.x,b.y,e,k+l.x,h+l.y,k-l.x+m,h-l.y+p,d.rgbaInt(),g.rgbaInt())}else glDraw(a.x,a.y,b.x,b.y,e,0,0,0,0,0,d.rgbaInt());else showWatermark&&++drawCount,b=vec2(b.x,-b.y),drawCanvas2D(a,b,e,f,p=>{if(n){const q=c.pos.x+tileFixBleedScale,r=c.pos.y+tileFixBleedScale,x=c.size.x-2*tileFixBleedScale,v=c.size.y-2*tileFixBleedScale;p.globalAlpha=d.a;p.drawImage(n.image,q,r,x,v,-.5,-.5,1,1);p.globalAlpha=1}else p.fillStyle=d,p.fillRect(-.5,-.5,1,1)},h,m)}function drawRect(a,b,c,d,e,f,g){drawTile(a,b,void 0,c,d,!1,void 0,e,f,g)}function drawLine(a,b,c=.1,d,e,f,g){b=vec2((b.x-a.x)/2,(b.y-a.y)/2);c=vec2(c,2*b.length());drawRect(a.add(b),c,d,b.angle(),e,f,g)}function drawPoly(a,b=new Color,c=0,d=new Color(0,0,0),e,f=mainContext){f.fillStyle=b.toString();f.beginPath();for(const g of e?a:a.map(worldToScreen))f.lineTo(g.x,g.y);f.closePath();f.fill();c&&(f.strokeStyle=d.toString(),f.lineWidth=e?c:c*cameraScale,f.stroke())}function drawEllipse(a,b=1,c=1,d=0,e=new Color,f=0,g=new Color(0,0,0),k,h=mainContext){k||(a=worldToScreen(a),b*=cameraScale,c*=cameraScale,f*=cameraScale);h.fillStyle=e.toString();h.beginPath();h.ellipse(a.x,a.y,b,c,d,0,9);h.fill();f&&(h.strokeStyle=g.toString(),h.lineWidth=f,h.stroke())}function drawCircle(a,b=1,c=new Color,d=0,e=new Color(0,0,0),f,g=mainContext){drawEllipse(a,b,b,0,c,d,e,f,g)}function drawCanvas2D(a,b,c,d,e,f,g=mainContext){f||(a=worldToScreen(a),b=b.scale(cameraScale));g.save();g.translate(a.x+.5,a.y+.5);g.rotate(c);g.scale(d?-b.x:b.x,-b.y);e(g);g.restore()}function drawText(a,b,c=1,d,e=0,f,g,k,h,m=mainContext){drawTextScreen(a,worldToScreen(b),c*cameraScale,d,e*cameraScale,f,g,k,h,m)}function drawTextOverlay(a,b,c=1,d,e=0,f,g,k,h){drawText(a,b,c,d,e,f,g,k,h,overlayContext)}function drawTextScreen(a,b,c=1,d=new Color,e=0,f=new Color(0,0,0),g="center",k=fontDefault,h,m=overlayContext){m.fillStyle=d.toString();m.lineWidth=e;m.strokeStyle=f.toString();m.textAlign=g;m.font=c+"px "+k;m.textBaseline="middle";m.lineJoin="round";b=b.copy();(a+"").split("\n").forEach(n=>{e&&m.strokeText(n,b.x,b.y,h);m.fillText(n,b.x,b.y,h);b.y+=c})}function setBlendMode(a,b=glEnable,c){ASSERT(!c||!b,"context only supported in canvas 2D mode");b?glAdditive=a:(c||=mainContext,c.globalCompositeOperation=a?"lighter":"source-over")}function combineCanvases(){glCopyToContext(mainContext,!0);mainContext.drawImage(overlayCanvas,0,0);glClearCanvas();overlayCanvas.width|=0}let engineFontImage;class FontImage{constructor(a,b=vec2(8),c=vec2(0,1),d=overlayContext){engineFontImage||((engineFontImage=new Image).src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQAAAAAYAQAAAAA9+x6JAAAAAnRSTlMAAHaTzTgAAAGiSURBVHjaZZABhxxBEIUf6ECLBdFY+Q0PMNgf0yCgsSAGZcT9sgIPtBWwIA5wgAPEoHUyJeeSlW+gjK+fegWwtROWpVQEyWh2npdpBmTUFVhb29RINgLIukoXr5LIAvYQ5ve+1FqWEMqNKTX3FAJHyQDRZvmKWubAACcv5z5Gtg2oyCWE+Yk/8JZQX1jTTCpKAFGIgza+dJCNBF2UskRlsgwitHbSV0QLgt9sTPtsRlvJjEr8C/FARWA2bJ/TtJ7lko34dNDn6usJUMzuErP89UUBJbWeozrwLLncXczd508deAjLWipLO4Q5XGPcJvPu92cNDaN0P5G1FL0nSOzddZOrJ6rNhbXGmeDvO3TF7DeJWl4bvaYQTNHCTeuqKZmbjHaSOFes+IX/+IhHrnAkXOAsfn24EM68XieIECoccD4KZLk/odiwzeo2rovYdhvb2HYFgyznJyDpYJdYOmfXgVdJTaUi4xA2uWYNYec9BLeqdl9EsoTw582mSFDX2DxVLbNt9U3YYoeatBad1c2Tj8t2akrjaIGJNywKB/7h75/gN3vCMSaadIUTAAAAAElFTkSuQmCC");this.image=a||engineFontImage;this.tileSize=b;this.paddingSize=c;this.context=d}drawText(a,b,c=1,d){this.drawTextScreen(a,worldToScreen(b).floor(),c*cameraScale|0,d)}drawTextScreen(a,b,c=4,d){const e=this.context;e.save();const f=this.tileSize,g=f.add(this.paddingSize).scale(c),k=this.image.width/this.tileSize.x|0;(a+"").split("\n").forEach((h,m)=>{const n=d?h.length*f.x*c/2|0:0;for(let q=h.length;q--;){var l=h[q].charCodeAt(0);if(32>l||127<l)l=127;var p=l-32;l=p%k;p=p/k|0;const r=b.add(vec2(q,m).multiply(g));e.drawImage(this.image,l*f.x,p*f.y,f.x,f.y,r.x-n,r.y,f.x*c,f.y*c)}});e.restore()}}function isFullscreen(){return!!document.fullscreenElement}function toggleFullscreen(){const a=mainCanvas.parentElement;isFullscreen()?document.exitFullscreen&&document.exitFullscreen():a.requestFullscreen&&a.requestFullscreen()}function setCursor(a="auto"){mainCanvas.parentElement.style.cursor=a}function keyIsDown(a,b=0){ASSERT(0<b||"number"!==typeof a||3>a,"use code string for keyboard");return inputData[b]&&!!(inputData[b][a]&1)}function keyWasPressed(a,b=0){ASSERT(0<b||"number"!==typeof a||3>a,"use code string for keyboard");return inputData[b]&&!!(inputData[b][a]&2)}function keyWasReleased(a,b=0){ASSERT(0<b||"number"!==typeof a||3>a,"use code string for keyboard");return inputData[b]&&!!(inputData[b][a]&4)}function clearInput(){inputData=[[]];touchGamepadButtons=[]}const mouseIsDown=keyIsDown,mouseWasPressed=keyWasPressed,mouseWasReleased=keyWasReleased;let mousePos=vec2(),mousePosScreen=vec2(),mouseWheel=0,isUsingGamepad=!1,preventDefaultInput=!1;function gamepadIsDown(a,b=0){return keyIsDown(a,b+1)}function gamepadWasPressed(a,b=0){return keyWasPressed(a,b+1)}function gamepadWasReleased(a,b=0){return keyWasReleased(a,b+1)}function gamepadStick(a,b=0){return gamepadStickData[b]?gamepadStickData[b][a]||vec2():vec2()}let inputData=[[]];function inputUpdate(){headlessMode||(touchInputEnable&&isTouchDevice||document.hasFocus()||clearInput(),mousePos=screenToWorld(mousePosScreen),gamepadsUpdate())}function inputUpdatePost(){if(!headlessMode){for(const a of inputData)for(const b in a)a[b]&=1;mouseWheel=0}}function inputInit(){function a(b){return inputWASDEmulateDirection?"KeyW"==b?"ArrowUp":"KeyS"==b?"ArrowDown":"KeyA"==b?"ArrowLeft":"KeyD"==b?"ArrowRight":b:b}headlessMode||(onkeydown=b=>{b.repeat||(isUsingGamepad=!1,inputData[0][b.code]=3,inputWASDEmulateDirection&&(inputData[0][a(b.code)]=3));preventDefaultInput&&b.preventDefault()},onkeyup=b=>{inputData[0][b.code]=4;inputWASDEmulateDirection&&(inputData[0][a(b.code)]=4)},onmousedown=b=>{soundEnable&&!headlessMode&&audioContext&&"running"!=audioContext.state&&audioContext.resume();isUsingGamepad=!1;inputData[0][b.button]=3;mousePosScreen=mouseToScreen(b);b.button&&b.preventDefault()},onmouseup=b=>inputData[0][b.button]=inputData[0][b.button]&2|4,onmousemove=b=>mousePosScreen=mouseToScreen(b),onwheel=b=>mouseWheel=b.ctrlKey?0:sign(b.deltaY),oncontextmenu=b=>!1,onblur=b=>clearInput(),isTouchDevice&&touchInputEnable&&touchInputInit())}function mouseToScreen(a){if(!mainCanvas||headlessMode)return vec2();const b=mainCanvas.getBoundingClientRect();return vec2(mainCanvas.width,mainCanvas.height).multiply(vec2(percent(a.x,b.left,b.right),percent(a.y,b.top,b.bottom)))}const gamepadStickData=[];function gamepadsUpdate(){const a=g=>{const k=h=>.3<h?percent(h,.3,.8):-.3>h?-percent(-h,.3,.8):0;return vec2(k(g.x),k(-g.y)).clampLength()};if(touchGamepadEnable&&isTouchDevice&&(ASSERT(touchGamepadButtons,"set touchGamepadEnable before calling init!"),touchGamepadTimer.isSet())){var b=gamepadStickData[0]||(gamepadStickData[0]=[]);b[0]=vec2();touchGamepadAnalog?b[0]=a(touchGamepadStick):.3<touchGamepadStick.lengthSquared()&&(b[0].x=Math.round(touchGamepadStick.x),b[0].y=-Math.round(touchGamepadStick.y),b[0]=b[0].clampLength());b=inputData[1]||(inputData[1]=[]);for(var c=10;c--;){var d=3==c?2:2==c?3:c,e=gamepadIsDown(d,0);b[d]=touchGamepadButtons[c]?e?1:3:e?4:0}}if(gamepadsEnable&&navigator&&navigator.getGamepads&&(debug||document.hasFocus()))for(b=navigator.getGamepads(),c=b.length;c--;){e=b[c];const g=inputData[c+1]||(inputData[c+1]=[]);d=gamepadStickData[c]||(gamepadStickData[c]=[]);if(e){for(var f=0;f<e.axes.length-1;f+=2)d[f>>1]=a(vec2(e.axes[f],e.axes[f+1]));for(f=e.buttons.length;f--;){const k=e.buttons[f],h=gamepadIsDown(f,c);g[f]=k.pressed?h?1:3:h?4:0;isUsingGamepad||=!c&&k.pressed}gamepadDirectionEmulateStick&&(e=vec2((gamepadIsDown(15,c)&&1)-(gamepadIsDown(14,c)&&1),(gamepadIsDown(12,c)&&1)-(gamepadIsDown(13,c)&&1)),e.lengthSquared()&&(d[0]=e.clampLength()));touchGamepadEnable&&isUsingGamepad&&touchGamepadTimer.unset()}}}function vibrate(a=100){vibrateEnable&&!headlessMode&&navigator&&navigator.vibrate&&navigator.vibrate(a)}function vibrateStop(){vibrate(0)}const isTouchDevice=!headlessMode&&void 0!==window.ontouchstart;let touchGamepadTimer=new Timer,touchGamepadButtons,touchGamepadStick;function touchInputInit(){function a(e){soundEnable&&!headlessMode&&audioContext&&"running"!=audioContext.state&&audioContext.resume();const f=e.touches.length;if(f){const g=vec2(e.touches[0].clientX,e.touches[0].clientY);mousePosScreen=mouseToScreen(g);d?isUsingGamepad=touchGamepadEnable:inputData[0][0]=3}else d&&(inputData[0][0]=inputData[0][0]&2|4);d=f;document.hasFocus()&&e.preventDefault();return!0}function b(e){touchGamepadStick=vec2();touchGamepadButtons=[];isUsingGamepad=!0;if(e.touches.length&&(touchGamepadTimer.set(),paused&&!d)){touchGamepadButtons[9]=1;a(e);return}const f=vec2(touchGamepadSize,mainCanvasSize.y-touchGamepadSize),g=mainCanvasSize.subtract(vec2(touchGamepadSize,touchGamepadSize)),k=mainCanvasSize.scale(.5);for(const m of e.touches){var h=mouseToScreen(vec2(m.clientX,m.clientY));h.distance(f)<touchGamepadSize?touchGamepadStick=h.subtract(f).scale(2/touchGamepadSize).clampLength():h.distance(g)<touchGamepadSize?(h=h.subtract(g).direction(),touchGamepadButtons[h]=1):h.distance(k)<touchGamepadSize&&!d&&(touchGamepadButtons[9]=1)}a(e);return!0}let c=a;touchGamepadEnable&&(c=b,touchGamepadButtons=[],touchGamepadStick=vec2());document.addEventListener("touchstart",e=>c(e),{passive:!1});document.addEventListener("touchmove",e=>c(e),{passive:!1});document.addEventListener("touchend",e=>c(e),{passive:!1});onmousedown=onmouseup=()=>0;let d}function touchGamepadRender(){if(touchInputEnable&&isTouchDevice&&!headlessMode&&touchGamepadEnable&&touchGamepadTimer.isSet()){var a=percent(touchGamepadTimer.get(),4,3);if(a&&!paused){var b=overlayContext;b.save();b.globalAlpha=a*touchGamepadAlpha;b.strokeStyle="#fff";b.lineWidth=3;b.fillStyle=0<touchGamepadStick.lengthSquared()?"#fff":"#000";b.beginPath();a=vec2(touchGamepadSize,mainCanvasSize.y-touchGamepadSize);if(touchGamepadAnalog)b.arc(a.x,a.y,touchGamepadSize/2,0,9),b.fill();else for(var c=10;c--;){var d=c*PI/4;b.arc(a.x,a.y,.6*touchGamepadSize,d+PI/8,d+PI/8);c%2&&b.arc(a.x,a.y,.33*touchGamepadSize,d,d);1==c&&b.fill()}b.stroke();a=vec2(mainCanvasSize.x-touchGamepadSize,mainCanvasSize.y-touchGamepadSize);for(c=4;c--;)d=a.add(vec2().setDirection(c,touchGamepadSize/2)),b.fillStyle=touchGamepadButtons[c]?"#fff":"#000",b.beginPath(),b.arc(d.x,d.y,touchGamepadSize/4,0,9),b.fill(),b.stroke();b.restore()}}}let audioContext=new AudioContext,audioGainNode;function audioInit(){soundEnable&&!headlessMode&&(audioGainNode=audioContext.createGain(),audioGainNode.connect(audioContext.destination),audioGainNode.gain.value=soundVolume)}class Sound{constructor(a,b=soundDefaultRange,c=soundDefaultTaper){soundEnable&&!headlessMode&&(this.range=b,this.taper=c,this.randomness=0,a&&(this.randomness=void 0!=a[1]?a[1]:.05,a[1]=0,this.sampleChannels=[zzfxG(...a)],this.sampleRate=zzfxR))}play(a,b=1,c=1,d=1,e=!1){if(soundEnable&&!headlessMode&&this.sampleChannels){var f;if(a){if(f=this.range){const g=cameraPos.distanceSquared(a);if(g>f*f)return;b*=percent(g**.5,f,f*this.taper)}f=2*worldToScreen(a).x/mainCanvas.width-1}a=c+c*this.randomness*d*rand(-1,1);this.gainNode=audioContext.createGain();return this.source=playSamples(this.sampleChannels,b,a,f,e,this.sampleRate,this.gainNode)}}setVolume(a=1){this.gainNode&&(this.gainNode.gain.value=a)}stop(){this.source&&this.source.stop();this.source=void 0}getSource(){return this.source}playNote(a,b,c){return this.play(b,c,2**(a/12),0)}getDuration(){return this.sampleChannels&&this.sampleChannels[0].length/this.sampleRate}isLoading(){return!this.sampleChannels}}class SoundWave extends Sound{constructor(a,b=0,c,d,e){super(void 0,c,d);soundEnable&&!headlessMode&&(this.randomness=b,fetch(a).then(f=>f.arrayBuffer()).then(f=>audioContext.decodeAudioData(f)).then(f=>{this.sampleChannels=[];for(let g=f.numberOfChannels;g--;)this.sampleChannels[g]=Array.from(f.getChannelData(g));this.sampleRate=f.sampleRate}).then(()=>e&&e(this)))}}function playAudioFile(a,b=1,c=!1){if(soundEnable&&!headlessMode)return new SoundWave(a,0,0,0,d=>d.play(void 0,b,1,1,c))}class Music extends Sound{constructor(a){super(void 0);soundEnable&&!headlessMode&&(this.randomness=0,this.sampleChannels=zzfxM(...a),this.sampleRate=zzfxR)}playMusic(a,b=!1){return super.play(void 0,a,1,1,b)}}function speak(a,b="",c=1,d=1,e=1){if(soundEnable&&!headlessMode&&speechSynthesis)return a=new SpeechSynthesisUtterance(a),a.lang=b,a.volume=2*c*soundVolume,a.rate=d,a.pitch=e,speechSynthesis.speak(a),a}function speakStop(){speechSynthesis&&speechSynthesis.cancel()}function getNoteFrequency(a,b=220){return b*2**(a/12)}function playSamples(a,b=1,c=1,d=0,e=!1,f=zzfxR,g){if(soundEnable&&!headlessMode){var k=audioContext.createBuffer(a.length,a[0].length,f),h=audioContext.createBufferSource();a.forEach((m,n)=>k.getChannelData(n).set(m));h.buffer=k;h.playbackRate.value=c;h.loop=e;g=g||audioContext.createGain();g.gain.value=b;g.connect(audioGainNode);a=new StereoPannerNode(audioContext,{pan:clamp(d,-1,1)});h.connect(a).connect(g);"running"!=audioContext.state?audioContext.resume().then(()=>h.start()):h.start();return h}}function zzfx(...a){return playSamples([zzfxG(...a)])}const zzfxR=44100;function zzfxG(a=1,b=.05,c=220,d=0,e=0,f=.1,g=0,k=1,h=0,m=0,n=0,l=0,p=0,q=0,r=0,x=0,v=0,D=1,z=0,E=0,A=0){let w=2*PI;var t=zzfxR;let F=h*=500*w/t/t;b=c*=rand(1+b,1-b)*w/t;let C=[],y=0,G=0,u=0,H=1,R=0,S=0,B=0,J;var L=w*abs(A)*2/t,K=Math.cos(L),M=Math.sin(L)/2/2,I=1+M;L=-2*K/I;M=(1-M)/I;let N=(1+sign(A)*K)/2/I;K=-(sign(A)+K)/I;let O=I=0,P=0,Q=0;d=d*t+9;z*=t;e*=t;f*=t;v*=t;m*=500*w/t**3;r*=w/t;n*=w/t;l*=t;p=p*t|0;for(J=d+z+e+f+v|0;u<J;C[u++]=B*a)++S%(100*x|0)||(B=g?1<g?2<g?3<g?Math.sin(y**3):clamp(Math.tan(y),1,-1):1-(2*y/w%2+2)%2:1-4*abs(Math.round(y/w)-y/w):Math.sin(y),B=(p?1-E+E*Math.sin(w*u/p):1)*sign(B)*abs(B)**k*(u<d?u/d:u<d+z?1-(u-d)/z*(1-D):u<d+z+e?D:u<J-v?(J-u-v)/f*D:0),B=v?B/2+(v>u?0:(u<J-v?1:(J-u)/v)*C[u-v|0]/2/a):B,A&&(B=Q=N*I+K*(I=O)+N*(O=B)-M*P-L*(P=Q))),t=(c+=h+=m)*Math.cos(r*G++),y+=t+t*q*Math.sin(u**5),H&&++H>l&&(c+=n,b+=n,H=0),!p||++R%p||(c=b,h=F,H=H||1);return C}function zzfxM(a,b,c,d=125){let e,f,g,k,h,m,n,l,p,q,r,x,v,D=0,z,E=[],A=[],w=[],t=0,F=0,C=1,y={},G=zzfxR/d*60>>2;for(;C;t++)E=[C=l=x=0],c.forEach((u,H)=>{n=b[u][t]||[0,0,0];C|=b[u][t]&&1;z=x+(b[u][0].length-2-(l?0:1))*G;v=H==c.length-1;e=2;for(g=x;e<n.length+v;l=++e){h=n[e];p=e==n.length+v-1&&v||q!=(n[0]||0)||h|0;for(f=0;f<G&&l;f++>G-99&&p&&1>r?r+=1/99:0)m=(1-r)*E[D++]/2||0,A[g]=(A[g]||0)-m*F+m,w[g]=(w[g++]||0)+m*F+m;h&&(r=h%1,F=n[1]||0,h|=0)&&(E=y[[q=n[D=0]||0,h]]=y[[q,h]]||(k=[...a[q]],k[2]*=2**((h-12)/12),0<h?zzfxG(...k):[]))}x=z});return[A,w]}let tileCollision=[],tileCollisionSize=vec2();function initTileCollision(a){tileCollisionSize=a;tileCollision=[];for(a=tileCollision.length=tileCollisionSize.area();a--;)tileCollision[a]=0}function setTileCollisionData(a,b=0){a.arrayCheck(tileCollisionSize)&&(tileCollision[(a.y|0)*tileCollisionSize.x+a.x|0]=b)}function getTileCollisionData(a){return a.arrayCheck(tileCollisionSize)?tileCollision[(a.y|0)*tileCollisionSize.x+a.x|0]:0}function tileCollisionTest(a,b=vec2(),c){const d=max(a.x-b.x/2|0,0);var e=max(a.y-b.y/2|0,0);const f=min(a.x+b.x/2,tileCollisionSize.x);for(a=min(a.y+b.y/2,tileCollisionSize.y);e<a;++e)for(b=d;b<f;++b){const g=tileCollision[e*tileCollisionSize.x+b];if(g&&(!c||c.collideWithTile(g,vec2(b,e))))return!0}return!1}function tileCollisionRaycast(a,b,c){const d=b.subtract(a),e=d.length();var f=d.normalize();f=vec2(abs(1/f.x),abs(1/f.y));let g=a.floor(),k=f.x*(0>d.x?a.x-g.x:g.x-a.x+1),h=f.y*(0>d.y?a.y-g.y:g.y-a.y+1);for(;;){const m=getTileCollisionData(g);if(m&&(!c||c.collideWithTile(m,g)))return debugRaycast&&debugLine(a,b,"#f00",.02),debugRaycast&&debugPoint(g.add(vec2(.5)),"#ff0"),g.add(vec2(.5));if(k>e&&h>e)break;k>h?(g.y+=sign(d.y),h+=f.y):(g.x+=sign(d.x),k+=f.x)}debugRaycast&&debugLine(a,b,"#00f",.02)}class TileLayerData{constructor(a,b=0,c=!1,d=new Color){this.tile=a;this.direction=b;this.mirror=c;this.color=d}clear(){this.tile=this.direction=0;this.mirror=!1;this.color=new Color}}class TileLayer extends EngineObject{constructor(a,b=tileCollisionSize,c=tile(),d=vec2(1),e=0){super(a,b,c,0,void 0,e);this.canvas=document.createElement("canvas");this.context=this.canvas.getContext("2d");this.scale=d;this.isOverlay=!1;this.data=[];for(a=this.size.area();a--;)this.data.push(new TileLayerData);headlessMode&&(this.redraw=()=>{},this.render=()=>{},this.redrawStart=()=>{},this.redrawEnd=()=>{},this.drawTileData=()=>{},this.drawCanvas2D=()=>{})}setData(a,b,c=!1){a.arrayCheck(this.size)&&(this.data[(a.y|0)*this.size.x+a.x|0]=b,c&&this.drawTileData(a))}getData(a){return a.arrayCheck(this.size)&&this.data[(a.y|0)*this.size.x+a.x|0]}update(){}render(){ASSERT(mainContext!=this.context,"must call redrawEnd() after drawing tiles");glOverlay||this.isOverlay||glCopyToContext(mainContext);const a=worldToScreen(this.pos.add(vec2(0,this.size.y*this.scale.y)));a.x|=0;a.y|=0;(this.isOverlay?overlayContext:mainContext).drawImage(this.canvas,a.x,a.y,cameraScale*this.size.x*this.scale.x,cameraScale*this.size.y*this.scale.y)}redraw(){this.redrawStart(!0);for(let a=this.size.x;a--;)for(let b=this.size.y;b--;)this.drawTileData(vec2(a,b),!1);this.redrawEnd()}redrawStart(a=!1){this.savedRenderSettings=[mainCanvas,mainContext,mainCanvasSize,cameraPos,cameraScale];mainCanvas=this.canvas;mainContext=this.context;mainCanvasSize=this.size.multiply(this.tileInfo.size);cameraPos=this.size.scale(.5);cameraScale=this.tileInfo.size.x;a&&(mainCanvas.width=mainCanvasSize.x,mainCanvas.height=mainCanvasSize.y);this.context.imageSmoothingEnabled=!tilesPixelated;glPreRender()}redrawEnd(){ASSERT(mainContext==this.context,"must call redrawStart() before drawing tiles");glCopyToContext(mainContext,!0);[mainCanvas,mainContext,mainCanvasSize,cameraPos,cameraScale]=this.savedRenderSettings}drawTileData(a,b=!0){var c=this.tileInfo.size;b&&(b=a.multiply(c),this.context.clearRect(b.x,this.canvas.height-b.y,c.x,-c.y));b=this.getData(a);void 0!=b.tile&&(ASSERT(mainContext==this.context,"must call redrawStart() before drawing tiles"),a=a.add(vec2(.5)),c=tile(b.tile,c,this.tileInfo.textureIndex),drawTile(a,vec2(1),c,b.color,b.direction*PI/2,b.mirror))}drawCanvas2D(a,b,c,d,e){const f=this.context;f.save();a=a.subtract(this.pos).multiply(this.tileInfo.size);b=b.multiply(this.tileInfo.size);f.translate(a.x,this.canvas.height-a.y);f.rotate(c);f.scale(d?-b.x:b.x,b.y);e(f);f.restore()}drawTile(a,b=vec2(1),c,d=new Color,e,f){this.drawCanvas2D(a,b,e,f,g=>{const k=c&&c.getTextureInfo();k?(g.globalAlpha=d.a,g.drawImage(k.image,c.pos.x,c.pos.y,c.size.x,c.size.y,-.5,-.5,1,1),g.globalAlpha=1):(g.fillStyle=d,g.fillRect(-.5,-.5,1,1))})}drawRect(a,b,c,d){this.drawTile(a,b,void 0,c,d)}}class ParticleEmitter extends EngineObject{constructor(a,b,c=0,d=0,e=100,f=PI,g,k=new Color,h=new Color,m=new Color(1,1,1,0),n=new Color(1,1,1,0),l=.5,p=.1,q=1,r=.1,x=.05,v=1,D=1,z=0,E=PI,A=.1,w=.2,t=!1,F=!1,C=!0,y=F?1e9:0,G=!1){super(a,vec2(),g,b,void 0,y);this.emitSize=c;this.emitTime=d;this.emitRate=e;this.emitConeAngle=f;this.colorStartA=k;this.colorStartB=h;this.colorEndA=m;this.colorEndB=n;this.randomColorLinear=C;this.particleTime=l;this.sizeStart=p;this.sizeEnd=q;this.speed=r;this.angleSpeed=x;this.damping=v;this.angleDamping=D;this.gravityScale=z;this.particleConeAngle=E;this.fadeRate=A;this.randomness=w;this.collideTiles=t;this.additive=F;this.localSpace=G;this.trailScale=0;this.particleCreateCallback=this.particleDestroyCallback=void 0;this.emitTimeBuffer=0}update(){this.parent&&super.update();if(!this.emitTime||this.getAliveTime()<=this.emitTime){if(this.emitRate*particleEmitRateScale){const a=1/this.emitRate/particleEmitRateScale;for(this.emitTimeBuffer+=timeDelta;0<this.emitTimeBuffer;this.emitTimeBuffer-=a)this.emitParticle()}}else this.destroy();debugParticles&&debugRect(this.pos,vec2(this.emitSize),"#0f0",0,this.angle)}emitParticle(){var a="number"===typeof this.emitSize?randInCircle(this.emitSize/2):vec2(rand(-.5,.5),rand(-.5,.5)).multiply(this.emitSize).rotate(this.angle);let b=rand(this.particleConeAngle,-this.particleConeAngle);this.localSpace||(a=this.pos.add(a),b+=this.angle);const c=this.randomness;var d=l=>l+l*rand(c,-c);const e=d(this.particleTime),f=d(this.sizeStart),g=d(this.sizeEnd),k=d(this.speed);d=d(this.angleSpeed)*randSign();var h=rand(this.emitConeAngle,-this.emitConeAngle);const m=randColor(this.colorStartA,this.colorStartB,this.randomColorLinear),n=randColor(this.colorEndA,this.colorEndB,this.randomColorLinear);h=this.localSpace?h:this.angle+h;a=new Particle(a,this.tileInfo,b,m,n,e,f,g,this.fadeRate,this.additive,this.trailScale,this.localSpace&&this,this.particleDestroyCallback);a.velocity=vec2().setAngle(h,k);a.angleVelocity=d;a.fadeRate=this.fadeRate;a.damping=this.damping;a.angleDamping=this.angleDamping;a.elasticity=this.elasticity;a.friction=this.friction;a.gravityScale=this.gravityScale;a.collideTiles=this.collideTiles;a.renderOrder=this.renderOrder;a.mirror=!!randInt(2);this.particleCreateCallback&&this.particleCreateCallback(a);return a}render(){}}class Particle extends EngineObject{constructor(a,b,c,d,e,f,g,k,h,m,n,l,p){super(a,vec2(),b,c);this.colorStart=d;this.colorEndDelta=e.subtract(d);this.lifeTime=f;this.sizeStart=g;this.sizeEndDelta=k-g;this.fadeRate=h;this.additive=m;this.trailScale=n;this.localSpaceEmitter=l;this.destroyCallback=p;this.clampSpeedLinear=!1}render(){const a=0<this.lifeTime?min((time-this.spawnTime)/this.lifeTime,1):1,b=vec2(this.sizeStart+a*this.sizeEndDelta);var c=this.fadeRate/2;c=new Color(this.colorStart.r+a*this.colorEndDelta.r,this.colorStart.g+a*this.colorEndDelta.g,this.colorStart.b+a*this.colorEndDelta.b,(this.colorStart.a+a*this.colorEndDelta.a)*(a<c?a/c:a>1-c?(1-a)/c:1));this.additive&&setBlendMode(!0);let d=this.pos,e=this.angle;this.localSpaceEmitter&&(d=this.localSpaceEmitter.pos.add(d.rotate(-this.localSpaceEmitter.angle)),e+=this.localSpaceEmitter.angle);if(this.trailScale){var f=this.velocity;this.localSpaceEmitter&&(f=f.rotate(-this.localSpaceEmitter.angle));var g=f.length();g&&(f=f.scale(1/g),g*=this.trailScale,b.y=max(b.x,g),e=f.angle(),drawTile(d.add(f.multiply(vec2(0,-g/2))),b,this.tileInfo,c,e,this.mirror))}else drawTile(d,b,this.tileInfo,c,e,this.mirror);this.additive&&setBlendMode();debugParticles&&debugRect(d,b,"#f005",0,e);1==a&&(this.color=c,this.size=b,this.destroyCallback&&this.destroyCallback(this),this.destroyed=1)}}const medals={};let medalsDisplayQueue=[],medalsSaveName,medalsDisplayTimeLast;function medalsInit(a){medalsSaveName=a;debugMedals||medalsForEach(b=>b.unlocked=!!localStorage[b.storageKey()]);engineAddPlugin(void 0,function(){if(medalsDisplayQueue.length){var b=medalsDisplayQueue[0],c=timeReal-medalsDisplayTimeLast;if(medalsDisplayTimeLast)if(c>medalDisplayTime)medalsDisplayTimeLast=0,medalsDisplayQueue.shift();else{const d=medalDisplayTime-medalDisplaySlideTime;b.render(c<medalDisplaySlideTime?1-c/medalDisplaySlideTime:c>d?(c-d)/medalDisplaySlideTime:0)}else medalsDisplayTimeLast=timeReal}})}function medalsForEach(a){Object.values(medals).forEach(b=>a(b))}class Medal{constructor(a,b,c="",d="🏆",e){ASSERT(0<=a&&!medals[a]);this.id=a;this.name=b;this.description=c;this.icon=d;this.unlocked=!1;e&&((this.image=new Image).src=e);medals[a]=this}unlock(){medalsPreventUnlock||this.unlocked||(ASSERT(medalsSaveName,"save name must be set"),localStorage[this.storageKey()]=this.unlocked=!0,medalsDisplayQueue.push(this))}render(a=0){const b=overlayContext;var c=min(medalDisplaySize.x,mainCanvas.width);const d=overlayCanvas.width-c;a*=-medalDisplaySize.y;b.save();b.beginPath();b.fillStyle=new Color(.9,.9,.9).toString();b.strokeStyle=new Color(0,0,0).toString();b.lineWidth=3;b.rect(d,a,c,medalDisplaySize.y);b.fill();b.stroke();b.clip();this.renderIcon(vec2(d+15+medalDisplayIconSize/2,a+medalDisplaySize.y/2));c=vec2(d+medalDisplayIconSize+30,a+28);drawTextScreen(this.name,c,38,new Color(0,0,0),0,void 0,"left");c.y+=32;drawTextScreen(this.description,c,24,new Color(0,0,0),0,void 0,"left");b.restore()}renderIcon(a,b=medalDisplayIconSize){this.image?overlayContext.drawImage(this.image,a.x-b/2,a.y-b/2,b,b):drawTextScreen(this.icon,a,.7*b,new Color(0,0,0))}storageKey(){return medalsSaveName+"_"+this.id}}let glCanvas,glContext,glAntialias=!0,glShader,glActiveTexture,glArrayBuffer,glGeometryBuffer,glPositionData,glColorData,glInstanceCount,glAdditive,glBatchAdditive;const gl_MAX_INSTANCES=1e4,gl_INDICES_PER_INSTANCE=11,gl_INSTANCE_BYTE_STRIDE=4*gl_INDICES_PER_INSTANCE,gl_INSTANCE_BUFFER_SIZE=gl_MAX_INSTANCES*gl_INSTANCE_BYTE_STRIDE;function glInit(){if(glEnable&&!headlessMode){glCanvas=document.createElement("canvas");glContext=glCanvas.getContext("webgl2",{antialias:glAntialias});var a=mainCanvas.parentElement;glOverlay&&a.appendChild(glCanvas);glShader=glCreateProgram("#version 300 es\nprecision highp float;uniform mat4 m;in vec2 g;in vec4 p,u,c,a;in float r;out vec2 v;out vec4 d,e;void main(){vec2 s=(g-.5)*p.zw;gl_Position=m*vec4(p.xy+s*cos(r)-vec2(-s.y,s)*sin(r),1,1);v=mix(u.xw,u.zy,g);d=c;e=a;}","#version 300 es\nprecision highp float;uniform sampler2D s;in vec2 v;in vec4 d,e;out vec4 c;void main(){c=texture(s,v)*d+e;}");a=new ArrayBuffer(gl_INSTANCE_BUFFER_SIZE);glPositionData=new Float32Array(a);glColorData=new Uint32Array(a);glArrayBuffer=glContext.createBuffer();glGeometryBuffer=glContext.createBuffer();a=new Float32Array([glInstanceCount=0,0,1,0,0,1,1,1]);glContext.bindBuffer(glContext.ARRAY_BUFFER,glGeometryBuffer);glContext.bufferData(glContext.ARRAY_BUFFER,a,glContext.STATIC_DRAW)}}function glPreRender(){if(glEnable&&!headlessMode){glClearCanvas();glContext.useProgram(glShader);glContext.activeTexture(glContext.TEXTURE0);textureInfos[0]&&glContext.bindTexture(glContext.TEXTURE_2D,glActiveTexture=textureInfos[0].glTexture);var a=glAdditive=glBatchAdditive=0,b=(d,e,f,g)=>{d=glContext.getAttribLocation(glShader,d);const k=f&&gl_INSTANCE_BYTE_STRIDE,h=f&&1,m=1==f;glContext.enableVertexAttribArray(d);glContext.vertexAttribPointer(d,g,e,m,k,a);glContext.vertexAttribDivisor(d,h);a+=g*f};glContext.bindBuffer(glContext.ARRAY_BUFFER,glGeometryBuffer);b("g",glContext.FLOAT,0,2);glContext.bindBuffer(glContext.ARRAY_BUFFER,glArrayBuffer);glContext.bufferData(glContext.ARRAY_BUFFER,gl_INSTANCE_BUFFER_SIZE,glContext.DYNAMIC_DRAW);b("p",glContext.FLOAT,4,4);b("u",glContext.FLOAT,4,4);b("c",glContext.UNSIGNED_BYTE,1,4);b("a",glContext.UNSIGNED_BYTE,1,4);b("r",glContext.FLOAT,4,1);b=vec2(2*cameraScale).divide(mainCanvasSize);var c=vec2(-1).subtract(cameraPos.multiply(b));glContext.uniformMatrix4fv(glContext.getUniformLocation(glShader,"m"),!1,[b.x,0,0,0,0,b.y,0,0,1,1,1,1,c.x,c.y,0,0])}}function glClearCanvas(){glContext.viewport(0,0,glCanvas.width=mainCanvas.width,glCanvas.height=mainCanvas.height);glContext.clear(glContext.COLOR_BUFFER_BIT)}function glSetTexture(a){headlessMode||a==glActiveTexture||(glFlush(),glContext.bindTexture(glContext.TEXTURE_2D,glActiveTexture=a))}function glCompileShader(a,b){b=glContext.createShader(b);glContext.shaderSource(b,a);glContext.compileShader(b);if(debug&&!glContext.getShaderParameter(b,glContext.COMPILE_STATUS))throw glContext.getShaderInfoLog(b);return b}function glCreateProgram(a,b){const c=glContext.createProgram();glContext.attachShader(c,glCompileShader(a,glContext.VERTEX_SHADER));glContext.attachShader(c,glCompileShader(b,glContext.FRAGMENT_SHADER));glContext.linkProgram(c);if(debug&&!glContext.getProgramParameter(c,glContext.LINK_STATUS))throw glContext.getProgramInfoLog(c);return c}function glCreateTexture(a){const b=glContext.createTexture();glContext.bindTexture(glContext.TEXTURE_2D,b);a&&a.width?glContext.texImage2D(glContext.TEXTURE_2D,0,glContext.RGBA,glContext.RGBA,glContext.UNSIGNED_BYTE,a):(a=new Uint8Array([255,255,255,255]),glContext.texImage2D(glContext.TEXTURE_2D,0,glContext.RGBA,1,1,0,glContext.RGBA,glContext.UNSIGNED_BYTE,a));a=tilesPixelated?glContext.NEAREST:glContext.LINEAR;glContext.texParameteri(glContext.TEXTURE_2D,glContext.TEXTURE_MIN_FILTER,a);glContext.texParameteri(glContext.TEXTURE_2D,glContext.TEXTURE_MAG_FILTER,a);return b}function glFlush(){if(glInstanceCount){var a=glBatchAdditive?glContext.ONE:glContext.ONE_MINUS_SRC_ALPHA;glContext.blendFuncSeparate(glContext.SRC_ALPHA,a,glContext.ONE,a);glContext.enable(glContext.BLEND);glContext.bufferSubData(glContext.ARRAY_BUFFER,0,glPositionData);glContext.drawArraysInstanced(glContext.TRIANGLE_STRIP,0,4,glInstanceCount);showWatermark&&(drawCount+=glInstanceCount);glInstanceCount=0;glBatchAdditive=glAdditive}}function glCopyToContext(a,b=!1){glEnable&&(glInstanceCount||b)&&(glFlush(),glOverlay&&!b||a.drawImage(glCanvas,0,0))}function glSetAntialias(a=!0){ASSERT(!glCanvas,"must be called before engineInit");glAntialias=a}function glDraw(a,b,c,d,e,f,g,k,h,m,n=0){ASSERT("number"==typeof m&&"number"==typeof n,"invalid color");(glInstanceCount>=gl_MAX_INSTANCES||glBatchAdditive!=glAdditive)&&glFlush();let l=glInstanceCount++*gl_INDICES_PER_INSTANCE;glPositionData[l++]=a;glPositionData[l++]=b;glPositionData[l++]=c;glPositionData[l++]=d;glPositionData[l++]=f;glPositionData[l++]=g;glPositionData[l++]=k;glPositionData[l++]=h;glColorData[l++]=m;glColorData[l++]=n;glPositionData[l++]=e}const engineName="LittleJS",engineVersion="1.11.5",frameRate=60,timeDelta=1/frameRate;let engineObjects=[],engineObjectsCollide=[],frame=0,time=0,timeReal=0,paused=!1;function setPaused(a){paused=a}let frameTimeLastMS=0,frameTimeBufferMS=0,averageFPS=0;const pluginUpdateList=[],pluginRenderList=[];function engineAddPlugin(a,b){ASSERT(!pluginUpdateList.includes(a));ASSERT(!pluginRenderList.includes(b));a&&pluginUpdateList.push(a);b&&pluginRenderList.push(b)}function engineInit(a,b,c,d,e,f=[],g=document.body){function k(n=0){var l=n-frameTimeLastMS;frameTimeLastMS=n;if(debug||showWatermark)averageFPS=lerp(.05,averageFPS,1e3/(l||1));n=debug&&keyIsDown("Equal");const p=debug&&keyIsDown("Minus");debug&&(l*=n?5:p?.2:1);timeReal+=l/1e3;frameTimeBufferMS+=paused?0:l;n||(frameTimeBufferMS=min(frameTimeBufferMS,50));h();if(paused){for(const r of engineObjects)r.parent||r.updateTransforms();inputUpdate();pluginUpdateList.forEach(r=>r());debugUpdate();c();inputUpdatePost()}else{l=0;0>frameTimeBufferMS&&-9<frameTimeBufferMS&&(l=frameTimeBufferMS,frameTimeBufferMS=0);for(;0<=frameTimeBufferMS;frameTimeBufferMS-=1e3/frameRate)time=frame++/frameRate,inputUpdate(),b(),pluginUpdateList.forEach(r=>r()),engineObjectsUpdate(),debugUpdate(),c(),inputUpdatePost();frameTimeBufferMS+=l}if(!headlessMode){mainCanvasSize=vec2(mainCanvas.width,mainCanvas.height);overlayContext.imageSmoothingEnabled=mainContext.imageSmoothingEnabled=!tilesPixelated;glPreRender();d();engineObjects.sort((r,x)=>r.renderOrder-x.renderOrder);for(var q of engineObjects)q.destroyed||q.render();e();pluginRenderList.forEach(r=>r());touchGamepadRender();debugRender();glCopyToContext(mainContext);showWatermark&&(overlayContext.textAlign="right",overlayContext.textBaseline="top",overlayContext.font="1em monospace",overlayContext.fillStyle="#000",q=engineName+" v"+engineVersion+" / "+drawCount+" / "+engineObjects.length+" / "+averageFPS.toFixed(1)+(glEnable?" GL":" 2D"),overlayContext.fillText(q,mainCanvas.width-3,3),overlayContext.fillStyle="#fff",overlayContext.fillText(q,mainCanvas.width-2,2),drawCount=0)}requestAnimationFrame(k)}function h(){if(!headlessMode){if(canvasFixedSize.x){mainCanvas.width=canvasFixedSize.x;mainCanvas.height=canvasFixedSize.y;const n=innerWidth/innerHeight,l=mainCanvas.width/mainCanvas.height;(glCanvas||mainCanvas).style.width=mainCanvas.style.width=overlayCanvas.style.width=n<l?"100%":"";(glCanvas||mainCanvas).style.height=mainCanvas.style.height=overlayCanvas.style.height=n<l?"":"100%"}else mainCanvas.width=min(innerWidth,canvasMaxSize.x),mainCanvas.height=min(innerHeight,canvasMaxSize.y);overlayCanvas.width=mainCanvas.width;overlayCanvas.height=mainCanvas.height;mainCanvasSize=vec2(mainCanvas.width,mainCanvas.height)}}function m(){new Promise(n=>n(a())).then(k)}ASSERT(!mainContext,"engine already initialized");ASSERT(Array.isArray(f),"pass in images as array");a||=()=>{};b||=()=>{};c||=()=>{};d||=()=>{};e||=()=>{};headlessMode?m():(g.style.cssText="margin:0;overflow:hidden;width:100vw;height:100vh;display:flex;align-items:center;justify-content:center;background:#000;"+(canvasPixelated?"image-rendering:pixelated;":"")+"user-select:none;-webkit-user-select:none;"+(touchInputEnable?"touch-action:none;-webkit-touch-callout:none":""),g.appendChild(mainCanvas=document.createElement("canvas")),mainContext=mainCanvas.getContext("2d"),inputInit(),audioInit(),debugInit(),glInit(),g.appendChild(overlayCanvas=document.createElement("canvas")),overlayContext=overlayCanvas.getContext("2d"),mainCanvas.style.cssText=overlayCanvas.style.cssText="position:absolute",glCanvas&&(glCanvas.style.cssText="position:absolute"),h(),g=f.map((n,l)=>new Promise(p=>{const q=new Image;q.crossOrigin="anonymous";q.onerror=q.onload=()=>{textureInfos[l]=new TextureInfo(q);p()};q.src=n})),f.length||g.push(new Promise(n=>{textureInfos[0]=new TextureInfo(new Image);n()})),showSplashScreen&&g.push(new Promise(n=>{function l(){clearInput();drawEngineSplashScreen(p+=.01);1<p?n():setTimeout(l,16)}let p=0;console.log(`${engineName} Engine v${engineVersion}`);l()})),Promise.all(g).then(m))}function engineObjectsUpdate(){function a(b){if(!b.destroyed){b.update();for(const c of b.children)a(c)}}engineObjectsCollide=engineObjects.filter(b=>b.collideSolidObjects);for(const b of engineObjects)b.parent||(a(b),b.updateTransforms());engineObjects=engineObjects.filter(b=>!b.destroyed)}function engineObjectsDestroy(){for(const a of engineObjects)a.parent||a.destroy();engineObjects=engineObjects.filter(a=>!a.destroyed)}function engineObjectsCollect(a,b,c=engineObjects){const d=[];if(a)if(b instanceof Vector2)for(const e of c)isOverlapping(a,b,e.pos,e.size)&&d.push(e);else{b*=b;for(const e of c)a.distanceSquared(e.pos)<b&&d.push(e)}else for(const e of c)d.push(e);return d}function engineObjectsCallback(a,b,c,d=engineObjects){engineObjectsCollect(a,b,d).forEach(e=>c(e))}function engineObjectsRaycast(a,b,c=engineObjects){const d=[];for(const e of c)e.collideRaycast&&isIntersecting(a,b,e.pos,e.size)&&(debugRaycast&&debugRect(e.pos,e.size,"#f00"),d.push(e));debugRaycast&&debugLine(a,b,d.length?"#f00":"#00f",.02);return d}function drawEngineSplashScreen(a){const b=overlayContext;var c=overlayCanvas.width=innerWidth,d=overlayCanvas.height=innerHeight,e=percent(a,1,.8),f=percent(a,0,.5),g=b.createRadialGradient(c/2,d/2,0,c/2,d/2,.7*Math.hypot(c,d));g.addColorStop(0,hsl(0,0,lerp(f,0,e/2),e).toString());g.addColorStop(1,hsl(0,0,0,e).toString());b.save();b.fillStyle=g;b.fillRect(0,0,c,d);g=(h,m,n,l,p)=>{b.beginPath();b.rect(h,m,n,p?l*k:l);(b.fillStyle=p)?b.fill():b.stroke()};f=(h,m,n,l=0,p=2*PI,q,r)=>{const x=(l+p)/2;l=k*(p-l)/2;b.beginPath();r&&b.lineTo(h,m);b.arc(h,m,n,x-l,x+l);(b.fillStyle=q)?b.fill():b.stroke()};e=(h=0,m=0)=>hsl([.98,.3,.57,.14][h%4]-10,.8,[0,.3,.5,.8,.9][m]).toString();a=wave(1,1,a);const k=percent(a,.1,.5);b.translate(c/2,d/2);c=min(6,min(c,d)/99);b.scale(c,c);b.translate(-40,-35);b.lineJoin=b.lineCap="round";b.lineWidth=.1+1.9*k;c=percent(a,.1,1);b.setLineDash([99*c,99]);g(7,16,18,-8,e(2,2));g(7,8,18,4,e(2,3));g(25,8,8,8,e(2,1));g(25,8,-18,8);g(25,8,8,8);g(25,16,7,23,e());g(11,39,14,-23,e(1,1));g(11,16,14,18,e(1,2));g(11,16,14,8,e(1,3));g(25,16,-14,24);g(15,29,6,-9,e(2,2));f(15,21,5,0,PI/2,e(2,4),1);g(21,21,-6,9);g(37,14,9,6,e(3,2));g(37,14,4.5,6,e(3,3));g(37,14,9,6);g(50,20,10,-8,e(0,1));g(50,20,6.5,-8,e(0,2));g(50,20,3.5,-8,e(0,3));g(50,20,10,-8);f(55,2,11.4,.5,PI-.5,e(3,3));f(55,2,11.4,.5,PI/2,e(3,2),1);f(55,2,11.4,.5,PI-.5);g(45,7,20,-7,e(0,2));g(45,-1,20,4,e(0,3));g(45,-1,20,8);for(c=5;c--;)f(60-6*c,30,9.9,0,2*PI,e(c+2,3)),f(60-6*c,30,10,-.5,PI+.5,e(c+2,2)),f(60-6*c,30,10.1,.5,PI-.5,e(c+2,1));f(36,30,10,PI/2,3*PI/2);f(48,30,10,PI/2,3*PI/2);f(60,30,10);b.beginPath();b.lineTo(36,20);b.lineTo(60,20);b.stroke();f(60,30,4,PI,3*PI,e(3,2));f(60,30,4,PI,2*PI,e(3,3));f(60,30,4,PI,3*PI);for(c=6;c--;)b.beginPath(),b.lineTo(53,54),b.lineTo(53,40),b.lineTo(53+(1+2.9*c)*k,40),b.lineTo(53+(4+3.5*c)*k,54),b.fillStyle=e(0,c%2+2),b.fill(),c%2&&b.stroke();g(6,40,5,5);g(6,40,5,5,e());g(15,54,38,-14,e());for(g=3;g--;)for(c=2;c--;)f(15*g+15,47,c?7:1,PI,3*PI,e(g,3)),b.stroke(),f(15*g+15,47,c?7:1,0,PI,e(g,2)),b.stroke();b.beginPath();b.lineTo(6,40);b.lineTo(68,40);b.stroke();b.beginPath();b.lineTo(77,54);b.lineTo(4,54);b.stroke();f=engineName;b.font="900 16px arial";b.textAlign="center";b.textBaseline="top";b.lineWidth=.1+3.9*k;g=0;for(c=0;c<f.length;++c)g+=b.measureText(f[c]).width;for(c=2;c--;)for(let h=0,m=41-g/2;h<f.length;++h)b.fillStyle=e(h,2),d=b.measureText(f[h]).width,b[c?"strokeText":"fillText"](f[h],m+d/2,55.5,17*k),m+=d;b.restore()}
@@ -1,4 +1,5 @@
1
- // LittleJS - MIT License - Copyright 2021 Frank Force
1
+ // LittleJS Engine - MIT License - Copyright 2021 Frank Force
2
+ // https://github.com/KilledByAPixel/LittleJS
2
3
 
3
4
  'use strict';
4
5
 
@@ -3873,6 +3874,10 @@ class TileLayer extends EngineObject
3873
3874
 
3874
3875
  // draw the entire cached level onto the canvas
3875
3876
  const pos = worldToScreen(this.pos.add(vec2(0,this.size.y*this.scale.y)));
3877
+
3878
+ // fix canvas jitter in some browsers if position is not an integer
3879
+ pos.x |= 0; pos.y |= 0;
3880
+
3876
3881
  (this.isOverlay ? overlayContext : mainContext).drawImage
3877
3882
  (
3878
3883
  this.canvas, pos.x, pos.y,
@@ -4301,7 +4306,7 @@ class Particle extends EngineObject
4301
4306
  render()
4302
4307
  {
4303
4308
  // modulate size and color
4304
- const p = min((time - this.spawnTime) / this.lifeTime, 1);
4309
+ const p = this.lifeTime > 0 ? min((time - this.spawnTime) / this.lifeTime, 1) : 1;
4305
4310
  const radius = this.sizeStart + p * this.sizeEndDelta;
4306
4311
  const size = vec2(radius);
4307
4312
  const fadeRate = this.fadeRate/2;
@@ -4864,7 +4869,7 @@ const engineName = 'LittleJS';
4864
4869
  * @type {String}
4865
4870
  * @default
4866
4871
  * @memberof Engine */
4867
- const engineVersion = '1.11.4';
4872
+ const engineVersion = '1.11.5';
4868
4873
 
4869
4874
  /** Frames per second to update
4870
4875
  * @type {Number}
@@ -4950,6 +4955,18 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
4950
4955
  ASSERT(!mainContext, 'engine already initialized');
4951
4956
  ASSERT(Array.isArray(imageSources), 'pass in images as array');
4952
4957
 
4958
+ // allow passing in empty functions
4959
+ if (!gameInit)
4960
+ gameInit = ()=>{};
4961
+ if (!gameUpdate)
4962
+ gameUpdate = ()=>{};
4963
+ if (!gameUpdatePost)
4964
+ gameUpdatePost = ()=>{};
4965
+ if (!gameRender)
4966
+ gameRender = ()=>{};
4967
+ if (!gameRenderPost)
4968
+ gameRenderPost = ()=>{};
4969
+
4953
4970
  // Called automatically by engine to setup render system
4954
4971
  function enginePreRender()
4955
4972
  {
@@ -24,7 +24,7 @@ function gameInit()
24
24
  canvasFixedSize = vec2(1280, 720); // 720p
25
25
  levelSize = vec2(38, 20);
26
26
  cameraPos = levelSize.scale(.5);
27
- paddle = new Paddle(vec2(levelSize.x/2-12,1));
27
+ paddle = new Paddle(vec2(levelSize.x/2-12, 1));
28
28
  score = brickCount = 0;
29
29
 
30
30
  // spawn bricks
@@ -5,27 +5,31 @@
5
5
  'use strict';
6
6
 
7
7
  ///////////////////////////////////////////////////////////////////////////////
8
- class Wall extends EngineObject
8
+ class PhysicsObject extends EngineObject
9
9
  {
10
- constructor(pos, size)
10
+ constructor(pos, size, tileInfo, angle, color)
11
11
  {
12
- super(pos, size);
13
-
14
- this.mass = 0; // make object have static physics
12
+ super(pos, size, tileInfo, angle, color);
15
13
  this.setCollision(); // make object collide
16
- this.color = hsl(0,0,0,0); // make object invisible
14
+ this.mass = 0; // make object have static physics
15
+ }
16
+ }
17
+
18
+ ///////////////////////////////////////////////////////////////////////////////
19
+ class Wall extends PhysicsObject
20
+ {
21
+ constructor(pos, size)
22
+ {
23
+ super(pos, size, 0, 0, new Color(0,0,0,0));
17
24
  }
18
25
  }
19
26
 
20
27
  ///////////////////////////////////////////////////////////////////////////////
21
- class Paddle extends EngineObject
28
+ class Paddle extends PhysicsObject
22
29
  {
23
30
  constructor(pos)
24
31
  {
25
32
  super(pos, vec2(5,.5));
26
-
27
- this.mass = 0; // make object have static physics
28
- this.setCollision(); // make object collide
29
33
  }
30
34
 
31
35
  update()
@@ -39,15 +43,12 @@ class Paddle extends EngineObject
39
43
  }
40
44
 
41
45
  ///////////////////////////////////////////////////////////////////////////////
42
- class Brick extends EngineObject
46
+ class Brick extends PhysicsObject
43
47
  {
44
48
  constructor(pos)
45
49
  {
46
50
  super(pos, vec2(2,1), tile(1, vec2(32,16)), 0, randColor());
47
51
  ++brickCount;
48
-
49
- this.mass = 0; // make object have static physics
50
- this.setCollision(); // make object collide
51
52
  }
52
53
 
53
54
  collideWithObject(o)
@@ -84,16 +85,16 @@ class Brick extends EngineObject
84
85
  }
85
86
 
86
87
  ///////////////////////////////////////////////////////////////////////////////
87
- class Ball extends EngineObject
88
+ class Ball extends PhysicsObject
88
89
  {
89
90
  constructor(pos)
90
91
  {
91
92
  super(pos, vec2(.5), tile(0));
92
93
 
93
94
  // make a bouncy ball
94
- this.setCollision();
95
95
  this.velocity = vec2(0, -.1);
96
96
  this.elasticity = 1;
97
+ this.mass = 1;
97
98
 
98
99
  // attach a trail effect
99
100
  const color = hsl(0,0,.2);
@@ -127,24 +128,22 @@ class Ball extends EngineObject
127
128
  {
128
129
  // prevent colliding with paddle if moving upwards
129
130
  if (o == paddle && this.velocity.y > 0)
130
- return 0;
131
-
132
- // speed up
133
- const speed = min(1.04*this.velocity.length(), .5);
134
- this.velocity = this.velocity.normalize(speed);
135
-
136
- // scale bounce sound pitch by speed
137
- sound_bounce.play(this.pos, 1, speed*2);
131
+ return false;
138
132
 
139
133
  if (o == paddle)
140
134
  {
141
135
  // put english on the ball when it collides with paddle
142
136
  this.velocity = this.velocity.rotate(.2 * (this.pos.x - o.pos.x));
143
137
  this.velocity.y = max(-this.velocity.y, .2);
144
- return 0;
138
+
139
+ // speed up
140
+ const speed = min(1.04*this.velocity.length(), .5);
141
+ this.velocity = this.velocity.normalize(speed);
142
+ sound_bounce.play(this.pos, 1, speed*2);
143
+
144
+ return false; // prevent default collision code
145
145
  }
146
146
 
147
- // prevent default collision with paddle
148
- return 1;
147
+ return true;
149
148
  }
150
149
  }
@@ -7,6 +7,6 @@
7
7
  </head><body>
8
8
 
9
9
  <script src=../../dist/littlejs.js?1105></script>
10
- <script src=../../plugins/postProcess.js></script>
10
+ <script src=../../plugins/postProcess.js?1105></script>
11
11
  <script src=gameObjects.js?1105></script>
12
12
  <script src=game.js?1105></script>
@@ -37,7 +37,7 @@ const tileColors =
37
37
  rgb(1,1,0),
38
38
  rgb(0,1,0),
39
39
  rgb(0,.6,1),
40
- rgb(.6,0,1),
40
+ rgb(.8,0,1),
41
41
  rgb(.5,.5,.5),
42
42
  ];
43
43
  const tileTypeCount = tileColors.length;
@@ -72,7 +72,7 @@ function gameInit()
72
72
  hsl(0,0,0,0), hsl(0,0,0,0), // colorEndA, colorEndB
73
73
  2, .2, .2, .1, .05, // time, sizeStart, sizeEnd, speed, angleSpeed
74
74
  .99, 1, 1, PI, // damping, angleDamping, gravityScale, cone
75
- .05, .5, true, true // fadeRate, randomness, collide, additive
75
+ .05, .8, true, true // fadeRate, randomness, collide, additive
76
76
  );
77
77
  particleEmitter.elasticity = .3; // bounce when it collides
78
78
  particleEmitter.trailScale = 2; // stretch in direction of motion
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "littlejsengine",
3
- "version": "1.11.4",
3
+ "version": "1.11.5",
4
4
  "description": "LittleJS - Tiny and Fast HTML5 Game Engine",
5
5
  "main": "dist/littlejs.esm.js",
6
6
  "types": "dist/littlejs.d.ts",
@@ -76,26 +76,26 @@ function initPostProcess(shaderCode, includeOverlay=false)
76
76
 
77
77
  // setup shader program to draw one triangle
78
78
  glContext.useProgram(glPostShader);
79
- glContext.bindBuffer(gl_ARRAY_BUFFER, glGeometryBuffer);
80
- glContext.pixelStorei(gl_UNPACK_FLIP_Y_WEBGL, 1);
81
- glContext.disable(gl_BLEND);
79
+ glContext.bindBuffer(glContext.ARRAY_BUFFER, glGeometryBuffer);
80
+ glContext.pixelStorei(glContext.UNPACK_FLIP_Y_WEBGL, 1);
81
+ glContext.disable(glContext.BLEND);
82
82
 
83
83
  // set textures, pass in the 2d canvas and gl canvas in separate texture channels
84
- glContext.activeTexture(gl_TEXTURE0);
85
- glContext.bindTexture(gl_TEXTURE_2D, glPostTexture);
86
- glContext.texImage2D(gl_TEXTURE_2D, 0, gl_RGBA, gl_RGBA, gl_UNSIGNED_BYTE, mainCanvas);
84
+ glContext.activeTexture(glContext.TEXTURE0);
85
+ glContext.bindTexture(glContext.TEXTURE_2D, glPostTexture);
86
+ glContext.texImage2D(glContext.TEXTURE_2D, 0, glContext.RGBA, glContext.RGBA, glContext.UNSIGNED_BYTE, mainCanvas);
87
87
 
88
88
  // set vertex position attribute
89
89
  const vertexByteStride = 8;
90
90
  const pLocation = glContext.getAttribLocation(glPostShader, 'p');
91
91
  glContext.enableVertexAttribArray(pLocation);
92
- glContext.vertexAttribPointer(pLocation, 2, gl_FLOAT, false, vertexByteStride, 0);
92
+ glContext.vertexAttribPointer(pLocation, 2, glContext.FLOAT, false, vertexByteStride, 0);
93
93
 
94
94
  // set uniforms and draw
95
95
  const uniformLocation = (name)=>glContext.getUniformLocation(glPostShader, name);
96
96
  glContext.uniform1i(uniformLocation('iChannel0'), 0);
97
97
  glContext.uniform1f(uniformLocation('iTime'), time);
98
98
  glContext.uniform3f(uniformLocation('iResolution'), mainCanvas.width, mainCanvas.height, 1);
99
- glContext.drawArrays(gl_TRIANGLE_STRIP, 0, 4);
99
+ glContext.drawArrays(glContext.TRIANGLE_STRIP, 0, 4);
100
100
  }
101
101
  }
@@ -0,0 +1,36 @@
1
+ <!DOCTYPE html><head><meta charset=utf-8></head><body>
2
+
3
+ <!-- LittleJS Engine -->
4
+ <script src=../src/engineDebug.js?1105></script>
5
+ <script src=../src/engineUtilities.js?1105></script>
6
+ <script src=../src/engineSettings.js?1105></script>
7
+ <script src=../src/engineObject.js?1105></script>
8
+ <script src=../src/engineDraw.js?1105></script>
9
+ <script src=../src/engineInput.js?1105></script>
10
+ <script src=../src/engineAudio.js?1105></script>
11
+ <script src=../src/engineTileLayer.js?1105></script>
12
+ <script src=../src/engineParticles.js?1105></script>
13
+ <script src=../src/engineMedals.js?1105></script>
14
+ <script src=../src/engineWebGL.js?1105></script>
15
+ <script src=../src/engine.js?1105></script>\
16
+ <script>
17
+
18
+ 'use strict';
19
+
20
+ // use fixed size canvas for examples
21
+ canvasFixedSize = vec2(960, 540);
22
+
23
+ // allow improved scaling
24
+ canvasPixelated = false;
25
+
26
+ // disable watermark
27
+ showWatermark = false;
28
+
29
+ // these functions can be overridden for each example
30
+ function gameInit() {}
31
+ function gameUpdate() {}
32
+ function gameUpdatePost() {}
33
+ function gameRender() {}
34
+ function gameRenderPost() {}
35
+
36
+ </script>
@@ -0,0 +1,12 @@
1
+ function gameRender()
2
+ {
3
+ // additive blend
4
+ setBlendMode(1);
5
+ drawTile(vec2(1, 3), vec2(6), tile(0), rgb(1,0,0));
6
+ drawTile(vec2(-1,3), vec2(6), tile(0), rgb(0,0,1));
7
+
8
+ // alpha blend
9
+ setBlendMode(0);
10
+ drawTile(vec2(1, -3), vec2(6), tile(0), rgb(1,0,0,.5));
11
+ drawTile(vec2(-1,-3), vec2(6), tile(0), rgb(0,0,1,.5));
12
+ }
@@ -0,0 +1,7 @@
1
+ function gameRender()
2
+ {
3
+ // draw to overlay canvas
4
+ drawTextScreen('Hello World',
5
+ mainCanvasSize.scale(.5), 80, // position, size
6
+ hsl(0,0,1), 6, hsl(0,0,0)); // color, outline size and color
7
+ }
@@ -0,0 +1,28 @@
1
+ function gameInit()
2
+ {
3
+ gravity = -.01; // set default gravity
4
+
5
+ // fire
6
+ new ParticleEmitter(
7
+ vec2(-5,-2), 0, // pos, angle
8
+ 2, 0, 200, PI, // emitSize, emitTime, emitRate, emitCone
9
+ tile(0), // tileInfo
10
+ rgb(1,.5,.1), rgb(1,.1,.1), // colorStartA, colorStartB
11
+ rgb(1,.5,.1,0), rgb(1,.1,.1,0), // colorEndA, colorEndB
12
+ .7, 2, 0, .2, .05, // time, sizeStart, sizeEnd, speed, angleSpeed
13
+ .9, 1, -1, PI, .05, // damp, angleDamp, gravity, particleCone, fade
14
+ .5, 0, 1, 0, 1e9 // randomness, collide, additive, colorLinear, renderOrder
15
+ );
16
+
17
+ // smoke
18
+ new ParticleEmitter(
19
+ vec2(5,-2), 0, // pos, angle
20
+ 3, 0, 100, PI, // emitSize, emitTime, emitRate, emitCone
21
+ tile(0), // tileInfo
22
+ hsl(0,0,0,.5), hsl(0,0,1,.5), // colorStartA, colorStartB
23
+ hsl(0,0,0,0), hsl(0,0,1,0), // colorEndA, colorEndB
24
+ 1, 1, 5, .2, .01, // time, sizeStart, sizeEnd, speed, angleSpeed
25
+ .85, 1, -1, PI, .3, // damp, angleDamp, gravity, particleCone, fade
26
+ .5, 0, 0, 1, 1e8 // randomness, collide, additive, colorLinear, renderOrder
27
+ );
28
+ }