littlejsengine 1.6.4 → 1.6.92

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.
Files changed (61) hide show
  1. package/README.md +61 -41
  2. package/build/littlejs.d.ts +160 -165
  3. package/build/littlejs.esm.js +451 -367
  4. package/build/littlejs.esm.min.js +1 -1
  5. package/build/littlejs.js +408 -314
  6. package/build/littlejs.min.js +1 -1
  7. package/build/littlejs.release.js +2573 -2470
  8. package/examples/breakout/game.js +5 -1
  9. package/examples/breakout/index.html +5 -5
  10. package/examples/breakoutTutorial/README.md +6 -6
  11. package/examples/breakoutTutorial/game.js +3 -0
  12. package/examples/breakoutTutorial/index.html +3 -3
  13. package/examples/electron/build.js +105 -0
  14. package/examples/electron/game.js +4 -2
  15. package/examples/electron/index.html +13 -2
  16. package/examples/electron/package.json +5 -3
  17. package/examples/empty/game.js +3 -1
  18. package/examples/empty/index.html +1 -1
  19. package/examples/favicon.png +0 -0
  20. package/examples/js13k/build.bat +2 -0
  21. package/examples/js13k/build.js +110 -0
  22. package/examples/js13k/game.js +109 -0
  23. package/examples/js13k/index.html +18 -0
  24. package/examples/js13k/tiles.png +0 -0
  25. package/examples/module/game.js +9 -3
  26. package/examples/module/index.html +2 -2
  27. package/examples/particles/index.html +7 -13
  28. package/examples/platformer/game.js +5 -2
  29. package/examples/platformer/gameEffects.js +1 -2
  30. package/examples/platformer/gamePlayer.js +2 -2
  31. package/examples/platformer/index.html +8 -8
  32. package/examples/puzzle/game.js +6 -4
  33. package/examples/puzzle/index.html +4 -4
  34. package/examples/starter/build.bat +2 -78
  35. package/examples/starter/build.js +109 -0
  36. package/examples/starter/game.js +4 -1
  37. package/examples/starter/index.html +15 -18
  38. package/examples/stress/index.html +2 -4
  39. package/examples/typescript/build.bat +2 -14
  40. package/examples/typescript/build.js +31 -0
  41. package/examples/typescript/game.js +94 -89
  42. package/examples/typescript/game.ts +9 -3
  43. package/examples/typescript/index.html +2 -2
  44. package/package.json +3 -3
  45. package/src/engine.js +30 -31
  46. package/src/engineAudio.js +44 -20
  47. package/src/engineBuild.bat +2 -132
  48. package/src/engineBuild.js +143 -0
  49. package/src/engineDebug.js +21 -32
  50. package/src/engineDraw.js +36 -28
  51. package/src/engineExport.js +41 -51
  52. package/src/engineInput.js +33 -20
  53. package/src/engineMedals.js +31 -8
  54. package/src/engineObject.js +34 -32
  55. package/src/engineParticles.js +9 -12
  56. package/src/engineRelease.js +18 -20
  57. package/src/engineSettings.js +4 -4
  58. package/src/engineTileLayer.js +49 -32
  59. package/src/engineUtilities.js +96 -74
  60. package/src/engineWebGL.js +8 -8
  61. package/examples/electron/build.bat +0 -52
@@ -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
 
@@ -39,12 +34,6 @@ const debugPointSize = .5;
39
34
  * @memberof Debug */
40
35
  let showWatermark = 1;
41
36
 
42
- /** True if god mode is enabled, handle this however you want
43
- * @type {Boolean}
44
- * @default
45
- * @memberof Debug */
46
- let godMode = 0;
47
-
48
37
  /** Key code used to toggle debug mode, Esc by default
49
38
  * @type {Boolean}
50
39
  * @default
@@ -62,7 +51,7 @@ debugParticles = 0, debugGamepads = 0, debugMedals = 0, debugTakeScreenshot, dow
62
51
  * @param {Boolean} assertion
63
52
  * @param {Object} output
64
53
  * @memberof Debug */
65
- const ASSERT = enableAsserts ? (...assert)=> console.assert(...assert) : ()=>{};
54
+ function ASSERT(...assert) { enableAsserts && console.assert(...assert); }
66
55
 
67
56
  /** Draw a debug rectangle in world space
68
57
  * @param {Vector2} pos
@@ -72,7 +61,7 @@ const ASSERT = enableAsserts ? (...assert)=> console.assert(...assert) : ()=>{};
72
61
  * @param {Number} [angle=0]
73
62
  * @param {Boolean} [fill=false]
74
63
  * @memberof Debug */
