littlejsengine 1.4.5 → 1.4.7
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/engine/engine.all.js +22 -12
- package/engine/engine.all.min.js +1 -1
- package/engine/engine.all.module.js +34 -27
- package/engine/engine.all.release.js +22 -12
- package/engine/engine.all.release.module.js +34 -27
- package/engine/engine.js +15 -8
- package/engine/engineBuild.bat +20 -1
- package/engine/{engineExportModule.js → engineExport.js} +12 -15
- package/engine/engineFont.png +0 -0
- package/engine/engineInput.js +6 -2
- package/engine/engineSettings.js +1 -1
- package/engine/engineWebGL.js +0 -1
- package/engine/index.d.ts +1 -5
- package/examples/breakout/index.html +3 -3
- package/examples/module/index.html +1 -1
- package/examples/particles/index.html +12 -12
- package/examples/platformer/index.html +6 -6
- package/examples/puzzle/index.html +2 -2
- package/examples/stress/index.html +1 -1
- package/index.html +13 -13
- package/package.json +3 -4
- package/engine/engineBuildModule.mjs +0 -14
package/engine/engine.all.js
CHANGED
|
@@ -1110,7 +1110,7 @@ let touchGamepadAnalog = 1;
|
|
|
1110
1110
|
/** Size of virutal gamepad for touch devices in pixels
|
|
1111
1111
|
* @default
|
|
1112
1112
|
* @memberof Settings */
|
|
1113
|
-
let touchGamepadSize =
|
|
1113
|
+
let touchGamepadSize = 99;
|
|
1114
1114
|
|
|
1115
1115
|
/** Transparency of touch gamepad overlay
|
|
1116
1116
|
* @default
|
|
@@ -2190,6 +2190,10 @@ const isTouchDevice = window.ontouchstart !== undefined;
|
|
|
2190
2190
|
// try to enable touch mouse
|
|
2191
2191
|
if (isTouchDevice)
|
|
2192
2192
|
{
|
|
2193
|
+
// override mouse events
|
|
2194
|
+
const mouseDown = onmousedown, mouseUp = onmouseup, mouseMove = onmousemove;
|
|
2195
|
+
onmousedown = onmouseup = onmousemove = (e)=> 0;
|
|
2196
|
+
|
|
2193
2197
|
// handle all touch events the same way
|
|
2194
2198
|
let wasTouching, hadTouch;
|
|
2195
2199
|
ontouchstart = ontouchmove = ontouchend = (e)=>
|
|
@@ -2206,10 +2210,10 @@ if (isTouchDevice)
|
|
|
2206
2210
|
// set event pos and pass it along
|
|
2207
2211
|
e.x = e.touches[0].clientX;
|
|
2208
2212
|
e.y = e.touches[0].clientY;
|
|
2209
|
-
wasTouching ?
|
|
2213
|
+
wasTouching ? mouseMove(e) : mouseDown(e);
|
|
2210
2214
|
}
|
|
2211
2215
|
else if (wasTouching)
|
|
2212
|
-
|
|
2216
|
+
mouseUp(e);
|
|
2213
2217
|
|
|
2214
2218
|
// set was touching
|
|
2215
2219
|
wasTouching = touching;
|
|
@@ -3812,7 +3816,6 @@ function glInit()
|
|
|
3812
3816
|
// create the canvas and tile texture
|
|
3813
3817
|
glCanvas = document.createElement('canvas');
|
|
3814
3818
|
glContext = glCanvas.getContext('webgl', {antialias: false});
|
|
3815
|
-
glCanvas.style = styleCanvas;
|
|
3816
3819
|
glTileTexture = glCreateTexture(tileImage);
|
|
3817
3820
|
|
|
3818
3821
|
// some browsers are much faster without copying the gl buffer so we just overlay it instead
|
|
@@ -4209,7 +4212,7 @@ gl_VERTEX_BYTE_STRIDE = (4 * 2) * 2 + (4) * 2; // vec2 * 2 + (char * 4) * 2
|
|
|
4209
4212
|
const engineName = 'LittleJS';
|
|
4210
4213
|
|
|
4211
4214
|
/** Version of engine */
|
|
4212
|
-
const engineVersion = '1.4.
|
|
4215
|
+
const engineVersion = '1.4.7';
|
|
4213
4216
|
|
|
4214
4217
|
/** Frames per second to update objects
|
|
4215
4218
|
* @default */
|
|
@@ -4238,15 +4241,11 @@ let timeReal = 0;
|
|
|
4238
4241
|
let paused = 0;
|
|
4239
4242
|
|
|
4240
4243
|
// Engine internal variables not exposed to documentation
|
|
4241
|
-
let
|
|
4244
|
+
let tileImageSize, tileImageFixBleed;
|
|
4242
4245
|
|
|
4243
4246
|
// Engine stat tracking, if showWatermark is true
|
|
4244
4247
|
let averageFPS, drawCount;
|
|
4245
4248
|
|
|
4246
|
-
// css text used for elements created by engine
|
|
4247
|
-
const styleBody = 'margin:0;overflow:hidden;background:#000';
|
|
4248
|
-
const styleCanvas = 'position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)';
|
|
4249
|
-
|
|
4250
4249
|
///////////////////////////////////////////////////////////////////////////////
|
|
4251
4250
|
|
|
4252
4251
|
/** Start up LittleJS engine with your callback functions
|
|
@@ -4266,11 +4265,16 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
4266
4265
|
tileImageFixBleed = vec2(tileFixBleedScale).divide(tileImageSize = vec2(tileImage.width, tileImage.height));
|
|
4267
4266
|
debug && (tileImage.onload=()=>ASSERT(1)); // tile sheet can not reloaded
|
|
4268
4267
|
|
|
4268
|
+
// setup css
|
|
4269
|
+
const styleBody = 'margin:0;overflow:hidden;background:#000' + // fill the window
|
|
4270
|
+
';touch-action:none' + // prevent mobile pinch to resize
|
|
4271
|
+
';user-select:none' + // prevent mobile hold to select
|
|
4272
|
+
';-webkit-user-select:none;-moz-user-select:none'; // compatibility for mobile
|
|
4273
|
+
|
|
4269
4274
|
// setup html
|
|
4270
4275
|
document.body.style = styleBody;
|
|
4271
4276
|
document.body.appendChild(mainCanvas = document.createElement('canvas'));
|
|
4272
4277
|
mainContext = mainCanvas.getContext('2d');
|
|
4273
|
-
mainCanvas.style = styleCanvas;
|
|
4274
4278
|
|
|
4275
4279
|
// init stuff and start engine
|
|
4276
4280
|
debugInit();
|
|
@@ -4279,13 +4283,19 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
4279
4283
|
// create overlay canvas for hud to appear above gl canvas
|
|
4280
4284
|
document.body.appendChild(overlayCanvas = document.createElement('canvas'));
|
|
4281
4285
|
overlayContext = overlayCanvas.getContext('2d');
|
|
4282
|
-
|
|
4286
|
+
|
|
4287
|
+
// set canvas style to fill the window
|
|
4288
|
+
const styleCanvas = 'position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)';
|
|
4289
|
+
(glCanvas||mainCanvas).style = overlayCanvas.style = mainCanvas.style = styleCanvas;
|
|
4283
4290
|
|
|
4284
4291
|
gameInit();
|
|
4285
4292
|
touchGamepadCreate();
|
|
4286
4293
|
engineUpdate();
|
|
4287
4294
|
};
|
|
4288
4295
|
|
|
4296
|
+
// frame time tracking
|
|
4297
|
+
let frameTimeLastMS = 0, frameTimeBufferMS = 0;
|
|
4298
|
+
|
|
4289
4299
|
// main update loop
|
|
4290
4300
|
const engineUpdate = (frameTimeMS=0)=>
|
|
4291
4301
|
{
|
package/engine/engine.all.min.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
let showWatermark=0,godMode=0;const debug=0,debugOverlay=0,debugPhysics=0,debugParticles=0,debugRaycast=0,debugGamepads=0,debugMedals=0,ASSERT=()=>{},debugInit=()=>{},debugUpdate=()=>{},debugRender=()=>{},debugRect=()=>{},debugCircle=()=>{},debugPoint=()=>{},debugLine=()=>{},debugAABB=()=>{},debugText=()=>{},debugClear=()=>{},debugSaveCanvas=()=>{};"use strict";const PI=Math.PI,abs=a=>0>a?-a:a,min=(a,b)=>a<b?a:b,max=(a,b)=>a>b?a:b,sign=a=>0>a?-1:1,mod=(a,b=1)=>(a%b+b)%b,clamp=(a,b=0,c=1)=>a<b?b:a>c?c:a,percent=(a,b=0,c=1)=>c-b?clamp((a-b)/(c-b)):0,lerp=(a,b=0,c=1)=>b+clamp(a)*(c-b),smoothStep=a=>a*a*(3-2*a),nearestPowerOfTwo=a=>2**Math.ceil(Math.log2(a)),isOverlapping=(a,b,c,d)=>2*abs(a.x-c.x)<b.x+d.x&2*abs(a.y-c.y)<b.y+d.y,wave=(a=1,b=1,c=time)=>b/2*(1-Math.cos(c*a*2*PI)),formatTime=a=>(a/60|0)+":"+(10>a%60?"0":"")+(a%60|0),rand=(a=1,b=0)=>b+(a-b)*Math.random(),randInt=(a=1,b=0)=>rand(a,b)|0,randSign=()=>2*(rand(2)|0)-1,randInCircle=(a=1,b=0)=>0<a?randVector(a*rand(b/a,1)**.5):new Vector2,randVector=(a=1)=>(new Vector2).setAngle(rand(2*PI),a),randColor=(a=new Color,b=new Color(0,0,0,1),c)=>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));let randSeed=1;const randSeeded=(a=1,b=0)=>{randSeed^=randSeed<<13;randSeed^=randSeed>>>17;randSeed^=randSeed<<5;return b+(a-b)*abs(randSeed%1e9)/1e9},vec2=(a=0,b)=>void 0==a.x?new Vector2(a,void 0==b?a:b):new Vector2(a.x,a.y);class Vector2{constructor(a=0,b=0){this.x=a;this.y=b}copy(){return new Vector2(this.x,this.y)}add(a){ASSERT(void 0!=a.x);return new Vector2(this.x+a.x,this.y+a.y)}subtract(a){ASSERT(void 0!=a.x);return new Vector2(this.x-a.x,this.y-a.y)}multiply(a){ASSERT(void 0!=a.x);return new Vector2(this.x*a.x,this.y*a.y)}divide(a){ASSERT(void 0!=a.x);return new Vector2(this.x/a.x,this.y/a.y)}scale(a){ASSERT(void 0==a.x);return new Vector2(this.x*a,this.y*a)}length(){return this.lengthSquared()**.5}lengthSquared(){return this.x**2+this.y**2}distance(a){return this.distanceSquared(a)**.5}distanceSquared(a){return(this.x-a.x)**2+(this.y-a.y)**2}normalize(a=1){const b=this.length();return b?this.scale(a/b):new Vector2(0,a)}clampLength(a=1){const b=this.length();return b>a?this.scale(a/b):this}dot(a){ASSERT(void 0!=a.x);return this.x*a.x+this.y*a.y}cross(a){ASSERT(void 0!=a.x);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)}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(void 0!=a.x);return this.add(a.subtract(this).scale(clamp(b)))}arrayCheck(a){return 0<=this.x&&0<=this.y&&this.x<a.x&&this.y<a.y}toString(a=3){return`(${(0>this.x?"":" ")+this.x.toFixed(a)},${(0>this.y?"":" ")+this.y.toFixed(a)} )`}}const colorRGBA=(a,b,c,d)=>new Color(a,b,c,d),colorHSLA=(a,b,c,d)=>(new Color).setHSLA(a,b,c,d);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){return new Color(this.r+a.r,this.g+a.g,this.b+a.b,this.a+a.a)}subtract(a){return new Color(this.r-a.r,this.g-a.g,this.b-a.b,this.a-a.a)}multiply(a){return new Color(this.r*a.r,this.g*a.g,this.b*a.b,this.a*a.a)}divide(a){return new Color(this.r/a.r,this.g/a.g,this.b/a.b,this.a/a.a)}scale(a,b=a){return new Color(this.r*a,this.g*a,this.b*a,this.a*b)}clamp(){return new Color(clamp(this.r),clamp(this.g),clamp(this.b),clamp(this.a))}lerp(a,b){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,k,g)=>(g=(g%1+1)%1)<1/6?f+6*(k-f)*g:.5>g?k:g<2/3?f+(k-f)*(2/3-g)*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),k=(e+f)/2;let g=0,h=0;if(e!=f){let n=e-f;h=.5<k?n/(2-e-f):n/(e+f);a==e?g=(b-c)/n+(b<c?6:0):b==e?g=(c-a)/n+2:c==e&&(g=(a-b)/n+4)}return[g/6,h,k,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=1){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)|0)<<8,c=(255*clamp(this.b)|0)<<16,d=(255*clamp(this.a)|0)<<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.unset()?"unset":Math.abs(this.get())+" seconds "+(0>this.get()?"before":"after")}valueOf(){return this.get()}}"use strict";let canvasMaxSize=vec2(1920,1200),canvasFixedSize=vec2(),cavasPixelated=1,fontDefault="arial",tileSizeDefault=vec2(16),tileFixBleedScale=.3,objectDefaultSize=vec2(1),enablePhysicsSolver=1,objectDefaultMass=1,objectDefaultDamping=.99,objectDefaultAngleDamping=.99,objectDefaultElasticity=0,objectDefaultFriction=.8,objectMaxSpeed=1,gravity=0,particleEmitRateScale=1,cameraPos=vec2(),cameraScale=max(tileSizeDefault.x,tileSizeDefault.y),glEnable=1,glOverlay=1,gamepadsEnable=1,gamepadDirectionEmulateStick=1,inputWASDEmulateDirection=1,touchGamepadEnable=0,touchGamepadAnalog=1,touchGamepadSize=80,touchGamepadAlpha=.3,vibrateEnable=1,soundVolume=.5,soundEnable=1,soundDefaultRange=30,soundDefaultTaper=.7,medalDisplayTime=5,medalDisplaySlideTime=.5,medalDisplayWidth=640,medalDisplayHeight=80,medalDisplayIconSize=50;"use strict";const engineName="LittleJS",engineVersion="1.4.5",frameRate=60,timeDelta=1/frameRate;let engineObjects=[],engineObjectsCollide=[],frame=0,time=0,timeReal=0,paused=0,frameTimeLastMS=0,frameTimeBufferMS=0,tileImageSize,tileImageFixBleed,averageFPS,drawCount;const styleBody="margin:0;overflow:hidden;background:#000",styleCanvas="position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)";function engineInit(a,b,c,d,e,f){tileImage.onerror=tileImage.onload=()=>{tileImageFixBleed=vec2(tileFixBleedScale).divide(tileImageSize=vec2(tileImage.width,tileImage.height));debug&&(tileImage.onload=()=>ASSERT(1));document.body.style=styleBody;document.body.appendChild(mainCanvas=document.createElement("canvas"));mainContext=mainCanvas.getContext("2d");mainCanvas.style=styleCanvas;debugInit();glEnable&&glInit();document.body.appendChild(overlayCanvas=document.createElement("canvas"));overlayContext=overlayCanvas.getContext("2d");overlayCanvas.style=styleCanvas;a();touchGamepadCreate();k()};const k=(g=0)=>{var h=g-frameTimeLastMS;frameTimeLastMS=g;if(debug||showWatermark)averageFPS=lerp(.05,averageFPS||0,1e3/(h||1));g=debug&&keyIsDown(107);const n=debug&&keyIsDown(109);debug&&(h*=g?5:n?.2:1);timeReal+=h/1e3;frameTimeBufferMS+=!paused*h;g||(frameTimeBufferMS=min(frameTimeBufferMS,50));canvasFixedSize.x?(overlayCanvas.width=mainCanvas.width=canvasFixedSize.x,overlayCanvas.height=mainCanvas.height=canvasFixedSize.y,h=innerWidth/innerHeight,g=mainCanvas.width/mainCanvas.height,mainCanvas.style.width=overlayCanvas.style.width=h<g?"100%":"",mainCanvas.style.height=overlayCanvas.style.height=h<g?"":"100%",glCanvas&&(glCanvas.style.width=mainCanvas.style.width,glCanvas.style.height=mainCanvas.style.height)):(overlayCanvas.width=mainCanvas.width=min(innerWidth,canvasMaxSize.x),overlayCanvas.height=mainCanvas.height=min(innerHeight,canvasMaxSize.y));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)inputUpdate(),b(),engineObjectsUpdate(),debugUpdate(),c(),inputUpdatePost();frameTimeBufferMS+=h}enginePreRender();d();engineObjects.sort((p,l)=>p.renderOrder-l.renderOrder);for(var m of engineObjects)m.destroyed||m.render();e();glRenderPostProcess();medalsRender();touchGamepadRender();debugRender();glEnable&&glCopyToContext(mainContext);showWatermark&&(overlayContext.textAlign="right",overlayContext.textBaseline="top",overlayContext.font="1em monospace",overlayContext.fillStyle="#000",m=engineName+" v"+engineVersion+" / "+drawCount+" / "+engineObjects.length+" / "+averageFPS.toFixed(1)+" "+(glEnable?"GL":"2D"),overlayContext.fillText(m,mainCanvas.width-3,3),overlayContext.fillStyle="#fff",overlayContext.fillText(m,mainCanvas.width-2,2),drawCount=0);requestAnimationFrame(k)};f?tileImage.src=f:tileImage.onload()}function enginePreRender(){mainCanvasSize=vec2(mainCanvas.width,mainCanvas.height);mainContext.imageSmoothingEnabled=!cavasPixelated;glEnable&&glPreRender(mainCanvas.width,mainCanvas.height,cameraPos.x,cameraPos.y,cameraScale)}function engineObjectsUpdate(){engineObjectsCollide=engineObjects.filter(b=>b.collideSolidObjects);const a=b=>{if(!b.destroyed){b.update();for(const c of b.children)a(c)}};for(const b of engineObjects)b.parent||a(b);engineObjects=engineObjects.filter(b=>!b.destroyed);time=++frame/frameRate}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(void 0!=b.x)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)}"use strict";class EngineObject{constructor(a=vec2(),b=objectDefaultSize,c=-1,d=tileSizeDefault,e=0,f,k=0){ASSERT(a&&void 0!=a.x&&void 0!=b.x);this.pos=a.copy();this.size=b;this.drawSize;this.tileIndex=c;this.tileSize=d;this.angle=e;this.color=f;this.additiveColor;this.mass=objectDefaultMass;this.damping=objectDefaultDamping;this.angleDamping=objectDefaultAngleDamping;this.elasticity=objectDefaultElasticity;this.friction=objectDefaultFriction;this.gravityScale=1;this.renderOrder=k;this.velocity=new Vector2;this.angleVelocity=0;this.spawnTime=time;this.children=[];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.pos.x+=this.velocity.x*=this.damping,this.pos.y+=this.velocity.y=this.damping*this.velocity.y+gravity*this.gravityScale,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||!isOverlapping(this.pos,this.size,d.pos,d.size)||!this.collideWithObject(d)|!d.collideWithObject(this)))if(isOverlapping(a,this.size,d.pos,d.size)){c=a.subtract(d.pos);var 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")}else{c=this.size.add(d.size);e=2*(a.y-d.pos.y)>c.y+gravity;var f=2*abs(a.y-d.pos.y)<c.y,k=2*abs(a.x-d.pos.x)<c.x;if(e||k||!f)if(this.pos.y=d.pos.y+(c.y/2+.001)*sign(a.y-d.pos.y),d.groundObject&&b||!d.mass)b&&(this.groundObject=d),this.velocity.y*=-this.elasticity;else if(d.mass){const g=(this.mass*this.velocity.y+d.mass*d.velocity.y)/(this.mass+d.mass),h=this.velocity.y*(this.mass-d.mass)/(this.mass+d.mass)+2*d.velocity.y*d.mass/(this.mass+d.mass),n=d.velocity.y*(d.mass-this.mass)/(this.mass+d.mass)+2*this.velocity.y*this.mass/(this.mass+d.mass),m=max(this.elasticity,d.elasticity);this.velocity.y=lerp(m,g,h);d.velocity.y=lerp(m,g,n)}e||!f&&k||(this.pos.x=d.pos.x+(c.x/2+.001)*sign(a.x-d.pos.x),d.mass?(c=(this.mass*this.velocity.x+d.mass*d.velocity.x)/(this.mass+d.mass),e=this.velocity.x*(this.mass-d.mass)/(this.mass+d.mass)+2*d.velocity.x*d.mass/(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),k=max(this.elasticity,d.elasticity),this.velocity.x=lerp(k,c,e),d.velocity.x=lerp(k,c,f)):this.velocity.x*=-this.elasticity);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(new Vector2(a.x,this.pos.y),this.size,this);d=tileCollisionTest(new Vector2(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.tileIndex,this.tileSize,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 1}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=1,b=1,c=1){ASSERT(a||!b);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}}}"use strict";const tileImage=new Image;let mainCanvas,mainContext,overlayCanvas,overlayContext,mainCanvasSize=vec2();const screenToWorld=a=>{ASSERT(mainCanvasSize.x&&mainCanvasSize.y,"mainCanvasSize is invalid");return a.add(vec2(.5)).subtract(mainCanvasSize.scale(.5)).multiply(vec2(1/cameraScale,-1/cameraScale)).add(cameraPos)},worldToScreen=a=>{ASSERT(mainCanvasSize.x&&mainCanvasSize.y,"mainCanvasSize is invalid");return a.subtract(cameraPos).multiply(vec2(cameraScale,-cameraScale)).add(mainCanvasSize.scale(.5)).subtract(vec2(.5))};function drawTile(a,b=vec2(1),c=-1,d=tileSizeDefault,e=new Color,f=0,k,g=new Color(0,0,0,0),h=glEnable){showWatermark&&++drawCount;if(glEnable&&h)if(0>c||!tileImage.width)glDraw(a.x,a.y,b.x,b.y,f,0,0,0,0,0,e.rgbaInt());else{var n=tileImageSize.x/d.x|0;h=d.x/tileImageSize.x;const m=d.y/tileImageSize.y,p=c%n*h;n=(c/n|0)*m;glDraw(a.x,a.y,k?-b.x:b.x,b.y,f,p+tileImageFixBleed.x,n+tileImageFixBleed.y,p-tileImageFixBleed.x+h,n-tileImageFixBleed.y+m,e.rgbaInt(),g.rgbaInt())}else drawCanvas2D(a,b,f,k,m=>{if(0>c)m.fillStyle=e,m.fillRect(-.5,-.5,1,1);else{var p=tileImageSize.x/d.x|0;const l=c%p*d.x+tileFixBleedScale;p=(c/p|0)*d.y+tileFixBleedScale;const u=d.x-2*tileFixBleedScale,v=d.y-2*tileFixBleedScale;m.globalAlpha=e.a;m.drawImage(tileImage,l,p,u,v,-.5,-.5,1,1)}})}function drawRect(a,b,c,d,e){drawTile(a,b,-1,tileSizeDefault,c,d,0,0,e)}function drawTileScreenSpace(a,b=vec2(1),c,d,e,f,k,g,h){drawTile(screenToWorld(a),b.scale(1/cameraScale),c,d,e,f,k,g,h)}function drawRectScreenSpace(a,b,c,d,e){drawTileScreenSpace(a,b,-1,tileSizeDefault,c,d,0,0,e)}function drawLine(a,b,c=.1,d,e){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)}function drawCanvas2D(a,b,c,d,e,f=mainContext){a=worldToScreen(a);b=b.scale(cameraScale);f.save();f.translate(a.x+.5|0,a.y+.5|0);f.rotate(c);f.scale(d?-b.x:b.x,b.y);e(f);f.restore()}function setBlendMode(a,b=glEnable){glEnable&&b?glSetBlendMode(a):mainContext.globalCompositeOperation=a?"lighter":"source-over"}function drawTextScreen(a,b,c=1,d=new Color,e=0,f=new Color(0,0,0),k="center",g=fontDefault,h=overlayContext){h.fillStyle=d;h.lineWidth=e;h.strokeStyle=f;h.textAlign=k;h.font=c+"px "+g;h.textBaseline="middle";h.lineJoin="round";b=b.copy();(a+"").split("\n").forEach(n=>{e&&h.strokeText(n,b.x,b.y);h.fillText(n,b.x,b.y);b.y+=c})}function drawText(a,b,c=1,d,e,f,k,g){drawTextScreen(a,worldToScreen(b),c*cameraScale,d,e*cameraScale,f,k,g,mainContext)}let engineFontImage;class FontImage{constructor(a,b=vec2(8),c=vec2(0,1),d=0,e=overlayContext){a||engineFontImage||(engineFontImage=new Image,engineFontImage.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.startTileIndex=d;this.context=e}drawTextScreen(a,b,c=4,d){const e=this.context;e.save();e.imageSmoothingEnabled=!cavasPixelated;const f=this.tileSize,k=f.add(this.paddingSize).scale(c),g=this.image.width/this.tileSize.x|0;(a+"").split("\n").forEach((h,n)=>{const m=d?h.length*f.x*c/2|0:0;for(let u=h.length;u--;){var p=h[u].charCodeAt();if(32>p||127<p)p=127;var l=this.startTileIndex+p-32;p=l%g;l=l/g|0;const v=b.add(vec2(u,n).multiply(k));e.drawImage(this.image,p*f.x,l*f.y,f.x,f.y,v.x-m,v.y,f.x*c,f.y*c)}});e.restore()}drawText(a,b,c=1,d){this.drawTextScreen(a,worldToScreen(b).floor(),c*cameraScale|0,d)}}const isFullscreen=()=>document.fullscreenElement;function toggleFullscreen(){isFullscreen()?document.exitFullscreen?document.exitFullscreen():document.mozCancelFullScreen&&document.mozCancelFullScreen():document.body.webkitRequestFullScreen?document.body.webkitRequestFullScreen():document.body.mozRequestFullScreen&&document.body.mozRequestFullScreen()}"use strict";const keyIsDown=(a,b=0)=>inputData[b]&&inputData[b][a]&1?1:0,keyWasPressed=(a,b=0)=>inputData[b]&&inputData[b][a]&2?1:0,keyWasReleased=(a,b=0)=>inputData[b]&&inputData[b][a]&4?1:0,clearInput=()=>inputData=[[]],mouseIsDown=keyIsDown,mouseWasPressed=keyWasPressed,mouseWasReleased=keyWasReleased;let mousePos=vec2(),mousePosScreen=vec2(),mouseWheel=0,isUsingGamepad=0,preventDefaultInput=0;const gamepadIsDown=(a,b=0)=>keyIsDown(a,b+1),gamepadWasPressed=(a,b=0)=>keyWasPressed(a,b+1),gamepadWasReleased=(a,b=0)=>keyWasReleased(a,b+1),gamepadStick=(a,b=0)=>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||(inputData[isUsingGamepad=0][remapKeyCode(a.keyCode)]=3),preventDefaultInput&&a.preventDefault())};onkeyup=a=>{debug&&a.target!=document.body||(inputData[0][remapKeyCode(a.keyCode)]=4)};const remapKeyCode=a=>inputWASDEmulateDirection?87==a?38:83==a?40:65==a?37:68==a?39:a:a;onmousedown=a=>{inputData[isUsingGamepad=0][a.button]=3;onmousemove(a);a.button&&a.preventDefault()};onmouseup=a=>inputData[0][a.button]=inputData[0][a.button]&2|4;onmousemove=a=>mousePosScreen=mouseToScreen(a);onwheel=a=>a.ctrlKey||(mouseWheel=sign(a.deltaY));oncontextmenu=a=>!1;const 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)))},stickData=[];function gamepadsUpdate(){if(touchGamepadEnable&&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]?1+2*!gamepadIsDown(c,0):4*gamepadIsDown(c,0)}}if(gamepadsEnable&&navigator&&navigator.getGamepads&&(document.hasFocus()||debug))for(a=navigator.getGamepads(),b=a.length;b--;){var d=a[b];const k=inputData[b+1]||(inputData[b+1]=[]);c=stickData[b]||(stickData[b]=[]);if(d){var e=g=>.3<g?percent(g,.3,.8):-.3>g?-percent(-g,.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],k[e]=f.pressed?1+2*!gamepadIsDown(e,b):4*gamepadIsDown(e,b),isUsingGamepad|=!b&&f.pressed,touchGamepadEnable&&touchGamepadTimer.unset();gamepadDirectionEmulateStick&&(d=vec2(gamepadIsDown(15,b)-gamepadIsDown(14,b),gamepadIsDown(12,b)-gamepadIsDown(13,b)),d.lengthSquared()&&(c[0]=d.clampLength()))}}}const vibrate=a=>vibrateEnable&&navigator&&navigator.vibrate&&navigator.vibrate(a),vibrateStop=()=>vibrate(0),isTouchDevice=void 0!==window.ontouchstart;if(isTouchDevice){let a,b;ontouchstart=ontouchmove=ontouchend=c=>{c.button=0;const d=c.touches.length;d?(b||zzfx(0,b=1),c.x=c.touches[0].clientX,c.y=c.touches[0].clientY,a?onmousemove(c):onmousedown(c)):a&&onmouseup(c);a=d;return!0}}let touchGamepadTimer=new Timer,touchGamepadButtons=[],touchGamepadStick=vec2();function touchGamepadCreate(){touchGamepadEnable&&isTouchDevice&&(ontouchstart=ontouchmove=ontouchend=a=>{if(touchGamepadEnable){touchGamepadStick=vec2();touchGamepadButtons=[];if(a.touches.length&&(touchGamepadTimer.isSet()||zzfx(0),isUsingGamepad=1,touchGamepadTimer.set(),paused)){touchGamepadButtons[9]=1;return}var b=vec2(touchGamepadSize,mainCanvasSize.y-touchGamepadSize),c=mainCanvasSize.subtract(vec2(touchGamepadSize,touchGamepadSize)),d=mainCanvasSize.scale(.5);for(const e of a.touches)a=mouseToScreen(vec2(e.clientX,e.clientY)),a.distance(b)<touchGamepadSize?touchGamepadAnalog?touchGamepadStick=a.subtract(b).scale(2/touchGamepadSize).clampLength():(a=a.subtract(b).angle(),touchGamepadStick.setAngle((4*a/PI+8.5|0)*PI/4)):a.distance(c)<touchGamepadSize?(a=a.subtract(c).direction(),touchGamepadButtons[a]=1):a.distance(d)<touchGamepadSize&&(touchGamepadButtons[9]=1)}})}function touchGamepadRender(){if(touchGamepadEnable&&touchGamepadTimer.isSet()){var a=percent(touchGamepadTimer,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((new Vector2).setAngle(b*PI/2,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()}}}"use strict";class Sound{constructor(a,b=soundDefaultRange,c=soundDefaultTaper){soundEnable&&(this.range=b,this.taper=c,this.randomness=a[1]||0,a[1]=0,this.cachedSamples=zzfxG(...a))}play(a,b=1,c=1,d=1){if(soundEnable){var e=0;if(a){if(e=this.range){const f=cameraPos.distanceSquared(a);if(f>e*e)return;b*=percent(f**.5,e,e*this.taper)}e=2*worldToScreen(a).x/mainCanvas.width-1}a=c+c*this.randomness*d*rand(-1,1);return playSamples([this.cachedSamples],b,a,e)}}playNote(a,b,c=1){if(soundEnable)return this.play(b,c,2**(a/12),0)}}class Music{constructor(a){soundEnable&&(this.cachedSamples=zzfxM(...a))}play(a=1,b=1){if(soundEnable)return playSamples(this.cachedSamples,a,1,0,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}const speakStop=()=>speechSynthesis&&speechSynthesis.cancel(),getNoteFrequency=(a,b=220)=>b*2**(a/12);let audioContext;function playSamples(a,b=1,c=1,d=0,e=0){if(soundEnable&&(audioContext||=new(window.AudioContext||webkitAudioContext),audioContext.resume(),"running"==audioContext.state)){var f=audioContext.createBuffer(a.length,a[0].length,zzfxR),k=audioContext.createBufferSource();a.forEach((g,h)=>f.getChannelData(h).set(g));k.buffer=f;k.playbackRate.value=c;k.loop=e;a=audioContext.createGain();a.gain.value=soundVolume*b;a.connect(audioContext.destination);(window.StereoPannerNode?k.connect(new StereoPannerNode(audioContext,{pan:clamp(d,-1,1)})):k).connect(a);k.start();return k}}const zzfx=(...a)=>playSamples([zzfxG(...a)]),zzfxR=44100;function zzfxG(a=1,b=.05,c=220,d=0,e=0,f=.1,k=0,g=1,h=0,n=0,m=0,p=0,l=0,u=0,v=0,A=0,r=0,B=1,y=0,C=0){let t=2*PI,F=h*=500*t/zzfxR/zzfxR,z=[];b=c*=(1+b*rand(-1,1))*t/zzfxR;let w=0,D=0,q=0,x=1,G=0,I=0,E=0,J,H;d=d*zzfxR+9;y*=zzfxR;e*=zzfxR;f*=zzfxR;r*=zzfxR;n*=500*t/zzfxR**3;v*=t/zzfxR;m*=t/zzfxR;p*=zzfxR;l=l*zzfxR|0;for(H=d+y+e+f+r|0;q<H;z[q++]=E)++I%(100*A|0)||(E=k?1<k?2<k?3<k?Math.sin((w%t)**3):max(min(Math.tan(w),1),-1):1-(2*w/t%2+2)%2:1-4*abs(Math.round(w/t)-w/t):Math.sin(w),E=(l?1-C+C*Math.sin(t*q/l):1)*sign(E)*abs(E)**g*a*soundVolume*(q<d?q/d:q<d+y?1-(q-d)/y*(1-B):q<d+y+e?B:q<H-r?(H-q-r)/f*B:0),E=r?E/2+(r>q?0:(q<H-r?1:(H-q)/r)*z[q-r|0]/2):E),J=(c+=h+=n)*Math.cos(v*D++),w+=J-J*u*(1-1e9*(Math.sin(q)+1)%2),x&&++x>p&&(c+=m,b+=m,x=0),!l||++G%l||(c=b,h=F,x=x||1);return z}function zzfxM(a,b,c,d=125){let e,f,k,g,h,n,m,p,l,u,v,A,r,B=0,y,C=[],t=[],F=[],z=0,w=0,D=1,q={},x=zzfxR/d*60>>2;for(;D;z++)C=[D=p=A=0],c.forEach((G,I)=>{m=b[G][z]||[0,0,0];D|=!!b[G][z];y=A+(b[G][0].length-2-!p)*x;r=I==c.length-1;f=2;for(g=A;f<m.length+r;p=++f){h=m[f];l=f==m.length+r-1&&r||u!=(m[0]||0)|h|0;for(k=0;k<x&&p;k++>x-99&&l?v+=(1>v)/99:0)n=(1-v)*C[B++]/2||0,t[g]=(t[g]||0)-n*w+n,F[g]=(F[g++]||0)+n*w+n;h&&(v=h%1,w=m[1]||0,h|=0)&&(C=q[[u=m[B=0]||0,h]]=q[[u,h]]||(e=[...a[u]],e[2]*=2**((h-12)/12),0<h?zzfxG(...e):[]))}A=y});return[t,F]}"use strict";let tileCollision=[],tileCollisionSize=vec2();function initTileCollision(a){tileCollisionSize=a;tileCollision=[];for(a=tileCollision.length=tileCollisionSize.area();a--;)tileCollision[a]=0}const setTileCollisionData=(a,b=0)=>a.arrayCheck(tileCollisionSize)&&(tileCollision[(a.y|0)*tileCollisionSize.x+a.x|0]=b),getTileCollisionData=a=>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 k=tileCollision[e*tileCollisionSize.x+b];if(k&&(!c||c.collideWithTile(k,new Vector2(b,e))))return 1}}function tileCollisionRaycast(a,b,c){a=a.floor();b=b.floor();var d=b.subtract(a);const e=abs(d.x),f=-abs(d.y),k=sign(d.x);d=sign(d.y);let g=e+f;for(let n=a.x,m=a.y;;){var h=getTileCollisionData(vec2(n,m));if(h&&(c?c.collideWithTileRaycast(h,new Vector2(n,m)):0<h))return debugRaycast&&debugLine(a,b,"#f00",.02,1),debugRaycast&&debugPoint(new Vector2(n+.5,m+.5),"#ff0",1),new Vector2(n+.5,m+.5);if(n==b.x&m==b.y)break;h=2*g;h>=f&&(g+=f,n+=k);h<=e&&(g+=e,m+=d)}debugRaycast&&debugLine(a,b,"#00f",.02,1)}class TileLayerData{constructor(a,b=0,c=0,d=new Color){this.tile=a;this.direction=b;this.mirror=c;this.color=d}clear(){this.tile=this.direction=this.mirror=0;color=new Color}}class TileLayer extends EngineObject{constructor(a,b=tileCollisionSize,c=tileSizeDefault,d=vec2(1),e=0){super(a,b,-1,c,0,void 0,e);this.canvas=document.createElement("canvas");this.context=this.canvas.getContext("2d");this.scale=d;this.isOverlay;this.data=[];for(a=this.size.area();a--;)this.data.push(new TileLayerData)}setData(a,b,c){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);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(1);this.drawAllTileData();this.redrawEnd()}redrawStart(a=0){a&&(this.canvas.width=this.size.x*this.tileSize.x,this.canvas.height=this.size.y*this.tileSize.y);this.savedRenderSettings=[mainCanvas,mainContext,cameraPos,cameraScale];mainCanvas=this.canvas;mainContext=this.context;cameraPos=this.size.scale(.5);cameraScale=this.tileSize.x;enginePreRender()}redrawEnd(){ASSERT(mainContext==this.context);glEnable&&glCopyToContext(mainContext,1);[mainCanvas,mainContext,cameraPos,cameraScale]=this.savedRenderSettings}drawTileData(a){const b=a.floor().add(this.pos).add(vec2(.5));this.drawCanvas2D(b,vec2(1),0,0,c=>c.clearRect(-.5,-.5,1,1));a=this.getData(a);void 0!=a.tile&&(ASSERT(mainContext==this.context),drawTile(b,vec2(1),a.tile,this.tileSize,a.color,a.direction*PI/2,a.mirror))}drawAllTileData(){for(let a=this.size.x;a--;)for(let b=this.size.y;b--;)this.drawTileData(vec2(a,b))}drawCanvas2D(a,b,c=0,d,e){const f=this.context;f.save();a=a.subtract(this.pos).multiply(this.tileSize);b=b.multiply(this.tileSize);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=-1,d=tileSizeDefault,e=new Color,f,k){this.drawCanvas2D(a,b,f,k,g=>{if(0>c)g.fillStyle=e,g.fillRect(-.5,-.5,1,1);else{const h=tileImage.width/d.x;g.globalAlpha=e.a;g.drawImage(tileImage,c%h*d.x,(c/h|0)*d.y,d.x,d.y,-.5,-.5,1,1)}})}drawRect(a,b,c,d){this.drawTile(a,b,-1,0,c,d)}}"use strict";class ParticleEmitter extends EngineObject{constructor(a,b,c=0,d=0,e=100,f=PI,k=-1,g=tileSizeDefault,h=new Color,n=new Color,m=new Color(1,1,1,0),p=new Color(1,1,1,0),l=.5,u=.1,v=1,A=.1,r=.05,B=1,y=1,C=0,t=PI,F=.1,z=.2,w,D,q=1,x=D?1e9:0,G){super(a,new Vector2,k,g,b,void 0,x);this.emitSize=c;this.emitTime=d;this.emitRate=e;this.emitConeAngle=f;this.colorStartA=h;this.colorStartB=n;this.colorEndA=m;this.colorEndB=p;this.randomColorLinear=q;this.particleTime=l;this.sizeStart=u;this.sizeEnd=v;this.speed=A;this.angleSpeed=r;this.damping=B;this.angleDamping=y;this.gravityScale=C;this.particleConeAngle=t;this.fadeRate=F;this.randomness=z;this.collideTiles=w;this.additive=D;this.localSpace=G;this.emitTimeBuffer=this.trailScale=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=void 0!=this.emitSize.x?new Vector2(rand(-.5,.5),rand(-.5,.5)).multiply(this.emitSize).rotate(this.angle):randInCircle(.5*this.emitSize),b=rand(this.particleConeAngle,-this.particleConeAngle);this.localSpace||(a=this.pos.add(a),b+=this.angle);a=new Particle(a,this.tileIndex,this.tileSize,b);const c=this.randomness;var d=m=>m+m*rand(c,-c);b=d(this.particleTime);const e=d(this.sizeStart),f=d(this.sizeEnd),k=d(this.speed);d=d(this.angleSpeed)*randSign();var g=rand(this.emitConeAngle,-this.emitConeAngle);const h=randColor(this.colorStartA,this.colorStartB,this.randomColorLinear),n=randColor(this.colorEndA,this.colorEndB,this.randomColorLinear);g=this.localSpace?g:this.angle+g;a.colorStart=h;a.colorEndDelta=n.subtract(h);a.velocity=(new Vector2).setAngle(g,k);a.angleVelocity=d;a.lifeTime=b;a.sizeStart=e;a.sizeEndDelta=f-e;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.additive=this.additive;a.renderOrder=this.renderOrder;a.trailScale=this.trailScale;a.mirror=.5>rand();a.localSpaceEmitter=this.localSpace&&this;a.destroyCallback=this.particleDestroyCallback;this.particleCreateCallback&&this.particleCreateCallback(a);return a}render(){}}class Particle extends EngineObject{constructor(a,b,c,d){super(a,new Vector2,b,c,d)}render(){const a=min((time-this.spawnTime)/this.lifeTime,1);var b=this.sizeStart+a*this.sizeEndDelta;b=new Vector2(b,b);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(1);let d=this.pos;var 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));e=f.length();f=f.scale(1/e);const k=e*this.trailScale;b.y=max(b.x,k);e=f.angle();drawTile(d.add(f.multiply(vec2(0,-k/2))),b,this.tileIndex,this.tileSize,c,e,this.mirror)}else drawTile(d,b,this.tileIndex,this.tileSize,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)}}"use strict";const medals=[];let medalsPreventUnlock,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;this.image=new Image;e&&(this.image.src=e)}unlock(){medalsPreventUnlock||this.unlocked||(ASSERT(medalsSaveName),localStorage[this.storageKey()]=this.unlocked=1,medalsDisplayQueue.push(this),newgrounds&&newgrounds.unlockMedal(this.id),localStorage["OS13kTrophy,"+this.icon+","+medalsSaveName+","+this.name]=this.description)}render(a=0){const b=overlayContext,c=min(medalDisplayWidth,mainCanvas.width),d=overlayCanvas.width-c;a*=-medalDisplayHeight;b.save();b.beginPath();b.fillStyle="#ddd";b.fill(b.rect(d,a,c,medalDisplayHeight));b.strokeStyle="#000";b.lineWidth=3;b.stroke();b.clip();this.renderIcon(d+15+medalDisplayIconSize/2,a+medalDisplayHeight/2);b.textAlign="left";b.font="38px "+fontDefault;b.fillText(this.name,d+medalDisplayIconSize+30,a+28);b.font="24px "+fontDefault;b.fillText(this.description,d+medalDisplayIconSize+30,a+60);b.restore()}renderIcon(a,b,c=medalDisplayIconSize){const d=overlayContext;d.fillStyle="#000";d.textAlign="center";d.textBaseline="middle";d.font=.7*c+"px "+fontDefault;this.image.src?d.drawImage(this.image,a-c/2,b-c/2,c,c):d.fillText(this.icon,a,b)}storageKey(){return medalsSaveName+"_"+this.id}}function medalsRender(){if(medalsDisplayQueue.length){var a=medalsDisplayQueue[0],b=timeReal-medalsDisplayTimeLast;if(medalsDisplayTimeLast)if(b>medalDisplayTime)medalsDisplayQueue.shift(medalsDisplayTimeLast=0);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){newgrounds=new Newgrounds(a,b)}class Newgrounds{constructor(a,b){ASSERT(!newgrounds&&a);this.app_id=a;this.cipher=b;this.host=location?location.hostname:"";b&&(this.cryptoJS=CryptoJS());this.session_id=new URL(location.href).searchParams.get("ngio_session_id")||0;if(0!=this.session_id){this.medals=(a=this.call("Medal.getList"))?a.result.data.medals:[];debugMedals&&console.log(this.medals);for(var c of this.medals)if(a=medals[c.id])a.image.src=c.icon,a.name=c.name,a.description=c.description,a.unlocked=c.unlocked,a.difficulty=c.difficulty,a.value=c.value,a.value&&(a.description=a.description+" ("+a.value+")");this.scoreboards=(c=this.call("ScoreBoard.getBoards"))?c.result.data.scoreboards:[];debugMedals&&console.log(this.scoreboards);setInterval(()=>this.call("Gateway.ping",0,1),3e5)}}unlockMedal(a){return this.call("Medal.unlock",{id:a},1)}postScore(a,b){return this.call("ScoreBoard.postScore",{id:a,value:b},1)}getScores(a,b=0,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},1)}call(a,b=0,c=0){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)}}const CryptoJS=()=>eval(Function("[M='GBMGXz^oVYPPKKbB`agTXU|LxPc_ZBcMrZvCr~wyGfWrwk@ATqlqeTp^N?p{we}jIpEnB_sEr`l?YDkDhWhprc|Er|XETG?pTl`e}dIc[_N~}fzRycIfpW{HTolvoPB_FMe_eH~BTMx]yyOhv?biWPCGc]kABencBhgERHGf{OL`Dj`c^sh@canhy[secghiyotcdOWgO{tJIE^JtdGQRNSCrwKYciZOa]Y@tcRATYKzv|sXpboHcbCBf`}SKeXPFM|RiJsSNaIb]QPc[D]Jy_O^XkOVTZep`ONmntLL`Qz~UupHBX_Ia~WX]yTRJIxG`ioZ{fefLJFhdyYoyLPvqgH?b`[TMnTwwfzDXhfM?rKs^aFr|nyBdPmVHTtAjXoYUloEziWDCw_suyYT~lSMksI~ZNCS[Bex~j]Vz?kx`gdYSEMCsHpjbyxQvw|XxX_^nQYue{sBzVWQKYndtYQMWRef{bOHSfQhiNdtR{o?cUAHQAABThwHPT}F{VvFmgN`E@FiFYS`UJmpQNM`X|tPKHlccT}z}k{sACHL?Rt@MkWplxO`ASgh?hBsuuP|xD~LSH~KBlRs]t|l|_tQAroDRqWS^SEr[sYdPB}TAROtW{mIkE|dWOuLgLmJrucGLpebrAFKWjikTUzS|j}M}szasKOmrjy[?hpwnEfX[jGpLt@^v_eNwSQHNwtOtDgWD{rk|UgASs@mziIXrsHN_|hZuxXlPJOsA^^?QY^yGoCBx{ekLuZzRqQZdsNSx@ezDAn{XNj@fRXIwrDX?{ZQHwTEfu@GhxDOykqts|n{jOeZ@c`dvTY?e^]ATvWpb?SVyg]GC?SlzteilZJAL]mlhLjYZazY__qcVFYvt@|bIQnSno@OXyt]OulzkWqH`rYFWrwGs`v|~XeTsIssLrbmHZCYHiJrX}eEzSssH}]l]IhPQhPoQ}rCXLyhFIT[clhzYOvyHqigxmjz`phKUU^TPf[GRAIhNqSOdayFP@FmKmuIzMOeoqdpxyCOwCthcLq?n`L`tLIBboNn~uXeFcPE{C~mC`h]jUUUQe^`UqvzCutYCgct|SBrAeiYQW?X~KzCz}guXbsUw?pLsg@hDArw?KeJD[BN?GD@wgFWCiHq@Ypp_QKFixEKWqRp]oJFuVIEvjDcTFu~Zz]a{IcXhWuIdMQjJ]lwmGQ|]g~c]Hl]pl`Pd^?loIcsoNir_kikBYyg?NarXZEGYspt_vLBIoj}LI[uBFvm}tbqvC|xyR~a{kob|HlctZslTGtPDhBKsNsoZPuH`U`Fqg{gKnGSHVLJ^O`zmNgMn~{rsQuoymw^JY?iUBvw_~mMr|GrPHTERS[MiNpY[Mm{ggHpzRaJaoFomtdaQ_?xuTRm}@KjU~RtPsAdxa|uHmy}n^i||FVL[eQAPrWfLm^ndczgF~Nk~aplQvTUpHvnTya]kOenZlLAQIm{lPl@CCTchvCF[fI{^zPkeYZTiamoEcKmBMfZhk_j_~Fjp|wPVZlkh_nHu]@tP|hS@^G^PdsQ~f[RqgTDqezxNFcaO}HZhb|MMiNSYSAnQWCDJukT~e|OTgc}sf[cnr?fyzTa|EwEtRG|I~|IO}O]S|rp]CQ}}DWhSjC_|z|oY|FYl@WkCOoPuWuqr{fJu?Brs^_EBI[@_OCKs}?]O`jnDiXBvaIWhhMAQDNb{U`bqVR}oqVAvR@AZHEBY@depD]OLh`kf^UsHhzKT}CS}HQKy}Q~AeMydXPQztWSSzDnghULQgMAmbWIZ|lWWeEXrE^EeNoZApooEmrXe{NAnoDf`m}UNlRdqQ@jOc~HLOMWs]IDqJHYoMziEedGBPOxOb?[X`KxkFRg@`mgFYnP{hSaxwZfBQqTm}_?RSEaQga]w[vxc]hMne}VfSlqUeMo_iqmd`ilnJXnhdj^EEFifvZyxYFRf^VaqBhLyrGlk~qowqzHOBlOwtx?i{m~`n^G?Yxzxux}b{LSlx]dS~thO^lYE}bzKmUEzwW^{rPGhbEov[Plv??xtyKJshbG`KuO?hjBdS@Ru}iGpvFXJRrvOlrKN?`I_n_tplk}kgwSXuKylXbRQ]]?a|{xiT[li?k]CJpwy^o@ebyGQrPfF`aszGKp]baIx~H?ElETtFh]dz[OjGl@C?]VDhr}OE@V]wLTc[WErXacM{We`F|utKKjgllAxvsVYBZ@HcuMgLboFHVZmi}eIXAIFhS@A@FGRbjeoJWZ_NKd^oEH`qgy`q[Tq{x?LRP|GfBFFJV|fgZs`MLbpPYUdIV^]mD@FG]pYAT^A^RNCcXVrPsgk{jTrAIQPs_`mD}rOqAZA[}RETFz]WkXFTz_m{N@{W@_fPKZLT`@aIqf|L^Mb|crNqZ{BVsijzpGPEKQQZGlApDn`ruH}cvF|iXcNqK}cxe_U~HRnKV}sCYb`D~oGvwG[Ca|UaybXea~DdD~LiIbGRxJ_VGheI{ika}KC[OZJLn^IBkPrQj_EuoFwZ}DpoBRcK]Q}?EmTv~i_Tul{bky?Iit~tgS|o}JL_VYcCQdjeJ_MfaA`FgCgc[Ii|CBHwq~nbJeYTK{e`CNstKfTKPzw{jdhp|qsZyP_FcugxCFNpKitlR~vUrx^NrSVsSTaEgnxZTmKc`R|lGJeX}ccKLsQZQhsFkeFd|ckHIVTlGMg`~uPwuHRJS_CPuN_ogXe{Ba}dO_UBhuNXby|h?JlgBIqMKx^_u{molgL[W_iavNQuOq?ap]PGB`clAicnl@k~pA?MWHEZ{HuTLsCpOxxrKlBh]FyMjLdFl|nMIvTHyGAlPogqfZ?PlvlFJvYnDQd}R@uAhtJmDfe|iJqdkYr}r@mEjjIetDl_I`TELfoR|qTBu@Tic[BaXjP?dCS~MUK[HPRI}OUOwAaf|_}HZzrwXvbnNgltjTwkBE~MztTQhtRSWoQHajMoVyBBA`kdgK~h`o[J`dm~pm]tk@i`[F~F]DBlJKklrkR]SNw@{aG~Vhl`KINsQkOy?WhcqUMTGDOM_]bUjVd|Yh_KUCCgIJ|LDIGZCPls{RzbVWVLEhHvWBzKq|^N?DyJB|__aCUjoEgsARki}j@DQXS`RNU|DJ^a~d{sh_Iu{ONcUtSrGWW@cvUjefHHi}eSSGrNtO?cTPBShLqzwMVjWQQCCFB^culBjZHEK_{dO~Q`YhJYFn]jq~XSnG@[lQr]eKrjXpG~L^h~tDgEma^AUFThlaR{xyuP@[^VFwXSeUbVetufa@dX]CLyAnDV@Bs[DnpeghJw^?UIana}r_CKGDySoRudklbgio}kIDpA@McDoPK?iYcG?_zOmnWfJp}a[JLR[stXMo?_^Ng[whQlrDbrawZeSZ~SJstIObdDSfAA{MV}?gNunLOnbMv_~KFQUAjIMj^GkoGxuYtYbGDImEYiwEMyTpMxN_LSnSMdl{bg@dtAnAMvhDTBR_FxoQgANniRqxd`pWv@rFJ|mWNWmh[GMJz_Nq`BIN@KsjMPASXORcdHjf~rJfgZYe_uulzqM_KdPlMsuvU^YJuLtofPhGonVOQxCMuXliNvJIaoC?hSxcxKVVxWlNs^ENDvCtSmO~WxI[itnjs^RDvI@KqG}YekaSbTaB]ki]XM@[ZnDAP~@|BzLRgOzmjmPkRE@_sobkT|SszXK[rZN?F]Z_u}Yue^[BZgLtR}FHzWyxWEX^wXC]MJmiVbQuBzkgRcKGUhOvUc_bga|Tx`KEM`JWEgTpFYVeXLCm|mctZR@uKTDeUONPozBeIkrY`cz]]~WPGMUf`MNUGHDbxZuO{gmsKYkAGRPqjc|_FtblEOwy}dnwCHo]PJhN~JoteaJ?dmYZeB^Xd?X^pOKDbOMF@Ugg^hETLdhwlA}PL@_ur|o{VZosP?ntJ_kG][g{Zq`Tu]dzQlSWiKfnxDnk}KOzp~tdFstMobmy[oPYjyOtUzMWdjcNSUAjRuqhLS@AwB^{BFnqjCmmlk?jpn}TksS{KcKkDboXiwK]qMVjm~V`LgWhjS^nLGwfhAYrjDSBL_{cRus~{?xar_xqPlArrYFd?pHKdMEZzzjJpfC?Hv}mAuIDkyBxFpxhstTx`IO{rp}XGuQ]VtbHerlRc_LFGWK[XluFcNGUtDYMZny[M^nVKVeMllQI[xtvwQnXFlWYqxZZFp_|]^oWX[{pOMpxXxvkbyJA[DrPzwD|LW|QcV{Nw~U^dgguSpG]ClmO@j_TENIGjPWwgdVbHganhM?ema|dBaqla|WBd`poj~klxaasKxGG^xbWquAl~_lKWxUkDFagMnE{zHug{b`A~IYcQYBF_E}wiA}K@yxWHrZ{[d~|ARsYsjeNWzkMs~IOqqp[yzDE|WFrivsidTcnbHFRoW@XpAV`lv_zj?B~tPCppRjgbbDTALeFaOf?VcjnKTQMLyp{NwdylHCqmo?oelhjWuXj~}{fpuX`fra?GNkDiChYgVSh{R[BgF~eQa^WVz}ATI_CpY?g_diae]|ijH`TyNIF}|D_xpmBq_JpKih{Ba|sWzhnAoyraiDvk`h{qbBfsylBGmRH}DRPdryEsSaKS~tIaeF[s]I~xxHVrcNe@Jjxa@jlhZueLQqHh_]twVMqG_EGuwyab{nxOF?`HCle}nBZzlTQjkLmoXbXhOtBglFoMz?eqre`HiE@vNwBulglmQjj]DB@pPkPUgA^sjOAUNdSu_`oAzar?n?eMnw{{hYmslYi[TnlJD'",..."]charCodeAtUinyxpf","for(;e<10359;c[e++]=p-=128,A=A?p-A&&A:p==34&&p)for(p=1;p<128;y=f.map((n,x)=>(U=r[n]*2+1,U=Math.log(U/(h-U)),t-=a[x]*U,U/500)),t=~-h/(1+Math.exp(t))|1,i=o%h<t,o=o%h+(i?t:h-t)*(o>>17)-!i*t,f.map((n,x)=>(U=r[n]+=(i*h/2-r[n]<<13)/((C[n]+=C[n]<5)+1/20)>>13,a[x]+=y[x]*(i-t/h))),p=p*2+i)for(f='010202103203210431053105410642065206541'.split(t=0).map((n,x)=>(U=0,[...n].map((n,x)=>(U=U*997+(c[e-n]|0)|0)),h*32-1&U*997+p+!!A*129)*12+x);o<h*32;o=o*64|M.charCodeAt(d++)&63);for(C=String.fromCharCode(...c);r=/[\0-#?@\\\\~]/.exec(C);)with(C.split(r))C=join(shift());return C")([],[],131072,[0,0,0,0,0,0,0,0,0,0,0,0],new Uint16Array(51e6).fill(32768),new Uint8Array(51e6),0,0,0,0));"use strict";let glCanvas,glContext,glTileTexture,glActiveTexture,glShader,glArrayBuffer,glVertexData,glPositionData,glColorData,glBatchCount,glBatchAdditive,glAdditive;function glInit(){glCanvas=document.createElement("canvas");glContext=glCanvas.getContext("webgl",{antialias:!1});glCanvas.style=styleCanvas;glTileTexture=glCreateTexture(tileImage);glOverlay&&document.body.appendChild(glCanvas);glShader=glCreateProgram("precision highp float;uniform mat4 m;attribute vec2 p,t;attribute vec4 c,a;varying vec2 v;varying vec4 d,e;void main(){gl_Position=m*vec4(p,1,1);v=t;d=c;e=a;}","precision highp float;varying vec2 v;varying vec4 d,e;uniform sampler2D s;void main(){gl_FragColor=texture2D(s,v)*d+e;}");glVertexData=new ArrayBuffer(gl_MAX_BATCH*gl_VERTICES_PER_QUAD*gl_VERTEX_BYTE_STRIDE);glArrayBuffer=glContext.createBuffer();glPositionData=new Float32Array(glVertexData);glColorData=new Uint32Array(glVertexData);glBatchCount=0}function glSetBlendMode(a){glAdditive=a}function glSetTexture(a=glTileTexture){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&&a.width&&glContext.texImage2D(gl_TEXTURE_2D,0,gl_RGBA,gl_RGBA,gl_UNSIGNED_BYTE,a);glContext.texParameteri(gl_TEXTURE_2D,gl_TEXTURE_MIN_FILTER,cavasPixelated?gl_NEAREST:gl_LINEAR);glContext.texParameteri(gl_TEXTURE_2D,gl_TEXTURE_MAG_FILTER,cavasPixelated?gl_NEAREST:gl_LINEAR);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 glPreRender(a,b,c,d,e){glContext.viewport(0,0,glCanvas.width=a,glCanvas.height=b);glContext.clear(gl_COLOR_BUFFER_BIT);glContext.useProgram(glShader);glContext.activeTexture(gl_TEXTURE0);glContext.bindTexture(gl_TEXTURE_2D,glActiveTexture=glTileTexture);glContext.bindBuffer(gl_ARRAY_BUFFER,glArrayBuffer);glContext.bufferData(gl_ARRAY_BUFFER,glVertexData.byteLength,gl_DYNAMIC_DRAW);glSetBlendMode();let f=0;const k=(g,h,n,m,p=0)=>{g=glContext.getAttribLocation(glShader,g);glContext.enableVertexAttribArray(g);glContext.vertexAttribPointer(g,m,h,p,gl_VERTEX_BYTE_STRIDE,f);f+=m*n};k("p",gl_FLOAT,4,2);k("t",gl_FLOAT,4,2);k("c",gl_UNSIGNED_BYTE,1,4,1);k("a",gl_UNSIGNED_BYTE,1,4,1);a=2*e/a;b=2*e/b;glContext.uniformMatrix4fv(glContext.getUniformLocation(glShader,"m"),0,new Float32Array([a,0,0,0,0,b,0,0,1,1,-1,1,-1-a*c,-1-b*d,0,0]))}function glFlush(){if(glBatchCount){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.subarray(0,glBatchCount*gl_VERTICES_PER_QUAD*gl_INDICIES_PER_VERT));glContext.drawArrays(gl_TRIANGLES,0,glBatchCount*gl_VERTICES_PER_QUAD);glBatchCount=0;glBatchAdditive=glAdditive}}function glCopyToContext(a,b){if(glBatchCount||b)glFlush(),glOverlay&&!b||a.drawImage(glCanvas,0,0)}function glDraw(a,b,c,d,e,f,k,g,h,n=4294967295,m=0){glBatchCount!=gl_MAX_BATCH&&glBatchAdditive==glAdditive||glFlush();var p=Math.cos(e)/2,l=Math.sin(e)/2;e=p*c;p*=d;c*=l;d*=l;l=glBatchCount++*gl_VERTICES_PER_QUAD*gl_INDICIES_PER_VERT;glPositionData[l++]=a-e-d;glPositionData[l++]=b-p+c;glPositionData[l++]=f;glPositionData[l++]=h;glColorData[l++]=n;glColorData[l++]=m;glPositionData[l++]=a+e+d;glPositionData[l++]=b+p-c;glPositionData[l++]=g;glPositionData[l++]=k;glColorData[l++]=n;glColorData[l++]=m;glPositionData[l++]=a-e+d;glPositionData[l++]=b+p+c;glPositionData[l++]=f;glPositionData[l++]=k;glColorData[l++]=n;glColorData[l++]=m;glPositionData[l++]=a-e-d;glPositionData[l++]=b-p+c;glPositionData[l++]=f;glPositionData[l++]=h;glColorData[l++]=n;glColorData[l++]=m;glPositionData[l++]=a+e-d;glPositionData[l++]=b-p-c;glPositionData[l++]=g;glPositionData[l++]=h;glColorData[l++]=n;glColorData[l++]=m;glPositionData[l++]=a+e+d;glPositionData[l++]=b+p-c;glPositionData[l++]=g;glPositionData[l++]=k;glColorData[l++]=n;glColorData[l++]=m}let glPostShader,glPostArrayBuffer,glPostTexture;function glInitPostProcess(a){ASSERT(!glPostShader);a||="void mainImage(out vec4 c,vec2 p){c=texture2D(iChannel0,p/iResolution.xy);}";glPostShader=glCreateProgram("precision highp float;attribute vec2 p;void main(){gl_Position=vec4(p,1,1);}","precision highp float;uniform sampler2D iChannel0;uniform vec3 iResolution;uniform float iTime;\n"+a+"\nvoid main(){mainImage(gl_FragColor,gl_FragCoord.xy);gl_FragColor.a=1.;}");glPostArrayBuffer=glContext.createBuffer();glPostTexture=glCreateTexture();mainCanvas.style.visibility="hidden"}function glRenderPostProcess(){if(glPostShader){var a=mainCanvas.width,b=mainCanvas.height;glEnable?(glFlush(),mainContext.drawImage(glCanvas,0,0)):glContext.viewport(0,0,glCanvas.width=a,glCanvas.height=b);glContext.useProgram(glPostShader);glContext.disable(gl_BLEND);glContext.bindBuffer(gl_ARRAY_BUFFER,glPostArrayBuffer);glContext.bufferData(gl_ARRAY_BUFFER,new Float32Array([-3,1,1,-3,1,1]),gl_STATIC_DRAW);glContext.pixelStorei(gl_UNPACK_FLIP_Y_WEBGL,!0);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 c=glContext.getAttribLocation(glPostShader,"p");glContext.enableVertexAttribArray(c);glContext.vertexAttribPointer(c,2,gl_FLOAT,0,8,0);glContext.uniform1i(glContext.getUniformLocation(glPostShader,"iChannel0"),0);glContext.uniform1f(glContext.getUniformLocation(glPostShader,"iTime"),time);glContext.uniform3f(glContext.getUniformLocation(glPostShader,"iResolution"),a,b,1);glContext.drawArrays(gl_TRIANGLES,0,3)}}const gl_ONE=1,gl_TRIANGLES=4,gl_SRC_ALPHA=770,gl_ONE_MINUS_SRC_ALPHA=771,gl_BLEND=3042,gl_TEXTURE_2D=3553,gl_UNSIGNED_BYTE=5121,gl_BYTE=5120,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_TEXTURE1=33985,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_VERTICES_PER_QUAD=6,gl_INDICIES_PER_VERT=6,gl_MAX_BATCH=65536,gl_VERTEX_BYTE_STRIDE=24;
|
|
1
|
+
let showWatermark=0,godMode=0;const debug=0,debugOverlay=0,debugPhysics=0,debugParticles=0,debugRaycast=0,debugGamepads=0,debugMedals=0,ASSERT=()=>{},debugInit=()=>{},debugUpdate=()=>{},debugRender=()=>{},debugRect=()=>{},debugCircle=()=>{},debugPoint=()=>{},debugLine=()=>{},debugAABB=()=>{},debugText=()=>{},debugClear=()=>{},debugSaveCanvas=()=>{};"use strict";const PI=Math.PI,abs=a=>0>a?-a:a,min=(a,b)=>a<b?a:b,max=(a,b)=>a>b?a:b,sign=a=>0>a?-1:1,mod=(a,b=1)=>(a%b+b)%b,clamp=(a,b=0,c=1)=>a<b?b:a>c?c:a,percent=(a,b=0,c=1)=>c-b?clamp((a-b)/(c-b)):0,lerp=(a,b=0,c=1)=>b+clamp(a)*(c-b),smoothStep=a=>a*a*(3-2*a),nearestPowerOfTwo=a=>2**Math.ceil(Math.log2(a)),isOverlapping=(a,b,c,d)=>2*abs(a.x-c.x)<b.x+d.x&2*abs(a.y-c.y)<b.y+d.y,wave=(a=1,b=1,c=time)=>b/2*(1-Math.cos(c*a*2*PI)),formatTime=a=>(a/60|0)+":"+(10>a%60?"0":"")+(a%60|0),rand=(a=1,b=0)=>b+(a-b)*Math.random(),randInt=(a=1,b=0)=>rand(a,b)|0,randSign=()=>2*(rand(2)|0)-1,randInCircle=(a=1,b=0)=>0<a?randVector(a*rand(b/a,1)**.5):new Vector2,randVector=(a=1)=>(new Vector2).setAngle(rand(2*PI),a),randColor=(a=new Color,b=new Color(0,0,0,1),c)=>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));let randSeed=1;const randSeeded=(a=1,b=0)=>{randSeed^=randSeed<<13;randSeed^=randSeed>>>17;randSeed^=randSeed<<5;return b+(a-b)*abs(randSeed%1e9)/1e9},vec2=(a=0,b)=>void 0==a.x?new Vector2(a,void 0==b?a:b):new Vector2(a.x,a.y);class Vector2{constructor(a=0,b=0){this.x=a;this.y=b}copy(){return new Vector2(this.x,this.y)}add(a){ASSERT(void 0!=a.x);return new Vector2(this.x+a.x,this.y+a.y)}subtract(a){ASSERT(void 0!=a.x);return new Vector2(this.x-a.x,this.y-a.y)}multiply(a){ASSERT(void 0!=a.x);return new Vector2(this.x*a.x,this.y*a.y)}divide(a){ASSERT(void 0!=a.x);return new Vector2(this.x/a.x,this.y/a.y)}scale(a){ASSERT(void 0==a.x);return new Vector2(this.x*a,this.y*a)}length(){return this.lengthSquared()**.5}lengthSquared(){return this.x**2+this.y**2}distance(a){return this.distanceSquared(a)**.5}distanceSquared(a){return(this.x-a.x)**2+(this.y-a.y)**2}normalize(a=1){const b=this.length();return b?this.scale(a/b):new Vector2(0,a)}clampLength(a=1){const b=this.length();return b>a?this.scale(a/b):this}dot(a){ASSERT(void 0!=a.x);return this.x*a.x+this.y*a.y}cross(a){ASSERT(void 0!=a.x);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)}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(void 0!=a.x);return this.add(a.subtract(this).scale(clamp(b)))}arrayCheck(a){return 0<=this.x&&0<=this.y&&this.x<a.x&&this.y<a.y}toString(a=3){return`(${(0>this.x?"":" ")+this.x.toFixed(a)},${(0>this.y?"":" ")+this.y.toFixed(a)} )`}}const colorRGBA=(a,b,c,d)=>new Color(a,b,c,d),colorHSLA=(a,b,c,d)=>(new Color).setHSLA(a,b,c,d);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){return new Color(this.r+a.r,this.g+a.g,this.b+a.b,this.a+a.a)}subtract(a){return new Color(this.r-a.r,this.g-a.g,this.b-a.b,this.a-a.a)}multiply(a){return new Color(this.r*a.r,this.g*a.g,this.b*a.b,this.a*a.a)}divide(a){return new Color(this.r/a.r,this.g/a.g,this.b/a.b,this.a/a.a)}scale(a,b=a){return new Color(this.r*a,this.g*a,this.b*a,this.a*b)}clamp(){return new Color(clamp(this.r),clamp(this.g),clamp(this.b),clamp(this.a))}lerp(a,b){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,h,g)=>(g=(g%1+1)%1)<1/6?f+6*(h-f)*g:.5>g?h:g<2/3?f+(h-f)*(2/3-g)*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),h=(e+f)/2;let g=0,k=0;if(e!=f){let n=e-f;k=.5<h?n/(2-e-f):n/(e+f);a==e?g=(b-c)/n+(b<c?6:0):b==e?g=(c-a)/n+2:c==e&&(g=(a-b)/n+4)}return[g/6,k,h,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=1){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)|0)<<8,c=(255*clamp(this.b)|0)<<16,d=(255*clamp(this.a)|0)<<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.unset()?"unset":Math.abs(this.get())+" seconds "+(0>this.get()?"before":"after")}valueOf(){return this.get()}}"use strict";let canvasMaxSize=vec2(1920,1200),canvasFixedSize=vec2(),cavasPixelated=1,fontDefault="arial",tileSizeDefault=vec2(16),tileFixBleedScale=.3,objectDefaultSize=vec2(1),enablePhysicsSolver=1,objectDefaultMass=1,objectDefaultDamping=.99,objectDefaultAngleDamping=.99,objectDefaultElasticity=0,objectDefaultFriction=.8,objectMaxSpeed=1,gravity=0,particleEmitRateScale=1,cameraPos=vec2(),cameraScale=max(tileSizeDefault.x,tileSizeDefault.y),glEnable=1,glOverlay=1,gamepadsEnable=1,gamepadDirectionEmulateStick=1,inputWASDEmulateDirection=1,touchGamepadEnable=0,touchGamepadAnalog=1,touchGamepadSize=99,touchGamepadAlpha=.3,vibrateEnable=1,soundVolume=.5,soundEnable=1,soundDefaultRange=30,soundDefaultTaper=.7,medalDisplayTime=5,medalDisplaySlideTime=.5,medalDisplayWidth=640,medalDisplayHeight=80,medalDisplayIconSize=50;"use strict";const engineName="LittleJS",engineVersion="1.4.7",frameRate=60,timeDelta=1/frameRate;let engineObjects=[],engineObjectsCollide=[],frame=0,time=0,timeReal=0,paused=0,tileImageSize,tileImageFixBleed,averageFPS,drawCount;function engineInit(a,b,c,d,e,f){tileImage.onerror=tileImage.onload=()=>{tileImageFixBleed=vec2(tileFixBleedScale).divide(tileImageSize=vec2(tileImage.width,tileImage.height));debug&&(tileImage.onload=()=>ASSERT(1));document.body.style="margin:0;overflow:hidden;background:#000;touch-action:none;user-select:none;-webkit-user-select:none;-moz-user-select: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=overlayCanvas.style=mainCanvas.style="position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)";a();touchGamepadCreate();k()};let h=0,g=0;const k=(n=0)=>{var m=n-h;h=n;if(debug||showWatermark)averageFPS=lerp(.05,averageFPS||0,1e3/(m||1));n=debug&&keyIsDown(107);const p=debug&&keyIsDown(109);debug&&(m*=n?5:p?.2:1);timeReal+=m/1e3;g+=!paused*m;n||(g=min(g,50));canvasFixedSize.x?(overlayCanvas.width=mainCanvas.width=canvasFixedSize.x,overlayCanvas.height=mainCanvas.height=canvasFixedSize.y,m=innerWidth/innerHeight,n=mainCanvas.width/mainCanvas.height,mainCanvas.style.width=overlayCanvas.style.width=m<n?"100%":"",mainCanvas.style.height=overlayCanvas.style.height=m<n?"":"100%",glCanvas&&(glCanvas.style.width=mainCanvas.style.width,glCanvas.style.height=mainCanvas.style.height)):(overlayCanvas.width=mainCanvas.width=min(innerWidth,canvasMaxSize.x),overlayCanvas.height=mainCanvas.height=min(innerHeight,canvasMaxSize.y));mainCanvasSize=vec2(mainCanvas.width,mainCanvas.height);if(paused)inputUpdate(),debugUpdate(),c(),inputUpdatePost();else{m=0;0>g&&-9<g&&(m=g,g=0);for(;0<=g;g-=1e3/frameRate)inputUpdate(),b(),engineObjectsUpdate(),debugUpdate(),c(),inputUpdatePost();g+=m}enginePreRender();d();engineObjects.sort((r,t)=>r.renderOrder-t.renderOrder);for(var l of engineObjects)l.destroyed||l.render();e();glRenderPostProcess();medalsRender();touchGamepadRender();debugRender();glEnable&&glCopyToContext(mainContext);showWatermark&&(overlayContext.textAlign="right",overlayContext.textBaseline="top",overlayContext.font="1em monospace",overlayContext.fillStyle="#000",l=engineName+" v"+engineVersion+" / "+drawCount+" / "+engineObjects.length+" / "+averageFPS.toFixed(1)+" "+(glEnable?"GL":"2D"),overlayContext.fillText(l,mainCanvas.width-3,3),overlayContext.fillStyle="#fff",overlayContext.fillText(l,mainCanvas.width-2,2),drawCount=0);requestAnimationFrame(k)};f?tileImage.src=f:tileImage.onload()}function enginePreRender(){mainCanvasSize=vec2(mainCanvas.width,mainCanvas.height);mainContext.imageSmoothingEnabled=!cavasPixelated;glEnable&&glPreRender(mainCanvas.width,mainCanvas.height,cameraPos.x,cameraPos.y,cameraScale)}function engineObjectsUpdate(){engineObjectsCollide=engineObjects.filter(b=>b.collideSolidObjects);const a=b=>{if(!b.destroyed){b.update();for(const c of b.children)a(c)}};for(const b of engineObjects)b.parent||a(b);engineObjects=engineObjects.filter(b=>!b.destroyed);time=++frame/frameRate}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(void 0!=b.x)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)}"use strict";class EngineObject{constructor(a=vec2(),b=objectDefaultSize,c=-1,d=tileSizeDefault,e=0,f,h=0){ASSERT(a&&void 0!=a.x&&void 0!=b.x);this.pos=a.copy();this.size=b;this.drawSize;this.tileIndex=c;this.tileSize=d;this.angle=e;this.color=f;this.additiveColor;this.mass=objectDefaultMass;this.damping=objectDefaultDamping;this.angleDamping=objectDefaultAngleDamping;this.elasticity=objectDefaultElasticity;this.friction=objectDefaultFriction;this.gravityScale=1;this.renderOrder=h;this.velocity=new Vector2;this.angleVelocity=0;this.spawnTime=time;this.children=[];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.pos.x+=this.velocity.x*=this.damping,this.pos.y+=this.velocity.y=this.damping*this.velocity.y+gravity*this.gravityScale,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||!isOverlapping(this.pos,this.size,d.pos,d.size)||!this.collideWithObject(d)|!d.collideWithObject(this)))if(isOverlapping(a,this.size,d.pos,d.size)){c=a.subtract(d.pos);var 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")}else{c=this.size.add(d.size);e=2*(a.y-d.pos.y)>c.y+gravity;var f=2*abs(a.y-d.pos.y)<c.y,h=2*abs(a.x-d.pos.x)<c.x;if(e||h||!f)if(this.pos.y=d.pos.y+(c.y/2+.001)*sign(a.y-d.pos.y),d.groundObject&&b||!d.mass)b&&(this.groundObject=d),this.velocity.y*=-this.elasticity;else if(d.mass){const g=(this.mass*this.velocity.y+d.mass*d.velocity.y)/(this.mass+d.mass),k=this.velocity.y*(this.mass-d.mass)/(this.mass+d.mass)+2*d.velocity.y*d.mass/(this.mass+d.mass),n=d.velocity.y*(d.mass-this.mass)/(this.mass+d.mass)+2*this.velocity.y*this.mass/(this.mass+d.mass),m=max(this.elasticity,d.elasticity);this.velocity.y=lerp(m,g,k);d.velocity.y=lerp(m,g,n)}e||!f&&h||(this.pos.x=d.pos.x+(c.x/2+.001)*sign(a.x-d.pos.x),d.mass?(c=(this.mass*this.velocity.x+d.mass*d.velocity.x)/(this.mass+d.mass),e=this.velocity.x*(this.mass-d.mass)/(this.mass+d.mass)+2*d.velocity.x*d.mass/(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),h=max(this.elasticity,d.elasticity),this.velocity.x=lerp(h,c,e),d.velocity.x=lerp(h,c,f)):this.velocity.x*=-this.elasticity);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(new Vector2(a.x,this.pos.y),this.size,this);d=tileCollisionTest(new Vector2(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.tileIndex,this.tileSize,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 1}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=1,b=1,c=1){ASSERT(a||!b);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}}}"use strict";const tileImage=new Image;let mainCanvas,mainContext,overlayCanvas,overlayContext,mainCanvasSize=vec2();const screenToWorld=a=>{ASSERT(mainCanvasSize.x&&mainCanvasSize.y,"mainCanvasSize is invalid");return a.add(vec2(.5)).subtract(mainCanvasSize.scale(.5)).multiply(vec2(1/cameraScale,-1/cameraScale)).add(cameraPos)},worldToScreen=a=>{ASSERT(mainCanvasSize.x&&mainCanvasSize.y,"mainCanvasSize is invalid");return a.subtract(cameraPos).multiply(vec2(cameraScale,-cameraScale)).add(mainCanvasSize.scale(.5)).subtract(vec2(.5))};function drawTile(a,b=vec2(1),c=-1,d=tileSizeDefault,e=new Color,f=0,h,g=new Color(0,0,0,0),k=glEnable){showWatermark&&++drawCount;if(glEnable&&k)if(0>c||!tileImage.width)glDraw(a.x,a.y,b.x,b.y,f,0,0,0,0,0,e.rgbaInt());else{var n=tileImageSize.x/d.x|0;k=d.x/tileImageSize.x;const m=d.y/tileImageSize.y,p=c%n*k;n=(c/n|0)*m;glDraw(a.x,a.y,h?-b.x:b.x,b.y,f,p+tileImageFixBleed.x,n+tileImageFixBleed.y,p-tileImageFixBleed.x+k,n-tileImageFixBleed.y+m,e.rgbaInt(),g.rgbaInt())}else drawCanvas2D(a,b,f,h,m=>{if(0>c)m.fillStyle=e,m.fillRect(-.5,-.5,1,1);else{var p=tileImageSize.x/d.x|0;const l=c%p*d.x+tileFixBleedScale;p=(c/p|0)*d.y+tileFixBleedScale;const r=d.x-2*tileFixBleedScale,t=d.y-2*tileFixBleedScale;m.globalAlpha=e.a;m.drawImage(tileImage,l,p,r,t,-.5,-.5,1,1)}})}function drawRect(a,b,c,d,e){drawTile(a,b,-1,tileSizeDefault,c,d,0,0,e)}function drawTileScreenSpace(a,b=vec2(1),c,d,e,f,h,g,k){drawTile(screenToWorld(a),b.scale(1/cameraScale),c,d,e,f,h,g,k)}function drawRectScreenSpace(a,b,c,d,e){drawTileScreenSpace(a,b,-1,tileSizeDefault,c,d,0,0,e)}function drawLine(a,b,c=.1,d,e){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)}function drawCanvas2D(a,b,c,d,e,f=mainContext){a=worldToScreen(a);b=b.scale(cameraScale);f.save();f.translate(a.x+.5|0,a.y+.5|0);f.rotate(c);f.scale(d?-b.x:b.x,b.y);e(f);f.restore()}function setBlendMode(a,b=glEnable){glEnable&&b?glSetBlendMode(a):mainContext.globalCompositeOperation=a?"lighter":"source-over"}function drawTextScreen(a,b,c=1,d=new Color,e=0,f=new Color(0,0,0),h="center",g=fontDefault,k=overlayContext){k.fillStyle=d;k.lineWidth=e;k.strokeStyle=f;k.textAlign=h;k.font=c+"px "+g;k.textBaseline="middle";k.lineJoin="round";b=b.copy();(a+"").split("\n").forEach(n=>{e&&k.strokeText(n,b.x,b.y);k.fillText(n,b.x,b.y);b.y+=c})}function drawText(a,b,c=1,d,e,f,h,g){drawTextScreen(a,worldToScreen(b),c*cameraScale,d,e*cameraScale,f,h,g,mainContext)}let engineFontImage;class FontImage{constructor(a,b=vec2(8),c=vec2(0,1),d=0,e=overlayContext){a||engineFontImage||(engineFontImage=new Image,engineFontImage.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.startTileIndex=d;this.context=e}drawTextScreen(a,b,c=4,d){const e=this.context;e.save();e.imageSmoothingEnabled=!cavasPixelated;const f=this.tileSize,h=f.add(this.paddingSize).scale(c),g=this.image.width/this.tileSize.x|0;(a+"").split("\n").forEach((k,n)=>{const m=d?k.length*f.x*c/2|0:0;for(let r=k.length;r--;){var p=k[r].charCodeAt();if(32>p||127<p)p=127;var l=this.startTileIndex+p-32;p=l%g;l=l/g|0;const t=b.add(vec2(r,n).multiply(h));e.drawImage(this.image,p*f.x,l*f.y,f.x,f.y,t.x-m,t.y,f.x*c,f.y*c)}});e.restore()}drawText(a,b,c=1,d){this.drawTextScreen(a,worldToScreen(b).floor(),c*cameraScale|0,d)}}const isFullscreen=()=>document.fullscreenElement;function toggleFullscreen(){isFullscreen()?document.exitFullscreen?document.exitFullscreen():document.mozCancelFullScreen&&document.mozCancelFullScreen():document.body.webkitRequestFullScreen?document.body.webkitRequestFullScreen():document.body.mozRequestFullScreen&&document.body.mozRequestFullScreen()}"use strict";const keyIsDown=(a,b=0)=>inputData[b]&&inputData[b][a]&1?1:0,keyWasPressed=(a,b=0)=>inputData[b]&&inputData[b][a]&2?1:0,keyWasReleased=(a,b=0)=>inputData[b]&&inputData[b][a]&4?1:0,clearInput=()=>inputData=[[]],mouseIsDown=keyIsDown,mouseWasPressed=keyWasPressed,mouseWasReleased=keyWasReleased;let mousePos=vec2(),mousePosScreen=vec2(),mouseWheel=0,isUsingGamepad=0,preventDefaultInput=0;const gamepadIsDown=(a,b=0)=>keyIsDown(a,b+1),gamepadWasPressed=(a,b=0)=>keyWasPressed(a,b+1),gamepadWasReleased=(a,b=0)=>keyWasReleased(a,b+1),gamepadStick=(a,b=0)=>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||(inputData[isUsingGamepad=0][remapKeyCode(a.keyCode)]=3),preventDefaultInput&&a.preventDefault())};onkeyup=a=>{debug&&a.target!=document.body||(inputData[0][remapKeyCode(a.keyCode)]=4)};const remapKeyCode=a=>inputWASDEmulateDirection?87==a?38:83==a?40:65==a?37:68==a?39:a:a;onmousedown=a=>{inputData[isUsingGamepad=0][a.button]=3;onmousemove(a);a.button&&a.preventDefault()};onmouseup=a=>inputData[0][a.button]=inputData[0][a.button]&2|4;onmousemove=a=>mousePosScreen=mouseToScreen(a);onwheel=a=>a.ctrlKey||(mouseWheel=sign(a.deltaY));oncontextmenu=a=>!1;const 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)))},stickData=[];function gamepadsUpdate(){if(touchGamepadEnable&&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]?1+2*!gamepadIsDown(c,0):4*gamepadIsDown(c,0)}}if(gamepadsEnable&&navigator&&navigator.getGamepads&&(document.hasFocus()||debug))for(a=navigator.getGamepads(),b=a.length;b--;){var d=a[b];const h=inputData[b+1]||(inputData[b+1]=[]);c=stickData[b]||(stickData[b]=[]);if(d){var e=g=>.3<g?percent(g,.3,.8):-.3>g?-percent(-g,.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],h[e]=f.pressed?1+2*!gamepadIsDown(e,b):4*gamepadIsDown(e,b),isUsingGamepad|=!b&&f.pressed,touchGamepadEnable&&touchGamepadTimer.unset();gamepadDirectionEmulateStick&&(d=vec2(gamepadIsDown(15,b)-gamepadIsDown(14,b),gamepadIsDown(12,b)-gamepadIsDown(13,b)),d.lengthSquared()&&(c[0]=d.clampLength()))}}}const vibrate=a=>vibrateEnable&&navigator&&navigator.vibrate&&navigator.vibrate(a),vibrateStop=()=>vibrate(0),isTouchDevice=void 0!==window.ontouchstart;if(isTouchDevice){const a=onmousedown,b=onmouseup,c=onmousemove;onmousedown=onmouseup=onmousemove=f=>0;let d,e;ontouchstart=ontouchmove=ontouchend=f=>{f.button=0;const h=f.touches.length;h?(e||zzfx(0,e=1),f.x=f.touches[0].clientX,f.y=f.touches[0].clientY,d?c(f):a(f)):d&&b(f);d=h;return!0}}let touchGamepadTimer=new Timer,touchGamepadButtons=[],touchGamepadStick=vec2();function touchGamepadCreate(){touchGamepadEnable&&isTouchDevice&&(ontouchstart=ontouchmove=ontouchend=a=>{if(touchGamepadEnable){touchGamepadStick=vec2();touchGamepadButtons=[];if(a.touches.length&&(touchGamepadTimer.isSet()||zzfx(0),isUsingGamepad=1,touchGamepadTimer.set(),paused)){touchGamepadButtons[9]=1;return}var b=vec2(touchGamepadSize,mainCanvasSize.y-touchGamepadSize),c=mainCanvasSize.subtract(vec2(touchGamepadSize,touchGamepadSize)),d=mainCanvasSize.scale(.5);for(const e of a.touches)a=mouseToScreen(vec2(e.clientX,e.clientY)),a.distance(b)<touchGamepadSize?touchGamepadAnalog?touchGamepadStick=a.subtract(b).scale(2/touchGamepadSize).clampLength():(a=a.subtract(b).angle(),touchGamepadStick.setAngle((4*a/PI+8.5|0)*PI/4)):a.distance(c)<touchGamepadSize?(a=a.subtract(c).direction(),touchGamepadButtons[a]=1):a.distance(d)<touchGamepadSize&&(touchGamepadButtons[9]=1)}})}function touchGamepadRender(){if(touchGamepadEnable&&touchGamepadTimer.isSet()){var a=percent(touchGamepadTimer,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((new Vector2).setAngle(b*PI/2,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()}}}"use strict";class Sound{constructor(a,b=soundDefaultRange,c=soundDefaultTaper){soundEnable&&(this.range=b,this.taper=c,this.randomness=a[1]||0,a[1]=0,this.cachedSamples=zzfxG(...a))}play(a,b=1,c=1,d=1){if(soundEnable){var e=0;if(a){if(e=this.range){const f=cameraPos.distanceSquared(a);if(f>e*e)return;b*=percent(f**.5,e,e*this.taper)}e=2*worldToScreen(a).x/mainCanvas.width-1}a=c+c*this.randomness*d*rand(-1,1);return playSamples([this.cachedSamples],b,a,e)}}playNote(a,b,c=1){if(soundEnable)return this.play(b,c,2**(a/12),0)}}class Music{constructor(a){soundEnable&&(this.cachedSamples=zzfxM(...a))}play(a=1,b=1){if(soundEnable)return playSamples(this.cachedSamples,a,1,0,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}const speakStop=()=>speechSynthesis&&speechSynthesis.cancel(),getNoteFrequency=(a,b=220)=>b*2**(a/12);let audioContext;function playSamples(a,b=1,c=1,d=0,e=0){if(soundEnable&&(audioContext||=new(window.AudioContext||webkitAudioContext),audioContext.resume(),"running"==audioContext.state)){var f=audioContext.createBuffer(a.length,a[0].length,zzfxR),h=audioContext.createBufferSource();a.forEach((g,k)=>f.getChannelData(k).set(g));h.buffer=f;h.playbackRate.value=c;h.loop=e;a=audioContext.createGain();a.gain.value=soundVolume*b;a.connect(audioContext.destination);(window.StereoPannerNode?h.connect(new StereoPannerNode(audioContext,{pan:clamp(d,-1,1)})):h).connect(a);h.start();return h}}const zzfx=(...a)=>playSamples([zzfxG(...a)]),zzfxR=44100;function zzfxG(a=1,b=.05,c=220,d=0,e=0,f=.1,h=0,g=1,k=0,n=0,m=0,p=0,l=0,r=0,t=0,A=0,u=0,B=1,y=0,C=0){let v=2*PI,F=k*=500*v/zzfxR/zzfxR,z=[];b=c*=(1+b*rand(-1,1))*v/zzfxR;let w=0,D=0,q=0,x=1,G=0,I=0,E=0,J,H;d=d*zzfxR+9;y*=zzfxR;e*=zzfxR;f*=zzfxR;u*=zzfxR;n*=500*v/zzfxR**3;t*=v/zzfxR;m*=v/zzfxR;p*=zzfxR;l=l*zzfxR|0;for(H=d+y+e+f+u|0;q<H;z[q++]=E)++I%(100*A|0)||(E=h?1<h?2<h?3<h?Math.sin((w%v)**3):max(min(Math.tan(w),1),-1):1-(2*w/v%2+2)%2:1-4*abs(Math.round(w/v)-w/v):Math.sin(w),E=(l?1-C+C*Math.sin(v*q/l):1)*sign(E)*abs(E)**g*a*soundVolume*(q<d?q/d:q<d+y?1-(q-d)/y*(1-B):q<d+y+e?B:q<H-u?(H-q-u)/f*B:0),E=u?E/2+(u>q?0:(q<H-u?1:(H-q)/u)*z[q-u|0]/2):E),J=(c+=k+=n)*Math.cos(t*D++),w+=J-J*r*(1-1e9*(Math.sin(q)+1)%2),x&&++x>p&&(c+=m,b+=m,x=0),!l||++G%l||(c=b,k=F,x=x||1);return z}function zzfxM(a,b,c,d=125){let e,f,h,g,k,n,m,p,l,r,t,A,u,B=0,y,C=[],v=[],F=[],z=0,w=0,D=1,q={},x=zzfxR/d*60>>2;for(;D;z++)C=[D=p=A=0],c.forEach((G,I)=>{m=b[G][z]||[0,0,0];D|=!!b[G][z];y=A+(b[G][0].length-2-!p)*x;u=I==c.length-1;f=2;for(g=A;f<m.length+u;p=++f){k=m[f];l=f==m.length+u-1&&u||r!=(m[0]||0)|k|0;for(h=0;h<x&&p;h++>x-99&&l?t+=(1>t)/99:0)n=(1-t)*C[B++]/2||0,v[g]=(v[g]||0)-n*w+n,F[g]=(F[g++]||0)+n*w+n;k&&(t=k%1,w=m[1]||0,k|=0)&&(C=q[[r=m[B=0]||0,k]]=q[[r,k]]||(e=[...a[r]],e[2]*=2**((k-12)/12),0<k?zzfxG(...e):[]))}A=y});return[v,F]}"use strict";let tileCollision=[],tileCollisionSize=vec2();function initTileCollision(a){tileCollisionSize=a;tileCollision=[];for(a=tileCollision.length=tileCollisionSize.area();a--;)tileCollision[a]=0}const setTileCollisionData=(a,b=0)=>a.arrayCheck(tileCollisionSize)&&(tileCollision[(a.y|0)*tileCollisionSize.x+a.x|0]=b),getTileCollisionData=a=>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 h=tileCollision[e*tileCollisionSize.x+b];if(h&&(!c||c.collideWithTile(h,new Vector2(b,e))))return 1}}function tileCollisionRaycast(a,b,c){a=a.floor();b=b.floor();var d=b.subtract(a);const e=abs(d.x),f=-abs(d.y),h=sign(d.x);d=sign(d.y);let g=e+f;for(let n=a.x,m=a.y;;){var k=getTileCollisionData(vec2(n,m));if(k&&(c?c.collideWithTileRaycast(k,new Vector2(n,m)):0<k))return debugRaycast&&debugLine(a,b,"#f00",.02,1),debugRaycast&&debugPoint(new Vector2(n+.5,m+.5),"#ff0",1),new Vector2(n+.5,m+.5);if(n==b.x&m==b.y)break;k=2*g;k>=f&&(g+=f,n+=h);k<=e&&(g+=e,m+=d)}debugRaycast&&debugLine(a,b,"#00f",.02,1)}class TileLayerData{constructor(a,b=0,c=0,d=new Color){this.tile=a;this.direction=b;this.mirror=c;this.color=d}clear(){this.tile=this.direction=this.mirror=0;color=new Color}}class TileLayer extends EngineObject{constructor(a,b=tileCollisionSize,c=tileSizeDefault,d=vec2(1),e=0){super(a,b,-1,c,0,void 0,e);this.canvas=document.createElement("canvas");this.context=this.canvas.getContext("2d");this.scale=d;this.isOverlay;this.data=[];for(a=this.size.area();a--;)this.data.push(new TileLayerData)}setData(a,b,c){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);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(1);this.drawAllTileData();this.redrawEnd()}redrawStart(a=0){a&&(this.canvas.width=this.size.x*this.tileSize.x,this.canvas.height=this.size.y*this.tileSize.y);this.savedRenderSettings=[mainCanvas,mainContext,cameraPos,cameraScale];mainCanvas=this.canvas;mainContext=this.context;cameraPos=this.size.scale(.5);cameraScale=this.tileSize.x;enginePreRender()}redrawEnd(){ASSERT(mainContext==this.context);glEnable&&glCopyToContext(mainContext,1);[mainCanvas,mainContext,cameraPos,cameraScale]=this.savedRenderSettings}drawTileData(a){const b=a.floor().add(this.pos).add(vec2(.5));this.drawCanvas2D(b,vec2(1),0,0,c=>c.clearRect(-.5,-.5,1,1));a=this.getData(a);void 0!=a.tile&&(ASSERT(mainContext==this.context),drawTile(b,vec2(1),a.tile,this.tileSize,a.color,a.direction*PI/2,a.mirror))}drawAllTileData(){for(let a=this.size.x;a--;)for(let b=this.size.y;b--;)this.drawTileData(vec2(a,b))}drawCanvas2D(a,b,c=0,d,e){const f=this.context;f.save();a=a.subtract(this.pos).multiply(this.tileSize);b=b.multiply(this.tileSize);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=-1,d=tileSizeDefault,e=new Color,f,h){this.drawCanvas2D(a,b,f,h,g=>{if(0>c)g.fillStyle=e,g.fillRect(-.5,-.5,1,1);else{const k=tileImage.width/d.x;g.globalAlpha=e.a;g.drawImage(tileImage,c%k*d.x,(c/k|0)*d.y,d.x,d.y,-.5,-.5,1,1)}})}drawRect(a,b,c,d){this.drawTile(a,b,-1,0,c,d)}}"use strict";class ParticleEmitter extends EngineObject{constructor(a,b,c=0,d=0,e=100,f=PI,h=-1,g=tileSizeDefault,k=new Color,n=new Color,m=new Color(1,1,1,0),p=new Color(1,1,1,0),l=.5,r=.1,t=1,A=.1,u=.05,B=1,y=1,C=0,v=PI,F=.1,z=.2,w,D,q=1,x=D?1e9:0,G){super(a,new Vector2,h,g,b,void 0,x);this.emitSize=c;this.emitTime=d;this.emitRate=e;this.emitConeAngle=f;this.colorStartA=k;this.colorStartB=n;this.colorEndA=m;this.colorEndB=p;this.randomColorLinear=q;this.particleTime=l;this.sizeStart=r;this.sizeEnd=t;this.speed=A;this.angleSpeed=u;this.damping=B;this.angleDamping=y;this.gravityScale=C;this.particleConeAngle=v;this.fadeRate=F;this.randomness=z;this.collideTiles=w;this.additive=D;this.localSpace=G;this.emitTimeBuffer=this.trailScale=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=void 0!=this.emitSize.x?new Vector2(rand(-.5,.5),rand(-.5,.5)).multiply(this.emitSize).rotate(this.angle):randInCircle(.5*this.emitSize),b=rand(this.particleConeAngle,-this.particleConeAngle);this.localSpace||(a=this.pos.add(a),b+=this.angle);a=new Particle(a,this.tileIndex,this.tileSize,b);const c=this.randomness;var d=m=>m+m*rand(c,-c);b=d(this.particleTime);const e=d(this.sizeStart),f=d(this.sizeEnd),h=d(this.speed);d=d(this.angleSpeed)*randSign();var g=rand(this.emitConeAngle,-this.emitConeAngle);const k=randColor(this.colorStartA,this.colorStartB,this.randomColorLinear),n=randColor(this.colorEndA,this.colorEndB,this.randomColorLinear);g=this.localSpace?g:this.angle+g;a.colorStart=k;a.colorEndDelta=n.subtract(k);a.velocity=(new Vector2).setAngle(g,h);a.angleVelocity=d;a.lifeTime=b;a.sizeStart=e;a.sizeEndDelta=f-e;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.additive=this.additive;a.renderOrder=this.renderOrder;a.trailScale=this.trailScale;a.mirror=.5>rand();a.localSpaceEmitter=this.localSpace&&this;a.destroyCallback=this.particleDestroyCallback;this.particleCreateCallback&&this.particleCreateCallback(a);return a}render(){}}class Particle extends EngineObject{constructor(a,b,c,d){super(a,new Vector2,b,c,d)}render(){const a=min((time-this.spawnTime)/this.lifeTime,1);var b=this.sizeStart+a*this.sizeEndDelta;b=new Vector2(b,b);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(1);let d=this.pos;var 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));e=f.length();f=f.scale(1/e);const h=e*this.trailScale;b.y=max(b.x,h);e=f.angle();drawTile(d.add(f.multiply(vec2(0,-h/2))),b,this.tileIndex,this.tileSize,c,e,this.mirror)}else drawTile(d,b,this.tileIndex,this.tileSize,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)}}"use strict";const medals=[];let medalsPreventUnlock,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;this.image=new Image;e&&(this.image.src=e)}unlock(){medalsPreventUnlock||this.unlocked||(ASSERT(medalsSaveName),localStorage[this.storageKey()]=this.unlocked=1,medalsDisplayQueue.push(this),newgrounds&&newgrounds.unlockMedal(this.id),localStorage["OS13kTrophy,"+this.icon+","+medalsSaveName+","+this.name]=this.description)}render(a=0){const b=overlayContext,c=min(medalDisplayWidth,mainCanvas.width),d=overlayCanvas.width-c;a*=-medalDisplayHeight;b.save();b.beginPath();b.fillStyle="#ddd";b.fill(b.rect(d,a,c,medalDisplayHeight));b.strokeStyle="#000";b.lineWidth=3;b.stroke();b.clip();this.renderIcon(d+15+medalDisplayIconSize/2,a+medalDisplayHeight/2);b.textAlign="left";b.font="38px "+fontDefault;b.fillText(this.name,d+medalDisplayIconSize+30,a+28);b.font="24px "+fontDefault;b.fillText(this.description,d+medalDisplayIconSize+30,a+60);b.restore()}renderIcon(a,b,c=medalDisplayIconSize){const d=overlayContext;d.fillStyle="#000";d.textAlign="center";d.textBaseline="middle";d.font=.7*c+"px "+fontDefault;this.image.src?d.drawImage(this.image,a-c/2,b-c/2,c,c):d.fillText(this.icon,a,b)}storageKey(){return medalsSaveName+"_"+this.id}}function medalsRender(){if(medalsDisplayQueue.length){var a=medalsDisplayQueue[0],b=timeReal-medalsDisplayTimeLast;if(medalsDisplayTimeLast)if(b>medalDisplayTime)medalsDisplayQueue.shift(medalsDisplayTimeLast=0);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){newgrounds=new Newgrounds(a,b)}class Newgrounds{constructor(a,b){ASSERT(!newgrounds&&a);this.app_id=a;this.cipher=b;this.host=location?location.hostname:"";b&&(this.cryptoJS=CryptoJS());this.session_id=new URL(location.href).searchParams.get("ngio_session_id")||0;if(0!=this.session_id){this.medals=(a=this.call("Medal.getList"))?a.result.data.medals:[];debugMedals&&console.log(this.medals);for(var c of this.medals)if(a=medals[c.id])a.image.src=c.icon,a.name=c.name,a.description=c.description,a.unlocked=c.unlocked,a.difficulty=c.difficulty,a.value=c.value,a.value&&(a.description=a.description+" ("+a.value+")");this.scoreboards=(c=this.call("ScoreBoard.getBoards"))?c.result.data.scoreboards:[];debugMedals&&console.log(this.scoreboards);setInterval(()=>this.call("Gateway.ping",0,1),3e5)}}unlockMedal(a){return this.call("Medal.unlock",{id:a},1)}postScore(a,b){return this.call("ScoreBoard.postScore",{id:a,value:b},1)}getScores(a,b=0,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},1)}call(a,b=0,c=0){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)}}const CryptoJS=()=>eval(Function("[M='GBMGXz^oVYPPKKbB`agTXU|LxPc_ZBcMrZvCr~wyGfWrwk@ATqlqeTp^N?p{we}jIpEnB_sEr`l?YDkDhWhprc|Er|XETG?pTl`e}dIc[_N~}fzRycIfpW{HTolvoPB_FMe_eH~BTMx]yyOhv?biWPCGc]kABencBhgERHGf{OL`Dj`c^sh@canhy[secghiyotcdOWgO{tJIE^JtdGQRNSCrwKYciZOa]Y@tcRATYKzv|sXpboHcbCBf`}SKeXPFM|RiJsSNaIb]QPc[D]Jy_O^XkOVTZep`ONmntLL`Qz~UupHBX_Ia~WX]yTRJIxG`ioZ{fefLJFhdyYoyLPvqgH?b`[TMnTwwfzDXhfM?rKs^aFr|nyBdPmVHTtAjXoYUloEziWDCw_suyYT~lSMksI~ZNCS[Bex~j]Vz?kx`gdYSEMCsHpjbyxQvw|XxX_^nQYue{sBzVWQKYndtYQMWRef{bOHSfQhiNdtR{o?cUAHQAABThwHPT}F{VvFmgN`E@FiFYS`UJmpQNM`X|tPKHlccT}z}k{sACHL?Rt@MkWplxO`ASgh?hBsuuP|xD~LSH~KBlRs]t|l|_tQAroDRqWS^SEr[sYdPB}TAROtW{mIkE|dWOuLgLmJrucGLpebrAFKWjikTUzS|j}M}szasKOmrjy[?hpwnEfX[jGpLt@^v_eNwSQHNwtOtDgWD{rk|UgASs@mziIXrsHN_|hZuxXlPJOsA^^?QY^yGoCBx{ekLuZzRqQZdsNSx@ezDAn{XNj@fRXIwrDX?{ZQHwTEfu@GhxDOykqts|n{jOeZ@c`dvTY?e^]ATvWpb?SVyg]GC?SlzteilZJAL]mlhLjYZazY__qcVFYvt@|bIQnSno@OXyt]OulzkWqH`rYFWrwGs`v|~XeTsIssLrbmHZCYHiJrX}eEzSssH}]l]IhPQhPoQ}rCXLyhFIT[clhzYOvyHqigxmjz`phKUU^TPf[GRAIhNqSOdayFP@FmKmuIzMOeoqdpxyCOwCthcLq?n`L`tLIBboNn~uXeFcPE{C~mC`h]jUUUQe^`UqvzCutYCgct|SBrAeiYQW?X~KzCz}guXbsUw?pLsg@hDArw?KeJD[BN?GD@wgFWCiHq@Ypp_QKFixEKWqRp]oJFuVIEvjDcTFu~Zz]a{IcXhWuIdMQjJ]lwmGQ|]g~c]Hl]pl`Pd^?loIcsoNir_kikBYyg?NarXZEGYspt_vLBIoj}LI[uBFvm}tbqvC|xyR~a{kob|HlctZslTGtPDhBKsNsoZPuH`U`Fqg{gKnGSHVLJ^O`zmNgMn~{rsQuoymw^JY?iUBvw_~mMr|GrPHTERS[MiNpY[Mm{ggHpzRaJaoFomtdaQ_?xuTRm}@KjU~RtPsAdxa|uHmy}n^i||FVL[eQAPrWfLm^ndczgF~Nk~aplQvTUpHvnTya]kOenZlLAQIm{lPl@CCTchvCF[fI{^zPkeYZTiamoEcKmBMfZhk_j_~Fjp|wPVZlkh_nHu]@tP|hS@^G^PdsQ~f[RqgTDqezxNFcaO}HZhb|MMiNSYSAnQWCDJukT~e|OTgc}sf[cnr?fyzTa|EwEtRG|I~|IO}O]S|rp]CQ}}DWhSjC_|z|oY|FYl@WkCOoPuWuqr{fJu?Brs^_EBI[@_OCKs}?]O`jnDiXBvaIWhhMAQDNb{U`bqVR}oqVAvR@AZHEBY@depD]OLh`kf^UsHhzKT}CS}HQKy}Q~AeMydXPQztWSSzDnghULQgMAmbWIZ|lWWeEXrE^EeNoZApooEmrXe{NAnoDf`m}UNlRdqQ@jOc~HLOMWs]IDqJHYoMziEedGBPOxOb?[X`KxkFRg@`mgFYnP{hSaxwZfBQqTm}_?RSEaQga]w[vxc]hMne}VfSlqUeMo_iqmd`ilnJXnhdj^EEFifvZyxYFRf^VaqBhLyrGlk~qowqzHOBlOwtx?i{m~`n^G?Yxzxux}b{LSlx]dS~thO^lYE}bzKmUEzwW^{rPGhbEov[Plv??xtyKJshbG`KuO?hjBdS@Ru}iGpvFXJRrvOlrKN?`I_n_tplk}kgwSXuKylXbRQ]]?a|{xiT[li?k]CJpwy^o@ebyGQrPfF`aszGKp]baIx~H?ElETtFh]dz[OjGl@C?]VDhr}OE@V]wLTc[WErXacM{We`F|utKKjgllAxvsVYBZ@HcuMgLboFHVZmi}eIXAIFhS@A@FGRbjeoJWZ_NKd^oEH`qgy`q[Tq{x?LRP|GfBFFJV|fgZs`MLbpPYUdIV^]mD@FG]pYAT^A^RNCcXVrPsgk{jTrAIQPs_`mD}rOqAZA[}RETFz]WkXFTz_m{N@{W@_fPKZLT`@aIqf|L^Mb|crNqZ{BVsijzpGPEKQQZGlApDn`ruH}cvF|iXcNqK}cxe_U~HRnKV}sCYb`D~oGvwG[Ca|UaybXea~DdD~LiIbGRxJ_VGheI{ika}KC[OZJLn^IBkPrQj_EuoFwZ}DpoBRcK]Q}?EmTv~i_Tul{bky?Iit~tgS|o}JL_VYcCQdjeJ_MfaA`FgCgc[Ii|CBHwq~nbJeYTK{e`CNstKfTKPzw{jdhp|qsZyP_FcugxCFNpKitlR~vUrx^NrSVsSTaEgnxZTmKc`R|lGJeX}ccKLsQZQhsFkeFd|ckHIVTlGMg`~uPwuHRJS_CPuN_ogXe{Ba}dO_UBhuNXby|h?JlgBIqMKx^_u{molgL[W_iavNQuOq?ap]PGB`clAicnl@k~pA?MWHEZ{HuTLsCpOxxrKlBh]FyMjLdFl|nMIvTHyGAlPogqfZ?PlvlFJvYnDQd}R@uAhtJmDfe|iJqdkYr}r@mEjjIetDl_I`TELfoR|qTBu@Tic[BaXjP?dCS~MUK[HPRI}OUOwAaf|_}HZzrwXvbnNgltjTwkBE~MztTQhtRSWoQHajMoVyBBA`kdgK~h`o[J`dm~pm]tk@i`[F~F]DBlJKklrkR]SNw@{aG~Vhl`KINsQkOy?WhcqUMTGDOM_]bUjVd|Yh_KUCCgIJ|LDIGZCPls{RzbVWVLEhHvWBzKq|^N?DyJB|__aCUjoEgsARki}j@DQXS`RNU|DJ^a~d{sh_Iu{ONcUtSrGWW@cvUjefHHi}eSSGrNtO?cTPBShLqzwMVjWQQCCFB^culBjZHEK_{dO~Q`YhJYFn]jq~XSnG@[lQr]eKrjXpG~L^h~tDgEma^AUFThlaR{xyuP@[^VFwXSeUbVetufa@dX]CLyAnDV@Bs[DnpeghJw^?UIana}r_CKGDySoRudklbgio}kIDpA@McDoPK?iYcG?_zOmnWfJp}a[JLR[stXMo?_^Ng[whQlrDbrawZeSZ~SJstIObdDSfAA{MV}?gNunLOnbMv_~KFQUAjIMj^GkoGxuYtYbGDImEYiwEMyTpMxN_LSnSMdl{bg@dtAnAMvhDTBR_FxoQgANniRqxd`pWv@rFJ|mWNWmh[GMJz_Nq`BIN@KsjMPASXORcdHjf~rJfgZYe_uulzqM_KdPlMsuvU^YJuLtofPhGonVOQxCMuXliNvJIaoC?hSxcxKVVxWlNs^ENDvCtSmO~WxI[itnjs^RDvI@KqG}YekaSbTaB]ki]XM@[ZnDAP~@|BzLRgOzmjmPkRE@_sobkT|SszXK[rZN?F]Z_u}Yue^[BZgLtR}FHzWyxWEX^wXC]MJmiVbQuBzkgRcKGUhOvUc_bga|Tx`KEM`JWEgTpFYVeXLCm|mctZR@uKTDeUONPozBeIkrY`cz]]~WPGMUf`MNUGHDbxZuO{gmsKYkAGRPqjc|_FtblEOwy}dnwCHo]PJhN~JoteaJ?dmYZeB^Xd?X^pOKDbOMF@Ugg^hETLdhwlA}PL@_ur|o{VZosP?ntJ_kG][g{Zq`Tu]dzQlSWiKfnxDnk}KOzp~tdFstMobmy[oPYjyOtUzMWdjcNSUAjRuqhLS@AwB^{BFnqjCmmlk?jpn}TksS{KcKkDboXiwK]qMVjm~V`LgWhjS^nLGwfhAYrjDSBL_{cRus~{?xar_xqPlArrYFd?pHKdMEZzzjJpfC?Hv}mAuIDkyBxFpxhstTx`IO{rp}XGuQ]VtbHerlRc_LFGWK[XluFcNGUtDYMZny[M^nVKVeMllQI[xtvwQnXFlWYqxZZFp_|]^oWX[{pOMpxXxvkbyJA[DrPzwD|LW|QcV{Nw~U^dgguSpG]ClmO@j_TENIGjPWwgdVbHganhM?ema|dBaqla|WBd`poj~klxaasKxGG^xbWquAl~_lKWxUkDFagMnE{zHug{b`A~IYcQYBF_E}wiA}K@yxWHrZ{[d~|ARsYsjeNWzkMs~IOqqp[yzDE|WFrivsidTcnbHFRoW@XpAV`lv_zj?B~tPCppRjgbbDTALeFaOf?VcjnKTQMLyp{NwdylHCqmo?oelhjWuXj~}{fpuX`fra?GNkDiChYgVSh{R[BgF~eQa^WVz}ATI_CpY?g_diae]|ijH`TyNIF}|D_xpmBq_JpKih{Ba|sWzhnAoyraiDvk`h{qbBfsylBGmRH}DRPdryEsSaKS~tIaeF[s]I~xxHVrcNe@Jjxa@jlhZueLQqHh_]twVMqG_EGuwyab{nxOF?`HCle}nBZzlTQjkLmoXbXhOtBglFoMz?eqre`HiE@vNwBulglmQjj]DB@pPkPUgA^sjOAUNdSu_`oAzar?n?eMnw{{hYmslYi[TnlJD'",..."]charCodeAtUinyxpf","for(;e<10359;c[e++]=p-=128,A=A?p-A&&A:p==34&&p)for(p=1;p<128;y=f.map((n,x)=>(U=r[n]*2+1,U=Math.log(U/(h-U)),t-=a[x]*U,U/500)),t=~-h/(1+Math.exp(t))|1,i=o%h<t,o=o%h+(i?t:h-t)*(o>>17)-!i*t,f.map((n,x)=>(U=r[n]+=(i*h/2-r[n]<<13)/((C[n]+=C[n]<5)+1/20)>>13,a[x]+=y[x]*(i-t/h))),p=p*2+i)for(f='010202103203210431053105410642065206541'.split(t=0).map((n,x)=>(U=0,[...n].map((n,x)=>(U=U*997+(c[e-n]|0)|0)),h*32-1&U*997+p+!!A*129)*12+x);o<h*32;o=o*64|M.charCodeAt(d++)&63);for(C=String.fromCharCode(...c);r=/[\0-#?@\\\\~]/.exec(C);)with(C.split(r))C=join(shift());return C")([],[],131072,[0,0,0,0,0,0,0,0,0,0,0,0],new Uint16Array(51e6).fill(32768),new Uint8Array(51e6),0,0,0,0));"use strict";let glCanvas,glContext,glTileTexture,glActiveTexture,glShader,glArrayBuffer,glVertexData,glPositionData,glColorData,glBatchCount,glBatchAdditive,glAdditive;function glInit(){glCanvas=document.createElement("canvas");glContext=glCanvas.getContext("webgl",{antialias:!1});glTileTexture=glCreateTexture(tileImage);glOverlay&&document.body.appendChild(glCanvas);glShader=glCreateProgram("precision highp float;uniform mat4 m;attribute vec2 p,t;attribute vec4 c,a;varying vec2 v;varying vec4 d,e;void main(){gl_Position=m*vec4(p,1,1);v=t;d=c;e=a;}","precision highp float;varying vec2 v;varying vec4 d,e;uniform sampler2D s;void main(){gl_FragColor=texture2D(s,v)*d+e;}");glVertexData=new ArrayBuffer(gl_MAX_BATCH*gl_VERTICES_PER_QUAD*gl_VERTEX_BYTE_STRIDE);glArrayBuffer=glContext.createBuffer();glPositionData=new Float32Array(glVertexData);glColorData=new Uint32Array(glVertexData);glBatchCount=0}function glSetBlendMode(a){glAdditive=a}function glSetTexture(a=glTileTexture){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&&a.width&&glContext.texImage2D(gl_TEXTURE_2D,0,gl_RGBA,gl_RGBA,gl_UNSIGNED_BYTE,a);glContext.texParameteri(gl_TEXTURE_2D,gl_TEXTURE_MIN_FILTER,cavasPixelated?gl_NEAREST:gl_LINEAR);glContext.texParameteri(gl_TEXTURE_2D,gl_TEXTURE_MAG_FILTER,cavasPixelated?gl_NEAREST:gl_LINEAR);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 glPreRender(a,b,c,d,e){glContext.viewport(0,0,glCanvas.width=a,glCanvas.height=b);glContext.clear(gl_COLOR_BUFFER_BIT);glContext.useProgram(glShader);glContext.activeTexture(gl_TEXTURE0);glContext.bindTexture(gl_TEXTURE_2D,glActiveTexture=glTileTexture);glContext.bindBuffer(gl_ARRAY_BUFFER,glArrayBuffer);glContext.bufferData(gl_ARRAY_BUFFER,glVertexData.byteLength,gl_DYNAMIC_DRAW);glSetBlendMode();let f=0;const h=(g,k,n,m,p=0)=>{g=glContext.getAttribLocation(glShader,g);glContext.enableVertexAttribArray(g);glContext.vertexAttribPointer(g,m,k,p,gl_VERTEX_BYTE_STRIDE,f);f+=m*n};h("p",gl_FLOAT,4,2);h("t",gl_FLOAT,4,2);h("c",gl_UNSIGNED_BYTE,1,4,1);h("a",gl_UNSIGNED_BYTE,1,4,1);a=2*e/a;b=2*e/b;glContext.uniformMatrix4fv(glContext.getUniformLocation(glShader,"m"),0,new Float32Array([a,0,0,0,0,b,0,0,1,1,-1,1,-1-a*c,-1-b*d,0,0]))}function glFlush(){if(glBatchCount){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.subarray(0,glBatchCount*gl_VERTICES_PER_QUAD*gl_INDICIES_PER_VERT));glContext.drawArrays(gl_TRIANGLES,0,glBatchCount*gl_VERTICES_PER_QUAD);glBatchCount=0;glBatchAdditive=glAdditive}}function glCopyToContext(a,b){if(glBatchCount||b)glFlush(),glOverlay&&!b||a.drawImage(glCanvas,0,0)}function glDraw(a,b,c,d,e,f,h,g,k,n=4294967295,m=0){glBatchCount!=gl_MAX_BATCH&&glBatchAdditive==glAdditive||glFlush();var p=Math.cos(e)/2,l=Math.sin(e)/2;e=p*c;p*=d;c*=l;d*=l;l=glBatchCount++*gl_VERTICES_PER_QUAD*gl_INDICIES_PER_VERT;glPositionData[l++]=a-e-d;glPositionData[l++]=b-p+c;glPositionData[l++]=f;glPositionData[l++]=k;glColorData[l++]=n;glColorData[l++]=m;glPositionData[l++]=a+e+d;glPositionData[l++]=b+p-c;glPositionData[l++]=g;glPositionData[l++]=h;glColorData[l++]=n;glColorData[l++]=m;glPositionData[l++]=a-e+d;glPositionData[l++]=b+p+c;glPositionData[l++]=f;glPositionData[l++]=h;glColorData[l++]=n;glColorData[l++]=m;glPositionData[l++]=a-e-d;glPositionData[l++]=b-p+c;glPositionData[l++]=f;glPositionData[l++]=k;glColorData[l++]=n;glColorData[l++]=m;glPositionData[l++]=a+e-d;glPositionData[l++]=b-p-c;glPositionData[l++]=g;glPositionData[l++]=k;glColorData[l++]=n;glColorData[l++]=m;glPositionData[l++]=a+e+d;glPositionData[l++]=b+p-c;glPositionData[l++]=g;glPositionData[l++]=h;glColorData[l++]=n;glColorData[l++]=m}let glPostShader,glPostArrayBuffer,glPostTexture;function glInitPostProcess(a){ASSERT(!glPostShader);a||="void mainImage(out vec4 c,vec2 p){c=texture2D(iChannel0,p/iResolution.xy);}";glPostShader=glCreateProgram("precision highp float;attribute vec2 p;void main(){gl_Position=vec4(p,1,1);}","precision highp float;uniform sampler2D iChannel0;uniform vec3 iResolution;uniform float iTime;\n"+a+"\nvoid main(){mainImage(gl_FragColor,gl_FragCoord.xy);gl_FragColor.a=1.;}");glPostArrayBuffer=glContext.createBuffer();glPostTexture=glCreateTexture();mainCanvas.style.visibility="hidden"}function glRenderPostProcess(){if(glPostShader){var a=mainCanvas.width,b=mainCanvas.height;glEnable?(glFlush(),mainContext.drawImage(glCanvas,0,0)):glContext.viewport(0,0,glCanvas.width=a,glCanvas.height=b);glContext.useProgram(glPostShader);glContext.disable(gl_BLEND);glContext.bindBuffer(gl_ARRAY_BUFFER,glPostArrayBuffer);glContext.bufferData(gl_ARRAY_BUFFER,new Float32Array([-3,1,1,-3,1,1]),gl_STATIC_DRAW);glContext.pixelStorei(gl_UNPACK_FLIP_Y_WEBGL,!0);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 c=glContext.getAttribLocation(glPostShader,"p");glContext.enableVertexAttribArray(c);glContext.vertexAttribPointer(c,2,gl_FLOAT,0,8,0);glContext.uniform1i(glContext.getUniformLocation(glPostShader,"iChannel0"),0);glContext.uniform1f(glContext.getUniformLocation(glPostShader,"iTime"),time);glContext.uniform3f(glContext.getUniformLocation(glPostShader,"iResolution"),a,b,1);glContext.drawArrays(gl_TRIANGLES,0,3)}}const gl_ONE=1,gl_TRIANGLES=4,gl_SRC_ALPHA=770,gl_ONE_MINUS_SRC_ALPHA=771,gl_BLEND=3042,gl_TEXTURE_2D=3553,gl_UNSIGNED_BYTE=5121,gl_BYTE=5120,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_TEXTURE1=33985,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_VERTICES_PER_QUAD=6,gl_INDICIES_PER_VERT=6,gl_MAX_BATCH=65536,gl_VERTEX_BYTE_STRIDE=24;
|
|
@@ -1110,7 +1110,7 @@ let touchGamepadAnalog = 1;
|
|
|
1110
1110
|
/** Size of virutal gamepad for touch devices in pixels
|
|
1111
1111
|
* @default
|
|
1112
1112
|
* @memberof Settings */
|
|
1113
|
-
let touchGamepadSize =
|
|
1113
|
+
let touchGamepadSize = 99;
|
|
1114
1114
|
|
|
1115
1115
|
/** Transparency of touch gamepad overlay
|
|
1116
1116
|
* @default
|
|
@@ -2190,6 +2190,10 @@ const isTouchDevice = window.ontouchstart !== undefined;
|
|
|
2190
2190
|
// try to enable touch mouse
|
|
2191
2191
|
if (isTouchDevice)
|
|
2192
2192
|
{
|
|
2193
|
+
// override mouse events
|
|
2194
|
+
const mouseDown = onmousedown, mouseUp = onmouseup, mouseMove = onmousemove;
|
|
2195
|
+
onmousedown = onmouseup = onmousemove = (e)=> 0;
|
|
2196
|
+
|
|
2193
2197
|
// handle all touch events the same way
|
|
2194
2198
|
let wasTouching, hadTouch;
|
|
2195
2199
|
ontouchstart = ontouchmove = ontouchend = (e)=>
|
|
@@ -2206,10 +2210,10 @@ if (isTouchDevice)
|
|
|
2206
2210
|
// set event pos and pass it along
|
|
2207
2211
|
e.x = e.touches[0].clientX;
|
|
2208
2212
|
e.y = e.touches[0].clientY;
|
|
2209
|
-
wasTouching ?
|
|
2213
|
+
wasTouching ? mouseMove(e) : mouseDown(e);
|
|
2210
2214
|
}
|
|
2211
2215
|
else if (wasTouching)
|
|
2212
|
-
|
|
2216
|
+
mouseUp(e);
|
|
2213
2217
|
|
|
2214
2218
|
// set was touching
|
|
2215
2219
|
wasTouching = touching;
|
|
@@ -3812,7 +3816,6 @@ function glInit()
|
|
|
3812
3816
|
// create the canvas and tile texture
|
|
3813
3817
|
glCanvas = document.createElement('canvas');
|
|
3814
3818
|
glContext = glCanvas.getContext('webgl', {antialias: false});
|
|
3815
|
-
glCanvas.style = styleCanvas;
|
|
3816
3819
|
glTileTexture = glCreateTexture(tileImage);
|
|
3817
3820
|
|
|
3818
3821
|
// some browsers are much faster without copying the gl buffer so we just overlay it instead
|
|
@@ -4209,7 +4212,7 @@ gl_VERTEX_BYTE_STRIDE = (4 * 2) * 2 + (4) * 2; // vec2 * 2 + (char * 4) * 2
|
|
|
4209
4212
|
const engineName = 'LittleJS';
|
|
4210
4213
|
|
|
4211
4214
|
/** Version of engine */
|
|
4212
|
-
const engineVersion = '1.4.
|
|
4215
|
+
const engineVersion = '1.4.7';
|
|
4213
4216
|
|
|
4214
4217
|
/** Frames per second to update objects
|
|
4215
4218
|
* @default */
|
|
@@ -4238,15 +4241,11 @@ let timeReal = 0;
|
|
|
4238
4241
|
let paused = 0;
|
|
4239
4242
|
|
|
4240
4243
|
// Engine internal variables not exposed to documentation
|
|
4241
|
-
let
|
|
4244
|
+
let tileImageSize, tileImageFixBleed;
|
|
4242
4245
|
|
|
4243
4246
|
// Engine stat tracking, if showWatermark is true
|
|
4244
4247
|
let averageFPS, drawCount;
|
|
4245
4248
|
|
|
4246
|
-
// css text used for elements created by engine
|
|
4247
|
-
const styleBody = 'margin:0;overflow:hidden;background:#000';
|
|
4248
|
-
const styleCanvas = 'position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)';
|
|
4249
|
-
|
|
4250
4249
|
///////////////////////////////////////////////////////////////////////////////
|
|
4251
4250
|
|
|
4252
4251
|
/** Start up LittleJS engine with your callback functions
|
|
@@ -4266,11 +4265,16 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
4266
4265
|
tileImageFixBleed = vec2(tileFixBleedScale).divide(tileImageSize = vec2(tileImage.width, tileImage.height));
|
|
4267
4266
|
debug && (tileImage.onload=()=>ASSERT(1)); // tile sheet can not reloaded
|
|
4268
4267
|
|
|
4268
|
+
// setup css
|
|
4269
|
+
const styleBody = 'margin:0;overflow:hidden;background:#000' + // fill the window
|
|
4270
|
+
';touch-action:none' + // prevent mobile pinch to resize
|
|
4271
|
+
';user-select:none' + // prevent mobile hold to select
|
|
4272
|
+
';-webkit-user-select:none;-moz-user-select:none'; // compatibility for mobile
|
|
4273
|
+
|
|
4269
4274
|
// setup html
|
|
4270
4275
|
document.body.style = styleBody;
|
|
4271
4276
|
document.body.appendChild(mainCanvas = document.createElement('canvas'));
|
|
4272
4277
|
mainContext = mainCanvas.getContext('2d');
|
|
4273
|
-
mainCanvas.style = styleCanvas;
|
|
4274
4278
|
|
|
4275
4279
|
// init stuff and start engine
|
|
4276
4280
|
debugInit();
|
|
@@ -4279,13 +4283,19 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
4279
4283
|
// create overlay canvas for hud to appear above gl canvas
|
|
4280
4284
|
document.body.appendChild(overlayCanvas = document.createElement('canvas'));
|
|
4281
4285
|
overlayContext = overlayCanvas.getContext('2d');
|
|
4282
|
-
|
|
4286
|
+
|
|
4287
|
+
// set canvas style to fill the window
|
|
4288
|
+
const styleCanvas = 'position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)';
|
|
4289
|
+
(glCanvas||mainCanvas).style = overlayCanvas.style = mainCanvas.style = styleCanvas;
|
|
4283
4290
|
|
|
4284
4291
|
gameInit();
|
|
4285
4292
|
touchGamepadCreate();
|
|
4286
4293
|
engineUpdate();
|
|
4287
4294
|
};
|
|
4288
4295
|
|
|
4296
|
+
// frame time tracking
|
|
4297
|
+
let frameTimeLastMS = 0, frameTimeBufferMS = 0;
|
|
4298
|
+
|
|
4289
4299
|
// main update loop
|
|
4290
4300
|
const engineUpdate = (frameTimeMS=0)=>
|
|
4291
4301
|
{
|
|
@@ -4476,8 +4486,11 @@ function engineObjectsCallback(pos, size, callbackFunction, objects=engineObject
|
|
|
4476
4486
|
pos.distanceSquared(o.pos) < sizeSquared && callbackFunction(o);
|
|
4477
4487
|
}
|
|
4478
4488
|
}
|
|
4479
|
-
|
|
4480
|
-
|
|
4489
|
+
|
|
4490
|
+
/**
|
|
4491
|
+
* LittleJS Module Export
|
|
4492
|
+
* <br> - Export engine as a module with extra functions where necessary
|
|
4493
|
+
*/
|
|
4481
4494
|
|
|
4482
4495
|
// setters for all variables that devs will need to modify
|
|
4483
4496
|
const setCameraPos = (v)=> cameraPos = v;
|
|
@@ -4519,11 +4532,9 @@ const setMedalDisplayWidth = (v)=> medalDisplayWidth = v;
|
|
|
4519
4532
|
const setMedalDisplayHeight = (v)=> medalDisplayHeight = v;
|
|
4520
4533
|
const setMedalDisplayIconSize = (v)=> medalDisplayIconSize = v;
|
|
4521
4534
|
const setMedalsPreventUnlock = (v)=> medalsPreventUnlock = v;
|
|
4522
|
-
const setDebugPointSize = (v)=> debugPointSize = v;
|
|
4523
4535
|
const setShowWatermark = (v)=> showWatermark = v;
|
|
4524
4536
|
const setGodMode = (v)=> godMode = v;
|
|
4525
4537
|
|
|
4526
|
-
// This is a full list of everything in engine.all
|
|
4527
4538
|
export {
|
|
4528
4539
|
// Custom methods
|
|
4529
4540
|
setCameraPos,
|
|
@@ -4565,7 +4576,6 @@ export {
|
|
|
4565
4576
|
setMedalDisplayHeight,
|
|
4566
4577
|
setMedalDisplayIconSize,
|
|
4567
4578
|
setMedalsPreventUnlock,
|
|
4568
|
-
setDebugPointSize,
|
|
4569
4579
|
setShowWatermark,
|
|
4570
4580
|
setGodMode,
|
|
4571
4581
|
|
|
@@ -4610,7 +4620,6 @@ export {
|
|
|
4610
4620
|
|
|
4611
4621
|
// Globals
|
|
4612
4622
|
debug,
|
|
4613
|
-
debugPointSize,
|
|
4614
4623
|
showWatermark,
|
|
4615
4624
|
godMode,
|
|
4616
4625
|
// Debug
|
|
@@ -4651,6 +4660,7 @@ export {
|
|
|
4651
4660
|
isOverlapping,
|
|
4652
4661
|
wave,
|
|
4653
4662
|
formatTime,
|
|
4663
|
+
|
|
4654
4664
|
// Random
|
|
4655
4665
|
rand,
|
|
4656
4666
|
randInt,
|
|
@@ -4661,13 +4671,13 @@ export {
|
|
|
4661
4671
|
randSeed,
|
|
4662
4672
|
randSeeded,
|
|
4663
4673
|
|
|
4664
|
-
//
|
|
4665
|
-
vec2,
|
|
4674
|
+
// Utility Classes
|
|
4666
4675
|
Vector2,
|
|
4667
|
-
colorRGBA,
|
|
4668
|
-
colorHSLA,
|
|
4669
4676
|
Color,
|
|
4670
4677
|
Timer,
|
|
4678
|
+
vec2,
|
|
4679
|
+
colorRGBA,
|
|
4680
|
+
colorHSLA,
|
|
4671
4681
|
|
|
4672
4682
|
// Base
|
|
4673
4683
|
EngineObject,
|
|
@@ -4799,11 +4809,8 @@ export {
|
|
|
4799
4809
|
time,
|
|
4800
4810
|
timeReal,
|
|
4801
4811
|
paused,
|
|
4802
|
-
|
|
4803
|
-
|
|
4804
|
-
//drawCount,
|
|
4805
|
-
//styleBody,
|
|
4806
|
-
//styleCanvas,
|
|
4812
|
+
averageFPS,
|
|
4813
|
+
drawCount,
|
|
4807
4814
|
engineInit,
|
|
4808
4815
|
//enginePreRender,
|
|
4809
4816
|
//engineObjectsUpdate,
|
|
@@ -755,7 +755,7 @@ let touchGamepadAnalog = 1;
|
|
|
755
755
|
/** Size of virutal gamepad for touch devices in pixels
|
|
756
756
|
* @default
|
|
757
757
|
* @memberof Settings */
|
|
758
|
-
let touchGamepadSize =
|
|
758
|
+
let touchGamepadSize = 99;
|
|
759
759
|
|
|
760
760
|
/** Transparency of touch gamepad overlay
|
|
761
761
|
* @default
|
|
@@ -841,7 +841,7 @@ let medalDisplayIconSize = 50;
|
|
|
841
841
|
const engineName = 'LittleJS';
|
|
842
842
|
|
|
843
843
|
/** Version of engine */
|
|
844
|
-
const engineVersion = '1.4.
|
|
844
|
+
const engineVersion = '1.4.7';
|
|
845
845
|
|
|
846
846
|
/** Frames per second to update objects
|
|
847
847
|
* @default */
|
|
@@ -870,15 +870,11 @@ let timeReal = 0;
|
|
|
870
870
|
let paused = 0;
|
|
871
871
|
|
|
872
872
|
// Engine internal variables not exposed to documentation
|
|
873
|
-
let
|
|
873
|
+
let tileImageSize, tileImageFixBleed;
|
|
874
874
|
|
|
875
875
|
// Engine stat tracking, if showWatermark is true
|
|
876
876
|
let averageFPS, drawCount;
|
|
877
877
|
|
|
878
|
-
// css text used for elements created by engine
|
|
879
|
-
const styleBody = 'margin:0;overflow:hidden;background:#000';
|
|
880
|
-
const styleCanvas = 'position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)';
|
|
881
|
-
|
|
882
878
|
///////////////////////////////////////////////////////////////////////////////
|
|
883
879
|
|
|
884
880
|
/** Start up LittleJS engine with your callback functions
|
|
@@ -898,11 +894,16 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
898
894
|
tileImageFixBleed = vec2(tileFixBleedScale).divide(tileImageSize = vec2(tileImage.width, tileImage.height));
|
|
899
895
|
debug && (tileImage.onload=()=>ASSERT(1)); // tile sheet can not reloaded
|
|
900
896
|
|
|
897
|
+
// setup css
|
|
898
|
+
const styleBody = 'margin:0;overflow:hidden;background:#000' + // fill the window
|
|
899
|
+
';touch-action:none' + // prevent mobile pinch to resize
|
|
900
|
+
';user-select:none' + // prevent mobile hold to select
|
|
901
|
+
';-webkit-user-select:none;-moz-user-select:none'; // compatibility for mobile
|
|
902
|
+
|
|
901
903
|
// setup html
|
|
902
904
|
document.body.style = styleBody;
|
|
903
905
|
document.body.appendChild(mainCanvas = document.createElement('canvas'));
|
|
904
906
|
mainContext = mainCanvas.getContext('2d');
|
|
905
|
-
mainCanvas.style = styleCanvas;
|
|
906
907
|
|
|
907
908
|
// init stuff and start engine
|
|
908
909
|
debugInit();
|
|
@@ -911,13 +912,19 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
911
912
|
// create overlay canvas for hud to appear above gl canvas
|
|
912
913
|
document.body.appendChild(overlayCanvas = document.createElement('canvas'));
|
|
913
914
|
overlayContext = overlayCanvas.getContext('2d');
|
|
914
|
-
|
|
915
|
+
|
|
916
|
+
// set canvas style to fill the window
|
|
917
|
+
const styleCanvas = 'position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)';
|
|
918
|
+
(glCanvas||mainCanvas).style = overlayCanvas.style = mainCanvas.style = styleCanvas;
|
|
915
919
|
|
|
916
920
|
gameInit();
|
|
917
921
|
touchGamepadCreate();
|
|
918
922
|
engineUpdate();
|
|
919
923
|
};
|
|
920
924
|
|
|
925
|
+
// frame time tracking
|
|
926
|
+
let frameTimeLastMS = 0, frameTimeBufferMS = 0;
|
|
927
|
+
|
|
921
928
|
// main update loop
|
|
922
929
|
const engineUpdate = (frameTimeMS=0)=>
|
|
923
930
|
{
|
|
@@ -2126,6 +2133,10 @@ const isTouchDevice = window.ontouchstart !== undefined;
|
|
|
2126
2133
|
// try to enable touch mouse
|
|
2127
2134
|
if (isTouchDevice)
|
|
2128
2135
|
{
|
|
2136
|
+
// override mouse events
|
|
2137
|
+
const mouseDown = onmousedown, mouseUp = onmouseup, mouseMove = onmousemove;
|
|
2138
|
+
onmousedown = onmouseup = onmousemove = (e)=> 0;
|
|
2139
|
+
|
|
2129
2140
|
// handle all touch events the same way
|
|
2130
2141
|
let wasTouching, hadTouch;
|
|
2131
2142
|
ontouchstart = ontouchmove = ontouchend = (e)=>
|
|
@@ -2142,10 +2153,10 @@ if (isTouchDevice)
|
|
|
2142
2153
|
// set event pos and pass it along
|
|
2143
2154
|
e.x = e.touches[0].clientX;
|
|
2144
2155
|
e.y = e.touches[0].clientY;
|
|
2145
|
-
wasTouching ?
|
|
2156
|
+
wasTouching ? mouseMove(e) : mouseDown(e);
|
|
2146
2157
|
}
|
|
2147
2158
|
else if (wasTouching)
|
|
2148
|
-
|
|
2159
|
+
mouseUp(e);
|
|
2149
2160
|
|
|
2150
2161
|
// set was touching
|
|
2151
2162
|
wasTouching = touching;
|
|
@@ -3748,7 +3759,6 @@ function glInit()
|
|
|
3748
3759
|
// create the canvas and tile texture
|
|
3749
3760
|
glCanvas = document.createElement('canvas');
|
|
3750
3761
|
glContext = glCanvas.getContext('webgl', {antialias: false});
|
|
3751
|
-
glCanvas.style = styleCanvas;
|
|
3752
3762
|
glTileTexture = glCreateTexture(tileImage);
|
|
3753
3763
|
|
|
3754
3764
|
// some browsers are much faster without copying the gl buffer so we just overlay it instead
|
|
@@ -755,7 +755,7 @@ let touchGamepadAnalog = 1;
|
|
|
755
755
|
/** Size of virutal gamepad for touch devices in pixels
|
|
756
756
|
* @default
|
|
757
757
|
* @memberof Settings */
|
|
758
|
-
let touchGamepadSize =
|
|
758
|
+
let touchGamepadSize = 99;
|
|
759
759
|
|
|
760
760
|
/** Transparency of touch gamepad overlay
|
|
761
761
|
* @default
|
|
@@ -841,7 +841,7 @@ let medalDisplayIconSize = 50;
|
|
|
841
841
|
const engineName = 'LittleJS';
|
|
842
842
|
|
|
843
843
|
/** Version of engine */
|
|
844
|
-
const engineVersion = '1.4.
|
|
844
|
+
const engineVersion = '1.4.7';
|
|
845
845
|
|
|
846
846
|
/** Frames per second to update objects
|
|
847
847
|
* @default */
|
|
@@ -870,15 +870,11 @@ let timeReal = 0;
|
|
|
870
870
|
let paused = 0;
|
|
871
871
|
|
|
872
872
|
// Engine internal variables not exposed to documentation
|
|
873
|
-
let
|
|
873
|
+
let tileImageSize, tileImageFixBleed;
|
|
874
874
|
|
|
875
875
|
// Engine stat tracking, if showWatermark is true
|
|
876
876
|
let averageFPS, drawCount;
|
|
877
877
|
|
|
878
|
-
// css text used for elements created by engine
|
|
879
|
-
const styleBody = 'margin:0;overflow:hidden;background:#000';
|
|
880
|
-
const styleCanvas = 'position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)';
|
|
881
|
-
|
|
882
878
|
///////////////////////////////////////////////////////////////////////////////
|
|
883
879
|
|
|
884
880
|
/** Start up LittleJS engine with your callback functions
|
|
@@ -898,11 +894,16 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
898
894
|
tileImageFixBleed = vec2(tileFixBleedScale).divide(tileImageSize = vec2(tileImage.width, tileImage.height));
|
|
899
895
|
debug && (tileImage.onload=()=>ASSERT(1)); // tile sheet can not reloaded
|
|
900
896
|
|
|
897
|
+
// setup css
|
|
898
|
+
const styleBody = 'margin:0;overflow:hidden;background:#000' + // fill the window
|
|
899
|
+
';touch-action:none' + // prevent mobile pinch to resize
|
|
900
|
+
';user-select:none' + // prevent mobile hold to select
|
|
901
|
+
';-webkit-user-select:none;-moz-user-select:none'; // compatibility for mobile
|
|
902
|
+
|
|
901
903
|
// setup html
|
|
902
904
|
document.body.style = styleBody;
|
|
903
905
|
document.body.appendChild(mainCanvas = document.createElement('canvas'));
|
|
904
906
|
mainContext = mainCanvas.getContext('2d');
|
|
905
|
-
mainCanvas.style = styleCanvas;
|
|
906
907
|
|
|
907
908
|
// init stuff and start engine
|
|
908
909
|
debugInit();
|
|
@@ -911,13 +912,19 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
911
912
|
// create overlay canvas for hud to appear above gl canvas
|
|
912
913
|
document.body.appendChild(overlayCanvas = document.createElement('canvas'));
|
|
913
914
|
overlayContext = overlayCanvas.getContext('2d');
|
|
914
|
-
|
|
915
|
+
|
|
916
|
+
// set canvas style to fill the window
|
|
917
|
+
const styleCanvas = 'position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)';
|
|
918
|
+
(glCanvas||mainCanvas).style = overlayCanvas.style = mainCanvas.style = styleCanvas;
|
|
915
919
|
|
|
916
920
|
gameInit();
|
|
917
921
|
touchGamepadCreate();
|
|
918
922
|
engineUpdate();
|
|
919
923
|
};
|
|
920
924
|
|
|
925
|
+
// frame time tracking
|
|
926
|
+
let frameTimeLastMS = 0, frameTimeBufferMS = 0;
|
|
927
|
+
|
|
921
928
|
// main update loop
|
|
922
929
|
const engineUpdate = (frameTimeMS=0)=>
|
|
923
930
|
{
|
|
@@ -2126,6 +2133,10 @@ const isTouchDevice = window.ontouchstart !== undefined;
|
|
|
2126
2133
|
// try to enable touch mouse
|
|
2127
2134
|
if (isTouchDevice)
|
|
2128
2135
|
{
|
|
2136
|
+
// override mouse events
|
|
2137
|
+
const mouseDown = onmousedown, mouseUp = onmouseup, mouseMove = onmousemove;
|
|
2138
|
+
onmousedown = onmouseup = onmousemove = (e)=> 0;
|
|
2139
|
+
|
|
2129
2140
|
// handle all touch events the same way
|
|
2130
2141
|
let wasTouching, hadTouch;
|
|
2131
2142
|
ontouchstart = ontouchmove = ontouchend = (e)=>
|
|
@@ -2142,10 +2153,10 @@ if (isTouchDevice)
|
|
|
2142
2153
|
// set event pos and pass it along
|
|
2143
2154
|
e.x = e.touches[0].clientX;
|
|
2144
2155
|
e.y = e.touches[0].clientY;
|
|
2145
|
-
wasTouching ?
|
|
2156
|
+
wasTouching ? mouseMove(e) : mouseDown(e);
|
|
2146
2157
|
}
|
|
2147
2158
|
else if (wasTouching)
|
|
2148
|
-
|
|
2159
|
+
mouseUp(e);
|
|
2149
2160
|
|
|
2150
2161
|
// set was touching
|
|
2151
2162
|
wasTouching = touching;
|
|
@@ -3748,7 +3759,6 @@ function glInit()
|
|
|
3748
3759
|
// create the canvas and tile texture
|
|
3749
3760
|
glCanvas = document.createElement('canvas');
|
|
3750
3761
|
glContext = glCanvas.getContext('webgl', {antialias: false});
|
|
3751
|
-
glCanvas.style = styleCanvas;
|
|
3752
3762
|
glTileTexture = glCreateTexture(tileImage);
|
|
3753
3763
|
|
|
3754
3764
|
// some browsers are much faster without copying the gl buffer so we just overlay it instead
|
|
@@ -4121,8 +4131,11 @@ gl_VERTICES_PER_QUAD = 6,
|
|
|
4121
4131
|
gl_INDICIES_PER_VERT = 6,
|
|
4122
4132
|
gl_MAX_BATCH = 1<<16,
|
|
4123
4133
|
gl_VERTEX_BYTE_STRIDE = (4 * 2) * 2 + (4) * 2; // vec2 * 2 + (char * 4) * 2
|
|
4124
|
-
|
|
4125
|
-
|
|
4134
|
+
|
|
4135
|
+
/**
|
|
4136
|
+
* LittleJS Module Export
|
|
4137
|
+
* <br> - Export engine as a module with extra functions where necessary
|
|
4138
|
+
*/
|
|
4126
4139
|
|
|
4127
4140
|
// setters for all variables that devs will need to modify
|
|
4128
4141
|
const setCameraPos = (v)=> cameraPos = v;
|
|
@@ -4164,11 +4177,9 @@ const setMedalDisplayWidth = (v)=> medalDisplayWidth = v;
|
|
|
4164
4177
|
const setMedalDisplayHeight = (v)=> medalDisplayHeight = v;
|
|
4165
4178
|
const setMedalDisplayIconSize = (v)=> medalDisplayIconSize = v;
|
|
4166
4179
|
const setMedalsPreventUnlock = (v)=> medalsPreventUnlock = v;
|
|
4167
|
-
const setDebugPointSize = (v)=> debugPointSize = v;
|
|
4168
4180
|
const setShowWatermark = (v)=> showWatermark = v;
|
|
4169
4181
|
const setGodMode = (v)=> godMode = v;
|
|
4170
4182
|
|
|
4171
|
-
// This is a full list of everything in engine.all
|
|
4172
4183
|
export {
|
|
4173
4184
|
// Custom methods
|
|
4174
4185
|
setCameraPos,
|
|
@@ -4210,7 +4221,6 @@ export {
|
|
|
4210
4221
|
setMedalDisplayHeight,
|
|
4211
4222
|
setMedalDisplayIconSize,
|
|
4212
4223
|
setMedalsPreventUnlock,
|
|
4213
|
-
setDebugPointSize,
|
|
4214
4224
|
setShowWatermark,
|
|
4215
4225
|
setGodMode,
|
|
4216
4226
|
|
|
@@ -4255,7 +4265,6 @@ export {
|
|
|
4255
4265
|
|
|
4256
4266
|
// Globals
|
|
4257
4267
|
debug,
|
|
4258
|
-
debugPointSize,
|
|
4259
4268
|
showWatermark,
|
|
4260
4269
|
godMode,
|
|
4261
4270
|
// Debug
|
|
@@ -4296,6 +4305,7 @@ export {
|
|
|
4296
4305
|
isOverlapping,
|
|
4297
4306
|
wave,
|
|
4298
4307
|
formatTime,
|
|
4308
|
+
|
|
4299
4309
|
// Random
|
|
4300
4310
|
rand,
|
|
4301
4311
|
randInt,
|
|
@@ -4306,13 +4316,13 @@ export {
|
|
|
4306
4316
|
randSeed,
|
|
4307
4317
|
randSeeded,
|
|
4308
4318
|
|
|
4309
|
-
//
|
|
4310
|
-
vec2,
|
|
4319
|
+
// Utility Classes
|
|
4311
4320
|
Vector2,
|
|
4312
|
-
colorRGBA,
|
|
4313
|
-
colorHSLA,
|
|
4314
4321
|
Color,
|
|
4315
4322
|
Timer,
|
|
4323
|
+
vec2,
|
|
4324
|
+
colorRGBA,
|
|
4325
|
+
colorHSLA,
|
|
4316
4326
|
|
|
4317
4327
|
// Base
|
|
4318
4328
|
EngineObject,
|
|
@@ -4444,11 +4454,8 @@ export {
|
|
|
4444
4454
|
time,
|
|
4445
4455
|
timeReal,
|
|
4446
4456
|
paused,
|
|
4447
|
-
|
|
4448
|
-
|
|
4449
|
-
//drawCount,
|
|
4450
|
-
//styleBody,
|
|
4451
|
-
//styleCanvas,
|
|
4457
|
+
averageFPS,
|
|
4458
|
+
drawCount,
|
|
4452
4459
|
engineInit,
|
|
4453
4460
|
//enginePreRender,
|
|
4454
4461
|
//engineObjectsUpdate,
|
package/engine/engine.js
CHANGED
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
const engineName = 'LittleJS';
|
|
23
23
|
|
|
24
24
|
/** Version of engine */
|
|
25
|
-
const engineVersion = '1.4.
|
|
25
|
+
const engineVersion = '1.4.7';
|
|
26
26
|
|
|
27
27
|
/** Frames per second to update objects
|
|
28
28
|
* @default */
|
|
@@ -51,15 +51,11 @@ let timeReal = 0;
|
|
|
51
51
|
let paused = 0;
|
|
52
52
|
|
|
53
53
|
// Engine internal variables not exposed to documentation
|
|
54
|
-
let
|
|
54
|
+
let tileImageSize, tileImageFixBleed;
|
|
55
55
|
|
|
56
56
|
// Engine stat tracking, if showWatermark is true
|
|
57
57
|
let averageFPS, drawCount;
|
|
58
58
|
|
|
59
|
-
// css text used for elements created by engine
|
|
60
|
-
const styleBody = 'margin:0;overflow:hidden;background:#000';
|
|
61
|
-
const styleCanvas = 'position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)';
|
|
62
|
-
|
|
63
59
|
///////////////////////////////////////////////////////////////////////////////
|
|
64
60
|
|
|
65
61
|
/** Start up LittleJS engine with your callback functions
|
|
@@ -79,11 +75,16 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
79
75
|
tileImageFixBleed = vec2(tileFixBleedScale).divide(tileImageSize = vec2(tileImage.width, tileImage.height));
|
|
80
76
|
debug && (tileImage.onload=()=>ASSERT(1)); // tile sheet can not reloaded
|
|
81
77
|
|
|
78
|
+
// setup css
|
|
79
|
+
const styleBody = 'margin:0;overflow:hidden;background:#000' + // fill the window
|
|
80
|
+
';touch-action:none' + // prevent mobile pinch to resize
|
|
81
|
+
';user-select:none' + // prevent mobile hold to select
|
|
82
|
+
';-webkit-user-select:none;-moz-user-select:none'; // compatibility for mobile
|
|
83
|
+
|
|
82
84
|
// setup html
|
|
83
85
|
document.body.style = styleBody;
|
|
84
86
|
document.body.appendChild(mainCanvas = document.createElement('canvas'));
|
|
85
87
|
mainContext = mainCanvas.getContext('2d');
|
|
86
|
-
mainCanvas.style = styleCanvas;
|
|
87
88
|
|
|
88
89
|
// init stuff and start engine
|
|
89
90
|
debugInit();
|
|
@@ -92,13 +93,19 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
92
93
|
// create overlay canvas for hud to appear above gl canvas
|
|
93
94
|
document.body.appendChild(overlayCanvas = document.createElement('canvas'));
|
|
94
95
|
overlayContext = overlayCanvas.getContext('2d');
|
|
95
|
-
|
|
96
|
+
|
|
97
|
+
// set canvas style to fill the window
|
|
98
|
+
const styleCanvas = 'position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)';
|
|
99
|
+
(glCanvas||mainCanvas).style = overlayCanvas.style = mainCanvas.style = styleCanvas;
|
|
96
100
|
|
|
97
101
|
gameInit();
|
|
98
102
|
touchGamepadCreate();
|
|
99
103
|
engineUpdate();
|
|
100
104
|
};
|
|
101
105
|
|
|
106
|
+
// frame time tracking
|
|
107
|
+
let frameTimeLastMS = 0, frameTimeBufferMS = 0;
|
|
108
|
+
|
|
102
109
|
// main update loop
|
|
103
110
|
const engineUpdate = (frameTimeMS=0)=>
|
|
104
111
|
{
|
package/engine/engineBuild.bat
CHANGED
|
@@ -94,8 +94,27 @@ if %ERRORLEVEL% NEQ 0 (
|
|
|
94
94
|
)
|
|
95
95
|
|
|
96
96
|
rem --- BUILD TYPESCRIPT DEFINITIONS ---
|
|
97
|
+
|
|
97
98
|
call npx tsc engine.all.js --declaration --allowJs --emitDeclarationOnly --outFile index.d.ts
|
|
98
99
|
if %ERRORLEVEL% NEQ 0 (
|
|
99
100
|
pause
|
|
100
101
|
exit /b %ERRORLEVEL%
|
|
101
|
-
)
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
rem --- BUILD ENGINE MODULE ---
|
|
105
|
+
|
|
106
|
+
set OUTPUT_FILENAME=engine.all.module.js
|
|
107
|
+
del %OUTPUT_FILENAME%
|
|
108
|
+
type engine.all.js >> %OUTPUT_FILENAME%
|
|
109
|
+
echo.>> %OUTPUT_FILENAME%
|
|
110
|
+
type engineExport.js >> %OUTPUT_FILENAME%
|
|
111
|
+
echo.>> %OUTPUT_FILENAME%
|
|
112
|
+
|
|
113
|
+
rem --- BUILD ENGINE MODULE RELEASE ---
|
|
114
|
+
|
|
115
|
+
set OUTPUT_FILENAME=engine.all.release.module.js
|
|
116
|
+
del %OUTPUT_FILENAME%
|
|
117
|
+
type engine.all.release.js >> %OUTPUT_FILENAME%
|
|
118
|
+
echo.>> %OUTPUT_FILENAME%
|
|
119
|
+
type engineExport.js >> %OUTPUT_FILENAME%
|
|
120
|
+
echo.>> %OUTPUT_FILENAME%
|
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* LittleJS Module Export
|
|
3
|
+
* <br> - Export engine as a module with extra functions where necessary
|
|
4
|
+
*/
|
|
2
5
|
|
|
3
6
|
// setters for all variables that devs will need to modify
|
|
4
7
|
const setCameraPos = (v)=> cameraPos = v;
|
|
@@ -40,11 +43,9 @@ const setMedalDisplayWidth = (v)=> medalDisplayWidth = v;
|
|
|
40
43
|
const setMedalDisplayHeight = (v)=> medalDisplayHeight = v;
|
|
41
44
|
const setMedalDisplayIconSize = (v)=> medalDisplayIconSize = v;
|
|
42
45
|
const setMedalsPreventUnlock = (v)=> medalsPreventUnlock = v;
|
|
43
|
-
const setDebugPointSize = (v)=> debugPointSize = v;
|
|
44
46
|
const setShowWatermark = (v)=> showWatermark = v;
|
|
45
47
|
const setGodMode = (v)=> godMode = v;
|
|
46
48
|
|
|
47
|
-
// This is a full list of everything in engine.all
|
|
48
49
|
export {
|
|
49
50
|
// Custom methods
|
|
50
51
|
setCameraPos,
|
|
@@ -86,7 +87,6 @@ export {
|
|
|
86
87
|
setMedalDisplayHeight,
|
|
87
88
|
setMedalDisplayIconSize,
|
|
88
89
|
setMedalsPreventUnlock,
|
|
89
|
-
setDebugPointSize,
|
|
90
90
|
setShowWatermark,
|
|
91
91
|
setGodMode,
|
|
92
92
|
|
|
@@ -131,7 +131,6 @@ export {
|
|
|
131
131
|
|
|
132
132
|
// Globals
|
|
133
133
|
debug,
|
|
134
|
-
debugPointSize,
|
|
135
134
|
showWatermark,
|
|
136
135
|
godMode,
|
|
137
136
|
// Debug
|
|
@@ -172,6 +171,7 @@ export {
|
|
|
172
171
|
isOverlapping,
|
|
173
172
|
wave,
|
|
174
173
|
formatTime,
|
|
174
|
+
|
|
175
175
|
// Random
|
|
176
176
|
rand,
|
|
177
177
|
randInt,
|
|
@@ -182,13 +182,13 @@ export {
|
|
|
182
182
|
randSeed,
|
|
183
183
|
randSeeded,
|
|
184
184
|
|
|
185
|
-
//
|
|
186
|
-
vec2,
|
|
185
|
+
// Utility Classes
|
|
187
186
|
Vector2,
|
|
188
|
-
colorRGBA,
|
|
189
|
-
colorHSLA,
|
|
190
187
|
Color,
|
|
191
188
|
Timer,
|
|
189
|
+
vec2,
|
|
190
|
+
colorRGBA,
|
|
191
|
+
colorHSLA,
|
|
192
192
|
|
|
193
193
|
// Base
|
|
194
194
|
EngineObject,
|
|
@@ -320,14 +320,11 @@ export {
|
|
|
320
320
|
time,
|
|
321
321
|
timeReal,
|
|
322
322
|
paused,
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
//drawCount,
|
|
326
|
-
//styleBody,
|
|
327
|
-
//styleCanvas,
|
|
323
|
+
averageFPS,
|
|
324
|
+
drawCount,
|
|
328
325
|
engineInit,
|
|
329
326
|
//enginePreRender,
|
|
330
327
|
//engineObjectsUpdate,
|
|
331
328
|
engineObjectsDestroy,
|
|
332
329
|
engineObjectsCallback,
|
|
333
|
-
};
|
|
330
|
+
};
|
package/engine/engineFont.png
CHANGED
|
Binary file
|
package/engine/engineInput.js
CHANGED
|
@@ -253,6 +253,10 @@ const isTouchDevice = window.ontouchstart !== undefined;
|
|
|
253
253
|
// try to enable touch mouse
|
|
254
254
|
if (isTouchDevice)
|
|
255
255
|
{
|
|
256
|
+
// override mouse events
|
|
257
|
+
const mouseDown = onmousedown, mouseUp = onmouseup, mouseMove = onmousemove;
|
|
258
|
+
onmousedown = onmouseup = onmousemove = (e)=> 0;
|
|
259
|
+
|
|
256
260
|
// handle all touch events the same way
|
|
257
261
|
let wasTouching, hadTouch;
|
|
258
262
|
ontouchstart = ontouchmove = ontouchend = (e)=>
|
|
@@ -269,10 +273,10 @@ if (isTouchDevice)
|
|
|
269
273
|
// set event pos and pass it along
|
|
270
274
|
e.x = e.touches[0].clientX;
|
|
271
275
|
e.y = e.touches[0].clientY;
|
|
272
|
-
wasTouching ?
|
|
276
|
+
wasTouching ? mouseMove(e) : mouseDown(e);
|
|
273
277
|
}
|
|
274
278
|
else if (wasTouching)
|
|
275
|
-
|
|
279
|
+
mouseUp(e);
|
|
276
280
|
|
|
277
281
|
// set was touching
|
|
278
282
|
wasTouching = touching;
|
package/engine/engineSettings.js
CHANGED
|
@@ -159,7 +159,7 @@ let touchGamepadAnalog = 1;
|
|
|
159
159
|
/** Size of virutal gamepad for touch devices in pixels
|
|
160
160
|
* @default
|
|
161
161
|
* @memberof Settings */
|
|
162
|
-
let touchGamepadSize =
|
|
162
|
+
let touchGamepadSize = 99;
|
|
163
163
|
|
|
164
164
|
/** Transparency of touch gamepad overlay
|
|
165
165
|
* @default
|
package/engine/engineWebGL.js
CHANGED
|
@@ -37,7 +37,6 @@ function glInit()
|
|
|
37
37
|
// create the canvas and tile texture
|
|
38
38
|
glCanvas = document.createElement('canvas');
|
|
39
39
|
glContext = glCanvas.getContext('webgl', {antialias: false});
|
|
40
|
-
glCanvas.style = styleCanvas;
|
|
41
40
|
glTileTexture = glCreateTexture(tileImage);
|
|
42
41
|
|
|
43
42
|
// some browsers are much faster without copying the gl buffer so we just overlay it instead
|
package/engine/index.d.ts
CHANGED
|
@@ -1664,7 +1664,7 @@ declare const gl_VERTEX_BYTE_STRIDE: number;
|
|
|
1664
1664
|
/** Name of engine */
|
|
1665
1665
|
declare const engineName: "LittleJS";
|
|
1666
1666
|
/** Version of engine */
|
|
1667
|
-
declare const engineVersion: "1.4.
|
|
1667
|
+
declare const engineVersion: "1.4.7";
|
|
1668
1668
|
/** Frames per second to update objects
|
|
1669
1669
|
* @default */
|
|
1670
1670
|
declare const frameRate: 60;
|
|
@@ -1683,11 +1683,7 @@ declare let time: number;
|
|
|
1683
1683
|
declare let timeReal: number;
|
|
1684
1684
|
/** Is the game paused? Causes time and objects to not be updated. */
|
|
1685
1685
|
declare let paused: number;
|
|
1686
|
-
declare let frameTimeLastMS: number;
|
|
1687
|
-
declare let frameTimeBufferMS: number;
|
|
1688
1686
|
declare let tileImageSize: any;
|
|
1689
1687
|
declare let tileImageFixBleed: any;
|
|
1690
1688
|
declare let averageFPS: any;
|
|
1691
1689
|
declare let drawCount: any;
|
|
1692
|
-
declare const styleBody: "margin:0;overflow:hidden;background:#000";
|
|
1693
|
-
declare const styleCanvas: "position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)";
|
|
@@ -5,6 +5,6 @@
|
|
|
5
5
|
<link rel=icon type=image/png href=../../favicon.png>
|
|
6
6
|
</head><body>
|
|
7
7
|
|
|
8
|
-
<script src=../../engine/engine.all.js?
|
|
9
|
-
<script src=gameObjects.js?
|
|
10
|
-
<script src=game.js?
|
|
8
|
+
<script src=../../engine/engine.all.js?120></script>
|
|
9
|
+
<script src=gameObjects.js?120></script>
|
|
10
|
+
<script src=game.js?120></script>
|
|
@@ -15,18 +15,18 @@ button
|
|
|
15
15
|
</style>
|
|
16
16
|
</head><body>
|
|
17
17
|
|
|
18
|
-
<script src=../../engine/engineDebug.js?
|
|
19
|
-
<script src=../../engine/engineUtilities.js?
|
|
20
|
-
<script src=../../engine/engineSettings.js?
|
|
21
|
-
<script src=../../engine/engineObject.js?
|
|
22
|
-
<script src=../../engine/engineDraw.js?
|
|
23
|
-
<script src=../../engine/engineInput.js?
|
|
24
|
-
<script src=../../engine/engineAudio.js?
|
|
25
|
-
<script src=../../engine/engineTileLayer.js?
|
|
26
|
-
<script src=../../engine/engineParticles.js?
|
|
27
|
-
<script src=../../engine/engineMedals.js?
|
|
28
|
-
<script src=../../engine/engineWebGL.js?
|
|
29
|
-
<script src=../../engine/engine.js?
|
|
18
|
+
<script src=../../engine/engineDebug.js?120></script>
|
|
19
|
+
<script src=../../engine/engineUtilities.js?120></script>
|
|
20
|
+
<script src=../../engine/engineSettings.js?120></script>
|
|
21
|
+
<script src=../../engine/engineObject.js?120></script>
|
|
22
|
+
<script src=../../engine/engineDraw.js?120></script>
|
|
23
|
+
<script src=../../engine/engineInput.js?120></script>
|
|
24
|
+
<script src=../../engine/engineAudio.js?120></script>
|
|
25
|
+
<script src=../../engine/engineTileLayer.js?120></script>
|
|
26
|
+
<script src=../../engine/engineParticles.js?120></script>
|
|
27
|
+
<script src=../../engine/engineMedals.js?120></script>
|
|
28
|
+
<script src=../../engine/engineWebGL.js?120></script>
|
|
29
|
+
<script src=../../engine/engine.js?120></script>
|
|
30
30
|
<script>
|
|
31
31
|
|
|
32
32
|
'use strict';
|
|
@@ -5,9 +5,9 @@
|
|
|
5
5
|
<link rel=icon type=image/png href=../../favicon.png>
|
|
6
6
|
</head><body>
|
|
7
7
|
|
|
8
|
-
<script src=../../engine/engine.all.js?
|
|
9
|
-
<script src=gameObjects.js?
|
|
10
|
-
<script src=gamePlayer.js?
|
|
11
|
-
<script src=gameEffects.js?
|
|
12
|
-
<script src=gameLevel.js?
|
|
13
|
-
<script src=game.js?
|
|
8
|
+
<script src=../../engine/engine.all.js?120></script>
|
|
9
|
+
<script src=gameObjects.js?120></script>
|
|
10
|
+
<script src=gamePlayer.js?120></script>
|
|
11
|
+
<script src=gameEffects.js?120></script>
|
|
12
|
+
<script src=gameLevel.js?120></script>
|
|
13
|
+
<script src=game.js?120></script>
|
package/index.html
CHANGED
|
@@ -6,21 +6,21 @@
|
|
|
6
6
|
</head><body>
|
|
7
7
|
|
|
8
8
|
<!-- LittleJS engine scripts -->
|
|
9
|
-
<script src=engine/engineDebug.js?
|
|
10
|
-
<script src=engine/engineUtilities.js?
|
|
11
|
-
<script src=engine/engineSettings.js?
|
|
12
|
-
<script src=engine/engineObject.js?
|
|
13
|
-
<script src=engine/engineDraw.js?
|
|
14
|
-
<script src=engine/engineInput.js?
|
|
15
|
-
<script src=engine/engineAudio.js?
|
|
16
|
-
<script src=engine/engineTileLayer.js?
|
|
17
|
-
<script src=engine/engineParticles.js?
|
|
18
|
-
<script src=engine/engineMedals.js?
|
|
19
|
-
<script src=engine/engineWebGL.js?
|
|
20
|
-
<script src=engine/engine.js?
|
|
9
|
+
<script src=engine/engineDebug.js?120></script>
|
|
10
|
+
<script src=engine/engineUtilities.js?120></script>
|
|
11
|
+
<script src=engine/engineSettings.js?120></script>
|
|
12
|
+
<script src=engine/engineObject.js?120></script>
|
|
13
|
+
<script src=engine/engineDraw.js?120></script>
|
|
14
|
+
<script src=engine/engineInput.js?120></script>
|
|
15
|
+
<script src=engine/engineAudio.js?120></script>
|
|
16
|
+
<script src=engine/engineTileLayer.js?120></script>
|
|
17
|
+
<script src=engine/engineParticles.js?120></script>
|
|
18
|
+
<script src=engine/engineMedals.js?120></script>
|
|
19
|
+
<script src=engine/engineWebGL.js?120></script>
|
|
20
|
+
<script src=engine/engine.js?120></script>
|
|
21
21
|
|
|
22
22
|
<!-- You can also include all engine scripts like this... -->
|
|
23
23
|
<!-- <script src=engine/engine.all.js></script> -->
|
|
24
24
|
|
|
25
25
|
<!-- Add your game scripts here -->
|
|
26
|
-
<script src=game.js?
|
|
26
|
+
<script src=game.js?120></script>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "littlejsengine",
|
|
3
|
-
"version": "1.4.
|
|
3
|
+
"version": "1.4.7",
|
|
4
4
|
"description": "LittleJS - Tiny and Fast HTML5 Game Engine",
|
|
5
5
|
"main": "engine/engine.all.module.js",
|
|
6
6
|
"repository": {
|
|
@@ -23,14 +23,13 @@
|
|
|
23
23
|
},
|
|
24
24
|
"homepage": "https://github.com/KilledByAPixel/LittleJS",
|
|
25
25
|
"scripts": {
|
|
26
|
-
"build
|
|
27
|
-
"build": "build.bat && npm run build:esm && npm run check-filesize"
|
|
26
|
+
"build": "build.bat"
|
|
28
27
|
},
|
|
29
28
|
"dependencies": {
|
|
30
29
|
"ect-bin": "^1.4.1",
|
|
31
30
|
"google-closure-compiler": "^20221102.0.1",
|
|
32
31
|
"roadroller": "^2.1.0",
|
|
33
|
-
"typescript": "^4.9.
|
|
32
|
+
"typescript": "^4.9.5",
|
|
34
33
|
"uglify-js": "^3.17.4"
|
|
35
34
|
}
|
|
36
35
|
}
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import fs from 'fs'; // node
|
|
2
|
-
|
|
3
|
-
const files = [
|
|
4
|
-
{ engine: './engine/engine.all.js', output: './engine/engine.all.module.js' },
|
|
5
|
-
{ engine: './engine/engine.all.release.js', output: './engine/engine.all.release.module.js' },
|
|
6
|
-
];
|
|
7
|
-
|
|
8
|
-
const footerFile = fs.readFileSync('./engine/engineExportModule.js');
|
|
9
|
-
|
|
10
|
-
files.forEach(({ engine, output }) => {
|
|
11
|
-
const engineFile = fs.readFileSync(engine);
|
|
12
|
-
const file = [engineFile, footerFile].join('\n');
|
|
13
|
-
fs.writeFileSync(output, file);
|
|
14
|
-
});
|