littlejsengine 1.6.4 → 1.6.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,10 +1,9 @@
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
 
@@ -20,24 +19,24 @@ const debugGamepads = 0;
20
19
  const debugMedals = 0;
21
20
 
22
21
  // debug commands are automatically removed from the final build
23
- const ASSERT = ()=> {}
24
- const debugInit = ()=> {}
25
- const debugUpdate = ()=> {}
26
- const debugRender = ()=> {}
27
- const debugRect = ()=> {}
28
- const debugCircle = ()=> {}
29
- const debugPoint = ()=> {}
30
- const debugLine = ()=> {}
31
- const debugAABB = ()=> {}
32
- const debugText = ()=> {}
33
- const debugClear = ()=> {}
34
- 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 (){}
35
34
  /**
36
35
  * LittleJS Utility Classes and Functions
37
- * <br> - General purpose math library
38
- * <br> - Vector2 - fast, simple, easy 2D vector class
39
- * <br> - Color - holds a rgba color with some math functions
40
- * <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
41
40
  * @namespace Utilities
42
41
  */
43
42
 
@@ -53,34 +52,34 @@ const PI = Math.PI;
53
52
  * @param {Number} value
54
53
  * @return {Number}
55
54
  * @memberof Utilities */
56
- const abs = (a)=> a < 0 ? -a : a;
55
+ function abs(a) { return a < 0 ? -a : a; }
57
56
 
58
57
  /** Returns lowest of two values passed in
59
58
  * @param {Number} valueA
60
59
  * @param {Number} valueB
61
60
  * @return {Number}
62
61
  * @memberof Utilities */
63
- const min = (a, b)=> a < b ? a : b;
62
+ function min(a, b) { return a < b ? a : b; }
64
63
 
65
64
  /** Returns highest of two values passed in
66
65
  * @param {Number} valueA
67
66
  * @param {Number} valueB
68
67
  * @return {Number}
69
68
  * @memberof Utilities */
70
- const max = (a, b)=> a > b ? a : b;
69
+ function max(a, b) { return a > b ? a : b; }
71
70
 
72
71
  /** Returns the sign of value passed in (also returns 1 if 0)
73
72
  * @param {Number} value
74
73
  * @return {Number}
75
74
  * @memberof Utilities */
76
- const sign = (a)=> a < 0 ? -1 : 1;
75
+ function sign(a) { return a < 0 ? -1 : 1; }
77
76
 
78
77
  /** Returns first parm modulo the second param, but adjusted so negative numbers work as expected
79
78
  * @param {Number} dividend
80
79
  * @param {Number} [divisor=1]
81
80
  * @return {Number}
82
81
  * @memberof Utilities */
83
- const mod = (a, b=1)=> ((a % b) + b) % b;
82
+ function mod(a, b=1) { return ((a % b) + b) % b; }
84
83
 
85
84
  /** Clamps the value beween max and min
86
85
  * @param {Number} value
@@ -88,7 +87,8 @@ const mod = (a, b=1)=> ((a % b) + b) % b;
88
87
  * @param {Number} [max=1]
89
88
  * @return {Number}
90
89
  * @memberof Utilities */
91
- 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; }
92
92
 
93
93
  /** Returns what percentage the value is between max and min
94
94
  * @param {Number} value
@@ -96,7 +96,8 @@ const clamp = (v, min=0, max=1)=> v < min ? min : v > max ? max : v;
96
96
  * @param {Number} [max=1]
97
97
  * @return {Number}
98
98
  * @memberof Utilities */
99
- 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; }
100
101
 
101
102
  /** Linearly interpolates the percent value between max and min
102
103
  * @param {Number} percent
@@ -104,19 +105,19 @@ const percent = (v, min=0, max=1)=> max-min ? clamp((v-min) / (max-min)) : 0;
104
105
  * @param {Number} [max=1]
105
106
  * @return {Number}
106
107
  * @memberof Utilities */
107
- 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); }
108
109
 
109
110
  /** Applies smoothstep function to the percentage value
110
111
  * @param {Number} value
111
112
  * @return {Number}
112
113
  * @memberof Utilities */
113
- const smoothStep = (p)=> p * p * (3 - 2 * p);
114
+ function smoothStep(p) { return p * p * (3 - 2 * p); }
114
115
 
115
116
  /** Returns the nearest power of two not less then the value
116
117
  * @param {Number} value
117
118
  * @return {Number}
118
119
  * @memberof Utilities */