75
- const debugRect = (pos, size=vec2(), color='#fff', time=0, angle=0, fill=false)=>
64
+ function debugRect(pos, size=vec2(), color='#fff', time=0, angle=0, fill=false)
76
65
  {
77
66
  ASSERT(typeof color == 'string'); // pass in regular html strings as colors
78
67
  debugPrimitives.push({pos, size:vec2(size), color, time:new Timer(time), angle, fill});
@@ -85,7 +74,7 @@ const debugRect = (pos, size=vec2(), color='#fff', time=0, angle=0, fill=false)=
85
74
  * @param {Number} [time=0]
86
75
  * @param {Boolean} [fill=false]
87
76
  * @memberof Debug */
88
- const debugCircle = (pos, radius=0, color='#fff', time=0, fill=false)=>
77
+ function debugCircle(pos, radius=0, color='#fff', time=0, fill=false)
89
78
  {
90
79
  ASSERT(typeof color == 'string'); // pass in regular html strings as colors
91
80
  debugPrimitives.push({pos, size:radius, color, time:new Timer(time), angle:0, fill});
@@ -97,7 +86,7 @@ const debugCircle = (pos, radius=0, color='#fff', time=0, fill=false)=>
97
86
  * @param {Number} [time=0]
98
87
  * @param {Number} [angle=0]
99
88
  * @memberof Debug */
100
- const debugPoint = (pos, color, time, angle)=> debugRect(pos, 0, color, time, angle);
89
+ function debugPoint(pos, color, time, angle) {debugRect(pos, 0, color, time, angle);}
101
90
 
102
91
  /** Draw a debug line in world space
103
92
  * @param {Vector2} posA
@@ -106,7 +95,7 @@ const debugPoint = (pos, color, time, angle)=> debugRect(pos, 0, color, time, an
106
95
  * @param {Number} [thickness=.1]
107
96
  * @param {Number} [time=0]
108
97
  * @memberof Debug */
109
- const debugLine = (posA, posB, color, thickness=.1, time)=>
98
+ function debugLine(posA, posB, color, thickness=.1, time)
110
99
  {
111
100
  const halfDelta = vec2((posB.x - posA.x)/2, (posB.y - posA.y)/2);
112
101
  const size = vec2(thickness, halfDelta.length()*2);
@@ -120,7 +109,7 @@ const debugLine = (posA, posB, color, thickness=.1, time)=>
120
109
  * @param {Vector2} sizeB
121
110
  * @param {String} [color='#fff']
122
111
  * @memberof Debug */
123
- const debugAABB = (pA, sA, pB, sB, color)=>
112
+ function debugAABB(pA, sA, pB, sB, color)
124
113
  {
125
114
  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
115
  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 +125,7 @@ const debugAABB = (pA, sA, pB, sB, color)=>
136
125
  * @param {Number} [angle=0]
137
126
  * @param {String} [font='monospace']
138
127
  * @memberof Debug */
139
- const debugText = (text, pos, size=1, color='#fff', time=0, angle=0, font='monospace')=>
128
+ function debugText(text, pos, size=1, color='#fff', time=0, angle=0, font='monospace')
140
129
  {
141
130
  ASSERT(typeof color == 'string'); // pass in regular html strings as colors
142
131
  debugPrimitives.push({text, pos, size, color, time:new Timer(time), angle, font});
@@ -144,13 +133,13 @@ const debugText = (text, pos, size=1, color='#fff', time=0, angle=0, font='monos
144
133
 
145
134
  /** Clear all debug primitives in the list
146
135
  * @memberof Debug */
147
- const debugClear = ()=> debugPrimitives = [];
136
+ function debugClear() { debugPrimitives = []; }
148
137
 
149
138
  /** Save a canvas to disk
150
139
  * @param {HTMLCanvasElement} canvas
151
140
  * @param {String} [filename]
152
141
  * @memberof Debug */
153
- const debugSaveCanvas = (canvas, filename = engineName + '.png') =>
142
+ function debugSaveCanvas(canvas, filename = engineName + '.png')
154
143
  {
155
144
  downloadLink.download = 'screenshot.png';
156
145
  downloadLink.href = canvas.toDataURL('image/png').replace('image/png','image/octet-stream');
@@ -160,14 +149,14 @@ const debugSaveCanvas = (canvas, filename = engineName + '.png') =>
160
149
  ///////////////////////////////////////////////////////////////////////////////
161
150
  // Engine debug function (called automatically)
162
151
 
163
- const debugInit = ()=>
152
+ function debugInit()
164
153
  {
165
154
  // create link for saving screenshots
166
155
  document.body.appendChild(downloadLink = document.createElement('a'));
167
156
  downloadLink.style.display = 'none';
168
157
  }
169
158
 
170
- const debugUpdate = ()=>
159
+ function debugUpdate()
171
160
  {
172
161
  if (!debug)
173
162
  return;
@@ -185,7 +174,7 @@ const debugUpdate = ()=>
185
174
  if (keyWasPressed(51)) // 3
186
175
  debugGamepads = !debugGamepads;
187
176
  if (keyWasPressed(52)) // 4
188
- godMode = !godMode;
177
+ debugRaycast = !debugRaycast;
189
178
  if (keyWasPressed(53)) // 5
190
179
  debugTakeScreenshot = 1;
191
180
  //if (keyWasPressed(54)) // 6
@@ -195,7 +184,7 @@ const debugUpdate = ()=>
195
184
  }
196
185
  }
197
186
 
198
- const debugRender = ()=>
187
+ function debugRender()
199
188
  {
200
189
  glCopyToContext(mainContext);
201
190
 
@@ -364,8 +353,8 @@ const debugRender = ()=>
364
353
  overlayContext.fillText('2: Debug Particles', x, y += h);
365
354
  overlayContext.fillStyle = debugGamepads ? '#f00' : '#fff';
366
355
  overlayContext.fillText('3: Debug Gamepads', x, y += h);
367
- overlayContext.fillStyle = godMode ? '#f00' : '#fff';
368
- overlayContext.fillText('4: God Mode', x, y += h);
356
+ overlayContext.fillStyle = debugRaycast ? '#f00' : '#fff';
357
+ overlayContext.fillText('4: Debug Raycasts', x, y += h);
369
358
  overlayContext.fillStyle = '#fff';
370
359
  overlayContext.fillText('5: Save Screenshot', x, y += h);
371
360
 
@@ -390,19 +379,19 @@ const debugRender = ()=>
390
379
  {
391
380
  overlayContext.fillText(debugPhysics ? 'Debug Physics' : '', x, y += h);
392
381
  overlayContext.fillText(debugParticles ? 'Debug Particles' : '', x, y += h);
393
- overlayContext.fillText(godMode ? 'God Mode' : '', x, y += h);
382
+ overlayContext.fillText(debugRaycast ? 'Debug Raycasts' : '', x, y += h);
394
383
  overlayContext.fillText(debugGamepads ? 'Debug Gamepads' : '', x, y += h);
395
384
  }
396
385
 
397
386
  overlayContext.restore();
398
387
  }
399
- }
388
+ }
400
389
  /**
401
390
  * 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
391
+ * - General purpose math library
392
+ * - Vector2 - fast, simple, easy 2D vector class
393
+ * - Color - holds a rgba color with some math functions
394
+ * - Timer - tracks time automatically
406
395
  * @namespace Utilities
407
396
  */
408
397
 
@@ -418,34 +407,34 @@ const PI = Math.PI;
418
407
  * @param {Number} value
419
408
  * @return {Number}
420
409
  * @memberof Utilities */
421
- const abs = (a)=> a < 0 ? -a : a;
410
+ function abs(value) { return Math.abs(value); }
422
411
 
423
412
  /** Returns lowest of two values passed in
424
413
  * @param {Number} valueA
425
414
  * @param {Number} valueB
426
415
  * @return {Number}
427
416
  * @memberof Utilities */
428
- const min = (a, b)=> a < b ? a : b;
417
+ function min(valueA, valueB) { return Math.min(valueA, valueB); }
429
418
 
430
419
  /** Returns highest of two values passed in
431
420
  * @param {Number} valueA
432
421
  * @param {Number} valueB
433
422
  * @return {Number}
434
423
  * @memberof Utilities */
435
- const max = (a, b)=> a > b ? a : b;
424
+ function max(valueA, valueB) { return Math.max(valueA, valueB); }
436
425
 
437
426
  /** Returns the sign of value passed in (also returns 1 if 0)
438
427
  * @param {Number} value
439
428
  * @return {Number}
440
429
  * @memberof Utilities */
441
- const sign = (a)=> a < 0 ? -1 : 1;
430
+ function sign(value) { return Math.sign(value); }
442
431
 
443
432
  /** Returns first parm modulo the second param, but adjusted so negative numbers work as expected
444
433
  * @param {Number} dividend
445
434
  * @param {Number} [divisor=1]
446
435
  * @return {Number}
447
436
  * @memberof Utilities */
448
- const mod = (a, b=1)=> ((a % b) + b) % b;
437
+ function mod(dividend, divisor=1) { return ((dividend % divisor) + divisor) % divisor; }
449
438
 
450
439
  /** Clamps the value beween max and min
451
440
  * @param {Number} value
@@ -453,7 +442,8 @@ const mod = (a, b=1)=> ((a % b) + b) % b;
453
442
  * @param {Number} [max=1]
454
443
  * @return {Number}
455
444
  * @memberof Utilities */
456
- const clamp = (v, min=0, max=1)=> v < min ? min : v > max ? max : v;
445
+ function clamp(value, min=0, max=1)
446
+ { return value < min ? min : value > max ? max : value; }
457
447
 
458
448
  /** Returns what percentage the value is between max and min
459
449
  * @param {Number} value
@@ -461,7 +451,8 @@ const clamp = (v, min=0, max=1)=> v < min ? min : v > max ? max : v;
461
451
  * @param {Number} [max=1]
462
452
  * @return {Number}
463
453
  * @memberof Utilities */
464
- const percent = (v, min=0, max=1)=> max-min ? clamp((v-min) / (max-min)) : 0;
454
+ function percent(value, min=0, max=1)
455
+ { return max-min ? clamp((value-min) / (max-min)) : 0; }
465
456
 
466
457
  /** Linearly interpolates the percent value between max and min
467
458
  * @param {Number} percent
@@ -469,28 +460,32 @@ const percent = (v, min=0, max=1)=> max-min ? clamp((v-min) / (max-min)) : 0;
469
460
  * @param {Number} [max=1]
470
461
  * @return {Number}
471
462
  * @memberof Utilities */
472
- const lerp = (p, min=0, max=1)=> min + clamp(p) * (max-min);
463
+ function lerp(percent, min=0, max=1){ return min + clamp(percent) * (max-min); }
473
464
 
474
465
  /** Applies smoothstep function to the percentage value
475
- * @param {Number} value
466
+ * @param {Number} percent
476
467
  * @return {Number}
477
468
  * @memberof Utilities */
478
- const smoothStep = (p)=> p * p * (3 - 2 * p);
469
+ function smoothStep(percent) { return percent * percent * (3 - 2 * percent); }
479
470
 
480
471
  /** Returns the nearest power of two not less then the value
481
472
  * @param {Number} value
482
473
  * @return {Number}
483
474
  * @memberof Utilities */
484
- const nearestPowerOfTwo = (v)=> 2**Math.ceil(Math.log2(v));
475
+ function nearestPowerOfTwo(value) { return 2**Math.ceil(Math.log2(value)); }
485
476
 
486
477
  /** Returns true if two axis aligned bounding boxes are overlapping
487
478
  * @param {Vector2} pointA - Center of box A
488
479
  * @param {Vector2} sizeA - Size of box A
489
480
  * @param {Vector2} pointB - Center of box B
490
- * @param {Vector2} [sizeB] - Size of box B
481
+ * @param {Vector2} sizeB - Size of box B
491
482
  * @return {Boolean} - True if overlapping
492
483
  * @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;
484
+ function isOverlapping(pointA, sizeA, pointB, sizeB)
485
+ {
486
+ return abs(pointA.x - pointB.x)*2 < sizeA.x + sizeB.x
487
+ && abs(pointA.y - pointB.y)*2 < sizeA.y + sizeB.y;
488
+ }
494
489
 
495
490
  /** Returns an oscillating wave between 0 and amplitude with frequency of 1 Hz by default
496
491
  * @param {Number} [frequency=1] - Frequency of the wave in Hz
@@ -498,13 +493,14 @@ const isOverlapping = (pA, sA, pB, sB)=> abs(pA.x - pB.x)*2 < sA.x + sB.x && abs
498
493
  * @param {Number} [t=time] - Value to use for time of the wave
499
494
  * @return {Number} - Value waving between 0 and amplitude
500
495
  * @memberof Utilities */
501
- const wave = (frequency=1, amplitude=1, t=time)=> amplitude/2 * (1 - Math.cos(t*frequency*2*PI));
496
+ function wave(frequency=1, amplitude=1, t=time)
497
+ { return amplitude/2 * (1 - Math.cos(t*frequency*2*PI)); }
502
498
 
503
499
  /** Formats seconds to mm:ss style for display purposes
504
500
  * @param {Number} t - time in seconds
505
501
  * @return {String}
506
502
  * @memberof Utilities */
507
- const formatTime = (t)=> (t/60|0) + ':' + (t%60<10?'0':'') + (t%60|0);
503
+ function formatTime(t) { return (t/60|0) + ':' + (t%60<10?'0':'') + (t%60|0); }
508
504
 
509
505
  ///////////////////////////////////////////////////////////////////////////////
510
506
 
@@ -516,32 +512,33 @@ const formatTime = (t)=> (t/60|0) + ':' + (t%60<10?'0':'') + (t%60|0);
516
512
  * @param {Number} [valueB=0]
517
513
  * @return {Number}
518
514
  * @memberof Random */
519
- const rand = (a=1, b=0)=> b + (a-b)*Math.random();
515
+ function rand(valueA=1, valueB=0) { return valueB + Math.random() * (valueA-valueB); }
520
516
 
521
517
  /** Returns a floored random value the two values passed in
522
518
  * @param {Number} [valueA=1]
523
519
  * @param {Number} [valueB=0]
524
520
  * @return {Number}
525
521
  * @memberof Random */
526
- const randInt = (a=1, b=0)=> rand(a,b)|0;
522
+ function randInt(valueA=1, valueB=0) { return Math.floor(rand(valueA,valueB)); }
527
523
 
528
524
  /** Randomly returns either -1 or 1
529
525
  * @return {Number}
530
526
  * @memberof Random */
531
- const randSign = ()=> randInt(2) * 2 - 1;
527
+ function randSign() { return randInt(2) * 2 - 1; }
532
528
 
533
529
  /** Returns a random Vector2 within a circular shape
534
530
  * @param {Number} [radius=1]
535
531
  * @param {Number} [minRadius=0]
536
532
  * @return {Vector2}
537
533
  * @memberof Random */
538
- const randInCircle = (radius=1, minRadius=0)=> radius > 0 ? randVector(radius * rand(minRadius / radius, 1)**.5) : new Vector2;
534
+ function randInCircle(radius=1, minRadius=0)
535
+ { return radius > 0 ? randVector(radius * rand(minRadius / radius, 1)**.5) : new Vector2; }
539
536
 
540
537
  /** Returns a random Vector2 with the passed in length
541
538
  * @param {Number} [length=1]
542
539
  * @return {Vector2}
543
540
  * @memberof Random */
544
- const randVector = (length=1)=> new Vector2().setAngle(rand(2*PI), length);
541
+ function randVector(length=1) { return new Vector2().setAngle(rand(2*PI), length); }
545
542
 
546
543
  /** Returns a random color between the two passed in colors, combine components if linear
547
544
  * @param {Color} [colorA=Color()]
@@ -549,8 +546,11 @@ const randVector = (length=1)=> new Vector2().setAngle(rand(2*PI), length);
549
546
  * @param {Boolean} [linear]
550
547
  * @return {Color}
551
548
  * @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));
549
+ function randColor(colorA=new Color, colorB=new Color(0,0,0,1), linear)
550
+ {
551
+ return linear ? colorA.lerp(colorB, rand()) :
552
+ new Color(rand(colorA.r,colorB.r), rand(colorA.g,colorB.g), rand(colorA.b,colorB.b), rand(colorA.a,colorB.a));
553
+ }
554
554
 
555
555
  /** Seed used by the randSeeded function
556
556
  * @type {Number}
@@ -561,17 +561,20 @@ 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(valueA=1, valueB=0)
572
572
  {
573
- randSeed ^= randSeed << 13; randSeed ^= randSeed >>> 17; randSeed ^= randSeed << 5; // xorshift
574
- return b + (a-b) * abs(randSeed % 1e9) / 1e9;
573
+ // xorshift algorithm
574
+ randSeed ^= randSeed << 13;
575
+ randSeed ^= randSeed >>> 17;
576
+ randSeed ^= randSeed << 5;
577
+ return valueB + (valueA-valueB) * abs(randSeed % 1e9) / 1e9;
575
578
  }
576
579
 
577
580
  ///////////////////////////////////////////////////////////////////////////////
@@ -588,19 +591,20 @@ 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
595
- * @param {Vector2} vector
599
+ * @param {Vector2} v
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)
@@ -625,27 +629,27 @@ class Vector2
625
629
  copy() { return new Vector2(this.x, this.y); }
626
630
 
627
631
  /** Returns a copy of this vector plus the vector passed in
628
- * @param {Vector2} vector
632
+ * @param {Vector2} v - other vector
629
633
  * @return {Vector2} */
630
634
  add(v) { ASSERT(isVector2(v)); return new Vector2(this.x + v.x, this.y + v.y); }
631
635
 
632
636
  /** Returns a copy of this vector minus the vector passed in
633
- * @param {Vector2} vector
637
+ * @param {Vector2} v - other vector
634
638
  * @return {Vector2} */
635
639
  subtract(v) { ASSERT(isVector2(v)); return new Vector2(this.x - v.x, this.y - v.y); }
636
640
 
637
641
  /** Returns a copy of this vector times the vector passed in
638
- * @param {Vector2} vector
642
+ * @param {Vector2} v - other vector
639
643
  * @return {Vector2} */
640
644
  multiply(v) { ASSERT(isVector2(v)); return new Vector2(this.x * v.x, this.y * v.y); }
641
645
 
642
646
  /** Returns a copy of this vector divided by the vector passed in
643
- * @param {Vector2} vector
647
+ * @param {Vector2} v - other vector
644
648
  * @return {Vector2} */
645
649
  divide(v) { ASSERT(isVector2(v)); return new Vector2(this.x / v.x, this.y / v.y); }
646
650
 
647
651
  /** Returns a copy of this vector scaled by the vector passed in
648
- * @param {Number} scale
652
+ * @param {Number} s - scale
649
653
  * @return {Vector2} */
650
654
  scale(s) { ASSERT(!isVector2(s)); return new Vector2(this.x * s, this.y * s); }
651
655
 
@@ -658,12 +662,12 @@ class Vector2
658
662
  lengthSquared() { return this.x**2 + this.y**2; }
659
663
 
660
664
  /** Returns the distance from this vector to vector passed in
661
- * @param {Vector2} vector
665
+ * @param {Vector2} v - other vector
662
666
  * @return {Number} */
663
667
  distance(v) { return this.distanceSquared(v)**.5; }
664
668
 
665
669
  /** Returns the distance squared from this vector to vector passed in
666
- * @param {Vector2} vector
670
+ * @param {Vector2} v - other vector
667
671
  * @return {Number} */
668
672
  distanceSquared(v) { return (this.x - v.x)**2 + (this.y - v.y)**2; }
669
673
 
@@ -678,12 +682,12 @@ class Vector2
678
682
  clampLength(length=1) { const l = this.length(); return l > length ? this.scale(length/l) : this; }
679
683
 
680
684
  /** Returns the dot product of this and the vector passed in
681
- * @param {Vector2} vector
685
+ * @param {Vector2} v - other vector
682
686
  * @return {Number} */
683
687
  dot(v) { ASSERT(isVector2(v)); return this.x*v.x + this.y*v.y; }
684
688
 
685
689
  /** Returns the cross product of this and the vector passed in
686
- * @param {Vector2} vector
690
+ * @param {Vector2} v - other vector
687
691
  * @return {Number} */
688
692
  cross(v) { ASSERT(isVector2(v)); return this.x*v.y - this.y*v.x; }
689
693
 
@@ -695,12 +699,17 @@ class Vector2
695
699
  * @param {Number} [angle=0]
696
700
  * @param {Number} [length=1]
697
701
  * @return {Vector2} */
698
- setAngle(a=0, length=1) { this.x = length*Math.sin(a); this.y = length*Math.cos(a); return this; }
702
+ setAngle(angle=0, length=1)
703
+ { this.x = length*Math.sin(angle); this.y = length*Math.cos(angle); return this; }
699
704
 
700
705
  /** Returns copy of this vector rotated by the angle passed in
701
706
  * @param {Number} angle
702
707
  * @return {Vector2} */
703
- rotate(a) { const c = Math.cos(a), s = Math.sin(a); return new Vector2(this.x*c-this.y*s, this.x*s+this.y*c); }
708
+ rotate(angle)
709
+ {
710
+ const c = Math.cos(angle), s = Math.sin(angle);
711
+ return new Vector2(this.x*c - this.y*s, this.x*s + this.y*c);
712
+ }
704
713
 
705
714
  /** Returns the integer direction of this vector, corrosponding to multiples of 90 degree rotation (0-3)
706
715
  * @return {Number} */
@@ -719,10 +728,11 @@ class Vector2
719
728
  area() { return abs(this.x * this.y); }
720
729
 
721
730
  /** Returns a new vector that is p percent between this and the vector passed in
722
- * @param {Vector2} vector
731
+ * @param {Vector2} v - other vector
723
732
  * @param {Number} percent
724
733
  * @return {Vector2} */
725
- lerp(v, p) { ASSERT(isVector2(v)); return this.add(v.subtract(this).scale(clamp(p))); }
734
+ lerp(v, percent)
735
+ { ASSERT(isVector2(v)); return this.add(v.subtract(this).scale(clamp(percent))); }
726
736
 
727
737
  /** Returns true if this vector is within the bounds of an array size passed in
728
738
  * @param {Vector2} arraySize
@@ -740,25 +750,25 @@ class Vector2
740
750
 
741
751
  /**
742
752
  * Create a color object with RGBA values
743
- * @param {Number} [r=1]
744
- * @param {Number} [g=1]
745
- * @param {Number} [b=1]
746
- * @param {Number} [a=1]
753
+ * @param {Number} [r=1] - red
754
+ * @param {Number} [g=1] - green
755
+ * @param {Number} [b=1] - blue
756
+ * @param {Number} [a=1] - alpha
747
757
  * @return {Color}
748
758
  * @memberof Utilities
749
759
  */
750
- const rgb = (r, g, b, a)=> new Color(r, g, b, a);
760
+ function rgb(r, g, b, a) { return new Color(r, g, b, a); }
751
761
 
752
762
  /**
753
763
  * Create a color object with HSLA values
754
- * @param {Number} [h=0]
755
- * @param {Number} [s=0]
756
- * @param {Number} [l=1]
757
- * @param {Number} [a=1]
764
+ * @param {Number} [h=0] - hue
765
+ * @param {Number} [s=0] - saturation
766
+ * @param {Number} [l=1] - lightness
767
+ * @param {Number} [a=1] - alpha
758
768
  * @return {Color}
759
769
  * @memberof Utilities
760
770
  */
761
- const hsl = (h, s, l, a)=> new Color().setHSLA(h, s, l, a);
771
+ function hsl(h, s, l, a) { return new Color().setHSLA(h, s, l, a); }
762
772
 
763
773
  /**
764
774
  * Color object (red, green, blue, alpha) with some helpful functions
@@ -771,11 +781,11 @@ const hsl = (h, s, l, a)=> new Color().setHSLA(h, s, l, a);
771
781
  */
772
782
  class Color
773
783
  {
774
- /** Create a color with the components passed in, white by default
775
- * @param {Number} [red=1]
776
- * @param {Number} [green=1]
777
- * @param {Number} [blue=1]
778
- * @param {Number} [alpha=1] */
784
+ /** Create a color with the rgba components passed in, white by default
785
+ * @param {Number} [r=1] - red
786
+ * @param {Number} [g=1] - green
787
+ * @param {Number} [b=1] - blue
788
+ * @param {Number} [a=1] - alpha*/
779
789
  constructor(r=1, g=1, b=1, a=1)
780
790
  {
781
791
  /** @property {Number} - Red */
@@ -793,22 +803,22 @@ class Color
793
803
  copy() { return new Color(this.r, this.g, this.b, this.a); }
794
804
 
795
805
  /** Returns a copy of this color plus the color passed in
796
- * @param {Color} color
806
+ * @param {Color} c - other color
797
807
  * @return {Color} */
798
808
  add(c) { return new Color(this.r+c.r, this.g+c.g, this.b+c.b, this.a+c.a); }
799
809
 
800
810
  /** Returns a copy of this color minus the color passed in
801
- * @param {Color} color
811
+ * @param {Color} c - other color
802
812
  * @return {Color} */
803
813
  subtract(c) { return new Color(this.r-c.r, this.g-c.g, this.b-c.b, this.a-c.a); }
804
814
 
805
815
  /** Returns a copy of this color times the color passed in
806
- * @param {Color} color
816
+ * @param {Color} c - other color
807
817
  * @return {Color} */
808
818
  multiply(c) { return new Color(this.r*c.r, this.g*c.g, this.b*c.b, this.a*c.a); }
809
819
 
810
820
  /** Returns a copy of this color divided by the color passed in
811
- * @param {Color} color
821
+ * @param {Color} c - other color
812
822
  * @return {Color} */
813
823
  divide(c) { return new Color(this.r/c.r, this.g/c.g, this.b/c.b, this.a/c.a); }
814
824
 
@@ -816,23 +826,24 @@ class Color
816
826
  * @param {Number} scale
817
827
  * @param {Number} [alphaScale=scale]
818
828
  * @return {Color} */
819
- scale(s, a=s) { return new Color(this.r*s, this.g*s, this.b*s, this.a*a); }
829
+ scale(scale, alphaScale=scale)
830
+ { return new Color(this.r*scale, this.g*scale, this.b*scale, this.a*alphaScale); }
820
831
 
821
832
  /** Returns a copy of this color clamped to the valid range between 0 and 1
822
833
  * @return {Color} */
823
834
  clamp() { return new Color(clamp(this.r), clamp(this.g), clamp(this.b), clamp(this.a)); }
824
835
 
825
836
  /** Returns a new color that is p percent between this and the color passed in
826
- * @param {Color} color
837
+ * @param {Color} c - other color
827
838
  * @param {Number} percent
828
839
  * @return {Color} */
829
- lerp(c, p) { return this.add(c.subtract(this).scale(clamp(p))); }
840
+ lerp(c, percent) { return this.add(c.subtract(this).scale(clamp(percent))); }
830
841
 
831
842
  /** Sets this color given a hue, saturation, lightness, and alpha
832
- * @param {Number} [hue=0]
833
- * @param {Number} [saturation=0]
834
- * @param {Number} [lightness=1]
835
- * @param {Number} [alpha=1]
843
+ * @param {Number} [h=0] - hue
844
+ * @param {Number} [s=0] - saturation
845
+ * @param {Number} [l=1] - lightness
846
+ * @param {Number} [a=1] - alpha
836
847
  * @return {Color} */
837
848
  setHSLA(h=0, s=0, l=1, a=1)
838
849
  {
@@ -973,12 +984,12 @@ class Timer
973
984
 
974
985
  /** Returns this timer expressed as a string
975
986
  * @return {String} */
976
- toString() { if (debug) { return this.unset() ? 'unset' : Math.abs(this.get()) + ' seconds ' + (this.get()<0 ? 'before' : 'after' ); }}
987
+ toString() { if (debug) { return this.isSet() ? Math.abs(this.get()) + ' seconds ' + (this.get()<0 ? 'before' : 'after' ) : 'unset'; }}
977
988
 
978
989
  /** Get how long since elapsed, returns 0 if not set (returns negative if currently active)
979
990
  * @return {Number} */
980
991
  valueOf() { return this.get(); }
981
- }
992
+ }
982
993
  /**
983
994
  * LittleJS Engine Settings
984
995
  * @namespace Settings
@@ -1017,11 +1028,11 @@ let canvasMaxSize = vec2(1920, 1200);
1017
1028
  * @memberof Settings */
1018
1029
  let canvasFixedSize = vec2();
1019
1030
 
1020
- /** Disables anti aliasing for pixel art if true
1031
+ /** Disables filtering for crisper pixel art if true
1021
1032
  * @type {Boolean}
1022
1033
  * @default
1023
1034
  * @memberof Settings */
1024
- let cavasPixelated = 1;
1035
+ let canvasPixelated = 1;
1025
1036
 
1026
1037
  /** Default font used for text rendering
1027
1038
  * @type {String}
@@ -1138,8 +1149,8 @@ let gamepadDirectionEmulateStick = 1;
1138
1149
  let inputWASDEmulateDirection = 1;
1139
1150
 
1140
1151
  /** 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
1152
+ * - Supports left analog stick, 4 face buttons and start button (button 9)
1153
+ * - Must be set by end of gameInit to be activated
1143
1154
  * @type {Boolean}
1144
1155
  * @default 0
1145
1156
  * @memberof Settings */
@@ -1227,33 +1238,33 @@ let medalDisplayIconSize = 50;
1227
1238
  * @type {Boolean}
1228
1239
  * @default 0
1229
1240
  * @memberof Settings */
1230
- let medalsPreventUnlock;
1231
- /*
1232
- LittleJS Object System
1233
- */
1241
+ let medalsPreventUnlock;
1242
+ /**
1243
+ * LittleJS Object System
1244
+ */
1234
1245
 
1235
1246
  'use strict';
1236
1247
 
1237
1248
  /**
1238
1249
  * 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
1250
+ * - Base object class used by the engine
1251
+ * - Automatically adds self to object list
1252
+ * - Will be updated and rendered each frame
1253
+ * - Renders as a sprite from a tilesheet by default
1254
+ * - Can have color and addtive color applied
1255
+ * - 2d Physics and collision system
1256
+ * - Sorted by renderOrder
1257
+ * - Objects can have children attached
1258
+ * - Parents are updated before children, and set child transform
1259
+ * - Call destroy() to get rid of objects
1260
+ *
1261
+ * The physics system used by objects is simple and fast with some caveats...
1262
+ * - Collision uses the axis aligned size, the object's rotation angle is only for rendering
1263
+ * - Objects are guaranteed to not intersect tile collision from physics
1264
+ * - If an object starts or is moved inside tile collision, it will not collide with that tile
1265
+ * - Collision for objects can be set to be solid to block other objects
1266
+ * - Objects may get pushed into overlapping other solid objects, if so they will push away
1267
+ * - Solid objects are more performance intensive and should be used sparingly
1257
1268
  * @example
1258
1269
  * // create an engine object, normally you would first extend the class with your own
1259
1270
  * const pos = vec2(2,3);
@@ -1308,7 +1319,7 @@ class EngineObject
1308
1319
  /** @property {Number} [renderOrder=0] - Objects are sorted by render order */
1309
1320
  this.renderOrder = renderOrder;
1310
1321
  /** @property {Vector2} [velocity=Vector2()] - Velocity of the object */
1311
- this.velocity = new Vector2();
1322
+ this.velocity = vec2();
1312
1323
  /** @property {Number} [angleVelocity=0] - Angular velocity of the object */
1313
1324
  this.angleVelocity = 0;
1314
1325
 
@@ -1368,15 +1379,17 @@ class EngineObject
1368
1379
  for (const o of engineObjectsCollide)
1369
1380
  {
1370
1381
  // non solid objects don't collide with eachother
1371
- if (!this.isSolid & !o.isSolid || o.destroyed || o.parent || o == this)
1382
+ if (!this.isSolid && !o.isSolid || o.destroyed || o.parent || o == this)
1372
1383
  continue;
1373
1384
 
1374
1385
  // check collision
1375
1386
  if (!isOverlapping(this.pos, this.size, o.pos, o.size))
1376
1387
  continue;
1377
1388
 
1378
- // pass collision to objects
1379
- if (!this.collideWithObject(o) | !o.collideWithObject(this))
1389
+ // notify objects of collision and check if should be resolved
1390
+ const collide1 = this.collideWithObject(o);
1391
+ const collide2 = o.collideWithObject(this);
1392
+ if (!collide1 || !collide2)
1380
1393
  continue;
1381
1394
 
1382
1395
  if (isOverlapping(oldPos, this.size, o.pos, o.size))
@@ -1401,7 +1414,7 @@ class EngineObject
1401
1414
  const isBlockedY = abs(oldPos.x - o.pos.x)*2 < sizeBoth.x;
1402
1415
  const elasticity = max(this.elasticity, o.elasticity);
1403
1416
 
1404
- if (smallStepUp | isBlockedY | !isBlockedX) // resolve y collision
1417
+ if (smallStepUp || isBlockedY || !isBlockedX) // resolve y collision
1405
1418
  {
1406
1419
  // push outside object collision
1407
1420
  this.pos.y = o.pos.y + (sizeBoth.y/2 + epsilon) * sign(oldPos.y - o.pos.y);
@@ -1430,7 +1443,7 @@ class EngineObject
1430
1443
  o.velocity.y = lerp(elasticity, inelastic, elastic1);
1431
1444
  }
1432
1445
  }
1433
- if (!smallStepUp & isBlockedX) // resolve x collision
1446
+ if (!smallStepUp && isBlockedX) // resolve x collision
1434
1447
  {
1435
1448
  // push outside collision
1436
1449
  this.pos.x = o.pos.x + (sizeBoth.x/2 + epsilon) * sign(oldPos.x - o.pos.x);
@@ -1465,9 +1478,9 @@ class EngineObject
1465
1478
  if (!tileCollisionTest(oldPos, this.size, this))
1466
1479
  {
1467
1480
  // test which side we bounced off (or both if a corner)
1468
- const isBlockedY = tileCollisionTest(new Vector2(oldPos.x, this.pos.y), this.size, this);
1469
- const isBlockedX = tileCollisionTest(new Vector2(this.pos.x, oldPos.y), this.size, this);
1470
- if (isBlockedY | !isBlockedX)
1481
+ const isBlockedY = tileCollisionTest(vec2(oldPos.x, this.pos.y), this.size, this);
1482
+ const isBlockedX = tileCollisionTest(vec2(this.pos.x, oldPos.y), this.size, this);
1483
+ if (isBlockedY || !isBlockedX)
1471
1484
  {
1472
1485
  // set if landed on ground
1473
1486
  this.groundObject = wasMovingDown;
@@ -1530,7 +1543,7 @@ class EngineObject
1530
1543
  * @param {EngineObject} object - the object to test against
1531
1544
  * @return {Boolean} - true if the collision should be resolved
1532
1545
  */
1533
- collideWithObject(o) { return 1; }
1546
+ collideWithObject(object) { return 1; }
1534
1547
 
1535
1548
  /** How long since the object was created
1536
1549
  * @return {Number} */
@@ -1538,7 +1551,7 @@ class EngineObject
1538
1551
 
1539
1552
  /** Apply acceleration to this object (adjust velocity, not affected by mass)
1540
1553
  * @param {Vector2} acceleration */
1541
- applyAcceleration(a) { if (this.mass) this.velocity = this.velocity.add(a); }
1554
+ applyAcceleration(acceleration) { if (this.mass) this.velocity = this.velocity.add(acceleration); }
1542
1555
 
1543
1556
  /** Apply force to this object (adjust velocity, affected by mass)
1544
1557
  * @param {Vector2} force */
@@ -1603,26 +1616,26 @@ class EngineObject
1603
1616
  return text;
1604
1617
  }
1605
1618
  }
1606
- }
1619
+ }
1607
1620
  /**
1608
1621
  * 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!
1622
+ * - Hybrid with both Canvas2D and WebGL available
1623
+ * - Super fast tile sheet rendering with WebGL
1624
+ * - Can apply rotation, mirror, color and additive color
1625
+ * - Many useful utility functions
1626
+ *
1627
+ * LittleJS uses a hybrid rendering solution with the best of both Canvas2D and WebGL.
1628
+ * There are 3 canvas/contexts available to draw to...
1629
+ * mainCanvas - 2D background canvas, non WebGL stuff like tile layers are drawn here.
1630
+ * glCanvas - Used by the accelerated WebGL batch rendering system.
1631
+ * overlayCanvas - Another 2D canvas that appears on top of the other 2 canvases.
1632
+ *
1633
+ * The WebGL rendering system is very fast with some caveats...
1634
+ * - The default setup supports only 1 tile sheet, to support more call glCreateTexture and glSetTexture
1635
+ * - Switching blend modes (additive) or textures causes another draw call which is expensive in excess
1636
+ * - Group additive rendering together using renderOrder to mitigate this issue
1637
+ *
1638
+ * The LittleJS rendering solution is intentionally simple, feel free to adjust it for your needs!
1626
1639
  * @namespace Draw
1627
1640
  */
1628
1641
 
@@ -1666,10 +1679,14 @@ let tileImageSize, tileImageFixBleed, drawCount;
1666
1679
  * @param {Vector2} screenPos
1667
1680
  * @return {Vector2}
1668
1681
  * @memberof Draw */
1669
- const screenToWorld = (screenPos)=>
1682
+ function screenToWorld(screenPos)
1670
1683
  {
1671
1684
  ASSERT(mainCanvasSize.x && mainCanvasSize.y, 'mainCanvasSize is invalid');
1672
- return screenPos.add(vec2(.5)).subtract(mainCanvasSize.scale(.5)).multiply(vec2(1/cameraScale,-1/cameraScale)).add(cameraPos);
1685
+ return screenPos
1686
+ .add(vec2(.5))
1687
+ .subtract(mainCanvasSize.scale(.5))
1688
+ .multiply(vec2(1/cameraScale,-1/cameraScale))
1689
+ .add(cameraPos);
1673
1690
  }
1674
1691
 
1675
1692
  /** Convert from world to screen space coordinates
@@ -1677,10 +1694,14 @@ const screenToWorld = (screenPos)=>
1677
1694
  * @param {Vector2} worldPos
1678
1695
  * @return {Vector2}
1679
1696
  * @memberof Draw */
1680
- const worldToScreen = (worldPos)=>
1697
+ function worldToScreen(worldPos)
1681
1698
  {
1682
1699
  ASSERT(mainCanvasSize.x && mainCanvasSize.y, 'mainCanvasSize is invalid');
1683
- return worldPos.subtract(cameraPos).multiply(vec2(cameraScale,-cameraScale)).add(mainCanvasSize.scale(.5)).subtract(vec2(.5));
1700
+ return worldPos
1701
+ .subtract(cameraPos)
1702
+ .multiply(vec2(cameraScale,-cameraScale))
1703
+ .add(mainCanvasSize.scale(.5))
1704
+ .subtract(vec2(.5));
1684
1705
  }
1685
1706
 
1686
1707
  /** Draw textured tile centered in world space, with color applied if using WebGL
@@ -1694,8 +1715,8 @@ const worldToScreen = (worldPos)=>
1694
1715
  * @param {Color} [additiveColor=Color(0,0,0,0)] - Additive color to be applied
1695
1716
  * @param {Boolean} [useWebGL=glEnable] - Use accelerated WebGL rendering
1696
1717
  * @memberof Draw */
1697
- function drawTile(pos, size=vec2(1), tileIndex=-1, tileSize=tileSizeDefault, color=new Color, angle=0, mirror,
1698
- additiveColor=new Color(0,0,0,0), useWebGL=glEnable)
1718
+ function drawTile(pos, size=vec2(1), tileIndex=-1, tileSize=tileSizeDefault, color=new Color,
1719
+ angle=0, mirror, additiveColor=new Color(0,0,0,0), useWebGL=glEnable)
1699
1720
  {
1700
1721
  showWatermark && ++drawCount;
1701
1722
  if (glEnable && useWebGL)
@@ -1886,9 +1907,9 @@ let engineFontImage;
1886
1907
 
1887
1908
  /**
1888
1909
  * 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
1910
+ * - 96 characters (from space to tilde) are stored in an image
1911
+ * - Uses a default 8x8 font if none is supplied
1912
+ * - You can also use fonts from the main tile sheet
1892
1913
  * @example
1893
1914
  * // use built in font
1894
1915
  * const font = new ImageFont;
@@ -1928,7 +1949,7 @@ class FontImage
1928
1949
  {
1929
1950
  const context = this.context;
1930
1951
  context.save();
1931
- context.imageSmoothingEnabled = !cavasPixelated;
1952
+ context.imageSmoothingEnabled = !canvasPixelated;
1932
1953
 
1933
1954
  const size = this.tileSize;
1934
1955
  const drawSize = size.add(this.paddingSize).scale(scale);
@@ -1974,7 +1995,7 @@ class FontImage
1974
1995
  /** Returns true if fullscreen mode is active
1975
1996
  * @return {Boolean}
1976
1997
  * @memberof Draw */
1977
- const isFullscreen = ()=> document.fullscreenElement;
1998
+ function isFullscreen() { return document.fullscreenElement; }
1978
1999
 
1979
2000
  /** Toggle fullsceen mode
1980
2001
  * @memberof Draw */
@@ -1988,13 +2009,13 @@ function toggleFullscreen()
1988
2009
  else if (document.body.requestFullscreen)
1989
2010
  document.body.requestFullscreen();
1990
2011
  }
1991
-
2012
+
1992
2013
  /**
1993
2014
  * 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
2015
+ * - Tracks key down, pressed, and released
2016
+ * - Also tracks mouse buttons, position, and wheel
2017
+ * - Supports multiple gamepads
2018
+ * - Virtual gamepad for touch devices with touchGamepadSize
1998
2019
  * @namespace Input
1999
2020
  */
2000
2021
 
@@ -2005,25 +2026,28 @@ function toggleFullscreen()
2005
2026
  * @param {Number} [device=0]
2006
2027
  * @return {Boolean}
2007
2028
  * @memberof Input */
2008
- const keyIsDown = (key, device=0)=> inputData[device] && inputData[device][key] & 1;
2029
+ function keyIsDown(key, device=0)
2030
+ { return inputData[device] && inputData[device][key] & 1; }
2009
2031
 
2010
2032
  /** Returns true if device key was pressed this frame
2011
2033
  * @param {Number} key
2012
2034
  * @param {Number} [device=0]
2013
2035
  * @return {Boolean}
2014
2036
  * @memberof Input */
2015
- const keyWasPressed = (key, device=0)=> inputData[device] && inputData[device][key] & 2 ? 1 : 0;
2037
+ function keyWasPressed(key, device=0)
2038
+ { return inputData[device] && inputData[device][key] & 2 ? 1 : 0; }
2016
2039
 
2017
2040
  /** Returns true if device key was released this frame
2018
2041
  * @param {Number} key
2019
2042
  * @param {Number} [device=0]
2020
2043
  * @return {Boolean}
2021
2044
  * @memberof Input */
2022
- const keyWasReleased = (key, device=0)=> inputData[device] && inputData[device][key] & 4 ? 1 : 0;
2045
+ function keyWasReleased(key, device=0)
2046
+ { return inputData[device] && inputData[device][key] & 4 ? 1 : 0; }
2023
2047
 
2024
2048
  /** Clears all input
2025
2049
  * @memberof Input */
2026
- const clearInput = ()=> inputData = [[]];
2050
+ function clearInput() { inputData = [[]]; }
2027
2051
 
2028
2052
  /** Returns true if mouse button is down
2029
2053
  * @function
@@ -2076,28 +2100,32 @@ let preventDefaultInput = 0;
2076
2100
  * @param {Number} [gamepad=0]
2077
2101
  * @return {Boolean}
2078
2102
  * @memberof Input */
2079
- const gamepadIsDown = (button, gamepad=0)=> keyIsDown(button, gamepad+1);
2103
+ function gamepadIsDown(button, gamepad=0)
2104
+ { return keyIsDown(button, gamepad+1); }
2080
2105
 
2081
2106
  /** Returns true if gamepad button was pressed
2082
2107
  * @param {Number} button
2083
2108
  * @param {Number} [gamepad=0]
2084
2109
  * @return {Boolean}
2085
2110
  * @memberof Input */
2086
- const gamepadWasPressed = (button, gamepad=0)=> keyWasPressed(button, gamepad+1);
2111
+ function gamepadWasPressed(button, gamepad=0)
2112
+ { return keyWasPressed(button, gamepad+1); }
2087
2113
 
2088
2114
  /** Returns true if gamepad button was released
2089
2115
  * @param {Number} button
2090
2116
  * @param {Number} [gamepad=0]
2091
2117
  * @return {Boolean}
2092
2118
  * @memberof Input */
2093
- const gamepadWasReleased = (button, gamepad=0)=> keyWasReleased(button, gamepad+1);
2119
+ function gamepadWasReleased(button, gamepad=0)
2120
+ { return keyWasReleased(button, gamepad+1); }
2094
2121
 
2095
2122
  /** Returns gamepad stick value
2096
2123
  * @param {Number} stick
2097
2124
  * @param {Number} [gamepad=0]
2098
2125
  * @return {Vector2}
2099
2126
  * @memberof Input */
2100
- const gamepadStick = (stick, gamepad=0)=> stickData[gamepad] ? stickData[gamepad][stick] || vec2() : vec2();
2127
+ function gamepadStick(stick, gamepad=0)
2128
+ { return stickData[gamepad] ? stickData[gamepad][stick] || vec2() : vec2(); }
2101
2129
 
2102
2130
  ///////////////////////////////////////////////////////////////////////////////
2103
2131
  // Input update called by engine
@@ -2136,12 +2164,18 @@ onkeydown = (e)=>
2136
2164
  e.repeat || (inputData[isUsingGamepad = 0][remapKey(e.which)] = 3);
2137
2165
  preventDefaultInput && e.preventDefault();
2138
2166
  }
2167
+
2139
2168
  onkeyup = (e)=>
2140
2169
  {
2141
2170
  if (debug && e.target != document.body) return;
2142
2171
  inputData[0][remapKey(e.which)] = 4;
2143
2172
  }
2144
- const remapKey = (c)=> inputWASDEmulateDirection ? c==87?38 : c==83?40 : c==65?37 : c==68?39 : c : c;
2173
+
2174
+ function remapKey(c)
2175
+ {
2176
+ return inputWASDEmulateDirection ?
2177
+ c==87?38 : c==83?40 : c==65?37 : c==68?39 : c : c;
2178
+ }
2145
2179
 
2146
2180
  ///////////////////////////////////////////////////////////////////////////////
2147
2181
  // Mouse event handlers
@@ -2150,10 +2184,10 @@ onmousedown = (e)=> {inputData[isUsingGamepad = 0][e.button] = 3; onmousemove(e)
2150
2184
  onmouseup = (e)=> inputData[0][e.button] = inputData[0][e.button] & 2 | 4;
2151
2185
  onmousemove = (e)=> mousePosScreen = mouseToScreen(e);
2152
2186
  onwheel = (e)=> e.ctrlKey || (mouseWheel = sign(e.deltaY));
2153
- oncontextmenu = (e)=> !1; // prevent right click menu
2187
+ oncontextmenu = (e)=> false; // prevent right click menu
2154
2188
 
2155
2189
  // convert a mouse or touch event position to screen space
2156
- const mouseToScreen = (mousePos)=>
2190
+ function mouseToScreen(mousePos)
2157
2191
  {
2158
2192
  if (!mainCanvas)
2159
2193
  return vec2(); // fix bug that can occur if user clicks before page loads
@@ -2199,8 +2233,7 @@ function gamepadsUpdate()
2199
2233
  if (gamepad)
2200
2234
  {
2201
2235
  // read clamp dead zone of analog sticks
2202
- const deadZone = .3, deadZoneMax = .8;
2203
- const applyDeadZone = (v)=>
2236
+ const deadZone = .3, deadZoneMax = .8, applyDeadZone = (v)=>
2204
2237
  v > deadZone ? percent( v, deadZone, deadZoneMax) :
2205
2238
  v < -deadZone ? -percent(-v, deadZone, deadZoneMax) : 0;
2206
2239
 
@@ -2233,11 +2266,12 @@ function gamepadsUpdate()
2233
2266
  /** Pulse the vibration hardware if it exists
2234
2267
  * @param {Number} [pattern=100] - a single value in miliseconds or vibration interval array
2235
2268
  * @memberof Input */
2236
- const vibrate = (pattern)=> vibrateEnable && navigator && navigator.vibrate && navigator.vibrate(pattern);
2269
+ function vibrate(pattern)
2270
+ { vibrateEnable && navigator && navigator.vibrate && navigator.vibrate(pattern); }
2237
2271
 
2238
2272
  /** Cancel any ongoing vibration
2239
2273
  * @memberof Input */
2240
- const vibrateStop = ()=> vibrate(0);
2274
+ function vibrateStop() { vibrate(0); }
2241
2275
 
2242
2276
  ///////////////////////////////////////////////////////////////////////////////
2243
2277
  // Touch input
@@ -2409,7 +2443,7 @@ function touchGamepadRender()
2409
2443
  const rightCenter = vec2(mainCanvasSize.x-touchGamepadSize, mainCanvasSize.y-touchGamepadSize);
2410
2444
  for (let i=4; i--;)
2411
2445
  {
2412
- const pos = rightCenter.add((new Vector2).setAngle(i*PI/2, touchGamepadSize/2));
2446
+ const pos = rightCenter.add(vec2().setAngle(i*PI/2, touchGamepadSize/2));
2413
2447
  overlayContext.fillStyle = touchGamepadButtons[i] ? '#fff' : '#000';
2414
2448
  overlayContext.beginPath();
2415
2449
  overlayContext.arc(pos.x, pos.y, touchGamepadSize/4, 0,9);
@@ -2419,23 +2453,24 @@ function touchGamepadRender()
2419
2453
 
2420
2454
  // set canvas back to normal
2421
2455
  overlayContext.restore();
2422
- }
2456
+ }
2423
2457
  /**
2424
2458
  * 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
2459
+ * - <a href=https://killedbyapixel.github.io/ZzFX/>ZzFX Sound Effects</a> - Sound Effect Generator
2460
+ * - <a href=https://keithclark.github.io/ZzFXM/>ZzFXM Music</a> - Music System
2461
+ * - Caches sounds and music for fast playback
2462
+ * - Can attenuate and apply stereo panning to sounds
2463
+ * - Ability to play mp3, ogg, and wave files
2464
+ * - Speech synthesis wrapper functions
2465
+ * @namespace Audio
2431
2466
  */
2432
2467
 
2433
2468
  'use strict';
2434
2469
 
2435
2470
  /**
2436
2471
  * 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>
2472
+ *
2473
+ * <a href=https://killedbyapixel.github.io/ZzFX/>Create sounds using the ZzFX Sound Designer.</a>
2439
2474
  * @example
2440
2475
  * // create a sound
2441
2476
  * const sound_example = new Sound([.5,.5]);
@@ -2519,8 +2554,8 @@ class Sound
2519
2554
 
2520
2555
  /**
2521
2556
  * 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>
2557
+ *
2558
+ * <a href=https://keithclark.github.io/ZzFXM/>Create music with the ZzFXM tracker.</a>
2524
2559
  * @example
2525
2560
  * // create some music
2526
2561
  * const music_example = new Music(
@@ -2630,14 +2665,15 @@ function speak(text, language='', volume=1, rate=1, pitch=1)
2630
2665
 
2631
2666
  /** Stop all queued speech
2632
2667
  * @memberof Audio */
2633
- const speakStop = ()=> speechSynthesis && speechSynthesis.cancel();
2668
+ function speakStop() {speechSynthesis && speechSynthesis.cancel();}
2634
2669
 
2635
2670
  /** Get frequency of a note on a musical scale
2636
2671
  * @param {Number} semitoneOffset - How many semitones away from the root note
2637
2672
  * @param {Number} [rootNoteFrequency=220] - Frequency at semitone offset 0
2638
2673
  * @return {Number} - The frequency of the note
2639
2674
  * @memberof Audio */
2640
- const getNoteFrequency = (semitoneOffset, rootFrequency=220)=> rootFrequency * 2**(semitoneOffset/12);
2675
+ function getNoteFrequency(semitoneOffset, rootFrequency=220)
2676
+ { return rootFrequency * 2**(semitoneOffset/12); }
2641
2677
 
2642
2678
  ///////////////////////////////////////////////////////////////////////////////
2643
2679
 
@@ -2695,12 +2731,12 @@ function playSamples(sampleChannels, volume=1, rate=1, pan=0, loop=0)
2695
2731
  // ZzFXMicro - Zuper Zmall Zound Zynth - v1.2.0 by Frank Force
2696
2732
 
2697
2733
  /** 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>
2734
+ *
2735
+ * <a href=https://killedbyapixel.github.io/ZzFX/>Create sounds using the ZzFX Sound Designer.</a>
2700
2736
  * @param {Array} zzfxSound - Array of ZzFX parameters, ex. [.5,.5]
2701
- * @return {Array} - Array of audio samples
2737
+ * @return {AudioBufferSourceNode} - The audio node of the sound played
2702
2738
  * @memberof Audio */
2703
- const zzfx = (...zzfxSound) => playSamples([zzfxG(...zzfxSound)]);
2739
+ function zzfx(...zzfxSound) { return playSamples([zzfxG(...zzfxSound)]); }
2704
2740
 
2705
2741
  /** Sample rate used for all ZzFX sounds
2706
2742
  * @default 44100
@@ -2708,7 +2744,29 @@ const zzfx = (...zzfxSound) => playSamples([zzfxG(...zzfxSound)]);
2708
2744
  const zzfxR = 44100;
2709
2745
 
2710
2746
  /** Generate samples for a ZzFX sound
2711
- * @memberof Audio */
2747
+ * @param {Number} [volume=1] - Volume scale (percent)
2748
+ * @param {Number} [randomness=.05] - How much to randomize frequency (percent Hz)
2749
+ * @param {Number} [frequency=220] - Frequency of sound (Hz)
2750
+ * @param {Number} [attack=0] - Attack time, how fast sound starts (seconds)
2751
+ * @param {Number} [sustain=0] - Sustain time, how long sound holds (seconds)
2752
+ * @param {Number} [release=.1] - Release time, how fast sound fades out (seconds)
2753
+ * @param {Number} [shape=0] - Shape of the sound wave
2754
+ * @param {Number} [shapeCurve=1] - Squarenes of wave (0=square, 1=normal, 2=pointy)
2755
+ * @param {Number} [slide=0] - How much to slide frequency (kHz/s)
2756
+ * @param {Number} [deltaSlide=0] - How much to change slide (kHz/s/s)
2757
+ * @param {Number} [pitchJump=0] - Frequency of pitch jump (Hz)
2758
+ * @param {Number} [pitchJumpTime=0] - Time of pitch jump (seconds)
2759
+ * @param {Number} [repeatTime=0] - Resets some parameters periodically (seconds)
2760
+ * @param {Number} [noise=0] - How much random noise to add (percent)
2761
+ * @param {Number} [modulation=0] - Frequency of modulation wave, negative flips phase (Hz)
2762
+ * @param {Number} [bitCrush=0] - Resamples at a lower frequency in (samples*100)
2763
+ * @param {Number} [delay=0] - Overlap sound with itself for reverb and flanger effects (seconds)
2764
+ * @param {Number} [sustainVolume=1] - Volume level for sustain (percent)
2765
+ * @param {Number} [decay=0] - Decay time, how long to reach sustain after attack (seconds)
2766
+ * @param {Number} [tremolo=0] - Trembling effect, rate controlled by repeat time (precent)
2767
+ * @return {Array} - Array of audio samples
2768
+ * @memberof Audio
2769
+ */
2712
2770
  function zzfxG
2713
2771
  (
2714
2772
  // parameters
@@ -2798,7 +2856,7 @@ function zzfxG
2798
2856
  * @param {Array} patterns - Array of pattern data
2799
2857
  * @param {Array} sequence - Array of pattern indexes
2800
2858
  * @param {Number} [BPM=125] - Playback speed of the song in BPM
2801
- * @returns {Array} - Left and right channel sample data
2859
+ * @return {Array} - Left and right channel sample data
2802
2860
  * @memberof Audio */
2803
2861
  function zzfxM(instruments, patterns, sequence, BPM = 125)
2804
2862
  {
@@ -2836,7 +2894,7 @@ function zzfxM(instruments, patterns, sequence, BPM = 125)
2836
2894
  patternChannel = patterns[patternIndex][channelIndex] || [0, 0, 0];
2837
2895
 
2838
2896
  // check if there are more channels
2839
- hasMore |= !!patterns[patternIndex][channelIndex];
2897
+ hasMore ||= !!patterns[patternIndex][channelIndex];
2840
2898
 
2841
2899
  // get next offset, use the length of first channel
2842
2900
  nextSampleOffset = outSampleOffset + (patterns[patternIndex][0].length - 2 - !notFirstBeat) * beatLength;
@@ -2849,7 +2907,7 @@ function zzfxM(instruments, patterns, sequence, BPM = 125)
2849
2907
 
2850
2908
  // stop if end, different instrument or new note
2851
2909
  stop = i == patternChannel.length + isSequenceEnd - 1 && isSequenceEnd ||
2852
- instrument != (patternChannel[0] || 0) | note | 0;
2910
+ instrument != (patternChannel[0] || 0) || note;
2853
2911
 
2854
2912
  // fill buffer with samples for previous beat, most cpu intensive part
2855
2913
  for (j = 0; j < beatLength && notFirstBeat;
@@ -2893,16 +2951,16 @@ function zzfxM(instruments, patterns, sequence, BPM = 125)
2893
2951
  }
2894
2952
 
2895
2953
  return [leftChannelBuffer, rightChannelBuffer];
2896
- }
2954
+ }
2897
2955
  /**
2898
2956
  * 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
2957
+ * - Caches arrays of tiles to off screen canvas for fast rendering
2958
+ * - Unlimted numbers of layers, allocates canvases as needed
2959
+ * - Interfaces with EngineObject for collision
2960
+ * - Collision layer is separate from visible layers
2961
+ * - It is recommended to have a visible layer that matches the collision
2962
+ * - Tile layers can be drawn to using their context with canvas2d
2963
+ * - Drawn directly to the main canvas without using WebGL
2906
2964
  * @namespace TileCollision
2907
2965
  */
2908
2966
 
@@ -2933,15 +2991,19 @@ function initTileCollision(size)
2933
2991
  * @param {Vector2} pos
2934
2992
  * @param {Number} [data=0]
2935
2993
  * @memberof TileCollision */
2936
- const setTileCollisionData = (pos, data=0)=>
2994
+ function setTileCollisionData(pos, data=0)
2995
+ {
2937
2996
  pos.arrayCheck(tileCollisionSize) && (tileCollision[(pos.y|0)*tileCollisionSize.x+pos.x|0] = data);
2997
+ }
2938
2998
 
2939
2999
  /** Get tile collision data
2940
3000
  * @param {Vector2} pos
2941
3001
  * @return {Number}
2942
3002
  * @memberof TileCollision */
2943
- const getTileCollisionData = (pos)=>
2944
- pos.arrayCheck(tileCollisionSize) ? tileCollision[(pos.y|0)*tileCollisionSize.x+pos.x|0] : 0;
3003
+ function getTileCollisionData(pos)
3004
+ {
3005
+ return pos.arrayCheck(tileCollisionSize) ? tileCollision[(pos.y|0)*tileCollisionSize.x+pos.x|0] : 0;
3006
+ }
2945
3007
 
2946
3008
  /** Check if collision with another object should occur
2947
3009
  * @param {Vector2} pos
@@ -2959,12 +3021,12 @@ function tileCollisionTest(pos, size=vec2(), object)
2959
3021
  for (let x = minX; x < maxX; ++x)
2960
3022
  {
2961
3023
  const tileData = tileCollision[y*tileCollisionSize.x+x];
2962
- if (tileData && (!object || object.collideWithTile(tileData, new Vector2(x, y))))
3024
+ if (tileData && (!object || object.collideWithTile(tileData, vec2(x, y))))
2963
3025
  return 1;
2964
3026
  }
2965
3027
  }
2966
3028
 
2967
- /** Return the center of tile if any that is hit (this does not return the exact hit point)
3029
+ /** Return the center of tile if any that is hit (does not return the exact intersection)
2968
3030
  * @param {Vector2} posStart
2969
3031
  * @param {Vector2} posEnd
2970
3032
  * @param {EngineObject} [object]
@@ -2973,28 +3035,41 @@ function tileCollisionTest(pos, size=vec2(), object)
2973
3035
  function tileCollisionRaycast(posStart, posEnd, object)
2974
3036
  {
2975
3037
  // test if a ray collides with tiles from start to end
2976
- // todo: a way to get the exact hit point, it must still register as inside the hit tile
2977
- const posDelta = (posEnd = posEnd.floor()).subtract(posStart = posStart.floor());
2978
- const dx = abs(posDelta.x), dy = -abs(posDelta.y);
2979
- const sx = sign(posDelta.x), sy = sign(posDelta.y);
2980
-
2981
- for (let x = posStart.x, y = posStart.y, e = dx + dy;;)
3038
+ // todo: a way to get the exact hit point, it must still be inside the hit tile
3039
+ const delta = posEnd.subtract(posStart);
3040
+ const totalLength = delta.length();
3041
+ const normalizedDelta = delta.normalize();
3042
+ const unit = vec2(abs(1/normalizedDelta.x), abs(1/normalizedDelta.y));
3043
+ const flooredPosStart = posStart.floor();
3044
+
3045
+ // setup iteration variables
3046
+ let pos = flooredPosStart;
3047
+ let xi = unit.x * (delta.x < 0 ? posStart.x - pos.x : pos.x - posStart.x + 1);
3048
+ let yi = unit.y * (delta.y < 0 ? posStart.y - pos.y : pos.y - posStart.y + 1);
3049
+
3050
+ while (1)
2982
3051
  {
2983
- const tileData = getTileCollisionData(vec2(x,y));
2984
- if (tileData && (object ? object.collideWithTileRaycast(tileData, new Vector2(x, y)) : tileData > 0))
3052
+ // check for tile collision
3053
+ const tileData = getTileCollisionData(pos);
3054
+ if (tileData && (!object || object.collideWithTile(tileData, pos)))
2985
3055
  {
2986
- debugRaycast && debugLine(posStart, posEnd, '#f00',.02, 1);
2987
- debugRaycast && debugPoint(new Vector2(x+.5, y+.5), '#ff0', 1);
2988
- return new Vector2(x+.5, y+.5);
3056
+ debugRaycast && debugLine(posStart, posEnd, '#f00', .02);
3057
+ debugRaycast && debugPoint(pos.add(vec2(.5)), '#ff0');
3058
+ return pos.add(vec2(.5));
2989
3059
  }
2990
3060
 
2991
- // update Bresenham line drawing algorithm
2992
- if (x == posEnd.x & y == posEnd.y) break;
2993
- const e2 = 2*e;
2994
- if (e2 >= dy) e += dy, x += sx;
2995
- if (e2 <= dx) e += dx, y += sy;
3061
+ // check if past the end
3062
+ if (xi > totalLength && yi > totalLength)
3063
+ break;
3064
+
3065
+ // get coordinates of the next tile to check
3066
+ if (xi > yi)
3067
+ pos.y += sign(delta.y), yi += unit.y;
3068
+ else
3069
+ pos.x += sign(delta.x), xi += unit.x;
2996
3070
  }
2997
- debugRaycast && debugLine(posStart, posEnd, '#00f',.02, 1);
3071
+
3072
+ debugRaycast && debugLine(posStart, posEnd, '#00f', .02);
2998
3073
  }
2999
3074
 
3000
3075
  ///////////////////////////////////////////////////////////////////////////////
@@ -3035,10 +3110,10 @@ class TileLayerData
3035
3110
 
3036
3111
  /**
3037
3112
  * 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
3113
+ * - Each Tile layer is rendered to an off screen canvas
3114
+ * - To allow dynamic modifications, layers are rendered using canvas 2d
3115
+ * - Some devices like mobile phones are limited to 4k texture resolution
3116
+ * - So with 16x16 tiles this limits layers to 256x256 on mobile devices
3042
3117
  * @extends EngineObject
3043
3118
  * @example
3044
3119
  * // create tile collision and visible tile layer
@@ -3237,13 +3312,10 @@ constructor(pos, size=tileCollisionSize, tileSize=tileSizeDefault, scale=vec2(1)
3237
3312
  * @param {Number} [angle=0] */
3238
3313
  drawRect(pos, size, color, angle)
3239
3314
  { this.drawTile(pos, size, -1, 0, color, angle); }
3240
- }
3241
- /*
3242
- LittleJS Particle System
3243
- - Spawns particles with randomness from parameters
3244
- - Updates particle physics
3245
- - Fast particle rendering
3246
- */
3315
+ }
3316
+ /**
3317
+ * LittleJS Particle System
3318
+ */
3247
3319
 
3248
3320
  'use strict';
3249
3321
 
@@ -3328,7 +3400,7 @@ class ParticleEmitter extends EngineObject
3328
3400
  localSpace
3329
3401
  )
3330
3402
  {
3331
- super(pos, new Vector2, tileIndex, tileSize, angle, undefined, renderOrder);
3403
+ super(pos, vec2(), tileIndex, tileSize, angle, undefined, renderOrder);
3332
3404
 
3333
3405
  // emitter settings
3334
3406
  /** @property {Number} - World space size of the emitter (float for circle diameter, vec2 for rect) */
@@ -3395,7 +3467,7 @@ class ParticleEmitter extends EngineObject
3395
3467
  this.parent && super.update();
3396
3468
 
3397
3469
  // update emitter
3398
- if (!this.emitTime | this.getAliveTime() <= this.emitTime)
3470
+ if (!this.emitTime || this.getAliveTime() <= this.emitTime)
3399
3471
  {
3400
3472
  // emit particles
3401
3473
  if (this.emitRate * particleEmitRateScale)
@@ -3417,7 +3489,7 @@ class ParticleEmitter extends EngineObject
3417
3489
  {
3418
3490
  // spawn a particle
3419
3491
  let pos = this.emitSize.x != undefined ? // check if vec2 was used for size
3420
- (new Vector2(rand(-.5,.5), rand(-.5,.5)))
3492
+ vec2(rand(-.5,.5), rand(-.5,.5))
3421
3493
  .multiply(this.emitSize).rotate(this.angle) // box emitter
3422
3494
  : randInCircle(this.emitSize/2); // circle emitter
3423
3495
  let angle = rand(this.particleConeAngle, -this.particleConeAngle);
@@ -3447,7 +3519,7 @@ class ParticleEmitter extends EngineObject
3447
3519
  // build particle settings
3448
3520
  particle.colorStart = colorStart;
3449
3521
  particle.colorEndDelta = colorEnd.subtract(colorStart);
3450
- particle.velocity = (new Vector2).setAngle(velocityAngle, speed);
3522
+ particle.velocity = vec2().setAngle(velocityAngle, speed);
3451
3523
  particle.angleVelocity = angleSpeed;
3452
3524
  particle.lifeTime = particleTime;
3453
3525
  particle.sizeStart = sizeStart;
@@ -3492,7 +3564,7 @@ class Particle extends EngineObject
3492
3564
  * @param {Number} [angle=0] - Angle to rotate the particle
3493
3565
  */
3494
3566
  constructor(pos, tileIndex, tileSize, angle)
3495
- { super(pos, new Vector2, tileIndex, tileSize, angle); }
3567
+ { super(pos, vec2(), tileIndex, tileSize, angle); }
3496
3568
 
3497
3569
  /** Render the particle, automatically called each frame, sorted by renderOrder */
3498
3570
  render()
@@ -3500,7 +3572,7 @@ class Particle extends EngineObject
3500
3572
  // modulate size and color
3501
3573
  const p = min((time - this.spawnTime) / this.lifeTime, 1);
3502
3574
  const radius = this.sizeStart + p * this.sizeEndDelta;
3503
- const size = new Vector2(radius, radius);
3575
+ const size = vec2(radius);
3504
3576
  const fadeRate = this.fadeRate/2;
3505
3577
  const color = new Color(
3506
3578
  this.colorStart.r + p * this.colorEndDelta.r,
@@ -3546,12 +3618,12 @@ class Particle extends EngineObject
3546
3618
  this.destroyed = 1;
3547
3619
  }
3548
3620
  }
3549
- }
3621
+ }
3550
3622
  /**
3551
3623
  * LittleJS Medal System
3552
- * <br> - Tracks and displays medals
3553
- * <br> - Saves medals to local storage
3554
- * <br> - Newgrounds integration
3624
+ * - Tracks and displays medals
3625
+ * - Saves medals to local storage
3626
+ * - Newgrounds integration
3555
3627
  * @namespace Medals
3556
3628
  */
3557
3629
 
@@ -3568,8 +3640,8 @@ let medalsDisplayQueue = [], medalsSaveName, medalsDisplayTimeLast;
3568
3640
  ///////////////////////////////////////////////////////////////////////////////
3569
3641
 
3570
3642
  /** Initialize medals with a save name used for storage
3571
- * <br> - Call this after creating all medals
3572
- * <br> - Checks if medals are unlocked
3643
+ * - Call this after creating all medals
3644
+ * - Checks if medals are unlocked
3573
3645
  * @param {String} saveName
3574
3646
  * @memberof Medals */
3575
3647
  function medalsInit(saveName)
@@ -3839,20 +3911,43 @@ class Newgrounds
3839
3911
  CryptoJS()
3840
3912
  {
3841
3913
  ///////////////////////////////////////////////////////////////////////////////
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
-
3914
+ // Crypto-JS - https://github.com/brix/crypto-js - MIT License
3915
+ //
3916
+ // [The MIT License (MIT)](http://opensource.org/licenses/MIT)
3917
+ //
3918
+ // Copyright (c) 2009-2013 Jeff Mott
3919
+ // Copyright (c) 2013-2016 Evan Vosberg
3920
+ //
3921
+ // Permission is hereby granted, free of charge, to any person obtaining a copy
3922
+ // of this software and associated documentation files (the "Software"), to deal
3923
+ // in the Software without restriction, including without limitation the rights
3924
+ // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
3925
+ // copies of the Software, and to permit persons to whom the Software is
3926
+ // furnished to do so, subject to the following conditions:
3927
+ //
3928
+ // The above copyright notice and this permission notice shall be included in
3929
+ // all copies or substantial portions of the Software.
3930
+ //
3931
+ // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
3932
+ // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
3933
+ // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
3934
+ // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
3935
+ // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
3936
+ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
3937
+ // THE SOFTWARE.
3845
3938
  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));
3939
+ // end of Crypto-JS
3940
+ ///////////////////////////////////////////////////////////////////////////////
3846
3941
  }
