littlejsengine 1.17.1 → 1.17.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/LICENSE +10 -10
  2. package/dist/littlejs.d.ts +254 -171
  3. package/dist/littlejs.esm.js +892 -628
  4. package/dist/littlejs.esm.min.js +1 -1
  5. package/dist/littlejs.js +889 -626
  6. package/dist/littlejs.min.js +1 -1
  7. package/dist/littlejs.release.js +885 -622
  8. package/examples/box2d/gameObjects.js +3 -3
  9. package/examples/electron/index.html +2 -2
  10. package/examples/index.html +10 -9
  11. package/examples/logo.png +0 -0
  12. package/examples/logo2.png +0 -0
  13. package/examples/module/build.bat +7 -0
  14. package/examples/module/build.js +121 -0
  15. package/examples/particles/index.html +1 -1
  16. package/examples/platformer/game.js +1 -1
  17. package/examples/platformer/gameEffects.js +5 -10
  18. package/examples/platformer/gameLevel.js +3 -3
  19. package/examples/puzzle/game.js +2 -2
  20. package/examples/shorts/base.html +4 -1
  21. package/examples/shorts/box2dPool.js +2 -2
  22. package/examples/shorts/box2dTileLayer.js +48 -0
  23. package/examples/shorts/colors.js +1 -1
  24. package/examples/shorts/fps.js +1 -1
  25. package/examples/shorts/music.js +5 -4
  26. package/examples/shorts/musicPlayer.js +4 -3
  27. package/examples/shorts/parallax.js +2 -2
  28. package/examples/shorts/tiltedView.js +1 -1
  29. package/examples/starter/index.html +2 -2
  30. package/package.json +5 -5
  31. package/plugins/box2d.js +166 -63
  32. package/plugins/pluginExport.js +1 -1
  33. package/plugins/postProcess.js +1 -1
  34. package/plugins/uiSystem.js +33 -20
  35. package/plugins/zzfxm.js +2 -2
  36. package/reference.md +9 -8
  37. package/src/engine.js +111 -134
  38. package/src/engineAudio.js +44 -58
  39. package/src/engineDebug.js +4 -4
  40. package/src/engineDraw.js +7 -4
  41. package/src/engineExport.js +2 -1
  42. package/src/engineInput.js +5 -5
  43. package/src/engineMath.js +9 -9
  44. package/src/engineObject.js +25 -25
  45. package/src/engineParticles.js +241 -118
  46. package/src/engineTileLayer.js +117 -88
  47. package/src/engineUtilities.js +105 -78
  48. package/src/engineWebGL.js +2 -2
  49. package/plugins/Box2D_License.txt +0 -17
@@ -19,50 +19,62 @@ const tileCollisionLayers = [];
19
19
 
20
20
  /** Get tile collision data for a given cell in the grid
21
21
  * @param {Vector2} pos
22
+ * @param {boolean} [solidOnly] - Only check solid layers?
22
23
  * @return {number}
23
24
  * @memberof TileLayers */
24
- function tileCollisionGetData(pos)
25
+ function tileCollisionGetData(pos, solidOnly=true)
25
26
  {
26
27
  // check all tile collision layers
27
28
  for (const layer of tileCollisionLayers)
29
+ if (!solidOnly || layer.isSolid)
28
30
  if (pos.arrayCheck(layer.size))
29
- return layer.getCollisionData(pos);
31
+ {
32
+ const data = layer.getCollisionData(pos);
33
+ if (data) return data;
34
+ }
30
35
  return 0;
31
36
  }
32
37
 
33
38
  /** Check if a tile layer collides with another object
34
- * @param {Vector2} pos
35
- * @param {Vector2} [size=vec2()]
36
- * @param {EngineObject} [object] - An object or undefined for generic test
37
- * @param {boolean} [solidOnly] - Only check solid layers if true
39
+ * @param {Vector2} pos
40
+ * @param {Vector2} [size=vec2()]
41
+ * @param {EngineObject|TileCollisionCallback} [callbackObject] - Callback, engine object, or undefined
42
+ * @param {boolean} [solidOnly] - Only check solid layers?
38
43
  * @return {TileCollisionLayer}
39
44
  * @memberof TileLayers */