119
- const nearestPowerOfTwo = (v)=> 2**Math.ceil(Math.log2(v));
120
+ function nearestPowerOfTwo(v) { return 2**Math.ceil(Math.log2(v)); }
120
121
 
121
122
  /** Returns true if two axis aligned bounding boxes are overlapping
122
123
  * @param {Vector2} pointA - Center of box A
@@ -125,7 +126,8 @@ const nearestPowerOfTwo = (v)=> 2**Math.ceil(Math.log2(v));
125
126
  * @param {Vector2} [sizeB] - Size of box B
126
127
  * @return {Boolean} - True if overlapping
127
128
  * @memberof Utilities */
128
- 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; }
129
131
 
130
132
  /** Returns an oscillating wave between 0 and amplitude with frequency of 1 Hz by default
131
133
  * @param {Number} [frequency=1] - Frequency of the wave in Hz
@@ -133,13 +135,14 @@ const isOverlapping = (pA, sA, pB, sB)=> abs(pA.x - pB.x)*2 < sA.x + sB.x && abs
133
135
  * @param {Number} [t=time] - Value to use for time of the wave
134
136
  * @return {Number} - Value waving between 0 and amplitude
135
137
  * @memberof Utilities */
136
- 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)); }
137
140
 
138
141
  /** Formats seconds to mm:ss style for display purposes
139
142
  * @param {Number} t - time in seconds
140
143
  * @return {String}
141
144
  * @memberof Utilities */
142
- 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); }
143
146
 
144
147
  ///////////////////////////////////////////////////////////////////////////////
145
148
 
@@ -151,32 +154,33 @@ const formatTime = (t)=> (t/60|0) + ':' + (t%60<10?'0':'') + (t%60|0);
151
154
  * @param {Number} [valueB=0]
152
155
  * @return {Number}
153
156
  * @memberof Random */
154
- const rand = (a=1, b=0)=> b + (a-b)*Math.random();
157
+ function rand(a=1, b=0) { return b + (a-b)*Math.random(); }
155
158
 
156
159
  /** Returns a floored random value the two values passed in
157
160
  * @param {Number} [valueA=1]
158
161
  * @param {Number} [valueB=0]
159
162
  * @return {Number}
160
163
  * @memberof Random */
161
- const randInt = (a=1, b=0)=> rand(a,b)|0;
164
+ function randInt(a=1, b=0) { return rand(a,b)|0; }
162
165
 
163
166
  /** Randomly returns either -1 or 1
164
167
  * @return {Number}
165
168
  * @memberof Random */
166
- const randSign = ()=> randInt(2) * 2 - 1;
169
+ function randSign() { return randInt(2) * 2 - 1; }
167
170
 
168
171
  /** Returns a random Vector2 within a circular shape
169
172
  * @param {Number} [radius=1]
170
173
  * @param {Number} [minRadius=0]
171
174
  * @return {Vector2}
172
175
  * @memberof Random */
173
- 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; }
174
178
 
175
179
  /** Returns a random Vector2 with the passed in length
176
180
  * @param {Number} [length=1]
177
181
  * @return {Vector2}
178
182
  * @memberof Random */
179
- const randVector = (length=1)=> new Vector2().setAngle(rand(2*PI), length);
183
+ function randVector(length=1) { return new Vector2().setAngle(rand(2*PI), length); }
180
184
 
181
185
  /** Returns a random color between the two passed in colors, combine components if linear
182
186
  * @param {Color} [colorA=Color()]
@@ -184,8 +188,8 @@ const randVector = (length=1)=> new Vector2().setAngle(rand(2*PI), length);
184
188
  * @param {Boolean} [linear]
185
189
  * @return {Color}
186
190
  * @memberof Random */
187
- const randColor = (cA = new Color, cB = new Color(0,0,0,1), linear)=>
188
- 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)); }
189
193
 
190
194
  /** Seed used by the randSeeded function
191
195
  * @type {Number}
@@ -196,16 +200,19 @@ let randSeed = 1;
196
200
  /** Set seed used by the randSeeded function, should not be 0
197
201
  * @param {Number} seed
198
202
  * @memberof Random */
199
- const setRandSeed = (seed)=> randSeed = seed;
203
+ function setRandSeed(seed) { randSeed = seed; }
200
204
 
201
205
  /** Returns a seeded random value between the two values passed in using randSeed
202
206
  * @param {Number} [valueA=1]
203
207
  * @param {Number} [valueB=0]
204
208
  * @return {Number}
205
209
  * @memberof Random */