3847
- }
3942
+ }
3848
3943
  /**
3849
3944
  * 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
3945
+ * - All webgl used by the engine is wrapped up here
3946
+ * - For normal stuff you won't need to see or call anything in this file
3947
+ * - For advanced stuff there are helper functions to create shaders, textures, etc
3948
+ * - Can be disabled with glEnable to revert to 2D canvas rendering
3949
+ * - Batches sprite rendering on GPU for incredibly fast performance
3950
+ * - Sprite transform math is done in the shader where possible
3856
3951
  * @namespace WebGL
3857
3952
  */
3858
3953
 
@@ -3927,7 +4022,7 @@ function glSetBlendMode(additive)
3927
4022
  }
3928
4023
 
3929
4024
  /** 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
4025
+ * - This may also flush the gl buffer resulting in more draw calls and worse performance
3931
4026
  * @param {WebGLTexture} [texture=glTileTexture]
3932
4027
  * @memberof WebGL */
3933
4028
  function glSetTexture(texture=glTileTexture)
@@ -3988,7 +4083,7 @@ function glCreateTexture(image)
3988
4083
  image && image.width && glContext.texImage2D(gl_TEXTURE_2D, 0, gl_RGBA, gl_RGBA, gl_UNSIGNED_BYTE, image);
3989
4084
 
