littlejsengine 1.6.0 → 1.6.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/README.md +138 -34
  2. package/build/littlejs.d.ts +104 -92
  3. package/build/littlejs.esm.js +414 -342
  4. package/build/littlejs.esm.min.js +1 -1
  5. package/build/littlejs.js +365 -296
  6. package/build/littlejs.min.js +1 -1
  7. package/build/littlejs.release.js +359 -291
  8. package/examples/breakout/game.js +5 -0
  9. package/examples/breakout/gameObjects.js +41 -48
  10. package/examples/breakout/index.html +3 -3
  11. package/examples/breakoutTutorial/README.md +514 -0
  12. package/examples/breakoutTutorial/game.js +181 -0
  13. package/examples/breakoutTutorial/images/1.png +0 -0
  14. package/examples/breakoutTutorial/images/10.png +0 -0
  15. package/examples/breakoutTutorial/images/11.png +0 -0
  16. package/examples/breakoutTutorial/images/2.png +0 -0
  17. package/examples/breakoutTutorial/images/3.png +0 -0
  18. package/examples/breakoutTutorial/images/4.png +0 -0
  19. package/examples/breakoutTutorial/images/5.png +0 -0
  20. package/examples/breakoutTutorial/images/6.png +0 -0
  21. package/examples/breakoutTutorial/images/7.png +0 -0
  22. package/examples/breakoutTutorial/images/8.png +0 -0
  23. package/examples/breakoutTutorial/images/9.png +0 -0
  24. package/examples/breakoutTutorial/index.html +10 -0
  25. package/examples/empty/game.js +10 -0
  26. package/examples/favicon.png +0 -0
  27. package/examples/module/index.html +1 -1
  28. package/examples/particles/index.html +1 -1
  29. package/examples/platformer/gameLevel.js +1 -1
  30. package/examples/platformer/gamePlayer.js +1 -1
  31. package/examples/platformer/index.html +6 -6
  32. package/examples/puzzle/game.js +1 -1
  33. package/examples/puzzle/index.html +2 -2
  34. package/examples/starter/build.bat +3 -2
  35. package/examples/starter/game.js +9 -9
  36. package/examples/starter/index.html +13 -13
  37. package/examples/stress/index.html +1 -1
  38. package/examples/typescript/game.js +89 -89
  39. package/examples/typescript/game.ts +10 -10
  40. package/examples/typescript/index.html +1 -1
  41. package/package.json +4 -4
  42. package/src/engine.js +29 -31
  43. package/src/engineAudio.js +53 -29
  44. package/src/engineBuild.bat +3 -1
  45. package/src/engineDebug.js +25 -24
  46. package/src/engineDraw.js +43 -39
  47. package/src/engineExport.js +49 -46
  48. package/src/engineInput.js +83 -74
  49. package/src/engineMedals.js +31 -8
  50. package/src/engineObject.js +25 -25
  51. package/src/engineParticles.js +3 -6
  52. package/src/engineRelease.js +19 -19
  53. package/src/engineSettings.js +4 -4
  54. package/src/engineTileLayer.js +18 -14
  55. package/src/engineUtilities.js +43 -34
  56. package/src/engineWebGL.js +8 -8
@@ -1,15 +1,15 @@
1
- /*
2
- LittleJS - Release Build
3
- MIT License - Copyright 2021 Frank Force
4
-
5
- - This file is used for release builds in place of engineDebug.js
6
- - Debug functionality will be disabled to lower size and increase performance
7
- */
1
+ /**
2
+ * LittleJS - Release Build
3
+ * MIT License - Copyright 2021 Frank Force
4
+ * - This file is used for release builds in place of engineDebug.js
5
+ * - Debug functionality is disabled to reduce size and increase performance
6
+ */
8
7
 
9
8
  'use strict';
10
9
 
11
10
  let showWatermark = 0;
12
11
  let godMode = 0;
12
+ let debugKeyCode = 0;
13
13
  const debug = 0;
14
14
  const debugOverlay = 0;
15
15
  const debugPhysics = 0;
@@ -19,24 +19,24 @@ const debugGamepads = 0;
19
19
  const debugMedals = 0;
20
20
 
21
21
  // debug commands are automatically removed from the final build
22
- const ASSERT = ()=> {}
23
- const debugInit = ()=> {}
24
- const debugUpdate = ()=> {}
25
- const debugRender = ()=> {}
26
- const debugRect = ()=> {}
27
- const debugCircle = ()=> {}
28
- const debugPoint = ()=> {}
29
- const debugLine = ()=> {}
30
- const debugAABB = ()=> {}
31
- const debugText = ()=> {}
32
- const debugClear = ()=> {}
33
- const debugSaveCanvas = ()=> {}
22
+ function ASSERT (){}
23
+ function debugInit (){}
24
+ function debugUpdate (){}
25
+ function debugRender (){}
26
+ function debugRect (){}
27
+ function debugCircle (){}
28
+ function debugPoint (){}
29
+ function debugLine (){}
30
+ function debugAABB (){}
31
+ function debugText (){}
32
+ function debugClear (){}
33
+ function debugSaveCanvas (){}
34
34
  /**
35
35
  * LittleJS Utility Classes and Functions
36
- * <br> - General purpose math library
37
- * <br> - Vector2 - fast, simple, easy 2D vector class
38
- * <br> - Color - holds a rgba color with some math functions
39
- * <br> - Timer - tracks time automatically
36
+ * - General purpose math library
37
+ * - Vector2 - fast, simple, easy 2D vector class
38
+ * - Color - holds a rgba color with some math functions
39
+ * - Timer - tracks time automatically
40
40
  * @namespace Utilities
41
41
  */
42
42
 
@@ -52,34 +52,34 @@ const PI = Math.PI;
52
52
  * @param {Number} value
53
53
  * @return {Number}
54
54
  * @memberof Utilities */
55
- const abs = (a)=> a < 0 ? -a : a;
55
+ function abs(a) { return a < 0 ? -a : a; }
56
56
 
57
57
  /** Returns lowest of two values passed in
58
58
  * @param {Number} valueA
59
59
  * @param {Number} valueB
60
60
  * @return {Number}
61
61
  * @memberof Utilities */
62
- const min = (a, b)=> a < b ? a : b;
62
+ function min(a, b) { return a < b ? a : b; }
63
63
 
64
64
  /** Returns highest of two values passed in
65
65
  * @param {Number} valueA
66
66
  * @param {Number} valueB
67
67
  * @return {Number}
68
68
  * @memberof Utilities */
69
- const max = (a, b)=> a > b ? a : b;
69
+ function max(a, b) { return a > b ? a : b; }
70
70
 
71
71
  /** Returns the sign of value passed in (also returns 1 if 0)
72
72
  * @param {Number} value
73
73
  * @return {Number}
74
74
  * @memberof Utilities */
75
- const sign = (a)=> a < 0 ? -1 : 1;
75
+ function sign(a) { return a < 0 ? -1 : 1; }
76
76
 
77
77
  /** Returns first parm modulo the second param, but adjusted so negative numbers work as expected
78
78
  * @param {Number} dividend
79
79
  * @param {Number} [divisor=1]
80
80
  * @return {Number}
81
81
  * @memberof Utilities */
82
- const mod = (a, b=1)=> ((a % b) + b) % b;
82
+ function mod(a, b=1) { return ((a % b) + b) % b; }
83
83
 
84
84
  /** Clamps the value beween max and min
85
85
  * @param {Number} value
@@ -87,7 +87,8 @@ const mod = (a, b=1)=> ((a % b) + b) % b;
87
87
  * @param {Number} [max=1]
88
88
  * @return {Number}
89
89
  * @memberof Utilities */
90
- const clamp = (v, min=0, max=1)=> v < min ? min : v > max ? max : v;
90
+ function clamp(v, min=0, max=1)
91
+ { return v < min ? min : v > max ? max : v; }
91
92
 
92
93
  /** Returns what percentage the value is between max and min
93
94
  * @param {Number} value
@@ -95,7 +96,8 @@ const clamp = (v, min=0, max=1)=> v < min ? min : v > max ? max : v;
95
96
  * @param {Number} [max=1]
96
97
  * @return {Number}
97
98
  * @memberof Utilities */
98
- const percent = (v, min=0, max=1)=> max-min ? clamp((v-min) / (max-min)) : 0;
99
+ function percent(v, min=0, max=1)
100
+ { return max-min ? clamp((v-min) / (max-min)) : 0; }
99
101
 
100
102
  /** Linearly interpolates the percent value between max and min
101
103
  * @param {Number} percent
@@ -103,19 +105,19 @@ const percent = (v, min=0, max=1)=> max-min ? clamp((v-min) / (max-min)) : 0;
103
105
  * @param {Number} [max=1]
104
106
  * @return {Number}
105
107
  * @memberof Utilities */
106
- const lerp = (p, min=0, max=1)=> min + clamp(p) * (max-min);
108
+ function lerp(p, min=0, max=1){ return min + clamp(p) * (max-min); }
107
109
 
108
110
  /** Applies smoothstep function to the percentage value
109
111
  * @param {Number} value
110
112
  * @return {Number}
111
113
  * @memberof Utilities */