206
- const randSeeded = (a=1, b=0)=>
210
+ function randSeeded(a=1, b=0)
207
211
  {
208
- 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;
209
216
  return b + (a-b) * abs(randSeed % 1e9) / 1e9;
210
217
  }
211
218
 
@@ -223,7 +230,8 @@ const randSeeded = (a=1, b=0)=>
223
230
  * b = vec2(); // set b to (0, 0)
224
231
  * @memberof Utilities
225
232
  */
226
- 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); }
227
235
 
228
236
  /**
229
237
  * Check if object is a valid Vector2
@@ -231,11 +239,11 @@ const vec2 = (x=0, y)=> x.x == undefined ? new Vector2(x, y == undefined? x : y)
231
239
  * @return {Boolean}
232
240
  * @memberof Utilities
233
241
  */
234
- const isVector2 = (v)=> !isNaN(v.x) && !isNaN(v.y);
242
+ function isVector2(v) { return !isNaN(v.x) && !isNaN(v.y); }
235
243
 
236
244
  /**
237
245
  * 2D Vector object with vector math library
238
- * <br> - Functions do not change this so they can be chained together
246
+ * - Functions do not change this so they can be chained together
239
247
  * @example
240
248
  * let a = new Vector2(2, 3); // vector with coordinates (2, 3)
241
249
  * let b = new Vector2; // vector with coordinates (0, 0)
@@ -382,7 +390,7 @@ class Vector2
382
390
  * @return {Color}
383
391
  * @memberof Utilities
384
392
  */
385
- const rgb = (r, g, b, a)=> new Color(r, g, b, a);
393
+ function rgb(r, g, b, a) { return new Color(r, g, b, a); }
386
394
 
387
395
  /**
388
396
  * Create a color object with HSLA values
@@ -393,7 +401,7 @@ const rgb = (r, g, b, a)=> new Color(r, g, b, a);
393
401
  * @return {Color}
394
402
  * @memberof Utilities
395
403
  */
396
- const hsl = (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); }
397
405
 
398
406
  /**
399
407
  * Color object (red, green, blue, alpha) with some helpful functions
@@ -652,11 +660,11 @@ let canvasMaxSize = vec2(1920, 1200);
652
660
  * @memberof Settings */
653
661
  let canvasFixedSize = vec2();
654
662
 
655
- /** Disables anti aliasing for pixel art if true
663
+ /** Disables filtering for crisper pixel art if true
656
664
  * @type {Boolean}
657
665
  * @default
658
666
  * @memberof Settings */
659
- let cavasPixelated = 1;
667
+ let canvasPixelated = 1;
660
668
 
661
669
  /** Default font used for text rendering
662
670
  * @type {String}
@@ -773,8 +781,8 @@ let gamepadDirectionEmulateStick = 1;
773
781
  let inputWASDEmulateDirection = 1;
774
782
 
775
783
  /** True if touch gamepad should appear on mobile devices
776
- * <br> - Supports left analog stick, 4 face buttons and start button (button 9)
777
- * <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
778
786
  * @type {Boolean}
779
787
  * @default 0
780
788
  * @memberof Settings */
@@ -863,27 +871,23 @@ let medalDisplayIconSize = 50;
863
871
  * @default 0
864
872
  * @memberof Settings */
865
873
  let medalsPreventUnlock;
866
- /*
867
- LittleJS - The Tiny JavaScript Game Engine That Can!
868
- MIT License - Copyright 2021 Frank Force
869
-
870
- Engine Features
871
- - Object oriented system with base class engine object
872
- - Base class object handles update, physics, collision, rendering, etc
873
- - Engine helper classes and functions like Vector2, Color, and Timer
874
- - Super fast rendering system for tile sheets
875
- - Sound effects audio with zzfx and music with zzfxm
876
- - Input processing system with gamepad and touchscreen support
877
- - Tile layer rendering and collision system
878
- - Particle effect system
879
- - Medal system tracks and displays achievements
880
- - Debug tools and debug rendering system
881
- - Post processing effects
882
- - Call engineInit() to start it up!
883
- */
884
-
885
- /**
886
- * 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!
887
891
  * @namespace Engine
888
892
  */
889
893
 
@@ -899,7 +903,7 @@ const engineName = 'LittleJS';
899
903
  * @type {String}
900
904
  * @default
901
905
  * @memberof Engine */
902
- const engineVersion = '1.6.4';
906
+ const engineVersion = '1.6.6';
903
907
 