3990
4085
  // use point filtering for pixelated rendering
3991
- const filter = cavasPixelated ? gl_NEAREST : gl_LINEAR;
4086
+ const filter = canvasPixelated ? gl_NEAREST : gl_LINEAR;
3992
4087
  glContext.texParameteri(gl_TEXTURE_2D, gl_TEXTURE_MIN_FILTER, filter);
3993
4088
  glContext.texParameteri(gl_TEXTURE_2D, gl_TEXTURE_MAG_FILTER, filter);
3994
4089
  glContext.texParameteri(gl_TEXTURE_2D, gl_TEXTURE_WRAP_S, gl_CLAMP_TO_EDGE);
@@ -4238,28 +4333,24 @@ gl_VERTICES_PER_QUAD = 6,
4238
4333
  gl_INDICIES_PER_VERT = 6,
4239
4334
  gl_MAX_BATCH = 1<<16,
4240
4335
  gl_VERTEX_BYTE_STRIDE = (4 * 2) * 2 + (4) * 2, // vec2 * 2 + (char * 4) * 2
4241
- 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
4336
+ gl_VERTEX_BUFFER_SIZE = gl_MAX_BATCH * gl_VERTICES_PER_QUAD * gl_VERTEX_BYTE_STRIDE;
4337
+ /**
4338
+ * LittleJS - The Tiny JavaScript Game Engine That Can!
4339
+ * MIT License - Copyright 2021 Frank Force
4340
+ *
4341
+ * Engine Features
4342
+ * - Object oriented system with base class engine object
4343
+ * - Base class object handles update, physics, collision, rendering, etc
4344
+ * - Engine helper classes and functions like Vector2, Color, and Timer
4345
+ * - Super fast rendering system for tile sheets
4346
+ * - Sound effects audio with zzfx and music with zzfxm
4347
+ * - Input processing system with gamepad and touchscreen support
4348
+ * - Tile layer rendering and collision system
4349
+ * - Particle effect system
4350
+ * - Medal system tracks and displays achievements
4351
+ * - Debug tools and debug rendering system
4352
+ * - Post processing effects
4353
+ * - Call engineInit() to start it up!
4263
4354
  * @namespace Engine
4264
4355
  */
