littlejsengine 1.6.4 → 1.6.6

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.js CHANGED
@@ -1,15 +1,10 @@
1
- /*
2
- LittleJS - Debug Build
3
- MIT License - Copyright 2021 Frank Force
4
- */
5
-
6
1
  /**
7
2
  * LittleJS Debug System
8
- * <br> - Press ~ to show debug overlay with mouse pick
9
- * <br> - Number keys toggle debug functions
10
- * <br> - +/- apply time scale
11
- * <br> - Debug primitive rendering
12
- * <br> - Save a 2d canvas as an image
3
+ * - Press ~ to show debug overlay with mouse pick
4
+ * - Number keys toggle debug functions
5
+ * - +/- apply time scale
6
+ * - Debug primitive rendering
7
+ * - Save a 2d canvas as an image
13
8
  * @namespace Debug
14
9
  */
15
10
 
@@ -62,7 +57,7 @@ debugParticles = 0, debugGamepads = 0, debugMedals = 0, debugTakeScreenshot, dow
62
57
  * @param {Boolean} assertion
63
58
  * @param {Object} output
64
59
  * @memberof Debug */
65
- const ASSERT = enableAsserts ? (...assert)=> console.assert(...assert) : ()=>{};
60
+ function ASSERT(...assert) { enableAsserts && console.assert(...assert); }
66
61
 
67
62
  /** Draw a debug rectangle in world space
68
63
  * @param {Vector2} pos
@@ -72,7 +67,7 @@ const ASSERT = enableAsserts ? (...assert)=> console.assert(...assert) : ()=>{};
72
67
  * @param {Number} [angle=0]
73
68
  * @param {Boolean} [fill=false]
74
69
  * @memberof Debug */