112
- const smoothStep = (p)=> p * p * (3 - 2 * p);
114
+ function smoothStep(p) { return p * p * (3 - 2 * p); }
113
115
 
114
116
  /** Returns the nearest power of two not less then the value
115
117
  * @param {Number} value
116
118
  * @return {Number}
117
119
  * @memberof Utilities */
118
- const nearestPowerOfTwo = (v)=> 2**Math.ceil(Math.log2(v));
120
+ function nearestPowerOfTwo(v) { return 2**Math.ceil(Math.log2(v)); }
119
121
 
120
122
  /** Returns true if two axis aligned bounding boxes are overlapping
121
123
  * @param {Vector2} pointA - Center of box A
@@ -124,7 +126,8 @@ const nearestPowerOfTwo = (v)=> 2**Math.ceil(Math.log2(v));
124
126
  * @param {Vector2} [sizeB] - Size of box B
125
127
  * @return {Boolean} - True if overlapping
126
128
  * @memberof Utilities */
127
- 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;
129
+ function isOverlapping(pA, sA, pB, sB)
130
+ { return abs(pA.x - pB.x)*2 < sA.x + sB.x && abs(pA.y - pB.y)*2 < sA.y + sB.y; }
128
131
 
129
132
  /** Returns an oscillating wave between 0 and amplitude with frequency of 1 Hz by default
130
133
  * @param {Number} [frequency=1] - Frequency of the wave in Hz
@@ -132,13 +135,14 @@ const isOverlapping = (pA, sA, pB, sB)=> abs(pA.x - pB.x)*2 < sA.x + sB.x && abs
132
135
  * @param {Number} [t=time] - Value to use for time of the wave
133
136
  * @return {Number} - Value waving between 0 and amplitude
134
137
  * @memberof Utilities */
135
- const wave = (frequency=1, amplitude=1, t=time)=> amplitude/2 * (1 - Math.cos(t*frequency*2*PI));
138
+ function wave(frequency=1, amplitude=1, t=time)
139
+ { return amplitude/2 * (1 - Math.cos(t*frequency*2*PI)); }
136
140
 
137
141
  /** Formats seconds to mm:ss style for display purposes
138
142
  * @param {Number} t - time in seconds
139
143
  * @return {String}
140
144
  * @memberof Utilities */
141
- const formatTime = (t)=> (t/60|0) + ':' + (t%60<10?'0':'') + (t%60|0);
145
+ function formatTime(t) { return (t/60|0) + ':' + (t%60<10?'0':'') + (t%60|0); }
142
146
 
143
147
  ///////////////////////////////////////////////////////////////////////////////
144
148
 
@@ -150,32 +154,33 @@ const formatTime = (t)=> (t/60|0) + ':' + (t%60<10?'0':'') + (t%60|0);
150
154
  * @param {Number} [valueB=0]
151
155
  * @return {Number}
152
156
  * @memberof Random */
153
- const rand = (a=1, b=0)=> b + (a-b)*Math.random();
157
+ function rand(a=1, b=0) { return b + (a-b)*Math.random(); }
154
158
 
155
159
  /** Returns a floored random value the two values passed in
156
160
  * @param {Number} [valueA=1]
157
161
  * @param {Number} [valueB=0]
158
162
  * @return {Number}
159
163
  * @memberof Random */
160
- const randInt = (a=1, b=0)=> rand(a,b)|0;
164
+ function randInt(a=1, b=0) { return rand(a,b)|0; }
161
165
 
162
166
  /** Randomly returns either -1 or 1
163
167
  * @return {Number}
164
168
  * @memberof Random */
165
- const randSign = ()=> randInt(2) * 2 - 1;
169
+ function randSign() { return randInt(2) * 2 - 1; }
166
170
 
167
171
  /** Returns a random Vector2 within a circular shape
168
172
  * @param {Number} [radius=1]
169
173
  * @param {Number} [minRadius=0]
170
174
  * @return {Vector2}
171
175
  * @memberof Random */
172
- const randInCircle = (radius=1, minRadius=0)=> radius > 0 ? randVector(radius * rand(minRadius / radius, 1)**.5) : new Vector2;
176
+ function randInCircle(radius=1, minRadius=0)
177
+ { return radius > 0 ? randVector(radius * rand(minRadius / radius, 1)**.5) : new Vector2; }
173
178
 
174
179
  /** Returns a random Vector2 with the passed in length
175
180
  * @param {Number} [length=1]
176
181
  * @return {Vector2}
177
182
  * @memberof Random */
178
- const randVector = (length=1)=> new Vector2().setAngle(rand(2*PI), length);
183
+ function randVector(length=1) { return new Vector2().setAngle(rand(2*PI), length); }
179
184
 
180
185
  /** Returns a random color between the two passed in colors, combine components if linear
181
186
  * @param {Color} [colorA=Color()]
@@ -183,8 +188,8 @@ const randVector = (length=1)=> new Vector2().setAngle(rand(2*PI), length);
183
188
  * @param {Boolean} [linear]
184
189
  * @return {Color}
185
190
  * @memberof Random */
186
- const randColor = (cA = new Color, cB = new Color(0,0,0,1), linear)=>
187
- 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));
191
+ function randColor(cA = new Color, cB = new Color(0,0,0,1), linear)
192
+ { return linear ? cA.lerp(cB, rand()) : new Color(rand(cA.r,cB.r),rand(cA.g,cB.g),rand(cA.b,cB.b),rand(cA.a,cB.a)); }
188
193
 
189
194
  /** Seed used by the randSeeded function
190
195
  * @type {Number}
@@ -195,16 +200,19 @@ let randSeed = 1;
195
200
  /** Set seed used by the randSeeded function, should not be 0
196
201
  * @param {Number} seed
197
202
  * @memberof Random */
198
- const setRandSeed = (seed)=> randSeed = seed;
203
+ function setRandSeed(seed) { randSeed = seed; }
199
204
 
200
205
  /** Returns a seeded random value between the two values passed in using randSeed
201
206
  * @param {Number} [valueA=1]
202
207
  * @param {Number} [valueB=0]
203
208
  * @return {Number}
204
209
  * @memberof Random */
205
- const randSeeded = (a=1, b=0)=>
210
+ function randSeeded(a=1, b=0)
206
211
  {
207
- randSeed ^= randSeed << 13; randSeed ^= randSeed >>> 17; randSeed ^= randSeed << 5; // xorshift
212
+ // xorshift algorithm
213
+ randSeed ^= randSeed << 13;
214
+ randSeed ^= randSeed >>> 17;
215
+ randSeed ^= randSeed << 5;
208
216
  return b + (a-b) * abs(randSeed % 1e9) / 1e9;
209
217
  }
210
218
 
@@ -222,7 +230,8 @@ const randSeeded = (a=1, b=0)=>
222
230
  * b = vec2(); // set b to (0, 0)
223
231
  * @memberof Utilities
224
232
  */
225
- const vec2 = (x=0, y)=> x.x == undefined ? new Vector2(x, y == undefined? x : y) : new Vector2(x.x, x.y);
233
+ function vec2(x=0, y)
234
+ { return x.x == undefined ? new Vector2(x, y == undefined? x : y) : new Vector2(x.x, x.y); }
226
235
 
227
236
  /**
228
237
  * Check if object is a valid Vector2
@@ -230,11 +239,11 @@ const vec2 = (x=0, y)=> x.x == undefined ? new Vector2(x, y == undefined? x : y)
230
239
  * @return {Boolean}
231
240
  * @memberof Utilities
232
241
  */
233
- const isVector2 = (v)=> !isNaN(v.x) && !isNaN(v.y);
242
+ function isVector2(v) { return !isNaN(v.x) && !isNaN(v.y); }
234
243
 
235
244
  /**
236
245
  * 2D Vector object with vector math library
237
- * <br> - Functions do not change this so they can be chained together
246
+ * - Functions do not change this so they can be chained together
238
247
  * @example
239
248
  * let a = new Vector2(2, 3); // vector with coordinates (2, 3)
240
249
  * let b = new Vector2; // vector with coordinates (0, 0)
@@ -381,7 +390,7 @@ class Vector2
381
390
  * @return {Color}
382
391
  * @memberof Utilities
383
392
  */
384
- const colorRGBA = (r, g, b, a)=> new Color(r, g, b, a);
393
+ function rgb(r, g, b, a) { return new Color(r, g, b, a); }
385
394
 
386
395
  /**
387
396
  * Create a color object with HSLA values
@@ -392,7 +401,7 @@ const colorRGBA = (r, g, b, a)=> new Color(r, g, b, a);
392
401
  * @return {Color}
393
402
  * @memberof Utilities
394
403
  */
395
- const colorHSLA = (h, s, l, a)=> new Color().setHSLA(h, s, l, a);
404
+ function hsl(h, s, l, a) { return new Color().setHSLA(h, s, l, a); }
396
405
 