4265
4356
 
@@ -4275,7 +4366,7 @@ const engineName = 'LittleJS';
4275
4366
  * @type {String}
4276
4367
  * @default
4277
4368
  * @memberof Engine */
4278
- const engineVersion = '1.6.4';
4369
+ const engineVersion = '1.6.92';
4279
4370
 
4280
4371
  /** Frames per second to update objects
4281
4372
  * @type {Number}
@@ -4345,10 +4436,11 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
4345
4436
  debug && (tileImage.onload=()=>ASSERT(1)); // tile sheet can not reloaded
4346
4437
 
4347
4438
  // 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
4439
+ const styleBody = 'margin:0;overflow:hidden;' + // fill the window
4440
+ 'background:#000;' + // set background color
4441
+ 'touch-action:none;' + // prevent mobile pinch to resize
4442
+ 'user-select:none;' + // prevent mobile hold to select
4443
+ '-webkit-user-select:none'; // compatibility for ios
4352
4444
  document.body.style = styleBody;
4353
4445
  document.body.appendChild(mainCanvas = document.createElement('canvas'));
4354
4446
  mainContext = mainCanvas.getContext('2d');
@@ -4361,8 +4453,10 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
4361
4453
  document.body.appendChild(overlayCanvas = document.createElement('canvas'));
4362
4454
  overlayContext = overlayCanvas.getContext('2d');
4363
4455
 
4364
- // set canvas style to fill the window
4365
- const styleCanvas = 'position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)';
4456
+ // set canvas style
4457
+ const styleCanvas = 'position:absolute;' +
4458
+ 'top:50%;left:50%;transform:translate(-50%,-50%);' + // center the canvas
4459
+ (canvasPixelated?'image-rendering:pixelated':''); // set pixelated rendering
4366
4460
  (glCanvas||mainCanvas).style = mainCanvas.style = overlayCanvas.style = styleCanvas;
4367
4461
 
4368
4462
  gameInit();
@@ -4370,7 +4464,7 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
4370
4464
  };
4371
4465
 
4372
4466
  // frame time tracking
4373
- let frameTimeLastMS = 0, frameTimeBufferMS, averageFPS;
4467
+ let frameTimeLastMS = 0, frameTimeBufferMS = 0, averageFPS;
4374
4468
 
4375
4469
  // main update loop
4376
4470
  function engineUpdate(frameTimeMS=0)
@@ -4495,7 +4589,7 @@ function enginePreRender()
4495
4589
  mainCanvasSize = vec2(mainCanvas.width, mainCanvas.height);
4496
4590
 
4497
4591
  // disable smoothing for pixel art
4498
- mainContext.imageSmoothingEnabled = !cavasPixelated;
4592
+ mainContext.imageSmoothingEnabled = !canvasPixelated;
4499
4593
 
4500
4594
  // setup gl rendering if enabled
4501
4595
  glEnable && glPreRender();
@@ -4509,7 +4603,7 @@ function engineObjectsUpdate()
4509
4603
  engineObjectsCollide = engineObjects.filter(o=>o.collideSolidObjects);
4510
4604
 
4511
4605
  // recursive object update
4512
- const updateObject = (o)=>
4606
+ function updateObject(o)
4513
4607
  {
4514
4608
  if (!o.destroyed)
4515
4609
  {
@@ -4561,210 +4655,202 @@ function engineObjectsCallback(pos, size, callbackFunction, objects=engineObject
4561
4655
  for (const o of objects)
4562
4656
  pos.distanceSquared(o.pos) < sizeSquared && callbackFunction(o);
4563
4657
  }
4564
- }
4565
-
4658
+ }
4659
+
4566
4660
  /**
4567
4661
  * LittleJS Module Export
4568
- * <br> - Export engine as a module with extra functions where necessary
4662
+ * - Export engine as a module with extra functions where necessary
4569
4663
  */
