littlejsengine 1.8.8 → 1.8.9
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/build/littlejs.d.ts +9 -9
- package/build/littlejs.esm.js +107 -90
- package/build/littlejs.esm.min.js +1 -1
- package/build/littlejs.js +107 -90
- package/build/littlejs.min.js +1 -1
- package/build/littlejs.release.js +107 -90
- package/examples/logo.png +0 -0
- package/examples/platformer/gameEffects.js +1 -1
- package/examples/starter/game.js +1 -1
- package/examples/starter/tiles.png +0 -0
- package/package.json +1 -1
- package/src/engine.js +28 -25
- package/src/engineAudio.js +72 -58
- package/src/engineParticles.js +7 -7
package/build/littlejs.js
CHANGED
|
@@ -3196,7 +3196,7 @@ function playSamples(sampleChannels, volume=1, rate=1, pan=0, loop=0, sampleRate
|
|
|
3196
3196
|
}
|
|
3197
3197
|
|
|
3198
3198
|
///////////////////////////////////////////////////////////////////////////////
|
|
3199
|
-
// ZzFXMicro - Zuper Zmall Zound Zynth - v1.
|
|
3199
|
+
// ZzFXMicro - Zuper Zmall Zound Zynth - v1.3.0 by Frank Force
|
|
3200
3200
|
|
|
3201
3201
|
/** Generate and play a ZzFX sound
|
|
3202
3202
|
*
|
|
@@ -3232,6 +3232,7 @@ const zzfxR = 44100;
|
|
|
3232
3232
|
* @param {Number} [sustainVolume=1] - Volume level for sustain (percent)
|
|
3233
3233
|
* @param {Number} [decay=0] - Decay time, how long to reach sustain after attack (seconds)
|
|
3234
3234
|
* @param {Number} [tremolo=0] - Trembling effect, rate controlled by repeat time (precent)
|
|
3235
|
+
* @param {Number} [filter=0] - Filter cutoff frequency, positive for HPF, negative for LPF (Hz)
|
|
3235
3236
|
* @return {Array} - Array of audio samples
|
|
3236
3237
|
* @memberof Audio
|
|
3237
3238
|
*/
|
|
@@ -3241,78 +3242,91 @@ function zzfxG
|
|
|
3241
3242
|
volume = 1, randomness = .05, frequency = 220, attack = 0, sustain = 0,
|
|
3242
3243
|
release = .1, shape = 0, shapeCurve = 1, slide = 0, deltaSlide = 0,
|
|
3243
3244
|
pitchJump = 0, pitchJumpTime = 0, repeatTime = 0, noise = 0, modulation = 0,
|
|
3244
|
-
bitCrush = 0, delay = 0, sustainVolume = 1, decay = 0, tremolo = 0
|
|
3245
|
+
bitCrush = 0, delay = 0, sustainVolume = 1, decay = 0, tremolo = 0, filter = 0
|
|
3245
3246
|
)
|
|
3246
3247
|
{
|
|
3247
|
-
//
|
|
3248
|
-
let PI2 = PI*2,
|
|
3249
|
-
|
|
3250
|
-
|
|
3248
|
+
// init parameters
|
|
3249
|
+
let PI2 = PI*2, sampleRate = zzfxR,
|
|
3250
|
+
startSlide = slide *= 500 * PI2 / sampleRate / sampleRate,
|
|
3251
|
+
startFrequency = frequency *=
|
|
3252
|
+
rand(1 + randomness, 1-randomness) * PI2 / sampleRate,
|
|
3253
|
+
b = [], t = 0, tm = 0, i = 0, j = 1, r = 0, c = 0, s = 0, f, length,
|
|
3254
|
+
|
|
3255
|
+
// biquad LP/HP filter
|
|
3256
|
+
quality = 2, w = PI2 * abs(filter) * 2 / sampleRate,
|
|
3257
|
+
cos = Math.cos(w), alpha = Math.sin(w) / 2 / quality,
|
|
3258
|
+
a0 = 1 + alpha, a1 = -2*cos / a0, a2 = (1 - alpha) / a0,
|
|
3259
|
+
b0 = (1 + sign(filter) * cos) / 2 / a0,
|
|
3260
|
+
b1 = -(sign(filter) + cos) / a0, b2 = b0,
|
|
3261
|
+
x2 = 0, x1 = 0, y2 = 0, y1 = 0;
|
|
3251
3262
|
|
|
3252
3263
|
// scale by sample rate
|
|
3253
|
-
attack = attack *
|
|
3254
|
-
decay *=
|
|
3255
|
-
sustain *=
|
|
3256
|
-
release *=
|
|
3257
|
-
delay *=
|
|
3258
|
-
deltaSlide *= 500 * PI2 /
|
|
3259
|
-
modulation *= PI2 /
|
|
3260
|
-
pitchJump *= PI2 /
|
|
3261
|
-
pitchJumpTime *=
|
|
3262
|
-
repeatTime = repeatTime *
|
|
3264
|
+
attack = attack * sampleRate + 9; // minimum attack to prevent pop
|
|
3265
|
+
decay *= sampleRate;
|
|
3266
|
+
sustain *= sampleRate;
|
|
3267
|
+
release *= sampleRate;
|
|
3268
|
+
delay *= sampleRate;
|
|
3269
|
+
deltaSlide *= 500 * PI2 / sampleRate**3;
|
|
3270
|
+
modulation *= PI2 / sampleRate;
|
|
3271
|
+
pitchJump *= PI2 / sampleRate;
|
|
3272
|
+
pitchJumpTime *= sampleRate;
|
|
3273
|
+
repeatTime = repeatTime * sampleRate | 0;
|
|
3274
|
+
volume *= soundVolume;
|
|
3263
3275
|
|
|
3264
3276
|
// generate waveform
|
|
3265
|
-
for
|
|
3266
|
-
i < length; b[i++] = s)
|
|
3277
|
+
for(length = attack + decay + sustain + release + delay | 0;
|
|
3278
|
+
i < length; b[i++] = s * volume) // sample
|
|
3267
3279
|
{
|
|
3268
|
-
if (!(++c%(bitCrush*100|0)))
|
|
3280
|
+
if (!(++c%(bitCrush*100|0))) // bit crush
|
|
3269
3281
|
{
|
|
3270
|
-
s = shape? shape>1? shape>2? shape>3?
|
|
3271
|
-
Math.sin(
|
|
3272
|
-
|
|
3273
|
-
1-(2*t/PI2%2+2)%2:
|
|
3274
|
-
1-4*abs(Math.round(t/PI2)-t/PI2):
|
|
3275
|
-
Math.sin(t);
|
|
3276
|
-
|
|
3282
|
+
s = shape? shape>1? shape>2? shape>3? // wave shape
|
|
3283
|
+
Math.sin(t*t) : // 4 noise
|
|
3284
|
+
clamp(Math.tan(t),1,-1): // 3 tan
|
|
3285
|
+
1-(2*t/PI2%2+2)%2: // 2 saw
|
|
3286
|
+
1-4*abs(Math.round(t/PI2)-t/PI2): // 1 triangle
|
|
3287
|
+
Math.sin(t); // 0 sin
|
|
3288
|
+
|
|
3277
3289
|
s = (repeatTime ?
|
|
3278
3290
|
1 - tremolo + tremolo*Math.sin(PI2*i/repeatTime) // tremolo
|
|
3279
3291
|
: 1) *
|
|
3280
|
-
sign(s)*(abs(s)**shapeCurve) *
|
|
3281
|
-
|
|
3282
|
-
i < attack
|
|
3283
|
-
i
|
|
3284
|
-
|
|
3285
|
-
|
|
3286
|
-
|
|
3287
|
-
|
|
3288
|
-
|
|
3289
|
-
|
|
3290
|
-
|
|
3291
|
-
|
|
3292
|
-
|
|
3293
|
-
|
|
3294
|
-
|
|
3295
|
-
|
|
3296
|
-
|
|
3297
|
-
f = (frequency += slide += deltaSlide) * // frequency
|
|
3298
|
-
Math.cos(modulation*tm++); // modulation
|
|
3299
|
-
t += f - f*noise*(1 - (Math.sin(i)+1)*1e9%2); // noise
|
|
3300
|
-
|
|
3301
|
-
if (j && ++j > pitchJumpTime) // pitch jump
|
|
3302
|
-
{
|
|
3303
|
-
frequency += pitchJump; // apply pitch jump
|
|
3304
|
-
startFrequency += pitchJump; // also apply to start
|
|
3305
|
-
j = 0; // reset pitch jump time
|
|
3292
|
+
sign(s)*(abs(s)**shapeCurve) * // curve
|
|
3293
|
+
(i < attack ? i/attack : // attack
|
|
3294
|
+
i < attack + decay ? // decay
|
|
3295
|
+
1-((i-attack)/decay)*(1-sustainVolume) : // decay falloff
|
|
3296
|
+
i < attack + decay + sustain ? // sustain
|
|
3297
|
+
sustainVolume : // sustain volume
|
|
3298
|
+
i < length - delay ? // release
|
|
3299
|
+
(length - i - delay)/release * // release falloff
|
|
3300
|
+
sustainVolume : // release volume
|
|
3301
|
+
0); // post release
|
|
3302
|
+
|
|
3303
|
+
s = delay ? s/2 + (delay > i ? 0 : // delay
|
|
3304
|
+
(i<length-delay? 1 : (length-i)/delay) * // release delay
|
|
3305
|
+
b[i-delay|0]/2/volume) : s; // sample delay
|
|
3306
|
+
|
|
3307
|
+
if (filter) // apply filter
|
|
3308
|
+
s = y1 = b2*x2 + b1*(x2=x1) + b0*(x1=s) - a2*y2 - a1*(y2=y1);
|
|
3306
3309
|
}
|
|
3307
3310
|
|
|
3308
|
-
|
|
3309
|
-
|
|
3310
|
-
|
|
3311
|
-
|
|
3312
|
-
|
|
3311
|
+
f = (frequency += slide += deltaSlide) *// frequency
|
|
3312
|
+
Math.cos(modulation*tm++); // modulation
|
|
3313
|
+
t += f + f*noise*Math.sin(i**5); // noise
|
|
3314
|
+
|
|
3315
|
+
if (j && ++j > pitchJumpTime) // pitch jump
|
|
3316
|
+
{
|
|
3317
|
+
frequency += pitchJump; // apply pitch jump
|
|
3318
|
+
startFrequency += pitchJump; // also apply to start
|
|
3319
|
+
j = 0; // stop pitch jump time
|
|
3320
|
+
}
|
|
3321
|
+
|
|
3322
|
+
if (repeatTime && !(++r % repeatTime)) // repeat
|
|
3323
|
+
{
|
|
3324
|
+
frequency = startFrequency; // reset frequency
|
|
3325
|
+
slide = startSlide; // reset slide
|
|
3326
|
+
j = j || 1; // reset pitch jump time
|
|
3313
3327
|
}
|
|
3314
3328
|
}
|
|
3315
|
-
|
|
3329
|
+
|
|
3316
3330
|
return b;
|
|
3317
3331
|
}
|
|
3318
3332
|
|
|
@@ -3808,13 +3822,13 @@ constructor(pos, size=tileCollisionSize, tileInfo=tile(), scale=vec2(1), renderO
|
|
|
3808
3822
|
class ParticleEmitter extends EngineObject
|
|
3809
3823
|
{
|
|
3810
3824
|
/** Create a particle system with the given settings
|
|
3811
|
-
* @param {Vector2} position
|
|
3812
|
-
* @param {Number} [angle=0]
|
|
3813
|
-
* @param {Number} [emitSize=0]
|
|
3814
|
-
* @param {Number} [emitTime=0]
|
|
3815
|
-
* @param {Number} [emitRate=100]
|
|
3825
|
+
* @param {Vector2} position - World space position of the emitter
|
|
3826
|
+
* @param {Number} [angle=0] - Angle to emit the particles
|
|
3827
|
+
* @param {Number|Vector2} [emitSize=0] - World space size of the emitter (float for circle diameter, vec2 for rect)
|
|
3828
|
+
* @param {Number} [emitTime=0] - How long to stay alive (0 is forever)
|
|
3829
|
+
* @param {Number} [emitRate=100] - How many particles per second to spawn, does not emit if 0
|
|
3816
3830
|
* @param {Number} [emitConeAngle=PI] - Local angle to apply velocity to particles from emitter
|
|
3817
|
-
* @param {TileInfo} [tileInfo]
|
|
3831
|
+
* @param {TileInfo} [tileInfo] - Tile info to render particles (undefined is untextured)
|
|
3818
3832
|
* @param {Color} [colorStartA=Color()] - Color at start of life 1, randomized between start colors
|
|
3819
3833
|
* @param {Color} [colorStartB=Color()] - Color at start of life 2, randomized between start colors
|
|
3820
3834
|
* @param {Color} [colorEndA=Color(1,1,1,0)] - Color at end of life 1, randomized between end colors
|
|
@@ -3870,7 +3884,7 @@ class ParticleEmitter extends EngineObject
|
|
|
3870
3884
|
super(pos, vec2(), tileInfo, angle, undefined, renderOrder);
|
|
3871
3885
|
|
|
3872
3886
|
// emitter settings
|
|
3873
|
-
/** @property {Number} - World space size of the emitter (float for circle diameter, vec2 for rect) */
|
|
3887
|
+
/** @property {Number|Vector2} - World space size of the emitter (float for circle diameter, vec2 for rect) */
|
|
3874
3888
|
this.emitSize = emitSize
|
|
3875
3889
|
/** @property {Number} - How long to stay alive (0 is forever) */
|
|
3876
3890
|
this.emitTime = emitTime;
|
|
@@ -4833,7 +4847,7 @@ const engineName = 'LittleJS';
|
|
|
4833
4847
|
* @type {String}
|
|
4834
4848
|
* @default
|
|
4835
4849
|
* @memberof Engine */
|
|
4836
|
-
const engineVersion = '1.8.
|
|
4850
|
+
const engineVersion = '1.8.9';
|
|
4837
4851
|
|
|
4838
4852
|
/** Frames per second to update objects
|
|
4839
4853
|
* @type {Number}
|
|
@@ -5155,21 +5169,23 @@ function engineObjectsCallback(pos, size, callbackFunction, objects=engineObject
|
|
|
5155
5169
|
|
|
5156
5170
|
function drawEngineSplashScreen(t)
|
|
5157
5171
|
{
|
|
5158
|
-
// background
|
|
5159
|
-
const grayscale = 0;
|
|
5160
5172
|
const x = mainContext;
|
|
5161
5173
|
const w = mainCanvas.width = innerWidth;
|
|
5162
5174
|
const h = mainCanvas.height = innerHeight;
|
|
5163
|
-
|
|
5164
|
-
|
|
5165
|
-
|
|
5166
|
-
|
|
5167
|
-
|
|
5168
|
-
|
|
5169
|
-
|
|
5170
|
-
|
|
5171
|
-
|
|
5172
|
-
|
|
5175
|
+
{
|
|
5176
|
+
// background
|
|
5177
|
+
const p3 = percent(t, 1, .8);
|
|
5178
|
+
const p4 = percent(t, 0, .5);
|
|
5179
|
+
const g = x.createRadialGradient(w/2,h/2,0,w/2,h/2,Math.hypot(w,h)*.7);
|
|
5180
|
+
g.addColorStop(0,hsl(0,0,lerp(p4,0,p3/2),p3));
|
|
5181
|
+
g.addColorStop(1,hsl(0,0,0,p3));
|
|
5182
|
+
x.save();
|
|
5183
|
+
x.fillStyle = g;
|
|
5184
|
+
x.fillRect(0,0,w,h);
|
|
5185
|
+
}
|
|
5186
|
+
|
|
5187
|
+
// draw LittleJS logo...
|
|
5188
|
+
|
|
5173
5189
|
const rect = (X, Y, W, H, C)=>
|
|
5174
5190
|
{
|
|
5175
5191
|
x.beginPath();
|
|
@@ -5194,7 +5210,7 @@ function drawEngineSplashScreen(t)
|
|
|
5194
5210
|
C ? x.fill() : x.stroke();
|
|
5195
5211
|
};
|
|
5196
5212
|
const color = (c=0, l=0) =>
|
|
5197
|
-
hsl([.98,.3,.57,.14][c%4]-10
|
|
5213
|
+
hsl([.98,.3,.57,.14][c%4]-10,.8,[0,.3,.5,.8,.9][l]);
|
|
5198
5214
|
const alpha = wave(1,1,t);
|
|
5199
5215
|
const p = percent(alpha, .1, .5);
|
|
5200
5216
|
|
|
@@ -5237,9 +5253,9 @@ function drawEngineSplashScreen(t)
|
|
|
5237
5253
|
rect(50,20,6,-10,color(0,2));
|
|
5238
5254
|
rect(50,20,3,-10,color(0,3));
|
|
5239
5255
|
rect(50,10,10,10);
|
|
5240
|
-
circle(55,2,11,.5,PI-.5,color(3,3));
|
|
5241
|
-
circle(55,2,11,.5,PI/2,color(3,2),1);
|
|
5242
|
-
circle(55,2,11,.5,PI-.5);
|
|
5256
|
+
circle(55,2,11.4,.5,PI-.5,color(3,3));
|
|
5257
|
+
circle(55,2,11.4,.5,PI/2,color(3,2),1);
|
|
5258
|
+
circle(55,2,11.4,.5,PI-.5);
|
|
5243
5259
|
rect(45,7,20,-7,color(0,2));
|
|
5244
5260
|
rect(45,0,20,3,color(0,3));
|
|
5245
5261
|
rect(45,0,20,7);
|
|
@@ -5277,8 +5293,9 @@ function drawEngineSplashScreen(t)
|
|
|
5277
5293
|
}
|
|
5278
5294
|
|
|
5279
5295
|
// wheels
|
|
5280
|
-
rect(
|
|
5281
|
-
rect(
|
|
5296
|
+
rect(6,40,5,5);
|
|
5297
|
+
rect(6,40,5,5,color());
|
|
5298
|
+
rect(15,54,38,-14,color());
|
|
5282
5299
|
for (let i=3; i--;)
|
|
5283
5300
|
for (let j=2; j--;)
|
|
5284
5301
|
{
|
|
@@ -5287,26 +5304,26 @@ function drawEngineSplashScreen(t)
|
|
|
5287
5304
|
circle(15*i+15,47,j?7:1,0,PI,color(i,2));
|
|
5288
5305
|
x.stroke();
|
|
5289
5306
|
}
|
|
5290
|
-
line(6,40,68,40) // center
|
|
5291
|
-
line(77,54,4,54) // bottom
|
|
5307
|
+
line(6,40,68,40); // center
|
|
5308
|
+
line(77,54,4,54); // bottom
|
|
5292
5309
|
|
|
5293
|
-
//
|
|
5310
|
+
// draw engine name
|
|
5294
5311
|
const s = engineName;
|
|
5295
5312
|
x.font = '900 16px arial';
|
|
5296
5313
|
x.textAlign = 'center';
|
|
5297
5314
|
x.textBaseline = 'top';
|
|
5298
|
-
x.lineWidth = 1+p*3
|
|
5315
|
+
x.lineWidth = 1+p*3;
|
|
5299
5316
|
let w2 = 0;
|
|
5300
5317
|
for (let i=0; i<s.length; ++i)
|
|
5301
5318
|
w2 += x.measureText(s[i]).width;
|
|
5302
5319
|
for (let j=2; j--;)
|
|
5303
5320
|
for (let i=0, X=41-w2/2; i<s.length; ++i)
|
|
5304
5321
|
{
|
|
5305
|
-
x.fillStyle = color(i,
|
|
5322
|
+
x.fillStyle = color(i,2);
|
|
5306
5323
|
const w = x.measureText(s[i]).width;
|
|
5307
5324
|
x[j?'strokeText':'fillText'](s[i],X+w/2,55.5,17*p);
|
|
5308
5325
|
X += w;
|
|
5309
5326
|
}
|
|
5310
|
-
|
|
5327
|
+
|
|
5311
5328
|
x.restore();
|
|
5312
5329
|
}
|
package/build/littlejs.min.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
let showWatermark=0,debugKey=0;const debug=0,debugOverlay=0,debugPhysics=0,debugParticles=0,debugRaycast=0,debugGamepads=0,debugMedals=0;function ASSERT(){}function debugInit(){}function debugUpdate(){}function debugRender(){}function debugRect(){}function debugCircle(){}function debugPoint(){}function debugLine(){}function debugAABB(){}function debugText(){}function debugClear(){}function debugSaveCanvas(){}const PI=Math.PI;function abs(a){return Math.abs(a)}function min(a,b){return Math.min(a,b)}function max(a,b){return Math.max(a,b)}function sign(a){return Math.sign(a)}function mod(a,b=1){return(a%b+b)%b}function clamp(a,b=0,c=1){return a<b?b:a>c?c:a}function percent(a,b,c){return c-b?clamp((a-b)/(c-b)):0}function lerp(a,b,c){return b+clamp(a)*(c-b)}function distanceWrap(a,b,c=1){a=(a-b)%c;return 2*a%c-a}function lerpWrap(a,b,c,d=1){return c+clamp(a)*distanceWrap(b,c,d)}function distanceAngle(a,b){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){return 2*abs(a.x-c.x)<b.x+d.x&&2*abs(a.y-c.y)<b.y+d.y}function wave(a=1,b=1,c=time){return b/2*(1-Math.cos(c*a*2*PI))}function formatTime(a){return(a/60|0)+":"+(10>a%60?"0":"")+(a%60|0)}function rand(a=1,b=0){return b+Math.random()*(a-b)}function randInt(a,b=0){return Math.floor(rand(a,b))}function randSign(){return 2*randInt(2)-1}function randVector(a=1){return(new Vector2).setAngle(rand(2*PI),a)}function randInCircle(a=1,b=0){return 0<a?randVector(a*rand(b/a,1)**.5):new Vector2}function randColor(a=new Color,b=new Color(0,0,0,1),c){return c?a.lerp(b,rand()):new Color(rand(a.r,b.r),rand(a.g,b.g),rand(a.b,b.b),rand(a.a,b.a))}class RandomGenerator{constructor(a){this.seed=a}float(a=1,b=0){this.seed^=this.seed<<13;this.seed^=this.seed>>>17;this.seed^=this.seed<<5;return b+(a-b)*abs(this.seed%1e9)/1e9}int(a,b=0){return Math.floor(this.float(a,b))}sign(){return 2*this.int(2)-1}}function vec2(a=0,b){return void 0==a.x?new Vector2(a,void 0==b?a:b):new Vector2(a.x,a.y)}function isVector2(a){return!isNaN(a.x)&&!isNaN(a.y)}class Vector2{constructor(a=0,b=0){this.x=a;this.y=b}copy(){return new Vector2(this.x,this.y)}add(a){ASSERT(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){return this.distanceSquared(a)**.5}distanceSquared(a){return(this.x-a.x)**2+(this.y-a.y)**2}normalize(a=1){const b=this.length();return b?this.scale(a/b):new Vector2(0,a)}clampLength(a=1){const b=this.length();return b>a?this.scale(a/b):this}dot(a){ASSERT(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)}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){return 0<=this.x&&0<=this.y&&this.x<a.x&&this.y<a.y}toString(a=3){if(debug)return`(${(0>this.x?"":" ")+this.x.toFixed(a)},${(0>this.y?"":" ")+this.y.toFixed(a)} )`}}function rgb(a,b,c,d){return new Color(a,b,c,d)}function hsl(a,b,c,d){return(new Color).setHSLA(a,b,c,d)}class Color{constructor(a=1,b=1,c=1,d=1){this.r=a;this.g=b;this.b=c;this.a=d}copy(){return new Color(this.r,this.g,this.b,this.a)}add(a){return new Color(this.r+a.r,this.g+a.g,this.b+a.b,this.a+a.a)}subtract(a){return new Color(this.r-a.r,this.g-a.g,this.b-a.b,this.a-a.a)}multiply(a){return new Color(this.r*a.r,this.g*a.g,this.b*a.b,this.a*a.a)}divide(a){return new Color(this.r/a.r,this.g/a.g,this.b/a.b,this.a/a.a)}scale(a,b=a){return new Color(this.r*a,this.g*a,this.b*a,this.a*b)}clamp(){return new Color(clamp(this.r),clamp(this.g),clamp(this.b),clamp(this.a))}lerp(a,b){return this.add(a.subtract(this).scale(clamp(b)))}setHSLA(a=0,b=0,c=1,d=1){b=.5>c?c*(1+b):c+b-c*b;c=2*c-b;const e=(f,g,k)=>(k=(k%1+1)%1)<1/6?f+6*(g-f)*k:.5>k?g:k<2/3?f+(g-f)*(2/3-k)*6:f;this.r=e(c,b,a+1/3);this.g=e(c,b,a);this.b=e(c,b,a-1/3);this.a=d;return this}getHSLA(){const a=clamp(this.r),b=clamp(this.g),c=clamp(this.b),d=clamp(this.a),e=Math.max(a,b,c),f=Math.min(a,b,c),g=(e+f)/2;let k=0,h=0;if(e!=f){let l=e-f;h=.5<g?l/(2-e-f):l/(e+f);a==e?k=(b-c)/l+(b<c?6:0):b==e?k=(c-a)/l+2:c==e&&(k=(a-b)/l+4)}return[k/6,h,g,d]}mutate(a=.05,b=0){return new Color(this.r+rand(a,-a),this.g+rand(a,-a),this.b+rand(a,-a),this.a+rand(b,-b)).clamp()}toString(a=1){const b=c=>(16>(c=255*c|0)?"0":"")+c.toString(16);return"#"+b(this.r)+b(this.g)+b(this.b)+(a?b(this.a):"")}setHex(a){this.r=clamp(parseInt(a.slice(1,3),16)/255);this.g=clamp(parseInt(a.slice(3,5),16)/255);this.b=clamp(parseInt(a.slice(5,7),16)/255);this.a=7<a.length?clamp(parseInt(a.slice(7,9),16)/255):1;return this}rgbaInt(){const a=255*clamp(this.r)|0,b=255*clamp(this.g)<<8,c=255*clamp(this.b)<<16,d=255*clamp(this.a)<<24;return a+b+c+d}}class Timer{constructor(a){this.time=void 0==a?void 0:time+a;this.setTime=a}set(a=0){this.time=time+a;this.setTime=a}unset(){this.time=void 0}isSet(){return void 0!=this.time}active(){return time<=this.time}elapsed(){return time>this.time}get(){return this.isSet()?time-this.time:0}getPercent(){return this.isSet()?percent(this.time-time,this.setTime,0):0}toString(){if(debug)return this.isSet()?Math.abs(this.get())+" seconds "+(0>this.get()?"before":"after"):"unset"}valueOf(){return this.get()}}let cameraPos=vec2(),cameraScale=32,canvasMaxSize=vec2(1920,1200),canvasFixedSize=vec2(),canvasPixelated=1,fontDefault="arial",showSplashScreen=0,glEnable=1,glOverlay=1,tileSizeDefault=vec2(16),tileFixBleedScale=.3,enablePhysicsSolver=1,objectDefaultMass=1,objectDefaultDamping=1,objectDefaultAngleDamping=1,objectDefaultElasticity=0,objectDefaultFriction=.8,objectMaxSpeed=1,gravity=0,particleEmitRateScale=1,gamepadsEnable=1,gamepadDirectionEmulateStick=1,inputWASDEmulateDirection=1,touchGamepadEnable=0,touchGamepadAnalog=1,touchGamepadSize=99,touchGamepadAlpha=.3,vibrateEnable=1,soundEnable=1,soundVolume=.5,soundDefaultRange=40,soundDefaultTaper=.7,medalDisplayTime=5,medalDisplaySlideTime=.5,medalDisplaySize=vec2(640,80),medalDisplayIconSize=50,medalsPreventUnlock;function setCameraPos(a){cameraPos=a}function setCameraScale(a){cameraScale=a}function setCanvasMaxSize(a){canvasMaxSize=a}function setCanvasFixedSize(a){canvasFixedSize=a}function setCanvasPixelated(a){canvasPixelated=a}function setFontDefault(a){fontDefault=a}function setShowSplashScreen(a){showSplashScreen=a}function setGlEnable(a){glEnable=a}function setGlOverlay(a){glOverlay=a}function setTileSizeDefault(a){tileSizeDefault=a}function setTileFixBleedScale(a){tileFixBleedScale=a}function setEnablePhysicsSolver(a){enablePhysicsSolver=a}function setObjectDefaultMass(a){objectDefaultMass=a}function setObjectDefaultDamping(a){objectDefaultDamping=a}function setObjectDefaultAngleDamping(a){objectDefaultAngleDamping=a}function setObjectDefaultElasticity(a){objectDefaultElasticity=a}function setObjectDefaultFriction(a){objectDefaultFriction=a}function setObjectMaxSpeed(a){objectMaxSpeed=a}function setGravity(a){gravity=a}function setParticleEmitRateScale(a){particleEmitRateScale=a}function setGamepadsEnable(a){gamepadsEnable=a}function setGamepadDirectionEmulateStick(a){gamepadDirectionEmulateStick=a}function setInputWASDEmulateDirection(a){inputWASDEmulateDirection=a}function setTouchGamepadEnable(a){touchGamepadEnable=a}function setTouchGamepadAnalog(a){touchGamepadAnalog=a}function setTouchGamepadSize(a){touchGamepadSize=a}function setTouchGamepadAlpha(a){touchGamepadAlpha=a}function setVibrateEnable(a){vibrateEnable=a}function setSoundEnable(a){soundEnable=a}function setSoundVolume(a){soundVolume=a}function setSoundDefaultRange(a){soundDefaultRange=a}function setSoundDefaultTaper(a){soundDefaultTaper=a}function setMedalDisplayTime(a){medalDisplayTime=a}function setMedalDisplaySlideTime(a){medalDisplaySlideTime=a}function setMedalDisplaySize(a){medalDisplaySize=a}function setMedalDisplayIconSize(a){medalDisplayIconSize=a}function setMedalsPreventUnlock(a){medalsPreventUnlock=a}function setShowWatermark(a){showWatermark=a}function setDebugKey(a){debugKey=a}class EngineObject{constructor(a=vec2(),b=vec2(1),c,d=0,e,f=0){ASSERT(isVector2(a)&&isVector2(b));ASSERT("number"!==typeof c||!c);ASSERT(!(f instanceof Color));this.pos=a.copy();this.size=b;this.drawSize;this.tileInfo=c;this.angle=d;this.color=e;this.additiveColor;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.collideTiles=!1;engineObjects.push(this)}update(){var a=this.parent;if(a)this.pos=this.localPos.multiply(vec2(a.getMirrorSign(),1)).rotate(-a.angle).add(a.pos),this.angle=a.getMirrorSign()*this.localAngle+a.angle;else if(this.velocity.x=clamp(this.velocity.x,-objectMaxSpeed,objectMaxSpeed),this.velocity.y=clamp(this.velocity.y,-objectMaxSpeed,objectMaxSpeed),a=this.pos.copy(),this.velocity.y+=gravity*this.gravityScale,this.pos.x+=this.velocity.x*=this.damping,this.pos.y+=this.velocity.y*=this.damping,this.angle+=this.angleVelocity*=this.angleDamping,ASSERT(0<=this.angleDamping&&1>=this.angleDamping),ASSERT(0<=this.damping&&1>=this.damping),enablePhysicsSolver&&this.mass){var b=0>this.velocity.y;if(this.groundObject){var c=this.groundObject.velocity?this.groundObject.velocity.x:0;this.velocity.x=c+(this.velocity.x-c)*this.friction;this.groundObject=0}if(this.collideSolidObjects)for(var d of engineObjectsCollide){if(!this.isSolid&&!d.isSolid||d.destroyed||d.parent||d==this)continue;if(!isOverlapping(this.pos,this.size,d.pos,d.size))continue;c=this.collideWithObject(d);var e=d.collideWithObject(this);if(!c||!e)continue;if(isOverlapping(a,this.size,d.pos,d.size)){c=a.subtract(d.pos);e=c.length();c=.01>e?randVector(.001):c.scale(.001/e);this.velocity=this.velocity.add(c);d.mass&&(d.velocity=d.velocity.subtract(c));debugOverlay&&debugPhysics&&debugAABB(this.pos,this.size,d.pos,d.size,"#f00");continue}e=this.size.add(d.size);var f=2*(a.y-d.pos.y)>e.y+gravity;const k=2*abs(a.y-d.pos.y)<e.y;var g=2*abs(a.x-d.pos.x)<e.x;c=max(this.elasticity,d.elasticity);if(f||g||!k)if(this.pos.y=d.pos.y+(e.y/2+.001)*sign(a.y-d.pos.y),d.groundObject&&b||!d.mass)b&&(this.groundObject=d),this.velocity.y*=-c;else if(d.mass){g=(this.mass*this.velocity.y+d.mass*d.velocity.y)/(this.mass+d.mass);const h=d.velocity.y*(d.mass-this.mass)/(this.mass+d.mass)+2*this.velocity.y*this.mass/(this.mass+d.mass);this.velocity.y=lerp(c,g,this.velocity.y*(this.mass-d.mass)/(this.mass+d.mass)+2*d.velocity.y*d.mass/(this.mass+d.mass));d.velocity.y=lerp(c,g,h)}!f&&k&&(this.pos.x=d.pos.x+(e.x/2+.001)*sign(a.x-d.pos.x),d.mass?(e=(this.mass*this.velocity.x+d.mass*d.velocity.x)/(this.mass+d.mass),f=d.velocity.x*(d.mass-this.mass)/(this.mass+d.mass)+2*this.velocity.x*this.mass/(this.mass+d.mass),this.velocity.x=lerp(c,e,this.velocity.x*(this.mass-d.mass)/(this.mass+d.mass)+2*d.velocity.x*d.mass/(this.mass+d.mass)),d.velocity.x=lerp(c,e,f)):this.velocity.x*=-c);debugOverlay&&debugPhysics&&debugAABB(this.pos,this.size,d.pos,d.size,"#f0f")}if(this.collideTiles&&tileCollisionTest(this.pos,this.size,this)&&!tileCollisionTest(a,this.size,this)){c=tileCollisionTest(vec2(a.x,this.pos.y),this.size,this);d=tileCollisionTest(vec2(this.pos.x,a.y),this.size,this);if(c||!d)this.groundObject=b,this.velocity.y*=-this.elasticity,b=(a.y-this.size.y/2|0)-(a.y-this.size.y/2),0>b&&b>this.damping*this.velocity.y+gravity*this.gravityScale&&(this.velocity.y=this.damping?(b-gravity*this.gravityScale)/this.damping:0),this.pos.y=a.y;d&&(this.pos.x=a.x,this.velocity.x*=-this.elasticity)}}}render(){drawTile(this.pos,this.drawSize||this.size,this.tileInfo,this.color,this.angle,this.mirror,this.additiveColor)}destroy(){if(!this.destroyed){this.destroyed=1;this.parent&&this.parent.removeChild(this);for(const a of this.children)a.destroy(a.parent=0)}}collideWithTile(a,b){return 0<a}collideWithTileRaycast(a,b){return 0<a}collideWithObject(a){return 1}getAliveTime(){return time-this.spawnTime}applyAcceleration(a){this.mass&&(this.velocity=this.velocity.add(a))}applyForce(a){this.applyAcceleration(a.scale(1/this.mass))}getMirrorSign(){return this.mirror?-1:1}addChild(a,b=vec2(),c=0){ASSERT(!a.parent&&!this.children.includes(a));this.children.push(a);a.parent=this;a.localPos=b.copy();a.localAngle=c}removeChild(a){ASSERT(a.parent==this&&this.children.includes(a));this.children.splice(this.children.indexOf(a),1);a.parent=0}setCollision(a=1,b=1,c=1){ASSERT(a||!b);this.collideSolidObjects=a;this.isSolid=b;this.collideTiles=c}toString(){if(debug){let a="type = "+this.constructor.name;if(this.pos.x||this.pos.y)a+="\npos = "+this.pos;if(this.velocity.x||this.velocity.y)a+="\nvelocity = "+this.velocity;if(this.size.x||this.size.y)a+="\nsize = "+this.size;this.angle&&(a+="\nangle = "+this.angle.toFixed(3));this.color&&(a+="\ncolor = "+this.color);return a}}}let mainCanvas,mainContext,overlayCanvas,overlayContext,mainCanvasSize=vec2(),textureInfos=[],drawCount;function tile(a=vec2(),b=tileSizeDefault,c=0){void 0==b.x&&(ASSERT(0<b),b=vec2(b));if(void 0==a.x){var d=textureInfos[c];d?(d=d.size.x/b.x|0,a=vec2(a%d*b.x,(a/d|0)*b.y)):a=vec2()}return new TileInfo(a,b,c)}class TileInfo{constructor(a=vec2(),b=tileSizeDefault,c=0){this.pos=a;this.size=b;this.textureIndex=c}offset(a){return new TileInfo(this.pos.add(a),this.size,this.textureIndex)}getTextureInfo(){return textureInfos[this.textureIndex]}}class TextureInfo{constructor(a){this.image=a;this.size=vec2(a.width,a.height);this.glTexture=glEnable&&glCreateTexture(a);this.fixBleedSize=vec2(tileFixBleedScale).divide(this.size)}}function screenToWorld(a){return new Vector2((a.x-mainCanvasSize.x/2+.5)/cameraScale+cameraPos.x,(a.y-mainCanvasSize.y/2+.5)/-cameraScale+cameraPos.y)}function worldToScreen(a){return new Vector2((a.x-cameraPos.x)*cameraScale+mainCanvasSize.x/2-.5,(a.y-cameraPos.y)*-cameraScale+mainCanvasSize.y/2-.5)}function drawTile(a,b=vec2(1),c,d=new Color,e=0,f,g=new Color(0,0,0,0),k=glEnable,h,l){ASSERT(!l||!k);ASSERT("number"!==typeof c||!c);showWatermark&&++drawCount;const m=c&&c.getTextureInfo();if(k)if(h&&(a=screenToWorld(a),b=b.scale(1/cameraScale)),m){k=c.pos.x/m.size.x;h=c.pos.y/m.size.y;l=c.size.x/m.size.x;const n=c.size.y/m.size.y,p=m.fixBleedSize;glSetTexture(m.glTexture);glDraw(a.x,a.y,f?-b.x:b.x,b.y,e,k+p.x,h+p.y,k-p.x+l,h-p.y+n,d.rgbaInt(),g.rgbaInt())}else glDraw(a.x,a.y,b.x,b.y,e,0,0,0,0,0,d.rgbaInt());else drawCanvas2D(a,b,e,f,n=>{if(m){const p=c.pos.x+tileFixBleedScale,t=c.pos.y+tileFixBleedScale,q=c.size.x-2*tileFixBleedScale,x=c.size.y-2*tileFixBleedScale;n.globalAlpha=d.a;n.drawImage(m.image,p,t,q,x,-.5,-.5,1,1);n.globalAlpha=1}else n.fillStyle=d,n.fillRect(-.5,-.5,1,1)},h,l)}function drawRect(a,b,c,d,e,f,g){drawTile(a,b,void 0,c,d,0,void 0,e,f,g)}function drawPoly(a,b=new Color,c=glEnable,d,e){ASSERT(!e||!c);if(c)glDrawPoints(d?a.map(screenToWorld):a,b.rgbaInt());else{e||=mainContext;e.fillStyle=b;e.beginPath();for(const f of d?a:a.map(worldToScreen))e.lineTo(f.x,f.y);e.fill()}}function drawLine(a,b,c=.1,d,e,f,g){b=vec2((b.x-a.x)/2,(b.y-a.y)/2);c=vec2(c,2*b.length());drawRect(a.add(b),c,d,b.angle(),e,f,g)}function drawCanvas2D(a,b,c,d,e,f,g=mainContext){f||(a=worldToScreen(a),b=b.scale(cameraScale));g.save();g.translate(a.x+.5,a.y+.5);g.rotate(c);g.scale(d?-b.x:b.x,b.y);e(g);g.restore()}function setBlendMode(a,b=glEnable,c){ASSERT(!c||!b);b?glAdditive=a:(c||=mainContext,c.globalCompositeOperation=a?"lighter":"source-over")}function drawText(a,b,c=1,d,e=0,f,g,k,h){drawTextScreen(a,worldToScreen(b),c*cameraScale,d,e*cameraScale,f,g,k,h)}function drawTextScreen(a,b,c=1,d=new Color,e=0,f=new Color(0,0,0),g="center",k=fontDefault,h=overlayContext){h.fillStyle=d;h.lineWidth=e;h.strokeStyle=f;h.textAlign=g;h.font=c+"px "+k;h.textBaseline="middle";h.lineJoin="round";b=b.copy();(a+"").split("\n").forEach(l=>{e&&h.strokeText(l,b.x,b.y);h.fillText(l,b.x,b.y);b.y+=c})}let engineFontImage;class FontImage{constructor(a,b=vec2(8),c=vec2(0,1),d=overlayContext){engineFontImage||((engineFontImage=new Image).src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQAAAAAYAQAAAAA9+x6JAAAAAnRSTlMAAHaTzTgAAAGiSURBVHjaZZABhxxBEIUf6ECLBdFY+Q0PMNgf0yCgsSAGZcT9sgIPtBWwIA5wgAPEoHUyJeeSlW+gjK+fegWwtROWpVQEyWh2npdpBmTUFVhb29RINgLIukoXr5LIAvYQ5ve+1FqWEMqNKTX3FAJHyQDRZvmKWubAACcv5z5Gtg2oyCWE+Yk/8JZQX1jTTCpKAFGIgza+dJCNBF2UskRlsgwitHbSV0QLgt9sTPtsRlvJjEr8C/FARWA2bJ/TtJ7lko34dNDn6usJUMzuErP89UUBJbWeozrwLLncXczd508deAjLWipLO4Q5XGPcJvPu92cNDaN0P5G1FL0nSOzddZOrJ6rNhbXGmeDvO3TF7DeJWl4bvaYQTNHCTeuqKZmbjHaSOFes+IX/+IhHrnAkXOAsfn24EM68XieIECoccD4KZLk/odiwzeo2rovYdhvb2HYFgyznJyDpYJdYOmfXgVdJTaUi4xA2uWYNYec9BLeqdl9EsoTw582mSFDX2DxVLbNt9U3YYoeatBad1c2Tj8t2akrjaIGJNywKB/7h75/gN3vCMSaadIUTAAAAAElFTkSuQmCC");this.image=a||engineFontImage;this.tileSize=b;this.paddingSize=c;this.context=d}drawText(a,b,c=1,d){this.drawTextScreen(a,worldToScreen(b).floor(),c*cameraScale|0,d)}drawTextScreen(a,b,c=4,d){const e=this.context;e.save();e.imageSmoothingEnabled=!canvasPixelated;const f=this.tileSize,g=f.add(this.paddingSize).scale(c),k=this.image.width/this.tileSize.x|0;(a+"").split("\n").forEach((h,l)=>{const m=d?h.length*f.x*c/2|0:0;for(let t=h.length;t--;){var n=h[t].charCodeAt();if(32>n||127<n)n=127;var p=n-32;n=p%k;p=p/k|0;const q=b.add(vec2(t,l).multiply(g));e.drawImage(this.image,n*f.x,p*f.y,f.x,f.y,q.x-m,q.y,f.x*c,f.y*c)}});e.restore()}}function isFullscreen(){return document.fullscreenElement}function toggleFullscreen(){isFullscreen()?document.exitFullscreen&&document.exitFullscreen():document.body.requestFullscreen&&document.body.requestFullscreen()}function keyIsDown(a,b=0){return inputData[b]&&inputData[b][a]&1}function keyWasPressed(a,b=0){return inputData[b]&&inputData[b][a]&2?1:0}function keyWasReleased(a,b=0){return inputData[b]&&inputData[b][a]&4?1:0}function clearInput(){inputData=[[]]}const mouseIsDown=keyIsDown,mouseWasPressed=keyWasPressed,mouseWasReleased=keyWasReleased;let mousePos=vec2(),mousePosScreen=vec2(),mouseWheel=0,isUsingGamepad=0,preventDefaultInput=0;function gamepadIsDown(a,b=0){return keyIsDown(a,b+1)}function gamepadWasPressed(a,b=0){return keyWasPressed(a,b+1)}function gamepadWasReleased(a,b=0){return keyWasReleased(a,b+1)}function gamepadStick(a,b=0){return stickData[b]?stickData[b][a]||vec2():vec2()}let inputData=[[]];function inputUpdate(){isTouchDevice||document.hasFocus()||clearInput();mousePos=screenToWorld(mousePosScreen);gamepadsUpdate()}function inputUpdatePost(){for(const a of inputData)for(const b in a)a[b]&=1;mouseWheel=0}onkeydown=a=>{debug&&a.target!=document.body||(a.repeat||(inputData[isUsingGamepad=0][a.which]=3,inputWASDEmulateDirection&&(inputData[0][remapKey(a.which)]=3)),preventDefaultInput&&a.preventDefault())};onkeyup=a=>{debug&&a.target!=document.body||(inputData[0][a.which]=4,inputWASDEmulateDirection&&(inputData[0][remapKey(a.which)]=4))};function remapKey(a){return inputWASDEmulateDirection?87==a?38:83==a?40:65==a?37:68==a?39:a:a}onmousedown=a=>{inputData[isUsingGamepad=0][a.button]=3;onmousemove(a);a.button&&a.preventDefault()};onmouseup=a=>inputData[0][a.button]=inputData[0][a.button]&2|4;onmousemove=a=>mousePosScreen=mouseToScreen(a);onwheel=a=>a.ctrlKey||(mouseWheel=sign(a.deltaY));oncontextmenu=a=>!1;function mouseToScreen(a){if(!mainCanvas)return vec2();const b=mainCanvas.getBoundingClientRect();return vec2(mainCanvas.width,mainCanvas.height).multiply(vec2(percent(a.x,b.left,b.right),percent(a.y,b.top,b.bottom)))}const stickData=[];function gamepadsUpdate(){if(touchGamepadEnable&&isTouchDevice&&(touchGamepadButtons||createTouchGamepad(),touchGamepadTimer.isSet())){(stickData[0]||(stickData[0]=[]))[0]=vec2(touchGamepadStick.x,-touchGamepadStick.y);var a=inputData[1]||(inputData[1]=[]);for(var b=10;b--;){var c=3==b?2:2==b?3:b;a[c]=touchGamepadButtons[b]?1+2*!gamepadIsDown(c,0):4*gamepadIsDown(c,0)}}if(gamepadsEnable&&navigator&&navigator.getGamepads&&(document.hasFocus()||debug))for(a=navigator.getGamepads(),b=a.length;b--;){var d=a[b];const g=inputData[b+1]||(inputData[b+1]=[]);c=stickData[b]||(stickData[b]=[]);if(d){var e=k=>.3<k?percent(k,.3,.8):-.3>k?-percent(-k,.3,.8):0;for(var f=0;f<d.axes.length-1;f+=2)c[f>>1]=vec2(e(d.axes[f]),e(-d.axes[f+1])).clampLength();for(e=d.buttons.length;e--;)f=d.buttons[e],g[e]=f.pressed?1+2*!gamepadIsDown(e,b):4*gamepadIsDown(e,b),isUsingGamepad|=!b&&f.pressed,touchGamepadEnable&&touchGamepadTimer.unset();gamepadDirectionEmulateStick&&(d=vec2(gamepadIsDown(15,b)-gamepadIsDown(14,b),gamepadIsDown(12,b)-gamepadIsDown(13,b)),d.lengthSquared()&&(c[0]=d.clampLength()))}}}function vibrate(a){vibrateEnable&&navigator&&navigator.vibrate&&navigator.vibrate(a)}function vibrateStop(){vibrate(0)}const isTouchDevice=void 0!==window.ontouchstart;if(isTouchDevice){let a,b=onmousedown,c=onmouseup;onmousedown=onmouseup=()=>0;ontouchstart=ontouchmove=ontouchend=d=>{d.button=0;soundEnable&&(audioContext?audioContext.resume():zzfx(0));const e=d.touches.length;e?(d.x=d.touches[0].clientX,d.y=d.touches[0].clientY,a?onmousemove(d):b(d)):a&&c(d);a=e;document.hasFocus()&&d.preventDefault();return!0}}let touchGamepadTimer=new Timer,touchGamepadButtons,touchGamepadStick;function createTouchGamepad(){touchGamepadButtons=[];touchGamepadStick=vec2();const a=ontouchstart;ontouchstart=ontouchmove=ontouchend=b=>{touchGamepadStick=vec2();touchGamepadButtons=[];if(b.touches.length&&(touchGamepadTimer.set(),paused)){touchGamepadButtons[9]=1;return}const c=vec2(touchGamepadSize,mainCanvasSize.y-touchGamepadSize),d=mainCanvasSize.subtract(vec2(touchGamepadSize,touchGamepadSize)),e=mainCanvasSize.scale(.5);for(const g of b.touches){var f=mouseToScreen(vec2(g.clientX,g.clientY));f.distance(c)<touchGamepadSize?touchGamepadAnalog?touchGamepadStick=f.subtract(c).scale(2/touchGamepadSize).clampLength():(f=f.subtract(c).angle(),touchGamepadStick.setAngle((4*f/PI+8.5|0)*PI/4)):f.distance(d)<touchGamepadSize?(f=f.subtract(d).direction(),touchGamepadButtons[f]=1):f.distance(e)<touchGamepadSize&&(touchGamepadButtons[9]=1)}a(b);isUsingGamepad=1;return!0}}function touchGamepadRender(){if(touchGamepadEnable&&touchGamepadTimer.isSet()){var a=percent(touchGamepadTimer,4,3);if(a&&!paused){overlayContext.save();overlayContext.globalAlpha=a*touchGamepadAlpha;overlayContext.strokeStyle="#fff";overlayContext.lineWidth=3;overlayContext.fillStyle=0<touchGamepadStick.lengthSquared()?"#fff":"#000";overlayContext.beginPath();a=vec2(touchGamepadSize,mainCanvasSize.y-touchGamepadSize);if(touchGamepadAnalog)overlayContext.arc(a.x,a.y,touchGamepadSize/2,0,9),overlayContext.fill();else for(var b=10;b--;){var c=b*PI/4;overlayContext.arc(a.x,a.y,.6*touchGamepadSize,c+PI/8,c+PI/8);b%2&&overlayContext.arc(a.x,a.y,.33*touchGamepadSize,c,c);1==b&&overlayContext.fill()}overlayContext.stroke();a=vec2(mainCanvasSize.x-touchGamepadSize,mainCanvasSize.y-touchGamepadSize);for(b=4;b--;)c=a.add(vec2().setAngle(b*PI/2,touchGamepadSize/2)),overlayContext.fillStyle=touchGamepadButtons[b]?"#fff":"#000",overlayContext.beginPath(),overlayContext.arc(c.x,c.y,touchGamepadSize/4,0,9),overlayContext.fill(),overlayContext.stroke();overlayContext.restore()}}}class Sound{constructor(a,b=soundDefaultRange,c=soundDefaultTaper){soundEnable&&(this.range=b,this.taper=c,this.randomness=0,a&&(this.randomness=a[1]||0,a[1]=0,this.sampleChannels=[zzfxG(...a)],this.sampleRate=zzfxR))}play(a,b=1,c=1,d=1,e=0){if(soundEnable&&this.sampleChannels){var f;if(a){if(f=this.range){const g=cameraPos.distanceSquared(a);if(g>f*f)return;b*=percent(g**.5,f,f*this.taper)}f=2*worldToScreen(a).x/mainCanvas.width-1}a=c+c*this.randomness*d*rand(-1,1);return this.source=playSamples(this.sampleChannels,b,a,f,e,this.sampleRate)}}stop(){this.source&&this.source.stop();this.source=0}playNote(a,b,c){return this.play(b,c,2**(a/12),0)}getDuration(){return this.sampleChannels&&this.sampleChannels[0].length/this.sampleRate}isPlaying(){return this.source&&!this.source.ended}isLoading(){return!this.sampleChannels}}class SoundWave extends Sound{constructor(a,b=0,c,d){super(0,c,d);this.randomness=b;soundEnable&&(soundDecoderContext||=new AudioContext,fetch(a).then(e=>e.arrayBuffer()).then(e=>soundDecoderContext.decodeAudioData(e)).then(e=>{this.sampleChannels=[];for(let f=e.numberOfChannels;f--;)this.sampleChannels[f]=e.getChannelData(f);this.sampleRate=e.sampleRate}))}}let soundDecoderContext;class Music extends Sound{constructor(a){super();soundEnable&&(this.randomness=0,this.sampleChannels=zzfxM(...a),this.sampleRate=zzfxR)}playMusic(a,b=1){return super.play(0,a,1,1,b)}}function playAudioFile(a,b=1,c=1){if(soundEnable)return a=new Audio(a),a.volume=soundVolume*b,a.loop=c,a.play(),a}function speak(a,b="",c=1,d=1,e=1){if(soundEnable&&speechSynthesis)return a=new SpeechSynthesisUtterance(a),a.lang=b,a.volume=2*c*soundVolume,a.rate=d,a.pitch=e,speechSynthesis.speak(a),a}function speakStop(){speechSynthesis&&speechSynthesis.cancel()}function getNoteFrequency(a,b=220){return b*2**(a/12)}let audioContext;function playSamples(a,b=1,c=1,d=0,e=0,f=zzfxR){if(soundEnable)if(audioContext||=new AudioContext,"running"!=audioContext.state)audioContext.resume();else{var g=audioContext.createBuffer(a.length,a[0].length,f);f=audioContext.createBufferSource();a.forEach((k,h)=>g.getChannelData(h).set(k));f.buffer=g;f.playbackRate.value=c;f.loop=e;a=audioContext.createGain();a.gain.value=soundVolume*b;a.connect(audioContext.destination);f.connect(new StereoPannerNode(audioContext,{pan:clamp(d,-1,1)})).connect(a);f.start();return f}}function zzfx(...a){return playSamples([zzfxG(...a)])}const zzfxR=44100;function zzfxG(a=1,b=.05,c=220,d=0,e=0,f=.1,g=0,k=1,h=0,l=0,m=0,n=0,p=0,t=0,q=0,x=0,u=0,B=1,y=0,C=0){let v=2*PI,E=h*=500*v/zzfxR/zzfxR,A=[];b=c*=(1+b*rand(-1,1))*v/zzfxR;let w=0,F=0,r=0,z=1,H=0,I=0,D=0,J,G;d=d*zzfxR+9;y*=zzfxR;e*=zzfxR;f*=zzfxR;u*=zzfxR;l*=500*v/zzfxR**3;q*=v/zzfxR;m*=v/zzfxR;n*=zzfxR;p=p*zzfxR|0;for(G=d+y+e+f+u|0;r<G;A[r++]=D)++I%(100*x|0)||(D=g?1<g?2<g?3<g?Math.sin((w%v)**3):max(min(Math.tan(w),1),-1):1-(2*w/v%2+2)%2:1-4*abs(Math.round(w/v)-w/v):Math.sin(w),D=(p?1-C+C*Math.sin(v*r/p):1)*sign(D)*abs(D)**k*a*soundVolume*(r<d?r/d:r<d+y?1-(r-d)/y*(1-B):r<d+y+e?B:r<G-u?(G-r-u)/f*B:0),D=u?D/2+(u>r?0:(r<G-u?1:(G-r)/u)*A[r-u|0]/2):D),J=(c+=h+=l)*Math.cos(q*F++),w+=J-J*t*(1-1e9*(Math.sin(r)+1)%2),z&&++z>n&&(c+=m,b+=m,z=0),!p||++H%p||(c=b,h=E,z||=1);return A}function zzfxM(a,b,c,d=125){let e,f,g,k,h,l,m,n,p,t,q,x,u,B=0,y,C=[],v=[],E=[],A=0,w=0,F=1,r={},z=zzfxR/d*60>>2;for(;F;A++)C=[F=n=x=0],c.forEach((H,I)=>{m=b[H][A]||[0,0,0];F||=!!b[H][A];y=x+(b[H][0].length-2-!n)*z;u=I==c.length-1;e=2;for(g=x;e<m.length+u;n=++e){h=m[e];p=e==m.length+u-1&&u||t!=(m[0]||0)||h|0;for(f=0;f<z&&n;f++>z-99&&p?q+=(1>q)/99:0)l=(1-q)*C[B++]/2||0,v[g]=(v[g]||0)-l*w+l,E[g]=(E[g++]||0)+l*w+l;h&&(q=h%1,w=m[1]||0,h|=0)&&(C=r[[t=m[B=0]||0,h]]=r[[t,h]]||(k=[...a[t]],k[2]*=2**((h-12)/12),0<h?zzfxG(...k):[]))}x=y});return[v,E]}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 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 l=getTileCollisionData(g);if(l&&(!c||c.collideWithTile(l,g)))return debugRaycast&&debugLine(a,b,"#f00",.02),debugRaycast&&debugPoint(g.add(vec2(.5)),"#ff0"),g.add(vec2(.5));if(k>e&&h>e)break;k>h?(g.y+=sign(d.y),h+=f.y):(g.x+=sign(d.x),k+=f.x)}debugRaycast&&debugLine(a,b,"#00f",.02)}class TileLayerData{constructor(a,b=0,c=0,d=new Color){this.tile=a;this.direction=b;this.mirror=c;this.color=d}clear(){this.tile=this.direction=this.mirror=0;color=new Color}}class TileLayer extends EngineObject{constructor(a,b=tileCollisionSize,c=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;this.data=[];for(a=this.size.area();a--;)this.data.push(new TileLayerData)}setData(a,b,c){a.arrayCheck(this.size)&&(this.data[(a.y|0)*this.size.x+a.x|0]=b,c&&this.drawTileData(a))}getData(a){return a.arrayCheck(this.size)&&this.data[(a.y|0)*this.size.x+a.x|0]}update(){}render(){ASSERT(mainContext!=this.context);glEnable&&!glOverlay&&!this.isOverlay&&glCopyToContext(mainContext);const a=worldToScreen(this.pos.add(vec2(0,this.size.y*this.scale.y)));(this.isOverlay?overlayContext:mainContext).drawImage(this.canvas,a.x,a.y,cameraScale*this.size.x*this.scale.x,cameraScale*this.size.y*this.scale.y)}redraw(){this.redrawStart(1);this.drawAllTileData();this.redrawEnd()}redrawStart(a=0){this.savedRenderSettings=[mainCanvas,mainContext,mainCanvasSize,cameraPos,cameraScale];mainCanvas=this.canvas;mainContext=this.context;cameraPos=this.size.scale(.5);cameraScale=this.tileInfo.size.x;a&&(mainCanvas.width=this.size.x*this.tileInfo.size.x,mainCanvas.height=this.size.y*this.tileInfo.size.y);enginePreRender()}redrawEnd(){ASSERT(mainContext==this.context);glEnable&&glCopyToContext(mainContext,1);[mainCanvas,mainContext,mainCanvasSize,cameraPos,cameraScale]=this.savedRenderSettings}drawTileData(a){const b=a.floor().add(this.pos).add(vec2(.5));this.drawCanvas2D(b,vec2(1),0,0,c=>c.clearRect(-.5,-.5,1,1));a=this.getData(a);if(void 0!=a.tile){ASSERT(mainContext==this.context);const c=tile(a.tile,this.tileInfo.size,this.tileInfo.textureIndex);drawTile(b,vec2(1),c,a.color,a.direction*PI/2,a.mirror)}}drawAllTileData(){for(let a=this.size.x;a--;)for(let b=this.size.y;b--;)this.drawTileData(vec2(a,b))}drawCanvas2D(a,b,c,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,-1,0,c,d)}}class ParticleEmitter extends EngineObject{constructor(a,b,c=0,d=0,e=100,f=PI,g,k=new Color,h=new Color,l=new Color(1,1,1,0),m=new Color(1,1,1,0),n=.5,p=.1,t=1,q=.1,x=.05,u=1,B=1,y=0,C=PI,v=.1,E=.2,A,w,F=1,r=w?1e9:0,z){super(a,vec2(),g,b,void 0,r);this.emitSize=c;this.emitTime=d;this.emitRate=e;this.emitConeAngle=f;this.colorStartA=k;this.colorStartB=h;this.colorEndA=l;this.colorEndB=m;this.randomColorLinear=F;this.particleTime=n;this.sizeStart=p;this.sizeEnd=t;this.speed=q;this.angleSpeed=x;this.damping=u;this.angleDamping=B;this.gravityScale=y;this.particleConeAngle=C;this.fadeRate=v;this.randomness=E;this.collideTiles=A;this.additive=w;this.localSpace=z;this.emitTimeBuffer=this.trailScale=0}update(){this.parent&&super.update();if(!this.emitTime||this.getAliveTime()<=this.emitTime){if(this.emitRate*particleEmitRateScale){const a=1/this.emitRate/particleEmitRateScale;for(this.emitTimeBuffer+=timeDelta;0<this.emitTimeBuffer;this.emitTimeBuffer-=a)this.emitParticle()}}else this.destroy();debugParticles&&debugRect(this.pos,vec2(this.emitSize),"#0f0",0,this.angle)}emitParticle(){var a=void 0!=this.emitSize.x?vec2(rand(-.5,.5),rand(-.5,.5)).multiply(this.emitSize).rotate(this.angle):randInCircle(this.emitSize/2),b=rand(this.particleConeAngle,-this.particleConeAngle);this.localSpace||(a=this.pos.add(a),b+=this.angle);a=new Particle(a,this.tileInfo,this.tileSize,b);const c=this.randomness;var d=m=>m+m*rand(c,-c);b=d(this.particleTime);const e=d(this.sizeStart),f=d(this.sizeEnd),g=d(this.speed);d=d(this.angleSpeed)*randSign();var k=rand(this.emitConeAngle,-this.emitConeAngle);const h=randColor(this.colorStartA,this.colorStartB,this.randomColorLinear),l=randColor(this.colorEndA,this.colorEndB,this.randomColorLinear);k=this.localSpace?k:this.angle+k;a.colorStart=h;a.colorEndDelta=l.subtract(h);a.velocity=vec2().setAngle(k,g);a.angleVelocity=d;a.lifeTime=b;a.sizeStart=e;a.sizeEndDelta=f-e;a.fadeRate=this.fadeRate;a.damping=this.damping;a.angleDamping=this.angleDamping;a.elasticity=this.elasticity;a.friction=this.friction;a.gravityScale=this.gravityScale;a.collideTiles=this.collideTiles;a.additive=this.additive;a.renderOrder=this.renderOrder;a.trailScale=this.trailScale;a.mirror=randInt(2);a.localSpaceEmitter=this.localSpace&&this;a.destroyCallback=this.particleDestroyCallback;this.particleCreateCallback&&this.particleCreateCallback(a);return a}render(){}}class Particle extends EngineObject{constructor(a,b,c){super(a,vec2(),b,c)}render(){const a=min((time-this.spawnTime)/this.lifeTime,1),b=vec2(this.sizeStart+a*this.sizeEndDelta);var c=this.fadeRate/2;c=new Color(this.colorStart.r+a*this.colorEndDelta.r,this.colorStart.g+a*this.colorEndDelta.g,this.colorStart.b+a*this.colorEndDelta.b,(this.colorStart.a+a*this.colorEndDelta.a)*(a<c?a/c:a>1-c?(1-a)/c:1));this.additive&&setBlendMode(1);let d=this.pos,e=this.angle;this.localSpaceEmitter&&(d=this.localSpaceEmitter.pos.add(d.rotate(-this.localSpaceEmitter.angle)),e+=this.localSpaceEmitter.angle);if(this.trailScale){var f=this.velocity;this.localSpaceEmitter&&(f=f.rotate(-this.localSpaceEmitter.angle));var g=f.length();g&&(f=f.scale(1/g),g*=this.trailScale,b.y=max(b.x,g),e=f.angle(),drawTile(d.add(f.multiply(vec2(0,-g/2))),b,this.tileInfo,c,e,this.mirror))}else drawTile(d,b,this.tileInfo,c,e,this.mirror);this.additive&&setBlendMode();debugParticles&&debugRect(d,b,"#f005",0,e);1==a&&(this.color=c,this.size=b,this.destroyCallback&&this.destroyCallback(this),this.destroyed=1)}}const medals=[];let medalsDisplayQueue=[],medalsSaveName,medalsDisplayTimeLast;function medalsInit(a){medalsSaveName=a;debugMedals||medals.forEach(b=>b.unlocked=localStorage[b.storageKey()]|0)}class Medal{constructor(a,b,c="",d="🏆",e){ASSERT(0<=a&&!medals[a]);medals[this.id=a]=this;this.name=b;this.description=c;this.icon=d;e&&((this.image=new Image).src=e)}unlock(){medalsPreventUnlock||this.unlocked||(ASSERT(medalsSaveName),localStorage[this.storageKey()]=this.unlocked=1,medalsDisplayQueue.push(this),newgrounds&&newgrounds.unlockMedal(this.id))}render(a=0){const b=overlayContext;var c=min(medalDisplaySize.x,mainCanvas.width);const d=overlayCanvas.width-c;a*=-medalDisplaySize.y;b.save();b.beginPath();b.fillStyle=new Color(.9,.9,.9);b.strokeStyle=new Color(0,0,0);b.lineWidth=3;b.fill(b.rect(d,a,c,medalDisplaySize.y));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,0,"left");c.y+=32;drawTextScreen(this.description,c,24,new Color(0,0,0),0,0,"left");b.restore()}renderIcon(a,b=medalDisplayIconSize){this.image?overlayContext.drawImage(this.image,a.x-b/2,a.y-b/2,b,b):drawTextScreen(this.icon,a,.7*b,new Color(0,0,0))}storageKey(){return medalsSaveName+"_"+this.id}}function medalsRender(){if(medalsDisplayQueue.length){var a=medalsDisplayQueue[0],b=timeReal-medalsDisplayTimeLast;if(medalsDisplayTimeLast)if(b>medalDisplayTime)medalsDisplayQueue.shift(medalsDisplayTimeLast=0);else{const c=medalDisplayTime-medalDisplaySlideTime;a.render(b<medalDisplaySlideTime?1-b/medalDisplaySlideTime:b>c?(b-c)/medalDisplaySlideTime:0)}else medalsDisplayTimeLast=timeReal}}let newgrounds;function newgroundsInit(a,b,c){newgrounds=new Newgrounds(a,b,c)}class Newgrounds{constructor(a,b,c){ASSERT(!newgrounds&&a);ASSERT(!b||c);this.app_id=a;this.cipher=b;this.cryptoJS=c;this.host=location?location.hostname:"";if(this.session_id=new URL(location.href).searchParams.get("ngio_session_id")){this.medals=(a=this.call("Medal.getList"))?a.result.data.medals:[];debugMedals&&console.log(this.medals);for(var d of this.medals)if(a=medals[d.id])a.image=new Image,a.image.src=d.icon,a.name=d.name,a.description=d.description,a.unlocked=d.unlocked,a.difficulty=d.difficulty,a.value=d.value,a.value&&(a.description=a.description+" ("+a.value+")");this.scoreboards=(d=this.call("ScoreBoard.getBoards"))?d.result.data.scoreboards:[];debugMedals&&console.log(this.scoreboards);setInterval(()=>this.call("Gateway.ping",0,1),3e5)}}unlockMedal(a){return this.call("Medal.unlock",{id:a},1)}postScore(a,b){return this.call("ScoreBoard.postScore",{id:a,value:b},1)}getScores(a,b=0,c=0,d=0,e=10){return this.call("ScoreBoard.getScores",{id:a,user:b,social:c,skip:d,limit:e})}logView(){return this.call("App.logView",{host:this.host},1)}call(a,b=0,c=0){a={component:a,parameters:b};if(this.cipher){b=this.cryptoJS;var d=b.enc.Base64.parse(this.cipher);const e=b.lib.WordArray.random(16);d=b.AES.encrypt(JSON.stringify(a),d,{iv:e});a.secure=b.enc.Base64.stringify(e.concat(d.ciphertext));a.parameters=0}b={app_id:this.app_id,session_id:this.session_id,call:a};a=new FormData;a.append("input",JSON.stringify(b));b=new XMLHttpRequest;b.open("POST","https://newgrounds.io/gateway_v3.php",!debugMedals&&c);b.send(a);debugMedals&&console.log(b.responseText);return b.responseText&&JSON.parse(b.responseText)}}let glCanvas,glContext,glActiveTexture,glShader,glArrayBuffer,glVertexData,glPositionData,glColorData,glBatchCount,glBatchAdditive,glAdditive;function glInit(){glCanvas=document.createElement("canvas");glContext=glCanvas.getContext("webgl2");glOverlay&&document.body.appendChild(glCanvas);glShader=glCreateProgram("#version 300 es\nprecision highp float;uniform mat4 m;in vec4 p,c,a;out vec4 v,d,e;void main(){gl_Position=m*vec4(p.xy,1,1);v=p;d=c;e=a;}","#version 300 es\nprecision highp float;in vec4 v,d,e;uniform sampler2D s;out vec4 c;void main(){c=texture(s,v.zw)*d+e;}");glVertexData=new ArrayBuffer(gl_VERTEX_BUFFER_SIZE);glPositionData=new Float32Array(glVertexData);glColorData=new Uint32Array(glVertexData);glArrayBuffer=glContext.createBuffer();glBatchCount=0}function glPreRender(){glContext.viewport(0,0,glCanvas.width=mainCanvas.width,glCanvas.height=mainCanvas.height);glContext.clear(gl_COLOR_BUFFER_BIT);glContext.useProgram(glShader);glContext.activeTexture(gl_TEXTURE0);glContext.bindTexture(gl_TEXTURE_2D,glActiveTexture=textureInfos[0].glTexture);glContext.bindBuffer(gl_ARRAY_BUFFER,glArrayBuffer);glContext.bufferData(gl_ARRAY_BUFFER,gl_VERTEX_BUFFER_SIZE,gl_DYNAMIC_DRAW);let a=glAdditive=0;var b=(f,g,k,h,l=0)=>{f=glContext.getAttribLocation(glShader,f);glContext.enableVertexAttribArray(f);glContext.vertexAttribPointer(f,h,g,l,gl_VERTEX_BYTE_STRIDE,a);a+=h*k};b("p",gl_FLOAT,4,4);b("c",gl_UNSIGNED_BYTE,1,4,1);b("a",gl_UNSIGNED_BYTE,1,4,1);b=2*cameraScale/mainCanvas.width;const c=2*cameraScale/mainCanvas.height,d=-1-b*cameraPos.x,e=-1-c*cameraPos.y;glContext.uniformMatrix4fv(glContext.getUniformLocation(glShader,"m"),0,new Float32Array([b,0,0,0,0,c,0,0,1,1,-1,1,d,e,0,0]))}function glSetTexture(a){a!=glActiveTexture&&(glFlush(),glContext.bindTexture(gl_TEXTURE_2D,glActiveTexture=a))}function glCompileShader(a,b){b=glContext.createShader(b);glContext.shaderSource(b,a);glContext.compileShader(b);if(debug&&!glContext.getShaderParameter(b,gl_COMPILE_STATUS))throw glContext.getShaderInfoLog(b);return b}function glCreateProgram(a,b){const c=glContext.createProgram();glContext.attachShader(c,glCompileShader(a,gl_VERTEX_SHADER));glContext.attachShader(c,glCompileShader(b,gl_FRAGMENT_SHADER));glContext.linkProgram(c);if(debug&&!glContext.getProgramParameter(c,gl_LINK_STATUS))throw glContext.getProgramInfoLog(c);return c}function glCreateTexture(a){const b=glContext.createTexture();glContext.bindTexture(gl_TEXTURE_2D,b);a&&glContext.texImage2D(gl_TEXTURE_2D,0,gl_RGBA,gl_RGBA,gl_UNSIGNED_BYTE,a);a=canvasPixelated?gl_NEAREST:gl_LINEAR;glContext.texParameteri(gl_TEXTURE_2D,gl_TEXTURE_MIN_FILTER,a);glContext.texParameteri(gl_TEXTURE_2D,gl_TEXTURE_MAG_FILTER,a);glContext.texParameteri(gl_TEXTURE_2D,gl_TEXTURE_WRAP_S,gl_CLAMP_TO_EDGE);glContext.texParameteri(gl_TEXTURE_2D,gl_TEXTURE_WRAP_T,gl_CLAMP_TO_EDGE);return b}function glFlush(){if(glBatchCount){var a=glBatchAdditive?gl_ONE:gl_ONE_MINUS_SRC_ALPHA;glContext.blendFuncSeparate(gl_SRC_ALPHA,a,gl_ONE,a);glContext.enable(gl_BLEND);glContext.bufferSubData(gl_ARRAY_BUFFER,0,glVertexData);glContext.drawArrays(gl_TRIANGLE_STRIP,0,glBatchCount);glBatchCount=0;glBatchAdditive=glAdditive}}function glCopyToContext(a,b){if(glBatchCount||b)glFlush(),glOverlay&&!b||a.drawImage(glCanvas,0,0)}function glDraw(a,b,c,d,e,f,g,k,h,l,m=0){(glBatchCount>=gl_MAX_BATCH-6||glBatchAdditive!=glAdditive)&&glFlush();var n=Math.cos(e)/2;const p=Math.sin(e)/2;e=n*c;n*=d;c*=p;d*=p;a=[a-e+d,b+n+c,f,g,a-e-d,b-n+c,f,h,a+e+d,b+n-c,k,g,a+e-d,b-n-c,k,h];for(let t=6,q=glBatchCount*gl_INDICIES_PER_VERT;t--;)b=4*clamp(t-1,0,3),glPositionData[q++]=a[b+0],glPositionData[q++]=a[b+1],glPositionData[q++]=a[b+2],glPositionData[q++]=a[b+3],glColorData[q++]=l,glColorData[q++]=m;glBatchCount+=6}function glDrawPoints(a,b){const c=a.length+2;(glBatchCount>=gl_MAX_BATCH-c||glBatchAdditive!=glAdditive)&&glFlush();for(let e=c,f=glBatchCount*gl_INDICIES_PER_VERT;e--;){var d=clamp(e-1,0,c-3);const g=d>>1;d=a[d%2?g:c-3-g];glPositionData[f++]=d.x;glPositionData[f++]=d.y;glPositionData[f++]=0;glPositionData[f++]=0;glColorData[f++]=0;glColorData[f++]=b}glBatchCount+=c}let glPostShader,glPostArrayBuffer,glPostTexture,glPostIncludeOverlay;function glInitPostProcess(a,b){ASSERT(!glPostShader);a||="void mainImage(out vec4 c,vec2 p){c=texture(iChannel0,p/iResolution.xy);}";glPostShader=glCreateProgram("#version 300 es\nprecision highp float;in vec2 p;void main(){gl_Position=vec4(p,1,1);}","#version 300 es\nprecision highp float;uniform sampler2D iChannel0;uniform vec3 iResolution;uniform float iTime;out vec4 c;\n"+a+"\nvoid main(){mainImage(c,gl_FragCoord.xy);c.a=1.;}");glPostArrayBuffer=glContext.createBuffer();glPostTexture=glCreateTexture();glPostIncludeOverlay=b;mainCanvas.style.visibility="hidden"}function glRenderPostProcess(){if(glPostShader){glEnable?(glFlush(),mainContext.drawImage(glCanvas,0,0)):glContext.viewport(0,0,glCanvas.width=mainCanvas.width,glCanvas.height=mainCanvas.height);glPostIncludeOverlay&&(mainContext.drawImage(overlayCanvas,0,0),overlayCanvas.width=mainCanvas.width);glContext.useProgram(glPostShader);glContext.disable(gl_BLEND);glContext.bindBuffer(gl_ARRAY_BUFFER,glPostArrayBuffer);glContext.bufferData(gl_ARRAY_BUFFER,new Float32Array([-3,1,1,-3,1,1]),gl_STATIC_DRAW);glContext.pixelStorei(gl_UNPACK_FLIP_Y_WEBGL,!0);glContext.activeTexture(gl_TEXTURE0);glContext.bindTexture(gl_TEXTURE_2D,glPostTexture);glContext.texImage2D(gl_TEXTURE_2D,0,gl_RGBA,gl_RGBA,gl_UNSIGNED_BYTE,mainCanvas);var a=glContext.getAttribLocation(glPostShader,"p");glContext.enableVertexAttribArray(a);glContext.vertexAttribPointer(a,2,gl_FLOAT,0,8,0);glContext.uniform1i(glContext.getUniformLocation(glPostShader,"iChannel0"),0);glContext.uniform1f(glContext.getUniformLocation(glPostShader,"iTime"),time);glContext.uniform3f(glContext.getUniformLocation(glPostShader,"iResolution"),mainCanvas.width,mainCanvas.height,1);glContext.drawArrays(gl_TRIANGLE_STRIP,0,3)}}const gl_ONE=1,gl_TRIANGLE_STRIP=5,gl_SRC_ALPHA=770,gl_ONE_MINUS_SRC_ALPHA=771,gl_BLEND=3042,gl_TEXTURE_2D=3553,gl_UNSIGNED_BYTE=5121,gl_FLOAT=5126,gl_RGBA=6408,gl_NEAREST=9728,gl_LINEAR=9729,gl_TEXTURE_MAG_FILTER=10240,gl_TEXTURE_MIN_FILTER=10241,gl_TEXTURE_WRAP_S=10242,gl_TEXTURE_WRAP_T=10243,gl_COLOR_BUFFER_BIT=16384,gl_CLAMP_TO_EDGE=33071,gl_TEXTURE0=33984,gl_ARRAY_BUFFER=34962,gl_STATIC_DRAW=35044,gl_DYNAMIC_DRAW=35048,gl_FRAGMENT_SHADER=35632,gl_VERTEX_SHADER=35633,gl_COMPILE_STATUS=35713,gl_LINK_STATUS=35714,gl_UNPACK_FLIP_Y_WEBGL=37440,gl_INDICIES_PER_VERT=6,gl_MAX_BATCH=1e5,gl_VERTEX_BYTE_STRIDE=24,gl_VERTEX_BUFFER_SIZE=gl_MAX_BATCH*gl_VERTEX_BYTE_STRIDE,engineName="LittleJS",engineVersion="1.8.8",frameRate=60,timeDelta=1/frameRate;let engineObjects=[],engineObjectsCollide=[],frame=0,time=0,timeReal=0,paused=0;function setPaused(a){paused=a}let frameTimeLastMS=0,frameTimeBufferMS=0,averageFPS=0;function engineInit(a,b,c,d,e,f=["tiles.png"]){function g(k=0){var h=k-frameTimeLastMS;frameTimeLastMS=k;if(debug||showWatermark)averageFPS=lerp(.05,averageFPS,1e3/(h||1));k=debug&&keyIsDown(107);const l=debug&&keyIsDown(109);debug&&(h*=k?5:l?.2:1);timeReal+=h/1e3;frameTimeBufferMS+=!paused*h;k||(frameTimeBufferMS=min(frameTimeBufferMS,50));canvasFixedSize.x?(mainCanvas.width=canvasFixedSize.x,mainCanvas.height=canvasFixedSize.y,h=innerWidth/innerHeight,k=mainCanvas.width/mainCanvas.height,(glCanvas||mainCanvas).style.width=mainCanvas.style.width=overlayCanvas.style.width=h<k?"100%":"",(glCanvas||mainCanvas).style.height=mainCanvas.style.height=overlayCanvas.style.height=h<k?"":"100%"):(mainCanvas.width=min(innerWidth,canvasMaxSize.x),mainCanvas.height=min(innerHeight,canvasMaxSize.y));overlayCanvas.width=mainCanvas.width;overlayCanvas.height=mainCanvas.height;mainCanvasSize=vec2(mainCanvas.width,mainCanvas.height);if(paused)inputUpdate(),debugUpdate(),c(),inputUpdatePost();else{h=0;0>frameTimeBufferMS&&-9<frameTimeBufferMS&&(h=frameTimeBufferMS,frameTimeBufferMS=0);for(;0<=frameTimeBufferMS;frameTimeBufferMS-=1e3/frameRate)time=frame++/frameRate,inputUpdate(),b(),engineObjectsUpdate(),debugUpdate(),c(),inputUpdatePost();frameTimeBufferMS+=h}enginePreRender();d();engineObjects.sort((n,p)=>n.renderOrder-p.renderOrder);for(var m of engineObjects)m.destroyed||m.render();e();glRenderPostProcess();medalsRender();touchGamepadRender();debugRender();glEnable&&glCopyToContext(mainContext);showWatermark&&(overlayContext.textAlign="right",overlayContext.textBaseline="top",overlayContext.font="1em monospace",overlayContext.fillStyle="#000",m=engineName+" v"+engineVersion+" / "+drawCount+" / "+engineObjects.length+" / "+averageFPS.toFixed(1)+(glEnable?" GL":" 2D"),overlayContext.fillText(m,mainCanvas.width-3,3),overlayContext.fillStyle="#fff",overlayContext.fillText(m,mainCanvas.width-2,2),drawCount=0);requestAnimationFrame(g)}ASSERT(Array.isArray(f));document.body.style="margin:0;overflow:hidden;background:#000;touch-action:none;user-select:none;-webkit-user-select:none;-webkit-touch-callout:none";document.body.appendChild(mainCanvas=document.createElement("canvas"));mainContext=mainCanvas.getContext("2d");debugInit();glEnable&&glInit();document.body.appendChild(overlayCanvas=document.createElement("canvas"));overlayContext=overlayCanvas.getContext("2d");(glCanvas||mainCanvas).style=mainCanvas.style=overlayCanvas.style="position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)";f=f.map((k,h)=>new Promise(l=>{const m=new Image;m.onerror=m.onload=()=>{textureInfos[h]=new TextureInfo(m);l()};m.src=k}));showSplashScreen&&f.push(new Promise(k=>{function h(){clearInput();drawEngineSplashScreen(l+=.01);1<l?k():setTimeout(h,16)}let l=0;console.log(`LittleJS Engine v${engineVersion}`);h()}));Promise.all(f).then(()=>{a();g()})}function enginePreRender(){mainCanvasSize=vec2(mainCanvas.width,mainCanvas.height);mainContext.imageSmoothingEnabled=!canvasPixelated;glEnable&&glPreRender()}function engineObjectsUpdate(){function a(b){if(!b.destroyed){b.update();for(const c of b.children)a(c)}}engineObjectsCollide=engineObjects.filter(b=>b.collideSolidObjects);for(const b of engineObjects)b.parent||a(b);engineObjects=engineObjects.filter(b=>!b.destroyed)}function engineObjectsDestroy(){for(const a of engineObjects)a.parent||a.destroy();engineObjects=engineObjects.filter(a=>!a.destroyed)}function engineObjectsCallback(a,b,c,d=engineObjects){if(a)if(void 0!=b.x)for(const e of d)isOverlapping(a,b,e.pos,e.size)&&c(e);else{b*=b;for(const e of d)a.distanceSquared(e.pos)<b&&c(e)}else for(const e of d)c(e)}function drawEngineSplashScreen(a){const b=mainContext;var c=mainCanvas.width=innerWidth,d=mainCanvas.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));g.addColorStop(1,hsl(0,0,0,e));b.save();b.fillStyle=g;b.fillRect(0,0,c,d);g=(h,l,m,n,p)=>{b.beginPath();b.rect(h,l,m,p?n*k:n);(b.fillStyle=p)?b.fill():b.stroke()};f=(h,l,m,n=0,p=2*PI,t,q)=>{const x=(n+p)/2;n=k*(p-n)/2;b.beginPath();q&&b.lineTo(h,l);b.arc(h,l,m,x-n,x+n);(b.fillStyle=t)?b.fill():b.stroke()};e=(h=0,l=0)=>hsl([.98,.3,.57,.14][h%4]-10,.8,[0,.3,.5,.8,.9][l]);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+k;c=percent(a,.1,1);b.setLineDash([99*c,99]);g(7,17,18,-8,e(2,2));g(7,9,18,4,e(2,3));g(25,9,8,8,e(2,1));g(25,9,-18,8);g(25,9,8,8);g(25,17,7,22,e());g(11,40,14,-23,e(1,1));g(11,17,14,17,e(1,2));g(11,17,14,9,e(1,3));g(15,31,6,-9,e(2,2));f(15,23,5,0,PI/2,e(2,4),1);g(25,17,-14,23);g(21,22,-6,9);g(37,14,9,6,e(3,2));g(37,14,4,6,e(3,3));g(37,14,9,6);g(50,20,10,-10,e(0,1));g(50,20,6,-10,e(0,2));g(50,20,3,-10,e(0,3));g(50,10,10,10);f(55,2,11,.5,PI-.5,e(3,3));f(55,2,11,.5,PI/2,e(3,2),1);f(55,2,11,.5,PI-.5);g(45,7,20,-7,e(0,2));g(45,0,20,3,e(0,3));g(45,0,20,7);for(c=5;c--;)f(60-6*c,30,9.9,0,2*PI,e(c+2,3)),f(60-6*c,30,10,-.5,PI+.5,e(c+2,2)),f(60-6*c,30,10.1,.5,PI-.5,e(c+2,1));f(36,30,10,PI/2,3*PI/2);f(47,30,10,PI/2,3*PI/2);f(60,30,10);b.beginPath();b.lineTo(36,20);b.lineTo(60,20);b.stroke();f(60,30,4,PI,3*PI,e(3,2));f(60,30,4,PI,2*PI,e(3,3));f(60,30,4,PI,3*PI);for(c=6;c--;)b.beginPath(),b.lineTo(53,54),b.lineTo(53,40),b.lineTo(53+(1+2.9*c)*k,40),b.lineTo(53+(4+3.5*c)*k,54),b.fillStyle=e(0,c%2+2),b.fill()||c%2&&b.stroke();g(5,40,9,6,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*k;g=0;for(c=0;c<f.length;++c)g+=b.measureText(f[c]).width;for(c=2;c--;)for(let h=0,l=41-g/2;h<f.length;++h)b.fillStyle=e(h,2),d=b.measureText(f[h]).width,b[c?"strokeText":"fillText"](f[h],l+d/2,55.5,17*k),l+=d;b.restore()}
|
|
1
|
+
let showWatermark=0,debugKey=0;const debug=0,debugOverlay=0,debugPhysics=0,debugParticles=0,debugRaycast=0,debugGamepads=0,debugMedals=0;function ASSERT(){}function debugInit(){}function debugUpdate(){}function debugRender(){}function debugRect(){}function debugCircle(){}function debugPoint(){}function debugLine(){}function debugAABB(){}function debugText(){}function debugClear(){}function debugSaveCanvas(){}const PI=Math.PI;function abs(a){return Math.abs(a)}function min(a,b){return Math.min(a,b)}function max(a,b){return Math.max(a,b)}function sign(a){return Math.sign(a)}function mod(a,b=1){return(a%b+b)%b}function clamp(a,b=0,c=1){return a<b?b:a>c?c:a}function percent(a,b,c){return c-b?clamp((a-b)/(c-b)):0}function lerp(a,b,c){return b+clamp(a)*(c-b)}function distanceWrap(a,b,c=1){a=(a-b)%c;return 2*a%c-a}function lerpWrap(a,b,c,d=1){return c+clamp(a)*distanceWrap(b,c,d)}function distanceAngle(a,b){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){return 2*abs(a.x-c.x)<b.x+d.x&&2*abs(a.y-c.y)<b.y+d.y}function wave(a=1,b=1,c=time){return b/2*(1-Math.cos(c*a*2*PI))}function formatTime(a){return(a/60|0)+":"+(10>a%60?"0":"")+(a%60|0)}function rand(a=1,b=0){return b+Math.random()*(a-b)}function randInt(a,b=0){return Math.floor(rand(a,b))}function randSign(){return 2*randInt(2)-1}function randVector(a=1){return(new Vector2).setAngle(rand(2*PI),a)}function randInCircle(a=1,b=0){return 0<a?randVector(a*rand(b/a,1)**.5):new Vector2}function randColor(a=new Color,b=new Color(0,0,0,1),c){return c?a.lerp(b,rand()):new Color(rand(a.r,b.r),rand(a.g,b.g),rand(a.b,b.b),rand(a.a,b.a))}class RandomGenerator{constructor(a){this.seed=a}float(a=1,b=0){this.seed^=this.seed<<13;this.seed^=this.seed>>>17;this.seed^=this.seed<<5;return b+(a-b)*abs(this.seed%1e9)/1e9}int(a,b=0){return Math.floor(this.float(a,b))}sign(){return 2*this.int(2)-1}}function vec2(a=0,b){return void 0==a.x?new Vector2(a,void 0==b?a:b):new Vector2(a.x,a.y)}function isVector2(a){return!isNaN(a.x)&&!isNaN(a.y)}class Vector2{constructor(a=0,b=0){this.x=a;this.y=b}copy(){return new Vector2(this.x,this.y)}add(a){ASSERT(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){return this.distanceSquared(a)**.5}distanceSquared(a){return(this.x-a.x)**2+(this.y-a.y)**2}normalize(a=1){const b=this.length();return b?this.scale(a/b):new Vector2(0,a)}clampLength(a=1){const b=this.length();return b>a?this.scale(a/b):this}dot(a){ASSERT(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)}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){return 0<=this.x&&0<=this.y&&this.x<a.x&&this.y<a.y}toString(a=3){if(debug)return`(${(0>this.x?"":" ")+this.x.toFixed(a)},${(0>this.y?"":" ")+this.y.toFixed(a)} )`}}function rgb(a,b,c,d){return new Color(a,b,c,d)}function hsl(a,b,c,d){return(new Color).setHSLA(a,b,c,d)}class Color{constructor(a=1,b=1,c=1,d=1){this.r=a;this.g=b;this.b=c;this.a=d}copy(){return new Color(this.r,this.g,this.b,this.a)}add(a){return new Color(this.r+a.r,this.g+a.g,this.b+a.b,this.a+a.a)}subtract(a){return new Color(this.r-a.r,this.g-a.g,this.b-a.b,this.a-a.a)}multiply(a){return new Color(this.r*a.r,this.g*a.g,this.b*a.b,this.a*a.a)}divide(a){return new Color(this.r/a.r,this.g/a.g,this.b/a.b,this.a/a.a)}scale(a,b=a){return new Color(this.r*a,this.g*a,this.b*a,this.a*b)}clamp(){return new Color(clamp(this.r),clamp(this.g),clamp(this.b),clamp(this.a))}lerp(a,b){return this.add(a.subtract(this).scale(clamp(b)))}setHSLA(a=0,b=0,c=1,d=1){b=.5>c?c*(1+b):c+b-c*b;c=2*c-b;const e=(f,g,k)=>(k=(k%1+1)%1)<1/6?f+6*(g-f)*k:.5>k?g:k<2/3?f+(g-f)*(2/3-k)*6:f;this.r=e(c,b,a+1/3);this.g=e(c,b,a);this.b=e(c,b,a-1/3);this.a=d;return this}getHSLA(){const a=clamp(this.r),b=clamp(this.g),c=clamp(this.b),d=clamp(this.a),e=Math.max(a,b,c),f=Math.min(a,b,c),g=(e+f)/2;let k=0,h=0;if(e!=f){let l=e-f;h=.5<g?l/(2-e-f):l/(e+f);a==e?k=(b-c)/l+(b<c?6:0):b==e?k=(c-a)/l+2:c==e&&(k=(a-b)/l+4)}return[k/6,h,g,d]}mutate(a=.05,b=0){return new Color(this.r+rand(a,-a),this.g+rand(a,-a),this.b+rand(a,-a),this.a+rand(b,-b)).clamp()}toString(a=1){const b=c=>(16>(c=255*c|0)?"0":"")+c.toString(16);return"#"+b(this.r)+b(this.g)+b(this.b)+(a?b(this.a):"")}setHex(a){this.r=clamp(parseInt(a.slice(1,3),16)/255);this.g=clamp(parseInt(a.slice(3,5),16)/255);this.b=clamp(parseInt(a.slice(5,7),16)/255);this.a=7<a.length?clamp(parseInt(a.slice(7,9),16)/255):1;return this}rgbaInt(){const a=255*clamp(this.r)|0,b=255*clamp(this.g)<<8,c=255*clamp(this.b)<<16,d=255*clamp(this.a)<<24;return a+b+c+d}}class Timer{constructor(a){this.time=void 0==a?void 0:time+a;this.setTime=a}set(a=0){this.time=time+a;this.setTime=a}unset(){this.time=void 0}isSet(){return void 0!=this.time}active(){return time<=this.time}elapsed(){return time>this.time}get(){return this.isSet()?time-this.time:0}getPercent(){return this.isSet()?percent(this.time-time,this.setTime,0):0}toString(){if(debug)return this.isSet()?Math.abs(this.get())+" seconds "+(0>this.get()?"before":"after"):"unset"}valueOf(){return this.get()}}let cameraPos=vec2(),cameraScale=32,canvasMaxSize=vec2(1920,1200),canvasFixedSize=vec2(),canvasPixelated=1,fontDefault="arial",showSplashScreen=0,glEnable=1,glOverlay=1,tileSizeDefault=vec2(16),tileFixBleedScale=.3,enablePhysicsSolver=1,objectDefaultMass=1,objectDefaultDamping=1,objectDefaultAngleDamping=1,objectDefaultElasticity=0,objectDefaultFriction=.8,objectMaxSpeed=1,gravity=0,particleEmitRateScale=1,gamepadsEnable=1,gamepadDirectionEmulateStick=1,inputWASDEmulateDirection=1,touchGamepadEnable=0,touchGamepadAnalog=1,touchGamepadSize=99,touchGamepadAlpha=.3,vibrateEnable=1,soundEnable=1,soundVolume=.5,soundDefaultRange=40,soundDefaultTaper=.7,medalDisplayTime=5,medalDisplaySlideTime=.5,medalDisplaySize=vec2(640,80),medalDisplayIconSize=50,medalsPreventUnlock;function setCameraPos(a){cameraPos=a}function setCameraScale(a){cameraScale=a}function setCanvasMaxSize(a){canvasMaxSize=a}function setCanvasFixedSize(a){canvasFixedSize=a}function setCanvasPixelated(a){canvasPixelated=a}function setFontDefault(a){fontDefault=a}function setShowSplashScreen(a){showSplashScreen=a}function setGlEnable(a){glEnable=a}function setGlOverlay(a){glOverlay=a}function setTileSizeDefault(a){tileSizeDefault=a}function setTileFixBleedScale(a){tileFixBleedScale=a}function setEnablePhysicsSolver(a){enablePhysicsSolver=a}function setObjectDefaultMass(a){objectDefaultMass=a}function setObjectDefaultDamping(a){objectDefaultDamping=a}function setObjectDefaultAngleDamping(a){objectDefaultAngleDamping=a}function setObjectDefaultElasticity(a){objectDefaultElasticity=a}function setObjectDefaultFriction(a){objectDefaultFriction=a}function setObjectMaxSpeed(a){objectMaxSpeed=a}function setGravity(a){gravity=a}function setParticleEmitRateScale(a){particleEmitRateScale=a}function setGamepadsEnable(a){gamepadsEnable=a}function setGamepadDirectionEmulateStick(a){gamepadDirectionEmulateStick=a}function setInputWASDEmulateDirection(a){inputWASDEmulateDirection=a}function setTouchGamepadEnable(a){touchGamepadEnable=a}function setTouchGamepadAnalog(a){touchGamepadAnalog=a}function setTouchGamepadSize(a){touchGamepadSize=a}function setTouchGamepadAlpha(a){touchGamepadAlpha=a}function setVibrateEnable(a){vibrateEnable=a}function setSoundEnable(a){soundEnable=a}function setSoundVolume(a){soundVolume=a}function setSoundDefaultRange(a){soundDefaultRange=a}function setSoundDefaultTaper(a){soundDefaultTaper=a}function setMedalDisplayTime(a){medalDisplayTime=a}function setMedalDisplaySlideTime(a){medalDisplaySlideTime=a}function setMedalDisplaySize(a){medalDisplaySize=a}function setMedalDisplayIconSize(a){medalDisplayIconSize=a}function setMedalsPreventUnlock(a){medalsPreventUnlock=a}function setShowWatermark(a){showWatermark=a}function setDebugKey(a){debugKey=a}class EngineObject{constructor(a=vec2(),b=vec2(1),c,d=0,e,f=0){ASSERT(isVector2(a)&&isVector2(b));ASSERT("number"!==typeof c||!c);ASSERT(!(f instanceof Color));this.pos=a.copy();this.size=b;this.drawSize;this.tileInfo=c;this.angle=d;this.color=e;this.additiveColor;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.collideTiles=!1;engineObjects.push(this)}update(){var a=this.parent;if(a)this.pos=this.localPos.multiply(vec2(a.getMirrorSign(),1)).rotate(-a.angle).add(a.pos),this.angle=a.getMirrorSign()*this.localAngle+a.angle;else if(this.velocity.x=clamp(this.velocity.x,-objectMaxSpeed,objectMaxSpeed),this.velocity.y=clamp(this.velocity.y,-objectMaxSpeed,objectMaxSpeed),a=this.pos.copy(),this.velocity.y+=gravity*this.gravityScale,this.pos.x+=this.velocity.x*=this.damping,this.pos.y+=this.velocity.y*=this.damping,this.angle+=this.angleVelocity*=this.angleDamping,ASSERT(0<=this.angleDamping&&1>=this.angleDamping),ASSERT(0<=this.damping&&1>=this.damping),enablePhysicsSolver&&this.mass){var b=0>this.velocity.y;if(this.groundObject){var c=this.groundObject.velocity?this.groundObject.velocity.x:0;this.velocity.x=c+(this.velocity.x-c)*this.friction;this.groundObject=0}if(this.collideSolidObjects)for(var d of engineObjectsCollide){if(!this.isSolid&&!d.isSolid||d.destroyed||d.parent||d==this)continue;if(!isOverlapping(this.pos,this.size,d.pos,d.size))continue;c=this.collideWithObject(d);var e=d.collideWithObject(this);if(!c||!e)continue;if(isOverlapping(a,this.size,d.pos,d.size)){c=a.subtract(d.pos);e=c.length();c=.01>e?randVector(.001):c.scale(.001/e);this.velocity=this.velocity.add(c);d.mass&&(d.velocity=d.velocity.subtract(c));debugOverlay&&debugPhysics&&debugAABB(this.pos,this.size,d.pos,d.size,"#f00");continue}e=this.size.add(d.size);var f=2*(a.y-d.pos.y)>e.y+gravity;const k=2*abs(a.y-d.pos.y)<e.y;var g=2*abs(a.x-d.pos.x)<e.x;c=max(this.elasticity,d.elasticity);if(f||g||!k)if(this.pos.y=d.pos.y+(e.y/2+.001)*sign(a.y-d.pos.y),d.groundObject&&b||!d.mass)b&&(this.groundObject=d),this.velocity.y*=-c;else if(d.mass){g=(this.mass*this.velocity.y+d.mass*d.velocity.y)/(this.mass+d.mass);const h=d.velocity.y*(d.mass-this.mass)/(this.mass+d.mass)+2*this.velocity.y*this.mass/(this.mass+d.mass);this.velocity.y=lerp(c,g,this.velocity.y*(this.mass-d.mass)/(this.mass+d.mass)+2*d.velocity.y*d.mass/(this.mass+d.mass));d.velocity.y=lerp(c,g,h)}!f&&k&&(this.pos.x=d.pos.x+(e.x/2+.001)*sign(a.x-d.pos.x),d.mass?(e=(this.mass*this.velocity.x+d.mass*d.velocity.x)/(this.mass+d.mass),f=d.velocity.x*(d.mass-this.mass)/(this.mass+d.mass)+2*this.velocity.x*this.mass/(this.mass+d.mass),this.velocity.x=lerp(c,e,this.velocity.x*(this.mass-d.mass)/(this.mass+d.mass)+2*d.velocity.x*d.mass/(this.mass+d.mass)),d.velocity.x=lerp(c,e,f)):this.velocity.x*=-c);debugOverlay&&debugPhysics&&debugAABB(this.pos,this.size,d.pos,d.size,"#f0f")}if(this.collideTiles&&tileCollisionTest(this.pos,this.size,this)&&!tileCollisionTest(a,this.size,this)){c=tileCollisionTest(vec2(a.x,this.pos.y),this.size,this);d=tileCollisionTest(vec2(this.pos.x,a.y),this.size,this);if(c||!d)this.groundObject=b,this.velocity.y*=-this.elasticity,b=(a.y-this.size.y/2|0)-(a.y-this.size.y/2),0>b&&b>this.damping*this.velocity.y+gravity*this.gravityScale&&(this.velocity.y=this.damping?(b-gravity*this.gravityScale)/this.damping:0),this.pos.y=a.y;d&&(this.pos.x=a.x,this.velocity.x*=-this.elasticity)}}}render(){drawTile(this.pos,this.drawSize||this.size,this.tileInfo,this.color,this.angle,this.mirror,this.additiveColor)}destroy(){if(!this.destroyed){this.destroyed=1;this.parent&&this.parent.removeChild(this);for(const a of this.children)a.destroy(a.parent=0)}}collideWithTile(a,b){return 0<a}collideWithTileRaycast(a,b){return 0<a}collideWithObject(a){return 1}getAliveTime(){return time-this.spawnTime}applyAcceleration(a){this.mass&&(this.velocity=this.velocity.add(a))}applyForce(a){this.applyAcceleration(a.scale(1/this.mass))}getMirrorSign(){return this.mirror?-1:1}addChild(a,b=vec2(),c=0){ASSERT(!a.parent&&!this.children.includes(a));this.children.push(a);a.parent=this;a.localPos=b.copy();a.localAngle=c}removeChild(a){ASSERT(a.parent==this&&this.children.includes(a));this.children.splice(this.children.indexOf(a),1);a.parent=0}setCollision(a=1,b=1,c=1){ASSERT(a||!b);this.collideSolidObjects=a;this.isSolid=b;this.collideTiles=c}toString(){if(debug){let a="type = "+this.constructor.name;if(this.pos.x||this.pos.y)a+="\npos = "+this.pos;if(this.velocity.x||this.velocity.y)a+="\nvelocity = "+this.velocity;if(this.size.x||this.size.y)a+="\nsize = "+this.size;this.angle&&(a+="\nangle = "+this.angle.toFixed(3));this.color&&(a+="\ncolor = "+this.color);return a}}}let mainCanvas,mainContext,overlayCanvas,overlayContext,mainCanvasSize=vec2(),textureInfos=[],drawCount;function tile(a=vec2(),b=tileSizeDefault,c=0){void 0==b.x&&(ASSERT(0<b),b=vec2(b));if(void 0==a.x){var d=textureInfos[c];d?(d=d.size.x/b.x|0,a=vec2(a%d*b.x,(a/d|0)*b.y)):a=vec2()}return new TileInfo(a,b,c)}class TileInfo{constructor(a=vec2(),b=tileSizeDefault,c=0){this.pos=a;this.size=b;this.textureIndex=c}offset(a){return new TileInfo(this.pos.add(a),this.size,this.textureIndex)}getTextureInfo(){return textureInfos[this.textureIndex]}}class TextureInfo{constructor(a){this.image=a;this.size=vec2(a.width,a.height);this.glTexture=glEnable&&glCreateTexture(a);this.fixBleedSize=vec2(tileFixBleedScale).divide(this.size)}}function screenToWorld(a){return new Vector2((a.x-mainCanvasSize.x/2+.5)/cameraScale+cameraPos.x,(a.y-mainCanvasSize.y/2+.5)/-cameraScale+cameraPos.y)}function worldToScreen(a){return new Vector2((a.x-cameraPos.x)*cameraScale+mainCanvasSize.x/2-.5,(a.y-cameraPos.y)*-cameraScale+mainCanvasSize.y/2-.5)}function drawTile(a,b=vec2(1),c,d=new Color,e=0,f,g=new Color(0,0,0,0),k=glEnable,h,l){ASSERT(!l||!k);ASSERT("number"!==typeof c||!c);showWatermark&&++drawCount;const m=c&&c.getTextureInfo();if(k)if(h&&(a=screenToWorld(a),b=b.scale(1/cameraScale)),m){k=c.pos.x/m.size.x;h=c.pos.y/m.size.y;l=c.size.x/m.size.x;const n=c.size.y/m.size.y,p=m.fixBleedSize;glSetTexture(m.glTexture);glDraw(a.x,a.y,f?-b.x:b.x,b.y,e,k+p.x,h+p.y,k-p.x+l,h-p.y+n,d.rgbaInt(),g.rgbaInt())}else glDraw(a.x,a.y,b.x,b.y,e,0,0,0,0,0,d.rgbaInt());else drawCanvas2D(a,b,e,f,n=>{if(m){const p=c.pos.x+tileFixBleedScale,t=c.pos.y+tileFixBleedScale,q=c.size.x-2*tileFixBleedScale,x=c.size.y-2*tileFixBleedScale;n.globalAlpha=d.a;n.drawImage(m.image,p,t,q,x,-.5,-.5,1,1);n.globalAlpha=1}else n.fillStyle=d,n.fillRect(-.5,-.5,1,1)},h,l)}function drawRect(a,b,c,d,e,f,g){drawTile(a,b,void 0,c,d,0,void 0,e,f,g)}function drawPoly(a,b=new Color,c=glEnable,d,e){ASSERT(!e||!c);if(c)glDrawPoints(d?a.map(screenToWorld):a,b.rgbaInt());else{e||=mainContext;e.fillStyle=b;e.beginPath();for(const f of d?a:a.map(worldToScreen))e.lineTo(f.x,f.y);e.fill()}}function drawLine(a,b,c=.1,d,e,f,g){b=vec2((b.x-a.x)/2,(b.y-a.y)/2);c=vec2(c,2*b.length());drawRect(a.add(b),c,d,b.angle(),e,f,g)}function drawCanvas2D(a,b,c,d,e,f,g=mainContext){f||(a=worldToScreen(a),b=b.scale(cameraScale));g.save();g.translate(a.x+.5,a.y+.5);g.rotate(c);g.scale(d?-b.x:b.x,b.y);e(g);g.restore()}function setBlendMode(a,b=glEnable,c){ASSERT(!c||!b);b?glAdditive=a:(c||=mainContext,c.globalCompositeOperation=a?"lighter":"source-over")}function drawText(a,b,c=1,d,e=0,f,g,k,h){drawTextScreen(a,worldToScreen(b),c*cameraScale,d,e*cameraScale,f,g,k,h)}function drawTextScreen(a,b,c=1,d=new Color,e=0,f=new Color(0,0,0),g="center",k=fontDefault,h=overlayContext){h.fillStyle=d;h.lineWidth=e;h.strokeStyle=f;h.textAlign=g;h.font=c+"px "+k;h.textBaseline="middle";h.lineJoin="round";b=b.copy();(a+"").split("\n").forEach(l=>{e&&h.strokeText(l,b.x,b.y);h.fillText(l,b.x,b.y);b.y+=c})}let engineFontImage;class FontImage{constructor(a,b=vec2(8),c=vec2(0,1),d=overlayContext){engineFontImage||((engineFontImage=new Image).src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQAAAAAYAQAAAAA9+x6JAAAAAnRSTlMAAHaTzTgAAAGiSURBVHjaZZABhxxBEIUf6ECLBdFY+Q0PMNgf0yCgsSAGZcT9sgIPtBWwIA5wgAPEoHUyJeeSlW+gjK+fegWwtROWpVQEyWh2npdpBmTUFVhb29RINgLIukoXr5LIAvYQ5ve+1FqWEMqNKTX3FAJHyQDRZvmKWubAACcv5z5Gtg2oyCWE+Yk/8JZQX1jTTCpKAFGIgza+dJCNBF2UskRlsgwitHbSV0QLgt9sTPtsRlvJjEr8C/FARWA2bJ/TtJ7lko34dNDn6usJUMzuErP89UUBJbWeozrwLLncXczd508deAjLWipLO4Q5XGPcJvPu92cNDaN0P5G1FL0nSOzddZOrJ6rNhbXGmeDvO3TF7DeJWl4bvaYQTNHCTeuqKZmbjHaSOFes+IX/+IhHrnAkXOAsfn24EM68XieIECoccD4KZLk/odiwzeo2rovYdhvb2HYFgyznJyDpYJdYOmfXgVdJTaUi4xA2uWYNYec9BLeqdl9EsoTw582mSFDX2DxVLbNt9U3YYoeatBad1c2Tj8t2akrjaIGJNywKB/7h75/gN3vCMSaadIUTAAAAAElFTkSuQmCC");this.image=a||engineFontImage;this.tileSize=b;this.paddingSize=c;this.context=d}drawText(a,b,c=1,d){this.drawTextScreen(a,worldToScreen(b).floor(),c*cameraScale|0,d)}drawTextScreen(a,b,c=4,d){const e=this.context;e.save();e.imageSmoothingEnabled=!canvasPixelated;const f=this.tileSize,g=f.add(this.paddingSize).scale(c),k=this.image.width/this.tileSize.x|0;(a+"").split("\n").forEach((h,l)=>{const m=d?h.length*f.x*c/2|0:0;for(let t=h.length;t--;){var n=h[t].charCodeAt();if(32>n||127<n)n=127;var p=n-32;n=p%k;p=p/k|0;const q=b.add(vec2(t,l).multiply(g));e.drawImage(this.image,n*f.x,p*f.y,f.x,f.y,q.x-m,q.y,f.x*c,f.y*c)}});e.restore()}}function isFullscreen(){return document.fullscreenElement}function toggleFullscreen(){isFullscreen()?document.exitFullscreen&&document.exitFullscreen():document.body.requestFullscreen&&document.body.requestFullscreen()}function keyIsDown(a,b=0){return inputData[b]&&inputData[b][a]&1}function keyWasPressed(a,b=0){return inputData[b]&&inputData[b][a]&2?1:0}function keyWasReleased(a,b=0){return inputData[b]&&inputData[b][a]&4?1:0}function clearInput(){inputData=[[]]}const mouseIsDown=keyIsDown,mouseWasPressed=keyWasPressed,mouseWasReleased=keyWasReleased;let mousePos=vec2(),mousePosScreen=vec2(),mouseWheel=0,isUsingGamepad=0,preventDefaultInput=0;function gamepadIsDown(a,b=0){return keyIsDown(a,b+1)}function gamepadWasPressed(a,b=0){return keyWasPressed(a,b+1)}function gamepadWasReleased(a,b=0){return keyWasReleased(a,b+1)}function gamepadStick(a,b=0){return stickData[b]?stickData[b][a]||vec2():vec2()}let inputData=[[]];function inputUpdate(){isTouchDevice||document.hasFocus()||clearInput();mousePos=screenToWorld(mousePosScreen);gamepadsUpdate()}function inputUpdatePost(){for(const a of inputData)for(const b in a)a[b]&=1;mouseWheel=0}onkeydown=a=>{debug&&a.target!=document.body||(a.repeat||(inputData[isUsingGamepad=0][a.which]=3,inputWASDEmulateDirection&&(inputData[0][remapKey(a.which)]=3)),preventDefaultInput&&a.preventDefault())};onkeyup=a=>{debug&&a.target!=document.body||(inputData[0][a.which]=4,inputWASDEmulateDirection&&(inputData[0][remapKey(a.which)]=4))};function remapKey(a){return inputWASDEmulateDirection?87==a?38:83==a?40:65==a?37:68==a?39:a:a}onmousedown=a=>{inputData[isUsingGamepad=0][a.button]=3;onmousemove(a);a.button&&a.preventDefault()};onmouseup=a=>inputData[0][a.button]=inputData[0][a.button]&2|4;onmousemove=a=>mousePosScreen=mouseToScreen(a);onwheel=a=>a.ctrlKey||(mouseWheel=sign(a.deltaY));oncontextmenu=a=>!1;function mouseToScreen(a){if(!mainCanvas)return vec2();const b=mainCanvas.getBoundingClientRect();return vec2(mainCanvas.width,mainCanvas.height).multiply(vec2(percent(a.x,b.left,b.right),percent(a.y,b.top,b.bottom)))}const stickData=[];function gamepadsUpdate(){if(touchGamepadEnable&&isTouchDevice&&(touchGamepadButtons||createTouchGamepad(),touchGamepadTimer.isSet())){(stickData[0]||(stickData[0]=[]))[0]=vec2(touchGamepadStick.x,-touchGamepadStick.y);var a=inputData[1]||(inputData[1]=[]);for(var b=10;b--;){var c=3==b?2:2==b?3:b;a[c]=touchGamepadButtons[b]?1+2*!gamepadIsDown(c,0):4*gamepadIsDown(c,0)}}if(gamepadsEnable&&navigator&&navigator.getGamepads&&(document.hasFocus()||debug))for(a=navigator.getGamepads(),b=a.length;b--;){var d=a[b];const g=inputData[b+1]||(inputData[b+1]=[]);c=stickData[b]||(stickData[b]=[]);if(d){var e=k=>.3<k?percent(k,.3,.8):-.3>k?-percent(-k,.3,.8):0;for(var f=0;f<d.axes.length-1;f+=2)c[f>>1]=vec2(e(d.axes[f]),e(-d.axes[f+1])).clampLength();for(e=d.buttons.length;e--;)f=d.buttons[e],g[e]=f.pressed?1+2*!gamepadIsDown(e,b):4*gamepadIsDown(e,b),isUsingGamepad|=!b&&f.pressed,touchGamepadEnable&&touchGamepadTimer.unset();gamepadDirectionEmulateStick&&(d=vec2(gamepadIsDown(15,b)-gamepadIsDown(14,b),gamepadIsDown(12,b)-gamepadIsDown(13,b)),d.lengthSquared()&&(c[0]=d.clampLength()))}}}function vibrate(a){vibrateEnable&&navigator&&navigator.vibrate&&navigator.vibrate(a)}function vibrateStop(){vibrate(0)}const isTouchDevice=void 0!==window.ontouchstart;if(isTouchDevice){let a,b=onmousedown,c=onmouseup;onmousedown=onmouseup=()=>0;ontouchstart=ontouchmove=ontouchend=d=>{d.button=0;soundEnable&&(audioContext?audioContext.resume():zzfx(0));const e=d.touches.length;e?(d.x=d.touches[0].clientX,d.y=d.touches[0].clientY,a?onmousemove(d):b(d)):a&&c(d);a=e;document.hasFocus()&&d.preventDefault();return!0}}let touchGamepadTimer=new Timer,touchGamepadButtons,touchGamepadStick;function createTouchGamepad(){touchGamepadButtons=[];touchGamepadStick=vec2();const a=ontouchstart;ontouchstart=ontouchmove=ontouchend=b=>{touchGamepadStick=vec2();touchGamepadButtons=[];if(b.touches.length&&(touchGamepadTimer.set(),paused)){touchGamepadButtons[9]=1;return}const c=vec2(touchGamepadSize,mainCanvasSize.y-touchGamepadSize),d=mainCanvasSize.subtract(vec2(touchGamepadSize,touchGamepadSize)),e=mainCanvasSize.scale(.5);for(const g of b.touches){var f=mouseToScreen(vec2(g.clientX,g.clientY));f.distance(c)<touchGamepadSize?touchGamepadAnalog?touchGamepadStick=f.subtract(c).scale(2/touchGamepadSize).clampLength():(f=f.subtract(c).angle(),touchGamepadStick.setAngle((4*f/PI+8.5|0)*PI/4)):f.distance(d)<touchGamepadSize?(f=f.subtract(d).direction(),touchGamepadButtons[f]=1):f.distance(e)<touchGamepadSize&&(touchGamepadButtons[9]=1)}a(b);isUsingGamepad=1;return!0}}function touchGamepadRender(){if(touchGamepadEnable&&touchGamepadTimer.isSet()){var a=percent(touchGamepadTimer,4,3);if(a&&!paused){overlayContext.save();overlayContext.globalAlpha=a*touchGamepadAlpha;overlayContext.strokeStyle="#fff";overlayContext.lineWidth=3;overlayContext.fillStyle=0<touchGamepadStick.lengthSquared()?"#fff":"#000";overlayContext.beginPath();a=vec2(touchGamepadSize,mainCanvasSize.y-touchGamepadSize);if(touchGamepadAnalog)overlayContext.arc(a.x,a.y,touchGamepadSize/2,0,9),overlayContext.fill();else for(var b=10;b--;){var c=b*PI/4;overlayContext.arc(a.x,a.y,.6*touchGamepadSize,c+PI/8,c+PI/8);b%2&&overlayContext.arc(a.x,a.y,.33*touchGamepadSize,c,c);1==b&&overlayContext.fill()}overlayContext.stroke();a=vec2(mainCanvasSize.x-touchGamepadSize,mainCanvasSize.y-touchGamepadSize);for(b=4;b--;)c=a.add(vec2().setAngle(b*PI/2,touchGamepadSize/2)),overlayContext.fillStyle=touchGamepadButtons[b]?"#fff":"#000",overlayContext.beginPath(),overlayContext.arc(c.x,c.y,touchGamepadSize/4,0,9),overlayContext.fill(),overlayContext.stroke();overlayContext.restore()}}}class Sound{constructor(a,b=soundDefaultRange,c=soundDefaultTaper){soundEnable&&(this.range=b,this.taper=c,this.randomness=0,a&&(this.randomness=a[1]||0,a[1]=0,this.sampleChannels=[zzfxG(...a)],this.sampleRate=zzfxR))}play(a,b=1,c=1,d=1,e=0){if(soundEnable&&this.sampleChannels){var f;if(a){if(f=this.range){const g=cameraPos.distanceSquared(a);if(g>f*f)return;b*=percent(g**.5,f,f*this.taper)}f=2*worldToScreen(a).x/mainCanvas.width-1}a=c+c*this.randomness*d*rand(-1,1);return this.source=playSamples(this.sampleChannels,b,a,f,e,this.sampleRate)}}stop(){this.source&&this.source.stop();this.source=0}playNote(a,b,c){return this.play(b,c,2**(a/12),0)}getDuration(){return this.sampleChannels&&this.sampleChannels[0].length/this.sampleRate}isPlaying(){return this.source&&!this.source.ended}isLoading(){return!this.sampleChannels}}class SoundWave extends Sound{constructor(a,b=0,c,d){super(0,c,d);this.randomness=b;soundEnable&&(soundDecoderContext||=new AudioContext,fetch(a).then(e=>e.arrayBuffer()).then(e=>soundDecoderContext.decodeAudioData(e)).then(e=>{this.sampleChannels=[];for(let f=e.numberOfChannels;f--;)this.sampleChannels[f]=e.getChannelData(f);this.sampleRate=e.sampleRate}))}}let soundDecoderContext;class Music extends Sound{constructor(a){super();soundEnable&&(this.randomness=0,this.sampleChannels=zzfxM(...a),this.sampleRate=zzfxR)}playMusic(a,b=1){return super.play(0,a,1,1,b)}}function playAudioFile(a,b=1,c=1){if(soundEnable)return a=new Audio(a),a.volume=soundVolume*b,a.loop=c,a.play(),a}function speak(a,b="",c=1,d=1,e=1){if(soundEnable&&speechSynthesis)return a=new SpeechSynthesisUtterance(a),a.lang=b,a.volume=2*c*soundVolume,a.rate=d,a.pitch=e,speechSynthesis.speak(a),a}function speakStop(){speechSynthesis&&speechSynthesis.cancel()}function getNoteFrequency(a,b=220){return b*2**(a/12)}let audioContext;function playSamples(a,b=1,c=1,d=0,e=0,f=zzfxR){if(soundEnable)if(audioContext||=new AudioContext,"running"!=audioContext.state)audioContext.resume();else{var g=audioContext.createBuffer(a.length,a[0].length,f);f=audioContext.createBufferSource();a.forEach((k,h)=>g.getChannelData(h).set(k));f.buffer=g;f.playbackRate.value=c;f.loop=e;a=audioContext.createGain();a.gain.value=soundVolume*b;a.connect(audioContext.destination);f.connect(new StereoPannerNode(audioContext,{pan:clamp(d,-1,1)})).connect(a);f.start();return f}}function zzfx(...a){return playSamples([zzfxG(...a)])}const zzfxR=44100;function zzfxG(a=1,b=.05,c=220,d=0,e=0,f=.1,g=0,k=1,h=0,l=0,m=0,n=0,p=0,t=0,q=0,x=0,v=0,D=1,z=0,E=0,A=0){let w=2*PI;var r=zzfxR;let F=h*=500*w/r/r;b=c*=rand(1+b,1-b)*w/r;let C=[],y=0,G=0,u=0,H=1,R=0,S=0,B=0,J;var L=w*abs(A)*2/r,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*r+9;z*=r;e*=r;f*=r;v*=r;l*=500*w/r**3;q*=w/r;m*=w/r;n*=r;p=p*r|0;a*=soundVolume;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*y):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))),r=(c+=h+=l)*Math.cos(q*G++),y+=r+r*t*Math.sin(u**5),H&&++H>n&&(c+=m,b+=m,H=0),!p||++R%p||(c=b,h=F,H=H||1);return C}function zzfxM(a,b,c,d=125){let e,f,g,k,h,l,m,n,p,t,q,x,v,D=0,z,E=[],A=[],w=[],r=0,F=0,C=1,y={},G=zzfxR/d*60>>2;for(;C;r++)E=[C=n=x=0],c.forEach((u,H)=>{m=b[u][r]||[0,0,0];C||=!!b[u][r];z=x+(b[u][0].length-2-!n)*G;v=H==c.length-1;e=2;for(g=x;e<m.length+v;n=++e){h=m[e];p=e==m.length+v-1&&v||t!=(m[0]||0)||h|0;for(f=0;f<G&&n;f++>G-99&&p?q+=(1>q)/99:0)l=(1-q)*E[D++]/2||0,A[g]=(A[g]||0)-l*F+l,w[g]=(w[g++]||0)+l*F+l;h&&(q=h%1,F=m[1]||0,h|=0)&&(E=y[[t=m[D=0]||0,h]]=y[[t,h]]||(k=[...a[t]],k[2]*=2**((h-12)/12),0<h?zzfxG(...k):[]))}x=z});return[A,w]}let tileCollision=[],tileCollisionSize=vec2();function initTileCollision(a){tileCollisionSize=a;tileCollision=[];for(a=tileCollision.length=tileCollisionSize.area();a--;)tileCollision[a]=0}function setTileCollisionData(a,b=0){a.arrayCheck(tileCollisionSize)&&(tileCollision[(a.y|0)*tileCollisionSize.x+a.x|0]=b)}function getTileCollisionData(a){return a.arrayCheck(tileCollisionSize)?tileCollision[(a.y|0)*tileCollisionSize.x+a.x|0]:0}function tileCollisionTest(a,b=vec2(),c){const d=max(a.x-b.x/2|0,0);var e=max(a.y-b.y/2|0,0);const f=min(a.x+b.x/2,tileCollisionSize.x);for(a=min(a.y+b.y/2,tileCollisionSize.y);e<a;++e)for(b=d;b<f;++b){const g=tileCollision[e*tileCollisionSize.x+b];if(g&&(!c||c.collideWithTile(g,vec2(b,e))))return 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 l=getTileCollisionData(g);if(l&&(!c||c.collideWithTile(l,g)))return debugRaycast&&debugLine(a,b,"#f00",.02),debugRaycast&&debugPoint(g.add(vec2(.5)),"#ff0"),g.add(vec2(.5));if(k>e&&h>e)break;k>h?(g.y+=sign(d.y),h+=f.y):(g.x+=sign(d.x),k+=f.x)}debugRaycast&&debugLine(a,b,"#00f",.02)}class TileLayerData{constructor(a,b=0,c=0,d=new Color){this.tile=a;this.direction=b;this.mirror=c;this.color=d}clear(){this.tile=this.direction=this.mirror=0;color=new Color}}class TileLayer extends EngineObject{constructor(a,b=tileCollisionSize,c=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;this.data=[];for(a=this.size.area();a--;)this.data.push(new TileLayerData)}setData(a,b,c){a.arrayCheck(this.size)&&(this.data[(a.y|0)*this.size.x+a.x|0]=b,c&&this.drawTileData(a))}getData(a){return a.arrayCheck(this.size)&&this.data[(a.y|0)*this.size.x+a.x|0]}update(){}render(){ASSERT(mainContext!=this.context);glEnable&&!glOverlay&&!this.isOverlay&&glCopyToContext(mainContext);const a=worldToScreen(this.pos.add(vec2(0,this.size.y*this.scale.y)));(this.isOverlay?overlayContext:mainContext).drawImage(this.canvas,a.x,a.y,cameraScale*this.size.x*this.scale.x,cameraScale*this.size.y*this.scale.y)}redraw(){this.redrawStart(1);this.drawAllTileData();this.redrawEnd()}redrawStart(a=0){this.savedRenderSettings=[mainCanvas,mainContext,mainCanvasSize,cameraPos,cameraScale];mainCanvas=this.canvas;mainContext=this.context;cameraPos=this.size.scale(.5);cameraScale=this.tileInfo.size.x;a&&(mainCanvas.width=this.size.x*this.tileInfo.size.x,mainCanvas.height=this.size.y*this.tileInfo.size.y);enginePreRender()}redrawEnd(){ASSERT(mainContext==this.context);glEnable&&glCopyToContext(mainContext,1);[mainCanvas,mainContext,mainCanvasSize,cameraPos,cameraScale]=this.savedRenderSettings}drawTileData(a){const b=a.floor().add(this.pos).add(vec2(.5));this.drawCanvas2D(b,vec2(1),0,0,c=>c.clearRect(-.5,-.5,1,1));a=this.getData(a);if(void 0!=a.tile){ASSERT(mainContext==this.context);const c=tile(a.tile,this.tileInfo.size,this.tileInfo.textureIndex);drawTile(b,vec2(1),c,a.color,a.direction*PI/2,a.mirror)}}drawAllTileData(){for(let a=this.size.x;a--;)for(let b=this.size.y;b--;)this.drawTileData(vec2(a,b))}drawCanvas2D(a,b,c,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,-1,0,c,d)}}class ParticleEmitter extends EngineObject{constructor(a,b,c=0,d=0,e=100,f=PI,g,k=new Color,h=new Color,l=new Color(1,1,1,0),m=new Color(1,1,1,0),n=.5,p=.1,t=1,q=.1,x=.05,v=1,D=1,z=0,E=PI,A=.1,w=.2,r,F,C=1,y=F?1e9:0,G){super(a,vec2(),g,b,void 0,y);this.emitSize=c;this.emitTime=d;this.emitRate=e;this.emitConeAngle=f;this.colorStartA=k;this.colorStartB=h;this.colorEndA=l;this.colorEndB=m;this.randomColorLinear=C;this.particleTime=n;this.sizeStart=p;this.sizeEnd=t;this.speed=q;this.angleSpeed=x;this.damping=v;this.angleDamping=D;this.gravityScale=z;this.particleConeAngle=E;this.fadeRate=A;this.randomness=w;this.collideTiles=r;this.additive=F;this.localSpace=G;this.emitTimeBuffer=this.trailScale=0}update(){this.parent&&super.update();if(!this.emitTime||this.getAliveTime()<=this.emitTime){if(this.emitRate*particleEmitRateScale){const a=1/this.emitRate/particleEmitRateScale;for(this.emitTimeBuffer+=timeDelta;0<this.emitTimeBuffer;this.emitTimeBuffer-=a)this.emitParticle()}}else this.destroy();debugParticles&&debugRect(this.pos,vec2(this.emitSize),"#0f0",0,this.angle)}emitParticle(){var a=void 0!=this.emitSize.x?vec2(rand(-.5,.5),rand(-.5,.5)).multiply(this.emitSize).rotate(this.angle):randInCircle(this.emitSize/2),b=rand(this.particleConeAngle,-this.particleConeAngle);this.localSpace||(a=this.pos.add(a),b+=this.angle);a=new Particle(a,this.tileInfo,this.tileSize,b);const c=this.randomness;var d=m=>m+m*rand(c,-c);b=d(this.particleTime);const e=d(this.sizeStart),f=d(this.sizeEnd),g=d(this.speed);d=d(this.angleSpeed)*randSign();var k=rand(this.emitConeAngle,-this.emitConeAngle);const h=randColor(this.colorStartA,this.colorStartB,this.randomColorLinear),l=randColor(this.colorEndA,this.colorEndB,this.randomColorLinear);k=this.localSpace?k:this.angle+k;a.colorStart=h;a.colorEndDelta=l.subtract(h);a.velocity=vec2().setAngle(k,g);a.angleVelocity=d;a.lifeTime=b;a.sizeStart=e;a.sizeEndDelta=f-e;a.fadeRate=this.fadeRate;a.damping=this.damping;a.angleDamping=this.angleDamping;a.elasticity=this.elasticity;a.friction=this.friction;a.gravityScale=this.gravityScale;a.collideTiles=this.collideTiles;a.additive=this.additive;a.renderOrder=this.renderOrder;a.trailScale=this.trailScale;a.mirror=randInt(2);a.localSpaceEmitter=this.localSpace&&this;a.destroyCallback=this.particleDestroyCallback;this.particleCreateCallback&&this.particleCreateCallback(a);return a}render(){}}class Particle extends EngineObject{constructor(a,b,c){super(a,vec2(),b,c)}render(){const a=min((time-this.spawnTime)/this.lifeTime,1),b=vec2(this.sizeStart+a*this.sizeEndDelta);var c=this.fadeRate/2;c=new Color(this.colorStart.r+a*this.colorEndDelta.r,this.colorStart.g+a*this.colorEndDelta.g,this.colorStart.b+a*this.colorEndDelta.b,(this.colorStart.a+a*this.colorEndDelta.a)*(a<c?a/c:a>1-c?(1-a)/c:1));this.additive&&setBlendMode(1);let d=this.pos,e=this.angle;this.localSpaceEmitter&&(d=this.localSpaceEmitter.pos.add(d.rotate(-this.localSpaceEmitter.angle)),e+=this.localSpaceEmitter.angle);if(this.trailScale){var f=this.velocity;this.localSpaceEmitter&&(f=f.rotate(-this.localSpaceEmitter.angle));var g=f.length();g&&(f=f.scale(1/g),g*=this.trailScale,b.y=max(b.x,g),e=f.angle(),drawTile(d.add(f.multiply(vec2(0,-g/2))),b,this.tileInfo,c,e,this.mirror))}else drawTile(d,b,this.tileInfo,c,e,this.mirror);this.additive&&setBlendMode();debugParticles&&debugRect(d,b,"#f005",0,e);1==a&&(this.color=c,this.size=b,this.destroyCallback&&this.destroyCallback(this),this.destroyed=1)}}const medals=[];let medalsDisplayQueue=[],medalsSaveName,medalsDisplayTimeLast;function medalsInit(a){medalsSaveName=a;debugMedals||medals.forEach(b=>b.unlocked=localStorage[b.storageKey()]|0)}class Medal{constructor(a,b,c="",d="🏆",e){ASSERT(0<=a&&!medals[a]);medals[this.id=a]=this;this.name=b;this.description=c;this.icon=d;e&&((this.image=new Image).src=e)}unlock(){medalsPreventUnlock||this.unlocked||(ASSERT(medalsSaveName),localStorage[this.storageKey()]=this.unlocked=1,medalsDisplayQueue.push(this),newgrounds&&newgrounds.unlockMedal(this.id))}render(a=0){const b=overlayContext;var c=min(medalDisplaySize.x,mainCanvas.width);const d=overlayCanvas.width-c;a*=-medalDisplaySize.y;b.save();b.beginPath();b.fillStyle=new Color(.9,.9,.9);b.strokeStyle=new Color(0,0,0);b.lineWidth=3;b.fill(b.rect(d,a,c,medalDisplaySize.y));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,0,"left");c.y+=32;drawTextScreen(this.description,c,24,new Color(0,0,0),0,0,"left");b.restore()}renderIcon(a,b=medalDisplayIconSize){this.image?overlayContext.drawImage(this.image,a.x-b/2,a.y-b/2,b,b):drawTextScreen(this.icon,a,.7*b,new Color(0,0,0))}storageKey(){return medalsSaveName+"_"+this.id}}function medalsRender(){if(medalsDisplayQueue.length){var a=medalsDisplayQueue[0],b=timeReal-medalsDisplayTimeLast;if(medalsDisplayTimeLast)if(b>medalDisplayTime)medalsDisplayQueue.shift(medalsDisplayTimeLast=0);else{const c=medalDisplayTime-medalDisplaySlideTime;a.render(b<medalDisplaySlideTime?1-b/medalDisplaySlideTime:b>c?(b-c)/medalDisplaySlideTime:0)}else medalsDisplayTimeLast=timeReal}}let newgrounds;function newgroundsInit(a,b,c){newgrounds=new Newgrounds(a,b,c)}class Newgrounds{constructor(a,b,c){ASSERT(!newgrounds&&a);ASSERT(!b||c);this.app_id=a;this.cipher=b;this.cryptoJS=c;this.host=location?location.hostname:"";if(this.session_id=new URL(location.href).searchParams.get("ngio_session_id")){this.medals=(a=this.call("Medal.getList"))?a.result.data.medals:[];debugMedals&&console.log(this.medals);for(var d of this.medals)if(a=medals[d.id])a.image=new Image,a.image.src=d.icon,a.name=d.name,a.description=d.description,a.unlocked=d.unlocked,a.difficulty=d.difficulty,a.value=d.value,a.value&&(a.description=a.description+" ("+a.value+")");this.scoreboards=(d=this.call("ScoreBoard.getBoards"))?d.result.data.scoreboards:[];debugMedals&&console.log(this.scoreboards);setInterval(()=>this.call("Gateway.ping",0,1),3e5)}}unlockMedal(a){return this.call("Medal.unlock",{id:a},1)}postScore(a,b){return this.call("ScoreBoard.postScore",{id:a,value:b},1)}getScores(a,b=0,c=0,d=0,e=10){return this.call("ScoreBoard.getScores",{id:a,user:b,social:c,skip:d,limit:e})}logView(){return this.call("App.logView",{host:this.host},1)}call(a,b=0,c=0){a={component:a,parameters:b};if(this.cipher){b=this.cryptoJS;var d=b.enc.Base64.parse(this.cipher);const e=b.lib.WordArray.random(16);d=b.AES.encrypt(JSON.stringify(a),d,{iv:e});a.secure=b.enc.Base64.stringify(e.concat(d.ciphertext));a.parameters=0}b={app_id:this.app_id,session_id:this.session_id,call:a};a=new FormData;a.append("input",JSON.stringify(b));b=new XMLHttpRequest;b.open("POST","https://newgrounds.io/gateway_v3.php",!debugMedals&&c);b.send(a);debugMedals&&console.log(b.responseText);return b.responseText&&JSON.parse(b.responseText)}}let glCanvas,glContext,glActiveTexture,glShader,glArrayBuffer,glVertexData,glPositionData,glColorData,glBatchCount,glBatchAdditive,glAdditive;function glInit(){glCanvas=document.createElement("canvas");glContext=glCanvas.getContext("webgl2");glOverlay&&document.body.appendChild(glCanvas);glShader=glCreateProgram("#version 300 es\nprecision highp float;uniform mat4 m;in vec4 p,c,a;out vec4 v,d,e;void main(){gl_Position=m*vec4(p.xy,1,1);v=p;d=c;e=a;}","#version 300 es\nprecision highp float;in vec4 v,d,e;uniform sampler2D s;out vec4 c;void main(){c=texture(s,v.zw)*d+e;}");glVertexData=new ArrayBuffer(gl_VERTEX_BUFFER_SIZE);glPositionData=new Float32Array(glVertexData);glColorData=new Uint32Array(glVertexData);glArrayBuffer=glContext.createBuffer();glBatchCount=0}function glPreRender(){glContext.viewport(0,0,glCanvas.width=mainCanvas.width,glCanvas.height=mainCanvas.height);glContext.clear(gl_COLOR_BUFFER_BIT);glContext.useProgram(glShader);glContext.activeTexture(gl_TEXTURE0);glContext.bindTexture(gl_TEXTURE_2D,glActiveTexture=textureInfos[0].glTexture);glContext.bindBuffer(gl_ARRAY_BUFFER,glArrayBuffer);glContext.bufferData(gl_ARRAY_BUFFER,gl_VERTEX_BUFFER_SIZE,gl_DYNAMIC_DRAW);let a=glAdditive=0;var b=(f,g,k,h,l=0)=>{f=glContext.getAttribLocation(glShader,f);glContext.enableVertexAttribArray(f);glContext.vertexAttribPointer(f,h,g,l,gl_VERTEX_BYTE_STRIDE,a);a+=h*k};b("p",gl_FLOAT,4,4);b("c",gl_UNSIGNED_BYTE,1,4,1);b("a",gl_UNSIGNED_BYTE,1,4,1);b=2*cameraScale/mainCanvas.width;const c=2*cameraScale/mainCanvas.height,d=-1-b*cameraPos.x,e=-1-c*cameraPos.y;glContext.uniformMatrix4fv(glContext.getUniformLocation(glShader,"m"),0,new Float32Array([b,0,0,0,0,c,0,0,1,1,-1,1,d,e,0,0]))}function glSetTexture(a){a!=glActiveTexture&&(glFlush(),glContext.bindTexture(gl_TEXTURE_2D,glActiveTexture=a))}function glCompileShader(a,b){b=glContext.createShader(b);glContext.shaderSource(b,a);glContext.compileShader(b);if(debug&&!glContext.getShaderParameter(b,gl_COMPILE_STATUS))throw glContext.getShaderInfoLog(b);return b}function glCreateProgram(a,b){const c=glContext.createProgram();glContext.attachShader(c,glCompileShader(a,gl_VERTEX_SHADER));glContext.attachShader(c,glCompileShader(b,gl_FRAGMENT_SHADER));glContext.linkProgram(c);if(debug&&!glContext.getProgramParameter(c,gl_LINK_STATUS))throw glContext.getProgramInfoLog(c);return c}function glCreateTexture(a){const b=glContext.createTexture();glContext.bindTexture(gl_TEXTURE_2D,b);a&&glContext.texImage2D(gl_TEXTURE_2D,0,gl_RGBA,gl_RGBA,gl_UNSIGNED_BYTE,a);a=canvasPixelated?gl_NEAREST:gl_LINEAR;glContext.texParameteri(gl_TEXTURE_2D,gl_TEXTURE_MIN_FILTER,a);glContext.texParameteri(gl_TEXTURE_2D,gl_TEXTURE_MAG_FILTER,a);glContext.texParameteri(gl_TEXTURE_2D,gl_TEXTURE_WRAP_S,gl_CLAMP_TO_EDGE);glContext.texParameteri(gl_TEXTURE_2D,gl_TEXTURE_WRAP_T,gl_CLAMP_TO_EDGE);return b}function glFlush(){if(glBatchCount){var a=glBatchAdditive?gl_ONE:gl_ONE_MINUS_SRC_ALPHA;glContext.blendFuncSeparate(gl_SRC_ALPHA,a,gl_ONE,a);glContext.enable(gl_BLEND);glContext.bufferSubData(gl_ARRAY_BUFFER,0,glVertexData);glContext.drawArrays(gl_TRIANGLE_STRIP,0,glBatchCount);glBatchCount=0;glBatchAdditive=glAdditive}}function glCopyToContext(a,b){if(glBatchCount||b)glFlush(),glOverlay&&!b||a.drawImage(glCanvas,0,0)}function glDraw(a,b,c,d,e,f,g,k,h,l,m=0){(glBatchCount>=gl_MAX_BATCH-6||glBatchAdditive!=glAdditive)&&glFlush();var n=Math.cos(e)/2;const p=Math.sin(e)/2;e=n*c;n*=d;c*=p;d*=p;a=[a-e+d,b+n+c,f,g,a-e-d,b-n+c,f,h,a+e+d,b+n-c,k,g,a+e-d,b-n-c,k,h];for(let t=6,q=glBatchCount*gl_INDICIES_PER_VERT;t--;)b=4*clamp(t-1,0,3),glPositionData[q++]=a[b+0],glPositionData[q++]=a[b+1],glPositionData[q++]=a[b+2],glPositionData[q++]=a[b+3],glColorData[q++]=l,glColorData[q++]=m;glBatchCount+=6}function glDrawPoints(a,b){const c=a.length+2;(glBatchCount>=gl_MAX_BATCH-c||glBatchAdditive!=glAdditive)&&glFlush();for(let e=c,f=glBatchCount*gl_INDICIES_PER_VERT;e--;){var d=clamp(e-1,0,c-3);const g=d>>1;d=a[d%2?g:c-3-g];glPositionData[f++]=d.x;glPositionData[f++]=d.y;glPositionData[f++]=0;glPositionData[f++]=0;glColorData[f++]=0;glColorData[f++]=b}glBatchCount+=c}let glPostShader,glPostArrayBuffer,glPostTexture,glPostIncludeOverlay;function glInitPostProcess(a,b){ASSERT(!glPostShader);a||="void mainImage(out vec4 c,vec2 p){c=texture(iChannel0,p/iResolution.xy);}";glPostShader=glCreateProgram("#version 300 es\nprecision highp float;in vec2 p;void main(){gl_Position=vec4(p,1,1);}","#version 300 es\nprecision highp float;uniform sampler2D iChannel0;uniform vec3 iResolution;uniform float iTime;out vec4 c;\n"+a+"\nvoid main(){mainImage(c,gl_FragCoord.xy);c.a=1.;}");glPostArrayBuffer=glContext.createBuffer();glPostTexture=glCreateTexture();glPostIncludeOverlay=b;mainCanvas.style.visibility="hidden"}function glRenderPostProcess(){if(glPostShader){glEnable?(glFlush(),mainContext.drawImage(glCanvas,0,0)):glContext.viewport(0,0,glCanvas.width=mainCanvas.width,glCanvas.height=mainCanvas.height);glPostIncludeOverlay&&(mainContext.drawImage(overlayCanvas,0,0),overlayCanvas.width=mainCanvas.width);glContext.useProgram(glPostShader);glContext.disable(gl_BLEND);glContext.bindBuffer(gl_ARRAY_BUFFER,glPostArrayBuffer);glContext.bufferData(gl_ARRAY_BUFFER,new Float32Array([-3,1,1,-3,1,1]),gl_STATIC_DRAW);glContext.pixelStorei(gl_UNPACK_FLIP_Y_WEBGL,!0);glContext.activeTexture(gl_TEXTURE0);glContext.bindTexture(gl_TEXTURE_2D,glPostTexture);glContext.texImage2D(gl_TEXTURE_2D,0,gl_RGBA,gl_RGBA,gl_UNSIGNED_BYTE,mainCanvas);var a=glContext.getAttribLocation(glPostShader,"p");glContext.enableVertexAttribArray(a);glContext.vertexAttribPointer(a,2,gl_FLOAT,0,8,0);glContext.uniform1i(glContext.getUniformLocation(glPostShader,"iChannel0"),0);glContext.uniform1f(glContext.getUniformLocation(glPostShader,"iTime"),time);glContext.uniform3f(glContext.getUniformLocation(glPostShader,"iResolution"),mainCanvas.width,mainCanvas.height,1);glContext.drawArrays(gl_TRIANGLE_STRIP,0,3)}}const gl_ONE=1,gl_TRIANGLE_STRIP=5,gl_SRC_ALPHA=770,gl_ONE_MINUS_SRC_ALPHA=771,gl_BLEND=3042,gl_TEXTURE_2D=3553,gl_UNSIGNED_BYTE=5121,gl_FLOAT=5126,gl_RGBA=6408,gl_NEAREST=9728,gl_LINEAR=9729,gl_TEXTURE_MAG_FILTER=10240,gl_TEXTURE_MIN_FILTER=10241,gl_TEXTURE_WRAP_S=10242,gl_TEXTURE_WRAP_T=10243,gl_COLOR_BUFFER_BIT=16384,gl_CLAMP_TO_EDGE=33071,gl_TEXTURE0=33984,gl_ARRAY_BUFFER=34962,gl_STATIC_DRAW=35044,gl_DYNAMIC_DRAW=35048,gl_FRAGMENT_SHADER=35632,gl_VERTEX_SHADER=35633,gl_COMPILE_STATUS=35713,gl_LINK_STATUS=35714,gl_UNPACK_FLIP_Y_WEBGL=37440,gl_INDICIES_PER_VERT=6,gl_MAX_BATCH=1e5,gl_VERTEX_BYTE_STRIDE=24,gl_VERTEX_BUFFER_SIZE=gl_MAX_BATCH*gl_VERTEX_BYTE_STRIDE,engineName="LittleJS",engineVersion="1.8.9",frameRate=60,timeDelta=1/frameRate;let engineObjects=[],engineObjectsCollide=[],frame=0,time=0,timeReal=0,paused=0;function setPaused(a){paused=a}let frameTimeLastMS=0,frameTimeBufferMS=0,averageFPS=0;function engineInit(a,b,c,d,e,f=["tiles.png"]){function g(k=0){var h=k-frameTimeLastMS;frameTimeLastMS=k;if(debug||showWatermark)averageFPS=lerp(.05,averageFPS,1e3/(h||1));k=debug&&keyIsDown(107);const l=debug&&keyIsDown(109);debug&&(h*=k?5:l?.2:1);timeReal+=h/1e3;frameTimeBufferMS+=!paused*h;k||(frameTimeBufferMS=min(frameTimeBufferMS,50));canvasFixedSize.x?(mainCanvas.width=canvasFixedSize.x,mainCanvas.height=canvasFixedSize.y,h=innerWidth/innerHeight,k=mainCanvas.width/mainCanvas.height,(glCanvas||mainCanvas).style.width=mainCanvas.style.width=overlayCanvas.style.width=h<k?"100%":"",(glCanvas||mainCanvas).style.height=mainCanvas.style.height=overlayCanvas.style.height=h<k?"":"100%"):(mainCanvas.width=min(innerWidth,canvasMaxSize.x),mainCanvas.height=min(innerHeight,canvasMaxSize.y));overlayCanvas.width=mainCanvas.width;overlayCanvas.height=mainCanvas.height;mainCanvasSize=vec2(mainCanvas.width,mainCanvas.height);if(paused)inputUpdate(),debugUpdate(),c(),inputUpdatePost();else{h=0;0>frameTimeBufferMS&&-9<frameTimeBufferMS&&(h=frameTimeBufferMS,frameTimeBufferMS=0);for(;0<=frameTimeBufferMS;frameTimeBufferMS-=1e3/frameRate)time=frame++/frameRate,inputUpdate(),b(),engineObjectsUpdate(),debugUpdate(),c(),inputUpdatePost();frameTimeBufferMS+=h}enginePreRender();d();engineObjects.sort((n,p)=>n.renderOrder-p.renderOrder);for(var m of engineObjects)m.destroyed||m.render();e();glRenderPostProcess();medalsRender();touchGamepadRender();debugRender();glEnable&&glCopyToContext(mainContext);showWatermark&&(overlayContext.textAlign="right",overlayContext.textBaseline="top",overlayContext.font="1em monospace",overlayContext.fillStyle="#000",m=engineName+" v"+engineVersion+" / "+drawCount+" / "+engineObjects.length+" / "+averageFPS.toFixed(1)+(glEnable?" GL":" 2D"),overlayContext.fillText(m,mainCanvas.width-3,3),overlayContext.fillStyle="#fff",overlayContext.fillText(m,mainCanvas.width-2,2),drawCount=0);requestAnimationFrame(g)}ASSERT(Array.isArray(f));document.body.style="margin:0;overflow:hidden;background:#000;touch-action:none;user-select:none;-webkit-user-select:none;-webkit-touch-callout:none";document.body.appendChild(mainCanvas=document.createElement("canvas"));mainContext=mainCanvas.getContext("2d");debugInit();glEnable&&glInit();document.body.appendChild(overlayCanvas=document.createElement("canvas"));overlayContext=overlayCanvas.getContext("2d");(glCanvas||mainCanvas).style=mainCanvas.style=overlayCanvas.style="position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)";f=f.map((k,h)=>new Promise(l=>{const m=new Image;m.onerror=m.onload=()=>{textureInfos[h]=new TextureInfo(m);l()};m.src=k}));showSplashScreen&&f.push(new Promise(k=>{function h(){clearInput();drawEngineSplashScreen(l+=.01);1<l?k():setTimeout(h,16)}let l=0;console.log(`LittleJS Engine v${engineVersion}`);h()}));Promise.all(f).then(()=>{a();g()})}function enginePreRender(){mainCanvasSize=vec2(mainCanvas.width,mainCanvas.height);mainContext.imageSmoothingEnabled=!canvasPixelated;glEnable&&glPreRender()}function engineObjectsUpdate(){function a(b){if(!b.destroyed){b.update();for(const c of b.children)a(c)}}engineObjectsCollide=engineObjects.filter(b=>b.collideSolidObjects);for(const b of engineObjects)b.parent||a(b);engineObjects=engineObjects.filter(b=>!b.destroyed)}function engineObjectsDestroy(){for(const a of engineObjects)a.parent||a.destroy();engineObjects=engineObjects.filter(a=>!a.destroyed)}function engineObjectsCallback(a,b,c,d=engineObjects){if(a)if(void 0!=b.x)for(const e of d)isOverlapping(a,b,e.pos,e.size)&&c(e);else{b*=b;for(const e of d)a.distanceSquared(e.pos)<b&&c(e)}else for(const e of d)c(e)}function drawEngineSplashScreen(a){const b=mainContext;var c=mainCanvas.width=innerWidth,d=mainCanvas.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));g.addColorStop(1,hsl(0,0,0,e));b.save();b.fillStyle=g;b.fillRect(0,0,c,d);g=(h,l,m,n,p)=>{b.beginPath();b.rect(h,l,m,p?n*k:n);(b.fillStyle=p)?b.fill():b.stroke()};f=(h,l,m,n=0,p=2*PI,t,q)=>{const x=(n+p)/2;n=k*(p-n)/2;b.beginPath();q&&b.lineTo(h,l);b.arc(h,l,m,x-n,x+n);(b.fillStyle=t)?b.fill():b.stroke()};e=(h=0,l=0)=>hsl([.98,.3,.57,.14][h%4]-10,.8,[0,.3,.5,.8,.9][l]);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+k;c=percent(a,.1,1);b.setLineDash([99*c,99]);g(7,17,18,-8,e(2,2));g(7,9,18,4,e(2,3));g(25,9,8,8,e(2,1));g(25,9,-18,8);g(25,9,8,8);g(25,17,7,22,e());g(11,40,14,-23,e(1,1));g(11,17,14,17,e(1,2));g(11,17,14,9,e(1,3));g(15,31,6,-9,e(2,2));f(15,23,5,0,PI/2,e(2,4),1);g(25,17,-14,23);g(21,22,-6,9);g(37,14,9,6,e(3,2));g(37,14,4,6,e(3,3));g(37,14,9,6);g(50,20,10,-10,e(0,1));g(50,20,6,-10,e(0,2));g(50,20,3,-10,e(0,3));g(50,10,10,10);f(55,2,11.4,.5,PI-.5,e(3,3));f(55,2,11.4,.5,PI/2,e(3,2),1);f(55,2,11.4,.5,PI-.5);g(45,7,20,-7,e(0,2));g(45,0,20,3,e(0,3));g(45,0,20,7);for(c=5;c--;)f(60-6*c,30,9.9,0,2*PI,e(c+2,3)),f(60-6*c,30,10,-.5,PI+.5,e(c+2,2)),f(60-6*c,30,10.1,.5,PI-.5,e(c+2,1));f(36,30,10,PI/2,3*PI/2);f(47,30,10,PI/2,3*PI/2);f(60,30,10);b.beginPath();b.lineTo(36,20);b.lineTo(60,20);b.stroke();f(60,30,4,PI,3*PI,e(3,2));f(60,30,4,PI,2*PI,e(3,3));f(60,30,4,PI,3*PI);for(c=6;c--;)b.beginPath(),b.lineTo(53,54),b.lineTo(53,40),b.lineTo(53+(1+2.9*c)*k,40),b.lineTo(53+(4+3.5*c)*k,54),b.fillStyle=e(0,c%2+2),b.fill()||c%2&&b.stroke();g(6,40,5,5);g(6,40,5,5,e());g(15,54,38,-14,e());for(g=3;g--;)for(c=2;c--;)f(15*g+15,47,c?7:1,PI,3*PI,e(g,3)),b.stroke(),f(15*g+15,47,c?7:1,0,PI,e(g,2)),b.stroke();b.beginPath();b.lineTo(6,40);b.lineTo(68,40);b.stroke();b.beginPath();b.lineTo(77,54);b.lineTo(4,54);b.stroke();f=engineName;b.font="900 16px arial";b.textAlign="center";b.textBaseline="top";b.lineWidth=1+3*k;g=0;for(c=0;c<f.length;++c)g+=b.measureText(f[c]).width;for(c=2;c--;)for(let h=0,l=41-g/2;h<f.length;++h)b.fillStyle=e(h,2),d=b.measureText(f[h]).width,b[c?"strokeText":"fillText"](f[h],l+d/2,55.5,17*k),l+=d;b.restore()}
|