littlejsengine 1.9.1 → 1.9.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -7
- package/{build → dist}/littlejs.d.ts +25 -20
- package/{build → dist}/littlejs.esm.js +160 -61
- package/dist/littlejs.esm.min.js +1 -0
- package/{build → dist}/littlejs.js +160 -61
- package/dist/littlejs.min.js +1 -0
- package/{build → dist}/littlejs.release.js +154 -59
- package/examples/breakout/game.js +2 -2
- package/examples/breakout/gameObjects.js +3 -3
- package/examples/breakout/index.html +3 -3
- package/examples/breakoutTutorial/index.html +2 -2
- package/examples/electron/build.js +1 -1
- package/examples/electron/game.js +49 -38
- package/examples/electron/index.html +2 -2
- package/examples/electron/tiles.png +0 -0
- package/examples/empty/index.html +1 -1
- package/examples/js13k/build.js +1 -1
- package/examples/js13k/game.js +10 -9
- package/examples/js13k/index.html +13 -13
- package/examples/js13k/tiles.png +0 -0
- package/examples/module/game.js +45 -41
- package/examples/module/index.html +1 -1
- package/examples/module/tiles.png +0 -0
- package/examples/particles/index.html +1 -1
- package/examples/platformer/data/gameTileData.tmx +143 -0
- package/examples/platformer/data/gameTileData.tsx +4 -0
- package/examples/platformer/game.js +0 -1
- package/examples/platformer/gameCharacter.js +1 -1
- package/examples/platformer/gameEffects.js +42 -110
- package/examples/platformer/gameLevel.js +149 -183
- package/examples/platformer/gameObjects.js +61 -16
- package/examples/platformer/gamePlayer.js +3 -2
- package/examples/platformer/gameTileData.js +181 -0
- package/examples/platformer/index.html +8 -7
- package/examples/platformer/tiles.png +0 -0
- package/examples/platformer/tilesLevel.png +0 -0
- package/examples/puzzle/game.js +12 -12
- package/examples/puzzle/index.html +2 -2
- package/examples/starter/build.js +1 -1
- package/examples/starter/game.js +6 -6
- package/examples/starter/index.html +13 -13
- package/examples/starter/tiles.png +0 -0
- package/examples/stress/index.html +1 -1
- package/examples/typescript/build.bat +4 -1
- package/examples/typescript/game.js +34 -31
- package/examples/typescript/game.ts +40 -36
- package/examples/typescript/index.html +1 -1
- package/examples/typescript/tiles.png +0 -0
- package/package.json +3 -3
- package/src/engine.js +1 -1
- package/src/engineAudio.js +4 -10
- package/src/engineBuild.js +9 -2
- package/src/engineDebug.js +6 -2
- package/src/engineInput.js +4 -4
- package/src/engineMedals.js +2 -2
- package/src/engineParticles.js +5 -5
- package/src/engineTileLayer.js +1 -1
- package/src/engineUtilities.js +136 -35
- package/src/engineWebGL.js +1 -1
- package/build/littlejs.esm.min.js +0 -1
- package/build/littlejs.min.js +0 -1
- package/index.d.ts +0 -2094
|
@@ -0,0 +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 debugCircle(){}function debugPoint(){}function debugLine(){}function debugAABB(){}function debugText(){}function debugClear(){}function debugSaveCanvas(){}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-b)):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 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%1e9)/1e9}int(a,b=0){return Math.floor(this.float(a,b))}sign(){return 2*this.int(2)-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}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){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)} )`}}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}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){b=.5>c?c*(1+b):c+b-c*b;c=2*c-b;const e=(f,g,k)=>(k=(k%1+1)%1)<1/6?f+6*(g-f)*k:.5>k?g:k<2/3?f+(g-f)*(2/3-k)*6: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;return this}getHSLA(){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 l=e-f;h=.5<g?l/(2-e-f):l/(e+f);a==e?k=(b-c)/l+(b<c?6:0):b==e?k=(c-a)/l+2:c==e&&(k=(a-b)/l+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*c|0)?"0":"")+c.toString(16);return"#"+b(this.r)+b(this.g)+b(this.b)+(a?b(this.a):"")}setHex(a){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=7<a.length?clamp(parseInt(a.slice(7,9),16)/255):1;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}}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,1200),canvasFixedSize=vec2(),canvasPixelated=!0,fontDefault="arial",showSplashScreen=!1,glEnable=!0,glOverlay=!0,tileSizeDefault=vec2(16),tileFixBleedScale=.3,enablePhysicsSolver=!0,objectDefaultMass=1,objectDefaultDamping=1,objectDefaultAngleDamping=1,objectDefaultElasticity=0,objectDefaultFriction=.8,objectMaxSpeed=1,gravity=0,particleEmitRateScale=1,gamepadsEnable=!0,gamepadDirectionEmulateStick=!0,inputWASDEmulateDirection=!0,touchGamepadEnable=!1,touchGamepadAnalog=!0,touchGamepadSize=99,touchGamepadAlpha=.3,vibrateEnable=!0,soundEnable=!0,soundVolume=.5,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 setFontDefault(a){fontDefault=a}function setShowSplashScreen(a){showSplashScreen=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 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}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,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.parent=void 0;this.localPos=vec2();this.localAngle=0;this.isSolid=this.collideSolidObjects=this.collideTiles=!1;engineObjects.push(this)}update(){var a=this.parent;if(a)this.pos=this.localPos.multiply(vec2(a.getMirrorSign(),1)).rotate(-a.angle).add(a.pos),this.angle=a.getMirrorSign()*this.localAngle+a.angle;else if(this.velocity.x=clamp(this.velocity.x,-objectMaxSpeed,objectMaxSpeed),this.velocity.y=clamp(this.velocity.y,-objectMaxSpeed,objectMaxSpeed),a=this.pos.copy(),this.velocity.y+=gravity*this.gravityScale,this.pos.x+=this.velocity.x*=this.damping,this.pos.y+=this.velocity.y*=this.damping,this.angle+=this.angleVelocity*=this.angleDamping,ASSERT(0<=this.angleDamping&&1>=this.angleDamping),ASSERT(0<=this.damping&&1>=this.damping),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&&debugAABB(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&&debugAABB(this.pos,this.size,d.pos,d.size,"#f0f")}if(this.collideTiles&&tileCollisionTest(this.pos,this.size,this)&&!tileCollisionTest(a,this.size,this)){c=tileCollisionTest(vec2(a.x,this.pos.y),this.size,this);d=tileCollisionTest(vec2(this.pos.x,a.y),this.size,this);if(c||!d)this.groundObject=b,this.velocity.y*=-this.elasticity,b=(a.y-this.size.y/2|0)-(a.y-this.size.y/2),0>b&&b>this.damping*this.velocity.y+gravity*this.gravityScale&&(this.velocity.y=this.damping?(b-gravity*this.gravityScale)/this.damping:0),this.pos.y=a.y;d&&(this.pos.x=a.x,this.velocity.x*=-this.elasticity)}}}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)}}collideWithTile(a,b){return 0<a}collideWithTileRaycast(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){ASSERT(a||!b,"solid objects must be set to collide");this.collideSolidObjects=a;this.isSolid=b;this.collideTiles=c}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}}}let mainCanvas,mainContext,overlayCanvas,overlayContext,mainCanvasSize=vec2(),textureInfos=[],drawCount;function tile(a=vec2(),b=tileSizeDefault,c=0){"number"===typeof b&&(ASSERT(0<b),b=vec2(b));if("number"===typeof a){var d=textureInfos[c];d?(d=d.size.x/b.x|0,a=vec2(a%d*b.x,(a/d|0)*b.y)):a=vec2()}return new TileInfo(a,b,c)}class TileInfo{constructor(a=vec2(),b=tileSizeDefault,c=0){this.pos=a;this.size=b;this.textureIndex=c}offset(a){return new TileInfo(this.pos.add(a),this.size,this.textureIndex)}getTextureInfo(){return textureInfos[this.textureIndex]}}class TextureInfo{constructor(a){this.image=a;this.size=vec2(a.width,a.height);this.glTexture=glEnable&&glCreateTexture(a);this.fixBleedSize=vec2(tileFixBleedScale).divide(this.size)}}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 drawTile(a,b=vec2(1),c,d=new Color,e=0,f,g=new Color(0,0,0,0),k=glEnable,h,l){ASSERT(!l||!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){k=c.pos.x/n.size.x;h=c.pos.y/n.size.y;l=c.size.x/n.size.x;const m=c.size.y/n.size.y,p=n.fixBleedSize;glSetTexture(n.glTexture);glDraw(a.x,a.y,f?-b.x:b.x,b.y,e,k+p.x,h+p.y,k-p.x+l,h-p.y+m,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,drawCanvas2D(a,b,e,f,m=>{if(n){const p=c.pos.x+tileFixBleedScale,t=c.pos.y+tileFixBleedScale,u=c.size.x-2*tileFixBleedScale,x=c.size.y-2*tileFixBleedScale;m.globalAlpha=d.a;m.drawImage(n.image,p,t,u,x,-.5,-.5,1,1);m.globalAlpha=1}else m.fillStyle=d,m.fillRect(-.5,-.5,1,1)},h,l)}function drawRect(a,b,c,d,e,f,g){drawTile(a,b,void 0,c,d,!1,void 0,e,f,g)}function drawPoly(a,b=new Color,c,d=mainContext){d.fillStyle=b.toString();d.beginPath();for(const e of c?a:a.map(worldToScreen))d.lineTo(e.x,e.y);d.fill()}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 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 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 drawText(a,b,c=1,d,e=0,f,g,k,h){drawTextScreen(a,worldToScreen(b),c*cameraScale,d,e*cameraScale,f,g,k,h)}function drawTextScreen(a,b,c=1,d=new Color,e=0,f=new Color(0,0,0),g="center",k=fontDefault,h=overlayContext){h.fillStyle=d.toString();h.lineWidth=e;h.strokeStyle=f.toString();h.textAlign=g;h.font=c+"px "+k;h.textBaseline="middle";h.lineJoin="round";b=b.copy();(a+"").split("\n").forEach(l=>{e&&h.strokeText(l,b.x,b.y);h.fillText(l,b.x,b.y);b.y+=c})}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();e.imageSmoothingEnabled=!canvasPixelated;const f=this.tileSize,g=f.add(this.paddingSize).scale(c),k=this.image.width/this.tileSize.x|0;(a+"").split("\n").forEach((h,l)=>{const n=d?h.length*f.x*c/2|0:0;for(let t=h.length;t--;){var m=h[t].charCodeAt(0);if(32>m||127<m)m=127;var p=m-32;m=p%k;p=p/k|0;const u=b.add(vec2(t,l).multiply(g));e.drawImage(this.image,m*f.x,p*f.y,f.x,f.y,u.x-n,u.y,f.x*c,f.y*c)}});e.restore()}}function isFullscreen(){return!!document.fullscreenElement}function toggleFullscreen(){isFullscreen()?document.exitFullscreen&&document.exitFullscreen():document.body.requestFullscreen&&document.body.requestFullscreen()}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=[[]]}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 stickData[b]?stickData[b][a]||vec2():vec2()}let inputData=[[]];function inputUpdate(){isTouchDevice||document.hasFocus()||clearInput();mousePos=screenToWorld(mousePosScreen);gamepadsUpdate()}function inputUpdatePost(){for(const a of inputData)for(const b in a)a[b]&=1;mouseWheel=0}onkeydown=a=>{debug&&a.target!=document.body||(a.repeat||(isUsingGamepad=!1,inputData[0][a.code]=3,inputWASDEmulateDirection&&(inputData[0][remapKey(a.code)]=3)),preventDefaultInput&&a.preventDefault())};onkeyup=a=>{debug&&a.target!=document.body||(inputData[0][a.code]=4,inputWASDEmulateDirection&&(inputData[0][remapKey(a.code)]=4))};function remapKey(a){return inputWASDEmulateDirection?"KeyW"==a?"ArrowUp":"KeyS"==a?"ArrowDown":"KeyA"==a?"ArrowLeft":"KeyD"==a?"ArrowRight":a:a}onmousedown=a=>{isUsingGamepad=!1;inputData[0][a.button]=3;mousePosScreen=mouseToScreen(a);a.button&&a.preventDefault()};onmouseup=a=>inputData[0][a.button]=inputData[0][a.button]&2|4;onmousemove=a=>mousePosScreen=mouseToScreen(a);onwheel=a=>mouseWheel=a.ctrlKey?0:sign(a.deltaY);oncontextmenu=a=>!1;function mouseToScreen(a){if(!mainCanvas)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 stickData=[];function gamepadsUpdate(){if(touchGamepadEnable&&isTouchDevice&&(touchGamepadButtons||createTouchGamepad(),touchGamepadTimer.isSet())){(stickData[0]||(stickData[0]=[]))[0]=vec2(touchGamepadStick.x,-touchGamepadStick.y);var a=inputData[1]||(inputData[1]=[]);for(var b=10;b--;){var c=3==b?2:2==b?3:b;a[c]=touchGamepadButtons[b]?gamepadIsDown(c,0)?1:3:gamepadIsDown(c,0)?4:0}}if(gamepadsEnable&&navigator&&navigator.getGamepads&&(document.hasFocus()||debug))for(a=navigator.getGamepads(),b=a.length;b--;){var d=a[b];const g=inputData[b+1]||(inputData[b+1]=[]);c=stickData[b]||(stickData[b]=[]);if(d){var e=k=>.3<k?percent(k,.3,.8):-.3>k?-percent(-k,.3,.8):0;for(var f=0;f<d.axes.length-1;f+=2)c[f>>1]=vec2(e(d.axes[f]),e(-d.axes[f+1])).clampLength();for(e=d.buttons.length;e--;){f=d.buttons[e];const k=gamepadIsDown(e,b);g[e]=f.pressed?k?1:3:k?4:0;isUsingGamepad||=!b&&f.pressed}gamepadDirectionEmulateStick&&(d=vec2((gamepadIsDown(15,b)&&1)-(gamepadIsDown(14,b)&&1),(gamepadIsDown(12,b)&&1)-(gamepadIsDown(13,b)&&1)),d.lengthSquared()&&(c[0]=d.clampLength()));touchGamepadEnable&&isUsingGamepad&&touchGamepadTimer.unset()}}}function vibrate(a=100){vibrateEnable&&navigator&&navigator.vibrate&&navigator.vibrate(a)}function vibrateStop(){vibrate(0)}const isTouchDevice=void 0!==window.ontouchstart;if(isTouchDevice){let a;onmousedown=onmouseup=()=>0;ontouchstart=ontouchmove=ontouchend=b=>{soundEnable&&audioContext&&"running"!=audioContext.state&&zzfx(0);const c=b.touches.length;if(c){const d=vec2(b.touches[0].clientX,b.touches[0].clientY);mousePosScreen=mouseToScreen(d);a?isUsingGamepad=!1:inputData[0][0]=3}else a&&(inputData[0][0]=inputData[0][0]&2|4);a=c;document.hasFocus()&&b.preventDefault();return!0}}let touchGamepadTimer=new Timer,touchGamepadButtons,touchGamepadStick;function createTouchGamepad(){touchGamepadButtons=[];touchGamepadStick=vec2();const a=ontouchstart;ontouchstart=ontouchmove=ontouchend=b=>{touchGamepadStick=vec2();touchGamepadButtons=[];if(b.touches.length&&(touchGamepadTimer.set(),paused)){touchGamepadButtons[9]=1;return}const c=vec2(touchGamepadSize,mainCanvasSize.y-touchGamepadSize),d=mainCanvasSize.subtract(vec2(touchGamepadSize,touchGamepadSize)),e=mainCanvasSize.scale(.5);for(const g of b.touches){var f=mouseToScreen(vec2(g.clientX,g.clientY));f.distance(c)<touchGamepadSize?touchGamepadAnalog?touchGamepadStick=f.subtract(c).scale(2/touchGamepadSize).clampLength():(f=f.subtract(c).angle(),touchGamepadStick.setAngle((4*f/PI+8.5|0)*PI/4)):f.distance(d)<touchGamepadSize?(f=f.subtract(d).direction(),touchGamepadButtons[f]=1):f.distance(e)<touchGamepadSize&&(touchGamepadButtons[9]=1)}a.bind(window)(b);return isUsingGamepad=!0}}function touchGamepadRender(){if(touchGamepadEnable&&touchGamepadTimer.isSet()){var a=percent(touchGamepadTimer.get(),4,3);if(a&&!paused){overlayContext.save();overlayContext.globalAlpha=a*touchGamepadAlpha;overlayContext.strokeStyle="#fff";overlayContext.lineWidth=3;overlayContext.fillStyle=0<touchGamepadStick.lengthSquared()?"#fff":"#000";overlayContext.beginPath();a=vec2(touchGamepadSize,mainCanvasSize.y-touchGamepadSize);if(touchGamepadAnalog)overlayContext.arc(a.x,a.y,touchGamepadSize/2,0,9),overlayContext.fill();else for(var b=10;b--;){var c=b*PI/4;overlayContext.arc(a.x,a.y,.6*touchGamepadSize,c+PI/8,c+PI/8);b%2&&overlayContext.arc(a.x,a.y,.33*touchGamepadSize,c,c);1==b&&overlayContext.fill()}overlayContext.stroke();a=vec2(mainCanvasSize.x-touchGamepadSize,mainCanvasSize.y-touchGamepadSize);for(b=4;b--;)c=a.add(vec2().setDirection(b,touchGamepadSize/2)),overlayContext.fillStyle=touchGamepadButtons[b]?"#fff":"#000",overlayContext.beginPath(),overlayContext.arc(c.x,c.y,touchGamepadSize/4,0,9),overlayContext.fill(),overlayContext.stroke();overlayContext.restore()}}}class Sound{constructor(a,b=soundDefaultRange,c=soundDefaultTaper){soundEnable&&(this.range=b,this.taper=c,this.randomness=0,a&&(this.randomness=a[1]||0,a[1]=0,this.sampleChannels=[zzfxG(...a)],this.sampleRate=zzfxR))}play(a,b=1,c=1,d=1,e=!1){if(soundEnable&&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);return this.source=playSamples(this.sampleChannels,b,a,f,e,this.sampleRate)}}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){super(void 0,c,d);this.randomness=b;soundEnable&&fetch(a).then(e=>e.arrayBuffer()).then(e=>audioContext.decodeAudioData(e)).then(e=>{this.sampleChannels=[];for(let f=e.numberOfChannels;f--;)this.sampleChannels[f]=Array.from(e.getChannelData(f));this.sampleRate=e.sampleRate})}}class Music extends Sound{constructor(a){super(void 0);soundEnable&&(this.randomness=0,this.sampleChannels=zzfxM(...a),this.sampleRate=zzfxR)}playMusic(a,b=!1){return super.play(void 0,a,1,1,b)}}function playAudioFile(a,b=1,c=!1){if(soundEnable)return a=new Audio(a),a.volume=soundVolume*b,a.loop=c,a.play(),a}function speak(a,b="",c=1,d=1,e=1){if(soundEnable&&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)}let audioContext=new AudioContext;function playSamples(a,b=1,c=1,d=0,e=!1,f=zzfxR){if(soundEnable)if("running"!=audioContext.state)audioContext.resume();else{var g=audioContext.createBuffer(a.length,a[0].length,f);f=audioContext.createBufferSource();a.forEach((k,h)=>g.getChannelData(h).set(k));f.buffer=g;f.playbackRate.value=c;f.loop=e;a=audioContext.createGain();a.gain.value=soundVolume*b;a.connect(audioContext.destination);f.connect(new StereoPannerNode(audioContext,{pan:clamp(d,-1,1)})).connect(a);f.start();return f}}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,l=0,n=0,m=0,p=0,t=0,u=0,x=0,v=0,D=1,z=0,E=0,A=0){let w=2*PI;var q=zzfxR;let F=h*=500*w/q/q;b=c*=rand(1+b,1-b)*w/q;let C=[],y=0,G=0,r=0,H=1,R=0,S=0,B=0,J;var L=w*abs(A)*2/q,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*q+9;z*=q;e*=q;f*=q;v*=q;l*=500*w/q**3;u*=w/q;n*=w/q;m*=q;p=p*q|0;a*=soundVolume;for(J=d+z+e+f+v|0;r<J;C[r++]=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*r/p):1)*sign(B)*abs(B)**k*(r<d?r/d:r<d+z?1-(r-d)/z*(1-D):r<d+z+e?D:r<J-v?(J-r-v)/f*D:0),B=v?B/2+(v>r?0:(r<J-v?1:(J-r)/v)*C[r-v|0]/2/a):B,A&&(B=Q=N*I+K*(I=O)+N*(O=B)-M*P-L*(P=Q))),q=(c+=h+=l)*Math.cos(u*G++),y+=q+q*t*Math.sin(r**5),H&&++H>m&&(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,l,n,m,p,t,u,x,v,D=0,z,E=[],A=[],w=[],q=0,F=0,C=1,y={},G=zzfxR/d*60>>2;for(;C;q++)E=[C=m=x=0],c.forEach((r,H)=>{n=b[r][q]||[0,0,0];C|=b[r][q]&&1;z=x+(b[r][0].length-2-(m?0:1))*G;v=H==c.length-1;e=2;for(g=x;e<n.length+v;m=++e){h=n[e];p=e==n.length+v-1&&v||t!=(n[0]||0)||h|0;for(f=0;f<G&&m;f++>G-99&&p&&1>u?u+=1/99:0)l=(1-u)*E[D++]/2||0,A[g]=(A[g]||0)-l*F+l,w[g]=(w[g++]||0)+l*F+l;h&&(u=h%1,F=n[1]||0,h|=0)&&(E=y[[t=n[D=0]||0,h]]=y[[t,h]]||(k=[...a[t]],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}}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 l=getTileCollisionData(g);if(l&&(!c||c.collideWithTile(l,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)}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");glEnable&&!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);enginePreRender()}redrawEnd(){ASSERT(mainContext==this.context,"must call redrawStart() before drawing tiles");glEnable&&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&&(a=this.pos.add(a).add(vec2(.5)),ASSERT(mainContext==this.context,"must call redrawStart() before drawing tiles"),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,l=new Color(1,1,1,0),n=new Color(1,1,1,0),m=.5,p=.1,t=1,u=.1,x=.05,v=1,D=1,z=0,E=PI,A=.1,w=.2,q=!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=l;this.colorEndB=n;this.randomColorLinear=C;this.particleTime=m;this.sizeStart=p;this.sizeEnd=t;this.speed=u;this.angleSpeed=x;this.damping=v;this.angleDamping=D;this.gravityScale=z;this.particleConeAngle=E;this.fadeRate=A;this.randomness=w;this.collideTiles=q;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=n=>n+n*rand(c,-c);const e=d(this.particleTime),f=d(this.sizeStart),g=d(this.sizeEnd),k=d(this.speed);d(this.angleSpeed);randSign();var h=rand(this.emitConeAngle,-this.emitConeAngle);d=randColor(this.colorStartA,this.colorStartB,this.randomColorLinear);const l=randColor(this.colorEndA,this.colorEndB,this.randomColorLinear);h=this.localSpace?h:this.angle+h;a=new Particle(a,this.tileInfo,b,d,l,e,f,g,this.fadeRate,this.additive,this.trailScale,this.localSpace&&this,this.particleDestroyCallback);a.velocity=vec2().setAngle(h,k);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,l,n,m,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=l;this.trailScale=n;this.localSpaceEmitter=m;this.destroyCallback=p}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||medals.forEach(b=>b.unlocked=localStorage[b.storageKey()]|0)}class Medal{constructor(a,b,c="",d="🏆",e){ASSERT(0<=a&&!medals[a]);medals[this.id=a]=this;this.name=b;this.description=c;this.icon=d;e&&((this.image=new Image).src=e)}unlock(){medalsPreventUnlock||this.unlocked||(ASSERT(medalsSaveName,"save name must be set"),localStorage[this.storageKey()]=this.unlocked=1,medalsDisplayQueue.push(this),newgrounds&&newgrounds.unlockMedal(this.id))}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}}function medalsRender(){if(medalsDisplayQueue.length){var a=medalsDisplayQueue[0],b=timeReal-medalsDisplayTimeLast;if(medalsDisplayTimeLast)if(b>medalDisplayTime)medalsDisplayTimeLast=0,medalsDisplayQueue.shift();else{const c=medalDisplayTime-medalDisplaySlideTime;a.render(b<medalDisplaySlideTime?1-b/medalDisplaySlideTime:b>c?(b-c)/medalDisplaySlideTime:0)}else medalsDisplayTimeLast=timeReal}}let newgrounds;function newgroundsInit(a,b,c){newgrounds=new Newgrounds(a,b,c)}class Newgrounds{constructor(a,b,c){ASSERT(!newgrounds&&0<a,"there can only be one newgrounds object");ASSERT(!b||c,"must provide cryptojs if there is a cipher");this.app_id=a;this.cipher=b;this.cryptoJS=c;this.host=location?location.hostname:"";if(this.session_id=new URL(location.href).searchParams.get("ngio_session_id")){this.medals=(a=this.call("Medal.getList"))?a.result.data.medals:[];debugMedals&&console.log(this.medals);for(var d of this.medals)if(a=medals[d.id])a.image=new Image,a.image.src=d.icon,a.name=d.name,a.description=d.description,a.unlocked=d.unlocked,a.difficulty=d.difficulty,a.value=d.value,a.value&&(a.description=a.description+" ("+a.value+")");this.scoreboards=(d=this.call("ScoreBoard.getBoards"))?d.result.data.scoreboards:[];debugMedals&&console.log(this.scoreboards);setInterval(()=>this.call("Gateway.ping",0,!0),3e5)}}unlockMedal(a){return this.call("Medal.unlock",{id:a},!0)}postScore(a,b){return this.call("ScoreBoard.postScore",{id:a,value:b},!0)}getScores(a,b,c=0,d=0,e=10){return this.call("ScoreBoard.getScores",{id:a,user:b,social:c,skip:d,limit:e})}logView(){return this.call("App.logView",{host:this.host},!0)}call(a,b,c=!1){a={component:a,parameters:b};if(this.cipher){b=this.cryptoJS;var d=b.enc.Base64.parse(this.cipher);const e=b.lib.WordArray.random(16);d=b.AES.encrypt(JSON.stringify(a),d,{iv:e});a.secure=b.enc.Base64.stringify(e.concat(d.ciphertext));a.parameters=0}b={app_id:this.app_id,session_id:this.session_id,call:a};a=new FormData;a.append("input",JSON.stringify(b));b=new XMLHttpRequest;b.open("POST","https://newgrounds.io/gateway_v3.php",!debugMedals&&c);b.send(a);debugMedals&&console.log(b.responseText);return b.responseText&&JSON.parse(b.responseText)}}let glCanvas,glContext,glShader,glActiveTexture,glArrayBuffer,glGeometryBuffer,glPositionData,glColorData,glInstanceCount,glAdditive,glBatchAdditive;function glInit(){glCanvas=document.createElement("canvas");glContext=glCanvas.getContext("webgl2");glOverlay&&document.body.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;in vec2 v;in vec4 d,e;uniform sampler2D s;out vec4 c;void main(){c=texture(s,v)*d+e;}");var 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(gl_ARRAY_BUFFER,glGeometryBuffer);glContext.bufferData(gl_ARRAY_BUFFER,a,gl_STATIC_DRAW)}function glPreRender(){glContext.viewport(0,0,glCanvas.width=mainCanvas.width,glCanvas.height=mainCanvas.height);glContext.clear(gl_COLOR_BUFFER_BIT);glContext.useProgram(glShader);glContext.activeTexture(gl_TEXTURE0);glContext.bindTexture(gl_TEXTURE_2D,glActiveTexture=textureInfos[0].glTexture);let a=glAdditive=glBatchAdditive=0;var b=(d,e,f,g)=>{d=glContext.getAttribLocation(glShader,d);const k=f&&gl_INSTANCE_BYTE_STRIDE,h=f&&1,l=1==f;glContext.enableVertexAttribArray(d);glContext.vertexAttribPointer(d,g,e,l,k,a);glContext.vertexAttribDivisor(d,h);a+=g*f};glContext.bindBuffer(gl_ARRAY_BUFFER,glGeometryBuffer);b("g",gl_FLOAT,0,2);glContext.bindBuffer(gl_ARRAY_BUFFER,glArrayBuffer);glContext.bufferData(gl_ARRAY_BUFFER,gl_INSTANCE_BUFFER_SIZE,gl_DYNAMIC_DRAW);b("p",gl_FLOAT,4,4);b("u",gl_FLOAT,4,4);b("c",gl_UNSIGNED_BYTE,1,4);b("a",gl_UNSIGNED_BYTE,1,4);b("r",gl_FLOAT,4,1);b=vec2(2*cameraScale).divide(mainCanvasSize);const c=vec2(-1).subtract(cameraPos.multiply(b));glContext.uniformMatrix4fv(glContext.getUniformLocation(glShader,"m"),!1,new Float32Array([b.x,0,0,0,0,b.y,0,0,1,1,1,1,c.x,c.y,0,0]))}function glSetTexture(a){a!=glActiveTexture&&(glFlush(),glContext.bindTexture(gl_TEXTURE_2D,glActiveTexture=a))}function glCompileShader(a,b){b=glContext.createShader(b);glContext.shaderSource(b,a);glContext.compileShader(b);if(debug&&!glContext.getShaderParameter(b,gl_COMPILE_STATUS))throw glContext.getShaderInfoLog(b);return b}function glCreateProgram(a,b){const c=glContext.createProgram();glContext.attachShader(c,glCompileShader(a,gl_VERTEX_SHADER));glContext.attachShader(c,glCompileShader(b,gl_FRAGMENT_SHADER));glContext.linkProgram(c);if(debug&&!glContext.getProgramParameter(c,gl_LINK_STATUS))throw glContext.getProgramInfoLog(c);return c}function glCreateTexture(a){const b=glContext.createTexture();glContext.bindTexture(gl_TEXTURE_2D,b);a&&glContext.texImage2D(gl_TEXTURE_2D,0,gl_RGBA,gl_RGBA,gl_UNSIGNED_BYTE,a);a=canvasPixelated?gl_NEAREST:gl_LINEAR;glContext.texParameteri(gl_TEXTURE_2D,gl_TEXTURE_MIN_FILTER,a);glContext.texParameteri(gl_TEXTURE_2D,gl_TEXTURE_MAG_FILTER,a);glContext.texParameteri(gl_TEXTURE_2D,gl_TEXTURE_WRAP_S,gl_CLAMP_TO_EDGE);glContext.texParameteri(gl_TEXTURE_2D,gl_TEXTURE_WRAP_T,gl_CLAMP_TO_EDGE);return b}function glFlush(){if(glInstanceCount){var a=glBatchAdditive?gl_ONE:gl_ONE_MINUS_SRC_ALPHA;glContext.blendFuncSeparate(gl_SRC_ALPHA,a,gl_ONE,a);glContext.enable(gl_BLEND);glContext.bufferSubData(gl_ARRAY_BUFFER,0,glPositionData);glContext.drawArraysInstanced(gl_TRIANGLE_STRIP,0,4,glInstanceCount);showWatermark&&(drawCount+=glInstanceCount);glInstanceCount=0;glBatchAdditive=glAdditive}}function glCopyToContext(a,b=!1){if(glInstanceCount||b)glFlush(),glOverlay&&!b||a.drawImage(glCanvas,0,0)}function glDraw(a,b,c,d,e,f,g,k,h,l,n=0){ASSERT("number"==typeof l&&"number"==typeof n,"invalid color");(glInstanceCount>=gl_MAX_INSTANCES||glBatchAdditive!=glAdditive)&&glFlush();let m=glInstanceCount*gl_INDICIES_PER_INSTANCE;glPositionData[m++]=a;glPositionData[m++]=b;glPositionData[m++]=c;glPositionData[m++]=d;glPositionData[m++]=f;glPositionData[m++]=g;glPositionData[m++]=k;glPositionData[m++]=h;glColorData[m++]=l;glColorData[m++]=n;glPositionData[m++]=e;glInstanceCount++}let glPostShader,glPostArrayBuffer,glPostTexture,glPostIncludeOverlay;function glInitPostProcess(a,b=!1){ASSERT(!glPostShader,"can only have 1 post effects shader");a||="void mainImage(out vec4 c,vec2 p){c=texture(iChannel0,p/iResolution.xy);}";glPostShader=glCreateProgram("#version 300 es\nprecision highp float;in vec2 p;void main(){gl_Position=vec4(p+p-1.,1,1);}","#version 300 es\nprecision highp float;uniform sampler2D iChannel0;uniform vec3 iResolution;uniform float iTime;out vec4 c;\n"+a+"\nvoid main(){mainImage(c,gl_FragCoord.xy);c.a=1.;}");glPostArrayBuffer=glContext.createBuffer();glPostTexture=glCreateTexture(void 0);glPostIncludeOverlay=b;mainCanvas.style.visibility="hidden";glPostIncludeOverlay&&(overlayCanvas.style.visibility="hidden")}function glRenderPostProcess(){if(glPostShader){glEnable?(glFlush(),mainContext.drawImage(glCanvas,0,0)):glContext.viewport(0,0,glCanvas.width=mainCanvas.width,glCanvas.height=mainCanvas.height);glPostIncludeOverlay&&mainContext.drawImage(overlayCanvas,0,0);glContext.useProgram(glPostShader);glContext.bindBuffer(gl_ARRAY_BUFFER,glGeometryBuffer);glContext.pixelStorei(gl_UNPACK_FLIP_Y_WEBGL,1);glContext.disable(gl_BLEND);glContext.activeTexture(gl_TEXTURE0);glContext.bindTexture(gl_TEXTURE_2D,glPostTexture);glContext.texImage2D(gl_TEXTURE_2D,0,gl_RGBA,gl_RGBA,gl_UNSIGNED_BYTE,mainCanvas);var a=glContext.getAttribLocation(glPostShader,"p");glContext.enableVertexAttribArray(a);glContext.vertexAttribPointer(a,2,gl_FLOAT,!1,8,0);glContext.uniform1i(glContext.getUniformLocation(glPostShader,"iChannel0"),0);glContext.uniform1f(glContext.getUniformLocation(glPostShader,"iTime"),time);glContext.uniform3f(glContext.getUniformLocation(glPostShader,"iResolution"),mainCanvas.width,mainCanvas.height,1);glContext.drawArrays(gl_TRIANGLE_STRIP,0,4)}}const gl_ONE=1,gl_TRIANGLE_STRIP=5,gl_SRC_ALPHA=770,gl_ONE_MINUS_SRC_ALPHA=771,gl_BLEND=3042,gl_TEXTURE_2D=3553,gl_UNSIGNED_BYTE=5121,gl_FLOAT=5126,gl_RGBA=6408,gl_NEAREST=9728,gl_LINEAR=9729,gl_TEXTURE_MAG_FILTER=10240,gl_TEXTURE_MIN_FILTER=10241,gl_TEXTURE_WRAP_S=10242,gl_TEXTURE_WRAP_T=10243,gl_COLOR_BUFFER_BIT=16384,gl_CLAMP_TO_EDGE=33071,gl_TEXTURE0=33984,gl_ARRAY_BUFFER=34962,gl_STATIC_DRAW=35044,gl_DYNAMIC_DRAW=35048,gl_FRAGMENT_SHADER=35632,gl_VERTEX_SHADER=35633,gl_COMPILE_STATUS=35713,gl_LINK_STATUS=35714,gl_UNPACK_FLIP_Y_WEBGL=37440,gl_INDICIES_PER_INSTANCE=11,gl_MAX_INSTANCES=1e4,gl_INSTANCE_BYTE_STRIDE=4*gl_INDICIES_PER_INSTANCE,gl_INSTANCE_BUFFER_SIZE=gl_MAX_INSTANCES*gl_INSTANCE_BYTE_STRIDE,engineName="LittleJS",engineVersion="1.9.2",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;function engineInit(a,b,c,d,e,f=["tiles.png"]){function g(k=0){var h=k-frameTimeLastMS;frameTimeLastMS=k;if(debug||showWatermark)averageFPS=lerp(.05,averageFPS,1e3/(h||1));k=debug&&keyIsDown("Equal");const l=debug&&keyIsDown("Minus");debug&&(h*=k?5:l?.2:1);timeReal+=h/1e3;frameTimeBufferMS+=paused?0:h;k||(frameTimeBufferMS=min(frameTimeBufferMS,50));canvasFixedSize.x?(mainCanvas.width=canvasFixedSize.x,mainCanvas.height=canvasFixedSize.y,h=innerWidth/innerHeight,k=mainCanvas.width/mainCanvas.height,(glCanvas||mainCanvas).style.width=mainCanvas.style.width=overlayCanvas.style.width=h<k?"100%":"",(glCanvas||mainCanvas).style.height=mainCanvas.style.height=overlayCanvas.style.height=h<k?"":"100%"):(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);if(paused)inputUpdate(),debugUpdate(),c(),inputUpdatePost();else{h=0;0>frameTimeBufferMS&&-9<frameTimeBufferMS&&(h=frameTimeBufferMS,frameTimeBufferMS=0);for(;0<=frameTimeBufferMS;frameTimeBufferMS-=1e3/frameRate)time=frame++/frameRate,inputUpdate(),b(),engineObjectsUpdate(),debugUpdate(),c(),inputUpdatePost();frameTimeBufferMS+=h}enginePreRender();d();engineObjects.sort((m,p)=>m.renderOrder-p.renderOrder);for(var n of engineObjects)n.destroyed||n.render();e();glRenderPostProcess();medalsRender();touchGamepadRender();debugRender();glEnable&&glCopyToContext(mainContext);showWatermark&&(overlayContext.textAlign="right",overlayContext.textBaseline="top",overlayContext.font="1em monospace",overlayContext.fillStyle="#000",n=engineName+" v"+engineVersion+" / "+drawCount+" / "+engineObjects.length+" / "+averageFPS.toFixed(1)+(glEnable?" GL":" 2D"),overlayContext.fillText(n,mainCanvas.width-3,3),overlayContext.fillStyle="#fff",overlayContext.fillText(n,mainCanvas.width-2,2),drawCount=0);requestAnimationFrame(g)}ASSERT(Array.isArray(f),"pass in images as array");document.body.style.cssText="margin:0;overflow:hidden;background:#000;touch-action:none;user-select:none;-webkit-user-select:none;-webkit-touch-callout:none";document.body.appendChild(mainCanvas=document.createElement("canvas"));mainContext=mainCanvas.getContext("2d");debugInit();glEnable&&glInit();document.body.appendChild(overlayCanvas=document.createElement("canvas"));overlayContext=overlayCanvas.getContext("2d");(glCanvas||mainCanvas).style.cssText=mainCanvas.style.cssText=overlayCanvas.style.cssText="position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)";f=f.map((k,h)=>new Promise(l=>{const n=new Image;n.onerror=n.onload=()=>{textureInfos[h]=new TextureInfo(n);l()};n.src=k}));showSplashScreen&&f.push(new Promise(k=>{function h(){clearInput();drawEngineSplashScreen(l+=.01);1<l?k():setTimeout(h,16)}let l=0;console.log(`${engineName} Engine v${engineVersion}`);h()}));Promise.all(f).then(()=>{a();g()})}function enginePreRender(){mainCanvasSize=vec2(mainCanvas.width,mainCanvas.height);mainContext.imageSmoothingEnabled=!canvasPixelated;glEnable&&glPreRender()}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);engineObjects=engineObjects.filter(b=>!b.destroyed)}function engineObjectsDestroy(){for(const a of engineObjects)a.parent||a.destroy();engineObjects=engineObjects.filter(a=>!a.destroyed)}function engineObjectsCallback(a,b,c,d=engineObjects){if(a)if("object"===typeof b)for(const e of d)isOverlapping(a,b,e.pos,e.size)&&c(e);else{b*=b;for(const e of d)a.distanceSquared(e.pos)<b&&c(e)}else for(const e of d)c(e)}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,l,n,m,p)=>{b.beginPath();b.rect(h,l,n,p?m*k:m);(b.fillStyle=p)?b.fill():b.stroke()};f=(h,l,n,m=0,p=2*PI,t,u)=>{const x=(m+p)/2;m=k*(p-m)/2;b.beginPath();u&&b.lineTo(h,l);b.arc(h,l,n,x-m,x+m);(b.fillStyle=t)?b.fill():b.stroke()};e=(h=0,l=0)=>hsl([.98,.3,.57,.14][h%4]-10,.8,[0,.3,.5,.8,.9][l]).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,17,18,-8,e(2,2));g(7,9,18,4,e(2,3));g(25,9,8,8,e(2,1));g(25,9,-18,8);g(25,9,8,8);g(25,17,7,22,e());g(11,40,14,-23,e(1,1));g(11,17,14,17,e(1,2));g(11,17,14,9,e(1,3));g(15,31,6,-9,e(2,2));f(15,23,5,0,PI/2,e(2,4),1);g(25,17,-14,23);g(21,22,-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,-10,e(0,1));g(50,20,6,-10,e(0,2));g(50,20,3,-10,e(0,3));g(50,10,10,10);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,0,20,3,e(0,3));g(45,0,20,7);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(47,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,l=41-g/2;h<f.length;++h)b.fillStyle=e(h,2),d=b.measureText(f[h]).width,b[c?"strokeText":"fillText"](f[h],l+d/2,55.5,17*k),l+=d;b.restore()}
|
|
@@ -156,13 +156,13 @@ function smoothStep(percent) { return percent * percent * (3 - 2 * percent); }
|
|
|
156
156
|
function nearestPowerOfTwo(value) { return 2**Math.ceil(Math.log2(value)); }
|
|
157
157
|
|
|
158
158
|
/** Returns true if two axis aligned bounding boxes are overlapping
|
|
159
|
-
* @param {Vector2} pointA
|
|
160
|
-
* @param {Vector2} sizeA
|
|
161
|
-
* @param {Vector2} pointB
|
|
162
|
-
* @param {Vector2} sizeB - Size of box B
|
|
163
|
-
* @return {Boolean}
|
|
159
|
+
* @param {Vector2} pointA - Center of box A
|
|
160
|
+
* @param {Vector2} sizeA - Size of box A
|
|
161
|
+
* @param {Vector2} pointB - Center of box B
|
|
162
|
+
* @param {Vector2} [sizeB=(0,0)] - Size of box B, a point if undefined
|
|
163
|
+
* @return {Boolean} - True if overlapping
|
|
164
164
|
* @memberof Utilities */
|
|
165
|
-
function isOverlapping(pointA, sizeA, pointB, sizeB)
|
|
165
|
+
function isOverlapping(pointA, sizeA, pointB, sizeB=vec2())
|
|
166
166
|
{
|
|
167
167
|
return abs(pointA.x - pointB.x)*2 < sizeA.x + sizeB.x
|
|
168
168
|
&& abs(pointA.y - pointB.y)*2 < sizeA.y + sizeB.y;
|
|
@@ -222,8 +222,8 @@ function randInCircle(radius=1, minRadius=0)
|
|
|
222
222
|
{ return radius > 0 ? randVector(radius * rand(minRadius / radius, 1)**.5) : new Vector2; }
|
|
223
223
|
|
|
224
224
|
/** Returns a random color between the two passed in colors, combine components if linear
|
|
225
|
-
* @param {Color} [colorA=
|
|
226
|
-
* @param {Color} [colorB=
|
|
225
|
+
* @param {Color} [colorA=(1,1,1,1)]
|
|
226
|
+
* @param {Color} [colorB=(0,0,0,1)]
|
|
227
227
|
* @param {Boolean} [linear]
|
|
228
228
|
* @return {Color}
|
|
229
229
|
* @memberof Random */
|
|
@@ -294,7 +294,11 @@ class RandomGenerator
|
|
|
294
294
|
* @memberof Utilities
|
|
295
295
|
*/
|
|
296
296
|
function vec2(x=0, y)
|
|
297
|
-
{
|
|
297
|
+
{
|
|
298
|
+
return typeof x === 'number' ?
|
|
299
|
+
new Vector2(x, y == undefined? x : y) :
|
|
300
|
+
new Vector2(x.x, x.y);
|
|
301
|
+
}
|
|
298
302
|
|
|
299
303
|
/**
|
|
300
304
|
* Check if object is a valid Vector2
|
|
@@ -302,7 +306,7 @@ function vec2(x=0, y)
|
|
|
302
306
|
* @return {Boolean}
|
|
303
307
|
* @memberof Utilities
|
|
304
308
|
*/
|
|
305
|
-
function isVector2(v) { return
|
|
309
|
+
function isVector2(v) { return v instanceof Vector2; }
|
|
306
310
|
|
|
307
311
|
/**
|
|
308
312
|
* 2D Vector object with vector math library
|
|
@@ -333,27 +337,47 @@ class Vector2
|
|
|
333
337
|
/** Returns a copy of this vector plus the vector passed in
|
|
334
338
|
* @param {Vector2} v - other vector
|
|
335
339
|
* @return {Vector2} */
|
|
336
|
-
add(v)
|
|
340
|
+
add(v)
|
|
341
|
+
{
|
|
342
|
+
ASSERT(isVector2(v));
|
|
343
|
+
return new Vector2(this.x + v.x, this.y + v.y);
|
|
344
|
+
}
|
|
337
345
|
|
|
338
346
|
/** Returns a copy of this vector minus the vector passed in
|
|
339
347
|
* @param {Vector2} v - other vector
|
|
340
348
|
* @return {Vector2} */
|
|
341
|
-
subtract(v)
|
|
349
|
+
subtract(v)
|
|
350
|
+
{
|
|
351
|
+
ASSERT(isVector2(v));
|
|
352
|
+
return new Vector2(this.x - v.x, this.y - v.y);
|
|
353
|
+
}
|
|
342
354
|
|
|
343
355
|
/** Returns a copy of this vector times the vector passed in
|
|
344
356
|
* @param {Vector2} v - other vector
|
|
345
357
|
* @return {Vector2} */
|
|
346
|
-
multiply(v)
|
|
358
|
+
multiply(v)
|
|
359
|
+
{
|
|
360
|
+
ASSERT(isVector2(v));
|
|
361
|
+
return new Vector2(this.x * v.x, this.y * v.y);
|
|
362
|
+
}
|
|
347
363
|
|
|
348
364
|
/** Returns a copy of this vector divided by the vector passed in
|
|
349
365
|
* @param {Vector2} v - other vector
|
|
350
366
|
* @return {Vector2} */
|
|
351
|
-
divide(v)
|
|
367
|
+
divide(v)
|
|
368
|
+
{
|
|
369
|
+
ASSERT(isVector2(v));
|
|
370
|
+
return new Vector2(this.x / v.x, this.y / v.y);
|
|
371
|
+
}
|
|
352
372
|
|
|
353
373
|
/** Returns a copy of this vector scaled by the vector passed in
|
|
354
374
|
* @param {Number} s - scale
|
|
355
375
|
* @return {Vector2} */
|
|
356
|
-
scale(s)
|
|
376
|
+
scale(s)
|
|
377
|
+
{
|
|
378
|
+
ASSERT(!isVector2(s));
|
|
379
|
+
return new Vector2(this.x * s, this.y * s);
|
|
380
|
+
}
|
|
357
381
|
|
|
358
382
|
/** Returns the length of this vector
|
|
359
383
|
* @return {Number} */
|
|
@@ -366,32 +390,56 @@ class Vector2
|
|
|
366
390
|
/** Returns the distance from this vector to vector passed in
|
|
367
391
|
* @param {Vector2} v - other vector
|
|
368
392
|
* @return {Number} */
|
|
369
|
-
distance(v)
|
|
393
|
+
distance(v)
|
|
394
|
+
{
|
|
395
|
+
ASSERT(isVector2(v));
|
|
396
|
+
return this.distanceSquared(v)**.5;
|
|
397
|
+
}
|
|
370
398
|
|
|
371
399
|
/** Returns the distance squared from this vector to vector passed in
|
|
372
400
|
* @param {Vector2} v - other vector
|
|
373
401
|
* @return {Number} */
|
|
374
|
-
distanceSquared(v)
|
|
402
|
+
distanceSquared(v)
|
|
403
|
+
{
|
|
404
|
+
ASSERT(isVector2(v));
|
|
405
|
+
return (this.x - v.x)**2 + (this.y - v.y)**2;
|
|
406
|
+
}
|
|
375
407
|
|
|
376
408
|
/** Returns a new vector in same direction as this one with the length passed in
|
|
377
409
|
* @param {Number} [length]
|
|
378
410
|
* @return {Vector2} */
|
|
379
|
-
normalize(length=1)
|
|
411
|
+
normalize(length=1)
|
|
412
|
+
{
|
|
413
|
+
const l = this.length();
|
|
414
|
+
return l ? this.scale(length/l) : new Vector2(0, length);
|
|
415
|
+
}
|
|
380
416
|
|
|
381
417
|
/** Returns a new vector clamped to length passed in
|
|
382
418
|
* @param {Number} [length]
|
|
383
419
|
* @return {Vector2} */
|
|
384
|
-
clampLength(length=1)
|
|
420
|
+
clampLength(length=1)
|
|
421
|
+
{
|
|
422
|
+
const l = this.length();
|
|
423
|
+
return l > length ? this.scale(length/l) : this;
|
|
424
|
+
}
|
|
385
425
|
|
|
386
426
|
/** Returns the dot product of this and the vector passed in
|
|
387
427
|
* @param {Vector2} v - other vector
|
|
388
428
|
* @return {Number} */
|
|
389
|
-
dot(v)
|
|
429
|
+
dot(v)
|
|
430
|
+
{
|
|
431
|
+
ASSERT(isVector2(v));
|
|
432
|
+
return this.x*v.x + this.y*v.y;
|
|
433
|
+
}
|
|
390
434
|
|
|
391
435
|
/** Returns the cross product of this and the vector passed in
|
|
392
436
|
* @param {Vector2} v - other vector
|
|
393
437
|
* @return {Number} */
|
|
394
|
-
cross(v)
|
|
438
|
+
cross(v)
|
|
439
|
+
{
|
|
440
|
+
ASSERT(isVector2(v));
|
|
441
|
+
return this.x*v.y - this.y*v.x;
|
|
442
|
+
}
|
|
395
443
|
|
|
396
444
|
/** Returns the angle of this vector, up is angle 0
|
|
397
445
|
* @return {Number} */
|
|
@@ -402,7 +450,11 @@ class Vector2
|
|
|
402
450
|
* @param {Number} [length]
|
|
403
451
|
* @return {Vector2} */
|
|
404
452
|
setAngle(angle=0, length=1)
|
|
405
|
-
{
|
|
453
|
+
{
|
|
454
|
+
this.x = length*Math.sin(angle);
|
|
455
|
+
this.y = length*Math.cos(angle);
|
|
456
|
+
return this;
|
|
457
|
+
}
|
|
406
458
|
|
|
407
459
|
/** Returns copy of this vector rotated by the angle passed in
|
|
408
460
|
* @param {Number} angle
|
|
@@ -413,9 +465,20 @@ class Vector2
|
|
|
413
465
|
return new Vector2(this.x*c - this.y*s, this.x*s + this.y*c);
|
|
414
466
|
}
|
|
415
467
|
|
|
468
|
+
/** Set the integer direction of this vector, corrosponding to multiples of 90 degree rotation (0-3)
|
|
469
|
+
* @param {Number} [direction]
|
|
470
|
+
* @param {Number} [length] */
|
|
471
|
+
setDirection(direction, length=1)
|
|
472
|
+
{
|
|
473
|
+
ASSERT(direction==0 || direction==1 || direction==2 || direction==3);
|
|
474
|
+
return vec2(direction%2 ? direction-1 ? -length : length : 0,
|
|
475
|
+
direction%2 ? 0 : direction ? -length : length);
|
|
476
|
+
}
|
|
477
|
+
|
|
416
478
|
/** Returns the integer direction of this vector, corrosponding to multiples of 90 degree rotation (0-3)
|
|
417
479
|
* @return {Number} */
|
|
418
|
-
direction()
|
|
480
|
+
direction()
|
|
481
|
+
{ return abs(this.x) > abs(this.y) ? this.x < 0 ? 3 : 1 : this.y < 0 ? 2 : 0; }
|
|
419
482
|
|
|
420
483
|
/** Returns a copy of this vector that has been inverted
|
|
421
484
|
* @return {Vector2} */
|
|
@@ -434,24 +497,34 @@ class Vector2
|
|
|
434
497
|
* @param {Number} percent
|
|
435
498
|
* @return {Vector2} */
|
|
436
499
|
lerp(v, percent)
|
|
437
|
-
{
|
|
500
|
+
{
|
|
501
|
+
ASSERT(isVector2(v));
|
|
502
|
+
return this.add(v.subtract(this).scale(clamp(percent)));
|
|
503
|
+
}
|
|
438
504
|
|
|
439
505
|
/** Returns true if this vector is within the bounds of an array size passed in
|
|
440
506
|
* @param {Vector2} arraySize
|
|
441
507
|
* @return {Boolean} */
|
|
442
|
-
arrayCheck(arraySize)
|
|
508
|
+
arrayCheck(arraySize)
|
|
509
|
+
{
|
|
510
|
+
ASSERT(isVector2(arraySize));
|
|
511
|
+
return this.x >= 0 && this.y >= 0 && this.x < arraySize.x && this.y < arraySize.y;
|
|
512
|
+
}
|
|
443
513
|
|
|
444
514
|
/** Returns this vector expressed as a string
|
|
445
515
|
* @param {Number} digits - precision to display
|
|
446
516
|
* @return {String} */
|
|
447
517
|
toString(digits=3)
|
|
448
|
-
{
|
|
518
|
+
{
|
|
519
|
+
if (debug)
|
|
520
|
+
return `(${(this.x<0?'':' ') + this.x.toFixed(digits)},${(this.y<0?'':' ') + this.y.toFixed(digits)} )`;
|
|
521
|
+
}
|
|
449
522
|
}
|
|
450
523
|
|
|
451
524
|
///////////////////////////////////////////////////////////////////////////////
|
|
452
525
|
|
|
453
526
|
/**
|
|
454
|
-
* Create a color object with RGBA values
|
|
527
|
+
* Create a color object with RGBA values, white by default
|
|
455
528
|
* @param {Number} [r=1] - red
|
|
456
529
|
* @param {Number} [g=1] - green
|
|
457
530
|
* @param {Number} [b=1] - blue
|
|
@@ -462,7 +535,7 @@ class Vector2
|
|
|
462
535
|
function rgb(r, g, b, a) { return new Color(r, g, b, a); }
|
|
463
536
|
|
|
464
537
|
/**
|
|
465
|
-
* Create a color object with HSLA values
|
|
538
|
+
* Create a color object with HSLA values, white by default
|
|
466
539
|
* @param {Number} [h=0] - hue
|
|
467
540
|
* @param {Number} [s=0] - saturation
|
|
468
541
|
* @param {Number} [l=1] - lightness
|
|
@@ -472,14 +545,22 @@ function rgb(r, g, b, a) { return new Color(r, g, b, a); }
|
|
|
472
545
|
*/
|
|
473
546
|
function hsl(h, s, l, a) { return new Color().setHSLA(h, s, l, a); }
|
|
474
547
|
|
|
548
|
+
/**
|
|
549
|
+
* Check if object is a valid Color
|
|
550
|
+
* @param {any} c
|
|
551
|
+
* @return {Boolean}
|
|
552
|
+
* @memberof Utilities
|
|
553
|
+
*/
|
|
554
|
+
function isColor(c) { return c instanceof Color; }
|
|
555
|
+
|
|
475
556
|
/**
|
|
476
557
|
* Color object (red, green, blue, alpha) with some helpful functions
|
|
477
558
|
* @example
|
|
478
559
|
* let a = new Color; // white
|
|
479
560
|
* let b = new Color(1, 0, 0); // red
|
|
480
561
|
* let c = new Color(0, 0, 0, 0); // transparent black
|
|
481
|
-
* let d =
|
|
482
|
-
* let e =
|
|
562
|
+
* let d = rgb(0, 0, 1); // blue using rgb color
|
|
563
|
+
* let e = hsl(.3, 1, .5); // green using hsl color
|
|
483
564
|
*/
|
|
484
565
|
class Color
|
|
485
566
|
{
|
|
@@ -507,22 +588,38 @@ class Color
|
|
|
507
588
|
/** Returns a copy of this color plus the color passed in
|
|
508
589
|
* @param {Color} c - other color
|
|
509
590
|
* @return {Color} */
|
|
510
|
-
add(c)
|
|
591
|
+
add(c)
|
|
592
|
+
{
|
|
593
|
+
ASSERT(isColor(c));
|
|
594
|
+
return new Color(this.r+c.r, this.g+c.g, this.b+c.b, this.a+c.a);
|
|
595
|
+
}
|
|
511
596
|
|
|
512
597
|
/** Returns a copy of this color minus the color passed in
|
|
513
598
|
* @param {Color} c - other color
|
|
514
599
|
* @return {Color} */
|
|
515
|
-
subtract(c)
|
|
600
|
+
subtract(c)
|
|
601
|
+
{
|
|
602
|
+
ASSERT(isColor(c));
|
|
603
|
+
return new Color(this.r-c.r, this.g-c.g, this.b-c.b, this.a-c.a);
|
|
604
|
+
}
|
|
516
605
|
|
|
517
606
|
/** Returns a copy of this color times the color passed in
|
|
518
607
|
* @param {Color} c - other color
|
|
519
608
|
* @return {Color} */
|
|
520
|
-
multiply(c)
|
|
609
|
+
multiply(c)
|
|
610
|
+
{
|
|
611
|
+
ASSERT(isColor(c));
|
|
612
|
+
return new Color(this.r*c.r, this.g*c.g, this.b*c.b, this.a*c.a);
|
|
613
|
+
}
|
|
521
614
|
|
|
522
615
|
/** Returns a copy of this color divided by the color passed in
|
|
523
616
|
* @param {Color} c - other color
|
|
524
617
|
* @return {Color} */
|
|
525
|
-
divide(c)
|
|
618
|
+
divide(c)
|
|
619
|
+
{
|
|
620
|
+
ASSERT(isColor(c));
|
|
621
|
+
return new Color(this.r/c.r, this.g/c.g, this.b/c.b, this.a/c.a);
|
|
622
|
+
}
|
|
526
623
|
|
|
527
624
|
/** Returns a copy of this color scaled by the value passed in, alpha can be scaled separately
|
|
528
625
|
* @param {Number} scale
|
|
@@ -539,7 +636,11 @@ class Color
|
|
|
539
636
|
* @param {Color} c - other color
|
|
540
637
|
* @param {Number} percent
|
|
541
638
|
* @return {Color} */
|
|
542
|
-
lerp(c, percent)
|
|
639
|
+
lerp(c, percent)
|
|
640
|
+
{
|
|
641
|
+
ASSERT(isColor(c));
|
|
642
|
+
return this.add(c.subtract(this).scale(clamp(percent)));
|
|
643
|
+
}
|
|
543
644
|
|
|
544
645
|
/** Sets this color given a hue, saturation, lightness, and alpha
|
|
545
646
|
* @param {Number} [h] - hue
|
|
@@ -2354,9 +2455,9 @@ if (isTouchDevice)
|
|
|
2354
2455
|
// handle all touch events the same way
|
|
2355
2456
|
ontouchstart = ontouchmove = ontouchend = (e)=>
|
|
2356
2457
|
{
|
|
2357
|
-
// fix stalled audio
|
|
2358
|
-
if (soundEnable)
|
|
2359
|
-
|
|
2458
|
+
// fix stalled audio requiring user interaction
|
|
2459
|
+
if (soundEnable && audioContext && audioContext.state != 'running')
|
|
2460
|
+
zzfx(0);
|
|
2360
2461
|
|
|
2361
2462
|
// check if touching and pass to mouse events
|
|
2362
2463
|
const touching = e.touches.length;
|
|
@@ -2502,7 +2603,7 @@ function touchGamepadRender()
|
|
|
2502
2603
|
const rightCenter = vec2(mainCanvasSize.x-touchGamepadSize, mainCanvasSize.y-touchGamepadSize);
|
|
2503
2604
|
for (let i=4; i--;)
|
|
2504
2605
|
{
|
|
2505
|
-
const pos = rightCenter.add(vec2().
|
|
2606
|
+
const pos = rightCenter.add(vec2().setDirection(i, touchGamepadSize/2));
|
|
2506
2607
|
overlayContext.fillStyle = touchGamepadButtons[i] ? '#fff' : '#000';
|
|
2507
2608
|
overlayContext.beginPath();
|
|
2508
2609
|
overlayContext.arc(pos.x, pos.y, touchGamepadSize/4, 0,9);
|
|
@@ -2661,22 +2762,19 @@ class SoundWave extends Sound
|
|
|
2661
2762
|
this.randomness = randomness;
|
|
2662
2763
|
|
|
2663
2764
|
if (!soundEnable) return;
|
|
2664
|
-
if (!soundDecoderContext)
|
|
2665
|
-
soundDecoderContext = new AudioContext;
|
|
2666
2765
|
|
|
2667
2766
|
fetch(filename)
|
|
2668
2767
|
.then(response => response.arrayBuffer())
|
|
2669
|
-
.then(arrayBuffer =>
|
|
2768
|
+
.then(arrayBuffer => audioContext.decodeAudioData(arrayBuffer))
|
|
2670
2769
|
.then(audioBuffer =>
|
|
2671
2770
|
{
|
|
2672
2771
|
this.sampleChannels = [];
|
|
2673
2772
|
for (let i = audioBuffer.numberOfChannels; i--;)
|
|
2674
|
-
this.sampleChannels[i] = audioBuffer.getChannelData(i);
|
|
2773
|
+
this.sampleChannels[i] = Array.from(audioBuffer.getChannelData(i));
|
|
2675
2774
|
this.sampleRate = audioBuffer.sampleRate;
|
|
2676
2775
|
});
|
|
2677
2776
|
}
|
|
2678
2777
|
}
|
|
2679
|
-
let soundDecoderContext; // audio context used only to decode audio files
|
|
2680
2778
|
|
|
2681
2779
|
/**
|
|
2682
2780
|
* Music Object - Stores a zzfx music track for later use
|
|
@@ -2790,8 +2888,9 @@ function getNoteFrequency(semitoneOffset, rootFrequency=220)
|
|
|
2790
2888
|
///////////////////////////////////////////////////////////////////////////////
|
|
2791
2889
|
|
|
2792
2890
|
/** Audio context used by the engine
|
|
2891
|
+
* @type {AudioContext}
|
|
2793
2892
|
* @memberof Audio */
|
|
2794
|
-
let audioContext;
|
|
2893
|
+
let audioContext = new AudioContext;
|
|
2795
2894
|
|
|
2796
2895
|
/** Play cached audio samples with given settings
|
|
2797
2896
|
* @param {Array} sampleChannels - Array of arrays of samples to play (for stereo playback)
|
|
@@ -2806,10 +2905,6 @@ function playSamples(sampleChannels, volume=1, rate=1, pan=0, loop=false, sample
|
|
|
2806
2905
|
{
|
|
2807
2906
|
if (!soundEnable) return;
|
|
2808
2907
|
|
|
2809
|
-
// create audio context if needed
|
|
2810
|
-
if (!audioContext)
|
|
2811
|
-
audioContext = new AudioContext;
|
|
2812
|
-
|
|
2813
2908
|
// prevent sounds from building up if they can't be played
|
|
2814
2909
|
if (audioContext.state != 'running')
|
|
2815
2910
|
{
|
|
@@ -3220,7 +3315,7 @@ class TileLayerData
|
|
|
3220
3315
|
* @param {Number} [direction] - Integer direction of tile, in 90 degree increments
|
|
3221
3316
|
* @param {Boolean} [mirror] - If the tile should be mirrored along the x axis
|
|
3222
3317
|
* @param {Color} [color] - Color of the tile */
|
|
3223
|
-
constructor(tile, direction=0, mirror=false, color=new Color
|
|
3318
|
+
constructor(tile, direction=0, mirror=false, color=new Color)
|
|
3224
3319
|
{
|
|
3225
3320
|
/** @property {Number} - The tile to use, untextured if undefined */
|
|
3226
3321
|
this.tile = tile;
|
|
@@ -3460,12 +3555,12 @@ class TileLayer extends EngineObject
|
|
|
3460
3555
|
* @example
|
|
3461
3556
|
* // create a particle emitter
|
|
3462
3557
|
* let pos = vec2(2,3);
|
|
3463
|
-
* let
|
|
3558
|
+
* let particleEmitter = new ParticleEmitter
|
|
3464
3559
|
* (
|
|
3465
|
-
* pos, 0, 1, 0, 500, PI,
|
|
3466
|
-
* tile(0, 16),
|
|
3467
|
-
*
|
|
3468
|
-
*
|
|
3560
|
+
* pos, 0, 1, 0, 500, PI, // pos, angle, emitSize, emitTime, emitRate, emiteCone
|
|
3561
|
+
* tile(0, 16), // tileInfo
|
|
3562
|
+
* rgb(1,1,1), rgb(0,0,0), // colorStartA, colorStartB
|
|
3563
|
+
* rgb(1,1,1,0), rgb(0,0,0,0), // colorEndA, colorEndB
|
|
3469
3564
|
* 2, .2, .2, .1, .05, // particleTime, sizeStart, sizeEnd, particleSpeed, particleAngleSpeed
|
|
3470
3565
|
* .99, 1, 1, PI, .05, // damping, angleDamping, gravityScale, particleCone, fadeRate,
|
|
3471
3566
|
* .5, 1 // randomness, collide, additive, randomColorLinear, renderOrder
|
|
@@ -3872,8 +3967,8 @@ class Medal
|
|
|
3872
3967
|
// draw containing rect and clip to that region
|
|
3873
3968
|
context.save();
|
|
3874
3969
|
context.beginPath();
|
|
3875
|
-
context.fillStyle =
|
|
3876
|
-
context.strokeStyle =
|
|
3970
|
+
context.fillStyle = new Color(.9,.9,.9).toString();
|
|
3971
|
+
context.strokeStyle = new Color(0,0,0).toString();
|
|
3877
3972
|
context.lineWidth = 3;
|
|
3878
3973
|
context.rect(x, y, width, medalDisplaySize.y);
|
|
3879
3974
|
context.fill();
|
|
@@ -4325,7 +4420,7 @@ function glDraw(x, y, sizeX, sizeY, angle, uv0X, uv0Y, uv1X, uv1Y, rgba, rgbaAdd
|
|
|
4325
4420
|
ASSERT(typeof rgba == 'number' && typeof rgbaAdditive == 'number', 'invalid color');
|
|
4326
4421
|
|
|
4327
4422
|
// flush if there is not enough room or if different blend mode
|
|
4328
|
-
if (glInstanceCount >= gl_MAX_INSTANCES
|
|
4423
|
+
if (glInstanceCount >= gl_MAX_INSTANCES || glBatchAdditive != glAdditive)
|
|
4329
4424
|
glFlush();
|
|
4330
4425
|
|
|
4331
4426
|
let offset = glInstanceCount * gl_INDICIES_PER_INSTANCE;
|
|
@@ -4505,7 +4600,7 @@ const engineName = 'LittleJS';
|
|
|
4505
4600
|
* @type {String}
|
|
4506
4601
|
* @default
|
|
4507
4602
|
* @memberof Engine */
|
|
4508
|
-
const engineVersion = '1.9.
|
|
4603
|
+
const engineVersion = '1.9.2';
|
|
4509
4604
|
|
|
4510
4605
|
/** Frames per second to update objects
|
|
4511
4606
|
* @type {Number}
|
|
@@ -62,8 +62,8 @@ function gameUpdatePost()
|
|
|
62
62
|
function gameRender()
|
|
63
63
|
{
|
|
64
64
|
// draw a the background
|
|
65
|
-
drawRect(cameraPos, levelSize.scale(2),
|
|
66
|
-
drawRect(cameraPos, levelSize,
|
|
65
|
+
drawRect(cameraPos, levelSize.scale(2), hsl(0,0,.5));
|
|
66
|
+
drawRect(cameraPos, levelSize, hsl(0,0,.02));
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
///////////////////////////////////////////////////////////////////////////////
|
|
@@ -13,7 +13,7 @@ class Wall extends EngineObject
|
|
|
13
13
|
|
|
14
14
|
this.mass = 0; // make object have static physics
|
|
15
15
|
this.setCollision(); // make object collide
|
|
16
|
-
this.color =
|
|
16
|
+
this.color = hsl(0,0,0,0); // make object invisible
|
|
17
17
|
}
|
|
18
18
|
}
|
|
19
19
|
|
|
@@ -60,7 +60,7 @@ class Brick extends EngineObject
|
|
|
60
60
|
|
|
61
61
|
// make explosion effect
|
|
62
62
|
const color1 = this.color;
|
|
63
|
-
const color2 = color1.lerp(
|
|
63
|
+
const color2 = color1.lerp(hsl(), .5);
|
|
64
64
|
new ParticleEmitter(
|
|
65
65
|
this.pos, 0, // pos, angle
|
|
66
66
|
this.size, .1, 200, PI, // emitSize, emitTime, emitRate, emiteCone
|
|
@@ -76,7 +76,7 @@ class Brick extends EngineObject
|
|
|
76
76
|
if (o.trailEffect)
|
|
77
77
|
{
|
|
78
78
|
o.trailEffect.colorStartA = this.color;
|
|
79
|
-
o.trailEffect.colorStartB = this.color.lerp(
|
|
79
|
+
o.trailEffect.colorStartB = this.color.lerp(hsl(), .5);
|
|
80
80
|
}
|
|
81
81
|
|
|
82
82
|
return 1;
|
|
@@ -5,6 +5,6 @@
|
|
|
5
5
|
<link rel=icon type=image/png href=../favicon.png>
|
|
6
6
|
</head><body>
|
|
7
7
|
|
|
8
|
-
<script src=../../
|
|
9
|
-
<script src=gameObjects.js?
|
|
10
|
-
<script src=game.js?
|
|
8
|
+
<script src=../../dist/littlejs.js?192></script>
|
|
9
|
+
<script src=gameObjects.js?192></script>
|
|
10
|
+
<script src=game.js?192></script>
|