40
- function tileCollisionTest(pos, size=vec2(), object, solidOnly=true)
45
+ function tileCollisionTest(pos, size=vec2(), callbackObject, solidOnly=true)
41
46
  {
42
47
  for (const layer of tileCollisionLayers)
43
48
  {
44
49
  if (!solidOnly || layer.isSolid)
45
- if (layer.collisionTest(pos, size, object))
50
+ if (layer.collisionTest(pos, size, callbackObject))
46
51
  return layer;
47
52
  }
48
53
  }
49
54
 
55
+ /**
56
+ * @callback TileCollisionCallback - Function to handle a tile collision test
57
+ * @param {number} tileData - the value of the tile at the position
58
+ * @param {Vector2} pos - world space position of tile where the collision occurred
59
+ * @memberof TileLayers
60
+ */
61
+
50
62
  /** Return the exact position of the boundary of first tile hit, undefined if nothing was hit.
51
- * The point will be inside the colliding tile if it hits (may have a tiny shift)
52
- * @param {Vector2} posStart
53
- * @param {Vector2} posEnd
54
- * @param {EngineObject} [object] - An object or undefined for generic test
55
- * @param {Vector2} [normal] - Optional normal of the surface hit
56
- * @param {boolean} [solidOnly=true] - Only check solid layers if true
63
+ * The point will be inside the colliding tile if it hits
64
+ * @param {Vector2} posStart
65
+ * @param {Vector2} posEnd
66
+ * @param {EngineObject|TileCollisionCallback} [callbackObject] - Callback, engine object, or undefined
67
+ * @param {Vector2} [normal] - Optional normal of the surface hit
68
+ * @param {boolean} [solidOnly=true] - Only check solid layers?
57
69
  * @return {Vector2|undefined} - position of the center of the tile hit or undefined if no hit
58
70
  * @memberof TileLayers */