4570
4664
 
4571
- // Setters for all variables that devs will need to modify
4572
-
4573
-
4574
4665
  /** Set position of camera in world space
4575
4666
  * @param {Vector2} pos
4576
4667
  * @memberof Settings */
4577
- const setCameraPos = (pos)=> cameraPos = pos;
4668
+ function setCameraPos(pos) { cameraPos = pos; }
4578
4669
 
4579
4670
  /** Set scale of camera in world space
4580
4671
  * @param {Number} scale
4581
4672
  * @memberof Settings */
4582
- const setCameraScale = (scale)=> cameraScale = scale;
4673
+ function setCameraScale(scale) { cameraScale = scale; }
4583
4674
 
4584
4675
  /** Set max size of the canvas
4585
4676
  * @param {Vector2} size
4586
4677
  * @memberof Settings */
4587
- const setCanvasMaxSize = (size)=> canvasMaxSize = size;
4678
+ function setCanvasMaxSize(size) { canvasMaxSize = size; }
4588
4679
 
4589
4680
  /** Set fixed size of the canvas
4590
4681
  * @param {Vector2} size
4591
4682
  * @memberof Settings */
4592
- const setCanvasFixedSize = (size)=> canvasFixedSize = size;
4683
+ function setCanvasFixedSize(size) { canvasFixedSize = size; }
4593
4684
 