904
908
  /** Frames per second to update objects
905
909
  * @type {Number}
@@ -969,10 +973,11 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
969
973
  debug && (tileImage.onload=()=>ASSERT(1)); // tile sheet can not reloaded
970
974
 
971
975
  // setup html
972
- const styleBody = 'margin:0;overflow:hidden;background:#000' + // fill the window
973
- ';touch-action:none' + // prevent mobile pinch to resize
974
- ';user-select:none' + // prevent mobile hold to select
975
- ';-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
976
981
  document.body.style = styleBody;
977
982
  document.body.appendChild(mainCanvas = document.createElement('canvas'));
978
983
  mainContext = mainCanvas.getContext('2d');
@@ -985,8 +990,10 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
985
990
  document.body.appendChild(overlayCanvas = document.createElement('canvas'));
986
991
  overlayContext = overlayCanvas.getContext('2d');
987
992
 
988
- // set canvas style to fill the window
989
- 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
990
997
  (glCanvas||mainCanvas).style = mainCanvas.style = overlayCanvas.style = styleCanvas;
991
998
 
992
999
  gameInit();
@@ -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);
@@ -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));
@@ -1844,9 +1851,9 @@ let engineFontImage;
1844
1851
 
1845
1852
  /**
1846
1853
  * Font Image Object - Draw text on a 2D canvas by using characters in an image
1847
- * <br> - 96 characters (from space to tilde) are stored in an image
1848
- * <br> - Uses a default 8x8 font if none is supplied
1849
- * <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
1850
1857
  * @example
1851
1858
  * // use built in font
1852
1859
  * const font = new ImageFont;
@@ -1886,7 +1893,7 @@ class FontImage
1886
1893
  {
1887
1894
  const context = this.context;
1888
1895
  context.save();
1889
- context.imageSmoothingEnabled = !cavasPixelated;
1896
+ context.imageSmoothingEnabled = !canvasPixelated;
1890
1897
 
1891
1898
  const size = this.tileSize;
1892
1899
  const drawSize = size.add(this.paddingSize).scale(scale);
@@ -1932,7 +1939,7 @@ class FontImage
1932
1939
  /** Returns true if fullscreen mode is active
1933
1940
  * @return {Boolean}
1934
1941
  * @memberof Draw */
1935
- const isFullscreen = ()=> document.fullscreenElement;
1942
+ function isFullscreen() { return document.fullscreenElement; }
1936
1943
 
1937
1944
  /** Toggle fullsceen mode
1938
1945
  * @memberof Draw */
@@ -1949,10 +1956,10 @@ function toggleFullscreen()
1949
1956
 
1950
1957
  /**
1951
1958
  * LittleJS Input System
1952
- * <br> - Tracks key down, pressed, and released
1953
- * <br> - Also tracks mouse buttons, position, and wheel
1954
- * <br> - Supports multiple gamepads
1955
- * <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
1956
1963
  * @namespace Input
1957
1964
  */
1958
1965
 
@@ -1963,25 +1970,28 @@ function toggleFullscreen()
1963
1970
  * @param {Number} [device=0]
1964
1971
  * @return {Boolean}
1965
1972
  * @memberof Input */
1966
- 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; }
1967
1975
 
1968
1976
  /** Returns true if device key was pressed this frame
1969
1977
  * @param {Number} key
1970
1978
  * @param {Number} [device=0]
1971
1979
  * @return {Boolean}
1972
1980
  * @memberof Input */
1973
- 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; }
1974
1983
 
1975
1984
  /** Returns true if device key was released this frame
1976
1985
  * @param {Number} key
1977
1986
  * @param {Number} [device=0]
1978
1987
  * @return {Boolean}
1979
1988
  * @memberof Input */
1980
- 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; }
1981
1991
 
1982
1992
  /** Clears all input
1983
1993
  * @memberof Input */
1984
- const clearInput = ()=> inputData = [[]];
1994
+ function clearInput() { inputData = [[]]; }
1985
1995
 
1986
1996
  /** Returns true if mouse button is down
1987
1997
  * @function
@@ -2034,28 +2044,32 @@ let preventDefaultInput = 0;
2034
2044
  * @param {Number} [gamepad=0]
2035
2045
  * @return {Boolean}
2036
2046
  * @memberof Input */
2037
- const gamepadIsDown = (button, gamepad=0)=> keyIsDown(button, gamepad+1);
2047
+ function gamepadIsDown(button, gamepad=0)
2048
+ { return keyIsDown(button, gamepad+1); }
2038
2049
 
2039
2050
  /** Returns true if gamepad button was pressed
2040
2051
  * @param {Number} button
2041
2052
  * @param {Number} [gamepad=0]
2042
2053
  * @return {Boolean}
2043
2054
  * @memberof Input */