397
406
  /**
398
407
  * Color object (red, green, blue, alpha) with some helpful functions
@@ -400,8 +409,8 @@ const colorHSLA = (h, s, l, a)=> new Color().setHSLA(h, s, l, a);
400
409
  * let a = new Color; // white
401
410
  * let b = new Color(1, 0, 0); // red
402
411
  * let c = new Color(0, 0, 0, 0); // transparent black
403
- * let d = colorRGBA(0, 0, 1); // blue using rgb color
404
- * let e = colorHSLA(.3, 1, .5); // green using hsl color
412
+ * let d = RGB(0, 0, 1); // blue using rgb color
413
+ * let e = HSL(.3, 1, .5); // green using hsl color
405
414
  */
406
415
  class Color
407
416
  {
@@ -651,11 +660,11 @@ let canvasMaxSize = vec2(1920, 1200);
651
660
  * @memberof Settings */
652
661
  let canvasFixedSize = vec2();
653
662
 
654
- /** Disables anti aliasing for pixel art if true
663
+ /** Disables filtering for crisper pixel art if true
655
664
  * @type {Boolean}
656
665
  * @default
657
666
  * @memberof Settings */
658
- let cavasPixelated = 1;
667
+ let canvasPixelated = 1;
659
668
 
660
669
  /** Default font used for text rendering
661
670
  * @type {String}
@@ -772,8 +781,8 @@ let gamepadDirectionEmulateStick = 1;
772
781
  let inputWASDEmulateDirection = 1;
773
782
 
774
783
  /** True if touch gamepad should appear on mobile devices
775
- * <br> - Supports left analog stick, 4 face buttons and start button (button 9)
776
- * <br> - Must be set by end of gameInit to be activated
784
+ * - Supports left analog stick, 4 face buttons and start button (button 9)
785
+ * - Must be set by end of gameInit to be activated
777
786
  * @type {Boolean}
778
787
  * @default 0
779
788
  * @memberof Settings */
@@ -862,27 +871,23 @@ let medalDisplayIconSize = 50;
862
871
  * @default 0
863
872
  * @memberof Settings */
864
873
  let medalsPreventUnlock;
865
- /*
866
- LittleJS - The Tiny JavaScript Game Engine That Can!
867
- MIT License - Copyright 2021 Frank Force
868
-
869
- Engine Features
870
- - Object oriented system with base class engine object
871
- - Base class object handles update, physics, collision, rendering, etc
872
- - Engine helper classes and functions like Vector2, Color, and Timer
873
- - Super fast rendering system for tile sheets
874
- - Sound effects audio with zzfx and music with zzfxm
875
- - Input processing system with gamepad and touchscreen support
876
- - Tile layer rendering and collision system
877
- - Particle effect system
878
- - Medal system tracks and displays achievements
879
- - Debug tools and debug rendering system
880
- - Post processing effects
881
- - Call engineInit() to start it up!
882
- */
883
-
884
- /**
885
- * LittleJS Engine Globals
874
+ /**
875
+ * LittleJS - The Tiny JavaScript Game Engine That Can!
876
+ * MIT License - Copyright 2021 Frank Force
877
+ *
878
+ * Engine Features
879
+ * - Object oriented system with base class engine object
880
+ * - Base class object handles update, physics, collision, rendering, etc
881
+ * - Engine helper classes and functions like Vector2, Color, and Timer
882
+ * - Super fast rendering system for tile sheets
883
+ * - Sound effects audio with zzfx and music with zzfxm
884
+ * - Input processing system with gamepad and touchscreen support
885
+ * - Tile layer rendering and collision system
886
+ * - Particle effect system
887
+ * - Medal system tracks and displays achievements
888
+ * - Debug tools and debug rendering system
889
+ * - Post processing effects
890
+ * - Call engineInit() to start it up!
886
891
  * @namespace Engine
887
892
  */
888
893
 
@@ -898,7 +903,7 @@ const engineName = 'LittleJS';
898
903
  * @type {String}
899
904
  * @default
900
905
  * @memberof Engine */
901
- const engineVersion = '1.6.0';
906
+ const engineVersion = '1.6.6';
902
907
 
903
908
  /** Frames per second to update objects
904
909
  * @type {Number}
@@ -968,10 +973,11 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
968
973
  debug && (tileImage.onload=()=>ASSERT(1)); // tile sheet can not reloaded
969
974
 
970
975
  // setup html
971
- const styleBody = 'margin:0;overflow:hidden;background:#000' + // fill the window
972
- ';touch-action:none' + // prevent mobile pinch to resize
973
- ';user-select:none' + // prevent mobile hold to select
974
- ';-webkit-user-select:none'; // compatibility for ios
976
+ const styleBody = 'margin:0;overflow:hidden;' + // fill the window
977
+ 'background:#000;' + // set background color
978
+ 'touch-action:none;' + // prevent mobile pinch to resize
979
+ 'user-select:none;' + // prevent mobile hold to select
980
+ '-webkit-user-select:none'; // compatibility for ios
975
981
  document.body.style = styleBody;
976
982
  document.body.appendChild(mainCanvas = document.createElement('canvas'));
977
983
  mainContext = mainCanvas.getContext('2d');
@@ -984,12 +990,13 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
984
990
  document.body.appendChild(overlayCanvas = document.createElement('canvas'));
985
991
  overlayContext = overlayCanvas.getContext('2d');
986
992
 
987
- // set canvas style to fill the window
988
- const styleCanvas = 'position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)';
993
+ // set canvas style
994
+ const styleCanvas = 'position:absolute;' +
995
+ 'top:50%;left:50%;transform:translate(-50%,-50%);' + // center the canvas
996
+ (canvasPixelated?'image-rendering:pixelated':''); // set pixelated rendering
989
997
  (glCanvas||mainCanvas).style = mainCanvas.style = overlayCanvas.style = styleCanvas;
990
998
 
991
999
  gameInit();
992
- touchGamepadCreate();
993
1000
  engineUpdate();
994
1001
  };
995
1002
 
@@ -1119,7 +1126,7 @@ function enginePreRender()
1119
1126
  mainCanvasSize = vec2(mainCanvas.width, mainCanvas.height);
1120
1127
 
1121
1128
  // disable smoothing for pixel art
1122
- mainContext.imageSmoothingEnabled = !cavasPixelated;
1129
+ mainContext.imageSmoothingEnabled = !canvasPixelated;
1123
1130
 
1124
1131
  // setup gl rendering if enabled
1125
1132
  glEnable && glPreRender();
@@ -1133,7 +1140,7 @@ function engineObjectsUpdate()
1133
1140
  engineObjectsCollide = engineObjects.filter(o=>o.collideSolidObjects);
1134
1141
 
1135
1142
  // recursive object update
1136
- const updateObject = (o)=>
1143
+ function updateObject(o)
1137
1144
  {
1138
1145
  if (!o.destroyed)
1139
1146
  {
@@ -1186,32 +1193,32 @@ function engineObjectsCallback(pos, size, callbackFunction, objects=engineObject
1186
1193
  pos.distanceSquared(o.pos) < sizeSquared && callbackFunction(o);
1187
1194
  }
1188
1195
  }
1189
- /*
1190
- LittleJS Object System
1191
- */
1196
+ /**
1197
+ * LittleJS Object System
1198
+ */
1192
1199
 
1193
1200
  'use strict';
1194
1201
 
1195
1202
  /**
1196
1203
  * LittleJS Object Base Object Class
1197
- * <br> - Base object class used by the engine
1198
- * <br> - Automatically adds self to object list
1199
- * <br> - Will be updated and rendered each frame
1200
- * <br> - Renders as a sprite from a tilesheet by default
1201
- * <br> - Can have color and addtive color applied
1202
- * <br> - 2d Physics and collision system
1203
- * <br> - Sorted by renderOrder
1204
- * <br> - Objects can have children attached
1205
- * <br> - Parents are updated before children, and set child transform
1206
- * <br> - Call destroy() to get rid of objects
1207
- * <br>
1208
- * <br>The physics system used by objects is simple and fast with some caveats...
1209
- * <br> - Collision uses the axis aligned size, the object's rotation angle is only for rendering
1210
- * <br> - Objects are guaranteed to not intersect tile collision from physics
1211
- * <br> - If an object starts or is moved inside tile collision, it will not collide with that tile
1212
- * <br> - Collision for objects can be set to be solid to block other objects
1213
- * <br> - Objects may get pushed into overlapping other solid objects, if so they will push away
1214
- * <br> - Solid objects are more performance intensive and should be used sparingly
1204
+ * - Base object class used by the engine
1205
+ * - Automatically adds self to object list
1206
+ * - Will be updated and rendered each frame
1207
+ * - Renders as a sprite from a tilesheet by default
1208
+ * - Can have color and addtive color applied
1209
+ * - 2d Physics and collision system
1210
+ * - Sorted by renderOrder
1211
+ * - Objects can have children attached
1212
+ * - Parents are updated before children, and set child transform
1213
+ * - Call destroy() to get rid of objects
1214
+ *
1215
+ * The physics system used by objects is simple and fast with some caveats...
1216
+ * - Collision uses the axis aligned size, the object's rotation angle is only for rendering
1217
+ * - Objects are guaranteed to not intersect tile collision from physics
1218
+ * - If an object starts or is moved inside tile collision, it will not collide with that tile
1219
+ * - Collision for objects can be set to be solid to block other objects
1220
+ * - Objects may get pushed into overlapping other solid objects, if so they will push away
1221
+ * - Solid objects are more performance intensive and should be used sparingly
1215
1222
  * @example
1216
1223
  * // create an engine object, normally you would first extend the class with your own
1217
1224
  * const pos = vec2(2,3);
@@ -1300,6 +1307,7 @@ class EngineObject
1300
1307
  this.velocity.y += gravity * this.gravityScale;
1301
1308
  this.pos.x += this.velocity.x *= this.damping;
1302
1309
  this.pos.y += this.velocity.y *= this.damping;
1310
+ this.angle += this.angleVelocity *= this.angleDamping;
1303
1311
 
1304
1312
  // physics sanity checks
1305
1313
  ASSERT(this.angleDamping >= 0 && this.angleDamping <= 1);
@@ -1356,6 +1364,7 @@ class EngineObject
1356
1364
  const smallStepUp = (oldPos.y - o.pos.y)*2 > sizeBoth.y + gravity; // prefer to push up if small delta
1357
1365
  const isBlockedX = abs(oldPos.y - o.pos.y)*2 < sizeBoth.y;
1358
1366
  const isBlockedY = abs(oldPos.x - o.pos.x)*2 < sizeBoth.x;
1367
+ const elasticity = max(this.elasticity, o.elasticity);
1359
1368
 
1360
1369
  if (smallStepUp | isBlockedY | !isBlockedX) // resolve y collision
1361
1370
  {
@@ -1368,7 +1377,7 @@ class EngineObject
1368
1377
  this.groundObject = o;
1369
1378
 
1370
1379
  // bounce if other object is fixed or grounded
1371
- this.velocity.y *= -this.elasticity;
1380
+ this.velocity.y *= -elasticity;
1372
1381
  }
1373
1382
  else if (o.mass)
1374
1383
  {
@@ -1382,7 +1391,6 @@ class EngineObject
1382
1391
  + this.velocity.y * 2 * this.mass / (this.mass + o.mass);
1383
1392
 
1384
1393
  // lerp betwen elastic or inelastic based on elasticity
1385
- const elasticity = max(this.elasticity, o.elasticity);
1386
1394
  this.velocity.y = lerp(elasticity, inelastic, elastic0);
1387
1395
  o.velocity.y = lerp(elasticity, inelastic, elastic1);
1388
1396
  }
@@ -1403,12 +1411,11 @@ class EngineObject
1403
1411
  + this.velocity.x * 2 * this.mass / (this.mass + o.mass);
1404
1412
 
1405
1413
  // lerp betwen elastic or inelastic based on elasticity
1406
- const elasticity = max(this.elasticity, o.elasticity);
1407
1414
  this.velocity.x = lerp(elasticity, inelastic, elastic0);
1408
1415
  o.velocity.x = lerp(elasticity, inelastic, elastic1);
1409
1416
  }
1410
1417
  else // bounce if other object is fixed
1411
- this.velocity.x *= -this.elasticity;
1418
+ this.velocity.x *= -elasticity;
1412
1419
  }
1413
1420
  debugOverlay && debugPhysics && debugAABB(this.pos, this.size, o.pos, o.size, '#f0f');
1414
1421
  }
@@ -1564,23 +1571,23 @@ class EngineObject
1564
1571
  }
1565
1572
  /**
1566
1573
  * LittleJS Drawing System
1567
- * <br> - Hybrid with both Canvas2D and WebGL available
1568
- * <br> - Super fast tile sheet rendering with WebGL
1569
- * <br> - Can apply rotation, mirror, color and additive color
1570
- * <br> - Many useful utility functions
1571
- * <br>
1572
- * <br>LittleJS uses a hybrid rendering solution with the best of both Canvas2D and WebGL.
1573
- * <br>There are 3 canvas/contexts available to draw to...
1574
- * <br> - mainCanvas - 2D background canvas, non WebGL stuff like tile layers are drawn here.
1575
- * <br> - glCanvas - Used by the accelerated WebGL batch rendering system.
1576
- * <br> - overlayCanvas - Another 2D canvas that appears on top of the other 2 canvases.
1577
- * <br>
1578
- * <br>The WebGL rendering system is very fast with some caveats...
1579
- * <br> - The default setup supports only 1 tile sheet, to support more call glCreateTexture and glSetTexture
1580
- * <br> - Switching blend modes (additive) or textures causes another draw call which is expensive in excess
1581
- * <br> - Group additive rendering together using renderOrder to mitigate this issue
1582
- * <br>
1583
- * <br>The LittleJS rendering solution is intentionally simple, feel free to adjust it for your needs!
1574
+ * - Hybrid with both Canvas2D and WebGL available
1575
+ * - Super fast tile sheet rendering with WebGL
1576
+ * - Can apply rotation, mirror, color and additive color
1577
+ * - Many useful utility functions
1578
+ *
1579
+ * LittleJS uses a hybrid rendering solution with the best of both Canvas2D and WebGL.
1580
+ * There are 3 canvas/contexts available to draw to...
1581
+ * mainCanvas - 2D background canvas, non WebGL stuff like tile layers are drawn here.
1582
+ * glCanvas - Used by the accelerated WebGL batch rendering system.
1583
+ * overlayCanvas - Another 2D canvas that appears on top of the other 2 canvases.
1584
+ *
1585
+ * The WebGL rendering system is very fast with some caveats...
1586
+ * - The default setup supports only 1 tile sheet, to support more call glCreateTexture and glSetTexture
1587
+ * - Switching blend modes (additive) or textures causes another draw call which is expensive in excess
1588
+ * - Group additive rendering together using renderOrder to mitigate this issue
1589
+ *
1590
+ * The LittleJS rendering solution is intentionally simple, feel free to adjust it for your needs!
1584
1591
  * @namespace Draw
1585
1592
  */
1586
1593
 
@@ -1624,7 +1631,7 @@ let tileImageSize, tileImageFixBleed, drawCount;
1624
1631
  * @param {Vector2} screenPos
1625
1632
  * @return {Vector2}
1626
1633
  * @memberof Draw */
1627
- const screenToWorld = (screenPos)=>
1634
+ function screenToWorld(screenPos)
1628
1635
  {
1629
1636
  ASSERT(mainCanvasSize.x && mainCanvasSize.y, 'mainCanvasSize is invalid');
1630
1637
  return screenPos.add(vec2(.5)).subtract(mainCanvasSize.scale(.5)).multiply(vec2(1/cameraScale,-1/cameraScale)).add(cameraPos);
@@ -1635,7 +1642,7 @@ const screenToWorld = (screenPos)=>
1635
1642
  * @param {Vector2} worldPos
1636
1643
  * @return {Vector2}
1637
1644
  * @memberof Draw */
1638
- const worldToScreen = (worldPos)=>
1645
+ function worldToScreen(worldPos)
1639
1646
  {
1640
1647
  ASSERT(mainCanvasSize.x && mainCanvasSize.y, 'mainCanvasSize is invalid');
1641
1648
  return worldPos.subtract(cameraPos).multiply(vec2(cameraScale,-cameraScale)).add(mainCanvasSize.scale(.5)).subtract(vec2(.5));
@@ -1790,6 +1797,23 @@ function setBlendMode(additive, useWebGL=glEnable)
1790
1797
  mainContext.globalCompositeOperation = additive ? 'lighter' : 'source-over';
1791
1798
  }
1792
1799
 
1800
+ /** Draw text on overlay canvas in world space
1801
+ * Automatically splits new lines into rows
1802
+ * @param {String} text
1803
+ * @param {Vector2} pos
1804
+ * @param {Number} [size=1]
1805
+ * @param {Color} [color=Color()]
1806
+ * @param {Number} [lineWidth=0]
1807
+ * @param {Color} [lineColor=Color(0,0,0)]
1808
+ * @param {String} [textAlign='center']
1809
+ * @param {String} [font=fontDefault]
1810
+ * @param {CanvasRenderingContext2D} [context=overlayContext]
1811
+ * @memberof Draw */
1812
+ function drawText(text, pos, size=1, color, lineWidth=0, lineColor, textAlign, font, context)
1813
+ {
1814
+ drawTextScreen(text, worldToScreen(pos), size*cameraScale, color, lineWidth*cameraScale, lineColor, textAlign, font, context);
1815
+ }
1816
+
1793
1817
  /** Draw text on overlay canvas in screen space
1794
1818
  * Automatically splits new lines into rows
1795
1819
  * @param {String} text
@@ -1799,6 +1823,8 @@ function setBlendMode(additive, useWebGL=glEnable)
1799
1823
  * @param {Number} [lineWidth=0]
1800
1824
  * @param {Color} [lineColor=Color(0,0,0)]
1801
1825
  * @param {String} [textAlign='center']
1826
+ * @param {String} [font=fontDefault]
1827
+ * @param {CanvasRenderingContext2D} [context=overlayContext]
1802
1828
  * @memberof Draw */
1803
1829
  function drawTextScreen(text, pos, size=1, color=new Color, lineWidth=0, lineColor=new Color(0,0,0), textAlign='center', font=fontDefault, context=overlayContext)
1804
1830
  {
@@ -1819,30 +1845,15 @@ function drawTextScreen(text, pos, size=1, color=new Color, lineWidth=0, lineCol
1819
1845
  });
1820
1846
  }
1821
1847
 
1822
- /** Draw text on overlay canvas in world space
1823
- * Automatically splits new lines into rows
1824
- * @param {String} text
1825
- * @param {Vector2} pos
1826
- * @param {Number} [size=1]
1827
- * @param {Color} [color=Color()]
1828
- * @param {Number} [lineWidth=0]
1829
- * @param {Color} [lineColor=Color(0,0,0)]
1830
- * @param {String} [textAlign='center']
1831
- * @memberof Draw */
1832
- function drawText(text, pos, size=1, color, lineWidth=0, lineColor, textAlign, font)
1833
- {
1834
- drawTextScreen(text, worldToScreen(pos), size*cameraScale, color, lineWidth*cameraScale, lineColor, textAlign, font, mainContext);
1835
- }
1836
-
1837
1848
  ///////////////////////////////////////////////////////////////////////////////
1838
1849
 
1839
1850
  let engineFontImage;
1840
1851
 
1841
1852
  /**
1842
1853
  * Font Image Object - Draw text on a 2D canvas by using characters in an image
1843
- * <br> - 96 characters (from space to tilde) are stored in an image
1844
- * <br> - Uses a default 8x8 font if none is supplied
1845
- * <br> - You can also use fonts from the main tile sheet
1854
+ * - 96 characters (from space to tilde) are stored in an image
1855
+ * - Uses a default 8x8 font if none is supplied
1856
+ * - You can also use fonts from the main tile sheet
1846
1857
  * @example
1847
1858
  * // use built in font
1848
1859
  * const font = new ImageFont;
@@ -1882,7 +1893,7 @@ class FontImage
1882
1893
  {
1883
1894
  const context = this.context;
1884
1895
  context.save();
1885
- context.imageSmoothingEnabled = !cavasPixelated;
1896
+ context.imageSmoothingEnabled = !canvasPixelated;
1886
1897
 
1887
1898
  const size = this.tileSize;
1888
1899
  const drawSize = size.add(this.paddingSize).scale(scale);
@@ -1928,7 +1939,7 @@ class FontImage
1928
1939
  /** Returns true if fullscreen mode is active
1929
1940
  * @return {Boolean}
1930
1941
  * @memberof Draw */
1931
- const isFullscreen = ()=> document.fullscreenElement;
1942
+ function isFullscreen() { return document.fullscreenElement; }
1932
1943
 
1933
1944
  /** Toggle fullsceen mode
1934
1945
  * @memberof Draw */
@@ -1945,10 +1956,10 @@ function toggleFullscreen()
1945
1956
 
1946
1957
  /**
1947
1958
  * LittleJS Input System
1948
- * <br> - Tracks key down, pressed, and released
1949
- * <br> - Also tracks mouse buttons, position, and wheel
1950
- * <br> - Supports multiple gamepads
1951
- * <br> - Virtual gamepad for touch devices with touchGamepadSize
1959
+ * - Tracks key down, pressed, and released
1960
+ * - Also tracks mouse buttons, position, and wheel
1961
+ * - Supports multiple gamepads
1962
+ * - Virtual gamepad for touch devices with touchGamepadSize
1952
1963
  * @namespace Input
1953
1964
  */
1954
1965
 
@@ -1959,25 +1970,28 @@ function toggleFullscreen()
1959
1970
  * @param {Number} [device=0]
1960
1971
  * @return {Boolean}
1961
1972
  * @memberof Input */
1962
- const keyIsDown = (key, device=0)=> inputData[device] && inputData[device][key] & 1;
1973
+ function keyIsDown(key, device=0)
1974
+ { return inputData[device] && inputData[device][key] & 1; }
1963
1975
 
1964
1976
  /** Returns true if device key was pressed this frame
1965
1977
  * @param {Number} key
1966
1978
  * @param {Number} [device=0]
1967
1979
  * @return {Boolean}
1968
1980
  * @memberof Input */
1969
- const keyWasPressed = (key, device=0)=> inputData[device] && inputData[device][key] & 2 ? 1 : 0;
1981
+ function keyWasPressed(key, device=0)
1982
+ { return inputData[device] && inputData[device][key] & 2 ? 1 : 0; }
1970
1983
 
1971
1984
  /** Returns true if device key was released this frame
1972
1985
  * @param {Number} key
1973
1986
  * @param {Number} [device=0]
1974
1987
  * @return {Boolean}
1975
1988
  * @memberof Input */
1976
- const keyWasReleased = (key, device=0)=> inputData[device] && inputData[device][key] & 4 ? 1 : 0;
1989
+ function keyWasReleased(key, device=0)
1990
+ { return inputData[device] && inputData[device][key] & 4 ? 1 : 0; }
1977
1991
 
1978
1992
  /** Clears all input
1979
1993
  * @memberof Input */
1980
- const clearInput = ()=> inputData = [[]];
1994
+ function clearInput() { inputData = [[]]; }
1981
1995
 
1982
1996
  /** Returns true if mouse button is down
1983
1997
  * @function
@@ -2030,28 +2044,32 @@ let preventDefaultInput = 0;
2030
2044
  * @param {Number} [gamepad=0]
2031
2045
  * @return {Boolean}
2032
2046
  * @memberof Input */
2033
- const gamepadIsDown = (button, gamepad=0)=> keyIsDown(button, gamepad+1);
2047
+ function gamepadIsDown(button, gamepad=0)
2048
+ { return keyIsDown(button, gamepad+1); }
2034
2049
 
2035
2050
  /** Returns true if gamepad button was pressed
2036
2051
  * @param {Number} button
2037
2052
  * @param {Number} [gamepad=0]
2038
2053
  * @return {Boolean}
2039
2054
  * @memberof Input */
2040
- const gamepadWasPressed = (button, gamepad=0)=> keyWasPressed(button, gamepad+1);
2055
+ function gamepadWasPressed(button, gamepad=0)
2056
+ { return keyWasPressed(button, gamepad+1); }
2041
2057
 
2042
2058
  /** Returns true if gamepad button was released
2043
2059
  * @param {Number} button
2044
2060
  * @param {Number} [gamepad=0]
2045
2061
  * @return {Boolean}
2046
2062
  * @memberof Input */
2047
- const gamepadWasReleased = (button, gamepad=0)=> keyWasReleased(button, gamepad+1);
2063
+ function gamepadWasReleased(button, gamepad=0)
2064
+ { return keyWasReleased(button, gamepad+1); }
2048
2065
 
2049
2066
  /** Returns gamepad stick value
2050
2067
  * @param {Number} stick
2051
2068
  * @param {Number} [gamepad=0]
2052
2069
  * @return {Vector2}
2053
2070
  * @memberof Input */
2054
- const gamepadStick = (stick, gamepad=0)=> stickData[gamepad] ? stickData[gamepad][stick] || vec2() : vec2();
2071
+ function gamepadStick(stick, gamepad=0)
2072
+ { return stickData[gamepad] ? stickData[gamepad][stick] || vec2() : vec2(); }
2055
2073
 
2056
2074
  ///////////////////////////////////////////////////////////////////////////////
2057
2075
  // Input update called by engine
@@ -2090,12 +2108,18 @@ onkeydown = (e)=>
2090
2108
  e.repeat || (inputData[isUsingGamepad = 0][remapKey(e.which)] = 3);
2091
2109
  preventDefaultInput && e.preventDefault();
2092
2110
  }
