littlejsengine 1.12.4 → 1.13.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/box2d.wasm.js +630 -0
- package/dist/box2d.wasm.wasm +0 -0
- package/dist/littlejs.d.ts +437 -219
- package/dist/littlejs.esm.js +5950 -5307
- package/dist/littlejs.esm.min.js +4 -1
- package/dist/littlejs.js +5921 -5295
- package/dist/littlejs.min.js +4 -1
- package/dist/littlejs.release.js +5485 -4886
- package/examples/box2d/game.js +37 -42
- package/examples/box2d/gameObjects.js +41 -37
- package/examples/box2d/index.html +2 -2
- package/examples/box2d/scenes.js +24 -20
- package/examples/box2d/tiles.png +0 -0
- package/examples/breakout/game.js +1 -1
- package/examples/breakout/gameObjects.js +2 -2
- package/examples/breakout/index.html +1 -1
- package/examples/breakoutTutorial/README.md +2 -2
- package/examples/breakoutTutorial/game.js +1 -1
- package/examples/breakoutTutorial/index.html +1 -1
- package/examples/empty/index.html +1 -1
- package/examples/htmlMenu/index.html +1 -1
- package/examples/index.html +410 -159
- package/examples/module/game.js +1 -1
- package/examples/module/index.html +1 -1
- package/examples/platformer/game.js +6 -15
- package/examples/platformer/gameCharacter.js +6 -6
- package/examples/platformer/gameEffects.js +74 -57
- package/examples/platformer/gameLevel.js +61 -70
- package/examples/platformer/gameObjects.js +4 -4
- package/examples/platformer/index.html +1 -1
- package/examples/puzzle/index.html +1 -1
- package/examples/shorts/base.html +24 -3
- package/examples/shorts/box2d.js +46 -0
- package/examples/shorts/box2dCar.js +45 -0
- package/examples/shorts/flappyGame.js +52 -0
- package/examples/shorts/helloWorld.js +6 -3
- package/examples/shorts/hillGlideGame.js +56 -0
- package/examples/shorts/landerGame.js +32 -0
- package/examples/shorts/maze.js +47 -0
- package/examples/shorts/medals.js +42 -0
- package/examples/shorts/nineSlice.js +21 -0
- package/examples/shorts/piano.js +52 -0
- package/examples/shorts/platformer.js +24 -20
- package/examples/shorts/{pong.js → pongGame.js} +1 -1
- package/examples/shorts/postProcess.js +45 -0
- package/examples/shorts/raycasting.js +71 -0
- package/examples/shorts/slidingPuzzle.js +42 -0
- package/examples/shorts/spaceGame.js +56 -0
- package/examples/shorts/starfield.js +17 -0
- package/examples/shorts/tileLayer.js +3 -5
- package/examples/shorts/tiles.png +0 -0
- package/examples/shorts/tiltedView.js +8 -7
- package/examples/shorts/topDown.js +18 -26
- package/examples/shorts/uiSystem.js +36 -0
- package/examples/starter/build.bat +6 -1
- package/examples/starter/game.js +4 -2
- package/examples/starter/index.html +23 -13
- package/examples/stress/index.html +2 -3
- package/examples/style.css +136 -0
- package/examples/typescript/build.js +1 -2
- package/examples/typescript/game.js +2 -2
- package/examples/typescript/game.ts +1 -1
- package/examples/typescript/index.html +1 -1
- package/examples/uiSystem/game.js +9 -25
- package/examples/uiSystem/index.html +1 -1
- package/package.json +1 -1
- package/plugins/box2d.js +29 -35
- package/plugins/drawUtilities.js +126 -0
- package/plugins/newgrounds.js +1 -1
- package/plugins/pluginExport.js +8 -2
- package/plugins/postProcess.js +2 -2
- package/plugins/uiSystem.js +162 -68
- package/plugins/zzfxm.js +1 -1
- package/reference.md +10 -10
- package/src/engine.js +59 -39
- package/src/engineAudio.js +38 -20
- package/src/engineBuild.bat +6 -1
- package/src/engineBuild.js +32 -7
- package/src/engineDebug.js +53 -26
- package/src/engineDraw.js +108 -57
- package/src/engineExport.js +22 -9
- package/src/engineInput.js +112 -40
- package/src/engineObject.js +68 -67
- package/src/engineParticles.js +26 -9
- package/src/engineSettings.js +15 -16
- package/src/engineTileLayer.js +312 -153
- package/src/engineUtilities.js +198 -130
- package/src/engineWebGL.js +58 -35
package/src/engineUtilities.js
CHANGED
|
@@ -22,19 +22,17 @@ const PI = Math.PI;
|
|
|
22
22
|
* @memberof Utilities */
|
|
23
23
|
function abs(value) { return Math.abs(value); }
|
|
24
24
|
|
|
25
|
-
/** Returns lowest
|
|
26
|
-
* @param {number}
|
|
27
|
-
* @param {number} valueB
|
|
25
|
+
/** Returns lowest value passed in
|
|
26
|
+
* @param {...number} values
|
|
28
27
|
* @return {number}
|
|
29
28
|
* @memberof Utilities */
|
|
30
|
-
function min(
|
|
29
|
+
function min(...values) { return Math.min(...values); }
|
|
31
30
|
|
|
32
|
-
/** Returns highest
|
|
33
|
-
* @param {number}
|
|
34
|
-
* @param {number} valueB
|
|
31
|
+
/** Returns highest value passed in
|
|
32
|
+
* @param {...number} values
|
|
35
33
|
* @return {number}
|
|
36
34
|
* @memberof Utilities */
|
|
37
|
-
function max(
|
|
35
|
+
function max(...values) { return Math.max(...values); }
|
|
38
36
|
|
|
39
37
|
/** Returns the sign of value passed in
|
|
40
38
|
* @param {number} value
|
|
@@ -67,12 +65,17 @@ function percent(value, valueA, valueB)
|
|
|
67
65
|
{ return (valueB-=valueA) ? clamp((value-valueA)/valueB) : 0; }
|
|
68
66
|
|
|
69
67
|
/** Linearly interpolates between values passed in using percent
|
|
70
|
-
* @param {number} percent
|
|
71
68
|
* @param {number} valueA
|
|
72
69
|
* @param {number} valueB
|
|
70
|
+
* @param {number} percent
|
|
73
71
|
* @return {number}
|
|
74
72
|
* @memberof Utilities */
|
|
75
|
-
function lerp(
|
|
73
|
+
function lerp(valueA, valueB, percent)
|
|
74
|
+
{
|
|
75
|
+
if (valueA >= 0 && valueA <= 1 && ((valueB < 0 || valueB > 1) && (percent < 0 || percent > 1)))
|
|
76
|
+
console.warn('lerp() parameter order changed! use lerp(start, end, p)');
|
|
77
|
+
return valueA + clamp(percent) * (valueB-valueA);
|
|
78
|
+
}
|
|
76
79
|
|
|
77
80
|
/** Returns signed wrapped distance between the two values passed in
|
|
78
81
|
* @param {number} valueA
|
|
@@ -84,14 +87,18 @@ function distanceWrap(valueA, valueB, wrapSize=1)
|
|
|
84
87
|
{ const d = (valueA - valueB) % wrapSize; return d*2 % wrapSize - d; }
|
|
85
88
|
|
|
86
89
|
/** Linearly interpolates between values passed in with wrapping
|
|
87
|
-
* @param {number} percent
|
|
88
90
|
* @param {number} valueA
|
|
89
91
|
* @param {number} valueB
|
|
92
|
+
* @param {number} percent
|
|
90
93
|
* @param {number} [wrapSize]
|
|
91
94
|
* @returns {number}
|
|
92
95
|
* @memberof Utilities */
|
|
93
|
-
function lerpWrap(
|
|
94
|
-
{
|
|
96
|
+
function lerpWrap(valueA, valueB, percent, wrapSize=1)
|
|
97
|
+
{
|
|
98
|
+
if (valueA >= 0 && valueA <= 1 && ((valueB < 0 || valueB > 1) && (percent < 0 || percent > 1)))
|
|
99
|
+
console.warn('lerpWrap() parameter order changed! use lerpWrap(start, end, p)');
|
|
100
|
+
return valueA + clamp(percent) * distanceWrap(valueB, valueA, wrapSize);
|
|
101
|
+
}
|
|
95
102
|
|
|
96
103
|
/** Returns signed wrapped distance between the two angles passed in
|
|
97
104
|
* @param {number} angleA
|
|
@@ -101,12 +108,12 @@ function lerpWrap(percent, valueA, valueB, wrapSize=1)
|
|
|
101
108
|
function distanceAngle(angleA, angleB) { return distanceWrap(angleA, angleB, 2*PI); }
|
|
102
109
|
|
|
103
110
|
/** Linearly interpolates between the angles passed in with wrapping
|
|
104
|
-
* @param {number} percent
|
|
105
111
|
* @param {number} angleA
|
|
106
112
|
* @param {number} angleB
|
|
113
|
+
* @param {number} percent
|
|
107
114
|
* @returns {number}
|
|
108
115
|
* @memberof Utilities */
|
|
109
|
-
function lerpAngle(
|
|
116
|
+
function lerpAngle(angleA, angleB, percent) { return lerpWrap(angleA, angleB, percent, 2*PI); }
|
|
110
117
|
|
|
111
118
|
/** Applies smoothstep function to the percentage value
|
|
112
119
|
* @param {number} percent
|
|
@@ -114,6 +121,12 @@ function lerpAngle(percent, angleA, angleB) { return lerpWrap(percent, angleA, a
|
|
|
114
121
|
* @memberof Utilities */
|
|
115
122
|
function smoothStep(percent) { return percent * percent * (3 - 2 * percent); }
|
|
116
123
|
|
|
124
|
+
/** Checks if the value passed in is a power of two
|
|
125
|
+
* @param {number} value
|
|
126
|
+
* @return {boolean}
|
|
127
|
+
* @memberof Utilities */
|
|
128
|
+
function isPowerOfTwo(value) { return !(value & (value - 1)); }
|
|
129
|
+
|
|
117
130
|
/** Returns the nearest power of two not less then the value
|
|
118
131
|
* @param {number} value
|
|
119
132
|
* @return {number}
|
|
@@ -189,6 +202,24 @@ function wave(frequency=1, amplitude=1, t=time)
|
|
|
189
202
|
* @memberof Utilities */
|
|
190
203
|
function formatTime(t) { return (t/60|0) + ':' + (t%60<10?'0':'') + (t%60|0); }
|
|
191
204
|
|
|
205
|
+
/** Fetches a JSON file from a URL and returns the parsed JSON object. Must be used with await!
|
|
206
|
+
* @param {string} url - URL of JSON file
|
|
207
|
+
* @return {Promise<object>}
|
|
208
|
+
* @memberof Utilities */
|
|
209
|
+
async function fetchJSON(url)
|
|
210
|
+
{
|
|
211
|
+
const response = await fetch(url);
|
|
212
|
+
return response.json();
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Check if object is a valid number, not NaN or undefined, but it may be infinite
|
|
217
|
+
* @param {any} n
|
|
218
|
+
* @return {boolean}
|
|
219
|
+
* @memberof Utilities
|
|
220
|
+
*/
|
|
221
|
+
function isNumber(n) { return typeof n == 'number' && !isNaN(n); }
|
|
222
|
+
|
|
192
223
|
///////////////////////////////////////////////////////////////////////////////
|
|
193
224
|
|
|
194
225
|
/** Random global functions
|
|
@@ -209,6 +240,12 @@ function rand(valueA=1, valueB=0) { return valueB + Math.random() * (valueA-valu
|
|
|
209
240
|
* @memberof Random */
|
|
210
241
|
function randInt(valueA, valueB=0) { return Math.floor(rand(valueA,valueB)); }
|
|
211
242
|
|
|
243
|
+
/** Randomly returns true or false given the chance of true passed in
|
|
244
|
+
* @param {number} [chance]
|
|
245
|
+
* @return {boolean}
|
|
246
|
+
* @memberof Random */
|
|
247
|
+
function randBool(chance=.5) { return rand() < chance; }
|
|
248
|
+
|
|
212
249
|
/** Randomly returns either -1 or 1
|
|
213
250
|
* @return {number}
|
|
214
251
|
* @memberof Random */
|
|
@@ -218,7 +255,7 @@ function randSign() { return randInt(2) * 2 - 1; }
|
|
|
218
255
|
* @param {number} [length]
|
|
219
256
|
* @return {Vector2}
|
|
220
257
|
* @memberof Random */
|
|
221
|
-
function
|
|
258
|
+
function randVec2(length=1) { return new Vector2().setAngle(rand(2*PI), length); }
|
|
222
259
|
|
|
223
260
|
/** Returns a random Vector2 within a circular shape
|
|
224
261
|
* @param {number} [radius]
|
|
@@ -226,7 +263,7 @@ function randVector(length=1) { return new Vector2().setAngle(rand(2*PI), length
|
|
|
226
263
|
* @return {Vector2}
|
|
227
264
|
* @memberof Random */
|
|
228
265
|
function randInCircle(radius=1, minRadius=0)
|
|
229
|
-
{ return radius > 0 ?
|
|
266
|
+
{ return radius > 0 ? randVec2(radius * rand(minRadius / radius, 1)**.5) : new Vector2; }
|
|
230
267
|
|
|
231
268
|
/** Returns a random color between the two passed in colors, combine components if linear
|
|
232
269
|
* @param {Color} [colorA=(1,1,1,1)]
|
|
@@ -255,8 +292,8 @@ function randColor(colorA=new Color, colorB=new Color(0,0,0,1), linear=false)
|
|
|
255
292
|
class RandomGenerator
|
|
256
293
|
{
|
|
257
294
|
/** Create a random number generator with the seed passed in
|
|
258
|
-
* @param {number} seed - Starting seed */
|
|
259
|
-
constructor(seed)
|
|
295
|
+
* @param {number} [seed] - Starting seed or engine default seed */
|
|
296
|
+
constructor(seed = 123456789)
|
|
260
297
|
{
|
|
261
298
|
/** @property {number} - random seed */
|
|
262
299
|
this.seed = seed;
|
|
@@ -281,14 +318,36 @@ class RandomGenerator
|
|
|
281
318
|
* @return {number} */
|
|
282
319
|
int(valueA, valueB=0) { return Math.floor(this.float(valueA, valueB)); }
|
|
283
320
|
|
|
321
|
+
/** Randomly returns true or false given the chance of true passed in
|
|
322
|
+
* @param {number} [chance]
|
|
323
|
+
* @return {boolean} */
|
|
324
|
+
bool(chance=.5) { return this.float() < chance; }
|
|
325
|
+
|
|
284
326
|
/** Randomly returns either -1 or 1 deterministically
|
|
285
327
|
* @return {number} */
|
|
286
328
|
sign() { return this.float() > .5 ? 1 : -1; }
|
|
329
|
+
|
|
330
|
+
/** Returns a seeded random value between the two values passed in with a random sign
|
|
331
|
+
* @param {number} [valueA]
|
|
332
|
+
* @param {number} [valueB]
|
|
333
|
+
* @return {number} */
|
|
334
|
+
floatSign(valueA=1, valueB=0) { return this.float(valueA, valueB) * this.sign(); }
|
|
335
|
+
|
|
336
|
+
/** Returns a random angle between -PI and PI
|
|
337
|
+
* @return {number} */
|
|
338
|
+
angle() { return this.float(-PI, PI); }
|
|
339
|
+
|
|
340
|
+
/** Returns a seeded vec2 with size between the two values passed in
|
|
341
|
+
* @param {number} valueA
|
|
342
|
+
* @param {number} [valueB]
|
|
343
|
+
* @return {Vector2} */
|
|
344
|
+
vec2(valueA=1, valueB=0)
|
|
345
|
+
{ return vec2(this.float(valueA, valueB), this.float(valueA, valueB)); }
|
|
287
346
|
}
|
|
288
347
|
|
|
289
348
|
///////////////////////////////////////////////////////////////////////////////
|
|
290
349
|
|
|
291
|
-
/**
|
|
350
|
+
/**
|
|
292
351
|
* Create a 2d vector, can take 1 or 2 scalar values
|
|
293
352
|
* @param {number} [x]
|
|
294
353
|
* @param {number} [y] - if y is undefined, x is used for both
|
|
@@ -299,7 +358,7 @@ class RandomGenerator
|
|
|
299
358
|
* b = vec2(); // set b to (0, 0)
|
|
300
359
|
* @memberof Utilities
|
|
301
360
|
*/
|
|
302
|
-
function vec2(x=0, y) { return new Vector2(x, y
|
|
361
|
+
function vec2(x=0, y) { return new Vector2(x, y === undefined ? x : y); }
|
|
303
362
|
|
|
304
363
|
/**
|
|
305
364
|
* Check if object is a valid Vector2
|
|
@@ -309,6 +368,15 @@ function vec2(x=0, y) { return new Vector2(x, y == undefined? x : y); }
|
|
|
309
368
|
*/
|
|
310
369
|
function isVector2(v) { return v instanceof Vector2; }
|
|
311
370
|
|
|
371
|
+
// vector2 asserts
|
|
372
|
+
function ASSERT_VECTOR2_VALID(v) { ASSERT(isVector2(v) && v.isValid(), 'Vector2 is invalid.', v); }
|
|
373
|
+
function ASSERT_NUMBER_VALID(n) { ASSERT(isNumber(n), 'Number is invalid.', n); }
|
|
374
|
+
function ASSERT_VECTOR2_NORMAL(v)
|
|
375
|
+
{
|
|
376
|
+
ASSERT_VECTOR2_VALID(v);
|
|
377
|
+
ASSERT(abs(v.lengthSquared()-1) < .01, 'Vector2 is not normal.', v);
|
|
378
|
+
}
|
|
379
|
+
|
|
312
380
|
/**
|
|
313
381
|
* 2D Vector object with vector math library
|
|
314
382
|
* - Functions do not change this so they can be chained together
|
|
@@ -329,7 +397,7 @@ class Vector2
|
|
|
329
397
|
this.x = x;
|
|
330
398
|
/** @property {number} - Y axis location */
|
|
331
399
|
this.y = y;
|
|
332
|
-
ASSERT(this.isValid());
|
|
400
|
+
ASSERT(this.isValid(), 'Constructed Vector2 is invalid.', this);
|
|
333
401
|
}
|
|
334
402
|
|
|
335
403
|
/** Sets values of this vector and returns self
|
|
@@ -340,7 +408,6 @@ class Vector2
|
|
|
340
408
|
{
|
|
341
409
|
this.x = x;
|
|
342
410
|
this.y = y;
|
|
343
|
-
ASSERT(this.isValid());
|
|
344
411
|
return this;
|
|
345
412
|
}
|
|
346
413
|
|
|
@@ -351,47 +418,27 @@ class Vector2
|
|
|
351
418
|
/** Returns a copy of this vector plus the vector passed in
|
|
352
419
|
* @param {Vector2} v - other vector
|
|
353
420
|
* @return {Vector2} */
|
|
354
|
-
add(v)
|
|
355
|
-
{
|
|
356
|
-
ASSERT(isVector2(v));
|
|
357
|
-
return new Vector2(this.x + v.x, this.y + v.y);
|
|
358
|
-
}
|
|
421
|
+
add(v) { return new Vector2(this.x + v.x, this.y + v.y);}
|
|
359
422
|
|
|
360
423
|
/** Returns a copy of this vector minus the vector passed in
|
|
361
424
|
* @param {Vector2} v - other vector
|
|
362
425
|
* @return {Vector2} */
|
|
363
|
-
subtract(v)
|
|
364
|
-
{
|
|
365
|
-
ASSERT(isVector2(v));
|
|
366
|
-
return new Vector2(this.x - v.x, this.y - v.y);
|
|
367
|
-
}
|
|
426
|
+
subtract(v) { return new Vector2(this.x - v.x, this.y - v.y); }
|
|
368
427
|
|
|
369
428
|
/** Returns a copy of this vector times the vector passed in
|
|
370
429
|
* @param {Vector2} v - other vector
|
|
371
430
|
* @return {Vector2} */
|
|
372
|
-
multiply(v)
|
|
373
|
-
{
|
|
374
|
-
ASSERT(isVector2(v));
|
|
375
|
-
return new Vector2(this.x * v.x, this.y * v.y);
|
|
376
|
-
}
|
|
431
|
+
multiply(v) { return new Vector2(this.x * v.x, this.y * v.y); }
|
|
377
432
|
|
|
378
433
|
/** Returns a copy of this vector divided by the vector passed in
|
|
379
434
|
* @param {Vector2} v - other vector
|
|
380
435
|
* @return {Vector2} */
|
|
381
|
-
divide(v)
|
|
382
|
-
{
|
|
383
|
-
ASSERT(isVector2(v));
|
|
384
|
-
return new Vector2(this.x / v.x, this.y / v.y);
|
|
385
|
-
}
|
|
436
|
+
divide(v) { return new Vector2(this.x / v.x, this.y / v.y); }
|
|
386
437
|
|
|
387
438
|
/** Returns a copy of this vector scaled by the vector passed in
|
|
388
439
|
* @param {number} s - scale
|
|
389
440
|
* @return {Vector2} */
|
|
390
|
-
scale(s)
|
|
391
|
-
{
|
|
392
|
-
ASSERT(!isVector2(s));
|
|
393
|
-
return new Vector2(this.x * s, this.y * s);
|
|
394
|
-
}
|
|
441
|
+
scale(s) { return new Vector2(this.x * s, this.y * s); }
|
|
395
442
|
|
|
396
443
|
/** Returns the length of this vector
|
|
397
444
|
* @return {number} */
|
|
@@ -404,20 +451,12 @@ class Vector2
|
|
|
404
451
|
/** Returns the distance from this vector to vector passed in
|
|
405
452
|
* @param {Vector2} v - other vector
|
|
406
453
|
* @return {number} */
|
|
407
|
-
distance(v)
|
|
408
|
-
{
|
|
409
|
-
ASSERT(isVector2(v));
|
|
410
|
-
return this.distanceSquared(v)**.5;
|
|
411
|
-
}
|
|
454
|
+
distance(v) { return this.distanceSquared(v)**.5; }
|
|
412
455
|
|
|
413
456
|
/** Returns the distance squared from this vector to vector passed in
|
|
414
457
|
* @param {Vector2} v - other vector
|
|
415
458
|
* @return {number} */
|
|
416
|
-
distanceSquared(v)
|
|
417
|
-
{
|
|
418
|
-
ASSERT(isVector2(v));
|
|
419
|
-
return (this.x - v.x)**2 + (this.y - v.y)**2;
|
|
420
|
-
}
|
|
459
|
+
distanceSquared(v) { return (this.x - v.x)**2 + (this.y - v.y)**2; }
|
|
421
460
|
|
|
422
461
|
/** Returns a new vector in same direction as this one with the length passed in
|
|
423
462
|
* @param {number} [length]
|
|
@@ -440,20 +479,19 @@ class Vector2
|
|
|
440
479
|
/** Returns the dot product of this and the vector passed in
|
|
441
480
|
* @param {Vector2} v - other vector
|
|
442
481
|
* @return {number} */
|
|
443
|
-
dot(v)
|
|
444
|
-
{
|
|
445
|
-
ASSERT(isVector2(v));
|
|
446
|
-
return this.x*v.x + this.y*v.y;
|
|
447
|
-
}
|
|
482
|
+
dot(v) { return this.x*v.x + this.y*v.y; }
|
|
448
483
|
|
|
449
484
|
/** Returns the cross product of this and the vector passed in
|
|
450
485
|
* @param {Vector2} v - other vector
|
|
451
486
|
* @return {number} */
|
|
452
|
-
cross(v)
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
487
|
+
cross(v) { return this.x*v.y - this.y*v.x; }
|
|
488
|
+
|
|
489
|
+
/** Returns a copy this vector reflected by the surface normal
|
|
490
|
+
* @param {Vector2} normal - surface normal (should be normalized)
|
|
491
|
+
* @param {number} restitution - how much to bounce, 1 is perfect bounce, 0 is no bounce
|
|
492
|
+
* @return {Vector2} */
|
|
493
|
+
reflect(normal, restitution=1)
|
|
494
|
+
{ return this.subtract(normal.scale((1+restitution)*this.dot(normal))); }
|
|
457
495
|
|
|
458
496
|
/** Returns the clockwise angle of this vector, up is angle 0
|
|
459
497
|
* @return {number} */
|
|
@@ -465,6 +503,8 @@ class Vector2
|
|
|
465
503
|
* @return {Vector2} */
|
|
466
504
|
setAngle(angle=0, length=1)
|
|
467
505
|
{
|
|
506
|
+
ASSERT_NUMBER_VALID(angle);
|
|
507
|
+
ASSERT_NUMBER_VALID(length);
|
|
468
508
|
this.x = length*Math.sin(angle);
|
|
469
509
|
this.y = length*Math.cos(angle);
|
|
470
510
|
return this;
|
|
@@ -474,7 +514,8 @@ class Vector2
|
|
|
474
514
|
* @param {number} angle
|
|
475
515
|
* @return {Vector2} */
|
|
476
516
|
rotate(angle)
|
|
477
|
-
{
|
|
517
|
+
{
|
|
518
|
+
ASSERT_NUMBER_VALID(angle);
|
|
478
519
|
const c = Math.cos(-angle), s = Math.sin(-angle);
|
|
479
520
|
return new Vector2(this.x*c - this.y*s, this.x*s + this.y*c);
|
|
480
521
|
}
|
|
@@ -484,8 +525,11 @@ class Vector2
|
|
|
484
525
|
* @param {number} [length] */
|
|
485
526
|
setDirection(direction, length=1)
|
|
486
527
|
{
|
|
528
|
+
ASSERT_NUMBER_VALID(direction);
|
|
529
|
+
ASSERT_NUMBER_VALID(length);
|
|
487
530
|
direction = mod(direction, 4);
|
|
488
|
-
ASSERT(direction==0 || direction==1 || direction==2 || direction==3
|
|
531
|
+
ASSERT(direction==0 || direction==1 || direction==2 || direction==3,
|
|
532
|
+
'Vector2.setDirection() direction must be an integer between 0 and 3.');
|
|
489
533
|
return vec2(direction%2 ? direction-1 ? -length : length : 0,
|
|
490
534
|
direction%2 ? 0 : direction ? -length : length);
|
|
491
535
|
}
|
|
@@ -499,10 +543,20 @@ class Vector2
|
|
|
499
543
|
* @return {Vector2} */
|
|
500
544
|
invert() { return new Vector2(this.y, -this.x); }
|
|
501
545
|
|
|
546
|
+
/** Returns a copy of this vector absolute values
|
|
547
|
+
* @return {Vector2} */
|
|
548
|
+
abs() { return new Vector2(abs(this.x), abs(this.y)); }
|
|
549
|
+
|
|
502
550
|
/** Returns a copy of this vector with each axis floored
|
|
503
551
|
* @return {Vector2} */
|
|
504
552
|
floor() { return new Vector2(Math.floor(this.x), Math.floor(this.y)); }
|
|
505
553
|
|
|
554
|
+
/** Returns new vec2 with modded values
|
|
555
|
+
* @param {number} [divisor]
|
|
556
|
+
* @return {Vector2} */
|
|
557
|
+
mod(divisor=1)
|
|
558
|
+
{ return new Vector2(mod(this.x, divisor), mod(this.y, divisor)); }
|
|
559
|
+
|
|
506
560
|
/** Returns the area this vector covers as a rectangle
|
|
507
561
|
* @return {number} */
|
|
508
562
|
area() { return abs(this.x * this.y); }
|
|
@@ -513,35 +567,36 @@ class Vector2
|
|
|
513
567
|
* @return {Vector2} */
|
|
514
568
|
lerp(v, percent)
|
|
515
569
|
{
|
|
516
|
-
|
|
517
|
-
|
|
570
|
+
ASSERT_VECTOR2_VALID(v);
|
|
571
|
+
ASSERT_NUMBER_VALID(percent);
|
|
572
|
+
const p = clamp(percent);
|
|
573
|
+
return new Vector2(v.x*p + this.x*(1-p), v.y*p + this.y*(1-p));
|
|
518
574
|
}
|
|
519
575
|
|
|
520
576
|
/** Returns true if this vector is within the bounds of an array size passed in
|
|
521
577
|
* @param {Vector2} arraySize
|
|
522
578
|
* @return {boolean} */
|
|
523
579
|
arrayCheck(arraySize)
|
|
524
|
-
{
|
|
525
|
-
ASSERT(isVector2(arraySize));
|
|
526
|
-
return this.x >= 0 && this.y >= 0 && this.x < arraySize.x && this.y < arraySize.y;
|
|
527
|
-
}
|
|
580
|
+
{ return this.x >= 0 && this.y >= 0 && this.x < arraySize.x && this.y < arraySize.y; }
|
|
528
581
|
|
|
529
582
|
/** Returns this vector expressed as a string
|
|
530
583
|
* @param {number} digits - precision to display
|
|
531
584
|
* @return {string} */
|
|
532
585
|
toString(digits=3)
|
|
533
586
|
{
|
|
587
|
+
ASSERT_NUMBER_VALID(digits);
|
|
534
588
|
if (debug)
|
|
535
|
-
|
|
589
|
+
{
|
|
590
|
+
if (this.isValid())
|
|
591
|
+
return `(${(this.x<0?'':' ') + this.x.toFixed(digits)},${(this.y<0?'':' ') + this.y.toFixed(digits)} )`;
|
|
592
|
+
else
|
|
593
|
+
return `(${this.x}, ${this.y})`;
|
|
594
|
+
}
|
|
536
595
|
}
|
|
537
596
|
|
|
538
597
|
/** Checks if this is a valid vector
|
|
539
598
|
* @return {boolean} */
|
|
540
|
-
isValid()
|
|
541
|
-
{
|
|
542
|
-
return typeof this.x == 'number' && !isNaN(this.x)
|
|
543
|
-
&& typeof this.y == 'number' && !isNaN(this.y);
|
|
544
|
-
}
|
|
599
|
+
isValid() { return isNumber(this.x) && isNumber(this.y); }
|
|
545
600
|
}
|
|
546
601
|
|
|
547
602
|
///////////////////////////////////////////////////////////////////////////////
|
|
@@ -576,13 +631,16 @@ function hsl(h, s, l, a) { return new Color().setHSLA(h, s, l, a); }
|
|
|
576
631
|
*/
|
|
577
632
|
function isColor(c) { return c instanceof Color; }
|
|
578
633
|
|
|
634
|
+
// color asserts
|
|
635
|
+
function ASSERT_COLOR_VALID(c) { ASSERT(isColor(c) && c.isValid(), 'Color is invalid.', c); }
|
|
636
|
+
|
|
579
637
|
/**
|
|
580
638
|
* Color object (red, green, blue, alpha) with some helpful functions
|
|
581
639
|
* @example
|
|
582
640
|
* let a = new Color; // white
|
|
583
641
|
* let b = new Color(1, 0, 0); // red
|
|
584
642
|
* let c = new Color(0, 0, 0, 0); // transparent black
|
|
585
|
-
* let d = rgb(0, 0, 1);
|
|
643
|
+
* let d = rgb(0, 0, 1); // blue using rgb color
|
|
586
644
|
* let e = hsl(.3, 1, .5); // green using hsl color
|
|
587
645
|
*/
|
|
588
646
|
class Color
|
|
@@ -602,7 +660,7 @@ class Color
|
|
|
602
660
|
this.b = b;
|
|
603
661
|
/** @property {number} - Alpha */
|
|
604
662
|
this.a = a;
|
|
605
|
-
ASSERT(this.isValid());
|
|
663
|
+
ASSERT(this.isValid(), 'Constructed Color is invalid.', this);
|
|
606
664
|
}
|
|
607
665
|
|
|
608
666
|
/** Sets values of this color and returns self
|
|
@@ -617,7 +675,6 @@ class Color
|
|
|
617
675
|
this.g = g;
|
|
618
676
|
this.b = b;
|
|
619
677
|
this.a = a;
|
|
620
|
-
ASSERT(this.isValid());
|
|
621
678
|
return this;
|
|
622
679
|
}
|
|
623
680
|
|
|
@@ -628,38 +685,22 @@ class Color
|
|
|
628
685
|
/** Returns a copy of this color plus the color passed in
|
|
629
686
|
* @param {Color} c - other color
|
|
630
687
|
* @return {Color} */
|
|
631
|
-
add(c)
|
|
632
|
-
{
|
|
633
|
-
ASSERT(isColor(c));
|
|
634
|
-
return new Color(this.r+c.r, this.g+c.g, this.b+c.b, this.a+c.a);
|
|
635
|
-
}
|
|
688
|
+
add(c) { return new Color(this.r+c.r, this.g+c.g, this.b+c.b, this.a+c.a); }
|
|
636
689
|
|
|
637
690
|
/** Returns a copy of this color minus the color passed in
|
|
638
691
|
* @param {Color} c - other color
|
|
639
692
|
* @return {Color} */
|
|
640
|
-
subtract(c)
|
|
641
|
-
{
|
|
642
|
-
ASSERT(isColor(c));
|
|
643
|
-
return new Color(this.r-c.r, this.g-c.g, this.b-c.b, this.a-c.a);
|
|
644
|
-
}
|
|
693
|
+
subtract(c) { return new Color(this.r-c.r, this.g-c.g, this.b-c.b, this.a-c.a); }
|
|
645
694
|
|
|
646
695
|
/** Returns a copy of this color times the color passed in
|
|
647
696
|
* @param {Color} c - other color
|
|
648
697
|
* @return {Color} */
|
|
649
|
-
multiply(c)
|
|
650
|
-
{
|
|
651
|
-
ASSERT(isColor(c));
|
|
652
|
-
return new Color(this.r*c.r, this.g*c.g, this.b*c.b, this.a*c.a);
|
|
653
|
-
}
|
|
698
|
+
multiply(c) { return new Color(this.r*c.r, this.g*c.g, this.b*c.b, this.a*c.a); }
|
|
654
699
|
|
|
655
700
|
/** Returns a copy of this color divided by the color passed in
|
|
656
701
|
* @param {Color} c - other color
|
|
657
702
|
* @return {Color} */
|
|
658
|
-
divide(c)
|
|
659
|
-
{
|
|
660
|
-
ASSERT(isColor(c));
|
|
661
|
-
return new Color(this.r/c.r, this.g/c.g, this.b/c.b, this.a/c.a);
|
|
662
|
-
}
|
|
703
|
+
divide(c) { return new Color(this.r/c.r, this.g/c.g, this.b/c.b, this.a/c.a); }
|
|
663
704
|
|
|
664
705
|
/** Returns a copy of this color scaled by the value passed in, alpha can be scaled separately
|
|
665
706
|
* @param {number} scale
|
|
@@ -678,8 +719,14 @@ class Color
|
|
|
678
719
|
* @return {Color} */
|
|
679
720
|
lerp(c, percent)
|
|
680
721
|
{
|
|
681
|
-
|
|
682
|
-
|
|
722
|
+
ASSERT_COLOR_VALID(c);
|
|
723
|
+
ASSERT_NUMBER_VALID(percent);
|
|
724
|
+
const p = clamp(percent);
|
|
725
|
+
return new Color(
|
|
726
|
+
c.r*p + this.r*(1-p),
|
|
727
|
+
c.g*p + this.g*(1-p),
|
|
728
|
+
c.b*p + this.b*(1-p),
|
|
729
|
+
c.a*p + this.a*(1-p));
|
|
683
730
|
}
|
|
684
731
|
|
|
685
732
|
/** Sets this color given a hue, saturation, lightness, and alpha
|
|
@@ -702,7 +749,7 @@ class Color
|
|
|
702
749
|
this.g = f(p, q, h);
|
|
703
750
|
this.b = f(p, q, h - 1/3);
|
|
704
751
|
this.a = a;
|
|
705
|
-
|
|
752
|
+
ASSERT_COLOR_VALID(this);
|
|
706
753
|
return this;
|
|
707
754
|
}
|
|
708
755
|
|
|
@@ -714,20 +761,19 @@ class Color
|
|
|
714
761
|
const g = clamp(this.g);
|
|
715
762
|
const b = clamp(this.b);
|
|
716
763
|
const a = clamp(this.a);
|
|
717
|
-
const
|
|
718
|
-
const
|
|
719
|
-
const l = (
|
|
720
|
-
|
|
764
|
+
const maxC = max(r, g, b);
|
|
765
|
+
const minC = min(r, g, b);
|
|
766
|
+
const l = (maxC + minC) / 2;
|
|
721
767
|
let h = 0, s = 0;
|
|
722
|
-
if (
|
|
768
|
+
if (maxC != minC)
|
|
723
769
|
{
|
|
724
|
-
let d =
|
|
725
|
-
s = l > .5 ? d / (2 -
|
|
726
|
-
if (r ==
|
|
770
|
+
let d = maxC - minC;
|
|
771
|
+
s = l > .5 ? d / (2 - maxC - minC) : d / (maxC + minC);
|
|
772
|
+
if (r == maxC)
|
|
727
773
|
h = (g - b) / d + (g < b ? 6 : 0);
|
|
728
|
-
else if (g ==
|
|
774
|
+
else if (g == maxC)
|
|
729
775
|
h = (b - r) / d + 2;
|
|
730
|
-
else if (b ==
|
|
776
|
+
else if (b == maxC)
|
|
731
777
|
h = (r - g) / d + 4;
|
|
732
778
|
}
|
|
733
779
|
return [h / 6, s, l, a];
|
|
@@ -739,6 +785,8 @@ class Color
|
|
|
739
785
|
* @return {Color} */
|
|
740
786
|
mutate(amount=.05, alphaAmount=0)
|
|
741
787
|
{
|
|
788
|
+
ASSERT_NUMBER_VALID(amount);
|
|
789
|
+
ASSERT_NUMBER_VALID(alphaAmount);
|
|
742
790
|
return new Color
|
|
743
791
|
(
|
|
744
792
|
this.r + rand(amount, -amount),
|
|
@@ -752,7 +800,10 @@ class Color
|
|
|
752
800
|
* @param {boolean} [useAlpha] - if alpha should be included in result
|
|
753
801
|
* @return {string} */
|
|
754
802
|
toString(useAlpha = true)
|
|
755
|
-
{
|
|
803
|
+
{
|
|
804
|
+
ASSERT(typeof useAlpha == 'boolean', 'Use alpha boolean is invalid.', useAlpha);
|
|
805
|
+
if (debug && !this.isValid())
|
|
806
|
+
return `#000`;
|
|
756
807
|
const toHex = (c)=> ((c=clamp(c)*255|0)<16 ? '0' : '') + c.toString(16);
|
|
757
808
|
return '#' + toHex(this.r) + toHex(this.g) + toHex(this.b) + (useAlpha ? toHex(this.a) : '');
|
|
758
809
|
}
|
|
@@ -762,7 +813,7 @@ class Color
|
|
|
762
813
|
* @return {Color} */
|
|
763
814
|
setHex(hex)
|
|
764
815
|
{
|
|
765
|
-
ASSERT(typeof hex == 'string' && hex[0] == '#');
|
|
816
|
+
ASSERT(typeof hex == 'string' && hex[0] == '#', 'Color hex code must be a string starting with #');
|
|
766
817
|
ASSERT([4,5,7,9].includes(hex.length), 'Invalid hex');
|
|
767
818
|
|
|
768
819
|
if (hex.length < 6)
|
|
@@ -782,7 +833,7 @@ class Color
|
|
|
782
833
|
this.a = hex.length == 9 ? fromHex(7) : 1;
|
|
783
834
|
}
|
|
784
835
|
|
|
785
|
-
|
|
836
|
+
ASSERT_COLOR_VALID(this);
|
|
786
837
|
return this;
|
|
787
838
|
}
|
|
788
839
|
|
|
@@ -800,11 +851,8 @@ class Color
|
|
|
800
851
|
/** Checks if this is a valid color
|
|
801
852
|
* @return {boolean} */
|
|
802
853
|
isValid()
|
|
803
|
-
{
|
|
804
|
-
return
|
|
805
|
-
&& typeof this.g == 'number' && !isNaN(this.g)
|
|
806
|
-
&& typeof this.b == 'number' && !isNaN(this.b)
|
|
807
|
-
&& typeof this.a == 'number' && !isNaN(this.a);
|
|
854
|
+
{
|
|
855
|
+
return isNumber(this.r) && isNumber(this.g) && isNumber(this.b) && isNumber(this.a);
|
|
808
856
|
}
|
|
809
857
|
}
|
|
810
858
|
|
|
@@ -816,11 +864,21 @@ class Color
|
|
|
816
864
|
* @memberof Utilities */
|
|
817
865
|
const WHITE = rgb();
|
|
818
866
|
|
|
867
|
+
/** Color - Clear White #ffffff with 0 alpha
|
|
868
|
+
* @type {Color}
|
|
869
|
+
* @memberof Utilities */
|
|
870
|
+
const CLEAR_WHITE = rgb(1,1,1,0);
|
|
871
|
+
|
|
819
872
|
/** Color - Black #000000
|
|
820
873
|
* @type {Color}
|
|
821
874
|
* @memberof Utilities */
|
|
822
875
|
const BLACK = rgb(0,0,0);
|
|
823
876
|
|
|
877
|
+
/** Color - Clear Black #000000 with 0 alpha
|
|
878
|
+
* @type {Color}
|
|
879
|
+
* @memberof Utilities */
|
|
880
|
+
const CLEAR_BLACK = rgb(0,0,0,0);
|
|
881
|
+
|
|
824
882
|
/** Color - Gray #808080
|
|
825
883
|
* @type {Color}
|
|
826
884
|
* @memberof Utilities */
|
|
@@ -881,18 +939,28 @@ class Timer
|
|
|
881
939
|
{
|
|
882
940
|
/** Create a timer object set time passed in
|
|
883
941
|
* @param {number} [timeLeft] - How much time left before the timer elapses in seconds */
|
|
884
|
-
constructor(timeLeft)
|
|
942
|
+
constructor(timeLeft)
|
|
943
|
+
{
|
|
944
|
+
ASSERT(timeLeft === undefined || isNumber(timeLeft), 'Constructed Timer is invalid.', timeLeft);
|
|
945
|
+
this.time = timeLeft === undefined ? undefined : time + timeLeft;
|
|
946
|
+
this.setTime = timeLeft;
|
|
947
|
+
}
|
|
885
948
|
|
|
886
949
|
/** Set the timer with seconds passed in
|
|
887
950
|
* @param {number} [timeLeft] - How much time left before the timer is elapsed in seconds */
|
|
888
|
-
set(timeLeft=0)
|
|
951
|
+
set(timeLeft=0)
|
|
952
|
+
{
|
|
953
|
+
ASSERT(isNumber(timeLeft), 'Timer is invalid.', timeLeft);
|
|
954
|
+
this.time = time + timeLeft;
|
|
955
|
+
this.setTime = timeLeft;
|
|
956
|
+
}
|
|
889
957
|
|
|
890
958
|
/** Unset the timer */
|
|
891
959
|
unset() { this.time = undefined; }
|
|
892
960
|
|
|
893
961
|
/** Returns true if set
|
|
894
962
|
* @return {boolean} */
|
|
895
|
-
isSet() { return this.time
|
|
963
|
+
isSet() { return this.time !== undefined; }
|
|
896
964
|
|
|
897
965
|
/** Returns true if set and has not elapsed
|
|
898
966
|
* @return {boolean} */
|
|
@@ -916,5 +984,5 @@ class Timer
|
|
|
916
984
|
|
|
917
985
|
/** Get how long since elapsed, returns 0 if not set (returns negative if currently active)
|
|
918
986
|
* @return {number} */
|
|
919
|
-
valueOf()
|
|
987
|
+
valueOf() { return this.get(); }
|
|
920
988
|
}
|