2044
- const gamepadWasPressed = (button, gamepad=0)=> keyWasPressed(button, gamepad+1);
2055
+ function gamepadWasPressed(button, gamepad=0)
2056
+ { return keyWasPressed(button, gamepad+1); }
2045
2057
 
2046
2058
  /** Returns true if gamepad button was released
2047
2059
  * @param {Number} button
2048
2060
  * @param {Number} [gamepad=0]
2049
2061
  * @return {Boolean}
2050
2062
  * @memberof Input */
2051
- const gamepadWasReleased = (button, gamepad=0)=> keyWasReleased(button, gamepad+1);
2063
+ function gamepadWasReleased(button, gamepad=0)
2064
+ { return keyWasReleased(button, gamepad+1); }
2052
2065
 
2053
2066
  /** Returns gamepad stick value
2054
2067
  * @param {Number} stick
2055
2068
  * @param {Number} [gamepad=0]
2056
2069
  * @return {Vector2}
2057
2070
  * @memberof Input */
2058
- 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(); }
2059
2073
 
2060
2074
  ///////////////////////////////////////////////////////////////////////////////
2061
2075
  // Input update called by engine
@@ -2094,12 +2108,18 @@ onkeydown = (e)=>
2094
2108
  e.repeat || (inputData[isUsingGamepad = 0][remapKey(e.which)] = 3);
2095
2109
  preventDefaultInput && e.preventDefault();
2096
2110
  }
2111
+
2097
2112
  onkeyup = (e)=>
2098
2113
  {
2099
2114
  if (debug && e.target != document.body) return;
2100
2115
  inputData[0][remapKey(e.which)] = 4;
2101
2116
  }
2102
- 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
+ }
2103
2123
 
2104
2124
  ///////////////////////////////////////////////////////////////////////////////
2105
2125
  // Mouse event handlers
@@ -2108,10 +2128,10 @@ onmousedown = (e)=> {inputData[isUsingGamepad = 0][e.button] = 3; onmousemove(e)
2108
2128
  onmouseup = (e)=> inputData[0][e.button] = inputData[0][e.button] & 2 | 4;
2109
2129
  onmousemove = (e)=> mousePosScreen = mouseToScreen(e);
2110
2130
  onwheel = (e)=> e.ctrlKey || (mouseWheel = sign(e.deltaY));
2111
- oncontextmenu = (e)=> !1; // prevent right click menu
2131
+ oncontextmenu = (e)=> false; // prevent right click menu
2112
2132
 
2113
2133
  // convert a mouse or touch event position to screen space
2114
- const mouseToScreen = (mousePos)=>
2134
+ function mouseToScreen(mousePos)
2115
2135
  {
2116
2136
  if (!mainCanvas)
2117
2137
  return vec2(); // fix bug that can occur if user clicks before page loads
@@ -2157,8 +2177,7 @@ function gamepadsUpdate()
2157
2177
  if (gamepad)
2158
2178
  {
2159
2179
  // read clamp dead zone of analog sticks
2160
- const deadZone = .3, deadZoneMax = .8;
2161
- const applyDeadZone = (v)=>
2180
+ const deadZone = .3, deadZoneMax = .8, applyDeadZone = (v)=>
2162
2181
  v > deadZone ? percent( v, deadZone, deadZoneMax) :
2163
2182
  v < -deadZone ? -percent(-v, deadZone, deadZoneMax) : 0;
2164
2183
 
@@ -2191,11 +2210,12 @@ function gamepadsUpdate()
2191
2210
  /** Pulse the vibration hardware if it exists
2192
2211
  * @param {Number} [pattern=100] - a single value in miliseconds or vibration interval array
2193
2212
  * @memberof Input */
2194
- const vibrate = (pattern)=> vibrateEnable && navigator && navigator.vibrate && navigator.vibrate(pattern);
2213
+ function vibrate(pattern)
2214
+ { vibrateEnable && navigator && navigator.vibrate && navigator.vibrate(pattern); }
2195
2215
 
2196
2216
  /** Cancel any ongoing vibration
2197
2217
  * @memberof Input */
2198
- const vibrateStop = ()=> vibrate(0);
2218
+ function vibrateStop() { vibrate(0); }
2199
2219
 
2200
2220
  ///////////////////////////////////////////////////////////////////////////////
2201
2221
  // Touch input
@@ -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
 
@@ -2653,12 +2675,12 @@ function playSamples(sampleChannels, volume=1, rate=1, pan=0, loop=0)
2653
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
@@ -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);