59
- function tileCollisionRaycast(posStart, posEnd, object, normal, solidOnly=true)
71
+ function tileCollisionRaycast(posStart, posEnd, callbackObject, normal, solidOnly=true)
60
72
  {
61
73
  for (const layer of tileCollisionLayers)
62
74
  {
63
75
  if (!solidOnly || layer.isSolid)
64
76
  {
65
- const hitPos = layer.collisionRaycast(posStart, posEnd, object, normal)
77
+ const hitPos = layer.collisionRaycast(posStart, posEnd, callbackObject, normal)
66
78
  if (hitPos) return hitPos;
67
79
  }
68
80
  }
@@ -183,17 +195,17 @@ class TileLayerData
183
195
  class CanvasLayer extends EngineObject
184
196
  {
185
197
  /** Create a canvas layer object
186
- * @param {Vector2} [position] - World space position of the layer
198
+ * @param {Vector2} [pos] - World space position of the layer
187
199
  * @param {Vector2} [size] - World space size of the layer
188
200
  * @param {number} [angle] - Angle the layer is rotated by
189
201
  * @param {number} [renderOrder] - Objects sorted by renderOrder
190
202
  * @param {Vector2} [canvasSize] - Default size of canvas, can be changed later
191
203
  * @param {boolean} [useWebGL] - Should this layer use WebGL for rendering
192
204
  */
193
- constructor(position, size, angle=0, renderOrder=0, canvasSize=vec2(512), useWebGL=glEnable)
205
+ constructor(pos, size, angle=0, renderOrder=0, canvasSize=vec2(512), useWebGL=true)
194
206
  {
195
207
  ASSERT(isVector2(canvasSize), 'canvasSize must be a Vector2');
196
- super(position, size, undefined, angle, WHITE, renderOrder);
208
+ super(pos, size, undefined, angle, WHITE, renderOrder);
197
209
 
198
210
  /** @property {HTMLCanvasElement} - The canvas used by this layer */
199
211
  this.canvas = headlessMode ? undefined : new OffscreenCanvas(canvasSize.x, canvasSize.y);
@@ -297,16 +309,16 @@ class CanvasLayer extends EngineObject
297
309
  class TileLayer extends CanvasLayer
298
310
  {
299
311
  /** Create a tile layer object
300
- * @param {Vector2} position - World space position
312
+ * @param {Vector2} pos - World space position
301
313
  * @param {Vector2} size - World space size
302
314
  * @param {TileInfo} [tileInfo] - Default tile info for layer (used for size and texture)
303
315
  * @param {number} [renderOrder] - Objects are sorted by renderOrder
316
+ * @param {boolean} [useWebGL] - Should this layer use WebGL for rendering
304
317
  */
305
- constructor(position, size, tileInfo=tile(), renderOrder=0)
318
+ constructor(pos, size, tileInfo=tile(), renderOrder=0, useWebGL=true)
306
319
  {
307
320
  const canvasSize = tileInfo ? size.multiply(tileInfo.size) : size;
308
- const useWebGL = true;
309
- super(position, size, 0, renderOrder, canvasSize, useWebGL);
321
+ super(pos, size, 0, renderOrder, canvasSize, useWebGL);
310
322
 
311
323
  /** @property {TileInfo} - Default tile info for layer */
312
324
  this.tileInfo = undefined;
@@ -318,15 +330,15 @@ class TileLayer extends CanvasLayer
318
330
  if (headlessMode)
319
331
  {
320
332
  // disable rendering in headless mode
321
- this.render = () => {};
322
- this.redraw = () => {};
323
- this.redrawStart = () => {};
324
- this.redrawEnd = () => {};
325
- this.drawTileData = () => {};
326
- this.redrawTileData = () => {};
327
- this.drawLayerTile = () => {};
328
- this.drawLayerRect = () => {};
329
- this.clearLayerRect = () => {};
333
+ this.render = ()=> {};
334
+ this.redraw = ()=> {};
335
+ this.redrawStart = ()=> {};
336
+ this.redrawEnd = ()=> {};
337
+ this.drawTileData = ()=> {};
338
+ this.redrawTileData = ()=> {};
339
+ this.drawLayerTile = ()=> {};
340
+ this.drawLayerRect = ()=> {};
341
+ this.clearLayerRect = ()=> {};
330
342
  return;
331
343
  }
332
344
 
@@ -360,6 +372,12 @@ class TileLayer extends CanvasLayer
360
372
  isRedraw ? this.drawTileData(layerPos) : this.redrawTileData(layerPos);
361
373
  }
362
374
 
375
+ /** Clear data at a given position in the array
376
+ * @param {Vector2} layerPos - Local position in array
377
+ * @param {boolean} [redraw] - Force the tile to redraw if true */
378
+ clearData(layerPos, redraw=false)
379
+ { this.setData(layerPos, new TileLayerData, redraw) }
380
+
363
381
  /** Get data at a given position in the array
364
382
  * @param {Vector2} layerPos - Local position in array
365
383
  * @return {TileLayerData} */
@@ -401,7 +419,7 @@ class TileLayer extends CanvasLayer
401
419
  for (let x = this.size.x; x--;)
402
420
  for (let y = this.size.y; y--;)
403
421
  this.drawTileData(vec2(x,y), false);
404
- this.hasWebGL() && glFlush();
422
+ this.isUsingWebGL && glFlush();
405
423
  this.onRedraw();
406
424
  this.redrawEnd();
407
425
  }
@@ -450,14 +468,13 @@ class TileLayer extends CanvasLayer
450
468
  if (!this.context) return;
451
469
  ASSERT(drawContext === this.context);
452
470
 
453
- if (glEnable && this.textureInfo.glTexture)
454
- glSetRenderTarget();
455
-
456
471
  // set stuff back to normal
472
+ if (this.isUsingWebGL)
473
+ glSetRenderTarget();
457
474
  [drawContext, mainCanvasSize, cameraPos, cameraScale, canvasClearColor] = this.savedRenderSettings;
458
475
  }
459
476
 
460
- /** Draw the tile at a given position in the tile grid
477
+ /** Draw the tile at a given position in the tile layer
461
478
  * This can be used to clear out tiles when they are destroyed
462
479
  * Tiles can also be redrawn if inside a redrawStart/End block
463
480
  * @param {Vector2} layerPos
@@ -481,7 +498,7 @@ class TileLayer extends CanvasLayer
481
498
  this.drawLayerTile(drawPos, drawSize, tileInfo, d.color, d.direction*PI/2, d.mirror);
482
499
  }
483
500
 
484
- /** Draw the tile at a given position in the tile grid
501
+ /** Draw the tile at a given position in the tile layer
485
502
  * This can be used to clear tiles when they are destroyed
486
503
  * For better performance use drawTileData inside a redrawStart/End block
487
504
  * @param {Vector2} layerPos
@@ -509,7 +526,7 @@ class TileLayer extends CanvasLayer
509
526
  angle=0, mirror, additiveColor)
510
527
  {
511
528
  const drawPos = pos.add(size.scale(.5));
512
- drawTile(drawPos, size, tileInfo, color, angle, mirror, additiveColor);
529
+ drawTile(drawPos, size, tileInfo, color, angle, mirror, additiveColor, this.isUsingWebGL);
513
530
  }
514
531
 
515
532
  /** Clear a rectangle in layer space
@@ -549,14 +566,15 @@ class TileLayer extends CanvasLayer
549
566
  class TileCollisionLayer extends TileLayer
550
567
  {
551
568
  /** Create a tile layer object
552
- * @param {Vector2} position - World space position
553
- * @param {Vector2} size - World space size
554
- * @param {TileInfo} [tileInfo] - Tile info for layer
569
+ * @param {Vector2} pos - World space position
570
+ * @param {Vector2} size - World space size
571
+ * @param {TileInfo} [tileInfo] - Tile info for layer
555
572
  * @param {number} [renderOrder] - Objects are sorted by renderOrder
573
+ * @param {boolean} [useWebGL] - Should this layer use WebGL for rendering
556
574
  */
557
- constructor(position, size, tileInfo=tile(), renderOrder=0)
575
+ constructor(pos, size, tileInfo=tile(), renderOrder=0, useWebGL=true)
558
576
  {
559
- super(position, size.floor(), tileInfo, renderOrder);
577
+ super(pos, size.floor(), tileInfo, renderOrder, useWebGL);
560
578
 
561
579
  /** @property {Array<number>} - The tile collision grid */
562
580
  this.collisionData = [];
@@ -592,41 +610,50 @@ class TileCollisionLayer extends TileLayer
592
610
  this.collisionData.fill(0);
593
611
  }
594
612
 
595
- /** Set tile collision data for a given cell in the grid
596
- * @param {Vector2} gridPos
613
+ /** Set tile collision data for a given cell in the layer
614
+ * @param {Vector2} layerPos
597
615
  * @param {number} [data] */
598
- setCollisionData(gridPos, data=1)
616
+ setCollisionData(layerPos, data=1)
599
617
  {
600
- ASSERT(isVector2(gridPos), 'gridPos must be a Vector2');
601
- const i = (gridPos.y|0)*this.size.x + gridPos.x|0;
602
- gridPos.arrayCheck(this.size) && (this.collisionData[i] = data);
618
+ ASSERT(isVector2(layerPos), 'layerPos must be a Vector2');
619
+ const i = (layerPos.y|0)*this.size.x + layerPos.x|0;
620
+ layerPos.arrayCheck(this.size) && (this.collisionData[i] = data);
603
621
  }
604
622
 
605
- /** Get tile collision data for a given cell in the grid
606
- * @param {Vector2} gridPos
623
+ /** Clear tile collision data for a given cell in the layer
624
+ * @param {Vector2} layerPos */
625
+ clearCollisionData(layerPos)
626
+ { this.setCollisionData(layerPos, 0); }
627
+
628
+ /** Get tile collision data for a given cell in the layer
629
+ * @param {Vector2} layerPos
607
630
  * @return {number} */
608
- getCollisionData(gridPos)
631
+ getCollisionData(layerPos)
609
632
  {
610
- ASSERT(isVector2(gridPos), 'gridPos must be a Vector2');
611
- const i = (gridPos.y|0)*this.size.x + gridPos.x|0;
612
- return gridPos.arrayCheck(this.size) ? this.collisionData[i] : 0;
633
+ ASSERT(isVector2(layerPos), 'layerPos must be a Vector2');
634
+ const i = (layerPos.y|0)*this.size.x + layerPos.x|0;
635
+ return layerPos.arrayCheck(this.size) ? this.collisionData[i] : 0;
613
636
  }
614
637
 
615
638
  /** Check if collision with another object should occur
616
639
  * @param {Vector2} pos
617
640
  * @param {Vector2} [size=vec2()]
618
- * @param {EngineObject} [object]
641
+ * @param {EngineObject|TileCollisionCallback} [callbackObject] - Callback, engine object, or undefined
619
642
  * @return {boolean} */
620
- collisionTest(pos, size=new Vector2, object)
643
+ collisionTest(pos, size=new Vector2, callbackObject)
621
644
  {
622
645
  ASSERT(isVector2(pos) && isVector2(size), 'pos and size must be Vector2s');
623
- ASSERT(!object || object instanceof EngineObject, 'object must be an EngineObject');
624
-
625
- // transform to local layer space
626
- const posX = pos.x - this.pos.x;
627
- const posY = pos.y - this.pos.y;
646
+ ASSERT(!callbackObject || typeof callbackObject === 'function' || callbackObject instanceof EngineObject, 'callbackObject must be a funtion or EngineObject');
647
+
648
+ // make function to check for collision
649
+ const collisionTest = callbackObject ? typeof callbackObject === 'function' ?
650
+ (tileData, pos)=> callbackObject(tileData, pos) :
651
+ (tileData, pos)=> callbackObject.collideWithTile(tileData, pos) :
652
+ ()=> true;
628
653
 
629
654
  // check any tiles in the area for collision
655
+ const posX = pos.x - this.pos.x;
656
+ const posY = pos.y - this.pos.y;
630
657
  const minX = max(posX - size.x/2|0, 0);
631
658
  const minY = max(posY - size.y/2|0, 0);
632
659
  const maxX = min(posX + size.x/2, this.size.x);
@@ -637,9 +664,7 @@ class TileCollisionLayer extends TileLayer
637
664
  {
638
665
  // check if the object should collide with this tile
639
666
  const tileData = this.collisionData[y*this.size.x+x];
640
- if (tileData)
641
- if (!object || object.collideWithTile(tileData,
642
- hitPos.set(x + this.pos.x, y + this.pos.y)))
667
+ if (tileData && collisionTest(tileData, hitPos.set(x+this.pos.x, y+this.pos.y)))
643
668
  return true;
644
669
  }
645
670
  return false;
@@ -647,35 +672,39 @@ class TileCollisionLayer extends TileLayer
647
672
 
648
673
  /** Return the exact position of the boundary of first tile hit, undefined if nothing was hit.
649
674
  * The point will be inside the colliding tile if it hits (may have a tiny shift)
650
- * @param {Vector2} posStart
651
- * @param {Vector2} posEnd
652
- * @param {EngineObject} [object] - An object or undefined for generic test
653
- * @param {Vector2} [normal] - Optional normal of the surface hit
675
+ * @param {Vector2} posStart
676
+ * @param {Vector2} posEnd
677
+ * @param {EngineObject|TileCollisionCallback} [callbackObject] - Callback, engine object, or undefined
678
+ * @param {Vector2} [normal] - Optional normal of the surface hit
654
679
  * @return {Vector2|undefined} */
655
- collisionRaycast(posStart, posEnd, object, normal)
680
+ collisionRaycast(posStart, posEnd, callbackObject, normal)
656
681
  {
657
682
  ASSERT(isVector2(posStart) && isVector2(posEnd), 'positions must be Vector2s');
658
- ASSERT(!object || object instanceof EngineObject, 'object must be an EngineObject');
659
-
660
- const localPos = new Vector2;
661
- const collisionTest = (pos)=>
683
+ ASSERT(!callbackObject || typeof callbackObject === 'function' || callbackObject instanceof EngineObject, 'callbackObject must be a funtion or EngineObject');
684
+
685
+ // make function to check for collision
686
+ const collisionTest = callbackObject ? typeof callbackObject === 'function' ?
687
+ (tileData, pos)=> callbackObject(tileData, pos) :
688
+ (tileData, pos)=> callbackObject.collideWithTile(tileData, pos) :
689
+ (tileData)=> tileData > 0;
690
+ const testFunction = (pos)=>
662
691
  {
663
- // check for tile collision
664
- localPos.set(pos.x - this.pos.x, pos.y - this.pos.y);
665
- const tileData = this.getCollisionData(localPos);
666
- return tileData && (!object || object.collideWithTile(tileData, pos));
692
+ const tileData = this.getCollisionData(localPos.set(pos.x - this.pos.x, pos.y - this.pos.y));
693
+ return tileData && collisionTest(tileData, pos);
667
694
  }
668
- debugRaycast && debugLine(posStart, posEnd, '#00f', .02);
669
- const hitPos = lineTest(posStart, posEnd, collisionTest, normal);
670
- if (hitPos)
695
+
696
+ // use line test against tile collision
697
+ const localPos = new Vector2;
698
+ const hitPos = lineTest(posStart, posEnd, testFunction, normal);
699
+ if (debugRaycast && hitPos)
671
700
  {
672
701
  const tilePos = hitPos.floor().add(vec2(.5));
673
- debugRaycast && debugRect(tilePos, vec2(1), '#f008');
674
- debugRaycast && debugLine(posStart, hitPos, '#f00', .02);
675
- debugRaycast && debugPoint(hitPos, '#0f0');
676
- debugRaycast && normal &&
677
- debugLine(hitPos, hitPos.add(normal), '#ff0', .02);
678
- return hitPos;
702
+ debugRect(tilePos, vec2(1), '#f008');
703
+ debugLine(posStart, posEnd, '#00f', .02);
704
+ debugLine(posStart, hitPos, '#f00', .02);
705
+ debugPoint(hitPos, '#0f0');
706
+ normal && debugLine(hitPos, hitPos.add(normal), '#ff0', .02);
679
707
  }
708
+ return hitPos;
680
709
  }
681
710
  }
@@ -7,6 +7,90 @@
7
7
 
8
8
  'use strict';
9
9
 
10
+ /**
11
+ * Timer object tracks how long has passed since it was set
12
+ * @memberof Engine
13
+ * @example
14
+ * let a = new Timer; // creates a timer that is not set
15
+ * a.set(3); // sets the timer to 3 seconds
16
+ *
17
+ * let b = new Timer(1); // creates a timer with 1 second left
18
+ * b.unset(); // unset the timer
19
+ */
20
+ class Timer
21
+ {
22
+ /** Create a timer object set time passed in
23
+ * @param {number} [timeLeft] - How much time left before the timer
24
+ * @param {boolean} [useRealTime] - Should the timer keep running even when the game is paused? (useful for UI) */
25
+ constructor(timeLeft, useRealTime=false)
26
+ {
27
+ ASSERT(timeLeft === undefined || isNumber(timeLeft), 'Constructed Timer is invalid.', timeLeft);
28
+ this.useRealTime = useRealTime;
29
+ const globalTime = this.getGlobalTime();
30
+ this.time = timeLeft === undefined ? undefined : globalTime + timeLeft;
31
+ this.setTime = timeLeft;
32
+ }
33
+
34
+ /** Set the timer with seconds passed in
35
+ * @param {number} [timeLeft] - How much time left before the timer is elapsed in seconds */
36
+ set(timeLeft=0)
37
+ {
38
+ ASSERT(isNumber(timeLeft), 'Timer is invalid.', timeLeft);
39
+ const globalTime = this.getGlobalTime();
40
+ this.time = globalTime + timeLeft;
41
+ this.setTime = timeLeft;
42
+ }
43
+
44
+ /** Set if the timer should keep running even when the game is paused
45
+ * @param {boolean} [useRealTime] */
46
+ setUseRealTime(useRealTime=true)
47
+ {
48
+ ASSERT(!this.isSet(), 'Cannot change global time setting while timer is set.');
49
+ this.useRealTime = useRealTime;
50
+ }
51
+
52
+ /** Unset the timer */
53
+ unset() { this.time = undefined; }
54
+
55
+ /** Returns true if set
56
+ * @return {boolean} */
57
+ isSet() { return this.time !== undefined; }
58
+
59
+ /** Returns true if set and has not elapsed
60
+ * @return {boolean} */
61
+ active() { return this.getGlobalTime() < this.time; }
62
+
63
+ /** Returns true if set and elapsed
64
+ * @return {boolean} */
65
+ elapsed() { return this.getGlobalTime() >= this.time; }
66
+
67
+ /** Get how long since elapsed, returns 0 if not set (returns negative if currently active)
68
+ * @return {number} */
69
+ get() { return this.isSet()? this.getGlobalTime() - this.time : 0; }
70
+
71
+ /** Get percentage elapsed based on time it was set to, returns 0 if not set
72
+ * @return {number} */
73
+ getPercent() { return this.isSet()? 1-percent(this.time - this.getGlobalTime(), 0, this.setTime) : 0; }
74
+
75
+ /** Get the time this timer was set to, returns 0 if not set
76
+ * @return {number} */
77
+ getSetTime() { return this.isSet() ? this.setTime : 0; }
78
+
79
+ /** Get the current global time this timer is based on
80
+ * @return {number} */
81
+ getGlobalTime() { return this.useRealTime ? timeReal : time; }
82
+
83
+ /** Returns this timer expressed as a string
84
+ * @return {string} */
85
+ toString() { return this.isSet() ? abs(this.get()) + ' seconds ' + (this.get()<0 ? 'before' : 'after' ) : 'unset'; }
86
+
87
+ /** Get how long since elapsed, returns 0 if not set (returns negative if currently active)
88
+ * @return {number} */
89
+ valueOf() { return this.get(); }
90
+ }
91
+
92
+ ///////////////////////////////////////////////////////////////////////////////
93
+
10
94
  /** Formats seconds to mm:ss style for display purposes
11
95
  * @param {number} t - time in seconds
12
96
  * @return {string}
@@ -93,84 +177,27 @@ function shareURL(title, url, callback)
93
177
 
94
178
  ///////////////////////////////////////////////////////////////////////////////
95
179
 
96
- /**
97
- * Timer object tracks how long has passed since it was set
98
- * @memberof Engine
99
- * @example
100
- * let a = new Timer; // creates a timer that is not set
101
- * a.set(3); // sets the timer to 3 seconds
102
- *
103
- * let b = new Timer(1); // creates a timer with 1 second left
104
- * b.unset(); // unset the timer
105
- */
106
- class Timer
180
+ /** Read save data from local storage
181
+ * @param {string} saveName - unique name for the game/save
182
+ * @param {Object} [defaultSaveData] - default values for save
183
+ * @return {Object}
184
+ * @memberof Utilities */
185
+ function readSaveData(saveName, defaultSaveData)
107
186
  {
108
- /** Create a timer object set time passed in
109
- * @param {number} [timeLeft] - How much time left before the timer
110
- * @param {boolean} [useRealTime] - Should the timer keep running even when the game is paused? (useful for UI) */
111
- constructor(timeLeft, useRealTime=false)
112
- {
113
- ASSERT(timeLeft === undefined || isNumber(timeLeft), 'Constructed Timer is invalid.', timeLeft);
114
- this.useRealTime = useRealTime;
115
- const globalTime = this.getGlobalTime();
116
- this.time = timeLeft === undefined ? undefined : globalTime + timeLeft;
117
- this.setTime = timeLeft;
118
- }
119
-
120
- /** Set the timer with seconds passed in
121
- * @param {number} [timeLeft] - How much time left before the timer is elapsed in seconds */
122
- set(timeLeft=0)
123
- {
124
- ASSERT(isNumber(timeLeft), 'Timer is invalid.', timeLeft);
125
- const globalTime = this.getGlobalTime();
126
- this.time = globalTime + timeLeft;
127
- this.setTime = timeLeft;
128
- }
129
-
130
- /** Set if the timer should keep running even when the game is paused
131
- * @param {boolean} [useRealTime] */
132
- setUseRealTime(useRealTime=true)
133
- {
134
- ASSERT(!this.isSet(), 'Cannot change global time setting while timer is set.');
135
- this.useRealTime = useRealTime;
136
- }
137
-
138
- /** Unset the timer */
139
- unset() { this.time = undefined; }
140
-
141
- /** Returns true if set
142
- * @return {boolean} */
143
- isSet() { return this.time !== undefined; }
144
-
145
- /** Returns true if set and has not elapsed
146
- * @return {boolean} */
147
- active() { return this.getGlobalTime() < this.time; }
148
-
149
- /** Returns true if set and elapsed
150
- * @return {boolean} */
151
- elapsed() { return this.getGlobalTime() >= this.time; }
152
-
153
- /** Get how long since elapsed, returns 0 if not set (returns negative if currently active)
154
- * @return {number} */
155
- get() { return this.isSet()? this.getGlobalTime() - this.time : 0; }
156
-
157
- /** Get percentage elapsed based on time it was set to, returns 0 if not set
158
- * @return {number} */
159
- getPercent() { return this.isSet()? 1-percent(this.time - this.getGlobalTime(), 0, this.setTime) : 0; }
160
-
161
- /** Get the time this timer was set to, returns 0 if not set
162
- * @return {number} */
163
- getSetTime() { return this.isSet() ? this.setTime : 0; }
164
-
165
- /** Get the current global time this timer is based on
166
- * @return {number} */
167
- getGlobalTime() { return this.useRealTime ? timeReal : time; }
168
-
169
- /** Returns this timer expressed as a string
170
- * @return {string} */
171
- toString() { return this.isSet() ? abs(this.get()) + ' seconds ' + (this.get()<0 ? 'before' : 'after' ) : 'unset'; }
187
+ ASSERT(isString(saveName), 'loadData requires saveName string');
188
+
189
+ // replace undefined values with defaults
190
+ const data = localStorage[saveName];
191
+ const loadedData = data ? JSON.parse(data) : {};
192
+ return { ...defaultSaveData, ...loadedData };
193
+ }
172
194
 
173
- /** Get how long since elapsed, returns 0 if not set (returns negative if currently active)
174
- * @return {number} */
175
- valueOf() { return this.get(); }
195
+ /** Write save data to local storage
196
+ * @param {string} saveName - unique name for the game/save
197
+ * @param {Object} saveData - object containing data to be saved
198
+ * @memberof Utilities */
199
+ function writeSaveData(saveName, saveData)
200
+ {
201
+ ASSERT(isString(saveName), 'saveData requires saveName string');
202
+ localStorage[saveName] = JSON.stringify(saveData);
176
203
  }
@@ -259,7 +259,7 @@ function glPreRender(clear=true)
259
259
  p.x, p.y, 0, 1];
260
260
 
261
261
  // set the same transform matrix for both shaders
262
- const initUniform = (program, uniform, value) =>
262
+ const initUniform = (program, uniform, value)=>
263
263
  {
264
264
  glContext.useProgram(program);
265
265
  const location = glContext.getUniformLocation(program, uniform);
@@ -653,7 +653,7 @@ function glSetRenderTarget(texture, clear=false)
653
653
  glContext.bindFramebuffer(glContext.FRAMEBUFFER, glFramebuffer);
654
654
  glContext.framebufferTexture2D(glContext.FRAMEBUFFER,
655
655
  glContext.COLOR_ATTACHMENT0, glContext.TEXTURE_2D, texture, 0);
656
- glPreRender(clear);
656
+ glPreRender(clear);
657
657
  }
658
658
  else
659
659
  {
@@ -1,17 +0,0 @@
1
- Copyright (c) 2006-2010 Erin Catto http://www.gphysics.com
2
-
3
- This software is provided 'as-is', without any express or implied
4
- warranty. In no event will the authors be held liable for any damages
5
- arising from the use of this software.
6
-
7
- Permission is granted to anyone to use this software for any purpose,
8
- including commercial applications, and to alter it and redistribute it
9
- freely, subject to the following restrictions:
10
-
11
- 1. The origin of this software must not be misrepresented; you must not
12
- claim that you wrote the original software. If you use this software
13
- in a product, an acknowledgment in the product documentation would be
14
- appreciated but is not required.
15
- 2. Altered source versions must be plainly marked as such, and must not be
16
- misrepresented as being the original software.
17
- 3. This notice may not be removed or altered from any source distribution.