2111
+
2093
2112
  onkeyup = (e)=>
2094
2113
  {
2095
2114
  if (debug && e.target != document.body) return;
2096
2115
  inputData[0][remapKey(e.which)] = 4;
2097
2116
  }
2098
- const remapKey = (c)=> inputWASDEmulateDirection ? c==87?38 : c==83?40 : c==65?37 : c==68?39 : c : c;
2117
+
2118
+ function remapKey(c)
2119
+ {
2120
+ return inputWASDEmulateDirection ?
2121
+ c==87?38 : c==83?40 : c==65?37 : c==68?39 : c : c;
2122
+ }
2099
2123
 
2100
2124
  ///////////////////////////////////////////////////////////////////////////////
2101
2125
  // Mouse event handlers
@@ -2104,10 +2128,10 @@ onmousedown = (e)=> {inputData[isUsingGamepad = 0][e.button] = 3; onmousemove(e)
2104
2128
  onmouseup = (e)=> inputData[0][e.button] = inputData[0][e.button] & 2 | 4;
2105
2129
  onmousemove = (e)=> mousePosScreen = mouseToScreen(e);
2106
2130
  onwheel = (e)=> e.ctrlKey || (mouseWheel = sign(e.deltaY));
2107
- oncontextmenu = (e)=> !1; // prevent right click menu
2131
+ oncontextmenu = (e)=> false; // prevent right click menu
2108
2132
 
2109
2133
  // convert a mouse or touch event position to screen space
2110
- const mouseToScreen = (mousePos)=>
2134
+ function mouseToScreen(mousePos)
2111
2135
  {
2112
2136
  if (!mainCanvas)
2113
2137
  return vec2(); // fix bug that can occur if user clicks before page loads
@@ -2153,8 +2177,7 @@ function gamepadsUpdate()
2153
2177
  if (gamepad)
2154
2178
  {
2155
2179
  // read clamp dead zone of analog sticks
2156
- const deadZone = .3, deadZoneMax = .8;
2157
- const applyDeadZone = (v)=>
2180
+ const deadZone = .3, deadZoneMax = .8, applyDeadZone = (v)=>
2158
2181
  v > deadZone ? percent( v, deadZone, deadZoneMax) :
2159
2182
  v < -deadZone ? -percent(-v, deadZone, deadZoneMax) : 0;
2160
2183
 
@@ -2187,11 +2210,12 @@ function gamepadsUpdate()
2187
2210
  /** Pulse the vibration hardware if it exists
2188
2211
  * @param {Number} [pattern=100] - a single value in miliseconds or vibration interval array
2189
2212
  * @memberof Input */
2190
- const vibrate = (pattern)=> vibrateEnable && navigator && navigator.vibrate && navigator.vibrate(pattern);
2213
+ function vibrate(pattern)
2214
+ { vibrateEnable && navigator && navigator.vibrate && navigator.vibrate(pattern); }
2191
2215
 
2192
2216
  /** Cancel any ongoing vibration
2193
2217
  * @memberof Input */
2194
- const vibrateStop = ()=> vibrate(0);
2218
+ function vibrateStop() { vibrate(0); }
2195
2219
 
2196
2220
  ///////////////////////////////////////////////////////////////////////////////
2197
2221
  // Touch input
@@ -2237,6 +2261,9 @@ if (isTouchDevice)
2237
2261
  return true;
2238
2262
  }
2239
2263
 
2264
+ // try to create touch game pad
2265
+ touchGamepadEnable && touchGamepadCreate();
2266
+
2240
2267
  return ontouchstart(e);
2241
2268
  }
2242
2269
  }