4594
4685
  /** Disables anti aliasing for pixel art if true
4595
4686
  * @param {Boolean} pixelated
4596
4687
  * @memberof Settings */
4597
- const setCavasPixelated = (pixelated)=> cavasPixelated = pixelated;
4688
+ function setCanvasPixelated(pixelated) { canvasPixelated = pixelated; }
4598
4689
 
4599
4690
  /** Set default font used for text rendering
4600
4691
  * @param {String} font
4601
4692
  * @memberof Settings */
4602
- const setFontDefault = (font)=> fontDefault = font;
4693
+ function setFontDefault(font) { fontDefault = font; }
4603
4694
 
4604
4695
  /** Set if webgl rendering is enabled
4605
4696
  * @param {Boolean} enable
4606
4697
  * @memberof Settings */
4607
- const setGlEnable = (enable)=> glEnable = enable;
4698
+ function setGlEnable(enable) { glEnable = enable; }
4608
4699
 
4609
4700
  /** Set to not composite the WebGL canvas
4610
4701
  * @param {Boolean} overlay
4611
4702
  * @memberof Settings */
4612
- const setGlOverlay = (overlay)=> glOverlay = overlay;
4703
+ function setGlOverlay(overlay) { glOverlay = overlay; }
4613
4704
 
4614
4705
  /** Set default size of tiles in pixels
4615
4706
  * @param {Vector2} size
4616
4707
  * @memberof Settings */
