littlejsengine 1.11.6 → 1.11.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/README.md +11 -16
- package/dist/littlejs.d.ts +14 -1
- package/dist/littlejs.esm.js +30 -19
- package/dist/littlejs.esm.min.js +1 -1
- package/dist/littlejs.js +27 -19
- package/dist/littlejs.min.js +1 -1
- package/dist/littlejs.release.js +23 -18
- package/examples/box2d/game.js +2 -2
- package/examples/box2d/index.html +5 -5
- package/examples/breakout/game.js +6 -5
- package/examples/breakout/index.html +4 -4
- package/examples/breakoutTutorial/index.html +2 -2
- package/examples/htmlMenu/index.html +2 -2
- package/examples/index.html +300 -32
- package/examples/module/index.html +1 -1
- package/examples/particles/index.html +1 -1
- package/examples/platformer/gamePlayer.js +2 -7
- package/examples/platformer/index.html +8 -8
- package/examples/puzzle/index.html +2 -2
- package/examples/shorts/base.html +26 -0
- package/{shortExamples/code → examples/shorts}/blending.js +1 -1
- package/examples/shorts/platformer.js +42 -0
- package/{shortExamples/code → examples/shorts}/playSound.js +3 -0
- package/{shortExamples/code → examples/shorts}/pong.js +3 -3
- package/examples/shorts/tiltedView.js +44 -0
- package/{shortExamples/code → examples/shorts}/timers.js +7 -3
- package/examples/shorts/topDown.js +44 -0
- package/examples/starter/index.html +13 -13
- package/examples/stress/index.html +2 -2
- package/examples/uiSystem/game.js +6 -1
- package/examples/uiSystem/index.html +3 -3
- package/package.json +1 -1
- package/plugins/Box2D_License.txt +17 -0
- package/plugins/uiSystem.js +2 -1
- package/reference.md +2 -1
- package/src/engine.js +1 -1
- package/src/engineDebug.js +5 -1
- package/src/engineExport.js +3 -0
- package/src/engineInput.js +19 -15
- package/src/engineRelease.js +1 -0
- package/src/engineTileLayer.js +2 -2
- package/shortExamples/base.html +0 -39
- package/shortExamples/index.html +0 -246
- /package/{shortExamples/code → examples/shorts}/animation.js +0 -0
- /package/{shortExamples/code → examples/shorts}/clock.js +0 -0
- /package/{shortExamples/code → examples/shorts}/colors.js +0 -0
- /package/{shortExamples/code → examples/shorts}/helloWorld.js +0 -0
- /package/{shortExamples/code → examples/shorts}/particles.js +0 -0
- /package/{shortExamples/code → examples/shorts}/shapes.js +0 -0
- /package/{shortExamples/code → examples/shorts}/spriteAtlas.js +0 -0
- /package/{shortExamples/code → examples/shorts}/systemFont.js +0 -0
- /package/{shortExamples/code → examples/shorts}/texture.js +0 -0
- /package/{shortExamples/code → examples/shorts}/tileLayer.js +0 -0
- /package/{shortExamples → examples/shorts}/tiles.png +0 -0
package/dist/littlejs.js
CHANGED
|
@@ -167,6 +167,10 @@ function debugText(text, pos, size=1, color='#fff', time=0, angle=0, font='monos
|
|
|
167
167
|
* @memberof Debug */
|
|
168
168
|
function debugClear() { debugPrimitives = []; }
|
|
169
169
|
|
|
170
|
+
/** Trigger debug system to take a screenshot
|
|
171
|
+
* @memberof Debug */
|
|
172
|
+
function debugScreenshot() { debugTakeScreenshot = 1; }
|
|
173
|
+
|
|
170
174
|
/** Save a canvas to disk
|
|
171
175
|
* @param {HTMLCanvasElement} canvas
|
|
172
176
|
* @param {String} [filename]
|
|
@@ -240,7 +244,7 @@ function debugUpdate()
|
|
|
240
244
|
if (keyWasPressed('Digit4'))
|
|
241
245
|
debugRaycast = !debugRaycast;
|
|
242
246
|
if (keyWasPressed('Digit5'))
|
|
243
|
-
|
|
247
|
+
debugScreenshot();
|
|
244
248
|
}
|
|
245
249
|
}
|
|
246
250
|
|
|
@@ -3003,6 +3007,15 @@ function keyWasReleased(key, device=0)
|
|
|
3003
3007
|
return inputData[device] && !!(inputData[device][key] & 4);
|
|
3004
3008
|
}
|
|
3005
3009
|
|
|
3010
|
+
/** Returns input vector from arrow keys or WASD if enabled
|
|
3011
|
+
* @return {Vector2}
|
|
3012
|
+
* @memberof Input */
|
|
3013
|
+
function keyDirection(up='ArrowUp', down='ArrowDown', left='ArrowLeft', right='ArrowRight')
|
|
3014
|
+
{
|
|
3015
|
+
const k = (key)=> keyIsDown(key) ? 1 : 0;
|
|
3016
|
+
return vec2(k(right) - k(left), k(up) - k(down));
|
|
3017
|
+
}
|
|
3018
|
+
|
|
3006
3019
|
/** Clears all input
|
|
3007
3020
|
* @memberof Input */
|
|
3008
3021
|
function clearInput() { inputData = [[]]; touchGamepadButtons = []; }
|
|
@@ -3086,9 +3099,9 @@ function gamepadStick(stick, gamepad=0)
|
|
|
3086
3099
|
{ return gamepadStickData[gamepad] ? gamepadStickData[gamepad][stick] || vec2() : vec2(); }
|
|
3087
3100
|
|
|
3088
3101
|
///////////////////////////////////////////////////////////////////////////////
|
|
3089
|
-
// Input
|
|
3102
|
+
// Input system functions called automatically by engine
|
|
3090
3103
|
|
|
3091
|
-
//
|
|
3104
|
+
// input is stored as a bit field for each key: 1 = isDown, 2 = wasPressed, 4 = wasReleased
|
|
3092
3105
|
// mouse and keyboard are stored together in device 0, gamepads are in devices > 0
|
|
3093
3106
|
let inputData = [[]];
|
|
3094
3107
|
|
|
@@ -3118,9 +3131,6 @@ function inputUpdatePost()
|
|
|
3118
3131
|
mouseWheel = 0;
|
|
3119
3132
|
}
|
|
3120
3133
|
|
|
3121
|
-
///////////////////////////////////////////////////////////////////////////////
|
|
3122
|
-
// Input event handlers
|
|
3123
|
-
|
|
3124
3134
|
function inputInit()
|
|
3125
3135
|
{
|
|
3126
3136
|
if (headlessMode) return;
|
|
@@ -3163,11 +3173,11 @@ function inputInit()
|
|
|
3163
3173
|
|
|
3164
3174
|
isUsingGamepad = false;
|
|
3165
3175
|
inputData[0][e.button] = 3;
|
|
3166
|
-
mousePosScreen =
|
|
3176
|
+
mousePosScreen = mouseEventToScreen(e);
|
|
3167
3177
|
e.button && e.preventDefault();
|
|
3168
3178
|
}
|
|
3169
3179
|
onmouseup = (e)=> inputData[0][e.button] = inputData[0][e.button] & 2 | 4;
|
|
3170
|
-
onmousemove = (e)=> mousePosScreen =
|
|
3180
|
+
onmousemove = (e)=> mousePosScreen = mouseEventToScreen(e);
|
|
3171
3181
|
onwheel = (e)=> mouseWheel = e.ctrlKey ? 0 : sign(e.deltaY);
|
|
3172
3182
|
oncontextmenu = (e)=> false; // prevent right click menu
|
|
3173
3183
|
onblur = (e) => clearInput(); // reset input when focus is lost
|
|
@@ -3178,14 +3188,12 @@ function inputInit()
|
|
|
3178
3188
|
}
|
|
3179
3189
|
|
|
3180
3190
|
// convert a mouse or touch event position to screen space
|
|
3181
|
-
function
|
|
3191
|
+
function mouseEventToScreen(mousePos)
|
|
3182
3192
|
{
|
|
3183
|
-
if (!mainCanvas || headlessMode)
|
|
3184
|
-
return vec2(); // fix bug that can occur if user clicks before page loads
|
|
3185
|
-
|
|
3186
3193
|
const rect = mainCanvas.getBoundingClientRect();
|
|
3187
|
-
|
|
3188
|
-
|
|
3194
|
+
const px = percent(mousePos.x, rect.left, rect.right);
|
|
3195
|
+
const py = percent(mousePos.y, rect.top, rect.bottom);
|
|
3196
|
+
return vec2(px*mainCanvas.width, py*mainCanvas.height);
|
|
3189
3197
|
}
|
|
3190
3198
|
|
|
3191
3199
|
///////////////////////////////////////////////////////////////////////////////
|
|
@@ -3340,7 +3348,7 @@ function touchInputInit()
|
|
|
3340
3348
|
{
|
|
3341
3349
|
// set event pos and pass it along
|
|
3342
3350
|
const p = vec2(e.touches[0].clientX, e.touches[0].clientY);
|
|
3343
|
-
mousePosScreen =
|
|
3351
|
+
mousePosScreen = mouseEventToScreen(p);
|
|
3344
3352
|
wasTouching ? isUsingGamepad = touchGamepadEnable : inputData[0][button] = 3;
|
|
3345
3353
|
}
|
|
3346
3354
|
else if (wasTouching)
|
|
@@ -3388,7 +3396,7 @@ function touchInputInit()
|
|
|
3388
3396
|
// check each touch point
|
|
3389
3397
|
for (const touch of e.touches)
|
|
3390
3398
|
{
|
|
3391
|
-
const touchPos =
|
|
3399
|
+
const touchPos = mouseEventToScreen(vec2(touch.clientX, touch.clientY));
|
|
3392
3400
|
if (touchPos.distance(stickCenter) < touchGamepadSize)
|
|
3393
3401
|
{
|
|
3394
3402
|
// virtual analog stick
|
|
@@ -4305,10 +4313,10 @@ class TileLayer extends EngineObject
|
|
|
4305
4313
|
!glOverlay && !this.isOverlay && glCopyToContext(mainContext);
|
|
4306
4314
|
|
|
4307
4315
|
// draw the entire cached level onto the canvas
|
|
4308
|
-
|
|
4316
|
+
let pos = worldToScreen(this.pos.add(vec2(0,this.size.y*this.scale.y)));
|
|
4309
4317
|
|
|
4310
4318
|
// fix canvas jitter in some browsers if position is not an integer
|
|
4311
|
-
pos
|
|
4319
|
+
pos = pos.floor();
|
|
4312
4320
|
|
|
4313
4321
|
(this.isOverlay ? overlayContext : mainContext).drawImage
|
|
4314
4322
|
(
|
|
@@ -5301,7 +5309,7 @@ const engineName = 'LittleJS';
|
|
|
5301
5309
|
* @type {String}
|
|
5302
5310
|
* @default
|
|
5303
5311
|
* @memberof Engine */
|
|
5304
|
-
const engineVersion = '1.11.
|
|
5312
|
+
const engineVersion = '1.11.7';
|
|
5305
5313
|
|
|
5306
5314
|
/** Frames per second to update
|
|
5307
5315
|
* @type {Number}
|
package/dist/littlejs.min.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
let showWatermark=0,debugKey="";const debug=0,debugOverlay=0,debugPhysics=0,debugParticles=0,debugRaycast=0,debugGamepads=0,debugMedals=0;function ASSERT(){}function debugInit(){}function debugUpdate(){}function debugRender(){}function debugRect(){}function debugPoly(){}function debugCircle(){}function debugPoint(){}function debugLine(){}function debugOverlap(){}function debugText(){}function debugClear(){}function debugSaveCanvas(){}function debugSaveText(){}function debugSaveDataURL(){}const PI=Math.PI;function abs(a){return Math.abs(a)}function min(a,b){return Math.min(a,b)}function max(a,b){return Math.max(a,b)}function sign(a){return Math.sign(a)}function mod(a,b=1){return(a%b+b)%b}function clamp(a,b=0,c=1){return a<b?b:a>c?c:a}function percent(a,b,c){return(c-=b)?clamp((a-b)/c):0}function lerp(a,b,c){return b+clamp(a)*(c-b)}function distanceWrap(a,b,c=1){a=(a-b)%c;return 2*a%c-a}function lerpWrap(a,b,c,d=1){return c+clamp(a)*distanceWrap(b,c,d)}function distanceAngle(a,b){return distanceWrap(a,b,2*PI)}function lerpAngle(a,b,c){return lerpWrap(a,b,c,2*PI)}function smoothStep(a){return a*a*(3-2*a)}function nearestPowerOfTwo(a){return 2**Math.ceil(Math.log2(a))}function isOverlapping(a,b,c,d=vec2()){return 2*abs(a.x-c.x)<b.x+d.x&&2*abs(a.y-c.y)<b.y+d.y}function isIntersecting(a,b,c,d){c=c.subtract(d.scale(.5));d=c.add(d);b=b.subtract(a);c=a.subtract(c);d=a.subtract(d);a=[-b.x,b.x,-b.y,b.y];b=[c.x,-d.x,c.y,-d.y];c=0;d=1;for(let e=4;e--;)if(a[e]){const f=b[e]/a[e];if(0>a[e]){if(f>d)return!1;c=max(f,c)}else{if(f<c)return!1;d=min(f,d)}}else if(0>b[e])return!1;return!0}function wave(a=1,b=1,c=time){return b/2*(1-Math.cos(c*a*2*PI))}function formatTime(a){return(a/60|0)+":"+(10>a%60?"0":"")+(a%60|0)}function rand(a=1,b=0){return b+Math.random()*(a-b)}function randInt(a,b=0){return Math.floor(rand(a,b))}function randSign(){return 2*randInt(2)-1}function randVector(a=1){return(new Vector2).setAngle(rand(2*PI),a)}function randInCircle(a=1,b=0){return 0<a?randVector(a*rand(b/a,1)**.5):new Vector2}function randColor(a=new Color,b=new Color(0,0,0,1),c=!1){return c?a.lerp(b,rand()):new Color(rand(a.r,b.r),rand(a.g,b.g),rand(a.b,b.b),rand(a.a,b.a))}class RandomGenerator{constructor(a){this.seed=a}float(a=1,b=0){this.seed^=this.seed<<13;this.seed^=this.seed>>>17;this.seed^=this.seed<<5;return b+(a-b)*abs(this.seed%1e8)/1e8}int(a,b=0){return Math.floor(this.float(a,b))}sign(){return.5<this.float()?1:-1}}function vec2(a=0,b){return"number"==typeof a?new Vector2(a,void 0==b?a:b):new Vector2(a.x,a.y)}function isVector2(a){return a instanceof Vector2}class Vector2{constructor(a=0,b=0){this.x=a;this.y=b;ASSERT(this.isValid())}set(a=0,b=0){this.x=a;this.y=b;ASSERT(this.isValid());return this}copy(){return new Vector2(this.x,this.y)}add(a){ASSERT(isVector2(a));return new Vector2(this.x+a.x,this.y+a.y)}subtract(a){ASSERT(isVector2(a));return new Vector2(this.x-a.x,this.y-a.y)}multiply(a){ASSERT(isVector2(a));return new Vector2(this.x*a.x,this.y*a.y)}divide(a){ASSERT(isVector2(a));return new Vector2(this.x/a.x,this.y/a.y)}scale(a){ASSERT(!isVector2(a));return new Vector2(this.x*a,this.y*a)}length(){return this.lengthSquared()**.5}lengthSquared(){return this.x**2+this.y**2}distance(a){ASSERT(isVector2(a));return this.distanceSquared(a)**.5}distanceSquared(a){ASSERT(isVector2(a));return(this.x-a.x)**2+(this.y-a.y)**2}normalize(a=1){const b=this.length();return b?this.scale(a/b):new Vector2(0,a)}clampLength(a=1){const b=this.length();return b>a?this.scale(a/b):this}dot(a){ASSERT(isVector2(a));return this.x*a.x+this.y*a.y}cross(a){ASSERT(isVector2(a));return this.x*a.y-this.y*a.x}angle(){return Math.atan2(this.x,this.y)}setAngle(a=0,b=1){this.x=b*Math.sin(a);this.y=b*Math.cos(a);return this}rotate(a){const b=Math.cos(a);a=Math.sin(a);return new Vector2(this.x*b+this.y*a,this.x*a+this.y*b)}setDirection(a,b=1){a=mod(a,4);ASSERT(0==a||1==a||2==a||3==a);return vec2(a%2?a-1?-b:b:0,a%2?0:a?-b:b)}direction(){return abs(this.x)>abs(this.y)?0>this.x?3:1:0>this.y?2:0}invert(){return new Vector2(this.y,-this.x)}floor(){return new Vector2(Math.floor(this.x),Math.floor(this.y))}area(){return abs(this.x*this.y)}lerp(a,b){ASSERT(isVector2(a));return this.add(a.subtract(this).scale(clamp(b)))}arrayCheck(a){ASSERT(isVector2(a));return 0<=this.x&&0<=this.y&&this.x<a.x&&this.y<a.y}toString(a=3){if(debug)return`(${(0>this.x?"":" ")+this.x.toFixed(a)},${(0>this.y?"":" ")+this.y.toFixed(a)} )`}isValid(){return"number"==typeof this.x&&!isNaN(this.x)&&"number"==typeof this.y&&!isNaN(this.y)}}function rgb(a,b,c,d){return new Color(a,b,c,d)}function hsl(a,b,c,d){return(new Color).setHSLA(a,b,c,d)}function isColor(a){return a instanceof Color}class Color{constructor(a=1,b=1,c=1,d=1){this.r=a;this.g=b;this.b=c;this.a=d;ASSERT(this.isValid())}set(a=1,b=1,c=1,d=1){this.r=a;this.g=b;this.b=c;this.a=d;ASSERT(this.isValid());return this}copy(){return new Color(this.r,this.g,this.b,this.a)}add(a){ASSERT(isColor(a));return new Color(this.r+a.r,this.g+a.g,this.b+a.b,this.a+a.a)}subtract(a){ASSERT(isColor(a));return new Color(this.r-a.r,this.g-a.g,this.b-a.b,this.a-a.a)}multiply(a){ASSERT(isColor(a));return new Color(this.r*a.r,this.g*a.g,this.b*a.b,this.a*a.a)}divide(a){ASSERT(isColor(a));return new Color(this.r/a.r,this.g/a.g,this.b/a.b,this.a/a.a)}scale(a,b=a){return new Color(this.r*a,this.g*a,this.b*a,this.a*b)}clamp(){return new Color(clamp(this.r),clamp(this.g),clamp(this.b),clamp(this.a))}lerp(a,b){ASSERT(isColor(a));return this.add(a.subtract(this).scale(clamp(b)))}setHSLA(a=0,b=0,c=1,d=1){a=mod(a,1);b=clamp(b);c=clamp(c);b=.5>c?c*(1+b):c+b-c*b;c=2*c-b;const e=(f,g,k)=>1>6*(k=mod(k,1))?f+6*(g-f)*k:1>2*k?g:2>3*k?f+(g-f)*(4-6*k):f;this.r=e(c,b,a+1/3);this.g=e(c,b,a);this.b=e(c,b,a-1/3);this.a=d;ASSERT(this.isValid());return this}HSLA(){const a=clamp(this.r),b=clamp(this.g),c=clamp(this.b),d=clamp(this.a),e=Math.max(a,b,c),f=Math.min(a,b,c),g=(e+f)/2;let k=0,h=0;if(e!=f){let m=e-f;h=.5<g?m/(2-e-f):m/(e+f);a==e?k=(b-c)/m+(b<c?6:0):b==e?k=(c-a)/m+2:c==e&&(k=(a-b)/m+4)}return[k/6,h,g,d]}mutate(a=.05,b=0){return new Color(this.r+rand(a,-a),this.g+rand(a,-a),this.b+rand(a,-a),this.a+rand(b,-b)).clamp()}toString(a=!0){const b=c=>(16>(c=255*clamp(c)|0)?"0":"")+c.toString(16);return"#"+b(this.r)+b(this.g)+b(this.b)+(a?b(this.a):"")}setHex(a){ASSERT("string"==typeof a&&"#"==a[0]);ASSERT([4,5,7,9].includes(a.length),"Invalid hex");6>a.length?(this.r=clamp(parseInt(a[1],16)/15),this.g=clamp(parseInt(a[2],16)/15),this.b=clamp(parseInt(a[3],16)/15),this.a=5==a.length?clamp(parseInt(a[4],16)/15):1):(this.r=clamp(parseInt(a.slice(1,3),16)/255),this.g=clamp(parseInt(a.slice(3,5),16)/255),this.b=clamp(parseInt(a.slice(5,7),16)/255),this.a=9==a.length?clamp(parseInt(a.slice(7,9),16)/255):1);ASSERT(this.isValid());return this}rgbaInt(){const a=255*clamp(this.r)|0,b=255*clamp(this.g)<<8,c=255*clamp(this.b)<<16,d=255*clamp(this.a)<<24;return a+b+c+d}isValid(){return"number"==typeof this.r&&!isNaN(this.r)&&"number"==typeof this.g&&!isNaN(this.g)&&"number"==typeof this.b&&!isNaN(this.b)&&"number"==typeof this.a&&!isNaN(this.a)}}const WHITE=rgb(),BLACK=rgb(0,0,0),GRAY=rgb(.5,.5,.5),RED=rgb(1,0,0),ORANGE=rgb(1,.5,0),YELLOW=rgb(1,1,0),GREEN=rgb(0,1,0),CYAN=rgb(0,1,1),BLUE=rgb(0,0,1),PURPLE=rgb(.5,0,1),MAGENTA=rgb(1,0,1);class Timer{constructor(a){this.time=void 0==a?void 0:time+a;this.setTime=a}set(a=0){this.time=time+a;this.setTime=a}unset(){this.time=void 0}isSet(){return void 0!=this.time}active(){return time<this.time}elapsed(){return time>=this.time}get(){return this.isSet()?time-this.time:0}getPercent(){return this.isSet()?1-percent(this.time-time,0,this.setTime):0}toString(){if(debug)return this.isSet()?Math.abs(this.get())+" seconds "+(0>this.get()?"before":"after"):"unset"}valueOf(){return this.get()}}let cameraPos=vec2(),cameraScale=32,canvasMaxSize=vec2(1920,1080),canvasFixedSize=vec2(),canvasPixelated=!0,tilesPixelated=!0,fontDefault="arial",showSplashScreen=!1,headlessMode=!1,glEnable=!0,glOverlay=!0,tileSizeDefault=vec2(16),tileFixBleedScale=0,enablePhysicsSolver=!0,objectDefaultMass=1,objectDefaultDamping=1,objectDefaultAngleDamping=1,objectDefaultElasticity=0,objectDefaultFriction=.8,objectMaxSpeed=1,gravity=0,particleEmitRateScale=1,gamepadsEnable=!0,gamepadDirectionEmulateStick=!0,inputWASDEmulateDirection=!0,touchInputEnable=!0,touchGamepadEnable=!1,touchGamepadAnalog=!0,touchGamepadSize=99,touchGamepadAlpha=.3,vibrateEnable=!0,soundEnable=!0,soundVolume=.3,soundDefaultRange=40,soundDefaultTaper=.7,medalDisplayTime=5,medalDisplaySlideTime=.5,medalDisplaySize=vec2(640,80),medalDisplayIconSize=50,medalsPreventUnlock=!1;function setCameraPos(a){cameraPos=a}function setCameraScale(a){cameraScale=a}function setCanvasMaxSize(a){canvasMaxSize=a}function setCanvasFixedSize(a){canvasFixedSize=a}function setCanvasPixelated(a){canvasPixelated=a}function setTilesPixelated(a){tilesPixelated=a}function setFontDefault(a){fontDefault=a}function setShowSplashScreen(a){showSplashScreen=a}function setHeadlessMode(a){headlessMode=a}function setGlEnable(a){glEnable=a}function setGlOverlay(a){glOverlay=a}function setTileSizeDefault(a){tileSizeDefault=a}function setTileFixBleedScale(a){tileFixBleedScale=a}function setEnablePhysicsSolver(a){enablePhysicsSolver=a}function setObjectDefaultMass(a){objectDefaultMass=a}function setObjectDefaultDamping(a){objectDefaultDamping=a}function setObjectDefaultAngleDamping(a){objectDefaultAngleDamping=a}function setObjectDefaultElasticity(a){objectDefaultElasticity=a}function setObjectDefaultFriction(a){objectDefaultFriction=a}function setObjectMaxSpeed(a){objectMaxSpeed=a}function setGravity(a){gravity=a}function setParticleEmitRateScale(a){particleEmitRateScale=a}function setGamepadsEnable(a){gamepadsEnable=a}function setGamepadDirectionEmulateStick(a){gamepadDirectionEmulateStick=a}function setInputWASDEmulateDirection(a){inputWASDEmulateDirection=a}function setTouchInputEnable(a){touchInputEnable=a}function setTouchGamepadEnable(a){touchGamepadEnable=a}function setTouchGamepadAnalog(a){touchGamepadAnalog=a}function setTouchGamepadSize(a){touchGamepadSize=a}function setTouchGamepadAlpha(a){touchGamepadAlpha=a}function setVibrateEnable(a){vibrateEnable=a}function setSoundEnable(a){soundEnable=a}function setSoundVolume(a){soundVolume=a;soundEnable&&!headlessMode&&audioGainNode&&(audioGainNode.gain.value=a)}function setSoundDefaultRange(a){soundDefaultRange=a}function setSoundDefaultTaper(a){soundDefaultTaper=a}function setMedalDisplayTime(a){medalDisplayTime=a}function setMedalDisplaySlideTime(a){medalDisplaySlideTime=a}function setMedalDisplaySize(a){medalDisplaySize=a}function setMedalDisplayIconSize(a){medalDisplayIconSize=a}function setMedalsPreventUnlock(a){medalsPreventUnlock=a}function setShowWatermark(a){showWatermark=a}function setDebugKey(a){debugKey=a}class EngineObject{constructor(a=vec2(),b=vec2(1),c,d=0,e=new Color,f=0){ASSERT(isVector2(a)&&isVector2(b),"ensure pos and size are vec2s");ASSERT("number"!==typeof c||!c,"old style tile setup");this.pos=a.copy();this.size=b;this.drawSize=void 0;this.tileInfo=c;this.angle=d;this.color=e;this.additiveColor=void 0;this.mirror=!1;this.mass=objectDefaultMass;this.damping=objectDefaultDamping;this.angleDamping=objectDefaultAngleDamping;this.elasticity=objectDefaultElasticity;this.friction=objectDefaultFriction;this.gravityScale=1;this.renderOrder=f;this.velocity=vec2();this.angleVelocity=0;this.spawnTime=time;this.children=[];this.clampSpeedLinear=!0;this.parent=void 0;this.localPos=vec2();this.localAngle=0;this.collideRaycast=this.isSolid=this.collideSolidObjects=this.collideTiles=!1;engineObjects.push(this)}updateTransforms(){const a=this.parent;if(a){const b=a.getMirrorSign();this.pos=this.localPos.multiply(vec2(b,1)).rotate(-a.angle).add(a.pos);this.angle=b*this.localAngle+a.angle}for(const b of this.children)b.updateTransforms()}update(){if(!this.parent){if(this.clampSpeedLinear)this.velocity.x=clamp(this.velocity.x,-objectMaxSpeed,objectMaxSpeed),this.velocity.y=clamp(this.velocity.y,-objectMaxSpeed,objectMaxSpeed);else{var a=this.velocity.lengthSquared();a>objectMaxSpeed*objectMaxSpeed&&(a=objectMaxSpeed/a**.5,this.velocity.x*=a,this.velocity.y*=a)}a=this.pos.copy();this.velocity.x*=this.damping;this.velocity.y*=this.damping;this.mass&&(this.velocity.y+=gravity*this.gravityScale);this.pos.x+=this.velocity.x;this.pos.y+=this.velocity.y;this.angle+=this.angleVelocity*=this.angleDamping;ASSERT(0<=this.angleDamping&&1>=this.angleDamping);ASSERT(0<=this.damping&&1>=this.damping);if(enablePhysicsSolver&&this.mass){var b=0>this.velocity.y;if(this.groundObject){var c=this.groundObject.velocity?this.groundObject.velocity.x:0;this.velocity.x=c+(this.velocity.x-c)*this.friction;this.groundObject=0}if(this.collideSolidObjects)for(var d of engineObjectsCollide){if(!this.isSolid&&!d.isSolid||d.destroyed||d.parent||d==this)continue;if(!isOverlapping(this.pos,this.size,d.pos,d.size))continue;c=this.collideWithObject(d);var e=d.collideWithObject(this);if(!c||!e)continue;if(isOverlapping(a,this.size,d.pos,d.size)){c=a.subtract(d.pos);e=c.length();c=.01>e?randVector(.001):c.scale(.001/e);this.velocity=this.velocity.add(c);d.mass&&(d.velocity=d.velocity.subtract(c));debugOverlay&&debugPhysics&&debugOverlap(this.pos,this.size,d.pos,d.size,"#f00");continue}e=this.size.add(d.size);var f=2*(a.y-d.pos.y)>e.y+gravity;const k=2*abs(a.y-d.pos.y)<e.y;var g=2*abs(a.x-d.pos.x)<e.x;c=max(this.elasticity,d.elasticity);if(f||g||!k)if(this.pos.y=d.pos.y+(e.y/2+.001)*sign(a.y-d.pos.y),d.groundObject&&b||!d.mass)b&&(this.groundObject=d),this.velocity.y*=-c;else if(d.mass){g=(this.mass*this.velocity.y+d.mass*d.velocity.y)/(this.mass+d.mass);const h=d.velocity.y*(d.mass-this.mass)/(this.mass+d.mass)+2*this.velocity.y*this.mass/(this.mass+d.mass);this.velocity.y=lerp(c,g,this.velocity.y*(this.mass-d.mass)/(this.mass+d.mass)+2*d.velocity.y*d.mass/(this.mass+d.mass));d.velocity.y=lerp(c,g,h)}!f&&k&&(this.pos.x=d.pos.x+(e.x/2+.001)*sign(a.x-d.pos.x),d.mass?(e=(this.mass*this.velocity.x+d.mass*d.velocity.x)/(this.mass+d.mass),f=d.velocity.x*(d.mass-this.mass)/(this.mass+d.mass)+2*this.velocity.x*this.mass/(this.mass+d.mass),this.velocity.x=lerp(c,e,this.velocity.x*(this.mass-d.mass)/(this.mass+d.mass)+2*d.velocity.x*d.mass/(this.mass+d.mass)),d.velocity.x=lerp(c,e,f)):this.velocity.x*=-c);debugOverlay&&debugPhysics&&debugOverlap(this.pos,this.size,d.pos,d.size,"#f0f")}if(this.collideTiles&&tileCollisionTest(this.pos,this.size,this)&&!tileCollisionTest(a,this.size,this)){d=tileCollisionTest(vec2(a.x,this.pos.y),this.size,this);c=tileCollisionTest(vec2(this.pos.x,a.y),this.size,this);if(d||!c)this.velocity.y*=-this.elasticity,(this.groundObject=b)?this.pos.y=(a.y-this.size.y/2|0)+this.size.y/2+1e-4:this.pos.y=a.y;c&&(this.pos.x=a.x,this.velocity.x*=-this.elasticity);debugOverlay&&debugPhysics&&debugRect(this.pos,this.size,"#f00")}}}}render(){drawTile(this.pos,this.drawSize||this.size,this.tileInfo,this.color,this.angle,this.mirror,this.additiveColor)}destroy(){if(!this.destroyed){this.destroyed=1;this.parent&&this.parent.removeChild(this);for(const a of this.children)a.destroy(a.parent=0)}}localToWorld(a){return this.pos.add(a.rotate(this.angle))}worldToLocal(a){return a.subtract(this.pos).rotate(-this.angle)}localToWorldVector(a){return a.rotate(-this.angle)}worldToLocalVector(a){return a.rotate(this.angle)}collideWithTile(a,b){return 0<a}collideWithObject(a){return!0}getAliveTime(){return time-this.spawnTime}applyAcceleration(a){this.mass&&(this.velocity=this.velocity.add(a))}applyForce(a){this.applyAcceleration(a.scale(1/this.mass))}getMirrorSign(){return this.mirror?-1:1}addChild(a,b=vec2(),c=0){ASSERT(!a.parent&&!this.children.includes(a));this.children.push(a);a.parent=this;a.localPos=b.copy();a.localAngle=c}removeChild(a){ASSERT(a.parent==this&&this.children.includes(a));this.children.splice(this.children.indexOf(a),1);a.parent=0}setCollision(a=!0,b=!0,c=!0,d=!0){ASSERT(a||!b,"solid objects must be set to collide");this.collideSolidObjects=a;this.isSolid=b;this.collideTiles=c;this.collideRaycast=d}toString(){if(debug){let a="type = "+this.constructor.name;if(this.pos.x||this.pos.y)a+="\npos = "+this.pos;if(this.velocity.x||this.velocity.y)a+="\nvelocity = "+this.velocity;if(this.size.x||this.size.y)a+="\nsize = "+this.size;this.angle&&(a+="\nangle = "+this.angle.toFixed(3));this.color&&(a+="\ncolor = "+this.color);return a}}renderDebugInfo(){if(debug){const a=vec2(max(this.size.x,.2),max(this.size.y,.2)),b=rgb(this.collideTiles?1:0,this.collideSolidObjects?1:0,this.isSolid?1:0,this.parent?.2:.5),c=this.parent?rgb(1,1,1,.5):rgb(0,0,0,.8);drawRect(this.pos,a,b,this.angle,!1);drawRect(this.pos,a.scale(.8),c,this.angle,!1);this.parent&&drawLine(this.pos,this.parent.pos,.1,rgb(0,0,1,.5),!1)}}}let mainCanvas,mainContext,overlayCanvas,overlayContext,mainCanvasSize=vec2(),textureInfos=[],drawCount;function tile(a=vec2(),b=tileSizeDefault,c=0,d=0){if(headlessMode)return new TileInfo;"number"===typeof b&&(ASSERT(0<b),b=vec2(b));var e=textureInfos[c];ASSERT(!!e,"Texture not loaded");const f=b.add(vec2(2*d));"number"===typeof a&&(e=e.size.x/f.x|0,a=0<e?vec2(a%e,a/e|0):vec2());a=vec2(a.x*f.x+d,a.y*f.y+d);return new TileInfo(a,b,c,d)}class TileInfo{constructor(a=vec2(),b=tileSizeDefault,c=0,d=0){this.pos=a.copy();this.size=b.copy();this.textureIndex=c;this.padding=d}offset(a){return new TileInfo(this.pos.add(a),this.size,this.textureIndex)}frame(a){ASSERT("number"==typeof a);return this.offset(vec2(a*(this.size.x+2*this.padding),0))}getTextureInfo(){return textureInfos[this.textureIndex]}}class TextureInfo{constructor(a){this.image=a;this.size=vec2(a.width,a.height);this.glTexture=glEnable&&glCreateTexture(a)}}function screenToWorld(a){return new Vector2((a.x-mainCanvasSize.x/2+.5)/cameraScale+cameraPos.x,(a.y-mainCanvasSize.y/2+.5)/-cameraScale+cameraPos.y)}function worldToScreen(a){return new Vector2((a.x-cameraPos.x)*cameraScale+mainCanvasSize.x/2-.5,(a.y-cameraPos.y)*-cameraScale+mainCanvasSize.y/2-.5)}function getCameraSize(){return mainCanvasSize.scale(1/cameraScale)}function drawTile(a,b=vec2(1),c,d=new Color,e=0,f,g=new Color(0,0,0,0),k=glEnable,h,m){ASSERT(!m||!k,"context only supported in canvas 2D mode");ASSERT("number"!==typeof c||!c,"this is an old style calls, to fix replace it with tile(tileIndex, tileSize)");const n=c&&c.getTextureInfo();if(k)if(h&&(a=screenToWorld(a),b=b.scale(1/cameraScale)),n){var l=vec2(1).divide(n.size);k=c.pos.x*l.x;h=c.pos.y*l.y;m=c.size.x*l.x;const p=c.size.y*l.y;l=l.scale(tileFixBleedScale);glSetTexture(n.glTexture);glDraw(a.x,a.y,f?-b.x:b.x,b.y,e,k+l.x,h+l.y,k-l.x+m,h-l.y+p,d.rgbaInt(),g.rgbaInt())}else glDraw(a.x,a.y,b.x,b.y,e,0,0,0,0,0,d.rgbaInt());else showWatermark&&++drawCount,b=vec2(b.x,-b.y),drawCanvas2D(a,b,e,f,p=>{if(n){const q=c.pos.x+tileFixBleedScale,r=c.pos.y+tileFixBleedScale,x=c.size.x-2*tileFixBleedScale,v=c.size.y-2*tileFixBleedScale;p.globalAlpha=d.a;p.drawImage(n.image,q,r,x,v,-.5,-.5,1,1);p.globalAlpha=1}else p.fillStyle=d,p.fillRect(-.5,-.5,1,1)},h,m)}function drawRect(a,b,c,d,e,f,g){drawTile(a,b,void 0,c,d,!1,void 0,e,f,g)}function drawLine(a,b,c=.1,d,e,f,g){b=vec2((b.x-a.x)/2,(b.y-a.y)/2);c=vec2(c,2*b.length());drawRect(a.add(b),c,d,b.angle(),e,f,g)}function drawPoly(a,b=new Color,c=0,d=new Color(0,0,0),e,f=mainContext){f.fillStyle=b.toString();f.beginPath();for(const g of e?a:a.map(worldToScreen))f.lineTo(g.x,g.y);f.closePath();f.fill();c&&(f.strokeStyle=d.toString(),f.lineWidth=e?c:c*cameraScale,f.stroke())}function drawEllipse(a,b=1,c=1,d=0,e=new Color,f=0,g=new Color(0,0,0),k,h=mainContext){k||(a=worldToScreen(a),b*=cameraScale,c*=cameraScale,f*=cameraScale);h.fillStyle=e.toString();h.beginPath();h.ellipse(a.x,a.y,b,c,d,0,9);h.fill();f&&(h.strokeStyle=g.toString(),h.lineWidth=f,h.stroke())}function drawCircle(a,b=1,c=new Color,d=0,e=new Color(0,0,0),f,g=mainContext){drawEllipse(a,b,b,0,c,d,e,f,g)}function drawCanvas2D(a,b,c,d,e,f,g=mainContext){f||(a=worldToScreen(a),b=b.scale(cameraScale));g.save();g.translate(a.x+.5,a.y+.5);g.rotate(c);g.scale(d?-b.x:b.x,-b.y);e(g);g.restore()}function drawText(a,b,c=1,d,e=0,f,g,k,h,m=mainContext){drawTextScreen(a,worldToScreen(b),c*cameraScale,d,e*cameraScale,f,g,k,h,m)}function drawTextOverlay(a,b,c=1,d,e=0,f,g,k,h){drawText(a,b,c,d,e,f,g,k,h,overlayContext)}function drawTextScreen(a,b,c=1,d=new Color,e=0,f=new Color(0,0,0),g="center",k=fontDefault,h,m=overlayContext){m.fillStyle=d.toString();m.lineWidth=e;m.strokeStyle=f.toString();m.textAlign=g;m.font=c+"px "+k;m.textBaseline="middle";m.lineJoin="round";b=b.copy();a=(a+"").split("\n");b.y-=(a.length-1)*c/2;a.forEach(n=>{e&&m.strokeText(n,b.x,b.y,h);m.fillText(n,b.x,b.y,h);b.y+=c})}function setBlendMode(a,b=glEnable,c){ASSERT(!c||!b,"context only supported in canvas 2D mode");b?glAdditive=a:(c||=mainContext,c.globalCompositeOperation=a?"lighter":"source-over")}function combineCanvases(){glCopyToContext(mainContext,!0);mainContext.drawImage(overlayCanvas,0,0);glClearCanvas();overlayCanvas.width|=0}let engineFontImage;class FontImage{constructor(a,b=vec2(8),c=vec2(0,1),d=overlayContext){engineFontImage||((engineFontImage=new Image).src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQAAAAAYAQAAAAA9+x6JAAAAAnRSTlMAAHaTzTgAAAGiSURBVHjaZZABhxxBEIUf6ECLBdFY+Q0PMNgf0yCgsSAGZcT9sgIPtBWwIA5wgAPEoHUyJeeSlW+gjK+fegWwtROWpVQEyWh2npdpBmTUFVhb29RINgLIukoXr5LIAvYQ5ve+1FqWEMqNKTX3FAJHyQDRZvmKWubAACcv5z5Gtg2oyCWE+Yk/8JZQX1jTTCpKAFGIgza+dJCNBF2UskRlsgwitHbSV0QLgt9sTPtsRlvJjEr8C/FARWA2bJ/TtJ7lko34dNDn6usJUMzuErP89UUBJbWeozrwLLncXczd508deAjLWipLO4Q5XGPcJvPu92cNDaN0P5G1FL0nSOzddZOrJ6rNhbXGmeDvO3TF7DeJWl4bvaYQTNHCTeuqKZmbjHaSOFes+IX/+IhHrnAkXOAsfn24EM68XieIECoccD4KZLk/odiwzeo2rovYdhvb2HYFgyznJyDpYJdYOmfXgVdJTaUi4xA2uWYNYec9BLeqdl9EsoTw582mSFDX2DxVLbNt9U3YYoeatBad1c2Tj8t2akrjaIGJNywKB/7h75/gN3vCMSaadIUTAAAAAElFTkSuQmCC");this.image=a||engineFontImage;this.tileSize=b;this.paddingSize=c;this.context=d}drawText(a,b,c=1,d){this.drawTextScreen(a,worldToScreen(b).floor(),c*cameraScale|0,d)}drawTextScreen(a,b,c=4,d){const e=this.context;e.save();const f=this.tileSize,g=f.add(this.paddingSize).scale(c),k=this.image.width/this.tileSize.x|0;(a+"").split("\n").forEach((h,m)=>{const n=d?h.length*f.x*c/2|0:0;for(let q=h.length;q--;){var l=h[q].charCodeAt(0);if(32>l||127<l)l=127;var p=l-32;l=p%k;p=p/k|0;const r=b.add(vec2(q,m).multiply(g));e.drawImage(this.image,l*f.x,p*f.y,f.x,f.y,r.x-n,r.y,f.x*c,f.y*c)}});e.restore()}}function isFullscreen(){return!!document.fullscreenElement}function toggleFullscreen(){const a=mainCanvas.parentElement;isFullscreen()?document.exitFullscreen&&document.exitFullscreen():a.requestFullscreen&&a.requestFullscreen()}function setCursor(a="auto"){mainCanvas.parentElement.style.cursor=a}function keyIsDown(a,b=0){ASSERT(0<b||"number"!==typeof a||3>a,"use code string for keyboard");return inputData[b]&&!!(inputData[b][a]&1)}function keyWasPressed(a,b=0){ASSERT(0<b||"number"!==typeof a||3>a,"use code string for keyboard");return inputData[b]&&!!(inputData[b][a]&2)}function keyWasReleased(a,b=0){ASSERT(0<b||"number"!==typeof a||3>a,"use code string for keyboard");return inputData[b]&&!!(inputData[b][a]&4)}function clearInput(){inputData=[[]];touchGamepadButtons=[]}const mouseIsDown=keyIsDown,mouseWasPressed=keyWasPressed,mouseWasReleased=keyWasReleased;let mousePos=vec2(),mousePosScreen=vec2(),mouseWheel=0,isUsingGamepad=!1,preventDefaultInput=!1;function gamepadIsDown(a,b=0){return keyIsDown(a,b+1)}function gamepadWasPressed(a,b=0){return keyWasPressed(a,b+1)}function gamepadWasReleased(a,b=0){return keyWasReleased(a,b+1)}function gamepadStick(a,b=0){return gamepadStickData[b]?gamepadStickData[b][a]||vec2():vec2()}let inputData=[[]];function inputUpdate(){headlessMode||(touchInputEnable&&isTouchDevice||document.hasFocus()||clearInput(),mousePos=screenToWorld(mousePosScreen),gamepadsUpdate())}function inputUpdatePost(){if(!headlessMode){for(const a of inputData)for(const b in a)a[b]&=1;mouseWheel=0}}function inputInit(){function a(b){return inputWASDEmulateDirection?"KeyW"==b?"ArrowUp":"KeyS"==b?"ArrowDown":"KeyA"==b?"ArrowLeft":"KeyD"==b?"ArrowRight":b:b}headlessMode||(onkeydown=b=>{b.repeat||(isUsingGamepad=!1,inputData[0][b.code]=3,inputWASDEmulateDirection&&(inputData[0][a(b.code)]=3));preventDefaultInput&&b.preventDefault()},onkeyup=b=>{inputData[0][b.code]=4;inputWASDEmulateDirection&&(inputData[0][a(b.code)]=4)},onmousedown=b=>{soundEnable&&!headlessMode&&audioContext&&"running"!=audioContext.state&&audioContext.resume();isUsingGamepad=!1;inputData[0][b.button]=3;mousePosScreen=mouseToScreen(b);b.button&&b.preventDefault()},onmouseup=b=>inputData[0][b.button]=inputData[0][b.button]&2|4,onmousemove=b=>mousePosScreen=mouseToScreen(b),onwheel=b=>mouseWheel=b.ctrlKey?0:sign(b.deltaY),oncontextmenu=b=>!1,onblur=b=>clearInput(),isTouchDevice&&touchInputEnable&&touchInputInit())}function mouseToScreen(a){if(!mainCanvas||headlessMode)return vec2();const b=mainCanvas.getBoundingClientRect();return vec2(mainCanvas.width,mainCanvas.height).multiply(vec2(percent(a.x,b.left,b.right),percent(a.y,b.top,b.bottom)))}const gamepadStickData=[];function gamepadsUpdate(){const a=g=>{const k=h=>.3<h?percent(h,.3,.8):-.3>h?-percent(-h,.3,.8):0;return vec2(k(g.x),k(-g.y)).clampLength()};if(touchGamepadEnable&&isTouchDevice&&(ASSERT(touchGamepadButtons,"set touchGamepadEnable before calling init!"),touchGamepadTimer.isSet())){var b=gamepadStickData[0]||(gamepadStickData[0]=[]);b[0]=vec2();touchGamepadAnalog?b[0]=a(touchGamepadStick):.3<touchGamepadStick.lengthSquared()&&(b[0].x=Math.round(touchGamepadStick.x),b[0].y=-Math.round(touchGamepadStick.y),b[0]=b[0].clampLength());b=inputData[1]||(inputData[1]=[]);for(var c=10;c--;){var d=3==c?2:2==c?3:c,e=gamepadIsDown(d,0);b[d]=touchGamepadButtons[c]?e?1:3:e?4:0}}if(gamepadsEnable&&navigator&&navigator.getGamepads&&(debug||document.hasFocus()))for(b=navigator.getGamepads(),c=b.length;c--;){e=b[c];const g=inputData[c+1]||(inputData[c+1]=[]);d=gamepadStickData[c]||(gamepadStickData[c]=[]);if(e){for(var f=0;f<e.axes.length-1;f+=2)d[f>>1]=a(vec2(e.axes[f],e.axes[f+1]));for(f=e.buttons.length;f--;){const k=e.buttons[f],h=gamepadIsDown(f,c);g[f]=k.pressed?h?1:3:h?4:0;isUsingGamepad||=!c&&k.pressed}gamepadDirectionEmulateStick&&(e=vec2((gamepadIsDown(15,c)&&1)-(gamepadIsDown(14,c)&&1),(gamepadIsDown(12,c)&&1)-(gamepadIsDown(13,c)&&1)),e.lengthSquared()&&(d[0]=e.clampLength()));touchGamepadEnable&&isUsingGamepad&&touchGamepadTimer.unset()}}}function vibrate(a=100){vibrateEnable&&!headlessMode&&navigator&&navigator.vibrate&&navigator.vibrate(a)}function vibrateStop(){vibrate(0)}const isTouchDevice=!headlessMode&&void 0!==window.ontouchstart;let touchGamepadTimer=new Timer,touchGamepadButtons,touchGamepadStick;function touchInputInit(){function a(e){soundEnable&&!headlessMode&&audioContext&&"running"!=audioContext.state&&audioContext.resume();const f=e.touches.length;if(f){const g=vec2(e.touches[0].clientX,e.touches[0].clientY);mousePosScreen=mouseToScreen(g);d?isUsingGamepad=touchGamepadEnable:inputData[0][0]=3}else d&&(inputData[0][0]=inputData[0][0]&2|4);d=f;document.hasFocus()&&e.preventDefault();return!0}function b(e){touchGamepadStick=vec2();touchGamepadButtons=[];isUsingGamepad=!0;if(e.touches.length&&(touchGamepadTimer.set(),paused&&!d)){touchGamepadButtons[9]=1;a(e);return}const f=vec2(touchGamepadSize,mainCanvasSize.y-touchGamepadSize),g=mainCanvasSize.subtract(vec2(touchGamepadSize,touchGamepadSize)),k=mainCanvasSize.scale(.5);for(const m of e.touches){var h=mouseToScreen(vec2(m.clientX,m.clientY));h.distance(f)<touchGamepadSize?touchGamepadStick=h.subtract(f).scale(2/touchGamepadSize).clampLength():h.distance(g)<touchGamepadSize?(h=h.subtract(g).direction(),touchGamepadButtons[h]=1):h.distance(k)<touchGamepadSize&&!d&&(touchGamepadButtons[9]=1)}a(e);return!0}let c=a;touchGamepadEnable&&(c=b,touchGamepadButtons=[],touchGamepadStick=vec2());document.addEventListener("touchstart",e=>c(e),{passive:!1});document.addEventListener("touchmove",e=>c(e),{passive:!1});document.addEventListener("touchend",e=>c(e),{passive:!1});onmousedown=onmouseup=()=>0;let d}function touchGamepadRender(){if(touchInputEnable&&isTouchDevice&&!headlessMode&&touchGamepadEnable&&touchGamepadTimer.isSet()){var a=percent(touchGamepadTimer.get(),4,3);if(a&&!paused){var b=overlayContext;b.save();b.globalAlpha=a*touchGamepadAlpha;b.strokeStyle="#fff";b.lineWidth=3;b.fillStyle=0<touchGamepadStick.lengthSquared()?"#fff":"#000";b.beginPath();a=vec2(touchGamepadSize,mainCanvasSize.y-touchGamepadSize);if(touchGamepadAnalog)b.arc(a.x,a.y,touchGamepadSize/2,0,9),b.fill();else for(var c=10;c--;){var d=c*PI/4;b.arc(a.x,a.y,.6*touchGamepadSize,d+PI/8,d+PI/8);c%2&&b.arc(a.x,a.y,.33*touchGamepadSize,d,d);1==c&&b.fill()}b.stroke();a=vec2(mainCanvasSize.x-touchGamepadSize,mainCanvasSize.y-touchGamepadSize);for(c=4;c--;)d=a.add(vec2().setDirection(c,touchGamepadSize/2)),b.fillStyle=touchGamepadButtons[c]?"#fff":"#000",b.beginPath(),b.arc(d.x,d.y,touchGamepadSize/4,0,9),b.fill(),b.stroke();b.restore()}}}let audioContext=new AudioContext,audioGainNode;function audioInit(){soundEnable&&!headlessMode&&(audioGainNode=audioContext.createGain(),audioGainNode.connect(audioContext.destination),audioGainNode.gain.value=soundVolume)}class Sound{constructor(a,b=soundDefaultRange,c=soundDefaultTaper){soundEnable&&!headlessMode&&(this.range=b,this.taper=c,this.randomness=0,a&&(this.randomness=void 0!=a[1]?a[1]:.05,a[1]=0,this.sampleChannels=[zzfxG(...a)],this.sampleRate=zzfxR))}play(a,b=1,c=1,d=1,e=!1){if(soundEnable&&!headlessMode&&this.sampleChannels){var f;if(a){if(f=this.range){const g=cameraPos.distanceSquared(a);if(g>f*f)return;b*=percent(g**.5,f,f*this.taper)}f=2*worldToScreen(a).x/mainCanvas.width-1}a=c+c*this.randomness*d*rand(-1,1);this.gainNode=audioContext.createGain();return this.source=playSamples(this.sampleChannels,b,a,f,e,this.sampleRate,this.gainNode)}}setVolume(a=1){this.gainNode&&(this.gainNode.gain.value=a)}stop(){this.source&&this.source.stop();this.source=void 0}getSource(){return this.source}playNote(a,b,c){return this.play(b,c,2**(a/12),0)}getDuration(){return this.sampleChannels&&this.sampleChannels[0].length/this.sampleRate}isLoading(){return!this.sampleChannels}}class SoundWave extends Sound{constructor(a,b=0,c,d,e){super(void 0,c,d);soundEnable&&!headlessMode&&(this.randomness=b,fetch(a).then(f=>f.arrayBuffer()).then(f=>audioContext.decodeAudioData(f)).then(f=>{this.sampleChannels=[];for(let g=f.numberOfChannels;g--;)this.sampleChannels[g]=Array.from(f.getChannelData(g));this.sampleRate=f.sampleRate}).then(()=>e&&e(this)))}}function playAudioFile(a,b=1,c=!1){if(soundEnable&&!headlessMode)return new SoundWave(a,0,0,0,d=>d.play(void 0,b,1,1,c))}class Music extends Sound{constructor(a){super(void 0);soundEnable&&!headlessMode&&(this.randomness=0,this.sampleChannels=zzfxM(...a),this.sampleRate=zzfxR)}playMusic(a,b=!1){return super.play(void 0,a,1,1,b)}}function speak(a,b="",c=1,d=1,e=1){if(soundEnable&&!headlessMode&&speechSynthesis)return a=new SpeechSynthesisUtterance(a),a.lang=b,a.volume=2*c*soundVolume,a.rate=d,a.pitch=e,speechSynthesis.speak(a),a}function speakStop(){speechSynthesis&&speechSynthesis.cancel()}function getNoteFrequency(a,b=220){return b*2**(a/12)}function playSamples(a,b=1,c=1,d=0,e=!1,f=zzfxR,g){if(soundEnable&&!headlessMode){var k=audioContext.createBuffer(a.length,a[0].length,f),h=audioContext.createBufferSource();a.forEach((m,n)=>k.getChannelData(n).set(m));h.buffer=k;h.playbackRate.value=c;h.loop=e;g=g||audioContext.createGain();g.gain.value=b;g.connect(audioGainNode);a=new StereoPannerNode(audioContext,{pan:clamp(d,-1,1)});h.connect(a).connect(g);"running"!=audioContext.state?audioContext.resume().then(()=>h.start()):h.start();return h}}function zzfx(...a){return playSamples([zzfxG(...a)])}const zzfxR=44100;function zzfxG(a=1,b=.05,c=220,d=0,e=0,f=.1,g=0,k=1,h=0,m=0,n=0,l=0,p=0,q=0,r=0,x=0,v=0,D=1,z=0,E=0,A=0){let w=2*PI;var t=zzfxR;let F=h*=500*w/t/t;b=c*=rand(1+b,1-b)*w/t;let C=[],y=0,G=0,u=0,H=1,R=0,S=0,B=0,J;var L=w*abs(A)*2/t,K=Math.cos(L),M=Math.sin(L)/2/2,I=1+M;L=-2*K/I;M=(1-M)/I;let N=(1+sign(A)*K)/2/I;K=-(sign(A)+K)/I;let O=I=0,P=0,Q=0;d=d*t+9;z*=t;e*=t;f*=t;v*=t;m*=500*w/t**3;r*=w/t;n*=w/t;l*=t;p=p*t|0;for(J=d+z+e+f+v|0;u<J;C[u++]=B*a)++S%(100*x|0)||(B=g?1<g?2<g?3<g?Math.sin(y**3):clamp(Math.tan(y),1,-1):1-(2*y/w%2+2)%2:1-4*abs(Math.round(y/w)-y/w):Math.sin(y),B=(p?1-E+E*Math.sin(w*u/p):1)*sign(B)*abs(B)**k*(u<d?u/d:u<d+z?1-(u-d)/z*(1-D):u<d+z+e?D:u<J-v?(J-u-v)/f*D:0),B=v?B/2+(v>u?0:(u<J-v?1:(J-u)/v)*C[u-v|0]/2/a):B,A&&(B=Q=N*I+K*(I=O)+N*(O=B)-M*P-L*(P=Q))),t=(c+=h+=m)*Math.cos(r*G++),y+=t+t*q*Math.sin(u**5),H&&++H>l&&(c+=n,b+=n,H=0),!p||++R%p||(c=b,h=F,H=H||1);return C}function zzfxM(a,b,c,d=125){let e,f,g,k,h,m,n,l,p,q,r,x,v,D=0,z,E=[],A=[],w=[],t=0,F=0,C=1,y={},G=zzfxR/d*60>>2;for(;C;t++)E=[C=l=x=0],c.forEach((u,H)=>{n=b[u][t]||[0,0,0];C|=b[u][t]&&1;z=x+(b[u][0].length-2-(l?0:1))*G;v=H==c.length-1;e=2;for(g=x;e<n.length+v;l=++e){h=n[e];p=e==n.length+v-1&&v||q!=(n[0]||0)||h|0;for(f=0;f<G&&l;f++>G-99&&p&&1>r?r+=1/99:0)m=(1-r)*E[D++]/2||0,A[g]=(A[g]||0)-m*F+m,w[g]=(w[g++]||0)+m*F+m;h&&(r=h%1,F=n[1]||0,h|=0)&&(E=y[[q=n[D=0]||0,h]]=y[[q,h]]||(k=[...a[q]],k[2]*=2**((h-12)/12),0<h?zzfxG(...k):[]))}x=z});return[A,w]}let tileCollision=[],tileCollisionSize=vec2();function initTileCollision(a){tileCollisionSize=a;tileCollision=[];for(a=tileCollision.length=tileCollisionSize.area();a--;)tileCollision[a]=0}function setTileCollisionData(a,b=0){a.arrayCheck(tileCollisionSize)&&(tileCollision[(a.y|0)*tileCollisionSize.x+a.x|0]=b)}function getTileCollisionData(a){return a.arrayCheck(tileCollisionSize)?tileCollision[(a.y|0)*tileCollisionSize.x+a.x|0]:0}function tileCollisionTest(a,b=vec2(),c){const d=max(a.x-b.x/2|0,0);var e=max(a.y-b.y/2|0,0);const f=min(a.x+b.x/2,tileCollisionSize.x);for(a=min(a.y+b.y/2,tileCollisionSize.y);e<a;++e)for(b=d;b<f;++b){const g=tileCollision[e*tileCollisionSize.x+b];if(g&&(!c||c.collideWithTile(g,vec2(b,e))))return!0}return!1}function tileCollisionRaycast(a,b,c){const d=b.subtract(a),e=d.length();var f=d.normalize();f=vec2(abs(1/f.x),abs(1/f.y));let g=a.floor(),k=f.x*(0>d.x?a.x-g.x:g.x-a.x+1),h=f.y*(0>d.y?a.y-g.y:g.y-a.y+1);for(;;){const m=getTileCollisionData(g);if(m&&(!c||c.collideWithTile(m,g)))return debugRaycast&&debugLine(a,b,"#f00",.02),debugRaycast&&debugPoint(g.add(vec2(.5)),"#ff0"),g.add(vec2(.5));if(k>e&&h>e)break;k>h?(g.y+=sign(d.y),h+=f.y):(g.x+=sign(d.x),k+=f.x)}debugRaycast&&debugLine(a,b,"#00f",.02)}class TileLayerData{constructor(a,b=0,c=!1,d=new Color){this.tile=a;this.direction=b;this.mirror=c;this.color=d}clear(){this.tile=this.direction=0;this.mirror=!1;this.color=new Color}}class TileLayer extends EngineObject{constructor(a,b=tileCollisionSize,c=tile(),d=vec2(1),e=0){super(a,b,c,0,void 0,e);this.canvas=document.createElement("canvas");this.context=this.canvas.getContext("2d");this.scale=d;this.isOverlay=!1;this.data=[];for(a=this.size.area();a--;)this.data.push(new TileLayerData);headlessMode&&(this.redraw=()=>{},this.render=()=>{},this.redrawStart=()=>{},this.redrawEnd=()=>{},this.drawTileData=()=>{},this.drawCanvas2D=()=>{})}setData(a,b,c=!1){a.arrayCheck(this.size)&&(this.data[(a.y|0)*this.size.x+a.x|0]=b,c&&this.drawTileData(a))}getData(a){return a.arrayCheck(this.size)&&this.data[(a.y|0)*this.size.x+a.x|0]}update(){}render(){ASSERT(mainContext!=this.context,"must call redrawEnd() after drawing tiles");glOverlay||this.isOverlay||glCopyToContext(mainContext);const a=worldToScreen(this.pos.add(vec2(0,this.size.y*this.scale.y)));a.x|=0;a.y|=0;(this.isOverlay?overlayContext:mainContext).drawImage(this.canvas,a.x,a.y,cameraScale*this.size.x*this.scale.x,cameraScale*this.size.y*this.scale.y)}redraw(){this.redrawStart(!0);for(let a=this.size.x;a--;)for(let b=this.size.y;b--;)this.drawTileData(vec2(a,b),!1);this.redrawEnd()}redrawStart(a=!1){this.savedRenderSettings=[mainCanvas,mainContext,mainCanvasSize,cameraPos,cameraScale];mainCanvas=this.canvas;mainContext=this.context;mainCanvasSize=this.size.multiply(this.tileInfo.size);cameraPos=this.size.scale(.5);cameraScale=this.tileInfo.size.x;a&&(mainCanvas.width=mainCanvasSize.x,mainCanvas.height=mainCanvasSize.y);this.context.imageSmoothingEnabled=!tilesPixelated;glPreRender()}redrawEnd(){ASSERT(mainContext==this.context,"must call redrawStart() before drawing tiles");glCopyToContext(mainContext,!0);[mainCanvas,mainContext,mainCanvasSize,cameraPos,cameraScale]=this.savedRenderSettings}drawTileData(a,b=!0){var c=this.tileInfo.size;b&&(b=a.multiply(c),this.context.clearRect(b.x,this.canvas.height-b.y,c.x,-c.y));b=this.getData(a);void 0!=b.tile&&(ASSERT(mainContext==this.context,"must call redrawStart() before drawing tiles"),a=a.add(vec2(.5)),c=tile(b.tile,c,this.tileInfo.textureIndex),drawTile(a,vec2(1),c,b.color,b.direction*PI/2,b.mirror))}drawCanvas2D(a,b,c,d,e){const f=this.context;f.save();a=a.subtract(this.pos).multiply(this.tileInfo.size);b=b.multiply(this.tileInfo.size);f.translate(a.x,this.canvas.height-a.y);f.rotate(c);f.scale(d?-b.x:b.x,b.y);e(f);f.restore()}drawTile(a,b=vec2(1),c,d=new Color,e,f){this.drawCanvas2D(a,b,e,f,g=>{const k=c&&c.getTextureInfo();k?(g.globalAlpha=d.a,g.drawImage(k.image,c.pos.x,c.pos.y,c.size.x,c.size.y,-.5,-.5,1,1),g.globalAlpha=1):(g.fillStyle=d,g.fillRect(-.5,-.5,1,1))})}drawRect(a,b,c,d){this.drawTile(a,b,void 0,c,d)}}class ParticleEmitter extends EngineObject{constructor(a,b,c=0,d=0,e=100,f=PI,g,k=new Color,h=new Color,m=new Color(1,1,1,0),n=new Color(1,1,1,0),l=.5,p=.1,q=1,r=.1,x=.05,v=1,D=1,z=0,E=PI,A=.1,w=.2,t=!1,F=!1,C=!0,y=F?1e9:0,G=!1){super(a,vec2(),g,b,void 0,y);this.emitSize=c;this.emitTime=d;this.emitRate=e;this.emitConeAngle=f;this.colorStartA=k;this.colorStartB=h;this.colorEndA=m;this.colorEndB=n;this.randomColorLinear=C;this.particleTime=l;this.sizeStart=p;this.sizeEnd=q;this.speed=r;this.angleSpeed=x;this.damping=v;this.angleDamping=D;this.gravityScale=z;this.particleConeAngle=E;this.fadeRate=A;this.randomness=w;this.collideTiles=t;this.additive=F;this.localSpace=G;this.trailScale=0;this.particleCreateCallback=this.particleDestroyCallback=void 0;this.emitTimeBuffer=0}update(){this.parent&&super.update();if(!this.emitTime||this.getAliveTime()<=this.emitTime){if(this.emitRate*particleEmitRateScale){const a=1/this.emitRate/particleEmitRateScale;for(this.emitTimeBuffer+=timeDelta;0<this.emitTimeBuffer;this.emitTimeBuffer-=a)this.emitParticle()}}else this.destroy();debugParticles&&debugRect(this.pos,vec2(this.emitSize),"#0f0",0,this.angle)}emitParticle(){var a="number"===typeof this.emitSize?randInCircle(this.emitSize/2):vec2(rand(-.5,.5),rand(-.5,.5)).multiply(this.emitSize).rotate(this.angle);let b=rand(this.particleConeAngle,-this.particleConeAngle);this.localSpace||(a=this.pos.add(a),b+=this.angle);const c=this.randomness;var d=l=>l+l*rand(c,-c);const e=d(this.particleTime),f=d(this.sizeStart),g=d(this.sizeEnd),k=d(this.speed);d=d(this.angleSpeed)*randSign();var h=rand(this.emitConeAngle,-this.emitConeAngle);const m=randColor(this.colorStartA,this.colorStartB,this.randomColorLinear),n=randColor(this.colorEndA,this.colorEndB,this.randomColorLinear);h=this.localSpace?h:this.angle+h;a=new Particle(a,this.tileInfo,b,m,n,e,f,g,this.fadeRate,this.additive,this.trailScale,this.localSpace&&this,this.particleDestroyCallback);a.velocity=vec2().setAngle(h,k);a.angleVelocity=d;a.fadeRate=this.fadeRate;a.damping=this.damping;a.angleDamping=this.angleDamping;a.elasticity=this.elasticity;a.friction=this.friction;a.gravityScale=this.gravityScale;a.collideTiles=this.collideTiles;a.renderOrder=this.renderOrder;a.mirror=!!randInt(2);this.particleCreateCallback&&this.particleCreateCallback(a);return a}render(){}}class Particle extends EngineObject{constructor(a,b,c,d,e,f,g,k,h,m,n,l,p){super(a,vec2(),b,c);this.colorStart=d;this.colorEndDelta=e.subtract(d);this.lifeTime=f;this.sizeStart=g;this.sizeEndDelta=k-g;this.fadeRate=h;this.additive=m;this.trailScale=n;this.localSpaceEmitter=l;this.destroyCallback=p;this.clampSpeedLinear=!1}render(){const a=0<this.lifeTime?min((time-this.spawnTime)/this.lifeTime,1):1,b=vec2(this.sizeStart+a*this.sizeEndDelta);var c=this.fadeRate/2;c=new Color(this.colorStart.r+a*this.colorEndDelta.r,this.colorStart.g+a*this.colorEndDelta.g,this.colorStart.b+a*this.colorEndDelta.b,(this.colorStart.a+a*this.colorEndDelta.a)*(a<c?a/c:a>1-c?(1-a)/c:1));this.additive&&setBlendMode(!0);let d=this.pos,e=this.angle;this.localSpaceEmitter&&(d=this.localSpaceEmitter.pos.add(d.rotate(-this.localSpaceEmitter.angle)),e+=this.localSpaceEmitter.angle);if(this.trailScale){var f=this.velocity;this.localSpaceEmitter&&(f=f.rotate(-this.localSpaceEmitter.angle));var g=f.length();g&&(f=f.scale(1/g),g*=this.trailScale,b.y=max(b.x,g),e=f.angle(),drawTile(d.add(f.multiply(vec2(0,-g/2))),b,this.tileInfo,c,e,this.mirror))}else drawTile(d,b,this.tileInfo,c,e,this.mirror);this.additive&&setBlendMode();debugParticles&&debugRect(d,b,"#f005",0,e);1==a&&(this.color=c,this.size=b,this.destroyCallback&&this.destroyCallback(this),this.destroyed=1)}}const medals={};let medalsDisplayQueue=[],medalsSaveName,medalsDisplayTimeLast;function medalsInit(a){medalsSaveName=a;debugMedals||medalsForEach(b=>b.unlocked=!!localStorage[b.storageKey()]);engineAddPlugin(void 0,function(){if(medalsDisplayQueue.length){var b=medalsDisplayQueue[0],c=timeReal-medalsDisplayTimeLast;if(medalsDisplayTimeLast)if(c>medalDisplayTime)medalsDisplayTimeLast=0,medalsDisplayQueue.shift();else{const d=medalDisplayTime-medalDisplaySlideTime;b.render(c<medalDisplaySlideTime?1-c/medalDisplaySlideTime:c>d?(c-d)/medalDisplaySlideTime:0)}else medalsDisplayTimeLast=timeReal}})}function medalsForEach(a){Object.values(medals).forEach(b=>a(b))}class Medal{constructor(a,b,c="",d="🏆",e){ASSERT(0<=a&&!medals[a]);this.id=a;this.name=b;this.description=c;this.icon=d;this.unlocked=!1;e&&((this.image=new Image).src=e);medals[a]=this}unlock(){medalsPreventUnlock||this.unlocked||(ASSERT(medalsSaveName,"save name must be set"),localStorage[this.storageKey()]=this.unlocked=!0,medalsDisplayQueue.push(this))}render(a=0){const b=overlayContext;var c=min(medalDisplaySize.x,mainCanvas.width);const d=overlayCanvas.width-c;a*=-medalDisplaySize.y;b.save();b.beginPath();b.fillStyle=new Color(.9,.9,.9).toString();b.strokeStyle=new Color(0,0,0).toString();b.lineWidth=3;b.rect(d,a,c,medalDisplaySize.y);b.fill();b.stroke();b.clip();this.renderIcon(vec2(d+15+medalDisplayIconSize/2,a+medalDisplaySize.y/2));c=vec2(d+medalDisplayIconSize+30,a+28);drawTextScreen(this.name,c,38,new Color(0,0,0),0,void 0,"left");c.y+=32;drawTextScreen(this.description,c,24,new Color(0,0,0),0,void 0,"left");b.restore()}renderIcon(a,b=medalDisplayIconSize){this.image?overlayContext.drawImage(this.image,a.x-b/2,a.y-b/2,b,b):drawTextScreen(this.icon,a,.7*b,new Color(0,0,0))}storageKey(){return medalsSaveName+"_"+this.id}}let glCanvas,glContext,glAntialias=!0,glShader,glActiveTexture,glArrayBuffer,glGeometryBuffer,glPositionData,glColorData,glInstanceCount,glAdditive,glBatchAdditive;const gl_MAX_INSTANCES=1e4,gl_INDICES_PER_INSTANCE=11,gl_INSTANCE_BYTE_STRIDE=4*gl_INDICES_PER_INSTANCE,gl_INSTANCE_BUFFER_SIZE=gl_MAX_INSTANCES*gl_INSTANCE_BYTE_STRIDE;function glInit(){if(glEnable&&!headlessMode){glCanvas=document.createElement("canvas");glContext=glCanvas.getContext("webgl2",{antialias:glAntialias});var a=mainCanvas.parentElement;glOverlay&&a.appendChild(glCanvas);glShader=glCreateProgram("#version 300 es\nprecision highp float;uniform mat4 m;in vec2 g;in vec4 p,u,c,a;in float r;out vec2 v;out vec4 d,e;void main(){vec2 s=(g-.5)*p.zw;gl_Position=m*vec4(p.xy+s*cos(r)-vec2(-s.y,s)*sin(r),1,1);v=mix(u.xw,u.zy,g);d=c;e=a;}","#version 300 es\nprecision highp float;uniform sampler2D s;in vec2 v;in vec4 d,e;out vec4 c;void main(){c=texture(s,v)*d+e;}");a=new ArrayBuffer(gl_INSTANCE_BUFFER_SIZE);glPositionData=new Float32Array(a);glColorData=new Uint32Array(a);glArrayBuffer=glContext.createBuffer();glGeometryBuffer=glContext.createBuffer();a=new Float32Array([glInstanceCount=0,0,1,0,0,1,1,1]);glContext.bindBuffer(glContext.ARRAY_BUFFER,glGeometryBuffer);glContext.bufferData(glContext.ARRAY_BUFFER,a,glContext.STATIC_DRAW)}}function glPreRender(){if(glEnable&&!headlessMode){glClearCanvas();glContext.useProgram(glShader);glContext.activeTexture(glContext.TEXTURE0);textureInfos[0]&&glContext.bindTexture(glContext.TEXTURE_2D,glActiveTexture=textureInfos[0].glTexture);var a=glAdditive=glBatchAdditive=0,b=(d,e,f,g)=>{d=glContext.getAttribLocation(glShader,d);const k=f&&gl_INSTANCE_BYTE_STRIDE,h=f&&1,m=1==f;glContext.enableVertexAttribArray(d);glContext.vertexAttribPointer(d,g,e,m,k,a);glContext.vertexAttribDivisor(d,h);a+=g*f};glContext.bindBuffer(glContext.ARRAY_BUFFER,glGeometryBuffer);b("g",glContext.FLOAT,0,2);glContext.bindBuffer(glContext.ARRAY_BUFFER,glArrayBuffer);glContext.bufferData(glContext.ARRAY_BUFFER,gl_INSTANCE_BUFFER_SIZE,glContext.DYNAMIC_DRAW);b("p",glContext.FLOAT,4,4);b("u",glContext.FLOAT,4,4);b("c",glContext.UNSIGNED_BYTE,1,4);b("a",glContext.UNSIGNED_BYTE,1,4);b("r",glContext.FLOAT,4,1);b=vec2(2*cameraScale).divide(mainCanvasSize);var c=vec2(-1).subtract(cameraPos.multiply(b));glContext.uniformMatrix4fv(glContext.getUniformLocation(glShader,"m"),!1,[b.x,0,0,0,0,b.y,0,0,1,1,1,1,c.x,c.y,0,0])}}function glClearCanvas(){glContext.viewport(0,0,glCanvas.width=mainCanvas.width,glCanvas.height=mainCanvas.height);glContext.clear(glContext.COLOR_BUFFER_BIT)}function glSetTexture(a){headlessMode||a==glActiveTexture||(glFlush(),glContext.bindTexture(glContext.TEXTURE_2D,glActiveTexture=a))}function glCompileShader(a,b){b=glContext.createShader(b);glContext.shaderSource(b,a);glContext.compileShader(b);if(debug&&!glContext.getShaderParameter(b,glContext.COMPILE_STATUS))throw glContext.getShaderInfoLog(b);return b}function glCreateProgram(a,b){const c=glContext.createProgram();glContext.attachShader(c,glCompileShader(a,glContext.VERTEX_SHADER));glContext.attachShader(c,glCompileShader(b,glContext.FRAGMENT_SHADER));glContext.linkProgram(c);if(debug&&!glContext.getProgramParameter(c,glContext.LINK_STATUS))throw glContext.getProgramInfoLog(c);return c}function glCreateTexture(a){const b=glContext.createTexture();glContext.bindTexture(glContext.TEXTURE_2D,b);a&&a.width?glContext.texImage2D(glContext.TEXTURE_2D,0,glContext.RGBA,glContext.RGBA,glContext.UNSIGNED_BYTE,a):(a=new Uint8Array([255,255,255,255]),glContext.texImage2D(glContext.TEXTURE_2D,0,glContext.RGBA,1,1,0,glContext.RGBA,glContext.UNSIGNED_BYTE,a));a=tilesPixelated?glContext.NEAREST:glContext.LINEAR;glContext.texParameteri(glContext.TEXTURE_2D,glContext.TEXTURE_MIN_FILTER,a);glContext.texParameteri(glContext.TEXTURE_2D,glContext.TEXTURE_MAG_FILTER,a);return b}function glFlush(){if(glInstanceCount){var a=glBatchAdditive?glContext.ONE:glContext.ONE_MINUS_SRC_ALPHA;glContext.blendFuncSeparate(glContext.SRC_ALPHA,a,glContext.ONE,a);glContext.enable(glContext.BLEND);glContext.bufferSubData(glContext.ARRAY_BUFFER,0,glPositionData);glContext.drawArraysInstanced(glContext.TRIANGLE_STRIP,0,4,glInstanceCount);showWatermark&&(drawCount+=glInstanceCount);glInstanceCount=0;glBatchAdditive=glAdditive}}function glCopyToContext(a,b=!1){glEnable&&(glInstanceCount||b)&&(glFlush(),glOverlay&&!b||a.drawImage(glCanvas,0,0))}function glSetAntialias(a=!0){ASSERT(!glCanvas,"must be called before engineInit");glAntialias=a}function glDraw(a,b,c,d,e,f,g,k,h,m,n=0){ASSERT("number"==typeof m&&"number"==typeof n,"invalid color");(glInstanceCount>=gl_MAX_INSTANCES||glBatchAdditive!=glAdditive)&&glFlush();let l=glInstanceCount++*gl_INDICES_PER_INSTANCE;glPositionData[l++]=a;glPositionData[l++]=b;glPositionData[l++]=c;glPositionData[l++]=d;glPositionData[l++]=f;glPositionData[l++]=g;glPositionData[l++]=k;glPositionData[l++]=h;glColorData[l++]=m;glColorData[l++]=n;glPositionData[l++]=e}const engineName="LittleJS",engineVersion="1.11.6",frameRate=60,timeDelta=1/frameRate;let engineObjects=[],engineObjectsCollide=[],frame=0,time=0,timeReal=0,paused=!1;function setPaused(a){paused=a}let frameTimeLastMS=0,frameTimeBufferMS=0,averageFPS=0;const pluginUpdateList=[],pluginRenderList=[];function engineAddPlugin(a,b){ASSERT(!pluginUpdateList.includes(a));ASSERT(!pluginRenderList.includes(b));a&&pluginUpdateList.push(a);b&&pluginRenderList.push(b)}function engineInit(a,b,c,d,e,f=[],g=document.body){function k(n=0){var l=n-frameTimeLastMS;frameTimeLastMS=n;if(debug||showWatermark)averageFPS=lerp(.05,averageFPS,1e3/(l||1));n=debug&&keyIsDown("Equal");const p=debug&&keyIsDown("Minus");debug&&(l*=n?5:p?.2:1);timeReal+=l/1e3;frameTimeBufferMS+=paused?0:l;n||(frameTimeBufferMS=min(frameTimeBufferMS,50));h();if(paused){for(const r of engineObjects)r.parent||r.updateTransforms();inputUpdate();pluginUpdateList.forEach(r=>r());debugUpdate();c();inputUpdatePost()}else{l=0;0>frameTimeBufferMS&&-9<frameTimeBufferMS&&(l=frameTimeBufferMS,frameTimeBufferMS=0);for(;0<=frameTimeBufferMS;frameTimeBufferMS-=1e3/frameRate)time=frame++/frameRate,inputUpdate(),b(),pluginUpdateList.forEach(r=>r()),engineObjectsUpdate(),debugUpdate(),c(),inputUpdatePost();frameTimeBufferMS+=l}if(!headlessMode){mainCanvasSize=vec2(mainCanvas.width,mainCanvas.height);overlayContext.imageSmoothingEnabled=mainContext.imageSmoothingEnabled=!tilesPixelated;glPreRender();d();engineObjects.sort((r,x)=>r.renderOrder-x.renderOrder);for(var q of engineObjects)q.destroyed||q.render();e();pluginRenderList.forEach(r=>r());touchGamepadRender();debugRender();glCopyToContext(mainContext);showWatermark&&(overlayContext.textAlign="right",overlayContext.textBaseline="top",overlayContext.font="1em monospace",overlayContext.fillStyle="#000",q=engineName+" v"+engineVersion+" / "+drawCount+" / "+engineObjects.length+" / "+averageFPS.toFixed(1)+(glEnable?" GL":" 2D"),overlayContext.fillText(q,mainCanvas.width-3,3),overlayContext.fillStyle="#fff",overlayContext.fillText(q,mainCanvas.width-2,2),drawCount=0)}requestAnimationFrame(k)}function h(){if(!headlessMode){if(canvasFixedSize.x){mainCanvas.width=canvasFixedSize.x;mainCanvas.height=canvasFixedSize.y;const n=innerWidth/innerHeight,l=mainCanvas.width/mainCanvas.height;(glCanvas||mainCanvas).style.width=mainCanvas.style.width=overlayCanvas.style.width=n<l?"100%":"";(glCanvas||mainCanvas).style.height=mainCanvas.style.height=overlayCanvas.style.height=n<l?"":"100%"}else mainCanvas.width=min(innerWidth,canvasMaxSize.x),mainCanvas.height=min(innerHeight,canvasMaxSize.y);overlayCanvas.width=mainCanvas.width;overlayCanvas.height=mainCanvas.height;mainCanvasSize=vec2(mainCanvas.width,mainCanvas.height)}}function m(){new Promise(n=>n(a())).then(k)}ASSERT(!mainContext,"engine already initialized");ASSERT(Array.isArray(f),"pass in images as array");a||=()=>{};b||=()=>{};c||=()=>{};d||=()=>{};e||=()=>{};headlessMode?m():(g.style.cssText="margin:0;overflow:hidden;width:100vw;height:100vh;display:flex;align-items:center;justify-content:center;background:#000;"+(canvasPixelated?"image-rendering:pixelated;":"")+"user-select:none;-webkit-user-select:none;"+(touchInputEnable?"touch-action:none;-webkit-touch-callout:none":""),g.appendChild(mainCanvas=document.createElement("canvas")),mainContext=mainCanvas.getContext("2d"),inputInit(),audioInit(),debugInit(),glInit(),g.appendChild(overlayCanvas=document.createElement("canvas")),overlayContext=overlayCanvas.getContext("2d"),mainCanvas.style.cssText=overlayCanvas.style.cssText="position:absolute",glCanvas&&(glCanvas.style.cssText="position:absolute"),h(),g=f.map((n,l)=>new Promise(p=>{const q=new Image;q.crossOrigin="anonymous";q.onerror=q.onload=()=>{textureInfos[l]=new TextureInfo(q);p()};q.src=n})),f.length||g.push(new Promise(n=>{textureInfos[0]=new TextureInfo(new Image);n()})),showSplashScreen&&g.push(new Promise(n=>{function l(){clearInput();drawEngineSplashScreen(p+=.01);1<p?n():setTimeout(l,16)}let p=0;console.log(`${engineName} Engine v${engineVersion}`);l()})),Promise.all(g).then(m))}function engineObjectsUpdate(){function a(b){if(!b.destroyed){b.update();for(const c of b.children)a(c)}}engineObjectsCollide=engineObjects.filter(b=>b.collideSolidObjects);for(const b of engineObjects)b.parent||(a(b),b.updateTransforms());engineObjects=engineObjects.filter(b=>!b.destroyed)}function engineObjectsDestroy(){for(const a of engineObjects)a.parent||a.destroy();engineObjects=engineObjects.filter(a=>!a.destroyed)}function engineObjectsCollect(a,b,c=engineObjects){const d=[];if(a)if(b instanceof Vector2)for(const e of c)isOverlapping(a,b,e.pos,e.size)&&d.push(e);else{b*=b;for(const e of c)a.distanceSquared(e.pos)<b&&d.push(e)}else for(const e of c)d.push(e);return d}function engineObjectsCallback(a,b,c,d=engineObjects){engineObjectsCollect(a,b,d).forEach(e=>c(e))}function engineObjectsRaycast(a,b,c=engineObjects){const d=[];for(const e of c)e.collideRaycast&&isIntersecting(a,b,e.pos,e.size)&&(debugRaycast&&debugRect(e.pos,e.size,"#f00"),d.push(e));debugRaycast&&debugLine(a,b,d.length?"#f00":"#00f",.02);return d}function drawEngineSplashScreen(a){const b=overlayContext;var c=overlayCanvas.width=innerWidth,d=overlayCanvas.height=innerHeight,e=percent(a,1,.8),f=percent(a,0,.5),g=b.createRadialGradient(c/2,d/2,0,c/2,d/2,.7*Math.hypot(c,d));g.addColorStop(0,hsl(0,0,lerp(f,0,e/2),e).toString());g.addColorStop(1,hsl(0,0,0,e).toString());b.save();b.fillStyle=g;b.fillRect(0,0,c,d);g=(h,m,n,l,p)=>{b.beginPath();b.rect(h,m,n,p?l*k:l);(b.fillStyle=p)?b.fill():b.stroke()};f=(h,m,n,l=0,p=2*PI,q,r)=>{const x=(l+p)/2;l=k*(p-l)/2;b.beginPath();r&&b.lineTo(h,m);b.arc(h,m,n,x-l,x+l);(b.fillStyle=q)?b.fill():b.stroke()};e=(h=0,m=0)=>hsl([.98,.3,.57,.14][h%4]-10,.8,[0,.3,.5,.8,.9][m]).toString();a=wave(1,1,a);const k=percent(a,.1,.5);b.translate(c/2,d/2);c=min(6,min(c,d)/99);b.scale(c,c);b.translate(-40,-35);b.lineJoin=b.lineCap="round";b.lineWidth=.1+1.9*k;c=percent(a,.1,1);b.setLineDash([99*c,99]);g(7,16,18,-8,e(2,2));g(7,8,18,4,e(2,3));g(25,8,8,8,e(2,1));g(25,8,-18,8);g(25,8,8,8);g(25,16,7,23,e());g(11,39,14,-23,e(1,1));g(11,16,14,18,e(1,2));g(11,16,14,8,e(1,3));g(25,16,-14,24);g(15,29,6,-9,e(2,2));f(15,21,5,0,PI/2,e(2,4),1);g(21,21,-6,9);g(37,14,9,6,e(3,2));g(37,14,4.5,6,e(3,3));g(37,14,9,6);g(50,20,10,-8,e(0,1));g(50,20,6.5,-8,e(0,2));g(50,20,3.5,-8,e(0,3));g(50,20,10,-8);f(55,2,11.4,.5,PI-.5,e(3,3));f(55,2,11.4,.5,PI/2,e(3,2),1);f(55,2,11.4,.5,PI-.5);g(45,7,20,-7,e(0,2));g(45,-1,20,4,e(0,3));g(45,-1,20,8);for(c=5;c--;)f(60-6*c,30,9.9,0,2*PI,e(c+2,3)),f(60-6*c,30,10,-.5,PI+.5,e(c+2,2)),f(60-6*c,30,10.1,.5,PI-.5,e(c+2,1));f(36,30,10,PI/2,3*PI/2);f(48,30,10,PI/2,3*PI/2);f(60,30,10);b.beginPath();b.lineTo(36,20);b.lineTo(60,20);b.stroke();f(60,30,4,PI,3*PI,e(3,2));f(60,30,4,PI,2*PI,e(3,3));f(60,30,4,PI,3*PI);for(c=6;c--;)b.beginPath(),b.lineTo(53,54),b.lineTo(53,40),b.lineTo(53+(1+2.9*c)*k,40),b.lineTo(53+(4+3.5*c)*k,54),b.fillStyle=e(0,c%2+2),b.fill(),c%2&&b.stroke();g(6,40,5,5);g(6,40,5,5,e());g(15,54,38,-14,e());for(g=3;g--;)for(c=2;c--;)f(15*g+15,47,c?7:1,PI,3*PI,e(g,3)),b.stroke(),f(15*g+15,47,c?7:1,0,PI,e(g,2)),b.stroke();b.beginPath();b.lineTo(6,40);b.lineTo(68,40);b.stroke();b.beginPath();b.lineTo(77,54);b.lineTo(4,54);b.stroke();f=engineName;b.font="900 16px arial";b.textAlign="center";b.textBaseline="top";b.lineWidth=.1+3.9*k;g=0;for(c=0;c<f.length;++c)g+=b.measureText(f[c]).width;for(c=2;c--;)for(let h=0,m=41-g/2;h<f.length;++h)b.fillStyle=e(h,2),d=b.measureText(f[h]).width,b[c?"strokeText":"fillText"](f[h],m+d/2,55.5,17*k),m+=d;b.restore()}
|
|
1
|
+
let showWatermark=0,debugKey="";const debug=0,debugOverlay=0,debugPhysics=0,debugParticles=0,debugRaycast=0,debugGamepads=0,debugMedals=0;function ASSERT(){}function debugInit(){}function debugUpdate(){}function debugRender(){}function debugRect(){}function debugPoly(){}function debugCircle(){}function debugPoint(){}function debugLine(){}function debugOverlap(){}function debugText(){}function debugClear(){}function debugScreenshot(){}function debugSaveCanvas(){}function debugSaveText(){}function debugSaveDataURL(){}const PI=Math.PI;function abs(a){return Math.abs(a)}function min(a,b){return Math.min(a,b)}function max(a,b){return Math.max(a,b)}function sign(a){return Math.sign(a)}function mod(a,b=1){return(a%b+b)%b}function clamp(a,b=0,c=1){return a<b?b:a>c?c:a}function percent(a,b,c){return(c-=b)?clamp((a-b)/c):0}function lerp(a,b,c){return b+clamp(a)*(c-b)}function distanceWrap(a,b,c=1){a=(a-b)%c;return 2*a%c-a}function lerpWrap(a,b,c,d=1){return c+clamp(a)*distanceWrap(b,c,d)}function distanceAngle(a,b){return distanceWrap(a,b,2*PI)}function lerpAngle(a,b,c){return lerpWrap(a,b,c,2*PI)}function smoothStep(a){return a*a*(3-2*a)}function nearestPowerOfTwo(a){return 2**Math.ceil(Math.log2(a))}function isOverlapping(a,b,c,d=vec2()){return 2*abs(a.x-c.x)<b.x+d.x&&2*abs(a.y-c.y)<b.y+d.y}function isIntersecting(a,b,c,d){c=c.subtract(d.scale(.5));d=c.add(d);b=b.subtract(a);c=a.subtract(c);d=a.subtract(d);a=[-b.x,b.x,-b.y,b.y];b=[c.x,-d.x,c.y,-d.y];c=0;d=1;for(let e=4;e--;)if(a[e]){const f=b[e]/a[e];if(0>a[e]){if(f>d)return!1;c=max(f,c)}else{if(f<c)return!1;d=min(f,d)}}else if(0>b[e])return!1;return!0}function wave(a=1,b=1,c=time){return b/2*(1-Math.cos(c*a*2*PI))}function formatTime(a){return(a/60|0)+":"+(10>a%60?"0":"")+(a%60|0)}function rand(a=1,b=0){return b+Math.random()*(a-b)}function randInt(a,b=0){return Math.floor(rand(a,b))}function randSign(){return 2*randInt(2)-1}function randVector(a=1){return(new Vector2).setAngle(rand(2*PI),a)}function randInCircle(a=1,b=0){return 0<a?randVector(a*rand(b/a,1)**.5):new Vector2}function randColor(a=new Color,b=new Color(0,0,0,1),c=!1){return c?a.lerp(b,rand()):new Color(rand(a.r,b.r),rand(a.g,b.g),rand(a.b,b.b),rand(a.a,b.a))}class RandomGenerator{constructor(a){this.seed=a}float(a=1,b=0){this.seed^=this.seed<<13;this.seed^=this.seed>>>17;this.seed^=this.seed<<5;return b+(a-b)*abs(this.seed%1e8)/1e8}int(a,b=0){return Math.floor(this.float(a,b))}sign(){return.5<this.float()?1:-1}}function vec2(a=0,b){return"number"==typeof a?new Vector2(a,void 0==b?a:b):new Vector2(a.x,a.y)}function isVector2(a){return a instanceof Vector2}class Vector2{constructor(a=0,b=0){this.x=a;this.y=b;ASSERT(this.isValid())}set(a=0,b=0){this.x=a;this.y=b;ASSERT(this.isValid());return this}copy(){return new Vector2(this.x,this.y)}add(a){ASSERT(isVector2(a));return new Vector2(this.x+a.x,this.y+a.y)}subtract(a){ASSERT(isVector2(a));return new Vector2(this.x-a.x,this.y-a.y)}multiply(a){ASSERT(isVector2(a));return new Vector2(this.x*a.x,this.y*a.y)}divide(a){ASSERT(isVector2(a));return new Vector2(this.x/a.x,this.y/a.y)}scale(a){ASSERT(!isVector2(a));return new Vector2(this.x*a,this.y*a)}length(){return this.lengthSquared()**.5}lengthSquared(){return this.x**2+this.y**2}distance(a){ASSERT(isVector2(a));return this.distanceSquared(a)**.5}distanceSquared(a){ASSERT(isVector2(a));return(this.x-a.x)**2+(this.y-a.y)**2}normalize(a=1){const b=this.length();return b?this.scale(a/b):new Vector2(0,a)}clampLength(a=1){const b=this.length();return b>a?this.scale(a/b):this}dot(a){ASSERT(isVector2(a));return this.x*a.x+this.y*a.y}cross(a){ASSERT(isVector2(a));return this.x*a.y-this.y*a.x}angle(){return Math.atan2(this.x,this.y)}setAngle(a=0,b=1){this.x=b*Math.sin(a);this.y=b*Math.cos(a);return this}rotate(a){const b=Math.cos(a);a=Math.sin(a);return new Vector2(this.x*b+this.y*a,this.x*a+this.y*b)}setDirection(a,b=1){a=mod(a,4);ASSERT(0==a||1==a||2==a||3==a);return vec2(a%2?a-1?-b:b:0,a%2?0:a?-b:b)}direction(){return abs(this.x)>abs(this.y)?0>this.x?3:1:0>this.y?2:0}invert(){return new Vector2(this.y,-this.x)}floor(){return new Vector2(Math.floor(this.x),Math.floor(this.y))}area(){return abs(this.x*this.y)}lerp(a,b){ASSERT(isVector2(a));return this.add(a.subtract(this).scale(clamp(b)))}arrayCheck(a){ASSERT(isVector2(a));return 0<=this.x&&0<=this.y&&this.x<a.x&&this.y<a.y}toString(a=3){if(debug)return`(${(0>this.x?"":" ")+this.x.toFixed(a)},${(0>this.y?"":" ")+this.y.toFixed(a)} )`}isValid(){return"number"==typeof this.x&&!isNaN(this.x)&&"number"==typeof this.y&&!isNaN(this.y)}}function rgb(a,b,c,d){return new Color(a,b,c,d)}function hsl(a,b,c,d){return(new Color).setHSLA(a,b,c,d)}function isColor(a){return a instanceof Color}class Color{constructor(a=1,b=1,c=1,d=1){this.r=a;this.g=b;this.b=c;this.a=d;ASSERT(this.isValid())}set(a=1,b=1,c=1,d=1){this.r=a;this.g=b;this.b=c;this.a=d;ASSERT(this.isValid());return this}copy(){return new Color(this.r,this.g,this.b,this.a)}add(a){ASSERT(isColor(a));return new Color(this.r+a.r,this.g+a.g,this.b+a.b,this.a+a.a)}subtract(a){ASSERT(isColor(a));return new Color(this.r-a.r,this.g-a.g,this.b-a.b,this.a-a.a)}multiply(a){ASSERT(isColor(a));return new Color(this.r*a.r,this.g*a.g,this.b*a.b,this.a*a.a)}divide(a){ASSERT(isColor(a));return new Color(this.r/a.r,this.g/a.g,this.b/a.b,this.a/a.a)}scale(a,b=a){return new Color(this.r*a,this.g*a,this.b*a,this.a*b)}clamp(){return new Color(clamp(this.r),clamp(this.g),clamp(this.b),clamp(this.a))}lerp(a,b){ASSERT(isColor(a));return this.add(a.subtract(this).scale(clamp(b)))}setHSLA(a=0,b=0,c=1,d=1){a=mod(a,1);b=clamp(b);c=clamp(c);b=.5>c?c*(1+b):c+b-c*b;c=2*c-b;const e=(f,g,k)=>1>6*(k=mod(k,1))?f+6*(g-f)*k:1>2*k?g:2>3*k?f+(g-f)*(4-6*k):f;this.r=e(c,b,a+1/3);this.g=e(c,b,a);this.b=e(c,b,a-1/3);this.a=d;ASSERT(this.isValid());return this}HSLA(){const a=clamp(this.r),b=clamp(this.g),c=clamp(this.b),d=clamp(this.a),e=Math.max(a,b,c),f=Math.min(a,b,c),g=(e+f)/2;let k=0,h=0;if(e!=f){let m=e-f;h=.5<g?m/(2-e-f):m/(e+f);a==e?k=(b-c)/m+(b<c?6:0):b==e?k=(c-a)/m+2:c==e&&(k=(a-b)/m+4)}return[k/6,h,g,d]}mutate(a=.05,b=0){return new Color(this.r+rand(a,-a),this.g+rand(a,-a),this.b+rand(a,-a),this.a+rand(b,-b)).clamp()}toString(a=!0){const b=c=>(16>(c=255*clamp(c)|0)?"0":"")+c.toString(16);return"#"+b(this.r)+b(this.g)+b(this.b)+(a?b(this.a):"")}setHex(a){ASSERT("string"==typeof a&&"#"==a[0]);ASSERT([4,5,7,9].includes(a.length),"Invalid hex");6>a.length?(this.r=clamp(parseInt(a[1],16)/15),this.g=clamp(parseInt(a[2],16)/15),this.b=clamp(parseInt(a[3],16)/15),this.a=5==a.length?clamp(parseInt(a[4],16)/15):1):(this.r=clamp(parseInt(a.slice(1,3),16)/255),this.g=clamp(parseInt(a.slice(3,5),16)/255),this.b=clamp(parseInt(a.slice(5,7),16)/255),this.a=9==a.length?clamp(parseInt(a.slice(7,9),16)/255):1);ASSERT(this.isValid());return this}rgbaInt(){const a=255*clamp(this.r)|0,b=255*clamp(this.g)<<8,c=255*clamp(this.b)<<16,d=255*clamp(this.a)<<24;return a+b+c+d}isValid(){return"number"==typeof this.r&&!isNaN(this.r)&&"number"==typeof this.g&&!isNaN(this.g)&&"number"==typeof this.b&&!isNaN(this.b)&&"number"==typeof this.a&&!isNaN(this.a)}}const WHITE=rgb(),BLACK=rgb(0,0,0),GRAY=rgb(.5,.5,.5),RED=rgb(1,0,0),ORANGE=rgb(1,.5,0),YELLOW=rgb(1,1,0),GREEN=rgb(0,1,0),CYAN=rgb(0,1,1),BLUE=rgb(0,0,1),PURPLE=rgb(.5,0,1),MAGENTA=rgb(1,0,1);class Timer{constructor(a){this.time=void 0==a?void 0:time+a;this.setTime=a}set(a=0){this.time=time+a;this.setTime=a}unset(){this.time=void 0}isSet(){return void 0!=this.time}active(){return time<this.time}elapsed(){return time>=this.time}get(){return this.isSet()?time-this.time:0}getPercent(){return this.isSet()?1-percent(this.time-time,0,this.setTime):0}toString(){if(debug)return this.isSet()?Math.abs(this.get())+" seconds "+(0>this.get()?"before":"after"):"unset"}valueOf(){return this.get()}}let cameraPos=vec2(),cameraScale=32,canvasMaxSize=vec2(1920,1080),canvasFixedSize=vec2(),canvasPixelated=!0,tilesPixelated=!0,fontDefault="arial",showSplashScreen=!1,headlessMode=!1,glEnable=!0,glOverlay=!0,tileSizeDefault=vec2(16),tileFixBleedScale=0,enablePhysicsSolver=!0,objectDefaultMass=1,objectDefaultDamping=1,objectDefaultAngleDamping=1,objectDefaultElasticity=0,objectDefaultFriction=.8,objectMaxSpeed=1,gravity=0,particleEmitRateScale=1,gamepadsEnable=!0,gamepadDirectionEmulateStick=!0,inputWASDEmulateDirection=!0,touchInputEnable=!0,touchGamepadEnable=!1,touchGamepadAnalog=!0,touchGamepadSize=99,touchGamepadAlpha=.3,vibrateEnable=!0,soundEnable=!0,soundVolume=.3,soundDefaultRange=40,soundDefaultTaper=.7,medalDisplayTime=5,medalDisplaySlideTime=.5,medalDisplaySize=vec2(640,80),medalDisplayIconSize=50,medalsPreventUnlock=!1;function setCameraPos(a){cameraPos=a}function setCameraScale(a){cameraScale=a}function setCanvasMaxSize(a){canvasMaxSize=a}function setCanvasFixedSize(a){canvasFixedSize=a}function setCanvasPixelated(a){canvasPixelated=a}function setTilesPixelated(a){tilesPixelated=a}function setFontDefault(a){fontDefault=a}function setShowSplashScreen(a){showSplashScreen=a}function setHeadlessMode(a){headlessMode=a}function setGlEnable(a){glEnable=a}function setGlOverlay(a){glOverlay=a}function setTileSizeDefault(a){tileSizeDefault=a}function setTileFixBleedScale(a){tileFixBleedScale=a}function setEnablePhysicsSolver(a){enablePhysicsSolver=a}function setObjectDefaultMass(a){objectDefaultMass=a}function setObjectDefaultDamping(a){objectDefaultDamping=a}function setObjectDefaultAngleDamping(a){objectDefaultAngleDamping=a}function setObjectDefaultElasticity(a){objectDefaultElasticity=a}function setObjectDefaultFriction(a){objectDefaultFriction=a}function setObjectMaxSpeed(a){objectMaxSpeed=a}function setGravity(a){gravity=a}function setParticleEmitRateScale(a){particleEmitRateScale=a}function setGamepadsEnable(a){gamepadsEnable=a}function setGamepadDirectionEmulateStick(a){gamepadDirectionEmulateStick=a}function setInputWASDEmulateDirection(a){inputWASDEmulateDirection=a}function setTouchInputEnable(a){touchInputEnable=a}function setTouchGamepadEnable(a){touchGamepadEnable=a}function setTouchGamepadAnalog(a){touchGamepadAnalog=a}function setTouchGamepadSize(a){touchGamepadSize=a}function setTouchGamepadAlpha(a){touchGamepadAlpha=a}function setVibrateEnable(a){vibrateEnable=a}function setSoundEnable(a){soundEnable=a}function setSoundVolume(a){soundVolume=a;soundEnable&&!headlessMode&&audioGainNode&&(audioGainNode.gain.value=a)}function setSoundDefaultRange(a){soundDefaultRange=a}function setSoundDefaultTaper(a){soundDefaultTaper=a}function setMedalDisplayTime(a){medalDisplayTime=a}function setMedalDisplaySlideTime(a){medalDisplaySlideTime=a}function setMedalDisplaySize(a){medalDisplaySize=a}function setMedalDisplayIconSize(a){medalDisplayIconSize=a}function setMedalsPreventUnlock(a){medalsPreventUnlock=a}function setShowWatermark(a){showWatermark=a}function setDebugKey(a){debugKey=a}class EngineObject{constructor(a=vec2(),b=vec2(1),c,d=0,e=new Color,f=0){ASSERT(isVector2(a)&&isVector2(b),"ensure pos and size are vec2s");ASSERT("number"!==typeof c||!c,"old style tile setup");this.pos=a.copy();this.size=b;this.drawSize=void 0;this.tileInfo=c;this.angle=d;this.color=e;this.additiveColor=void 0;this.mirror=!1;this.mass=objectDefaultMass;this.damping=objectDefaultDamping;this.angleDamping=objectDefaultAngleDamping;this.elasticity=objectDefaultElasticity;this.friction=objectDefaultFriction;this.gravityScale=1;this.renderOrder=f;this.velocity=vec2();this.angleVelocity=0;this.spawnTime=time;this.children=[];this.clampSpeedLinear=!0;this.parent=void 0;this.localPos=vec2();this.localAngle=0;this.collideRaycast=this.isSolid=this.collideSolidObjects=this.collideTiles=!1;engineObjects.push(this)}updateTransforms(){const a=this.parent;if(a){const b=a.getMirrorSign();this.pos=this.localPos.multiply(vec2(b,1)).rotate(-a.angle).add(a.pos);this.angle=b*this.localAngle+a.angle}for(const b of this.children)b.updateTransforms()}update(){if(!this.parent){if(this.clampSpeedLinear)this.velocity.x=clamp(this.velocity.x,-objectMaxSpeed,objectMaxSpeed),this.velocity.y=clamp(this.velocity.y,-objectMaxSpeed,objectMaxSpeed);else{var a=this.velocity.lengthSquared();a>objectMaxSpeed*objectMaxSpeed&&(a=objectMaxSpeed/a**.5,this.velocity.x*=a,this.velocity.y*=a)}a=this.pos.copy();this.velocity.x*=this.damping;this.velocity.y*=this.damping;this.mass&&(this.velocity.y+=gravity*this.gravityScale);this.pos.x+=this.velocity.x;this.pos.y+=this.velocity.y;this.angle+=this.angleVelocity*=this.angleDamping;ASSERT(0<=this.angleDamping&&1>=this.angleDamping);ASSERT(0<=this.damping&&1>=this.damping);if(enablePhysicsSolver&&this.mass){var b=0>this.velocity.y;if(this.groundObject){var c=this.groundObject.velocity?this.groundObject.velocity.x:0;this.velocity.x=c+(this.velocity.x-c)*this.friction;this.groundObject=0}if(this.collideSolidObjects)for(var d of engineObjectsCollide){if(!this.isSolid&&!d.isSolid||d.destroyed||d.parent||d==this)continue;if(!isOverlapping(this.pos,this.size,d.pos,d.size))continue;c=this.collideWithObject(d);var e=d.collideWithObject(this);if(!c||!e)continue;if(isOverlapping(a,this.size,d.pos,d.size)){c=a.subtract(d.pos);e=c.length();c=.01>e?randVector(.001):c.scale(.001/e);this.velocity=this.velocity.add(c);d.mass&&(d.velocity=d.velocity.subtract(c));debugOverlay&&debugPhysics&&debugOverlap(this.pos,this.size,d.pos,d.size,"#f00");continue}e=this.size.add(d.size);var f=2*(a.y-d.pos.y)>e.y+gravity;const k=2*abs(a.y-d.pos.y)<e.y;var g=2*abs(a.x-d.pos.x)<e.x;c=max(this.elasticity,d.elasticity);if(f||g||!k)if(this.pos.y=d.pos.y+(e.y/2+.001)*sign(a.y-d.pos.y),d.groundObject&&b||!d.mass)b&&(this.groundObject=d),this.velocity.y*=-c;else if(d.mass){g=(this.mass*this.velocity.y+d.mass*d.velocity.y)/(this.mass+d.mass);const h=d.velocity.y*(d.mass-this.mass)/(this.mass+d.mass)+2*this.velocity.y*this.mass/(this.mass+d.mass);this.velocity.y=lerp(c,g,this.velocity.y*(this.mass-d.mass)/(this.mass+d.mass)+2*d.velocity.y*d.mass/(this.mass+d.mass));d.velocity.y=lerp(c,g,h)}!f&&k&&(this.pos.x=d.pos.x+(e.x/2+.001)*sign(a.x-d.pos.x),d.mass?(e=(this.mass*this.velocity.x+d.mass*d.velocity.x)/(this.mass+d.mass),f=d.velocity.x*(d.mass-this.mass)/(this.mass+d.mass)+2*this.velocity.x*this.mass/(this.mass+d.mass),this.velocity.x=lerp(c,e,this.velocity.x*(this.mass-d.mass)/(this.mass+d.mass)+2*d.velocity.x*d.mass/(this.mass+d.mass)),d.velocity.x=lerp(c,e,f)):this.velocity.x*=-c);debugOverlay&&debugPhysics&&debugOverlap(this.pos,this.size,d.pos,d.size,"#f0f")}if(this.collideTiles&&tileCollisionTest(this.pos,this.size,this)&&!tileCollisionTest(a,this.size,this)){d=tileCollisionTest(vec2(a.x,this.pos.y),this.size,this);c=tileCollisionTest(vec2(this.pos.x,a.y),this.size,this);if(d||!c)this.velocity.y*=-this.elasticity,(this.groundObject=b)?this.pos.y=(a.y-this.size.y/2|0)+this.size.y/2+1e-4:this.pos.y=a.y;c&&(this.pos.x=a.x,this.velocity.x*=-this.elasticity);debugOverlay&&debugPhysics&&debugRect(this.pos,this.size,"#f00")}}}}render(){drawTile(this.pos,this.drawSize||this.size,this.tileInfo,this.color,this.angle,this.mirror,this.additiveColor)}destroy(){if(!this.destroyed){this.destroyed=1;this.parent&&this.parent.removeChild(this);for(const a of this.children)a.destroy(a.parent=0)}}localToWorld(a){return this.pos.add(a.rotate(this.angle))}worldToLocal(a){return a.subtract(this.pos).rotate(-this.angle)}localToWorldVector(a){return a.rotate(-this.angle)}worldToLocalVector(a){return a.rotate(this.angle)}collideWithTile(a,b){return 0<a}collideWithObject(a){return!0}getAliveTime(){return time-this.spawnTime}applyAcceleration(a){this.mass&&(this.velocity=this.velocity.add(a))}applyForce(a){this.applyAcceleration(a.scale(1/this.mass))}getMirrorSign(){return this.mirror?-1:1}addChild(a,b=vec2(),c=0){ASSERT(!a.parent&&!this.children.includes(a));this.children.push(a);a.parent=this;a.localPos=b.copy();a.localAngle=c}removeChild(a){ASSERT(a.parent==this&&this.children.includes(a));this.children.splice(this.children.indexOf(a),1);a.parent=0}setCollision(a=!0,b=!0,c=!0,d=!0){ASSERT(a||!b,"solid objects must be set to collide");this.collideSolidObjects=a;this.isSolid=b;this.collideTiles=c;this.collideRaycast=d}toString(){if(debug){let a="type = "+this.constructor.name;if(this.pos.x||this.pos.y)a+="\npos = "+this.pos;if(this.velocity.x||this.velocity.y)a+="\nvelocity = "+this.velocity;if(this.size.x||this.size.y)a+="\nsize = "+this.size;this.angle&&(a+="\nangle = "+this.angle.toFixed(3));this.color&&(a+="\ncolor = "+this.color);return a}}renderDebugInfo(){if(debug){const a=vec2(max(this.size.x,.2),max(this.size.y,.2)),b=rgb(this.collideTiles?1:0,this.collideSolidObjects?1:0,this.isSolid?1:0,this.parent?.2:.5),c=this.parent?rgb(1,1,1,.5):rgb(0,0,0,.8);drawRect(this.pos,a,b,this.angle,!1);drawRect(this.pos,a.scale(.8),c,this.angle,!1);this.parent&&drawLine(this.pos,this.parent.pos,.1,rgb(0,0,1,.5),!1)}}}let mainCanvas,mainContext,overlayCanvas,overlayContext,mainCanvasSize=vec2(),textureInfos=[],drawCount;function tile(a=vec2(),b=tileSizeDefault,c=0,d=0){if(headlessMode)return new TileInfo;"number"===typeof b&&(ASSERT(0<b),b=vec2(b));var e=textureInfos[c];ASSERT(!!e,"Texture not loaded");const f=b.add(vec2(2*d));"number"===typeof a&&(e=e.size.x/f.x|0,a=0<e?vec2(a%e,a/e|0):vec2());a=vec2(a.x*f.x+d,a.y*f.y+d);return new TileInfo(a,b,c,d)}class TileInfo{constructor(a=vec2(),b=tileSizeDefault,c=0,d=0){this.pos=a.copy();this.size=b.copy();this.textureIndex=c;this.padding=d}offset(a){return new TileInfo(this.pos.add(a),this.size,this.textureIndex)}frame(a){ASSERT("number"==typeof a);return this.offset(vec2(a*(this.size.x+2*this.padding),0))}getTextureInfo(){return textureInfos[this.textureIndex]}}class TextureInfo{constructor(a){this.image=a;this.size=vec2(a.width,a.height);this.glTexture=glEnable&&glCreateTexture(a)}}function screenToWorld(a){return new Vector2((a.x-mainCanvasSize.x/2+.5)/cameraScale+cameraPos.x,(a.y-mainCanvasSize.y/2+.5)/-cameraScale+cameraPos.y)}function worldToScreen(a){return new Vector2((a.x-cameraPos.x)*cameraScale+mainCanvasSize.x/2-.5,(a.y-cameraPos.y)*-cameraScale+mainCanvasSize.y/2-.5)}function getCameraSize(){return mainCanvasSize.scale(1/cameraScale)}function drawTile(a,b=vec2(1),c,d=new Color,e=0,f,g=new Color(0,0,0,0),k=glEnable,h,m){ASSERT(!m||!k,"context only supported in canvas 2D mode");ASSERT("number"!==typeof c||!c,"this is an old style calls, to fix replace it with tile(tileIndex, tileSize)");const n=c&&c.getTextureInfo();if(k)if(h&&(a=screenToWorld(a),b=b.scale(1/cameraScale)),n){var l=vec2(1).divide(n.size);k=c.pos.x*l.x;h=c.pos.y*l.y;m=c.size.x*l.x;const p=c.size.y*l.y;l=l.scale(tileFixBleedScale);glSetTexture(n.glTexture);glDraw(a.x,a.y,f?-b.x:b.x,b.y,e,k+l.x,h+l.y,k-l.x+m,h-l.y+p,d.rgbaInt(),g.rgbaInt())}else glDraw(a.x,a.y,b.x,b.y,e,0,0,0,0,0,d.rgbaInt());else showWatermark&&++drawCount,b=vec2(b.x,-b.y),drawCanvas2D(a,b,e,f,p=>{if(n){const q=c.pos.x+tileFixBleedScale,r=c.pos.y+tileFixBleedScale,x=c.size.x-2*tileFixBleedScale,v=c.size.y-2*tileFixBleedScale;p.globalAlpha=d.a;p.drawImage(n.image,q,r,x,v,-.5,-.5,1,1);p.globalAlpha=1}else p.fillStyle=d,p.fillRect(-.5,-.5,1,1)},h,m)}function drawRect(a,b,c,d,e,f,g){drawTile(a,b,void 0,c,d,!1,void 0,e,f,g)}function drawLine(a,b,c=.1,d,e,f,g){b=vec2((b.x-a.x)/2,(b.y-a.y)/2);c=vec2(c,2*b.length());drawRect(a.add(b),c,d,b.angle(),e,f,g)}function drawPoly(a,b=new Color,c=0,d=new Color(0,0,0),e,f=mainContext){f.fillStyle=b.toString();f.beginPath();for(const g of e?a:a.map(worldToScreen))f.lineTo(g.x,g.y);f.closePath();f.fill();c&&(f.strokeStyle=d.toString(),f.lineWidth=e?c:c*cameraScale,f.stroke())}function drawEllipse(a,b=1,c=1,d=0,e=new Color,f=0,g=new Color(0,0,0),k,h=mainContext){k||(a=worldToScreen(a),b*=cameraScale,c*=cameraScale,f*=cameraScale);h.fillStyle=e.toString();h.beginPath();h.ellipse(a.x,a.y,b,c,d,0,9);h.fill();f&&(h.strokeStyle=g.toString(),h.lineWidth=f,h.stroke())}function drawCircle(a,b=1,c=new Color,d=0,e=new Color(0,0,0),f,g=mainContext){drawEllipse(a,b,b,0,c,d,e,f,g)}function drawCanvas2D(a,b,c,d,e,f,g=mainContext){f||(a=worldToScreen(a),b=b.scale(cameraScale));g.save();g.translate(a.x+.5,a.y+.5);g.rotate(c);g.scale(d?-b.x:b.x,-b.y);e(g);g.restore()}function drawText(a,b,c=1,d,e=0,f,g,k,h,m=mainContext){drawTextScreen(a,worldToScreen(b),c*cameraScale,d,e*cameraScale,f,g,k,h,m)}function drawTextOverlay(a,b,c=1,d,e=0,f,g,k,h){drawText(a,b,c,d,e,f,g,k,h,overlayContext)}function drawTextScreen(a,b,c=1,d=new Color,e=0,f=new Color(0,0,0),g="center",k=fontDefault,h,m=overlayContext){m.fillStyle=d.toString();m.lineWidth=e;m.strokeStyle=f.toString();m.textAlign=g;m.font=c+"px "+k;m.textBaseline="middle";m.lineJoin="round";b=b.copy();a=(a+"").split("\n");b.y-=(a.length-1)*c/2;a.forEach(n=>{e&&m.strokeText(n,b.x,b.y,h);m.fillText(n,b.x,b.y,h);b.y+=c})}function setBlendMode(a,b=glEnable,c){ASSERT(!c||!b,"context only supported in canvas 2D mode");b?glAdditive=a:(c||=mainContext,c.globalCompositeOperation=a?"lighter":"source-over")}function combineCanvases(){glCopyToContext(mainContext,!0);mainContext.drawImage(overlayCanvas,0,0);glClearCanvas();overlayCanvas.width|=0}let engineFontImage;class FontImage{constructor(a,b=vec2(8),c=vec2(0,1),d=overlayContext){engineFontImage||((engineFontImage=new Image).src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQAAAAAYAQAAAAA9+x6JAAAAAnRSTlMAAHaTzTgAAAGiSURBVHjaZZABhxxBEIUf6ECLBdFY+Q0PMNgf0yCgsSAGZcT9sgIPtBWwIA5wgAPEoHUyJeeSlW+gjK+fegWwtROWpVQEyWh2npdpBmTUFVhb29RINgLIukoXr5LIAvYQ5ve+1FqWEMqNKTX3FAJHyQDRZvmKWubAACcv5z5Gtg2oyCWE+Yk/8JZQX1jTTCpKAFGIgza+dJCNBF2UskRlsgwitHbSV0QLgt9sTPtsRlvJjEr8C/FARWA2bJ/TtJ7lko34dNDn6usJUMzuErP89UUBJbWeozrwLLncXczd508deAjLWipLO4Q5XGPcJvPu92cNDaN0P5G1FL0nSOzddZOrJ6rNhbXGmeDvO3TF7DeJWl4bvaYQTNHCTeuqKZmbjHaSOFes+IX/+IhHrnAkXOAsfn24EM68XieIECoccD4KZLk/odiwzeo2rovYdhvb2HYFgyznJyDpYJdYOmfXgVdJTaUi4xA2uWYNYec9BLeqdl9EsoTw582mSFDX2DxVLbNt9U3YYoeatBad1c2Tj8t2akrjaIGJNywKB/7h75/gN3vCMSaadIUTAAAAAElFTkSuQmCC");this.image=a||engineFontImage;this.tileSize=b;this.paddingSize=c;this.context=d}drawText(a,b,c=1,d){this.drawTextScreen(a,worldToScreen(b).floor(),c*cameraScale|0,d)}drawTextScreen(a,b,c=4,d){const e=this.context;e.save();const f=this.tileSize,g=f.add(this.paddingSize).scale(c),k=this.image.width/this.tileSize.x|0;(a+"").split("\n").forEach((h,m)=>{const n=d?h.length*f.x*c/2|0:0;for(let q=h.length;q--;){var l=h[q].charCodeAt(0);if(32>l||127<l)l=127;var p=l-32;l=p%k;p=p/k|0;const r=b.add(vec2(q,m).multiply(g));e.drawImage(this.image,l*f.x,p*f.y,f.x,f.y,r.x-n,r.y,f.x*c,f.y*c)}});e.restore()}}function isFullscreen(){return!!document.fullscreenElement}function toggleFullscreen(){const a=mainCanvas.parentElement;isFullscreen()?document.exitFullscreen&&document.exitFullscreen():a.requestFullscreen&&a.requestFullscreen()}function setCursor(a="auto"){mainCanvas.parentElement.style.cursor=a}function keyIsDown(a,b=0){ASSERT(0<b||"number"!==typeof a||3>a,"use code string for keyboard");return inputData[b]&&!!(inputData[b][a]&1)}function keyWasPressed(a,b=0){ASSERT(0<b||"number"!==typeof a||3>a,"use code string for keyboard");return inputData[b]&&!!(inputData[b][a]&2)}function keyWasReleased(a,b=0){ASSERT(0<b||"number"!==typeof a||3>a,"use code string for keyboard");return inputData[b]&&!!(inputData[b][a]&4)}function keyDirection(a="ArrowUp",b="ArrowDown",c="ArrowLeft",d="ArrowRight"){return vec2((keyIsDown(d)?1:0)-(keyIsDown(c)?1:0),(keyIsDown(a)?1:0)-(keyIsDown(b)?1:0))}function clearInput(){inputData=[[]];touchGamepadButtons=[]}const mouseIsDown=keyIsDown,mouseWasPressed=keyWasPressed,mouseWasReleased=keyWasReleased;let mousePos=vec2(),mousePosScreen=vec2(),mouseWheel=0,isUsingGamepad=!1,preventDefaultInput=!1;function gamepadIsDown(a,b=0){return keyIsDown(a,b+1)}function gamepadWasPressed(a,b=0){return keyWasPressed(a,b+1)}function gamepadWasReleased(a,b=0){return keyWasReleased(a,b+1)}function gamepadStick(a,b=0){return gamepadStickData[b]?gamepadStickData[b][a]||vec2():vec2()}let inputData=[[]];function inputUpdate(){headlessMode||(touchInputEnable&&isTouchDevice||document.hasFocus()||clearInput(),mousePos=screenToWorld(mousePosScreen),gamepadsUpdate())}function inputUpdatePost(){if(!headlessMode){for(const a of inputData)for(const b in a)a[b]&=1;mouseWheel=0}}function inputInit(){function a(b){return inputWASDEmulateDirection?"KeyW"==b?"ArrowUp":"KeyS"==b?"ArrowDown":"KeyA"==b?"ArrowLeft":"KeyD"==b?"ArrowRight":b:b}headlessMode||(onkeydown=b=>{b.repeat||(isUsingGamepad=!1,inputData[0][b.code]=3,inputWASDEmulateDirection&&(inputData[0][a(b.code)]=3));preventDefaultInput&&b.preventDefault()},onkeyup=b=>{inputData[0][b.code]=4;inputWASDEmulateDirection&&(inputData[0][a(b.code)]=4)},onmousedown=b=>{soundEnable&&!headlessMode&&audioContext&&"running"!=audioContext.state&&audioContext.resume();isUsingGamepad=!1;inputData[0][b.button]=3;mousePosScreen=mouseEventToScreen(b);b.button&&b.preventDefault()},onmouseup=b=>inputData[0][b.button]=inputData[0][b.button]&2|4,onmousemove=b=>mousePosScreen=mouseEventToScreen(b),onwheel=b=>mouseWheel=b.ctrlKey?0:sign(b.deltaY),oncontextmenu=b=>!1,onblur=b=>clearInput(),isTouchDevice&&touchInputEnable&&touchInputInit())}function mouseEventToScreen(a){const b=mainCanvas.getBoundingClientRect(),c=percent(a.x,b.left,b.right);a=percent(a.y,b.top,b.bottom);return vec2(c*mainCanvas.width,a*mainCanvas.height)}const gamepadStickData=[];function gamepadsUpdate(){const a=g=>{const k=h=>.3<h?percent(h,.3,.8):-.3>h?-percent(-h,.3,.8):0;return vec2(k(g.x),k(-g.y)).clampLength()};if(touchGamepadEnable&&isTouchDevice&&(ASSERT(touchGamepadButtons,"set touchGamepadEnable before calling init!"),touchGamepadTimer.isSet())){var b=gamepadStickData[0]||(gamepadStickData[0]=[]);b[0]=vec2();touchGamepadAnalog?b[0]=a(touchGamepadStick):.3<touchGamepadStick.lengthSquared()&&(b[0].x=Math.round(touchGamepadStick.x),b[0].y=-Math.round(touchGamepadStick.y),b[0]=b[0].clampLength());b=inputData[1]||(inputData[1]=[]);for(var c=10;c--;){var d=3==c?2:2==c?3:c,e=gamepadIsDown(d,0);b[d]=touchGamepadButtons[c]?e?1:3:e?4:0}}if(gamepadsEnable&&navigator&&navigator.getGamepads&&(debug||document.hasFocus()))for(b=navigator.getGamepads(),c=b.length;c--;){e=b[c];const g=inputData[c+1]||(inputData[c+1]=[]);d=gamepadStickData[c]||(gamepadStickData[c]=[]);if(e){for(var f=0;f<e.axes.length-1;f+=2)d[f>>1]=a(vec2(e.axes[f],e.axes[f+1]));for(f=e.buttons.length;f--;){const k=e.buttons[f],h=gamepadIsDown(f,c);g[f]=k.pressed?h?1:3:h?4:0;isUsingGamepad||=!c&&k.pressed}gamepadDirectionEmulateStick&&(e=vec2((gamepadIsDown(15,c)&&1)-(gamepadIsDown(14,c)&&1),(gamepadIsDown(12,c)&&1)-(gamepadIsDown(13,c)&&1)),e.lengthSquared()&&(d[0]=e.clampLength()));touchGamepadEnable&&isUsingGamepad&&touchGamepadTimer.unset()}}}function vibrate(a=100){vibrateEnable&&!headlessMode&&navigator&&navigator.vibrate&&navigator.vibrate(a)}function vibrateStop(){vibrate(0)}const isTouchDevice=!headlessMode&&void 0!==window.ontouchstart;let touchGamepadTimer=new Timer,touchGamepadButtons,touchGamepadStick;function touchInputInit(){function a(e){soundEnable&&!headlessMode&&audioContext&&"running"!=audioContext.state&&audioContext.resume();const f=e.touches.length;if(f){const g=vec2(e.touches[0].clientX,e.touches[0].clientY);mousePosScreen=mouseEventToScreen(g);d?isUsingGamepad=touchGamepadEnable:inputData[0][0]=3}else d&&(inputData[0][0]=inputData[0][0]&2|4);d=f;document.hasFocus()&&e.preventDefault();return!0}function b(e){touchGamepadStick=vec2();touchGamepadButtons=[];isUsingGamepad=!0;if(e.touches.length&&(touchGamepadTimer.set(),paused&&!d)){touchGamepadButtons[9]=1;a(e);return}const f=vec2(touchGamepadSize,mainCanvasSize.y-touchGamepadSize),g=mainCanvasSize.subtract(vec2(touchGamepadSize,touchGamepadSize)),k=mainCanvasSize.scale(.5);for(const m of e.touches){var h=mouseEventToScreen(vec2(m.clientX,m.clientY));h.distance(f)<touchGamepadSize?touchGamepadStick=h.subtract(f).scale(2/touchGamepadSize).clampLength():h.distance(g)<touchGamepadSize?(h=h.subtract(g).direction(),touchGamepadButtons[h]=1):h.distance(k)<touchGamepadSize&&!d&&(touchGamepadButtons[9]=1)}a(e);return!0}let c=a;touchGamepadEnable&&(c=b,touchGamepadButtons=[],touchGamepadStick=vec2());document.addEventListener("touchstart",e=>c(e),{passive:!1});document.addEventListener("touchmove",e=>c(e),{passive:!1});document.addEventListener("touchend",e=>c(e),{passive:!1});onmousedown=onmouseup=()=>0;let d}function touchGamepadRender(){if(touchInputEnable&&isTouchDevice&&!headlessMode&&touchGamepadEnable&&touchGamepadTimer.isSet()){var a=percent(touchGamepadTimer.get(),4,3);if(a&&!paused){var b=overlayContext;b.save();b.globalAlpha=a*touchGamepadAlpha;b.strokeStyle="#fff";b.lineWidth=3;b.fillStyle=0<touchGamepadStick.lengthSquared()?"#fff":"#000";b.beginPath();a=vec2(touchGamepadSize,mainCanvasSize.y-touchGamepadSize);if(touchGamepadAnalog)b.arc(a.x,a.y,touchGamepadSize/2,0,9),b.fill();else for(var c=10;c--;){var d=c*PI/4;b.arc(a.x,a.y,.6*touchGamepadSize,d+PI/8,d+PI/8);c%2&&b.arc(a.x,a.y,.33*touchGamepadSize,d,d);1==c&&b.fill()}b.stroke();a=vec2(mainCanvasSize.x-touchGamepadSize,mainCanvasSize.y-touchGamepadSize);for(c=4;c--;)d=a.add(vec2().setDirection(c,touchGamepadSize/2)),b.fillStyle=touchGamepadButtons[c]?"#fff":"#000",b.beginPath(),b.arc(d.x,d.y,touchGamepadSize/4,0,9),b.fill(),b.stroke();b.restore()}}}let audioContext=new AudioContext,audioGainNode;function audioInit(){soundEnable&&!headlessMode&&(audioGainNode=audioContext.createGain(),audioGainNode.connect(audioContext.destination),audioGainNode.gain.value=soundVolume)}class Sound{constructor(a,b=soundDefaultRange,c=soundDefaultTaper){soundEnable&&!headlessMode&&(this.range=b,this.taper=c,this.randomness=0,a&&(this.randomness=void 0!=a[1]?a[1]:.05,a[1]=0,this.sampleChannels=[zzfxG(...a)],this.sampleRate=zzfxR))}play(a,b=1,c=1,d=1,e=!1){if(soundEnable&&!headlessMode&&this.sampleChannels){var f;if(a){if(f=this.range){const g=cameraPos.distanceSquared(a);if(g>f*f)return;b*=percent(g**.5,f,f*this.taper)}f=2*worldToScreen(a).x/mainCanvas.width-1}a=c+c*this.randomness*d*rand(-1,1);this.gainNode=audioContext.createGain();return this.source=playSamples(this.sampleChannels,b,a,f,e,this.sampleRate,this.gainNode)}}setVolume(a=1){this.gainNode&&(this.gainNode.gain.value=a)}stop(){this.source&&this.source.stop();this.source=void 0}getSource(){return this.source}playNote(a,b,c){return this.play(b,c,2**(a/12),0)}getDuration(){return this.sampleChannels&&this.sampleChannels[0].length/this.sampleRate}isLoading(){return!this.sampleChannels}}class SoundWave extends Sound{constructor(a,b=0,c,d,e){super(void 0,c,d);soundEnable&&!headlessMode&&(this.randomness=b,fetch(a).then(f=>f.arrayBuffer()).then(f=>audioContext.decodeAudioData(f)).then(f=>{this.sampleChannels=[];for(let g=f.numberOfChannels;g--;)this.sampleChannels[g]=Array.from(f.getChannelData(g));this.sampleRate=f.sampleRate}).then(()=>e&&e(this)))}}function playAudioFile(a,b=1,c=!1){if(soundEnable&&!headlessMode)return new SoundWave(a,0,0,0,d=>d.play(void 0,b,1,1,c))}class Music extends Sound{constructor(a){super(void 0);soundEnable&&!headlessMode&&(this.randomness=0,this.sampleChannels=zzfxM(...a),this.sampleRate=zzfxR)}playMusic(a,b=!1){return super.play(void 0,a,1,1,b)}}function speak(a,b="",c=1,d=1,e=1){if(soundEnable&&!headlessMode&&speechSynthesis)return a=new SpeechSynthesisUtterance(a),a.lang=b,a.volume=2*c*soundVolume,a.rate=d,a.pitch=e,speechSynthesis.speak(a),a}function speakStop(){speechSynthesis&&speechSynthesis.cancel()}function getNoteFrequency(a,b=220){return b*2**(a/12)}function playSamples(a,b=1,c=1,d=0,e=!1,f=zzfxR,g){if(soundEnable&&!headlessMode){var k=audioContext.createBuffer(a.length,a[0].length,f),h=audioContext.createBufferSource();a.forEach((m,n)=>k.getChannelData(n).set(m));h.buffer=k;h.playbackRate.value=c;h.loop=e;g=g||audioContext.createGain();g.gain.value=b;g.connect(audioGainNode);a=new StereoPannerNode(audioContext,{pan:clamp(d,-1,1)});h.connect(a).connect(g);"running"!=audioContext.state?audioContext.resume().then(()=>h.start()):h.start();return h}}function zzfx(...a){return playSamples([zzfxG(...a)])}const zzfxR=44100;function zzfxG(a=1,b=.05,c=220,d=0,e=0,f=.1,g=0,k=1,h=0,m=0,n=0,l=0,p=0,q=0,r=0,x=0,v=0,D=1,z=0,E=0,A=0){let w=2*PI;var t=zzfxR;let F=h*=500*w/t/t;b=c*=rand(1+b,1-b)*w/t;let C=[],y=0,G=0,u=0,H=1,R=0,S=0,B=0,J;var L=w*abs(A)*2/t,K=Math.cos(L),M=Math.sin(L)/2/2,I=1+M;L=-2*K/I;M=(1-M)/I;let N=(1+sign(A)*K)/2/I;K=-(sign(A)+K)/I;let O=I=0,P=0,Q=0;d=d*t+9;z*=t;e*=t;f*=t;v*=t;m*=500*w/t**3;r*=w/t;n*=w/t;l*=t;p=p*t|0;for(J=d+z+e+f+v|0;u<J;C[u++]=B*a)++S%(100*x|0)||(B=g?1<g?2<g?3<g?Math.sin(y**3):clamp(Math.tan(y),1,-1):1-(2*y/w%2+2)%2:1-4*abs(Math.round(y/w)-y/w):Math.sin(y),B=(p?1-E+E*Math.sin(w*u/p):1)*sign(B)*abs(B)**k*(u<d?u/d:u<d+z?1-(u-d)/z*(1-D):u<d+z+e?D:u<J-v?(J-u-v)/f*D:0),B=v?B/2+(v>u?0:(u<J-v?1:(J-u)/v)*C[u-v|0]/2/a):B,A&&(B=Q=N*I+K*(I=O)+N*(O=B)-M*P-L*(P=Q))),t=(c+=h+=m)*Math.cos(r*G++),y+=t+t*q*Math.sin(u**5),H&&++H>l&&(c+=n,b+=n,H=0),!p||++R%p||(c=b,h=F,H=H||1);return C}function zzfxM(a,b,c,d=125){let e,f,g,k,h,m,n,l,p,q,r,x,v,D=0,z,E=[],A=[],w=[],t=0,F=0,C=1,y={},G=zzfxR/d*60>>2;for(;C;t++)E=[C=l=x=0],c.forEach((u,H)=>{n=b[u][t]||[0,0,0];C|=b[u][t]&&1;z=x+(b[u][0].length-2-(l?0:1))*G;v=H==c.length-1;e=2;for(g=x;e<n.length+v;l=++e){h=n[e];p=e==n.length+v-1&&v||q!=(n[0]||0)||h|0;for(f=0;f<G&&l;f++>G-99&&p&&1>r?r+=1/99:0)m=(1-r)*E[D++]/2||0,A[g]=(A[g]||0)-m*F+m,w[g]=(w[g++]||0)+m*F+m;h&&(r=h%1,F=n[1]||0,h|=0)&&(E=y[[q=n[D=0]||0,h]]=y[[q,h]]||(k=[...a[q]],k[2]*=2**((h-12)/12),0<h?zzfxG(...k):[]))}x=z});return[A,w]}let tileCollision=[],tileCollisionSize=vec2();function initTileCollision(a){tileCollisionSize=a;tileCollision=[];for(a=tileCollision.length=tileCollisionSize.area();a--;)tileCollision[a]=0}function setTileCollisionData(a,b=0){a.arrayCheck(tileCollisionSize)&&(tileCollision[(a.y|0)*tileCollisionSize.x+a.x|0]=b)}function getTileCollisionData(a){return a.arrayCheck(tileCollisionSize)?tileCollision[(a.y|0)*tileCollisionSize.x+a.x|0]:0}function tileCollisionTest(a,b=vec2(),c){const d=max(a.x-b.x/2|0,0);var e=max(a.y-b.y/2|0,0);const f=min(a.x+b.x/2,tileCollisionSize.x);for(a=min(a.y+b.y/2,tileCollisionSize.y);e<a;++e)for(b=d;b<f;++b){const g=tileCollision[e*tileCollisionSize.x+b];if(g&&(!c||c.collideWithTile(g,vec2(b,e))))return!0}return!1}function tileCollisionRaycast(a,b,c){const d=b.subtract(a),e=d.length();var f=d.normalize();f=vec2(abs(1/f.x),abs(1/f.y));let g=a.floor(),k=f.x*(0>d.x?a.x-g.x:g.x-a.x+1),h=f.y*(0>d.y?a.y-g.y:g.y-a.y+1);for(;;){const m=getTileCollisionData(g);if(m&&(!c||c.collideWithTile(m,g)))return debugRaycast&&debugLine(a,b,"#f00",.02),debugRaycast&&debugPoint(g.add(vec2(.5)),"#ff0"),g.add(vec2(.5));if(k>e&&h>e)break;k>h?(g.y+=sign(d.y),h+=f.y):(g.x+=sign(d.x),k+=f.x)}debugRaycast&&debugLine(a,b,"#00f",.02)}class TileLayerData{constructor(a,b=0,c=!1,d=new Color){this.tile=a;this.direction=b;this.mirror=c;this.color=d}clear(){this.tile=this.direction=0;this.mirror=!1;this.color=new Color}}class TileLayer extends EngineObject{constructor(a,b=tileCollisionSize,c=tile(),d=vec2(1),e=0){super(a,b,c,0,void 0,e);this.canvas=document.createElement("canvas");this.context=this.canvas.getContext("2d");this.scale=d;this.isOverlay=!1;this.data=[];for(a=this.size.area();a--;)this.data.push(new TileLayerData);headlessMode&&(this.redraw=()=>{},this.render=()=>{},this.redrawStart=()=>{},this.redrawEnd=()=>{},this.drawTileData=()=>{},this.drawCanvas2D=()=>{})}setData(a,b,c=!1){a.arrayCheck(this.size)&&(this.data[(a.y|0)*this.size.x+a.x|0]=b,c&&this.drawTileData(a))}getData(a){return a.arrayCheck(this.size)&&this.data[(a.y|0)*this.size.x+a.x|0]}update(){}render(){ASSERT(mainContext!=this.context,"must call redrawEnd() after drawing tiles");glOverlay||this.isOverlay||glCopyToContext(mainContext);let a=worldToScreen(this.pos.add(vec2(0,this.size.y*this.scale.y)));a=a.floor();(this.isOverlay?overlayContext:mainContext).drawImage(this.canvas,a.x,a.y,cameraScale*this.size.x*this.scale.x,cameraScale*this.size.y*this.scale.y)}redraw(){this.redrawStart(!0);for(let a=this.size.x;a--;)for(let b=this.size.y;b--;)this.drawTileData(vec2(a,b),!1);this.redrawEnd()}redrawStart(a=!1){this.savedRenderSettings=[mainCanvas,mainContext,mainCanvasSize,cameraPos,cameraScale];mainCanvas=this.canvas;mainContext=this.context;mainCanvasSize=this.size.multiply(this.tileInfo.size);cameraPos=this.size.scale(.5);cameraScale=this.tileInfo.size.x;a&&(mainCanvas.width=mainCanvasSize.x,mainCanvas.height=mainCanvasSize.y);this.context.imageSmoothingEnabled=!tilesPixelated;glPreRender()}redrawEnd(){ASSERT(mainContext==this.context,"must call redrawStart() before drawing tiles");glCopyToContext(mainContext,!0);[mainCanvas,mainContext,mainCanvasSize,cameraPos,cameraScale]=this.savedRenderSettings}drawTileData(a,b=!0){var c=this.tileInfo.size;b&&(b=a.multiply(c),this.context.clearRect(b.x,this.canvas.height-b.y,c.x,-c.y));b=this.getData(a);void 0!=b.tile&&(ASSERT(mainContext==this.context,"must call redrawStart() before drawing tiles"),a=a.add(vec2(.5)),c=tile(b.tile,c,this.tileInfo.textureIndex),drawTile(a,vec2(1),c,b.color,b.direction*PI/2,b.mirror))}drawCanvas2D(a,b,c,d,e){const f=this.context;f.save();a=a.subtract(this.pos).multiply(this.tileInfo.size);b=b.multiply(this.tileInfo.size);f.translate(a.x,this.canvas.height-a.y);f.rotate(c);f.scale(d?-b.x:b.x,b.y);e(f);f.restore()}drawTile(a,b=vec2(1),c,d=new Color,e,f){this.drawCanvas2D(a,b,e,f,g=>{const k=c&&c.getTextureInfo();k?(g.globalAlpha=d.a,g.drawImage(k.image,c.pos.x,c.pos.y,c.size.x,c.size.y,-.5,-.5,1,1),g.globalAlpha=1):(g.fillStyle=d,g.fillRect(-.5,-.5,1,1))})}drawRect(a,b,c,d){this.drawTile(a,b,void 0,c,d)}}class ParticleEmitter extends EngineObject{constructor(a,b,c=0,d=0,e=100,f=PI,g,k=new Color,h=new Color,m=new Color(1,1,1,0),n=new Color(1,1,1,0),l=.5,p=.1,q=1,r=.1,x=.05,v=1,D=1,z=0,E=PI,A=.1,w=.2,t=!1,F=!1,C=!0,y=F?1e9:0,G=!1){super(a,vec2(),g,b,void 0,y);this.emitSize=c;this.emitTime=d;this.emitRate=e;this.emitConeAngle=f;this.colorStartA=k;this.colorStartB=h;this.colorEndA=m;this.colorEndB=n;this.randomColorLinear=C;this.particleTime=l;this.sizeStart=p;this.sizeEnd=q;this.speed=r;this.angleSpeed=x;this.damping=v;this.angleDamping=D;this.gravityScale=z;this.particleConeAngle=E;this.fadeRate=A;this.randomness=w;this.collideTiles=t;this.additive=F;this.localSpace=G;this.trailScale=0;this.particleCreateCallback=this.particleDestroyCallback=void 0;this.emitTimeBuffer=0}update(){this.parent&&super.update();if(!this.emitTime||this.getAliveTime()<=this.emitTime){if(this.emitRate*particleEmitRateScale){const a=1/this.emitRate/particleEmitRateScale;for(this.emitTimeBuffer+=timeDelta;0<this.emitTimeBuffer;this.emitTimeBuffer-=a)this.emitParticle()}}else this.destroy();debugParticles&&debugRect(this.pos,vec2(this.emitSize),"#0f0",0,this.angle)}emitParticle(){var a="number"===typeof this.emitSize?randInCircle(this.emitSize/2):vec2(rand(-.5,.5),rand(-.5,.5)).multiply(this.emitSize).rotate(this.angle);let b=rand(this.particleConeAngle,-this.particleConeAngle);this.localSpace||(a=this.pos.add(a),b+=this.angle);const c=this.randomness;var d=l=>l+l*rand(c,-c);const e=d(this.particleTime),f=d(this.sizeStart),g=d(this.sizeEnd),k=d(this.speed);d=d(this.angleSpeed)*randSign();var h=rand(this.emitConeAngle,-this.emitConeAngle);const m=randColor(this.colorStartA,this.colorStartB,this.randomColorLinear),n=randColor(this.colorEndA,this.colorEndB,this.randomColorLinear);h=this.localSpace?h:this.angle+h;a=new Particle(a,this.tileInfo,b,m,n,e,f,g,this.fadeRate,this.additive,this.trailScale,this.localSpace&&this,this.particleDestroyCallback);a.velocity=vec2().setAngle(h,k);a.angleVelocity=d;a.fadeRate=this.fadeRate;a.damping=this.damping;a.angleDamping=this.angleDamping;a.elasticity=this.elasticity;a.friction=this.friction;a.gravityScale=this.gravityScale;a.collideTiles=this.collideTiles;a.renderOrder=this.renderOrder;a.mirror=!!randInt(2);this.particleCreateCallback&&this.particleCreateCallback(a);return a}render(){}}class Particle extends EngineObject{constructor(a,b,c,d,e,f,g,k,h,m,n,l,p){super(a,vec2(),b,c);this.colorStart=d;this.colorEndDelta=e.subtract(d);this.lifeTime=f;this.sizeStart=g;this.sizeEndDelta=k-g;this.fadeRate=h;this.additive=m;this.trailScale=n;this.localSpaceEmitter=l;this.destroyCallback=p;this.clampSpeedLinear=!1}render(){const a=0<this.lifeTime?min((time-this.spawnTime)/this.lifeTime,1):1,b=vec2(this.sizeStart+a*this.sizeEndDelta);var c=this.fadeRate/2;c=new Color(this.colorStart.r+a*this.colorEndDelta.r,this.colorStart.g+a*this.colorEndDelta.g,this.colorStart.b+a*this.colorEndDelta.b,(this.colorStart.a+a*this.colorEndDelta.a)*(a<c?a/c:a>1-c?(1-a)/c:1));this.additive&&setBlendMode(!0);let d=this.pos,e=this.angle;this.localSpaceEmitter&&(d=this.localSpaceEmitter.pos.add(d.rotate(-this.localSpaceEmitter.angle)),e+=this.localSpaceEmitter.angle);if(this.trailScale){var f=this.velocity;this.localSpaceEmitter&&(f=f.rotate(-this.localSpaceEmitter.angle));var g=f.length();g&&(f=f.scale(1/g),g*=this.trailScale,b.y=max(b.x,g),e=f.angle(),drawTile(d.add(f.multiply(vec2(0,-g/2))),b,this.tileInfo,c,e,this.mirror))}else drawTile(d,b,this.tileInfo,c,e,this.mirror);this.additive&&setBlendMode();debugParticles&&debugRect(d,b,"#f005",0,e);1==a&&(this.color=c,this.size=b,this.destroyCallback&&this.destroyCallback(this),this.destroyed=1)}}const medals={};let medalsDisplayQueue=[],medalsSaveName,medalsDisplayTimeLast;function medalsInit(a){medalsSaveName=a;debugMedals||medalsForEach(b=>b.unlocked=!!localStorage[b.storageKey()]);engineAddPlugin(void 0,function(){if(medalsDisplayQueue.length){var b=medalsDisplayQueue[0],c=timeReal-medalsDisplayTimeLast;if(medalsDisplayTimeLast)if(c>medalDisplayTime)medalsDisplayTimeLast=0,medalsDisplayQueue.shift();else{const d=medalDisplayTime-medalDisplaySlideTime;b.render(c<medalDisplaySlideTime?1-c/medalDisplaySlideTime:c>d?(c-d)/medalDisplaySlideTime:0)}else medalsDisplayTimeLast=timeReal}})}function medalsForEach(a){Object.values(medals).forEach(b=>a(b))}class Medal{constructor(a,b,c="",d="🏆",e){ASSERT(0<=a&&!medals[a]);this.id=a;this.name=b;this.description=c;this.icon=d;this.unlocked=!1;e&&((this.image=new Image).src=e);medals[a]=this}unlock(){medalsPreventUnlock||this.unlocked||(ASSERT(medalsSaveName,"save name must be set"),localStorage[this.storageKey()]=this.unlocked=!0,medalsDisplayQueue.push(this))}render(a=0){const b=overlayContext;var c=min(medalDisplaySize.x,mainCanvas.width);const d=overlayCanvas.width-c;a*=-medalDisplaySize.y;b.save();b.beginPath();b.fillStyle=new Color(.9,.9,.9).toString();b.strokeStyle=new Color(0,0,0).toString();b.lineWidth=3;b.rect(d,a,c,medalDisplaySize.y);b.fill();b.stroke();b.clip();this.renderIcon(vec2(d+15+medalDisplayIconSize/2,a+medalDisplaySize.y/2));c=vec2(d+medalDisplayIconSize+30,a+28);drawTextScreen(this.name,c,38,new Color(0,0,0),0,void 0,"left");c.y+=32;drawTextScreen(this.description,c,24,new Color(0,0,0),0,void 0,"left");b.restore()}renderIcon(a,b=medalDisplayIconSize){this.image?overlayContext.drawImage(this.image,a.x-b/2,a.y-b/2,b,b):drawTextScreen(this.icon,a,.7*b,new Color(0,0,0))}storageKey(){return medalsSaveName+"_"+this.id}}let glCanvas,glContext,glAntialias=!0,glShader,glActiveTexture,glArrayBuffer,glGeometryBuffer,glPositionData,glColorData,glInstanceCount,glAdditive,glBatchAdditive;const gl_MAX_INSTANCES=1e4,gl_INDICES_PER_INSTANCE=11,gl_INSTANCE_BYTE_STRIDE=4*gl_INDICES_PER_INSTANCE,gl_INSTANCE_BUFFER_SIZE=gl_MAX_INSTANCES*gl_INSTANCE_BYTE_STRIDE;function glInit(){if(glEnable&&!headlessMode){glCanvas=document.createElement("canvas");glContext=glCanvas.getContext("webgl2",{antialias:glAntialias});var a=mainCanvas.parentElement;glOverlay&&a.appendChild(glCanvas);glShader=glCreateProgram("#version 300 es\nprecision highp float;uniform mat4 m;in vec2 g;in vec4 p,u,c,a;in float r;out vec2 v;out vec4 d,e;void main(){vec2 s=(g-.5)*p.zw;gl_Position=m*vec4(p.xy+s*cos(r)-vec2(-s.y,s)*sin(r),1,1);v=mix(u.xw,u.zy,g);d=c;e=a;}","#version 300 es\nprecision highp float;uniform sampler2D s;in vec2 v;in vec4 d,e;out vec4 c;void main(){c=texture(s,v)*d+e;}");a=new ArrayBuffer(gl_INSTANCE_BUFFER_SIZE);glPositionData=new Float32Array(a);glColorData=new Uint32Array(a);glArrayBuffer=glContext.createBuffer();glGeometryBuffer=glContext.createBuffer();a=new Float32Array([glInstanceCount=0,0,1,0,0,1,1,1]);glContext.bindBuffer(glContext.ARRAY_BUFFER,glGeometryBuffer);glContext.bufferData(glContext.ARRAY_BUFFER,a,glContext.STATIC_DRAW)}}function glPreRender(){if(glEnable&&!headlessMode){glClearCanvas();glContext.useProgram(glShader);glContext.activeTexture(glContext.TEXTURE0);textureInfos[0]&&glContext.bindTexture(glContext.TEXTURE_2D,glActiveTexture=textureInfos[0].glTexture);var a=glAdditive=glBatchAdditive=0,b=(d,e,f,g)=>{d=glContext.getAttribLocation(glShader,d);const k=f&&gl_INSTANCE_BYTE_STRIDE,h=f&&1,m=1==f;glContext.enableVertexAttribArray(d);glContext.vertexAttribPointer(d,g,e,m,k,a);glContext.vertexAttribDivisor(d,h);a+=g*f};glContext.bindBuffer(glContext.ARRAY_BUFFER,glGeometryBuffer);b("g",glContext.FLOAT,0,2);glContext.bindBuffer(glContext.ARRAY_BUFFER,glArrayBuffer);glContext.bufferData(glContext.ARRAY_BUFFER,gl_INSTANCE_BUFFER_SIZE,glContext.DYNAMIC_DRAW);b("p",glContext.FLOAT,4,4);b("u",glContext.FLOAT,4,4);b("c",glContext.UNSIGNED_BYTE,1,4);b("a",glContext.UNSIGNED_BYTE,1,4);b("r",glContext.FLOAT,4,1);b=vec2(2*cameraScale).divide(mainCanvasSize);var c=vec2(-1).subtract(cameraPos.multiply(b));glContext.uniformMatrix4fv(glContext.getUniformLocation(glShader,"m"),!1,[b.x,0,0,0,0,b.y,0,0,1,1,1,1,c.x,c.y,0,0])}}function glClearCanvas(){glContext.viewport(0,0,glCanvas.width=mainCanvas.width,glCanvas.height=mainCanvas.height);glContext.clear(glContext.COLOR_BUFFER_BIT)}function glSetTexture(a){headlessMode||a==glActiveTexture||(glFlush(),glContext.bindTexture(glContext.TEXTURE_2D,glActiveTexture=a))}function glCompileShader(a,b){b=glContext.createShader(b);glContext.shaderSource(b,a);glContext.compileShader(b);if(debug&&!glContext.getShaderParameter(b,glContext.COMPILE_STATUS))throw glContext.getShaderInfoLog(b);return b}function glCreateProgram(a,b){const c=glContext.createProgram();glContext.attachShader(c,glCompileShader(a,glContext.VERTEX_SHADER));glContext.attachShader(c,glCompileShader(b,glContext.FRAGMENT_SHADER));glContext.linkProgram(c);if(debug&&!glContext.getProgramParameter(c,glContext.LINK_STATUS))throw glContext.getProgramInfoLog(c);return c}function glCreateTexture(a){const b=glContext.createTexture();glContext.bindTexture(glContext.TEXTURE_2D,b);a&&a.width?glContext.texImage2D(glContext.TEXTURE_2D,0,glContext.RGBA,glContext.RGBA,glContext.UNSIGNED_BYTE,a):(a=new Uint8Array([255,255,255,255]),glContext.texImage2D(glContext.TEXTURE_2D,0,glContext.RGBA,1,1,0,glContext.RGBA,glContext.UNSIGNED_BYTE,a));a=tilesPixelated?glContext.NEAREST:glContext.LINEAR;glContext.texParameteri(glContext.TEXTURE_2D,glContext.TEXTURE_MIN_FILTER,a);glContext.texParameteri(glContext.TEXTURE_2D,glContext.TEXTURE_MAG_FILTER,a);return b}function glFlush(){if(glInstanceCount){var a=glBatchAdditive?glContext.ONE:glContext.ONE_MINUS_SRC_ALPHA;glContext.blendFuncSeparate(glContext.SRC_ALPHA,a,glContext.ONE,a);glContext.enable(glContext.BLEND);glContext.bufferSubData(glContext.ARRAY_BUFFER,0,glPositionData);glContext.drawArraysInstanced(glContext.TRIANGLE_STRIP,0,4,glInstanceCount);showWatermark&&(drawCount+=glInstanceCount);glInstanceCount=0;glBatchAdditive=glAdditive}}function glCopyToContext(a,b=!1){glEnable&&(glInstanceCount||b)&&(glFlush(),glOverlay&&!b||a.drawImage(glCanvas,0,0))}function glSetAntialias(a=!0){ASSERT(!glCanvas,"must be called before engineInit");glAntialias=a}function glDraw(a,b,c,d,e,f,g,k,h,m,n=0){ASSERT("number"==typeof m&&"number"==typeof n,"invalid color");(glInstanceCount>=gl_MAX_INSTANCES||glBatchAdditive!=glAdditive)&&glFlush();let l=glInstanceCount++*gl_INDICES_PER_INSTANCE;glPositionData[l++]=a;glPositionData[l++]=b;glPositionData[l++]=c;glPositionData[l++]=d;glPositionData[l++]=f;glPositionData[l++]=g;glPositionData[l++]=k;glPositionData[l++]=h;glColorData[l++]=m;glColorData[l++]=n;glPositionData[l++]=e}const engineName="LittleJS",engineVersion="1.11.7",frameRate=60,timeDelta=1/frameRate;let engineObjects=[],engineObjectsCollide=[],frame=0,time=0,timeReal=0,paused=!1;function setPaused(a){paused=a}let frameTimeLastMS=0,frameTimeBufferMS=0,averageFPS=0;const pluginUpdateList=[],pluginRenderList=[];function engineAddPlugin(a,b){ASSERT(!pluginUpdateList.includes(a));ASSERT(!pluginRenderList.includes(b));a&&pluginUpdateList.push(a);b&&pluginRenderList.push(b)}function engineInit(a,b,c,d,e,f=[],g=document.body){function k(n=0){var l=n-frameTimeLastMS;frameTimeLastMS=n;if(debug||showWatermark)averageFPS=lerp(.05,averageFPS,1e3/(l||1));n=debug&&keyIsDown("Equal");const p=debug&&keyIsDown("Minus");debug&&(l*=n?5:p?.2:1);timeReal+=l/1e3;frameTimeBufferMS+=paused?0:l;n||(frameTimeBufferMS=min(frameTimeBufferMS,50));h();if(paused){for(const r of engineObjects)r.parent||r.updateTransforms();inputUpdate();pluginUpdateList.forEach(r=>r());debugUpdate();c();inputUpdatePost()}else{l=0;0>frameTimeBufferMS&&-9<frameTimeBufferMS&&(l=frameTimeBufferMS,frameTimeBufferMS=0);for(;0<=frameTimeBufferMS;frameTimeBufferMS-=1e3/frameRate)time=frame++/frameRate,inputUpdate(),b(),pluginUpdateList.forEach(r=>r()),engineObjectsUpdate(),debugUpdate(),c(),inputUpdatePost();frameTimeBufferMS+=l}if(!headlessMode){mainCanvasSize=vec2(mainCanvas.width,mainCanvas.height);overlayContext.imageSmoothingEnabled=mainContext.imageSmoothingEnabled=!tilesPixelated;glPreRender();d();engineObjects.sort((r,x)=>r.renderOrder-x.renderOrder);for(var q of engineObjects)q.destroyed||q.render();e();pluginRenderList.forEach(r=>r());touchGamepadRender();debugRender();glCopyToContext(mainContext);showWatermark&&(overlayContext.textAlign="right",overlayContext.textBaseline="top",overlayContext.font="1em monospace",overlayContext.fillStyle="#000",q=engineName+" v"+engineVersion+" / "+drawCount+" / "+engineObjects.length+" / "+averageFPS.toFixed(1)+(glEnable?" GL":" 2D"),overlayContext.fillText(q,mainCanvas.width-3,3),overlayContext.fillStyle="#fff",overlayContext.fillText(q,mainCanvas.width-2,2),drawCount=0)}requestAnimationFrame(k)}function h(){if(!headlessMode){if(canvasFixedSize.x){mainCanvas.width=canvasFixedSize.x;mainCanvas.height=canvasFixedSize.y;const n=innerWidth/innerHeight,l=mainCanvas.width/mainCanvas.height;(glCanvas||mainCanvas).style.width=mainCanvas.style.width=overlayCanvas.style.width=n<l?"100%":"";(glCanvas||mainCanvas).style.height=mainCanvas.style.height=overlayCanvas.style.height=n<l?"":"100%"}else mainCanvas.width=min(innerWidth,canvasMaxSize.x),mainCanvas.height=min(innerHeight,canvasMaxSize.y);overlayCanvas.width=mainCanvas.width;overlayCanvas.height=mainCanvas.height;mainCanvasSize=vec2(mainCanvas.width,mainCanvas.height)}}function m(){new Promise(n=>n(a())).then(k)}ASSERT(!mainContext,"engine already initialized");ASSERT(Array.isArray(f),"pass in images as array");a||=()=>{};b||=()=>{};c||=()=>{};d||=()=>{};e||=()=>{};headlessMode?m():(g.style.cssText="margin:0;overflow:hidden;width:100vw;height:100vh;display:flex;align-items:center;justify-content:center;background:#000;"+(canvasPixelated?"image-rendering:pixelated;":"")+"user-select:none;-webkit-user-select:none;"+(touchInputEnable?"touch-action:none;-webkit-touch-callout:none":""),g.appendChild(mainCanvas=document.createElement("canvas")),mainContext=mainCanvas.getContext("2d"),inputInit(),audioInit(),debugInit(),glInit(),g.appendChild(overlayCanvas=document.createElement("canvas")),overlayContext=overlayCanvas.getContext("2d"),mainCanvas.style.cssText=overlayCanvas.style.cssText="position:absolute",glCanvas&&(glCanvas.style.cssText="position:absolute"),h(),g=f.map((n,l)=>new Promise(p=>{const q=new Image;q.crossOrigin="anonymous";q.onerror=q.onload=()=>{textureInfos[l]=new TextureInfo(q);p()};q.src=n})),f.length||g.push(new Promise(n=>{textureInfos[0]=new TextureInfo(new Image);n()})),showSplashScreen&&g.push(new Promise(n=>{function l(){clearInput();drawEngineSplashScreen(p+=.01);1<p?n():setTimeout(l,16)}let p=0;console.log(`${engineName} Engine v${engineVersion}`);l()})),Promise.all(g).then(m))}function engineObjectsUpdate(){function a(b){if(!b.destroyed){b.update();for(const c of b.children)a(c)}}engineObjectsCollide=engineObjects.filter(b=>b.collideSolidObjects);for(const b of engineObjects)b.parent||(a(b),b.updateTransforms());engineObjects=engineObjects.filter(b=>!b.destroyed)}function engineObjectsDestroy(){for(const a of engineObjects)a.parent||a.destroy();engineObjects=engineObjects.filter(a=>!a.destroyed)}function engineObjectsCollect(a,b,c=engineObjects){const d=[];if(a)if(b instanceof Vector2)for(const e of c)isOverlapping(a,b,e.pos,e.size)&&d.push(e);else{b*=b;for(const e of c)a.distanceSquared(e.pos)<b&&d.push(e)}else for(const e of c)d.push(e);return d}function engineObjectsCallback(a,b,c,d=engineObjects){engineObjectsCollect(a,b,d).forEach(e=>c(e))}function engineObjectsRaycast(a,b,c=engineObjects){const d=[];for(const e of c)e.collideRaycast&&isIntersecting(a,b,e.pos,e.size)&&(debugRaycast&&debugRect(e.pos,e.size,"#f00"),d.push(e));debugRaycast&&debugLine(a,b,d.length?"#f00":"#00f",.02);return d}function drawEngineSplashScreen(a){const b=overlayContext;var c=overlayCanvas.width=innerWidth,d=overlayCanvas.height=innerHeight,e=percent(a,1,.8),f=percent(a,0,.5),g=b.createRadialGradient(c/2,d/2,0,c/2,d/2,.7*Math.hypot(c,d));g.addColorStop(0,hsl(0,0,lerp(f,0,e/2),e).toString());g.addColorStop(1,hsl(0,0,0,e).toString());b.save();b.fillStyle=g;b.fillRect(0,0,c,d);g=(h,m,n,l,p)=>{b.beginPath();b.rect(h,m,n,p?l*k:l);(b.fillStyle=p)?b.fill():b.stroke()};f=(h,m,n,l=0,p=2*PI,q,r)=>{const x=(l+p)/2;l=k*(p-l)/2;b.beginPath();r&&b.lineTo(h,m);b.arc(h,m,n,x-l,x+l);(b.fillStyle=q)?b.fill():b.stroke()};e=(h=0,m=0)=>hsl([.98,.3,.57,.14][h%4]-10,.8,[0,.3,.5,.8,.9][m]).toString();a=wave(1,1,a);const k=percent(a,.1,.5);b.translate(c/2,d/2);c=min(6,min(c,d)/99);b.scale(c,c);b.translate(-40,-35);b.lineJoin=b.lineCap="round";b.lineWidth=.1+1.9*k;c=percent(a,.1,1);b.setLineDash([99*c,99]);g(7,16,18,-8,e(2,2));g(7,8,18,4,e(2,3));g(25,8,8,8,e(2,1));g(25,8,-18,8);g(25,8,8,8);g(25,16,7,23,e());g(11,39,14,-23,e(1,1));g(11,16,14,18,e(1,2));g(11,16,14,8,e(1,3));g(25,16,-14,24);g(15,29,6,-9,e(2,2));f(15,21,5,0,PI/2,e(2,4),1);g(21,21,-6,9);g(37,14,9,6,e(3,2));g(37,14,4.5,6,e(3,3));g(37,14,9,6);g(50,20,10,-8,e(0,1));g(50,20,6.5,-8,e(0,2));g(50,20,3.5,-8,e(0,3));g(50,20,10,-8);f(55,2,11.4,.5,PI-.5,e(3,3));f(55,2,11.4,.5,PI/2,e(3,2),1);f(55,2,11.4,.5,PI-.5);g(45,7,20,-7,e(0,2));g(45,-1,20,4,e(0,3));g(45,-1,20,8);for(c=5;c--;)f(60-6*c,30,9.9,0,2*PI,e(c+2,3)),f(60-6*c,30,10,-.5,PI+.5,e(c+2,2)),f(60-6*c,30,10.1,.5,PI-.5,e(c+2,1));f(36,30,10,PI/2,3*PI/2);f(48,30,10,PI/2,3*PI/2);f(60,30,10);b.beginPath();b.lineTo(36,20);b.lineTo(60,20);b.stroke();f(60,30,4,PI,3*PI,e(3,2));f(60,30,4,PI,2*PI,e(3,3));f(60,30,4,PI,3*PI);for(c=6;c--;)b.beginPath(),b.lineTo(53,54),b.lineTo(53,40),b.lineTo(53+(1+2.9*c)*k,40),b.lineTo(53+(4+3.5*c)*k,54),b.fillStyle=e(0,c%2+2),b.fill(),c%2&&b.stroke();g(6,40,5,5);g(6,40,5,5,e());g(15,54,38,-14,e());for(g=3;g--;)for(c=2;c--;)f(15*g+15,47,c?7:1,PI,3*PI,e(g,3)),b.stroke(),f(15*g+15,47,c?7:1,0,PI,e(g,2)),b.stroke();b.beginPath();b.lineTo(6,40);b.lineTo(68,40);b.stroke();b.beginPath();b.lineTo(77,54);b.lineTo(4,54);b.stroke();f=engineName;b.font="900 16px arial";b.textAlign="center";b.textBaseline="top";b.lineWidth=.1+3.9*k;g=0;for(c=0;c<f.length;++c)g+=b.measureText(f[c]).width;for(c=2;c--;)for(let h=0,m=41-g/2;h<f.length;++h)b.fillStyle=e(h,2),d=b.measureText(f[h]).width,b[c?"strokeText":"fillText"](f[h],m+d/2,55.5,17*k),m+=d;b.restore()}
|
package/dist/littlejs.release.js
CHANGED
|
@@ -34,6 +34,7 @@ function debugLine (){}
|
|
|
34
34
|
function debugOverlap (){}
|
|
35
35
|
function debugText (){}
|
|
36
36
|
function debugClear (){}
|
|
37
|
+
function debugScreenshot (){}
|
|
37
38
|
function debugSaveCanvas (){}
|
|
38
39
|
function debugSaveText (){}
|
|
39
40
|
function debugSaveDataURL(){}
|
|
@@ -2574,6 +2575,15 @@ function keyWasReleased(key, device=0)
|
|
|
2574
2575
|
return inputData[device] && !!(inputData[device][key] & 4);
|
|
2575
2576
|
}
|
|
2576
2577
|
|
|
2578
|
+
/** Returns input vector from arrow keys or WASD if enabled
|
|
2579
|
+
* @return {Vector2}
|
|
2580
|
+
* @memberof Input */
|
|
2581
|
+
function keyDirection(up='ArrowUp', down='ArrowDown', left='ArrowLeft', right='ArrowRight')
|
|
2582
|
+
{
|
|
2583
|
+
const k = (key)=> keyIsDown(key) ? 1 : 0;
|
|
2584
|
+
return vec2(k(right) - k(left), k(up) - k(down));
|
|
2585
|
+
}
|
|
2586
|
+
|
|
2577
2587
|
/** Clears all input
|
|
2578
2588
|
* @memberof Input */
|
|
2579
2589
|
function clearInput() { inputData = [[]]; touchGamepadButtons = []; }
|
|
@@ -2657,9 +2667,9 @@ function gamepadStick(stick, gamepad=0)
|
|
|
2657
2667
|
{ return gamepadStickData[gamepad] ? gamepadStickData[gamepad][stick] || vec2() : vec2(); }
|
|
2658
2668
|
|
|
2659
2669
|
///////////////////////////////////////////////////////////////////////////////
|
|
2660
|
-
// Input
|
|
2670
|
+
// Input system functions called automatically by engine
|
|
2661
2671
|
|
|
2662
|
-
//
|
|
2672
|
+
// input is stored as a bit field for each key: 1 = isDown, 2 = wasPressed, 4 = wasReleased
|
|
2663
2673
|
// mouse and keyboard are stored together in device 0, gamepads are in devices > 0
|
|
2664
2674
|
let inputData = [[]];
|
|
2665
2675
|
|
|
@@ -2689,9 +2699,6 @@ function inputUpdatePost()
|
|
|
2689
2699
|
mouseWheel = 0;
|
|
2690
2700
|
}
|
|
2691
2701
|
|
|
2692
|
-
///////////////////////////////////////////////////////////////////////////////
|
|
2693
|
-
// Input event handlers
|
|
2694
|
-
|
|
2695
2702
|
function inputInit()
|
|
2696
2703
|
{
|
|
2697
2704
|
if (headlessMode) return;
|
|
@@ -2734,11 +2741,11 @@ function inputInit()
|
|
|
2734
2741
|
|
|
2735
2742
|
isUsingGamepad = false;
|
|
2736
2743
|
inputData[0][e.button] = 3;
|
|
2737
|
-
mousePosScreen =
|
|
2744
|
+
mousePosScreen = mouseEventToScreen(e);
|
|
2738
2745
|
e.button && e.preventDefault();
|
|
2739
2746
|
}
|
|
2740
2747
|
onmouseup = (e)=> inputData[0][e.button] = inputData[0][e.button] & 2 | 4;
|
|
2741
|
-
onmousemove = (e)=> mousePosScreen =
|
|
2748
|
+
onmousemove = (e)=> mousePosScreen = mouseEventToScreen(e);
|
|
2742
2749
|
onwheel = (e)=> mouseWheel = e.ctrlKey ? 0 : sign(e.deltaY);
|
|
2743
2750
|
oncontextmenu = (e)=> false; // prevent right click menu
|
|
2744
2751
|
onblur = (e) => clearInput(); // reset input when focus is lost
|
|
@@ -2749,14 +2756,12 @@ function inputInit()
|
|
|
2749
2756
|
}
|
|
2750
2757
|
|
|
2751
2758
|
// convert a mouse or touch event position to screen space
|
|
2752
|
-
function
|
|
2759
|
+
function mouseEventToScreen(mousePos)
|
|
2753
2760
|
{
|
|
2754
|
-
if (!mainCanvas || headlessMode)
|
|
2755
|
-
return vec2(); // fix bug that can occur if user clicks before page loads
|
|
2756
|
-
|
|
2757
2761
|
const rect = mainCanvas.getBoundingClientRect();
|
|
2758
|
-
|
|
2759
|
-
|
|
2762
|
+
const px = percent(mousePos.x, rect.left, rect.right);
|
|
2763
|
+
const py = percent(mousePos.y, rect.top, rect.bottom);
|
|
2764
|
+
return vec2(px*mainCanvas.width, py*mainCanvas.height);
|
|
2760
2765
|
}
|
|
2761
2766
|
|
|
2762
2767
|
///////////////////////////////////////////////////////////////////////////////
|
|
@@ -2911,7 +2916,7 @@ function touchInputInit()
|
|
|
2911
2916
|
{
|
|
2912
2917
|
// set event pos and pass it along
|
|
2913
2918
|
const p = vec2(e.touches[0].clientX, e.touches[0].clientY);
|
|
2914
|
-
mousePosScreen =
|
|
2919
|
+
mousePosScreen = mouseEventToScreen(p);
|
|
2915
2920
|
wasTouching ? isUsingGamepad = touchGamepadEnable : inputData[0][button] = 3;
|
|
2916
2921
|
}
|
|
2917
2922
|
else if (wasTouching)
|
|
@@ -2959,7 +2964,7 @@ function touchInputInit()
|
|
|
2959
2964
|
// check each touch point
|
|
2960
2965
|
for (const touch of e.touches)
|
|
2961
2966
|
{
|
|
2962
|
-
const touchPos =
|
|
2967
|
+
const touchPos = mouseEventToScreen(vec2(touch.clientX, touch.clientY));
|
|
2963
2968
|
if (touchPos.distance(stickCenter) < touchGamepadSize)
|
|
2964
2969
|
{
|
|
2965
2970
|
// virtual analog stick
|
|
@@ -3876,10 +3881,10 @@ class TileLayer extends EngineObject
|
|
|
3876
3881
|
!glOverlay && !this.isOverlay && glCopyToContext(mainContext);
|
|
3877
3882
|
|
|
3878
3883
|
// draw the entire cached level onto the canvas
|
|
3879
|
-
|
|
3884
|
+
let pos = worldToScreen(this.pos.add(vec2(0,this.size.y*this.scale.y)));
|
|
3880
3885
|
|
|
3881
3886
|
// fix canvas jitter in some browsers if position is not an integer
|
|
3882
|
-
pos
|
|
3887
|
+
pos = pos.floor();
|
|
3883
3888
|
|
|
3884
3889
|
(this.isOverlay ? overlayContext : mainContext).drawImage
|
|
3885
3890
|
(
|
|
@@ -4872,7 +4877,7 @@ const engineName = 'LittleJS';
|
|
|
4872
4877
|
* @type {String}
|
|
4873
4878
|
* @default
|
|
4874
4879
|
* @memberof Engine */
|
|
4875
|
-
const engineVersion = '1.11.
|
|
4880
|
+
const engineVersion = '1.11.7';
|
|
4876
4881
|
|
|
4877
4882
|
/** Frames per second to update
|
|
4878
4883
|
* @type {Number}
|
package/examples/box2d/game.js
CHANGED
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
</head><body>
|
|
8
8
|
|
|
9
9
|
<script src=../../dist/littlejs.js></script>
|
|
10
|
-
<script src=../../plugins/Box2D_v2.3.1_min.wasm.js?
|
|
11
|
-
<script src=../../plugins/box2d.js?
|
|
12
|
-
<script src=scenes.js?
|
|
13
|
-
<script src=gameObjects.js?
|
|
14
|
-
<script src=game.js?
|
|
10
|
+
<script src=../../plugins/Box2D_v2.3.1_min.wasm.js?1117></script>
|
|
11
|
+
<script src=../../plugins/box2d.js?1117></script>
|
|
12
|
+
<script src=scenes.js?1117></script>
|
|
13
|
+
<script src=gameObjects.js?1117></script>
|
|
14
|
+
<script src=game.js?1117></script>
|
|
@@ -21,9 +21,10 @@ const sound_bounce = new Sound([,,1e3,,.03,.02,1,2,,,940,.03,,,,,.2,.6,,.06]);
|
|
|
21
21
|
///////////////////////////////////////////////////////////////////////////////
|
|
22
22
|
function gameInit()
|
|
23
23
|
{
|
|
24
|
-
canvasFixedSize = vec2(
|
|
24
|
+
canvasFixedSize = vec2(1920, 1080); // 1080p
|
|
25
25
|
levelSize = vec2(38, 20);
|
|
26
26
|
cameraPos = levelSize.scale(.5);
|
|
27
|
+
cameraScale = 48;
|
|
27
28
|
paddle = new Paddle(vec2(levelSize.x/2-12, 1));
|
|
28
29
|
score = brickCount = 0;
|
|
29
30
|
|
|
@@ -34,9 +35,9 @@ function gameInit()
|
|
|
34
35
|
new Brick(pos);
|
|
35
36
|
|
|
36
37
|
// create walls
|
|
37
|
-
new Wall(vec2(-.5,levelSize.y/2), vec2(1,100)) // top
|
|
38
|
-
new Wall(vec2(levelSize.x+.5,levelSize.y/2), vec2(1,100)) // left
|
|
39
|
-
new Wall(vec2(levelSize.x/2,levelSize.y+.5), vec2(100,1)) // right
|
|
38
|
+
new Wall(vec2(-.5,levelSize.y/2), vec2(1,100)); // top
|
|
39
|
+
new Wall(vec2(levelSize.x+.5,levelSize.y/2), vec2(1,100)); // left
|
|
40
|
+
new Wall(vec2(levelSize.x/2,levelSize.y+.5), vec2(100,1)); // right
|
|
40
41
|
|
|
41
42
|
setupPostProcess(); // set up a post processing shader
|
|
42
43
|
}
|
|
@@ -71,7 +72,7 @@ function gameRenderPost()
|
|
|
71
72
|
{
|
|
72
73
|
// use built in image font for text
|
|
73
74
|
const font = new FontImage;
|
|
74
|
-
font.drawText('Score: ' + score, cameraPos.add(vec2(0,9.
|
|
75
|
+
font.drawText('Score: ' + score, cameraPos.add(vec2(0,9.7)), .15, true);
|
|
75
76
|
if (!brickCount)
|
|
76
77
|
font.drawText('You Win!', cameraPos.add(vec2(0,-5)), .2, true);
|
|
77
78
|
else if (!ball)
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
<link rel=icon type=image/png href=../favicon.png>
|
|
7
7
|
</head><body>
|
|
8
8
|
|
|
9
|
-
<script src=../../dist/littlejs.js?
|
|
10
|
-
<script src=../../plugins/postProcess.js?
|
|
11
|
-
<script src=gameObjects.js?
|
|
12
|
-
<script src=game.js?
|
|
9
|
+
<script src=../../dist/littlejs.js?1117></script>
|
|
10
|
+
<script src=../../plugins/postProcess.js?1117></script>
|
|
11
|
+
<script src=gameObjects.js?1117></script>
|
|
12
|
+
<script src=game.js?1117></script>
|