@@ -2250,76 +2277,69 @@ let touchGamepadTimer = new Timer, touchGamepadButtons, touchGamepadStick;
2250
2277
  // create the touch gamepad, called automatically by the engine
2251
2278
  function touchGamepadCreate()
2252
2279
  {
2253
- if (!touchGamepadEnable || !isTouchDevice)
2254
- return;
2255
-
2256
2280
  // touch input internal variables
2257
2281
  touchGamepadButtons = [];
2258
2282
  touchGamepadStick = vec2();
2259
2283
 
2260
- // setup touch input
2261
- ontouchstart = (e)=>
2284
+ let touchHandler = ontouchstart;
2285
+ ontouchstart = ontouchmove = ontouchend = (e)=>
2262
2286
  {
2263
- // fix mobile audio, force it to play a sound on first touch
2264
- zzfx(0);
2265
-
2266
- ontouchstart = ontouchmove = ontouchend = (e)=>
2287
+ // clear touch gamepad input
2288
+ touchGamepadStick = vec2();
2289
+ touchGamepadButtons = [];
2290
+
2291
+ const touching = e.touches.length;
2292
+ if (touching)
2267
2293
  {
2268
- // clear touch gamepad input
2269
- touchGamepadStick = vec2();
2270
- touchGamepadButtons = [];
2271
-
2272
- const touching = e.touches.length;
2273
- if (touching)
2294
+ touchGamepadTimer.set();
2295
+ if (paused)
2274
2296
  {
2275
- // set that gamepad is active
2276
- isUsingGamepad = 1;
2277
- touchGamepadTimer.set();
2278
-
2279
- if (paused)
2280
- {
2281
- // touch anywhere to press start when paused
2282
- touchGamepadButtons[9] = 1;
2283
- return;
2284
- }
2297
+ // touch anywhere to press start when paused
2298
+ touchGamepadButtons[9] = 1;
2299
+ return;
2285
2300
  }
2301
+ }
2286
2302
 
2287
- // get center of left and right sides
2288
- const stickCenter = vec2(touchGamepadSize, mainCanvasSize.y-touchGamepadSize);
2289
- const buttonCenter = mainCanvasSize.subtract(vec2(touchGamepadSize, touchGamepadSize));
2290
- const startCenter = mainCanvasSize.scale(.5);
2303
+ // get center of left and right sides
2304
+ const stickCenter = vec2(touchGamepadSize, mainCanvasSize.y-touchGamepadSize);
2305
+ const buttonCenter = mainCanvasSize.subtract(vec2(touchGamepadSize, touchGamepadSize));
2306
+ const startCenter = mainCanvasSize.scale(.5);
2291
2307
 
2292
- // check each touch point
2293
- for (const touch of e.touches)
2308
+ // check each touch point
2309
+ for (const touch of e.touches)
2310
+ {
2311
+ const touchPos = mouseToScreen(vec2(touch.clientX, touch.clientY));
2312
+ if (touchPos.distance(stickCenter) < touchGamepadSize)
2294
2313
  {
2295
- const touchPos = mouseToScreen(vec2(touch.clientX, touch.clientY));
2296
- if (touchPos.distance(stickCenter) < touchGamepadSize)
2314
+ // virtual analog stick
2315
+ if (touchGamepadAnalog)
2316
+ touchGamepadStick = touchPos.subtract(stickCenter).scale(2/touchGamepadSize).clampLength();
2317
+ else
2297
2318
  {
2298
- // virtual analog stick
2299
- if (touchGamepadAnalog)
2300
- touchGamepadStick = touchPos.subtract(stickCenter).scale(2/touchGamepadSize).clampLength();
2301
- else
2302
- {
2303
- // 8 way dpad
2304
- const angle = touchPos.subtract(stickCenter).angle();
2305
- touchGamepadStick.setAngle((angle * 4 / PI + 8.5 | 0) * PI / 4);
2306
- }
2307
- }
2308
- else if (touchPos.distance(buttonCenter) < touchGamepadSize)
2309
- {
2310
- // virtual face buttons
2311
- const button = touchPos.subtract(buttonCenter).direction();
2312
- touchGamepadButtons[button] = 1;
2313
- }
2314
- else if (touchPos.distance(startCenter) < touchGamepadSize)
2315
- {
2316
- // virtual start button in center
2317
- touchGamepadButtons[9] = 1;
2319
+ // 8 way dpad
2320
+ const angle = touchPos.subtract(stickCenter).angle();
2321
+ touchGamepadStick.setAngle((angle * 4 / PI + 8.5 | 0) * PI / 4);
2318
2322
  }
2319
2323
  }
2324
+ else if (touchPos.distance(buttonCenter) < touchGamepadSize)
2325
+ {
2326
+ // virtual face buttons
2327
+ const button = touchPos.subtract(buttonCenter).direction();
2328
+ touchGamepadButtons[button] = 1;
2329
+ }
2330
+ else if (touchPos.distance(startCenter) < touchGamepadSize)
2331
+ {
2332
+ // virtual start button in center
2333
+ touchGamepadButtons[9] = 1;
2334
+ }
2320
2335
  }
2321
2336
 
2322
- return ontouchstart(e);
2337
+ // call default touch handler and set to using gamepad
2338
+ touchHandler(e);
2339
+ isUsingGamepad = 1;
2340
+
2341
+ // must return true so the document will get focus
2342
+ return true;
2323
2343
  }
2324
2344
  }