75
- const debugRect = (pos, size=vec2(), color='#fff', time=0, angle=0, fill=false)=>
70
+ function debugRect(pos, size=vec2(), color='#fff', time=0, angle=0, fill=false)
76
71
  {
77
72
  ASSERT(typeof color == 'string'); // pass in regular html strings as colors
78
73
  debugPrimitives.push({pos, size:vec2(size), color, time:new Timer(time), angle, fill});
@@ -85,7 +80,7 @@ const debugRect = (pos, size=vec2(), color='#fff', time=0, angle=0, fill=false)=
85
80
  * @param {Number} [time=0]
86
81
  * @param {Boolean} [fill=false]
87
82
  * @memberof Debug */
88
- const debugCircle = (pos, radius=0, color='#fff', time=0, fill=false)=>
83
+ function debugCircle(pos, radius=0, color='#fff', time=0, fill=false)
89
84
  {
90
85
  ASSERT(typeof color == 'string'); // pass in regular html strings as colors
91
86
  debugPrimitives.push({pos, size:radius, color, time:new Timer(time), angle:0, fill});
@@ -97,7 +92,7 @@ const debugCircle = (pos, radius=0, color='#fff', time=0, fill=false)=>
97
92
  * @param {Number} [time=0]
98
93
  * @param {Number} [angle=0]
99
94
  * @memberof Debug */
100
- const debugPoint = (pos, color, time, angle)=> debugRect(pos, 0, color, time, angle);
95
+ function debugPoint(pos, color, time, angle) {debugRect(pos, 0, color, time, angle);}
101
96
 
102
97
  /** Draw a debug line in world space
103
98
  * @param {Vector2} posA
@@ -106,7 +101,7 @@ const debugPoint = (pos, color, time, angle)=> debugRect(pos, 0, color, time, an
106
101
  * @param {Number} [thickness=.1]
107
102
  * @param {Number} [time=0]
108
103
  * @memberof Debug */
109
- const debugLine = (posA, posB, color, thickness=.1, time)=>
104
+ function debugLine(posA, posB, color, thickness=.1, time)
110
105
  {
111
106
  const halfDelta = vec2((posB.x - posA.x)/2, (posB.y - posA.y)/2);
112
107
  const size = vec2(thickness, halfDelta.length()*2);
@@ -120,7 +115,7 @@ const debugLine = (posA, posB, color, thickness=.1, time)=>
120
115
  * @param {Vector2} sizeB
121
116
  * @param {String} [color='#fff']
122
117
  * @memberof Debug */
123
- const debugAABB = (pA, sA, pB, sB, color)=>
118
+ function debugAABB(pA, sA, pB, sB, color)
124
119
  {
125
120
  const minPos = vec2(min(pA.x - sA.x/2, pB.x - sB.x/2), min(pA.y - sA.y/2, pB.y - sB.y/2));
126
121
  const maxPos = vec2(max(pA.x + sA.x/2, pB.x + sB.x/2), max(pA.y + sA.y/2, pB.y + sB.y/2));
@@ -136,7 +131,7 @@ const debugAABB = (pA, sA, pB, sB, color)=>
136
131
  * @param {Number} [angle=0]
137
132
  * @param {String} [font='monospace']
138
133
  * @memberof Debug */
139
- const debugText = (text, pos, size=1, color='#fff', time=0, angle=0, font='monospace')=>
134
+ function debugText(text, pos, size=1, color='#fff', time=0, angle=0, font='monospace')
140
135
  {
141
136
  ASSERT(typeof color == 'string'); // pass in regular html strings as colors
142
137
  debugPrimitives.push({text, pos, size, color, time:new Timer(time), angle, font});
@@ -144,13 +139,13 @@ const debugText = (text, pos, size=1, color='#fff', time=0, angle=0, font='monos
144
139
 
145
140
  /** Clear all debug primitives in the list
146
141
  * @memberof Debug */
147
- const debugClear = ()=> debugPrimitives = [];
142
+ function debugClear() { debugPrimitives = []; }
148
143
 
149
144
  /** Save a canvas to disk
150
145
  * @param {HTMLCanvasElement} canvas
151
146
  * @param {String} [filename]
152
147
  * @memberof Debug */
153
- const debugSaveCanvas = (canvas, filename = engineName + '.png') =>
148
+ function debugSaveCanvas(canvas, filename = engineName + '.png')
154
149
  {
155
150
  downloadLink.download = 'screenshot.png';
156
151
  downloadLink.href = canvas.toDataURL('image/png').replace('image/png','image/octet-stream');
@@ -160,14 +155,14 @@ const debugSaveCanvas = (canvas, filename = engineName + '.png') =>
160
155
  ///////////////////////////////////////////////////////////////////////////////
161
156
  // Engine debug function (called automatically)
162
157
 
163
- const debugInit = ()=>
158
+ function debugInit()
164
159
  {
165
160
  // create link for saving screenshots
166
161
  document.body.appendChild(downloadLink = document.createElement('a'));
167
162
  downloadLink.style.display = 'none';
168
163
  }
169
164
 
170
- const debugUpdate = ()=>
165
+ function debugUpdate()
171
166
  {
172
167
  if (!debug)
173
168
  return;
@@ -195,7 +190,7 @@ const debugUpdate = ()=>
195
190
  }
196
191
  }
197
192
 
198
- const debugRender = ()=>
193
+ function debugRender()
199
194
  {
200
195
  glCopyToContext(mainContext);
201
196
 
@@ -399,10 +394,10 @@ const debugRender = ()=>
399
394
  }
400
395
  /**
401
396
  * LittleJS Utility Classes and Functions
402
- * <br> - General purpose math library
403
- * <br> - Vector2 - fast, simple, easy 2D vector class
404
- * <br> - Color - holds a rgba color with some math functions
405
- * <br> - Timer - tracks time automatically
397
+ * - General purpose math library
398
+ * - Vector2 - fast, simple, easy 2D vector class
399
+ * - Color - holds a rgba color with some math functions
400
+ * - Timer - tracks time automatically
406
401
  * @namespace Utilities
407
402
  */
408
403
 
@@ -418,34 +413,34 @@ const PI = Math.PI;
418
413
  * @param {Number} value
419
414
  * @return {Number}
420
415
  * @memberof Utilities */
421
- const abs = (a)=> a < 0 ? -a : a;
416
+ function abs(a) { return a < 0 ? -a : a; }
422
417
 
423
418
  /** Returns lowest of two values passed in
424
419
  * @param {Number} valueA
425
420
  * @param {Number} valueB
426
421
  * @return {Number}
427
422
  * @memberof Utilities */
428
- const min = (a, b)=> a < b ? a : b;
423
+ function min(a, b) { return a < b ? a : b; }
429
424
 
430
425
  /** Returns highest of two values passed in
431
426
  * @param {Number} valueA
432
427
  * @param {Number} valueB
433
428
  * @return {Number}
434
429
  * @memberof Utilities */
435
- const max = (a, b)=> a > b ? a : b;
430
+ function max(a, b) { return a > b ? a : b; }
436
431
 
437
432
  /** Returns the sign of value passed in (also returns 1 if 0)
438
433
  * @param {Number} value
439
434
  * @return {Number}
440
435
  * @memberof Utilities */
441
- const sign = (a)=> a < 0 ? -1 : 1;
436
+ function sign(a) { return a < 0 ? -1 : 1; }
442
437
 
443
438
  /** Returns first parm modulo the second param, but adjusted so negative numbers work as expected
444
439
  * @param {Number} dividend
445
440
  * @param {Number} [divisor=1]
446
441
  * @return {Number}
447
442
  * @memberof Utilities */
448
- const mod = (a, b=1)=> ((a % b) + b) % b;
443
+ function mod(a, b=1) { return ((a % b) + b) % b; }
449
444
 
450
445
  /** Clamps the value beween max and min
451
446
  * @param {Number} value
@@ -453,7 +448,8 @@ const mod = (a, b=1)=> ((a % b) + b) % b;
453
448
  * @param {Number} [max=1]
454
449
  * @return {Number}
455
450
  * @memberof Utilities */
456
- const clamp = (v, min=0, max=1)=> v < min ? min : v > max ? max : v;
451
+ function clamp(v, min=0, max=1)
452
+ { return v < min ? min : v > max ? max : v; }
457
453
 
458
454
  /** Returns what percentage the value is between max and min
459
455
  * @param {Number} value
@@ -461,7 +457,8 @@ const clamp = (v, min=0, max=1)=> v < min ? min : v > max ? max : v;
461
457
  * @param {Number} [max=1]
462
458
  * @return {Number}
463
459
  * @memberof Utilities */
464
- const percent = (v, min=0, max=1)=> max-min ? clamp((v-min) / (max-min)) : 0;
460
+ function percent(v, min=0, max=1)
461
+ { return max-min ? clamp((v-min) / (max-min)) : 0; }
465
462
 
466
463
  /** Linearly interpolates the percent value between max and min
467
464
  * @param {Number} percent
@@ -469,19 +466,19 @@ const percent = (v, min=0, max=1)=> max-min ? clamp((v-min) / (max-min)) : 0;
469
466
  * @param {Number} [max=1]
470
467
  * @return {Number}
471
468
  * @memberof Utilities */
472
- const lerp = (p, min=0, max=1)=> min + clamp(p) * (max-min);
469
+ function lerp(p, min=0, max=1){ return min + clamp(p) * (max-min); }
473
470
 
474
471
  /** Applies smoothstep function to the percentage value
475
472
  * @param {Number} value
476
473
  * @return {Number}
477
474
  * @memberof Utilities */
478
- const smoothStep = (p)=> p * p * (3 - 2 * p);
475
+ function smoothStep(p) { return p * p * (3 - 2 * p); }
479
476
 
480
477
  /** Returns the nearest power of two not less then the value
481
478
  * @param {Number} value
482
479
  * @return {Number}
483
480
  * @memberof Utilities */
484
- const nearestPowerOfTwo = (v)=> 2**Math.ceil(Math.log2(v));
481
+ function nearestPowerOfTwo(v) { return 2**Math.ceil(Math.log2(v)); }
485
482
 
486
483
  /** Returns true if two axis aligned bounding boxes are overlapping
487
484
  * @param {Vector2} pointA - Center of box A
@@ -490,7 +487,8 @@ const nearestPowerOfTwo = (v)=> 2**Math.ceil(Math.log2(v));
490
487
  * @param {Vector2} [sizeB] - Size of box B
491
488
  * @return {Boolean} - True if overlapping
492
489
  * @memberof Utilities */
493
- const isOverlapping = (pA, sA, pB, sB)=> abs(pA.x - pB.x)*2 < sA.x + sB.x && abs(pA.y - pB.y)*2 < sA.y + sB.y;
490
+ function isOverlapping(pA, sA, pB, sB)
491
+ { return abs(pA.x - pB.x)*2 < sA.x + sB.x && abs(pA.y - pB.y)*2 < sA.y + sB.y; }
494
492
 
495
493
  /** Returns an oscillating wave between 0 and amplitude with frequency of 1 Hz by default
496
494
  * @param {Number} [frequency=1] - Frequency of the wave in Hz
@@ -498,13 +496,14 @@ const isOverlapping = (pA, sA, pB, sB)=> abs(pA.x - pB.x)*2 < sA.x + sB.x && abs
498
496
  * @param {Number} [t=time] - Value to use for time of the wave
499
497
  * @return {Number} - Value waving between 0 and amplitude
500
498
  * @memberof Utilities */
501
- const wave = (frequency=1, amplitude=1, t=time)=> amplitude/2 * (1 - Math.cos(t*frequency*2*PI));
499
+ function wave(frequency=1, amplitude=1, t=time)
500
+ { return amplitude/2 * (1 - Math.cos(t*frequency*2*PI)); }
502
501
 
503
502
  /** Formats seconds to mm:ss style for display purposes
504
503
  * @param {Number} t - time in seconds
505
504
  * @return {String}
506
505
  * @memberof Utilities */
507
- const formatTime = (t)=> (t/60|0) + ':' + (t%60<10?'0':'') + (t%60|0);
506
+ function formatTime(t) { return (t/60|0) + ':' + (t%60<10?'0':'') + (t%60|0); }
508
507
 
509
508
  ///////////////////////////////////////////////////////////////////////////////
510
509
 
@@ -516,32 +515,33 @@ const formatTime = (t)=> (t/60|0) + ':' + (t%60<10?'0':'') + (t%60|0);
516
515
  * @param {Number} [valueB=0]
517
516
  * @return {Number}
518
517
  * @memberof Random */
519
- const rand = (a=1, b=0)=> b + (a-b)*Math.random();
518
+ function rand(a=1, b=0) { return b + (a-b)*Math.random(); }
520
519
 
521
520
  /** Returns a floored random value the two values passed in
522
521
  * @param {Number} [valueA=1]
523
522
  * @param {Number} [valueB=0]
524
523
  * @return {Number}
525
524
  * @memberof Random */
526
- const randInt = (a=1, b=0)=> rand(a,b)|0;
525
+ function randInt(a=1, b=0) { return rand(a,b)|0; }
527
526
 
528
527
  /** Randomly returns either -1 or 1
529
528
  * @return {Number}
530
529
  * @memberof Random */
531
- const randSign = ()=> randInt(2) * 2 - 1;
530
+ function randSign() { return randInt(2) * 2 - 1; }
532
531
 
533
532
  /** Returns a random Vector2 within a circular shape
534
533
  * @param {Number} [radius=1]
535
534
  * @param {Number} [minRadius=0]
536
535
  * @return {Vector2}
537
536
  * @memberof Random */
538
- const randInCircle = (radius=1, minRadius=0)=> radius > 0 ? randVector(radius * rand(minRadius / radius, 1)**.5) : new Vector2;
537
+ function randInCircle(radius=1, minRadius=0)
538
+ { return radius > 0 ? randVector(radius * rand(minRadius / radius, 1)**.5) : new Vector2; }
539
539
 
540
540
  /** Returns a random Vector2 with the passed in length
541
541
  * @param {Number} [length=1]
542
542
  * @return {Vector2}
543
543
  * @memberof Random */
544
- const randVector = (length=1)=> new Vector2().setAngle(rand(2*PI), length);
544
+ function randVector(length=1) { return new Vector2().setAngle(rand(2*PI), length); }
545
545
 
546
546
  /** Returns a random color between the two passed in colors, combine components if linear
547
547
  * @param {Color} [colorA=Color()]
@@ -549,8 +549,8 @@ const randVector = (length=1)=> new Vector2().setAngle(rand(2*PI), length);
549
549
  * @param {Boolean} [linear]
550
550
  * @return {Color}
551
551
  * @memberof Random */
552
- const randColor = (cA = new Color, cB = new Color(0,0,0,1), linear)=>
553
- linear ? cA.lerp(cB, rand()) : new Color(rand(cA.r,cB.r),rand(cA.g,cB.g),rand(cA.b,cB.b),rand(cA.a,cB.a));
552
+ function randColor(cA = new Color, cB = new Color(0,0,0,1), linear)
553
+ { return linear ? cA.lerp(cB, rand()) : new Color(rand(cA.r,cB.r),rand(cA.g,cB.g),rand(cA.b,cB.b),rand(cA.a,cB.a)); }
554
554
 
555
555
  /** Seed used by the randSeeded function
556
556
  * @type {Number}
@@ -561,16 +561,19 @@ let randSeed = 1;
561
561
  /** Set seed used by the randSeeded function, should not be 0
562
562
  * @param {Number} seed
563
563
  * @memberof Random */
564
- const setRandSeed = (seed)=> randSeed = seed;
564
+ function setRandSeed(seed) { randSeed = seed; }
565
565
 
566
566
  /** Returns a seeded random value between the two values passed in using randSeed
567
567
  * @param {Number} [valueA=1]
568
568
  * @param {Number} [valueB=0]
569
569
  * @return {Number}
570
570
  * @memberof Random */
571
- const randSeeded = (a=1, b=0)=>
571
+ function randSeeded(a=1, b=0)
572
572
  {
573
- randSeed ^= randSeed << 13; randSeed ^= randSeed >>> 17; randSeed ^= randSeed << 5; // xorshift
573
+ // xorshift algorithm
574
+ randSeed ^= randSeed << 13;
575
+ randSeed ^= randSeed >>> 17;
576
+ randSeed ^= randSeed << 5;
574
577
  return b + (a-b) * abs(randSeed % 1e9) / 1e9;
575
578
  }
576
579
 
@@ -588,7 +591,8 @@ const randSeeded = (a=1, b=0)=>
588
591
  * b = vec2(); // set b to (0, 0)
589
592
  * @memberof Utilities
590
593
  */
591
- const vec2 = (x=0, y)=> x.x == undefined ? new Vector2(x, y == undefined? x : y) : new Vector2(x.x, x.y);
594
+ function vec2(x=0, y)
595
+ { return x.x == undefined ? new Vector2(x, y == undefined? x : y) : new Vector2(x.x, x.y); }
592
596
 
593
597
  /**
594
598
  * Check if object is a valid Vector2
@@ -596,11 +600,11 @@ const vec2 = (x=0, y)=> x.x == undefined ? new Vector2(x, y == undefined? x : y)
596
600
  * @return {Boolean}
597
601
  * @memberof Utilities
598
602
  */
599
- const isVector2 = (v)=> !isNaN(v.x) && !isNaN(v.y);
603
+ function isVector2(v) { return !isNaN(v.x) && !isNaN(v.y); }
600
604
 
601
605
  /**
602
606
  * 2D Vector object with vector math library
603
- * <br> - Functions do not change this so they can be chained together
607
+ * - Functions do not change this so they can be chained together
604
608
  * @example
605
609
  * let a = new Vector2(2, 3); // vector with coordinates (2, 3)
606
610
  * let b = new Vector2; // vector with coordinates (0, 0)
@@ -747,7 +751,7 @@ class Vector2
747
751
  * @return {Color}
748
752
  * @memberof Utilities
749
753
  */
750
- const rgb = (r, g, b, a)=> new Color(r, g, b, a);
754
+ function rgb(r, g, b, a) { return new Color(r, g, b, a); }
751
755
 
752
756
  /**
753
757
  * Create a color object with HSLA values
@@ -758,7 +762,7 @@ const rgb = (r, g, b, a)=> new Color(r, g, b, a);
758
762
  * @return {Color}
759
763
  * @memberof Utilities
760
764
  */
761
- const hsl = (h, s, l, a)=> new Color().setHSLA(h, s, l, a);
765
+ function hsl(h, s, l, a) { return new Color().setHSLA(h, s, l, a); }
762
766
 
763
767
  /**
764
768
  * Color object (red, green, blue, alpha) with some helpful functions
@@ -1017,11 +1021,11 @@ let canvasMaxSize = vec2(1920, 1200);
1017
1021
  * @memberof Settings */
1018
1022
  let canvasFixedSize = vec2();
1019
1023
 
1020
- /** Disables anti aliasing for pixel art if true
1024
+ /** Disables filtering for crisper pixel art if true
1021
1025
  * @type {Boolean}
1022
1026
  * @default
1023
1027
  * @memberof Settings */
1024
- let cavasPixelated = 1;
1028
+ let canvasPixelated = 1;
1025
1029
 
1026
1030
  /** Default font used for text rendering
1027
1031
  * @type {String}
@@ -1138,8 +1142,8 @@ let gamepadDirectionEmulateStick = 1;
1138
1142
  let inputWASDEmulateDirection = 1;
1139
1143
 
1140
1144
  /** True if touch gamepad should appear on mobile devices
1141
- * <br> - Supports left analog stick, 4 face buttons and start button (button 9)
1142
- * <br> - Must be set by end of gameInit to be activated
1145
+ * - Supports left analog stick, 4 face buttons and start button (button 9)
1146
+ * - Must be set by end of gameInit to be activated
1143
1147
  * @type {Boolean}
1144
1148
  * @default 0
1145
1149
  * @memberof Settings */
@@ -1228,32 +1232,32 @@ let medalDisplayIconSize = 50;
1228
1232
  * @default 0
1229
1233
  * @memberof Settings */
1230
1234
  let medalsPreventUnlock;
1231
- /*
1232
- LittleJS Object System
1233
- */
1235
+ /**
1236
+ * LittleJS Object System
1237
+ */
1234
1238
 
1235
1239
  'use strict';
1236
1240
 
1237
1241
  /**
1238
1242
  * LittleJS Object Base Object Class
1239
- * <br> - Base object class used by the engine
1240
- * <br> - Automatically adds self to object list
1241
- * <br> - Will be updated and rendered each frame
1242
- * <br> - Renders as a sprite from a tilesheet by default
1243
- * <br> - Can have color and addtive color applied
1244
- * <br> - 2d Physics and collision system
1245
- * <br> - Sorted by renderOrder
1246
- * <br> - Objects can have children attached
1247
- * <br> - Parents are updated before children, and set child transform
1248
- * <br> - Call destroy() to get rid of objects
1249
- * <br>
1250
- * <br>The physics system used by objects is simple and fast with some caveats...
1251
- * <br> - Collision uses the axis aligned size, the object's rotation angle is only for rendering
1252
- * <br> - Objects are guaranteed to not intersect tile collision from physics
1253
- * <br> - If an object starts or is moved inside tile collision, it will not collide with that tile
1254
- * <br> - Collision for objects can be set to be solid to block other objects
1255
- * <br> - Objects may get pushed into overlapping other solid objects, if so they will push away
1256
- * <br> - Solid objects are more performance intensive and should be used sparingly
1243
+ * - Base object class used by the engine
1244
+ * - Automatically adds self to object list
1245
+ * - Will be updated and rendered each frame
1246
+ * - Renders as a sprite from a tilesheet by default
1247
+ * - Can have color and addtive color applied
1248
+ * - 2d Physics and collision system
1249
+ * - Sorted by renderOrder
1250
+ * - Objects can have children attached
1251
+ * - Parents are updated before children, and set child transform
1252
+ * - Call destroy() to get rid of objects
1253
+ *
1254
+ * The physics system used by objects is simple and fast with some caveats...
1255
+ * - Collision uses the axis aligned size, the object's rotation angle is only for rendering
1256
+ * - Objects are guaranteed to not intersect tile collision from physics
1257
+ * - If an object starts or is moved inside tile collision, it will not collide with that tile
1258
+ * - Collision for objects can be set to be solid to block other objects
1259
+ * - Objects may get pushed into overlapping other solid objects, if so they will push away
1260
+ * - Solid objects are more performance intensive and should be used sparingly
1257
1261
  * @example
1258
1262
  * // create an engine object, normally you would first extend the class with your own
1259
1263
  * const pos = vec2(2,3);
@@ -1606,23 +1610,23 @@ class EngineObject
1606
1610
  }
1607
1611
  /**
1608
1612
  * LittleJS Drawing System
1609
- * <br> - Hybrid with both Canvas2D and WebGL available
1610
- * <br> - Super fast tile sheet rendering with WebGL
1611
- * <br> - Can apply rotation, mirror, color and additive color
1612
- * <br> - Many useful utility functions
1613
- * <br>
1614
- * <br>LittleJS uses a hybrid rendering solution with the best of both Canvas2D and WebGL.
1615
- * <br>There are 3 canvas/contexts available to draw to...
1616
- * <br> - mainCanvas - 2D background canvas, non WebGL stuff like tile layers are drawn here.
1617
- * <br> - glCanvas - Used by the accelerated WebGL batch rendering system.
1618
- * <br> - overlayCanvas - Another 2D canvas that appears on top of the other 2 canvases.
1619
- * <br>
1620
- * <br>The WebGL rendering system is very fast with some caveats...
1621
- * <br> - The default setup supports only 1 tile sheet, to support more call glCreateTexture and glSetTexture
1622
- * <br> - Switching blend modes (additive) or textures causes another draw call which is expensive in excess
1623
- * <br> - Group additive rendering together using renderOrder to mitigate this issue
1624
- * <br>
1625
- * <br>The LittleJS rendering solution is intentionally simple, feel free to adjust it for your needs!
1613
+ * - Hybrid with both Canvas2D and WebGL available
1614
+ * - Super fast tile sheet rendering with WebGL
1615
+ * - Can apply rotation, mirror, color and additive color
1616
+ * - Many useful utility functions
1617
+ *
1618
+ * LittleJS uses a hybrid rendering solution with the best of both Canvas2D and WebGL.
1619
+ * There are 3 canvas/contexts available to draw to...
1620
+ * mainCanvas - 2D background canvas, non WebGL stuff like tile layers are drawn here.
1621
+ * glCanvas - Used by the accelerated WebGL batch rendering system.
1622
+ * overlayCanvas - Another 2D canvas that appears on top of the other 2 canvases.
1623
+ *
1624
+ * The WebGL rendering system is very fast with some caveats...
1625
+ * - The default setup supports only 1 tile sheet, to support more call glCreateTexture and glSetTexture
1626
+ * - Switching blend modes (additive) or textures causes another draw call which is expensive in excess
1627
+ * - Group additive rendering together using renderOrder to mitigate this issue
1628
+ *
1629
+ * The LittleJS rendering solution is intentionally simple, feel free to adjust it for your needs!
1626
1630
  * @namespace Draw
1627
1631
  */
1628
1632
 
@@ -1666,7 +1670,7 @@ let tileImageSize, tileImageFixBleed, drawCount;
1666
1670
  * @param {Vector2} screenPos
1667
1671
  * @return {Vector2}
1668
1672
  * @memberof Draw */
1669
- const screenToWorld = (screenPos)=>
1673
+ function screenToWorld(screenPos)
1670
1674
  {
1671
1675
  ASSERT(mainCanvasSize.x && mainCanvasSize.y, 'mainCanvasSize is invalid');
1672
1676
  return screenPos.add(vec2(.5)).subtract(mainCanvasSize.scale(.5)).multiply(vec2(1/cameraScale,-1/cameraScale)).add(cameraPos);
@@ -1677,7 +1681,7 @@ const screenToWorld = (screenPos)=>
1677
1681
  * @param {Vector2} worldPos
1678
1682
  * @return {Vector2}
1679
1683
  * @memberof Draw */
1680
- const worldToScreen = (worldPos)=>
1684
+ function worldToScreen(worldPos)
1681
1685
  {
1682
1686
  ASSERT(mainCanvasSize.x && mainCanvasSize.y, 'mainCanvasSize is invalid');
1683
1687
  return worldPos.subtract(cameraPos).multiply(vec2(cameraScale,-cameraScale)).add(mainCanvasSize.scale(.5)).subtract(vec2(.5));
@@ -1886,9 +1890,9 @@ let engineFontImage;
1886
1890
 
1887
1891
  /**
1888
1892
  * Font Image Object - Draw text on a 2D canvas by using characters in an image
1889
- * <br> - 96 characters (from space to tilde) are stored in an image
1890
- * <br> - Uses a default 8x8 font if none is supplied
1891
- * <br> - You can also use fonts from the main tile sheet
1893
+ * - 96 characters (from space to tilde) are stored in an image
1894
+ * - Uses a default 8x8 font if none is supplied
1895
+ * - You can also use fonts from the main tile sheet
1892
1896
  * @example
1893
1897
  * // use built in font
1894
1898
  * const font = new ImageFont;
@@ -1928,7 +1932,7 @@ class FontImage
1928
1932
  {
1929
1933
  const context = this.context;
1930
1934
  context.save();
1931
- context.imageSmoothingEnabled = !cavasPixelated;
1935
+ context.imageSmoothingEnabled = !canvasPixelated;
1932
1936
 
1933
1937
  const size = this.tileSize;
1934
1938
  const drawSize = size.add(this.paddingSize).scale(scale);
@@ -1974,7 +1978,7 @@ class FontImage
1974
1978
  /** Returns true if fullscreen mode is active
1975
1979
  * @return {Boolean}
1976
1980
  * @memberof Draw */
1977
- const isFullscreen = ()=> document.fullscreenElement;
1981
+ function isFullscreen() { return document.fullscreenElement; }
1978
1982
 
1979
1983
  /** Toggle fullsceen mode
1980
1984
  * @memberof Draw */
@@ -1991,10 +1995,10 @@ function toggleFullscreen()
1991
1995
 
1992
1996
  /**
1993
1997
  * LittleJS Input System
1994
- * <br> - Tracks key down, pressed, and released
1995
- * <br> - Also tracks mouse buttons, position, and wheel
1996
- * <br> - Supports multiple gamepads
1997
- * <br> - Virtual gamepad for touch devices with touchGamepadSize
1998
+ * - Tracks key down, pressed, and released
1999
+ * - Also tracks mouse buttons, position, and wheel
2000
+ * - Supports multiple gamepads
2001
+ * - Virtual gamepad for touch devices with touchGamepadSize
1998
2002
  * @namespace Input
1999
2003
  */
2000
2004
 
@@ -2005,25 +2009,28 @@ function toggleFullscreen()
2005
2009
  * @param {Number} [device=0]
2006
2010
  * @return {Boolean}
2007
2011
  * @memberof Input */
2008
- const keyIsDown = (key, device=0)=> inputData[device] && inputData[device][key] & 1;
2012
+ function keyIsDown(key, device=0)
2013
+ { return inputData[device] && inputData[device][key] & 1; }
2009
2014
 
2010
2015
  /** Returns true if device key was pressed this frame
2011
2016
  * @param {Number} key
2012
2017
  * @param {Number} [device=0]
2013
2018
  * @return {Boolean}
2014
2019
  * @memberof Input */
2015
- const keyWasPressed = (key, device=0)=> inputData[device] && inputData[device][key] & 2 ? 1 : 0;
2020
+ function keyWasPressed(key, device=0)
2021
+ { return inputData[device] && inputData[device][key] & 2 ? 1 : 0; }
2016
2022
 
2017
2023
  /** Returns true if device key was released this frame
2018
2024
  * @param {Number} key
2019
2025
  * @param {Number} [device=0]
2020
2026
  * @return {Boolean}
2021
2027
  * @memberof Input */
2022
- const keyWasReleased = (key, device=0)=> inputData[device] && inputData[device][key] & 4 ? 1 : 0;
2028
+ function keyWasReleased(key, device=0)
2029
+ { return inputData[device] && inputData[device][key] & 4 ? 1 : 0; }
2023
2030
 
2024
2031
  /** Clears all input
2025
2032
  * @memberof Input */
2026
- const clearInput = ()=> inputData = [[]];
2033
+ function clearInput() { inputData = [[]]; }
2027
2034
 
2028
2035
  /** Returns true if mouse button is down
2029
2036
  * @function
@@ -2076,28 +2083,32 @@ let preventDefaultInput = 0;
2076
2083
  * @param {Number} [gamepad=0]
2077
2084
  * @return {Boolean}
2078
2085
  * @memberof Input */
2079
- const gamepadIsDown = (button, gamepad=0)=> keyIsDown(button, gamepad+1);
2086
+ function gamepadIsDown(button, gamepad=0)
2087
+ { return keyIsDown(button, gamepad+1); }
2080
2088
 
2081
2089
  /** Returns true if gamepad button was pressed
2082
2090
  * @param {Number} button
2083
2091
  * @param {Number} [gamepad=0]
2084
2092
  * @return {Boolean}
2085
2093
  * @memberof Input */
2086
- const gamepadWasPressed = (button, gamepad=0)=> keyWasPressed(button, gamepad+1);
2094
+ function gamepadWasPressed(button, gamepad=0)
2095
+ { return keyWasPressed(button, gamepad+1); }
2087
2096
 
2088
2097
  /** Returns true if gamepad button was released
2089
2098
  * @param {Number} button
2090
2099
  * @param {Number} [gamepad=0]
2091
2100
  * @return {Boolean}
2092
2101
  * @memberof Input */
2093
- const gamepadWasReleased = (button, gamepad=0)=> keyWasReleased(button, gamepad+1);
2102
+ function gamepadWasReleased(button, gamepad=0)
2103
+ { return keyWasReleased(button, gamepad+1); }
2094
2104
 
2095
2105
  /** Returns gamepad stick value
2096
2106
  * @param {Number} stick
2097
2107
  * @param {Number} [gamepad=0]
2098
2108
  * @return {Vector2}
2099
2109
  * @memberof Input */
2100
- const gamepadStick = (stick, gamepad=0)=> stickData[gamepad] ? stickData[gamepad][stick] || vec2() : vec2();
2110
+ function gamepadStick(stick, gamepad=0)
2111
+ { return stickData[gamepad] ? stickData[gamepad][stick] || vec2() : vec2(); }
2101
2112
 
2102
2113
  ///////////////////////////////////////////////////////////////////////////////
2103
2114
  // Input update called by engine
@@ -2136,12 +2147,18 @@ onkeydown = (e)=>
2136
2147
  e.repeat || (inputData[isUsingGamepad = 0][remapKey(e.which)] = 3);
2137
2148
  preventDefaultInput && e.preventDefault();
2138
2149
  }
2150
+
2139
2151
  onkeyup = (e)=>
2140
2152
  {
2141
2153
  if (debug && e.target != document.body) return;
2142
2154
  inputData[0][remapKey(e.which)] = 4;
2143
2155
  }
2144
- const remapKey = (c)=> inputWASDEmulateDirection ? c==87?38 : c==83?40 : c==65?37 : c==68?39 : c : c;
2156
+
2157
+ function remapKey(c)
2158
+ {
2159
+ return inputWASDEmulateDirection ?
2160
+ c==87?38 : c==83?40 : c==65?37 : c==68?39 : c : c;
2161
+ }
2145
2162
 
2146
2163
  ///////////////////////////////////////////////////////////////////////////////
2147
2164
  // Mouse event handlers
@@ -2150,10 +2167,10 @@ onmousedown = (e)=> {inputData[isUsingGamepad = 0][e.button] = 3; onmousemove(e)
2150
2167
  onmouseup = (e)=> inputData[0][e.button] = inputData[0][e.button] & 2 | 4;
2151
2168
  onmousemove = (e)=> mousePosScreen = mouseToScreen(e);
2152
2169
  onwheel = (e)=> e.ctrlKey || (mouseWheel = sign(e.deltaY));
2153
- oncontextmenu = (e)=> !1; // prevent right click menu
2170
+ oncontextmenu = (e)=> false; // prevent right click menu
2154
2171
 
2155
2172
  // convert a mouse or touch event position to screen space
2156
- const mouseToScreen = (mousePos)=>
2173
+ function mouseToScreen(mousePos)
2157
2174
  {
2158
2175
  if (!mainCanvas)
2159
2176
  return vec2(); // fix bug that can occur if user clicks before page loads
@@ -2199,8 +2216,7 @@ function gamepadsUpdate()
2199
2216
  if (gamepad)
2200
2217
  {
2201
2218
  // read clamp dead zone of analog sticks
2202
- const deadZone = .3, deadZoneMax = .8;
2203
- const applyDeadZone = (v)=>
2219
+ const deadZone = .3, deadZoneMax = .8, applyDeadZone = (v)=>
2204
2220
  v > deadZone ? percent( v, deadZone, deadZoneMax) :
2205
2221
  v < -deadZone ? -percent(-v, deadZone, deadZoneMax) : 0;
2206
2222
 
@@ -2233,11 +2249,12 @@ function gamepadsUpdate()
2233
2249
  /** Pulse the vibration hardware if it exists
2234
2250
  * @param {Number} [pattern=100] - a single value in miliseconds or vibration interval array
2235
2251
  * @memberof Input */
2236
- const vibrate = (pattern)=> vibrateEnable && navigator && navigator.vibrate && navigator.vibrate(pattern);
2252
+ function vibrate(pattern)
2253
+ { vibrateEnable && navigator && navigator.vibrate && navigator.vibrate(pattern); }
2237
2254
 
2238
2255
  /** Cancel any ongoing vibration
2239
2256
  * @memberof Input */
2240
- const vibrateStop = ()=> vibrate(0);
2257
+ function vibrateStop() { vibrate(0); }
2241
2258
 
2242
2259
  ///////////////////////////////////////////////////////////////////////////////
2243
2260
  // Touch input
@@ -2422,20 +2439,21 @@ function touchGamepadRender()
2422
2439
  }
2423
2440
  /**
2424
2441
  * LittleJS Audio System
2425
- * <br> - <a href=https://killedbyapixel.github.io/ZzFX/>ZzFX Sound Effects</a>
2426
- * <br> - <a href=https://keithclark.github.io/ZzFXM/>ZzFXM Music</a>
2427
- * <br> - Caches sounds and music for fast playback
2428
- * <br> - Can attenuate and apply stereo panning to sounds
2429
- * <br> - Ability to play mp3, ogg, and wave files
2430
- * <br> - Speech synthesis wrapper functions
2442
+ * - <a href=https://killedbyapixel.github.io/ZzFX/>ZzFX Sound Effects</a> - Sound Effect Generator
2443
+ * - <a href=https://keithclark.github.io/ZzFXM/>ZzFXM Music</a> - Music System
2444
+ * - Caches sounds and music for fast playback
2445
+ * - Can attenuate and apply stereo panning to sounds
2446
+ * - Ability to play mp3, ogg, and wave files
2447
+ * - Speech synthesis wrapper functions
2448
+ * @namespace Audio
2431
2449
  */
2432
2450
 
2433
2451
  'use strict';
2434
2452
 
2435
2453
  /**
2436
2454
  * Sound Object - Stores a zzfx sound for later use and can be played positionally
2437
- * <br>
2438
- * <br><b><a href=https://killedbyapixel.github.io/ZzFX/>Create sounds using the ZzFX Sound Designer.</a></b>
2455
+ *
2456
+ * <a href=https://killedbyapixel.github.io/ZzFX/>Create sounds using the ZzFX Sound Designer.</a>
2439
2457
  * @example
2440
2458
  * // create a sound
2441
2459
  * const sound_example = new Sound([.5,.5]);
@@ -2519,8 +2537,8 @@ class Sound
2519
2537
 
2520
2538
  /**
2521
2539
  * Music Object - Stores a zzfx music track for later use
2522
- * <br>
2523
- * <br><b><a href=https://keithclark.github.io/ZzFXM/>Create music with the ZzFXM tracker.</a></b>
2540
+ *
2541
+ * <a href=https://keithclark.github.io/ZzFXM/>Create music with the ZzFXM tracker.</a>
2524
2542
  * @example
2525
2543
  * // create some music
2526
2544
  * const music_example = new Music(
@@ -2630,14 +2648,15 @@ function speak(text, language='', volume=1, rate=1, pitch=1)
2630
2648
 
2631
2649
  /** Stop all queued speech
2632
2650
  * @memberof Audio */
2633
- const speakStop = ()=> speechSynthesis && speechSynthesis.cancel();
2651
+ function speakStop() {speechSynthesis && speechSynthesis.cancel();}
2634
2652
 
2635
2653
  /** Get frequency of a note on a musical scale
2636
2654
  * @param {Number} semitoneOffset - How many semitones away from the root note
2637
2655
  * @param {Number} [rootNoteFrequency=220] - Frequency at semitone offset 0
2638
2656
  * @return {Number} - The frequency of the note
2639
2657
  * @memberof Audio */
2640
- const getNoteFrequency = (semitoneOffset, rootFrequency=220)=> rootFrequency * 2**(semitoneOffset/12);
2658
+ function getNoteFrequency(semitoneOffset, rootFrequency=220)
2659
+ { return rootFrequency * 2**(semitoneOffset/12); }
2641
2660
 
2642
2661
  ///////////////////////////////////////////////////////////////////////////////
2643
2662
 
@@ -2695,12 +2714,12 @@ function playSamples(sampleChannels, volume=1, rate=1, pan=0, loop=0)
2695
2714
  // ZzFXMicro - Zuper Zmall Zound Zynth - v1.2.0 by Frank Force
2696
2715
 
2697
2716
  /** Generate and play a ZzFX sound
2698
- * <br>
2699
- * <br><b><a href=https://killedbyapixel.github.io/ZzFX/>Create sounds using the ZzFX Sound Designer.</a></b>
2717
+ *
2718
+ * <a href=https://killedbyapixel.github.io/ZzFX/>Create sounds using the ZzFX Sound Designer.</a>
2700
2719
  * @param {Array} zzfxSound - Array of ZzFX parameters, ex. [.5,.5]
2701
- * @return {Array} - Array of audio samples
2720
+ * @return {AudioBufferSourceNode} - The audio node of the sound played
2702
2721
  * @memberof Audio */
2703
- const zzfx = (...zzfxSound) => playSamples([zzfxG(...zzfxSound)]);
2722
+ function zzfx(...zzfxSound) { return playSamples([zzfxG(...zzfxSound)]); }
2704
2723
 
2705
2724
  /** Sample rate used for all ZzFX sounds
2706
2725
  * @default 44100
@@ -2708,7 +2727,29 @@ const zzfx = (...zzfxSound) => playSamples([zzfxG(...zzfxSound)]);
2708
2727
  const zzfxR = 44100;
2709
2728
 
2710
2729
  /** Generate samples for a ZzFX sound
2711
- * @memberof Audio */
2730
+ * @param {Number} [volume=1] - Volume scale (percent)
2731
+ * @param {Number} [randomness=.05] - How much to randomize frequency (percent Hz)
2732
+ * @param {Number} [frequency=220] - Frequency of sound (Hz)
2733
+ * @param {Number} [attack=0] - Attack time, how fast sound starts (seconds)
2734
+ * @param {Number} [sustain=0] - Sustain time, how long sound holds (seconds)
2735
+ * @param {Number} [release=.1] - Release time, how fast sound fades out (seconds)
2736
+ * @param {Number} [shape=0] - Shape of the sound wave
2737
+ * @param {Number} [shapeCurve=1] - Squarenes of wave (0=square, 1=normal, 2=pointy)
2738
+ * @param {Number} [slide=0] - How much to slide frequency (kHz/s)
2739
+ * @param {Number} [deltaSlide=0] - How much to change slide (kHz/s/s)
2740
+ * @param {Number} [pitchJump=0] - Frequency of pitch jump (Hz)
2741
+ * @param {Number} [pitchJumpTime=0] - Time of pitch jump (seconds)
2742
+ * @param {Number} [repeatTime=0] - Resets some parameters periodically (seconds)
2743
+ * @param {Number} [noise=0] - How much random noise to add (percent)
2744
+ * @param {Number} [modulation=0] - Frequency of modulation wave, negative flips phase (Hz)
2745
+ * @param {Number} [bitCrush=0] - Resamples at a lower frequency in (samples*100)
2746
+ * @param {Number} [delay=0] - Overlap sound with itself for reverb and flanger effects (seconds)
2747
+ * @param {Number} [sustainVolume=1] - Volume level for sustain (percent)
2748
+ * @param {Number} [decay=0] - Decay time, how long to reach sustain after attack (seconds)
2749
+ * @param {Number} [tremolo=0] - Trembling effect, rate controlled by repeat time (precent)
2750
+ * @return {Array} - Array of audio samples
2751
+ * @memberof Audio
2752
+ */
2712
2753
  function zzfxG
2713
2754
  (
2714
2755
  // parameters
@@ -2798,7 +2839,7 @@ function zzfxG
2798
2839
  * @param {Array} patterns - Array of pattern data
2799
2840
  * @param {Array} sequence - Array of pattern indexes
2800
2841
  * @param {Number} [BPM=125] - Playback speed of the song in BPM
2801
- * @returns {Array} - Left and right channel sample data
2842
+ * @return {Array} - Left and right channel sample data
2802
2843
  * @memberof Audio */
2803
2844
  function zzfxM(instruments, patterns, sequence, BPM = 125)
2804
2845
  {
@@ -2896,13 +2937,13 @@ function zzfxM(instruments, patterns, sequence, BPM = 125)
2896
2937
  }
2897
2938
  /**
2898
2939
  * LittleJS Tile Layer System
2899
- * <br> - Caches arrays of tiles to off screen canvas for fast rendering
2900
- * <br> - Unlimted numbers of layers, allocates canvases as needed
2901
- * <br> - Interfaces with EngineObject for collision
2902
- * <br> - Collision layer is separate from visible layers
2903
- * <br> - It is recommended to have a visible layer that matches the collision
2904
- * <br> - Tile layers can be drawn to using their context with canvas2d
2905
- * <br> - Drawn directly to the main canvas without using WebGL
2940
+ * - Caches arrays of tiles to off screen canvas for fast rendering
2941
+ * - Unlimted numbers of layers, allocates canvases as needed
2942
+ * - Interfaces with EngineObject for collision
2943
+ * - Collision layer is separate from visible layers
2944
+ * - It is recommended to have a visible layer that matches the collision
2945
+ * - Tile layers can be drawn to using their context with canvas2d
2946
+ * - Drawn directly to the main canvas without using WebGL
2906
2947
  * @namespace TileCollision
2907
2948
  */
2908
2949
 
@@ -2933,15 +2974,19 @@ function initTileCollision(size)
2933
2974
  * @param {Vector2} pos
2934
2975
  * @param {Number} [data=0]
2935
2976
  * @memberof TileCollision */
2936
- const setTileCollisionData = (pos, data=0)=>
2977
+ function setTileCollisionData(pos, data=0)
2978
+ {
2937
2979
  pos.arrayCheck(tileCollisionSize) && (tileCollision[(pos.y|0)*tileCollisionSize.x+pos.x|0] = data);
2980
+ }
2938
2981
 
2939
2982
  /** Get tile collision data
2940
2983
  * @param {Vector2} pos
2941
2984
  * @return {Number}
2942
2985
  * @memberof TileCollision */
2943
- const getTileCollisionData = (pos)=>
2944
- pos.arrayCheck(tileCollisionSize) ? tileCollision[(pos.y|0)*tileCollisionSize.x+pos.x|0] : 0;
2986
+ function getTileCollisionData(pos)
2987
+ {
2988
+ return pos.arrayCheck(tileCollisionSize) ? tileCollision[(pos.y|0)*tileCollisionSize.x+pos.x|0] : 0;
2989
+ }
2945
2990
 
2946
2991
  /** Check if collision with another object should occur
2947
2992
  * @param {Vector2} pos
@@ -3035,10 +3080,10 @@ class TileLayerData
3035
3080
 
3036
3081
  /**
3037
3082
  * Tile layer object - cached rendering system for tile layers
3038
- * <br> - Each Tile layer is rendered to an off screen canvas
3039
- * <br> - To allow dynamic modifications, layers are rendered using canvas 2d
3040
- * <br> - Some devices like mobile phones are limited to 4k texture resolution
3041
- * <br> - So with 16x16 tiles this limits layers to 256x256 on mobile devices
3083
+ * - Each Tile layer is rendered to an off screen canvas
3084
+ * - To allow dynamic modifications, layers are rendered using canvas 2d
3085
+ * - Some devices like mobile phones are limited to 4k texture resolution
3086
+ * - So with 16x16 tiles this limits layers to 256x256 on mobile devices
3042
3087
  * @extends EngineObject
3043
3088
  * @example
3044
3089
  * // create tile collision and visible tile layer
@@ -3238,12 +3283,9 @@ constructor(pos, size=tileCollisionSize, tileSize=tileSizeDefault, scale=vec2(1)
3238
3283
  drawRect(pos, size, color, angle)
3239
3284
  { this.drawTile(pos, size, -1, 0, color, angle); }
3240
3285
  }
3241
- /*
3242
- LittleJS Particle System
3243
- - Spawns particles with randomness from parameters
3244
- - Updates particle physics
3245
- - Fast particle rendering
3246
- */
3286
+ /**
3287
+ * LittleJS Particle System
3288
+ */
3247
3289
 
3248
3290
  'use strict';
3249
3291
 
@@ -3549,9 +3591,9 @@ class Particle extends EngineObject
3549
3591
  }
3550
3592
  /**
3551
3593
  * LittleJS Medal System
3552
- * <br> - Tracks and displays medals
3553
- * <br> - Saves medals to local storage
3554
- * <br> - Newgrounds integration
3594
+ * - Tracks and displays medals
3595
+ * - Saves medals to local storage
3596
+ * - Newgrounds integration
3555
3597
  * @namespace Medals
3556
3598
  */
3557
3599
 
@@ -3568,8 +3610,8 @@ let medalsDisplayQueue = [], medalsSaveName, medalsDisplayTimeLast;
3568
3610
  ///////////////////////////////////////////////////////////////////////////////
3569
3611
 
3570
3612
  /** Initialize medals with a save name used for storage
3571
- * <br> - Call this after creating all medals
3572
- * <br> - Checks if medals are unlocked
3613
+ * - Call this after creating all medals
3614
+ * - Checks if medals are unlocked
3573
3615
  * @param {String} saveName
3574
3616
  * @memberof Medals */
3575
3617
  function medalsInit(saveName)
@@ -3839,20 +3881,43 @@ class Newgrounds
3839
3881
  CryptoJS()
3840
3882
  {
3841
3883
  ///////////////////////////////////////////////////////////////////////////////
3842
- // Crypto-JS - https://github.com/brix/crypto-js [The MIT License (MIT)]
3843
- // Copyright (c) 2009-2013 Jeff Mott Copyright (c) 2013-2016 Evan Vosberg
3844
-
3884
+ // Crypto-JS - https://github.com/brix/crypto-js - MIT License
3885
+ //
3886
+ // [The MIT License (MIT)](http://opensource.org/licenses/MIT)
3887
+ //
3888
+ // Copyright (c) 2009-2013 Jeff Mott
3889
+ // Copyright (c) 2013-2016 Evan Vosberg
3890
+ //
3891
+ // Permission is hereby granted, free of charge, to any person obtaining a copy
3892
+ // of this software and associated documentation files (the "Software"), to deal
3893
+ // in the Software without restriction, including without limitation the rights
3894
+ // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
3895
+ // copies of the Software, and to permit persons to whom the Software is
3896
+ // furnished to do so, subject to the following conditions:
3897
+ //
3898
+ // The above copyright notice and this permission notice shall be included in
3899
+ // all copies or substantial portions of the Software.
3900
+ //
3901
+ // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
3902
+ // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
3903
+ // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
3904
+ // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
3905
+ // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
3906
+ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
3907
+ // THE SOFTWARE.
3845
3908
  return eval(Function("[M='GBMGXz^oVYPPKKbB`agTXU|LxPc_ZBcMrZvCr~wyGfWrwk@ATqlqeTp^N?p{we}jIpEnB_sEr`l?YDkDhWhprc|Er|XETG?pTl`e}dIc[_N~}fzRycIfpW{HTolvoPB_FMe_eH~BTMx]yyOhv?biWPCGc]kABencBhgERHGf{OL`Dj`c^sh@canhy[secghiyotcdOWgO{tJIE^JtdGQRNSCrwKYciZOa]Y@tcRATYKzv|sXpboHcbCBf`}SKeXPFM|RiJsSNaIb]QPc[D]Jy_O^XkOVTZep`ONmntLL`Qz~UupHBX_Ia~WX]yTRJIxG`ioZ{fefLJFhdyYoyLPvqgH?b`[TMnTwwfzDXhfM?rKs^aFr|nyBdPmVHTtAjXoYUloEziWDCw_suyYT~lSMksI~ZNCS[Bex~j]Vz?kx`gdYSEMCsHpjbyxQvw|XxX_^nQYue{sBzVWQKYndtYQMWRef{bOHSfQhiNdtR{o?cUAHQAABThwHPT}F{VvFmgN`E@FiFYS`UJmpQNM`X|tPKHlccT}z}k{sACHL?Rt@MkWplxO`ASgh?hBsuuP|xD~LSH~KBlRs]t|l|_tQAroDRqWS^SEr[sYdPB}TAROtW{mIkE|dWOuLgLmJrucGLpebrAFKWjikTUzS|j}M}szasKOmrjy[?hpwnEfX[jGpLt@^v_eNwSQHNwtOtDgWD{rk|UgASs@mziIXrsHN_|hZuxXlPJOsA^^?QY^yGoCBx{ekLuZzRqQZdsNSx@ezDAn{XNj@fRXIwrDX?{ZQHwTEfu@GhxDOykqts|n{jOeZ@c`dvTY?e^]ATvWpb?SVyg]GC?SlzteilZJAL]mlhLjYZazY__qcVFYvt@|bIQnSno@OXyt]OulzkWqH`rYFWrwGs`v|~XeTsIssLrbmHZCYHiJrX}eEzSssH}]l]IhPQhPoQ}rCXLyhFIT[clhzYOvyHqigxmjz`phKUU^TPf[GRAIhNqSOdayFP@FmKmuIzMOeoqdpxyCOwCthcLq?n`L`tLIBboNn~uXeFcPE{C~mC`h]jUUUQe^`UqvzCutYCgct|SBrAeiYQW?X~KzCz}guXbsUw?pLsg@hDArw?KeJD[BN?GD@wgFWCiHq@Ypp_QKFixEKWqRp]oJFuVIEvjDcTFu~Zz]a{IcXhWuIdMQjJ]lwmGQ|]g~c]Hl]pl`Pd^?loIcsoNir_kikBYyg?NarXZEGYspt_vLBIoj}LI[uBFvm}tbqvC|xyR~a{kob|HlctZslTGtPDhBKsNsoZPuH`U`Fqg{gKnGSHVLJ^O`zmNgMn~{rsQuoymw^JY?iUBvw_~mMr|GrPHTERS[MiNpY[Mm{ggHpzRaJaoFomtdaQ_?xuTRm}@KjU~RtPsAdxa|uHmy}n^i||FVL[eQAPrWfLm^ndczgF~Nk~aplQvTUpHvnTya]kOenZlLAQIm{lPl@CCTchvCF[fI{^zPkeYZTiamoEcKmBMfZhk_j_~Fjp|wPVZlkh_nHu]@tP|hS@^G^PdsQ~f[RqgTDqezxNFcaO}HZhb|MMiNSYSAnQWCDJukT~e|OTgc}sf[cnr?fyzTa|EwEtRG|I~|IO}O]S|rp]CQ}}DWhSjC_|z|oY|FYl@WkCOoPuWuqr{fJu?Brs^_EBI[@_OCKs}?]O`jnDiXBvaIWhhMAQDNb{U`bqVR}oqVAvR@AZHEBY@depD]OLh`kf^UsHhzKT}CS}HQKy}Q~AeMydXPQztWSSzDnghULQgMAmbWIZ|lWWeEXrE^EeNoZApooEmrXe{NAnoDf`m}UNlRdqQ@jOc~HLOMWs]IDqJHYoMziEedGBPOxOb?[X`KxkFRg@`mgFYnP{hSaxwZfBQqTm}_?RSEaQga]w[vxc]hMne}VfSlqUeMo_iqmd`ilnJXnhdj^EEFifvZyxYFRf^VaqBhLyrGlk~qowqzHOBlOwtx?i{m~`n^G?Yxzxux}b{LSlx]dS~thO^lYE}bzKmUEzwW^{rPGhbEov[Plv??xtyKJshbG`KuO?hjBdS@Ru}iGpvFXJRrvOlrKN?`I_n_tplk}kgwSXuKylXbRQ]]?a|{xiT[li?k]CJpwy^o@ebyGQrPfF`aszGKp]baIx~H?ElETtFh]dz[OjGl@C?]VDhr}OE@V]wLTc[WErXacM{We`F|utKKjgllAxvsVYBZ@HcuMgLboFHVZmi}eIXAIFhS@A@FGRbjeoJWZ_NKd^oEH`qgy`q[Tq{x?LRP|GfBFFJV|fgZs`MLbpPYUdIV^]mD@FG]pYAT^A^RNCcXVrPsgk{jTrAIQPs_`mD}rOqAZA[}RETFz]WkXFTz_m{N@{W@_fPKZLT`@aIqf|L^Mb|crNqZ{BVsijzpGPEKQQZGlApDn`ruH}cvF|iXcNqK}cxe_U~HRnKV}sCYb`D~oGvwG[Ca|UaybXea~DdD~LiIbGRxJ_VGheI{ika}KC[OZJLn^IBkPrQj_EuoFwZ}DpoBRcK]Q}?EmTv~i_Tul{bky?Iit~tgS|o}JL_VYcCQdjeJ_MfaA`FgCgc[Ii|CBHwq~nbJeYTK{e`CNstKfTKPzw{jdhp|qsZyP_FcugxCFNpKitlR~vUrx^NrSVsSTaEgnxZTmKc`R|lGJeX}ccKLsQZQhsFkeFd|ckHIVTlGMg`~uPwuHRJS_CPuN_ogXe{Ba}dO_UBhuNXby|h?JlgBIqMKx^_u{molgL[W_iavNQuOq?ap]PGB`clAicnl@k~pA?MWHEZ{HuTLsCpOxxrKlBh]FyMjLdFl|nMIvTHyGAlPogqfZ?PlvlFJvYnDQd}R@uAhtJmDfe|iJqdkYr}r@mEjjIetDl_I`TELfoR|qTBu@Tic[BaXjP?dCS~MUK[HPRI}OUOwAaf|_}HZzrwXvbnNgltjTwkBE~MztTQhtRSWoQHajMoVyBBA`kdgK~h`o[J`dm~pm]tk@i`[F~F]DBlJKklrkR]SNw@{aG~Vhl`KINsQkOy?WhcqUMTGDOM_]bUjVd|Yh_KUCCgIJ|LDIGZCPls{RzbVWVLEhHvWBzKq|^N?DyJB|__aCUjoEgsARki}j@DQXS`RNU|DJ^a~d{sh_Iu{ONcUtSrGWW@cvUjefHHi}eSSGrNtO?cTPBShLqzwMVjWQQCCFB^culBjZHEK_{dO~Q`YhJYFn]jq~XSnG@[lQr]eKrjXpG~L^h~tDgEma^AUFThlaR{xyuP@[^VFwXSeUbVetufa@dX]CLyAnDV@Bs[DnpeghJw^?UIana}r_CKGDySoRudklbgio}kIDpA@McDoPK?iYcG?_zOmnWfJp}a[JLR[stXMo?_^Ng[whQlrDbrawZeSZ~SJstIObdDSfAA{MV}?gNunLOnbMv_~KFQUAjIMj^GkoGxuYtYbGDImEYiwEMyTpMxN_LSnSMdl{bg@dtAnAMvhDTBR_FxoQgANniRqxd`pWv@rFJ|mWNWmh[GMJz_Nq`BIN@KsjMPASXORcdHjf~rJfgZYe_uulzqM_KdPlMsuvU^YJuLtofPhGonVOQxCMuXliNvJIaoC?hSxcxKVVxWlNs^ENDvCtSmO~WxI[itnjs^RDvI@KqG}YekaSbTaB]ki]XM@[ZnDAP~@|BzLRgOzmjmPkRE@_sobkT|SszXK[rZN?F]Z_u}Yue^[BZgLtR}FHzWyxWEX^wXC]MJmiVbQuBzkgRcKGUhOvUc_bga|Tx`KEM`JWEgTpFYVeXLCm|mctZR@uKTDeUONPozBeIkrY`cz]]~WPGMUf`MNUGHDbxZuO{gmsKYkAGRPqjc|_FtblEOwy}dnwCHo]PJhN~JoteaJ?dmYZeB^Xd?X^pOKDbOMF@Ugg^hETLdhwlA}PL@_ur|o{VZosP?ntJ_kG][g{Zq`Tu]dzQlSWiKfnxDnk}KOzp~tdFstMobmy[oPYjyOtUzMWdjcNSUAjRuqhLS@AwB^{BFnqjCmmlk?jpn}TksS{KcKkDboXiwK]qMVjm~V`LgWhjS^nLGwfhAYrjDSBL_{cRus~{?xar_xqPlArrYFd?pHKdMEZzzjJpfC?Hv}mAuIDkyBxFpxhstTx`IO{rp}XGuQ]VtbHerlRc_LFGWK[XluFcNGUtDYMZny[M^nVKVeMllQI[xtvwQnXFlWYqxZZFp_|]^oWX[{pOMpxXxvkbyJA[DrPzwD|LW|QcV{Nw~U^dgguSpG]ClmO@j_TENIGjPWwgdVbHganhM?ema|dBaqla|WBd`poj~klxaasKxGG^xbWquAl~_lKWxUkDFagMnE{zHug{b`A~IYcQYBF_E}wiA}K@yxWHrZ{[d~|ARsYsjeNWzkMs~IOqqp[yzDE|WFrivsidTcnbHFRoW@XpAV`lv_zj?B~tPCppRjgbbDTALeFaOf?VcjnKTQMLyp{NwdylHCqmo?oelhjWuXj~}{fpuX`fra?GNkDiChYgVSh{R[BgF~eQa^WVz}ATI_CpY?g_diae]|ijH`TyNIF}|D_xpmBq_JpKih{Ba|sWzhnAoyraiDvk`h{qbBfsylBGmRH}DRPdryEsSaKS~tIaeF[s]I~xxHVrcNe@Jjxa@jlhZueLQqHh_]twVMqG_EGuwyab{nxOF?`HCle}nBZzlTQjkLmoXbXhOtBglFoMz?eqre`HiE@vNwBulglmQjj]DB@pPkPUgA^sjOAUNdSu_`oAzar?n?eMnw{{hYmslYi[TnlJD'",...']charCodeAtUinyxpf',"for(;e<10359;c[e++]=p-=128,A=A?p-A&&A:p==34&&p)for(p=1;p<128;y=f.map((n,x)=>(U=r[n]*2+1,U=Math.log(U/(h-U)),t-=a[x]*U,U/500)),t=~-h/(1+Math.exp(t))|1,i=o%h<t,o=o%h+(i?t:h-t)*(o>>17)-!i*t,f.map((n,x)=>(U=r[n]+=(i*h/2-r[n]<<13)/((C[n]+=C[n]<5)+1/20)>>13,a[x]+=y[x]*(i-t/h))),p=p*2+i)for(f='010202103203210431053105410642065206541'.split(t=0).map((n,x)=>(U=0,[...n].map((n,x)=>(U=U*997+(c[e-n]|0)|0)),h*32-1&U*997+p+!!A*129)*12+x);o<h*32;o=o*64|M.charCodeAt(d++)&63);for(C=String.fromCharCode(...c);r=/[\0-#?@\\\\~]/.exec(C);)with(C.split(r))C=join(shift());return C")([],[],1<<17,[0,0,0,0,0,0,0,0,0,0,0,0],new Uint16Array(51e6).fill(1<<15),new Uint8Array(51e6),0,0,0,0));
3909
+ // end of Crypto-JS
3910
+ ///////////////////////////////////////////////////////////////////////////////
3846
3911
  }
3847
3912
  }
3848
3913
  /**
3849
3914
  * LittleJS WebGL Interface
3850
- * <br> - All webgl used by the engine is wrapped up here
3851
- * <br> - For normal stuff you won't need to see or call anything in this file
3852
- * <br> - For advanced stuff there are helper functions to create shaders, textures, etc
3853
- * <br> - Can be disabled with glEnable to revert to 2D canvas rendering
3854
- * <br> - Batches sprite rendering on GPU for incredibly fast performance
3855
- * <br> - Sprite transform math is done in the shader where possible
3915
+ * - All webgl used by the engine is wrapped up here
3916
+ * - For normal stuff you won't need to see or call anything in this file
3917
+ * - For advanced stuff there are helper functions to create shaders, textures, etc
3918
+ * - Can be disabled with glEnable to revert to 2D canvas rendering
3919
+ * - Batches sprite rendering on GPU for incredibly fast performance
3920
+ * - Sprite transform math is done in the shader where possible
3856
3921
  * @namespace WebGL
3857
3922
  */
3858
3923
 
@@ -3927,7 +3992,7 @@ function glSetBlendMode(additive)
3927
3992
  }
3928
3993
 
3929
3994
  /** Set the WebGl texture, not normally necessary unless multiple tile sheets are used
3930
- * <br> - This may also flush the gl buffer resulting in more draw calls and worse performance
3995
+ * - This may also flush the gl buffer resulting in more draw calls and worse performance
3931
3996
  * @param {WebGLTexture} [texture=glTileTexture]
3932
3997
  * @memberof WebGL */
3933
3998
  function glSetTexture(texture=glTileTexture)
@@ -3988,7 +4053,7 @@ function glCreateTexture(image)
3988
4053
  image && image.width && glContext.texImage2D(gl_TEXTURE_2D, 0, gl_RGBA, gl_RGBA, gl_UNSIGNED_BYTE, image);
3989
4054
 
3990
4055
  // use point filtering for pixelated rendering
3991
- const filter = cavasPixelated ? gl_NEAREST : gl_LINEAR;
4056
+ const filter = canvasPixelated ? gl_NEAREST : gl_LINEAR;
3992
4057
  glContext.texParameteri(gl_TEXTURE_2D, gl_TEXTURE_MIN_FILTER, filter);
3993
4058
  glContext.texParameteri(gl_TEXTURE_2D, gl_TEXTURE_MAG_FILTER, filter);
3994
4059
  glContext.texParameteri(gl_TEXTURE_2D, gl_TEXTURE_WRAP_S, gl_CLAMP_TO_EDGE);
@@ -4239,27 +4304,23 @@ gl_INDICIES_PER_VERT = 6,
4239
4304
  gl_MAX_BATCH = 1<<16,
4240
4305
  gl_VERTEX_BYTE_STRIDE = (4 * 2) * 2 + (4) * 2, // vec2 * 2 + (char * 4) * 2
4241
4306
  gl_VERTEX_BUFFER_SIZE = gl_MAX_BATCH * gl_VERTICES_PER_QUAD * gl_VERTEX_BYTE_STRIDE;
4242
- /*
4243
- LittleJS - The Tiny JavaScript Game Engine That Can!
4244
- MIT License - Copyright 2021 Frank Force
4245
-
4246
- Engine Features
4247
- - Object oriented system with base class engine object
4248
- - Base class object handles update, physics, collision, rendering, etc
4249
- - Engine helper classes and functions like Vector2, Color, and Timer
4250
- - Super fast rendering system for tile sheets
4251
- - Sound effects audio with zzfx and music with zzfxm
4252
- - Input processing system with gamepad and touchscreen support
4253
- - Tile layer rendering and collision system
4254
- - Particle effect system
4255
- - Medal system tracks and displays achievements
4256
- - Debug tools and debug rendering system
4257
- - Post processing effects
4258
- - Call engineInit() to start it up!
4259
- */
4260
-
4261
- /**
4262
- * LittleJS Engine Globals
4307
+ /**
4308
+ * LittleJS - The Tiny JavaScript Game Engine That Can!
4309
+ * MIT License - Copyright 2021 Frank Force
4310
+ *
4311
+ * Engine Features
4312
+ * - Object oriented system with base class engine object
4313
+ * - Base class object handles update, physics, collision, rendering, etc
4314
+ * - Engine helper classes and functions like Vector2, Color, and Timer
4315
+ * - Super fast rendering system for tile sheets
4316
+ * - Sound effects audio with zzfx and music with zzfxm
4317
+ * - Input processing system with gamepad and touchscreen support
4318
+ * - Tile layer rendering and collision system
4319
+ * - Particle effect system
4320
+ * - Medal system tracks and displays achievements
4321
+ * - Debug tools and debug rendering system
4322
+ * - Post processing effects
4323
+ * - Call engineInit() to start it up!
4263
4324
  * @namespace Engine
4264
4325
  */
4265
4326
 
@@ -4275,7 +4336,7 @@ const engineName = 'LittleJS';
4275
4336
  * @type {String}
4276
4337
  * @default
4277
4338
  * @memberof Engine */
4278
- const engineVersion = '1.6.4';
4339
+ const engineVersion = '1.6.6';
4279
4340
 
4280
4341
  /** Frames per second to update objects
4281
4342
  * @type {Number}
@@ -4345,10 +4406,11 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
4345
4406
  debug && (tileImage.onload=()=>ASSERT(1)); // tile sheet can not reloaded
4346
4407
 
4347
4408
  // setup html
4348
- const styleBody = 'margin:0;overflow:hidden;background:#000' + // fill the window
4349
- ';touch-action:none' + // prevent mobile pinch to resize
4350
- ';user-select:none' + // prevent mobile hold to select
4351
- ';-webkit-user-select:none'; // compatibility for ios
4409
+ const styleBody = 'margin:0;overflow:hidden;' + // fill the window
4410
+ 'background:#000;' + // set background color
4411
+ 'touch-action:none;' + // prevent mobile pinch to resize
4412
+ 'user-select:none;' + // prevent mobile hold to select
4413
+ '-webkit-user-select:none'; // compatibility for ios
4352
4414
  document.body.style = styleBody;
4353
4415
  document.body.appendChild(mainCanvas = document.createElement('canvas'));
4354
4416
  mainContext = mainCanvas.getContext('2d');
@@ -4361,8 +4423,10 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
4361
4423
  document.body.appendChild(overlayCanvas = document.createElement('canvas'));
4362
4424
  overlayContext = overlayCanvas.getContext('2d');
4363
4425
 
4364
- // set canvas style to fill the window
4365
- const styleCanvas = 'position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)';
4426
+ // set canvas style
4427
+ const styleCanvas = 'position:absolute;' +
4428
+ 'top:50%;left:50%;transform:translate(-50%,-50%);' + // center the canvas
4429
+ (canvasPixelated?'image-rendering:pixelated':''); // set pixelated rendering
4366
4430
  (glCanvas||mainCanvas).style = mainCanvas.style = overlayCanvas.style = styleCanvas;
4367
4431
 
4368
4432
  gameInit();
@@ -4495,7 +4559,7 @@ function enginePreRender()
4495
4559
  mainCanvasSize = vec2(mainCanvas.width, mainCanvas.height);
4496
4560
 
4497
4561
  // disable smoothing for pixel art
4498
- mainContext.imageSmoothingEnabled = !cavasPixelated;
4562
+ mainContext.imageSmoothingEnabled = !canvasPixelated;
4499
4563
 
4500
4564
  // setup gl rendering if enabled
4501
4565
  glEnable && glPreRender();
@@ -4509,7 +4573,7 @@ function engineObjectsUpdate()
4509
4573
  engineObjectsCollide = engineObjects.filter(o=>o.collideSolidObjects);
4510
4574
 
4511
4575
  // recursive object update
4512
- const updateObject = (o)=>
4576
+ function updateObject(o)
4513
4577
  {
4514
4578
  if (!o.destroyed)
4515
4579
  {