4617
- const setTileSizeDefault = (size)=> tileSizeDefault = size;
4708
+ function setTileSizeDefault(size) { tileSizeDefault = size; }
4618
4709
 
4619
4710
  /** Set to prevent tile bleeding from neighbors in pixels
4620
4711
  * @param {Number} scale
4621
4712
  * @memberof Settings */
4622
- const setTileFixBleedScale = (scale)=> tileFixBleedScale = scale;
4713
+ function setTileFixBleedScale(scale) { tileFixBleedScale = scale; }
4623
4714
 
4624
4715
  /** Set if collisions between objects are enabled
4625
4716
  * @param {Boolean} enable
4626
4717
  * @memberof Settings */
4627
- const setEnablePhysicsSolver = (enable)=> enablePhysicsSolver = enable;
4718
+ function setEnablePhysicsSolver(enable) { enablePhysicsSolver = enable; }
4628
4719
 
4629
4720
  /** Set default object mass for collison calcuations
4630
4721
  * @param {Number} mass
4631
4722
  * @memberof Settings */
4632
- const setObjectDefaultMass = (mass)=> objectDefaultMass = mass;
4723
+ function setObjectDefaultMass(mass) { objectDefaultMass = mass; }
4633
4724
 
4634
4725
  /** Set how much to slow velocity by each frame
4635
4726
  * @param {Number} damping
4636
4727
  * @memberof Settings */
4637
- const setObjectDefaultDamping = (damp)=> objectDefaultDamping = damp;
4728
+ function setObjectDefaultDamping(damp) { objectDefaultDamping = damp; }
4638
4729
 
4639
4730
  /** Set how much to slow angular velocity each frame
4640
4731
  * @param {Number} damping
4641
4732
  * @memberof Settings */
4642
- const setObjectDefaultAngleDamping = (damp)=> objectDefaultAngleDamping = damp;
4733
+ function setObjectDefaultAngleDamping(damp) { objectDefaultAngleDamping = damp; }
4643
4734
 
4644
4735
  /** Set how much to bounce when a collision occur
4645
4736
  * @param {Number} elasticity
4646
4737
  * @memberof Settings */
4647
- const setObjectDefaultElasticity = (elasticity)=> objectDefaultElasticity = elasticity;
4738
+ function setObjectDefaultElasticity(elasticity) { objectDefaultElasticity = elasticity; }
4648
4739
 
4649
4740
  /** Set how much to slow when touching
4650
4741
  * @param {Number} friction
4651
4742
  * @memberof Settings */
4652
- const setObjectDefaultFriction = (friction)=> objectDefaultFriction = friction;
4743
+ function setObjectDefaultFriction(friction) { objectDefaultFriction = friction; }
4653
4744
 
4654
4745
  /** Set max speed to avoid fast objects missing collisions
4655
4746
  * @param {Number} speed
4656
4747
  * @memberof Settings */
4657
- const setObjectMaxSpeed = (speed)=> objectMaxSpeed = speed;
4748
+ function setObjectMaxSpeed(speed) { objectMaxSpeed = speed; }
4658
4749
 
4659
4750
  /** Set how much gravity to apply to objects along the Y axis
4660
4751
  * @param {Number} gravity
4661
4752
  * @memberof Settings */
4662
- const setGravity = (g)=> gravity = g;
4753
+ function setGravity(g) { gravity = g; }
4663
4754
 
4664
4755
  /** Set to scales emit rate of particles
4665
4756
  * @param {Number} scale
4666
4757
  * @memberof Settings */
4667
- const setParticleEmitRateScale = (scale)=> particleEmitRateScale = scale;
4758
+ function setParticleEmitRateScale(scale) { particleEmitRateScale = scale; }
4668
4759
 
4669
4760
  /** Set if gamepads are enabled
4670
4761
  * @param {Boolean} enable
4671
4762
  * @memberof Settings */
4672
- const setGamepadsEnable = (enable)=> gamepadsEnable = enable;
4763
+ function setGamepadsEnable(enable) { gamepadsEnable = enable; }
4673
4764
 
4674
4765
  /** Set if the dpad input is also routed to the left analog stick
4675
4766
  * @param {Boolean} enable
4676
4767
  * @memberof Settings */
4677
- const setGamepadDirectionEmulateStick = (enable)=> gamepadDirectionEmulateStick = enable;
4768
+ function setGamepadDirectionEmulateStick(enable) { gamepadDirectionEmulateStick = enable; }
4678
4769
 
4679
4770
  /** Set if true the WASD keys are also routed to the direction keys
4680
4771
  * @param {Boolean} enable
4681
4772
  * @memberof Settings */
4682
- const setInputWASDEmulateDirection = (enable)=> inputWASDEmulateDirection = enable;
4773
+ function setInputWASDEmulateDirection(enable) { inputWASDEmulateDirection = enable; }
4683
4774
 
4684
4775
  /** Set if touch gamepad should appear on mobile devices
4685
4776
  * @param {Boolean} enable
4686
4777
  * @memberof Settings */
4687
- const setTouchGamepadEnable = (enable)=> touchGamepadEnable = enable;
4778
+ function setTouchGamepadEnable(enable) { touchGamepadEnable = enable; }
4688
4779
 
4689
4780
  /** Set if touch gamepad should be analog stick or 8 way dpad
4690
4781
  * @param {Boolean} analog
4691
4782
  * @memberof Settings */
4692
- const setTouchGamepadAnalog = (analog)=> touchGamepadAnalog = analog;
4783
+ function setTouchGamepadAnalog(analog) { touchGamepadAnalog = analog; }
4693
4784
 
4694
4785
  /** Set size of virutal gamepad for touch devices in pixels
4695
4786
  * @param {Number} size
4696
4787
  * @memberof Settings */
4697
- const setTouchGamepadSize = (size)=> touchGamepadSize = size;
4788
+ function setTouchGamepadSize(size) { touchGamepadSize = size; }
4698
4789
 
4699
4790
  /** Set transparency of touch gamepad overlay
4700
4791
  * @param {Number} alpha
4701
4792
  * @memberof Settings */
4702
- const setTouchGamepadAlpha = (alpha)=> touchGamepadAlpha = alpha;
4793
+ function setTouchGamepadAlpha(alpha) { touchGamepadAlpha = alpha; }
4703
4794
 
4704
4795
  /** Set to allow vibration hardware if it exists
4705
4796
  * @param {Boolean} enable
4706
4797
  * @memberof Settings */
4707
- const setVibrateEnable = (enable)=> vibrateEnable = enable;
4798
+ function setVibrateEnable(enable) { vibrateEnable = enable; }
4708
4799
 
4709
4800
  /** Set to disable all audio code
4710
4801
  * @param {Boolean} enable
4711
4802
  * @memberof Settings */
4712
- const setSoundEnable = (enable)=> soundEnable = enable;
4803
+ function setSoundEnable(enable) { soundEnable = enable; }
4713
4804
 
4714
4805
  /** Set volume scale to apply to all sound, music and speech
4715
4806
  * @param {Number} volume
4716
4807
  * @memberof Settings */
4717
- const setSoundVolume = (volume)=> soundVolume = volume;
4808
+ function setSoundVolume(volume) { soundVolume = volume; }
4718
4809
 
4719
4810
  /** Set default range where sound no longer plays
4720
4811
  * @param {Number} range
4721
4812
  * @memberof Settings */
4722
- const setSoundDefaultRange = (range)=> soundDefaultRange = range;
4813
+ function setSoundDefaultRange(range) { soundDefaultRange = range; }
4723
4814
 
4724
4815
  /** Set default range percent to start tapering off sound
4725
4816
  * @param {Number} taper
4726
4817
  * @memberof Settings */
4727
- const setSoundDefaultTaper = (taper)=> soundDefaultTaper = taper;
4818
+ function setSoundDefaultTaper(taper) { soundDefaultTaper = taper; }
4728
4819
 
4729
4820
  /** Set how long to show medals for in seconds
4730
4821
  * @param {Number} time
4731
4822
  * @memberof Settings */
4732
- const setMedalDisplayTime = (time)=> medalDisplayTime = time;
4823
+ function setMedalDisplayTime(time) { medalDisplayTime = time; }
4733
4824
 
4734
4825
  /** Set how quickly to slide on/off medals in seconds
4735
4826
  * @param {Number} time
4736
4827
  * @memberof Settings */
4737
- const setMedalDisplaySlideTime = (time)=> medalDisplaySlideTime = time;
4828
+ function setMedalDisplaySlideTime(time) { medalDisplaySlideTime = time; }
4738
4829
 
4739
4830
  /** Set size of medal display
4740
4831
  * @param {Vector2} size
4741
4832
  * @memberof Settings */
4742
- const setMedalDisplaySize = (size)=> medalDisplaySize = size;
4833
+ function setMedalDisplaySize(size) { medalDisplaySize = size; }
4743
4834
 
4744
4835
  /** Set size of icon in medal display
4745
4836
  * @param {Number} size
4746
4837
  * @memberof Settings */
4747
- const setMedalDisplayIconSize = (size)=> medalDisplayIconSize = size;
4838
+ function setMedalDisplayIconSize(size) { medalDisplayIconSize = size; }
4748
4839
 
4749
4840
  /** Set to stop medals from being unlockable
4750
4841
  * @param {Boolean} preventUnlock
4751
4842
  * @memberof Settings */
4752
- const setMedalsPreventUnlock = (prevent)=> medalsPreventUnlock = prevent;
4843
+ function setMedalsPreventUnlock(preventUnlock) { medalsPreventUnlock = preventUnlock; }
4753
4844
 
4754
4845
  /** Set if watermark with FPS should be shown
4755
4846
  * @param {Boolean} show
4756
4847
  * @memberof Debug */
4757
- const setShowWatermark = (show)=> showWatermark = show;
4758
-
4759
- /** Set if god mode is enabled
4760
- * @param {Boolean} enable
4761
- * @memberof Debug */
4762
- const setGodMode = (enable)=> godMode = enable;
4848
+ function setShowWatermark(show) { showWatermark = show; }
4763
4849
 
4764
4850
  /** Set key code used to toggle debug mode, Esc by default
4765
4851
  * @param {Number} key
4766
4852
  * @memberof Debug */
4767
- const setDebugKey = (key)=> debugKey = key;
4853
+ function setDebugKey(key) { debugKey = key; }
4768
4854
 
4769
4855
  export {
4770
4856
  // Setters for global variables
@@ -4772,7 +4858,7 @@ export {
4772
4858
  setCameraScale,
4773
4859
  setCanvasMaxSize,
4774
4860
  setCanvasFixedSize,
4775
- setCavasPixelated,
4861
+ setCanvasPixelated,
4776
4862
  setFontDefault,
4777
4863
  setGlEnable,
4778
4864
  setGlOverlay,
@@ -4805,13 +4891,12 @@ export {
4805
4891
  setMedalDisplayIconSize,
4806
4892
  setMedalsPreventUnlock,
4807
4893
  setShowWatermark,
4808
- setGodMode,
4809
4894
  setDebugKey,
4810
4895
 
4811
4896
  // Settings
4812
4897
  canvasMaxSize,
4813
4898
  canvasFixedSize,
4814
- cavasPixelated,
4899
+ canvasPixelated,
4815
4900
  fontDefault,
4816
4901
  tileSizeDefault,
4817
4902
  tileFixBleedScale,
@@ -4848,7 +4933,6 @@ export {
4848
4933
  // Globals
4849
4934
  debug,
4850
4935
  showWatermark,
4851
- godMode,
4852
4936
 
4853
4937
  // Debug
4854
4938
  ASSERT,
@@ -5004,4 +5088,4 @@ export {
5004
5088
  engineObjectsUpdate,
5005
5089
  engineObjectsDestroy,
5006
5090
  engineObjectsCallback,
5007
- };
5091
+ };