2325
2345
 
@@ -2345,7 +2365,7 @@ function touchGamepadRender()
2345
2365
  overlayContext.beginPath();
2346
2366
 
2347
2367
  const leftCenter = vec2(touchGamepadSize, mainCanvasSize.y-touchGamepadSize);
2348
- if (touchGamepadAnalog)
2368
+ if (touchGamepadAnalog) // draw circle shaped gamepad
2349
2369
  {
2350
2370
  overlayContext.arc(leftCenter.x, leftCenter.y, touchGamepadSize/2, 0, 9);
2351
2371
  overlayContext.fill();
@@ -2380,20 +2400,21 @@ function touchGamepadRender()
2380
2400
  }
2381
2401
  /**
2382
2402
  * LittleJS Audio System
2383
- * <br> - <a href=https://killedbyapixel.github.io/ZzFX/>ZzFX Sound Effects</a>
2384
- * <br> - <a href=https://keithclark.github.io/ZzFXM/>ZzFXM Music</a>
2385
- * <br> - Caches sounds and music for fast playback
2386
- * <br> - Can attenuate and apply stereo panning to sounds
2387
- * <br> - Ability to play mp3, ogg, and wave files
2388
- * <br> - Speech synthesis wrapper functions
2403
+ * - <a href=https://killedbyapixel.github.io/ZzFX/>ZzFX Sound Effects</a> - Sound Effect Generator
2404
+ * - <a href=https://keithclark.github.io/ZzFXM/>ZzFXM Music</a> - Music System
2405
+ * - Caches sounds and music for fast playback
2406
+ * - Can attenuate and apply stereo panning to sounds
2407
+ * - Ability to play mp3, ogg, and wave files
2408
+ * - Speech synthesis wrapper functions
2409
+ * @namespace Audio
2389
2410
  */
2390
2411
 
