littlejsengine 1.12.6 → 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/littlejs.d.ts +387 -177
- package/dist/littlejs.esm.js +5934 -5294
- package/dist/littlejs.esm.min.js +4 -1
- package/dist/littlejs.js +5887 -5263
- package/dist/littlejs.min.js +4 -1
- package/dist/littlejs.release.js +5426 -4829
- package/examples/box2d/game.js +1 -1
- package/examples/box2d/gameObjects.js +7 -6
- package/examples/box2d/index.html +1 -1
- package/examples/box2d/scenes.js +7 -7
- 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 +396 -153
- package/examples/module/game.js +1 -1
- package/examples/module/index.html +1 -1
- package/examples/platformer/game.js +4 -4
- 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 +23 -3
- package/examples/shorts/box2dCar.js +8 -12
- 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/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 +4 -7
- package/examples/starter/build.bat +6 -1
- package/examples/starter/game.js +2 -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 +8 -24
- package/examples/uiSystem/index.html +1 -1
- package/package.json +1 -1
- package/plugins/box2d.js +8 -8
- package/plugins/drawUtilities.js +126 -0
- package/plugins/newgrounds.js +1 -1
- package/plugins/pluginExport.js +7 -1
- package/plugins/postProcess.js +2 -2
- package/plugins/uiSystem.js +153 -65
- package/plugins/zzfxm.js +1 -1
- package/reference.md +10 -10
- package/src/engine.js +50 -29
- package/src/engineAudio.js +14 -5
- package/src/engineBuild.bat +6 -1
- package/src/engineBuild.js +20 -5
- package/src/engineDebug.js +53 -26
- package/src/engineDraw.js +108 -57
- package/src/engineExport.js +21 -9
- package/src/engineInput.js +109 -37
- 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 +182 -130
- package/src/engineWebGL.js +47 -36
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}
|
|
@@ -199,6 +212,14 @@ async function fetchJSON(url)
|
|
|
199
212
|
return response.json();
|
|
200
213
|
}
|
|
201
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
|
+
|
|
202
223
|
///////////////////////////////////////////////////////////////////////////////
|
|
203
224
|
|
|
204
225
|
/** Random global functions
|
|
@@ -219,6 +240,12 @@ function rand(valueA=1, valueB=0) { return valueB + Math.random() * (valueA-valu
|
|
|
219
240
|
* @memberof Random */
|
|
220
241
|
function randInt(valueA, valueB=0) { return Math.floor(rand(valueA,valueB)); }
|
|
221
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
|
+
|
|
222
249
|
/** Randomly returns either -1 or 1
|
|
223
250
|
* @return {number}
|
|
224
251
|
* @memberof Random */
|
|
@@ -228,7 +255,7 @@ function randSign() { return randInt(2) * 2 - 1; }
|
|
|
228
255
|
* @param {number} [length]
|
|
229
256
|
* @return {Vector2}
|
|
230
257
|
* @memberof Random */
|
|
231
|
-
function
|
|
258
|
+
function randVec2(length=1) { return new Vector2().setAngle(rand(2*PI), length); }
|
|
232
259
|
|
|
233
260
|
/** Returns a random Vector2 within a circular shape
|
|
234
261
|
* @param {number} [radius]
|
|
@@ -236,7 +263,7 @@ function randVector(length=1) { return new Vector2().setAngle(rand(2*PI), length
|
|
|
236
263
|
* @return {Vector2}
|
|
237
264
|
* @memberof Random */
|
|
238
265
|
function randInCircle(radius=1, minRadius=0)
|
|
239
|
-
{ return radius > 0 ?
|
|
266
|
+
{ return radius > 0 ? randVec2(radius * rand(minRadius / radius, 1)**.5) : new Vector2; }
|
|
240
267
|
|
|
241
268
|
/** Returns a random color between the two passed in colors, combine components if linear
|
|
242
269
|
* @param {Color} [colorA=(1,1,1,1)]
|
|
@@ -265,8 +292,8 @@ function randColor(colorA=new Color, colorB=new Color(0,0,0,1), linear=false)
|
|
|
265
292
|
class RandomGenerator
|
|
266
293
|
{
|
|
267
294
|
/** Create a random number generator with the seed passed in
|
|
268
|
-
* @param {number} seed - Starting seed */
|
|
269
|
-
constructor(seed)
|
|
295
|
+
* @param {number} [seed] - Starting seed or engine default seed */
|
|
296
|
+
constructor(seed = 123456789)
|
|
270
297
|
{
|
|
271
298
|
/** @property {number} - random seed */
|
|
272
299
|
this.seed = seed;
|
|
@@ -291,6 +318,11 @@ class RandomGenerator
|
|
|
291
318
|
* @return {number} */
|
|
292
319
|
int(valueA, valueB=0) { return Math.floor(this.float(valueA, valueB)); }
|
|
293
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
|
+
|
|
294
326
|
/** Randomly returns either -1 or 1 deterministically
|
|
295
327
|
* @return {number} */
|
|
296
328
|
sign() { return this.float() > .5 ? 1 : -1; }
|
|
@@ -300,11 +332,22 @@ class RandomGenerator
|
|
|
300
332
|
* @param {number} [valueB]
|
|
301
333
|
* @return {number} */
|
|
302
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)); }
|
|
303
346
|
}
|
|
304
347
|
|
|
305
348
|
///////////////////////////////////////////////////////////////////////////////
|
|
306
349
|
|
|
307
|
-
/**
|
|
350
|
+
/**
|
|
308
351
|
* Create a 2d vector, can take 1 or 2 scalar values
|
|
309
352
|
* @param {number} [x]
|
|
310
353
|
* @param {number} [y] - if y is undefined, x is used for both
|
|
@@ -315,7 +358,7 @@ class RandomGenerator
|
|
|
315
358
|
* b = vec2(); // set b to (0, 0)
|
|
316
359
|
* @memberof Utilities
|
|
317
360
|
*/
|
|
318
|
-
function vec2(x=0, y) { return new Vector2(x, y
|
|
361
|
+
function vec2(x=0, y) { return new Vector2(x, y === undefined ? x : y); }
|
|
319
362
|
|
|
320
363
|
/**
|
|
321
364
|
* Check if object is a valid Vector2
|
|
@@ -325,6 +368,15 @@ function vec2(x=0, y) { return new Vector2(x, y == undefined? x : y); }
|
|
|
325
368
|
*/
|
|
326
369
|
function isVector2(v) { return v instanceof Vector2; }
|
|
327
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
|
+
|
|
328
380
|
/**
|
|
329
381
|
* 2D Vector object with vector math library
|
|
330
382
|
* - Functions do not change this so they can be chained together
|
|
@@ -345,7 +397,7 @@ class Vector2
|
|
|
345
397
|
this.x = x;
|
|
346
398
|
/** @property {number} - Y axis location */
|
|
347
399
|
this.y = y;
|
|
348
|
-
ASSERT(this.isValid());
|
|
400
|
+
ASSERT(this.isValid(), 'Constructed Vector2 is invalid.', this);
|
|
349
401
|
}
|
|
350
402
|
|
|
351
403
|
/** Sets values of this vector and returns self
|
|
@@ -356,7 +408,6 @@ class Vector2
|
|
|
356
408
|
{
|
|
357
409
|
this.x = x;
|
|
358
410
|
this.y = y;
|
|
359
|
-
ASSERT(this.isValid());
|
|
360
411
|
return this;
|
|
361
412
|
}
|
|
362
413
|
|
|
@@ -367,47 +418,27 @@ class Vector2
|
|
|
367
418
|
/** Returns a copy of this vector plus the vector passed in
|
|
368
419
|
* @param {Vector2} v - other vector
|
|
369
420
|
* @return {Vector2} */
|
|
370
|
-
add(v)
|
|
371
|
-
{
|
|
372
|
-
ASSERT(isVector2(v));
|
|
373
|
-
return new Vector2(this.x + v.x, this.y + v.y);
|
|
374
|
-
}
|
|
421
|
+
add(v) { return new Vector2(this.x + v.x, this.y + v.y);}
|
|
375
422
|
|
|
376
423
|
/** Returns a copy of this vector minus the vector passed in
|
|
377
424
|
* @param {Vector2} v - other vector
|
|
378
425
|
* @return {Vector2} */
|
|
379
|
-
subtract(v)
|
|
380
|
-
{
|
|
381
|
-
ASSERT(isVector2(v));
|
|
382
|
-
return new Vector2(this.x - v.x, this.y - v.y);
|
|
383
|
-
}
|
|
426
|
+
subtract(v) { return new Vector2(this.x - v.x, this.y - v.y); }
|
|
384
427
|
|
|
385
428
|
/** Returns a copy of this vector times the vector passed in
|
|
386
429
|
* @param {Vector2} v - other vector
|
|
387
430
|
* @return {Vector2} */
|
|
388
|
-
multiply(v)
|
|
389
|
-
{
|
|
390
|
-
ASSERT(isVector2(v));
|
|
391
|
-
return new Vector2(this.x * v.x, this.y * v.y);
|
|
392
|
-
}
|
|
431
|
+
multiply(v) { return new Vector2(this.x * v.x, this.y * v.y); }
|
|
393
432
|
|
|
394
433
|
/** Returns a copy of this vector divided by the vector passed in
|
|
395
434
|
* @param {Vector2} v - other vector
|
|
396
435
|
* @return {Vector2} */
|
|
397
|
-
divide(v)
|
|
398
|
-
{
|
|
399
|
-
ASSERT(isVector2(v));
|
|
400
|
-
return new Vector2(this.x / v.x, this.y / v.y);
|
|
401
|
-
}
|
|
436
|
+
divide(v) { return new Vector2(this.x / v.x, this.y / v.y); }
|
|
402
437
|
|
|
403
438
|
/** Returns a copy of this vector scaled by the vector passed in
|
|
404
439
|
* @param {number} s - scale
|
|
405
440
|
* @return {Vector2} */
|
|
406
|
-
scale(s)
|
|
407
|
-
{
|
|
408
|
-
ASSERT(!isVector2(s));
|
|
409
|
-
return new Vector2(this.x * s, this.y * s);
|
|
410
|
-
}
|
|
441
|
+
scale(s) { return new Vector2(this.x * s, this.y * s); }
|
|
411
442
|
|
|
412
443
|
/** Returns the length of this vector
|
|
413
444
|
* @return {number} */
|
|
@@ -420,20 +451,12 @@ class Vector2
|
|
|
420
451
|
/** Returns the distance from this vector to vector passed in
|
|
421
452
|
* @param {Vector2} v - other vector
|
|
422
453
|
* @return {number} */
|
|
423
|
-
distance(v)
|
|
424
|
-
{
|
|
425
|
-
ASSERT(isVector2(v));
|
|
426
|
-
return this.distanceSquared(v)**.5;
|
|
427
|
-
}
|
|
454
|
+
distance(v) { return this.distanceSquared(v)**.5; }
|
|
428
455
|
|
|
429
456
|
/** Returns the distance squared from this vector to vector passed in
|
|
430
457
|
* @param {Vector2} v - other vector
|
|
431
458
|
* @return {number} */
|
|
432
|
-
distanceSquared(v)
|
|
433
|
-
{
|
|
434
|
-
ASSERT(isVector2(v));
|
|
435
|
-
return (this.x - v.x)**2 + (this.y - v.y)**2;
|
|
436
|
-
}
|
|
459
|
+
distanceSquared(v) { return (this.x - v.x)**2 + (this.y - v.y)**2; }
|
|
437
460
|
|
|
438
461
|
/** Returns a new vector in same direction as this one with the length passed in
|
|
439
462
|
* @param {number} [length]
|
|
@@ -456,20 +479,19 @@ class Vector2
|
|
|
456
479
|
/** Returns the dot product of this and the vector passed in
|
|
457
480
|
* @param {Vector2} v - other vector
|
|
458
481
|
* @return {number} */
|
|
459
|
-
dot(v)
|
|
460
|
-
{
|
|
461
|
-
ASSERT(isVector2(v));
|
|
462
|
-
return this.x*v.x + this.y*v.y;
|
|
463
|
-
}
|
|
482
|
+
dot(v) { return this.x*v.x + this.y*v.y; }
|
|
464
483
|
|
|
465
484
|
/** Returns the cross product of this and the vector passed in
|
|
466
485
|
* @param {Vector2} v - other vector
|
|
467
486
|
* @return {number} */
|
|
468
|
-
cross(v)
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
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))); }
|
|
473
495
|
|
|
474
496
|
/** Returns the clockwise angle of this vector, up is angle 0
|
|
475
497
|
* @return {number} */
|
|
@@ -481,6 +503,8 @@ class Vector2
|
|
|
481
503
|
* @return {Vector2} */
|
|
482
504
|
setAngle(angle=0, length=1)
|
|
483
505
|
{
|
|
506
|
+
ASSERT_NUMBER_VALID(angle);
|
|
507
|
+
ASSERT_NUMBER_VALID(length);
|
|
484
508
|
this.x = length*Math.sin(angle);
|
|
485
509
|
this.y = length*Math.cos(angle);
|
|
486
510
|
return this;
|
|
@@ -490,7 +514,8 @@ class Vector2
|
|
|
490
514
|
* @param {number} angle
|
|
491
515
|
* @return {Vector2} */
|
|
492
516
|
rotate(angle)
|
|
493
|
-
{
|
|
517
|
+
{
|
|
518
|
+
ASSERT_NUMBER_VALID(angle);
|
|
494
519
|
const c = Math.cos(-angle), s = Math.sin(-angle);
|
|
495
520
|
return new Vector2(this.x*c - this.y*s, this.x*s + this.y*c);
|
|
496
521
|
}
|
|
@@ -500,8 +525,11 @@ class Vector2
|
|
|
500
525
|
* @param {number} [length] */
|
|
501
526
|
setDirection(direction, length=1)
|
|
502
527
|
{
|
|
528
|
+
ASSERT_NUMBER_VALID(direction);
|
|
529
|
+
ASSERT_NUMBER_VALID(length);
|
|
503
530
|
direction = mod(direction, 4);
|
|
504
|
-
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.');
|
|
505
533
|
return vec2(direction%2 ? direction-1 ? -length : length : 0,
|
|
506
534
|
direction%2 ? 0 : direction ? -length : length);
|
|
507
535
|
}
|
|
@@ -515,10 +543,20 @@ class Vector2
|
|
|
515
543
|
* @return {Vector2} */
|
|
516
544
|
invert() { return new Vector2(this.y, -this.x); }
|
|
517
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
|
+
|
|
518
550
|
/** Returns a copy of this vector with each axis floored
|
|
519
551
|
* @return {Vector2} */
|
|
520
552
|
floor() { return new Vector2(Math.floor(this.x), Math.floor(this.y)); }
|
|
521
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
|
+
|
|
522
560
|
/** Returns the area this vector covers as a rectangle
|
|
523
561
|
* @return {number} */
|
|
524
562
|
area() { return abs(this.x * this.y); }
|
|
@@ -529,35 +567,36 @@ class Vector2
|
|
|
529
567
|
* @return {Vector2} */
|
|
530
568
|
lerp(v, percent)
|
|
531
569
|
{
|
|
532
|
-
|
|
533
|
-
|
|
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));
|
|
534
574
|
}
|
|
535
575
|
|
|
536
576
|
/** Returns true if this vector is within the bounds of an array size passed in
|
|
537
577
|
* @param {Vector2} arraySize
|
|
538
578
|
* @return {boolean} */
|
|
539
579
|
arrayCheck(arraySize)
|
|
540
|
-
{
|
|
541
|
-
ASSERT(isVector2(arraySize));
|
|
542
|
-
return this.x >= 0 && this.y >= 0 && this.x < arraySize.x && this.y < arraySize.y;
|
|
543
|
-
}
|
|
580
|
+
{ return this.x >= 0 && this.y >= 0 && this.x < arraySize.x && this.y < arraySize.y; }
|
|
544
581
|
|
|
545
582
|
/** Returns this vector expressed as a string
|
|
546
583
|
* @param {number} digits - precision to display
|
|
547
584
|
* @return {string} */
|
|
548
585
|
toString(digits=3)
|
|
549
586
|
{
|
|
587
|
+
ASSERT_NUMBER_VALID(digits);
|
|
550
588
|
if (debug)
|
|
551
|
-
|
|
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
|
+
}
|
|
552
595
|
}
|
|
553
596
|
|
|
554
597
|
/** Checks if this is a valid vector
|
|
555
598
|
* @return {boolean} */
|
|
556
|
-
isValid()
|
|
557
|
-
{
|
|
558
|
-
return typeof this.x == 'number' && !isNaN(this.x)
|
|
559
|
-
&& typeof this.y == 'number' && !isNaN(this.y);
|
|
560
|
-
}
|
|
599
|
+
isValid() { return isNumber(this.x) && isNumber(this.y); }
|
|
561
600
|
}
|
|
562
601
|
|
|
563
602
|
///////////////////////////////////////////////////////////////////////////////
|
|
@@ -592,13 +631,16 @@ function hsl(h, s, l, a) { return new Color().setHSLA(h, s, l, a); }
|
|
|
592
631
|
*/
|
|
593
632
|
function isColor(c) { return c instanceof Color; }
|
|
594
633
|
|
|
634
|
+
// color asserts
|
|
635
|
+
function ASSERT_COLOR_VALID(c) { ASSERT(isColor(c) && c.isValid(), 'Color is invalid.', c); }
|
|
636
|
+
|
|
595
637
|
/**
|
|
596
638
|
* Color object (red, green, blue, alpha) with some helpful functions
|
|
597
639
|
* @example
|
|
598
640
|
* let a = new Color; // white
|
|
599
641
|
* let b = new Color(1, 0, 0); // red
|
|
600
642
|
* let c = new Color(0, 0, 0, 0); // transparent black
|
|
601
|
-
* let d = rgb(0, 0, 1);
|
|
643
|
+
* let d = rgb(0, 0, 1); // blue using rgb color
|
|
602
644
|
* let e = hsl(.3, 1, .5); // green using hsl color
|
|
603
645
|
*/
|
|
604
646
|
class Color
|
|
@@ -618,7 +660,7 @@ class Color
|
|
|
618
660
|
this.b = b;
|
|
619
661
|
/** @property {number} - Alpha */
|
|
620
662
|
this.a = a;
|
|
621
|
-
ASSERT(this.isValid());
|
|
663
|
+
ASSERT(this.isValid(), 'Constructed Color is invalid.', this);
|
|
622
664
|
}
|
|
623
665
|
|
|
624
666
|
/** Sets values of this color and returns self
|
|
@@ -633,7 +675,6 @@ class Color
|
|
|
633
675
|
this.g = g;
|
|
634
676
|
this.b = b;
|
|
635
677
|
this.a = a;
|
|
636
|
-
ASSERT(this.isValid());
|
|
637
678
|
return this;
|
|
638
679
|
}
|
|
639
680
|
|
|
@@ -644,38 +685,22 @@ class Color
|
|
|
644
685
|
/** Returns a copy of this color plus the color passed in
|
|
645
686
|
* @param {Color} c - other color
|
|
646
687
|
* @return {Color} */
|
|
647
|
-
add(c)
|
|
648
|
-
{
|
|
649
|
-
ASSERT(isColor(c));
|
|
650
|
-
return new Color(this.r+c.r, this.g+c.g, this.b+c.b, this.a+c.a);
|
|
651
|
-
}
|
|
688
|
+
add(c) { return new Color(this.r+c.r, this.g+c.g, this.b+c.b, this.a+c.a); }
|
|
652
689
|
|
|
653
690
|
/** Returns a copy of this color minus the color passed in
|
|
654
691
|
* @param {Color} c - other color
|
|
655
692
|
* @return {Color} */
|
|
656
|
-
subtract(c)
|
|
657
|
-
{
|
|
658
|
-
ASSERT(isColor(c));
|
|
659
|
-
return new Color(this.r-c.r, this.g-c.g, this.b-c.b, this.a-c.a);
|
|
660
|
-
}
|
|
693
|
+
subtract(c) { return new Color(this.r-c.r, this.g-c.g, this.b-c.b, this.a-c.a); }
|
|
661
694
|
|
|
662
695
|
/** Returns a copy of this color times the color passed in
|
|
663
696
|
* @param {Color} c - other color
|
|
664
697
|
* @return {Color} */
|
|
665
|
-
multiply(c)
|
|
666
|
-
{
|
|
667
|
-
ASSERT(isColor(c));
|
|
668
|
-
return new Color(this.r*c.r, this.g*c.g, this.b*c.b, this.a*c.a);
|
|
669
|
-
}
|
|
698
|
+
multiply(c) { return new Color(this.r*c.r, this.g*c.g, this.b*c.b, this.a*c.a); }
|
|
670
699
|
|
|
671
700
|
/** Returns a copy of this color divided by the color passed in
|
|
672
701
|
* @param {Color} c - other color
|
|
673
702
|
* @return {Color} */
|
|
674
|
-
divide(c)
|
|
675
|
-
{
|
|
676
|
-
ASSERT(isColor(c));
|
|
677
|
-
return new Color(this.r/c.r, this.g/c.g, this.b/c.b, this.a/c.a);
|
|
678
|
-
}
|
|
703
|
+
divide(c) { return new Color(this.r/c.r, this.g/c.g, this.b/c.b, this.a/c.a); }
|
|
679
704
|
|
|
680
705
|
/** Returns a copy of this color scaled by the value passed in, alpha can be scaled separately
|
|
681
706
|
* @param {number} scale
|
|
@@ -694,8 +719,14 @@ class Color
|
|
|
694
719
|
* @return {Color} */
|
|
695
720
|
lerp(c, percent)
|
|
696
721
|
{
|
|
697
|
-
|
|
698
|
-
|
|
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));
|
|
699
730
|
}
|
|
700
731
|
|
|
701
732
|
/** Sets this color given a hue, saturation, lightness, and alpha
|
|
@@ -718,7 +749,7 @@ class Color
|
|
|
718
749
|
this.g = f(p, q, h);
|
|
719
750
|
this.b = f(p, q, h - 1/3);
|
|
720
751
|
this.a = a;
|
|
721
|
-
|
|
752
|
+
ASSERT_COLOR_VALID(this);
|
|
722
753
|
return this;
|
|
723
754
|
}
|
|
724
755
|
|
|
@@ -730,20 +761,19 @@ class Color
|
|
|
730
761
|
const g = clamp(this.g);
|
|
731
762
|
const b = clamp(this.b);
|
|
732
763
|
const a = clamp(this.a);
|
|
733
|
-
const
|
|
734
|
-
const
|
|
735
|
-
const l = (
|
|
736
|
-
|
|
764
|
+
const maxC = max(r, g, b);
|
|
765
|
+
const minC = min(r, g, b);
|
|
766
|
+
const l = (maxC + minC) / 2;
|
|
737
767
|
let h = 0, s = 0;
|
|
738
|
-
if (
|
|
768
|
+
if (maxC != minC)
|
|
739
769
|
{
|
|
740
|
-
let d =
|
|
741
|
-
s = l > .5 ? d / (2 -
|
|
742
|
-
if (r ==
|
|
770
|
+
let d = maxC - minC;
|
|
771
|
+
s = l > .5 ? d / (2 - maxC - minC) : d / (maxC + minC);
|
|
772
|
+
if (r == maxC)
|
|
743
773
|
h = (g - b) / d + (g < b ? 6 : 0);
|
|
744
|
-
else if (g ==
|
|
774
|
+
else if (g == maxC)
|
|
745
775
|
h = (b - r) / d + 2;
|
|
746
|
-
else if (b ==
|
|
776
|
+
else if (b == maxC)
|
|
747
777
|
h = (r - g) / d + 4;
|
|
748
778
|
}
|
|
749
779
|
return [h / 6, s, l, a];
|
|
@@ -755,6 +785,8 @@ class Color
|
|
|
755
785
|
* @return {Color} */
|
|
756
786
|
mutate(amount=.05, alphaAmount=0)
|
|
757
787
|
{
|
|
788
|
+
ASSERT_NUMBER_VALID(amount);
|
|
789
|
+
ASSERT_NUMBER_VALID(alphaAmount);
|
|
758
790
|
return new Color
|
|
759
791
|
(
|
|
760
792
|
this.r + rand(amount, -amount),
|
|
@@ -768,7 +800,10 @@ class Color
|
|
|
768
800
|
* @param {boolean} [useAlpha] - if alpha should be included in result
|
|
769
801
|
* @return {string} */
|
|
770
802
|
toString(useAlpha = true)
|
|
771
|
-
{
|
|
803
|
+
{
|
|
804
|
+
ASSERT(typeof useAlpha == 'boolean', 'Use alpha boolean is invalid.', useAlpha);
|
|
805
|
+
if (debug && !this.isValid())
|
|
806
|
+
return `#000`;
|
|
772
807
|
const toHex = (c)=> ((c=clamp(c)*255|0)<16 ? '0' : '') + c.toString(16);
|
|
773
808
|
return '#' + toHex(this.r) + toHex(this.g) + toHex(this.b) + (useAlpha ? toHex(this.a) : '');
|
|
774
809
|
}
|
|
@@ -778,7 +813,7 @@ class Color
|
|
|
778
813
|
* @return {Color} */
|
|
779
814
|
setHex(hex)
|
|
780
815
|
{
|
|
781
|
-
ASSERT(typeof hex == 'string' && hex[0] == '#');
|
|
816
|
+
ASSERT(typeof hex == 'string' && hex[0] == '#', 'Color hex code must be a string starting with #');
|
|
782
817
|
ASSERT([4,5,7,9].includes(hex.length), 'Invalid hex');
|
|
783
818
|
|
|
784
819
|
if (hex.length < 6)
|
|
@@ -798,7 +833,7 @@ class Color
|
|
|
798
833
|
this.a = hex.length == 9 ? fromHex(7) : 1;
|
|
799
834
|
}
|
|
800
835
|
|
|
801
|
-
|
|
836
|
+
ASSERT_COLOR_VALID(this);
|
|
802
837
|
return this;
|
|
803
838
|
}
|
|
804
839
|
|
|
@@ -816,11 +851,8 @@ class Color
|
|
|
816
851
|
/** Checks if this is a valid color
|
|
817
852
|
* @return {boolean} */
|
|
818
853
|
isValid()
|
|
819
|
-
{
|
|
820
|
-
return
|
|
821
|
-
&& typeof this.g == 'number' && !isNaN(this.g)
|
|
822
|
-
&& typeof this.b == 'number' && !isNaN(this.b)
|
|
823
|
-
&& typeof this.a == 'number' && !isNaN(this.a);
|
|
854
|
+
{
|
|
855
|
+
return isNumber(this.r) && isNumber(this.g) && isNumber(this.b) && isNumber(this.a);
|
|
824
856
|
}
|
|
825
857
|
}
|
|
826
858
|
|
|
@@ -832,11 +864,21 @@ class Color
|
|
|
832
864
|
* @memberof Utilities */
|
|
833
865
|
const WHITE = rgb();
|
|
834
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
|
+
|
|
835
872
|
/** Color - Black #000000
|
|
836
873
|
* @type {Color}
|
|
837
874
|
* @memberof Utilities */
|
|
838
875
|
const BLACK = rgb(0,0,0);
|
|
839
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
|
+
|
|
840
882
|
/** Color - Gray #808080
|
|
841
883
|
* @type {Color}
|
|
842
884
|
* @memberof Utilities */
|
|
@@ -897,18 +939,28 @@ class Timer
|
|
|
897
939
|
{
|
|
898
940
|
/** Create a timer object set time passed in
|
|
899
941
|
* @param {number} [timeLeft] - How much time left before the timer elapses in seconds */
|
|
900
|
-
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
|
+
}
|
|
901
948
|
|
|
902
949
|
/** Set the timer with seconds passed in
|
|
903
950
|
* @param {number} [timeLeft] - How much time left before the timer is elapsed in seconds */
|
|
904
|
-
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
|
+
}
|
|
905
957
|
|
|
906
958
|
/** Unset the timer */
|
|
907
959
|
unset() { this.time = undefined; }
|
|
908
960
|
|
|
909
961
|
/** Returns true if set
|
|
910
962
|
* @return {boolean} */
|
|
911
|
-
isSet() { return this.time
|
|
963
|
+
isSet() { return this.time !== undefined; }
|
|
912
964
|
|
|
913
965
|
/** Returns true if set and has not elapsed
|
|
914
966
|
* @return {boolean} */
|
|
@@ -932,5 +984,5 @@ class Timer
|
|
|
932
984
|
|
|
933
985
|
/** Get how long since elapsed, returns 0 if not set (returns negative if currently active)
|
|
934
986
|
* @return {number} */
|
|
935
|
-
valueOf()
|
|
987
|
+
valueOf() { return this.get(); }
|
|
936
988
|
}
|