2391
2412
  'use strict';
2392
2413
 
2393
2414
  /**
2394
2415
  * Sound Object - Stores a zzfx sound for later use and can be played positionally
2395
- * <br>
2396
- * <br><b><a href=https://killedbyapixel.github.io/ZzFX/>Create sounds using the ZzFX Sound Designer.</a></b>
2416
+ *
2417
+ * <a href=https://killedbyapixel.github.io/ZzFX/>Create sounds using the ZzFX Sound Designer.</a>
2397
2418
  * @example
2398
2419
  * // create a sound
2399
2420
  * const sound_example = new Sound([.5,.5]);
@@ -2477,8 +2498,8 @@ class Sound
2477
2498
 
2478
2499
  /**
2479
2500
  * Music Object - Stores a zzfx music track for later use
2480
- * <br>
2481
- * <br><b><a href=https://keithclark.github.io/ZzFXM/>Create music with the ZzFXM tracker.</a></b>
2501
+ *
2502
+ * <a href=https://keithclark.github.io/ZzFXM/>Create music with the ZzFXM tracker.</a>
2482
2503
  * @example
2483
2504
  * // create some music
2484
2505
  * const music_example = new Music(
@@ -2588,14 +2609,15 @@ function speak(text, language='', volume=1, rate=1, pitch=1)
2588
2609
 
2589
2610
  /** Stop all queued speech
2590
2611
  * @memberof Audio */
2591
- const speakStop = ()=> speechSynthesis && speechSynthesis.cancel();
2612
+ function speakStop() {speechSynthesis && speechSynthesis.cancel();}
2592
2613
 
2593
2614
  /** Get frequency of a note on a musical scale
2594
2615
  * @param {Number} semitoneOffset - How many semitones away from the root note
2595
2616
  * @param {Number} [rootNoteFrequency=220] - Frequency at semitone offset 0
2596
2617
  * @return {Number} - The frequency of the note
2597
2618
  * @memberof Audio */
2598
- const getNoteFrequency = (semitoneOffset, rootFrequency=220)=> rootFrequency * 2**(semitoneOffset/12);
2619
+ function getNoteFrequency(semitoneOffset, rootFrequency=220)
2620
+ { return rootFrequency * 2**(semitoneOffset/12); }
2599
2621
 
2600
2622
  ///////////////////////////////////////////////////////////////////////////////
2601
2623
 
@@ -2650,15 +2672,15 @@ function playSamples(sampleChannels, volume=1, rate=1, pan=0, loop=0)
2650
2672
  }
2651
2673
 
2652
2674
  ///////////////////////////////////////////////////////////////////////////////
2653
- // ZzFXMicro - Zuper Zmall Zound Zynth - v1.1.8 by Frank Force
2675
+ // ZzFXMicro - Zuper Zmall Zound Zynth - v1.2.0 by Frank Force
2654
2676
 
2655
2677
  /** Generate and play a ZzFX sound
2656
- * <br>
2657
- * <br><b><a href=https://killedbyapixel.github.io/ZzFX/>Create sounds using the ZzFX Sound Designer.</a></b>
2678
+ *
2679
+ * <a href=https://killedbyapixel.github.io/ZzFX/>Create sounds using the ZzFX Sound Designer.</a>
2658
2680
  * @param {Array} zzfxSound - Array of ZzFX parameters, ex. [.5,.5]
2659
- * @return {Array} - Array of audio samples
2681
+ * @return {AudioBufferSourceNode} - The audio node of the sound played
2660
2682
  * @memberof Audio */
2661
- const zzfx = (...zzfxSound) => playSamples([zzfxG(...zzfxSound)]);
2683
+ function zzfx(...zzfxSound) { return playSamples([zzfxG(...zzfxSound)]); }
2662
2684
 
2663
2685
  /** Sample rate used for all ZzFX sounds
2664
2686
  * @default 44100
@@ -2666,7 +2688,29 @@ const zzfx = (...zzfxSound) => playSamples([zzfxG(...zzfxSound)]);
2666
2688
  const zzfxR = 44100;
2667
2689
 
2668
2690
  /** Generate samples for a ZzFX sound
2669
- * @memberof Audio */
2691
+ * @param {Number} [volume=1] - Volume scale (percent)
2692
+ * @param {Number} [randomness=.05] - How much to randomize frequency (percent Hz)
2693
+ * @param {Number} [frequency=220] - Frequency of sound (Hz)
2694
+ * @param {Number} [attack=0] - Attack time, how fast sound starts (seconds)
2695
+ * @param {Number} [sustain=0] - Sustain time, how long sound holds (seconds)
2696
+ * @param {Number} [release=.1] - Release time, how fast sound fades out (seconds)
2697
+ * @param {Number} [shape=0] - Shape of the sound wave
2698
+ * @param {Number} [shapeCurve=1] - Squarenes of wave (0=square, 1=normal, 2=pointy)
2699
+ * @param {Number} [slide=0] - How much to slide frequency (kHz/s)
2700
+ * @param {Number} [deltaSlide=0] - How much to change slide (kHz/s/s)
2701
+ * @param {Number} [pitchJump=0] - Frequency of pitch jump (Hz)
2702
+ * @param {Number} [pitchJumpTime=0] - Time of pitch jump (seconds)
2703
+ * @param {Number} [repeatTime=0] - Resets some parameters periodically (seconds)
2704
+ * @param {Number} [noise=0] - How much random noise to add (percent)
2705
+ * @param {Number} [modulation=0] - Frequency of modulation wave, negative flips phase (Hz)
2706
+ * @param {Number} [bitCrush=0] - Resamples at a lower frequency in (samples*100)
2707
+ * @param {Number} [delay=0] - Overlap sound with itself for reverb and flanger effects (seconds)
2708
+ * @param {Number} [sustainVolume=1] - Volume level for sustain (percent)
2709
+ * @param {Number} [decay=0] - Decay time, how long to reach sustain after attack (seconds)
2710
+ * @param {Number} [tremolo=0] - Trembling effect, rate controlled by repeat time (precent)
2711
+ * @return {Array} - Array of audio samples
2712
+ * @memberof Audio
2713
+ */
2670
2714
  function zzfxG
2671
2715
  (
2672
2716
  // parameters
@@ -2676,11 +2720,11 @@ function zzfxG
2676
2720
  bitCrush = 0, delay = 0, sustainVolume = 1, decay = 0, tremolo = 0
2677
2721
  )
2678
2722
  {
2679
- // init parameters
2723
+ // locals
2680
2724
  let PI2 = PI*2, startSlide = slide *= 500 * PI2 / zzfxR / zzfxR, b=[],
2681
2725
  startFrequency = frequency *= (1 + randomness*rand(-1,1)) * PI2 / zzfxR,
2682
- t=0, tm=0, i=0, j=1, r=0, c=0, s=0, f, length;
2683
-
2726
+ t=0, tm=0, i=0, j=1, r=0, c=0, s=0, f, length
2727
+
2684
2728
  // scale by sample rate
2685
2729
  attack = attack * zzfxR + 9; // minimum attack to prevent pop
2686
2730
  decay *= zzfxR;
@@ -2730,18 +2774,18 @@ function zzfxG
2730
2774
  Math.cos(modulation*tm++); // modulation
2731
2775
  t += f - f*noise*(1 - (Math.sin(i)+1)*1e9%2); // noise
2732
2776
 
2733
- if (j && ++j > pitchJumpTime) // pitch jump
2777
+ if (j && ++j > pitchJumpTime) // pitch jump
2734
2778
  {
2735
- frequency += pitchJump; // apply pitch jump
2736
- startFrequency += pitchJump; // also apply to start
2737
- j = 0; // reset pitch jump time
2779
+ frequency += pitchJump; // apply pitch jump
2780
+ startFrequency += pitchJump; // also apply to start
2781
+ j = 0; // reset pitch jump time
2738
2782
  }
2739
2783
 
2740
2784
  if (repeatTime && !(++r % repeatTime)) // repeat
2741
2785
  {
2742
- frequency = startFrequency; // reset frequency
2743
- slide = startSlide; // reset slide
2744
- j = j || 1; // reset pitch jump time
2786
+ frequency = startFrequency; // reset frequency
2787
+ slide = startSlide; // reset slide
2788
+ j ||= 1; // reset pitch jump time
2745
2789
  }
2746
2790
  }
2747
2791
 
@@ -2756,7 +2800,7 @@ function zzfxG
2756
2800
  * @param {Array} patterns - Array of pattern data
2757
2801
  * @param {Array} sequence - Array of pattern indexes
2758
2802
  * @param {Number} [BPM=125] - Playback speed of the song in BPM
2759
- * @returns {Array} - Left and right channel sample data
2803
+ * @return {Array} - Left and right channel sample data
2760
2804
  * @memberof Audio */
2761
2805
  function zzfxM(instruments, patterns, sequence, BPM = 125)
2762
2806
  {
@@ -2854,13 +2898,13 @@ function zzfxM(instruments, patterns, sequence, BPM = 125)
2854
2898
  }
2855
2899
  /**
2856
2900
  * LittleJS Tile Layer System
2857
- * <br> - Caches arrays of tiles to off screen canvas for fast rendering
2858
- * <br> - Unlimted numbers of layers, allocates canvases as needed
2859
- * <br> - Interfaces with EngineObject for collision
2860
- * <br> - Collision layer is separate from visible layers
2861
- * <br> - It is recommended to have a visible layer that matches the collision
2862
- * <br> - Tile layers can be drawn to using their context with canvas2d
2863
- * <br> - Drawn directly to the main canvas without using WebGL
2901
+ * - Caches arrays of tiles to off screen canvas for fast rendering
2902
+ * - Unlimted numbers of layers, allocates canvases as needed
2903
+ * - Interfaces with EngineObject for collision
2904
+ * - Collision layer is separate from visible layers
2905
+ * - It is recommended to have a visible layer that matches the collision
2906
+ * - Tile layers can be drawn to using their context with canvas2d
2907
+ * - Drawn directly to the main canvas without using WebGL
2864
2908
  * @namespace TileCollision
2865
2909
  */
2866
2910
 
@@ -2891,15 +2935,19 @@ function initTileCollision(size)
2891
2935
  * @param {Vector2} pos
2892
2936
  * @param {Number} [data=0]
2893
2937
  * @memberof TileCollision */
2894
- const setTileCollisionData = (pos, data=0)=>
2938
+ function setTileCollisionData(pos, data=0)
2939
+ {
2895
2940
  pos.arrayCheck(tileCollisionSize) && (tileCollision[(pos.y|0)*tileCollisionSize.x+pos.x|0] = data);
2941
+ }
2896
2942
 
2897
2943
  /** Get tile collision data
2898
2944
  * @param {Vector2} pos
2899
2945
  * @return {Number}
2900
2946
  * @memberof TileCollision */
2901
- const getTileCollisionData = (pos)=>
2902
- pos.arrayCheck(tileCollisionSize) ? tileCollision[(pos.y|0)*tileCollisionSize.x+pos.x|0] : 0;
2947
+ function getTileCollisionData(pos)
2948
+ {
2949
+ return pos.arrayCheck(tileCollisionSize) ? tileCollision[(pos.y|0)*tileCollisionSize.x+pos.x|0] : 0;
2950
+ }
2903
2951
 
2904
2952
  /** Check if collision with another object should occur
2905
2953
  * @param {Vector2} pos
@@ -2993,10 +3041,10 @@ class TileLayerData
2993
3041
 
2994
3042
  /**
2995
3043
  * Tile layer object - cached rendering system for tile layers
2996
- * <br> - Each Tile layer is rendered to an off screen canvas
2997
- * <br> - To allow dynamic modifications, layers are rendered using canvas 2d
2998
- * <br> - Some devices like mobile phones are limited to 4k texture resolution
2999
- * <br> - So with 16x16 tiles this limits layers to 256x256 on mobile devices
3044
+ * - Each Tile layer is rendered to an off screen canvas
3045
+ * - To allow dynamic modifications, layers are rendered using canvas 2d
3046
+ * - Some devices like mobile phones are limited to 4k texture resolution
3047
+ * - So with 16x16 tiles this limits layers to 256x256 on mobile devices
3000
3048
  * @extends EngineObject
3001
3049
  * @example
3002
3050
  * // create tile collision and visible tile layer
@@ -3196,12 +3244,9 @@ constructor(pos, size=tileCollisionSize, tileSize=tileSizeDefault, scale=vec2(1)
3196
3244
  drawRect(pos, size, color, angle)
3197
3245
  { this.drawTile(pos, size, -1, 0, color, angle); }
3198
3246
  }
3199
- /*
3200
- LittleJS Particle System
3201
- - Spawns particles with randomness from parameters
3202
- - Updates particle physics
3203
- - Fast particle rendering
3204
- */
3247
+ /**
3248
+ * LittleJS Particle System
3249
+ */
3205
3250
 
3206
3251
  'use strict';
3207
3252
 
@@ -3507,9 +3552,9 @@ class Particle extends EngineObject
3507
3552
  }
3508
3553
  /**
3509
3554
  * LittleJS Medal System
3510
- * <br> - Tracks and displays medals
3511
- * <br> - Saves medals to local storage
3512
- * <br> - Newgrounds integration
3555
+ * - Tracks and displays medals
3556
+ * - Saves medals to local storage
3557
+ * - Newgrounds integration
3513
3558
  * @namespace Medals
3514
3559
  */
3515
3560
 
@@ -3526,8 +3571,8 @@ let medalsDisplayQueue = [], medalsSaveName, medalsDisplayTimeLast;
3526
3571
  ///////////////////////////////////////////////////////////////////////////////
3527
3572
 
3528
3573
  /** Initialize medals with a save name used for storage
3529
- * <br> - Call this after creating all medals
3530
- * <br> - Checks if medals are unlocked
3574
+ * - Call this after creating all medals
3575
+ * - Checks if medals are unlocked
3531
3576
  * @param {String} saveName
3532
3577
  * @memberof Medals */
3533
3578
  function medalsInit(saveName)
@@ -3797,20 +3842,43 @@ class Newgrounds
3797
3842
  CryptoJS()
3798
3843
  {
3799
3844
  ///////////////////////////////////////////////////////////////////////////////
3800
- // Crypto-JS - https://github.com/brix/crypto-js [The MIT License (MIT)]
3801
- // Copyright (c) 2009-2013 Jeff Mott Copyright (c) 2013-2016 Evan Vosberg
3802
-
3845
+ // Crypto-JS - https://github.com/brix/crypto-js - MIT License
3846
+ //
3847
+ // [The MIT License (MIT)](http://opensource.org/licenses/MIT)
3848
+ //
3849
+ // Copyright (c) 2009-2013 Jeff Mott
3850
+ // Copyright (c) 2013-2016 Evan Vosberg
3851
+ //
3852
+ // Permission is hereby granted, free of charge, to any person obtaining a copy
3853
+ // of this software and associated documentation files (the "Software"), to deal
3854
+ // in the Software without restriction, including without limitation the rights
3855
+ // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
3856
+ // copies of the Software, and to permit persons to whom the Software is
3857
+ // furnished to do so, subject to the following conditions:
3858
+ //
3859
+ // The above copyright notice and this permission notice shall be included in
3860
+ // all copies or substantial portions of the Software.
3861
+ //
3862
+ // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
3863
+ // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
3864
+ // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
3865
+ // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
3866
+ // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
3867
+ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
3868
+ // THE SOFTWARE.
3803
3869
  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));
3870
+ // end of Crypto-JS
3871
+ ///////////////////////////////////////////////////////////////////////////////
3804
3872
  }
3805
3873
  }
3806
3874
  /**
3807
3875
  * LittleJS WebGL Interface
3808
- * <br> - All webgl used by the engine is wrapped up here
3809
- * <br> - For normal stuff you won't need to see or call anything in this file
3810
- * <br> - For advanced stuff there are helper functions to create shaders, textures, etc
3811
- * <br> - Can be disabled with glEnable to revert to 2D canvas rendering
3812
- * <br> - Batches sprite rendering on GPU for incredibly fast performance
3813
- * <br> - Sprite transform math is done in the shader where possible
3876
+ * - All webgl used by the engine is wrapped up here
3877
+ * - For normal stuff you won't need to see or call anything in this file
3878
+ * - For advanced stuff there are helper functions to create shaders, textures, etc
3879
+ * - Can be disabled with glEnable to revert to 2D canvas rendering
3880
+ * - Batches sprite rendering on GPU for incredibly fast performance
3881
+ * - Sprite transform math is done in the shader where possible
3814
3882
  * @namespace WebGL
3815
3883
  */
3816
3884
 
@@ -3885,7 +3953,7 @@ function glSetBlendMode(additive)
3885
3953
  }
3886
3954
 
3887
3955
  /** Set the WebGl texture, not normally necessary unless multiple tile sheets are used
3888
- * <br> - This may also flush the gl buffer resulting in more draw calls and worse performance
3956
+ * - This may also flush the gl buffer resulting in more draw calls and worse performance
3889
3957
  * @param {WebGLTexture} [texture=glTileTexture]
3890
3958
  * @memberof WebGL */
3891
3959
  function glSetTexture(texture=glTileTexture)
@@ -3946,7 +4014,7 @@ function glCreateTexture(image)
3946
4014
  image && image.width && glContext.texImage2D(gl_TEXTURE_2D, 0, gl_RGBA, gl_RGBA, gl_UNSIGNED_BYTE, image);
3947
4015
 
3948
4016
  // use point filtering for pixelated rendering
3949
- const filter = cavasPixelated ? gl_NEAREST : gl_LINEAR;
4017
+ const filter = canvasPixelated ? gl_NEAREST : gl_LINEAR;
3950
4018
  glContext.texParameteri(gl_TEXTURE_2D, gl_TEXTURE_MIN_FILTER, filter);
3951
4019
  glContext.texParameteri(gl_TEXTURE_2D, gl_TEXTURE_MAG_FILTER, filter);
3952
4020
  glContext.texParameteri(gl_TEXTURE_2D, gl_TEXTURE_WRAP_S, gl_CLAMP_TO_EDGE);